diff --git a/.gitignore b/.gitignore index 6e7ad76b..32ef11d6 100644 --- a/.gitignore +++ b/.gitignore @@ -366,27 +366,46 @@ src/extras/GitSHA1.cpp *.d *.sim.o *.sim.o3 -dreamcast/1ST_READ.BIN -dreamcast/IP.BIN -dreamcast/re3.cdi -dreamcast/re3.iso -dreamcast/re3.elf -dreamcast/re3.elf.bin -dreamcast/re3-sim.elf -dreamcast/dca3.cdi -dreamcast/dca3.iso -dreamcast/dca3.elf -dreamcast/dca3.elf.bin -dreamcast/dca3-sim.elf -dreamcast/texconv* -dreamcast/imgtool* -dreamcast/extract-sfx* -dreamcast/pack-sfx* -dreamcast/analyze-profile* -dreamcast/aud2adpcm* -dreamcast/repack-data -dreamcast/output.map -dreamcast/dca3.ds.iso -dreamcast/git-version.h -dreamcast/git-version.tmp + +liberty/1ST_READ.BIN +liberty/IP.BIN +liberty/*.cdi +liberty/*.iso +liberty/dca-liberty.elf +liberty/dca-liberty.elf.bin +liberty/dca-liberty-sim.elf +liberty/animtool* +liberty/texconv* +liberty/imgtool* +liberty/coltool* +liberty/streamheaderpack* +liberty/extract-sfx* +liberty/pack-sfx* +liberty/analyze-profile* +liberty/aud2adpcm* +liberty/repack-data +liberty/output.map +liberty/git-version + +miami/1ST_READ.BIN +miami/IP.BIN +miami/*.cdi +miami/*.iso +miami/dca-miami.elf +miami/dca-miami.elf.bin +miami/dca-miami-sim.elf +miami/animtool* +miami/texconv* +miami/imgtool* +miami/coltool* +miami/streamheaderpack* +miami/extract-sfx* +miami/pack-sfx* +miami/analyze-profile* +miami/aud2adpcm* +miami/adf2mp3* +miami/repack-data +miami/output.map +miami/git-version + .DS_Store diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index deb86975..71854ac9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,7 +5,7 @@ image: name: ghcr.io/kos-builds/kos-dc:latest-14.1.0 entrypoint: ["/bin/sh", "-c", 'echo gitlab command: "$@" && /bin/bash -c "$@"'] -build-dreamcast: +build-dreamcast-liberty: variables: CCACHE_BASEDIR: $CI_PROJECT_DIR CCACHE_DIR: $CI_PROJECT_DIR/ccache @@ -23,13 +23,13 @@ build-dreamcast: - make -C /opt/toolchains/dc/kos/kernel/arch/dreamcast/sound/arm - make -C /opt/toolchains/dc/kos -j $(nproc) script: - - cd dreamcast + - cd liberty - make -j $(nproc) artifacts: paths: - - dreamcast/dca3.elf + - liberty/dca-liberty.elf -build-dreamcast-ide-32mb: +build-dreamcast-miami: variables: CCACHE_BASEDIR: $CI_PROJECT_DIR CCACHE_DIR: $CI_PROJECT_DIR/ccache @@ -47,13 +47,13 @@ build-dreamcast-ide-32mb: - make -C /opt/toolchains/dc/kos/kernel/arch/dreamcast/sound/arm - make -C /opt/toolchains/dc/kos -j $(nproc) script: - - cd dreamcast - - make -j $(nproc) WITH_IDE=1 WITH_32MB=1 TARGET=dca3-ide-32mb.elf + - cd miami + - make -j $(nproc) artifacts: paths: - - dreamcast/dca3-ide-32mb.elf + - miami/dca-miami.elf -build-texconv: +build-texconv-liberty: variables: CCACHE_BASEDIR: $CI_PROJECT_DIR CCACHE_DIR: $CI_PROJECT_DIR/ccache @@ -68,13 +68,13 @@ build-texconv: - update-ccache-symlinks script: - export PATH="/usr/lib/ccache:$PATH" - - cd dreamcast + - cd liberty - make texconv -j $(nproc) artifacts: paths: - - dreamcast/texconv + - liberty/texconv -build-texconv-clang: +build-texconv-liberty-clang: variables: CCACHE_BASEDIR: $CI_PROJECT_DIR CCACHE_DIR: $CI_PROJECT_DIR/ccache @@ -89,14 +89,14 @@ build-texconv-clang: - update-ccache-symlinks script: - export PATH="/usr/lib/ccache:$PATH" - - cd dreamcast + - cd liberty - CC=clang CXX=clang++ make texconv -j $(nproc) - mv texconv texconv-clang artifacts: paths: - - dreamcast/texconv-clang + - liberty/texconv-clang -build-sim: +build-texconv-miami: variables: CCACHE_BASEDIR: $CI_PROJECT_DIR CCACHE_DIR: $CI_PROJECT_DIR/ccache @@ -106,19 +106,61 @@ build-sim: - $CCACHE_DIR stage: build before_script: - - dpkg --add-architecture i386 - apt update - - apt install -y build-essential gcc g++ gcc-multilib g++-multilib libx11-dev:i386 ccache + - apt install -y build-essential gcc g++ ccache - update-ccache-symlinks script: - export PATH="/usr/lib/ccache:$PATH" - - cd dreamcast + - cd miami + - make texconv -j $(nproc) + artifacts: + paths: + - miami/texconv + +build-texconv-miami-clang: + variables: + CCACHE_BASEDIR: $CI_PROJECT_DIR + CCACHE_DIR: $CI_PROJECT_DIR/ccache + cache: + - key: ccache-$CI_JOB_NAME + paths: + - $CCACHE_DIR + stage: build + before_script: + - apt update + - apt install -y build-essential gcc g++ clang ccache + - update-ccache-symlinks + script: + - export PATH="/usr/lib/ccache:$PATH" + - cd miami + - CC=clang CXX=clang++ make texconv -j $(nproc) + - mv texconv texconv-clang + artifacts: + paths: + - miami/texconv-clang + +build-sim-liberty: + variables: + CCACHE_BASEDIR: $CI_PROJECT_DIR + CCACHE_DIR: $CI_PROJECT_DIR/ccache + cache: + - key: ccache-$CI_JOB_NAME + paths: + - $CCACHE_DIR + stage: build + before_script: + - apt update + - apt install -y build-essential gcc g++ libx11-dev ccache + - update-ccache-symlinks + script: + - export PATH="/usr/lib/ccache:$PATH" + - cd liberty - make -f sim.mk -j $(nproc) artifacts: paths: - - dreamcast/dca3-sim.elf + - liberty/dca-liberty-sim.elf -build-sim-clang: +build-sim-liberty-clang: variables: CCACHE_BASEDIR: $CI_PROJECT_DIR CCACHE_DIR: $CI_PROJECT_DIR/ccache @@ -128,24 +170,55 @@ build-sim-clang: - $CCACHE_DIR stage: build before_script: - - dpkg --add-architecture i386 - apt update - - apt install -y build-essential gcc g++ gcc-multilib g++-multilib libx11-dev:i386 clang ccache + - apt install -y build-essential gcc g++ libx11-dev clang ccache - update-ccache-symlinks script: - export PATH="/usr/lib/ccache:$PATH" - - cd dreamcast - - CC=clang CXX=clang++ make -f sim.mk -j $(nproc) TARGET=dca3-sim-clang.elf + - cd liberty + - CC=clang CXX=clang++ make -f sim.mk -j $(nproc) TARGET=dca-liberty-sim-clang.elf artifacts: paths: - - dreamcast/dca3-sim-clang.elf + - liberty/dca-liberty-sim-clang.elf -pages: - stage: deploy +build-sim-miami: + variables: + CCACHE_BASEDIR: $CI_PROJECT_DIR + CCACHE_DIR: $CI_PROJECT_DIR/ccache + cache: + - key: ccache-$CI_JOB_NAME + paths: + - $CCACHE_DIR + stage: build + before_script: + - apt update + - apt install -y build-essential gcc g++ libx11-dev ccache + - update-ccache-symlinks script: - - echo "The site will be deployed to $CI_PAGES_URL" + - export PATH="/usr/lib/ccache:$PATH" + - cd miami + - make -f sim.mk -j $(nproc) artifacts: paths: - - public - rules: - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH \ No newline at end of file + - miami/dca-liberty-sim.elf + +build-sim-miami-clang: + variables: + CCACHE_BASEDIR: $CI_PROJECT_DIR + CCACHE_DIR: $CI_PROJECT_DIR/ccache + cache: + - key: ccache-$CI_JOB_NAME + paths: + - $CCACHE_DIR + stage: build + before_script: + - apt update + - apt install -y build-essential gcc g++ libx11-dev clang ccache + - update-ccache-symlinks + script: + - export PATH="/usr/lib/ccache:$PATH" + - cd miami + - CC=clang CXX=clang++ make -f sim.mk -j $(nproc) TARGET=dca-miami-sim-clang.elf + artifacts: + paths: + - miami/dca-miami-sim-clang.elf \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 82b6d2fe..5aa93a55 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -140,6 +140,12 @@ "svd": "cpp", "print": "cpp", "strstream": "cpp", - "regex": "cpp" + "regex": "cpp", + "cassert": "cpp", + "barrier": "cpp", + "coroutine": "cpp", + "future": "cpp", + "latch": "cpp", + "syncstream": "cpp" } } diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index fedf86aa..00000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,100 +0,0 @@ -cmake_minimum_required(VERSION 3.14) - -set(EXECUTABLE re3) -set(PROJECT RE3) - -project(${EXECUTABLE} C CXX) -set(${PROJECT}_AUTHOR "${PROJECT} Team") -list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") - -include(GetGitRevisionDescription) -get_git_head_revision(GIT_REFSPEC GIT_SHA1 "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR") -message(STATUS "Building ${CMAKE_PROJECT_NAME} GIT SHA1: ${GIT_SHA1}") - -if(NINTENDO_SWITCH) - list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/nx") - include(NXFunctions) -endif() - -if(NOT COMMAND re3_platform_target) - function(re3_platform_target) - endfunction() -endif() - -if(WIN32) - set(${PROJECT}_AUDIOS "OAL" "MSS") -else() - set(${PROJECT}_AUDIOS "OAL") -endif() - -set(${PROJECT}_AUDIO "OAL" CACHE STRING "Audio") - -option(${PROJECT}_INSTALL "Enable installation of ${EXECUTABLE} + gamefiles" OFF) -option(${PROJECT}_WITH_OPUS "Build ${EXECUTABLE} with opus support" OFF) -option(${PROJECT}_WITH_LIBSNDFILE "Build ${EXECUTABLE} with libsndfile (instead of internal decoder)" OFF) - -set_property(CACHE ${PROJECT}_AUDIO PROPERTY STRINGS ${${PROJECT}_AUDIOS}) -message(STATUS "${PROJECT}_AUDIO = ${${PROJECT}_AUDIO} (choices=${${PROJECT}_AUDIOS})") -set("${PROJECT}_AUDIO_${${PROJECT}_AUDIO}" ON) -if(NOT ${PROJECT}_AUDIO IN_LIST ${PROJECT}_AUDIOS) - message(FATAL_ERROR "Illegal ${PROJECT}_AUDIO=${${PROJECT}_AUDIO}") -endif() - -option(${PROJECT}_VENDORED_LIBRW "Use vendored librw" ON) -if(${PROJECT}_VENDORED_LIBRW) - add_subdirectory(vendor/librw) -else() - find_package(librw REQUIRED) -endif() -add_subdirectory(src) - -if(${PROJECT}_INSTALL) - install(DIRECTORY gamefiles/ DESTINATION ".") - if(LIBRW_PLATFORM_NULL) - set(platform "-null") - elseif(LIBRW_PLATFORM_PS2) - set(platform "-ps2") - elseif(LIBRW_PLATFORM_GL3) - if(LIBRW_GL3_GFXLIB STREQUAL "GLFW") - set(platform "-gl3-glfw") - else() - set(platform "-gl3-sdl2") - endif() - elseif(LIBRW_PLATFORM_D3D9) - set(platform "-d3d9") - endif() - if(${PROJECT}_AUDIO_OAL) - set(audio "-oal") - elseif(${PROJECT}_AUDIO_MSS) - set(audio "-mss") - endif() - if(${PROJECT}_WITH_OPUS) - set(audio "${audio}-opus") - endif() - if(NOT LIBRW_PLATFORM_PS2) - if(WIN32) - set(os "-win") - elseif(APPLE) - set(os "-apple") - elseif(UNIX) - set(os "-linux") - elseif(NINTENDO_SWITCH) - set(os "-switch") - else() - set(compiler "-UNK") - message(WARNING "Unknown os. Created cpack package will be wrong. (override using cpack -P)") - endif() - endif() - - set(CPACK_PACKAGE_NAME "${PROJECT_NAME}${platform}${audio}${os}${compiler}") - set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "GTA III reversed") - set(CPACK_PACKAGE_VENDOR "GTAModding") - # FIXME: missing license (https://github.com/GTAmodding/re3/issues/794) - # set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/LICENSE") - # set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") - set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_NAME}") - set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}") - set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}") - set(CPACK_GENERATOR "ZIP") - include(CPack) -endif() diff --git a/CODING_STYLE.md b/CODING_STYLE.md deleted file mode 100644 index b8be02bc..00000000 --- a/CODING_STYLE.md +++ /dev/null @@ -1,107 +0,0 @@ -# Coding style - -I started writing in [Plan 9 style](http://man.cat-v.org/plan_9/6/style), -but realize that this is not the most popular style, so I'm willing to compromise. -Try not to deviate too much so the code will look similar across the whole project. - -To give examples, these two styles (or anything in between) are fine: - -``` -type -functionname(args) -{ - if(a == b){ - s1; - s2; - }else{ - s3; - s4; - } - if(x != y) - s5; -} - -type functionname(args) -{ - if (a == b) { - s1; - s2; - } else { - s3; - s4; - } - if (x != y) - s5; -} -``` - -This one (or anything more extreme) is heavily discouraged: - -``` -type functionname ( args ) -{ - if ( a == b ) - { - s1; - s2; - } - else - { - s3; - s4; - } - if ( x != y ) - { - s5; - } -} -``` - -i.e. - -* Put the brace on the same line as control statements - -* Put the brace on the next line after function definitions and structs/classes - -* Put an `else` on the same line with the braces - -* Don't put braces around single statements - -* Put the function return type on a separate line - -* Indent with TABS - -As for the less cosmetic choices, here are some guidelines how the code should look: - -* Don't use magic numbers where the original source code would have had an enum or similar. -Even if you don't know the exact meaning it's better to call something `FOOBAR_TYPE_4` than just `4`, -since `4` will be used in other places and you can't easily see where else the enum value is used. - -* Don't just copy paste code from IDA, make it look nice - -* Use the right types. In particular: - - * don't use types like `__int16`, we have `int16` for that - - * don't use `unsigned`, we have typedefs for that - - * don't use `char` for anything but actual characters, use `int8`, `uint8` or `bool` - - * don't even think about using win32 types (`BYTE`, `WORD`, &c.) unless you're writing win32 specific code - - * declare pointers like `int *ptr;`, not `int* ptr;` - -* As for variable names, the original gta source code was not written in a uniform style, -but here are some observations: - - * many variables employ a form of hungarian notation, i.e.: - - * `m_` may be used for class member variables (mostly those that are considered private) - - * `ms_` for (mostly private) static members - - * `f` is a float, `i` or `n` is an integer, `b` is a boolean, `a` is an array - - * do *not* use `dw` for `DWORD` or so, we're not programming win32 - -* Generally, try to make the code look as if R* could have written it diff --git a/README.md b/README.md index 35ab7f09..1f696bf0 100644 --- a/README.md +++ b/README.md @@ -28,12 +28,12 @@ You will also need the following tools installed ### Cloning the dca3-game repo and downloading the prebuilt elf - Open dreamsdk shell -- type `git clone https://gitlab.com/skmp/dca3-game.git` (and press enter) +- type `git clone --branch alpha https://gitlab.com/skmp/dca3-game.git` (and press enter) - It should take a moment and successfully clone the repo - type `cd dca3-game/dreamcast` (and press enter) - type `explorer .` (and press enter). - A folder named dreamcast with some files should be open. Keep it on the side. -- Download the artifacts from https://gitlab.com/skmp/dca3-game/-/jobs/8725216645 +- Download the Alpha Prebuilt Elf from https://gitlab.com/skmp/dca3-game/-/jobs/8725216645 - Open artifacts.zip and extract dca3.elf to the folder that was kept open before. - Close the folder and dreamsdk shell diff --git a/autoconf/LICENSE.txt b/autoconf/LICENSE.txt deleted file mode 100644 index eb1b1720..00000000 --- a/autoconf/LICENSE.txt +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2016 Blizzard Entertainment and individual contributors. -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - 3. Neither the name of Premake nor the names of its contributors may be - used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/autoconf/api.lua b/autoconf/api.lua deleted file mode 100644 index 064ea795..00000000 --- a/autoconf/api.lua +++ /dev/null @@ -1,305 +0,0 @@ ---- --- Autoconfiguration. --- Copyright (c) 2016 Blizzard Entertainment --- Enhanced by re3 ---- -local p = premake -local autoconf = p.modules.autoconf -autoconf.cache = {} -autoconf.parameters = "" - - ---- --- register autoconfigure api. ---- -p.api.register { - name = "autoconfigure", - scope = "config", - kind = "table" -} - ---- --- Check for a particular include file. --- --- @cfg : Current config. --- @variable : The variable to store the result, such as 'HAVE_STDINT_H'. --- @filename : The header file to check for. ---- -function check_include(cfg, variable, filename) - local res = autoconf.cache_compile(cfg, variable, function () - p.outln('#include <' .. filename .. '>') - p.outln('int main(void) { return 0; }') - end) - - if res.value then - autoconf.set_value(cfg, variable, 1) - end -end - - ---- --- Check for size of a particular type. --- --- @cfg : Current config. --- @variable : The variable to use, such as 'SIZEOF_SIZE_T', this method will also add "'HAVE_' .. variable". --- @type : The type to check. --- @headers : An optional array of header files to include. --- @defines : An optional array of defines to define. ---- -function check_type_size(cfg, variable, type, headers, defines) - check_include(cfg, 'HAVE_SYS_TYPES_H', 'sys/types.h') - check_include(cfg, 'HAVE_STDINT_H', 'stdint.h') - check_include(cfg, 'HAVE_STDDEF_H', 'stddef.h') - - local res = autoconf.cache_compile(cfg, variable .. cfg.platform, - function () - if cfg.autoconf['HAVE_SYS_TYPES_H'] then - p.outln('#include ') - end - - if cfg.autoconf['HAVE_STDINT_H'] then - p.outln('#include ') - end - - if cfg.autoconf['HAVE_STDDEF_H'] then - p.outln('#include ') - end - - autoconf.include_defines(defines) - autoconf.include_headers(headers) - p.outln("") - p.outln("#define SIZE (sizeof(" .. type .. "))") - p.outln("char info_size[] = {'I', 'N', 'F', 'O', ':', 's','i','z','e','[',") - p.outln(" ('0' + ((SIZE / 10000)%10)),") - p.outln(" ('0' + ((SIZE / 1000)%10)),") - p.outln(" ('0' + ((SIZE / 100)%10)),") - p.outln(" ('0' + ((SIZE / 10)%10)),") - p.outln(" ('0' + (SIZE %10)),") - p.outln(" ']', '\\0'};") - p.outln("") - p.outln("int main(int argc, char *argv[]) {") - p.outln(" int require = 0;") - p.outln(" require += info_size[argc];") - p.outln(" (void)argv;") - p.outln(" return require;") - p.outln("}") - end, - function (e) - -- if the compile step succeeded, we should have a binary with 'INFO:size[*****]' - -- somewhere in there. - local content = io.readfile(e.binary) - if content then - local size = string.find(content, 'INFO:size') - if size then - e.size = tonumber(string.sub(content, size+10, size+14)) - end - end - end - ) - - if res.size then - autoconf.set_value(cfg, 'HAVE_' .. variable, 1) - autoconf.set_value(cfg, variable, res.size) - end -end - - ---- --- Check if the given struct or class has the specified member variable --- --- @cfg : current config. --- @variable : variable to store the result. --- @type : the name of the struct or class you are interested in --- @member : the member which existence you want to check --- @headers : an optional array of header files to include. --- @defines : An optional array of defines to define. ---- -function check_struct_has_member(cfg, variable, type, member, headers, defines) - local res = autoconf.cache_compile(cfg, variable, function () - autoconf.include_defines(defines) - autoconf.include_headers(headers) - p.outln('int main(void) {') - p.outln(' (void)sizeof(((' .. type .. '*)0)->' .. member ..');') - p.outln(' return 0;') - p.outln('}') - end) - - if res.value then - autoconf.set_value(cfg, variable, 1) - end -end - - ---- --- Check if a symbol exists as a function, variable, or macro --- --- @cfg : current config. --- @variable : variable to store the result. --- @symbol : The symbol to check for. --- @headers : an optional array of header files to include. --- @defines : An optional array of defines to define. ---- -function check_symbol_exists(cfg, variable, symbol, headers, defines) - local h = headers - local res = autoconf.cache_compile(cfg, variable, function () - autoconf.include_defines(defines) - autoconf.include_headers(headers) - p.outln('int main(int argc, char** argv) {') - p.outln(' (void)argv;') - p.outln('#ifndef ' .. symbol) - p.outln(' return ((int*)(&' .. symbol .. '))[argc];') - p.outln('#else') - p.outln(' (void)argc;') - p.outln(' return 0;') - p.outln('#endif') - p.outln('}') - end) - - if res.value then - autoconf.set_value(cfg, variable, 1) - end -end - - ---- --- try compiling a piece of c/c++ ---- -function autoconf.try_compile(cfg, cpp) - local ts = autoconf.toolset(cfg) - if ts then - return ts.try_compile(cfg, cpp, autoconf.parameters) - else - p.warnOnce('autoconf', 'no toolset found, autoconf always failing.') - end -end - - -function autoconf.cache_compile(cfg, entry, func, post) - if not autoconf.cache[entry] then - local cpp = p.capture(func) - local res = autoconf.try_compile(cfg, cpp) - if res then - local e = { binary = res, value = true } - if post then - post(e) - end - autoconf.cache[entry] = e - else - autoconf.cache[entry] = { } - end - end - return autoconf.cache[entry] -end - - ---- --- get the current configured toolset, or the default. ---- -function autoconf.toolset(cfg) - local ts = p.config.toolset(cfg) - if not ts then - local tools = { - -- Actually we always return nil on msc. see msc.lua - ['vs2010'] = p.tools.msc, - ['vs2012'] = p.tools.msc, - ['vs2013'] = p.tools.msc, - ['vs2015'] = p.tools.msc, - ['vs2017'] = p.tools.msc, - ['vs2019'] = p.tools.msc, - ['gmake'] = premake.tools.gcc, - ['gmake2'] = premake.tools.gcc, - ['codelite'] = premake.tools.gcc, - ['xcode4'] = premake.tools.clang, - } - ts = tools[_ACTION] - end - return ts -end - - ---- --- store the value of the variable in the configuration ---- -function autoconf.set_value(cfg, variable, value) - cfg.autoconf[variable] = value -end - - ---- --- write the cfg.autoconf table to the file ---- -function autoconf.writefile(cfg, filename) - if cfg.autoconf then - local file = io.open(filename, "w+") - for variable, value in pairs(cfg.autoconf) do - file:write('#define ' .. variable .. ' ' .. tostring(value) .. (_eol or '\n')) - end - file:close() - end -end - - ---- --- Utility method to add a table of headers. ---- -function autoconf.include_headers(headers) - if headers ~= nil then - if type(headers) == "table" then - for _, v in ipairs(headers) do - p.outln('#include <' .. v .. '>') - end - else - p.outln('#include <' .. headers .. '>') - end - end -end - -function autoconf.include_defines(defines) - if defines ~= nil then - if type(defines) == "table" then - for _, v in ipairs(defines) do - p.outln('#define ' .. v) - end - else - p.outln('#define ' .. defines) - end - end -end - ---- --- attach ourselfs to the running action. ---- -p.override(p.action, 'call', function (base, name) - local a = p.action.get(name) - - -- store the old callback. - local onBaseProject = a.onProject or a.onproject - - -- override it with our own. - a.onProject = function(prj) - -- go through each configuration, and call the setup configuration methods. - for cfg in p.project.eachconfig(prj) do - cfg.autoconf = {} - if cfg.autoconfigure then - verbosef('Running auto config steps for "%s/%s".', prj.name, cfg.name) - for file, func in pairs(cfg.autoconfigure) do - func(cfg) - - if not (file ~= "dontWrite") then - os.mkdir(cfg.objdir) - local filename = path.join(cfg.objdir, file) - autoconf.writefile(cfg, filename) - end - end - end - end - - -- then call the old onProject. - if onBaseProject then - onBaseProject(prj) - end - end - - -- now call the original action.call methods - base(name) -end) diff --git a/autoconf/autoconf.lua b/autoconf/autoconf.lua deleted file mode 100644 index 6c99f9da..00000000 --- a/autoconf/autoconf.lua +++ /dev/null @@ -1,18 +0,0 @@ ---- --- Autoconfiguration. --- Copyright (c) 2016 Blizzard Entertainment ---- - local p = premake - - if not premake.modules.autoconf then - p.modules.autoconf = {} - p.modules.autoconf._VERSION = p._VERSION - - verbosef('Loading autoconf module...') - include('api.lua') - include('msc.lua') - include('clang.lua') - include('gcc.lua') - end - - return p.modules.autoconf diff --git a/autoconf/clang.lua b/autoconf/clang.lua deleted file mode 100644 index fdb5f405..00000000 --- a/autoconf/clang.lua +++ /dev/null @@ -1,27 +0,0 @@ ---- --- Autoconfiguration. --- Copyright (c) 2016 Blizzard Entertainment ---- -local p = premake -local clang = p.tools.clang - -function clang.try_compile(cfg, text, parameters) - -- write the text to a temporary file. - local cppFile = path.join(cfg.objdir, "temp.cpp") - if not io.writefile(cppFile, text) then - return nil - end - - if parameters == nil then - parameters = "" - end - - local outFile = path.join(cfg.objdir, "temp.out") - - -- compile that text file. - if os.execute('clang "' .. cppFile .. '" ' .. parameters .. ' -o "' .. outFile ..'" &> /dev/null') then - return outFile - else - return nil - end -end diff --git a/autoconf/gcc.lua b/autoconf/gcc.lua deleted file mode 100644 index 34520139..00000000 --- a/autoconf/gcc.lua +++ /dev/null @@ -1,27 +0,0 @@ ---- --- Autoconfiguration. --- Copyright (c) 2016 Blizzard Entertainment ---- -local p = premake -local gcc = p.tools.gcc - -function gcc.try_compile(cfg, text, parameters) - -- write the text to a temporary file. - local cppFile = path.join(cfg.objdir, "temp.cpp") - if not io.writefile(cppFile, text) then - return nil - end - - if parameters == nil then - parameters = "" - end - - local outFile = path.join(cfg.objdir, "temp.out") - - -- compile that text file. - if os.execute('gcc "' .. cppFile .. '" ' .. parameters .. ' -o "' .. outFile ..'" &> /dev/null') then - return outFile - else - return nil - end -end diff --git a/autoconf/msc.lua b/autoconf/msc.lua deleted file mode 100644 index b96a82ec..00000000 --- a/autoconf/msc.lua +++ /dev/null @@ -1,62 +0,0 @@ ---- --- Autoconfiguration. --- Copyright (c) 2016 Blizzard Entertainment ---- -local p = premake -local msc = p.tools.msc - --- "parameters" is unused, matter of fact this file is unused - re3 -function msc.try_compile(cfg, text, parameters) - - return nil ---[[ - -- write the text to a temporary file. - local cppFile = path.join(cfg.objdir, "temp.cpp") - if not io.writefile(cppFile, text) then - return nil - end - - -- write out a batch file. - local batch = p.capture(function () - p.outln('@echo off') - p.outln('SET mypath=%~dp0') - p.outln('pushd %mypath%') - - local map = { - vs2010 = 'VS100COMNTOOLS', - vs2012 = 'VS110COMNTOOLS', - vs2013 = 'VS120COMNTOOLS', - vs2015 = 'VS140COMNTOOLS', - vs2017 = 'VS141COMNTOOLS', - vs2019 = 'VS142COMNTOOLS', - } - - local a = map[_ACTION] - if a then - a = path.translate(os.getenv(a), '/') - a = path.join(a, '../../VC/vcvarsall.bat') - - if cfg.platform == 'x86' then - p.outln('call "' .. a .. '" > NUL') - else - p.outln('call "' .. a .. '" amd64 > NUL') - end - - p.outln('cl.exe /nologo temp.cpp > NUL') - else - error('Unsupported Visual Studio version: ' .. _ACTION) - end - end) - - local batchFile = path.join(cfg.objdir, "compile.bat") - if not io.writefile(batchFile, batch) then - return nil - end - - if os.execute(batchFile) then - return path.join(cfg.objdir, "temp.exe") - else - return nil - end ---]] -end diff --git a/cmake/FindMilesSDK.cmake b/cmake/FindMilesSDK.cmake deleted file mode 100644 index dcf4da33..00000000 --- a/cmake/FindMilesSDK.cmake +++ /dev/null @@ -1,34 +0,0 @@ -# - Find Miles SDK -# Find the Miles SDK header + import library -# -# MilesSDK_INCLUDE_DIR - Where to find mss.h -# MilesSDK_LIBRARIES - List of libraries when using MilesSDK. -# MilesSDK_FOUND - True if Miles SDK found. -# MilesSDK::MilesSDK - Imported library of Miles SDK - -find_path(MilesSDK_INCLUDE_DIR mss.h - PATHS "${MilesSDK_DIR}" - PATH_SUFFIXES include -) - -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_miles_sdk_libname mss64) -else() - set(_miles_sdk_libname mss32) -endif() - -find_library(MilesSDK_LIBRARIES NAMES ${_miles_sdk_libname} - PATHS "${MilesSDK_DIR}" - PATH_SUFFIXES lib -) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(MilesSDK DEFAULT_MSG MilesSDK_LIBRARIES MilesSDK_INCLUDE_DIR) - -if(NOT TARGET MilesSDK::MilesSDK) - add_library(MilesSDK::MilesSDK UNKNOWN IMPORTED) - set_target_properties(MilesSDK::MilesSDK PROPERTIES - IMPORTED_LOCATION "${MilesSDK_LIBRARIES}" - INTERFACE_INCLUDE_DIRECTORIES "${MilesSDK_INCLUDE_DIR}" - ) -endif() diff --git a/cmake/FindSndFile.cmake b/cmake/FindSndFile.cmake deleted file mode 100644 index 5381af48..00000000 --- a/cmake/FindSndFile.cmake +++ /dev/null @@ -1,67 +0,0 @@ -# Found on http://hg.kvats.net -# -# - Try to find libsndfile -# -# Once done this will define -# -# SNDFILE_FOUND - system has libsndfile -# SNDFILE_INCLUDE_DIRS - the libsndfile include directory -# SNDFILE_LIBRARIES - Link these to use libsndfile -# SNDFILE_CFLAGS - Compile options to use libsndfile -# SndFile::SndFile - Imported library of libsndfile -# -# Copyright (C) 2006 Wengo -# -# Redistribution and use is allowed according to the terms of the New -# BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. -# - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_search_module(PKG_SNDFILE "sndfile") -endif() - -find_path(SNDFILE_INCLUDE_DIR - NAMES - sndfile.h - HINTS - ${PKG_SNDFILE_INCLUDE_DIRS} - PATHS - /usr/include - /usr/local/include - /opt/local/include - /sw/include - ) - -find_library(SNDFILE_LIBRARY - NAMES - sndfile - HINTS - ${PKG_SNDFILE_LIBRARIES} - PATHS - /usr/lib - /usr/local/lib - /opt/local/lib - /sw/lib -) - -set(SNDFILE_CFLAGS "${PKG_SNDFILE_CFLAGS_OTHER}" CACHE STRING "CFLAGS of libsndfile") - -set(SNDFILE_INCLUDE_DIRS "${SNDFILE_INCLUDE_DIR}") -set(SNDFILE_LIBRARIES "${SNDFILE_LIBRARY}") - -if(SNDFILE_INCLUDE_DIRS AND SNDFILE_LIBRARIES) - set(SNDFILE_FOUND TRUE) -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(SndFile DEFAULT_MSG SNDFILE_INCLUDE_DIRS SNDFILE_LIBRARIES) - -if(NOT TARGET SndFile::SndFile) - add_library(__SndFile INTERFACE) - target_compile_options(__SndFile INTERFACE ${SNDFILE_CFLAGS}) - target_include_directories(__SndFile INTERFACE ${SNDFILE_INCLUDE_DIRS}) - target_link_libraries(__SndFile INTERFACE ${SNDFILE_LIBRARIES}) - add_library(SndFile::SndFile ALIAS __SndFile) -endif() diff --git a/cmake/Findmpg123.cmake b/cmake/Findmpg123.cmake deleted file mode 100644 index aa59ad82..00000000 --- a/cmake/Findmpg123.cmake +++ /dev/null @@ -1,38 +0,0 @@ -# - Find mpg123 -# Find the native mpg123 includes and library -# -# mpg123_INCLUDE_DIR - Where to find mpg123.h -# mpg123_LIBRARIES - List of libraries when using mpg123. -# mpg123_CFLAGS - Compile options to use mpg123 -# mpg123_FOUND - True if mpg123 found. -# MPG123::libmpg123 - Imported library of libmpg123 - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_search_module(PKG_MPG123 mpg123) -endif() - -find_path(mpg123_INCLUDE_DIR mpg123.h - HINTS ${PKG_MPG123_INCLUDE_DIRS} - PATHS "${mpg123_DIR}" - PATH_SUFFIXES include -) - -find_library(mpg123_LIBRARIES NAMES mpg123 mpg123-0 libmpg123-0 - HINTS ${PKG_MPG123_LIBRARIES} - PATHS "${mpg123_DIR}" - PATH_SUFFIXES lib -) - -set(mpg123_CFLAGS "${PKG_MPG123_CFLAGS_OTHER}" CACHE STRING "CFLAGS of mpg123") - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(mpg123 DEFAULT_MSG mpg123_LIBRARIES mpg123_INCLUDE_DIR) - -if(NOT TARGET MPG123::libmpg123) - add_library(__libmpg123 INTERFACE) - target_compile_options(__libmpg123 INTERFACE ${mpg123_CFLAGS}) - target_include_directories(__libmpg123 INTERFACE ${mpg123_INCLUDE_DIR}) - target_link_libraries(__libmpg123 INTERFACE ${mpg123_LIBRARIES}) - add_library(MPG123::libmpg123 ALIAS __libmpg123) -endif() diff --git a/cmake/Findopusfile.cmake b/cmake/Findopusfile.cmake deleted file mode 100644 index faae7645..00000000 --- a/cmake/Findopusfile.cmake +++ /dev/null @@ -1,64 +0,0 @@ -# - Try to find opusfile -# -# Once done this will define -# -# OPUSFILE_FOUND - system has opusfile -# OPUSFILE_INCLUDE_DIRS - the opusfile include directories -# OPUSFILE_LIBRARIES - Link these to use opusfile -# OPUSFILE_CFLAGS - Compile options to use opusfile -# opusfile::opusfile - Imported library of opusfile -# - -# FIXME: opusfile does not ship an official opusfile cmake script, -# rename this file/variables/target when/if it has. - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_search_module(PKG_OPUSFILE "opusfile") -endif() - -find_path(OPUSFILE_INCLUDE_DIR - NAMES - opusfile.h - PATH_SUFFIXES - opusfile - HINTS - ${PKG_OPUSFILE_INCLUDE_DIRS} - PATHS - /usr/include - /usr/local/include - /opt/local/include - /sw/include - ) - -find_library(OPUSFILE_LIBRARY - NAMES - opusfile - HINTS - ${PKG_OPUSFILE_LIBRARIES} - PATHS - /usr/lib - /usr/local/lib - /opt/local/lib - /sw/lib -) - -set(OPUSFILE_CFLAGS "${PKG_OPUSFILE_CFLAGS_OTHER}" CACHE STRING "CFLAGS of opusfile") - -set(OPUSFILE_INCLUDE_DIRS "${OPUSFILE_INCLUDE_DIR}") -set(OPUSFILE_LIBRARIES "${OPUSFILE_LIBRARY}") - -if (OPUSFILE_INCLUDE_DIRS AND OPUSFILE_LIBRARIES) -set(OPUSFILE_FOUND TRUE) -endif (OPUSFILE_INCLUDE_DIRS AND OPUSFILE_LIBRARIES) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(opusfile DEFAULT_MSG OPUSFILE_INCLUDE_DIRS OPUSFILE_LIBRARIES) - -if(NOT TARGET opusfile::opusfile) - add_library(__opusfile INTERFACE) - target_compile_options(__opusfile INTERFACE ${OPUSFILE_CFLAGS}) - target_include_directories(__opusfile INTERFACE ${OPUSFILE_INCLUDE_DIRS}) - target_link_libraries(__opusfile INTERFACE ${OPUSFILE_LIBRARIES}) - add_library(opusfile::opusfile ALIAS __opusfile) -endif() diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake deleted file mode 100644 index 87f691ad..00000000 --- a/cmake/GetGitRevisionDescription.cmake +++ /dev/null @@ -1,284 +0,0 @@ -# - Returns a version string from Git -# -# These functions force a re-configure on each git commit so that you can -# trust the values of the variables in your build system. -# -# get_git_head_revision( [ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR]) -# -# Returns the refspec and sha hash of the current head revision -# -# git_describe( [ ...]) -# -# Returns the results of git describe on the source tree, and adjusting -# the output so that it tests false if an error occurs. -# -# git_describe_working_tree( [ ...]) -# -# Returns the results of git describe on the working tree (--dirty option), -# and adjusting the output so that it tests false if an error occurs. -# -# git_get_exact_tag( [ ...]) -# -# Returns the results of git describe --exact-match on the source tree, -# and adjusting the output so that it tests false if there was no exact -# matching tag. -# -# git_local_changes() -# -# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes. -# Uses the return code of "git diff-index --quiet HEAD --". -# Does not regard untracked files. -# -# Requires CMake 2.6 or newer (uses the 'function' command) -# -# Original Author: -# 2009-2020 Ryan Pavlik -# http://academic.cleardefinition.com -# -# Copyright 2009-2013, Iowa State University. -# Copyright 2013-2020, Ryan Pavlik -# Copyright 2013-2020, Contributors -# SPDX-License-Identifier: BSL-1.0 -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at -# http://www.boost.org/LICENSE_1_0.txt) - -if(__get_git_revision_description) - return() -endif() -set(__get_git_revision_description YES) - -# We must run the following at "include" time, not at function call time, -# to find the path to this module rather than the path to a calling list file -get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) - -# Function _git_find_closest_git_dir finds the next closest .git directory -# that is part of any directory in the path defined by _start_dir. -# The result is returned in the parent scope variable whose name is passed -# as variable _git_dir_var. If no .git directory can be found, the -# function returns an empty string via _git_dir_var. -# -# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and -# neither foo nor bar contain a file/directory .git. This wil return -# C:/bla/.git -# -function(_git_find_closest_git_dir _start_dir _git_dir_var) - set(cur_dir "${_start_dir}") - set(git_dir "${_start_dir}/.git") - while(NOT EXISTS "${git_dir}") - # .git dir not found, search parent directories - set(git_previous_parent "${cur_dir}") - get_filename_component(cur_dir ${cur_dir} DIRECTORY) - if(cur_dir STREQUAL git_previous_parent) - # We have reached the root directory, we are not in git - set(${_git_dir_var} - "" - PARENT_SCOPE) - return() - endif() - set(git_dir "${cur_dir}/.git") - endwhile() - set(${_git_dir_var} - "${git_dir}" - PARENT_SCOPE) -endfunction() - -function(get_git_head_revision _refspecvar _hashvar) - _git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR) - - if("${ARGN}" STREQUAL "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR") - set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR TRUE) - else() - set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR FALSE) - endif() - if(NOT "${GIT_DIR}" STREQUAL "") - file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}" - "${GIT_DIR}") - if("${_relative_to_source_dir}" MATCHES "[.][.]" AND NOT ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR) - # We've gone above the CMake root dir. - set(GIT_DIR "") - endif() - endif() - if("${GIT_DIR}" STREQUAL "") - set(${_refspecvar} - "GITDIR-NOTFOUND" - PARENT_SCOPE) - set(${_hashvar} - "GITDIR-NOTFOUND" - PARENT_SCOPE) - return() - endif() - - # Check if the current source dir is a git submodule or a worktree. - # In both cases .git is a file instead of a directory. - # - if(NOT IS_DIRECTORY ${GIT_DIR}) - # The following git command will return a non empty string that - # points to the super project working tree if the current - # source dir is inside a git submodule. - # Otherwise the command will return an empty string. - # - execute_process( - COMMAND "${GIT_EXECUTABLE}" rev-parse - --show-superproject-working-tree - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - OUTPUT_VARIABLE out - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - if(NOT "${out}" STREQUAL "") - # If out is empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule - file(READ ${GIT_DIR} submodule) - string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE - ${submodule}) - string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE) - get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) - get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} - ABSOLUTE) - set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") - else() - # GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree - file(READ ${GIT_DIR} worktree_ref) - # The .git directory contains a path to the worktree information directory - # inside the parent git repo of the worktree. - # - string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir - ${worktree_ref}) - string(STRIP ${git_worktree_dir} git_worktree_dir) - _git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR) - set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD") - endif() - else() - set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") - endif() - set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") - if(NOT EXISTS "${GIT_DATA}") - file(MAKE_DIRECTORY "${GIT_DATA}") - endif() - - if(NOT EXISTS "${HEAD_SOURCE_FILE}") - return() - endif() - set(HEAD_FILE "${GIT_DATA}/HEAD") - configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY) - - configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" - "${GIT_DATA}/grabRef.cmake" @ONLY) - include("${GIT_DATA}/grabRef.cmake") - - set(${_refspecvar} - "${HEAD_REF}" - PARENT_SCOPE) - set(${_hashvar} - "${HEAD_HASH}" - PARENT_SCOPE) -endfunction() - -function(git_describe _var) - if(NOT GIT_FOUND) - find_package(Git QUIET) - endif() - get_git_head_revision(refspec hash) - if(NOT GIT_FOUND) - set(${_var} - "GIT-NOTFOUND" - PARENT_SCOPE) - return() - endif() - if(NOT hash) - set(${_var} - "HEAD-HASH-NOTFOUND" - PARENT_SCOPE) - return() - endif() - - # TODO sanitize - #if((${ARGN}" MATCHES "&&") OR - # (ARGN MATCHES "||") OR - # (ARGN MATCHES "\\;")) - # message("Please report the following error to the project!") - # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") - #endif() - - #message(STATUS "Arguments to execute_process: ${ARGN}") - - execute_process( - COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN} - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - RESULT_VARIABLE res - OUTPUT_VARIABLE out - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - if(NOT res EQUAL 0) - set(out "${out}-${res}-NOTFOUND") - endif() - - set(${_var} - "${out}" - PARENT_SCOPE) -endfunction() - -function(git_describe_working_tree _var) - if(NOT GIT_FOUND) - find_package(Git QUIET) - endif() - if(NOT GIT_FOUND) - set(${_var} - "GIT-NOTFOUND" - PARENT_SCOPE) - return() - endif() - - execute_process( - COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN} - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - RESULT_VARIABLE res - OUTPUT_VARIABLE out - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - if(NOT res EQUAL 0) - set(out "${out}-${res}-NOTFOUND") - endif() - - set(${_var} - "${out}" - PARENT_SCOPE) -endfunction() - -function(git_get_exact_tag _var) - git_describe(out --exact-match ${ARGN}) - set(${_var} - "${out}" - PARENT_SCOPE) -endfunction() - -function(git_local_changes _var) - if(NOT GIT_FOUND) - find_package(Git QUIET) - endif() - get_git_head_revision(refspec hash) - if(NOT GIT_FOUND) - set(${_var} - "GIT-NOTFOUND" - PARENT_SCOPE) - return() - endif() - if(NOT hash) - set(${_var} - "HEAD-HASH-NOTFOUND" - PARENT_SCOPE) - return() - endif() - - execute_process( - COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD -- - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - RESULT_VARIABLE res - OUTPUT_VARIABLE out - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - if(res EQUAL 0) - set(${_var} - "CLEAN" - PARENT_SCOPE) - else() - set(${_var} - "DIRTY" - PARENT_SCOPE) - endif() -endfunction() diff --git a/cmake/GetGitRevisionDescription.cmake.in b/cmake/GetGitRevisionDescription.cmake.in deleted file mode 100644 index 116efc4e..00000000 --- a/cmake/GetGitRevisionDescription.cmake.in +++ /dev/null @@ -1,43 +0,0 @@ -# -# Internal file for GetGitRevisionDescription.cmake -# -# Requires CMake 2.6 or newer (uses the 'function' command) -# -# Original Author: -# 2009-2010 Ryan Pavlik -# http://academic.cleardefinition.com -# Iowa State University HCI Graduate Program/VRAC -# -# Copyright 2009-2012, Iowa State University -# Copyright 2011-2015, Contributors -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at -# http://www.boost.org/LICENSE_1_0.txt) -# SPDX-License-Identifier: BSL-1.0 - -set(HEAD_HASH) - -file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) - -string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) -if(HEAD_CONTENTS MATCHES "ref") - # named branch - string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") - if(EXISTS "@GIT_DIR@/${HEAD_REF}") - configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) - else() - configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) - file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) - if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") - set(HEAD_HASH "${CMAKE_MATCH_1}") - endif() - endif() -else() - # detached HEAD - configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) -endif() - -if(NOT HEAD_HASH) - file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) - string(STRIP "${HEAD_HASH}" HEAD_HASH) -endif() diff --git a/cmake/nx/NXFunctions.cmake b/cmake/nx/NXFunctions.cmake deleted file mode 100644 index cf3f974b..00000000 --- a/cmake/nx/NXFunctions.cmake +++ /dev/null @@ -1,38 +0,0 @@ -if(NOT COMMAND nx_generate_nacp) - message(FATAL_ERROR "The `nx_generate_nacp` cmake command is not available. Please use an appropriate Nintendo Switch toolchain.") -endif() - -if(NOT COMMAND nx_create_nro) - message(FATAL_ERROR "The `nx_create_nro` cmake command is not available. Please use an appropriate Nintendo Switch toolchain.") -endif() - -set(CMAKE_EXECUTABLE_SUFFIX ".elf") - -function(re3_platform_target TARGET) - cmake_parse_arguments(RPT "INSTALL" "" "" ${ARGN}) - - get_target_property(TARGET_TYPE "${TARGET}" TYPE) - if(TARGET_TYPE STREQUAL "EXECUTABLE") - nx_generate_nacp(${TARGET}.nacp - NAME "${TARGET}" - AUTHOR "${${PROJECT}_AUTHOR}" - VERSION "1.0.0-${GIT_SHA1}" - ) - - nx_create_nro(${TARGET} - NACP ${TARGET}.nacp - ICON "${PROJECT_SOURCE_DIR}/res/images/logo_256.jpg" - ) - - if(${PROJECT}_INSTALL AND RPT_INSTALL) - get_target_property(TARGET_OUTPUT_NAME ${TARGET} OUTPUT_NAME) - if(NOT TARGET_OUTPUT_NAME) - set(TARGET_OUTPUT_NAME "${TARGET}") - endif() - - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_OUTPUT_NAME}.nro" - DESTINATION "." - ) - endif() - endif() -endfunction() diff --git a/codewarrior/Debug/gta3.txt b/codewarrior/Debug/gta3.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/codewarrior/Release/gta3.txt b/codewarrior/Release/gta3.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/codewarrior/re3.mcp.xml b/codewarrior/re3.mcp.xml deleted file mode 100644 index 9a41471b..00000000 --- a/codewarrior/re3.mcp.xml +++ /dev/null @@ -1,15378 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]> - - - - - Debug - - - - UserSourceTrees - - - AlwaysSearchUserPathsfalse - InterpretDOSAndUnixPathstrue - RequireFrameworkStyleIncludesfalse - UserSearchPaths - - SearchPath - Path - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\animation - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\audio - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\buildings - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\collision - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\control - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\core - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\entities - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\math - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\modelinfo - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\objects - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\peds - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\renderer - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\rw - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\save - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\skel - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\text - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\vehicles - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\weapons - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\vendor\milessdk\lib - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\vendor\milessdk\include - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\sdk\dx8sdk\Lib - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\sdk\rwsdk\lib\d3d8\release - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\sdk\rwsdk\include\d3d8 - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\extras - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SystemSearchPaths - - SearchPath - Path..\sdk\rwsdk\include\d3d8 - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\sdk\dx8sdk\Include - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - PathWin32-x86 Support\Headers\ - PathFormatWindows - PathRootCodeWarrior - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - PathWin32-x86 Support\Libraries\ - PathFormatWindows - PathRootCodeWarrior - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - PathMSL - PathFormatWindows - PathRootCodeWarrior - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\audio\eax - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - - - MWRuntimeSettings_WorkingDirectory - MWRuntimeSettings_CommandLine - MWRuntimeSettings_HostApplication - Path - PathFormatGeneric - PathRootAbsolute - - MWRuntimeSettings_EnvVars - - - LinkerWin32 x86 Linker - PreLinker - PostLinker - TargetnameDebug - OutputDirectory - Path - PathFormatWindows - PathRootProject - - SaveEntriesUsingRelativePathsfalse - - - FileMappings - - FileTypeTEXT - FileExtension.c - CompilerMW C/C++ x86 - EditLanguageC/C++ - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.c++ - CompilerMW C/C++ x86 - EditLanguageC/C++ - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.cc - CompilerMW C/C++ x86 - EditLanguageC/C++ - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.cp - CompilerMW C/C++ x86 - EditLanguageC/C++ - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.cpp - CompilerMW C/C++ x86 - EditLanguageC/C++ - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.def - Compiler - EditLanguage - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.h - CompilerMW C/C++ x86 - EditLanguageC/C++ - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMaketrue - - - FileTypeTEXT - FileExtension.p - CompilerMW Pascal x86 - EditLanguage - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.pas - CompilerMW Pascal x86 - EditLanguage - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.pch - CompilerMW C/C++ x86 - EditLanguageC/C++ - Precompiletrue - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.pch++ - CompilerMW C/C++ x86 - EditLanguageC/C++ - Precompiletrue - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.ppu - CompilerMW Pascal x86 - EditLanguage - Precompiletrue - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.rc - CompilerMW WinRC - EditLanguage - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.res - CompilerWinRes Import - EditLanguage - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileExtension.doc - Compiler - EditLanguage - Precompilefalse - Launchabletrue - ResourceFilefalse - IgnoredByMaketrue - - - FileExtension.lib - CompilerLib Import x86 - EditLanguage - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileExtension.obj - CompilerObj Import x86 - EditLanguage - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileExtension.res - CompilerWinRes Import - EditLanguage - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - - - CacheModDatestrue - ActivateBrowsertrue - DumpBrowserInfofalse - CacheSubprojectstrue - UseThirdPartyDebuggerfalse - DebuggerAppPath - Path - PathFormatGeneric - PathRootAbsolute - - DebuggerCmdLineArgs - DebuggerWorkingDir - Path - PathFormatGeneric - PathRootAbsolute - - - - LogSystemMessagesfalse - AutoTargetDLLsfalse - StopAtWatchpointstrue - PauseWhileRunningfalse - PauseInterval5 - PauseUIFlags0 - AltExePath - Path - PathFormatGeneric - PathRootAbsolute - - StopAtTempBPOnLaunchtrue - CacheSymbolicstrue - TempBPFunctionNamemain - TempBPType0 - - - Enabledfalse - ConnectionName - DownloadPath - LaunchRemoteAppfalse - RemoteAppPath - - - OtherExecutables - - - CustomColor1 - Red0 - Green32767 - Blue0 - - CustomColor2 - Red0 - Green32767 - Blue0 - - CustomColor3 - Red0 - Green32767 - Blue0 - - CustomColor4 - Red0 - Green32767 - Blue0 - - - - MWCodeGen_X86_processorPentiumII - MWCodeGen_X86_alignmentbytes8 - MWCodeGen_X86_exceptionsZeroOverhead - MWCodeGen_X86_extinst_mmx0 - MWCodeGen_X86_extinst_3dnow0 - MWCodeGen_X86_use_mmx_3dnow_convention0 - MWCodeGen_X86_machinecodelisting0 - MWCodeGen_X86_intrinsics0 - MWCodeGen_X86_syminfo0 - MWCodeGen_X86_codeviewinfo1 - MWCodeGen_X86_extinst_cmov_fcomi0 - MWCodeGen_X86_extinst_sse0 - - - MWDebugger_X86_Exceptions - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - - - - PDisasmX86_showHeaderstrue - PDisasmX86_showSymTabtrue - PDisasmX86_showCodetrue - PDisasmX86_showSourcefalse - PDisasmX86_showHextrue - PDisasmX86_showRelocationtrue - PDisasmX86_showCommentsfalse - PDisasmX86_showDebugfalse - PDisasmX86_showExceptionsfalse - PDisasmX86_showDatatrue - PDisasmX86_showRawfalse - PDisasmX86_verbosefalse - - - MWFrontEnd_C_cplusplus1 - MWFrontEnd_C_checkprotos0 - MWFrontEnd_C_arm0 - MWFrontEnd_C_trigraphs0 - MWFrontEnd_C_onlystdkeywords0 - MWFrontEnd_C_enumsalwaysint1 - MWFrontEnd_C_mpwpointerstyle0 - MWFrontEnd_C_prefixname - MWFrontEnd_C_ansistrict0 - MWFrontEnd_C_mpwcnewline0 - MWFrontEnd_C_wchar_type1 - MWFrontEnd_C_enableexceptions1 - MWFrontEnd_C_dontreusestrings0 - MWFrontEnd_C_poolstrings1 - MWFrontEnd_C_dontinline1 - MWFrontEnd_C_useRTTI1 - MWFrontEnd_C_multibyteaware1 - MWFrontEnd_C_unsignedchars0 - MWFrontEnd_C_autoinline0 - MWFrontEnd_C_booltruefalse1 - MWFrontEnd_C_direct_to_som0 - MWFrontEnd_C_som_env_check0 - MWFrontEnd_C_alwaysinline0 - MWFrontEnd_C_inlinelevel0 - MWFrontEnd_C_ecplusplus0 - MWFrontEnd_C_objective_c0 - MWFrontEnd_C_defer_codegen0 - - - MWLinker_X86_entrypointusageDefault - MWLinker_X86_entrypoint - MWLinker_X86_subsystemWinGUI - MWLinker_X86_subsysmajorid4 - MWLinker_X86_subsysminorid0 - MWLinker_X86_usrmajorid0 - MWLinker_X86_usrminorid0 - MWLinker_X86_commandfile - MWLinker_X86_generatemap0 - MWLinker_X86_linksym0 - MWLinker_X86_linkCV1 - - - MWProject_X86_typeApplication - MWProject_X86_outfileDebug\gta3.exe - MWProject_X86_baseaddress4194304 - MWProject_X86_maxstacksize1024 - MWProject_X86_minstacksize4 - MWProject_X86_size1024 - MWProject_X86_minsize4 - MWProject_X86_importlib - - - MWWarning_C_warn_illpragma0 - MWWarning_C_warn_emptydecl0 - MWWarning_C_warn_possunwant1 - MWWarning_C_warn_unusedvar1 - MWWarning_C_warn_unusedarg0 - MWWarning_C_warn_extracomma1 - MWWarning_C_pedantic0 - MWWarning_C_warningerrors0 - MWWarning_C_warn_hidevirtual1 - MWWarning_C_warn_implicitconv0 - MWWarning_C_warn_notinlined0 - MWWarning_C_warn_structclass0 - - - MWWinRC_prefixnameResourcePrefix.h - - - GlobalOptimizer_X86__optimizationlevelLevel0 - GlobalOptimizer_X86__optforSize - - - - Name - Comdlg32.lib - MacOS - Library - - - - Name - Gdi32.lib - MacOS - Library - - - - Name - Kernel32.lib - MacOS - Library - - - - Name - User32.lib - MacOS - Library - - - - Name - MSL_All_x86_D.lib - MacOS - Unknown - Debug - - - Name - AnimationId.h - Windows - Text - - - - Name - AnimBlendAssocGroup.cpp - Windows - Text - Debug - - - Name - AnimBlendAssocGroup.h - Windows - Text - - - - Name - AnimBlendAssociation.cpp - Windows - Text - Debug - - - Name - AnimBlendAssociation.h - Windows - Text - - - - Name - AnimBlendClumpData.cpp - Windows - Text - Debug - - - Name - AnimBlendClumpData.h - Windows - Text - - - - Name - AnimBlendHierarchy.cpp - Windows - Text - Debug - - - Name - AnimBlendHierarchy.h - Windows - Text - - - - Name - AnimBlendList.h - Windows - Text - - - - Name - AnimBlendNode.cpp - Windows - Text - Debug - - - Name - AnimBlendNode.h - Windows - Text - - - - Name - AnimBlendSequence.cpp - Windows - Text - Debug - - - Name - AnimBlendSequence.h - Windows - Text - - - - Name - AnimManager.cpp - Windows - Text - Debug - - - Name - AnimManager.h - Windows - Text - - - - Name - Bones.cpp - Windows - Text - Debug - - - Name - Bones.h - Windows - Text - - - - Name - CutsceneMgr.cpp - Windows - Text - Debug - - - Name - CutsceneMgr.h - Windows - Text - - - - Name - FrameUpdate.cpp - Windows - Text - Debug - - - Name - RpAnimBlend.cpp - Windows - Text - Debug - - - Name - RpAnimBlend.h - Windows - Text - - - - Name - audio_enums.h - Windows - Text - - - - Name - AudioCollision.cpp - Windows - Text - Debug - - - Name - AudioCollision.h - Windows - Text - - - - Name - AudioLogic.cpp - Windows - Text - - - - Name - AudioManager.cpp - Windows - Text - Debug - - - Name - AudioManager.h - Windows - Text - - - - Name - AudioSamples.h - Windows - Text - - - - Name - AudioScriptObject.cpp - Windows - Text - Debug - - - Name - AudioScriptObject.h - Windows - Text - - - - Name - DMAudio.cpp - Windows - Text - Debug - - - Name - DMAudio.h - Windows - Text - - - - Name - MusicManager.cpp - Windows - Text - Debug - - - Name - MusicManager.h - Windows - Text - - - - Name - PolRadio.cpp - Windows - Text - Debug - - - Name - PolRadio.h - Windows - Text - - - - Name - sampman.h - Windows - Text - - - - Name - sampman_miles.cpp - Windows - Text - Debug - - - Name - soundlist.h - Windows - Text - - - - Name - eax.h - Windows - Text - - - - Name - eax-util.cpp - Windows - Text - Debug - - - Name - eax-util.h - Windows - Text - - - - Name - Building.cpp - Windows - Text - Debug - - - Name - Building.h - Windows - Text - - - - Name - Solid.h - Windows - Text - - - - Name - Treadable.cpp - Windows - Text - Debug - - - Name - Treadable.h - Windows - Text - - - - Name - ColBox.cpp - Windows - Text - Debug - - - Name - ColBox.h - Windows - Text - - - - Name - ColLine.cpp - Windows - Text - Debug - - - Name - ColLine.h - Windows - Text - - - - Name - Collision.cpp - Windows - Text - Debug - - - Name - Collision.h - Windows - Text - - - - Name - ColModel.cpp - Windows - Text - Debug - - - Name - ColModel.h - Windows - Text - - - - Name - ColPoint.cpp - Windows - Text - Debug - - - Name - ColPoint.h - Windows - Text - - - - Name - ColSphere.cpp - Windows - Text - Debug - - - Name - ColSphere.h - Windows - Text - - - - Name - ColTriangle.cpp - Windows - Text - Debug - - - Name - ColTriangle.h - Windows - Text - - - - Name - CompressedVector.h - Windows - Text - - - - Name - TempColModels.cpp - Windows - Text - Debug - - - Name - TempColModels.h - Windows - Text - - - - Name - VuCollision.cpp - Windows - Text - Debug - - - Name - VuCollision.h - Windows - Text - - - - Name - AutoPilot.cpp - Windows - Text - Debug - - - Name - AutoPilot.h - Windows - Text - - - - Name - Bridge.cpp - Windows - Text - Debug - - - Name - Bridge.h - Windows - Text - - - - Name - CarAI.cpp - Windows - Text - Debug - - - Name - CarAI.h - Windows - Text - - - - Name - CarCtrl.cpp - Windows - Text - Debug - - - Name - CarCtrl.h - Windows - Text - - - - Name - Curves.cpp - Windows - Text - Debug - - - Name - Curves.h - Windows - Text - - - - Name - Darkel.cpp - Windows - Text - Debug - - - Name - Darkel.h - Windows - Text - - - - Name - GameLogic.cpp - Windows - Text - Debug - - - Name - GameLogic.h - Windows - Text - - - - Name - Garages.cpp - Windows - Text - Debug - - - Name - Garages.h - Windows - Text - - - - Name - NameGrid.cpp - Windows - Text - Debug - - - Name - NameGrid.h - Windows - Text - - - - Name - OnscreenTimer.cpp - Windows - Text - Debug - - - Name - OnscreenTimer.h - Windows - Text - - - - Name - PathFind.cpp - Windows - Text - Debug - - - Name - PathFind.h - Windows - Text - - - - Name - Phones.cpp - Windows - Text - Debug - - - Name - Phones.h - Windows - Text - - - - Name - Pickups.cpp - Windows - Text - Debug - - - Name - Pickups.h - Windows - Text - - - - Name - PowerPoints.cpp - Windows - Text - Debug - - - Name - PowerPoints.h - Windows - Text - - - - Name - Record.cpp - Windows - Text - Debug - - - Name - Record.h - Windows - Text - - - - Name - Remote.cpp - Windows - Text - Debug - - - Name - Remote.h - Windows - Text - - - - Name - Replay.cpp - Windows - Text - Debug - - - Name - Replay.h - Windows - Text - - - - Name - Restart.cpp - Windows - Text - Debug - - - Name - Restart.h - Windows - Text - - - - Name - RoadBlocks.cpp - Windows - Text - Debug - - - Name - RoadBlocks.h - Windows - Text - - - - Name - SceneEdit.cpp - Windows - Text - Debug - - - Name - SceneEdit.h - Windows - Text - - - - Name - ScriptDebug.cpp - Windows - Text - Debug - - - Name - Script.cpp - Windows - Text - Debug - - - Name - Script.h - Windows - Text - - - - Name - Script2.cpp - Windows - Text - Debug - - - Name - Script3.cpp - Windows - Text - Debug - - - Name - Script4.cpp - Windows - Text - Debug - - - Name - Script5.cpp - Windows - Text - Debug - - - Name - Script6.cpp - Windows - Text - Debug - - - Name - ScriptCommands.h - Windows - Text - - - - Name - TrafficLights.cpp - Windows - Text - Debug - - - Name - TrafficLights.h - Windows - Text - - - - Name - Accident.cpp - Windows - Text - Debug - - - Name - Accident.h - Windows - Text - - - - Name - AnimViewer.cpp - Windows - Text - Debug - - - Name - AnimViewer.h - Windows - Text - - - - Name - Cam.cpp - Windows - Text - Debug - - - Name - Camera.cpp - Windows - Text - Debug - - - Name - Camera.h - Windows - Text - - - - Name - CdStream.cpp - Windows - Text - Debug - - - Name - CdStream.h - Windows - Text - - - - Name - CdStreamPosix.cpp - Windows - Text - Debug - - - Name - Clock.cpp - Windows - Text - Debug - - - Name - Clock.h - Windows - Text - - - - Name - common.h - Windows - Text - - - - Name - config.h - Windows - Text - - - - Name - ControllerConfig.cpp - Windows - Text - Debug - - - Name - ControllerConfig.h - Windows - Text - - - - Name - Crime.h - Windows - Text - - - - Name - Debug.cpp - Windows - Text - Debug - - - Name - Debug.h - Windows - Text - - - - Name - Directory.cpp - Windows - Text - Debug - - - Name - Directory.h - Windows - Text - - - - Name - EventList.cpp - Windows - Text - Debug - - - Name - EventList.h - Windows - Text - - - - Name - FileLoader.cpp - Windows - Text - Debug - - - Name - FileLoader.h - Windows - Text - - - - Name - FileMgr.cpp - Windows - Text - Debug - - - Name - FileMgr.h - Windows - Text - - - - Name - Fire.cpp - Windows - Text - Debug - - - Name - Fire.h - Windows - Text - - - - Name - Frontend.cpp - Windows - Text - Debug - - - Name - Frontend.h - Windows - Text - - - - Name - Frontend_PS2.cpp - Windows - Text - Debug - - - Name - Frontend_PS2.h - Windows - Text - - - - Name - FrontEndControls.cpp - Windows - Text - Debug - - - Name - FrontEndControls.h - Windows - Text - - - - Name - FrontendTriggers.h - Windows - Text - - - - Name - Game.cpp - Windows - Text - Debug - - - Name - Game.h - Windows - Text - - - - Name - General.h - Windows - Text - - - - Name - IniFile.cpp - Windows - Text - Debug - - - Name - IniFile.h - Windows - Text - - - - Name - Lists.cpp - Windows - Text - Debug - - - Name - Lists.h - Windows - Text - - - - Name - main.cpp - Windows - Text - Debug - - - Name - main.h - Windows - Text - - - - Name - MenuScreens.cpp - Windows - Text - Debug - - - Name - MenuScreensCustom.cpp - Windows - Text - Debug - - - Name - obrstr.cpp - Windows - Text - Debug - - - Name - obrstr.h - Windows - Text - - - - Name - Pad.cpp - Windows - Text - Debug - - - Name - Pad.h - Windows - Text - - - - Name - Placeable.cpp - Windows - Text - Debug - - - Name - Placeable.h - Windows - Text - - - - Name - PlayerInfo.cpp - Windows - Text - Debug - - - Name - PlayerInfo.h - Windows - Text - - - - Name - Pools.cpp - Windows - Text - Debug - - - Name - Pools.h - Windows - Text - - - - Name - Profile.cpp - Windows - Text - Debug - - - Name - Profile.h - Windows - Text - - - - Name - Radar.cpp - Windows - Text - Debug - - - Name - Radar.h - Windows - Text - - - - Name - Range2D.cpp - Windows - Text - Debug - - - Name - Range2D.h - Windows - Text - - - - Name - Range3D.cpp - Windows - Text - Debug - - - Name - Range3D.h - Windows - Text - - - - Name - re3.cpp - Windows - Text - Debug - - - Name - References.cpp - Windows - Text - Debug - - - Name - References.h - Windows - Text - - - - Name - Stats.cpp - Windows - Text - Debug - - - Name - Stats.h - Windows - Text - - - - Name - Streaming.cpp - Windows - Text - Debug - - - Name - Streaming.h - Windows - Text - - - - Name - SurfaceTable.cpp - Windows - Text - Debug - - - Name - SurfaceTable.h - Windows - Text - - - - Name - templates.h - Windows - Text - - - - Name - timebars.cpp - Windows - Text - Debug - - - Name - timebars.h - Windows - Text - - - - Name - Timer.cpp - Windows - Text - Debug - - - Name - Timer.h - Windows - Text - - - - Name - TimeStep.cpp - Windows - Text - Debug - - - Name - TimeStep.h - Windows - Text - - - - Name - User.cpp - Windows - Text - Debug - - - Name - User.h - Windows - Text - - - - Name - Wanted.cpp - Windows - Text - Debug - - - Name - Wanted.h - Windows - Text - - - - Name - World.cpp - Windows - Text - Debug - - - Name - World.h - Windows - Text - - - - Name - ZoneCull.cpp - Windows - Text - Debug - - - Name - ZoneCull.h - Windows - Text - - - - Name - Zones.cpp - Windows - Text - Debug - - - Name - Zones.h - Windows - Text - - - - Name - Dummy.cpp - Windows - Text - Debug - - - Name - Dummy.h - Windows - Text - - - - Name - Entity.cpp - Windows - Text - Debug - - - Name - Entity.h - Windows - Text - - - - Name - Physical.cpp - Windows - Text - Debug - - - Name - Physical.h - Windows - Text - - - - Name - math.cpp - Windows - Text - Debug - - - Name - maths.h - Windows - Text - - - - Name - Matrix.cpp - Windows - Text - Debug - - - Name - Matrix.h - Windows - Text - - - - Name - Quaternion.cpp - Windows - Text - Debug - - - Name - Quaternion.h - Windows - Text - - - - Name - Rect.cpp - Windows - Text - Debug - - - Name - Rect.h - Windows - Text - - - - Name - Vector.cpp - Windows - Text - Debug - - - Name - Vector.h - Windows - Text - - - - Name - Vector2D.h - Windows - Text - - - - Name - VuVector.h - Windows - Text - - - - Name - BaseModelInfo.cpp - Windows - Text - Debug - - - Name - BaseModelInfo.h - Windows - Text - - - - Name - ClumpModelInfo.cpp - Windows - Text - Debug - - - Name - ClumpModelInfo.h - Windows - Text - - - - Name - MloModelInfo.cpp - Windows - Text - Debug - - - Name - MloModelInfo.h - Windows - Text - - - - Name - ModelIndices.cpp - Windows - Text - Debug - - - Name - ModelIndices.h - Windows - Text - - - - Name - ModelInfo.cpp - Windows - Text - Debug - - - Name - ModelInfo.h - Windows - Text - - - - Name - PedModelInfo.cpp - Windows - Text - Debug - - - Name - PedModelInfo.h - Windows - Text - - - - Name - SimpleModelInfo.cpp - Windows - Text - Debug - - - Name - SimpleModelInfo.h - Windows - Text - - - - Name - TimeModelInfo.cpp - Windows - Text - Debug - - - Name - TimeModelInfo.h - Windows - Text - - - - Name - VehicleModelInfo.cpp - Windows - Text - Debug - - - Name - VehicleModelInfo.h - Windows - Text - - - - Name - XtraCompsModelInfo.h - Windows - Text - - - - Name - CutsceneHead.cpp - Windows - Text - Debug - - - Name - CutsceneHead.h - Windows - Text - - - - Name - CutsceneObject.cpp - Windows - Text - Debug - - - Name - CutsceneObject.h - Windows - Text - - - - Name - DummyObject.cpp - Windows - Text - Debug - - - Name - DummyObject.h - Windows - Text - - - - Name - Object.cpp - Windows - Text - Debug - - - Name - Object.h - Windows - Text - - - - Name - ObjectData.cpp - Windows - Text - Debug - - - Name - ObjectData.h - Windows - Text - - - - Name - ParticleObject.cpp - Windows - Text - Debug - - - Name - ParticleObject.h - Windows - Text - - - - Name - Projectile.cpp - Windows - Text - Debug - - - Name - Projectile.h - Windows - Text - - - - Name - CivilianPed.cpp - Windows - Text - Debug - - - Name - CivilianPed.h - Windows - Text - - - - Name - CopPed.cpp - Windows - Text - Debug - - - Name - CopPed.h - Windows - Text - - - - Name - DummyPed.h - Windows - Text - - - - Name - EmergencyPed.cpp - Windows - Text - Debug - - - Name - EmergencyPed.h - Windows - Text - - - - Name - Gangs.cpp - Windows - Text - Debug - - - Name - Gangs.h - Windows - Text - - - - Name - Ped.cpp - Windows - Text - Debug - - - Name - Ped.h - Windows - Text - - - - Name - PedAI.cpp - Windows - Text - Debug - - - Name - PedChat.cpp - Windows - Text - Debug - - - Name - PedDebug.cpp - Windows - Text - Debug - - - Name - PedFight.cpp - Windows - Text - Debug - - - Name - PedIK.cpp - Windows - Text - Debug - - - Name - PedIK.h - Windows - Text - - - - Name - PedPlacement.cpp - Windows - Text - Debug - - - Name - PedPlacement.h - Windows - Text - - - - Name - PedRoutes.cpp - Windows - Text - Debug - - - Name - PedRoutes.h - Windows - Text - - - - Name - PedType.cpp - Windows - Text - Debug - - - Name - PedType.h - Windows - Text - - - - Name - PlayerPed.cpp - Windows - Text - Debug - - - Name - PlayerPed.h - Windows - Text - - - - Name - Population.cpp - Windows - Text - Debug - - - Name - Population.h - Windows - Text - - - - Name - 2dEffect.h - Windows - Text - - - - Name - Antennas.cpp - Windows - Text - Debug - - - Name - Antennas.h - Windows - Text - - - - Name - Clouds.cpp - Windows - Text - Debug - - - Name - Clouds.h - Windows - Text - - - - Name - Console.cpp - Windows - Text - Debug - - - Name - Console.h - Windows - Text - - - - Name - Coronas.cpp - Windows - Text - Debug - - - Name - Coronas.h - Windows - Text - - - - Name - Credits.cpp - Windows - Text - Debug - - - Name - Credits.h - Windows - Text - - - - Name - Draw.cpp - Windows - Text - Debug - - - Name - Draw.h - Windows - Text - - - - Name - Fluff.cpp - Windows - Text - Debug - - - Name - Fluff.h - Windows - Text - - - - Name - Font.cpp - Windows - Text - Debug - - - Name - Font.h - Windows - Text - - - - Name - Glass.cpp - Windows - Text - Debug - - - Name - Glass.h - Windows - Text - - - - Name - Hud.cpp - Windows - Text - Debug - - - Name - Hud.h - Windows - Text - - - - Name - Instance.cpp - Windows - Text - Debug - - - Name - Instance.h - Windows - Text - - - - Name - Lines.cpp - Windows - Text - Debug - - - Name - Lines.h - Windows - Text - - - - Name - MBlur.cpp - Windows - Text - Debug - - - Name - MBlur.h - Windows - Text - - - - Name - Particle.cpp - Windows - Text - Debug - - - Name - Particle.h - Windows - Text - - - - Name - ParticleMgr.cpp - Windows - Text - Debug - - - Name - ParticleMgr.h - Windows - Text - - - - Name - ParticleType.h - Windows - Text - - - - Name - PlayerSkin.cpp - Windows - Text - Debug - - - Name - PlayerSkin.h - Windows - Text - - - - Name - PointLights.cpp - Windows - Text - Debug - - - Name - PointLights.h - Windows - Text - - - - Name - RenderBuffer.cpp - Windows - Text - Debug - - - Name - RenderBuffer.h - Windows - Text - - - - Name - Renderer.cpp - Windows - Text - Debug - - - Name - Renderer.h - Windows - Text - - - - Name - Rubbish.cpp - Windows - Text - Debug - - - Name - Rubbish.h - Windows - Text - - - - Name - Shadows.cpp - Windows - Text - Debug - - - Name - Shadows.h - Windows - Text - - - - Name - Skidmarks.cpp - Windows - Text - Debug - - - Name - Skidmarks.h - Windows - Text - - - - Name - SpecialFX.cpp - Windows - Text - Debug - - - Name - SpecialFX.h - Windows - Text - - - - Name - Sprite.cpp - Windows - Text - Debug - - - Name - Sprite.h - Windows - Text - - - - Name - Sprite2d.cpp - Windows - Text - Debug - - - Name - Sprite2d.h - Windows - Text - - - - Name - TexList.cpp - Windows - Text - Debug - - - Name - TexList.h - Windows - Text - - - - Name - Timecycle.cpp - Windows - Text - Debug - - - Name - Timecycle.h - Windows - Text - - - - Name - WaterCannon.cpp - Windows - Text - Debug - - - Name - WaterCannon.h - Windows - Text - - - - Name - WaterLevel.cpp - Windows - Text - Debug - - - Name - WaterLevel.h - Windows - Text - - - - Name - Weather.cpp - Windows - Text - Debug - - - Name - Weather.h - Windows - Text - - - - Name - ClumpRead.cpp - Windows - Text - Debug - - - Name - Lights.cpp - Windows - Text - Debug - - - Name - Lights.h - Windows - Text - - - - Name - MemoryHeap.cpp - Windows - Text - Debug - - - Name - MemoryHeap.h - Windows - Text - - - - Name - MemoryMgr.cpp - Windows - Text - Debug - - - Name - MemoryMgr.h - Windows - Text - - - - Name - NodeName.cpp - Windows - Text - Debug - - - Name - NodeName.h - Windows - Text - - - - Name - RwHelper.cpp - Windows - Text - Debug - - - Name - RwHelper.h - Windows - Text - - - - Name - RwMatFX.cpp - Windows - Text - Debug - - - Name - RwPS2AlphaTest.cpp - Windows - Text - Debug - - - Name - TexRead.cpp - Windows - Text - Debug - - - Name - TexturePools.cpp - Windows - Text - Debug - - - Name - TexturePools.h - Windows - Text - - - - Name - TxdStore.cpp - Windows - Text - Debug - - - Name - TxdStore.h - Windows - Text - - - - Name - VisibilityPlugins.cpp - Windows - Text - Debug - - - Name - VisibilityPlugins.h - Windows - Text - - - - Name - Date.cpp - Windows - Text - Debug - - - Name - Date.h - Windows - Text - - - - Name - GenericGameStorage.cpp - Windows - Text - Debug - - - Name - GenericGameStorage.h - Windows - Text - - - - Name - MemoryCard.cpp - Windows - Text - Debug - - - Name - MemoryCard.h - Windows - Text - - - - Name - PCSave.cpp - Windows - Text - Debug - - - Name - PCSave.h - Windows - Text - - - - Name - crossplatform.cpp - Windows - Text - Debug - - - Name - crossplatform.h - Windows - Text - - - - Name - events.cpp - Windows - Text - Debug - - - Name - events.h - Windows - Text - - - - Name - platform.h - Windows - Text - - - - Name - skeleton.cpp - Windows - Text - Debug - - - Name - skeleton.h - Windows - Text - - - - Name - resource.h - Windows - Text - - - - Name - win.cpp - Windows - Text - Debug - - - Name - win.h - Windows - Text - - - - Name - win.rc - Windows - Text - Debug - - - Name - Messages.cpp - Windows - Text - Debug - - - Name - Messages.h - Windows - Text - - - - Name - Pager.cpp - Windows - Text - Debug - - - Name - Pager.h - Windows - Text - - - - Name - Text.cpp - Windows - Text - Debug - - - Name - Text.h - Windows - Text - - - - Name - Automobile.cpp - Windows - Text - Debug - - - Name - Automobile.h - Windows - Text - - - - Name - Bike.h - Windows - Text - - - - Name - Boat.cpp - Windows - Text - Debug - - - Name - Boat.h - Windows - Text - - - - Name - CarGen.cpp - Windows - Text - Debug - - - Name - CarGen.h - Windows - Text - - - - Name - Cranes.cpp - Windows - Text - Debug - - - Name - Cranes.h - Windows - Text - - - - Name - DamageManager.cpp - Windows - Text - Debug - - - Name - DamageManager.h - Windows - Text - - - - Name - Door.cpp - Windows - Text - Debug - - - Name - Door.h - Windows - Text - - - - Name - Floater.cpp - Windows - Text - Debug - - - Name - Floater.h - Windows - Text - - - - Name - HandlingMgr.cpp - Windows - Text - Debug - - - Name - HandlingMgr.h - Windows - Text - - - - Name - Heli.cpp - Windows - Text - Debug - - - Name - Heli.h - Windows - Text - - - - Name - Plane.cpp - Windows - Text - Debug - - - Name - Plane.h - Windows - Text - - - - Name - Train.cpp - Windows - Text - Debug - - - Name - Train.h - Windows - Text - - - - Name - Transmission.cpp - Windows - Text - Debug - - - Name - Transmission.h - Windows - Text - - - - Name - Vehicle.cpp - Windows - Text - Debug - - - Name - Vehicle.h - Windows - Text - - - - Name - BulletInfo.cpp - Windows - Text - Debug - - - Name - BulletInfo.h - Windows - Text - - - - Name - Explosion.cpp - Windows - Text - Debug - - - Name - Explosion.h - Windows - Text - - - - Name - ProjectileInfo.cpp - Windows - Text - Debug - - - Name - ProjectileInfo.h - Windows - Text - - - - Name - ShotInfo.cpp - Windows - Text - Debug - - - Name - ShotInfo.h - Windows - Text - - - - Name - Weapon.cpp - Windows - Text - Debug - - - Name - Weapon.h - Windows - Text - - - - Name - WeaponEffects.cpp - Windows - Text - Debug - - - Name - WeaponEffects.h - Windows - Text - - - - Name - WeaponInfo.cpp - Windows - Text - Debug - - - Name - WeaponInfo.h - Windows - Text - - - - Name - WeaponType.h - Windows - Text - - - - Name - mss32.lib - Windows - Library - Debug - - - Name - d3d8.lib - Windows - Library - Debug - - - Name - ddraw.lib - Windows - Library - Debug - - - Name - dxguid.lib - Windows - Library - Debug - - - Name - strmiids.lib - Windows - Library - Debug - - - Name - dinput8.lib - Windows - Library - Debug - - - Name - winmm.lib - Windows - Library - Debug - - - Name - rwcore.lib - Windows - Library - Debug - - - Name - rpworld.lib - Windows - Library - Debug - - - Name - rpmatfx.lib - Windows - Library - Debug - - - Name - rpskin.lib - Windows - Library - Debug - - - Name - rphanim.lib - Windows - Library - Debug - - - Name - rtbmp.lib - Windows - Library - Debug - - - Name - rtquat.lib - Windows - Library - Debug - - - Name - rtcharse.lib - Windows - Library - Debug - - - Name - ole32.lib - Windows - Library - Debug - - - Name - shell32.lib - Windows - Library - Debug - - - Name - uuid.lib - Windows - Library - Debug - - - - - Name - AnimationId.h - Windows - - - Name - AnimBlendAssocGroup.cpp - Windows - - - Name - AnimBlendAssocGroup.h - Windows - - - Name - AnimBlendAssociation.cpp - Windows - - - Name - AnimBlendAssociation.h - Windows - - - Name - AnimBlendClumpData.cpp - Windows - - - Name - AnimBlendClumpData.h - Windows - - - Name - AnimBlendHierarchy.cpp - Windows - - - Name - AnimBlendHierarchy.h - Windows - - - Name - AnimBlendList.h - Windows - - - Name - AnimBlendNode.cpp - Windows - - - Name - AnimBlendNode.h - Windows - - - Name - AnimBlendSequence.cpp - Windows - - - Name - AnimBlendSequence.h - Windows - - - Name - AnimManager.cpp - Windows - - - Name - AnimManager.h - Windows - - - Name - Bones.cpp - Windows - - - Name - Bones.h - Windows - - - Name - CutsceneMgr.cpp - Windows - - - Name - CutsceneMgr.h - Windows - - - Name - FrameUpdate.cpp - Windows - - - Name - RpAnimBlend.cpp - Windows - - - Name - RpAnimBlend.h - Windows - - - Name - audio_enums.h - Windows - - - Name - AudioCollision.cpp - Windows - - - Name - AudioCollision.h - Windows - - - Name - AudioLogic.cpp - Windows - - - Name - AudioManager.cpp - Windows - - - Name - AudioManager.h - Windows - - - Name - AudioSamples.h - Windows - - - Name - AudioScriptObject.cpp - Windows - - - Name - AudioScriptObject.h - Windows - - - Name - DMAudio.cpp - Windows - - - Name - DMAudio.h - Windows - - - Name - MusicManager.cpp - Windows - - - Name - MusicManager.h - Windows - - - Name - PolRadio.cpp - Windows - - - Name - PolRadio.h - Windows - - - Name - sampman.h - Windows - - - Name - sampman_miles.cpp - Windows - - - Name - soundlist.h - Windows - - - Name - eax.h - Windows - - - Name - eax-util.cpp - Windows - - - Name - eax-util.h - Windows - - - Name - Building.cpp - Windows - - - Name - Building.h - Windows - - - Name - Solid.h - Windows - - - Name - Treadable.cpp - Windows - - - Name - Treadable.h - Windows - - - Name - ColBox.cpp - Windows - - - Name - ColBox.h - Windows - - - Name - ColLine.cpp - Windows - - - Name - ColLine.h - Windows - - - Name - Collision.cpp - Windows - - - Name - Collision.h - Windows - - - Name - ColModel.cpp - Windows - - - Name - ColModel.h - Windows - - - Name - ColPoint.cpp - Windows - - - Name - ColPoint.h - Windows - - - Name - ColSphere.cpp - Windows - - - Name - ColSphere.h - Windows - - - Name - ColTriangle.cpp - Windows - - - Name - ColTriangle.h - Windows - - - Name - CompressedVector.h - Windows - - - Name - TempColModels.cpp - Windows - - - Name - TempColModels.h - Windows - - - Name - VuCollision.cpp - Windows - - - Name - VuCollision.h - Windows - - - Name - AutoPilot.cpp - Windows - - - Name - AutoPilot.h - Windows - - - Name - Bridge.cpp - Windows - - - Name - Bridge.h - Windows - - - Name - CarAI.cpp - Windows - - - Name - CarAI.h - Windows - - - Name - CarCtrl.cpp - Windows - - - Name - CarCtrl.h - Windows - - - Name - Curves.cpp - Windows - - - Name - Curves.h - Windows - - - Name - Darkel.cpp - Windows - - - Name - Darkel.h - Windows - - - Name - GameLogic.cpp - Windows - - - Name - GameLogic.h - Windows - - - Name - Garages.cpp - Windows - - - Name - Garages.h - Windows - - - Name - NameGrid.cpp - Windows - - - Name - NameGrid.h - Windows - - - Name - OnscreenTimer.cpp - Windows - - - Name - OnscreenTimer.h - Windows - - - Name - PathFind.cpp - Windows - - - Name - PathFind.h - Windows - - - Name - Phones.cpp - Windows - - - Name - Phones.h - Windows - - - Name - Pickups.cpp - Windows - - - Name - Pickups.h - Windows - - - Name - PowerPoints.cpp - Windows - - - Name - PowerPoints.h - Windows - - - Name - Record.cpp - Windows - - - Name - Record.h - Windows - - - Name - Remote.cpp - Windows - - - Name - Remote.h - Windows - - - Name - Replay.cpp - Windows - - - Name - Replay.h - Windows - - - Name - Restart.cpp - Windows - - - Name - Restart.h - Windows - - - Name - RoadBlocks.cpp - Windows - - - Name - RoadBlocks.h - Windows - - - Name - SceneEdit.cpp - Windows - - - Name - SceneEdit.h - Windows - - - Name - ScriptDebug.cpp - Windows - - - Name - Script.cpp - Windows - - - Name - Script.h - Windows - - - Name - Script2.cpp - Windows - - - Name - Script3.cpp - Windows - - - Name - Script4.cpp - Windows - - - Name - Script5.cpp - Windows - - - Name - Script6.cpp - Windows - - - Name - ScriptCommands.h - Windows - - - Name - TrafficLights.cpp - Windows - - - Name - TrafficLights.h - Windows - - - Name - Accident.cpp - Windows - - - Name - Accident.h - Windows - - - Name - AnimViewer.cpp - Windows - - - Name - AnimViewer.h - Windows - - - Name - Cam.cpp - Windows - - - Name - Camera.cpp - Windows - - - Name - Camera.h - Windows - - - Name - CdStream.cpp - Windows - - - Name - CdStream.h - Windows - - - Name - CdStreamPosix.cpp - Windows - - - Name - Clock.cpp - Windows - - - Name - Clock.h - Windows - - - Name - common.h - Windows - - - Name - config.h - Windows - - - Name - ControllerConfig.cpp - Windows - - - Name - ControllerConfig.h - Windows - - - Name - Crime.h - Windows - - - Name - Debug.cpp - Windows - - - Name - Debug.h - Windows - - - Name - Directory.cpp - Windows - - - Name - Directory.h - Windows - - - Name - EventList.cpp - Windows - - - Name - EventList.h - Windows - - - Name - FileLoader.cpp - Windows - - - Name - FileLoader.h - Windows - - - Name - FileMgr.cpp - Windows - - - Name - FileMgr.h - Windows - - - Name - Fire.cpp - Windows - - - Name - Fire.h - Windows - - - Name - Frontend.cpp - Windows - - - Name - Frontend.h - Windows - - - Name - Frontend_PS2.cpp - Windows - - - Name - Frontend_PS2.h - Windows - - - Name - FrontEndControls.cpp - Windows - - - Name - FrontEndControls.h - Windows - - - Name - FrontendTriggers.h - Windows - - - Name - Game.cpp - Windows - - - Name - Game.h - Windows - - - Name - General.h - Windows - - - Name - IniFile.cpp - Windows - - - Name - IniFile.h - Windows - - - Name - Lists.cpp - Windows - - - Name - Lists.h - Windows - - - Name - main.cpp - Windows - - - Name - main.h - Windows - - - Name - MenuScreens.cpp - Windows - - - Name - MenuScreensCustom.cpp - Windows - - - Name - obrstr.cpp - Windows - - - Name - obrstr.h - Windows - - - Name - Pad.cpp - Windows - - - Name - Pad.h - Windows - - - Name - Placeable.cpp - Windows - - - Name - Placeable.h - Windows - - - Name - PlayerInfo.cpp - Windows - - - Name - PlayerInfo.h - Windows - - - Name - Pools.cpp - Windows - - - Name - Pools.h - Windows - - - Name - Profile.cpp - Windows - - - Name - Profile.h - Windows - - - Name - Radar.cpp - Windows - - - Name - Radar.h - Windows - - - Name - Range2D.cpp - Windows - - - Name - Range2D.h - Windows - - - Name - Range3D.cpp - Windows - - - Name - Range3D.h - Windows - - - Name - re3.cpp - Windows - - - Name - References.cpp - Windows - - - Name - References.h - Windows - - - Name - Stats.cpp - Windows - - - Name - Stats.h - Windows - - - Name - Streaming.cpp - Windows - - - Name - Streaming.h - Windows - - - Name - SurfaceTable.cpp - Windows - - - Name - SurfaceTable.h - Windows - - - Name - templates.h - Windows - - - Name - timebars.cpp - Windows - - - Name - timebars.h - Windows - - - Name - Timer.cpp - Windows - - - Name - Timer.h - Windows - - - Name - TimeStep.cpp - Windows - - - Name - TimeStep.h - Windows - - - Name - User.cpp - Windows - - - Name - User.h - Windows - - - Name - Wanted.cpp - Windows - - - Name - Wanted.h - Windows - - - Name - World.cpp - Windows - - - Name - World.h - Windows - - - Name - ZoneCull.cpp - Windows - - - Name - ZoneCull.h - Windows - - - Name - Zones.cpp - Windows - - - Name - Zones.h - Windows - - - Name - Dummy.cpp - Windows - - - Name - Dummy.h - Windows - - - Name - Entity.cpp - Windows - - - Name - Entity.h - Windows - - - Name - Physical.cpp - Windows - - - Name - Physical.h - Windows - - - Name - math.cpp - Windows - - - Name - maths.h - Windows - - - Name - Matrix.cpp - Windows - - - Name - Matrix.h - Windows - - - Name - Quaternion.cpp - Windows - - - Name - Quaternion.h - Windows - - - Name - Rect.cpp - Windows - - - Name - Rect.h - Windows - - - Name - Vector.cpp - Windows - - - Name - Vector.h - Windows - - - Name - Vector2D.h - Windows - - - Name - VuVector.h - Windows - - - Name - BaseModelInfo.cpp - Windows - - - Name - BaseModelInfo.h - Windows - - - Name - ClumpModelInfo.cpp - Windows - - - Name - ClumpModelInfo.h - Windows - - - Name - MloModelInfo.cpp - Windows - - - Name - MloModelInfo.h - Windows - - - Name - ModelIndices.cpp - Windows - - - Name - ModelIndices.h - Windows - - - Name - ModelInfo.cpp - Windows - - - Name - ModelInfo.h - Windows - - - Name - PedModelInfo.cpp - Windows - - - Name - PedModelInfo.h - Windows - - - Name - SimpleModelInfo.cpp - Windows - - - Name - SimpleModelInfo.h - Windows - - - Name - TimeModelInfo.cpp - Windows - - - Name - TimeModelInfo.h - Windows - - - Name - VehicleModelInfo.cpp - Windows - - - Name - VehicleModelInfo.h - Windows - - - Name - XtraCompsModelInfo.h - Windows - - - Name - CutsceneHead.cpp - Windows - - - Name - CutsceneHead.h - Windows - - - Name - CutsceneObject.cpp - Windows - - - Name - CutsceneObject.h - Windows - - - Name - DummyObject.cpp - Windows - - - Name - DummyObject.h - Windows - - - Name - Object.cpp - Windows - - - Name - Object.h - Windows - - - Name - ObjectData.cpp - Windows - - - Name - ObjectData.h - Windows - - - Name - ParticleObject.cpp - Windows - - - Name - ParticleObject.h - Windows - - - Name - Projectile.cpp - Windows - - - Name - Projectile.h - Windows - - - Name - CivilianPed.cpp - Windows - - - Name - CivilianPed.h - Windows - - - Name - CopPed.cpp - Windows - - - Name - CopPed.h - Windows - - - Name - DummyPed.h - Windows - - - Name - EmergencyPed.cpp - Windows - - - Name - EmergencyPed.h - Windows - - - Name - Gangs.cpp - Windows - - - Name - Gangs.h - Windows - - - Name - Ped.cpp - Windows - - - Name - Ped.h - Windows - - - Name - PedAI.cpp - Windows - - - Name - PedChat.cpp - Windows - - - Name - PedDebug.cpp - Windows - - - Name - PedFight.cpp - Windows - - - Name - PedIK.cpp - Windows - - - Name - PedIK.h - Windows - - - Name - PedPlacement.cpp - Windows - - - Name - PedPlacement.h - Windows - - - Name - PedRoutes.cpp - Windows - - - Name - PedRoutes.h - Windows - - - Name - PedType.cpp - Windows - - - Name - PedType.h - Windows - - - Name - PlayerPed.cpp - Windows - - - Name - PlayerPed.h - Windows - - - Name - Population.cpp - Windows - - - Name - Population.h - Windows - - - Name - 2dEffect.h - Windows - - - Name - Antennas.cpp - Windows - - - Name - Antennas.h - Windows - - - Name - Clouds.cpp - Windows - - - Name - Clouds.h - Windows - - - Name - Console.cpp - Windows - - - Name - Console.h - Windows - - - Name - Coronas.cpp - Windows - - - Name - Coronas.h - Windows - - - Name - Credits.cpp - Windows - - - Name - Credits.h - Windows - - - Name - Draw.cpp - Windows - - - Name - Draw.h - Windows - - - Name - Fluff.cpp - Windows - - - Name - Fluff.h - Windows - - - Name - Font.cpp - Windows - - - Name - Font.h - Windows - - - Name - Glass.cpp - Windows - - - Name - Glass.h - Windows - - - Name - Hud.cpp - Windows - - - Name - Hud.h - Windows - - - Name - Instance.cpp - Windows - - - Name - Instance.h - Windows - - - Name - Lines.cpp - Windows - - - Name - Lines.h - Windows - - - Name - MBlur.cpp - Windows - - - Name - MBlur.h - Windows - - - Name - Particle.cpp - Windows - - - Name - Particle.h - Windows - - - Name - ParticleMgr.cpp - Windows - - - Name - ParticleMgr.h - Windows - - - Name - ParticleType.h - Windows - - - Name - PlayerSkin.cpp - Windows - - - Name - PlayerSkin.h - Windows - - - Name - PointLights.cpp - Windows - - - Name - PointLights.h - Windows - - - Name - RenderBuffer.cpp - Windows - - - Name - RenderBuffer.h - Windows - - - Name - Renderer.cpp - Windows - - - Name - Renderer.h - Windows - - - Name - Rubbish.cpp - Windows - - - Name - Rubbish.h - Windows - - - Name - Shadows.cpp - Windows - - - Name - Shadows.h - Windows - - - Name - Skidmarks.cpp - Windows - - - Name - Skidmarks.h - Windows - - - Name - SpecialFX.cpp - Windows - - - Name - SpecialFX.h - Windows - - - Name - Sprite.cpp - Windows - - - Name - Sprite.h - Windows - - - Name - Sprite2d.cpp - Windows - - - Name - Sprite2d.h - Windows - - - Name - TexList.cpp - Windows - - - Name - TexList.h - Windows - - - Name - Timecycle.cpp - Windows - - - Name - Timecycle.h - Windows - - - Name - WaterCannon.cpp - Windows - - - Name - WaterCannon.h - Windows - - - Name - WaterLevel.cpp - Windows - - - Name - WaterLevel.h - Windows - - - Name - Weather.cpp - Windows - - - Name - Weather.h - Windows - - - Name - ClumpRead.cpp - Windows - - - Name - Lights.cpp - Windows - - - Name - Lights.h - Windows - - - Name - MemoryHeap.cpp - Windows - - - Name - MemoryHeap.h - Windows - - - Name - MemoryMgr.cpp - Windows - - - Name - MemoryMgr.h - Windows - - - Name - NodeName.cpp - Windows - - - Name - NodeName.h - Windows - - - Name - RwHelper.cpp - Windows - - - Name - RwHelper.h - Windows - - - Name - RwMatFX.cpp - Windows - - - Name - RwPS2AlphaTest.cpp - Windows - - - Name - TexRead.cpp - Windows - - - Name - TexturePools.cpp - Windows - - - Name - TexturePools.h - Windows - - - Name - TxdStore.cpp - Windows - - - Name - TxdStore.h - Windows - - - Name - VisibilityPlugins.cpp - Windows - - - Name - VisibilityPlugins.h - Windows - - - Name - Date.cpp - Windows - - - Name - Date.h - Windows - - - Name - GenericGameStorage.cpp - Windows - - - Name - GenericGameStorage.h - Windows - - - Name - MemoryCard.cpp - Windows - - - Name - MemoryCard.h - Windows - - - Name - PCSave.cpp - Windows - - - Name - PCSave.h - Windows - - - Name - crossplatform.cpp - Windows - - - Name - crossplatform.h - Windows - - - Name - events.cpp - Windows - - - Name - events.h - Windows - - - Name - platform.h - Windows - - - Name - skeleton.cpp - Windows - - - Name - skeleton.h - Windows - - - Name - resource.h - Windows - - - Name - win.cpp - Windows - - - Name - win.h - Windows - - - Name - win.rc - Windows - - - Name - Messages.cpp - Windows - - - Name - Messages.h - Windows - - - Name - Pager.cpp - Windows - - - Name - Pager.h - Windows - - - Name - Text.cpp - Windows - - - Name - Text.h - Windows - - - Name - Automobile.cpp - Windows - - - Name - Automobile.h - Windows - - - Name - Bike.h - Windows - - - Name - Boat.cpp - Windows - - - Name - Boat.h - Windows - - - Name - CarGen.cpp - Windows - - - Name - CarGen.h - Windows - - - Name - Cranes.cpp - Windows - - - Name - Cranes.h - Windows - - - Name - DamageManager.cpp - Windows - - - Name - DamageManager.h - Windows - - - Name - Door.cpp - Windows - - - Name - Door.h - Windows - - - Name - Floater.cpp - Windows - - - Name - Floater.h - Windows - - - Name - HandlingMgr.cpp - Windows - - - Name - HandlingMgr.h - Windows - - - Name - Heli.cpp - Windows - - - Name - Heli.h - Windows - - - Name - Plane.cpp - Windows - - - Name - Plane.h - Windows - - - Name - Train.cpp - Windows - - - Name - Train.h - Windows - - - Name - Transmission.cpp - Windows - - - Name - Transmission.h - Windows - - - Name - Vehicle.cpp - Windows - - - Name - Vehicle.h - Windows - - - Name - BulletInfo.cpp - Windows - - - Name - BulletInfo.h - Windows - - - Name - Explosion.cpp - Windows - - - Name - Explosion.h - Windows - - - Name - ProjectileInfo.cpp - Windows - - - Name - ProjectileInfo.h - Windows - - - Name - ShotInfo.cpp - Windows - - - Name - ShotInfo.h - Windows - - - Name - Weapon.cpp - Windows - - - Name - Weapon.h - Windows - - - Name - WeaponEffects.cpp - Windows - - - Name - WeaponEffects.h - Windows - - - Name - WeaponInfo.cpp - Windows - - - Name - WeaponInfo.h - Windows - - - Name - WeaponType.h - Windows - - - Name - mss32.lib - Windows - - - Name - d3d8.lib - Windows - - - Name - ddraw.lib - Windows - - - Name - dxguid.lib - Windows - - - Name - strmiids.lib - Windows - - - Name - dinput8.lib - Windows - - - Name - winmm.lib - Windows - - - Name - rwcore.lib - Windows - - - Name - rpworld.lib - Windows - - - Name - rpmatfx.lib - Windows - - - Name - rpskin.lib - Windows - - - Name - rphanim.lib - Windows - - - Name - rtbmp.lib - Windows - - - Name - rtquat.lib - Windows - - - Name - rtcharse.lib - Windows - - - Name - ole32.lib - Windows - - - Name - shell32.lib - Windows - - - Name - uuid.lib - Windows - - - Name - MSL_All_x86_D.lib - MacOS - - - Name - Comdlg32.lib - MacOS - - - Name - Gdi32.lib - MacOS - - - Name - Kernel32.lib - MacOS - - - Name - User32.lib - MacOS - - - - - Release - - - - UserSourceTrees - - - AlwaysSearchUserPathsfalse - InterpretDOSAndUnixPathstrue - RequireFrameworkStyleIncludesfalse - UserSearchPaths - - SearchPath - Path - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\animation - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\audio - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\buildings - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\collision - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\control - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\core - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\entities - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\math - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\modelinfo - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\objects - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\peds - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\renderer - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\rw - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\save - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\skel - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\text - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\vehicles - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\weapons - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\vendor\milessdk\lib - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\vendor\milessdk\include - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\sdk\dx8sdk\Lib - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\sdk\rwsdk\lib\d3d8\release - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\sdk\rwsdk\include\d3d8 - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\extras - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SystemSearchPaths - - SearchPath - Path..\sdk\rwsdk\include\d3d8 - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\sdk\dx8sdk\Include - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - PathWin32-x86 Support\Headers\ - PathFormatWindows - PathRootCodeWarrior - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - PathWin32-x86 Support\Libraries\ - PathFormatWindows - PathRootCodeWarrior - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - PathMSL - PathFormatWindows - PathRootCodeWarrior - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - SearchPath - Path..\src\audio\eax - PathFormatWindows - PathRootProject - - Recursivetrue - FrameworkPathfalse - HostFlagsAll - - - - - MWRuntimeSettings_WorkingDirectory - MWRuntimeSettings_CommandLine - MWRuntimeSettings_HostApplication - Path - PathFormatGeneric - PathRootAbsolute - - MWRuntimeSettings_EnvVars - - - LinkerWin32 x86 Linker - PreLinker - PostLinker - TargetnameRelease - OutputDirectory - Path - PathFormatWindows - PathRootProject - - SaveEntriesUsingRelativePathsfalse - - - FileMappings - - FileTypeTEXT - FileExtension.c - CompilerMW C/C++ x86 - EditLanguageC/C++ - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.c++ - CompilerMW C/C++ x86 - EditLanguageC/C++ - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.cc - CompilerMW C/C++ x86 - EditLanguageC/C++ - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.cp - CompilerMW C/C++ x86 - EditLanguageC/C++ - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.cpp - CompilerMW C/C++ x86 - EditLanguageC/C++ - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.def - Compiler - EditLanguage - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.h - CompilerMW C/C++ x86 - EditLanguageC/C++ - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMaketrue - - - FileTypeTEXT - FileExtension.p - CompilerMW Pascal x86 - EditLanguage - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.pas - CompilerMW Pascal x86 - EditLanguage - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.pch - CompilerMW C/C++ x86 - EditLanguageC/C++ - Precompiletrue - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.pch++ - CompilerMW C/C++ x86 - EditLanguageC/C++ - Precompiletrue - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.ppu - CompilerMW Pascal x86 - EditLanguage - Precompiletrue - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.rc - CompilerMW WinRC - EditLanguage - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileTypeTEXT - FileExtension.res - CompilerWinRes Import - EditLanguage - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileExtension.doc - Compiler - EditLanguage - Precompilefalse - Launchabletrue - ResourceFilefalse - IgnoredByMaketrue - - - FileExtension.lib - CompilerLib Import x86 - EditLanguage - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileExtension.obj - CompilerObj Import x86 - EditLanguage - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - FileExtension.res - CompilerWinRes Import - EditLanguage - Precompilefalse - Launchablefalse - ResourceFilefalse - IgnoredByMakefalse - - - - - CacheModDatestrue - ActivateBrowserfalse - DumpBrowserInfofalse - CacheSubprojectstrue - UseThirdPartyDebuggerfalse - DebuggerAppPath - Path - PathFormatGeneric - PathRootAbsolute - - DebuggerCmdLineArgs - DebuggerWorkingDir - Path - PathFormatGeneric - PathRootAbsolute - - - - LogSystemMessagesfalse - AutoTargetDLLsfalse - StopAtWatchpointstrue - PauseWhileRunningfalse - PauseInterval5 - PauseUIFlags0 - AltExePath - Path - PathFormatGeneric - PathRootAbsolute - - StopAtTempBPOnLaunchtrue - CacheSymbolicstrue - TempBPFunctionNamemain - TempBPType0 - - - Enabledfalse - ConnectionName - DownloadPath - LaunchRemoteAppfalse - RemoteAppPath - - - OtherExecutables - - - CustomColor1 - Red0 - Green32767 - Blue0 - - CustomColor2 - Red0 - Green32767 - Blue0 - - CustomColor3 - Red0 - Green32767 - Blue0 - - CustomColor4 - Red0 - Green32767 - Blue0 - - - - MWCodeGen_X86_processorPentiumII - MWCodeGen_X86_alignmentbytes8 - MWCodeGen_X86_exceptionsZeroOverhead - MWCodeGen_X86_extinst_mmx0 - MWCodeGen_X86_extinst_3dnow0 - MWCodeGen_X86_use_mmx_3dnow_convention0 - MWCodeGen_X86_machinecodelisting0 - MWCodeGen_X86_intrinsics1 - MWCodeGen_X86_syminfo0 - MWCodeGen_X86_codeviewinfo1 - MWCodeGen_X86_extinst_cmov_fcomi0 - MWCodeGen_X86_extinst_sse0 - - - MWDebugger_X86_Exceptions - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - - - - PDisasmX86_showHeaderstrue - PDisasmX86_showSymTabtrue - PDisasmX86_showCodetrue - PDisasmX86_showSourcefalse - PDisasmX86_showHextrue - PDisasmX86_showRelocationtrue - PDisasmX86_showCommentsfalse - PDisasmX86_showDebugfalse - PDisasmX86_showExceptionsfalse - PDisasmX86_showDatatrue - PDisasmX86_showRawfalse - PDisasmX86_verbosefalse - - - MWFrontEnd_C_cplusplus1 - MWFrontEnd_C_checkprotos0 - MWFrontEnd_C_arm0 - MWFrontEnd_C_trigraphs0 - MWFrontEnd_C_onlystdkeywords0 - MWFrontEnd_C_enumsalwaysint1 - MWFrontEnd_C_mpwpointerstyle0 - MWFrontEnd_C_prefixname - MWFrontEnd_C_ansistrict0 - MWFrontEnd_C_mpwcnewline0 - MWFrontEnd_C_wchar_type1 - MWFrontEnd_C_enableexceptions1 - MWFrontEnd_C_dontreusestrings0 - MWFrontEnd_C_poolstrings1 - MWFrontEnd_C_dontinline0 - MWFrontEnd_C_useRTTI1 - MWFrontEnd_C_multibyteaware1 - MWFrontEnd_C_unsignedchars0 - MWFrontEnd_C_autoinline0 - MWFrontEnd_C_booltruefalse1 - MWFrontEnd_C_direct_to_som0 - MWFrontEnd_C_som_env_check0 - MWFrontEnd_C_alwaysinline0 - MWFrontEnd_C_inlinelevel0 - MWFrontEnd_C_ecplusplus0 - MWFrontEnd_C_objective_c0 - MWFrontEnd_C_defer_codegen0 - - - MWLinker_X86_entrypointusageDefault - MWLinker_X86_entrypoint - MWLinker_X86_subsystemWinGUI - MWLinker_X86_subsysmajorid4 - MWLinker_X86_subsysminorid0 - MWLinker_X86_usrmajorid0 - MWLinker_X86_usrminorid0 - MWLinker_X86_commandfile - MWLinker_X86_generatemap0 - MWLinker_X86_linksym0 - MWLinker_X86_linkCV1 - - - MWProject_X86_typeApplication - MWProject_X86_outfileRelease\gta3.exe - MWProject_X86_baseaddress4194304 - MWProject_X86_maxstacksize1024 - MWProject_X86_minstacksize4 - MWProject_X86_size1024 - MWProject_X86_minsize4 - MWProject_X86_importlib - - - MWWarning_C_warn_illpragma0 - MWWarning_C_warn_emptydecl0 - MWWarning_C_warn_possunwant1 - MWWarning_C_warn_unusedvar1 - MWWarning_C_warn_unusedarg0 - MWWarning_C_warn_extracomma1 - MWWarning_C_pedantic0 - MWWarning_C_warningerrors0 - MWWarning_C_warn_hidevirtual1 - MWWarning_C_warn_implicitconv0 - MWWarning_C_warn_notinlined0 - MWWarning_C_warn_structclass0 - - - MWWinRC_prefixnameResourcePrefix.h - - - GlobalOptimizer_X86__optimizationlevelLevel4 - GlobalOptimizer_X86__optforSpeed - - - - Name - MSL_All_x86.lib - MacOS - Library - - - - Name - Comdlg32.lib - MacOS - Library - - - - Name - Gdi32.lib - MacOS - Library - - - - Name - Kernel32.lib - MacOS - Library - - - - Name - User32.lib - MacOS - Library - - - - Name - AnimationId.h - Windows - Text - - - - Name - AnimBlendAssocGroup.cpp - Windows - Text - Debug - - - Name - AnimBlendAssocGroup.h - Windows - Text - - - - Name - AnimBlendAssociation.cpp - Windows - Text - Debug - - - Name - AnimBlendAssociation.h - Windows - Text - - - - Name - AnimBlendClumpData.cpp - Windows - Text - Debug - - - Name - AnimBlendClumpData.h - Windows - Text - - - - Name - AnimBlendHierarchy.cpp - Windows - Text - Debug - - - Name - AnimBlendHierarchy.h - Windows - Text - - - - Name - AnimBlendList.h - Windows - Text - - - - Name - AnimBlendNode.cpp - Windows - Text - Debug - - - Name - AnimBlendNode.h - Windows - Text - - - - Name - AnimBlendSequence.cpp - Windows - Text - Debug - - - Name - AnimBlendSequence.h - Windows - Text - - - - Name - AnimManager.cpp - Windows - Text - Debug - - - Name - AnimManager.h - Windows - Text - - - - Name - Bones.cpp - Windows - Text - Debug - - - Name - Bones.h - Windows - Text - - - - Name - CutsceneMgr.cpp - Windows - Text - Debug - - - Name - CutsceneMgr.h - Windows - Text - - - - Name - FrameUpdate.cpp - Windows - Text - Debug - - - Name - RpAnimBlend.cpp - Windows - Text - Debug - - - Name - RpAnimBlend.h - Windows - Text - - - - Name - audio_enums.h - Windows - Text - - - - Name - AudioCollision.cpp - Windows - Text - Debug - - - Name - AudioCollision.h - Windows - Text - - - - Name - AudioLogic.cpp - Windows - Text - - - - Name - AudioManager.cpp - Windows - Text - Debug - - - Name - AudioManager.h - Windows - Text - - - - Name - AudioSamples.h - Windows - Text - - - - Name - AudioScriptObject.cpp - Windows - Text - Debug - - - Name - AudioScriptObject.h - Windows - Text - - - - Name - DMAudio.cpp - Windows - Text - Debug - - - Name - DMAudio.h - Windows - Text - - - - Name - MusicManager.cpp - Windows - Text - Debug - - - Name - MusicManager.h - Windows - Text - - - - Name - PolRadio.cpp - Windows - Text - Debug - - - Name - PolRadio.h - Windows - Text - - - - Name - sampman.h - Windows - Text - - - - Name - sampman_miles.cpp - Windows - Text - Debug - - - Name - soundlist.h - Windows - Text - - - - Name - eax.h - Windows - Text - - - - Name - eax-util.cpp - Windows - Text - Debug - - - Name - eax-util.h - Windows - Text - - - - Name - Building.cpp - Windows - Text - Debug - - - Name - Building.h - Windows - Text - - - - Name - Solid.h - Windows - Text - - - - Name - Treadable.cpp - Windows - Text - Debug - - - Name - Treadable.h - Windows - Text - - - - Name - ColBox.cpp - Windows - Text - Debug - - - Name - ColBox.h - Windows - Text - - - - Name - ColLine.cpp - Windows - Text - Debug - - - Name - ColLine.h - Windows - Text - - - - Name - Collision.cpp - Windows - Text - Debug - - - Name - Collision.h - Windows - Text - - - - Name - ColModel.cpp - Windows - Text - Debug - - - Name - ColModel.h - Windows - Text - - - - Name - ColPoint.cpp - Windows - Text - Debug - - - Name - ColPoint.h - Windows - Text - - - - Name - ColSphere.cpp - Windows - Text - Debug - - - Name - ColSphere.h - Windows - Text - - - - Name - ColTriangle.cpp - Windows - Text - Debug - - - Name - ColTriangle.h - Windows - Text - - - - Name - CompressedVector.h - Windows - Text - - - - Name - TempColModels.cpp - Windows - Text - Debug - - - Name - TempColModels.h - Windows - Text - - - - Name - VuCollision.cpp - Windows - Text - Debug - - - Name - VuCollision.h - Windows - Text - - - - Name - AutoPilot.cpp - Windows - Text - Debug - - - Name - AutoPilot.h - Windows - Text - - - - Name - Bridge.cpp - Windows - Text - Debug - - - Name - Bridge.h - Windows - Text - - - - Name - CarAI.cpp - Windows - Text - Debug - - - Name - CarAI.h - Windows - Text - - - - Name - CarCtrl.cpp - Windows - Text - Debug - - - Name - CarCtrl.h - Windows - Text - - - - Name - Curves.cpp - Windows - Text - Debug - - - Name - Curves.h - Windows - Text - - - - Name - Darkel.cpp - Windows - Text - Debug - - - Name - Darkel.h - Windows - Text - - - - Name - GameLogic.cpp - Windows - Text - Debug - - - Name - GameLogic.h - Windows - Text - - - - Name - Garages.cpp - Windows - Text - Debug - - - Name - Garages.h - Windows - Text - - - - Name - NameGrid.cpp - Windows - Text - Debug - - - Name - NameGrid.h - Windows - Text - - - - Name - OnscreenTimer.cpp - Windows - Text - Debug - - - Name - OnscreenTimer.h - Windows - Text - - - - Name - PathFind.cpp - Windows - Text - Debug - - - Name - PathFind.h - Windows - Text - - - - Name - Phones.cpp - Windows - Text - Debug - - - Name - Phones.h - Windows - Text - - - - Name - Pickups.cpp - Windows - Text - Debug - - - Name - Pickups.h - Windows - Text - - - - Name - PowerPoints.cpp - Windows - Text - Debug - - - Name - PowerPoints.h - Windows - Text - - - - Name - Record.cpp - Windows - Text - Debug - - - Name - Record.h - Windows - Text - - - - Name - Remote.cpp - Windows - Text - Debug - - - Name - Remote.h - Windows - Text - - - - Name - Replay.cpp - Windows - Text - Debug - - - Name - Replay.h - Windows - Text - - - - Name - Restart.cpp - Windows - Text - Debug - - - Name - Restart.h - Windows - Text - - - - Name - RoadBlocks.cpp - Windows - Text - Debug - - - Name - RoadBlocks.h - Windows - Text - - - - Name - SceneEdit.cpp - Windows - Text - Debug - - - Name - SceneEdit.h - Windows - Text - - - - Name - ScriptDebug.cpp - Windows - Text - Debug - - - Name - Script.cpp - Windows - Text - Debug - - - Name - Script.h - Windows - Text - - - - Name - Script2.cpp - Windows - Text - Debug - - - Name - Script3.cpp - Windows - Text - Debug - - - Name - Script4.cpp - Windows - Text - Debug - - - Name - Script5.cpp - Windows - Text - Debug - - - Name - Script6.cpp - Windows - Text - Debug - - - Name - ScriptCommands.h - Windows - Text - - - - Name - TrafficLights.cpp - Windows - Text - Debug - - - Name - TrafficLights.h - Windows - Text - - - - Name - Accident.cpp - Windows - Text - Debug - - - Name - Accident.h - Windows - Text - - - - Name - AnimViewer.cpp - Windows - Text - Debug - - - Name - AnimViewer.h - Windows - Text - - - - Name - Cam.cpp - Windows - Text - Debug - - - Name - Camera.cpp - Windows - Text - Debug - - - Name - Camera.h - Windows - Text - - - - Name - CdStream.cpp - Windows - Text - Debug - - - Name - CdStream.h - Windows - Text - - - - Name - CdStreamPosix.cpp - Windows - Text - Debug - - - Name - Clock.cpp - Windows - Text - Debug - - - Name - Clock.h - Windows - Text - - - - Name - common.h - Windows - Text - - - - Name - config.h - Windows - Text - - - - Name - ControllerConfig.cpp - Windows - Text - Debug - - - Name - ControllerConfig.h - Windows - Text - - - - Name - Crime.h - Windows - Text - - - - Name - Debug.cpp - Windows - Text - Debug - - - Name - Debug.h - Windows - Text - - - - Name - Directory.cpp - Windows - Text - Debug - - - Name - Directory.h - Windows - Text - - - - Name - EventList.cpp - Windows - Text - Debug - - - Name - EventList.h - Windows - Text - - - - Name - FileLoader.cpp - Windows - Text - Debug - - - Name - FileLoader.h - Windows - Text - - - - Name - FileMgr.cpp - Windows - Text - Debug - - - Name - FileMgr.h - Windows - Text - - - - Name - Fire.cpp - Windows - Text - Debug - - - Name - Fire.h - Windows - Text - - - - Name - Frontend.cpp - Windows - Text - Debug - - - Name - Frontend.h - Windows - Text - - - - Name - Frontend_PS2.cpp - Windows - Text - Debug - - - Name - Frontend_PS2.h - Windows - Text - - - - Name - FrontEndControls.cpp - Windows - Text - Debug - - - Name - FrontEndControls.h - Windows - Text - - - - Name - FrontendTriggers.h - Windows - Text - - - - Name - Game.cpp - Windows - Text - Debug - - - Name - Game.h - Windows - Text - - - - Name - General.h - Windows - Text - - - - Name - IniFile.cpp - Windows - Text - Debug - - - Name - IniFile.h - Windows - Text - - - - Name - Lists.cpp - Windows - Text - Debug - - - Name - Lists.h - Windows - Text - - - - Name - main.cpp - Windows - Text - Debug - - - Name - main.h - Windows - Text - - - - Name - MenuScreens.cpp - Windows - Text - Debug - - - Name - MenuScreensCustom.cpp - Windows - Text - Debug - - - Name - obrstr.cpp - Windows - Text - Debug - - - Name - obrstr.h - Windows - Text - - - - Name - Pad.cpp - Windows - Text - Debug - - - Name - Pad.h - Windows - Text - - - - Name - Placeable.cpp - Windows - Text - Debug - - - Name - Placeable.h - Windows - Text - - - - Name - PlayerInfo.cpp - Windows - Text - Debug - - - Name - PlayerInfo.h - Windows - Text - - - - Name - Pools.cpp - Windows - Text - Debug - - - Name - Pools.h - Windows - Text - - - - Name - Profile.cpp - Windows - Text - Debug - - - Name - Profile.h - Windows - Text - - - - Name - Radar.cpp - Windows - Text - Debug - - - Name - Radar.h - Windows - Text - - - - Name - Range2D.cpp - Windows - Text - Debug - - - Name - Range2D.h - Windows - Text - - - - Name - Range3D.cpp - Windows - Text - Debug - - - Name - Range3D.h - Windows - Text - - - - Name - re3.cpp - Windows - Text - Debug - - - Name - References.cpp - Windows - Text - Debug - - - Name - References.h - Windows - Text - - - - Name - Stats.cpp - Windows - Text - Debug - - - Name - Stats.h - Windows - Text - - - - Name - Streaming.cpp - Windows - Text - Debug - - - Name - Streaming.h - Windows - Text - - - - Name - SurfaceTable.cpp - Windows - Text - Debug - - - Name - SurfaceTable.h - Windows - Text - - - - Name - templates.h - Windows - Text - - - - Name - timebars.cpp - Windows - Text - Debug - - - Name - timebars.h - Windows - Text - - - - Name - Timer.cpp - Windows - Text - Debug - - - Name - Timer.h - Windows - Text - - - - Name - TimeStep.cpp - Windows - Text - Debug - - - Name - TimeStep.h - Windows - Text - - - - Name - User.cpp - Windows - Text - Debug - - - Name - User.h - Windows - Text - - - - Name - Wanted.cpp - Windows - Text - Debug - - - Name - Wanted.h - Windows - Text - - - - Name - World.cpp - Windows - Text - Debug - - - Name - World.h - Windows - Text - - - - Name - ZoneCull.cpp - Windows - Text - Debug - - - Name - ZoneCull.h - Windows - Text - - - - Name - Zones.cpp - Windows - Text - Debug - - - Name - Zones.h - Windows - Text - - - - Name - Dummy.cpp - Windows - Text - Debug - - - Name - Dummy.h - Windows - Text - - - - Name - Entity.cpp - Windows - Text - Debug - - - Name - Entity.h - Windows - Text - - - - Name - Physical.cpp - Windows - Text - Debug - - - Name - Physical.h - Windows - Text - - - - Name - math.cpp - Windows - Text - Debug - - - Name - maths.h - Windows - Text - - - - Name - Matrix.cpp - Windows - Text - Debug - - - Name - Matrix.h - Windows - Text - - - - Name - Quaternion.cpp - Windows - Text - Debug - - - Name - Quaternion.h - Windows - Text - - - - Name - Rect.cpp - Windows - Text - Debug - - - Name - Rect.h - Windows - Text - - - - Name - Vector.cpp - Windows - Text - Debug - - - Name - Vector.h - Windows - Text - - - - Name - Vector2D.h - Windows - Text - - - - Name - VuVector.h - Windows - Text - - - - Name - BaseModelInfo.cpp - Windows - Text - Debug - - - Name - BaseModelInfo.h - Windows - Text - - - - Name - ClumpModelInfo.cpp - Windows - Text - Debug - - - Name - ClumpModelInfo.h - Windows - Text - - - - Name - MloModelInfo.cpp - Windows - Text - Debug - - - Name - MloModelInfo.h - Windows - Text - - - - Name - ModelIndices.cpp - Windows - Text - Debug - - - Name - ModelIndices.h - Windows - Text - - - - Name - ModelInfo.cpp - Windows - Text - Debug - - - Name - ModelInfo.h - Windows - Text - - - - Name - PedModelInfo.cpp - Windows - Text - Debug - - - Name - PedModelInfo.h - Windows - Text - - - - Name - SimpleModelInfo.cpp - Windows - Text - Debug - - - Name - SimpleModelInfo.h - Windows - Text - - - - Name - TimeModelInfo.cpp - Windows - Text - Debug - - - Name - TimeModelInfo.h - Windows - Text - - - - Name - VehicleModelInfo.cpp - Windows - Text - Debug - - - Name - VehicleModelInfo.h - Windows - Text - - - - Name - XtraCompsModelInfo.h - Windows - Text - - - - Name - CutsceneHead.cpp - Windows - Text - Debug - - - Name - CutsceneHead.h - Windows - Text - - - - Name - CutsceneObject.cpp - Windows - Text - Debug - - - Name - CutsceneObject.h - Windows - Text - - - - Name - DummyObject.cpp - Windows - Text - Debug - - - Name - DummyObject.h - Windows - Text - - - - Name - Object.cpp - Windows - Text - Debug - - - Name - Object.h - Windows - Text - - - - Name - ObjectData.cpp - Windows - Text - Debug - - - Name - ObjectData.h - Windows - Text - - - - Name - ParticleObject.cpp - Windows - Text - Debug - - - Name - ParticleObject.h - Windows - Text - - - - Name - Projectile.cpp - Windows - Text - Debug - - - Name - Projectile.h - Windows - Text - - - - Name - CivilianPed.cpp - Windows - Text - Debug - - - Name - CivilianPed.h - Windows - Text - - - - Name - CopPed.cpp - Windows - Text - Debug - - - Name - CopPed.h - Windows - Text - - - - Name - DummyPed.h - Windows - Text - - - - Name - EmergencyPed.cpp - Windows - Text - Debug - - - Name - EmergencyPed.h - Windows - Text - - - - Name - Gangs.cpp - Windows - Text - Debug - - - Name - Gangs.h - Windows - Text - - - - Name - Ped.cpp - Windows - Text - Debug - - - Name - Ped.h - Windows - Text - - - - Name - PedAI.cpp - Windows - Text - Debug - - - Name - PedChat.cpp - Windows - Text - Debug - - - Name - PedDebug.cpp - Windows - Text - Debug - - - Name - PedFight.cpp - Windows - Text - Debug - - - Name - PedIK.cpp - Windows - Text - Debug - - - Name - PedIK.h - Windows - Text - - - - Name - PedPlacement.cpp - Windows - Text - Debug - - - Name - PedPlacement.h - Windows - Text - - - - Name - PedRoutes.cpp - Windows - Text - Debug - - - Name - PedRoutes.h - Windows - Text - - - - Name - PedType.cpp - Windows - Text - Debug - - - Name - PedType.h - Windows - Text - - - - Name - PlayerPed.cpp - Windows - Text - Debug - - - Name - PlayerPed.h - Windows - Text - - - - Name - Population.cpp - Windows - Text - Debug - - - Name - Population.h - Windows - Text - - - - Name - 2dEffect.h - Windows - Text - - - - Name - Antennas.cpp - Windows - Text - Debug - - - Name - Antennas.h - Windows - Text - - - - Name - Clouds.cpp - Windows - Text - Debug - - - Name - Clouds.h - Windows - Text - - - - Name - Console.cpp - Windows - Text - Debug - - - Name - Console.h - Windows - Text - - - - Name - Coronas.cpp - Windows - Text - Debug - - - Name - Coronas.h - Windows - Text - - - - Name - Credits.cpp - Windows - Text - Debug - - - Name - Credits.h - Windows - Text - - - - Name - Draw.cpp - Windows - Text - Debug - - - Name - Draw.h - Windows - Text - - - - Name - Fluff.cpp - Windows - Text - Debug - - - Name - Fluff.h - Windows - Text - - - - Name - Font.cpp - Windows - Text - Debug - - - Name - Font.h - Windows - Text - - - - Name - Glass.cpp - Windows - Text - Debug - - - Name - Glass.h - Windows - Text - - - - Name - Hud.cpp - Windows - Text - Debug - - - Name - Hud.h - Windows - Text - - - - Name - Instance.cpp - Windows - Text - Debug - - - Name - Instance.h - Windows - Text - - - - Name - Lines.cpp - Windows - Text - Debug - - - Name - Lines.h - Windows - Text - - - - Name - MBlur.cpp - Windows - Text - Debug - - - Name - MBlur.h - Windows - Text - - - - Name - Particle.cpp - Windows - Text - Debug - - - Name - Particle.h - Windows - Text - - - - Name - ParticleMgr.cpp - Windows - Text - Debug - - - Name - ParticleMgr.h - Windows - Text - - - - Name - ParticleType.h - Windows - Text - - - - Name - PlayerSkin.cpp - Windows - Text - Debug - - - Name - PlayerSkin.h - Windows - Text - - - - Name - PointLights.cpp - Windows - Text - Debug - - - Name - PointLights.h - Windows - Text - - - - Name - RenderBuffer.cpp - Windows - Text - Debug - - - Name - RenderBuffer.h - Windows - Text - - - - Name - Renderer.cpp - Windows - Text - Debug - - - Name - Renderer.h - Windows - Text - - - - Name - Rubbish.cpp - Windows - Text - Debug - - - Name - Rubbish.h - Windows - Text - - - - Name - Shadows.cpp - Windows - Text - Debug - - - Name - Shadows.h - Windows - Text - - - - Name - Skidmarks.cpp - Windows - Text - Debug - - - Name - Skidmarks.h - Windows - Text - - - - Name - SpecialFX.cpp - Windows - Text - Debug - - - Name - SpecialFX.h - Windows - Text - - - - Name - Sprite.cpp - Windows - Text - Debug - - - Name - Sprite.h - Windows - Text - - - - Name - Sprite2d.cpp - Windows - Text - Debug - - - Name - Sprite2d.h - Windows - Text - - - - Name - TexList.cpp - Windows - Text - Debug - - - Name - TexList.h - Windows - Text - - - - Name - Timecycle.cpp - Windows - Text - Debug - - - Name - Timecycle.h - Windows - Text - - - - Name - WaterCannon.cpp - Windows - Text - Debug - - - Name - WaterCannon.h - Windows - Text - - - - Name - WaterLevel.cpp - Windows - Text - Debug - - - Name - WaterLevel.h - Windows - Text - - - - Name - Weather.cpp - Windows - Text - Debug - - - Name - Weather.h - Windows - Text - - - - Name - ClumpRead.cpp - Windows - Text - Debug - - - Name - Lights.cpp - Windows - Text - Debug - - - Name - Lights.h - Windows - Text - - - - Name - MemoryHeap.cpp - Windows - Text - Debug - - - Name - MemoryHeap.h - Windows - Text - - - - Name - MemoryMgr.cpp - Windows - Text - Debug - - - Name - MemoryMgr.h - Windows - Text - - - - Name - NodeName.cpp - Windows - Text - Debug - - - Name - NodeName.h - Windows - Text - - - - Name - RwHelper.cpp - Windows - Text - Debug - - - Name - RwHelper.h - Windows - Text - - - - Name - RwMatFX.cpp - Windows - Text - Debug - - - Name - RwPS2AlphaTest.cpp - Windows - Text - Debug - - - Name - TexRead.cpp - Windows - Text - Debug - - - Name - TexturePools.cpp - Windows - Text - Debug - - - Name - TexturePools.h - Windows - Text - - - - Name - TxdStore.cpp - Windows - Text - Debug - - - Name - TxdStore.h - Windows - Text - - - - Name - VisibilityPlugins.cpp - Windows - Text - Debug - - - Name - VisibilityPlugins.h - Windows - Text - - - - Name - Date.cpp - Windows - Text - Debug - - - Name - Date.h - Windows - Text - - - - Name - GenericGameStorage.cpp - Windows - Text - Debug - - - Name - GenericGameStorage.h - Windows - Text - - - - Name - MemoryCard.cpp - Windows - Text - Debug - - - Name - MemoryCard.h - Windows - Text - - - - Name - PCSave.cpp - Windows - Text - Debug - - - Name - PCSave.h - Windows - Text - - - - Name - crossplatform.cpp - Windows - Text - Debug - - - Name - crossplatform.h - Windows - Text - - - - Name - events.cpp - Windows - Text - Debug - - - Name - events.h - Windows - Text - - - - Name - platform.h - Windows - Text - - - - Name - skeleton.cpp - Windows - Text - Debug - - - Name - skeleton.h - Windows - Text - - - - Name - resource.h - Windows - Text - - - - Name - win.cpp - Windows - Text - Debug - - - Name - win.h - Windows - Text - - - - Name - win.rc - Windows - Text - Debug - - - Name - Messages.cpp - Windows - Text - Debug - - - Name - Messages.h - Windows - Text - - - - Name - Pager.cpp - Windows - Text - Debug - - - Name - Pager.h - Windows - Text - - - - Name - Text.cpp - Windows - Text - Debug - - - Name - Text.h - Windows - Text - - - - Name - Automobile.cpp - Windows - Text - Debug - - - Name - Automobile.h - Windows - Text - - - - Name - Bike.h - Windows - Text - - - - Name - Boat.cpp - Windows - Text - Debug - - - Name - Boat.h - Windows - Text - - - - Name - CarGen.cpp - Windows - Text - Debug - - - Name - CarGen.h - Windows - Text - - - - Name - Cranes.cpp - Windows - Text - Debug - - - Name - Cranes.h - Windows - Text - - - - Name - DamageManager.cpp - Windows - Text - Debug - - - Name - DamageManager.h - Windows - Text - - - - Name - Door.cpp - Windows - Text - Debug - - - Name - Door.h - Windows - Text - - - - Name - Floater.cpp - Windows - Text - Debug - - - Name - Floater.h - Windows - Text - - - - Name - HandlingMgr.cpp - Windows - Text - Debug - - - Name - HandlingMgr.h - Windows - Text - - - - Name - Heli.cpp - Windows - Text - Debug - - - Name - Heli.h - Windows - Text - - - - Name - Plane.cpp - Windows - Text - Debug - - - Name - Plane.h - Windows - Text - - - - Name - Train.cpp - Windows - Text - Debug - - - Name - Train.h - Windows - Text - - - - Name - Transmission.cpp - Windows - Text - Debug - - - Name - Transmission.h - Windows - Text - - - - Name - Vehicle.cpp - Windows - Text - Debug - - - Name - Vehicle.h - Windows - Text - - - - Name - BulletInfo.cpp - Windows - Text - Debug - - - Name - BulletInfo.h - Windows - Text - - - - Name - Explosion.cpp - Windows - Text - Debug - - - Name - Explosion.h - Windows - Text - - - - Name - ProjectileInfo.cpp - Windows - Text - Debug - - - Name - ProjectileInfo.h - Windows - Text - - - - Name - ShotInfo.cpp - Windows - Text - Debug - - - Name - ShotInfo.h - Windows - Text - - - - Name - Weapon.cpp - Windows - Text - Debug - - - Name - Weapon.h - Windows - Text - - - - Name - WeaponEffects.cpp - Windows - Text - Debug - - - Name - WeaponEffects.h - Windows - Text - - - - Name - WeaponInfo.cpp - Windows - Text - Debug - - - Name - WeaponInfo.h - Windows - Text - - - - Name - WeaponType.h - Windows - Text - - - - Name - mss32.lib - Windows - Library - Debug - - - Name - d3d8.lib - Windows - Library - Debug - - - Name - ddraw.lib - Windows - Library - Debug - - - Name - dxguid.lib - Windows - Library - Debug - - - Name - strmiids.lib - Windows - Library - Debug - - - Name - dinput8.lib - Windows - Library - Debug - - - Name - winmm.lib - Windows - Library - Debug - - - Name - rwcore.lib - Windows - Library - Debug - - - Name - rpworld.lib - Windows - Library - Debug - - - Name - rpmatfx.lib - Windows - Library - Debug - - - Name - rpskin.lib - Windows - Library - Debug - - - Name - rphanim.lib - Windows - Library - Debug - - - Name - rtbmp.lib - Windows - Library - Debug - - - Name - rtquat.lib - Windows - Library - Debug - - - Name - rtcharse.lib - Windows - Library - Debug - - - Name - ole32.lib - Windows - Library - Debug - - - Name - shell32.lib - Windows - Library - Debug - - - Name - uuid.lib - Windows - Library - Debug - - - - - Name - AnimationId.h - Windows - - - Name - AnimBlendAssocGroup.cpp - Windows - - - Name - AnimBlendAssocGroup.h - Windows - - - Name - AnimBlendAssociation.cpp - Windows - - - Name - AnimBlendAssociation.h - Windows - - - Name - AnimBlendClumpData.cpp - Windows - - - Name - AnimBlendClumpData.h - Windows - - - Name - AnimBlendHierarchy.cpp - Windows - - - Name - AnimBlendHierarchy.h - Windows - - - Name - AnimBlendList.h - Windows - - - Name - AnimBlendNode.cpp - Windows - - - Name - AnimBlendNode.h - Windows - - - Name - AnimBlendSequence.cpp - Windows - - - Name - AnimBlendSequence.h - Windows - - - Name - AnimManager.cpp - Windows - - - Name - AnimManager.h - Windows - - - Name - Bones.cpp - Windows - - - Name - Bones.h - Windows - - - Name - CutsceneMgr.cpp - Windows - - - Name - CutsceneMgr.h - Windows - - - Name - FrameUpdate.cpp - Windows - - - Name - RpAnimBlend.cpp - Windows - - - Name - RpAnimBlend.h - Windows - - - Name - audio_enums.h - Windows - - - Name - AudioCollision.cpp - Windows - - - Name - AudioCollision.h - Windows - - - Name - AudioLogic.cpp - Windows - - - Name - AudioManager.cpp - Windows - - - Name - AudioManager.h - Windows - - - Name - AudioSamples.h - Windows - - - Name - AudioScriptObject.cpp - Windows - - - Name - AudioScriptObject.h - Windows - - - Name - DMAudio.cpp - Windows - - - Name - DMAudio.h - Windows - - - Name - MusicManager.cpp - Windows - - - Name - MusicManager.h - Windows - - - Name - PolRadio.cpp - Windows - - - Name - PolRadio.h - Windows - - - Name - sampman.h - Windows - - - Name - sampman_miles.cpp - Windows - - - Name - soundlist.h - Windows - - - Name - eax.h - Windows - - - Name - eax-util.cpp - Windows - - - Name - eax-util.h - Windows - - - Name - Building.cpp - Windows - - - Name - Building.h - Windows - - - Name - Solid.h - Windows - - - Name - Treadable.cpp - Windows - - - Name - Treadable.h - Windows - - - Name - ColBox.cpp - Windows - - - Name - ColBox.h - Windows - - - Name - ColLine.cpp - Windows - - - Name - ColLine.h - Windows - - - Name - Collision.cpp - Windows - - - Name - Collision.h - Windows - - - Name - ColModel.cpp - Windows - - - Name - ColModel.h - Windows - - - Name - ColPoint.cpp - Windows - - - Name - ColPoint.h - Windows - - - Name - ColSphere.cpp - Windows - - - Name - ColSphere.h - Windows - - - Name - ColTriangle.cpp - Windows - - - Name - ColTriangle.h - Windows - - - Name - CompressedVector.h - Windows - - - Name - TempColModels.cpp - Windows - - - Name - TempColModels.h - Windows - - - Name - VuCollision.cpp - Windows - - - Name - VuCollision.h - Windows - - - Name - AutoPilot.cpp - Windows - - - Name - AutoPilot.h - Windows - - - Name - Bridge.cpp - Windows - - - Name - Bridge.h - Windows - - - Name - CarAI.cpp - Windows - - - Name - CarAI.h - Windows - - - Name - CarCtrl.cpp - Windows - - - Name - CarCtrl.h - Windows - - - Name - Curves.cpp - Windows - - - Name - Curves.h - Windows - - - Name - Darkel.cpp - Windows - - - Name - Darkel.h - Windows - - - Name - GameLogic.cpp - Windows - - - Name - GameLogic.h - Windows - - - Name - Garages.cpp - Windows - - - Name - Garages.h - Windows - - - Name - NameGrid.cpp - Windows - - - Name - NameGrid.h - Windows - - - Name - OnscreenTimer.cpp - Windows - - - Name - OnscreenTimer.h - Windows - - - Name - PathFind.cpp - Windows - - - Name - PathFind.h - Windows - - - Name - Phones.cpp - Windows - - - Name - Phones.h - Windows - - - Name - Pickups.cpp - Windows - - - Name - Pickups.h - Windows - - - Name - PowerPoints.cpp - Windows - - - Name - PowerPoints.h - Windows - - - Name - Record.cpp - Windows - - - Name - Record.h - Windows - - - Name - Remote.cpp - Windows - - - Name - Remote.h - Windows - - - Name - Replay.cpp - Windows - - - Name - Replay.h - Windows - - - Name - Restart.cpp - Windows - - - Name - Restart.h - Windows - - - Name - RoadBlocks.cpp - Windows - - - Name - RoadBlocks.h - Windows - - - Name - SceneEdit.cpp - Windows - - - Name - SceneEdit.h - Windows - - - Name - ScriptDebug.cpp - Windows - - - Name - Script.cpp - Windows - - - Name - Script.h - Windows - - - Name - Script2.cpp - Windows - - - Name - Script3.cpp - Windows - - - Name - Script4.cpp - Windows - - - Name - Script5.cpp - Windows - - - Name - Script6.cpp - Windows - - - Name - ScriptCommands.h - Windows - - - Name - TrafficLights.cpp - Windows - - - Name - TrafficLights.h - Windows - - - Name - Accident.cpp - Windows - - - Name - Accident.h - Windows - - - Name - AnimViewer.cpp - Windows - - - Name - AnimViewer.h - Windows - - - Name - Cam.cpp - Windows - - - Name - Camera.cpp - Windows - - - Name - Camera.h - Windows - - - Name - CdStream.cpp - Windows - - - Name - CdStream.h - Windows - - - Name - CdStreamPosix.cpp - Windows - - - Name - Clock.cpp - Windows - - - Name - Clock.h - Windows - - - Name - common.h - Windows - - - Name - config.h - Windows - - - Name - ControllerConfig.cpp - Windows - - - Name - ControllerConfig.h - Windows - - - Name - Crime.h - Windows - - - Name - Debug.cpp - Windows - - - Name - Debug.h - Windows - - - Name - Directory.cpp - Windows - - - Name - Directory.h - Windows - - - Name - EventList.cpp - Windows - - - Name - EventList.h - Windows - - - Name - FileLoader.cpp - Windows - - - Name - FileLoader.h - Windows - - - Name - FileMgr.cpp - Windows - - - Name - FileMgr.h - Windows - - - Name - Fire.cpp - Windows - - - Name - Fire.h - Windows - - - Name - Frontend.cpp - Windows - - - Name - Frontend.h - Windows - - - Name - Frontend_PS2.cpp - Windows - - - Name - Frontend_PS2.h - Windows - - - Name - FrontEndControls.cpp - Windows - - - Name - FrontEndControls.h - Windows - - - Name - FrontendTriggers.h - Windows - - - Name - Game.cpp - Windows - - - Name - Game.h - Windows - - - Name - General.h - Windows - - - Name - IniFile.cpp - Windows - - - Name - IniFile.h - Windows - - - Name - Lists.cpp - Windows - - - Name - Lists.h - Windows - - - Name - main.cpp - Windows - - - Name - main.h - Windows - - - Name - MenuScreens.cpp - Windows - - - Name - MenuScreensCustom.cpp - Windows - - - Name - obrstr.cpp - Windows - - - Name - obrstr.h - Windows - - - Name - Pad.cpp - Windows - - - Name - Pad.h - Windows - - - Name - Placeable.cpp - Windows - - - Name - Placeable.h - Windows - - - Name - PlayerInfo.cpp - Windows - - - Name - PlayerInfo.h - Windows - - - Name - Pools.cpp - Windows - - - Name - Pools.h - Windows - - - Name - Profile.cpp - Windows - - - Name - Profile.h - Windows - - - Name - Radar.cpp - Windows - - - Name - Radar.h - Windows - - - Name - Range2D.cpp - Windows - - - Name - Range2D.h - Windows - - - Name - Range3D.cpp - Windows - - - Name - Range3D.h - Windows - - - Name - re3.cpp - Windows - - - Name - References.cpp - Windows - - - Name - References.h - Windows - - - Name - Stats.cpp - Windows - - - Name - Stats.h - Windows - - - Name - Streaming.cpp - Windows - - - Name - Streaming.h - Windows - - - Name - SurfaceTable.cpp - Windows - - - Name - SurfaceTable.h - Windows - - - Name - templates.h - Windows - - - Name - timebars.cpp - Windows - - - Name - timebars.h - Windows - - - Name - Timer.cpp - Windows - - - Name - Timer.h - Windows - - - Name - TimeStep.cpp - Windows - - - Name - TimeStep.h - Windows - - - Name - User.cpp - Windows - - - Name - User.h - Windows - - - Name - Wanted.cpp - Windows - - - Name - Wanted.h - Windows - - - Name - World.cpp - Windows - - - Name - World.h - Windows - - - Name - ZoneCull.cpp - Windows - - - Name - ZoneCull.h - Windows - - - Name - Zones.cpp - Windows - - - Name - Zones.h - Windows - - - Name - Dummy.cpp - Windows - - - Name - Dummy.h - Windows - - - Name - Entity.cpp - Windows - - - Name - Entity.h - Windows - - - Name - Physical.cpp - Windows - - - Name - Physical.h - Windows - - - Name - math.cpp - Windows - - - Name - maths.h - Windows - - - Name - Matrix.cpp - Windows - - - Name - Matrix.h - Windows - - - Name - Quaternion.cpp - Windows - - - Name - Quaternion.h - Windows - - - Name - Rect.cpp - Windows - - - Name - Rect.h - Windows - - - Name - Vector.cpp - Windows - - - Name - Vector.h - Windows - - - Name - Vector2D.h - Windows - - - Name - VuVector.h - Windows - - - Name - BaseModelInfo.cpp - Windows - - - Name - BaseModelInfo.h - Windows - - - Name - ClumpModelInfo.cpp - Windows - - - Name - ClumpModelInfo.h - Windows - - - Name - MloModelInfo.cpp - Windows - - - Name - MloModelInfo.h - Windows - - - Name - ModelIndices.cpp - Windows - - - Name - ModelIndices.h - Windows - - - Name - ModelInfo.cpp - Windows - - - Name - ModelInfo.h - Windows - - - Name - PedModelInfo.cpp - Windows - - - Name - PedModelInfo.h - Windows - - - Name - SimpleModelInfo.cpp - Windows - - - Name - SimpleModelInfo.h - Windows - - - Name - TimeModelInfo.cpp - Windows - - - Name - TimeModelInfo.h - Windows - - - Name - VehicleModelInfo.cpp - Windows - - - Name - VehicleModelInfo.h - Windows - - - Name - XtraCompsModelInfo.h - Windows - - - Name - CutsceneHead.cpp - Windows - - - Name - CutsceneHead.h - Windows - - - Name - CutsceneObject.cpp - Windows - - - Name - CutsceneObject.h - Windows - - - Name - DummyObject.cpp - Windows - - - Name - DummyObject.h - Windows - - - Name - Object.cpp - Windows - - - Name - Object.h - Windows - - - Name - ObjectData.cpp - Windows - - - Name - ObjectData.h - Windows - - - Name - ParticleObject.cpp - Windows - - - Name - ParticleObject.h - Windows - - - Name - Projectile.cpp - Windows - - - Name - Projectile.h - Windows - - - Name - CivilianPed.cpp - Windows - - - Name - CivilianPed.h - Windows - - - Name - CopPed.cpp - Windows - - - Name - CopPed.h - Windows - - - Name - DummyPed.h - Windows - - - Name - EmergencyPed.cpp - Windows - - - Name - EmergencyPed.h - Windows - - - Name - Gangs.cpp - Windows - - - Name - Gangs.h - Windows - - - Name - Ped.cpp - Windows - - - Name - Ped.h - Windows - - - Name - PedAI.cpp - Windows - - - Name - PedChat.cpp - Windows - - - Name - PedDebug.cpp - Windows - - - Name - PedFight.cpp - Windows - - - Name - PedIK.cpp - Windows - - - Name - PedIK.h - Windows - - - Name - PedPlacement.cpp - Windows - - - Name - PedPlacement.h - Windows - - - Name - PedRoutes.cpp - Windows - - - Name - PedRoutes.h - Windows - - - Name - PedType.cpp - Windows - - - Name - PedType.h - Windows - - - Name - PlayerPed.cpp - Windows - - - Name - PlayerPed.h - Windows - - - Name - Population.cpp - Windows - - - Name - Population.h - Windows - - - Name - 2dEffect.h - Windows - - - Name - Antennas.cpp - Windows - - - Name - Antennas.h - Windows - - - Name - Clouds.cpp - Windows - - - Name - Clouds.h - Windows - - - Name - Console.cpp - Windows - - - Name - Console.h - Windows - - - Name - Coronas.cpp - Windows - - - Name - Coronas.h - Windows - - - Name - Credits.cpp - Windows - - - Name - Credits.h - Windows - - - Name - Draw.cpp - Windows - - - Name - Draw.h - Windows - - - Name - Fluff.cpp - Windows - - - Name - Fluff.h - Windows - - - Name - Font.cpp - Windows - - - Name - Font.h - Windows - - - Name - Glass.cpp - Windows - - - Name - Glass.h - Windows - - - Name - Hud.cpp - Windows - - - Name - Hud.h - Windows - - - Name - Instance.cpp - Windows - - - Name - Instance.h - Windows - - - Name - Lines.cpp - Windows - - - Name - Lines.h - Windows - - - Name - MBlur.cpp - Windows - - - Name - MBlur.h - Windows - - - Name - Particle.cpp - Windows - - - Name - Particle.h - Windows - - - Name - ParticleMgr.cpp - Windows - - - Name - ParticleMgr.h - Windows - - - Name - ParticleType.h - Windows - - - Name - PlayerSkin.cpp - Windows - - - Name - PlayerSkin.h - Windows - - - Name - PointLights.cpp - Windows - - - Name - PointLights.h - Windows - - - Name - RenderBuffer.cpp - Windows - - - Name - RenderBuffer.h - Windows - - - Name - Renderer.cpp - Windows - - - Name - Renderer.h - Windows - - - Name - Rubbish.cpp - Windows - - - Name - Rubbish.h - Windows - - - Name - Shadows.cpp - Windows - - - Name - Shadows.h - Windows - - - Name - Skidmarks.cpp - Windows - - - Name - Skidmarks.h - Windows - - - Name - SpecialFX.cpp - Windows - - - Name - SpecialFX.h - Windows - - - Name - Sprite.cpp - Windows - - - Name - Sprite.h - Windows - - - Name - Sprite2d.cpp - Windows - - - Name - Sprite2d.h - Windows - - - Name - TexList.cpp - Windows - - - Name - TexList.h - Windows - - - Name - Timecycle.cpp - Windows - - - Name - Timecycle.h - Windows - - - Name - WaterCannon.cpp - Windows - - - Name - WaterCannon.h - Windows - - - Name - WaterLevel.cpp - Windows - - - Name - WaterLevel.h - Windows - - - Name - Weather.cpp - Windows - - - Name - Weather.h - Windows - - - Name - ClumpRead.cpp - Windows - - - Name - Lights.cpp - Windows - - - Name - Lights.h - Windows - - - Name - MemoryHeap.cpp - Windows - - - Name - MemoryHeap.h - Windows - - - Name - MemoryMgr.cpp - Windows - - - Name - MemoryMgr.h - Windows - - - Name - NodeName.cpp - Windows - - - Name - NodeName.h - Windows - - - Name - RwHelper.cpp - Windows - - - Name - RwHelper.h - Windows - - - Name - RwMatFX.cpp - Windows - - - Name - RwPS2AlphaTest.cpp - Windows - - - Name - TexRead.cpp - Windows - - - Name - TexturePools.cpp - Windows - - - Name - TexturePools.h - Windows - - - Name - TxdStore.cpp - Windows - - - Name - TxdStore.h - Windows - - - Name - VisibilityPlugins.cpp - Windows - - - Name - VisibilityPlugins.h - Windows - - - Name - Date.cpp - Windows - - - Name - Date.h - Windows - - - Name - GenericGameStorage.cpp - Windows - - - Name - GenericGameStorage.h - Windows - - - Name - MemoryCard.cpp - Windows - - - Name - MemoryCard.h - Windows - - - Name - PCSave.cpp - Windows - - - Name - PCSave.h - Windows - - - Name - crossplatform.cpp - Windows - - - Name - crossplatform.h - Windows - - - Name - events.cpp - Windows - - - Name - events.h - Windows - - - Name - platform.h - Windows - - - Name - skeleton.cpp - Windows - - - Name - skeleton.h - Windows - - - Name - resource.h - Windows - - - Name - win.cpp - Windows - - - Name - win.h - Windows - - - Name - win.rc - Windows - - - Name - Messages.cpp - Windows - - - Name - Messages.h - Windows - - - Name - Pager.cpp - Windows - - - Name - Pager.h - Windows - - - Name - Text.cpp - Windows - - - Name - Text.h - Windows - - - Name - Automobile.cpp - Windows - - - Name - Automobile.h - Windows - - - Name - Bike.h - Windows - - - Name - Boat.cpp - Windows - - - Name - Boat.h - Windows - - - Name - CarGen.cpp - Windows - - - Name - CarGen.h - Windows - - - Name - Cranes.cpp - Windows - - - Name - Cranes.h - Windows - - - Name - DamageManager.cpp - Windows - - - Name - DamageManager.h - Windows - - - Name - Door.cpp - Windows - - - Name - Door.h - Windows - - - Name - Floater.cpp - Windows - - - Name - Floater.h - Windows - - - Name - HandlingMgr.cpp - Windows - - - Name - HandlingMgr.h - Windows - - - Name - Heli.cpp - Windows - - - Name - Heli.h - Windows - - - Name - Plane.cpp - Windows - - - Name - Plane.h - Windows - - - Name - Train.cpp - Windows - - - Name - Train.h - Windows - - - Name - Transmission.cpp - Windows - - - Name - Transmission.h - Windows - - - Name - Vehicle.cpp - Windows - - - Name - Vehicle.h - Windows - - - Name - BulletInfo.cpp - Windows - - - Name - BulletInfo.h - Windows - - - Name - Explosion.cpp - Windows - - - Name - Explosion.h - Windows - - - Name - ProjectileInfo.cpp - Windows - - - Name - ProjectileInfo.h - Windows - - - Name - ShotInfo.cpp - Windows - - - Name - ShotInfo.h - Windows - - - Name - Weapon.cpp - Windows - - - Name - Weapon.h - Windows - - - Name - WeaponEffects.cpp - Windows - - - Name - WeaponEffects.h - Windows - - - Name - WeaponInfo.cpp - Windows - - - Name - WeaponInfo.h - Windows - - - Name - WeaponType.h - Windows - - - Name - mss32.lib - Windows - - - Name - d3d8.lib - Windows - - - Name - ddraw.lib - Windows - - - Name - dxguid.lib - Windows - - - Name - strmiids.lib - Windows - - - Name - dinput8.lib - Windows - - - Name - winmm.lib - Windows - - - Name - rwcore.lib - Windows - - - Name - rpworld.lib - Windows - - - Name - rpmatfx.lib - Windows - - - Name - rpskin.lib - Windows - - - Name - rphanim.lib - Windows - - - Name - rtbmp.lib - Windows - - - Name - rtquat.lib - Windows - - - Name - rtcharse.lib - Windows - - - Name - MSL_All_x86.lib - MacOS - - - Name - Comdlg32.lib - MacOS - - - Name - Gdi32.lib - MacOS - - - Name - Kernel32.lib - MacOS - - - Name - User32.lib - MacOS - - - Name - ole32.lib - Windows - - - Name - shell32.lib - Windows - - - Name - uuid.lib - Windows - - - - - - - Debug - Release - - - - animation - - Debug - Name - AnimationId.h - Windows - - - Debug - Name - AnimBlendAssocGroup.cpp - Windows - - - Debug - Name - AnimBlendAssocGroup.h - Windows - - - Debug - Name - AnimBlendAssociation.cpp - Windows - - - Debug - Name - AnimBlendAssociation.h - Windows - - - Debug - Name - AnimBlendClumpData.cpp - Windows - - - Debug - Name - AnimBlendClumpData.h - Windows - - - Debug - Name - AnimBlendHierarchy.cpp - Windows - - - Debug - Name - AnimBlendHierarchy.h - Windows - - - Debug - Name - AnimBlendList.h - Windows - - - Debug - Name - AnimBlendNode.cpp - Windows - - - Debug - Name - AnimBlendNode.h - Windows - - - Debug - Name - AnimBlendSequence.cpp - Windows - - - Debug - Name - AnimBlendSequence.h - Windows - - - Debug - Name - AnimManager.cpp - Windows - - - Debug - Name - AnimManager.h - Windows - - - Debug - Name - Bones.cpp - Windows - - - Debug - Name - Bones.h - Windows - - - Debug - Name - CutsceneMgr.cpp - Windows - - - Debug - Name - CutsceneMgr.h - Windows - - - Debug - Name - FrameUpdate.cpp - Windows - - - Debug - Name - RpAnimBlend.cpp - Windows - - - Debug - Name - RpAnimBlend.h - Windows - - - audio - - Debug - Name - audio_enums.h - Windows - - - Debug - Name - AudioCollision.cpp - Windows - - - Debug - Name - AudioCollision.h - Windows - - - Debug - Name - AudioLogic.cpp - Windows - - - Debug - Name - AudioManager.cpp - Windows - - - Debug - Name - AudioManager.h - Windows - - - Debug - Name - AudioSamples.h - Windows - - - Debug - Name - AudioScriptObject.cpp - Windows - - - Debug - Name - AudioScriptObject.h - Windows - - - Debug - Name - DMAudio.cpp - Windows - - - Debug - Name - DMAudio.h - Windows - - - Debug - Name - MusicManager.cpp - Windows - - - Debug - Name - MusicManager.h - Windows - - - Release - Name - PolRadio.cpp - Windows - - - Release - Name - PolRadio.h - Windows - - - Debug - Name - sampman.h - Windows - - - Debug - Name - sampman_miles.cpp - Windows - - - Debug - Name - soundlist.h - Windows - - - Debug - Name - eax.h - Windows - - - Debug - Name - eax-util.cpp - Windows - - - Debug - Name - eax-util.h - Windows - - - buildings - - Debug - Name - Building.cpp - Windows - - - Debug - Name - Building.h - Windows - - - Debug - Name - Solid.h - Windows - - - Debug - Name - Treadable.cpp - Windows - - - Debug - Name - Treadable.h - Windows - - - collision - - Debug - Name - ColBox.cpp - Windows - - - Debug - Name - ColBox.h - Windows - - - Debug - Name - ColLine.cpp - Windows - - - Debug - Name - ColLine.h - Windows - - - Debug - Name - Collision.cpp - Windows - - - Debug - Name - Collision.h - Windows - - - Debug - Name - ColModel.cpp - Windows - - - Debug - Name - ColModel.h - Windows - - - Debug - Name - ColPoint.cpp - Windows - - - Debug - Name - ColPoint.h - Windows - - - Debug - Name - ColSphere.cpp - Windows - - - Debug - Name - ColSphere.h - Windows - - - Debug - Name - ColTriangle.cpp - Windows - - - Debug - Name - ColTriangle.h - Windows - - - Debug - Name - CompressedVector.h - Windows - - - Debug - Name - TempColModels.cpp - Windows - - - Debug - Name - TempColModels.h - Windows - - - Debug - Name - VuCollision.cpp - Windows - - - Debug - Name - VuCollision.h - Windows - - - control - - Debug - Name - AutoPilot.cpp - Windows - - - Debug - Name - AutoPilot.h - Windows - - - Debug - Name - Bridge.cpp - Windows - - - Debug - Name - Bridge.h - Windows - - - Debug - Name - CarAI.cpp - Windows - - - Debug - Name - CarAI.h - Windows - - - Debug - Name - CarCtrl.cpp - Windows - - - Debug - Name - CarCtrl.h - Windows - - - Debug - Name - Curves.cpp - Windows - - - Debug - Name - Curves.h - Windows - - - Debug - Name - Darkel.cpp - Windows - - - Debug - Name - Darkel.h - Windows - - - Debug - Name - GameLogic.cpp - Windows - - - Debug - Name - GameLogic.h - Windows - - - Debug - Name - Garages.cpp - Windows - - - Debug - Name - Garages.h - Windows - - - Debug - Name - NameGrid.cpp - Windows - - - Debug - Name - NameGrid.h - Windows - - - Debug - Name - OnscreenTimer.cpp - Windows - - - Debug - Name - OnscreenTimer.h - Windows - - - Debug - Name - PathFind.cpp - Windows - - - Debug - Name - PathFind.h - Windows - - - Debug - Name - Phones.cpp - Windows - - - Debug - Name - Phones.h - Windows - - - Debug - Name - Pickups.cpp - Windows - - - Debug - Name - Pickups.h - Windows - - - Debug - Name - PowerPoints.cpp - Windows - - - Debug - Name - PowerPoints.h - Windows - - - Debug - Name - Record.cpp - Windows - - - Debug - Name - Record.h - Windows - - - Debug - Name - Remote.cpp - Windows - - - Debug - Name - Remote.h - Windows - - - Debug - Name - Replay.cpp - Windows - - - Debug - Name - Replay.h - Windows - - - Debug - Name - Restart.cpp - Windows - - - Debug - Name - Restart.h - Windows - - - Debug - Name - RoadBlocks.cpp - Windows - - - Debug - Name - RoadBlocks.h - Windows - - - Debug - Name - SceneEdit.cpp - Windows - - - Debug - Name - SceneEdit.h - Windows - - - Debug - Name - ScriptDebug.cpp - Windows - - - Debug - Name - Script.cpp - Windows - - - Debug - Name - Script.h - Windows - - - Debug - Name - Script2.cpp - Windows - - - Debug - Name - Script3.cpp - Windows - - - Debug - Name - Script4.cpp - Windows - - - Debug - Name - Script5.cpp - Windows - - - Debug - Name - Script6.cpp - Windows - - - Debug - Name - ScriptCommands.h - Windows - - - Debug - Name - TrafficLights.cpp - Windows - - - Debug - Name - TrafficLights.h - Windows - - - core - - Debug - Name - Accident.cpp - Windows - - - Debug - Name - Accident.h - Windows - - - Debug - Name - AnimViewer.cpp - Windows - - - Debug - Name - AnimViewer.h - Windows - - - Debug - Name - Cam.cpp - Windows - - - Debug - Name - Camera.cpp - Windows - - - Debug - Name - Camera.h - Windows - - - Debug - Name - CdStream.cpp - Windows - - - Debug - Name - CdStream.h - Windows - - - Debug - Name - CdStreamPosix.cpp - Windows - - - Debug - Name - Clock.cpp - Windows - - - Debug - Name - Clock.h - Windows - - - Debug - Name - common.h - Windows - - - Debug - Name - config.h - Windows - - - Debug - Name - ControllerConfig.cpp - Windows - - - Debug - Name - ControllerConfig.h - Windows - - - Debug - Name - Crime.h - Windows - - - Debug - Name - Debug.cpp - Windows - - - Debug - Name - Debug.h - Windows - - - Debug - Name - Directory.cpp - Windows - - - Debug - Name - Directory.h - Windows - - - Debug - Name - EventList.cpp - Windows - - - Debug - Name - EventList.h - Windows - - - Debug - Name - FileLoader.cpp - Windows - - - Debug - Name - FileLoader.h - Windows - - - Debug - Name - FileMgr.cpp - Windows - - - Debug - Name - FileMgr.h - Windows - - - Debug - Name - Fire.cpp - Windows - - - Debug - Name - Fire.h - Windows - - - Debug - Name - Frontend.cpp - Windows - - - Debug - Name - Frontend.h - Windows - - - Debug - Name - Frontend_PS2.cpp - Windows - - - Debug - Name - Frontend_PS2.h - Windows - - - Debug - Name - FrontEndControls.cpp - Windows - - - Debug - Name - FrontEndControls.h - Windows - - - Debug - Name - FrontendTriggers.h - Windows - - - Debug - Name - Game.cpp - Windows - - - Debug - Name - Game.h - Windows - - - Debug - Name - General.h - Windows - - - Debug - Name - IniFile.cpp - Windows - - - Debug - Name - IniFile.h - Windows - - - Debug - Name - Lists.cpp - Windows - - - Debug - Name - Lists.h - Windows - - - Debug - Name - main.cpp - Windows - - - Debug - Name - main.h - Windows - - - Debug - Name - MenuScreens.cpp - Windows - - - Debug - Name - MenuScreensCustom.cpp - Windows - - - Debug - Name - obrstr.cpp - Windows - - - Debug - Name - obrstr.h - Windows - - - Debug - Name - Pad.cpp - Windows - - - Debug - Name - Pad.h - Windows - - - Debug - Name - Placeable.cpp - Windows - - - Debug - Name - Placeable.h - Windows - - - Debug - Name - PlayerInfo.cpp - Windows - - - Debug - Name - PlayerInfo.h - Windows - - - Debug - Name - Pools.cpp - Windows - - - Debug - Name - Pools.h - Windows - - - Debug - Name - Profile.cpp - Windows - - - Debug - Name - Profile.h - Windows - - - Debug - Name - Radar.cpp - Windows - - - Debug - Name - Radar.h - Windows - - - Debug - Name - Range2D.cpp - Windows - - - Debug - Name - Range2D.h - Windows - - - Debug - Name - Range3D.cpp - Windows - - - Debug - Name - Range3D.h - Windows - - - Debug - Name - re3.cpp - Windows - - - Debug - Name - References.cpp - Windows - - - Debug - Name - References.h - Windows - - - Debug - Name - Stats.cpp - Windows - - - Debug - Name - Stats.h - Windows - - - Debug - Name - Streaming.cpp - Windows - - - Debug - Name - Streaming.h - Windows - - - Debug - Name - SurfaceTable.cpp - Windows - - - Debug - Name - SurfaceTable.h - Windows - - - Debug - Name - templates.h - Windows - - - Debug - Name - timebars.cpp - Windows - - - Debug - Name - timebars.h - Windows - - - Debug - Name - Timer.cpp - Windows - - - Debug - Name - Timer.h - Windows - - - Debug - Name - TimeStep.cpp - Windows - - - Debug - Name - TimeStep.h - Windows - - - Debug - Name - User.cpp - Windows - - - Debug - Name - User.h - Windows - - - Debug - Name - Wanted.cpp - Windows - - - Debug - Name - Wanted.h - Windows - - - Debug - Name - World.cpp - Windows - - - Debug - Name - World.h - Windows - - - Debug - Name - ZoneCull.cpp - Windows - - - Debug - Name - ZoneCull.h - Windows - - - Debug - Name - Zones.cpp - Windows - - - Debug - Name - Zones.h - Windows - - - entities - - Debug - Name - Dummy.cpp - Windows - - - Debug - Name - Dummy.h - Windows - - - Debug - Name - Entity.cpp - Windows - - - Debug - Name - Entity.h - Windows - - - Debug - Name - Physical.cpp - Windows - - - Debug - Name - Physical.h - Windows - - - extras - - math - - Debug - Name - math.cpp - Windows - - - Debug - Name - maths.h - Windows - - - Debug - Name - Matrix.cpp - Windows - - - Debug - Name - Matrix.h - Windows - - - Debug - Name - Quaternion.cpp - Windows - - - Debug - Name - Quaternion.h - Windows - - - Debug - Name - Rect.cpp - Windows - - - Debug - Name - Rect.h - Windows - - - Debug - Name - Vector.cpp - Windows - - - Debug - Name - Vector.h - Windows - - - Debug - Name - Vector2D.h - Windows - - - Debug - Name - VuVector.h - Windows - - - modelinfo - - Debug - Name - BaseModelInfo.cpp - Windows - - - Debug - Name - BaseModelInfo.h - Windows - - - Debug - Name - ClumpModelInfo.cpp - Windows - - - Debug - Name - ClumpModelInfo.h - Windows - - - Debug - Name - MloModelInfo.cpp - Windows - - - Debug - Name - MloModelInfo.h - Windows - - - Debug - Name - ModelIndices.cpp - Windows - - - Debug - Name - ModelIndices.h - Windows - - - Debug - Name - ModelInfo.cpp - Windows - - - Debug - Name - ModelInfo.h - Windows - - - Debug - Name - PedModelInfo.cpp - Windows - - - Debug - Name - PedModelInfo.h - Windows - - - Debug - Name - SimpleModelInfo.cpp - Windows - - - Debug - Name - SimpleModelInfo.h - Windows - - - Debug - Name - TimeModelInfo.cpp - Windows - - - Debug - Name - TimeModelInfo.h - Windows - - - Debug - Name - VehicleModelInfo.cpp - Windows - - - Debug - Name - VehicleModelInfo.h - Windows - - - Debug - Name - XtraCompsModelInfo.h - Windows - - - objects - - Debug - Name - CutsceneHead.cpp - Windows - - - Debug - Name - CutsceneHead.h - Windows - - - Debug - Name - CutsceneObject.cpp - Windows - - - Debug - Name - CutsceneObject.h - Windows - - - Debug - Name - DummyObject.cpp - Windows - - - Debug - Name - DummyObject.h - Windows - - - Debug - Name - Object.cpp - Windows - - - Debug - Name - Object.h - Windows - - - Debug - Name - ObjectData.cpp - Windows - - - Debug - Name - ObjectData.h - Windows - - - Debug - Name - ParticleObject.cpp - Windows - - - Debug - Name - ParticleObject.h - Windows - - - Debug - Name - Projectile.cpp - Windows - - - Debug - Name - Projectile.h - Windows - - - peds - - Debug - Name - CivilianPed.cpp - Windows - - - Debug - Name - CivilianPed.h - Windows - - - Debug - Name - CopPed.cpp - Windows - - - Debug - Name - CopPed.h - Windows - - - Debug - Name - DummyPed.h - Windows - - - Debug - Name - EmergencyPed.cpp - Windows - - - Debug - Name - EmergencyPed.h - Windows - - - Debug - Name - Gangs.cpp - Windows - - - Debug - Name - Gangs.h - Windows - - - Debug - Name - Ped.cpp - Windows - - - Debug - Name - Ped.h - Windows - - - Debug - Name - PedAI.cpp - Windows - - - Debug - Name - PedChat.cpp - Windows - - - Debug - Name - PedDebug.cpp - Windows - - - Debug - Name - PedFight.cpp - Windows - - - Debug - Name - PedIK.cpp - Windows - - - Debug - Name - PedIK.h - Windows - - - Debug - Name - PedPlacement.cpp - Windows - - - Debug - Name - PedPlacement.h - Windows - - - Debug - Name - PedRoutes.cpp - Windows - - - Debug - Name - PedRoutes.h - Windows - - - Debug - Name - PedType.cpp - Windows - - - Debug - Name - PedType.h - Windows - - - Debug - Name - PlayerPed.cpp - Windows - - - Debug - Name - PlayerPed.h - Windows - - - Debug - Name - Population.cpp - Windows - - - Debug - Name - Population.h - Windows - - - renderer - - Debug - Name - 2dEffect.h - Windows - - - Debug - Name - Antennas.cpp - Windows - - - Debug - Name - Antennas.h - Windows - - - Debug - Name - Clouds.cpp - Windows - - - Debug - Name - Clouds.h - Windows - - - Debug - Name - Console.cpp - Windows - - - Debug - Name - Console.h - Windows - - - Debug - Name - Coronas.cpp - Windows - - - Debug - Name - Coronas.h - Windows - - - Debug - Name - Credits.cpp - Windows - - - Debug - Name - Credits.h - Windows - - - Debug - Name - Draw.cpp - Windows - - - Debug - Name - Draw.h - Windows - - - Debug - Name - Fluff.cpp - Windows - - - Debug - Name - Fluff.h - Windows - - - Debug - Name - Font.cpp - Windows - - - Debug - Name - Font.h - Windows - - - Debug - Name - Glass.cpp - Windows - - - Debug - Name - Glass.h - Windows - - - Debug - Name - Hud.cpp - Windows - - - Debug - Name - Hud.h - Windows - - - Debug - Name - Instance.cpp - Windows - - - Debug - Name - Instance.h - Windows - - - Debug - Name - Lines.cpp - Windows - - - Debug - Name - Lines.h - Windows - - - Debug - Name - MBlur.cpp - Windows - - - Debug - Name - MBlur.h - Windows - - - Debug - Name - Particle.cpp - Windows - - - Debug - Name - Particle.h - Windows - - - Debug - Name - ParticleMgr.cpp - Windows - - - Debug - Name - ParticleMgr.h - Windows - - - Debug - Name - ParticleType.h - Windows - - - Debug - Name - PlayerSkin.cpp - Windows - - - Debug - Name - PlayerSkin.h - Windows - - - Debug - Name - PointLights.cpp - Windows - - - Debug - Name - PointLights.h - Windows - - - Debug - Name - RenderBuffer.cpp - Windows - - - Debug - Name - RenderBuffer.h - Windows - - - Debug - Name - Renderer.cpp - Windows - - - Debug - Name - Renderer.h - Windows - - - Debug - Name - Rubbish.cpp - Windows - - - Debug - Name - Rubbish.h - Windows - - - Debug - Name - Shadows.cpp - Windows - - - Debug - Name - Shadows.h - Windows - - - Debug - Name - Skidmarks.cpp - Windows - - - Debug - Name - Skidmarks.h - Windows - - - Debug - Name - SpecialFX.cpp - Windows - - - Debug - Name - SpecialFX.h - Windows - - - Debug - Name - Sprite.cpp - Windows - - - Debug - Name - Sprite.h - Windows - - - Debug - Name - Sprite2d.cpp - Windows - - - Debug - Name - Sprite2d.h - Windows - - - Debug - Name - TexList.cpp - Windows - - - Debug - Name - TexList.h - Windows - - - Debug - Name - Timecycle.cpp - Windows - - - Debug - Name - Timecycle.h - Windows - - - Debug - Name - WaterCannon.cpp - Windows - - - Debug - Name - WaterCannon.h - Windows - - - Debug - Name - WaterLevel.cpp - Windows - - - Debug - Name - WaterLevel.h - Windows - - - Debug - Name - Weather.cpp - Windows - - - Debug - Name - Weather.h - Windows - - - rw - - Debug - Name - ClumpRead.cpp - Windows - - - Debug - Name - Lights.cpp - Windows - - - Debug - Name - Lights.h - Windows - - - Debug - Name - MemoryHeap.cpp - Windows - - - Debug - Name - MemoryHeap.h - Windows - - - Debug - Name - MemoryMgr.cpp - Windows - - - Debug - Name - MemoryMgr.h - Windows - - - Debug - Name - NodeName.cpp - Windows - - - Debug - Name - NodeName.h - Windows - - - Debug - Name - RwHelper.cpp - Windows - - - Debug - Name - RwHelper.h - Windows - - - Debug - Name - RwMatFX.cpp - Windows - - - Debug - Name - RwPS2AlphaTest.cpp - Windows - - - Debug - Name - TexRead.cpp - Windows - - - Debug - Name - TexturePools.cpp - Windows - - - Debug - Name - TexturePools.h - Windows - - - Debug - Name - TxdStore.cpp - Windows - - - Debug - Name - TxdStore.h - Windows - - - Debug - Name - VisibilityPlugins.cpp - Windows - - - Debug - Name - VisibilityPlugins.h - Windows - - - save - - Debug - Name - Date.cpp - Windows - - - Debug - Name - Date.h - Windows - - - Debug - Name - GenericGameStorage.cpp - Windows - - - Debug - Name - GenericGameStorage.h - Windows - - - Debug - Name - MemoryCard.cpp - Windows - - - Debug - Name - MemoryCard.h - Windows - - - Debug - Name - PCSave.cpp - Windows - - - Debug - Name - PCSave.h - Windows - - - skel - - Debug - Name - crossplatform.cpp - Windows - - - Debug - Name - crossplatform.h - Windows - - - Debug - Name - events.cpp - Windows - - - Debug - Name - events.h - Windows - - - Debug - Name - platform.h - Windows - - - Debug - Name - skeleton.cpp - Windows - - - Debug - Name - skeleton.h - Windows - - - Debug - Name - resource.h - Windows - - - Debug - Name - win.cpp - Windows - - - Debug - Name - win.h - Windows - - - Debug - Name - win.rc - Windows - - - text - - Debug - Name - Messages.cpp - Windows - - - Debug - Name - Messages.h - Windows - - - Debug - Name - Pager.cpp - Windows - - - Debug - Name - Pager.h - Windows - - - Debug - Name - Text.cpp - Windows - - - Debug - Name - Text.h - Windows - - - vehicles - - Debug - Name - Automobile.cpp - Windows - - - Debug - Name - Automobile.h - Windows - - - Debug - Name - Bike.h - Windows - - - Debug - Name - Boat.cpp - Windows - - - Debug - Name - Boat.h - Windows - - - Debug - Name - CarGen.cpp - Windows - - - Debug - Name - CarGen.h - Windows - - - Debug - Name - Cranes.cpp - Windows - - - Debug - Name - Cranes.h - Windows - - - Debug - Name - DamageManager.cpp - Windows - - - Debug - Name - DamageManager.h - Windows - - - Debug - Name - Door.cpp - Windows - - - Debug - Name - Door.h - Windows - - - Debug - Name - Floater.cpp - Windows - - - Debug - Name - Floater.h - Windows - - - Debug - Name - HandlingMgr.cpp - Windows - - - Debug - Name - HandlingMgr.h - Windows - - - Debug - Name - Heli.cpp - Windows - - - Debug - Name - Heli.h - Windows - - - Debug - Name - Plane.cpp - Windows - - - Debug - Name - Plane.h - Windows - - - Debug - Name - Train.cpp - Windows - - - Debug - Name - Train.h - Windows - - - Debug - Name - Transmission.cpp - Windows - - - Debug - Name - Transmission.h - Windows - - - Debug - Name - Vehicle.cpp - Windows - - - Debug - Name - Vehicle.h - Windows - - - weapons - - Debug - Name - BulletInfo.cpp - Windows - - - Debug - Name - BulletInfo.h - Windows - - - Debug - Name - Explosion.cpp - Windows - - - Debug - Name - Explosion.h - Windows - - - Debug - Name - ProjectileInfo.cpp - Windows - - - Debug - Name - ProjectileInfo.h - Windows - - - Debug - Name - ShotInfo.cpp - Windows - - - Debug - Name - ShotInfo.h - Windows - - - Debug - Name - Weapon.cpp - Windows - - - Debug - Name - Weapon.h - Windows - - - Debug - Name - WeaponEffects.cpp - Windows - - - Debug - Name - WeaponEffects.h - Windows - - - Debug - Name - WeaponInfo.cpp - Windows - - - Debug - Name - WeaponInfo.h - Windows - - - Debug - Name - WeaponType.h - Windows - - - RenderWare - - Debug - Name - rwcore.lib - Windows - - - Debug - Name - rpworld.lib - Windows - - - Debug - Name - rpmatfx.lib - Windows - - - Debug - Name - rpskin.lib - Windows - - - Debug - Name - rphanim.lib - Windows - - - Debug - Name - rtbmp.lib - Windows - - - Debug - Name - rtquat.lib - Windows - - - Debug - Name - rtcharse.lib - Windows - - - DirectX - - Debug - Name - d3d8.lib - Windows - - - Debug - Name - ddraw.lib - Windows - - - Debug - Name - dxguid.lib - Windows - - - Debug - Name - strmiids.lib - Windows - - - Debug - Name - dinput8.lib - Windows - - - Miles - - Debug - Name - mss32.lib - Windows - - - MSL ANSI Libraries - - Debug - Name - MSL_All_x86_D.lib - MacOS - - - Release - Name - MSL_All_x86.lib - MacOS - - - Win32 SDK Libraries - - Debug - Name - Gdi32.lib - MacOS - - - Debug - Name - Kernel32.lib - MacOS - - - Debug - Name - User32.lib - MacOS - - - Debug - Name - Comdlg32.lib - MacOS - - - Debug - Name - winmm.lib - Windows - - - Debug - Name - ole32.lib - Windows - - - Debug - Name - shell32.lib - Windows - - - Debug - Name - uuid.lib - Windows - - - - - diff --git a/conanfile.py b/conanfile.py deleted file mode 100644 index b6424eb2..00000000 --- a/conanfile.py +++ /dev/null @@ -1,135 +0,0 @@ -from conans import ConanFile, CMake, tools -from conans.errors import ConanException, ConanInvalidConfiguration -import os -import shutil -import textwrap - - -class Re3Conan(ConanFile): - name = "re3" - version = "master" - license = "???" # FIXME: https://github.com/GTAmodding/re3/issues/794 - settings = "os", "arch", "compiler", "build_type" - generators = "cmake", "cmake_find_package" - options = { - "audio": ["openal", "miles"], - "with_libsndfile": [True, False], - "with_opus": [True, False], - } - default_options = { - "audio": "openal", - "with_libsndfile": False, - "with_opus": False, - # "libsndfile:with_external_libs": False, - # "mpg123:flexible_resampling": False, - # "mpg123:network": False, - # "mpg123:icy": False, - # "mpg123:id3v2": False, - # "mpg123:ieeefloat": False, - # "mpg123:layer1": False, - # "mpg123:layer2": False, - # "mpg123:layer3": False, - # "mpg123:moreinfo": False, - # "sdl2:vulkan": False, - # "sdl2:opengl": True, - # "sdl2:sdl2main": True, - } - no_copy_source = True - - @property - def _os_is_playstation2(self): - try: - return self.settings.os == "Playstation2" - except ConanException: - return False - - def configure(self): - if self.options.audio != "openal": - self.options.with_libsndfile = False - - def requirements(self): - self.requires("librw/{}".format(self.version)) - self.requires("mpg123/1.26.4") - if self.options.audio == "openal": - self.requires("openal/1.21.0") - elif self.options.audio == "miles": - self.requires("miles-sdk/{}".format(self.version)) - if self.options.with_libsndfile: - self.requires("libsndfile/1.0.30") - if self.options.with_opus: - self.requires("opusfile/0.12") - - def export_sources(self): - for d in ("cmake", "gamefiles", "src"): - shutil.copytree(src=d, dst=os.path.join(self.export_sources_folder, d)) - self.copy("CMakeLists.txt") - - def validate(self): - if self.options["librw"].platform == "gl3" and self.options["librw"].gl3_gfxlib != "glfw": - raise ConanInvalidConfiguration("Only `glfw` is supported as gl3_gfxlib.") - #if not self.options.with_opus: - # if not self.options["libsndfile"].with_external_libs: - # raise ConanInvalidConfiguration("re3 with opus support requires a libsndfile built with external libs (=ogg/flac/opus/vorbis)") - - @property - def _re3_audio(self): - return { - "miles": "MSS", - "openal": "OAL", - }[str(self.options.audio)] - - def build(self): - if self.source_folder == self.build_folder: - raise Exception("cannot build with source_folder == build_folder") - try: - os.unlink(os.path.join(self.install_folder, "Findlibrw.cmake")) - tools.save("FindOpenAL.cmake", - textwrap.dedent( - """ - set(OPENAL_FOUND ON) - set(OPENAL_INCLUDE_DIR ${OpenAL_INCLUDE_DIRS}) - set(OPENAL_LIBRARY ${OpenAL_LIBRARIES}) - set(OPENAL_DEFINITIONS ${OpenAL_DEFINITIONS}) - """), append=True) - if self.options["librw"].platform == "gl3" and self.options["librw"].gl3_gfxlib == "glfw": - tools.save("Findglfw3.cmake", - textwrap.dedent( - """ - if(NOT TARGET glfw) - message(STATUS "Creating glfw TARGET") - add_library(glfw INTERFACE IMPORTED) - set_target_properties(glfw PROPERTIES - INTERFACE_LINK_LIBRARIES CONAN_PKG::glfw) - endif() - """), append=True) - tools.save("CMakeLists.txt", - textwrap.dedent( - """ - cmake_minimum_required(VERSION 3.0) - project(cmake_wrapper) - - include("{}/conanbuildinfo.cmake") - conan_basic_setup(TARGETS NO_OUTPUT_DIRS) - - add_subdirectory("{}" re3) - """).format(self.install_folder.replace("\\", "/"), - self.source_folder.replace("\\", "/"))) - except FileNotFoundError: - pass - cmake = CMake(self) - cmake.definitions["RE3_AUDIO"] = self._re3_audio - cmake.definitions["RE3_WITH_OPUS"] = self.options.with_opus - cmake.definitions["RE3_INSTALL"] = True - cmake.definitions["RE3_VENDORED_LIBRW"] = False - env = {} - if self._os_is_playstation2: - cmake.definitions["CMAKE_TOOLCHAIN_FILE"] = self.deps_user_info["ps2dev-cmaketoolchain"].cmake_toolchain_file - env["PS2SDK"] = self.deps_cpp_info["ps2dev-ps2sdk"].rootpath - - with tools.environment_append(env): - cmake.configure(source_folder=self.build_folder) - cmake.build() - - def package(self): - cmake = CMake(self) - cmake.install() diff --git a/dreamcast/common.mk b/dreamcast/common.mk deleted file mode 100644 index 2a0365eb..00000000 --- a/dreamcast/common.mk +++ /dev/null @@ -1,396 +0,0 @@ -GIT_VERSION := $(shell git describe --always --tags --long --dirty 2>/dev/null || echo "NO_GIT") -CI_JOB_ID ?= 00000000 - - -git-version.tmp: - @echo "Generating git-version.tmp with GIT_VERSION = \"$(GIT_VERSION)\"" - @echo "#pragma once" > git-version.tmp - @echo "#ifndef VERSION_H" >> git-version.tmp - @echo "#define VERSION_H" >> git-version.tmp - @echo "#define GIT_VERSION \"$(GIT_VERSION)\"" >> git-version.tmp - @echo "#define CI_JOB_ID \"$(CI_JOB_ID)\"" >> git-version.tmp - @echo "#endif // VERSION_H" >> git-version.tmp - -git-version.h: git-version.tmp - @if [ ! -f git-version.h ] || ! cmp -s git-version.tmp git-version.h; then \ - echo "Updating git-version.h"; \ - cp git-version.tmp git-version.h; \ - else \ - echo "git-version.h is up to date. No change."; \ - fi - -.PHONY: git-version.tmp - -../src/skel/dc/dc.cpp: git-version.h - - -# List all of your C files here, but change the extension to ".o" -# Include "romdisk.o" if you want a rom disk. -RE3_OBJS = \ - ../src/animation/AnimBlendAssocGroup.o \ - ../src/animation/AnimBlendAssociation.o \ - ../src/animation/AnimBlendClumpData.o \ - ../src/animation/AnimBlendHierarchy.o \ - ../src/animation/AnimBlendNode.o \ - ../src/animation/AnimBlendSequence.o \ - ../src/animation/AnimManager.o \ - ../src/animation/Bones.o \ - ../src/animation/CutsceneMgr.o \ - ../src/animation/FrameUpdate.o \ - ../src/animation/RpAnimBlend.o \ - \ - ../src/buildings/Building.o \ - ../src/buildings/Treadable.o \ - \ - ../src/collision/ColBox.o \ - ../src/collision/ColLine.o \ - ../src/collision/Collision.o \ - ../src/collision/ColModel.o \ - ../src/collision/ColPoint.o \ - ../src/collision/ColSphere.o \ - ../src/collision/ColTriangle.o \ - ../src/collision/TempColModels.o \ - ../src/collision/VuCollision.o \ - \ - ../src/control/AutoPilot.o \ - ../src/control/Bridge.o \ - ../src/control/CarAI.o \ - ../src/control/CarCtrl.o \ - ../src/control/Curves.o \ - ../src/control/Darkel.o \ - ../src/control/GameLogic.o \ - ../src/control/Garages.o \ - ../src/control/NameGrid.o \ - ../src/control/OnscreenTimer.o \ - ../src/control/PathFind.o \ - ../src/control/Phones.o \ - ../src/control/Pickups.o \ - ../src/control/PowerPoints.o \ - ../src/control/Record.o \ - ../src/control/Remote.o \ - ../src/control/Replay.o \ - ../src/control/Restart.o \ - ../src/control/RoadBlocks.o \ - ../src/control/SceneEdit.o \ - ../src/control/Script.o \ - ../src/control/Script2.o \ - ../src/control/Script3.o \ - ../src/control/Script4.o \ - ../src/control/Script5.o \ - ../src/control/Script6.o \ - ../src/control/ScriptDebug.o \ - ../src/control/TrafficLights.o \ - \ - ../src/core/Accident.o \ - ../src/core/Cam.o \ - ../src/core/Camera.o \ - ../src/core/CdStreamDC.o \ - ../src/core/Clock.o \ - ../src/core/ControllerConfig.o \ - ../src/core/Debug.o \ - ../src/core/Directory.o \ - ../src/core/EventList.o \ - ../src/core/FileLoader.o \ - ../src/core/FileMgr.o \ - ../src/core/Fire.o \ - ../src/core/Frontend.o \ - ../src/core/FrontEndControls.o \ - ../src/core/Frontend_PS2.o \ - ../src/core/Game.o \ - ../src/core/IniFile.o \ - ../src/core/Lists.o \ - ../src/core/main.o \ - ../src/core/MenuScreens.o \ - ../src/core/MenuScreensCustom.o \ - ../src/core/obrstr.o \ - ../src/core/Pad.o \ - ../src/core/Placeable.o \ - ../src/core/PlayerInfo.o \ - ../src/core/Pools.o \ - ../src/core/Profile.o \ - ../src/core/Radar.o \ - ../src/core/Range2D.o \ - ../src/core/Range3D.o \ - ../src/core/re3.o \ - ../src/core/References.o \ - ../src/core/Stats.o \ - ../src/core/Streaming.o \ - ../src/core/SurfaceTable.o \ - ../src/core/timebars.o \ - ../src/core/Timer.o \ - ../src/core/TimeStep.o \ - ../src/core/User.o \ - ../src/core/Wanted.o \ - ../src/core/World.o \ - ../src/core/ZoneCull.o \ - ../src/core/Zones.o \ - \ - ../src/entities/Dummy.o \ - ../src/entities/Entity.o \ - ../src/entities/Physical.o \ - \ - ../src/fakerw/fake.o \ - \ - ../src/math/math.o \ - ../src/math/Matrix.o \ - ../src/math/Quaternion.o \ - ../src/math/Rect.o \ - ../src/math/Vector.o \ - \ - ../src/modelinfo/BaseModelInfo.o \ - ../src/modelinfo/ClumpModelInfo.o \ - ../src/modelinfo/MloModelInfo.o \ - ../src/modelinfo/ModelIndices.o \ - ../src/modelinfo/ModelInfo.o \ - ../src/modelinfo/PedModelInfo.o \ - ../src/modelinfo/SimpleModelInfo.o \ - ../src/modelinfo/TimeModelInfo.o \ - ../src/modelinfo/VehicleModelInfo.o \ - \ - ../src/objects/CutsceneHead.o \ - ../src/objects/CutsceneObject.o \ - ../src/objects/DummyObject.o \ - ../src/objects/Object.o \ - ../src/objects/ObjectData.o \ - ../src/objects/ParticleObject.o \ - ../src/objects/Projectile.o \ - \ - ../src/peds/CivilianPed.o \ - ../src/peds/CopPed.o \ - ../src/peds/EmergencyPed.o \ - ../src/peds/Gangs.o \ - ../src/peds/Ped.o \ - ../src/peds/PedAI.o \ - ../src/peds/PedChat.o \ - ../src/peds/PedDebug.o \ - ../src/peds/PedFight.o \ - ../src/peds/PedIK.o \ - ../src/peds/PedPlacement.o \ - ../src/peds/PedRoutes.o \ - ../src/peds/PedType.o \ - ../src/peds/PlayerPed.o \ - ../src/peds/Population.o \ - \ - ../src/renderer/Antennas.o \ - ../src/renderer/Clouds.o \ - ../src/renderer/Console.o \ - ../src/renderer/Coronas.o \ - ../src/renderer/Credits.o \ - ../src/renderer/Draw.o \ - ../src/renderer/Fluff.o \ - ../src/renderer/Font.o \ - ../src/renderer/Glass.o \ - ../src/renderer/Hud.o \ - ../src/renderer/Instance.o \ - ../src/renderer/Lines.o \ - ../src/renderer/MBlur.o \ - ../src/renderer/Particle.o \ - ../src/renderer/ParticleMgr.o \ - ../src/renderer/PlayerSkin.o \ - ../src/renderer/PointLights.o \ - ../src/renderer/RenderBuffer.o \ - ../src/renderer/Renderer.o \ - ../src/renderer/Rubbish.o \ - ../src/renderer/Shadows.o \ - ../src/renderer/Skidmarks.o \ - ../src/renderer/SpecialFX.o \ - ../src/renderer/Sprite.o \ - ../src/renderer/Sprite2d.o \ - ../src/renderer/TexList.o \ - ../src/renderer/Timecycle.o \ - ../src/renderer/WaterCannon.o \ - ../src/renderer/WaterLevel.o \ - ../src/renderer/Weather.o \ - \ - ../src/rw/ClumpRead.o \ - ../src/rw/Lights.o \ - ../src/rw/MemoryHeap.o \ - ../src/rw/MemoryMgr.o \ - ../src/rw/NodeName.o \ - ../src/rw/RwHelper.o \ - ../src/rw/RwMatFX.o \ - ../src/rw/RwPS2AlphaTest.o \ - ../src/rw/TexRead.o \ - ../src/rw/TexturePools.o \ - ../src/rw/TxdStore.o \ - ../src/rw/VisibilityPlugins.o \ - \ - ../src/skel/crossplatform.o \ - ../src/skel/events.o \ - ../src/skel/skeleton.o \ - ../src/skel/dc/dc.o \ - \ - ../src/text/Messages.o \ - ../src/text/Pager.o \ - ../src/text/Text.o \ - \ - ../src/vehicles/Automobile.o \ - ../src/vehicles/Boat.o \ - ../src/vehicles/CarGen.o \ - ../src/vehicles/Cranes.o \ - ../src/vehicles/DamageManager.o \ - ../src/vehicles/Door.o \ - ../src/vehicles/Floater.o \ - ../src/vehicles/HandlingMgr.o \ - ../src/vehicles/Heli.o \ - ../src/vehicles/Plane.o \ - ../src/vehicles/Train.o \ - ../src/vehicles/Transmission.o \ - ../src/vehicles/Vehicle.o \ - \ - ../src/weapons/BulletInfo.o \ - ../src/weapons/Explosion.o \ - ../src/weapons/ProjectileInfo.o \ - ../src/weapons/ShotInfo.o \ - ../src/weapons/Weapon.o \ - ../src/weapons/WeaponEffects.o \ - ../src/weapons/WeaponInfo.o \ - \ - ../src/audio/AudioCollision.o \ - ../src/audio/AudioLogic.o \ - ../src/audio/AudioManager.o \ - ../src/audio/AudioScriptObject.o \ - ../src/audio/DMAudio.o \ - ../src/audio/MusicManager.o \ - ../src/audio/PolRadio.o \ - ../src/audio/sampman_miles.o \ - ../src/audio/sampman_oal.o \ - \ - ../src/save/Date.o \ - ../src/save/GenericGameStorage.o \ - ../src/save/MemoryCard.o \ - ../src/save/PCSave.o \ - \ - ../src/extras/debugmenu.o \ - ../src/extras/frontendoption.o \ - ../src/extras/postfx.o \ - ../src/extras/screendroplets.o \ - \ - ../src/vmu/vmu.o \ - ../vendor/miniLZO/minilzo.o \ - \ - -# Excluded \ - ../src/extras/custompipes.o \ - ../src/extras/custompipes_d3d9.o \ - ../src/extras/custompipes_gl.o \ - ../src/core/CdStream.o \ - ../src/core/CdStreamPosix.o \ - ../src/extras \ - ../src/extras/GitSHA1.cpp.in \ - ../src/core/AnimViewer.o \ - -RW_OBJS = \ - ../vendor/librw/src/anim.o \ - ../vendor/librw/src/base.o \ - ../vendor/librw/src/camera.o \ - ../vendor/librw/src/charset.o \ - ../vendor/librw/src/clump.o \ - ../vendor/librw/src/engine.o \ - ../vendor/librw/src/error.o \ - ../vendor/librw/src/frame.o \ - ../vendor/librw/src/geometry.o \ - ../vendor/librw/src/geoplg.o \ - ../vendor/librw/src/hanim.o \ - ../vendor/librw/src/image.o \ - ../vendor/librw/src/light.o \ - ../vendor/librw/src/matfx.o \ - ../vendor/librw/src/pipeline.o \ - ../vendor/librw/src/plg.o \ - ../vendor/librw/src/prim.o \ - ../vendor/librw/src/raster.o \ - ../vendor/librw/src/render.o \ - ../vendor/librw/src/skin.o \ - ../vendor/librw/src/texture.o \ - ../vendor/librw/src/tristrip.o \ - ../vendor/librw/src/userdata.o \ - ../vendor/librw/src/uvanim.o \ - ../vendor/librw/src/world.o \ - \ - ../vendor/librw/src/dc/rwdc.o \ - ../vendor/librw/src/dc/alloc.o - -# Excluded \ - ../vendor/librw/src/d3d-x/d3d.o \ - ../vendor/librw/src/d3d-x/d3d8.o \ - ../vendor/librw/src/d3d-x/d3d8render.o \ - ../vendor/librw/src/d3d/d3d8.o \ - ../vendor/librw/src/d3d/d3d8matfx.o \ - ../vendor/librw/src/d3d/d3d8render.o \ - ../vendor/librw/src/d3d/d3d8skin.o \ - ../vendor/librw/src/d3d/d3d9.o \ - ../vendor/librw/src/d3d/d3d9matfx.o \ - ../vendor/librw/src/d3d/d3d9render.o \ - ../vendor/librw/src/d3d/d3d9skin.o \ - ../vendor/librw/src/d3d/d3d.o \ - ../vendor/librw/src/d3d/d3ddevice.o \ - ../vendor/librw/src/d3d/d3dimmed.o \ - ../vendor/librw/src/d3d/d3drender.o \ - ../vendor/librw/src/d3d/xbox.o \ - ../vendor/librw/src/d3d/xboxmatfx.o \ - ../vendor/librw/src/d3d/xboxskin.o \ - ../vendor/librw/src/d3d/xboxvfmt.o \ - \ - ../vendor/librw/src/gl/gl3.o \ - ../vendor/librw/src/gl/gl3device.o \ - ../vendor/librw/src/gl/gl3immed.o \ - ../vendor/librw/src/gl/gl3matfx.o \ - ../vendor/librw/src/gl/gl3pipe.o \ - ../vendor/librw/src/gl/gl3raster.o \ - ../vendor/librw/src/gl/gl3render.o \ - ../vendor/librw/src/gl/gl3shader.o \ - ../vendor/librw/src/gl/gl3skin.o \ - ../vendor/librw/src/gl/wdgl.o \ - ../vendor/librw/src/gl/glad/glad.cXXX \ - \ - ../vendor/librw/src/ps2/pds.o \ - ../vendor/librw/src/ps2/ps2.o \ - ../vendor/librw/src/ps2/ps2device.o \ - ../vendor/librw/src/ps2/ps2matfx.o \ - ../vendor/librw/src/ps2/ps2raster.o \ - ../vendor/librw/src/ps2/ps2skin.o \ - -INCLUDE = \ --I../src/animation \ --I../src/audio \ --I../src/buildings \ --I../src/collision \ --I../src/control \ --I../src/core \ --I../src/entities \ --I../src/extras \ --I../src/fakerw \ --I../src/math \ --I../src/modelinfo \ --I../src/objects \ --I../src/peds \ --I../src/renderer \ --I../src/rw \ --I../src/save \ --I../src/skel \ --I../src/text \ --I../src/vehicles \ --I../src/weapons \ --I../src/audio/eax \ --I../src/audio/oal \ --I../src/extras/shaders \ --I../src/extras/shaders/obj \ --I../src/skel/glfw \ --I../src/skel/win \ -\ --I../vendor/librw \ -\ --I../vendor/miniLZO - -DEFINES = -DRW_DC -DLIBRW $(if $(WITH_LOGGING),-DWITH_LOGGING) $(if $(WITH_DCLOAD),-DDC_CHDIR=/pc) \ - $(if $(WITH_BEEPS),-DWITH_BEEPS) -FLAGS = -fpermissive -Wno-sign-compare -Wno-parentheses -Wno-maybe-uninitialized \ - -Wno-format -Wno-strict-aliasing -Wno-unused-variable \ - -Wno-unused-but-set-variable -Wno-write-strings \ - -Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion \ - -Wno-multichar -Wno-unused-value -Wno-char-subscripts -Wno-reorder \ - -Wno-unused-function -Wno-class-memaccess -fno-permissive - -CPPFLAGS += $(INCLUDE) $(DEFINES) $(FLAGS) -CFLAGS += -std=gnu17 $(CPPFLAGS) -CXXFLAGS += -std=gnu++20 $(CPPFLAGS) \ No newline at end of file diff --git a/dreamcast/gen-mesh-variants.py b/dreamcast/gen-mesh-variants.py deleted file mode 100644 index 9668b6a6..00000000 --- a/dreamcast/gen-mesh-variants.py +++ /dev/null @@ -1,114 +0,0 @@ - -def variant(small_xyz, pad_xyz, small_uv): - return f""" - &submitMesh, - &submitMesh, - &submitMesh, - &submitMesh, - &submitMesh, - &submitMesh, - &submitMesh, - &submitMesh, - 0, - 0, - 0, - 0, - &submitMesh, - &submitMesh, - &submitMesh, - &submitMesh, - &submitMesh, - &submitMesh, - &submitMesh, - &submitMesh, - &submitMesh, - &submitMesh, - &submitMesh, - &submitMesh, - 0, - 0, - 0, - 0, - &submitMesh, - &submitMesh, - &submitMesh, - &submitMesh, - - 0, - &submitMesh, - 0, - &submitMesh, - 0, - &submitMesh, - 0, - &submitMesh, - 0, - 0, - 0, - 0, - 0, - &submitMesh, - 0, - &submitMesh, - 0, - &submitMesh, - 0, - &submitMesh, - 0, - &submitMesh, - 0, - &submitMesh, - 0, - 0, - 0, - 0, - 0, - &submitMesh, - 0, - &submitMesh, - - 0, - &submitMesh, - 0, - &submitMesh, - 0, - &submitMesh, - 0, - &submitMesh, - 0, - 0, - 0, - 0, - 0, - &submitMesh, - 0, - &submitMesh, - 0, - &submitMesh, - 0, - &submitMesh, - 0, - &submitMesh, - 0, - &submitMesh, - 0, - 0, - 0, - 0, - 0, - &submitMesh, - 0, - &submitMesh, -""" - -str = "" -str += variant(False, False, False) -str += variant(False, False, True) -str += variant(False, True, False) -str += variant(False, True, True) -str += variant(True, False, False) -str += variant(True, False, True) -str += variant(True, True, False) -str += variant(True, True, True) - -print(str) \ No newline at end of file diff --git a/dreamcast/save.ico b/dreamcast/save.ico deleted file mode 100644 index 64f7c674..00000000 Binary files a/dreamcast/save.ico and /dev/null differ diff --git a/dreamcast/settings.ico b/dreamcast/settings.ico deleted file mode 100644 index 5230be86..00000000 Binary files a/dreamcast/settings.ico and /dev/null differ diff --git a/gamefiles/TEXT/JAPANESE.gxt b/gamefiles/liberty/TEXT/JAPANESE.gxt similarity index 100% rename from gamefiles/TEXT/JAPANESE.gxt rename to gamefiles/liberty/TEXT/JAPANESE.gxt diff --git a/gamefiles/TEXT/american.gxt b/gamefiles/liberty/TEXT/american.gxt similarity index 100% rename from gamefiles/TEXT/american.gxt rename to gamefiles/liberty/TEXT/american.gxt diff --git a/gamefiles/TEXT/english.gxt b/gamefiles/liberty/TEXT/english.gxt similarity index 100% rename from gamefiles/TEXT/english.gxt rename to gamefiles/liberty/TEXT/english.gxt diff --git a/gamefiles/TEXT/french.gxt b/gamefiles/liberty/TEXT/french.gxt similarity index 100% rename from gamefiles/TEXT/french.gxt rename to gamefiles/liberty/TEXT/french.gxt diff --git a/gamefiles/TEXT/german.gxt b/gamefiles/liberty/TEXT/german.gxt similarity index 100% rename from gamefiles/TEXT/german.gxt rename to gamefiles/liberty/TEXT/german.gxt diff --git a/gamefiles/TEXT/italian.gxt b/gamefiles/liberty/TEXT/italian.gxt similarity index 100% rename from gamefiles/TEXT/italian.gxt rename to gamefiles/liberty/TEXT/italian.gxt diff --git a/gamefiles/TEXT/polish.gxt b/gamefiles/liberty/TEXT/polish.gxt similarity index 100% rename from gamefiles/TEXT/polish.gxt rename to gamefiles/liberty/TEXT/polish.gxt diff --git a/gamefiles/TEXT/russian.gxt b/gamefiles/liberty/TEXT/russian.gxt similarity index 100% rename from gamefiles/TEXT/russian.gxt rename to gamefiles/liberty/TEXT/russian.gxt diff --git a/gamefiles/TEXT/spanish.gxt b/gamefiles/liberty/TEXT/spanish.gxt similarity index 100% rename from gamefiles/TEXT/spanish.gxt rename to gamefiles/liberty/TEXT/spanish.gxt diff --git a/gamefiles/data/PARTICLE.CFG b/gamefiles/liberty/data/PARTICLE.CFG similarity index 100% rename from gamefiles/data/PARTICLE.CFG rename to gamefiles/liberty/data/PARTICLE.CFG diff --git a/gamefiles/data/main_d.scm b/gamefiles/liberty/data/main_d.scm similarity index 100% rename from gamefiles/data/main_d.scm rename to gamefiles/liberty/data/main_d.scm diff --git a/gamefiles/data/main_freeroam.scm b/gamefiles/liberty/data/main_freeroam.scm similarity index 100% rename from gamefiles/data/main_freeroam.scm rename to gamefiles/liberty/data/main_freeroam.scm diff --git a/gamefiles/gamecontrollerdb.txt b/gamefiles/liberty/gamecontrollerdb.txt similarity index 100% rename from gamefiles/gamecontrollerdb.txt rename to gamefiles/liberty/gamecontrollerdb.txt diff --git a/gamefiles/models/fonts_j.txd b/gamefiles/liberty/models/fonts_j.txd similarity index 100% rename from gamefiles/models/fonts_j.txd rename to gamefiles/liberty/models/fonts_j.txd diff --git a/gamefiles/models/fonts_p.txd b/gamefiles/liberty/models/fonts_p.txd similarity index 100% rename from gamefiles/models/fonts_p.txd rename to gamefiles/liberty/models/fonts_p.txd diff --git a/gamefiles/models/fonts_r.txd b/gamefiles/liberty/models/fonts_r.txd similarity index 100% rename from gamefiles/models/fonts_r.txd rename to gamefiles/liberty/models/fonts_r.txd diff --git a/gamefiles/models/frontend_ds3.txd b/gamefiles/liberty/models/frontend_ds3.txd similarity index 100% rename from gamefiles/models/frontend_ds3.txd rename to gamefiles/liberty/models/frontend_ds3.txd diff --git a/gamefiles/models/frontend_ds4.txd b/gamefiles/liberty/models/frontend_ds4.txd similarity index 100% rename from gamefiles/models/frontend_ds4.txd rename to gamefiles/liberty/models/frontend_ds4.txd diff --git a/gamefiles/models/frontend_nsw.txd b/gamefiles/liberty/models/frontend_nsw.txd similarity index 100% rename from gamefiles/models/frontend_nsw.txd rename to gamefiles/liberty/models/frontend_nsw.txd diff --git a/gamefiles/models/frontend_x360.txd b/gamefiles/liberty/models/frontend_x360.txd similarity index 100% rename from gamefiles/models/frontend_x360.txd rename to gamefiles/liberty/models/frontend_x360.txd diff --git a/gamefiles/models/frontend_xone.txd b/gamefiles/liberty/models/frontend_xone.txd similarity index 100% rename from gamefiles/models/frontend_xone.txd rename to gamefiles/liberty/models/frontend_xone.txd diff --git a/gamefiles/models/generic.txd b/gamefiles/liberty/models/generic.txd similarity index 100% rename from gamefiles/models/generic.txd rename to gamefiles/liberty/models/generic.txd diff --git a/gamefiles/models/menu.txd b/gamefiles/liberty/models/menu.txd similarity index 100% rename from gamefiles/models/menu.txd rename to gamefiles/liberty/models/menu.txd diff --git a/gamefiles/models/nswbtns.txd b/gamefiles/liberty/models/nswbtns.txd similarity index 100% rename from gamefiles/models/nswbtns.txd rename to gamefiles/liberty/models/nswbtns.txd diff --git a/gamefiles/models/particle.txd b/gamefiles/liberty/models/particle.txd similarity index 100% rename from gamefiles/models/particle.txd rename to gamefiles/liberty/models/particle.txd diff --git a/gamefiles/models/ps3btns.txd b/gamefiles/liberty/models/ps3btns.txd similarity index 100% rename from gamefiles/models/ps3btns.txd rename to gamefiles/liberty/models/ps3btns.txd diff --git a/gamefiles/models/x360btns.txd b/gamefiles/liberty/models/x360btns.txd similarity index 100% rename from gamefiles/models/x360btns.txd rename to gamefiles/liberty/models/x360btns.txd diff --git a/gamefiles/neo/carTweakingTable.dat b/gamefiles/liberty/neo/carTweakingTable.dat similarity index 100% rename from gamefiles/neo/carTweakingTable.dat rename to gamefiles/liberty/neo/carTweakingTable.dat diff --git a/gamefiles/neo/neo.txd b/gamefiles/liberty/neo/neo.txd similarity index 100% rename from gamefiles/neo/neo.txd rename to gamefiles/liberty/neo/neo.txd diff --git a/gamefiles/neo/rimTweakingTable.dat b/gamefiles/liberty/neo/rimTweakingTable.dat similarity index 100% rename from gamefiles/neo/rimTweakingTable.dat rename to gamefiles/liberty/neo/rimTweakingTable.dat diff --git a/gamefiles/neo/worldTweakingTable.dat b/gamefiles/liberty/neo/worldTweakingTable.dat similarity index 100% rename from gamefiles/neo/worldTweakingTable.dat rename to gamefiles/liberty/neo/worldTweakingTable.dat diff --git a/gamefiles/miami/TEXT/american.gxt b/gamefiles/miami/TEXT/american.gxt new file mode 100644 index 00000000..ca03b34c Binary files /dev/null and b/gamefiles/miami/TEXT/american.gxt differ diff --git a/gamefiles/miami/TEXT/french.gxt b/gamefiles/miami/TEXT/french.gxt new file mode 100644 index 00000000..ece0d8ed Binary files /dev/null and b/gamefiles/miami/TEXT/french.gxt differ diff --git a/gamefiles/miami/TEXT/german.gxt b/gamefiles/miami/TEXT/german.gxt new file mode 100644 index 00000000..fbec75cb Binary files /dev/null and b/gamefiles/miami/TEXT/german.gxt differ diff --git a/gamefiles/miami/TEXT/italian.gxt b/gamefiles/miami/TEXT/italian.gxt new file mode 100644 index 00000000..8b6d317c Binary files /dev/null and b/gamefiles/miami/TEXT/italian.gxt differ diff --git a/gamefiles/miami/TEXT/russian.gxt b/gamefiles/miami/TEXT/russian.gxt new file mode 100644 index 00000000..375323ba Binary files /dev/null and b/gamefiles/miami/TEXT/russian.gxt differ diff --git a/gamefiles/miami/TEXT/spanish.gxt b/gamefiles/miami/TEXT/spanish.gxt new file mode 100644 index 00000000..101e97c6 Binary files /dev/null and b/gamefiles/miami/TEXT/spanish.gxt differ diff --git a/gamefiles/miami/data/freeroam_miami.scm b/gamefiles/miami/data/freeroam_miami.scm new file mode 100644 index 00000000..36f5dfd0 Binary files /dev/null and b/gamefiles/miami/data/freeroam_miami.scm differ diff --git a/gamefiles/miami/gamecontrollerdb.txt b/gamefiles/miami/gamecontrollerdb.txt new file mode 100644 index 00000000..728fddc2 --- /dev/null +++ b/gamefiles/miami/gamecontrollerdb.txt @@ -0,0 +1,943 @@ +# Game Controller DB for SDL in 2.0.9 format +# Source: https://github.com/gabomdq/SDL_GameControllerDB + +# Windows +03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows, +03000000c82d00002038000000000000,8bitdo,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d000011ab000000000000,8BitDo F30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00001038000000000000,8BitDo F30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000090000000000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00005106000000000000,8BitDo M30 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000310000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00002028000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00008010000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00000190000000000000,8BitDo N30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00001590000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00006528000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00015900000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00065280000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, +03000000022000000090000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000203800000900000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000130000000000000,8BitDo SF30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000060000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000061000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d000021ab000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, +03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00003028000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000030000000000000,8BitDo SN30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000351000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00001290000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d000020ab000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00004028000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00006228000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000260000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000261000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000031000000000000,8BitDo Wireless Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00003032000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, +03000000a00500003232000000000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, +030000008f0e00001200000000000000,Acme GA-02,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows, +03000000fa190000f0ff000000000000,Acteck AGJ-3200,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +030000006f0e00001413000000000000,Afterglow,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00000263000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00001101000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00001401000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00001402000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00001901000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00001a01000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000d62000001d57000000000000,Airflo PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000869800002400000000007801,Astro C40 TR,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000d6200000e557000000000000,Batarang,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows, +030000006f0e00003201000000000000,Battlefield 4 PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000d62000002a79000000000000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000bc2000006012000000000000,Betop 2126F,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000bc2000000055000000000000,Betop BFM Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000bc2000006312000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000bc2000006321000000000000,BETOP CONTROLLER,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000bc2000006412000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000c01100000555000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000c01100000655000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000790000000700000000000000,Betop Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows, +03000000808300000300000000000000,Betop Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows, +030000006b1400000055000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000006b1400000103000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, +0300000066f700000500000000000000,BrutalLegendTest,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, +03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows, +03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000457500000401000000000000,Cobra,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000005e040000a102000000000000,Controller (Xbox 360 Wireless Receiver for Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000005e040000ff02000000000000,Controller (Xbox One For Windows) - Wired,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000005e040000ea02000000000000,Controller (Xbox One For Windows) - Wireless,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Windows, +03000000a306000022f6000000000000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, +03000000451300000830000000000000,Defender Game Racer X7,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000007d0400000840000000000000,Destroyer Tiltpad,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,x:b0,y:b3,platform:Windows, +03000000791d00000103000000000000,Dual Box WII,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000bd12000002e0000000000000,Dual USB Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows, +030000008f0e00000910000000000000,DualShock 2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows, +030000006f0e00003001000000000000,EA SPORTS PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000b80500000410000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows, +03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows, +03000000120c0000f61c000000000000,Elite,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000008f0e00000f31000000000000,EXEQ,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, +03000000341a00000108000000000000,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000006f0e00008401000000000000,Faceoff Deluxe+ Audio Wired Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00008001000000000000,Faceoff Wired Pro Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000852100000201000000000000,FF-GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00008500000000000000,Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00008400000000000000,Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows, +030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows, +03000000790000002201000000000000,Game Controller for PC,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +0300000066f700000100000000000000,Game VIB Joystick,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows, +03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows, +03000000790000004618000000000000,GameCube Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows, +030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000280400000140000000000000,GamePad Pro USB,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +03000000ac0500003d03000000000000,GameSir,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000ac0500004d04000000000000,GameSir,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +03000000c01100000140000000000000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00000102000000007801,GameStop Xbox 360 Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000009b2800003200000000000000,GC/N64 to USB v3.4,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows, +030000009b2800006000000000000000,GC/N64 to USB v3.6,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows, +030000008305000009a0000000000000,Genius,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000008305000031b0000000000000,Genius Maxfire Blaze 3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +03000000451300000010000000000000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000005c1a00003330000000000000,Genius MaxFire Grandias 12V,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows, +03000000300f00000b01000000000000,GGE909 Recoil Pad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, +03000000f0250000c283000000000000,Gioteck,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000f025000021c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000f0250000c383000000000000,Gioteck VX2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000f0250000c483000000000000,Gioteck VX2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +030000007d0400000540000000000000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00004900000000000000,Hatsune Miku Sho Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000001008000001e1000000000000,Havit HV-G60,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows, +03000000d81400000862000000000000,HitBox Edition Cthulhu+,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, +03000000632500002605000000000000,HJD-X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +030000000d0f00002d00000000000000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00005f00000000000000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00005e00000000000000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00004000000000000000,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00005400000000000000,Hori Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00000900000000000000,Hori Pad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00004d00000000000000,Hori Pad A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00009200000000000000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00001600000000007803,HORI Real Arcade Pro EX-SE (Xbox 360),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows, +030000000d0f00009c00000000000000,Hori TAC Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f0000c100000000000000,Horipad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00006e00000000000000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00006600000000000000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00005500000000000000,Horipad 4 FPS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f0000ee00000000000000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000250900000017000000000000,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows, +030000008f0e00001330000000000000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Windows, +03000000d81d00000f00000000000000,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000d81d00001000000000000000,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000830500006020000000000000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Windows, +03000000b50700001403000000000000,Impact Black,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, +030000006f0e00002401000000000000,INJUSTICE FightStick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +03000000ac0500002c02000000000000,IPEGA,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000491900000204000000000000,Ipega PG-9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000491900000304000000000000,Ipega PG-9087 - Bluetooth Gamepad,+righty:+a5,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows, +030000006e0500000a20000000000000,JC-DUX60 ELECOM MMO Gamepad,a:b2,b:b3,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b14,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b15,righttrigger:b13,rightx:a3,righty:a4,start:b20,x:b0,y:b1,platform:Windows, +030000006e0500000520000000000000,JC-P301U,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows, +030000006e0500000320000000000000,JC-U3613M (DInput),a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows, +030000006e0500000720000000000000,JC-W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows, +030000007e0500000620000000000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows, +030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows, +030000007e0500000720000000000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, +030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, +03000000bd12000003c0000000000000,JY-P70UR,a:b1,b:b0,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b11,righttrigger:b9,rightx:a3,righty:a2,start:b4,x:b3,y:b2,platform:Windows, +03000000242f00002d00000000000000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000242f00008a00000000000000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, +03000000790000000200000000000000,King PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows, +030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006d040000d2ca000000000000,Logitech Cordless Precision,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows, +030000006d04000016c2000000000000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006d04000018c2000000000000,Logitech F510 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006d04000019c2000000000000,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006d0400001ac2000000000000,Logitech Precision Gamepad,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000006d0400000ac2000000000000,Logitech WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Windows, +03000000380700006652000000000000,Mad Catz C.T.R.L.R,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, +03000000380700005032000000000000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000380700005082000000000000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008433000000000000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008483000000000000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008134000000000000,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008184000000000000,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700006252000000000000,Mad Catz Micro C.T.R.L.R,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008532000000000000,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000380700003888000000000000,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000002a0600001024000000000000,Matricom,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows, +03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows, +03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows, +03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows, +03000000242f00007300000000000000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, +0300000079000000d218000000000000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000d620000010a7000000000000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000008f0e00001030000000000000,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,platform:Windows, +0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, +03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000790000002418000000000000,Mega Drive,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b2,start:b9,x:b3,y:b4,platform:Windows, +03000000380700006382000000000000,MLG GamePad PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000c62400002a89000000000000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c62400002b89000000000000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c62400001a89000000000000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c62400001b89000000000000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000efbe0000edfe000000000000,Monect Virtual Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows, +03000000250900006688000000000000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, +030000006b140000010c000000000000,NACON GC-400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Windows, +03000000152000000182000000000000,NGDS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows, +03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows, +030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000000d0500000308000000000000,Nostromo N45,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Windows, +03000000550900001472000000000000,NVIDIA Controller v01.04,a:b11,b:b10,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b5,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b4,righttrigger:a5,rightx:a3,righty:a6,start:b3,x:b9,y:b8,platform:Windows, +030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,rightshoulder:b5,rightstick:a2,righttrigger:b7,start:b9,x:b2,y:b3,platform:Windows, +03000000782300000a10000000000000,Onlive Wireless Controller,a:b15,b:b14,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b11,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b13,y:b12,platform:Windows, +03000000d62000006d57000000000000,OPP PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006b14000001a1000000000000,Orange Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows, +03000000362800000100000000000000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows, +03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00000901000000000000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, +030000004c050000da0c000000000000,PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, +03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000d62000009557000000000000,Pro Elite PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000d62000009f31000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000d6200000c757000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000632500002306000000000000,PS Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows, +03000000e30500009605000000000000,PS to USB convert cable,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, +03000000100800000100000000000000,PS1 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, +030000008f0e00007530000000000000,PS1 Controller,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000100800000300000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows, +03000000250900008888000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, +03000000666600006706000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Windows, +030000006b1400000303000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000009d0d00001330000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +03000000250900000500000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows, +030000004c0500006802000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b10,lefttrigger:a3~,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:a4~,rightx:a2,righty:a5,start:b8,x:b3,y:b0,platform:Windows, +03000000632500007505000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, +030000008f0e00001431000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000003807000056a8000000000000,PS3 RF pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000100000008200000000000000,PS360+ v1.66,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000004c050000e60c000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows, +03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows, +03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows, +03000000300f00001210000000000000,QanBa Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, +03000000341a00000104000000000000,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows, +03000000222c00000223000000000000,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000222c00000023000000000000,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000321500000204000000000000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000321500000104000000000000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000321500000507000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000321500000707000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000321500000011000000000000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000321500000009000000000000,Razer Serval,+lefty:+a2,-lefty:-a1,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,leftx:a0,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000000d0f00001100000000000000,REAL ARCADE PRO.3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00006a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00006b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00008a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00008b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00007000000000000000,REAL ARCADE PRO.4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00002200000000000000,REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00005b00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00005c00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000790000001100000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows, +03000000bd12000013d0000000000000,Retrolink USB SEGA Saturn Classic,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,lefttrigger:b6,rightshoulder:b2,righttrigger:b7,start:b8,x:b3,y:b4,platform:Windows, +0300000000f000000300000000000000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows, +0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows, +030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000006b140000020d000000000000,Revolution Pro Controller 2(1/2),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000006b140000130d000000000000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00001e01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00002801000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00002f01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, +03000000a306000023f6000000000000,Saitek Cyborg V.1 Game pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, +03000000300f00001201000000000000,Saitek Dual Analog Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, +03000000a30600000701000000000000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,platform:Windows, +03000000a30600000cff000000000000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b0,y:b1,platform:Windows, +03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, +03000000300f00001001000000000000,Saitek P480 Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, +03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, +03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Windows, +03000000a30600002106000000000000,Saitek PS1000,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, +03000000a306000020f6000000000000,Saitek PS2700,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, +03000000300f00001101000000000000,Saitek Rumble Pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, +03000000730700000401000000000000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows, +0300000000050000289b000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, +030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, +030000005e0400008e02000000007801,ShanWan PS3/PC Wired GamePad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows, +03000000341a00000908000000000000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000008f0e00000800000000000000,SpeedLink Strike FX,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000c01100000591000000000000,Speedlink Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000d11800000094000000000000,Stadia Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b11,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows, +03000000110100001914000000000000,SteelSeries,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000381000001214000000000000,SteelSeries Free,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows, +03000000110100003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000381000001814000000000000,SteelSeries Stratus XL,a:b0,b:b1,back:b18,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b19,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b2,y:b3,platform:Windows, +03000000790000001c18000000000000,STK-7024X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,platform:Windows, +03000000d620000011a7000000000000,Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000457500002211000000000000,SZMY-POWER PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000004f04000007d0000000000000,T Mini Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000004f0400000ab1000000000000,T.16000M,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b10,x:b2,y:b3,platform:Windows, +03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, +030000004f04000015b3000000000000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, +030000004f04000023b3000000000000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000004f0400000ed0000000000000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows, +030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, +03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, +03000000d62000006000000000000000,Tournament PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000005f140000c501000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000b80500000210000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +030000004f04000087b6000000000000,TWCS Throttle,dpdown:b8,dpleft:b9,dpright:b7,dpup:b6,leftstick:b5,lefttrigger:-a5,leftx:a0,lefty:a1,righttrigger:+a5,platform:Windows, +03000000d90400000200000000000000,TwinShock PS2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, +030000006e0500001320000000000000,U4113,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000101c0000171c000000000000,uRage Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000300f00000701000000000000,USB 4-Axis 12-Button Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, +03000000341a00002308000000000000,USB gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000005509000000b4000000000000,USB gamepad,a:b10,b:b11,back:b5,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,guide:b14,leftshoulder:b8,leftstick:b6,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b4,x:b12,y:b13,platform:Windows, +030000006b1400000203000000000000,USB gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +03000000790000000a00000000000000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows, +03000000f0250000c183000000000000,USB gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000ff1100004133000000000000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows, +03000000632500002305000000000000,USB Vibration Joystick (BM),a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000790000001a18000000000000,Venom,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00000302000000000000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00000702000000000000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +0300000034120000adbe000000000000,vJoy Device,a:b0,b:b1,back:b15,dpdown:b6,dpleft:b7,dpright:b8,dpup:b5,guide:b16,leftshoulder:b9,leftstick:b13,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b14,righttrigger:b12,rightx:+a3,righty:+a4,start:b4,x:b2,y:b3,platform:Windows, +030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000005e040000ff02000000007801,Xbox One Elite Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000005e040000130b000000000000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000341a00000608000000000000,Xeox,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +03000000450c00002043000000000000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +03000000ac0500005b05000000000000,Xiaoji Gamesir-G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000172700004431000000000000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, +03000000786901006e70000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000790000004f18000000000000,ZD-T Android,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000120c0000101e000000000000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, + +# Mac OS X +030000008f0e00000300000009010000,2In1 USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, +03000000c82d00000090000001000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00005106000000010000,8BitDo M30 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00001590000001000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +030000003512000012ab000001000000,8BitDo NES30 Gamepad,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000022000000090000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000190000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00001290000001000000,8BitDo SN30 Gamepad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000160000001000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000260000001000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000261000000010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000031000001000000,8BitDo Wireless Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001890000001000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a31,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000a00500003232000009010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, +03000000c62400001a89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X, +03000000c62400001b89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000d62000002a79000000010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X, +03000000a306000022f6000001030000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, +03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000006f0e00000102000000000000,GameStop Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +030000007d0400000540000001010000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00002d00000000100000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00005f00000000010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00005e00000000010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00006e00000000010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00006600000000010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00006600000000000000,HORIPAD FPS PLUS 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f0000ee00000000010000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000008f0e00001330000011010000,HuiJia SNES Controller,a:b4,b:b2,back:b16,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b12,rightshoulder:b14,start:b18,x:b6,y:b0,platform:Mac OS X, +03000000830500006020000000010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X, +03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X, +030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Mac OS X, +030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Mac OS X, +03000000242f00002d00000007010000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, +030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000006d04000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000006d04000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000006d04000019c2000005030000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000006d0400001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +030000006d04000018c2000000010000,Logitech RumblePad 2 USB,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X, +030000006d04000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000380700005032000000010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000380700005082000000010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000380700008433000000010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000380700008483000000010000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000242f00007300000000020000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Mac OS X, +0300000079000000d218000026010000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, +03000000d620000010a7000003010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X, +03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X, +03000000d8140000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, +030000005e0400002700000001010000,Microsoft SideWinder Plug & Play Game Pad,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,leftx:a0,lefty:a1,righttrigger:b5,x:b2,y:b3,platform:Mac OS X, +03000000d62000007162000001000000,Moga Pro 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X, +03000000c62400002a89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c62400002b89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000632500007505000000020000,NEOGEO mini PAD Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,platform:Mac OS X, +030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Mac OS X, +03000000d620000011a7000000020000,Nintendo Switch Core (Plus) Wired Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, +030000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, +03000000550900001472000025050000,NVIDIA Controller v01.04,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Mac OS X, +030000006f0e00000901000002010000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, +030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X, +030000004c050000da0c000000010000,Playstation Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X, +03000000d62000006dca000000010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, +030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, +030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +03000000321500000204000000010000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000321500000104000000010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000321500000010000000010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000321500000507000001010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000321500000011000000010000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000321500000009000000020000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X, +030000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X, +0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, +03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, +030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000006b140000130d000000010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000c6240000fefa000000000000,Rock Candy Gamepad for PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Mac OS X, +03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X, +03000000b40400000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X, +030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, +0300000000f00000f100000000000000,SNES RetroPort,a:b2,b:b3,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,rightshoulder:b7,start:b6,x:b0,y:b1,platform:Mac OS X, +030000004c050000e60c000000010000,Sony DualSense,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000d11800000094000000010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X, +030000005e0400008e02000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X, +03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X, +03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X, +03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X, +03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X, +03000000457500002211000000010000,SZMY-POWER PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X, +030000004f0400000ed0000000020000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X, +03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, +03000000bd12000015d0000000010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, +03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X, +030000006f0e00000302000025040000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, +030000006f0e00000702000003060000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000791d00000103000009010000,Wii Classic Controller,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, +050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X, +050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X, +030000005e0400008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +03000000c6240000045d000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +030000005e040000050b000003090000,Xbox Elite Wireless Controller Series 2,a:b0,b:b1,back:b31,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b53,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +030000005e040000d102000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +030000005e040000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +030000005e040000e302000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +030000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +030000005e040000e002000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X, +030000005e040000e002000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X, +030000005e040000ea02000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +030000005e040000fd02000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000120c0000100e000000010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000120c0000101e000000010000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, + +# Linux +03000000c82d00000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00001038000000010000,8Bitdo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00001590000011010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000310000011010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux, +05000000c82d00008010000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux, +03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00000060000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00000061000000010000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, +030000003512000012ab000010010000,8Bitdo SFC30 GamePad,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux, +05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00003028000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000160000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00001290000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00006228000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000260000011010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00000261000000010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000202800000900000000010000,8BitDo SNES30 Gamepad,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, +030000005e0400008e02000020010000,8BitDo Wireless Adapter (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c82d00000031000011010000,8BitDo Wireless Adapter (DInput),a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, +05000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, +030000006f0e00001302000000010000,Afterglow,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00003901000020060000,Afterglow Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00003901000000430000,Afterglow Prismatic Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00003901000013020000,Afterglow Prismatic Wired Controller 048-007-NA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000007c1800000006000010010000,Alienware Dual Compatible Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Linux, +05000000491900000204000021000000,Amazon Fire Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b17,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b12,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000790000003018000011010000,Arcade Fightstick F300,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux, +05000000050b00000045000040000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux, +03000000120c00000500000010010000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux, +03000000c62400001b89000011010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000d62000002a79000011010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000c21100000791000011010000,Be1 GC101 Controller 1.03 mode,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +03000000c31100000791000011010000,Be1 GC101 GAMEPAD 1.03 mode,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +030000005e0400008e02000003030000,Be1 GC101 Xbox 360 Controller mode,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux, +03000000ffff0000ffff000000010000,Chinese-made Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, +03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +030000000b0400003365000000010000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Linux, +03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux, +03000000a306000022f6000011010000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux, +03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux, +03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux, +030000004f04000004b3000010010000,Dual Power 2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, +030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux, +03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000008f0e00000800000010010000,Gasia Co. Ltd PS(R) Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000007d0400000540000000010000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000008f0e00000610000000010000,GreenAsia Electronics 4Axes 12Keys GamePad ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux, +030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, +0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +03000000f0250000c383000010010000,GT VX2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux, +03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +03000000632500002605000010010000,HJD-X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux, +030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f0000c100000011010000,HORI CO. LTD. HORIPAD S,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00006a00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00006b00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00008500000010010000,HORI Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00008600000002010000,Hori Fighting Commander,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000000d0f00009200000011010000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f0000aa00000011010000,HORI Real Arcade Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +030000000d0f0000d800000072056800,HORI Real Arcade Pro S,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, +030000000d0f00001600000000010000,Hori Real Arcade Pro.EX-SE (Xbox 360),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, +030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00006600000011010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f0000ee00000011010000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000008f0e00001330000010010000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Linux, +03000000242e00008816000001010000,Hyperkin X91,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux, +050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, +03000000d80400008200000003000000,IMS PCU#0 Gamepad Interface,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,platform:Linux, +03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux, +0500000049190000020400001b010000,Ipega PG-9069 - Bluetooth Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000632500007505000011010000,Ipega PG-9099 - Bluetooth Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux, +03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, +03000000300f00000b01000010010000,Jess Tech GGE909 PC Recoil Pad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, +03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, +030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux, +050000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux, +030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux, +050000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux, +03000000242f00002d00000011010000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +03000000242f00008a00000011010000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux, +030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006d0400001ec2000019200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006d0400000ac2000010010000,Logitech Inc. WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Linux, +030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux, +050000004d4f435554452d3035305800,M54-PC,a:b0,b:b1,x:b3,y:b4,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftstick:b13,rightstick:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,platform:Linux, +05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000380700005032000011010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000380700005082000011010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux, +03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000380700008433000011010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000380700008483000011010000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000120c00000500000000010000,Manta Dualshock 2,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, +03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux, +03000000790000004318000010010000,Mayflash GameCube Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux, +03000000242f00007300000011010000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux, +0300000079000000d218000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000d620000010a7000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +0300000025090000e803000001010000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux, +03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, +030000005e0400000e00000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux, +030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +050000005e040000050b000003090000,Microsoft X-Box One Elite 2 pad,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +030000005e040000e302000003020000,Microsoft X-Box One Elite pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000d102000001010000,Microsoft X-Box One pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000dd02000003020000,Microsoft X-Box One pad (Firmware 2015),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000d102000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, +030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, +030000005e040000000b000008040000,Microsoft Xbox One Elite 2 pad - Wired,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000ea02000008040000,Microsoft Xbox One S pad - Wired,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c62400001a53000000010000,Mini PE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux, +05000000d6200000e589000001000000,Moga 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, +05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, +05000000d62000007162000001000000,Moga Pro 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, +03000000c62400002b89000011010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000c62400002a89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b22,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000c62400001a89000000010000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000250900006688000000010000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, +030000006b140000010c000010010000,NACON GC-400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +030000000d0f00000900000010010000,Natec Genesis P44,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Linux, +060000007e0500000820000000000000,Nintendo Combined Joy-Cons (joycond),a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, +030000007e0500003703000000016800,Nintendo GameCube Controller,a:b0,b:b2,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b1,y:b3,platform:Linux, +03000000790000004618000010010000,Nintendo GameCube Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5~,righty:a2~,start:b9,x:b0,y:b3,platform:Linux, +050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +050000007e0500000920000001800000,Nintendo Switch Pro Controller (joycond),a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, +030000007e0500000920000011810000,Nintendo Switch Pro Controller Wired (joycond),a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, +050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux, +05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux, +03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, +03000000550900001472000011010000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux, +05000000550900001472000001000000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux, +03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +19000000010000000100000001010000,odroidgo2_joypad,a:b1,b:b0,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,guide:b10,leftshoulder:b4,leftstick:b12,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b14,start:b15,x:b2,y:b3,platform:Linux, +19000000010000000200000011000000,odroidgo2_joypad_v11,a:b1,b:b0,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b12,leftshoulder:b4,leftstick:b14,lefttrigger:b13,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b16,start:b17,x:b2,y:b3,platform:Linux, +030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, +05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, +05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, +03000000830500005020000010010000,Padix Co. Ltd. Rockfire PSX/USB Bridge,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b2,y:b3,platform:Linux, +03000000790000001c18000011010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +030000006f0e0000b802000001010000,PDP AFTERGLOW Wired Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e0000b802000013020000,PDP AFTERGLOW Wired Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00008001000011010000,PDP CO. LTD. Faceoff Wired Pro Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00003101000000010000,PDP EA Sports Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e0000c802000012010000,PDP Kingdom Hearts Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00008701000011010000,PDP Rock Candy Wired Controller for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +030000006f0e00000901000011010000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e0000a802000023020000,PDP Wired Controller for Xbox One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +030000006f0e00008501000011010000,PDP Wired Fight Pad Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +05000000491900000204000000000000,PG-9118,x:b76,a:b73,b:b74,y:b77,back:b83,start:b84,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b79,lefttrigger:b81,rightshoulder:b80,righttrigger:b82,leftstick:b86,rightstick:b87,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Linux, +0500000049190000030400001b010000,PG-9099,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +030000004c050000da0c000011010000,Playstation Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, +03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000d62000006dca000011010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000c62400001a58000001010000,PowerA Xbox One Cabled,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006d040000d2ca000011010000,Precision Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, +03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +030000006f0e00001402000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000008f0e00000300000010010000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +050000004c0500006802000000800000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +05000000504c415953544154494f4e00,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +060000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +03000000c01100000140000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +050000004c050000c405000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +030000004c050000e60c000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux, +030000009b2800003200000001010000,Raphnet Technologies GC/N64 to USB v3.4,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, +030000009b2800006000000001010000,Raphnet Technologies GC/N64 to USB v3.6,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, +030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux, +030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000008916000000fd000024010000,Razer Onza Tournament Edition,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000321500000204000011010000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000321500000104000011010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000321500000810000011010000,Razer Panthera Evo Arcade Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000321500000010000011010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000321500000507000000010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000321500000011000011010000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000008916000000fe000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c6240000045d000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, +050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, +0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000790000001100000010010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux, +0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,platform:Linux, +0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux, +030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000006b140000130d000011010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00001f01000000010000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00001e01000011010000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00004601000001010000,Rock Candy Xbox One Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux, +03000000a30600001005000000010000,Saitek P150,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b2,righttrigger:b5,x:b3,y:b4,platform:Linux, +03000000a30600000701000000010000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,platform:Linux, +03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b0,y:b1,platform:Linux, +03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux, +03000000300f00001201000010010000,Saitek P380,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, +03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux, +03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux, +03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux, +03000000a306000020f6000011010000,Saitek PS2700 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux, +03000000d81d00000e00000010010000,Savior,a:b0,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b9,x:b4,y:b5,platform:Linux, +03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,platform:Linux, +03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +03000000632500007505000010010000,SHANWAN PS3/PC Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +03000000bc2000000055000010010000,ShanWan PS3/PC Wired GamePad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000632500002305000010010000,ShanWan USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +03000000341a00000908000010010000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, +030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000d11800000094000011010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, +03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, +03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, +03000000de2800000211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux, +03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, +03000000de2800004211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux, +03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, +05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, +05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, +03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000381000003014000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000381000003114000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +0500000011010000311400001b010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b32,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000110100001914000009010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000ad1b000038f0000090040000,Street Fighter IV FightStick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000003b07000004a1000000010000,Suncom SFX Plus for USB,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Linux, +03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, +0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux, +03000000457500002211000010010000,SZMY-POWER CO. LTD. GAMEPAD,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +030000008f0e00000d31000010010000,SZMY-POWER CO. LTD. GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000008f0e00001431000010010000,SZMY-POWER CO.,LTD. PS3 gamepad,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Linux, +030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, +030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, +030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000004f0400000ed0000011010000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000b50700000399000000010000,Thrustmaster Firestorm Digital 2,a:b2,b:b4,back:b11,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b0,righttrigger:b9,start:b1,x:b3,y:b5,platform:Linux, +030000004f04000003b3000010010000,Thrustmaster Firestorm Dual Analog 2,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b9,rightx:a2,righty:a3,x:b1,y:b3,platform:Linux, +030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux, +030000004f04000026b3000002040000,Thrustmaster Gamepad GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c6240000025b000002020000,Thrustmaster GPX Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000004f04000007d0000000010000,Thrustmaster T Mini Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000004f04000012b3000010010000,Thrustmaster vibrating gamepad,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, +03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux, +03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux, +030000005e0400008e02000070050000,Torid,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c01100000591000011010000,Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, +03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, +03000000790000000600000007010000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux, +03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux, +030000006f0e00000302000011010000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00000702000011010000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, +03000000791d00000103000010010000,Wii Classic Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +050000000d0f0000f600000001000000,Wireless HORIPAD Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux, +030000005e040000a102000014010000,Xbox 360 Wireless Receiver (XBOX),a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000d102000002010000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +050000005e040000fd02000030110000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +050000005e040000050b000002090000,Xbox One Elite Series 2,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +030000005e040000ea02000000000000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +030000005e040000ea02000001030000,Xbox One Wireless Controller (Model 1708),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000001050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +030000005e0400008e02000000010000,xbox360 Wireless EasySMX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000450c00002043000010010000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +03000000ac0500005b05000010010000,Xiaoji Gamesir-G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux, +03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux, +xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000120c0000101e000011010000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000c0160000dc27000001010000,OnyxSoft Dual JoyDivision,platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b6,leftshoulder:b4,rightshoulder:b5,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0, + +# Android +05000000c82d000006500000ffff3f00,8BitDo M30 Gamepad,a:b1,b:b0,back:b4,guide:b17,leftshoulder:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a4,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000051060000ffff3f00,8BitDo M30 Gamepad,a:b1,b:b0,back:b4,guide:b17,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000015900000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000065280000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +050000000220000000900000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +050000002038000009000000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000000600000ffff3f00,8BitDo SF30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000000610000ffff3f00,8BitDo SF30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000012900000ffff3f00,8BitDo SN30 Gamepad,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000062280000ffff3f00,8BitDo SN30 Gamepad,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000001600000ffff3f00,8BitDo SN30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000002600000ffff0f00,8BitDo SN30 Pro+,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +050000002028000009000000ffff3f00,8BitDo SNES30 Gamepad,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +050000003512000020ab000000780f00,8BitDo SNES30 Gamepad,a:b21,b:b20,back:b30,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b26,rightshoulder:b27,start:b31,x:b24,y:b23,platform:Android, +05000000c82d000018900000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000030320000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +05000000bc20000000550000ffff3f00,GameSir G3w,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +05000000d6020000e5890000dfff3f00,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, +0500000031366332860c44aadfff0f00,GS Gamepad,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +0500000083050000602000000ffe0000,iBuffalo SNES Controller,a:b1,b:b0,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b15,rightshoulder:b16,start:b10,x:b3,y:b2,platform:Android, +64633436313965656664373634323364,Microsoft X-Box 360 pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, +7573622067616d657061642020202020,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Android, +050000007e05000009200000ffff0f00,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b17,y:b2,platform:Android, +37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005509000003720000cf7f3f00,NVIDIA Controller v01.01,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005509000010720000ffff3f00,NVIDIA Controller v01.03,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005509000014720000df7f3f00,NVIDIA Controller v01.04,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, +050000004c05000068020000dfff3f00,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +030000004c050000cc09000000006800,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000004c050000c4050000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, +050000004c050000c4050000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000004c050000cc090000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, +050000004c050000cc090000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, +050000004c050000e60c0000fffe3f00,PS5 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, +62653861643333663663383332396665,Razer Kishi,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000003215000005070000ffff3f00,Razer Raiju Mobile,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000003215000007070000ffff3f00,Razer Raiju Mobile,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000003215000000090000bf7f3f00,Razer Serval,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, +32633532643734376632656664383733,Sony DualSense,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, +61303162353165316365336436343139,Sony DualSense,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, +05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android, +05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android, +050000004f0400000ed00000fffe3f00,ThrustMaster eSwap PRO Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +5477696e20555342204a6f7973746963,Twin USB Joystick,a:b22,b:b21,back:b28,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android, +30306539356238653637313730656134,Wireless HORIPAD Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, +050000005e040000fd020000ff7f3f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000e00200000ffe3f00,Xbox One Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android, +050000005e040000fd020000ffff3f00,Xbox One Wireless Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000130b0000ffff3f00,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +65633038363832353634653836396239,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e04000091020000ff073f00,Xbox Wireless Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, +34356136633366613530316338376136,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,platform:Android, +050000001727000044310000ffff3f00,XiaoMi Game Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, + +# iOS +05000000ac0500000100000000006d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS, +05000000ac050000010000004f066d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS, +05000000ac05000001000000cf076d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS, +05000000ac05000001000000df076d01,*,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS, +05000000ac05000001000000ff076d01,*,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, +05000000ac0500000200000000006d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,platform:iOS, +05000000ac050000020000004f066d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,platform:iOS, +4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS, +4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS, +050000004c050000cc090000df070000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS, +050000004c050000cc090000ff070000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, +050000004c050000cc090000ff870001,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,touchpad:b11,x:b2,y:b3,platform:iOS, +05000000ac0500000300000000006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS, +05000000ac0500000300000043006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS, +05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS, +05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS, +050000005e040000050b0000ff070001,Xbox Elite Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, +050000005e040000e0020000df070000,Xbox Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS, +050000005e040000e0020000ff070000,Xbox Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, diff --git a/gamefiles/miami/models/fonts_r.txd b/gamefiles/miami/models/fonts_r.txd new file mode 100644 index 00000000..b6d50ac2 Binary files /dev/null and b/gamefiles/miami/models/fonts_r.txd differ diff --git a/gamefiles/miami/models/frontend_ds2.txd b/gamefiles/miami/models/frontend_ds2.txd new file mode 100644 index 00000000..7cf6c41f Binary files /dev/null and b/gamefiles/miami/models/frontend_ds2.txd differ diff --git a/gamefiles/miami/models/frontend_ds3.txd b/gamefiles/miami/models/frontend_ds3.txd new file mode 100644 index 00000000..06518848 Binary files /dev/null and b/gamefiles/miami/models/frontend_ds3.txd differ diff --git a/gamefiles/miami/models/frontend_ds4.txd b/gamefiles/miami/models/frontend_ds4.txd new file mode 100644 index 00000000..96193714 Binary files /dev/null and b/gamefiles/miami/models/frontend_ds4.txd differ diff --git a/gamefiles/miami/models/frontend_nsw.txd b/gamefiles/miami/models/frontend_nsw.txd new file mode 100644 index 00000000..cbd0afc7 Binary files /dev/null and b/gamefiles/miami/models/frontend_nsw.txd differ diff --git a/gamefiles/miami/models/frontend_x360.txd b/gamefiles/miami/models/frontend_x360.txd new file mode 100644 index 00000000..acb33099 Binary files /dev/null and b/gamefiles/miami/models/frontend_x360.txd differ diff --git a/gamefiles/miami/models/frontend_xone.txd b/gamefiles/miami/models/frontend_xone.txd new file mode 100644 index 00000000..b092e8e2 Binary files /dev/null and b/gamefiles/miami/models/frontend_xone.txd differ diff --git a/gamefiles/miami/models/generic.txd b/gamefiles/miami/models/generic.txd new file mode 100644 index 00000000..885cba15 Binary files /dev/null and b/gamefiles/miami/models/generic.txd differ diff --git a/gamefiles/miami/models/nswbtns.txd b/gamefiles/miami/models/nswbtns.txd new file mode 100644 index 00000000..9831ce54 Binary files /dev/null and b/gamefiles/miami/models/nswbtns.txd differ diff --git a/gamefiles/miami/models/particle.txd b/gamefiles/miami/models/particle.txd new file mode 100644 index 00000000..b0c585f4 Binary files /dev/null and b/gamefiles/miami/models/particle.txd differ diff --git a/gamefiles/miami/models/ps3btns.txd b/gamefiles/miami/models/ps3btns.txd new file mode 100644 index 00000000..6f485f14 Binary files /dev/null and b/gamefiles/miami/models/ps3btns.txd differ diff --git a/gamefiles/miami/models/x360btns.txd b/gamefiles/miami/models/x360btns.txd new file mode 100644 index 00000000..3c6ac314 Binary files /dev/null and b/gamefiles/miami/models/x360btns.txd differ diff --git a/gamefiles/miami/neo/carTweakingTable.dat b/gamefiles/miami/neo/carTweakingTable.dat new file mode 100644 index 00000000..5bdc3596 --- /dev/null +++ b/gamefiles/miami/neo/carTweakingTable.dat @@ -0,0 +1,104 @@ +# Fresnel RO Table +# SUNNY CLOUDY RAINY, FOGGY EXTRASUNNY HURRICANE EXTRACOLOURS +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # Midnight +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 1am +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 2am +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 3am +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 4am +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 5am +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 6am +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 7am +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 8am +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 9am +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 10am +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 11am +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # Midday +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 1pm +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 2pm +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 3pm +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 4pm +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 5pm +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 6pm +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 7pm +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 8pm +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 9pm +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 10pm +0.4 0.4 0.4 0.4 0.4 0.4 0.4 # 11pm +# Specular Power Table (ther higher, the tighter the highlite) +# SUNNY CLOUDY RAINY, FOGGY EXTRASUNNY HURRICANE EXTRACOLOURS +25 15 70 10 15 15 18 # Midnight +25 15 70 10 15 15 18 # 1am +25 15 70 10 15 15 18 # 2am +25 15 70 10 15 15 18 # 3am +25 15 70 10 15 15 18 # 4am +25 15 70 10 15 15 18 # 5am +25 15 70 10 15 15 18 # 6am +25 15 70 10 15 15 18 # 7am +25 15 70 10 15 15 18 # 8am +25 15 70 10 15 15 18 # 9am +25 15 70 10 15 15 18 # 10am +25 15 70 10 15 15 18 # 11am +25 15 70 10 15 15 18 # Midday +25 15 70 10 15 15 18 # 1pm +25 15 70 10 15 15 18 # 2pm +25 15 70 10 15 15 18 # 3pm +25 15 70 10 15 15 18 # 4pm +25 15 70 10 15 15 18 # 5pm +25 15 70 10 15 15 18 # 6pm +25 15 70 10 15 15 18 # 7pm +25 15 70 10 15 15 18 # 8pm +25 15 70 10 15 15 18 # 9pm +25 15 70 10 15 15 18 # 10pm +25 15 70 10 15 15 18 # 11pm +# Diffuse Colour Modifier Table (Red,Green,Blue,Amount) +# SUNNY CLOUDY RAINY, FOGGY EXTRASUNNY HURRICANE EXTRACOLOURS +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # Midnight +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 1am +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 2am +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 3am +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 4am +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 5am +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 6am +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 7am +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 8am +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 9am +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 10am +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 11am +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # Midday +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 1pm +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 2pm +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 3pm +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 4pm +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 5pm +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 6pm +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 7pm +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 8pm +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 9pm +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 10pm +0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0 # 11pm +# Specular Colour Table (Red,Green,Blue,Ignored) +# SUNNY CLOUDY RAINY, FOGGY EXTRASUNNY HURRICANE EXTRACOLOURS +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # Midnight +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 1am +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 2am +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 3am +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 4am +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 5am +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 6am +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 7am +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 8am +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 9am +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 10am +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 11am +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # Midday +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 1pm +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 2pm +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 3pm +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 4pm +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 5pm +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 6pm +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 7pm +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 8pm +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 9pm +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 10pm +178, 178, 178, 100 140, 140, 150, 50 220, 220, 220, 75 255, 255, 255, 20 178, 178, 178, 100 178, 178, 178, 75 178, 178, 178, 100 # 11pm diff --git a/gamefiles/miami/neo/neo.txd b/gamefiles/miami/neo/neo.txd new file mode 100644 index 00000000..b2fa6e66 Binary files /dev/null and b/gamefiles/miami/neo/neo.txd differ diff --git a/gamefiles/miami/neo/rimTweakingTable.dat b/gamefiles/miami/neo/rimTweakingTable.dat new file mode 100644 index 00000000..6a1e0106 --- /dev/null +++ b/gamefiles/miami/neo/rimTweakingTable.dat @@ -0,0 +1,130 @@ +# Ramp Start Table +# SUNNY CLOUDY RAINY, FOGGY EXTRASUNNY HURRICANE EXTRACOLOURS +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # Midnight +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 1am +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 2am +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 3am +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 4am +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 5am +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 6am +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 7am +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 8am +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 9am +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 10am +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 11am +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # Midday +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 1pm +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 2pm +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 3pm +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 4pm +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 5pm +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 6pm +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 7pm +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 8pm +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 9pm +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 10pm +0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 0, 0, 0, 255 # 11pm +# Ramp End Table +# SUNNY CLOUDY RAINY, FOGGY EXTRASUNNY HURRICANE EXTRACOLOURS +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # Midnight +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 1am +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 2am +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 3am +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 4am +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 5am +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 6am +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 7am +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 8am +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 9am +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 10am +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 11am +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # Midday +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 1pm +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 2pm +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 3pm +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 4pm +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 5pm +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 6pm +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 7pm +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 8pm +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 9pm +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 10pm +255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 255, 255, 255, 255 # 11pm +# Offset Table +# SUNNY CLOUDY RAINY, FOGGY EXTRASUNNY HURRICANE EXTRACOLOURS +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # Midnight +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 1am +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 2am +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 3am +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 4am +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 5am +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 6am +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 7am +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 8am +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 9am +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 10am +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 11am +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # Midday +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 1pm +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 2pm +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 3pm +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 4pm +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 5pm +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 6pm +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 7pm +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 8pm +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 9pm +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 10pm +0.5 0.5 0.5 0.5 0.5 0.5 0.5 # 11pm +# Scale Table (the lentgh of the light band?) +# SUNNY CLOUDY RAINY, FOGGY EXTRASUNNY HURRICANE EXTRACOLOURS +1 0.5 1.5 0.5 1.5 1.5 1.5 # Midnight +1 0.5 1.5 0.5 1.5 1.5 1.5 # 1am +1 0.5 1.5 0.5 1.5 1.5 1.5 # 2am +1 0.5 1.5 0.5 1.5 1.5 1.5 # 3am +1 0.5 1.5 0.5 1.5 1.5 1.5 # 4am +1 0.5 1.5 0.5 1.5 1.5 1.5 # 5am +1 0.5 1.5 0.5 1.5 1.5 1.5 # 6am +1 0.5 1.5 0.5 1.5 1.5 1.5 # 7am +1 0.5 1.5 0.5 1.5 1.5 1.5 # 8am +1 0.5 1.5 0.5 1.5 1.5 1.5 # 9am +1 0.5 1.5 0.5 1.5 1.5 1.5 # 10am +1 0.5 1.5 0.5 1.5 1.5 1.5 # 11am +1 0.5 1.5 0.5 1.5 1.5 1.5 # Midday +1 0.5 1.5 0.5 1.5 1.5 1.5 # 1pm +1 0.5 1.5 0.5 1.5 1.5 1.5 # 2pm +1 0.5 1.5 0.5 1.5 1.5 1.5 # 3pm +1 0.5 1.5 0.5 1.5 1.5 1.5 # 4pm +1 0.5 1.5 0.5 1.5 1.5 1.5 # 5pm +1 0.5 1.5 0.5 1.5 1.5 1.5 # 6pm +1 0.5 1.5 0.5 1.5 1.5 1.5 # 7pm +1 0.5 1.5 0.5 1.5 1.5 1.5 # 8pm +1 0.5 1.5 0.5 1.5 1.5 1.5 # 9pm +1 0.5 1.5 0.5 1.5 1.5 1.5 # 10pm +1 0.5 1.5 0.5 1.5 1.5 1.5 # 11pm +# Scaling Table (how strong the overall effect is) +# SUNNY CLOUDY RAINY, FOGGY EXTRASUNNY HURRICANE EXTRACOLOURS time multiplier +1 0.3 0.5 0.1 1 1 1 # Midnight 0.5 +1 0.3 0.5 0.1 1 1 1 # 1am 0.5 +1 0.3 0.5 0.1 1 1 1 # 2am 0.5 +1 0.3 0.5 0.1 1 1 1 # 3am 0.5 +1 0.3 0.5 0.1 1 1 1 # 4am 0.5 +1 0.3 0.5 0.1 1 1 1 # 5am 0.5 +1 0.3 0.5 0.1 1 1 1 # 6am 0.5 +1.2 0.36 0.6 0.12 1.2 1.2 1.2 # 7am 0.6 +1.4 0.42 0.7 0.14 1.4 1.4 1.4 # 8am 0.7 +1.6 0.48 0.8 0.16 1.6 1.6 1.6 # 9am 0.8 +1.8 0.54 0.9 0.18 1.8 1.8 1.8 # 10am 0.9 +2 0.6 1 0.2 2 2 2 # 11am 1 +2 0.6 1 0.2 2 2 2 # Midday tweaking value +2 0.6 1 0.2 2 2 2 # 1pm 1 +2 0.6 1 0.2 2 2 2 # 2pm 1 +2.2 0.66 1.1 0.22 2.2 2.2 2.2 # 3pm 1.1 +2.2 0.66 1.1 0.22 2.2 2.2 2.2 # 4pm 1.1 +2.4 0.72 1.2 0.24 2.4 2.4 2.4 # 5pm 1.2 +2.4 0.72 1.2 0.24 2.4 2.4 2.4 # 6pm 1.2 +2.2 0.66 1.1 0.22 2.2 2.2 2.2 # 7pm 1.1 +2 0.6 1 0.2 2 2 2 # 8pm 1 +1.6 0.48 0.8 0.16 1.6 1.6 1.6 # 9pm 0.8 +1.2 0.36 0.6 0.12 1.2 1.2 1.2 # 10pm 0.6 +1 0.3 0.5 0.1 1 1 1 # 11pm 0.5 diff --git a/gamefiles/miami/neo/worldTweakingTable.dat b/gamefiles/miami/neo/worldTweakingTable.dat new file mode 100644 index 00000000..8b6ac79c --- /dev/null +++ b/gamefiles/miami/neo/worldTweakingTable.dat @@ -0,0 +1,30 @@ +# LM blend Table +# SUNNY CLOUDY RAINY, FOGGY EXTRASUNNY HURRICANE EXTRACOLOURS basic daytime ramp +0.45 0.6 0.75 0.525 0.45 0.75 0.375 # Midnight 0.75 0.1 +0.48 0.64 0.8 0.56 0.48 0.8 0.4 # 1am 0.8 0.2 +0.51 0.68 0.85 0.595 0.51 0.85 0.425 # 2am 0.85 0.3 +0.54 0.72 0.9 0.63 0.54 0.9 0.45 # 3am 0.9 0.4 +0.57 0.76 0.95 0.665 0.57 0.95 0.475 # 4am 0.95 0.6 +0.6 0.8 1 0.7 0.6 1 0.5 # 5am 1 0.8 +0.6 0.8 1 0.7 0.6 1 0.5 # 6am 1 1 +0.6 0.8 1 0.7 0.6 1 0.5 # 7am 1 1 +0.6 0.8 1 0.7 0.6 1 0.5 # 8am 1 1 +0.6 0.8 1 0.7 0.6 1 0.5 # 9am 1 1 +0.6 0.8 1 0.7 0.6 1 0.5 # 10am 1 1 +0.6 0.8 1 0.7 0.6 1 0.5 # 11am 1 1 +0.6 0.8 1 0.7 0.6 1 0.5 # Midday 1 tweak here +0.6 0.8 1 0.7 0.6 1 0.5 # 1pm 1 1 +0.6 0.8 1 0.7 0.6 1 0.5 # 2pm 1 1 +0.6 0.8 1 0.7 0.6 1 0.5 # 3pm 1 1 +0.6 0.8 1 0.7 0.6 1 0.5 # 4pm 1 1 +0.6 0.8 1 0.7 0.6 1 0.5 # 5pm 1 1 +0.6 0.8 1 0.7 0.6 1 0.5 # 6pm 1 0.8 +0.57 0.76 0.95 0.665 0.57 0.95 0.475 # 7pm 0.95 0.6 +0.54 0.72 0.9 0.63 0.54 0.9 0.45 # 8pm 0.9 0.5 +0.51 0.68 0.85 0.595 0.51 0.85 0.425 # 9pm 0.85 0.4 +0.48 0.64 0.8 0.56 0.48 0.8 0.4 # 10pm 0.8 0.3 +0.45 0.6 0.75 0.525 0.45 0.75 0.375 # 11pm 0.75 0.2 + + + + diff --git a/utils/gxt/american.txt b/gxt/liberty/american.txt similarity index 100% rename from utils/gxt/american.txt rename to gxt/liberty/american.txt diff --git a/utils/gxt/build.bat b/gxt/liberty/build.bat similarity index 100% rename from utils/gxt/build.bat rename to gxt/liberty/build.bat diff --git a/utils/gxt/english.txt b/gxt/liberty/english.txt similarity index 100% rename from utils/gxt/english.txt rename to gxt/liberty/english.txt diff --git a/utils/gxt/french.txt b/gxt/liberty/french.txt similarity index 100% rename from utils/gxt/french.txt rename to gxt/liberty/french.txt diff --git a/utils/gxt/german.txt b/gxt/liberty/german.txt similarity index 100% rename from utils/gxt/german.txt rename to gxt/liberty/german.txt diff --git a/utils/gxt/gxt.exe b/gxt/liberty/gxt.exe similarity index 100% rename from utils/gxt/gxt.exe rename to gxt/liberty/gxt.exe diff --git a/utils/gxt/italian.txt b/gxt/liberty/italian.txt similarity index 100% rename from utils/gxt/italian.txt rename to gxt/liberty/italian.txt diff --git a/utils/gxt/polish.txt b/gxt/liberty/polish.txt old mode 100755 new mode 100644 similarity index 100% rename from utils/gxt/polish.txt rename to gxt/liberty/polish.txt diff --git a/utils/gxt/russian.txt b/gxt/liberty/russian.txt similarity index 100% rename from utils/gxt/russian.txt rename to gxt/liberty/russian.txt diff --git a/utils/gxt/spanish.txt b/gxt/liberty/spanish.txt similarity index 100% rename from utils/gxt/spanish.txt rename to gxt/liberty/spanish.txt diff --git a/gxt/miami/american.txt b/gxt/miami/american.txt new file mode 100644 index 00000000..94ea95c4 --- /dev/null +++ b/gxt/miami/american.txt @@ -0,0 +1,14438 @@ +[IN_VEH] +~g~Hey! Get back in the vehicle! + +[HEY] +~g~Don't go solo, keep your posse together! + +[HELP3] +You can only sprint for short periods before becoming tired. + +[HELP4_D] +Push the right analog stick up to ~h~accelerate. + +[HELP5_D] +Pull the right analog stick back to brake, or to reverse if the vehicle has stopped. + +[HELP7_A] +Press and hold the~h~ ~k~~PED_LOCK_TARGET~ button ~w~to ~h~target~w~ with the sniper rifle. + +[HELP7_D] +Press and hold the~h~ ~k~~PED_LOCK_TARGET~ button ~w~to ~h~target ~w~with the sniper rifle. + +[HELP10] +This badge indicates you have a police wanted level. + +[HELP11] +The more badges the higher your wanted level. + +[HELP13] +Sometimes you may need to use pathways not shown on the radar. + +[TIMER] +This is a timed mission, you must complete it before the timer counts down to zero. + +[HORN] +~g~Sound the horn. + +[NOMONEY] +~g~You need more cash! + +[REWARD] +REWARD $~1~ + +[M_FAIL] +MISSION FAILED! + +[M_PASS] +MISSION PASSED! $~1~ + +[DEAD] +WASTED! + +[BUSTED] +BUSTED! + +[WEATHE1] +FORCE WEATHER SUNNY + +[WEATHE2] +FORCE WEATHER EXTRA SUNNY + +[WEATHE3] +FORCE WEATHER CLOUDY + +[WEATHE4] +FORCE WEATHER RAINY + +[WEATHE5] +FORCE WEATHER FOGGY + +[WEATHE6] +WEATHER NORMAL + +[NUMBER] +~1~ + +[LOADCAR] +LOADING VEHICLE... (PRESS L1 TO CANCEL) + +[CARSOFF] +Cars turned off. + +[CARS_ON] +Cars turned on. + +[TEXTXYZ] +Writing coordinates to file... + +[CHEATON] +Cheat mode ON + +[CHEATOF] +Cheat mode OFF + +[IMPORT1] +Go outside and wait for your vehicle. + +[PAGEB11] +Flamethrower delivered to hideout. + +[WANT_A] +You will only be arrested if you have a ~h~wanted level. + +[WANT_B] +Your ~h~wanted level~w~ is represented by the row of stars in the top right of the screen. + +[WANT_C] +You now have a ~h~wanted level~w~ of one... + +[WANT_D] +two... + +[WANT_E] +three... + +[WANT_F] +As your ~h~wanted level~w~ increases you will attract more powerful forms of law enforcement. + +[WANT_G] +When you are ~h~'busted'~w~ you are returned to the nearest police station. + +[WANT_H] +The cops will take all your weapons and some of your cash as a bribe. + +[WANT_I] +Any mission you were on will be failed. + +[WANT_J] +You will find ways of reducing your wanted level the more you play. + +[WANT_K] +If you are in a car, ~h~SPRAY SHOPS~w~ will ~h~clear your wanted level. + +[HEAL_B] +When you are ~h~'wasted'~w~ you are returned to the nearest hospital. + +[HEAL_C] +You will lose your weapons and the doctors will take some cash for patching you up. + +[HEAL_E] +You will find ways of healing or protecting yourself the more you play the game. + +[SAVE1] +Walk into the corona to ~h~Save the game~w~. You cannot save during a mission. + +[SAVE2] +Any vehicle left in this garage will be stored when the game is saved. + +[AMMU] +Go inside Ammu-Nation to buy a weapon. + +[R_TIME] +RACE TIME: + +[PROP_1] +You don't have enough cash for this property + +[PROP_2] +You cannot buy property whilst on a mission + +[IND_ZON] +Vice City Beach + +[COM_ZON] +Vice City Mainland + +[BEACH1] +Ocean Beach + +[BEACH2] +Washington Beach + +[BEACH3] +Vice Point + +[GOLFC] +Leaf Links + +[STARI] +Starfish Island + +[DOCKS] +Viceport + +[HAVANA] +Little Havana + +[HAITI] +Little Haiti + +[PORNI] +Prawn Island + +[DTOWN] +Downtown + +[VICE_C] +Vice City + +[A_PORT] +Escobar International + +[JUNKY] +Junk Yard + +[PISTOL] +Pistol + +[PYTHON] +.357 + +[UZI] +Uz-1 + +[TEC9] +Tec 9 + +[M4] +M4 + +[INGRAM] +Mac + +[MP5] +MP + +[RUGER] +Kruger + +[SNIPE] +Sniper rifle + +[GRENADE] +Grenades + +[SHOTGN1] +Shotgun + +[SHOTGN2] +S.P.A.S. 12 + +[SHOTGN3] +Stubby shotgun + +[ARMOUR] +Body Armor + +[LASER] +.308 Sniper + +[BASEBAT] +Baseball bat + +[HAMMER] +Hammer + +[SCREWD] +Screwdriver + +[CLEVER] +Meat Cleaver + +[MACHETE] +Machete + +[KNIFE] +Knife + +[KATANA] +Katana + +[CHAINSA] +Chainsaw + +[G_COST] +Cost: $~1~ + +[CAR_1] +Ambulance + +[MALIBU] +The Malibu Club + +[MANSION] +Diaz's Mansion + +[TMANS] +Vercetti Estate + +[STRIP] +The 'Pole Position Club' + +[MALL1] +North Point Mall + +[BANKINT] +El Banco Corrupto Grande + +[RANGE] +Rifle Range + +[POL_HQ] +VCPD HQ + +[INT_B] +An Old Friend + +[INTB_1] +~g~Go to the Lawyer's office. + +[LAW_1] +The Party + +[LAW_2] +Back Alley Brawl + +[LAW_3] +Jury Fury + +[LAW_4] +Riot + +[COL_1] +Treacherous Swine + +[COL_2] +Mall Shootout + +[COL_3] +Guardian Angels + +[COL_4] +Sir, Yes Sir! + +[COL_5] +All Hands On Deck! + +[COK_1] +The Chase + +[COK_2] +Phnom Penh '86 + +[COK_3] +The Fastest Boat + +[COK_4] +Supply & Demand + +[KENT_1] +Death Row + +[ASS_1] +Rub Out + +[BUD_1] +Shakedown + +[BUD_2] +Bar Brawl + +[BUD_3] +Cop Land + +[CAP_1] +Cap the Collector + +[FIN_1] +Keep your Friends Close... + +[BANK_1] +No Escape? + +[BANK_2] +The Shootist + +[BANK_3] +The Driver + +[BANK_4] +The Job + +[CNT_1] +Spilling the Beans + +[CNT_2] +Hit the Courier + +[PORN_1] +Recruitment Drive + +[PORN_2] +Dildo Dodo + +[PORN_3] +Martha's Mug Shot + +[PORN_4] +G-spotlight + +[TAX_1] +Kaufman Cabs + +[TAXI_1] +V.I.P. + +[TAXI_2] +Friendly Rivalry + +[TAXI_3] +Cabmaggedon + +[ICE_1] +Distribution + +[TEX_1] +Four Iron + +[TEX_2] +Two Bit Hit + +[TEX_3] +Demolition Man + +[PHIL_1] +Gun Runner + +[PHIL_2] +Boomshine Saigon + +[BIKE_1] +Alloy Wheels of Steel + +[BIKE_2] +Messing with the Man + +[BIKE_3] +Hog Tied + +[ROCK_1] +Love Juice + +[ROCK_2] +Psycho Killer + +[ROCK_3] +Publicity Tour + +[ROCK_4] +Love Fist!! + +[HAT_1] +Juju Scramble + +[HAT_2] +Bombs Away! + +[HAT_3] +Dirty Lickin's + +[CUB_1] +Stunt Boat Challenge + +[CUB_2] +Cannon Fodder + +[CUB_3] +Naval Engagement + +[CUB_4] +Trojan Voodoo + +[JOB_1] +Road Kill + +[JOB_2] +Waste the Wife + +[JOB_3] +Autocide + +[JOB_4] +Check Out at the Check In + +[JOB_5] +Loose Ends + +[ANSWER] +Press the ~h~~k~~PED_ANSWER_PHONE~~w~ to answer your cell phone. + +[MOB_01A] +Awright me ol'china! It's Paul. I might have a little result for you, but I need to speak to you in person. + +[MOB_01B] +I'm enjoying a little R&R at the Club Malibu. + +[MOB_01C] +Reckon you're gonna owe me a favor or two after this, sunshine. I'll see you later. + +[MOB_02A] +Ssssnniiiiffffff Hey! Hello, Tommy? Tommy! + +[MOB_02B] +We got a situation over at the Print Works. You better go and check it out. + +[MOB_02C] +Some kind of mess or other. Things are messed up. I gotta go. + +[MOB_03A] +Mr. Vercetti? I have here a signed piece of crap stating + +[MOB_03B] +that you have taken on all of BJ's Auto's debts. + +[MOB_03C] +With BJ's sudden disappearance I have no choice + +[MOB_03D] +but to hold you responsible for his financial insecurities. + +[MOB_03E] +Until this account is settled in full + +[MOB_03F] +you should consider Vice City's streets to be very unfriendly. + +[MOB_04A] +How you doin' mate? It's Paulo again. + +[MOB_04B] +Look Tommy, I forgot to mention we're going to need some extra muscle for the concert. A bit of security. + +[MOB_04C] +There's a biker gang led by Mitch Baker, it would be great publicity. Very rock and roll, baby. + +[MOB_04D] +Sort this out for me and I'll get you some back stage passes for the gig, awright? + +[MOB_05A] +Hey, it's Mitch. You did good Tommy, it's good to have the old girl back. + +[MOB_05B] +You tell Kent Paul he'll get his security for the gig. + +[MOB_05C] +You have my word on that. + +[MOB_05D] +Now keep yourself out of trouble. + +[MOB_06A] +Tommy, 'nuf dead man been chattin' about you, my dear. + +[MOB_06B] +Thought you might need something to make you feel better. So Auntie Poulet make you some stew, aye? + +[MOB_06C] +Come by me kitchen some time, ok Tommy? + +[MOB_08A] +Hey Tommy, I thought you might need some business advice. + +[MOB_08B] +Once you got an operation up and running, you'll need to drop by and take the week's cash. + +[MOB_08C] +Let the guys think they got the run of the place and they'll try shaving the profits - ok? + +[MOB_08D] +Hey, I know how to handle business, Ken, ok? + +[MOB_08E] +Ok, ok. I know, you know. I know. I was, + +[MOB_08F] +I was just, you know, telling you I know, that you know, that I know. + +[MOB_08G] +Just keeping it sharp baby! + +[MOB_08H] +Whatever, Ken, whatever... + +[MOB_09A] +Hey Leo! I got some work for you! + +[MOB_09B] +This ain't Leo. + +[MOB_09C] +Hey, if Leo knows you got his phone, he gonna kill you! + +[MOB_09E] +You killed Leo? You must have big cojones - wanna work for me?! + +[MOB_09F] +Drop by my father's cafe in Little Havana and we'll talk mano a mano. + +[MOB_10A] +Tommy! Look, I gotta ask you a favor. + +[MOB_10B] +Steve! How's filming going! + +[MOB_10C] +Fine, fine. I, heh, WE need a car chase scene, but our budget can't stretch to it. + +[MOB_10D] +I've left some wheels around town. You'll know what to do. + +[MOB_10E] +Ok Steve, I'll keep an eye out. Catch you later. + +[MOB_11A] +Howdy son, just thought I'd ring you up and give you some advice. + +[MOB_11B] +Hey, Avery. What's eating you? + +[MOB_11C] +There's a lot of opportunity in this town if you own the right real estate, you catch my drift? + +[MOB_11D] +I reckon so... + +[MOB_11E] +All I'm saying is keep your eyes open and you might find the perfect business opportunity. I'll catch y'later. + +[MOB_11F] +Later, Avery. + +[MOB12_A] +Hey Tommy, it's Avery! Now listen, I got me all tied up at the moment + +[MOB12_B] +and I have a representative of mine needs chaperoning out to the Gator Keys. + +[MOB12_C] +I'm after some land out that way, so I'm sending someone out to sweeten the deal. + +[MOB12_D] +Could you do me a favor and make sure he gets there ok? + +[MOB12_E] +Yeah, sure thing Avery. Where'd you want me to pick him up? + +[MOB12_F] +He's just finishing some business at the building site. I said you'd pick him up from there. + +[MOB12_G] +No problem. See you later, Avery. + +[MOB13_A] +Vercetti? VERCETTI!! Damn you man, you've got to help me! + +[MOB13_B] +Mr. Moffat? How's family life? + +[MOB13_C] +Damn you to hell, HELL, do hear me?! + +[MOB13_D] +Well it was nice chatting... + +[MOB13_E] +WAIT! Wait, Vercetti - Tommy, can I call you Tommy? + +[MOB13_F] +We're both businessmen, yeah? You know a good deal when you hear one, ok? + +[MOB13_G] +I don't have time to chat, get to the point. + +[MOB13_H] +MONEY. Money is the goddamned point. + +[MOB13_I] +I've escaped the coop again, but it's never long before they track me down - they think it's a damned game! + +[MOB13_J] +I'm at a pay phone somewhere in this god forsaken shit hole. + +[MOB13_K] +Get me out of here before they take me back and...and..oh go-o-od... + +[MOB13_L] +Well, I'm busy for the next - + +[MOB13_M] +No! Don't shit with me here, have a heart! No man should have to do such, such things. + +[MOB13_N] +I'm on my knees here Tommy, in the dirt begging you please... + +[MOB13_O] +I guess I could swing by that way, see if I can spot you... + +[MOB13_P] +Oh god, they're coming. For the love of Christ hurry, hurry! + +[MOB_14A] +Hey there Tommy, you're gonna love me mate. + +[MOB_14B] +A little birdy told me that Vice City SWAT Division has a deposit box at a certain rather large banking establishment, + +[MOB_14C] +where they keep all the bribes they've taken over the years, + +[MOB_14D] +like some kind of old boys' retirement fund. + +[MOB_14E] +Of course, if this information should ever help you acquire any of that cash, + +[MOB_14F] +I guess you'd feel obliged to push some of it my way? + +[MOB_14G] +I'll bear that in mind, thanks Kent. + +[MOB_14H] +It's Paul. I'm from Kent, near London, you prat. + +[MOB_14I] +My provincial English geography ain't what it was. + +[MOB15_A] +Tommy, mate, it's Paul, from Kent, + +[MOB15_B] +a couple of proper sorts have your name written all over them, down at the Malibu. + +[MOB15_C] +What are you talking about? + +[MOB15_D] +Sorts. Birds. You know. Girls. Tastey ones, don't think they're brasses or nothing. + +[MOB15_E] +You gotta come check them out. + +[MOB16_A] +Tommy, Paulo here, que pasa amigo? + +[MOB16_B] +What do you want Paul. I don't want any fake label clothes. + +[MOB16_C] +Very funny, mate, but you know I don't touch bent gear. + +[MOB16_D] +Nah, I was just calling to see if I get a part in one your movies, + +[MOB16_E] +back in England I did a lot of blue stuff, mate. + +[MOB16_F] +I'm packing more heat than you, my son. + +[MOB16_G] +Paul, thanks for the offer, I'll bear it in mind. + +[MOB16_H] +Seriously, don't forget about me, after all I done for you. + +[MOB16_I] +That's what I'm trying to forget about. + +[MOB19_A] +Tommy V, It's KP here. Kent Paul. Word on the street is people want to rip you off. + +[MOB19_B] +Keep your eye's peeled, my son. And remember, I didn't say nothing to you about this. + +[MOB_20A] +Alright, Tommy, it's Paul. I just heard from a mush that you've been a real naughty boy. + +[MOB_20B] +Somebody has taken offense to you acting like the big guy all of a sudden, giving it the big shot thing. + +[MOB_20C] +Well, don't say I never warned you or nothing. Boasting is a mug's game, son. + +[MOB_20D] +Anyway, I heard there's some price been put on your head and someone's going to have a crack at you, + +[MOB_20E] +so watch yourself, and remember me, mate. + +[MOB21_A] +Tommy, Thomas, it's Cortez. Que pasa? + +[MOB21_B] +Things are interesting. How are you, my friend? + +[MOB21_G] +I wanted to ask you about Mercedes. + +[MOB21_H] +Ok, what about her? + +[MOB21_I] +Oh Tommy, Tommy. I, I hear these stories, all these stories - I don't know what to think. + +[MOB21_K] +Maybe she thinks she can do what she likes, but Tommy, tell me, is it true? + +[MOB21_M] +Is what true? + +[MOB21_N] +These stories I hear. Is she really going to be a lawyer? + +[MOB21_O] +Oh Tommy, the shame, the shame! You know, we Cortez's are a proud family. + +[MOB21_P] +We would never allow a daughter of ours to become a lawyer. Please tell me it isn't so. I don't think I could take it. + +[MOB21_Q] +Oh Colonel, I can assure you Mercedes is never going to become a lawyer. Don't worry about it. + +[MOB21_R] +Oh thank you, Tommy. Tommy, thank you. The shame would be unbearable. She is a lady, not a parasite, you know. + +[MOB21_S] +I know, colonel. + +[MOB21_T] +Anyway, Tommy, you must excuse me, the new minister of the interior has arrived. + +[MOB21_U] +Many years ago, I killed his father in a failed coup so I must be polite. Good day, amigo. + +[MOB21_C] +Tommy, it is always a struggle here. Excuse the poor line, we have just had another failed coup. + +[MOB21_D] +The people are the most demanding mistress of all. + +[MOB21_E] +So far, we have had three revolutions and four coups since I return from Vice City. + +[MOB21_F] +Luckily, I have been promoted each time. + +[MOB21_J] +Maybe everyone is humiliating me. + +[MOB21_L] +but tell me Tommy, is it true? + +[MOB22_A] +Tommy, you are proving very useful, my friend. + +[MOB22_B] +Thanks, Cortez. What about my deal? + +[MOB22_C] +Tommy, I am working tirelessly on your behalf to ensue we get to the bottom of this trench of stinking lies and deceit, + +[MOB22_D] +you have my word on that, but in the meantime, + +[MOB22_E] +please accept the esteemed thanks of my people for your work on our behalf. + +[MOB_25A] +Tommy, Thomas it's Cortez. Look, the French are giving me all kinds of trouble, amigo. + +[MOB_25B] +Damn hypocrites. They spend a hundred years stealing from poor countries and they call me a thief! + +[MOB_25C] +I am going to need your help as soon as possible, amigo. + +[MOB_25D] +So please hurry, Tommy, I need you, all right? I hate the damn French. + +[MOB_26A] +Hello, Tommy? + +[MOB_26B] +Yeah? + +[MOB_26C] +It's Baker. I just wanted to say I really enjoyed the show. + +[MOB_26D] +Me and the boys want to thank you, and remind you, + +[MOB_26E] +you got our respect. Good day. Keep riding hard, son. + +[MOB_29A] +Hello, is this Mr. Tommy Vercetti? + +[MOB_29B] +Yes. + +[MOB_29C] +Well, I hear through the vine of grapes you the man when someone got a vermin infestation. + +[MOB_29D] +Maybe... + +[MOB_29E] +Well, I got a real vermin infestation. Haitians everywhere. + +[MOB_29F] +My name is Umberto Robina and I want you to meet me at the Cafe Robina as soon as you can, + +[MOB_29G] +'cause I tell you, these damn Haitians gone too far this time. + +[MOB_29H] +Test + +[MOB_30A] +Tommy, is Umberto Robina + +[MOB_30B] +Hey, how's the cafe? + +[MOB_30C] +Oh, wonderful. Incredible. Tommy, incredible. No wimps, Tommy, just real men, and the beautiful women! + +[MOB_30D] +Anyway, I wanted to tell you, me and Papi, to us, you Cuban. + +[MOB_30E] +You have proved yourself, man. You got big cojones. + +[MOB_30F] +Well thank you, Umberto. Nobody's said that to me since I left jail. I'll see you around. + +[MOB_33A] +Tommy, it's Phil, now cut out all the reminiscing crap and listen to me, you hear? + +[MOB_33B] +Good. I got me some extra strength boomshine nearing fermentation time and I was wondering if you'd fancy having a shot. + +[MOB_33C] +Seriously, Tommy, if you like a drink, or if you need to strip paint, this stuff'll make a man out of you. + +[MOB_33D] +Sure did out of me, even though I can't see out of one eye. I'll be waiting for you, y'hear. + +[MOB_34A] +Tommy, I really enjoyed working with you. Ain't had so much fun since the ridge in Nam, pal. + +[MOB_34B] +Anyhows, you need anything, you call on me, you hear? + +[MOB_34C] +I always remember those I served with, + +[MOB_34D] +and I am sure I can help you out, you hear? + +[MOB_35A] +Tommy, the wound is healing well. Funny thing is, + +[MOB_35B] +I have fought in 6 battle zones and always walked away without a scratch, and now this! + +[MOB_35C] +One armed Phil. Still, I got me a healthy selection of one handed fire power so I'll never be unarmed Phil, you hear. + +[MOB_35D] +Any way son, cut out the sentimental crap and go buy yourself a drink, you hear! + +[MOB_36A] +Tommy, it's Phil, I want to thank you for helping me out back there son, + +[MOB_36B] +Damn Charlie, he'll always ambush you somewhere or other, + +[MOB_36C] +Anyway the wound is healing well, and it means I'll no longer be defrauding the government on my disability check. + +[MOB_40A] +Hey Tommy, it's Sonny. How's the sun tan? + +[MOB_40B] +I ain't got no sun tan. + +[MOB_40C] +Well, you ain't got my money, either, so I'm wondering to myself, + +[MOB_40D] +what are you doing? So, tell me, Tommy, what are you doing? + +[MOB_40E] +I'm looking for the money, Sonny. Don't worry. + +[MOB_40F] +I am worrying, Tommy, that's my style, + +[MOB_40G] +because I seem to have this problem in my life with unreliable people. + +[MOB_40H] +Don't be an unreliable person, Tommy, please. + +[MOB_40I] +Do us both a favor. I'm looking forward to hearing from you. + +[MOB_41A] +Tommy, remember me? + +[MOB_41B] +Hello Sonny. + +[MOB_41C] +That's right, Sonny. We're old friends, + +[MOB_41D] +You never write me, you never call. Don't you want to be friends no more? + +[MOB_41E] +I've been busy trying to sort things out. You didn't give me a lot of support down here, Sonny. + +[MOB_41F] +Oh, my fault is it? We'll I've heard you been busy all right. + +[MOB_41G] +Busy killing drugs barons. Busy taking over. + +[MOB_41H] +Don't forget about us, Tommy, 'cause I can assure you, I ain't forgotten about you. + +[MOB_42A] +Tommy. + +[MOB_42B] +Sonny. + +[MOB_42C] +Obviously you are suffering from hearing problems, so I'll try again. + +[MOB_42D] +Where's the goddamned money, where's the goddamned stuff, and where's my cut of your new action? + +[MOB_42E] +You are making an idiot out of me, Tommy, and I'm not laughing yet. + +[MOB_43A] +Tommy, Tommy, Tommy, I had Sonny on the phone, ok, are you with me?. + +[MOB_43B] +I don't know about you, but there's something about a man threatening to murder my family + +[MOB_43C] +which really scares the crap out of me. What are you going to do? + +[MOB_43D] +Ken, take it easy. + +[MOB_43E] +I AM calm, calm as a man can be when he's fearing for his life! + +[MOB_43F] +Stay off the idiot fuel and look after yourself. + +[MOB_43G] +No one's gonna take us out. I'll see you later. + +[MOB_43H] +I am calm. Don't I sound calm? Must be impending death that is doing this to my voice. + +[MOB45_A] +Tommy We gotta talk about stuff. + +[MOB45_B] +What's the problem Lance? + +[MOB45_C] +It's you, my friend, I feel you're not giving me a fair slice. + +[MOB45_D] +And more than that, you been embarrassing me in front of the boys. I can't have that. + +[MOB45_E] +Lance, it ain't like that. You've been making mistakes. + +[MOB45_F] +Tommy, I'm not your message boy. I'm not your running boy. + +[MOB45_G] +Lance, don't screw up, and we won't have any problems. I screw up, you can lay into me any time. + +[MOB45_H] +Tommy, I've done everything for you, you treat me like a fool. Don't do that. + +[MOB45_I] +Lance, I won't rip you off or stab you in the back, okay? + +[MOB45_J] +Just take it easy. This is tough enough without you getting all emotional on me. + +[MOB45_K] +Trust me. Do you hear me, do you hear me? + +[MOB45_L] +I hear you, Tommy, but I can't take this much more. + +[MOB45_M] +Lance, don't be like this. Now I'm warning you. + +[MOB45_N] +Do you hear me? Just relax, take a few days off. Okay? I'll talk to you. + +[MOB46_A] +Yo, Tommy! It's Lance. + +[MOB46_B] +Yeah? + +[MOB46_C] +Oh, nice to hear from you, Lance. Come on, man, be cool, be cool. + +[MOB46_D] +I'm in the middle of something. What do you want? + +[MOB46_E] +Nothing. Just to say, you know. Look Tommy, we can do this thing. + +[MOB46_F] +You and me, no problem. You know what I mean? + +[MOB46_G] +We're going to have to do it, 'cause otherwise, we're going to be dead, Lance. + +[MOB46_H] +We're in too far now. But thanks for the call. I'll speak to you later. + +[MOB_47A] +Tommy, Lance, we got big problems. Come down here. Right away. + +[MOB52_A] +Hey Leo, I think we got a buyer for Diaz's merchandise. + +[MOB52_B] +You gotta give him a ring, man, set up the deal, you know? + +[MOB52_C] +Where are you now? + +[MOB52_D] +You ok Leo? You sound kinda different. + +[MOB52_E] +Just tell me where you are. + +[MOB52_F] +Who the hell is this? Put Leo on, man! + +[MOB52_G] +Leo's gone away for a while, he left me in charge. + +[MOB52_H] +Screw you, man! + +[MOB54_A] +Hiya Tommy! + +[MOB54_B] +Hi Mercedes, howyadoin'? + +[MOB54_C] +I got a new apartment up in Vice Point + +[MOB54_D] +- thought you might want to drop by sometime. + +[MOB54_E] +I'd love to. I'll catch you later. + +[MOB55_A] +Tommy, it's me. + +[MOB55_B] +Hi Mercedes. + +[MOB55_C] +Tommy, I so bored, when we going to have some fun? + +[MOB55_D] +What do you mean? + +[MOB55_E] +Well, I know you're busy fighting and killing and corrupting people, + +[MOB55_F] +but I just want to have some fun. So don't forget about me, you hear? + +[MOB56_A] +Tommy, I hear you kill Ricardo Diaz. + +[MOB56_B] +there was an unfortunate fire at his mansion. + +[MOB56_C] +I think he burnt to death in an acrylic shirt. + +[MOB56_D] +Tommy, I so proud of you. I knew you were a real man. + +[MOB56_E] +He awful trouser stain of a man, you make me so proud to be your friend. + +[MOB56_F] +No, I know you going to be busy trying to take over this town, + +[MOB56_G] +but don't forget about me, you hear? + +[MOB57_A] +It's merceedes. I no longer love you Tommy. + +[MOB57_B] +I no longer do. Honest. 'cause you no longer nice to Mercedes. + +[MOB57_C] +You no longer treat her like a lady. You ignore me and I hate you. + +[MOB57_D] +I insist you come to see me right away! + +[MOB58_A] +Tommy. + +[MOB58_B] +Hey Mercedes. + +[MOB58_C] +Hey indeed Mr. Tough Guy. I real angry with you Tommy. + +[MOB58_D] +Never make me hang out with Jezz Torrent again. + +[MOB58_E] +He is pathetic. Half way through he starts crying about his doggie + +[MOB58_F] +that died when he was 7 years old and that his mommy never loved him. + +[MOB58_G] +And Tommy. He wear a wig and a bra in private. + +[MOB58_H] +I not very happy with you! + +[MOB59_A] +Ooh Tommy, its Mercedes. + +[MOB59_B] +I just want to say, I have so much fun on that film set. + +[MOB59_C] +Anything else you have like that, you let me know. + +[MOB59_D] +I really mean that. I always wanted to be an actress. + +[MOB59_E] +I think I learn a lot about the dramatic process. + +[MOB59_F] +It so enlightening! Thank you. Thank you. I see you real soon. Adios. + +[MOB_99] +Get to the payphone at location. + +[MOB_98] +Get to the payphone at location. + +[MOB_97] +Get to the payphone at location. + +[MOB_96] +Get to the payphone at location. + +[MOB_95] +Get to the payphone at location. + +[A_TIME] ++~1~ seconds + +[DODO_FT] +You flew for ~1~ seconds! + +[GA_8] +Use the detonator to activate the bomb. + +[GA_10] +Nice one. Here's your $~1~ + +[GA_11] +We got these wheels already. It's worthless to us! + +[GA_12] +Bomb armed + +[GA_13] +Delivered like a pro. Complete the list and there'll be a bonus for you. + +[GA_14] +All the cars. NICE! Here's a little something. + +[GA_15] +Hope you like the new color. + +[GA_16] +Respray is complementary. + +[GA_19] +We're not interested in that model. + +[GA_20] +We got more of these than we can shift. Sorry man, no deal. + +[CHASE] +Highest media attention + +[CHASE1] +Ignored + +[CHASE2] +Boring + +[CHASE3] +Vaguely interesting + +[CHASE4] +Local paper Page 7 + +[CHASE5] +Front page of local paper + +[CHASE6] +Vice Courier Page 2 + +[CHASE7] +Vice Courier Front page + +[CHASE8] +Local TV 3am + +[CHASE9] +Local TV news + +[CHASE10] +Local TV Live coverage + +[CHASE11] +UFA Today page 12 + +[CHASE12] +UFA Today page 4 + +[CHASE13] +Picture in UFA Today + +[CHASE14] +National TV 4am + +[CHASE15] +National TV news + +[CHASE16] +National TV live coverage + +[CHASE17] +International news + +[CHASE18] +National crisis + +[CHASE19] +International crisis + +[CHASE20] +World event + +[CHASE21] +Stuff of legends + +[CR_1] +Crane cannot lift this vehicle. + +[PU_MONY] +You don't have enough cash. + +[CO_ALL] +You got all of them. Here's a little something... + +[FEM_ON] +ON + +[FEM_OFF] +OFF + +[FEM_YES] +Yes + +[FEM_NO] +No + +[FEC_NA] +NA + +[FEC_CWL] +Cycle Weapon left + +[FEC_CWR] +Cycle Weapon right + +[FEC_LOF] +Look forward + +[FEC_TAR] +Target + +[FEC_MOV] +Movement + +[FEC_CAM] +Camera modes + +[FEC_PAU] +Pause + +[FEC_ENV] +Enter vehicle + +[FEC_JUM] +Jump + +[FEC_ATT] +Attack or Fire weapon + +[FEC_RUN] +Run + +[FEC_FPC] +First person camera + +[FEC_LL] +Look left + +[FEC_LB] +Look behind + +[FEC_LR] +Look right + +[FEC_HOR] +Horn + +[FEC_VES] +Vehicle control + +[FEC_BRA] +Brake or Reverse + +[FEC_HAB] +Hand brake + +[FEC_CAW] +Car weapon + +[FEC_ACC] +Accelerate + +[FEC_CCF] +Configuration + +[FEC_CF1] +Setup 1 + +[FEC_CF2] +Setup 2 + +[FEC_CF3] +Setup 3 + +[FEC_CF4] +Setup 4 + +[FEC_CDP] +Controller Display + +[FEC_ONF] +On foot + +[FEC_INC] +In car + +[FEC_VIB] +Vibration + +[FEL_ENG] +English + +[FEL_FRE] +French + +[FEL_GER] +German + +[FEL_ITA] +Italian + +[FEL_SPA] +Spanish + +[FED_DBG] +Menu Debug + +[FED_RID] +Reload IDE + +[FED_RIP] +Reload IPL + +[FED_PAH] +Parse Heap + +[FED_DFL] +CTheScripts::DbgFlag + +[FED_DLS] +Big White Debug Light Switched + +[FED_SPR] +Show Ped Road Groups + +[FED_SCR] +Show Car Road Grups + +[FED_SCZ] +Show Cull Zones + +[FED_DSR] +Debug Streaming Requests + +[FED_SCP] +gbShowCollisionPolys + +[PL_STAT] +Player stats + +[PE_WAST] +People you've wasted + +[PE_WSOT] +People wasted by others + +[TM_BUST] +Times busted + +[GNG_WST] +Gang members wasted + +[DED_CRI] +Criminals wasted + +[PER_COM] +Percentage completed + +[KGS_EXP] +Kgs of explosives used + +[ACCURA] +Accuracy + +[ST_WEAP] +Weapon Budget + +[ST_PROP] +Property Budget + +[ST_AUTO] +Auto Repair and Painting Budget + +[ST_PHOT] +Photographs Taken + +[ST_LOAN] +Visits From Loan Sharks + +[ST_STOR] +Stores Knocked Off + +[ST_MOVI] +Movie Stunts + +[ST_PIZZ] +Pizza's Delivered + +[ST_GARB] +Garbage Pickups Made + +[TOP_SHO] +Top Shooting Range Score + +[SHO_RAN] +Shooting Range Rank + +[SEAGULL] +Seagulls Sniped + +[PROPOWN] +Property Owned + +[ST_TIME] +Playing Time + +[ST_FTIM] +Flight hours + +[ST_PRAN] +Pilot Ranking + +[ST_RAN0] +Learner + +[ST_RAN1] +Navigator + +[ST_RAN2] +Co Pilot + +[ST_RAN3] +Junior + +[ST_RAN4] +Competent + +[ST_RAN5] +Senior + +[ST_RAN6] +Ace + +[ST_RAN7] +Red baron + +[ST_DRWN] +Fishes Fed + +[ST_FASH] +Fashion Budget + +[ST_DAMA] +Property Destroyed + +[TM_DED] +Hospital visits + +[DAYSPS] +Days passed in game + +[NUMSHV] +Safehouse visits + +[MXCARD] +Max. INSANE Jump dist. (ft) + +[MXCARJ] +Max. INSANE Jump height (ft) + +[MXCARDM] +Max. INSANE Jump dist. (m) + +[MXCARJM] +Max. INSANE Jump height (m) + +[MXFLIP] +Max. INSANE Jump flips + +[MXJUMP] +Max. INSANE Jump rotation + +[BUL_FIR] +Bullets fired + +[BUL_HIT] +Bullets that hit + +[SPRAYIN] +Sprayings + +[BSTSTU] +Best INSANE stunt so far + +[INSTUN] +Insane stunt + +[PRINST] +Perfect insane stunt + +[DBINST] +Double insane stunt + +[DBPINS] +Perfect double insane stunt + +[TRINST] +Triple insane stunt + +[PRTRST] +Perfect triple insane stunt + +[QUINST] +Quadruple insane stunt + +[PQUINS] +Perfect quadruple insane stunt + +[NOSTUC] +No INSANE stunts completed + +[NOUNIF] +Unique Jumps completed + +[NMISON] +Mission attempts + +[PASDRO] +Passengers dropped off + +[MONTAX] +Cash made in taxi + +[DAYPLC] +Daily police spending + +[CRIMRA] +Criminal rating: + +[STPR_1] +The Malibu + +[STPR_2] +Print Works + +[STPR_3] +Film Studio + +[STPR_4] +Ice Cream Factory + +[STPR_5] +Car Showroom + +[STPR_6] +Taxi Company + +[STPR_7] +Boatyard + +[SET1EN] +SetUp 1. Enabled + +[GMSAVE] +Save Game + +[FEDS_TB] +Back + +[FEST_OO] +out of + +[FEC_TUC] +Turret control + +[FEC_RS3] +Radio station cycle (L3 button) + +[FEC_HO3] +Horn (L3 button) + +[C_FAIL] +Vigilante mission ended! + +[C_ESCP] +~r~The suspect has escaped! + +[C_VIGIL] +VIGILANTE BONUS!! + +[HEAL_A] +Your ~h~health~w~ is displayed in orange in the top right of the screen. + +[WRONGCD] +Incorrect disc. Please insert correct disc. + +[NOCD] +The disc tray is empty. Please insert disc. + +[OPENCD] +The disc tray is open. Please close the disc tray. + +[CDERROR] +Error reading the Grand Theft Auto: Vice City DVD + +[RESTART] +Starting new game + +[GA_3] +No more freebies. $100 to respray! + +[GA_1] +Whoa! I don't touch nothing THAT hot! + +[GA_1A] +Come back when you're not so busy... + +[HELP9_C] +Press the~h~ ~k~~PED_FIREWEAPON~ ~w~button to ~h~fire~w~ the sniper rifle. + +[TAXI2] +~r~You're out of time! + +[PAGEB13] +Health delivered to hideout + +[PAGEB14] +Adrenaline delivered to hideout + +[FESZ_CA] +Cancel + +[FES_NGA] +New Game + +[FES_CAN] +Cancel + +[FESZ_QL] +All unsaved progress in your current game will be lost. Proceed with loading? + +[FESZ_QD] +Proceed with deleting this save game? + +[FESZ_QO] +Proceed with overwriting this save game? + +[T4X4_1] +'PCJ Playground' + +[BMX_1] +'Trial by Dirt' + +[BMX_2] +'Test Track' + +[BMXFAIL] +~r~You failed to set a new record! + +[BMX_REC] +~g~New Record Set:~1~ !! + +[T4X4_3] +'GRIPPED!' + +[MM_1] +'CONE CRAZY' + +[T4X4_F] +~r~You bailed! Too tough for you?! + +[LANDSTK] +Landstalker + +[IDAHO] +Idaho + +[STINGER] +Stinger + +[LINERUN] +Linerunner + +[PEREN] +Perennial + +[SENTINL] +Sentinel + +[RIO] +Rio + +[PATRIOT] +Patriot + +[FIRETRK] +Firetruck + +[TRASHM] +Trashmaster + +[STRETCH] +Stretch + +[MANANA] +Manana + +[INFERNS] +Infernus + +[VOODOO] +Voodoo + +[PONY] +Pony + +[MULE] +Mule + +[CHEETAH] +Cheetah + +[AMBULAN] +Ambulance + +[FBICAR] +FBI Washington + +[MOONBM] +Moonbeam + +[ESPERAN] +Esperanto + +[TAXI] +Taxi + +[WASHING] +Washington + +[BOBCAT] +Bobcat + +[WHOOPEE] +Mr. Whoopee + +[BFINJC] +BF Injection + +[HUNTER] +Hunter + +[POLICAR] +Police + +[ENFORCR] +Enforcer + +[SECURI] +Securicar + +[BANSHEE] +Banshee + +[PREDATR] +Predator + +[BUS] +Bus + +[RHINO] +Rhino + +[BARRCKS] +Barracks OL + +[CUBAN] +Cuban Hermes + +[HELI] +Helicopter + +[DODO] +Dodo + +[COACH] +Coach + +[CABBIE] +Cabbie + +[STALION] +Stallion + +[RUMPO] +Rumpo + +[RCBANDT] +RC Bandit + +[ROMERO] +Romero's Hearse + +[PACKER] +Packer + +[ADMIRAL] +Admiral + +[SQUALO] +Squalo + +[SEASPAR] +Sea Sparrow + +[PIZZABO] +Pizza Boy + +[GANGBUR] +Gang Burrito + +[TROPIC] +Tropic + +[SPEEDER] +Speeder + +[REEFER] +Reefer + +[FLATBED] +Flatbed + +[YANKEE] +Yankee + +[CADDY] +Caddy + +[ZEBRA] +Zebra Cab + +[TOPFUN] +Top Fun + +[SKIMMER] +Skimmer + +[PCJ600] +PCJ 600 + +[PHOENIX] +Phoenix + +[FAGGIO] +Faggio + +[FREEWAY] +Freeway + +[RCBARON] +RC Baron + +[RCRAIDE] +RC Raider + +[GLENDAL] +Glendale + +[OCEANIC] +Oceanic + +[SANCHEZ] +Sanchez + +[SPARROW] +Sparrow + +[LOVEFIS] +Love Fist + +[COASTG] +Coast Guard + +[DINGHY] +Dinghy + +[HERMES] +Hermes + +[SABRE] +Sabre + +[SABRETU] +Sabre Turbo + +[WALTON] +Walton + +[REGINA] +Regina + +[COMET] +Comet + +[DELUXO] +Deluxo + +[BURRITO] +Burrito + +[SPAND] +Spand Express + +[MARQUIS] +Marquis + +[BAGGAGE] +Baggage Handler + +[KAUFMAN] +Kaufman Cab + +[COASTMA] +Coastguard Maverick + +[MAVERIC] +Maverick + +[RANCHER] +Rancher + +[FBIRANC] +FBI Rancher + +[VIRGO] +Virgo + +[GREENWO] +Greenwood + +[HOTRING] +Hotring Racer + +[BLISTAC] +Blista Compact + +[FEST_DF] +Dist. traveled on foot (miles) + +[FEST_DC] +Dist. traveled by car (miles) + +[FESTDFM] +Distance traveled on foot (m) + +[FESTDCM] +Distance traveled by car (m) + +[TOT_DIS] +Total distance traveled (miles) + +[TOTDISM] +Total distance traveled (m) + +[DISTHEL] +Dist. traveled by helicopter (miles) + +[DISTHEM] +Distance traveled by helicopter (m) + +[DISTBOA] +Dist. traveled by boat (miles) + +[DISTBOM] +Distance traveled by boat (m) + +[FEST_LS] +People saved in an Ambulance + +[FEST_CC] +Criminals killed on Vigilante Mission + +[FEST_FE] +Total fires extinguished + +[FEST_RP] +Rampages passed + +[FEST_MP] +Missions passed + +[FEST_BB] +Bling-bling Scramble: + +[FEST_H0] +Most checkpoints + +[FEST_GC] +Gang Cars Totaled: + +[FEST_H1] +Diablo destruction + +[FEST_H2] +Mafia Massacre + +[FEST_H3] +Casino Calamity + +[FEST_H4] +Rumpo Wrecker + +[USJ] +UNIQUE STUNT BONUS! + +[RATNG1] +Upstanding Citizen + +[RATNG2] +Nobody Special + +[RATNG3] +Litterer + +[RATNG4] +Shoplifter + +[RATNG5] +Vandal + +[RATNG6] +Do boy + +[RATNG7] +Pickpocket + +[RATNG8] +Clepto + +[RATNG9] +Snitch + +[RATNG10] +Rat + +[RATNG11] +Leece + +[RATNG12] +Scam Artist + +[RATNG13] +Trickster + +[RATNG14] +Numbers Runner + +[RATNG15] +Hustler + +[RATNG16] +Bully + +[RATNG17] +Riff-Raff + +[RATNG18] +Scalawag + +[RATNG19] +Ruffian + +[RATNG20] +Outlaw + +[RATNG21] +Thug + +[RATNG22] +Drop Man + +[RATNG23] +SA Goon + +[RATNG24] +Goon + +[RATNG25] +Jailbird + +[RATNG26] +Ex-Con + +[RATNG27] +Felon + +[RATNG28] +Bag Man + +[RATNG29] +Wiseguy + +[RATNG30] +Wheelman + +[RATNG31] +Hired Muscle + +[RATNG32] +Hatchetman + +[RATNG33] +Headhunter + +[RATNG34] +Enforcer + +[RATNG35] +Ronin + +[RATNG36] +Fixer + +[RATNG37] +Hitman + +[RATNG38] +Associate + +[RATNG39] +Butcher + +[RATNG40] +Cleaner + +[RATNG41] +Assassin + +[RATNG42] +Consigliere + +[RATNG43] +Made Man + +[RATNG44] +Right-Hand Man + +[RATNG45] +Executioner + +[RATNG46] +Lieutenant + +[RATNG47] +Underboss + +[RATNG48] +Capo + +[RATNG49] +Boss + +[RATNG50] +Kingpin + +[RATNG51] +Don + +[RATNG52] +Godfather + +[PAGE_00] +. + +[WELCOME] +WELCOME TO + +[TSCORE] +EARNINGS: $~1~ + +[PBOAT_2] { reVC update } +Press the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button to fire the boat cannons. + +[HJSTAT] +Distance: ~1~.~1~m Height: ~1~.~1~m Flips: ~1~ Rotation: ~1~_ + +[HJSTATW] +Distance: ~1~.~1~m Height: ~1~.~1~m Flips: ~1~ Rotation: ~1~_ And what a great landing! + +[ATUTOR] +Press the ~h~~k~~TOGGLE_SUBMISSIONS~ ~w~button to toggle Paramedic missions on or off. + +[FEST_HA] +Highest Paramedic Mission level + +[C_KILLS] +CRIMINALS KILLED: ~1~ + +[HJSTATF] +Distance: ~1~ft Height: ~1~ft Flips: ~1~ Rotation: ~1~_ + +[HJSTAWF] +Distance: ~1~ft Height: ~1~ft Flips: ~1~ Rotation: ~1~_ And what a great landing! + +[CINCAM] +Cinematic Camera + +[RC4] +'RUMPO RAMPAGE' + +[LEGAL] +~g~Eliminate the criminal threat! + +[GA_2] +New engine and paint job. The cops won't recognize you! + +[HELP15] +When on foot press the ~h~~k~~PED_LOOKBEHIND~ ~w~button to ~h~look behind~w~. + +[FEC_LB4] +Look behind (R3 button) + +[PERPIC] +Hidden Packages found + +[CO_ONE] +Hidden Package ~1~ of ~1~ + +[GA_21] +You cannot store any more cars in this garage. + +[CHEAT1] +Cheat activated + +[CHEAT2] +Weapon cheat + +[CHEAT3] +Health cheat + +[CHEAT4] +Armor cheat + +[CHEAT5] +Wanted level cheat + +[CHEAT6] +Money cheat + +[CHEAT7] +Weather cheat + +[USJ_ALL] +ALL UNIQUE STUNTS COMPLETED! + +[JAN] +Jan + +[FEB] +Feb + +[MAR] +Mar + +[APR] +Apr + +[MAY] +May + +[JUN] +Jun + +[JUL] +Jul + +[AUG] +Aug + +[SEP] +Sept + +[OCT] +Oct + +[NOV] +Nov + +[DEC] +Dec + +[DEFDT] +--:---:---- --:--:-- + +[BONUS] +~g~BONUS $~1~ + +[HORN1] +Press the ~h~~k~~VEHICLE_HORN~ ~w~button to activate the ~h~horn. + +[HORN2] +Press the ~h~~k~~VEHICLE_HORN~ ~w~button to activate the ~h~horn + +[HORN3] +Press the ~h~~k~~VEHICLE_HORN~ ~w~button to activate the ~h~horn + +[FEC_EXV] +Enter and exit vehicle + +[TAXI_M] +'TAXI DRIVER' + +[COP_M] +'VIGILANTE' + +[FIRE_M] +'FIREFIGHTER' + +[AMBUL_M] +'PARAMEDIC' + +[HJ_IS] +INSANE STUNT BONUS: $~1~ + +[HJ_PIS] +PERFECT INSANE STUNT BONUS: $~1~ + +[HJ_DIS] +DOUBLE INSANE STUNT BONUS: $~1~ + +[HJ_PDIS] +PERFECT DOUBLE INSANE STUNT BONUS: $~1~ + +[HJ_TIS] +TRIPLE INSANE STUNT BONUS: $~1~ + +[HJ_PTIS] +PERFECT TRIPLE INSANE STUNT BONUS: $~1~ + +[HJ_QIS] +QUADRUPLE INSANE STUNT BONUS: $~1~ + +[HJ_PQIS] +PERFECT QUADRUPLE INSANE STUNT BONUS: $~1~ + +[FESZ_LS] +Load Successful. + +[HELI_1A] +Test your skills with the Sparrow, see how quickly you can complete the course. + +[HELI_1B] +Course Complete! $ ~1~ + +[HELIODD] +Helicopter odd jobs + +[LAW] +THE LAWYER MISSIONS + +[LAW1_1] +~g~Go get some new threads from Rafael's clothes shop. + +[LAW4_6] +Burn the management! + +[LAW4_7] +Kill the bosses! + +[LAW4_8] +Fight, Fight, Fight, Fight. + +[LAW4_9] +More Holiday, Less Work! + +[LAW4_11] +Fight! Fight! Fight! Fight! + +[LAW4_12] +Viva la revolution! + +[GENERAL] +THE COLONEL MISSIONS + +[GEN3_4] +Tommy Vercetti. Let's go... + +[GEN3_13] +What's the matter with you man?! Get on the roof across the yard before they turn up! + +[GEN3_17] +Sheeit! You trying to kill me?! + +[GEN3_21] +~g~He got Diaz's money! Chase him down and get it back! + +[GEN3_24] +~r~Diaz died! You failed to protect him! + +[GEN3_26] +~r~You shot Diaz! + +[GEN3_27] +~r~You shot Diaz's bodyguards! + +[GEN3_31] +~g~Now go to the drop off and watch over Diaz. + +[GEN3_32] +~g~Get to your vantage point on the roof of the building opposite Lance. + +[COKE] +THE COKE BARON MISSIONS + +[COK1_3] +Hope you fall and break your neck! + +[COK1_6] +I'm sick of these pricks. + +[COK2_7] +See those marker boys? Try taking out the lights! + +[COK2_10] +You sure is better at shooting than talking. + +[COK2_11] +Thanks. You're a real charmer yourself. + +[COK2_12] +I Know, Tommy. + +[COK2_18] +You cool with Kenny Loggins + +[COK2_19] +Hell, I love this record + +[COK2_26] +~r~You killed Lance! + +[COK3_1] +Don't shoot, dude! + +[COK3_2] +What's in this stuff? + +[COK3_3] +He's taking the boat. Bummer. + +[COK3_4] +Help! Some square's stealing the boat, man! + +[COK4_W] +Uugghh! That's the last of them. + +[COK4_X] +I'm going to start her up. + +[COK4_Y] +I think we've got some new friends. + +[COK4_2] +Yeah. + +[COK4_6] +Do you know where we're going? + +[COK4_7] +Are we lost? + +[COK4_8] +We got some competition! + +[COK4_9] +Take 'em out! + +[COK4_9A] +It's time for the Lance Vance Dance! + +[COK4_10] +They're matchwood! And fish food. + +[COK4_11] +We made it! Those other boats ain't VIP class. + +[COK4_17] +They're getting desperate! + +[COK4_18] +My damn feet are wet! WE'RE TAKING ON WATER! + +[COK4_21] +Bridge coming up! + +[COK4_22] +Bail out, she's about to blow! + +[COK4_23] +Good shooting. + +[COK4_29] +~r~You killed Lance! + +[ASS1_6] +Go on Tommy, I'll be ok! + +[ASS1_7] +Eat this, you murdering bastards!! + +[ASS1_8] +I'm pinned down! + +[ASS1_9] +I got you covered Tommy! + +[ASS1_10] +Hey, this is a real nice herbaceous border + +[ASS1_11] +Hey Tommy, can my room have a view of the bay? + +[ASS1_12] +Beautiful high ceilings in here... + +[ASS1_3] +Lance! I need cover! + +[ASS1_4] +Diaz must be inside! + +[ASS1_5] +Lance! + +[TAXWAR] +TAXI WAR MISSIONS + +[NOTAXI] +~g~You need a Kaufman Cab to activate this mission. + +[TAXW1_5] +~g~You need to be in a Kaufman cab! + +[TAX2_4] +Go on, Tommy. + +[TAX2_5] +Beat the hell out of him. + +[TAX2_6] +He hasn't even got a license. + +[TAX2_7] +Damn limo services. + +[TAXW3_1] +~g~Go and pick up Mercedes. + +[RACE1] +~g~3..2..1.. GO GO GO! + +[RACE2] +~g~3 + +[RACE3] +~g~2 + +[RACE4] +~g~1 + +[RACE5] +~g~GO! + +[FIRST] +~b~1st + +[SECOND] +~b~2nd + +[THIRD] +~b~3rd + +[FOURTH] +~b~4th + +[RACETM] +~b~RACE TIME: ~1~:~1~ + +[RACETM2] +~b~RACE TIME: ~1~:0~1~ + +[RACEFA] +~r~You failed to win the race! + +[TEX1_5] +~r~He got away! + +[SEG3_2] +~g~Get to the van that contains the RC Raider and remote timed bombs. + +[SEG3_3] +~g~You must use the RC RAIDER to transport 4 bombs to 4 target zones on the building site. + +[SERG3_5] +~g~You can only carry one bomb at a time and cannot pick up successfully planted bombs. + +[SEG3_7] +~g~Once you have dropped the FIRST bomb successfully in a target zone the detonation timer will start; you must then drop all the bombs within this time period. + +[SEG3_8] +~g~All 4 bombs must be located at the 4 target zones to pass the mission and demolish the building. + +[SEG3_9] +~g~You hit the target! 3 more to go. + +[SEG3_10] +~g~You hit the target! 2 more to go. + +[SEG3_11] +~g~You hit the target! Just 1 more to go! + +[SEG3_12] +~r~You missed the target! Go get a bomb! + +[SEG3_13] +~g~Drop the bomb at a target zone. + +[SEG3_14] +~r~You ran out of time and failed to demolish the building. + +[SEG3_15] +~r~Your RC Raider has been destroyed! How you gonna transport the bombs now? + +[AVERY] +AVERY MISSIONS + +[ASM] +ASSASSIN MISSIONS + +[ASM_1] +ASSASSIN MISSION 1 + +[ASM1_1] +~g~Mr. Teal, your help in eradicating those out-of-towners was invaluable to business. I have more work for you with a more 'hands-on' approach. Your next job is taped under the phone. + +[ASM1_2] +~g~Get to the payphone outside the Mall in Washington. + +[ASM1_3] +~g~Carl Pearson, Pizza Delivery Man. He must not complete his deliveries. + +[ASM1_4] +~g~Kill the Pizza Delivery Man before he completes his deliveries. + +[ASM_2] +ASSASSIN MISSION 2 + +[ASM_3] +ASSASSIN MISSION 3 + +[ASM3_A] +TEXT NO LONGER NEEDED + +[ASM3_B] +TEXT NO LONGER NEEDED + +[ASM3_1] +~g~Go and get the weapon Mr. Black has left for you. + +[ASM3_2] +~g~Don't get too close to the target or he may spot you! + +[ASM3_3] +~g~For a quick safe kill find a secluded spot nearby with a clear view of your target. + +[ASM3_4] +~g~He's seen you! Better waste him any way you can! + +[ASM3_5] +~g~Marcus Hammond is working on an advertising board in Washington. + +[ASM3_6] +~g~Franco Carter is working for DBP Security off Ocean Drive. + +[ASM3_7] +~g~Dick Tanner is situated near the Jewelry shop in Vice Point. + +[ASM3_8] +~g~Nick Kong is situated near Washington Beach. + +[ASM3_9] +~g~Stuntman Driver is situated in Washington. + +[ASM3_10] +~r~You failed to kill them all. + +[ASM_4] +ASSASSIN MISSION 4 + +[ASM4_1] +~g~Go get the rifle left for you in the foliage outside the airport terminal. + +[ASM4_2] +~g~Don't miss your target or you may alert his bodyguards, and remember keep your distance so he does not spot you. + +[ASM4_3] +~g~Watch the woman on the balcony above the check-in desks inside the airport terminal. DO NOT KILL HER. + +[ASM4_4] +~g~Kill the man she hands the briefcase to but only AFTER HE PICKS IT UP. Then retrieve the briefcase and take it to Ammu-Nation in Downtown. + +[ASM4_5] +~g~Get the briefcase! + +[ASM4_6] +~g~Take the briefcase to Ammu-Nation in Downtown. + +[ASM4_7] +~r~You killed the woman, you fool! + +[ASM4_8] +~r~The target heard you firing your weapon! The deal is off! + +[ASM4_9] +~r~The target has boarded his flight! + +[ASM4_11] +~r~The target has seen you! The deal is off! + +[ASM4_13] +~g~He's spotted you and is making a run for it, nail him and get the briefcase! + +[ASM4_14] +~g~The distance bar in the upper right of the screen gives you an indication to how close you are to your target do not let it fill or he will see you. + +[ASM_5] +ASSASSIN MISSION 5 + +[KICK] +KICKSTART + +[KICK1_3] +~g~Number of times foot put down: ~1~ + +[KICK1_4] +~g~Time penalty: ~1~ seconds + +[BANK] +BANKJOB MISSIONS + +[BANK1] +BANKJOB MISSION 1 + +[BANK2] +BANKJOB MISSION 2 + +[BJM2_21] +~g~Hit as many targets as you can while your ammo lasts. + +[BANK3] +BANKJOB MISSION 3 + +[BJM3_1] +~g~Get a fast car and get to the starting grid. + +[BNK4_2A] +Boys at the car lot did a great job on this baby. + +[BNK4_3G] +Oh crap, now the cops are onto us! + +[BNK4_3H] +- and we're not even there yet. + +[BNK4_3K] +We'll have to lose the cops first... + +[BNK4_3L] +Christ Tommy, you trying to kill us all!? + +[BNK4_3N] +Everything I care about gets trashed! + +[BNK4_26] +Hot damn! Here they come! + +[BNK4_32] +Use explosives to open the deposit boxes! + +[BNK4_36] +Where's Cam? + +[BNK4_37] +History... + +[BNK4_38] +That's the last of them. GO! GO! GO! + +[BNK_39] +Shit! Where's Hilary? + +[BK4_40A] +I'll give him abandonment issues! + +[BNK4_42] +Hey guys! Get in! I got you covered! + +[BNK4_43] +I've got our asses covered, DRIVE! + +[BNK4_44] +We made it! We're rich! RICH! + +[BNK4_45] +Shame Cam didn't make it, he was a good guy! + +[BNK4_46] +Yeah. Still... more for us! + +[BNK4_47] +Damned straight! YEEEEHAAAH! + +[BNK4_48] +Tommy, would you like a massage? + +[BNK4_49] +Well, Hi there, Mercedes! Yeah, I'm a little tense... + +[BNK450A] +What'd I tell you Tommy? What'd I tell you? Bent SWAT better watch out when Kent Paul is in town. + +[BNK450B] +Come on, gimme a bigger slice, mate, c'mon. I gotta get some new threads. + +[BNK4_51] +You look fine to me. + +[KENT] +KENT PAULS MISSIONS + +[KENT1] +KENT PAUL MISSION1 + +[COUNT] +COUNTERFEITING MISSIONS + +[COUNT1] +COUNTERFEITING MISSION 1 + +[COUNT2] +COUNTERFEITING MISSION 2 + +[BIKE] +THE BIKER GANG MISSIONS + +[BIKE1] +BIKER MISSION 1 + +[BIKE2] +BIKER MISSION 2 + +[BIKE3] +BIKER MISSION 3 + +[GOAWAY1] +~g~Come back when you have finished the Haitian gang missions. + +[HAIT] +THE HAITIAN GANG MISSIONS + +[HAIT1] +HAITIAN MISSION 1 + +[HAIT2] +HAITIAN MISSION 2 + +[HAIT3] +HAITIAN MISSION 3 + +[HAM3_6] +~g~Use the sniper rifle I have left to accomplish your task. + +[ROCK] +THE ROCKBAND GANG MISSIONS + +[ROK1_4] +~g~Ok, I think this is what you were after... + +[ROK1_1E] +~g~It'll cost more than you've got! + +[ROK1_1F] +~g~Come back when you got the money. + +[RBM2_6] +~g~Wow! She's a bloke, stop him! + +[ROCK3] +ROCKBAND MISSION 3 + +[RBM3_5] +~g~Take Love Fist to the venue. + +[CUBANM] +THE CUBAN GANG MISSIONS + +[CUBAN1] +CUBAN MISSION 1 + +[CUBAN2] +CUBAN MISSION 2 + +[CUB2_10] +~r~You are supposed to be killing Haitians, not Cubans. + +[CUBAN3] +CUBAN MISSION 3 + +[CUBAN4] +CUBAN MISSION 4 + +[PROT] +PROTECTION MISSIONS + +[PORN] +PORN MISSIONS + +[PORN1] +PORN MISSION 1 + +[POR1_03] +~r~Candy is dead! + +[PORN2] +PORN MISSION 2 + +[PORN3] +PORN MISSION 3 + +[PORN4] +PORN MISSION 4 + +[PHIL] +PHIL MISSIONS + +[PHIL1] +PHIL MISSION 1 + +[PHIL2] +PHIL MISSION 2 + +[PIZ1_A] +PIZZA BOY MISSION + +[CNTBUY1] +Printworks purchased: $~1~ + +[CARBUY] +Car Showroom purchased: $~1~ + +[PORNBUY] +Film Studio purchased: $~1~ + +[ICEBUY] +Ice Cream Factory purchased: $~1~ + +[TAXIBUY] +Taxi Firm purchased: $~1~ + +[BANKBUY] +The Malibu purchased: $~1~ + +[BOATBUY] +Boatyard purchased: $~1~ + +[PRNT_NO] +You cannot buy the Print Works at this time, come back later. + +[CAR_NO] +You cannot buy the Car Showroom at this time, come back later. + +[PORN_NO] +You cannot buy the Film Studio at this time, come back later. + +[ICE_NO] +You cannot buy the Ice Cream Factory at this time, come back later. + +[TAXI_NO] +You cannot buy the Taxi Company at this time, come back later. + +[BANK_NO] +You cannot buy The Malibu at this time, come back later. + +[BOAT_NO] +You cannot buy the Boatyard at this time, come back later. + +[COL2_6] +Freeze, imperialist American pig! + +[COL2_6B] +Zat iz propertay of ze government Francais. + +[COL2_6C] +'And eet over! + +[COL3_A] +Thomas I appreciate you coming + +[COL3_B] +Forgive me for getting straight to business, + +[COL3_C] +Diaz has asked me to oversee a minor business transaction. + +[COL3_D] +Let's hope it goes better than last time. + +[COL3_E] +Which is why I thought of you, my friend. + +[COL3_F] +I've dropped some protection at the multistory carpark. + +[COL3_G] +Pick it up then go and watch over Diaz's men at the drop off. + +[COL4_2] +Don't know Sir! + +[COL4_5] +Sir, Yes Sir! + +[COL4_10] +Lets go eat some doughnuts. + +[COL4_16] +Sir! Moving vehicle Sir! + +[COL4_25] +Vehicle self destruct initiated! + +[COL5_6] +Mercedes, that girl will be the death of me. + +[COL5_8] +Damn cockroach! + +[COL5_5] +Die French peegs! + +[CNT2_1] +Kill him. + +[CNT2_2] +Get the Plates! + +[CNT2_3] +Protect the courier! + +[FINKILL] +Ok boys, kill him! + +[FIN_1A] +Come here you double-crossing piece of shit! + +[FIN_1B] +You're going down, you back stabbing prick! + +[FIN_1C] +This is the last dance for lance vance! + +[FIN_2B] +Oh you think so! + +[FIN_2C] +I said I had enough of that at school! + +[FIN_3] +No one to cover your ass now, eh Tommy? + +[FIN_4] +You're history, Tommy, history + +[FIN_5] +You picked the wrong side, Lance... + +[FIN_6] +Sonny's up with the safe and MY money... + +[FIN_10] +Sonny? SONNY! I'm coming for ya! + +[FIN_11A] +You took fifteen years from me Sonny... + +[FIN_11B] +And now I'm gonna make you pay! + +[FIN_12A] +You still don't get it do you! + +[FIN_12B] +I OWN you, Tommy. + +[FIN_12C] +Those fifteen years were mine to spend! + +[FIN_13] +Get him boys, he never understood a thing. + +[RACES_4] +3 + +[RACES_5] +2 + +[RACES_6] +1 + +[RACES_7] +GO! + +[RACES_9] +Time: ~1~:~1~ + +[RACES] +TIME: + +[RACES17] +New best time: ~1~:~1~ + +[RACES18] +YOU HAVE WON: $~1~ + +[RACES20] +New best time: ~1~:0~1~ + +[RACES21] +Time: ~1~:0~1~ + +[RCH1_1] +NO LONGER NEEDED NO LONGER NEEDED NO LONGER NEEDED NO LONGER NEEDED NO LONGER NEEDED + +[RCH1_2] +~g~The CHECKPOINTS are scattered throughout the airport. + +[RCH1_3] +NO LONGER NEEDED NO LONGER NEEDED NO LONGER NEEDED NO LONGER NEEDED NO LONGER NEEDED + +[RCH1_5] +Time: + +[RCRC1_2] +~g~Get to the starting grid now! + +[RCRC1_4] +~g~3 + +[RCRC1_5] +~g~2 + +[RCRC1_6] +~g~1 + +[RCRC1_7] +~g~GO! + +[RCRC1_8] +~g~Race time: ~1~ seconds + +[RCPL1_1] +~g~Compete in a CHECKPOINT RACE with 3 other RC Baron's + +[RCPL1_2] +~g~You must go through the ~o~CENTRE CORONA ~g~to successfully pass a checkpoint. + +[RCPL1_3] +~g~Get to the starting grid now! + +[ICC1_O] +What is wrong with you?? + +[FEA_2SP] +2 Speakers + +[FEA_4SP] +More than 2 speakers + +[FEA_EAR] +Headphones + +[FEA_NAH] +NO AUDIO HARDWARE + +[FET_APP] +APPLY + +[FES_SKN] +SKIN NAME + +[FES_DAT] +DATE + +[FES_SET] +Use Skin + +[FET_DEF] +Restore Defaults + +[FESZ_QZ] +Are you sure you want to save this game? + +[FES_SCG] +Save the current game? + +[FES_LCG] +Load the game and continue playing? + +[FEC_FIR] +Fire + +[FEC_NWE] +Next weapon + +[FEC_PWE] +Previous weapon + +[FEC_FOR] +Forward + +[FEC_BAC] +Backwards + +[FEC_LEF] +Left + +[FEC_RIG] +Right + +[FEC_ZIN] +Zoom in + +[FEC_ZOT] +Zoom out + +[FEC_EEX] +Enter+exit + +[FEC_RAD] +Radio + +[FEC_SUB] +Sub-mission + +[FEC_CMR] +Change camera + +[FEC_JMP] +Jump + +[FEC_SPN] +Sprint + +[FEC_HND] +Handbrake + +[FEC_LOL] +Look left + +[FEC_LOR] +Look right + +[FEC_NTR] +Next target + +[FEC_PTT] +Previous target + +[FEC_LBA] +Look behind + +[FEC_CEN] +Center camera + +[FET_CFT] +ON FOOT + +[FET_CCR] +IN CAR + +[FET_CAC] +ACTION + +[FEC_IBT] +- + +[FEC_MXO] +MXB1 + +[FEC_MXT] +MXB2 + +[FEC_UNB] +UNBOUND + +[FEC_TFL] +Look left+Turret L + +[FEC_TFR] +Look right+Turret R + +[FEC_MWF] +MS WHEEL UP + +[FEC_MWB] +MS WHEEL DN + +[FEC_ORR] +or + +[FEC_NUS] +NOT USED + +[FEC_LUD] +Look Up + +[FEC_LDU] +Look Down + +[FEC_CMP] +COMBO: LOOK L+R + +[LAW_1A] +law_1a + +[LAW_1B] +law_1b + +[LAW_2A] +law_2a + +[LAW_2B] +law_2b + +[FEH_STA] +STATS + +[FEH_LOA] +LOAD + +[FEH_CON] +CONTROLS + +[FEH_AUD] +AUDIO + +[FEH_LAN] +LANGUAGE + +[FEH_SGA] +START NEW GAME + +[FEO_CON] +Controller Setup + +[FEO_AUD] +Audio Setup + +[FEO_DIS] +Display Setup + +[FEO_LAN] +Language Setup + +[FEO_PLA] +Player Setup + +[FEA_OUT] +Output + +[FEA_ST] +Stereo + +[FEA_DTS] +DTS + +[FEA_RSS] +Radio station + +[FEA_NON] +RADIO OFF + +[FEA_FM0] +WILDSTYLE + +[FEA_FM1] +FLASH FM + +[FEA_FM2] +KCHAT + +[FEA_FM3] +FEVER 105 + +[FEA_FM4] +VROCK + +[FEA_FM5] +VCPR + +[FEA_FM7] +EMOTION 98.3 + +[FEA_FM8] +WAVE 103 + +[FED_BRI] +Brightness + +[FED_TRA] +Trails + +[FED_SUB] +Subtitles + +[FED_WIS] +Wide Screen + +[FED_POS] +Screen Position + +[FEP_RES] +Resume + +[FEP_STG] +Start Game + +[FEP_STA] +Stats + +[FEP_BRI] +Briefs + +[FEP_OPT] +Options + +[FEP_QUI] +Quit Game + +[FES_LOA] +Load game + +[FES_DEL] +Delete game + +[FEC_CSU] +Controller Setup + +[FEC_RED] +Redefine Controls + +[FEC_MOU] +Mouse Settings + +[DISTGOL] +Dist. traveled by golf cart (miles) + +[DISTGOM] +Distance traveled by golf cart (m) + +[ST_FAVR] +Favorite radio station + +[ST_WSTR] +least favorite radio station + +[ST_FAVV] +Favorite vehicle + +[ST_STAR] +Total number of wanted stars attained + +[ST_HEAD] +Number of headshots + +[ST_GANG] +Least favorite gang + +[ST_STGN] +Total number of wanted stars evaded + +[TYREPOP] +Tires popped with gunfire + +[TYRESLA] +Tires slashed with a blade + +[ST_BRK] +Number of bloodring kills + +[ST_LTBR] +Longest time in bloodring (secs) + +[ST_GNG1] +Cubans + +[ST_GNG2] +Haitians + +[ST_GNG3] +Streetwannabe's + +[ST_GNG4] +Diaz's gang + +[ST_GNG5] +Security guards + +[ST_GNG6] +Biker gang + +[ST_GNG7] +Vercetti gang + +[ST_GNG8] +Golfers + +[FEA_FM6] +ESPANTOSO + +[ST_ASSI] +Assassination Contracts Completed + +[DISTBIK] +Dist. traveled by bike (miles) + +[DISTBIM] +Distance traveled by bike (m) + +[HOTEL] +Ocean View + +[KICK1_9] +CHECKPOINTS: + +[FIN_B6] +You do not have enough money to start this mission. + +[TEX3_9] +~g~Pickup a bomb by maneuvering the RC helicopter next to it. + +[HELP22] +Go to the green house blip on the radar. + +[FES_FMS] +Format Successful. Select OK to continue. + +[FES_SSC] +Save Successful. Select OK to continue. + +[FES_DSC] +Delete Successful. Select OK to continue. + +[FESZ_QC] +Proceed with overwriting this corrupted save game? + +[FES_CHE] +Warning! One or more cheats have been activated. This may affect your save game. It is recommended that you do not save this game. + +[FET_SG] +SAVE GAME + +[FEH_BRI] +BRIEF + +[FEH_DIS] +DISPLAY + +[FEH_MAP] +MAP + +[FEM_OK] +OK + +[FEC_CRO] +Crouch + +[FEC_CR3] +Crouch (L3 button) + +[FEC_SMT] +Sub-mission + +[FEC_SM3] +Sub-mission (R3 button) + +[FEC_RSC] +Radio stations + +[ST_PR01] +Flyboy + +[ST_PR02] +Aircraftman + +[ST_PR03] +Pilot Officer + +[ST_PR04] +Corporal + +[ST_PR05] +Lieutenant + +[ST_PR06] +Sergeant + +[ST_PR07] +Captain + +[ST_PR08] +Biggs + +[ST_PR09] +Wedge + +[ST_PR10] +Red Baron + +[ST_PR11] +Goose + +[ST_PR12] +Viper + +[ST_PR13] +Jester + +[ST_PR14] +Chappy + +[ST_PR15] +Iceman + +[ST_PR16] +Maverick + +[ST_PR17] +Noops + +[ST_PR18] +Air Chief Marshal + +[ST_PR19] +Ace + +[FET_LG] +LOAD GAME + +[CAR_EXP] +Road Vehicles destroyed + +[BOA_EXP] +Boats destroyed + +[HEL_DST] +Planes & Helicopters destroyed + +[STFT_01] +Fastest time on 'Alloy Wheels Of Steel' + +[STFT_02] +Fastest time on 'The Driver' + +[STFT_03] +Fastest time in Dirt Ring + +[STFT_04] +Fastest time on RC Plane Race + +[STFT_05] +Fastest time on RC Car Race + +[STFT_06] +Fastest time on RC helicopter Pickup + +[STFT_07] +Fastest time on 'Terminal Velocity' + +[STFT_08] +Fastest time on 'Ocean Drive' + +[STFT_09] +Fastest time on 'Border Run' + +[STFT_10] +Fastest time on 'Capital Cruise' + +[STFT_11] +Fastest time on 'Tour!' + +[STFT_12] +Fastest time on 'V.C. Endurance' + +[STHC_01] +Highest score for Shooter + +[STHC_02] +Best Percentage of hits for Shooter + +[STHC_03] +Number of drug deals made + +[HELP24] +You can now take jobs from the Colonel. + +[HELP25] +You can now take jobs from Avery Carrington. + +[HELP29] +You can visit the clothes store when you're not on a mission. + +[HELP30] +When you buy new clothes your wanted level will be set to zero. + +[ASM4_24] +distance: + +[RBM1_6] +~g~Take Mercedes and the 'Love Juice' to the band at the recording studio. + +[RBM1_3] +NO LONGER NEEDED + +[HAM1_5] +NO LONGER NEEDED + +[RBM1_11] +NO LONGER NEEDED + +[HELP31] +To do a drive-by, first look left or right using the ~h~~k~~VEHICLE_LOOKLEFT~~w~ or the ~h~~k~~VEHICLE_LOOKRIGHT~. + +[HELP34] +You must have a sub machine gun to perform a drive-by. + +[STRIP_1] +~r~Not enough cash, you cheap sleazebag. + +[EXIT_1] +Press the ~h~~k~~PED_SPRINT~ ~w~button to exit. + +[ASM1_B] +Your next job is taped under the phone. + +[ASM1_C] +I have more work for you with a more 'hands-on' approach. + +[SCARF] +Apartment 3c + +[LAW4_10] +Rich management suck! + +[RCH1_6] +~g~Use the RC Helicopter to collect checkpoints scattered throughout the airport. + +[RCH1_9] +~b~TOTAL TIME: ~1~:~1~ + +[RCH1_10] +~b~TOTAL TIME: ~1~:0~1~ + +[WHEEL01] +TWO WHEELS DOUBLE BONUS: $ ~1~ Distance: ~1~.~1~m Time: ~1~ seconds + +[WHEEL02] +TWO WHEELS DOUBLE BONUS: $ ~1~ Distance: ~1~ feet Time: ~1~ seconds + +[WHEEL03] +TWO WHEELS BONUS: $ ~1~ Time: ~1~ seconds + +[WHEEL04] +TWO WHEELS BONUS: $ ~1~ Distance: ~1~.~1~m + +[WHEEL05] +TWO WHEELS BONUS: $ ~1~ Distance: ~1~ feet + +[WHEEL06] +WHEELIE BONUS: $ ~1~ Distance: ~1~.~1~m Time: ~1~ seconds + +[WHEEL07] +WHEELIE BONUS: $ ~1~ Distance: ~1~ feet Time: ~1~ seconds + +[WHEEL08] +WHEELIE BONUS: $ ~1~ Time: ~1~ seconds + +[WHEEL09] +WHEELIE BONUS: $ ~1~ Distance: ~1~.~1~m + +[WHEEL10] +WHEELIE BONUS: $ ~1~ Distance: ~1~ feet + +[WHEEL11] +STOPPIE BONUS: $ ~1~ Distance: ~1~.~1~m Time: ~1~ seconds + +[WHEEL12] +STOPPIE BONUS: $ ~1~ Distance: ~1~ feet Time: ~1~ seconds + +[WHEEL13] +STOPPIE BONUS: $ ~1~ Time: ~1~ seconds + +[WHEEL14] +STOPPIE BONUS: $ ~1~ Distance: ~1~.~1~m + +[WHEEL15] +STOPPIE BONUS: $ ~1~ Distance: ~1~ feet + +[ROK3_72] +Love Fist! + +[POR1_19] +Hey! + +[DESPERA] +Desperado + +[MOB_99A] +Get to the payphone next to the mall in Washington. + +[MOB_98A] +Get to the pay phone in Vice Point. + +[MOB_96A] +Get to the payphone at the airport terminal. + +[MOB_95A] +Get to the payphone in Little Havana. + +[BNK1_1] +Can I help you, sir? + +[BNK1_2] +There's an imposter! + +[BNK1_3] +He's gone insane! + +[BNK1_4] +Who the hell are you? + +[BNK1_5] +Where's your badge? + +[BNK1_6] +There they are! Shoot to kill! + +[MOB_24A] +Hola, is this Mr. Vercetti? + +[MOB_24B] +Yeah. + +[MOB_24C] +This is Cortez. You were at my party. + +[MOB_24D] +Yeah. I remember. + +[MOB_24E] +Mr. Vercetti, it was a most unfortunate incident that happened with your business deal. + +[MOB_24F] +I know. + +[MOB_24G] +I want you to know me and my people are doing their utmost to get to the bottom of it. + +[MOB_24H] +If you'd like to talk to me more privately, you can find me at the boat, eh? Okay? Good day, senor. + +[BNK2_6] +This guy's a lunatic! + +[ANGEL] +Angel + +[CUBJET] +Cuban Jetmax + +[SANDKIN] +Sandking + +[POLMAV] +Police Maverick + +[BOXVILL] +Boxville + +[BENSON] +Benson + +[HOTRINA] +Hotring Racer + +[HOTRINB] +Hotring Racer + +[BLOODRA] +Bloodring Banger + +[BLOODRB] +Bloodring Banger + +[MAFIACR] +Mafia Cruiser + +[COP_M2] +'VICE SQUAD' + +[COP_M3] +'BROWN THUNDER' + +[BNK3_2] +I'm not driving for you, no way, I'm sharing this at group. + +[FEM_SL1] +Save File 1 Not Present + +[FEM_SL2] +Save File 2 Not Present + +[FEM_SL3] +Save File 3 Not Present + +[FEM_SL4] +Save File 4 Not Present + +[FEM_SL5] +Save File 5 Not Present + +[FEM_SL6] +Save File 6 Not Present + +[FEM_SL7] +Save File 7 Not Present + +[FEM_SL8] +Save File 8 Not Present + +[FEA_CHA] +Changing audio output to STEREO. Please wait... + +[FEA_CHD] +Warning! You are changing from STEREO output to DTS. Please wait... + +[FEI_SEL] +Select + +[FEI_BAC] +Back + +[FEI_RES] +Resume + +[FEI_NAV] +Navigate + +[FEI_BTX] +/ button - + +[FEI_BTT] +" button - + +[FEI_STA] +START button - + +[FEI_BTD] +; = > < - + +[FEI_STO] +Stop + +[ST_ICEC] +'Ice Cream' Sold + +[MOB_68A] +Tommy son, have I got a surprise for you + +[MOB_68B] +I'm down at the recording studios with some major artists + +[MOB_68C] +Why don't you pay us a visit? + +[MOB_68D] +You know it makes sense, dontcha? See ya later. + +[OUTFT1] +Street + +[OUTFT2] +Soiree + +[OUTFT3] +Coveralls + +[OUTFT4] +Country Club + +[OUTFT5] +Havana + +[OUTFT6] +Cop + +[OUTFT7] +Bank Job + +[OUTFT8] +Casual + +[OUTFT9] +Mr Vercetti + +[OUTFT10] +Tracksuit + +[OUTFT13] +MC Tommy + +[CAR_AS1] +CAR SHOWROOM ASSET COMPLETED + +[CAR_AS2] +~g~Sunshine Auto's will now generate revenue up to a maximum of $~1~. Make sure you collect it regularly. + +[BUYSAVE] +~g~You can now save your game here when not on a mission. + +[BUYGARG] +~g~You can also store vehicles in this garage. + +[STRPBUY] +Pole Position Club purchased: $~1~ + +[GA_4] +Car bombs are $500 each + +[GA_5] +Your car is already fitted with a bomb. + +[GA_6] { reVC update } +Park it, prime it by pressing the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button and LEG IT! + +[GA_7] { reVC update } +Arm with the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button. Bomb will go off when engine is started. + +[GA_6B] { reVC update } +Park it, prime it by pressing the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button and LEG IT! + +[GA_7B] { reVC update } +Arm with the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button. Bomb will go off when engine is started. + +[MOB_70A] +Tommy, it's me, Colonel Cortez. Look senor, I believe you are a man who can get things done. So please help me. + +[MOB_70B] +You can find me at the boat. + +[PICK2] +.357 delivered to Ocean View Hotel! + +[PICK3] +Chainsaw delivered to Ocean View Hotel! + +[PICK4] +Flame Thrower delivered to Ocean View Hotel! + +[PICK5] +.308 Sniper delivered to Ocean View Hotel! + +[PICK6] +Minigun delivered to Ocean View Hotel! + +[PICK7] +Rocket Launcher delivered to Ocean View Hotel! + +[PICK8] +Sea Sparrow now available from the Mansion on Starfish Island! + +[PICK9] +Tank now available from the Army Barracks! + +[PICK10] +Hunter now available from the Army Barracks! + +[CLOTH1] +Soiree outfit delivered to Rafael's on Ocean Beach. + +[CLOTH2] +Street outfit delivered to Safehouses. + +[CLOTH3] +Coveralls outfit delivered to Tooled Up in The North Point Mall. + +[CLOTH4] +Country Club outfit delivered to The Golf Club in Leaf Links. + +[CLOTH5] +Havana outfit delivered to Little Havana Streetwear in Little Havana. + +[CLOTH6] +Cop outfit delivered to Police Station on Washington Beach. + +[CLOTH7] +Casual outfit delivered to Gash in The North Point Mall. + +[CLOTH8] +Mr Vercetti outfit delivered to Collar & Cuffs on Ocean Beach. + +[CLOTH9] +Tracksuit outfit delivered to Jocksport in Downtown. + +[CLOTH10] +Bank Job outfit delivered to Malibu Club in Vice Point. + +[MOB_62A] +Tommy, is Ricardo Diaz, I want to thank you for looking out for me my man. + +[MOB_62B] +I ask that prick Cortez, he say you the real deal, my friend, why you not come see me. + +[MOB_62C] +I need a guy like you. All I have now is dickheads, + +[MOB_62D] +dickheads everywhere, yo. I make you real rich. + +[GOAWAY2] +~g~Come back when you have finished the Biker gang missions. + +[COL2_9] +You American idiot! They followed you here! + +[LOADCOL] +Loading... + +[STFT_17] +Fastest time on 'PCJ Playground' + +[STFT_18] +Fastest time on 'Trial By Dirt' + +[STFT_19] +Fastest time on 'Test Track' + +[NEW_REC] +~g~New Record Set!! ~w~~1~ minutes ~g~and ~w~~1~ seconds. + +[BMX_HOW] +~g~Do two laps of the dirt track, ~y~passing through ~g~the ~y~CHECKPOINTS ~g~as you go! + +[BMXREW1] +~g~Each time you beat your previous record for the two laps + +[BMXREW2] +~g~a larger ~y~REWARD ~g~will be awarded! + +[BMXRAIN] +~g~Looks like rain... + +[ITBEG] +In the beginning... + +[NBMNBUY] +El Swanko Casa purchased: $~1~ + +[LNKVBUY] +Links View Apartment purchased: $~1~ + +[HYCOBUY] +Hyman Condo purchased: $~1~ + +[BUYGARS] +~g~You can also store vehicles in these garages. + +[OCHEBUY] +Ocean Heights Apartment purchased: $~1~ + +[WASHBUY] +1102 Washington Street purchased: $~1~ + +[VCPTBUY] +3321 Vice Point purchased: $~1~ + +[HELP6_C] +Press the ~h~~k~~VEHICLE_HANDBRAKE~ ~w~button to apply the vehicle's handbrake. + +[HELP2_A] +Press the ~h~~k~~PED_SPRINT~ ~w~button when running to ~h~sprint + +[HELP4_A] +Press the ~h~~k~~VEHICLE_ACCELERATE~ ~w~button to accelerate. + +[HELP5_A] +Press the ~h~~k~~VEHICLE_BRAKE~ ~w~button to brake, or to reverse if the vehicle has stopped. + +[HELP8_A] +Press the~h~ ~k~~PED_SNIPER_ZOOM_IN~ button ~w~to ~h~zoom in ~w~with the rifle and the~h~ ~k~~PED_SNIPER_ZOOM_OUT~ button ~w~to ~h~zoom out ~w~again. + +[PBOAT_1] { reVC update } +Press the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button to fire the boat cannons. + +[SEG3_4] { reVC update } +~g~You can pick up bombs by simply piloting your RC Raider adjacent to each one, to drop a bomb press the ~h~~k~~VEHICLE_FIREWEAPON~ ~g~button. + +[RCR1_3] { reVC update } +~g~If you wish to quit this mission press the ~h~~k~~VEHICLE_FIREWEAPON~ ~g~button to detonate your RC car. + +[HELP32] { reVC update } +Then fire using the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button. + +[HELP33] { reVC update } +Then fire using the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button. + +[TTUTOR] +Press the ~h~~k~~TOGGLE_SUBMISSIONS~ button~w~ to toggle taxi missions on or off. + +[TTUTOR2] +Press the ~h~~k~~TOGGLE_SUBMISSIONS~ button~w~ to toggle taxi missions on or off. + +[FTUTOR] +Press the ~h~~k~~TOGGLE_SUBMISSIONS~ button~w~ to toggle fire truck missions on or off. + +[FTUTOR2] +Press the ~h~~k~~TOGGLE_SUBMISSIONS~ button~w~ to toggle fire truck missions on or off. + +[CTUTOR] +Press the ~h~~k~~TOGGLE_SUBMISSIONS~ button~w~ to toggle Vigilante missions on or off. + +[CTUTOR2] +Press the ~h~~k~~TOGGLE_SUBMISSIONS~ button~w~ to toggle Vigilante missions on or off. + +[HELP8_B] +Press the ~h~~k~~PED_SNIPER_ZOOM_IN~ button ~w~to ~h~zoom in ~w~with the rifle and the~h~ ~k~~PED_SNIPER_ZOOM_OUT~ button ~w~to ~h~zoom out ~w~again. + +[ATUTOR3] +Press the ~h~~k~~TOGGLE_SUBMISSIONS~ button~w~ to toggle Paramedic missions on or off. + +[GUN_H1] +~w~Press the~h~ ~k~~PED_SPRINT~ ~w~button to buy. Press the~h~ ~k~~VEHICLE_ENTER_EXIT~ ~w~button to exit. + +[PU_CF3] { reVC update } +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to replace current weapon in this slot. + +[PU_CF4] { reVC update } +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to replace current weapon in this slot. + +[HELP9_B] +Press the~h~ ~k~~PED_FIREWEAPON~ button ~w~to ~h~fire~w~ the sniper rifle. + +[HELP37] +If you do not want to enter the vehicle while car jacking someone, press the ~h~~k~~PED_SPRINT~ ~w~button. + +[HELP6_A] +Press the~h~ ~k~~VEHICLE_HANDBRAKE~ button ~w~to apply the vehicle's ~h~handbrake. + +[HELP6_D] +Press the~h~ ~k~~VEHICLE_HANDBRAKE~ button ~w~to apply the vehicle's ~h~handbrake. + +[HELP26] +Press the ~h~~k~~VEHICLE_ENTER_EXIT~ ~w~button to enter or exit a vehicle. + +[HELP27] +Press the ~h~~k~~VEHICLE_TURRETUP~~w~ button and the ~h~~k~~VEHICLE_TURRETDOWN~~w~ button, to shift your weight on a bike. + +[HELP28] +Press the ~h~~k~~VEHICLE_TURRETUP~~w~ button and the ~h~~k~~VEHICLE_TURRETDOWN~~w~ button, to shift your weight on a bike. + +[HELP35] +Press the ~h~~k~~GO_LEFT~~w~, and the ~h~~k~~GO_RIGHT~~w~, to steer the vehicle. + +[HELP36] +Press the ~h~~k~~GO_LEFT~~w~, and the ~h~~k~~GO_RIGHT~~w~, to steer the vehicle. + +[HELP42] +Follow the ~q~pink blip~w~ to find the hotel. + +[HELP19] +Walk into the ~q~pink marker ~w~to continue. + +[HELP1] +Stop in the center of the ~q~pink marker. + +[HELP12] +Walk into the center of the ~q~pink marker~w~ to trigger a mission. + +[SEG3_6] +~g~To successfully hit a target zone, you must drop the bomb in the area represented by the ~q~pink marker~w~. You can drop the bombs in any order. + +[S_PROMP] +When not on a mission you can save your progress by collecting the ~h~cassette tape pickup. + +[HELP16] +Walk through the front door of the ~h~Ocean View~w~ Hotel to enter the building. + +[HELP43] +~g~Goto the ~h~Ocean View~g~ hotel on Ocean Drive. + +[HELI_F1] +~r~Heli Checkpoint mission cancelled! + +[AMMUHLP] +If you need any weapons visit ~h~Ammu-Nation~w~. Follow the ~h~Gun blip~w~ on the radar. + +[HELI_1] +Downtown Chopper Checkpoint + +[HELI_2] +Ocean Beach Chopper Checkpoint + +[HELI_3] +Vice Point Chopper Checkpoint + +[HELI_4] +Little Haiti Chopper Checkpoint + +[FST_MFR] +Most Favorite Radio Station + +[FST_LFR] +Least Favorite Radio Station + +[FEI_HOL] +Hold + +[FEI_ZOO] +Zoom + +[FEI_BTR] +> < - + +[FEI_NA] +N\A + +[MESA] +Mesa Grande + +[STRP_NO] +You cannot buy the Stripclub at this time, come back later. + +[CHSE] +CHASE + +[NBMN_L] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase El Swanko Casa for $~1~ + +[NBMN_T] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase El Swanko Casa for $~1~ + +[NBMN_C] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase El Swanko Casa for $~1~ + +[LNKV_L] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Links View Apartment for $~1~ + +[LNKV_T] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Links View Apartment for $~1~ + +[LNKV_C] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Links View Apartment for $~1~ + +[HYCO_L] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Hyman Condo for $~1~ + +[HYCO_T] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Hyman Condo for $~1~ + +[HYCO_C] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Hyman Condo for $~1~ + +[OCHE_L] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Ocean Heights Apartment for $~1~ + +[OCHE_T] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Ocean Heights Apartment for $~1~ + +[OCHE_C] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Ocean Heights Apartment for $~1~ + +[WASH_L] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase 1102 Washington Street for $~1~ + +[WASH_T] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase 1102 Washington Street for $~1~ + +[WASH_C] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase 1102 Washington Street for $~1~ + +[VCPT_L] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase 3321 Vice Point for $~1~ + +[VCPT_T] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase 3321 Vice Point for $~1~ + +[VCPT_C] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase 3321 Vice Point for $~1~ + +[PRNT_L] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Print Works for $~1~ + +[PRNT_T] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Print Works for $~1~ + +[PRNT_C] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Print Works for $~1~ + +[CAR_L] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Car Showroom for $~1~ + +[CAR_T] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Car Showroom for $~1~ + +[CAR_C] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Car Showroom for $~1~ + +[PORN_L] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Film Studios for $~1~ + +[PORN_T] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Film Studios for $~1~ + +[PORN_C] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Film Studios for $~1~ + +[ICE_L] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Ice Cream Factory for $~1~ + +[ICE_T] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Ice Cream Factory for $~1~ + +[ICE_C] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Ice Cream Factory for $~1~ + +[TAXI_L] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Taxi Company for $~1~ + +[TAXI_T] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Taxi Company for $~1~ + +[TAXI_C] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Taxi Company for $~1~ + +[BANK_L] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase The Malibu for $~1~ + +[BANK_T] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase The Malibu for $~1~ + +[BANK_C] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase The Malibu for $~1~ + +[BOAT_L] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Boatyard for $~1~ + +[BOAT_T] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Boatyard for $~1~ + +[BOAT_C] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Boatyard for $~1~ + +[STRP_L] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Pole Position Club for $~1~ + +[STRP_T] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Pole Position Club for $~1~ + +[STRP_C] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Pole Position Club for $~1~ + +[STOCK] +~r~out of stock + +[HELP14] +To find the Lawyer's office, follow the ~h~L blip~w~ on the radar + +[RAMPAGE] +RAMPAGE!! + +[RAMP_F] +RAMPAGE FAILED!! + +[RAMP_P] +RAMPAGE PASSED!! + +[RAMP_A] +ALL RAMPAGES COMPLETED!! + +[PAGE_01] +Kill ~1~ Gang Members in 2 Minutes! + +[PAGE_02] +Destroy ~1~ Vehicles in 2 Minutes! + +[PAGE_03] +Drive-by and Waste ~1~ Gang Members in 2 Minutes! + +[PAGE_04] +Runover and Kill ~1~ Gang Members in 2 Minutes! + +[PAGE_05] +Gun Down ~1~ Gang Members in 2 Minutes! + +[SENTXS] +Sentinel XS + +[MAP_LEG] +Map Legend + +[VCNMAV] +VCN Maverick + +[LG_01] +Player position + +[LG_02] +Avery Carrington + +[LG_03] +Biker Contact + +[LG_04] +Colonel Cortez + +[LG_05] +Ricardo Diaz + +[LG_06] +Kent Paul + +[LG_07] +Lawyer + +[LG_08] +Phil Cassidy + +[LG_09] +Boatyard + +[LG_10] +Malibu Club + +[LG_11] +Cubans + +[LG_12] +Film Studio + +[LG_13] +Ammu-Nation + +[LG_14] +Haitians + +[LG_15] +Hardware Store + +[LG_16] +Safe House + +[LG_17] +Ice Cream + +[LG_18] +Kaufman Cabs + +[LG_19] +Love Fist + +[LG_20] +Print Works + +[LG_21] +Property + +[LG_22] +Pay 'n' Spray + +[LG_23] +Clothes Shop + +[LG_24] +Tommy's Mansion + +[LG_25] +Telephone + +[LG_26] +Wildstyle Radio Station + +[LG_27] +Flash FM Radio Station + +[LG_28] +KChat Radio Station + +[LG_29] +Fever 105 Radio Station + +[LG_30] +VROck Radio Station + +[LG_31] +VCPR Radio Station + +[LG_32] +Espantoso Radio Station + +[LG_33] +Emotion 98.3 Radio Station + +[LG_34] +Wave 103 Radio Station + +[LG_36] +Sun Yard + +[LG_37] +Strip Club + +[MAP_YAH] +YOU ARE HERE + +[TAXSHRT] +~g~You can use this Kaufman Cab to take you to destinations instead of driving. It will cost you $9. + +[MOB_09D] +Maybe Leo's already dead. Maybe I killed Leo and took his phone - you think of that prick? + +[FE_MLG] +MAP LEGEND + +[FED_RDR] +RADAR MODE + +[FED_HUD] +HUD MODE + +[FED_RDB] +BLIPS ONLY + +[FEST_HV] +Highest Vigilante Mission level + +[BRIBE1] +You have just picked up a police bribe, this will reduce your wanted level by one star. + +[CLOHELP] +Clean Clothes!! + +[CRED001] +ROCKSTAR NORTH + +[CRD001A] +STUDIO DIRECTOR + +[CRD001B] +ANDREW SEMPLE + +[CRED002] +PRODUCER + +[CRD002A] +DEVELOPMENT DIRECTOR + +[CRED003] +LESLIE BENZIES + +[CRED004] +ART DIRECTOR + +[CRED005] +AARON GARBUT + +[CRED006] +TECHNICAL DIRECTORS + +[CRED007] +OBBE VERMEIJ + +[CRED008] +ADAM FOWLER + +[CRED010] +ANDREW DUTHIE + +[CRED011] +CRAIG FILSHIE + +[CRED012] +WILLIAM MILLS + +[CRED013] +CHRIS ROTHWELL + +[CRD013A] +IMRAN SARWAR + +[CRD013B] +JAMES WORRALL + +[CRD013C] +JOHN HAIME + +[CRED014] +WRITTEN BY + +[CRED015] +JAMES WORRALL + +[CRED016] +PAUL KUROWSKI + +[CRED017] +DAN HOUSER + +[CRED018] +LEAD CHARACTER DESIGNER + +[CRD018A] +CHARACTER DESIGNER + +[CRED019] +IAN MCQUE + +[CRD019A] +TOKS SOLARIN + +[CRD019B] +ALAN DAVIDSON + +[CRED020] +LEAD ANIMATOR + +[CRED021] +ALEX HORTON + +[CRD022A] +ANIMATORS + +[CRED022] +LEE MONTGOMERY + +[CRD022B] +DUNCAN SHIELDS + +[CRD022C] +GUS BRAID + +[CRED023] +VEHICLE DESIGNERS + +[CRD023A] +JOLYON ORME + +[CRD023B] +ALAN DUNCAN + +[CRD024A] +LEAD VEHICLE DESIGNER + +[CRED024] +PAUL KUROWSKI + +[CRED025] +MAP DESIGNERS + +[CRED026] +ADAM COCHRANE + +[CRED027] +NIK TAYLOR + +[CRED028] +GARY MCADAM + +[CRED029] +KEIRAN BAILLIE + +[CRED030] +ALISDAIR WOOD + +[CRED031] +ANDREW SOOSAY + +[CRD031A] +STEVEN MULHOLLAND + +[CRD031B] +WAYLAND STANDING + +[CRD031C] +CAMPBELL J. DICK + +[CRD031D] +GRAPHIC DESIGNER + +[CRD031E] +STUART PETRI + +[CRED032] +LEAD PROGRAMMER + +[CRD032A] +PROGRAMMERS + +[CRED033] +ALEXANDER ROGER + +[CRED034] +GRAEME WILLIAMSON + +[CRED035] +BARANE CHAN + +[CRED036] +DEREK PAYNE + +[CRED037] +GORDON YEOMAN + +[CRD037A] +ALAN CAMPBELL + +[CRD037B] +MARK HANLON + +[CRD037C] +ANDRZEJ MADAJCZYK + +[CRED038] +LEAD MUSIC PRODUCER + +[CRED039] +CRAIG CONNER + +[CRED040] +STUART ROSS + +[CRED041] +LEAD AUDIO ENGINEER + +[CRED042] +ALLAN WALKER + +[CRD041A] +AUDIO ENGINEER + +[CRD041B] +AUDIO + +[CRD042A] +WILL MORTON + +[CRED043] +AUDIO PROGRAMMER + +[CRED044] +RAYMOND USHER + +[CRED045] +TEST MANAGER + +[CRED046] +CRAIG ARBUTHNOTT + +[CRED047] +LEAD QA + +[CRED048] +NEIL CORBETT + +[CRED049] +KEVIN WONG + +[CRED050] +QA + +[CRED051] +DAVID BEDDOES + +[CRED052] +DAVID WATSON + +[CRED053] +BARRY CLARK + +[CRED054] +ROSS SPARROW + +[CRED055] +JAMES ALLAN + +[CRED056] +NEIL MEIKLE + +[CRD056A] +GEORGE WILLIAMSON + +[CRD056B] +MATT JONES + +[CRD056C] +ROB HARBOUR + +[CRD056D] +TOM WHITTAKER + +[CRED057] +LEAD TECHNICAL SUPPORT + +[CRED058] +LORRAINE ROY + +[CRD057A] +TECHNICAL SUPPORT + +[CRED059] +CHRISTINE CHALMERS + +[CRD060A] +OFFICE SUPPORT + +[CRD060B] +KIM GURNEY + +[CRD060C] +CASSIE OLIVER + +[CRED060] +ROCKSTAR NEW YORK + +[CRED061] +EXECUTIVE PRODUCER + +[CRED062] +SAM HOUSER + +[CRED063] +PRODUCER + +[CRED064] +DAN HOUSER + +[CRED065] +VP OF DEVELOPMENT + +[CRED066] +JAMIE KING + +[CRED067] +CHIEF TECHNOLOGY OFFICER + +[CRED068] +GARY J. FOREMAN + +[CRED069] +ASSOCIATE PRODUCER + +[CRED070] +JEREMY POPE + +[CRD071A] +DIRECTOR OF QUALITY ASSURANCE + +[CRD072A] +JEFF ROSA + +[CRED071] +MUSIC SUPERVISOR + +[CRED072] +TERRY DONOVAN + +[CRED073] +PRODUCTION TEAM + +[CRED074] +TERRY DONOVAN + +[CRED075] +JENNIFER KOLBE + +[CRED076] +JENEFER GROSS + +[CRED077] +LAURA PATERSON + +[CRED078] +JEFF CASTANEDA + +[CRED079] +JERONIMO BARRERA + +[CRED080] +CARLY SLATER + +[CRED081] +JUNG KWAK + +[CRED082] +BRIAN WOOD + +[CRED083] +RENAUD SEBANNE + +[CRED084] +RICHARD KRUGER + +[CRD084A] +DANIEL EINZIG + +[CRD084B] +JACEN BURROWS + +[CRD084C] +LINN PR + +[CRD084D] +COVER ART + +[CRD084E] +STEPHEN BLISS + +[CRED085] +KENT PAUL'S 80 NOSTALGIA ZONE + +[CRED086] +WRITTEN BY DAN HOUSER + +[CRD086A] +PRODUCED BY ADAM TEDMAN + +[CRED087] +WWW.KENTPAUL.COM AND WWW.VICECITY.COM + +[CRED088] +CREATED BY + +[CRD088A] +ADAM TEDMAN + +[CRD088B] +DAVID YU + +[CRD088C] +JERRY LUNA + +[CRD088D] +STUART PETRI + +[CRD088E] +MICHAEL CARNEVALE + +[CRD088F] +GREG LAU + +[CRD088G] +FUTABA HAYASHI + +[CRED089] +QA MANAGER + +[CRED090] +CRAIG ARBUTHNOTT + +[CRED091] +LEAD ANALYST + +[CRED092] +ADAM DAVIDSON + +[CRD092A] +JOE HOWELL + +[CRD092B] +MARC FERNANDEZ + +[CRED093] +GAME ANALYST + +[CRED094] +RICHARD HUIE + +[CRED095] +ROCKSTAR TEST TEAM + +[CRED096] +LANCE WILLIAMS + +[CRED097] +JOE GREENE + +[CRED098] +BRIAN PLANER + +[CRD098A] +ELIZABETH SATTERWHITE + +[CRD098B] +JAMEEL VEGA + +[CRD098C] +MIKE HONG + +[CRED099] +LEE CUMMINGS + +[CRED100] +STORY + +[CRED101] +JAMES WORRALL + +[CRED102] +DAN HOUSER + +[CRED103] +ADAM TEDMAN + +[CRED104] +PAUL YEATES + +[CRED105] +JENEFER GROSS + +[CRED106] +LAURA PATERSON + +[CRED107] +CUT SCENES + +[CRED108] +WRITTEN BY DAN HOUSER AND JAMES WORRALL + +[CRED109] +AUDIO DIRECTED BY DAN HOUSER AND NAVID KHONSARI + +[CRED110] +PRODUCED BY JAMIE KING + +[CRD110A] +TALENT PROCUREMENT: JAMIE KING, SEAN MACALUSO + +[CRED111] +CAST + +[CRD111A] +SUPPORTING CHARACTERS + +[CRED112] +TOMMY VERCETTI - RAY LIOTTA + +[CRED113] +KEN ROSENBERG - WILLIAN FICHTNER + +[CRED114] +SONNY FORELLI - TOM SIZEMORE + +[CRED115] +STEVE SCOTT - DENNIS HOPPER + +[CRED116] +AVERY CARRINGTON - BURT REYNOLDS + +[CRED117] +RICARDO DIAZ - LUIS GUZMAN + +[CRED118] +LANCE VANCE - PHILIP MICHAEL THOMAS + +[CRED119] +COLONEL JUAN CORTEZ - ROBERT DAVI + +[CRED120] +UMBERTO ROBINA - DANNY TREJO + +[CRED121] +PHIL CASSIDY - GARY BUSEY + +[CRED122] +MITCH BAKER - LEE MAJORS + +[CRED123] +MERCEDES CORTEZ - FAIRUZA BALK + +[CRED124] +KENT PAUL - DANNY DYER + +[CRED125] +JEZZ TORRENT - KEVIN MCKIDD + +[CRED126] +TAXI CONTROLLER - DEBORAH HARRY + +[CRED127] +CANDY SUXXX - JENNA JAMESON + +[CRED128] +BJ SMITH - LAWRENCE TAYLOR + +[CRED129] +AUNTIE POULET - YOUREE CLEOMILI HARRIS + +[CRED130] +SUPPLIER - ARMANDO RIESCO + +[CRED131] +COUGAR - BLAYNE PERRY + +[CRED132] +HILARY - CHARLES TUCKER + +[CRED133] +CONGRESSMAN ALEX SHRUB - CHRIS LUCAS + +[CRED134] +OLD MAN KELLY - GEORGE DICENZO + +[CRD134A] +CAM JONES - GREG SIMS + +[CRD134B] +PSYCHO - HUNTER PLATIN + +[CRD134C] +MAUDE THE ICE CREAM LADY - JANE GENNARO + +[CRD134D] +JETHRO - JOHN ZURHELLEN + +[CRD134E] +GONZALES - JORGE PUPO + +[CRD134F] +DWAYNE - NAVID KHONSARI + +[CRD134G] +DICK - PETER MCKAY + +[CRD134H] +MIKE THE GOON & PORN GUY - ROBERT CIHRA + +[CRD134I] +PERCY - RUSSELL FOREMAN + +[CRED135] +MOTION CAPTURE + +[CRED136] +ANIMATED BY + +[CRD136A] +TECHNICAL DIRECTION BY ALEX HORTON + +[CRED137] +DIRECTED BY + +[CRD137A] +DIRECTED BY NAVID KHONSARI + +[CRED138] +PRODUCED BY + +[CRD138A] +PRODUCED BY JAMIE KING + +[CRD138B] +RENAUD SEBBANE + +[CRED139] +RECORDED AT PERSPECTIVE STUDIOS, BROOKLYN + +[CRED140] +MOTION CAPTURE ACTORS + +[CRD140A] +BLAYNE PERRY + +[CRD140B] +JONATHON SALE + +[CRD140C] +CHARLES TUCKER + +[CRD140D] +EDDIE MARRERO + +[CRD140E] +WILLIAM MCCALL + +[CRD140F] +JORGE PUPO + +[CRD140G] +ROBERT JACKSON + +[CRD140H] +TARA RADCLIFFE + +[CRD140I] +JENIFER GAMBETESE + +[CRD140J] +KRIS ACHEVARRIA + +[CRD140K] +ALI ORDOUBADI + +[CRD140L] +KAHLEEM POOLE + +[CRED141] +PEDESTRIAN DIALOGUE + +[CRD141A] +WRITTEN BY DAN HOUSER, MARC FERNANDEZ, GILLIAN TELLING AND NAVID KHONSARI + +[CRD141B] +WITH HELP FROM JEREMY POPE, LANCE WILLIAMS, AND JENNY JEMISON + +[CRED142] +WRITTEN BY + +[CRD142A] +DAN HOUSER AND JAMES WORRALL + +[CRED143] +DIRECTED BY DAN HOUSER, CRAIG CONNER, MARC FERNANDEZ, AND ALLAN WALKER + +[CRED144] +PRODUCED BY RENAUD SEBANNE + +[CRED145] +PEDESTRIANS + +[CRED146] +ADAM DAVIDSON, ADAM WATKINS, ALEJANDRO K. BROWN, ALEX ANTHONY SIOUKAS, ALEX GARCIA, + +[CRED147] +ALICE SALTZMAN, ALISON CIHRA, AMY SALIMA, AMY SALZMAN, ANDREA VIDELA, ANTHONY ATTI, + +[CRED148] +ANTHONY RIVERA, BIJAN SHAMS, BLAYNE PERRY, BRETT BISOGNO, BREYE MATA, BRIAN PANEN, + +[CRED149] +BROCK VODER, CAREY BERTINI, CHARISSE LAMBERT, CHRIS DIFAT, CHRIS REISENBERGER, + +[CRED150] +CHRISTOPHER BRODAY, CHRISTOPHER CARRO, CYNTHIA GREENE, DAMARIES LOPEZ, DAN LEE, + +[CRED151] +DAN SCHNEIDER, DAN TOYAMA, DAVID DEAN CHALTFIELD, JR., DAVID HARRISON, DAVID WILEY, + +[CRED152] +DEBORAH COLLINS, DEBRANDA CHANEY-GILES, DEMETRA KOUKOULAS, DENISE ROSADO, + +[CRED153] +DEVIN BENNETT, DEVIN WINTERBOTTOM, DORIS WOO, DOUGLAS HARRISON, DUNCAN COUTTS, + +[CRED154] +DUPE AJAYI, EDWIN AVELLANEDA, ELIZABETH HOWELL, ELIZABETH SATTERWHITE, ERIC NAGLE, + +[CRED155] +ESTEBAN KARPLUS, F. FONT, FUTABA HAYASHI, GENE HILGREEN, GERALD COSGROVE, + +[CRED156] +GERARD LUNA, GILLIAN TELLING, GREGG CARLUCCI, GREGORY CLERVOIX, GREGORY SCHWEIZER, + +[CRED157] +HADLEY TOMICKI, J. ROSSETT, JAMEEL VEGA, JASON JONES, JEFF ROSA, JENNIFER JEMISON, + +[CRED158] +JEREMY TAGGERT, JESSICA RIDER, JOSEPH GREENE,JOSEPH HOWELL, KATE DUKICH, + +[CRED159] +KEL O'NEILL, KEVIN HOPKINS, LADAWN JAMES, LANCE WILLIAMS, LAURA BUBBLES, + +[CRED160] +LAURA PATTERSON, LEE CUMMINGS, LETICIA L. YOUNG, LINDSAY KENNEDY, LISA ORITZ, + +[CRED161] +LORNA JORDAN, LUCIO AMADIO, MARCO FERNANDEZ, MARIKO TANAKA, MARLON MATTHEWS, + +[CRED162] +MARY TELLING, MASAYOSHI MITSUYAMA, MATTHEW CHUNG, MAX ALLSTADT, MAX BOGDANOV, + +[CRED163] +MELISSA ALVAREZ, MICHAEL MAY, MICHAEL ROTHSTEIN, MIGUEL VIDAL, MIKE FEDERLINE, + +[CRED164] +NATALIE DESCALZO, N'GAI MEMBERS, NICOLAS MALLO, NOELLE SADLER, NORBERT MORIVAN, + +[CRED165] +OSWALD GREENE, JR., PETER MCKAY, PETER APPEL, PRESTON SAVARESE, RAFAEL GONZALES, + +[CRED166] +RANDY JOHNSON, REY CONCEPCION, RICHARD KROGER, ROB TIBBS, ROBERT JACKSON, + +[CRED167] +ROBERT SCHULER, ROSS A. MCINTYRE, RUSSELL FOREMAN, RUTH NUNEZ, SALVADORE SUAZO, + +[CRED168] +SAM WHITE, SANTOS GONZALES, SCOTT SMITH, SEYMOUR FRAILMAN, SPELMAN BRAUMAN, + +[CRED169] +STEPHANIE TELLING, STEVE KNEZEVICH, STEVE ROBERT, SUMIKO YASUDA, SUSAN LEWIS, + +[CRED170] +SYLVIA COLACIOS, TOMOKO MIYAZAKI, TRON, VERDEL HALE, YVES MONDESIR, ZENO LEINFELDER, + +[CRED171] +DAVID BEDDOES, CHRISTINE CHALMERS, BARRY CLARK, NEIL CORBETT, KIM GURNEY, NEIL MEIKLE, + +[CRED172] +CASSIE OLIVER, LORRAINE ROY, DAVID WATSON, KEVIN WONG, WILL MORTON + +[CRED175] +ADAM DAVIDSON + +[CRED176] +LANCE WILLIAMS + +[CRED177] +NEIL MCCAFFREY + +[CRED178] +LAURA PATERSON + +[CRED179] +REY CONCEPCION + +[CRED180] +CHARLES HEROLD + +[CRED181] +ANDREW GREENWALD + +[CRED182] +JAMES MIELKE + +[CRED183] +PETER SUCIU + +[CRED184] +ALEX ODULIO + +[CRED185] +DON NKRUMAH + +[CRED186] +KENDALL PITTMAN + +[CRED187] +SAL SUAZO + +[CRED188] +EREK MATEO + +[CRED189] +CHRIS DIFATE + +[CRED190] +LEILA MILTON + +[CRED191] +DARREN ZOLTOWSKI + +[CRED192] +VIRGINIA SMITH + +[CRED193] +KEVIN CASSIN + +[CRED194] +JASON SHIGEMORI + +[CRED195] +KELLY KINSELLA + +[CRED196] +MOLLIE STICKNEY + +[CRED197] +STANTON SARJEANT + +[CRED198] +LAURA WALSH + +[CRED199] +MARK GARONE + +[CRED200] +JOANNA SLY + +[CRED201] +ELIZABETH HOWELL + +[CRED202] +ANA HERCULES + +[CRED203] +SHIRLEY IRICK + +[CRED204] +KASHONA FIELDS + +[CRED205] +JOEL M. LILJE + +[CRED206] +JOHN DIBENEDETTO + +[CRED207] +NANCY GILES + +[CRED208] +RYAN CROY + +[CRED209] +JENNIFER KOLBE + +[CRED210] +LIAM BURKE + +[CRED211] +SIGRID PREISSL + +[CRED212] +ANITA FITZSIMONS + +[CRED213] +PHILIPPA RASELLI + +[CRED214] +WIL QUESNEL + +[CRED215] +FALKO BURKERT + +[CRED216] +SARA SEWELL + +[CRED217] +RADIO STATIONS AND MUSIC + +[CRED218] +MUSIC CONSULTANCY + +[CRD218A] +HEINZ HENN + +[CRD218B] +STUART ROSS + +[CRED219] +SOUNDTRACK CO-ORDINATOR + +[CRED220] +TERRY DONOVAN + +[CRED221] +PRODUCER FOR ROCKSTAR GAMES + +[CRED222] +DAN HOUSER AND LAZLOW + +[CRED223] +PRODUCER FOR ROCKSTAR NORTH + +[CRED224] +CRAIG CONNER + +[CRED225] +ALLAN WALKER + +[CRED226] +LAZLOW + +[CRED227] +DJ BANTER AND IMAGING + +[CRED228] +WRITTEN BY DAN HOUSER AND LAZLOW + +[CRED229] +FLASH FM + +[CRD229A] +TONI-MARIA CHAMBERS + +[CRD229B] +IMAGING VOICE AND PRODUCTION-JEFF BERLIN + +[CRED230] +SPECIAL THANKS TO + +[CRED231] +TOMMY MOTTOLA, + +[CRED232] +MICHELLE ANTHONY, + +[CRED233] +STEVE BARNETT, + +[CRED234] +CHUCK FLECKENSTEIN, + +[CRED235] +RITA LIBERATOR + +[CRED236] +MARTIN & CLAIRE LOGAN + +[CRED237] +SANDRA HUTTON + +[CRED238] +CHRISTINE DAVIDSON + +[CRED239] +ALAN, RED & BIGFOOT + +[CRED240] +LE T + +[CRED241] +COLIN DONALD + +[CRED242] +KERRY STALLWOOD + +[CRED243] +ALAN MCGREGOR + +[CRED244] +CHRIS MORTON + +[CRED245] +EMIL BUSSE + +[CRED246] +EMILY BAILLIE + +[CRED247] +KEVIN ARCHIBALD + +[CRED248] +MORAG KERR + +[CRED249] +CATH WALKER + +[CRED250] +ISO BAR + +[CRED251] +WATERLINE + +[CRED252] +NEWS CAFE + +[CRD251A] +THE POND + +[CRD252A] +PIVO + +[CRED253] +BUDGET VIDEO RENTALS + +[CRED254] +LORNA'S SCOOTER + +[CRED255] +GARETH MURFIN + +[CRED256] +ADDITIONAL ART + +[CRED257] +TONY PORTER + +[CRED258] +CRAIG MOORE + +[CRED259] +CUT SCENE LIP-SYNC ANIMATION + +[CRED260] +COSGROVE HALL FILMS + +[CRED261] +PRODUCER - OWEN BALLHATCHET + +[CRED262] +SENIOR ANIMATOR - JON TURNER + +[CRED263] +ANIMATORS - RICHARD DRUMM + +[CRED264] +DAVE BROWN + +[CRED265] +MAIR THOMAS + +[CRED266] +PRASHANT PATEL + +[CRED267] +AUDIO TECHNOLOGY CONSULTANT + +[CRED268] +RIK EDE FOR GAMESOUND LTD. + +[CRED269] +DTS INTEGRATION SUPPORT + +[CRED270] +TED LAVERTY FOR DTS + +[CRED271] +CHRIS GREER FOR DTS + +[CRED272] +JASON PAGE FOR SCEE + +[CRED273] +RESEARCH AND ANALYSIS + +[CRED274] +VROCK + +[CRED275] +DJ: LAZLOW AS HIMSELF + +[CRED276] +IMAGING VOICE-JOE KELLY + +[CRED277] +IMAGING PRODUCTION-JONATHAN HANST + +[CRED278] +WAVE 103 + +[CRED279] +DJ: ADAM FIRST-JAMIE CANFIELD + +[CRED280] +IMAGING VOICE-JEN SWEENEY + +[CRED281] +IMAGING PRODUCTION-JONATHAN HANST + +[CRED282] +FEVER 105 + +[CRED283] +DJ: OLIVER 'LADYKILLER' BISCUIT-JULIUS DYSON + +[CRED284] +IMAGING VOICE MALE-ED MCMANN + +[CRED285] +IMAGING VOICE FEMALE-SHAWNEE SMITH + +[CRED286] +IMAGING PRODUCTION- LISTEN KITCHEN + +[CRED287] +EMOTION 98.3 + +[CRED288] +DJ: FERNANDO- FRANK CHAVEZ + +[CRED289] +IMAGING VOICE-JEN SWEENEY + +[CRED290] +IMAGING PRODUCTION-JONATHAN HANST + +[CRED291] +RADIO ESPANTOSO + +[CRED292] +DJ: PEPE-TONY CHILRODES + +[CRED293] +WILDSTYLE + +[CRED294] +DJ: MISTER MAGIC AS HIMSELF + +[CRED295] +IMAGING VOICE-FRANK SILVESTRO + +[CRED296] +IMAGING PRODUCTION-LAZLOW + +[CRED297] +KCHAT + +[CRED298] +WRITTEN BY DAN HOUSER AND LAZLOW + +[CRED299] +PRODUCED AND EDITED BY LAZLOW + +[CRED300] +DJ AMY SHECKENHAUSEN -LEYNA WEBER + +[CRED301] +JEZ TORRENT-KEVIN MCKIDD + +[CRED302] +MANDY -COLLEEN CORBETT + +[CRED303] +MICHELLE CARAPADIS-MARY BIRDSONG + +[CRED304] +MR.ZOO-CARL DOWLING + +[CRED305] +GETHSEMANEE-LYNN LIPTON + +[CRED306] +CLAUDE MAGINOT-JOHN MAUCERI + +[CRED307] +BJ SMITH-LAWRENCE TAYLOR + +[CRED308] +THOR-FRANK FAVA + +[CRED309] +RADIO CALLERS + +[CRED310] +COUZIN ED, JOSH CLARK, JASON BUHRMESTER, JUAN ALLER, WAYNE OLIVER, SUSAN LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, KEITH BROADAS + +[CRED311] +LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, + +[CRED312] +DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, + +[CRED313] +KEITH BROADAS + +[CRED314] +VCPR + +[CRED315] +WRITTEN BY DAN HOUSER AND LAZLOW + +[CRED316] +PRODUCED BY LAZLOW + +[CRED317] +MAURICE CHAVEZ-PHILLIP ANTHONY RODRIGUEZ + +[CRED318] +JONATHAN FREELOADER- PATRICK OLSEN + +[CRED319] +MICHELLE MONTANIUS-KELLY GUEST + +[CRED320] +REP. ALEX SHRUB- CHRIS LUCAS + +[CRED321] +CALLUM CRAYSHAW- SEAN MODICA + +[CRED322] +JOHN F. HICKORY- LJ GANSEN + +[CRED323] +PASTOR RICHARDS- DAVID GREEN + +[CRED324] +JAN BROWN- MAUREEN SILLIMAN + +[CRED325] +BARRY STARK- RENAUD SEBBANE + +[CRED326] +JENNY LOUISE CRAB- MARY BIRDSONG + +[CRED327] +KONSTANTINOS SMITH- KONSTANTINOS.COM + +[CRED328] +JEREMY ROBARD-PETER SILVESTRO + +[CRED329] +RADIO COMMERCIALS + +[CRED330] +WRITTEN BY DAN HOUSER AND LAZLOW + +[CRED331] +PRODUCED BY LAZLOW + +[CRED332] +ADDITIONAL JINGLES PRODUCED BY CRAIG CONNER + +[CRED333] +COMMERCIAL VOICES: + +[CRED334] +ADAM DAVIDSON, ALEX ANTHONY, ALICE SALTZMAN, AMY SALZMAN, KATE DUKICH, + +[CRED335] +ARAN RONICLE, BARB JONES, BEN KRECH, BRIAN THOMAS, BROCK YODER, CHRIS + +[CRED336] +FERRANTE, CRAIG CONNER, DAVE RYAN, DAVID GREEN, DORIS WOO, DOUGLAS + +[CRED337] +HARRISON, ED MCMANN, FRANK CHAVEZ, FRANK FAVA, GENE HILGREEN, GREG + +[CRED338] +SCHWEIZER, HUNTER PLATIN, JAMES FERRANTE, JEFF BERLIN, JEFF ROSA, JOE KELLY, + +[CRED339] +JOHN MAUCERI, JOSH CLARK, JULIE WEMYSS, KEVIN STRALEY, KIM GURNEY, LANCE + +[CRED340] +WILLIAMS, LAURA PATERSON, LAZLOW, LISA ORTIZ, LORNA JORDAN, LUCIEN JONES, + +[CRED341] +MAUREEN SILLIMAN, MIKE FERRANTE JR., PETE GUSTIN, PETER SILVESTRO, RAFF + +[CRED342] +CROLLA, RANDY JOHNSON, RICHARD KRUGER, RON REEVE, SHELLEY MILLER, SKY, TJ ALLARD + +[CRD344A] +AUDIO RECORDED AT DIGITAL ARTS STUDIOS, + +[CRED344] +NYC, TRACK 9 STUDIOS, NYC, + +[CRED345] +WEDDINGTON MULTIMEDIA, LOS ANGELES, + +[CRD345A] +SYNC SOUND, NYC AND RADIO LAZLOW, LONG ISLAND. + +[CRED346] +THANKS TO AXEL ERICSON AND WON LEE AT DIGITAL ARTS, PAUL VASQUEZ AT TRACK 9 STUDIOS, JOHN BOWEN AND JOHN HASSLER AT SYNC SOUND + +[CRED347] +MARK LLOYD + +[CRED348] +TIM BATES + +[CRED349] +KIT BROWN + +[CRED350] +ANDY MASON + +[CRED351] +PHIL DEANE + +[CRED352] +PHIL ALEXANDER + +[CRED353] +MATT HEWITT + +[CRED354] +DENBY GRACE + +[CRED355] +ANTOINE CABROL + +[CRED356] +JONOTHAN STONES + +[CRED357] +MIKE BLACKBURN + +[CRED358] +TIM MCGAFF + +[SUNSHIN] +Sunshine Autos + +[CHERRYP] +Cherry Popper Icecreams + +[KAUFCAB] +Kaufman Cabs + +[BOATYAR] +The Boatyard + +[WANT_L] +Your wanted level has been suspended, if you commit a crime whilst the stars are flashing your full wanted level will be reinstated. + +[PICK1] +Body Armor delivered to Ocean View Hotel! + +[HOTRNG] +HOTRING + +[BLODRNG] +BLOODRING + +[DIRTRNG] +DIRTRING + +[FEC_ABR] +Accelerate, Brake or Reverse + +[FEI_BTU] +; = - + +[FEI_SCR] +Scroll + +[SKUMBUY] +Skumole Shack purchased: $~1~ + +[SKUM_L] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Skumole Shack for $~1~ + +[SKUM_T] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Skumole Shack for $~1~ + +[SKUM_C] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Skumole Shack for $~1~ + +[LG_35] +Destination + +[BOAT_AS] +~g~The Boatyard will now generate revenue up to a maximum of $~1~. Make sure you collect it regularly. + +[BOAT_A2] +BOATYARD ASSET COMPLETED + +[BOAT_N] +Checkpoint Charlie + +[BOAT_P] +~g~collect the packages before the time runs out. + +[FEI_R1B] +R1 \ R2 button - + +[HELP9_A] +Press the~h~ ~k~~PED_FIREWEAPON~ button ~w~to ~h~fire~w~ the sniper rifle. + +[HELP21] +Press the ~h~~k~~VEHICLE_ENTER_EXIT~ ~w~button to enter or exit a vehicle. + +[CREAM] +Distribution + +[UMBERTO] +Cafe Robina + +[PU_CF1] +Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to pick up this weapon. It will replace any weapon you have of the same type. + +[FED_RDM] +MAP & BLIPS + +[FEC_ILU] +Invert Look in 1st Person + +[NITRO] +All taxi's now have a boost jump! Just press the horn button. + +[RATNG53] +Untrustworthy + +[RATNG54] +Embarrassment + +[RATNG55] +Hacker + +[RATNG56] +Cheater + +[RATNG57] +Total Liar + +[STHC_04] +Highest score with Keepie-Uppy beach ball + +[STHC_05] +Hotring Best Result + +[STFT_13] +Fastest time on Downtown Chopper Checkpoint + +[STFT_14] +Fastest time on Ocean Beach Chopper Checkpoint + +[STFT_15] +Fastest time on Vice Point Chopper Checkpoint + +[STFT_16] +Fastest time on Little Haiti Chopper Checkpoint + +[STFT_21] +Fastest time in Hotring + +[STFT_22] +Fastest lap time in Hotring + +[STFT_20] +Fastest time for 'Cone Crazy' + +[HELP44] +Stop in the ~q~pink marker. + +[HELP45] +Press the ~h~~k~~PED_DUCK~~w~ button to duck. This will increase the accuracy of guns you are holding. + +[RCR1_5] +RC Bandit Race + +[RCPL1_7] +RC Baron Race + +[RCH1_11] +RC Raider Pickup + +[FEA_CTD] +Warning! This feature requires DTS compatible hardware to be connected. Proceed? + +[FEM_STE] +USE STEREO + +[GREET] +Greetings from... + +[LANCE_1] +Come on man, drive more careful! + +[LANCE_2] +Hey watch what you're doin! + +[LANCE_3] +Hey where are we goin' now? + +[LANCE_4] +What are we doin' now? + +[LAW4_15] +More money! + +[MERC_5] +Nice car, Mr. Vercetti. + +[MERC_26] +FASTER, FASTER, FASTER! + +[MERC_27] +Careful, Tommy, I only have a nose job last month. + +[MERC_28] +Tommy, drive careful. + +[MERC_29] +Tommy, go slower. + +[MERC_30] +Tommy, please kill someone other than me. + +[MERC_31] +Tommy, baby, don't kill me! + +[MERC_32] +Tommy, I'm glad you stole this car! + +[MERC_40] +I had so much fun. + +[MERC_43] +Adios, angel. + +[MERC_44] +You keep working out, now, you hear. + +[MERC_45] +Ciao, beautiful. + +[COL5_17] +Oh my god they've got a helicopter! + +[COL5_18] +Shoot the helicopter! + +[COL5_19] +Tommy, take that chopper out! + +[COL5_20] +He's coming again! Blow that chopper! + +[COL5_21] +Look at the size of that chopper! + +[COL5_22] +Here he comes again! + +[FEA_DSM] +Warning! This savegame is set to use DTS. This requires DTS compatible hardware to be connected. Please select whether you want to proceed using DTS or STEREO output. + +[STFT_23] +Fastest time for Checkpoint Charlie + +[HELP50] +Press the ~h~~k~~PED_ANSWER_PHONE~~w~ button to set the camera behind you. + +[HELP51] +Press the ~h~~k~~PED_ANSWER_PHONE~~w~ button to set the camera behind you. + +[HELP52] +Press the ~h~~k~~PED_ANSWER_PHONE~~w~ button to set the camera behind you. + +[HELP53] +Press ~h~~k~~PED_CYCLE_WEAPON_LEFT~~w~ button or ~h~~k~~PED_CYCLE_WEAPON_RIGHT~~w~ button to cycle through your available weapons. + +[HELP46] +There are eight different types of weapon. + +[HELP47] +You can carry one of each type of weapon at a time - one type of pistol, one type of shotgun. + +[HELP54] +~w~Cost: $~1~ ~r~Buying this will replace your current weapon. + +[HELP2A2] +Press the ~h~~k~~PED_SPRINT~~w~ button when running to ~h~sprint + +[HLPSN_A] +The sniper rifle allows you to zoom in and fire accurately at targets from a distance. + +[HLPSN_B] +Press and hold the~h~ ~k~~PED_LOCK_TARGET~ ~w~button to ~h~target~w~ with the sniper rifle. + +[HLPSN_C] +Press and hold the~h~ ~k~~PED_LOCK_TARGET~ ~w~button to ~h~target~w~ with the sniper rifle. + +[HLPSN_D] +Press the ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ button ~w~to ~h~zoom in ~w~with the rifle and the~h~ ~k~~PED_SNIPER_ZOOM_OUT~ button ~w~to ~h~zoom out ~w~again. + +[HLPSN_E] +Press the ~h~~k~~PED_FIREWEAPON~~w~ button ~w~to ~h~fire~w~ the sniper rifle. + +[HLPSN_F] +Press the ~h~~k~~PED_FIREWEAPON~~w~ button ~w~to ~h~fire~w~ the sniper rifle. + +[HLPSN_G] +Press the ~h~~k~~PED_FIREWEAPON~~w~ button to ~h~fire~w~ the sniper rifle. + +[PLANE_H] +Use the ~h~~k~~VEHICLE_ACCELERATE~~w~ button to accelerate, Left and right to turn. + +[PLANE_4] { reVC update } +{ Use the ~h~~k~~VEHICLE_ACCELERATE~~w~ button to accelerate, Left and right to turn. } +Use the right analog stick to accelerate, pull back on the left analog stick to climb, push forwards to descend. Left and right to turn. + +[HELP55] +Press the ~h~~k~~PED_FIREWEAPON~~w~ button to attack the chef. + +[STPR_8] +Pole Position Club + +[STPR_9] +3321 Vice Point + +[STPR_10] +Links View Apartment + +[STPR_11] +El Swanko Casa + +[STPR_12] +1102 Washington Street + +[STPR_13] +Ocean Heights Apartment + +[STPR_14] +Skumole Shack + +[STPR_15] +Hyman Condo + +[RCCANX] +~r~RC plane cancelled. + +[CLT_HL2] +When a Clothes Pickup is collected, a one star or two star wanted level will be cleared. + +[CRED009] +MISSION DESIGN + +[CRED359] +LEE JOHNSON + +[CRED360] +HENDRIK LESSER + +[CRED361] +PASQUALE STACCHIOTTI + +[CRED362] +ENRIQUE FERNANDEZ + +[CRED363] +PAUL BYERS + +[CRED364] +MIKE EMENY + +[CRED365] +ROB DUNKIN + +[CRED366] +CHARLIE KINLOCH + +[CRED367] +KEVIN HOBSON + +[CRED368] +JIM CREE + +[MOB_66A] +Tommy, Tommy why you coming back here for? + +[MOB_66B] +I tell you we don't want to see you around here no more. + +[MOB_67A] +Tommy, me thinks you should be staying away, you hear? + +[MOB_67B] +The Haitian boys not too 'appy with you. + +[MOB_18A] +Tommy, it's Paulo, how are you? Right mate, anyway, thought I had to drop you a line. + +[MOB_18B] +Oh my good lord, my son, you will not believe the quality of the brass I just encountered. + +[MOB_18C] +Street walker or something, just down in Little Havana, mate. + +[MOB_18D] +Said her name was Mercedes or something. + +[MOB_18E] +Oh my god, mate. You gotta check this bird out. + +[MOB_18F] +Could strip the lead out of a pencil. Said I was the best she ever had and all. + +[MOB_18G] +Keep you potatoes skinned for her. Be seeing you. + +[MOB_72A] +Tommy, it's me, Lance. Keep your mouth shut there Tommy, 'cause I ain't got time to talk. + +[MOB_72B] +I ain't interested in what you got to say. Why should I be? You don't care about me, do you? + +[MOB_72C] +You gotta look after me a bit better. Give me a fair slice. You know... + +[MOB_72D] +Tommy... oh, look, man, I'm sorry. It's just that... + +[MOB_72E] +People patronize me all my life, treat me like a little kid. + +[MOB_72F] +My brother would do that. Please, man, don't do that. + +[MOB_72G] +I gotta go. + +[MOB_63A] +Tommy, it's Earnest. Earnest Kelly. + +[MOB_63B] +How are you? + +[MOB_63C] +I'm doing okay. I'll need a stick to walk, but I should be back at work soon enough. + +[MOB_63D] +Good. + +[MOB_63E] +I heard about Lance. What a little prick, eh? + +[MOB_63F] +Yes. + +[MOB_63G] +Never trust a man who walks the streets in his pajamas. That's what I say. Glad you killed him. I hope it was painful for the prick. + +[MOB_63H] +I think it was. I just didn't think he was like that... + +[MOB_63I] +Tommy, for a raging lunatic, you're pretty naive. I'll be back at work soon, teach you a thing or two about life, you hear. + +[MOB_63J] +Take your time, Earnest. Look after yourself. + +[MOB_16A] +Tommy, Paulo here, que pasa amigo? + +[MOB_16B] +What do you want Paul? I don't want any fake label clothes. + +[MOB_16C] +Very funny, mate, but you know I don't touch bent gear. Nah, I was just calling to see if I get a part in one your movies. + +[MOB_16D] +Back in England I did a lot of blue stuff, mate. I'm packing more heat than you, my son. + +[MOB_16E] +Paul, thanks for the offer, I'll bear it in mind. + +[MOB_16F] +Seriously, don't forget about me, after all I done for you. + +[MOB_16G] +That's what I'm trying to forget about. + +[MOB_17A] +Tommy Vercetti, how's it going, Mr. big shot? I hear all these things about you, some kind of player in town, now eh... + +[MOB_17B] +Paul, you're drunk. + +[MOB_17C] +Nah, you stupid prat, I ain't drunk. I only had a couple and some treats, ain't been to bed for a couple of days, you know. + +[MOB_17D] +Anyway, don't give me that. I ain't a mug. Who set you up in this town? Who? Me. That's who. + +[MOB_17F] +Really? + +[MOB_17G] +Don't give me that. Don't! I introduced you to people. I showed you the ropes, did a lot of stuff for you, and this is how you repay me. + +[MOB_17H] +You ignore me. You won't give me a way in, after all I did for you! What do you think I am? A div or something? + +[MOB_17I] +Paul, take it easy. I've been busy, don't be an idiot. + +[MOB_17J] +I ain't no idiot, mush. That's what they said in borstal. Are you asking for trouble son, because you're going to get it! + +[MOB_17K] +Tommy, mate. Please. You was my big hope! Please, don't laugh at me! + +[MOB_17L] +Paul, get some sleep, seriously. + +[MOB_73A] +Tommy, it's Steve. + +[MOB_73B] +Hey, Steve. + +[MOB_73C] +Hey indeed, genius. You're a marvel! I'm a marvel! They love us. We are re-writing the record books, pal. + +[MOB_73D] +We are talking major awards here. Finally, I can put my dad in a home an tell him to shut up. + +[MOB_73E] +Eeer, that's cool, Steve. + +[MOB_73F] +Cool? It's hot, man. Hot. H. O. T. He never believed in me. Never thought I was an artist, and now I've made it. + +[MOB_73G] +I'm the best damn skin flick director of all time, my friend. I just wanted to say, it's a pleasure to have met you. + +[MOB_73H] +Thanks steve. + +[MOB_73I] +I love you, baby. Don't go changing on me, you hear. + +[MOB_73J] +I hear you. Good bye, Steve. + +[BOLLOX] +Press the ~h~~k~~VEHICLE_HANDBRAKE~ ~w~button to drop a bomb. Press the ~h~~k~~VEHICLE_ENTER_EXIT~ ~w~button to cancel. + +[BRID_OP] +Storm warning over, all bridges to the mainland are now open. + +[BRID_CL] +Storm warning: All bridges to the mainland are closed. + +[LG_38] +Target + +[ASSET_C] +POLE POSITION ASSET COMPLETED! + +[ASSET_D] +~g~The Pole Position Club will now generate revenue up to a maximum of $~1~ per day. Pick up your cash regularly! + +[ST_WHEE] +Longest Wheelie time (secs) + +[ST_STOP] +Longest Stoppie time (secs) + +[ST_2WHE] +Longest 2 wheels time (secs) + +[ST_WHED] +Longest Wheelie distance (m) + +[ST_STOD] +Longest Stoppie distance (m) + +[ST_2WHD] +Longest 2 wheels distance (m) + +[OUTFT11] +Tracksuit + +[OUTFT12] +Frankie + +[RELOAD] +~g~You have won the fast reload ability! + +[APACHE] +Hunter delivered to helipad in Ocean Beach. + +[CRED369] +JOHN MCCARDLE + +[CRED370] +DAVID MURDOCH + +[CRED371] +CHRIS BROWN + +[CRED372] +PAUL GREEN + +[CRED373] +KYLE MILNE + +[CRED374] +KEVIN YUN + +[CRED375] +ERICK COBBS + +[CRED376] +RANDY BLAKE + +[CRED377] +BRANDON LIM + +[CRED378] +BRANDON FENOL + +[CRED379] +MICHAEL MANOLE + +[CRED380] +ALETHEIA SIMONSON + +[CRED381] +JOHN JANSEN + +[CUNTY] +New clothes delivered to the Vercetti Estate! + +[GOODBOY] +$50 Good Citizen Bonus! + +[NEWCONT] +New ~h~Contact Point ~w~opened at the marina in Ocean Beach!! + +[FIRELVL] +Fire Truck Mission level ~1~ + +[FEM_RET] +RETRY + +[FESZ_QR] +Are you sure you want to start a new game? All progress will be lost. Proceed? + +[SEG3_1] +TIME: + +[HELP56] +Press the ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~ button to change camera modes. + +[HELP57] +Press the ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~ button to change camera modes. + +[HELP58] +While targeting you can press the ~h~~k~~PED_CYCLE_TARGET_LEFT~ button or ~k~~PED_CYCLE_TARGET_RIGHT~ ~w~button to cycle through targets. + +[HELP59] +While targeting you can press the ~h~~k~~PED_CYCLE_TARGET_LEFT~ button or ~k~~PED_CYCLE_TARGET_RIGHT~ ~w~button to cycle through targets. + +[HELP60] +If you press the ~w~~k~~PED_SPRINT~ ~w~button while car jacking, you will not enter that vehicle. + +[HELP61] +You now have limitless ammo and double health on all vehicles. + +[FEC_LB1] +Look + +[FEC_LB2] +behind + +[FEC_LB3] +Look behind + +[FEC_R3] +(R3 button) + +[FEC_PED] +Controls On Foot + +[FEC_VEH] +Controls In Vehicle + +[FEC_FPR] +Controls For First Person + +[FEC_CMM] +Common Controls + +[FEC_PWL] +Go Left + +[FEC_PWR] +Go Right + +[FEC_PWF] +Walk Forward + +[FEC_PWT] +Walk towards camera + +[FEC_PLB] +Look Behind. + +[FEC_PFR] +Fire Weapon + +[FEC_CLE] +Cycle Weapon Left + +[FEC_CRI] +Cycle Weapon Right + +[FEC_LKT] +Lock Target + +[FEC_PJP] +Ped Jump + +[FEC_PSP] +Ped Sprint + +[FEC_PSH] +Ped Shoot + +[FEC_TLF] +Next Target To Left + +[FEC_TRG] +Next Target to Right + +[FEC_CCM] +Center Camera Behind player. + +[FEC_SZI] +Sniper Rifle Zoom In + +[FEC_SZO] +Sniper Rifle Zoom Out + +[FEC_LKL] +First Person Look Left + +[FEC_LRT] +First Person Look Right + +[FEC_LUP] +1st Person Look Up + +[FEC_LDN] +1st Person Look Down + +[FEC_LBH] +Look Behind Vehicle + +[FEC_LLF] +Look Left of Vehicle + +[FEC_LRG] +Look Right of Vehicle + +[FEC_HRN] +Horn + +[FEC_HBR] +Vehicle Handbrake + +[FEC_ACL] +Vehicle Accelerate + +[FEC_BRK] +Vehicle Brake + +[FEC_TSM] +Toggle SubMissions + +[FEC_CRD] +Change Radio Station + +[FEC_ENT] +Enter/Exit Vehicle + +[FEC_WPN] +Fire Weapon + +[FEC_PAS] +Pause + +[FEC_FPO] +1st Person Weapons + +[FEC_SMS] +Show mouse pointer + +[FEC_CMS] +Change camera mode all situations. + +[FEC_TSS] +Take Screen Shot + +[FEC_DBG] +DEBUG MENU + +[FEC_TGD] +Toggle Pad Game/Debug + +[FEC_TDO] +Turn Debug Camera Off + +[FEC_IVH] +Invert Mouse Horizontally: + +[FEC_MSL] +LMB + +[FEC_MSM] +MMB + +[FEC_MSR] +RMB + +[FEC_QUE] +??? + +[FEC_TWO] +Only Two Keyboard Keys Allowed + +[FEC_UMS] +Unique Mouse Keys only please. + +[FEC_OMS] +Only One Mouse Keys Allowed + +[FEC_UJS] +Unique Joystick buttons only please. + +[FEC_OJS] +Only One Joystick Buttons per action allowed + +[FEC_PTL] +Use LockTarget with Weapon Switch Left. + +[FEC_PTR] +Use LockTarget with Weapon Switch Right. + +[FEC_LBC] +Use Look Left With Look Right. + +[FEC_JBO] +JOY ~1~ + +[FEC_WAR] +Warning + +[FEC_OKK] +O.K. + +[FEC_DLF] +Delete Failed. + +[FEC_SVU] +Save Unsuccessful. + +[FEC_LUN] +Load Unsuccessful. File Corrupted, Please delete. + +[FEC_PAD] +Gamepad + +[FEC_JOY] +Joystick + +[FES_CSA] +Select a skin from the list below: + +[FET_HRD] +DEFAULT SETTINGS RESTORED + +[FET_MST] +MOUSE CONTROLLED STEERING + +[FEC_STR] +NUM STAR + +[FET_MIG] +LEFT, RIGHT, MOUSEWHEEL TO ADJUST + +[FET_CIG] +BACKSPACE TO CLEAR - LMB, RETURN TO CHANGE + +[FET_DSN] +Default Player Skin.bmp + +[FET_RSO] +ORIGINAL SETTING RESTORED + +[FET_RSC] +HARDWARE NOT AVAILABLE - ORIGINAL SETTING RESTORED + +[FEA_3DH] +AUDIO HARDWARE + +[FEA_SPK] +SPEAKERS CONFIGURATION + +[FEM_LOD] +DRAW DISTANCE + +[FEM_VSC] +FRAME SYNC + +[FEM_FRM] +FRAME LIMITER + +[FEM_MM] +MAIN MENU + +[FED_RES] +SCREEN RESOLUTION + +[FET_CTL] +CONTROLLER SETUP + +[FET_OPT] +OPTIONS + +[FEC_MSH] +MOUSE SENSITIVITY + +[FEC_IVV] +INVERT MOUSE VERTICALLY + +[FEC_FNC] +F~1~ + +[FEC_IRT] +INS + +[FEC_DLL] +DEL + +[FEC_HME] +HOME + +[FEC_END] +END + +[FEC_PGU] +PGUP + +[FEC_PGD] +PGDN + +[FEC_UPA] +UP + +[FEC_DWA] +DOWN + +[FEC_LFA] +LEFT + +[FEC_RFA] +RIGHT + +[FEC_NUM] +NUM + +[FEC_NMN] +NUM~1~ + +[FEC_FWS] +NUM / + +[FEC_PLS] +NUM + + +[FEC_MIN] +NUM - + +[FEC_DOT] +NUM . + +[FEC_NLK] +NUMLOCK + +[FEC_ETR] +ENT + +[FEC_SLK] +SCROLL LOCK + +[FEC_PSB] +BREAK + +[FEC_BSP] +BSPACE + +[FEC_TAB] +TAB + +[FEC_CLK] +CAPSLOCK + +[FEC_RTN] +RET + +[FEC_LSF] +LSHIFT + +[FEC_RSF] +RSHIFT + +[FEC_LCT] +LCTRL + +[FEC_RCT] +RCTRL + +[FEC_LAL] +LALT + +[FEC_RAL] +RALT + +[FEC_LWD] +LWIN + +[FEC_RWD] +RWIN + +[FEC_WRC] +WINCLICK + +[FEC_SPC] +SPC + +[WIN_TTL] +Grand Theft Auto VC + +[WIN_95] +Grand Theft Auto VC cannot run on Windows 95 + +[WIN_DX] +Grand Theft Auto VC requires at least DirectX version 8.1 + +[FET_EIG] +CANNOT SET A CONTROL FOR THIS ACTION + +[FET_DAM] +DYNAMIC ACOUSTIC MODELING + +[FEQ_SRE] +Are you sure you want to quit? All progress since the last save game will be lost. Proceed? + +[FEQ_SRW] +Are you sure you want to quit the game? + +[FET_QG] +QUIT GAME + +[FEN_STA] +START GAME + +[FET_PAU] +PAUSE MENU + +[REPLAY] +REPLAY + +[FEC_ANS] +Action + +[CVT_MSG] +Converting textures to optimal format for your video card + +[FEC_SFT] +SHIFT + +[FEH_VMP] +VIEW MAP + +[FES_DEE] +Delete Failed! Please try again. + +[FES_CMP] +Save failed! Please try again. + +[FESZ_WR] +Saving current game. Please wait... + +[FELD_WR] +Loading game. Please wait... + +[FEDL_WR] +Deleting saved game. Please wait... + +[PCRESRT] +Starting new game. Please wait... + +[FET_STI] +Standard Controls + +[FET_CTI] +Classic Controls + +[FET_PS] +PLAYER SKIN SETUP + +[FEH_NA] +OPTION NOT AVAILABLE + +[FEH_MPH] +MOUSE, CURSORS TO MOVE - PGUP, PGDN, MSWHEEL TO ZOOM, L - LEGEND + +[FEA_MP3] +MP3 PLAYER + +[NO_PCCD] +Please insert your GTA Vice City CD, or press ESC to cancel + +[FEH_SSA] +CURSORS TO MOVE - S TO SAVE TO FILE + +[FES_CMI] +LAST MISSION PASSED + +[FET_STS] +STATS SAVED TO 'STATS.HTML' + 'STATS.TXT' + +[WIN_VDM] +Grand Theft Auto VC cannot find enough available video memory + +[FEC_ERI] +Error! One or more control actions are not bound to a key or button. Please check all control actions are set + +[FEC_TFU] +Turret + Lean Up + +[FEC_TFD] +Turret + Lean Down + +[FET_RIG] +SELECT A NEW CONTROL FOR THIS ACTION + +[FEA_NM3] +NO MP3 FILES FOUND + +[FEA_MPB] +MP3 VOLUME BOOST + +[FEA_MUS] +MUSIC VOLUME + +[FEA_SFX] +SFX VOLUME + +[CVT_ERR] +You have run out of disk space. Please make some space on your harddisk before continuing. Press ESC to cancel. + +[FEA_ADP] +AUTO-DETECT HARDWARE + +{=================================== MISSION TABLE AMBULAE ===================================} + +[ATUTOR2:AMBULAE] +~g~Drive the patients to Hospital CAREFULLY. Each bump reduces their chances of survival. + +[A_FULL:AMBULAE] +~r~Ambulance full!! + +[A_FAIL2:AMBULAE] +~r~Your lack of urgency has been fatal to the patient! + +[A_FAIL3:AMBULAE] +~r~The patient is dead!! + +[A_PASS:AMBULAE] +Rescued! + +[A_COMP2:AMBULAE] +You will never get tired! + +[A_CANC:AMBULAE] +~r~Paramedic mission cancelled! + +[A_COMP3:AMBULAE] +Paramedic missions complete! You will never get tired when running! + +[ALEVEL:AMBULAE] +Paramedic Mission Level ~1~ + +[A_FAIL1:AMBULAE] +Paramedic mission ended. + +[A_SAVES:AMBULAE] +PEOPLE SAVED: ~1~ + +[A_COMP1:AMBULAE] +Paramedic missions complete: $~1~ + +{=================================== MISSION TABLE ASSIN1 ===================================} + +[ASM1_5:ASSIN1] +~r~Your target completed his deliveries! + +[ASM1_6:ASSIN1] +Deliveries left: + +[ASM1_7:ASSIN1] +~g~Carl Pearson, Pizza Delivery Man. Kill him before he completes his deliveries. + +[ASM1_A:ASSIN1] +Mr. Teal, your help in eradicating those out-of-towners was invaluable to business. I have more work for you with a more 'hands-on' approach. + +[ASM1_D:ASSIN1] +Mr. Teal, your help in eradicating those out-of-towners was invaluable to business. + +{=================================== MISSION TABLE ASSIN2 ===================================} + +[ASM2_1:ASSIN2] +~g~Mrs. Dawson will be leaving the Jewelry shop in Vice Point soon. Kill her. It must look like a car accident. + +[ASM2_3:ASSIN2] +~g~She's gonna blow! Get out of there! + +[ASM2_4:ASSIN2] +~r~You smashed up her car when she wasn't in it! She won't touch it now! + +[ASM2_5:ASSIN2] +~r~She got away! + +[ASM2_6:ASSIN2] +~r~You were too near the scene of the 'accident'! + +[ASM2_7:ASSIN2] +~r~It's supposed to look like an accident! Try ramming her off the road instead! + +[ASM2_8:ASSIN2] +~g~You must make Mrs. Dawson's death look like an accident. Do not use any weapons. + +[ASM2_9:ASSIN2] +~g~You need some wheels for this job! + +[ASM2_10:ASSIN2] +~g~When her car bursts into flames make your self scarce! + +[ASM2_11:ASSIN2] +Help me! + +[ASM2_12:ASSIN2] +Somebody help me! + +[ASM2_13:ASSIN2] +Oh my god! + +[ASM2_A:ASSIN2] +My compliments on a job well done Mr. Teal. My client was very pleased. + +[ASM2_2:ASSIN2] +health: + +{=================================== MISSION TABLE ASSIN3 ===================================} + +[ASM3_11:ASSIN3] +TIME: + +[ASM3_C:ASSIN3] +A European gang plans to hit a bank in Vice City. My employers would rather this didn't happen. + +[ASM3_D:ASSIN3] +Each member of the gang has a cover while they are here in Vice City. Some have menial jobs, others are on vacation. + +[ASM3_E:ASSIN3] +Each target and their likely whereabouts are taped under the phone. + +[ASM3_14:ASSIN3] +~g~Dick Tanner is working for DBP Security in Ocean Drive. + +[ASM3_15:ASSIN3] +~g~Marcus Hammond and Franco Carter are located near the Jewelry shop in Vice Point. + +[ASM3_16:ASSIN3] +~g~Nick Kong is cruising off Washington Beach. + +[ASM3_18:ASSIN3] +~g~Don't get too close to the target or he may spot you! + +[ASM3_19:ASSIN3] +~g~He's seen you! Waste him! + +[ASM3_20:ASSIN3] +~g~They have seen you! Make sure you waste them both! + +[ASM3_21:ASSIN3] +~r~You did not kill all the gang members in time! + +[ASM3_22:ASSIN3] +~g~Don't get too close to the targets or they may spot you! + +[ASM3_12:ASSIN3] +~g~A selection of weapons have been left for you nearby if you require them. You have ~h~9 MINUTES ~g~to kill all members of the gang. + +[ASM3_13:ASSIN3] +~g~Mike Griffin is working on an advertising board in Washington. + +[ASM3_17:ASSIN3] +~g~Charlie Dilson is riding in Washington. + +{=================================== MISSION TABLE ASSIN4 ===================================} + +[ASM4_10:ASSIN4] +~g~Looks like you're not the only one after the briefcase! Get it to Ammu-Nation, quick time! + +[ASM4_12:ASSIN4] +Distance: + +[ASM4_15:ASSIN4] +~g~Get the sniper rifle to your right. + +[ASM4_16:ASSIN4] +~g~Watch the woman on the balcony, she will walk down the escalators and ask someone the time. + +[ASM4_17:ASSIN4] +~g~Once the conversation has finished kill the person she spoke to, but do not kill her. + +[ASM4_18:ASSIN4] +~g~Once the target is dead retrieve his briefcase and take it to Ammu-Nation in Downtown. + +[ASM4_19:ASSIN4] +~g~Keep your distance from the target! Use the distance bar in the upper right corner of the screen. + +[ASM4_20:ASSIN4] +~g~If it gets full he will see you. + +[ASM4_21:ASSIN4] +~g~Get the briefcase! + +[ASM4_22:ASSIN4] +~g~Take the briefcase to Ammu-Nation in Downtown. + +[ASM4_23:ASSIN4] +~g~He has spotted you! Nail him and get the briefcase! + +[ASM4_25:ASSIN4] +~r~You killed the woman you fool! + +[ASM4_26:ASSIN4] +~r~The target has boarded his flight! + +[ASM4_27:ASSIN4] +~r~The target has seen you! You should have kept your distance! + +[ASM4_28:ASSIN4] +~r~The target heard you firing your weapon! + +[ASM4_29:ASSIN4] +~r~Itchy trigger finger? You killed him too soon! + +[ASM4_A:ASSIN4] +Time to fry bigger fish, Mr. Teal. There's a rifle in the foliage to your right. + +[ASM4_B:ASSIN4] +Watch the woman standing on the balcony above the check-in desks. She will walk through the crowd and ask someone the time. + +[ASM4_C:ASSIN4] +You must kill that person, retrieve his case and take it to the location taped under the phone. + +{=================================== MISSION TABLE ASSIN5 ===================================} + +[ASM5_A:ASSIN5] +There is a valuable exchange taking place on the roof of the Cherry Popper Ice Cream Company. + +[ASM5_B:ASSIN5] +Kill everyone involved, steal the merchandise and take it to the helipad at the airport. + +[ASM5_C:ASSIN5] +There is a gate to your left that leads to the back of the factory. + +[ASM5_1:ASSIN5] +~g~Enter the compound behind the Cherry Popper Ice Cream Company The deal is taking place on the roof. + +[ASM5_2:ASSIN5] +~g~Get the merchandise and take it to the helipad at the airport! + +[ASM5_3:ASSIN5] +~g~Take the merchandise to the helipad at the airport! + +{=================================== MISSION TABLE BANKJ1 ===================================} + +[WANTED1:BANKJ1] +~g~Shake the cops and lose your wanted level! + +[BJM1_A:BANKJ1] +Tommy! Hey, Tommy, look at this, this is great! I've got us this minibar installed! + +[BJM1_B:BANKJ1] +We got a whole bar downstairs, Ken. + +[BJM1_C:BANKJ1] +Yeah, yeah, whatever. Well, I got the chalkboard you asked for. + +[BJM1_D:BANKJ1] +Ah, that's the benefit of a law school education: the ability to follow instructions. + +[BJM1_E:BANKJ1] +Now, I need a safe man. + +[BJM1_F:BANKJ1] +Oh, all right, well, let me think...safe, safe, safe, safe - I got it! This guy will blow you away! + +[BJM1_G:BANKJ1] +Ahh, nah, that schmuck. He's on the inside. + +[BJM1_H:BANKJ1] +Where inside? + +[BJM1_I:BANKJ1] +In a police headquarter cell awaiting transfer. + +[BJM1_J:BANKJ1] +I think he's about to get paroled.... + +[BJM1_1:BANKJ1] +~g~Break Cam Jones out of police custody! + +[BJM1_3:BANKJ1] +~g~You will find something useful in the station's locker room. + +[BJM1_21:BANKJ1] +~g~The key card to the cells can be found upstairs in the station. + +[BNK1_7:BANKJ1] +Cam Jones? + +[BNK1_8:BANKJ1] +I'm busting you out! + +[BNK1_10:BANKJ1] +Yeah, that's me.. + +[BNK1_11:BANKJ1] +Whatever you say! + +[BNK1_13:BANKJ1] +I'm gonna be doing a job and you're my safe cracker. + +[BNK1_14:BANKJ1] +Beats losing my ass in a cell! + +[BJM1_22:BANKJ1] +~g~Get Cam back to his house! + +[BJM1_23:BANKJ1] +~g~You need to get the key card first! + +[BNK1_12:BANKJ1] +Lose the heat and get me back to my place! + +[BJM1_20:BANKJ1] +Put the weapon away or face the consequences! + +[BJM1_5:BANKJ1] +Only authorized personnel beyond this point! + +[BJM1_2:BANKJ1] +~r~You were supposed to bust Cam out, not get him killed! + +[BJM1_4:BANKJ1] +He's armed! Kill him! + +{=================================== MISSION TABLE BANKJ2 ===================================} + +[BJM2_A:BANKJ2] +We need a stick up man. You know one? + +[BJM2_B:BANKJ2] +Hey, Tommy, Tommy, Tommy, this stuff keeps you sharp, man. + +[BJM2_C:BANKJ2] +WoooOOOooo! + +[BJM2_D:BANKJ2] +I could be your stick up man! Stick 'em up! Stick 'em up! + +[BJM2_E:BANKJ2] +You ain't a stick up man, you're an idiot. + +[BJM2_F:BANKJ2] +Now get yourself a drink and shut up. + +[BJM2_G:BANKJ2] +Hey, get outta my way! Yeh yeh yeh - ow ow ow! + +[BJM2_H:BANKJ2] +Cam, what do you think? + +[BJM2_I:BANKJ2] +Well, the best shooter in this town is a guy named Cassidy. + +[BJM2_J:BANKJ2] +Is that so? + +[BJM2_K:BANKJ2] +Yeah. A military guy, or he thinks he is. + +[BJM2_L:BANKJ2] +I doubt he was ever in the army, but he certainly knows how to get a hold of guns. + +[BJM2_M:BANKJ2] +He'll be down at the shooting range. + +[BJM2_2A:BANKJ2] +You Phil Cassidy? + +[BJM2_2B:BANKJ2] +Why? + +[BJM2_2C:BANKJ2] +I'm looking for a man who can handle a gun. From this setup, I'm not too convinced. + +[BJM2_2D:BANKJ2] +Son, I could shoot a fly off your head at 80 feet. + +[BJM2_2E:BANKJ2] +Oh really? + +[BJM2_2F:BANKJ2] +Yeah. I learnt in the army. + +[BJM2_2G:BANKJ2] +Fly shooting real popular in the army? Glad I don't pay tax. + +[BJM2_2H:BANKJ2] +You tryin' to be funny kid? + +[BJM2_2I:BANKJ2] +Ha ha ha ha ha! + +[BJM2_2J:BANKJ2] +Let's shoot. + +[BJM2_1:BANKJ2] +~g~Go to Ammu-Nation in Downtown and talk to Phil Cassidy. + +[BJM2_3:BANKJ2] +HIT RATE: ~1~% + +[BJM2_4:BANKJ2] +SCORE ROUND ONE: ~1~ + +[BJM2_6:BANKJ2] +SCORE ROUND TWO: ~1~ + +[BJM2_7:BANKJ2] +TOTAL SCORE FOR SHOOT: ~1~ + +[BJM2_9:BANKJ2] +~g~Get to round two's starting point. + +[BJM2_11:BANKJ2] +~r~Phil's dead! + +[BJM2_12:BANKJ2] +~r~One of the shooters is dead! + +[BJM2_14:BANKJ2] +~g~Move on to the next area! + +[BJM2_15:BANKJ2] +SCORE: + +[BJM2_17:BANKJ2] +~g~Go and talk to Phil. + +[BJM2_18:BANKJ2] +SCORE TO BEAT: + +[BJM2_19:BANKJ2] +~g~Hit as many targets as you can in the time limit! + +[BJM2_22:BANKJ2] +~r~You have left the shooting range! + +[BJM2_23:BANKJ2] +~g~If you leave the shooting range during the competition, you will fail the mission. + +[BJM2_24:BANKJ2] +~g~The closest target is worth one point. + +[BJM2_25:BANKJ2] +~g~The middle target is worth two points. + +[BJM2_27:BANKJ2] +~g~All targets this round are worth one point. + +[BNK2_2:BANKJ2] +AIM 3-2-1 FIRE! + +[BNK2_3:BANKJ2] +AREA CLEAR! + +[BNK2_4:BANKJ2] +Hoooeee! + +[BNK2_5:BANKJ2] +Couldn't hit a barn door! + +[BNK2_7:BANKJ2] +So you wanna do me a favor, and help me put together a job? + +[BNK2_8:BANKJ2] +Son, after shooting like that, if you asked me to be your wife, I'd say yes. + +[BNK2_9A:BANKJ2] +Son, you better get your fancy talking and big ideas and shove 'em where there ain't no sun. You can't shoot nothin'! + +[BNK2_9B:BANKJ2] +You can't shoot nothin'. + +[BJM2_28:BANKJ2] +SCORE ROUND THREE: ~1~ + +[BJM2_20:BANKJ2] +~g~When you run out of ~w~time ~g~or ~w~ammunition ~g~the round is over! + +[BJM2_26:BANKJ2] +~g~The far target is worth three points. + +[BNK2_1:BANKJ2] +LIVE AMMUNITION + +[RANGE_1:BANKJ2] +SCORE FOR SHOOT: ~1~ + +[BJM2_2:BANKJ2] +~g~To exit the round press the ~h~~k~~PED_JUMPING~ ~g~button. + +[BJM2_N:BANKJ2] +Relax + +{=================================== MISSION TABLE BANKJ3 ===================================} + +[BJM3_A:BANKJ3] +Things are starting to come together nicely here. + +[BJM3_B:BANKJ3] +What's the plan, Tommy? Que pasa, amigo? + +[BJM3_C:BANKJ3] +The plan is you keep doing that like a moron. Anyhow, we need a driver. + +[BJM3_D:BANKJ3] +Tommy, I'll do it. I can drive. + +[BJM3_E:BANKJ3] +You want Hilary, mister. Not some smart-talking law school chump. + +[BJM3_F:BANKJ3] +Hilary's the real deal. You ain't never seen anyone drive so fast. I'll give him a call here. + +[BJM3_G:BANKJ3] +Hey Hil, it's Phil. How's it going? No. don't talk. We'll reminisce later. You want to do me a favor? + +[BJM3_H:BANKJ3] +I got me a guy from up north. No, no, I don't think he was in the service, but he wants a driver. + +[BJM3_I:BANKJ3] +For a bit of action. Okay, I understand. + +[BJM3_J:BANKJ3] +What'd he say? + +[BJM3_K:BANKJ3] +Well, he'll do it, no problem. Well, there might be a little problem - see, he has abandonment issues. + +[BJM3_L:BANKJ3] +Seems he won't work for anyone who can't beat him. Something to do with his momma. + +[BJM3_M:BANKJ3] +Anyway, he wants to race you first, said he'd meet you outside.. + +[BJM3_2A:BANKJ3] +You Tommy? Of course you're Tommy, I mean, + +[BJM3_2B:BANKJ3] +Why else would anyone want to speak to me? + +[BJM3_2C:BANKJ3] +OK. Consider it this way - + +[BJM3_2D:BANKJ3] +I'll drive for you IF, and only IF, you can drive properly. + +[BJM3_2E:BANKJ3] +Leave me alone - and I'll never forgive you. + +[BJM3_2:BANKJ3] +~r~Hilary is dead! + +[BJM3_4:BANKJ3] +~g~You need a car to take part! + +[BNK3_1:BANKJ3] +Ok. I'll drive for you, but please, treat me bad. + +[BNK3_3A:BANKJ3] +Illegal street race in progress at Vice Point. + +[BNK3_3B:BANKJ3] +Calling all officers. + +[BNK3_3C:BANKJ3] +Street racers, this is illegal and forbidden! + +{=================================== MISSION TABLE BANKJ4 ===================================} + +[BNK4_A:BANKJ4] +~w~As you can see, gentlemen, this is going to be the easiest buck we ever made. + +[BNK4_B:BANKJ4] +~w~Tommy, seriously, you gotta consider going into law. + +[BNK4_C:BANKJ4] +~w~What the hell are you smoking, man? This ain't no simple plan! + +[BNK4_D:BANKJ4] +~w~Well, who needs a simple plan anyway? + +[BNK4_E:BANKJ4] +~w~Take communism, now that was a simple plan. Didn't do Russia any favors, huh? + +[BNK4_F:BANKJ4] +~w~Calm down, all right? With a team like this it's going to be no problem. + +[BNK4_G:BANKJ4] +~w~We got Cam on safe. Phil? You and me will handle security, and Hilary'll drive the getaway car. + +[BNK4_H:BANKJ4] +~w~Uh, heh heh, aren't you forgetting somebody? Somebody who helped you to no end in this town? Somebody who... + +[BNK4_I:BANKJ4] +~w~Ken... Ken, that's right. Ken here, he washes the money for us and he keeps the drinks on ice. + +[BNK4_J:BANKJ4] +~w~I don't understand what I am supposed to be doing here. + +[BNK4_K:BANKJ4] +~w~Look, it's easy. Haven't you ever seen a movie? + +[BNK4_L:BANKJ4] +~w~We walk into the bank, we wave the gun around, and leave very rich men. + +[P_DEAD:BANKJ4] +~r~Phil's dead!! + +[C_DEAD:BANKJ4] +~r~Cam's dead!! + +[H_DEAD:BANKJ4] +~r~Hilary's dead!! + +[P_HIND:BANKJ4] +~r~You've lost Phil! + +[C_HIND:BANKJ4] +~r~Cam's been left behind! + +[H_HIND:BANKJ4] +~r~Hilary's been abandoned! + +[GETCAR:BANKJ4] +~g~Get in the getaway car to do the bank job! + +[TRASHED:BANKJ4] +~r~YOU TRASHED THE GETAWAY CAR!! + +[BNK4_1:BANKJ4] +I'll drive. + +[BNK4_2:BANKJ4] +Great. A passenger. Wait 'til I tell the group about this. + +[BNK4_3A:BANKJ4] +Hey, watch the wheels, Tommy! + +[BNK4_3B:BANKJ4] +Tommy, Hilary's taking up too much room! + +[BNK4_3C:BANKJ4] +I am not! + +[BNK4_3D:BANKJ4] +Are too! + +[BNK4_3E:BANKJ4] +Hey, shut up you two, or you can get out and walk. + +[BNK4_3F:BANKJ4] +Yeah - HILARY. + +[BNK4_3I:BANKJ4] +For god's sake, Phil, stop waving that thing around! + +[BNK4_3J:BANKJ4] +Yeah, you'll have somebody's eye out! + +[BNK4_3M:BANKJ4] +The car's ruined. RUINED! + +[BNK4_3O:BANKJ4] +You cling to an illusion of permanence. + +[BNK4_3P:BANKJ4] +What? + +[BNK4_3Q:BANKJ4] +You think all things will last. + +[BNK4_3R:BANKJ4] +Youth, loved ones, pizza, + +[BNK4_3S:BANKJ4] +All will pass or end and you must accept that. + +[BNK4_3T:BANKJ4] +You're right. Thanks, Cam. + +[BNK4_3U:BANKJ4] +Don't mention it. + +[BNK4_3V:BANKJ4] +Hey Tommy, why have we stopped? + +[BNK4_4A:BANKJ4] +~w~Keep driving around the block, OK? + +[BNK4_5:BANKJ4] +~w~Okay, Tommy, Okay. + +[BNK4_6:BANKJ4] +~w~THIS IS A RAID! + +[BNK4_7:BANKJ4] +~w~NOBODY MOVE! + +[BNK4_8:BANKJ4] +~w~EVERYBODY UP AGAINST THAT WALL! + +[BNK4_9:BANKJ4] +Phil, hold down the fort! + +[BNK4_10:BANKJ4] +Wilco roger that! + +[BNK4_11:BANKJ4] +Come on Cam, the vault's upstairs... + +[BK4_12A:BANKJ4] +Damn! It's a Flange 9000! + +[BK4_12B:BANKJ4] +This could take hours to crack, + +[BK4_12C:BANKJ4] +Or five minutes if you could find the manager. + +[BNK4_13:BANKJ4] +I'll go find where he's holed up. + +[BK4_14A:BANKJ4] +Phil, things still sweet? + +[BNK4_15:BANKJ4] +Sure. Everything's reeaal quiet. + +[BNK4_16:BANKJ4] +You - you're coming with me! + +[BNK4_17:BANKJ4] +Ok! Ok! Just don't shoot! + +[BNK4_18:BANKJ4] +I SAID NOBODY MOVE! + +[BK4_19A:BANKJ4] +It's on a time lock, + +[BK4_19B:BANKJ4] +You might as well give up now! + +[BK4_20A:BANKJ4] +Hell, I can bypass the time lock, + +[BK4_20B:BANKJ4] +Then we just need your key code and we're good! + +[BNK4_21:BANKJ4] +Stay here. You try anything and you're dead. + +[BNK422A:BANKJ4] +Cam, how long? + +[BK4_23A:BANKJ4] +Give me 5 minutes! + +[BK4_24A:BANKJ4] +I'm gonna check on Phil, I'll be right back. + +[BK4_24B:BANKJ4] +I told you not to touch that alarm! + +[BNK4_25:BANKJ4] +The SWAT team will be here any minute! + +[BNK4_27:BANKJ4] +I could do with some help here, Tommy! + +[BNK4_28:BANKJ4] +Vice City S.W.A.T! You are completely surrounded! + +[BNK4_29:BANKJ4] +Surrounded? HA HA HA HAAAAAaaa! + +[BNK4_30:BANKJ4] +They're crapping themselves, corrupt bastards! + +[BK4_31A:BANKJ4] +Tommy! The vault's open! + +[BK4_34A:BANKJ4] +Ok, we got the SWAT retirement fund. Let's get out of here! + +[BK4_34B:BANKJ4] +Ok, you asked for it! You've had your last chance! + +[BK4_35A:BANKJ4] +They're storming the place! + +[BK4_35B:BANKJ4] +Take cover! + +[BNK4_94:BANKJ4] +~w~Ok, guys. Nice an easy just as we planned. + +[BM_DEAD:BANKJ4] +~r~You needed the bank manager alive!! + +[ASSET_A:BANKJ4] +BANK HEIST ASSET COMPLETED! + +[ASSET_B:BANKJ4] +~g~The Malibu Club will now generate revenue up to a maximum of $~1~ per day. Pick up your cash regularly! + +[IDIOT:BANKJ4] +~r~ That's right, just wander about dressed like a lunatic and draw attention to yourself, IDIOT! + +{=================================== MISSION TABLE BARON1 ===================================} + +[COK1_A:BARON1] +Come on, baby, go! Yeah! Yeah! Arrrrr! + +[COK1_B:BARON1] +Stupid horse! I'll chop your head off! Grrrrr... + +[COK1_C:BARON1] +Who is this dickhead? + +[COK1_D:BARON1] +Tommy Vercetti. You remember me. + +[COK1_E:BARON1] +Excuse me. I'm a little anxious. Never trust a goddamn horse! + +[COK1_F:BARON1] +You do a good job - you work for me now. + +[COK1_H:BARON1] +As I said, amigo, you work for me now. Shut up. Some Judas has betrayed me. + +[COK1_I:BARON1] +He thinks I don't know how much money I should be making, but stealing 3% is as good as stealing 100%. + +[COK1_J:BARON1] +No one does this to me. NO ONE!! + +[COK1_K:BARON1] +You follow him from his apartment and you see where he goes! Later, we will kill him. + +[COK1_1:BARON1] +Ooh shit! + +[COK1_2:BARON1] +Too slow grandad! + +[COK1_4:BARON1] +Loser. + +[COK1_5:BARON1] +You better keep on running, asshole! + +[COK1_8:BARON1] +~g~Quick! Grab some wheels and follow him! + +[COK1_9:BARON1] +~r~You're supposed to follow him, not kill him! + +[COK1_10:BARON1] +~g~Go to the thief's house and find out where he's stashing the money. + +[COK1_11:BARON1] +~g~Have a look through his window. + +[COK1_7:BARON1] +~g~He's escaped to the roof, keep on his tail but don't kill him! + +[COK1_G:BARON1] +I work for money. + +{=================================== MISSION TABLE BARON2 ===================================} + +[COK2_A:BARON2] +What kind of incompetent fool are you? + +[COK2_B:BARON2] +FOOL! FOOL! FOOL! FOOL! + +[COK2_C:BARON2] +Tommy! + +[COK2_D:BARON2] +What, Ricardo? + +[COK2_E:BARON2] +These idiots - they always trying to screw you. + +[COK2_F:BARON2] +That's the problem with this business. + +[COK2_G:BARON2] +What do you think you're doing? + +[COK2_H:BARON2] +These pricks have failed me miserably, + +[COK2_I:BARON2] +Soon any mom and pop will think they can sell gallo in Vice City. + +[COK2_J:BARON2] +What next, huh? The stinking Mafia?! + +[COK2_K:BARON2] +That gang place is a fortress at ground level, + +[COK2_L:BARON2] +so Quentin here - Quentin! QUENTIN! + +[COK2_M:BARON2] +He'll fly you over the area! + +[COK2_N:BARON2] +Eradicate them! + +[COK2_O:BARON2] +What do you think you're doing? + +[COK2_P:BARON2] +What are you doing here? + +[COK2_Q:BARON2] +Hey, I've been asking around and it's obvious + +[COK2_R:BARON2] +that Diaz jumped the deal and iced my brother. + +[COK2_S:BARON2] +And he'll kill you, too! + +[COK2_T:BARON2] +I can take Diaz! + +[COK2_U:BARON2] +No - listen to me! I'll handle Diaz - + +[COK2_1:BARON2] +One thing puzzling me, What's with 'Quentin!? + +[COK2_2:BARON2] +I dunno, I always kinda liked it...Quentin Vance... + +[COK2_3:BARON2] +Vance? Your name's Lance Vance? + +[COK2_4:BARON2] +Hey! I got enough of that at school! + +[COK2_5:BARON2] +You ever fired one of those from a whirly? + +[COK2_8:BARON2] +Where the hell are we headed anyway? + +[COK2_9:BARON2] +Prawn Island. + +[COK2_13:BARON2] +Lance Vance. Poor bastard. + +[COK2_14:BARON2] +Ok, we're almost there. + +[COK2_15:BARON2] +We'll make a couple of passes. + +[COK2_16:BARON2] +So take out as many guns as you can. + +[COK2_17:BARON2] +Then I'll set you down and you're on your way. + +[COK2_20:BARON2] +Damn! This is a war zone! Take out some of those gunmen! + +[COK2_21:BARON2] +We're taking hits here, man! + +[COK2_22:BARON2] +This thing ain't cheap to fix! Take them out! + +[COK2_23:BARON2] +Ok, you're on your own from here! Good luck, brother! + +[COK2_24:BARON2] +Heli Health: + +[COK2_25:BARON2] +~g~Go and collect the money on the roof. + +[COK2_27:BARON2] +You're on MY turf asshole! + +[COK2_28:BARON2] +You're going down! + +[COK2_6:BARON2] +No. I'll get a bit of practice on the way though. + +[OPEN_B:BARON2] +The road blocks to the mainland have now been removed + +[COK2_V:BARON2] +- he's beginning to trust me. + +{=================================== MISSION TABLE BARON3 ===================================} + +[COK3_A:BARON3] +Not so pleased with your selves NOW, huh! + +[COK3_B:BARON3] +Ahahahahaa, Ahahahahaa. + +[COK3_C:BARON3] +Whoa! Watch where you're waving that thing! + +[COK3_D:BARON3] +No more pigeon shit on MY car, eh Tommy! + +[COK3_E:BARON3] +Guess not. + +[COK3_F:BARON3] +You're damn right. Now listen, + +[COK3_G:BARON3] +you know who owns the fastest boat on the east coast? + +[COK3_H:BARON3] +Not off hand, no. + +[COK3_I:BARON3] +ME. And I want it to stay that way. + +[COK3_J:BARON3] +Every smuggler from here to Caracas has one dream, a faster boat. + +[COK3_K:BARON3] +Rumor has it the boatyard has just completed such a vessel. + +[COK3_L:BARON3] +for some Costa Rican dickhead. + +[COK3_M:BARON3] +And Tommy...I WANT THAT BOAT!!! + +[COK3_N:BARON3] +I think your pigeons are back. + +[COK3_O:BARON3] +Ah! I thought I got you. Where'd you come from? + +[COK3_P:BARON3] +Pigeons! Boom! Aaaaah! + +[COK3_5:BARON3] +~g~Find the switch to lower the boat. + +[COK3_6:BARON3] +~g~Get the boat to the mansion. + +[COK3_7:BARON3] +~r~You destroyed the boat! + +[COK3_8:BARON3] +~g~Go to the boatyard at the docks and steal the fastest boat. + +[COK3_9:BARON3] +~g~Now get into the boat. + +{=================================== MISSION TABLE BARON4 ===================================} + +[COK4_A:BARON4] +Eject! PLASTIC CRAP! + +[COK4_B:BARON4] +You doing this to me? + +[COK4_C:BARON4] +Who do you think you are, you piece of plastic SHIT? Aaarrgh! + +[COK4_D:BARON4] +SCREW YOU! + +[COK4_E:BARON4] +It eats my favorite El burro movie, it die! + +[COK4_F:BARON4] +What else could I do? + +[COK4_G:BARON4] +It's probably not plugged in. + +[COK4_H:BARON4] +What? + +[COK4_I:BARON4] +Damn - no matter, I can buy a hundred more. + +[COK4_J:BARON4] +Now Tommy, + +[COK4_K:BARON4] +each month a freelancer sails into Vice City and moors his yacht. + +[COK4_L:BARON4] +He sells his cargo to the first boat. + +[COK4_M:BARON4] +I want you to take the speedboat + +[COK4_N:BARON4] +and beat all the other shitheads to it, + +[COK4_O:BARON4] +then you bring the cargo here, ok!? + +[COK4_P:BARON4] +Let me guess, you thought I could use a guardian angel. + +[COK4_Q:BARON4] +I'm just saying you need to let me in there, my man. + +[COK4_T:BARON4] +and you're probably gonna wanna kiss me! + +[COK4_U:BARON4] +Wacko. + +[COK4_V:BARON4] +Hahahahahaha! + +[COK4_1:BARON4] +So Tommy, we know it was Diaz busted our deal.. + +[COK4_3:BARON4] +So why the hell are we running errands for him? + +[COK4_4:BARON4] +The more we learn now, the less we have to learn when we take this town over! + +[COK4_5:BARON4] +I like your style, man. Real fresh. + +[COK4_12:BARON4] +Watch yourself, they're coming from all over! + +[COK4_13:BARON4] +Got 'em. Head for Diaz's as fast as you can! + +[COK4_14:BARON4] +You want some of this?! + +[COK4_15:BARON4] +Sleep with the fish! + +[COK4_16:BARON4] +Eat it! EAT IT! + +[COK4_19:BARON4] +More trouble up ahead! + +[COK4_20:BARON4] +There are gunmen on that jetty! + +[COK4_24:BARON4] +Good shooting, my friend. You're a real, proper, grade A lunatic. + +[COK4_25:BARON4] +Well, thank you. + +[COK4_26:BARON4] +See you around, Tommy. + +[COK4_27:BARON4] +Okay, Mr. Lance Vance Dance. + +[COK4_28:BARON4] +~g~Get to the yacht before the other boats do! + +[COK4_31:BARON4] +~g~Go to the fastest boat at the jetty! + +[COK4_32:BARON4] +~r~Too slow! + +[COK4_33:BARON4] +~r~You destroyed the boat! + +[COK4_34:BARON4] +~g~Take those boats out! + +[COK4_35:BARON4] +Boat health: + +[COK4_R:BARON4] +Now you can feed me all this 'lonely tough guy' crap, + +[COK4_S:BARON4] +but I know one day I'm gonna save your ass, + +{=================================== MISSION TABLE BARON5 ===================================} + +[PROP_A:BARON5] +PROPERTY ACQUIRED! + +[COK4_30:BARON5] +~r~Lance is dead! + +[ASS1_A:BARON5] +I got us some cannons in the trunk. + +[ASS1_B:BARON5] +Holy shit! Where'd you get all this stuff? + +[ASS1_C:BARON5] +Been saving it for a rainy day. + +[ASS1_D:BARON5] +You like? + +[ASS1_E:BARON5] +Yeah, I like. + +[ASS1_F:BARON5] +You stupid pricks... + +[ASS1_G:BARON5] +my beautiful house + +[ASS1_H:BARON5] +look what you've done to it! + +[ASS1_I:BARON5] +This is for my brother! + +[ASS1_J:BARON5] +I trusted you, Tommy. + +[ASS1_K:BARON5] +I woulda had you made... + +[ASS1_L:BARON5] +Say goodnight, Mr. Diaz. + +[ASS1_1:BARON5] +This place is going to be crawling with assholes...be careful... + +[ASS1_2:BARON5] +Don't worry Tommy, I'll cover you. + +[ASS1_13:BARON5] +DIAZ?! I've come to take over your business! + +[ASS1_15:BARON5] +~g~Storm the mansion and kill Diaz! + +[ASS1_16:BARON5] +~g~Kill Diaz! + +[ASS1_17:BARON5] +~g~There are multiple routes into the mansion. + +[BUD1:BARON5] +Lances Health: + +[ASS1_18:BARON5] +~g~The door is locked, try another route. + +[ASS1_19:BARON5] +This way! + +[ASS1_14:BARON5] +TOMMY! You betrayed me, you idiot! I'm gonna kill you real soon.. + +[ASS1_20:BARON5] +Tommy, my problem with Quentin, not wit you, man! + +{=================================== MISSION TABLE BIKE1 ===================================} + +[BM1_A:BIKE1] +Where's Baker? + +[BM1_B:BIKE1] +I'm looking for Big Mitch Baker... + +[BM1_C:BIKE1] +Who's lookin'? + +[BM1_D:BIKE1] +Tommy Vercetti. + +[BM1_E:BIKE1] +Vercetti + +[BM1_F:BIKE1] +You don't look like the law, so that's bought you a minute. + +[BM1_G:BIKE1] +You better talk fast. + +[BM1_H:BIKE1] +Kent Paul said you might be interested in pulling security for a gig he's got set up. + +[BM1_I:BIKE1] +Kent Paul? Sheesh! No wonder he sent ya. + +[BM1_J:BIKE1] +The last time he was here he left through the window in nothing but his limey birthday suit. + +[BM1_K:BIKE1] +Are you interested or not? + +[BM1_L:BIKE1] +We only do favors for our own. + +[BM1_M:BIKE1] +How do I join? + +[BM1_N:BIKE1] +This ain't no country club, boy. Can you handle a bike? + +[BM1_O:BIKE1] +Can you sit on a stool and drink? + +[BM1_P:BIKE1] +Cougar, Zeppelin, go see how this girl handles a bike... + +[BM1_3:BIKE1] +~r~The racers have been attacked! + +[BIKE1_1:BIKE1] +All right, fancy clothes. Let's see what you can do. + +[BM1_1:BIKE1] +~g~Get a Freeway or an Angel and get to the starting grid. + +[BM1_2:BIKE1] +~g~You need a Freeway or an Angel to compete! + +{=================================== MISSION TABLE BIKE2 ===================================} + +[BM2_1:BIKE2] +CHAOSMETER: + +[BM2_A:BIKE2] +Ah, got ya again. + +[BM2_B:BIKE2] +Hey Vercetti. + +[BM2_C:BIKE2] +Cougar says you can handle a bike pretty good. + +[BM2_D:BIKE2] +Yeah, how many more errands am I gonna to have to run? + +[BM2_E:BIKE2] +I'm a very busy man. + +[BM2_F:BIKE2] +If it's a fight that's gonna settle this then bring it on. + +[BM2_G:BIKE2] +Being one of us ain't just about brawlin'. It's about being part of a family. + +[BM2_H:BIKE2] +Yeah, I've been part of a family before alright. It didn't work out. + +[BM2_I:BIKE2] +Yeah, right, but this family takes care of its own. + +[BM2_J:BIKE2] +We don't ask a man to do the dirty work and then let him do fifteen years hard time. + +[BM2_K:BIKE2] +Yeah, that's right. I've done my homework. + +[BM2_L:BIKE2] +This here's the biggest family of misfits, outcasts and badasses. + +[BM2_M:BIKE2] +Hell, some of us has even been betrayed by our own country. + +[BM2_N:BIKE2] +I was locked up during 'Nam. Ugly business. + +[BM2_O:BIKE2] +Which is why I'm gonna ask you to go mess with the man. + +[BM2_Q:BIKE2] +So get out there, grab a bike and show this city how pissed you are! + +[BM2_R:BIKE2] +Alright, alright. + +[BM2_3:BIKE2] +~g~This sound indicates that you have filled a part of the meter, continue to do so. + +[BM2_2:BIKE2] +~g~You must fill the Chaos Meter in the time given to show us how much of a badass you are! + +[BM2_4:BIKE2] +~r~You failed to fill the Chaos Meter in time! + +[BM2_P:BIKE2] +This whole damn country needs a kick in the ass, and we're the ones to deliver it. + +{=================================== MISSION TABLE BIKE3 ===================================} + +[BM3_A:BIKE3] +Hey there, Mitch. + +[BM3_B:BIKE3] +Well, if it ain't 'bad ass' Vercetti. + +[BM3_C:BIKE3] +Now I wanna see how good you can fight for your patch. + +[BM3_D:BIKE3] +A local street gang made the mistake of stealing my hog... + +[BM3_E:BIKE3] +probably because of some machismo thing or somethin'. + +[BM3_F:BIKE3] +Me and the boys would go over there and teach them a lesson in respect an'all. + +[BM3_G:BIKE3] +Anyways. + +[BM3_H:BIKE3] +Then I got to thinking - this would make a good initiation for you. + +[BM3_I:BIKE3] +You get my bike back, you can tell Paul he's got his security. + +[BM3_2:BIKE3] +~r~You were supposed to bring the bike back, not destroy it! + +[BM3_3:BIKE3] +~g~Get the bike back to the bar! + +[BM3_4:BIKE3] +~g~Get on the bike! + +[INTRUDE:BIKE3] +~g~You've been spotted! + +[BM3_6:BIKE3] +~g~They are holed up behind Ammu-Nation in the Downtown area. + +[BM3_7:BIKE3] +~g~You will need a fast bike to gain access to the roof. + +[BM3_8:BIKE3] +~g~Use the bike to jump from these stairs to the roof on the far side of the road. + +[BM3_1:BIKE3] +~g~A local gang has stolen Mitch Baker's Angel. Get it back! + +[BM3_9:BIKE3] +~g~Get Mitch's Angel and get out of there! + +{=================================== MISSION TABLE BMX_1 ===================================} + +[GETBIK2:BMX_1] +~r~You have ~w~~1~ seconds ~g~to get on a Dirt Bike! + +{=================================== MISSION TABLE BOATBUY ===================================} + +[DRUG_2:BOATBUY] +Put it out. There's a dude here. + +[DRUG_3:BOATBUY] +Hey suit dude! I guess you're the new owner? + +[DRUG_4:BOATBUY] +Yeah. Which one of the boats is the fastest? + +[DRUG_5:BOATBUY] +It's already in the water, dude, + +[DRUG_6:BOATBUY] +I though you might want to try her out. + +[DRUG_7:BOATBUY] +Dude, she's already running with a 300 horse power engine... + +[DRUG_8:BOATBUY] +...and the fiberglass hull, she just shoots through the waves.! + +[DRUG_9:BOATBUY] +She can do like zero to sixty in four seconds flat dude... + +[DRUG_10:BOATBUY] +and she can hold like twenty bales of the best Jamaican smoke right in the hull! + +[DRUG_11:BOATBUY] +So go ahead dude, she's ready to fly! + +[DRUG_12:BOATBUY] +Yo yo, uh, suit dude, you gotta light? + +[DRUG_1:BOATBUY] +Hello? Hel-lo?! Hello? + +[DRUG_13:BOATBUY] +Dude? + +{=================================== MISSION TABLE CAP_1 ===================================} + +[CAP1_B1:CAP_1] +~g~The Mafia is taxing your businesses. Find them and kill them. + +[CAP1_B2:CAP_1] +~g~The Mafia has taxed the Boatyard! + +[CAP1_B3:CAP_1] +~g~The Mafia has taxed the Ice Cream Factory! + +[CAP1_B4:CAP_1] +~g~The Mafia has taxed the Car Showroom! + +[CAP1_B5:CAP_1] +~g~The Mafia has taxed the Taxi Firm! + +[CAP_01:CAP_1] +Ok, what's the emergency? + +[CAP_02:CAP_1] +WHO? + +[CAP_03:CAP_1] +Tommy...some mob thugs ...said they'd come to take their cut... + +[CAP_04:CAP_1] +...said it was a Mr. Forello's money...I feel like crap. + +[CAP_05:CAP_1] +Forelli? SONNY Forelli? + +[CAP_06:CAP_1] +Yeah, that's the guy...I think...they were very insistent... + +[CAP_07:CAP_1] +Don't you worry, pop, I'm not angry with you. + +[CAP_08:CAP_1] +Get him to the hospital. + +[CAP_09:CAP_1] +Tommy...rip that guy a new asshole for me... + +[CAP_10:CAP_1] +I'm gonna rip him two! + +[CAP1_2:CAP_1] +You know the rules, Vercetti! + +[CAP1_3:CAP_1] +Mr. Forelli sends his regards! + +[CAP1_4:CAP_1] +It's the Harwood Butcher! + +[CAP1_5:CAP_1] +You tell Sonny - stay away! + +[CAP1_6:CAP_1] +Vice City is MINE now, NOT his. + +[CAP1_7:CAP_1] +You think you can take me down, Vercetti? + +[CAP1_8:CAP_1] +We'll keep coming after you until you die, Vercetti. + +[CAP1_9:CAP_1] +You don't stand a chance, you psychotic prick. + +[CAP1_10:CAP_1] +I'll murder you Vercetti. + +[CAP1_11:CAP_1] +You always were a jerk. + +[CAP1_12:CAP_1] +You're going to die, Vercetti. + +[CAP1_B6:CAP_1] +~g~You have found the collector, kill him. + +[CAP1_B7:CAP_1] +~g~You've lost the collector. + +[CAP1_B8:CAP_1] +~r~The collector has taxed all of your businesses. + +[CAP1_B9:CAP_1] +~g~The mafia has taxed The Malibu! + +[CAP1_B0:CAP_1] +~g~The mafia has taxed the film studio! + +[CAP1_C2:CAP_1] +~g~The Mafia has arrived at the Boatyard! + +[CAP1_C3:CAP_1] +~g~The Mafia has arrived at the Ice Cream Factory! + +[CAP1_C4:CAP_1] +~g~The Mafia has arrived at the Car Showroom! + +[CAP1_C5:CAP_1] +~g~The Mafia has arrived at the Taxi Firm! + +[CAP1_C9:CAP_1] +~g~The Mafia has arrived at The Malibu! + +[CAP1_C0:CAP_1] +~g~The Mafia has arrived at the film studio! + +[CAP1_D2:CAP_1] +~g~The Mafia is leaving the Boatyard! + +[CAP1_D3:CAP_1] +~g~The Mafia is leaving the Ice Cream Factory! + +[CAP1_D4:CAP_1] +~g~The Mafia is leaving the Car Showroom! + +[CAP1_D5:CAP_1] +~g~The Mafia is leaving the Taxi Firm! + +[CAP1_D9:CAP_1] +~g~The Mafia is leaving The Malibu! + +[CAP1_D0:CAP_1] +~g~The Mafia is leaving the film studio! + +[CAP1B10:CAP_1] +You've capped the Collectors. More are on their way. + +{=================================== MISSION TABLE CARBUY ===================================} + +[CAR1_1:CARBUY] +B.J. Smith. And you must be Mr. Vercetti. + +[CAR1_2:CARBUY] +Would you like the tour? + +[CAR1_3:CARBUY] +Might as well. + +[CAR1_4:CARBUY] +Well, I'm very sad to be selling the dealership to y'all. + +[CAR1_5:CARBUY] +This was my first investment after I turned pro. + +[CAR1_6:CARBUY] +But now it's time for me to move on. + +[CAR1_7:CARBUY] +You're leaving town? + +[CAR1_8:CARBUY] +Not in too much of a hurry, I hope? + +[CAR1_9:CARBUY] +No. I'm just coming out of retirement, and preparing for my future comeback. + +[CAR1_10:CARBUY] +The business wasn't too strong, + +[CAR1_11:CARBUY] +and my staff took it upon themselves to get a bit more + +[CAR1_12:CARBUY] +creative with the generation of wealth. + +[CAR1_13:CARBUY] +Obviously, I could wind down the business before I hand it over. + +[CAR1_14:CARBUY] +Hell, I could burn the place down if I wanted to. + +[CAR1_15:CARBUY] +This is prime development land. + +[CAR1_16:CARBUY] +Oh, I wouldn't worry about any of that. + +[CAR1_17:CARBUY] +This place seems perfect. + +[CAR1_18:CARBUY] +Yeh it does, So I take it we have a deal? + +{=================================== MISSION TABLE CARPAR1 ===================================} + +[MM_1_A:CARPAR1] +~g~Collect ~y~5 checkpoints~r~ WITHOUT ~g~smashing any ~r~CONES! ~g~You may collect checkpoints in ~y~ANY ORDER. + +[CONE_1:CARPAR1] +~r~You smashed a cone!! + +[MM_1_C:CARPAR1] +~y~PASS THROUGH~g~ a checkpoint to start the timer. ~g~Each checkpoint will credit you with ~y~~1~ SECONDS~g~. + +{=================================== MISSION TABLE COPCAR ===================================} + +[KILLS:COPCAR] +KILLS: + +[C_BREIF:COPCAR] +~g~Suspect last seen in the ~a~ area. + +[COPCART:COPCAR] +~g~You have ~1~ seconds to return to a police vehicle before the mission ends. + +[C_CANC:COPCAR] +~r~Vigilante mission cancelled! + +[C_TIME:COPCAR] +~r~Your time as a law enforcer is over! + +[C_COMP1:COPCAR] +Vigilante mission level 12 complete: Your max Body Armor increased to 150 + +[CLEVEL:COPCAR] +Vigilante Mission Level ~1~ + +[C_PASS:COPCAR] +THREAT ELIMINATED: $ ~1~ + +{=================================== MISSION TABLE COUNT1 ===================================} + +[CM1_A:COUNT1] +Mr. Vercetti? Hey. You bought the old print works? + +[CM1_B:COUNT1] +Yeah, my old man used to work on these. + +[CM1_C:COUNT1] +I was going to follow him in his trade, but...I lived a different life. + +[CM1_D:COUNT1] +You planning on selling the old machinery, breaking it down? + +[CM1_E:COUNT1] +I'm thinking we might print something - a newspaper, a magazine... + +[CM1_F:COUNT1] +Oh, crap, sonny, low grade crap. I've always fancied printing money. It ain't too hard. + +[CM1_G:COUNT1] +You know, I've been doing it on a small scale for years. + +[CM1_H:COUNT1] +Really? + +[CM1_I:COUNT1] +Sure. But we'd need some good quality plates. + +[CM1_J:COUNT1] +Of course! There's a counterfeiting syndicate already operating in Florida. + +[CM1_K:COUNT1] +A syndicate? + +[CM1_L:COUNT1] +Yeah. Just rumors is all I've heard. + +[CM1_M:COUNT1] +I know a man who's good with rumors... + +[CM1_N:COUNT1] +I used to spend the evenings with him, cleaning the rollers... + +[CM1_2A:COUNT1] +Look at the arse on that! + +[CM1_2B:COUNT1] +Awright girl, it's your loss mate init! + +[CM1_2C:COUNT1] +Awright me ol'china, how's it hangin'? + +[CM1_2D:COUNT1] +What do you know about counterfeiting? + +[CM1_2E:COUNT1] +Oh I'm fine Paul, how 'bout you? + +[CM1_2F:COUNT1] +Come 'ere! + +[CM1_2G:COUNT1] +Awright! Awright! Awright!! You're obviously a busy man. + +[CM1_2H:COUNT1] +All I know about dodgy readys is the Triads supply the plates. + +[CM1_2I:COUNT1] +They've got a shipping company down the docks, + +[CM1_2J:COUNT1] +the boss man would know when the plates are coming in next! + +[CM1_2K:COUNT1] +Thanks Paul. + +[CM1_2L:COUNT1] +What's the matter with you, you maniac! + +[CM1_2M:COUNT1] +Give me another drink, lively! + +[CM1_3:COUNT1] +~g~You've been spotted! + +[CM1_5:COUNT1] +~g~Go and meet Kent Paul at the Malibu Club! + +[CNT1_1:COUNT1] +Who are you? Oooof! Aaiieee! Not the face! Not the face! + +[CM1_1:COUNT1] +~g~Go to the Chartered Libertine Lines boat at the docks. + +[CM1_2:COUNT1] +~g~The Shipping Officer will have the information that is required. + +[CNT1_2:COUNT1] +Ok, I talk! I talk! + +[CM1_6:COUNT1] +~g~Get the information back to the Print Works! + +{=================================== MISSION TABLE COUNT2 ===================================} + +[CNT2_B1:COUNT2] +Alright, the courier's moving the plates from the docks today. + +[CNT2_B2:COUNT2] +I'm gonna go intercept them, grab the plates, lose any heat, and make my way back here. + +[CNT2_B3:COUNT2] +Now. Depending how well this goes, + +[CNT2_B4:COUNT2] +we may have five minutes to print the money before the counterfeit syndicate finds us, or we may have all year. + +[CNT2_B5:COUNT2] +Either way, I want green rolling off the presses five minutes after I get back. Got it? + +[CNT2_B6:COUNT2] +Don't you worry Tommy. We'll be ready. + +[CNT2_B7:COUNT2] +Me an'the boys will be around in the neighborhood case you need any heat taken care of. + +[CNT2_B8:COUNT2] +All right, everybody cool? All right. I'll catch you later... + +[CNT2_01:COUNT2] +~g~The counterfeit plates ~y~courier~g~ is arriving at the ~r~docks~g~ in a helicopter any second now. + +[CNT2_02:COUNT2] +~r~The plates courier has fled in the helicopter. + +[CNT2_03:COUNT2] +~r~The courier has arrived at his destination safely, you're too late! + +[CNT2_04:COUNT2] +~r~You destroyed the plates in the explosion! + +[CNT2_05:COUNT2] +~g~You have the counterfeit plates. Take them to the print works. + +[CNT2_06:COUNT2] +~g~The courier has died and dropped the plates, get to them before anyone else. + +[CNT2_07:COUNT2] +~g~One of the guards has picked up the plates, don't let 'em get away. + +[CNT2_08:COUNT2] +~g~The ~r~courier~g~ with the plates has arrived at the docks. + +[CNT2_4:COUNT2] +Private business. You're not welcome! + +[CNT2_09:COUNT2] +PRINT WORKS ASSET COMPLETED + +[CNT2_10:COUNT2] +~g~The print works will now generate revenue up to a maximum of $~1~. Make sure you collect it regularly. + +[CNT2_11:COUNT2] +~r~The plates are at the bottom of the sea! + +{=================================== MISSION TABLE CUBAN1 ===================================} + +[CUB1_A:CUBAN1] +Si, men? + +[CUB1_B:CUBAN1] +Hey, easy Papi, this man's for me. You, you the boy? + +[CUB1_C:CUBAN1] +Oh yeh. You the boy. I think so, you know? + +[CUB1_D:CUBAN1] +No. I don't think I do. + +[CUB1_E:CUBAN1] +Oh yeah? You come here, tough guy. + +[CUB1_F:CUBAN1] +You think you can take me on? + +[CUB1_G:CUBAN1] +You think you can play stupid with me? + +[CUB1_H:CUBAN1] +No, I think you're playing plenty stupid enough for both of us. + +[CUB1_I:CUBAN1] +Hey, he call you dumb, son. + +[CUB1_J:CUBAN1] +And I call him a little girl, Papi. + +[CUB1_K:CUBAN1] +Look at him, all dressed up like that. + +[CUB1_L:CUBAN1] +What is this, ladies night? + +[CUB1_M:CUBAN1] +You some kind of tough guy, you dress like a woman? + +[CUB1_N:CUBAN1] +You got on panties like a woman too, huh? + +[CUB1_O:CUBAN1] +What you got against women? You prefer men, big boy? + +[CUB1_P:CUBAN1] +I like women! I like all women! I love my mother, chico! + +[CUB1_Q:CUBAN1] +Alright, alright, I'll take your word for it. Relax. + +[CUB1_R:CUBAN1] +Can you drive, amigo? + +[CUB1_S:CUBAN1] +Yeah... like a woman. + +[CUB1_T:CUBAN1] +Very funny. I like you, big boy. Maybe you can help. + +[CUB1_U:CUBAN1] +Maybe you can prove you a man. Huh? + +[CUB1_V:CUBAN1] +Take out the boat. + +[CUB1_W:CUBAN1] +Show me you got some big cojones, + +[CUB1_X:CUBAN1] +and not some little bitty chiquita ones. + +[CUB1_02:CUBAN1] +Ok man, treat her like a woman. + +[CUB1_03:CUBAN1] +Not bad, you're a real man. + +[CUB1_04:CUBAN1] +You got real big cojones, amigo. + +[CUB1_05:CUBAN1] +Amigo, you a man, man. + +[CUB1_06:CUBAN1] +Call yourself a man, man? + +[CUB1_07:CUBAN1] +You a little scaredy kitten, baby boy, go cry to your mommy! + +[CUB1_09:CUBAN1] +Man, you the man, man. I like you, man. I like you a lot. + +[CUB1_10:CUBAN1] +Any time, man. 'cause you got cojones. And all my friends have big cojones. + +[CUB1_11:CUBAN1] +~r~You Killed Rico! + +[CUB1_12:CUBAN1] +Go through the first checkpoint to begin the test. + +[CUB1_14:CUBAN1] +Get back in the boat! + +[CUB1_15:CUBAN1] +~r~You are too slow, man. + +[CUB1_01:CUBAN1] +Hey, I'm Rico. You the man with the big cojones? + +[CUB1_13:CUBAN1] +~g~You have three minutes to get round the course. + +[CUB1_08:CUBAN1] +You a big waste of space. Walk like a man, talk like a man, but you drive like an idiot. + +{=================================== MISSION TABLE CUBAN2 ===================================} + +[CUB2_A:CUBAN2] +Un cafecito, por favor, Alberto.. + +[CUB2_N:CUBAN2] +No problema, Tommy. + +[CUB2_B:CUBAN2] +Papi! Una grande problema! + +[CUB2_O:CUBAN2] +Umberto my son, what happened? + +[CUB2_C:CUBAN2] +The Haitians! I hate these Haitians! + +[CUB2_D:CUBAN2] +They mess with me for the last time! + +[CUB2_E:CUBAN2] +These Hai - these Haitians! We take 'em out! + +[CUB2_F:CUBAN2] +Only we need some backup. + +[CUB2_H:CUBAN2] +Amigo, you drive good! + +[CUB2_I:CUBAN2] +For a woman. Right? + +[CUB2_J:CUBAN2] +This is no time for joking! + +[CUB2_K:CUBAN2] +Come on, drive for me again! + +[CUB2_L:CUBAN2] +Take my boys over there, and then we'll take these Haitians down! + +[CUB2_01:CUBAN2] +Not enough room, man, you need a bigger car. + +[CUB2_02:CUBAN2] +We need reinforcements from the cafe! + +[CUB2_03:CUBAN2] +~g~Get a car and pick up the Cubans from outside Robina's Cafe. + +[CUB2_04:CUBAN2] +~g~Go and drop the Cubans off at the fight. + +[CUB2_05:CUBAN2] +Take out that cowardly sniper! + +[CUB2_07:CUBAN2] +They fight like girls! Take cover! + +[CUB2_09:CUBAN2] +Sniper on the roof! + +[CUB2_11:CUBAN2] +~r~You fool, we needed that car. + +[CUB2_12:CUBAN2] +Hey amigo! Good to see you could make it! + +[CUB2_13:CUBAN2] +Stinking nest of Haitians, we gonna kill 'em all! + +[CUB2_14:CUBAN2] +CHAAAAAARGE! + +[CUB2_15:CUBAN2] +Now, my brothers, CHAAARRRGGEE!! + +[CUB2_16:CUBAN2] +Tommy, we have proved our manful bravery! + +[CUB2_17:CUBAN2] +Let us steal the van full of drugs and make good our escape! + +[CUB2_18:CUBAN2] +~g~Get a car and pick up the Cubans + +[CUB2_19:CUBAN2] +We gonna fight like men! + +[CUB2_21:CUBAN2] +Fight like men with huge cojones! + +[CUB2_22:CUBAN2] +~g~ Finish off the rest of the Haitians so the Cubans can move forward. + +[CUB2_23:CUBAN2] +~g~ Little Haiti will be swarming with Haitians trying to even the score with the Cubans. Watch your back. + +[CUB2_24:CUBAN2] +~g~Return to Robina's Cafe with the Van and park it round the back. + +[CUB2_25:CUBAN2] +KILL ALL THE HAITIANS!! + +[CUB2_G:CUBAN2] +I lost a few hermanos already out there. + +[CUB2_M:CUBAN2] +They mess with me, they mess with the biggest boy in town! + +{=================================== MISSION TABLE CUBAN3 ===================================} + +[CUB3_B:CUBAN3] +Poppa, don't serve this snake in the straw. + +[CUB3_C:CUBAN3] +You're two-faced, Tommy! + +[CUB3_E:CUBAN3] +The Haitians, man. They're laughing at me! + +[CUB3_G:CUBAN3] +They're laughing at me, Tommy. At me! + +[CUB3_I:CUBAN3] +Nobody does whatever they like, Umberto, they do what you let them do. + +[CUB3_J:CUBAN3] +What? + +[CUB3_K:CUBAN3] +You want somebody taken care of? + +[CUB3_L:CUBAN3] +I can handle it, but it's gonna cost you. + +[CUB3_M:CUBAN3] +I know we're brothers and all, but this is business. + +[CUB3_N:CUBAN3] +Tommy. You a real man. Businessman, a gentleman. + +[CUB3_O:CUBAN3] +These Haitians. They have a load of product coming in off shore, really good stuff. + +[CUB3_P:CUBAN3] +We take it, and we finish them. + +[CUB3_Q:CUBAN3] +You take it, and I look after you. Like my brother. Like my son. + +[CUB3_R:CUBAN3] +I think I prefer the cash to being bounced on your knee, amigo. + +[CUB3_01:CUBAN3] +Hey Rico. Nice boat, you ready? + +[CUB3_03:CUBAN3] +~g~Collect all the briefcases filled with the drugs and cash. + +[CUB3_04:CUBAN3] +~g~Get the drugs and cash back to Umberto. + +[CUB3_05:CUBAN3] +Si Tommy. Now you be a good shot today, + +[CUB3_06:CUBAN3] +My boat, she no good full of holes, ok? + +[CUB3_07:CUBAN3] +~g~ Go meet Rico. He'll drive you to the meet location. + +[CUB3_02:CUBAN3] +~g~KILL ALL THE HAITIANS ON THE BOATS!! + +[CUB3_08:CUBAN3] +Uh oh.. Pack of Cubans. We under attack! + +[CUB3_A:CUBAN3] +Alberto. Una cafe, senor. + +[CUB3_D:CUBAN3] +You're either two-faced, or you're a wimp, baby boy! + +[CUB3_F:CUBAN3] +Easy, easy. What's your problem? + +[CUB3_H:CUBAN3] +Umberto Robina! They're doing whatever they like! + +{=================================== MISSION TABLE CUBAN4 ===================================} + +[CUB4_A:CUBAN4] +Hey, ladies. You know what I'm gonna do? + +[CUB4_B:CUBAN4] +I'm gonna kill me a Haitian. And then? + +[CUB4_C:CUBAN4] +And then I'm going to make love like a man. + +[CUB4_D:CUBAN4] +You know that, chica? Something like this. + +[CUB4_E:CUBAN4] +Loser! + +[CUB4_F:CUBAN4] +Prick. + +[CUB4_G:CUBAN4] +Hey, baby, I wouldn't touch you with a ten foot pole! + +[CUB4_H:CUBAN4] +Umberto Robina, he likes the ladies! Not some goat in a skirt! + +[CUB4_I:CUBAN4] +Tommy!! Tommy, I love you, I love you! Let's go! + +[CUB4_J:CUBAN4] +Go where? Can't I get a cup of coffee first? + +[CUB4_K:CUBAN4] +No time for coffee! Besides, I just had one. + +[CUB4_L:CUBAN4] +We gonna take out the Haitians. + +[CUB4_M:CUBAN4] +Tommy, how do you take out a snake? + +[CUB4_N:CUBAN4] +You bite him in the ass! Hahaha! + +[CUB4_O:CUBAN4] +Whatever you say, Umberto. + +[CUB4_P:CUBAN4] +Tommy, you go and get us a little Haitian car. + +[CUB4_Q:CUBAN4] +When you get it, come back and pick up my boy. + +[CUB4_R:CUBAN4] +Pepe, and take him out to the Haitians. + +[CUB4_S:CUBAN4] +Then, you go around to the Haitians processing plant, and you use their solvent as an explosive. + +[CUB4_T:CUBAN4] +Boom! Bye bye! + +[CUB4_U:CUBAN4] +Umberto, what about you? + +[CUB4_V:CUBAN4] +Uhh... I'm going to stay behind, and watch over the cafe with Poppa. + +[CUB4_W:CUBAN4] +He not feeling so good. You know? + +[CUB4_02:CUBAN4] +~g~The bombs will be set with a 45 second timer. + +[CUB4_04:CUBAN4] +~r~You've alerted the base, there is no way we will get in now! + +[CUB4_07:CUBAN4] +Oy - the solvent is round the back, amigo. + +[CUB4_08:CUBAN4] +Hola, amigos. + +[CUB4_09:CUBAN4] +Bueno. Haitian Putas. Muerte. + +[CUB4_10:CUBAN4] +Vamos. + +[CUB4_11:CUBAN4] +Vamos indeed. + +[CUB4_12:CUBAN4] +Hey, we need a Haitian gang car! + +[CUB4_13:CUBAN4] +Oye, let's go find our muchachos! + +[CUB4_14:CUBAN4] +Follow my compadres. + +[CUB4_15:CUBAN4] +Ok, in you go... + +[CUB4_16:CUBAN4] +I'm going to plant the bomb, cover me! + +[CUB4_17:CUBAN4] +RUN!! + +[CUB4_18:CUBAN4] +Man, this a nice part of town... + +[CUB4_19:CUBAN4] +This place is a dump, man. + +[CUB4_20:CUBAN4] +I had a beautiful woman... lived around this neighborhood. + +[CUB4_21:CUBAN4] +You know, they do nice pizzas here. + +[CUB4_22:CUBAN4] +Whoah, man. You drive like a crazy bitch! + +[CUB4_23:CUBAN4] +You lost, man? + +[CUB4_24:CUBAN4] +You've left Pepe behind, go and get him. + +[CUB4_03:CUBAN4] +~g~Stay in the car until safely parked inside the compound. + +[CUB4_26:CUBAN4] +~g~Take Pepe, head North into Little Haiti and steal a Voodoo car. + +[CUB4_27:CUBAN4] +~g~Go and meet up with Rico and the other Cubans. + +[CUB4_28:CUBAN4] +~g~Join the other Cubans at the Haitian Drugs Factory. + +[CUB4_29:CUBAN4] +~g~Walk into each of the markers to plant a bomb at that location. + +[CUB4_30:CUBAN4] +~g~After all three bombs are planted, get clear of the factory before it blows. + +[CUB4_31:CUBAN4] +~g~Get clear of the factory!! + +[CUB4_32:CUBAN4] +~g~Park the car at the blip and get out. + +[CUB4_06:CUBAN4] +~r~You did not get far enough away from the base and we had to abort the explosion! + +{=================================== MISSION TABLE FINALE ===================================} + +[FIN1_01:FINALE] +What's going on? + +[FIN1_02:FINALE] +Tommy! Oh good, good. Listen, listen. Uh, listen, + +[FIN1_03:FINALE] +I like fish. I love fish. + +[FIN1_04:FINALE] +I love them as pets in bowls, or as food on a plate, + +[FIN1_05:FINALE] +but as much as I love em, I don't want to sleep with them. + +[FIN1_06:FINALE] +Okay, but right now your Italian brothers are coming from up there to fit me with some cement shoes, and I... + +[FIN1_07:FINALE] +Shut up Ken. Sit down. + +[FIN1_08:FINALE] +Lance, what the hell's going on? + +[FIN1_09:FINALE] +It's your friends up north Tommy. They ain't too happy you capped their man. + +[FIN1_10:FINALE] +They're coming down to see the business today. + +[FIN1_11:FINALE] +They took longer than I thought... + +[FIN1_12:FINALE] +Guys, we gotta make this final we gotta leave no doubt that this is my operation. Mine! + +[FIN1_13:FINALE] +Ken, you get the first run of counterfeit cash and put three mil in briefcases. + +[FIN1_14:FINALE] +Lance, you get the guys together... + +[FIN2_01:FINALE] +Tommy! + +[FIN2_02:FINALE] +What? No big hugs for your old buddy? + +[FIN2_03:FINALE] +I've had fifteen years out of the loop, + +[FIN2_04:FINALE] +I'm a bit rusty on family etiquette. + +[FIN2_05:FINALE] +Always angry, eh Tommy. + +[FIN2_06:FINALE] +Didn't I say your temper would get you into trouble, huh? + +[FIN2_07:FINALE] +There's three mil in the cases... + +[FIN2_08:FINALE] +How many was it? Ten? No, eleven men. + +[FIN2_09:FINALE] +That's how you get to be called the Harwood Butcher! Heh-heh-heh! + +[FIN2_10:FINALE] +You sent me to kill one man, ONE MAN. They knew I was coming Sonny... + +[FIN2_11:FINALE] +Tommy, Tommy, watch your tone. + +[FIN2_12:FINALE] +Anyone would think you blame me for that unfortunate set of circumstances. + +[FIN2_13:FINALE] +Just take the money... + +[FIN2_14:FINALE] +Get the damn cash. + +[FIN2_15:FINALE] +You know, Tommy? I did what I could for you, I pulled strings, called in favors. + +[FIN2_16:FINALE] +I was your friend, Tommy. I hoped you'd see sense, see what's good for business. + +[FIN2_17:FINALE] +I trusted you, Tommy, and you disappointed me. + +[FIN2_18:FINALE] +But at least someone in your chicken shit organization knows how to do business, + +[FIN2_19:FINALE] +Isn't that right, Lance? + +[FIN2_20:FINALE] +I'm sorry Tommy. This is Vice City. This is business. + +[FIN2_21:FINALE] +You sold us out... + +[FIN2_22:FINALE] +No. I sold YOU out, Tommy, I sold YOU out. + +[FIN2_23:FINALE] +The real cash is upstairs in the safe. + +[FIN2_24:FINALE] +Tommy, what was the big plan? + +[FIN2_25:FINALE] +You think I'd just take the fake cash? + +[FIN2_26:FINALE] +Save face and run away with my tail between my legs?! + +[FIN2_27:FINALE] +No. + +[FIN2_28:FINALE] +I just wanted to piss you off before I kill you. + +[FIN3_01:FINALE] +Tommy? + +[FIN3_02:FINALE] +Oh my god, Tommy! What happened? + +[FIN3_03:FINALE] +What does it look like? + +[FIN3_04:FINALE] +It looks like you ruined your suit! + +[FIN3_05:FINALE] +and Tommy, that was a beautiful suit! Tommy, what on earth happened? + +[FIN3_06:FINALE] +I had a disagreement with a business associate, you know how it is. + +[FIN3_07:FINALE] +Tommy, I have a disagreement, I send them an angry letter. + +[FIN3_08:FINALE] +Maybe I pee in their mailbox. I don't start World War III. + +[FIN3_09:FINALE] +You know, maybe you should speak to my shrink... + +[FIN3_10:FINALE] +That stupid prick, Lance... + +[FIN3_11:FINALE] +Tommy. I never liked that guy, okay? + +[FIN3_12:FINALE] +He's neurotic, he's insecure, he's self-centered - the guy's an asshole! + +[FIN3_13:FINALE] +I'm glad you took him out! + +[FIN3_14:FINALE] +I don't think we're gonna be getting any more heat from up north either... + +[FIN3_15:FINALE] +...'cause there ain't no 'up north', anymore. + +[FIN3_16:FINALE] +It's all down south now. + +[FIN3_17:FINALE] +Wait, does that mean what I think it means..? Tommy, baby! + +[FIN3_18:FINALE] +What do you think it means? + +[FIN3_19:FINALE] +That we're in charge... I mean, that you're in charge. Oh, Tommy! + +[FIN3_20:FINALE] +You know, Ken. I think this could be the beginning of a beautiful business relationship.... + +[FIN3_21:FINALE] +After all, you're a conniving, backstabbing, two-bit thief + +[FIN3_22:FINALE] +and I'm a convicted psychotic killer and drug dealer. + +[FIN3_23:FINALE] +I know. Ain't it just beautiful? + +[FIN_B1:FINALE] +~g~Go and kill ~y~Lance Vance~g~ the backstabber. + +[FIN_B2:FINALE] +~g~Kill ~p~Sonny~g~ and finish this once and for all. + +[FIN_B3:FINALE] +~g~The Mafia are trying to steal your money. Defend the safe. + +[FIN_B4:FINALE] +~g~You are close to death, get some ~w~health~g~ from downstairs. + +[FIN_B5:FINALE] +~g~The Mafia is stealing your money, defend the ~c~safe + +[FIN_B7:FINALE] +~r~The mafia has stolen all your money. + +[DEFSAFE:FINALE] +~g~Get back to the safe and defend it. + +{=================================== MISSION TABLE FIRETRK ===================================} + +[F_PASS1:FIRETRK] +Fire extinguished! + +[F_FAIL2:FIRETRK] +~r~You're too late! + +[F_CANC:FIRETRK] +~r~Fire Fighter mission cancelled! + +[F_EXTIN:FIRETRK] +FIRES: + +[F_START:FIRETRK] +~g~Burning vehicle reported in the ~a~ area. Go and extinguish the fire. + +[SIREN_1:FIRETRK] +To turn on this vehicle's sirens tap the ~h~~k~~VEHICLE_HORN~ ~w~button. + +[SIREN_2:FIRETRK] +To turn on this vehicle's sirens tap the ~h~~k~~VEHICLE_HORN~ ~w~button. + +[FIREPRO:FIRETRK] +Fire Truck Mission level 12 complete. You are now completely fireproof!! + +[F_FAIL1:FIRETRK] +Fire Fighter mission ended. + +[F_STAR1:FIRETRK] +~g~Burning vehicles reported in the ~a~ area. Go and extinguish the fire. + +[SPRAY_4:FIRETRK] { reVC update } +Use the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button to fire the water cannon. Aim using ~h~~k~~VEHICLE_TURRETLEFT~~w~ and ~h~~k~~VEHICLE_TURRETRIGHT~~w~. + +[SPRAY_1:FIRETRK] { reVC update } +Use the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button to fire the water cannon. Aim using ~h~~k~~VEHICLE_TURRETLEFT~~w~ and ~h~~k~~VEHICLE_TURRETRIGHT~~w~. + +{=================================== MISSION TABLE GENERA1 ===================================} + +[GEN1_A:GENERA1] +Mr. Vercetti! + +[GEN1_B:GENERA1] +Colonel. + +[GEN1_D:GENERA1] +No - thanks. + +[GEN1_E:GENERA1] +I'm ashamed to admit that one of the causes of our mutual problem appears to have been the loose tongue of a man I used to trust. + +[GEN1_F:GENERA1] +I've been carrying Gonzalez for years, but now his incompetence reaches new heights! + +[GEN1_G:GENERA1] +It is only right that you kill Gonzalez... + +[GEN1_H:GENERA1] +Did he do it? It's the money that's important to me. + +[GEN1_J:GENERA1] +He will be at his Penthouse, half drunk probably. Use this... + +[GEN1_06:GENERA1] +Eh! He's got a blade! + +[GEN1_07:GENERA1] +Go away from me, you cheap bastard! + +[GEN1_08:GENERA1] +Oh sweet Jesus, I've wasted my life and my looks! + +[GEN1_10:GENERA1] +I'm going to shut that big mouth of yours! + +[GEN1_11:GENERA1] +Stop running you fat slimeball! + +[GEN1_12:GENERA1] +Stand still and I'll make it quick! + +[GEN1_13:GENERA1] +Quit your squealing, no one cares, fatso! + +[GEN1_C:GENERA1] +Thank you for coming. Please sit. Lobster? + +[GEN1_05:GENERA1] +~g~Go and kill Gonzalez! + +[GEN1_09:GENERA1] +I pay you double, Tommy, DOUBLE! + +[GEN1_18:GENERA1] +~r~Gonzalez has made it safely to the Police Station! + +[GEN1_19:GENERA1] +~g~The Vice City Police are on to you! + +[GEN1_20:GENERA1] +~g~Get into a vehicle. + +[GEN1_21:GENERA1] +~g~Get to the~h~ Pay 'N' Spray~g~ in~h~ Vice Point~g~. + +[GEN1_22:GENERA1] +Drive your vehicle into the spray shop to lose your ~h~wanted level, ~h~repair and ~h~respray your vehicle. Cost - ~h~$100. This time it's free. + +[GEN1_01:GENERA1] +When jogging, press and hold the~h~ ~k~~PED_FIREWEAPON~ ~w~button to prepare a melee attack. + +[GEN1_02:GENERA1] +When jogging, press and hold the~h~ ~k~~PED_FIREWEAPON~ ~w~button to prepare a melee attack. + +[GEN1_03:GENERA1] +When jogging, press and hold the~h~ ~k~~PED_FIREWEAPON~ ~w~button to prepare a melee attack. + +[GEN1_14:GENERA1] +Release the~h~ ~k~~PED_FIREWEAPON~ ~w~button to make the attack. + +[GEN1_15:GENERA1] +Release the~h~ ~k~~PED_FIREWEAPON~ ~w~button to make the attack. + +[GEN1_16:GENERA1] +Release the~h~ ~k~~PED_FIREWEAPON~ ~w~button to make the attack. + +[GEN1_I:GENERA1] +For this kindness I'll reward you, and then we will find your money together. + +[GEN1_23:GENERA1] +~g~Go back through the doors to return to the ground floor. + +{=================================== MISSION TABLE GENERA2 ===================================} + +[COL2_B:GENERA2] +This looks delicious, huh? Tapir snout? + +[COL2_C:GENERA2] +Uhhh... no, no. No, thanks. + +[COL2_D:GENERA2] +Tommy, you are like a pampas breeze that has freed me from the stench of corruption, + +[COL2_E:GENERA2] +although, I must appear to mourn his passing and carry on with business as usual. + +[COL2_F:GENERA2] +This isn't getting me any closer to my money... + +[COL2_G:GENERA2] +Tommy, my friend, you are not in Liberty now. Here we do things differently. + +[COL2_H:GENERA2] +I will continue with my enquiries but in the meantime I have a valuable deal to close. + +[COL2_I:GENERA2] +A favor for a friend, Cortez? + +[COL2_J:GENERA2] +You're a good friend, Tommy. I knew you would not let me down. + +[COL2_K:GENERA2] +I need you to meet a courier who has obtained some valuable technology for me... + +[COL2_1:GENERA2] +Ze rain, she is tres wet zis time of the year... + +[COL2_2:GENERA2] +What? + +[COL2_3:GENERA2] +Ah, comment? + +[COL2_4:GENERA2] +Look, Cortez sent me. Just give me the damn chips. + +[COL2_5:GENERA2] +Oh...d'accord. + +[COL2_B1:GENERA2] +~g~Meet the courier at the mall. + +[COL2_B2:GENERA2] +~g~The courier is fleeing with the guidance chips! Don't let him get away! + +[COL2_B3:GENERA2] +~g~Take the guidance chips back to the Colonel. + +[COL2_F1:GENERA2] +~r~You killed the contact! + +[COL2_F2:GENERA2] +~g~The courier is dead. Grab the guidance chips. + +[COL2_6A:GENERA2] +Freeze, imperialist American pig! Zat iz propertay of ze government Francais. 'And eet over! + +[BLIPHLP:GENERA2] +The blip on the radar is a triangle pointing up, this shows that the target is higher than the player. + +[COL2_F3:GENERA2] +~r~The guidance chips are at the bottom of the sea. + +[COL2_F4:GENERA2] +~r~The courier has escaped! You failed to get the guidance chips. + +[COL2_A:GENERA2] +Tommy! Come, join me. + +{=================================== MISSION TABLE GENERA3 ===================================} + +[GEN3_A:GENERA3] +Thomas, I appreciate your coming. + +[GEN3_B:GENERA3] +Forgive me for getting straight to business. + +[GEN3_C:GENERA3] +Diaz has asked me to oversee a minor business transaction. + +[GEN3_D:GENERA3] +Let's hope it goes better than last time, huh? + +[GEN3_E:GENERA3] +Which is why I thought of you, my friend. + +[GEN3_F:GENERA3] +I've dropped some protection at the multistory carpark. + +[GEN3_G:GENERA3] +Pick it up - then go and watch over Diaz's men at the drop off. + +[GEN3_H:GENERA3] +Gracias, amigo. + +[GEN3_1:GENERA3] +Hogging all the action, I see... + +[GEN3_2:GENERA3] +Look, you wanna do something other than just shadowing me everywhere? Why don't you come along and show me if you're any use. + +[GEN3_3:GENERA3] +I might just do that. The name's Lance, by the way. + +[GEN3_5:GENERA3] +You must be Cortez's new gun. + +[GEN3_6:GENERA3] +Until more gainful opportunities arise. + +[GEN3_7:GENERA3] +They'll be here any minute - we both better get a good vantage point... + +[GEN3_8:GENERA3] +OK! I'll take the balcony, you get the roof across the yard. + +[GEN3_9:GENERA3] +I live! Dickheads! And it's all down to you! What is your name? + +[GEN3_10:GENERA3] +Tommy. + +[GEN3_11:GENERA3] +I see you soon, amigo, I think! + +[GEN3_12:GENERA3] +Shit. Where's that guy Lance? + +[GEN3_14:GENERA3] +Tommy! I need some help here! + +[GEN3_15:GENERA3] +Don't worry, I got you covered! + +[GEN3_16:GENERA3] +Diaz's men are getting cut down! + +[GEN3_19:GENERA3] +~g~Haitians! They're busting the deal! Protect Diaz! + +[GEN3_20:GENERA3] +~g~The Colonel has arranged some firepower for you at the multistory carpark. + +[GEN3_22:GENERA3] +Diaz's Health: + +[GEN3_23:GENERA3] +~g~You've left Lance behind! Go and get him! + +[GEN3_25:GENERA3] +~r~Lance died! + +[GEN3_28:GENERA3] +~g~Take the briefcase back to Diaz. + +[GEN3_29:GENERA3] +~g~Collect the briefcase and take it back to Diaz. + +[GEN3_30:GENERA3] +~r~He got away with the money! Diaz will have your balls for this! + +[GEN3_33:GENERA3] +~r~Check your fire!! You're supposed to be watching over Diaz and his men, not shooting them! + +[GEN3_34:GENERA3] +~r~There ain't gonna be a deal if you shoot the Cubans! + +[GEN3_35:GENERA3] +~g~He's stolen Diaz's money! + +[GEN3_36:GENERA3] +~g~Grab the bike, chase him down and get Diaz's money back! + +[GEN3_37:GENERA3] +~g~Here come the Cubans. Watch over the deal making sure Diaz and Lance are safe. + +[GEN3_38:GENERA3] +~r~Diaz died! You failed to protect him! + +[GEN3_39:GENERA3] +~g~Get to your vantage point up the stairs. + +[GEN3_44:GENERA3] +~g~Go with Lance to the drop off and watch over Diaz. + +[GEN3_45:GENERA3] +They'll be here any minute, we both better get a good vantage point. + +[GEN3_40:GENERA3] { reVC update } +To ~h~shoot straight ahead ~w~on a ~h~motorbike ~w~press the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button. + +[GEN3_41:GENERA3] { reVC update } +To ~h~shoot straight ahead ~w~on a ~h~motorbike ~w~press the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button. + +[GEN3_46:GENERA3] +Sheeit! + +[GEN3_47:GENERA3] +Tommy! + +[GEN3_48:GENERA3] +Damn! + +[GEN3_49:GENERA3] +lance's health: + +[GEN3_50:GENERA3] +~r~You lost Diaz's money! Next time try not to reduce the money to ashes! + +[GEN3_51:GENERA3] +More damned Haitians in a shitty van! + +[GEN3_54:GENERA3] +Don't just stand there, you pricks, chase that Haitian dickhead down! + +[GEN3_55:GENERA3] +Tommy! I'll stay here and watch over Diaz! + +[GEN3_18:GENERA3] +~g~Here come the Cubans, keep close to Diaz. Watch over the deal making Diaz and Lance are safe. + +[GEN3_56:GENERA3] +~r~Diaz was ambushed and died! Next time keep him in your sights! + +[GEN3_57:GENERA3] +The Kruger is an assault rifle, which allows you to manually aim in 1st person. + +[GEN3_58:GENERA3] +Press and hold the~h~ ~k~~PED_LOCK_TARGET~~w~ button to ~h~aim~w~ with an assault rifle. + +[GEN3_59:GENERA3] +Press and hold the~h~ ~k~~PED_LOCK_TARGET~~w~ button to ~h~aim~w~ with an assault rifle. + +[GEN3_60:GENERA3] +Press the ~h~~k~~PED_FIREWEAPON~~w~ button to ~h~fire~w~ an assault rifle. + +[GEN3_61:GENERA3] +Press the ~h~~k~~PED_FIREWEAPON~~w~ button to ~h~fire~w~ an assault rifle. + +[GEN3_62:GENERA3] +Press the ~h~~k~~PED_FIREWEAPON~~w~ button to ~h~fire~w~ an assault rifle. + +[GEN3_63:GENERA3] +As well as performing drive-by's,~h~ motorbikes ~w~allow you to ~h~shoot forwards~w~. + +[GEN3_64:GENERA3] { reVC update } +To shoot forwards while on a bike press the ~h~~k~~VEHICLE_FIREWEAPON~~w~ button. + +[GEN3_65:GENERA3] { reVC update } +To shoot forwards while on a bike press the ~h~~k~~VEHICLE_FIREWEAPON~~w~ button. + +[GEN3_66:GENERA3] { reVC update } +To shoot forwards while on a bike press the ~h~~k~~VEHICLE_FIREWEAPON~~w~ button. + +[GEN3_67:GENERA3] +You must have a sub machine gun to shoot forwards on a motorbike. + +[GEN3_53:GENERA3] +MY MONEY! + +[GEN3_52:GENERA3] +These Haitians think they can take RICARDO DIAZ!?! + +{=================================== MISSION TABLE GENERA4 ===================================} + +[DETON:GENERA4] +DETONATION: + +[COL4_3:GENERA4] +CONVOY HALT! + +[COL4_6:GENERA4] +WE'RE TAKING ENEMY FIRE! + +[COL4_7:GENERA4] +Civilian, move away from the tank! + +[COL4_8:GENERA4] +I SAID, move away, IMMEDIATELY! + +[COL4_9:GENERA4] +DEFENSIVE POSITIONS! + +[COL4_11:GENERA4] +Get that civilian out of our way soldier! - Sir, Yes Sir! + +[COL4_12:GENERA4] +Civilian in the TANK! STOP HIM! + +[COL4_13:GENERA4] +This is a military convoy, do not obstruct our route. + +[COL4_14:GENERA4] +Drop him soldier. + +[COL4_15:GENERA4] +Get that civilian vehicle out of our way! - Sir! Moving vehicle Sir! + +[COL4_17:GENERA4] +Ok, PLATOON MOVE IT OUT! + +[COL4_18:GENERA4] +Someone's on the tank Sir! + +[COL4_19:GENERA4] +Go get some doughnuts, soldier! - Sir, Yes Sir! + +[COL4_20:GENERA4] +Target acquired, Sir + +[COL4_21:GENERA4] +SNIPER! + +[COL4_22:GENERA4] +I'm getting out of here. + +[COL4_23:GENERA4] +Objective completed! Platoon dismissed! - Lets go eat some doughnuts. + +[COL4_24:GENERA4] +Security protocol Delta India Echo triggered! Vehicle self destruct initiated! + +[COL4_26:GENERA4] +Prepare to die Communist scum! + +[COL4_B2:GENERA4] +~r~The tank arrived at its destination safely! + +[COL4_B5:GENERA4] +~r~The tank has been destroyed! + +[COL4_01:GENERA4] +Diaz was pleased, and would like to meet you again. + +[COL4_02:GENERA4] +Is that a good thing? + +[COL4_03:GENERA4] +Of course! Although I'm starting to think that Diaz was responsible for our unfortunate loss... + +[COL4_04:GENERA4] +What makes you say that? + +[COL4_05:GENERA4] +One does not wave accusations at a man like Diaz - I'm merely thinking out loud... + +[COL4_06:GENERA4] +No matter. I have a proposal that you could profit from... + +[COL4_07:GENERA4] +I don't have time to run more errands, Cortez. + +[COL4_08:GENERA4] +I would have thought a man with such dangerous debts would be hungry for opportunities. Please, Tommy, at least hear me out. + +[COL4_09:GENERA4] +Go on... + +[COL410:GENERA4] +I have a buyer for a piece of military hardware that is being taken through town. Pick it up for me... + +[COL411:GENERA4] +and once you get it, I want you to call me immediately, then... + +[COL4_B4:GENERA4] +~g~The tank is locked. Find a way to lure out the occupants. + +[COL4_1:GENERA4] +What's up with the Gunner? - Don't know Sir! + +[COL4_4:GENERA4] +Get topside soldier. - Sir, Yes Sir! + +[COL4_B1:GENERA4] +~g~Go and acquire the piece of military hardware that is being taken through town. + +[COL4_B3:GENERA4] +~g~Drop the tank off in the Colonels lockup before it self destructs. + +[COL4_B6:GENERA4] +~g~Find a way to steal the tank! + +[COL4_B7:GENERA4] +~g~Drive the tank into the garage. + +[COL4_B8:GENERA4] +~g~Get out of the tank and walk out of the garage. + +{=================================== MISSION TABLE GENERA5 ===================================} + +[COL5A_1:GENERA5] +Circumstances force a hasty departure, amigo. + +[COL5A_2:GENERA5] +What's the problem? + +[COL5A_3:GENERA5] +Ehh, the French want their missile technology back and after that last incident, + +[COL5A_4:GENERA5] +I feel it is time to find safer harbors. + +[COL5A_5:GENERA5] +Wouldn't it be safer to fly? + +[COL5A_6:GENERA5] +I'd be dead before I reached check-in. Besides, I need to get my merchandise out of the country. + +[COL5A_7:GENERA5] +Need another gun? + +[COL5A_8:GENERA5] +You, my friend, are worth ten guns... + +[COL5B_1:GENERA5] +Thomas, you have protected and served me well. + +[COL5B_2:GENERA5] +But now you must leave us before we reach the open seas. + +[COL5B_4:GENERA5] +Thank you, Colonel. + +[COL5B_5:GENERA5] +One more request. While I'm away, could you keep an eye on Mercedes for me? + +[COL5B_6:GENERA5] +I think she could look after herself, but sure, I'll keep an eye out. + +[COL5B_7:GENERA5] +Gracias, amigo. Hasta luego. + +[COL5B_8:GENERA5] +Adios, amigo. + +[COL5_7:GENERA5] +Stop shooting at me! + +[COL5_9:GENERA5] +Tommy, stop them shooting at me! + +[COL5_10:GENERA5] +I have diplomatic immunity! + +[COL5_11:GENERA5] +Don't shoot, I am a Colonel! + +[COL5_12:GENERA5] +Thomas, kill them, my country will love you. + +[COL5_13:GENERA5] +Tommy, we are being overrun by the French! + +[COL5_14:GENERA5] +Tommy, everywhere I look, there are French men, I hate it! + +[COL5_15:GENERA5] +Tommy, how are you? + +[COL5_16:GENERA5] +This is for Piaf and Gainesbourg and your stupid french bread! + +[COL5_1:GENERA5] +Port side! Port side! + +[COL5_2:GENERA5] +They're attacking from starboard! + +[COL5_3:GENERA5] +The bridge up ahead! + +[COL5_4:GENERA5] +They've got a helicopter! + +[COL5_B1:GENERA5] +~g~Defend the Colonel and his yacht at all costs. + +[COL5_B2:GENERA5] +~g~Get up front and clear the route for the Colonel's yacht. + +[COL5_B3:GENERA5] +~r~The Colonel is dead! + +[COL5_B4:GENERA5] +~g~Shoot the attacking helicopter out of the sky. + +[COL5B_3:GENERA5] +I will lower my personal launch. Keep it, my friend, a token of my gratitude. + +[COL5_B5:GENERA5] +~g~Shoot down the helicopters, do not endanger the yacht. + +[COL5_B6:GENERA5] +~g~You have run out of ammo, get more from the stairs on the top deck. + +[COL5_B7:GENERA5] +~g~You are running low on health, get more from the stairs on the top deck. + +{=================================== MISSION TABLE HAIT1 ===================================} + +[HAM1_A:HAIT1] +Hello? Hello? + +[HAM1_C:HAIT1] +You must be the big bad man me grandaddy been chattin' 'bout. + +[HAM1_D:HAIT1] +Tells me tings about you, you know, when he visits, + +[HAM1_E:HAIT1] +and about the others who wait for you. + +[HAM1_F:HAIT1] +Now, we all dead for long time, but you, + +[HAM1_G:HAIT1] +I wouldn't want to be in your shoes, ha ha ha ha ha! + +[HAM1_H:HAIT1] +I got a message to come here. + +[HAM1_I:HAIT1] +Can you hear dem? + +[HAM1_J:HAIT1] +Dem callin' your name, boy, must want you pretty bad, don't ya tink? + +[HAM1_K:HAIT1] +Now you do old Auntie Poulet a turn, huh, maybe she help you. + +[HAM1_L:HAIT1] +Maybe she can give you a little juju after all of dis. + +[HAM1_M:HAIT1] +Give you some magic to give the law man the stink eye, hmmmmm? + +[HAM1_N:HAIT1] +Look, this is all very, um... give me what? + +[HAM1_O:HAIT1] +I,I, I think I've got the wrong address... + +[HAM1_P:HAIT1] +Do me these tings, Tommy...... + +[HAM1_Q:HAIT1] +The Cubans, nasty proud foofoos, mmm, + +[HAM1_R:HAIT1] +been making my lovely Haitian boys shake de heads. + +[HAM1_S:HAIT1] +Now they told the policeman where me been stashing my powders. + +[HAM1_T:HAIT1] +Dey tink it drugs, them stupid. + +[HAM1_U:HAIT1] +Now be a good boy Tommy and go and get the powders for Auntie Poulet. + +[HAM1_V:HAIT1] +Yeah, yeah, sure, sure. + +[HAM1_1:HAIT1] +~g~The cops are closing in on our stashes. BE quick, and beat dem to it! + +[HAM1_2:HAIT1] +~r~The cops got to the stash first! + +[HAM1_3:HAIT1] +~g~Get this stuff back to the hideout! + +[HAM1_4:HAIT1] +~g~Good! Now get the next one! + +[HAM1_6:HAIT1] +~r~The Stash was destroyed, you idiot! + +[HAM1_7:HAIT1] +~g~The cops have got our stash! Retrieve it before they get away! + +[HAM1_8:HAIT1] +~g~The cops are on the way to pick up the stash, get a move on! + +[HAT_1A:HAIT1] +~g~Don't move a muscle, chump! + +[HAM1_B:HAIT1] +Come in, my dear, and rest your soul. + +{=================================== MISSION TABLE HAIT2 ===================================} + +[HAT2_B1:HAIT2] +~g~Get to the van that contains the flying bombs. + +[HAT2_B2:HAIT2] +Kill the Cubans... + +[HAT2_B4:HAIT2] +...and destroy their boats! + +[HAT2_B5:HAIT2] +~g~The Cubans are making a run for it. Don't let them get away! + +[HAT2_B6:HAIT2] +~r~The RC plane is getting too far out of range! + +[HAT2_B7:HAIT2] +~g~One of the Cubans in escaping in a car. Don't leave any witnesses! + +[HAT2_B8:HAIT2] +~r~You have no RC planes left! + +[HAT2_B9:HAIT2] +RC Planes: + +[HAT2_1:HAIT2] +Oh, sorry, I - I must have the wrong address... + +[HAT2_2:HAIT2] +Well, you might as well come in and rest your soles and have some tea. + +[HAT2_3:HAIT2] +Do you have something there for me, Tommy? + +[HAT2_4:HAIT2] +Yeah... + +[HAT2_5:HAIT2] +This place feels familiar to me, uh - it's - a smell from childhood - a deja vu... + +[HAT2_6:HAIT2] +Now Tommy, I'm going to whisper a lickle errand for you. Hear me well, aye? + +[HAT2_7:HAIT2] +You look like someone I, I... + +[HAT2_8:HAIT2] +The Cubans have fast boats they use to cross the seas with drugs. + +[HAT2_9:HAIT2] +It is their livelihood. + +[HAT2_10:HAIT2] +Me nephew bin making lickle flying bombs to take dem out. + +[HAT2_11:HAIT2] +Blow de boats to coffin wood. + +[HAT2_12:HAIT2] +Thanks for the tea. + +[HAT2_B3:HAIT2] { reVC update } +Press the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button to drop a bomb. Press the ~h~~k~~VEHICLE_ENTER_EXIT~ ~w~button to cancel. + +{=================================== MISSION TABLE HAIT3 ===================================} + +[HAM3_A:HAIT3] +Hello? Hello - uh..I'm looking for somebody around here... + +[HAM3_B:HAIT3] +You looking hungry, Tommy. + +[HAM3_C:HAIT3] +Do I know you? + +[HAM3_D:HAIT3] +Hush now. + +[HAM3_E:HAIT3] +One more ting an I can let you go, Tommy. + +[HAM3_F:HAIT3] +My boys gone war wit dem Cuban boys. + +[HAM3_G:HAIT3] +But no guns. + +[HAM3_H:HAIT3] +Hmm, but de Cubans have a surprise comin'. + +[HAM3_I:HAIT3] +While they fight in de streets, you take this rifle and kill dem in de hubbub. + +[HAM3_J:HAIT3] +No one sees you, no one hear you. + +[HAM3_K:HAIT3] +Now, Tommy, you do this for me, and you no longer tied to my apron strings. + +[HAM3_1:HAIT3] +~g~We must win this battle. If all the Haitians die we lose. + +[HAM3_3:HAIT3] +~g~I expect the Cubans to cheat so be on your guard. + +[HAM3_4:HAIT3] +~r~You have been spotted! The mission is a failure! + +[HAM3_5:HAIT3] +~g~You must kill the Cubans from a distance. You must not be seen. + +[HAM3_8:HAIT3] +~g~Haitians are dying! Improve your aim. + +[HAM3_7:HAIT3] +~g~Look Out! The Cubans have brought reinforcements. Kill them all!! + +[HAM3_2:HAIT3] +~r~The Haitians have died! + +[HAM3_L:HAIT3] +Kay auntie.. + +{=================================== MISSION TABLE HOTEL ===================================} + +[INTB_A:HOTEL] +Tommy! Tommy, it's been too long. + +[INTB_B:HOTEL] +Hello Sonny. + +[INTB_C:HOTEL] +I know, I know. You're just overwhelmed with emotion. + +[INTB_D:HOTEL] +Fifteen years - seems like only yesterday. + +[INTB_E:HOTEL] +I guess that's a perspective thing. + +[INTB_F:HOTEL] +Hey, doing time for the family is no piece of cake, + +[INTB_G:HOTEL] +but the family looks after its own, ok? + +[INTB_H:HOTEL] +So, how'd the deal go down - you sitting on some white gold? + +[INTB_I:HOTEL] +Look Sonny, we were set up. The deal was an ambush. Harry and Lee are dead. + +[INTB_J:HOTEL] +You better be kidding me Tommy. Tell me you still got the money. + +[INTB_K:HOTEL] +...no Sonny...I don't have the money. + +[INTB_L:HOTEL] +That was my money, Tommy, MY MONEY! + +[INTB_M:HOTEL] +You better not be screwing me Tommy because you know I'm not a man to be screwed with! + +[INTB_N:HOTEL] +Wait Sonny. + +[INTB_O:HOTEL] +You have my personal assurance that I'm going to get your money back and the drugs. + +[INTB_P:HOTEL] +And I'm gonna mail you the dicks of those responsible. + +[INTB_Q:HOTEL] +Hey, I already know that. You're not a fool Tommy, but I warn you, neither am I. + +[INTB_R:HOTEL] +If it was anybody else you'd be DEAD already. + +[INTB_S:HOTEL] +But because it's you, because we got history, I'm gonna let you handle this. + +[INTB_T:HOTEL] +Look, Sonny, you got my word. + +[INTB_U:HOTEL] +I'll be in touch. + +{=================================== MISSION TABLE ICECRE1 ===================================} + +[ICC1_1:ICECRE1] +~g~Use your Ice Cream van distribute drugs around Vice City. + +[ICC1_3:ICECRE1] +~g~You receive money for each transaction you make, but the more transactions you make the more police attention you get. + +[ICC1_4:ICECRE1] +~g~There aren't any customers in this area try another one. + +[ICC1_5:ICECRE1] +Deals done: + +[ICC1_6:ICECRE1] +~g~Use the Mr. Whoopee van to distribute Cherry Poppers product around Vice City. + +[ICC1_7:ICECRE1] +~g~You receive money for each transaction you make, but the more transactions you make the more police attention you get. + +[ICC1_9:ICECRE1] +~g~Local gangs will not appreciate you doing business on their turf so expect hostility if you do so. + +[ICC1_10:ICECRE1] +~g~You made ~1~ deals! + +[ICC1_11:ICECRE1] +~g~You made ~1~ deal! + +[ICC1_12:ICECRE1] +PROPERTY ACQUIRED! + +[ICC1_13:ICECRE1] +~r~You didn't make any deals! + +[ICC1_14:ICECRE1] +ICECREAM ASSET COMPLETED + +[ICC1_15:ICECRE1] +~g~The icecream factory will now generate revenue up to a maximum of $~1~. Make sure you collect it regularly. + +[ICC1_2:ICECRE1] +~g~Park your van and press the ~h~~k~~VEHICLE_HORN~~w~ to play your ice cream jingle to notify customers that your ready for business. + +[ICC1_16:ICECRE1] +~g~Use your Mr. Whoopee van to distribute Cherry Poppers product around Vice City. + +[ICC1_8:ICECRE1] +~g~To make a transaction, ~h~park your van ~g~and press the ~h~~k~~VEHICLE_HORN~ ~g~button to play the ice cream jingle to attract customers. + +[ICE_AT1:ICECRE1] +ICECREAM FACTORY ASSET COMPLETED + +[ICE_AT2:ICECRE1] +~g~The Cherry Popper factory will now generate revenue up to a maximum of $~1~. Make sure you collect it regularly. + +[ICC1_17:ICECRE1] +Distribution mission over + +[ICC1_18:ICECRE1] +Total ice cream sales: $~1~ + +[ICC1_19:ICECRE1] +Total deals done: ~1~ + +{=================================== MISSION TABLE ICECUT ===================================} + +[ICC1_A:ICECUT] +Who are you? + +[ICC1_B:ICECUT] +Your new owner. + +[ICC1_C:ICECUT] +Were you now, or at any time, a child? + +[ICC1_D:ICECUT] +What are you talking about? + +[ICC1_E:ICECUT] +Were you a child? + +[ICC1_F:ICECUT] +Yes! Calm down! What's wrong with you? + +[ICC1_G:ICECUT] +I knew it. A child. + +[ICC1_H:ICECUT] +A dirty, stinking, sniveling, snotting, vile, puking, crying little baby! + +[ICC1_K:ICECUT] +Ow! Calm down. + +[ICC1_L:ICECUT] +I HATE babies, and I hate children. + +[ICC1_N:ICECUT] +Enough already! + +[ICC1_P:ICECUT] +You make soft ice cream, okay? It's purely for kids. + +[ICC1_Q:ICECUT] +What kind of psycho are you? + +[ICC1_R:ICECUT] +Just so I understand this, why make children happy if you hate them? + +[ICC1_S:ICECUT] +Oh, you stupid, sniveling, snotty- + +[ICC1_T:ICECUT] +Shut up! + +[ICC1_U:ICECUT] +- Brat! + +[ICC1_V:ICECUT] +The ice cream is a front. + +[ICC1_W:ICECUT] +We distribute other, non-dairy products. + +[ICC1_X:ICECUT] +And if I see a kid, I put him to good use. + +[ICC1_Y:ICECUT] +Don't I, kiddies? Yes - yes, I do. Mummy doesn't love you. + +[ICC1_Z:ICECUT] +She HATES you! + +[ICC1_ZA:ICECUT] +PROPERTY ACQUIRED! + +[ICC1_M:ICECUT] +They're dirty, sniveling, snotting, vile, puking little.. + +[ICC1_I:ICECUT] +A baby.. an awful, horrible, disgusting little boo hoo. + +[ICC1_J:ICECUT] +Mommy doesn't love you. You little shit! + +{=================================== MISSION TABLE INTRO ===================================} + +[INT1_A:INTRO] +Tommy Vercetti...Huh! shit. + +[INT1_B:INTRO] +Didn't think they'd ever let him out. + +[INT1_C:INTRO] +He kept his head down, helps people forget. + +[INT1_D:INTRO] +People will remember soon enough. + +[INT1_E:INTRO] +When they see him walking down the streets of their neighborhoods. + +[INT1_F:INTRO] +It will be bad for business. + +[INT1_G:INTRO] +Well, what are we gonna do, Sonny? + +[INT1_H:INTRO] +We treat him like an old friend and keep him busy out of town. OK? + +[INT1_I:INTRO] +We been talking about expanding down South, right? + +[INT1_J:INTRO] +Vice City is twenty-four carat gold these days. + +[INT1_K:INTRO] +The Colombians, the Mexicans, hell, + +[INT1_L:INTRO] +even those Cuban refugees are cutting themselves a piece of some nice action. + +[INT1_M:INTRO] +But it's all drugs, Sonny, + +[INT1_N:INTRO] +None of the families will touch that shit! + +[INT1_O:INTRO] +Times are changing. + +[INT1_P:INTRO] +The families can't keep their backs turned while our enemies reap the rewards. + +[INT1_Q:INTRO] +So, we send someone down to do the dirty work for us... + +[INT1_R:INTRO] +and cut ourselves a nice quiet slice. OK? + +[INT1_S:INTRO] +Who's our contact down there? + +[INT1_T:INTRO] +Ken Rosenberg, schmuck of a lawyer. + +[INT1_U:INTRO] +How's he gonna hold Vercetti's leash? + +[INT1_V:INTRO] +We don't need him to. + +[INT1_W:INTRO] +We just set him loose in Vice City, + +[INT1_X:INTRO] +we give him a little cash to get started. OK? + +[INT1_Y:INTRO] +Give it a few months. + +[INT1_Z:INTRO] +Then we go down, + +[INT1_A1:INTRO] +pay him a little visit, right? + +[INT1_A2:INTRO] +see how he's doing. + +[INT2_A:INTRO] +Hey, hey, guys! It's, uh, Ken Rosenberg here! Hey! Heh, heh, hey, great, hey! + +[INT2_B:INTRO] +Well, uh, I'm gonna drive you guys to the meet, okay? + +[INT2_C:INTRO] +Now, I've talked to the suppliers and they are very, huh-ha, + +[INT2_D:INTRO] +keen to start a business relationship, so, uh, + +[INT2_E:INTRO] +if all goes well, we should, uh, + +[INT2_F:INTRO] +be doing very nicely for ourselves, which is, y'know... + +[INT2_G:INTRO] +good.. + +[INT2_H:INTRO] +Okay, so. They're brothers, okay. + +[INT2_I:INTRO] +One operates the uh, the business, + +[INT2_J:INTRO] +and the other one does the flying. + +[INT2_K:INTRO] +Now they operate out of Mexico, + +[INT2_M:INTRO] +They own a farm in Panama. + +[INT2_N:INTRO] +Okay, all right, listen - + +[INT2_O:INTRO] +you guys, when we get there should I stay in the car, + +[INT2_P:INTRO] +or do you guys want me to come in with you guys? + +[INT2_Q:INTRO] +No. Stay in the car. + +[INT2_R:INTRO] +You know what, I thought about it, + +[INT2_S:INTRO] +I'm gonna watch the car. + +[INT3_A:INTRO] +Ok, that's them in the chopper. + +[INT3_B:INTRO] +All right, here's the deal. + +[INT3_C:INTRO] +They want a straight exchange on open ground. + +[INT3_D:INTRO] +All right? Ok. Stay tight, let's go. + +[INT3_E:INTRO] +All right. Take it easy, now. + +[INT3_F:INTRO] +I'm right here. The cars running, baby! + +[INT3_G:INTRO] +Got it? + +[INT3_H:INTRO] +100% pure grade-A Colombian, my friend. + +[INT3_I:INTRO] +The greens? + +[INT3_J:INTRO] +Tens and twenties...used. + +[INT3_L:INTRO] +Go on, get out of here! Drive! + +[INT4_A:INTRO] +Screwed! We're screwed! + +[INT4_B:INTRO] +This is soo typical, + +[INT4_C:INTRO] +I poke my head out of the gutter for one freakin' second, + +[INT4_D:INTRO] +and fate shovels shit in my face! + +[INT4_E:INTRO] +Well, screw you! + +[INT4_F:INTRO] +Shut your face and quit complaining! You're alive, aren't ya'? + +[INT4_G:INTRO] +Drop me right up here. + +[INT4_H:INTRO] +Go dump the car, then go get some sleep. + +[INT4_I:INTRO] +I'll drop by your office tomorrow and we can start sorting this mess out. + +[INT4_J:INTRO] +OK, that's a good idea, I'll get some sleep. + +[INT4_K:INTRO] +What are you gonna do? + +[INT4_L:INTRO] +Make my way back to my hotel, + +[INT4_M:INTRO] +clear my head, and figure this crap out. + +[INT4_N:INTRO] +OK. + +[INTRO1:INTRO] +I poke my head out of the gutter for one freakin' second and fate shovels shit in my face! + +[INTRO2:INTRO] +Go get some sleep. + +[INTRO3:INTRO] +What are you gonna do? + +[INTRO4:INTRO] +I'll drop by your office tomorrow and we can start sorting this mess out. + +[INT3_K:INTRO] +I think we have a deal, my friend. HA HA! + +[INT3_M:INTRO] +Let me see it. + +[INT2_L:INTRO] +no, no, no, wait... + +[INT3_N:INTRO] +Oh Shit! + +{=================================== MISSION TABLE KENT1 ===================================} + +[KPM1_A:KENT1] +Awright mush, I'm gonna save your Vera, mate. + +[KPM1_B:KENT1] +What the hell are you talking about? + +[KPM1_C:KENT1] +You know that wanker Diaz, the Bugle Master. + +[KPM1_D:KENT1] +He's got your boy, Lance. Word is your mate tried to jump him... + +[KPM1_E:KENT1] +didn't jump high enough if you know what I mean. + +[KPM1_F:KENT1] +Where did he take him? In plain English? + +[KPM1_G:KENT1] +Keep your barnet on! They got him across town at the junkyard. + +[KPM1_H:KENT1] +Bloody hell....you nutter! + +[KPM1_2:KENT1] +~r~You were supposed to get Lance out alive! + +[KPM1_3:KENT1] +LANCE'S HEALTH: + +[RESC_1:KENT1] +You ok to use a gun? + +[RESC_2:KENT1] +Sure...I guess...nice to see you, too. + +[RESC_3:KENT1] +Let's get out of here. + +[RESC_4:KENT1] +There goes my careful planning blown to shit, thanks to you. You screwed up real good, Lance! + +[RESC_5:KENT1] +He killed my brother. What do you expect me to do, mow his lawns? + +[RESC_6:KENT1] +We're gonna have to take out that prick Diaz before he takes us out. + +[RESC_7:KENT1] +Get patched up and meet me on the bridge to Star Island, ok? + +[RESC_8:KENT1] +Ok, I got you. + +[KPM1_1:KENT1] +~g~Lance is being held at the junk yard, Go and rescue him! + +[KPM1_4:KENT1] +~g~Get Lance to the hospital! + +[M_PASSN:KENT1] +MISSION PASSED! + +[KPM1_5:KENT1] +~g~Diaz's guys are after you! Get Lance to the hospital. + +{=================================== MISSION TABLE KICKSTT ===================================} + +[KICK1_2:KICKSTT] +~r~You did not get back to the bike quickly enough! + +[KICK1_7:KICKSTT] +~r~You have wrecked the bike! + +[KICK1_8:KICKSTT] +~g~Get on the bike! + +[KICK1_T:KICKSTT] +TIME TAKEN: + +[KICKTM:KICKSTT] +~b~EVENT TIME: ~1~:~1~ + +[KICKTM2:KICKSTT] +~b~EVENT TIME: ~1~:0~1~ + +[GETBIKE:KICKSTT] +~g~You have ~1~ seconds to return to a dirtbike before the mission ends. + +[KICK1_1:KICKSTT] +~g~Complete the course as quickly as possible. + +[KICK1_6:KICKSTT] +~g~Well done! + +[KICK_10:KICKSTT] +~g~Use the Sanchez to complete the course by passing through all of the checkpoints. + +[KICK_12:KICKSTT] +~r~You bottled it! + +[KICK_13:KICKSTT] +~r~You have taken too long! + +[KICK_11:KICKSTT] +~g~To leave the mission stand in the ~q~pink marker~g~ on foot. + +{=================================== MISSION TABLE LAWYER1 ===================================} + +[LAW1_A:LAWYER1] +Go get some sleep, he says - + +[LAW1_B:LAWYER1] +- I have been sitting in this chair all night with the lights off drinking coffee! + +[LAW1_C:LAWYER1] +This is a disaster. We are so screwed, man! + +[LAW1_D:LAWYER1] +These gorillas, listen to me, are gonna come down here and rip my head off. It's ridiculous! + +[LAW1_E:LAWYER1] +I did NOT go to law school for this! Ok, now what the hell are we gonna do? + +[LAW1_F:LAWYER1] +Shut up, sit down, relax. I'll tell you what we're gonna do. + +[LAW1_G:LAWYER1] +You're gonna find out who took our cocaine - and then, I'm gonna kill them. + +[LAW1_H:LAWYER1] +That's a good idea. That's a GREAT idea. Let me think, let me think, let me think. + +[LAW1_I:LAWYER1] +- OH! There's this retired Colonel, Colonel Juan Garcia Cortez. + +[LAW1_J:LAWYER1] +He's the one that helped me set up this deal + +[LAW1_K:LAWYER1] +well away from Vice City's established thugs. Ok? + +[LAW1_L:LAWYER1] +Now, listen. He's holding his party out in the bay on his expensive yacht + +[LAW1_M:LAWYER1] +and all of Vice City's big players are gonna be there. OK? + +[LAW1_N:LAWYER1] +I have an invite, of course I have an invite, + +[LAW1_O:LAWYER1] +but there's no way that I'm going out there, sticking my head out the door - no way! Not gonna happen. + +[LAW1_P:LAWYER1] +I told you, shut up! I'll go myself... + +[LAW1_Q:LAWYER1] +Ho - whoa, whoa! Hey, I like 1978 too, but, y'know, this isn't gonna be a beer and strippers do. + +[LAW1_R:LAWYER1] +I mean, no offense, but I think that you might turn heads on the runway for the wrong reasons. + +[LAW1_S:LAWYER1] +What's wrong with the way I'm dressed? + +[LAW1_T:LAWYER1] +Ok, look, here. Stop by Rafael's, tell him I sent 'ya. He'll make you look respectable. + +[LAW1_U:LAWYER1] +OK, go, c'mon... + +[LAWP_1:LAWYER1] +Buenas noches. + +[LAWP_2:LAWYER1] +I understand you are here on the behalf of Mr. Rosenberg, + +[LAWP_3:LAWYER1] +I hope any recent problems have not affected his health, or uh, + +[LAWP_4:LAWYER1] +mental well being, Mr...uh? + +[LAWP_5:LAWYER1] +Vercetti. He's just got a touch of...agoraphobia. + +[LAWP_6:LAWYER1] +Excellent, excellent. And you? + +[LAWP_7:LAWYER1] +I just want my merchandise. + +[LAWP_8:LAWYER1] +Ah. It's an unfortunate set of circumstances for all involved. + +[LAWP_9:LAWYER1] +Of course I have initiated my own lines of inquiry, + +[LAWP_10:LAWYER1] +but such a delicate matter will take time. + +[LAWP_11:LAWYER1] +Perhaps we will talk later. + +[LAWP_12:LAWYER1] +Meanwhile, let me introduce you to my daughter, + +[LAWP_13:LAWYER1] +Mercedes! + +[LAWP_14:LAWYER1] +Caramia, could you look after our guest while I attend to my necessary obligations? + +[LAWP_15:LAWYER1] +Of course, daddy. + +[LAWP_16:LAWYER1] +Please excuse me. + +[LAWP_17:LAWYER1] +Mercedes!? + +[LAWP_18:LAWYER1] +You try living with it. + +[LAWP_19:LAWYER1] +Anyway, let me point out some of our more distinguished guests... + +[LAWP_20:LAWYER1] +That's our congressman Alex Shrub with rising silicone star Candy Suxxx... + +[LAWP_21:LAWYER1] +And have you met my lovely wife Laura? No? + +[LAWP_22:LAWYER1] +Well, unfortunately she's in Alabama. This is Candy. + +[LAWP_23:LAWYER1] +And over there we have the Vice City Mambas' star tight end, BJ - + +[LAWP_24:LAWYER1] +always the charmer. + +[LAWP_25:LAWYER1] +I blocked down on him and then I put him in a wheelchair! + +[LAWP_26:LAWYER1] +Haha, that is good! + +[LAWP_27:LAWYER1] +Well now, I'm looking at some prime real estate property. + +[LAWP_28:LAWYER1] +And that poolside amphibian is Jezz Torrent, + +[LAWP_29:LAWYER1] +lead singer with Love Fist. + +[LAWP_30:LAWYER1] +Can I tell yous - do you know how they play ping-pong in Thailand? + +[LAWP_31:LAWYER1] +Let me tell you's, + +[LAWP_32:LAWYER1] +it does not involve a paddle, if you know what I mean! + +[LAWP_33:LAWYER1] +Impotent. + +[LAWP_34:LAWYER1] +And the chatty trio. + +[LAWP_35:LAWYER1] +That sleeping sweat gland is Papa's right hand gimp, Gonzalez + +[LAWP_36:LAWYER1] +and the other two are Pastor Richards + +[LAWP_37:LAWYER1] +and pseudo intellectual film director, Steve Scott. + +[LAWP_38:LAWYER1] +...passion with the nympho invaders, + +[LAWP_39:LAWYER1] +when the giant shark comes in and + +[LAWP_40:LAWYER1] +just bites their dicks off! + +[LAWP_41:LAWYER1] +Ha now, you never saw anything like that before, have you? + +[LAWP_42:LAWYER1] +Colonel! + +[LAWP_43:LAWYER1] +your parties as ever are a triumph, hahahaha! + +[LAWP_44:LAWYER1] +I can only apologize for my late arrival. + +[LAWP_45:LAWYER1] +Ah, de nada amigo. How do we find you? + +[LAWP_46:LAWYER1] +Our business is very trying - barbarians at the gates. + +[LAWP_47:LAWYER1] +A time for rewarding one's friends and liquidating one's enemies, amigo. + +[LAWP_48:LAWYER1] +Who's the loudmouth? + +[LAWP_49:LAWYER1] +Ricardo Diaz. He's Mr. Coke. + +[LAWP_50:LAWYER1] +Mercedes! + +[LAWP_51:LAWYER1] +Oh, I was just taking my friend back into town. + +[LAWP_52:LAWYER1] +Another time, Ricardo! + +[LAWP_53:LAWYER1] +Let's get out of here. + +[LAWP_54:LAWYER1] +Actually, take me to the Pole Position club. + +[LAW1_2:LAWYER1] +~g~Get to the Colonel's boat. + +[LAW1_4:LAWYER1] +~r~You killed the Colonel's daughter! + +[LAW1_5:LAWYER1] +Will you be working for my father? + +[LAW1_6:LAWYER1] +Maybe. + +[LAW1_7:LAWYER1] +Do you mind me resting my hand in your lap? + +[LAW1_8:LAWYER1] +Maybe... + +[LAW1_9:LAWYER1] +It's so difficult having a rich and powerful father. Vamos. + +[LAW1_10:LAWYER1] +See you around, handsome! + +[LAW1_11:LAWYER1] +I'm sure you will. + +[LAW1_12:LAWYER1] +Hmmmm...nice bike. + +[LAW1_13:LAWYER1] +No! My Bike! + +[LAW1_3:LAWYER1] +~g~Take the Colonel's daughter to the Pole Position club. + +[HELP20:LAWYER1] +Follow the ~h~T-shirt~w~ blip on the radar to find Rafael's. + +[LAW1_14:LAWYER1] +Wow, I like, really dig your motorcycle. + +[LAW1_15:LAWYER1] +Yeah babe, just picked it up from Howlin' Pete's + +{=================================== MISSION TABLE LAWYER2 ===================================} + +[LAW2_A:LAWYER2] +Ah! Well, I hope you're having a good time. Because I'm going out of my mind with worry here. What did you find out? + +[LAW2_B:LAWYER2] +That there are more criminals in this town than in prison. We need a lead from the streets... + +[LAW2_C:LAWYER2] +Ok, let me think, let me think, let me think - + +[LAW2_D:LAWYER2] +- AH! I've got it! + +[LAW2_E:LAWYER2] +Ok, There's this limey, some music industry slimeball, + +[LAW2_F:LAWYER2] +goes by the name of Kent Paul. + +[LAW2_G:LAWYER2] +Anyway, he's got his nose so far up most of Vice City's ass + +[LAW2_I:LAWYER2] +it's this guy, all right? He's always at The Malibu. + +[LAW2_J:LAWYER2] +I'll go pay him a visit. + +[LAW2B_A:LAWYER2] +Where'd you pop up from? + +[LAW2B_B:LAWYER2] +I've been looking for a bird like you for ages, mate... + +[LAW2B_C:LAWYER2] +Kent Paul, mate. Yeah, I'm the guvnor 'round here. + +[LAW2B_D:LAWYER2] +I'm looking for some English guy... + +[LAW2B_E:LAWYER2] +I sort things out, you know what I mean? + +[LAW2B_F:LAWYER2] +I'll treat you. Whatever you want, I'll get you, girl. + +[LAW2B_G:LAWYER2] +Don't you worry about a thing, mate. + +[LAW2B_H:LAWYER2] +Get lost, honey. + +[LAW2B_I:LAWYER2] +Oi oi oi oi oi! + +[LAW2B_J:LAWYER2] +You Kent Paul? I'm a friend of Rosenberg's... + +[LAW2B_K:LAWYER2] +Rosenberg...Rosenberg...Oh, that bonkers ambulance chaser! + +[LAW2B_L:LAWYER2] +That guy could defend an innocent man all the way to death row! + +[LAW2B_M:LAWYER2] +Give us another drink, bruv. + +[LAW2B_N:LAWYER2] +Everybody's a comedian. + +[LAW2B_O:LAWYER2] +Listen to me, I'm missing twenty keys and a lot of cash... + +[LAW2B_P:LAWYER2] +Drugs, mate? It's a mug's game. + +[LAW2B_Q:LAWYER2] +What do you know about it? + +[LAW2B_R:LAWYER2] +Oi oi! What I was coming to was, + +[LAW2B_S:LAWYER2] +there's some chef-cum-trumpetshifter who deals out kitchen of a hotel on Ocean Drive. + +[LAW2B_T:LAWYER2] +He's been looking real pleased with himself lately. You could go and check him out...?! + +[LAW2B_U:LAWYER2] +I will - and I'll be seeing you around. + +[LAW2B_V:LAWYER2] +Yeah, that's right. Go on - walk away, you mug. I'll knock you spark out! + +[LAW2B_W:LAWYER2] +Give me a drink - and where's that slut! + +[LAW2C_A:LAWYER2] +Oh, way to go, tough guy. Beat him to a pulp. That should make him real chatty. + +[LAW2C_B:LAWYER2] +You want some, too? + +[LAW2C_C:LAWYER2] +Hey, chill. I want what you want, brother. + +[LAW2C_D:LAWYER2] +Oh, yeah? And what's that? + +[LAW2C_E:LAWYER2] +Your green - and my dead brother's white lady. Unfortunately, you just silenced our lead. + +[LAW2C_F:LAWYER2] +Accidents happen. Get lost. + +[LAW2C_G:LAWYER2] +Hey, hey, whoa. No need to go all 'Lone Ranger' on my ass. + +[LAW2C_H:LAWYER2] +The way I see it - we two hombres in a strange town. We need to watch each other's back. + +[LAW2C_I:LAWYER2] +My back's just fine, brother... + +[LAW2C_J:LAWYER2] +You sure about that? Here, take this. + +[LAW2C_K:LAWYER2] +Follow me! + +[LAW2_1:LAWYER2] +Hey, whatchoo lookin' at? + +[LAW2_2:LAWYER2] +You better start talking.. + +[LAW2_3:LAWYER2] +Hey, make me, you prick! + +[LAW2_4:LAWYER2] +This way! + +[LAW2_5:LAWYER2] +I'm going to go see what I can dig up. I'll be watching you, Tommy. + +[LAW2_6:LAWYER2] +~g~Go to the Malibu Club and find Kent Paul. + +[LAW2_7:LAWYER2] +~g~Go and find the chef on Ocean Drive. + +[LAW2_10:LAWYER2] +~g~Drive back to the hotel. + +[LAW2_11:LAWYER2] +~g~Pick up his cell phone. + +[LAW2_12:LAWYER2] +Cell phone acquired! You can now receive phone calls. + +[LAW2_13:LAWYER2] +~g~You've left Lance behind! Go and get him! + +[LAW2_14:LAWYER2] +We gotta get the hell outta here! + +[GUN_2A:LAWYER2] +Hold the ~h~~k~~PED_LOCK_TARGET~ ~w~button to ~h~auto-target~w~, press the ~h~~k~~PED_FIREWEAPON~ ~w~button to ~h~fire! + +[GUN_2C:LAWYER2] +Hold the ~h~~k~~PED_LOCK_TARGET~ ~w~button to ~h~auto-target~w~, press the ~h~~k~~PED_FIREWEAPON~ ~w~button to ~h~fire! + +[GUN_2D:LAWYER2] +Hold the ~h~~k~~PED_LOCK_TARGET~ ~w~button to ~h~auto-target~w~, press the ~h~~k~~PED_FIREWEAPON~ ~w~button to ~h~fire! + +[HELP17:LAWYER2] +Press the ~h~~k~~PED_FIREWEAPON~ ~w~button to attack the chef. + +[HELP18:LAWYER2] +Press the~h~ ~k~~PED_FIREWEAPON~ ~w~button to attack the chef. + +[LAW3_11:LAWYER2] +Stand in the ~q~pink marker~w~ to view the weapons on offer. + +[LAW3_12:LAWYER2] +You can select weapons by pressing ~h~left~w~ or ~h~right~w~ on the ~h~directional button. + +[LAW3_13:LAWYER2] +If you have enough cash you can buy weapons by pressing the ~h~~k~~PED_SPRINT~ ~w~button. + +[LAW3_14:LAWYER2] +You can exit the shop by pressing the ~h~~k~~VEHICLE_ENTER_EXIT~ ~w~button. + +[LAW3_15:LAWYER2] +Follow the ~h~Gun blip~w~ on the radar to find ~h~Ammu-Nation. + +[LAW2_15:LAWYER2] +~g~Go to Ammu-Nation. + +[LAW2_H:LAWYER2] +that if anybody knows the whereabouts of 20 k's of coke, + +[LAW2_K:LAWYER2] +Take it easy now. + +[LAW2_16:LAWYER2] +One thing you gotta realize about this town. You gotta pack some heat. + +[LAW2_17:LAWYER2] +C'mon, the local gun shop's a couple of blocks away. + +[LAW2_18:LAWYER2] +Tommy, every man needs a little R&R once in a while. + +[LAW2_19:LAWYER2] +This here's the Pole Position Strip Club. You might want to drop in some time. + +{=================================== MISSION TABLE LAWYER3 ===================================} + +[LAW3_A:LAWYER3] +Aaah! Oh, for god's sake, it's you! Oh, Jeez - I'm gonna need new pants! + +[LAW3_B:LAWYER3] +Hey, those psychos from up north - they've been on the horn, and they're coming down here soon. + +[LAW3_C:LAWYER3] +Now where is the goddamn money?! + +[LAW3_D:LAWYER3] +Relax, relax. We're not at that part yet. + +[LAW3_E:LAWYER3] +Ohhh... I thought that you were taking care of this, I really did! + +[LAW3_F:LAWYER3] +And now those guidos say we gotta do them a favor. + +[LAW3_G:LAWYER3] +You mean I gotta do 'em a favor. + +[LAW3_H:LAWYER3] +Oh, of course that's what I mean. Do I look like I can intimidate a jury? + +[LAW3_I:LAWYER3] +I couldn't intimidate a child - and believe me, I've tried. + +[LAW3_J:LAWYER3] +Now, look. It's either that, or Forelli's cousin, Giorgio, gets five years for fraud. + +[LAW3_K:LAWYER3] +You gotta take these guys OUT! + +[LAW3_L:LAWYER3] +I understand. Help the jury change their minds. Don't worry about it. + +[LAW3_M:LAWYER3] +No no no no no - NO! I tried that. The jury case didn't go so well, + +[LAW3_N:LAWYER3] +so MAKE them change their minds. + +[LAW3_1:LAWYER3] +Giorgio sends his regards. + +[LAW3_2:LAWYER3] +Remember, guilty is a dirty word. + +[LAW3_3:LAWYER3] +Innocent until I say otherwise. + +[LAW3_4:LAWYER3] +You know he's not guilty. + +[LAW3_5:LAWYER3] +You remember Giorgio? You remember he's innocent. + +[LAW3_6:LAWYER3] +Not guilty, understand? Good. + +[LAW3_8:LAWYER3] +~r~You killed a Juror! + +[LAW3_9:LAWYER3] +~g~Smash up the Juror's car to get him out! + +[HELP40:LAWYER3] +You can smash cars up by using the hammer or a similar weapon. + +[HELP41:LAWYER3] +or you can ram them with a vehicle + +[LAW3_20:LAWYER3] +~g~Smash up the Juror's car! + +[LAW3_21:LAWYER3] +I can't believe this is happening! + +[LAW3_22:LAWYER3] +Unbelievable! + +[LAW3_23:LAWYER3] +Ok! Ok, man! I get the message! + +[LAW3_24:LAWYER3] +~g~That hammer would be useful. + +[LAW3_7:LAWYER3] +~g~Go and intimidate the two jurors, but DON'T kill them! + +[HELP23:LAWYER3] +You can follow the ~h~hammer blip~w~ on the radar if you want to buy melee weapons from the hardware store. + +[LAW3_16:LAWYER3] +Dumb Florida Moron. + +[LAW3_17:LAWYER3] +Get out of the way! + +{=================================== MISSION TABLE LAWYER4 ===================================} + +[LAW4_A:LAWYER4] +Avery, it goes without saying... Tommy! Tommy! Any progress? No, no, no - tell me later, tell me later. + +[LAW4_B:LAWYER4] +Tommy, this is Avery Carrington - I believe you met at the party? + +[LAW4_C:LAWYER4] +Not in person. + +[LAW4_D:LAWYER4] +Howdy. + +[LAW4_E:LAWYER4] +Avery here has a proposition. + +[LAW4_F:LAWYER4] +Haven't we got other things on our mind? + +[LAW4_G:LAWYER4] +I'm trying to keep the wolves from the door, so could you please cut me some slack? + +[LAW4_H:LAWYER4] +I'm stretched like a wire and even if I'm dead by the end of the week, I'd like to think that I didn't die poor. + +[LAW4_I:LAWYER4] +Now just calm down, both of you. + +[LAW4_J:LAWYER4] +Son, you help me and any greaseballs giving you a hard time, I'll see to it they take a long dirt nap. + +[LAW4_K:LAWYER4] +Ok. What could I do for ya'? + +[LAW4_L:LAWYER4] +This delivery company's got its depot on some prime land. They won't sell. + +[LAW4_M:LAWYER4] +They're hanging on like a big old prairie rat, so we gotta go in there and smoke that vermin out. + +[LAW4_N:LAWYER4] +Head on down there and stir up a hornet's nest + +[LAW4_O:LAWYER4] +- the security will have their hands full and then you can sneak in and put 'em out of business. + +[LAW4_P:LAWYER4] +And you could drop by Rafael's for a change of clothes. You might be there a while, but yeah, go for it. + +[LAW4_Q:LAWYER4] +Should be a riot. + +[LAW4_R:LAWYER4] +If the balls drop like they should, stop by my office sometime... + +[LAW4_1:LAWYER4] +Please disperse. The management will discuss any grievances in the appropriate manner! + +[LAW4_2:LAWYER4] +Please disperse. Go back to your homes! + +[LAW4_3:LAWYER4] +Please disperse! This is inappropriate! + +[LAW4_4:LAWYER4] +Please disperse. You will all end up on the streets. + +[LAW4_5:LAWYER4] +Sticks out, boys! Let's crack some commie skulls! + +[LAW4_13:LAWYER4] +~g~Start fighting with at least 4 workers to get a riot started. + +[LAW4_14:LAWYER4] +~g~Destroy the vans in the compound! + +[HELP38:LAWYER4] +If you take out someone who's holding a weapon, they will drop it. + +[HELP39:LAWYER4] +You can target and shoot explosive barrels but keep your distance. + +{=================================== MISSION TABLE MIAMI_1 ===================================} + +[T4X4_1A:MIAMI_1] +~g~You have ~1~ seconds to collect ~y~24~g~ checkpoints. ~g~You may collect them in ~y~ANY ORDER. + +[T4X4_1B:MIAMI_1] +~y~PASS THROUGH~g~ the first checkpoint to start the ~r~TIMER. + +[T4X4_1C:MIAMI_1] +~1~ of 24! + +[GETBIK1:MIAMI_1] +You have ~1~ seconds to get on a PCJ 600! + +[GETBIK3:MIAMI_1] +~r~You need a PCJ 600 to attempt this mission! + +{=================================== MISSION TABLE MM ===================================} + +[BLOD_04:MM] +CAR HEALTH: + +[BLOD_05:MM] +~g~TARGET TIME: ~1~ Minute + +[BLOD_06:MM] +~g~TARGET TIME: ~1~ Minutes + +[BLOD_07:MM] +NEW Best Time: ~1~ Seconds + +[BLOD_08:MM] +Cars Destroyed: ~1~ + +[BLOD_09:MM] +$~1~ + +[BLOD_10:MM] +WINNER!! + +[BLOD_01:MM] +Drive through the checkpoints to increase your overall time. + +[BLOD_02:MM] +You will fail if your overall time reaches zero. + +[BLOD_03:MM] +Get your overall time above the Target Time to win! + +{=================================== MISSION TABLE OVALRIG ===================================} + +[HOTR_01:OVALRIG] +~g~The Race lasts for 12 laps. Only 1st, 2nd and 3rd places qualify for winnings. + +[HOTR_02:OVALRIG] +~g~If your car is destroyed you will be disqualified. + +[HOTR_03:OVALRIG] +~g~When your car is damaged you can get it repaired at the pitstop. + +[HOTR_04:OVALRIG] +~g~This is the way to leave the stadium. + +[HOTR_05:OVALRIG] +Car Health: + +[HOTR_06:OVALRIG] +Laps: + +[HOTR_07:OVALRIG] +New best time: ~1~:0~1~ + +[HOTR_08:OVALRIG] +Time: ~1~:~1~ + +[HOTR_10:OVALRIG] +Race Time: + +[HOTR_09:OVALRIG] +Position: + +[HOTR_12:OVALRIG] +~r~Your car has been destroyed! + +[HOTR_13:OVALRIG] +~r~You didn't win the race! + +[HOTR_14:OVALRIG] +~r~You have been disqualified! + +[HOTR_15:OVALRIG] +Time: ~1~:~1~ + +[HOTR_16:OVALRIG] +Time: ~1~:0~1~ + +[HOTR_17:OVALRIG] +Best Time: ~1~:~1~ + +[HOTR_18:OVALRIG] +Best Time: ~1~:0~1~ + +[HOTR_19:OVALRIG] +Best Time: NA + +[HOTR_20:OVALRIG] +New Best Time: ~1~:~1~ + +[HOTR_21:OVALRIG] +New Best Time: ~1~:0~1~ + +[HOTR_22:OVALRIG] +Best Result: NA + +[HOTR_23:OVALRIG] +Best Result: 1st + +[HOTR_24:OVALRIG] +Best Result: 2nd + +[HOTR_25:OVALRIG] +Best Result: 3rd + +[HOTR_26:OVALRIG] +Best Result: ~1~th + +[HOTR_27:OVALRIG] +Best Lap Time: ~1~.~1~ seconds + +[HOTR_28:OVALRIG] +Best Lap Time: ~1~.0~1~ seconds + +[HOTR_29:OVALRIG] +$~1~ + +[HOTR_30:OVALRIG] +1ST PLACE + +[HOTR_31:OVALRIG] +2ND PLACE + +[HOTR_32:OVALRIG] +3RD PLACE + +[HOTR_33:OVALRIG] +Best Lap Time: NA + +[HOTR_11:OVALRIG] +New best lap time: ~1~.~1~ seconds + +[HOTR_34:OVALRIG] +New best lap time: ~1~.0~1~ seconds + +{=================================== MISSION TABLE PHIL1 ===================================} + +[PHIL1_A:PHIL1] +Phil? + +[PHIL1_B:PHIL1] +RUN! + +[PHIL1_C:PHIL1] +Run + +[PHIL1_E:PHIL1] +Shit Phil, you drink that stuff? + +[PHIL1_F:PHIL1] +Hell, you don't have to drink it + +[PHIL1_G:PHIL1] +- just a good whiff will set you off. Hoowwee! + +[PHIL1_H:PHIL1] +Listen Phil, you said you could fix me up with some firepower... + +[PHIL1_I:PHIL1] +Sure thing. + +[PHIL1_J:PHIL1] +There's some Mexican gun-runner been doing me for business of late. + +[PHIL1_K:PHIL1] +He does his weekly run about now. + +[PHIL1_L:PHIL1] +Ram his hardware off the back of his trucks before he goes to ground. + +[PHIL1_M:PHIL1] +And you'd be doing me a favor while you're at it. + +[PHIL1_N:PHIL1] +Then finish him off. + +[PHI1_01:PHIL1] +~g~Go and knock the arms off the back of the dealers' trucks. + +[PHI1_02:PHIL1] +~g~The arms dealer dropped his load. Smash the crate and pick up the weapon. + +[PHI1_03:PHIL1] +~g~Looks like they have called for back up. + +[PHI1_04:PHIL1] +~g~Now go and finish off the remaining arms dealers. + +[PHI1_HP:PHIL1] +When using Detonator Grenades, throw a grenade then trigger the explosion at any time. + +[PHIL1_O:PHIL1] +Hoooooweeeeee! + +[PHIL1_D:PHIL1] +Never get a naked flame too close to one of Phil Cassidy's Boomshine stills! + +{=================================== MISSION TABLE PHIL2 ===================================} + +[PHIL2_A:PHIL2] +Hey Phil, how's it goin? + +[PHIL2_B:PHIL2] +Heeyyyy, Tommy. Howyadoin'? Ish been too long... + +[PHIL2_C:PHIL2] +I swear you should lay off that boomshine, man - + +[PHIL2_D:PHIL2] +smells like paint stripper. Making my eyes burn... + +[PHIL2_E:PHIL2] +Shshs shhh youshelf Tommy, + +[PHIL2_F:PHIL2] +and come over here because there's someshin' I wanna show you.. someshin. + +[PHIL2_G:PHIL2] +Woof! God! Should I be able to smell that from way over here? I'm feeling woozy. + +[PHIL2_H:PHIL2] +Don'tchaworry about the shmell Tommy, you jush wash thish. + +[PHIL2_I:PHIL2] +Shittycheapbatteriesh or shumin'. There'sh shum more on the bench. + +[PHIL2_J:PHIL2] +TA-DAAA! + +[PHIL2_K:PHIL2] +Aww Damn! + +[PHI2_01:PHIL2] +~g~Quick, get Phil to the hospital. + +[PHI2_03:PHIL2] +~r~Phil Cassidy is dead!!! Now who's gonna supply arms in Vice City? + +[PHI2_05:PHIL2] +Not the hospital, man! Too many cops and Viet Cong! + +[PHI2_06:PHIL2] +There's an ex-army surgeon owes me a few favors and a lawnmower. + +[PHI2_07:PHIL2] +He's got a place down Little Havana - ooo look, a giant fish. + +[PHI2_08:PHIL2] +Watch out! Charlie in the tree line! + +[PHI2_09:PHIL2] +Is it me or are the roads made of jelly? + +[PHI2_10:PHIL2] +Broken Spoon to Mother Hen, you copy? + +[PHI2_11:PHIL2] +Spooney Wooney Woo Woo Woooo! + +[PHI2_12:PHIL2] +He's come for me boy! + +[PHI2_13:PHIL2] +Black feathered wings beating all around... + +[PHI2_14:PHIL2] +It's beautiful, man ... it's beautiful ... but so cold ... + +[PHI2_15:PHIL2] +10-4 we've got a drunk driver. + +[PHI2_04:PHIL2] +PHIL'S HEALTH: + +[PHI_AS1:PHIL2] +PHILS PLACE ASSET COMPLETED + +[PHI_AS2:PHIL2] +~g~New Weapons available to purchase from Phils Place. + +{=================================== MISSION TABLE PIZZA ===================================} + +[PIZ1_01:PIZZA] +~g~Go deliver these pizzas, you must throw the pizza to the customers. Do a drive-by to throw the pizzas. + +[PIZ1_02:PIZZA] +~g~You have thrown all your pizzas, go back and get some more. + +[PIZ1_05:PIZZA] +~g~You have five minutes to deliver the orders before the customers phone another pizza shop. + +[PIZ1_07:PIZZA] +~r~You killed the customer! You're fired. + +[PIZ1_08:PIZZA] +~r~You are out of time. You're fired. + +[PIZ1_09:PIZZA] +~r~You destroyed our bike! You're fired. + +[PIZ1_11:PIZZA] +Hey! Get back on the bike! + +[PIZ1_12:PIZZA] +Pizzas left: + +[PIZ1_06:PIZZA] +Press the~h~ ~k~~TOGGLE_SUBMISSIONS~~w~ when on the bike to cancel the mission. + +[PIZ1_13:PIZZA] +Get these delivered nice and hot. + +[PIZ1_14:PIZZA] +Pal, pizza's for you. + +[PIZ1_15:PIZZA] +Hey, come on Mister, deliver these quick. + +[PIZ1_16:PIZZA] +What are you waiting around for Mister? You got pizza to deliver. + +[PIZ1_17:PIZZA] +I know you didn't want to be a pizza boy, well I don't give a damn. + +[PIZ1_18:PIZZA] +Deliver these. + +[PIZ1_19:PIZZA] +These need delivering. + +[PIZ1_20:PIZZA] +Come on Mister, deliver these things or you're sacked. + +[PIZ1_21:PIZZA] +We got people waiting pal. + +[PIZ1_22:PIZZA] +What are you waiting around for? These need delivering! + +[PIZ1_23:PIZZA] +Deliver the damn food Mister. + +[PIZ1_24:PIZZA] +These need delivering pal. + +[PIZ1_25:PIZZA] +Man, can you take these? + +[PIZ1_26:PIZZA] +Mister, deliver these pronto, avamos amigo. + +[PIZ1_27:PIZZA] +Come on, we're in a rush, deliver these. + +[PIZ1_28:PIZZA] +You again? well deliver these quick pal. + +[PIZ1_29:PIZZA] +No wasting time this time pal. + +[PIZ1_30:PIZZA] +Come on you lazy bastard, deliver this crap on time. + +[PIZ1_31:PIZZA] +You'll never get a promotion unless you move faster this time. + +[PIZ1_32:PIZZA] +~r~Pizza's too hot to handle? + +[PIZ1_33:PIZZA] +~g~Return to the restaurant for more orders. + +[PIZ1_34:PIZZA] +~g~Pizza delivered, here's your cash. + +[PIZ_WON:PIZZA] +Pizza Mission Complete. Your max Health increased to 150. + +{=================================== MISSION TABLE PORN1 ===================================} + +[POR1_A:PORN1] +Action. + +[POR1_B:PORN1] +Whoa! Now that's big. + +[POR1_C:PORN1] +12 inches. That is regulation baby. + +[POR1_D:PORN1] +CUT!! Who IS this idiot? You! YOU! Why are you in my space? WHY? + +[POR1_E:PORN1] +What is all this crap? + +[POR1_F:PORN1] +Aliens? Fishing poles? + +[POR1_G:PORN1] +Who's ever seen a shark that big? + +[POR1_H:PORN1] +All this stuff's gotta go. + +[POR1_I:PORN1] +Why'd you get in this business, ya prick? + +[POR1_J:PORN1] +Huh? + +[POR1_K:PORN1] +For the pussy, that's why! What is this?? + +[POR1_L:PORN1] +This is my art - SECURITY! + +[POR1_M:PORN1] +Look, you pompous asshole, I own you now. I own all of this. + +[POR1_N:PORN1] +We're gonna turn this place around... + +[POR1_O:PORN1] +I'm gonna make you rich. + +[POR1_P:PORN1] +Uh. You're - You - you're Tommy Vercetti? But I thought that you were... + +[POR1_Q:PORN1] +That's right. + +[POR1_R:PORN1] +We're gonna be making some changes around here and start making some real money. + +[POR1_S:PORN1] +Actually, have you ever thought about, umm... + +[POR1_T:PORN1] +But first we're going to need some good-looking broads. + +[POR1_U:PORN1] +Yeh, girls are fine but you... whew! + +[POR1_02:PORN1] +~g~ Go and take out Candy's pimp, then return and pick up Candy. + +[POR1_04:PORN1] +Yo, Candy. I'm looking for movie talent - you interested? + +[POR1_05:PORN1] +Sure! But, you'd have to talk to my agent... + +[POR1_06:PORN1] +The HELL are you doin'? + +[POR1_07:PORN1] +You should have stayed at home today! + +[POR1_7B:PORN1] +Can you believe this asshole? + +[POR1_08:PORN1] +Hey Mercedes! + +[POR1_09:PORN1] +Hey Tommy! You wanna party? + +[POR1_10:PORN1] +Not now sweets. You interested in doing some movies? + +[POR1_11:PORN1] +Of course. As long as it's cheap and sleazy. + +[POR1_13:PORN1] +~g~Take the girls back to the Studio to meet Steve. + +[POR1_17:PORN1] +Whoa, cool shark! + +[POR1_18:PORN1] +~r~Mercedes is dead! + +[POR1_20:PORN1] +Tommy where are you going? Get back here! + +[POR1_21:PORN1] +Where are you going? + +[POR1_22:PORN1] +Tommy, when are we going to spend some time alone together? + +[POR1_01:PORN1] +~g~Candy Suxxx would be perfect for a starring role! + +[POR1_12:PORN1] +~g~Take Candy with you to meet up with Mercedes. + +[POR1_16:PORN1] +Maybe later, babe... + +[POR1_24:PORN1] +~g~Go back and collect Candy. + +[POR1_25:PORN1] +~g~You have left Candy behind, go and get her. + +[POR1_23:PORN1] +~g~Candy will be taking care of business ~h~Downtown~g~. + +[POR1_26:PORN1] +~g~Here's Candy, looks like she has been with Congressman Shrub again. + +[POR1_15:PORN1] +Tommy, you coming in for a warm-up? + +[POR1_14:PORN1] +Heh heh - you're hired! + +[POR1_27:PORN1] +Come on, let's go. + +[POR1_28:PORN1] +Tommy be careful! My implants aren't insured yet! + +[POR1_29:PORN1] +You call that driving? + +[POR1_30:PORN1] +I can't do porno after this! + +[POR1_31:PORN1] +What? Are you trying to kill me? I thought I was the star! + +{=================================== MISSION TABLE PORN2 ===================================} + +[POR2_A:PORN2] +How's filming going, Steve? + +[POR2_B:PORN2] +Well, Candy is a natural and that new girl - she's insatiable! + +[POR2_C:PORN2] +She went through half the cast and crew before I even took a light reading. + +[POR2_D:PORN2] +Anyway, hey, tomorrow we're going on location to shoot the boat scenes - + +[POR2_E:PORN2] +Boat scenes?! What boat scenes? + +[POR2_F:PORN2] +The fishermen are in the throes of passion when this giant shark comes in - + +[POR2_G:PORN2] +What'd I say about the giant shark? + +[POR2_H:PORN2] +I said, 'NO GIANT SHARK', alright? + +[POR2_I:PORN2] +Just keep the cameras pointed at the poontang! + +[POR2_J:PORN2] +Ok ok, hey Tommy, a guy's gotta try, right? + +[POR2_K:PORN2] +Get those flyers printed up? + +[POR2_L:PORN2] +Yeah, but nobody's gonna let us distribute those things, I mean + +[POR2_M:PORN2] +They're just too, uh, they're unimaginative. + +[POR2_N:PORN2] +You don't worry about that. + +[POR2_O:PORN2] +I've got my own ideas for distribution. + +[POR2_P:PORN2] +O.K. Hey, Candy, uh - in my trailer. + +[POR2_01:PORN2] +~g~There is a seaplane that was used as a prop in some old indie film round the back of the studios. + +[POR2_02:PORN2] +~g~Pick one of the checkpoints to start dropping the flyers from. + +[POR2_03:PORN2] +~g~Drop the flyers all the way to the end checkpoint. + +[POR2_04:PORN2] +~r~LOW FUEL!!! + +[POR2_05:PORN2] +~g~Use it to distribute the flyers around town. + +[DILDO:PORN2] +Skimmer Fuel: + +[POR2_Q:PORN2] +Oh, boy. + +[PORN2_9:PORN2] +~g~You have ~1~ seconds to return to a Skimmer before the mission ends. + +{=================================== MISSION TABLE PORN3 ===================================} + +[POR3_A:PORN3] +Ok, what's the problem now? + +[POR3_B:PORN3] +SSShhhh! + +[POR3_C:PORN3] +Well, after his close encounter with the nympho-invaders, + +[POR3_D:PORN3] +our hero finds himself unable to think of anything but this huge phallic mountain - + +[POR3_E:PORN3] +and that's when I want to do the scene with the vat of mashed potatoes, but then we, uh - + +[POR3_F:PORN3] +I don't give a crap about that! + +[POR3_G:PORN3] +J - Just keep going, keep going! + +[POR3_H:PORN3] +Hey Tommy... + +[POR3_I:PORN3] +You mentioned something about some legal problem on the phone? + +[POR3_J:PORN3] +Congressman Alex Shrub has jumped on the pre-election bandwagon, he's going after the puritan vote. + +[POR3_K:PORN3] +Rumors are he's gonna support measures to restrict, shall we say, + +[POR3_L:PORN3] +the more fleshy aspects of this nation's great entertainment industry. + +[POR3_M:PORN3] +Great. + +[POR3_N:PORN3] +Candy! You know Shrub, + +[POR3_O:PORN3] +you guys get up to anything kinky? + +[POR3_P:PORN3] +Oh yeah, oh yeah, oh yeah! Yes yes yes YES OOOoooh! + +[POR3_Q:PORN3] +Please - tell me you got that. + +[POR3_R:PORN3] +Was that part of the, uh... or was she talking to..? + +[POR3_S:PORN3] +Hey, I can never tell. Anyway... + +[POR3_T:PORN3] +You're probably best following her after the shoot, + +[POR3_U:PORN3] +see if she'll lead you to their new love nest. + +[POR3_V:PORN3] +You got a camera? + +[POR3_X:PORN3] +Yeah. Get him a camera. + +[POR3_02:PORN3] +~r~You've killed the Congressman! There's no way you can blackmail him now. + +[POR3_03:PORN3] +~r~You've alerted the Congressman's protection, they will get him out of there immediately. + +[POR3_04:PORN3] +Uh, Candy, could you call me Martha? + +[POR3_05:PORN3] +Oh Alex - I mean Martha. Whatever you say... + +[POR3_06:PORN3] +Martha, someone's watching.. how kinky. + +[POR3_07:PORN3] +You! Give me that camera! + +[POR3_01:PORN3] +~g~Follow Candy's ~h~Stretch~g~. + +[POR3_15:PORN3] +~r~You trashed Candy's Stretch! + +[POR3_17:PORN3] +~g~Get back to the Porn Studios with the film. + +[POR3_19:PORN3] +~r~You ran out of film! + +[POR3_21:PORN3] +~g~You lost Candy's Stretch! + +[POR3_22:PORN3] +~g~The WK Chariot Hotel across from his balcony should provide an ideal photo-grabbing location. + +[POR3_23:PORN3] +~g~There is a side door that will allow you access to the hotel. + +[POR3_08:PORN3] +Press and hold the~h~ ~k~~PED_LOCK_TARGET~ ~w~button to ~h~target~w~ with the camera. + +[POR3_09:PORN3] +Press and hold the~h~ ~k~~PED_LOCK_TARGET~ ~w~button to ~h~target ~w~with the camera. + +[POR3_10:PORN3] +Press the~h~ ~k~~PED_SNIPER_ZOOM_IN~ ~w~button to ~h~zoom in ~w~with the camera and the~h~ ~k~~PED_SNIPER_ZOOM_OUT~ ~w~button to ~h~zoom out ~w~again. + +[POR3_11:PORN3] +Press the~h~ ~k~~PED_SNIPER_ZOOM_IN~ ~w~button to ~h~zoom in ~w~with the camera and the~h~ ~k~~PED_SNIPER_ZOOM_OUT~ ~w~button to ~h~zoom out ~w~again. + +[POR3_12:PORN3] +Press the~h~ ~k~~PED_FIREWEAPON~ ~w~button to take a picture. + +[POR3_13:PORN3] +Press the~h~ ~k~~PED_FIREWEAPON~ ~w~button to take a picture. + +[POR3_14:PORN3] +Press the~h~ ~k~~PED_FIREWEAPON~ ~w~button to take a picture. + +[POR3_20:PORN3] +~g~If you need transport, use the ~h~Sparrow~g~ round the back. + +[POR3_16:PORN3] +~g~You need three good blackmail photographs of Alex Shrub with Candy. + +[POR3_24:PORN3] +PHOTOS TAKEN: + +{=================================== MISSION TABLE PORN4 ===================================} + +[POR4_A:PORN4] +I'm sorry, but I just can't swallow this right now. + +[POR4_B:PORN4] +Oh COME ON darling! + +[POR4_C:PORN4] +He's hung like a sperm whale for pity's sake, + +[POR4_D:PORN4] +how can you not feel the part?! + +[POR4_E:PORN4] +But Stevie... + +[POR4_F:PORN4] +How's my star director? + +[POR4_G:PORN4] +Oh, man. The struggle between the artistic integrity and + +[POR4_H:PORN4] +the humping, pumping action continues unabated. + +[POR4_I:PORN4] +And before you ask, yes, all four videos will be released by their... + +[POR4_J:PORN4] +Honey, can you PLEASE keep the anaconda in the shot, + +[POR4_K:PORN4] +he costs more per hour than you do! + +[POR4_L:PORN4] +Oh, sorry Steve. + +[POR4_M:PORN4] +I was thinking, we need some kind of big stunt to really promote the launch. + +[POR4_N:PORN4] +Something that will make a real impact on the City - you got any ideas? + +[POR4_O:PORN4] +Well, in the old days they used to have gala events, + +[POR4_P:PORN4] +stars, limos, the night sky crisscrossed with searchlights... + +[POR4_Q:PORN4] +Searchlights! I've got an idea... + +[POR4_R:PORN4] +...yeah, yeah, yeah. The little sequined numbers, and the limos, oh, premieres + +[POR4_S:PORN4] +Oh, yes ma'am, of course ma'am, + +[POR4_T:PORN4] +and the press, and the barrage of lights... + +[POR4_01:PORN4] +~g~Go ~y~Downtown~g~ and adjust the spotlight on top of the building. + +[POR4_02:PORN4] +~g~A fast bike will be needed to jump from roof to roof. The Security Guard usually drives a ~y~PCJ 600~g~ to work... + +[POR4_03:PORN4] +~g~You will need to get onto the roofs of the buildings. There should be a lift into one of the upper offices... + +[POR4_06:PORN4] +~g~Return to the lower office if you need access to the rooftops again. + +[POR4_07:PORN4] +~g~You will need a bike so you can jump from building to building. + +[POR4_08:PORN4] +~g~Smash through the window to start the course. You have until 07:00 before it gets too light to get up there unseen. + +[POR4_09:PORN4] +~g~The pickups will show you which building to jump to next. + +[POR4_10:PORN4] +~r~It's too light to get up there unseen. + +[POR4_11:PORN4] +~g~Return to the ladder if you need access to the rooftops again. + +[POR4_05:PORN4] +~g~These stairs will lead round to a lower office. + +[POR_AS1:PORN4] +FILM STUDIO ASSET COMPLETED + +[POR_AS2:PORN4] +~g~Inter Global Films will now generate revenue up to a maximum of $~1~. Make sure you collect it regularly. + +{=================================== MISSION TABLE PROT1 ===================================} + +[PRO1_B:PROT1] +I can't stand this look. Tommy, whadaya say? Whadaya say we put a bar in... + +[PRO1_D:PROT1] +Listen to me, + +[PRO1_E:PROT1] +The time to take over this town is now. It's all out there waiting for us. + +[PRO1_F:PROT1] +We need to start seizing territory, + +[PRO1_G:PROT1] +let Vice City know we're the new players in town, know what I'm saying? + +[PRO1_I:PROT1] +What you need is a legitimate front Tommy, real estate. It's never done me no harm. + +[PRO1_J:PROT1] +We need to start using some muscle or we can kiss all that hard work goodbye. + +[PRO1_K:PROT1] +Local businesses know Diaz is dead, and they're refusing to pay protection! + +[PRO1_L:PROT1] +Ooh! We could try bribery... + +[PRO1_M:PROT1] +Bribery? Screw bribery! I'll show you how to make 'em scared! + +[PRO1_01:PROT1] +~g~Do a hit and run on the shop fronts and the owners will be begging for protection. + +[PRO1_03:PROT1] +~r~This was supposed to be a hit and run, not a 'hit and have coffee'. + +[PRO1_04:PROT1] +My livelihood, destroyed! + +[PRO1_05:PROT1] +Ruined...RUINED!! + +[PRO1_06:PROT1] +I pay through the ass for protection! + +[PRO1_07:PROT1] +My beautiful window display! + +[PRO1_08:PROT1] +My store. My wonderful store. + +[PRO1_09:PROT1] +Vercetti. Remember the name. + +[PRO1_10:PROT1] +I run this town now. ME! + +[BUYP1:PROT1] +You can now buy property in certain areas of the map. + +[BUYP2:PROT1] +If you see a green house pickup, you can buy that property. + +[PRO1_N:PROT1] +I'll be back here in five minutes... + +[PRO1_11:PROT1] +~g~Get to ~y~The North Point Mall~g~ in ~y~Vice Point~g~. + +[PRO1_12:PROT1] +~g~Smash the panes of glass in each shop front and the owners will be begging for new protection. + +[PRO1_A:PROT1] +Oh, we gotta redecorate this place. We gotta make it look older. + +[PRO1_C:PROT1] +You're my lawyer, Rosenberg, not my interior decorator. Got it? + +[BUYP3:PROT1] +Stand inside the pickup, then press the ~h~~k~~PED_ANSWER_PHONE~~w~ button to purchase that property. + +[PRO1_13:PROT1] +~g~You have five minutes to smash them all. + +{=================================== MISSION TABLE PROT2 ===================================} + +[PRO2_A:PROT2] +What's the problem? + +[PRO2_B:PROT2] +Some bar is refusing to pay. + +[PRO2_C:PROT2] +They reckon they're protected by a local gang of thugs. + +[PRO2_D:PROT2] +But don't worry Tommy, I can handle it. + +[PRO2_E:PROT2] +You call this handling it? + +[PRO2_F:PROT2] +You two, off your asses... + +[PRO2_G:PROT2] +Let's go. + +[PRO2_10:PROT2] +~g~Two more have made a run for it. Track them down and finish this. + +[PRO2_11:PROT2] +Get in the car, useless. + +[PRO2_02:PROT2] +Your protection needs a little more protection. + +[PRO2_03:PROT2] +Aw hell, not again! I don't need this crap! + +[PRO2_04:PROT2] +These idiots operate out of DBP Security around the block. + +[PRO2_05:PROT2] +You guys just sort it out amongst yourselves. + +[PRO2_06:PROT2] +I'll be seeing you later. + +[PRO2_07:PROT2] +Yeah, yeah, whatever. + +[PRO2_09:PROT2] +~g~Go and speak to the Front Page Bar Owner. + +[PRO2_01:PROT2] +~g~Take out the guards protecting the Front Page Bar and find out who supplied them. + +[PRO2_08:PROT2] +~g~DBP Security will know you are on your way, go and get them before they clear out. + +{=================================== MISSION TABLE PROT3 ===================================} + +[PRO3_A:PROT3] +You moron! What were you thinking?! + +[PRO3_B:PROT3] +Do you realize what this means?! + +[PRO3_C:PROT3] +We could all be sunk! + +[PRO3_D:PROT3] +The timer must have got screwed. + +[PRO3_E:PROT3] +That place was wired to go up like a firework factory. + +[PRO3_F:PROT3] +Then somebody tipped off the cops... + +[PRO3_G:PROT3] +What's the problem, fellas? + +[PRO3_H:PROT3] +Mike was supposed to torch some place in the mall, + +[PRO3_I:PROT3] +but he screwed the fuses and now the cops are crawling all over it. + +[PRO3_J:PROT3] +We gotta get our stuff and get out of here! + +[PRO3_K:PROT3] +Relax, both of you, let me think for a second! + +[PRO3_L:PROT3] +Tommy Vercetti just doesn't cut and run. + +[PRO3_M:PROT3] +The cops are gonna be going over that building with a fine toothed comb, right? + +[PRO3_N:PROT3] +But that takes time. + +[PRO3_O:PROT3] +We gotta go in and torch that place ourselves. + +[PRO3_P:PROT3] +Yeah, but... + +[PRO3_Q:PROT3] +No one but a cop could get within a mile of that place! + +[PRO3_R:PROT3] +So we go as cops. + +[PRO3_S:PROT3] +We gotta get uniforms - and we're gonna need a squad car. + +[PRO3_T:PROT3] +All thanks to you Mike. + +[PRO3_U:PROT3] +I'm sorry. + +[PRO3_V:PROT3] +I got it. + +[PRO3_W:PROT3] +What we got to do is lure the cops in with the finger, + +[PRO3_X:PROT3] +put them in a lock-up + +[PRO3_Y:PROT3] +and jump 'em. + +[PRO3_Z:PROT3] +Good plan. Let's go! + +[PRO3_A1:PROT3] +Alright. + +[PRO3_01:PROT3] +Ok Lance, let's get the cops' attention! + +[PRO3_02:PROT3] +~g~ Take a cop car and go and plant the bomb at the Tarbrush Coffee Shop in the Mall. + +[PRO3_03:PROT3] +~g~ You've left Lance behind, go and get him. + +[PRO3_04:PROT3] +~g~ Let's go. + +[PRO3_05:PROT3] +~r~You killed Lance! + +[PRO3_07:PROT3] +~g~ You have blown your cover. Hurry up and plant the bomb! + +[PRO3_09:PROT3] +Tie 'em up and gag 'em! + +[PRO3_10:PROT3] +Ooo. Fits perfectly! + +[PRO3_11:PROT3] +Bit tight around the crotch though... + +[PRO3_12:PROT3] +Oh yeah yeah, mine too. Mine too. + +[PRO3_13:PROT3] +Easy brother! No cop drives this bad! + +[PRO3_14:PROT3] +Remember - smile at the other cops + +[PRO3_15:PROT3] +Hey there officer. Nice badge, nice badge. + +[PRO3_16:PROT3] +Real smooth, Lance. + +[PRO3_17:PROT3] +Ok, timers are set, 5 seconds and ticking. + +[PRO3_18:PROT3] +5 seconds?!! We got to get the hell out of here! + +[PRO3_19:PROT3] +Now that got them really irritated. + +[PRO3_20:PROT3] +~g~ Get two cops to follow you into the garage. + +[PRO3_21:PROT3] +~g~Get a wanted level so the cops will follow you into the lock-up. + +[PRO3_22:PROT3] +~g~The lock-up door is blocked! You need to clear the door so it can close. + +[PRO3_23:PROT3] +~g~Walk into the marker to plant the bomb. + +[PRO3_24:PROT3] +~g~Get clear of the Cafe! + +[PRO_AS1:PROT3] +PROTECTION RING ASSET COMPLETED + +[PRO_AS2:PROT3] +~g~Vercetti Estate will now generate revenue up to a maximum of $~1~. Make sure you collect it regularly. + +[PRO3_08:PROT3] +~g~ Get back to ~h~Vercetti Estate~g~ on ~h~Starfish Island~g~. + +{=================================== MISSION TABLE RACES ===================================} + +[RACES_2:RACES] +~g~You need a vehicle to race, this is not a foot race! + +[RACES_3:RACES] +3..2..1.. GO GO GO! + +[RACES_8:RACES] +~r~You didn't win the race! + +[RACES00:RACES] +Race ~1~: + +[RACES01:RACES] +Terminal Velocity + +[RACES02:RACES] +Ocean Drive + +[RACES03:RACES] +Border Run + +[RACES04:RACES] +Capital Cruise + +[RACES05:RACES] +Tour! + +[RACES06:RACES] +V.C. Endurance + +[RACES07:RACES] +Entrance Fee: $~1~ + +[RACES08:RACES] +Best Time: ~1~:~1~ + +[RACES09:RACES] +Best Result: 1st + +[RACES10:RACES] +Best Result: 2nd + +[RACES11:RACES] +Best Result: 3rd + +[RACES12:RACES] +Best Result: 4th + +[RACES13:RACES] +Track Length: ~1~.~1~ km + +[RACES15:RACES] +Best Time: NA + +[RACES16:RACES] +Best Result: NA + +[RACES19:RACES] +You cannot afford to enter this race. + +[RACES22:RACES] +Best Time: ~1~:0~1~ + +[RACES23:RACES] +Track Length: ~1~.~1~ miles + +[RACES_1:RACES] +~g~Get a fast vehicle and get to the starting grid. + +[RACEHLP:RACES] +~w~Press the~h~ ~k~~PED_SPRINT~ ~w~button to start the selected race. Press the~h~ ~k~~VEHICLE_ENTER_EXIT~ ~w~button to exit. + +{=================================== MISSION TABLE RCHELI1 ===================================} + +[WRECKED:RCHELI1] +~r~The vehicle is wrecked! + +[RCH1_4:RCHELI1] +Checkpoints: + +[RCH1_7:RCHELI1] +~g~There are 20 checkpoints in total. + +[RCH1_12:RCHELI1] +~g~The RC helicopter is getting too far out of range! + +[RCH1_13:RCHELI1] +~r~The RC helicopter went out of range! + +[RCH1_8:RCHELI1] { reVC update } +~g~If you wish to quit this mission press the ~h~~k~~VEHICLE_FIREWEAPON~ ~g~button to detonate your RC Helicopter. + +{=================================== MISSION TABLE RCPLNE1 ===================================} + +[RCPL1_4:RCPLNE1] +~g~Compete in a CHECKPOINT RACE with 3 other RC Plane's + +[RCPL1_5:RCPLNE1] +~g~Fly through the checkpoints scattered throughout Vice City. + +[RCPL1_6:RCPLNE1] { reVC update } +~g~If you wish to quit this mission press the ~h~~k~~VEHICLE_FIREWEAPON~ ~g~button to detonate your RC Plane. + +[RCPL1_8:RCPLNE1] +~g~Your RC Plane is going out of range! + +[RCPL1_9:RCPLNE1] +~r~Your RC Plane went out of range! + +{=================================== MISSION TABLE RCRACE1 ===================================} + +[RCRC1_1:RCRACE1] +~g~Compete in a CHECKPOINT RACE with 3 other RC Bandits over 2 LAPS + +[RCRC1_3:RCRACE1] +~g~Final lap! + +[RCR1_4:RCRACE1] +laps left: + +[RCR1_1:RCRACE1] +~g~Compete in a checkpoint race with 3 other RC Cars. + +[RCR1_2:RCRACE1] +~g~Be the first to complete two laps of the track to win! + +[RCR1_6:RCRACE1] +~g~Your RC Car is going out of range! + +[RCR1_7:RCRACE1] +~r~Your RC Car went out of range! + +{=================================== MISSION TABLE ROCK1 ===================================} + +[RBM1_A:ROCK1] +AllllllllRrrighttt! + +[RBM1_B:ROCK1] +Yessss! Brilliant, bloody brilliant! + +[RBM1_D:ROCK1] +Hey, you ever met Love Fist before? + +[RBM1_E:ROCK1] +No, I haven't but I've always loved your music. + +[RBM1_F:ROCK1] +Let me introduce you to the band. + +[RBM1_G:ROCK1] +This is Percy, Dick, and Willy's in the kaze, and that was Jezz in the booth earlier, + +[RBM1_H:ROCK1] +and guys, I want you to meet a good friend of mine. + +[RBM1_I:ROCK1] +This is Tommy. We go way back. + +[RBM1_J:ROCK1] +All right, pal. + +[RBM1_K:ROCK1] +And eh, what was your name again? + +[RBM1_L:ROCK1] +Leave it out, Jezz you, remember - + +[RBM1_M:ROCK1] +don't be playing them games with me, mate, + +[RBM1_N:ROCK1] +I'm too crafty for that, sunshine! + +[RBM1_O:ROCK1] +You see, the thing is, Tom, the boys need some help. + +[RBM1_P:ROCK1] +They ain't too connected here, they don't have the old 'how's your father?' + +[RBM1_Q:ROCK1] +We need some drugs, pal! + +[RBM1_R:ROCK1] +Gonna get on the old Love Fist fury, you know?! + +[RBM1_S:ROCK1] +Well, this is Vice City, man. What's the problem? + +[RBM1_U:ROCK1] +Love Juice, man! + +[RBM1_V:ROCK1] +Love Juice? + +[RBM1_W:ROCK1] +Aye, two parts boomshine, 1 part trumpet, 5 fizz bombs and a liter of petrol. + +[RBM1_X:ROCK1] +Can you help us out, pal? + +[RBM1_Y:ROCK1] +Aw, it would really mean a lot to the boys. + +[RBM1_Z:ROCK1] +You can do that for the boys, right? + +[RBM1_7:ROCK1] +~r~You did not get the Love Juice in time! + +[RBM1_8:ROCK1] +~r~Mercedes is dead! + +[RBM1_10:ROCK1] +~r~You idiot! You have destroyed the merchandise! + +[RBM1_13:ROCK1] +~g~Get the 'Love Juice' and Mercedes to the band before they are needed on stage. + +[RBM1_15:ROCK1] +~r~You have lost the dealer, our cash and the drugs! + +[RBM1_17:ROCK1] +~g~Kill the dealer and get the drugs! + +[MOB_07A:ROCK1] +Hey mate, the guys could do with some company, if you know what I mean... + +[MOB_07B:ROCK1] +I know just the girl. + +[ROK1_5:ROCK1] +Hey, Mercedes! + +[ROK1_6:ROCK1] +Hiya, Tommy. And how are you? + +[ROK1_7:ROCK1] +Just fine. Listen, you fancy having Love Fist? + +[ROK1_8:ROCK1] +Ok, but just as a favor I expect returned.. + +[RBM1_14:ROCK1] +~g~You need a car or a motorcycle! + +[RBM1_1:ROCK1] +~g~Go and collect Mercedes from her apartment. + +[RBM1_12:ROCK1] +~g~Go and collect the 'Love Juice' ingredients from the dealer. + +[ROK1_2:ROCK1] +NO LONGER NEEDED + +[ROK1_3:ROCK1] +NO LONGER NEEDED + +[MERC_39:ROCK1] +I'll see you later, big boy. + +[RBM1_C:ROCK1] +Hey, Tommy! Glad you could make it. + +[RBM1_9:ROCK1] +~g~Go and collect some love juice from the dealer for Love Fist! + +[ROK1_1A:ROCK1] +Looking for something special? I got what you need! + +[ROK1_9:ROCK1] +Thanks for the money, sucker! + +[RBM1_T:ROCK1] +We need Love Juice, man, you know? + +{=================================== MISSION TABLE ROCK2 ===================================} + +[RBM2_A:ROCK2] +Tommy, man. Am I glad to see you! + +[RBM2_B:ROCK2] +What's going on? + +[RBM2_C:ROCK2] +Bad vibes, Tommy.... + +[RBM2_E:ROCK2] +There's this cat, we hardly know him, but he knows us. + +[RBM2_F:ROCK2] +Like this cat. Knows all about us. + +[RBM2_G:ROCK2] +Knows that Willy likes his ladies' underwear, eh! + +[RBM2_H:ROCK2] +Or that Percy likes Duran Duran! + +[RBM2_K:ROCK2] +Yeah, the love rocket thing, right. But listen, this cat... + +[RBM2_L:ROCK2] +yeh, yeh, the guy, he wants Love Fist dead. + +[RBM2_M:ROCK2] +Dead Tommy. + +[RBM2_N:ROCK2] +Love Fist gone. You know what they say, the good die young. + +[RBM2_O:ROCK2] +but Tommy, you gotta save Love Fist! + +[RBM2_P:ROCK2] +We got a signing in two hours and I think... + +[RBM2_Q:ROCK2] +And the boys think the stalker's gonna try some monkey business there. + +[RBM2_1:ROCK2] +~g~Drive the limo to the signing event and try to draw the psycho out. + +[RBM2_2:ROCK2] +~r~You've wrecked the band's car! + +[RBM2_3:ROCK2] +~g~Get to the signing! + +[RBM2_4:ROCK2] +~g~Get the Psycho! Don't let him escape! + +[RBM2_5:ROCK2] +~r~You lost him, you idiot! + +[RBM2_7:ROCK2] +~r~The fans have been attacked, the psycho won't show! + +[RBM2_8:ROCK2] +~r~The security guards have been attacked, the psycho won't show! + +[PSYCH_1:ROCK2] +I'll see Love Fist burn! + +[PSYCH_2:ROCK2] +Love Fist ruined my life! + +[RBM2_I:ROCK2] +Shut up ye fool. Just 'cause Jezz bangs sheep. + +[RBM2_R:ROCK2] +Oi shut it! + +[RBM2_D:ROCK2] +Aye, I'm not joking, it's heavy stuff man, heavy you know? + +[RBM2_J:ROCK2] +It's a love rocket thing, you know? + +{=================================== MISSION TABLE ROCK3 ===================================} + +[RBM3_A:ROCK3] +Tommy! Tommy! Tommy, man, that psycho's back! + +[RBM3_B:ROCK3] +What's going on? + +[RBM3_C:ROCK3] +That psycho won't leave Love Fist alone! + +[RBM3_D:ROCK3] +You didnae kill him man. And now he's back. + +[RBM3_E:ROCK3] +Yeah, yeah, yeah, and the thing is... + +[RBM3_F:ROCK3] +The thing is, we need someone to drive the limo we can trust, + +[RBM3_G:ROCK3] +cause that nutter keeps making threats! + +[RBM3_I:ROCK3] +We're all bricking ourselves, man. + +[RBM3_J:ROCK3] +Okay guys, calm down, I'll handle this. + +[RBM3_K:ROCK3] +Normally I wouldn't busy myself with driving around a bunch of drunken Scottish bisexuals, + +[RBM3_L:ROCK3] +but, in your case I'll make an exception. + +[RBM3_4:ROCK3] +~r~You've killed Love Fist! + +[RBM3_6:ROCK3] +DETONATION: + +[RBM3_1:ROCK3] +~g~Drive Love Fist to the venue. + +[RBM3_2:ROCK3] +While the bomb is armed if you try to leave the car it will explode... + +[RBM3_3:ROCK3] +If the detonation bar completely fills the bomb will explode. + +[RBM3_8:ROCK3] +The faster you drive the lower the detonation bar will go. + +[RBM3_7:ROCK3] +~g~BOMB DEFUSED! + +[ROK3_62:ROCK3] +so we thought we'd show you our Temple of Rock - + +[ROK3_63:ROCK3] +Get a feel for that Love Fist fury! + +[ROK3_64:ROCK3] +Listen to yourself, man. It's papier-mache and gaffa tape. + +[ROK3_65:ROCK3] +Hey, to the kids, it's a temple and we are the priests! + +[ROK3_66:ROCK3] +Aye, well, if the kids like their priests half cut and tone deaf, + +[ROK3_67:ROCK3] +who am I to argue? + +[ROK3_68:ROCK3] +Oh geez, the tape's getting chewed again. + +[ROK3_69:ROCK3] +At this rate, we'll never get to play live. + +[ROK3_70:ROCK3] +Oohh shite! My bowels... + +[ROK3_73:ROCK3] +Jezz is running the tape, + +[RBM3_9:ROCK3] +If you are stopped or drive slowly the detonation bar will increase. + +[RBM3_H:ROCK3] +I'm shitin' masel' man. I need ma ma! + +[ROK3_71:ROCK3] +We gotta get on with it - thanks again Tommy, Know what I am saying, nice one, bye! + +[ROK3_1:ROCK3] +At last man, time for a well earned drink. The venue's just a hundred yards down the road. + +[ROK3_2:ROCK3] +Better make it a large one then. Hey Tommy, change the tunes, man. + +[ROK3_3:ROCK3] +I get confused if my head ain't banging. Ah look, what's this? Hey Tommy, stick this tape on. + +[ROK3_4:ROCK3] +Love Fist. Your time polluting the airwaves is over. I gave you the chance to be friends. + +[ROK3_5:ROCK3] +Now, I'm giving you the chance to die. Try to slow down and your limousine will explode, along with your BIG, HAIRY ARSES! + +[ROK3_6:ROCK3] +Tommy pal, you gotta save the band! I'm getting bored of this. Just keep the pedal to the metal!! + +[ROK3_7:ROCK3] +We gotta find the bomb! Can't we just drive around all day? Aye, we've got plenty to drink.. + +[ROK3_8:ROCK3] +Won't the bomb not be in the engine? We'll have to stop to get it. We're all going to die! I'm gonna get drunk! + +[ROK3_9:ROCK3] +Hey, there's a queue here pal! The answer ain't in the drinks cabinet! Get out of my way! + +[ROK3_10:ROCK3] +Hey, the vodka bottle's got wires coming out of it! That's not vodka, that's BOOMSHINE! + +[ROK3_11:ROCK3] +WAAAAAAGGGHHHH!!!! And it's wired to blow!! WAAAAAAAAAAAAGGGHHHHHHHH!!!! + +[ROK3_12:ROCK3] +They always said the drink would kill me. I've seen this on the telly. you gotta pull out one of the wires. Which wire? I don't know, man. + +[ROK3_13:ROCK3] +I don't have a clue. Willy, say something. I'm gonna play bass in hell. + +[ROK3_14:ROCK3] +Tommy man, keep driving fast, pal. Somebody do something. Aye, clever! + +[ROK3_15:ROCK3] +'Somebody do something', what kind of crap is that, I've seen braver girls. Okay tough guy, you do something. + +[ROK3_16:ROCK3] +Look, man, I play a musical instrument I don't have a clue about bomb disposal. Willy could just suck the boomshine out with a straw. + +[ROK3_17:ROCK3] +Aye, I've heard that your good at that kind of thing. Hey, I was off my tits that night, as well you know! + +[ROK3_18:ROCK3] +Just pass Willy a straw! A straw?!?! This is the Love Fist Tour Bus! + +[ROK3_19:ROCK3] +Where am I gonna get a straw from, know wot I mean? Which wire, Tommy? The green one. There isn't a green one. + +[ROK3_20:ROCK3] +Or is this one green? Any of these wires look green to you? + +[ROK3_21:ROCK3] +Oh no! Death's on the cards! Everything looks green! I should have dumped you lot when I had the chance man. + +[ROK3_22:ROCK3] +Glory seeker. Capitalist. I've been carrying you for years. Shut up. You're a muppit. + +[ROK3_23:ROCK3] +A big screaming girl. Yeah. Shut up and pull a wire. Which wire? This one.. + +[ROK3_24:ROCK3] +NO! Man, we're okay. We ain't been blown up, pal. + +[ROK3_25:ROCK3] +Tommy, man, nice one. Rock and roll, man. Ain't we got a gig to go to? + +[ROK3_26:ROCK3] +A racket to make? Groupies to abuse? LOVE FIST! + +[ROK3_27:ROCK3] +Have you finished with that bottle? + +{=================================== MISSION TABLE SERG1 ===================================} + +[TEX1_A:SERG1] +Come in and park yourself on the hide, son. + +[TEX1_B:SERG1] +Hell, my daddy used to say, never look a gift horse in the mouth, and by golly, he never did. + +[TEX1_C:SERG1] +Would you like a drop of the old Kentucky? + +[TEX1_D:SERG1] +No thanks. + +[TEX1_E:SERG1] +A clean thinker! I like that. + +[TEX1_F:SERG1] +Now, the property business isn't all about high-falootin' paper pushing. + +[TEX1_G:SERG1] +It's about dirt! And the will to claim that dirt! You with me, son? + +[TEX1_H:SERG1] +Oh yeah. + +[TEX1_K:SERG1] +Persuasion's my forte. + +[TEX1_L:SERG1] +Yeh, he'll be down at the country club, down on the golf course. + +[TEX1_M:SERG1] +They don't allow guns, so his bodyguards won't be packing lawgivers. + +[TEX1_N:SERG1] +Go beat eight tons of crap out of him. + +[TEX1_O:SERG1] +Here now - I got you a membership, and boy you're going to need more appropriate clothing. + +[TEX1_2:SERG1] +~g~Now head to the Leaf Links Golf Club. + +[TEX1_3:SERG1] +Who's this guy? Boys, deal with him. + +[TEX1_6:SERG1] +Nice ass baby! + +[TEX1_7:SERG1] +Is this me? + +[TEX1_I:SERG1] +Well, I need some tenacious bastard to let go of some dirt, + +[TEX1_J:SERG1] +and you look to me like the kind of guy to persuade him. + +[TEX1_0:SERG1] +~g~The target is at the driving range enjoying a game of golf. Make sure it's his last. + +[TEX1_8:SERG1] +Each time you enter a caddy you automatically receive a golf club, providing your melee weapon slot is empty. + +[TEX1_9:SERG1] +Get him! + +[TEX1_10:SERG1] +Kill that Psycho! + +[TEX1_1:SERG1] +~g~Go and pick up some golfing clothes from Jocksports. + +{=================================== MISSION TABLE SERG2 ===================================} + +[TEX_2A:SERG2] +~g~Excellent! They've spotted you! + +[TEX_2B:SERG2] +~r~Fool! People have to WITNESS a Cuban doing the hit! + +[TEX_2C:SERG2] +~g~Go get yourself some Cuban gang colors in Little Havana! + +[TEX_2D:SERG2] +~g~Take out the Haitian Gang Lord at Romero's Funeral Parlor! + +[TEX2_A:SERG2] +Tommy, this is Donald Love. Donald, this here is Tommy Vercetti, + +[TEX2_B:SERG2] +the latest gunslinger to come to these parts. + +[TEX2_C:SERG2] +Yeh...uh... + +[TEX2_D:SERG2] +Donald, you just shut up and listen, and you might learn something. + +[TEX2_E:SERG2] +Now, nothing brings down real estate prices quicker than a good old-fashioned gang war - + +[TEX2_F:SERG2] +'cept maybe a disaster, like a biblical plague or something, + +[TEX2_G:SERG2] +but, that may be going too far in this case. + +[TEX2_H:SERG2] +You getting this down, you four-eyed prick? + +[TEX2_I:SERG2] +Now recently a Haitian gang lord died. Apparently the Cubans did it, nobody's certain. + +[TEX2_J:SERG2] +But let's make them certain! You disguise yourself as a Cuban hombre, + +[TEX2_K:SERG2] +and head on down to crash that funeral. Mix it up, and then high tail it. + +[TEX2_L:SERG2] +You getting this down, Donald? + +[TEX2_M:SERG2] +Well, that ought to put the coyote in the chicken coop, huh? + +[TEX2_N:SERG2] +And then we'll just sit back, and watch the prices tumble. + +[TEXEXIT:SERG2] +~g~Now get out of Little Haiti! + +{=================================== MISSION TABLE SERG3 ===================================} + +[TEX3_A:SERG3] +Now look here, son. I got a problem and I reckon you could help me with it. + +[TEX3_B:SERG3] +I'm no builder. + +[TEX3_C:SERG3] +No, I was thinking more of your demolition skills. + +[TEX3_D:SERG3] +Now this here, this is the development as planned and this, + +[TEX3_E:SERG3] +this is the property that we're looking at. + +[TEX3_F:SERG3] +You're trying to say this new office block is kind of in the way. + +[TEX3_G:SERG3] +You catch on quick. + +[TEX3_H:SERG3] +Now I'm going to head out of town for a while + +[TEX3_I:SERG3] +and if that office development were to face sudden and insurmountable structural problems, then I.. + +[TEX3_J:SERG3] +As a civil minded individual you'd feel obliged to step in and + +[TEX3_K:SERG3] +save the rejuvenation of an important area of the city? + +[TEX3_L:SERG3] +Where can I get more guys like you? + +[TEX3_1:SERG3] +~g~Use the RC helicopter to transport bombs to four demolition points on the building site. + +[TEX3_2:SERG3] +~g~You must place one bomb at each target. You can place bombs in any order. + +[TEX3_3:SERG3] +~g~Maneuver the RC helicopter next to a bomb to pick it up. it can carry one bomb at a time.. + +[TEX3_5:SERG3] +~g~If you place a bomb unsuccessfully you can pick it up and try again. + +[TEX3_7:SERG3] +~g~You must then place the remaining bombs in 7 minutes! + +[TEX3_8:SERG3] +~g~You missed the target! Pick up a bomb and try again! + +[TEX3_10:SERG3] +~g~Drop the bomb at a target. + +[TEX3_11:SERG3] +targets left: + +[TEX3_17:SERG3] +~r~You ran out of time! + +[TEX3_18:SERG3] +~r~Your RC Helicopter has been destroyed! + +[TEX3_19:SERG3] +~r~You dropped your bomb in the water! That ain't no way to fish! + +[TEX3_20:SERG3] +~r~The RC Helicopter is nearly out of range! + +[TEX3_21:SERG3] +~r~The RC Helicopter went out of range! + +[TEX3_24:SERG3] +Press the ~h~~k~~VEHICLE_LOOKLEFT~ ~w~button to rotate the helicopter counter-clockwise. + +[TEX3_25:SERG3] +Press the ~h~~k~~VEHICLE_LOOKRIGHT~ ~w~button to rotate the helicopter clockwise. + +[TEX3_27:SERG3] +~g~A central stairway allows access to all the floors in the building. + +[TEX3_31:SERG3] +~r~You destroyed the TOPFUN van that contained the bombs and RC helicopter! + +[TEX3_32:SERG3] +You can ~h~look behind~w~ by simultaneously pressing the ~h~~k~~VEHICLE_LOOKLEFT~~w~ and the ~h~~k~~VEHICLE_LOOKRIGHT~~w~ buttons. + +[TEX3_4:SERG3] { reVC update } +~g~To drop a bomb press the~h~ ~k~~VEHICLE_FIREWEAPON~ ~g~button. + +[TEX3_29:SERG3] { reVC update } +To drop a bomb press the~h~ ~k~~VEHICLE_FIREWEAPON~ ~w~button. + +[TEX3_26:SERG3] +Pressing the ~h~~k~~VEHICLE_BRAKE~ ~w~button ~w~decreases the rotor speed, causing the helicopter to~h~ descend. + +[TEX3_22:SERG3] +Pressing the ~h~~k~~VEHICLE_ACCELERATE~ ~w~button increases the rotor speed, causing the helicopter to ~h~ascend. + +[TEX3_16:SERG3] +~g~Get to the ~w~TOPFUN ~g~van near the building site to be demolished. + +[TEX3_33:SERG3] +Once you pick up a bomb the radar will show you the position of the target relative to the RC helicopter. + +[TEX3_34:SERG3] +An ~h~upwards pointing triangular blip ~w~indicates the target is ~h~above ~w~the RC helicopter. + +[TEX3_35:SERG3] +A ~h~downward pointing triangular blip ~w~indicates the target is ~h~below ~w~the RC helicopter. + +[TEX3_36:SERG3] +A ~h~square blip ~w~indicates the target is on the ~h~same level ~w~as the RC helicopter + +[TEX3_6:SERG3] +~g~Once you have picked up a bomb for the first time, the detonation timer will start. + +[TEX3_28:SERG3] +To ~h~pick up a bomb~w~, simply maneuver the RC helicopter next to it. The RC Helicopter can carry one bomb at a time. + +[TEX3_30:SERG3] +~g~To pick up a bomb, simply maneuver the RC helicopter next to it. The RC Helicopter can carry one bomb at a time. + +[TEX3_12:SERG3] +~g~Bomb planted! Only 3 more targets to go! Go back and get another bomb. + +[TEX3_13:SERG3] +~g~Bomb planted! Only 2 more targets to go! Go back and get another bomb. + +[TEX3_14:SERG3] +~g~Bomb planted! Only 1 more target to go! Go back and get another bomb. + +[TEX3_15:SERG3] +~r~Detonation timer initiated! ~g~You must plant the ~w~4 bombs ~g~in the remaining time! + +[TEX3_37:SERG3] +Pushing ~h~back on the analog stick ~w~decreases the rotor speed, causing the helicopter to~h~ descend. + +[TEX3_38:SERG3] { reVC update } +{ Pressing the ~h~~k~~VEHICLE_ACCELERATE~ ~w~button increases the rotor speed, causing the helicopter to ~h~ascend. } +Pushing ~h~forward on the analog stick ~w~increases the rotor speed, causing the helicopter to ~h~ascend. + +[TEX3_39:SERG3] +~g~To drop a bomb press the ~h~~k~~VEHICLE_HANDBRAKE~ ~g~button. + +[TEX3_40:SERG3] +To drop a bomb press the ~h~~k~~VEHICLE_HANDBRAKE~ ~w~button. + +[TEX3_23:SERG3] +Press the ~h~~k~~VEHICLE_TURRETUP~~w~ and ~h~~k~~VEHICLE_TURRETDOWN~~w~ buttons to tilt the helicopter in the direction you wish to maneuver it. + +{=================================== MISSION TABLE TAXI1 ===================================} + +[FARES:TAXI1] +FARES: + +[TAXI1:TAXI1] +~g~Look for a fare. + +[TSCORE2:TAXI1] +$~1~ + +[IN_ROW:TAXI1] +~1~ IN A ROW bonus! $~1~ + +[TAXI3:TAXI1] +~r~Your passenger fled in terror! + +[TAXI7:TAXI1] +~r~Your car is trashed, get it repaired. + +[TAXI4:TAXI1] +Fare complete! + +[TAXI5:TAXI1] +SPEED BONUS!! + +[TAXI6:TAXI1] +Taxi mission over + +[TAXIH1:TAXI1] +Stop near a highlighted pedestrian to pick them up then drive them to their destination before the time runs out. + +[FARE1:TAXI1] +~g~Destination ~w~'The Pole Position Club' ~g~in Ocean Beach. + +[FARE3:TAXI1] +~g~Destination ~w~'The Marina' ~g~in Ocean Beach. + +[FARE4:TAXI1] +~g~Destination ~w~'Ammu-Nation' ~g~in Ocean Beach. + +[FARE5:TAXI1] +~g~Destination ~w~'The Hardware store' ~g~in Washington Beach. + +[FARE6:TAXI1] +~g~Destination ~w~'The North Point Mall' ~g~in Vice Point. + +[MFARE1:TAXI1] +~g~Destination ~w~'Ammu-Nation' ~g~in Downtown. + +[MFARE2:TAXI1] +~g~Destination ~w~'the Terminal' ~g~at Escobar International Airport. + +[WFARE3:TAXI1] +~g~Destination ~w~'Sunshine Autos' ~g~in Little Havana. + +[WFARE4:TAXI1] +~g~Destination ~w~'Kaufman Cabs' ~g~in Little Haiti. + +[WFARE5:TAXI1] +~g~Destination ~w~'The Hardware store' ~g~in Little Havana. + +[WFARE6:TAXI1] +~g~Destination ~w~'Howlin Petes Bike Emporium' ~g~in Downtown. + +[FARE7:TAXI1] +~g~Destination ~w~'The Jewelers' ~g~in Vice Point. + +[FARE8:TAXI1] +~g~Destination ~w~'The Beach' ~g~in Ocean Beach. + +[FARE9:TAXI1] +~g~Destination ~w~'The Beach' ~g~in Washington Beach. + +[FARE10:TAXI1] +~g~Destination ~w~'The Beach' ~g~in Vice Point. + +[FARE11:TAXI1] +~g~Destination ~w~'Hospital' ~g~in Ocean Beach. + +[FARE12:TAXI1] +~g~Destination ~w~'Hospital' ~g~in Vice Point. + +[FARE13:TAXI1] +~g~Destination ~w~'Police Station' ~g~in Washington Beach. + +[FARE14:TAXI1] +~g~Destination ~w~'Police Station' ~g~in Vice Point. + +[FARE15:TAXI1] +~g~Destination ~w~'Pizza Restaurant' ~g~in Vice Point. + +[WFARE7:TAXI1] +~g~Destination ~w~'Police Station' ~g~in Little Havana. + +[WFARE8:TAXI1] +~g~Destination ~w~'Police Station' ~g~in Downtown. + +[WFARE9:TAXI1] +~g~Destination ~w~'Hospital' ~g~in Downtown. + +[WFARE10:TAXI1] +~g~Destination ~w~'Hospital' ~g~in Little Havana. + +[WFARE11:TAXI1] +~g~Destination ~w~'The Stadium' ~g~in Downtown. + +[WFARE12:TAXI1] +~g~Destination ~w~'Pizza Restaurant' ~g~in Little Haiti. + +[WFARE13:TAXI1] +~g~Destination ~w~'Pizza Restaurant' ~g~in Downtown. + +[WFARE14:TAXI1] +~g~Destination ~w~'Docks' ~g~in Viceport. + +[WFARE15:TAXI1] +~g~Destination ~w~'Chemist' ~g~in Little Haiti. + +[FARE2:TAXI1] +~g~Destination ~w~'The Malibu club' ~g~in Vice Point. + +{=================================== MISSION TABLE TAXICUT ===================================} + +[TAXC_A:TAXICUT] +Guess you're the new owner. + +[TAXC_B:TAXICUT] +What are you, mob? Cartel? You don't look Mexican... + +[TAXC_C:TAXICUT] +Anyhoo, I guess you better get on with the 'things are gonna change around here' crap, + +[TAXC_D:TAXICUT] +maybe threaten one of the drivers - + +[TAXC_E:TAXICUT] +go steady on Ted over there, he's just had his hernia fixed. + +[TAXC_F:TAXICUT] +Well, yeah. Things are going to change around here, lady. + +[TAXC_G:TAXICUT] +Oh crap, sonny. Might as well leave this to me - + +[TAXC_H:TAXICUT] +I've been doing this for years. + +[TAXC_I:TAXICUT] +Now hear this. + +[TAXC_J:TAXICUT] +We are now under new management and things are going to change around here again. + +[TAXC_K:TAXICUT] +Our new management, the + +[TAXC_L:TAXICUT] +- Which gang are you? + +[TAXC_M:TAXICUT] +Well, I'm not part of any gang actually. + +[TAXC_N:TAXICUT] +What's your goddamned name, kid? + +[TAXC_O:TAXICUT] +Vercetti, Tommy Vercetti. + +[TAXC_P:TAXICUT] +Our new management, the Vercetti Gang, + +[TAXC_Q:TAXICUT] +is gonna make sure we get no trouble. + +[TAXC_R:TAXICUT] +Capiche? Out! + +[TAXC_S:TAXICUT] +Did you like the 'capiche'? I liked the 'capiche'. + +[TAXC_T:TAXICUT] +So this is how it's worked in the past, + +[TAXC_U:TAXICUT] +We run the firm as usual. + +[TAXC_V:TAXICUT] +If we get any trouble from rival firms, you beat the crap out of them. + +[TAXC_W:TAXICUT] +Then they beat the crap out of us, + +[TAXC_X:TAXICUT] +then you beat the crap out of them, + +[TAXC_Y:TAXICUT] +etcetera, etcetera. You got it? + +[TAXC_Z:TAXICUT] +Uh, yeah, I guess... + +[TAXC_A1:TAXICUT] +Just grab a taxi from the garage if you feel like jumping in. + +{=================================== MISSION TABLE TAXIWA1 ===================================} + +[OUTTIME:TAXIWA1] +~r~Too slow, man, too slow! + +[TAX1_1:TAXIWA1] +Ok, we got a high class fare needs picking up from Starfish island - any takers? + +[TAX1_2:TAXIWA1] +Tommy here, I'll take it! + +[TAX1_3:TAXIWA1] +This is my fare, back off asshole! + +[TAX1_4:TAXIWA1] +Come on come on, Get in, quick! + +[TAX1_5:TAXIWA1] +Ok, ok! Just don't hurt me! + +[TAXW1_1:TAXIWA1] +~g~Pick up the V.I.P. on Starfish Island. + +[TAXW1_2:TAXIWA1] +~g~Get the V.I.P back! Take the other car out! + +[TAXW1_3:TAXIWA1] +~r~The V.I.P. is dead! + +[TAXW1_4:TAXIWA1] +~r~The V.I.P. has been dropped off! + +[TAXW1_6:TAXIWA1] +~g~Take the V.I.P. to the airport! + +{=================================== MISSION TABLE TAXIWA2 ===================================} + +[TAX2_1:TAXIWA2] +Calling all cars, we're losing fares all over town. What's with you guys? + +[TAX2_2:TAXIWA2] +VC Cabs keep beating us to it. They've just got too many cars - we can't compete! + +[TAX2_3:TAXIWA2] +Mr. Vercetti, if you're out there listening in, you gotta put some VC Cabs out of action before we go bust! + +[TAXW2_1:TAXIWA2] +~g~Destroy 3 of the rival taxis! + +{=================================== MISSION TABLE TAXIWA3 ===================================} + +[TAX3_1:TAXIWA3] +Car 13, We got a Miss Cortez, asked for you especially. + +[TAX3_2:TAXIWA3] +Ok, I got it. Car 13 out! + +[TAX3_3:TAXIWA3] +Hmmmm, no sign of Mercedes... + +[TAXW3_3:TAXIWA3] +~g~Take out the leader cab! + +[TAXW3_2:TAXIWA3] +~g~Stay alive until the timer runs out. + +[TAX_AS1:TAXIWA3] +TAXI FIRM ASSET COMPLETED + +[TAX_AS2:TAXIWA3] +~g~Kaufman Cabs will now generate revenue up to a maximum of $~1~. Make sure you collect it regularly. + +[TAX3_4:TAXIWA3] +It's time for Kaufman Cab's guardian angel to eat some fender! + +[TAX3_5:TAXIWA3] +Hey boy I'm gonna tan your hide! + +{ reVC updates } +{ new languages } +[FEL_JAP] +JAPANESE + +[FEL_POL] +POLISH + +[FEL_RUS] +RUSSIAN + +{ new display menus } +[FET_GFX] +GRAPHICS SETUP + +[FED_MIP] +MIP MAPPING + +[FED_AAS] +ANTI ALIASING + +[FED_FIL] +TEXTURE FILTERING + +[FED_BIL] +BILINEAR + +[FED_TRL] +TRILINEAR + +[FED_WND] +WINDOWED + +[FED_FLS] +FULLSCREEN + +[FEM_CSB] +CUTSCENE BORDERS + +[FEM_SCF] +SCREEN FORMAT + +[FEM_ISL] +MAP MEMORY USAGE + +[FEM_LOW] +LOW + +[FEM_MED] +MEDIUM + +[FEM_HIG] +HIGH + +[FEM_2PR] +PS2 ALPHA TEST + +[FEC_FRC] +FREE CAM + +{ Linux joy detection } +[FEC_JOD] +DETECT JOYSTICK + +[FEC_JPR] +Press any key on the joystick of your choice that you want to use on the game, and it will be selected. + +[FEC_JDE] +Detected joystick + +{ mission restart } +[FET_RMS] +REPLAY MISSION + +[FESZ_RM] +RETRY? + +[FED_VPL] +VEHICLE PIPELINE + +[FED_PRM] +PED RIM LIGHT + +[FED_RGL] +ROAD GLOSS + +[FED_CLF] +COLOUR FILTER + +[FED_WLM] +WORLD LIGHTMAPS + +[FED_MBL] +MOTION BLUR + +[FEM_SIM] +SIMPLE + +[FEM_NRM] +NORMAL + +[FEM_MOB] +MOBILE + +[FED_MFX] +MATFX + +[FED_NEO] +NEO + +[FEM_PS2] +PS2 + +[FEM_XBX] +XBOX + +[FEC_IVP] +INVERT PAD VERTICALLY + +[FEM_NON] +NONE + +[FEC_DS2] +DUALSHOCK 2 + +[FEC_DS3] +DUALSHOCK 3 + +[FEC_DS4] +DUALSHOCK 4 + +[FEC_360] +XBOX 360 CONTROLLER + +[FEC_ONE] +XBOX ONE CONTROLLER + +[FEC_NSW] +NINTENDO SWITCH CONTROLLER + +[FEC_TYP] +GAMEPAD TYPE + +[FET_AGS] +GAMEPAD SETTINGS + +[FEM_AUT] { aspect ratio related } +AUTO + +[FEM_PED] +PED DENSITY + +[FEM_CAR] +CAR DENSITY + +{ end of file } +[DUMMY] +THIS LABEL NEEDS TO BE HERE !!! +AS THE LAST LABEL DOES NOT GET COMPILED \ No newline at end of file diff --git a/gxt/miami/build.bat b/gxt/miami/build.bat new file mode 100644 index 00000000..7327497f --- /dev/null +++ b/gxt/miami/build.bat @@ -0,0 +1,5 @@ +gxt -g VC -i "american.txt" -o "../../gamefiles/TEXT/american.gxt" +gxt -g VC -i "french.txt" -o "../../gamefiles/TEXT/french.gxt" +gxt -g VC -i "german.txt" -o "../../gamefiles/TEXT/german.gxt" +gxt -g VC -i "italian.txt" -o "../../gamefiles/TEXT/italian.gxt" +gxt -g VC -i "spanish.txt" -o "../../gamefiles/TEXT/spanish.gxt" \ No newline at end of file diff --git a/gxt/miami/french.txt b/gxt/miami/french.txt new file mode 100644 index 00000000..73860f9c --- /dev/null +++ b/gxt/miami/french.txt @@ -0,0 +1,14726 @@ +{ + New strings are at the bottom of file. + Do not change the order of strings. + You can fix the typos of existing translation but please refrain from + unnecessary edits like rephasing because you think it suits better for your taste. +} + +[RAMPAGE] +RODEO! + +[RAMP_F] +ECHEC DU RODEO! + +[RAMP_P] +RODEO REUSSI! + +[RAMP_A] +TOUS LES RODEOS REUSSIS! + +[PAGE_01] +Tue ~1~ membres de gang en 2 minutes! + +[PAGE_02] +Détruis ~1~ véhicules en 2 minutes! + +[PAGE_03] +Roule à côte des membres de gang et tue-en ~1~ en 2 minutes! + +[PAGE_04] +Ecrase ~1~ membres de gang en 2 minutes! + +[PAGE_05] +Eclate la tête de ~1~ membres de gang en 2 minutes! + +[FEC_ABR] +Accélérer, freiner ou marche arrière + +[CLOHELP] +Vêtements changés! + +[FE_MLG] +LEGENDE DE LA CARTE + +[FED_RDR] +RADAR + +[FED_HUD] +L'INTERFACE + +[FED_RDL] +GRAND + +[FED_RDB] +BIPS UNIQUEMENT + +[FED_HUF] +FONDU + +[FEI_BTU] +; = - + +[FEI_SCR] +Fait défiler + +[FEST_HV] +Missions d'Autodéfense, niveau maximum + +[LG_01] +Position du joueur + +[LG_02] +Avery Carrington + +[LG_03] +Motards + +[LG_04] +Colonel Cortez + +[LG_05] +Ricardo Diaz + +[LG_06] +Kent Paul + +[LG_07] +Avocat + +[LG_08] +Phil Cassidy + +[LG_09] +Chantier naval + +[LG_10] +Club Malibu + +[LG_11] +Cubains + +[LG_12] +Studio de Cinéma + +[LG_13] +Ammu-Nation + +[LG_14] +Haïtiens + +[LG_15] +Quincaillerie + +[LG_16] +Planque + +[LG_17] +Crèmes glacées + +[LG_18] +Taxi Kaufman + +[LG_19] +Love Fist + +[LG_20] +Imprimerie + +[LG_21] +Propriété + +[LG_22] +Pay 'n' Spray + +[LG_23] +Boutique de fringues + +[LG_24] +Manoir de Tommy + +[LG_25] +Téléphone + +[LG_26] +Station de radio Wildstyle + +[LG_27] +Station de radio Flash FM + +[LG_28] +Station de radio KChat + +[LG_29] +Station de radio Fever 105 + +[LG_30] +Station de radio VRock + +[LG_31] +Station de radio VCPR + +[LG_32] +Station de radio Espantoso + +[LG_33] +Station de radio Emotion 98.3 + +[LG_34] +Station de radio Onde 103 + +[LG_35] +Destination + +[LG_36] +Sun Yard + +[LG_37] +Boîte de striptease + +[MAP_YAH] +VOUS ETES ICI + +[MAP_LEG] +Légende + +[SENTXS] +Sentinelle XS + +[VCNMAV] +VCN Maverick + +[TAXSHRT] +~g~Au lieu de conduire, tu peux te servir de ce taxi Kaufman pour te déplacer. Cela te coûtera $9. + +[BRIBE1] +Tu viens de récupérer un pot-de-vin de la police. Cela réduira ton indice de recherche d'une étoile. + +[SUNSHIN] +Sunshine Autos + +[KAUFCAB] +Taxis Kaufman + +[BOATYAR] +Le chantier naval + +[WANT_L] +Tu as perdu ton indice de recherche. Ne commets pas de délit tant que les étoiles brillent ou tu seras de nouveau très recherché. + +[HOTRNG] +HOTRING + +[BLODRNG] +BLOODRING + +[DIRTRNG] +DIRTRING + +[IN_VEH] +~g~Eh! Remonte dans ta caisse! + +[HEY] +~g~Te la joue pas perso, reste avec tes potes! + +[HELP3] +T'es vite crevé, ton sprint ne dure donc pas longtemps. + +[HELP4_D] +Pousse le ~h~joystick analogique droit ~w~ vers le haut pour ~h~accélérer. + +[HELP5_D] +Tire le ~h~joystick analogique droit~w~ vers toi pour ~h~freiner~w~ ou pour ~h~reculer~w~ si le véhicule est à l'arrêt. + +[HELP7_A] +Maintiens la ~h~ ~k~~PED_LOCK_TARGET~ ~w~ enfoncée pour ~h~viser~w~ avec le fusil à lunette. + +[HELP7_D] +Maintiens la ~h~ ~k~~PED_LOCK_TARGET~ ~w~ enfoncée pour ~h~viser~w~ avec le fusil à lunette. + +[HELP10] +Ce badge t'indique que t'es recherché par la police. + +[HELP11] +Plus t'as de badges, plus ton indice de recherche est important. + +[HELP13] +Tu as parfois intérêt à emprunter des chemins non indiqués sur le radar. + +[TIMER] +Cette mission est en temps limité. Tu dois donc la réussir avant que le compteur n'atteigne zéro. + +[HORN] +~g~Klaxonne! + +[NOMONEY] +~g~T'as pas assez de cash! + +[REWARD] +RECOMPENSE $~1~ + +[M_FAIL] +ECHEC DE LA MISSION! + +[M_PASS] +MISSION ACCOMPLIE! $~1~ + +[DEAD] +T'ES MORT! + +[BUSTED] +CHOPE! + +[WEATHE1] +TEMPS ENSOLEILLE + +[WEATHE2] +TEMPS TRES ENSOLEILLE + +[WEATHE3] +TEMPS NUAGEUX + +[WEATHE4] +TEMPS PLUVIEUX + +[WEATHE5] +TEMPS BRUMEUX + +[WEATHE6] +TEMPS NORMAL + +[NUMBER] +~1~ + +[LOADCAR] +CHARGEMENT DU VEHICULE... (APPUIE SUR F1 POUR ANNULER) + +[CARSOFF] +Trafic désactivé. + +[CARS_ON] +Trafic activé. + +[TEXTXYZ] +Ecriture des coordonnées sur le fichier... + +[CHEATON] +Mode triche activé + +[CHEATOF] +Mode triche désactivé + +[IMPORT1] +Va dehors et attends ton véhicule. + +[PAGEB11] +Lance-flammes livré à la planque + +[WANT_A] +Tu ne seras arrêté que si tu as un ~h~indice de recherche. + +[WANT_B] +La rangée d'étoiles dans le coin supérieur droit de l'écran symbolise ton ~h~indice de recherche~w~. + +[WANT_C] +Ton ~h~indice de recherche~w~ est désormais de un... + +[WANT_D] +deux... + +[WANT_E] +trois... + +[WANT_F] +Plus ton ~h~indice de recherche~w~ augmente, plus les forces de l'ordre t'en veulent. + +[WANT_G] +Quand tu es ~h~'chopé'~w~, tu es amené au poste de police le plus proche. + +[WANT_H] +Les flics se laisseront corrompre contre toutes tes armes et une partie de ton cash. + +[WANT_I] +Toutes les missions en cours échoueront. + +[WANT_J] +Plus tu joueras, plus tu trouveras de moyens de diminuer ton indice de recherche. + +[WANT_K] +En voiture, les ~h~ATELIERS DE PEINTURE~w~ te permettront d'effacer ton indice de recherche. + +[HEAL_B] +Quand tu es ~h~'H.S'~w~, t'es amené à l'hôpital le plus proche. + +[HEAL_C] +On te confisque alors toute ton artillerie et les docteurs prennent ton argent pour te remettre en forme. + +[HEAL_E] +En jouant, tu trouveras d'autres moyens de te soigner ou de te protéger. + +[SAVE1] +Entre dans la corona pour ~h~sauvegarder la partie~w~. Tu ne peux pas sauvegarder en cours de mission. + +[SAVE2] +Tout véhicule laissé dans ce garage sera enregistré lors de la sauvegarde. + +[AMMU] +Va chez Ammu-Nation pour acheter une arme. + +[R_TIME] +TEMPS DE COURSE : + +[PROP_1] +Tu n'as pas assez de cash pour acheter cette propriété + +[PROP_2] +Tu ne peux pas acheter une propriété en cours de mission + +[IND_ZON] +Vice City Beach + +[COM_ZON] +Partie centrale de Vice City + +[BEACH1] +Ocean Beach + +[BEACH2] +Washington Beach + +[BEACH3] +Vice Point + +[GOLFC] +Leaf Links + +[STARI] +Starfish Island + +[DOCKS] +Port de Vice + +[HAVANA] +Little Havana + +[HAITI] +Little Haiti + +[PORNI] +Prawn Island + +[DTOWN] +Centre + +[VICE_C] +Vice City + +[A_PORT] +Aéroport Escobar + +[JUNKY] +Décharge + +[PISTOL] +Pistolet + +[PYTHON] +.357 + +[UZI] +Uz-1 + +[TEC9] +Tec 9 + +[M4] +M4 + +[INGRAM] +Mac + +[MP5] +MP + +[RUGER] +Kruger + +[SNIPE] +Fusil à lunette + +[GRENADE] +Grenades + +[SHOTGN1] +Fusil à pompe + +[SHOTGN2] +S.P.A.S. 12 + +[SHOTGN3] +Fusil à pompe Stubby + +[ARMOUR] +Gilet pare-balles + +[LASER] +.308 Lunette + +[BASEBAT] +Batte de baseball + +[HAMMER] +Marteau + +[SCREWD] +Tournevis + +[CLEVER] +Couperet + +[MACHETE] +Machette + +[KNIFE] +Couteau + +[KATANA] +Katana + +[CHAINSA] +Tronçonneuse + +[G_COST] +$~1~ + +[CAR_1] +Ambulance + +[MALIBU] +Malibu Club + +[MANSION] +La résidence de Diaz + +[TMANS] +Chez Tommy + +[STRIP] +'Pole Position Club' + +[MALL1] +Centre commercial de North Point + +[BANKINT] +El Banco Corrupto Grande + +[RANGE] +Champ de tir + +[POL_HQ] +QG de Vice City Police + +[INT_B] +Un vieil ami + +[INTB_1] +~g~Va au bureau de l'avocat. + +[LAW_1] +La Fête + +[LAW_2] +Baston de Rue + +[LAW_3] +Jury sous pression + +[LAW_4] +Emeute + +[COL_1] +Salaud de traître + +[COL_2] +Fusillade + +[COL_3] +Anges gardiens + +[COL_4] +Chef, oui, chef ! + +[COL_5] +Sur le Pont + +[COK_1] +Course poursuite + +[COK_2] +Phnom Penh '86 + +[COK_3] +Speedboat + +[COK_4] +Offre & demande + +[KENT_1] +Couloir de la mort + +[ASS_1] +Liquidation + +[BUD_1] +EXTORSION + +[BUD_2] +Rixe de Bar + +[BUD_3] +CopLand + +[CAP_1] +L'encaisseur + +[FIN_1] +Amis proches + +[BANK_1] +Sans Issue + +[BANK_2] +Le Flingueur + +[BANK_3] +Le Chauffeur + +[BANK_4] +Hold-up + +[CNT_1] +La Mèche est vendue + +[CNT_2] +Halte au Messager + +[PORN_1] +Bout d'essai + +[PORN_2] +Dodo Vibro + +[PORN_3] +Martha + +[PORN_4] +Projecteur-G + +[TAX_1] +Taxis Kaufman + +[TAXI_1] +V.I.P. + +[TAXI_2] +Concurrence Amicale + +[TAXI_3] +Cabmaggedon + +[ICE_1] +Distribution + +[TEX_1] +Quatre fers + +[TEX_2] +Funérailles + +[TEX_3] +Demolition Man + +[PHIL_1] +Trafiquant d'armes + +[PHIL_2] +Boomshine Saigon + +[BIKE_1] +Alliage d'Acier + +[BIKE_2] +Colère + +[BIKE_3] +Bécane + +[ROCK_1] +Love Juice + +[ROCK_2] +Psychopathe + +[ROCK_3] +Promo + +[ROCK_4] +Les Love Fist! + +[HAT_1] +Juju + +[HAT_2] +Bombes! + +[HAT_3] +Raclée + +[CUB_1] +Cascades aquatiques + +[CUB_2] +Chair à Canon + +[CUB_3] +Bataille Navale + +[CUB_4] +Vaudou + +[JOB_1] +Accident de la route + +[JOB_2] +Bute la femme! + +[JOB_3] +Autocide + +[JOB_4] +Enregistrement + +[JOB_5] +Détails + +[ANSWER] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~~w~ pour répondre au téléphone. + +[MOB_01A] +Comment ça va, mon pote? C'est Paul! J'ai peut-être quelque chose d'intéressant pour toi, mais faut que je te parle en personne. + +[MOB_01B] +Je suis en train de me la couler douce au Malibu. + +[MOB_01C] +Tu me devras un ou deux petits services en échange, mon mignon. A tout à l'heure... + +[MOB_02A] +Sniiiiiiiiiiiif! Hé, salut, Tommy! Tommy? + +[MOB_02B] +On a un problème à l'imprimerie. Tu ferais mieux d'aller y jeter un coup d'oeil. + +[MOB_02C] +Quelque chose a merdé. C'est le bordel. Faut que j'te laisse. + +[MOB_03A] +Mr Vercetti? J'ai ici un document signé qui établit formellement + +[MOB_03B] +que vous avez racheté toutes les dettes de BJ's Auto. + +[MOB_03C] +La disparition soudaine de BJ ne me laisse pas d'autre choix + +[MOB_03D] +que de vous tenir pour responsable du passif de cette société. + +[MOB_03E] +Jusqu'à ce que ce compte soit complètement soldé + +[MOB_03F] +vous devriez considérer les rues de Vice City comme très dangereuses. + +[MOB_04A] +Comment va, vieille branche? + +[MOB_04B] +Ecoute, Tommy, j'avais oublié de te dire qu'on va avoir besoin de balaises en plus pour le concert. + +[MOB_04C] +Y'a un gang de motards dirigé par Mitch Baker, ça nous ferai une super publicité. Vraiment Rock'n'roll, Baby. + +[MOB_04D] +Règle ça pour moi et je te laisserai accéder aux coulisses du concert, d'accord? + +[MOB_05A] +Bien joué, Tommy, j'suis content d'avoir récupéré ma bécane. + +[MOB_05B] +Dis à Mr Kent Paul que la sécurité sera assurée pour le concert. + +[MOB_05C] +Tu as ma parole. + +[MOB_05D] +Bon, maintenant, évite de t'attirer des emmerdes. + +[MOB_06A] +Tommy! Ceux d'en bas, ils parlent de toi, mon petit... + +[MOB_06B] +Je me disais que tu te laisserais peut-être tenter par un doux foyer et par une soupe au gombo de tata Poulet? Mmm? + +[MOB_06C] +Fais un saut par ma cuisine un de ces jours, hein, Tommy? + +[MOB_08A] +Salut, Tommy, je me disais que t'aurais peut-être besoin d'un conseil en affaires... + +[MOB_08B] +Dès que tu as une opération en route, il faut que tu passes prendre le fric de la semaine. + +[MOB_08C] +Si tu laisses les mecs penser que ce sont eux qui dirigent l'affaire, ils essayent de ronger tes bénefs! Ok? + +[MOB_08D] +Ca va, Ken, je connais le business... + +[MOB_08E] +Ok, ok, je sais que tu sais. Je sais. C'était... + +[MOB_08F] +C'était juste pour te dire que je sais que tu sais que je sais... + +[MOB_08G] +Ouvre simplement l'oeil! + +[MOB_08H] +T'inquiète, Ken, t'inquiète... + +[MOB_09A] +Salut, Léo! J'ai du boulot pour toi! + +[MOB_09B] +C'est pas Léo. + +[MOB_09C] +Si Léo apprend que t'as son téléphone, il va te buter aussi sec! + +[MOB_09D] +Peut-être que Léo est mort. Peut-être même que c'est moi qui l'ai buté et que j'ai hérité de son téléphone. T'avais pas pensé à ça hein, connard? + +[MOB_09E] +T'as buté Léo? Alors, c'est que t'en as dans le froc! Tu veux bosser pour moi? + +[MOB_09F] +Passe au café de mon père dans Little Havana et on pourra causer entre hommes. + +[MOB_10A] +Tommy! Ecoute, j'ai un service à te demander! + +[MOB_10B] +Steve! Comment ça marche, le cinoche? + +[MOB_10C] +Ca va, ça va. Mais on a besoin d'une scène de poursuite en bagnole et on a pas le budget pour ça. + +[MOB_10D] +J'ai laissé des bagnoles dans les environs. Tu sauras quoi faire. + +[MOB_10E] +OK, Steve, je vais voir ce que je peux faire. A plus tard. + +[MOB_11A] +Salut petit, je me suis dit que j'allais te passer un coup de fil pour te donner un bon conseil. + +[MOB_11B] +Salut Avery, qu'est-ce que tu voulais me dire? + +[MOB_11C] +Les perspectives ne manquent pas dans cette ville, si tu possèdes le bon business... Tu vois ce que je veux dire? + +[MOB_11D] +Je pense que oui... + +[MOB_11E] +Je te conseille d'ouvrir l'oeil, et le bon, si tu veux dégotter un bon business. A plus tard. + +[MOB_11F] +Ciao, Avery. + +[MOB12_A] +Tommy! C'est Avery! Ecoute, je suis débordé de boulot pour le moment, + +[MOB12_B] +et j'ai un de mes réprésentants qui a besoin d'être chaperonné jusqu'aux Gator Keys. + +[MOB12_C] +Je suis sur l'achat d'un terrain là-bas, alors j'envoie un mec graisser quelques pattes. + +[MOB12_D] +J'ai besoin que tu t'assures qu'il arrive bien là-bas. OK? + +[MOB12_E] +OK, pas de problème, Avery! Mais où c'est que je le trouve, ce mec? + +[MOB12_F] +Il boucle une affaire au chantier. Je lui ai dit que tu le prendrais là-bas. + +[MOB12_G] +Ok, Avery. J'y vais. A plus tard. + +[MOB13_A] +Vercetti? VERCETTI! Putain, mec, faut que tu me files un coup de main! + +[MOB13_B] +Mr Moffat? Comment va la famille? + +[MOB13_C] +Merde, mais quel con! Tu m'entends? + +[MOB13_D] +Ca m'a fait plaisir d'avoir des nouvelles... + +[MOB13_E] +Attends! Attends! Vercetti, euh... Tommy. Je peux t'appeler Tommy? + +[MOB13_F] +On est tous les deux dans les affaires, pas vrai? Et tu sais renifler les bonnes, hein? + +[MOB13_G] +J'ai pas le temps de causer, viens-en au fait! + +[MOB13_H] +Du FRIC! Voilà le putain de fait! + +[MOB13_I] +Je me suis encore tiré de taule, mais ça prend jamais longtemps avant qu'ils me retombent dessus. Pour eux, c'est qu'un putain de jeu! + +[MOB13_J] +Je t'appelle d'une cabine quelque part dans le trou du cul du monde! + +[MOB13_K] +Sors-moi de là avant qu'ils me chopent encore et... et... ah... meeeerde... + +[MOB13_L] +Eh bien, c'est que je suis très occupé pour les prochaines- + +[MOB13_M] +Non! Me laisse pas dans cette merde! T'as pas de coeur ou quoi? Putain, j'devrais pas avoir à m'abaisser comme ça! + +[MOB13_N] +Je suis à genoux, Tommy! Et c'est les rotules dans la poussière que je te supplie... + +[MOB13_O] +J'imagine que je pourrais passer dans le coin et voir si je te trouve... + +[MOB13_P] +Oh, merde, les voilà! Putain, magne-toi, Tommy! Vite! + +[MOB_14A] +Salut, Tommy, on va bien s'entendre, toi et moi. + +[MOB_14B] +Une tendre amie est venu me sussurrer dans l'oreille que la division SWAT de Vice City a un coffre dans une grosse banque, + +[MOB_14C] +et ils y gardent les pots-de-vin de toutes ces dernières années. + +[MOB_14D] +C'est une sorte de caisse de retraite pour vieux garçons. + +[MOB_14E] +Evidemment, si cette info pouvait t'aider à ramasser un peu de ce pognon, + +[MOB_14F] +j'imagine que tu te sentirais obligé de m'en refiler un peu, pas vrai, Tommy? + +[MOB_14G] +Je vais garder ça à l'esprit, merci, Kent. + +[MOB_14H] +Je m'appelle Paul, abruti! Et je suis du Kent, près de Londres. + +[MOB_14I] +Pour ce que j'en ai à foutre de la campagne anglaise, depuis l'école... + +[MOB15_A] +Tommy, mon vieux, c'est Paul, du Kent. + +[MOB15_B] +Il y a une ou deux authentiques dames qui ont ton nom plein la bouche, au Malibu. + +[MOB15_C] +De quoi tu parles?! + +[MOB15_D] +De gonzesses, de poulettes, de filles, quoi! Et bien élevées, pas le genre à causer pognon. + +[MOB15_E] +Faut que tu viennes voir ça. + +[MOB16_A] +Tommy, c'est Paulo, que pasa amigo? + +[MOB16_B] +Qu'est-ce que tu m'veux, Paul? J'ai pas besoin de fausses fringues de marque. + +[MOB16_C] +Très drôle, mon vieux, mais je fais pas dans la fringue de tapette, moi. + +[MOB16_D] +Bon, je t'appelle parce que j'me demande si je ne pourrais pas avoir un rôle dans un de tes films. + +[MOB16_E] +J'ai fait pas mal de pornos en Angleterre, mon vieux. + +[MOB16_F] +Au plumard, je suis une mitrailleuse lourde. + +[MOB16_G] +Merci de me proposer tes services, Paul, j'y réfléchirai. + +[MOB16_H] +Sérieusement, pense à moi, après tout ce que j'ai fait pour toi... + +[MOB16_I] +Justement, j'essaie de l'oublier. + +[MOB17_A] +Tommy Vercetti, comment ça va, big boss? + +[MOB17_B] +J'ai entendu tous ces trucs sur toi. Un flambeur en ville maintenant... + +[MOB17_C] +Paul, t'es bourré. + +[MOB17_D] +Naan! Pauvre abruti! J'suis pas saoul! + +[MOB17_E] +J'ai juste pris un ou deux verres et quelques tournées, et ça fait deux jours que j'ai pas fermé l'oeil! + +[MOB17_F] +De toute façon, ne me traite pas comme ça. + +[MOB17_G] +Je suis pas une poire. Qui c'est qui t'a lancé dans cette ville? Hein, qui? Moi! + +[MOB17_H] +Ah ouais? + +[MOB17_I] +Ouais! + +[MOB17_J] +Paul, t'énerve pas. J'étais occupé, sois pas stupide. + +[MOB17_K] +Je suis pas stupide, connard! Ils me l'ont dit en maison de redressement! + +[MOB17_L] +Si tu cherches les emmerdes, mon pote, tu vas les trouver! + +[MOB17_M] +Tommy, s'il te plaît! T'étais mon grand espoir! Te fous pas de moi! + +[MOB17_N] +Paul, va roupiller un bon coup, sérieusement. + +[MOB18_A] +Tommy, c'est Paulo, comment ça va, mon pote, je me suis dit que j'allais te passer un petit coup de fil. + +[MOB18_B] +Ah, mon pote, tu croiras jamais l'incroyable petit lot que je viens de lever... + +[MOB18_C] +Elle se promenait juste en bas dans Little Havana, mon pote. + +[MOB18_D] +Elle m'a dit qu'elle s'appelait Mercedes ou un truc comme ça. Ah, mon pote, faut que t'ailles voir cette fille! + +[MOB18_E] +Elle ferait bander un eunuque! Elle m'a dit que j'étais le meilleur coup de sa vie et tout et tout! + +[MOB18_F] +Vas-y, trouve-la. A plus tard. + +[MOB19_A] +Tommy V, c'est KP! Kent Paul. Le bruit court que des mecs veulent te faire la peau. + +[MOB19_B] +Ouvre l'oeil, mon pote. Et oublie pas... Je t'ai rien dit. + +[MOB_20A] +Salut, Tommy, c'est Paul. Je viens juste d'entendre dire que t'avais été un vilain garçon. + +[MOB_20B] +Il y a quelqu'un qui s'est vexé de te voir jouer les caïds et ça lui a donné de mauvaises idées... + +[MOB_20C] +Bon, dis à personne que je t'ai rencardé. Les grandes gueules finissent toujours par l'avoir dans l'os. + +[MOB_20D] +Enfin, j'ai entendu dire que ta tête a été mise à prix et que quelqu'un va essayer de t'avoir, + +[MOB_20E] +alors fais gaffe, et m'oublie pas, mon pote. + +[MOB71_A] +Tommy, Thomas, c'est Cortez. Que pasa? + +[MOB71_B] +Les affaires tournent... Et toi, comment ça va? + +[MOB71_C] +Tommy, ici, c'est toujours la guerre. Excusez la mauvaise ligne, mais on vient juste d'avoir un nouveau coup d'état manqué. + +[MOB71_D] +Le peuple est la plus exigeante des maîtresses. + +[MOB71_E] +Si je compte bien, y'a eu trois révolutions et quatre coups d'état depuis que je suis rentré de Vice City. + +[MOB71_F] +Heureusement, à chaque fois, j'ai pris du galon. + +[MOB71_G] +Je voulais vous demander quelque chose à propos de Mercedes... + +[MOB71_H] +Ouais? Quoi? + +[MOB71_I] +Voilà, Tommy, Tommy, j'ai entendu toutes ces histoires et je sais pas quoi penser... + +[MOB71_J] +Peut-être que tout le monde essaye de m'humilier... + +[MOB71_K] +Peut-être qu'elle se croit tout permis, mais dites-moi juste la vérité, Tommy, c'est vrai? + +[MOB71_L] +Mais dites-moi juste la vérité, Tommy, c'est vrai? + +[MOB71_M] +Qu'est-ce qui est vrai? + +[MOB71_N] +Ces bruits qui courent... Elle va vraiment devenir avocate? + +[MOB71_O] +Ah, Tommy, c'est une véritable honte! Nous, les Cortez, sommes une honorable famille et nous ne tolèrerons jamais qu'une de nos filles devienne avocate. + +[MOB71_P] +Je vous en prie, dites-moi que ce n'est pas vrai, je ne crois pas que je pourrais le supporter. + +[MOB71_Q] +Colonel, je peux vous assurer que Mercedes ne deviendra jamais avocate. Ne vous inquiètez pas. + +[MOB71_R] +Merci, Tommy, cette honte me serait insupportable. C'est une femme, pas un parasite, vous comprenez. + +[MOB71_S] +Je le sais, colonel. + +[MOB71_T] +Bon, vous allez devoir m'excuser, Tommy, mais le nouveau ministre de l'intérieur vient d'arriver... + +[MOB71_U] +Il y a de nombreuses années, j'ai tué son père au cours d'une tentative de coup d'état ratée et je dois me montrer poli. Adios, amigo. + +[MOB22_A] +Tommy, vous vous révèlez très utile, amigo. + +[MOB22_B] +Merci, Cortez. Et mon affaire? + +[MOB22_C] +Tommy, je travaille inlassablement en ce sens pour aller au fond des choses et de la vérité dans cette terrible histoire. + +[MOB22_D] +Vous avez ma parole, mais en attendant, + +[MOB22_E] +acceptez, je vous en prie, les sincères remerciements de mon peuple pour vos actions bénéfiques. + +[MOB_25A] +Tommy, c'est Cortez. Les Français me font beaucoup d'ennuis... + +[MOB_25B] +Les sales hypocrites! Ils passent des siècles à piller les pays pauvres et ils osent me traiter de voleur! + +[MOB_25C] +Je vais avoir besoin de votre aide aussi vite que possible. + +[MOB_25D] +Vite, Tommy, j'ai vraiment besoin de vous. Je hais ces maudits Français! + +[MOB_26A] +Allo, Tommy? + +[MOB_26B] +Ouais? + +[MOB_26C] +C'est moi, Baker. Je voulais juste te dire que j'ai beaucoup apprécié le spectacle. + +[MOB_26D] +Moi et mes gars, on veut te remercier, et te rappeler + +[MOB_26E] +que tu as tout notre respect. Bon, salut. Et continue comme ça, mon pote. + +[MOB_29A] +Allo? Vous êtes bien M. Tommy Vercetti? + +[MOB_29B] +Ouais. + +[MOB_29C] +Bien. J'ai entendu dire que vous êtes l'homme à appeler quand on est victime d'une invasion de vermine. + +[MOB_29D] +Ca se peut... + +[MOB_29E] +Eh bien, là je suis vraiment envahi par la vermine. Les haïtiens sont partout... + +[MOB_29F] +Je m'appelle Umberto Robina et je vous propose une rencontre au Café Robina aussitôt que vous pourrez. + +[MOB_29G] +Moi j'vous le dis, ces maudits haïtiens sont allés trop loin, cette fois... + +[MOB_29H] +Test + +[MOB_30A] +Tommy, c'est Umberto Robina. + +[MOB_30B] +Quoi de neuf au café? + +[MOB_30C] +Oh, merveilleux, Tommy, vraiment incroyable! Ici, plus de mauviettes, Tommy. Juste de vrais hommes et des belles filles! + +[MOB_30D] +Je voulais te dire que moi et papa, maintenant, on te considère comme cubain. + +[MOB_30E] +T'as fait tes preuves, mon frère et t'en as une sacrée paire dans l'pantalon! + +[MOB_30F] +Euh, merci, Umberto. Personne ne m'a dit ça depuis que je suis sorti de taule. Bon, salut. + +[MOB_33A] +Tommy, c'est Phil! Bon, arrête tes conneries et écoute-moi, tu veux? + +[MOB_33B] +Bon. J'ai du whisky extra fort, presque fermenté, et je me demandais si tu voulais pas venir boire un coup... + +[MOB_33C] +Sans déconner, Tommy, si t'as besoin d'un verre ou de décoller de la peinture, t'es le bienvenu, ce truc fera de toi un homme! + +[MOB_33D] +Ca me fait cet effet, même si je vois plus rien d'un oeil. Je t'attends, t'entends? + +[MOB_34A] +Tommy, ça m'a fait vraiment plaisir de bosser avec toi. Je m'étais pas autant marré depuis la guerre du Vietnam, mon pote. + +[MOB_34B] +Bon, si t'as besoin de quelque chose, tu m'appelles, t'entends? + +[MOB_34C] +Je n'oublie jamais ceux avec qui j'ai servi + +[MOB_34D] +et je suis sûr que je peux t'aider, t'entends? + +[MOB_35A] +Tommy, la blessure cicatrise bien. Le truc drôle, c'est que + +[MOB_35B] +j'ai combattu dans six batailles et que je m'en suis toujours sorti sans une égratignure, et voilà! + +[MOB_35C] +'Phil, le manchot d'un bras.' Enfin, j'ai encore une large selection d'armes de poing alors je serai jamais 'Phil, le désarmé', t'entends? + +[MOB_35D] +Aller, mon pote, laisse tomber les conneries sentimentales et va te boire un verre, t'entends? + +[MOB_36A] +Tommy, c'est Phil. Je voulais te remercier pour le coup de main, mon pote. + +[MOB_36B] +Salopard de Charlie, toujours à tendre des embuscades ici ou là, + +[MOB_36C] +mais la blessure cicatrise bien, et ça veut aussi dire que j'aurai plus à truander le gouvernement pour le contrôle d'invalidité. Merci, mon pote. + +[MOB_40A] +Salut, Tommy, c'est Sonny. Alors ce bronzage? + +[MOB_40B] +J'suis pas bronzé. + +[MOB_40C] +Ouais et t'as pas non plus mon pognon, alors je m'interrogeais... + +[MOB_40D] +Je me disais : 'Qu'est-ce qu'il fout?' Alors, je te le demande, Tommy, qu'est-ce que tu fous? + +[MOB_40E] +Je cherche le pognon, Sonny, t'inquéte pas! + +[MOB_40F] +Je m'inquiéte, Tommy, c'est mon genre, + +[MOB_40G] +parce que j'ai ce problème récurrent dans la vie de tomber sur des gens indignes de confiance... + +[MOB_40H] +Ne sois pas indigne de ma confiance, Tommy, s'il te plaît... + +[MOB_40I] +Rends-nous à tous deux ce service. J'attends de tes nouvelles... + +[MOB_41A] +Tommy, tu te souviens de moi? + +[MOB_41B] +Salut, Sonny. + +[MOB_41C] +Ouais, exactement, Sonny, on est de vieux amis, + +[MOB_41D] +mais tu ne m'écris jamais, tu ne m'appelles jamais. Tu ne veux plus qu'on soit amis? + +[MOB_41E] +J'ai été très occupé à régler des trucs ici et là. Tu ne m'aides pas beaucoup, ici. + +[MOB_41F] +Oh, c'est ma faute? Bon, j'ai entendu que tu étais très occupé, d'accord, + +[MOB_41G] +occupé à liquider des barons de la drogue, occupé à prendre le pouvoir. + +[MOB_41H] +Alors je te préviens simplement de ne pas m'oublier, Tommy, parce que moi, je ne t'oublie pas... + +[MOB_42A] +Tommy. + +[MOB_42B] +Sonny. + +[MOB_42C] +Faut croire que t'es à moitié sourd, alors je vais me répéter : + +[MOB_42D] +Tommy, où est ce putain de pognon? Où est cette putain de marchandise? Et où est ma putain de part sur ta nouvelle affaire? + +[MOB_42E] +Tu te fous vraiment d'ma gueule, Tommy, et ça me fait pas rire! + +[MOB_43A] +Tommy! Tommy! J'ai eu Sonny au téléphone. Tu m'écoutes? + +[MOB_43B] +Je ne sais pas pour toi, mais il y a un mec qui menace de buter toute ma famille, + +[MOB_43C] +et ça me fout vraiment les jetons. Qu'est-ce que tu comptes faire? + +[MOB_43D] +Reste calme, Ken, tout va bien se passer. + +[MOB_43E] +Je suis calme! Calme comme un homme qui craint pour sa propre vie! + +[MOB_43F] +Ken, j'ai d'autres choses en tête pour l'instant, + +[MOB_43G] +on s'occupera de Forelli quand l'heure sera venue. + +[MOB_43H] +Je suis calme. J'ai pas l'air calme? J'sais pas, c'est ma voix qui a changé à cause de la mort qui me pend au nez! + +[MOB45_A] +Tommy, faut qu'on cause sérieusement. + +[MOB45_B] +C'est quoi le problème, Lance? + +[MOB45_C] +C'est toi le problème, mon pote, j'ai l'impression que tu m'files pas une part équitable... + +[MOB45_D] +Mais le pire, c'est que tu m'as foutu la honte devant les mecs. Je supporte pas ça. + +[MOB45_E] +Lance, c'est pas ça du tout. T'as fait des bourdes, c'est tout. + +[MOB45_F] +Tommy, je suis pas ton petit messager et encore moins ton garçon de courses. + +[MOB45_G] +Lance, déconne pas et on aura pas d'emmerdes! Et si c'est moi qui déconne, tu peux me rentrer dedans quand tu veux. + +[MOB45_H] +Tommy, j'ai tout fait pour toi et tu me traites comme un con. Fais pas ça. + +[MOB45_I] +Lance, je ne vais pas t'arnaquer ou te planter un couteau dans le dos, ok? + +[MOB45_J] +T'énerve pas. C'est déjà assez dur comme ça, sans que tu perdes les pédales. + +[MOB45_K] +Fais-moi confiance. Tu m'entends? Tu m'entends? + +[MOB45_L] +Je t'entends, Tommy, mais je peux pas supporter ça plus longtemps. + +[MOB45_M] +Lance, arrête de déconner. Maintenant, je te préviens. + +[MOB45_N] +Tu m'entends? Relax! Prends quelques jours de repos et on en reparle, ok? + +[MOB46_A] +Tommy, c'est Lance. + +[MOB46_B] +Ouais? + +[MOB46_C] +Même pas un 'ça fait plaisir d'avoir des tes nouvelles'? Allez, mec, sois sympa! + +[MOB46_D] +Je suis occupé. Qu'est-ce que tu veux? + +[MOB46_E] +Rien. C'était juste pour te dire... Tu sais, Tommy, on peut faire ce truc. + +[MOB46_F] +Toi et moi, sans problème. Tu vois ce que je veux dire? + +[MOB46_G] +Va bien falloir qu'on le fasse, parce que sinon, on est morts, Lance. + +[MOB46_H] +On peut plus reculer, maintenant, mais merci de ton appel. On se reparle plus tard. + +[MOB_47A] +Tommy, c'est Lance, on a de gros problèmes. Amène-toi ici en vitesse. + +[MOB52_A] +Salut, Léo, je crois qu'on a un acheteur pour la marchandise de Diaz. + +[MOB52_B] +Il faut que tu l'appelles, mec, pour organiser le deal, tu comprends? + +[MOB52_C] +T'es où, là? + +[MOB52_D] +T'es sûr que ça va, Léo? T'as une voix bizarre... + +[MOB52_E] +Dis-moi juste où t'es. + +[MOB52_F] +Qui c'est qui cause? Je veux parler à Léo! + +[MOB52_G] +Léo est parti pour un moment, je le remplace. + +[MOB52_H] +Va te faire foutre, connard! + +[MOB54_A] +Salut, Tommy! + +[MOB54_B] +Salut, Mercedes, quoi de neuf? + +[MOB54_C] +J'ai un nouvel appartement dans Vice Point. + +[MOB54_D] +J'ai pensé que tu voudrais peut-être passer un de ces quatre... + +[MOB54_E] +J'adorerai. A plus tard. + +[MOB55_A] +Tommy, c'est moi. + +[MOB55_B] +Salut, Mercedes. + +[MOB55_C] +Tommy, je m'ennuie tellement! Quand est-ce qu'on va s'amuser? + +[MOB55_D] +Comment ça? + +[MOB55_E] +Bon, je sais que t'es très occupé à te battre, à tuer des gens ou à les corrompre, + +[MOB55_F] +mais moi, j'ai envie de m'amuser un peu! Alors, ne m'oublie pas, Tommy! + +[MOB56_A] +Tommy, j'ai entendu dire que tu avais tué Ricardo Diaz! + +[MOB56_B] +Un feu accidentel s'est déclenché dans sa résidence... + +[MOB56_C] +Je pense qu'il a cramé dans sa chemise en acrylique. + +[MOB56_D] +Tommy, je suis si fière de toi! Je savais que tu étais un vrai macho! + +[MOB56_E] +Ah, mon dur de dur, je suis si fière d'être ton amie! + +[MOB56_F] +Je sais que tu vas être très occupé à essayer de t'emparer de cette ville, + +[MOB56_G] +mais ne m'oublie pas, d'accord? + +[MOB57_A] +C'est Mercedes. Je ne t'aime plus, Tommy! + +[MOB57_B] +C'est fini! C'est vrai! Parce que tu n'es plus gentil avec moi! + +[MOB57_C] +Tu ne me traites plus comme une dame! Tu m'ignores et je te déteste! + +[MOB57_D] +Je veux que tu viennes me voir tout de suite! " + +[MOB58_A] +Tommy. + +[MOB58_B] +Salut, Mercedes. + +[MOB58_C] +Bonjour monsieur le vrai dur! Je suis très en colère contre toi, Tommy! + +[MOB58_D] +Ne m'oblige plus jamais à supporter Jezz Torrent! + +[MOB58_E] +Il est pathétique! En plein milieu il a commencé à pleurer à cause de son toutou + +[MOB58_F] +qui est mort quand il avait 7 ans et parce que sa mère ne l'a jamais aimé... + +[MOB58_G] +En plus, Tommy, il porte une perruque et un soutien-gorge dans l'intimité! + +[MOB58_H] +Je t'en veux vraiment! + +[MOB59_A] +Ooh Tommy, c'est Mercedes. + +[MOB59_B] +Je voulais juste te dire que je m'étais éclatée sur le tournage! + +[MOB59_C] +Si tu as quelque chose d'autre de ce genre, pense à moi! + +[MOB59_D] +Je suis sincère! J'ai toujours voulu être actrice! + +[MOB59_E] +Je crois que j'ai beaucoup appris sur l'art dramatique! + +[MOB59_F] +C'est tellement révélateur! Merci beaucoup! On se voit très bientôt, adios! + +[MOB_99] +Rends-toi à la cabine indiquée. + +[MOB_98] +Rends-toi à la cabine indiquée. + +[MOB_97] +Rends-toi à la cabine indiquée. + +[MOB_96] +Rends-toi à la cabine indiquée. + +[MOB_95] +Rends-toi à la cabine indiquée. + +[A_TIME] ++~1~ secondes + +[F_RANGE] +La radio du camion de pompiers ne capte plus rien. Rapproche-toi d'une caserne! + +[DODO_FT] +Tu as 'volé' pendant ~1~ secondes! + +[GA_8] +Utilise le détonateur pour armer la bombe. + +[GA_10] +Bravo. Voilà tes ~1~$. + +[GA_11] +On en a déjà des comme ça. Ca nous sert à rien! + +[GA_12] +Bombe armée + +[GA_13] +Comme un pro! Finis la liste et t'auras un bonus! + +[GA_14] +Toutes les voitures! PARFAIT! Tiens, voilà un petit quelque chose! + +[GA_15] +J'espère que t'aimes la nouvelle couleur. + +[GA_16] +La peinture est en option! + +[GA_19] +Ce modèle ne nous intéresse pas! + +[GA_20] +On en a trop des comme ça! Désolé, mec. + +[CHASE] +Attention maximale des médias + +[CHASE1] +Ignoré + +[CHASE2] +Ennuyeux + +[CHASE3] +Plus ou moins intéressant + +[CHASE4] +Journal local page 7 + +[CHASE5] +Journal local La une + +[CHASE6] +Vice Courier Rubrique 1 + +[CHASE7] +Vice Courier La une + +[CHASE8] +Télé locale 3h du mat' + +[CHASE9] +Télé locale Infos + +[CHASE10] +Télé locale Direct + +[CHASE11] +UFA Today page 12 + +[CHASE12] +UFA Today page 4 + +[CHASE13] +Photo dans UFA Today + +[CHASE14] +Télé nationale 4h du mat' + +[CHASE15] +Télé nationale Infos + +[CHASE16] +Télé nationale Direct + +[CHASE17] +Infos internationales + +[CHASE18] +Crise nationale + +[CHASE19] +Crise internationale + +[CHASE20] +Evénement mondial + +[CHASE21] +Truc de légendes + +[CR_1] +La grue ne peut pas soulever ce véhicule. + +[PU_MONY] +T'as pas assez de liquide. + +[CO_ALL] +Tu les as toutes eues. Tiens, voilà pour toi... + +[FEM_ON] +Activé + +[FEM_OFF] +Désactivé + +[FEM_YES] +Oui + +[FEM_NO] +Non + +[FEM_RET] +Réessayer + +[FEC_NA] +NA + +[FEC_CWL] +Faire défiler les armes vers la gauche + +[FEC_CWR] +Faire défiler les armes vers la droite + +[FEC_LOF] +Regarder devant + +[FEC_TAR] +Viser + +[FEC_MOV] +Déplacement + +[FEC_CAM] +Modes caméra + +[FEC_PAU] +Pause + +[FEC_ENV] +Monter dans un véhicule + +[FEC_JUM] +Sauter + +[FEC_ATT] +Attaquer ou tirer + +[FEC_RUN] +Courir + +[FEC_FPC] +Vue subjective + +[FEC_LL] +Rergarder à gauche + +[FEC_LB] +Regarder derrière + +[FEC_LR] +Regarder à droite + +[FEC_HOR] +Klaxon + +[FEC_VES] +Commandes du véhicule + +[FEC_BRA] +Freiner ou marche arrière + +[FEC_HAB] +Frein à main + +[FEC_CAW] +Arme du véhicule + +[FEC_ACC] +Accélérateur + +[FEC_CCF] +Configuration : + +[FEC_CF1] +Config. 1 + +[FEC_CF2] +Config. 2 + +[FEC_CF3] +Config. 3 + +[FEC_CF4] +Config. 4 + +[FEC_CDP] +Affichage manette : + +[FEC_ONF] +à pied + +[FEC_INC] +En voiture + +[FEC_VIB] +Vibrations : + +[FEL_ENG] +Anglais + +[FEL_FRE] +Français + +[FEL_GER] +Allemand + +[FEL_ITA] +Italien + +[FEL_SPA] +Espagnol + +[FED_DBG] +Menu Debug + +[FED_RID] +Recharger IDE + +[FED_RIP] +Recharger IPL + +[FED_PAH] +Analyser Saut + +[FED_DFL] +CTheScripts::DbgFlag + +[FED_DLS] +Lumière blanche de debug allumée + +[FED_SPR] +Affiche les groupes de pietons + +[FED_SCR] +Affiche les groupes en voiture + +[FED_SCZ] +Affiche les zones de massacre + +[FED_DSR] +Debug les demandes de répartition par niveaux + +[FED_SCP] +gb Indique les polys de collision + +[PL_STAT] +Stats du joueur + +[PE_WAST] +Personnes tuées par toi + +[PE_WSOT] +Personnes tuées par d'autres + +[TM_BUST] +Nb de fois capturé + +[GNG_WST] +Gangsters tués + +[DED_CRI] +Criminels tués + +[PER_COM] +Pourcentage accompli + +[KGS_EXP] +Kilos d'explosifs utilisés + +[ACCURA] +Précision + +[ST_WEAP] +Budget arsenal + +[ST_PROP] +Budget propriété + +[ST_AUTO] +Budget réparation et peinture voiture + +[ST_PHOT] +Photos prises + +[ST_LOAN] +Visites des usuriers + +[ST_STOR] +Magasins liquidés + +[ST_MOVI] +Cascades de films + +[ST_PIZZ] +Pizza livrées + +[ST_GARB] +Collectes de détritus effectuées + +[ST_ICEC] +Crèmes glacées vendues + +[TOP_SHO] +Meilleur score au champ de tir + +[SHO_RAN] +Position au champ de tir + +[SEAGULL] +Mouettes descendues + +[PROPOWN] +Propriété possédée + +[ST_TIME] +Temps de jeu + +[ST_FTIM] +Heures de vol + +[ST_PRAN] +Classement pilote + +[ST_RAN0] +Apprenti + +[ST_RAN1] +Navigateur + +[ST_RAN2] +Copilote + +[ST_RAN3] +Junior + +[ST_RAN4] +Compétent + +[ST_RAN5] +Senior + +[ST_RAN6] +As + +[ST_RAN7] +Baron rouge + +[ST_DRWN] +Poissons nourris + +[ST_FASH] +Budget fringues + +[ST_DAMA] +Propriété détruite + +[TM_DED] +Visites à l'hôpital + +[DAYSPS] +Jours passés dans le jeu + +[NUMSHV] +Visites dans l'entrepôt + +[MXCARD] +Dist. max. de saut de Cascade dangereuse (ft) + +[MXCARJ] +Hauteur max. de saut de Cascade dangereuse(ft) + +[MXCARDM] +Dist. max. de saut de Cascade dangereuse (m) + +[MXCARJM] +Hauteur max. de saut de Cascade dangereuse (m) + +[MXFLIP] +Saltos max. de saut de Cascade dangereuse + +[MXJUMP] +Rotation max. de saut de Cascade dangereuse + +[BUL_FIR] +Balles tirées + +[BUL_HIT] +Balles ayant atteint la cible + +[SPRAYIN] +Peintures + +[BSTSTU] +Meilleure cacade jusqu'à maintenant + +[INSTUN] +Cascade dangereuse + +[PRINST] +Cascade dangereuse parfaite + +[DBINST] +Double cascade dangereuse + +[DBPINS] +Double cascade dangereuse parfaite + +[TRINST] +Triple cascade dangereuse + +[PRTRST] +Triple cascade dangereuse parfaite + +[QUINST] +Quadruple cascade dangereuse + +[PQUINS] +Quadruple cascade dangereuse parfaite + +[NOSTUC] +Aucune cascade dangereuse réalisée + +[NOUNIF] +Sauts uniques accomplis + +[NMISON] +Tentatives de mission + +[PASDRO] +Passagers perdus + +[MONTAX] +Total des courses en taxi + +[DAYPLC] +Dépenses quotidiennes de police + +[CRIMRA] +Taux de criminalité : + +[STPR_1] +Le Malibu + +[STPR_2] +Imprimerie + +[STPR_3] +Studio de cinéma + +[STPR_4] +Usine de crème glacée + +[STPR_5] +Concession automobile + +[STPR_6] +Compagnie de taxis + +[STPR_7] +Chantier naval + +[SET1EN] +Config. 1. Activée + +[GMSAVE] +Sauvegarder partie + +[FEDS_TB] +Retour + +[FEST_OO] +sur + +[FEC_TUC] +Commandes tourelle + +[FEC_RS3] +Faire défiler les stations de radio (touche L3) + +[FEC_HO3] +Klaxonner (touche L3) + +[C_FAIL] +Mission d'autodéfense terminée! + +[C_ESCP] +~r~Le suspect s'est échappé! + +[C_VIGIL] +BONUS AUTODEFENSE! + +[HEAL_A] +Ton niveau de ~h~santé~w~ s'affiche en orange en haut à droite de l'écran. + +[WRONGCD] +Disque invalide. Insère le bon disque. + +[NOCD] +Le compartiment à disque est vide. Insère un disque. + +[OPENCD] +Le compartiment à disque est ouvert. Referme-le. + +[CDERROR] +Erreur au cours de la lecture du DVD de Grand Theft Auto: Vice City + +[RESTART] +Démarrage d'une nouvelle partie + +[GA_3] +Plus de cadeaux. 100$ pour repeindre! + +[GA_1] +Oula! Je ne touche à rien d'aussi chaud, moi! + +[GA_1A] +Reviens quand tu seras moins occupé... + +[HELP9_C] +Appuie sur la~h~ ~k~~PED_FIREWEAPON~ ~w~pour ~h~tirer~w~ avec le fusil à lunette. + +[TAXI2] +~r~Tu n'as plus le temps! + +[PAGEB13] +Santé livrée à la planque + +[PAGEB14] +Adrénaline livrée à la planque + +[FESZ_CA] +Annuler + +[FES_NGA] +Nouvelle partie + +[FES_CAN] +Annuler + +[FESZ_QL] +Toutes les étapes non sauvegardées de cette partie seront perdues. Charger la partie? + +[FESZ_QD] +Effacer cette sauvegarde? + +[FESZ_QO] +Ecraser cette sauvegarde? + +[FESZ_QR] +Veux-tu vraiment commencer une autre partie? Toutes les données depuis la dernière partie sauvegardée seront perdues. Continuer? + +[T4X4_1] +'Terrain de jeu PCJ' + +[BMX_1] +'Sélection par la boue' + +[BMX_2] +'Piste d'essai' + +[BMXFAIL] +~r~Tu n'as pas réussi à établir un nouveau record! + +[T4X4_3] +'DANS LA POCHE!' + +[MM_1] +'Cônes en folie' + +[T4X4_F] +~r~Tu t'es barré! Trop dur pour toi? + +[LANDSTK] +Landstalker + +[IDAHO] +Idaho + +[STINGER] +Stinger + +[LINERUN] +Linerunner + +[PEREN] +Perennial + +[SENTINL] +Sentinelle + +[RIO] +Rio + +[PATRIOT] +Patriote + +[FIRETRK] +Camion de pompier + +[TRASHM] +Trashmaster + +[STRETCH] +Stretch + +[MANANA] +Manana + +[INFERNS] +Infernus + +[VOODOO] +Voodoo + +[PONY] +Pony + +[MULE] +Mule + +[CHEETAH] +Cheetah + +[AMBULAN] +Ambulance + +[FBICAR] +FBI Washington + +[MOONBM] +Moonbeam + +[ESPERAN] +Esperanto + +[TAXI] +Taxi + +[WASHING] +Washington + +[BOBCAT] +Bobcat + +[WHOOPEE] +Mr Whoopee + +[BFINJC] +BF Injection + +[HUNTER] +Hunter + +[POLICAR] +Police + +[ENFORCR] +Enforcer + +[SECURI] +Securicar + +[BANSHEE] +Banshee + +[PREDATR] +Predator + +[BUS] +Bus + +[RHINO] +Rhino + +[BARRCKS] +Barracks OL + +[CUBAN] +Cuban Hermes + +[HELI] +Hélicoptère + +[DODO] +Dodo + +[COACH] +Coach + +[CABBIE] +Cabbie + +[STALION] +Stallion + +[RUMPO] +Rumpo + +[RCBANDT] +RC Bandit + +[ROMERO] +Corbillard de Romero + +[PACKER] +Packer + +[ADMIRAL] +Amiral + +[SQUALO] +Squalo + +[SEASPAR] +Sea Sparrow + +[PIZZABO] +Pizza Boy + +[GANGBUR] +Gang Burrito + +[TROPIC] +Tropic + +[SPEEDER] +Speeder + +[REEFER] +Reefer + +[FLATBED] +Flatbed + +[YANKEE] +Yankee + +[CADDY] +Caddy + +[ZEBRA] +Taxi Zebra + +[TOPFUN] +Top Fun + +[SKIMMER] +Skimmer + +[PCJ600] +PCJ 600 + +[PHOENIX] +Phoenix + +[FAGGIO] +Faggio + +[FREEWAY] +Freeway + +[RCBARON] +RC Baron + +[RCRAIDE] +RC Raider + +[GLENDAL] +Glendale + +[OCEANIC] +Oceanic + +[SANCHEZ] +Sanchez + +[SPARROW] +Sparrow + +[LOVEFIS] +Love Fist + +[COASTG] +Garde-côte + +[DINGHY] +Dinghy + +[HERMES] +Hermes + +[SABRE] +Sabre + +[SABRETU] +Sabre Turbo + +[WALTON] +Walton + +[REGINA] +Regina + +[COMET] +Comet + +[DELUXO] +Deluxo + +[BURRITO] +Burrito + +[SPAND] +Spandex Express + +[MARQUIS] +Marquis + +[BAGGAGE] +Bagagiste + +[KAUFMAN] +Taxi Kaufman + +[COASTMA] +Garde-côte Maverick + +[MAVERIC] +Maverick + +[RANCHER] +Rancher + +[FBIRANC] +FBI Rancher + +[VIRGO] +Virgo + +[GREENWO] +Greenwood + +[HOTRING] +Hotring Racer + +[BLISTAC] +Blista Compact + +[FEST_DF] +Dist. parcourue à pied (miles) + +[FEST_DC] +Dist. parcourue en voiture (miles) + +[FESTDFM] +Distance parcourue à pied (m) + +[FESTDCM] +Distance parcourue en voiture (m) + +[TOT_DIS] +Distance totale parcourue (miles) + +[TOTDISM] +Distance totale parcourue (m) + +[DISTHEL] +Dist. parcourue en hélicoptère (miles) + +[DISTHEM] +Dist. parcourue en hélicoptère (m) + +[DISTBOA] +Dist. parcourue en bateau (miles) + +[DISTBOM] +Dist. parcourue en bateau (m) + +[FEST_LS] +Personnes secourues en ambulance + +[FEST_CC] +Criminels tués lors de la mission d'autodéfense + +[FEST_FE] +Nombre total de feux éteints + +[FEST_RP] +Rodéos réussis + +[FEST_MP] +Missions réussies + +[FEST_BB] +Les fous du volant : + +[FEST_H0] +Nombre maximum de points de passage + +[FEST_GC] +Nombre total de voitures de gang : + +[FEST_H1] +Destruction de diablo + +[FEST_H2] +Massacre de la Mafia + +[FEST_H3] +Le désastre du casino + +[FEST_H4] +Le destructeur de Rumpo + +[USJ] +BONUS POUR CASCADE UNIQUE! + +[RATNG1] +Citoyen honnête + +[RATNG2] +Quidam + +[RATNG3] +Eboueur + +[RATNG4] +Voleur à l'étalage + +[RATNG5] +Vandale + +[RATNG6] +Escroc + +[RATNG7] +Pickpocket + +[RATNG8] +Cleptomane + +[RATNG9] +Mouchard + +[RATNG10] +Rat + +[RATNG11] +Sangsue + +[RATNG12] +Le roi de l'arnaque + +[RATNG13] +Aigrefin + +[RATNG14] +Grippe-sou + +[RATNG15] +Crapule + +[RATNG16] +Rascal + +[RATNG17] +Racaille + +[RATNG18] +Garnement + +[RATNG19] +Vaurien + +[RATNG20] +Hors-la-loi + +[RATNG21] +Voyou + +[RATNG22] +Lâche + +[RATNG23] +Idiot SA + +[RATNG24] +Idiot + +[RATNG25] +Récidiviste + +[RATNG26] +Ancien taulard + +[RATNG27] +Criminel + +[RATNG28] +Clochard + +[RATNG29] +Sage + +[RATNG30] +Pilote + +[RATNG31] +Gros bras + +[RATNG32] +Tueur à gages + +[RATNG33] +Chasseur de tête + +[RATNG34] +Force de l'ordre + +[RATNG35] +Ronin + +[RATNG36] +Magouilleur + +[RATNG37] +Tueur + +[RATNG38] +Associé + +[RATNG39] +Boucher + +[RATNG40] +Nettoyeur + +[RATNG41] +Assassin + +[RATNG42] +Conseiller + +[RATNG43] +Autodidacte + +[RATNG44] +Bras droit + +[RATNG45] +Exécutant + +[RATNG46] +Lieutenant + +[RATNG47] +Sous-chef + +[RATNG48] +Capo + +[RATNG49] +Patron + +[RATNG50] +Noops + +[RATNG51] +Don + +[RATNG52] +Roi de la putain de ville + +[PAGE_00] +. + +[WELCOME] +BIENVENUE A + +[TSCORE] +REVENUS : ~1~$ + +[PBOAT_2] { reVC update } +Appuie sur la ~h~~k~~VEHICLE_FIREWEAPON~~w~ pour tirer avec les canons du bateau. + +[HJSTAT] +Distance : ~1~.~1~ m Hauteur : ~1~.~1~ m Saltos : ~1~ Rotation : ~1~_ + +[HJSTATW] +Distance : ~1~.~1~ m Hauteur : ~1~.~1~ m Saltos : ~1~ Rotation : ~1~_ Et quelle belle réception! + +[ATUTOR] +Appuie sur la ~h~~k~~TOGGLE_SUBMISSIONS~~w~ pour activer ou désactiver les missions ambulance. + +[FEST_HA] +Mission ambulance, niveau maximum + +[C_KILLS] +CRIMINELS TUES : ~1~ + +[HJSTATF] +Distance : ~1~ ft Hauteur : ~1~ ft Saltos : ~1~ Rotation : ~1~_ + +[HJSTAWF] +Distance : ~1~ ft Hauteur : ~1~ ft Saltos : ~1~ Rotation : ~1~_ Et quelle belle réception! + +[CRED001] +ROCKSTAR NORTH + +[CRD001A] +DIRECTEUR DU STUDIO + +[CRD001B] +ANDREW SEMPLE + +[CRED002] +PRODUCTEUR + +[CRD002A] +DIRECTEUR DU DEVELOPPEMENT + +[CRED003] +LESLIE BENZIES + +[CRED004] +CONCEPTEUR GRAPHIQUE + +[CRED005] +AARON GARBUT + +[CRED006] +DIRECTEURS TECHNIQUES + +[CRED007] +OBBE VERMEIJ + +[CRED008] +ADAM FOWLER + +[CRED010] +ANDREW DUTHIE + +[CRED011] +CRAIG FILSHIE + +[CRED012] +WILLIAM MILLS + +[CRED013] +CHRIS ROTHWELL + +[CRD013A] +IMRAN SARWAR + +[CRD013B] +JAMES WORRALL + +[CRD013C] +JOHN HAIME + +[CRED014] +ECRIT PAR + +[CRED015] +JAMES WORRALL + +[CRED016] +PAUL KUROWSKI + +[CRED017] +DAN HOUSER + +[CRED018] +CONCEPTEUR PRINCIPAL DES PERSONNAGES + +[CRD018A] +CONCEPTEUR DES PERSONNAGES + +[CRED019] +IAN MCQUE + +[CRD019A] +TOKS SOLARIN + +[CRD019B] +ALAN DAVIDSON + +[CRED020] +ANIMATEUR PRINCIPAL + +[CRED021] +ALEX HORTON + +[CRD022A] +ANIMATEURS + +[CRED022] +LEE MONTGOMERY + +[CRD022B] +DUNCAN SHIELDS + +[CRD022C] +GUS BRAID + +[CRED023] +CONCEPTEURS DES VEHICULES + +[CRD023A] +JOLYON ORME + +[CRD023B] +ALAN DUNCAN + +[CRD024A] +CONCEPTEUR PRINCIPAL DES VEHICULES + +[CRED024] +PAUL KUROWSKI + +[CRED025] +CONCEPTEURS DE CARTE + +[CRED026] +ADAM COCHRANE + +[CRED027] +NIK TAYLOR + +[CRED028] +GARY MCADAM + +[CRED029] +KEIRAN BAILLIE + +[CRED030] +ALISDAIR WOOD + +[CRED031] +ANDREW SOOSAY + +[CRD031A] +STEVEN MULHOLLAND + +[CRD031B] +WAYLAND STANDING + +[CRD031C] +CAMPBELL J. DICK + +[CRD031D] +CONCEPTEUR GRAPHIQUE + +[CRD031E] +STUART PETRI + +[CRED032] +PROGRAMMEUR PRINCIPAL + +[CRD032A] +PROGRAMMEURS + +[CRED033] +ALEXANDER ROGER + +[CRED034] +GRAEME WILLIAMSON + +[CRED035] +BARANE CHAN + +[CRED036] +DEREK PAYNE + +[CRED037] +GORDON YEOMAN + +[CRD037A] +ALAN CAMPBELL + +[CRD037B] +MARK HANLON + +[CRD037C] +ANDRZEJ MADAJCZYK + +[CRED038] +PRODUCTEUR MUSIQUE PRINCIPAL + +[CRED039] +CRAIG CONNER + +[CRED040] +STUART ROSS + +[CRED041] +INGENIEUR AUDIO PRINCIPAL + +[CRED042] +ALLAN WALKER + +[CRD041A] +INGENIEUR DU SON + +[CRD041B] +AUDIO + +[CRD042A] +WILL MORTON + +[CRED043] +PROGRAMMEUR AUDIO + +[CRED044] +RAYMOND USHER + +[CRED045] +RESPONSABLE DES TESTS + +[CRED046] +CRAIG ARBUTHNOTT + +[CRED047] +RESPONSABLE CQ + +[CRED048] +NEIL CORBETT + +[CRED049] +KEVIN WONG + +[CRED050] +CQ + +[CRED051] +DAVID BEDDOES + +[CRED052] +DAVID WATSON + +[CRED053] +BARRY CLARK + +[CRED054] +ROSS SPARROW + +[CRED055] +JAMES ALLAN + +[CRED056] +NEIL MEIKLE + +[CRD056A] +GEORGE WILLIAMSON + +[CRD056B] +MATT JONES + +[CRD056C] +ROB HARBOUR + +[CRD056D] +TOM WHITTAKER + +[CRED057] +ASSISTANCE TECHNIQUE PRINCIPALE + +[CRED058] +LORRAINE ROY + +[CRD057A] +ASSISTANCE TECHNIQUE + +[CRED059] +CHRISTINE CHALMERS + +[CRD060A] +SERVICES GENERAUX + +[CRD060B] +KIM GURNEY + +[CRD060C] +CASSIE OLIVER + +[CRED060] +ROCKSTAR NEW YORK + +[CRED061] +PRODUCTEUR EXECUTIF + +[CRED062] +SAM HOUSER + +[CRED063] +PRODUCTEUR + +[CRED064] +DAN HOUSER + +[CRED065] +VICE-PRESIDENT DU DEVELOPPEMENT + +[CRED066] +JAMIE KING + +[CRED067] +RESPONSABLE TECHNOLOGIE + +[CRED068] +GARY J. FOREMAN + +[CRED069] +PRODUCTEUR ASSOCIE + +[CRED070] +JEREMY POPE + +[CRD071A] +RESPONSABLE CONTROLE QUALITE + +[CRD072A] +JEFF ROSA + +[CRED071] +RESPONSABLE MUSIQUE + +[CRED072] +TERRY DONOVAN + +[CRED073] +EQUIPE DE PRODUCTION + +[CRED074] +TERRY DONOVAN + +[CRED075] +JENNIFER KOLBE + +[CRED076] +JENEFER GROSS + +[CRED077] +LAURA PATERSON + +[CRED078] +JEFF CASTANEDA + +[CRED079] +JERONIMO BARRERA + +[CRED080] +CARLY SLATER + +[CRED081] +JUNG KWAK + +[CRED082] +BRIAN WOOD + +[CRED083] +RENAUD SEBANNE + +[CRED084] +RICHARD KRUGER + +[CRD084A] +DANIEL EINZIG + +[CRD084B] +JACEN BURROWS + +[CRD084C] +LINN PR + +[CRD084D] +COUVERTURE + +[CRD084E] +STEPHEN BLISS + +[CRED085] +KENT PAUL'S 80 NOSTALGIA ZONE + +[CRED086] +ECRIT PAR DAN HOUSER + +[CRD086A] +PRODUIT PAR ADAM TEDMAN + +[CRED087] +WWW.KENTPAUL.COM ET WWW.VICECITY.COM + +[CRED088] +CREES PAR + +[CRD088A] +ADAM TEDMAN + +[CRD088B] +DAVID YU + +[CRD088C] +JERRY LUNA + +[CRD088D] +STUART PETRI + +[CRD088E] +MICHAEL CARNEVALE + +[CRD088F] +GREG LAU + +[CRD088G] +FUTABA HAYASHI + +[CRED089] +RESPONSABLE CQ + +[CRED090] +CRAIG ARBUTHNOTT + +[CRED091] +ANALYSTE PRINCIPAL + +[CRED092] +ADAM DAVIDSON + +[CRD092A] +JOE HOWELL + +[CRD092B] +MARC FERNANDEZ + +[CRED093] +ANALYSTE DE JEU + +[CRED094] +RICHARD HUIE + +[CRED095] +EQUIPE DE TEST CHEZ ROCKSTAR + +[CRED096] +LANCE WILLIAMS + +[CRED097] +JOE GREENE + +[CRED098] +BRIAN PLANER + +[CRD098A] +ELIZABETH SATTERWHITE + +[CRD098B] +JAMEEL VEGA + +[CRD098C] +MIKE HONG + +[CRED099] +LEE CUMMINGS + +[CRED100] +HISTOIRE + +[CRED101] +JAMES WORRALL + +[CRED102] +DAN HOUSER + +[CRED103] +ADAM TEDMAN + +[CRED104] +PAUL YEATES + +[CRED105] +JENEFER GROSS + +[CRED106] +LAURA PATERSON + +[CRED107] +CINEMATIQUES + +[CRED108] +ECRITES PAR DAN HOUSER ET JAMES WORRALL + +[CRED109] +DIRECTION DE L'AUDIO PAR DAN HOUSER ET NAVID KHONSARI + +[CRED110] +PRODUCTION DE JAMIE KING + +[CRD110A] +CASTING : JAMIE KING, SEAN MACALUSO + +[CRED111] +DISTRIBUTION + +[CRD111A] +PERSONNAGES SECONDAIRES + +[CRED112] +TOMMY VERCETTI : RAY LIOTTA + +[CRED113] +KEN ROSENBERG : WILLIAN FICHTNER + +[CRED114] +SONNY FORELLI : TOM SIZEMORE + +[CRED115] +STEVE SCOTT : DENNIS HOPPER + +[CRED116] +AVERY CARRINGTON : BURT REYNOLDS + +[CRED117] +RICARDO DIAZ : LUIS GUZMAN + +[CRED118] +LANCE VANCE : PHILIP MICHAEL THOMAS + +[CRED119] +COLONEL JUAN CORTEZ : ROBERT DAVI + +[CRED120] +UMBERTO ROBINA : DANNY TREJO + +[CRED121] +PHIL CASSIDY : GARY BUSEY + +[CRED122] +MITCH BAKER : LEE MAJORS + +[CRED123] +MERCEDES CORTEZ : FAIRUZA BALK + +[CRED124] +KENT PAUL : DANNY DYER + +[CRED125] +JEZZ TORRENT : KEVIN MCKIDD + +[CRED126] +REGULATRICE TAXI : DEBORAH HARRY + +[CRED127] +CANDY SUXX : JENNA JAMESON + +[CRED128] +BJ SMITH : LAWRENCE TAYLOR + +[CRED129] +TATA POULET : YOUREE CLEOMILI HARRIS + +[CRED130] +FOURNISSEUR : ARMANDO RIESCO + +[CRED131] +COUGAR : BLAYNE PERRY + +[CRED132] +HILARY : CHARLES TUCKER + +[CRED133] +ALEX SHRUB, REPRESENTANT DU CONGRES : CHRIS LUCAS + +[CRED134] +LE VIEUX KELLY : GEORGE DICENZO + +[CRD134A] +CAM JONES : GREG SIMS + +[CRD134B] +PSYCHOPATHE : HUNTER PLATIN + +[CRD134C] +MAUDE, LA VENDEUSE DE GLACES : JANE GENNARO + +[CRD134D] +JETHRO : JOHN ZURHELLEN + +[CRD134E] +GONZALES : JORGE PUPO + +[CRD134F] +DWAYNE : NAVID KHONSARI + +[CRD134G] +DICK : PETER MCKAY + +[CRD134H] +MIKE, L'ACTEUR DE PORNO : ROBERT CIHRA + +[CRD134I] +PERCY : RUSSELL FOREMAN + +[CRED135] +CAPTURE DE MOUVEMENTS + +[CRED136] +ANIME PAR + +[CRD136A] +DIRECTION TECHNIQUE PAR ALEX HORTON + +[CRED137] +DIRIGE PAR + +[CRD137A] +DIRIGE PAR NAVID KHONSARI + +[CRED138] +PRODUIT PAR + +[CRD138A] +PRODUIT PAR JAMIE KING + +[CRD138B] +RENAUD SEBBANE + +[CRED139] +ENREGISTRE AUX PERSPECTIVE STUDIOS, BROOKLYN + +[CRED140] +ACTEURS POUR LA CAPTURE DE MOUVEMENTS + +[CRD140A] +BLAYNE PERRY + +[CRD140B] +JONATHON SALE + +[CRD140C] +CHARLES TUCKER + +[CRD140D] +EDDIE MARRERO + +[CRD140E] +WILLIAM MCCALL + +[CRD140F] +JORGE PUPO + +[CRD140G] +ROBERT JACKSON + +[CRD140H] +TARA RADCLIFFE + +[CRD140I] +JENIFER GAMBETESE + +[CRD140J] +KRIS ACHEVARRIA + +[CRD140K] +ALI ORDOUBADI + +[CRD140L] +KAHLEEM POOLE + +[CRED141] +DIALOGUES DES PIETONS + +[CRD141A] +ECRIT PAR DAN HOUSER, MARC FERNANDEZ, GILLIAN TELLING ET NAVID KHONSARI + +[CRD141B] +AVEC L'AIDE DE JEREMY POPE, LANCE WILLIAMS ET JENNY JEMISON + +[CRED142] +ECRITS PAR + +[CRD142A] +DAN HOUSER ET JAMES WORRALL + +[CRED143] +DIRIGE PAR DAN HOUSER, CRAIG CONNER, MARC FERNANDEZ ET ALLAN WALKER + +[CRED144] +PRODUITS PAR RENAUD SEBANNE + +[CRED145] +PIETONS + +[CRED146] +ADAM DAVIDSON, ADAM WATKINS, ALEJANDRO K. BROWN, ALEX ANTHONY SIOUKAS, ALEX GARCIA, + +[CRED147] +ALICE SALTZMAN, ALISON CIHRA, AMY SALIMA, AMY SALZMAN, ANDREA VIDELA, ANTHONY ATTI, + +[CRED148] +ANTHONY RIVERA, BIJAN SHAMS, BLAYNE PERRY, BRETT BISOGNO, BREYE MATA, BRIAN PANEN, + +[CRED149] +BROCK VODER, CAREY BERTINI, CHARISSE LAMBERT, CHRIS DIFAT, CHRIS REISENBERGER, + +[CRED150] +CHRISTOPHER BRODAY, CHRISTOPHER CARRO, CYNTHIA GREENE, DAMARIES LOPEZ, DAN LEE, + +[CRED151] +DAN SCHNEIDER, DAN TOYAMA, DAVID DEAN CHALTFIELD, JR., DAVID HARRISON, DAVID WILEY, + +[CRED152] +DEBORAH COLLINS, DEBRANDA CHANEY-GILES, DEMETRA KOUKOULAS, DENISE ROSADO, + +[CRED153] +DEVIN BENNETT, DEVIN WINTERBOTTOM, DORIS WOO, DOUGLAS HARRISON, DUNCAN COUTTS, + +[CRED154] +DUPE AJAYI, EDWIN AVELLANEDA, ELIZABETH HOWELL, ELIZABETH SATTERWHITE, ERIC NAGLE, + +[CRED155] +ESTEBAN KARPLUS, F. FONT, FUTABA HAYASHI, GENE HILGREEN, GERALD COSGROVE, + +[CRED156] +GERARD LUNA, GILLIAN TELLING, GREGG CARLUCCI, GREGORY CLERVOIX, GREGORY SCHWEIZER, + +[CRED157] +HADLEY TOMICKI, J. ROSSETT, JAMEEL VEGA, JASON JONES, JEFF ROSA, JENNIFER JEMISON, + +[CRED158] +JEREMY TAGGERT, JESSICA RIDER, JOSEPH GREENE,JOSEPH HOWELL, KATE DUKICH, + +[CRED159] +KEL O'NEILL, KEVIN HOPKINS, LADAWN JAMES, LANCE WILLIAMS, LAURA BUBBLES, + +[CRED160] +LAURA PATTERSON, LEE CUMMINGS, LETICIA L. YOUNG, LINDSAY KENNEDY, LISA ORITZ, + +[CRED161] +LORNA JORDAN, LUCIO AMADIO, MARCO FERNANDEZ, MARIKO TANAKA, MARLON MATTHEWS, + +[CRED162] +MARY TELLING, MASAYOSHI MITSUYAMA, MATTHEW CHUNG, MAX ALLSTADT, MAX BOGDANOV, + +[CRED163] +MELISSA ALVAREZ, MICHAEL MAY, MICHAEL ROTHSTEIN, MIGUEL VIDAL, MIKE FEDERLINE, + +[CRED164] +NATALIE DESCALZO, N'GAI MEMBERS, NICOLAS MALLO, NOELLE SADLER, NORBERT MORIVAN, + +[CRED165] +OSWALD GREENE, JR., PETER MCKAY, PETER APPEL, PRESTON SAVARESE, RAFAEL GONZALES, + +[CRED166] +RANDY JOHNSON, REY CONCEPCION, RICHARD KROGER, ROB TIBBS, ROBERT JACKSON, + +[CRED167] +ROBERT SCHULER, ROSS A. MCINTYRE, RUSSELL FOREMAN, RUTH NUNEZ, SALVADORE SUAZO, + +[CRED168] +SAM WHITE, SANTOS GONZALES, SCOTT SMITH, SEYMOUR FRAILMAN, SPELMAN BRAUMAN, + +[CRED169] +STEPHANIE TELLING, STEVE KNEZEVICH, STEVE ROBERT, SUMIKO YASUDA, SUSAN LEWIS, + +[CRED170] +SYLVIA COLACIOS, TOMOKO MIYAZAKI, TRON, VERDEL HALE, YVES MONDESIR, ZENO LEINFELDER, + +[CRED171] +DAVID BEDDOES, CHRISTINE CHALMERS, BARRY CLARK, NEIL CORBETT, KIM GURNEY, NEIL MEIKLE, + +[CRED172] +CASSIE OLIVER, LORRAINE ROY, DAVID WATSON, KEVIN WONG, WILL MORTON + +[CRED175] +ADAM DAVIDSON + +[CRED176] +LANCE WILLIAMS + +[CRED177] +NEIL MCCAFFREY + +[CRED178] +LAURA PATERSON + +[CRED179] +REY CONCEPCION + +[CRED180] +CHARLES HEROLD + +[CRED181] +ANDREW GREENWALD + +[CRED182] +JAMES MIELKE + +[CRED183] +PETER SUCIU + +[CRED184] +ALEX ODULIO + +[CRED185] +DON NKRUMAH + +[CRED186] +KENDALL PITTMAN + +[CRED187] +SAL SUAZO + +[CRED188] +EREK MATEO + +[CRED189] +CHRIS DIFATE + +[CRED190] +LEILA MILTON + +[CRED191] +DARREN ZOLTOWSKI + +[CRED192] +VIRGINIA SMITH + +[CRED193] +KEVIN CASSIN + +[CRED194] +JASON SHIGEMORI + +[CRED195] +KELLY KINSELLA + +[CRED196] +MOLLIE STICKNEY + +[CRED197] +STANTON SARJEANT + +[CRED198] +LAURA WALSH + +[CRED199] +MARK GARONE + +[CRED200] +JOANNA SLY + +[CRED201] +ELIZABETH HOWELL + +[CRED202] +ANA HERCULES + +[CRED203] +SHIRLEY IRICK + +[CRED204] +KASHONA FIELDS + +[CRED205] +JOEL M. LILJE + +[CRED206] +JOHN DIBENEDETTO + +[CRED207] +NANCY GILES + +[CRED208] +RYAN CROY + +[CRED209] +JENNIFER KOLBE + +[CRED210] +LIAM BURKE + +[CRED211] +SIGRID PREISSL + +[CRED212] +ANITA FITZSIMONS + +[CRED213] +PHILIPPA RASELLI + +[CRED214] +WIL QUESNEL + +[CRED215] +FALKO BURKERT + +[CRED216] +SARA SEWELL + +[CRED217] +STATIONS DE RADIO ET MUSIQUE + +[CRED218] +CONSULTANT EN MUSIQUE + +[CRD218A] +HEINZ HENN + +[CRD218B] +STUART ROSS + +[CRED219] +COORDINATEUR DE LA BANDE-SON + +[CRED220] +TERRY DONOVAN + +[CRED221] +PRODUCTEUR CHEZ ROCKSTAR GAMES + +[CRED222] +DAN HOUSER ET LAZLOW + +[CRED223] +PRODUCTEUR CHEZ ROCKSTAR NORTH + +[CRED224] +CRAIG CONNER + +[CRED225] +ALLAN WALKER + +[CRED226] +LAZLOW + +[CRED227] +DJ BANTER ET IMAGING + +[CRED228] +ECRIT PAR DAN HOUSER ET LAZLOW + +[CRED229] +FLASH FM + +[CRD229A] +TONI-MARIA CHAMBERS + +[CRD229B] +VOIX ET PRODUCTION : JEFF BERLIN + +[CRED230] +REMERCIEMENTS SPECIAUX A + +[CRED231] +TOMMY MOTTOLA, + +[CRED232] +MICHELLE ANTHONY, + +[CRED233] +STEVE BARNETT, + +[CRED234] +CHUCK FLECKENSTEIN, + +[CRED235] +RITA LIBERATOR + +[CRED236] +MARTIN ET CLAIRE LOGAN + +[CRED237] +SANDRA HUTTON + +[CRED238] +CHRISTINE DAVIDSON + +[CRED239] +ALAN, RED ET BIGFOOT + +[CRED240] +LE T + +[CRED241] +COLIN DONALD + +[CRED242] +KERRY STALLWOOD + +[CRED243] +ALAN MCGREGOR + +[CRED244] +CHRIS MORTON + +[CRED245] +EMIL BUSSE + +[CRED246] +EMILY BAILLIE + +[CRED247] +KEVIN ARCHIBALD + +[CRED248] +MORAG KERR + +[CRED249] +CATH WALKER + +[CRED250] +ISO BAR + +[CRED251] +WATERLINE + +[CRED252] +NEWS CAFE + +[CRD251A] +THE POND + +[CRD252A] +PIVO + +[CRED253] +BUDGET VIDEO RENTALS + +[CRED254] +LORNA'S SCOOTER + +[CRED255] +GARETH MURFIN + +[CRED256] +GRAPHISMES ADDITIONNELS + +[CRED257] +TONY PORTER + +[CRED258] +CRAIG MOORE + +[CRED259] +ANIMATION SYNCHRO LABIALE DES CINEMATIQUES + +[CRED260] +COSGROVE HALL FILMS + +[CRED261] +PRODUCTEUR : OWEN BALLHATCHET + +[CRED262] +ANIMATEUR SENIOR : JON TURNER + +[CRED263] +ANIMATEURS : RICHARD DRUMM + +[CRED264] +DAVE BROWN + +[CRED265] +MAIR THOMAS + +[CRED266] +PRASHANT PATEL + +[CRED267] +CONSULTANT TECHNOLOGIE AUDIO + +[CRED268] +RIK EDE POUR GAMESOUND LTD. + +[CRED269] +ASSISTANCE INTEGRATION DTS + +[CRED270] +TED LAVERTY POUR DTS + +[CRED271] +CHRIS GREER POUR DTS + +[CRED272] +JASON PAGE POUR SCEE + +[CRED273] +RECHERCHE ET ANALYSE + +[CRED274] +VROCK + +[CRED275] +DJ : LAZLOW + +[CRED276] +VOIX : JOE KELLY + +[CRED277] +PRODUCTION : JONATHAN HANST + +[CRED278] +ONDE 103 + +[CRED279] +DJ : ADAM FIRST-JAMIE CANFIELD + +[CRED280] +VOIX : JEN SWEENLEY + +[CRED281] +PRODUCTION : JONATHAN HANST + +[CRED282] +FEVER 105 + +[CRED283] +DJ : OLIVER 'LADYKILLER' BISCUIT-JULIUS DYSON + +[CRED284] +VOIX D'HOMME : ED McMANN + +[CRED285] +VOIX DE FEMME : SHWNEE SMITH + +[CRED286] +PRODUCTION : LISTEN KISTEN + +[CRED287] +EMOTION 98.3 + +[CRED288] +DJ : FERNANDO- FRANK CHAVEZ + +[CRED289] +VOIX : JEN SWEENLEY + +[CRED290] +PRODUCTION : JONATHAN HANST + +[CRED291] +RADIO ESPANTOSO + +[CRED292] +DJ : PEPE-TONY CHILRODES + +[CRED293] +WILDSTYLE + +[CRED294] +DJ : MISTER MAGIC + +[CRED295] +VOIX : FRANK SILVESTRO + +[CRED296] +PRODUCTION : LAZLOW + +[CRED297] +KCHAT + +[CRED298] +ECRIT PAR DAN HOUSER ET LAZLOW + +[CRED299] +PRODUIT ET EDITE PAR LAZLOW + +[CRED300] +DJ AMY SHECKENHAUSEN : LEYNA WEBER + +[CRED301] +JEZ TORRENT : KEVIN MCKIDD + +[CRED302] +MANDY : COLLEEN CORBETT + +[CRED303] +MICHELLE CARAPADIS : MARY BIRDSONG + +[CRED304] +MR.ZOO : CARL DOWLING + +[CRED305] +GETHSEMANEE : LYNN LIPTON + +[CRED306] +CLAUDE MAGINOT : JOHN MAUCERI + +[CRED307] +BJ SMITH : LAWRENCE TAYLOR + +[CRED308] +THOR : FRANK FAVA + +[CRED309] +INTERLOCUTEURS + +[CRED310] +COUZIN ED, JOSH CLARK, JASON BUHRMESTER, JUAN ALLER, WAYNE OLIVER, SUSAN LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, KEITH BROADAS + +[CRED311] +LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, + +[CRED312] +DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, + +[CRED313] +KEITH BROADAS + +[CRED314] +VCPR + +[CRED315] +ECRIT PAR DAN HOUSER ET LAZLOW + +[CRED316] +PRODUIT PAR LAZLOW + +[CRED317] +MAURICE CHAVEZ : PHILLIP ANTHONY RODRIGUEZ + +[CRED318] +JONATHAN FREELOADER : PATRICK OLSEN + +[CRED319] +MICHELLE MONTANIUS : KELLY GUEST + +[CRED320] +ALEX SHRUB : CHRIS LUCAS + +[CRED321] +CALLUM CRAYSHAW : SEAN MODICA + +[CRED322] +JOHN F. HICKORY : LJ GANSEN + +[CRED323] +LE PASTEUR RICHARDS : DAVID GREEN + +[CRED324] +JAN BROWN : MAUREEN SILLIMAN + +[CRED325] +BARRY STARK : RENAUD SEBBANE + +[CRED326] +JENNY LOUISE CRAB : MARY BIRDSONG + +[CRED327] +KONSTANTINOS SMITH : KONSTANTINOS.COM + +[CRED328] +JEREMY ROBARD : PETER SILVESTRO + +[CRED329] +PUBLICITES RADIO + +[CRED330] +ECRITES PAR DAN HOUSER ET LAZLOW + +[CRED331] +PRODUITES PAR LAZLOW + +[CRED332] +JINGLES SUPPLEMENTAIRES PRODUITS PAR CRAIG CONNER + +[CRED333] +VOIX DES PUBLICITES : + +[CRED334] +ADAM DAVIDSON, ALEX ANTHONY, ALICE SALTZMAN, AMY SALZMAN, KATE DUKICH, + +[CRED335] +ARAN RONICLE, BARB JONES, BEN KRECH, BRIAN THOMAS, BROCK YODER, CHRIS + +[CRED336] +FERRANTE, CRAIG CONNER, DAVE RYAN, DAVID GREEN, DORIS WOO, DOUGLAS + +[CRED337] +HARRISON, ED MCMANN, FRANK CHAVEZ, FRANK FAVA, GENE HILGREEN, GREG + +[CRED338] +SCHWEIZER, HUNTER PLATIN, JAMES FERRANTE, JEFF BERLIN, JEFF ROSA, JOE KELLY, + +[CRED339] +JOHN MAUCERI, JOSH CLARK, JULIE WEMYSS, KEVIN STRALEY, KIM GURNEY, LANCE + +[CRED340] +WILLIAMS, LAURA PATERSON, LAZLOW, LISA ORTIZ, LORNA JORDAN, LUCIEN JONES, + +[CRED341] +MAUREEN SILLIMAN, MIKE FERRANTE JR., PETE GUSTIN, PETER SILVESTRO, RAFF + +[CRED342] +CROLLA, RANDY JOHNSON, RICHARD KRUGER, RON REEVE, SHELLEY MILLER, SKY, TJ ALLARD + +[CRD344A] +ENREGISTREMENTS AUX DIGITAL ARTS STUDIOS, + +[CRED344] +NYC, TRACK 9 STUDIOS, NYC, + +[CRED345] +WEDDINGTON MULTIMEDIA, LOS ANGELES, + +[CRD345A] +SYNC SOUND, NYC ET RADIO LAZLOW, LONG ISLAND. + +[CRED346] +MERCI A AXEL ERICSON ET WON LEE DE DIGITAL ARTS, PAUL VASQUEZ DETRACK 9 STUDIOS, JOHN BOWEN ET JOHN HASSLER DE SYNC SOUND. + +[CRED347] +MARK LLOYD + +[CRED348] +TIM BATES + +[CRED349] +KIT BROWN + +[CRED350] +ANDY MASON + +[CRED351] +PHIL DEANE + +[CRED352] +PHIL ALEXANDER + +[CRED353] +MATT HEWITT + +[CRED354] +DENBY GRACE + +[CRED355] +ANTOINE CABROL + +[CRED356] +JONATHAN STONES + +[CRED357] +MIKE BLACKBURN + +[CRED358] +TIM MCGAFF + +[CINCAM] +Caméra cinématique + +[RC4] +'RODEO DE RUMPO' + +[LEGAL] +~g~Elimine toute menace criminelle! + +[GA_2] +Nouveau moteur et nouvelle peinture. Les flics ne te reconnaîtront plus! + +[HELP15] +Si tu es à pied, appuie sur la ~h~~k~~PED_LOOKBEHIND~~w~ pour ~h~regarder derrière~w~. Utilise le ~h~joystick analogique droit~w~ pour ~h~regarder autour~w~ de toi. + +[FEC_LB4] +Regarder derrière (touche R3) + +[PERPIC] +Paquets cachés trouvés + +[CO_ONE] +Paquet caché ~1~ sur ~1~ + +[GA_21] +Impossible de garer plus de véhicules dans ce garage. + +[CHEAT1] +Codes activés + +[CHEAT2] +Code d'arme + +[CHEAT3] +Code de santé + +[CHEAT4] +Code d'armure + +[CHEAT5] +Code d'indice de recherche + +[CHEAT6] +Code d'argent + +[CHEAT7] +Code de météo + +[USJ_ALL] +TOUTES LES CASCADES ONT ETE ACCOMPLIES! + +[JAN] +Jan + +[FEB] +Fév + +[MAR] +Mar + +[APR] +Avr + +[MAY] +Mai + +[JUN] +Jun + +[JUL] +Jui + +[AUG] +Aoû + +[SEP] +Sept + +[OCT] +Oct + +[NOV] +Nov + +[DEC] +Déc + +[DEFDT] +--:---:---- --:--:-- + +[BONUS] +~g~BONUS $~1~ + +[HORN1] +Appuie sur la ~h~~k~~VEHICLE_HORN~ ~w~pour ~h~klaxonner. + +[HORN2] +Appuie sur la ~h~~k~~VEHICLE_HORN~ ~w~pour ~h~klaxonner. + +[HORN3] +Appuie sur la ~h~~k~~VEHICLE_HORN~ ~w~pour ~h~klaxonner. + +[FEC_EXV] +Monter et descendre d'un véhicule + +[TAXI_M] +'CHAUFFEUR DE TAXI' + +[COP_M] +'AUTODEFENSE' + +[FIRE_M] +'POMPIER' + +[AMBUL_M] +'AMBULANCE' + +[HJ_IS] +BONUS DE CASCADE DANGEREUSE : $~1~ + +[HJ_PIS] +BONUS DE CASCADE DANGEREUSE PARFAITE : $~1~ + +[HJ_DIS] +BONUS DE DOUBLE CASCADE DANGEREUSE : $~1~ + +[HJ_PDIS] +BONUS DE DOUBLE CASCADE DANGEREUSE PARFAITE : $~1~ + +[HJ_TIS] +BONUS DE TRIPLE CASCADE DANGEREUSE : $~1~ + +[HJ_PTIS] +BONUS DE TRIPLE CASCADE DANGEREUSE PARFAITE : $~1~ + +[HJ_QIS] +BONUS DE QUADRUPLE CASCADE DANGEREUSE : $~1~ + +[HJ_PQIS] +BONUS DE QUADRUPLE CASCADE DANGEREUSE PARFAITE : $~1~ + +[FESZ_LS] +Chargement effectué. + +[HELI_1A] +Teste tes capacités avec le Sparrow et vois en combien de temps tu peux réussir le parcours. + +[HELI_1B] +Parcours réussi! + +[HELIODD] +Petits boulots en hélico + +[INT2_M] +Ils sont proprios d'une ferme au Panama! + +[INT2_N] +Ok, écoutez un peu... + +[INT2_O] +Les gars, quand on sera là-bas, est-ce que je dois rester dans la bagnole, + +[INT2_P] +où bien est-ce que vous voulez absolument que je vienne avec vous? + +[INT2_Q] +Non, Reste dans la bagnole. + +[INT2_R] +Vous savez quoi, j'ai réfléchi à la question, + +[INT2_S] +Je vais surveiller la bagnole. + +[INT4_A] +On est baisés! Baisés! + +[INT4_B] +Putain, c'est vraiment le coup classique! + +[INT4_C] +Je sors la tête d'un caniveau pour une seconde de défonce, + +[INT4_D] +et le destin me chie de la merde plein la gueule! + +[INT4_E] +Allez vous faire mettre! + +[INT4_F] +Ferme-la et arrêter de gueuler! T'es encore vivant, pas vrai? + +[INT4_G] +Laisse-moi là. + +[INT4_H] +Débarrasse-toi de la caisse et va roupiller un bon coup. + +[INT4_I] +Je passerai à ton bureau demain et on essayera de trouver une solution à ce merdier. + +[INT4_J] +Ouais, bonne idée, je vais aller roupiller. + +[INT4_K] +Qu'est-ce que tu vas faire? + +[INT4_L] +Rentrer à mon hôtel. + +[INT4_M] +Et penser à autre chose que ces conneries. + +[INT4_N] +Ok. + +[LAW] +MISSIONS AVOCAT + +[LAW1_1] +~g~Va te procurer de nouvelles fringues à la boutique de Rafael. + +[LAW4_6] +Brûlez la direction! + +[LAW4_7] +Tuez les patrons! + +[LAW4_8] +Allez! Battez-vous! Allez! + +[LAW4_9] +Plus de vacances! Moins de travail! + +[LAW4_11] +Allez! Battez-vous! Allez! + +[LAW4_12] +Viva la revolucion! + +[GENERAL] +MISSIONS DU COLONEL + +[GEN3_4] +Tommy Vercetti. Allons-y... + +[GEN3_13] +C'est quoi ton problème, mec? Monte sur le toit de l'autre côté de la cour avant qu'ils rappliquent! + +[GEN3_17] +Meeeerde! T'essayes de m'buter ou quoi? + +[GEN3_21] +~g~Il a le pognon de Diaz! Attrape-le et récupère le fric! + +[GEN3_24] +~r~Diaz est mort! Tu devais le protéger! + +[GEN3_26] +~r~T'as flingué Diaz! + +[GEN3_27] +~r~T'as descendu les gardes du corps de Diaz! + +[GEN3_31] +~g~Va au rencart et veille sur Diaz. + +[GEN3_32] +~g~Va te mettre en position sur le toit du bâtiment en face de Lance. + +[COKE] +MISSIONS BARON DE LA COKE + +[COK1_3] +J'espère que tu vas te casser la gueule! + +[COK1_6] +J'en ai marre de ces merdeux. + +[COK2_7] +Tu vois ces marqueurs? Essaye de flinguer les lumières! + +[COK2_10] +En tout cas, tu tires mieux que tu causes. + +[COK2_11] +Merci. T'as un certain charme aussi. + +[COK2_12] +Je sais, Tommy. + +[COK2_18] +T'aimes bien Kenny Loggins? + +[COK2_19] +Tu parles, j'adore ce disque ouais! + +[COK2_26] +~r~T'as buté Lance! + +[COK3_1] +Tire pas, mec! + +[COK3_2] +Qu'est-ce que c'est que ça? + +[COK3_3] +Il embarque le bateau! Merde! + +[COK3_4] +A l'aide! Il y a un mec qui nous fauche le bateau! + +[COK4_W] +Bon! C'est le dernier. + +[COK4_X] +Je vais mettre le moteur en route. + +[COK4_Y] +Je crois qu'on a de la visite... + +[COK4_2] +Ouais. + +[COK4_6] +Tu sais où on va? + +[COK4_7] +On est paumés? + +[COK4_8] +On a de la concurrence! + +[COK4_9] +Bousille-les! + +[COK4_9A] +L'heure de la danse de Lance Vance a sonné! + +[COK4_10] +Réduits en miettes! Bons à nourrir la poiscaille! + +[COK4_11] +On a réussi! Les autres bateaux ne sont pas de taille. + +[COK4_17] +Ils sont prêts à tout! + +[COK4_18] +J'ai les pieds mouillés! On prend la flotte! + +[COK4_21] +Pont droit devant! + +[COK4_22] +Ecope, on va chavirer! + +[COK4_23] +Joli coup. + +[COK4_29] +~r~T'as buté Lance! + +[ASS1_6] +Continue, Tommy, ça va aller! + +[ASS1_7] +Prenez ça, enculés d'assassins! + +[ASS1_8] +Je suis coincé! + +[ASS1_9] +Je te couvre, Tommy! + +[ASS1_10] +Hé, c'est chouette toutes ces plantes... + +[ASS1_11] +Hé, Tommy, je peux avoir une chambre avec vue sur la baie? + +[ASS1_12] +Il y a vraiment de superbes plafonds, ici... + +[ASS1_3] +Lance! Couvre-moi! + +[ASS1_5] +Lance! + +[ASS1_15] +~g~Attaque la résidence et tue Diaz! + +[ASS1_17] +~g~Il existe de nombreux accès à la résidence. + +[TAXWAR] +MISSIONS GUERRE DES TAXIS + +[NOTAXI] +~g~T'as besoin d'un taxi Kaufman pour activer cette mission. + +[TAXW1_5] +~g~Faut que tu sois dans un taxi Kaufman! + +[TAX2_4] +Vas-y, Tommy. + +[TAX2_5] +Massacre-lui la tête. + +[TAX2_6] +Il a même pas son permis. + +[TAX2_7] +Putain de limousines! + +[TAXW3_1] +~g~Va prendre Mercedes. + +[RACE1] +~g~3..2..1.. PARTEZ! + +[RACE2] +~g~3 + +[RACE3] +~g~2 + +[RACE4] +~g~1 + +[RACE5] +~g~PARTEZ! + +[FIRST] +~b~1er + +[SECOND] +~b~2e + +[THIRD] +~b~3e + +[FOURTH] +~b~4e + +[RACETM] +~b~TEMPS DE COURSE : ~1~:~1~ + +[RACETM2] +~b~TEMPS DE COURSE : ~1~:0~1~ + +[RACEFA] +~r~Tu n'as pas gagné la course! + +[TEX1_5] +~r~Il s'est tiré! + +[SEG3_1] +TEMPS: + +[SEG3_2] +~g~Va à la camionnette qui contient le bombardier télécommandé et les bombes à retardement. + +[SEG3_3] +~g~Utilise le bombardier télécommandé pour transporter 4 bombes vers 4 zones cibles sur le chantier. + +[SERG3_5] +~g~Tu ne peux transporter qu'une bombe à la fois et tu ne peux récupérer une bombe déjà larguée avec succès. + +[SEG3_7] +~g~Une fois que tu as largué la première bombe sur une zone cible, le compte à rebours s'enclenche. Tu dois larguer toutes les bombes avant la fin de celui-ci. + +[SEG3_8] +~g~Les 4 bombes doivent être larguées sur les 4 zones cibles pour réussir la mission et détruire le bâtiment. + +[SEG3_9] +~g~Tu as touché la cible! Plus que 3! + +[SEG3_10] +~g~Tu as touché la cible! Plus que 2! + +[SEG3_11] +~g~Tu as touché la cible! Plus qu'une! + +[SEG3_12] +~r~Tu as raté la cible! Va récupérer une bombe! + +[SEG3_13] +~g~Largue la bombe sur une zone cible. + +[SEG3_14] +~r~Le temps est écoulé et tu n'as pu détruire le bâtiment. + +[SEG3_15] +~r~Ton bombardier télécommandé a été détruit! Comment tu vas transporter les bombes, maintenant? + +[AVERY] +MISSIONS AVERY + +[ASM] +MISSIONS ASSASSIN + +[ASM_1] +MISSION ASSASSIN 1 + +[ASM1_1] +~g~Ton aide dans l'éradication de ces indésirables fut une excellente affaire. J'ai un autre travail pour toi. Regarde sous le téléphone. + +[ASM1_2] +~g~Rends-toi à la cabine en dehors du centre commercial à Washington. + +[ASM1_3] +~g~Carl Pearson, livreur de pizzas. Il ne doit pas terminer sa tournée. + +[ASM1_4] +~g~Tue le livreur de pizzas avant qu'il le livre toutes ses pizzas. + +[ASM_2] +MISSION ASSASSIN 2 + +[ASM_3] +MISSION ASSASSIN 3 + +[ASM3_A] +Marcus Hammond, Franco Carter, Dick Tanner, Nick Kong et Stuntman Driver appartiennent tous au syndicat européen qui s'apprête à faire un hold-up. + +[ASM3_B] +Ils sont tous en position. Il faut qu'ils soient tous morts avant le commencement. Tu as 9 minutes. J'ai laissé quelques flingues à proximité qui devraient t'être utiles. + +[ASM3_1] +~g~Va récupérer l'arme que M. Black a laissé pour toi. + +[ASM3_2] +~g~Ne t'approche pas trop près de la cible ou tu risques d'être repéré. + +[ASM3_3] +~g~Pour un travail propre et rapide, installe-toi près de leurs positions dans des emplacements d'où tu pourras facilement les liquider, sans être repéré. + +[ASM3_4] +~g~Il t'a vu! Vaudrait mieux le buter en vitesse, maintenant! + +[ASM3_5] +~g~Marcus Hammond est en position près des panneaux publicitaires dans Washington. + +[ASM3_6] +~g~Franco Carter est en position à côté de DBP Security non loin d'Ocean Drive. + +[ASM3_7] +~g~Dick Tanner est en position à côté de la bijouterie dans Vice Point. + +[ASM3_8] +~g~Nick Kong est en position près de Washington Beach. + +[ASM3_9] +~g~Stuntman Driver est en position à Washington. + +[ASM3_10] +~r~Tu n'as pas réussi à tous les descendre. + +[ASM_4] +MISSION ASSASSIN 4 + +[ASM4_1] +~g~Va récupérer le fusil laissé à ton intention dans le feuillage à l'extérieur du terminal de l'aéroport. + +[ASM4_2] +~g~Ne rate pas la cible où tu alerteras ses gardes du corps et garde tes distances pour qu'il ne te repère pas. + +[ASM4_3] +~g~Observe la femme sur le balcon au-dessus des comptoirs d'enregistrements du terminal. NE LA TUE PAS. + +[ASM4_4] +~g~Tue l'homme à qui elle donnera la serviette, mais seulement quand il l'aura récupérée. Prends ensuite la serviette et amène-la à Ammu-Nation dans le centre. + +[ASM4_5] +~g~Récupère la serviette! + +[ASM4_6] +~g~Amène la serviette à Ammu-Nation dans le centre! + +[ASM4_7] +~r~Imbécile! Tu as tué la femme! + +[ASM4_8] +~r~La cible t'a entendu tirer! Le deal est annulé! + +[ASM4_9] +~r~La cible a embarqué à bord de son avion! + +[ASM4_10] +~r~On dirait que tu n'es pas le seul à t'intéresser à cette serviette! Amène-la en vitesse à Ammu-Nation! + +[ASM4_11] +~r~La cible t'a repéré! Le deal est annulé! + +[ASM4_13] +~g~Il t'a repéré et essaye de s'enfuir! Rattrape-le et récupère la serviette! + +[ASM4_14] +~g~La barre de distance dans le coin supérieur droit de l'écran te signale ta proximité avec la cible. Ne la laisse pas devenir pleine ou il te repèrera. + +[ASM_5] +MISSION ASSASSIN 5 + +[KICK] +DEMARRAGE AU PIED + +[KICK1_3] +~g~Nombre de fautes de pied : ~1~ + +[KICK1_4] +~g~Pénalité de temps : ~1~ secondes + +[BANK] +MISSIONS BRAQUAGE + +[BANK1] +MISSION BRAQUAGE 1 + +[BANK2] +MISSION BRAQUAGE 2 + +[BJM2_3] +TAUX DE REUSSITE : ~1~% + +[BJM2_15] +SCORE : + +[BJM2_18] +SCORE A BATTRE : + +[BJM2_19] +~g~Touche autant de cibles que possible dans le temps imparti! + +[BJM2_21] +~g~Touche autant de cibles que possible jusqu'à épuisement de tes munitions. + +[BANK3] +MISSION BRAQUAGE 3 + +[BJM3_1] +~g~Trouve une bagnole qui a des chevaux et rends-toi sur la grille de départ. + +[BNK4_2A] +Les gars au garage ont fait du super boulot sur ce bébé. + +[BNK4_3G] +Oh, merde, maintenant, on a les flics au cul! + +[BNK4_3H] +Et on n'est même pas encore sur place... + +[BNK4_3K] +Va d'abord falloir semer les flics... + +[BNK4_3L] +Merde, Tommy, t'essayes de tous nous tuer? + +[BNK4_3N] +Tout ce que j'aime part en fumée! + +[BNK422A] +Cam, combien de temps? + +[BK4_23A] +Donne-moi 3 minutes! + +[BNK4_26] +Bordel de merde! Les voilà! + +[BNK4_32] +Sers-toi des explosifs pour ouvrir les coffres! + +[BNK4_43] +Je nous couvre, FONCE! + +[BNK4_51] +Je te trouve très bien comme ça. + +[KENT] +MISSIONS KENT PAUL + +[KENT1] +MISSION KENT PAUL 1 + +[COUNT] +MISSIONS CONTREFACON + +[COUNT1] +MISSION CONTREFACON 1 + +[COUNT2] +MISSION CONTREFACON 2 + +[BIKE] +MISSIONS BIKERS + +[BIKE1] +MISSION BIKERS 1 + +[BIKE2] +MISSION BIKERS 2 + +[BIKE3] +MISSION BIKERS 3 + +[GOAWAY1] +Reviens quand tu auras fini les missions du gang haïtien. + +[HAIT] +MISSIONS GANG HAITIEN + +[HAIT1] +MISSION GANG HAITIEN 1 + +[HAIT2] +MISSION GANG HAITIEN 2 + +[HAIT3] +MISSION GANG HAITIEN 3 + +[HAM3_6] +~g~Utilise le fusil à lunette que je t'ai donné pour les tuer! + +[ROCK] +MISSIONS GROUPE DE ROCK + +[ROK1_4] +~g~Ok, je crois que c'est ce que vous vouliez... + +[ROK1_1E] +~g~Ca te coûtera plus que ce que tu as! + +[ROK1_1F] +~g~Reviens quand tu auras le pognon! + +[RBM2_6] +~g~Waou! C'est un mec! Arrête-le! + +[ROCK3] +MISSION GROUPE DE ROCK 3 + +[ROK3_6D] +~r~en même temps que vos sales grosses têtes chevelues! + +[ROK3_40] +Près de la glacière? + +[RBM3_5] +~g~Emmène les Love Fist au concert. + +[CUBANM] +MISSIONS GANG CUBAIN + +[CUBAN1] +MISSION GANG CUBAIN 1 + +[CUBAN2] +MISSION GANG CUBAIN 2 + +[CUB2_10] +~r~T'es supposé tuer des Haïtiens, pas des Cubains! + +[CUBAN3] +MISSION GANG CUBAIN 3 + +[CUBAN4] +MISSION GANG CUBAIN 40 + +[CUB4_25] +Ok, allons-y! + +[PROT] +MISSIONS DE PROTECTION + +[PRO1_H] +Arrêtez de vous exciter, tous les deux. Je commence à peine à voir comment les choses marchent ici. + +[PRO1_02] +~g~Sors du centre commercial. + +[PRO3_06] +~g~Sème les flics. + +[PORN] +MISSIONS PORNO + +[PORN1] +MISSION PORNO 1 + +[POR1_03] +~r~Candy est morte! + +[PORN2] +MISSION PORNO 2 + +[PORN3] +MISSION PORNO 3 + +[POR3_18] +T'as été repéré! + +[PORN4] +MISSION PORNO 4 + +[POR4_04] +~g~Les bureaux se trouvent de l'autre côté de cette porte. + +[PHIL] +MISSIONS PHIL + +[PHIL1] +MISSION PHIL 1 + +[PHI1_06] +Qu'est-ce que tu fous à conduire comme ça? + +[PHI1_07] +Hé! + +[PHIL2] +MISSION PHIL 2 + +[PIZ1_A] +MISSION LIVREUR DE PIZZA + +[PIZ1_03] +~g~Retourne à la pizzeria pour d'autres commandes. + +[PIZ1_04] +~g~Voilà tes nouvelles commandes. + +[PIZ1_10] +Appuie sur la ~h~ touche R3 ~w~ pour annuler les missions pizzas. + +[CNTBUY1] +Imprimerie achetée : $~1~ + +[CARBUY] +Concession achetée : $~1~ + +[PORNBUY] +Studio de cinéma acheté: $~1~ + +[ICEBUY] +Usine de crème glacée achetée: $~1~ + +[TAXIBUY] +Compagnie de taxis achetée: $~1~ + +[BANKBUY] +Club Malibu acheté : $~1~ + +[BOATBUY] +Chantier naval acheté: $~1~ + +[PRNT_NO] +Tu ne peux pas acheter l'imprimerie pour l'instant, reviens plus tard. + +[CAR_NO] +Tu ne peux pas acheter la concession automobile pour l'instant, reviens plus tard. + +[PORN_NO] +Tu ne peux pas acheter le studio de cinéma pour l'instant, reviens plus tard. + +[ICE_NO] +Tu ne peux pas acheter l'usine de crème glacée pour l'instant, reviens plus tard. + +[TAXI_NO] +Tu ne peux pas acheter la compagnie de taxis pour l'instant, reviens plus tard. + +[BANK_NO] +Tu ne peux pas acheter le club Malibu pour l'instant, reviens plus tard. + +[BOAT_NO] +Tu ne peux pas acheter le chantier naval pour l'instant, reviens plus tard. + +[PRNT_R3] +Appuie sur la touche R3 pour acheter l'imprimerie au prix de $~1~ + +[CAR_R3] +Appuie sur la touche R3 pour acheter la concession automobile au prix de $~1~ + +[PORN_R3] +Appuie sur la touche R3 pour acheter le studio de cinéma au prix de $~1~ + +[ICE_R3] +Appuie sur la touche R3 pour acheter l'usine de crème glacée au prix de $~1~ + +[TAXI_R3] +Appuie sur la touche R3 pour acheter la compagnie de taxis au prix de $~1~ + +[BANK_R3] +Appuie sur la touche R3 pour acheter le club Malibu au prix de $~1~ + +[BOAT_R3] +Appuie sur la touche R3 pour acheter le chantier naval au prix de $~1~ + +[COL2_6] +Arrête espèce de porc impérialiste d'Américain! + +[COL2_6B] +Ceci est la propriété du gouvernement français! + +[COL2_6C] +Et c'est terminé. + +[COL3_A] +Thomas, merci d'être venu. + +[COL3_B] +Désolé d'aller droit au but... + +[COL3_C] +Diaz m'a demandé de superviser une petite transaction financière. + +[COL3_D] +Espérons que ça se passera mieux que la dernière fois. + +[COL3_E] +C'est la raison pour laquelle j'ai pensé à vous, mon ami. + +[COL3_F] +J'ai laissé un revolver au parking à niveaux. + +[COL3_G] +Récupèrez-le et allez surveiller les mecs de Diaz au dépôt. + +[COL4_2] +Je sais pas, chef! + +[COL4_5] +Chef, oui, chef! + +[COL4_10] +Allons manger quelques beignets. + +[COL4_16] +Chef, on déplace le véhicule, chef! + +[COL4_25] +Autodestruction du véhicule initialisée! + +[COL5_6] +Mercedes, cette fille aura ma peau. + +[COL5_8] +Maudit cafard! + +[COL5_5] +Crevez, porcs de Français! + +[CNT2_1] +Tuez-le! + +[CNT2_2] +Récupérez les plaques! + +[CNT2_3] +Protégez le coursier! + +[FINKILL] +Ok, les mecs, butez-le! + +[FIN_6] +Sonny est en haut avec le coffre et MON argent... + +[FIN_10] +Sonny? SONNY! Je viens te chercher! + +[RACES_4] +3 + +[RACES_5] +2 + +[RACES_6] +1 + +[RACES_7] +PARTEZ! + +[RACES_9] +Temps : ~1~:~1~ + +[RACES] +TEMPS : + +[RACES17] +Nv temps record : ~1~:~1~ + +[RACES20] +Nv temps record : ~1~:0~1~ + +[RACES21] +Temps : ~1~:0~1~ + +[RCH1_1] +~g~Utilise l'hélicoptère radiocommandé, le RC Raider, pour PASSER les points de passage. + +[RCH1_2] +~g~Les POINTS DE PASSAGE sont disséminés dans l'aéroport. + +[RCH1_3] +~g~Tu as ~c~8 minutes~g~ pour passer les ~c~20! + +[RCH1_5] +Temps + +[RCRC1_1] +~g~Fais une COURSE DE POINTS DE PASSAGE contre deux autres RC Bandits sur 2 TOURS + +[RCRC1_2] +~g~Va sur la grille de départ! + +[RCRC1_3] +~g~Dernier tour! + +[RCRC1_4] +~g~3 + +[RCRC1_5] +~g~2 + +[RCRC1_6] +~g~1 + +[RCRC1_7] +~g~PARTEZ! + +[RCRC1_8] +~g~Temps de course : ~1~ secondes + +[RCPL1_1] +~g~Fais une COURSE DE POINTS DE PASSAGE contre 3 autres RC Baron + +[RCPL1_2] +~g~Tu dois passer par le ~o~CENTRE CORONA ~g~pour valider un point de passage. + +[RCPL1_3] +~g~Va sur la grille de départ maintenant! + +[FEA_2SP] +2 haut-parleurs + +[FEA_4SP] +Plus de 2 haut-parleurs + +[FEA_EAR] +Casque + +[FEA_NAH] +PAS DE MATERIEL AUDIO + +[FET_APP] +APPLIQUER + +[FES_SKN] +NOM DU SKIN + +[FES_DAT] +DATE + +[FES_SET] +Utiliser Skin + +[FET_DEF] +Par défaut + +[FESZ_QZ] +Veux-tu vraiment sauvegarder cette partie? + +[FES_SCG] +Sauvegarder la partie en cours? + +[FES_LCG] +Charger la partie et continuer à jouer? + +[FEC_FIR] +Tirer + +[FEC_NWE] +Arme suivante + +[FEC_PWE] +Arme précédente + +[FEC_FOR] +Avant + +[FEC_BAC] +Arrière + +[FEC_LEF] +Gauche + +[FEC_RIG] +Droite + +[FEC_ZIN] +Zoom avant + +[FEC_ZOT] +Zoom arrière + +[FEC_EEX] +Entrer+sortir + +[FEC_RAD] +Radio + +[FEC_SUB] +Sous-mission + +[FEC_CMR] +Changer caméra + +[FEC_JMP] +Sauter + +[FEC_SPN] +Courir + +[FEC_HND] +Frein à main + +[FEC_LOL] +Regarder à gauche + +[FEC_LOR] +Regarder à droite + +[FEC_NTR] +Cible suivante + +[FEC_PTT] +Cible précédente + +[FEC_LBA] +Regarder derrière + +[FEC_CEN] +Centrer caméra + +[FET_CCN] +Classique + +[FET_SCN] +Standard + +[FET_CFT] +A PIED + +[FET_CCR] +EN VOITURE + +[FET_CAC] +ACTION + +[FEC_IBT] +- + +[FEC_MXO] +MXB1 + +[FEC_MXT] +MXB2 + +[FEC_UNB] +NON LIE + +[FEC_TFL] +Regard gauche+tourelle G + +[FEC_TFR] +Regarde droite+tourelle D + +[FEC_MWF] +VOLANT MS HAUT + +[FEC_MWB] +VOLANT MX BAS + +[FEC_ORR] +ou + +[FEC_NUS] +NON UTILISE + +[FEC_LUD] +Regarder en haut + +[FEC_LDU] +Regarder en bas + +[FEC_CMP] +COMBO : REGARDER G+D + +[LAW_1A] +law_1a + +[LAW_1B] +law_1b + +[LAW_2A] +law_2a + +[LAW_2B] +law_2b + +[FEH_STA] +STATS + +[FEH_LOA] +CHARGER + +[FEH_CON] +COMMANDES + +[FEH_AUD] +AUDIO + +[FEH_DIS] +AFFICHAGE + +[FEH_LAN] +LANGUE + +[FEH_SGA] +LANCER NOUVELLE PARTIE + +[FEO_CON] +Configuration manette + +[FEO_AUD] +Configuration audio + +[FEO_DIS] +Configuration affichage + +[FEO_LAN] +Choix langue + +[FEO_PLA] +Configuration joueur + +[FEA_OUT] +Sortie + +[FEA_ST] +Stéréo + +[FEA_DTS] +DTS + +[FEA_RSS] +Station de radio + +[FEA_NON] +RADIO ETEINTE + +[FEA_FM0] +WILDSTYLE + +[FEA_FM1] +FLASH FM + +[FEA_FM2] +KCHAT + +[FEA_FM3] +FEVER 105 + +[FEA_FM4] +VROCK + +[FEA_FM5] +VCPR + +[FEA_FM7] +EMOTION 98.3 + +[FEA_FM8] +ONDE 103 + +[FED_BRI] +Luminosité + +[FED_TRA] +Rémanences : + +[FED_SUB] +Sous-titres + +[FED_WIS] +Ecran large + +[FED_POS] +Position écran + +[FEP_RES] +Reprendre + +[FEP_STG] +Commencer partie + +[FEP_STA] +Stats + +[FEP_BRI] +Briefings + +[FEP_OPT] +Options + +[FEP_QUI] +Quitter partie + +[FES_LOA] +Charger partie + +[FES_DEL] +Effacer partie + +[FEC_CSU] +Configuration manette + +[FEC_RED] +Réassigner commandes + +[FEC_MOU] +Paramètres souris + +[DISTGOL] +Dist. parcourue en voiturette (miles) + +[DISTGOM] +Distance parcourue en voiturette (m) + +[ST_FAVR] +Station de radio préférée + +[ST_WSTR] +Station de radio la moins écoutée + +[ST_FAVV] +Véhicule préféré + +[ST_STAR] +Total d'étoiles de recherche obtenues + +[ST_HEAD] +Nombre de tirs à la tête + +[ST_GANG] +Gang le moins apprécié + +[ST_STGN] +Total d'étoiles de recherche évitées + +[TYREPOP] +Pneus crevés avec des balles + +[TYRESLA] +Pneus lacérés avec une lame + +[ST_BRK] +Nombre de victimes dans le Bloodring + +[ST_LTBR] +Plus longue durée dans le Bloodring (sec) + +[ST_GNG1] +Cubains + +[ST_GNG2] +Haïtiens + +[ST_GNG3] +Faux voyous + +[ST_GNG4] +Gang de Diaz + +[ST_GNG5] +Agents de sécurité + +[ST_GNG6] +Gang de motards + +[ST_GNG7] +Gang de Vercetti + +[ST_GNG8] +Golfeurs + +[FEA_FM6] +ESPANTOSO + +[ST_ASSI] +Contrats exécutés + +[DISTBIK] +Dist. parcourue à moto (miles) + +[DISTBIM] +Dist. parcourue à moto (m) + +[HOTEL] +Hôtel Ocean View + +[ICC1_1] +~g~Utilise ta camionnette de crème glacée pour distribuer de la drogue à Vice City. + +[ICC1_2] +~g~Gare ta camionnette et appuie sur ~h~~k~~VEHICLE_HORN~~w~ pour jouer le jingle et signaler à tes clients que tu es prêt. + +[ICC1_3] +~g~Tu reçois de l'argent pour chaque transaction faite, mais plus tu fais d'affaires, plus tu attires l'attention de la police. + +[KICK1_9] +POINTS DE PASSAGE RESTANTS : + +[FIN_B6] +Tu n'as pas assez d'argent pour commencer cette mission. + +[TEX3_3] +~g~Pour ramasser une bombe, manoeuvre l'hélico au-dessus d'elle. La bombe est alors automatiquement fixée sous la carlingue. + +[TEX3_9] +~g~Ramasse une bombe en manoeuvrant l'hélico à côté d'elle. + +[HELP22] +Va vers la position du bip de la maison verte sur le radar. + +[FES_FMS] +Formatage terminé. Sélectionne OK pour continuer. + +[FES_SSC] +Sauvegarde terminée. Sélectionne OK pour continuer. + +[FES_DSC] +Suppression terminée. Sélectionne OK pour continuer. + +[FESZ_QC] +Ecraser ce fichier de sauvegarde corrompu? + +[FES_CHE] +Attention! Un ou plusieurs codes ont été activés, ce qui peut affecter votre fichier de sauvegarde. Sauvegarde déconseillée. + +[FET_SG] +SAUVEGARDER + +[FEH_BRI] +BRIEFING + +[FEH_MAP] +CARTE + +[FEM_OK] +OK + +[FEC_CRO] +S'accroupir + +[FEC_CR3] +S'accroupir (touche L3) + +[FEC_SMT] +Sous-mission + +[FEC_SM3] +Sous-mission (touche R3) + +[FEC_RSC] +Stations de radio + +[ST_PR01] +Pilote + +[ST_PR02] +Soldat armée de l'air + +[ST_PR03] +Officier pilote + +[ST_PR04] +Caporal + +[ST_PR05] +Lieutenant + +[ST_PR06] +Sergent + +[ST_PR07] +Capitaine + +[ST_PR08] +Biggs + +[ST_PR09] +Wedge + +[ST_PR10] +Baron rouge + +[ST_PR11] +Goose + +[ST_PR12] +Viper + +[ST_PR13] +Jester + +[ST_PR14] +Chappy + +[ST_PR15] +Iceman + +[ST_PR16] +Maverick + +[ST_PR17] +Noops + +[ST_PR18] +Maréchal de l'air + +[ST_PR19] +As + +[FET_LG] +Charger partie + +[CAR_EXP] +Véhicules routiers détruits + +[BOA_EXP] +Bateaux détruits + +[HEL_DST] +Avions et hélicoptères détruits + +[STFT_01] +Meilleur temps sur 'Alliage d'Acier' + +[STFT_02] +Meilleur temps sur 'Le chauffeur' + +[STFT_03] +Meilleur temps sur Dirt Ring + +[STFT_04] +Meilleur temps course avion télécommandé + +[STFT_05] +Meilleur temps course voiture télécommandée + +[STFT_06] +Meilleur temps hélico télécommandé + +[STFT_07] +Meilleur temps sur 'Terminal Velocity' + +[STFT_08] +Meilleur temps sur 'Ocean Drive' + +[STFT_09] +Meilleur temps sur 'Border Run' + +[STFT_10] +Meilleur temps sur 'Capital Cruise' + +[STFT_11] +Meilleur temps sur 'Virée!' + +[STFT_12] +Meilleur temps sur 'Endurance V.C.' + +[STHC_01] +Meilleur score sur Le Flingueur. + +[STHC_02] +Meilleur pourcentage de tir au but sur le Flingueur. + +[STHC_03] +Nombre de deals de drogue + +[HELP24] +Tu peux maintenant bosser pour le colonel. + +[HELP25] +Tu peux maintenant bosser pour Avery Carrington + +[HELP29] +Tu peux te rendre au magasin de fringues quand tu n'es pas en mission. + +[HELP30] +Quand tu achètes de nouvelles fringues, ton indice de recherche retombe à zéro. + +[BJM2_22] +~r~Tu as quitté le stand de tir! + +[BJM2_23] +~g~Si tu quittes le stand de tir pendant la compétition, la mission est un échec. + +[ASM4_24] +Distance : + +[RBM1_6] +~g~Ramène Mercedes et le Love Juice au groupe, au studio d'enregistrement. + +[RBM1_3] +PLUS NECESSAIRE + +[HAM1_5] +PLUS NECESSAIRE + +[RBM1_11] +PLUS NECESSAIRE + +[HELP31] +Pour réaliser un arrosage latéral, regarde d'abord à droite ou à gauche en utilisant ~k~~VEHICLE_LOOKLEFT~ ou ~k~~VEHICLE_LOOKRIGHT~. + +[HELP34] +Tu dois être armé d'une mitraillette pour réaliser un arrosage latéral. + +[STRIP_1] +~r~T'as pas assez de pognon, pauvre fauché! + +[EXIT_1] +Appuie sur ~k~~PED_SPRINT~ pour sortir. + +[ASM1_A] +M. Teal, votre aide dans l'éradication de ces indésirables fut une excellente affaire. J'ai un autre travail plus en finesse pour vous. + +[ASM1_B] +Regardez sous le téléphone. + +[ASM1_C] +J'ai un autre travail plus en finesse pour vous. + +[SCARF] +Appartement 3c + +[LAW4_10] +Les patrons sont tous des salauds! + +[GEN1_04] +~g~Passe la porte pour accéder au toit de Gonzalez. + +[GEN1_17] +~g~Gonzalez se fait la malle! Suis-le et finis-en avec lui! + +[RCH1_9] +~b~TEMPS TOTAL : ~1~:~1~ + +[RCH1_10] +~b~TEMPS TOTAL : ~1~:0~1~ + +[WHEEL01] +DOUBLE BONUS DEUX ROUES : $ ~1~ Distance : ~1~.~1~m Temps : ~1~ secondes + +[WHEEL02] +DOUBLE BONUS DEUX ROUES : $ ~1~ Distance : ~1~ feet Temps : ~1~ secondes + +[WHEEL03] +BONUS DEUX ROUES : $ ~1~ Temps : ~1~ secondes + +[WHEEL04] +BONUS DEUX ROUES : $ ~1~ Distance : ~1~.~1~m + +[WHEEL05] +BONUS DEUX ROUES : $ ~1~ Distance : ~1~ pieds + +[WHEEL06] +BONUS ROUE ARRIERE : $ ~1~ Distance : ~1~.~1~m Temps : ~1~ secondes + +[WHEEL07] +BONUS ROUE ARRIERE : $ ~1~ Distance : ~1~ feet Temps : ~1~ secondes + +[WHEEL08] +BONUS ROUE ARRIERE : $ ~1~ Temps : ~1~ secondes + +[WHEEL09] +BONUS ROUE ARRIERE : $ ~1~ Distance : ~1~.~1~m + +[WHEEL10] +BONUS ROUE ARRIERE : $ ~1~ Distance : ~1~ pieds + +[WHEEL11] +BONUS ROUE AVANT : $ ~1~ Distance : ~1~.~1~m Temps : ~1~ secondes + +[WHEEL12] +BONUS ROUE AVANT : $ ~1~ Distance : ~1~ pieds Temps : ~1~ secondes + +[WHEEL13] +BONUS ROUE AVANT: $ ~1~ Temps : ~1~ secondes + +[WHEEL14] +BONUS ROUE AVANT : $ ~1~ Distance : ~1~.~1~m + +[WHEEL15] +BONUS ROUE AVANT : $ ~1~ Distance : ~1~ pieds + +[ROK3_72] +Love Fist! + +[POR1_19] +Hé! + +[DESPERA] +Desperado + +[MOB_99A] +Rends-toi à la cabine près du centre commercial à Washington. + +[MOB_98A] +Rends-toi à la cabine dans Vice Point. + +[MOB_96A] +Rends-toi à la cabine du terminal de l'aéroport. + +[MOB_95A] +Rends-toi à la cabine dans Little Havana. + +[BNK1_1] +Je peux vous aider, monsieur? + +[BNK1_2] +Il y a un imposteur! + +[BNK1_3] +Il a pêté les plombs! + +[BNK1_4] +Bon sang, t'es qui, toi? + +[BNK1_5] +Où es ton badge? + +[BNK1_6] +Les voilà! Descendez-les! + +[MOB_24A] +Bonjour, c'est M. Vercetti? + +[MOB_24B] +Oui. + +[MOB_24C] +ici Cortez. Vous étiez à ma fête. + +[MOB_24D] +Oui, je me souviens. + +[MOB_24E] +M. Vercetti, c'est un regrettable incident, ce qui s'est produit avec votre affaire. + +[MOB_24F] +Je sais. + +[MOB_24G] +Je veux que vous sachiez que mes hommes et moi, faisons le maximum pour tirer ça au clair. + +[MOB_24H] +Si vous souhaitez me parler en privé, vous me trouverez sur le bateau. Au revoir, senor. + +[BNK2_2] +VISEZ 3-2-1 FEU! + +[BNK2_3] +ZONE DEGAGEE! + +[BNK2_6] +Ce mec est cinglé! + +[ANGEL] +Angel + +[CUBJET] +Jetmax cubain + +[SANDKIN] +Sandking + +[POLMAV] +Maverick Police + +[BOXVILL] +Boxville + +[BENSON] +Benson + +[HOTRINA] +Hotring Racer + +[HOTRINB] +Hotring Racer + +[BLOODRA] +Bloodring Banger + +[BLOODRB] +Bloodring Banger + +[MAFIACR] +Mafia Cruiser + +[COP_M2] +'VICE SQUAD' + +[COP_M3] +'BROWN THUNDER' + +[BJM2_20] +~g~Quand tu n'as plus de ~w~temps ~g~ou de ~w~munitions ~g~, la manche est terminée! + +[BNK3_2] +Pas question que je conduise pour toi! J'en parlerai pendant la réunion de groupe! + +[FEM_SL1] +Pas de sauvegarde 1 + +[FEM_SL2] +Pas de sauvegarde 2 + +[FEM_SL3] +Pas de sauvegarde 3 + +[FEM_SL4] +Pas de sauvegarde 4 + +[FEM_SL5] +Pas de sauvegarde 5 + +[FEM_SL6] +Pas de sauvegarde 6 + +[FEM_SL7] +Pas de sauvegarde 7 + +[FEM_SL8] +Pas de sauvegarde 8 + +[FEA_CHA] +Passage en mode STEREO. Patiente un instant... + +[FEA_CHD] +Attention! Tu es en train de passer du mode STEREO au mode DTS. Patiente un instant... + +[FEI_SEL] +Sélectionner + +[FEI_BAC] +Retour + +[FEI_RES] +Reprendre + +[FEI_NAV] +Explorer + +[FEI_BTX] +Touche / - + +[FEI_BTT] +Touche " - + +[FEI_STA] +Touche START - + +[FEI_BTD] +; = > < - + +[FEI_STO] +Arrêter + +[MOB_68A] +Tommy, mon fiston, j'ai une surprise pour toi! + +[MOB_68B] +Je suis au studio d'enregistrement avec quelques artistes réputés. + +[MOB_68C] +Pourquoi tu ne viendrais pas faire un tour? + +[MOB_68D] +C'est une bonne idée, non? Allez, à plus tard. C'est plutôt normal, non? Allez, à plus tard. + +[OUTFT1] +Streetwear + +[OUTFT2] +Soirée + +[OUTFT3] +Bleu de travail + +[OUTFT4] +Country club + +[OUTFT5] +Cubano + +[OUTFT6] +Flic + +[OUTFT7] +Braqueur + +[OUTFT8] +Décontracté + +[OUTFT9] +M. Vercetti + +[OUTFT10] +Survêtement + +[OUTFT13] +MC Tommy + +[HOTR_07] +Nouveau record : ~1~:0~1~ + +[HOTR_08] +Temps : ~1~:~1~ + +[GEN3_45] +Ils seront là d'une minute à l'autre. On ferait mieux de se trouver une bonne planque... + +[CAR_AS1] +CONCESSION AUTOMOBILE ACQUISE + +[CAR_AS2] +~g~Sunshine Autos génère dorénavant un revenu de $~1~ maximum. Pense à récupérer le fric régulièrement. + +[BUYSAVE] +~g~Puisque tu n'es pas en mission, tu peux sauvegarder ta partie gratuitement. + +[BUYGARG] +~g~Tu peux aussi garer des véhicules dans ce garage. + +[STRPBUY] +Club Pole Position acquis : $~1~ + +[STRP_R3] +Pour acheter le club Pole Position pour $~1~, appuie sur la touche R3. + +[NBMN_R3] +Pour acheter Elswanko Casa pour $~1~, appuie sur la touche R3. + +[GA_4] +Les bombes pour voiture coûtent $1000 pièce. + +[GA_5] +Ta caisse est déjà équipée d'une bombe. + +[GA_6] { reVC update } +Gare-toi, amorce-la en appuyant sur la ~h~~k~~VEHICLE_FIREWEAPON~~w~ et BARRE-TOI vite! + +[GA_7] { reVC update } +Amorce la bombe en appuyant sur la ~h~~k~~VEHICLE_FIREWEAPON~~w~. Elle explosera au démarrage. + +[GA_6B] { reVC update } +Gare-toi, amorce-la en appuyant sur la ~h~~k~~VEHICLE_FIREWEAPON~~w~ et BARRE-TOI vite! + +[GA_7B] { reVC update } +Amorce la bombe en appuyant sur la ~h~~k~~VEHICLE_FIREWEAPON~~w~. Elle explosera au démarrage. + +[MOB_70A] +Tommy, c'est moi, le colonel Cortez. Ecoutez senor, je parie qu'avec vous, tout est possible. + +[MOB_70B] +Je serai sur le bateau. + +[PICK1] +Gilet pare-balles livré dans l'hôtel Ocean View! + +[PICK2] +.357 livré à la planque! + +[PICK3] +Tronçonneuse livrée à la planque! + +[PICK4] +Lance-flammes livré à la planque! + +[PICK5] +.308 Lunette livré à la planque! + +[PICK6] +Mitrailleuse livrée à la planque! + +[PICK7] +Lance-roquettes livré à la planque! + +[PICK8] +Sea Sparrow désormais disponible dans la résidence! + +[PICK9] +Char désormais disponible dans la caserne de l'armée! + +[PICK10] +Hunter désormais disponible dans la caserne de l'armée! + +[HELP41] +tu peux aussi les défoncer avec un véhicule + +[ICC1_6] +~g~Utilise le Mr. Whopee pour distribuer des produits Cherry Poppers dans Vice City. + +[ICC1_12] +PROPRIETE ACQUISE! + +[CLOTH1] +Tenue de soirée livrée chez Rafael sur Ocean Beach. + +[CLOTH2] +Tenue streetwear livrée aux planques. + +[CLOTH3] +Bleu de travail livré chez Tooled Up dans le centre commercial de North Point. + +[CLOTH4] +Tenue pour le Country Club livrée au Leaf Links Golf Club. + +[CLOTH5] +Tenue Cubano livrée chez Little Havana Streetwear dans Little Havana. + +[CLOTH6] +Uniforme de flic livré au poste de police de Washington Beach. + +[CLOTH7] +Tenue décontractée livrée chez Gash dans le centre commercial de North Point. + +[CLOTH8] +Tenue de M. Vercetti livrée chez Collar & Cuffs sur Ocean Beach. + +[CLOTH9] +Survêtement livré chez Jocksport dans le centre. + +[CLOTH10] +Tenue de braqueur livrée au Malibu de Vice Point. + +[RBM1_9] +~g~Va chez le dealer et rapporte du Love Juice pour les Love Fist! + +[MOB_62A] +Tommy, c'est Ricardo Diaz, je voulais te remercier de m'avoir sauvé. + +[MOB_62B] +J'ai demandé à ce con de Cortez. Il a dit que tu feras l'affaire, mon ami. Viens me voir à l'occasion. + +[MOB_62C] +C'est d'un mec comme toi dont j'ai besoin. J'ai plus que des têtes de bites, + +[MOB_62D] +des têtes de bite partout! Tu vas gagner plein de pognon. + +[GOAWAY2] +~g~Reviens quand tu auras terminé les missions Bikers. + +[COL2_9] +Imbécile de Ricain! Ils vous ont suivi jusqu'ici! + +[LOADCOL] +Chargement... + +[STFT_17] +Meilleur temps sur Terrain de jeu PCJ + +[STFT_18] +Meilleur temps sur 'Sélection par la boue' + +[STFT_19] +Meilleur temps sur Piste d'essai + +[NEW_REC] +Nouveau record! ~1~ minutes et ~1~ secondes. + +[BMX_HOW] +~g~Fais deux tours de circuit, ~y~en passant par ~g~les ~y~POINTS DE PASSAGE~g~! + +[BMXREW1] +~g~Chaque fois que tu inscris un nouveau record pour les deux tours, + +[BMXREW2] +~g~une meilleure ~y~RECOMPENSE ~g~t'est offerte! + +[BMXRAIN] +~g~On dirait qu'il pleut... + +[ITBEG] +AU DEBUT... + +[NBMNBUY] +El Swanko Casa acheté : $ ~1~ + +[LNKVBUY] +Appartement de Links View acheté : $ ~1~ + +[HYCOBUY] +Hyman Condo acheté : $ ~1~ + +[BUYGARS] +~g~Tu peux également stocker des véhicules dans ces garages. + +[OCHEBUY] +Appartement d'Ocean Heights acheté : $ ~1~ + +[WASHBUY] +1102 Washington Street acheté : $ ~1~ + +[VCPTBUY] +3321 Vice Point acheté : $ ~1~ + +[SKUMBUY] +Skumole Terrace achetée : $~1~ + +[HELP6_C] +Appuie sur la ~h~~k~~VEHICLE_HANDBRAKE~~w~ pour actionner le frein à main. + +[HELP2_A] +Appuie sur la ~h~~k~~PED_SPRINT~~w~ lorsque tu cours pour ~h~sprinter. + +[HELP4_A] +Appuie sur la ~h~~k~~VEHICLE_ACCELERATE~~w~ pour accélérer. + +[HELP5_A] +Appuie sur la ~h~~k~~VEHICLE_BRAKE~~w~ pour freiner ou faire marche arrière si le véhicule est à l'arrêt. + +[HELP8_A] +Appuie sur la ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ pour faire un zoom avant avec le fusil et sur la ~x~touche /~w~ pour faire un zoom arrière. + +[PBOAT_1] { reVC update } +Appuie sur la~h~ ~k~~VEHICLE_FIREWEAPON~~w~ pour tirer avec les canons du bateau. + +[SEG3_4] { reVC update } +~g~Tu peux ramasser des bombes en pilotant ton avion radiocommandé à côté. Appuie sur la ~h~~k~~VEHICLE_FIREWEAPON~ ~g~touche. + +[RCR1_3] { reVC update } +~g~Si tu veux quitter cette mission, appuie sur la ~h~~k~~VEHICLE_FIREWEAPON~~g~ pour faire exploser ta voiture radiocommandée. + +[HELP32] { reVC update } +Appuie ensuite sur la ~h~~k~~VEHICLE_FIREWEAPON~~w~ pour tirer. + +[HELP33] { reVC update } +Appuie ensuite sur la ~h~~k~~VEHICLE_FIREWEAPON~~w~ pour tirer. + +[TTUTOR] +Appuie sur la ~h~~k~~TOGGLE_SUBMISSIONS~~w~ pour activer ou désactiver les missions Taxi. + +[TTUTOR2] +Appuie sur la ~h~~k~~TOGGLE_SUBMISSIONS~~w~ pour activer ou désactiver les missions Taxi. + +[FTUTOR] +Appuie sur la ~h~~k~~TOGGLE_SUBMISSIONS~~w~ pour activer ou désactiver les missions Camion de pompiers. + +[FTUTOR2] +Appuie sur la ~h~~k~~TOGGLE_SUBMISSIONS~~w~ pour activer ou désactiver les missions Camion de pompiers. + +[CTUTOR] +Appuie sur la ~h~~k~~TOGGLE_SUBMISSIONS~~w~ pour activer ou désactiver les missions Auto-défense. + +[CTUTOR2] +Appuie sur la ~h~~k~~TOGGLE_SUBMISSIONS~~w~ pour activer ou désactiver les missions Auto-défense. + +[HELP8_B] +Appuie sur la~h~ ~k~~PED_SNIPER_ZOOM_IN~ ~w~pour faire un ~h~zoom avant~w~ avec le fusil et sur la ~h~~k~~PED_SNIPER_ZOOM_OUT~~w~ pour faire un ~h~zoom arrière~w~. + +[ATUTOR3] +Appuie sur la ~h~~k~~TOGGLE_SUBMISSIONS~~w~ pour activer ou désactiver les missions Ambulance. + +[GUN_H1] +~w~Appuie sur la~h~ ~k~~PED_SPRINT~~w~ pour acheter. ~w~Appuie sur la~h~ ~k~~VEHICLE_ENTER_EXIT~~w~ pour quitter. + +[PU_CF3] { reVC update } +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~~w~ pour remplacer l'arme actuelle dans cet emplacement. + +[PU_CF4] { reVC update } +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~~w~ pour remplacer l'arme actuelle dans cet emplacement. + +[HELP9_B] +Appuie sur la~h~ ~k~~PED_FIREWEAPON~ ~w~pour ~h~tirer~w~ avec le fusil à lunette. + +[HELP37] +Si tu ne veux pas monter dans un véhicule quand tu braques le conducteur, appuie sur la ~h~~k~~PED_SPRINT~. + +[HELP6_A] +Appuie sur la ~h~~k~~VEHICLE_HANDBRAKE~ ~w~ pour actionner le frein à main. + +[HELP6_D] +Appuie sur la ~h~~k~~VEHICLE_HANDBRAKE~ ~w~ pour actionner le frein à main. + +[HELP26] +Appuie sur la ~h~~k~~VEHICLE_ENTER_EXIT~ ~w~ pour monter ou sortir d'un véhicule. + +[HELP27] +Appuie sur ~h~~k~~VEHICLE_TURRETUP~~w~ ou ~h~~k~~VEHICLE_TURRETDOWN~~w~ pour déplacer ton poids sur une moto. + +[HELP28] +Appuie sur ~h~~k~~VEHICLE_TURRETUP~~w~ ou ~h~~k~~VEHICLE_TURRETDOWN~~w~ pour déplacer ton poids sur une moto. + +[HELP35] +Appuie sur ~h~~k~~GO_LEFT~~w~ ou ~h~~k~~GO_RIGHT~~w~ pour diriger le véhicule. + +[HELP36] +Appuie sur ~h~~k~~GO_LEFT~~w~ ou ~h~~k~~GO_RIGHT~~w~ pour diriger le véhicule. + +[HELP42] +Suis le ~q~point rose~w~ pour trouver l'hôtel. + +[HELP19] +Marche sur le ~q~marqueur rose~w~ pour continuer. + +[HELP1] +Arrête-toi au centre du ~q~marqueur rose. + +[HELP12] +Marche au centre du ~q~marqueur rose~w~ pour lancer une mission. + +[SEG3_6] +~g~Pour toucher ta cible, tu dois larguer la bombe sur la zone indiquée par le ~q~marqueur rose~w~. Tu peux larguer les bombes dans n'importe quel ordre. + +[S_PROMP] +Lorsque tu n'es pas en mission, tu peux sauvegarder la partie en ramassant la ~h~cassette. + +[HELP16] +Franchis la porte d'entrée de l'hôtel ~h~Ocean View~w~ pour y pénétrer. + +[HELP43] +~g~Rends-toi à l'hôtel ~h~Ocean View~g~ sur Ocean Drive. + +[HELI_F1] +~r~Mission Point de passage hélico annulée! + +[AMMUHLP] +Si tu as besoin d'armes, va chez ~h~Ammu-Nation~w~. Suis le ~h~point pistolet~w~ sur le radar. + +[HELI_1] +Point de passage hélico du Centre + +[HELI_2] +Point de passage hélico d'Ocean Beach + +[HELI_3] +Point de passage hélico de Vice Point + +[HELI_4] +Point de passage hélico de Little Haiti + +[FST_MFR] +Station de radio préférée + +[FST_LFR] +Station de radio la moins écoutée + +[FEI_HOL] +Maintenir + +[FEI_ZOO] +Zoom + +[FEI_BTR] +> < - + +[FEI_NA] +Aucun + +[MESA] +Grande table + +[STRP_NO] +Tu ne peux pas acheter la boîte de striptease maintenant. Reviens plus tard. + +[CHSE] +POURSUITE + +[NBMN_L] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter El Swanko Casa pour $~1~. + +[NBMN_T] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter El Swanko Casa pour $~1~. + +[NBMN_C] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter El Swanko Casa pour $~1~. + +[LNKV_L] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'appartement de Links View pour $~1~. + +[LNKV_T] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'appartement de Links View pour $~1~. + +[LNKV_C] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'appartement de Links View pour $~1~. + +[HYCO_L] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter Hyman Condo pour $~1~. + +[HYCO_T] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter Hyman Condo pour $~1~. + +[HYCO_C] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter Hyman Condo pour $~1~. + +[OCHE_L] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'appartement d'Ocean Heights pour $~1~. + +[OCHE_T] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'appartement d'Ocean Heights pour $~1~. + +[OCHE_C] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'appartement d'Ocean Heights pour $~1~. + +[WASH_L] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le 1102 Washington Street pour $~1~. + +[WASH_T] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le 1102 Washington Street pour $~1~. + +[WASH_C] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le 1102 Washington Street pour $~1~. + +[VCPT_L] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le 3321 Vice Point pour $~1~. + +[VCPT_T] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le 3321 Vice Point pour $~1~. + +[VCPT_C] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le 3321 Vice Point pour $~1~. + +[SKUM_L] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~~w~ pour acheter la Skumole Terrace pour $~1~. + +[SKUM_T] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~~w~ pour acheter la Skumole Terrace pour $~1~. + +[SKUM_C] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~~w~ pour acheter la Skumole Terrace pour $~1~. + +[PRNT_L] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'imprimerie pour $~1~. + +[PRNT_T] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'imprimerie pour $~1~. + +[PRNT_C] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'imprimerie pour $~1~. + +[CAR_L] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter la concession automobile pour $~1~. + +[CAR_T] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter la concession automobile pour $~1~. + +[CAR_C] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter la concession automobile pour $~1~. + +[PORN_L] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le studio de cinéma pour $~1~. + +[PORN_T] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le studio de cinéma pour $~1~. + +[PORN_C] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le studio de cinéma pour $~1~. + +[ICE_L] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'usine de crème glacée pour $~1~. + +[ICE_T] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'usine de crème glacée pour $~1~. + +[ICE_C] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'usine de crème glacée pour $~1~. + +[TAXI_L] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter la compagnie de taxis pour $~1~. + +[TAXI_T] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter la compagnie de taxis pour $~1~. + +[TAXI_C] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter la compagnie de taxis pour $~1~. + +[BANK_L] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le Malibu pour $~1~. + +[BANK_T] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le Malibu pour $~1~. + +[BANK_C] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le Malibu pour $~1~. + +[BOAT_L] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le chantier naval pour $~1~. + +[BOAT_T] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le chantier naval pour $~1~. + +[BOAT_C] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le chantier naval pour $~1~. + +[STRP_L] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le Pole Position Club pour $~1~. + +[STRP_T] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le Pole Position Club pour $~1~. + +[STRP_C] +Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le Pole Position Club pour $~1~. + +[STOCK] +~r~stock écoulé + +[HELP14] +Pour trouver le cabinet d'avocats, suis le ~h~point L~w~ sur le radar. + +[BOAT_AS] +~g~Le chantier naval génère dorénavant un revenu de $~1~ maximum. Pense à récupérer le fric régulièrement. + +[BOAT_A2] +CHANTIER NAVAL OK + +[BOAT_N] +Checkpoint Charlie + +[BOAT_P] +~g~Récupère les paquets avant la fin du temps imparti. + +[FEI_R1B] +Touches R1\R2 - + +[HELP9_A] +Appuie sur la touche ~h~~k~~PED_FIREWEAPON~ ~w~pour tirer avec le fusil à lunette. + +[HELP21] +Appuie sur la touche ~h~~k~~VEHICLE_ENTER_EXIT~ ~w~pour monter dans un véhicule ou en sortir. + +[CREAM] +Distribution + +[UMBERTO] +Café Robina + +[PU_CF1] +Appuie sur la touche ~h~~k~~PED_ANSWER_PHONE~ ~w~pour ramasser cette arme. Elle remplacera toute autre arme du même type que tu possèdes. + +[FED_RDM] +CARTE ET POINTS + +[FEC_ILU] +Vue inversée à la 1ere personne : + +[NITRO] +Tous les taxis disposent d'un saut turbo! Appuie simplement sur la touche klaxonner. + +[RATNG53] +Faux cul + +[RATNG54] +Faiseur d'emmerdes + +[RATNG55] +Malhonnête + +[RATNG56] +Tricheur + +[RATNG57] +Mythomane + +[STHC_04] +Meilleur score au beach ball de Keepie-Uppy + +[STHC_05] +Meilleur résultat au Hotring + +[STFT_13] +Meilleur tps point de passage hélico du centre + +[STFT_14] +Meilleur tps point de passage hélico d'Ocean Beach + +[STFT_15] +Meilleur tps point de passage hélico de Vice Point + +[STFT_16] +Meilleur tps point de passage hélico de Little Haiti + +[STFT_21] +Meilleur temps au Hotring + +[STFT_22] +Meilleur temps au tour au Hotring + +[STFT_20] +Meilleur temps au 'Cone Crazy' + +[HELP44] +Arrête-toi sur le ~q~marqueur rose. + +[HELP45] +Appuie sur la ~h~~k~~PED_DUCK~~w~ pour t'accroupir. Ceci augmente la précision des flingues que tu portes. + +[RCR1_5] +Course de Bandit RC + +[RCPL1_7] +Course de Baron RC + +[RCH1_11] +Course de Raider RC + +[FEA_CTD] +Attention! Un équipement matériel compatible DTS est requis pour cette fonction. Continuer? + +[FEM_STE] +STEREO + +[FEM_UDY] +DTS + +[GREET] +Bien le bonjour de... + +[LANCE_1] +Hé, mec, conduis un peu mieux! + +[LANCE_2] +Hé, fais gaffe à ce que tu fais! + +[LANCE_3] +Hé, tu vas où, là? + +[LANCE_4] +On fait quoi maintenant? + +[LAW4_15] +Plus de fric! + +[MERC_5] +Belle voiture, M. Vercetti. + +[MERC_26] +PLUS VITE, PLUS VITE, PLUS VITE! + +[MERC_27] +Attention, Tommy, je me suis fait refaire le nez le mois dernier. + +[MERC_28] +Tommy, conduis prudemment! + +[MERC_29] +Tommy, va moins vite! + +[MERC_30] +Tommy, tu veux bien tuer quelqu'un d'autre que moi? + +[MERC_31] +Tommy, chéri, ne me tue pas! + +[MERC_32] +Tommy, ça me très plaisir que t'aies volé cette caisse! + +[MERC_40] +J'ai vraiment passé un bon moment. + +[MERC_43] +Adios, mon chou. + +[MERC_44] +Continue la muscu, d'accord? + +[MERC_45] +Ciao, mon beau. + +[COL5_17] +Oh mon dieu, ils ont un hélicoptère! + +[COL5_18] +Abattez l'hélico! + +[COL5_19] +Tommy, débarrasse-nous de cet hélico! + +[COL5_20] +Il revient! Détruisez cet hélico! + +[COL5_21] +Visez la taille de cet hélico! + +[COL5_22] +Le revoilà! + +[FEA_DSM] +Attention! Cette sauvegarde est paramétrée pour un son DTS. Du matériel compatible DTS doit être connecté. Choisir entre une sortie audio STEREO ou DTS. + +[STFT_23] +Meilleur temps à Checkpoint Charlie + +[HELP50] +Appuie sur la touche ~h~~k~~PED_ANSWER_PHONE~~w~ pour placer la caméra derrière toi. + +[HELP51] +Appuie sur la touche ~h~~k~~PED_ANSWER_PHONE~~w~ pour placer la caméra derrière toi. + +[HELP52] +Appuie sur la touche ~h~~k~~PED_ANSWER_PHONE~~w~ pour placer la caméra derrière toi. + +[HELP53] +Appuie sur la touche ~h~~k~~PED_CYCLE_WEAPON_LEFT~~w~ ou sur la touche ~h~~k~~PED_CYCLE_WEAPON_RIGHT~~w~ pour faire défiler tes armes disponibles. + +[HELP46] +Il existe huit différents types d'armes. + +[HELP47] +Tu peux porter une arme de chaque type à la fois, un type de pistolet, un type de fusil à pompe, etc. + +[HELP54] +~w~Prix : $~1~ ~r~L'achat de cette arme remplacera celle que tu possèdes déjà. + +[HELP2A2] +Appuie sur la touche ~h~~k~~PED_SPRINT~~w~ quand tu cours, pour ~h~sprinter. + +[HLPSN_A] +Grâce au fusil à lunette, tu peux zoomer de loin sur une cible et la viser avec précision. + +[HLPSN_B] +Appuie sur la touche~h~ ~k~~PED_LOCK_TARGET~ ~w~et maintiens-la enfoncée pour ~h~viser~w~ avec le fusil à lunette. + +[HLPSN_C] +Appuie sur la touche~h~ ~k~~PED_LOCK_TARGET~ ~w~et maintiens-la enfoncée pour ~h~viser~w~ avec le fusil à lunette. + +[HLPSN_D] +Appuie sur la touche ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ pour ~h~faire un zoom avant ~w~avec le fusil à lunette et sur la touche~h~ ~k~~PED_SNIPER_ZOOM_OUT~ ~w~pour ~h~faire un zoom arrière~w~. + +[HLPSN_E] +Appuie sur la touche ~h~~k~~PED_FIREWEAPON~~w~ ~w~pour ~h~tirer~w~ avec le fusil à lunette. + +[HLPSN_F] +Appuie sur la touche ~h~~k~~PED_FIREWEAPON~~w~ ~w~pour ~h~tirer~w~ avec le fusil à lunette. + +[HLPSN_G] +Appuie sur la touche ~h~~k~~PED_FIREWEAPON~~w~ ~w~pour ~h~tirer~w~ avec le fusil à lunette. + +[PLANE_H] +Utilise la touche ~h~~k~~VEHICLE_ACCELERATE~~w~ pour accélérer. Gauche et droite pour tourner. + +[PLANE_4] { reVC update } +{Utilise la touche ~h~~k~~VEHICLE_ACCELERATE~~w~ pour accélérer. Gauche et droite pour tourner.} +Utilise le joystick analogique droit pour accélérer, appuie vers le bas sur le joystick analogique gauche pour monter et vers le haut pour descendre. Gauche et droite pour tourner. + +[HELP55] +Appuie sur la touche ~h~~k~~PED_FIREWEAPON~~w~ pour attaquer le chef. + +[STPR_8] +Pole Position Club + +[STPR_9] +3321 Vice Point + +[STPR_10] +Appartement de Links View + +[STPR_11] +El Swanko Casa + +[STPR_12] +1102 Washington Street + +[STPR_13] +Appartement d'Ocean Heights + +[STPR_14] +Skumole Shack + +[STPR_15] +Hyman Condo + +[RCCANX] +~r~Avion RC annulé. + +[CLT_HL2] +Lorsque tu ramasses des fringues, une ou deux étoiles d'indice de recherche sont enlevées. + +[CRED009] +CONCEPTION DES MISSIONS + +[CRED359] +LEE JOHNSON + +[CRED360] +HENDRIK LESSER + +[CRED361] +PASQUALE STACCHIOTTI + +[CRED362] +ENRIQUE FERNANDEZ + +[CRED363] +PAUL BYERS + +[CRED364] +MIKE EMENY + +[CRED365] +ROB DUNKIN + +[CRED366] +CHARLIE KINLOCH + +[CRED367] +KEVIN HOBSON + +[CRED368] +JIM CREE + +[MOB_66A] +Tommy, Tommy, Tommy, pourquoi t'es revenu ici? + +[MOB_66B] +On t'a déjà dit qu'on voulait plus te revoir. + +[MOB_67A] +Tommy, je crois que tu devrais rester à l'écart, tu saisis? + +[MOB_67B] +Les petits gars Haïtiens t'aiment pas beaucoup. + +[MOB_18A] +Tommy, c'est Paulo. Tu vas bien? Bon, mec, il faut que je te parle. + +[MOB_18B] +Ah, mon pote, tu croiras jamais l'incroyable petit lot que je viens de lever... + +[MOB_18C] +Elle se promenait juste en bas dans Little Havana, mon pote. + +[MOB_18D] +Elle m'a dit qu'elle s'appelait Mercedes ou un truc comme ça. + +[MOB_18E] +Ah, mon pote, faut que t'ailles voir cette fille! + +[MOB_18F] +Elle ferait bander un eunuque! Elle m'a dit que j'étais le meilleur coup de sa vie et tout et tout! + +[MOB_18G] +Vas-y, trouve-la. A plus tard! + +[MOB_72A] +Tommy, c'est moi, Lance. Ferme-la, Tommy, parce que j'ai pas le temps de discuter! + +[MOB_72B] +J'me tape de ce que t'as à me dire. Pourquoi ça m'intéresserait? T'en as rien à foutre de moi, non? + +[MOB_72C] +Il faut que tu sois plus sympa avec moi. Donne-moi une belle part. Tu vois... + +[MOB_72D] +Tommy... Ecoute, mec, je suis désolé. C'est que... + +[MOB_72E] +Les gens arrêtent pas de me materner, de me traiter comme un gamin. + +[MOB_72F] +Mon frère pourrait me faire ça, mais pas toi. S'il te plaît. + +[MOB_72G] +Faut que j'y aille. + +[MOB_63A] +Tommy, c'est Earnest. Earnest Kelly. + +[MOB_63B] +Ca va? + +[MOB_63C] +Bien. Il va me falloir une canne pour marcher, mais je devrais revenir rapidement dans les affaires. + +[MOB_63D] +Bien. + +[MOB_63E] +J'ai appris au sujet de Lance. Quel enculé, hein? + +[MOB_63F] +Ouais. + +[MOB_63G] +Ne jamais faire confiance à un mec qui se trimballe en pyjama. C'est ce que je dis toujours. J'espère bien qu'il en a bavé, ce con. + +[MOB_63H] +Je crois que oui. Je pensais pas qu'il était comme ça... + +[MOB_63I] +Tommy, t'as beau être cinglé, t'es vraiment naïf. Il va falloir que je t'apprenne deux ou trois trucs sur la vie, dès que je serai remis sur pied. + +[MOB_63J] +Prends ton temps, Earnest et surtout, prends soin de toi. + +[MOB_16A] +Tommy, c'est Paulo. Que pasa amigo? + +[MOB_16B] +Qu'est-ce tu m'veux, Paul? J'veux pas de fringues de contrefaçon. + +[MOB_16C] +Très drôle, mon pote, mais tu sais que je fais pas dans la merde. Non, j'appelle juste pour savoir si t'aurais pas un rôle pour moi dans un de tes films. + +[MOB_16D] +J'ai fait pas mal de X en Angleterre, mec. J'en ai plus que toi dans l'pantalon. + +[MOB_16E] +Paul, merci pour la proposition, je vais y réfléchir. + +[MOB_16F] +Sérieux, pense à moi, après tout ce que j'ai fait pour toi. + +[MOB_16G] +C'est ce que j'essaie d'oublier, justement... + +[MOB_17A] +Tommy Vercetti, comment vas-tu? J'ai entendu tous ces trucs sur toi. Un flambeur en ville maintenant... + +[MOB_17B] +Paul, t'es bourré? + +[MOB_17C] +Non, pauv' con, je suis pas bourré! J'ai juste pris deux ou trois verres et quelques tournées, et ça fait deux jours que j'ai pas fermé l'oeil! + +[MOB_17D] +De toute façon, me traite pas comme ça. Je suis pas une poire. Qui c'est qui t'a lancé dans cette ville? Hein, qui? Moi! + +[MOB_17F] +Ah ouais? + +[MOB_17G] +Me traite pas comme ça? Qui c'est qui t'a présenté à des gens? Je t'ai montré tous les trucs, je me suis fait chier pour toi et voilà comment tu m'remercies. + +[MOB_17H] +Tu m'ignores. Tu me laisses de côté après tout ce que j'ai fait pour toi! Qui tu crois que je suis? Une merde ou je sais pas quoi? + +[MOB_17I] +Paul, calme-toi. J'ai été occupé, ne sois pas stupide. + +[MOB_17J] +Je suis pas stupide, connard! Ils me l'ont dit en maison de redressement! Si tu cherches les emmerdes, mon pote, tu vas les trouver! + +[MOB_17K] +Tommy, s'il te plaît! T'étais mon grand espoir! Te fous pas de moi! + +[MOB_17L] +Paul, va roupiller un bon coup, sérieusement. + +[MOB_73A] +Tommy, c'est Steve. + +[MOB_73B] +Salut, Steve. + +[MOB_73C] +Salut le génie. T'es merveilleux! Je suis une merveille! Ils nous adorent. On réécrit le livre des records, mon pote! + +[MOB_73D] +Je parle de putain de récompenses. Enfin je peux envoyer mon père à l'hospice et lui dire de fermer sa gueule. + +[MOB_73E] +Euh... C'est cool, Steve. + +[MOB_73F] +Cool? C'est génial, mec. GENIAL quoi! Il n'a jamais voulu croire en moi, mais regarde ce qu'on a réussi à faire. + +[MOB_73G] +Je suis le plus grand réalisateur de films de cul faits maison de la planète, mon pote. Je voulais juste te dire que je suis heureux de t'avoir rencontré. + +[MOB_73H] +Merci, Steve. + +[MOB_73I] +Je t'aime, bébé. Et reste comme tu es, d'accord? + +[MOB_73J] +D'accord. Salut, Steve. + +[BOLLOX] +Appuie sur la touche ~o~R1 ~w~pour larguer une bombe. Appuie sur la touche ~t~" ~w~pour annuler. + +[BRID_OP] +Avis de tempête terminé : tous les ponts sont maintenant accessibles. + +[BRID_CL] +Avis de tempête : tous les ponts sont fermés. + +[LG_38] +Cible + +[ASSET_C] +Pole Position, OK + +[ASSET_D] +~g~Le club Pole Position va maintenant créer un revenu d'un maximum de $~1~ par jour. Venez retirer votre argent régulièrement. + +[ST_WHEE] +Temps maximum sur roue arrière (secs) + +[ST_STOP] +Temps maximum sur roue avant (secs) + +[ST_2WHE] +Temps maximum sur deux roues (secs) + +[ST_WHED] +Distance maximum sur roue arrière (m) + +[ST_STOD] +Distance maximum sur roue avant (m) + +[ST_2WHD] +Distance maximum sur deux roues (m) + +[OUTFT11] +Survêtement + +[OUTFT12] +Frankie + +[RELOAD] +~g~Tu as gagné la possibilité de recharger ton arme rapidement + +[APACHE] +Hunter livré à l'héliport d'Ocean Beach + +[CRED369] +JOHN MCCARDLE + +[CRED370] +DAVID MURDOCH + +[CRED371] +CHRIS BROWN + +[CRED372] +PAUL GREEN + +[CRED373] +KYLE MILNE + +[CUNTY] +Nouveaux vêtements livrés au Domaine Vercetti + +[GOODBOY] +$50 pour bonne conduite + +[NEWCONT] +Nouveau ~h~point de contact ~w~créé à la marina d'Ocean Beach + +[FIRELVL] +Mission Camion de pompiers niveau ~1~ + +[HELP56] +Appuie sur la touche ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~ pour changer le mode de la caméra. + +[HELP57] +Appuie sur la touche ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~ pour changer le mode de la caméra. + +[HELP58] +Tout en visant, appuie sur la touche ~h~~k~~PED_CYCLE_TARGET_LEFT~~w~ ou ~h~~k~~PED_CYCLE_TARGET_RIGHT~~w~ pour faire défiler les cibles. + +[HELP59] +Tout en visant, appuie sur la touche ~h~~k~~PED_CYCLE_TARGET_LEFT~~w~ ou ~h~~k~~PED_CYCLE_TARGET_RIGHT~~w~ pour faire défiler les cibles. + +[HELP60] +Si tu appuies sur la touche ~h~~k~~PED_SPRINT~ ~w~quand tu essaies de piquer une caisse, tu ne peux pas monter dedans. + +[HELP61] +Tu as désormais des munitions illimitées et tous tes véhicules sont deux fois plus résistants. + +[CRED374] +KEVIN YUN + +[CRED375] +ERICK COBBS + +[CRED376] +RANDY BLAKE + +[CRED377] +BRANDON LIM + +[CRED378] +BRANDON FENOL + +[CRED379] +MICHAEL MANOLE + +[CRED380] +ALETHEIA SIMONSON + +[CRED381] +JOHN JANSEN + +[FEC_LB1] +Regarder + +[FEC_LB2] +derrière + +[FEC_LB3] +Regarder derrière + +[FEC_R3] +(touche R3) + +[FEC_PED] +Commandes à pied + +[FEC_VEH] +Commandes des véhicules + +[FEC_FPR] +Commandes en vue subjective + +[FEC_CMM] +Commandes principales + +[FEC_PWL] +Aller à gauche + +[FEC_PWR] +Aller à droite + +[FEC_PWF] +Avancer + +[FEC_PWT] +Avancer vers caméra + +[FEC_PLB] +Vue arrière + +[FEC_PFR] +Tirer + +[FEC_CLE] +Défilement Gauche des armes + +[FEC_CRI] +Défilement Droite des armes + +[FEC_LKT] +Verrouiller cible + +[FEC_PJP] +Saut à pied + +[FEC_PSP] +Sprint à pied + +[FEC_PSH] +Tir à pied + +[FEC_TLF] +Cible suivante Gauche + +[FEC_TRG] +Cible suivante Droite + +[FEC_CCM] +Centrer caméra derrière joueur + +[FEC_SZI] +Fusil à lunette zoom avant + +[FEC_SZO] +Fusil à lunette zoom arrière + +[FEC_LKL] +Regarder à gauche en vue subjective + +[FEC_LRT] +Regarder à droite en vue subjective + +[FEC_LUP] +Regarder en haut en vue subjective + +[FEC_LDN] +Regarder en bas en vue subjective + +[FEC_LBH] +Regarder derrière le véhicule + +[FEC_LLF] +Regarder à gauche du véhicule + +[FEC_LRG] +Regarder à droite du véhicule + +[FEC_HRN] +Klaxon + +[FEC_HBR] +Frein à main + +[FEC_ACL] +Accélérer + +[FEC_BRK] +Freiner + +[FEC_TSM] +Activer/Désactiver sous-missions + +[FEC_CRD] +Changer la station de radio + +[FEC_ENT] +Entrer/Sortir d'un véhicule + +[FEC_WPN] +Tirer + +[FEC_PAS] +Pause + +[FEC_FPO] +Changer d'arme en vue subjective + +[FEC_SMS] +Afficher/Masquer curseur + +[FEC_CMS] +Changer de mode de caméra + +[FEC_TSS] +Faire une capture d'écran + +[FEC_DBG] +Menu Debug + +[FEC_TGD] +Alterner manette jeu/debug + +[FEC_TDO] +Désactiver caméra debug + +[FEC_IVH] +Inverser souris horizontale + +[FEC_MSL] +BGS + +[FEC_MSM] +BMS + +[FEC_MSR] +BDS + +[FEC_QUE] +??? + +[FEC_TWO] +Deux touches clavier au maximum + +[FEC_UMS] +Boutons souris uniquement + +[FEC_OMS] +Un bouton souris au maximum + +[FEC_UJS] +Un bouton joystick au maximum + +[FEC_OJS] +Un bouton joystick maximum par action + +[FEC_PTL] +Utiliser verrouillage de cible avec commande de tir gauche + +[FEC_PTR] +Utiliser verrouillage de cible avec commande de tir droite + +[FEC_LBC] +Utiliser regarder gauche avec regarder droite + +[FEC_JBO] +JOY ~1~ + +[FEC_WAR] +Avertissement + +[FEC_OKK] +O.K. + +[FEC_DLF] +Erreur lors de suppression + +[FEC_SVU] +Erreur lors de la sauvegarde + +[FEC_LUN] +Erreur lors du chargement. Fichier corrompu, veuillez le supprimer. + +[FEC_PAD] +Manette + +[FEC_JOY] +Joystick + +[FES_CSA] +Sélectionnez une apparence dans la liste suivante : + +[FET_HRD] +PARAMETRES PAR DEFAUT RETABLIS + +[FET_MST] +DIRECTION CONTROLEE PAR LA SOURIS + +[FEC_STR] +ETOILE PAV.NUM. + +[FET_MIG] +GAUCHE, DROITE, MOLETTE SOURIS POUR REGLER + +[FET_CIG] +RETOUR ARRIERE POUR EFFACER - BGS, RETOUR POUR CHANGER + +[FET_DSN] +Skin joueur par defaut.bmp + +[FET_RSO] +PARAMETRE D'ORIGINE RETABLI + +[FET_RSC] +MATERIEL INDISPONIBLE - PARAMETRE D'ORIGINE RETABLI + +[FEA_3DH] +CONFIG. CARTE-SON + +[FEA_SPK] +CONFIG. HAUT-PARLEURS + +[FEM_LOD] +DISTANCE MODELES + +[FEM_VSC] +SYNCHRO VIDEO + +[FEM_FRM] +RESTRICTION VIDEO + +[FEM_MM] +MENU PRINCIPAL + +[FED_RES] +RESOLUTION ECRAN + +[FET_CTL] +CONFIG. PERIPHERIQUE + +[FET_OPT] +OPTIONS + +[FEC_MSH] +SENSIBILITE SOURIS + +[FEC_IVV] +INVERSER SOURIS VERTIC. + +[FET_MTI] +CONFIG. SOURIS + +[FEC_FNC] +F~1~ + +[FEC_IRT] +INSER + +[FEC_DLL] +SUPPR + +[FEC_HME] +ORIG + +[FEC_END] +FIN + +[FEC_PGU] +PAGE HAUT + +[FEC_PGD] +PAGE BAS + +[FEC_UPA] +HAUT + +[FEC_DWA] +BAS + +[FEC_LFA] +GAUCHE + +[FEC_RFA] +DROITE + +[FEC_NUM] +PAV.NUM + +[FEC_NMN] +PAV.NUM~1~ + +[FEC_FWS] +PAV.NUM / + +[FEC_PLS] +PAV.NUM + + +[FEC_MIN] +PAV.NUM - + +[FEC_DOT] +PAV.NUM . + +[FEC_NLK] +VERR NUM + +[FEC_ETR] +ENTR + +[FEC_SLK] +ARRET DEFIL + +[FEC_PSB] +PAUSE + +[FEC_BSP] +RET. ARR. + +[FEC_TAB] +TAB + +[FEC_CLK] +VERR MAJ + +[FEC_RTN] +RETOUR + +[FEC_LSF] +MAJ. G + +[FEC_RSF] +MAJ. D + +[FEC_LCT] +CTRL G + +[FEC_RCT] +CTRL D + +[FEC_LAL] +ALT G + +[FEC_RAL] +ALT D + +[FEC_LWD] +WIN G + +[FEC_RWD] +WIN D + +[FEC_WRC] +CLIC WIN + +[FEC_SPC] +ESP + +[WIN_TTL] +Grand Theft Auto VC + +[WIN_95] +Grand Theft Auto VC n'est pas compatible WINDOWS 95 + +[WIN_DX] +Grand Theft Auto VC requiert la version 8.1 de DirectX minimum. + +[FET_EIG] +IMPOSSIBLE DE PARAMETRER UNE TOUCHE POUR CETTE ACTION + +[FET_DAM] +MODELAGE ACCOUST. DYNAMIQUE + +[FEQ_SRE] +Etes-vous sûr de vouloir quitter ? Votre progression depuis la dernière sauvegarde sera perdue. Continuer ? + +[FEQ_SRW] +Etes-vous sûr de vouloir quitter la partie ? + +[FET_QG] +QUITTER PARTIE + +[FEN_STA] +COMMENCER PARTIE + +[FET_PAU] +MENU PAUSE + +[REPLAY] +RALENTI + +[FET_PS] +CONFIG. JOUEURS + +[FEC_ANS] +Action + +[CVT_MSG] +Conversion des textures vers un format optimal pour votre carte graphique + +[FEC_SFT] +MAJ + +[FEH_VMP] +VOIR CARTE + +[FES_DEE] +Echec de la suppression ! Recommencer. + +[FES_CMP] +Echec de la sauvegarde ! Recommencer. + +[FESZ_WR] +Sauvegarde en cours. Un instant... + +[FELD_WR] +Chargement en cours. Un instant... + +[FEDL_WR] +Suppression en cours. Un instant... + +[PCRESRT] +Lancement d'une nouvelle partie. Un instant... + +[FET_STI] +Commandes standard + +[FET_CTI] +Commandes classiques + +[FEH_NA] +OPTION NON DISPONIBLE + +[FEH_MPH] +SOURIS, CURSEURS POUR SE DEPLACER - PAGE HAUT, PAGE BAS, MOLETTE POUR ZOOMER, L - LEGENDE + +[FEA_MP3] +LECTEUR MP3 + +[NO_PCCD] +Insérer le disque de GTA Vice City ou appuyer sur ECHAP pour annuler + +[FEH_SSA] +CURSEURS POUR SE DEPLACER - S POUR SAUVEGARDER LE FICHIER + +[FES_CMI] +DERNIERE MISSION REUSSIE + +[FET_STS] +STATS SAUVEGARDEES DANS 'STATS.HTML' + 'STATS.TXT' + +[WIN_VDM] +Grand Theft Auto VC ne dispose pas de suffisamment de mémoire graphique. + +[FEC_ERI] +Erreur ! Une ou plusieurs actions ne sont pas assignées à une touche ou à un bouton. Vérifier que toutes les actions sont bien assignées. + +[FEC_TFU] +Tourelle + Orienter haut + +[FEC_TFD] +Tourelle + Orienter bas + +[FET_RIG] +CHOISIR UNE AUTRE COMMANDE POUR CETTE ACTION + +[FEA_NM3] +AUCUN FICHIER MP3 TROUVE + +[FEA_MPB] +VOLUME MP3 A FOND + +[FEA_MUS] +VOLUME MUSIQUE + +[FEA_SFX] +VOLUME EFFETS SONORES + +[CVT_ERR] +Espace disque épuisé. Libérez de la mémoire sur votre disque dur pour continuer. Appuyez sur ECHAP pour annuler. + +[FEA_ADP] +AUTODETECT MATERIEL + +{=================================== MISSION TABLE AMBULAE ===================================} + +[ATUTOR2:AMBULAE] +~g~Conduis les patients à l'hôpital. DOUCEMENT. Chaque secousse réduit leurs chances de survie. + +[A_FULL:AMBULAE] +~r~Ambulance pleine! + +[A_FAIL2:AMBULAE] +~r~Ton manque de rapidité a été fatal pour le patient! + +[A_FAIL3:AMBULAE] +~r~Le patient est mort!! + +[A_PASS:AMBULAE] +Sauvé! + +[A_COMP2:AMBULAE] +Tu ne seras plus jamais essoufflé! + +[A_COMP1:AMBULAE] +Missions Ambulance réussies : $~1~ + +[A_CANC:AMBULAE] +~r~Mission ambulance annulée! + +[A_COMP3:AMBULAE] +Missions ambulance accomplies! Maintenant, tu peux sprinter indéfiniment! + +[ALEVEL:AMBULAE] +Mission ambulance, niveau ~1~ + +[A_FAIL1:AMBULAE] +Mission ambulance achevée. + +[A_SAVES:AMBULAE] +PERSONNES SAUVEES : ~1~ + +{=================================== MISSION TABLE ASSIN1 ===================================} + +[ASM1_5:ASSIN1] +~r~Il a livré toutes ses pizzas! + +[ASM1_6:ASSIN1] +Livraisons restantes : + +[ASM1_7:ASSIN1] +~g~Carl Pearson, livreur de pizzas. Tue-le avant qu'il ne termine ses livraisons. + +[ASM1_D:ASSIN1] +Ton aide dans l'éradication de ces indésirables fut une excellente affaire. + +{=================================== MISSION TABLE ASSIN2 ===================================} + +[ASM2_1:ASSIN2] +~g~Mme Dawson va bientôt quitter la bijouterie à Vice Point. Tue-la. Il faut que ça ressemble à un accident de voiture. + +[ASM2_3:ASSIN2] +~g~Ca va sauter, écarte toi! + +[ASM2_4:ASSIN2] +~r~T'as bousillé sa bagnole alors qu'elle était même pas dedans. Elle est pas près de l'utiliser, maintenant! + +[ASM2_5:ASSIN2] +~r~Elle s'est enfuie! + +[ASM2_6:ASSIN2] +~r~Tu étais trop près de l'accident! + +[ASM2_7:ASSIN2] +~g~N'utilise pas d'armes! Il faut que ça ressemble à un accident! Sors-la plutôt de la route! + +[ASM2_8:ASSIN2] +~g~La mort de madame Dawson doit avoir l'air d'un accident. N'utilise pas d'armes. + +[ASM2_9:ASSIN2] +~g~Il te faut une voiture pour ce boulot! + +[ASM2_10:ASSIN2] +~g~Quand sa bagnole prendra feu, éloigne-toi le plus possible du lieu de l'accident. + +[ASM2_11:ASSIN2] +A l'aide! + +[ASM2_12:ASSIN2] +Que quelqu'un m'aide! + +[ASM2_13:ASSIN2] +Oh non! + +[ASM2_A:ASSIN2] +Mes félicitations pour ce travail bien fait, Mr. Teal. Mon client était ravi. + +[ASM2_2:ASSIN2] +Santé : + +{=================================== MISSION TABLE ASSIN3 ===================================} + +[ASM3_11:ASSIN3] +TEMPS : + +[ASM3_C:ASSIN3] +Un gang européen prépare un braquage de banque à Vice City. Mes employeurs préfèreraient que cela n'arrive pas. + +[ASM3_D:ASSIN3] +Chaque membre du gang dispose d'une couverture à Vice City. Certains ont des petits boulots, d'autres jouent les touristes. + +[ASM3_E:ASSIN3] +Chaque cible et leur position probable sont indiqués sous le téléphone. + +[ASM3_14:ASSIN3] +~g~Dick Tanner est à côté de DBP Security sur Ocean Drive. + +[ASM3_15:ASSIN3] +~g~Marcus Hammond et Franco Carter sont près de la bijouterie de Vice Point. + +[ASM3_16:ASSIN3] +~g~Nick Kong est à côté de Washington Beach. + +[ASM3_18:ASSIN3] +~g~Ne t'approche pas trop près de la cible où elle risque de te repérer et d'essayer de se faire la malle! + +[ASM3_19:ASSIN3] +~g~Il t'a repéré! Bute-le! + +[ASM3_20:ASSIN3] +~g~Ils t'ont repéré! Tue-les vite tous les deux! + +[ASM3_21:ASSIN3] +~r~Tu n'as pas tué tous les membres du gang à temps! + +[ASM3_22:ASSIN3] +~g~Ne t'approche pas trop des cibles ou elles risquent de te repérer et d'essayer de filer. + +[ASM3_12:ASSIN3] +~g~Des armes ont été laissées à ta disposition dans le coin si tu en as besoin. Tu as ~h~9 MINUTES ~g~pour tuer tous les membres du gang. + +[ASM3_13:ASSIN3] +~g~Mike Griffin travaille sur un panneau publicitaire à Washington. + +[ASM3_17:ASSIN3] +~g~Charlie Dilson est à moto sur Washington. + +{=================================== MISSION TABLE ASSIN4 ===================================} + +[ASM4_12:ASSIN4] +Distance : + +[ASM4_15:ASSIN4] +~g~Prends le fusil à lunette sur ta droite. + +[ASM4_16:ASSIN4] +~g~Observe la femme au balcon. Elle va descendre l'escalator et demander l'heure à quelqu'un. + +[ASM4_17:ASSIN4] +~g~Lorsque la conversation est terminée, liquide son interlocuteur sans faire de mal à la femme. + +[ASM4_18:ASSIN4] +~g~Une fois la cible éliminée, récupère la serviette et amène-la à Ammu-Nation dans le centre. + +[ASM4_19:ASSIN4] +~g~Reste à distance de la cible. Sa proximité t'est indiquée par la barre de distance située dans le coin supérieur droit de l'écran. + +[ASM4_20:ASSIN4] +~g~Ne la laisse pas devenir pleine ou tu seras repéré. + +[ASM4_21:ASSIN4] +~g~Récupère la serviette! + +[ASM4_22:ASSIN4] +~g~Ramène la serviette à Ammu-Nation dans le centre. + +[ASM4_23:ASSIN4] +~g~Il t'a repéré et s'enfuit. Rattrape-le et récupère la serviette! + +[ASM4_25:ASSIN4] +~r~T'as buté la femme, imbécile! + +[ASM4_26:ASSIN4] +~r~La cible a embarqué pour son vol! + +[ASM4_27:ASSIN4] +~r~La cible t'a repéré! Tu devais garder tes distances! + +[ASM4_28:ASSIN4] +~r~La cible t'a repéré! Elle t'a entendu tirer! + +[ASM4_29:ASSIN4] +~r~Ne le tue qu'après qu'il ait parlé à la femme! + +[ASM4_A:ASSIN4] +L'heure est venue de s'occuper d'un gros poisson, M. Teal. Il y a un fusil dans le feuillage à votre droite. + +[ASM4_B:ASSIN4] +Surveillez la femme sur le balcon situé au-dessus des guichets d'enregistrement. Elle va avancer à travers la foule et demander l'heure à quelqu'un. + +[ASM4_C:ASSIN4] +Vous devez tuer son interlocuteur, récupérer la mallette et l'apporter à l'emplacement indiqué sous le téléphone. + +{=================================== MISSION TABLE ASSIN5 ===================================} + +[ASM5_A:ASSIN5] +Un deal important se déroule sur le toit de l'usine de crème glacée Cherry Popper. + +[ASM5_B:ASSIN5] +Descendez toutes les personnes impliquées, embarquez la marchandise et amènez-la à la piste pour hélicos de l'aéroport. + +[ASM5_C:ASSIN5] +Il y a une porte sur votre gauche qui mène à l'arrière de l'usine. + +[ASM5_1:ASSIN5] +~g~Entre dans l'enceinte située derrière l'usine de crème glacée Cherry Popper et va jusqu'au toit où se fait le deal. + +[ASM5_2:ASSIN5] +~g~Récupère la marchandise et amène-la à la piste pour hélicos de l'aéroport. + +[ASM5_3:ASSIN5] +~g~Emmène la marchandise à la piste pour hélicos de l'aéroport! + +{=================================== MISSION TABLE BANKJ1 ===================================} + +[WANTED1:BANKJ1] +~g~Largue les flics et perds ton indice de recherche! + +[BJM1_A:BANKJ1] +Tommy! Hé, regarde-ça, c'est génial! J'ai fait installer un minibar! + +[BJM1_B:BANKJ1] +Y'a déjà un vrai bar en bas, Ken. + +[BJM1_C:BANKJ1] +Ouais, je sais, et alors? Bon, j'ai le tableau noir que tu m'as demandé. + +[BJM1_D:BANKJ1] +Ah, c'est là qu'on voit l'intérêt de ton école de droit : t'as appris à suivre les instructions. + +[BJM1_E:BANKJ1] +Bon, il me faut un mec sûr. + +[BJM1_F:BANKJ1] +Euh, d'accord, laisse-moi réfléchir... Sûr... sûr... sûr... Je sais! Je connais un mec qui te plaira! + +[BJM1_G:BANKJ1] +Aaah, non, merde, ce con est au trou. + +[BJM1_H:BANKJ1] +Comment ça au trou? + +[BJM1_I:BANKJ1] +Dans un poste de police. Il attend son transfert. + +[BJM1_J:BANKJ1] +Je crois qu'il va être remis en liberté sur parole... + +[BJM1_1:BANKJ1] +~g~Sors Cam Jones de chez les flics! + +[BJM1_3:BANKJ1] +~g~Tu trouveras quelque chose d'utile dans la salle des casiers. + +[BJM1_21:BANKJ1] +~g~La carte d'accès aux cellules se trouve à l'étage. + +[BNK1_7:BANKJ1] +Cam Jones? + +[BNK1_8:BANKJ1] +Je viens t'aider à mettre les bouts! + +[BNK1_10:BANKJ1] +Ouais, c'est moi... + +[BNK1_11:BANKJ1] +Ca me va! + +[BNK1_13:BANKJ1] +J'ai un boulot à faire et t'es mon perceur de coffre. + +[BNK1_14:BANKJ1] +Marre de perdre mon temps dans une cellule. + +[BJM1_22:BANKJ1] +~g~Ramène Cam chez lui! + +[BJM1_23:BANKJ1] +~g~Tu dois d'abord trouver la carte d'accès! + +[BNK1_12:BANKJ1] +Sème les flics et ramène-moi chez moi! + +[BJM1_20:BANKJ1] +Jette ton flingue ou tu vas en subir les conséquences! + +[BJM1_5:BANKJ1] +Accès réservé au personnel autorisé! + +[BJM1_2:BANKJ1] +~r~T'étais supposé faire sortir Cam, pas le faire tuer! + +[BJM1_4:BANKJ1] +Il est armé! Descendez-le! + +{=================================== MISSION TABLE BANKJ2 ===================================} + +[BJM2_A:BANKJ2] +Il nous faut un braqueur. T'en connais un? + +[BJM2_B:BANKJ2] +Hé, Tommy, Tommy, ce truc ça permet d'rester dans l'coup, mec! + +[BJM2_C:BANKJ2] +WoooOOOooo! + +[BJM2_D:BANKJ2] +J'pourrais être ton braqueur! Haut les mains! Haut les mains! + +[BJM2_E:BANKJ2] +T'es pas un braqueur, t'es un idiot. + +[BJM2_F:BANKJ2] +Va boire un verre et ferme-la. + +[BJM2_G:BANKJ2] +Hé, tire-toi de mon chemin! Ye ye ye ow ow! + +[BJM2_H:BANKJ2] +Cam, qu'est-ce que t'en penses? + +[BJM2_I:BANKJ2] +Ben, le meilleur tireur de cette ville, c'est un mec qui s'appelle Cassidy. + +[BJM2_J:BANKJ2] +Ah ouais? + +[BJM2_K:BANKJ2] +Ouais. Un militaire ou en tout cas c'est ce qu'il croit. + +[BJM2_L:BANKJ2] +Je doute qu'il ait jamais été dans l'armée, mais il sait sûrement où dégotter des flingues. + +[BJM2_M:BANKJ2] +Il devrait être au champ de tir. + +[BJM2_2A:BANKJ2] +C'est toi, Phil Cassidy? + +[BJM2_2B:BANKJ2] +Pourquoi? + +[BJM2_2C:BANKJ2] +Je cherche un mec qui sache se servir d'un flingue. D'après ce que je vois, j'suis pas convaincu... + +[BJM2_2D:BANKJ2] +Mon pote, je peux dégomme une mouche sur ta tête à 25 mètres! + +[BJM2_2E:BANKJ2] +Vraiment? + +[BJM2_2F:BANKJ2] +Ouais. J'ai appris ça à l'armée. + +[BJM2_2G:BANKJ2] +Ils s'amusent souvent à descendre des mouches à l'armée? Heureusement que je paye pas d'impôts! + +[BJM2_2H:BANKJ2] +Tu t'crois marrant peut-être, minus? + +[BJM2_2I:BANKJ2] +Ha ha ha ha ha! + +[BJM2_2J:BANKJ2] +Tirons quelques coups. + +[BJM2_1:BANKJ2] +~g~Va à Ammu-Nation dans le centre et parle à Phil Cassidy. + +[BJM2_4:BANKJ2] +SCORE MANCHE 1 : ~1~ + +[BJM2_6:BANKJ2] +SCORE MANCHE 2 : ~1~ + +[BJM2_7:BANKJ2] +SCORE TOTAL : ~1~ + +[BJM2_9:BANKJ2] +~g~Va au point de départ de la deuxième manche. + +[BJM2_11:BANKJ2] +~r~Phil est mort! + +[BJM2_12:BANKJ2] +~r~Un des tireurs est mort! + +[BJM2_14:BANKJ2] +~g~Continue jusqu'au prochain secteur! + +[BJM2_17:BANKJ2] +~g~Va parler à Phil. + +[BJM2_24:BANKJ2] +~g~La cible la plus proche rapporte un point. + +[BJM2_25:BANKJ2] +~g~La cible intermédiaire rapporte deux points. + +[BJM2_27:BANKJ2] +~g~Toutes les cibles de cette manche rapportent un point. + +[BNK2_4:BANKJ2] +Waou! + +[BNK2_5:BANKJ2] +Il raterait une vache dans un couloir! + +[BNK2_7:BANKJ2] +Bon, tu veux bien me rendre un service et m'assister sur un boulot? + +[BNK2_8:BANKJ2] +Mon gars, vu comment tu tires, si tu me demandes en mariage, j'accepte. + +[BNK2_9A:BANKJ2] +Mon gars, tu ferais mieux de remballer ton baratin et tes grands projets. T'es trop mauvais tireur. + +[BNK2_9B:BANKJ2] +T'es trop mauvais tireur. + +[BJM2_28:BANKJ2] +SCORE MANCHE TROIS : ~1~ + +[BJM2_26:BANKJ2] +~g~La cible éloignée rapporte trois points. + +[BNK2_1:BANKJ2] +BALLES REELLES + +[RANGE_1:BANKJ2] +SCORE POUR LE TIR : ~1~ + +[BJM2_2:BANKJ2] +~g~Pour quitter la manche, appuie sur la ~h~~k~~PED_JUMPING~. + +[BJM2_N:BANKJ2] +Du calme... + +{=================================== MISSION TABLE BANKJ3 ===================================} + +[BJM3_A:BANKJ3] +Les choses commencent à se mettre tranquillement en place, ici. + +[BJM3_B:BANKJ3] +C'est quoi le plan, Tommy? Que pasa, amigo? + +[BJM3_C:BANKJ3] +Le plan, c'est que toi, tu restes ici à glander. Bon, on a besoin d'un chauffeur. + +[BJM3_D:BANKJ3] +Tommy, je m'en charge! Je peux conduire. + +[BJM3_E:BANKJ3] +C'est Hilary que tu veux, mec! Pas un stupide vantard d'école de droit. + +[BJM3_F:BANKJ3] +Hilary, c'est le meilleur. T'as jamais vu quelqu'un conduire aussi vite. Je vais l'appeler. + +[BJM3_G:BANKJ3] +Salut, Hil, c'est Phil. Comment ça va? Non, attends, on causera plus tard, j'ai besoin que tu me rendes un service. + +[BJM3_H:BANKJ3] +J'ai avec moi un mec du nord. Non, je crois pas qu'il était dans l'armée, mais il a besoin d'un conducteur. + +[BJM3_I:BANKJ3] +Pour un peu d'adrénaline. Ok, compris. + +[BJM3_J:BANKJ3] +Qu'est-ce qu'il a dit? + +[BJM3_K:BANKJ3] +Bon, il marche, pas de problème. En fait, y'a juste un détail, tu vois, il pose toujours une condition. + +[BJM3_L:BANKJ3] +Il refuse de bosser pour quelqu'un qui ne peut pas le battre. Un truc à cause de sa mère. + +[BJM3_M:BANKJ3] +Quoi qu'il en soit, il veut t'affronter d'abord et il a dit qu'il te retrouvait dehors... + +[BJM3_2A:BANKJ3] +C'est toi Tommy? Evidemment que c'est toi Tommy, enfin, je veux dire, + +[BJM3_2B:BANKJ3] +pourquoi est-ce que quelqu'un d'autre parlerait avec moi? + +[BJM3_2C:BANKJ3] +Bon. Ecoute-moi. + +[BJM3_2D:BANKJ3] +Je conduis pour toi que SI, et seulement SI, tu peux conduire convenablement. + +[BJM3_2E:BANKJ3] +Abandonne et je ne te pardonnerai jamais. + +[BJM3_2:BANKJ3] +~r~Hilary est mort! + +[BJM3_4:BANKJ3] +~g~T'as besoin d'une voiture! + +[BNK3_1:BANKJ3] +Ok, je piloterai pour toi, mais s'il te plaît, traite-moi mal. + +[BNK3_3A:BANKJ3] +Course de rue illégale dans Vice Point! + +[BNK3_3B:BANKJ3] +Appel à tous les officiers. + +[BNK3_3C:BANKJ3] +Les courses de rue sont formellement interdites! + +{=================================== MISSION TABLE BANKJ4 ===================================} + +[BNK4_A:BANKJ4] +~w~Comme vous pouvez voir, les mecs, ça va être le fric le plus facile qu'on se soit jamais fait! + +[BNK4_B:BANKJ4] +~w~Tommy, sérieusement, va falloir que tu considères la question judiciaire. + +[BNK4_C:BANKJ4] +~w~Merde! Qu'est-ce que tu fumes, mon pote? J'appelle pas ça un plan! + +[BNK4_D:BANKJ4] +~w~Bon, de toute manière, on a pas besoin de plan! + +[BNK4_E:BANKJ4] +~w~Prenez le communisme, ça c'était un plan! Et regarde où en est la Russie, maintenant! + +[BNK4_F:BANKJ4] +~w~Du calme, Ok? Avec une équipe comme ça, il n'y aura pas le moindre problème! + +[BNK4_G:BANKJ4] +~w~On amène Cam au coffre. Phil, toi et moi, on s'occupe de la sécurité et Hilary conduit la bagnole. + +[BNK4_H:BANKJ4] +~w~Hum, euh, t'oublies pas quelqu'un? Quelqu'un qui t'aurait aidé depuis le début dans cette ville? Quelqu'un... + +[BNK4_I:BANKJ4] +~w~Ken... Ken, c'est vrai. Bon, Ken va s'occuper de blanchir le pognon et de garder les boissons au frais! + +[BNK4_J:BANKJ4] +~w~Je comprends pas ce que je suis supposé faire ici. + +[BNK4_K:BANKJ4] +~w~Ecoute, c'est dans la poche. T'as jamais vu de film? + +[BNK4_L:BANKJ4] +~w~On entre dans la banque, on sort nos flingues et on ressort plein aux as! + +[P_DEAD:BANKJ4] +~r~Phil est mort! + +[C_DEAD:BANKJ4] +~r~Cam est mort! + +[H_DEAD:BANKJ4] +~r~Hilary est mort! + +[P_HIND:BANKJ4] +~r~T'as perdu Phil! + +[C_HIND:BANKJ4] +~r~T'as oublié Cam! + +[H_HIND:BANKJ4] +~r~Hilary est resté derrière! + +[GETCAR:BANKJ4] +~g~Monte dans la bagnole pour terminer le boulot! + +[TRASHED:BANKJ4] +~r~T'as détruit la bagnole pour s'enfuir! + +[BNK4_1:BANKJ4] +Je vais conduire. + +[BNK4_2:BANKJ4] +Super. Un passager. Attends que j'en parle à la réunion! + +[BNK4_3A:BANKJ4] +Hé, fais gaffe à la caisse, Tommy! + +[BNK4_3B:BANKJ4] +Tommy, Hilary prend trop de place! + +[BNK4_3C:BANKJ4] +C'est pas vrai! + +[BNK4_3D:BANKJ4] +Toi aussi! + +[BNK4_3E:BANKJ4] +Hé, fermez-la tous les deux ou vous rentrez à pied! + +[BNK4_3F:BANKJ4] +Ouais, Hilary. + +[BNK4_3I:BANKJ4] +Putain, Phil! Arrête d'agiter ce truc devant moi! + +[BNK4_3J:BANKJ4] +Ouais, tu vas éborgner quelqu'un! + +[BNK4_3M:BANKJ4] +Mon bébé! Elle est foutue! + +[BNK4_3O:BANKJ4] +Tu t'accroches à l'illusion de la permanence! + +[BNK4_3P:BANKJ4] +Quoi?! + +[BNK4_3Q:BANKJ4] +Tu crois que toutes les choses dureront! + +[BNK4_3R:BANKJ4] +La jeunesse, les amours, les pizzas... + +[BNK4_3S:BANKJ4] +Tout prend fin un jour, et tu dois l'accepter. + +[BNK4_3T:BANKJ4] +Ouais, t'as raison, merci, Cam. + +[BNK4_3U:BANKJ4] +De rien. + +[BNK4_3V:BANKJ4] +Hé, Tommy, pourquoi on s'arrête? + +[BNK4_4A:BANKJ4] +~w~Hilary, continue à tourner autour du quartier. + +[BNK4_5:BANKJ4] +~w~Ok, Tommy, ok. + +[BNK4_6:BANKJ4] +~w~C'EST UN HOLD-UP! + +[BNK4_7:BANKJ4] +~w~PERSONNE NE BOUGE! + +[BNK4_8:BANKJ4] +~w~TOUT LE MONDE CONTRE LE MUR! + +[BNK4_9:BANKJ4] +Phil! Monte la garde! + +[BNK4_10:BANKJ4] +Bien compris! + +[BNK4_11:BANKJ4] +Amène-toi, Cam, la salle des coffres est en haut... + +[BK4_12A:BANKJ4] +Merde! C'est un Flange 9000! + +[BK4_12B:BANKJ4] +Ca peut prendre des heures à ouvrir, + +[BK4_12C:BANKJ4] +ou cinq minutes si tu peux trouver le directeur... + +[BNK4_13:BANKJ4] +Je vais aller voir où il se planque. + +[BK4_14A:BANKJ4] +Ca roule, Phil? + +[BNK4_15:BANKJ4] +Pas de problème. Tout le monde se tient relax. + +[BNK4_16:BANKJ4] +Toi! Tu viens avec moi! + +[BNK4_17:BANKJ4] +D'accord! D'accord! Ne tirez pas! + +[BNK4_18:BANKJ4] +J'AI DIT PERSONNE NE BOUGE! + +[BK4_19A:BANKJ4] +C'est réglé sur une serrure à horloge, + +[BK4_19B:BANKJ4] +Vous feriez aussi bien d'abandonner tout de suite! + +[BK4_20A:BANKJ4] +Putain, je peux court-circuiter l'horloge, + +[BK4_20B:BANKJ4] +on a juste besoin de ton code secret et c'est bingo! + +[BNK4_21:BANKJ4] +Reste ici. T'essayes quoi que ce soit et t'es mort! Pigé? + +[BK4_24A:BANKJ4] +Je vais voir Phil, je reviens tout de suite. + +[BK4_24B:BANKJ4] +Je t'avais dit de pas toucher à cette alarme! + +[BNK4_25:BANKJ4] +Le commando du SWAT sera là d'une minute à l'autre! + +[BNK4_27:BANKJ4] +J'aurais besoin d'un coup de main, Tommy! + +[BNK4_28:BANKJ4] +Ici le SWAT de Vice City! Vous êtes complètement encerclés! + +[BNK4_29:BANKJ4] +Encerclés? HA HA HA HAAAAAaaa! + +[BNK4_30:BANKJ4] +Ils racontent que des conneries, ces ripoux! + +[BK4_31A:BANKJ4] +Tommy! La salle des coffres est ouverte! + +[BK4_34A:BANKJ4] +Ok, on a la caisse de retraite du SWAT! Tirons-nous de là! + +[BK4_34B:BANKJ4] +Bon, vous l'aurez cherché! C'était votre dernière chance! + +[BK4_35A:BANKJ4] +Ils lancent l'assaut! + +[BK4_35B:BANKJ4] +Planquez-vous! + +[BNK4_36:BANKJ4] +Où est Cam? + +[BNK4_37:BANKJ4] +Du passé... + +[BNK4_38:BANKJ4] +C'était le dernier! On dégage! Allez! + +[BNK_39:BANKJ4] +Merde! Où est Hilary? + +[BK4_40A:BANKJ4] +Il me payera ça! + +[BNK4_42:BANKJ4] +Hé! Les mecs! Montez! Je vous couvre! + +[BNK4_44:BANKJ4] +On a réussi! On est riches! RICHES! + +[BNK4_45:BANKJ4] +Dommage que Cam y soit resté, c'était un mec réglo! + +[BNK4_46:BANKJ4] +Ouais. Enfin... On hérite de sa part! + +[BNK4_47:BANKJ4] +Tu l'as dit! Ouais! + +[BNK4_48:BANKJ4] +Tommy, que dirais-tu d'un massage? + +[BNK4_49:BANKJ4] +Salut, Mercedes! Ouais, pourquoi pas, je me sens un peu tendu... + +[BNK450A:BANKJ4] +Qu'est-ce que je te disais, Tommy? Hein, quoi? Le SWAT a du souci à se faire quand Kent Paul est en ville. + +[BNK450B:BANKJ4] +Allez, allonge une plus grosse part, mon pote, j'ai besoin de nouvelles fringues! + +[BNK4_94:BANKJ4] +~w~Ok, les mecs. Tout roule comme prévu. + +[BM_DEAD:BANKJ4] +~r~T'avais besoin du directeur vivant! + +[ASSET_A:BANKJ4] +BRAQUAGE DE BANQUE OK! + +[ASSET_B:BANKJ4] +~g~Le Malibu Club génère dorénavant un revenu de $~1~ maximum. Pense à récupérer le fric régulièrement. + +[IDIOT:BANKJ4] +~r~T'as raison, trimballe-toi déguisé en malade pour attirer l'attention! Espèce d'abruti! + +{=================================== MISSION TABLE BARON1 ===================================} + +[COK1_A:BARON1] +Allez, sale carne, allez! Allez! Putain... + +[COK1_B:BARON1] +Connard de cheval! Je te ferai décapiter! + +[COK1_C:BARON1] +C'est qui ce con? + +[COK1_D:BARON1] +Tommy Vercetti, vous vous souvenez de moi? + +[COK1_E:BARON1] +Excuse-moi, mais j'suis un peu enervé. Fais jamais confiance à une saloperie de canasson! + +[COK1_F:BARON1] +Tu fais du bon boulot. Maintenant, tu travailles pour moi. + +[COK1_H:BARON1] +Comme je disais, amigo, tu bosses pour moi. Maintenant, la ferme! Un salopard m'a trahi! + +[COK1_I:BARON1] +Il croit que je sais pas combien de pognon je devrais gagner... Mais me faucher 3%, c'est pareil que d'me faucher 100%! + +[COK1_J:BARON1] +Personne ne me fait ça! PERSONNE! + +[COK1_K:BARON1] +Tu vas le suivre depuis son appart' pour voir où il va! On le descendra plus tard. + +[COK1_1:BARON1] +Oh merde! + +[COK1_2:BARON1] +Un peu trop lent, papi! + +[COK1_4:BARON1] +Pauv' mec. + +[COK1_5:BARON1] +Tu ferais mieux de continuer à courir, connard! + +[COK1_8:BARON1] +~g~Vite! Grimpe dans une caisse et suis-le! + +[COK1_9:BARON1] +~r~Tu es supposé le suivre, pas le buter! + +[COK1_10:BARON1] +~r~Va à la maison des voleurs et trouve où il planque le fric! + +[COK1_11:BARON1] +~g~Regarde par cette fenêtre. + +[COK1_7:BARON1] +~g~Il s'est échappé vers le toit. Suis-le mais ne le tue pas! + +[COK1_G:BARON1] +Je travaille pour du fric. + +{=================================== MISSION TABLE BARON2 ===================================} + +[COK2_V:BARON2] +Il commence à me faire confiance... + +[COK2_A:BARON2] +Qui c'est qui m'a foutu un con pareil! + +[COK2_B:BARON2] +CON! CON! CON! CON! + +[COK2_C:BARON2] +Tommy - + +[COK2_D:BARON2] +Quoi, Ricardo? + +[COK2_E:BARON2] +Ces merdeux, faut toujours qu'ils essayent de m'baiser... + +[COK2_F:BARON2] +C'est le problème avec ce genre de biz... + +[COK2_G:BARON2] +Qu'est-ce que tu fous toi? + +[COK2_H:BARON2] +Ces connards m'ont vraiment manqué de respect. + +[COK2_I:BARON2] +Bientôt, le premier connard venu va penser qu'il peut vendre de la dope à Vice City. + +[COK2_J:BARON2] +Et ensuite, ça sera quoi? Ces pourris de la Mafia, hein? + +[COK2_K:BARON2] +Le Q.G. de ce gang est une vrai forteresse au rez-de-chaussée, + +[COK2_L:BARON2] +alors Quentin ici... Quentin! QUENTIN! + +[COK2_M:BARON2] +Il va te faire survoler la zone! + +[COK2_N:BARON2] +Liquide-les! + +[COK2_O:BARON2] +Qu'est-ce que tu fous toi? + +[COK2_P:BARON2] +Qu'est-ce que tu fous ici? + +[COK2_Q:BARON2] +Hé, je me suis un peu renseigné et c'est sûr + +[COK2_R:BARON2] +que c'est Diaz qui a bousillé le deal et refroidi mon frère! + +[COK2_S:BARON2] +Et il va t'buter toi aussi! + +[COK2_T:BARON2] +Je peux me faire Diaz! + +[COK2_U:BARON2] +Non, écoute-moi! JE m'occupe de Diaz! + +[COK2_1:BARON2] +Il y a un truc que j'pige pas, pourquoi 'Quentin'? + +[COK2_2:BARON2] +Je sais pas, ça sonne bien... Quentin Vance... + +[COK2_3:BARON2] +Vance? Tu t'appelles Lance Vance? + +[COK2_4:BARON2] +Ca va, hein! J'en ai déjà eu pour mon compte à l'école! + +[COK2_5:BARON2] +T'as déjà tiré depuis un hélico? + +[COK2_8:BARON2] +On va où, au fait? + +[COK2_9:BARON2] +Prawn Island. + +[COK2_13:BARON2] +Lance Vance. Mon pauv' vieux... + +[COK2_14:BARON2] +Ok, on y est presque. + +[COK2_15:BARON2] +On va faire un ou deux passages, + +[COK2_16:BARON2] +Alors, descends autant de tireurs que tu peux. + +[COK2_17:BARON2] +Et puis je te dépose et tu te débrouilles tout seul. + +[COK2_20:BARON2] +Merde, c'est la guerre en bas! Descends ces tireurs! + +[COK2_21:BARON2] +On s'fait canarder, mec! + +[COK2_22:BARON2] +Ca coûte un max de réparer c'coucou, alors bute-les! + +[COK2_23:BARON2] +Bon, t'es tout seul à partir de là. Bonne chance, mon frère! + +[COK2_24:BARON2] +Etat hélico : + +[COK2_25:BARON2] +~g~Va récupérer le pognon sur le toit. + +[COK2_27:BARON2] +T'es sur MES plates-bandes, connard! + +[COK2_28:BARON2] +Ta fin est proche! + +[COK2_6:BARON2] +Non. Je vais m'entraîner un peu en chemin. + +[OPEN_B:BARON2] +Les barrières sur les routes pour le centre ville ont été retirées. + +{=================================== MISSION TABLE BARON3 ===================================} + +[COK3_A:BARON3] +Tu fais moins le fier maintenant, hein! + +[COK3_B:BARON3] +Ahahahaha, ahahahaha! + +[COK3_C:BARON3] +Ho! Fais gaffe où tu braques ce truc! + +[COK3_D:BARON3] +Plus de merde de pigeon sur ma bagnole, hein, Tommy! + +[COK3_E:BARON3] +Faut croire que non... + +[COK3_F:BARON3] +T'as sacrément raison. Bon, écoute, + +[COK3_G:BARON3] +tu sais qui possède le bateau le plus rapide de la côte est? + +[COK3_H:BARON3] +Je suis pas bien sûr. + +[COK3_I:BARON3] +MOI! Et je voudrais pas que ça change... + +[COK3_J:BARON3] +Tous les contrebandiers d'ici à Caracas ne rêvent que d'un seul truc, un bateau plus rapide que le mien. + +[COK3_K:BARON3] +Et la rumeur prétend que le chantier naval Hull-o-caust vient juste d'en finir un + +[COK3_L:BARON3] +pour le compte de je ne sais quel connard du Costa Rica. + +[COK3_M:BARON3] +Alors, Tommy... JE VEUX CE BATEAU! + +[COK3_N:BARON3] +Je crois que tes pigeons sont de retour... + +[COK3_O:BARON3] +Ah! Je croyais que je t'avais eu! D'où tu sors? + +[COK3_P:BARON3] +Pigeons d'mes deux! Boum! Aaaaah! + +[COK3_5:BARON3] +~g~Trouve l'interrupteur pour abaisser le bateau. + +[COK3_6:BARON3] +~g~Amène le bateau à la résidence. + +[COK3_7:BARON3] +~r~T'as détruit le bateau! + +[COK3_8:BARON3] +~g~Va au chantier naval des docks et fauche le bateau le plus rapide. + +[COK3_9:BARON3] +~g~Monte dans le bateau. + +{=================================== MISSION TABLE BARON4 ===================================} + +[COK4_A:BARON4] +Ejection! Connerie d'engin! + +[COK4_B:BARON4] +Pourquoi tu m'fais ça? + +[COK4_C:BARON4] +Pour qui tu t'prends, putain de magnéto! Grrr! + +[COK4_D:BARON4] +ENCULE! + +[COK4_E:BARON4] +Si il m'bouffe ma cassette préférée d'El burro, il est fini! + +[COK4_F:BARON4] +Qu'est-ce que je peux faire d'autre? + +[COK4_G:BARON4] +Il doit pas être branché, c'est tout... + +[COK4_H:BARON4] +Hein? + +[COK4_I:BARON4] +Merde! Tant pis, j'peux m'en acheter cent si ça me chante. + +[COK4_J:BARON4] +Bon, Tommy, + +[COK4_K:BARON4] +tous les mois, il y a un indépendant qui met le cap sur Vice City pour y amarrer son yatch. + +[COK4_L:BARON4] +Il vend sa cargaison au premier bateau. + +[COK4_M:BARON4] +Je veux que tu prennes le speedboat, + +[COK4_N:BARON4] +que tu largues tous les autres connards intéressés + +[COK4_O:BARON4] +et que tu ramènes la cargaison ici. Ok? + +[COK4_P:BARON4] +Laisse-moi deviner. T'as pensé que je pourrais avoir besoin d'un ange-gardien. + +[COK4_Q:BARON4] +Je dis juste que t'as intérêt à me laisser entrer, mon pote. + +[COK4_R:BARON4] +Tu peux m'sortir tout un tas de conneries sur le dur solitaire, + +[COK4_S:BARON4] +mais je sais qu'un de ces quatre, j'vais t'sauver la mise. + +[COK4_T:BARON4] +et qu'après tu voudras me rouler une pelle! + +[COK4_U:BARON4] +Taré, va! + +[COK4_V:BARON4] +Hahahaha + +[COK4_1:BARON4] +Tommy, on sait que c'est Diaz qui a fait foirer notre deal... + +[COK4_3:BARON4] +Alors pourquoi on bosse pour sa gueule? + +[COK4_4:BARON4] +Plus on en apprend maintenant, plus on en saura quand on s'emparera de cette ville! + +[COK4_5:BARON4] +J'aime bien ton style, mon pote. Vraiment culotté. + +[COK4_12:BARON4] +Fais gaffe, ils arrivent de partout! + +[COK4_13:BARON4] +On les a eu! Fonce chez Diaz aussi vite que tu peux! + +[COK4_14:BARON4] +Tu veux ta part de plancton? + +[COK4_15:BARON4] +Salue la poiscaille de ma part! + +[COK4_16:BARON4] +Mange! Mange! + +[COK4_19:BARON4] +Encore des emmerdes droit devant! + +[COK4_20:BARON4] +Il y a des porte-flingues sur cette jetée! + +[COK4_24:BARON4] +Joli coup, mon pote. T'es vraiment un cinglé de première! + +[COK4_25:BARON4] +Ah... Merci. + +[COK4_26:BARON4] +A la prochaine, Tommy. + +[COK4_27:BARON4] +Okay, M. Lance Vance Danse. + +[COK4_28:BARON4] +~g~Atteins le yatch avant les autres bateaux! + +[COK4_31:BARON4] +~g~Va jusqu'au bateau le plus rapide de la jetée! + +[COK4_32:BARON4] +~r~Trop lent! + +[COK4_33:BARON4] +~r~T'as détruit le bateau! + +[COK4_34:BARON4] +~g~Bousille ces bateaux! + +[COK4_35:BARON4] +Etat du bateau : + +{=================================== MISSION TABLE BARON5 ===================================} + +[PROP_A:BARON5] +PROPRIETE ACHETEE! + +[COK4_30:BARON5] +~r~Lance est mort! + +[ASS1_A:BARON5] +J'ai récupéré des flingues. Ils sont dans l'coffre. + +[ASS1_B:BARON5] +Merde alors! Où c'est que t'as dégoté tout ça? + +[ASS1_C:BARON5] +Je l'gardais au frais pour une grande occasion... + +[ASS1_D:BARON5] +Ca te botte? + +[ASS1_E:BARON5] +Tu parles que ça m'botte. + +[ASS1_F:BARON5] +Espèce de connard... + +[ASS1_G:BARON5] +Ma belle maison... + +[ASS1_H:BARON5] +Regarde ce que t'en as fait! + +[ASS1_I:BARON5] +Ca c'est pour mon frère! + +[ASS1_J:BARON5] +Je te faisais confiance, Tommy... + +[ASS1_K:BARON5] +Je t'aurais aidé à devenir... + +[ASS1_L:BARON5] +Bonne nuit, Diaz. + +[ASS1_1:BARON5] +Cet endroit va bientôt grouiller de salopards... Fais gaffe. + +[ASS1_2:BARON5] +T'en fais pas, Tommy, je te couvre. + +[ASS1_4:BARON5] +Diaz doit être à l'intérieur! + +[ASS1_13:BARON5] +DIAZ! Je suis venu reprendre ton affaire! + +[ASS1_14:BARON5] +TOMMY! Tu me trahis... Idiot! Je vais te buter tout de suite! + +[ASS1_16:BARON5] +~g~Tue Diaz! + +[BUD1:BARON5] +Lance + +[ASS1_18:BARON5] +~g~La porte est verrouillée. Trouve un autre chemin. + +[ASS1_19:BARON5] +Par ici! + +[ASS1_20:BARON5] +Tommy, mon problème c'est Quentin, pas toi, mec! + +{=================================== MISSION TABLE BIKE1 ===================================} + +[BM1_A:BIKE1] +Où est Baker? + +[BM1_B:BIKE1] +Je cherche Big Mitch Baker... + +[BM1_C:BIKE1] +Qui le cherche? + +[BM1_D:BIKE1] +Tommy Vercetti. + +[BM1_E:BIKE1] +Vercetti... + +[BM1_F:BIKE1] +T'as pas l'air d'un flic, ce qui te donne droit à une minute... + +[BM1_G:BIKE1] +T'as intérêt à faire court! + +[BM1_H:BIKE1] +Kent Paul dit que ça pourrait t'intéresser de te charger de la sécurité pour un boulot qu'il prépare. + +[BM1_I:BIKE1] +Kent Paul? Ah! Pas étonnant qu'il t'ai envoyé. + +[BM1_J:BIKE1] +La dernière fois qu'il est passé ici, il en est ressorti par la fenêtre avec rien d'autre que son costume d'anglais. + +[BM1_K:BIKE1] +Bon, t'es intéressé ou pas? + +[BM1_L:BIKE1] +On ne rend des services qu'à ceux qui font partie de la bande. + +[BM1_M:BIKE1] +Comment je me joins à vous? + +[BM1_N:BIKE1] +On est pas un club de loisirs, mon pote! Tu peux conduire une bécane? + +[BM1_O:BIKE1] +Tu peux t'asseoir sur un tabouret de bar et boire? + +[BM1_P:BIKE1] +Cougar, Zeppelin, allez voir comment elle se débrouille sur une bécane... + +[BM1_2:BIKE1] +~g~Il te faut une Freeway ou une Angel pour participer! + +[BM1_3:BIKE1] +~r~Les concurrents ont été attaqués! + +[BIKE1_1:BIKE1] +Bon, chouettes fringues. Voyons ce que tu peux faire. + +[BM1_1:BIKE1] +~g~Enfourche une Freeway ou une Angel et va sur la grille de départ. + +{=================================== MISSION TABLE BIKE2 ===================================} + +[BM2_4:BIKE2] +~r~Tu n'as pas rempli le chaosmètre à temps! + +[BM2_1:BIKE2] +CHAOSMETRE : + +[BM2_A:BIKE2] +Ha ha ha, je t'ai encore eu! + +[BM2_B:BIKE2] +Hé, Vercetti. + +[BM2_C:BIKE2] +Cougar dit que tu te débrouilles plutôt bien sur une bécane. + +[BM2_D:BIKE2] +Ouais, mais combien d'épreuves il va encore falloir que je me tape? + +[BM2_E:BIKE2] +Je suis très occupé, mon pote. + +[BM2_F:BIKE2] +Si c'est une baston qui peut régler la question, alors amène-toi! + +[BM2_G:BIKE2] +Devenir l'un d'entre nous, c'est pas qu'une question de baston. C'est appartenir à une famille. + +[BM2_H:BIKE2] +Ouais, j'ai déjà appartenu à une famille avant et ça n'a pas bien collé. + +[BM2_I:BIKE2] +Peut-être, mais notre famille veille sur les siens. + +[BM2_J:BIKE2] +On demande à personne de faire le sale boulot pour l'abandonner au trou pendant quinze ans... + +[BM2_K:BIKE2] +Et ouais, je me suis renseigné. + +[BM2_L:BIKE2] +Ce que tu vois, c'est la plus grande famille d'inadaptés, de parias et d'ordures qui existe. + +[BM2_M:BIKE2] +Certains ont même été trahis par leur propre pays! + +[BM2_N:BIKE2] +On m'a foutu en taule pendant la guerre du Vietnam! Saloperie... + +[BM2_O:BIKE2] +Voilà pourquoi je vais te demander d'aller foutre le bordel. + +[BM2_P:BIKE2] +Ce pays de merde a vraiment besoin d'un coup de pied au cul et c'est nous qui allons le lui donner! + +[BM2_Q:BIKE2] +Alors sors de là, attrape une bécane et montre à cette ville à quel point t'es énervé! + +[BM2_R:BIKE2] +D'accord. + +[BM2_2:BIKE2] +~g~Tu dois remplir le chaosmètre dans le temps imparti pour nous prouver que t'es un dur de dur! + +[BM2_3:BIKE2] +~g~Ce bruit veut dire qu'une partie du chaosmètre est remplie, continue comme ça. + +{=================================== MISSION TABLE BIKE3 ===================================} + +[BM3_A:BIKE3] +Salut, Mitch. + +[BM3_B:BIKE3] +Hé! Mais c'est ce gros méchant de Vercetti! + +[BM3_C:BIKE3] +Maintenant, je veux voir à quel point tu peux te battre pour ton secteur. + +[BM3_D:BIKE3] +Un gang local a commis l'erreur de me faucher ma bécane... + +[BM3_E:BIKE3] +Probablement un truc de machos ou quelque chose de ce genre. + +[BM3_F:BIKE3] +Moi et mes gars, on pourrait aller leur donner la leçon qu'ils méritent... + +[BM3_G:BIKE3] +Enfin... + +[BM3_H:BIKE3] +Je me suis dit que ça serait une excellente initiation pour toi. + +[BM3_I:BIKE3] +Tu me ramènes ma bécane et tu pourras dire à Paul qu'on assurera la sécurité. + +[BM3_2:BIKE3] +~r~Tu étais supposé ramener la bécane, pas la bousiller! + +[BM3_3:BIKE3] +~g~Ramène la bécane au bar! + +[BM3_4:BIKE3] +~g~Monte sur la bécane! + +[INTRUDE:BIKE3] +~g~T'as été repéré! + +[BM3_6:BIKE3] +~g~Ils se planquent derrière Ammu-Nation dans le centre. + +[BM3_7:BIKE3] +~g~T'auras besoin d'une bécane rapide pour accéder au toit. + +[BM3_8:BIKE3] +~g~Sers-toi de la bécane pour sauter de ces marches jusqu'à l'autre côté de la route. + +[BM3_1:BIKE3] +~g~Un gang local a volé l'Angel de Mitch Baker. Récupère-la! + +[BM3_9:BIKE3] +~g~Récupère l'Angel de Mitch Baker et dégage de là! + +{=================================== MISSION TABLE BMX_1 ===================================} + +[BMX_REC:BMX_1] +~g~Nouveau record établi : ~1~! + +[GETBIK2:BMX_1] +Tu as ~1~ secondes pour enfourcher une bécane! + +{=================================== MISSION TABLE BOATBUY ===================================} + +[DRUG_1:BOATBUY] +Bonjour? Y'a quelqu'un? Y'A QUELQU'UN??? + +[DRUG_2:BOATBUY] +Ferme-la. Il y a un mec, ici. + +[DRUG_3:BOATBUY] +Hé, mon pote! T'es le nouveau proprio? + +[DRUG_4:BOATBUY] +Ouais. Lequel de ces bateaux est le plus rapide? + +[DRUG_5:BOATBUY] +Il est déjà à l'eau, mon pote, + +[DRUG_6:BOATBUY] +je me suis dit que tu voudrais peut-être l'essayer. + +[DRUG_7:BOATBUY] +Mon pote, il est déjà équipé d'un moteur de 300 chevaux... + +[DRUG_8:BOATBUY] +...et avec la coque en fibre de verre, il touche même pas l'eau! + +[DRUG_9:BOATBUY] +Il monte à 60 noeuds en quatre secondes, mon pote... + +[DRUG_10:BOATBUY] +Et il peut embarquer vingt écopes de la meilleure jamaïquaine dans la coque. + +[DRUG_11:BOATBUY] +Alors vas-y, mon pote, il t'attend pour décoler! + +[DRUG_12:BOATBUY] +Ouais, hé, mon pote, t'as du feu? + +[DRUG_13:BOATBUY] +Mec? + +{=================================== MISSION TABLE CAP_1 ===================================} + +[CAP1_B1:CAP_1] +~g~La mafia taxe tes commerces. Trouve-les et bute-les. + +[CAP1_B2:CAP_1] +~g~La mafia taxe le chantier naval! + +[CAP1_B3:CAP_1] +~g~La mafia taxe l'usine de crème glacée! + +[CAP1_B4:CAP_1] +~g~La mafia taxe la concession automobile! + +[CAP1_B5:CAP_1] +~g~La mafia taxe la compagnie de taxis! + +[CAP_01:CAP_1] +Ok, où est l'urgence? + +[CAP_02:CAP_1] +QUI? + +[CAP_03:CAP_1] +Tommy... Des truands de la mafia... Ils ont dit qu'ils venaient prendre leur part... + +[CAP_04:CAP_1] +... Ils ont dit que c'était le pognon de Mr Forello... J'me sens mal... + +[CAP_05:CAP_1] +Forelli? SONNY Forelli? + +[CAP_06:CAP_1] +Ouais, c'est ça... je crois... Ils ont beaucoup insisté... + +[CAP_07:CAP_1] +T'inquiète pas, je suis pas en colère contre toi. + +[CAP_08:CAP_1] +Emmène-le à l'hosto. + +[CAP_09:CAP_1] +Tommy... Taille un nouveau trou du cul à ce salopard de ma part... + +[CAP_10:CAP_1] +Je vais lui en rajouter deux! + +[CAP1_2:CAP_1] +Tu connais la règle, Vercetti! + +[CAP1_3:CAP_1] +Avec les compliments de M. Forelli! + +[CAP1_4:CAP_1] +C'est le boucher d'Harwood! + +[CAP1_5:CAP_1] +Tu diras à Sonny... de dégager! + +[CAP1_6:CAP_1] +Vice City est à moi, désormais, pas à lui! + +[CAP1_7:CAP_1] +Tu crois que tu peux m'avoir, Vercetti? + +[CAP1_8:CAP_1] +On va continuer à s'amener jusqu'à ce que tu sois raide mort, Vercetti! + +[CAP1_9:CAP_1] +T'as pas une chance, connard psychotique! + +[CAP1_10:CAP_1] +Je vais te descendre, Vercetti! + +[CAP1_11:CAP_1] +T'as toujours été un con! + +[CAP1_12:CAP_1] +Tu vas crever, Vercetti! + +[CAP1_B6:CAP_1] +~g~Tu as trouvé l'encaisseur. Tue-le. + +[CAP1_B7:CAP_1] +~g~Tu as perdu l'encaisseur. + +[CAP1_B8:CAP_1] +~r~L'encaisseur a taxé toutes tes affaires. + +[CAP1_B9:CAP_1] +~g~La mafia a taxé le Malibu! + +[CAP1_B0:CAP_1] +~g~La mafia a taxé le studio de cinéma! + +[CAP1_C2:CAP_1] +~g~La mafia est arrivée au chantier naval! + +[CAP1_C3:CAP_1] +~g~La mafia est arrivée à l'usine de crème glacée! + +[CAP1_C4:CAP_1] +~g~La mafia est arrivée à la concession automobile! + +[CAP1_C5:CAP_1] +~g~La mafia est arrivée à la compagnie de taxis! + +[CAP1_C9:CAP_1] +~g~La mafia est arrivée au Malibu! + +[CAP1_C0:CAP_1] +~g~La mafia est arrivée au studio de cinéma! + +[CAP1_D2:CAP_1] +~g~La mafia quitte le chantier naval! + +[CAP1_D3:CAP_1] +~g~La mafia quitte l'usine de crème glacée! + +[CAP1_D4:CAP_1] +~g~La mafia quitte la concession automobile! + +[CAP1_D5:CAP_1] +~g~La mafia quitte la compagnie de taxis! + +[CAP1_D9:CAP_1] +~g~La mafia quitte le Malibu! + +[CAP1_D0:CAP_1] +~g~La mafia quitte le studio de cinéma! + +[CAP1B10:CAP_1] +Tu t'es occupé des taxeurs. Y'en a d'autres qui arrivent. + +{=================================== MISSION TABLE CARBUY ===================================} + +[CAR1_1:CARBUY] +B.J. Smith. Et vous devez être Mr Vercetti. + +[CAR1_2:CARBUY] +Vous voulez visiter? + +[CAR1_3:CARBUY] +Pourquoi pas? + +[CAR1_4:CARBUY] +Vous savez, je suis bien triste de vendre l'affaire... + +[CAR1_5:CARBUY] +C'était mon premier investissement de professionnel. + +[CAR1_6:CARBUY] +Mais l'heure est venue pour moi de continuer ma route. + +[CAR1_7:CARBUY] +Vous quittez la ville? + +[CAR1_8:CARBUY] +Pas trop pressé, j'espère? + +[CAR1_9:CARBUY] +Non, je sors juste de ma retraite et je prépare mon retour dans les affaires. + +[CAR1_10:CARBUY] +Cette affaire était pas trop solide, + +[CAR1_11:CARBUY] +mais mon personnel a pris sur lui de faire un peu plus + +[CAR1_12:CARBUY] +créatif en ce qui concerne les méthodes pour devenir rentable. + +[CAR1_13:CARBUY] +Evidemment, je pourrais démanteler l'affaire avant de la refiler. + +[CAR1_14:CARBUY] +Merde, je pourrais y foutre le feu si ça me chantait. + +[CAR1_15:CARBUY] +C'est un terrain de premier ordre. + +[CAR1_16:CARBUY] +Oh, je ne m'en ferais pas pour ça. + +[CAR1_17:CARBUY] +Cet endroit me semble idéal. + +[CAR1_18:CARBUY] +Ouais, il l'est, alors, marché conclu? + +{=================================== MISSION TABLE CARPAR1 ===================================} + +[MM_1_A:CARPAR1] +~g~Récupère ~y~5 points de passage~r~ SANS ~g~renverser de ~r~CONES! ~g~Tu peux les récupérer dans ~y~N'IMPORTE QUEL ORDRE. + +[CONE_1:CARPAR1] +~r~Tu as renversé un cône! + +[MM_1_C:CARPAR1] +~y~FRANCHIS~g~ un point de passage pour enclencher le compteur. ~g~Chaque point de passage te crédite ~y~~1~ SECONDES~g~. + +{=================================== MISSION TABLE COPCAR ===================================} + +[CLEVEL:COPCAR] +Missions d'autodéfense niveau ~1~ + +[C_COMP1:COPCAR] +Niveau 12 de mission Auto-défense réussi : votre max de gilet pare-balles passe à 150. + +[KILLS:COPCAR] +VICTIMES : + +[C_BREIF:COPCAR] +~g~Suspect aperçu pour la dernière fois dans le secteur ~a~. + +[C_PASS:COPCAR] +MENACE ENRAYEE : $~1~ + +[COPCART:COPCAR] +~g~Tu as ~1~ secondes pour retourner à une voiture de police avant que la mission ne s'achève. + +[C_CANC:COPCAR] +~r~Mission d'autodéfense annulée! + +[C_TIME:COPCAR] +~r~Ta carrière de flic est terminée! + +{=================================== MISSION TABLE COUNT1 ===================================} + +[CM1_A:COUNT1] +Vercetti? Hé, vous avez acheté la vieille imprimerie? + +[CM1_B:COUNT1] +Ouais, mon vieux a travaillé sur ces machines. + +[CM1_C:COUNT1] +J'aurais voulu marcher sur ses traces, mais... J'ai suivi une autre voie. + +[CM1_D:COUNT1] +Vous avez l'intention de vendre les vieilles machines, et de tout casser? + +[CM1_E:COUNT1] +Je m'disais qu'on pourrait peut-être imprimer quelque chose, un journal ou un magazine... + +[CM1_F:COUNT1] +Arrête tes conneries, fiston! J'ai toujours rêvé d'imprimer mon propre pognon! C'est pas très compliqué! + +[CM1_G:COUNT1] +Tu sais, j'ai fait ça sur une petite échelle pendant des années. + +[CM1_H:COUNT1] +Ah ouais? + +[CM1_I:COUNT1] +Ouais. Mais il nous faut des plaques de bonne qualité. + +[CM1_J:COUNT1] +Evidemment! Il y a un syndicat de contrefaçon qui opère déjà en Floride! + +[CM1_K:COUNT1] +Un syndicat? + +[CM1_L:COUNT1] +Ouais. C'est le bruit qui court en tout cas. + +[CM1_M:COUNT1] +Je connais un mec qui en saura sûrement plus... + +[CM1_N:COUNT1] +On avait l'habitude de passer nos soirées ensemble, à nettoyer les rouleaux... + +[CM1_2A:COUNT1] +Mate-moi ce cul! + +[CM1_2B:COUNT1] +Ok, ma belle, c'est l'heure de l'initiation! + +[CM1_2C:COUNT1] +Salut, vieille branche, comment ça va? + +[CM1_2D:COUNT1] +Que sais-tu à propos de la contrefaçon? + +[CM1_2E:COUNT1] +Oh, je vais bien, Paul, et toi? + +[CM1_2F:COUNT1] +Viens ici! + +[CM1_2G:COUNT1] +Bon! Bon! Bon! Tu es visiblement un homme très occupé! + +[CM1_2H:COUNT1] +Tout ce que je sais, c'est que les Triades fournissent les plaques. + +[CM1_2I:COUNT1] +Elles ont une compagnie de navigation sur les quais + +[CM1_2J:COUNT1] +et le patron devrait savoir quand les plaques arrivent! + +[CM1_2K:COUNT1] +Merci, Paul. + +[CM1_2L:COUNT1] +C'est quoi ton problème, pauvre maniaque! + +[CM1_2M:COUNT1] +Un autre verre, en vitesse! + +[CM1_3:COUNT1] +~g~T'as été repéré! + +[CM1_5:COUNT1] +~g~Va retrouver Kent Paul au Malibu! + +[CNT1_1:COUNT1] +T'es qui toi? Aïe! Pas le visage! Pas le visage! + +[CM1_1:COUNT1] +~g~Va au bateau de la Chartered Libertine Lines sur les docks. + +[CM1_2:COUNT1] +~g~L'officier de navigation détient l'information dont tu as besoin. + +[CNT1_2:COUNT1] +Ok, je parle! Je parle! + +[CM1_6:COUNT1] +~g~Ramène l'info à l'imprimerie! + +{=================================== MISSION TABLE COUNT2 ===================================} + +[CNT2_B1:COUNT2] +Bon, le coursier déplace les plaques des docks aujourd'hui. + +[CNT2_B2:COUNT2] +Faut l'intercepter et récupérer ces plaques, semer les flics éventuels et revenir ici. + +[CNT2_B3:COUNT2] +En fonction de comment ça marche, + +[CNT2_B4:COUNT2] +on aura entre cinq minutes et toute l'année pour imprimer le pognon avant que le syndicat de contrefaçon rapplique. + +[CNT2_B5:COUNT2] +Quoi qu'il en soit, je veux voir les premiers billets sortir de la presse cinq minutes après mon retour! C'est compris? + +[CNT2_B6:COUNT2] +T'inquiète pas, Tommy, on sera prêts. + +[CNT2_B7:COUNT2] +Les gars et moi, on sera dans le coin au cas où t'aurais besoin de renforts. + +[CNT2_B8:COUNT2] +Bon, tout le monde a compris? Parfait. A plus tard. + +[CNT2_01:COUNT2] +~g~Le ~r~coursier~g~ qui porte les plaques va bientôt arriver aux ~y~docks~g~ en hélico. + +[CNT2_02:COUNT2] +~r~Le coursier s'est enfui en hélico! + +[CNT2_03:COUNT2] +~r~Le coursier est parvenu à destination sain et sauf, c'est trop tard! + +[CNT2_04:COUNT2] +~r~Tu as détruit les plaques dans l'explosion! + +[CNT2_05:COUNT2] +~g~Tu as les plaques. Emmène-les à l'imprimerie! + +[CNT2_06:COUNT2] +~g~Le coursier est mort et il a jeté les plaques. Récupère-les en premier. + +[CNT2_07:COUNT2] +~g~Un des gardes a ramassé les plaques, ne le laisse pas s'enfuir! + +[CNT2_08:COUNT2] +~g~Le ~r~coursier~g~ avec les plaques est arrivé aux docks. + +[CNT2_4:COUNT2] +Affaire privée! Dégagez! + +[CNT2_09:COUNT2] +IMPRIMERIE ACQUISE + +[CNT2_10:COUNT2] +~g~L'imprimerie va maintenant générer un revenu maximum de $~1~. Veille à relever régulièrement les compteurs. + +[CNT2_11:COUNT2] +~r~Les plaques sont au fond de la mer! + +{=================================== MISSION TABLE CUBAN1 ===================================} + +[CUB1_A:CUBAN1] +Si, mec? + +[CUB1_B:CUBAN1] +Hé, du calme, papa, ce mec est là pour moi. Hé, c'est toi le mec? + +[CUB1_C:CUBAN1] +Oh ouais. C'est toi le mec. Je crois bien, tu sais. + +[CUB1_D:CUBAN1] +Non, je crois pas. + +[CUB1_E:CUBAN1] +Ah ouais? Amène-toi, gros dur! + +[CUB1_F:CUBAN1] +Tu crois que tu peux jouer avec moi? + +[CUB1_G:CUBAN1] +Tu penses que tu peux jouer au con avec moi? + +[CUB1_H:CUBAN1] +Non, je pense que tu joues assez le con pour nous deux. + +[CUB1_I:CUBAN1] +Hé, il t'a traité de con, fiston. + +[CUB1_J:CUBAN1] +Et moi, je le traite de fillette, papa. + +[CUB1_K:CUBAN1] +Regarde-le, tout habillé comme ça. + +[CUB1_L:CUBAN1] +C'est la soirée des pouffiasses, ce soir? + +[CUB1_M:CUBAN1] +Toi qu'est plutôt balaise, pourquoi tu te fringues en nana? + +[CUB1_N:CUBAN1] +Et t'as une petite culotte comme les gonzesses? + +[CUB1_O:CUBAN1] +Qu'est-ce que t'as contre les femmes? Tu préfères les mecs, mon gros? + +[CUB1_P:CUBAN1] +J'aime les femmes! J'aime toutes les femmes! J'aime ma maman, Chico! + +[CUB1_Q:CUBAN1] +Ok, ok, j'te crois sur parole. + +[CUB1_R:CUBAN1] +Tu sais conduire, amigo? + +[CUB1_S:CUBAN1] +Ouais... Comme une gonzesse. + +[CUB1_T:CUBAN1] +Très drôle... Tu me plais, ma grosse, peut-être que tu peux aider. + +[CUB1_U:CUBAN1] +Tu peux peut-être nous montrer que t'es un vrai mec? + +[CUB1_V:CUBAN1] +Sors le bateau. + +[CUB1_W:CUBAN1] +Prouve-moi que t'as des couilles énormes + +[CUB1_X:CUBAN1] +et pas une paire de pois chiches! + +[CUB1_02:CUBAN1] +Ok, mec, traite-la comme une gonzesse. + +[CUB1_03:CUBAN1] +Pas mal, t'es un vrai macho. + +[CUB1_04:CUBAN1] +Merde, t'en a vraiment dans le pantalon, amigo. + +[CUB1_05:CUBAN1] +Amigo, t'es un vrai mec, mec. + +[CUB1_06:CUBAN1] +Tu te prends pour un homme, mec? + +[CUB1_07:CUBAN1] +T'es qu'une petite poule mouillée, petit, c'est ta mère qui te manque? + +[CUB1_08:CUBAN1] +Tu gâches tout! Tu marches comme un mec, tu causes comme un mec, mais tu conduis comme une gonzesse! + +[CUB1_09:CUBAN1] +Mec, t'es un vrai mec de chez mec, mec! Putain, je t'adore, mec! + +[CUB1_10:CUBAN1] +C'est quand tu veux, mec. T'as des couilles de taureau et tous mes potes ont des couilles énormes! + +[CUB1_11:CUBAN1] +~r~T'as tué Rico! + +[CUB1_12:CUBAN1] +Va jusqu'au premier point de contrôle pour commencer le test. + +[CUB1_14:CUBAN1] +Retourne dans le bateau! + +[CUB1_15:CUBAN1] +~r~T'es trop lent, mec! + +[CUB1_01:CUBAN1] +Salut, moi c'est Rico. C'est toi qu'a des couilles de taureau? + +[CUB1_13:CUBAN1] +~g~Tu as trois minutes pour terminer le parcours. + +{=================================== MISSION TABLE CUBAN2 ===================================} + +[CUB2_25:CUBAN2] +BUTE TOUS LES HAITIENS! + +[CUB2_A:CUBAN2] +Un cafecito, por favor, Alberto... + +[CUB2_N:CUBAN2] +Tout de suite, Tommy. + +[CUB2_B:CUBAN2] +Poppa! Un gran problema! + +[CUB2_O:CUBAN2] +Umberto, mon fils, que s'est-il passé? + +[CUB2_C:CUBAN2] +Les Haïtiens! Je hais les Haïtiens! + +[CUB2_D:CUBAN2] +C'est la dernière fois qu'ils me font chier! + +[CUB2_E:CUBAN2] +Ces Haïtiens... On va les liquider! + +[CUB2_F:CUBAN2] +On a juste besoin de renforts... + +[CUB2_G:CUBAN2] +J'ai déjà perdu pas mal de frères là-bas. + +[CUB2_H:CUBAN2] +Amigo, tu conduis pas mal! + +[CUB2_I:CUBAN2] +Pour une gonzesse... Pas vrai? + +[CUB2_J:CUBAN2] +C'est pas l'heure des vannes! + +[CUB2_K:CUBAN2] +Allez, conduis encore pour moi! + +[CUB2_L:CUBAN2] +Récupère mes gars là-bas et puis on s'occupe de ces Haïtiens! + +[CUB2_M:CUBAN2] +Ils m'ont cherché et ils sont tombés sur le mec le plus balaise de la ville! + +[CUB2_01:CUBAN2] +C'est trop petit, mec, il te faut une plus grosse bagnole. + +[CUB2_02:CUBAN2] +On a besoin de renforts du café! + +[CUB2_03:CUBAN2] +~g~Prend une bagnole et va ramasser les Cubains à l'extérieur du café de Robina. + +[CUB2_04:CUBAN2] +~g~Amène les Cubains à l'endroit de la bagarre. + +[CUB2_05:CUBAN2] +Liquide ce lâche de tireur embusqué! + +[CUB2_07:CUBAN2] +Ils se battent comme des filles! A couvert! + +[CUB2_09:CUBAN2] +Un sniper sur le toit! + +[CUB2_11:CUBAN2] +~r~Imbécile, on avait besoin de cette bagnole! + +[CUB2_12:CUBAN2] +Hé, amigo! Content de te voir que t'as réussi! + +[CUB2_13:CUBAN2] +Putains de Haïtiens, on va tous les descendre! + +[CUB2_14:CUBAN2] +CHAAAAAARGEZ! + +[CUB2_15:CUBAN2] +Maintenant, mes frères! Chaaaargez! + +[CUB2_16:CUBAN2] +Tommy, on a fait la preuve de notre courage! + +[CUB2_17:CUBAN2] +Piquons cette camionnette bourrée de drogue et tirons-nous! + +[CUB2_18:CUBAN2] +~g~Trouve une bagnole et va chercher les Cubains. + +[CUB2_19:CUBAN2] +On va se battre comme des hommes! + +[CUB2_21:CUBAN2] +Battez-vous avec vos couilles! + +[CUB2_22:CUBAN2] +~g~Elimine les derniers Haïtiens pour que les Cubains puissent avancer. + +[CUB2_23:CUBAN2] +~g~Little Haiti va grouiller de Haïtiens voulant régler leurs comptes avec les Cubains. Fais gaffe à tes couilles. + +[CUB2_24:CUBAN2] +~g~Retourne au café de Robina avec la camionnette et gare-la derrière. + +{=================================== MISSION TABLE CUBAN3 ===================================} + +[CUB3_A:CUBAN3] +Alberto. Un café, senor. + +[CUB3_B:CUBAN3] +Papa, ne sers pas ce morbac... + +[CUB3_C:CUBAN3] +T'es un hypocrite, Tommy! + +[CUB3_D:CUBAN3] +T'es soit un hypocrite, soit une gonzesse! + +[CUB3_E:CUBAN3] +Les Haïtiens, mec! Ils se foutent de ma gueule! + +[CUB3_F:CUBAN3] +Du calme. C'est quoi ton problème? + +[CUB3_G:CUBAN3] +Ils se foutent ma gueule, Tommy, de ma gueule! + +[CUB3_H:CUBAN3] +Umberto Robina! Ils font comme ça leur chante! + +[CUB3_I:CUBAN3] +Personne fait ce qu'il veut, Umberto, ils font ce que tu les laisses faire. + +[CUB3_J:CUBAN3] +Quoi? + +[CUB3_K:CUBAN3] +Tu veux que quelqu'un s'en occupe? + +[CUB3_L:CUBAN3] +Je peux m'en charger, mais ça va te coûter cher! + +[CUB3_M:CUBAN3] +Je sais qu'on est frères et tout et tout, mais ça, c'est les affaires... + +[CUB3_N:CUBAN3] +Tommy, t'es un vrai mec! Un businessman! Un gentleman! + +[CUB3_O:CUBAN3] +Ces Haïtiens. Ils ont de la marchandise de première qualité qui arrive par la mer. + +[CUB3_P:CUBAN3] +On la prend et on en finit avec eux. + +[CUB3_Q:CUBAN3] +Tu t'en charges et je veillerai sur toi. Comme mon frère. Comme mon fils. + +[CUB3_R:CUBAN3] +J'préfère que tu me files du pognon, plutôt que de me faire sauter sur tes genoux, amigo... + +[CUB3_01:CUBAN3] +Salut, Rico. Chouette bateau. T'es prêt? + +[CUB3_03:CUBAN3] +~g~Récupère toutes les serviettes pleines de drogue et de cash. + +[CUB3_04:CUBAN3] +~g~Ramène la drogue et le cash à Umberto. + +[CUB3_05:CUBAN3] +Si, Tommy, faut que tu tires bien, aujourd'hui. + +[CUB3_06:CUBAN3] +Mon bateau, je voudrais pas qu'il se retrouve plein de trous, ok? + +[CUB3_07:CUBAN3] +~g~Va retrouver Rico. Il te conduira au rendez-vous. + +[CUB3_02:CUBAN3] +~g~TUE TOUS LES HAITIENS EN BATEAU! + +[CUB3_08:CUBAN3] +Oh oh... Les Cubains. On nous attaque! + +{=================================== MISSION TABLE CUBAN4 ===================================} + +[CUB4_A:CUBAN4] +Salut, les filles, vous savez ce que je vais faire? + +[CUB4_B:CUBAN4] +Je vais aller me descendre un Haïtien. Et après, vous savez quoi? + +[CUB4_C:CUBAN4] +Après, je vais me vider les couilles comme un homme! + +[CUB4_D:CUBAN4] +T'as déjà vu ça chica? Un peu comme ça... + +[CUB4_E:CUBAN4] +Pauv' mec! + +[CUB4_F:CUBAN4] +Connard! + +[CUB4_G:CUBAN4] +Hé, ma poule, je te toucherai même pas avec une perche! + +[CUB4_H:CUBAN4] +Umberto Robina, il aime les femmes! Pas les vieilles dindes en jupe! + +[CUB4_I:CUBAN4] +Tommy! Tommy, je t'aime, je t'aime! Allons-y! + +[CUB4_J:CUBAN4] +Où ça? Je peux pas avoir un café d'abord? + +[CUB4_K:CUBAN4] +Pas le temps pour un café! D'ailleurs, je viens d'en prendre un! + +[CUB4_L:CUBAN4] +Faut qu'on liquide les Haïtiens! + +[CUB4_M:CUBAN4] +Tommy, tu sais comment on tue un serpent? + +[CUB4_N:CUBAN4] +Tu le mords au cul! Ahahaha! + +[CUB4_O:CUBAN4] +Si tu le dis, Umberto. + +[CUB4_P:CUBAN4] +Tommy, va nous trouver une petite bagnole haïtienne! + +[CUB4_Q:CUBAN4] +Dès que tu l'as, reviens récupérer mon petit + +[CUB4_R:CUBAN4] +Pepe et emmène-le chez les Haïtiens. + +[CUB4_S:CUBAN4] +Ensuite, tu vas au centre de retraitement des Haïtiens et tu te sers de leurs solvants comme explosif. + +[CUB4_T:CUBAN4] +Boom! Et bye bye! + +[CUB4_U:CUBAN4] +Et toi, Umberto? + +[CUB4_V:CUBAN4] +Oh, je reste en arrière, je surveille le café avec papa. + +[CUB4_W:CUBAN4] +Il ne se sent pas très bien, tu sais. + +[CUB4_02:CUBAN4] +~g~Les bombes ont un retardateur de 45 secondes. + +[CUB4_04:CUBAN4] +~r~Tu as mis leur base en alerte! Plus question d'entrer, maintenant! + +[CUB4_07:CUBAN4] +Les solvants sont à l'arrière, amigo. + +[CUB4_08:CUBAN4] +Hola, amigos. + +[CUB4_09:CUBAN4] +Bueno. Putains d'Haïtiens. Muerte. + +[CUB4_10:CUBAN4] +Vamos. + +[CUB4_11:CUBAN4] +Si, vamos. + +[CUB4_12:CUBAN4] +Hé, on a besoin d'une bagnole haïtienne! + +[CUB4_13:CUBAN4] +Bueno, allons trouver ces muchachos! + +[CUB4_14:CUBAN4] +Suis mes amigos. + +[CUB4_15:CUBAN4] +Ok, tu entres... + +[CUB4_16:CUBAN4] +Je vais poser la bombe, couvrez-moi! + +[CUB4_17:CUBAN4] +COURS! + +[CUB4_18:CUBAN4] +Mec, c'est un quartier cool... + +[CUB4_19:CUBAN4] +Cet endroit est une décharge, mec. + +[CUB4_20:CUBAN4] +J'avais une belle copine... Elle vivait dans le coin. + +[CUB4_21:CUBAN4] +Tu sais, ils font de super pizzas, ici! + +[CUB4_22:CUBAN4] +Hé, mec, tu conduis comme une putain de gonzesse! + +[CUB4_23:CUBAN4] +T'es perdu, mec? + +[CUB4_24:CUBAN4] +T'as laissé Pepe derrière, va le récupérer! + +[CUB4_03:CUBAN4] +~g~Ne sors pas de la bagnole avant qu'elle soit garée en sécurité dans l'enceinte. + +[CUB4_26:CUBAN4] +~g~Emmène Pepe, file au nord dans Little Haïti et fauche une bagnole Vaudou. + +[CUB4_27:CUBAN4] +~g~Va retrouver Rico et les autres Cubains. + +[CUB4_28:CUBAN4] +~g~Va rejoindre les Cubains dans l'usine de dope haïtienne. + +[CUB4_29:CUBAN4] +~g~Marche sur chacun des marqueurs pour y poser une bombe. + +[CUB4_30:CUBAN4] +~g~Une fois les trois bombes posées, sors de l'usine avant qu'elle n'explose. + +[CUB4_31:CUBAN4] +~g~Sors de l'usine! + +[CUB4_32:CUBAN4] +~g~Gare la bagnole à l'endroit du bip et descends. + +[CUB4_06:CUBAN4] +~r~Tu ne t'es pas assez éloigné de la base et on a dû annuler l'explosion! + +{=================================== MISSION TABLE FINALE ===================================} + +[FIN_1A:FINALE] +Amène-toi, putain de salopard de traître! + +[FIN_1B:FINALE] +Tu vas crever, Judas de mes deux! + +[FIN_1C:FINALE] +C'est la dernière danse de Lance Vance! + +[FIN_2B:FINALE] +Ah ouais, c'est ce que tu crois? + +[FIN_2C:FINALE] +J'ai dit que je l'avais assez entendu celle-là à l'école! + +[FIN_3:FINALE] +Plus personne pour couvrir ton cul, hein, Tommy? + +[FIN_4:FINALE] +T'es plus que du passé, Tommy, du passé! + +[FIN_5:FINALE] +T'as choisi le mauvais camp, Lance... + +[FIN_11A:FINALE] +Tu m'as volé quinze ans de ma vie, Sonny... + +[FIN_11B:FINALE] +Et je viens te présenter l'addition! + +[FIN_12A:FINALE] +T'as encore rien compris! + +[FIN_12B:FINALE] +Tu m'appartiens, Tommy... + +[FIN_12C:FINALE] +C'est moi qui devait les faire ces quinze années! + +[FIN_13:FINALE] +Attrapez-le, les mecs, il a jamais rien pigé. + +[FIN1_01:FINALE] +Qu'est-ce qui se passe? + +[FIN1_02:FINALE] +Tommy! Ah, bien, bien. Ecoute, écoute... Euh, écoute. + +[FIN1_03:FINALE] +J'aime bien les poissons. Ouais, j'adore le poisson! + +[FIN1_04:FINALE] +Je l'aime à toutes les sauces, dans le bocal ou dans l'assiette, + +[FIN1_05:FINALE] +mais c'est pas parce que je l'aime que je veux dormir avec! + +[FIN1_06:FINALE] +Et maintenant, il y a tes cousins ritals qui s'amènent pour me coller les pieds dans le béton, et je... + +[FIN1_07:FINALE] +La ferme, Ken. Assis. + +[FIN1_08:FINALE] +Lance, bordel, qu'est-ce qui se passe? + +[FIN1_09:FINALE] +C'est tes amis du nord, Tommy... Ils ont pas aimé ce que t'as fait à leur mec. + +[FIN1_10:FINALE] +Ils s'amènent aujourd'hui pour régler cette affaire. + +[FIN1_11:FINALE] +Ils ont mis plus de temps que je pensais... + +[FIN1_12:FINALE] +Les mecs, faut en finir et faire comprendre à tout le monde que désormais, c'est mon business! LE MIEN! + +[FIN1_13:FINALE] +Ken, prend la première production de faux dollars et mets-en pour vingt millions dans des porte-documents. + +[FIN1_14:FINALE] +Lance, tu rassembles tout le monde... + +[FIN2_01:FINALE] +Tommy! + +[FIN2_02:FINALE] +Quoi? T'embrasses pas ton vieux pote? + +[FIN2_03:FINALE] +Ca fait quinze piges que je suis hors-circuit + +[FIN2_04:FINALE] +et je suis un peu rouillé question étiquette. + +[FIN2_05:FINALE] +Toujours la haine, hein, Tommy! + +[FIN2_06:FINALE] +Je te l'avais pas dit que ton sale caractère te causerait des emmerdes? + +[FIN2_07:FINALE] +Il y a vingt millions là-dedans... + +[FIN2_08:FINALE] +Combien ils étaient déjà? Dix, non onze... + +[FIN2_09:FINALE] +C'est pour ça qu'on t'a surnommé le boucher d'Hartwood! Hé hé hé! + +[FIN2_10:FINALE] +Tu m'avais envoyé buter un mec, un seul mec. Mais ils savaient que je venais, Sonny... + +[FIN2_11:FINALE] +Fais gaffe à ce que tu dis... + +[FIN2_12:FINALE] +Quelqu'un pourrait penser que tu veux me coller sur le dos un malheureux concours de circonstances... + +[FIN2_13:FINALE] +Prends simplement ce pognon... + +[FIN2_14:FINALE] +Prendre le putain de fric? + +[FIN2_15:FINALE] +Tu sais, Tommy, j'ai fait ce que je pouvais pour toi, j'ai tiré des ficelles et demandé des services. + +[FIN2_16:FINALE] +J'étais ton pote, Tommy. J'espérais que tu entendrais raison et comprendrais ce qui est bon pour les affaires. + +[FIN2_17:FINALE] +Je t'ai fait confiance, Tommy et je suis déçu... + +[FIN2_18:FINALE] +Mais il y a au moins un mec dans ton organisation de merde qui s'y connaît en business, + +[FIN2_19:FINALE] +Pas vrai, Lance? + +[FIN2_20:FINALE] +Désolé, Tommy, On est à Vice City. C'est les affaires... + +[FIN2_21:FINALE] +Tu nous as vendus... + +[FIN2_22:FINALE] +Non, juste toi, Tommy, juste TOI. + +[FIN2_23:FINALE] +Le vrai pognon est en haut dans le coffre! + +[FIN2_24:FINALE] +Alors, Tommy, c'était quoi le grand plan? + +[FIN2_25:FINALE] +Tu croyais que j'allais simplement prendre les faux biftons? + +[FIN2_26:FINALE] +Sauver la face et m'enfuir la queue entre les jambes? + +[FIN2_27:FINALE] +Non. + +[FIN2_28:FINALE] +Je voulais juste te faire chier avant de te buter. + +[FIN3_01:FINALE] +Tommy? + +[FIN3_02:FINALE] +Oh, mon Dieu, Tommy! Qu'est-ce qui s'est passé? + +[FIN3_03:FINALE] +A ton avis? + +[FIN3_04:FINALE] +On dirait que t'as niqué ton costard! + +[FIN3_05:FINALE] +Sans compter qu'il était chouette, Tommy! Mais qu'est-ce qui s'est passé? + +[FIN3_06:FINALE] +J'ai eu un désaccord avec un partenaire commercial, tu sais comment c'est. + +[FIN3_07:FINALE] +Tommy, moi, quand j'ai un désaccord avec un partenaire commercial, je lui envoie une lettre enflammée... + +[FIN3_08:FINALE] +A la rigueur, je pisse dans sa boite aux lettres... Mais je commence pas la troisième guerre mondiale! + +[FIN3_09:FINALE] +Tu sais, tu devrais peut-être voir mon psy... + +[FIN3_10:FINALE] +Ce sale con de Lance... + +[FIN3_11:FINALE] +Tommy. J'ai jamais pu blairer ce mec, tu sais? + +[FIN3_12:FINALE] +C'est un névrosé dangereux et égocentrique. Un vrai salopard! + +[FIN3_13:FINALE] +Je suis bien content que tu l'aies liquidé! + +[FIN3_14:FINALE] +Je crois pas qu'on doive s'attendre à plus d'emmerdes en provenance du nord... + +[FIN3_15:FINALE] +... Parce qu'il y a plus de nord... + +[FIN3_16:FINALE] +Maintenant, il n'y a plus que le sud... + +[FIN3_17:FINALE] +Attends, est-ce que ça veut dire ce que je crois que ça veut dire, Tommy? + +[FIN3_18:FINALE] +Qu'est-ce que tu crois que ça veut dire? + +[FIN3_19:FINALE] +Qu'on est au commandement... Enfin, que tu es au commandement. Oh, Tommy... + +[FIN3_20:FINALE] +Tu sais, Ken, c'est peut-être le début d'une grande amitié en affaires... + +[FIN3_21:FINALE] +Après tout, t'es un voleur minable un peu magouilleur sur les bords... + +[FIN3_22:FINALE] +Et moi je suis un tueur psychotique doublé d'un dealer... + +[FIN3_23:FINALE] +Ouais. Si c'est pas grandiose! + +[FIN_B1:FINALE] +~g~Va tuer ~y~Vance~g~ le traître. + +[FIN_B2:FINALE] +~g~Trouve ~p~Sonni~g~ et règle-lui son compte pour de bon. + +[FIN_B3:FINALE] +~g~La mafia essaye de voler ton pognon. Défends le coffre. + +[FIN_B4:FINALE] +~g~Tu es presque mort. Va chercher ~w~de quoi te soigner~g~ en bas. + +[FIN_B5:FINALE] +~g~La mafia te vole ton pognon. Défends le ~c~coffre. + +[FIN_B7:FINALE] +~r~La mafia t'a volé tout ton pognon. + +[DEFSAFE:FINALE] +~g~Retourne au coffre et protège-le. + +{=================================== MISSION TABLE FIRETRK ===================================} + +[F_PASS1:FIRETRK] +Feu éteint! + +[F_FAIL2:FIRETRK] +~r~T'arrives trop tard! + +[F_CANC:FIRETRK] +~r~Mission pompier annulée! + +[F_EXTIN:FIRETRK] +INCENDIES : + +[F_START:FIRETRK] +~g~Véhicule en feu repéré dans le secteur ~a~. Va éteindre le feu. + +[SIREN_1:FIRETRK] +Pour déclencher la sirène de ce véhicule, appuie brièvement sur la ~h~~k~~VEHICLE_HORN~~w~. + +[SIREN_2:FIRETRK] +Pour déclencher la sirène de ce véhicule, appuie brièvement sur la ~h~~k~~VEHICLE_HORN~~w~. + +[FIREPRO:FIRETRK] +Mission Camion de pompiers niveau 12 réussie. T'es complètement ignifugé maintenant! + +[F_FAIL1:FIRETRK] +Mission Camion de pompiers achevée. + +[F_STAR1:FIRETRK] +~g~Véhicules en flammes dans la zone ~a~. Va éteindre l'incendie. + +[SPRAY_4:FIRETRK] { reVC update } +Utilise la touche ~h~~k~~VEHICLE_FIREWEAPON~ ~w~pour tirer avec le canon à eau et le ~h~~k~~VEHICLE_TURRETLEFT~~w~ et ~h~~k~~VEHICLE_TURRETRIGHT~~w~ pour viser. + +[SPRAY_1:FIRETRK] { reVC update } +Utilise la touche ~h~~k~~VEHICLE_FIREWEAPON~ ~w~pour tirer avec le canon à eau et le ~h~~k~~VEHICLE_TURRETLEFT~~w~ et ~h~~k~~VEHICLE_TURRETRIGHT~~w~ pour viser. + +{=================================== MISSION TABLE GENERA1 ===================================} + +[GEN1_A:GENERA1] +Mr. Vercetti! + +[GEN1_B:GENERA1] +Colonel. + +[GEN1_D:GENERA1] +Non merci. + +[GEN1_E:GENERA1] +Ca me gêne beaucoup d'admettre que la cause d'un de nos problèmes communs semble être un homme en qui j'avais confiance. + +[GEN1_F:GENERA1] +J'ai supporté Gonzalez pendant des années, mais son incompétence a atteint de nouveaux sommets. + +[GEN1_G:GENERA1] +Vous avez le droit de tuer Gonzalez... + +[GEN1_H:GENERA1] +C'est lui qui m'a entubé? La seule chose qui m'intéresse, c'est le pognon! + +[GEN1_I:GENERA1] +Je vous récompenserai pour ce service et nous chercherons ensuite votre argent ensemble. + +[GEN1_J:GENERA1] +Il sera dans son appartement, probablement à moitié saoul. Servez-vous de ça... + +[GEN1_06:GENERA1] +Hé! Il a une tronçonneuse! + +[GEN1_07:GENERA1] +T'approche pas de moi, connard! + +[GEN1_08:GENERA1] +Oh, mon Dieu, j'ai gâché ma vie et j'suis devenu un laidron! + +[GEN1_10:GENERA1] +Je vais t'faire fermer ta grande gueule une bonne fois pour toutes. + +[GEN1_11:GENERA1] +Essaye pas de t'enfuir, bâtard! + +[GEN1_12:GENERA1] +Laisse-toi faire et ça sera rapide! + +[GEN1_13:GENERA1] +Arrête de gueuler, ça intéresse personne! + +[GEN1_C:GENERA1] +Merci d'être venu. Asseyez-vous. Du homard? + +[GEN1_05:GENERA1] +~g~Va tuer Gonzales! + +[GEN1_09:GENERA1] +Je t'offre le double, Tommy, LE DOUBLE! + +[GEN1_18:GENERA1] +~r~Gonzalez a réussi à atteindre le poste de police vivant! + +[GEN1_19:GENERA1] +~g~Les flics de Vice City te cherchent! + +[GEN1_20:GENERA1] +~g~Monte dans une bagnole. + +[GEN1_21:GENERA1] +~g~Va au~h~ Pay 'N' Spray~g~ à~h~ Vice Point~g~. + +[GEN1_22:GENERA1] +~g~Conduis ton véhicule à l'intérieur de l'atelier de peinture pour perdre ton ~h~indice de recherche~g~, ~h~réparer ~g~et ~h~repeindre ~g~ton véhicule. Coût : ~h~$100~g~. Cette coup-ci, c'est gratuit. + +[GEN1_01:GENERA1] +Quand tu cours, maintiens la~h~ ~k~~PED_FIREWEAPON~~w~ enfoncée pour préparer une attaque au corps à corps. + +[GEN1_02:GENERA1] +Quand tu cours, maintiens la~h~ ~k~~PED_FIREWEAPON~~w~ enfoncée pour préparer une attaque au corps à corps. + +[GEN1_03:GENERA1] +Quand tu cours, maintiens la~h~ ~k~~PED_FIREWEAPON~~w~ enfoncée pour préparer une attaque au corps à corps. + +[GEN1_14:GENERA1] +Relâche la~h~ ~k~~PED_FIREWEAPON~~w~ pour attaquer. + +[GEN1_15:GENERA1] +Relâche la~h~ ~k~~PED_FIREWEAPON~~w~ pour attaquer. + +[GEN1_16:GENERA1] +Relâche la~h~ ~k~~PED_FIREWEAPON~~w~ pour attaquer. + +[GEN1_23:GENERA1] +~g~Franchis à nouveau les portes pour revenir au rez-de-chaussée. + +{=================================== MISSION TABLE GENERA2 ===================================} + +[COL2_A:GENERA2] +Tommy! Venez donc vous joindre à moi! + +[COL2_B:GENERA2] +Ca a l'air bon, hein! Un peu de museau de tapir? + +[COL2_C:GENERA2] +Euh... Non. Non, merci. + +[COL2_D:GENERA2] +Tommy, vous êtes comme le vent de la pampa qui m'a purifié de la puanteur de la corruption. + +[COL2_E:GENERA2] +Je dois cependant montrer que je pleure sa mort et continuer les affaires comme d'habitude. + +[COL2_F:GENERA2] +Ca me rapproche pas beaucoup de mon pognon... + +[COL2_G:GENERA2] +Tommy, mon cher, vous n'êtes pas à Liberty. Ici, ça se passe autrement. + +[COL2_H:GENERA2] +Je vais continuer mon enquête, mais en attendant, j'ai une bonne affaire à conclure. + +[COL2_I:GENERA2] +Un service à rendre à un ami, Cortez. + +[COL2_J:GENERA2] +Vous êtes un ami, Tommy. Je savais que vous ne me laisseriez pas tomber. + +[COL2_K:GENERA2] +Il faut que vous rencontriez un coursier qui m'a obtenu une technologie intéressante... + +[COL2_1:GENERA2] +Ah, la pluie est très dense en cette saison... + +[COL2_2:GENERA2] +Quoi? + +[COL2_3:GENERA2] +Ah, comment? + +[COL2_4:GENERA2] +Ecoute, Cortez m'envoie. File-moi ces putain de processeurs. + +[COL2_5:GENERA2] +Oh... D'accord. + +[COL2_B1:GENERA2] +~g~Retrouve le coursier au centre commercial. + +[COL2_B2:GENERA2] +~g~Le coursier se barre avec les processeurs de guidage. Ne le laisse pas filer! + +[COL2_B3:GENERA2] +~g~Ramène les processeurs de guidage au Colonel. + +[COL2_F1:GENERA2] +~r~T'as buté le contact! + +[COL2_F2:GENERA2] +~r~Le coursier est mort. Récupère les processeurs. + +[COL2_6A:GENERA2] +Arrête, espèce de porc impérialiste d'américain! Ceci est la propriété du gouvernement français! Rendez-le! + +[BLIPHLP:GENERA2] +Le bip sur le radar est un triangle pointant vers le haut, ce qui signifie que la cible est plus élevée que toi. + +[COL2_F3:GENERA2] +~r~Les processeurs de guidage sont au fond de la mer. + +[COL2_F4:GENERA2] +~r~Le coursier s'est fait la malle! Tu n'as pas récupéré les processeurs de guidage! + +{=================================== MISSION TABLE GENERA3 ===================================} + +[GEN3_49:GENERA3] +Santé de Lance: + +[GEN3_A:GENERA3] +Merci d'être venu, Thomas. + +[GEN3_B:GENERA3] +Pardonnez-moi d'en venir directement au fait... + +[GEN3_C:GENERA3] +Diaz m'a demandé de superviser une petite transaction... + +[GEN3_D:GENERA3] +Espérons que ça se passera mieux que l'autre fois, n'est-ce pas? + +[GEN3_E:GENERA3] +C'est pour ça que j'ai pensé à vous, amigo. + +[GEN3_F:GENERA3] +J'ai déposé une arme dans le parking à niveaux. + +[GEN3_G:GENERA3] +Récupèrez-le et allez surveiller les hommes de Diaz au rendez-vous. + +[GEN3_H:GENERA3] +Gracias, amigo... + +[GEN3_1:GENERA3] +On s'accapare toute l'action, à ce que je vois... + +[GEN3_2:GENERA3] +Ecoute, tu veux pas faire autre chose que de me suivre partout comme mon ombre? Pourquoi tu ne viens pas pour me montrer à quoi t'es bon? + +[GEN3_3:GENERA3] +Pourquoi pas... Au fait, moi c'est Lance. + +[GEN3_5:GENERA3] +Tu dois être le nouveau porte-flingues de Cortez. + +[GEN3_6:GENERA3] +Jusqu'à ce que de meilleures opportunités se présentent... + +[GEN3_7:GENERA3] +Ils seront là d'une minute à l'autre. On ferait mieux de se trouver une bonne planque... + +[GEN3_8:GENERA3] +OK! Je vais sur le balcon et toi tu t'mets sur le toit de l'autre côté de la cour. + +[GEN3_9:GENERA3] +J'suis vivant! Têtes de noeuds! Et ça c'est grâce à toi! C'est quoi ton nom? + +[GEN3_10:GENERA3] +Tommy. + +[GEN3_11:GENERA3] +On se reverra bientôt, je pense! + +[GEN3_12:GENERA3] +Bon, où est passé Lance? Merde... + +[GEN3_14:GENERA3] +Tommy! J'ai besoin d'un coup de main! + +[GEN3_15:GENERA3] +T'en fais pas, je te couvre! + +[GEN3_16:GENERA3] +Les hommes de Diaz se font massacrer! + +[GEN3_19:GENERA3] +~g~Les haïtiens! Ils veulent foutre le deal en l'air! Protège Diaz! + +[GEN3_20:GENERA3] +~g~Va au parking à niveaux où tu peux récupérer le flingue que le Colonel a laissé pour toi. + +[GEN3_22:GENERA3] +Santé de Diaz : + +[GEN3_23:GENERA3] +~g~T'as laissé Lance derrière! Va le chercher! + +[GEN3_25:GENERA3] +~r~Lance est mort! + +[GEN3_28:GENERA3] +~g~Ramène la serviette à Diaz. + +[GEN3_29:GENERA3] +~g~Récupère la serviette et ramène-la à Diaz. + +[GEN3_30:GENERA3] +~r~Il s'est barré avec le pognon! Diaz va te couper les couilles! + +[GEN3_33:GENERA3] +~r~Tu es supposé surveiller Diaz et ses hommes, pas les flinguer! + +[GEN3_34:GENERA3] +~r~Ils ne vont pas pouvoir faire affaire si tu descends les cubains! + +[GEN3_35:GENERA3] +~g~Il a volé le pognon de Diaz! + +[GEN3_36:GENERA3] +~g~Saute sur la moto, rattrape-le et récupère le pognon de Diaz! + +[GEN3_37:GENERA3] +~g~Voilà les Cubains. Surveille le deal en veillant à la sécurité de Diaz et de Lance. + +[GEN3_38:GENERA3] +~r~Diaz est mort! Tu devais le protéger! + +[GEN3_39:GENERA3] +~g~Prends position en haut des escaliers. + +[GEN3_44:GENERA3] +~g~Va avec Lance au rendez-vous et protège Diaz. + +[GEN3_40:GENERA3] { reVC update } +Pour ~h~tirer droit devant ~w~lorsque tu es en ~h~moto~w~, appuie sur la ~h~~k~~VEHICLE_FIREWEAPON~. + +[GEN3_41:GENERA3] { reVC update } +Pour ~h~tirer droit devant ~w~lorsque tu es en ~h~moto~w~, appuie sur la ~h~~k~~VEHICLE_FIREWEAPON~. + +[GEN3_46:GENERA3] +Chiiiier! + +[GEN3_47:GENERA3] +Tommy! + +[GEN3_48:GENERA3] +Merde! + +[GEN3_50:GENERA3] +~r~Tu as paumé le fric de Diaz! La prochaine fois, essaie de ne pas cramer le blé! + +[GEN3_51:GENERA3] +Encore des Haïtiens dans un camion merdique! + +[GEN3_54:GENERA3] +s Restez pas plantés là, bande d'abrutis! Butez-moi ce Haïtien de merde! + +[GEN3_55:GENERA3] +Tommy! Je reste là et je m'occupe de Diaz! + +[GEN3_18:GENERA3] +~g~Voilà les Cubains. Reste près de Diaz. Surveille les opérations tout en t'assurant que Diaz et Lance sont en sécurité. + +[GEN3_56:GENERA3] +~r~Diaz a été pris dans une embuscade et s'est fait tuer! La prochaine fois, ne le quitte pas des yeux! + +[GEN3_57:GENERA3] +Le Kruger est un fusil d'assaut permettant de viser manuellement à la 1ere personne. + +[GEN3_58:GENERA3] +Appuie sur la touche~h~ R1~w~ et maintiens-la enfoncée pour ~h~viser~w~ avec un fusil d'assaut. + +[GEN3_59:GENERA3] +Appuie sur la touche~h~ L1~w~ et maintiens-la enfoncée pour ~h~viser~w~ avec un fusil d'assaut. + +[GEN3_60:GENERA3] +Appuie sur la touche ~h~~k~~PED_FIREWEAPON~~w~ pour ~h~tirer~w~ avec un fusil d'assaut. + +[GEN3_61:GENERA3] +Appuie sur la touche ~h~~k~~PED_FIREWEAPON~~w~ pour ~h~tirer~w~ avec un fusil d'assaut. + +[GEN3_62:GENERA3] +Appuie sur la touche ~h~~k~~PED_FIREWEAPON~~w~ pour ~h~tirer~w~ avec un fusil d'assaut. + +[GEN3_63:GENERA3] +Tu peux fusiller des types de côté sur une ~h~ moto ~w~, mais également ~h~tirer droit devant~w~. + +[GEN3_64:GENERA3] { reVC update } +Appuie sur la touche ~h~~k~~VEHICLE_FIREWEAPON~~w~ pour tirer droit devant toi sur une moto. + +[GEN3_65:GENERA3] { reVC update } +Appuie sur la touche ~h~~k~~VEHICLE_FIREWEAPON~~w~ pour tirer droit devant toi sur une moto. + +[GEN3_66:GENERA3] { reVC update } +Appuie sur la touche ~h~~k~~VEHICLE_FIREWEAPON~~w~ pour tirer droit devant toi sur une moto. + +[GEN3_67:GENERA3] +Il te faut une mitrailleuse pour tirer droit devant toi sur une moto. + +[GEN3_53:GENERA3] +Mon argent! + +[GEN3_52:GENERA3] +Ces Haitiens pensent qu'ils peuvent se payer Ricardo Diaz! + +{=================================== MISSION TABLE GENERA4 ===================================} + +[DETON:GENERA4] +DETONATION : + +[COL4_3:GENERA4] +CONVOI, HALTE! + +[COL4_6:GENERA4] +ON EST SOUS LE FEU ENNEMI! + +[COL4_7:GENERA4] +Civil, éloigne-toi du char! + +[COL4_8:GENERA4] +J'ai dit, dégage! IMMEDIATEMENT! + +[COL4_9:GENERA4] +POSITIONS DEFENSIVES! + +[COL4_11:GENERA4] +Dégagez ce civil de notre chemin, soldat! -Chef, oui, chef! + +[COL4_12:GENERA4] +Un civil dans le char! ARRETEZ-LE! + +[COL4_13:GENERA4] +Ceci est un convoi militaire, ne bloquez pas la route! + +[COL4_14:GENERA4] +Descendez-le, soldat. + +[COL4_15:GENERA4] +Dégagez ce véhicule civil de la route! -Chef, on déplace le véhicule, chef! + +[COL4_17:GENERA4] +Ok, peloton, on dégage! + +[COL4_18:GENERA4] +Y'a quelqu'un dans le char, chef! + +[COL4_19:GENERA4] +Trouvez-moi quelques beignets, soldat! -Chef, oui, chef! + +[COL4_20:GENERA4] +Cible verrouillée, chef! + +[COL4_21:GENERA4] +TIREUR D'ELITE! + +[COL4_22:GENERA4] +Je me tire d'ici. + +[COL4_23:GENERA4] +Objectif atteint! Peloton, rompez! -Allons manger quelques beignets! + +[COL4_24:GENERA4] +Protocole de sécurité Delta India Echo déclenché! Autodestruction du véhicule initialisée! + +[COL4_26:GENERA4] +Prépare-toi à crever, ordure communiste! + +[COL4_B2:GENERA4] +~r~Le char est parvenu sans problème à destination! + +[COL4_B5:GENERA4] +~r~Le char est détruit! + +[COL4_01:GENERA4] +Diaz était très content et il veut vous voir. + +[COL4_02:GENERA4] +Et c'est une bonne chose? + +[COL4_03:GENERA4] +Evidemment! Même si je commence à penser que Diaz est responsable de notre malheureuse perte... + +[COL4_04:GENERA4] +Qu'est-ce qui vous fait dire ça? + +[COL4_05:GENERA4] +On n'accuse pas comme ça un homme comme Diaz... Je ne faisais que penser à haute voix... + +[COL4_06:GENERA4] +Quoi qu'il en soit, j'ai une proposition qui devrait vous intéresser... + +[COL4_07:GENERA4] +Je n'ai plus le temps de faire des commissions, Cortez. + +[COL4_08:GENERA4] +J'aurais pensé qu'un homme avec des dettes aussi délicates serait ouvert à toute opportunité. Mais écoutez quand même, Tommy. + +[COL4_09:GENERA4] +Allez-y... + +[COL410:GENERA4] +J'ai un acheteur pour une pièce d'artillerie qui est emmenée à travers la ville. Récupèrez-la pour moi. + +[COL411:GENERA4] +Et une fois que vous l'aurez, je veux que vous m'appeliez de suite, et... + +[COL4_B4:GENERA4] +~g~Le char est verrouillé. Trouve le moyen d'en faire sortir les occupants. + +[COL4_1:GENERA4] +Qu'est-ce qui se passe avec l'artilleur? - Aucune idée, chef! + +[COL4_4:GENERA4] +Montez voir, soldat! - Chef, oui, chef! + +[COL4_B1:GENERA4] +~g~Va faucher la pièce d'artillerie escortée à travers la ville. + +[COL4_B3:GENERA4] +~g~Amène le char dans le garage du colonel avant qu'il ne s'autodétruise. + +[COL4_B6:GENERA4] +~g~Trouve un moyen de piquer le char! + +[COL4_B7:GENERA4] +~g~Conduis le char dans le garage. + +[COL4_B8:GENERA4] +~g~Sors du char et du garage. + +{=================================== MISSION TABLE GENERA5 ===================================} + +[COL5A_1:GENERA5] +Les circonstances me forcent à un départ en hâte, amigo. + +[COL5A_2:GENERA5] +Quel est le problème? + +[COL5A_3:GENERA5] +Les français veulent récupérer leur technologie de missile et après le dernier incident, + +[COL5A_4:GENERA5] +je crois que l'heure est venue de me mettre à l'abri. + +[COL5A_5:GENERA5] +Ca serait pas plus sûr de partir en avion ? + +[COL5A_6:GENERA5] +Je serais mort avant d'avoir atteint l'enregistrement. D'ailleurs, il faut que je fasse sortir la marchandise du pays. + +[COL5A_7:GENERA5] +Besoin d'un autre flingue? + +[COL5A_8:GENERA5] +Vous, amigo, vous en valez dix... hahahaha. + +[COL5B_1:GENERA5] +Tommy, vous m'avez bien protégé et servi. + +[COL5B_2:GENERA5] +Mais maintenant, vous devez nous laisser avant qu'on arrive en pleine mer. + +[COL5B_4:GENERA5] +Merci, Colonel. + +[COL5B_5:GENERA5] +Une dernière chose, pendant que je suis parti, pouvez-vous surveiller Mercedes pour moi? + +[COL5B_6:GENERA5] +Je crois qu'elle peut se surveiller tout seule, mais soyez tranquille, je garderai l'oeil ouvert. + +[COL5B_7:GENERA5] +Merci, mon ami. Jusqu'à mon retour. + +[COL5B_8:GENERA5] +Adios, amigo. + +[COL5_7:GENERA5] +Arrêtez de me tirer dessus! + +[COL5_9:GENERA5] +Tommy, empêchez-les de me tirer dessus! + +[COL5_10:GENERA5] +J'ai l'immunité diplomatique! + +[COL5_11:GENERA5] +Ne tirez pas, je suis un Colonel! + +[COL5_12:GENERA5] +Tommy, descendez-les, mon pays vous en sera reconnaissant. + +[COL5_13:GENERA5] +Tommy, on est submergés par les Français! + +[COL5_14:GENERA5] +Tommy, il y a des Français partout! Je déteste ça! + +[COL5_15:GENERA5] +Tommy, comment ça va? + +[COL5_16:GENERA5] +Ca, c'est pour Piaf et Gainsbourg et votre saloperie de pain français! + +[COL5_1:GENERA5] +A bâbord! A bâbord! + +[COL5_2:GENERA5] +Ils attaquent à tribord! + +[COL5_3:GENERA5] +Le pont devant! + +[COL5_4:GENERA5] +Ils ont un hélico! + +[COL5_B1:GENERA5] +~g~Protège le colonel et son yacht à tout prix. + +[COL5_B2:GENERA5] +~g~Passe devant et dégage la route pour le yacht du colonel. + +[COL5_B3:GENERA5] +~r~Le colonel est mort! + +[COL5_B4:GENERA5] +~g~Abats l'hélicoptère vous tire dessus. + +[COL5B_3:GENERA5] +Je vous laisse ma vedette personnelle. Gardez-la en témoignage de ma gratitude! + +[COL5_B5:GENERA5] +~g~Descends les hélicos et protège bien le yacht. + +[COL5_B6:GENERA5] +~g~Tu n'as plus de munitions, va en chercher d'autres dans les escaliers du pont supérieur. + +[COL5_B7:GENERA5] +~g~Ta santé diminue. Régénère-la dans les escaliers du pont supérieur. + +{=================================== MISSION TABLE HAIT1 ===================================} + +[HAM1_A:HAIT1] +Bonjour? Y'a quelqu'un? + +[HAM1_B:HAIT1] +Entre, mon petit, et repose ton esprit. + +[HAM1_C:HAIT1] +Tu dois être le grand méchant homme dont m'a parlé mon grand-père. + +[HAM1_D:HAIT1] +Il me dit des choses sur toi, tu sais, quand il me rend visite + +[HAM1_E:HAIT1] +et aussi des choses sur les autres qui t'attendent. + +[HAM1_F:HAIT1] +Bon, nous sommes tous morts depuis longtemps, mais toi, + +[HAM1_G:HAIT1] +je ne voudrais pas être à ta place, hé hé hé! + +[HAM1_H:HAIT1] +J'ai reçu un message. De venir ici. + +[HAM1_I:HAIT1] +Tu les entends? + +[HAM1_J:HAIT1] +Ils crient ton nom, mon garçon, ils doivent vraiment te vouloir, tu ne crois pas? + +[HAM1_K:HAIT1] +Maintenant, si tu rends service à tata Poulet, peut-être qu'elle peut t'aider. + +[HAM1_L:HAIT1] +Peut-être qu'elle peut te donner un petit grigri après tout ça. + +[HAM1_M:HAIT1] +Te donner de la magie pour donner le mauvais oeil au policier, mmmm? + +[HAM1_N:HAIT1] +Ecoute, tout ça est très, hum... me donner quoi? + +[HAM1_O:HAIT1] +Je... Je crois que je me suis planté d'adresse... + +[HAM1_P:HAIT1] +Fais-ça pour moi, Tommy... + +[HAM1_Q:HAIT1] +Les Cubains, ces vilains coqs arrogants, hum, + +[HAM1_R:HAIT1] +ont fait des ennuis à mes adorables garçons haïtiens. + +[HAM1_S:HAIT1] +Et maintenant, ils ont dit au policier où je cachais mes poudres! + +[HAM1_T:HAIT1] +Ils pensent que c'est des drogues, les idiots! + +[HAM1_U:HAIT1] +Alors, sois un gentil garçon, Tommy, et va chercher les poudres de tata Poulet. + +[HAM1_V:HAIT1] +Oui, d'accord, d'accord. + +[HAM1_1:HAIT1] +~g~Les policiers se rapprochent des poudres! Va les récupérer avant eux! + +[HAM1_2:HAIT1] +~r~Les policiers ont trouvé la cachette les premiers! + +[HAM1_3:HAIT1] +~g~Ramène tout ça à la planque! + +[HAM1_4:HAIT1] +~g~Bien! Maintenant, la suivante! + +[HAM1_6:HAIT1] +~r~La cachette a été détruite, imbécile! + +[HAM1_7:HAIT1] +~g~Les policiers ont les poudres! Récupère-les avant qu'ils filent! + +[HAM1_8:HAIT1] +~g~Les flics sont en route pour récupérer les poudres, magne-toi! + +[HAT_1A:HAIT1] +~g~Bouge pas d'un poil, connard! + +{=================================== MISSION TABLE HAIT2 ===================================} + +[HAT2_B1:HAIT2] +~g~Va à la camionnette contenant les bombes volantes. + +[HAT2_B2:HAIT2] +Tue les Cubains... + +[HAT2_B4:HAIT2] +... Et détruis leurs bateaux! + +[HAT2_B5:HAIT2] +~g~Les Cubains essayent de s'enfuir! Ne les laisse pas faire! + +[HAT2_B6:HAIT2] +~r~L'avion télécommandé est hors de portée! + +[HAT2_B7:HAIT2] +~g~Un des Cubains s'échappe en voiture. Ne laisse aucun témoin! + +[HAT2_B8:HAIT2] +~r~Tu n'as plus d'avions télécommandés! + +[HAT2_B9:HAIT2] +Avions télécommandés : + +[HAT2_1:HAIT2] +Oh, désolé, j'ai dû me tromper d'adresse... + +[HAT2_2:HAIT2] +Eh bien, entre donc te détendre en buvant un peu de thé. + +[HAT2_3:HAIT2] +Tu as quelque chose pour moi, Tommy? + +[HAT2_4:HAIT2] +Ouais... + +[HAT2_5:HAIT2] +Cet endroit me semble familier. Un vague souvenir d'enfance, un air de déjà vu... + +[HAT2_6:HAIT2] +Tommy, je voudrais te demander de faire une petite commission pour moi... + +[HAT2_7:HAIT2] +Tu me rappelles quelqu'un que... + +[HAT2_8:HAIT2] +Les Cubains ont des bateaux très rapides qui leur servent à traverser les mers avec de la drogue. + +[HAT2_9:HAIT2] +C'est leur gagne-pain. + +[HAT2_10:HAIT2] +Mon neveu a fabriqué quelques bombes volantes pour nous en débarrasser. + +[HAT2_11:HAIT2] +Transforme leurs bateaux en allumettes! + +[HAT2_12:HAIT2] +Bon, merci pour le thé. + +[HAT2_B3:HAIT2] { reVC update } +Appuie sur la touche ~h~~k~~VEHICLE_FIREWEAPON~~w~ pour larguer une bombe ou sur ~h~~k~~VEHICLE_ENTER_EXIT~~w~ pour annuler. + +{=================================== MISSION TABLE HAIT3 ===================================} + +[HAM3_A:HAIT3] +Salut, euh, je... Je cherche quelqu'un dans le coin... + +[HAM3_B:HAIT3] +Tu as l'air d'avoir faim, Tommy. + +[HAM3_C:HAIT3] +On se connait? + +[HAM3_D:HAIT3] +Chut! + +[HAM3_E:HAIT3] +Encore une chose et je te laisse partir, Tommy. + +[HAM3_F:HAIT3] +Mes garçons sont en guerre contre les Cubains. + +[HAM3_G:HAIT3] +Mais pas de revolvers. + +[HAM3_H:HAIT3] +Hum, mais les Cubains ont une suprise qui les attend! + +[HAM3_I:HAIT3] +Pendant qu'ils se battent dans les rues, prends ce fusil et tue-les dans la confusion! + +[HAM3_J:HAIT3] +Personne ne te verra, personne ne t'entendra! + +[HAM3_K:HAIT3] +Si tu fais ça pour tata Poulet, Tommy, tu ne seras plus lié aux cordons de mon tablier! + +[HAM3_1:HAIT3] +~g~On doit gagner cette bataille! Si tous les Haïtiens meurent, c'est foutu! + +[HAM3_3:HAIT3] +~g~Les Cubains risquent de tricher, alors fais attention. + +[HAM3_4:HAIT3] +~r~Tu as été repéré! C'est foutu! + +[HAM3_5:HAIT3] +~g~Tu dois tuer les cubains de loin. Ne te fais pas voir. + +[HAM3_8:HAIT3] +~g~Les Haïtiens se font descendre! Vise mieux! + +[HAM3_7:HAIT3] +~g~Fais gaffe! Les Cubains ont amené des renforts. Liquide-les tous! + +[HAM3_2:HAIT3] +~r~Les Haïtiens sont morts! + +[HAM3_L:HAIT3] +Tata... + +{=================================== MISSION TABLE HOTEL ===================================} + +[INTB_A:HOTEL] +Tommy! Ca fait une paye! + +[INTB_B:HOTEL] +Salut Sonny... + +[INTB_C:HOTEL] +Je sais, je sais, t'es submergé par l'émotion, hein? + +[INTB_D:HOTEL] +Quinze piges et c'est comme si c'était hier! + +[INTB_E:HOTEL] +C'est une façon de voir les choses... + +[INTB_F:HOTEL] +Hé, bosser pour la famille, tu sais, c'est pas de la tarte, + +[INTB_G:HOTEL] +mais la famille veille toujours sur les siens, tu comprends? + +[INTB_H:HOTEL] +Bon, raconte un peu comment l'affaire s'est passée? Ca y est, t'es blindé? + +[INTB_I:HOTEL] +Ecoute, Sonny, on s'est fait piéger. C'était une embuscade. Et ils ont buté Harry et Lee. + +[INTB_J:HOTEL] +J'espère pour toi que tu rigoles, Tommy... Et t'as plutôt intérêt à toujours avoir le fric... + +[INTB_K:HOTEL] +... Non... Sonny... J'ai plus le fric... + +[INTB_L:HOTEL] +C'était mon fric, Tommy! MON FRIC!!! + +[INTB_M:HOTEL] +Vaudrait mieux pas que t'essayes de me baiser Tommy, parce que je suis pas le genre à aimer ça... + +[INTB_N:HOTEL] +Attend Sonny... + +[INTB_O:HOTEL] +Je te donne ma parole d'honneur que je vais récupérer ton fric et la dope. + +[INTB_P:HOTEL] +Et en prime, je t'enverrai les couilles de ceux qui ont fait ça! + +[INTB_Q:HOTEL] +Ca, je le sais déjà... Tommy, t'es pas con, mais je te préviens, moi non plus... + +[INTB_R:HOTEL] +Et si c'était pas toi, tu serais déjà mort... + +[INTB_S:HOTEL] +Mais j'ai un faible pour toi et en souvenir du passé, je vais te laisser t'en charger. + +[INTB_T:HOTEL] +T'en fais pas, Sonny, t'as ma parole! + +[INTB_U:HOTEL] +Je te tiens au courant, ciao. + +{=================================== MISSION TABLE ICECRE1 ===================================} + +[ICC1_4:ICECRE1] +~g~Il n'y a aucun client dans ce secteur. Essaye ailleurs. + +[ICC1_5:ICECRE1] +Deals effectués : + +[ICC1_7:ICECRE1] +~g~Tu reçois de l'argent pour chaque transaction faite, mais plus tu fais d'affaires, plus tu attires l'attention de la police. + +[ICC1_8:ICECRE1] +~g~Pour effectuer une transaction, ~h~gare ta camionnette ~g~et appuie sur la ~h~~k~~VEHICLE_HORN~ ~g~pour jouer le jingle et attirer les clients. + +[ICC1_9:ICECRE1] +~g~Les gangs locaux n'apprécient pas qu'on fasse des affaires sur leur territoire. Attends-toi à des représailles. + +[ICC1_10:ICECRE1] +~g~Tu as fait ~1~ deals! + +[ICC1_11:ICECRE1] +~g~Tu as fait ~1~ deals! + +[ICC1_13:ICECRE1] +~r~T'as fait aucun deal! + +[ICC1_14:ICECRE1] +USINE DE CREME GLACEE OK + +[ICC1_15:ICECRE1] +~g~L'usine de crème glacée génère dorénavant un revenu de $~1~ maximum. Pense à récupérer le fric régulièrement. + +[ICC1_16:ICECRE1] +~g~Utilise la camionnette de Mr Whoopee pour distribuer les produits Cherry Poppers dans Vice City. + +[ICE_AT1:ICECRE1] +USINE DE CREME GLACEE OK + +[ICE_AT2:ICECRE1] +~g~L'usine Cherry Popper génère dorénavant un revenu de $~1~ maximum. Pense à récupérer le fric régulièrement. + +[ICC1_17:ICECRE1] +Mission Distribution terminée + +[ICC1_18:ICECRE1] +Total des ventes de glaces : $~1~ + +[ICC1_19:ICECRE1] +Total de deals réalisés : ~1~ + +{=================================== MISSION TABLE ICECUT ===================================} + +[ICC1_A:ICECUT] +T'es qui toi? + +[ICC1_B:ICECUT] +Le nouveau proprio. + +[ICC1_C:ICECUT] +Est-ce que t'as déjà été enfant? + +[ICC1_D:ICECUT] +Mais de quoi tu parles? + +[ICC1_E:ICECUT] +As-tu été un gosse? + +[ICC1_F:ICECUT] +Ouais! Vas-y, c'est quoi ton problème? + +[ICC1_G:ICECUT] +Je le savais. Un chiard. + +[ICC1_H:ICECUT] +Un putain de gosse pourri gâté, un chialeur, un emmerdeur, un bébé à sa maman! + +[ICC1_I:ICECUT] +Un bébé... Un horrible et dégoûtant petit morveux. Ouin ouin. + +[ICC1_J:ICECUT] +Ta maman t'aime pas. Petite merde! + +[ICC1_K:ICECUT] +Hé! Calme-toi. + +[ICC1_L:ICECUT] +Je HAIS les mouflets et les mômes. + +[ICC1_M:ICECUT] +Ce sont des putains de pourris gâtés, de chialeurs, d'emmerdeurs... + +[ICC1_N:ICECUT] +Ca suffit maintenant! + +[ICC1_O:ICECUT] +C'est quoi ton problème? + +[ICC1_P:ICECUT] +Tu confectionnes des glaces, non? C'est un truc pour les gosses. + +[ICC1_Q:ICECUT] +T'es quoi comme genre de cinglé, toi? + +[ICC1_R:ICECUT] +Que je comprenne... A quoi ça sert de rendre les gosses heureux si tu les déteste? + +[ICC1_S:ICECUT] +Espèce d'abruti, petit curieux, fouinard- + +[ICC1_T:ICECUT] +La ferme! + +[ICC1_U:ICECUT] +-de merde! + +[ICC1_V:ICECUT] +Les glaces c'est une couverture. + +[ICC1_W:ICECUT] +On fait aussi dans autre chose que les produits laitiers. + +[ICC1_X:ICECUT] +Et si je vois un gosse, j'en fait mon affaire. + +[ICC1_Y:ICECUT] +N'est-ce pas, gamin? Ouais, c'est bien ça. Ta maman t'aime pas. + +[ICC1_Z:ICECUT] +Elle te DETESTE! + +[ICC1_ZA:ICECUT] +PROPRIETE ACQUISE! + +{=================================== MISSION TABLE INTRO ===================================} + +[INT1_A:INTRO] +Tommy Vercetti... Oh, merde! + +[INT1_B:INTRO] +J'pensais pas qu'ils le laisseraient sortir un jour... + +[INT1_C:INTRO] +Il a gardé un profil bas, pour se faire oublier... + +[INT1_D:INTRO] +Mais les gens vont vite retrouver la mémoire + +[INT1_E:INTRO] +quand ils vont le voir débouler dans les rues du voisinage. + +[INT1_F:INTRO] +Et ça va pas être bon pour les affaires... + +[INT1_G:INTRO] +Et alors, qu'est-ce qu'on va faire, Sonny? + +[INT1_H:INTRO] +On le traite comme un vieil ami et on lui trouve de quoi s'occuper en-dehors de la ville. OK? + +[INT1_I:INTRO] +On parlait justement de s'étendre vers le sud... Pas vrai? + +[INT1_J:INTRO] +Vice City, c'est du 24 carats, maintenant. + +[INT1_K:INTRO] +Les colombiens, les mexicains... Putain, + +[INT1_L:INTRO] +même les réfugiés cubains se taillent une belle part du gâteau! + +[INT1_M:INTRO] +Mais ils font tous dans la dope, Sonny! + +[INT1_N:INTRO] +Aucune famille ne touche à cette merde! + +[INT1_O:INTRO] +Les temps changent... + +[INT1_P:INTRO] +Les familles ne peuvent pas rester les bras croisés pendant que nos adversaires ramassent le jackpot. + +[INT1_Q:INTRO] +Alors, on envoie un mec là-bas faire le sale boulot + +[INT1_R:INTRO] +et pour nous en tailler une bonne part. OK? + +[INT1_S:INTRO] +C'est qui, notre contact là-bas? + +[INT1_T:INTRO] +Ken Rosenberg, le con d'avocat... + +[INT1_U:INTRO] +Et comment il va faire pour tenir Vercetti en laisse? + +[INT1_V:INTRO] +Il aura pas besoin de faire ça. + +[INT1_W:INTRO] +On le lâche juste dans Vice City + +[INT1_X:INTRO] +et on lui refile un peu de cash pour commencer. OK? + +[INT1_Y:INTRO] +Donne-lui quelques mois... + +[INT1_Z:INTRO] +Et puis, on descendra + +[INT1_A1:INTRO] +lui rendre une petite visite, tu saisis? + +[INT1_A2:INTRO] +Histoire de voir comment il se débrouille... + +[INT2_A:INTRO] +Hey, salut les gars, c'est moi, Ken Rosenberg! Hé ! Super! + +[INT2_B:INTRO] +Bon, euh, je vais vous conduire au rendez-vous, ok? + +[INT2_C:INTRO] +J'ai parlé avec les fournisseurs, ils sont très, hé hé, + +[INT2_D:INTRO] +très impatients de commencer cette affaire, et... euh, + +[INT2_E:INTRO] +si tout se passe sans accroc, on devrait... euh, + +[INT2_F:INTRO] +s'en mettre plein les poches, ce qui est vraiment, vous savez... + +[INT2_G:INTRO] +une bonne chose... + +[INT2_H:INTRO] +Ok. Bon, ils sont frères, ok. + +[INT2_I:INTRO] +Il y en a un qui fait marcher... euh, l'affaire + +[INT2_J:INTRO] +et l'autre qui s'occupe du transport en hélicoptère. + +[INT2_K:INTRO] +Maintenant, ils opèrent du Mexique, + +[INT3_A:INTRO] +Ok, c'est eux, dans l'hélico. + +[INT3_B:INTRO] +Bon, voilà le deal. + +[INT3_C:INTRO] +Ils veulent un échange en terrain découvert. + +[INT3_D:INTRO] +OK? Bon, allons-y et restez groupés. + +[INT3_E:INTRO] +Bon, personne s'énerve, maintenant. + +[INT3_F:INTRO] +Je suis ici! Le moteur tourne! + +[INT3_G:INTRO] +Tu l'as? + +[INT3_H:INTRO] +De la colombienne 100% pure, la meilleure qualité, amigo. + +[INT3_I:INTRO] +Les billets? + +[INT3_J:INTRO] +Des 10 et des 20... usagés. + +[INT3_L:INTRO] +Allez! Sors-nous de là! Roule! + +[INTRO1:INTRO] +Je sors la tête d'un caniveau pour une seconde de défonce, et le destin me chie de la merde plein la gueule! + +[INTRO2:INTRO] +Va roupiller. + +[INTRO3:INTRO] +Qu'est-ce que tu vas faire? + +[INTRO4:INTRO] +Je passerai à ton bureau demain et on essayera de trouver une solution à ce merdier. + +[INT3_K:INTRO] +Je crois que nous allons faire affaire, amigo, hé hé hé! + +[INT3_M:INTRO] +Laisse-moi voir. + +[INT2_L:INTRO] +non, non, non, attends... + +{=================================== MISSION TABLE KENT1 ===================================} + +[KPM1_A:KENT1] +D'accord, mon vieux, je vais sauver ton petit ami. + +[KPM1_B:KENT1] +De quoi tu m'causes? + +[KPM1_C:KENT1] +Tu connais ce branleur, Diaz, la grande gueule... + +[KPM1_D:KENT1] +Il a ton pote, Lance. Il paraît que celui-ci a essayé de lui sauter sur le poil. + +[KPM1_E:KENT1] +Mais il a pas sauté assez haut, si tu vois ce que je veux dire... + +[KPM1_F:KENT1] +Où il l'a emmené? Et tourne pas autour du pot! + +[KPM1_G:KENT1] +T'énerve pas! Ils l'ont emmené de l'autre côté de la ville, à la décharge. + +[KPM1_H:KENT1] +Putain... Pauvre cinglé... + +[KPM1_2:KENT1] +~r~Tu étais supposé sortir Lance vivant de ce guêpier! + +[KPM1_3:KENT1] +SANTE DE LANCE : + +[RESC_1:KENT1] +Tu peux te servir d'un flingue? + +[RESC_2:KENT1] +Ouais... je pense... Content de te voir moi aussi. + +[RESC_3:KENT1] +Sortons de là! + +[RESC_4:KENT1] +Ca fout tous mes plans en l'air, tes conneries. Cette fois-ci, t'as vraiment merdé pour de bon, Lance! + +[RESC_5:KENT1] +Il a descendu mon frère! Tu croyais que j'allais faire quoi? Lui tondre sa pelouse? + +[RESC_6:KENT1] +Va falloir qu'on liquide ce connard de Diaz avant qu'il en fasse autant avec nous. + +[RESC_7:KENT1] +Va récupérer et retrouve-moi sur le pont de Star Island. Ok? + +[RESC_8:KENT1] +OK. + +[KPM1_1:KENT1] +~g~Lance est retenu prisonnier dans la décharge. Va le sauver! + +[KPM1_4:KENT1] +~g~Amène Lance à l'hosto! + +[M_PASSN:KENT1] +MISSION TERMINEE! + +[KPM1_5:KENT1] +~g~Les mecs de Diaz sont après toi, amène Lance à l'hosto. + +{=================================== MISSION TABLE KICKSTT ===================================} + +[KICK1_2:KICKSTT] +~r~Tu n'as pas récupéré la moto assez vite! + +[KICK1_7:KICKSTT] +~r~Tu as niqué la moto! + +[KICK1_8:KICKSTT] +~g~Monte sur la moto! + +[KICK1_T:KICKSTT] +TEMPS PRIS : + +[KICKTM:KICKSTT] +~b~TEMPS DE L'EPREUVE : ~1~:~1~ + +[KICKTM2:KICKSTT] +~b~TEMPS DE L'EPREUVE : ~1~:0~1~ + +[GETBIKE:KICKSTT] +~g~Il te reste ~1~ secondes pour retourner à une moto avant la fin de la mission. + +[KICK1_1:KICKSTT] +~g~Termine le parcours aussi vite que possible. + +[KICK1_6:KICKSTT] +~g~Bien joué! + +[KICK_10:KICKSTT] +~g~Utilise la Sanchez pour terminer le parcours sans oublier aucun point de passage. + +[KICK_12:KICKSTT] +~r~Tu t'es dégonflé! + +[KICK_13:KICKSTT] +~r~T'as mis trop de temps! + +[KICK_11:KICKSTT] +~g~Pour quitter la mission, marche sur le ~q~marqueur rose~g~. + +{=================================== MISSION TABLE LAWYER1 ===================================} + +[LAW1_A:LAWYER1] +Va roupiller, qu'il m'a dit - + +[LAW1_B:LAWYER1] +- et je suis resté toute la nuit le cul vissé sur cette chaise dans le noir à boire du café! + +[LAW1_C:LAWYER1] +C'est la merde. On est baisés jusqu'à l'os, mon pote! + +[LAW1_D:LAWYER1] +Ecoute-moi, ces balaises, ils vont débarquer ici pour m'arracher la tête! C'est pas possible! + +[LAW1_E:LAWYER1] +J'me suis pas tapé l'école de droit pour ça! Putain, qu'est-ce qu'on va faire maintenant? + +[LAW1_F:LAWYER1] +Ferme-la, pose ton cul et détends-toi. Je vais te dire ce qu'on va faire. + +[LAW1_G:LAWYER1] +Faut que tu trouves les types qui nous ont fauché la coke et après je les descends. + +[LAW1_H:LAWYER1] +Bonne idée. Excellente idée. Laisse-moi réfléchir, laisse-moi réfléchir. + +[LAW1_I:LAWYER1] +Je sais! Il y a ce Colonel à la retraite, le Colonel Juan Garcia Cortez. + +[LAW1_J:LAWYER1] +C'est lui qui m'a aidé à organiser l'affaire + +[LAW1_K:LAWYER1] +en-dehors des truands établis de Vice City. Ok? + +[LAW1_L:LAWYER1] +Maintenant, écoute. Il organise une fiesta dans la baie, sur son yatch du luxe + +[LAW1_M:LAWYER1] +et tous les gros pontes de Vice City seront là! + +[LAW1_N:LAWYER1] +J'ai une invitation, bien sûr que j'ai une invitation, + +[LAW1_O:LAWYER1] +mais il est pas question que je sorte d'ici. Non, pas question! + +[LAW1_P:LAWYER1] +Ferme-la, je t'ai dit! Je vais y aller moi-même... + +[LAW1_Q:LAWYER1] +Attends deux secondes! Tu sais, moi aussi, j'aime bien 1978, mais là, c'est pas une soirée bière et striptease... + +[LAW1_R:LAWYER1] +Je veux dire, sans vouloir t'offenser, hein, tu risque de te faire refouler à l'entrée, quoi... + +[LAW1_S:LAWYER1] +Y'a un problème avec mes fringues? + +[LAW1_T:LAWYER1] +Bon, écoute. Fais un saut chez Rafael, dis-lui que tu viens de ma part et il te relookera. + +[LAW1_U:LAWYER1] +Ok, allez, vas-y... + +[LAWP_1:LAWYER1] +Buenas noches. + +[LAWP_2:LAWYER1] +Si je comprends bien, c'est M. Rosenberg qui vous envoie. + +[LAWP_3:LAWYER1] +J'espère que de récents problèmes n'ont pas affecté sa santé, ou ... hum... + +[LAWP_4:LAWYER1] +son équilibre mental, M....? + +[LAWP_5:LAWYER1] +Vercetti. Il fait simplement un peu... d'agoraphobie. + +[LAWP_6:LAWYER1] +Excellent, excellent. Et vous? + +[LAWP_7:LAWYER1] +Je veux juste ma marchandise. + +[LAWP_8:LAWYER1] +Ah. C'est un malheureux concours de circonstances pour toutes les personnes impliquées... + +[LAWP_9:LAWYER1] +Bien entendu, j'ai de mon côté ordonné une enquête... + +[LAWP_10:LAWYER1] +Mais c'est un problème délicat qui nécessite du temps... + +[LAWP_11:LAWYER1] +Peut-être en reparlerons-nous plus tard, voulez-vous? + +[LAWP_12:LAWYER1] +En attendant, laissez-moi vous présenter ma fille... + +[LAWP_13:LAWYER1] +Mercedes! + +[LAWP_14:LAWYER1] +Caramia, pourrais-tu t'occuper de notre invité pendant que je m'occupe des autres convives? + +[LAWP_15:LAWYER1] +D'accord, papa. + +[LAWP_16:LAWYER1] +Veuillez m'excuser... + +[LAWP_17:LAWYER1] +Mercedes? + +[LAWP_18:LAWYER1] +Et il faut vivre avec ça... + +[LAWP_19:LAWYER1] +Quoi qu'il en soit, laissez-moi vous montrer quelques uns de nos distingués invités... + +[LAWP_20:LAWYER1] +Celui-ci est le membre du congrès Alex Shrub accompagné par la star montante siliconée Candy Suxxx... + +[LAWP_21:LAWYER1] +Et connaissez-vous ma charmante femme Laura? Non? + +[LAWP_22:LAWYER1] +Eh bien, malheureusement, elle est en Alabama, voici Candy. + +[LAWP_23:LAWYER1] +Et là-bas, nous avons l'ailier vedette des Mambas de Vice City, BJ. + +[LAWP_24:LAWYER1] +toujours aussi charmeur + +[LAWP_25:LAWYER1] +Mais je l'ai plaqué et mis dans une chaise roulante! + +[LAWP_26:LAWYER1] +Ha ha! Excellent! + +[LAWP_27:LAWYER1] +En ce moment, je cherche une nouvelle propriété de luxe. + +[LAWP_28:LAWYER1] +Et ce molusque c'est Jezz Torrent, + +[LAWP_29:LAWYER1] +Le chanteur principal des Love Fist. + +[LAWP_30:LAWYER1] +Savez-vous... comment ils jouent au ping-pong, en Thaïlande? + +[LAWP_31:LAWYER1] +Je vais vous le dire... + +[LAWP_32:LAWYER1] +Ils n'utilisent pas de raquettes, si vous voyez ce que je veux dire! + +[LAWP_33:LAWYER1] +L'impotent. + +[LAWP_34:LAWYER1] +Et le trio bavard. + +[LAWP_35:LAWYER1] +Cette outre à sueur ronflante est le bras droit handicapé de papa, Gonzales. + +[LAWP_36:LAWYER1] +Les deux autres sont le Pasteur Richards + +[LAWP_37:LAWYER1] +et un réalisateur pseudo intellectuel nommé Steve Scott. + +[LAWP_38:LAWYER1] +...passion avec les envahisseuses nymphomanes, + +[LAWP_39:LAWYER1] +Le requin géant s'amène et + +[LAWP_40:LAWYER1] +il leur bouffe simplement les couilles! + +[LAWP_41:LAWYER1] +Ah, c'est là. Vous n'avez jamais vu un truc pareil avant, hein? + +[LAWP_42:LAWYER1] +Colonel! + +[LAWP_43:LAWYER1] +Vos fêtes sont toujours aussi réussies, hahahahha! + +[LAWP_44:LAWYER1] +Je ne puis que m'excuser de mon arrivée tardive. + +[LAWP_45:LAWYER1] +Ah, de nada amigo. Comment allez-vous? + +[LAWP_46:LAWYER1] +Nos affaires sont très difficile - Les barbares sont aux portes de la ville. + +[LAWP_47:LAWYER1] +L'heure est venue de récompenser les amis et de liquider les ennemis, amigo. + +[LAWP_48:LAWYER1] +C'est qui, la grande gueule? + +[LAWP_49:LAWYER1] +Ricardo Diaz, c'est Mr.Coke. + +[LAWP_50:LAWYER1] +Mercedes! + +[LAWP_51:LAWYER1] +Oh, je raccompagnais justement mon ami en ville. + +[LAWP_52:LAWYER1] +Une autre fois, Ricardo! + +[LAWP_53:LAWYER1] +Partons d'ici! + +[LAWP_54:LAWYER1] +En fait, emmenez-moi plutôt au Pole Position club. + +[LAW1_2:LAWYER1] +~g~Va au yatch du Colonel. + +[LAW1_4:LAWYER1] +~r~Tu as tué la fille du Colonel! + +[LAW1_5:LAWYER1] +Vous allez travailler pour mon père? + +[LAW1_6:LAWYER1] +Peut-être. + +[LAW1_7:LAWYER1] +Ca vous ennuie si je pose ma main sur votre genou? + +[LAW1_8:LAWYER1] +Peut-être... + +[LAW1_9:LAWYER1] +C'est trop dur d'avoir un père riche et puissant! Vamos! + +[LAW1_10:LAWYER1] +A plus tard, mon mignon! + +[LAW1_11:LAWYER1] +Sans aucun doute. + +[LAW1_12:LAWYER1] +Mmmm... Chouette bécane. + +[LAW1_13:LAWYER1] +Non! Ma moto! + +[LAW1_3:LAWYER1] +~g~Emmène la fille du Colonel au Pole Position Club. + +[HELP20:LAWYER1] +Suis le ~h~point T-shirt~w~ sur le radar pour trouver Rafael. + +[LAW1_14:LAWYER1] +Mmmm, j'aime beaucoup ta grosse moto. + +[LAW1_15:LAWYER1] +Ouais, bébé, je viens de piquer ça à ce con + +{=================================== MISSION TABLE LAWYER2 ===================================} + +[LAW2_A:LAWYER2] +Ah! Eh bien, j'espère que tu t'es bien amusé! Parce que moi, je suis mort d'inquiétude! Qu'est-ce que t'as trouvé? + +[LAW2_B:LAWYER2] +Qu'y'a plus de criminels dans cette ville qu'en taule. Faut qu'on trouve une piste dans la rue. + +[LAW2_C:LAWYER2] +Attends, laisse-moi réfléchir, voyons... + +[LAW2_D:LAWYER2] +Ah! J'ai trouvé! + +[LAW2_E:LAWYER2] +Bon, il y a bien cet anglais, une espèce de déchet du biz de la musique, + +[LAW2_F:LAWYER2] +il est connu sous le nom de Kent Paul. + +[LAW2_G:LAWYER2] +Quoi qu'il en soit, il a le nez plongé dans la merde de Vice City depuis un moment + +[LAW2_H:LAWYER2] +et ça, si quelqu'un sait où sont les 20 kilos de coke, + +[LAW2_I:LAWYER2] +c'est bien lui, ok? Il est toujours fourré au Malibu... + +[LAW2_J:LAWYER2] +Je vais aller lui rendre une petite visite... + +[LAW2B_A:LAWYER2] +D'où tu sors, toi? + +[LAW2B_B:LAWYER2] +Je cherche un oiseau dans ton genre depuis une éternité... + +[LAW2B_C:LAWYER2] +Kent Paul, mon pote. Ouais, dans le coin, c'est moi le patron. + +[LAW2B_D:LAWYER2] +Je cherche un Anglais... + +[LAW2B_E:LAWYER2] +Je résouds les problèmes, si tu vois ce que je veux dire? + +[LAW2B_F:LAWYER2] +Je vais m'occuper de toi. Quoi que tu demandes, tu l'auras... + +[LAW2B_G:LAWYER2] +Ne t'inquiéte absolument de rien. + +[LAW2B_H:LAWYER2] +Dégage, chérie! + +[LAW2B_I:LAWYER2] +Hé hé hé hé hé! + +[LAW2B_J:LAWYER2] +C'est toi, Kent Paul? Je suis un pote de Rosenberg... + +[LAW2B_K:LAWYER2] +Rosenberg...Rosenberg... Ah, ce cinglé qui poursuit les ambulances! + +[LAW2B_L:LAWYER2] +Ce mec peut défendre un innocent jusqu'au couloir de la mort! + +[LAW2B_M:LAWYER2] +Donne-nous un autre verre! + +[LAW2B_N:LAWYER2] +On est tous des comédiens. + +[LAW2B_O:LAWYER2] +Ecoute-moi, il me manque vingt kilos et un gros tas de pognon... + +[LAW2B_P:LAWYER2] +Une affaire de drogue? C'est toujours des pièges à con... + +[LAW2B_Q:LAWYER2] +Qu'est-ce que tu sais à ce propos? + +[LAW2B_R:LAWYER2] +Hé! Hé! Là où je voulais en venir, + +[LAW2B_S:LAWYER2] +c'est à un cuisinier-trompettiste qui deale à l'extérieur de la cuisine d'un hôtel sur Ocean Drive. + +[LAW2B_T:LAWYER2] +Il avait l'air très content de lui, ces derniers temps. Tu pourrais peut-être aller voir ça de plus près... + +[LAW2B_U:LAWYER2] +Bon, je vais y aller. A plus tard. + +[LAW2B_V:LAWYER2] +Ouais, c'est ça. Allez, dégage, imbécile ou je te botte le cul! + +[LAW2B_W:LAWYER2] +Donne-moi à boire et putain, où est cette salope? + +[LAW2C_A:LAWYER2] +Ouais, c'est bien, massacre-le, ça devrait le rendre plus bavard. + +[LAW2C_B:LAWYER2] +T'en veux aussi? + +[LAW2C_C:LAWYER2] +Hé, relax, je veux la même chose que toi, mon frère. + +[LAW2C_D:LAWYER2] +Ah ouais? Et c'est quoi? + +[LAW2C_E:LAWYER2] +Ton fric et la blanche de mon regretté frère. Malheureusement, tu viens de refroidir notre piste. + +[LAW2C_F:LAWYER2] +C'était un accident. Dégage. + +[LAW2C_G:LAWYER2] +Hé, hé! Pas la peine de jouer les justiciers solitaires avec moi! + +[LAW2C_H:LAWYER2] +Voilà comment je vois les choses... On est deux mecs dans une ville étrange. Et on a besoin de veiller l'un sur l'autre. + +[LAW2C_I:LAWYER2] +J'ai pas besoin de toi, mon frère... + +[LAW2C_J:LAWYER2] +T'en es sûr? Tiens, attrape! + +[LAW2C_K:LAWYER2] +Suis-moi! + +[LAW2_1:LAWYER2] +Qu'est-ce que tu regardes? + +[LAW2_2:LAWYER2] +Tu ferais mieux de commencer à causer... + +[LAW2_3:LAWYER2] +Suce-moi, connard! + +[LAW2_4:LAWYER2] +Par là! + +[LAW2_5:LAWYER2] +Je vais voir ce que je peux trouver. Je garde un oeil sur toi, Tommy. + +[LAW2_6:LAWYER2] +~g~Va au Malibu Club et trouve Kent Paul. + +[LAW2_7:LAWYER2] +~g~Trouve le cuisinier sur Ocean Drive. + +[LAW2_10:LAWYER2] +~g~Retourne à l'hôtel. + +[LAW2_11:LAWYER2] +~g~Ramasse son portable! + +[LAW2_12:LAWYER2] +Portable acquis! Tu peux maintenant recevoir des coups de fil. + +[LAW2_13:LAWYER2] +~g~Tu as laissé Lance derrière! Va le chercher! + +[LAW2_14:LAWYER2] +On doit foutre le camp d'ici! + +[GUN_2A:LAWYER2] +Maintiens la ~h~~k~~PED_LOCK_TARGET~ ~w~enfoncée pour ~h~viser automatiquement~w~. Appuie sur la~h~ ~k~~PED_FIREWEAPON~~w~ pour ~h~tirer! + +[GUN_2C:LAWYER2] +Maintiens la ~h~~k~~PED_LOCK_TARGET~ ~w~enfoncée pour ~h~viser automatiquement~w~. Appuie sur la~h~ ~k~~PED_FIREWEAPON~~w~ pour ~h~tirer! + +[GUN_2D:LAWYER2] +Maintiens la ~h~~k~~PED_LOCK_TARGET~ ~w~enfoncée pour ~h~viser automatiquement~w~. Appuie sur la~h~ ~k~~PED_FIREWEAPON~~w~ pour ~h~tirer! + +[HELP17:LAWYER2] +Appuie sur la~h~ ~k~~PED_FIREWEAPON~ ~w~pour attaquer le chef. + +[HELP18:LAWYER2] +Appuie sur la~h~ ~k~~PED_FIREWEAPON~~w~ pour attaquer le chef. + +[LAW3_11:LAWYER2] +Place-toi sur le ~q~marqueur rose~w~ pour consulter les armes en vente. + +[LAW3_12:LAWYER2] +Tu peux sélectionner des armes en appuyant sur ~h~gauche~w~ ou ~h~droite~w~ de la ~h~touche directionnelle. + +[LAW3_13:LAWYER2] +Si tu as assez de blé, tu peux acheter des armes en appuyant sur la ~h~~k~~PED_SPRINT~. + +[LAW3_14:LAWYER2] +Tu peux sortir du magasin en appuyant sur la ~h~~k~~VEHICLE_ENTER_EXIT~. + +[LAW3_15:LAWYER2] +Suis le ~h~point pistolet~w~ sur le radar pour trouver ~h~Ammu-Nation + +[LAW2_15:LAWYER2] +~g~Va chez Ammu-Nation. + +[LAW2_K:LAWYER2] +calme-toi, maintenant. + +[LAW2_16:LAWYER2] +Il faut que tu comprennes un truc sur cette ville. Tu dois toujours avoir un flingue sur toi. + +[LAW2_17:LAWYER2] +Allez, l'armurerie du coin n'est qu'à quelques pas d'ici. + +[LAW2_18:LAWYER2] +Tommy, tout le monde a besoin de se la couler douce de temps en temps. + +[LAW2_19:LAWYER2] +Voici le Pole Position Strip Club. Tu pourrais y passer de temps en temps. + +{=================================== MISSION TABLE LAWYER3 ===================================} + +[LAW3_A:LAWYER3] +Aaaaaah! Oh, nom de Dieu, c'est toi! Merde, je vais avoir besoin d'un nouveau pantalon! + +[LAW3_B:LAWYER3] +Putain, ces psychopathes du nord ont téléphoné pour dire qu'ils rappliquaient bientôt... + +[LAW3_C:LAWYER3] +Alors, où est ce sacré pognon? + +[LAW3_D:LAWYER3] +Relax, relax, on n'en est pas encore là. + +[LAW3_E:LAWYER3] +Je croyais vraiment que tu t'en occupais... + +[LAW3_F:LAWYER3] +Et maintenant, ces mecs disent qu'il faut qu'on leur rende un service... + +[LAW3_G:LAWYER3] +Tu veux dire que JE dois leur rendre un service... + +[LAW3_H:LAWYER3] +Ouais, évidemment! Est-ce que j'ai l'air de pouvoir impressionner un jury? + +[LAW3_I:LAWYER3] +Je ne pourrais même pas faire peur à un gosse et crois-moi, j'ai essayé! + +[LAW3_J:LAWYER3] +Maintenant, écoute, c'est ça ou le cousin de Forelli, Georgio, se prend cinq ans pour escroquerie. + +[LAW3_K:LAWYER3] +Faut que tu t'occupes de ces mecs! + +[LAW3_L:LAWYER3] +Je comprends. Demander au jury de changer d'opinion. T'en fais pas. + +[LAW3_M:LAWYER3] +Non non non non! Ca, j'ai déjà essayé! Et l'affaire ne s'est pas arrangée! + +[LAW3_N:LAWYER3] +Tu dois les OBLIGER à changer d'opinion! + +[LAW3_1:LAWYER3] +Avec les compliments de Georgio... + +[LAW3_2:LAWYER3] +N'oublie pas que le mot 'coupable' est proscrit! + +[LAW3_3:LAWYER3] +Il est innocent, jusqu'à ce que je dise le contraire. + +[LAW3_4:LAWYER3] +Tu sais qu'il n'est pas coupable... + +[LAW3_5:LAWYER3] +Tu te souviens de Georgio? Et donc qu'il est innocent? + +[LAW3_6:LAWYER3] +Non coupable. C'est compris... bien. + +[LAW3_8:LAWYER3] +~r~Tu as tué un juré! + +[LAW3_9:LAWYER3] +~g~Bousille la voiture du juré pour le faire sortir! + +[HELP40:LAWYER3] +Tu peux bousiller les voitures avec le marteau ou un outil similaire. + +[LAW3_10:LAWYER3] +~g~Tu peux aller à la ~h~quincaillerie~g~ pour acheter une arme de corps à corps. + +[LAW3_20:LAWYER3] +~g~Bousille la voiture du juré! + +[LAW3_21:LAWYER3] +Je n'arrive pas à le croire! + +[LAW3_22:LAWYER3] +Incroyable! + +[LAW3_23:LAWYER3] +Ok! C'est bon, j'ai compris! + +[LAW3_24:LAWYER3] +~g~Ce marteau pourrait être utile. + +[LAW3_7:LAWYER3] +~g~Va intimider les deux jurés, mais NE LES TUE PAS! + +[HELP23:LAWYER3] +Tu peux suivre le ~h~marteau~w~ sur le radar si tu veux acheter des armes de corps à corps dans la quincaillerie. + +[LAW3_16:LAWYER3] +de Pete de Floride. + +[LAW3_17:LAWYER3] +Casse-toi de là! + +{=================================== MISSION TABLE LAWYER4 ===================================} + +[LAW4_A:LAWYER4] +Avery, cela va sans dire... Ah, Tommy! Des nouvelles? Non, non, tu m'en parleras plus tard. Plus tard. + +[LAW4_B:LAWYER4] +Tommy, voici Avery Carrington. Je crois que vous vous êtes rencontrés à la fête? + +[LAW4_C:LAWYER4] +Pas directement. + +[LAW4_D:LAWYER4] +Salut. + +[LAW4_E:LAWYER4] +Avery a une proposition à nous faire. + +[LAW4_F:LAWYER4] +On a pas déjà du pain sur la planche? + +[LAW4_G:LAWYER4] +J'essaye de maintenir les loups au loin, alors lâche-moi un peu la grappe! + +[LAW4_H:LAWYER4] +Je suis tendu comme un cable et même si je dois crever avant la fin de la semaine, j'ai pas envie de mourir pauvre! + +[LAW4_I:LAWYER4] +Bon, vous énervez pas, tous les deux. + +[LAW4_J:LAWYER4] +Ecoute, petit, tu me files un coup de main et le moindre latino qui te fait chier, je m'occupe de lui. + +[LAW4_K:LAWYER4] +Ok, qu'est-ce que je peux faire pour toi? + +[LAW4_L:LAWYER4] +Cette société de livraison a son dépôt sur un terrain très intéressant. Et ils ne veulent pas vendre. + +[LAW4_M:LAWYER4] +Ils s'accrochent à leur terrain comme des rats entêtés, alors va falloir exploser cette vermine. + +[LAW4_N:LAWYER4] +Va là-bas et mets le feu aux poudres. + +[LAW4_O:LAWYER4] +La sécurité sera débordée et tu pourras te glisser dedans et en finir avec eux... + +[LAW4_P:LAWYER4] +Tu peux aussi passer chez Rafael pour changer de fringues. T'en as sans doute pour un moment, mais bon, fonce! + +[LAW4_Q:LAWYER4] +Ca va être une vraie émeute. + +[LAW4_R:LAWYER4] +Si tout se passe comme sur des roulettes, passe me voir au bureau... + +[LAW4_1:LAWYER4] +Dispersez-vous! La direction n'écoutera vos revendications que dans les conditions appropriées! + +[LAW4_2:LAWYER4] +Dispersez-vous! Retournez chez vous! + +[LAW4_3:LAWYER4] +Dispersez-vous! C'est inapproprié! + +[LAW4_4:LAWYER4] +Dispersez-vous ou vous finirez tous à la rue! + +[LAW4_5:LAWYER4] +Sortez vos bâtons, les mecs, on va briser quelques crânes de cocos! + +[LAW4_13:LAWYER4] +~g~Commence à te battre avec au moins 4 ouvriers pour déclencher une émeute. + +[LAW4_14:LAWYER4] +~g~Détruis les camionnettes du complexe! + +[HELP38:LAWYER4] +Si tu butes quelqu'un qui porte une arme, il la lâche. + +[HELP39:LAWYER4] +Tu peux tirer sur les barils d'explosifs, mais garde tes distances. + +{=================================== MISSION TABLE MIAMI_1 ===================================} + +[T4X4_1A:MIAMI_1] +~g~Tu as ~1~ secondes pour récupérer ~y~24~g~ points de passage. ~g~Fais-le dans ~y~N'IMPORTE QUEL ORDRE. + +[T4X4_1B:MIAMI_1] +~y~Passe A TRAVERS~g~ le premier point de passage pour déclencher le ~r~CHRONOMETRE. + +[T4X4_1C:MIAMI_1] +~1~ sur 24! + +[GETBIK1:MIAMI_1] +Tu as ~1~ secondes pour enfourcher une PCJ 600! + +[GETBIK3:MIAMI_1] +~r~Tu as besoin d'une PCJ 600 pour cette mission! + +{=================================== MISSION TABLE MM ===================================} + +[BLOD_04:MM] +ETAT DE LA VOITURE : + +[BLOD_05:MM] +~g~TEMPS VISE : ~1~ minute + +[BLOD_06:MM] +~g~TEMPS VISE : ~1~ minutes + +[BLOD_07:MM] +NV meilleur tps : ~1~ secondes + +[BLOD_08:MM] +Voitures détruites : ~1~ + +[BLOD_09:MM] +$~1~ + +[BLOD_10:MM] +VAINQUEUR! + +[BLOD_01:MM] +Franchis les points de passage pour augmenter ton temps général. + +[BLOD_02:MM] +Tu as perdu si ton temps général est à zéro. + +[BLOD_03:MM] +Il faut que ton temps général soit supérieur au temps visé pour gagner! + +{=================================== MISSION TABLE OVALRIG ===================================} + +[HOTR_01:OVALRIG] +~g~La course dure 12 tours. Seuls les trois premiers arrivés reçoivent une récompense. + +[HOTR_02:OVALRIG] +~g~Tu es disqualifié si ta bagnole est détruite. + +[HOTR_03:OVALRIG] +~g~Si ta bagnole est bousillée, tu peux la faire réparer aux stands. + +[HOTR_04:OVALRIG] +~g~C'est par là pour sortir du stade. + +[HOTR_05:OVALRIG] +Etat de la voiture : + +[HOTR_06:OVALRIG] +Tours : + +[HOTR_10:OVALRIG] +Temps de course : + +[HOTR_09:OVALRIG] +Position : + +[HOTR_12:OVALRIG] +~r~Ta caisse est bousillée! + +[HOTR_13:OVALRIG] +~r~Tu n'as pas gagné la course! + +[HOTR_14:OVALRIG] +~r~Tu as été disqualifié! + +[HOTR_15:OVALRIG] +Temps : ~1~:~1~ + +[HOTR_16:OVALRIG] +Temps : ~1~:0~1~ + +[HOTR_17:OVALRIG] +Meilleur temps : ~1~:~1~ + +[HOTR_18:OVALRIG] +Meilleur temps : ~1~:0~1~ + +[HOTR_19:OVALRIG] +Meilleur temps : aucun + +[HOTR_20:OVALRIG] +Nv meilleur tps : ~1~:~1~ + +[HOTR_21:OVALRIG] +Nv meilleur tps : ~1~:0~1~ + +[HOTR_22:OVALRIG] +Meilleur résultat : aucun + +[HOTR_23:OVALRIG] +Meilleur résultat : 1er + +[HOTR_24:OVALRIG] +Meilleur résultat : 2e + +[HOTR_25:OVALRIG] +Meilleur résultat : 3e + +[HOTR_26:OVALRIG] +Meilleur résultat : ~1~e + +[HOTR_27:OVALRIG] +Meilleur tps au tr : ~1~.~1~ secondes + +[HOTR_28:OVALRIG] +Meilleur tps au tr : ~1~.0~1~ secondes + +[HOTR_29:OVALRIG] +$~1~ + +[HOTR_30:OVALRIG] +1ERE PLACE + +[HOTR_31:OVALRIG] +2E PLACE + +[HOTR_32:OVALRIG] +3E PLACE + +[HOTR_33:OVALRIG] +Meilleur tps au tr : aucun + +[HOTR_11:OVALRIG] +Nv meilleur tps au tr : ~1~.~1~ secondes + +[HOTR_34:OVALRIG] +Nv meilleur tps au tr : ~1~.0~1~ secondes + +{=================================== MISSION TABLE PHIL1 ===================================} + +[PHI1_HP:PHIL1] +Tu peux lancer une grenade à détonateur quelque part puis l'y faire exploser au moment souhaité. + +[PHIL1_A:PHIL1] +Phil? + +[PHIL1_B:PHIL1] +COURS! + +[PHIL1_C:PHIL1] +Cours. + +[PHIL1_E:PHIL1] +Merde, Phil, tu bois cette saloperie? + +[PHIL1_F:PHIL1] +Putain, t'as pas besoin de la boire! + +[PHIL1_G:PHIL1] +Tu la renifles juste un bon coup et t'exploses direct! + +[PHIL1_H:PHIL1] +Ecoute, Phil, t'avais dit que tu pourrais t'occuper de ma puissance de feu... + +[PHIL1_I:PHIL1] +Pas de problème. + +[PHIL1_J:PHIL1] +Il y a ce mexicain qui trafique des armes, il a pas été réglo avec moi. + +[PHIL1_K:PHIL1] +Il fait sa tournée hebdomadaire, maintenant. + +[PHIL1_L:PHIL1] +Défonce l'arrière de son camion et sers-toi avant qu'il réagisse. + +[PHIL1_M:PHIL1] +Et rends-moi un service au passage tant que tu y es. + +[PHIL1_N:PHIL1] +Ensuite, règle-lui son compte... + +[PHI1_01:PHIL1] +~g~Va piquer les armes à l'arrière du camion du trafiquant. + +[PHI1_02:PHIL1] +~g~Le trafiquant a largué son chargement. Brise la caisse et ramasse l'artillerie. + +[PHI1_03:PHIL1] +~g~On dirait qu'ils ont demandé des renforts... + +[PHI1_04:PHIL1] +~g~Va maintenant finir les trafiquants d'armes survivants. + +[PHIL1_O:PHIL1] +Yeeeeeeehhaaaaa! + +[PHIL1_D:PHIL1] +N'approche jamais une allumette enflammée du boomshine de Phil Cassidy! + +{=================================== MISSION TABLE PHIL2 ===================================} + +[PHIL2_A:PHIL2] +Salut, Phil, ça roule? + +[PHIL2_B:PHIL2] +Saluut Tommyyyy! Comment ça va? Ca fait tellement longtemps... + +[PHIL2_C:PHIL2] +Je te jure que tu devrais arrêter le boomshine... + +[PHIL2_D:PHIL2] +Ca sent comme du décapant à peinture, j'ai les yeux qui brûlent... + +[PHIL2_E:PHIL2] +Ferme-la, Tommy + +[PHIL2_F:PHIL2] +et amène-toi par là. Y'a quelque chose que je veux te montrer... Quelque chose... + +[PHIL2_G:PHIL2] +Waou! Putain! C'est normal que je sente l'odeur d'ici? Je me sens tout drôle... + +[PHIL2_H:PHIL2] +T'inquiète pas de l'odeur, Tommy, regarde juste ça. + +[PHIL2_I:PHIL2] +Putain de saloperies de piles bon marché! Il y en a d'autres sur le banc. + +[PHIL2_J:PHIL2] +TA-DAAA! + +[PHIL2_K:PHIL2] +Oh merde! + +[PHI2_01:PHIL2] +~g~Vite, emmène Phil à l'hosto! + +[PHI2_03:PHIL2] +~r~Phil Cassidy est mort! Maintenant, qui va nous filer des flingues à Liberty? + +[PHI2_05:PHIL2] +Pas à l'hosto, mec, c'est plein de flics et de vietcongs! + +[PHI2_06:PHIL2] +Il y a un ancien chirurgien de l'armée qui me doit quelques services et une tondeuse... + +[PHI2_07:PHIL2] +Il crêche à Little Havana, oh! Regarde! Un poisson géant! + +[PHI2_08:PHIL2] +Fais gaffe! Charlie sur la ligne des arbres! + +[PHI2_09:PHIL2] +C'est moi qui hallucine ou la route est vraiment toute molle? + +[PHI2_10:PHIL2] +Cuillère Brisée à Mère Poule, tu me reçois? + +[PHI2_11:PHIL2] +Spooney Wooney Woo Woo Woooo! + +[PHI2_12:PHIL2] +La mort m'appelle, mon garçon! + +[PHI2_13:PHIL2] +Des ailes noires qui battent tout autour... + +[PHI2_14:PHIL2] +C'est magnifique, mec, magnifique... Mais tellement froid... + +[PHI2_15:PHIL2] +10-4 On a un conducteur en état d'ivresse. + +[PHI2_04:PHIL2] +SANTE DE PHIL : + +[PHI_AS1:PHIL2] +CHEZ PHILS PLACE OK + +[PHI_AS2:PHIL2] +~g~De nouvelles armes sont en vente chez Phils Place. + +{=================================== MISSION TABLE PIZZA ===================================} + +[PIZ1_01:PIZZA] +~g~Va livrer ces pizzas. Tu dois les balancer aux clients. Roule à côté d'eux pour lancer les pizzas. + +[PIZ1_02:PIZZA] +~g~T'as balancé toutes tes pizzas, retourne en chercher d'autres. + +[PIZ1_05:PIZZA] +~g~Tu as 5 minutes pour livrer les commandes avant que que les clients n'appelent une autre pizzeria. + +[PIZ1_07:PIZZA] +~r~T'as tué le client! T'es viré! + +[PIZ1_08:PIZZA] +~r~T'es à la bourre! T'es viré! + +[PIZ1_09:PIZZA] +~r~T'as bousillé notre bécane! T'es viré! + +[PIZ1_11:PIZZA] +Hé! Retourne sur ta bécane! + +[PIZ1_12:PIZZA] +Pizzas restantes : + +[PIZ1_06:PIZZA] +Appuie sur la~h~ ~k~~TOGGLE_SUBMISSIONS~~w~ quand tu es sur la moto pour annuler la mission. + +[PIZ1_13:PIZZA] +Effectue ces livraisons en vitesse. + +[PIZ1_14:PIZZA] +Mon pote, un pizza pour toi! + +[PIZ1_15:PIZZA] +Allez, mon vieux, livre ça en vitesse. + +[PIZ1_16:PIZZA] +Qu'est-ce que t'attends, mon vieux? T'as des pizzas à livrer! + +[PIZ1_17:PIZZA] +Je sais que tu voulais pas faire ce boulot, mais je m'en tape! + +[PIZ1_18:PIZZA] +Va livrer ça. + +[PIZ1_19:PIZZA] +On a besoin de livrer ça. + +[PIZ1_20:PIZZA] +Allez, mon vieux, livre ça ou t'es viré! + +[PIZ1_21:PIZZA] +Il y a des gens qui attendent, mon vieux. + +[PIZ1_22:PIZZA] +Mais qu'est-ce que t'attends? Faut livrer ça! + +[PIZ1_23:PIZZA] +Va livrer cette saloperie de bouffe! + +[PIZ1_24:PIZZA] +Les clients ont les crocs, mon vieux! + +[PIZ1_25:PIZZA] +Tu peux prendre ça, mon vieux? + +[PIZ1_26:PIZZA] +Tiens, va livrer ça en vitesse, amigo. + +[PIZ1_27:PIZZA] +Magne-toi, on est débordés. Va livrer ça! + +[PIZ1_28:PIZZA] +Encore toi? Qu'est-ce que t'attends? Va livrer ça! + +[PIZ1_29:PIZZA] +Perds pas de temps, cette fois-ci. + +[PIZ1_30:PIZZA] +Espèce de bon à rien! Va livrer ça en vitesse! + +[PIZ1_31:PIZZA] +T'auras jamais de promotion si tu traînes encore ce coup-ci. + +[PIZ1_32:PIZZA] +~r~La pizza est trop chaude ou quoi? + +[PIZ1_33:PIZZA] +~g~Retourne au restaurant pour d'autres commandes. + +[PIZ1_34:PIZZA] +~g~Pizza livrée. Voilà ton fric. + +[PIZ_WON:PIZZA] +Mission Pizza terminée. votre max de Santé passe à 150. + +{=================================== MISSION TABLE PORN1 ===================================} + +[POR1_A:PORN1] +Action + +[POR1_B:PORN1] +Ooooh! C'est un sacré morceau! + +[POR1_C:PORN1] +30 centimètres, c'est la taille réglementaire, bébé. + +[POR1_D:PORN1] +COUPEZ! Qui c'est cet abruti? Toi! Toi! T'es dans le champ? POURQUOI? + +[POR1_E:PORN1] +Qu'est-ce que c'est que ces conneries? + +[POR1_F:PORN1] +Des aliens? Des cannes à pêche? + +[POR1_G:PORN1] +J'ai jamais vu un requin aussi gros... + +[POR1_H:PORN1] +Faut que tout ça dégage! + +[POR1_I:PORN1] +Pourquoi t'es dans le métier, connard? + +[POR1_J:PORN1] +Hein? + +[POR1_K:PORN1] +Ben, pour les chattes, évidemment! Alors, qu'est-ce que c'est que ça? + +[POR1_L:PORN1] +Ceci est mon art - SECURITE! + +[POR1_M:PORN1] +Ecoute-moi, trou du cul arrogant, tu m'appartiens maintenant. Tout m'appartient ici. + +[POR1_N:PORN1] +On va révolutionner cet endroit... + +[POR1_O:PORN1] +Je vais faire ta fortune... + +[POR1_P:PORN1] +Oh. Tu es... Tu... es Tommy Vercetti? Mais je croyais que tu étais... + +[POR1_Q:PORN1] +Exactement... + +[POR1_R:PORN1] +Va y avoir quelques changements dans le coin et on va commencer à se faire des ronds. + +[POR1_S:PORN1] +Au fait, as-tu jamais pensé à... + +[POR1_T:PORN1] +Mais d'abord, on va avoir besoin de quelques putes bien foutues... + +[POR1_U:PORN1] +Ouais, les filles c'est bien mais toi... waou! + +[POR1_02:PORN1] +~g~Bute le mac de Candy, puis retourne la récupérer. + +[POR1_04:PORN1] +Salut, Candy. Je cherche des actrices, ça te branche? + +[POR1_05:PORN1] +Et comment! Mais faut causer à mon agent... + +[POR1_06:PORN1] +Putain qu'est-ce que tu fous? + +[POR1_07:PORN1] +Tu aurais mieux fait de rester à la maison, aujourd'hui! + +[POR1_7B:PORN1] +J'arrive pas à croire ce trou du cul! + +[POR1_08:PORN1] +Salut, Mercedes! + +[POR1_09:PORN1] +Salut, Tommy! Tu viens faire la fête? + +[POR1_10:PORN1] +Non, pas maintenant, chérie. Ca te dirait de faire du cinéma? + +[POR1_11:PORN1] +Evidemment! Mais seulement si c'est sordide et bon marché! + +[POR1_13:PORN1] +~g~Emmène les filles au studio y retrouver Steve. + +[POR1_14:PORN1] +T'es engagé! + +[POR1_15:PORN1] +Hé tommy, tu viens pour un bout d'essai? + +[POR1_17:PORN1] +Waou! Sympa le requin! + +[POR1_18:PORN1] +~r~Mercedes est morte! + +[POR1_20:PORN1] +Tommy! Où tu vas? Reviens ici! + +[POR1_21:PORN1] +Où tu vas? + +[POR1_22:PORN1] +Tommy, quand c'est qu'on va pouvoir être un peu seuls ensemble? + +[POR1_01:PORN1] +~g~Candy Suxxx serait parfaite pour un premier rôle! + +[POR1_12:PORN1] +~g~Emmène Candy avec toi pour rencontrer Mercedes. + +[POR1_16:PORN1] +Plus tard, peut-être, mon chou... + +[POR1_24:PORN1] +~g~Retourne chercher Candy. + +[POR1_25:PORN1] +~g~Tu as laissé Candy. Retourne la chercher! + +[POR1_23:PORN1] +~g~Candy va s'occuper de l'affaire dans le ~h~Centre~g~. + +[POR1_26:PORN1] +~g~Voilà Candy. On dirait qu'elle a encore eu rendez-vous avec le représentant du Congrès, Alex Shrub. + +[POR1_27:PORN1] +Allez, partons d'ici! + +[POR1_28:PORN1] +Tommy, sois prudent! Mes implants ne sont pas encore assurés! + +[POR1_29:PORN1] +Tu appelles ça conduire? + +[POR1_30:PORN1] +Je pourrais plus faire du porno après ça! + +[POR1_31:PORN1] +Quoi? T'essaies de me tuer ou quoi? Je croyais que c'était moi, la star! + +{=================================== MISSION TABLE PORN2 ===================================} + +[POR2_A:PORN2] +Comment ça se passe, Steve? + +[POR2_B:PORN2] +Eh ben, Candy est faite pour le rôle et la nouvelle fille est infatigable! + +[POR2_C:PORN2] +Elle s'est tapé la moitié du casting et des techniciens avant même que j'ai fini de régler les éclairages! + +[POR2_D:PORN2] +Enfin, demain, on va en extérieurs pour tourner les scènes du bateau... + +[POR2_E:PORN2] +Les scènes du bateau? Quelles scènes du bateau? + +[POR2_F:PORN2] +Les pêcheurs sont dans les filets de la passion quand ce requin géant arrive... + +[POR2_G:PORN2] +Qu'est-ce que j'avais dit à propos du requin géant? + +[POR2_H:PORN2] +J'avais dit : PAS DE REQUIN GEANT! Ok? + +[POR2_I:PORN2] +Laisse simplement les caméras braquées sur le pieu! + +[POR2_J:PORN2] +Ok ok, Tommy, j'avais bien le droit d'essayer... + +[POR2_K:PORN2] +Les prospectus sont imprimés? + +[POR2_L:PORN2] +Ouais, mais personne va nous laisser distribuer ces trucs, je veux dire... + +[POR2_M:PORN2] +Ils sont un peu trop, hum, enfin, ils manquent un peu de poésie... + +[POR2_N:PORN2] +T'en fais pas pour ça! + +[POR2_O:PORN2] +J'ai mes propres plans pour la distribution... + +[POR2_P:PORN2] +OK. Hé, Candy, amène-toi dans ma caravane! + +[POR2_01:PORN2] +~g~Il y a un hydravion à l'arrière des studios qui servait pour la propagande des vieux films. + +[POR2_02:PORN2] +~g~Atteins l'un des points de contrôle pour commencer à larguer les prospectus. + +[POR2_03:PORN2] +~g~Largue les prospectus tout le long du chemin jusqu'au dernier point de contrôle. + +[POR2_04:PORN2] +~r~NIVEAU DE CARBURANT BAS! + +[POR2_05:PORN2] +Utilise-le pour distribuer les prospectus dans la ville. + +[DILDO:PORN2] +Kérosène : + +[POR2_Q:PORN2] +Oh, merde! + +[PORN2_9:PORN2] +~g~Il te reste ~1~ secondes pour retourner à une Skimmer avant la fin de la mission. + +{=================================== MISSION TABLE PORN3 ===================================} + +[POR3_A:PORN3] +Bon, c'est quoi le problème, maintenant? + +[POR3_B:PORN3] +Chuuuut! + +[POR3_C:PORN3] +Eh bien, après l'intime rencontre avec les envahisseuses nymphomanes, + +[POR3_D:PORN3] +notre jeune héros se retrouve incapable de penser à autre chose que cette énorme montagne phallique + +[POR3_E:PORN3] +et c'est là qu'on veut placer la scène avec la cuve de purée de patates, mais alors on... + +[POR3_F:PORN3] +Ces conneries m'intéressent pas! + +[POR3_G:PORN3] +Continuez simplement le boulot, continuez... + +[POR3_H:PORN3] +Hé, Tommy... + +[POR3_I:PORN3] +T'as évoqué un problème légal au téléphone... + +[POR3_J:PORN3] +Ah ouais! Le représentant du Congrès Alex Shrub a pris le train électoral en marche à la pêche des votes puritains... + +[POR3_K:PORN3] +La rumeur laisse entendre qu'il soutient des mesures de restriction contre... + +[POR3_L:PORN3] +Disons... les formes les plus charnelles de la grande industrie cinématographique de ce pays. + +[POR3_M:PORN3] +Super. + +[POR3_N:PORN3] +Candy! Tu connais Shrub. + +[POR3_O:PORN3] +Vous faites des trucs bizarres tous les deux? + +[POR3_P:PORN3] +Oh oui! Oui! Oh oui! Oui oui oui ouuuuuuuuuuuuuuuuuui! + +[POR3_Q:PORN3] +Dis-moi que t'as eu ça... + +[POR3_R:PORN3] +Est-ce que ça faisait partie du... euh... Ou bien elle parlait à... + +[POR3_S:PORN3] +Impossible à dire... Dans tous les cas... + +[POR3_T:PORN3] +Tu ferais mieux de la suivre après le tournage + +[POR3_U:PORN3] +et voir si elle te mènera pas à leur nouveau nid d'amour. + +[POR3_V:PORN3] +T'as un appareil photo? + +[POR3_X:PORN3] +Hé, file-lui un appareil photo! + +[POR3_02:PORN3] +~r~Tu as tué le représentant du Congrès! T'auras du mal à le faire chanter, maintenant! + +[POR3_03:PORN3] +~r~Tu as alerté la sécurité du représentant du Congrès, ils vont le faire sortir tout de suite. + +[POR3_04:PORN3] +Candy, tu peux m'appeler Martha? + +[POR3_05:PORN3] +Oh Alex, enfin, je veux dire Martha. Comme tu veux... + +[POR3_06:PORN3] +Martha, il y a quelqu'un qui regarde... C'est pervers... + +[POR3_07:PORN3] +Vous là-bas, donnez-moi cet appareil photo! + +[POR3_01:PORN3] +~g~Suis la ~h~Stretch~g~ de Candy. + +[POR3_15:PORN3] +~r~T'as bousillé la Stretch de Candy! + +[POR3_17:PORN3] +~g~Retourne aux Porn Studios avec la pellicule. + +[POR3_19:PORN3] +~r~T'es à court de pellicule! + +[POR3_21:PORN3] +~g~T'as perdu la Stretch de Candy! + +[POR3_22:PORN3] +~g~Le WK Chariot Hotel de l'autre côté de ce balcon devrait être idéal, comme point de vue. + +[POR3_23:PORN3] +~g~Il y a une porte latérale qui te permettra d'accéder à l'hôtel. + +[POR3_08:PORN3] +Appuie sur la~h~ ~k~~PED_LOCK_TARGET~ ~w~et maintiens-la enfoncée pour ~h~cadrer~w~ la photo. + +[POR3_09:PORN3] +Appuie sur la~h~ ~k~~PED_LOCK_TARGET~ ~w~et maintiens-la enfoncée pour ~h~cadrer~w~ la photo. + +[POR3_10:PORN3] +Appuie sur la~h~ ~k~~PED_SNIPER_ZOOM_IN~ ~w~pour faire un ~h~zoom avant~w~ et sur la~h~ ~k~~PED_SNIPER_ZOOM_OUT~ ~w~pour faire un ~h~zoom arrière~w~. + +[POR3_11:PORN3] +Appuie sur la~h~ ~k~~PED_SNIPER_ZOOM_IN~ ~w~pour faire un ~h~zoom avant~w~ et sur la~h~ ~k~~PED_SNIPER_ZOOM_OUT~ ~w~pour faire un ~h~zoom arrière~w~. + +[POR3_12:PORN3] +Appuie sur la~h~ ~k~~PED_FIREWEAPON~ ~w~pour prendre une photo. + +[POR3_13:PORN3] +Appuie sur la~h~ ~k~~PED_FIREWEAPON~ ~w~pour prendre une photo. + +[POR3_14:PORN3] +Appuie sur la~h~ ~k~~PED_FIREWEAPON~ ~w~pour prendre une photo. + +[POR3_20:PORN3] +~g~Si tu as besoin de te déplacer, utilise la ~h~Sparrow~g~ garée derrière. + +[POR3_16:PORN3] +~g~Il te faut trois bons clichés d'Alex Shrub avec Candy pour le faire chanter. + +[POR3_24:PORN3] +PHOTOS PRISES : + +{=================================== MISSION TABLE PORN4 ===================================} + +[POR4_A:PORN4] +Je suis désolée, mais je ne peux pas avaler maintenant! + +[POR4_B:PORN4] +Oh, ALLEZ! Chérie! + +[POR4_C:PORN4] +Il décharge comme une baleine, par pitié + +[POR4_D:PORN4] +comment tu fais pour ne pas saisir le rôle? + +[POR4_E:PORN4] +Mais Stevie... + +[POR4_F:PORN4] +Comment va mon réalisateur vedette? + +[POR4_G:PORN4] +Ah, mon pote, la lutte incessante entre l'intégrité artistique et + +[POR4_H:PORN4] +l'éjaculation sous toutes ses formes est loin d'être terminée. + +[POR4_I:PORN4] +Et avant que tu me poses la question, je t'informe que les quatre vidéos sortiront dans les... + +[POR4_J:PORN4] +Chérie, veux-tu garder l'anaconda dans le champ, S'IL TE PLAIT. + +[POR4_K:PORN4] +Il prend plus de l'heure que toi! + +[POR4_L:PORN4] +Oh, pardon, Steve... + +[POR4_M:PORN4] +Je me disais, faudrait monter un gros coup de pub pour la promo du film. + +[POR4_N:PORN4] +Un truc qui aurait vraiment de l'impact sur la ville... T'as pas une idée? + +[POR4_O:PORN4] +Eh ben, autrefois, on faisait des galas, + +[POR4_P:PORN4] +avec des vedettes, des limousines, la nuit éclairée par les projos... + +[POR4_Q:PORN4] +Des projecteurs? J'ai une idée! + +[POR4_R:PORN4] +...Ouais, ouais, ouais! Le petit numéro de paillettes, les limousines, ah, les premières! + +[POR4_S:PORN4] +Oh oui, chère madaaaame, bien sûr, chère madaaaame... + +[POR4_T:PORN4] +et la presse, et le barrage de lumières... + +[POR4_01:PORN4] +~g~Va dans le ~y~Centre~g~ et ajuste le projecteur sur le toit du bâtiment. + +[POR4_02:PORN4] +~g~Une bécane rapide est nécessaire pour sauter de toit en toit. L'agent de sécurité a l'habitude de venir en ~y~PCJ 600~g~ au boulot... + +[POR4_03:PORN4] +~g~Faut que tu montes sur les toits. Il devrait y avoir un ascenceur dans l'un des immeubles de bureaux. + +[POR4_06:PORN4] +~g~Retourne à l'immeuble de bureaux du bas si tu as besoin d'accéder à nouveau au toit. + +[POR4_07:PORN4] +~g~Tu as besoin d'une bécane pour sauter de bâtiment en bâtiment! + +[POR4_08:PORN4] +~g~Fonce à travers la fenêtre pour commencer. Tu as jusqu'à 07:00 avant que la lumière du jour ne risque de te faire découvrir. + +[POR4_09:PORN4] +~g~Les marqueurs t'indiquent sur quel bâtiment sauter ensuite. + +[POR4_10:PORN4] +~r~Il fait maintenant trop clair pour monter sans te faire voir. + +[POR4_11:PORN4] +Retourne à l'escalier si tu as à nouveau besoin d'accéder au toit. + +[POR4_05:PORN4] +~g~Ces escaliers mènent à un bureau. + +[POR_AS1:PORN4] +STUDIO DE CINEMA OK + +[POR_AS2:PORN4] +~g~Inter Global Films génère dorénavant un revenu de $~1~ maximum. Pense à récupérer le fric régulièrement. + +{=================================== MISSION TABLE PROT1 ===================================} + +[PRO1_B:PROT1] +Je supporte pas ce style. Tommy, qu'est-ce que t'en penses? Qu'est-ce que tu dirais qu'on mette un bar dans... + +[PRO1_D:PROT1] +Ecoutez-moi... + +[PRO1_E:PROT1] +L'heure est venue de mettre la main sur cette ville! Le monde n'attend plus que nous! + +[PRO1_F:PROT1] +Faut qu'on commence à s'emparer de territoires! + +[PRO1_G:PROT1] +Faut qu'on fasse comprendre à tout Vice City qu'on est les nouveaux maîtres du jeu! Tu vois ce que je veux dire? + +[PRO1_I:PROT1] +Ce qu'il te faut, c'est une façade légale, Tommy! Une affaire officielle! Ca m'a jamais rapporté d'emmerdes! + +[PRO1_J:PROT1] +Faut qu'on commence à utiliser des balaises ou autant faire une croix sur tout le boulot qu'on s'est tapé jusque là. + +[PRO1_K:PROT1] +Les commerces locaux savent que Diaz est mort et ils refusent de payer pour leur protection! + +[PRO1_L:PROT1] +Ah. On pourrait essayer la corruption... + +[PRO1_M:PROT1] +La corruption! Mon cul, la corruption! Je vais vous montrer comment on leur fait peur! + +[PRO1_01:PROT1] +~g~Défoule-toi sur les devantures des magasins et les proprios supplieront pour être protégés. + +[PRO1_03:PROT1] +~r~C'était supposé être un raid éclair, pas une pause café. + +[PRO1_04:PROT1] +Mon gagne-pain est détruit! + +[PRO1_05:PROT1] +Tout est en ruines! + +[PRO1_06:PROT1] +Je paye le prix fort pour ma protection! + +[PRO1_07:PROT1] +Ma belle vitrine! + +[PRO1_08:PROT1] +Mon magasin! Mon beau magasin! + +[PRO1_09:PROT1] +Vercetti. Souvenez-vous de mon nom... + +[PRO1_10:PROT1] +Je règne désormais sur cette ville. MOI! + +[BUYP1:PROT1] +Tu peux désormais acheter des propriétés dans certaines zones de la carte. + +[BUYP2:PROT1] +Si tu vois un marqueur vert sur une propriété, c'est que tu peux l'acheter. + +[PRO1_N:PROT1] +Je reviens dans cinq minutes... + +[PRO1_11:PROT1] +~g~Va au ~y~Centre commercial de North Point~g~ dans ~y~Vice Point~g~. + +[PRO1_12:PROT1] +~g~Brise les vitrines de chaque magasin et les proprios supplieront pour une nouvelle protection. + +[PRO1_A:PROT1] +Ah, faut qu'on redécore cet endroit, faut qu'on lui donne un air plus vieux. + +[PRO1_C:PROT1] +T'es mon avocat, Rosenberg. Pas mon décorateur. Compris? + +[BUYP3:PROT1] +Tiens-toi sur le marqueur, puis appuie sur la touche ~h~~k~~PED_ANSWER_PHONE~~w~ pour acheter cette propriété. + +[PRO1_13:PROT1] +~g~Tu as cinq minutes pour tous les avoir. + +{=================================== MISSION TABLE PROT2 ===================================} + +[PRO2_A:PROT2] +C'est quoi l'problème? + +[PRO2_B:PROT2] +Un bar refuse de payer. + +[PRO2_C:PROT2] +Le boss considère qu'il est sous la protection d'un gang de voyous du coin... + +[PRO2_D:PROT2] +Mais t'en fais pas, Tommy, je peux m'en occuper. + +[PRO2_E:PROT2] +C'est ça que t'appelles t'en occuper? + +[PRO2_F:PROT2] +Vous deux, levez vos culs de là... + +[PRO2_G:PROT2] +Allons-y. + +[PRO2_01:PROT2] +~g~Liquide les gardes qui protègent le Front Page Bar et trouve qui les a approvisionnés. + +[PRO2_10:PROT2] +~g~Il y en a deux de plus qui se sont fait la malle. Finis-en avec eux. + +[PRO2_11:PROT2] +Montez dans la caisse, bons à rien. + +[PRO2_02:PROT2] +Ta protection a besoin d'être légèrement renforcée. + +[PRO2_03:PROT2] +Ah, non, pas encore! J'en ai marre! + +[PRO2_04:PROT2] +Ces imbéciles opèrent dans le quartier à partir de DBP Security. + +[PRO2_05:PROT2] +Réglez la question entre vous, les mecs. + +[PRO2_06:PROT2] +Bon, à plus tard. + +[PRO2_07:PROT2] +Ouais, ouais, c'est ça. + +[PRO2_08:PROT2] +~g~Les abrutis du DBP Security sauront que t'y vas, alors fonce et chope-les avant qu'ils décampent! + +[PRO2_09:PROT2] +~g~Va parler au proprio du Front Page Bar. + +{=================================== MISSION TABLE PROT3 ===================================} + +[PRO3_A:PROT3] +Pauvre crétin! A quoi tu pensais? + +[PRO3_B:PROT3] +Tu réalise ce qui aurait pu arriver? + +[PRO3_C:PROT3] +On a failli tous se faire avoir! + +[PRO3_D:PROT3] +Le retardateur a dû déconner... + +[PRO3_E:PROT3] +Cet endroit était prêt à sauter comme une usine de feux d'artifice. + +[PRO3_F:PROT3] +Et pis quelqu'un a rencardé les flics... + +[PRO3_G:PROT3] +C'est quoi le problème, les mecs? + +[PRO3_H:PROT3] +Mike devait foutre le feu quelque part dans le centre commercial, + +[PRO3_I:PROT3] +mais il a foiré son coup et maintenant, les flics grouillent, là-bas. + +[PRO3_J:PROT3] +Faut qu'on récupère notre bordel et qu'on s'tire de là! + +[PRO3_K:PROT3] +Du calme, vous deux, laissez-moi réfléchir une seconde! + +[PRO3_L:PROT3] +Tommy Vercetti a pas l'habitude de mettre les bouts comme ça... + +[PRO3_M:PROT3] +Les flics vont passer ce bâtiment au peigne fin, pas vrai? + +[PRO3_N:PROT3] +Mais ça prend du temps... + +[PRO3_O:PROT3] +On va entrer et foutre le feu nous-mêmes! + +[PRO3_P:PROT3] +Ouais, mais... + +[PRO3_Q:PROT3] +Il y a que les flics qui peuvent approcher à moins d'un kilomètre de là-bas! + +[PRO3_R:PROT3] +Alors, on ira en tant que flics! + +[PRO3_S:PROT3] +On va se dégotter des uniformes et il nous faut aussi une bagnole de patrouille. + +[PRO3_T:PROT3] +Tout ça à cause de toi, Mike. + +[PRO3_U:PROT3] +Je suis désolé. + +[PRO3_V:PROT3] +J'ai trouvé! + +[PRO3_W:PROT3] +Tout ce qu'on a à faire, c'est appâter les flics avec un doigt levé, + +[PRO3_X:PROT3] +de les prendre au piège + +[PRO3_Y:PROT3] +et de leur sauter dessus! + +[PRO3_Z:PROT3] +Super plan! En route! + +[PRO3_A1:PROT3] +OK. + +[PRO3_01:PROT3] +Ok Lance, attirons l'attention des flics! + +[PRO3_02:PROT3] +~g~Prends la bagnole des flics et va poser la bombe au Tarbrush Coffee Shop dans le centre commercial. + +[PRO3_03:PROT3] +~g~T'as laissé Lance derrière, va le récupérer! + +[PRO3_04:PROT3] +~g~Allons-y! + +[PRO3_05:PROT3] +~r~T'as buté Lance! + +[PRO3_07:PROT3] +~g~Tu as grillé ta couverture! Magne-toi de poser la bombe! + +[PRO3_09:PROT3] +Ligotons et bâillonnons-les! + +[PRO3_10:PROT3] +Aaah! Il me va parfaitement! + +[PRO3_11:PROT3] +Mais quand même un peu serré au niveau des couilles... + +[PRO3_12:PROT3] +Ah ouais? Le mien aussi, c'est pareil! + +[PRO3_13:PROT3] +Du calme, mon pote! Les flics conduisent pas aussi mal! + +[PRO3_14:PROT3] +Oublie pas... Faut sourire aux autres flics... + +[PRO3_15:PROT3] +Salut officier. Bel insigne, bel insigne... + +[PRO3_16:PROT3] +Tout doux, Lance. + +[PRO3_17:PROT3] +Ok, les retardateurs sont programmés, 5 secondes et ça saute! + +[PRO3_18:PROT3] +5 secondes? Putain faut dégager d'ici en vitesse! + +[PRO3_19:PROT3] +Maintenant, ils vont vraiment être fâchés... + +[PRO3_20:PROT3] +~g~Fais-toi suivre par deux flics dans le garage. + +[PRO3_21:PROT3] +~g~Tu dois avoir un indice de recherche pour que les flics te suivent dans le piège. + +[PRO3_22:PROT3] +~g~La porte est bloquée! Dégage le passage pour qu'elle puisse être refermée. + +[PRO3_23:PROT3] +~g~Marche sur le marqueur pour poser la bombe. + +[PRO3_24:PROT3] +~g~Tire-toi du café! + +[PRO_AS1:PROT3] +PROTECTION OK + +[PRO_AS2:PROT3] +~g~Vercetti Estate génère désormais un revenu de $~1~ maximum. Pense à récupérer le fric régulièrement. + +[PRO3_08:PROT3] +~g~ Retourne à ~h~Vercetti Estate~g~ sur ~h~Starfish Island~g~. + +{=================================== MISSION TABLE RACES ===================================} + +[RACES_2:RACES] +~g~Il te faut un véhicule, c'est pas une course à pied! + +[RACES_3:RACES] +3... 2... 1... PARTEZ! + +[RACES_8:RACES] +~r~Tu n'as pas gagné la course! + +[RACES00:RACES] +Course ~1~ : + +[RACES01:RACES] +Terminal Velocity + +[RACES02:RACES] +Ocean Drive + +[RACES03:RACES] +Border Run + +[RACES04:RACES] +Capital Cruise + +[RACES05:RACES] +Virée! + +[RACES06:RACES] +Endurance V.C. + +[RACES07:RACES] +Participation : ~1~$ + +[RACES08:RACES] +Meilleur temps : ~1~:~1~ + +[RACES09:RACES] +Meilleure perf : 1er + +[RACES10:RACES] +Meilleure perf : 2e + +[RACES11:RACES] +Meilleure perf : 3e + +[RACES12:RACES] +Meilleure perf : 4e + +[RACES13:RACES] +Longueur circuit : ~1~.~1~ km + +[RACES15:RACES] +Meilleur temps : NA + +[RACES16:RACES] +Meilleure perf : NA + +[RACES18:RACES] +TU AS GAGNE : $~1~ + +[RACES19:RACES] +Tu n'as pas assez de moyens pour participer. + +[RACES22:RACES] +Meilleur temps : ~1~:0~1~ + +[RACES23:RACES] +Longueur circuit : ~1~.~1~ miles + +[RACES_1:RACES] +~g~Trouve un bolide et rends-toi sur la grille de départ. + +[RACEHLP:RACES] +~w~Appuie sur la~h~ ~k~~PED_SPRINT~~w~ pour commencer la course choisie. Appuie sur la~h~ ~k~~VEHICLE_ENTER_EXIT~~w~ pour quitter. + +{=================================== MISSION TABLE RCHELI1 ===================================} + +[WRECKED:RCHELI1] +Ton véhicule est mort! + +[RCH1_4:RCHELI1] +Points de passage restants : + +[RCH1_6:RCHELI1] +~g~Utilise l'hélico télécommandé pour passer les points de passage à travers l'aéroport. + +[RCH1_7:RCHELI1] +~g~Il y a 20 points de passage en tout. + +[RCH1_12:RCHELI1] +~g~L'hélicoptère télécommandé est presque hors de portée! + +[RCH1_13:RCHELI1] +~r~L'hélicoptère radiocommandé est hors de portée! + +[RCH1_8:RCHELI1] { reVC update } +~g~Si tu veux quitter cette mission, appuie sur la ~h~~k~~VEHICLE_FIREWEAPON~~g~ pour faire exploser ton hélicoptère radiocommandé. + +{=================================== MISSION TABLE RCPLNE1 ===================================} + +[RCPL1_4:RCPLNE1] +~g~Fais une COURSE DE POINTS DE PASSAGE avec 3 autres avions télécommandés. + +[RCPL1_5:RCPLNE1] +~g~Passe par les points de passage dispersés dans Vice City. + +[RCPL1_6:RCPLNE1] { reVC update } +~g~Si tu veux quitter cette mission, appuie sur la ~h~~k~~VEHICLE_FIREWEAPON~~g~ pour faire exploser ton avion radiocommandé. + +[RCPL1_8:RCPLNE1] +~g~Ton avion RC va sortir du périmètre! + +[RCPL1_9:RCPLNE1] +~r~Ton avion RC est sorti du périmètre! + +{=================================== MISSION TABLE RCRACE1 ===================================} + +[RCR1_4:RCRACE1] +Tours restants : + +[RCR1_1:RCRACE1] +~g~Fais une course de points de passage contre 3 autres voitures RC. + +[RCR1_2:RCRACE1] +~g~Sois le premier à boucler 2 tours de circuit pour gagner! + +[RCR1_6:RCRACE1] +~g~Ta voiture RC va sortir du périmètre! + +[RCR1_7:RCRACE1] +~r~Ta voiture RC est sortie du périmètre! + +{=================================== MISSION TABLE ROCK1 ===================================} + +[RBM1_A:ROCK1] +Okaaaaaaaaaay! + +[RBM1_B:ROCK1] +Grandiose! Vraiment génial! + +[RBM1_D:ROCK1] +Hé, t'as déjà rencontré les Love Fist? + +[RBM1_E:ROCK1] +Non, mais j'adore votre musique! + +[RBM1_F:ROCK1] +Laisse-moi te présenter au groupe. + +[RBM1_G:ROCK1] +Voici P, Percy, Dick et Willy est aux chiottes et Jezz était dans la cabine tout à l'heure... + +[RBM1_H:ROCK1] +Les mecs, je veux vous présenter un bon ami à moi. + +[RBM1_I:ROCK1] +Voici Tommy. Ca fait un bail qu'on se connait. + +[RBM1_J:ROCK1] +Ok, mon pote. + +[RBM1_K:ROCK1] +Ah, euh... C'est quoi déjà, ton nom? + +[RBM1_L:ROCK1] +Arrête-ça Jezz tu t'en souviens, + +[RBM1_M:ROCK1] +et essaye pas de jouer au plus malin avec moi, + +[RBM1_N:ROCK1] +je suis trop intelligent, mon mignon! + +[RBM1_O:ROCK1] +Tu vois, le problème, Tom, c'est que ces garçons ont besoin d'aide. + +[RBM1_P:ROCK1] +Ils n'ont pas beaucoup de contacts ici et ils savent pas à qui s'adresser. + +[RBM1_Q:ROCK1] +On a besoin de drogue, mon pote! + +[RBM1_R:ROCK1] +Faut qu'on réveille la vieille fureur de Love Fist, tu vois? + +[RBM1_S:ROCK1] +Et bien, ici, c'est Vice City, quel est le problème? + +[RBM1_U:ROCK1] +Du Love Juice, mec! + +[RBM1_V:ROCK1] +Du Love Juice? + +[RBM1_W:ROCK1] +Ouais, deux doses de boomshine, une dose de trompette, 5 bombes fizz et un litre d'essence. + +[RBM1_X:ROCK1] +Tu peux nous aider, mon pote? + +[RBM1_Y:ROCK1] +Ah, ça rendrait tellement service aux garçons! + +[RBM1_Z:ROCK1] +Tu peux faire ça pour les garçons, pas vrai? + +[RBM1_8:ROCK1] +~r~Mercedes est morte! + +[RBM1_10:ROCK1] +~r~Imbécile! Tu as détruit la marchandise! + +[RBM1_13:ROCK1] +~g~Amène le Love Juice et Mercedes au groupe avant qu'ils n'entrent sur scène. + +[RBM1_15:ROCK1] +~r~Tu as perdu le dealer, notre cash et la drogue! + +[RBM1_17:ROCK1] +~g~Tue le dealer et prends la drogue! + +[MOB_07A:ROCK1] +Salut, mon vieux, les gars auraient besoin d'un peu de compagnie, si tu vois ce que je veux dire... + +[MOB_07B:ROCK1] +Je connais justement la fille qu'il faut. + +[ROK1_5:ROCK1] +Salut Mercedes! + +[ROK1_6:ROCK1] +Salut, Tommy. Comment vas-tu? + +[ROK1_7:ROCK1] +Ca roule. Ecoute, les Love Fist, ça te branche? + +[ROK1_8:ROCK1] +Ok, mais c'est un service qu'il faudra me retourner... + +[RBM1_14:ROCK1] +~g~T'as besoin d'une bagnole ou d'une bécane! + +[RBM1_1:ROCK1] +~g~Va prendre Mercedes à son appartement. + +[RBM1_12:ROCK1] +~g~Va récupérer les ingrédients du Love Juice chez le dealer. + +[ROK1_2:ROCK1] +PLUS NECESSAIRE + +[ROK1_3:ROCK1] +PLUS NECESSAIRE + +[MERC_39:ROCK1] +A plus tard, champion. + +[RBM1_C:ROCK1] +Hé, Tommy! Content que t'aies réussi! + +[ROK1_1A:ROCK1] +Tu cherches quelque chose de spécial? J'ai ce qu'il te faut! + +[ROK1_9:ROCK1] +Merci pour le pognon, suce boules! + +[RBM1_T:ROCK1] +On a besoin de Love Juice, mec, tu vois? + +{=================================== MISSION TABLE ROCK2 ===================================} + +[RBM2_A:ROCK2] +Tommy, mon pote, je suis content de te voir! + +[RBM2_B:ROCK2] +Qu'est-ce qui se passe? + +[RBM2_C:ROCK2] +Des mauvaises vibrations, Tommy... + +[RBM2_E:ROCK2] +Il y a ce chat, on le connaît pas, mais lui il nous connaît. + +[RBM2_F:ROCK2] +C'est comme ce chat. Il nous connaît tous. + +[RBM2_G:ROCK2] +Il sait que Willy a un faible pour les culottes de ses femmes, hé hé! + +[RBM2_H:ROCK2] +Ou que Percy aime bien Duran Duran! + +[RBM2_K:ROCK2] +Ouais, le truc de la fusée d'amour, c'est vrai. Mais écoute, ce chat... + +[RBM2_L:ROCK2] +ouais, ouais, ce gars, il veut voir les Love Fist morts! + +[RBM2_M:ROCK2] +Morts, Tommy. + +[RBM2_N:ROCK2] +Les Love Fist disparus. Tu sais ce qu'on dit, les meilleurs meurent jeunes... + +[RBM2_O:ROCK2] +Tommy, faut que tu sauves les Love Fist! + +[RBM2_P:ROCK2] +On a une séance d'autographes dans deux heures et je pense que... + +[RBM2_Q:ROCK2] +Et les garçons pensent que le mec va essayer quelque chose de très vilain là-bas. + +[RBM2_1:ROCK2] +~g~Conduis la limousine à la séance d'autographes et essaye de démasquer le psychopathe . + +[RBM2_2:ROCK2] +~r~T'as bousillé la bagnole du groupe! + +[RBM2_3:ROCK2] +~g~Va à la séance d'autographes! + +[RBM2_4:ROCK2] +~g~Attrape le psychopathe! Ne le laisse pas s'échapper! + +[RBM2_5:ROCK2] +~r~Tu l'as perdu, imbécile! + +[RBM2_7:ROCK2] +~r~Les fans ont été attaqués, le psychopathe ne se montrera pas! + +[RBM2_8:ROCK2] +~r~Les agents de sécurité ont été attaqués, le psychopathe ne se montrera pas! + +[PSYCH_1:ROCK2] +Je veux voir les Love Fist morts! + +[PSYCH_2:ROCK2] +Les Love Fist ont gâché ma vie! + +[RBM2_I:ROCK2] +Ferme-la un peu. C'est pas parce que Jezz encule les moutons... + +[RBM2_R:ROCK2] +Oh, ferme-la! + +[RBM2_D:ROCK2] +Je plaisante pas. C'est pas des conneries, mec, tu piges? + +[RBM2_J:ROCK2] +C'est le truc de la fusée d'amour, tu sais? + +{=================================== MISSION TABLE ROCK3 ===================================} + +[RBM3_A:ROCK3] +Tommy! Le psychopathe est de retour! + +[RBM3_B:ROCK3] +Qu'est-ce qui se passe? + +[RBM3_C:ROCK3] +Ce psychopathe ne laissera jamais les Love Fist tranquilles! + +[RBM3_D:ROCK3] +Tu l'as pas tué, mon pote et il est revenu. + +[RBM3_E:ROCK3] +Ouais, ouais et le truc c'est que... + +[RBM3_F:ROCK3] +Le truc, c'est qu'on a besoin de quelqu'un de confiance pour conduire la limousine, + +[RBM3_G:ROCK3] +parce que ce timbré continue à nous menacer! + +[RBM3_I:ROCK3] +On chie tous dans nos frocs, mec. + +[RBM3_J:ROCK3] +Ok, les gars, du calme, je vais m'en occuper... + +[RBM3_K:ROCK3] +Normalement, j'ai mieux à foutre que de faire le taxi pour une bande d'Ecossais bisexuels bourrés, + +[RBM3_L:ROCK3] +mais dans votre cas, je vais faire une exception. + +[ROK3_03:ROCK3] +Autant pas faire dans la demi-mesure, alors. + +[ROK3_04:ROCK3] +Hé, Tommy, change la musique! + +[ROK3_08:ROCK3] +Ca commence à me faire chier, cette histoire. + +[ROK3_09:ROCK3] +Garde juste le pied au plancher! + +[ROK3_61:ROCK3] +Faut qu'on trouve la bombe! + +[ROK3_28:ROCK3] +Je vais bientôt jouer de la basse en enfer... + +[ROK3_30:ROCK3] +Que quelqu'un fasse quelque chose! + +[ROK3_32:ROCK3] +Ok, toi monsieur le dur à cuire, fais quelque chose! + +[ROK3_34:ROCK3] +Willy pourrait juste sucer le boomshine à la paille. + +[ROK3_37:ROCK3] +Passez une paille à Willy! + +[ROK3_41:ROCK3] +Quel fil, Tommy? + +[ROK3_42:ROCK3] +Le vert. + +[ROK3_43:ROCK3] +Y'en a pas de vert. Ou alors celui-la est vert? + +[ROK3_44:ROCK3] +Est-ce qu'un de ces fils te semble vert? + +[ROK3_49:ROCK3] +Ca fait des années que je te supporte! + +[ROK3_51:ROCK3] +Une grosse poufiasse hurlante... + +[ROK3_52:ROCK3] +Ouais! + +[ROK3_53:ROCK3] +Ferme-la et arrache un fil! + +[ROK3_54:ROCK3] +Lequel? + +[ROK3_55:ROCK3] +Celui-là... + +[ROK3_56:ROCK3] +Non! + +[ROK3_57:ROCK3] +Hé, on est ok, on a pas sauté, les potes! Tommy, mon pote bravo. Et Rock and Roll! + +[ROK3_58:ROCK3] +On a pas un concert où aller? Un racket à faire? Des fans à insulter? + +[ROK3_59:ROCK3] +LOVE FIST! + +[ROK3_60:ROCK3] +T'as fini avec cette bouteille? + +[RBM3_4:ROCK3] +~r~Tu as tué les Love Fist! + +[RBM3_6:ROCK3] +DETONATION : + +[RBM3_1:ROCK3] +~g~Conduis les Love Fist jusqu'au concert. + +[RBM3_2:ROCK3] +Si tu essayes de quitter la voiture tant que la bombe est armée, elle explosera. + +[RBM3_3:ROCK3] +Si la jauge de détonation devient pleine, la bombe explose. + +[RBM3_8:ROCK3] +Plus tu conduis vite, plus la jauge de détonation diminue. + +[RBM3_7:ROCK3] +~g~BOMBE DESAMORCEE! + +[ROK3_6A:ROCK3] +~g~Love Fist. Vous avez fini de polluer les ondes! + +[ROK3_6B:ROCK3] +~g~Je vous ai donné l'occasion de devenir mes amis. Je vais maintenant vous offrir celle de mourir! + +[ROK3_62:ROCK3] +alors on a pensé te montrer notre Temple du rock- + +[ROK3_63:ROCK3] +Que tu te fasses une idée de la fureur des Love Fist! + +[ROK3_64:ROCK3] +Ecoute-toi toi même, mon pote. C'est de la daube! + +[ROK3_65:ROCK3] +Hé, à tous les gosses, c'est un temple et nous sommes les prêtres! + +[ROK3_66:ROCK3] +Ouais, eh ben, si les gosses aiment leurs prêtres à moitié saouls et complètement sourds, + +[ROK3_67:ROCK3] +c'est leur problème. + +[ROK3_68:ROCK3] +Ah, merde, la bande de la cassette est encore bouffée. + +[ROK3_69:ROCK3] +Si ça continue, faudra jouer en live. + +[ROK3_70:ROCK3] +Ooooh merde! Mes intestins... + +[ROK3_74:ROCK3] +Ah, tiens, qu'est-ce que c'est que ça? Tommy, mets cette cassette. + +[ROK3_01:ROCK3] +Mon pote, c'est enfin l'heure d'un verre bien mérité. + +[ROK3_02:ROCK3] +La salle de concert est à une centaine de mètres plus loin. + +[ROK3_05:ROCK3] +Je me sens bizarre quand ma tête ne tambourine pas. + +[ROK3_07:ROCK3] +Tommy, faut que tu sauves le groupe! + +[ROK3_29:ROCK3] +Tommy, continue de foncer, mec! + +[ROK3_31:ROCK3] +Que quelqu'un fasse quelque chose... C'est quoi ces conneries? J'ai déjà vu des minettes plus courageuses. + +[ROK3_33:ROCK3] +Ecoute, mec. Moi, je joue d'un instrument, j'y connais rien aux bombes. + +[ROK3_35:ROCK3] +Ouais, j'ai entendu dire que t'étais doué pour ce genre de trucs! + +[ROK3_38:ROCK3] +Une paille? Hé, ici, c'est le bus de tournée des Love Fist! + +[ROK3_39:ROCK3] +Où c'est que tu veux que je trouve une paille? + +[ROK3_46:ROCK3] +J'aurais dû te larguer quand j'en avais l'occasion. + +[ROK3_47:ROCK3] +Capitaliste! + +[ROK3_48:ROCK3] +Arriviste! + +[ROK3_73:ROCK3] +Jezz écoute la cassette, + +[RBM3_9:ROCK3] +Si tu t'arrêtes ou conduis trop lentement, la jauge de détonation augmente. + +[ROK3_50:ROCK3] +Ferme-la, pauvre abruti! + +[ROK3_36:ROCK3] +Hé, j'étais raide défoncé cette nuit-là, et tu le sais bien! + +[RBM3_H:ROCK3] +Je chie dans mon froc, mec. Je veux ma maman! + +[ROK3_45:ROCK3] +Oh, je vois la mort dans les cartes! Tout est vert! + +[ROK3_6C:ROCK3] +~g~Si tu ralentis, la limousine explosera et les GROS TOCARDS CHEVELUS avec! + +[ROK3_71:ROCK3] +On doit continuer. Merci encore, Tommy! T'as assuré, mec! A plus! + +[ROK3_1:ROCK3] +Enfin, mec, voici l'heure d'un verre bien mérité. La salle est juste à quelques mètres d'ici. + +[ROK3_2:ROCK3] +Tu m'en sers un grand alors. Hé, Tommy, change de musique, mec. + +[ROK3_3:ROCK3] +Je pète les plombs si je secoue pas la tête. Hé, regarde, c'est quoi ça? Tommy, mets cette cassette. + +[ROK3_4:ROCK3] +Love Fist. Vous avez fini de polluer les ondes. Je vous ai donné une chance de devenir mes amis. + +[ROK3_5:ROCK3] +Maintenant vous allez mourir. Essayez de freiner et c'est la fin! Paf, plus de limousine et plus de GROS NASES CHEVELUS non plus. + +[ROK3_6:ROCK3] +Tommy, mec, tu dois sauver le groupe! J'en ai marre de ces conneries. Enlève pas ton pied de la pédale, mec! + +[ROK3_7:ROCK3] +On doit trouver la bombe! On peut pas continuer à rouler toute la journée? Ouais, quoi, on a plein de trucs à boire... + +[ROK3_8:ROCK3] +La bombe, elle serait pas dans le moteur? Va falloir qu'on s'arrête pour la désamorcer! Merde, mec, on va tous crever! J'vais m'bourrer la gueule! + +[ROK3_9:ROCK3] +Hé, on est dans la mouise, mon pote! Tu trouveras pas ta réponse dans la boisson! Tire-toi de là! + +[ROK3_10:ROCK3] +Hé, y'a des fils qui sortent de la bouteille de vodka! C'est pas de la vodka, c'est du BOOMSHINE! + +[ROK3_11:ROCK3] +AAAAAHHHHHH! Elle va exploser! AAAAAAAAAAAHHHHH!!!!!!!!!! + +[ROK3_12:ROCK3] +On m'avait prévenu que l'alcool me perdrait. J'ai déjà vu ça à la télé. Il suffit de tirer un de ces fils, mais lequel? J'en sais foutre rien, mec! + +[ROK3_13:ROCK3] +Putain, j'en sais rien! Willy, dis quelque chose. J'vais jouer de la basse en enfer. + +[ROK3_14:ROCK3] +Tommy, mec, continue de rouler à fond la caisse. Que quelqu'un fasse quelque chose! Ouais, c'est ça. + +[ROK3_15:ROCK3] +'Que quelqu'un fasse quelque chose'. Putain, c'est quoi ces conneries. J'ai connu des tantes plus braves. Vas-y le gros dur, fais quelque chose! + +[ROK3_16:ROCK3] +Ecoute, mec, je suis musicien moi, j'sais pas comment ça fonctionne les bombes. Willy a qu'à sucer le boomshine à la paille. + +[ROK3_17:ROCK3] +Ouais, on m'a dit que ça, tu savais faire. J'étais tout émoustillé ce soir-là, comme tu le sais! + +[ROK3_18:ROCK3] +T'as qu'à filer une paille à Willy! Une paille? C'est le bus de tournée des Love Fist! + +[ROK3_19:ROCK3] +Où est-ce que je vais bien pouvoir trouver une paille? Quel fil, Tommy? Le vert. Y'en a pas de vert. + +[ROK3_20:ROCK3] +Où peut-être qu'il est vert celui-là? T'en vois un vert, toi? + +[ROK3_21:ROCK3] +Oh non! On va crever! Je vois la vie en vert! J'aurais dû me débarrasser de toi qu'en j'en avais l'occasion. + +[ROK3_22:ROCK3] +Chasse la gloire! Capitaliste! Ca fait des années que je te soutiens. Ferme-là, tafiole! + +[ROK3_23:ROCK3] +Une grosse chialeuse, ouais, ferme-là et tire le putain de fil! Lequel? Celui-là... + +[ROK3_24:ROCK3] +NON! On est pas morts. On a pas sauté, mec! + +[ROK3_25:ROCK3] +Tommy, bien joué, mec. Rock and roll, mon pote! On a pas un concert à donner, nous? + +[ROK3_26:ROCK3] +Amasser du blé? Abuser des minettes? LOVE FIST! + +[ROK3_27:ROCK3] +T'as fini avec cette bouteille? + +{=================================== MISSION TABLE SERG1 ===================================} + +[TEX1_A:SERG1] +Entre et pose ton cul sur une des banquettes. + +[TEX1_B:SERG1] +Putain, mon père avait l'habitude de dire qu'à cheval donné, on ne regarde pas les dents et il l'a jamais fait! + +[TEX1_C:SERG1] +Tu veux une larme de bon vieux whisky? + +[TEX1_D:SERG1] +Non merci. + +[TEX1_E:SERG1] +Un mec qui aime avoir les idées claires, c'est bien. + +[TEX1_F:SERG1] +En ce moment, mes affaires tournent pas comme elles le devraient... + +[TEX1_G:SERG1] +C'est la merde! Et je veux nettoyer une partie de cette merde! T'es avec moi? + +[TEX1_H:SERG1] +Euh, ouais. + +[TEX1_I:SERG1] +En fait, j'ai besoin d'un mec obstiné pour me débarrasser d'une merde. + +[TEX1_J:SERG1] +T'as l'air du genre persuasif. + +[TEX1_K:SERG1] +La persuasion c'est ma spécialité. + +[TEX1_L:SERG1] +Bon, ce mec sera au country club du golf. + +[TEX1_M:SERG1] +Ils autorisent pas les flingues, alors ses gardes du corps en auront pas. + +[TEX1_N:SERG1] +Va me massacrer ce salopard. + +[TEX1_O:SERG1] +Tiens, voilà ta carte de membre, mais va falloir te trouver des fringues plus appropriées. + +[TEX1_2:SERG1] +~g~Maintenant, direction le Leaf Links Golf Club. + +[TEX1_0:SERG1] +~g~La cible est à ta portée, en train de jouer au golf. Fais en sorte que ce soit sa dernière partie! + +[TEX1_3:SERG1] +C'est qui ce mec? Occupez-vous de lui! + +[TEX1_6:SERG1] +Quel beau cul! + +[TEX1_7:SERG1] +C'est moi, ça? + +[TEX1_8:SERG1] +Chaque fois que tu ouvres ton caddie, tu reçois automatiquement un club de golf si ton emplacement d'arme de corps à corps est vide. + +[TEX1_9:SERG1] +Attrapez-le! + +[TEX1_10:SERG1] +Tuez-moi ce dingue! + +[TEX1_1:SERG1] +~g~Va chercher des fringues de golfeur chez Jocksport's. + +{=================================== MISSION TABLE SERG2 ===================================} + +[TEXEXIT:SERG2] +~g~Maintenant, casse-toi de Little Haiti! + +[TEX_2A:SERG2] +~g~Parfait! Ils t'ont repéré! + +[TEX_2B:SERG2] +~r~Imbécile! Les gens doivent VOIR un cubain faire le coup! + +[TEX_2C:SERG2] +~g~Va te trouver des fringues de cubain dans Little Havana! + +[TEX_2D:SERG2] +~g~Maintenant, liquide le chef de gang haïtien au salon funéraire Romero! + +[TEX2_A:SERG2] +Tommy, voici Donald Love. Donald, Tommy Vercetti, + +[TEX2_B:SERG2] +la dernière crapule à débarquer ici. + +[TEX2_C:SERG2] +Ouais... Euh... + +[TEX2_D:SERG2] +Donald, ferme-la et écoute, t'apprendras peut-être quelque chose. + +[TEX2_E:SERG2] +Bon. Il y a rien de mieux qu'une bonne guerre des gangs pour faire baisser les prix de l'immobilier. + +[TEX2_F:SERG2] +A part une catastrophe, peut-être, comme une peste biblique ou un truc dans le genre. + +[TEX2_G:SERG2] +Mais ça serait aller un peu trop loin, dans le cas présent. + +[TEX2_H:SERG2] +Tu piges, tête de gland? + +[TEX2_I:SERG2] +Un chef du gang haïtien est mort y'a pas longtemps. C'est apparemment un coup des Cubains, mais personne en est sûr. + +[TEX2_J:SERG2] +Alors on va arranger ça! Tu te déguises en Cubain + +[TEX2_K:SERG2] +et tu fonces foutre le bordel aux funérailles. Tu leur balances la sauce et tu dégages vite fait. + +[TEX2_L:SERG2] +T'as compris, Donald? + +[TEX2_M:SERG2] +C'est comme faire entrer le renard dans le poulailler, hein? + +[TEX2_N:SERG2] +Et puis on attend tranquillement que les prix s'écroulent. + +{=================================== MISSION TABLE SERG3 ===================================} + +[TEX3_A:SERG3] +Regarde un peu, mon pote. J'ai un problème et je compte sur ton aide. + +[TEX3_B:SERG3] +Je suis pas constructeur. + +[TEX3_C:SERG3] +Ben, je pensais surtout à tes talents de démolisseur. + +[TEX3_D:SERG3] +Bon, là, c'est le développement prévu, et ça, + +[TEX3_E:SERG3] +c'est la propriété sur laquelle on a des vues. + +[TEX3_F:SERG3] +T'essayes de me dire que ce nouveau bloc de bureaux est comme qui dirait sur le chemin. + +[TEX3_G:SERG3] +T'as tout compris. + +[TEX3_H:SERG3] +Bon, je vais aller faire un tour en ville pendant un moment + +[TEX3_I:SERG3] +et si la construction de ces bureaux devait faire face à de subits et insurmontables problèmes structurels, alors je... + +[TEX3_J:SERG3] +En tant qu'honnête citoyen, vous vous sentiriez obligé d'intervenir + +[TEX3_K:SERG3] +pour la réhabilitation d'un important secteur de la ville? + +[TEX3_L:SERG3] +Où on peut en trouver d'autres, des mecs dans ton genre? + +[TEX3_1:SERG3] +~g~Utilise l'hélico télécommandé pour transporter des bombes sur les quatre cibles du bâtiment que tu dois détruire. + +[TEX3_2:SERG3] +~g~Tu dois larguer une bombe sur chaque cible, et ce, dans n'importe quel ordre. + +[TEX3_5:SERG3] +~g~Si tu largues une bombe sans succès, tu peux la récupérer pour un nouvel essai. + +[TEX3_7:SERG3] +~g~Tu a alors 7 minutes pour larguer toutes les autres bombes! + +[TEX3_8:SERG3] +~g~Tu as raté la cible! Récupère la bombe et réessaye! + +[TEX3_10:SERG3] +~g~Largue la bombe sur la cible. + +[TEX3_11:SERG3] +Cibles restantes: + +[TEX3_17:SERG3] +~r~Tu as dépassé le temps imparti et le bâtiment n'est pas détruit. + +[TEX3_18:SERG3] +~r~Ton hélico télécommandé a été détruit! Comment vas-tu faire pour transporter les bombes, maintenant? + +[TEX3_19:SERG3] +~r~Tu as largué ta bombe dans l'eau! Tu as besoin des 4 bombes pour détruire le bâtiment! + +[TEX3_20:SERG3] +~g~Ton hélico télécommandé est presque hors de portée! Retourne au bâtiment et termine le travail! + +[TEX3_21:SERG3] +~r~Ton hélico télécommandé est hors de portée! + +[TEX3_24:SERG3] +Appuie sur ~h~~k~~VEHICLE_LOOKLEFT~ ~w~pour faire pivoter l'hélico dans le sens inverse des aiguilles d'une montre. + +[TEX3_25:SERG3] +Appuie sur ~h~~k~~VEHICLE_LOOKLEFT~ ~w~pour faire pivoter l'hélico dans le sens des aiguilles d'une montre. + +[TEX3_27:SERG3] +~g~L'escalier central mène à tous les étages du bâtiment. + +[TEX3_31:SERG3] +~r~Tu as détruit la camionnette qui contenait l'hélico télécommandé et les bombes! + +[TEX3_32:SERG3] +Tu peux ~h~regarder derrière~w~ toi en ~h~appuyant simultanément sur ~k~~VEHICLE_LOOKLEFT~ et ~k~~VEHICLE_LOOKRIGHT~~w~. + +[TEX3_4:SERG3] { reVC update } +~g~Pour larguer une bombe, appuie sur la~h~ ~k~~VEHICLE_FIREWEAPON~~w~. + +[TEX3_29:SERG3] { reVC update } +Pour larguer une bombe, appuie sur la~h~ ~k~~VEHICLE_FIREWEAPON~. + +[TEX3_26:SERG3] +Appuie sur la ~h~~k~~VEHICLE_BRAKE~ ~w~pour réduire la vitesse du rotor et ainsi faire~h~ descendre l'hélicoptère. + +[TEX3_22:SERG3] +Appuie sur la ~h~~k~~VEHICLE_ACCELERATE~ ~w~pour augmenter la vitesse du rotor et ainsi faire~h~ monter l'hélicoptère. + +[TEX3_16:SERG3] +~g~Va jusqu'au camion ~w~TOPFUN ~g~ près du bâtiment à démolir. + +[TEX3_33:SERG3] +Une fois que tu as ramassé une bombe, le radar t'indique la position de la cible par rapport à l'hélico. + +[TEX3_34:SERG3] +Le ~h~point triangulaire vers le haut ~w~indique que la cible se trouve ~h~au-dessus ~w~de l'hélicoptère radiocommandé. + +[TEX3_35:SERG3] +Le ~h~point triangulaire vers le bas ~w~indique que la cible se trouve ~h~en dessous ~w~de l'hélicoptère radiocommandé. + +[TEX3_36:SERG3] +Le ~h~point carré ~w~indique que la cible se trouve ~h~au même niveau ~w~que l'hélicoptère radiocommandé. + +[TEX3_6:SERG3] +~g~Une fois que tu as ramassé une bombe pour la première fois, le compte à rebours s'enclenche. + +[TEX3_28:SERG3] +Pour ~h~ramasser une bombe~w~, manoeuvre l'hélico radiocommandé à côté. Il ne peut transporter qu'une seule bombe à la fois. + +[TEX3_30:SERG3] +~g~Pour ramasser une bombe, manoeuvre l'hélico radiocommandé à côté. Il ne peut transporter qu'une seule bombe à la fois. + +[TEX3_12:SERG3] +~g~Bombe posée! Plus que 3 cibles! Va chercher une autre bombe! + +[TEX3_13:SERG3] +~g~Bombe posée! Plus que 2 cibles! Va chercher une autre bombe! + +[TEX3_14:SERG3] +~g~Bombe posée! Plus qu'une cible! Va chercher une autre bombe! + +[TEX3_15:SERG3] +~r~Compte à rebours enclenché! ~g~Tu dois poser les ~w~4 bombes ~g~avant la fin du temps imparti! + +[TEX3_37:SERG3] +Pousse le ~h~joystick analogique droit vers le haut ~w~pour vitesse la vitesse du rotor et ainsi faire ~h~ monter l'hélicoptère. + +[TEX3_38:SERG3] { reVC update } +{Pousse le ~h~~k~~VEHICLE_ACCELERATE~ ~w~pour augmenter la réduire du rotor et ainsi faire ~h~ descendre l'hélicoptère.} +Pousse le ~h~joystick analogique droit vers la bas ~w~pour augmenter la réduire du rotor et ainsi faire ~h~ descendre l'hélicoptère. + +[TEX3_39:SERG3] +~g~Appuie sur la touche ~h~~k~~VEHICLE_HANDBRAKE~ ~g~pour larguer une bombe. + +[TEX3_40:SERG3] +Appuie sur la touche ~h~~k~~VEHICLE_HANDBRAKE~ ~w~pour larguer une bombe. + +[TEX3_23:SERG3] +Appuyer sur les touches ~h~~k~~VEHICLE_TURRETUP~~w~ et ~h~~k~~VEHICLE_TURRETDOWN~~w~ pour orienter l'hélicoptère dans la direction souhaitée. + +{=================================== MISSION TABLE TAXI1 ===================================} + +[FARES:TAXI1] +CLIENTS: + +[TAXI1:TAXI1] +Cherche une course. + +[TSCORE2:TAXI1] +~1~$ + +[IN_ROW:TAXI1] +~1~ DE SUITE! Bonus : ~1~$ + +[TAXI3:TAXI1] +~r~Ton client est parti terrorisé! + +[TAXI7:TAXI1] +~r~Ta voiture est endommagée. Fais-la réparer. + +[TAXI4:TAXI1] +La course est finie! + +[TAXI5:TAXI1] +BONUS DE VITESSE! + +[TAXI6:TAXI1] +La mission taxi est finie + +[TAXIH1:TAXI1] +Arrête-toi près d'un piéton mis en évidence pour qu'il monte, puis conduis-le à destination avant la fin du temps imparti. + +[FARE1:TAXI1] +~g~Destination ~w~'le Pole Position Club' ~g~à Ocean Beach. + +[FARE3:TAXI1] +~g~Destination ~w~'le Marina' ~g~à Ocean Beach. + +[FARE4:TAXI1] +~g~Destination ~w~'Ammu-Nation' ~g~à Ocean Beach. + +[FARE5:TAXI1] +~g~Destination ~w~'la quincaillerie' ~g~à Washington Beach. + +[FARE6:TAXI1] +~g~Destination ~w~'le centre commercial de North Point' ~g~à Vice Point. + +[MFARE1:TAXI1] +~g~Destination ~w~'Ammu-Nation' ~g~dans le Centre. + +[MFARE2:TAXI1] +~g~Destination ~w~'le Terminal' ~g~à l'aéroport International Escobar. + +[WFARE3:TAXI1] +~g~Destination ~w~'Sunshine Autos' ~g~dans Little Havana. + +[WFARE4:TAXI1] +~g~Destination ~w~'les taxis Kaufman' ~g~dans Little Haiti. + +[WFARE5:TAXI1] +~g~Destination ~w~'la quincaillerie' ~g~dans Little Havana. + +[WFARE6:TAXI1] +~g~Destination ~w~'Howlin Petes Bike Emporium' ~g~dans le Centre. + +[FARE7:TAXI1] +~g~Destination ~w~'Les bijoutiers' ~g~à Vice Point. + +[FARE8:TAXI1] +~g~Destination ~w~'la plage' ~g~à Ocean Beach. + +[FARE9:TAXI1] +~g~Destination ~w~'la plage' ~g~à Washington Beach. + +[FARE10:TAXI1] +~g~Destination ~w~'la plage' ~g~à Vice Point. + +[FARE11:TAXI1] +~g~Destination ~w~'l'hôpital' ~g~à Ocean Beach. + +[FARE12:TAXI1] +~g~Destination ~w~'l'hôpital' ~g~à Vice Point. + +[FARE13:TAXI1] +~g~Destination ~w~'le commissariat de police' ~g~à Washington Beach. + +[FARE14:TAXI1] +~g~Destination ~w~'le commissariat de police' ~g~à Vice Point. + +[FARE15:TAXI1] +~g~Destination ~w~'le restaurant de pizza' ~g~à Vice Point. + +[WFARE7:TAXI1] +~g~Destination ~w~'le commissariat de police' ~g~à Little Havana. + +[WFARE8:TAXI1] +~g~Destination ~w~'le commissariat de police' ~g~à Downtown. + +[WFARE9:TAXI1] +~g~Destination ~w~'l'hôpital' ~g~à Downtown. + +[WFARE10:TAXI1] +~g~Destination ~w~'l'hôpital' ~g~à Little Havana. + +[WFARE11:TAXI1] +~g~Destination ~w~'le stade' ~g~à Downtown. + +[WFARE12:TAXI1] +~g~Destination ~w~'le restaurant de pizza' ~g~à Little Haiti. + +[WFARE13:TAXI1] +~g~Destination ~w~'le restaurant de pizza' ~g~à Downtown. + +[WFARE14:TAXI1] +~g~Destination ~w~'les docks' ~g~à Viceport. + +[WFARE15:TAXI1] +~g~Destination ~w~'la pharmacie' ~g~à Little Haiti. + +[FARE2:TAXI1] +~g~Destination ~w~'le Malibu club' ~g~à Vice Point. + +{=================================== MISSION TABLE TAXICUT ===================================} + +[TAXC_A:TAXICUT] +J'imagine que t'es le nouveau proprio. + +[TAXC_B:TAXICUT] +D'où tu sors? La Mafia? Le Cartel? T'as pas l'air mexicain... + +[TAXC_C:TAXICUT] +Quoi qu'il en soit, tu ferais mieux de continuer avec le barratin comme quoi les choses vont changer, + +[TAXC_D:TAXICUT] +peut-être même menacer des conducteurs... + +[TAXC_E:TAXICUT] +Vas-y mollo avec Ted là-bas, on vient juste de lui soigner son hernie. + +[TAXC_F:TAXICUT] +Eh bien, ouais. Les choses vont changer dans le coin, madame. + +[TAXC_G:TAXICUT] +Arrête tes conneries, petit. Vaut mieux que tu me laisses faire. + +[TAXC_H:TAXICUT] +Ca fait des années que je suis dans ce genre de biz. + +[TAXC_I:TAXICUT] +Ecoutez tous. + +[TAXC_J:TAXICUT] +On a une nouvelle direction et les choses vont encore changer dans le coin. + +[TAXC_K:TAXICUT] +Notre nouvelle direction, la... + +[TAXC_L:TAXICUT] +Tu fais partie de quel gang? + +[TAXC_M:TAXICUT] +En fait, je fais pas partie d'un gang... + +[TAXC_N:TAXICUT] +C'est quoi ton foutu nom, petit? + +[TAXC_O:TAXICUT] +Vercetti, Tommy Vercetti. + +[TAXC_P:TAXICUT] +Notre nouvelle direction, le gang Vercetti, + +[TAXC_Q:TAXICUT] +va veiller à nous éviter les emmerdes. + +[TAXC_R:TAXICUT] +Capiche? Terminé! + +[TAXC_S:TAXICUT] +Qu'est-ce que t'as pensé du 'capiche'? Moi, j'ai bien aimé le 'capiche'. + +[TAXC_T:TAXICUT] +Bon, alors c'est comme ça que ça marchait avant + +[TAXC_U:TAXICUT] +et on va continuer à faire tourner l'entreprise comme ça. + +[TAXC_V:TAXICUT] +Si on a des ennuis avec une compagnie concurrente, tu leur flanques une dérouillée. + +[TAXC_W:TAXICUT] +Puis ils nous flanquent une dérouillée. + +[TAXC_X:TAXICUT] +puis tu leur flanques une dérouillée, + +[TAXC_Y:TAXICUT] +et cetera et cetera. T'as pigé? + +[TAXC_Z:TAXICUT] +Euh, ouais, je crois... + +[TAXC_A1:TAXICUT] +Prends simplement un taxi au garage si tu te sens de bosser. + +{=================================== MISSION TABLE TAXIWA1 ===================================} + +[OUTTIME:TAXIWA1] +~r~T'es trop lent, mec, trop lent! + +[TAX1_1:TAXIWA1] +Ok, on a un client rupin qui a besoin d'être ramassé à Starfish island, ça intéresse quelqu'un? + +[TAX1_2:TAXIWA1] +Ici, Tommy, je prends! + +[TAX1_3:TAXIWA1] +C'est mon client, dégage! + +[TAX1_4:TAXIWA1] +Allez, vite, monte! + +[TAX1_5:TAXIWA1] +Ok, ok, mais ne me faites pas de mal! + +[TAXW1_1:TAXIWA1] +~g~Va prendre le V.I.P. sur Starfish Island. + +[TAXW1_2:TAXIWA1] +~g~Ramène le V.I.P.! Bousille l'autre bagnole! + +[TAXW1_3:TAXIWA1] +~r~Le V.I.P. est mort! + +[TAXW1_4:TAXIWA1] +~r~Le V.I.P. a été déposé! + +[TAXW1_6:TAXIWA1] +~g~Emmène le V.I.P. à l'aéroport! + +{=================================== MISSION TABLE TAXIWA2 ===================================} + +[TAX2_1:TAXIWA2] +Appel à toutes les voitures, on perd des clients dans toute la ville! Qu'est-ce que vous foutez, les gars? + +[TAX2_2:TAXIWA2] +Les taxis VC nous les prennent! Ils ont trop de voitures, on peut pas rivaliser! + +[TAX2_3:TAXIWA2] +Vercetti, si t'es en ville en train d'écouter, va falloir mettre quelques taxis VC hors circuit ou c'est la faillite! + +[TAXW2_1:TAXIWA2] +~g~Détruis 3 taxis concurrents! + +{=================================== MISSION TABLE TAXIWA3 ===================================} + +[TAX3_1:TAXIWA3] +Voiture 13, on a une demoiselle Cortez à prendre dans le centre, elle vous a spécialement demandé. + +[TAX3_2:TAXIWA3] +Ok, je prends. Voiture 13 en course! + +[TAX3_3:TAXIWA3] +Hmmmm, aucun signe de Mercedes... + +[TAXW3_3:TAXIWA3] +~g~Bousille le taxi leader! + +[TAXW3_2:TAXIWA3] +~g~Reste vivant jusqu'à la fin du temps imparti. + +[TAX_AS1:TAXIWA3] +COMPAGNIE DE TAXIS ACQUISE + +[TAX_AS2:TAXIWA3] +~G~Les taxis Kaufman génère dorénavant un revenu de $~1~ maximum. Pense à récupérer le fric régulièrement. + +[TAX3_4:TAXIWA3] +L'heure est venue pour l'ange gardien des taxis Kaufman de froisser de la tôle! + +[TAX3_5:TAXIWA3] +Hé mec, j'vais te bousiller ta caisse! + +{ reVC updates } +{ new languages } +[FEL_JAP] +JAPONAIS + +[FEL_POL] +POLONAIS + +[FEL_RUS] +RUSSE + +{ new display menus } +[FET_GFX] +GRAPHICS SETUP + +[FED_MIP] +MIP MAPPING + +[FED_AAS] +ANTI ALIASING + +[FED_FIL] +TEXTURE FILTERING + +[FED_BIL] +BILINEAR + +[FED_TRL] +TRILINEAR + +[FED_WND] +WINDOWED + +[FED_FLS] +FULLSCREEN + +[FEM_CSB] +CUTSCENE BORDERS + +[FEM_SCF] +SCREEN FORMAT + +[FEM_ISL] +MAP MEMORY USAGE + +[FEM_LOW] +LOW + +[FEM_MED] +MEDIUM + +[FEM_HIG] +HIGH + +[FEM_2PR] +PS2 ALPHA TEST + +[FEC_FRC] +FREE CAM + +{ Linux joy detection } +[FEC_JOD] +DETECT JOYSTICK + +[FEC_JPR] +Press any key on the joystick of your choice that you want to use on the game, and it will be selected. + +[FEC_JDE] +Detected joystick + +{ mission restart } +[FET_RMS] +REJOUER MISSION + +[FESZ_RM] +REJOUER? + +[FED_VPL] +VEHICLE PIPELINE + +[FED_PRM] +PED RIM LIGHT + +[FED_RGL] +ROAD GLOSS + +[FED_CLF] +COLOUR FILTER + +[FED_WLM] +WORLD LIGHTMAPS + +[FED_MBL] +MOTION BLUR + +[FEM_SIM] +SIMPLE + +[FEM_NRM] +NORMAL + +[FEM_MOB] +MOBILE + +[FED_MFX] +MATFX + +[FED_NEO] +NEO + +[FEM_PS2] +PS2 + +[FEM_XBX] +XBOX + +[FEC_IVP] +INVERT PAD VERTICALLY + +[FEM_NON] +NONE + +[FEC_DS2] +DUALSHOCK 2 + +[FEC_DS3] +DUALSHOCK 3 + +[FEC_DS4] +DUALSHOCK 4 + +[FEC_360] +XBOX 360 CONTROLLER + +[FEC_ONE] +XBOX ONE CONTROLLER + +[FEC_NSW] +NINTENDO SWITCH CONTROLLER + +[FEC_TYP] +GAMEPAD TYPE + +[FET_AGS] +GAMEPAD SETTINGS + +[FEM_AUT] { aspect ratio related } +AUTO + +[FEM_PED] +PED DENSITY + +[FEM_CAR] +CAR DENSITY + +[DUMMY] +THIS LABEL NEEDS TO BE HERE !!! +AS THE LAST LABEL DOES NOT GET COMPILED \ No newline at end of file diff --git a/gxt/miami/german.txt b/gxt/miami/german.txt new file mode 100644 index 00000000..27e91def --- /dev/null +++ b/gxt/miami/german.txt @@ -0,0 +1,14726 @@ +{ + New strings are at the bottom of file. + Do not change the order of strings. + You can fix the typos of existing translation but please refrain from + unnecessary edits like rephasing because you think it suits better for your taste. +} + +[RAMPAGE] +AMOKLAUF!! + +[RAMP_F] +AMOKLAUF FEHLGESCHLAGEN!! + +[RAMP_P] +AMOKLAUF BESTANDEN!! + +[RAMP_A] +ALLE AMOKLÄUFE BESTANDEN!! + +[PAGE_01] +Dein Job: ~1~ Gang-Mitglieder in 2 Minuten! + +[PAGE_02] +Zerstöre ~1~ Fahrzeuge in 2 Minuten! + +[PAGE_03] +Erledige ~1~ Gang-Mitglieder in 2 Minuten im Vorbeifahren! + +[PAGE_04] +Überfahre ~1~ Gang-Mitglieder in 2 Minuten! + +[PAGE_05] +Dein Job: ~1~ Gang-Mitglieder in 2 Minuten! + +[SENTXS] +Sentinel XS + +[MAP_LEG] +Legende + +[VCNMAV] +VCN Maverick + +[LG_01] +Position des Spielers + +[LG_02] +Avery Carrington + +[LG_03] +Biker-Kontaktpunkt + +[LG_04] +Colonel Cortez + +[LG_05] +Ricardo Diaz + +[LG_06] +Kent Paul + +[LG_07] +Rechtsanwalt + +[LG_08] +Phil Cassidy + +[LG_09] +Bootswerft + +[LG_10] +Malibu Club + +[LG_11] +Kubaner + +[LG_12] +Filmstudio + +[LG_13] +AmmuNation + +[LG_14] +Haitianer + +[LG_15] +Eisenwarenladen + +[LG_16] +Versteck + +[LG_17] +Eiscreme + +[LG_18] +Kaufman-Taxis + +[LG_19] +Love Fist + +[LG_20] +Druckerei + +[LG_21] +Immobilie + +[LG_22] +Pay 'n' Spray + +[LG_23] +Bekleidungsgeschäft + +[LG_24] +Tommys Villa + +[LG_25] +Telefon + +[LG_26] +Radiosender Wildstyle + +[LG_27] +Radiosender Flash FM + +[LG_28] +Radiosender KChat + +[LG_29] +Radiosender Fever 105 + +[LG_30] +Radiosender VRock + +[LG_31] +Polizeifunk-Zentrale + +[LG_32] +Radiosender Espantoso + +[LG_33] +Radiosender Emotion 98.3 + +[LG_34] +Radiosender Wave 103 + +[LG_36] +Sun Yard + +[LG_37] +Stripper-Bar + +[MAP_YAH] +DU BIST HIER + +[TAXSHRT] +~g~Du kannst dieses Kaufman-Taxi nehmen, statt selbst Auto zu fahren. Das kostet dich $9. + +[MOB_09D] +Vielleicht hab ich Leo ja erledigt und mir sein Handy genommen. Hast du schon mal daran gedacht, du Penner? + +[FE_MLG] +KARTENLEGENDE + +[FED_RDR] +RADAR-MODUS + +[FED_HUD] +HUD-MODUS + +[FED_RDL] +GROSS + +[FED_RDB] +NUR SYMBOLE + +[FED_HUF] +INFOS EIN- & AUSBLENDEN + +[FEST_HV] +Höchstes Bürgerwehr-Missions-Level + +[BRIBE1] +Du hast soeben Polizei-Bestechungsgeld aufgenommen, dein Fahndungslevel verringert sich damit um einen Stern. + +[CLOHELP] +Saubere Klamotten!! + +[SUNSHIN] +Sunshine Autos + +[CHERRYP] +Cherry Popper Eiscreme + +[KAUFCAB] +Kaufman-Taxis + +[BOATYAR] +Die Bootswerft + +[WANT_L] +Dein Fahndungslevel ist bis auf weiteres aufgehoben. Solltest du ein Verbrechen begehen, während die Sterne blinken, wird dein volles Fahndungslevel wieder aktiv. + +[PICK1] +Kugelsichere Weste im Ocean View Hotel angeliefert! + +[HOTRNG] +HOTRING + +[BLODRNG] +CHAOS-DERBY + +[DIRTRNG] +DIRTRING + +[FEC_ABR] +Beschleunigen, Bremsen oder Zurücksetzen + +[FEI_BTU] +; = - + +[FEI_SCR] +Scrollen + +[SKUMBUY] +Skumole Shack gekauft: $ ~1~ + +[SKUM_L] +Drücke die ~h~L1-Taste~w~, um Skumole Shack zu kaufen. Preis: $~1~ + +[SKUM_T] +Drücke die ~t~"-Taste~w~, um Skumole Shack zu kaufen. Preis: $~1~ + +[SKUM_C] +Drücke die ~o~|-Taste~w~, um Skumole Shack zu kaufen. Preis: $~1~ + +[LG_35] +Ziel + +[IN_VEH] +~g~Hey! Zurück ins Auto!! + +[HEY] +~g~Keine Alleingänge. Halt die Gang beisammen! + +[HELP3] +Du kannst nur kurze Zeit sprinten, ohne müde zu werden. + +[HELP4_D] +Drücke den~h~ rechten Analog-Stick nach oben, um zu ~h~beschleunigen. + +[HELP5_D] +Zieh den ~h~rechten Analog-Stick~w~ zurück, um zu ~h~bremsen~w~, oder um ~h~zurückzusetzen~w~, wenn das Fahrzeug steht. + +[HELP7_A] +Halte die~h~ ~k~~PED_LOCK_TARGET~ ~w~gedrückt, um mit dem Präzisionsgewehr zu zielen. + +[HELP7_D] +Halte die~h~ ~k~~PED_LOCK_TARGET~ ~w~gedrückt, um mit dem Präzisionsgewehr zu zielen. + +[HELP10] +Dieser Stern zeigt an, dass du von der Polizei gesucht wirst. + +[HELP11] +Je mehr Sterne, desto dringender wirst du gesucht. + +[HELP13] +Manchmal musst du vielleicht Wege finden, die das Radar nicht zeigt. + +[TIMER] +Diese Mission hat ein Zeitlimit. Du musst sie beendet haben, bevor die Zeit um ist. + +[HORN] +~g~Drück auf die Hupe. + +[NOMONEY] +~g~Du brauchst mehr Cash! + +[REWARD] +BELOHNUNG $~1~ + +[M_FAIL] +MISSION FEHLGESCHLAGEN! + +[M_PASS] +MISSION ERFÜLLT! $~1~ + +[DEAD] +AUSSER GEFECHT! + +[BUSTED] +VERHAFTET! + +[WEATHE1] +WETTER AUF SONNIG UMSTELLEN + +[WEATHE2] +WETTER AUF SEHR SONNIG UMSTELLEN + +[WEATHE3] +WETTER AUF BEWÖLKT UMSTELLEN + +[WEATHE4] +WETTER AUF REGNERISCH UMSTELLEN + +[WEATHE5] +WETTER AUF NEBLIG UMSTELLEN + +[WEATHE6] +WETTER NORMAL + +[NUMBER] +~1~ + +[LOADCAR] +LADE FAHRZEUG... (ABBRECHEN MIT L1) + +[CARSOFF] +Deaktivierte Fahrzeuge. + +[CARS_ON] +Aktivierte Fahrzeuge. + +[TEXTXYZ] +Schreibe Koordinaten in Datei... + +[CHEATON] +Cheat Modus AN + +[CHEATOF] +Cheat Modus AUS + +[IMPORT1] +Geh nach draußen und warte auf dein Fahrzeug. + +[PAGEB11] +Flammenwerfer in Versteck angeliefert. + +[WANT_A] +Verhaftet wirst du nur, wenn die Polizei nach dir ~h~fahndet. + +[WANT_B] +Dein ~h~Fahndungslevel~w~ wird durch die Reihe von Sternen oben rechts auf dem Bildschirm dargestellt. + +[WANT_C] +Du hast jetzt einen ~h~Fahndungslevel~w~ von eins... + +[WANT_D] +zwei... + +[WANT_E] +drei... + +[WANT_F] +Steigt dein ~h~Fahndungslevel~w~, wirst du von besser ausgebildeten Polizisten gejagt. + +[WANT_G] +Wirst du ~h~verhaftet~w~, wirst du zum nächsten Polizeirevier gebracht. + +[WANT_H] +Die Cops werden dir alle Waffen abnehmen und kassieren ein wenig Bestechungsgeld von dir. + +[WANT_I] +Wenn dir das auf einer Mission passiert, ist die Mission fehlgeschlagen. + +[WANT_J] +Im Verlauf des Spiels wirst du Möglichkeiten entdecken, deinen Fahndungslevel zu reduzieren. + +[WANT_K] +Wenn du in einem Wagen sitzt, werden ~h~LACKIEREREIEN~w~ den Fahndungslevel ~h~annullieren. + +[HEAL_B] +Wenn du ~h~'außer Gefecht'~w~ bist, wirst du zur nächsten Klinik gebracht. + +[HEAL_C] +Du verlierst alle Waffen, und die Ärzte knöpfen dir ein wenig Cash für die Behandlung ab. + +[HEAL_E] +Je länger du spielst, desto mehr Wege wirst du finden, dich selbst zu verarzten oder zu schützen. + +[SAVE1] +Stell dich in die Markierung. So kannst du dein ~h~Spiel speichern~w~. Während einer Mission kannst du nicht speichern. + +[SAVE2] +Jedes Fahrzeug, das in dieser Garage abgestellt wird, wird für dich aufbewahrt, wenn das Spiel gespeichert wird. + +[AMMU] +Betritt den Ammu-Nation, um eine Waffe zu kaufen. + +[R_TIME] +ZEIT: + +[PROP_1] +Du hast nicht genug Cash für dieses Objekt. + +[PROP_2] +Während einer Mission kannst du keine Objekte kaufen. + +[IND_ZON] +Vice City Beach + +[COM_ZON] +Vice City Mainland + +[BEACH1] +Ocean Beach + +[BEACH2] +Washington Beach + +[BEACH3] +Vice Point + +[GOLFC] +Leaf Links Golfclub + +[STARI] +Starfish Island + +[DOCKS] +Viceport + +[HAVANA] +Little Havana + +[HAITI] +Little Haiti + +[PORNI] +Prawn Island + +[DTOWN] +Downtown + +[VICE_C] +Vice City + +[A_PORT] +Escobar Inter. Airport + +[JUNKY] +Schrottplatz + +[PISTOL] +Pistole + +[PYTHON] +.357 + +[UZI] +Uz-1 + +[TEC9] +Tec 9 + +[M4] +M4 + +[INGRAM] +Mac + +[MP5] +MP + +[RUGER] +Kruger + +[SNIPE] +Präzisionsgewehr + +[GRENADE] +Granaten + +[SHOTGN1] +Schrotflinte + +[SHOTGN2] +S.P.A.S. 12 + +[SHOTGN3] +Abgesägte Schrotflinte + +[ARMOUR] +Kugelsichere Weste + +[LASER] +.308 Präzisionsgewehr + +[BASEBAT] +Baseballschläger + +[HAMMER] +Hammer + +[SCREWD] +Schraubenzieher + +[CLEVER] +Fleischerbeil + +[MACHETE] +Machete + +[KNIFE] +Messer + +[KATANA] +Katana + +[CHAINSA] +Kettensäge + +[G_COST] +$~1~ + +[CAR_1] +Krankenwagen + +[MALIBU] +Malibu Club + +[MANSION] +Diaz' Haus + +[TMANS] +Vercetti Estate + +[STRIP] +Pole Position Club + +[MALL1] +North Point Einkaufszentrum + +[BANKINT] +El Banco Corrupto Grande + +[RANGE] +Schießstand + +[POL_HQ] +Vice City Polizeihauptquartier + +[INT_B] +Ein alter Freund + +[INTB_1] +~g~Begib dich in die Anwaltskanzlei. + +[LAW_1] +Die Party + +[LAW_2] +Dunkle Gassen + +[LAW_3] +Die Geschworenen + +[LAW_4] +Aufruhr + +[COL_1] +Der Verräter + +[COL_2] +Kugelhagel im Einkaufszentrum + +[COL_3] +Die Schutzengel + +[COL_4] +Zu Befehl, Sir! + +[COL_5] +Alle Mann an Deck! + +[COK_1] +Die Jagd + +[COK_2] +Phnom Penh '86 + +[COK_3] +Das schnellste Boot + +[COK_4] +Angebot & Nachfrage + +[KENT_1] +Die Befreiungsaktion + +[ASS_1] +Pizza Mortale + +[BUD_1] +Fette Beute + +[BUD_2] +Zoff in der Bar + +[BUD_3] +Cop-Land + +[CAP_1] +Der Eintreiber + +[FIN_1] +Freunde und andere Feinde + +[BANK_1] +Kein Entkommen? + +[BANK_2] +Der Scharfschütze + +[BANK_3] +Der Fahrer + +[BANK_4] +Der Coup + +[CNT_1] +Der Singvogel + +[CNT_2] +Der Kurier + +[PORN_1] +Alle meine Pferdchen + +[PORN_2] +Der Dildo-Jet + +[PORN_3] +Ein Mann namens Martha + +[PORN_4] +Heiße Lightshow + +[TAX_1] +Kaufman-Taxis + +[TAXI_1] +V.I.P + +[TAXI_2] +Konkurrentenjagd + +[TAXI_3] +Das Taxi-Inferno + +[ICE_1] +Eiscreme und andere Leckereien + +[TEX_1] +Schlagende Argumente + +[TEX_2] +Das Begräbnis + +[TEX_3] +Schutt und Asche + +[PHIL_1] +Der Waffenschieber + +[PHIL_2] +TNT-Whiskey + +[BIKE_1] +Wheels of Steel + +[BIKE_2] +Chaos-City + +[BIKE_3] +Big Bakers Bike + +[ROCK_1] +Love Juice + +[ROCK_2] +Der Psychokiller + +[ROCK_3] +Die PR-Tour + +[ROCK_4] +Love Fist! + +[HAT_1] +Mysteriöses Pulver + +[HAT_2] +Fliegende Bomben + +[HAT_3] +Schmutzige Methoden + +[CUB_1] +Stunt-Boot-Action + +[CUB_2] +Kanonenfutter + +[CUB_3] +Die Seeschlacht + +[CUB_4] +Trojanisches Voodoo + +[JOB_1] +Der Pizza-Lieferant + +[JOB_2] +Ein bedauerlicher Unfall + +[JOB_3] +Die rasende Schrottfabrik + +[JOB_4] +Trouble am Check-In + +[JOB_5] +Gefeuert! + +[ANSWER] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um den Anruf auf deinem Handy entgegenzunehmen. + +[MOB_01A] +Hey, mein Alter! Paul hier. Ich hätte da vielleicht was für dich, aber das müssen wir unter vier Augen besprechen. + +[MOB_01B] +Ich bin im Malibu und lass es mir gutgehen. + +[MOB_01C] +Ich denke, ich hab einen gut bei dir, wenn du das hörst, Kumpel. Bis gleich. + +[MOB_02A] +Hey! Hallo, Tommy? Tommy! + +[MOB_02B] +In der Druckerei ist irgendwas im Busch. Fahr mal rüber und kümmere dich darum. + +[MOB_02C] +Es scheint ziemlichen Ärger zu geben. Ich muss Schluss machen. + +[MOB_03A] +Mr. Vercetti? Ich habe hier einen unterschriebenen Wisch, + +[MOB_03B] +der besagt, dass Sie alle Schulden von 'BJ's Autos' übernehmen. + +[MOB_03C] +Nach BJ's plötzlichem Verschwinden hab ich keine andere Wahl, + +[MOB_03D] +als Sie für seine Verbindlichkeiten zur Rechenschaft zu ziehen. + +[MOB_03E] +Bis Sie die volle Summe beglichen haben, + +[MOB_03F] +sollte Ihnen klar sein, dass Vice City ein heißes Pflaster für Sie ist. + +[MOB_04A] +Wie geht's, mein Alter? + +[MOB_04B] +Ich hab vergessen, dir zu sagen, dass wir für das Konzert noch ein paar Ordner brauchen. + +[MOB_04C] +Da gibt's so 'ne Biker-Gang, die wär super für die Publicity. + +[MOB_04D] +Wenn du mir das arrangierst, besorg ich dir 'n VIP-Pass für den Abend. Ok? + +[MOB_05A] +Gut gemacht, Tommy. Schön, den Ofen wiederzuhaben. + +[MOB_05B] +Sag Mr. Kent Paul, dass er seine Ordner für das Konzert kriegt. + +[MOB_05C] +Du hast mein Wort darauf. + +[MOB_05D] +Halt die Ohren steif. + +[MOB_06A] +Tommy, man hört so allerlei über dich, mein Junge. + +[MOB_06B] +Wenn du willst, kocht Tante Poulet dir ein leckeres Süppchen, dann kannst du mal abschalten, hm? + +[MOB_06C] +Komm doch die Tage mal bei mir vorbei, ok, Tommy? + +[MOB_08A] +Tommy, ich dachte mir, du könntest meinen Rat als Geschäftsmann brauchen. + +[MOB_08B] +Wenn du ein Unternehmen am Laufen hast, musst du einmal pro Woche die Einnahmen kassieren gehen. + +[MOB_08C] +Sonst werden deine Leute übermütig und versuchen, in die eigene Tasche zu wirtschaften. Ok? + +[MOB_08D] +Hey, ich weiß selbst, wie man so was macht, Ken, ok? + +[MOB_08E] +Ok, ok. Ich weiß ja, dass du Bescheid weißt. + +[MOB_08F] +Ich meinte ja nur, damit du weißt, dass ich im Zweifelsfall auch Bescheid weiß. + +[MOB_08G] +Nur für alle Fälle, mein Alter! + +[MOB_08H] +Wenn du meinst, Ken... + +[MOB_09A] +Hey, Leo! Ich hab Arbeit für dich! + +[MOB_09B] +Hier ist nicht Leo. + +[MOB_09C] +Hey, wenn Leo erfährt, dass du mit seinem Handy telefonierst, bist du fällig. + +[MOB_09E] +Du hast Leo erledigt? Du musst Mumm haben - willst du für mich arbeiten? + +[MOB_09F] +Komm ins Café von meinem Vater in Little Havana, dann können wir reden. + +[MOB_10A] +Tommy! Du musst mir einen Gefallen tun. + +[MOB_10B] +Steve! Was machen die Dreharbeiten? + +[MOB_10C] +Gut. Ich- äh, WIR brauchen noch eine Autoverfolgungsjagd, aber unser Budget ist knapp. + +[MOB_10D] +Ich hab in der Stadt verteilt ein paar Karren stehen. Du weißt, was zu tun ist. + +[MOB_10E] +Ok, Steve, ich halt die Augen offen. Bis dann. + +[MOB_11A] +Hallo, Söhnchen. Ich wollte dir kurz 'nen kleinen Tipp geben. + +[MOB_11B] +Hi, Avery. Worum geht's denn? + +[MOB_11C] +Man kann in dieser Stadt viel Geld machen, wenn man die richtigen Immobilien hat. Klingelt's? + +[MOB_11D] +Ich denke schon. + +[MOB_11E] +Also, halt die Augen offen, dann könnte sich dir die ideale Gelegenheit bieten. Bis bald. + +[MOB_11F] +Ciao, Avery. + +[MOB12_A] +Hey, Tommy. Avery hier. Hör mal, ich hab gerade alle Hände voll zu tun, + +[MOB12_B] +und ein Bevollmächtigter von mir müsste zu den Gator Keys eskortiert werden. + +[MOB12_C] +Ich will da ein Stück Land kaufen und schick ihn hin, um den Deal abzuschließen. + +[MOB12_D] +Könntest du dafür sorgen, dass er gut dort ankommt? + +[MOB12_E] +Mach ich doch glatt, Avery. Wo soll ich ihn abholen? + +[MOB12_F] +Er hat gerade noch an der Baustelle zu tun. Hol ihn doch am besten dort ab. + +[MOB12_G] +Kein Problem. Bis dann, Avery. + +[MOB13_A] +Vercetti? VERCETTI!! Verdammt, Mann, Sie müssen mir helfen! + +[MOB13_B] +Mr. Moffat? Wie geht's der Familie? + +[MOB13_C] +Zur Hölle mit Ihnen, hören Sie! + +[MOB13_D] +Tja, war nett, mit Ihnen zu plaudern... + +[MOB13_E] +WARTEN SIE! Vercetti - Tommy, kann ich Tommy zu Ihnen sagen? + +[MOB13_F] +Wir sind beide Geschäftsleute. Sie erkennen doch ein gutes Geschäft sofort, oder? + +[MOB13_G] +Ich hab keine Zeit für Palaver. Kommen Sie zum Punkt. + +[MOB13_H] +GELD. Geld ist der Punkt, verdammt! + +[MOB13_I] +Ich bin den Kerlen nochmal entwischt, aber sie sitzen mir im Nacken. Die machen sich einen Spaß daraus! + +[MOB13_J] +Ich bin in einer Telefonzelle, irgendwo in diesem verdammten Loch. + +[MOB13_K] +Holen Sie mich hier raus, bevor sie mich schnappen und-... oh Gott... + +[MOB13_L] +Ich bin leider ausgebucht bis-... + +[MOB13_M] +Nein! Verarschen Sie mich nicht, haben Sie Erbarmen! Kein Mensch hat das verdient! + +[MOB13_N] +Ich flehe Sie an, Tommy, auf den Knien flehe ich Sie an! Bitte... + +[MOB13_O] +Naja, ich könnte 'nen kurzen Abstecher machen. Vielleicht finde ich Sie... + +[MOB13_P] +Oh Gott, sie kommen! Um Himmels Willen, beeilen Sie sich! + +[MOB_14A] +Hey, Tommy, hör zu, das wird dir gefallen. + +[MOB_14B] +Ein Vögelchen hat mir gesteckt, dass das Vice City-Spezialkommando in einem renommierten Finanzinstitut ein Schließfach hat. + +[MOB_14C] +Da sind die Schmiergelder drin, die sie über die Jahre kassiert haben. + +[MOB_14D] +So 'ne Art Altersversorgung für die ganze Mannschaft. + +[MOB_14E] +Sollte dir diese Info je helfen, dir was von dem Kuchen anzueignen, + +[MOB_14F] +würdest du dich sicher verpflichtet fühlen, mir 'n Stück davon abzugeben? + +[MOB_14G] +Ich werd dich nicht vergessen. Danke, Kent. + +[MOB_14H] +'Paul'heiße ich, Scherzkeks. Ich KOMME aus Kent, Nähe London. + +[MOB_14I] +Mein geographisches Wissen über England ist eben nicht mehr, was es war. + +[MOB15_A] +Tommy, Alter. Hier ist Paul aus Kent. + +[MOB15_B] +Unten im Malibu, da sind ein paar Keulen, die haben ein Auge auf dich geworfen. + +[MOB15_C] +Ich verstehe nur Bahnhof. + +[MOB15_D] +Hasen. Miezen. Na, Puppen, eben. Coole Babes, keine Professionellen, oder so. + +[MOB15_E] +Du solltest mal kommen und sie dir ansehen. + +[MOB16_A] +Tommy, hier Paul. Wie geht's, mein Freund? + +[MOB16_B] +Was willst du, Paul? Ich brauch keine getürkten Designer-Klamotten. + +[MOB16_C] +Sehr witzig. Du weißt, dass ich mit getürkter Ware nichts am Hut habe. + +[MOB16_D] +Wollte nur hören, ob ich nicht 'ne Rolle in einem von deinen Filmen kriegen könnte. + +[MOB16_E] +In England habe ich viel einschlägiges Zeug gedreht. + +[MOB16_F] +Ich hab mehr zu bieten als du, mein Alter. + +[MOB16_G] +Paul, danke für das Angebot. Ich komm auf dich zurück. + +[MOB16_H] +Lass mich nicht hängen. Denk dran, was ich alles für dich getan habe. + +[MOB16_I] +Das versuch ich ja grade zu vergessen. + +[MOB17_A] +Tommy Vercetti. Wie geht's, großer Meister? + +[MOB17_B] +Man hört so einiges über dich. Bist jetzt 'ne große Nummer in der Stadt, hä? + +[MOB17_C] +Paul, du bist betrunken. + +[MOB17_D] +Nein, du Trottel, ich bin nicht betrunken. + +[MOB17_E] +Hab mir nur ein paar Ladungen Stoff gegeben, war seit ein paar Tagen nicht im Bett. + +[MOB17_F] +Und du brauchst mich nicht dumm anzureden. + +[MOB17_G] +Ich bin nicht irgendwer. Wer hat dir denn in dieser Stadt den Weg geebnet? Ich! + +[MOB17_H] +Tatsächlich? + +[MOB17_I] +Tatsächlich! + +[MOB17_J] +Paul, reg dich ab. Ich hatte viel zu tun. Sei kein Idiot. + +[MOB17_K] +Ich bin kein Idiot. Das haben sie schon im Jugendknast gesagt. + +[MOB17_L] +Wenn du Ärger haben willst, Freundchen, den kannst du haben! + +[MOB17_M] +Tommy, bitte! Du warst meine große Hoffnung. Bitte, mach dich nicht lustig über mich! + +[MOB17_N] +Paul, schlaf mal 'ne Runde. Im Ernst. + +[MOB18_A] +Tommy, hier Paul. Wie geht's, Alter? Hey, ich dachte mir, das musst du hören... + +[MOB18_B] +Echt der Hammer. Du glaubst nicht, was mir für 'ne Puppe über den Weg gelaufen ist. + +[MOB18_C] +'ne Bordsteinschwalbe, oder sowas. Unten in Little Havana. + +[MOB18_D] +Sagt, sie heißt Mercedes oder so ähnlich. Wahnsinn, Alter. Die Puppe musst du dir geben. + +[MOB18_E] +Da würde 'nen Toter Hormonkoller kriegen. Sie sagt, ich wär der beste, den sie je hatte. + +[MOB18_F] +Halt die Augen nach ihr offen. Bis dann. + +[MOB19_A] +Tommy, hier KP - Kent Paul. Ich hab läuten hören, dass jemand dich leimen will. + +[MOB19_B] +Also, sei wachsam, mein Freund. Und kein Sterbenswörtchen, dass du das von mir weißt. + +[MOB_20A] +Hallo, Tommy. Hier Paul. Ich höre, dass du ein paar Leuten auf den Schlips getreten bist. + +[MOB_20B] +Irgendjemand sieht es anscheinend nicht gern, dass du auf einmal den großen Zampano spielst. + +[MOB_20C] +Also, sag nicht, ich hätte dich nicht gewarnt. Es rächt sich, wenn man's übertreibt. + +[MOB_20D] +Jedenfalls ist angeblich ein Kopfgeld auf dich ausgesetzt und es ist schon jemand hinter dir her. + +[MOB_20E] +Also pass auf dich auf. Und vergiss mich nicht ganz. + +[MOB71_A] +Tommy, Thomas, hier Cortez. Wie geht's? + +[MOB71_B] +Es bleibt spannend. Und selbst? + +[MOB71_C] +Ein ewiger Kampf, Tommy. Entschuldigen Sie die schlechte Verbindung, wir hatten wieder einen Putschversuch. + +[MOB71_D] +Es gibt keine anspruchsvollere Geliebte als das Volk. + +[MOB71_E] +Seit ich aus Vice City zurück bin, gab's drei Revolutionen und vier Staatsstreiche. + +[MOB71_F] +Zum Glück bin ich jedesmal befördert worden. + +[MOB71_G] +Ich wollte Sie wegen Mercedes etwas fragen. + +[MOB71_H] +OK. Was ist mit ihr? + +[MOB71_I] +Ich hör Geschichten über sie und weiß nicht, was ich davon halten soll. + +[MOB71_J] +Vielleicht wollen mich alle demütigen. + +[MOB71_K] +Vielleicht wird sie übermütig, seit ich weg musste, sagen Sie mir nur eins, Tommy. Ist das wahr?. + +[MOB71_L] +sagen Sie mir nur eins, Tommy. Ist das wahr? + +[MOB71_M] +Ist was wahr? + +[MOB71_N] +Die Geschichten, die ich höre? Will sie wirklich Anwältin werden? + +[MOB71_O] +Welche Schande, Tommy. Wir Cortez sind eine stolze Familie und würden nie einer Tochter erlauben, Anwältin zu werden. + +[MOB71_P] +Sagen Sie mir, dass es nicht wahr ist. Das ertrage ich nicht. + +[MOB71_Q] +Colonel, ich versichere Ihnen, dass Mercedes niemals Anwältin wird. Keine Angst. + +[MOB71_R] +Danke, Tommy - das wäre zu viel der Schande. Sie ist eine Dame, keine Schmarotzerin. + +[MOB71_S] +Ich weiß, Colonel + +[MOB71_T] +Tommy, Sie müssen jetzt entschuldigen, gerade kommt der neue Innenminister. + +[MOB71_U] +Ich hab seinen Vater vor Jahren bei einem Putschversuch erledigt. Ich muss mich gut mit ihm stellen. Wiederhören. + +[MOB22_A] +Tommy, Sie erweisen sich als sehr nützlich, mein Freund. + +[MOB22_B] +Danke, Cortez. Was ist mit meinem Deal? + +[MOB22_C] +Tommy, ich mühe mich ohne Unterlass, um diesem Sumpf von elenden Lügen und Intrigen auf den Grund zu gehen. + +[MOB22_D] +Sie haben mein Wort darauf. Einstweilen möchte ich + +[MOB22_E] +Ihnen den Dank meines Volkes aussprechen, für das Sie so viel getan haben. + +[MOB_25A] +Tommy, hier Cortez. Die Franzosen machen mir Ärger. Und wie. + +[MOB_25B] +Verdammte Heuchler! Jahrhundertelang beuten sie arme Länder aus und mich schimpfen sie einen Dieb! + +[MOB_25C] +Ich brauche dingend Ihre Hilfe. + +[MOB_25D] +Beeilen Sie sich. Ich brauche Sie. Ich hasse die Franzosen. + +[MOB_26A] +Hallo, Tommy? + +[MOB_26B] +Ja? + +[MOB_26C] +Baker hier. Wollte dir nur sagen, der Gig hat Spaß gemacht. + +[MOB_26D] +Danke, auch im Namen der Gang. Eins sollst du wissen: + +[MOB_26E] +Du hast unsern Respekt. Halt die Nase im Wind, Junge. + +[MOB_29A] +Hallo? Spreche ich mit Mr. Tommy Vercetti? + +[MOB_29B] +Ja. + +[MOB_29C] +Ich hab mir sagen lassen, du wärst der richtige Mann, wenn man Kroppzeug am Hals hat. + +[MOB_29D] +Vielleicht... + +[MOB_29E] +Tja, ich hab 'ne regelrechte Plage am Hals. Haitianer, überall. + +[MOB_29F] +Mein Name ist Umberto Robina und es wär mir recht, wenn du baldmöglichst ins Café Robina kämst. + +[MOB_29G] +Diesmal haben's die Haitianer nämlich zu weit getrieben. + +[MOB_29H] +Test + +[MOB_30A] +Tommy, hier Umberto Robina. + +[MOB_30B] +Wie läuft das Café? + +[MOB_30C] +Oh, bestens. Sagenhaft, Tommy, sagenhaft. Keine Memmen, Tommy, nur echte Männer. Und wunderschöne Frauen! + +[MOB_30D] +Ich wollte nur sagen, für mich und Paps bist du jetzt einer von uns. Ein Kubaner. + +[MOB_30E] +Du bist ein ganzer Kerl. Du hast Mumm in den Knochen. + +[MOB_30F] +Danke, Umberto. Seit ich aus dem Knast raus bin, hat das keiner zu mir gesagt. Bis dann. + +[MOB_33A] +Tommy, Phil hier. Spar dir die sentimentalen Worte, hör mir einfach zu, ok? + +[MOB_33B] +Also, ich brau hier 'nen extra starken TNT-Whiskey und ich wollte fragen, ob du mal 'nen Schluck probieren willst. + +[MOB_33C] +Im Ernst, Tommy, wenn du 'nen Drink willst, oder 'ne Wand abbeizen musst - das Zeug macht einen Mann aus dir. + +[MOB_33D] +Bei mir hat's jedenfalls gewirkt, auch wenn ich jetzt auf einem Auge nichts mehr sehe. Ich warte auf dich. + +[MOB_34A] +Tommy, war ein Vergnügen, mit dir zu arbeiten. So einen Spaß hatte ich seit Vietnam nicht mehr. + +[MOB_34B] +Also, falls du je irgendwas brauchst, ruf mich an, ok? + +[MOB_34C] +Ich vergesse keinen, mit dem ich in der Schlacht war. + +[MOB_34D] +Und ich kann dir bestimmt irgendwann helfen, ok? + +[MOB_35A] +Tommy, die Wunde verheilt gut. Ist nur komisch: + +[MOB_35B] +da hab ich auf 6 Schlachtfeldern gekämpft und nie 'n Kratzer abgekriegt und dann das! + +[MOB_35C] +Jetzt bin ich der einarmige Phil. Hab aber 'n gutes Arsenal an Handfeuerwaffen, also bin ich auch mit Arm ab nicht arm dran. + +[MOB_35D] +Also hör auf mit der sentimentalen Scheiße und hol dir 'nen Drink, ok! + +[MOB_36A] +Tommy, hier Phil. Wollte mich bedanken, dass du mich da rausgehauen hast. + +[MOB_36B] +Verdammte Vietnamesen. Wo du hinschaust ein Hinterhalt. + +[MOB_36C] +Die Wunde heilt gut. Und jetzt kassiere ich meine Versehrtenrente endlich völlig zurecht. Danke, Kumpel. + +[MOB_40A] +Hey, Tommy, hier Sonny. Was macht die Sonnenbräune? + +[MOB_40B] +Ich hab keine Sonnenbräune. + +[MOB_40C] +Naja, mein Geld hast du jedenfalls auch nicht. Daher frag ich mich, + +[MOB_40D] +was treibst du eigentlich die ganze Zeit, Tommy? Sag doch mal? + +[MOB_40E] +Ich bin auf der Suche nach deinem Geld, Sonny. Keine Sorge. + +[MOB_40F] +Ich mach mir aber Sorgen, Tommy, ich kann nicht anders. + +[MOB_40G] +Ich hab nämlich anscheinend mit zu viel unzuverlässigen Menschen zu tun. + +[MOB_40H] +Sei du bitte kein unzuverlässiger Mensch, Tommy. + +[MOB_40I] +Tu dir und mir einen Gefallen. Ich freu mich, von dir zu hören. + +[MOB_41A] +Tommy, kennst du mich noch? + +[MOB_41B] +Hey, Sonny. + +[MOB_41C] +Ja genau, Sonny. Wir sind doch alte Freunde, + +[MOB_41D] +aber du schreibst nie, rufst nie an. Willst du denn nicht mehr mein Freund sein? + +[MOB_41E] +Ich hab alle Hände voll zu tun, die Sache zu regeln. Und du bist auch keine große Hilfe. + +[MOB_41F] +Ach, ich bin also schuld? Ja, ich hab gehört, dass du zu tun hast... + +[MOB_41G] +Musst Drogenbarone erledigen, ihre Geschäfte übernehmen. + +[MOB_41H] +Vergiss mich nicht, Tommy. Ich verspreche dir nämlich, dass ich dich nicht vergesse... + +[MOB_42A] +Tommy. + +[MOB_42B] +Sonny. + +[MOB_42C] +Anscheinend hörst du neuerdings schlecht, deshalb frag ich dich jetzt nochmal: + +[MOB_42D] +Tommy, wo ist die verdammte Kohle? Wo ist der verdammte Stoff und wo ist mein Anteil an deinem neuen Geschäft? + +[MOB_42E] +Du hältst mich zum Narren, Tommy, ich find's bloß nicht zum Lachen. + +[MOB_43A] +Tommy, Tommy, Tommy, Sonny hat gerade angerufen, dämmert dir was? + +[MOB_43B] +Ich weiß nicht, wie das mit dir ist, aber wenn mir jemand droht, dass er meine Familie umbringt, + +[MOB_43C] +dann macht mir das eine Scheißangst. Was gedenkst du zu unternehmen? + +[MOB_43D] +Nur die Ruhe, Ken, alles wird cool. + +[MOB_43E] +Ich BIN ruhig. So ruhig wie man sein kann, wenn man um sein Leben fürchtet! + +[MOB_43F] +Ken, ich muss mich gerade auf was anderes konzentrieren. + +[MOB_43G] +Wir kümmern uns zu gegebener Zeit um Forelli. + +[MOB_43H] +Ich bin ruhig. Hör ich mich nicht ruhig an? Muss die Todesangst sein, die auf die Stimme schlägt. + +[MOB45_A] +Tommy, wir müssen miteinander reden. + +[MOB45_B] +Wo liegt das Problem, Lance? + +[MOB45_C] +Bei dir, mein Freund: Ich finde, du gibst mir keinen fairen Anteil. + +[MOB45_D] +Noch dazu hast du mich vor den Jungs blamiert. Das kann ich nicht auf mir sitzen lassen. + +[MOB45_E] +Lance, das siehst du falsch. Du hast Fehler gemacht. + +[MOB45_F] +Tommy, ich bin nicht dein Laufbursche. Nicht dein Lakai. + +[MOB45_G] +Lance, alles ist ok, solange du keinen Mist baust. Und sollte ICH Mist bauen, steh ich dafür gerade. + +[MOB45_H] +Tommy, ich hab alles für dich getan und du trittst mich mit Füßen. Mach das doch nicht. + +[MOB45_I] +Lance, ich betrüg dich nicht und falle dir nicht in den Rücken, ok? + +[MOB45_J] +Reiß dich zusammen. Es ist schwer genug, ohne dass du mir die Ohren vollheulst. + +[MOB45_K] +Vertrau mir, hörst du. Hörst du? + +[MOB45_L] +Ja, Tommy, aber ich halt das nicht mehr lange aus. + +[MOB45_M] +Lance, komm mir bloß nicht so, ich warne dich. + +[MOB45_N] +Reg dich ab, mach ein paar Tage frei, dann unterhalten wir uns, ok? + +[MOB46_A] +Tommy - Lance. + +[MOB46_B] +Ja? + +[MOB46_C] +Kein 'Schön, dass du anrufst, Lance!' Kein freundliches Wort? Was soll das? + +[MOB46_D] +Ich habe gerade zu tun, Lance. Was willst du? + +[MOB46_E] +Nichts. Ich wollte dir nur sagen, wir kriegen das hin. + +[MOB46_F] +Du und ich, ohne Probleme. Du verstehst mich? + +[MOB46_G] +Wir müssen es hinkriegen. Sonst sind wir erledigt, Lance. + +[MOB46_H] +Wir stecken schon viel zu tief drin. Aber danke für den Anruf. Du hörst von mir. + +[MOB_47A] +Tommy - Lance. Wir stecken in der Patsche. Du musst sofort kommen. + +[MOB52_A] +Hey, Leo, ich glaube, wir haben einen Käufer für Diaz' Ware. + +[MOB52_B] +Du musst ihn anrufen und den Deal in die Wege leiten, ok? + +[MOB52_C] +Wo bist du gerade? + +[MOB52_D] +Ist irgendwas, Leo? Du hörst dich so komisch an. + +[MOB52_E] +Sag mir, wo du bist. + +[MOB52_F] +Verdammt, wer ist da dran? Gib mir Leo! + +[MOB52_G] +Leo ist 'ne Weile verreist, ich vertrete ihn. + +[MOB52_H] +Zum Teufel mit dir! + +[MOB54_A] +Hi, Tommy! + +[MOB54_B] +Hi, Mercedes. Wie geht's? + +[MOB54_C] +Ich hab 'ne neue Wohnung in Vice Point. + +[MOB54_D] +Vielleicht kommst du mich ja mal besuchen. + +[MOB54_E] +Klar, gern. Also, bis später. + +[MOB55_A] +Tommy, ich bin's. + +[MOB55_B] +Hi, Mercedes. + +[MOB55_C] +Tommy, mir ist so langweilig. Wann hast du denn mal Zeit für mich? + +[MOB55_D] +Was soll denn das heißen? + +[MOB55_E] +Ich weiß, du hast so viel zu tun, musst Leute beseitigen und bestechen. + +[MOB55_F] +Aber ich will mal wieder Spaß haben. Also vergiss mich nicht, hörst du? + +[MOB56_A] +Tommy, es heißt, du hast Ricardo Diaz erledigt. + +[MOB56_B] +In seinem Haus ist ein Feuer ausgebrochen. + +[MOB56_C] +Ich glaube, er hatte ein Acrylhemd an und ist verbrannt. + +[MOB56_D] +Tommy, ich bin so stolz auf dich. Ich wusste, du bist ein echter Mann. + +[MOB56_E] +Und er war ein Mistkerl. Ich bin so stolz, dass du mein Freund bist. + +[MOB56_F] +Ich weiß, du hast zu tun, du musst schließlich die Stadt erobern. + +[MOB56_G] +Aber vergiss mich nicht, hörst du? + +[MOB57_A] +Ich bin's, Mercedes. Ich liebe dich nicht mehr, Tommy. + +[MOB57_B] +Wirklich nicht mehr. Du bist nicht mehr nett zu Mercedes. + +[MOB57_C] +Du behandelst mich nicht mehr wie eine Dame. Du ignorierst mich und ich hasse dich. + +[MOB57_D] +Ich bestehe darauf, dass du sofort zu mir kommst! + +[MOB58_A] +Tommy. + +[MOB58_B] +Hey, Mercedes. + +[MOB58_C] +Selber hey, du toller Hecht. Ich bin sauer auf dich, Tommy. + +[MOB58_D] +Schick mich nie mehr zu diesem Jezz Torrent. + +[MOB58_E] +So ein Jammerlappen. Mittendrin fängt er an zu weinen wegen seinem Hündchen, + +[MOB58_F] +das gestorben ist, als er 7 war, und dass seine Mama ihn nie lieb hatte. + +[MOB58_G] +Und Tommy - privat läuft er in Perücke und BH rum. + +[MOB58_H] +Ich bin nicht gut auf dich zu sprechen! + +[MOB59_A] +Oh, Tommy. Ich bin's, Mercedes. + +[MOB59_B] +Ich wollte dir nur sagen, es ist ganz toll beim Film. + +[MOB59_C] +Wenn du nochmal etwas für mich hast, sag Bescheid. + +[MOB59_D] +Wirklich. Ich wollte immer Schauspielerin werden. + +[MOB59_E] +Ich denke, ich lerne viel über die Dramaturgie. + +[MOB59_F] +Es ist so lehrreich! Vielen, vielen Dank. Bis bald. Adios. + +[MOB_99] +Geh zu der Telefonzelle vor Ort. + +[MOB_98] +Geh zu der Telefonzelle vor Ort. + +[MOB_97] +Geh zu der Telefonzelle vor Ort. + +[MOB_96] +Geh zu der Telefonzelle vor Ort. + +[MOB_95] +Geh zu der Telefonzelle vor Ort. + +[A_TIME] ++~1~ Sekunden + +[F_RANGE] +~g~Du bist außer Reichweite des Feuerwehrfunks. Fahr näher an eine Feuerwache heran! + +[DODO_FT] +Du bist ~1~ Sekunden geflogen! + +[GA_8] +Benutze den Zünder, um die Bombe hochgehen zu lassen. + +[GA_10] +Hübsche Karre. Hier sind deine $~1~. + +[GA_11] +So eine Karre haben wir schon. Die können wir nicht gebrauchen. + +[GA_12] +Bombe ist scharf. + +[GA_13] +Auf dich ist Verlass. Wenn du alle, die auf der Liste stehen, abgeliefert hast, kriegst du einen Bonus. + +[GA_14] +Du hast alle georderten Karren geliefert. Sehr gut. Hier, für dich. + +[GA_15] +Hoffentlich gefällt dir die neue Farbe. + +[GA_16] +Das Umspritzen ist gratis. + +[GA_19] +An dem Modell haben wir kein Interesse. + +[GA_20] +Von der Sorte haben wir schon mehr als genug. Sorry, da kommen wir nicht ins Geschäft. + +[CHASE] +Größtes Medieninteresse bisher + +[CHASE1] +Null + +[CHASE2] +Minimal + +[CHASE3] +Vages Interesse + +[CHASE4] +Lokalblatt Seite 7 + +[CHASE5] +Lokalblatt Titelseite + +[CHASE6] +Vice Courier einspaltig + +[CHASE7] +Vice Courier Titelseite + +[CHASE8] +Lokalsender 3-Uhr-Nachrichten + +[CHASE9] +Lokalsender 20-Uhr-Nachrichten + +[CHASE10] +Lokalsender Live-Berichterstattung + +[CHASE11] +UFA Today Seite 12 + +[CHASE12] +UFA Today Seite 4 + +[CHASE13] +Foto in UFA Today + +[CHASE14] +Landesweites TV 4-Uhr-News + +[CHASE15] +Landesweites TV 20-Uhr-News + +[CHASE16] +Landesweite Live-Berichterstattung + +[CHASE17] +Internationale Berichterstattung + +[CHASE18] +Nationale Krise + +[CHASE19] +Internationale Krise + +[CHASE20] +Weltereignis + +[CHASE21] +Stoff aus dem Legenden sind + +[CR_1] +Kran kann dieses Fahrzeug nicht anheben. + +[PU_MONY] +Du hast nicht genug Geld. + +[CO_ALL] +Du hast sie alle geliefert. Hier, für dich. + +[FEM_ON] +AN + +[FEM_OFF] +AUS + +[FEM_YES] +Ja + +[FEM_NO] +Nein + +[FEM_RET] +Wiederholen + +[FEC_NA] +Nicht verfügbar + +[FEC_CWL] +Eine Waffe nach links + +[FEC_CWR] +Eine Waffe nach rechts + +[FEC_LOF] +Nach vorne sehen + +[FEC_TAR] +Zielen + +[FEC_MOV] +Bewegung + +[FEC_CAM] +Blickwinkel + +[FEC_PAU] +Pause + +[FEC_ENV] +In Fahrzeug einsteigen + +[FEC_JUM] +Springen + +[FEC_ATT] +Angreifen oder Waffe abfeuern + +[FEC_RUN] +Rennen + +[FEC_FPC] +Subjektive Kamera + +[FEC_LL] +Nach links sehen + +[FEC_LB] +Nach hinten sehen + +[FEC_LR] +Nach rechts sehen + +[FEC_HOR] +Hupe + +[FEC_VES] +Fahrzeugsteuerung + +[FEC_BRA] +Bremsen oder rückwärts fahren + +[FEC_HAB] +Handbremse + +[FEC_CAW] +Fahrzeugwaffe + +[FEC_ACC] +Beschleunigen + +[FEC_CCF] +Konfiguration : + +[FEC_CF1] +Konfig. 1 + +[FEC_CF2] +Konfig. 2 + +[FEC_CF3] +Konfig. 3 + +[FEC_CF4] +Konfig. 4 + +[FEC_CDP] +Controller-Anzeige : + +[FEC_ONF] +Zu Fuß + +[FEC_INC] +Im Auto + +[FEC_VIB] +Vibration : + +[FEL_ENG] +Englisch + +[FEL_FRE] +Französisch + +[FEL_GER] +Deutsch + +[FEL_ITA] +Italienisch + +[FEL_SPA] +Spanisch + +[FED_DBG] +Menu Debug + +[FED_RID] +Reload IDE + +[FED_RIP] +Reload IPL + +[FED_PAH] +Parse Heap + +[FED_DFL] +CTheScripts::DbgFlag + +[FED_DLS] +Big White Debug Light Switched + +[FED_SPR] +Show Ped Road Groups + +[FED_SCR] +Show Car Road Grups + +[FED_SCZ] +Show Cull Zones + +[FED_DSR] +Debug Streaming Requests + +[FED_SCP] +gbShowCollisionPolys + +[PL_STAT] +Spielerstatistiken + +[PE_WAST] +Von dir abservierte Personen + +[PE_WSOT] +Von anderen abservierte Personen + +[TM_BUST] +Zahl deiner Verhaftungen + +[GNG_WST] +Gang-Mitglieder + +[DED_CRI] +Kriminelle + +[PER_COM] +Absolviert (in Prozent) + +[KGS_EXP] +Sprengstoffverbrauch in kg + +[ACCURA] +Treffsicherheit + +[ST_WEAP] +Waffen-Budget + +[ST_PROP] +Budget für Objekte + +[ST_AUTO] +Budget für Autoreparaturen und Lackierung + +[ST_PHOT] +Von dir gemachte Fotos + +[ST_LOAN] +Besuche von Kredithaien + +[ST_STOR] +Geknackte Läden + +[ST_MOVI] +Film-Stunts + +[ST_PIZZ] +Gelieferte Pizzas + +[ST_GARB] +Getätigte Müllfuhren + +[ST_ICEC] +Verkaufte Eiscreme + +[TOP_SHO] +Beste Schießstand-Punktzahl + +[SHO_RAN] +Schießstand-Rangliste + +[SEAGULL] +Abgeschossene Möwen + +[PROPOWN] +Objekte in deinem Besitz + +[ST_TIME] +Gespielte Zeit + +[ST_FTIM] +Flugstunden + +[ST_PRAN] +Piloten-Rating + +[ST_RAN0] +Anfänger + +[ST_RAN1] +Navigator + +[ST_RAN2] +Co-Pilot + +[ST_RAN3] +Halb-Profi + +[ST_RAN4] +Könner + +[ST_RAN5] +Profi + +[ST_RAN6] +Ass + +[ST_RAN7] +Roter Baron + +[ST_DRWN] +Gefütterte Fische + +[ST_FASH] +Kleidungsbudget + +[ST_DAMA] +Zerstörte Objekte + +[TM_DED] +Krankenhausbesuche + +[DAYSPS] +Im Spiel verstrichene Tage + +[NUMSHV] +Besuche in Versteck + +[MXCARD] +Weitester IRRSINNS-Sprung (in Fuß) + +[MXCARJ] +Höchster IRRSINNS-Sprung (in Fuß) + +[MXCARDM] +Weitester IRRSINNS-Sprung (m) + +[MXCARJM] +Höchster IRRSINNS-Sprung (m) + +[MXFLIP] +Max. Anzahl Salti + +[MXJUMP] +Max. Drehungen im Sprung + +[BUL_FIR] +Verschossene Kugeln + +[BUL_HIT] +Kugeln, die trafen + +[SPRAYIN] +Anzahl Lackierungen + +[BSTSTU] +Bester IRRSINNS-Stunt bisher + +[INSTUN] +Irrsinns-Stunt + +[PRINST] +Super Irrsinns-Stunt + +[DBINST] +Doppelter Irrsinns-Stunt + +[DBPINS] +Super-Doppel-Irrsinns-Stunt + +[TRINST] +Dreifacher Irrsinns-Stunt + +[PRTRST] +Super-Dreifach-Irrsinns-Stunt + +[QUINST] +Vierfacher Irrsinns-Stunt + +[PQUINS] +Super-Vierfach-Irrsinns-Stunt + +[NOSTUC] +Bisher keine Irrsinns-Stunts geschafft + +[NOUNIF] +Monster-Stunts geschafft + +[NMISON] +Begonnene Missionen + +[PASDRO] +Ans Ziel beförderte Fahrgäste + +[MONTAX] +Mit Taxi verdientes Geld + +[DAYPLC] +Tagesetat für Polizei + +[CRIMRA] +Rang: + +[STPR_1] +Malibu Club + +[STPR_2] +Druckerei + +[STPR_3] +Filmstudio + +[STPR_4] +Eiscremefabrik + +[STPR_5] +Autohaus + +[STPR_6] +Taxiunternehmen + +[STPR_7] +Bootswerft + +[SET1EN] +Konfig. 1 aktiviert + +[GMSAVE] +Spiel speichern + +[FEDS_TB] +Zurück + +[FEST_OO] +von + +[FEC_TUC] +Geschützsteuerung + +[FEC_RS3] +Radiosender auswählen (L3-Taste) + +[FEC_HO3] +Hupe (L3-Taste) + +[C_FAIL] +Bürgerwehr-Mission beendet! + +[C_ESCP] +~r~Der Verdächtige ist entwischt! + +[C_VIGIL] +BÜRGERWEHR BONUS!! + +[HEAL_A] +Dein ~h~Gesundheitszustand~w~ wird rechts oben auf dem Bildschirm in Orange angezeigt. + +[WRONGCD] +Falsche DVD. Bitte legen Sie die richtige DVD ein. + +[NOCD] +Die DVD-Lade ist leer. Bitte DVD einlegen. + +[OPENCD] +Die DVD-Lade ist offen. Bitte schließen. + +[CDERROR] +Fehler beim Lesen der 'Grand Theft Auto: Vice City' DVD. + +[RESTART] +Neues Spiel wird gestartet + +[GA_3] +Keine Gratisjobs mehr. Umspritzen kostet $100! + +[GA_1] +Hey, so was heißes rühre ich nicht an! + +[GA_1A] +Komm wieder, wenn du nicht so viel zu tun hast... + +[HELP9_C] +Drücke die~h~ ~k~~PED_FIREWEAPON~~w~, um das Präzisionsgewehr ~h~abzufeuern~w~. + +[TAXI2] +~r~Die Zeit ist um! + +[PAGEB13] +Gesundheits-Powerups in Versteck angeliefert. + +[PAGEB14] +Adrenalin in Versteck vorrätig. + +[FESZ_CA] +Abbrechen + +[FES_NGA] +Neues Spiel + +[FES_CAN] +Abbrechen + +[FESZ_QL] +Alle nicht gespeicherten Daten des aktuellen Spiels werden verloren gehen. Ladevorgang fortsetzen? + +[FESZ_QD] +Dieses gespeicherte Spiel wirklich löschen? + +[FESZ_QO] +Dieses gespeicherte Spiel wirklich überschreiben? + +[FESZ_QR] +Wirklich ein neues Spiel beginnen? Alle Daten seit dem letzten Speichern werden verloren gehen. Weiter? + +[T4X4_1] +'PCJ Rallye' + +[BMX_1] +'Krasses Gelände' + +[BMX_2] +'Teststrecke' + +[BMXFAIL] +~r~Du hast es nicht geschafft, einen neuen Rekord aufzustellen! + +[BMX_REC] +~g~Neuer Rekord:~1~ !! + +[T4X4_3] +'CHECKPOINT FIEBER' + +[MM_1] +'PYLONEN-RALLYE' + +[T4X4_F] +~r~Du hast gekniffen! Schon überfordert? + +[LANDSTK] +Landstalker + +[IDAHO] +Idaho + +[STINGER] +Stinger + +[LINERUN] +Linerunner + +[PEREN] +Perennial + +[SENTINL] +Sentinel + +[RIO] +Rio + +[PATRIOT] +Patriot + +[FIRETRK] +Feuerwehrwagen + +[TRASHM] +Trashmaster + +[STRETCH] +Stretch Limo + +[MANANA] +Manana + +[INFERNS] +Infernus + +[VOODOO] +Voodoo + +[PONY] +Pony + +[MULE] +Mule + +[CHEETAH] +Cheetah + +[AMBULAN] +Krankenwagen + +[FBICAR] +FBI Washington + +[MOONBM] +Moonbeam + +[ESPERAN] +Esperanto + +[TAXI] +Taxi + +[WASHING] +Washington + +[BOBCAT] +Bobcat + +[WHOOPEE] +Mr Whoopee + +[BFINJC] +BF Injection + +[HUNTER] +Hunter + +[POLICAR] +Polizei + +[ENFORCR] +Enforcer + +[SECURI] +Securicar + +[BANSHEE] +Banshee + +[PREDATR] +Predator + +[BUS] +Bus + +[RHINO] +Rhino + +[BARRCKS] +Barracks OL + +[CUBAN] +Kubanischer Hermes + +[HELI] +Helikopter + +[DODO] +Dodo + +[COACH] +Coach + +[CABBIE] +Cabbie + +[STALION] +Stallion + +[RUMPO] +Rumpo + +[RCBANDT] +RC Bandit + +[ROMERO] +Romeros Leichenwagen + +[PACKER] +Packer + +[ADMIRAL] +Admiral + +[SQUALO] +Squalo + +[SEASPAR] +Sea Sparrow + +[PIZZABO] +Pizza Boy + +[GANGBUR] +Gang Burrito + +[TROPIC] +Tropic + +[SPEEDER] +Speeder + +[REEFER] +Reefer + +[FLATBED] +Flatbed + +[YANKEE] +Yankee + +[CADDY] +Caddy + +[ZEBRA] +Zebra Cab + +[TOPFUN] +Top Fun + +[SKIMMER] +Skimmer + +[PCJ600] +PCJ 600 + +[PHOENIX] +Phoenix + +[FAGGIO] +Faggio + +[FREEWAY] +Freeway + +[RCBARON] +RC Baron + +[RCRAIDE] +RC Raider + +[GLENDAL] +Glendale + +[OCEANIC] +Oceanic + +[SANCHEZ] +Sanchez + +[SPARROW] +Sparrow + +[LOVEFIS] +Love Fist + +[COASTG] +Küstenwache + +[DINGHY] +Dinghy + +[HERMES] +Hermes + +[SABRE] +Sabre + +[SABRETU] +Sabre Turbo + +[WALTON] +Walton + +[REGINA] +Regina + +[COMET] +Comet + +[DELUXO] +Deluxo + +[BURRITO] +Burrito + +[SPAND] +Spandex Express + +[MARQUIS] +Marquis + +[BAGGAGE] +Baggage Handler + +[KAUFMAN] +Kaufman-Taxi + +[COASTMA] +Küstenwachen-Maverick + +[MAVERIC] +Maverick + +[RANCHER] +Rancher + +[FBIRANC] +FBI Rancher + +[VIRGO] +Virgo + +[GREENWO] +Greenwood + +[HOTRING] +Hotring Racer + +[BLISTAC] +Blista Compact + +[FEST_DF] +Zu Fuß zurückgel. Strecke (Meilen) + +[FEST_DC] +Mit Auto gefahrene Strecke (Meilen) + +[FESTDFM] +Zu Fuß zurückgelegte Strecke (Meter) + +[FESTDCM] +Mit Auto gefahrene Strecke (Meter) + +[TOT_DIS] +Insgesamt zurückgel. Strecke (Meilen) + +[TOTDISM] +Insgesamt zurückgel. Strecke (Meter) + +[DISTHEL] +Mit Helikopter geflogene Strecke (Meilen) + +[DISTHEM] +Mit Helikopter geflogene Strecke (Meter) + +[DISTBOA] +Mit Boot zurückgel. Strecke (Meilen) + +[DISTBOM] +Mit Boot zurückgel. Strecke (Meter) + +[FEST_LS] +Mit Krankenwagen gerettete Menschen + +[FEST_CC] +Kriminelle bei Bürgerwehr-Mission + +[FEST_FE] +Gelöschte Feuer gesamt + +[FEST_RP] +Bestandene Amokfahrten + +[FEST_MP] +Erledigte Missionen + +[FEST_BB] +Schnelle Autos, Schnelles Geld: + +[FEST_H0] +Meiste Checkpoints + +[FEST_GC] +Geschrottete Gang-Autos: + +[FEST_H1] +Im Dschungel der Diablos + +[FEST_H2] +Mafia-Massaker + +[FEST_H3] +Der Casino-Coup + +[FEST_H4] +Rock'n'Roll mit Rumpo + +[USJ] +MONSTER-STUNT-BONUS! + +[RATNG1] +Unbescholtener Bürger + +[RATNG2] +Durchschnittstyp + +[RATNG3] +Falschparker + +[RATNG4] +Ladendieb + +[RATNG5] +Rowdy + +[RATNG6] +Laufbursche + +[RATNG7] +Taschendieb + +[RATNG8] +Kleptomane + +[RATNG9] +Informant + +[RATNG10] +Spitzel + +[RATNG11] +Verräter + +[RATNG12] +Hochstapler + +[RATNG13] +Betrüger + +[RATNG14] +Schieber + +[RATNG15] +Falschspieler + +[RATNG16] +Schläger + +[RATNG17] +Straßengauner + +[RATNG18] +Gauner + +[RATNG19] +Halunke + +[RATNG20] +Outlaw + +[RATNG21] +Schurke + +[RATNG22] +Kurier + +[RATNG23] +Gangster + +[RATNG24] +Obergangster + +[RATNG25] +Galgenvogel + +[RATNG26] +Ex-Knacki + +[RATNG27] +Schwerer Junge + +[RATNG28] +Profi + +[RATNG29] +Drahtzieher + +[RATNG30] +Fahrer + +[RATNG31] +Soldat + +[RATNG32] +Gorilla + +[RATNG33] +Kopfgeldjäger + +[RATNG34] +Mann fürs Grobe + +[RATNG35] +Ronin + +[RATNG36] +Abräumer + +[RATNG37] +Hit-Man + +[RATNG38] +Partner + +[RATNG39] +Butcher + +[RATNG40] +Troubleshooter + +[RATNG41] +Attentäter + +[RATNG42] +Adjutant + +[RATNG43] +Gemachter Mann + +[RATNG44] +Rechte Hand + +[RATNG45] +Vollstrecker + +[RATNG46] +Leutnant + +[RATNG47] +Vize-Boss + +[RATNG48] +Capo + +[RATNG49] +Boss + +[RATNG50] +Oberboss + +[RATNG51] +Don + +[RATNG52] +King of the City + +[PAGE_00] +.. + +[WELCOME] +WILLKOMMEN IN + +[TSCORE] +EINKÜNFTE: $~1~ + +[PBOAT_2] { reVC update } +Drücke die ~h~~k~~VEHICLE_FIREWEAPON~~w~, um die Bordkanonen abzufeuern. + +[HJSTAT] +Distanz: ~1~.~1~m Höhe: ~1~.~1~m Saltos: ~1~ Drehungen: ~1~_ + +[HJSTATW] +Distanz: ~1~.~1~m Höhe: ~1~.~1~m Saltos: ~1~ Drehungen: ~1~_ Und was für eine Landung! + +[ATUTOR] +Drücke die ~h~~k~~TOGGLE_SUBMISSIONS~~w~, um Krankenwagen-Missionen an- oder abzuschalten. + +[FEST_HA] +Höchster Krankenwagen-Missions-Level + +[C_KILLS] +KRIMINELLE: ~1~ + +[HJSTATF] +Distanz: ~1~Fuß Höhe: ~1~Fuß Saltos: ~1~ Drehungen: ~1~_ + +[HJSTAWF] +Distanz: ~1~Fuß Höhe: ~1~Fuß Saltos: ~1~ Drehungen: ~1~_ Und was für eine Landung! + +[CRED001] +ROCKSTAR NORTH + +[CRD001A] +STUDIO DIRECTOR + +[CRD001B] +ANDREW SEMPLE + +[CRED002] +PRODUCER + +[CRD002A] +DEVELOPMENT DIRECTOR + +[CRED003] +LESLIE BENZIES + +[CRED004] +ART DIRECTOR + +[CRED005] +AARON GARBUT + +[CRED006] +TECHNICAL DIRECTORS + +[CRED007] +OBBE VERMEIJ + +[CRED008] +ADAM FOWLER + +[CRED010] +ANDREW DUTHIE + +[CRED011] +CRAIG FILSHIE + +[CRED012] +WILLIAM MILLS + +[CRED013] +CHRIS ROTHWELL + +[CRD013A] +IMRAN SARWAR + +[CRD013B] +JAMES WORRALL + +[CRD013C] +JOHN HAIME + +[CRED014] +WRITTEN BY + +[CRED015] +JAMES WORRALL + +[CRED016] +PAUL KUROWSKI + +[CRED017] +DAN HOUSER + +[CRED018] +LEAD CHARACTER DESIGNER + +[CRD018A] +CHARACTER DESIGNER + +[CRED019] +IAN MCQUE + +[CRD019A] +TOKS SOLARIN + +[CRD019B] +ALAN DAVIDSON + +[CRED020] +LEAD ANIMATOR + +[CRED021] +ALEX HORTON + +[CRD022A] +ANIMATORS + +[CRED022] +LEE MONTGOMERY + +[CRD022B] +DUNCAN SHIELDS + +[CRD022C] +GUS BRAID + +[CRED023] +VEHICLE DESIGNERS + +[CRD023A] +JOLYON ORME + +[CRD023B] +ALAN DUNCAN + +[CRD024A] +LEAD VEHICLE DESIGNER + +[CRED024] +PAUL KUROWSKI + +[CRED025] +MAP DESIGNERS + +[CRED026] +ADAM COCHRANE + +[CRED027] +NIK TAYLOR + +[CRED028] +GARY MCADAM + +[CRED029] +KEIRAN BAILLIE + +[CRED030] +ALISDAIR WOOD + +[CRED031] +ANDREW SOOSAY + +[CRD031A] +STEVEN MULHOLLAND + +[CRD031B] +WAYLAND STANDING + +[CRD031C] +CAMPBELL J. DICK + +[CRD031D] +GRAPHIC DESIGNER + +[CRD031E] +STUART PETRI + +[CRED032] +LEAD PROGRAMMER + +[CRD032A] +PROGRAMMERS + +[CRED033] +ALEXANDER ROGER + +[CRED034] +GRAEME WILLIAMSON + +[CRED035] +BARANE CHAN + +[CRED036] +DEREK PAYNE + +[CRED037] +GORDON YEOMAN + +[CRD037A] +ALAN CAMPBELL + +[CRD037B] +MARK HANLON + +[CRD037C] +ANDRZEJ MADAJCZYK + +[CRED038] +LEAD MUSIC PRODUCER + +[CRED039] +CRAIG CONNER + +[CRED040] +STUART ROSS + +[CRED041] +LEAD AUDIO ENGINEER + +[CRED042] +ALLAN WALKER + +[CRD041A] +AUDIO ENGINEER + +[CRD041B] +AUDIO + +[CRD042A] +WILL MORTON + +[CRED043] +AUDIO PROGRAMMER + +[CRED044] +RAYMOND USHER + +[CRED045] +TEST MANAGER + +[CRED046] +CRAIG ARBUTHNOTT + +[CRED047] +LEAD QA + +[CRED048] +NEIL CORBETT + +[CRED049] +KEVIN WONG + +[CRED050] +QA + +[CRED051] +DAVID BEDDOES + +[CRED052] +DAVID WATSON + +[CRED053] +BARRY CLARK + +[CRED054] +ROSS SPARROW + +[CRED055] +JAMES ALLAN + +[CRED056] +NEIL MEIKLE + +[CRD056A] +GEORGE WILLIAMSON + +[CRD056B] +MATT JONES + +[CRD056C] +ROB HARBOUR + +[CRD056D] +TOM WHITTAKER + +[CRED057] +LEAD TECHNICAL SUPPORT + +[CRED058] +LORRAINE ROY + +[CRD057A] +TECHNICAL SUPPORT + +[CRED059] +CHRISTINE CHALMERS + +[CRD060A] +OFFICE SUPPORT + +[CRD060B] +KIM GURNEY + +[CRD060C] +CASSIE OLIVER + +[CRED060] +ROCKSTAR NEW YORK + +[CRED061] +EXECUTIVE PRODUCER + +[CRED062] +SAM HOUSER + +[CRED063] +PRODUCER + +[CRED064] +DAN HOUSER + +[CRED065] +VP OF DEVELOPMENT + +[CRED066] +JAMIE KING + +[CRED067] +CHIEF TECHNOLOGY OFFICER + +[CRED068] +GARY J. FOREMAN + +[CRED069] +ASSOCIATE PRODUCER + +[CRED070] +JEREMY POPE + +[CRD071A] +DIRECTOR OF QUALITY ASSURANCE + +[CRD072A] +JEFF ROSA + +[CRED071] +MUSIC SUPERVISOR + +[CRED072] +TERRY DONOVAN + +[CRED073] +PRODUCTION TEAM + +[CRED074] +TERRY DONOVAN + +[CRED075] +JENNIFER KOLBE + +[CRED076] +JENEFER GROSS + +[CRED077] +LAURA PATERSON + +[CRED078] +JEFF CASTANEDA + +[CRED079] +JERONIMO BARRERA + +[CRED080] +CARLY SLATER + +[CRED081] +JUNG KWAK + +[CRED082] +BRIAN WOOD + +[CRED083] +RENAUD SEBANNE + +[CRED084] +RICHARD KRUGER + +[CRD084A] +DANIEL EINZIG + +[CRD084B] +JACEN BURROWS + +[CRD084C] +LINN PR + +[CRD084D] +COVER ART + +[CRD084E] +STEPHEN BLISS + +[CRED085] +KENT PAUL'S 80 NOSTALGIA ZONE + +[CRED086] +WRITTEN BY DAN HOUSER + +[CRD086A] +PRODUCED BY ADAM TEDMAN + +[CRED087] +WWW.KENTPAUL.COM AND WWW.VICECITY.COM + +[CRED088] +CREATED BY + +[CRD088A] +ADAM TEDMAN + +[CRD088B] +DAVID YU + +[CRD088C] +JERRY LUNA + +[CRD088D] +STUART PETRI + +[CRD088E] +MICHAEL CARNEVALE + +[CRD088F] +GREG LAU + +[CRD088G] +FUTABA HAYASHI + +[CRED089] +QA MANAGER + +[CRED090] +CRAIG ARBUTHNOTT + +[CRED091] +LEAD ANALYST + +[CRED092] +ADAM DAVIDSON + +[CRD092A] +JOE HOWELL + +[CRD092B] +MARC FERNANDEZ + +[CRED093] +GAME ANALYST + +[CRED094] +RICHARD HUIE + +[CRED095] +ROCKSTAR TEST TEAM + +[CRED096] +LANCE WILLIAMS + +[CRED097] +JOE GREENE + +[CRED098] +BRIAN PLANER + +[CRD098A] +ELIZABETH SATTERWHITE + +[CRD098B] +JAMEEL VEGA + +[CRD098C] +MIKE HONG + +[CRED099] +LEE CUMMINGS + +[CRED100] +STORY + +[CRED101] +JAMES WORRALL + +[CRED102] +DAN HOUSER + +[CRED103] +ADAM TEDMAN + +[CRED104] +PAUL YEATES + +[CRED105] +JENEFER GROSS + +[CRED106] +LAURA PATERSON + +[CRED107] +CUT SCENES + +[CRED108] +WRITTEN BY DAN HOUSER AND JAMES WORRALL + +[CRED109] +AUDIO DIRECTED BY DAN HOUSER AND NAVID KHONSARI + +[CRED110] +PRODUCED BY JAMIE KING + +[CRD110A] +TALENT PROCUREMENT: JAMIE KING, SEAN MACALUSO + +[CRED111] +CAST + +[CRD111A] +SUPPORTING CHARACTERS + +[CRED112] +TOMMY VERCETTI - RAY LIOTTA + +[CRED113] +KEN ROSENBERG - WILLIAN FICHTNER + +[CRED114] +SONNY FORELLI - TOM SIZEMORE + +[CRED115] +STEVE SCOTT - DENNIS HOPPER + +[CRED116] +AVERY CARRINGTON - BURT REYNOLDS + +[CRED117] +RICARDO DIAZ - LUIS GUZMAN + +[CRED118] +LANCE VANCE - PHILIP MICHAEL THOMAS + +[CRED119] +COLONEL JUAN CORTEZ - ROBERT DAVI + +[CRED120] +UMBERTO ROBINA - DANNY TREJO + +[CRED121] +PHIL CASSIDY - GARY BUSEY + +[CRED122] +MITCH BAKER - LEE MAJORS + +[CRED123] +MERCEDES CORTEZ - FAIRUZA BALK + +[CRED124] +KENT PAUL - DANNY DYER + +[CRED125] +JEZZ TORRENT - KEVIN MCKIDD + +[CRED126] +TAXI CONTROLLER - DEBORAH HARRY + +[CRED127] +CANDY SUXX - JENNA JAMESON + +[CRED128] +BJ SMITH - LAWRENCE TAYLOR + +[CRED129] +AUNTIE POULET - YOUREE CLEOMILI HARRIS + +[CRED130] +SUPPLIER - ARMANDO RIESCO + +[CRED131] +COUGAR - BLAYNE PERRY + +[CRED132] +HILARY - CHARLES TUCKER + +[CRED133] +CONGRESSMAN ALEX SHRUB - CHRIS LUCAS + +[CRED134] +OLD MAN KELLY - GEORGE DICENZO + +[CRD134A] +CAM JONES - GREG SIMS + +[CRD134B] +PSYCHO - HUNTER PLATIN + +[CRD134C] +MAUDE THE ICE CREAM LADY - JANE GENNARO + +[CRD134D] +JETHRO - JOHN ZURHELLEN + +[CRD134E] +GONZALES - JORGE PUPO + +[CRD134F] +DWAYNE - NAVID KHONSARI + +[CRD134G] +DICK - PETER MCKAY + +[CRD134H] +MIKE THE GOON & PORN GUY - ROBERT CIHRA + +[CRD134I] +PERCY - RUSSELL FOREMAN + +[CRED135] +MOTION CAPTURE + +[CRED136] +ANIMATED BY + +[CRD136A] +TECHNICAL DIRECTION BY ALEX HORTON + +[CRED137] +DIRECTED BY + +[CRD137A] +DIRECTED BY NAVID KHONSARI + +[CRED138] +PRODUCED BY + +[CRD138A] +PRODUCED BY JAMIE KING + +[CRD138B] +RENAUD SEBBANE + +[CRED139] +RECORDED AT PERSPECTIVE STUDIOS, BROOKLYN + +[CRED140] +MOTION CAPTURE ACTORS + +[CRD140A] +BLAYNE PERRY + +[CRD140B] +JONATHON SALE + +[CRD140C] +CHARLES TUCKER + +[CRD140D] +EDDIE MARRERO + +[CRD140E] +WILLIAM MCCALL + +[CRD140F] +JORGE PUPO + +[CRD140G] +ROBERT JACKSON + +[CRD140H] +TARA RADCLIFFE + +[CRD140I] +JENIFER GAMBETESE + +[CRD140J] +KRIS ACHEVARRIA + +[CRD140K] +ALI ORDOUBADI + +[CRD140L] +KAHLEEM POOLE + +[CRED141] +PEDESTRIAN DIALOGUE + +[CRD141A] +WRITTEN BY DAN HOUSER, MARC FERNANDEZ, GILLIAN TELLING AND NAVID KHONSARI + +[CRD141B] +WITH HELP FROM JEREMY POPE, LANCE WILLIAMS, AND JENNY JEMISON + +[CRED142] +WRITTEN BY + +[CRD142A] +DAN HOUSER AND JAMES WORRALL + +[CRED143] +DIRECTED BY DAN HOUSER, CRAIG CONNER, MARC FERNANDEZ, AND ALLAN WALKER + +[CRED144] +PRODUCED BY RENAUD SEBANNE + +[CRED145] +PEDESTRIANS + +[CRED146] +ADAM DAVIDSON, ADAM WATKINS, ALEJANDRO K. BROWN, ALEX ANTHONY SIOUKAS, ALEX GARCIA, + +[CRED147] +ALICE SALTZMAN, ALISON CIHRA, AMY SALIMA, AMY SALZMAN, ANDREA VIDELA, ANTHONY ATTI, + +[CRED148] +ANTHONY RIVERA, BIJAN SHAMS, BLAYNE PERRY, BRETT BISOGNO, BREYE MATA, BRIAN PANEN, + +[CRED149] +BROCK VODER, CAREY BERTINI, CHARISSE LAMBERT, CHRIS DIFAT, CHRIS REISENBERGER, + +[CRED150] +CHRISTOPHER BRODAY, CHRISTOPHER CARRO, CYNTHIA GREENE, DAMARIES LOPEZ, DAN LEE, + +[CRED151] +DAN SCHNEIDER, DAN TOYAMA, DAVID DEAN CHALTFIELD, JR., DAVID HARRISON, DAVID WILEY, + +[CRED152] +DEBORAH COLLINS, DEBRANDA CHANEY-GILES, DEMETRA KOUKOULAS, DENISE ROSADO, + +[CRED153] +DEVIN BENNETT, DEVIN WINTERBOTTOM, DORIS WOO, DOUGLAS HARRISON, DUNCAN COUTTS, + +[CRED154] +DUPE AJAYI, EDWIN AVELLANEDA, ELIZABETH HOWELL, ELIZABETH SATTERWHITE, ERIC NAGLE, + +[CRED155] +ESTEBAN KARPLUS, F. FONT, FUTABA HAYASHI, GENE HILGREEN, GERALD COSGROVE, + +[CRED156] +GERARD LUNA, GILLIAN TELLING, GREGG CARLUCCI, GREGORY CLERVOIX, GREGORY SCHWEIZER, + +[CRED157] +HADLEY TOMICKI, J. ROSSETT, JAMEEL VEGA, JASON JONES, JEFF ROSA, JENNIFER JEMISON, + +[CRED158] +JEREMY TAGGERT, JESSICA RIDER, JOSEPH GREENE,JOSEPH HOWELL, KATE DUKICH, + +[CRED159] +KEL O'NEILL, KEVIN HOPKINS, LADAWN JAMES, LANCE WILLIAMS, LAURA BUBBLES, + +[CRED160] +LAURA PATTERSON, LEE CUMMINGS, LETICIA L. YOUNG, LINDSAY KENNEDY, LISA ORITZ, + +[CRED161] +LORNA JORDAN, LUCIO AMADIO, MARCO FERNANDEZ, MARIKO TANAKA, MARLON MATTHEWS, + +[CRED162] +MARY TELLING, MASAYOSHI MITSUYAMA, MATTHEW CHUNG, MAX ALLSTADT, MAX BOGDANOV, + +[CRED163] +MELISSA ALVAREZ, MICHAEL MAY, MICHAEL ROTHSTEIN, MIGUEL VIDAL, MIKE FEDERLINE, + +[CRED164] +NATALIE DESCALZO, N'GAI MEMBERS, NICOLAS MALLO, NOELLE SADLER, NORBERT MORIVAN, + +[CRED165] +OSWALD GREENE, JR., PETER MCKAY, PETER APPEL, PRESTON SAVARESE, RAFAEL GONZALES, + +[CRED166] +RANDY JOHNSON, REY CONCEPCION, RICHARD KROGER, ROB TIBBS, ROBERT JACKSON, + +[CRED167] +ROBERT SCHULER, ROSS A. MCINTYRE, RUSSELL FOREMAN, RUTH NUNEZ, SALVADORE SUAZO, + +[CRED168] +SAM WHITE, SANTOS GONZALES, SCOTT SMITH, SEYMOUR FRAILMAN, SPELMAN BRAUMAN, + +[CRED169] +STEPHANIE TELLING, STEVE KNEZEVICH, STEVE ROBERT, SUMIKO YASUDA, SUSAN LEWIS, + +[CRED170] +SYLVIA COLACIOS, TOMOKO MIYAZAKI, TRON, VERDEL HALE, YVES MONDESIR, ZENO LEINFELDER, + +[CRED171] +DAVID BEDDOES, CHRISTINE CHALMERS, BARRY CLARK, NEIL CORBETT, KIM GURNEY, NEIL MEIKLE, + +[CRED172] +CASSIE OLIVER, LORRAINE ROY, DAVID WATSON, KEVIN WONG, WILL MORTON + +[CRED175] +ADAM DAVIDSON + +[CRED176] +LANCE WILLIAMS + +[CRED177] +NEIL MCCAFFREY + +[CRED178] +LAURA PATERSON + +[CRED179] +REY CONCEPCION + +[CRED180] +CHARLES HEROLD + +[CRED181] +ANDREW GREENWALD + +[CRED182] +JAMES MIELKE + +[CRED183] +PETER SUCIU + +[CRED184] +ALEX ODULIO + +[CRED185] +DON NKRUMAH + +[CRED186] +KENDALL PITTMAN + +[CRED187] +SAL SUAZO + +[CRED188] +EREK MATEO + +[CRED189] +CHRIS DIFATE + +[CRED190] +LEILA MILTON + +[CRED191] +DARREN ZOLTOWSKI + +[CRED192] +VIRGINIA SMITH + +[CRED193] +KEVIN CASSIN + +[CRED194] +JASON SHIGEMORI + +[CRED195] +KELLY KINSELLA + +[CRED196] +MOLLIE STICKNEY + +[CRED197] +STANTON SARJEANT + +[CRED198] +LAURA WALSH + +[CRED199] +MARK GARONE + +[CRED200] +JOANNA SLY + +[CRED201] +ELIZABETH HOWELL + +[CRED202] +ANA HERCULES + +[CRED203] +SHIRLEY IRICK + +[CRED204] +KASHONA FIELDS + +[CRED205] +JOEL M. LILJE + +[CRED206] +JOHN DIBENEDETTO + +[CRED207] +NANCY GILES + +[CRED208] +RYAN CROY + +[CRED209] +JENNIFER KOLBE + +[CRED210] +LIAM BURKE + +[CRED211] +SIGRID PREISSL + +[CRED212] +ANITA FITZSIMONS + +[CRED213] +PHILIPPA RASELLI + +[CRED214] +WIL QUESNEL + +[CRED215] +FALKO BURKERT + +[CRED216] +SARA SEWELL + +[CRED217] +RADIO STATIONS AND MUSIC + +[CRED218] +MUSIC CONSULTANCY + +[CRD218A] +HEINZ HENN + +[CRD218B] +STUART ROSS + +[CRED219] +SOUNDTRACK CO-ORDINATOR + +[CRED220] +TERRY DONOVAN + +[CRED221] +PRODUCER FOR ROCKSTAR GAMES + +[CRED222] +DAN HOUSER AND LAZLOW + +[CRED223] +PRODUCER FOR ROCKSTAR NORTH + +[CRED224] +CRAIG CONNER + +[CRED225] +ALLAN WALKER + +[CRED226] +LAZLOW + +[CRED227] +DJ BANTER AND IMAGING + +[CRED228] +WRITTEN BY DAN HOUSER AND LAZLOW + +[CRED229] +FLASH FM + +[CRD229A] +TONI-MARIA CHAMBERS + +[CRD229B] +IMAGING VOICE AND PRODUCTION-JEFF BERLIN + +[CRED230] +SPECIAL THANKS TO + +[CRED231] +TOMMY MOTTOLA, + +[CRED232] +MICHELLE ANTHONY, + +[CRED233] +STEVE BARNETT, + +[CRED234] +CHUCK FLECKENSTEIN, + +[CRED235] +RITA LIBERATOR + +[CRED236] +MARTIN & CLAIRE LOGAN + +[CRED237] +SANDRA HUTTON + +[CRED238] +CHRISTINE DAVIDSON + +[CRED239] +ALAN, RED & BIGFOOT + +[CRED240] +LE T + +[CRED241] +COLIN DONALD + +[CRED242] +KERRY STALLWOOD + +[CRED243] +ALAN MCGREGOR + +[CRED244] +CHRIS MORTON + +[CRED245] +EMIL BUSSE + +[CRED246] +EMILY BAILLIE + +[CRED247] +KEVIN ARCHIBALD + +[CRED248] +MORAG KERR + +[CRED249] +CATH WALKER + +[CRED250] +ISO BAR + +[CRED251] +WATERLINE + +[CRED252] +NEWS CAFE + +[CRD251A] +THE POND + +[CRD252A] +PIVO + +[CRED253] +BUDGET VIDEO RENTALS + +[CRED254] +LORNA'S SCOOTER + +[CRED255] +GARETH MURFIN + +[CRED256] +ADDITIONAL ART + +[CRED257] +TONY PORTER + +[CRED258] +CRAIG MOORE + +[CRED259] +CUT SCENE LIP-SYNC ANIMATION + +[CRED260] +COSGROVE HALL FILMS + +[CRED261] +PRODUCER - OWEN BALLHATCHET + +[CRED262] +SENIOR ANIMATOR - JON TURNER + +[CRED263] +ANIMATORS - RICHARD DRUMM + +[CRED264] +DAVE BROWN + +[CRED265] +MAIR THOMAS + +[CRED266] +PRASHANT PATEL + +[CRED267] +AUDIO TECHNOLOGY CONSULTANT + +[CRED268] +RIK EDE FOR GAMESOUND LTD. + +[CRED269] +DTS INTEGRATION SUPPORT + +[CRED270] +TED LAVERTY FOR DTS + +[CRED271] +CHRIS GREER FOR DTS + +[CRED272] +JASON PAGE FOR SCEE + +[CRED273] +RESEARCH AND ANALYSIS + +[CRED274] +VROCK + +[CRED275] +DJ: LAZLOW AS HIMSELF + +[CRED276] +IMAGING VOICE-JOE KELLY + +[CRED277] +IMAGING PRODUCTION-JONATHAN HANST + +[CRED278] +WAVE 103 + +[CRED279] +DJ: ADAM FIRST-JAMIE CANFIELD + +[CRED280] +IMAGING VOICE-JEN SWEENEY + +[CRED281] +IMAGING PRODUCTION-JONATHAN HANST + +[CRED282] +FEVER 105 + +[CRED283] +DJ: OLIVER 'LADYKILLER' BISCUIT-JULIUS DYSON + +[CRED284] +IMAGING VOICE MALE-ED MCMANN + +[CRED285] +IMAGING VOICE FEMALE-SHAWNEE SMITH + +[CRED286] +IMAGING PRODUCTION- LISTEN KITCHEN + +[CRED287] +EMOTION 98.3 + +[CRED288] +DJ: FERNANDO- FRANK CHAVEZ + +[CRED289] +IMAGING VOICE-JEN SWEENEY + +[CRED290] +IMAGING PRODUCTION-JONATHAN HANST + +[CRED291] +RADIO ESPANTOSO + +[CRED292] +DJ: PEPE-TONY CHILRODES + +[CRED293] +WILDSTYLE + +[CRED294] +DJ: MISTER MAGIC AS HIMSELF + +[CRED295] +IMAGING VOICE-FRANK SILVESTRO + +[CRED296] +IMAGING PRODUCTION-LAZLOW + +[CRED297] +KCHAT + +[CRED298] +WRITTEN BY DAN HOUSER AND LAZLOW + +[CRED299] +PRODUCED AND EDITED BY LAZLOW + +[CRED300] +DJ AMY SHECKENHAUSEN -LEYNA WEBER + +[CRED301] +JEZ TORRENT-KEVIN MCKIDD + +[CRED302] +MANDY -COLLEEN CORBETT + +[CRED303] +MICHELLE CARAPADIS-MARY BIRDSONG + +[CRED304] +MR.ZOO-CARL DOWLING + +[CRED305] +GETHSEMANEE-LYNN LIPTON + +[CRED306] +CLAUDE MAGINOT-JOHN MAUCERI + +[CRED307] +BJ SMITH-LAWRENCE TAYLOR + +[CRED308] +THOR-FRANK FAVA + +[CRED309] +RADIO CALLERS + +[CRED310] +COUZIN ED, JOSH CLARK, JASON BUHRMESTER, JUAN ALLER, WAYNE OLIVER, SUSAN LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, KEITH BROADAS + +[CRED311] +LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, + +[CRED312] +DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, + +[CRED313] +KEITH BROADAS + +[CRED314] +VCPR + +[CRED315] +WRITTEN BY DAN HOUSER AND LAZLOW + +[CRED316] +PRODUCED BY LAZLOW + +[CRED317] +MAURICE CHAVEZ-PHILLIP ANTHONY RODRIGUEZ + +[CRED318] +JONATHAN FREELOADER- PATRICK OLSEN + +[CRED319] +MICHELLE MONTANIUS-KELLY GUEST + +[CRED320] +REP. ALEX SHRUB- CHRIS LUCAS + +[CRED321] +CALLUM CRAYSHAW- SEAN MODICA + +[CRED322] +JOHN F. HICKORY- LJ GANSEN + +[CRED323] +PASTOR RICHARDS- DAVID GREEN + +[CRED324] +JAN BROWN- MAUREEN SILLIMAN + +[CRED325] +BARRY STARK- RENAUD SEBBANE + +[CRED326] +JENNY LOUISE CRAB- MARY BIRDSONG + +[CRED327] +KONSTANTINOS SMITH- KONSTANTINOS.COM + +[CRED328] +JEREMY ROBARD-PETER SILVESTRO + +[CRED329] +RADIO COMMERCIALS + +[CRED330] +WRITTEN BY DAN HOUSER AND LAZLOW + +[CRED331] +PRODUCED BY LAZLOW + +[CRED332] +ADDITIONAL JINGLES PRODUCED BY CRAIG CONNER + +[CRED333] +COMMERCIAL VOICES: + +[CRED334] +ADAM DAVIDSON, ALEX ANTHONY, ALICE SALTZMAN, AMY SALZMAN, KATE DUKICH, + +[CRED335] +ARAN RONICLE, BARB JONES, BEN KRECH, BRIAN THOMAS, BROCK YODER, CHRIS + +[CRED336] +FERRANTE, CRAIG CONNER, DAVE RYAN, DAVID GREEN, DORIS WOO, DOUGLAS + +[CRED337] +HARRISON, ED MCMANN, FRANK CHAVEZ, FRANK FAVA, GENE HILGREEN, GREG + +[CRED338] +SCHWEIZER, HUNTER PLATIN, JAMES FERRANTE, JEFF BERLIN, JEFF ROSA, JOE KELLY, + +[CRED339] +JOHN MAUCERI, JOSH CLARK, JULIE WEMYSS, KEVIN STRALEY, KIM GURNEY, LANCE + +[CRED340] +WILLIAMS, LAURA PATERSON, LAZLOW, LISA ORTIZ, LORNA JORDAN, LUCIEN JONES, + +[CRED341] +MAUREEN SILLIMAN, MIKE FERRANTE JR., PETE GUSTIN, PETER SILVESTRO, RAFF + +[CRED342] +CROLLA, RANDY JOHNSON, RICHARD KRUGER, RON REEVE, SHELLEY MILLER, SKY, TJ ALLARD + +[CRD344A] +AUDIO RECORDED AT DIGITAL ARTS STUDIOS, + +[CRED344] +NYC, TRACK 9 STUDIOS, NYC, + +[CRED345] +WEDDINGTON MULTIMEDIA, LOS ANGELES, + +[CRD345A] +SYNC SOUND, NYC AND RADIO LAZLOW, LONG ISLAND. + +[CRED346] +THANKS TO AXEL ERICSON AND WON LEE AT DIGITAL ARTS, PAUL VASQUEZ AT TRACK 9 STUDIOS, JOHN BOWEN AND JOHN HASSLER AT SYNC SOUND + +[CRED347] +MARK LLOYD + +[CRED348] +TIM BATES + +[CRED349] +KIT BROWN + +[CRED350] +ANDY MASON + +[CRED351] +PHIL DEANE + +[CRED352] +PHIL ALEXANDER + +[CRED353] +MATT HEWITT + +[CRED354] +DENBY GRACE + +[CRED355] +ANTOINE CABROL + +[CRED356] +JONATHAN STONES + +[CRED357] +MIKE BLACKBURN + +[CRED358] +TIM MCGAFF + +[CINCAM] +Cinematic-Kamera + +[RC4] +'ROCK'N ROLL MIT RUMPO' + +[LEGAL] +~g~Schalte die kriminelle Bedrohung aus! + +[GA_2] +Neuer Motor und neue Lackierung. Die Cops werden dich nicht identifizieren! + +[HELP15] +Wenn zu Fuß unterwegs, drücke die ~h~~k~~PED_LOOKBEHIND~~w~, um ~h~nach hinten zu sehen~w~. Benutze den ~h~rechten Analog-Stick~w~, um dich ~h~umzusehen~w~. + +[FEC_LB4] +Nach hinten sehen (R3-Taste) + +[PERPIC] +Versteckte Päckchen gefunden + +[CO_ONE] +Verstecktes Päckchen ~1~ von ~1~ + +[GA_21] +In dieser Garage bringst du keine Autos mehr unter. + +[CHEAT1] +Cheat aktiviert + +[CHEAT2] +Waffen-Cheat + +[CHEAT3] +Health-Cheat + +[CHEAT4] +Panzerungs-Cheat + +[CHEAT5] +Fahndungslevel-Cheat + +[CHEAT6] +Geld-Cheat + +[CHEAT7] +Wetter-Cheat + +[USJ_ALL] +ALLE MONSTER-STUNTS ABSOLVIERT! + +[JAN] +Jan + +[FEB] +Feb + +[MAR] +Mär + +[APR] +Apr + +[MAY] +Mai + +[JUN] +Jun + +[JUL] +Jul + +[AUG] +Aug + +[SEP] +Sept + +[OCT] +Okt + +[NOV] +Nov + +[DEC] +Dez + +[DEFDT] +--:---:---- --:--:-- + +[BONUS] +~g~BONUS $~1~ + +[HORN1] +Drück die ~h~~k~~VEHICLE_HORN~~w~, um zu ~h~hupen. + +[HORN2] +Drück die ~h~~k~~VEHICLE_HORN~~w~, um zu ~h~hupen. + +[HORN3] +Drück die ~h~~k~~VEHICLE_HORN~~w~, um zu ~h~hupen. + +[FEC_EXV] +In Fahrzeug ein- und aussteigen. + +[TAXI_M] +'TAXIFAHRER' + +[COP_M] +'BÜRGERWEHR' + +[FIRE_M] +'FEUERWEHRMANN' + +[AMBUL_M] +'SANITÄTER' + +[HJ_IS] +IRRSINNS-STUNT-BONUS: $~1~ + +[HJ_PIS] +SUPER IRRSINNS-STUNT-BONUS: $~1~ + +[HJ_DIS] +DOPPELTER IRRSINNS-STUNT-BONUS: $~1~ + +[HJ_PDIS] +SUPER-DOPPEL-IRRSINNS-STUNT-BONUS: $~1~ + +[HJ_TIS] +DREIFACHER IRRSINNS-STUNT-BONUS: $~1~ + +[HJ_PTIS] +SUPER-DREIFACH-IRRSINNS-STUNT-BONUS: $~1~ + +[HJ_QIS] +VIERFACHER IRRSINNS-STUNT-BONUS: $~1~ + +[HJ_PQIS] +SUPER-VIERFACH-IRRSINNS-STUNT-BONUS: $~1~ + +[FESZ_LS] +Ladevorgang beendet. + +[HELI_1A] +Teste dein Können mit dem 'Sparrow'. Probiere, wie schnell du den Kurs abfliegen kannst. + +[HELI_1B] +Kurs absolviert! + +[HELIODD] +Helikopter-Jobs + +[LAW] +DIE ANWALTSMISSIONEN + +[LAW1_1] +~g~Besorge dir neue Sachen in Rafaels Shop. + +[LAW4_6] +Nieder mit der Geschäftsleitung! + +[LAW4_7] +Tod den Bossen! + +[LAW4_8] +Kampf, Kampf, Kampf, Kampf! + +[LAW4_9] +Mehr Urlaub, weniger Arbeit! + +[LAW4_11] +Kampf, Kampf, Kampf, Kampf! + +[LAW4_12] +Es lebe die Revolution! + +[GENERAL] +DIE COLONEL-MISSIONEN + +[GEN3_4] +Tommy Vercetti. Los, komm... + +[GEN3_13] +Was ist los, Mann?! Steig auf das Dach gegenüber im Hof bevor sie kommen! + +[GEN3_17] +Mist! Willst du mich umbringen?! + +[GEN3_21] +~g~Er hat Diaz' Geld! Stelle ihn und hole es zurück! + +[GEN3_24] +~r~Diaz hat's erwischt! Du hast versagt! + +[GEN3_26] +~r~Du hast Diaz erwischt! + +[GEN3_27] +~r~Du hast Diaz' Bodyguards erwischt! + +[GEN3_31] +~g~Begib dich zum Übergabeort und pass auf Diaz auf. + +[GEN3_32] +~g~Begib dich zu deinem Beobachtungsposten auf dem Dach des Gebäudes gegenüber von Lance. + +[COKE] +DIE KOKS-BARON-MISSIONEN + +[COK1_3] +Ich hoffe, du brichst dir das Genick! + +[COK1_6] +Ich hab diese Idioten satt! + +[COK2_7] +Siehst du diese Bojen? Versuch, die Lichter auszuschießen. + +[COK2_10] +Eines ist sicher: Du schießt besser als du laberst. + +[COK2_11] +Danke. Du bist ein echter Charmeur. + +[COK2_12] +Ich weiß, Tommy. + +[COK2_18] +Stehst du auf Kenny Loggins? + +[COK2_19] +Mann, ich liebe diese Scheibe. + +[COK2_26] +~r~Du hast Lance erwischt! + +[COK3_1] +Nicht schießen, Mann! + +[COK3_2] +Was ist in dem Zeug drin? + +[COK3_3] +Er klaut das Boot. Mist! + +[COK3_4] +Hilfe! Irgendein Irrer stiehlt das Boot, Mann! + +[COK4_W] +Uff! Das ist der letzte. + +[COK4_X] +Ich lass die Mühle an. + +[COK4_Y] +Ich glaube, wir haben ein paar neue Freunde. + +[COK4_2] +Ja. + +[COK4_6] +Weißt du, wo's lang geht? + +[COK4_7] +Haben wir uns verfahren? + +[COK4_8] +Wir haben Konkurrenz bekommen! + +[COK4_9] +Knöpf sie dir vor! + +[COK4_9A] +Zeit für den Lance Vance Dance! + +[COK4_10] +Die sind Kleinholz! Und Fischfutter. + +[COK4_11] +Geschafft! Die anderen Schiffchen taugen nicht viel. + +[COK4_17] +Die wollen's wissen! + +[COK4_18] +Ich hab nasse Füße! WIR KRIEGEN WASSER INS BOOT! + +[COK4_21] +Achtung, Brücke! + +[COK4_22] +Raus! Das Ding geht hoch! + +[COK4_23] +Gut geschossen. + +[COK4_29] +~r~Du hast Lance erwischt! + +[ASS1_6] +Geh nur, Tommy, ich komm schon zurecht! + +[ASS1_7] +Das ist für euch, ihr verdammten Mörder!! + +[ASS1_8] +Ich komm hier nicht weg! + +[ASS1_9] +Ich bin da, Tommy! + +[ASS1_10] +Hey, das ist aber ein herzallerliebstes Beet. + +[ASS1_11] +Hey Tommy, krieg ich ein Zimmer mit Blick auf die Bucht? + +[ASS1_12] +Schön hohe Decken hat's hier... + +[ASS1_3] +Lance! Ich brauche Deckung! + +[ASS1_4] +Diaz muss drinnen sein! + +[ASS1_5] +Lance! + +[ASS1_15] +~g~Stürme die Villa und erledige Diaz! + +[ASS1_17] +~g~Es führen mehrere Wege in die Villa. + +[TAXWAR] +TAXI-KRIEG-MISSIONEN + +[NOTAXI] +~g~Du brauchst ein Kaufman-Taxi, um diese Mission zu aktivieren. + +[TAXW1_5] +~g~Du musst in einem Kaufman-Taxi sitzen! + +[TAX2_4] +Auf geht's, Tommy. + +[TAX2_5] +Polier ihm die Visage. + +[TAX2_6] +Der hat nicht mal eine Lizenz. + +[TAX2_7] +Verdammte Limo-Services. + +[TAXW3_1] +~g~Hole Mercedes ab. + +[RACE1] +~g~3...2...1...LOS, LOS, LOS! + +[RACE2] +~g~3 + +[RACE3] +~g~2 + +[RACE4] +~g~1 + +[RACE5] +~g~LOS! + +[FIRST] +~b~1. + +[SECOND] +~b~2. + +[THIRD] +~b~3. + +[FOURTH] +~b~4. + +[RACETM] +~b~ZEIT: ~1~:~1~ + +[RACETM2] +~b~ZEIT: ~1~:0~1~ + +[RACEFA] +~r~Du hast das Rennen verloren! + +[TEX1_5] +~r~Er ist entkommen! + +[SEG3_1] +ZEIT: + +[SEG3_2] +~g~Begib dich zu dem Transporter mit dem RC Raider und den Zeitzünder-Bomben. + +[SEG3_3] +~g~Du musst den RC Raider benutzen, um 4 Bomben zu 4 Zielzonen auf dem Grundstück zu bringen. + +[SERG3_5] +~g~Du kannst immer nur 1 Bombe transportieren und kannst erfolgreich platzierten Bomben nicht wieder aufnehmen. + +[SEG3_7] +~g~Sobald du die ERSTE Bombe platziert hast, läuft der Timer des Zeitzünders an. Du musst dann alle Bomben innerhalb dieses Zeitraums platzieren. + +[SEG3_8] +~g~Alle 4 Bomben müssen in den 4 Zielzonen platziert werden, um die Mission zu erfüllen und das Gebäude zu demolieren. + +[SEG3_9] +~g~Zielzone getroffen! Noch 3 Bomben. + +[SEG3_10] +~g~Zielzone getroffen! Noch 2 Bomben. + +[SEG3_11] +~g~Zielzone getroffen! Noch 1 Bombe. + +[SEG3_12] +~r~Ziel verfehlt. Hol dir eine Bombe! + +[SEG3_13] +~g~Wirf die Bombe in einer Zielzone ab. + +[SEG3_14] +~r~Die Zeit ist um. Demolierung des Gebäudes fehlgeschlagen. + +[SEG3_15] +~r~Dein RC Raider ist zerstört. Wie willst du jetzt die Bomben transportieren? + +[AVERY] +AVERY-MISSIONEN + +[ASM] +ATTENTÄTERMISSIONEN + +[ASM_1] +ATTENTÄTERMISSION 1 + +[ASM1_1] +~g~Mr. Teal, Ihre Hilfe bei der Beseitigung der Landeier war äußerst wertvoll. Ich habe noch mehr Arbeit, die eine eher 'zupackende' Art verlangt. Ihr nächster Job klebt unter dem Telefon. + +[ASM1_2] +~g~Begib zu dich dem Fernsprecher vor dem Einkaufszentrum in Washington. + +[ASM1_3] +~g~Carl Pearson, Pizza-Lieferant. Er darf seine Lieferungen nicht durchführen. + +[ASM1_4] +~g~Schalte den Pizza-Lieferanten aus, bevor er seine Lieferungen abschließt. + +[ASM_2] +ATTENTÄTERMISSION 2 + +[ASM_3] +ATTENTÄTERMISSION 3 + +[ASM3_A] +Marcus Hammond, Franco Carter, Dick Tanner, Nick Kong und Stuntman Driver gehören zu einem europäischen Syndikat und planen einen Überfall. + +[ASM3_B] +Sie sind alle bereits in Position. Schalten Sie sie aus, bevor es losgeht. Sie haben 9 Minuten. Ich habe in der Nähe Waffen deponiert, die Sie brauchen werden. + +[ASM3_1] +~g~Hol dir die Waffe, die Mr. Black für dich deponiert hat. + +[ASM3_2] +~g~Geh nicht zu dicht an die Zielperson heran, sonst bemerkt sie dich. + +[ASM3_3] +~g~Es geht schneller, wenn du dir eine günstige Position nahe ihrem Standort suchst und zuschlägst, ohne gesehen zu werden. + +[ASM3_4] +~g~Er hat dich gesehen. Du musst ihn irgendwie ausschalten! + +[ASM3_5] +~g~Marcus Hammond befindet sich bei den Werbetafeln in Washington Beach. + +[ASM3_6] +~g~Franco Carter befindet sich bei DBP Security nahe dem Ocean Drive. + +[ASM3_7] +~g~Dick Tanner ist in der Nähe des Juweliers in Vice Point. + +[ASM3_8] +~g~Nick Kong befindet sich nähe Washington Beach. + +[ASM3_9] +~g~Stuntman Driver ist in Washington. + +[ASM3_10] +Du hast nicht alle ausgeschaltet. + +[ASM_4] +ATTENTÄTERMISSION 4 + +[ASM4_1] +~g~Hol dir das Gewehr, das im Laub vor dem Flughafen-Terminal für dich deponiert wurde. + +[ASM4_2] +~g~Verfehle dein Ziel nicht, du alarmierst sonst die Leibwächter. Und bleib auf Distanz, damit er dich nicht bemerkt. + +[ASM4_3] +~g~Beobachte die Frau über den Check-in-Schaltern im Terminal. TU IHR NICHTS. + +[ASM4_4] +~g~Erledige den Mann, dem sie den Aktenkoffer gibt, aber erst, NACHDEM ER IHN GENOMMEN HAT. Hole den Koffer und bringe ihn ins Ammu-Nation Downtown. + +[ASM4_5] +Hol dir den Aktenkoffer. + +[ASM4_6] +~g~Bring den Aktenkoffer ins Ammu-Nation Downtown. + +[ASM4_7] +~r~Du hast die Frau erwischt, du Idiot! + +[ASM4_8] +~r~Die Zielperson hat gehört, wie du geschossen hast. Der Deal ist geplatzt! + +[ASM4_9] +~r~Die Zielperson hat das Flugzeug bestiegen! + +[ASM4_11] +~r~Die Zielperson hat dich gesehen! Der Deal ist geplatzt! + +[ASM4_13] +~g~Er hat dich bemerkt und flieht. Schalte ihn aus und hol dir den Aktenkoffer! + +[ASM4_14] +~g~Der Distanz-Balken am oberen rechten Bildschirmrand zeigt dir, wie nahe du der Zielperson bist. Lass ihn nicht voll werden, sonst sieht sie dich. + +[ASM_5] +ATTENTÄTERMISSION 5 + +[KICK] +KICKSTART + +[KICK1_3] +~g~Anzahl, wie oft der Fuß abgesetzt wurde: ~1~ + +[KICK1_4] +~g~Zeitstrafe: ~1~ Sekunden + +[BANK] +BANK-MISSIONEN + +[BANK1] +BANK-MISSION 1 + +[BANK2] +BANK-MISSION 2 + +[BJM2_21] +~g~Triff so viele Ziele als möglich, solange deine Munition reicht. + +[BANK3] +BANK-MISSION 3 + +[BJM3_1] +~g~Besorg dir ein schnelles Auto und fahre an den Start. + +[BNK4_2A] +Die Mechaniker haben das Baby super hergerichtet. + +[BNK4_3G] +Oh, Mist, jetzt haben wir die Cops am Hals! + +[BNK4_3H] +Und wir sind noch nicht mal da. + +[BNK4_3K] +Also, erst mal müssen wir die Cops abschütteln... + +[BNK4_3L] +Großer Gott, Tommy, willst du uns alle umbringen?! + +[BNK4_3N] +Alles, was ich gern habe, geht kaputt! + +[BNK4_26] +Verdammt! Da sind sie schon! + +[BNK4_32] +Spreng die Schließfächer mit Sprengstoff auf. + +[BNK4_36] +Wo ist Cam? + +[BNK4_37] +In der Hölle. + +[BNK4_38] +Das ist der letzte! LOS! LOS! LOS! + +[BNK_39] +Shit! Wo bleibt Hilary? + +[BK4_40A] +Ich werd was für seine Verlustängste tun! + +[BNK4_42] +Hey, Jungs! Los, rein! Ich geb euch Deckung! + +[BNK4_43] +Ich hab alles im Griff! FAHR! + +[BNK4_44] +Geschafft! Wir sind reich! REICH! + +[BNK4_45] +Ein Jammer, dass Cam es nicht gepackt hat. Er war 'n guter Kerl. + +[BNK4_46] +Ja. Andererseits, so bleibt mehr für uns! + +[BNK4_47] +Sehr richtig! YEEEEHAAAH! + +[BNK4_48] +Tommy, eine Massage gefällig? + +[BNK4_49] +Hi, Mercedes. Ja, ich bin ein bisschen verspannt... + +[BNK450A] +Was hab ich gesagt, Tommy? Korrupte Bullen müssen sich vorsehen, wenn Kent Paul in der Stadt ist. + +[BNK450B] +Komm, gib mir 'nen größeren Batzen ab. Na komm. Ich brauch neue Klamotten. + +[BNK4_51] +Ich finde, du siehst ok aus. + +[KENT] +KENT PAUL-MISSIONEN + +[KENT1] +KENT PAUL-MISSION 1 + +[COUNT] +FÄLSCHER-MISSIONEN + +[COUNT1] +FÄLSCHER-MISSION 1 + +[COUNT2] +FÄLSCHER-MISSION 2 + +[BIKE] +DIE BIKER GANG-MISSIONEN + +[BIKE1] +BIKER-MISSION 1 + +[BIKE2] +BIKER-MISSION 2 + +[BIKE3] +BIKER-MISSION 3 + +[GOAWAY1] +Komm wieder, wenn du die Haiti-Gang-Missionen abgeschlossen hast. + +[HAIT] +DIE HAITI-GANG-MISSIONEN + +[HAIT1] +HAITI-MISSION 1 + +[HAIT2] +HAITI-MISSION 2 + +[HAIT3] +HAITI-MISSION 3 + +[HAM3_6] +~g~Verwende das Präzisionsgewehr, das ich für dich besorgt habe. + +[ROCK] +DIE ROCKBAND-GANG-MISSIONEN + +[ROK1_4] +~g~Ok, ich glaube, das ist es, was du suchst... + +[ROK1_1E] +~g~Das kostet mehr als du hast! + +[ROK1_1F] +~g~Komm wieder, wenn du die Kohle hast. + +[RBM2_6] +~g~Wow! Die ist 'n Kerl! Halt ihn auf! + +[ROCK3] +ROCKBAND-MISSION 3 + +[ROK3_6D] +~r~und IHR UND EUER KOPFPUTZ mit dazu! + +[ROK3_40] +Neben dem Kühler für die Babynahrung? + +[RBM3_5] +~g~Bring Love Fist zu ihrem Auftritt. + +[CUBANM] +DIE KUBA-GANG-MISSIONEN + +[CUBAN1] +KUBA-MISSION 1 + +[CUBAN2] +KUBA-MISSION 2 + +[CUB2_10] +~r~Du sollst Haitianer ausschalten, keine Kubaner! + +[CUBAN3] +KUBA-MISSION 3 + +[CUBAN4] +KUBA-MISSION 4 + +[CUB4_04] +~r~Du hast die Basis alarmiert. Jetzt kommen wir niemals rein! + +[CUB4_05] +~r~Du solltest doch im Auto bleiben. Jetzt lassen sie uns niemals rein. + +[CUB4_25] +Okay, los geht's. + +[PROT] +SCHUTZGELD-MISSIONEN + +[PRO1_02] +~g~Verlasse das Einkaufszentrum. + +[PRO3_06] +~g~Häng die Cops ab. + +[PORN] +PORNO-MISSIONEN + +[PORN1] +PORNO-MISSION 1 + +[POR1_03] +~r~Candy ist erledigt! + +[PORN2] +PORNO-MISSION 2 + +[PORN3] +PORNO-MISSION 3 + +[POR3_18] +Man hat dich bemerkt! + +[PORN4] +PORNO-MISSON 4 + +[POR4_04] +~g~Die Büros liegen auf der anderen Seite dieses Tors. + +[PHIL] +PHIL-MISSIONEN + +[PHIL1] +PHIL-MISSION 1 + +[PHIL2] +PHIL-MISSION 2 + +[PIZ1_A] +PIZZABOTEN-MISSION + +[PIZ1_03] +~g~Fahr zurück zum Pizza-Service für weitere Bestellungen. + +[PIZ1_04] +~g~Hier sind die neuen Bestellungen. + +[PIZ1_10] +Drücke die ~h~R3-Taste~w~, um die Pizza-Missionen abzubrechen. + +[CNTBUY1] +Druckerei gekauft: $ ~1~ + +[CARBUY] +Autohaus gekauft: $ ~1~ + +[PORNBUY] +Filmstudio gekauft: $ ~1~ + +[ICEBUY] +Eiscremefabrik gekauft: $ ~1~ + +[TAXIBUY] +Taxiunternehmen gekauft: $ ~1~ + +[BANKBUY] +Malibu Club gekauft: $ ~1~ + +[BOATBUY] +Bootswerft gekauft: $ ~1~ + +[PRNT_NO] +Zurzeit kannst du die Druckerei nicht kaufen. Komm später wieder. + +[CAR_NO] +Zurzeit kannst du das Autohaus nicht kaufen. Komm später wieder. + +[PORN_NO] +Zurzeit kannst du das Filmstudio nicht kaufen. Komm später wieder. + +[ICE_NO] +Zurzeit kannst du die Eiscremefabrik nicht kaufen. Komm später wieder. + +[TAXI_NO] +Zurzeit kannst du das Taxiunternehmen nicht kaufen. Komm später wieder. + +[BANK_NO] +Zurzeit kannst du den Malibu Club nicht kaufen. Komm später wieder. + +[BOAT_NO] +Zurzeit kannst du die Bootswerft nicht kaufen. Komm später wieder. + +[PRNT_R3] +Drücke R3, um die Druckerei zu kaufen. Preis: $~1~ + +[CAR_R3] +Drücke R3, um das Autohaus zu kaufen. Preis: $~1~ + +[PORN_R3] +Drücke R3, um das Filmstudio zu kaufen. Preis: $~1~ + +[ICE_R3] +Drücke R3, um die Eiscremefabrik zu kaufen. Preis: $~1~ + +[TAXI_R3] +Drücke R3, um das Taxiunternehmen zu kaufen. Preis: $~1~ + +[BANK_R3] +Drücke R3, um den Malibu Club zu kaufen. Preis: $~1~ + +[BOAT_R3] +Drücke R3, um die Bootswerft zu kaufen. Preis: $~1~ + +[COL2_6] +Keine Bewegung, amerikanisches Imperialistenschwein! + +[COL2_6B] +Das ist Eigentum des französischen Staates. + +[COL2_6C] +Her damit! + +[COL3_A] +Thomas, schön, dass Sie da sind. + +[COL3_B] +Verzeihen Sie, dass ich direkt zum Geschäftlichen komme. + +[COL3_C] +Diaz bat mich, eine kleine Transaktion für ihn zu überwachen. + +[COL3_D] +Hoffentlich läuft es diesmal besser als zuletzt. + +[COL3_E] +Deshalb wende ich mich ja an Sie, mein Freund. + +[COL3_F] +Ich habe im Parkhaus ein wenig Artillerie deponiert. + +[COL3_G] +Holen Sie sich die und beschützen Sie Diaz' Männer bei der Übergabe. + +[COL4_2] +Ich weiß nicht, Sir! + +[COL4_5] +Sir, zu Befehl, Sir! + +[COL4_10] +Gehen wir ein paar Donuts essen. + +[COL4_16] +Fahrzeug wird weggeschafft, Sir! + +[COL4_25] +Selbstzerstörung des Fahrzeugs eingeleitet! + +[COL5_6] +Mercedes - dieses Kind bringt mich noch ins Grab. + +[COL5_8] +Verdammte Bande! + +[COL5_5] +Nehmt das, Franzosenschweine! + +[CNT2_1] +Macht ihn fertig! + +[CNT2_2] +Hol die Platten! + +[CNT2_3] +Beschütze den Kurier! + +[FINKILL] +Ok, Jungs, macht ihn fertig! + +[FIN_1A] +Komm her, du hinterhältiger Dreckskerl! + +[FIN_1B] +Jetzt bist du fällig, du mieser Verräter! + +[FIN_1C] +Das wird dein letzter 'Dance', Lance Vance! + +[FIN_2B] +Ach, wirklich? + +[FIN_2C] +Der dumme Spruch war schon im Kindergarten alt! + +[FIN_3] +Keiner da, um dich rauszuhauen, diesmal, hä, Tommy? + +[FIN_4] +Du bist am Ende, Tommy. + +[FIN_5] +Du hast dich auf die falsche Seite geschlagen, Lance... + +[FIN_6] +Sonny ist oben am Safe mit MEINEM Geld... + +[FIN_10] +Sonny? SONNY! Jetzt bist du fällig! + +[FIN_11A] +Du hast mir 15 Jahre gestohlen, Sonny. + +[FIN_11B] +Und das wirst du mir jetzt büßen! + +[FIN_12A] +Du kapierst es immer noch nicht, oder? + +[FIN_12B] +Dein Arsch gehört mir, Tommy. + +[FIN_12C] +Diese 15 Jahre hast du anstelle von mir abgesessen! + +[FIN_13] +Schnappt ihn euch. Er hat nie was begriffen. + +[RACES_4] +3 + +[RACES_5] +2 + +[RACES_6] +1 + +[RACES_7] +LOS! + +[RACES_9] +Zeit: ~1~:~1~ + +[RACES] +ZEIT: + +[RACES17] +Neue Bestzeit: ~1~:~1~ + +[RACES18] +PREISGELD: $~1~ + +[RACES20] +Neue Bestzeit: ~1~:0~1~ + +[RACES21] +Zeit: ~1~:0~1~ + +[RCH1_1] +~g~Benutze den ferngesteuerten Helikopter, den RC Raider, um die Checkpoints zu PASSIEREN. + +[RCH1_2] +~g~Die CHECKPOINTS sind überall auf dem Flughafen verteilt. + +[RCH1_3] +~g~Du hast ~c~8 MINUTEN~g~, um alle ~c~20 zu passieren! + +[RCH1_5] +Zeit + +[RCRC1_2] +~g~Begib dich zur Startlinie! + +[RCRC1_4] +~g~3 + +[RCRC1_5] +~g~2 + +[RCRC1_6] +~g~1 + +[RCRC1_7] +~g~LOS! + +[RCRC1_8] +~g~Absolvierte Zeit: ~1~ Sekunden + +[RCPL1_1] +~g~Liefere dir mit 3 anderen RC Barons ein CHECKPOINT-RENNEN. + +[RCPL1_2] +~g~Du musst durch die ~o~MITTLERE MARKIERUNG ~g~hindurch, um einen Checkpoint zu passieren. + +[RCPL1_3] +~g~Begib dich zur Startlinie! + +[ICC1_O] +Was ist denn in Sie gefahren? + +[FEA_2SP] +2 Boxen + +[FEA_4SP] +Mehr als 2 Boxen + +[FEA_EAR] +Kopfhörer + +[FEA_NAH] +KEINE AUDIO HARDWARE + +[FET_APP] +ÜBERNEHMEN + +[FES_SKN] +SKIN-NAME + +[FES_DAT] +DATUM + +[FES_SET] +Skin verwenden + +[FET_DEF] +Standard wiederherst. + +[FESZ_QZ] +Dieses Spiel wirklich speichern? + +[FES_SCG] +Laufendes Spiel speichern? + +[FES_LCG] +Spiel laden und weiterspielen? + +[FEC_FIR] +Feuern + +[FEC_NWE] +Nächste Waffe + +[FEC_PWE] +Vorherige Waffe + +[FEC_FOR] +Vorwärts + +[FEC_BAC] +Rückwärts + +[FEC_LEF] +Links + +[FEC_RIG] +Rechts + +[FEC_ZIN] +Heranzoomen + +[FEC_ZOT] +Hinauszoomen + +[FEC_EEX] +Ein-/Aussteigen + +[FEC_RAD] +Radio + +[FEC_SUB] +Spezialmission + +[FEC_CMR] +Blickwinkel ändern + +[FEC_JMP] +Springen + +[FEC_SPN] +Sprinten + +[FEC_HND] +Handbremse + +[FEC_LOL] +Nach links sehen + +[FEC_LOR] +Nach rechts sehen + +[FEC_NTR] +Nächstes Ziel + +[FEC_PTT] +Vorheriges Ziel + +[FEC_LBA] +Nach hinten sehen + +[FEC_CEN] +Kamera zentrieren + +[FET_CCN] +Classic + +[FET_SCN] +Standard + +[FET_CFT] +ZU FUSS + +[FET_CCR] +IN FAHRZEUG + +[FET_CAC] +AKTION + +[FEC_IBT] +- + +[FEC_MXO] +MXB1 + +[FEC_MXT] +MXB2 + +[FEC_UNB] +NICHT BEL. + +[FEC_TFL] +Links sehen + Geschütz L + +[FEC_TFR] +Rechts sehen + Geschütz R + +[FEC_MWF] +MAUSRAD AUFW. + +[FEC_MWB] +MAUSRAD ABW. + +[FEC_ORR] +oder + +[FEC_NUS] +NICHT VERWENDET + +[FEC_LUD] +Aufw. sehen + +[FEC_LDU] +Abw. sehen + +[FEC_CMP] +COMBO: L+R SEHEN + +[LAW_1A] +law_1a + +[LAW_1B] +law_1b + +[LAW_2A] +law_2a + +[LAW_2B] +law_2b + +[FEH_STA] +STATISTIKEN + +[FEH_LOA] +LADEN + +[FEH_CON] +STEUERUNG + +[FEH_AUD] +AUDIO + +[FEH_DIS] +ANZEIGE + +[FEH_LAN] +SPRACHE + +[FEH_SGA] +NEUES SPIEL STARTEN + +[FEO_CON] +Controller-Setup + +[FEO_AUD] +Audio-Setup + +[FEO_DIS] +Anzeigen-Setup + +[FEO_LAN] +Sprachen-Setup + +[FEO_PLA] +Spieler-Setup + +[FEA_OUT] +Ausgabe + +[FEA_ST] +Stereo + +[FEA_DTS] +DTS + +[FEA_RSS] +Radiosender + +[FEA_NON] +RADIO AUS + +[FEA_FM0] +WILDSTYLE + +[FEA_FM1] +FLASH FM + +[FEA_FM2] +KCHAT + +[FEA_FM3] +FEVER 105 + +[FEA_FM4] +VROCK + +[FEA_FM5] +VCPR + +[FEA_FM7] +EMOTION 98.3 + +[FEA_FM8] +WAVE 103 + +[FED_BRI] +Helligkeit + +[FED_TRA] +Unschärfe-FX + +[FED_SUB] +Untertitel + +[FED_WIS] +Breitbild + +[FED_POS] +Anzeige-Position + +[FEP_RES] +Fortsetzen + +[FEP_STG] +Spiel starten + +[FEP_STA] +Statistiken + +[FEP_BRI] +Missionsinfos + +[FEP_OPT] +Optionen + +[FEP_QUI] +Spiel beenden + +[FES_LOA] +Spiel laden + +[FES_DEL] +Spiel löschen + +[FEC_CSU] +Controller-Setup + +[FEC_RED] +Steuerung ändern + +[FEC_MOU] +Maus-Einstellg. + +[DISTGOL] +Mit Golfwagen zurückgel. Entfernung (Meilen) + +[DISTGOM] +Mit Golfwagen zurückgel. Entfernung (Meter) + +[ST_FAVR] +Lieblings-Radiosender + +[ST_WSTR] +Unbeliebtester Radiosender + +[ST_FAVV] +Lieblingsfahrzeug + +[ST_STAR] +Anzahl angehäufter Fahndungssterne + +[ST_HEAD] +Anzahl Köpfe + +[ST_GANG] +Unbeliebteste Gang + +[ST_STGN] +Anzahl losgewordener Fahndungssterne + +[TYREPOP] +Zerschossene Reifen + +[TYRESLA] +Aufgeschlitzte Reifen + +[ST_BRK] +Erledigte Gegner im Chaos-Derby + +[ST_LTBR] +Längste Zeit im Chaos-Derby (Sekunden) + +[ST_GNG1] +Kubaner + +[ST_GNG2] +Haitianer + +[ST_GNG3] +Möchtegern-Gangster + +[ST_GNG4] +Diaz' Gang + +[ST_GNG5] +Sicherheitsbeamte + +[ST_GNG6] +Biker-Gang + +[ST_GNG7] +Vercetti-Gang + +[ST_GNG8] +Golfer + +[FEA_FM6] +ESPANTOSO + +[ST_ASSI] +Ausgeführte Attentätermissionen + +[DISTBIK] +Mit Motorrad zurückgel. Strecke (Meilen) + +[DISTBIM] +Mit Motorrad zurückgel. Strecke (Meter) + +[HOTEL] +Ocean View Hotel + +[KICK1_9] +VERBLEIBENDE CHECKPOINTS: + +[FIN_B6] +Du hast nicht genug Geld, um diese Mission zu beginnen. + +[TEX3_9] +~g~Steuere den Helikopter über eine Bombe, um sie aufzunehmen. + +[HELP22] +Begib dich zu dem grünen Haus-Symbol auf dem Radar. + +[FES_SSC] +Daten wurden gespeichert. Weiter mit OK. + +[FES_DSC] +Daten wurden gelöscht. Weiter mit OK. + +[FESZ_QC] +Dieses beschädigte Spiel überschreiben? + +[FES_CHE] +Achtung! Ein oder mehrere Cheats sind aktiviert, dies kann sich auf die Speicherung auswirken. Es wird empfohlen, dieses Spiel nicht zu speichern. + +[FET_SG] +SPIEL SPEICHERN + +[FEH_BRI] +MISSIONSINFO + +[FEH_MAP] +KARTE + +[FEM_OK] +OK + +[FEC_CRO] +Ducken + +[FEC_CR3] +Ducken (L3-Taste) + +[FEC_SMT] +Spezialmission + +[FEC_SM3] +Spezialmission (R3-Taste) + +[FEC_RSC] +Radiosender + +[ST_PR01] +Flieger + +[ST_PR02] +Luftwaffensoldat + +[ST_PR03] +Gefreiter + +[ST_PR04] +Unteroffizier + +[ST_PR05] +Leutnant + +[ST_PR06] +Stabsunteroffizier + +[ST_PR07] +Hauptmann + +[ST_PR08] +Könner + +[ST_PR09] +Profi + +[ST_PR10] +Roter Baron + +[ST_PR11] +Wildgans + +[ST_PR12] +Viper + +[ST_PR13] +Falke + +[ST_PR14] +Adler + +[ST_PR15] +Blitz + +[ST_PR16] +Tornado + +[ST_PR17] +Taifun + +[ST_PR18] +Luftwaffengeneral + +[ST_PR19] +Ass + +[FET_LG] +SPIEL LADEN + +[CAR_EXP] +Straßenfahrzeuge zerstört + +[BOA_EXP] +Boote zerstört + +[HEL_DST] +Flugzeuge & Helikopter zerstört + +[STFT_01] +Schnellste Zeit bei 'Wheels of Steels' + +[STFT_02] +Schnellste Zeit bei 'Der Fahrer' + +[STFT_03] +Schnellste Zeit auf Geländemotorradstrecke + +[STFT_04] +Schnellste Zeit bei Modellflugzeug-Rennen + +[STFT_05] +Schnellste Zeit mit den ferngesteuerten Autos + +[STFT_06] +Schnellste Zeit bei Helikopter-Rennen + +[STFT_07] +Schnellste Zeit bei 'Todeskaracho' + +[STFT_08] +Schnellste Zeit bei 'Ocean Drive' + +[STFT_09] +Schnellste Zeit bei 'Küsten-Rallye' + +[STFT_10] +Schnellste Zeit bei 'Capital Cruise' + +[STFT_11] +Schnellste Zeit bei 'Tour!' + +[STFT_12] +Schnellste Zeit bei 'V.C. Endurance' + +[STHC_01] +High-Score bei Schießstand-Mission + +[STHC_02] +Beste Trefferquote am Schießstand (in Prozent) + +[STHC_03] +Anzahl getätigter Drogendeals + +[HELP24] +Du kannst jetzt Aufträge vom Colonel annehmen. + +[HELP25] +Du kannst jetzt Aufträge von Avery Carrington annehmen. + +[HELP29] +Außerhalb eine Mision kannst du zu dem Bekleidungsgeschäft gehen. + +[HELP30] +Wenn du neue Klamotten kaufst, reduziert sich dein Fahndungslevel auf null. + +[ASM4_24] +Entfernung: + +[RBM1_6] +~g~Bring Mercedes und den 'Love Juice' zu der Band ins Aufnahmestudio. + +[RBM1_3] +NICHT MEHR BENÖTIGT + +[HAM1_5] +NICHT MEHR BENÖTIGT + +[RBM1_11] +NICHT MEHR BENÖTIGT + +[HELP31] +Um aus dem fahrenden Fahrzeug zu schießen, sieh zuerst mit ~k~~VEHICLE_LOOKLEFT~ oder ~k~~VEHICLE_LOOKRIGHT~ nach links oder rechts. + +[HELP34] +Du brauchst eine Maschinenpistole für einen 'Drive-By'. + +[STRIP_1] +~r~Nicht genug Cash, du windiger Geizhals! + +[EXIT_1] +~k~~PED_SPRINT~ zum Beenden. + +[ASM1_B] +Ihr nächster Auftrag klebt unter dem Telefon. + +[ASM1_C] +Ich habe noch mehr Arbeit, die eine eher 'zupackende' Art verlangt. + +[SCARF] +Apartment 3c + +[LAW4_10] +Reiche Manager sind Schweine! + +[RCH1_6] +~g~Benutze den ferngesteuerten Helikopter, um Checkpoints überall auf dem Flughafen abzufliegen. + +[RCH1_9] +~b~GESAMTZEIT: ~1~:~1~ + +[RCH1_10] +~b~GESAMTZEIT: ~1~:0~1~ + +[WHEEL01] +ZWEIRAD DOPPELBONUS: $ ~1~ Distanz: ~1~.~1~m Zeit: ~1~ Sekunden + +[WHEEL02] +ZWEIRAD DOPPELBONUS: $ ~1~ Distanz: ~1~ Fuß Zeit: ~1~ Sekunden + +[WHEEL03] +ZWEIRAD BONUS: $ ~1~ Zeit: ~1~ Sekunden + +[WHEEL04] +ZWEIRAD BONUS: $ ~1~ Distanz: ~1~.~1~m + +[WHEEL05] +ZWEIRAD BONUS: $ ~1~ Distanz: ~1~ Fuß + +[WHEEL06] +WHEELIE BONUS: $ ~1~ Distanz: ~1~.~1~m Zeit: ~1~ Sekunden + +[WHEEL07] +WHEELIE BONUS: $ ~1~ Distanz: ~1~ Fuß Time: ~1~ Sekunden + +[WHEEL08] +WHEELIE BONUS: $ ~1~ Zeit: ~1~ Sekunden + +[WHEEL09] +WHEELIE BONUS: $ ~1~ Distanz: ~1~.~1~m + +[WHEEL10] +WHEELIE BONUS: $ ~1~ Distanz: ~1~.~1~ Fuß + +[WHEEL11] +VOLLBREMSUNGSBONUS: $ ~1~ Distanz: ~1~.~1~m Zeit: ~1~ Sekunden + +[WHEEL12] +VOLLBREMSUNGSBONUS: $ ~1~ Distanz: ~1~ Fuß Zeit: ~1~ Sekunden + +[WHEEL13] +VOLLBREMSUNGSBONUS: $ ~1~ Zeit: ~1~ Sekunden + +[WHEEL14] +VOLLBREMSUNGSBONUS: $ ~1~ Distanz: ~1~.~1~m + +[WHEEL15] +VOLLBREMSUNGSBONUS: $ ~1~ Distanz: ~1~ Fuß + +[ROK3_72] +Love Fist! + +[ROK3_74] +Ah, seht mal, was ist das? Hey, Tommy, leg mal dieses Band ein. + +[POR1_19] +Hey! + +[DESPERA] +Desperado + +[MOB_99A] +Begib dich zu dem Fernsprecher neben dem Einkaufszentrum in Washington Beach. + +[MOB_98A] +Begib dich zu dem Fernsprecher in Vice Point. + +[MOB_96A] +Begib dich zu dem Fernsprecher beim Flughafen-Terminal. + +[MOB_95A] +Begib dich zu dem Fernsprecher in Little Havana. + +[BNK1_1] +Kann ich Ihnen helfen, Sir? + +[BNK1_2] +Der Typ ist verkleidet! + +[BNK1_3] +Er ist verrückt geworden! + +[BNK1_4] +Wer zum Teufel sind Sie? + +[BNK1_5] +Wo ist Ihre Dienstmarke? + +[BNK1_6] +Da sind sie! Feuer frei! + +[MOB_24A] +Hallo, spricht da Mr. Vercetti? + +[MOB_24B] +Ja. + +[MOB_24C] +Hier Cortez. Sie waren bei meiner Party. + +[MOB_24D] +Ja. Ich erinnere mich. + +[MOB_24E] +Mr. Vercetti, es war höchst unglücklich, was da bei der Abwicklung Ihres Geschäfts vorgefallen ist. + +[MOB_24F] +Ich weiß. + +[MOB_24G] +Sie sollen wissen, dass ich und meine Leute alles tun, um der Sache auf den Grund zu gehen. + +[MOB_24H] +Falls Sie mit mir persönlich sprechen wollen, finden Sie mich auf dem Schiff. Guten Tag, Senor. + +[BNK2_6] +Der Typ ist geisteskrank! + +[ANGEL] +Angel + +[CUBJET] +Kubanischer Jetmax + +[SANDKIN] +Sandking + +[POLMAV] +Polizei-Maverick + +[BOXVILL] +Boxville + +[BENSON] +Benson + +[HOTRINA] +Hotring Racer + +[HOTRINB] +Hotring Racer + +[BLOODRA] +Chaos-Derby Banger + +[BLOODRB] +Chaos-Derby Banger + +[MAFIACR] +Mafia Cruiser + +[COP_M2] +'EINSATZ IN VICE CITY' + +[COP_M3] +'DONNER ÜBER VICE CITY' + +[BNK3_2] +Ich fahre nicht für dich, niemals. Das erzähle ich in der Therapie. + +[FEM_SL1] +Datei 1 nicht vorhanden + +[FEM_SL2] +Datei 2 nicht vorhanden + +[FEM_SL3] +Datei 3 nicht vorhanden + +[FEM_SL4] +Datei 4 nicht vorhanden + +[FEM_SL5] +Datei 5 nicht vorhanden + +[FEM_SL6] +Datei 6 nicht vorhanden + +[FEM_SL7] +Datei 7 nicht vorhanden + +[FEM_SL8] +Datei 8 nicht vorhanden + +[FEA_CHA] +Tonausgabe wird auf STEREO umgeschaltet. Bitte warten... + +[FEA_CHD] +Achtung! Sie schalten die Tonausgabe von STEREO auf DTS um. Bitte warten... + +[FEI_SEL] +Auswahl + +[FEI_BAC] +Zurück + +[FEI_RES] +Weiter + +[FEI_NAV] +Navigieren + +[FEI_BTX] +/-Taste - + +[FEI_BTT] +"-Taste - + +[FEI_STA] +START-Taste - + +[FEI_BTD] +; = > < - + +[FEI_STO] +Stop + +[MOB_68A] +Tommy, Alter, ich hab eine Überraschung für dich. + +[MOB_68B] +Ich bin im Aufnahmestudio mit ein paar super Musikern. + +[MOB_68C] +Warum kommst du nicht kurz vorbei? + +[MOB_68D] +Kannst dir denken, dass es sich lohnt, oder? Bis dann. + +[OUTFT1] +Straße + +[OUTFT2] +Abendgarderobe + +[OUTFT3] +Overall + +[OUTFT4] +Country Club + +[OUTFT5] +Havana + +[OUTFT6] +Cop + +[OUTFT7] +Bankräuber + +[OUTFT8] +Freizeit + +[OUTFT9] +Mr. Vercetti + +[OUTFT10] +Trainingsanzug + +[OUTFT13] +MC Tommy + +[CAR_AS1] +AUTOHAUS ERWORBEN + +[CAR_AS2] +~g~Sunshine Autos generiert nun bis zu $~1~ Einkünfte. Hol dir das Geld regelmäßig ab. + +[BUYSAVE] +~g~Außerhalb einer Mission kannst du dein Spiel hier umsonst speichern. + +[BUYGARG] +~g~Du kannst in dieser Garage auch Autos abstellen. + +[STRPBUY] +Pole Position Club gekauft: $ ~1~ + +[STRP_R3] +Drücke R3, um den Pole Position Club zu kaufen. Preis: $~1~ + +[NBMN_R3] +Drücke R3, um Elswanko Casa zu kaufen. Preis: $~1~ + +[GA_4] +Autobomben kosten $1000 pro Stück. + +[GA_5] +In deinem Wagen ist schon eine Autobombe. + +[GA_6] { reVC update } +Park die Karre, mach sie durch Drücken der ~h~~k~~VEHICLE_FIREWEAPON~~w~ scharf und dann HAU AB! + +[GA_7] { reVC update } +Mach die Bombe mit der ~h~~k~~VEHICLE_FIREWEAPON~~w~ scharf. Dann geht sie hoch, wenn der Wagen angelassen wird. + +[GA_6B] { reVC update } +Park die Karre, mach sie durch Drücken der ~h~~k~~VEHICLE_FIREWEAPON~~w~ scharf und dann HAU AB! + +[GA_7B] { reVC update } +Mach die Bombe mit der ~h~~k~~VEHICLE_FIREWEAPON~~w~ scharf. Dann geht sie hoch, wenn der Wagen angelassen wird. + +[MOB_70A] +Tommy, ich bin's, Colonel Cortez. Hören Sie, Sie sind doch ein Mann, der Dinge zu erledigen weiß. + +[MOB_70B] +Sie finden mich auf dem Boot. + +[PICK2] +.357 in Verstecken angeliefert! + +[PICK3] +Kettensägen in Verstecken angeliefert! + +[PICK4] +Flammenwerfer in Verstecken angeliefert! + +[PICK5] +.308 Präzisionsgewehr in Verstecken angeliefert! + +[PICK6] +Mini-Kanonen in Verstecken angeliefert! + +[PICK7] +Raketenwerfer in Verstecken angeliefert! + +[PICK8] +Sea Sparrow jetzt bei Vercetti Estate verfügbar! + +[PICK9] +Panzer jetzt in Army-Kaserne verfügbar! + +[PICK10] +Hunter jetzt in Army-Kaserne verfügbar! + +[CLOTH1] +Abendgarderobe bei Rafaels in Ocean Beach erhältlich. + +[CLOTH2] +Straßenkleidung in Verstecken angeliefert. + +[CLOTH3] +Overall bei 'Tooled Up' im North Point Einkaufszentrum erhältlich. + +[CLOTH4] +Country Club-Bekleidung beim Golf Club in Leaf Links erhältlich. + +[CLOTH5] +Havana-Outfit bei 'Little Havana Streetwear' in Little Havana erhältlich. + +[CLOTH6] +Polizeiuniform bei Polizeistation in Washington Beach erhältlich. + +[CLOTH7] +Freizeitbekleidung bei 'Gash' im North Point Einkaufszentrum erhältlich. + +[CLOTH8] +Mr. Vercetti-Outfit bei 'Collar & Cuffs'in Ocean Beach erhältlich. + +[CLOTH9] +Trainingsanzug bei 'Jocksport' in Downtown erhältlich. + +[CLOTH10] +Bankräuber-Outfit beim Malibu Club in Vice Point erhältlich. + +[MOB_62A] +Tommy, hier Ricardo Diaz. Ich wollte dir danken, dass du mich gerettet hast. + +[MOB_62B] +Ich hab den Trottel von Cortez gefragt. Er meint, du wärst ein Mann für alle Fälle. Komm doch mal bei mir vorbei. + +[MOB_62C] +Ich brauche einen Kerl wie dich. Ich hab nämlich nur Schwachköpfe. + +[MOB_62D] +Nur lauter Schwachköpfe. Ich mache dich schwer reich. + +[GOAWAY2] +Komm wieder, wenn du die Biker Gang-Missionen abgeschlossen hast. + +[COL2_9] +Du amerikanischer Idiot! Sie sind dir hierher gefolgt! + +[LOADCOL] +Lade... + +[STFT_17] +Schnellste Zeit bei 'PCJ Rallye' + +[STFT_18] +Schnellste Zeit bei 'Krasses Gelände' + +[STFT_19] +Schnellste Zeit bei 'Teststrecke' + +[NEW_REC] +Neuer Rekord!! ~1~ Minuten und ~1~ Sekunden. + +[BMX_HOW] +~g~Fahr zwei Runden auf der Geländemotorradstrecke. ~y~Passiere dabei ~g~die ~y~CHECKPOINTS~g~! + +[BMXREW1] +~g~Jedes Mal wenn du deine bisherige Bestzeit für die zwei Runden verbesserst, + +[BMXREW2] +~g~bekommst du eine noch höhere ~y~BELOHNUNG~g~! + +[BMXRAIN] +~g~Sieht nach Regen aus... + +[ITBEG] +Am Anfang... + +[NBMNBUY] +El Swanko Casa gekauft: $ ~1~ + +[LNKVBUY] +Links View Apartment gekauft: $ ~1~ + +[HYCOBUY] +Hyman Condo gekauft: $ ~1~ + +[BUYGARS] +~g~Du kannst in diesen Garagen auch Autos abstellen. + +[OCHEBUY] +Ocean Heights Apartment gekauft: $ ~1~ + +[WASHBUY] +1102 Washington Street gekauft: $ ~1~ + +[VCPTBUY] +3321 Vice Point gekauft: $ ~1~ + +[HELP6_C] +Drücke die ~h~~k~~VEHICLE_HANDBRAKE~~w~, um die Handbremse anzuziehen. + +[HELP2_A] +Drücke die ~h~~k~~PED_SPRINT~~w~, um zu ~h~sprinten. + +[HELP4_A] +Drücke die ~h~~k~~VEHICLE_ACCELERATE~~w~, um zu beschleunigen. + +[HELP5_A] +Drücke die ~h~~k~~VEHICLE_BRAKE~~w~, um zu bremsen, oder um zurückzusetzen, wenn das Fahrzeug steht. + +[HELP8_A] +Drücke die ~h~~k~~PED_SNIPER_ZOOM_IN~~w~, um an das Ziel heranzuzoomen und die ~x~/-Taste~w~,um herauszuzoomen. + +[PBOAT_1] { reVC update } +Drücke die~h~ ~k~~VEHICLE_FIREWEAPON~~w~, um die Bordkanonen abzufeuern. + +[SEG3_4] { reVC update } +~g~Um Bomben aufzunehmen, steuere den RC Raider einfach nahe an sie heran. Um eine abzuwerfen, drücke die ~h~~k~~VEHICLE_FIREWEAPON~~g~-Taste. + +[RCR1_3] { reVC update } +~g~Wenn du diese Mission abbrechen willst, drücke die ~h~~k~~VEHICLE_FIREWEAPON~~g~, um dein Auto zu sprengen. + +[HELP32] { reVC update } +Dann feuere mit der ~h~~k~~VEHICLE_FIREWEAPON~. + +[HELP33] { reVC update } +Dann feuere mit der ~h~~k~~VEHICLE_FIREWEAPON~. + +[TTUTOR] +Drücke die ~h~~k~~TOGGLE_SUBMISSIONS~~w~, um Taxi-Missionen an- oder abzuschalten. + +[TTUTOR2] +Drücke die ~h~~k~~TOGGLE_SUBMISSIONS~~w~, um Taxi-Missionen an- oder abzuschalten. + +[FTUTOR] +Drücke die ~h~~k~~TOGGLE_SUBMISSIONS~~w~, um Feuerwehrwagen-Missionen an- oder abzuschalten. + +[FTUTOR2] +Drücke die ~h~~k~~TOGGLE_SUBMISSIONS~~w~, um Feuerwehrwagen-Missionen an- oder abzuschalten. + +[CTUTOR] +Drücke die ~h~~k~~TOGGLE_SUBMISSIONS~~w~, um Bürgerwehr-Missionen zu aktivieren oder zu deaktivieren. + +[CTUTOR2] +Drücke die ~h~~k~~TOGGLE_SUBMISSIONS~~w~, um Bürgerwehr-Missionen zu aktivieren oder zu deaktivieren. + +[HELP8_B] +Drücke die~h~ ~k~~PED_SNIPER_ZOOM_IN~~w~, um ~h~an das Ziel heranzuzoomen ~w~und die~h~ ~k~~PED_SNIPER_ZOOM_OUT~~w~, um ~h~herauszuzoomen~w~. + +[ATUTOR3] +Drücke die ~h~~k~~TOGGLE_SUBMISSIONS~~w~, um Krankenwagen-Missionen an- oder abzuschalten. + +[GUN_H1] +~w~Drück die~h~ ~k~~PED_SPRINT~~w~, um zu kaufen. ~w~Drück die~h~ ~k~~VEHICLE_ENTER_EXIT~~w~, um zu gehen. + +[PU_CF3] { reVC update } +Drück die ~h~~k~~PED_ANSWER_PHONE~~w~, um die augenblickliche Waffe in diesem Slot auszutauschen. + +[PU_CF4] { reVC update } +Drück die ~h~~k~~PED_ANSWER_PHONE~~w~, um die augenblickliche Waffe in diesem Slot auszutauschen. + +[HELP9_B] +Drücke die~h~ ~k~~PED_FIREWEAPON~~w~, um das Präzisionsgewehr ~h~abzufeuern~w~. + +[HELP37] +Wenn du doch nicht in ein Auto einsteigen willst, das du im Begriff bist, zu klauen, drück die ~h~~k~~PED_SPRINT~. + +[HELP6_A] +Drücke die ~h~~k~~VEHICLE_HANDBRAKE~~w~, um die Handbremse anzuziehen. + +[HELP6_D] +Drücke die ~h~~k~~VEHICLE_HANDBRAKE~~w~, um die Handbremse anzuziehen. + +[HELP26] +Drücke die ~h~~k~~VEHICLE_ENTER_EXIT~~w~, um in ein Fahrzeug ein- oder auszusteigen. + +[HELP27] +Drücke ~h~~k~~VEHICLE_TURRETUP~~w~ oder ~h~~k~~VEHICLE_TURRETDOWN~~w~ um dein Gewicht auf einem Motorrad zu verlagern. + +[HELP28] +Drücke ~h~~k~~VEHICLE_TURRETUP~~w~ oder ~h~~k~~VEHICLE_TURRETDOWN~~w~ um dein Gewicht auf einem Motorrad zu verlagern. + +[HELP35] +Benutze die ~h~~k~~GO_LEFT~~w~ oder ~h~~k~~GO_RIGHT~~w~ um das Fahrzeug zu steuern. + +[HELP36] +Benutze die ~h~~k~~GO_LEFT~~w~ oder ~h~~k~~GO_RIGHT~~w~ um das Fahrzeug zu steuern. + +[HELP42] +Folge dem ~q~rosa Symbol~w~, um zum Hotel zu kommen. + +[HELP19] +Stell dich in die ~q~rosa Markierung~w~, um weiterzumachen. + +[HELP1] +Halte in der Mitte der ~q~rosa Markierung. + +[HELP12] +Geh ins Zentrum der ~q~rosa Markierung~w~, um eine Mission zu starten. + +[SEG3_6] +~g~Um eine Zielzone zu treffen, musst du die Bombe innerhalb der ~q~rosa Markierung~g~ abwerfen. Die Reihenfolge spielt dabei keine Rolle. + +[S_PROMP] +Außerhalb einer Mission kannst du dein Spiel speichern, indem du das ~h~Cassetten-Symbol aufnimmst. + +[HELP16] +Geh durch die Eingangstür, um das ~h~Ocean View Hotel~w~ zu betreten. + +[HELP43] +~g~Begib dich zum ~h~Ocean View Hotel~g~ am Ocean Drive. + +[HELI_F1] +~r~Heli-Ceckpoint-Mission abgebrochen! + +[AMMUHLP] +Wenn du Waffen brauchst, geh zu ~h~AmmuNation~w~. Das ~h~Pistolensymbol~w~ auf dem Radar zeigt dir den Weg. + +[HELI_1] +Downtown Heli-Checkpoint + +[HELI_2] +Ocean Beach Heli-Checkpoint + +[HELI_3] +Vice Point Heli-Checkpoint + +[HELI_4] +Little Haiti Heli-Checkpoint + +[FST_MFR] +Lieblings-Radiosender + +[FST_LFR] +Unbeliebtester Radiosender + +[FEI_HOL] +Halten + +[FEI_ZOO] +Zoom + +[FEI_BTR] +> < - + +[FEI_NA] +Nicht verfügbar + +[MESA] +Mesa Grande + +[STRP_NO] +Zurzeit kannst du die Stripper-Bar nicht kaufen. Komm später wieder. + +[CHSE] +VERFOLGUNGSJAGD + +[NBMN_L] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um El Swanko Casa zu kaufen. Preis: $~1~ + +[NBMN_T] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um El Swanko Casa zu kaufen. Preis: $~1~ + +[NBMN_C] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um El Swanko Casa zu kaufen. Preis: $~1~ + +[LNKV_L] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Links View Apartment zu kaufen. Preis: $~1~ + +[LNKV_T] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Links View Apartment zu kaufen. Preis: $~1~ + +[LNKV_C] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Links View Apartment zu kaufen. Preis: $~1~ + +[HYCO_L] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Hyman Condo zu kaufen. Preis: $~1~ + +[HYCO_T] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Hyman Condo zu kaufen. Preis: $~1~ + +[HYCO_C] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Hyman Condo zu kaufen. Preis: $~1~ + +[OCHE_L] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Ocean Heights Apartment zu kaufen. Preis: $~1~ + +[OCHE_T] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Ocean Heights Apartment zu kaufen. Preis: $~1~ + +[OCHE_C] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Ocean Heights Apartment zu kaufen. Preis: $~1~ + +[WASH_L] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um 1102 Washington Street zu kaufen. Preis: $~1~ + +[WASH_T] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um 1102 Washington Street zu kaufen. Preis: $~1~ + +[WASH_C] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um 1102 Washington Street zu kaufen. Preis: $~1~ + +[VCPT_L] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um 3321 Vice Point zu kaufen. Preis: $~1~ + +[VCPT_T] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um 3321 Vice Point zu kaufen. Preis: $~1~ + +[VCPT_C] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um 3321 Vice Point zu kaufen. Preis: $~1~ + +[PRNT_L] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um die Druckerei zu kaufen. Preis: $~1~ + +[PRNT_T] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um die Druckerei zu kaufen. Preis: $~1~ + +[PRNT_C] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um die Druckerei zu kaufen. Preis: $~1~ + +[CAR_L] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Autohaus zu kaufen. Preis: $~1~ + +[CAR_T] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Autohaus zu kaufen. Preis: $~1~ + +[CAR_C] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Autohaus zu kaufen. Preis: $~1~ + +[PORN_L] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Filmstudio zu kaufen. Preis: $~1~ + +[PORN_T] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Filmstudio zu kaufen. Preis: $~1~ + +[PORN_C] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Filmstudio zu kaufen. Preis: $~1~ + +[ICE_L] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um die Eiscremefabrik zu kaufen. Preis: $~1~ + +[ICE_T] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um die Eiscremefabrik zu kaufen. Preis: $~1~ + +[ICE_C] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um die Eiscremefabrik zu kaufen. Preis: $~1~ + +[TAXI_L] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Taxiunternehmen zu kaufen. Preis: $~1~ + +[TAXI_T] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Taxiunternehmen zu kaufen. Preis: $~1~ + +[TAXI_C] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Taxiunternehmen zu kaufen. Preis: $~1~ + +[BANK_L] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um den Malibu Club zu kaufen. Preis: $~1~ + +[BANK_T] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um den Malibu Club zu kaufen. Preis: $~1~ + +[BANK_C] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um den Malibu Club zu kaufen. Preis: $~1~ + +[BOAT_L] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um die Bootswerft zu kaufen. Preis: $~1~ + +[BOAT_T] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um die Bootswerft zu kaufen. Preis: $~1~ + +[BOAT_C] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um die Bootswerft zu kaufen. Preis: $~1~ + +[STRP_L] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um den Pole Position Club zu kaufen. Preis: $~1~ + +[STRP_T] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um den Pole Position Club zu kaufen. Preis: $~1~ + +[STRP_C] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um den Pole Position Club zu kaufen. Preis: $~1~ + +[STOCK] +~r~nicht vorrätig + +[HELP14] +Um das Büro des Anwalts zu finden, folge dem ~h~'L'~w~ auf dem Radar. + +[BOAT_AS] +~g~Die Bootswerft generiert nun bis zu $~1~ Einkünfte. Hol dir das Geld regelmäßig ab. + +[BOAT_A2] +BOOTSWERFT-MISSIONEN ERFÜLLT + +[BOAT_N] +Checkpoint Charlie + +[BOAT_P] +~g~Sammle die Päckchen ein, ehe die Zeit um ist. + +[FEI_R1B] +R1- \ R2-Taste - + +[HELP9_A] +Drücke die ~h~~k~~PED_FIREWEAPON~~w~-Taste, um das Präzisionsgewehr abzufeuern. + +[HELP21] +Drücke die ~h~~k~~VEHICLE_ENTER_EXIT~~w~-Taste, um in ein Fahrzeug ein- oder auszusteigen. + +[CREAM] +Stoff-Auslieferung + +[UMBERTO] +Café Robina + +[PU_CF1] +Drück die ~h~~k~~PED_ANSWER_PHONE~~w~-Taste, um diese Waffe aufzunehmen. Falls du schon eine Waffe von diesem Typ hast, wird sie durch die neue ersetzt. + +[FED_RDM] +KARTE & SYMBOLE + +[FEC_ILU] +Kamera invertieren : + +[NITRO] +Alle Taxis haben jetzt einen Jumper-Turbo! Du musst nur die Taste für die Hupe drücken. + +[RATNG53] +Windei + +[RATNG54] +Niete + +[RATNG55] +Hacker + +[RATNG56] +Mogelpaket + +[RATNG57] +Totaler Lügner + +[STHC_04] +High-Score beim Fußball-Hochhalten + +[STHC_05] +Bestes Hotring-Resultat + +[STFT_13] +Schnellste Zeit bei Downtown Heli-Checkpoint + +[STFT_14] +Schnellste Zeit Ocean Beach Heli-Checkpoint + +[STFT_15] +Schnellste Zeit bei Vice Point Heli-Checkpoint + +[STFT_16] +Schnellste Zeit bei Little Haiti Heli-Checkpoint + +[STFT_21] +Schnellste Zeit bei Hotring + +[STFT_22] +Schnellste Rundenzeit bei Hotring + +[STFT_20] +Schnellste Zeit bei 'Pylonen-Rallye' + +[HELP44] +Halte in der ~q~rosa Markierung. + +[HELP45] +Drücke die ~h~~k~~PED_DUCK~~w~ um dich zu ducken. Dadurch erhöht sich die Treffsicherheit der Waffen, die du hältst. + +[RCR1_5] +RC Bandit-Rennen + +[RCPL1_7] +RC Baron-Rennen + +[RCH1_11] +RC Raider Checkpoint-Jagd + +[FEA_CTD] +Achtung! Für diesen Modus muss DTS-kompatible Hardware angeschlosen sein. Fortfahren? + +[FEM_STE] +AUF STEREO EINSTELLEN + +[FEM_UDY] +AUF DTS EINSTELLEN + +[GREET] +Grüße aus... + +[LANCE_1] +Hey, Mann, fahr vorsichtiger! + +[LANCE_2] +Hey, pass doch auf, was du machst! + +[LANCE_3] +Hey, wo fahren wir jetzt hin? + +[LANCE_4] +Was machen wir jetzt? + +[LAW4_15] +Mehr Geld! + +[MERC_5] +Schönes Auto, Mr. Vercetti. + +[MERC_26] +SCHNELLER, SCHNELLER, SCHNELLER! + +[MERC_27] +Vorsichtig, Tommy, ich hab mir erst letzten Monat die Nase korrigieren lassen. + +[MERC_28] +Tommy, fahr vorsichtig. + +[MERC_29] +Tommy, fahr langsamer. + +[MERC_30] +Tommy, bring jemand anders um, aber bitte nicht mich. + +[MERC_31] +Tommy, Baby, bring mich nicht um! + +[MERC_32] +Tommy, ich bin froh, dass du dieses Auto gestohlen hast! + +[MERC_40] +Ich hatte so viel Spaß. + +[MERC_43] +Adios, mein Engel. + +[MERC_44] +Und mach schön weiter Bodybuilding, hörst du? + +[MERC_45] +Ciao, mein Hübscher. + +[COL5_17] +Oh, Gott, sie haben einen Helikopter! + +[COL5_18] +Schießt den Helikopter ab! + +[COL5_19] +Tommy, schießen Sie den Helikopter ab! + +[COL5_20] +Da kommt er wieder! Schießt den Helikopter ab! + +[COL5_21] +Sieh dir diesen riesigen Helikopter an! + +[COL5_22] +Da kommt er wieder! + +[FEA_DSM] +Achtung! Dieses Spiel ist auf DTS-Tonausgabe eingestellt. Dazu muss DTS-kompatible Hardware angeschlossen sein. Bitte wählen Sie, ob Sie mit DTS oder STEREO-Tonausgabe fortfahren wollen. + +[STFT_23] +Schnellste Zeit bei Checkpoint Carlie + +[HELP50] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~-Taste, um die Spielerfigur von hinten zu sehen. + +[HELP51] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~-Taste, um die Spielerfigur von hinten zu sehen. + +[HELP52] +Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~-Taste, um die Spielerfigur von hinten zu sehen. + +[HELP53] +Benutze die ~h~~k~~PED_CYCLE_WEAPON_LEFT~~w~-Taste und die ~h~~k~~PED_CYCLE_WEAPON_RIGHT~~w~-Taste, um zwischen deinen Waffen zu wechseln. + +[HELP46] +Es gibt acht verschiedene Waffentypen. + +[HELP47] +Du kannst von jedem Waffentyp immer nur eine bei dir tragen - also einen Typ Pistole, einen Typ Schrotflinte, usw. + +[HELP54] +~w~Preis: $~1~ ~r~Deine augenblickliche Waffe wird ersetzt, wenn du diese kaufst. + +[HELP2A2] +Drücke die ~h~~k~~PED_SPRINT~~w~-Taste, um zu ~h~sprinten. + +[HLPSN_A] +Das Präzisionsgewehr ermöglicht dir, an dein Ziel heranzuzoomen und auf größere Distanz mit hoher Genauigkeit zu schießen. + +[HLPSN_B] +Halte die~h~ ~k~~PED_LOCK_TARGET~~w~-Taste gedrückt, um mit dem Präzisionsgewehr zu ~h~zielen~w~. + +[HLPSN_C] +Halte die~h~ ~k~~PED_LOCK_TARGET~~w~-Taste gedrückt, um mit dem Präzisionsgewehr zu ~h~zielen~w~. + +[HLPSN_D] +Drücke die ~h~~k~~PED_SNIPER_ZOOM_IN~~w~-Taste, um ~h~an das Ziel heranzuzoomen ~w~und die~h~ ~k~~PED_SNIPER_ZOOM_OUT~~w~,um ~h~herauszuzoomen~w~. + +[HLPSN_E] +Drücke die ~h~~k~~PED_FIREWEAPON~~w~-Taste, um das Präzisionsgewehr ~h~abzufeuern~w~. + +[HLPSN_F] +Drücke die ~h~~k~~PED_FIREWEAPON~~w~-Taste, um das Präzisionsgewehr ~h~abzufeuern~w~. + +[HLPSN_G] +Drücke die ~h~~k~~PED_FIREWEAPON~~w~-Taste, um das Präzisionsgewehr ~h~abzufeuern~w~. + +[PLANE_H] +Benutze die ~h~~k~~VEHICLE_ACCELERATE~~w~-Taste, um zu beschleunigen. Links bzw. Rechts für Richtungswechsel. + +[PLANE_4] { reVC update } +{Benutze die ~h~~k~~VEHICLE_ACCELERATE~~w~-Taste, um zu beschleunigen. Links bzw. Rechts für Richtungswechsel.} +Benutze den rechten Analog-Stick, um zu beschleunigen. Ziehe den linken Analog-Stick, um zu steigen oder drücke ihn nach vorn, um zu sinken. Links bzw. Rechts für Richtungswechsel. + +[HELP55] +Drücke die ~h~~k~~PED_FIREWEAPON~~w~-Taste, um den Küchenchef anzugreifen. + +[STPR_8] +Pole Position Club + +[STPR_9] +3321 Vice Point + +[STPR_10] +Links View Apartment + +[STPR_11] +El Swanko Casa + +[STPR_12] +1102 Washington Street + +[STPR_13] +Ocean Heights Apartment + +[STPR_14] +Skumole Shack + +[STPR_15] +Hyman Condo + +[RCCANX] +~r~Flugzeug-Mission abgebrochen. + +[CLT_HL2] +Wenn du neue Kleider aufnimmst, werden dadurch Fahndungslevel von einem bis zwei Sternen annulliert. + +[CRED009] +MISSION DESIGN + +[CRED359] +LEE JOHNSON + +[CRED360] +HENDRIK LESSER + +[CRED361] +PASQUALE STACCHIOTTI + +[CRED362] +ENRIQUE FERNANDEZ + +[CRED363] +PAUL BYERS + +[CRED364] +MIKE EMENY + +[CRED365] +ROB DUNKIN + +[CRED366] +CHARLIE KINLOCH + +[CRED367] +KEVIN HOBSON + +[CRED368] +JIM CREE + +[MOB_66A] +Tommy, Tommy, Tommy, wieso bist du wieder zurückgekommen? + +[MOB_66B] +Ich hab dir doch gesagt, wir wollen dich hier nicht mehr sehen. + +[MOB_67A] +Tommy, ich glaube, du solltest dich hier nicht mehr blicken lassen, hörst du? + +[MOB_67B] +Die Haitianer sind nicht sehr gut auf dich zu sprechen. + +[MOB_18A] +Tommy, hier Paul. Wie geht's, Alter? Hey, ich dachte mir, das musst du hören... + +[MOB_18B] +Echt der Hammer. Du glaubst nicht, was mir für 'ne Puppe über den Weg gelaufen ist. + +[MOB_18C] +'ne Bordsteinschwalbe, oder sowas. Unten in Little Havana. + +[MOB_18D] +Sagt, sie heißt Mercedes oder so ähnlich. + +[MOB_18E] +Wahnsinn, Alter. Die Puppe musst du dir geben. + +[MOB_18F] +Da würde 'nen Toter Hormonkoller kriegen. Sie sagt, ich wär der beste, den sie je hatte. + +[MOB_18G] +Halt die Augen nach ihr offen. Bis dann. + +[MOB_72A] +Tommy, ich bin's, Lance. Du hältst jetzt mal den Rand, Tommy, ich hab nämlich keine Zeit für Geschwätz. + +[MOB_72B] +Interessiert mich auch nicht, was du zu sagen hast. Warum auch? Ich bin dir doch sowieso scheißegal, stimmt's? + +[MOB_72C] +Du solltest dich mehr um mich kümmern. Mir einen fairen Anteil geben. Weißt du... + +[MOB_72D] +Tommy... hör mal, Mann, es tut mir leid. Nur... + +[MOB_72E] +ich werd schon mein Leben lang immer nur von oben herab behandelt, wie ein kleines Kind. + +[MOB_72F] +Mein Bruder hat das auch immer gemacht. Bitte, mein Alter, mach du das nicht. + +[MOB_72G] +Ich muss auflegen. + +[MOB_63A] +Tommy, hier Earnest. Earnest Kelly. + +[MOB_63B] +Wie geht's? + +[MOB_63C] +Ganz gut. Werd zum Laufen 'nen Stock brauchen, müsste aber bald wieder arbeiten können. + +[MOB_63D] +Gut. + +[MOB_63E] +Ich hab das mit Lance gehört. Was für ein Schwein, hä? + +[MOB_63F] +Ja. + +[MOB_63G] +Trau nie einem Mann, der im Pyjama auf der Straße herumläuft. Gut, dass du ihn erledigt hast. Ich hoffe, es war nicht kurz und schmerzlos. + +[MOB_63H] +Eher nicht. Ich hätte nur nicht gedacht, dass er so einer ist... + +[MOB_63I] +Tommy, für einen wildgewordenen Irren bist du ziemlich naiv. Ich bin bald wieder an der Arbeit, dann bring ich dir mal ein paar Sachen übers Leben bei, ok? + +[MOB_63J] +Lass dir Zeit, Earnest. Pass auf dich auf. + +[MOB_16A] +Tommy, hier Paul. Wie geht's, mein Freund? + +[MOB_16B] +Was willst du, Paul? Ich brauch keine getürkten Designer-Klamotten. + +[MOB_16C] +Sehr witzig. Du weißt, dass ich mit getürkter Ware nichts am Hut habe. Wollte nur hören, ob ich nicht 'ne Rolle in einem von deinen Filmen kriegen könnte. + +[MOB_16D] +In England habe ich damals viel einschlägiges Zeug gedreht. Ich hab mehr zu bieten als du, mein Alter. + +[MOB_16E] +Paul, danke für das Angebot. Ich komm auf dich zurück. + +[MOB_16F] +Lass mich nicht hängen. Denk dran, was ich alles für dich getan habe. + +[MOB_16G] +Das versuch ich ja grade zu vergessen. + +[MOB_17A] +Tommy Vercetti. Wie geht's, großer Meister? Man hört so einiges über dich. Bist jetzt 'ne große Nummer in der Stadt, hä? + +[MOB_17B] +Paul, du bist betrunken. + +[MOB_17C] +Nein, du Trottel, ich bin nicht betrunken. Hab mir nur ein paar Ladungen Stoff gegeben, war seit ein paar Tagen nicht im Bett. + +[MOB_17D] +Und du brauchst mich nicht dumm anzureden. Ich bin nicht irgendwer. Wer hat dir denn in dieser Stadt den Weg geebnet? Ich! + +[MOB_17F] +Tatsächlich? + +[MOB_17G] +Komm mir nicht so! Ich hab dich mit den ganzen Leuten bekanntgemacht. Hab dir gezeigt, wie der Hase läuft, hab alles mögliche für dich getan, und so dankst du es mir?! + +[MOB_17H] +Du ignorierst mich. Du gibst mir keine Chance, mitzumischen, nach allem, was ich für dich getan habe! Hältst du mich für einen Schwachkopf? + +[MOB_17I] +Paul, reg dich ab. Ich hatte viel zu tun. Sei kein Idiot. + +[MOB_17J] +Ich bin kein Idiot. Das haben sie schon im Jugendknast gesagt. Wenn du Ärger haben willst, Freundchen, den kannst du haben! + +[MOB_17K] +Tommy, bitte! Du warst meine große Hoffnung. Bitte, mach dich nicht lustig über mich! + +[MOB_17L] +Paul, schlaf mal 'ne Runde. Im Ernst. + +[MOB_73A] +Tommy, hier Steve. + +[MOB_73B] +Hey, Steve. + +[MOB_73C] +Hey, aber wie! Du bist ein Genie! Ich bin ein Genie! Sie lieben uns alle. Wir brechen alle Rekorde, mein Alter. + +[MOB_73D] +Uns winken ganz große Filmpreise. Jetzt kann ich endlich meinen alten Herrn ins Heim stecken und ihm sagen, er soll die Klappe halten. + +[MOB_73E] +Äh, das ist cool, Steve. + +[MOB_73F] +Cool? Mann, das ist heiß! Heiß! H.E.I.ß! Er hat nie an mich geglaubt. Hat immer gedacht, ich wäre kein Künstler, und jetzt hab ich's geschafft! + +[MOB_73G] +Ich bin der größte Porno-Regisseur aller Zeiten, mein Freund. Wollte dir nur sagen, es ist mir eine Freude, dich kennengelernt zu haben. + +[MOB_73H] +Danke, Steve. + +[MOB_73I] +Ich liebe dich, Baby. Bleib bloß genau so wie du bist, ok? + +[MOB_73J] +Werd's mir merken. Ciao, Steve. + +[BOLLOX] +Drücke die ~o~R1~w~-Taste, um eine Bombe abzuwerfen. Drücke die ~t~"~w~-Taste zum Abbrechen. + +[BRID_OP] +Sturmwarnung vorüber. Alle Brücken zum Festland sind wieder geöffnet. + +[BRID_CL] +Sturmwarnung: Alle Brücken zum Festland sind gesperrt. + +[LG_38] +Ziel + +[ASSET_C] +POLE POSITION ERWORBEN! + +[ASSET_D] +~g~Der Pole Position Club sorgt nun für ein Einkommen von bis zu $~1~ pro Tag. Hol dir dein Geld regelmässig! + +[ST_WHEE] +Längste 'Wheelie' Zeit (sekunden) + +[ST_STOP] +Längste 'Stoppie' Zeit (sekunden) + +[ST_2WHE] +Längste 2 Rad Zeit (sekunden) + +[ST_WHED] +Längste 'Wheelie' Distanz (m) + +[ST_STOD] +Längste 'Stoppie' Distanz (m) + +[ST_2WHD] +Längste 2 Rad Distanz (m) + +[OUTFT11] +Trainer + +[OUTFT12] +Frankie + +[RELOAD] +~g~Du hast die schnell Nachladefähigkeit gewonnen! + +[APACHE] +Hunter zur Heli Landeplattform am Ocean Beach geliefert. + +[CRED369] +JOHN MCCARDLE + +[CRED370] +DAVID MURDOCH + +[CRED371] +CHRIS BROWN + +[CRED372] +PAUL GREEN + +[CRED373] +KYLE MILNE + +[CUNTY] +Neue Kleider wurden zum Vercetti Estate geliefert! + +[GOODBOY] +$50 'Guter Bürger' Bonus! + +[NEWCONT] +Neuer Kontaktpunkt am Jachthafen am Ocean Beach!! + +[FIRELVL] +Feuerwehr-Mission Level ~1~ + +[HELP56] +Drücke die ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~-Taste, um den Blickwinkel zu ändern. + +[HELP57] +Drücke die ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~-Taste, um den Blickwinkel zu ändern. + +[HELP58] +Beim Zielen kann durch Drücken der ~h~~k~~PED_CYCLE_TARGET_LEFT~~w~-Taste zwischen Zielen hin- und her gewechselt werden. + +[HELP59] +Beim Zielen kann durch Drücken der ~h~~k~~PED_CYCLE_TARGET_LEFT~~w~-Taste zwischen Zielen hin- und her gewechselt werden. + +[HELP60] +Wenn du während eines Autodiebstahls die ~h~~k~~PED_SPRINT~ ~w~-Taste drückst, steigst du nicht in das Fahrzeug ein. + +[HELP61] +Du hast jetzt unbegrenzt Munition und doppelte Health für alle Fahrzeuge. + +[CRED374] +KEVIN YUN + +[CRED375] +ERICK COBBS + +[CRED376] +RANDY BLAKE + +[CRED377] +BRANDON LIM + +[CRED378] +BRANDON FENOL + +[CRED379] +MICHAEL MANOLE + +[CRED380] +ALETHEIA SIMONSON + +[CRED381] +JOHN JANSEN + +[FEC_LB1] +Schau + +[FEC_LB2] +nach hinten + +[FEC_LB3] +Nach hinten schauen + +[FEC_R3] +(R3-Taste) + +[FEC_PED] +Steuerung zu Fuß + +[FEC_VEH] +Steuerung in Fahrzeug + +[FEC_FPR] +Steuerung für First-Person + +[FEC_CMM] +Allgemeine Steuerung + +[FEC_PWL] +Nach links + +[FEC_PWR] +Nach rechts + +[FEC_PWF] +Vorwärts gehen + +[FEC_PWT] +Auf Kamera zugehen + +[FEC_PLB] +Nach hinten schauen + +[FEC_PFR] +Waffe abfeuern + +[FEC_CLE] +Eine Waffe nach links + +[FEC_CRI] +Eine Waffe nach rechts + +[FEC_LKT] +Ziel fixieren + +[FEC_PJP] +Fußgänger springen + +[FEC_PSP] +Fußgänger sprinten + +[FEC_PSH] +Fußgänger schießen + +[FEC_TLF] +Ein Ziel nach links + +[FEC_TRG] +Ein Ziel nach rechts + +[FEC_CCM] +Kamera hinter Spieler zentrieren + +[FEC_SZI] +Mit Präzisionsgewehr heranzoomen + +[FEC_SZO] +Mit Präzisionsgewehr herauszoomen + +[FEC_LKL] +First-Person nach links schauen + +[FEC_LRT] +First-Person nach rechts schauen + +[FEC_LUP] +First-Person nach oben schauen + +[FEC_LDN] +First-Person nach unten schauen + +[FEC_LBH] +Aus Fahrzeug nach hinten schauen + +[FEC_LLF] +Aus Fahrzeug nach links schauen + +[FEC_LRG] +Aus Fahrzeug nach rechts schauen + +[FEC_HRN] +Hupe + +[FEC_HBR] +Handbremse + +[FEC_ACL] +Gas geben + +[FEC_BRK] +Bremsen + +[FEC_TSM] +Spezialmissionen An/Aus + +[FEC_CRD] +Radiosender wechseln + +[FEC_ENT] +In Fahrzeug Ein-/Aussteigen + +[FEC_WPN] +Waffe abfeuern + +[FEC_PAS] +Pause + +[FEC_FPO] +First Person Weapons Toggle. + +[FEC_SMS] +Mauszeiger An/Aus + +[FEC_CMS] +Blickwinkel wechseln. + +[FEC_TSS] +Screen Shot + +[FEC_DBG] +Debug-Menü + +[FEC_TGD] +Mit Pad zwischen Spiel- u. Debug-Modus wechseln + +[FEC_TDO] +Debug-Kamera Aus + +[FEC_IVH] +Maus horizontal invertieren + +[FEC_MSL] +MAUSTASTE L + +[FEC_MSM] +MAUSTASTE M + +[FEC_MSR] +MAUSTASTE R + +[FEC_QUE] +??? + +[FEC_TWO] +Nur zwei Tastaturtasten erlaubt + +[FEC_OMS] +Nur eine Maustaste erlaubt + +[FEC_OJS] +Nur ein Joystick-Button pro Aktion erlaubt + +[FEC_PTL] +"Ziel fixieren" u. "Waffenauswahl links" gleichzeitig drücken. + +[FEC_PTR] +"Ziel fixieren" u. "Waffenauswahl rechts" gleichzeitig drücken. + +[FEC_LBC] +"Nach links schauen" u. "Nach rechts schauen" gleichzeitig drücken. + +[FEC_JBO] +JOY ~1~ + +[FEC_WAR] +Achtung! + +[FEC_OKK] +OK + +[FEC_DLF] +Löschen fehlgeschlagen. + +[FEC_SVU] +Speichern fehlgeschlagen. + +[FEC_LUN] +Laden fehlgeschlagen. Datei beschädigt. Bitte löschen. + +[FEC_PAD] +Gamepad + +[FEC_JOY] +Joystick + +[FES_CSA] +Wählen Sie eine Skin aus der Liste aus: + +[FET_HRD] +STANDARDEINSTLLG. WIEDERHERGESTELLT + +[FET_MST] +MAUSSTEUERUNG + +[FEC_STR] +NUM STERN + +[FET_MIG] +LINKS,RECHTS,MAUSRAD ZUR EINSTLLG. + +[FET_CIG] +RÜCKT. ZUM LÖSCHEN - LMT,RETURN ZUM ÄNDERN + +[FET_DSN] +Standard-Player Skin.bmp + +[FET_RSO] +ORIGINAL-EINSTELLG. WIEDERHERGESTELLT + +[FET_RSC] +HARDWARE NICHT VERFÜGBAR - ORIGINAL-EINSTELLG. WIEDERHERGESTELLT + +[FEA_3DH] +AUDIO HARDWARE + +[FEA_SPK] +BOXEN KONFIGURATION + +[FEM_LOD] +DISTANZ-DARSTELLG. + +[FEM_VSC] +FRAME SYNC + +[FEM_FRM] +FRAME LIMITER + +[FEM_MM] +HAUPTMENÜ + +[FED_RES] +BILDSCHIRMAUFLSG. + +[FET_CTL] +CONTROLLER-SETUP + +[FET_OPT] +OPTIONEN + +[FEC_MSH] +MAUSEMPFINDLICHKEIT + +[FEC_IVV] +MAUS VERTIKAL INVERTIEREN + +[FET_MTI] +MAUS STEURUNGSKONFIG. + +[FEC_FNC] +F~1~ + +[FEC_IRT] +EINFG + +[FEC_DLL] +ENTF + +[FEC_HME] +POS1 + +[FEC_END] +ENDE + +[FEC_PGU] +BILD AUF + +[FEC_PGD] +BILD AB + +[FEC_UPA] +AUF + +[FEC_DWA] +AB + +[FEC_LFA] +LINKS + +[FEC_RFA] +RECHTS + +[FEC_NUM] +NUM + +[FEC_NMN] +NUM~1~ + +[FEC_FWS] +NUM / + +[FEC_PLS] +NUM + + +[FEC_MIN] +NUM - + +[FEC_DOT] +NUM , + +[FEC_NLK] +NUMLOCK + +[FEC_ETR] +ENT + +[FEC_SLK] +ROLLEN + +[FEC_PSB] +UNTBR + +[FEC_BSP] +RÜCKT. + +[FEC_TAB] +TAB + +[FEC_CLK] +CAPSLOCK + +[FEC_RTN] +RET + +[FEC_LSF] +LUMSCHALT + +[FEC_RSF] +RUMSCHALT + +[FEC_LCT] +LSTRG + +[FEC_RCT] +RSTRG + +[FEC_LAL] +LALT + +[FEC_RAL] +RALT + +[FEC_LWD] +LWIN + +[FEC_RWD] +RWIN + +[FEC_WRC] +WINKLICK + +[FEC_SPC] +LEERT. + +[WIN_TTL] +GTA VC + +[WIN_95] +GTA VC läuft nicht unter Windows 95 + +[WIN_DX] +GTA VC benötigt mind. DirectX Version 8.1 + +[FET_EIG] +KANN DIESER AKTION KEINE STEUERUNG ZUWEISEN + +[FET_DAM] +DYNAMISCHE AKUSTIK + +[FEQ_SRE] +Wirklich beenden? Alle Daten seit dem letzten Speichern werden verlorengehen. Weiter? + +[FEQ_SRW] +Spiel wirklich beenden? + +[FET_QG] +SPIEL BEENDEN + +[FEN_STA] +SPIEL STARTEN + +[REPLAY] +WIEDERHOLUNG + +[FET_PAU] +PAUSENMENÜ + +[FEC_ANS] +Aktion + +[CVT_MSG] +Texturen werden in optimales Format für Ihre Grafikkarte konvertiert + +[FEC_SFT] +UMSCHALT + +[CVT_ERR] +Kein Platz mehr auf der Festplatte. Bitte schaffen Sie Speicherplatz, bevor Sie fortfahren. ESC zum Abbrechen. + +[FEH_VMP] +KARTE ANSEHEN + +[FES_DEE] +Löschen fehlgeschlagen! Bitte noch einmal versuchen. + +[FES_CMP] +Speichern fehlgeschlagen! Bitte noch einmal versuchen. + +[FESZ_WR] +Spiel wird gespeichert. Bitte warten... + +[FELD_WR] +Spiel wird geladen. Bitte warten... + +[FEDL_WR] +Gespeichertes Spiel wird gelöscht. Bitte warten... + +[PCRESRT] +Neues Spiel wird gestartet. Bitte warten... + +[FET_STI] +Standard-Steuerung + +[FET_CTI] +Classic-Steuerung + +[FET_PS] +SPIELER-SKIN SETUP + +[FEH_NA] +OPTION NICHT VERFÜGBAR + +[FEH_MPH] +MAUS, CURSOR ZUM BEWEGEN - BILD AUFW., BILD ABW., MAUSRAD ZUM ZOOMEN, L - LEGENDE + +[FEA_MP3] +MP3 PLAYER + +[NO_PCCD] +Bitte legen Sie die GTA Vice City Disk ein, oder drücken Sie ESC zum Abbrechen + +[FEH_SSA] +CURSOR ZUM BEWEGEN - S UM ZU SPEICHERN + +[FES_CMI] +LETZTE ERFÜLLTE MISSION + +[FET_STS] +STATISTIKEN GESPEICHERT IN 'STATS.HTML' + 'STATS.TXT' + +[WIN_VDM] +Nicht genug verfügbarer Grafikspeicher für GTA Vice City vorhanden + +[FEC_ERI] +Fehler! Eine oder mehrere Aktionen haben keine Tastenbelegungen. Bitte alle Aktionen belegen. + +[FEC_TFU] +Geschütz + nach hinten neigen + +[FEC_TFD] +Geschütz + nach vorne neigen + +[FET_RIG] +WÄHLEN SIE EINE NEUE TASTENBELEGUNG FÜR DIESE AKTION + +[FEA_NM3] +KEINE MP3-DATEIEN GEFUNDEN + +[FEA_MPB] +MP3 LAUTSTÄRKE-BOOST + +[FEA_MUS] +LAUTSTÄRKE MUSIK + +[FEA_SFX] +LAUTSTÄRKE SFX + +[FEA_ADP] +AUTOMATISCHE HARDWARE ERKENNUNG + +{=================================== MISSION TABLE AMBULAE ===================================} + +[A_COMP1:AMBULAE] +Krankenwagen-Missionen abgeschlossen: $ ~1~ + +[ATUTOR2:AMBULAE] +~g~Fahre die Patienten VORSICHTIG in die Klinik. Jede Erschütterung verringert ihre Überlebenschancen. + +[A_FULL:AMBULAE] +~r~Krankenwagen voll!! + +[A_FAIL2:AMBULAE] +~r~Deine Bummelei war tödlich für den Patienten! + +[A_FAIL3:AMBULAE] +~r~Der Patient ist tot!! + +[A_PASS:AMBULAE] +Gerettet! + +[A_COMP2:AMBULAE] +Du ermüdest nie! + +[A_CANC:AMBULAE] +~r~Krankenwagen-Mission abgebrochen! + +[A_COMP3:AMBULAE] +Krankenwagen-Missionen abgeschlossen! Du wirst beim Rennen nie ermüden! + +[ALEVEL:AMBULAE] +Krankenwagen-Mission Level ~1~ + +[A_FAIL1:AMBULAE] +Krankenwagen-Mission beendet. + +[A_SAVES:AMBULAE] +GERETTETE MENSCHEN: ~1~ + +{=================================== MISSION TABLE ASSIN1 ===================================} + +[ASM1_5:ASSIN1] +~r~Er hat seine Lieferungen abgeschlossen! + +[ASM1_6:ASSIN1] +Weitere Lieferungen: + +[ASM1_7:ASSIN1] +~g~Carl Pearson, Pizza-Lieferant. Schalte ihn aus, bevor er seine Lieferungen abschließt. + +[ASM1_A:ASSIN1] +Mr. Teal, Ihre Hilfe bei der Beseitigung der Landeier war äußerst wertvoll. Ich habe noch mehr Arbeit, die eine eher 'zupackende' Art verlangt. + +[ASM1_D:ASSIN1] +Mr. Teal, Ihre Hilfe bei der Beseitigung der Landeier war äußerst wertvoll. + +{=================================== MISSION TABLE ASSIN2 ===================================} + +[ASM2_1:ASSIN2] +~g~Mrs. Dawson verlässt bald den Juwelier in Vice Point. Schalte sie aus. Es muss wie ein Autounfall aussehen. + +[ASM2_3:ASSIN2] +~g~Das Fahrzeug wird explodieren! Hau ab! + +[ASM2_4:ASSIN2] +~r~Du hast ihr Auto beschädigt, obwohl sie nicht drin saß! Jetzt wird sie nicht einsteigen! + +[ASM2_5:ASSIN2] +~r~Sie ist entwischt! + +[ASM2_6:ASSIN2] +~r~Du warst zu nah am Unfallort! + +[ASM2_7:ASSIN2] +~g~Keine Waffen! Es soll wie ein Unfall aussehen! Dränge sie stattdessen von der Fahrbahn! + +[ASM2_8:ASSIN2] +~g~Das ganze muss wie ein Unfall aussehen. Benutze keine Waffen. + +[ASM2_9:ASSIN2] +Du brauchst einen fahrbaren Untersatz für diesen Job. + +[ASM2_10:ASSIN2] +~g~Wenn ihr Auto in Flammen aufgeht, entferne dich so weit wie möglich von der Unfallstelle. + +[ASM2_11:ASSIN2] +Hilfe! + +[ASM2_12:ASSIN2] +Hilf mir doch jemand! + +[ASM2_13:ASSIN2] +Oh Gott! + +[ASM2_A:ASSIN2] +Mein Kompliment für die gute Arbeit, Mr. Teal. Mein Kunde war sehr zufrieden. + +[ASM2_2:ASSIN2] +Health: + +{=================================== MISSION TABLE ASSIN3 ===================================} + +[ASM3_11:ASSIN3] +ZEIT: + +[ASM3_C:ASSIN3] +Eine europäische Gang plant einen Überfall auf eine Bank in Vice City. Meinen Arbeitgebern wäre sehr daran gelegen, dass das nicht passiert. + +[ASM3_D:ASSIN3] +Alle Mitglieder der Gang haben eine Tarnung, solange sie sich hier in Vice City aufhalten. Manche haben Jobs, andere geben sich als Touristen aus. + +[ASM3_E:ASSIN3] +Infos über alle Zielpersonen und ihre wahrscheinlichen Aufenthaltsorte kleben unter dem Telefon. + +[ASM3_14:ASSIN3] +~g~Dick Tanner hält sich bei DBP Security am Ocean Drive auf. + +[ASM3_15:ASSIN3] +~g~Marc Hammond und Franco Carter halten sich in der Nähe des Juwelierladens in Vice Point auf. + +[ASM3_16:ASSIN3] +~g~Nick Kong hält sich in der Nähe von Washington Beach auf. + +[ASM3_18:ASSIN3] +~g~Geh nicht zu nahe an deine Zielperson heran, sonst entdeckt sie dich und du musst hinter ihr herjagen. + +[ASM3_19:ASSIN3] +~g~Er hat dich gesehen! Schalte ihn aus! + +[ASM3_20:ASSIN3] +~g~Sie haben dich gesehen! Schalte alle beide aus! + +[ASM3_21:ASSIN3] +~r~Du hast nicht alle Mitglieder der Gang rechtzeitig erledigt! + +[ASM3_22:ASSIN3] +~g~Geh nicht zu nahe an deine Zielpersonen heran, sonst entdecken sie dich und versuchen zu fliehen. + +[ASM3_12:ASSIN3] +~g~In der Nähe sind einige Waffen für dich deponiert worden, falls du sie brauchen solltest. Du hast ~h~9 MINUTEN~g~, um alle Gang-Mitglieder auszuschalten. + +[ASM3_13:ASSIN3] +~g~Mike Griffin arbeitet an einer Plakatwand in Washington. + +[ASM3_17:ASSIN3] +~g~Charlie Dilson fährt mit dem Motorrad in Washington herum. + +{=================================== MISSION TABLE ASSIN4 ===================================} + +[ASM4_10:ASSIN4] +~g~Du warst anscheinend nicht der einzige, der hinter dem Aktenkoffer her war. Bring ihn schnell ins Ammu-Nation! + +[ASM4_12:ASSIN4] +Distanz: + +[ASM4_15:ASSIN4] +~g~Nimm das Präzisionsgewehr zu deiner Rechten. + +[ASM4_16:ASSIN4] +~g~Behalte die Frau auf der Empore im Auge. Sie wird die Rolltreppe hinuntergehen und die Zielperson nach der Uhrzeit fragen. + +[ASM4_17:ASSIN4] +~g~Schalte die Zielperson aus, NACHDEM die Frau mit ihr gesprochen hat. Aber erledige nicht die Frau. + +[ASM4_18:ASSIN4] +~g~Wenn die Zielperson ausgeschaltet ist, nimm ihren Aktenkoffer und bringe ihn zum Ammu-Nation in Downtown. + +[ASM4_19:ASSIN4] +~g~Halte Abstand von der Zielperson. Die Entfernungs-Anzeige rechts oben am Bildschirm zeigt an, wie nahe du an der Zielperson bist. + +[ASM4_20:ASSIN4] +~g~Lass den Anzeigebalken nicht an den Anschlag geraten, sonst sieht dich die Zielperson. + +[ASM4_21:ASSIN4] +~g~Schnapp dir den Aktenkoffer! + +[ASM4_22:ASSIN4] +~g~Bring den Aktenkoffer zum Ammu-Nation in Downtown. + +[ASM4_23:ASSIN4] +~g~Er hat dich entdeckt und versucht zu fliehen. Erledige ihn und schnapp dir den Aktenkoffer! + +[ASM4_25:ASSIN4] +~r~Du hast die Frau ausgeschaltet, du Idiot! + +[ASM4_26:ASSIN4] +~r~Die Zielperson hat das Flugzeug bestiegen! + +[ASM4_27:ASSIN4] +~r~Die Zielperson hat dich gesehen! Du hättest Abstand halten sollen! + +[ASM4_28:ASSIN4] +~r~Die Zielperson hat dich gesehen! Er hat gehört, wie du geschossen hast! + +[ASM4_29:ASSIN4] +~r~Schalte ihn erst aus, wenn er mit der Frau gesprochen hat! + +[ASM4_A:ASSIN4] +Es wird Zeit für dickere Brocken, Mr. Teal. Unter dem Busch zu Ihrer Rechten ist ein Gewehr. + +[ASM4_B:ASSIN4] +Behalten Sie die Frau auf der Empore über dem Check-In im Auge. Sie wird durch die Menge gehen und jemanden nach der Uhrzeit fragen. + +[ASM4_C:ASSIN4] +Sie müssen die betreffende Person ausschalten, ihren Aktenkoffer nehmen und ihn zu der Adresse, die unter dem Telefon klebt, bringen. + +{=================================== MISSION TABLE ASSIN5 ===================================} + +[ASM5_A:ASSIN5] +Auf dem Dach der Cherry Popper-Eiscremefabrik findet eine Übergabe von wertvoller Ware statt. + +[ASM5_B:ASSIN5] +Erledigen Sie alle Beteiligten, klauen Sie die Ware und bringen Sie sie zum Heliport am Flughafen. + +[ASM5_C:ASSIN5] +Links von Ihnen ist ein Tor, das zur Rückseite der Fabrik führt. + +[ASM5_1:ASSIN5] +~g~Geh auf das Gelände hinter der Cherry Popper-Eiscremefabrik und dann auf das Dach, wo der Deal abgewickelt wird. + +[ASM5_2:ASSIN5] +~g~Schnapp dir die Ware und bring sie zum Heliport am Flughafen. + +[ASM5_3:ASSIN5] +~g~Bring die Ware zum Heliport am Flughafen! + +{=================================== MISSION TABLE BANKJ1 ===================================} + +[WANTED1:BANKJ1] +~g~Schüttle die Cops ab. Verringere deinen Fahndungslevel. + +[BJM1_A:BANKJ1] +Tommy! Hey, Tommy, sieh mal, das ist super! Ich hab eine Minibar einbauen lassen! + +[BJM1_B:BANKJ1] +Wir haben unten eine ausgewachsene Bar, Ken. + +[BJM1_C:BANKJ1] +Ja, ja, wie auch immer. Tja, ich hab die Tafel besorgt, die du haben wolltest. + +[BJM1_D:BANKJ1] +Ah, das ist der Lohn des Jurastudiums: Die Fähigkeit, Anweisungen auszuführen. + +[BJM1_E:BANKJ1] +Also, ich brauche einen Safeknacker. + +[BJM1_F:BANKJ1] +Oh, ok, mal nachdenken...Safeknacker, Safeknacker...Ich hab's! Du wirst begeistert sein! + +[BJM1_G:BANKJ1] +Aah, nicht dieser Schwachkopf. Der sitzt doch. + +[BJM1_H:BANKJ1] +Wo sitzt er denn? + +[BJM1_I:BANKJ1] +In einer Zelle in einem Polizeirevier. Er wartet auf seine Verlegung. + +[BJM1_J:BANKJ1] +Ich glaube fast, er kommt auf Bewährung raus... + +[BJM1_1:BANKJ1] +~g~Befreie Cam Jones aus der Haft! + +[BJM1_3:BANKJ1] +~g~In der Umkleide des Reviers findest du etwas Nützliches. + +[BJM1_21:BANKJ1] +~g~Die Key Card zu den Zellen findet sich im Obergeschoss des Reviers. + +[BNK1_7:BANKJ1] +Cam Jones? + +[BNK1_8:BANKJ1] +Ich hol dich hier raus. + +[BNK1_10:BANKJ1] +Ja, der bin ich... + +[BNK1_11:BANKJ1] +Ganz wie du meinst! + +[BNK1_13:BANKJ1] +Ich ziehe einen Job durch, und du bist mein Safeknacker. + +[BNK1_14:BANKJ1] +Besser als in 'ner Zelle zu verrotten! + +[BJM1_22:BANKJ1] +~g~Bring Cam nach Hause! + +[BJM1_23:BANKJ1] +~g~Du brauchst zunächst die Magnetkarte für die Tür! + +[BNK1_12:BANKJ1] +Häng die Bullen ab und bring mich nach Hause! + +[BJM1_20:BANKJ1] +Die Waffe weg oder es passiert was! + +[BJM1_5:BANKJ1] +Zutritt ab hier nur für befugtes Personal. + +[BJM1_2:BANKJ1] +~r~Du solltest Cam die Freiheit bringen, nicht den Tod! + +[BJM1_4:BANKJ1] +Er ist bewaffnet! Erledigt ihn! + +{=================================== MISSION TABLE BANKJ2 ===================================} + +[BJM2_A:BANKJ2] +Wir brauchen einen Überfall-Experten. Kennt ihr einen? + +[BJM2_B:BANKJ2] +Hey, Tommy, Tommy, Tommy, das Zeug bringt dich nach vorn, Mann. + +[BJM2_C:BANKJ2] +WoooOOOooo! + +[BJM2_D:BANKJ2] +Ich könnte dein Experte sein! Überfall! Überfall! + +[BJM2_E:BANKJ2] +Du bist kein Experte, du bist ein Idiot. + +[BJM2_F:BANKJ2] +Mach dir 'nen Drink und halt die Klappe. + +[BJM2_G:BANKJ2] +Hey, aus dem Weg! + +[BJM2_H:BANKJ2] +Cam, was meinst du? + +[BJM2_I:BANKJ2] +Tja, der beste Schütze in der Stadt ist ein Kerl namens Cassidy. + +[BJM2_J:BANKJ2] +Ach ja? + +[BJM2_K:BANKJ2] +Ja. Soldat, oder wenigstens hält er sich dafür. + +[BJM2_L:BANKJ2] +Ich bezweifle, dass er je bei der Army war, aber er kann mit der Knarre umgehen. + +[BJM2_M:BANKJ2] +Wahrscheinlich ist er in der Schießanlage. + +[BJM2_2A:BANKJ2] +Bist du Phil Cassidy? + +[BJM2_2B:BANKJ2] +Wieso? + +[BJM2_2C:BANKJ2] +Ich suche einen, der mit der Kanone umgehen kann. Was ich hier so sehe, überzeugt mich nicht. + +[BJM2_2D:BANKJ2] +Jungchen, ich schieße dir auf 25 Meter eine Fliege vom Kopf. + +[BJM2_2E:BANKJ2] +Ach wirklich? + +[BJM2_2F:BANKJ2] +Ja. Hab ich bei der Army gelernt. + +[BJM2_2G:BANKJ2] +Ist Fliegen-Schießen so beliebt in der Army? Gut, dass ich keine Steuern zahle. + +[BJM2_2H:BANKJ2] +Sollte das witzig sein, Jungchen? + +[BJM2_2I:BANKJ2] +Ha ha ha ha ha! + +[BJM2_2J:BANKJ2] +Lass uns schießen. + +[BJM2_1:BANKJ2] +~g~Begib dich nach Downtown ins Ammu-Nation und sprich mit Phil Cassidy. + +[BJM2_3:BANKJ2] +TREFFERQUOTE: ~1~% + +[BJM2_4:BANKJ2] +PUNKTE RUNDE EINS: ~1~ + +[BJM2_6:BANKJ2] +PUNKTE RUNDE ZWEI: ~1~ + +[BJM2_7:BANKJ2] +GESAMTPUNKTZAHL FÜR DAS SCHIESSEN: ~1~ + +[BJM2_9:BANKJ2] +~g~Begib dich zum Startpunkt für Runde Zwei. + +[BJM2_11:BANKJ2] +~r~Phil ist erledigt! + +[BJM2_12:BANKJ2] +~r~Einer der Schützen ist erledigt! + +[BJM2_14:BANKJ2] +~g~Begib dich zum nächsten Areal! + +[BJM2_15:BANKJ2] +PUNKTE: + +[BJM2_17:BANKJ2] +~g~Sprich mit Phil. + +[BJM2_18:BANKJ2] +ZU SCHLAGEN: + +[BJM2_19:BANKJ2] +~g~Triff innerhalb des Zeitlimits so viele Ziele wie du kannst! + +[BJM2_22:BANKJ2] +~r~Du hast den Schießstand verlassen! + +[BJM2_23:BANKJ2] +~g~Wenn du den Schießstand während des Wettbewerbs verlässt, ist die Mission gescheitert. + +[BJM2_24:BANKJ2] +~g~Das nahegelegenste Ziel bringt 1 Punkt. + +[BJM2_25:BANKJ2] +~g~Das mittlere Ziel bringt 2 Punkte. + +[BJM2_27:BANKJ2] +~g~Alle Ziele dieser Runde bringen 1 Punkt. + +[BNK2_2:BANKJ2] +ZIELEN 3-2-1 FEUER! + +[BNK2_3:BANKJ2] +AREAL KLAR! + +[BNK2_4:BANKJ2] +Huuuiii! + +[BNK2_5:BANKJ2] +Der träfe nicht mal ein Scheunentor. + +[BNK2_7:BANKJ2] +Also, was ist jetzt, hilfst du mir bei dem Job? + +[BNK2_8:BANKJ2] +Jungchen, so wie du schießt, würde ich dich sogar heiraten. + +[BNK2_9A:BANKJ2] +Junge, deine Sprüche und deine Hirngespinste kannst du dir sonst wohin stecken. Du bist ein lausiger Schütze. + +[BNK2_9B:BANKJ2] +Du bist ein lausiger Schütze. + +[BJM2_28:BANKJ2] +PUNKTE RUNDE DREI: ~1~ + +[BJM2_20:BANKJ2] +~g~Geht dir die ~w~Zeit ~g~oder die ~w~Munition ~g~aus, ist die Runde beendet! + +[BJM2_26:BANKJ2] +~g~Das am weitesten entfernte Ziel bringt 3 Punkte. + +[BNK2_1:BANKJ2] +SCHARFE MUNITION + +[RANGE_1:BANKJ2] +PUNKTZAHL SCHIESS-RUNDE:~1~ + +[BJM2_2:BANKJ2] +~g~Um die Runde zu beenden, drücke die ~h~~k~~PED_JUMPING~. + +[BJM2_N:BANKJ2] +Nur die Ruhe. + +{=================================== MISSION TABLE BANKJ3 ===================================} + +[BJM3_A:BANKJ3] +Langsam fügt sich alles sehr schön zusammen hier. + +[BJM3_B:BANKJ3] +Was ist der Plan, Tommy? Was geht ab, Amigo? + +[BJM3_C:BANKJ3] +Der Plan ist, dass du dich wie ein Vollidiot benimmst. Wir brauchen einen Fahrer. + +[BJM3_D:BANKJ3] +Tommy, ich mach's. Ich kann fahren. + +[BJM3_E:BANKJ3] +Nimm Hilary, Boss, nicht diesen Labersack von einem Rechtverdreher. + +[BJM3_F:BANKJ3] +Hilary ist der beste. So schnell wie den hast noch keinen fahren sehen. Ich rufe ihn mal an. + +[BJM3_G:BANKJ3] +Hey, Hil, hier Phil. Wie läuft's? Nein, sag nichts. Dazu haben wir später Zeit. Tust du mir einen Gefallen? + +[BJM3_H:BANKJ3] +Ich hab hier einen Typ aus dem Norden. Nein, ich glaub, er war nicht beim Militär. Aber er braucht einen Fahrer. + +[BJM3_I:BANKJ3] +Für einen Job. Ok, verstehe. + +[BJM3_J:BANKJ3] +Was hat er gesagt? + +[BJM3_K:BANKJ3] +Er macht's. Kein Problem. Na ja, ein kleines vielleicht: Er leidet unter Verlustängsten. + +[BJM3_L:BANKJ3] +Er arbeitet anscheinend nicht für Leute, die ihn nicht schlagen können. Hat was mit seiner Mutter zu tun. + +[BJM3_M:BANKJ3] +Jedenfalls will er erst ein Rennen gegen dich fahren. Er wartet draußen auf dich. + +[BJM3_2A:BANKJ3] +Bist du Tommy? Klar bist du Tommy, ich meine, + +[BJM3_2B:BANKJ3] +wieso sollte sonst einer mit mir reden wollen? + +[BJM3_2C:BANKJ3] +Ok. Das ganze läuft so ab: + +[BJM3_2D:BANKJ3] +Ich fahre für dich, WENN und NUR WENN du selbst anständig fährst. + +[BJM3_2E:BANKJ3] +Verlierst du mich, verzeihe ich dir das nie. + +[BJM3_2:BANKJ3] +Hilary ist erledigt! + +[BJM3_4:BANKJ3] +~g~Du brauchst ein Auto, um mitzumachen. + +[BNK3_1:BANKJ3] +Ok, ich fahre für dich. Aber bitte, behandle mich schlecht. + +[BNK3_3A:BANKJ3] +Illegales Straßenrennen bei Vice Point. + +[BNK3_3B:BANKJ3] +An alle Einsatzkräfte. + +[BNK3_3C:BANKJ3] +Straßenrennen sind verboten und illegal! + +{=================================== MISSION TABLE BANKJ4 ===================================} + +[BNK4_A:BANKJ4] +~w~Ihr seht Gentlemen, das wird leicht verdientes Geld für uns. + +[BNK4_B:BANKJ4] +~w~Ernsthaft, Tommy, du solltest dir überlegen, Anwalt zu werden. + +[BNK4_C:BANKJ4] +~w~Was zum Geier rauchst du eigentlich, Mann? Das ist kein simpler Plan. + +[BNK4_D:BANKJ4] +~w~Ach, wer braucht schon simple Pläne? + +[BNK4_E:BANKJ4] +~w~Der Kommunismus, das war ein simpler Plan. Hat Russland aber nicht viel genützt, hah? + +[BNK4_F:BANKJ4] +~w~Ganz ruhig. Mit einem Team wie diesem ist das alles kein Problem. + +[BNK4_G:BANKJ4] +~w~Cam übernimmt den Safe. Phil? Wir beide kümmern uns um die Sicherheit, und Hilary fährt den Fluchtwagen. + +[BNK4_H:BANKJ4] +~w~Äh, hast du nicht jemanden vergessen? Jemanden, der dir unzählige Male geholfen hat in dieser Stadt? Jemanden...? + +[BNK4_I:BANKJ4] +~w~Ken...Ken, richtig. Ken wäscht das Geld für uns und stellt schon mal die Drinks kalt. + +[BNK4_J:BANKJ4] +~w~Ich verstehe nicht, was ich hier soll. + +[BNK4_K:BANKJ4] +~w~Ist doch ganz einfach. Warst du noch nie im Kino? + +[BNK4_L:BANKJ4] +~w~Wir latschen in die Bank rein, fuchteln mit den Knarren rum und gehen stinkreich wieder raus. + +[P_DEAD:BANKJ4] +~r~Phil ist erledigt!! + +[C_DEAD:BANKJ4] +~r~Cam ist erledigt!! + +[H_DEAD:BANKJ4] +~r~Hilary ist erledigt!! + +[P_HIND:BANKJ4] +~r~Du hast Phil verloren! + +[C_HIND:BANKJ4] +~r~Cam wurde abgehängt! + +[H_HIND:BANKJ4] +~r~Hilary wurde im Stich gelassen! + +[GETCAR:BANKJ4] +Steig in den Fluchtwagen und führe den Plan aus! + +[TRASHED:BANKJ4] +~r~DU HAST DEN FLUCHTWAGEN GESCHROTTET!! + +[BNK4_1:BANKJ4] +Ich fahre. + +[BNK4_2:BANKJ4] +Na prima. Beifahrer. Wenn ich das in der Therapie erzähle. + +[BNK4_3A:BANKJ4] +Hey, pass auf, wo du hinfährst, Tommy! + +[BNK4_3B:BANKJ4] +Tommy, Hilary macht sich so breit! + +[BNK4_3C:BANKJ4] +Stimmt gar nicht! + +[BNK4_3D:BANKJ4] +Doch! + +[BNK4_3E:BANKJ4] +Hey, Klappe, ihr zwei, oder ihr könnt zu Fuß gehen. + +[BNK4_3F:BANKJ4] +Ja, Hilary. + +[BNK4_3I:BANKJ4] +Herrgott, Phil, hör auf, mit diesem Ding rumzufuchteln! + +[BNK4_3J:BANKJ4] +Ja, am Ende geht das noch ins Auge! + +[BNK4_3M:BANKJ4] +Mein Baby! Alles Schrott! + +[BNK4_3O:BANKJ4] +Du hängst zu sehr an der Illusion der Ewigkeit. + +[BNK4_3P:BANKJ4] +Was? + +[BNK4_3Q:BANKJ4] +Du denkst, alles bleibt ewig. + +[BNK4_3R:BANKJ4] +Jugend, geliebte Menschen, Pizza, + +[BNK4_3S:BANKJ4] +Alles geht mal vorbei, und das musst du akzeptieren. + +[BNK4_3T:BANKJ4] +Hey, du hast recht. Danke, Cam. + +[BNK4_3U:BANKJ4] +Nichts zu danken. + +[BNK4_3V:BANKJ4] +Hey, Tommy, wieso halten wir? + +[BNK4_4A:BANKJ4] +~w~Hilary, fahr ein bisschen um den Block. + +[BNK4_5:BANKJ4] +~w~Ok, Tommy, ok. + +[BNK4_6:BANKJ4] +~w~DIES IST EIN ÜBERFALL! + +[BNK4_7:BANKJ4] +~w~KEINER BEWEGT SICH! + +[BNK4_8:BANKJ4] +~w~ALLE AN DIE WAND! + +[BNK4_9:BANKJ4] +Phil, halt die Stellung! + +[BNK4_10:BANKJ4] +Verstanden! + +[BNK4_11:BANKJ4] +Los Cam, der Tresor ist oben... + +[BK4_12A:BANKJ4] +Verdammt, das ist ein Flange 9000! + +[BK4_12B:BANKJ4] +Könnte Stunden dauern, den zu knacken. + +[BK4_12C:BANKJ4] +Oder 5 Minuten, wenn du den Manager findest. + +[BNK4_13:BANKJ4] +Ich seh nach, wo er sich verkrochen hat. + +[BK4_14A:BANKJ4] +Phil, alles im grünen Bereich? + +[BNK4_15:BANKJ4] +Klar. Im dunkelgrünen Bereich. + +[BNK4_16:BANKJ4] +Du da! Mitkommen! + +[BNK4_17:BANKJ4] +Ok! Ok! Nicht schießen! + +[BNK4_18:BANKJ4] +ICH SAGTE, KEINER RÜHRT SICH! + +[BK4_19A:BANKJ4] +Der Safe hat eine Zeitsperre, + +[BK4_19B:BANKJ4] +ihr könnt ebenso gut gleich aufgeben! + +[BK4_20A:BANKJ4] +Ach, die Zeitsperre kann ich umgehen. + +[BK4_20B:BANKJ4] +Dann brauchen wir nur noch deinen Code und alles ist in Butter! + +[BNK4_21:BANKJ4] +Bleib hier. Wenn du Zicken machst, bist du Fischfutter, klar? + +[BNK422A:BANKJ4] +Cam, wie lange noch? + +[BK4_23A:BANKJ4] +Gib mir noch 3 Minuten! + +[BK4_24A:BANKJ4] +Ich seh mal nach Phil. Bin gleich wieder da. + +[BK4_24B:BANKJ4] +Ich hab gesagt, Finger weg vom Alarm! + +[BNK4_25:BANKJ4] +Das Spezialkommando muss gleich hier sein! + +[BNK4_27:BANKJ4] +Ich könnte ein bisschen Hilfe brauchen, Tommy! + +[BNK4_28:BANKJ4] +Vice City Spezialkommando! Sie sind umzingelt! + +[BNK4_29:BANKJ4] +Umzingelt? HA HA HA HAAAAAaaa! + +[BNK4_30:BANKJ4] +Die scheißen sich ein, die korrupten Schweine! + +[BK4_31A:BANKJ4] +Tommy, der Tresor ist offen! + +[BK4_34A:BANKJ4] +Ok, wir haben die Pensionskasse der Bullen. Los, raus hier! + +[BK4_34B:BANKJ4] +Ok, ihr habt es so gewollt. Ihr hattet eure letzte Chance! + +[BK4_35A:BANKJ4] +Die stürmen den Laden! + +[BK4_35B:BANKJ4] +In Deckung! + +[BNK4_94:BANKJ4] +~w~Ok, Jungs. Jeder hält sich an den Plan . + +[BM_DEAD:BANKJ4] +~r~Du brauchst den Bank Manager lebend!! + +[ASSET_A:BANKJ4] +BANK-MISSIONEN ERFÜLLT! + +[ASSET_B:BANKJ4] +~g~Der Malibu Club generiert nun bis zu $~1~ Einkünfte. Hol dir das Geld regelmäßig ab. + +[IDIOT:BANKJ4] +~r~Na super - angezogen wie ein Irrer durch die Gegend laufen und Aufmerksamkeit erregen, IDIOT! + +{=================================== MISSION TABLE BARON1 ===================================} + +[COK1_A:BARON1] +Komm schon, Brauner, komm! + +[COK1_B:BARON1] +Dämlicher Klepper! Dich mach ich einen Kopf kürzer! + +[COK1_C:BARON1] +Wer ist der Blödmann? + +[COK1_D:BARON1] +Tommy Vercetti, Sie erinnern sich. + +[COK1_E:BARON1] +Tschuldigung, ich bin etwas nervös. Vertrau niemals einem verdammten Pferd! + +[COK1_F:BARON1] +Du machst deine Sache gut. Du arbeitest jetzt für mich. + +[COK1_H:BARON1] +Wie gesagt, Amigo, du arbeitest für mich. Basta. So ein Mistkerl hat mich betrogen. + +[COK1_I:BARON1] +Er denkt, ich weiß nicht, wie viel Geld mir zusteht. Aber 3% zu klauen ist genauso schlimm wie 100% zu klauen. + +[COK1_J:BARON1] +Niemand haut mich übers Ohr. NIEMAND!! + +[COK1_K:BARON1] +Folge ihm von seiner Wohnung aus und sieh nach, wo er hin will. Später erledigen wir ihn. + +[COK1_1:BARON1] +Oh, Shit! + +[COK1_2:BARON1] +Zu langsam, Opa! + +[COK1_4:BARON1] +Versager. + +[COK1_5:BARON1] +Ich würde einen Zahn zulegen, du Mistsack! + +[COK1_8:BARON1] +~g~Schnell! Schnapp dir einen Untersatz und folge ihm! + +[COK1_9:BARON1] +~r~Du sollst ihn verfolgen, nicht umlegen! + +[COK1_10:BARON1] +~r~Begib dich zum Haus des Diebes und such das Geldversteck. + +[COK1_11:BARON1] +~g~Schau durch sein Fenster. + +[COK1_7:BARON1] +~g~Er ist aufs Dach geflohen. Bleib ihm auf den Fersen, aber erledige ihn nicht. + +[COK1_G:BARON1] +Ich arbeite gegen Geld. + +{=================================== MISSION TABLE BARON2 ===================================} + +[COK2_A:BARON2] +Was bist du eigentlich für ein inkompetenter Narr? + +[COK2_B:BARON2] +NARR! NARR! NARR! NARR! + +[COK2_C:BARON2] +Tommy- + +[COK2_D:BARON2] +Was, Ricardo? + +[COK2_E:BARON2] +Diese Idioten - dauernd wollen sie einen reinlegen. + +[COK2_F:BARON2] +Das ist der Fehler an dieser Branche. + +[COK2_G:BARON2] +Was soll denn das? Aaaaah! + +[COK2_H:BARON2] +Diese Mistkerle haben mich bitter enttäuscht. + +[COK2_I:BARON2] +Bald denkt jeder Depp, er kann in Vice City Koks verkaufen. + +[COK2_J:BARON2] +Was kommt als nächstes, hah? Die verstunkene Mafia?! + +[COK2_K:BARON2] +Diese Bandengegend ist eine Festung ohne Mauern. + +[COK2_L:BARON2] +Also: Quentin hier - Quentin! QUENTIN! + +[COK2_M:BARON2] +Er fliegt dich über das Areal. + +[COK2_N:BARON2] +Heb ihr Nest aus! + +[COK2_O:BARON2] +Was soll denn das? + +[COK2_P:BARON2] +Was willst du hier? + +[COK2_Q:BARON2] +Hey, ich hab mich umgehört, und es ist klar, + +[COK2_R:BARON2] +dass Diaz in den Deal reingeplatzt ist und meinen Bruder erledigt hat. + +[COK2_S:BARON2] +Und dich erledigt er auch! + +[COK2_T:BARON2] +Mit Diaz nehm ich's auf! + +[COK2_U:BARON2] +Nein, hör zu! Ich übernehme Diaz - + +[COK2_V:BARON2] +er beginnt mir zu vertrauen. + +[COK2_1:BARON2] +Eines versteh ich nicht: Was soll das mit 'Quentin'!? + +[COK2_2:BARON2] +Weiß nicht. Hat mir immer gefallen...Quentin Vance... + +[COK2_3:BARON2] +Vance? Du heißt wirklich Lance Vance?! + +[COK2_4:BARON2] +Hey! Das hab ich schon in der Schule oft genug gehört! + +[COK2_5:BARON2] +Hast du so was schon mal aus einem Helikopter abgefeuert? + +[COK2_8:BARON2] +Wo fliegen wir denn hier? + +[COK2_9:BARON2] +Prawn Island. + +[COK2_13:BARON2] +'Lance Vance'. Du armer Teufel. + +[COK2_14:BARON2] +Ok, wir sind gleich da. + +[COK2_15:BARON2] +Wir fliegen ein paar Mal drüber weg. + +[COK2_16:BARON2] +Schalte so viele Kerle aus wie du kannst. + +[COK2_17:BARON2] +Dann setze ich dich ab, und du bist auf dich allein gestellt. + +[COK2_20:BARON2] +Shit! Das ist ja wie im Krieg hier! Schalt welche von den Schützen aus! + +[COK2_21:BARON2] +Wir werden getroffen, Mann! + +[COK2_22:BARON2] +Reparaturen an dem Kübel hier kosten! Schalt sie aus! + +[COK2_23:BARON2] +Ok, von jetzt an bist du auf dich alleine gestellt. Viel Glück! + +[COK2_24:BARON2] +Zustand Helikopter: + +[COK2_25:BARON2] +~g~Hol das Geld auf dem Dach. + +[COK2_27:BARON2] +Du bist auf MEINEM Gebiet, Mistkerl! + +[COK2_28:BARON2] +Dich mach ich fertig! + +[COK2_6:BARON2] +Nein. Aber ich kann ja unterwegs ein bisschen üben. + +[OPEN_B:BARON2] +Die Straßensperren zum Festland sind aufgehoben worden. + +{=================================== MISSION TABLE BARON3 ===================================} + +[COK3_A:BARON3] +Das gefällt euch nicht, was?! + +[COK3_B:BARON3] +Ahahahahaa, Ahahahahaa. + +[COK3_C:BARON3] +Vorsicht! Passen Sie auf, wo Sie damit hin zielen! + +[COK3_D:BARON3] +Kein Taubendreck mehr auf MEINEM Dach, was Tommy? + +[COK3_E:BARON3] +Sieht nicht so aus. + +[COK3_F:BARON3] +Das kannst du laut sagen. Also, hör zu: + +[COK3_G:BARON3] +Weißt du, wem das schnellste Boot an der Ostküste gehört? + +[COK3_H:BARON3] +Nicht wirklich. + +[COK3_I:BARON3] +MIR. Und das soll auch so bleiben. + +[COK3_J:BARON3] +Jeder Schmuggler von hier bis Caracas hat EINEN Traum: ein schnelleres Boot. + +[COK3_K:BARON3] +Es heißt, die Hull Werft hat gerade so ein Boot fertiggestellt, + +[COK3_L:BARON3] +für irgendeinen costaricanischen Idioten. + +[COK3_M:BARON3] +Und, Tommy...ICH WILL DIESES BOOT!!! + +[COK3_N:BARON3] +Ich glaube, die Tauben sind wieder da. + +[COK3_O:BARON3] +Ah! Ich dachte, ich hab euch erwischt. Wo kommst du denn her? + +[COK3_P:BARON3] +Tauben! Boom! Aaaaah! + +[COK3_5:BARON3] +~g~Suche den Schalter, um das Boot abzusenken. + +[COK3_6:BARON3] +~g~Bringe das Boot zur Villa. + +[COK3_7:BARON3] +~r~Du hast das Boot zerstört! + +[COK3_8:BARON3] +~g~Begib dich zur Bootswerft an den Docks und klau das schnellste Boot. + +[COK3_9:BARON3] +~g~Jetzt steig in das Boot. + +{=================================== MISSION TABLE BARON4 ===================================} + +[COK4_A:BARON4] +Eject! PLASTIKMÜLL! + +[COK4_B:BARON4] +Wie kannst du mir das antun? + +[COK4_C:BARON4] +Was bildest du dir ein, du mieses Stück Plastikdreck! Aaargh! + +[COK4_D:BARON4] +VERDAMMT! + +[COK4_E:BARON4] +Wenn das Ding meinen Lieblings-El-Burro-Film frisst, ist Sense! + +[COK4_F:BARON4] +Was kann ich noch tun? + +[COK4_G:BARON4] +Ist wahrscheinlich nicht eingesteckt. + +[COK4_H:BARON4] +Was? + +[COK4_I:BARON4] +Verdammt. Egal, ich kann mir noch 100 davon kaufen. + +[COK4_J:BARON4] +Also, Tommy, + +[COK4_K:BARON4] +jeden Monat legt ein 'Freiberufler' mit seiner Jacht in Vice City an. + +[COK4_L:BARON4] +Er verkauft seine Fracht an das erstbeste Schiff. + +[COK4_M:BARON4] +Du schnappst dir das Rennboot, + +[COK4_N:BARON4] +und bist vor allen anderen Pennern bei ihm. + +[COK4_O:BARON4] +Dann bringst du die Fracht hierher, ok? + +[COK4_P:BARON4] +Lass mich raten. Du dachtest, ich brauche einen Schutzengel. + +[COK4_Q:BARON4] +Ich denke nur, du solltest mich mitnehmen, Mann. + +[COK4_R:BARON4] +Du kannst lange den einsamen Macho-Helden markieren, + +[COK4_S:BARON4] +aber eines Tages werde ich dir den Hintern retten + +[COK4_T:BARON4] +und du wirst mich wahrscheinlich dafür küssen wollen. + +[COK4_U:BARON4] +Spinner. + +[COK4_V:BARON4] +Hahahaha! + +[COK4_1:BARON4] +Tommy, wir wissen, es war Diaz, der unseren Deal platzen ließ. + +[COK4_3:BARON4] +Wieso spielen wir dann die Laufburschen für ihn? + +[COK4_4:BARON4] +Je mehr wir jetzt lernen, desto weniger müssen wir lernen, wenn wir diese Stadt übernehmen! + +[COK4_5:BARON4] +Dein Stil gefällt mir. Echt erfrischend. + +[COK4_12:BARON4] +Pass auf, die kommen von überall + +[COK4_13:BARON4] +Erwischt! Fahr zu Diaz so schnell du kannst! + +[COK4_14:BARON4] +Willst du was abhaben? + +[COK4_15:BARON4] +Schönen Gruß an die Fische! + +[COK4_16:BARON4] +Nimm das! Und das! + +[COK4_19:BARON4] +Gleich kommt's noch dicker! + +[COK4_20:BARON4] +Da sind Schützen auf dem Pier! + +[COK4_24:BARON4] +Gut geschossen, mein Freund. Du bist ein erstklassiger Psycho. + +[COK4_25:BARON4] +Danke sehr. + +[COK4_26:BARON4] +Wir sehen uns, Tommy. + +[COK4_27:BARON4] +Ok, Mr. Lance Vance Dance. + +[COK4_28:BARON4] +~g~Sei vor den anderen Booten bei der Jacht! + +[COK4_31:BARON4] +~g~Begib dich zum schnellsten Boot auf dem Pier! + +[COK4_32:BARON4] +~r~Zu langsam! + +[COK4_33:BARON4] +~r~Du hast das Boot zerstört! + +[COK4_34:BARON4] +~g~Zieh diese Boote aus dem Verkehr! + +[COK4_35:BARON4] +Zustand Boot: + +{=================================== MISSION TABLE BARON5 ===================================} + +[PROP_A:BARON5] +OBJEKT ERWORBEN! + +[COK4_30:BARON5] +~r~Lance ist erledigt! + +[ASS1_A:BARON5] +Im Kofferraum sind ein paar Kanonen. + +[ASS1_B:BARON5] +Wahnsinn! Wo hast du denn die alle her? + +[ASS1_C:BARON5] +Hab ich für schlechte Zeiten zurückgelegt. + +[ASS1_D:BARON5] +Zufrieden? + +[ASS1_E:BARON5] +Ja. Und wie! + +[ASS1_F:BARON5] +Ihr dämlichen Idioten! + +[ASS1_G:BARON5] +Mein schönes Haus + +[ASS1_H:BARON5] +Seht euch an, was ihr angestellt habt! + +[ASS1_I:BARON5] +Das ist für meinen Bruder! + +[ASS1_J:BARON5] +Ich habe dir vertraut, Tommy. + +[ASS1_K:BARON5] +Ich hätte was aus dir gemacht... + +[ASS1_L:BARON5] +Gute Nacht, Mr. Diaz. + +[ASS1_1:BARON5] +Bald wimmelt's hier von Arschlöchern. Sei vorsichtig. + +[ASS1_2:BARON5] +Keine Panik, Tommy, ich geb dir Deckung. + +[ASS1_13:BARON5] +DIAZ?! Ich bin hier, um deinen Laden zu übernehmen! + +[ASS1_14:BARON5] +TOMMY! Du Verräter...Du Idiot! Dich mache ich fix und fertig... + +[ASS1_16:BARON5] +~g~Erledige Diaz! + +[BUD1:BARON5] +Lance + +[ASS1_18:BARON5] +~g~Die Tür ist verschlossen. Versuche es auf einem anderen Weg. + +[ASS1_19:BARON5] +Da lang! + +[ASS1_20:BARON5] +Tommy, ich hab ein Problem mit Quentin, nicht mit dir, Mann! + +{=================================== MISSION TABLE BIKE1 ===================================} + +[BM1_A:BIKE1] +Wo ist Baker? + +[BM1_B:BIKE1] +Ich suche Big Mitch Baker... + +[BM1_C:BIKE1] +Wer sucht ihn? + +[BM1_D:BIKE1] +Tommy Vercetti. + +[BM1_E:BIKE1] +Vercetti. + +[BM1_F:BIKE1] +Du siehst nicht wie ein Bulle aus, das heißt, du hast 1 Minute. + +[BM1_G:BIKE1] +Also drück auf die Tube. + +[BM1_H:BIKE1] +Kent Paul sagt, ihr wärt interessiert, die Security für einen Gig zu übernehmen, den er plant. + +[BM1_I:BIKE1] +Kent Paul? Pfff! Kein Wunder, dass er dich schickt. + +[BM1_J:BIKE1] +Als er das letzte Mal hier war, ist er durch Fenster wieder gegangen - und zwar splitternackt. + +[BM1_K:BIKE1] +Seid ihr nun interessiert oder nicht? + +[BM1_L:BIKE1] +Gefälligkeiten gibt's nur für Mitglieder. + +[BM1_M:BIKE1] +Wie kann ich beitreten? + +[BM1_N:BIKE1] +Wir sind hier kein Golfklub, Kleiner. Kannst du 'n Bike fahren? + +[BM1_O:BIKE1] +Kannst du auf 'm Barhocker sitzen und saufen? + +[BM1_P:BIKE1] +Cougar, Zeppelin, checkt mal ab, wie diese Sissy hier fährt. + +[BM1_2:BIKE1] +~g~Du brauchst eine Freeway oder eine Angel, um mitzumachen! + +[BM1_3:BIKE1] +~r~Die Fahrer wurden angegriffen! + +[BIKE1_1:BIKE1] +Ok, Schickimickibürschchen, dann zeig mal, was du drauf hast. + +[BM1_1:BIKE1] +~g~Schnapp dir eine Freeway oder eine Angel und geh in Startposition. + +{=================================== MISSION TABLE BIKE2 ===================================} + +[BM2_2:BIKE2] +~g~Du musst den Chaosmesser in der vorgegebenen Zeit vollmachen, um uns zu zeigen, aus welchen Holz du bist! + +[BM2_4:BIKE2] +~r~Du hast den Chaosmesser nicht rechtzeitig vollgemacht! + +[BM2_1:BIKE2] +CHAOSMESSER: + +[BM2_A:BIKE2] +Ha, ha, ha, hab dich wieder erwischt. + +[BM2_B:BIKE2] +Hey, Vercetti. + +[BM2_C:BIKE2] +Cougar meint, du fährst ziemlich gut. + +[BM2_D:BIKE2] +Ja, wie lange soll ich noch hier rumgurken? + +[BM2_E:BIKE2] +Ich bin ein sehr beschäftigter Mann. + +[BM2_F:BIKE2] +Wenn ich mich kloppen soll, damit das klar geht, dann los. + +[BM2_G:BIKE2] +Es geht bei uns nicht nur ums dreinschlagen. Man muss zur Familie gehören. + +[BM2_H:BIKE2] +Ja, ich hab schon mal zu einer Familie gehört. Hat nicht funktioniert. + +[BM2_I:BIKE2] +Klar. Aber unsere Familie kümmert sich um ihre Leute. + +[BM2_J:BIKE2] +Wir verlangen nicht, dass einer die Drecksarbeit macht und danach 15 Jahre einsitzt. + +[BM2_K:BIKE2] +Ja, ganz recht. Ich hab meine Hausaufgaben gemacht. + +[BM2_L:BIKE2] +Das hier ist die größte Familie von Außenseitern, Outlaws und Unruhestiftern. + +[BM2_M:BIKE2] +Ein paar von uns wurden sogar von ihrem eigenen Land verraten. + +[BM2_N:BIKE2] +Während des Vietnamkriegs war ich eingelocht. Miese Sache. + +[BM2_O:BIKE2] +Drum sollst du denen ja zeigen, was Sache ist. + +[BM2_P:BIKE2] +Dieses ganze verdammte Land braucht einen Arschtritt, und wir geben ihm diesen Tritt. + +[BM2_Q:BIKE2] +Also los, schnapp dir ein Bike und zeig der Stadt, wie sauer du bist. + +[BM2_R:BIKE2] +Alles klar. + +[BM2_3:BIKE2] +~g~Dieser Ton zeigt an, dass du einen Teil gefüllt hast. Mach ihn immer voller. + +{=================================== MISSION TABLE BIKE3 ===================================} + +[BM3_A:BIKE3] +Hi, Mitch. + +[BM3_B:BIKE3] +Ah, sieh an, 'Outlaw' Vercetti. + +[BM3_C:BIKE3] +Jetzt will ich sehen, wie du für deine Kumpels kämpfst. + +[BM3_D:BIKE3] +Eine Straßengang von hier hat den Fehler gemacht, meinen Hobel zu klauen. + +[BM3_E:BIKE3] +Wollten wahrscheinlich zeigen, was für coole Machos sie sind. + +[BM3_F:BIKE3] +Ich und die Jungs wollten ihnen eigentlich ein bisschen Respekt einbläuen. + +[BM3_G:BIKE3] +Aber- + +[BM3_H:BIKE3] +-dann dachte ich mir, das wäre doch ein guter Test für dich. + +[BM3_I:BIKE3] +Bring mir meine Maschine zurück und Paul kriegt seine Security. + +[BM3_2:BIKE3] +~r~Du solltest die Maschine zurückbringen, nicht schrotten! + +[BM3_3:BIKE3] +~g~Bring die Maschine zur Bar! + +[BM3_4:BIKE3] +~g~Setz dich auf die Maschine! + +[INTRUDE:BIKE3] +~g~Man hat dich bemerkt! + +[BM3_6:BIKE3] +~g~Sie sind Downtown hinter dem Ammu-Nation. + +[BM3_7:BIKE3] +~g~Du brauchst eine schnelle Maschine, um auf das Dach zu gelangen. + +[BM3_8:BIKE3] +~g~Spring mit dem Bike von diesen Stufen auf das Dach auf der anderen Seite der Straße. + +[BM3_1:BIKE3] +~g~Eine Gang hat Mitch Bakers Maschine geklaut. Beschaff sie ihm wieder! + +[BM3_9:BIKE3] +~g~Schnapp dir Mitchs Maschine und mach dich aus dem Staub! + +{=================================== MISSION TABLE BMX_1 ===================================} + +[GETBIK2:BMX_1] +Du hast ~1~ Sekunden, um auf ein Geländemotorrad zu steigen! + +{=================================== MISSION TABLE BOATBUY ===================================} + +[DRUG_1:BOATBUY] +Hallo? Hal-lo?! Hallo? + +[DRUG_13:BOATBUY] +Meister? + +[DRUG_2:BOATBUY] +Mach aus. Da kommt einer. + +[DRUG_3:BOATBUY] +Tag, Herr Anzugmensch. Sie müssen der neue Eigentümer sein. + +[DRUG_4:BOATBUY] +Ja. Welches von den Booten ist das schnellste? + +[DRUG_5:BOATBUY] +Das ist schon im Wasser, Meister. + +[DRUG_6:BOATBUY] +Dachte mir, dass Sie es mal testen wollen. + +[DRUG_7:BOATBUY] +Das Ding hat jetzt schon 300 PS unter der Haube, Meister. + +[DRUG_8:BOATBUY] +Und mit dem Fiberglas-Rumpf - das Ding pfeift nur so durch die Wellen! + +[DRUG_9:BOATBUY] +In vier Sekunden von null auf sechzig Meilen, Meister. + +[DRUG_10:BOATBUY] +Und im Rumpf kriegen Sie so 20 Ladungen vom besten jamaikanischen Gras unter. + +[DRUG_11:BOATBUY] +Nur zu, Chef, machen Sie 'ne Spritztour! + +[DRUG_12:BOATBUY] +Hey Anzugmensch, haben Sie mal Feuer? + +{=================================== MISSION TABLE CAP_1 ===================================} + +[CAP1_B1:CAP_1] +~g~Die Mafia verlangt Schutzgeld von dir. Stöbere sie auf und erledige sie. + +[CAP1_B2:CAP_1] +~g~Die Mafia verlangt von dir Schutzgeld für die Bootswerft! + +[CAP1_B3:CAP_1] +~g~Die Mafia verlangt von dir Schutzgeld für die Eiscremefabrik! + +[CAP1_B4:CAP_1] +~g~Die Mafia verlangt von dir Schutzgeld für das Autohaus! + +[CAP1_B5:CAP_1] +~g~Die Mafia verlangt von dir Schutzgeld für das Taxiunternehmen! + +[CAP_01:CAP_1] +Ok, wo brennt's denn? + +[CAP_02:CAP_1] +Wer war das? + +[CAP_03:CAP_1] +Tommy... ein paar Mafia-Typen... sie wollen wiederkommen, ihren Anteil abkassieren. + +[CAP_04:CAP_1] +Ein gewisser Mr. Forello hätte dir Geld gegeben. Mir geht's schlecht. + +[CAP_05:CAP_1] +Forelli? SONNY Forelli? + +[CAP_06:CAP_1] +Ja, so heißt er, glaube ich... ich konnte nichts gegen sie ausrichten. + +[CAP_07:CAP_1] +Keine Angst, mein Freund, ich mach dir keine Vorwürfe. + +[CAP_08:CAP_1] +Bringt ihn ins Krankenhaus. + +[CAP_09:CAP_1] +Tommy... mach den Kerl 'nen Kopf kürzer für mich... + +[CAP_10:CAP_1] +Ich mach ihn zwei Köpfe kürzer... + +[CAP1_2:CAP_1] +Du kennst die Regeln, Vercetti! + +[CAP1_3:CAP_1] +Mr. Forelli bestellt schöne Grüße! + +[CAP1_4:CAP_1] +Das ist der Harwood Butcher! + +[CAP1_5:CAP_1] +Sag Sonny, er soll mir vom Leib bleiben! + +[CAP1_6:CAP_1] +Vice City gehört nicht mehr ihm, sondern MIR! + +[CAP1_7:CAP_1] +Du glaubst, du kannst mich vom Thron stürzen, Vercetti? + +[CAP1_8:CAP_1] +Wir jagen dich bis ans Ende deiner Tage, Vercetti. + +[CAP1_9:CAP_1] +Du hast keine Chance, du irrer Idiot! + +[CAP1_10:CAP_1] +Ich mach dich fertig, Vercetti. + +[CAP1_11:CAP_1] +Du warst schon immer ein Schwachkopf. + +[CAP1_12:CAP_1] +Du wirst dran glauben, Vercetti. + +[CAP1_B6:CAP_1] +~g~Du hast den Eintreiber gefunden, erledige ihn. + +[CAP1_B7:CAP_1] +~g~Du hast den Eintreiber verloren. + +[CAP1_B8:CAP_1] +~r~Der Eintreiber verlangt für all deine Geschäfte Schutzgeld. + +[CAP1_B9:CAP_1] +~g~Die Mafia verlangt Schutzgeld für das Malibu! + +[CAP1_B0:CAP_1] +~g~Die Mafia verlangt Schutzgeld für das Filmstudio! + +[CAP1_C2:CAP_1] +~g~Die Mafia ist bei der Bootswerft angekommen! + +[CAP1_C3:CAP_1] +~g~Die Mafia ist bei der Eiscremefabrik angekommen! + +[CAP1_C4:CAP_1] +~g~Die Mafia ist bei dem Autohaus angekommen! + +[CAP1_C5:CAP_1] +~g~Die Mafia ist bei dem Taxiunternehmen angekommen! + +[CAP1_C9:CAP_1] +~g~Die Mafia ist im Malibu Club angekommen! + +[CAP1_C0:CAP_1] +~g~Die Mafia ist im Filmstudio angekommen! + +[CAP1_D2:CAP_1] +~g~Die Mafia verlässt die Bootswerft! + +[CAP1_D3:CAP_1] +~g~Die Mafia verlässt die Eiscremefabrik! + +[CAP1_D4:CAP_1] +~g~Die Mafia verlässt das Autohaus! + +[CAP1_D5:CAP_1] +~g~Die Mafia verlässt das Taxiunternehmen! + +[CAP1_D9:CAP_1] +~g~Die Mafia verlässt den Malibu Club! + +[CAP1_D0:CAP_1] +~g~Die Mafia verlässt das Filmstudio! + +[CAP1B10:CAP_1] +Du hast die Eintreiber erledigt. Es kommen weitere. + +{=================================== MISSION TABLE CARBUY ===================================} + +[CAR1_1:CARBUY] +B.J. Smith. Und Sie müssen Mr. Vercetti sein. + +[CAR1_2:CARBUY] +Soll ich Sie herumführen? + +[CAR1_3:CARBUY] +Warum nicht? + +[CAR1_4:CARBUY] +Tja, ich verkaufe das Autohaus ja eigentlich nur ungern. + +[CAR1_5:CARBUY] +War meine erste Investition, nachdem ich Football-Profi wurde. + +[CAR1_6:CARBUY] +Aber es wird Zeit für eine Luftveränderung. + +[CAR1_7:CARBUY] +Sie verlassen die Stadt? + +[CAR1_8:CARBUY] +Nicht in allzu großer Eile, hoffe ich doch? + +[CAR1_9:CARBUY] +Nein, ich bereite mich nur auf mein Comeback als Football-Profi vor. Ich hatte schon aufgehört. + +[CAR1_10:CARBUY] +Das Geschäft lief nicht allzu gut. + +[CAR1_11:CARBUY] +Da haben sich meine Angestellten was einfallen lassen, + +[CAR1_12:CARBUY] +um ein bisschen was 'nebenbei' zu erwirtschaften. + +[CAR1_13:CARBUY] +Ich könnte den Laden auch dichtmachen, bevor ich ihn Ihnen verkaufe. + +[CAR1_14:CARBUY] +Ich könnte die Bude bei Bedarf sogar abfackeln. + +[CAR1_15:CARBUY] +Das ist erstklassiger Baugrund hier. + +[CAR1_16:CARBUY] +Machen Sie sich mal keine Gedanken. + +[CAR1_17:CARBUY] +Der Laden ist genau das, was ich brauche. + +[CAR1_18:CARBUY] +Ja. Dann kommen wir also ins Geschäft? + +{=================================== MISSION TABLE CARPAR1 ===================================} + +[MM_1_A:CARPAR1] +~g~Passiere ~y~5 checkpoints~r~ OHNE irgendwelche ~r~Hütchen ~g~umzufahren! ~g~Die ~y~REIHENFOLGE IST BELIEBIG. + +[CONE_1:CARPAR1] +~r~Du hast ein Hütchen umgefahren!! + +[MM_1_C:CARPAR1] +~y~PASSIERE~g~ einen Checkpoint, dann läuft die Zeit. ~g~Jeder Checkpoint bringt dir ~y~~1~ SEKUNDEN~g~. + +{=================================== MISSION TABLE COPCAR ===================================} + +[C_COMP1:COPCAR] +Bürgerwehr-Mission Level 12 beendet: Deine max. Panzerung erhöht sich auf 150 + +[CLEVEL:COPCAR] +Bürgerwehr-Mission Level ~1~ + +[C_PASS:COPCAR] +BEDROHUNG AUSGERÄUMT: $ ~1~ + +[KILLS:COPCAR] +HITS: + +[C_BREIF:COPCAR] +~g~Verdächtiger wurde zuletzt in der Gegend von ~a~ gesichtet. + +[COPCART:COPCAR] +~g~Du hast ~1~ Sekunden, um zu einem Polizeifahrzeug zurückzukehren, bevor die Mission endet. + +[C_CANC:COPCAR] +~r~Bürgerwehr-Mission abgebrochen! + +[C_TIME:COPCAR] +~r~Deine Zeit als Gesetzeshüter ist vorbei! + +{=================================== MISSION TABLE COUNT1 ===================================} + +[CM1_A:COUNT1] +Mr.Vercetti? Hey. Sie haben die alte Druckerei gekauft? + +[CM1_B:COUNT1] +Ja, mein Vater hat an diesen Dingern gearbeitet. + +[CM1_C:COUNT1] +Ich sollte in seine Fußstapfen treten, aber...es ist anders gekommen. + +[CM1_D:COUNT1] +Wollen Sie die alten Maschinen verkaufen, das Werk abreißen? + +[CM1_E:COUNT1] +Ich denke nach, ob wir was drucken sollten. Eine Zeitung, ein Magazin... + +[CM1_F:COUNT1] +Ach, Blödsinn, absoluter Blödsinn. Ich drucke lieber Geld. Ist gar nicht so schwer. + +[CM1_G:COUNT1] +In kleinerem Umfang mache ich das schon seit Jahren. + +[CM1_H:COUNT1] +Tatsächlich? + +[CM1_I:COUNT1] +Klar. Aber wir bräuchten gute Platten. + +[CM1_J:COUNT1] +Natürlich! In Florida gibt es schon ein Geldfälscher-Syndikat. + +[CM1_K:COUNT1] +Ein Syndikat? + +[CM1_L:COUNT1] +Ja. Hab aber nur gerüchteweise davon gehört. + +[CM1_M:COUNT1] +Ich kenne einen, der kennt sich mit Gerüchten aus... + +[CM1_N:COUNT1] +Hab immer die Abende mit ihm verbracht, die Walzen reinigen. + +[CM1_2A:COUNT1] +Sieh dir diesen Hintern an! + +[CM1_2B:COUNT1] +Tja, Kleine, du weißt nicht, was dir entgeht! + +[CM1_2C:COUNT1] +Na, alter Freund, wie läuft's so? + +[CM1_2D:COUNT1] +Was weißt du über Geldfälscherei? + +[CM1_2E:COUNT1] +'Oh, alles bestens, Paul, und bei dir?' + +[CM1_2F:COUNT1] +Komm her! + +[CM1_2G:COUNT1] +Ist ja gut, ist ja gut. Anscheinend bist du schwer beschäftigt. + +[CM1_2H:COUNT1] +Über Falschgeld weiß ich nur, dass die Triaden die Platten liefern. + +[CM1_2I:COUNT1] +Die haben eine Reederei unten bei den Docks. + +[CM1_2J:COUNT1] +Der Boss weiß, wann die nächsten Platten reinkommen. + +[CM1_2K:COUNT1] +Danke, Paul. + +[CM1_2L:COUNT1] +Was ist bloß los mit dir, du Wahnsinniger! + +[CM1_2M:COUNT1] +Gib mir noch einen Drink, hopp! + +[CM1_3:COUNT1] +~g~Man hat dich bemerkt! + +[CM1_5:COUNT1] +~g~Begib dich zu Kent Paul in den Malibu Club! + +[CNT1_1:COUNT1] +Wer sind Sie? Uff! Aiiee! Nicht ins Gesicht! Nicht ins Gesicht! + +[CM1_1:COUNT1] +~g~Begib dich zum Schiff der Chartered Libertine Lines bei den Docks. + +[CM1_2:COUNT1] +~g~Der Reedereichef verfügt über die benötigten Informationen. + +[CNT1_2:COUNT1] +Ok, ich rede ja! Ich rede! + +[CM1_6:COUNT1] +~g~Begib dich mit den Informationen zurück in die Druckerei! + +{=================================== MISSION TABLE COUNT2 ===================================} + +[CNT2_B1:COUNT2] +Ok, der Kurier holt die Druckplatten heute vom Hafen ab. + +[CNT2_B2:COUNT2] +Ich fange ihn ab, schnapp mir die Platten, häng die Cops ab und komm wieder hierher. + +[CNT2_B3:COUNT2] +Also, je nachdem wie es läuft, haben wir + +[CNT2_B4:COUNT2] +entweder 5 Minuten zum Gelddrucken, bis das Fälschersyndikat uns findet, oder wir haben alle Zeit der Welt. + +[CNT2_B5:COUNT2] +Jedenfalls will ich 5 Minuten nachdem ich hier bin, Scheinchen aus der Presse kommen sehen! + +[CNT2_B6:COUNT2] +Keine Sorge, Tommy. Wir sind bereit. + +[CNT2_B7:COUNT2] +Ich und die Jungs bleiben in der Nähe, falls die Cops dir in die Quere kommen. + +[CNT2_B8:COUNT2] +Ok. Jeder weiß, was er zu tun hat? Gut. Bis später. + +[CNT2_01:COUNT2] +~g~Der ~r~Kurier~g~ mit den Druckplatten kommt jeden Moment in einem Helikopter an den ~y~Docks~g~ an. + +[CNT2_02:COUNT2] +~r~Der Kurier mit den Platten ist im Helikopter geflohen. + +[CNT2_03:COUNT2] +~r~Der Kurier ist an seinem Ziel angekommen. Du kommst zu spät! + +[CNT2_04:COUNT2] +~r~Du hast die Platten in der Explosion zerstört! + +[CNT2_05:COUNT2] +~g~Du hast die Druckplatten. Bring sie in die Druckerei. + +[CNT2_06:COUNT2] +~g~Der Kurier ist hinüber. Hol dir die Platten, ehe dir jemand zuvorkommt. + +[CNT2_07:COUNT2] +~g~Eine der Wachen hat die Platten genommen. Lass ihn nicht entkommen! + +[CNT2_08:COUNT2] +~g~Der ~r~Kurier~g~ mit den Platten ist an den Docks eingetroffen. + +[CNT2_4:COUNT2] +Privatangelegenheit. Du hast hier nichts verloren! + +[CNT2_09:COUNT2] +DRUCKEREI ERWORBEN + +[CNT2_10:COUNT2] +~g~Die Druckerei generiert nun bis zu $~1~ Einkünfte. Hol dir das Geld regelmäßig ab. + +[CNT2_11:COUNT2] +~r~Die Platten liegen auf dem Meeresgrund! + +{=================================== MISSION TABLE CUBAN1 ===================================} + +[CUB1_A:CUBAN1] +Was ist, Mann? + +[CUB1_B:CUBAN1] +Hey, langsam, Paps, der ist für mich. Bist du der Bursche? + +[CUB1_C:CUBAN1] +Oh ja, du bist der Bursche. Glaub schon. + +[CUB1_D:CUBAN1] +Nein, glaub ich nicht. + +[CUB1_E:CUBAN1] +Ach ja? Komm her du Großmaul. + +[CUB1_F:CUBAN1] +Meinst du, du kannst es mit mir aufnehmen? + +[CUB1_G:CUBAN1] +Meinst du, du kannst mich für blöd verkaufen? + +[CUB1_H:CUBAN1] +Nein, ich glaube, deine Blödheit reicht für uns beide. + +[CUB1_I:CUBAN1] +Hey, er sagt, du bist dumm, mein Sohn. + +[CUB1_J:CUBAN1] +Und ich sage, er ist ein kleines Mädchen, Paps. + +[CUB1_K:CUBAN1] +Sieh dir doch an, wie er angezogen ist. + +[CUB1_L:CUBAN1] +Was ist los? Ist heute Weiberabend? + +[CUB1_M:CUBAN1] +Du willst ein harter Kerl sein und ziehst dich an wie ein Weib? + +[CUB1_N:CUBAN1] +Hast du auch ein Höschen an, oder was? + +[CUB1_O:CUBAN1] +Was hast du gegen Frauen? Ziehst du Männer vor? + +[CUB1_P:CUBAN1] +Ich liebe Frauen! Ich liebe alle Frauen! Ich liebe meine Mutter, Chico. + +[CUB1_Q:CUBAN1] +Ok, ok, ich glaub's dir ja. + +[CUB1_R:CUBAN1] +Kannst du fahren, Amigo? + +[CUB1_S:CUBAN1] +Ja...wie eine Frau. + +[CUB1_T:CUBAN1] +Sehr witzig. Du gefällst mir, Großer. Vielleicht kannst du mir helfen. + +[CUB1_U:CUBAN1] +Vielleicht kannst du beweisen, dass du ein Mann bist. Hah? + +[CUB1_V:CUBAN1] +Schnapp dir das Boot. + +[CUB1_W:CUBAN1] +Zeig mir, dass du ein ganzer Kerl bist, + +[CUB1_X:CUBAN1] +und kein kleines Mädchen. + +[CUB1_02:CUBAN1] +Ok Mann, behandle es wie eine Frau. + +[CUB1_03:CUBAN1] +Nicht schlecht, du bist ein echter Mann. + +[CUB1_04:CUBAN1] +Mann, du bist ein ganzer Kerl, Amigo. + +[CUB1_05:CUBAN1] +Amigo, du bist ein Mann, Mann. + +[CUB1_06:CUBAN1] +Du willst ein Mann sein, Mann? + +[CUB1_07:CUBAN1] +Du bist ein feiges Würstchen, Kleiner, heul dich bei Mammi aus. + +[CUB1_08:CUBAN1] +Du bist zu nichts zu gebrauchen. Macht einen auf harter Mann, aber fährt wie ein Idiot. + +[CUB1_09:CUBAN1] +Mann, du bist der Hammer, Mann. Du gefällst mir. Du gefällst mir sehr. + +[CUB1_10:CUBAN1] +Du bist gut, Mann. Weil du ein ganzer Kerl bist. Alle meine Freunde sind ganze Kerle. + +[CUB1_11:CUBAN1] +Du hast Rico erledigt! + +[CUB1_12:CUBAN1] +Fahre durch den ersten Checkpoint, um den Test zu beginnen. + +[CUB1_14:CUBAN1] +Steig wieder ins Boot! + +[CUB1_15:CUBAN1] +Du bist zu langsam, Mann. + +[CUB1_01:CUBAN1] +Hi, ich bin Rico. Bist du der 'ganze Kerl'? + +[CUB1_13:CUBAN1] +~g~Du hast drei Minuten, um den Rundkurs zu schaffen. + +{=================================== MISSION TABLE CUBAN2 ===================================} + +[CUB2_25:CUBAN2] +ERLEDIGE ALLE HAITIANER!! + +[CUB2_M:CUBAN2] +Wer sich mit mir anlegt, hat sich den stärksten der Stadt rausgesucht! + +[CUB2_A:CUBAN2] +Eine Kaffee, bitte, Alberto. + +[CUB2_N:CUBAN2] +Kein Problem, Tommy. + +[CUB2_B:CUBAN2] +Paps! Es gibt ein Riesenproblem! + +[CUB2_O:CUBAN2] +Umberto, mein Sohn, was ist passiert? + +[CUB2_C:CUBAN2] +Die Haitianer! Ich hasse die Haitianer! + +[CUB2_D:CUBAN2] +Die haben mir das letzte Mal ans Bein gepinkelt! + +[CUB2_E:CUBAN2] +Diese Haitianer. Wir machen sie fertig! + +[CUB2_F:CUBAN2] +Aber dazu brauchen wir Hilfe. + +[CUB2_G:CUBAN2] +Ich hab dabei schon ein paar Brüder verloren. + +[CUB2_H:CUBAN2] +Amigo, du fährst gut! + +[CUB2_I:CUBAN2] +Für eine Frau, was? + +[CUB2_J:CUBAN2] +Jetzt ist nicht die Zeit für Witze! + +[CUB2_K:CUBAN2] +Komm, fahr noch mal für mich! + +[CUB2_L:CUBAN2] +Bring meine Jungs da rüber, und dann erledigen wir diese Haitianer! + +[CUB2_01:CUBAN2] +Da ist nicht genug Platz, Mann, du brauchst ein größeres Auto. + +[CUB2_02:CUBAN2] +Wir brauchen Verstärkung aus dem Café! + +[CUB2_03:CUBAN2] +~g~Besorg dir ein Auto und hole die Kubaner vor Robinas Café ab. + +[CUB2_04:CUBAN2] +~g~Setze die Kubaner am Kampfort ab. + +[CUB2_05:CUBAN2] +Schalte den feigen Heckenschützen aus! + +[CUB2_07:CUBAN2] +Die kämpfen wie Weiber! In Deckung! + +[CUB2_09:CUBAN2] +Heckenschütze auf dem Dach! + +[CUB2_11:CUBAN2] +~r~Du Idiot! Das Auto hätten wir gebraucht! + +[CUB2_12:CUBAN2] +Hey, Amigo! Schön zu sehen, dass du's geschafft hast! + +[CUB2_13:CUBAN2] +Stinkendes Haitianer-Nest. Das werden wir komplett ausheben! + +[CUB2_14:CUBAN2] +ANGRIFF! + +[CUB2_15:CUBAN2] +Los, meine Brüder, ANGRIFF!! + +[CUB2_16:CUBAN2] +Tommy, wir haben unseren mannhaften Mut bewiesen! + +[CUB2_17:CUBAN2] +Lass uns diesen Wagen voller Stoff nehmen und abhauen! + +[CUB2_18:CUBAN2] +~g~Besorge dir ein Auto und hole die Kubaner ab. + +[CUB2_19:CUBAN2] +Wir werden kämpfen wie Männer! + +[CUB2_21:CUBAN2] +Kämpfen wie ganze Kerle! + +[CUB2_22:CUBAN2] +~g~Schalte die restlichen Haitianer aus, damit die Kubaner vorrücken können. + +[CUB2_23:CUBAN2] +~g~In Little Haiti wird es von Haitianern wimmeln, die sich an den Kubanern rächen wollen. Sei vorsichtig. + +[CUB2_24:CUBAN2] +~g~Kehre mit dem Van zu Robinas Café zurück und parke hinter dem Haus. + +{=================================== MISSION TABLE CUBAN3 ===================================} + +[CUB3_A:CUBAN3] +Alberto? Einen Kaffee, Senor? + +[CUB3_B:CUBAN3] +Paps, gib dieser falschen Schlange nichts. + +[CUB3_C:CUBAN3] +Du hast zwei Gesichter, Tommy! + +[CUB3_D:CUBAN3] +Du hast entweder zwei Gesichter oder du bist ein Feigling, Kleiner! + +[CUB3_E:CUBAN3] +Die Haitianer, Mann, die lachen mich aus. + +[CUB3_F:CUBAN3] +Ruhig, ruhig. Was gibt es für Probleme? + +[CUB3_G:CUBAN3] +Sie lachen mich aus, Tommy. Mich! + +[CUB3_H:CUBAN3] +Umberto Robina. Die tun, was sie wollen! + +[CUB3_I:CUBAN3] +Die tun nicht, was sie wollen, Umberto. Sie tun, was du sie tun lässt. + +[CUB3_J:CUBAN3] +Was? + +[CUB3_K:CUBAN3] +Soll jemand aus dem Weg geräumt werden? + +[CUB3_L:CUBAN3] +Ich kann das machen, aber es kostet. + +[CUB3_M:CUBAN3] +Ich weiß, wir sind Brüder und alles, aber hier geht's um ein Geschäft. + +[CUB3_N:CUBAN3] +Tommy. Du bist ein echter Mann. Ein Geschäftsmann, ein Gentleman. + +[CUB3_O:CUBAN3] +Die Haitianer, sie erwarten eine Schiffladung Stoff, richtig gutes Zeug. + +[CUB3_P:CUBAN3] +Wir schnappen es uns und erledigen sie. + +[CUB3_Q:CUBAN3] +Du schnappst es dir, ich passe auf dich auf. Wie auf einen Bruder, einen Sohn. + +[CUB3_R:CUBAN3] +Ich glaube, Cash ist mir lieber als auf deinen Knien zu reiten, Amigo. + +[CUB3_01:CUBAN3] +Hey, Rico. Nettes Boot. Bist du bereit? + +[CUB3_03:CUBAN3] +~g~Sammle alle Aktenkoffer mit Stoff und Geld ein. + +[CUB3_04:CUBAN3] +~g~Bringe die Drogen und das Geld zu Umberto. + +[CUB3_05:CUBAN3] +Ja, Tommy. Also, sei ein guter Kapitän heute. + +[CUB3_06:CUBAN3] +Mein Boot nützt mir nichts, wenn es durchlöchert ist, ok? + +[CUB3_07:CUBAN3] +~g~Begib dich zu Rico. Er fährt dich zum Treffpunkt. + +[CUB3_02:CUBAN3] +~g~ERLEDIGE ALLE HAITIANER AUF DEN BOOTEN!! + +[CUB3_08:CUBAN3] +Oh-oh... eine Bande Kubaner. Wir werden angegriffen! + +{=================================== MISSION TABLE CUBAN4 ===================================} + +[CUB4_A:CUBAN4] +Hey, Ladies. Wisst ihr, was ich mache? + +[CUB4_B:CUBAN4] +Ich erledige einen Haitianer. Und dann? + +[CUB4_C:CUBAN4] +Dann mache ich Liebe wie ein Mann. + +[CUB4_D:CUBAN4] +Weißt du, Kleine? So in der Art. + +[CUB4_E:CUBAN4] +Penner! + +[CUB4_F:CUBAN4] +Vollidiot. + +[CUB4_G:CUBAN4] +Hey, Baby, dich würde ich nicht mit 'ner Kneifzange anfassen! + +[CUB4_H:CUBAN4] +Umberto Robina mag Frauen, keine Ziege mit Rock! + +[CUB4_I:CUBAN4] +Tommy!! Tommy, ich liebe dich, ich liebe dich! Lass uns gehen! + +[CUB4_J:CUBAN4] +Wohin denn? Kann ich nicht noch einen Kaffee trinken? + +[CUB4_K:CUBAN4] +Keine Zeit! Außerdem habe ich erst einen getrunken. + +[CUB4_L:CUBAN4] +Wir nehmen uns die Haitianer vor. + +[CUB4_M:CUBAN4] +Tommy, wie erledigt man eine Schlange? + +[CUB4_N:CUBAN4] +Man beißt sie in den Hintern! Hahaha! + +[CUB4_O:CUBAN4] +Wenn du es sagst, Umberto. + +[CUB4_P:CUBAN4] +Tommy, geh und besorge uns ein kleines haitianisches Auto. + +[CUB4_Q:CUBAN4] +Wenn du es hast, komm zurück und hol meinen Jungen ab + +[CUB4_R:CUBAN4] +Pepe, und fahre ihn zu den Haitianern. + +[CUB4_S:CUBAN4] +Dann begibst du dich zur Laboranlage der Haitianer und verwendest ihr Lösungsmittel als Sprengstoff. + +[CUB4_T:CUBAN4] +Bumm! Bye bye! + +[CUB4_U:CUBAN4] +Umberto, was ist mit dir? + +[CUB4_V:CUBAN4] +Oh, ich halte mich raus und passe mit Paps auf das Café auf. + +[CUB4_W:CUBAN4] +Er fühlt sich nicht besonders, weißt du. + +[CUB4_02:CUBAN4] +~g~Die Bomben werden per Zeitzünder auf 45 sek. gestellt sein. + +[CUB4_07:CUBAN4] +Zum Lösungsmittel geht's hintenrum, Amigo. + +[CUB4_08:CUBAN4] +Hola, Amigos. + +[CUB4_09:CUBAN4] +Bueno. Haitian Putas. Muerte. + +[CUB4_10:CUBAN4] +Vamos. + +[CUB4_11:CUBAN4] +Genau, vamos. + +[CUB4_12:CUBAN4] +Hey, wir brauchen ein haitianisches Gang-Auto! + +[CUB4_13:CUBAN4] +Oye, suchen wir unsere Muchachos! + +[CUB4_14:CUBAN4] +Folge meinen Compadres. + +[CUB4_15:CUBAN4] +Ok, nur immer rein... + +[CUB4_16:CUBAN4] +Ich lege die Bombe. Gib mir Deckung. + +[CUB4_17:CUBAN4] +RENN! + +[CUB4_18:CUBAN4] +Mann, das ist ein schönes Viertel hier... + +[CUB4_19:CUBAN4] +Das ist doch eine Müllhalde, Mann. + +[CUB4_20:CUBAN4] +Ich kannte mal eine schöne Frau, die hat hier gewohnt. + +[CUB4_21:CUBAN4] +Die machen gute Pizza hier. + +[CUB4_22:CUBAN4] +Hey, Mann! Du fährst wie ein Verrückter! + +[CUB4_23:CUBAN4] +Hast du dich verfahren, Mann? + +[CUB4_24:CUBAN4] +Du hast Pepe vergessen, hole ihn. + +[CUB4_03:CUBAN4] +~g~Bleib im Wagen, bis er sicher auf dem Gelände geparkt ist. + +[CUB4_26:CUBAN4] +~g~Nimm dir Pepe, fahr Richtung Norden nach Little Haiti und klaue einen Voodoo. + +[CUB4_27:CUBAN4] +~g~Triff dich mit Rico und den anderen Kubanern. + +[CUB4_28:CUBAN4] +~g~Triff dich mit den anderen Kubanern bei der haitianischen Drogenfabrik. + +[CUB4_29:CUBAN4] +~g~Gehe in jede der Markierungen, um an dieser Stelle eine Bombe zu legen. + +[CUB4_30:CUBAN4] +~g~Wenn alle drei Bomben gelegt sind, entferne dich von der Fabrik bevor sie hochgeht. + +[CUB4_31:CUBAN4] +~g~Mach, dass du von der Fabrik wegkommst!! + +[CUB4_32:CUBAN4] +~g~Park den Wagen an der im Radar markierten Stelle und steig aus. + +[CUB4_06:CUBAN4] +~r~Du bist nicht weit genug von der Basis weggekommen, wir mussten die Detonation abbrechen! + +{=================================== MISSION TABLE FINALE ===================================} + +[FIN1_01:FINALE] +Was ist los? + +[FIN1_02:FINALE] +Tommy! Ah, gut, gut. Hör zu. Hör zu... + +[FIN1_03:FINALE] +Ich mag Fische. Ich liebe Fische. + +[FIN1_04:FINALE] +Ich liebe sie als Haustiere oder auch lecker zubereitet. + +[FIN1_05:FINALE] +Aber ich liebe sie nicht so sehr, dass ich als Fischfutter im Hafen enden will. + +[FIN1_06:FINALE] +Und jetzt kommen auf einmal deine italienischen Brüder an und wollen mir Zementschuhe verpassen. Und ich... + +[FIN1_07:FINALE] +Halt den Mund, Ken. Setz dich. + +[FIN1_08:FINALE] +Lance, was läuft hier, verdammt? + +[FIN1_09:FINALE] +Deine Freunde aus dem Norden, Tommy. Die sind nicht sehr froh, dass du ihren Mann erledigt hast. + +[FIN1_10:FINALE] +Sie kommen heute, um nach dem Rechten zu sehen. + +[FIN1_11:FINALE] +Sie haben länger gebraucht, als ich dachte... + +[FIN1_12:FINALE] +Jungs, wir müssen denen ein für alle Mal klar machen, dass das mein Laden ist. MEINER! + +[FIN1_13:FINALE] +Ken, hol die erste Ladung von dem Falschgeld und pack 20 Mios in Aktenkoffer. + +[FIN1_14:FINALE] +Lance, du trommelst die anderen Jungs zusammen... + +[FIN2_01:FINALE] +Tommy! + +[FIN2_02:FINALE] +Was ist? Keine Umarmung für deinen alten Freund? + +[FIN2_03:FINALE] +Ich war 15 Jahre weg vom Fenster, + +[FIN2_04:FINALE] +bin nicht mehr auf dem Laufenden, was Manieren angeht. + +[FIN2_05:FINALE] +Immer Wut im Bauch, hä, Tommy? + +[FIN2_06:FINALE] +Ich sag's ja, dein Temperament wird dir nochmal schlecht bekommen. + +[FIN2_07:FINALE] +In den Koffern sind 20 Millionen... + +[FIN2_08:FINALE] +Wie viele waren es denn? Zehn? Nein, elf Männer. + +[FIN2_09:FINALE] +So kommt man zu dem Spitznamen 'Harwood-Butcher'! Hehehe! + +[FIN2_10:FINALE] +Ich sollte EINEN Mann für dich erledigen! Die wussten, dass ich komme, Sonny... + +[FIN2_11:FINALE] +Wie redest du denn mit mir? + +[FIN2_12:FINALE] +Wie kommst du darauf, mich für diesen unglücklichen Zufall verantwortlich zu machen? + +[FIN2_13:FINALE] +Nimm einfach das Geld... + +[FIN2_14:FINALE] +Holt das Geld! + +[FIN2_15:FINALE] +Weißt du, Tommy, ich hab für dich getan, was nur ging. Himmel und Hölle in Bewegung gesetzt. + +[FIN2_16:FINALE] +Ich war dein Freund. Ich dachte, du nimmst Vernunft an. Kapierst, was gut fürs Geschäft ist. + +[FIN2_17:FINALE] +Ich hab dir vertraut, Tommy, und du hast mich enttäuscht. + +[FIN2_18:FINALE] +Aber wenigstens einer in deiner mickrigen Organisation weiß, wie man Geschäfte macht. + +[FIN2_19:FINALE] +Stimmt's, Lance? + +[FIN2_20:FINALE] +Sorry, Tommy. So läuft's in Vice City. So läuft das Geschäft. + +[FIN2_21:FINALE] +Du hast uns verraten... + +[FIN2_22:FINALE] +Nein, ich hab DICH verraten, Tommy, nur DICH. + +[FIN2_23:FINALE] +Die echten Piepen sind oben im Safe. + +[FIN2_24:FINALE] +Tommy, wie hast du dir das denn vorgestellt? + +[FIN2_25:FINALE] +Dachtest du, ich lass mich mit Blüten abspeisen? + +[FIN2_26:FINALE] +Dass ich den Schwanz einziehe und abhaue, um nicht das Gesicht zu verlieren? + +[FIN2_27:FINALE] +Nein. + +[FIN2_28:FINALE] +Ich wollte dich nur noch ein bisschen ärgern, bevor ich dich fertig mache. + +[FIN3_01:FINALE] +Tommy? + +[FIN3_02:FINALE] +Oh Gott, Tommy! Was ist passiert? + +[FIN3_03:FINALE] +Wonach sieht's denn aus? + +[FIN3_04:FINALE] +Sieht aus, als wär dein Anzug ruiniert! + +[FIN3_05:FINALE] +Und das war ein wunderbarer Anzug! Tommy, Herrgott, was ist passiert? + +[FIN3_06:FINALE] +Kleine Meinungsverschiedenheit mit einem Geschäftsfreund. Wie das so ist. + +[FIN3_07:FINALE] +Wenn ich eine Meinungsverschiedenheit mit einem Geschäftsfreund habe, schick ich ihm einen bösen Brief. + +[FIN3_08:FINALE] +Oder ich pinkle ihm in den Briefkasten, aber ich fang nicht den 3. Weltkrieg an. + +[FIN3_09:FINALE] +Vielleicht solltest du mal zu meinem Therapeuten gehen... + +[FIN3_10:FINALE] +Dieser Dreckskerl von Lance... + +[FIN3_11:FINALE] +Tommy, ich konnte den Kerl ja nie leiden. + +[FIN3_12:FINALE] +Er ist neurotisch, unsicher, selbstsüchtig - Er ist ein Arschloch! + +[FIN3_13:FINALE] +Gut, dass du ihn fertig gemacht hast! + +[FIN3_14:FINALE] +Ich glaub auch nicht, dass wir nochmal Ärger mit denen aus dem Norden kriegen... + +[FIN3_15:FINALE] +Die aus dem Norden gibt's nämlich nicht mehr. + +[FIN3_16:FINALE] +Gibt nur noch die im Süden. + +[FIN3_17:FINALE] +Moment, verstehe ich dich richtig, Tommy, Heißt das...? + +[FIN3_18:FINALE] +Na, was meinst du, was das heißt? + +[FIN3_19:FINALE] +Dass wir jetzt die Herren im Haus sind... ich meine, dass DU der Herr im Haus bist. Oh, Tommy... + +[FIN3_20:FINALE] +Weißt du, Ken, das könnte der Beginn einer wunderbaren Geschäftsbeziehung sein... + +[FIN3_21:FINALE] +Schließlich bist du ein hinterlistiger, mieser kleiner Dieb... + +[FIN3_22:FINALE] +und ich bin ein verurteilter Psychopath und Dealer. + +[FIN3_23:FINALE] +Ich weiß. Ist das nicht wunderbar? + +[FIN_B1:FINALE] +~g~Erledige den Verräter ~y~Vance~g~. + +[FIN_B2:FINALE] +~g~Erledige ~p~Sonny~g~, um die Sache ein für alle Mal zuende zu bringen. + +[FIN_B3:FINALE] +~g~Die Mafia will dein Geld stehlen. Verteidige den Safe. + +[FIN_B4:FINALE] +~g~Du hältst nicht mehr lange durch. Hol dir unten ein wenig ~w~Energie~g~. + +[FIN_B5:FINALE] +~g~Die Mafia stiehlt dein Geld. Verteidige den ~c~Safe. + +[FIN_B7:FINALE] +~r~Die Mafia hat dein gesamtes Geld gestohlen. + +[DEFSAFE:FINALE] +~g~Geh zurück zum Safe und verteidige ihn. + +{=================================== MISSION TABLE FIRETRK ===================================} + +[F_PASS1:FIRETRK] +Feuer gelöscht! + +[F_FAIL2:FIRETRK] +~r~Du kommst zu spät! + +[F_CANC:FIRETRK] +~r~Feuerwehr-Mission abgebrochen! + +[F_EXTIN:FIRETRK] +FEUER: + +[F_START:FIRETRK] +~g~Brennendes Fahrzeug in der Gegend von ~a~ gemeldet. Lösche den Brand. + +[SIREN_1:FIRETRK] +Um die Sirenen dieses Fahrzeugs einzuschalten, drücke die ~h~~k~~VEHICLE_HORN~~w~. + +[SIREN_2:FIRETRK] +Um die Sirenen dieses Fahrzeugs einzuschalten, drücke die ~h~~k~~VEHICLE_HORN~~w~. + +[FIREPRO:FIRETRK] +Feuerwehr-Mission Level 12 abgeschlossen: Du bist jetzt absolut feuerfest!! + +[F_FAIL1:FIRETRK] +Feuerwehr-Mission beendet. + +[F_STAR1:FIRETRK] +~g~Brennende Fahrzeuge in der Gegend von ~a~ gemeldet. Lösche den Brand. + +[SPRAY_4:FIRETRK] { reVC update } +Benutze die ~h~~k~~VEHICLE_FIREWEAPON~~w~-Taste, um die Wasserkanone abzufeuern und den ~h~~k~~VEHICLE_TURRETLEFT~~w~ und ~h~~k~~VEHICLE_TURRETRIGHT~~w~, um mit der Wasserkanone zu zielen. + +[SPRAY_1:FIRETRK] { reVC update } +Benutze die ~h~~k~~VEHICLE_FIREWEAPON~~w~-Taste, um die Wasserkanone abzufeuern und den ~h~~k~~VEHICLE_TURRETLEFT~~w~ und ~h~~k~~VEHICLE_TURRETRIGHT~~w~, um mit der Wasserkanone zu zielen. + +{=================================== MISSION TABLE GENERA1 ===================================} + +[GEN1_I:GENERA1] +Für diesen kleinen Gefallen werde ich Sie belohnen. Danach suchen wir Ihr Geld gemeinsam. + +[GEN1_A:GENERA1] +Mr. Vercetti! + +[GEN1_B:GENERA1] +Colonel. + +[GEN1_D:GENERA1] +Nein danke. + +[GEN1_E:GENERA1] +Es ist mir peinlich, aber es scheint, unser Problem ist zum Teil auf das lose Mundwerk einer mir vertrauten Person zurückzuführen. + +[GEN1_F:GENERA1] +Seit Jahren schleppe ich Gonzalez mit, aber nun erreicht seine Unfähigkeit einen neuen Höhepunkt. + +[GEN1_G:GENERA1] +Es ist nur gerecht, wenn Sie Gonzalez erledigen. + +[GEN1_H:GENERA1] +War er es? Mir geht es in erster Linie um das Geld. + +[GEN1_J:GENERA1] +Er ist in seinem Penthouse, vermutlich halb betrunken. Nehmen Sie das hier. + +[GEN1_06:GENERA1] +Er hat eine Kettensäge!! + +[GEN1_07:GENERA1] +Bleib mir vom Leib, du elender Mistkerl! + +[GEN1_08:GENERA1] +Gütiger Himmel, mein Leben ist ruiniert, und mein Aussehen dazu! + +[GEN1_10:GENERA1] +Ich werd dir dein Mundwerk stopfen! + +[GEN1_11:GENERA1] +Bleib stehen, du Fettsack! + +[GEN1_12:GENERA1] +Bleib stehen, und es geht ganz schnell! + +[GEN1_13:GENERA1] +Hör auf zu jammern, das interessiert keinen, Fettsack! + +[GEN1_C:GENERA1] +Danke für Ihr Kommen. Setzen Sie sich. Hummer? + +[GEN1_04:GENERA1] +~g~Benutze den Eingang, um Zugang zu Gonzalez' Dachwohnung zu erhalten. + +[GEN1_05:GENERA1] +~g~Erledige Gonzalez! + +[GEN1_09:GENERA1] +Ich zahle dir das Doppelte, Tommy, das DOPPELTE! + +[GEN1_17:GENERA1] +~g~Gonzalez versucht zu fliehen! Folge ihm durch die Türen und erledige ihn. + +[GEN1_18:GENERA1] +~r~Gonzalez hat das Polizeirevier sicher erreicht! + +[GEN1_19:GENERA1] +~g~Die Polizei von Vice City ist hinter dir her! + +[GEN1_20:GENERA1] +~g~Steig in ein Fahrzeug. + +[GEN1_21:GENERA1] +~g~Begib dich zur~h~ Pay 'N' Spray-Lackiererei~g~ in~h~ Vice Point~g~. + +[GEN1_22:GENERA1] +~g~Bring dein Fahrzeug in die Lackiererei, um deinen ~h~Fahndungslevel~g~ loszuwerden und das Fahrzeug zu ~h~reparieren ~g~und ~h~umzuspritzen~g~. Kosten - ~h~$100~g~. Diesmal gratis. + +[GEN1_01:GENERA1] +Um im Laufen eine Nahkampfattacke vorzubereiten, die~o~ |-Taste~w~ gedrückt halten. + +[GEN1_02:GENERA1] +Um im Laufen eine Nahkampfattacke vorzubereiten, die~x~ /-Taste~w~ gedrückt halten. + +[GEN1_03:GENERA1] +Um im Laufen eine Nahkampfattacke vorzubereiten, die~h~ R1-Taste~w~ gedrückt halten. + +[GEN1_14:GENERA1] +Lass die~h~ ~k~~PED_FIREWEAPON~~w~ los, um die Attacke auszuführen. + +[GEN1_15:GENERA1] +Lasse die~h~ ~k~~PED_FIREWEAPON~~w~ los, um die Attacke auszuführen. + +[GEN1_16:GENERA1] +Lasse die~h~ ~k~~PED_FIREWEAPON~~w~ los, um die Attacke auszuführen. + +[GEN1_23:GENERA1] +~g~Geh durch die Türen zurück ins Erdgeschoss. + +{=================================== MISSION TABLE GENERA2 ===================================} + +[COL2_A:GENERA2] +Tommy! Setzen Sie sich zu mir. + +[COL2_B:GENERA2] +Sieht das nicht köstlich aus? Tapirschnauze? + +[COL2_C:GENERA2] +Äh... Nein. Nein, danke. + +[COL2_D:GENERA2] +Tommy, Sie sind wie eine Pampas-Brise, die mich vom Gestank der Korruption befreit hat. + +[COL2_E:GENERA2] +Natürlich muss ich so tun, als trauere ich um ihn und muss wie immer meine Arbeit machen. + +[COL2_F:GENERA2] +Das bringt mich meinem Geld nicht näher... + +[COL2_G:GENERA2] +Tommy, mein Freund, Sie sind hier nicht in Liberty. Hier regeln wir Dinge anders. + +[COL2_H:GENERA2] +Ich werde weiter nachforschen, zunächst hätte ich aber ein lukratives Geschäft abzuschließen. + +[COL2_I:GENERA2] +Freunden tu ich gern einen Gefallen, Cortez... + +[COL2_J:GENERA2] +Sie sind ein guter Freund, Tommy. Ich wusste, ich kann auf Sie zählen. + +[COL2_K:GENERA2] +Sie müssen sich mit einem Kurier treffen, der wertvolle 'Technologie' für mich beschafft hat. + +[COL2_1:GENERA2] +Die Regen ist sich sehr nass um diese Jahreszeit... + +[COL2_2:GENERA2] +Was? + +[COL2_3:GENERA2] +Äh, comment? + +[COL2_4:GENERA2] +Hören Sie, Cortez schickt mich. Geben Sie mir die verdammten Mikrochips. + +[COL2_5:GENERA2] +Oh... ok. + +[COL2_B1:GENERA2] +~g~Triff dich mit dem Kurier im Einkaufszentrum. + +[COL2_B2:GENERA2] +~g~Der Kurier flieht mit den Lenkwaffen-Chips. Lass ihn nicht entkommen! + +[COL2_B3:GENERA2] +~g~Bring die Lenkwaffen-Chips zum Colonel. + +[COL2_F1:GENERA2] +~r~Du hast die Kontaktperson erledigt! + +[COL2_F2:GENERA2] +~r~Der Kurier ist hinüber. Schnapp dir die Lenkwaffen-Chips. + +[COL2_6A:GENERA2] +Keine Bewegung, amerikanisches Imperialistenschwein! Das ist Eigentum des französischen Staates. Her damit! + +[BLIPHLP:GENERA2] +Das Radar zeigt ein nach oben zeigendes Dreieck, das bedeutet, dass das Ziel sich in größerer Höhe befindet als der Spieler. + +[COL2_F3:GENERA2] +~r~Die Lenkwaffen-Chips liegen auf dem Meeresgrund. + +[COL2_F4:GENERA2] +~r~Der Kurier ist entkommen! Du hast dir die Chips durch die Lappen gehen lassen. + +{=================================== MISSION TABLE GENERA3 ===================================} + +[GEN3_49:GENERA3] +Lance' Gesundheitszustand: + +[GEN3_A:GENERA3] +Thomas, danke, dass Sie kommen. + +[GEN3_B:GENERA3] +Verzeihen Sie, wenn ich gleich zur Sache komme. + +[GEN3_C:GENERA3] +Diaz bat mich, eine kleinere geschäftliche Transaktion zu überwachen. + +[GEN3_D:GENERA3] +Wird hoffentlich besser laufen als die letzte, hah? + +[GEN3_E:GENERA3] +Darum habe ich an Sie gedacht, mein Freund. + +[GEN3_F:GENERA3] +Ich habe etwas zu Ihrem Schutz beim Parkhaus deponiert. + +[GEN3_G:GENERA3] +Holen Sie es ab. Und dann bewachen Sie Diaz' Männer bei dem Deal. + +[GEN3_H:GENERA3] +Danke, Amigo. + +[GEN3_1:GENERA3] +Reißt sich alles selbst unter den Nagel, wie ich sehe. + +[GEN3_2:GENERA3] +Sag mal, ist das alles, was du kannst, mir dauernd nachzuschnüffeln? Komm mit und zeig mir, dass du was drauf hast. + +[GEN3_3:GENERA3] +Könnte ich machen. Ich heiße übrigens Lance. + +[GEN3_5:GENERA3] +Du musst Cortez' neuer Mann sein. + +[GEN3_6:GENERA3] +Bis ich was besseres finde... + +[GEN3_7:GENERA3] +Sie müssen bald hier sein. Wir sollten uns gute Beobachtungsposten suchen. + +[GEN3_8:GENERA3] +Ok! Ich nehm den Balkon, du kletterst auf das Dach drüben im Hof. + +[GEN3_9:GENERA3] +Ich lebe! Idioten! Und das nur wegen dir! Wie heißt du? + +[GEN3_10:GENERA3] +Tommy. + +[GEN3_11:GENERA3] +Wir sehen uns bald wieder, glaube ich! + +[GEN3_12:GENERA3] +Wo steckt denn Lance? Shit... + +[GEN3_14:GENERA3] +Tommy! Ich brauch Hilfe! + +[GEN3_15:GENERA3] +Keine Sorge, ich hab alles im Griff! + +[GEN3_16:GENERA3] +Diaz' Männer werden umgemäht! + +[GEN3_19:GENERA3] +~g~Haitianer! Sie greifen an! Beschütze Diaz! + +[GEN3_20:GENERA3] +~g~Begib dich zum Parkhaus. Hol dir die Waffe, die der Colonel dort für dich deponiert hat. + +[GEN3_22:GENERA3] +Diaz' Gesundheitszustand: + +[GEN3_23:GENERA3] +~g~Du hast Lance vergessen. Hole ihn! + +[GEN3_25:GENERA3] +~r~Lance hat's erwischt! + +[GEN3_28:GENERA3] +~g~Bring den Aktenkoffer zu Diaz zurück. + +[GEN3_29:GENERA3] +~g~Hol dir den Aktenkoffer und bringe ihn zu Diaz zurück. + +[GEN3_30:GENERA3] +~r~Er ist mit dem Geld entwischt! Dafür macht Diaz dich kalt! + +[GEN3_33:GENERA3] +~r~Du sollst Diaz und seine Männer bewachen, nicht beschießen! + +[GEN3_34:GENERA3] +~r~Es gibt keinen Deal, wenn du die Kubaner erledigst! + +[GEN3_35:GENERA3] +~g~Er hat Diaz' Geld gestohlen! + +[GEN3_36:GENERA3] +~g~Schnapp dir das Motorrad, stelle ihn und hol Diaz' Geld zurück! + +[GEN3_37:GENERA3] +~g~. Die Kubaner kommen. Überwache den Deal. Pass auf Diaz und Lance auf. + +[GEN3_38:GENERA3] +~r~Diaz hat's erwischt! Du hast versagt! + +[GEN3_39:GENERA3] +~g~Begib dich zu deinem Beobachtungsposten die Treppe hoch. + +[GEN3_44:GENERA3] +~g~Begib dich mit Lance zum Übergabeort und pass auf Diaz auf. + +[GEN3_45:GENERA3] +Sie müssen bald hier sein. Wir sollten uns gute Beobachtungsposten suchen. + +[GEN3_40:GENERA3] { reVC update } +Um auf einem ~h~Motorrad ~w~sitzend ~h~geradeaus zu feuern~w~, drücke die ~h~~k~~VEHICLE_FIREWEAPON~. + +[GEN3_41:GENERA3] { reVC update } +Um auf einem ~h~Motorrad ~w~sitzend ~h~geradeaus zu feuern~w~, drücke die ~h~~k~~VEHICLE_FIREWEAPON~. + +[GEN3_46:GENERA3] +Scheiße! + +[GEN3_47:GENERA3] +Tommy! + +[GEN3_48:GENERA3] +Verdammt! + +[GEN3_50:GENERA3] +~r~Du hast Diaz' Geld verloren! Versuch das nächste Mal, das Geld nicht zu vernichten! + +[GEN3_51:GENERA3] +Noch mehr verdammte Haitianer in einem beschissenen Van! + +[GEN3_54:GENERA3] +Steht nicht rum, ihr Idioten! Schnappt euch diesen haitianischen Mistkerl! + +[GEN3_55:GENERA3] +Tommy! Ich bleibe hier und passe auf Diaz auf! + +[GEN3_18:GENERA3] +~g~Die Kubaner kommen. Bleib in Diaz' Nähe. Überwache den Deal. Pass auf Diaz und Lance auf. + +[GEN3_56:GENERA3] +~r~Diaz ist hinüber, er ist in einen Hinterhalt geraten. Pass das nächste Mal auf ihn auf! + +[GEN3_57:GENERA3] +Die Kruger ist ein Sturmgewehr, das einem erlaubt, in der subjektiven Kamera-Einstellung manuell zu zielen. + +[GEN3_58:GENERA3] +Halte die~h~ R1~w~-Taste gedrückt, um mit dem Sturmgewehr zu ~h~zielen~w~. + +[GEN3_59:GENERA3] +Halte die~h~ L1~w~-Taste gedrückt, um mit dem Sturmgewehr zu ~h~zielen~w~. + +[GEN3_60:GENERA3] +Drücke die ~h~~k~~PED_FIREWEAPON~~w~-Taste, um das Sturmgewehr ~h~abzufeuern~w~. + +[GEN3_61:GENERA3] +Drücke die ~h~~k~~PED_FIREWEAPON~~w~-Taste, um das Sturmgewehr ~h~abzufeuern~w~. + +[GEN3_62:GENERA3] +Drücke die ~h~~k~~PED_FIREWEAPON~~w~-Taste, um das Sturmgewehr ~h~abzufeuern~w~. + +[GEN3_63:GENERA3] +Auf~h~ Motorrädern ~w~kann man nicht nur im Vorbeifahren seitlich auf Ziele schießen, man kann auch ~h~geradeaus feuern~w~. + +[GEN3_64:GENERA3] { reVC update } +Um auf einem Motorrad sitzend geradeaus zu feuern, drücke die ~h~~k~~VEHICLE_FIREWEAPON~~w~-Taste. + +[GEN3_65:GENERA3] { reVC update } +Um auf einem Motorrad sitzend geradeaus zu feuern, drücke die ~h~~k~~VEHICLE_FIREWEAPON~~w~-Taste. + +[GEN3_66:GENERA3] { reVC update } +Um auf einem Motorrad sitzend geradeaus zu feuern, drücke die ~h~~k~~VEHICLE_FIREWEAPON~~w~-Taste. + +[GEN3_67:GENERA3] +Du brauchst eine Maschinenpistole, um auf einem Motorrad sitzend geradeaus zu feuern. + +[GEN3_53:GENERA3] +MEIN GELD! + +[GEN3_52:GENERA3] +Diese Haitianer denken sie könnten RICARDO DIAZ stürzen!?! + +{=================================== MISSION TABLE GENERA4 ===================================} + +[DETON:GENERA4] +DETONATION: + +[COL4_3:GENERA4] +KONVOI HALT! + +[COL4_6:GENERA4] +WIR STEHEN UNTER FEINDLICHEM BESCHUSS! + +[COL4_7:GENERA4] +Weg da von dem Panzer, Zivilist! + +[COL4_8:GENERA4] +ICH SAGTE, WEG DA! SOFORT! + +[COL4_9:GENERA4] +AUF GEFECHTSPOSITIONEN! + +[COL4_11:GENERA4] +Schaffen Sie den Zivilisten aus dem Weg, Soldat! - Sir, zu Befehl, Sir! + +[COL4_12:GENERA4] +Zivilist im PANZER! HALTET IHN AUF! + +[COL4_13:GENERA4] +Dies ist ein Militärkonvoi, machen Sie den Weg frei! + +[COL4_14:GENERA4] +Erledigen sie ihn, Soldat. + +[COL4_15:GENERA4] +Schaffen Sie das Zivilfahrzeug aus dem Weg! -Fahrzeug wird weggeschafft, Sir! + +[COL4_17:GENERA4] +OK, EINHEIT WEITER! + +[COL4_18:GENERA4] +Da ist jemand auf dem Panzer, Sir! + +[COL4_19:GENERA4] +Gehen Sie ein paar Donuts holen, Soldat! - Zu Befehl, Sir! + +[COL4_20:GENERA4] +Ziel erfasst, Sir. + +[COL4_21:GENERA4] +HECKENSCHÜTZE! + +[COL4_22:GENERA4] +Ich seh zu, dass ich hier wegkomme! + +[COL4_23:GENERA4] +Einsatz durchgeführt! Einheit, wegtreten! - Gehen wir ein paar Donuts essen. + +[COL4_24:GENERA4] +Sicherheitsmechanismus Delta India Echo aktiviert. Selbstzerstörung des Fahrzeugs eingeleitet! + +[COL4_26:GENERA4] +Mach dein Testament, dreckiger Kommunist! + +[COL4_B2:GENERA4] +~r~Der Panzer ist wohlbehalten an seinem Ziel angekommen! + +[COL4_B5:GENERA4] +~r~Der Panzer ist zerstört worden! + +[COL4_01:GENERA4] +Diaz war sehr angetan und würde Sie gerne wiedersehen. + +[COL4_02:GENERA4] +Ist das eine gute Nachricht? + +[COL4_03:GENERA4] +Aber sicher! Obwohl mich der Verdacht beschleicht, dass Diaz für unsere Verluste verantwortlich war... + +[COL4_04:GENERA4] +Wie kommen Sie darauf? + +[COL4_05:GENERA4] +Man erhebt keine Anschuldigungen gegen einen Mann wie Diaz. Ich habe nur laut nachgedacht. + +[COL4_06:GENERA4] +Egal. Ich habe einen Vorschlag, der lukrativ für Sie sein könnte... + +[COL4_07:GENERA4] +Ich habe keine Zeit für weitere Aufträge, Cortez. + +[COL4_08:GENERA4] +Ein Mann mit solch bedrohlichen Schulden sollte doch um jede Verdienstmöglichkeit dankbar sein. Hören Sie mich wenigstens an. + +[COL4_09:GENERA4] +Na gut... + +[COL410:GENERA4] +Ich habe einen Käufer für ein 'Militärgerät', das durch die Stadt transportiert wird. Beschaffen Sie es! + +[COL411:GENERA4] +Wenn Sie es haben, rufen Sie mich unverzüglich an... + +[COL4_B4:GENERA4] +~g~Der Panzer ist abgeschlossen. Lass dir etwas einfallen, um die Besatzung herauszulocken. + +[COL4_1:GENERA4] +Was ist mit dem Kanonier? - Weiß nicht, Sir! + +[COL4_4:GENERA4] +Los, sehen Sie nach, Soldat! - Zu Befehl, Sir! + +[COL4_B1:GENERA4] +~g~Besorge das militärische Fahrzeug, das durch die Stadt gefahren wird. + +[COL4_B3:GENERA4] +~g~Liefere den Panzer in der Garage des Colonels ab, bevor er sich selbst zerstört. + +[COL4_B6:GENERA4] +~g~Finde einen Weg, den Panzer zu klauen! + +[COL4_B7:GENERA4] +~g~Fahr den Panzer in die Garage. + +[COL4_B8:GENERA4] +~g~Steig aus dem Panzer und verlasse die Garage. + +{=================================== MISSION TABLE GENERA5 ===================================} + +[COL5A_1:GENERA5] +Die Lage erfordert ein eiliges Verschwinden, Amigo. + +[COL5A_2:GENERA5] +Was gibt's für ein Problem? + +[COL5A_3:GENERA5] +Die Franzosen wollen ihre Lenkwaffen-Chips wieder und nach dem letzten Zwischenfall + +[COL5A_4:GENERA5] +zieht es mich in sicherere Gefilde. + +[COL5A_5:GENERA5] +Wäre es nicht sicherer zu fliegen? + +[COL5A_6:GENERA5] +Ich wäre erledigt, bevor ich eingecheckt hätte. Außerdem muss ich Ware außer Landes schaffen. + +[COL5A_7:GENERA5] +Brauchen Sie noch einen Bodyguard? + +[COL5A_8:GENERA5] +Sie, mein Freund, sind zehn Bodyguards wert. Hahaha. + +[COL5B_1:GENERA5] +Thomas, Sie haben mich beschützt und mir treu gedient. + +[COL5B_2:GENERA5] +Aber jetzt müssen Sie uns verlassen, ehe wir das offene Meer erreichen. + +[COL5B_4:GENERA5] +Danke, Colonel. + +[COL5B_5:GENERA5] +Eine Bitte noch. Könnten Sie ein Auge auf Mercedes haben, solange ich weg bin? + +[COL5B_6:GENERA5] +Ich glaub zwar, sie kann auf sich selbst aufpassen, aber klar. + +[COL5B_7:GENERA5] +Danke, mein Freund. Bis zu meiner Rückkehr. + +[COL5B_8:GENERA5] +Adios, Amigo. + +[COL5_7:GENERA5] +Hören Sie auf, auf mich zu schießen! + +[COL5_9:GENERA5] +Tommy, die sollen aufhören, auf mich zu schießen! + +[COL5_10:GENERA5] +Ich genieße diplomatische Immunität. + +[COL5_11:GENERA5] +Nicht schießen, ich bin ein Colonel! + +[COL5_12:GENERA5] +Thomas, machen Sie sie fertig. Mein Land wird es Ihnen danken. + +[COL5_13:GENERA5] +Tommy, wir werden von den Franzosen überrannt! + +[COL5_14:GENERA5] +Tommy, wo ich hinsehe, überall Franzosen! Wie ich es hasse! + +[COL5_15:GENERA5] +Tommy, alles in Ordnung? + +[COL5_16:GENERA5] +Das ist für Piaf und Gainesbourg und für euer dämliches französisches Weißbrot! + +[COL5_1:GENERA5] +Backbord! Backbord! + +[COL5_2:GENERA5] +Sie greifen von Steuerbord an! + +[COL5_3:GENERA5] +Die Brücke da vorn! + +[COL5_4:GENERA5] +Sie haben einen Helikopter! + +[COL5_B1:GENERA5] +~g~Beschütze den Colonel und seine Jacht um jeden Preis. + +[COL5_B2:GENERA5] +~g~Geh nach vorn und räume der Jacht des Colonels den Weg frei. + +[COL5_B3:GENERA5] +~r~Der Colonel ist hinüber! + +[COL5_B4:GENERA5] +~g~Schieße den angreifenden Helikopter vom Himmel. + +[COL5B_3:GENERA5] +Ich werde meine Privatbarkasse zu Wasser lassen. Sie gehört Ihnen, als Ausdruck meiner Dankbarkeit. + +[COL5_B5:GENERA5] +~g~Schieß die Helikopter ab, gefährde nicht die Jacht. + +[COL5_B6:GENERA5] +~g~Du hast keine Munition mehr. Hol dir an der Treppe des Oberdecks Nachschub. + +[COL5_B7:GENERA5] +~g~Du hast nur noch wenig Energie. Hol dir an der Treppe des Oberdecks Nachschub. + +{=================================== MISSION TABLE HAIT1 ===================================} + +[HAM1_A:HAIT1] +Hallo? Hallo? + +[HAM1_B:HAIT1] +Komm rein, mein Lieber, und ruh dich aus. + +[HAM1_C:HAIT1] +Du musst der große böse Mann sein, von dem mein Großvater erzählt hat. + +[HAM1_D:HAIT1] +Er erzählte immer von dir, wenn er zu Besuch kam, + +[HAM1_E:HAIT1] +und von den anderen, die auf dich warten. + +[HAM1_F:HAIT1] +Tja, wir müssen alle mal sterben, aber du... + +[HAM1_G:HAIT1] +....in deiner Haut möchte ich dann nicht stecken. Hehehe! + +[HAM1_H:HAIT1] +Es hieß, ich soll hierher kommen. + +[HAM1_I:HAIT1] +Kannst du sie hören? + +[HAM1_J:HAIT1] +Sie rufen deinen Namen, Junge. Die müssen ziemlich scharf auf dich sein, was? + +[HAM1_K:HAIT1] +Aber wenn du der alten Tante Poulet hilfst, hilft sie dir vielleicht auch. + +[HAM1_L:HAIT1] +Vielleicht kann sie dir danach einen kleinen Talisman schenken. + +[HAM1_M:HAIT1] +Ein bisschen Magie, die den Männern des Gesetzes schlechte Augen macht, hmm? + +[HAM1_N:HAIT1] +Hören Sie, das ist alles sehr, äh... Sie geben mir was? + +[HAM1_O:HAIT1] +Ich...ich...ich glaube, ich bin hier falsch. + +[HAM1_P:HAIT1] +Erweise mir ein paar Gefälligkeiten, Tommy... + +[HAM1_Q:HAIT1] +Die Kubaner, miese, hochnäsige Narren, hmmm, + +[HAM1_R:HAIT1] +haben meine lieben Haiti-Boys sehr geärgert. + +[HAM1_S:HAIT1] +Jetzt haben sie den Polizisten erzählt, wo ich meine Pülverchen versteckt habe. + +[HAM1_T:HAIT1] +Sie denken, das sind Drogen, diese Dummköpfe. + +[HAM1_U:HAIT1] +Sei ein braver Bub, Tommy, und hole Tante Poulet die Pülverchen. + +[HAM1_V:HAIT1] +Ja, ja. Sicher, sicher. + +[HAM1_1:HAIT1] +~g~Die Cops nähern sich dem Zeug. Hol es, bevor sie dort sind. + +[HAM1_2:HAIT1] +~r~Die Cops waren schneller bei dem Zeug! + +[HAM1_3:HAIT1] +~g~Bring das Zeug zum Unterschlupf! + +[HAM1_4:HAIT1] +~g~Gut. Jetzt das nächste! + +[HAM1_6:HAIT1] +~r~Das Zeug wurde vernichtet, du Idiot! + +[HAM1_7:HAIT1] +~g~Die Cops haben das Zeug! Hol es dir wieder, bevor sie weg sind! + +[HAM1_8:HAIT1] +~g~Die Cops sind unterwegs, um das Zeug abzuholen. Beeil dich! + +[HAT_1A:HAIT1] +~g~Keine Bewegung, Freundchen! + +{=================================== MISSION TABLE HAIT2 ===================================} + +[HAT2_B1:HAIT2] +~g~Begib dich zu dem Wagen, in dem die fliegenden Bomben sind. + +[HAT2_B2:HAIT2] +Schalte die Kubaner aus... + +[HAT2_B4:HAIT2] +.... und zerstöre ihre Boote! + +[HAT2_B5:HAIT2] +~g~Die Kubaner hauen ab. Lass sie nicht entkommen! + +[HAT2_B6:HAIT2] +~r~Das ferngesteuerte Flugzeug ist außer Reichweite! + +[HAT2_B7:HAIT2] +~g~Einer der Kubaner flieht in einem Auto. Lass ihn nicht entkommen! + +[HAT2_B8:HAIT2] +~r~Du hast keine ferngesteuerten Flugzeuge mehr! + +[HAT2_B9:HAIT2] +Ferngesteuerte Flugzeuge: + +[HAT2_1:HAIT2] +Oh. Entschuldigung, ich muss die falsche Adresse haben... + +[HAT2_2:HAIT2] +Du kannst gern reinkommen und dich bei einer Tasse Tee ausruhen. + +[HAT2_3:HAIT2] +Hast du etwas für mich, Tommy? + +[HAT2_4:HAIT2] +Ja... + +[HAT2_5:HAIT2] +Das kommt mir so bekannt vor hier. Ein Geruch aus meiner Kindheit - das muss ein Déjà-vu sein... + +[HAT2_6:HAIT2] +Tommy, du kannst was für mich tun, ich werd's dir erklären. Hör gut zu, ja? + +[HAT2_7:HAIT2] +Sie sehen aus wie jemand, den... + +[HAT2_8:HAIT2] +Die Kubaner haben schnelle Boote, mit denen transportieren sie Drogen übers Meer. + +[HAT2_9:HAIT2] +Davon leben sie. + +[HAT2_10:HAIT2] +Mein Neffe hat kleine fliegende Bomben gebaut, um ihnen das Handwerk zu legen. + +[HAT2_11:HAIT2] +Lass ihre Boote in die Luft fliegen. + +[HAT2_12:HAIT2] +Tja, danke für den Tee. + +[HAT2_B3:HAIT2] { reVC update } +Um eine Bombe abzuwerfen, drück die ~h~~k~~VEHICLE_FIREWEAPON~~w~-Taste. ~h~~k~~VEHICLE_ENTER_EXIT~~w~-Taste zum Beenden. + +{=================================== MISSION TABLE HAIT3 ===================================} + +[HAM3_A:HAIT3] +Hallo, hallo, ich, äh, ich suche hier jemanden... + +[HAM3_B:HAIT3] +Du siehst hungrig aus, Tommy. + +[HAM3_C:HAIT3] +Kenne ich Sie? + +[HAM3_D:HAIT3] +Sei jetzt still. + +[HAM3_E:HAIT3] +Eine Gefälligkeit noch, dann lasse ich dich gehen, Tommy. + +[HAM3_F:HAIT3] +Meine Jungs haben die Kubaner zum Kampf gefordert. + +[HAM3_G:HAIT3] +Aber ohne Kanonen. + +[HAM3_H:HAIT3] +Hmm, aber die Kubaner werden ihr blaues Wunder erleben. + +[HAM3_I:HAIT3] +Wenn sie in den Straßen kämpfen, nimmst du dieses Gewehr und räumst auf. + +[HAM3_J:HAIT3] +Keiner sieht dich, keiner hört dich. + +[HAM3_K:HAIT3] +Wenn du das für mich tust, Tommy, dann kommst frei aus meinen Schürzenbändern. + +[HAM3_1:HAIT3] +~g~Wir müssen gewinnen. Werden alle Haitianer erledigt, haben wir verloren. + +[HAM3_3:HAIT3] +~g~Vermutlich werden die Kubaner schummeln. Sei auf der Hut! + +[HAM3_4:HAIT3] +~r~Du wurdest entdeckt! Die Mission ist fehlgeschlagen! + +[HAM3_5:HAIT3] +~g~Du musst die Kubaner aus der Distanz erwischen. Man darf dich nicht sehen. + +[HAM3_8:HAIT3] +~g~Es gibt Verluste unter den Haitianern! Du musst besser zielen! + +[HAM3_7:HAIT3] +~g~Vorsicht! Die Kubaner haben Verstärkung mitgebracht. Schalte sie alle aus!! + +[HAM3_2:HAIT3] +~r~Die Haitianer sind weg vom Fenster! + +[HAM3_L:HAIT3] +Okay, Tantchen... + +{=================================== MISSION TABLE HOTEL ===================================} + +[INTB_A:HOTEL] +Tommy! Tommy, wir haben uns lange nicht gesehen. + +[INTB_B:HOTEL] +Hallo, Sonny. + +[INTB_C:HOTEL] +Ich weiß, ich weiß. Dir kommen vor Rührung die Tränen. + +[INTB_D:HOTEL] +15 Jahre ist es her - dabei kommt's mir vor, als wär's gestern gewesen. + +[INTB_E:HOTEL] +DU hast leicht reden. + +[INTB_F:HOTEL] +Hey, für die Familie in den Knast zu gehen, ist kein Zuckerschlecken, + +[INTB_G:HOTEL] +aber die Familie zeigt sich für sowas erkenntlich, ok? + +[INTB_H:HOTEL] +Also, wie ist der Deal gelaufen - hast du Schnee an der Hand? + +[INTB_I:HOTEL] +Sonny, wir sind reingelegt worden. Der Deal war eine Falle. Harry und Lee sind tot. + +[INTB_J:HOTEL] +Das ist nicht dein Ernst, Tommy. Du hast doch hoffentlich noch das Geld. + +[INTB_K:HOTEL] +Nein, Sonny... Ich hab das Geld nicht mehr. + +[INTB_L:HOTEL] +Das war mein Geld, Tommy. MEIN GELD! + +[INTB_M:HOTEL] +Versuch bloß nicht, mich reinzulegen, Tommy. Mich legt man nicht rein, das weißt du! + +[INTB_N:HOTEL] +Warte, Sonny. + +[INTB_O:HOTEL] +Du hast mein Wort darauf, dass ich dir dein Geld wiederbeschaffe. Und die Drogen. + +[INTB_P:HOTEL] +Und ich liefere dir die Kerle, die dahinterstecken. + +[INTB_Q:HOTEL] +Das weiß ich doch. Du bist kein Idiot, Tommy, aber ich warne dich - ich bin auch keiner. + +[INTB_R:HOTEL] +Wenn du's nicht wärst - ein anderer wär längst fällig! + +[INTB_S:HOTEL] +Aber uns beide verbindet eine alte Freundschaft. Ich lasse dich das regeln. + +[INTB_T:HOTEL] +Sonny, du hast mein Ehrenwort. + +[INTB_U:HOTEL] +Du hörst von mir. + +{=================================== MISSION TABLE ICECRE1 ===================================} + +[ICC1_1:ICECRE1] +~g~Benutze deinen Eis-Wagen, um in Vice City Drogen zu verkaufen. + +[ICC1_2:ICECRE1] +~g~Parke den Eis-Wagen und drücke ~h~~k~~VEHICLE_HORN~~w~, um den Eiscreme-Jingle abzuspielen, damit deine Kunden wissen, dass du Ware zu verkaufen hast. + +[ICC1_3:ICECRE1] +~g~Für jede Transaktion bekommst du Geld. Aber je mehr Transaktionen du tätigst, desto stärker wird die Polizei auf dich aufmerksam. + +[ICC1_4:ICECRE1] +~g~In dieser Gegend sind keine Kunden. Versuche es woanders. + +[ICC1_5:ICECRE1] +Getätigte Deals: + +[ICC1_6:ICECRE1] +~g~Nimm den Mr. Whopee, um in Vice City Cherry Popper-Produkte zu vertreiben. + +[ICC1_7:ICECRE1] +~g~Für jede Transaktion bekommst du Geld. Aber je mehr Transaktionen du tätigst, desto stärker wird die Polizei auf dich aufmerksam. + +[ICC1_8:ICECRE1] +~g~Um eine Transaktion zu tätigen, ~h~parke deinen Wagen ~g~und drücke die ~h~~k~~VEHICLE_HORN~~g~, um den Eiscreme-Jingle abzuspielen, damit deine Kunden wissen, dass du Ware zu verkaufen hast. + +[ICC1_9:ICECRE1] +~g~Andere Gangs werden es nicht gern sehen, dass du in ihrem Revier Geschäfte machst, du musst also mit Feindseligkeiten rechnen. + +[ICC1_10:ICECRE1] +~g~Du hast ~1~ Deals getätigt! + +[ICC1_11:ICECRE1] +~g~Du hast ~1~ Deal getätigt. + +[ICC1_12:ICECRE1] +OBJEKT ERWORBEN! + +[ICC1_13:ICECRE1] +~r~Du hast keine Deals getätigt! + +[ICC1_14:ICECRE1] +EISCREME-MISSIONEN ERFÜLLT + +[ICC1_15:ICECRE1] +~g~Die Eiscremefabrik generiert nun bis zu $~1~ Einkünfte. Hol dir das Geld regelmäßig ab. + +[ICC1_16:ICECRE1] +~g~Nimm den Mr. Whoopee, um in Vice City Cherry Popper-Produkte zu vertreiben. + +[ICE_AT1:ICECRE1] +EISCREMEFABRIK-MISSIONEN ERFÜLLT + +[ICE_AT2:ICECRE1] +~g~Die Cherry Popper-Fabrik generiert nun bis zu $~1~ Einkünfte. Hol dir das Geld regelmäßig ab. + +[ICC1_17:ICECRE1] +Stoff-Auslieferungs-Mission beendet + +[ICC1_18:ICECRE1] +Eiscremeverkauf insgesamt: $~1~ + +[ICC1_19:ICECRE1] +Insgesamt getätigte Deals: ~1~ + +{=================================== MISSION TABLE ICECUT ===================================} + +[ICC1_M:ICECUT] +Sie sind schmutzige, verrotzte, verlauste, eklige, sabbernde kleine... + +[ICC1_I:ICECUT] +Ein Baby... ein widerwärtiges, gräßliches, ekelhaftes kleines Gör! + +[ICC1_J:ICECUT] +Mammi liebt dich nicht. Du kleines Stück Scheiße! + +[ICC1_A:ICECUT] +Wer sind Sie? + +[ICC1_B:ICECUT] +Der neue Inhaber dieses Ladens. + +[ICC1_C:ICECUT] +Sind Sie, oder waren Sie je ein Kind? + +[ICC1_D:ICECUT] +Was soll denn das heißen? + +[ICC1_E:ICECUT] +Waren Sie je ein Kind? + +[ICC1_F:ICECUT] +Ja! Immer mit der Ruhe! Was ist denn mit Ihnen los? + +[ICC1_G:ICECUT] +Ich wusste es. Ein Kind. + +[ICC1_H:ICECUT] +Ein schmutziges, stinkendes, verrotztes, verlaustes, nölendes kleines Baby! + +[ICC1_K:ICECUT] +Au! Beruhigen Sie sich doch! + +[ICC1_L:ICECUT] +Ich HASSE Babies. Und ich hasse Kinder. + +[ICC1_N:ICECUT] +Es reicht jetzt! + +[ICC1_P:ICECUT] +Sie stellen doch Softeis her, oder? Das essen doch nur Kinder. + +[ICC1_Q:ICECUT] +Sind Sie komplett irre? + +[ICC1_R:ICECUT] +Erklären Sie mir das mal - warum Kinder glücklich machen, wenn Sie sie hassen? + +[ICC1_S:ICECUT] +Oh, du dämliches, verrotztes, verlaustes- + +[ICC1_T:ICECUT] +Schluss jetzt! + +[ICC1_U:ICECUT] +- Gör! + +[ICC1_V:ICECUT] +Das Eis ist nur Tarnung! + +[ICC1_W:ICECUT] +Wir vertreiben auch andere Waren. Nicht-Milchprodukte. + +[ICC1_X:ICECUT] +Und wenn ich ein Kind sehe, dann weiß ich, was ich mit ihm anfange. + +[ICC1_Y:ICECUT] +Nicht wahr, Kinderchen? Ja, ja. Mammi hat euch gar nicht lieb. + +[ICC1_Z:ICECUT] +Sie HASST euch! + +[ICC1_ZA:ICECUT] +OBJEKT ERWORBEN! + +{=================================== MISSION TABLE INTRO ===================================} + +[INT1_A:INTRO] +Tommy Vercetti...Hah! Shit. + +[INT1_B:INTRO] +Hätte nicht gedacht, dass der noch mal rauskommt. + +[INT1_C:INTRO] +Er hat den Kopf eingezogen. War fast vergessen. + +[INT1_D:INTRO] +Aber bald wird man sich an ihn erinnern. + +[INT1_E:INTRO] +Wenn man ihn wieder durch ihre Viertel tigern sieht. + +[INT1_F:INTRO] +Wird schlecht fürs Geschäft sein. + +[INT1_G:INTRO] +Tja, was sollen wir machen, Sonny? + +[INT1_H:INTRO] +Wir machen auf alte Kumpels und schicken ihn woanders hin. Ok? + +[INT1_I:INTRO] +Wir wollten doch sowieso nach Süden expandieren, oder? + +[INT1_J:INTRO] +In Vice City liegt zurzeit das Geld auf der Straße. + +[INT1_K:INTRO] +Die Kolumbianer, die Mexikaner, + +[INT1_L:INTRO] +ja, sogar die kubanischen Flüchtlinge machen alle glänzende Geschäfte. + +[INT1_M:INTRO] +Aber das geht nur mit Drogen, Sonny. + +[INT1_N:INTRO] +Aber keine der Familien rührt dieses Zeug an! + +[INT1_O:INTRO] +Die Zeiten ändern sich. + +[INT1_P:INTRO] +Die Familien können nicht wegsehen, während unsere Feinde groß abkassieren. + +[INT1_Q:INTRO] +Also schicken wir jemanden für die Drecksarbeit da runter + +[INT1_R:INTRO] +und schneiden uns 'ne hübsche Scheibe ab. Ok? + +[INT1_S:INTRO] +Wer ist unser Kontaktmann da unten? + +[INT1_T:INTRO] +Ken Rosenberg, ein Idiot von einem Anwalt. + +[INT1_U:INTRO] +Wie soll der Vercetti im Zaum halten? + +[INT1_V:INTRO] +Muss er gar nicht. + +[INT1_W:INTRO] +Wir lassen ihn einfach auf Vice City los. + +[INT1_X:INTRO] +Wir geben ihm ein bisschen Startgeld, ok? + +[INT1_Y:INTRO] +Wir warten ein paar Monate. + +[INT1_Z:INTRO] +Dann fahren wir hin + +[INT1_A1:INTRO] +und schauen mal bei ihm rein, klar? + +[INT1_A2:INTRO] +Mal sehen, wie er sich macht. + +[INT2_A:INTRO] +Hey, hey, Jungs! Ich bin, äh, Ken Rosenberg. Ha, ha, sehr gut, hey! + +[INT2_B:INTRO] +Tja, äh, ich soll euch zu dem Trefffen fahren, okay? + +[INT2_C:INTRO] +Ich hab mit den Lieferanten geredet, und die würden, + +[INT2_D:INTRO] +liebend gern mit uns ins Geschäft kommen. Und, äh, + +[INT2_E:INTRO] +wenn alles gut geht, dann dürfte da + +[INT2_F:INTRO] +ein Haufen Kohle für uns drin sein. Und das ist doch, na ja... + +[INT2_G:INTRO] +gut... + +[INT2_H:INTRO] +Okay. Es sind zwei Brüder, ok? + +[INT2_I:INTRO] +Der eine schmeißt, äh, den Laden, + +[INT2_J:INTRO] +der andere macht die Flüge. + +[INT2_K:INTRO] +Die arbeiten von Mexiko aus, + +[INT2_M:INTRO] +Sie haben eine Farm in Panama. + +[INT2_N:INTRO] +Okay, passt auf, Jungs, + +[INT2_O:INTRO] +wenn wir dort ankommen, sollte ich im Auto bleiben, + +[INT2_P:INTRO] +oder soll ich mit reinkommen? + +[INT2_Q:INTRO] +Nein. Bleib im Wagen. + +[INT2_R:INTRO] +Wisst ihr was, ich hab's mir überlegt. + +[INT2_S:INTRO] +Ich pass auf den Wagen auf. + +[INT3_A:INTRO] +Ok, das sind sie, da im Helikopter. + +[INT3_B:INTRO] +Ok, das ganze läuft so ab: + +[INT3_C:INTRO] +Die wollen eine saubere Übergabe auf offenem Gelände. + +[INT3_D:INTRO] +Alles klar? Ok, dann wollen wir mal. + +[INT3_E:INTRO] +Ok, ganz ruhig jetzt. + +[INT3_F:INTRO] +Ich bin hier. Der Wagen läuft, Baby! + +[INT3_G:INTRO] +Hast du's? + +[INT3_H:INTRO] +100% astreines kolumbianisches Koks, mein Freund. + +[INT3_I:INTRO] +Die Kohle? + +[INT3_J:INTRO] +Zehner und Zwanziger. Gebrauchte Scheine. + +[INT3_L:INTRO] +Los, los, weg hier! Fahr los! + +[INT4_A:INTRO] +Gearscht. Wir sind voll gearscht! + +[INT4_B:INTRO] +Das ist wieder typisch. + +[INT4_C:INTRO] +Da passe ich eine einziges Mal nicht auf, + +[INT4_D:INTRO] +und prompt kriege ich eins reingewürgt. + +[INT4_E:INTRO] +Hier! + +[INT4_F:INTRO] +Hör endlich auf zu jammern. Du lebst schließlich noch, oder? + +[INT4_G:INTRO] +Lass mich hier raus. + +[INT4_H:INTRO] +Schaff das Auto weg und leg dich schlafen. + +[INT4_I:INTRO] +Ich komme morgen zu dir ins Büro, dann sehen wir weiter. + +[INT4_J:INTRO] +Ok, gute Idee. Ich leg mich erstmal hin. + +[INT4_K:INTRO] +Was hast du vor? + +[INT4_L:INTRO] +Ich geh zurück in mein Hotel. + +[INT4_M:INTRO] +Ich muss nachdenken, was schiefgelaufen ist. + +[INT4_N:INTRO] +Ok. + +[INTRO1:INTRO] +Da passe ich ein einziges Mal nicht auf, und prompt kriege ich eins reingewürgt. + +[INTRO2:INTRO] +Leg dich schlafen. + +[INTRO3:INTRO] +Was hast du vor? + +[INTRO4:INTRO] +Ich komme morgen zu dir ins Büro, dann sehen wir weiter. + +[INT3_K:INTRO] +Tja, der Deal kann steigen, mein Freund. + +[INT3_M:INTRO] +Zeig her. + +[INT2_L:INTRO] +Nein, nein, nein, wartet... + +{=================================== MISSION TABLE KENT1 ===================================} + +[KPM1_A:KENT1] +Ok, du Knallkopf, ich werde deine Haut retten. + +[KPM1_B:KENT1] +Was sagst du? + +[KPM1_C:KENT1] +Du kennst doch Diaz, den Idioten, den Koks-König? + +[KPM1_D:KENT1] +Er hat deinen Freund Lance. Es heißt, dein Kumpel wollte ihm an den Kragen. + +[KPM1_E:KENT1] +Hat ihn aber fast den eigenen gekostet hat, falls du weißt, was ich meine. + +[KPM1_F:KENT1] +Wo hat er ihn hingebracht? Im Klartext! + +[KPM1_G:KENT1] +Mach dich mal locker! Sie haben ihn beim Schrottplatz erwischt. + +[KPM1_H:KENT1] +Verdammt noch mal. Psycho. + +[KPM1_2:KENT1] +~r~Du solltest Lance lebend da rausholen! + +[KPM1_3:KENT1] +Lance' Gesundheitszustand: + +[RESC_1:KENT1] +Kannst du eine Waffe halten? + +[RESC_2:KENT1] +Klar, glaub schon. Freut mich, dich zu sehen. + +[RESC_3:KENT1] +Los, wir verschwinden von hier. + +[RESC_4:KENT1] +Dank dir ist mein ganzer schöner Plan im Eimer. Das hast du sauber vermasselt, Lance. + +[RESC_5:KENT1] +Er hat meinen Bruder auf dem Gewissen. Soll ich ihm den Rasen mähen? + +[RESC_6:KENT1] +Wir müssen diesen Diaz erledigen bevor er uns erledigt. + +[RESC_7:KENT1] +Lass dich zusammenflicken, dann treffen wir uns auf der Brücke nach Star Island, ok? + +[RESC_8:KENT1] +Ok, alles klar. + +[KPM1_1:KENT1] +~g~Lance wird auf dem Schrottplatz gefangengehalten. Rette ihn! + +[KPM1_4:KENT1] +~g~Bring Lance ins Krankenhaus! + +[M_PASSN:KENT1] +MISSION ERFÜLLT! + +[KPM1_5:KENT1] +~g~Diaz' Leute sind hinter euch her. Bring Lance ins Krankenhaus. + +{=================================== MISSION TABLE KICKSTT ===================================} + +[KICK1_2:KICKSTT] +~r~Du warst nicht schnell genug bei der Maschine! + +[KICK1_7:KICKSTT] +~r~Du hast die Maschine geschrottet! + +[KICK1_8:KICKSTT] +~g~Setz dich auf das Motorrad! + +[KICK1_T:KICKSTT] +BENÖTIGTE ZEIT: + +[KICKTM:KICKSTT] +~b~ZEIT: ~1~:~1~ + +[KICKTM2:KICKSTT] +~b~ZEIT: ~1~:0~1~ + +[GETBIKE:KICKSTT] +~g~Du hast ~1~ Sekunden, um zu einer Geländemaschine zurückzukehren, bevor die Mission endet. + +[KICK1_1:KICKSTT] +~g~Absolviere den Kurs so schnell wie möglich. + +[KICK1_6:KICKSTT] +~g~Gut gemacht! + +[KICK_10:KICKSTT] +~G~Nimm den Sanchez und absolviere den Kurs, indem du alle Checkpoints passierst. + +[KICK_12:KICKSTT] +~r~Du hast es vermasselt! + +[KICK_13:KICKSTT] +~r~Du hast zu lange gebraucht! + +[KICK_11:KICKSTT] +~g~Um die Mission zu beenden, stell dich zu Fuß in die ~q~rosa Markierung~g~. + +{=================================== MISSION TABLE LAWYER1 ===================================} + +[LAW1_A:LAWYER1] +Leg dich schlafen, sagt er - + +[LAW1_B:LAWYER1] +- Ich sitz die ganze Nacht im Dunklen hier rum und trink Kaffee! + +[LAW1_C:LAWYER1] +Das ist eine Katastrophe. Wir sind so gearscht, Mann! + +[LAW1_D:LAWYER1] +Diese Gorillas, die kommen hier runter und reißen mir den Kopf ab. Es ist beinahe zum lachen! + +[LAW1_E:LAWYER1] +Dafür habe ich NICHT Jura studiert. Ok, was sollen wir jetzt machen? + +[LAW1_F:LAWYER1] +Sei still, setz dich hin und bleib ruhig. Ich sag dir, was wir machen. + +[LAW1_G:LAWYER1] +Du findest raus, wer das Koks geklaut hat - und ich nehm ihn mir vor. + +[LAW1_H:LAWYER1] +Gute Idee. SEHR gute Idee. Lass mich nachdenken, lass mich nachdenken. + +[LAW1_I:LAWYER1] +OH! Da gibt es diesen Colonel a.D., Colonel Juan Garcia Cortez. + +[LAW1_J:LAWYER1] +Der half mir, diesen Deal einzufädeln, + +[LAW1_K:LAWYER1] +und zwar ohne Vice Citys Gangster-Establishment. Ok? + +[LAW1_L:LAWYER1] +Pass auf, der gibt eine Party in der Bucht, auf seiner Luxusjacht + +[LAW1_M:LAWYER1] +da kommt alles, was in Vice City Rang und Namen hat. + +[LAW1_N:LAWYER1] +Ich hab natürlich eine Einladung, versteht sich, + +[LAW1_O:LAWYER1] +aber mich kriegen keine zehn Pferde hier raus. Auf keinen Fall! + +[LAW1_P:LAWYER1] +Halt die Klappe! Ich geh selbst hin... + +[LAW1_Q:LAWYER1] +Moment! Hey, ich steh ja auch auf den 78er Look , aber das wird dort kein nostalgisches Saufgelage. + +[LAW1_R:LAWYER1] +Ich meine, nichts gegen dich, aber mit den Klamotten wirst du dort ziemlich blöd angeschaut. + +[LAW1_S:LAWYER1] +Wieso? Was ist mit meinen Sachen? + +[LAW1_T:LAWYER1] +Pass auf. Fahr zu Rafael. Sag ihm, ich schicke dich, und er soll dich ordentlich einkleiden. + +[LAW1_U:LAWYER1] +Ok, Los jetzt. Mach hinne... + +[LAWP_1:LAWYER1] +Guten Abend. + +[LAWP_2:LAWYER1] +Ich höre, Sie sind anstelle von Mr. Rosenberg hier. + +[LAWP_3:LAWYER1] +Ich hoffe, gewisse Vorfälle haben seiner Gesundheit nicht geschadet, + +[LAWP_4:LAWYER1] +oder seiner Psyche, Mr...äh? + +[LAWP_5:LAWYER1] +Vercetti. Er leidet ein wenig an...Platzangst. + +[LAWP_6:LAWYER1] +Ausgezeichnet, ausgezeichnet. Und Sie? + +[LAWP_7:LAWYER1] +Ich will nur meine Ware. + +[LAWP_8:LAWYER1] +Ah. Eine missliche Lage für alle Beteiligten. + +[LAWP_9:LAWYER1] +Natürlich stelle ich selbst Nachforschungen an, aber + +[LAWP_10:LAWYER1] +bei solch heiklen Sachen dauert das ein wenig. + +[LAWP_11:LAWYER1] +Wir sprechen uns vielleicht später. Hm? + +[LAWP_12:LAWYER1] +Inzwischen möchte ich Ihnen meine Tochter vorstellen, + +[LAWP_13:LAWYER1] +Mercedes! + +[LAWP_14:LAWYER1] +Könntest du dich um unseren Gast kümmern, während ich mich um andere Dinge + +[LAWP_15:LAWYER1] +Natürlich, Daddy. + +[LAWP_16:LAWYER1] +Entschuldigen Sie mich, bitte. + +[LAWP_17:LAWYER1] +Mercedes!? + +[LAWP_18:LAWYER1] +Leb du mal mit so 'nem Namen. + +[LAWP_19:LAWYER1] +Na gut, ich zeig dir mal einige unserer bekannteren Gäste... + +[LAWP_20:LAWYER1] +Das ist unser Abgeordneter Alex Shrub mit dem aufgehenden Sternchen Candy Suxx.. + +[LAWP_21:LAWYER1] +Und kennen Sie schon meine reizende Frau Laura? Nein? + +[LAWP_22:LAWYER1] +Nun, leider ist sie in Alabama. Das hier ist Candy. + +[LAWP_23:LAWYER1] +Und hier haben wir den Star-Verteidiger der Vice City Mambas, BJ. + +[LAWP_24:LAWYER1] +Immer charmant. + +[LAWP_25:LAWYER1] +Ich hab ihn voll geblockt. Der sitzt heute im Rollstuhl! + +[LAWP_26:LAWYER1] +Haha, das ist gut! + +[LAWP_27:LAWYER1] +Tja, ich bin an einem super Grundstück dran. + +[LAWP_28:LAWYER1] +Und der Schleimbold dort ist Jezz Torrent, + +[LAWP_29:LAWYER1] +Der Sänger von 'Love Fist'. + +[LAWP_30:LAWYER1] +Wisst ihr, wie sie in Thailand Pingpong spielen? + +[LAWP_31:LAWYER1] +Ich verrat's euch, + +[LAWP_32:LAWYER1] +man spielt ohne Schläger, wenn ihr wisst, was ich meine! + +[LAWP_33:LAWYER1] +Impotent. + +[LAWP_34:LAWYER1] +Und das schwatzhafte Trio. + +[LAWP_35:LAWYER1] +Diese schlafende Schweißfabrik ist Papas Obersklave, Gonzalez. + +[LAWP_36:LAWYER1] +und die anderen beiden sind Pastor Richards + +[LAWP_37:LAWYER1] +und der pseudo-intellektuelle Regisseur Steve Scott. + +[LAWP_38:LAWYER1] +....leidenschaftlich mit den nymphomanischen Aliens + +[LAWP_39:LAWYER1] +Da kommt der riesige Hai und + +[LAWP_40:LAWYER1] +beißt ihnen ihr Ding ab! + +[LAWP_41:LAWYER1] +Ha! So was hat doch die Welt noch nicht gesehen, oder? + +[LAWP_42:LAWYER1] +Colonel! + +[LAWP_43:LAWYER1] +Ihre Party ist wie immer fantastisch, hahahaha! + +[LAWP_44:LAWYER1] +Ich entschuldige mich für die Verspätung. + +[LAWP_45:LAWYER1] +Ah, nicht doch, Amigo. Wie geht es ihnen? + +[LAWP_46:LAWYER1] +Unsere Geschäfte laufen schwierig - die Barbaren stehen vor den Toren. + +[LAWP_47:LAWYER1] +Eine Zeit, Freunde zu belohnen und Feinde auszuschalten, Amigo. + +[LAWP_48:LAWYER1] +Wer ist das Großmaul? + +[LAWP_49:LAWYER1] +Ricardo Diaz. Er ist Mr. Koks. + +[LAWP_50:LAWYER1] +Mercedes! + +[LAWP_51:LAWYER1] +Oh, ich will gerade meinen Freund in die Stadt bringen. + +[LAWP_52:LAWYER1] +Ein andermal, Ricardo! + +[LAWP_53:LAWYER1] +Lass uns verschwinden. + +[LAWP_54:LAWYER1] +Fahr mich zum Pole Position Club. + +[LAW1_2:LAWYER1] +~g~Begib dich zur Jacht des Colonels. + +[LAW1_4:LAWYER1] +~r~Du hast die Tochter des Colonels erledigt! + +[LAW1_5:LAWYER1] +Wirst du für meinen Vater arbeiten? + +[LAW1_6:LAWYER1] +Vielleicht. + +[LAW1_7:LAWYER1] +Darf ich meine Hand in deinen Schoß legen? + +[LAW1_8:LAWYER1] +Vielleicht... + +[LAW1_9:LAWYER1] +Es ist schwer, einen so reichen, mächtigen Vater zu haben. Los. + +[LAW1_10:LAWYER1] +Wir sehen uns, mein Hübscher! + +[LAW1_11:LAWYER1] +Ganz bestimmt. + +[LAW1_12:LAWYER1] +Hmm, netter Ofen. + +[LAW1_13:LAWYER1] +Nein! Mein Motorrad! + +[LAW1_3:LAWYER1] +~g~Bring die Tochter des Colonels zum Pole Position Club. + +[HELP20:LAWYER1] +Folge dem ~h~T-shirt-Symbol~w~ auf dem Radar, um Rafael's zu finden. + +[LAW1_14:LAWYER1] +Wow, das ist ja wirklich ein tolles Motorrad. + +[LAW1_15:LAWYER1] +Ja, Baby, hab ich mir gerade bei Howlin' Pete's besorgt. + +{=================================== MISSION TABLE LAWYER2 ===================================} + +[LAW2_A:LAWYER2] +Ah! Tja, ich hoffe, du amüsierst dich gut, während ich hier vor Angst halb umkomme. Was hast du rausgefunden? + +[LAW2_B:LAWYER2] +Dass es in dieser Stadt mehr Gangster gibt als im Knast. Wir brauchen einen Tipp von der Straße... + +[LAW2_C:LAWYER2] +Ok, lass mich nachdenken, lass mich nachdenken - + +[LAW2_D:LAWYER2] +- AH! Ich hab's! + +[LAW2_E:LAWYER2] +Ok, es gibt da so 'n Engländer, so 'n Idiot aus der Musikbranche. + +[LAW2_F:LAWYER2] +Er nennt sich Kent Paul. + +[LAW2_G:LAWYER2] +Und der ist in all den Kreisen von Vice City richtig dick drin. + +[LAW2_H:LAWYER2] +Wenn einer weiß, wo 20 Kilo Koks abgeblieben sind, + +[LAW2_I:LAWYER2] +dann dieser Typ. Er ist immer im Malibu. + +[LAW2_J:LAWYER2] +Ich seh ihn mir mal an. + +[LAW2B_A:LAWYER2] +Wo kommst du denn her? + +[LAW2B_B:LAWYER2] +Nach einer wie dir suche ich schon seit Ewigkeiten. + +[LAW2B_C:LAWYER2] +Kent Paul. Ja, ich bin hier die Nummer Eins. + +[LAW2B_D:LAWYER2] +Ich suche einen Engländer... + +[LAW2B_E:LAWYER2] +Ich ziehe hier die Strippen, verstehst du? + +[LAW2B_F:LAWYER2] +Ich lade dich ein. Ich kann dir alles besorgen, Süße. + +[LAW2B_G:LAWYER2] +Mach dir keine Gedanken. + +[LAW2B_H:LAWYER2] +Verzieh dich, Schätzchen. + +[LAW2B_I:LAWYER2] +Oi oi oi oi! + +[LAW2B_J:LAWYER2] +Bist du Kent Paul? Ich bin ein Freund von Rosenberg... + +[LAW2B_K:LAWYER2] +Rosenberg...Rosenberg... Ach, dieser abgedrehte Winkeladvokat! + +[LAW2B_L:LAWYER2] +Der bringt noch den Unschuldigsten auf den elektrischen Stuhl! + +[LAW2B_M:LAWYER2] +Mach uns noch 'nen Drink, mein Freund. + +[LAW2B_N:LAWYER2] +Bist ein echter Komiker. + +[LAW2B_O:LAWYER2] +Hör zu, ich vermisse 20 Kilo und einen Haufen Geld... + +[LAW2B_P:LAWYER2] +Drogen? Das ist doch Schwachsinn. + +[LAW2B_Q:LAWYER2] +Was weißt du darüber? + +[LAW2B_R:LAWYER2] +Oi, oi! Gerade wollte ich's sagen... + +[LAW2B_S:LAWYER2] +Es gibt da einen Koch, der verdealt Koks in der Küche eines Hotels am Ocean Drive. + +[LAW2B_T:LAWYER2] +Macht einen ziemlich zufriedenen Eindruck in letzter Zeit. Solltest du mal auschecken. + +[LAW2B_U:LAWYER2] +Mach ich. Und wir sehen uns. + +[LAW2B_V:LAWYER2] +Ja, ja. Nur zu. Hau bloß ab, du Stinktier. Dir polier ich noch die Visage! + +[LAW2B_W:LAWYER2] +Gib mir 'nen Drink. Und wo ist die Puppe? + +[LAW2C_A:LAWYER2] +Oh, sehr gut, Rambo, schlag ihn ruhig zu Brei. Dann redet er ganz bestimmt. + +[LAW2C_B:LAWYER2] +Willst du auch ein paar? + +[LAW2C_C:LAWYER2] +Hey, ruhig. Ich will das gleiche wie du, Bruder. + +[LAW2C_D:LAWYER2] +Ach ja? Und das wäre? + +[LAW2C_E:LAWYER2] +Deine Kohle und den Stoff meines toten Bruders. Aber du hast gerade unsere Spur erledigt. + +[LAW2C_F:LAWYER2] +Dumm gelaufen. Verzieh dich. + +[LAW2C_G:LAWYER2] +Hey, hey! Kein Grund, hier den dicken Mann zu spielen. + +[LAW2C_H:LAWYER2] +Sieh mal: Wir sind zwei Hombres in 'ner fremden Stadt. Wir sollten uns gegenseitig helfen. + +[LAW2C_I:LAWYER2] +Ich helf mir selbst, Bruder. + +[LAW2C_J:LAWYER2] +Bist du dir sicher? Hier nimm das. + +[LAW2C_K:LAWYER2] +Komm mit! + +[LAW2_1:LAWYER2] +Was glotzt du denn so? + +[LAW2_2:LAWYER2] +Rede endlich... + +[LAW2_3:LAWYER2] +Zwing mich doch dazu, du Pfeife! + +[LAW2_4:LAWYER2] +Mir nach! + +[LAW2_5:LAWYER2] +Ich seh zu, was ich rausfinde. Ich behalte dich im Auge, Tommy. + +[LAW2_6:LAWYER2] +~g~Begib dich zum Malibu Club und suche Kent Paul. + +[LAW2_7:LAWYER2] +~g~Suche den Küchenchef auf dem Ocean Drive. + +[LAW2_10:LAWYER2] +~g~Fahre zurück zum Hotel. + +[LAW2_11:LAWYER2] +~g~Nimm sein Handy. + +[LAW2_12:LAWYER2] +Du hast jetzt ein Handy und kannst Telefongespräche entgegennehmen! + +[LAW2_13:LAWYER2] +~g~Du hast Lance zurückgelassen! Geh ihn holen! + +[LAW2_14:LAWYER2] +Verdammt, nichts wie weg hier! + +[GUN_2A:LAWYER2] +Halte die ~h~~k~~PED_LOCK_TARGET~ ~w~gedrückt, um ~h~automatisch zu zielen~w~. Drücke die ~h~~k~~PED_FIREWEAPON~~w~, um zu ~h~feuern! + +[GUN_2C:LAWYER2] +Halte die ~h~~k~~PED_LOCK_TARGET~ ~w~gedrückt, um ~h~automatisch zu zielen~w~. Drücke die ~h~~k~~PED_FIREWEAPON~~w~, um zu ~h~feuern! + +[GUN_2D:LAWYER2] +Halte die ~h~~k~~PED_LOCK_TARGET~ ~w~gedrückt, um ~h~automatisch zu zielen~w~. Drücke die ~h~~k~~PED_FIREWEAPON~~w~, um zu ~h~feuern! + +[HELP17:LAWYER2] +Drücke die ~h~~k~~PED_FIREWEAPON~~w~, um den Küchenchef anzugreifen. + +[HELP18:LAWYER2] +Drücke die~h~ ~k~~PED_FIREWEAPON~~w~, um den Küchenchef anzugreifen. + +[LAW3_11:LAWYER2] +Stell dich in die ~q~rosa Markierung~w~, um zu sehen, was im Angebot ist. + +[LAW3_12:LAWYER2] +Du kannst Waffen auswählen, indem du die ~h~linke~w~ oder ~h~rechte~w~ ~h~Richtungstaste drückst. + +[LAW3_13:LAWYER2] +Wenn du genug Geld hast, kannst du Waffen durch Drücken der ~h~~k~~PED_SPRINT~~w~ kaufen. + +[LAW3_14:LAWYER2] +Um zu gehen, drücke die ~h~~k~~VEHICLE_ENTER_EXIT~ + +[LAW3_15:LAWYER2] +Folge dem ~h~Pistolensymbol~w~ auf dem Radar, so kommst du zu AmmuNation. + +[LAW2_15:LAWYER2] +~g~Begib dich zu AmmuNation. + +[LAW2_K:LAWYER2] +Nur die Ruhe. + +[LAW2_16:LAWYER2] +Eins musst du wissen: In dieser Stadt darfst du nie unbewaffnet sein. + +[LAW2_17:LAWYER2] +Komm, der nächste Waffenladen ist ein paar Blocks von hier. + +[LAW2_18:LAWYER2] +Tommy, jeder Mann braucht ab und zu mal ein bisschen Entspannung. + +[LAW2_19:LAWYER2] +Das ist die Pole Position Stripper-Bar. Solltest du bei Gelegenheit mal reinschauen. + +{=================================== MISSION TABLE LAWYER3 ===================================} + +[_A:LAWYER3] +Arrgh! Ach, du lieber Gott! Du! Meine Güte, ich brauch 'ne neue Hose! + +[LAW3_B:LAWYER3] +Hey, diese Psychos aus dem Norden haben angerufen. Sie kommen bald hier runter. + +[LAW3_C:LAWYER3] +Also, wo ist das verdammte Geld? + +[LAW3_D:LAWYER3] +Ganz ruhig. Soweit sind wir noch nicht. + +[LAW3_E:LAWYER3] +Ich hab wirklich gedacht, du erledigst das. + +[LAW3_F:LAWYER3] +Und jetzt sagen diese Gauner, wir sollen ihnen einen Gefallen tun. + +[LAW3_G:LAWYER3] +Du meinst, ICH soll ihnen einen Gefallen tun. + +[LAW3_H:LAWYER3] +Du sagst es. Sehe ich aus, als könnte ich Geschworene einschüchtern? + +[LAW3_I:LAWYER3] +Ich kann nicht mal ein Kind einschüchtern. Ich hab's versucht. + +[LAW3_J:LAWYER3] +Hör zu, wenn du kneifst, kriegt Forellis Cousin Georgio 5 Jahre wegen Betrugs. + +[LAW3_K:LAWYER3] +Du musst diese Typen ausschalten. + +[LAW3_L:LAWYER3] +Verstehe. Den Geschworenen helfen 'umzudenken'. Kein Problem. + +[LAW3_M:LAWYER3] +Nein, nein, nein! Das hab ich schon versucht. Das ist nicht gelaufen, + +[LAW3_N:LAWYER3] +ZWING sie dazu, umzudenken. + +[LAW3_1:LAWYER3] +Georgio lässt grüßen. + +[LAW3_2:LAWYER3] +Denk dran, 'Schuldig' ist ein hässliches Wort. + +[LAW3_3:LAWYER3] +'Unschuldig', bis ich was anderes sage. + +[LAW3_4:LAWYER3] +Er ist nicht schuldig. + +[LAW3_5:LAWYER3] +Du kennst Georgio? Merk dir: Er ist nicht schuldig. + +[LAW3_6:LAWYER3] +Nicht schuldig. Verstanden...gut. + +[LAW3_8:LAWYER3] +~r~Du hast einen Geschworenen erledigt! + +[LAW3_9:LAWYER3] +~g~Schrotte das Auto des Geschworenen, damit er aussteigt! + +[HELP40:LAWYER3] +Du kannst Autos mit dem Hammer oder einer ähnlichen Waffe zertrümmern. + +[HELP41:LAWYER3] +Oder du kannst sie mit einem Fahrzeug rammen. + +[LAW3_10:LAWYER3] +~g~Eine Nahkampfwaffe kannst du im ~h~Eisenwarenladen~g~ kaufen. + +[LAW3_20:LAWYER3] +~g~Schrotte das Auto des Geschworenen! + +[LAW3_21:LAWYER3] +Das kann doch wohl nicht wahr sein! + +[LAW3_22:LAWYER3] +Unglaublich! + +[LAW3_23:LAWYER3] +Ok! Ok! Ich hab kapiert! + +[LAW3_24:LAWYER3] +~g~Dieser Hammer wäre nützlich. + +[LAW3_7:LAWYER3] +~g~Schüchtere die 2 Geschworenen ein, aber erledige sie NICHT! + +[HELP23:LAWYER3] +Folge dem ~h~Hammer-Symbol~w~ auf dem Radar, wenn du beim Eisenwarenladen Nahkampfwaffen kaufen willst. + +[LAW3_16:LAWYER3] +Dämlicher Florida-Idiot! + +[LAW3_17:LAWYER3] +Aus dem Weg! + +{=================================== MISSION TABLE LAWYER4 ===================================} + +[LAW4_A:LAWYER4] +Avery, es versteht sich von selbst... Tommy! Tommy! Fortschritte gemacht? Nein, erzähl's mir später. + +[LAW4_B:LAWYER4] +Tommy, das ist Avery Carrington. Kennt ihr euch nicht von der Party? + +[LAW4_C:LAWYER4] +Nicht persönlich. + +[LAW4_D:LAWYER4] +Tagchen. + +[LAW4_E:LAWYER4] +Avery hat einen Vorschlag für uns. + +[LAW4_F:LAWYER4] +Haben wir nicht was besseres zu tun? + +[LAW4_G:LAWYER4] +Ich versuch hier, unseren Hals zu retten. Also würdest du mich bitte ausreden lassen? + +[LAW4_H:LAWYER4] +Ich hab Angst. Aber wenn ich schon Ende der Woche sterbe, möchte ich wenigstens nicht arm sterben. + +[LAW4_I:LAWYER4] +Jetzt beruhigt euch, ihr beiden. + +[LAW4_J:LAWYER4] +Junge, wenn du mir hilfst, sorge ich dafür, dass jeder, der dir Ärger macht, unter die Erde kommt. + +[LAW4_K:LAWYER4] +Ok, was kann ich für Sie tun? + +[LAW4_L:LAWYER4] +Eine Spedition hat ihr Lager auf einem Top-Grundstück - und will nicht verkaufen. + +[LAW4_M:LAWYER4] +Die sitzen da drauf wie die Ratten in ihren Löchern. Also müssen wir dieses Ungeziefer ausräuchern. + +[LAW4_N:LAWYER4] +Fahr hin und stich ein wenig ins Wespennest. + +[LAW4_O:LAWYER4] +Das wird die Security beschäftigen. Dann schleichst du dich rein und machst den Laden platt. + +[LAW4_P:LAWYER4] +Und du könntest dich bei Rafael's neu einkleiden. Kann 'ne Weile dauern, aber mach das ruhig mal. + +[LAW4_Q:LAWYER4] +Das gibt ein Fest. + +[LAW4_R:LAWYER4] +Wenn alles läuft wie geplant, komm mal zu mir ins Büro... + +[LAW4_1:LAWYER4] +Bitte, geht auseinander! Die Geschäftsleitung wird sich aller Probleme annehmen! + +[LAW4_2:LAWYER4] +Bitte, geht auseinander! Geht wieder nach Hause! + +[LAW4_3:LAWYER4] +Bitte, geht auseinander! Das ist nicht akzeptabel! + +[LAW4_4:LAWYER4] +Bitte, geht auseinander! Ihr landet alle auf der Straße! + +[LAW4_5:LAWYER4] +Die Knüppel raus, Jungs! Diesen Kommis zeigten wir's! + +[LAW4_13:LAWYER4] +Fange mit mind. 4 Arbeitern Streit an, um einen Aufruhr zu starten. + +[LAW4_14:LAWYER4] +~g~Zerstöre die Transporter auf dem Gelände! + +[HELP38:LAWYER4] +Wenn du jemanden ausschaltest, der eine Waffe trägt, lässt er sie fallen. + +[HELP39:LAWYER4] +Du kannst explosive Fässer anvisieren und abschießen, aber bleib auf Distanz. + +{=================================== MISSION TABLE MIAMI_1 ===================================} + +[T4X4_1A:MIAMI_1] +~g~Du hast ~1~ Sekunden, um ~y~24~g~ Checkpoints abzufahren. ~g~Die ~y~REIHENFOLGE IST BELIEBIG. + +[T4X4_1B:MIAMI_1] +~y~PASSIERE~g~ den ersten Checkpoint, dann läuft die ~r~STOPPUHR. + +[T4X4_1C:MIAMI_1] +~1~ von 24! + +[GETBIK1:MIAMI_1] +Du hast ~1~ Sekunden, um auf eine PCJ 600 zu steigen! + +[GETBIK3:MIAMI_1] +~r~Du brauchst eine PCJ 600, um diese Mission durchzuführen! + +{=================================== MISSION TABLE MM ===================================} + +[BLOD_04:MM] +ZUSTAND AUTO: + +[BLOD_05:MM] +~g~ZIELZEIT: ~1~ Minute + +[BLOD_06:MM] +~g~ZIELZEIT: ~1~ Minuten + +[BLOD_07:MM] +NEUE Bestzeit: ~1~ Sekunden + +[BLOD_08:MM] +Zerstörte Autos: ~1~ + +[BLOD_09:MM] +$~1~ + +[BLOD_10:MM] +SIEGER!! + +[BLOD_01:MM] +Fahr durch die Checkpoints, um deine Gesamtzeit zu verlängern. + +[BLOD_02:MM] +Wenn die Gesamtzeit abgelaufen ist, hast du versagt. + +[BLOD_03:MM] +Um zu gewinnen, muss deine Gesamtzeit die Zielzeit überschreiten! + +{=================================== MISSION TABLE OVALRIG ===================================} + +[HOTR_01:OVALRIG] +~g~Das Rennen geht über 12 Runden. Nur die ersten drei Plätze qualifizeren für einen Gewinn. + +[HOTR_02:OVALRIG] +~g~Wird dein Auto zerstört, wirst du disqualifiziert. + +[HOTR_03:OVALRIG] +~g~Wird dein Auto beschädigt, kannst du es an der Box reparieren lassen. + +[HOTR_04:OVALRIG] +~g~Da geht es aus dem Stadion raus. + +[HOTR_05:OVALRIG] +Zustand Auto: + +[HOTR_06:OVALRIG] +Runden: + +[HOTR_07:OVALRIG] +Neue Bestzeit: ~1~:0~1~ + +[HOTR_08:OVALRIG] +Zeit: ~1~:~1~ + +[HOTR_10:OVALRIG] +Absolvierte Zeit: + +[HOTR_09:OVALRIG] +Position: + +[HOTR_12:OVALRIG] +~r~Dein Fahrzeug ist zerstört worden! + +[HOTR_13:OVALRIG] +~r~Du hast das Rennen nicht gewonnen! + +[HOTR_14:OVALRIG] +~r~Du bist disqualifiziert worden! + +[HOTR_15:OVALRIG] +Zeit: ~1~:~1~ + +[HOTR_16:OVALRIG] +Zeit: ~1~:0~1~ + +[HOTR_17:OVALRIG] +Bestzeit: ~1~:~1~ + +[HOTR_18:OVALRIG] +Bestzeit: ~1~:0~1~ + +[HOTR_19:OVALRIG] +Bestzeit: Nicht verfügbar + +[HOTR_20:OVALRIG] +Neue Bestzeit: ~1~:~1~ + +[HOTR_21:OVALRIG] +Neue Bestzeit: ~1~:0~1~ + +[HOTR_22:OVALRIG] +Beste Platzierung: Nicht verfügbar + +[HOTR_23:OVALRIG] +Beste Platzierung: 1. + +[HOTR_24:OVALRIG] +Beste Platzierung: 2. + +[HOTR_25:OVALRIG] +Beste Platzierung: 3. + +[HOTR_26:OVALRIG] +Beste Platzierung: ~1~. + +[HOTR_27:OVALRIG] +Beste Rundenzeit: ~1~.~1~ Sekunden + +[HOTR_28:OVALRIG] +Beste Rundenzeit: ~1~.0~1~ Sekunden + +[HOTR_29:OVALRIG] +$~1~ + +[HOTR_30:OVALRIG] +1. PLATZ + +[HOTR_31:OVALRIG] +2. PLATZ + +[HOTR_32:OVALRIG] +3. PLATZ + +[HOTR_33:OVALRIG] +Beste Rundenzeit: Nicht verfügbar + +[HOTR_11:OVALRIG] +Neue beste Rundenzeit: ~1~.~1~ Sekunden + +[HOTR_34:OVALRIG] +Neue beste Rundenzeit: ~1~.0~1~ Sekunden + +{=================================== MISSION TABLE PHIL1 ===================================} + +[PHI1_HP:PHIL1] +Wenn du Granaten mit Fernzünder benutzt, wirf die Granate, dann löse die Explosion zu einem beliebigen Zeitpunkt aus. + +[PHIL1_A:PHIL1] +Phil? + +[PHIL1_B:PHIL1] +SCHNELL WEG! + +[PHIL1_C:PHIL1] +Schnell weg! + +[PHIL1_E:PHIL1] +Scheiße, Phil, das Zeug trinkst du? + +[PHIL1_F:PHIL1] +Hey, du musst es nicht trinken. + +[PHIL1_G:PHIL1] +Das haut schon rein, wenn du nur dran riechst. + +[PHIL1_H:PHIL1] +Hör mal, Phil, du sagtest, du könntest mir Artillerie besorgen... + +[PHIL1_I:PHIL1] +Klar. + +[PHIL1_J:PHIL1] +In letzter Zeit macht mir ein mexikanischer Waffenschieber Konkurrenz. + +[PHIL1_K:PHIL1] +Der müsste jetzt gerade auf seiner wöchentlichen Runde sein. + +[PHIL1_L:PHIL1] +Ramm mit deiner Karre die Ware von seinem Wagen runter, bevor er wieder abtaucht. + +[PHIL1_M:PHIL1] +Tust mir einen großen Gefallen damit. + +[PHIL1_N:PHIL1] +Und dann mach ihn fertig. + +[PHI1_01:PHIL1] +~g~Ramme die Waffen von der Ladefläche des Waffenschiebers. + +[PHI1_02:PHIL1] +~g~Der Waffenhändler hat die Ladung verloren. Schlag die Kiste kaputt und nimm die Waffe. + +[PHI1_03:PHIL1] +~g~Sie haben anscheinend Verstärkung gerufen. + +[PHI1_04:PHIL1] +~g~Jetzt erledige die restlichen Waffenschieber. + +[PHI1_06:PHIL1] +Pass doch auf, wo du hinfährst! + +[PHI1_07:PHIL1] +Hey! + +[PHIL1_O:PHIL1] +Huuuuhuuu! + +[PHIL1_D:PHIL1] +Komm nie mit 'ner offenen Flamme in die Nähe von Phil Cassidys TNT-Whiskey! + +{=================================== MISSION TABLE PHIL2 ===================================} + +[PHIL2_A:PHIL2] +Hey, Phil, wie geht's? + +[PHIL2_B:PHIL2] +Hey, Tommy. Alles klar? Lange nicht gesehen... + +[PHIL2_C:PHIL2] +Du solltest wirklich die Finger von dem TNT-Whiskey lassen. + +[PHIL2_D:PHIL2] +Das Zeug riecht ja wie Terpentin. Mir brennen schon die Augen. + +[PHIL2_E:PHIL2] +Lass stecken, Tommy. + +[PHIL2_F:PHIL2] +Komm hier rüber, ich will dir nämlich was zeigen. + +[PHIL2_G:PHIL2] +Wahnsinn! Das rieche ich ja schon von hier. Mir ist schon ganz schwindlig. + +[PHIL2_H:PHIL2] +Kümmer dich nicht um den Geruch. Tommy, sieh dir das an. + +[PHIL2_I:PHIL2] +Billige Schrottbatterien. Da auf der Bank sind noch welche. + +[PHIL2_J:PHIL2] +Tata! + +[PHIL2_K:PHIL2] +Oh, verflucht! + +[PHI2_01:PHIL2] +~g~Schnell, bring Phil ins Krankenhaus. + +[PHI2_03:PHIL2] +~r~Phil Cassidy ist tot!!! Wer soll Liberty nun mit Waffen versorgen? + +[PHI2_05:PHIL2] +Nicht ins Krankenhaus, Mann! Zu viele Cops und Vietcong! + +[PHI2_06:PHIL2] +Ich kenn 'nen Ex-Army-Arzt, der schuldet mir einen Gefallen und 'nen Rasenmäher. + +[PHI2_07:PHIL2] +Er hat 'ne Praxis unten in Little Havana. Uh, guck mal, ein Riesenfisch. + +[PHI2_08:PHIL2] +Achtung! Da in den Bäumen - Vietcong! + +[PHI2_09:PHIL2] +Spinn ich, oder ist die Straße aus Gummi? + +[PHI2_10:PHIL2] +Broken Spoon an Mother Hen, bitte kommen! + +[PHI2_11:PHIL2] +Spooney Wooney Woo Woo Woooo! + +[PHI2_12:PHIL2] +Er kommt mich holen, Jungchen! + +[PHI2_13:PHIL2] +Schwarze Schwingen breiten sich über mir aus... + +[PHI2_14:PHIL2] +Es ist wunderschön, Mann. Wunderschön... mir ist nur so kalt... + +[PHI2_15:PHIL2] +Roger! Wir haben einen betrunkenen Fahrer. + +[PHI2_04:PHIL2] +Phils Gesundheitszustand: + +[PHI_AS1:PHIL2] +PHIL-MISSIONEN ERFÜLLT + +[PHI_AS2:PHIL2] +~g~Bei Phil gibt es neue Waffen zu kaufen. + +{=================================== MISSION TABLE PIZZA ===================================} + +[PIZ1_01:PIZZA] +~g~Liefere diese Pizzas aus. Du musst den Kunden die Pizzas zuwerfen, während du an ihnen vorbeifährst. + +[PIZ1_02:PIZZA] +~g~Du hast alle Pizzas zugestellt. Fahr zurück und hole noch mehr. + +[PIZ1_05:PIZZA] +~g~Du hast 5 Minuten, um die Pizzas zu liefern, sonst rufen die Kunden einen anderen Pizza-Service an. + +[PIZ1_07:PIZZA] +~r~Du hast den Kunden erledigt! Du bist gefeuert. + +[PIZ1_08:PIZZA] +~r~Die Zeit ist um. Du bist gefeuert. + +[PIZ1_09:PIZZA] +~r~Du hast dein Motorrad geschrottet! Du bist gefeuert. + +[PIZ1_11:PIZZA] +Hey! Steig wieder aufs Motorrad! + +[PIZ1_12:PIZZA] +Verbleibende Pizzas: + +[PIZ1_06:PIZZA] +Drücke die ~h~ R3-Taste~w~, wenn du auf einem Bike sitzt und die Mission abbrechen willst. + +[PIZ1_13:PIZZA] +Liefere sie schön heiß ab. + +[PIZ1_14:PIZZA] +Kumpel, Pizzas für dich. + +[PIZ1_15:PIZZA] +Hey, na los, Mister, liefere sie schnell aus. + +[PIZ1_16:PIZZA] +Worauf wartest du, Mister? Du sollst Pizzas liefern. + +[PIZ1_17:PIZZA] +Ich weiß, du wolltest kein Pizza-Lieferant sein. Na ja, mir egal. + +[PIZ1_18:PIZZA] +Liefere die aus. + +[PIZ1_19:PIZZA] +Die müssen ausgeliefert werden. + +[PIZ1_20:PIZZA] +Na los, Mister, liefere die Dinger aus, oder du fliegst. + +[PIZ1_21:PIZZA] +Die Leute warten, Kumpel. + +[PIZ1_22:PIZZA] +Wartest du auf bessere Zeiten? Die müssen ausgeliefert werden! + +[PIZ1_23:PIZZA] +Liefer den verdammten Fraß aus, Mister. + +[PIZ1_24:PIZZA] +Die müssen ausgeliefert werden, Kumpel. + +[PIZ1_25:PIZZA] +Mann, kannst du die übernehmen? + +[PIZ1_26:PIZZA] +Mister, liefere die Dinger schnell ab, hopp, Amigo. + +[PIZ1_27:PIZZA] +Komm schon, wir sind unter Druck, liefere die Dinger aus. + +[PIZ1_28:PIZZA] +Du schon wieder? Liefere die hier schnell aus, Kumpel. + +[PIZ1_29:PIZZA] +Keine Trödelei diesmal, Kumpel. + +[PIZ1_30:PIZZA] +Na los, du fauler Hund, liefere den Fraß rechtzeitig aus. + +[PIZ1_31:PIZZA] +Du wirst nie befördert, wenn du diesmal nicht schneller machst. + +[PIZ1_32:PIZZA] +~r~Ist dir die Pizza zu heiß? + +[PIZ1_33:PIZZA] +~g~Kehre zum Restaurant zurück, um weitere Aufträge zu bekommen. + +[PIZ1_34:PIZZA] +~g~Pizza geliefert, hier ist dein Geld. + +[PIZ_WON:PIZZA] +Pizza-Mission abgeschlossen. Deine max. Gesundheit erhöht sich auf 150 + +{=================================== MISSION TABLE PORN1 ===================================} + +[POR1_15:PORN1] +Hey, Tommy, kommst du auf eine Aufwärmrunde mit rein!? + +[POR1_14:PORN1] +Du bist engagiert! + +[POR1_A:PORN1] +Action! + +[POR1_B:PORN1] +Wow! Der ist aber groß! + +[POR1_C:PORN1] +30cm, das ist Vorschrift, Baby. + +[POR1_D:PORN1] +SCHNITT! Wer ist dieser Idiot? Du da! Was machst du in meinem Studio? Was willst du? + +[POR1_E:PORN1] +Was soll das alles hier? + +[POR1_F:PORN1] +Aliens? Angelruten? + +[POR1_G:PORN1] +Wer hat jemals einen so großen Hai gesehen? + +[POR1_H:PORN1] +Das muss alles raus hier. + +[POR1_I:PORN1] +Wieso bist du in diese Branche gegangen, du Idiot? + +[POR1_J:PORN1] +Hah? + +[POR1_K:PORN1] +Wegen der Pussys, deswegen. Was ist das?? + +[POR1_L:PORN1] +Das ist meine Kunst- SICHERHEITSDIENST! + +[POR1_M:PORN1] +Hör zu, du aufgeblasener Penner, du gehörst jetzt mir. Mir gehört das alles hier. + +[POR1_N:PORN1] +Wir krempeln den Laden hier um. + +[POR1_O:PORN1] +Ich mache dich reich. + +[POR1_P:PORN1] +Äh, du...du bist Tommy Vercetti? Aber ich dachte, du wärst... + +[POR1_Q:PORN1] +Ganz recht. + +[POR1_R:PORN1] +Wir ändern hier ein paar Dinge und dann machen wir richtig Kohle. + +[POR1_S:PORN1] +Hast du dir schon mal überlegt... + +[POR1_T:PORN1] +Aber zuerst brauchen wir mal ein paar hübsche Mädels hier. + +[POR1_U:PORN1] +Ja, Girls sind ok, aber du...wow! + +[POR1_02:PORN1] +~g~Schalte Candys Agent aus, dann komm wieder und hole Candy. + +[POR1_04:PORN1] +Hey, Candy. Ich suche Filmtalente. Interessiert? + +[POR1_05:PORN1] +Klar! Aber da musst du mit meinem Agenten reden. + +[POR1_06:PORN1] +Was zum Teufel soll das? + +[POR1_07:PORN1] +Du hättest heute zu Hause bleiben sollen! + +[POR1_7B:PORN1] +Was sagt man zu so 'nem Arschloch? + +[POR1_08:PORN1] +Hey, Mercedes! + +[POR1_09:PORN1] +Hi, Tommy. Na, ein bisschen feiern? + +[POR1_10:PORN1] +Jetzt nicht, Süße. Bist du an Filmaufnahmen interessiert? + +[POR1_11:PORN1] +Klar. Wenn's schön billig und dreckig ist. + +[POR1_13:PORN1] +~g~Bring die Girls ins Studio zu Steve. + +[POR1_17:PORN1] +Wow, cooler Hai! + +[POR1_18:PORN1] +~r~Mercedes ist erledigt! + +[POR1_20:PORN1] +Tommy, wo willst du hin? Komm zurück! + +[POR1_21:PORN1] +Wo willst du hin? + +[POR1_22:PORN1] +Tommy, wann sehen wir uns mal ganz allein, nur du und ich? + +[POR1_01:PORN1] +~g~Candy Suxxx wäre perfekt für die Hauptrolle! + +[POR1_12:PORN1] +~g~Nimm Candy mit zu deinem Treffen mit Mercedes. + +[POR1_16:PORN1] +Vielleicht später, Schätzchen... + +[POR1_24:PORN1] +~g~Geh zurück und hole Candy. + +[POR1_25:PORN1] +~g~Du hast Candy vergessen. Geh sie holen. + +[POR1_23:PORN1] +~g~Candy wird sich um das Geschäft in ~h~Downtown~g~ kümmern. + +[POR1_26:PORN1] +~g~Da ist Candy. Sie scheint wieder mit dem Kongressabgeordneten Shrub zusammen gewesen zu sein. + +[POR1_27:PORN1] +Los, gehen wir. + +[POR1_28:PORN1] +Tommy, sei vorsichtig! Meine Implantate sind noch nicht versichert! + +[POR1_29:PORN1] +Das nennst du fahren? + +[POR1_30:PORN1] +Danach kann ich keinen Porno mehr machen! + +[POR1_31:PORN1] +Was ist los? Willst du mich umbringen? Ich dachte, ich wäre der Star! + +{=================================== MISSION TABLE PORN2 ===================================} + +[POR2_A:PORN2] +Was macht die Filmerei, Steve? + +[POR2_B:PORN2] +Tja, Candy ist ein Naturtalent, und die Neue ist unersättlich! + +[POR2_C:PORN2] +Die hatte schon vor der ersten Probe das halbe Team durch. + +[POR2_D:PORN2] +Jedenfalls, morgen haben wir einen Außendreh. Wir schießen ein paar Boot-Szenen. + +[POR2_E:PORN2] +Boot-Szenen? Was für Boot-Szenen? + +[POR2_F:PORN2] +Die Fischer zappeln im Netz der Leidenschaft, als ein riesiger Hai daherkommt - + +[POR2_G:PORN2] +Was habe ich über den Riesenhai gesagt? + +[POR2_H:PORN2] +Ich sagte, 'KEIN RIESENHAI', ok? + +[POR2_I:PORN2] +Halt die Kameras auf die Mädels gerichtet! + +[POR2_J:PORN2] +Ok, ok. Hey, Tommy, probieren kann ich's ja mal, oder? + +[POR2_K:PORN2] +Habt ihr die Flyer drucken lassen? + +[POR2_L:PORN2] +Ja, aber man wird uns die Dinger nicht verteilen lassen. Ich meine, + +[POR2_M:PORN2] +die sind einfach zu, äh, zu deutlich. + +[POR2_N:PORN2] +Mach dir darüber keine Gedanken. + +[POR2_O:PORN2] +Ich hab da so meine Ideen, wie wir die verteilen. + +[POR2_P:PORN2] +Ok. Hey, Candy, äh, in meinen Wohnwagen. + +[POR2_01:PORN2] +~g~Hinter den Studios steht ein altes Wasserflugzeug, das mal als Requisite für einen Indy-Film diente. + +[POR2_02:PORN2] +~g~Suche dir einen Checkpoint aus, um mit dem Abwurf der Flyer zu beginnen. + +[POR2_03:PORN2] +~g~Wirf die Flyer überall bis zum End-Checkpoint ab. + +[POR2_04:PORN2] +~r~TREIBSTOFFMANGEL!!! + +[POR2_05:PORN2] +Benutze es, um die Flyer in der Stadt zu verteilen. + +[DILDO:PORN2] +Skimmer-Tankinhalt: + +[POR2_Q:PORN2] +Oh, Mann. + +[PORN2_9:PORN2] +~g~Du hast ~1~ Sekunden, um zu einer Skimmer zurückzukehren, bevor die Mission endet. + +{=================================== MISSION TABLE PORN3 ===================================} + +[POR3_A:PORN3] +Ok, was ist jetzt wieder? + +[POR3_B:PORN3] +Schsch! + +[POR3_C:PORN3] +Nun, nach seiner Begegnung mit den Nympho-Aliens + +[POR3_D:PORN3] +kann unser Held an nichts anderes denken, als an einen riesigen Phallusberg- + +[POR3_E:PORN3] +und da wollen wir die Szene mit dem Bottich voll Kartoffelpüree drehen, aber dann... + +[POR3_F:PORN3] +Das interessiert mich nicht die Bohne. + +[POR3_G:PORN3] +D-Dreh einfach weiter. Weiter, weiter. + +[POR3_H:PORN3] +Hey, Tommy... + +[POR3_I:PORN3] +Du hast am Telefon was von rechtlichen Problemen gesagt? + +[POR3_J:PORN3] +Ach ja! Der Kongressabgeordnete Alex Shrub ist auf Wahlkampftour und buhlt um Stimmen bei den Puritanern. + +[POR3_K:PORN3] +Es heißt, er unterstützt ein Verbot der, sagen wir mal, eher + +[POR3_L:PORN3] +fleischlichen Bereiche der großartigen Unterhaltungsindustrie unseres Landes. + +[POR3_M:PORN3] +Großartig. + +[POR3_N:PORN3] +Candy! Du kennst doch Shrub. + +[POR3_O:PORN3] +Macht ihr auch ausgefallene Sachen? + +[POR3_P:PORN3] +Oh ja, oh ja, oh ja! Ja, ja, ja, JA, Oooooooh! + +[POR3_Q:PORN3] +Bitte sag, dass du das hast. + +[POR3_R:PORN3] +Gehörte das zum, äh... oder war das die Antwort für... + +[POR3_S:PORN3] +Hey, ich kann das nie unterscheiden. Jedenfalls... + +[POR3_T:PORN3] +Das beste wird sein, du folgst ihr nach dem Dreh. + +[POR3_U:PORN3] +Mal sehen, ob sie dich zu ihrem neuen Liebesnest führt. + +[POR3_V:PORN3] +Hast du eine Kamera? + +[POR3_X:PORN3] +Ja. Gebt ihm eine Kamera. + +[POR3_02:PORN3] +~r~Du hast den Abgeordneten erledigt! Jetzt kannst du ihn nicht mehr erpressen. + +[POR3_03:PORN3] +~r~Du hast die Bodyguards des Abgeordneten aufgescheucht. Sie werden ihn sofort wegbringen. + +[POR3_04:PORN3] +Candy, könntest du mich Martha nennen? + +[POR3_05:PORN3] +Oh, Alex - ich meine Martha - ich tu alles, was du willst. + +[POR3_06:PORN3] +Martha, jemand sieht uns zu. Wie erregend. + +[POR3_07:PORN3] +Sie da! Geben Sie mir die Kamera. + +[POR3_01:PORN3] +~g~Folge Candys ~h~Stretch-Limo~g~. + +[POR3_15:PORN3] +~r~Du hast Candys Stretch-Limo geschrottet! + +[POR3_17:PORN3] +~g~Begib dich mit dem Film zurück ins Pornostudio. + +[POR3_19:PORN3] +~r~Der Film ist alle! + +[POR3_21:PORN3] +~g~Du hast Candys Stretch-Limo verloren! + +[POR3_22:PORN3] +~g~Das WK Chariot Hotel gegenüber seines Balkons dürfte eine ideale Position zum Fotografieren bieten. + +[POR3_23:PORN3] +~g~Es gibt eine Seitentür, durch die du in das Hotel kommst. + +[POR3_08:PORN3] +Halte die~h~ ~k~~PED_LOCK_TARGET~ ~w~gedrückt, um mit der Kamera zu ~h~zielen~w~. + +[POR3_09:PORN3] +Halte die~h~ ~k~~PED_LOCK_TARGET~ ~w~gedrückt, um mit der Kamera zu ~h~zielen~w~. + +[POR3_10:PORN3] +Drücke die~h~ ~k~~PED_SNIPER_ZOOM_IN~~w~, um ~h~heranzuzoomen ~w~und die~h~ ~k~~PED_SNIPER_ZOOM_OUT~~w~, um wieder ~h~wegzuzoomen~w~. + +[POR3_11:PORN3] +Drücke die~h~ ~k~~PED_SNIPER_ZOOM_IN~~w~, um ~h~heranzuzoomen ~w~und die~h~ ~k~~PED_SNIPER_ZOOM_OUT~~w~, um wieder ~h~wegzuzoomen~w~. + +[POR3_12:PORN3] +Drücke die~h~ ~k~~PED_FIREWEAPON~~w~, um ein Bild zu machen. + +[POR3_13:PORN3] +Drücke die~h~ ~k~~PED_FIREWEAPON~~w~, um ein Bild zu machen. + +[POR3_14:PORN3] +Drücke die~h~ ~k~~PED_FIREWEAPON~~w~, um ein Bild zu machen. + +[POR3_20:PORN3] +~g~Wenn du ein Transportmittel brauchst, nimm den ~h~Sparrow~g~ hinter dem Haus. + +[POR3_16:PORN3] +~g~Du brauchst drei gute kompromittierende Fotos von Alex Shrub mit Candy. + +[POR3_24:PORN3] +GESCHOSSENE FOTOS: + +{=================================== MISSION TABLE PORN4 ===================================} + +[POR4_A:PORN4] +Tut mir leid, ich kann das jetzt nicht schlucken. + +[POR4_B:PORN4] +Ach KOMM, Darling! + +[POR4_C:PORN4] +Der ist ausgestattet wie ein Pottwal, Herrgott noch mal, + +[POR4_D:PORN4] +wie kannst du dich da nicht einfühlen?! + +[POR4_E:PORN4] +Aber Stevie... + +[POR4_F:PORN4] +Wie geht's meinem Starregisseur? + +[POR4_G:PORN4] +Oh Mann. Der Kampf zwischen künstlerischer Ambition und + +[POR4_H:PORN4] +diesem Rumgenudle tobt unvermindert. + +[POR4_I:PORN4] +Und bevor du fragst: Ja, alle vier Videos werden veröffentlicht, wenn... + +[POR4_J:PORN4] +Schätzchen, kannst du BITTE die Anaconda im Bild halten, + +[POR4_K:PORN4] +die kostet pro Stunde mehr als du! + +[POR4_L:PORN4] +Oh, sorry, Steve. + +[POR4_M:PORN4] +Ich hab mir gedacht, als Werbung für den Start brauchen wir einen richtigen Knaller. + +[POR4_N:PORN4] +Irgendwas, was so richtig Furore macht in der Stadt. Hast du eine Idee? + +[POR4_O:PORN4] +Na ja, früher gab's da immer Galas, + +[POR4_P:PORN4] +Stars, Limos, riesige Suchscheinwerfer am Nachthimmel... + +[POR4_Q:PORN4] +Suchscheinwerfer? Ich hab eine Idee... + +[POR4_R:PORN4] +....ja, ja, ja. Die heißen Mädels mit ihren Paillettenkleidern und die Limos, oh, Premieren. + +[POR4_S:PORN4] +O ja, Ma'am, natürlich, Ma'am, + +[POR4_T:PORN4] +Und die Presse und die Lichter-Flut... + +[POR4_01:PORN4] +~g~Begib dich nach ~y~Downtown~g~und richte den Scheinwerfer auf dem Gebäude aus. + +[POR4_02:PORN4] +~g~Du brauchst ein schnelles Bike, um von Dach zu Dach zu springen. Der Wachmann fährt immer mit einer ~y~PCJ 600~g~zur Arbeit... + +[POR4_03:PORN4] +~g~Du wirst auf die Gebäudedächer müssen. In eines der oberen Büros sollte ein Lift führen... + +[POR4_06:PORN4] +~g~Kehre in das tiefer gelegene Büro zurück, wenn du noch mal auf die Dächer musst. + +[POR4_07:PORN4] +~g~Du brauchst ein Motorrad, um von Gebäude zu Gebäude zu springen. + +[POR4_08:PORN4] +~g~Brich durch das Fenster, um zu starten. Du hast bis 07:00 Zeit. Dann wird es zu hell, um ungesehen hinaufzukommen. + +[POR4_09:PORN4] +~g~Die Pfeile zeigen dir, zu welchem Gebäude du als nächstes springen musst. + +[POR4_10:PORN4] +~r~Es ist zu hell, um ungesehen dort hinaufzukommen. + +[POR4_11:PORN4] +Kehre zur Leiter zurück, wenn du noch mal auf die Dächer musst. + +[POR4_05:PORN4] +~g~Diese Treppe führt zu einem tiefer gelegenen Büro. + +[POR_AS1:PORN4] +FILMSTUDIO-MISSIONEN ERFÜLLT + +[POR_AS2:PORN4] +~g~Inter Global Films generiert nun bis zu $~1~ Einkünfte. Hol dir das Geld regelmäßig ab. + +{=================================== MISSION TABLE PROT1 ===================================} + +[PRO1_A:PROT1] +Oh, wir müssen diesen Laden umbauen. Das muss älter aussehen. + +[PRO1_B:PROT1] +Ich kann diesen Look nicht ab, Tommy. Was meinst du, sollen wir eine Bar einbauen..? + +[PRO1_D:PROT1] +Hört mal. + +[PRO1_E:PROT1] +Die Zeit ist gekommen, die Stadt zu übernehmen. Sie wartet nur auf uns. + +[PRO1_F:PROT1] +Wir müssen langsam Gebiete einnehmen. + +[PRO1_G:PROT1] +Vice City soll merken, dass wir die neuen Bosse sind, versteht ihr? + +[PRO1_H:PROT1] +Jetzt beruhigt euch mal kurz. Allmählich kapiere ich schon, wie das ganze hier läuft. + +[PRO1_I:PROT1] +Was du brauchst, ist eine legale Fassade, Tommy, Immobilien. Hat mir nicht geschadet. + +[PRO1_J:PROT1] +Wir müssen die Muskeln spielen lassen, sonst war die ganze harte Arbeit umsonst. + +[PRO1_K:PROT1] +Die Geschäftsleute hier wissen, dass Diaz weg ist und weigern sich, Schutzgeld zu zahlen. + +[PRO1_L:PROT1] +Oh, wir könnten es mit Schmiergeld versuchen... + +[PRO1_M:PROT1] +Schmiergeld? Blödsinn! Ich zeig euch, wie man denen Angst macht. + +[PRO1_01:PROT1] +~g~Demoliere die Schaufenster der Läden und die Inhaber werden darum betteln, zahlen zu dürfen. + +[PRO1_03:PROT1] +~r~Du sollst abhauen, nicht Kaffee trinken gehen. + +[PRO1_04:PROT1] +Mein Lebenswerk! Zerstört! + +[PRO1_05:PROT1] +Ich bin ruiniert...RUINIERT!! + +[PRO1_06:PROT1] +Ich zahle einen Haufen Schutzgeld! + +[PRO1_07:PROT1] +Mein schönes Schaufenster! + +[PRO1_08:PROT1] +Mein Laden! Mein schöner Laden! + +[PRO1_09:PROT1] +Vercetti. Merkt euch den Namen. + +[PRO1_10:PROT1] +Ich bin jetzt der Boss in der Stadt. ICH! + +[BUYP1:PROT1] +Du kannst jetzt in bestimmten Gegenden auf der Karte Objekte kaufen. + +[BUYP2:PROT1] +Wenn du ein Gebäude mit grüner Markierung siehst, kannst du dieses kaufen. + +[PRO1_N:PROT1] +Ich bin in fünf Minuten zurück... + +[PRO1_11:PROT1] +~g~Begib dich zum ~y~North Point Einkaufszentrum~g~ in ~y~Vice Point~g~. + +[PRO1_12:PROT1] +~g~Zerbrich die Schaufenster eines jeden Ladens, und die Inhaber werden um neuen Schutz betteln. + +[PRO1_C:PROT1] +Du bist mein Anwalt, nicht mein Innenarchitekt. Klar? + +[BUYP3:PROT1] +Stell dich in die Markierung und drücke die ~h~~k~~PED_ANSWER_PHONE~~w~-Taste, um das Objekt zu kaufen. + +[PRO1_13:PROT1] +~g~Du hast fünf Minuten, um alle zu demolieren. + +{=================================== MISSION TABLE PROT2 ===================================} + +[PRO2_01:PROT2] +~g~Schalte die Wachen aus, die die Front Page Bar beschützen und finde raus, wer hinter ihnen steckt. + +[PRO2_08:PROT2] +~g~Die DBP Security wird wissen, dass du kommst. Schnapp sie dir, ehe sie abhauen. + +[PRO2_A:PROT2] +Was gibt's für Probleme? + +[PRO2_B:PROT2] +Eine Bar weigert sich zu zahlen. + +[PRO2_C:PROT2] +Die glauben, die werden von so einer Schlägerbande beschützt. + +[PRO2_D:PROT2] +Aber keine Sorge, Tommy, ich regle das. + +[PRO2_E:PROT2] +Das nennst du regeln? + +[PRO2_F:PROT2] +Ihr zwei. Hebt mal den Hintern... + +[PRO2_G:PROT2] +Los. + +[PRO2_10:PROT2] +Zwei sind abgehauen. Finde sie und bring die Sache zuende. + +[PRO2_11:PROT2] +Steig in den Wagen, Nichtsnutz. + +[PRO2_02:PROT2] +Dein Schutz braucht ein bisschen mehr Schutz. + +[PRO2_03:PROT2] +Ach, verdammt! Nicht schon wieder! Das brauche ich wirklich nicht! + +[PRO2_04:PROT2] +Diese Idioten arbeiten eigentlich für die DBP Security gleich um die Ecke. + +[PRO2_05:PROT2] +Macht ihr das mal unter euch aus. + +[PRO2_06:PROT2] +Wir sehen uns. + +[PRO2_07:PROT2] +Ja, ja. Wenn's sein muss. + +[PRO2_09:PROT2] +~g~Begib dich zur Front Page Bar und sprich mit dem Besitzer. + +{=================================== MISSION TABLE PROT3 ===================================} + +[PRO3_A:PROT3] +Du Trottel! Was hast du dir dabei gedacht?! + +[PRO3_B:PROT3] +Ist dir klar, was das bedeutet?! + +[PRO3_C:PROT3] +Das könnte das Ende für uns alle sein! + +[PRO3_D:PROT3] +Der Zeitzünder muss hin gewesen sein. + +[PRO3_E:PROT3] +Der Laden war mit Sprengstoff vollgepackt wie eine Feuerwerkfabrik. + +[PRO3_F:PROT3] +Dann hat jemand den Cops einen Tipp gegeben... + +[PRO3_G:PROT3] +Was gibt's für Probleme, Jungs? + +[PRO3_H:PROT3] +Mike sollte einen Laden im Einkaufszentrum abfackeln, + +[PRO3_I:PROT3] +aber er hat's vermasselt und jetzt wimmelt es dort von Bullen. + +[PRO3_J:PROT3] +Wir müssen unser Zeug holen und verschwinden! + +[PRO3_K:PROT3] +Langsam, ihr beiden, lasst mich kurz nachdenken! + +[PRO3_L:PROT3] +Tommy Vercetti läuft nicht einfach weg. + +[PRO3_M:PROT3] +Die Cops werden das Gebäude sorgfältig durchkämmen, oder? + +[PRO3_N:PROT3] +Aber das dauert. + +[PRO3_O:PROT3] +Wir müssen den Laden selbst abfackeln. + +[PRO3_P:PROT3] +Ja, aber... + +[PRO3_Q:PROT3] +Nur ein Cop kommt auch nur in die Nähe von dem Laden! + +[PRO3_R:PROT3] +Dann gehen wir eben als Cops. + +[PRO3_S:PROT3] +Wir brauchen Uniformen und einen Streifenwagen. + +[PRO3_T:PROT3] +Und das alles nur wegen dir, Mike. + +[PRO3_U:PROT3] +Tut mir leid. + +[PRO3_V:PROT3] +Ich hab's. + +[PRO3_W:PROT3] +Wir müssen die Cops hereinlocken, + +[PRO3_X:PROT3] +dann sperren wir sie ein + +[PRO3_Y:PROT3] +und überwältigen sie. + +[PRO3_Z:PROT3] +Guter Plan. Los geht's! + +[PRO3_A1:PROT3] +Ok. + +[PRO3_01:PROT3] +Ok, Lance, machen wir die Cops auf uns aufmerksam! + +[PRO3_02:PROT3] +~g~ Nimm den Streifenwagen und lege die Bombe im Tarbrush Coffee Shop im Einkaufszentrum. + +[PRO3_03:PROT3] +~g~Du hast Lance vergessen. Hole ihn. + +[PRO3_04:PROT3] +~g~ Los geht's. + +[PRO3_05:PROT3] +~r~Du hast Lance erledigt! + +[PRO3_07:PROT3] +~g~Die Tarnung ist aufgeflogen. Beeilung, platziere die Bombe! + +[PRO3_09:PROT3] +Fessle und kneble sie! + +[PRO3_10:PROT3] +Uuh! Passt perfekt! + +[PRO3_11:PROT3] +Bisschen eng im Schritt vielleicht... + +[PRO3_12:PROT3] +Oh ja, ja. Meine auch, meine auch. + +[PRO3_13:PROT3] +Vorsicht, Bruder! Kein Cop fährt so schlecht! + +[PRO3_14:PROT3] +Denk dran, lächle die anderen Cops an. + +[PRO3_15:PROT3] +Hallo, Officer. Hübsche Marke, hübsche Marke. + +[PRO3_16:PROT3] +Ganz toll, Lance. + +[PRO3_17:PROT3] +Ok, die Zünder sind auf 5 Sekunden gestellt. + +[PRO3_18:PROT3] +5 Sekunden?!! Nichts wie raus hier! + +[PRO3_19:PROT3] +Jetzt sind sie so richtig stocksauer. + +[PRO3_20:PROT3] +~g~Bring 2 Cops dazu, dir in die Garage zu folgen. + +[PRO3_21:PROT3] +~g~Beschaff dir einen Fahndungslevel, damit dir die Cops in die Garage folgen. + +[PRO3_22:PROT3] +~g~Das Garagentor ist blockiert! Du musst es freiräumen, damit es schließen kann. + +[PRO3_23:PROT3] +~g~Stell dich in die Markierung, um die Bombe zu platzieren. + +[PRO3_24:PROT3] +~g~Verschwinde aus der Nähe des Cafés! + +[PRO_AS1:PROT3] +SCHUTZGELD-MISSIONEN ERFÜLLT + +[PRO_AS2:PROT3] +~g~Das Vercetti Estate generiert nun bis zu $~1~ Einkünfte. Hol dir das Geld regelmäßig ab. + +[PRO3_08:PROT3] +~g~Du musst zurück zu ~h~Vercetti Estate~g~ auf ~h~Starfish Island~g~. + +{=================================== MISSION TABLE RACES ===================================} + +[RACES_2:RACES] +~g~Du brauchst ein Fahrzeug! Das ist kein Wettlaufen! + +[RACES_3:RACES] +3..2..1.. LOS, LOS, LOS! + +[RACES_8:RACES] +~r~Du hast das Rennen nicht gewonnen! + +[RACES00:RACES] +Rennen ~1~: + +[RACES01:RACES] +Todeskaracho + +[RACES02:RACES] +Ocean Drive + +[RACES03:RACES] +Küsten-Rallye + +[RACES04:RACES] +Capital Cruise + +[RACES05:RACES] +Tour! + +[RACES06:RACES] +V.C. Endurance + +[RACES07:RACES] +Startgebühr: $~1~ + +[RACES08:RACES] +Bestzeit: ~1~:~1~ + +[RACES09:RACES] +Beste Platzierung: 1. + +[RACES10:RACES] +Beste Platzierung: 2. + +[RACES11:RACES] +Beste Platzierung: 3. + +[RACES12:RACES] +Beste Platzierung: 4. + +[RACES13:RACES] +Streckenlänge: ~1~.~1~ km + +[RACES15:RACES] +Bestzeit: Nicht verfügbar + +[RACES16:RACES] +Beste Platzierung: Nicht verfügbar + +[RACES19:RACES] +Du hast nicht genug Geld, um an diesem Rennen teilzunehmen. + +[RACES22:RACES] +Bestzeit: ~1~:0~1~ + +[RACES23:RACES] +Streckenlänge: ~1~.~1~ Meilen + +[RACES_1:RACES] +~g~Schnapp dir ein schnelles Fahrzeug und begib dich zur Startlinie. + +[RACEHLP:RACES] +~w~Drücke die~h~ ~k~~PED_SPRINT~~w~, um das ausgewählte Rennen zu starten. Drücke die~h~ ~k~~VEHICLE_ENTER_EXIT~~w~, um abzubrechen. + +{=================================== MISSION TABLE RCHELI1 ===================================} + +[WRECKED:RCHELI1] +~r~Das Fahrzeug ist Schrott! + +[RCH1_4:RCHELI1] +Verbleibende Checkpoints: + +[RCH1_7:RCHELI1] +~g~Es gibt insgesamt 20 Checkpoints. + +[RCH1_12:RCHELI1] +~g~Der ferngesteuerte Helikopter gerät außer Reichweite! + +[RCH1_13:RCHELI1] +~r~Der ferngesteuerte Helikopter ist außer Reichweite! + +[RCH1_8:RCHELI1] { reVC update } +~g~Wenn du diese Mission abbrechen willst, drücke die ~h~~k~~VEHICLE_FIREWEAPON~~g~, um deinen Heli zu sprengen. + +{=================================== MISSION TABLE RCPLNE1 ===================================} + +[RCPL1_4:RCPLNE1] +~g~Miss dich mit 3 anderen ferngesteuerten Maschinen in einem CHECKPOINT RENNEN + +[RCPL1_5:RCPLNE1] +~g~Flieg durch die Checkpoints, die über Vice City verteilt sind. + +[RCPL1_6:RCPLNE1] { reVC update } +~g~Wenn du diese Mission abbrechen willst, drücke die ~h~~k~~VEHICLE_FIREWEAPON~~g~, um dein Flugzeug zu sprengen. + +[RCPL1_8:RCPLNE1] +~g~Dein ferngesteuertes Flugzeug gerät außer Reichweite! + +[RCPL1_9:RCPLNE1] +~r~Dein ferngesteuertes Flugzeug ist außer Reichweite! + +{=================================== MISSION TABLE RCRACE1 ===================================} + +[RCRC1_1:RCRACE1] +~g~Liefere dir mit 3 anderen RC Bandits ein CHECKPOINT-RENNEN über 2 RUNDEN. + +[RCRC1_3:RCRACE1] +~g~Letzte Runde! + +[RCR1_4:RCRACE1] +Verbleibende Runden: + +[RCR1_1:RCRACE1] +~g~Tritt in einem Checkpoint-Rennen gegen 3 andere ferngesteuerte Autos an. + +[RCR1_2:RCRACE1] +~g~Du musst als erster 2 volle Runden auf dem Kurs fahren! + +[RCR1_6:RCRACE1] +~g~Dein ferngesteuertes Auto gerät außer Reichweite! + +[RCR1_7:RCRACE1] +~r~Dein ferngesteuertes Auto ist außer Reichweite! + +{=================================== MISSION TABLE ROCK1 ===================================} + +[RBM1_A:ROCK1] +AllllllllRrrighttt! + +[RBM1_B:ROCK1] +Fantastisch, einfach fantastisch! + +[RBM1_D:ROCK1] +Hey, kennst du die Jungs von Love Fist? + +[RBM1_E:ROCK1] +Nein, aber ihre Musik habe ich immer geliebt. + +[RBM1_F:ROCK1] +Ich stelle dir die Band vor. + +[RBM1_G:ROCK1] +Das ist P, Percy, Dick, und Willy ist auf dem Klo. Und das vorher in der Kabine war Jezz. + +[RBM1_H:ROCK1] +Jungs, ich möchte euch einen guten Freund vorstellen. + +[RBM1_I:ROCK1] +Das ist Tommy. Wir kennen uns schon ewig. + +[RBM1_J:ROCK1] +Alles paletti, Mann. + +[RBM1_K:ROCK1] +Und, äh, wie war dein Name noch mal? + +[RBM1_L:ROCK1] +Lass das, Jezz. Merk dir eines, + +[RBM1_M:ROCK1] +mit mir kannst du diese Spielchen nicht machen. + +[RBM1_N:ROCK1] +Ich bin dir einfach über, Sonnenscheinchen. + +[RBM1_O:ROCK1] +Die Sache ist die, Tom, die Jungs brauchen Hilfe. + +[RBM1_P:ROCK1] +Die haben hier keine Connections, kein Vitamin B. + +[RBM1_Q:ROCK1] +Wir brauchen Stoff, Alter. + +[RBM1_R:ROCK1] +Damit wir den alten Love Fist Spirit finden, verstehst du?! + +[RBM1_S:ROCK1] +Wir sind hier in Vice City, Mann. Wo liegt das Problem? + +[RBM1_U:ROCK1] +Love Juice, Mann! + +[RBM1_V:ROCK1] +Love Juice? + +[RBM1_W:ROCK1] +Genau. 2 Teile TNT-Whiskey, 1 Teil Koks, 5 Päckchen Brause und 1 Liter Sprit. + +[RBM1_X:ROCK1] +Kannst du uns weiterhelfen, Alter? + +[RBM1_Y:ROCK1] +Es würde den Jungs wirklich viel bedeuten. + +[RBM1_Z:ROCK1] +Du kannst den Jungs doch helfen, oder? + +[RBM1_7:ROCK1] +~r~Du hast den Love Juice nicht rechtzeitig besorgt! + +[RBM1_8:ROCK1] +~r~Mercedes ist erledigt! + +[RBM1_10:ROCK1] +~r~Du Idiot! Du hast die Ware vernichtet! + +[RBM1_13:ROCK1] +~g~Bring den Love Juice und Mercedes zu der Band bevor sie auf die Bühne muss. + +[RBM1_15:ROCK1] +~r~Du hast den Dealer verloren, unser Geld und den Stoff! + +[RBM1_17:ROCK1] +~g~Knöpf dir den Dealer vor und hol dir den Stoff! + +[MOB_07A:ROCK1] +Hey, die Jungs könnten ein bisschen Gesellschaft vertragen, falls du weißt, was ich meine... + +[MOB_07B:ROCK1] +Da kenne ich genau die richtige. + +[ROK1_5:ROCK1] +Hey, Mercedes! + +[ROK1_6:ROCK1] +Hi, Tommy. Na, wie läuft's so? + +[ROK1_7:ROCK1] +Alles bestens. Hör mal, willst du die Jungs von Love Fist verführen? + +[ROK1_8:ROCK1] +Ok, aber dafür bist du mir einen Gefallen schuldig. + +[RBM1_14:ROCK1] +~g~Du brauchst ein Auto oder Motorrad! + +[RBM1_1:ROCK1] +~g~Hol Mercedes in ihrer Wohnung ab. + +[RBM1_12:ROCK1] +~g~Besorg die Zutaten für den 'Love Juice' von dem Dealer. + +[ROK1_2:ROCK1] +NICHT MEHR BENÖTIGT + +[ROK1_3:ROCK1] +NICHT MEHR BENÖTIGT + +[MERC_39:ROCK1] +Wir sehen uns später, Big Boy. + +[RBM1_C:ROCK1] +Hey, Tommy! Schön, dass du kommen konntest. + +[RBM1_9:ROCK1] +~g~Besorg von dem Dealer Love Juice für Love Fist! + +[ROK1_1A:ROCK1] +Suchst du was Bestimmtes? Ich habe genau, was du brauchst! + +[ROK1_9:ROCK1] +Danke für die Kohle, du Trottel! + +[RBM1_T:ROCK1] +Wir brauchen Love Juice, Mann, klar? + +{=================================== MISSION TABLE ROCK2 ===================================} + +[RBM2_A:ROCK2] +Tommy, Mann, bin ich froh, dich zu sehen! + +[RBM2_B:ROCK2] +Was ist los? + +[RBM2_C:ROCK2] +Schlechte Vibes, Tommy... + +[RBM2_E:ROCK2] +Da ist so 'n Typ, wir kennen ihn kaum, aber er kennt uns. + +[RBM2_F:ROCK2] +So wie er da. Weiß alles über uns. + +[RBM2_G:ROCK2] +Weiß, dass Willy auf Damenunterwäsche steht, eh! + +[RBM2_H:ROCK2] +Oder dass Percy auf Duran Duran steht! + +[RBM2_K:ROCK2] +Ja, die Liebesrakete, schon gut. Aber hör zu, dieser Typ... + +[RBM2_L:ROCK2] +ja, ja, der Typ will Love Fist ausknipsen. + +[RBM2_M:ROCK2] +Ausknipsen, Tommy. + +[RBM2_N:ROCK2] +Love Fist soll verschwinden. Wie es immer heißt: Die Besten sterben jung. + +[RBM2_O:ROCK2] +Aber Tommy, du musst Love Fist retten! + +[RBM2_P:ROCK2] +Wir geben in zwei Stunden eine Autogrammstunde, und ich glaube... + +[RBM2_Q:ROCK2] +Und die Jungs glauben, dass der Kerl dort irgendwas plant. + +[RBM2_1:ROCK2] +~g~Fahre die Limo zum Ort der Autogrammstunde und versuche, den Irren zu finden. + +[RBM2_2:ROCK2] +~r~Du hast den Wagen der Band geschrottet! + +[RBM2_3:ROCK2] +~g~Geh zu der Autogrammstunde! + +[RBM2_4:ROCK2] +~g~Erledige den Irren. Lass ihn nicht entkommen! + +[RBM2_5:ROCK2] +~r~Du hast ihn verloren, du Idiot! + +[RBM2_7:ROCK2] +~r~Die Fans wurden angegriffen. Der Irre wird nicht auftauchen! + +[RBM2_8:ROCK2] +~r~Die Security-Leute wurden angegriffen. Der Irre wird nicht auftauchen! + +[PSYCH_1:ROCK2] +Love Fist wird in der Hölle braten! + +[PSYCH_2:ROCK2] +Love Fist hat mein Leben zerstört! + +[RBM2_I:ROCK2] +Halt's Maul, du Irrer! Nur weil Jezz auf Schafe steht. + +[RBM2_R:ROCK2] +Hey, Schnauze! + +[RBM2_D:ROCK2] +Das ist kein Spaß, das ist 'ne brutale Geschichte, brutal, ok? + +[RBM2_J:ROCK2] +Ich sage nur, die Liebesrakete, ok? + +{=================================== MISSION TABLE ROCK3 ===================================} + +[RBM3_A:ROCK3] +Tommy! Tommy! Tommy, Mann, der Irre ist wieder da! + +[RBM3_B:ROCK3] +Was ist denn los? + +[RBM3_C:ROCK3] +Dieser Irre lässt Love Fist nicht in Ruhe! + +[RBM3_D:ROCK3] +Du hast ihn nicht erwischt, Mann. Und jetzt ist er wieder da. + +[RBM3_E:ROCK3] +Ja, ja, ja, und es ist nämlich so... + +[RBM3_F:ROCK3] +wir brauchen für die Limo einen Fahrer, dem wir vertrauen können, + +[RBM3_G:ROCK3] +weil der Irre uns ständig bedroht! + +[RBM3_I:ROCK3] +Wir scheißen uns alle in die Hosen, Mann. + +[RBM3_J:ROCK3] +Ok, Jungs, nur die Ruhe. Ich mach das schon. + +[RBM3_K:ROCK3] +Normalerweise würde ich keine besoffenen, schottischen Tucken durch die Gegend kutschieren, + +[RBM3_L:ROCK3] +aber für euch mache ich eine Ausnahme. + +[ROK3_03:ROCK3] +Dann mach mir einen großen. + +[ROK3_04:ROCK3] +Hey, Tommy, nun mach mal halblang, Mann. + +[ROK3_08:ROCK3] +Langsam wird's langweilig. + +[ROK3_09:ROCK3] +Halt bloß das Pedal durchgedrückt!! + +[ROK3_61:ROCK3] +Wir müssen die Bombe finden! + +[ROK3_28:ROCK3] +Ich werd Bass in der Hölle spielen. + +[ROK3_30:ROCK3] +Tu doch einer was! + +[ROK3_32:ROCK3] +Ok, Obermacho, dann tu du doch was! + +[ROK3_34:ROCK3] +Willy könnte den TNT-Whiskey mit einem Strohhalm raussaugen. + +[ROK3_37:ROCK3] +Gebt Willy einen Strohhalm! + +[ROK3_41:ROCK3] +Welchen Draht, Tommy? + +[ROK3_42:ROCK3] +Den grünen. + +[ROK3_43:ROCK3] +Da ist kein grüner. Oder ist der hier grün? + +[ROK3_44:ROCK3] +Sieht für dich einer von den Drähten grün aus? + +[ROK3_49:ROCK3] +Ich hab euch doch jahrelang nur mitgeschleppt. + +[ROK3_51:ROCK3] +Ein großes keifendes Weib. + +[ROK3_52:ROCK3] +Ja. + +[ROK3_53:ROCK3] +Klappe jetzt und zieht einen Draht raus. + +[ROK3_54:ROCK3] +Welchen? + +[ROK3_55:ROCK3] +Den da... + +[ROK3_56:ROCK3] +NEIN! + +[ROK3_57:ROCK3] +Mann, alles ok. Wir sind nicht hochgegangen, Alter. Tommy, Mann, gut gemacht. Rock 'n' Roll, Mann. + +[ROK3_58:ROCK3] +Müssen wir nicht zu einem Gig? Krach machen? Groupies abgreifen? + +[ROK3_59:ROCK3] +LOVE FIST! + +[ROK3_60:ROCK3] +Bist du fertig mit der Pulle? + +[RBM3_4:ROCK3] +~r~Love Fist ist Geschichte! + +[RBM3_6:ROCK3] +DETONATION: + +[RBM3_1:ROCK3] +~g~Chauffiere Love Fist zu ihrem Auftritt. + +[RBM3_2:ROCK3] +Falls du versuchst, aus dem Auto auszusteigen, wenn die Bombe scharfgemacht ist, wird sie explodieren... + +[RBM3_3:ROCK3] +Ist der Detonations-Balken am Anschlag, explodiert die Bombe. + +[RBM3_8:ROCK3] +Je schneller du fährst, desto niedriger der Detonations-Balken. + +[RBM3_7:ROCK3] +~g~BOMBE ENTSCHÄRFT! + +[ROK3_6A:ROCK3] +~g~Love Fist. Ihr habt den Äther lange genug verschmutzt! + +[ROK3_6B:ROCK3] +~g~Ich wollte euer Freund sein. Jetzt will ich, dass ihr untergeht. + +[ROK3_62:ROCK3] +Wir dachten, wir zeigen dir mal unseren Rocktempel... + +[ROK3_63:ROCK3] +Kannst dir die Power von Love Fist reinziehen! + +[ROK3_64:ROCK3] +Hör sich das einer an, Mann. Das ist Pappmaché und Klebeband. + +[ROK3_65:ROCK3] +Hey, für die Kids ist es ein Tempel, und wir sind die Priester! + +[ROK3_66:ROCK3] +Tja, wenn die Kids es toll finden, dass ihre Priester dicht und unmusikalisch sind, + +[ROK3_67:ROCK3] +was soll man da sagen? + +[ROK3_68:ROCK3] +Oh, Mist, das Ding frisst schon wieder das Band. + +[ROK3_69:ROCK3] +Wenn das so ist, müssen wir live spielen. + +[ROK3_70:ROCK3] +Ooooh Shit! Mein Darm... + +[ROK3_01:ROCK3] +Endlich, Mann, Zeit für einen wohlverdienten Drink. + +[ROK3_02:ROCK3] +Die Halle ist nur 100 Meter die Straße runter. + +[ROK3_05:ROCK3] +Ich werd irre, wenn ich keinen Sprit kriege. + +[ROK3_07:ROCK3] +Tommy, mein Freund, wir müssen die Band retten! + +[ROK3_29:ROCK3] +Tommy, bleib auf dem Gas, Alter. + +[ROK3_31:ROCK3] +Ganz toll. 'Tu doch einer was.' Was ist denn das für eine Ansage? Da kenn ich mutigere Mädels. + +[ROK3_33:ROCK3] +Alter, ich bin Musiker. Vom Bomben-Entschärfen hab ich keinen blassen Dunst. + +[ROK3_35:ROCK3] +Eben, so was liegt dir doch, was ich so höre. + +[ROK3_38:ROCK3] +Einen Strohhalm?! Dies ist der Tour-Bus von Love Fist! + +[ROK3_39:ROCK3] +Wo zum Geier sollte hier ein Strohhalm sein? + +[ROK3_46:ROCK3] +Ich hätte euch alle rausschmeißen sollen, als es noch ging, Mann. + +[ROK3_47:ROCK3] +Kapitalist. + +[ROK3_48:ROCK3] +Kameradenschwein. + +[ROK3_73:ROCK3] +Jezz spielt das Band ab, + +[RBM3_9:ROCK3] +Wenn du angehalten wirst oder langsam fährst, wächst der Detonations-Balken. + +[ROK3_50:ROCK3] +Halt die Klappe. Du bist ein Idiot. + +[ROK3_36:ROCK3] +Hey, ich war an dem Abend vielleicht komplett zugedröhnt! + +[RBM3_H:ROCK3] +Ich scheiß mir in die Hose, Mann. Ich will zu meiner Mama! + +[ROK3_45:ROCK3] +Oh nein. Im Angesicht des Todes sieht alles grün aus. + +[ROK3_6C:ROCK3] +~g~Wenn ihr langsamer werdet, geht eure Limo hoch, IHR UND EURE HAARIGEN ÄRSCHE mit dazu! + +[ROK3_71:ROCK3] +Wir müssen uns ranhalten. Danke nochmal, Tommy, du hast es echt drauf. Ciao. + +[ROK3_1:ROCK3] +Endlich, Mann, Zeit für einen wohlverdienten Drink. Die Halle ist nur 100 Meter die Straße runter. + +[ROK3_2:ROCK3] +Mach mir mal einen großen. Hey, Tommy, leg mal 'nen anderen Sound auf, Mann. + +[ROK3_3:ROCK3] +Ich werd wirr im Kopf, wenn nichts zum Headbangen läuft. Hey, Tommy, leg mal dieses Band ein. + +[ROK3_4:ROCK3] +Love Fist. Ihr habt den Äther lange genug verschmutzt! Ich wollte euer Freund sein. + +[ROK3_5:ROCK3] +Jetzt will ich, dass ihr untergeht. Wenn ihr langsamer werdet, geht eure Limo hoch, IHR UND EURE HAARIGEN ÄRSCHE mit dazu! + +[ROK3_6:ROCK3] +Tommy, mein Freund, du musst die Band retten! Langsam wird's langweilig. Halt bloß das Pedal durchgedrückt!! + +[ROK3_7:ROCK3] +Wir müssen die Bombe finden! Können wir nicht einfach den ganzen Tag rumfahren? Wir haben jedenfalls jede Menge zu saufen. + +[ROK3_8:ROCK3] +Könnte die Bombe nicht unter der Motorhaube sein? Da kommen wir nie ran, ohne anzuhalten! Wir werden alle sterben! Ich besauf mich! + +[ROK3_9:ROCK3] +Hey, hier gibt's eine Warteschlange, Alter! Die Lösung liegt nicht in der Minibar! Weg da! + +[ROK3_10:ROCK3] +Hey, aus der Wodkapulle kommen Drähte raus! Das ist kein Wodka, das ist TNT-WHISKEY! + +[ROK3_11:ROCK3] +WAAAAAAGGGHHHH!!!! Das Ding ist scharfgemacht!! WAAAAAAAAAAGGGHHHHHH!!!! + +[ROK3_12:ROCK3] +Man hat mir immer gesagt, der Alk wird mich killen... Das kenn ich aus dem Fernsehen. Du musst einen der Drähte rausziehen. Welchen Draht? Weiß ich doch nicht, Mann. + +[ROK3_13:ROCK3] +Keinen Schimmer. Willy, sag doch mal was. Ich werd Bass in der Hölle spielen. + +[ROK3_14:ROCK3] +Tommy, bleib auf dem Gas, Mann. Tu doch einer was! Ganz toll... + +[ROK3_15:ROCK3] +'Tu doch einer was.' Was ist denn das für eine Ansage? Da kenn ich mutigere Mädchen. Ok, Obermacho, dann tu du doch was! + +[ROK3_16:ROCK3] +Alter, ich bin Musiker. Mit Bomben kenne ich mich nicht aus. Willy könnte den TNT-Whiskey mit einem Strohhalm raussaugen. + +[ROK3_17:ROCK3] +Eben, so was liegt dir doch, was ich so höre. Hey, ich war total blau an dem Abend, das wisst ihr ganz genau! + +[ROK3_18:ROCK3] +Gebt Willy einen Strohhalm! Einen Strohhalm?! Wi sind im Band-Auto von Love Fist! + +[ROK3_19:ROCK3] +Wo zum Geier sollte hier ein Strohhalm sein? Welchen Draht, Tommy? Den grünen. Da ist kein grüner. + +[ROK3_20:ROCK3] +Oder ist der hier grün? Sieht für dich einer von den Drähten grün aus? + +[ROK3_21:ROCK3] +Oh nein. Im Angesicht des Todes sieht alles grün aus. Ich hätte euch alle rausschmeißen sollen, als es noch ging, Mann. + +[ROK3_22:ROCK3] +Kameradenschwein. Kapitalist. Ich hab euch doch jahrelang nur mitgeschleppt. Halt die Klappe. Du bist ein Idiot. + +[ROK3_23:ROCK3] +Ein großes keifendes Weib. Ja. Klappe jetzt und zieht einen Draht raus. Welchen? Den da... + +[ROK3_24:ROCK3] +NEIN! Mann, alles ok. Wir sind nicht hochgegangen, Alter. + +[ROK3_25:ROCK3] +Tommy, Mann, gut gemacht. Rock 'n' Roll, Mann. Müssen wir nicht zu einem Gig? + +[ROK3_26:ROCK3] +Krach machen? Groupies abgreifen? LOVE FIST! + +[ROK3_27:ROCK3] +Bist du fertig mit der Pulle? + +{=================================== MISSION TABLE SERG1 ===================================} + +[TEX1_0:SERG1] +~g~Die Zielperson ist auf der Driving Range. Sorg dafür, dass er seinen letzten Golfball geschlagen hat! + +[TEX1_A:SERG1] +Komm rein und setz dich auf deinen Hintern, Junge. + +[TEX1_B:SERG1] +Mein Daddy hat immer gesagt, einem geschenkten Gaul schaut man nicht ins Maul. Hat's auch nie getan. + +[TEX1_C:SERG1] +Ein Gläschen alter Kentucky gefällig? + +[TEX1_D:SERG1] +Nein danke. + +[TEX1_E:SERG1] +Ein nüchterner Denker, das gefällt mir. + +[TEX1_F:SERG1] +Im Immobiliengeschäft geht's nicht um hochtrabende Verträge. + +[TEX1_G:SERG1] +Es geht um Land. Und darum, dieses Land zu kriegen. Kannst du mir folgen? + +[TEX1_H:SERG1] +Oh, ja. + +[TEX1_I:SERG1] +Ich will, dass so ein sturer Hund sein Land hergibt. + +[TEX1_J:SERG1] +Mir scheint, du könntest ihn dazu überreden. + +[TEX1_K:SERG1] +Ich bin der reinste Überredungskünstler. + +[TEX1_L:SERG1] +Ja. Er wird im Country Club sein, auf dem Golfplatz. + +[TEX1_M:SERG1] +Knarren sind dort verboten. Seine Bodyguards werden also unbewaffnet sein. + +[TEX1_N:SERG1] +Du sollst ihn so richtig gründlich vermöbeln. + +[TEX1_O:SERG1] +Hier, ich hab dir einen Mitgliedsausweis besorgt. Aber du brauchst passendere Kleidung. + +[TEX1_2:SERG1] +~g~Begib dich jetzt zum Leaf Links Golf Club. + +[TEX1_3:SERG1] +Wer ist der Kerl? Jungs, nehmt ihn euch vor. + +[TEX1_6:SERG1] +Hübscher Hintern, Baby! + +[TEX1_7:SERG1] +Bin ich das? + +[TEX1_8:SERG1] +Jedesmal wenn du in einen Golfwagen steigst, erhältst du automatisch einen Golfschläger, vorausgesetzt, du hast nicht schon eine Nahkampfwaffe. + +[TEX1_9:SERG1] +Schnapp ihn dir! + +[TEX1_10:SERG1] +Mach den Irren fertig! + +[TEX1_1:SERG1] +~g~Besorg dir bei Jocksport Golferklamotten. + +{=================================== MISSION TABLE SERG2 ===================================} + +[TEXEXIT:SERG2] +~g~Jetzt verschwinde aus Little Haiti! + +[TEX_2A:SERG2] +~g~Großartig! Sie haben dich bemerkt! + +[TEX_2B:SERG2] +~r~Narr! Die Leute müssen SEHEN, dass der Täter ein Kubaner ist! + +[TEX_2C:SERG2] +~g~Besorge dir bei Rafael's Kleidung in den Farben der kubanischen Gang. + +[TEX_2D:SERG2] +~g~Nimm dir jetzt den Boss der Haitianer in Romeros Beerdigungsinstitut vor. + +[TEX2_A:SERG2] +Tommy, das ist Donald Love. Donald, das ist Tommy Vercetti, + +[TEX2_B:SERG2] +der neueste Draufgänger hier in der Stadt. + +[TEX2_C:SERG2] +Ja...äh... + +[TEX2_D:SERG2] +Donald, sei still und hör zu. Vielleicht kannst du was lernen. + +[TEX2_E:SERG2] +Also. Nichts lässt Immobilienpreise schneller abstürzen als ein guter alter Bandenkrieg. + +[TEX2_F:SERG2] +Außer vielleicht eine Katastrophe, eine biblische Plage oder so, + +[TEX2_G:SERG2] +aber das ginge hier wohl zu weit. + +[TEX2_H:SERG2] +Kannst du mir folgen, Brillenschlange? + +[TEX2_I:SERG2] +Jüngst starb ein haitianischer Gang-Boss. Man tippt, es waren die Kubaner, aber keiner weiß es. + +[TEX2_J:SERG2] +Aber wir wollen sicher gehen. Du verkleidest dich als kubanischer Hombre + +[TEX2_K:SERG2] +und machst Krawall bei der Beerdigung. Misch sie auf und dann verzieh dich. + +[TEX2_L:SERG2] +Kannst du mir folgen, Donald? + +[TEX2_M:SERG2] +Das dürfte das Fass zum Überlaufen bringen, was? + +[TEX2_N:SERG2] +Und wir lehnen uns zurück und sehen zu, wie die Preise purzeln. + +{=================================== MISSION TABLE SERG3 ===================================} + +[TEX3_A:SERG3] +Pass auf, Junge. Ich habe ein Problem und denke, du könntest mir weiterhelfen. + +[TEX3_B:SERG3] +Ich bin kein Bauunternehmer. + +[TEX3_C:SERG3] +Nein, ich dachte mehr an deine Talente als Abrissunternehmer. + +[TEX3_D:SERG3] +Das hier zeigt, was wir geplant haben. Und so- + +[TEX3_E:SERG3] +-so sieht der betreffende Grund heute aus. + +[TEX3_F:SERG3] +Sie wollen sagen, das neue Bürohaus da ist Ihnen im Weg. + +[TEX3_G:SERG3] +Gut mitgedacht. + +[TEX3_H:SERG3] +Also, ich verdrücke mich eine Weile aus der Stadt, + +[TEX3_I:SERG3] +und wenn dieses Bürogebäude urplötzlich irreparable Schäden aufweist, dann... + +[TEX3_J:SERG3] +....fühlen Sie sich als guter Mensch verpflichtet, einzuspringen und + +[TEX3_K:SERG3] +-für die Neugestaltung eines wichtigen Gebiets der Stadt zu sorgen? + +[TEX3_L:SERG3] +Wo finde ich mehr Männer wie dich? + +[TEX3_1:SERG3] +~g~Benutze den ferngesteuerten Helikopter, um Bomben zu 4 Zielen an dem zur Sprengung vorgesehenen Gebäude zu transportieren. + +[TEX3_2:SERG3] +~g~Du musst an jedem Ziel eine Bombe abwerfen. Die Reihenfolge ist beliebig. + +[TEX3_3:SERG3] +~g~Steuere den Helikopter direkt über eine Bombe, um sie aufzunehmen. Die Bombe heftet sich dann automatisch an den Helikopter. + +[TEX3_5:SERG3] +~g~Wenn eine Bombe ihr Ziel verfehlt, kannst du sie erneut aufnehmen und es nochmal versuchen. + +[TEX3_7:SERG3] +~g~Dann hast du noch 7 Minuten, um die restlichen Bomben im Ziel zu platzieren. + +[TEX3_8:SERG3] +~g~Du hast das Ziel verfehlt! Nimm die Bombe wieder auf und versuche es nochmal! + +[TEX3_10:SERG3] +~g~Wirf die Bombe über einem der Ziele ab. + +[TEX3_11:SERG3] +Verbleibende Ziele: + +[TEX3_17:SERG3] +~r~Die Zeit ist um. Du hast es nicht geschafft, das Gebäude zu sprengen. + +[TEX3_18:SERG3] +~r~Dein Helikopter wurde zerstört! Wie willst du jetzt die Bomben transportieren? + +[TEX3_19:SERG3] +~r~Deine Bombe ist im Wasser gelandet! Du brauchst ALLE 4 Bomben, um die Sprengung vorzunehmen. + +[TEX3_20:SERG3] +~g~Dein Helikopter ist fast außer Reichweite. Du musst zurück zur Baustelle, um deine Arbeit zuende zu bringen! + +[TEX3_21:SERG3] +~r~Dein Helikopter befindet sich außer Reichweite! + +[TEX3_24:SERG3] +Drücke ~h~~k~~VEHICLE_LOOKLEFT~~w~, um den Helikopter gegen den Uhrzeigersinn zu drehen. + +[TEX3_25:SERG3] +Drücke ~h~~k~~VEHICLE_LOOKLEFT~~w~, um den Helikopter im Uhrzeigersinn zu drehen. + +[TEX3_27:SERG3] +~g~Über eine Haupttreppe hat man Zugang zu allen Stockwerken des Gebäudes. + +[TEX3_31:SERG3] +~r~Du hast den Wagen mit den Bomben und dem ferngesteuerten Helikopter zerstört! + +[TEX3_32:SERG3] +Du kannst ~h~nach hinten sehen~w~, indem du ~h~gleichzeitig ~k~~VEHICLE_LOOKLEFT~ und ~k~~VEHICLE_LOOKRIGHT~ drückst~w~. + +[TEX3_4:SERG3] { reVC update } +~g~Um eine Bombe abzuwerfen, drücke die~h~ ~k~~VEHICLE_FIREWEAPON~~w~. + +[TEX3_29:SERG3] { reVC update } +Um eine Bombe abzuwerfen, drücke die~h~ ~k~~VEHICLE_FIREWEAPON~. + +[TEX3_26:SERG3] +Drücke die ~h~~k~~VEHICLE_BRAKE~~w~, um die Rotorgeschwindigkeit zu verringern, der Helikopter ~h~verliert dann an Höhe. + +[TEX3_22:SERG3] +Drücke die ~h~~k~~VEHICLE_ACCELERATE~~w~, um die Rotorgeschwindigkeit zu erhöhen, der Helikopter ~h~gewinnt dann an Höhe. + +[TEX3_16:SERG3] +~g~Begib dich zu dem ~w~TOPFUN~g~-Wagen nahe dem zum Abriss vorgesehenen Gebäude. + +[TEX3_33:SERG3] +Wenn du eine Bombe aufgenommen hast, zeigt dir das Radar die Position des Ziels in Relation zu dem ferngesteuerten Helikopter. + +[TEX3_34:SERG3] +Ein ~h~nach oben zeigendes Dreieck ~w~bedeutet, dass das Ziel sich in ~h~größerer Höhe ~w~befindet als der Helikopter. + +[TEX3_35:SERG3] +Ein ~h~nach unten zeigendes Dreieck ~w~bedeutet, dass das Ziel sich in ~h~geringerer Höhe ~w~befindet als der Helikopter. + +[TEX3_36:SERG3] +Ein ~h~Viereck ~w~ bedeutet, dass das Ziel sich auf ~h~der gleichen Höhe ~w~befindet wie der Helikopter. + +[TEX3_6:SERG3] +~g~Wenn du die erste Bombe aufgenommen hast, wird der Zeitzünder aktiviert. + +[TEX3_28:SERG3] +Um ~h~eine Bombe aufzunehmen~w~, steuere den Helikopter direkt über sie. Der Helikopter kann immer nur eine Bombe tragen. + +[TEX3_30:SERG3] +~g~Um eine Bombe aufzunehmen, steuere den Helikopter direkt über sie. Der Helikopter kann immer nur eine Bombe tragen. + +[TEX3_12:SERG3] +~g~Bombe platziert! Es bleiben nur noch 3 Ziele! Hol die nächste Bombe. + +[TEX3_13:SERG3] +~g~Bombe platziert! Es bleiben nur noch 2 Ziele! Hol die nächste Bombe. + +[TEX3_14:SERG3] +~g~Bombe platziert! Es bleibt nur noch 1 Ziel! Hol die nächste Bombe. + +[TEX3_15:SERG3] +~r~Zeitzünder aktiviert! ~g~ Du musst die ~w~4 Bomben ~g~in der verbleibenden Zeit platzieren. + +[TEX3_37:SERG3] +Zieh den ~h~ Rechten Analog-Stick zurück~w~, um die Rotorgeschwindigkeit zu erhöhen, der Helikopter ~h~ gewinnt dann an Höhe. + +[TEX3_38:SERG3] { reVC update } +{Drück den ~h~ ~k~~VEHICLE_ACCELERATE~~w~, um die Rotorgeschwindigkeit zu verringern, der Helikopter ~h~ verliert dann an Höhe.} +Drück den ~h~ Rechten Analog-Stick nach vorn~w~, um die Rotorgeschwindigkeit zu verringern, der Helikopter ~h~ verliert dann an Höhe. + +[TEX3_39:SERG3] +Um eine Bombe abzuwerfen, drücke die ~h~~k~~VEHICLE_HANDBRAKE~~g~-Taste. + +[TEX3_40:SERG3] +Um eine Bombe abzuwerfen, drücke die ~h~~k~~VEHICLE_HANDBRAKE~~w~-Taste. + +[TEX3_23:SERG3] +Mit ~h~~k~~VEHICLE_TURRETUP~~w~ und ~h~~k~~VEHICLE_TURRETDOWN~~w~ neigst du den Helikopter in die Richtung, in die du ihn steuern willst. + +{=================================== MISSION TABLE TAXI1 ===================================} + +[FARES:TAXI1] +FAHRTEN: + +[TAXI1:TAXI1] +~g~Besorg dir einen Fahrgast. + +[TSCORE2:TAXI1] +$~1~ + +[IN_ROW:TAXI1] +~1~ SERIEN-Bonus! $~1~ + +[TAXI3:TAXI1] +~r~Dein Fahrgast ist entsetzt geflohen! + +[TAXI7:TAXI1] +~r~Dein Wagen ist Schrott. Repariere ihn. + +[TAXI4:TAXI1] +Fahrt abgeschlossen! + +[TAXI5:TAXI1] +SPEED BONUS!! + +[TAXI6:TAXI1] +Taxi-Mission beendet + +[TAXIH1:TAXI1] +Halte neben einem markierten Fußgänger, um ihn einsteigen zu lassen, dann bringe ihn rechtzeitig an sein Fahrtziel. + +[FARE1:TAXI1] +~g~Fahrtziel ~w~'Pole Position Club' ~g~in Ocean Beach. + +[FARE3:TAXI1] +~g~Fahrtziel ~w~'Marina' ~g~in Ocean Beach. + +[FARE4:TAXI1] +~g~Fahrtziel ~w~'Ammu-Nation' ~g~in Ocean Beach. + +[FARE5:TAXI1] +~g~Fahrtziel ~w~'Eisenwarenladen' ~g~in Washington Beach. + +[FARE6:TAXI1] +~g~Fahrtziel ~w~'North Point Einkaufszentrum' ~g~in Vice Point. + +[MFARE1:TAXI1] +~g~Fahrtziel ~w~'Ammu-Nation' ~g~in Downtown. + +[MFARE2:TAXI1] +~g~Fahrtziel ~w~'Terminal' ~g~im Escobar International Airport. + +[WFARE3:TAXI1] +~g~Fahrtziel ~w~'Sunshine Autos' ~g~in Little Havana. + +[WFARE4:TAXI1] +~g~Fahrtziel ~w~'Kaufman-Taxis' ~g~in Little Haiti. + +[WFARE5:TAXI1] +~g~Fahrtziel ~w~'Eisenwarenladen' ~g~in Little Havana. + +[WFARE6:TAXI1] +~g~Fahrtziel ~w~'Howlin Petes Bike Emporium' ~g~in Downtown. + +[FARE7:TAXI1] +~g~Fahrtziel ~w~'die Juweliere' ~g~in Vice Point. + +[FARE8:TAXI1] +~g~Fahrtziel ~w~'der Strand' ~g~in Ocean Beach. + +[FARE9:TAXI1] +~g~Fahrtziel ~w~'der Strand' ~g~in Washington Beach. + +[FARE10:TAXI1] +~g~Fahrtziel ~w~'der Strand' ~g~in Vice Point. + +[FARE11:TAXI1] +~g~Fahrtziel ~w~'das Krankenhaus' ~g~in Ocean Beach. + +[FARE12:TAXI1] +~g~Fahrtziel ~w~'das Krankenhaus' ~g~in Vice Point. + +[FARE13:TAXI1] +~g~Fahrtziel ~w~'die Polizeistation' ~g~in Washington Beach. + +[FARE14:TAXI1] +~g~Fahrtziel ~w~'die Polizeistation' ~g~in Vice Point. + +[FARE15:TAXI1] +~g~Fahrtziel ~w~'die Pizzagaststätte' ~g~in Vice Point. + +[WFARE7:TAXI1] +~g~Fahrtziel ~w~'die Polizeistation' ~g~in Little Havana. + +[WFARE8:TAXI1] +~g~Fahrtziel ~w~'die Polizeistation' ~g~in Downtown. + +[WFARE9:TAXI1] +~g~Fahrtziel ~w~'das Krankenhaus' ~g~in Downtown. + +[WFARE10:TAXI1] +~g~Fahrtziel ~w~'das Krankenhaus' ~g~in Little Havana. + +[WFARE11:TAXI1] +~g~Fahrtziel ~w~'der Stadium' ~g~in Downtown. + +[WFARE12:TAXI1] +~g~Fahrtziel ~w~'die Pizzagaststätte' ~g~in Little Haiti. + +[WFARE13:TAXI1] +~g~Fahrtziel ~w~'die Pizzagaststätte' ~g~in Downtown. + +[WFARE14:TAXI1] +~g~Fahrtziel ~w~'die Docks' ~g~in Viceport. + +[WFARE15:TAXI1] +~g~Fahrtziel ~w~'die Apotheke' ~g~in Little Haiti. + +[FARE2:TAXI1] +~g~Fahrtziel ~w~'Malibu Club' ~g~in Vice Point. + +{=================================== MISSION TABLE TAXICUT ===================================} + +[TAXC_A:TAXICUT] +Schätze, sie sind der neue Besitzer. + +[TAXC_B:TAXICUT] +Sind Sie 'n Mafioso? Oder vom Kartell? Sehen nicht aus wie ein Mexikaner. + +[TAXC_C:TAXICUT] +Egal. Halten Sie schon endlich Ihre Predigt von wegen 'Jetzt wird alles anders', + +[TAXC_D:TAXICUT] +bedrohen Sie ein paar von den Fahrern- + +[TAXC_E:TAXICUT] +aber nicht Ted, der ist gerade an der Leiste operiert. + +[TAXC_F:TAXICUT] +Tja, also, hier wird sich einiges ändern, Lady. + +[TAXC_G:TAXICUT] +Aber nicht doch, Jungchen. Überlassen Sie das lieber mir - + +[TAXC_H:TAXICUT] +Ich mach das schon seit Jahren. + +[TAXC_I:TAXICUT] +Alles mal herhören. + +[TAXC_J:TAXICUT] +Wir haben eine neue Geschäftsleitung, und es wird sich wieder mal einiges ändern hier. + +[TAXC_K:TAXICUT] +Unsere neue Geschäftsleitung, die- + +[TAXC_L:TAXICUT] +Von welcher Gang sind Sie? + +[TAXC_M:TAXICUT] +Ich gehöre keiner Gang an. + +[TAXC_N:TAXICUT] +Und wie heißen Sie, junger Mann? + +[TAXC_O:TAXICUT] +Vercetti, Tommy Vercetti. + +[TAXC_P:TAXICUT] +Unsere neue Geschäftsleitung, die Vercetti Gang, + +[TAXC_Q:TAXICUT] +wird dafür sorgen, dass wir keinen Ärger kriegen. + +[TAXC_R:TAXICUT] +Capiche? Ende! + +[TAXC_S:TAXICUT] +Wie fanden Sie das 'Capiche'? Ich fand's gut. + +[TAXC_T:TAXICUT] +Also, so ist das immer gelaufen: + +[TAXC_U:TAXICUT] +Wir führen die Firma weiter wie gewohnt. + +[TAXC_V:TAXICUT] +Wenn die Konkurrenz Ärger macht, gebt ihr ihnen eines auf die Mütze. + +[TAXC_W:TAXICUT] +Dann geben die uns eines auf die Mütze. + +[TAXC_X:TAXICUT] +Dann geben sie denen eines auf die Mütze. + +[TAXC_Y:TAXICUT] +Und so weiter, und so fort. Kapiert? + +[TAXC_Z:TAXICUT] +Äh, ja, ich glaub schon. + +[TAXC_A1:TAXICUT] +Schnappen Sie sich ein Taxi aus der Garage, wenn Sie Lust haben. + +{=================================== MISSION TABLE TAXIWA1 ===================================} + +[OUTTIME:TAXIWA1] +~r~Zu langsam, Mann, zu langsam! + +[TAX1_1:TAXIWA1] +Ok, eine V.I.P. müsste von Starfish Island abgeholt werden. Jemand interessiert? + +[TAX1_2:TAXIWA1] +Tommy hier. Ich übernehme das. + +[TAX1_3:TAXIWA1] +Das ist meine Fuhre. Hau ab! + +[TAX1_4:TAXIWA1] +Los, los, steigen Sie ein. Schnell! + +[TAX1_5:TAXIWA1] +Ok, ok! Aber tun Sie mir nichts! + +[TAXW1_1:TAXIWA1] +~g~Hol die V.I.P. auf Starfish Island ab. + +[TAXW1_2:TAXIWA1] +~g~Hol die V.I.P. zurück! Schalte den anderen Wagen aus! + +[TAXW1_3:TAXIWA1] +~r~Die V.I.P. ist Geschichte! + +[TAXW1_4:TAXIWA1] +~r~Die V.I.P. wurde abgesetzt! + +[TAXW1_6:TAXIWA1] +~g~Bring die V.I.P zum Flughafen! + +{=================================== MISSION TABLE TAXIWA2 ===================================} + +[TAX2_1:TAXIWA2] +An alle Wagen. Wir kriegen nirgends Fahrgäste. Was ist los mit euch? + +[TAX2_2:TAXIWA2] +VC-Taxi ist dauernd schneller als wir. Die haben einfach zu viele Autos. Keine Chance. + +[TAX2_3:TAXIWA2] +Mr. Vercetti, wenn Sie zufällig mithören: Sie müssen ein paar VC-Taxis ausschalten, sonst sind wir pleite! + +[TAXW2_1:TAXIWA2] +~g~Schalte 3 Taxis der Konkurrenz aus! + +{=================================== MISSION TABLE TAXIWA3 ===================================} + +[TAX3_1:TAXIWA3] +Wagen 13, eine Miss Cortez möchte von Ihnen ganz persönlich in Downtown abgeholt werden. + +[TAX3_2:TAXIWA3] +Ok, verstanden. Wagen 13 Ende. + +[TAX3_3:TAXIWA3] +Hmmm, keine Spur von Mercedes... + +[TAXW3_3:TAXIWA3] +~g~Schalte das Taxi des Anführers aus! + +[TAXW3_2:TAXIWA3] +~g~Halte durch, bis die Zeit abgelaufen ist. + +[TAX_AS1:TAXIWA3] +TAXIUNTERNEHMEN ERWORBEN + +[TAX_AS2:TAXIWA3] +~g~Kaufman-Taxis generiert nun bis zu $~1~ Einkünfte. Hol dir das Geld regelmäßig ab. + +[TAX3_4:TAXIWA3] +Wird Zeit, dass der Schutzengel von Kaufman-Taxis eine vor den Latz kriegt! + +[TAX3_5:TAXIWA3] +Hey, Freundchen, dir zieh ich das Fell über die Ohren! + +{ reVC updates } +{ new languages } +[FEL_JAP] +JAPANISCH + +[FEL_POL] +POLNISCH + +[FEL_RUS] +RUSSISCH + +{ new display menus } +[FET_GFX] +GRAFIK-SETUP + +[FED_MIP] +MIP MAPPING + +[FED_AAS] +KANTENGLÄTTUNG + +[FED_FIL] +TEXTURFILTERUNG + +[FED_BIL] +BILINEAR + +[FED_TRL] +TRILINEAR + +[FED_WND] +FENSTERMODUS + +[FED_FLS] +VOLLBILD + +[FEM_CSB] +CUTSCENE BALKEN + +[FEM_SCF] +BILDSCHIRMFORMAT + +[FEM_ISL] +KARTENSPEICHERNUTZUNG + +[FEM_LOW] +NIEDRIG + +[FEM_MED] +MITTEL + +[FEM_HIG] +HOCH + +[FEM_2PR] +PS2 ALPHA TEST + +[FEC_FRC] +FREIE KAMERA + +{ Linux joy detection } +[FEC_JOD] +JOYSTICK ERKENNEN + +[FEC_JPR] +Drücke eine beliebige Taste auf dem Joystick der für das Spiel verwendet werden soll, und er wird ausgewählt. + +[FEC_JDE] +Joystick erkannt + +{ mission restart } +[FET_RMS] +MISSION WIEDERHOLEN + +[FESZ_RM] +WIEDERHOLEN? + +[FED_VPL] +FAHRZEUG-PIPELINE + +[FED_PRM] +CHARAKTER KANTEN LICHT + +[FED_RGL] +GLÄNZENDE STRAßEN + +[FED_CLF] +FARBFILTER + +[FED_WLM] +WELT LIGHTMAPS + +[FED_MBL] +BEWEGUNGSUNSCHÄRFE + +[FEM_SIM] +SIMPEL + +[FEM_NRM] +NORMAL + +[FEM_MOB] +MOBILE + +[FED_MFX] +MATFX + +[FED_NEO] +NEO + +[FEM_PS2] +PS2 + +[FEM_XBX] +XBOX + +[FEC_IVP] +PAD VERTIKAL INVERTIEREN + +[FEM_NON] +NONE + +[FEC_DS2] +DUALSHOCK 2 + +[FEC_DS3] +DUALSHOCK 3 + +[FEC_DS4] +DUALSHOCK 4 + +[FEC_360] +XBOX 360 CONTROLLER + +[FEC_ONE] +XBOX ONE CONTROLLER + +[FEC_NSW] +NINTENDO SWITCH CONTROLLER + +[FEC_TYP] +GAMEPAD-TYP + +[FET_AGS] +KONTROLLEREINSTELLUNGEN + +[FEM_AUT] { aspect ratio related } +AUTO + +[FEM_PED] +PED DENSITY + +[FEM_CAR] +CAR DENSITY + +[DUMMY] +THIS LABEL NEEDS TO BE HERE !!! +AS THE LAST LABEL DOES NOT GET COMPILED diff --git a/gxt/miami/gxt.exe b/gxt/miami/gxt.exe new file mode 100644 index 00000000..0f55b760 Binary files /dev/null and b/gxt/miami/gxt.exe differ diff --git a/gxt/miami/italian.txt b/gxt/miami/italian.txt new file mode 100644 index 00000000..97f510cd --- /dev/null +++ b/gxt/miami/italian.txt @@ -0,0 +1,14735 @@ +{ + New strings are at the bottom of file. + Do not change the order of strings. + You can fix the typos of existing translation but please refrain from + unnecessary edits like rephasing because you think it suits better for your taste. +} + +[IN_VEH] +~g~Ehi! Torna nel veicolo! + +[HEY] +~g~Non procedere da solo, stai insieme ai tuoi compagni! + +[HELP3] +Ricorda che puoi eseguire uno scatto solo per brevi tratti. + +[HELP4_D] +Sposta la ~h~levetta analogica destra~w~ verso l'alto per ~h~accelerare~w~. + +[HELP5_D] +Sposta la ~h~levetta analogica destra~w~ verso il basso per ~h~frenare~w~ o, se il veicolo è fermo, per inserire la ~h~retromarcia~w~. + +[HELP7_A] +Tieni premuto il ~h~~k~~PED_LOCK_TARGET~~w~ per ~h~mirare~w~ con il fucile di precisione. + +[HELP7_D] +Tieni premuto il ~h~~k~~PED_LOCK_TARGET~~w~ per ~h~mirare~w~ con il fucile di precisione. + +[HELP10] +Questa stella indica il tuo livello di sospetto. + +[HELP11] +Più stelle hai, maggiore è il tuo livello di sospetto. + +[HELP13] +In alcuni casi potresti dover utilizzare passaggi non segnati sul radar. + +[TIMER] +Questa è una missione a tempo: devi portarla a termine prima che il contatore raggiunga lo zero. + +[HORN] +~g~Suona il clacson. + +[NOMONEY] +~g~Ti servono più soldi! + +[REWARD] +RICOMPENSA ~1~$ + +[M_FAIL] +MISSIONE FALLITA! + +[M_PASS] +MISSIONE COMPLETATA! ~1~$ + +[DEAD] +MASSACRATO! + +[BUSTED] +BECCATO! + +[WEATHE1] +FORZA TEMPO SOLEGGIATO + +[WEATHE2] +FORZA TEMPO MOLTO SOLEGGIATO + +[WEATHE3] +FORZA TEMPO NUVOLOSO + +[WEATHE4] +FORZA TEMPO PIOVOSO + +[WEATHE5] +FORZA TEMPO NEBBIOSO + +[WEATHE6] +TEMPO NORMALE + +[NUMBER] +~1~ + +[LOADCAR] +CARICAMENTO VEICOLO... (PREMI L1 PER ANNULLARE) + +[CARSOFF] +Veicoli disabilitati. + +[CARS_ON] +Veicoli abilitati. + +[TEXTXYZ] +Scrittura coordinate sul file... + +[CHEATON] +Modalità trucchi attivata + +[CHEATOF] +Modalità trucchi disattivata + +[IMPORT1] +Esci fuori e aspetta il tuo veicolo. + +[PAGEB11] +Lanciafiamme depositato nel nascondiglio + +[WANT_A] +Verrai arrestato solo se hai un ~h~livello di sospetto~w~. + +[WANT_B] +Il tuo ~h~livello di sospetto~w~ è rappresentato da una riga di stelle nell'angolo superiore destro dello schermo. + +[WANT_C] +Adesso hai un ~h~livello di sospetto~w~ pari a uno... + +[WANT_D] +due... + +[WANT_E] +tre... + +[WANT_F] +Man mano che il tuo ~h~livello di sospetto~w~ aumenta, attirerai l'attenzione di forze dell'ordine sempre più potenti. + +[WANT_G] +Se vieni ~h~'beccato'~w~, verrai portato alla più vicina stazione di polizia. + +[WANT_H] +I poliziotti prenderanno tutte le tue armi e parte dei tuoi risparmi come bustarella. + +[WANT_I] +Qualsiasi missione stavi affrontando verrà considerata come fallita. + +[WANT_J] +Scoprirai alcuni modi per ridurre il tuo livello di sospetto procedendo nel gioco. + +[WANT_K] +Se sei in un veicolo, i ~h~carozzieri~w~ potranno ~h~azzerare il tuo livello di sospetto~w~. + +[HEAL_B] +Quando sei ~h~'massacrato'~w~, verrai trasportato al più vicino ospedale. + +[HEAL_C] +Perderai tutte le tue armi e i dottori prenderanno parte dei tuoi risparmi per rimetterti in sesto. + +[HEAL_E] +Scoprirai alcuni modi per curarti o per proteggerti dagli attacchi procedendo nel gioco. + +[SAVE1] +Passa attraverso la corona per ~h~salvare la partita~w~. Non puoi salvare durante una missione. + +[SAVE2] +Qualsiasi veicolo parcheggiato nel garage verrà salvato insieme alla partita. + +[AMMU] +Entra in Ammu-Nation per comprare un'arma. + +[R_TIME] +TEMPO DI GARA: + +[PROP_1] +Non hai abbastanza soldi per comprare questa proprietà. + +[PROP_2] +Non puoi comprare una proprietà mentre sei in missione. + +[IND_ZON] +Vice City Beach + +[COM_ZON] +Vice City Mainland + +[BEACH1] +Ocean Beach + +[BEACH2] +Washington Beach + +[BEACH3] +Vice Point + +[GOLFC] +Leaf Links + +[STARI] +Starfish Island + +[DOCKS] +Porto + +[HAVANA] +Little Havana + +[HAITI] +Little Haiti + +[PORNI] +Prawn Island + +[DTOWN] +Downtown + +[VICE_C] +Vice City + +[A_PORT] +Escobar International + +[JUNKY] +Discarica + +[PISTOL] +Pistola + +[PYTHON] +.357 + +[UZI] +Uz-1 + +[TEC9] +Tec 9 + +[M4] +M4 + +[INGRAM] +Mac + +[MP5] +MP + +[RUGER] +Kruger + +[SNIPE] +Fucile di precisione + +[GRENADE] +Granate + +[SHOTGN1] +Fucile a pompa + +[SHOTGN2] +S.P.A.S. 12 + +[SHOTGN3] +Fucile a canne mozze + +[ARMOUR] +Giubbotto antiproiettile + +[LASER] +.308 Fucile di precisione + +[BASEBAT] +Mazza da baseball + +[HAMMER] +Martello + +[SCREWD] +Cacciavite + +[CLEVER] +Mannaia + +[MACHETE] +Macete + +[KNIFE] +Coltello + +[KATANA] +Katana + +[CHAINSA] +Motosega + +[G_COST] +~1~$ + +[CAR_1] +Ambulanza + +[MALIBU] +Il club Malibu + +[MANSION] +Villa di Diaz + +[TMANS] +Chez Tommy + +[STRIP] +Il club Pole Position + +[MALL1] +North Point Mall + +[BANKINT] +El Banco Corrupto Grande + +[RANGE] +Poligono di tiro + +[POL_HQ] +Polizia di VC + +[INT_B] +Un vecchio amico + +[INTB_1] +~g~Vai all'ufficio degli avvocati. + +[LAW_1] +Il party + +[LAW_2] +Rissa nel vicolo + +[LAW_3] +Furia sulla giuria + +[LAW_4] +Sommossa + +[COL_1] +Fottuto traditore + +[COL_2] +Sparatoria al Mall + +[COL_3] +Angeli custodi + +[COL_4] +Sissignore, signore! + +[COL_5] +Tutti sul ponte! + +[COK_1] +L'inseguimento + +[COK_2] +Phnom Penh '86 + +[COK_3] +La barca più veloce + +[COK_4] +Domanda e offerta + +[KENT_1] +Il braccio della morte + +[ASS_1] +Eliminazione + +[BUD_1] +Estorsione + +[BUD_2] +Rissa al bar + +[BUD_3] +Cop Land + +[CAP_1] +Fuori l'esattore + +[FIN_1] +Tieniti stretto gli amici... + +[BANK_1] +Senza via di fuga? + +[BANK_2] +Il tiratore + +[BANK_3] +L'autista + +[BANK_4] +La rapina + +[CNT_1] +Sfornare verdoni + +[CNT_2] +Colpire il corriere + +[PORN_1] +Giro di reclutamento + +[PORN_2] +Il dodo del dildo + +[PORN_3] +La foto di Martha + +[PORN_4] +Riflettori sul punto G + +[TAX_1] +Taxi di Kaufman + +[TAXI_1] +VIP + +[TAXI_2] +Rivalità amichevole + +[TAXI_3] +Cabmaggedon + +[ICE_1] +Distribuzione + +[TEX_1] +Ferro quattro + +[TEX_2] +Due in un colpo + +[TEX_3] +Il demolitore + +[PHIL_1] +Corriere delle armi + +[PHIL_2] +Broda e Saigon + +[BIKE_1] +Due ruote d'acciaio + +[BIKE_2] +Bollire la pentola + +[BIKE_3] +Valla a prendere! + +[ROCK_1] +Il succo dell'amore + +[ROCK_2] +Killer psicotico + +[ROCK_3] +Giro pubblicitario + +[ROCK_4] +Love Fist! + +[HAT_1] +Le polveri della zia + +[HAT_2] +Bombardamento! + +[HAT_3] +Tutto di nascosto + +[CUB_1] +Gara sull'acqua + +[CUB_2] +Carne da cannone + +[CUB_3] +Battaglia navale + +[CUB_4] +Voodoo a doppio taglio + +[JOB_1] +Morte per strada + +[JOB_2] +Elimina la moglie + +[JOB_3] +Incidente... + +[JOB_4] +L'ultima partenza + +[JOB_5] +Risoluzione + +[ANSWER] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per rispondere al cellulare. + +[MOB_01A] +Ciao vecchio mio! Sono Paul. Potrei avere delle informazioni per te, ma ti voglio parlare in privato. + +[MOB_01B] +Mi sto godendo un aperitivo al Malibu. + +[MOB_01C] +Credo che mi dovrai un favore o due dopo questo, carino. Ci vediamo dopo. + +[MOB_02A] +Ssssnniiiiffffff! Ehi! Tommy? Ciao, Tommy! + +[MOB_02B] +Ci sono dei problemi alla stamperia. Faresti meglio ad andare a controllare. + +[MOB_02C] +Un qualche tipo di casino. C'è qualcosa che non va. Devo andare. + +[MOB_03A] +Mr. Vercetti? Ho qui davanti a me un pezzo di carta che afferma + +[MOB_03B] +che lei si è preso carico dei debiti di BJ's Auto. + +[MOB_03C] +Con l'improvvisa scomparsa di BJ, non ho altra scelta + +[MOB_03D] +che ritenerla responsabile delle sue debolezze finanziarie. + +[MOB_03E] +Finché questo debito non sarà ripagato, + +[MOB_03F] +dovrà considerare le strade di Vice City non molto amichevoli. + +[MOB_04A] +Come butta, amico? + +[MOB_04B] +Ascolta Tommy, mi sono dimenticato di dirti che avremo bisogno di qualche gorilla extra per il concerto. + +[MOB_04C] +C'è una banda di motociclisti e sarebbero una perfetta pubblicità. + +[MOB_04D] +Occupatene tu e ti farò accedere al back stage dello spettacolo, OK? + +[MOB_05A] +Ehi, sono Mitch! Ottimo lavoro, Tommy: è bello riavere indietro la mia vecchia bambina. + +[MOB_05B] +Dì a Mr. Kent Paul che avrà la sorveglianza per lo spettacolo. + +[MOB_05C] +Hai la mia parola. + +[MOB_05D] +Adesso tieniti fuori dai guai. + +[MOB_06A] +Tommy, si è parlato molto di te, ragazzo. + +[MOB_06B] +Pensavo ti servisse un po' di calore, per cui la zia Poulet ti ha preparato una bella zuppa, hmmm? + +[MOB_06C] +Passa a trovarmi in cucina ogni tanto, OK Tommy? + +[MOB_08A] +Ehi, Tommy, ho pensato che ti potessero servire dei consigli. + +[MOB_08B] +Quando hai un'operazione in corso, dovrai passare a ritirare i soldi della settimana. + +[MOB_08C] +Lascia pensare ai ragazzi che abbiano il controllo del posto e loro cercheranno di ridurne i profitti, OK? + +[MOB_08D] +Ehi Ken, so come far funzionare le cose, OK. + +[MOB_08E] +OK, OK! Lo so che lo sai. Lo so. Stavo solo... + +[MOB_08F] +Ti stavo solo ricordando che lo so, cioè che tu sai che io so. + +[MOB_08G] +Solo per ricordartelo! + +[MOB_08H] +Sì Ken, va bene... + +[MOB_09A] +Ehi Leo! Ho del lavoro per te! + +[MOB_09B] +Non sono Leo! + +[MOB_09C] +Ehi, se Leo scopre che hai il suo telefono, ti farà a pezzi! + +[MOB_09D] +Forse Leo è già morto. Forse ho ucciso Leo e ho preso il suo telefono: non ci hai pensato, idiota? + +[MOB_09E] +Hai ucciso Leo? Devi avere due grandi cojones. Vuoi lavorare per me? + +[MOB_09F] +Passa dal bar di mio padre a Little Havana e ne possiamo parlare. + +[MOB_10A] +Tommy! Senti, devo chiederti un favore. + +[MOB_10B] +Steve! Come va il film? + +[MOB_10C] +Bene, bene. Senti ho, ehm, abbiamo bisogno di una scena di inseguimento, ma siamo a corto di denaro. + +[MOB_10D] +Ho lasciato qualche macchina in città. Sono certo che sai cosa fare. + +[MOB_10E] +OK Steve, terrò un occhio aperto. Ci vediamo! + +[MOB_11A] +Ciao figliolo, t'ho chiamato per darti un consiglio. + +[MOB_11B] +Ciao Avery. Come butta? + +[MOB_11C] +Ci sono un sacco di opportunità in questa città se possiedi le proprietà giuste, mi segui? + +[MOB_11D] +Tutto chiaro... + +[MOB_11E] +Volevo dirti di tenere gli occhi aperti: potresti trovare l'occasione giusta per far soldi. Ci becchiamo! + +[MOB_11F] +Ciao, Avery. + +[MOB12_A] +Ehi Tommy! Sono Avery! Ascolta: sono piuttosto incasinato al momento + +[MOB12_B] +e un mio rappresentante ha bisogno di un accompagnatore fino a Gator Keys. + +[MOB12_C] +Sto cercando di acquistare del terreno da quelle parti, per cui mando qualcuno in avanscoperta. + +[MOB12_D] +Mi faresti un favore? Potresti controllare che arrivi a destinazione tutto intero? + +[MOB12_E] +Certo, nessun problema Avery. Dove vuoi che lo passi a prendere? + +[MOB12_F] +Sta finendo un lavoro presso il cantiere. Gli ho detto che saresti passato lì. + +[MOB12_G] +Nessun problema. Ci vediamo, Avery. + +[MOB13_A] +Vercetti? VERCETTI!!! Maledizione, mi devi aiutare! + +[MOB13_B] +Mr. Moffat? Come sta la famiglia? + +[MOB13_C] +Brutto maledetto, BASTARDO! Mi hai sentito? + +[MOB13_D] +Beh, è stato un piacere chiacchierare... + +[MOB13_E] +ASPETTA! Aspetta Vercetti... Tommy, posso chiamarti Tommy? + +[MOB13_F] +Siamo entrambi professionisti, vero? Sono certo che sai riconoscere un affare, vero? + +[MOB13_G] +Non ho tempo da perdere, arriva al punto. + +[MOB13_H] +SOLDI. I soldi sono il punto! + +[MOB13_I] +Sono fuggito nuovamente da quei maledetti, ma non ci metteranno molto a trovarmi... Pensano si tratti di uno stupido gioco! + +[MOB13_J] +Sono a un telefono pubblico da qualche parte in questo posto di merda. + +[MOB13_K] +Aiutami a scappare prima che mi prendano e... e... oddio... + +[MOB13_L] +Beh, sono impegnato per i prossimi... + +[MOB13_M] +No! Non fare lo stronzo con me, abbi cuore! Nessuno dovrebbe fare una... una cosa simile! + +[MOB13_N] +Sono in ginocchio, Tommy: nella polvere a chiederti pietà... + +[MOB13_O] +Beh, forse potrei passare da quelle parti, vediamo se riesco a trovarti... + +[MOB13_P] +Oddio, stanno arrivando! Ti supplico, muoviti, muoviti! + +[MOB_14A] +Ehi, ciao Tommy: vedrai che mi adorerai! + +[MOB_14B] +Un piccolo uccellino mi ha detto che la divisione SWAT di Vice City ha un deposito presso un'importante banca, + +[MOB_14C] +dove tengono tutte le bustarelle intascate nel corso degli anni, + +[MOB_14D] +un po' come una pensione per gli anni a venire. + +[MOB_14E] +Logicamente, se questa informazione dovesse aiutarti a ottenere i soldi, + +[MOB_14F] +immagino che ti sentiresti obbligato a passarmene una parte... + +[MOB_14G] +Me lo ricorderò, grazie Kent. + +[MOB_14H] +Sono Paul. Vengo dal Kent, vicino a Londra, cretino. + +[MOB_14I] +Le mie conoscenze di geografia non sono mai state un gran che. + +[MOB15_A] +Tommy, amico, sono Paul, dal Kent, + +[MOB15_B] +un paio di squinzie hanno il tuo nome scritto dappertutto giù al Malibu. + +[MOB15_C] +Di cosa stai parlando? + +[MOB15_D] +Squinzie. Passere. Chiaro no? Strafighe. E anche ben messe, non credo fossero mignotte... + +[MOB15_E] +Dovresti venire a dare un'occhiata. + +[MOB16_A] +Tommy, sono Paulo, que pasa amigo? + +[MOB16_B] +Che cosa vuoi Paul: non sono interessato a vestiti di marca taroccati. + +[MOB16_C] +Molto divertente, amico, ma lo sai che non tratto merce di seconda classe. + +[MOB16_D] +Nah, ti ho chiamato per sapere se potevo avere una parte nei tuoi film, + +[MOB16_E] +quando ero in Inghilterra ho fatto un sacco di cose, sai? + +[MOB16_F] +Ho attributi più forniti di te, amico. + +[MOB16_G] +Paul, grazie per l'offerta, lo terrò presente. + +[MOB16_H] +Davvero, non scordarti di me dopo tutto quello che ho fatto per te. + +[MOB16_I] +È proprio ciò che sto cercando di dimenticare... + +[MOB17_A] +Tommy Vercetti: come va, Mr. Pezzo Grosso? + +[MOB17_B] +Ho saputo le tue novità: un vero giocatore in città, eh... + +[MOB17_C] +Paul, sei ubriaco. + +[MOB17_D] +No, idiota. Non sono ubriaco. + +[MOB17_E] +Solo un paio di bicchierini e qualche pillola: non dormo da un paio di giorni, sai... + +[MOB17_F] +Comunque, niente prediche. + +[MOB17_G] +Non sono uno stupido. Chi ti ha portato in questa città? Chi? Io, ecco chi. + +[MOB17_H] +Davvero? + +[MOB17_I] +Davvero! + +[MOB17_J] +Paul, sta' buono. Sono stato occupato, non fare l'idiota. + +[MOB17_K] +Non sono un idiota, chiaro? Questo è ciò che hanno detto in riformatorio. + +[MOB17_L] +Stai forse cercando guai, amico? Beh, stai per trovarli! + +[MOB17_M] +Tommy, amico. Per favore. Sei la mia più grande speranza! Per favore, non ridere di me. + +[MOB17_N] +Paul, cerca di dormire, davvero. + +[MOB18_A] +Tommy, sono Paulo, come butta? Bene amico, comunque, volevo farti una chiamata. + +[MOB18_B] +Ossignore, amico, non crederai mai che razza di strafiga ho appena incontrato. + +[MOB18_C] +Non so esattamente chi fosse, ma passeggiava per Little Havana. + +[MOB18_D] +Ha detto che si chiamava Mercedes o qualcosa del genere. Oddio, dovresti proprio vedere questa gnocca. + +[MOB18_E] +Riuscirebbe a succhiare via la mina da una matita. Mi ha detto che ero il migliore che avesse mai avuto e via dicendo. + +[MOB18_F] +Dovresti farci un giro. Ci vediamo! + +[MOB19_A] +Tommy V, sono KP. Kent Paul. Gira voce che ti vogliano fare a pezzi. + +[MOB19_B] +Tieni gli occhi bene aperti, amico e ricorda: io non ti ho detto niente al riguardo. + +[MOB_20A] +Ehi Tommy, sono Paul. Ho appena sentito che sei stato un ragazzaccio cattivo. + +[MOB_20B] +Qualcuno si è offeso per il tuo comportamento da grand'uomo, e adesso è davvero arrabbiato. + +[MOB_20C] +Beh, non dire che non ti ho avvertito: vantarsi è un gioco pericoloso, amico. + +[MOB_20D] +Comunque, ho anche sentito che c'è una taglia sulla tua testa e che qualcuno ti ha già preso di mira, + +[MOB_20E] +per cui guardati la schiena e ricordati di me, amico. + +[MOB71_A] +Tommy, Tommy, sono Cortez. Que pasa? + +[MOB71_B] +La situazione è interessante. A lei come va? + +[MOB71_C] +Tommy, qui è sempre una battaglia. Scusami per la battuta scontata, ma abbiamo sventato un altro golpe. + +[MOB71_D] +La gente non smette mai di pretendere di più. + +[MOB71_E] +Da quando sono tornato da Vice City, abbiamo avuto tre rivoluzioni e quattro golpe falliti. + +[MOB71_F] +Fortunatamente, sono stato promosso ogni volta. + +[MOB71_G] +Ti volevo chiedere di Mercedes. + +[MOB71_H] +Sì? Cosa vuole sapere? + +[MOB71_I] +Oh Tommy, ho sentito tutte queste storie e non so cosa pensare. + +[MOB71_J] +Forse tutti si divertono a umiliarmi. + +[MOB71_K] +Forse pensa di poter fare ciò che vuole ma dimmi, Tommy: è tutto vero? + +[MOB71_L] +ma dimmi, Tommy: è tutto vero? + +[MOB71_M] +Che cosa? + +[MOB71_N] +Le storie che ho sentito: sta pensando davvero di diventare avvocato? + +[MOB71_O] +Oh Tommy, che vergogna. Noi Cortez siamo una famiglia all'antica e non permetteremo mai che una di noi diventasse avvocato. + +[MOB71_P] +Ti prego, dimmi che non è vero. Non credo resisterei al colpo. + +[MOB71_Q] +Colonnello, le assicuro che Mercedes non diventerà mai un avvocato. Non si preoccupi. + +[MOB71_R] +Grazie Tommy, la vergogna sarebbe schiacciante. Lei è una vera dama, non una parassita, capisci? + +[MOB71_S] +Capisco, Colonnello. + +[MOB71_T] +Comunque, Tommy, devi scusarmi: è appena arrivato il nuovo ministro degli interni. + +[MOB71_U] +Qualche anno fa, ho ucciso suo padre in un golpe fallito, per cui devo essere gentile. Buona giornata, amico. + +[MOB22_A] +Tommy, ti stai rivelando molto utile, amico. + +[MOB22_B] +Grazie, Cortez. Cosa mi dice del nostro accordo? + +[MOB22_C] +Tommy, sto lavorando senza sosta nel tentativo di raggiungere il fondo di questa fossa piena di bugie e inganni, + +[MOB22_D] +hai la mia parola al riguardo, ma nel frattempo, + +[MOB22_E] +ti prego di accettare i più sinceri ringraziamenti da parte della gente per cui stai lavorando. + +[MOB_25A] +Tommy, sono Cortez. I Francesi mi stanno causando tutti i problemi possibili. + +[MOB_25B] +Maledetti ipocriti. Hanno passato secoli a derubare la povera gente e adesso osano chiamare me ladro! + +[MOB_25C] +Avrò bisogno del tuo aiuto il prima possibile. + +[MOB_25D] +Sbrigati, Tommy: ho bisogno di te. Odio questi dannati Francesi. + +[MOB_26A] +Ehi, Tommy? + +[MOB_26B] +Sì? + +[MOB_26C] +Sono Baker. Volevo dirti che ho davvero apprezzato lo show. + +[MOB_26D] +Io e i ragazzi vogliamo ringraziarti e ricordarti + +[MOB_26E] +che hai tutto il nostro rispetto. Buona giornata, continua così. + +[MOB_29A] +Salve, parlo con Mr. Tommy Vercetti? + +[MOB_29B] +Sì. + +[MOB_29C] +Beh, ho sentito dire che sei tu l'uomo da chiamare in caso di infestazione da parassiti. + +[MOB_29D] +Forse... + +[MOB_29E] +Beh, ho in corso un'infestazione davvero pesante: Haitiani da tutte le parti. + +[MOB_29F] +Mi chiamo Umberto Robina e vorrei incontrarmi con te al Cafe Robina il prima possibile, + +[MOB_29G] +perché questa volta i fottuti Haitiani sono andati troppo oltre. + +[MOB_29H] +Test + +[MOB_30A] +Tommy, sono Umberto Robina. + +[MOB_30B] +Come va al caffè? + +[MOB_30C] +Oh, meraviglioso. Incredibile, Tommy, incredibile. Niente perdenti, Tommy, solo veri uomini, sai no, e donne stupende. + +[MOB_30D] +Comunque, volevo dirti che per tutti noi adesso sei un vero Cubano. + +[MOB_30E] +Hai provato di essere all'altezza. Hai provato di avere due grandi cojones. + +[MOB_30F] +Err... grazie, Umberto. Nessuno mi ha detto una cosa simile da quando ho lasciato la galera. Ci becchiamo in giro. + +[MOB_33A] +Tommy, sono Phil: smettila di perdere tempo con le vaccate e ascoltami, intesi? + +[MOB_33B] +Bene. Ho un bel po' di broda bella forte quasi a perfetta fermentazione e mi chiedevo se ne volevi assaggiare un po'. + +[MOB_33C] +Ti assicuro, Tommy, che se ami bere, o se devi rimuovere della vernice, questa è la roba che fa per te. + +[MOB_33D] +Per me è stata una bomba, anche se non ci vedo più bene da un occhio. Ti aspetto, ciao. + +[MOB_34A] +Tommy, mi è davvero piaciuto lavorare con te. Non mi divertivo così dalle scorrerie in Vietnam, amico. + +[MOB_34B] +Se mai ti servisse qualcosa, chiamami, capito? + +[MOB_34C] +Ricordo sempre quelli coi quali ho prestato servizio, + +[MOB_34D] +e sono sicuro di poterti dare una mano, chiaro? + +[MOB_35A] +Tommy, la ferita si sta rimarginando bene. È divertente: + +[MOB_35B] +ho combattuti in sei campi di battaglia e sono sempre uscito senza un graffio... e adesso questo! + +[MOB_35C] +Phil senza un braccio. Comunque, ho una abbondante selezione di armi a una mano, per cui almeno non sarò disarmato! + +[MOB_35D] +Comunque, figliolo, lasciamo stare le stronzate sentimentali e vedi di scolarti qualcosa da parte mia! + +[MOB_36A] +Tommy, sono Phil. Ti volevo ringraziare di essere stato sul campo, figliolo. + +[MOB_36B] +Maledetti Viet, non perdono occasione per farti un attentato... + +[MOB_36C] +Comunque, la ferita sta guarendo: adesso non mi sentirò più in colpa quando riceverò il mio consueto assegno per disabili. Grazie, amico. + +[MOB_40A] +Ehi, Tommy, sono Sonny. Come va l'abbronzatura? + +[MOB_40B] +Non ho preso il sole. + +[MOB_40C] +Beh, non hai preso neanche i miei soldi, per cui mi chiedevo: + +[MOB_40D] +che cosa stai combinando? Dimmi un po', Tommy, che cosa fai? + +[MOB_40E] +Sto cercando i soldi, Sonny, non ti preoccupare. + +[MOB_40F] +Invece mi preoccupo, Tommy, è il mio stile, + +[MOB_40G] +poiché ho sempre dei problemi con gente che non ama rispettare i patti. + +[MOB_40H] +Non diventare inaffidabile, Tommy, fallo per me... + +[MOB_40I] +Fai un favore a entrambi. Non vedo l'ora di ricevere buone nuove. + +[MOB_41A] +Tommy, ti ricordi di me? + +[MOB_41B] +Ehi, Sonny. + +[MOB_41C] +Sì, sono proprio io, Sonny. Siamo vecchi amici, + +[MOB_41D] +e tu non mi scrivi mai, non mi chiami mai. Non vuoi più essere mio amico? + +[MOB_41E] +Sono stato occupato a mettere le cose a posto. Non mi hai dato un gran che di aiuto da queste parti. + +[MOB_41F] +Ah, è questa la mia colpa? Beh, ho sentito che sei stato molto occupato. + +[MOB_41G] +Occupato a uccidere i baroni della droga. Occupato a prendere il potere. + +[MOB_41H] +Non dimenticarti di noi, Tommy, poiché, te lo assicuro, io non mi sono dimenticato di te. + +[MOB_42A] +Tommy. + +[MOB_42B] +Sonny. + +[MOB_42C] +Hai di sicuro dei problemi di udito, per cui proverò ancora... + +[MOB_42D] +Tommy, dove sono i maledetti soldi, dov'è la maledetta roba e dov'è la mia parte dei tuoi ultimi guadagni? + +[MOB_42E] +Tommy, mi stai facendo fare la figura dell'idiota, e io non sto ridendo. + +[MOB_43A] +Tommy, Tommy, Tommy, avevo Sonny al telefono... OK, sei con me? + +[MOB_43B] +Non so te, ma so che qualcuno sta minacciando di morte la mia famiglia, + +[MOB_43C] +il che mi sta davvero facendo cagare sotto. Che cosa intendi fare? + +[MOB_43D] +Stai calmo, Ken, andrà tutto bene. + +[MOB_43E] +Io SONO calmo, calmo quanto un uomo in pericolo di vita! + +[MOB_43F] +Ken, ora come ora ho altre cose per la testa, + +[MOB_43G] +ci occuperemo di Forelli al momento giusto. + +[MOB_43H] +Io sono calmo. Non ti sembro calmo? Forse è la morte incombente che mi sta facendo tremare la voce. + +[MOB45_A] +Tommy, dobbiamo parlare di un po' di cose. + +[MOB45_B] +C'è qualche problema, Lance? + +[MOB45_C] +Si tratta di te, amico, non mi sembra di ricevere la fetta che merito. + +[MOB45_D] +E inoltre, mi hai imbarazzato davanti ai ragazzi. Non lo posso permettere. + +[MOB45_E] +Lance, non è andata così. Hai commesso degli errori. + +[MOB45_F] +Tommy, non sono il tuo fattorino. Non sono un tuo scagnozzo. + +[MOB45_G] +Lance, non fare casini e non avremo alcun problema. Se faccio un errore, me la puoi far pagare in qualsiasi momento. + +[MOB45_H] +Tommy, ho fatto tutto per te e mi tratti come un idiota. Non farlo. + +[MOB45_I] +Lance, non ti causerò problemi e non ti pugnalerò alle spalle, promesso. + +[MOB45_J] +Rilassati. La cosa è già abbastanza difficile senza che tu ti metta a fare il tenero con me. + +[MOB45_K] +Fidati: mi hai sentito, mi hai sentito? + +[MOB45_L] +Ti ho sentito, ma Tommy, non sopporterò ancora per lungo. + +[MOB45_M] +Lance, non fare così: adesso sono io ad avvertirti. + +[MOB45_N] +Mi hai sentito? Rilassati, prenditi qualche giorno e poi ne riparliamo, OK? + +[MOB46_A] +Tommy, sono Lance. + +[MOB46_B] +Sì? + +[MOB46_C] +Sono felice di sentirti Lance. Forza, stai tranquillo, stai tranquillo. + +[MOB46_D] +Sono nel mezzo di un problema: ho bisogno di te. + +[MOB46_E] +Niente, giusto per dire. Tommy, ce ne possiamo occupare. + +[MOB46_F] +Io e te, nessun problema. Capisci ciò che intendo? + +[MOB46_G] +Dobbiamo proprio farlo, se no siamo entrambi morti, Lance. + +[MOB46_H] +Siamo andati troppo oltre, ma grazie della chiamata. Ci sentiamo più tardi. + +[MOB_47A] +Tommy, sono Lance. Abbiamo un problema serio. Vieni qui in fretta. + +[MOB52_A] +Ehi, Leo, sembra ci sia un compratore per la roba di Diaz. + +[MOB52_B] +Devi fargli uno squillo, ragazzo, e preparare l'accordo, chiaro? + +[MOB52_C] +Dove ti trovi adesso? + +[MOB52_D] +Stai bene Leo? Mi sembri un po' strano... + +[MOB52_E] +Dimmi dove ti trovi. + +[MOB52_F] +Chi diavolo sei? Passami subito Leo! + +[MOB52_G] +Leo se ne è andato per un po' e adesso sono io in carica. + +[MOB52_H] +Fottiti, stronzo! + +[MOB54_A] +Ciao Tommy! + +[MOB54_B] +Ciao Mercedes, come butta? + +[MOB54_C] +Ho un nuovo appartamento in Vice Point... + +[MOB54_D] +magari un giorno di questi potresti farci un salto. + +[MOB54_E] +Mi piacerebbe, ci sentiamo. + +[MOB55_A] +Tommy, sono io. + +[MOB55_B] +Ciao Mercedes. + +[MOB55_C] +Tommy, sono così annoiata: perché non ci divertiamo un po' insieme? + +[MOB55_D] +Cosa intendi dire? + +[MOB55_E] +Beh, lo so che sei occupato a combattere, uccidere e corrompere gente, + +[MOB55_F] +ma io ho voglia di divertirmi un po'. Non ti scordare di me, capito? + +[MOB56_A] +Tommy, ho sentito che hai ucciso Ricardo Diaz. + +[MOB56_B] +C'è stato uno sfortunato incendio nella sua villa. + +[MOB56_C] +Credo sia morto bruciato nella sua camicia in acrilico. + +[MOB56_D] +Tommy, sono così fiera di te. Lo sapevo che eri un vero uomo. + +[MOB56_E] +Era un inutile idiota senza testa: mi rendi orgogliosa di essere tua amica. + +[MOB56_F] +No, lo so che sei impegnato a prendere il possesso di questa città, + +[MOB56_G] +ma non dimenticarti di me, capito? + +[MOB57_A] +Sono Mercedes. Non ti amo più, Tommy. + +[MOB57_B] +Proprio no, onestamente. Tu non sei più gentile con la tua Mercedes. + +[MOB57_C] +Non mi tratti più come una signora: mi ignori e io ti odio. + +[MOB57_D] +Insisto che tu venga a trovarmi immediatamente! + +[MOB58_A] +Tommy. + +[MOB58_B] +Ehi Mercedes. + +[MOB58_C] +Ehi sì, Mr. Tipo Duro. Sono davvero arrabbiata con te, Tommy. + +[MOB58_D] +Non farmi più passare le serate con Jezz Torrent. + +[MOB58_E] +È davvero patetico. A metà delle scale ha iniziato a piagnucolare per il suo cane + +[MOB58_F] +che è morto quando aveva sette anni e a ripetere che la mamma non lo ha mai amato. + +[MOB58_G] +E Tommy, indossa una parrucca e un reggiseno quando è in intimità. + +[MOB58_H] +Non sono molto felice di te! + +[MOB59_A] +Ooh Tommy, sono Mercedes. + +[MOB59_B] +Volevo solo dirti che mi sono divertita così tanto sul set del film. + +[MOB59_C] +Se hai qualcos'altro del genere, fammelo sapere. + +[MOB59_D] +Lo penso davvero. Ho sempre desiderato fare l'attrice. + +[MOB59_E] +Credo di aver imparato molto sull'approccio drammatico. + +[MOB59_F] +È così illuminante! Grazie, grazie! Spero di vederti presto! Adios. + +[MOB_99] +Raggiungi il telefono pubblico. + +[MOB_98] +Raggiungi il telefono pubblico. + +[MOB_97] +Raggiungi il telefono pubblico. + +[MOB_96] +Raggiungi il telefono pubblico. + +[MOB_95] +Raggiungi il telefono pubblico. + +[A_TIME] ++~1~ secondi + +[F_RANGE] +~g~Sei fuori dal raggio d'azione della radio. Avvicinati alla stazione dei pompieri! + +[DODO_FT] +Hai volato per ~1~ secondi! + +[GA_8] +Usa il detonatore per attivare la bomba. + +[GA_10] +Non male. Eccoti ~1~$ + +[GA_11] +Ne abbiamo già una. A noi non serve! + +[GA_12] +Bomba innescata + +[GA_13] +Un furto da manuale. Completa la lista e ci sarà un bonus per te. + +[GA_14] +Hai portato tutte le macchine. BENE! Eccoti un bonus... + +[GA_15] +Spero che ti piaccia il nuovo colore. + +[GA_16] +La riverniciatura è gratuita. + +[GA_19] +Non siamo interessati a questo modello. + +[GA_20] +Di questo modello ne abbiamo in abbondanza. Mi dispiace, non siamo interessati. + +[CHASE] +Massima attenzione dei media + +[CHASE1] +Ignoto + +[CHASE2] +Noioso + +[CHASE3] +Appena interessante + +[CHASE4] +Giornale locale, pagina 7 + +[CHASE5] +Giornale locale, prima pagina + +[CHASE6] +Vice Courier, 1 colonna + +[CHASE7] +Vice Courier, prima pagina + +[CHASE8] +TV locale, 3 AM + +[CHASE9] +TV locale, news + +[CHASE10] +TV locale, servizio in diretta + +[CHASE11] +UFA Today, pagina 12 + +[CHASE12] +UFA Today, pagina 4 + +[CHASE13] +Foto in UFA Today + +[CHASE14] +TV nazionale, 4 AM + +[CHASE15] +TV nazionale, news + +[CHASE16] +TV nazionale, servizio in diretta + +[CHASE17] +Avvenimento internazionale + +[CHASE18] +Crisi nazionale + +[CHASE19] +Crisi internazionale + +[CHASE20] +Evento mondiale + +[CHASE21] +Roba da leggenda + +[CR_1] +La gru non può alzare questo veicolo. + +[PU_MONY] +Non hai abbastanza soldi. + +[CO_ALL] +Li hai presi tutti. Eccoti un piccolo extra... + +[FEM_ON] +ON + +[FEM_OFF] +OFF + +[FEM_YES] +Sì + +[FEM_NO] +No + +[FEM_RET] +Riprova + +[FEC_NA] +ND + +[FEC_CWL] +Scorri armi a sinistra + +[FEC_CWR] +Scorri armi a destra + +[FEC_LOF] +Guarda avanti + +[FEC_TAR] +Bersaglio + +[FEC_MOV] +Movimento + +[FEC_CAM] +Modalità visuale + +[FEC_PAU] +Pausa + +[FEC_ENV] +Sali sul veicolo + +[FEC_JUM] +Salta + +[FEC_ATT] +Attacca o fuoco con arma + +[FEC_RUN] +Corri + +[FEC_FPC] +Visuale in prima persona + +[FEC_LL] +Guarda a sinistra + +[FEC_LB] +Guarda indietro + +[FEC_LR] +Guarda a destra + +[FEC_HOR] +Clacson + +[FEC_VES] +Comandi veicolo + +[FEC_BRA] +Freno o retromarcia + +[FEC_HAB] +Freno a mano + +[FEC_CAW] +Arma vettura + +[FEC_ACC] +Accelera + +[FEC_CCF] +Configurazione : + +[FEC_CF1] +Config 1 + +[FEC_CF2] +Config 2 + +[FEC_CF3] +Config 3 + +[FEC_CF4] +Config 4 + +[FEC_CDP] +Schermata controller : + +[FEC_ONF] +A piedi + +[FEC_INC] +In macchina + +[FEC_VIB] +Vibrazione : + +[FEL_ENG] +Inglese + +[FEL_FRE] +Francese + +[FEL_GER] +Tedesco + +[FEL_ITA] +Italiano + +[FEL_SPA] +Spagnolo + +[FED_DBG] +Menu Debug + +[FED_RID] +Reload IDE + +[FED_RIP] +Reload IPL + +[FED_PAH] +Parse Heap + +[FED_DFL] +CTheScripts::DbgFlag + +[FED_DLS] +Big White Debug Light Switched + +[FED_SPR] +Show Ped Road Groups + +[FED_SCR] +Show Car Road Grups + +[FED_SCZ] +Show Cull Zones + +[FED_DSR] +Debug Streaming Requests + +[FED_SCP] +gbShowCollisionPolys + +[PL_STAT] +Statistiche giocatore + +[PE_WAST] +Persone massacrate + +[PE_WSOT] +Persone massacrate da altri + +[TM_BUST] +Arresti subiti + +[GNG_WST] +Membri delle gang massacrati + +[DED_CRI] +Criminali massacrati + +[PER_COM] +Percentuale completata + +[KGS_EXP] +Kg di esplosivo utilizzati + +[ACCURA] +Precisione + +[ST_WEAP] +Budget armi + +[ST_PROP] +Budget proprietà + +[ST_AUTO] +Budget riparazioni e colorazione auto + +[ST_PHOT] +Fotografie scattate + +[ST_LOAN] +Visite degli usurai + +[ST_STOR] +Edifici razziati + +[ST_MOVI] +Stunt nei film + +[ST_PIZZ] +Pizze consegnate + +[ST_GARB] +Spazzatura raccolta + +[ST_ICEC] +Gelati venduti + +[TOP_SHO] +Miglior punteggio al poligono + +[SHO_RAN] +Graduatoria al poligono + +[SEAGULL] +Gabbiani abbattuti + +[PROPOWN] +Proprietà posseduti + +[ST_TIME] +Tempo di gioco + +[ST_FTIM] +Ore di volo + +[ST_PRAN] +Graduatoria pilota + +[ST_RAN0] +Apprendista + +[ST_RAN1] +Navigatore + +[ST_RAN2] +Copilota + +[ST_RAN3] +Pratico + +[ST_RAN4] +Competente + +[ST_RAN5] +Anziano + +[ST_RAN6] +Asso + +[ST_RAN7] +Barone Rosso + +[ST_DRWN] +Pesci sfamati + +[ST_FASH] +Budget abbigliamento + +[ST_DAMA] +Proprietà distrutte + +[TM_DED] +Visite all'ospedale + +[DAYSPS] +Giorni trascorsi nel gioco + +[NUMSHV] +Visite al rifugio + +[MXCARD] +Distanza max salto FOLLE (ft) + +[MXCARJ] +Altezza max salto FOLLE (ft) + +[MXCARDM] +Distanza max salto FOLLE (m) + +[MXCARJM] +Altezza max salto FOLLE (m) + +[MXFLIP] +Numero max ribaltamenti FOLLI + +[MXJUMP] +Numero max rotazioni FOLLI + +[BUL_FIR] +Colpi sparati + +[BUL_HIT] +Colpi a segno + +[SPRAYIN] +Riverniciature + +[BSTSTU] +Migliore acrobazia FOLLE + +[INSTUN] +Acrobazia folle + +[PRINST] +Acrobazia folle perfetta + +[DBINST] +Doppia acrobazia folle + +[DBPINS] +Doppia acrobazia folle perfetta + +[TRINST] +Tripla acrobazia folle + +[PRTRST] +Tripla acrobazia folle perfetta + +[QUINST] +Quadrupla acrobazia folle + +[PQUINS] +Quadrupla acrobazia folle perfetta + +[NOSTUC] +Nessuna acrobazia FOLLE effettuata + +[NOUNIF] +Acrobazie uniche completate + +[NMISON] +Missioni provate + +[PASDRO] +Passeggeri scaricati + +[MONTAX] +Soldi guadagnati in taxi + +[DAYPLC] +Spesa giornaliera polizia + +[CRIMRA] +Livello criminalità: + +[STPR_1] +Malibu + +[STPR_2] +Tipografia + +[STPR_3] +Studio cinematografico + +[STPR_4] +Fabbrica di gelato + +[STPR_5] +Lavaggio auto + +[STPR_6] +Compagnia di taxi + +[STPR_7] +Cantiere navale + +[SET1EN] +Config. 1. Abilitata. + +[GMSAVE] +Salva partita + +[FEDS_TB] +Indietro + +[FEST_OO] +su + +[FEC_TUC] +Comandi torretta + +[FEC_RS3] +Stazioni radio (tasto L3) + +[FEC_HO3] +Clacson (tasto L3) + +[C_FAIL] +Missione Vigilante terminata! + +[C_ESCP] +~r~Il sospettato è fuggito! + +[C_VIGIL] +BONUS VIGILANTE! + +[HEAL_A] +La tua ~h~salute~w~ è indicata in color arancione in alto a destra sullo schermo. + +[WRONGCD] +Disco errato. Inserisci il disco corretto. + +[NOCD] +Il cassetto del disco è vuoto. Inserisci il disco. + +[OPENCD] +Il cassetto del disco è aperto: è necessario chiuderlo. + +[CDERROR] +Errore di lettura dal DVD Grand Theft Auto: Vice City. + +[RESTART] +Avvio di una nuova partita + +[GA_3] +Niente più sconti. $100 per la riverniciatura! + +[GA_1] +Wow! Non tocco niente di COSÌ caldo! + +[GA_1A] +Torna quando non sarai così occupato... + +[HELP9_C] +Premi il ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~ col fucile di precisione. + +[TAXI2] +~r~Tempo scaduto! + +[PAGEB13] +Salute consegnata al nascondiglio + +[PAGEB14] +Adrenalina consegnata al nascondiglio + +[FESZ_CA] +Annulla + +[FES_NGA] +Nuova partita + +[FES_CAN] +Annulla + +[FESZ_QL] +Tutti i progressi della partita attuale non ancora salvati andranno perduti. Vuoi procedere con il caricamento? + +[FESZ_QD] +Vuoi eliminare questa partita salvata? + +[FESZ_QO] +Vuoi procedere con la sovrascrittura di questa partita salvata? + +[FESZ_QR] +Sei sicuro di voler iniziare una nuova partita? Tutti i progressi fatti fino all'ultimo salvataggio andranno perduti. Vuoi procedere? + +[T4X4_1] +'Parco divertimenti PCJ' + +[BMX_1] +'Prova del fango' + +[BMX_2] +'Tracciato di prova' + +[BMXFAIL] +~r~Non sei riuscito a stabilire un nuovo record! + +[T4X4_3] +'TENUTA!' + +[MM_1] +'FOLLIA CONICA' + +[T4X4_F] +~r~Ti sei ritirato! Troppo difficile per te? + +[LANDSTK] +Landstalker + +[IDAHO] +Idaho + +[STINGER] +Stinger + +[LINERUN] +Linerunner + +[PEREN] +Familiare + +[SENTINL] +Sentinel + +[RIO] +Rio + +[PATRIOT] +Patriot + +[FIRETRK] +Camion dei pompieri + +[TRASHM] +Nettezza urbana + +[STRETCH] +Stretch + +[MANANA] +Manana + +[INFERNS] +Infernus + +[VOODOO] +Voodoo + +[PONY] +Pony + +[MULE] +Mule + +[CHEETAH] +Cheetah + +[AMBULAN] +Ambulanza + +[FBICAR] +FBI Washington + +[MOONBM] +Moonbeam + +[ESPERAN] +Esperanto + +[TAXI] +Taxi + +[WASHING] +Washington + +[BOBCAT] +Bobcat + +[WHOOPEE] +Mr Whoopee + +[BFINJC] +BF a iniezione + +[HUNTER] +Hunter + +[POLICAR] +Polizia + +[ENFORCR] +Volante + +[SECURI] +Securicar + +[BANSHEE] +Banshee + +[PREDATR] +Predator + +[BUS] +Autobus + +[RHINO] +Rhino + +[BARRCKS] +Caserma OL + +[CUBAN] +Cuban Hermes + +[HELI] +Elicottero + +[DODO] +Dodo + +[COACH] +Pullman + +[CABBIE] +Vecchio taxi + +[STALION] +Stallion + +[RUMPO] +Rumpo + +[RCBANDT] +Bandit radiocomandato + +[ROMERO] +Carro funebre di Romero + +[PACKER] +Packer + +[ADMIRAL] +Admiral + +[SQUALO] +Squalo + +[SEASPAR] +Sea Sparrow + +[PIZZABO] +Pizza Boy + +[GANGBUR] +Gang Burrito + +[TROPIC] +Tropic + +[SPEEDER] +Speeder + +[REEFER] +Reefer + +[FLATBED] +Flatbed + +[YANKEE] +Yankee + +[CADDY] +Caddy + +[ZEBRA] +Taxi Zebra + +[TOPFUN] +Top Fun + +[SKIMMER] +Skimmer + +[PCJ600] +PCJ 600 + +[PHOENIX] +Phoenix + +[FAGGIO] +Faggio + +[FREEWAY] +Freeway + +[RCBARON] +Baron radiocomandato + +[RCRAIDE] +Raider radiocomandato + +[GLENDAL] +Glendale + +[OCEANIC] +Oceanic + +[SANCHEZ] +Sanchez + +[SPARROW] +Sparrow + +[LOVEFIS] +Love Fist + +[COASTG] +Coast Guard + +[DINGHY] +Dinghy + +[HERMES] +Hermes + +[SABRE] +Sabre + +[SABRETU] +Sabre Turbo + +[WALTON] +Walton + +[REGINA] +Regina + +[COMET] +Comet + +[DELUXO] +Deluxo + +[BURRITO] +Burrito + +[SPAND] +Spandex Express + +[MARQUIS] +Marquis + +[BAGGAGE] +Baggage Handler + +[KAUFMAN] +Taxi Kaufman + +[COASTMA] +Coastguard Maverick + +[MAVERIC] +Maverick + +[RANCHER] +Rancher + +[FBIRANC] +FBI Rancher + +[VIRGO] +Virgo + +[GREENWO] +Greenwood + +[HOTRING] +Hotring Racer + +[BLISTAC] +Blista Compact + +[FEST_DF] +Distanza a piedi (miglia) + +[FEST_DC] +Distanza in auto (miglia) + +[FESTDFM] +Distanza a piedi (metri) + +[FESTDCM] +Distanza in auto (metri) + +[TOT_DIS] +Distanza totale (miglia) + +[TOTDISM] +Distanza totale (metri) + +[DISTHEL] +Distanza in elicottero (miglia) + +[DISTHEM] +Distanza in elicottero (metri) + +[DISTBOA] +Distanza in barca (miglia) + +[DISTBOM] +Distanza in barca (metri) + +[FEST_LS] +Persone salvate in ambulanza + +[FEST_CC] +Criminali uccisi in missioni Vigilante + +[FEST_FE] +Incendi estinti + +[FEST_RP] +Violenze eseguite + +[FEST_MP] +Missioni completate + +[FEST_BB] +Bling-bling Scramble: + +[FEST_H0] +Maggior numero di punti di controllo + +[FEST_GC] +Auto delle gang distrutte: + +[FEST_H1] +Distruzione Diablo + +[FEST_H2] +Massacro Mafioso + +[FEST_H3] +Calamità al Casinò + +[FEST_H4] +Demolizioni Rumpo + +[USJ] +BONUS ACROBAZIA UNICA! + +[RATNG1] +Cittadino onesto + +[RATNG2] +Sconosciuto + +[RATNG3] +Pezzente + +[RATNG4] +Furfantello + +[RATNG5] +Vandalo + +[RATNG6] +Fattorino + +[RATNG7] +Borsaiolo + +[RATNG8] +Cleptomane + +[RATNG9] +Teppista + +[RATNG10] +Ladruncolo + +[RATNG11] +Sanguisuga + +[RATNG12] +Truffatore + +[RATNG13] +Farabutto + +[RATNG14] +Informatore + +[RATNG15] +Imbroglione + +[RATNG16] +Bullo + +[RATNG17] +Canaglia + +[RATNG18] +Scapestrato + +[RATNG19] +Ruffiano + +[RATNG20] +Fuorilegge + +[RATNG21] +Delinquente + +[RATNG22] +Ladro + +[RATNG23] +Scagnozzo + +[RATNG24] +Tirapiedi + +[RATNG25] +Avanzo di galera + +[RATNG26] +Ex galeotto + +[RATNG27] +Criminale + +[RATNG28] +Picchiatore + +[RATNG29] +Saggio + +[RATNG30] +Autista + +[RATNG31] +Guardia del corpo + +[RATNG32] +Attaccabrighe + +[RATNG33] +Cacciatore di taglie + +[RATNG34] +Vigilante + +[RATNG35] +Ronin + +[RATNG36] +Riparatore + +[RATNG37] +Sicario + +[RATNG38] +Socio + +[RATNG39] +Macellaio + +[RATNG40] +Pulitore + +[RATNG41] +Assassino + +[RATNG42] +Consigliere + +[RATNG43] +Consulente + +[RATNG44] +Braccio destro + +[RATNG45] +Esecutore + +[RATNG46] +Tenente + +[RATNG47] +Aiutante del boss + +[RATNG48] +Capo + +[RATNG49] +Boss + +[RATNG50] +Intoccabile + +[RATNG51] +Don + +[RATNG52] +Re della fottuta città + +[PAGE_00] +. + +[WELCOME] +BENVENUTO A + +[TSCORE] +GUADAGNI: ~1~$ + +[PBOAT_2] { reVC update } +Premi il ~h~~k~~VEHICLE_FIREWEAPON~~w~ per sparare con i cannoni della barca. + +[HJSTAT] +Distanza: ~1~.~1~m Altezza: ~1~.~1~m Ribaltamenti: ~1~ Rotazioni: ~1~_ + +[HJSTATW] +Distanza: ~1~.~1~m Altezza: ~1~.~1~m Ribaltamenti: ~1~ Rotazioni: ~1~_ E che grande atterraggio! + +[ATUTOR] +Premi il ~h~~k~~TOGGLE_SUBMISSIONS~~w~ per attivare o disattivare le missioni Infermiere. + +[FEST_HA] +Livello massimo della missione Infermiere + +[C_KILLS] +CRIMINALI UCCISI: ~1~ + +[HJSTATF] +Distanza: ~1~ piedi Altezza: ~1~ piedi Ribaltamenti: ~1~ Rotazioni: ~1~_ + +[HJSTAWF] +Distanza: ~1~ piedi Altezza: ~1~ piedi Ribaltamenti: ~1~ Rotazioni: ~1~_ E che grande atterraggio! + +[CRED001] +ROCKSTAR NORTH + +[CRD001A] +DIRETTORE DELLO STUDIO + +[CRD001B] +ANDREW SEMPLE + +[CRED002] +PRODUTTORE + +[CRD002A] +DIRETTORE DI SVILUPPO + +[CRED003] +LESLIE BENZIES + +[CRED004] +DIRETTORE ARTISTICO + +[CRED005] +AARON GARBUT + +[CRED006] +DIRETTORI TECNICI + +[CRED007] +OBBE VERMEIJ + +[CRED008] +ADAM FOWLER + +[CRED010] +ANDREW DUTHIE + +[CRED011] +CRAIG FILSHIE + +[CRED012] +WILLIAM MILLS + +[CRED013] +CHRIS ROTHWELL + +[CRD013A] +IMRAN SARWAR + +[CRD013B] +JAMES WORRALL + +[CRD013C] +JOHN HAIME + +[CRED014] +SCRITTO DA + +[CRED015] +JAMES WORRALL + +[CRED016] +PAUL KUROWSKI + +[CRED017] +DAN HOUSER + +[CRED018] +PROGETTAZIONE PERSONAGGI PRINCIPALI + +[CRD018A] +PROGETTAZIONE PERSONAGGI + +[CRED019] +IAN MCQUE + +[CRD019A] +TOKS SOLARIN + +[CRD019B] +ALAN DAVIDSON + +[CRED020] +ANIMATORE CAPO + +[CRED021] +ALEX HORTON + +[CRD022A] +ANIMATORI + +[CRED022] +LEE MONTGOMERY + +[CRD022B] +DUNCAN SHIELDS + +[CRD022C] +GUS BRAID + +[CRED023] +PROGETTAZIONE VEICOLI + +[CRD023A] +JOLYON ORME + +[CRD023B] +ALAN DUNCAN + +[CRD024A] +CAPO PROGETTAZIONE VEICOLI + +[CRED024] +PAUL KUROWSKI + +[CRED025] +PROGETTAZIONE MAPPA + +[CRED026] +ADAM COCHRANE + +[CRED027] +NIK TAYLOR + +[CRED028] +GARY MCADAM + +[CRED029] +KEIRAN BAILLIE + +[CRED030] +ALISDAIR WOOD + +[CRED031] +ANDREW SOOSAY + +[CRD031A] +STEVEN MULHOLLAND + +[CRD031B] +WAYLAND STANDING + +[CRD031C] +CAMPBELL J. DICK + +[CRD031D] +PROGETTAZIONE GRAFICA + +[CRD031E] +STUART PETRI + +[CRED032] +PROGRAMMATORE CAPO + +[CRD032A] +PROGRAMMATORI + +[CRED033] +ALEXANDER ROGER + +[CRED034] +GRAEME WILLIAMSON + +[CRED035] +BARANE CHAN + +[CRED036] +DEREK PAYNE + +[CRED037] +GORDON YEOMAN + +[CRD037A] +ALAN CAMPBELL + +[CRD037B] +MARK HANLON + +[CRD037C] +ANDRZEJ MADAJCZYK + +[CRED038] +CAPO PRODUZIONE MUSICA + +[CRED039] +CRAIG CONNER + +[CRED040] +STUART ROSS + +[CRED041] +CAPO INGEGNERE AUDIO + +[CRED042] +ALLAN WALKER + +[CRD041A] +INGEGNERE AUDIO + +[CRD041B] +AUDIO + +[CRD042A] +WILL MORTON + +[CRED043] +PROGRAMMATORE AUDIO + +[CRED044] +RAYMOND USHER + +[CRED045] +RESPONSABILE TESTING + +[CRED046] +CRAIG ARBUTHNOTT + +[CRED047] +CAPO CQ + +[CRED048] +NEIL CORBETT + +[CRED049] +KEVIN WONG + +[CRED050] +CQ + +[CRED051] +DAVID BEDDOES + +[CRED052] +DAVID WATSON + +[CRED053] +BARRY CLARK + +[CRED054] +ROSS SPARROW + +[CRED055] +JAMES ALLAN + +[CRED056] +NEIL MEIKLE + +[CRD056A] +GEORGE WILLIAMSON + +[CRD056B] +MATT JONES + +[CRD056C] +ROB HARBOUR + +[CRD056D] +TOM WHITTAKER + +[CRED057] +CAPO SUPPORTO TECNICO + +[CRED058] +LORRAINE ROY + +[CRD057A] +SUPPORTO TECNICO + +[CRED059] +CHRISTINE CHALMERS + +[CRD060A] +SUPPORTO UFFICIO + +[CRD060B] +KIM GURNEY + +[CRD060C] +CASSIE OLIVER + +[CRED060] +ROCKSTAR NEW YORK + +[CRED061] +PRODUTTORE ESECUTIVO + +[CRED062] +SAM HOUSER + +[CRED063] +PRODUTTORE + +[CRED064] +DAN HOUSER + +[CRED065] +VP DI SVILUPPO + +[CRED066] +JAMIE KING + +[CRED067] +CAPO RESPONSABILE TECNOLOGICO + +[CRED068] +GARY J. FOREMAN + +[CRED069] +PRODUTTORE ASSOCIATO + +[CRED070] +JEREMY POPE + +[CRD071A] +DIRETTORE DEL CQ + +[CRD072A] +JEFF ROSA + +[CRED071] +SUPERVISORE MUSICA + +[CRED072] +TERRY DONOVAN + +[CRED073] +SQUADRA DI PRODUZIONE + +[CRED074] +TERRY DONOVAN + +[CRED075] +JENNIFER KOLBE + +[CRED076] +JENEFER GROSS + +[CRED077] +LAURA PATERSON + +[CRED078] +JEFF CASTANEDA + +[CRED079] +JERONIMO BARRERA + +[CRED080] +CARLY SLATER + +[CRED081] +JUNG KWAK + +[CRED082] +BRIAN WOOD + +[CRED083] +RENAUD SEBANNE + +[CRED084] +RICHARD KRUGER + +[CRD084A] +DANIEL EINZIG + +[CRD084B] +JACEN BURROWS + +[CRD084C] +LINN PR + +[CRD084D] +GRAFICA DI COPERTINA + +[CRD084E] +STEPHEN BLISS + +[CRED085] +KENT PAUL'S 80 NOSTALGIA ZONE + +[CRED086] +COMPOSTA DA DAN HOUSER + +[CRD086A] +PRODOTTA DA ADAM TEDMAN + +[CRED087] +WWW.KENTPAUL.COM E WWW.VICECITY.COM + +[CRED088] +CREATO DA + +[CRD088A] +ADAM TEDMAN + +[CRD088B] +DAVID YU + +[CRD088C] +JERRY LUNA + +[CRD088D] +STUART PETRI + +[CRD088E] +MICHAEL CARNEVALE + +[CRD088F] +GREG LAU + +[CRD088G] +FUTABA HAYASHI + +[CRED089] +RESPONSABILE CQ + +[CRED090] +CRAIG ARBUTHNOTT + +[CRED091] +CAPO ANALISTA + +[CRED092] +ADAM DAVIDSON + +[CRD092A] +JOE HOWELL + +[CRD092B] +MARC FERNANDEZ + +[CRED093] +ANALISTA DI GIOCO + +[CRED094] +RICHARD HUIE + +[CRED095] +SQUADRA TESTING ROCKSTAR + +[CRED096] +LANCE WILLIAMS + +[CRED097] +JOE GREENE + +[CRED098] +BRIAN PLANER + +[CRD098A] +ELIZABETH SATTERWHITE + +[CRD098B] +JAMEEL VEGA + +[CRD098C] +MIKE HONG + +[CRED099] +LEE CUMMINGS + +[CRED100] +SCENEGGIATURA + +[CRED101] +JAMES WORRALL + +[CRED102] +DAN HOUSER + +[CRED103] +ADAM TEDMAN + +[CRED104] +PAUL YEATES + +[CRED105] +JENEFER GROSS + +[CRED106] +LAURA PATERSON + +[CRED107] +SEQUENZE ANIMATE + +[CRED108] +SCRITTE DA DAN HOUSER E JAMES WORRALL + +[CRED109] +DIREZIONE AUDIO DI DAN HOUSER E NAVID KHONSARI + +[CRED110] +PRODOTTE DA JAMIE KING + +[CRD110A] +RICERCA TALENTI: JAMIE KING, SEAN MACALUSO + +[CRED111] +CAST + +[CRD111A] +PERSONAGGI PRINCIPALI + +[CRED112] +TOMMY VERCETTI - RAY LIOTTA + +[CRED113] +KEN ROSENBERG - WILLIAN FICHTNER + +[CRED114] +SONNY FORELLI - TOM SIZEMORE + +[CRED115] +STEVE SCOTT - DENNIS HOPPER + +[CRED116] +AVERY CARRINGTON - BURT REYNOLDS + +[CRED117] +RICARDO DIAZ - LUIS GUZMAN + +[CRED118] +LANCE VANCE - PHILIP MICHAEL THOMAS + +[CRED119] +COLONNELLO JUAN CORTEZ - ROBERT DAVI + +[CRED120] +UMBERTO ROBINA - DANNY TREJO + +[CRED121] +PHIL CASSIDY - GARY BUSEY + +[CRED122] +MITCH BAKER - LEE MAJORS + +[CRED123] +MERCEDES CORTEZ - FAIRUZA BALK + +[CRED124] +KENT PAUL - DANNY DYER + +[CRED125] +JEZZ TORRENT - KEVIN MCKIDD + +[CRED126] +CENTRALINISTA TAXI - DEBORAH HARRY + +[CRED127] +CANDY SUXX - JENNA JAMESON + +[CRED128] +BJ SMITH - LAWRENCE TAYLOR + +[CRED129] +AUNTIE POULET - YOUREE CLEOMILI HARRIS + +[CRED130] +SPACCIATORE - ARMANDO RIESCO + +[CRED131] +COUGAR - BLAYNE PERRY + +[CRED132] +HILARY - CHARLES TUCKER + +[CRED133] +DEPUTATO ALEX SHRUB - CHRIS LUCAS + +[CRED134] +VECCHIO KELLY - GEORGE DICENZO + +[CRD134A] +CAM JONES - GREG SIMS + +[CRD134B] +PSICOPATICO - HUNTER PLATIN + +[CRD134C] +MAUDE, LA SIGNORA DEI GELATI - JANE GENNARO + +[CRD134D] +JETHRO - JOHN ZURHELLEN + +[CRD134E] +GONZALES - JORGE PUPO + +[CRD134F] +DWAYNE - NAVID KHONSARI + +[CRD134G] +DICK - PETER MCKAY + +[CRD134H] +MIKE & PORNOSTAR - ROBERT CIHRA + +[CRD134I] +PERCY - RUSSELL FOREMAN + +[CRED135] +MOTION CAPTURE + +[CRED136] +ANIMATO DA + +[CRD136A] +DIREZIONE TECNICA DI ALEX HORTON + +[CRED137] +DIRETTO DA + +[CRD137A] +DIRETTO DA NAVID KHONSARI + +[CRED138] +PRODOTTO DA + +[CRD138A] +PRODOTTO DA JAMIE KING + +[CRD138B] +RENAUD SEBBANE + +[CRED139] +REGISTRATO PRESSO PERSPECTIVE STUDIOS, BROOKLYN + +[CRED140] +ATTORI MOTION CAPTURE + +[CRD140A] +BLAYNE PERRY + +[CRD140B] +JONATHON SALE + +[CRD140C] +CHARLES TUCKER + +[CRD140D] +EDDIE MARRERO + +[CRD140E] +WILLIAM MCCALL + +[CRD140F] +JORGE PUPO + +[CRD140G] +ROBERT JACKSON + +[CRD140H] +TARA RADCLIFFE + +[CRD140I] +JENIFER GAMBETESE + +[CRD140J] +KRIS ACHEVARRIA + +[CRD140K] +ALI ORDOUBADI + +[CRD140L] +KAHLEEM POOLE + +[CRED141] +DIALOGHI PEDONI + +[CRD141A] +SCRITTI DA DAN HOUSER, MARC FERNANDEZ, GILLIAN TELLING E NAVID KHONSARI + +[CRD141B] +CON L'AIUTO DI JEREMY POPE, LANCE WILLIAMS E JENNY JEMISON + +[CRED142] +SCRITTI DA + +[CRD142A] +DAN HOUSER E JAMES WORRALL + +[CRED143] +DIRETTI DA DAN HOUSER, CRAIG CONNER, MARC FERNANDEZ E ALLAN WALKER + +[CRED144] +PRODOTTI DA RENAUD SEBANNE + +[CRED145] +PEDONI + +[CRED146] +ADAM DAVIDSON, ADAM WATKINS, ALEJANDRO K. BROWN, ALEX ANTHONY SIOUKAS, ALEX GARCIA, + +[CRED147] +ALICE SALTZMAN, ALISON CIHRA, AMY SALIMA, AMY SALZMAN, ANDREA VIDELA, ANTHONY ATTI, + +[CRED148] +ANTHONY RIVERA, BIJAN SHAMS, BLAYNE PERRY, BRETT BISOGNO, BREYE MATA, BRIAN PANEN, + +[CRED149] +BROCK VODER, CAREY BERTINI, CHARISSE LAMBERT, CHRIS DIFAT, CHRIS REISENBERGER, + +[CRED150] +CHRISTOPHER BRODAY, CHRISTOPHER CARRO, CYNTHIA GREENE, DAMARIES LOPEZ, DAN LEE, + +[CRED151] +DAN SCHNEIDER, DAN TOYAMA, DAVID DEAN CHALTFIELD, JR., DAVID HARRISON, DAVID WILEY, + +[CRED152] +DEBORAH COLLINS, DEBRANDA CHANEY-GILES, DEMETRA KOUKOULAS, DENISE ROSADO, + +[CRED153] +DEVIN BENNETT, DEVIN WINTERBOTTOM, DORIS WOO, DOUGLAS HARRISON, DUNCAN COUTTS, + +[CRED154] +DUPE AJAYI, EDWIN AVELLANEDA, ELIZABETH HOWELL, ELIZABETH SATTERWHITE, ERIC NAGLE, + +[CRED155] +ESTEBAN KARPLUS, F. FONT, FUTABA HAYASHI, GENE HILGREEN, GERALD COSGROVE, + +[CRED156] +GERARD LUNA, GILLIAN TELLING, GREGG CARLUCCI, GREGORY CLERVOIX, GREGORY SCHWEIZER, + +[CRED157] +HADLEY TOMICKI, J. ROSSETT, JAMEEL VEGA, JASON JONES, JEFF ROSA, JENNIFER JEMISON, + +[CRED158] +JEREMY TAGGERT, JESSICA RIDER, JOSEPH GREENE,JOSEPH HOWELL, KATE DUKICH, + +[CRED159] +KEL O'NEILL, KEVIN HOPKINS, LADAWN JAMES, LANCE WILLIAMS, LAURA BUBBLES, + +[CRED160] +LAURA PATTERSON, LEE CUMMINGS, LETICIA L. YOUNG, LINDSAY KENNEDY, LISA ORITZ, + +[CRED161] +LORNA JORDAN, LUCIO AMADIO, MARCO FERNANDEZ, MARIKO TANAKA, MARLON MATTHEWS, + +[CRED162] +MARY TELLING, MASAYOSHI MITSUYAMA, MATTHEW CHUNG, MAX ALLSTADT, MAX BOGDANOV, + +[CRED163] +MELISSA ALVAREZ, MICHAEL MAY, MICHAEL ROTHSTEIN, MIGUEL VIDAL, MIKE FEDERLINE, + +[CRED164] +NATALIE DESCALZO, N'GAI MEMBERS, NICOLAS MALLO, NOELLE SADLER, NORBERT MORIVAN, + +[CRED165] +OSWALD GREENE, JR., PETER MCKAY, PETER APPEL, PRESTON SAVARESE, RAFAEL GONZALES, + +[CRED166] +RANDY JOHNSON, REY CONCEPCION, RICHARD KROGER, ROB TIBBS, ROBERT JACKSON, + +[CRED167] +ROBERT SCHULER, ROSS A. MCINTYRE, RUSSELL FOREMAN, RUTH NUNEZ, SALVADORE SUAZO, + +[CRED168] +SAM WHITE, SANTOS GONZALES, SCOTT SMITH, SEYMOUR FRAILMAN, SPELMAN BRAUMAN, + +[CRED169] +STEPHANIE TELLING, STEVE KNEZEVICH, STEVE ROBERT, SUMIKO YASUDA, SUSAN LEWIS, + +[CRED170] +SYLVIA COLACIOS, TOMOKO MIYAZAKI, TRON, VERDEL HALE, YVES MONDESIR, ZENO LEINFELDER, + +[CRED171] +DAVID BEDDOES, CHRISTINE CHALMERS, BARRY CLARK, NEIL CORBETT, KIM GURNEY, NEIL MEIKLE, + +[CRED172] +CASSIE OLIVER, LORRAINE ROY, DAVID WATSON, KEVIN WONG, WILL MORTON + +[CRED175] +ADAM DAVIDSON + +[CRED176] +LANCE WILLIAMS + +[CRED177] +NEIL MCCAFFREY + +[CRED178] +LAURA PATERSON + +[CRED179] +REY CONCEPCION + +[CRED180] +CHARLES HEROLD + +[CRED181] +ANDREW GREENWALD + +[CRED182] +JAMES MIELKE + +[CRED183] +PETER SUCIU + +[CRED184] +ALEX ODULIO + +[CRED185] +DON NKRUMAH + +[CRED186] +KENDALL PITTMAN + +[CRED187] +SAL SUAZO + +[CRED188] +EREK MATEO + +[CRED189] +CHRIS DIFATE + +[CRED190] +LEILA MILTON + +[CRED191] +DARREN ZOLTOWSKI + +[CRED192] +VIRGINIA SMITH + +[CRED193] +KEVIN CASSIN + +[CRED194] +JASON SHIGEMORI + +[CRED195] +KELLY KINSELLA + +[CRED196] +MOLLIE STICKNEY + +[CRED197] +STANTON SARJEANT + +[CRED198] +LAURA WALSH + +[CRED199] +MARK GARONE + +[CRED200] +JOANNA SLY + +[CRED201] +ELIZABETH HOWELL + +[CRED202] +ANA HERCULES + +[CRED203] +SHIRLEY IRICK + +[CRED204] +KASHONA FIELDS + +[CRED205] +JOEL M. LILJE + +[CRED206] +JOHN DIBENEDETTO + +[CRED207] +NANCY GILES + +[CRED208] +RYAN CROY + +[CRED209] +JENNIFER KOLBE + +[CRED210] +LIAM BURKE + +[CRED211] +SIGRID PREISSL + +[CRED212] +ANITA FITZSIMONS + +[CRED213] +PHILIPPA RASELLI + +[CRED214] +WIL QUESNEL + +[CRED215] +FALKO BURKERT + +[CRED216] +SARA SEWELL + +[CRED217] +STAZIONI RADIO E MUSICA + +[CRED218] +CONSULENZA MUSICALE + +[CRD218A] +HEINZ HENN + +[CRD218B] +STUART ROSS + +[CRED219] +COORDINATORE TRACCE AUDIO + +[CRED220] +TERRY DONOVAN + +[CRED221] +PRODUTTORI ROCKSTAR GAMES + +[CRED222] +DAN HOUSER E LAZLOW + +[CRED223] +PRODUTTORE ROCKSTAR NORTH + +[CRED224] +CRAIG CONNER + +[CRED225] +ALLAN WALKER + +[CRED226] +LAZLOW + +[CRED227] +DJ BANTER E IMAGING + +[CRED228] +SCRITTO DA DAN HOUSER E LAZLOW + +[CRED229] +FLASH FM + +[CRD229A] +TONI-MARIA CHAMBERS + +[CRD229B] +VOCE E PRODUZIONE IMAGING - JEFF BERLIN + +[CRED230] +UN RINGRAZIAMENTO SPECIALE A + +[CRED231] +TOMMY MOTTOLA, + +[CRED232] +MICHELLE ANTHONY, + +[CRED233] +STEVE BARNETT, + +[CRED234] +CHUCK FLECKENSTEIN, + +[CRED235] +RITA LIBERATOR + +[CRED236] +MARTIN & CLAIRE LOGAN + +[CRED237] +SANDRA HUTTON + +[CRED238] +CHRISTINE DAVIDSON + +[CRED239] +ALAN, RED & BIGFOOT + +[CRED240] +LE T + +[CRED241] +COLIN DONALD + +[CRED242] +KERRY STALLWOOD + +[CRED243] +ALAN MCGREGOR + +[CRED244] +CHRIS MORTON + +[CRED245] +EMIL BUSSE + +[CRED246] +EMILY BAILLIE + +[CRED247] +KEVIN ARCHIBALD + +[CRED248] +MORAG KERR + +[CRED249] +CATH WALKER + +[CRED250] +ISO BAR + +[CRED251] +WATERLINE + +[CRED252] +NEWS CAFE + +[CRD251A] +THE POND + +[CRD252A] +PIVO + +[CRED253] +BUDGET VIDEO RENTALS + +[CRED254] +LORNA'S SCOOTER + +[CRED255] +GARETH MURFIN + +[CRED256] +GRAFICA ADDIZIONALE + +[CRED257] +TONY PORTER + +[CRED258] +CRAIG MOORE + +[CRED259] +SINCRONIZZAZIONE LABIALE SEQUENZE + +[CRED260] +COSGROVE HALL FILMS + +[CRED261] +PRODUTTORE - OWEN BALLHATCHET + +[CRED262] +ANIMATORE SENIOR- JON TURNER + +[CRED263] +ANIMATORI - RICHARD DRUMM + +[CRED264] +DAVE BROWN + +[CRED265] +MAIR THOMAS + +[CRED266] +PRASHANT PATEL + +[CRED267] +CONSULENTE TECNOLOGICO AUDIO + +[CRED268] +RIK EDE DI GAMESOUND LTD. + +[CRED269] +SUPPORTO INTEGRAZIONE DTS + +[CRED270] +TED LAVERTY DI DTS + +[CRED271] +CHRIS GREER DI DTS + +[CRED272] +JASON PAGE DI DTS + +[CRED273] +RICERCA E ANALISI + +[CRED274] +VROCK + +[CRED275] +DJ: LAZLOW COME SE STESSO + +[CRED276] +VOCE IMAGING - JOE KELLY + +[CRED277] +PRODUZIONE IMAGING - JONATHAN HANST + +[CRED278] +WAVE 103 + +[CRED279] +DJ: ADAM FIRST - JAMIE CANFIELD + +[CRED280] +VOCE IMAGING - JEN SWEENEY + +[CRED281] +PRODUZIONE IMAGING - JONATHAN HANST + +[CRED282] +FEVER 105 + +[CRED283] +DJ: OLIVER 'LADYKILLER' BISCUIT - JULIUS DYSON + +[CRED284] +VOCE IMAGING MASCHILE - ED MCMANN + +[CRED285] +VOCE IMAGING FEMMINILE - SHAWNEE SMITH + +[CRED286] +PRODUZIONE IMAGING - LISTEN KITCHEN + +[CRED287] +EMOTION 98.3 + +[CRED288] +DJ: FERNANDO - FRANK CHAVEZ + +[CRED289] +VOCE IMAGING - JEN SWEENEY + +[CRED290] +PRODUZIONE IMAGING - JONATHAN HANST + +[CRED291] +RADIO ESPANTOSO + +[CRED292] +DJ: PEPE - TONY CHILRODES + +[CRED293] +WILDSTYLE + +[CRED294] +DJ: MISTER MAGIC COME SE STESSO + +[CRED295] +VOCE IMAGING - FRANK SILVESTRO + +[CRED296] +PRODUZIONE IMAGING - LAZLOW + +[CRED297] +KCHAT + +[CRED298] +SCRITTA DA DAN HOUSER E LAZLOW + +[CRED299] +PRODOTTA E MIXATA DA LAZLOW + +[CRED300] +DJ AMY SHECKENHAUSEN - LEYNA WEBER + +[CRED301] +JEZ TORRENT - KEVIN MCKIDD + +[CRED302] +MANDY - COLLEEN CORBETT + +[CRED303] +MICHELLE CARAPADIS - MARY BIRDSONG + +[CRED304] +MR.ZOO - CARL DOWLING + +[CRED305] +GETHSEMANEE - LYNN LIPTON + +[CRED306] +CLAUDE MAGINOT - JOHN MAUCERI + +[CRED307] +BJ SMITH - LAWRENCE TAYLOR + +[CRED308] +THOR - FRANK FAVA + +[CRED309] +VOCI PUBBLICITÀ + +[CRED310] +COUZIN ED, JOSH CLARK, JASON BUHRMESTER, JUAN ALLER, WAYNE OLIVER, SUSAN LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, KEITH BROADAS + +[CRED311] +LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, + +[CRED312] +DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, + +[CRED313] +KEITH BROADAS + +[CRED314] +VCPR + +[CRED315] +SCRITTO DA DAN HOUSER E LAZLOW + +[CRED316] +PRODOTTO DA LAZLOW + +[CRED317] +MAURICE CHAVEZ - PHILLIP ANTHONY RODRIGUEZ + +[CRED318] +JONATHAN FREELOADER - PATRICK OLSEN + +[CRED319] +MICHELLE MONTANIUS - KELLY GUEST + +[CRED320] +DEP. ALEX SHRUB - CHRIS LUCAS + +[CRED321] +CALLUM CRAYSHAW - SEAN MODICA + +[CRED322] +JOHN F. HICKORY - LJ GANSEN + +[CRED323] +PASTOR RICHARDS - DAVID GREEN + +[CRED324] +JAN BROWN - MAUREEN SILLIMAN + +[CRED325] +BARRY STARK - RENAUD SEBBANE + +[CRED326] +JENNY LOUISE CRAB - MARY BIRDSONG + +[CRED327] +KONSTANTINOS SMITH - KONSTANTINOS.COM + +[CRED328] +JEREMY ROBARD - PETER SILVESTRO + +[CRED329] +PUBBLICITÀ RADIO + +[CRED330] +SCRITTE DA DAN HOUSER E LAZLOW + +[CRED331] +PRODOTTE DA LAZLOW + +[CRED332] +JINGLE AGGIUNTIVI PRODOTTI DA CRAIG CONNER + +[CRED333] +VOCI PUBBLICITÀ + +[CRED334] +ADAM DAVIDSON, ALEX ANTHONY, ALICE SALTZMAN, AMY SALZMAN, KATE DUKICH, + +[CRED335] +ARAN RONICLE, BARB JONES, BEN KRECH, BRIAN THOMAS, BROCK YODER, CHRIS + +[CRED336] +FERRANTE, CRAIG CONNER, DAVE RYAN, DAVID GREEN, DORIS WOO, DOUGLAS + +[CRED337] +HARRISON, ED MCMANN, FRANK CHAVEZ, FRANK FAVA, GENE HILGREEN, GREG + +[CRED338] +SCHWEIZER, HUNTER PLATIN, JAMES FERRANTE, JEFF BERLIN, JEFF ROSA, JOE KELLY, + +[CRED339] +JOHN MAUCERI, JOSH CLARK, JULIE WEMYSS, KEVIN STRALEY, KIM GURNEY, LANCE + +[CRED340] +WILLIAMS, LAURA PATERSON, LAZLOW, LISA ORTIZ, LORNA JORDAN, LUCIEN JONES, + +[CRED341] +MAUREEN SILLIMAN, MIKE FERRANTE JR., PETE GUSTIN, PETER SILVESTRO, RAFF + +[CRED342] +CROLLA, RANDY JOHNSON, RICHARD KRUGER, RON REEVE, SHELLEY MILLER, SKY, TJ ALLARD + +[CRD344A] +AUDIO REGISTRATO PRESSO DIGITAL ARTS STUDIOS, + +[CRED344] +NYC, TRACK 9 STUDIOS, NYC, + +[CRED345] +WEDDINGTON MULTIMEDIA, LOS ANGELES, + +[CRD345A] +SYNC SOUND, NYC E RADIO LAZLOW, LONG ISLAND. + +[CRED346] +UN RINGRAZIAMENTO A AXEL ERICSON E WON LEE DI DIGITAL ARTS, PAUL VASQUEZ DI TRACK 9 STUDIOS, JOHN BOWEN E JOHN HASSLER DI SYNC SOUND + +[CRED347] +MARK LLOYD + +[CRED348] +TIM BATES + +[CRED349] +KIT BROWN + +[CRED350] +ANDY MASON + +[CRED351] +PHIL DEANE + +[CRED352] +PHIL ALEXANDER + +[CRED353] +MATT HEWITT + +[CRED354] +DENBY GRACE + +[CRED355] +ANTOINE CABROL + +[CRED356] +JONOTHAN STONES + +[CRED357] +MIKE BLACKBURN + +[CRED358] +TIM MCGAFF + +[CINCAM] +Camera mobile + +[RC4] +'LA FURIA DI RUMPO' + +[LEGAL] +~g~Elimina la minaccia criminale! + +[GA_2] +Motore nuovo e carrozzeria riverniciata. Gli sbirri non ti riconosceranno! + +[HELP15] +Quando sei a piedi, premi il ~h~~k~~PED_LOOKBEHIND~~w~ per ~h~guardare indietro~w~. Usa la ~h~levetta analogica destra~w~ per ~h~guardarti attorno~w~. + +[FEC_LB4] +Guarda indietro (tasto R3) + +[PERPIC] +Pacchetti speciali recuperati + +[CO_ONE] +Pacchetto speciale ~1~ su ~1~ + +[GA_21] +Non puoi parcheggiare altri veicoli in questo garage. + +[CHEAT1] +Trucco attivato + +[CHEAT2] +Trucco armi + +[CHEAT3] +Trucco salute + +[CHEAT4] +Trucco armatura + +[CHEAT5] +Trucco livello di sospetto + +[CHEAT6] +Trucco soldi + +[CHEAT7] +Trucco tempo atmosferico + +[USJ_ALL] +TUTTE LE ACROBAZIE UNICHE COMPLETATE + +[JAN] +Gen + +[FEB] +Feb + +[MAR] +Mar + +[APR] +Apr + +[MAY] +Mag + +[JUN] +Giu + +[JUL] +Lug + +[AUG] +Ago + +[SEP] +Set + +[OCT] +Ott + +[NOV] +Nov + +[DEC] +Dic + +[DEFDT] +--:---:---- --:--:-- + +[BONUS] +~g~BONUS ~1~$ + +[HORN1] +Premi il ~h~~k~~VEHICLE_HORN~~w~ per attivare il ~h~clacson~w~. + +[HORN2] +Premi il ~h~~k~~VEHICLE_HORN~~w~ per attivare il ~h~clacson~w~. + +[HORN3] +Premi il ~h~~k~~VEHICLE_HORN~~w~ per attivare il ~h~clacson~w~. + +[FEC_EXV] +Esci dal veicolo + +[TAXI_M] +'TAXI DRIVER' + +[COP_M] +'VIGILANTE' + +[FIRE_M] +'POMPIERE' + +[AMBUL_M] +'INFERMIERE' + +[HJ_IS] +BONUS ACROBAZIA FOLLE: ~1~$ + +[HJ_PIS] +BONUS ACROBAZIA FOLLE PERFETTA: ~1~$ + +[HJ_DIS] +BONUS DOPPIA ACROBAZIA FOLLE: ~1~$ + +[HJ_PDIS] +BONUS DOPPIA ACROBAZIA FOLLE PERFETTA: ~1~$ + +[HJ_TIS] +BONUS TRIPLA ACROBAZIA FOLLE: ~1~$ + +[HJ_PTIS] +BONUS TRIPLA ACROBAZIA FOLLE PERFETTA: ~1~$ + +[HJ_QIS] +BONUS QUADRUPLA ACROBAZIA FOLLE: ~1~$ + +[HJ_PQIS] +BONUS QUADRUPLA ACROBAZIA FOLLE PERFETTA: ~1~$ + +[FESZ_LS] +Caricamento completato. + +[HELI_1A] +Prova le tue abilità con lo Sparrow: scopri quanto velocemente riesci a completare il percorso. + +[HELI_1B] +Percorso completato! + +[HELIODD] +Strani incarichi con l'elicottero + +[INT2_M] +Hanno una fattoria in Panama. + +[INT2_N] +OK, va bene, ascoltate: + +[INT2_O] +quando arriviamo volete che resti in macchina + +[INT2_P] +o preferite che venga con voi altri? + +[INT2_Q] +No, resta in macchina. + +[INT2_R] +Lo sapete, ci ho pensato su. + +[INT2_S] +Penso proprio che resterò in macchina. + +[INT4_A] +Fottuti! Siamo fottuti! + +[INT4_B] +È sempre la stessa storia, + +[INT4_C] +metto fuori la testa per un maledetto secondo, + +[INT4_D] +e il fato mi tira palate di merda in faccia! + +[INT4_E] +Bene, vaffanculo! + +[INT4_F] +Smettila di fare casino e di lamentarti. Siamo vivi, non è vero? + +[INT4_G] +Fammi scendere qua. + +[INT4_H] +Fai scomparire la macchina e fatti una bella dormita. + +[INT4_I] +Passerò domani a trovarti in ufficio e troveremo un modo per risolvere questo casino. + +[INT4_J] +OK, mi sembra un'ottima idea, andrò a dormire. + +[INT4_K] +Che cosa intendi fare? + +[INT4_L] +Tornare nella mia stanza, + +[INT4_M] +schiarirmi le idee e risolvere questo casino. + +[INT4_N] +OK. + +[LAW] +MISSIONI DELL'AVVOCATO + +[LAW1_1] +~g~Vai a prendere degli abiti nuovi dal negozio di vestiti Rafael's. + +[LAW4_6] +Brucia l'amministrazione! + +[LAW4_7] +Uccidi i responsabili! + +[LAW4_8] +Combattere, combattere, combattere, combattere,. + +[LAW4_9] +Più ferie, meno lavoro! + +[LAW4_11] +Combattere, combattere, combattere, combattere,. + +[LAW4_12] +Viva la rivoluzione. + +[GENERAL] +MISSIONI DEL COLONNELLO + +[GEN3_4] +Tommy Vercetti. Andiamo... + +[GEN3_13] +Che cosa diavolo ti prende? Vai sul tetto dall'altra parte del cortile prima che arrivino! + +[GEN3_17] +Meeerda! Stai cercando di ammazzarmi? + +[GEN3_21] +~g~Ha preso i soldi di Diaz! Inseguilo e riprendili! + +[GEN3_24] +~r~Diaz è morto! Non sei riuscito a proteggerlo! + +[GEN3_26] +~r~Hai ucciso Diaz! + +[GEN3_27] +~r~Hai ucciso una guardia di Diaz! + +[GEN3_31] +~g~Vai all'appuntamento e proteggi Diaz. + +[GEN3_32] +~g~Raggiungi il tetto nell'edificio di fronte a Lance e appostati. + +[COKE] +MISSIONI DEL BARONE DELLA COCA + +[COK1_3] +Spero tu cada e ti rompa l'osso del collo! + +[COK1_6] +Sono stanco di tutto questo. + +[COK2_7] +Vedi quei segnali, ragazzo? Cerca di colpire le luci! + +[COK2_10] +Di certo sei più bravo a sparare che a parlare. + +[COK2_11] +Grazie. Anche tu hai una bella parlantina. + +[COK2_12] +Lo so, Tommy. + +[COK2_18] +Ti diverti con Kenny Loggins. + +[COK2_19] +Cielo, adoro questo album! + +[COK2_26] +~r~Hai ucciso Lance! + +[COK3_1] +Non sparare, amico! + +[COK3_2] +Che cosa sta succedendo? + +[COK3_3] +Sta prendendo la barca. Farabutto! + +[COK3_4] +Aiuto! Un idiota sta rubando la barca! + +[COK4_W] +Uugghh! Questo è l'ultimo. + +[COK4_X] +Vado ad accendere... + +[COK4_Y] +A quanto sembra abbiamo dei nuovi amici. + +[COK4_2] +Sì. + +[COK4_6] +Sai dove stiamo andando? + +[COK4_7] +Ci siamo persi? + +[COK4_8] +Abbiamo un po' di competizione! + +[COK4_9] +Falli fuori! + +[COK4_9A] +È giunta l'ora di un po' di Lance Vance Dance! + +[COK4_10] +Sono pronti per il macero! E per sfamare i pesci. + +[COK4_11] +Ce l'abbiamo fatta! Le altre imbarcazioni non sono della nostra classe. + +[COK4_17] +Stanno iniziando a pregare! + +[COK4_18] +I miei piedi sono bagnati! STIAMO IMBARCANDO ACQUA! + +[COK4_21] +Ponte in arrivo! + +[COK4_22] +Lanciati, sta per esplodere! + +[COK4_23] +Bel colpo! + +[COK4_29] +~r~Hai ucciso Lance! + +[ASS1_6] +Vai Tommy, andrà tutto bene! + +[ASS1_7] +Beccatevi questo, maledetti assassini! + +[ASS1_8] +Sono bloccato! + +[ASS1_9] +Ti copro io Tommy! + +[ASS1_10] +Ehi, questo sì che è un bel confine alberato. + +[ASS1_11] +Ehi Tommy, posso avere la stanza con la vista sulla baia? + +[ASS1_12] +Che bel soffitto alto che c'è qui... + +[ASS1_3] +Lance! Ho bisogno di copertura! + +[ASS1_5] +Lance! + +[ASS1_15] +~g~Distruggi la dimora e uccidi Diaz! + +[ASS1_17] +~g~Sono disponibili più strade all'interno della villa. + +[TAXWAR] +MISSIONI GUERRA DEI TAXI + +[NOTAXI] +~g~Hai bisogno di un taxi Kaufman per attivare questa missione. + +[TAXW1_5] +~g~Devi avere un taxi Kaufman! + +[TAX2_4] +Forza, Tommy. + +[TAX2_5] +Riempilo di botte! + +[TAX2_6] +Non ha neppure il permesso. + +[TAX2_7] +Maledetti servizi di limousine. + +[TAXW3_1] +~g~Vai a prendere Mercedes. + +[RACE1] +~g~3..2..1.. VIA VIA VIA! + +[RACE2] +~g~3 + +[RACE3] +~g~2 + +[RACE4] +~g~1 + +[RACE5] +~g~VIA! + +[FIRST] +~b~PRIMO + +[SECOND] +~b~SECONDO + +[THIRD] +~b~TERZO + +[FOURTH] +~b~QUARTO + +[RACETM] +~b~TEMPO DI GARA: ~1~:~1~ + +[RACETM2] +~b~TEMPO DI GARA: ~1~:0~1~ + +[RACEFA] +~r~Non sei riuscito a vincere la gara! + +[TEX1_5] +~r~È riuscito a fuggire! + +[SEG3_1] +TEMPO: + +[SEG3_2] +~g~Raggiungi il van che contiene il Raider radiocomandato e le bombe a tempo. + +[SEG3_3] +~g~Devi utilizzare il RAIDER RADIOCOMANDATO per trasportare le 4 bombe nelle 4 aree bersaglio all'interno del cantiere. + +[SERG3_5] +~g~Puoi trasportare solo una bomba alla volta e non puoi raccogliere dell'esplosivo correttamente posizionato. + +[SEG3_7] +~g~Una volta posizionata la PRIMA bomba con successo su di un bersaglio, partirà il conto alla rovescia, dovrai piazzare tutti gli esplosivi entro il tempo limite. + +[SEG3_8] +~g~Le 4 bombe devono essere tutte posizionate su ognuno dei bersagli per passare la missione e demolire l'edificio. + +[SEG3_9] +~g~Bomba piazzata! Ancora 3. + +[SEG3_10] +~g~Bomba piazzata! Ancora 2. + +[SEG3_11] +~g~Bomba piazzata! Ne manca una sola. + +[SEG3_12] +~g~Bomba NON piazzata! Recupera l'esplosivo! + +[SEG3_13] +~g~Piazza la bomba nella zona bersaglio. + +[SEG3_14] +~r~Tempo esaurito: hai fallito nel demolire l'edificio. + +[SEG3_15] +~r~Il Raider radiocomandato è stato distrutto! Come farai adesso a trasportare le bombe? + +[AVERY] +MISSIONI DI AVERY + +[ASM] +MISSIONI DA SICARIO + +[ASM_1] +MISSIONE DA SICARIO 1 + +[ASM1_1] +~g~Mr. Teal, il tuo aiuto nello sradicare gli stranieri è stato inestimabile per il nostro lavoro. Ho un altro lavoro con un contatto più 'diretto'. I dettagli sono stati attaccati sotto al telefono. + +[ASM1_2] +~g~Raggiungi il telefono pubblico presso il Mall in Washington Beach. + +[ASM1_3] +~g~Carl Pearson, fattorino di una pizzeria. Non deve completare le sue consegne. + +[ASM1_4] +~g~Uccidi il fattorino prima che possa completare le sue consegne. + +[ASM_2] +MISSIONE DA SICARIO 2 + +[ASM_3] +MISSIONE DA SICARIO 3 + +[ASM3_A] +Marcus Hammond, Franco Carter, Dick Tanner, Nick Kong e Stuntman Driver fanno tutti parte di un'associazione europea che si sta preparando a commettere una rapina. + +[ASM3_B] +Si trovano tutti nella loro posizione pronti all'azione. Voglio che vengano tutti eliminati prima che inizi il colpo. Ho disposto delle utili armi nei paraggi. + +[ASM3_1] +~g~Recupera le armi che Mr. Black ha lasciato per te. + +[ASM3_2] +~g~Non avvicinarti troppo, se no il bersaglio potrebbe vederti. + +[ASM3_3] +~g~Per un'eliminazione più rapida, colpisci da una locazione vicino alle loro postazioni ed evita di farti vedere. + +[ASM3_4] +~g~Ti ha visto! Fallo fuori il prima possibile! + +[ASM3_5] +~g~Marcus Hammond si trova presso dei cartelli pubblicitari in Washington. + +[ASM3_6] +~g~Franco Carter si trova presso la DBP Security vicino a Ocean Drive. + +[ASM3_7] +~g~Dick Tanner si trova presso la gioielleria in Vice Point. + +[ASM3_8] +~g~Nick Kong si trova presso Washington Beach. + +[ASM3_9] +~g~Stuntman Driver si trova presso Washington Beach. + +[ASM3_10] +~r~Non sei riuscito a eliminarli tutti quanti. + +[ASM_4] +MISSIONE DA SICARIO 4 + +[ASM4_1] +~g~Recupera il fucile che ti abbiamo lasciato tra le foglie presso il terminal dell'aeroporto. + +[ASM4_2] +~g~Non mancare il bersaglio o allarmerai le sue guardie del corpo: ricorda di tenere una certa distanza per evitare di essere visto. + +[ASM4_3] +~g~Osserva la donna sul balcone sopra ai banconi del check-in dentro il terminal. NON UCCIDERLA. + +[ASM4_4] +~g~Elimina l'uomo a cui passa la valigetta ma solo DOPO CHE L'AVRÀ RACCOLTA. Poi recupera la valigetta e portala ad Ammu-Nation in Downtown. + +[ASM4_5] +~g~Recupera la valigetta! + +[ASM4_6] +~g~Porta la valigetta ad Ammu-Nation in Downtown + +[ASM4_7] +~r~Hai ucciso la donna, idiota! + +[ASM4_8] +~r~Il bersaglio ha sentito il colpo d'arma da fuoco! L'accordo è saltato! + +[ASM4_9] +~r~Il bersaglio è salito a bordo del volo! + +[ASM4_10] +~g~Sembra tu non sia l'unico interessato alla valigetta! Portala in fretta ad Ammu-Nation! + +[ASM4_11] +~r~Il bersaglio ti ha visto! L'accordo è saltato! + +[ASM4_13] +~g~Ti ha visto e sta scappando! Uccidilo e recupera la valigetta! + +[ASM4_14] +~g~L'indicatore della distanza nella parte superiore destra dello schermo ti tiene informato sulla distanza dal bersaglio. Se si riempirà, verrai avvistato. + +[ASM_5] +MISSIONE DA SICARIO 5 + +[KICK] +KICKSTART + +[KICK1_3] +~g~Numero di volte che hai appoggiato i piedi: ~1~ + +[KICK1_4] +~g~Penalità: ~1~ secondi + +[BANK] +MISSIONI RAPINA + +[BANK1] +MISSIONE RAPINA 1 + +[BANK2] +MISSIONE RAPINA 2 + +[BJM2_3] +PERCENTUALE DI CENTRI: ~1~% + +[BJM2_15] +PUNTEGGIO: + +[BJM2_18] +PUNTEGGIO DA BATTERE: + +[BJM2_19] +~g~Colpisci il maggior numero di bersagli entro il tempo limite! + +[BJM2_21] +~g~Colpisci il maggior numero di bersagli finché hai ancora colpi. + +[BANK3] +MISSIONE RAPINA 3 + +[BJM3_1] +~g~Trova una macchina da corsa e posizionati sulla griglia di partenza. + +[BNK4_2A] +I ragazzi dell'officina hanno fatto un ottimo lavoro su questa piccola. + +[BNK4_3G] +Ommerda, abbiamo la polizia alle calcagna! + +[BNK4_3H] +...e non siamo ancora neanche arrivati. + +[BNK4_3K] +Dobbiamo seminare prima la polizia... + +[BNK4_3L] +Cristo Tommy, stai cercando di farci fuori? + +[BNK4_3N] +Tutto ciò che amo finisce a pezzi! + +[BNK422A] +Cam, quanto tempo? + +[BK4_23A] +Mi bastano 3 minuti! + +[BNK4_26] +Maledizione! Ecco che arrivano! + +[BNK4_32] +Usa l'esplosivo per aprire le cassette di sicurezza! + +[BNK4_43] +Vi copro io il culo, PARTI! + +[BNK4_51] +A me sembri a posto. + +[KENT] +MISSIONI DI KENT PAUL + +[KENT1] +MISSIONE DI KENT PAUL 1 + +[COUNT] +MISSIONI DI FALSIFICAZIONE + +[COUNT1] +MISSIONE DI FALSIFICAZIONE 1 + +[COUNT2] +MISSIONE DI FALSIFICAZIONE 2 + +[BIKE] +MISSIONI DEI MOTOCICLISTI + +[BIKE1] +MISSIONE DEI MOTOCICLISTI 1 + +[BIKE2] +MISSIONE DEI MOTOCICLISTI 2 + +[BIKE3] +MISSIONE DEI MOTOCICLISTI 3 + +[GOAWAY1] +Ritorna quando avrai completato le missioni per la gang di Haiti. + +[HAIT] +MISSIONI DEGLI HAITIANI + +[HAIT1] +MISSIONE DEGLI HAITIANI 1 + +[HAIT2] +MISSIONE DEGLI HAITIANI 2 + +[HAIT3] +MISSIONE DEGLI HAITIANI 3 + +[HAM3_6] +~g~Utilizza il fucile di precisione che ti ho lasciato per completare la missione. + +[ROCK] +MISSIONI DELLA BAND + +[ROK1_4] +~g~OK, credo tu stessi cercando questo... + +[ROK1_1E] +~g~Ti costerà di più di quanto hai! + +[ROK1_1F] +~g~Ritorna quando avrai i soldi. + +[RBM2_6] +~g~Wow! È un uomo, bloccalo! + +[ROCK3] +MISSIONE DELLA BAND 3 + +[ROK3_6D] +~r~insieme alle vostre GROSSE TESTE DA CAPELLONI! + +[ROK3_40] +Vicino al fottuto minibar? + +[RBM3_5] +~g~Porta i Love Fist allo spettacolo. + +[CUBANM] +MISSIONI DEI CUBANI + +[CUBAN1] +MISSIONE DEI CUBANI 1 + +[CUBAN2] +MISSIONE DEI CUBANI 2 + +[CUB2_10] +~r~Dovresti uccidere gli Haitiani, non i Cubani. + +[CUBAN3] +MISSIONE DEI CUBANI 3 + +[CUBAN4] +MISSIONE DEI CUBANI 4 + +[CUB4_05] +~r~Ti avevo detto di restare in macchina! Adesso non riusciremo mai a entrare! + +[CUB4_25] +OK, andiamo. + +[PROT] +MISSIONI DI PROTEZIONE + +[PRO1_H] +Perché non ti calmi per un secondo, sto finalmente cominciando a capire come funzionano le cose da queste parti. + +[PRO1_02] +~g~Esci dal Mall. + +[PRO3_06] +~g~Fai perdere le tracce. + +[PORN] +MISSIONI PORNOGRAFIA + +[PORN1] +MISSIONE PORNOGRAFIA 1 + +[POR1_03] +~r~Candy è morta! + +[PORN2] +MISSIONE PORNOGRAFIA 2 + +[PORN3] +MISSIONE PORNOGRAFIA 3 + +[POR3_18] +Sei stato visto! + +[PORN4] +MISSIONE PORNOGRAFIA 4 + +[POR4_04] +~g~Gli uffici sono dall'altra parte di questo cancello. + +[PHIL] +MISSIONI DI PHIL + +[PHIL1] +MISSIONE DI PHIL 1 + +[PHI1_06] +Ma che combini, come diavolo guidi? + +[PHI1_07] +Ehi! + +[PHIL2] +MISSIONE DI PHIL 2 + +[PIZ1_A] +MISSIONE RECAPITO PIZZE + +[PIZ1_03] +~g~Ritorna alla pizzeria e prendi le nuove ordinazioni. + +[PIZ1_04] +~g~Ecco le nuove ordinazioni. + +[PIZ1_10] +Premi il ~h~tasto R3~w~ per annullare la missione. + +[CNTBUY1] +Tipografia acquistata: ~1~$ + +[CARBUY] +Concessionario acquistato: ~1~$ + +[PORNBUY] +Studio cinematografico acquistato: ~1~$ + +[ICEBUY] +Fabbrica di gelato acquistata: ~1~$ + +[TAXIBUY] +Compagnia di taxi acquistata: ~1~$ + +[BANKBUY] +Malibu acquistato: ~1~$ + +[BOATBUY] +Cantiere navale acquistato: ~1~$ + +[PRNT_NO] +Non puoi ancora comprare la tipografia, prova più tardi. + +[CAR_NO] +Non puoi ancora comprare il concessionario, prova più tardi. + +[PORN_NO] +Non puoi ancora comprare lo studio cinematografico, prova più tardi. + +[ICE_NO] +Non puoi ancora comprare la fabbrica di gelato, prova più tardi. + +[TAXI_NO] +Non puoi ancora comprare la compagnia di taxi, prova più tardi. + +[BANK_NO] +Non puoi ancora comprare il Malibu, prova più tardi. + +[BOAT_NO] +Non puoi ancora comprare il cantiere navale, prova più tardi. + +[PRNT_R3] +Premi il tasto R3 per acquistare la tipografia per ~1~$ + +[CAR_R3] +Premi il tasto R3 per acquistare il concessionario per ~1~$ + +[PORN_R3] +Premi il tasto R3 per acquistare lo studio cinematografico per ~1~$ + +[ICE_R3] +Premi il tasto R3 per acquistare la fabbrica di gelato per ~1~$ + +[TAXI_R3] +Premi il tasto R3 per acquistare la compagnia di taxi per ~1~$ + +[BANK_R3] +Premi il tasto R3 per acquistare il Malibu per ~1~$ + +[BOAT_R3] +Premi il tasto R3 per acquistare il cantiere navale per ~1~$ + +[COL2_6] +Fermo, brutto maiale imperialista americano! + +[COL2_6B] +Questa è proprietà del governo francese. + +[COL2_6C] +È finita! + +[COL3_A] +Thomas, grazie per essere venuto. + +[COL3_B] +Scusami se giungo subito al punto. + +[COL3_C] +Diaz mi ha chiesto di sovrintendere una piccola transizione di lavoro. + +[COL3_D] +Speriamo vada meglio dell'ultima... + +[COL3_E] +Ed è per questo che ho pensato a te, amico. + +[COL3_F] +Ho lasciato un po' di supporto al parcheggio multipiano. + +[COL3_G] +Raccogli il materiale, poi raggiungi e sorveglia gli uomini di Diaz all'incontro. + +[COL4_2] +Non so signore! + +[COL4_5] +Signorsì, signore! + +[COL4_10] +Andiamo a prendere delle ciambelle. + +[COL4_16] +Signore! Sposto il veicolo, signore! + +[COL4_25] +Autodistruzione del veicolo attivata! + +[COL5_6] +Mercedes, quella ragazza sarà la mia rovina. + +[COL5_8] +Maledetti scarafaggi! + +[COL5_5] +Morite, maiali Francesi! + +[CNT2_1] +Uccidetelo + +[CNT2_2] +Recuperate le matrici! + +[CNT2_3] +Proteggete il corriere. + +[FINKILL] +OK ragazzi, uccidetelo! + +[FIN_6] +Sonny è interessato alla mia cassaforte e ai miei soldi... + +[FIN_10] +Sonny? SONNY! Sto venendo a prenderti! + +[RACES_4] +3 + +[RACES_5] +2 + +[RACES_6] +1 + +[RACES_7] +VIA! + +[RACES_9] +Tempo: ~1~:~1~ + +[RACES] +TEMPO: + +[RACES17] +Nuovo miglior tempo: ~1~:~1~ + +[RACES20] +Nuovo miglior tempo: ~1~:0~1~ + +[RACES21] +Tempo: ~1~:0~1~ + +[RCH1_1] +~g~Usa l'elicottero radiocomandato, il Raider, per PASSARE ATTRAVERSO i punti di controllo. + +[RCH1_2] +~g~I punti di controllo sono sparsi per tutto l'aeroporto. + +[RCH1_3] +~g~Hai ~c~8 MINUTI~g~ per attraversarli tutti e ~c~20~g~! + +[RCH1_5] +Tempo + +[RCRC1_1] +~g~Sfida nella GARA A TAPPE altri 3 Bandit radiocomandati su 2 GIRI. + +[RCRC1_2] +~g~Raggiungi subito la griglia di partenza! + +[RCRC1_3] +~g~Giro finale! + +[RCRC1_4] +~g~3 + +[RCRC1_5] +~g~2 + +[RCRC1_6] +~g~1 + +[RCRC1_7] +~g~VIA! + +[RCRC1_8] +~g~Tempo di gara: ~1~ secondi + +[RCPL1_1] +~g~Sfida nella GARA A TAPPE altri 3 Baron radiocomandati. + +[RCPL1_2] +~g~Devi attraversare la ~o~CORONA CENTRALE~g~ per passare con successo un punto di controllo. + +[RCPL1_3] +~g~Raggiungi subito la griglia di partenza! + +[FEA_2SP] +2 altoparlanti + +[FEA_4SP] +Più di 2 altoparlanti + +[FEA_EAR] +Cuffie + +[FEA_NAH] +NESSUNA PERIFERICA AUDIO + +[FET_APP] +APPLICA + +[FES_SKN] +NOME SKIN + +[FES_DAT] +DATA + +[FES_SET] +Usa skin + +[FET_DEF] +Ripristina predefiniti + +[FESZ_QZ] +Sei sicuro di voler salvare la partita? + +[FES_SCG] +Salvare la partita? + +[FES_LCG] +Vuoi caricare e continuare la partita? + +[FEC_FIR] +Fuoco + +[FEC_NWE] +Arma successiva + +[FEC_PWE] +Arma precedente + +[FEC_FOR] +Avanti + +[FEC_BAC] +Indietro + +[FEC_LEF] +Sinistra + +[FEC_RIG] +Destra + +[FEC_ZIN] +Ingrandisci + +[FEC_ZOT] +Riduci + +[FEC_EEX] +Entra o esci + +[FEC_RAD] +Radio + +[FEC_SUB] +Sottomissione + +[FEC_CMR] +Cambia visuale + +[FEC_JMP] +Salto + +[FEC_SPN] +Scatto + +[FEC_HND] +Freno a mano + +[FEC_LOL] +Sguardo a sinistra + +[FEC_LOR] +Sguardo a destra + +[FEC_NTR] +Bersaglio successivo + +[FEC_PTT] +Bersaglio precedente + +[FEC_LBA] +Sguardo indietro + +[FEC_CEN] +Centra visuale + +[FET_CCN] +Classico + +[FET_SCN] +Predefinito + +[FET_CFT] +A PIEDI + +[FET_CCR] +IN MACCHINA + +[FET_CAC] +AZIONE + +[FEC_IBT] +- + +[FEC_MXO] +MXB1 + +[FEC_MXT] +MXB2 + +[FEC_UNB] +NON ASSEGNATO + +[FEC_TFL] +Sguardo+Torretta a SX + +[FEC_TFR] +Sguardo+Torretta a DX + +[FEC_MWF] +VOLANTE SU + +[FEC_MWB] +VOLANTE GIÙ + +[FEC_ORR] +o + +[FEC_NUS] +NON USATO + +[FEC_LUD] +Sguardo su + +[FEC_LDU] +Sguardo giù + +[FEC_CMP] +COMBO: SGUARDO SX+DX + +[LAW_1A] +law_1a + +[LAW_1B] +law_1b + +[LAW_2A] +law_2a + +[LAW_2B] +law_2b + +[FEH_STA] +STATISTICHE + +[FEH_LOA] +CARICA + +[FEH_CON] +COMANDI + +[FEH_AUD] +AUDIO + +[FEH_DIS] +VIDEO + +[FEH_LAN] +LINGUA + +[FEH_SGA] +NUOVA PARTITA + +[FEO_CON] +Impostazioni controller + +[FEO_AUD] +Impostazioni audio + +[FEO_DIS] +Impostazioni video + +[FEO_LAN] +Impostazioni lingua + +[FEO_PLA] +Impostazioni giocatore + +[FEA_OUT] +Uscita + +[FEA_ST] +Stereo + +[FEA_DTS] +DTS + +[FEA_RSS] +Stazione radio + +[FEA_NON] +NO RADIO + +[FEA_FM0] +WILDSTYLE + +[FEA_FM1] +FLASH FM + +[FEA_FM2] +KCHAT + +[FEA_FM3] +FEVER 105 + +[FEA_FM4] +VROCK + +[FEA_FM5] +VCPR + +[FEA_FM7] +EMOTION 98.3 + +[FEA_FM8] +WAVE 103 + +[FED_BRI] +Luminosità + +[FED_TRA] +Tracce : + +[FED_SUB] +Sottotitoli + +[FED_WIS] +Panoramico + +[FED_POS] +Posizione schermo + +[FEP_RES] +Riprendi + +[FEP_STG] +Avvia partita + +[FEP_STA] +Statistiche + +[FEP_BRI] +Briefing + +[FEP_OPT] +Opzioni + +[FEP_QUI] +Esci + +[FES_LOA] +Carica partita + +[FES_DEL] +Elimina partita + +[FEC_CSU] +Impostazioni controller + +[FEC_RED] +Ridefinisci comandi + +[FEC_MOU] +Impostazioni mouse + +[DISTGOL] +Distanza percorsa su cart da golf (miglia) + +[DISTGOM] +Distanza percorsa su cart da golf (metri) + +[ST_FAVR] +Stazione radio preferita + +[ST_WSTR] +Stazione radio meno amata + +[ST_FAVV] +Veicolo preferito + +[ST_STAR] +Totale stelle di sospetto + +[ST_HEAD] +Colpi alla testa + +[ST_GANG] +Gang meno amata + +[ST_STGN] +Totale stelle di sospetto evase + +[TYREPOP] +Pneumatici esplosi con proiettili + +[TYRESLA] +Pneumatici tagliati con una lama + +[ST_BRK] +Numero di uccisioni nel Bloodring + +[ST_LTBR] +Permanenza massima nel Bloodring (sec) + +[ST_GNG1] +Cubani + +[ST_GNG2] +Haitiani + +[ST_GNG3] +Teppistelli + +[ST_GNG4] +Gang di Diaz + +[ST_GNG5] +Sorveglianza + +[ST_GNG6] +Gang di motociclisti + +[ST_GNG7] +Gang di Vercetti + +[ST_GNG8] +Giocatori di golf + +[FEA_FM6] +ESPANTOSO + +[ST_ASSI] +Contratti da sicario completati + +[DISTBIK] +Distanza percorsa in moto (miglia) + +[DISTBIM] +Distanza percorsa in moto (metri) + +[HOTEL] +Hotel Ocean View + +[ICC1_1] +~g~Utilizza il camioncino dei gelati per distribuire droga in Vice City. + +[ICC1_2] +~g~Parcheggia il camioncino e premi ~h~~k~~VEHICLE_HORN~~w~ per attivare il campanello e attrarre i clienti. + +[ICC1_3] +~g~Riceverai soldi per ogni transizione, ma più ne esegui, maggiori saranno le probabilità di attirare l'attenzione della polizia. + +[KICK1_9] +PUNTI DI CONTROLLO RIMANENTI: + +[FIN_B6] +Non hai abbastanza soldi per avviare questa missione. + +[TEX3_3] +~g~Per raccogliere una bomba, avvicinati con l'elicottero: la bomba verrà automaticamente raccolta. + +[TEX3_9] +~g~Raccogli una bomba avvicinandole l'elicottero. + +[HELP22] +Raggiungi la casa verde sul radar. + +[FES_FMS] +Formattazione completata. Seleziona OK per continuare. + +[FES_SSC] +Salvataggio completato. Seleziona OK per continuare. + +[FES_DSC] +Eliminazione completata. Seleziona OK per continuare. + +[FESZ_QC] +Vuoi sovrascrivere il salvataggio corrotto? + +[FES_CHE] +Attenzione! Uno o più trucchi sono stati attivati: ciò influenzerà il salvataggio. Si consiglia di non salvare questa partita. + +[FET_SG] +SALVA PARTITA + +[FEH_BRI] +BRIEFING + +[FEH_MAP] +MAPPA + +[FEM_OK] +OK + +[FEC_CRO] +Abbassarsi + +[FEC_CR3] +Abbassarsi (tasto L3) + +[FEC_SMT] +Sottomissione + +[FEC_SM3] +Sottomissione (tasto R3) + +[FEC_RSC] +Stazioni radio + +[ST_PR01] +Apprendista + +[ST_PR02] +Aviere + +[ST_PR03] +Ufficiale + +[ST_PR04] +Caporale + +[ST_PR05] +Tenente + +[ST_PR06] +Sergente + +[ST_PR07] +Capitano + +[ST_PR08] +Biggs + +[ST_PR09] +Wedge + +[ST_PR10] +Barone rosso + +[ST_PR11] +Goose + +[ST_PR12] +Viper + +[ST_PR13] +Jester + +[ST_PR14] +Chappy + +[ST_PR15] +Iceman + +[ST_PR16] +Maverick + +[ST_PR17] +Noops + +[ST_PR18] +Maresciallo + +[ST_PR19] +Asso + +[FET_LG] +CARICA PARTITA + +[CAR_EXP] +Veicoli distrutti + +[BOA_EXP] +Imbarcazioni distrutte + +[HEL_DST] +Velivoli distrutti + +[STFT_01] +Tempo migliore in 'Due ruote d'acciaio' + +[STFT_02] +Tempo migliore in 'L'autista' + +[STFT_03] +Tempo migliore nella prova del fango + +[STFT_04] +Tempo migliore con gli aerei radiocomandati + +[STFT_05] +Tempo migliore con le macchine radiocomandate + +[STFT_06] +Tempo migliore di raccolta con l'elicottero + +[STFT_07] +Tempo migliore in 'Terminal Velocity' + +[STFT_08] +Tempo migliore in 'Ocean Drive' + +[STFT_09] +Tempo migliore in 'Border Run' + +[STFT_10] +Tempo migliore in 'Capital Cruise' + +[STFT_11] +Tempo migliore in 'Tour!' + +[STFT_12] +Tempo migliore in 'V.C. Endurance' + +[STHC_01] +Punteggio migliore al poligono + +[STHC_02] +Miglior percentuale di colpi al poligono + +[STHC_03] +Numero di traffici di droga + +[HELP24] +Adesso puoi accettare le missioni del Colonnello. + +[HELP25] +Adesso puoi accettare le missioni di Avery Carrington. + +[HELP29] +Puoi far visita al negozio di abbigliamento quando non sei in missione. + +[HELP30] +Quando compri nuovi abiti, il tuo livello di sospetto scenderà a zero. + +[BJM2_22] +~r~Sei uscito dal poligono di tiro! + +[BJM2_23] +~g~Se esci dal poligono di tiro durante la competizione, fallirai la missione. + +[ASM4_24] +Distanza: + +[RBM1_6] +~g~Porta Mercedes e la 'Love Juice' al gruppo nello studio di registrazione. + +[RBM1_3] +NON NECESSARIO + +[HAM1_5] +NON NECESSARIO + +[RBM1_11] +NON NECESSARIO + +[HELP31] +Per eseguire un assalto in macchina, guarda a destra o a sinistra premendo il tasto L2 o il tasto R2. + +[HELP34] +Hai bisogno di una mitragliatrice per eseguire un assalto in macchina. + +[STRIP_1] +~r~Soldi insufficienti, brutto idiota. + +[EXIT_1] +Premi ~k~~PED_SPRINT~ per uscire. + +[ASM1_A] +Mr. Teal, il tuo aiuto nello sradicare gli stranieri è stato inestimabile per il nostro lavoro. Ho un altro compito con un contatto più 'diretto'. + +[ASM1_B] +I dettagli per il prossimo colpo sono stati attaccati sotto al telefono. + +[ASM1_C] +Ho un altro compito con un contatto più 'diretto'. + +[SCARF] +Appartamento 3c + +[LAW4_10] +La ricca amministrazione ci ha rotto! + +[GEN1_04] +~g~Attraversa la porta per accedere alla piattaforma sul tetto di Gonzalez. + +[GEN1_17] +~g~Gonzalez sta fuggendo! Seguilo attraverso le porte e finiscilo! + +[RCH1_9] +~b~TEMPO TOTALE: ~1~:~1~ + +[RCH1_10] +~b~TEMPO TOTALE: ~1~:0~1~ + +[WHEEL01] +BONUS DOPPIO SU DUE RUOTE: ~1~$ Distanza: ~1~.~1~ m Tempo: ~1~ secondi + +[WHEEL02] +BONUS DOPPIO SU DUE RUOTE: ~1~$ Distanza: ~1~ piedi Tempo: ~1~ secondi + +[WHEEL03] +BONUS SU DUE RUOTE: ~1~$ Tempo: ~1~ secondi + +[WHEEL04] +BONUS SU DUE RUOTE: ~1~$ Distanza: ~1~.~1~ m + +[WHEEL05] +BONUS SU DUE RUOTE: ~1~$ Distanza: ~1~ piedi + +[WHEEL06] +BONUS IMPENNATA: ~1~$ Distanza: ~1~.~1~ m Tempo: ~1~ secondi + +[WHEEL07] +BONUS IMPENNATA: ~1~$ Distanza: ~1~ piedi Tempo: ~1~ secondi + +[WHEEL08] +BONUS IMPENNATA: ~1~$ Tempo: ~1~ secondi + +[WHEEL09] +BONUS IMPENNATA: ~1~$ Distanza: ~1~.~1~ m + +[WHEEL10] +BONUS IMPENNATA: ~1~$ Distanza: ~1~ piedi + +[WHEEL11] +BONUS FRENATA IN PUNTA: ~1~$ Distanza: ~1~.~1~ m Tempo: ~1~ secondi + +[WHEEL12] +BONUS FRENATA IN PUNTA: ~1~$ Distanza: ~1~ piedi Tempo: ~1~ secondi + +[WHEEL13] +BONUS FRENATA IN PUNTA: ~1~$ Tempo: ~1~ secondi + +[WHEEL14] +BONUS FRENATA IN PUNTA: ~1~$ Distanza: ~1~.~1~ m + +[WHEEL15] +BONUS FRENATA IN PUNTA: ~1~$ Distanza: ~1~ piedi + +[ROK3_72] +Love Fist! + +[POR1_19] +Ehi! + +[DESPERA] +Desperado + +[MOB_99A] +Raggiungi il telefono pubblico vicino al Mall in Washington. + +[MOB_98A] +Raggiungi il telefono pubblico in Vice Point. + +[MOB_96A] +Raggiungi il telefono pubblico al terminal dell'aeroporto. + +[MOB_95A] +Raggiungi il telefono pubblico in Little Havana. + +[BNK1_1] +La posso aiutare, signore? + +[BNK1_2] +Un impostore! + +[BNK1_3] +È impazzito! + +[BNK1_4] +Ma chi diavolo sei? + +[BNK1_5] +Dov'è il tuo distintivo? + +[BNK1_6] +Eccoli là! Sparate per uccidere! + +[MOB_24A] +Salve: Mr Vercetti? + +[MOB_24B] +Sì. + +[MOB_24C] +Sono Cortez. Eri invitato al mio party. + +[MOB_24D] +Sì, ricordo. + +[MOB_24E] +Mr. Vercetti, è stato davvero uno sfortunato incidente quello avvenuto al tuo accordo. + +[MOB_24F] +Lo so. + +[MOB_24G] +Sappi che io e la mia gente stiamo facendo il massimo per scoprire cosa è successo. + +[MOB_24H] +Se vuoi parlarne in prima persona, mi troverai sulla mia barca. Buona giornata, senor. + +[BNK2_2] +MIRARE 3-2-1 FUOCO! + +[BNK2_3] +AREA RIPULITA! + +[BNK2_6] +Questo tipo è pazzo! + +[ANGEL] +Angel + +[CUBJET] +Jetmax Cubano + +[SANDKIN] +Sandking + +[POLMAV] +Maverick della polizia + +[BOXVILL] +Boxville + +[BENSON] +Benson + +[HOTRINA] +Hotring Racer + +[HOTRINB] +Hotring Racer + +[BLOODRA] +Bloodring Banger + +[BLOODRB] +Bloodring Banger + +[MAFIACR] +Cruiser della Mafia + +[COP_M2] +'VICE SQUAD' + +[COP_M3] +'BROWN THUNDER' + +[BJM2_20] +~g~Quando finisci il ~w~tempo~g~ o i ~w~colpi~g~, il turno è finito! + +[BNK3_2] +Non intendo guidare per te, per niente, e lo dirò ai miei compagni di terapia. + +[FEM_SL1] +Salvataggio 1 non trovato + +[FEM_SL2] +Salvataggio 2 non trovato + +[FEM_SL3] +Salvataggio 3 non trovato + +[FEM_SL4] +Salvataggio 4 non trovato + +[FEM_SL5] +Salvataggio 5 non trovato + +[FEM_SL6] +Salvataggio 6 non trovato + +[FEM_SL7] +Salvataggio 7 non trovato + +[FEM_SL8] +Salvataggio 8 non trovato + +[FEA_CHA] +Modifica uscita audio in STEREO. Attendere... + +[FEA_CHD] +Attenzione! Modifica uscita audio da STEREO a DTS. Attendere... + +[FEI_SEL] +Seleziona + +[FEI_BAC] +Indietro + +[FEI_RES] +Riprendi + +[FEI_NAV] +Naviga + +[FEI_BTX] +Tasto / - + +[FEI_BTT] +Tasto " - + +[FEI_STA] +Tasto START - + +[FEI_BTD] +; = > < - + +[FEI_STO] +Stop + +[MOB_68A] +Ehi Tommy, ho una sorpresa per te. + +[MOB_68B] +Sono allo studio cinematografico con dei grandi artisti. + +[MOB_68C] +Perché non vieni a farci visita? + +[MOB_68D] +Lo sai che è una buona idea, vero? Ci vediamo. + +[OUTFT1] +Da strada + +[OUTFT2] +Eleganti + +[OUTFT3] +Tuta + +[OUTFT4] +Sportivo + +[OUTFT5] +Havana + +[OUTFT6] +Da poliziotto + +[OUTFT7] +Da rapina + +[OUTFT8] +Casual + +[OUTFT9] +Mr. Vercetti + +[OUTFT10] +Da corsa + +[OUTFT13] +MC Tommy + +[HOTR_07] +Nuovo miglior tempo: ~1~:0~1~ + +[HOTR_08] +Tempo: ~1~:~1~ + +[GEN3_45] +Arriveranno fra pochi minuti: troviamo un buon punto dove appostarci... + +[CAR_AS1] +BENI CONCESSIONARIA ACQUISITI + +[CAR_AS2] +~g~La concessionaria d'ora in poi genererà introiti per un massimo di ~1~$. Ricordati di recuperarli regolarmente. + +[BUYSAVE] +~g~Se non sei in missione, potrai ora salvare la partita da qui gratuitamente. + +[BUYGARG] +~g~Potrai anche depositare i mezzi in questo garage. + +[STRPBUY] +Club Pole Position acquistato: ~1~$ + +[STRP_R3] +Premi il tasto R3 per acquistare il club Pole Position per ~1~$ + +[NBMN_R3] +Premi il tasto R3 per acquistare Elswanko Casa per ~1~$ + +[GA_4] +Le bombe per le macchine costano 1000$ + +[GA_5] +La tua macchina ha già una bomba installata. + +[GA_6] { reVC update } +Parcheggiala, attivala premendo il ~h~~k~~VEHICLE_FIREWEAPON~~w~ e DATTELA A GAMBE! + +[GA_7] { reVC update } +Arma la bomba con il ~h~~k~~VEHICLE_FIREWEAPON~~w~: esploderà non appena qualcuno cercherà di avviarla. + +[GA_6B] { reVC update } +Parcheggiala, attivala premendo il ~h~~k~~VEHICLE_FIREWEAPON~~w~ e DATTELA A GAMBE! + +[GA_7B] { reVC update } +Arma la bomba con il ~h~~k~~VEHICLE_FIREWEAPON~~w~: esploderà non appena qualcuno cercherà di avviarla. + +[MOB_70A] +Tommy, sono io, il Colonnello Cortez. Ascolta senor, credo tu sia una persona capace di risolvere i problemi. + +[MOB_70B] +Mi troverai sulla mia barca. + +[PICK1] +Giubbotto antiproiettile consegnato all'hotel Ocean View! + +[PICK2] +.357 consegnato al nascondiglio! + +[PICK3] +Motosega consegnata al nascondiglio! + +[PICK4] +Lanciafiamme consegnato al nascondiglio! + +[PICK5] +.308 Fucile di precisione consegnato al nascondiglio! + +[PICK6] +Mitragliatore consegnato al nascondiglio! + +[PICK7] +Lanciamissili consegnato al nascondiglio! + +[PICK8] +Sea Sparrow adesso disponibile alla villa! + +[PICK9] +Carro armato adesso disponibile alle caserme! + +[PICK10] +Hunter adesso disponibile alle caserme! + +[HELP41] +o puoi speronarli con il tuo mezzo + +[ICC1_6] +~g~Utilizza il Mr. Whopee per distribuire i prodotti Cherry Poppers per Vice City. + +[ICC1_12] +PROPRIETÀ ACQUISTATA! + +[CLOTH1] +Abiti eleganti consegnati a Rafaels in Ocean Beach. + +[CLOTH2] +Abiti da strada consegnati al nascondiglio. + +[CLOTH3] +Tuta consegnata a Tooled Up nel Mall North Point. + +[CLOTH4] +Abiti sportivi consegnati al club di Golf in Leafs Links. + +[CLOTH5] +Abiti havana consegnati a Little Havana Streetwear in Little Havana. + +[CLOTH6] +Abiti da poliziotto consegnati alla stazione di polizia in Washington Beach. + +[CLOTH7] +Abiti casual consegnati a Gash nel Mall North Point. + +[CLOTH8] +Abiti Mr. Vercetti consegnati a Collar & Cuffs in Ocean Beach. + +[CLOTH9] +Abiti da corsa consegnati a Jocksport in Downtown. + +[CLOTH10] +Abiti da rapina consegnati al club Malibu in Vice Point. + +[RBM1_9] +~g~Recupera della Love Juice dallo spacciatore per i Love Fist! + +[MOB_62A] +Tommy, sono Ricardo Diaz. Ti volevo ringraziare per l'aiuto ai miei uomini. + +[MOB_62B] +Ho chiesto a quel coglione di Cortez e mi ha detto che il vero affare sei tu. Amico, vienimi a trovare. + +[MOB_62C] +Ho bisogno di un tipo come te. Sono circondato da coglioni, + +[MOB_62D] +coglioni dappertutto, davvero. Ti farò diventare ricco. + +[GOAWAY2] +~g~Ritorna quando avrai completato le missioni dei motociclisti. + +[COL2_9] +Idiota di un americano! Ti sei fatto seguire! + +[LOADCOL] +Caricamento... + +[STFT_17] +Tempo migliore in 'Parco divertimenti PCJ' + +[STFT_18] +Tempo migliore in 'Prova del fango' + +[STFT_19] +Tempo migliore in 'Tracciato di prova' + +[NEW_REC] +Nuovo record!!! ~1~ minuti e ~1~ secondi. + +[BMX_HOW] +~g~Fai due giri sul tracciato sterrato, ~y~passando attraverso~g~ i ~y~PUNTI DI CONTROLLO~g~! + +[BMXREW1] +~g~Ogni volta che batti il tuo record precedente per i due giri, + +[BMXREW2] +~g~otterrai una ~y~RICOMPENSA~g~ più cospicua! + +[BMXRAIN] +~g~È come se piovesse... + +[ITBEG] +All'inizio... + +[NBMNBUY] +El Swanko Casa acquistata: ~1~$ + +[LNKVBUY] +Appartamento Links View acquistato: ~1~$ + +[HYCOBUY] +Condominio Hyman acquistato: ~1~$ + +[BUYGARS] +~g~Puoi parcheggiare altri veicoli in questi garage. + +[OCHEBUY] +Appartamento Ocean Heights acquistato: ~1~$ + +[WASHBUY] +1102 Washington Street acquistata: ~1~$ + +[VCPTBUY] +3321 Vice Point acquistata: ~1~$ + +[SKUMBUY] +Skumole Shack acquistato: ~1~$ + +[HELP6_C] +Premi il ~h~~k~~VEHICLE_HANDBRAKE~~w~ per tirare il freno a mano. + +[HELP2_A] +Premi il ~h~~k~~PED_SPRINT~~w~ mentre corri per effettuare uno ~h~scatto~w~. + +[HELP4_A] +Premi il ~h~~k~~VEHICLE_ACCELERATE~~w~ per ~h~accelerare~w~. + +[HELP5_A] +Premi il ~h~~k~~VEHICLE_BRAKE~~w~ per frenare o, se il veicolo è fermo, per inserire la retromarcia. + +[HELP8_A] +Premi il ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ per zoomare col fucile e il ~x~tasto /~w~ per allargare il campo. + +[PBOAT_1] { reVC update } +Premi il ~h~~k~~VEHICLE_FIREWEAPON~~w~ per sparare con i cannoni della barca. + +[SEG3_4] { reVC update } +~g~Puoi raccogliere una bomba avvicinando il Raider telecomandato. Per posizionare la bomba, premi il ~h~~k~~VEHICLE_FIREWEAPON~. + +[RCR1_3] { reVC update } +~g~Se desideri interrompere la missione, premi il ~h~~k~~VEHICLE_FIREWEAPON~~g~ per far esplodere la macchina. + +[HELP32] { reVC update } +Poi spara con il ~h~~k~~VEHICLE_FIREWEAPON~~w~. + +[HELP33] { reVC update } +Poi spara con il ~h~~k~~VEHICLE_FIREWEAPON~~w~. + +[TTUTOR] +Premi il ~h~~k~~TOGGLE_SUBMISSIONS~~w~ per attivare o disattivare le missioni Taxi. + +[TTUTOR2] +Premi il ~h~~k~~TOGGLE_SUBMISSIONS~~w~ per attivare o disattivare le missioni Taxi. + +[FTUTOR] +Premi il ~h~~k~~TOGGLE_SUBMISSIONS~~w~ per attivare o disattivare le missioni Camion dei pompieri. + +[FTUTOR2] +Premi il ~h~~k~~TOGGLE_SUBMISSIONS~~w~ per attivare o disattivare le missioni Camion dei pompieri. + +[CTUTOR] +Premi il ~h~~k~~TOGGLE_SUBMISSIONS~~w~ per attivare o disattivare le missioni Vigilante. + +[CTUTOR2] +Premi il ~h~~k~~TOGGLE_SUBMISSIONS~~w~ per attivare o disattivare le missioni Vigilante. + +[HELP8_B] +Premi il ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ per ~h~zoomare~w~ col fucile e il ~h~~k~~PED_SNIPER_ZOOM_OUT~~w~ per ~h~allargare~w~ il campo. + +[ATUTOR3] +Premi il ~h~~k~~TOGGLE_SUBMISSIONS~~w~ per attivare o disattivare le missioni Infermiere. + +[GUN_H1] +~w~Premi il ~h~~k~~PED_SPRINT~~w~ per comprare. ~w~Premi il ~h~~k~~VEHICLE_ENTER_EXIT~~w~ per uscire. + +[PU_CF3] { reVC update } +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per sostituire l'arma attuale con quella presente in questo slot. + +[PU_CF4] { reVC update } +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per sostituire l'arma attuale con quella presente in questo slot. + +[HELP9_B] +Premi il ~h~~k~~PED_FIREWEAPON~~w~ per sparare con il fucile di precisione. + +[HELP37] +Se non vuoi entrare in un veicolo mentre stai assalendo un guidatore, premi il ~h~~k~~PED_SPRINT~~w~. + +[HELP6_A] +Premi il ~h~~k~~VEHICLE_HANDBRAKE~~w~ per tirare il freno a mano. + +[HELP6_D] +Premi il ~h~~k~~VEHICLE_HANDBRAKE~~w~ per tirare il freno a mano. + +[HELP26] +Premi il ~h~~k~~VEHICLE_ENTER_EXIT~~w~ per entrare o uscire da un veicolo. + +[HELP27] +Premi il ~h~~k~~VEHICLE_TURRETUP~~w~ o ~h~~k~~VEHICLE_TURRETDOWN~~w~ per spostare il peso sulla moto. + +[HELP28] +Premi il ~h~~k~~VEHICLE_TURRETUP~~w~ o ~h~~k~~VEHICLE_TURRETDOWN~~w~ per spostare il peso sulla moto. + +[HELP35] +Premi il ~h~~k~~GO_LEFT~~w~ o la ~h~~k~~GO_RIGHT~~w~ per sterzare. + +[HELP36] +Premi il ~h~~k~~GO_LEFT~~w~ o la ~h~~k~~GO_RIGHT~~w~ per sterzare. + +[HELP42] +Segui il ~q~segnalino rosa~w~ per trovare l'hotel. + +[HELP19] +Cammina sul ~q~segnalino rosa~w~ per continuare. + +[HELP1] +Fermati al centro del ~q~segnalino rosa~w~. + +[HELP12] +Cammina nel centro del ~q~segnalino rosa~w~ per attivare una missione. + +[SEG3_6] +~g~Per colpire con successo un bersaglio, devi sganciare la bomba nell'area con un ~q~segnalino rosa~g~. Puoi posizionare le bombe in un ordine qualsiasi. + +[S_PROMP] +Quando non sei in missione, puoi salvare la partita raccogliendo il bonus ~h~audiocassetta~w~. + +[HELP16] +Passa attraverso la porta principale dell'hotel ~h~Ocean View~w~ per entrare nell'edificio. + +[HELP43] +~g~Raggiungi l'hotel ~h~Ocean View~g~ in Ocean Drive. + +[HELI_F1] +~r~Gara a tappe con l'elicottero annullata! + +[AMMUHLP] +Se hai bisogno di armi, fai un salto ad ~h~Ammu-Nation~w~. Segui il ~h~segnale a forma di pistola~w~ sul radar. + +[HELI_1] +Tappa elicottero Downtown + +[HELI_2] +Tappa elicottero Ocean Beach + +[HELI_3] +Tappa elicottero Vice Point + +[HELI_4] +Tappa elicottero Little Haiti + +[FST_MFR] +Stazione radio preferita + +[FST_LFR] +Stazione radio meno amata + +[FEI_HOL] +Trattieni + +[FEI_ZOO] +Zoom + +[FEI_BTR] +> < - + +[FEI_NA] +N\D + +[MESA] +Mesa Grande + +[STRP_NO] +Non puoi ancora comprare il club a luci rosse, prova più tardi. + +[CHSE] +INSEGUI + +[NBMN_L] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare El Swanko Casa per ~1~$ + +[NBMN_T] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare El Swanko Casa per ~1~$ + +[NBMN_C] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare El Swanko Casa per ~1~$ + +[LNKV_L] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare l'appartamento Links View per ~1~$ + +[LNKV_T] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare l'appartamento Links View per ~1~$ + +[LNKV_C] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare l'appartamento Links View per ~1~$ + +[HYCO_L] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il condominio Hyman per ~1~$ + +[HYCO_T] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il condominio Hyman per ~1~$ + +[HYCO_C] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il condominio Hyman per ~1~$ + +[OCHE_L] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare l'appartamento Ocean Heights per ~1~$ + +[OCHE_T] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare l'appartamento Ocean Heights per ~1~$ + +[OCHE_C] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare l'appartamento Ocean Heights per ~1~$ + +[WASH_L] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare 1102 Washington Street per ~1~$ + +[WASH_T] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare 1102 Washington Street per ~1~$ + +[WASH_C] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare 1102 Washington Street per ~1~$ + +[VCPT_L] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare 3321 Vice Point per ~1~$ + +[VCPT_T] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare 3321 Vice Point per ~1~$ + +[VCPT_C] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare 3321 Vice Point per ~1~$ + +[SKUM_L] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare lo Skumole Shack per ~1~$ + +[SKUM_T] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare lo Skumole Shack per ~1~$ + +[SKUM_C] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare lo Skumole Shack per ~1~$ + +[PRNT_L] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la tipografia per ~1~$ + +[PRNT_T] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la tipografia per ~1~$ + +[PRNT_C] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la tipografia per ~1~$ + +[CAR_L] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il concessionario per ~1~$ + +[CAR_T] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il concessionario per ~1~$ + +[CAR_C] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il concessionario per ~1~$ + +[PORN_L] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare lo studio cinematografico per ~1~$ + +[PORN_T] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare lo studio cinematografico per ~1~$ + +[PORN_C] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare lo studio cinematografico per ~1~$ + +[ICE_L] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la fabbrica di gelato per ~1~$ + +[ICE_T] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la fabbrica di gelato per ~1~$ + +[ICE_C] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la fabbrica di gelato per ~1~$ + +[TAXI_L] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la compagnia di taxi per ~1~$ + +[TAXI_T] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la compagnia di taxi per ~1~$ + +[TAXI_C] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la compagnia di taxi per ~1~$ + +[BANK_L] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il Malibu per ~1~$ + +[BANK_T] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il Malibu per ~1~$ + +[BANK_C] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il Malibu per ~1~$ + +[BOAT_L] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il cantiere navale per ~1~$ + +[BOAT_T] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il cantiere navale per ~1~$ + +[BOAT_C] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il cantiere navale per ~1~$ + +[STRP_L] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il club Pole Position per ~1~$ + +[STRP_T] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il club Pole Position per ~1~$ + +[STRP_C] +Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il club Pole Position per ~1~$ + +[STOCK] +~r~scorte esaurite + +[HELP14] +Per trovare l'ufficio dell'avvocato, segui la ~h~L~w~ sul radar. + +[RAMPAGE] +VIOLENZA! + +[RAMP_F] +VIOLENZA FALLITA! + +[RAMP_P] +VIOLENZA COMPIUTA! + +[RAMP_A] +TUTTE LE VIOLENZE COMPIUTE! + +[PAGE_01] +Uccidi ~1~ membri della gang in 2 minuti! + +[PAGE_02] +Distruggi ~1~ veicoli in 2 minuti! + +[PAGE_03] +Uccidi sparando in corsa ~1~ membri della gang in 2 minuti! + +[PAGE_04] +Travolgi ~1~ membri della gang in 2 minuti! + +[PAGE_05] +Fai saltare la testa a ~1~ membri della gang in 2 minuti! + +[SENTXS] +Sentinel XS + +[MAP_LEG] +Legenda + +[VCNMAV] +VCN Maverick + +[LG_01] +Posizione giocatore + +[LG_02] +Avery Carrington + +[LG_03] +Contatto motociclisti + +[LG_04] +Colonnello Cortez + +[LG_05] +Ricardo Diaz + +[LG_06] +Kent Paul + +[LG_07] +Avvocato + +[LG_08] +Phil Cassidy + +[LG_09] +Cantiere navale + +[LG_10] +Club Malibu + +[LG_11] +Cubani + +[LG_12] +Studio cinematografico + +[LG_13] +AmmuNation + +[LG_14] +Haitiani + +[LG_15] +Ferramenta + +[LG_16] +Nascondiglio + +[LG_17] +Fabbrica di gelato + +[LG_18] +Taxi Kaufman + +[LG_19] +Love Fist + +[LG_20] +Tipografia + +[LG_21] +Proprietà + +[LG_22] +Pay 'n' Spray + +[LG_23] +Negozio di vestiario + +[LG_24] +Dimora di Tommy + +[LG_25] +Telefono + +[LG_26] +Stazione radio Wildstyle + +[LG_27] +Stazione radio Flash FM + +[LG_28] +Stazione radio KChat + +[LG_29] +Stazione radio Fever 105 + +[LG_30] +Stazione radio VROck + +[LG_31] +Stazione radio VCPR + +[LG_32] +Stazione radio Espantoso + +[LG_33] +Stazione radio Emotion 98.3 + +[LG_34] +Stazione radio Wave 103 + +[LG_36] +Sun Yard + +[LG_37] +Club a luci rosse + +[MAP_YAH] +TU SEI QUI + +[TAXSHRT] +~g~Puoi utilizzare questo taxi Kaufman per arrivare a destinazione invece di guidare. Ti costerà 9$. + +[FE_MLG] +LEGENDA MAPPA + +[FED_RDR] +RADAR + +[FED_HUD] +INTERFACCIA + +[FED_RDL] +GRANDE + +[FED_RDB] +SOLO SEGNALI + +[FED_HUF] +DISSOLVENZA + +[FEST_HV] +Massimo livello missione Vigilante + +[BRIBE1] +Hai appena raccolto una bustarella della polizia: il tuo livello di sospetto è sceso di una stella. + +[CLOHELP] +Abiti puliti! + +[SUNSHIN] +Sunshine Autos + +[CHERRYP] +Gelateria Cherry Popper + +[KAUFCAB] +Taxi Kaufman + +[BOATYAR] +Cantiere navale + +[WANT_L] +Il tuo livello di sospetto è sospeso: se commetterai un crimine mentre le stelle lampeggiano, il tuo livello di sospetto verrà ripristinato. + +[HOTRNG] +HOTRING + +[BLODRNG] +BLOODRING + +[DIRTRNG] +DIRTRING + +[FEC_ABR] +Accelera, frena o inverti marcia + +[FEI_BTU] +; = - + +[FEI_SCR] +Scorri + +[LG_35] +Destinazione + +[BOAT_AS] +~g~Il cantiere navale d'ora in poi genererà introiti per un massimo di ~1~$. Ricordati di recuperarli regolarmente. + +[BOAT_A2] +CANTIERE NAVALE COMPLETATO + +[BOAT_N] +Tappa Charlie + +[BOAT_P] +~g~Raccogli i pacchi prima dello scadere del tempo. + +[FEI_R1B] +Tasto R1 \ R2 - + +[HELP9_A] +Premi il tasto ~h~~k~~PED_FIREWEAPON~~w~ per sparare con il fucile di precisione. + +[HELP21] +Premi il tasto ~h~~k~~VEHICLE_ENTER_EXIT~~w~ per entrare o uscire da un veicolo. + +[CREAM] +Distribuzione + +[UMBERTO] +Cafe Robina + +[PU_CF1] +Premi il tasto ~h~~k~~PED_ANSWER_PHONE~~w~ per raccogliere quest'arma e sostituirla con qualsiasi altra dello stesso tipo in tuo possesso. + +[FED_RDM] +MAPPA E INDICATORI + +[FEC_ILU] +Sguardo invertito in prima persona : + +[NITRO] +Tutti i taxi adesso hanno il super salto! Premi il pulsante del clacson. + +[RATNG53] +Inaffidabile + +[RATNG54] +Imbarazzante + +[RATNG55] +Hacker + +[RATNG56] +Baro + +[RATNG57] +Bugiardo assoluto + +[STHC_04] +Miglior punteggio con la palla sulla spiaggia + +[STHC_05] +Miglior risultato Hotring + +[STFT_13] +Tempo migliore corsa in elicottero a Downtown + +[STFT_14] +Tempo migliore corsa in elicottero a Ocean Beach + +[STFT_15] +Tempo migliore corsa in elicottero a Vice Point + +[STFT_16] +Tempo migliore corsa in elicottero a Little Haiti + +[STFT_21] +Tempo migliore nell'Hotring + +[STFT_22] +Giro migliore nell'Hotring + +[STFT_20] +Tempo migliore in 'Cone Crazy' + +[HELP44] +Fermati nel ~q~ segnalino rosa. + +[HELP45] +Premi il ~h~~k~~PED_DUCK~~w~ per abbassarti. In questo modo migliorerai la precisione con le armi da fuoco. + +[RCR1_5] +Corsa Bandit radiocomandati + +[RCPL1_7] +Corsa Baron radiocomandati + +[RCH1_11] +Raccolta Raider radiocomandato + +[FEA_CTD] +Attenzione! Questa caratteristica richiede delle periferiche compatibili DTS. Vuoi procedere? + +[FEM_STE] +USA STEREO + +[FEM_UDY] +USA DTS + +[GREET] +Saluti da... + +[LANCE_1] +Ehi bello, guida con più prudenza! + +[LANCE_2] +Ehi, guarda cosa hai combinato! + +[LANCE_3] +Ehi, ma dove stai andando adesso? + +[LANCE_4] +Cosa diavolo stai facendo? + +[LAW4_15] +Più soldi! + +[MERC_5] +Bella machina, Mr. Vercetti. + +[MERC_26] +PIÙ VELOCE, PIÙ VELOCE, PIÙ VELOCE! + +[MERC_27] +Attento Tommy, mi hanno rifatto il naso il mese scorso. + +[MERC_28] +Tommy, guida con attenzione. + +[MERC_29] +Tommy, vai più piano! + +[MERC_30] +Tommy per favore, fai fuori qualcun altro! + +[MERC_31] +Tommy, baby, non farmi fuori! + +[MERC_32] +Tommy, sono felice che hai rubato questa macchina! + +[MERC_40] +Mi sono davvero divertita. + +[MERC_43] +Adios, angioletto. + +[MERC_44] +Continua a lavorarci, capito? + +[MERC_45] +Ciao, bellezza. + +[COL5_17] +Oddio, hanno un elicottero! + +[COL5_18] +Abbatti l'elicottero! + +[COL5_19] +Tommy, abbatti quell'elicottero! + +[COL5_20] +Sta tornando! Distruggi l'elicottero! + +[COL5_21] +Ma guarda quanto è grosso! + +[COL5_22] +Sta tornando di nuovo! + +[FEA_DSM] +Attenzione! Questo salvataggio utilizza il DTS e richiede periferiche compatibili. Scegli se vuoi procedere utilizzando l'uscita DTS o quella STEREO. + +[STFT_23] +Tempo migliore per la tappa Charlie + +[HELP50] +Premi il tasto ~h~~k~~PED_ANSWER_PHONE~~w~ per posizionare la visuale da dietro. + +[HELP51] +Premi il tasto ~h~~k~~PED_ANSWER_PHONE~~w ~ per posizionare la visuale da dietro. + +[HELP52] +Premi il tasto ~h~~k~~PED_ANSWER_PHONE~~w ~ per posizionare la visuale da dietro. + +[HELP53] +Premi il tasto ~h~~k~~PED_CYCLE_WEAPON_LEFT~~w~ o il tasto ~h~~k~~PED_CYCLE_WEAPON_RIGHT~~w~ per passare in rassegna le armi disponibili. + +[HELP46] +Sono disponibili otto diversi tipi di arma. + +[HELP47] +Puoi trasportare una sola arma per tipo alla volta: un tipo di pistola, un tipo di fucile a pompa... + +[HELP54] +~w~Costo: ~1~$ ~r~Comprando quest'arma sostituirai quella in uso. + +[HELP2A2] +Premi il tasto ~h~~k~~PED_SPRINT~~w~ mentre corri per effettuare uno ~h~scatto~w~. + +[HLPSN_A] +Il fucile di precisione ti permette di ingrandire l'immagine e colpire i bersagli con precisione dalla distanza. + +[HLPSN_B] +Tieni premuto il tasto ~h~~k~~PED_LOCK_TARGET~~w~ per ~h~mirare~w~ col fucile di precisione. + +[HLPSN_C] +Tieni premuto il tasto ~h~L1~w~ per ~h~mirare~w~ col fucile di precisione. + +[HLPSN_D] +Premi il tasto ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ per ~h~zoomare~w~ col fucile e il tasto ~h~~k~~PED_SNIPER_ZOOM_OUT~~w~ per ~h~allargare il campo~w~. + +[HLPSN_E] +Premi il tasto ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~ col fucile di precisione. + +[HLPSN_F] +Premi il tasto ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~ col fucile di precisione. + +[HLPSN_G] +Premi il tasto ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~ col fucile di precisione. + +[PLANE_H] +Usa il tasto ~h~~k~~VEHICLE_ACCELERATE~~w~ per accelerare e a sinistra e a destra per curvare. + +[PLANE_4] { reVC update } +{Usa il tasto ~h~~k~~VEHICLE_ACCELERATE~~w~ per accelerare e a sinistra e a destra per curvare.} +Usa la levetta analogica destra per accelerare, premi la levetta analogica sinistra in basso per salire, in alto per scendere e a sinistra e a destra per curvare. + +[HELP55] +Premi il tasto ~h~~k~~PED_FIREWEAPON~~w~ per attaccare lo chef. + +[STPR_8] +Club Pole Position + +[STPR_9] +3321 Vice Point + +[STPR_10] +Appartamento Links View + +[STPR_11] +El Swanko Casa + +[STPR_12] +1102 Washington Street + +[STPR_13] +Appartamento Ocean Heights + +[STPR_14] +Skumole Shack + +[STPR_15] +Condominio Hyman + +[RCCANX] +~r~Aereo radiocomandato annullato. + +[CLT_HL2] +Quando viene raccolto un bonus vestiti, vengono rimosse una o due stelle del livello di sospetto. + +[CRED009] +PROGETTAZIONE MISSIONI + +[CRED359] +LEE JOHNSON + +[CRED360] +HENDRIK LESSER + +[CRED361] +PASQUALE STACCHIOTTI + +[CRED362] +ENRIQUE FERNANDEZ + +[CRED363] +PAUL BYERS + +[CRED364] +MIKE EMENY + +[CRED365] +ROB DUNKIN + +[CRED366] +CHARLIE KINLOCH + +[CRED367] +KEVIN HOBSON + +[CRED368] +JIM CREE + +[MOB_66A] +Tommy, Tommy, Tommy, perché sei tornato qua? + +[MOB_66B] +Te l'avevo detto che non vi volevamo più vedere. + +[MOB_67A] +Tommy, credo che dovresti andartene, capito? + +[MOB_67B] +I ragazzi Haitiani non sono molto felici di te. + +[MOB_18A] +Tommy, sono Paulo, come stai? Bene amico, ascolta: volevo farti una chiamata. + +[MOB_18B] +Ossignore, amico, non crederai mai che razza di strafiga ho appena incontrato. + +[MOB_18C] +Una prostituta forse, passeggiava per Little Havana. + +[MOB_18D] +Ha detto che si chiamava Mercedes o qualcosa del genere. + +[MOB_18E] +Oddio, dovresti proprio vedere questa gnocca. + +[MOB_18F] +Riuscirebbe a succhiare via la mina da una matita. Mi detto che ero il migliore che avesse mai avuto e via dicendo. + +[MOB_18G] +Dovresti farci un giro. Ci vediamo! + +[MOB_72A] +Tommy, sono io, Lance. Tieni la bocca chiusa Tommy, perché non ho tempo da perdere. + +[MOB_72B] +Non mi importa cos'hai da dire. Perché dovrei? Non te ne frega niente di me, non è vero? + +[MOB_72C] +Dovresti fare più attenzione a me. Dovresti darmi la fetta che merito. Lo sai... + +[MOB_72D] +Tommy... senti amico, mi dispiace. È solo che... + +[MOB_72E] +La gente mi ha sempre trattato con accondiscenda, come se fossi un bambino. + +[MOB_72F] +Mio fratello lo farebbe. Per favore, amico, tu non farlo. + +[MOB_72G] +Devo andare. + +[MOB_63A] +Tommy, sono Earnest. Earnest Kelly. + +[MOB_63B] +Come stai? + +[MOB_63C] +Non male. Avrò bisogno di un bastone per camminare, ma dovrei tornare al lavoro ben presto. + +[MOB_63D] +Bene. + +[MOB_63E] +Ho sentito di Lance. Che brutto storno, eh? + +[MOB_63F] +Sì. + +[MOB_63G] +Mai fidarsi di qualcuno che cammina per strada in pigiama. Ecco cosa dico. Sono felice che l'hai fatto fuori. Spero che sia stato doloroso per lui. + +[MOB_63H] +Credo di sì. Non credevo fosse quel genere di persona... + +[MOB_63I] +Tommy, per essere un pazzo violento, sei piuttosto ingenuo. Tornerò presto al lavoro e ti insegnerò un paio di cose sulla vita. Ci sentiamo. + +[MOB_63J] +Fai con calma, Earnest. Abbi cura di te. + +[MOB_16A] +Tommy, sono Paulo, que pasa amigo? + +[MOB_16B] +Che cosa vuoi Paul: non sono interessato a vestiti di marca taroccati. + +[MOB_16C] +Molto divertente, amico, ma lo sai che non tratto merce di seconda classe. Nah, ti ho chiamato per sapere se potevo avere una parte nei tuoi film. + +[MOB_16D] +Quando ero in Inghilterra ho fatto un sacco di cose, sai? Ho attributi più forniti di te, amico. + +[MOB_16E] +Paul, grazie per l'offerta, lo terrò presente. + +[MOB_16F] +Davvero, non scordarti di me dopo tutto quello che ho fatto per te. + +[MOB_16G] +È proprio ciò che sto cercando di dimenticare... + +[MOB_17A] +Tommy Vercetti: come va, Mr. Pezzo Grosso? Ho saputo le tue novità: un vero giocatore in città, eh... + +[MOB_17B] +Paul, sei ubriaco. + +[MOB_17C] +No, idiota. Non sono ubriaco. Solo un paio di bicchierini e qualche pillola: non dormo da un paio di giorni, sai... + +[MOB_17D] +Comunque, niente prediche. Non sono uno stupido. Chi ti ha portato in questa città? Chi? Io, ecco chi. + +[MOB_17F] +Davvero? + +[MOB_17G] +Niente prediche, smettila! Ti ho fatto conoscere la gente giusta, ti ho mostrato che corde tirare. Ho fatto molto per te e questo è il modo con cui mi ringrazi. + +[MOB_17H] +Mi ignori. Non mi aiuti a entrare dopo tutto ciò che ho fatto per te! Cosa credi che sia? Un idiota? + +[MOB_17I] +Paul, sta' buono. Sono stato occupato, non fare l'idiota. + +[MOB_17J] +Non sono un idiota, chiaro? Questo è ciò che hanno detto in riformatorio. Stai forse cercando guai, amico? Beh, stai per trovarli! + +[MOB_17K] +Tommy, amico. Per favore. Sei la mia più grande speranza! Per favore, non ridere di me. + +[MOB_17L] +Paul, cerca di dormire, davvero. + +[MOB_73A] +Tommy, sono Steve. + +[MOB_73B] +Ehi Steve. + +[MOB_73C] +Ehi, ciao genio. Sei uno spettacolo! Anch'io sono uno spettacolo! Ci adorano. Stiamo battendo tutti i record, amico. + +[MOB_73D] +Stiamo ottenendo dei grandi riconoscimenti. Finalmente posso comprare una casa al mio vecchio così se ne starà zitto. + +[MOB_73E] +Eeer, mi sembra perfetto, Steve. + +[MOB_73F] +Perfetto? È meraviglioso! Meraviglioso, MERAVIGLIOSO! Non ha mai creduto in me. Non pensava fossi un artista, ma adesso ce l'ho fatta. + +[MOB_73G] +Sono il miglior direttore sadomaso di tutti i tempi, amico. Volevo solo dirti che è stato un piacere averti incontrato. + +[MOB_73H] +Grazie Steve. + +[MOB_73I] +Ti voglio bene, amico. Non cambiare, capito? + +[MOB_73J] +Capito. Ci sentiamo Steve. + +[BOLLOX] +Premi il tasto ~o~R1~w~ per sganciare una bomba. Premi il tasto ~t~"~w~ per annullare. + +[BRID_OP] +Pericolo uragano terminato: tutti i ponti per la terra ferma sono nuovamente aperti. + +[BRID_CL] +Pericolo uragano: tutti i ponti per la terra ferma sono chiusi. + +[LG_38] +Bersaglio + +[ASSET_C] +POLE POSITION COMPLETATA! + +[ASSET_D] +~g~ Il Club Pole Position generera' un reddito fino ad un massimo di $~1~ al giorno. Ritira il denaro regolarmente! + +[ST_WHEE] +Impennata più lunga (sec) + +[ST_STOP] +Frenata in punta più lunga (sec) + +[ST_2WHE] +Permanenza su 2 ruote più lunga (sec) + +[ST_WHED] +Distanza piu' lunga in impennata (m) + +[ST_STOD] +Distanza piu' lunga in frenata (m) + +[ST_2WHD] +Distanza piu' lunga su 2 ruote (m) + +[OUTFT11] +Tuta da ginnastica + +[OUTFT12] +Frankie + +[RELOAD] +~g~Hai vinto l'abilita' di ricarica veloce! + +[APACHE] +Hunter consegnato all'eliporto di Ocean Beach + +[CRED369] +JOHN MCCARDLE + +[CRED370] +DAVID MURDOCH + +[CRED371] +CHRIS BROWN + +[CRED372] +PAUL GREEN + +[CRED373] +KYLE MILNE + +[CUNTY] +Nuovi vestiti consegnati alla Proprieta' di Vercetti! + +[GOODBOY] +$50 Come Bonus Buon Cittadino! + +[NEWCONT] +Nuovo ~h~Punto di Contatto~w~ disponibile alla Marina di Ocean Beach!! + +[FIRELVL] +Missione Camion dei pompieri livello ~1~ + +[HELP56] +Premi il tasto ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~ per cambiare la modalità di visuale. + +[HELP57] +Premi il tasto ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~ per cambiare la modalità di visuale. + +[HELP58] +Mentre prendi la mira, premi il ~h~~k~~PED_CYCLE_TARGET_LEFT~~w~, ~h~~k~~PED_CYCLE_TARGET_RIGHT~~w~ per cambiare bersaglio. + +[HELP59] +Mentre prendi la mira, premi il ~h~~k~~PED_CYCLE_TARGET_LEFT~~w~, ~h~~k~~PED_CYCLE_TARGET_RIGHT~~w~ per cambiare bersaglio. + +[HELP60] +Se premi il tasto ~h~~k~~PED_SPRINT~~w~ mentre assalti una macchina, non entrerai nel veicolo. + +[HELP61] +Adesso hai colpi infiniti e il doppio dell'energia su tutti i veicoli. + +[CRED374] +KEVIN YUN + +[CRED375] +ERICK COBBS + +[CRED376] +RANDY BLAKE + +[CRED377] +BRANDON LIM + +[CRED378] +BRANDON FENOL + +[CRED379] +MICHAEL MANOLE + +[CRED380] +ALETHEIA SIMONSON + +[CRED381] +JOHN JANSEN + +[FEC_LB1] +Guarda + +[FEC_LB2] +indietro + +[FEC_LB3] +Guarda indietro + +[FEC_R3] +(tasto R3) + +[FEC_PED] +Comandi a piedi + +[FEC_VEH] +Comandi in un veicolo + +[FEC_FPR] +Comandi in prima persona + +[FEC_CMM] +Comandi comuni + +[FEC_PWL] +Cammina a sinistra + +[FEC_PWR] +Cammina a destra + +[FEC_PWF] +Cammina in avanti + +[FEC_PWT] +Cammina verso la telecamera + +[FEC_PLB] +Guardati indietro + +[FEC_PFR] +Spara con l'arma + +[FEC_CLE] +Arma precedente + +[FEC_CRI] +Arma successiva + +[FEC_LKT] +Aggancia bersaglio + +[FEC_PJP] +Salto Ped + +[FEC_PSP] +Scatto Ped + +[FEC_PSH] +Sparo Ped + +[FEC_TLF] +Bersaglio successivo a sinistra + +[FEC_TRG] +Bersaglio successivo a destra + +[FEC_CCM] +Centra visuale dietro al giocatore + +[FEC_SZI] +Ingrandimento fucile di precisione + +[FEC_SZO] +Riduzione fucile di precisione + +[FEC_LKL] +Sguardo a sinistra + +[FEC_LRT] +Sguardo a destra + +[FEC_LUP] +Sguardo verso l'alto + +[FEC_LDN] +Sguardo verso il basso + +[FEC_LBH] +Guarda dietro al veicolo + +[FEC_LLF] +Guarda a sinistra del veicolo + +[FEC_LRG] +Guarda a destra del veicolo + +[FEC_HRN] +Clacson + +[FEC_HBR] +Freno a mano + +[FEC_ACL] +Acceleratore + +[FEC_BRK] +Freno + +[FEC_TSM] +Attiva/Disattiva sottomissioni + +[FEC_CRD] +Cambia stazione radio + +[FEC_ENT] +Entra/Esci da un veicolo + +[FEC_WPN] +Spara con l'arma + +[FEC_PAS] +Pausa + +[FEC_FPO] +Attiva/Disattiva visuale di puntamento + +[FEC_SMS] +Mostra/Nascondi puntatore del mouse + +[FEC_CMS] +Cambia la modalità di visuale in qualsiasi situazione + +[FEC_TSS] +Salva un'immagine di gioco + +[FEC_DBG] +Menu debug + +[FEC_TGD] +Pad game/Debug + +[FEC_TDO] +Disabilita visuale debug + +[FEC_IVH] +Inverti mouse orizzontalmente + +[FEC_MSL] +PULSANTE SX + +[FEC_MSM] +PULSANTE CEN + +[FEC_MSR] +PULSANTE DX + +[FEC_QUE] +??? + +[FEC_TWO] +Un massimo di due tasti per comando. + +[FEC_UMS] +Un pulsante del mouse può essere assegnato a un solo comando! + +[FEC_OMS] +Solo un pulsante del mouse. + +[FEC_UJS] +Un pulsante del joystick può essere usato per un solo comando! + +[FEC_OJS] +Solo un pulsante del joystick. + +[FEC_PTL] +Usa Aggancia bersaglio con Arma precedente + +[FEC_PTR] +Usa Aggancia bersaglio con Arma successiva + +[FEC_LBC] +Usa Guarda a sinistra con Guarda a destra + +[FEC_JBO] +JOY ~1~ + +[FEC_WAR] +Attenzione + +[FEC_OKK] +OK + +[FEC_DLF] +Eliminazione fallita. + +[FEC_SVU] +Salvataggio fallito. + +[FEC_LUN] +Caricamento fallito. File corrotto: è necessario eliminarlo. + +[FEC_PAD] +Gamepad + +[FEC_JOY] +Joystick + +[FES_CSA] +Seleziona una skin dalla seguente lista: + +[FET_HRD] +IMPOSTAZIONI PREDEFINITE RIPRISTINATE + +[FET_MST] +STERZO VIA MOUSE + +[FEC_STR] +NUM ASTERISCO + +[FET_MIG] +SINISTRA, DESTRA, ROTELLA PER MODIFICARE + +[FET_CIG] +INDIETRO PER AZZERARE - PULSANTE SX, INVIO PER MODIFICARE + +[FET_DSN] +Skin predefinita.bmp + +[FET_RSO] +RIPRISTINATE IMPOSTAZIONI ORIGINALI + +[FET_RSC] +HARDWARE NON DISPONIBILE - RIPRISTINATE IMPOSTAZIONI ORIGINALI + +[FEA_3DH] +HARDWARE AUDIO + +[FEA_SPK] +CONFIGURAZIONE ALTOPARLANTI + +[FEM_LOD] +DISTANZA VISUALE + +[FEM_VSC] +SINCRONIA FRAME + +[FEM_FRM] +LIMITATORE FRAME + +[FEM_MM] +MENU PRINCIPALE + +[FED_RES] +RISOLUZIONE + +[FET_CTL] +IMPOSTAZIONI DEI COMANDI + +[FET_OPT] +OPZIONI + +[FEC_MSH] +SENSIBILITÀ MOUSE + +[FEC_IVV] +INVERTI MOUSE VERTICALMENTE + +[FET_MTI] +CONFIGURAZIONE COL MOUSE + +[FEC_FNC] +F~1~ + +[FEC_IRT] +INS + +[FEC_DLL] +CANC + +[FEC_HME] +HOME + +[FEC_END] +FINE + +[FEC_PGU] +PAG SU + +[FEC_PGD] +PAG GIÙ + +[FEC_UPA] +SU + +[FEC_DWA] +GIÙ + +[FEC_LFA] +SINISTRA + +[FEC_RFA] +DESTRA + +[FEC_NUM] +NUM + +[FEC_NMN] +NUM~1~ + +[FEC_FWS] +NUM / + +[FEC_PLS] +NUM + + +[FEC_MIN] +NUM - + +[FEC_DOT] +NUM . + +[FEC_NLK] +BLOC NUM + +[FEC_ETR] +INVIO + +[FEC_SLK] +BLOC SCORR + +[FEC_PSB] +PAUSA + +[FEC_BSP] +INDIETRO + +[FEC_TAB] +TAB + +[FEC_CLK] +BLOC MAIUSC + +[FEC_RTN] +RET + +[FEC_LSF] +MAIUSC SX + +[FEC_RSF] +MAIUSC DX + +[FEC_LCT] +CTRL SX + +[FEC_RCT] +CTRL DX + +[FEC_LAL] +ALT SX + +[FEC_RAL] +ALT DX + +[FEC_LWD] +WIN SX + +[FEC_RWD] +WIN DX + +[FEC_WRC] +CLIC WIN + +[FEC_SPC] +BARRA + +[WIN_TTL] +Grand Theft Auto VC + +[WIN_95] +Grand Theft Auto VC non è supportato da Windows 95 + +[WIN_DX] +Grand Theft Auto VC richiede DirectX versione 8.1 o superiore + +[FET_EIG] +IMPOSSIBILE ASSEGNARE UN COMANDO A QUESTA AZIONE + +[FET_DAM] +MODELLAZIONE ACUSTICA DINAMICA + +[FEQ_SRE] +Sei sicuro di voler uscire? Tutti i progressi dall'ultimo salvataggio verranno persi. Vuoi procedere? + +[FEQ_SRW] +Sei sicuro di voler uscire dal gioco? + +[FET_QG] +ESCI DAL GIOCO + +[FEN_STA] +AVVIA LA PARTITA + +[FET_PAU] +MENU PAUSA + +[REPLAY] +REPLAY + +[FEC_ANS] +Azione + +[CVT_MSG] +Conversione texture per prestazioni ottimali con la tua scheda video + +[FEC_SFT] +MAIUSC + +[FEH_VMP] +VEDI MAPPA + +[FES_DEE] +Eliminazione fallita! Prova nuovamente. + +[FES_CMP] +Salvataggio fallito! Prova nuovamente. + +[FESZ_WR] +Salvataggio partita. Un momento... + +[FELD_WR] +Caricamento partita. Un momento... + +[FEDL_WR] +Eliminazione salvataggio. Un momento... + +[PCRESRT] +Avvio nuova partita. Un momento... + +[FET_STI] +Configurazione standard + +[FET_CTI] +Configurazione classica + +[FET_PS] +IMPOSTAZIONE SKIN GIOCATORE + +[FEH_NA] +OPZIONE NON DISPONIBILE + +[FEH_MPH] +MOUSE, FRECCE PER MUOVERSI - PAGSU, PAGGIÙ, ROTELLINA PER LO ZOOM, L - LEGENDA + +[FEA_MP3] +RIPRODUTTORE MP3 + +[NO_PCCD] +Inserisci il CD di GTA Vice City o premi Esc per annullare + +[FEH_SSA] +FRECCE PER MUOVERSI - S PER SALVARE + +[FES_CMI] +ULTIMA MISSIONE COMPLETATA + +[FET_STS] +STATISTICHE SALVATE NEL FILE 'STATS.HTML' + 'STATS.TXT' + +[WIN_VDM] +Memoria video insufficiente per eseguire Grand Theft Auto VC + +[FEC_ERI] +Errore! Uno o più azioni non sono assegnate a nessun tasto o pulsante. Assicurati che a tutte le azioni corrisponda un comando. + +[FEC_TFU] +Torretta + Inclina in su + +[FEC_TFD] +Torretta + Inclina in giù + +[FET_RIG] +SELEZIONA IL NUOVO COMANDO PER QUESTA AZIONE + +[FEA_NM3] +NESSUN FILE MP3 TROVATO + +[FEA_MPB] +INCREMENTO VOLUME MP3 + +[FEA_MUS] +VOLUME MUSICA + +[FEA_SFX] +VOLUME EFFETTI + +[CVT_ERR] +Spazio su disco esaurito; è necessario liberare dello spazio prima di poter procedere. Premi ESC per annullare. + +[FEA_ADP] +RILEVAMENTO AUTOMATICO + +{=================================== MISSION TABLE AMBULAE ===================================} + +[ATUTOR2:AMBULAE] +~g~Conduci i pazienti all'ospedale guidando CON PRUDENZA! Qualsiasi scossone può ucciderli. + +[A_FULL:AMBULAE] +~r~Ambulanza piena! + +[A_FAIL2:AMBULAE] +~r~La tua scarsa sollecitudine è stata fatale al paziente! + +[A_FAIL3:AMBULAE] +~r~Il paziente è morto! + +[A_PASS:AMBULAE] +Salvato! + +[A_COMP2:AMBULAE] +Non ti stancherai mai! + +[A_COMP1:AMBULAE] +Missioni Infermiere completate: ~1~$ + +[A_CANC:AMBULAE] +~r~Missione Infermiere annullata! + +[A_COMP3:AMBULAE] +Missione Infermiere completata! Non ti stancherai mentre corri! + +[ALEVEL:AMBULAE] +Missione Infermiere livello ~1~ + +[A_FAIL1:AMBULAE] +Missione Infermiere terminata. + +[A_SAVES:AMBULAE] +PERSONE SALVATE: ~1~ + +{=================================== MISSION TABLE ASSIN1 ===================================} + +[ASM1_5:ASSIN1] +~r~Ha completato le sue consegne! + +[ASM1_6:ASSIN1] +Consegne rimanenti: + +[ASM1_7:ASSIN1] +~g~Carl Pearson, fattorino di una pizzeria. Uccidilo prima che completi le sue consegne. + +[ASM1_D:ASSIN1] +Mr. Teal, il tuo aiuto nello sradicare gli stranieri è stato inestimabile per il nostro lavoro. + +{=================================== MISSION TABLE ASSIN2 ===================================} + +[ASM2_1:ASSIN2] +~g~Mrs. Dawson lascerà presto la gioielleria in Vice Point. Devi ucciderla. Deve sembrare un incidente d'auto. + +[ASM2_3:ASSIN2] +~g~Sta per esplodere! Allontanati! + +[ASM2_4:ASSIN2] +~r~Hai danneggiato la sua auto, ma lei non è sopra! Adesso non ci entrerà di sicuro! + +[ASM2_5:ASSIN2] +~r~È riuscita a scappare! + +[ASM2_6:ASSIN2] +~r~Sei stato visto troppo vicino alla scena dell'incidente! + +[ASM2_7:ASSIN2] +~g~Non utilizzare armi! Deve sembrare un incidente! Spingila fuori strada! + +[ASM2_8:ASSIN2] +~g~La morte di Mrs. Dawson deve sembrare un incidente. Non utilizzare armi. + +[ASM2_9:ASSIN2] +~g~Avrai bisogno di una vettura per questo lavoro! + +[ASM2_10:ASSIN2] +~g~Quando la sua macchina prende fuoco, allontanati il più possibile dal luogo dell'incidente. + +[ASM2_11:ASSIN2] +Aiuto! + +[ASM2_12:ASSIN2] +Qualcuno mi aiuti! + +[ASM2_13:ASSIN2] +Oddio! + +[ASM2_A:ASSIN2] +Complimenti per l'ottimo lavoro, Mr. Teal. Il mio cliente è molto soddisfatto. + +[ASM2_2:ASSIN2] +Salute: + +{=================================== MISSION TABLE ASSIN3 ===================================} + +[ASM3_11:ASSIN3] +TEMPO: + +[ASM3_C:ASSIN3] +Una gang europea ha pianificato un furto in banca a Vice City. I miei clienti preferirebbero che il colpo non vada a segno. + +[ASM3_D:ASSIN3] +Ogni membro della gang ha un nascondiglio nella città: alcuni hanno dei lavori di copertura, altri sono in vacanza. + +[ASM3_E:ASSIN3] +Dettagli e posizione più probabile di ogni bersaglio sono riportati nel nastro sotto al telefono. + +[ASM3_14:ASSIN3] +~g~Dick Tanner si trova presso la DBP Security in Ocean Drive. + +[ASM3_15:ASSIN3] +~g~Marcus Hammond e Franco Carter si trovano presso la gioielleria in Vice Point. + +[ASM3_16:ASSIN3] +~g~Nick Kong si trova presso Washington Beach. + +[ASM3_18:ASSIN3] +~g~Non avvicinarti troppo, se no il bersaglio potrebbe vederti e sarai costretto a inseguirlo! + +[ASM3_19:ASSIN3] +~g~Ti ha visto! Fallo fuori! + +[ASM3_20:ASSIN3] +~g~Ti hanno visto! Falli fuori entrambi! + +[ASM3_21:ASSIN3] +~r~Non hai ucciso tutti i membri della gang in tempo! + +[ASM3_22:ASSIN3] +~g~Non avvicinarti troppo, se no i bersagli potrebbero vederti e cercare di scappare! + +[ASM3_12:ASSIN3] +~g~Un assortimento di armi è stato lasciato per te nel caso di bisogno. Hai ~h~9 MINUTI~g~ per uccidere tutti i membri della gang. + +[ASM3_13:ASSIN3] +~g~Mike Griffin lavora su un cartello pubblicitario in Washington. + +[ASM3_17:ASSIN3] +~g~Charlie Dilson è in macchina in Washington. + +{=================================== MISSION TABLE ASSIN4 ===================================} + +[ASM4_12:ASSIN4] +Distanza: + +[ASM4_15:ASSIN4] +~g~Recupera il fucile di precisione alla tua destra. + +[ASM4_16:ASSIN4] +~g~Osserva la donna sul balcone: scenderà per le scale e chiederà l'ora a un uomo. + +[ASM4_17:ASSIN4] +~g~Solo quando la conversazione avrà termine, dovrai uccidere l'uomo con cui ha parlato ma NON la donna. + +[ASM4_18:ASSIN4] +~g~Una volta eliminato il bersaglio, recupera la sua valigetta e portala ad Ammu-Nation in Downtown. + +[ASM4_19:ASSIN4] +~g~Tieni la distanza dal bersaglio: la barra nella parte superiore destra dello schermo indica quanto gli sei vicino. + +[ASM4_20:ASSIN4] +~g~Se si riempirà, verrai avvistato. + +[ASM4_21:ASSIN4] +~g~Recupera la valigetta! + +[ASM4_22:ASSIN4] +~g~Porta la valigetta ad Ammu-Nation in Downtown. + +[ASM4_23:ASSIN4] +~g~Ti ha visto e sta fuggendo! Uccidilo e recupera la valigetta! + +[ASM4_25:ASSIN4] +~r~Hai ucciso la donna, idiota! + +[ASM4_26:ASSIN4] +~r~Il bersaglio è salito a bordo del volo! + +[ASM4_27:ASSIN4] +~r~Il bersaglio ti ha visto! Avresti dovuto tenerti a distanza! + +[ASM4_28:ASSIN4] +~r~Il bersaglio ti ha visto! Ti ha sentito sparare! + +[ASM4_29:ASSIN4] +~r~Dovevi ucciderlo solo dopo che aveva parlato con la donna! + +[ASM4_A:ASSIN4] +È giunta l'ora di occuparsi di pesci più grossi, Mr Teal. Troverai un fucile tra le foglie alla tua destra. + +[ASM4_B:ASSIN4] +Osserva la donna sul balcone sopra i banchi dei check-in: scenderà per le scale e chiederà l'ora a un uomo. + +[ASM4_C:ASSIN4] +Dovrai uccidere questa persona, recuperare la sua valigetta e portarla nella locazione riportata sotto al telefono. + +{=================================== MISSION TABLE ASSIN5 ===================================} + +[ASM5_A:ASSIN5] +C'è uno scambio di beni sopra il tetto della fabbrica di gelato Cherry Popper. + +[ASM5_B:ASSIN5] +Uccidi tutti gli elementi coinvolti, recupera la merce e portala all'eliporto presso l'aeroporto. + +[ASM5_C:ASSIN5] +Alla tua sinistra troverai un cancello che porta al retro della fabbrica. + +[ASM5_1:ASSIN5] +~g~Entra nel cortile dietro alla fabbrica di gelato Cherry Popper e apriti la strada fino al luogo dello scambio. + +[ASM5_2:ASSIN5] +~g~Recupera la merce e portala all'eliporto presso l'aeroporto. + +[ASM5_3:ASSIN5] +~g~Porta la merce all'eliporto presso l'aeroporto! + +{=================================== MISSION TABLE BANKJ1 ===================================} + +[WANTED1:BANKJ1] +~g~Semina i poliziotti per perdere il tuo livello di sospetto. + +[BJM1_A:BANKJ1] +Tommy! Ehi Tommy, guarda qua, è grandioso! Ho fatto installare un minibar! + +[BJM1_B:BANKJ1] +Abbiamo un intero bar al piano di sotto, Ken. + +[BJM1_C:BANKJ1] +Sì, sì, certo. Beh, ho portato quella lavagna che mi avevi richiesto. + +[BJM1_D:BANKJ1] +Ah, questi sono i benefici della scuola dell'obbligo: l'abilità di seguire le istruzioni. + +[BJM1_E:BANKJ1] +Adesso mi serve uno scassinatore. + +[BJM1_F:BANKJ1] +Oh, va bene, beh, fammi pensare... scassinatore, scassinatore, scassinatore... Trovato! Questo tipo ti farà impazzire! + +[BJM1_G:BANKJ1] +Ahh, nah, quello schmuck. È dentro. + +[BJM1_H:BANKJ1] +Dentro dove? + +[BJM1_I:BANKJ1] +In una cella nel quartier generale della polizia in attesa di trasferimento. + +[BJM1_J:BANKJ1] +Credo stia per essere rilasciato sulla parola... + +[BJM1_1:BANKJ1] +~g~Fai fuggire Cam Jones dalla custodia della polizia! + +[BJM1_3:BANKJ1] +~g~Troverai ciò che ti serve nello spogliatoio della stazione. + +[BJM1_21:BANKJ1] +~g~La tessera per le celle si trova nel piano superiore della stazione. + +[BNK1_7:BANKJ1] +Cam Jones? + +[BNK1_8:BANKJ1] +Ti sto facendo scappare! + +[BNK1_10:BANKJ1] +Sì, sono io... + +[BNK1_11:BANKJ1] +Come vuoi! + +[BNK1_13:BANKJ1] +Devo fare un lavoro e tu sarai il mio scassinatore. + +[BNK1_14:BANKJ1] +Sempre meglio che restare col culo in galera! + +[BJM1_22:BANKJ1] +~g~Riporta Cam a casa sua! + +[BJM1_23:BANKJ1] +~g~Prima devi recuperare la tessera! + +[BNK1_12:BANKJ1] +Fai perdere le tracce e riportami a casa! + +[BJM1_20:BANKJ1] +Metti via l'arma o ne pagherai le conseguenze! + +[BJM1_5:BANKJ1] +Solo personale autorizzato oltre a questo punto! + +[BJM1_2:BANKJ1] +~r~Dovevi far fuggire Cam, non farlo morire! + +[BJM1_4:BANKJ1] +È armato! Uccidetelo! + +{=================================== MISSION TABLE BANKJ2 ===================================} + +[BJM2_A:BANKJ2] +Abbiamo bisogno di un tiratore. Ne conosci uno? + +[BJM2_B:BANKJ2] +Ehi Tommy, Tommy, Tommy, questa roba ti tiene in forma, amico. + +[BJM2_C:BANKJ2] +WoooOOOooo! + +[BJM2_D:BANKJ2] +Potrei essere io il tuo tiratore! Spara! Spara! + +[BJM2_E:BANKJ2] +Non sei un tiratore, sei un idiota. + +[BJM2_F:BANKJ2] +Fatti un drink e sta zitto. + +[BJM2_G:BANKJ2] +Ehi, via dai miei piedi! Ye ye ye ow ow. + +[BJM2_H:BANKJ2] +Cam, cosa mi dici? + +[BJM2_I:BANKJ2] +Beh, il miglior tiratore in città è un tipo chiamato Cassidy. + +[BJM2_J:BANKJ2] +Davvero? + +[BJM2_K:BANKJ2] +Sì. Un militare, o per lo meno crede di esserlo. + +[BJM2_L:BANKJ2] +Non credo sia mai stato nell'esercito, ma sicuramente sa come tenere in mano un'arma. + +[BJM2_M:BANKJ2] +Lo troverai di sicuro al poligono di tiro. + +[BJM2_2A:BANKJ2] +Sei Phil Cassidy? + +[BJM2_2B:BANKJ2] +Perché? + +[BJM2_2C:BANKJ2] +Sto cercando un uomo che sappia come sparare. Da quello che vedo, non sono molto convinto... + +[BJM2_2D:BANKJ2] +Figliolo, potrei colpire una mosca sulla tua testa a 30 metri di distanza. + +[BJM2_2E:BANKJ2] +Davvero? + +[BJM2_2F:BANKJ2] +Certo. Ho fatto pratica nell'esercito. + +[BJM2_2G:BANKJ2] +Lo sparo alla mosca è così popolare nell'esercito? Per fortuna non pago le tasse. + +[BJM2_2H:BANKJ2] +Stai cercando di fare lo spiritoso? + +[BJM2_2I:BANKJ2] +Ah ah ah ah ah! + +[BJM2_2J:BANKJ2] +Spariamo. + +[BJM2_1:BANKJ2] +~g~Raggiungi Ammu-Nation in Downtown e parla con Phil Cassidy. + +[BJM2_4:BANKJ2] +PUNTEGGIO PRIMO TURNO: ~1~ + +[BJM2_6:BANKJ2] +PUNTEGGIO SECONDO TURNO: ~1~ + +[BJM2_7:BANKJ2] +PUNTEGGIO TOTALE: ~1~ + +[BJM2_9:BANKJ2] +~G~Raggiungi il punto di partenza del secondo turno. + +[BJM2_11:BANKJ2] +~r~Phil è morto! + +[BJM2_12:BANKJ2] +~r~Uno dei tiratori è morto! + +[BJM2_14:BANKJ2] +~g~Raggiungi la prossima area! + +[BJM2_17:BANKJ2] +~g~Vai a parlare con Phil. + +[BJM2_24:BANKJ2] +~g~Il bersaglio più vicino vale un punto. + +[BJM2_25:BANKJ2] +~g~Il bersaglio centrale vale due punti. + +[BJM2_27:BANKJ2] +~g~In questo turno, tutti i bersagli valgono un punto. + +[BNK2_4:BANKJ2] +Hoooeee! + +[BNK2_5:BANKJ2] +Non riusciresti a colpire neanche un elefante! + +[BNK2_7:BANKJ2] +Fammi un favore, aiutami a mettere a segno un colpo. + +[BNK2_8:BANKJ2] +Figliolo, da come hai sparato, se mi chiedessi di sposarti direi di sì. + +[BNK2_9A:BANKJ2] +Figliolo, ficcati la tua parlata e le tue grandi idee là dove non batte il sole. Non sai sparare per niente. + +[BNK2_9B:BANKJ2] +Non sai sparare per niente. + +[BJM2_28:BANKJ2] +PUNTEGGIO TERZO TURNO: ~1~ + +[BJM2_26:BANKJ2] +~g~Il bersaglio più lontano vale tre punti. + +[BNK2_1:BANKJ2] +MUNIZIONI VERE + +[RANGE_1:BANKJ2] +PUNTEGGIO PER COLPO: ~1~ + +[BJM2_2:BANKJ2] +~g~Per interrompere il turno, premi il ~h~~k~~PED_JUMPING~~g~. + +[BJM2_N:BANKJ2] +Rilassati. + +{=================================== MISSION TABLE BANKJ3 ===================================} + +[BJM3_A:BANKJ3] +Le cose stanno iniziando a prendere forma. + +[BJM3_B:BANKJ3] +Qual è il piano Tommy? Que pasa, amigo? + +[BJM3_C:BANKJ3] +La tua parte consiste nel continuare a fare l'idiota. Comunque, adesso abbiamo bisogno di un pilota. + +[BJM3_D:BANKJ3] +Tommy, lo farò io. Io so guidare bene. + +[BJM3_E:BANKJ3] +Ti serve Hilary, capo. Di certo non un sapientone avvocato del cazzo. + +[BJM3_F:BANKJ3] +Hilary è la persona giusta. Non troverai nessuno capace di guidare così veloce. Gli posso fare una chiamata. + +[BJM3_G:BANKJ3] +Ehi Hil, sono Phil. Come butta? No, lascia stare, ne parliamo un'altra volta. Mi faresti un favore? + +[BJM3_H:BANKJ3] +Ho qui un tipo del nord... No, no, non credo abbia fatto il servizio militare, ma ha bisogno di un guidatore. + +[BJM3_I:BANKJ3] +Per un po' di azione. OK, capisco. + +[BJM3_J:BANKJ3] +Che cosa ha detto? + +[BJM3_K:BANKJ3] +Beh, ci sta, nessun problema. Beh, un piccolo problema c'è: ha subito uno shock da abbandono. + +[BJM3_L:BANKJ3] +Sembra non voglia lavorare con nessuno che non sia capace di batterlo. Qualcosa che ha a che fare con la sua vecchia. + +[BJM3_M:BANKJ3] +Comunque, vuole prima sfidarti in una corsa, ha detto che ci aspettava fuori... + +[BJM3_2A:BANKJ3] +Sei Tommy? Beh, certo che sei Tommy, cioè... + +[BJM3_2B:BANKJ3] +Per quale altro motivo qualcuno vorrebbe parlare con me? + +[BJM3_2C:BANKJ3] +OK, vedila in questo modo... + +[BJM3_2D:BANKJ3] +Io guiderò SE, e solo SE, tu sai guidare come si deve. + +[BJM3_2E:BANKJ3] +Lasciami solo... e non te lo perdonerò mai. + +[BJM3_2:BANKJ3] +~r~Hilary è morto! + +[BJM3_4:BANKJ3] +~g~Hai bisogno di una vettura per partecipare! + +[BNK3_1:BANKJ3] +OK. Guiderò per te, ma per favore trattami male! + +[BNK3_3A:BANKJ3] +Corsa illegale in corso presso Vice Point. + +[BNK3_3B:BANKJ3] +A tutte le pattuglie. + +[BNK3_3C:BANKJ3] +Queste corse sono illegali e vietate! + +{=================================== MISSION TABLE BANKJ4 ===================================} + +[BNK4_A:BANKJ4] +~w~Come potete vedere, signori, questi saranno i soldi più facili della storia. + +[BNK4_B:BANKJ4] +~w~Tommy, seriamente, dovresti pensare di fare l'avvocato. + +[BNK4_C:BANKJ4] +~w~Ma che cosa ti fumi, amico? Non è per niente un piano semplice! + +[BNK4_D:BANKJ4] +~w~Beh, chi ha bisogno di un piano semplice? + +[BNK4_E:BANKJ4] +~w~Prendi il comunismo, beh, quello era un piano semplice. Non ha fatto così bene alla Russia, no? + +[BNK4_F:BANKJ4] +~w~Rilassatevi, OK? Con una squadra come la nostra, non ci saranno problemi. + +[BNK4_G:BANKJ4] +~w~Abbiamo Cam per la cassaforte. Phil? Noi ci occuperemo della sorveglianza, mentre Hilary guiderà l'auto per la fuga. + +[BNK4_H:BANKJ4] +~w~Uh, eh eh, non ti stai dimenticando qualcuno? Qualcuno che ti ha costantemente aiutato in questa città? Qualcuno...? + +[BNK4_I:BANKJ4] +~w~Ken... Ken, ma certo. Il nostro Ken si occuperà di lavare il denaro sporco e di tenere le birre al fresco. + +[BNK4_J:BANKJ4] +~w~Non ho ancora capito cosa ci sto a fare io qua. + +[BNK4_K:BANKJ4] +~w~Beh, è semplice: non hai mai visto un film d'azione? + +[BNK4_L:BANKJ4] +~w~Entriamo nella banca, sventoliamo in giro l'arma e usciamo molto ricchi. + +[P_DEAD:BANKJ4] +~r~Phil è morto! + +[C_DEAD:BANKJ4] +~r~Cam è morto! + +[H_DEAD:BANKJ4] +~r~Hilary è morto! + +[P_HIND:BANKJ4] +~r~Hai perso Phil! + +[C_HIND:BANKJ4] +~r~Cam è restato indietro! + +[H_HIND:BANKJ4] +~r~Hai abbandonato Hilary! + +[GETCAR:BANKJ4] +~g~Entra nell'auto e preparati alla rapina! + +[TRASHED:BANKJ4] +~r~HAI DISTRUTTO L'AUTO PER LA FUGA!!! + +[BNK4_1:BANKJ4] +Guido io. + +[BNK4_2:BANKJ4] +Ottimo. Un passeggero. Aspetta che lo dica al resto del gruppo. + +[BNK4_3A:BANKJ4] +Ehi, fai attenzione alla macchina, Tommy! + +[BNK4_3B:BANKJ4] +Tommy, Hilary occupa troppo spazio! + +[BNK4_3C:BANKJ4] +Non è vero! + +[BNK4_3D:BANKJ4] +Invece sì! + +[BNK4_3E:BANKJ4] +Ehi, state entrambi zitti o ve ne andate a piedi. + +[BNK4_3F:BANKJ4] +Sì Hilary. + +[BNK4_3I:BANKJ4] +Perdio, Phil, smettila di sventolare quell'affare in giro! + +[BNK4_3J:BANKJ4] +Sì, finirai col cavare un occhio a qualcuno! + +[BNK4_3M:BANKJ4] +La mia bambina! È a pezzi! + +[BNK4_3O:BANKJ4] +Sei accecato dall'illusione di permanenza. + +[BNK4_3P:BANKJ4] +Cosa? + +[BNK4_3Q:BANKJ4] +Pensi che tutto duri per sempre. + +[BNK4_3R:BANKJ4] +La giovinezza, le persone care, la pizza... + +[BNK4_3S:BANKJ4] +Ma tutto passa e finisce e tu lo devi accettare. + +[BNK4_3T:BANKJ4] +Ehi, hai ragione. Grazie Cam. + +[BNK4_3U:BANKJ4] +È un piacere. + +[BNK4_3V:BANKJ4] +Ehi Tommy, perché ci siamo fermati? + +[BNK4_4A:BANKJ4] +~w~Hilary, continua a guidare attorno al quartiere. + +[BNK4_5:BANKJ4] +~w~OK Tommy, OK. + +[BNK4_6:BANKJ4] +~w~QUESTA È UNA RAPINA! + +[BNK4_7:BANKJ4] +~w~NESSUNO SI MUOVA! + +[BNK4_8:BANKJ4] +~w~TUTTI CONTRO QUEL MURO! + +[BNK4_9:BANKJ4] +Phil, mantieni il controllo! + +[BNK4_10:BANKJ4] +Ricevuto capo! + +[BNK4_11:BANKJ4] +Vieni Cam, la cassaforte è di sopra... + +[BK4_12A:BANKJ4] +Maledizione! È una Flange 9000! + +[BK4_12B:BANKJ4] +Potrei impiegare ore ad aprirla, + +[BK4_12C:BANKJ4] +o cinque minuti se trovi l'amministratore. + +[BNK4_13:BANKJ4] +Vado a scoprire dove si è rintanato. + +[BK4_14A:BANKJ4] +Phil, va tutto bene? + +[BNK4_15:BANKJ4] +Certo. Tutto liscio come l'olio. + +[BNK4_16:BANKJ4] +Tu, tu vieni con me! + +[BNK4_17:BANKJ4] +OK, OK! Ti prego, non sparare! + +[BNK4_18:BANKJ4] +HO DETTO NESSUNO SI MUOVA! + +[BK4_19A:BANKJ4] +L'apertura è temporizzata, + +[BK4_19B:BANKJ4] +potete anche rinunciarci! + +[BK4_20A:BANKJ4] +Diamine, posso aggirare l'apertura a tempo, + +[BK4_20B:BANKJ4] +ma poi avremo bisogno del tuo codice per spalancarla! + +[BNK4_21:BANKJ4] +Sta qua. Se fai uno scherzo diventi cibo per pesci. Chiaro? + +[BK4_24A:BANKJ4] +Vado a controllare Phil, torno subito. + +[BK4_24B:BANKJ4] +Te l'avevo detto di non toccare l'allarme! + +[BNK4_25:BANKJ4] +La squadra SWAT sarà qui fra pochi minuti! + +[BNK4_27:BANKJ4] +Un po' di aiuto non farebbe male, Tommy. + +[BNK4_28:BANKJ4] +SWAT di Vice City! Siete completamente circondati! + +[BNK4_29:BANKJ4] +Circondati? AH AH AH Aaaaaaaaaah! + +[BNK4_30:BANKJ4] +Si stanno cagando addosso, corrotti bastardi! + +[BK4_31A:BANKJ4] +Tommy! La cassaforte è aperta! + +[BK4_34A:BANKJ4] +OK, abbiamo i fondi pensione della SWAT. Andiamocene da qui! + +[BK4_34B:BANKJ4] +OK, l'avete voluto voi! Il tempo è scaduto! + +[BK4_35A:BANKJ4] +Stanno assalendo l'edificio! + +[BK4_35B:BANKJ4] +Al riparo! + +[BNK4_36:BANKJ4] +Dov'è Cam? + +[BNK4_37:BANKJ4] +Non ce l'ha fatta... + +[BNK4_38:BANKJ4] +Questo è l'ultimo. VIA! VIA! VIA! + +[BNK_39:BANKJ4] +Merda! Dov'è Hilary? + +[BK4_40A:BANKJ4] +Gliela do io l'illusione di permanenza! + +[BNK4_42:BANKJ4] +Ehi, ragazzi! Entrate, vi copro io! + +[BNK4_44:BANKJ4] +Ce l'abbiamo fatta! Siamo ricchi, RICCHI! + +[BNK4_45:BANKJ4] +Peccato che Cam non ce l'ha fatta, era un bravo ragazzo! + +[BNK4_46:BANKJ4] +Sì, comunque... ora ce n'è più per noi! + +[BNK4_47:BANKJ4] +Maledettamente vero! YEEEEHAAAH! + +[BNK4_48:BANKJ4] +Tommy, vuoi un massaggio? + +[BNK4_49:BANKJ4] +Ciao Mercedes! Sì, effettivamente sono un po' teso... + +[BNK450A:BANKJ4] +Sai cosa ti dico Tommy? Sai cosa ti dico? I membri corrotti della SWAT faranno meglio a fare attenzione finché Kent Paul è in città. + +[BNK450B:BANKJ4] +Eddai, dammene una fetta più grande! Amico, dai! Mi devo comprare dei vestiti nuovi! + +[BNK4_94:BANKJ4] +~w~OK, ragazzi. Procediamo come pianificato. + +[BM_DEAD:BANKJ4] +~r~Hai bisogno dell'amministratore vivo! + +[ASSET_A:BANKJ4] +RAPINA ALLA BANCA COMPLETATA! + +[ASSET_B:BANKJ4] +~g~Il Malibu d'ora in poi genererà introiti per un massimo di ~1~$. Ricordati di recuperarli regolarmente. + +[IDIOT:BANKJ4] +~r~Vai in giro vestito come un pazzo e attiri l'attenzione! Sei un IDIOTA! + +{=================================== MISSION TABLE BARON1 ===================================} + +[COK1_A:BARON1] +Forza, dai, forza! Yeh yeh aaaah... + +[COK1_B:BARON1] +Stupido cavallo! Ti taglierò la testa! + +[COK1_C:BARON1] +Chi è questo stronzo? + +[COK1_D:BARON1] +Tommy Vercetti, ti ricorderai di me. + +[COK1_E:BARON1] +Scusami, sono un po' ansioso. Mai fidarsi di un fottuto cavallo! + +[COK1_F:BARON1] +Hai fatto un bel lavoro: adesso lavori per me. + +[COK1_H:BARON1] +Come ti ho detto, amigo, lavori per me. Adesso sta zitto. Un qualche giuda mi ha tradito. + +[COK1_I:BARON1] +Pensava non sapessi quanti soldi stessi facendo, ma rubarmi il 3% è come rubarmi il 100%. + +[COK1_J:BARON1] +Nessuno fa questo a me. NESSUNO!!! + +[COK1_K:BARON1] +Seguilo nel suo appartamento e scopri dove va! Successivamente, lo elimineremo noi. + +[COK1_1:BARON1] +Ommerda! + +[COK1_2:BARON1] +Troppo lento, nonnino! + +[COK1_4:BARON1] +Perdente. + +[COK1_5:BARON1] +Ti conviene continuare a correre, stronzo! + +[COK1_8:BARON1] +~g~Svelto! Recupera un mezzo e inseguilo! + +[COK1_9:BARON1] +~r~Dovevi inseguirlo, non ucciderlo! + +[COK1_10:BARON1] +~r~Entra nella casa del ladro e scopri dove ha nascosto il denaro. + +[COK1_11:BARON1] +~g~Dai un'occhiata da questa finestra. + +[COK1_7:BARON1] +~g~È scappato sul tetto: stagli dietro ma non ucciderlo! + +[COK1_G:BARON1] +Lavoro per soldi. + +{=================================== MISSION TABLE BARON2 ===================================} + +[COK2_A:BARON2] +Ma che fottuti incompetenti non siete? + +[COK2_B:BARON2] +IDIOTI! IDIOTI! IDIOTI! IDIOTI! + +[COK2_C:BARON2] +Tommy, + +[COK2_D:BARON2] +questi idioti... cercano sempre di fregarti. + +[COK2_E:BARON2] +È questo il problema in questo business. + +[COK2_F:BARON2] +Che cosa credi di fare? Aaaaah! + +[COK2_G:BARON2] +Questi stronzi hanno fallito miseramente. + +[COK2_H:BARON2] +Ben presto anche i papà e le mamme penseranno di poter spacciare coca in Vice City. + +[COK2_I:BARON2] +Chi sarà il prossimo adesso? La fottuta mafia? + +[COK2_J:BARON2] +La base della loro banda è una fortezza al piano terra, + +[COK2_K:BARON2] +per cui Quentin... Quentin! QUENTIN! + +[COK2_L:BARON2] +Lui ti porterà in volo sull'area! + +[COK2_M:BARON2] +Sradicateli! + +[COK2_N:BARON2] +Che cosa credi di fare? + +[COK2_O:BARON2] +Che cosa fai qua? + +[COK2_P:BARON2] +Ehi, ho chiesto in giro ed è ovvio + +[COK2_Q:BARON2] +che Diaz ha fatto saltare l'accordo e ha freddato mio fratello. + +[COK2_R:BARON2] +E ucciderà anche te! + +[COK2_S:BARON2] +Io posso far fuori Diaz! + +[COK2_T:BARON2] +No, ascoltami! Io mi occuperò di Diaz: + +[COK2_U:BARON2] +sta cominciando a fidarsi di me. + +[COK2_1:BARON2] +Ho una domanda, però: perché 'Quentin'? + +[COK2_2:BARON2] +Non lo so, mi è sempre piaciuto... Quentin Vance... + +[COK2_3:BARON2] +Vance? Tu sei Lance Vance? + +[COK2_4:BARON2] +Ehi, ne ho avuto abbastanza a scuola! + +[COK2_5:BARON2] +Hai mai provato a sparare con quelli da un elicottero? + +[COK2_8:BARON2] +Dove ci stiamo dirigendo? + +[COK2_9:BARON2] +Prawn Island. + +[COK2_13:BARON2] +Lance Vance. Brutto bastardo. + +[COK2_14:BARON2] +OK, siamo quasi arrivati. + +[COK2_15:BARON2] +Faremo un paio di passaggi. + +[COK2_16:BARON2] +Cerca di colpire il maggior numero di nemici possibile. + +[COK2_17:BARON2] +Poi ti scarico e dovrai cavartela da solo. + +[COK2_20:BARON2] +Merda! Siamo in una zona di guerra! Abbatti un po' di gente! + +[COK2_21:BARON2] +Stiamo sotto tiro, amico! + +[COK2_22:BARON2] +Questo affare è maledettamente costoso da riparare! Falli fuori! + +[COK2_23:BARON2] +OK, adesso dovrai cavartela da solo... Buona fortuna, fratello! + +[COK2_24:BARON2] +Condizioni elicottero: + +[COK2_25:BARON2] +~g~Recupera i soldi sul tetto. + +[COK2_27:BARON2] +Sei nel MIO territorio, stronzo! + +[COK2_28:BARON2] +Stai per affondare! + +[COK2_6:BARON2] +No. Farò un po' di pratica lungo la strada. + +[OPEN_B:BARON2] +Il blocco stradale per la terraferma è stato rimosso. + +[COK2_V:BARON2] +sta cominciando a fidarsi di me. + +{=================================== MISSION TABLE BARON3 ===================================} + +[COK3_A:BARON3] +Scommetto che adesso non vi divertite più! + +[COK3_B:BARON3] +Ah ah ah ah! + +[COK3_C:BARON3] +Woh! Fai attenzione a dove punti quel coso. + +[COK3_D:BARON3] +Basta con merda di piccione sulla MIA macchina, vero Tommy? + +[COK3_E:BARON3] +Direi di sì. + +[COK3_F:BARON3] +Hai proprio ragione. Adesso ascolta: + +[COK3_G:BARON3] +sai chi possiede la barca più veloce della costa est? + +[COK3_H:BARON3] +Non così su due piedi. + +[COK3_I:BARON3] +IO. E voglio che la situazione non cambi. + +[COK3_J:BARON3] +Ogni contrabbandiere da qui a Caracas ha un solo sogno: un'imbarcazione più veloce. + +[COK3_K:BARON3] +Si mormora che il cantiere Hull-o-Caust ha appena finito di costruirne una + +[COK3_L:BARON3] +per una testa di cazzo del Costa Rica. + +[COK3_M:BARON3] +E Tommy... IO VOGLIO QUELLA BARCA!!! + +[COK3_N:BARON3] +Credo che siano tornati i tuoi piccioni. + +[COK3_O:BARON3] +Ah! Pensavo di averti preso. Da dove salti fuori? + +[COK3_P:BARON3] +Piccioni! Boom! Ah ah ah! + +[COK3_5:BARON3] +~g~Trova l'interruttore per abbassare l'imbarcazione. + +[COK3_6:BARON3] +~g~Porta la barca alla villa di Diaz. + +[COK3_7:BARON3] +~r~Hai distrutto la barca! + +[COK3_8:BARON3] +~g~Raggiungi il cantiere al porto e ruba la barca più veloce. + +[COK3_9:BARON3] +~g~Adesso entra nella barca. + +{=================================== MISSION TABLE BARON4 ===================================} + +[COK4_A:BARON4] +Sputa fuori! CATORCIO DI PLASTICA! + +[COK4_B:BARON4] +Come osi farmi questo? + +[COK4_C:BARON4] +Chi credi di essere, brutto pezzo di merdosa plastica! Aaarrgh! + +[COK4_D:BARON4] +VAFFANCULO! + +[COK4_E:BARON4] +Se hai distrutto il mio film favorito di El Burro ti spacco! + +[COK4_F:BARON4] +Che altro posso fare? + +[COK4_G:BARON4] +Forse non è collegato... + +[COK4_H:BARON4] +Cosa? + +[COK4_I:BARON4] +Merda... non importa, ne posso comprare a centinaia. + +[COK4_J:BARON4] +Adesso Tommy, + +[COK4_K:BARON4] +ogni mese un freelance naviga fino a Vice City e ormeggia il suo yacht. + +[COK4_L:BARON4] +Vende il suo carico alla prima imbarcazione. + +[COK4_M:BARON4] +Voglio che tu prenda la barca più veloce, + +[COK4_N:BARON4] +batta tutti gli altri stronzi + +[COK4_O:BARON4] +e riporti qui il carico, OK? + +[COK4_P:BARON4] +Fammi indovinare, pensavi mi avrebbe fatto comodo un angelo custode? + +[COK4_Q:BARON4] +Vorrei solo che mi lasciassi venire, amico mio. + +[COK4_R:BARON4] +Puoi continuare con queste stronzate alla rambo, + +[COK4_S:BARON4] +ma sono sicuro che un giorno o l'altro ti salverò il culo. + +[COK4_T:BARON4] +e allora vorrai baciarmi! + +[COK4_U:BARON4] +Sei pazzo. + +[COK4_V:BARON4] +Ah ah ah! + +[COK4_1:BARON4] +Allora Tommy, sappiamo che è stato Diaz a far saltare il nostro affare... + +[COK4_3:BARON4] +Allora per quale motivo continuiamo a lavorare per lui? + +[COK4_4:BARON4] +Più cose scopriamo adesso, meno dovremo imparare quando ci impossesseremo di questa città! + +[COK4_5:BARON4] +Mi piace il tuo stile, amico. Molto fico. + +[COK4_12:BARON4] +Attento, stanno arrivando da tutte le parti! + +[COK4_13:BARON4] +Preso! Torniamo da Diaz il più velocemente possibile! + +[COK4_14:BARON4] +Ne vuoi un po'? + +[COK4_15:BARON4] +Salutami i pesci! + +[COK4_16:BARON4] +Prendi questo! E questo! + +[COK4_19:BARON4] +Altri guai in arrivo! + +[COK4_20:BARON4] +Ci stanno sparando da quel molo! + +[COK4_24:BARON4] +Bel colpo, amico. Sei un vero pazzo di prima classe. + +[COK4_25:BARON4] +Beh, grazie. + +[COK4_26:BARON4] +Ci vediamo, Tommy. + +[COK4_27:BARON4] +OK, Mr. Lance Vance Dance. + +[COK4_28:BARON4] +~g~Raggiungi lo yacht prima delle altre imbarcazioni! + +[COK4_31:BARON4] +~g~Raggiungi la barca più veloce attraccata al molo! + +[COK4_32:BARON4] +~r~Troppo lento! + +[COK4_33:BARON4] +~r~Hai distrutto la barca! + +[COK4_34:BARON4] +~g~Affonda quelle imbarcazioni! + +[COK4_35:BARON4] +Condizioni barca: + +{=================================== MISSION TABLE BARON5 ===================================} + +[PROP_A:BARON5] +PROPRIETÀ ACQUISITA! + +[COK4_30:BARON5] +~r~Lance è morto! + +[ASS1_A:BARON5] +Ho messo un po' di cannoni nel bagagliaio. + +[ASS1_B:BARON5] +Accidenti! Dove hai trovato tutta questa roba? + +[ASS1_C:BARON5] +La tenevo da parte per una giornata di pioggia. + +[ASS1_D:BARON5] +Ti piace? + +[ASS1_E:BARON5] +Sì, mi piace. + +[ASS1_F:BARON5] +Brutto idiota... + +[ASS1_G:BARON5] +La mia bellissima casa... + +[ASS1_H:BARON5] +Guarda cosa hai combinato! + +[ASS1_I:BARON5] +Questo è per mio fratello! + +[ASS1_J:BARON5] +Mi fidavo di te, Tommy! + +[ASS1_K:BARON5] +Ti avrei fatto diventare... + +[ASS1_L:BARON5] +Buona notte, Mr. Diaz. + +[ASS1_1:BARON5] +Questo posto sarà zeppo di stronzi... fai attenzione... + +[ASS1_2:BARON5] +Non preoccuparti, Tommy, ti copro io. + +[ASS1_4:BARON5] +Diaz deve essere qui dentro! + +[ASS1_13:BARON5] +DIAZ? Sono venuto a prendermi il tuo business! + +[ASS1_14:BARON5] +TOMMY! Mi hai tradito... Idiota! Ti ucciderò fra un attimo! + +[ASS1_16:BARON5] +~g~Uccidi Diaz! + +[BUD1:BARON5] +Lance + +[ASS1_18:BARON5] +~g~La porta è chiusa: trova un percorso alternativo. + +[ASS1_19:BARON5] +Di qua! + +[ASS1_20:BARON5] +Tommy, ho un problema con Quentin, non con te! + +{=================================== MISSION TABLE BIKE1 ===================================} + +[BM1_A:BIKE1] +Dov'è Baker? + +[BM1_B:BIKE1] +Sto cercando Big Mitch Baker... + +[BM1_C:BIKE1] +Chi lo cerca? + +[BM1_D:BIKE1] +Tommy Vercetti. + +[BM1_E:BIKE1] +Vercetti. + +[BM1_F:BIKE1] +Non mi sembri della polizia, il che ti ha fatto guadagnare un minuto. + +[BM1_G:BIKE1] +Ti conviene parlare in fretta. + +[BM1_H:BIKE1] +Kent Paul mi ha detto che saresti stato interessato a occuparti della sorveglianza di uno spettacolo che sta allestendo. + +[BM1_I:BIKE1] +Kent Paul? Sheesh! Non mi stupisce abbia mandato te. + +[BM1_J:BIKE1] +L'ultima volta che è stato qui è uscito dalla finestra con il vestitino che gli hanno dato alla nascita. + +[BM1_K:BIKE1] +Sei interessato o no? + +[BM1_L:BIKE1] +Facciamo solo favori tra compagni. + +[BM1_M:BIKE1] +Come faccio a diventarlo? + +[BM1_N:BIKE1] +Non è uno circolo sportivo, amico. Sai guidare una moto? + +[BM1_O:BIKE1] +Sai sederti su uno sgabello e bere birra? + +[BM1_P:BIKE1] +Cougar, Zeppelin, scoprite se questa ragazzina sa portare una moto... + +[BM1_2:BIKE1] +~g~Hai bisogno di una Freeway o di un'Angel per gareggiare! + +[BM1_3:BIKE1] +~r~I partecipanti sono stati attaccati! + +[BIKE1_1:BIKE1] +Bene, bei vestiti. Vediamo cosa sai fare. + +[BM1_1:BIKE1] +~g~Recupera una Freeway o un'Angel e posizionati sulla griglia di partenza. + +{=================================== MISSION TABLE BIKE2 ===================================} + +[BM2_1:BIKE2] +CAOS: + +[BM2_A:BIKE2] +Ah ah ah, ti ho preso di nuovo! + +[BM2_B:BIKE2] +Ehi, Vercetti. + +[BM2_C:BIKE2] +Cougar ha detto che sai portare la moto come si deve. + +[BM2_D:BIKE2] +Già, quanti altri giretti devo ancora fare? + +[BM2_E:BIKE2] +Sono un uomo molto impegnato. + +[BM2_F:BIKE2] +Se serve una bella scazzottata per mettere le cose a posto, dillo chiaramente. + +[BM2_G:BIKE2] +Essere uno di noi non significa solo azzuffarsi. Si tratta di far parte di una famiglia. + +[BM2_H:BIKE2] +Sì, sono già stato parte di una famiglia. Non ha funzionato. + +[BM2_I:BIKE2] +Sì, certo, ma questa famiglia si occupa dei suoi membri. + +[BM2_J:BIKE2] +Non chiediamo a uno di fare il lavoro sporco per poi lasciarlo marcire quindici anni ai lavori forzati. + +[BM2_K:BIKE2] +Sì, esatto. Ho fatto la mia parte. + +[BM2_L:BIKE2] +Questa è la più grande famiglia di disadattati, reietti e derelitti. + +[BM2_M:BIKE2] +Merda, alcuni di noi sono stati persino traditi dalla propria nazione. + +[BM2_N:BIKE2] +Sono stato imprigionato durante il Vietnam. Brutta storia. + +[BM2_O:BIKE2] +Ed è per questo che ti chiedo di far bollire la pentola. + +[BM2_P:BIKE2] +Questa fottuta nazione ha bisogno di un bel calcio nel culo, e saremo noi a darglielo. + +[BM2_Q:BIKE2] +Per cui esci, prendi una moto e mostra a questa città quanto siamo arrabbiati! + +[BM2_R:BIKE2] +Va bene. + +[BM2_2:BIKE2] +~g~Devi riempire l'indicatore del caos entro il tempo stabilito per mostrare quanto sei arrabbiato! + +[BM2_3:BIKE2] +~g~Questo suono indica che hai riempito parte dell'indicatore, continua così. + +[BM2_4:BIKE2] +~r~Non sei riuscito a riempire l'indicatore del caos in tempo! + +{=================================== MISSION TABLE BIKE3 ===================================} + +[BM3_A:BIKE3] +Ehi, ciao Mitch. + +[BM3_B:BIKE3] +Accidenti Vercetti, hai davvero le palle. + +[BM3_C:BIKE3] +Adesso voglio vedere se sei pronto a combattere per la tua famiglia. + +[BM3_D:BIKE3] +Una gang locale ha fatto l'errore di rubare il mio mezzo... + +[BM3_E:BIKE3] +probabilmente come prova di coraggio o qualcosa del genere. + +[BM3_F:BIKE3] +Io e i miei uomini stavamo per andare a dar loro una lezione sul rispetto. + +[BM3_G:BIKE3] +Comunque, + +[BM3_H:BIKE3] +ho pensato che sarebbe stata un'ottima occasione per la tua iniziazione. + +[BM3_I:BIKE3] +Riportami indietro la mia moto e potrai dire a Paul che avrà la sua sorveglianza. + +[BM3_2:BIKE3] +~r~Dovevi riportare indietro la moto, non distruggerla! + +[BM3_3:BIKE3] +~g~Riporta la moto al bar! + +[BM3_4:BIKE3] +~g~Sali sulla moto! + +[INTRUDE:BIKE3] +~g~Ti hanno visto! + +[BM3_6:BIKE3] +~g~Si sono rintanati dietro Ammu-Nation in Downtown. + +[BM3_7:BIKE3] +~g~Avrai bisogno di una moto più veloce per accedere al tetto. + +[BM3_8:BIKE3] +~g~Usa la moto per saltare da queste scale fino al tetto nella parte più lontana della strada. + +[BM3_1:BIKE3] +~g~Una gang locale ha rubato la Angel di Mitch Baker: riportala indietro! + +[BM3_9:BIKE3] +~g~Recupera l'Angel di Mitch Baker e vattene! + +{=================================== MISSION TABLE BMX_1 ===================================} + +[BMX_REC:BMX_1] +~g~Nuovo record stabilito: ~1~!! + +[GETBIK2:BMX_1] +Hai ~1~ secondi per salire su una Dirt Bike! + +{=================================== MISSION TABLE BOATBUY ===================================} + +[DRUG_1:BOATBUY] +Salve? Saaalve? Salve? + +[DRUG_2:BOATBUY] +Tranquillo amico, sono qua. + +[DRUG_3:BOATBUY] +Ehi, damerino! Dimmi, sei il nuovo proprietario? + +[DRUG_4:BOATBUY] +Sì. Qual è la barca più veloce? + +[DRUG_5:BOATBUY] +È già in acqua, amico, + +[DRUG_6:BOATBUY] +pensavo che la volessi provare. + +[DRUG_7:BOATBUY] +Amico, ha già sopra un bel motore da 300 cavalli... + +[DRUG_8:BOATBUY] +...e la struttura in vetroresina: vola davvero sull'acqua! + +[DRUG_9:BOATBUY] +Può andare da zero a sessanta in quattro secondi spaccati... + +[DRUG_10:BOATBUY] +e può tenere venti pacchi della migliore erba giamaicana nella stiva! + +[DRUG_11:BOATBUY] +Dacci dentro, amico, è pronta a volare! + +[DRUG_12:BOATBUY] +Yo yo, uh, ehi amico, hai da accendere? + +[DRUG_13:BOATBUY] +Amico? + +{=================================== MISSION TABLE CAP_1 ===================================} + +[CAP1_B1:CAP_1] +~g~La Mafia sta tassando il tuo business. Trovali e falli fuori. + +[CAP1_B2:CAP_1] +~g~La Mafia ha tassato il tuo cantiere navale! + +[CAP1_B3:CAP_1] +~g~La Mafia ha tassato la tua fabbrica di gelato! + +[CAP1_B4:CAP_1] +~g~La Mafia ha tassato la concessionaria! + +[CAP1_B5:CAP_1] +~g~La Mafia ha tassato la tua compagnia di taxi! + +[CAP_01:CAP_1] +OK, qual è l'emergenza? + +[CAP_02:CAP_1] +CHI? + +[CAP_03:CAP_1] +Tommy... dei maledetti mafiosi... dicevano di essere venuti a prendere la loro fetta... + +[CAP_04:CAP_1] +...dicevano che erano soldi di Mr. Forello... mi sento di merda. + +[CAP_05:CAP_1] +Forelli? SONNY Forelli? + +[CAP_06:CAP_1] +Sì, quel nome... credo... hanno davvero insistito... + +[CAP_07:CAP_1] +Non preoccuparti amico, non sono arrabbiato con te. + +[CAP_08:CAP_1] +Portalo all'ospedale. + +[CAP_09:CAP_1] +Tommy... fai a quel tipo un nuovo buco da cui cagare... + +[CAP_10:CAP_1] +Ho intenzione di fargliene due. + +[CAP1_2:CAP_1] +Conosci le regole, Vercetti! + +[CAP1_3:CAP_1] +Mr. Forelli manda i suoi saluti! + +[CAP1_4:CAP_1] +È il macellaio di Harwood! + +[CAP1_5:CAP_1] +Dì a Sonny di stare alla larga! + +[CAP1_6:CAP_1] +Vice City è MIA adesso, NON sua. + +[CAP1_7:CAP_1] +Pensi di potermi tagliar fuori, Vercetti? + +[CAP1_8:CAP_1] +Ti inseguiremo fino a farti fuori, Vercetti. + +[CAP1_9:CAP_1] +Non hai nessuna possibilità, brutto cazzone psicopatico. + +[CAP1_10:CAP_1] +Ti ucciderò, Vercetti. + +[CAP1_11:CAP_1] +Sei sempre stato uno stronzo. + +[CAP1_12:CAP_1] +Ti ammazzo, Vercetti. + +[CAP1_B6:CAP_1] +~g~Hai trovato l'esattore: uccidilo! + +[CAP1_B7:CAP_1] +~g~Hai perso l'esattore. + +[CAP1_B8:CAP_1] +~r~L'esattore ha tassato tutte le tue proprietà. + +[CAP1_B9:CAP_1] +~g~La Mafia ha tassato il Malibu! + +[CAP1_B0:CAP_1] +~g~La Mafia ha tassato lo studio cinematografico! + +[CAP1_C2:CAP_1] +~g~La Mafia è arrivata al cantiere navale! + +[CAP1_C3:CAP_1] +~g~La Mafia è arrivata alla fabbrica di gelato! + +[CAP1_C4:CAP_1] +~g~La Mafia è arrivata al concessionario! + +[CAP1_C5:CAP_1] +~g~La Mafia è arrivata alla compagnia di taxi! + +[CAP1_C9:CAP_1] +~g~La Mafia è arrivata al Malibu! + +[CAP1_C0:CAP_1] +~g~La Mafia è arrivata allo studio cinematografico! + +[CAP1_D2:CAP_1] +~g~La Mafia sta abbandonando il cantiere navale! + +[CAP1_D3:CAP_1] +~g~La Mafia sta abbandonando la fabbrica di gelato! + +[CAP1_D4:CAP_1] +~g~La Mafia sta abbandonando il concessionario! + +[CAP1_D5:CAP_1] +~g~La Mafia sta abbandonando la compagnia di taxi! + +[CAP1_D9:CAP_1] +~g~La Mafia sta abbandonando il Malibu! + +[CAP1_D0:CAP_1] +~g~La Mafia sta abbandonando lo studio cinematografico! + +[CAP1B10:CAP_1] +Hai beccato gli esattori. Altri stanno arrivando. + +{=================================== MISSION TABLE CARBUY ===================================} + +[CAR1_1:CARBUY] +B.J. Smith. E tu devi essere Mr. Vercetti. + +[CAR1_2:CARBUY] +Ti andrebbe un giro? + +[CAR1_3:CARBUY] +Perché no. + +[CAR1_4:CARBUY] +Bene, mi dispiace un po' vendere il concessionario. + +[CAR1_5:CARBUY] +È stato il mio primo investimento da quando mi sono messo in proprio. + +[CAR1_6:CARBUY] +Ma adesso è giunta l'ora di andare avanti. + +[CAR1_7:CARBUY] +Lasci la città? + +[CAR1_8:CARBUY] +Non troppo in fretta, spero? + +[CAR1_9:CARBUY] +No. Sto per prepararmi al pensionamento e ai miei futuri investimenti. + +[CAR1_10:CARBUY] +Il mercato non tirava molto, + +[CAR1_11:CARBUY] +e la mia squadra si è preoccupata personalmente di diventare + +[CAR1_12:CARBUY] +più creativa nella produzione di contanti. + +[CAR1_13:CARBUY] +Logicamente, potrei smantellare l'organizzazione prima di vendere. + +[CAR1_14:CARBUY] +Diamine, potrei radere al suolo tutto quanto se volessi. + +[CAR1_15:CARBUY] +Quest'area è in pieno sviluppo. + +[CAR1_16:CARBUY] +Oh, non mi preoccuperei di ciò. + +[CAR1_17:CARBUY] +Il posto mi sembra perfetto. + +[CAR1_18:CARBUY] +Sì, lo è, allora siamo d'accordo? + +{=================================== MISSION TABLE CARPAR1 ===================================} + +[MM_1_A:CARPAR1] +~g~Attraversa ~y~5 punti di controllo ~r~SENZA~g~ abbattere i ~r~CONI~g~! ~g~Puoi attraversarli in ~y~QUALSIASI ORDINE~g~. + +[CONE_1:CARPAR1] +~r~Hai abbattuto un cono! + +[MM_1_C:CARPAR1] +~y~ATTRAVERSA~g~ un punto di controllo per attivare il TIMER. ~g~Ogni punto di controllo ti fornirà ~y~~1~ SECONDI~g~ extra. + +{=================================== MISSION TABLE COPCAR ===================================} + +[KILLS:COPCAR] +UCCISIONI: + +[C_BREIF:COPCAR] +~g~Sospettato visto in prossimità dell'area ~a~. + +[C_PASS:COPCAR] +MINACCIA ELIMINATA: ~1~$ + +[COPCART:COPCAR] +~g~Hai ~1~ secondi per tornare a un veicolo della polizia prima che la missione abbia termine. + +[C_CANC:COPCAR] +~r~Missione Vigilante annullata! + +[C_TIME:COPCAR] +~r~È scaduto il tuo tempo come tutore della legge! + +[C_COMP1:COPCAR] +Missione Vigilante livello 12 completata: valore massimo dell'armatura aumentato a 150. + +[CLEVEL:COPCAR] +Missione Vigilante livello ~1~ + +{=================================== MISSION TABLE COUNT1 ===================================} + +[CM1_A:COUNT1] +Mr. Vercetti? Ehi, hai comprato la vecchia tipografia? + +[CM1_B:COUNT1] +Sì, il mio vecchio un tempo lavorava nel campo. + +[CM1_C:COUNT1] +Avrei voluto seguire le sue orme, ma... ho deciso di vivere diversamente. + +[CM1_D:COUNT1] +Stai progettando di vendere il vecchio macchinario smontandolo in pezzi? + +[CM1_E:COUNT1] +Ho pensato che potremmo stampare ancora qualcosa... un giornale, una rivista... + +[CM1_F:COUNT1] +Che merda, figliolo, roba di bassa qualità. Ho sempre sognato stampare soldi. Non è poi così difficile. + +[CM1_G:COUNT1] +Sai, l'ho fatto su scala ridotta per anni. + +[CM1_H:COUNT1] +Davvero? + +[CM1_I:COUNT1] +Certo. Ma prima abbiamo bisogno di matrici di buona qualità. + +[CM1_J:COUNT1] +Ma certo! C'è già un'organizzazione di falsificatori in azione in Florida. + +[CM1_K:COUNT1] +Un'organizzazione? + +[CM1_L:COUNT1] +Sì. Beh, ho sentito solo delle voci. + +[CM1_M:COUNT1] +Conosco un tipo che si intende di voci... + +[CM1_N:COUNT1] +Un tempo passavo le serate con lui, mentre pulivo i rulli... + +[CM1_2A:COUNT1] +Accidenti che culo incredibile! + +[CM1_2B:COUNT1] +Va bene, bellezza, sei tu che ci perdi! + +[CM1_2C:COUNT1] +Ehi, ciao amico, come butta? + +[CM1_2D:COUNT1] +Che cosa sai della falsificazione? + +[CM1_2E:COUNT1] +Oh, sto bene Paul, e a te? + +[CM1_2F:COUNT1] +Vieni qua. + +[CM1_2G:COUNT1] +Va bene! Va bene! Va bene! Sei ovviamente un tipo impegnato. + +[CM1_2H:COUNT1] +Tutto ciò che so sul campo è che la Triade fornisce le matrici. + +[CM1_2I:COUNT1] +Hanno una società di spedizioni nel porto, + +[CM1_2J:COUNT1] +il capo di certo saprà quando passeranno nuovamente delle matrici! + +[CM1_2K:COUNT1] +Grazie Paul. + +[CM1_2L:COUNT1] +Qual è il tuo problema, sei un maniaco! + +[CM1_2M:COUNT1] +Dammi un altro drink, in fretta! + +[CM1_3:COUNT1] +~g~Ti hanno visto! + +[CM1_5:COUNT1] +~g~Incontrati con Kent Paul al Malibu Club! + +[CNT1_1:COUNT1] +Chi sei? Oooof! Aaiieee! Non il viso! Non il viso! + +[CM1_1:COUNT1] +~g~Vai alla compagnia marittima Chartered Libertine nel porto. + +[CM1_2:COUNT1] +~g~L'ufficiale delle spedizioni è in possesso delle informazioni che ti servono. + +[CNT1_2:COUNT1] +OK, parlerò, parlerò! + +[CM1_6:COUNT1] +~g~Ritorna con le informazioni alla tipografia! + +{=================================== MISSION TABLE COUNT2 ===================================} + +[CNT2_B1:COUNT2] +Bene, il corriere porterà le matrici al porto oggi stesso. + +[CNT2_B2:COUNT2] +Ho intenzione di intercettare il corriere, recuperare le matrici, far perdere le tracce e tornare qua. + +[CNT2_B3:COUNT2] +Bene. A seconda di quanto bene vadano le cose, + +[CNT2_B4:COUNT2] +potremmo avere cinque minuti per stampare soldi prima che l'associazione dei falsificatori ci trovi, o potremmo avere tutto l'anno. + +[CNT2_B5:COUNT2] +In ogni caso, voglio veder bigliettoni verdi uscire cinque minuti dopo che sarò tornato. Tutto chiaro? + +[CNT2_B6:COUNT2] +Non preoccuparti, Tommy. Saremo pronti. + +[CNT2_B7:COUNT2] +Io e i ragazzi saremo in giro per il quartiere se mai dovessi avere bisogno di una mano con gli inseguitori. + +[CNT2_B8:COUNT2] +Ottimo: siete tutti pronti? Perfetto. Ci vediamo più tardi... + +[CNT2_01:COUNT2] +~g~Il ~r~corriere~g~ con le matrici arriverà fra pochi attimi al ~y~porto~g~ in elicottero. + +[CNT2_02:COUNT2] +~r~Il corriere è scappato con l'elicottero. + +[CNT2_03:COUNT2] +~r~Il corriere è arrivato a destinazione sano e salvo. Sei in ritardo! + +[CNT2_04:COUNT2] +~r~Hai distrutto le matrici nell'esplosione! + +[CNT2_05:COUNT2] +~g~Hai preso le matrici. Riportale alla tipografia. + +[CNT2_06:COUNT2] +~g~Il corriere è morto e ha lasciato cadere le matrici: raccoglile prima che lo faccia qualcun altro. + +[CNT2_07:COUNT2] +~g~Una delle guardie ha raccolto le matrici: non lasciarlo scappare! + +[CNT2_08:COUNT2] +~g~Il ~r~corriere~g~ con le matrici è arrivato al porto. + +[CNT2_4:COUNT2] +Faccende private, non sei il ben venuto. + +[CNT2_09:COUNT2] +BENI TIPOGRAFIA ACQUISITI + +[CNT2_10:COUNT2] +~g~La tipografia d'ora in poi genererà introiti per un massimo di ~1~$. Ricordati di recuperarli regolarmente. + +[CNT2_11:COUNT2] +~r~Le matrici adesso sono in fondo all'oceano! + +{=================================== MISSION TABLE CUBAN1 ===================================} + +[CUB1_A:CUBAN1] +Sì amico? + +[CUB1_B:CUBAN1] +Ehi, tranquillo papà, è venuto per me. Sei tu l'uomo? + +[CUB1_C:CUBAN1] +Oh sì, sei tu l'uomo. Credo per lo meno, vero? + +[CUB1_D:CUBAN1] +No, non credo. + +[CUB1_E:CUBAN1] +Ah sì? Vieni qua, osso duro. + +[CUB1_F:CUBAN1] +Pensi di potermi prendere in giro? + +[CUB1_G:CUBAN1] +Credi di poter fare lo stupido con me? + +[CUB1_H:CUBAN1] +No, credo tu stia facendo abbastanza lo stupido per entrambi. + +[CUB1_I:CUBAN1] +Ehi figliolo, ha detto che sei stupido. + +[CUB1_J:CUBAN1] +E io lo chiamo femminuccia, papà. + +[CUB1_K:CUBAN1] +Guarda un po' come va in giro vestito. + +[CUB1_L:CUBAN1] +Che cos'è questa? L'ultima moda femminile per la notte? + +[CUB1_M:CUBAN1] +Perché un osso duro come te si veste da donnina? + +[CUB1_N:CUBAN1] +Hai le mutandine come le signorine, eh? + +[CUB1_O:CUBAN1] +Cos'hai contro le donne? Preferisci gli uomini, amico? + +[CUB1_P:CUBAN1] +Mi piacciono le donne! Tutte le donne! Adoro mia madre, chico. + +[CUB1_Q:CUBAN1] +Va bene, va bene, ti credo sulla parola. + +[CUB1_R:CUBAN1] +Sai guidare, amigo? + +[CUB1_S:CUBAN1] +Certo... come una donna. + +[CUB1_T:CUBAN1] +Molto divertente. Mi piaci, ragazzo. Forse ci puoi aiutare. + +[CUB1_U:CUBAN1] +Potresti provare di essere un uomo. Huh? + +[CUB1_V:CUBAN1] +Porta fuori la barca. + +[CUB1_W:CUBAN1] +Mostra di avere due grossi cojones, + +[CUB1_X:CUBAN1] +e non piccoli minuscoli chiquita. + +[CUB1_02:CUBAN1] +OK amico, trattala come una vera signora. + +[CUB1_03:CUBAN1] +Non male, sei un vero uomo. + +[CUB1_04:CUBAN1] +Accidenti, hai davvero due grossi cojones, amigo. + +[CUB1_05:CUBAN1] +Amigo, sei un vero uomo. + +[CUB1_06:CUBAN1] +E tu ti reputi un uomo? + +[CUB1_07:CUBAN1] +Sei un piccolo gattino impaurito, bamboccio: vai a piangere dalla mamma! + +[CUB1_08:CUBAN1] +Sei un grande spreco di spazio. Cammini come un uomo, parli come un uomo, ma guidi come un idiota. + +[CUB1_09:CUBAN1] +Amico, sei tu l'uomo. Mi piaci, amico. Mi piaci molto. + +[CUB1_10:CUBAN1] +A qualsiasi ora, amico, perché tu hai i cojones e tutti i miei amici hanno grandi cajones. + +[CUB1_11:CUBAN1] +~r~Hai ucciso Rico! + +[CUB1_12:CUBAN1] +Passa per il primo punto di controllo per cominciare il test. + +[CUB1_14:CUBAN1] +Torna alla barca! + +[CUB1_15:CUBAN1] +~r~Sei troppo lento, amico. + +[CUB1_01:CUBAN1] +Ehi, sono Rico. Sei tu l'uomo con i grandi cojones? + +[CUB1_13:CUBAN1] +~g~Hai tre minuti per completare il percorso. + +{=================================== MISSION TABLE CUBAN2 ===================================} + +[CUB2_A:CUBAN2] +Un cafecito, por favor, Alberto... + +[CUB2_N:CUBAN2] +Nessun problema, Tommy. + +[CUB2_B:CUBAN2] +Papà! Un gran problema! + +[CUB2_O:CUBAN2] +Umberto, figlio mio, cosa succede? + +[CUB2_C:CUBAN2] +Gli Haitiani! Odio quei maledetti Haitiani! + +[CUB2_D:CUBAN2] +Hanno osato crearmi problemi per l'ultima vota! + +[CUB2_E:CUBAN2] +Questi Haitiani. Facciamoli fuori! + +[CUB2_F:CUBAN2] +Avremo bisogno di supporto. + +[CUB2_G:CUBAN2] +Ho già perso degli hermanos là fuori. + +[CUB2_H:CUBAN2] +Amigo, tu guidi bene! + +[CUB2_I:CUBAN2] +Per una donna, giusto? + +[CUB2_J:CUBAN2] +Non è il momento degli scherzi! + +[CUB2_K:CUBAN2] +Forza, guida nuovamente per me! + +[CUB2_L:CUBAN2] +Porta i miei ragazzi in posizione e faremo fuori quegli Haitiani! + +[CUB2_M:CUBAN2] +Si sono messi contro me, hanno scherzato con il pezzo grosso della città! + +[CUB2_01:CUBAN2] +Troppo piccola, amico, devi prendere una macchina più grande. + +[CUB2_02:CUBAN2] +Abbiamo bisogno di rinforzi dal café! + +[CUB2_03:CUBAN2] +~g~Prendi una macchina e recupera i Cubani di fronte al café Robina. + +[CUB2_04:CUBAN2] +~g~Scarica i Cubani sul luogo dello scontro. + +[CUB2_05:CUBAN2] +Fai fuori quel codardo di un cecchino! + +[CUB2_07:CUBAN2] +Combattono come donnicciole! Mettetevi al riparo! + +[CUB2_09:CUBAN2] +Cecchino sul tetto! + +[CUB2_11:CUBAN2] +~r~Idiota, avevamo bisogno della macchina. + +[CUB2_12:CUBAN2] +Ehi amigo! Sono felice che ce l'hai fatta. + +[CUB2_13:CUBAN2] +Covo puzzolente di Haitiani, li uccideremo tutti! + +[CUB2_14:CUBAN2] +CAAARICAAAA! + +[CUB2_15:CUBAN2] +Adesso, fratelli, CAAARICAAAA! + +[CUB2_16:CUBAN2] +Tommy, abbiamo provato il nostro coraggio! + +[CUB2_17:CUBAN2] +Rubiamo quel van pieno di droga e scappiamo in fretta! + +[CUB2_18:CUBAN2] +~g~Recupera una macchina per caricare i Cubani. + +[CUB2_19:CUBAN2] +Combatteremo come uomini! + +[CUB2_21:CUBAN2] +Combattiamo come uomini con grandi cojones! + +[CUB2_22:CUBAN2] +~g~Elimina il resto degli Haitiani così i Cubani potranno avanzare. + +[CUB2_23:CUBAN2] +~g~Little Haiti sarà piena di Haitiani desiderosi di pareggiare il conto con i Cubani. Guardati la schiena. + +[CUB2_24:CUBAN2] +~g~Torna al Café Robina con il van e parcheggialo sul retro. + +[CUB2_25:CUBAN2] +UCCIDI TUTTI GLI HAITIANI!!! + +{=================================== MISSION TABLE CUBAN3 ===================================} + +[CUB3_A:CUBAN3] +Alberto. Un caffè, senor. + +[CUB3_B:CUBAN3] +Papà, non servire questo serpente nascosto nella paglia. + +[CUB3_C:CUBAN3] +Sei un doppiogiochista, Tommy! + +[CUB3_D:CUBAN3] +O fai il doppio gioco o sei un perdente, ragazzo! + +[CUB3_E:CUBAN3] +Gli Haitiani! Merda, stanno ridendo di me. + +[CUB3_F:CUBAN3] +Ehi. Calmati. Qual è il tuo problema? + +[CUB3_G:CUBAN3] +Ridono di me, Tommy! Di me! + +[CUB3_H:CUBAN3] +Umberto Robina! Fanno tutto ciò che vogliono! + +[CUB3_I:CUBAN3] +Nessuno fa ciò che vuole, Umberto, fanno ciò che tu permetti loro di fare. + +[CUB3_J:CUBAN3] +Cosa? + +[CUB3_K:CUBAN3] +Vuoi che mi occupi di qualcuno? + +[CUB3_L:CUBAN3] +Posso pensarci io, ma ti costerà. + +[CUB3_M:CUBAN3] +Lo so che siamo come fratelli, ma stiamo parlando di affari. + +[CUB3_N:CUBAN3] +Tommy, sei un vero uomo. Un vero businessman, un gentleman. + +[CUB3_O:CUBAN3] +Questi Haitiani. Hanno un carico di merce in arrivo via mare, roba di prima qualità. + +[CUB3_P:CUBAN3] +La prendiamo e li facciamo fuori. + +[CUB3_Q:CUBAN3] +Tu la prendi, io ti copro. Come mio fratello, come mio figlio. + +[CUB3_R:CUBAN3] +Preferisco i soldi che saltellare sulle tue gambe, amigo. + +[CUB3_01:CUBAN3] +Ehi Rico, bella barca. Sei pronto? + +[CUB3_03:CUBAN3] +~g~Raccogli tutte le valigette piene di soldi e droga. + +[CUB3_04:CUBAN3] +~g~Riporta la droga e i soldi a Umberto. + +[CUB3_05:CUBAN3] +Sì Tommy. Vedi di sparare come si deve oggi, + +[CUB3_06:CUBAN3] +non voglio riempire la mia barca di buchi, OK? + +[CUB3_07:CUBAN3] +~g~Incontrati con Rico, lui ti porterà fino alla locazione dell'incontro. + +[CUB3_02:CUBAN3] +~g~UCCIDI TUTTI GLI HAITIANI SULLE BARCHE!!! + +[CUB3_08:CUBAN3] +Uh oh... un gruppo di Cubani. Siamo sotto attacco! + +{=================================== MISSION TABLE CUBAN4 ===================================} + +[CUB4_A:CUBAN4] +Ehi signorine, sapete che cosa voglio fare? + +[CUB4_B:CUBAN4] +Voglio far fuori un Haitiano. E poi? + +[CUB4_C:CUBAN4] +Poi voglio fare l'amore come un vero uomo. + +[CUB4_D:CUBAN4] +Capito chica? Proprio così. + +[CUB4_E:CUBAN4] +Fallito! + +[CUB4_F:CUBAN4] +Cafone. + +[CUB4_G:CUBAN4] +Ehi bella, non ti toccherei neanche con un bastone da lontano! + +[CUB4_H:CUBAN4] +Umberto Robina, a lui piacciono le vere donne! Non delle capre in minigonna! + +[CUB4_I:CUBAN4] +Tommy! Tommy, ti voglio bene, davvero! Andiamo! + +[CUB4_J:CUBAN4] +Dove? Non posso prendere prima una tazza di caffè? + +[CUB4_K:CUBAN4] +Non c'è tempo per il caffè! Inoltre, io l'ho già preso. + +[CUB4_L:CUBAN4] +Dobbiamo far fuori gli Haitiani. + +[CUB4_M:CUBAN4] +Tommy, come fai a uccidere un serpente? + +[CUB4_N:CUBAN4] +Lo mordi sul culo! Ah ah ah! + +[CUB4_O:CUBAN4] +Se lo dici te, Umberto. + +[CUB4_P:CUBAN4] +Tommy, recupera una piccola auto Haitiana. + +[CUB4_Q:CUBAN4] +Quando ce l'hai, torna qui e carica il mio ragazzo, + +[CUB4_R:CUBAN4] +Pepe, e portalo fino agli Haitiani. + +[CUB4_S:CUBAN4] +Poi aggiri l'impianto di lavorazione degli Haitiani e utilizzi il solvente come esplosivo. + +[CUB4_T:CUBAN4] +Boom! Bye bye! + +[CUB4_U:CUBAN4] +Umberto, e tu? + +[CUB4_V:CUBAN4] +Oh, io resto nelle retrovie e controllo il café con papà. + +[CUB4_W:CUBAN4] +Non si sente molto bene, sai? + +[CUB4_02:CUBAN4] +~g~Le bombe verranno piazzate con un timer di 45 secondi. + +[CUB4_04:CUBAN4] +~r~Hai allarmato la base, adesso non riusciremo mai a entrare! + +[CUB4_07:CUBAN4] +Il solvente è sul retro, amigo. + +[CUB4_08:CUBAN4] +Hola, Amigos. + +[CUB4_09:CUBAN4] +Bueno. Haitiane Putas. Muerte. + +[CUB4_10:CUBAN4] +Vamos. + +[CUB4_11:CUBAN4] +Vamos davvero. + +[CUB4_12:CUBAN4] +Ehi, abbiamo bisogno di un'auto degli Haitiani! + +[CUB4_13:CUBAN4] +Oye, andiamo a trovare i nostri muchachos! + +[CUB4_14:CUBAN4] +Seguimi, compadres. + +[CUB4_15:CUBAN4] +OK, vai dentro... + +[CUB4_16:CUBAN4] +Io posizionerò le bombe, coprimi! + +[CUB4_17:CUBAN4] +CORRI! + +[CUB4_18:CUBAN4] +Amico, questa sì che è una bella parte della città... + +[CUB4_19:CUBAN4] +Questo luogo è una fogna. + +[CUB4_20:CUBAN4] +Avevo una bella donna... viveva da queste parti. + +[CUB4_21:CUBAN4] +Lo sai, fanno delle buone pizze da queste parti. + +[CUB4_22:CUBAN4] +Whoa, amico. Guidi come un cavallo impazzito! + +[CUB4_23:CUBAN4] +Ti sei perso, amico? + +[CUB4_24:CUBAN4] +Hai lasciato indietro Pepe, vai a riprenderlo! + +[CUB4_03:CUBAN4] +~g~Resta in macchina fino a quando questa non sarà parcheggiata all'interno. + +[CUB4_26:CUBAN4] +~g~Prendi Pepe, dirigiti a nord verso Little Haiti e ruba una macchina Voodoo. + +[CUB4_27:CUBAN4] +~g~Raggiungi Rico e gli altri Cubani. + +[CUB4_28:CUBAN4] +~g~Unisciti agli altri Cubani presso la fabbrica di droga Haitiana. + +[CUB4_29:CUBAN4] +~g~Cammina su ogni segnalino per posizionare una bomba. + +[CUB4_30:CUBAN4] +~g~Dopo aver posizionato tutte e tre le bombe, scappa dalla fabbrica prima che esploda. + +[CUB4_31:CUBAN4] +~g~Scappa dalla fabbrica!!! + +[CUB4_32:CUBAN4] +~g~Parcheggia l'auto nel punto segnalato ed esci. + +[CUB4_06:CUBAN4] +~r~Non ti sei allontanato a sufficienza dalla base e abbiamo dovuto interrompere l'esplosione! + +{=================================== MISSION TABLE FINALE ===================================} + +[FIN_1A:FINALE] +Vieni qui brutto doppiogiochista di merda! + +[FIN_1B:FINALE] +Ti faccio a pezzi, traditore del cazzo! + +[FIN_1C:FINALE] +Questo è l'ultimo ballo per Lance Vance! + +[FIN_2B:FINALE] +Oh, ma davvero! + +[FIN_2C:FINALE] +Te l'avevo detto che ne avevo avuto abbastanza a scuola! + +[FIN_3:FINALE] +Adesso chi ti coprirà il culo, eh Tommy? + +[FIN_4:FINALE] +Sei storia, Tommy, storia! + +[FIN_5:FINALE] +Hai scelto il lato sbagliato, Lance... + +[FIN_11A:FINALE] +Sonny, mi hai rubato quindici anni di vita... + +[FIN_11B:FINALE] +E adesso te la farò pagare! + +[FIN_12A:FINALE] +Non hai ancora capito, vero? + +[FIN_12B:FINALE] +Io ti POSSIEDO, Tommy. + +[FIN_12C:FINALE] +Quei quindici anni erano miei! + +[FIN_13:FINALE] +Prendetelo ragazzi, non ha mai capito niente. + +[FIN1_01:FINALE] +Cosa sta succedendo? + +[FIN1_02:FINALE] +Tommy! Oh bene, bene. Ascolta, ascolta. Uh, ascolta. + +[FIN1_03:FINALE] +A me piacciono i pesci. Adoro i pesci. + +[FIN1_04:FINALE] +Mi piacciono quando nuotano in una boccia o come cibo in un piatto, + +[FIN1_05:FINALE] +ma per quanto li ami, non voglio dormire in loro compagnia. + +[FIN1_06:FINALE] +Va bene, ma adesso i tuoi fratelli italiani stanno venendo per mettermi ai piedi delle belle scarpe in cemento e io... + +[FIN1_07:FINALE] +Sta zitto Ken. Siediti. + +[FIN1_08:FINALE] +Lance, di che diavolo stai parlando? + +[FIN1_09:FINALE] +Sono i tuoi amici del nord, Tommy. Non sono felici di cosa hai fatto al loro uomo. + +[FIN1_10:FINALE] +Oggi verranno giù per questioni di lavoro. + +[FIN1_11:FINALE] +Ci hanno messo di più di quanto credessi... + +[FIN1_12:FINALE] +Ragazzi, dobbiamo essere molto chiari. Non voglio che ci siano dubbi sul fatto che questa è la mia operazione. MIA! + +[FIN1_13:FINALE] +Ken, recupera la prima partita di soldi falsi e metti venti milioni in delle valigette. + +[FIN1_14:FINALE] +Lance, raduna i ragazzi... + +[FIN2_01:FINALE] +Tommy! + +[FIN2_02:FINALE] +Cosa? Niente abbraccio per un vecchio amico? + +[FIN2_03:FINALE] +Sono stato per quindici anni fuori dal giro, + +[FIN2_04:FINALE] +sono un po' arrugginito in fatto di etichetta familiare. + +[FIN2_05:FINALE] +Sempre arrabbiato, vero Tommy? + +[FIN2_06:FINALE] +Non ti avevo detto che il tuo carattere ti avrebbe messo nei guai, eh? + +[FIN2_07:FINALE] +Ci sono venti milioni in quelle valigette... + +[FIN2_08:FINALE] +Quanti hai detto? Dieci? No, undici uomini. + +[FIN2_09:FINALE] +È così che sei diventato il macellaio di Harwood! Eh eh eh! + +[FIN2_10:FINALE] +Mi hai inviato per uccidere un uomo, UN UOMO. Sapevano che stavo arrivando Sonny... + +[FIN2_11:FINALE] +Modera il tono. + +[FIN2_12:FINALE] +Qualcuno potrebbe pensare che stai incolpando me per le sfortunate circostanze. + +[FIN2_13:FINALE] +Prendi i fottuti soldi... + +[FIN2_14:FINALE] +Prendi i fottuti soldi? + +[FIN2_15:FINALE] +Lo sai, Tommy? Ho fatto quello che ho potuto per te. Ho tirato i fili, ho chiesto favori. + +[FIN2_16:FINALE] +Ero tuo amico, Tommy. Pensavo che avresti avuto buon senso, che avresti capito cosa è bene per il business. + +[FIN2_17:FINALE] +Mi fidavo di te, Tommy. E tu mi hai deluso. + +[FIN2_18:FINALE] +Ma alla fine qualcuno nella tua organizzazione del cazzo ha capito come si fa il business. + +[FIN2_19:FINALE] +Non è vero, Lance? + +[FIN2_20:FINALE] +Mi dispiace Tommy. Questa è Vice City. Questo è il suo business. + +[FIN2_21:FINALE] +Ci hai venduto... + +[FIN2_22:FINALE] +No. Ho venduto TE, Tommy, ho venduto TE. + +[FIN2_23:FINALE] +I soldi veri sono di sopra nella cassaforte. + +[FIN2_24:FINALE] +Allora Tommy, questo era il grande piano? + +[FIN2_25:FINALE] +Pensavi davvero che avrei preso i soldi finti? + +[FIN2_26:FINALE] +Salva la faccia e scappa con la coda tra le gambe! + +[FIN2_27:FINALE] +No. + +[FIN2_28:FINALE] +Volevo solo farti incazzare prima di ucciderti. + +[FIN3_01:FINALE] +Tommy? + +[FIN3_02:FINALE] +Oh mio Dio, Tommy! Che cosa è successo? + +[FIN3_03:FINALE] +Che cosa ti sembra sia successo? + +[FIN3_04:FINALE] +Mi sembra ti si sia rovinato l'abito! + +[FIN3_05:FINALE] +Accidenti, Tommy, era così bello! Tommy, che diavolo è successo? + +[FIN3_06:FINALE] +Ho avuto una divergenza di vedute con un socio in affari, sai come vanno queste cose. + +[FIN3_07:FINALE] +Tommy, se io ho una divergenza con un socio, gli mando una lettera di insulti. + +[FIN3_08:FINALE] +Forse gli piscio nella casella della posta. Ma non do il via alla terza guerra mondiale! + +[FIN3_09:FINALE] +Lo sai, forse dovresti parlare con il mio strizzacervelli... + +[FIN3_10:FINALE] +Quello stupido idiota, Lance... + +[FIN3_11:FINALE] +Tommy, non mi è mai piaciuto quel tipo, OK? + +[FIN3_12:FINALE] +È nevrotico, insicuro, egocentrico... quel tipo è un vero stronzo! + +[FIN3_13:FINALE] +Sono felice che l'hai fatto fuori! + +[FIN3_14:FINALE] +Non penso che riceveremo ulteriori problemi dal nord... + +[FIN3_15:FINALE] +...fondamentalmente perché non c'è più un 'nord'. + +[FIN3_16:FINALE] +Adesso è tutto a sud. + +[FIN3_17:FINALE] +Aspetta, vuoi forse dire ciò che penso io, caro il mio Tommy? + +[FIN3_18:FINALE] +Cosa credi che significhi? + +[FIN3_19:FINALE] +Che siamo in carica... cioè, che sei in carica! Oh Tommy... + +[FIN3_20:FINALE] +Lo sai, Ken, questo potrebbe essere l'inizio di uno splendido rapporto lavorativo... + +[FIN3_21:FINALE] +Dopo tutto, tu sei un convincente, diffamante, fottuto ladro. + +[FIN3_22:FINALE] +e io sono uno psicopatico assassino convinto e uno spacciatore. + +[FIN3_23:FINALE] +Lo so, non è meraviglioso? + +[FIN_B1:FINALE] +~g~Uccidi ~y~Vance~g~, il fottuto traditore. + +[FIN_B2:FINALE] +~g~Trova ~p~Sonny~g~ e metti la parola fine a questa storia. + +[FIN_B3:FINALE] +~g~La Mafia sta cercando di rubare i tuoi soldi. Difendi la cassaforte. + +[FIN_B4:FINALE] +~g~Sei prossimo alla morte: recupera della ~w~salute~g~ dal basso. + +[FIN_B5:FINALE] +~g~La Mafia sta rubando i tuoi soldi. Difendi la ~c~cassaforte~g~. + +[FIN_B7:FINALE] +~r~La Mafia ha rubato i tuoi soldi! + +[DEFSAFE:FINALE] +~g~Ritorna alla cassaforte e difendila. + +{=================================== MISSION TABLE FIRETRK ===================================} + +[F_PASS1:FIRETRK] +Incendio spento! + +[F_FAIL2:FIRETRK] +~r~Sei arrivato tardi! + +[F_CANC:FIRETRK] +~r~Missione Pompieri annullata! + +[F_EXTIN:FIRETRK] +INCENDI: + +[F_START:FIRETRK] +~g~Veicolo in fiamme avvistato in ~a~. Vai a spegnere il fuoco. + +[SIREN_1:FIRETRK] +Per attivare la sirena di questo veicolo premi il ~h~~k~~VEHICLE_HORN~~w~. + +[SIREN_2:FIRETRK] +Per attivare la sirena di questo veicolo premi il ~h~~k~~VEHICLE_HORN~~w~. + +[FIREPRO:FIRETRK] +Missione Camion dei pompieri livello 12 completata: adesso sei permanentemente ignifugo!!! + +[F_FAIL1:FIRETRK] +Missione Camion dei pompieri terminata. + +[F_STAR1:FIRETRK] +~g~Veicoli in fiamme presso l'area ~a~. Vai a spegnere l'incendio. + +[SPRAY_4:FIRETRK] { reVC update } +Premi il tasto ~h~~k~~VEHICLE_FIREWEAPON~~w~ per sparare con il cannone ad acqua e la ~h~~k~~VEHICLE_TURRETLEFT~~w~ e ~h~~k~~VEHICLE_TURRETRIGHT~~w~ per mirare. + +[SPRAY_1:FIRETRK] { reVC update } +Premi il tasto ~h~~k~~VEHICLE_FIREWEAPON~~w~ per sparare con il cannone ad acqua e la ~h~~k~~VEHICLE_TURRETLEFT~~w~ e ~h~~k~~VEHICLE_TURRETRIGHT~~w~ per mirare. + +{=================================== MISSION TABLE GENERA1 ===================================} + +[GEN1_A:GENERA1] +Mr. Vercetti! + +[GEN1_B:GENERA1] +Colonnello. + +[GEN1_D:GENERA1] +No, grazie. + +[GEN1_E:GENERA1] +Mi vergogno ad ammettere che una delle cause dei nostri problemi comuni sembra sia stata la linguaccia di una persona di cui credevo di potermi fidare. + +[GEN1_F:GENERA1] +Mi sono tenuto Gonzalez per anni, ma ora la sua incompetenza ha raggiunto il fondo. + +[GEN1_G:GENERA1] +Sarebbe solo un bene se eliminassi Gonzalez... + +[GEN1_H:GENERA1] +È stato lui? A me importa solo dei miei soldi. + +[GEN1_I:GENERA1] +Questa gentilezza verrà ricompensata dopo troveremo insieme i tuoi soldi. + +[GEN1_J:GENERA1] +Lo troverai nella sua Penthouse, probabilmente mezzo ubriaco. Usa questo... + +[GEN1_06:GENERA1] +Eeek! Ha una motosega! + +[GEN1_07:GENERA1] +Stammi lontano, brutto bastardo! + +[GEN1_08:GENERA1] +Oddio santo, ho distrutto la mia vita e il mio look! + +[GEN1_10:GENERA1] +Ho deciso di chiudere quella tua maledetta boccaccia! + +[GEN1_11:GENERA1] +Smettila di correre, brutto grassone! + +[GEN1_12:GENERA1] +Stai fermo, così la facciamo finita! + +[GEN1_13:GENERA1] +Smettila di strillare, non gliene frega a nessuno, grassone! + +[GEN1_C:GENERA1] +Grazie per essere venuto. Siediti. Vuoi dell'aragosta? + +[GEN1_05:GENERA1] +~g~Uccidi Gonzalez! + +[GEN1_09:GENERA1] +Ti pagherò doppio, Tommy, DOPPIO! + +[GEN1_18:GENERA1] +~r~Gonzalez ha raggiunto sano e salvo la stazione di polizia! + +[GEN1_19:GENERA1] +~g~La polizia di Vice City ti sta inseguendo! + +[GEN1_20:GENERA1] +~g~Prendi un veicolo. + +[GEN1_21:GENERA1] +~g~Raggiungi il ~h~Pay 'N' Spray~g~ in ~h~Vice Point~g~. + +[GEN1_22:GENERA1] +~g~Guida il tuo veicolo attraverso il carrozziere per perdere il ~h~livello di sospetto~g~, ~h~riparare~g~ e ~h~ricolorare~g~ il mezzo. Costo: ~h~100$~g~. Questa volta è gratis. + +[GEN1_01:GENERA1] +Quando corri, tieni premuto il ~o~tasto |~w~ per preparare un attacco corpo a corpo. + +[GEN1_02:GENERA1] +Quando corri, tieni premuto il ~x~tasto /~w~ per preparare un attacco corpo a corpo. + +[GEN1_03:GENERA1] +Quando corri, tieni premuto il ~h~tasto R1~w~ per preparare un attacco corpo a corpo. + +[GEN1_14:GENERA1] +Rilascia il ~h~~k~~PED_FIREWEAPON~~w~ per eseguire l'attacco. + +[GEN1_15:GENERA1] +Rilascia il ~h~~k~~PED_FIREWEAPON~~w~ per eseguire l'attacco. + +[GEN1_16:GENERA1] +Rilascia il ~h~~k~~PED_FIREWEAPON~~w~ per eseguire l'attacco. + +[GEN1_23:GENERA1] +~g~Passa per le porte per tornare al piano terra. + +{=================================== MISSION TABLE GENERA2 ===================================} + +[COL2_A:GENERA2] +Tommy! Vieni qui da me. + +[COL2_B:GENERA2] +Non ti sembra delizioso, vero? Lingua di Tapia? + +[COL2_C:GENERA2] +Eerr... No, no grazie. + +[COL2_D:GENERA2] +Tommy, sei come un vento fresco della pampa che mi libera dall'olezzo della corruzione, + +[COL2_E:GENERA2] +benché mi addolori la recente dipartita, devo continuare come sempre con il mio lavoro. + +[COL2_F:GENERA2] +Non mi sembra ci stiamo avvicinando ai miei soldi... + +[COL2_G:GENERA2] +Tommy, amico mio, non sei più a Liberty. Qui le cose vanno in modo diverso. + +[COL2_H:GENERA2] +Continuerò le mie ricerche, ma nel frattempo ho un interessante accordo da proporti. + +[COL2_I:GENERA2] +Un favore per un amico, Cortez. + +[COL2_J:GENERA2] +Sei un vero amico, Tommy, Ero certo che non mi avresti deluso. + +[COL2_K:GENERA2] +Ho bisogno che tu incontri un corriere che mi ha recuperato una tecnologia di cui ho bisogno... + +[COL2_1:GENERA2] +La pioggia, è così très umido in questo periodo dell'anno... + +[COL2_2:GENERA2] +Cosa? + +[COL2_3:GENERA2] +Ah, comment? + +[COL2_4:GENERA2] +Ascolta, mi manda Cortez. Dammi quei maledetti chip. + +[COL2_5:GENERA2] +Oh... d'accord. + +[COL2_B1:GENERA2] +~g~Incontra il corriere al Mall. + +[COL2_B2:GENERA2] +~g~Il correre sta scappando con i chip: non farlo scappare! + +[COL2_B3:GENERA2] +~g~Porta i chip al Colonnello. + +[COL2_F1:GENERA2] +~r~Hai ucciso il contatto! + +[COL2_F2:GENERA2] +~r~Il corriere è morto. Raccogli i chip. + +[COL2_6A:GENERA2] +Fermo, brutto maiale imperialista americano! Questa è proprietà del governo francese. Passamela! + +[BLIPHLP:GENERA2] +Se il segnale sul radar è un triangolo verso l'alto, significa che il bersaglio è in posizione più elevata rispetto al giocatore. + +[COL2_F3:GENERA2] +~r~I chip adesso riposano in fondo all'oceano. + +[COL2_F4:GENERA2] +~r~Il corriere è scappato! Non sei riuscito a recuperare i chip. + +{=================================== MISSION TABLE GENERA3 ===================================} + +[GEN3_A:GENERA3] +Thomas, grazie per essere venuto. + +[GEN3_B:GENERA3] +Perdonami se giungo subito al punto. + +[GEN3_C:GENERA3] +Diaz mi ha chiesto di sovrintendere una piccola transizione di lavoro. + +[GEN3_D:GENERA3] +Speriamo vada meglio dell'ultima... + +[GEN3_E:GENERA3] +Ed è per questo che ho pensato a te, amico. + +[GEN3_F:GENERA3] +Ho lasciato un po' di supporto al parcheggio multipiano. + +[GEN3_G:GENERA3] +Raccogli il materiale, poi raggiungi e sorveglia gli uomini di Diaz all'incontro. + +[GEN3_H:GENERA3] +Gracias, amigo. + +[GEN3_1:GENERA3] +Ami l'azione, vedo... + +[GEN3_2:GENERA3] +Senti, ti capita mai di fare qualcos'altro che non sia seguirmi dappertutto? Perché non vieni con me e mi dimostri di servire a qualcosa? + +[GEN3_3:GENERA3] +Potrei anche farlo. A proposito, mi chiamo Lance. + +[GEN3_5:GENERA3] +Devi essere il nuovo uomo di Cortez. + +[GEN3_6:GENERA3] +Fino a quando non mi capiteranno opportunità più lucrose. + +[GEN3_7:GENERA3] +Arriveranno fra pochi minuti: meglio trovare un buon punto dove appostarsi... + +[GEN3_8:GENERA3] +OK! Io prendo il balcone, tu prendi il tetto dall'altra parte del cortile. + +[GEN3_9:GENERA3] +Sono vivo, brutti stronzi! E tutto grazie a te! Come ti chiami? + +[GEN3_10:GENERA3] +Tommy. + +[GEN3_11:GENERA3] +Ci incontreremo di nuovo, credo! + +[GEN3_12:GENERA3] +Dove diavolo è andato Lance? Merda... + +[GEN3_14:GENERA3] +Tommy! Ho bisogno di aiuto! + +[GEN3_15:GENERA3] +Non preoccuparti, ti copro io! + +[GEN3_16:GENERA3] +Gli uomini di Diaz si stanno facendo massacrare! + +[GEN3_19:GENERA3] +~g~Haitiani! Stanno mandando a monte l'affare! Proteggi Diaz! + +[GEN3_20:GENERA3] +~g~Entra nel parcheggio multipiano e recupera ciò che il Colonnello ha lasciato per te. + +[GEN3_22:GENERA3] +Salute di Diaz: + +[GEN3_23:GENERA3] +~g~Hai lasciato Lance indietro! Vallo a prendere! + +[GEN3_25:GENERA3] +~r~Lance è morto! + +[GEN3_28:GENERA3] +~g~Riporta la valigetta a Diaz. + +[GEN3_29:GENERA3] +~g~Raccogli la valigetta e riportala a Diaz. + +[GEN3_30:GENERA3] +~r~È scappato con i soldi! Diaz ti strapperà le palle per questo! + +[GEN3_33:GENERA3] +~r~Dovresti proteggere Diaz e i suoi uomini, non sparargli addosso! + +[GEN3_34:GENERA3] +~r~Non ci sarà nessun accordo se spari ai Cubani! + +[GEN3_35:GENERA3] +~g~Ha rubato i soldi di Diaz! + +[GEN3_36:GENERA3] +~g~Prendi la moto, inseguilo e recupera i soldi di Diaz! + +[GEN3_37:GENERA3] +~g~Stanno arrivando i Cubani. Assicurati che niente vada storto e proteggi Diaz e Lance. + +[GEN3_38:GENERA3] +~r~Diaz è morto! Non sei riuscito a proteggerlo! + +[GEN3_39:GENERA3] +~g~Raggiungi la tua postazione sopra alle scale. + +[GEN3_44:GENERA3] +~g~Vai con Lance all'appuntamento e proteggi Diaz. + +[GEN3_40:GENERA3] { reVC update } +Per ~h~sparare davanti~w~ su una ~h~moto~w~ premi il ~h~~k~~VEHICLE_FIREWEAPON~~w~. + +[GEN3_41:GENERA3] { reVC update } +Per ~h~sparare davanti~w~ su una ~h~moto~w~ premi il ~h~~k~~VEHICLE_FIREWEAPON~~w~. + +[GEN3_46:GENERA3] +Meerda! + +[GEN3_47:GENERA3] +Tommy! + +[GEN3_48:GENERA3] +Maledizione! + +[GEN3_49:GENERA3] +SALUTE DI LANCE: + +[GEN3_50:GENERA3] +~r~Hai distrutto i soldi di Diaz! La prossima volta cerca di non ridurre le banconote in cenere! + +[GEN3_51:GENERA3] +Altri fottuti Haitiani in un merdoso van! + +[GEN3_54:GENERA3] +Non restare lì impalato, idiota, insegui quello stronzo di un Haitiano! + +[GEN3_55:GENERA3] +Tommy! Io resto qua a proteggere Diaz! + +[GEN3_18:GENERA3] +~g~Stanno arrivando i Cubani, resta vicino a Diaz. Assicurati che niente vada storto e proteggi Diaz e Lance. + +[GEN3_56:GENERA3] +~r~Diaz è stato assalito e ucciso! La prossima volta, cerca di proteggerlo! + +[GEN3_57:GENERA3] +Il Kruger è un fucile d'assalto che permette di mirare in prima persona. + +[GEN3_58:GENERA3] +Tieni premuto il tasto ~h~R1~w~ per ~h~mirare~w~ con il fucile d'assalto. + +[GEN3_59:GENERA3] +Tieni premuto il tasto ~h~L1~w~ per ~h~mirare~w~ col fucile d'assalto. + +[GEN3_60:GENERA3] +Premi il tasto ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~ col fucile d'assalto. + +[GEN3_61:GENERA3] +Premi il tasto ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~ col fucile d'assalto. + +[GEN3_62:GENERA3] +Premi il tasto ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~ col fucile d'assalto. + +[GEN3_63:GENERA3] +Oltre a permetterti di sparare in corsa, con le ~h~moto~w~ puoi anche ~h~sparare in avanti~w~. + +[GEN3_64:GENERA3] { reVC update } +Per sparare in avanti quando sei su una moto, premi il tasto ~h~~k~~VEHICLE_FIREWEAPON~~w~. + +[GEN3_65:GENERA3] { reVC update } +Per sparare in avanti quando sei su una moto, premi il tasto ~h~~k~~VEHICLE_FIREWEAPON~~w~. + +[GEN3_66:GENERA3] { reVC update } +Per sparare in avanti quando sei su una moto, premi il tasto ~h~~k~~VEHICLE_FIREWEAPON~~w~. + +[GEN3_67:GENERA3] +Hai bisogno di una mitragliatrice per poter sparare in avanti. + +[GEN3_53:GENERA3] +I MIEI SOLDI! + +[GEN3_52:GENERA3] +Questi Haitiani pensano che possono farsi RICARDO DIAZ!?! + +{=================================== MISSION TABLE GENERA4 ===================================} + +[DETON:GENERA4] +DETONAZIONE: + +[COL4_3:GENERA4] +CONVOGLIO ALT! + +[COL4_6:GENERA4] +SIAMO SOTTO FUOCO NEMICO! + +[COL4_7:GENERA4] +Civile, si allontani dal carro armato! + +[COL4_8:GENERA4] +HO DETTO, si allontani, IMMEDIATAMENTE! + +[COL4_9:GENERA4] +POSIZIONI DIFENSIVE! + +[COL4_11:GENERA4] +Porta via quel civile, soldato! - Signorsì, signore! + +[COL4_12:GENERA4] +Civile nel CARRO ARMATO! FERMATELO! + +[COL4_13:GENERA4] +Questo è un convoglio militare, non ostacoli il passaggio. + +[COL4_14:GENERA4] +Fallo fuori, soldato. + +[COL4_15:GENERA4] +Spostate quel veicolo civile via dalla strada! -Signore! Sposto il veicolo, signore! + +[COL4_17:GENERA4] +OK, PLOTONE, IN MARCIA! + +[COL4_18:GENERA4] +Qualcuno è entrato nel carro armato, signore! + +[COL4_19:GENERA4] +Vai a prendere delle ciambelle, soldato! - Signorsì, signore! + +[COL4_20:GENERA4] +Bersaglio acquisito, signore! + +[COL4_21:GENERA4] +CECCHINO! + +[COL4_22:GENERA4] +Io me la squaglio. + +[COL4_23:GENERA4] +Obiettivo completato! Plotone riposo! -Andiamo a mangiarci qualche ciambella. + +[COL4_24:GENERA4] +Protocollo di sicurezza Delta India Echo attivato! Autodistruzione del veicolo attivata! + +[COL4_26:GENERA4] +Preparati a morire, maledetto comunista! + +[COL4_B2:GENERA4] +~r~Il carro armato è arrivato con successo a destinazione! + +[COL4_B5:GENERA4] +~r~Il carro armato è stato distrutto! + +[COL4_01:GENERA4] +Diaz era soddisfatto e vorrebbe incontrarti nuovamente. + +[COL4_02:GENERA4] +È un buon segno? + +[COL4_03:GENERA4] +Ma certo! Benché credo che Diaz sia il responsabile della nostra sfortunata perdita... + +[COL4_04:GENERA4] +Che cosa glielo fa pensare? + +[COL4_05:GENERA4] +Nessuno accusa direttamente un uomo come Diaz... sto solo pensando a voce altra... + +[COL4_06:GENERA4] +Non importa. Ho una proposta che potrebbe interessarti... + +[COL4_07:GENERA4] +Non ho più tempo per i suoi lavori, Cortez. + +[COL4_08:GENERA4] +Pensavo che un uomo con dei debiti così pericolosi fosse desideroso di opportunità. Per favore, Tommy, almeno ascoltami. + +[COL4_09:GENERA4] +Mi dica... + +[COL410:GENERA4] +Ho un acquirente per un mezzo militare che sta attraversando la città. Recuperalo per me... + +[COL411:GENERA4] +e, quando lo avrai, vorrei che mi chiamassi immediatamente. Poi... + +[COL4_B4:GENERA4] +~g~Il carro armato è chiuso. Trova un modo per far uscire gli occupanti. + +[COL4_1:GENERA4] +Che cos'ha il mitragliatore? -Non so signore! + +[COL4_4:GENERA4] +-Vai di sopra, soldato. -Signorsì, signore! + +[COL4_B1:GENERA4] +~g~Impadronisciti del mezzo militare che sta attraversando la città. + +[COL4_B3:GENERA4] +~g~Porta il carro armato al garage del Colonnello prima che si autodistrugga. + +[COL4_B6:GENERA4] +~g~Trova un modo per rubare il carro armato! + +[COL4_B7:GENERA4] +~g~Porta il carro armato dentro al garage. + +[COL4_B8:GENERA4] +~g~Esci dal carro armato e abbandona il garage. + +{=================================== MISSION TABLE GENERA5 ===================================} + +[COL5A_1:GENERA5] +Le circostanze richiedono una rapida partenza, amigo. + +[COL5A_2:GENERA5] +Qual è il problema? + +[COL5A_3:GENERA5] +Uh, i Francesi rivogliono la loro tecnologia missilistica indietro dopo quell'incidente... + +[COL5A_4:GENERA5] +Sento che è giunta l'ora di partire per porti più sicuri. + +[COL5A_5:GENERA5] +Non sarebbe meglio via aria? + +[COL5A_6:GENERA5] +Sarei morto ancor prima di raggiungere il check-in. Inoltre, devo portare la mia merce fuori dal paese. + +[COL5A_7:GENERA5] +Serve un'altra pistola? + +[COL5A_8:GENERA5] +Tu, amico, vali come dieci pistole... ah ah ah! + +[COL5B_1:GENERA5] +Thomas, mi hai protetto e servito bene. + +[COL5B_2:GENERA5] +Ma adesso devi abbandonarci prima che raggiungiamo il mare aperto. + +[COL5B_4:GENERA5] +Grazie mille, Colonnello. + +[COL5B_5:GENERA5] +Un'ultima richiesta, mentre sono via: terresti un occhio su Mercedes per me? + +[COL5B_6:GENERA5] +Credo sappia badare a sé stessa, ma certo, la terrò sott'occhio. + +[COL5B_7:GENERA5] +Grazie mille, amico. A presto! + +[COL5B_8:GENERA5] +Adios, amigo. + +[COL5_7:GENERA5] +Smettila di spararmi! + +[COL5_9:GENERA5] +Tommy, falli smettere di sparare! + +[COL5_10:GENERA5] +Ho l'immunità diplomatica! + +[COL5_11:GENERA5] +Non sparare, sono il Colonnello! + +[COL5_12:GENERA5] +Thomas, uccidili: la mia nazione ti sarà riconoscente. + +[COL5_13:GENERA5] +Tommy, siamo assaliti dai Francesi! + +[COL5_14:GENERA5] +Tommy, dovunque guardo vedo dei Francesi. Io li odio! + +[COL5_15:GENERA5] +Tommy, come stai? + +[COL5_16:GENERA5] +Questo è per la Piaf e Gainesbourg e per le vostre stupide baguette! + +[COL5_1:GENERA5] +A babordo! A babordo! + +[COL5_2:GENERA5] +Ci attaccano da tribordo! + +[COL5_3:GENERA5] +Il ponte qui davanti! + +[COL5_4:GENERA5] +Hanno un elicottero! + +[COL5_B1:GENERA5] +~g~Difendi a tutti i costi il Colonnello e il suo yacht. + +[COL5_B2:GENERA5] +~g~Vai a prua e libera la strada per il passaggio dello yacht del Colonnello. + +[COL5_B3:GENERA5] +~r~Il Colonnello è morto! + +[COL5_B4:GENERA5] +~g~Abbatti l'elicottero! + +[COL5B_3:GENERA5] +Farò calare la mia lancia personale. Tienila, amico, un pegno della mia gratitudine. + +[COL5_B5:GENERA5] +~g~Abbatti gli elicotteri e proteggi lo yacht. + +[COL5_B6:GENERA5] +~g~Hai finito i colpi: recuperane altri dalle scale al ponte superiore. + +[COL5_B7:GENERA5] +~g~Sei a corto di salute: recuperane altra dalle scale al ponte superiore + +{=================================== MISSION TABLE HAIT1 ===================================} + +[HAM1_A:HAIT1] +Permesso? + +[HAM1_B:HAIT1] +Entra pure, caro, e riposa l'anima. + +[HAM1_C:HAIT1] +Devi essere il grande uomo cattivo di cui parlava mio nonno. + +[HAM1_D:HAIT1] +Mi ha raccontato di te, sai, quando mi fa visita, + +[HAM1_E:HAIT1] +e degli altri che ti aspettano. + +[HAM1_F:HAIT1] +Adesso, siamo tutti morti da tempo, ma tu sai, + +[HAM1_G:HAIT1] +non vorrei essere nei tuoi panni, eh eh eh! + +[HAM1_H:HAIT1] +Ho ricevuto un messaggio. Sono venuto. + +[HAM1_I:HAIT1] +Li puoi sentire? + +[HAM1_J:HAIT1] +Stanno chiamando il tuo nome, devono proprio volerti, non credi? + +[HAM1_K:HAIT1] +Magari adesso dai una mano alla zia Poulet, uh, e forse lei ti aiuta. + +[HAM1_L:HAIT1] +Forse potrà darti un piccolo juju dopo tutto questo. + +[HAM1_M:HAIT1] +Un po' di magia per dare all'uomo di legge il malocchio, hmmm? + +[HAM1_N:HAIT1] +Ascolta, tutto questo è, uhm... darmi cosa? + +[HAM1_O:HAIT1] +Credo, credo proprio di aver sbagliato indirizzo... + +[HAM1_P:HAIT1] +Fai per me questo favore, Tommy... + +[HAM1_Q:HAIT1] +I Cubani, foofoo molto orgogliosi, hmmm, + +[HAM1_R:HAIT1] +hanno dato molti grattacapi ai miei ragazzi Haitiani. + +[HAM1_S:HAIT1] +Adesso hanno detto alla polizia dove nascondo le mie polveri. + +[HAM1_T:HAIT1] +Pensano sia droga, gli stupidi. + +[HAM1_U:HAIT1] +Adesso fai il bravo, Tommy, e vai a prendere le polveri per la zia Poulet. + +[HAM1_V:HAIT1] +Sì, sì, certo, vado. + +[HAM1_1:HAIT1] +~g~I poliziotti si stanno avvicinando alle nostre riserve. Fermali prima che ci riescano. + +[HAM1_2:HAIT1] +~r~I poliziotti sono arrivati per primi alla riserva! + +[HAM1_3:HAIT1] +~g~Riporta questa roba al nascondiglio! + +[HAM1_4:HAIT1] +~g~Ottimo! Adesso pensa al prossimo! + +[HAM1_6:HAIT1] +~r~La riserva è stata distrutta, idiota! + +[HAM1_7:HAIT1] +~g~La polizia ha preso la nostra riserva! Recuperala prima che se ne vada! + +[HAM1_8:HAIT1] +~g~I poliziotti sono vicini alla riserva: datti una mossa! + +[HAT_1A:HAIT1] +~g~Non muoverti, allocco! + +{=================================== MISSION TABLE HAIT2 ===================================} + +[HAT2_B1:HAIT2] +~g~Raggiungi il van che contiene le bombe volanti. + +[HAT2_B2:HAIT2] +Uccidi i Cubani... + +[HAT2_B4:HAIT2] +...e distruggi le loro imbarcazioni! + +[HAT2_B5:HAIT2] +~g~I Cubani stanno fuggendo: non lasciarli scappare! + +[HAT2_B6:HAIT2] +~r~L'aereo radiocomandato si sta allontanando dal raggio d'azione! + +[HAT2_B7:HAIT2] +~g~Uno dei Cubani sta fuggendo in macchina. Non lasciare nessun testimone! + +[HAT2_B8:HAIT2] +~g~Non hai più aerei radiocomandati! + +[HAT2_B9:HAIT2] +Aerei radiocomandati: + +[HAT2_1:HAIT2] +Oh. Scusa, devo aver sbagliato indirizzo... + +[HAT2_2:HAIT2] +Puoi entrare comunque a riposare i piedi e a bere un po' di tè. + +[HAT2_3:HAIT2] +Hai qualcosa per me, Tommy? + +[HAT2_4:HAIT2] +Sì... + +[HAT2_5:HAIT2] +Questo posto mi sembra familiare. Un odore della mia gioventù... un deja vu... + +[HAT2_6:HAIT2] +Adesso Tommy, ti sussurrerò una piccola commissione che devi farmi. Ascoltami attentamente, va bene? + +[HAT2_7:HAIT2] +Mi ricordi qualcuno che... + +[HAT2_8:HAIT2] +I Cubani hanno imbarcazioni molto veloci che utilizzano per trasportare la droga via mare. + +[HAT2_9:HAIT2] +È il loro modo di vivere. + +[HAT2_10:HAIT2] +Mio nipote ha costruito delle simpatiche bombe per farli fuori. + +[HAT2_11:HAIT2] +Fai esplodere le barche e inchioda le loro bare. + +[HAT2_12:HAIT2] +Beh, grazie per il tè. + +[HAT2_B3:HAIT2] { reVC update } +Premi il ~h~~k~~VEHICLE_FIREWEAPON~~w~ per sganciare una bomba. Premi il ~h~~k~~VEHICLE_ENTER_EXIT~ "~w~ per annullare. + +{=================================== MISSION TABLE HAIT3 ===================================} + +[HAM3_A:HAIT3] +Salve, salve, io, uh... Stavo cercando qualcuno... + +[HAM3_B:HAIT3] +Mi sembri affamato, Tommy. + +[HAM3_C:HAIT3] +Ci conosciamo? + +[HAM3_D:HAIT3] +Zitto ora. + +[HAM3_E:HAIT3] +Un'ultima cosa e potrai andare, Tommy. + +[HAM3_F:HAIT3] +I miei ragazzi sono in guerra con i Cubani. + +[HAM3_G:HAIT3] +Ma niente pistole. + +[HAM3_H:HAIT3] +Hmmm, ma i Cubani hanno una sorpresa in serbo. + +[HAM3_I:HAIT3] +Mentre combattono per la strada, tu prendi questo fucile e li elimini durante la confusione. + +[HAM3_J:HAIT3] +Nessuno ti vede, nessuno ti sente. + +[HAM3_K:HAIT3] +Adesso, Tommy, fai questo per me e non sarai più aggrappato al mio grembiule. + +[HAM3_1:HAIT3] +~g~Dobbiamo vincere questa battaglia. Se tutti gli Haitiani muoiono, sarà la fine. + +[HAM3_3:HAIT3] +~g~Sospetto che i Cubani bareranno, per cui stai in guardia. + +[HAM3_4:HAIT3] +~r~Ti hanno visto! La missione è fallita! + +[HAM3_5:HAIT3] +~g~Devi eliminare i Cubani da lontano. Non devono vederti. + +[HAM3_8:HAIT3] +~g~Gli Haitiani stanno morendo! Migliora la tua mira! + +[HAM3_7:HAIT3] +~g~Attento! I Cubani hanno chiamato rinforzi. Uccidili tutti quanti! + +[HAM3_2:HAIT3] +~r~Gli Haitiani sono morti! + +[HAM3_L:HAIT3] +Sssì zia... + +{=================================== MISSION TABLE HOTEL ===================================} + +[INTB_A:HOTEL] +Tommy! Tommy, quanto tempo è passato! + +[INTB_B:HOTEL] +Ciao Sonny. + +[INTB_C:HOTEL] +Lo so, lo so, sei sopraffatto dall'emozione. + +[INTB_D:HOTEL] +Quindici anni... sembra come se fosse ieri. + +[INTB_E:HOTEL] +Credo dipenda dal punto di vista. + +[INTB_F:HOTEL] +Ehi, stare dentro per la famiglia non è una passeggiata, + +[INTB_G:HOTEL] +ma la famiglia si preoccupa dei suoi, OK? + +[INTB_H:HOTEL] +Allora, raccontami come è andata... Eri in ballo con l'oro bianco? + +[INTB_I:HOTEL] +Ascolta Sonny, ci avevano incastrato. L'incontro era un'imboscata. Harry e Lee sono morti. + +[INTB_J:HOTEL] +Basta con queste storie, Tommy. Hai ancora i soldi, vero? + +[INTB_K:HOTEL] +...no Sonny... non ho i soldi. + +[INTB_L:HOTEL] +Quelli erano i miei soldi, Tommy, I MIEI SOLDI! + +[INTB_M:HOTEL] +Non cercare di fottermi, Tommy, perché sai che non sono una persona a cui piace essere presa per il culo! + +[INTB_N:HOTEL] +Aspetta Sonny. + +[INTB_O:HOTEL] +Hai la mia parola che recupererò i tuoi soldi e la droga. + +[INTB_P:HOTEL] +E ti farò avere per posta l'uccello dei responsabili. + +[INTB_Q:HOTEL] +Ehi, ne sono certo. Non sei uno stupido, Tommy, ma ti avverto: non lo sono neanch'io. + +[INTB_R:HOTEL] +Se fosse stato qualcun altro, adesso sarebbe già MORTO. + +[INTB_S:HOTEL] +Ma visto che sei tu, che ci conosciamo da tempo, ti permetterò di gestire la situazione. + +[INTB_T:HOTEL] +Ehi, Sonny, hai la mia parola. + +[INTB_U:HOTEL] +Mi farò vivo. + +{=================================== MISSION TABLE ICECRE1 ===================================} + +[ICC1_4:ICECRE1] +~g~Non ci sono clienti in quest'area, prova in un'altra. + +[ICC1_5:ICECRE1] +Transazioni effettuate: + +[ICC1_7:ICECRE1] +~g~Riceverai soldi per ogni transazione, ma più ne esegui, maggiori saranno le probabilità di attirare l'attenzione della polizia. + +[ICC1_8:ICECRE1] +~g~Per eseguire una transazione, ~h~parcheggia il camioncino~g~ e premi il ~h~~k~~VEHICLE_HORN~~g~ per suonare il campanello e attrarre i clienti. + +[ICC1_9:ICECRE1] +~g~Le gang locali non apprezzeranno il tuo lavoro nel loro territorio, per cui aspettati azioni ostili. + +[ICC1_10:ICECRE1] +~g~Hai portato a termine ~1~ transazioni! + +[ICC1_11:ICECRE1] +~g~Hai portato a termine ~1~ transazione! + +[ICC1_13:ICECRE1] +~r~Non hai portato a termine nessuna transazione. + +[ICC1_14:ICECRE1] +BENI FABBRICA DI GELATO ACQUISITI + +[ICC1_15:ICECRE1] +~g~La fabbrica di gelato d'ora in poi genererà introiti per un massimo di ~1~$. Ricordati di recuperarli regolarmente. + +[ICC1_16:ICECRE1] +~g~Utilizza il Mr. Whoopee per distribuire i prodotti Cherry Poppers per Vice City. + +[ICE_AT1:ICECRE1] +FABBRICA DI GELATO COMPLETATA + +[ICE_AT2:ICECRE1] +~g~La fabbrica Cherry Popper d'ora in poi genererà introiti per un massimo di ~1~$. Ricordati di recuperarli regolarmente. + +[ICC1_17:ICECRE1] +Missione di distribuzione terminata. + +[ICC1_18:ICECRE1] +Vendita totale gelati: ~1~$ + +[ICC1_19:ICECRE1] +Transizioni totali eseguite: ~1~ + +{=================================== MISSION TABLE ICECUT ===================================} + +[ICC1_A:ICECUT] +Chi sei? + +[ICC1_B:ICECUT] +Il nuovo proprietario. + +[ICC1_C:ICECUT] +Sei per caso, o lo sei mai stato, un bambino? + +[ICC1_D:ICECUT] +Ma di cosa stai parlando? + +[ICC1_E:ICECUT] +Sei stato un bambino? + +[ICC1_F:ICECUT] +Certo! Calmati! Che ti prende? + +[ICC1_G:ICECUT] +Lo sapevo. Un bambino. + +[ICC1_H:ICECUT] +Uno sporco, puzzolente, moccioso, lamentoso, vile, vomitevole, piagnucoloso piccolo bambino! + +[ICC1_I:ICECUT] +Un bambino... un mostruoso, orribile disgustoso piccolo bambino. Boo hoo. + +[ICC1_J:ICECUT] +La mamma non ti ama, brutto piccolo avanzo! + +[ICC1_K:ICECUT] +Ow! Accidenti, calmati! + +[ICC1_L:ICECUT] +Io ODIO i bambini, odio i poppanti. + +[ICC1_M:ICECUT] +Sono sporchi, puzzolenti, mocciosi, vomitevoli piccoli... + +[ICC1_N:ICECUT] +Ne ho avuto abbastanza! + +[ICC1_O:ICECUT] +Ma che diavolo hai? + +[ICC1_P:ICECUT] +Tu fai gelati, vero? Sono solo per bambini. + +[ICC1_Q:ICECUT] +Ma che psicopatica sei? + +[ICC1_R:ICECUT] +È così mi sono chiesta: perché fare felici i bambini se li odio? + +[ICC1_S:ICECUT] +Oh, stupidi, schifosi, mocciosi... + +[ICC1_T:ICECUT] +Sta zitta! + +[ICC1_U:ICECUT] +...Monellaccio! + +[ICC1_V:ICECUT] +La gelateria è solo una facciata. + +[ICC1_W:ICECUT] +Noi distribuiamo altro, prodotti non caseari. + +[ICC1_X:ICECUT] +E se vedo un bambino, non perdo occasione. + +[ICC1_Y:ICECUT] +Non è vero, bambini? Sì, sì, lo faccio. La mamma non vi ama. + +[ICC1_Z:ICECUT] +Lei vi ODIA! + +[ICC1_ZA:ICECUT] +PROPRIETÀ ACQUISTATA! + +{=================================== MISSION TABLE INTRO ===================================} + +[INT1_A:INTRO] +Tommy Vercetti... Ommerda! + +[INT1_B:INTRO] +Pensavo non lo avrebbero mai rilasciato. + +[INT1_C:INTRO] +Ha tenuto la testa bassa... molta gente ha dimenticato. + +[INT1_D:INTRO] +Ma molti ricorderanno ben presto... + +[INT1_E:INTRO] +Quando lo vedranno camminare per le strade del loro vicinato. + +[INT1_F:INTRO] +Non sarà una cosa positiva per il business. + +[INT1_G:INTRO] +Beh, che cosa facciamo, Sonny? + +[INT1_H:INTRO] +Lo trattiamo come un amico e lo teniamo impegnato fuori città, OK? + +[INT1_I:INTRO] +Parlavamo di espanderci verso sud, vero? + +[INT1_J:INTRO] +Vice City è oro puro in questi giorni. + +[INT1_K:INTRO] +I Colombiani, i Messicani, che diamine, + +[INT1_L:INTRO] +anche i rifugiati Cubani si sono ritagliati una bella fetta della torta. + +[INT1_M:INTRO] +Ma si tratta di droga, Sonny, + +[INT1_N:INTRO] +nessuno delle famiglie toccherà quella merda! + +[INT1_O:INTRO] +I tempi cambiano. + +[INT1_P:INTRO] +Le famiglie non possono girare le spalle mentre i nostri nemici si godono il raccolto. + +[INT1_Q:INTRO] +Per cui mandiamo qualcuno a fare il lavoro sporco + +[INT1_R:INTRO] +e a prendere una bella fetta per noi, OK? + +[INT1_S:INTRO] +Chi è il nostro contatto laggiù? + +[INT1_T:INTRO] +Ken Rosenberg, uno schmuck di un avvocato. + +[INT1_U:INTRO] +Come farà a tenere al guinzaglio Vercetti? + +[INT1_V:INTRO] +Non dovrà farlo. + +[INT1_W:INTRO] +Mandiamolo alla ribalta a Vice City + +[INT1_X:INTRO] +con un po' di denaro per cominciare, OK? + +[INT1_Y:INTRO] +Lasciamogli qualche mese. + +[INT1_Z:INTRO] +Poi andiamo giù, + +[INT1_A1:INTRO] +gli facciamo visita, giusto? + +[INT1_A2:INTRO] +E vediamo come se la sta cavando... + +[INT2_A:INTRO] +Ehi, ehi, amici! Sono, uh, Ken Rosenberg! Eh, eh, ottimo, ehi! + +[INT2_B:INTRO] +Bene, uh, vi accompagno in auto all'appuntamento, OK? + +[INT2_C:INTRO] +Ho parlato con i fornitori e loro sono molto, ehm, + +[INT2_D:INTRO] +desiderosi di avviare una relazione commerciale, per cui, uh, + +[INT2_E:INTRO] +se tutto dovesse andare come deve, noi, dovremmo, uh, + +[INT2_F:INTRO] +ottenere dei buoni risultati economici, il che, cioè... + +[INT2_G:INTRO] +è bene... + +[INT2_H:INTRO] +OK allora. Sono fratelli, OK. + +[INT2_I:INTRO] +Uno si occupa del, uh, business, + +[INT2_J:INTRO] +mentre l'altro si occupa dei voli. + +[INT2_K:INTRO] +Adesso operano dal Messico, + +[INT3_A:INTRO] +OK, sono loro sull'elicottero. + +[INT3_B:INTRO] +Bene, questo è l'accordo. + +[INT3_C:INTRO] +Vogliono uno scambio diretto in campo aperto. + +[INT3_D:INTRO] +Va bene? OK, nervi saldi, andiamo. + +[INT3_E:INTRO] +Vedi di star calmo. + +[INT3_F:INTRO] +Resto qui con l'auto pronta a partire! + +[INT3_G:INTRO] +Chiaro? + +[INT3_H:INTRO] +Colombiana 100% pura di prima classe, amici miei. + +[INT3_I:INTRO] +I verdoni? + +[INT3_J:INTRO] +Da dieci e da venti... usati. + +[INT3_L:INTRO] +Forza, vieni qua! Muoviti! + +[INTRO1:INTRO] +metto fuori la testa per un maledetto secondo, e il fato mi tira palate di merda in faccia! + +[INTRO2:INTRO] +Vai a riposarti. + +[INTRO3:INTRO] +Che cosa intendi fare? + +[INTRO4:INTRO] +Passerò domani a trovarti in ufficio e troveremo un modo per risolvere questo casino. + +[INT3_K:INTRO] +Siamo d'accordo allora, amico. AH AH! + +[INT3_M:INTRO] +Fammi dare un'occhiata. + +[INT2_L:INTRO] +no, no, no, aspetta... + +{=================================== MISSION TABLE KENT1 ===================================} + +[KPM1_A:KENT1] +Ehi bello, ho delle informazioni sul tuo amico. + +[KPM1_B:KENT1] +Ma di cosa stai parlando? + +[KPM1_C:KENT1] +Conosci quel fallito di Diaz, il signore degli spacciatori? + +[KPM1_D:KENT1] +Ha preso il tuo amico, Lance. Sembra che abbia cercato di scavalcarlo... + +[KPM1_E:KENT1] +ma non ha saltato abbastanza in alto, se capisci cosa intendo. + +[KPM1_F:KENT1] +Dove lo hanno portato? Senza giri di parole! + +[KPM1_G:KENT1] +Stai calmo! Lo hanno trasportato attraverso la città fino alla discarica. + +[KPM1_H:KENT1] +Maledizione... Brutto idiota. + +[KPM1_2:KENT1] +~r~Dovevi portare Lance fuori vivo da lì! + +[KPM1_3:KENT1] +SALUTE DI LANCE: + +[RESC_1:KENT1] +Ce la fai a usare un'arma? + +[RESC_2:KENT1] +Certo... credo... È bello rivederti! + +[RESC_3:KENT1] +Andiamocene da qua. + +[RESC_4:KENT1] +Bene, con questo hai mandato a puttane tutti i miei bei piani. L'hai proprio fatta grossa, Lance. + +[RESC_5:KENT1] +Ha ucciso mio fratello. Che cosa ti aspettavi facessi, che gli tagliassi il prato? + +[RESC_6:KENT1] +Dobbiamo far fuori quello stronzo di Diaz prima che sia lui a farlo. + +[RESC_7:KENT1] +Preparati e incontriamoci al ponte di Star Island, OK? + +[RESC_8:KENT1] +OK, ricevuto. + +[KPM1_1:KENT1] +~g~Lance viene tenuto nella discarica: vai a salvarlo! + +[KPM1_4:KENT1] +~g~Porta Lance all'ospedale! + +[M_PASSN:KENT1] +MISSIONE COMPLETATA! + +[KPM1_5:KENT1] +~g~Gli uomini di Diaz ti stanno dietro: porta Lance all'ospedale. + +{=================================== MISSION TABLE KICKSTT ===================================} + +[KICK1_2:KICKSTT] +~r~Non sei tornato abbastanza velocemente alla tua moto! + +[KICK1_7:KICKSTT] +~r~Hai distrutto la moto! + +[KICK1_8:KICKSTT] +~g~Ritorna sulla moto! + +[KICK1_T:KICKSTT] +TEMPO IMPIEGATO: + +[KICKTM:KICKSTT] +~b~DURATA EVENTO: ~1~:~1~ + +[KICKTM2:KICKSTT] +~b~DURATA EVENTO: ~1~:0~1~ + +[GETBIKE:KICKSTT] +~g~Hai ~1~ secondi per tornare sulla tua moto prima che la missione abbia termine. + +[KICK1_1:KICKSTT] +~g~Completa il tracciato il più velocemente possibile. + +[KICK1_6:KICKSTT] +~g~Complimenti! + +[KICK_10:KICKSTT] +~g~Usa il Sanchez per completare il percorso passando per tutti i punti di controllo. + +[KICK_12:KICKSTT] +~g~Ti sei bloccato! + +[KICK_13:KICKSTT] +~g~Ci hai messo troppo tempo! + +[KICK_11:KICKSTT] +~g~Per abbandonare la missione, posizionati a piedi sul ~q~segnalino rosa~g~. + +{=================================== MISSION TABLE LAWYER1 ===================================} + +[LAW1_A:LAWYER1] +Vai a riposarti un po', ha detto... + +[LAW1_B:LAWYER1] +...ho passato tutta la sera su questa seggiola a bere caffè con la luce spenta. + +[LAW1_C:LAWYER1] +È un disastro. Siamo veramente fottuti, amico! + +[LAW1_D:LAWYER1] +Quei gorilla, non sto scherzando, verranno qui da me e mi staccheranno la testa. È ridicolo! + +[LAW1_E:LAWYER1] +NON mi sono laureato in giurisprudenza per questo! Bene, dimmi un po': che cosa hai intenzione di fare? + +[LAW1_F:LAWYER1] +Sta zitto, siediti e rilassati. Ti dirò che cosa faremo. + +[LAW1_G:LAWYER1] +Tu dovrai scoprire chi ha preso la nostra cocaina... e io li farò fuori. + +[LAW1_H:LAWYER1] +È una buona idea. Anzi, è un'OTTIMA idea. Fammici pensare, fammici pensare, fammici pensare. + +[LAW1_I:LAWYER1] +Ah! C'è quel Colonnello in pensione, il Colonnello Juan Garcia Cortez. + +[LAW1_J:LAWYER1] +È stato lui che mi ha aiutato a preparare il piano + +[LAW1_K:LAWYER1] +ben lontano dai criminali di Vice City. OK? + +[LAW1_L:LAWYER1] +Adesso ascolta: sta organizzando un party nella baia sul suo costoso yacht + +[LAW1_M:LAWYER1] +e tutte le personalità di spicco di Vice City saranno presenti. + +[LAW1_N:LAWYER1] +Io ho un invito, chiaramente ne ho uno... + +[LAW1_O:LAWYER1] +ma non ho la minima intenzione di mettere la testa fuori dalla porta, per nessun motivo! + +[LAW1_P:LAWYER1] +Te l'ho detto, sta zitto! Ci andrò io... + +[LAW1_Q:LAWYER1] +Whoa, whoa, whoa! Ehi, anche a me piace lo stile del 78, ma, lo sai, non si tratta di una festa con birra e spogliarelliste. + +[LAW1_R:LAWYER1] +Intendo dire, senza offesa, che molta gente potrebbe girarsi a guardarti per la ragione sbagliata... + +[LAW1_S:LAWYER1] +Vuoi dire che c'è qualcosa che non va in come mi vesto? + +[LAW1_T:LAWYER1] +OK, ascolta. Fermati da Rafael's, digli che ti mando io e lui si occuperà di renderti presentabile. + +[LAW1_U:LAWYER1] +OK, forza, andiamo... + +[LAWP_1:LAWYER1] +Buenas noches. + +[LAWP_2:LAWYER1] +Da quanto ho capito, è qui per conto di Mr. Rosenberg. + +[LAWP_3:LAWYER1] +Spero che gli avvenimenti recenti non abbiano avuto effetti sulla sua salute o, uh, + +[LAWP_4:LAWYER1] +sulla sua sanità mentale, Mr...? + +[LAWP_5:LAWYER1] +Vercetti. Ha solo avuto un attacco di... agorafobia. + +[LAWP_6:LAWYER1] +Eccellente, eccellente. E lei? + +[LAWP_7:LAWYER1] +Voglio solo la mia merce. + +[LAWP_8:LAWYER1] +Ah. Si è trattato di sfortunate circostanze per tutte le persone coinvolte. + +[LAWP_9:LAWYER1] +Chiaramente, ho dato il via a delle ricerche personali + +[LAWP_10:LAWYER1] +ma una situazione così delicata richiede tempo. + +[LAWP_11:LAWYER1] +Magari ne parleremo più tardi, va bene? + +[LAWP_12:LAWYER1] +Nel frattempo, le presento mia figlia, + +[LAWP_13:LAWYER1] +Mercedes! + +[LAWP_14:LAWYER1] +Cara mia, perché non ti prendi cura del nostro ospite mentre io mi occupo delle mie cose? + +[LAWP_15:LAWYER1] +Certamente, papà. + +[LAWP_16:LAWYER1] +Mi scusi. + +[LAWP_17:LAWYER1] +Mercedes? + +[LAWP_18:LAWYER1] +Non è facile portare questo nome. + +[LAWP_19:LAWYER1] +Comunque, lasci che le presenti alcuni dei nostri distinti ospiti... + +[LAWP_20:LAWYER1] +Lui è Alex Shrub, il nostro deputato, con Candy Suxxx, la famosa star siliconata... + +[LAWP_21:LAWYER1] +Avete conosciuto la mia dolce moglie, Laura? + +[LAWP_22:LAWYER1] +Veramente questa è Candy, mia moglie si trova in Alabama ora. + +[LAWP_23:LAWYER1] +E da quella parte abbiamo la stella dei Vice City Mambas, BJ. + +[LAWP_24:LAWYER1] +un vero ammaliatore + +[LAWP_25:LAWYER1] +L'ho placcato, davvero, e l'ho fatto finire su una sedia a rotelle! + +[LAWP_26:LAWYER1] +Ah, ah, questa è buona! + +[LAWP_27:LAWYER1] +Beh, adesso sto cercando di acquistare dei terreni di qualità. + +[LAWP_28:LAWYER1] +E quella specie di anfibio da piscina è Jezz Torrent, + +[LAWP_29:LAWYER1] +voce solista dei Love Fist. + +[LAWP_30:LAWYER1] +Ve lo posso dire... sapete come giocano a ping-pong in Tailandia? + +[LAWP_31:LAWYER1] +Da non crederci... + +[LAWP_32:LAWYER1] +di certo, non richiede l'uso della racchetta, se capite cosa intendo! + +[LAWP_33:LAWYER1] +Impotente. + +[LAWP_34:LAWYER1] +E il loquace trio. + +[LAWP_35:LAWYER1] +Quella pustola sudata e addormentata è il braccio destro di papà, Gonzalez, + +[LAWP_36:LAWYER1] +mentre gli altri due sono il pastore Richards + +[LAWP_37:LAWYER1] +e il regista pseudo-intellettuale, Steve Scott. + +[LAWP_38:LAWYER1] +...pare che abbiano una passione per le ninfomani... + +[LAWP_39:LAWYER1] +...quando lo squalo gigante arriva e + +[LAWP_40:LAWYER1] +strappa a morsi i loro uccelli! + +[LAWP_41:LAWYER1] +Ah! Non si è mai visto qualcosa del genere prima, vero? + +[LAWP_42:LAWYER1] +Colonnello! + +[LAWP_43:LAWYER1] +I suoi party sono sempre un successo! Ah ah ah! + +[LAWP_44:LAWYER1] +Posso solo chiedere scusa per il mio ritardo. + +[LAWP_45:LAWYER1] +Ah, de nada amigo. E come vanno gli affari? + +[LAWP_46:LAWYER1] +Sai, sono molto difficili...i nemici sono sempre alle costole. + +[LAWP_47:LAWYER1] +Il tempo di ricompensare gli amici e liquidare i nemici, amigo. + +[LAWP_48:LAWYER1] +Chi è il chiacchierone? + +[LAWP_49:LAWYER1] +Ricardo Diaz. È Mr. Coca. + +[LAWP_50:LAWYER1] +Mercedes! + +[LAWP_51:LAWYER1] +Oh, stavo giusto riaccompagnando i miei amici in città. + +[LAWP_52:LAWYER1] +Un'altra volta, Ricardo! + +[LAWP_53:LAWYER1] +Andiamocene. + +[LAWP_54:LAWYER1] +Anzi, portatemi al club Pole Position. + +[LAW1_2:LAWYER1] +~g~Raggiungi lo yacht del Colonnello. + +[LAW1_4:LAWYER1] +~r~Hai ucciso la figlia del Colonnello! + +[LAW1_5:LAWYER1] +Lavorerai per mio padre? + +[LAW1_6:LAWYER1] +Forse. + +[LAW1_7:LAWYER1] +Ti dispiace se appoggio la mano tra le tue gambe? + +[LAW1_8:LAWYER1] +Forse... + +[LAW1_9:LAWYER1] +È così difficile avere un padre ricco e potente. Vamos. + +[LAW1_10:LAWYER1] +Ci vediamo in giro, stallone! + +[LAW1_11:LAWYER1] +Sono sicuro di sì. + +[LAW1_12:LAWYER1] +Hmmm... bella moto. + +[LAW1_13:LAWYER1] +No! La mia moto! + +[LAW1_3:LAWYER1] +~g~Porta la figlia del Colonnello al club Pole Position. + +[HELP20:LAWYER1] +Segui il ~h~segnale a forma di maglietta~w~ per trovare Rafael's. + +[LAW1_14:LAWYER1] +Accidenti, mi piace: la tua moto va alla grande. + +[LAW1_15:LAWYER1] +Eh sì, l'ho appena presa da Howlin' Pete's. + +{=================================== MISSION TABLE LAWYER2 ===================================} + +[LAW2_A:LAWYER2] +Ah! Beh, spero tu ti sia divertito. Io stavo quasi per impazzire dalla preoccupazione. Che cos'hai scoperto? + +[LAW2_B:LAWYER2] +Che ci sono più criminali a piede libero in città che nella prigione. Abbiamo bisogno di qualcuno di strada... + +[LAW2_C:LAWYER2] +OK, fammici pensare, fammici pensare, fammici pensare... + +[LAW2_D:LAWYER2] +Ah! Trovato! + +[LAW2_E:LAWYER2] +OK, c'è questo inglese, un tizio legato al campo della musica, + +[LAW2_F:LAWYER2] +mi sembra si chiami Kent Paul. + +[LAW2_G:LAWYER2] +Comunque, ha ficcato il naso nelle chiappe di metà della gente di Vice City + +[LAW2_H:LAWYER2] +per cui, se qualcuno sa dove sono questi 20 chili di coca, + +[LAW2_I:LAWYER2] +è sicuramente lui, chiaro? È sempre al Malibu. + +[LAW2_J:LAWYER2] +Andrò a trovarlo. + +[LAW2B_A:LAWYER2] +Ehi, da dove sbuchi? + +[LAW2B_B:LAWYER2] +Ho cercato per anni un fiorellino come te, bellezza... + +[LAW2B_C:LAWYER2] +Kent Paul, tesoro. Sì, sono il capo da queste parti. + +[LAW2B_D:LAWYER2] +Sto cercando un tipo inglese... + +[LAW2B_E:LAWYER2] +Risolvo i problemi, capisci cosa intendo? + +[LAW2B_F:LAWYER2] +Ti tratterò bene: qualsiasi cosa tu voglia, te la farò avere, cara. + +[LAW2B_G:LAWYER2] +Non preoccuparti di niente, bellezza. + +[LAW2B_H:LAWYER2] +Vattene, tesoro. + +[LAW2B_I:LAWYER2] +Oi oi oi oi oi! + +[LAW2B_J:LAWYER2] +Sei Kent Paul? Sono un amico di Rosenberg... + +[LAW2B_K:LAWYER2] +Rosenberg... Rosenberg... Oh, l'avvocato matto! + +[LAW2B_L:LAWYER2] +Quel tipo potrebbe difendere un innocente fino alla sedia elettrica! + +[LAW2B_M:LAWYER2] +Dacci un altro drink, amico. + +[LAW2B_N:LAWYER2] +Un altro comico. + +[LAW2B_O:LAWYER2] +Ascoltami: mi hanno soffiato venti chili e un sacco di contanti... + +[LAW2B_P:LAWYER2] +Droga amico? È roba da delinquenti. + +[LAW2B_Q:LAWYER2] +Che cosa ne sai? + +[LAW2B_R:LAWYER2] +Oi oi! Ciò che volevo dire è + +[LAW2B_S:LAWYER2] +che c'è uno chef che spaccia sottobanco nella cucina di un hotel su Ocean Drive. + +[LAW2B_T:LAWYER2] +Recentemente, mi è sembrato molto felice di come gli buttava... Potresti passare a trovarlo e investigare... + +[LAW2B_U:LAWYER2] +Lo farò... ci becchiamo in giro. + +[LAW2B_V:LAWYER2] +Beh, sì certo! Vai, vai pure idiota. Ci penserò io a tenerti a bada! + +[LAW2B_W:LAWYER2] +Dammi un bicchiere... e dov'è finita quella sgualdrina? + +[LAW2C_A:LAWYER2] +Oh, ottimo lavoro, sei proprio un duro. Riducilo in poltiglia, questo sicuramente lo aiuterà a diventare più loquace! + +[LAW2C_B:LAWYER2] +Ne vuoi anche tu? + +[LAW2C_C:LAWYER2] +Ehi, calma. Quel che va bene a te, va bene a me, amico. + +[LAW2C_D:LAWYER2] +Ah sì? E di cosa si tratta? + +[LAW2C_E:LAWYER2] +I verdoni... e l'amica bianca del mio povero fratello. Sfortunatamente, hai appena fatto fuori il nostro contatto. + +[LAW2C_F:LAWYER2] +Gli incidenti capitano. Scompari. + +[LAW2C_G:LAWYER2] +Ehi, ehi, whoa. Non c'è bisogno di recitare la parte del 'Giustiziere solitario'. + +[LAW2C_H:LAWYER2] +Io la vedo così: siamo due hombres in una strana città. Dobbiamo guardarci a vicenda le spalle. + +[LAW2C_I:LAWYER2] +Le mie spalle stanno benissimo, fratello... + +[LAW2C_J:LAWYER2] +Ne sei sicuro? Ehi, prendi questa. + +[LAW2C_K:LAWYER2] +Seguimi! + +[LAW2_1:LAWYER2] +Che cos'hai da guardare? + +[LAW2_2:LAWYER2] +Ti conviene iniziare a parlare... + +[LAW2_3:LAWYER2] +Maledetto stronzo! + +[LAW2_4:LAWYER2] +Da questa parte! + +[LAW2_5:LAWYER2] +Vediamo cosa riesco a scoprire. Ci vediamo, Tommy. + +[LAW2_6:LAWYER2] +~g~Vai al Malibu e trova Kent Paul. + +[LAW2_7:LAWYER2] +~g~Trova lo chef in Ocean Drive. + +[LAW2_10:LAWYER2] +~g~Torna in macchina all'hotel. + +[LAW2_11:LAWYER2] +~g~Raccogli il suo cellulare. + +[LAW2_12:LAWYER2] +Cellulare acquisito! Adesso potrai ricevere telefonate! + +[LAW2_13:LAWYER2] +~g~Hai lasciato Lance indietro! Vallo a prendere! + +[LAW2_14:LAWYER2] +Andiamocene da qui! + +[GUN_2A:LAWYER2] +Tieni premuto il ~h~~k~~PED_LOCK_TARGET~~w~ per la ~h~mira automatica~w~ e premi il ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~! + +[GUN_2C:LAWYER2] +Tieni premuto il ~h~~k~~PED_LOCK_TARGET~~w~ per la ~h~mira automatica~w~ e premi il ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~! + +[GUN_2D:LAWYER2] +Tieni premuto il ~h~~k~~PED_LOCK_TARGET~~w~ per la ~h~mira automatica~w~ e premi il ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~! + +[HELP17:LAWYER2] +Premi il ~h~~k~~PED_FIREWEAPON~~w~ per attaccare lo chef. + +[HELP18:LAWYER2] +Premi il ~h~~k~~PED_FIREWEAPON~~w~ per attaccare lo chef. + +[LAW3_11:LAWYER2] +Posizionati sul ~q~segnalino rosa~w~ per vedere le armi in offerta. + +[LAW3_12:LAWYER2] +Puoi selezionare l'arma premendo il ~h~tasto direzionale~w~ a ~h~sinistra~w~ o a ~h~destra~w~. + +[LAW3_13:LAWYER2] +Se hai denaro a sufficienza puoi comprare armi premendo il ~h~~k~~PED_SPRINT~~w~. + +[LAW3_14:LAWYER2] +Puoi uscire da un negozio premendo il ~h~~k~~VEHICLE_ENTER_EXIT~~w~. + +[LAW3_15:LAWYER2] +Segui il ~h~segnale a forma di pistola~w~ per trovare ~h~Ammu-Nation~w~. + +[LAW2_15:LAWYER2] +~g~Raggiungi Ammu-Nation. + +[LAW2_K:LAWYER2] +Vedi di star calmo. + +[LAW2_16:LAWYER2] +Devi sapere qualcosa su questa città: hai bisogno di potenza di fuoco. + +[LAW2_17:LAWYER2] +Forza, l'armaiolo locale è solo a qualche isolato di distanza. + +[LAW2_18:LAWYER2] +Tommy, tutti quanti hanno bisogno di un po' di relax ogni tanto. + +[LAW2_19:LAWYER2] +Ecco il club a luci rosse Pole Position. Facci un salto ogni tanto. + +{=================================== MISSION TABLE LAWYER3 ===================================} + +[LAW3_A:LAWYER3] +Aarrgh! Maledizione, sei tu! Oddio, avrò bisogno di un nuovo paio di pantaloni! + +[LAW3_B:LAWYER3] +Ehi, quei psicopatici del nord hanno drizzato le orecchie e verranno a visitarci molto presto. + +[LAW3_C:LAWYER3] +Dove diavolo sono i fottuti soldi? + +[LAW3_D:LAWYER3] +Rilassati, rilassati. Non siamo ancora arrivati a questa parte. + +[LAW3_E:LAWYER3] +Pensavo ti stessi prendendo cura di questo aspetto, davvero. + +[LAW3_F:LAWYER3] +E adesso quei delinquenti si aspettano addirittura da noi un favore. + +[LAW3_G:LAWYER3] +Vuoi dire che io devo far loro un favore. + +[LAW3_H:LAWYER3] +Oh, beh, certo, proprio così. Credi davvero che potrei intimidire una giuria? + +[LAW3_I:LAWYER3] +Non riuscirei a intimidire neppure un bambino... e credimi, ci ho provato. + +[LAW3_J:LAWYER3] +Adesso o ci riusciamo, oppure il cugino di Forelli, Giorgio, si becca cinque anni per frode. + +[LAW3_K:LAWYER3] +Devi CONVINCERE questi tipi! + +[LAW3_L:LAWYER3] +Capisco. Devo aiutare la giuria a cambiare idea. Non preoccuparti... + +[LAW3_M:LAWYER3] +No no no no NO! Ci ho provato. Le cose con la giuria non sono andate per il meglio, + +[LAW3_N:LAWYER3] +per cui ASSICURATI che cambino davvero idea. + +[LAW3_1:LAWYER3] +Giorgio ti manda i suoi saluti. + +[LAW3_2:LAWYER3] +Ricorda: colpevole è una parolaccia. + +[LAW3_3:LAWYER3] +Innocente fino a prova contraria... ecco cosa dire. + +[LAW3_4:LAWYER3] +Lo sai che non è colpevole. + +[LAW3_5:LAWYER3] +Ti ricordi di Giorgio? Ricordati che è innocente. + +[LAW3_6:LAWYER3] +Non colpevole... Capito? Bene. + +[LAW3_8:LAWYER3] +~r~Hai ucciso un giurato! + +[LAW3_9:LAWYER3] +~g~Distruggi l'auto del giurato per farlo uscire! + +[HELP40:LAWYER3] +Puoi distruggere le macchine utilizzando un martello o un'arma simile. + +[LAW3_10:LAWYER3] +~g~Visita il ~h~ferramenta~g~ per comprare un'arma corpo a corpo. + +[LAW3_20:LAWYER3] +~g~Distruggi la macchina del giurato! + +[LAW3_21:LAWYER3] +Non posso credere che sta succedendo! + +[LAW3_22:LAWYER3] +Incredibile! + +[LAW3_23:LAWYER3] +OK! OK! Ho compreso il messaggio! + +[LAW3_24:LAWYER3] +~g~Quel martello potrebbe tornare utile. + +[LAW3_7:LAWYER3] +~g~Minaccia i due giurati, ma NON ucciderli! + +[HELP23:LAWYER3] +Segui il ~h~segnale a forma di martello~w~ se vuoi comprare delle armi corpo a copro dal ferramenta. + +[LAW3_16:LAWYER3] +Stupidi idioti del Florida. + +[LAW3_17:LAWYER3] +Via dai piedi. + +{=================================== MISSION TABLE LAWYER4 ===================================} + +[LAW4_A:LAWYER4] +Avery, è chiaro che... Tommy! Tommy! Qualche progresso? No no no, parliamone dopo, mi racconti dopo. + +[LAW4_B:LAWYER4] +Tommy, ti presento Avery Carrington: credo tu lo abbia incontrato al party. + +[LAW4_C:LAWYER4] +Non di persona. + +[LAW4_D:LAWYER4] +Piacere. + +[LAW4_E:LAWYER4] +Avery ha una proposta da farci. + +[LAW4_F:LAWYER4] +Non ti sembra che abbiamo già altro da fare? + +[LAW4_G:LAWYER4] +Sto cercando di tenere lontani i lupi dalla porta, per cui apprezzerei un po' di collaborazione. + +[LAW4_H:LAWYER4] +Sono tirato come un filo e, anche se dovessi morire alla fine della settimana, desidererei non farlo da povero! + +[LAW4_I:LAWYER4] +Adesso calmatevi, tutti e due. + +[LAW4_J:LAWYER4] +Figliolo, dammi una mano e vedrò di mettere a nanna sottoterra qualsiasi stronzo vi stia dando problemi. + +[LAW4_K:LAWYER4] +OK, cosa posso fare? + +[LAW4_L:LAWYER4] +Questa società di consegne ha un deposito su un terreno di prima qualità. Non vogliono vendere. + +[LAW4_M:LAWYER4] +Si sono rintanati come dei ratti di prateria, per cui vorrei andassi a trovarli e li facessi uscire con un po' di fumo. + +[LAW4_N:LAWYER4] +Raggiungi il posto e fai scoppiare un vespaio: + +[LAW4_O:LAWYER4] +mentre la sorveglianza sarà impegnata, entra dentro e mettili fuori gioco. + +[LAW4_P:LAWYER4] +Passa pure da Rafael's per un cambio d'abito: ci potresti mettere un po', ma va bene lo stesso. + +[LAW4_Q:LAWYER4] +Potrebbe funzionare una sommossa. + +[LAW4_R:LAWYER4] +Se le cose vanno come devono, passa a trovarmi nel mio ufficio... + +[LAW4_1:LAWYER4] +Disperdetevi! L'amministrazione discuterà qualsiasi rimostranza in modo appropriato! + +[LAW4_2:LAWYER4] +Disperdetevi! Tornate a casa! + +[LAW4_3:LAWYER4] +Disperdetevi! Questo comportamento è inaccettabile! + +[LAW4_4:LAWYER4] +Disperdetevi! Rischiate di finire tutti per strada! + +[LAW4_5:LAWYER4] +Forza, ragazzi! Spacchiamo il cranio a qualche rosso! + +[LAW4_13:LAWYER4] +~g~Comincia a combattere con almeno 4 lavoratori per avviare la sommossa. + +[LAW4_14:LAWYER4] +~g~Distruggi i van nell'autorimessa! + +[HELP38:LAWYER4] +Se uccidi qualcuno con un'arma, la lascerà cadere. + +[HELP39:LAWYER4] +Puoi far esplodere i barili di esplosivo, ma tieni la distanza! + +{=================================== MISSION TABLE MIAMI_1 ===================================} + +[T4X4_1A:MIAMI_1] +~g~Hai ~1~ secondi per attraversare ~y~24~g~ punti di controllo. ~g~Puoi attraversarli in ~y~QUALSIASI ORDINE~g~. + +[T4X4_1B:MIAMI_1] +~y~ATTRAVERSA~g~ il primo punto di controllo per attivare il ~r~TIMER~g~. + +[T4X4_1C:MIAMI_1] +~1~ su 24! + +[GETBIK1:MIAMI_1] +Hai ~1~ secondi per salire su una PCJ 600! + +[GETBIK3:MIAMI_1] +~r~Hai bisogno di una PCJ 600 per affrontare questa missione! + +{=================================== MISSION TABLE MM ===================================} + +[BLOD_04:MM] +CONDIZIONI MACCHINA: + +[BLOD_05:MM] +~g~TEMPO BERSAGLIO: ~1~ minuto + +[BLOD_06:MM] +~g~TEMPO BERSAGLIO: ~1~ minuti + +[BLOD_07:MM] +NUOVO miglior tempo: ~1~ secondi + +[BLOD_08:MM] +Veicoli distrutti: ~1~ + +[BLOD_09:MM] +~1~$ + +[BLOD_10:MM] +VINCITORE!!! + +[BLOD_01:MM] +Guida attraverso i punti di controllo per aumentare il tuo tempo globale. + +[BLOD_02:MM] +Fallirai se il tempo globale scadrà a zero. + +[BLOD_03:MM] +Porta il tuo tempo globale sopra a quello bersaglio per vincere! + +{=================================== MISSION TABLE OVALRIG ===================================} + +[HOTR_01:OVALRIG] +~g~La gara dura 12 giri: solo il primo, il secondo e il terzo si qualificheranno come vincitori. + +[HOTR_02:OVALRIG] +~g~Se il tuo mezzo viene distrutto, sarai squalificato. + +[HOTR_03:OVALRIG] +~g~Se danneggi il mezzo, potrai ripararlo ai box. + +[HOTR_04:OVALRIG] +~g~Da qui potrai uscire dallo stadio. + +[HOTR_05:OVALRIG] +Condizioni macchina: + +[HOTR_06:OVALRIG] +Giri: + +[HOTR_10:OVALRIG] +Tempo di gara: + +[HOTR_09:OVALRIG] +Posizione: + +[HOTR_12:OVALRIG] +~r~La tua macchina è stata distrutta! + +[HOTR_13:OVALRIG] +~r~Non hai vinto la gara! + +[HOTR_14:OVALRIG] +~r~Sei stato squalificato! + +[HOTR_15:OVALRIG] +Tempo: ~1~:~1~ + +[HOTR_16:OVALRIG] +Tempo: ~1~:0~1~ + +[HOTR_17:OVALRIG] +Tempo migliore: ~1~:~1~ + +[HOTR_18:OVALRIG] +Tempo migliore: ~1~:0~1~ + +[HOTR_19:OVALRIG] +Tempo migliore: NA + +[HOTR_20:OVALRIG] +Nuovo tempo migliore: ~1~:~1~ + +[HOTR_21:OVALRIG] +Nuovo tempo migliore: ~1~:0~1~ + +[HOTR_22:OVALRIG] +Miglior risultato: NA + +[HOTR_23:OVALRIG] +Miglior risultato: PRIMO + +[HOTR_24:OVALRIG] +Miglior risultato: SECONDO + +[HOTR_25:OVALRIG] +Miglior risultato: TERZO + +[HOTR_26:OVALRIG] +Miglior risultato: ~1~ + +[HOTR_27:OVALRIG] +Miglior giro: ~1~.~1~ secondi + +[HOTR_28:OVALRIG] +Miglior giro: ~1~.0~1~ secondi + +[HOTR_29:OVALRIG] +~1~$ + +[HOTR_30:OVALRIG] +PRIMA POSIZIONE + +[HOTR_31:OVALRIG] +SECONDA POSIZIONE + +[HOTR_32:OVALRIG] +TERZA POSIZIONE + +[HOTR_33:OVALRIG] +Miglior giro: NA + +[HOTR_11:OVALRIG] +Nuovo giro migliore: ~1~.~1~ secondi + +[HOTR_34:OVALRIG] +Nuovo giro migliore: ~1~.0~1~ secondi + +{=================================== MISSION TABLE PHIL1 ===================================} + +[PHIL1_A:PHIL1] +Phil? + +[PHIL1_B:PHIL1] +CORRI! + +[PHIL1_C:PHIL1] +Corri! + +[PHIL1_E:PHIL1] +Merda Phil, tu bevi quella schifezza? + +[PHIL1_F:PHIL1] +Accidenti, non la devi mica bere... + +[PHIL1_G:PHIL1] +basta una sniffata per sballare. + +[PHIL1_H:PHIL1] +Ascoltami Phil, mi hai detto che potevi fornirmi delle armi... + +[PHIL1_I:PHIL1] +Stanne certo! + +[PHIL1_J:PHIL1] +C'è un trafficante d'armi Messicano con cui ho fatto un po' di affari. + +[PHIL1_K:PHIL1] +Fa la sua consegna settimanale più o meno adesso. + +[PHIL1_L:PHIL1] +Sperona il suo camion e fai cadere le armi prima che scappi. + +[PHIL1_M:PHIL1] +E già che ci sei, fammi un favore: + +[PHIL1_N:PHIL1] +fallo fuori. + +[PHI1_01:PHIL1] +~g~Fai cadere le armi dal camion del trafficante. + +[PHI1_02:PHIL1] +~g~Il trafficante ha lasciato cadere il carico. Distruggi le casse e recupera le armi. + +[PHI1_03:PHIL1] +~g~Sembra abbiano chiesto rinforzi. + +[PHI1_04:PHIL1] +~g~Adesso finisci i trafficanti ancora vivi. + +[PHI1_HP:PHIL1] +Quando utilizzi una granata a tempo, lanciala e poi falla esplodere in un secondo momento. + +[PHIL1_O:PHIL1] +Hoooooweeeeee! + +[PHIL1_D:PHIL1] +Nona avvicinare mai una fiamma alla broda di Phil Cassidy! + +{=================================== MISSION TABLE PHIL2 ===================================} + +[PHIL2_A:PHIL2] +Ehi Phil, come butta? + +[PHIL2_B:PHIL2] +Eeeeehi Tommy, Come va? E passcato moolto tempo... + +[PHIL2_C:PHIL2] +Devi davvero smetterla con quella broda, + +[PHIL2_D:PHIL2] +accidenti, puzza di solvente. Mi fa bruciare gli occhi... + +[PHIL2_E:PHIL2] +Shshs shhh silenzio Tommy, + +[PHIL2_F:PHIL2] +e vieni qua che ho qualcoscia da farti vedere... qualcoscia... + +[PHIL2_G:PHIL2] +Woof! Oddio! Riuscirei a sniffarlo da qui in fondo! Mi gira già la testa! + +[PHIL2_H:PHIL2] +Non preoccuparti per l'odore, Tommy, guarda quescto... + +[PHIL2_I:PHIL2] +La fottuta batteria o qualcosc'altro... Scen'è un altra sulla mensola... + +[PHIL2_J:PHIL2] +TA-DAAA! + +[PHIL2_K:PHIL2] +Ommerda! + +[PHI2_01:PHIL2] +~g~Forza, porta Phil all'ospedale. + +[PHI2_03:PHIL2] +~r~Phil Cassidy è morto!!! Adesso chi ti rifornirà di armi? + +[PHI2_05:PHIL2] +Non all'ospedale, amico! Troppi poliziotti e Viet Cong! + +[PHI2_06:PHIL2] +C'è un vecchio ex-medico militare che mi deve qualche favore e una falciatrice. + +[PHI2_07:PHIL2] +Si trova lungo Little Havana, oooh guarda, un pesce gigante. + +[PHI2_08:PHIL2] +Attento! Nemico tra gli alberi! + +[PHI2_09:PHIL2] +Mi sbaglio o le strade sono di gelatina? + +[PHI2_10:PHIL2] +Cucchiaio Storto a Mamma Gallina, mi ricevi? + +[PHI2_11:PHIL2] +Cucchiaione-ione Woo Woo Woooo! + +[PHI2_12:PHIL2] +Sta venendo per me, amico! + +[PHI2_13:PHIL2] +Piume nere che svolazzano dappertutto... + +[PHI2_14:PHIL2] +È così bello, amico... così bello... ma così freddo... + +[PHI2_15:PHIL2] +10-4, abbiamo un guidatore ubriaco. + +[PHI2_04:PHIL2] +SALUTE DI PHIL: + +[PHI_AS1:PHIL2] +PHILS PLACE COMPLETATO + +[PHI_AS2:PHIL2] +~g~Nuove armi disponibili presso Phils Place. + +{=================================== MISSION TABLE PIZZA ===================================} + +[PIZ1_01:PIZZA] +~g~Vai a consegnare queste pizze: devi lanciarle ai clienti. Fai un giro e tira le pizze. + +[PIZ1_02:PIZZA] +~g~Hai lanciato tutte le pizze, torna indietro e prendine altre. + +[PIZ1_05:PIZZA] +~g~Hai cinque minuti per consegnare le pizze prima che i clienti chiamino un altro negozio di consegne. + +[PIZ1_07:PIZZA] +~r~Hai ucciso un cliente! Sei licenziato! + +[PIZ1_08:PIZZA] +~r~Tempo scaduto. Sei licenziato! + +[PIZ1_09:PIZZA] +~r~Hai distrutto il nostro mezzo! Sei licenziato! + +[PIZ1_11:PIZZA] +Ehi! Rimettiti in sella! + +[PIZ1_12:PIZZA] +Pizze mancanti: + +[PIZ1_06:PIZZA] +Premi il ~h~~k~~TOGGLE_SUBMISSIONS~~w~ mentre sei in moto per interrompere la missione. + +[PIZ1_13:PIZZA] +Consegnale belle calde e fragranti. + +[PIZ1_14:PIZZA] +Amico, eccoti le pizze. + +[PIZ1_15:PIZZA] +Ehi, amico, consegnale in fretta. + +[PIZ1_16:PIZZA] +Che cosa stai aspettando, amico? Hai delle pizze da consegnare! + +[PIZ1_17:PIZZA] +So che non volevi fare il fattorino delle pizze, ma non me ne frega niente. + +[PIZ1_18:PIZZA] +Consegna queste. + +[PIZ1_19:PIZZA] +Devi consegnare queste. + +[PIZ1_20:PIZZA] +Forza, ragazzo, consegna queste o sei licenziato. + +[PIZ1_21:PIZZA] +C'è gente in attesa, amico. + +[PIZ1_22:PIZZA] +Che cosa stai aspettando? Devono essere consegnate! + +[PIZ1_23:PIZZA] +Consegna queste maledette pizze. + +[PIZ1_24:PIZZA] +Forza, consegnale calde. + +[PIZ1_25:PIZZA] +Ehi, ti occuperesti di queste? + +[PIZ1_26:PIZZA] +Ehi, consegna subito queste: forza amico. + +[PIZ1_27:PIZZA] +Forza, siamo di fretta: consegnale subito. + +[PIZ1_28:PIZZA] +Ancora tu? Bene, consegnale in fretta, amico. + +[PIZ1_29:PIZZA] +Non c'è tempo da perdere, consegnale tutte. + +[PIZ1_30:PIZZA] +Muoviti, brutto pelandrone, consegna questa roba in tempo. + +[PIZ1_31:PIZZA] +Non otterrai una promozione se non ti muoverai più in fretta. + +[PIZ1_32:PIZZA] +~r~La pizza è troppo calda per te? + +[PIZ1_33:PIZZA] +~g~Torna alla pizzeria per altre ordinazioni. + +[PIZ1_34:PIZZA] +~g~Pizze consegnate, ecco i soldi. + +[PIZ_WON:PIZZA] +Missione Pizza completata. Valore massimo dell'salute aumentato a 150. + +{=================================== MISSION TABLE PORN1 ===================================} + +[POR1_A:PORN1] +Azione! + +[POR1_B:PORN1] +Whoa! Questo sì che è un bel... + +[POR1_C:PORN1] +30 centimetri, è questa la norma, baby. + +[POR1_D:PORN1] +TAGLIA! Chi è quell'idiota? Tu! TU! Che cosa stai facendo sul mio set? PERCHÉ? + +[POR1_E:PORN1] +Cosa sono tute queste stronzate? + +[POR1_F:PORN1] +Alieni? Canne da pesca? + +[POR1_G:PORN1] +Chi ha mai visto uno squalo così grande? + +[POR1_H:PORN1] +Tutta questa roba deve andarsene. + +[POR1_I:PORN1] +Perché sei venuto a rompere, idiota? + +[POR1_J:PORN1] +Eh? + +[POR1_K:PORN1] +Per la passera, ecco perché! Che cos'è questo? + +[POR1_L:PORN1] +Questa è la mia arte. SORVEGLIANZA! + +[POR1_M:PORN1] +Ascolta, lurido fighettino, ti ho comprato. Ho comprato tutto questo. + +[POR1_N:PORN1] +E ho intenzione di cambiare le cose da queste parti... + +[POR1_O:PORN1] +Ti farò ricco. + +[POR1_P:PORN1] +Uh. Tu sei... tu... tu sei Tommy Vercetti? Pensavo che tu fossi... + +[POR1_Q:PORN1] +Esatto. + +[POR1_R:PORN1] +Faremo numerosi cambiamenti da queste parte e inizieremo a fare soldi veri. + +[POR1_S:PORN1] +Effettivamente, hai mai pensato che... + +[POR1_T:PORN1] +Ma prima dobbiamo trovare delle tope che meritino. + +[POR1_U:PORN1] +Sì. Le ragazze sono carine, ma sai... Wow! + +[POR1_02:PORN1] +~g~Metti fuori gioco il pappone di Candy e poi passa a prenderla. + +[POR1_04:PORN1] +Yo, Candy. Sto cercando delle attrici talentuose: sei interessata? + +[POR1_05:PORN1] +Certo! Ma prima dovrai parlare con il mio agente... + +[POR1_06:PORN1] +Che DIAVOLO stai facendo? + +[POR1_07:PORN1] +Avresti fatto meglio a stare a casa oggi! + +[POR1_7B:PORN1] +Ma hai visto questo stronzo? + +[POR1_08:PORN1] +Ehi Mercedes! + +[POR1_09:PORN1] +Ehi Tommy, vuoi divertirti? + +[POR1_10:PORN1] +Non adesso, tesoro. Sei interessata a fare dei film? + +[POR1_11:PORN1] +Certo, sempre che possano premiare il mio talento. + +[POR1_13:PORN1] +~g~Porta le ragazze allo studio e presentale a Steve. + +[POR1_14:PORN1] +Sei assunta! + +[POR1_15:PORN1] +Ehi Tommy, vieni anche tu per un riscaldamento? + +[POR1_17:PORN1] +Whoa, che bello squalo! + +[POR1_18:PORN1] +~r~Mercedes è morta! + +[POR1_20:PORN1] +Tommy, dove stai andando? Torna qui! + +[POR1_21:PORN1] +Dove stai andando? + +[POR1_22:PORN1] +Tommy, quando passeremo un po' di tempo io e te da soli? + +[POR1_01:PORN1] +~g~Candy Suxxx sarebbe perfetta per il ruolo da protagonista! + +[POR1_12:PORN1] +~g~Porta Candy con te all'appuntamento con Mercedes. + +[POR1_16:PORN1] +Magari più tardi... + +[POR1_24:PORN1] +~g~Torna indietro e prendi Candy. + +[POR1_25:PORN1] +~g~Hai lasciato Candy indietro, torna a prenderla. + +[POR1_23:PORN1] +~g~Candy starà lavorando in ~h~Downtown~g~. + +[POR1_26:PORN1] +~g~Ecco Candy, sembra sia stata ancora con il deputato Shrub. + +[POR1_27:PORN1] +Forza, andiamo. + +[POR1_28:PORN1] +Tommy, fai attenzione! Il mio seno siliconato non è ancora assicurato! + +[POR1_29:PORN1] +È questo il modo di guidare? + +[POR1_30:PORN1] +Non posso recitare dopo tutto questo! + +[POR1_31:PORN1] +Cosa? Stai cercando di uccidermi? Credevo di essere una star! + +{=================================== MISSION TABLE PORN2 ===================================} + +[POR2_A:PORN2] +Come stanno andando le riprese, Steve? + +[POR2_B:PORN2] +Beh, Candy è un talento naturale e l'altra ragazza... è insaziabile! + +[POR2_C:PORN2] +Si è passata metà del cast e dello staff prima ancora che iniziassi a fare i controlli delle luci. + +[POR2_D:PORN2] +Comunque, eh, domani andiamo fuori a registrare le scene sulla barca... + +[POR2_E:PORN2] +Scene sulla barca? Quali scene sulla barca? + +[POR2_F:PORN2] +I pescatori sono presi dalla passione quando lo squalo gigante arriva e... + +[POR2_G:PORN2] +Che cosa ho detto sullo squalo gigante? + +[POR2_H:PORN2] +Ho detto: 'NIENTE SQUALO GIGANTE', tutto chiaro? + +[POR2_I:PORN2] +Lascia semplicemente le telecamere puntate sull'azione! + +[POR2_J:PORN2] +OK, OK. Ehi Tommy, ci devo pure provare, no? + +[POR2_K:PORN2] +Hai fatto stampare quei volantini? + +[POR2_L:PORN2] +Sì, ma nessuno ci farà distribuire quella roba, cioè... + +[POR2_M:PORN2] +Lasciano semplicemente troppo, uh... poco all'immaginazione. + +[POR2_N:PORN2] +Non preoccuparti per quello. + +[POR2_O:PORN2] +Ho le mie idee sulla distribuzione. + +[POR2_P:PORN2] +OK. Ehi Candy, uh, nella mia roulotte. + +[POR2_01:PORN2] +~g~C'è un idrovolante che è stato utilizzato nelle scene di alcuni vecchi film di Indy nel retro degli studi. + +[POR2_02:PORN2] +~g~Passa sopra uno dei punti di controllo per cominciare a distribuire volantini. + +[POR2_03:PORN2] +~g~Lancia i volantini fino alla fine del punto di controllo. + +[POR2_04:PORN2] +~r~CARBURANTE AL MINIMO!!! + +[POR2_05:PORN2] +Usalo per distribuire i volantini per la città. + +[DILDO:PORN2] +Carburante: + +[POR2_Q:PORN2] +Accidenti. + +[PORN2_9:PORN2] +~g~Hai ~1~ secondi per tornare sulla tua Skimmer prima che la missione abbia termine. + +{=================================== MISSION TABLE PORN3 ===================================} + +[POR3_A:PORN3] +OK, qual è il problema adesso? + +[POR3_B:PORN3] +SSShhhh! + +[POR3_C:PORN3] +Beh, dopo il suo incontro ravvicinato con le ninfo-invasori, + +[POR3_D:PORN3] +il nostro eroe non riesce a pensare ad altro che a questa immensa montagna fallica... + +[POR3_E:PORN3] +ed è qui che vogliamo girare la scena con la tinozza piena di puré, ma poi noi... + +[POR3_F:PORN3] +Non me ne può fregar di meno, + +[POR3_G:PORN3] +Basta che continui, continui, continui. + +[POR3_H:PORN3] +Ehi Tommy... + +[POR3_I:PORN3] +mi hai accennato qualcosa al telefono riguardo dei problemi legali... + +[POR3_J:PORN3] +Ah sì! Il deputato Alex Shrub, lanciato verso la cattura di nuovi elettori, ha deciso di conquistare i puritani. + +[POR3_K:PORN3] +Si mormora che voglia supportare delle misure per limitare, diciamo, + +[POR3_L:PORN3] +l'aspetto più carnale della grande industria nazionale dell'intrattenimento. + +[POR3_M:PORN3] +Ottimo. + +[POR3_N:PORN3] +Candy! Conosci Shrub, + +[POR3_O:PORN3] +voi ragazzi vi siete lanciati in qualcosa di perverso? + +[POR3_P:PORN3] +Oh sì, oh sì, oh sì! Sì sì sì SÌ OOOoooh! + +[POR3_Q:PORN3] +Ti prego, dimmi che lo hai ripreso. + +[POR3_R:PORN3] +Faceva parte del... uh... o stava parlando con... + +[POR3_S:PORN3] +Ehi, non saprei dire. Comunque... + +[POR3_T:PORN3] +Ti consiglio vivamente di seguirla dopo le riprese, + +[POR3_U:PORN3] +vedi se va dritta al suo nuovo nido d'amore. + +[POR3_V:PORN3] +Hai una macchina fotografica? + +[POR3_X:PORN3] +Certo, dategliene una. + +[POR3_02:PORN3] +~r~Hai ucciso il deputato! Adesso come farai a ricattarlo? + +[POR3_03:PORN3] +~r~Hai allarmato la sorveglianza del deputato: lo porteranno immediatamente via. + +[POR3_04:PORN3] +Candy, mi chiameresti Martha? + +[POR3_05:PORN3] +Oh Alex, cioè Martha. Tutto ciò che vuoi! + +[POR3_06:PORN3] +Martha, qualcuno ci sta osservando... che cosa perversa. + +[POR3_07:PORN3] +Ehi tu. Dammi quella macchina fotografica! + +[POR3_01:PORN3] +~g~Segui la ~h~limousine~g~ di Candy. + +[POR3_15:PORN3] +~r~Hai distrutto la limousine di Candy! + +[POR3_17:PORN3] +~g~Torna allo studio con il rullino. + +[POR3_19:PORN3] +~r~Hai finito il rullino! + +[POR3_21:PORN3] +~g~Hai perso la limousine di Candy! + +[POR3_22:PORN3] +~g~L'hotel WR Chariot di fronte al balcone dovrebbe essere un luogo ideale per scattare le foto. + +[POR3_23:PORN3] +~g~C'è un ingresso laterale che ti permetterà di entrare nell'hotel. + +[POR3_08:PORN3] +Tieni premuto il ~h~~k~~PED_LOCK_TARGET~~w~ per ~h~mirare~w~ con la macchina fotografica. + +[POR3_09:PORN3] +Tieni premuto il ~h~~k~~PED_LOCK_TARGET~~w~ per ~h~mirare~w~ con la macchina fotografica. + +[POR3_10:PORN3] +Premi il ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ per ~h~zoomare~w~ con la macchina fotografica e il ~h~~k~~PED_SNIPER_ZOOM_OUT~~w~ per ~h~allargare il campo~w~. + +[POR3_11:PORN3] +Premi il ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ per ~h~zoomare~w~ con la macchina fotografica e il ~h~~k~~PED_SNIPER_ZOOM_OUT~~w~ per ~h~allargare il campo~w~. + +[POR3_12:PORN3] +Premi il ~h~~k~~PED_FIREWEAPON~~w~ per scattare una foto. + +[POR3_13:PORN3] +Premi il ~h~~k~~PED_FIREWEAPON~~w~ per scattare una foto. + +[POR3_14:PORN3] +Premi il ~h~~k~~PED_FIREWEAPON~~w~ per scattare una foto. + +[POR3_20:PORN3] +~g~Se hai bisogno di un mezzo, usa lo ~h~Sparrow~g~ sul retro. + +[POR3_16:PORN3] +~g~Hai bisogno di tre foto di qualità di Alex Shrub con Candy. + +[POR3_24:PORN3] +FOTO SCATTATE: + +{=================================== MISSION TABLE PORN4 ===================================} + +[POR4_A:PORN4] +Mi dispiace, ma adesso proprio non riesco ad ingoiarlo. + +[POR4_B:PORN4] +Oh EDDAI tesoro! + +[POR4_C:PORN4] +È inalberato come un capodoglio, accidenti, + +[POR4_D:PORN4] +come fai a non farti coinvolgere dalla parte? + +[POR4_E:PORN4] +Ma Stevie... + +[POR4_F:PORN4] +Come sta andando il mio regista? + +[POR4_G:PORN4] +Oh, accidenti. Lo scontro tra l'integrità artistica e + +[POR4_H:PORN4] +i sobbalzi e le pompanti azioni continua senza sosta. + +[POR4_I:PORN4] +E, prima che tu me lo chieda, sì, tutti e quattro i film saranno pronti per... + +[POR4_J:PORN4] +Dolcezza, potresti PER FAVORE tenere l'anaconda nell'inquadratura, + +[POR4_K:PORN4] +mi costa più lei all'ora di te! + +[POR4_L:PORN4] +Oh, scusa Steve. + +[POR4_M:PORN4] +Pensavo, abbiamo bisogno di qualche grossa acrobazia per promuovere il lancio. + +[POR4_N:PORN4] +Qualcosa che farà davvero impatto sulla città... ti viene in mente niente? + +[POR4_O:PORN4] +Beh, nei vecchio tempi si facevano le feste di gala, + +[POR4_P:PORN4] +attori, limousine, il cielo notturno illuminato dai riflettori... + +[POR4_Q:PORN4] +Riflettori? Mi è venuta un'idea... + +[POR4_R:PORN4] +...sì, sì, sì. I vestiti pieni di lustrini, e le limousine, oh, le prime visioni... + +[POR4_S:PORN4] +Oh, sì signora, certo signora, + +[POR4_T:PORN4] +e la stampa, e gli sbarramenti di luci... + +[POR4_01:PORN4] +~g~Raggiungi ~y~Downtown~g~ e posiziona i riflettori sopra l'edificio. + +[POR4_02:PORN4] +~g~Avrai bisogno di una moto veloce per saltare da un tetto all'altro. La guardia della sorveglianza possiede una ~y~PCJ 600~g~... + +[POR4_03:PORN4] +~g~Devi raggiungere i tetti degli edifici. Ci dovrebbe essere un ascensore in uno degli uffici ai piani superiori... + +[POR4_06:PORN4] +~g~Torna all'ufficio ai piani inferiori se vuoi accedere nuovamente ai tetti. + +[POR4_07:PORN4] +~g~Avrai bisogno di una moto per saltare da un edificio all'altro. + +[POR4_08:PORN4] +~g~Lanciati attraverso la finestra per avviare il percorso. Avrai tempo fino alle 07:00 prima che diventi troppo luminoso per muoverti non visto. + +[POR4_09:PORN4] +~g~I bonus ti mostreranno il successivo edificio su cui saltare. + +[POR4_10:PORN4] +~r~È troppo tardi per arrivare in cima non visto. + +[POR4_11:PORN4] +Ritorna alla scala se vuoi accedere nuovamente ai tetti. + +[POR4_05:PORN4] +~g~Queste scale portano a un ufficio ai piani inferiori. + +[POR_AS1:PORN4] +STUDIO CINEMATOGRAFICO COMPLETATO + +[POR_AS2:PORN4] +~g~L'Inter Global Films d'ora in poi genererà introiti per un massimo di ~1~$. Ricordati di recuperarli regolarmente. + +{=================================== MISSION TABLE PROT1 ===================================} + +[PRO1_B:PROT1] +Non sopporto più questo aspetto. Tommy, che ne dici? E se mettessimo un bar... + +[PRO1_D:PROT1] +Ascoltami, + +[PRO1_E:PROT1] +è giunta l'ora di prendere in mano questa città. È tutto pronto per questo. + +[PRO1_F:PROT1] +Dobbiamo cominciare a impadronirci del territorio + +[PRO1_G:PROT1] +e far sapere a Vice City che ci sono nuovi giocatori in città, mi segui? + +[PRO1_I:PROT1] +Tommy, ciò di cui hai bisogno è una facciata legale, delle proprietà. A me ha sempre fatto comodo. + +[PRO1_J:PROT1] +Dobbiamo iniziare a mostrare i denti o tutto questo lavoro sarà stato vano. + +[PRO1_K:PROT1] +I gruppi locali sanno che Diaz è morto e si rifiutano di pagare per la protezione! + +[PRO1_L:PROT1] +Potremmo provare con la corruzione... + +[PRO1_M:PROT1] +Corruzione? A fanculo la corruzione! Ti mostrerò io come si fa a spaventarli. + +[PRO1_01:PROT1] +~g~Assali i negozi e fuggi: i proprietari chiederanno la tua protezione. + +[PRO1_03:PROT1] +~r~Si trattava di un assali e fuggi, non di un assali e bevi un caffè. + +[PRO1_04:PROT1] +Tutti i miei beni, distrutti! + +[PRO1_05:PROT1] +Rovinato... ROVINATO! + +[PRO1_06:PROT1] +Maledizione, devo pagare la protezione! + +[PRO1_07:PROT1] +La mia meravigliosa vetrina! + +[PRO1_08:PROT1] +Il mio negozio, il mio bellissimo negozio. + +[PRO1_09:PROT1] +Vercetti, ricorda questo nome. + +[PRO1_10:PROT1] +Adesso gestisco io questa città. IO! + +[BUYP1:PROT1] +Non puoi comprare proprietà in certe aree della mappa. + +[BUYP2:PROT1] +Se vedi un'icona a forma di casa verde, puoi comprare quella proprietà. + +[PRO1_N:PROT1] +Sarò di ritorno fra cinque minuti... + +[PRO1_11:PROT1] +~g~Raggiungi il ~y~Mall North Point~g~ in ~y~Vice Point~g~. + +[PRO1_12:PROT1] +~g~Distruggi le vetrine di tutti i negozi e i proprietari saranno pronti a chiederti protezione. + +[PRO1_A:PROT1] +Oh, dobbiamo ridecorare questo posto, deve sembrare più vecchio. + +[PRO1_C:PROT1] +Sei il mio avvocato, Rosenberg, non il mio arredatore. Chiaro? + +[BUYP3:PROT1] +Posizionati all'interno del simbolo e premi il tasto ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la proprietà. + +[PRO1_13:PROT1] +~g~Hai cinque minuti per distruggerli tutti. + +{=================================== MISSION TABLE PROT2 ===================================} + +[PRO2_A:PROT2] +Qual è il problema? + +[PRO2_B:PROT2] +Alcuni bar si rifiutano di pagare. + +[PRO2_C:PROT2] +Dicono di essere protetti da una gang locale di malviventi. + +[PRO2_D:PROT2] +Ma non preoccuparti, Tommy, me ne occupo io. + +[PRO2_E:PROT2] +E tu te ne occupi in questo modo? + +[PRO2_F:PROT2] +Voi due, alzate il culo... + +[PRO2_G:PROT2] +Andiamo. + +[PRO2_01:PROT2] +~g~Fai fuori le guardie che sorvegliano il Front Page Bar e scopri chi le ha rifornite. + +[PRO2_10:PROT2] +~g~Altri due sono riusciti a scappare. Rintracciali e finisci questa storia. + +[PRO2_11:PROT2] +Entra in macchina, idiota. + +[PRO2_02:PROT2] +La tua protezione ha bisogno di più protezione. + +[PRO2_03:PROT2] +Maledizione, non ancora! Non ne voglio sapere! + +[PRO2_04:PROT2] +Questi idioti operano per la DBP Security, con sede dietro all'isolato. + +[PRO2_05:PROT2] +Vedete di risolverla per i fatti vostri. + +[PRO2_06:PROT2] +Ci si vede più tardi. + +[PRO2_07:PROT2] +Sì, sì, certo. + +[PRO2_08:PROT2] +~g~La DBP Security presto scoprirà le tue intenzioni: attacca prima che si ritiri. + +[PRO2_09:PROT2] +~g~Vai a parlare con il proprietario del Front Page. + +{=================================== MISSION TABLE PROT3 ===================================} + +[PRO3_A:PROT3] +Cretino! Ma a cosa pensavi? + +[PRO3_B:PROT3] +Ti rendi conto che cosa hai combinato? + +[PRO3_C:PROT3] +Potremmo essere tutti rovinati! + +[PRO3_D:PROT3] +Il timer deve essersi inceppato. + +[PRO3_E:PROT3] +Quel posto era stato minato per saltare come una fabbrica di fuochi d'artificio. + +[PRO3_F:PROT3] +Ma qualcuno ha informato i poliziotti... + +[PRO3_G:PROT3] +Qual è il problema, ragazzi? + +[PRO3_H:PROT3] +Mike doveva dar fuoco a un locale del Mall, + +[PRO3_I:PROT3] +ma ha fatto casino con i fusibili e adesso la polizia sta indagando. + +[PRO3_J:PROT3] +Dobbiamo preparare la nostra roba e andarcene da qui! + +[PRO3_K:PROT3] +Rilassatevi, tutti e due: fatemi pensare per un attimo! + +[PRO3_L:PROT3] +Tommy Vercetti non scappa con la coda fra le gambe. + +[PRO3_M:PROT3] +I poliziotti passeranno al setaccio l'intero edificio, giusto? + +[PRO3_N:PROT3] +Ma tutto ciò richiede tempo. + +[PRO3_O:PROT3] +Dobbiamo entrare e dar fuoco noi stessi al locale. + +[PRO3_P:PROT3] +Sì, ma... + +[PRO3_Q:PROT3] +Solo un poliziotto potrebbe avvicinarsi a meno di un chilometro dal luogo! + +[PRO3_R:PROT3] +Allora andiamo come poliziotti. + +[PRO3_S:PROT3] +Recupereremo delle uniformi... e avremo bisogno di una volante. + +[PRO3_T:PROT3] +Tutto questo grazie a te, Mike. + +[PRO3_U:PROT3] +Mi dispiace. + +[PRO3_V:PROT3] +Ho capito. + +[PRO3_W:PROT3] +Quello che dobbiamo fare è attirare i poliziotti con un gestaccio, + +[PRO3_X:PROT3] +Metterli con le spalle al muro + +[PRO3_Y:PROT3] +e sopraffarli. + +[PRO3_Z:PROT3] +Ottimo piano, andiamo! + +[PRO3_A1:PROT3] +Bene. + +[PRO3_01:PROT3] +OK Lance, attira l'attenzione dei poliziotti! + +[PRO3_02:PROT3] +~g~Prendi la macchina della polizia e vai a posizionare la bomba nel Tarbrush Café dentro al Mall. + +[PRO3_03:PROT3] +~g~Hai lasciato Lance indietro, torna a prenderlo. + +[PRO3_04:PROT3] +~g~Andiamo. + +[PRO3_05:PROT3] +~r~Hai ucciso Lance! + +[PRO3_07:PROT3] +~g~È saltata la vostra copertura. Muoviti a piazzare la bomba! + +[PRO3_09:PROT3] +Legali e imbavagliali! + +[PRO3_10:PROT3] +Oooh. Mi calza a pennello! + +[PRO3_11:PROT3] +Un po' stretto sul pacco... + +[PRO3_12:PROT3] +Oh sì, hai ragione, anche a me! + +[PRO3_13:PROT3] +Tranquillo fratello: nessun poliziotto guida così male! + +[PRO3_14:PROT3] +Ricorda: sorridi agli altri poliziotti! + +[PRO3_15:PROT3] +Ehi agente, bel distintivo, bel distintivo. + +[PRO3_16:PROT3] +Un po' più sciolto, Lance. + +[PRO3_17:PROT3] +OK, i timer sono pronti, 5 secondi alla scadenza. + +[PRO3_18:PROT3] +5 secondi? Dobbiamo scappare di corsa da qua! + +[PRO3_19:PROT3] +Questo deve davvero averli irritati. + +[PRO3_20:PROT3] +~g~Fatevi inseguire dai due poliziotti nel garage. + +[PRO3_21:PROT3] +~g~Ottieni un livello di sospetto così i poliziotti ti inseguiranno fino al garage. + +[PRO3_22:PROT3] +~g~La porta del garage è bloccata! Liberala così potrai chiuderla. + +[PRO3_23:PROT3] +~g~Cammina sul segnalino per piazzare la bomba. + +[PRO3_24:PROT3] +~g~Allontanati dal Café! + +[PRO_AS1:PROT3] +CIRCOLO DI PROTEZIONE COMPLETATO + +[PRO_AS2:PROT3] +~g~La proprietà di Vercetti d'ora in poi genererà introiti per un massimo di ~1~$. Ricordati di recuperarli regolarmente. + +[PRO3_08:PROT3] +~g~Torna alla ~h~proprietà di Vercetti~g~ su ~h~Starfish Island~g~. + +{=================================== MISSION TABLE RACES ===================================} + +[RACES_2:RACES] +~g~Hai bisogno di un veicolo per partecipare, non è mica una maratona! + +[RACES_3:RACES] +3..2..1.. VIA VIA VIA! + +[RACES_8:RACES] +~r~Non hai vinto la corsa! + +[RACES00:RACES] +Corsa ~1~: + +[RACES01:RACES] +Terminal Velocity + +[RACES02:RACES] +Ocean Drive + +[RACES03:RACES] +Border Run + +[RACES04:RACES] +Capital Cruise + +[RACES05:RACES] +Tour! + +[RACES06:RACES] +V.C. Endurance + +[RACES07:RACES] +Costo d'ingresso: ~1~$ + +[RACES08:RACES] +Tempo migliore: ~1~:~1~ + +[RACES09:RACES] +Miglior risultato: PRIMO + +[RACES10:RACES] +Miglior risultato: SECONDO + +[RACES11:RACES] +Miglior risultato: TERZO + +[RACES12:RACES] +Miglior risultato: QUARTO + +[RACES13:RACES] +Lunghezza tracciato: ~1~.~1~ km + +[RACES15:RACES] +Miglior tempo: ND + +[RACES16:RACES] +Miglior risultato: ND + +[RACES18:RACES] +HAI VINTO: ~1~$ + +[RACES19:RACES] +Non puoi permetterti di partecipare a questa gara. + +[RACES22:RACES] +Tempo migliore: ~1~:0~1~ + +[RACES23:RACES] +Lunghezza tracciato: ~1~.~1~ miglia + +[RACES_1:RACES] +~g~Recupera un veicolo veloce e raggiungi la griglia di partenza. + +[RACEHLP:RACES] +~w~Premi il ~h~~k~~PED_SPRINT~~w~ per avviare la corsa selezionata. ~w~Premi il ~h~~k~~VEHICLE_ENTER_EXIT~~w~ per uscire. + +{=================================== MISSION TABLE RCHELI1 ===================================} + +[WRECKED:RCHELI1] +~r~Il veicolo è a pezzi! + +[RCH1_4:RCHELI1] +Punti di controllo rimanenti: + +[RCH1_6:RCHELI1] +~g~Usa l'elicottero radiocomandato per attraversare i punti di controllo nella zona dell'aeroporto. + +[RCH1_7:RCHELI1] +~g~In totale ci sono 20 punti di controllo. + +[RCH1_12:RCHELI1] +~r~L'elicottero radiocomandato si sta allontanando dal raggio d'azione! + +[RCH1_13:RCHELI1] +~r~L'elicottero radiocomandato è fuori portata! + +[RCH1_8:RCHELI1] { reVC update } +~g~Se desideri interrompere la missione, premi il ~h~~k~~VEHICLE_FIREWEAPON~~g~ per far esplodere l'elicottero. + +{=================================== MISSION TABLE RCPLNE1 ===================================} + +[RCPL1_4:RCPLNE1] +~g~Sfida nella GARA A TAPPE altri 3 aerei radiocomandati. + +[RCPL1_5:RCPLNE1] +~g~Vola attraverso i punti di controllo in Vice City. + +[RCPL1_6:RCPLNE1] { reVC update } +~g~Se desideri interrompere la missione, premi il ~h~~k~~VEHICLE_FIREWEAPON~~g~ per far esplodere l'aereo. + +[RCPL1_8:RCPLNE1] +~g~L'aereo radiocomandato è quasi fuori portata! + +[RCPL1_9:RCPLNE1] +~r~L'aereo radiocomandato è fuori portata! + +{=================================== MISSION TABLE RCRACE1 ===================================} + +[RCR1_4:RCRACE1] +Giri rimanenti: + +[RCR1_1:RCRACE1] +~g~Sfida in una gara a tappe altre 3 macchine radiocomandate. + +[RCR1_2:RCRACE1] +~g~Per vincere, completa per primo due giri del tracciato! + +[RCR1_6:RCRACE1] +~g~La macchina radiocomandata è quasi fuori portata! + +[RCR1_7:RCRACE1] +~r~La macchina radiocomandata è fuori portata! + +{=================================== MISSION TABLE ROCK1 ===================================} + +[RBM1_A:ROCK1] +AllllllllRrrighttt! + +[RBM1_B:ROCK1] +Brillante, davvero brillante! + +[RBM1_D:ROCK1] +Ehi, hai già incontrato i Love Fist? + +[RBM1_E:ROCK1] +No, non ancora, ma mi piace molto la vostra musica. + +[RBM1_F:ROCK1] +Lascia che ti presenti la band. + +[RBM1_G:ROCK1] +Questo è P, Percy, Dick. Willy è nel cesso! Jezz è invece il tipo che hai visto prima nella cabina di registrazione. + +[RBM1_H:ROCK1] +Ragazzi, vorrei presentarvi un mio carissimo amico. + +[RBM1_I:ROCK1] +Si chiama Tommy. Ci conosciamo da tempo. + +[RBM1_J:ROCK1] +Ciao amico. + +[RBM1_K:ROCK1] +E, ehm, qual era il tuo nome? + +[RBM1_L:ROCK1] +Eddai Jezz che lo sai, + +[RBM1_M:ROCK1] +non fare questi scherzetti con me, amico, + +[RBM1_N:ROCK1] +sono troppo furbo per cascarci, tesoro! + +[RBM1_O:ROCK1] +Vedi, la situazione Tom è che i ragazzi hanno bisogno di aiuto. + +[RBM1_P:ROCK1] +Non hanno i contatti giusti da questa parte, del tipo 'come sta tuo padre'. + +[RBM1_Q:ROCK1] +Abbiamo bisogno di droga, amico! + +[RBM1_R:ROCK1] +Dobbiamo fare un po' di casino alla Love Fist, capito? + +[RBM1_S:ROCK1] +Beh, questa è Vice City. Qual è il problema? + +[RBM1_U:ROCK1] +Love Juice, sì! + +[RBM1_V:ROCK1] +Love Juice? + +[RBM1_W:ROCK1] +Sì, due parti di broda, una parte di polvere, cinque bombe effervescenti e un litro di petrolio. + +[RBM1_X:ROCK1] +Ci puoi aiutare, amico? + +[RBM1_Y:ROCK1] +Beh, significherebbe molto per i ragazzi. + +[RBM1_Z:ROCK1] +Lo farai per loro, vero? + +[RBM1_7:ROCK1] +~r~Non hai portato la Love Juice in tempo! + +[RBM1_8:ROCK1] +~r~Mercedes è morta! + +[RBM1_10:ROCK1] +~r~Idiota! Hai distrutto la merce! + +[RBM1_13:ROCK1] +~g~Porta la 'Love Juice' e Mercedes alla band prima che salgano sul palco. + +[RBM1_15:ROCK1] +~r~Hai perso lo spacciatore, i soldi e la droga! + +[RBM1_17:ROCK1] +~g~Uccidi lo spacciatore e recupera la droga! + +[MOB_07A:ROCK1] +Ehi amico, ai ragazzi non dispiacerebbe un po' di compagnia, se capisci cosa intendo... + +[MOB_07B:ROCK1] +Conosco la ragazza giusta. + +[ROK1_5:ROCK1] +Ehi Mercedes! + +[ROK1_6:ROCK1] +Ciao Tommy, come stai? + +[ROK1_7:ROCK1] +Non male. Senti, ti piacerebbe intrattenere i Love Fist? + +[ROK1_8:ROCK1] +OK, ma per questo favore voglio una ricompensa... + +[RBM1_14:ROCK1] +~g~Hai bisogno di una macchina o di una moto! + +[RBM1_1:ROCK1] +~g~Vai a prendere Mercedes al suo appartamento. + +[RBM1_12:ROCK1] +~g~Raccogli gli ingredienti per la 'Love Juice' dallo spacciatore. + +[ROK1_2:ROCK1] +NON NECESSARIO + +[ROK1_3:ROCK1] +NON NECESSARIO + +[MERC_39:ROCK1] +Ci vediamo più tardi, tesoro. + +[RBM1_C:ROCK1] +Ehi, Tommy! Sono felice che ce tu l'abbia fatta. + +[ROK1_1A:ROCK1] +Stai cercando qualcosa di speciale? Ho proprio ciò che ti serve! + +[ROK1_9:ROCK1] +Grazie per i soldi, idiota! + +[RBM1_T:ROCK1] +Abbiamo bisogno di Love Juice, hai presente? + +{=================================== MISSION TABLE ROCK2 ===================================} + +[RBM2_A:ROCK2] +Tommy, amico, sono felice di vederti! + +[RBM2_B:ROCK2] +Cosa succede? + +[RBM2_C:ROCK2] +Brutte vibrazioni, Tommy... + +[RBM2_E:ROCK2] +C'è questo tipo, lo conosciamo appena, ma lui la sa lunga su di noi. + +[RBM2_F:ROCK2] +Come questo tipo. Sa tutto su di noi. + +[RBM2_G:ROCK2] +Sa che Willy ama la biancheria intima femminile, eh! + +[RBM2_H:ROCK2] +O che a Percy piacciono i Duran Duran! + +[RBM2_K:ROCK2] +Sì, come nella storia dei missili, esatto. Ma ascolta, questo bastardo... + +[RBM2_L:ROCK2] +cioè, sì, questo tipo, vuole vedere i Love Fist morti. + +[RBM2_M:ROCK2] +Morti, Tommy. + +[RBM2_N:ROCK2] +Love Fist morti. Lo sai come si dice, i buoni muoiono giovani. + +[RBM2_O:ROCK2] +Ma Tommy, devi salvare i Love Fist! + +[RBM2_P:ROCK2] +Abbiamo un concerto fra due ore e credo... + +[RBM2_Q:ROCK2] +I ragazzi credono che il maniaco stia architettando qualcosa di losco. + +[RBM2_1:ROCK2] +~g~Guida la limousine al concerto e cerca di attirare allo scoperto lo psicopatico. + +[RBM2_2:ROCK2] +~r~Hai distrutto l'auto della band! + +[RBM2_3:ROCK2] +~g~Vai al concerto! + +[RBM2_4:ROCK2] +~g~Becca lo psicopatico! Non farlo scappare! + +[RBM2_5:ROCK2] +~r~Lo hai perso, idiota! + +[RBM2_7:ROCK2] +~r~I fan sono stati attaccati: lo psicopatico non si farà vivo! + +[RBM2_8:ROCK2] +~r~Le guardie della sorveglianza sono state attaccate: lo psicopatico non si farà vivo! + +[PSYCH_1:ROCK2] +Vedrò i Love Fist bruciare! + +[PSYCH_2:ROCK2] +I Love Fist hanno rovinato la mia vita! + +[RBM2_I:ROCK2] +Sta zitto, idiota. Solo perché a Jezz piacciono le pecore. + +[RBM2_R:ROCK2] +Ma sta' zitto! + +[RBM2_D:ROCK2] +Certo, niente scherzi, è roba forte, davvero forte sai? + +[RBM2_J:ROCK2] +È una questione di passione, sai? + +{=================================== MISSION TABLE ROCK3 ===================================} + +[RBM3_A:ROCK3] +Tommy! Tommy! Tommy amico, lo psicopatico è tornato! + +[RBM3_B:ROCK3] +Che succede? + +[RBM3_C:ROCK3] +Quello psicopatico non vuole lasciar stare i Love Fist! + +[RBM3_D:ROCK3] +Non lo hai ucciso, amico. E adesso è tornato. + +[RBM3_E:ROCK3] +Sì, sì, sì e il problema è che... + +[RBM3_F:ROCK3] +Il problema è che abbiamo bisogno di qualcuno di cui ci fidiamo per guidare la limousine, + +[RBM3_G:ROCK3] +poiché il pazzo continua a minacciarci! + +[RBM3_I:ROCK3] +Stiamo letteralmente cadendo a pezzi. + +[RBM3_J:ROCK3] +OK ragazzi, calmatevi. Me ne occuperò io. + +[RBM3_K:ROCK3] +Generalmente non perdo tempo a portare in giro un gruppo di ubriaconi scozzesi bisessuali, + +[RBM3_L:ROCK3] +ma, nel vostro caso, farò un'eccezione. + +[ROK3_03:ROCK3] +Conviene fare un giro più lungo. + +[ROK3_04:ROCK3] +Ehi Tommy, cambia la stazione. + +[ROK3_08:ROCK3] +Mi sto annoiando di tutto questo. + +[ROK3_09:ROCK3] +Tieni premuto il maledetto pedale! + +[ROK3_61:ROCK3] +Dobbiamo trovare la bomba! + +[ROK3_28:ROCK3] +Suonerò il basso all'inferno. + +[ROK3_30:ROCK3] +Qualcuno faccia qualcosa. + +[ROK3_32:ROCK3] +OK, osso duro, fai qualcosa tu allora. + +[ROK3_34:ROCK3] +Willy, potresti succhiare la broda con una cannuccia. + +[ROK3_37:ROCK3] +Passa una cannuccia a Willy! + +[ROK3_41:ROCK3] +Quale filo, Tommy? + +[ROK3_42:ROCK3] +Quello verde. + +[ROK3_43:ROCK3] +Non c'è un filo verde! Questo forse è verde... + +[ROK3_44:ROCK3] +Qualcuno di questi fili ti sembra verde? + +[ROK3_49:ROCK3] +Ti ho tenuto con me per anni. + +[ROK3_51:ROCK3] +Una ragazzina isterica. + +[ROK3_52:ROCK3] +Sì. + +[ROK3_53:ROCK3] +Taci e taglia un filo. + +[ROK3_54:ROCK3] +Quale filo? + +[ROK3_55:ROCK3] +Questo qua... + +[ROK3_56:ROCK3] +NO! + +[ROK3_57:ROCK3] +Oddio, siamo vivi. Non siamo esplosi, amico. Tommy, bel lavoro. Rock and roll, ragazzi. + +[ROK3_58:ROCK3] +Non abbiamo uno spettacolo a cui andare? Del casino da fare? Fans di cui abusare? + +[ROK3_59:ROCK3] +LOVE FIST! + +[ROK3_60:ROCK3] +Hai finito con quella bottiglia? + +[RBM3_4:ROCK3] +~r~Hai ucciso i Love Fist! + +[RBM3_6:ROCK3] +DETONAZIONE: + +[RBM3_1:ROCK3] +~g~Trasporta i Love Fist al concerto. + +[RBM3_2:ROCK3] +Mentre la bomba è innescata, esploderà se cercherai di lasciare la macchina... + +[RBM3_3:ROCK3] +Se la barra di detonazione si riempie completamente, la bomba esploderà. + +[RBM3_8:ROCK3] +Più velocemente guidi, più lentamente si riempirà. + +[RBM3_7:ROCK3] +~g~BOMBA DISINNESCATA! + +[ROK3_6A:ROCK3] +~g~Love Fist. Avete finito di inquinare l'etere. + +[ROK3_6B:ROCK3] +~g~Vi ho dato la possibilità di essere amici. Adesso vi darò la possibilità di morire. + +[ROK3_62:ROCK3] +Per cui abbiamo pensato di mostrarti il nostro Tempio del Rock. + +[ROK3_63:ROCK3] +Per percepire il furore alla Love Fist! + +[ROK3_64:ROCK3] +Ascolta come parli, amico. Si tratta di carta pesta e adesivo. + +[ROK3_65:ROCK3] +Ehi, ragazzi, questo è il tempio e noi siamo i sacerdoti. + +[ROK3_66:ROCK3] +Beh, se i ragazzi amano i loro sacerdoti a pezzi e mezzi sordi, + +[ROK3_67:ROCK3] +chi sono per discutere? + +[ROK3_68:ROCK3] +Oddio, si è rovinato nuovamente il nastro. + +[ROK3_69:ROCK3] +Se andiamo avanti così, dovremo suonare dal vivo. + +[ROK3_70:ROCK3] +Oooh merda! La pancia... + +[ROK3_74:ROCK3] +Ah guarda: che cos'è? Ehi Tommy, metti su questo nastro. + +[ROK3_01:ROCK3] +Finalmente, amico: è l'ora di un bel drink. + +[ROK3_02:ROCK3] +Il concerto è a cento metri lungo la strada. + +[ROK3_05:ROCK3] +Mi rimbambisco se non agito la testa. + +[ROK3_07:ROCK3] +Tommy, amico, devi salvare la band! + +[ROK3_29:ROCK3] +Tommy, continua a guidare senza rallentare! + +[ROK3_31:ROCK3] +Bella: 'Qualcuno faccia qualcosa'. Ma che idiozia è questa? Ho conosciuto froci più coraggiosi. + +[ROK3_33:ROCK3] +Ascolta, sono un musicista. Non ho la minima idea di come disarmare una bomba. + +[ROK3_35:ROCK3] +Sì, mi è giunta voce che sei bravo in queste cose. + +[ROK3_38:ROCK3] +Una cannuccia? Questo è il furgone da tour dei Love Fist! + +[ROK3_39:ROCK3] +Dove diavolo la trovo una cannuccia, amico? + +[ROK3_46:ROCK3] +Avrei dovuto scaricarti quando ne ho avuto la possibilità. + +[ROK3_47:ROCK3] +Capitalista. + +[ROK3_48:ROCK3] +Schiavo dalla gloria. + +[ROK3_73:ROCK3] +Jezz sta riproducendo il nastro. + +[RBM3_9:ROCK3] +Se ti fermi o guidi lentamente, la barra di detonazione aumenterà. + +[ROK3_50:ROCK3] +Sta zitto. Sei un idiota. + +[ROK3_36:ROCK3] +Ehi, ero completamente fuori di testa quella notte, e voi lo sapete! + +[RBM3_H:ROCK3] +Mi sto cagando addosso, amico. Voglio la mamma! + +[ROK3_45:ROCK3] +La fine imminente mi fa vedere tutto verde. + +[ROK3_6C:ROCK3] +~g~Rallenta e la limousine esploderà, insieme ai vostri GROSSI CULI PELOSI! + +[ROK3_71:ROCK3] +Dobbiamo andare avanti con quello che abbiamo. Grazie ancora Tommy. Cioè, sai cosa intendo, ciao! + +[ROK3_1:ROCK3] +Alla fine è giunta l'ora di un meritato bicchierino. Lo spettacolo si tiene a circa cento metri lungo la strada. + +[ROK3_2:ROCK3] +Conviene fare un giro più lungo. Ehi Tommy, cambia la stazione. + +[ROK3_3:ROCK3] +Sì Tommy, mi rimbambisco se non agito la testa. Ehi guarda, cos'è questo? Ehi Tommy, metti su questa cassetta. + +[ROK3_4:ROCK3] +Love Fist. Avete finito di inquinare l'etere. Vi ho dato la possibilità di essere amici. + +[ROK3_5:ROCK3] +Adesso vi darò la possibilità di morire. Se rallentate, la vostra limousine esploderà, insieme ai vostri GROSSI CULI PELOSI! + +[ROK3_6:ROCK3] +Tommy, devi salvare la band! Mi sto annoiando di tutto questo. Tieni premuto il maledetto pedale! + +[ROK3_7:ROCK3] +Dobbiamo trovare la bomba! Perché? Non possiamo guidare tutto il giorno? È vero, c'è un sacco di roba da bere... + +[ROK3_8:ROCK3] +La bomba non sarà nel motore? Dovremmo fermarci per prenderla! Moriremo tutti quanti! Credo che mi ubriacherò! + +[ROK3_9:ROCK3] +Ehi, c'è una coda da rispettare, amico! La risposta non è nel minibar! Spostati! + +[ROK3_10:ROCK3] +Ehi, dalla bottiglia di vodka escono un sacco di fili! Non è vodka, quella è BRODA! + +[ROK3_11:ROCK3] +WAAAAAAGGGHHHH!!! Ed è predisposta per esplodere! WAAAAAAAAAAAAGGGHHHHHHHH!!! + +[ROK3_12:ROCK3] +Lo dicevano che l'alcol mi avrebbe fatto fuori... L'ho visto in televisione, devi tagliare uno dei fili. Quale? Non lo so, amico. + +[ROK3_13:ROCK3] +Non ne ho idea. Willy, dimmi qualcosa. Suonerò il basso all'inferno. + +[ROK3_14:ROCK3] +Tommy, continua a guidare senza rallentare! Qualcuno faccia qualcosa. Bella! + +[ROK3_15:ROCK3] +'Qualcuno faccia qualcosa'. Ma che idiozia è questa? Ho conosciuto froci più coraggiosi. OK, osso duro, fai qualcosa tu allora. + +[ROK3_16:ROCK3] +Ascolta, io sono un musicista. Non ho la minima idea di come disarmare una bomba. Willy, potresti succhiare la broda con una cannuccia. + +[ROK3_17:ROCK3] +Sì, mi è giunta voce che sei bravo in queste cose. Ehi, ero completamente fuori di testa quella notte, e voi lo sapete! + +[ROK3_18:ROCK3] +Passa una cannuccia a Willy! Una cannuccia? Questo è il furgone da tour di Love Fist! + +[ROK3_19:ROCK3] +Dove diavolo la trovo una cannuccia, non so se mi spiego? Quale filo, Tommy? Quello verde. Non c'è un filo verde! + +[ROK3_20:ROCK3] +Questo forse è verde... Qualcuno di questi fili ti sembra verde? + +[ROK3_21:ROCK3] +Ih no! La fine imminente mi fa vedere tutto verde! Avrei dovuto scaricarti quando ne ho avuto la possibilità. + +[ROK3_22:ROCK3] +Accecato dalla gloria. Capitalista. Ti ho tenuto con me per anni. Sta zitto. Sei un idiota. + +[ROK3_23:ROCK3] +Una ragazzina isterica. Sì. Taci e taglia un filo. Quale filo? Questo qua... + +[ROK3_24:ROCK3] +NO! Oddio, siamo vivi. Non siamo esplosi, amico. + +[ROK3_25:ROCK3] +Tommy, bel lavoro. Rock and roll, ragazzi. Non abbiamo uno spettacolo a cui andare? + +[ROK3_26:ROCK3] +Del casino da fare? Fans di cui abusare? LOVE FIST! + +[ROK3_27:ROCK3] +Hai finito con quella bottiglia? + +{=================================== MISSION TABLE SERG1 ===================================} + +[TEX1_A:SERG1] +Vieni a metterti qui vicino a me, figliolo. + +[TEX1_B:SERG1] +Diamine, mio padre diceva sempre 'A caval donato non si guarda in bocca' ed effettivamente non lo ha mai fatto. + +[TEX1_C:SERG1] +Vuoi un goccio di sano Kentucky? + +[TEX1_D:SERG1] +No, grazie. + +[TEX1_E:SERG1] +Sei uno dalla testa lucida, mi piace. + +[TEX1_F:SERG1] +Ora, il business dei terreni non ha niente a che fare con quello che dicono i giornali. + +[TEX1_G:SERG1] +Si tratta solo di polvere! E la volontà di possederla! Mi segui, figliolo? + +[TEX1_H:SERG1] +Sì, certo. + +[TEX1_J:SERG1] +Tu mi sembri la persona giusta per convincerlo. + +[TEX1_K:SERG1] +La persuasione è il mio forte. + +[TEX1_L:SERG1] +Bene. Lo troverai al circolo sportivo, presso il campo da golf. + +[TEX1_M:SERG1] +Non permettono di entrare con le armi, per cui le sue guardie del corpo non saranno armate. + +[TEX1_N:SERG1] +Vai e riempilo di santissime mazzate. + +[TEX1_O:SERG1] +Prendi, ti ho iscritto al club, inoltre, credo proprio avrai bisogno di abiti più adeguati. + +[TEX1_2:SERG1] +~g~Raggiungi il club di golf Leaf Links. + +[TEX1_0:SERG1] +~g~Il bersaglio si sta allenando a golf: assicurati che sia la sua ultima partita. + +[TEX1_3:SERG1] +Chi è questo tipo? Ragazzi, occupatevene voi. + +[TEX1_6:SERG1] +Bel culo bimba! + +[TEX1_7:SERG1] +Ma sono io? + +[TEX1_I:SERG1] +Bene, c'è un tenace bastardo che non si decide a vendere. + +[TEX1_8:SERG1] +Ogni volta che entri su un cart da golf, ottieni automaticamente una mazza da golf, sempre se lo spazio per l'arma corpo a corpo è disponibile. + +[TEX1_9:SERG1] +Prendilo! + +[TEX1_10:SERG1] +Uccidi lo psicopatico! + +[TEX1_1:SERG1] +~g~Recupera degli abiti da golf presso Jocksport's. + +{=================================== MISSION TABLE SERG2 ===================================} + +[TEX_2A:SERG2] +~g~Eccellente! Ti hanno visto! + +[TEX_2B:SERG2] +~r~Pazzo! La gente deve essere TESTIMONE di un attacco da parte di un Cubano! + +[TEX_2C:SERG2] +~g~Vai a recuperare dei vestiti con i colori della gang dei Cubani da Rafael's! + +[TEX_2D:SERG2] +~g~Adesso metti fuori gioco il capo della gang degli Haitiani presso l'impresa di pompe funebri Romero's. + +[TEX2_A:SERG2] +Tommy, questo è Donald Love. Donald, questo è Tommy Vercetti, + +[TEX2_B:SERG2] +l'ultimo cowboy arrivato da queste parti. + +[TEX2_C:SERG2] +Yeh... uh... + +[TEX2_D:SERG2] +Donald, sta zitto e ascoltami, potresti imparare qualcosa. + +[TEX2_E:SERG2] +Adesso, niente fa calare i prezzi dei terreni come una bella guerra tra gang rivali. + +[TEX2_F:SERG2] +Eccetto, forse, un disastro, come una piaga biblica o qualcosa del genere. + +[TEX2_G:SERG2] +Ma per il caso in questione mi sembra un po' troppo. + +[TEX2_H:SERG2] +Capisci, brutto idiota quattr'occhi? + +[TEX2_I:SERG2] +Recentemente è morto un capo gang degli Haitiani e, a quanto sembra, sono stati i Cubani. + +[TEX2_J:SERG2] +Togliamo loro qualsiasi dubbio, allora! Vestiti da ombre Cubano + +[TEX2_K:SERG2] +e vai a disturbare il funerale. Fai un bel casino e dattela a gambe. + +[TEX2_L:SERG2] +Hai capito, Donald? + +[TEX2_M:SERG2] +Questo dovrebbe mettere il coyote nel pollaio, vero? + +[TEX2_N:SERG2] +Dopodiché dovremo solo attendere e veder crollare i prezzi. + +[TEXEXIT:SERG2] +~g~Vattene da Little Haiti! + +{=================================== MISSION TABLE SERG3 ===================================} + +[TEX3_A:SERG3] +Guarda qua, figliolo. Ho un problema e sono certo tu possa darmi una mano. + +[TEX3_B:SERG3] +Non sono un geometra. + +[TEX3_C:SERG3] +No, mi riferivo principalmente alle tue doti di demolitore. + +[TEX3_D:SERG3] +Ecco la faccenda: questo è il progetto approvato, e questo, + +[TEX3_E:SERG3] +questo è il terreno che stiamo seguendo. + +[TEX3_F:SERG3] +Stai cercando di dirmi che questo nuovo isolato di uffici è nel posto sbagliato. + +[TEX3_G:SERG3] +Capisci al volo. + +[TEX3_H:SERG3] +Adesso devo allontanarmi per un po' dalla città... + +[TEX3_I:SERG3] +...e se la costruzione di quegli uffici dovesse subire un improvviso e insormontabile problema strutturale, beh, allora... + +[TEX3_J:SERG3] +come ogni civile e ragionevole individuo, ti sentiresti obbligato a intervenire + +[TEX3_K:SERG3] +per favorire il ringiovanimento di un'area così importante della città? + +[TEX3_L:SERG3] +Dove posso ordinare altra gente come te? + +[TEX3_1:SERG3] +~g~Utilizza l'elicottero radiocomandato per trasportare le bombe nelle quattro aree bersaglio dell'edificio. + +[TEX3_2:SERG3] +~g~Devi posizionare una bomba su ogni bersaglio. Puoi piazzare l'esplosivo in qualsiasi ordine. + +[TEX3_5:SERG3] +~g~Se fai cadere una bomba senza successo, potrai raccoglierla nuovamente e riprovare. + +[TEX3_7:SERG3] +~g~Dovrai piazzare tutti gli esplosivi rimanenti in 7 minuti! + +[TEX3_8:SERG3] +~g~Hai mancato il bersaglio! Raccogli la bomba e riprova! + +[TEX3_10:SERG3] +~g~Fai cadere la bomba sul bersaglio. + +[TEX3_11:SERG3] +Bersagli rimanenti: + +[TEX3_17:SERG3] +~r~Il tempo è scaduto: non sei riuscito a demolire l'edificio! + +[TEX3_18:SERG3] +~r~L'elicottero radiocomandato è stato distrutto! Come farai adesso a trasportare le bombe? + +[TEX3_19:SERG3] +~r~Hai fatto cadere la bomba in acqua! Hai bisogno di tutte le 4 bombe per demolire il cantiere! + +[TEX3_20:SERG3] +~g~L'elicottero radiocomandato è quasi fuori portata. Torna indietro al cantiere e completa il lavoro! + +[TEX3_21:SERG3] +~r~L'elicottero radiocomandato è fuori portata! + +[TEX3_24:SERG3] +Premi il ~h~~k~~VEHICLE_LOOKLEFT~~w~ per ruotare l'elicottero in senso antiorario. + +[TEX3_25:SERG3] +Premi il ~h~~k~~VEHICLE_LOOKLEFT~~w~ per ruotare l'elicottero in senso orario. + +[TEX3_27:SERG3] +~g~Le scale centrali ti permetteranno di accedere a tutti i piani dell'edificio. + +[TEX3_31:SERG3] +~r~Hai distrutto il van contenente le bombe e l'elicottero radiocomandato! + +[TEX3_32:SERG3] +Puoi ~h~guardare indietro~w~ premendo ~h~simultaneamente ~k~~VEHICLE_LOOKLEFT~ e ~k~~VEHICLE_LOOKRIGHT~~w~. + +[TEX3_4:SERG3] { reVC update } +~g~Per sganciare una bomba, premi il ~h~~k~~VEHICLE_FIREWEAPON~~g~. + +[TEX3_29:SERG3] { reVC update } +Per sganciare una bomba, premi il ~h~~k~~VEHICLE_FIREWEAPON~~w~. + +[TEX3_26:SERG3] +Premi il ~h~~k~~VEHICLE_BRAKE~~w~ per ridurre la velocità del rotore dell'elicottero in modo da farlo abbassare. + +[TEX3_22:SERG3] +Premi il ~h~~k~~VEHICLE_ACCELERATE~~w~ per aumentare la velocità del rotore dell'elicottero in modo da ~h~farlo alzare. + +[TEX3_16:SERG3] +~g~Raggiungi il van ~w~TOPFUN~g~ presso il cantiere edile da demolire. + +[TEX3_33:SERG3] +Una volta raccolta una bomba, il radar mostrerà la pozione del bersaglio rispetto all'elicottero radiocomandato. + +[TEX3_34:SERG3] +Un ~h~triangolo verso l'alto~w~ indica che il bersaglio si trova ~h~più in alto~w~ dell'elicottero. + +[TEX3_35:SERG3] +Un ~h~triangolo verso il basso~w~ indica che il bersaglio si trova ~h~più in basso~w~ dell'elicottero. + +[TEX3_36:SERG3] +Un ~h~quadrato~w~ indica che il bersaglio si trova alla ~h~stessa altezza~w~ dell'elicottero. + +[TEX3_6:SERG3] +~g~Una volta raccolta la prima bomba, comincerà il conto alla rovescia. + +[TEX3_28:SERG3] +Per ~h~raccogliere una bomba~w~, avvicinaci l'elicottero radiocomandato. L'elicottero può trasportare una sola bomba alla volta. + +[TEX3_30:SERG3] +Per raccogliere una bomba, avvicinaci l'elicottero radiocomandato. L'elicottero può trasportare una sola bomba alla volta. + +[TEX3_12:SERG3] +~g~Bomba posizionata! Ancora 3 bersagli! Torna indietro e recupera un'altra bomba. + +[TEX3_13:SERG3] +~g~Bomba posizionata! Ancora 2 bersagli! Torna indietro e recupera un'altra bomba. + +[TEX3_14:SERG3] +~g~Bomba posizionata! Ancora 1 bersaglio! Torna indietro e recupera un'altra bomba. + +[TEX3_15:SERG3] +~r~Conto alla rovescia avviato! ~g~Devi posizionare le ~w~4 bombe~g~ in tempo! + +[TEX3_37:SERG3] +Sposta ~h~in basso la levetta analogica destra~w~ per aumentare la velocità del rotore e ~h~far salire~w~ l'elicottero. + +[TEX3_38:SERG3] { reVC update } +{Sposta ~h~~k~~VEHICLE_ACCELERATE~~w~ per ridurre la velocità del rotore e ~h~far scendere~w~ l'elicottero.} +Sposta ~h~in alto la levetta analogica destra~w~ per ridurre la velocità del rotore e ~h~far scendere~w~ l'elicottero. + +[TEX3_39:SERG3] +~g~Per sganciare una bomba, premi il tasto ~h~~k~~VEHICLE_HANDBRAKE~~g~. + +[TEX3_40:SERG3] +Per sganciare una bomba, premi il tasto ~h~~k~~VEHICLE_HANDBRAKE~~w~. + +[TEX3_23:SERG3] +Premi i tasti ~h~~k~~VEHICLE_TURRETUP~~w~ e ~h~~k~~VEHICLE_TURRETDOWN~~w~ per inclinare l'elicottero nella direzione desiderata. + +{=================================== MISSION TABLE TAXI1 ===================================} + +[FARES:TAXI1] +CLIENTI: + +[TAXI1:TAXI1] +~g~Trova un passeggero. + +[TSCORE2:TAXI1] +~1~$ + +[IN_ROW:TAXI1] +Bonus ~1~ DI SEGUITO! ~1~$ + +[TAXI3:TAXI1] +~r~Il passeggero è fuggito terrorizzato! + +[TAXI7:TAXI1] +~r~La tua auto è distrutta, falla riparare. + +[TAXI4:TAXI1] +Corsa completata! + +[TAXI5:TAXI1] +BONUS VELOCITÀ! + +[TAXI6:TAXI1] +Missione taxi terminata + +[TAXIH1:TAXI1] +Fermati vicino a un pedone evidenziato per farlo salire a bordo e portarlo a destinazione prima che scada il tempo. + +[FARE1:TAXI1] +~g~Destinazione: ~w~'Club Pole Position'~g~ in Ocean Beach. + +[FARE3:TAXI1] +~g~Destinazione: ~w~'Marina'~g~ in Ocean Beach. + +[FARE4:TAXI1] +~g~Destinazione: ~w~'Ammu-Nation'~g~ in Ocean Beach. + +[FARE5:TAXI1] +~g~Destinazione: ~w~'Hardware store'~g~ in Washington Beach. + +[FARE6:TAXI1] +~g~Destinazione: ~w~'Mall North Point'~g~ in Vice Point. + +[MFARE1:TAXI1] +~g~Destinazione: ~w~'Ammu-Nation'~g~ in Downtown. + +[MFARE2:TAXI1] +~g~Destinazione: ~w~'il terminal'~g~ all'aeroporto internazionale Escobar. + +[WFARE3:TAXI1] +~g~Destinazione: ~w~'Sunshine Autos'~g~ in Little Havana. + +[WFARE4:TAXI1] +~g~Destinazione: ~w~'Taxi Kaufman'~g~ in Little Haiti. + +[WFARE5:TAXI1] +~g~Destinazione: ~w~'Hardware store'~g~ in Little Havana. + +[WFARE6:TAXI1] +~g~Destinazione: ~w~'Howlin Petes Bike'~g~ in Downtown. + +[FARE7:TAXI1] +~g~Destinazione: ~w~'il negozio dei gioiellieri'~g~ in Vice Point. + +[FARE8:TAXI1] +~g~Destinazione: ~w~'la spiaggia'~g~ in Ocean Beach. + +[FARE9:TAXI1] +~g~Destinazione: ~w~'la spiaggia'~g~ in Washington Beach. + +[FARE10:TAXI1] +~g~Destinazione: ~w~'la spiaggia'~g~ in Vice Point. + +[FARE11:TAXI1] +~g~Destinazione: ~w~'l'ospedale'~g~ in Ocean Beach. + +[FARE12:TAXI1] +~g~Destinazione: ~w~'l'ospedale'~g~ in Vice Point. + +[FARE13:TAXI1] +~g~Destinazione: ~w~'la stazione di polizia'~g~ in Washington Beach. + +[FARE14:TAXI1] +~g~Destinazione: ~w~'la stazione di polizia'~g~ in Vice Point. + +[FARE15:TAXI1] +~g~Destinazione: ~w~'il ristorante della pizza'~g~ in Vice Point. + +[WFARE7:TAXI1] +~g~Destinazione: ~w~'la stazione di polizia'~g~ in Little Havana. + +[WFARE8:TAXI1] +~g~Destinazione: ~w~'la stazione di polizia'~g~ in Downtown. + +[WFARE9:TAXI1] +~g~Destinazione: ~w~'l'ospedale'~g~ in Downtown. + +[WFARE10:TAXI1] +~g~Destinazione: ~w~'l'ospedale'~g~ in Little Havana. + +[WFARE11:TAXI1] +~g~Destinazione: ~w~'lo stadio'~g~ in Downtown. + +[WFARE12:TAXI1] +~g~Destinazione: ~w~'il ristorante della pizza'~g~ in Little Haiti. + +[WFARE13:TAXI1] +~g~Destinazione: ~w~'il ristorante della pizza'~g~ in Downtown. + +[WFARE14:TAXI1] +~g~Destinazione: ~w~'i bacini'~g~ in Viceport. + +[WFARE15:TAXI1] +~g~Destinazione: ~w~'la farmacia'~g~ in Little Haiti. + +[FARE2:TAXI1] +~g~Destinazione: ~w~'Club Malibu'~g~ in Vice Point. + +{=================================== MISSION TABLE TAXICUT ===================================} + +[TAXC_A:TAXICUT] +Immagino tu sia il nuovo proprietario. + +[TAXC_B:TAXICUT] +Di chi fai parte? Mafia? Cartello? Non sembri Messicano... + +[TAXC_C:TAXICUT] +Comunque, immagino ora comincerai con la vecchia frase 'le cose stanno per cambiare da queste parti', + +[TAXC_D:TAXICUT] +magari minaccerai uno degli autisti... + +[TAXC_E:TAXICUT] +vacci piano con quello là, Ted, ha appena avuto un'ernia al disco. + +[TAXC_F:TAXICUT] +Beh, sì. Le cose stanno per cambiare da queste parti, signora. + +[TAXC_G:TAXICUT] +Che felicità, figliolo. Me ne occupo io... + +[TAXC_H:TAXICUT] +Ormai lo faccio da anni. + +[TAXC_I:TAXICUT] +Attenzione a tutti: + +[TAXC_J:TAXICUT] +è cambiata la gestione e le cose stanno nuovamente per cambiare da queste parti. + +[TAXC_K:TAXICUT] +Il nostro nuovo direttore, della... + +[TAXC_L:TAXICUT] +Di quale gang fai parte? + +[TAXC_M:TAXICUT] +Beh, non faccio parte di nessuna gang. + +[TAXC_N:TAXICUT] +Allora qual è il tuo maledetto nome, ragazzo? + +[TAXC_O:TAXICUT] +Vercetti, Tommy Vercetti. + +[TAXC_P:TAXICUT] +In nostro nuovo direttore, della Vercetti Gang, + +[TAXC_Q:TAXICUT] +si assicurerà che non ci succedano incidenti. + +[TAXC_R:TAXICUT] +Capisce? Fine! + +[TAXC_S:TAXICUT] +Ti è piaciuto il 'Capisce'? A me è piaciuto il 'Capisce'. + +[TAXC_T:TAXICUT] +Allora è così che ha funzionato nel passato: + +[TAXC_U:TAXICUT] +noi portiamo avanti l'azienda come sempre. + +[TAXC_V:TAXICUT] +Se abbiamo problemi con la concorrenza, tu ti occupi di riempirli di botte. + +[TAXC_W:TAXICUT] +Poi loro ci riempiono di botte, + +[TAXC_X:TAXICUT] +poi tu riempi loro di botte nuovamente, + +[TAXC_Y:TAXICUT] +eccetera, eccetera. Tutto chiaro? + +[TAXC_Z:TAXICUT] +Uh, sì, almeno credo... + +[TAXC_A1:TAXICUT] +Prendi un taxi dal garage se ti senti di lanciarti subito nella mischia. + +{=================================== MISSION TABLE TAXIWA1 ===================================} + +[OUTTIME:TAXIWA1] +~r~Troppo lento, troppo lento! + +[TAX1_1:TAXIWA1] +OK, abbiamo un cliente danaroso che richiede un taxi da Starfish Island: qualche volontario? + +[TAX1_2:TAXIWA1] +Qui Tommy, lo prendo io! + +[TAX1_3:TAXIWA1] +Questo cliente è mio, indietro! + +[TAX1_4:TAXIWA1] +Forza, forza, entra in fretta! + +[TAX1_5:TAXIWA1] +OK, OK! Ti prego, non farmi male! + +[TAXW1_1:TAXIWA1] +~g~Passa a prendere il VIP su Starfish Island. + +[TAXW1_2:TAXIWA1] +~g~Porta indietro il VIP! Metti fuori gioco l'altra macchina! + +[TAXW1_3:TAXIWA1] +~r~Il VIP è morto! + +[TAXW1_4:TAXIWA1] +~r~Il VIP è arrivato a destinazione! + +[TAXW1_6:TAXIWA1] +~g~Porta il VIP all'aeroporto! + +{=================================== MISSION TABLE TAXIWA2 ===================================} + +[TAX2_1:TAXIWA2] +Chiamata a tutti i veicoli: stiamo perdendo clienti in tutta la città. Che cosa sta succedendo? + +[TAX2_2:TAXIWA2] +I taxi VC continuano a batterci sul tempo. Sono in troppi, non possiamo competere! + +[TAX2_3:TAXIWA2] +Mr. Vercetti, se sei in ascolto, devi mettere fuori gioco un po' dei taxi VC, se no falliamo! + +[TAXW2_1:TAXIWA2] +~g~Distruggi 3 taxi rivali! + +{=================================== MISSION TABLE TAXIWA3 ===================================} + +[TAX3_1:TAXIWA3] +Taxi 13, la signorina Cortez ha bisogno di essere prelevata da Down Town e ha richiesto esplicitamente di te. + +[TAX3_2:TAXIWA3] +OK, ricevuto. Taxi 13 chiudo. + +[TAX3_3:TAXIWA3] +Hmmm, nessun segno di Mercedes... + +[TAXW3_3:TAXIWA3] +~g~Metti fuori combattimento il capo dei taxisti! + +[TAXW3_2:TAXIWA3] +~g~Resta vivo fino allo scadere del tempo. + +[TAX_AS1:TAXIWA3] +BENI COMPAGNIA DEI TAXI ACQUISITI + +[TAX_AS2:TAXIWA3] +La compagnia dei taxi d'ora in poi genererà introiti per un massimo di ~1~$. Ricordati di recuperarli regolarmente. + +[TAX3_4:TAXIWA3] +È giunta l'ora che l'angelo custode dei Taxi Kaufman entri in azione! + +[TAX3_5:TAXIWA3] +Ehi ragazzo, ti abbronzo il fondoschiena! + +{ re3 updates } +{ new languages } +[FEL_JAP] +GIAPPONESE + +[FEL_POL] +POLACCO + +[FEL_RUS] +RUSSO + +{ new display menus } +[FET_GFX] +GRAPHICS SETUP + +[FED_MIP] +MIP MAPPING + +[FED_AAS] +ANTI ALIASING + +[FED_FIL] +TEXTURE FILTERING + +[FED_BIL] +BILINEAR + +[FED_TRL] +TRILINEAR + +[FED_WND] +WINDOWED + +[FED_FLS] +FULLSCREEN + +[FEM_CSB] +CUTSCENE BORDERS + +[FEM_SCF] +SCREEN FORMAT + +[FEM_ISL] +MAP MEMORY USAGE + +[FEM_LOW] +LOW + +[FEM_MED] +MEDIUM + +[FEM_HIG] +HIGH + +[FEM_2PR] +PS2 ALPHA TEST + +[FEC_FRC] +FREE CAM + +{ Linux joy detection } +[FEC_JOD] +DETECT JOYSTICK + +[FEC_JPR] +Press any key on the joystick of your choice that you want to use on the game, and it will be selected. + +[FEC_JDE] +Detected joystick + +{ mission restart } +[FET_RMS] +RIGIOCA MISSIONE + +[FESZ_RM] +RIGIOCA? + +[FED_VPL] +VEHICLE PIPELINE + +[FED_PRM] +PED RIM LIGHT + +[FED_RGL] +ROAD GLOSS + +[FED_CLF] +COLOUR FILTER + +[FED_WLM] +WORLD LIGHTMAPS + +[FED_MBL] +MOTION BLUR + +[FEM_SIM] +SIMPLE + +[FEM_NRM] +NORMAL + +[FEM_MOB] +MOBILE + +[FED_MFX] +MATFX + +[FED_NEO] +NEO + +[FEM_PS2] +PS2 + +[FEM_XBX] +XBOX + +[FEC_IVP] +INVERT PAD VERTICALLY + +[FEM_NON] +NONE + +[FEC_DS2] +DUALSHOCK 2 + +[FEC_DS3] +DUALSHOCK 3 + +[FEC_DS4] +DUALSHOCK 4 + +[FEC_360] +XBOX 360 CONTROLLER + +[FEC_ONE] +XBOX ONE CONTROLLER + +[FEC_NSW] +NINTENDO SWITCH CONTROLLER + +[FEC_TYP] +GAMEPAD TYPE + +[FET_AGS] +GAMEPAD SETTINGS + +[FEM_AUT] { aspect ratio related } +AUTO + +[FEM_PED] +PED DENSITY + +[FEM_CAR] +CAR DENSITY + +[DUMMY] +THIS LABEL NEEDS TO BE HERE !!! +AS THE LAST LABEL DOES NOT GET COMPILED \ No newline at end of file diff --git a/gxt/miami/spanish.txt b/gxt/miami/spanish.txt new file mode 100644 index 00000000..c3a4e0ee --- /dev/null +++ b/gxt/miami/spanish.txt @@ -0,0 +1,14422 @@ +{ + New strings are at the bottom of file. + Do not change the order of strings. + You can fix the typos of existing translation but please refrain from + unnecessary edits like rephasing because you think it suits better for your taste. + + SPANISH NOTE: + This is European Spanish, do not mix it with Latin American Spanish. + If you want the Latin American Spanish translation you'd have to create + a separate txt for it. +} + +{ Grand Theft Auto Vice City Spanish (Spain) Translation } +{ Contains some of the official fixes made by Rockstar for the iOS port } +{ Additional translation rewrites, corrections and fixes by IlDucci } +[IN_VEH] +~g~¡Eh! ¡Vuelve al vehículo! + +[HEY] +~g~¡No vayas solo, mantén tu grupo unido! + +[HELP3] +Solo puedes esprintar durante cortos periodos de tiempo, antes de cansarte. + +[HELP4_D] +Mueve el ~h~joystick analógico derecho~w~ hacia arriba para ~h~acelerar. + +[HELP5_D] +Tira del ~h~joystick analógico derecho~w~ hacia atrás para ~h~frenar~w~, o para ~h~dar marcha atrás~w~ si el vehículo se ha detenido. + +[HELP7_A] +Pulsa y mantén pulsado el ~h~~k~~PED_LOCK_TARGET~ ~w~para ~h~apuntar~w~ con el rifle de francotirador. + +[HELP7_D] +Pulsa y mantén pulsado el ~h~~k~~PED_LOCK_TARGET~ ~w~para ~h~apuntar~w~ con el rifle de francotirador. + +[HELP10] +Esta insignia indica que tienes un nivel de se busca por la policía. + +[HELP11] +Cuantas más insignias tengas, mayor es tu nivel de se busca. + +[HELP13] +Algunas veces puedes necesitar ir por caminos que el radar no detecta. + +[TIMER] +Ésta es una misión cronometrada, debes completarla antes de que el cronómetro llegue a cero. + +[HORN] +~g~Toca la bocina. + +[NOMONEY] +~g~¡Necesitas más dinero! + +[REWARD] +RECOMPENSA ~1~ $ + +[M_FAIL] +¡MISIÓN FALLIDA! + +[M_PASS] +¡MISIÓN SUPERADA! ~1~ $ + +[DEAD] +¡ELIMINADO! + +[BUSTED] +¡ARRESTADO! + +[WEATHE1] +FORZAR TIEMPO SOLEADO + +[WEATHE2] +FORZAR TIEMPO EXTREMADAMENTE SOLEADO + +[WEATHE3] +FORZAR TIEMPO NUBLADO + +[WEATHE4] +FORZAR TIEMPO LLUVIOSO + +[WEATHE5] +FORZAR TIEMPO CON NIEBLA + +[WEATHE6] +TIEMPO NORMAL + +[NUMBER] +~1~ + +[LOADCAR] +CARGANDO VEHÍCULO... (PULSA L1 PARA CANCELAR) + +[CARSOFF] +Coches desactivados. + +[CARS_ON] +Coches activados. + +[TEXTXYZ] +Escribiendo coordenadas en el archivo... + +[CHEATON] +Modo trucos ACTIVADO + +[CHEATOF] +Modo trucos DESACTIVADO + +[IMPORT1] +Ve fuera y espera tu vehículo. + +[PAGEB11] +Entregado lanzallamas en el escondite + +[WANT_A] +Sólo serás arrestado si tienes un ~h~nivel de se busca. + +[WANT_B] +Tu ~h~nivel de se busca~w~ viene representado por la fila de estrellas en la parte superior derecha de la pantalla. + +[WANT_C] +Ahora tienes un ~h~nivel de se busca~w~ de una... + +[WANT_D] +dos... + +[WANT_E] +tres... + +[WANT_F] +A medida que tu ~h~nivel de se busca~w~ aumente, atraerás a representantes más poderosos de las fuerzas policiales. + +[WANT_G] +Cuando te hayan ~h~''arrestado''~w~ serás conducido a la comisaría más cercana. + +[WANT_H] +Los polis te quitarán todas las armas y parte de tu dinero como soborno. + +[WANT_I] +Cualquier misión en la que estuvieses habrá fracasado. + +[WANT_J] +Encontrarás modos de reducir tu nivel de se busca cuanto más juegues. + +[WANT_K] +Si estás en un coche, el ~h~TALLER DE PINTURA~w~ limpiará ~h~tu nivel de búsqueda. + +[HEAL_B] +Cuando seas ~h~''eliminado''~w~ serás conducido al hospital más cercano. + +[HEAL_C] +Perderás tus armas y los médicos te quitarán algo de dinero por recomponerte. + +[HEAL_E] +Encontrarás modos de curarte o de protegerte cuanto más juegues al juego. + +[BRIBE1] +Acabas de recoger un soborno de la policía, esto disminuirá en una estrella tu nivel de se busca. + +[SAVE1] +Entra al corona para ~h~guardar la partida~w~. No puedes guardar durante una misión. + +[SAVE2] +Cualquier vehículo que quede en este garaje será almacenado cuando se guarde la partida. + +[AMMU] +Ve dentro de Ammu-Nation para comprar un arma. + +[R_TIME] +TIEMPO DE CARRERA: + +[PROP_1] +No tienes suficiente dinero para adquirir esta propiedad + +[PROP_2] +No puedes comprar propiedades mientras estás en una misión. + +[IND_ZON] +Vice City Beach + +[COM_ZON] +Península de Vice City + +[BEACH1] +Ocean Beach + +[BEACH2] +Washington Beach + +[BEACH3] +Vice Point + +[GOLFC] +Leaf Links + +[STARI] +Starfish Island + +[DOCKS] +Viceport + +[HAVANA] +Little Havana + +[HAITI] +Little Haiti + +[PORNI] +Prawn Island + +[DTOWN] +Centro de la ciudad + +[VICE_C] +Vice City + +[A_PORT] +Escobar International + +[JUNKY] +Vertedero + +[PISTOL] +Pistola + +[PYTHON] +.357 + +[UZI] +Uz-1 + +[TEC9] +Tec 9 + +[M4] +M4 + +[INGRAM] +Mac + +[MP5] +MP + +[RUGER] +Kruger + +[SNIPE] +Rifle de francotirador + +[GRENADE] +Granadas + +[SHOTGN1] +Escopeta + +[SHOTGN2] +S.P.A.S. 12 + +[SHOTGN3] +Escopeta de cañones recortados + +[ARMOUR] +Chaleco antibalas + +[LASER] +.308 Rifle de francotirador + +[BASEBAT] +Bate de béisbol + +[HAMMER] +Martillo + +[SCREWD] +Destornillador + +[CLEVER] +Cuchillo de carnicero + +[MACHETE] +Machete + +[KNIFE] +Cuchillo + +[KATANA] +Katana + +[CHAINSA] +Sierra eléctrica + +[G_COST] +~1~ $ + +[CAR_1] +Ambulancia + +[MALIBU] +El Club Malibú + +[MANSION] +Mansión de Díaz + +[TMANS] +Finca de Vercetti + +[STRIP] +El Club Pole Position + +[MALL1] +Centro comercial North Point + +[BANKINT] +El Banco Corrupto Grande + +[RANGE] +Campo de tiro con rifle + +[POL_HQ] +Cuartel general de VCPD + +[INT_B] +Un viejo amigo + +[INTB_1] +~g~Ve a la oficina del Abogado. + +[LAW_1] +La fiesta + +[LAW_2] +Pelea en el callejón trasero + +[LAW_3] +Furia en el jurado + +[LAW_4] +Disturbios + +[COL_1] +Cerdo traidor + +[COL_2] +Tiros en el centro comercial + +[COL_3] +Ángeles guardianes + +[COL_4] +¡Señor, sí, señor! + +[COL_5] +¡Todos con las manos arriba! + +[COK_1] +La caza + +[COK_2] +Phnom Penh '86 + +[COK_3] +El barco más rápido + +[COK_4] +Oferta y demanda + +[KENT_1] +El corredor de la muerte + +[ASS_1] +Borrar + +[BUD_1] +Extorsión + +[BUD_2] +Pelea en el bar + +[BUD_3] +Tierra de polis + +[CAP_1] +Liquida al cobrador + +[FIN_1] +Mantén cerca a tus amigos... + +[BANK_1] +¿Sin escapatoria? + +[BANK_2] +El tirador + +[BANK_3] +El conductor + +[BANK_4] +El atraco + +[CNT_1] +Descubriendo el pastel + +[CNT_2] +Ataca al mensajero + +[PORN_1] +Campaña de reclutamiento + +[PORN_2] +Consolador Dodo + +[PORN_3] +La foto policial de Marta + +[PORN_4] +Punto G + +[TAX_1] +Taxis Kaufman + +[TAXI_1] +V.I.P. + +[TAXI_2] +Rivalidad amistosa + +[TAXI_3] +Taxigedón + +[ICE_1] +Distribución + +[TEX_1] +Hierro número cuatro + +[TEX_2] +Dos leves impactos + +[TEX_3] +Demoledor + +[PHIL_1] +Traficante de armas + +[PHIL_2] +Boomshine Saigon + +[BIKE_1] +Moto con llantas de aleación + +[BIKE_2] +Incitando al macho + +[BIKE_3] +Moto robada + +[ROCK_1] +Love Juice + +[ROCK_2] +Asesino psicópata + +[ROCK_3] +Gira publicitaria + +[ROCK_4] +¡Love Fist! + +[HAT_1] +Poción mágica + +[HAT_2] +¡Bombas fuera! + +[HAT_3] +Juego sucio + +[CUB_1] +El desafío del barco trucado + +[CUB_2] +Carne de cañón + +[CUB_3] +Encuentro naval + +[CUB_4] +Vudú troyano + +[JOB_1] +Muerte en la carretera + +[JOB_2] +Elimina a la esposa + +[JOB_3] +Autocidio + +[JOB_4] +Comprobar el registro + +[JOB_5] +Cabos sueltos + +[ANSWER] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para responder a una llamada a tu teléfono móvil. + +[MOB_01A] +¡Estás bien figura! Soy Paul. Puede que tenga algún resultado para ti, pero necesito hablar contigo cara a cara. + +[MOB_01B] +Estoy de relax en el Malibu. + +[MOB_01C] +Creo que después de esto me vas a deber un favor o dos, guapo. Hasta luego. + +[MOB_02A] +Ssssnniiiiffffff... ¡Eh! Hola, ¿Tommy? ¡Tommy! + +[MOB_02B] +Tenemos un problema en la imprenta. Mejor que vayas y lo compruebes. + +[MOB_02C] +Algún tipo de lío. Las cosas andan revueltas. Tengo que irme. + +[MOB_03A] +¿Sr. Vercetti? Tengo un trozo de mierda firmada que dice + +[MOB_03B] +que usted ha asumido todas las deudas de BJ's Auto. + +[MOB_03C] +Con la repentina desaparición de BJ no tengo más remedio + +[MOB_03D] +que hacerle responsable de su inestabilidad financiera + +[MOB_03E] +Hasta que esta cuenta quede completamente saldada + +[MOB_03F] +debería considerar que las calles de Vice City son poco seguras. + +[MOB_04A] +¿Cómo estás, tío? Soy Paulo. + +[MOB_04B] +Mira, Tommy, me olvidé de mencionar que vamos a necesitar más seguridad para el concierto. + +[MOB_04C] +Hay una banda de moteros liderada por Mitch Baker que serían la publicidad ideal. + +[MOB_04D] +Solucióname esto y te conseguiré un pase para la actuación, ¿vale? + +[MOB_05A] +Soy Mitch. Hiciste un buen trabajo, Tommy. Es bueno volver a tener a mi pequeña en casa. + +[MOB_05B] +Dile al Sr. Kent Paul que dispondrá de seguridad para la actuación. + +[MOB_05C] +Tiene mi palabra de ello. + +[MOB_05D] +Intenta no meterte en problemas. + +[MOB_06A] +Tommy, esos de abajo han estado rajando sobre ti, chico. + +[MOB_06B] +Pensé que podrías necesitar consuelo casero así que la tía Poulet te preparará una sopa, ¿hmmm? + +[MOB_06C] +Pásate por mi cocina alguna vez, ¿de acuerdo Tommy? + +[MOB_08A] +Eh Tommy, pensé que podrías necesitar algún asesoramiento financiero. + +[MOB_08B] +Una vez que tengas la operación en marcha, tendrás que pasarte a recoger la recaudación de la semana. + +[MOB_08C] +Deja que esos tipos crean que son los amos del lugar e intentarán cepillarse los beneficios... ¿vale? + +[MOB_08D] +Eh, sé como llevar el negocio, Ken. + +[MOB_08E] +Vale, vale. Lo sé, ya sabes. Lo sé. Estaba, + +[MOB_08F] +Sólo estaba, ya sabes, diciéndote que sé, que tú sabes que yo lo sé. + +[MOB_08G] +¡Solo manteniéndolo a punto, nene! + +[MOB_08H] +Lo que sea, Ken, lo que sea... + +[MOB_09A] +¡Eh, Leo! ¡Tengo trabajo para ti! + +[MOB_09B] +No soy Leo. + +[MOB_09C] +¡Eh, como Leo sepa que tienes su teléfono, te va a liquidar! + +[MOB_09D] +Quizás Leo ya esté muerto. Quizás yo le maté y cogí su teléfono, ¿te has parado a pensar eso, gilipollas? + +[MOB_09E] +¿Mataste a Leo? Debes tener unas pelotas muy grandes... ¿quieres trabajar para mí? + +[MOB_09F] +Pásate por el café de mi padre en Little Havana y podremos hablar de tú a tú. + +[MOB_10A] +¡Tommy! Mira, tengo que pedirte un favor. + +[MOB_10B] +¡Steve! ¿Cómo van con el rodaje? + +[MOB_10C] +Bien, bien. Yo, je, NOSOTROS necesitamos una escena con una persecución de coches, pero nuestro presupuesto no da para más. + +[MOB_10D] +Me han dejado un coche en la ciudad. Tú sabrás qué hacer. + +[MOB_10E] +De acuerdo, Steve, estaré al tanto. Te veo luego. + +[MOB_11A] +¿Qué hay, hijo? Pensé sólo en llamarte para darte algún consejo. + +[MOB_11B] +Hola, Avery. ¿Qué te preocupa? + +[MOB_11C] +Hay muchas oportunidades en esta ciudad si dispones de las propiedades adecuadas, ¿entiendes lo que quiero decir? + +[MOB_11D] +Creo que sí... + +[MOB_11E] +Lo que digo es que mantengas los ojos abiertos y podrás encontrar la oportunidad de negocio perfecta. Te veré luego. + +[MOB_11F] +Nos vemos, Avery. + +[MOB12_A] +¡Eh, Tommy, soy Avery! Ahora escucha, estoy muy liado en este momento + +[MOB12_B] +y uno de mis representantes necesita carabina para ir a Gator Keys. + +[MOB12_C] +Estoy detrás de unas tierras por allí, así que voy a enviar alguien a que ablande el trato. + +[MOB12_D] +Podrías hacerme un favor y asegurarte de que llega allí, ¿entendido? + +[MOB12_E] +Sí, eso está hecho, Avery. ¿Dónde te gustaría que le recogiese? + +[MOB12_F] +Está terminando algunos negocios en la obra. Le dije que le recogerías allí. + +[MOB12_G] +Ningún problema. Nos vemos, Avery. + +[MOB13_A] +¿Vercetti? ¡VERCETTI! ¡Maldito seas, tío, tienes que ayudarme! + +[MOB13_B] +¿Sr. Moffat? ¿Cómo le va la vida en familia? + +[MOB13_C] +Maldito seas, DEMONIOS, ¿me oyes? + +[MOB13_D] +Bueno, fue agradable charlar... + +[MOB13_E] +¡ESPERA! Espera, Vercetti Tommy, ¿Puedo llamarte Tommy? + +[MOB13_F] +Los dos somos hombres de negocios, ¿verdad? Reconoces un buen negocio cuando lo ves. + +[MOB13_G] +No tengo tiempo para charlar, vaya al grano. + +[MOB13_H] +DINERO. El dinero es el maldito asunto. + +[MOB13_I] +Me escapado de la trena otra vez, pero nunca tardan mucho en encontrarme... ¡creen que es un maldito juego! + +[MOB13_J] +Estoy en un teléfono público en alguna parte de este agujero de mierda olvidado de la mano de Dios. + +[MOB13_K] +Sácame de aquí antes de que me lleven de vuelta y... oh... Dios... + +[MOB13_L] +Bueno, estoy ocupado durante las próximas... + +[MOB13_M] +¡No! ¡No me jodas, ten piedad! Ningún hombre debería tener que hacer esas, esas cosas. + +[MOB13_N] +Estoy de rodillas, Tommy, en el fango, rogándote, por favor... + +[MOB13_O] +Supongo que podría desviarme hacia allí, veré si puedo localizarte... + +[MOB13_P] +Oh, Dios, ya vienen. Por el amor de Dios, date prisa, ¡date prisa! + +[MOB_14A] +Hola, Tommy, me vas a adorar, amigo. + +[MOB_14B] +Un pajarito me ha dicho que la división SWAT de Vice City tiene un depósito en una entidad bancaria ciertamente grande, + +[MOB_14C] +donde guardan todos los sobornos que han recibido durante estos años, + +[MOB_14D] +¡como una especie de fondo de retiro para los veteranos! + +[MOB_14E] +Por supuesto, si esta información te ayudase a adquirir parte de ese dinero, + +[MOB_14F] +¿Supongo que te sentirías obligado a mandarme algo? + +[MOB_14G] +Lo tendré en mente, gracias Kent. + +[MOB_14H] +Es Paul. Soy de Kent, cerca de Londres, idiota. + +[MOB_14I] +Mi conocimiento de la geografía provincial inglesa ya no es lo que era. + +[MOB15_A] +Tommy, colega, soy Paul, de Kent, + +[MOB15_B] +un par de titis han escrito tu nombre por todas partes, en el Malibú. + +[MOB15_C] +¿De qué estás hablando? + +[MOB15_D] +Tías. Palomitas. Ya sabes. Chicas. De las buenas, no creas que son furcias ni nada por el estilo. + +[MOB15_E] +Tienes que venir a echarles un vistazo. + +[MOB16_A] +Tommy, aquí Paulo, ¿qué pasa amigo? + +[MOB16_B] +¿Qué quieres Paul? No quiero ropa de imitación. + +[MOB16_C] +Muy divertido, colega, pero yo no toco la ropa falsificada. + +[MOB16_D] +Nada, sólo llamaba para ver si consigo un papel en una de tus películas, + +[MOB16_E] +en Inglaterra hice un montón de porno, colega. + +[MOB16_F] +Guardo más fuego que tu, hijo mío. + +[MOB16_G] +Paul, gracias por la oferta, lo tendré en mente. + +[MOB16_H] +En serio, no te olvides de mí, después de todo lo que he hecho por ti. + +[MOB16_I] +Eso es lo que estoy intentando olvidar. + +[MOB19_A] +Tommy V, aquí KP. Kent Paul. En las calles se dice que hay gente que te quiere despellejar. + +[MOB19_B] +Mantén los ojos abiertos. Y recuerda, yo no te he dicho nada de esto. + +[MOB_20A] +De acuerdo, Tommy, soy Paul. Me ha dicho un pajarito que has sido un chico muy malo. + +[MOB_20B] +A alguien le ha ofendido que de repente estés actuando como si fueses el mandamás, dándote aires de grandeza. + +[MOB_20C] +Bueno, no digas que nunca te advertí. Presumir es un juego de tontos, hijo. + +[MOB_20D] +En cualquier caso, oí que se ha puesto un precio a tu cabeza y que alguien te la va a partir, + +[MOB_20E] +así que ten cuidado, y acuérdate de mí, colega. + +[MOB21_A] +Tommy, Thomas, soy Cortez. ¿Qué pasa? + +[MOB21_B] +Las cosas van bien. ¿Cómo estás? + +[MOB21_G] +De todos modos, Tommy, quisiera preguntarte algo sobre Mercedes. + +[MOB21_H] +¿Sí? ¿Qué pasa con ella? + +[MOB21_I] +Oh Tommy, Tommy, oigo todas esas historias y no sé qué pensar. + +[MOB21_K] +Quizás ella piense que puede hacer lo que quiera ahora que pero dime, Tommy, ¿es verdad? + +[MOB21_M] +¿El qué es verdad? + +[MOB21_N] +Las historias que he oído. ¿De verdad va a ser abogada? + +[MOB21_O] +Oh Tommy, la vergüenza. Los Cortez somos una familia orgullosa + +[MOB21_P] +y nunca permitiríamos que una hija nuestra se convirtiese en abogada. Por favor, dime que no es así. No creo que pudiese soportarlo. + +[MOB21_Q] +Coronel, puedo asegurarle que Mercedes nunca será abogada. No tema. + +[MOB21_R] +Gracias, Tommy, gracias. La vergüenza sería insoportable. + +[MOB21_S] +Lo sé, coronel. + +[MOB21_T] +Bueno, Tommy, debes perdonarme, ha llegado el nuevo ministro de interior. + +[MOB21_U] +Hace muchos años, yo maté a su padre en un golpe fallido así que debo ser cortés. Buenos días, amigo. + +[MOB21_C] +Tommy, aquí siempre es una lucha. Siento el ruido, acabamos de tener otro golpe fallido. + +[MOB21_D] +La gente es la señora más exigente de todas. + +[MOB21_E] +Desde que he vuelto de Vice City y hasta ahora, hemos tenido tres revoluciones y cuatro golpes. + +[MOB21_F] +Afortunadamente, me han ascendido cada vez. + +[MOB21_J] +Quizás todo el mundo me está humillando. + +[MOB21_L] +pero dime, Tommy, ¿es verdad? + +[MOB22_A] +Tommy, estás demostrando ser muy útil, amigo mío. + +[MOB22_B] +Gracias, Cortez. ¿Qué hay de mi asunto? + +[MOB22_C] +Tommy, estoy trabajando infatigablemente en tu nombre para conseguir llegar al fondo de esta zanja de apestosas mentiras y engaños, + +[MOB22_D] +tienes mi palabra al respecto, pero mientras tanto, + +[MOB22_E] +por favor, acepta el estimable agradecimiento de mi gente por tu trabajo para nosotros. + +[MOB_25A] +Tommy, soy Cortez. Los franceses me están causando todo tipo de problemas. + +[MOB_25B] +Malditos hipócritas. ¡Se pasan cien años robando a países pobres y me llaman ladrón a mí! + +[MOB_25C] +Voy a necesitar tu ayuda tan pronto como sea posible. + +[MOB_25D] +Por favor, Tommy, date prisa, te necesito, ¿de acuerdo? Odio a esos malditos franceses. + +[MOB_26A] +Hola, ¿Tommy? + +[MOB_26B] +¿Sí? + +[MOB_26C] +Soy Baker. Sólo quería decirte que verdaderamente disfruté con el espectáculo. + +[MOB_26D] +Los chicos y yo queremos darte las gracias, y recordarte, + +[MOB_26E] +que te tienes nuestro respeto. Buenos días. Sigue cabalgando duro, chico. + +[MOB_29A] +Hola, ¿hablo con el Sr. Tommy Vercetti? + +[MOB_29B] +Sí. + +[MOB_29C] +Bueno, he oído por radio macuto que usted es el hombre que hay que buscar cuando alguien tiene una plaga de bichos. + +[MOB_29D] +Puede... + +[MOB_29E] +Bueno, yo tengo una plaga de bichos de verdad. Haitianos, por todas partes. + +[MOB_29F] +Mi nombre es Umberto Robina y quiero que nos reunamos en el Café Robina lo antes posible, + +[MOB_29G] +porque, como le digo, estos malditos haitianos se han pasado de la raya esta vez. + +[MOB_29H] +Prueba + +[MOB_30A] +Tommy, soy Umberto Robina. + +[MOB_30B] +¿Cómo está el café? + +[MOB_30C] +Oh, maravilloso. Increíble. Tommy, increíble. Sin blandengues, Tommy, sólo hombres de verdad, ya sabes, ¡y las mujeres hermosas! + +[MOB_30D] +De todos modos, quería decirte que para papá y para mí, para nosotros, ahora eres un cubano. + +[MOB_30E] +Has demostrado que tienes unas pelotas de cojones, tío. + +[MOB_30F] +Gracias, Umberto. Nadie me había dicho eso desde que salí de la cárcel. Nos vemos. + +[MOB_33A] +Tommy, soy Phil, olvida toda esa mierda del pasado y escúchame, ¿me oyes? + +[MOB_33B] +Bien. Tengo un pelotazo extra a punto de fermentar y me preguntaba si te apetecería probarlo. + +[MOB_33C] +En serio, Tommy, si quieres una copa, o si necesitas ponerte a tono, este material hará de ti un hombre. + +[MOB_33D] +A mí me pasó aunque todavía no puedo ver por un ojo. Te estaré esperando, lo que oyes. + +[MOB_34A] +Tommy, de verdad que he disfrutado trabajando contigo. No me había divertido tanto desde la loma en Vietnam, colega. + +[MOB_34B] +De todas formas, si necesitas algo, llámame, ¿me oyes? + +[MOB_34C] +Siempre me acuerdo de aquellos con los que he servido, + +[MOB_34D] +y estoy seguro de que puedo ayudarte, ¿me oyes? + +[MOB_35A] +Tommy, la herida está cicatrizando bien. Lo gracioso es + +[MOB_35B] +que he luchado en 6 zonas de combate y siempre he salido sin un rasguño, ¡y ahora esto! + +[MOB_35C] +Phil el manco. Aún así, tengo una buena selección armas de fuego para mancos, así que nunca seré Phil el desarmado, lo que oyes. + +[MOB_35D] +Venga, hijo, corta con esa mierda sentimental y pídete una copa, ¡me oyes! + +[MOB_36A] +Tommy, soy Phil, quiero darte las gracias por ayudarme allí, hijo, + +[MOB_36B] +maldito Charlie, siempre te tenderá una emboscada de un modo o de otro. + +[MOB_36C] +Bueno, la herida está cicatrizando bien, y eso quiere decir que voy a dejar de estafar al gobierno con mi cheque de incapacidad. Gracias, colega. + +[MOB_40A] +Eh, Tommy, soy Sonny. ¿Qué tal el bronceado? + +[MOB_40B] +No tengo bronceado. + +[MOB_40C] +Bueno, tampoco tienes mi dinero, así que me estoy preguntando, + +[MOB_40D] +¿qué estás haciendo? Así que, dime Tommy ¿qué estás haciendo? + +[MOB_40E] +Estoy buscando el dinero, Sonny. No te preocupes. + +[MOB_40F] +Me estoy preocupando, Tommy, ese es mi estilo, + +[MOB_40G] +porque parece que en mi vida tengo este problema con gente que no es de confianza. + +[MOB_40H] +No seas una persona poco fiable, Tommy, por favor. + +[MOB_40I] +Haznos un favor. Tengo muchas ganas de tener noticias tuyas. + +[MOB_41A] +Tommy, ¿te acuerdas de mí? + +[MOB_41B] +Hola, Sonny. + +[MOB_41C] +Sí, está bien, Sonny. Somos viejos amigos, + +[MOB_41D] +y nunca me escribes, nunca llamas. ¿No quieres que sigamos siendo amigos? + +[MOB_41E] +He estado muy ocupado intentando arreglar las cosas. No me diste mucho apoyo. + +[MOB_41F] +Oh, y es culpa mía, ¿verdad? Bueno, he oído que has estado muy ocupado. + +[MOB_41G] +Ocupado matando a capos. Ocupado tomando el poder. + +[MOB_41H] +No te olvides de nosotros, Tommy, porque puedo asegurarte que yo no me he olvidado de ti. + +[MOB_42A] +Tommy. + +[MOB_42B] +Sonny. + +[MOB_42C] +Obviamente tienes un problema a la hora de escuchar a los demás, de modo que lo intentaré de nuevo. + +[MOB_42D] +Tommy, ¿dónde está el maldito dinero, dónde está el maldito material y dónde está mi parte de tus nuevos golpes? + +[MOB_42E] +Me estás haciendo quedar como un idiota, Tommy, y no me hace gracia. + +[MOB_43A] +Tommy, Tommy, Tommy, tenía a Sonny al teléfono, de acuerdo, ¿me sigues? + +[MOB_43B] +No sé tú, pero hay algo sobre un hombre que amenaza con asesinar a mi familia + +[MOB_43C] +que realmente me pone enfermo. ¿Qué vas a hacer? + +[MOB_43D] +Ten calma, Ken, todo irá bien. + +[MOB_43E] +¡ESTOY calmado, todo lo calmado que puede estar un hombre cuando teme por su vida! + +[MOB_43F] +Ken, ahora mismo tengo otras cosas en la cabeza, + +[MOB_43G] +nos ocuparemos de Forelli cuando llegue el momento. + +[MOB_43H] +Estoy calmado. ¿No te lo parece? Debe ser la muerte inminente la que le hace esto a mi voz. + +[MOB45_A] +Tommy, tenemos cosas de las que hablar. + +[MOB45_B] +¿Cuál es el problema, Lance? + +[MOB45_C] +Eres tú, amigo, creo que no me estás dando una tajada justa. + +[MOB45_D] +Y lo que es más, me estás avergonzando delante de los chicos. No puedo permitirlo. + +[MOB45_E] +Lance, las cosas no son así. Has estado cometiendo errores. + +[MOB45_F] +Tommy, no soy tu chico de los recados. No soy tu emisario. + +[MOB45_G] +Lance, no la cagues, y no tendremos ningún problema. Si la cago yo, entonces podrás venir a reprochármelo en cualquier momento. + +[MOB45_H] +Tommy, he hecho de todo por ti, pero me tratas como si fuese estúpido. No hagas eso. + +[MOB45_I] +Lance no te voy a timar ni a apuñalarte por la espalda, de acuerdo. + +[MOB45_J] +Tómatelo con calma. Esto ya es lo suficientemente duro sin que te me pongas sentimental. + +[MOB45_K] +Confía en mí. ¿Me oyes, me oyes? + +[MOB45_L] +Te oigo, pero Tommy, no puedo aguantar esto por más tiempo. + +[MOB45_M] +Lance, no seas así. Ahora te estoy dando un aviso. + +[MOB45_N] +¿Me oyes? Relájate, tómate unos días libres, y luego hablaremos, ¿vale? + +[MOB46_A] +¡Eh, Tommy! Soy Lance. + +[MOB46_B] +¿Sí? + +[MOB46_C] +Me alegro tanto de tener noticias tuyas, Lance. Venga, tío, enróllate, enróllate. + +[MOB46_D] +Estoy ocupado. ¿Qué quieres? + +[MOB46_E] +Nada. Solo llamo para decirte, ya sabes. Mira Tommy, podemos hacer esto. + +[MOB46_F] +Tú y yo, sin ningún problema. ¿Sabes lo que quiero decir? + +[MOB46_G] +Vamos a tener que hacerlo, porque de otro modo, estaremos muertos, Lance. + +[MOB46_H] +Ya hemos ido demasiado lejos. Pero gracias por llamar. Hablaré contigo más tarde. + +[MOB_47A] +Tommy, Lance, tenemos problemas serios. Ven aquí. Enseguida. + +[MOB52_A] +Eh, Leo, creo que tengo un comprador para la mercancía de Díaz. + +[MOB52_B] +Tienes que llamarle, tío, montar el trato, ¿sabes? + +[MOB52_C] +¿Dónde estás? + +[MOB52_D] +¿Estás bien, Leo? Te noto un tanto diferente. + +[MOB52_E] +Simplemente dime dónde estás. + +[MOB52_F] +¿Quién demonios eres? ¡Que se ponga Leo! + +[MOB52_G] +Leo se ha ido de viaje unos días, me dejó al cargo de todo. + +[MOB52_H] +¡Que te jodan, tío! + +[MOB54_A] +¡Hola, Tommy! + +[MOB54_B] +Hola, Mercedes, ¿cómo te va? + +[MOB54_C] +Tengo un apartamento nuevo en Vice Point, + +[MOB54_D] +pensé que quizás querrías pasarte en algún momento. + +[MOB54_E] +Me encantaría. Te veo luego. + +[MOB55_A] +Tommy, soy yo. + +[MOB55_B] +Hola, Mercedes. + +[MOB55_C] +Tommy, estoy tan aburrida, ¿cuándo vamos a divertirnos un poco? + +[MOB55_D] +¿Qué quieres decir? + +[MOB55_E] +Bueno, sé que estás ocupado luchando y matando y sobornando a gente, + +[MOB55_F] +pero yo sólo quiero divertirme un poco. Así que no te olvides de mí, ¿me oyes? + +[MOB56_A] +Tommy, oí que te cargaste a Ricardo Díaz. + +[MOB56_B] +Hubo un desafortunado tiroteo en su mansión. + +[MOB56_C] +Creo que se quemó hasta morir en su camisa acrílica. + +[MOB56_D] +Tommy, estoy tan orgullosa de ti. Sabía que eras un hombre de verdad. + +[MOB56_E] +Era un marrano en pantalones, me haces sentir muy orgullosa de ser tu amiga. + +[MOB56_F] +No, sé que vas a estar ocupado intentando tomar las riendas de esta ciudad, + +[MOB56_G] +pero no te olvides de mí, ¿me oyes? + +[MOB57_A] +Soy Mercedes. Ya no te amo, Tommy. + +[MOB57_B] +Ya no lo hago. De verdad. Porque ya no eres amable con Mercedes. + +[MOB57_C] +Ya no la tratas como una dama. Me ignoras y te odio. + +[MOB57_D] +¡Insisto en que vengas a verme enseguida! + +[MOB58_A] +Tommy. + +[MOB58_B] +Hola, Mercedes. + +[MOB58_C] +Hola, tipo duro. Estoy muy enfadada contigo, Tommy. + +[MOB58_D] +No me hagas ir nunca más por ahí con Jezz Torrent otra vez. + +[MOB58_E] +Es patético. A mitad de camino, empieza a llorar por su perrito + +[MOB58_F] +que murió cuando él tenía siete años y que si su madre nunca le había querido. + +[MOB58_G] +Y Tommy. En privado lleva peluca y sostén. + +[MOB58_H] +¡No estoy muy contenta contigo! + +[MOB59_A] +Ooh Tommy, soy Mercedes. + +[MOB59_B] +Sólo quería decirte que me divertí mucho en el plató de rodaje. + +[MOB59_C] +Cualquier otra cosa que tengas así, me lo dices. + +[MOB59_D] +Lo digo de verdad. Siempre quise ser actriz. + +[MOB59_E] +Creo que aprendí mucho sobre el proceso dramático. + +[MOB59_F] +¡Es tan instructivo! Gracias. Gracias. Hasta muy, muy pronto. Adiós. + +[MOB_99] +Ve al teléfono público del lugar. + +[MOB_98] +Ve al teléfono público del lugar. + +[MOB_97] +Ve al teléfono público del lugar. + +[MOB_96] +Ve al teléfono público del lugar. + +[MOB_95] +Ve al teléfono público del lugar. + +[A_TIME] ++~1~ segundos + +[DODO_FT] +¡Volaste durante ~1~ segundos! + +[GA_8] +Utiliza el detonador para activar la bomba. + +[GA_10] +Muy bien. Aquí están tus ~1~ $. + +[GA_11] +Ya tenemos ese coche. ¡No nos sirve! + +[GA_12] +Bomba activada. + +[GA_13] +Entregado como un profesional. Completa la lista y habrá una bonificación para ti. + +[GA_14] +Todos los coches. ¡ESTUPENDO! Aquí tienes una cosilla. + +[GA_15] +Espero que te guste el nuevo color. + +[GA_16] +Volver a pintarlo es gratis. + +[GA_19] +No estamos interesados en este modelo. + +[GA_20] +Tenemos más de eso de los que podemos colocar. Lo siento, tío, no hay trato. + +[CHASE] +Atención de la prensa + +[CHASE1] +Ignorado + +[CHASE2] +Aburrido + +[CHASE3] +Algo interesante + +[CHASE4] +Página 7 del periódico local + +[CHASE5] +Portada del periódico local + +[CHASE6] +Página 2 del Vice Courier + +[CHASE7] +Portada del Vice Courier + +[CHASE8] +TV Local a las 3 de la mañana + +[CHASE9] +Noticias locales en TV + +[CHASE10] +Cobertura en directo de la TV Local + +[CHASE11] +Página 12 del UFA Today + +[CHASE12] +Página 4 del UFA Today + +[CHASE13] +Imagen en UFA Today + +[CHASE14] +TV Nacional a las 4 de la mañana + +[CHASE15] +Noticias de la TV Nacional + +[CHASE16] +Cobertura en directo de la TV Nacional + +[CHASE17] +Noticias internacionales + +[CHASE18] +Crisis nacional + +[CHASE19] +Crisis internacional + +[CHASE20] +Acontecimiento mundial + +[CHASE21] +Leyenda + +[CR_1] +La grúa no puede levantar este vehículo + +[PU_MONY] +No tienes suficiente dinero. + +[CO_ALL] +Los tienes todos. Aquí tienes una cosilla... + +[FEM_ON] +SÍ + +[FEM_OFF] +NO + +[FEM_YES] +Sí + +[FEM_NO] +No + +[FEM_RET] +REINTENTAR + +[FEC_NA] +NO DISPONIBLE + +[FEC_CWL] +Escoger arma hacia la izquierda + +[FEC_CWR] +Escoger arma hacia la derecha + +[FEC_LOF] +Mirar hacia adelante + +[FEC_TAR] +Objetivo + +[FEC_MOV] +Movimiento + +[FEC_CAM] +Modos de cámara + +[FEC_PAU] +Pausa + +[FEC_ENV] +Entrar en el vehículo + +[FEC_JUM] +Saltar + +[FEC_ATT] +Atacar o disparar arma + +[FEC_RUN] +Correr + +[FEC_FPC] +Cámara en primera persona + +[FEC_LL] +Mirar a la izquierda + +[FEC_LB] +Retrovisor + +[FEC_LR] +Mirar a la derecha + +[FEC_ABR] +Acelerar, frenar o dar marcha atrás + +[FEC_HOR] +Bocina + +[FEC_VES] +Control del vehículo + +[FEC_BRA] +Freno o marcha atrás + +[FEC_HAB] +Freno de mano + +[FEC_CAW] +Arma del vehículo + +[FEC_ACC] +Acelerar + +[FEC_CCF] +Configuración: + +[FEC_CF1] +Configuración 1 + +[FEC_CF2] +Configuración 2 + +[FEC_CF3] +Configuración 3 + +[FEC_CF4] +Configuración 4 + +[FEC_CDP] +Pantalla del mando + +[FEC_ONF] +A pie + +[FEC_INC] +En coche + +[FEC_VIB] +Vibración : + +[FEL_ENG] +Inglés + +[FEL_FRE] +Francés + +[FEL_GER] +Alemán + +[FEL_ITA] +Italiano + +[FEL_SPA] +Español + +[FED_DBG] +Menú de soluciones técnicas + +[FED_RID] +Recargar IDE + +[FED_RIP] +Recargar IPL + +[FED_PAH] +Parse Heap + +[FED_DFL] +CLos guiones::DbgBandera + +[FED_DLS] +Gran luz blanca de solución técnica encendida + +[FED_SPR] +Mostrar grupos de aceras + +[FED_SCR] +Mostrar grupos de carreteras + +[FED_SCZ] +Mostrar zonas de desechos + +[FED_DSR] +Solicitud de solución técnica del streaming + +[FED_SCP] +gbMostrarcolisiónPolys + +[PL_STAT] +Estadísticas del jugador + +[PE_WAST] +Gente que te has cargado + +[PE_WSOT] +Gente eliminada por otros + +[TM_BUST] +Veces detenido + +[GNG_WST] +Miembros de la banda liquidados + +[DED_CRI] +Criminales liquidados + +[PER_COM] +Porcentaje completado + +[KGS_EXP] +Kilogramos de explosivos empleados + +[ACCURA] +Precisión + +[ST_WEAP] +Presupuesto armamentístico + +[ST_PROP] +Presupuesto de propiedades + +[ST_AUTO] +Presupuesto para reparación de coches y pintura + +[ST_PHOT] +Fotografías tomadas + +[ST_LOAN] +Visitas de usureros prestamistas + +[ST_STOR] +Almacenes desvalijados + +[ST_MOVI] +Escenas de especialista + +[ST_PIZZ] +Pizzas entregadas + +[ST_GARB] +Recogidas de basura efectuadas + +[ST_ICEC] +''Helado'' vendido + +[TOP_SHO] +Puntuación máxima en el campo de tiro + +[SHO_RAN] +Clasificación del campo de tiro + +[SEAGULL] +Gaviotas disparadas + +[PROPOWN] +Propiedad en posesión + +[ST_TIME] +Tiempo de juego + +[ST_FTIM] +Horas de vuelo + +[ST_PRAN] +Clasificación de los pilotos + +[ST_RAN0] +Aprendiz + +[ST_RAN1] +Navegante + +[ST_RAN2] +Copiloto + +[ST_RAN3] +Junior + +[ST_RAN4] +Competente + +[ST_RAN5] +Senior + +[ST_RAN6] +As + +[ST_RAN7] +Barón rojo + +[ST_DRWN] +Peces alimentados + +[ST_FASH] +Presupuesto para moda + +[ST_DAMA] +Coste de propiedades destruidas + +[TM_DED] +Visitas al hospital + +[DAYSPS] +Días transcurridos en el juego + +[NUMSHV] +Visitas a pisos francos + +[MXCARD] +Max. Distancia de salto LOCO (pies) + +[MXCARJ] +Max. Altura de salto LOCO (pies) + +[MXCARDM] +Máxima distancia de salto LOCO (m) + +[MXCARJM] +Máxima altura de salto LOCO (m) + +[MXFLIP] +Máximas vueltas de salto LOCO + +[MXJUMP] +Máxima rotación de salto LOCO + +[BUL_FIR] +Balas disparadas + +[BUL_HIT] +Aciertos + +[SPRAYIN] +Pintura + +[BSTSTU] +Mejor maniobra LOCA hasta ahora + +[INSTUN] +Maniobra loca + +[PRINST] +Maniobra loca perfecta + +[DBINST] +Maniobra loca doble + +[DBPINS] +Maniobra loca doble perfecta + +[TRINST] +Maniobra loca triple + +[PRTRST] +Maniobra loca triple perfecta + +[QUINST] +Maniobra loca cuádruple + +[PQUINS] +Maniobra loca cuádruple perfecta + +[NOSTUC] +No se ha completado ninguna + +[NOUNIF] +Saltos únicos completados + +[NMISON] +Intentos de misión + +[PASDRO] +Pasajeros en destino + +[MONTAX] +Dinero conseguido con el taxi + +[DAYPLC] +Gastos diarios provocados a la policía + +[CRIMRA] +Tasa criminal: + +[STPR_1] +El Malibú + +[STPR_2] +Imprenta + +[STPR_3] +Estudio de cine + +[STPR_4] +Fábrica de helados + +[STPR_5] +Salón de coches + +[STPR_6] +Compañía de taxis + +[STPR_7] +Astillero + +[SET1EN] +Configuración 1. Activada + +[GMSAVE] +Guardar partida + +[FEDS_TB] +Volver + +[FEST_OO] +de + +[FEC_TUC] +Control de la torreta + +[FEC_RS3] +Cambiar emisora de radio (Botón L3) + +[FEC_HO3] +Bocina (Botón L3) + +[C_FAIL] +¡Misión de justiciero terminada! + +[C_ESCP] +~r~¡El sospechoso ha escapado! + +[C_VIGIL] +¡BONIFICACIÓN DE JUSTICIERO! + +[HEAL_A] +Tu ~h~salud~w~ aparece en naranja en la parte superior derecha de la pantalla. + +[WRONGCD] +Disco incorrecto. Introduce el disco correcto. + +[NOCD] +La bandeja del disco está vacía. Por favor, inserta el disco. + +[OPENCD] +La bandeja del disco está abierta. Por favor, cierra la bandeja de disco. + +[CDERROR] +Error al leer el DVD de Grand Theft Auto: Vice City + +[RESTART] +Iniciando nueva partida + +[GA_3] +No hay más regalitos. ¡100 $ para volver a pintar! + +[GA_1] +¡Hala! ¡Demasiado peligroso para mi gusto! + +[GA_1A] +Vuelve cuando no estés tan ocupado... + +[HELP9_C] +Pulsa ~h~~k~~k~~PED_FIREWEAPON~~w~para ~h~disparar~w~ el fusil de francotirador. + +[TAXI2] +~r~¡Te has quedado sin tiempo! + +[PAGEB13] +Salud entregada en el escondite + +[PAGEB14] +Adrenalina entregada en el escondite + +[FESZ_CA] +Cancelar + +[FES_NGA] +Nueva partida + +[FES_CAN] +Cancelar + +[FESZ_QL] +Todo el progreso no guardado de tu partida actual se perderá. ¿Continuar cargando? + +[FESZ_QD] +¿Proceder a borrar esta partida guardada? + +[FESZ_QO] +¿Proceder para sobrescribir esta partida guardada? + +[FESZ_QR] +¿Seguro que quieres empezar una nueva partida? Todo el progreso desde la última partida guardada se perderá. ¿Continuar? + +[T4X4_1] +Prueba de PCJ + +[BMX_1] +Trial de tierra + +[BMX_2] +Pista de pruebas + +[BMXFAIL] +~r~¡No has conseguido un nuevo récord! + +[BMX_REC] +~g~¡Nuevo récord establecido: ~1~! + +[T4X4_3] +¡AGARRADO! + +[MM_1] +CONOCIRCUITO + +[T4X4_F] +~r~¡Te rajaste! ¿Demasiado duro para ti? + +[LANDSTK] +Landstalker + +[IDAHO] +Idaho + +[STINGER] +Stinger + +[LINERUN] +Linerunner + +[PEREN] +Perennial + +[SENTINL] +Sentinel + +[RIO] +Río + +[PATRIOT] +Patriot + +[FIRETRK] +Camión de bomberos + +[TRASHM] +Trashmaster + +[STRETCH] +Limusina + +[MANANA] +Mañana + +[INFERNS] +Infernus + +[VOODOO] +Voodoo + +[PONY] +Pony + +[MULE] +Mule + +[CHEETAH] +Cheetah + +[AMBULAN] +Ambulancia + +[FBICAR] +FBI Washington + +[MOONBM] +Moonbeam + +[ESPERAN] +Esperanto + +[TAXI] +Taxi + +[WASHING] +Washington + +[BOBCAT] +Bobcat + +[WHOOPEE] +Mr Whoopee + +[BFINJC] +BF Injection + +[HUNTER] +Hunter + +[POLICAR] +Policía + +[ENFORCR] +Enforcer + +[SECURI] +Securicar + +[BANSHEE] +Banshee + +[PREDATR] +Predator + +[BUS] +Autobús + +[RHINO] +Rhino + +[BARRCKS] +Barracks OL + +[CUBAN] +Cuban Hermes + +[HELI] +Helicóptero + +[DODO] +Dodo + +[COACH] +Autocar + +[CABBIE] +Taxi clásico + +[STALION] +Stallion + +[RUMPO] +Rumpo + +[RCBANDT] +Bandit RC + +[ROMERO] +Romero's Hearse + +[PACKER] +Packer + +[ADMIRAL] +Admiral + +[SQUALO] +Squalo + +[SEASPAR] +Sea Sparrow + +[PIZZABO] +Pizza Boy + +[GANGBUR] +Gang Burrito + +[TROPIC] +Tropic + +[SPEEDER] +Speeder + +[REEFER] +Reefer + +[FLATBED] +Flatbed + +[YANKEE] +Yankee + +[CADDY] +Caddy + +[ZEBRA] +Taxi cebra + +[TOPFUN] +Top Fun + +[SKIMMER] +Skimmer + +[PCJ600] +PCJ 600 + +[PHOENIX] +Phoenix + +[FAGGIO] +Faggio + +[FREEWAY] +Freeway + +[RCBARON] +Barón RC + +[RCRAIDE] +Raider RC + +[GLENDAL] +Glendale + +[OCEANIC] +Oceanic + +[SANCHEZ] +Sanchez + +[SPARROW] +Sparrow + +[LOVEFIS] +Love Fist + +[COASTG] +Guardacostas + +[DINGHY] +Dinghy + +[HERMES] +Hermes + +[SABRE] +Sabre + +[SABRETU] +Sabre Turbo + +[WALTON] +Walton + +[REGINA] +Regina + +[COMET] +Comet + +[DELUXO] +Deluxo + +[BURRITO] +Burrito + +[SPAND] +Spand Express + +[MARQUIS] +Marquis + +[BAGGAGE] +Mozo de equipaje + +[KAUFMAN] +Taxi Kaufman + +[COASTMA] +Guardacostas Maverick + +[MAVERIC] +Maverick + +[RANCHER] +Rancher + +[FBIRANC] +FBI Rancher + +[VIRGO] +Virgo + +[GREENWO] +Greenwood + +[HOTRING] +Hotring Racer + +[BLISTAC] +Blista Compact + +[FEST_DF] +Dist. recorrida a pie (millas) + +[FEST_DC] +Dist. recorrida en coche (millas) + +[FESTDFM] +Distancia recorrida a pie (m) + +[FESTDCM] +Distancia recorrida en coche (m) + +[TOT_DIS] +Distancia total recorrida (millas) + +[TOTDISM] +Distancia total recorrida (m) + +[DISTHEL] +Dist. recorrida en helicóptero (millas) + +[DISTHEM] +Distancia recorrida en helicóptero (m) + +[DISTBOA] +Dist. recorrida en barco (millas) + +[DISTBOM] +Distancia recorrida en barco (m) + +[FEST_LS] +Gente salvada en una ambulancia + +[FEST_CC] +Criminales asesinados en misión Vigilante + +[FEST_FE] +Total de fuegos extinguidos + +[FEST_RP] +Masacres superadas + +[FEST_MP] +Misiones superadas + +[FEST_BB] +Bling-bling Scramble: + +[FEST_H0] +Mayoría de puntos de control + +[FEST_GC] +Total de coches de bandas: + +[FEST_H1] +Destrucción del diablo + +[FEST_H2] +Masacre de la mafia + +[FEST_H3] +Casino Calamity + +[FEST_H4] +Rumpo Wrecker + +[USJ] +¡BONIFICACIÓN POR MANIOBRA ÚNICA! + +[USJ_DUN] +MANIOBRA YA COMPLETADA + +[RATNG1] +Ciudadano honrado + +[RATNG2] +Nadie en especial + +[RATNG3] +Basurero + +[RATNG4] +Cleptómano + +[RATNG5] +Vándalo + +[RATNG6] +Chico de los recados + +[RATNG7] +Carterista + +[RATNG8] +Cleptómano + +[RATNG9] +Chivato + +[RATNG10] +Rata + +[RATNG11] +Lince + +[RATNG12] +Estafador + +[RATNG13] +Timador + +[RATNG14] +Corredor de apuestas + +[RATNG15] +Buscavidas + +[RATNG16] +Matón + +[RATNG17] +Chusma + +[RATNG18] +Bribón + +[RATNG19] +Rufián + +[RATNG20] +Proscrito + +[RATNG21] +Malhechor + +[RATNG22] +Asesino + +[RATNG23] +Super matón + +[RATNG24] +Matón + +[RATNG25] +Presidiario + +[RATNG26] +Exconvicto + +[RATNG27] +Criminal + +[RATNG28] +Saqueador + +[RATNG29] +Mafioso + +[RATNG30] +Conductor + +[RATNG31] +Gorila + +[RATNG32] +Asesino profesional + +[RATNG33] +Asuntos internos + +[RATNG34] +Agente + +[RATNG35] +Ronin + +[RATNG36] +Manitas + +[RATNG37] +Asesino a sueldo + +[RATNG38] +Asociado + +[RATNG39] +Carnicero + +[RATNG40] +Encargado de la limpieza + +[RATNG41] +Asesino + +[RATNG42] +Consigliere + +[RATNG43] +Maleante + +[RATNG44] +Mano derecha + +[RATNG45] +Ejecutor + +[RATNG46] +Teniente + +[RATNG47] +Subjefe + +[RATNG48] +Capo + +[RATNG49] +Jefe + +[RATNG50] +Negado + +[RATNG51] +Don + +[RATNG52] +Rey de la Jodida Ciudad + +[PAGE_00] +. + +[WELCOME] +BIENVENIDO A + +[TSCORE] +GANANCIAS: ~1~ $ + +[PBOAT_2] { reVC update } +Pulsa ~h~~k~~VEHICLE_FIREWEAPON~~w~ para disparar los cañones del barco. + +[HJSTAT] +Distancia: ~1~.~1~m Altura: ~1~.~1~m Vueltas: ~1~ Rotación: ~1~_ + +[HJSTATW] +Distancia: ~1~.~1~m Altura: ~1~.~1~m Vueltas: ~1~ Rotación: ~1~_ ¡qué gran aterrizaje! + +[ATUTOR] +Pulsa ~h~~k~~TOGGLE_SUBMISSIONS~~w~ para activar o desactivar las misiones de ATS. + +[FEST_HA] +Nivel más alto de misión de ATS + +[C_KILLS] +CRIMINALES ASESINADOS: ~1~ + +[HJSTATF] +Distancia: ~1~ pies Altura: ~1~ pies Vueltas: ~1~ Rotación: ~1~_ + +[HJSTAWF] +Distancia: ~1~ pies Altura: ~1~ pies Vueltas: ~1~ Rotación: ~1~_, ¡y qué gran aterrizaje! + +[CRED001] +ROCKSTAR NORTH + +[CRD001A] +DIRECTOR DEL ESTUDIO + +[CRD001B] +ANDREW SEMPLE + +[CRED002] +PRODUCTOR + +[CRD002A] +DIRECTOR DE DESARROLLO + +[CRED003] +LESLIE BENZIES + +[CRED004] +DIRECTOR DE ARTE + +[CRED005] +AARON GARBUT + +[CRED006] +DIRECTORES TÉCNICOS + +[CRED007] +OBBE VERMEIJ + +[CRED008] +ADAM FOWLER + +[CRED010] +ANDREW DUTHIE + +[CRED011] +CRAIG FILSHIE + +[CRED012] +WILLIAM MILLS + +[CRED013] +CHRIS ROTHWELL + +[CRD013A] +IMRAN SARWAR + +[CRD013B] +JAMES WORRALL + +[CRD013C] +JOHN HAIME + +[CRED014] +ESCRITO POR + +[CRED015] +JAMES WORRALL + +[CRED016] +PAUL KUROWSKI + +[CRED017] +DAN HOUSER + +[CRED018] +DISEÑO DEL PERSONAJE PRINCIPAL + +[CRD018A] +DISEÑO DE PERSONAJES + +[CRED019] +IAN MCQUE + +[CRD019A] +TOKS SOLARIN + +[CRD019B] +ALAN DAVIDSON + +[CRED020] +ANIMACIÓN PRINCIPAL + +[CRED021] +ALEX HORTON + +[CRD022A] +ANIMACIÓN + +[CRED022] +LEE MONTGOMERY + +[CRD022B] +DUNCAN SHIELDS + +[CRD022C] +GUS BRAID + +[CRED023] +DISEÑO DE VEHÍCULOS + +[CRD023A] +JOLYON ORME + +[CRD023B] +ALAN DUNCAN + +[CRD024A] +DISEÑO DEL VEHÍCULO PRINCIPAL + +[CRED024] +PAUL KUROWSKI + +[CRED025] +DISEÑO DE MAPAS + +[CRED026] +ADAM COCHRANE + +[CRED027] +NIK TAYLOR + +[CRED028] +GARY MCADAM + +[CRED029] +KEIRAN BAILLIE + +[CRED030] +ALISDAIR WOOD + +[CRED031] +ANDREW SOOSAY + +[CRD031A] +STEVEN MULHOLLAND + +[CRD031B] +WAYLAND STANDING + +[CRD031C] +CAMPBELL J. DICK + +[CRD031D] +DISEÑO GRÁFICO + +[CRD031E] +STUART PETRI + +[CRED032] +PROGRAMADOR JEFE + +[CRD032A] +PROGRAMADORES + +[CRED033] +ALEXANDER ROGER + +[CRED034] +GRAEME WILLIAMSON + +[CRED035] +BARANE CHAN + +[CRED036] +DEREK PAYNE + +[CRED037] +GORDON YEOMAN + +[CRD037A] +ALAN CAMPBELL + +[CRD037B] +MARK HANLON + +[CRD037C] +ANDRZEJ MADAJCZYK + +[CRED038] +PRODUCTOR JEFE MUSICAL + +[CRED039] +CRAIG CONNER + +[CRED040] +STUART ROSS + +[CRED041] +INGENIERO JEFE DE AUDIO + +[CRED042] +ALLAN WALKER + +[CRD041A] +INGENIERO DE AUDIO + +[CRD041B] +AUDIO + +[CRD042A] +WILL MORTON + +[CRED043] +PROGRAMADOR DE AUDIO + +[CRED044] +RAYMOND USHER + +[CRED045] +JEFE DE PRUEBAS + +[CRED046] +CRAIG ARBUTHNOTT + +[CRED047] +CONTROL DE CALIDAD PRINCIPAL + +[CRED048] +NEIL CORBETT + +[CRED049] +KEVIN WONG + +[CRED050] +CONTROL DE CALIDAD + +[CRED051] +DAVID BEDDOES + +[CRED052] +DAVID WATSON + +[CRED053] +BARRY CLARK + +[CRED054] +ROSS SPARROW + +[CRED055] +JAMES ALLAN + +[CRED056] +NEIL MEIKLE + +[CRD056A] +GEORGE WILLIAMSON + +[CRD056B] +MATT JONES + +[CRD056C] +ROB HARBOUR + +[CRD056D] +TOM WHITTAKER + +[CRED057] +SOPORTE TÉCNICO PRINCIPAL + +[CRED058] +LORRAINE ROY + +[CRD057A] +SOPORTE TÉCNICO + +[CRED059] +CHRISTINE CHALMERS + +[CRD060A] +SOPORTE DE OFICINA + +[CRD060B] +KIM GURNEY + +[CRD060C] +CASSIE OLIVER + +[CRED060] +ROCKSTAR NUEVA YORK + +[CRED061] +GABINETE DE APOYO + +[CRED062] +SAM HOUSER + +[CRED063] +PRODUCTOR + +[CRED064] +DAN HOUSER + +[CRED065] +VICEPRESIDENTE DE DESARROLLO + +[CRED066] +JAMIE KING + +[CRED067] +OFICIAL JEFE DE TECNOLOGÍA + +[CRED068] +GARY J. FOREMAN + +[CRED069] +PRODUCTOR ASOCIADO + +[CRED070] +JEREMY POPE + +[CRD071A] +DIRECTOR DE CONTROL DE CALIDAD + +[CRD072A] +JEFF ROSA + +[CRED071] +SUPERVISOR MUSICAL + +[CRED072] +TERRY DONOVAN + +[CRED073] +EQUIPO DE PRODUCCIÓN + +[CRED074] +TERRY DONOVAN + +[CRED075] +JENNIFER KOLBE + +[CRED076] +JENEFER GROSS + +[CRED077] +LAURA PATERSON + +[CRED078] +JEFF CASTANEDA + +[CRED079] +JERONIMO BARRERA + +[CRED080] +CARLY SLATER + +[CRED081] +JUNG KWAK + +[CRED082] +BRIAN WOOD + +[CRED083] +RENAUD SEBANNE + +[CRED084] +RICHARD KRUGER + +[CRD084A] +DANIEL EINZIG + +[CRD084B] +JACEN BURROWS + +[CRD084C] +LINN PR + +[CRD084D] +DISEÑO DE PORTADA + +[CRD084E] +STEPHEN BLISS + +[CRED085] +KENT PAUL'S 80 NOSTALGIA ZONE + +[CRED086] +ESCRITO POR DAN HOUSER + +[CRD086A] +PRODUCIDO POR ADAM TEDMAN + +[CRED087] +WWW.KENTPAUL.COM AND WWW.VICECITY.COM + +[CRED088] +CREADO POR + +[CRD088A] +ADAM TEDMAN + +[CRD088B] +DAVID YU + +[CRD088C] +JERRY LUNA + +[CRD088D] +STUART PETRI + +[CRD088E] +MICHAEL CARNEVALE + +[CRD088F] +GREG LAU + +[CRD088G] +FUTABA HAYASHI + +[CRED089] +JEFE DE CONTROL DE CALIDAD + +[CRED090] +CRAIG ARBUTHNOTT + +[CRED091] +ANALISTA JEFE + +[CRED092] +ADAM DAVIDSON + +[CRD092A] +JOE HOWELL + +[CRD092B] +MARC FERNANDEZ + +[CRED093] +ANALISTA DEL JUEGO + +[CRED094] +RICHARD HUIE + +[CRED095] +EQUIPO DE PRUEBAS DE ROCKSTAR + +[CRED096] +LANCE WILLIAMS + +[CRED097] +JOE GREENE + +[CRED098] +BRIAN PLANER + +[CRD098A] +ELIZABETH SATTERWHITE + +[CRD098B] +JAMEEL VEGA + +[CRD098C] +MIKE HONG + +[CRED099] +LEE CUMMINGS + +[CRED100] +HISTORIA + +[CRED101] +JAMES WORRALL + +[CRED102] +DAN HOUSER + +[CRED103] +ADAM TEDMAN + +[CRED104] +PAUL YEATES + +[CRED105] +JENEFER GROSS + +[CRED106] +LAURA PATERSON + +[CRED107] +SECUENCIAS + +[CRED108] +ESCRITAS POR DAN HOUSER Y JAMES WORRALL + +[CRED109] +DIRECCIÓN DE AUDIO DE DAN HOUSER Y NAVID KHONSARI + +[CRED110] +PRODUCIDAS POR JAMIE KING + +[CRD110A] +SELECCIÓN DE ACTORES: JAMIE KING, SEAN MACALUSO + +[CRED111] +REPARTO + +[CRD111A] +PERSONAJES DE REPARTO + +[CRED112] +TOMMY VERCETTI - RAY LIOTTA + +[CRED113] +KEN ROSENBERG - WILLIAN FICHTNER + +[CRED114] +SONNY FORELLI - TOM SIZEMORE + +[CRED115] +STEVE SCOTT - DENNIS HOPPER + +[CRED116] +AVERY CARRINGTON - BURT REYNOLDS + +[CRED117] +RICARDO DÍAZ - LUIS GUZMÁN + +[CRED118] +LANCE VANCE - PHILIP MICHAEL THOMAS + +[CRED119] +CORONEL JUAN CORTEZ - ROBERT DAVI + +[CRED120] +UMBERTO ROBINA - DANNY TREJO + +[CRED121] +PHIL CASSIDY - GARY BUSEY + +[CRED122] +MITCH BAKER - LEE MAJORS + +[CRED123] +MERCEDES CORTEZ - FAIRUZA BALK + +[CRED124] +KENT PAUL - DANNY DYER + +[CRED125] +JEZZ TORRENT - KEVIN MCKIDD + +[CRED126] +SUPERVISOR TAXIS - DEBORAH HARRY + +[CRED127] +CANDY SUXX - JENNA JAMESON + +[CRED128] +BJ SMITH - LAWRENCE TAYLOR + +[CRED129] +TÍA POULET - YOUREE CLEOMILI HARRIS + +[CRED130] +PROVEEDOR - ARMANDO RIESCO + +[CRED131] +COUGAR - BLAYNE PERRY + +[CRED132] +HILARY - CHARLES TUCKER + +[CRED133] +CONGRESISTA ALEX SHRUB - CHRIS LUCAS + +[CRED134] +VIEJO KELLY - GEORGE DICENZO + +[CRD134A] +CAM JONES - GREG SIMS + +[CRD134B] +PSICÓPATA - HUNTER PLATIN + +[CRD134C] +MAUDE LA VENDEDORA DE HELADOS - JANE GENNARO + +[CRD134D] +JETHRO - JOHN ZURHELLEN + +[CRD134E] +GONZÁLEZ - JORGE PUPO + +[CRD134F] +DWAYNE - NAVID KHONSARI + +[CRD134G] +DICK - PETER MCKAY + +[CRD134H] +MIKE EL MATÓN & CHICO PORNO - ROBERT CIHRA + +[CRD134I] +PERCY - RUSSELL FOREMAN + +[CRED135] +CAPTURA DE MOVIMIENTOS + +[CRED136] +ANIMACIÓN DE + +[CRD136A] +DIRECCIÓN TÉCNICA DE ALEX HORTON + +[CRED137] +DIRIGIDO POR + +[CRD137A] +DIRIGIDO POR NAVID KHONSARI + +[CRED138] +PRODUCIDO POR + +[CRD138A] +PRODUCIDO POR JAMIE KING + +[CRD138B] +RENAUD SEBBANE + +[CRED139] +GRABADO EN PERSPECTIVE STUDIOS, BROOKLYN + +[CRED140] +ACTORES DE CAPTURA DE MOVIMIENTOS + +[CRD140A] +BLAYNE PERRY + +[CRD140B] +JONATHON SALE + +[CRD140C] +CHARLES TUCKER + +[CRD140D] +EDDIE MARRERO + +[CRD140E] +WILLIAM MCCALL + +[CRD140F] +JORGE PUPO + +[CRD140G] +ROBERT JACKSON + +[CRD140H] +TARA RADCLIFFE + +[CRD140I] +JENIFER GAMBETESE + +[CRD140J] +KRIS ACHEVARRIA + +[CRD140K] +ALI ORDOUBADI + +[CRD140L] +KAHLEEM POOLE + +[CRED141] +DIÁLOGOS DE LOS PEATONES + +[CRD141A] +ESCRITOS POR DAN HOUSER, MARC FERNÁNDEZ, GILLIAN TELLING Y NAVID KHONSARI + +[CRD141B] +CON LA AYUDA DE JEREMY POPE, LANCE WILLIAMS, Y JENNY JEMISON + +[CRED142] +ESCRITO POR + +[CRD142A] +DAN HOUSER Y JAMES WORRALL + +[CRED143] +DIRIGIDO POR DAN HOUSER, CRAIG CONNER, MARC FERNÁNDEZ, Y ALLAN WALKER + +[CRED144] +PRODUCIDO POR RENAUD SEBANNE + +[CRED145] +PEATONES + +[CRED146] +ADAM DAVIDSON, ADAM WATKINS, ALEJANDRO K. BROWN, ALEX ANTHONY SIOUKAS, ALEX GARCÍA, + +[CRED147] +ALICE SALTZMAN, ALISON CIHRA, AMY SALIMA, AMY SALZMAN, ANDREA VIDELA, ANTHONY ATTI, + +[CRED148] +ANTHONY RIVERA, BIJAN SHAMS, BLAYNE PERRY, BRETT BISOGNO, BREYE MATA, BRIAN PANEN, + +[CRED149] +BROCK VODER, CAREY BERTINI, CHARISSE LAMBERT, CHRIS DIFAT, CHRIS REISENBERGER, + +[CRED150] +CHRISTOPHER BRODAY, CHRISTOPHER CARRO, CYNTHIA GREENE, DAMARIES LÓPEZ, DAN LEE, + +[CRED151] +DAN SCHNEIDER, DAN TOYAMA, DAVID DEAN CHALTFIELD, JR., DAVID HARRISON, DAVID WILEY, + +[CRED152] +DEBORAH COLLINS, DEBRANDA CHANEY-GILES, DEMETRA KOUKOULAS, DENISE ROSADO, + +[CRED153] +DEVIN BENNETT, DEVIN WINTERBOTTOM, DORIS WOO, DOUGLAS HARRISON, DUNCAN COUTTS, + +[CRED154] +DUPE AJAYI, EDWIN AVELLANEDA, ELIZABETH HOWELL, ELIZABETH SATTERWHITE, ERIC NAGLE, + +[CRED155] +ESTEBAN KARPLUS, F. FONT, FUTABA HAYASHI, GENE HILGREEN, GERALD COSGROVE, + +[CRED156] +GERARD LUNA, GILLIAN TELLING, GREGG CARLUCCI, GREGORY CLERVOIX, GREGORY SCHWEIZER, + +[CRED157] +HADLEY TOMICKI, J. ROSSETT, JAMEEL VEGA, JASON JONES, JEFF ROSA, JENNIFER JEMISON, + +[CRED158] +JEREMY TAGGERT, JESSICA RIDER, JOSEPH GREENE,JOSEPH HOWELL, KATE DUKICH, + +[CRED159] +KEL O'NEILL, KEVIN HOPKINS, LADAWN JAMES, LANCE WILLIAMS, LAURA BUBBLES, + +[CRED160] +LAURA PATTERSON, LEE CUMMINGS, LETICIA L. YOUNG, LINDSAY KENNEDY, LISA ORITZ, + +[CRED161] +LORNA JORDAN, LUCIO AMADIO, MARCO FERNÁNDEZ, MARIKO TANAKA, MARLON MATTHEWS, + +[CRED162] +MARY TELLING, MASAYOSHI MITSUYAMA, MATTHEW CHUNG, MAX ALLSTADT, MAX BOGDANOV, + +[CRED163] +MELISSA ÁLVAREZ, MICHAEL MAY, MICHAEL ROTHSTEIN, MIGUEL VIDAL, MIKE FEDERLINE, + +[CRED164] +NATALIE DESCALZO, N'GAI MEMBERS, NICOLAS MALLO, NOELLE SADLER, NORBERT MORIVAN, + +[CRED165] +OSWALD GREENE, JR., PETER MCKAY, PETER APPEL, PRESTON SAVARESE, RAFAEL GONZÁLEZ, + +[CRED166] +RANDY JOHNSON, REY CONCEPCIÓN, RICHARD KROGER, ROB TIBBS, ROBERT JACKSON, + +[CRED167] +ROBERT SCHULER, ROSS A. MCINTYRE, RUSSELL FOREMAN, RUTH NÚÑEZ, SALVADORE SUAZO, + +[CRED168] +SAM WHITE, SANTOS GONZÁLEZ, SCOTT SMITH, SEYMOUR FRAILMAN, SPELMAN BRAUMAN, + +[CRED169] +STEPHANIE TELLING, STEVE KNEZEVICH, STEVE ROBERT, SUMIKO YASUDA, SUSAN LEWIS, + +[CRED170] +SYLVIA COLACIOS, TOMOKO MIYAZAKI, TRON, VERDEL HALE, YVES MONDESIR, ZENO LEINFELDER, + +[CRED171] +DAVID BEDDOES, CHRISTINE CHALMERS, BARRY CLARK, NEIL CORBETT, KIM GURNEY, NEIL MEIKLE, + +[CRED172] +CASSIE OLIVER, LORRAINE ROY, DAVID WATSON, KEVIN WONG, WILL MORTON + +[CRED175] +ADAM DAVIDSON + +[CRED176] +LANCE WILLIAMS + +[CRED177] +NEIL MCCAFFREY + +[CRED178] +LAURA PATERSON + +[CRED179] +REY CONCEPCIÓN + +[CRED180] +CHARLES HEROLD + +[CRED181] +ANDREW GREENWALD + +[CRED182] +JAMES MIELKE + +[CRED183] +PETER SUCIU + +[CRED184] +ALEX ODULIO + +[CRED185] +DON NKRUMAH + +[CRED186] +KENDALL PITTMAN + +[CRED187] +SAL SUAZO + +[CRED188] +EREK MATEO + +[CRED189] +CHRIS DIFATE + +[CRED190] +LEILA MILTON + +[CRED191] +DARREN ZOLTOWSKI + +[CRED192] +VIRGINIA SMITH + +[CRED193] +KEVIN CASSIN + +[CRED194] +JASON SHIGEMORI + +[CRED195] +KELLY KINSELLA + +[CRED196] +MOLLIE STICKNEY + +[CRED197] +STANTON SARJEANT + +[CRED198] +LAURA WALSH + +[CRED199] +MARK GARONE + +[CRED200] +JOANNA SLY + +[CRED201] +ELIZABETH HOWELL + +[CRED202] +ANA HÉRCULES + +[CRED203] +SHIRLEY IRICK + +[CRED204] +KASHONA FIELDS + +[CRED205] +JOEL M. LILJE + +[CRED206] +JOHN DIBENEDETTO + +[CRED207] +NANCY GILES + +[CRED208] +RYAN CROY + +[CRED209] +JENNIFER KOLBE + +[CRED210] +LIAM BURKE + +[CRED211] +SIGRID PREISSL + +[CRED212] +ANITA FITZSIMONS + +[CRED213] +PHILIPPA RASELLI + +[CRED214] +WIL QUESNEL + +[CRED215] +FALKO BURKERT + +[CRED216] +SARA SEWELL + +[CRED217] +EMISORAS DE RADIO Y MÚSICA + +[CRED218] +ASESOR MUSICAL + +[CRD218A] +HEINZ HENN + +[CRD218B] +STUART ROSS + +[CRED219] +COORDINADOR DE LA BANDA SONORA + +[CRED220] +TERRY DONOVAN + +[CRED221] +PRODUCTOR DE ROCKSTAR GAMES + +[CRED222] +DAN HOUSER Y LAZLOW + +[CRED223] +PRODUCTOR DE ROCKSTAR NORTH + +[CRED224] +CRAIG CONNER + +[CRED225] +ALLAN WALKER + +[CRED226] +LAZLOW + +[CRED227] +DJ BANTER E IMÁGENES + +[CRED228] +ESCRITO POR DAN HOUSER Y LAZLOW + +[CRED229] +FLASH FM + +[CRD229A] +TONI-MARIA CHAMBERS + +[CRD229B] +VOCES FICTICIAS Y PRODUCCIÓN-JEFF BERLIN + +[CRED230] +GRACIAS ESPECIALES A + +[CRED231] +TOMMY MOTTOLA, + +[CRED232] +MICHELLE ANTHONY, + +[CRED233] +STEVE BARNETT, + +[CRED234] +CHUCK FLECKENSTEIN, + +[CRED235] +RITA LIBERATOR + +[CRED236] +MARTIN & CLAIRE LOGAN + +[CRED237] +SANDRA HUTTON + +[CRED238] +CHRISTINE DAVIDSON + +[CRED239] +ALAN, RED & BIGFOOT + +[CRED240] +LE T + +[CRED241] +COLIN DONALD + +[CRED242] +KERRY STALLWOOD + +[CRED243] +ALAN MCGREGOR + +[CRED244] +CHRIS MORTON + +[CRED245] +EMIL BUSSE + +[CRED246] +EMILY BAILLIE + +[CRED247] +KEVIN ARCHIBALD + +[CRED248] +MORAG KERR + +[CRED249] +CATH WALKER + +[CRED250] +ISO BAR + +[CRED251] +WATERLINE + +[CRED252] +NEWS CAFE + +[CRD251A] +THE POND + +[CRD252A] +PIVO + +[CRED253] +BUDGET VIDEO RENTALS + +[CRED254] +LORNA'S SCOOTER + +[CRED255] +GARETH MURFIN + +[CRED256] +ARTE ADICIONAL + +[CRED257] +TONY PORTER + +[CRED258] +CRAIG MOORE + +[CRED259] +ANIMACIÓN DE SINCRONIZACIÓN LABIAL DE LAS SECUENCIAS + +[CRED260] +COSGROVE HALL FILMS + +[CRED261] +PRODUCTOR - OWEN BALLHATCHET + +[CRED262] +ANIMADOR SENIOR - JON TURNER + +[CRED263] +ANIMADORES - RICHARD DRUMM + +[CRED264] +DAVE BROWN + +[CRED265] +MAIR THOMAS + +[CRED266] +PRASHANT PATEL + +[CRED267] +ASESOR TECNOLOGÍA DE AUDIO + +[CRED268] +RIK EDE PARA GAMESOUND LTD. + +[CRED269] +SOPORTE DTS INTEGRATION + +[CRED270] +TED LAVERTY DE DTS + +[CRED271] +CHRIS GREER DE DTS + +[CRED272] +JASON PAGE DE SCEE + +[CRED273] +INVESTIGACIÓN Y ANÁLISIS + +[CRED274] +VROCK + +[CRED275] +DJ: LAZLOW COMO ÉL MISMO + +[CRED276] +VOZ-JOE KELLY + +[CRED277] +PRODUCCIÓN-JONATHAN HANST + +[CRED278] +WAVE 103 + +[CRED279] +DJ: ADAM FIRST-JAMIE CANFIELD + +[CRED280] +VOZ-JEN SWEENEY + +[CRED281] +PRODUCCIÓN-JONATHAN HANST + +[CRED282] +FEVER 105 + +[CRED283] +DJ: OLIVER 'LADYKILLER' BISCUIT-JULIUS DYSON + +[CRED284] +VOZ MASCULINA-ED MCMANN + +[CRED285] +VOZ FEMENINA-SHAWNEE SMITH + +[CRED286] +PRODUCCIÓN- LISTEN KITCHEN + +[CRED287] +EMOTION 98.3 + +[CRED288] +DJ: FERNANDO- FRANK CHAVEZ + +[CRED289] +VOZ-JEN SWEENEY + +[CRED290] +PRODUCCIÓN-JONATHAN HANST + +[CRED291] +RADIO ESPANTOSO + +[CRED292] +DJ: PEPE-TONY CHILRODES + +[CRED293] +WILDSTYLE + +[CRED294] +DJ: MISTER MAGIC AS HIMSELF + +[CRED295] +VOZ-FRANK SILVESTRO + +[CRED296] +PRODUCCIÓN-LAZLOW + +[CRED297] +KCHAT + +[CRED298] +ESCRITO POR DAN HOUSER Y LAZLOW + +[CRED299] +PRODUCIDO Y EDITADO POR LAZLOW + +[CRED300] +DJ AMY SHECKENHAUSEN -LEYNA WEBER + +[CRED301] +JEZ TORRENT-KEVIN MCKIDD + +[CRED302] +MANDY -COLLEEN CORBETT + +[CRED303] +MICHELLE CARAPADIS-MARY BIRDSONG + +[CRED304] +MR.ZOO-CARL DOWLING + +[CRED305] +GETHSEMANEE-LYNN LIPTON + +[CRED306] +CLAUDE MAGINOT-JOHN MAUCERI + +[CRED307] +BJ SMITH-LAWRENCE TAYLOR + +[CRED308] +THOR-FRANK FAVA + +[CRED309] +PERSONAS LLAMANDO A LA RADIO + +[CRED310] +COUZIN ED, JOSH CLARK, JASON BUHRMESTER, JUAN ALLER, WAYNE OLIVER, SUSAN LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, KEITH BROADAS + +[CRED311] +LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, + +[CRED312] +DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, + +[CRED313] +KEITH BROADAS + +[CRED314] +VCPR + +[CRED315] +ESCRITO POR DAN HOUSER Y LAZLOW + +[CRED316] +PRODUCIDO POR LAZLOW + +[CRED317] +MAURICE CHAVEZ-PHILLIP ANTHONY RODRIGUEZ + +[CRED318] +JONATHAN FREELOADER- PATRICK OLSEN + +[CRED319] +MICHELLE MONTANIUS-KELLY GUEST + +[CRED320] +REP. ALEX SHRUB- CHRIS LUCAS + +[CRED321] +CALLUM CRAYSHAW- SEAN MODICA + +[CRED322] +JOHN F. HICKORY- LJ GANSEN + +[CRED323] +PASTOR RICHARDS- DAVID GREEN + +[CRED324] +JAN BROWN- MAUREEN SILLIMAN + +[CRED325] +BARRY STARK- RENAUD SEBBANE + +[CRED326] +JENNY LOUISE CRAB- MARY BIRDSONG + +[CRED327] +KONSTANTINOS SMITH- KONSTANTINOS.COM + +[CRED328] +JEREMY ROBARD-PETER SILVESTRO + +[CRED329] +ANUNCIOS DE RADIO + +[CRED330] +ESCRITOS POR DAN HOUSER Y LAZLOW + +[CRED331] +PRODUCIDOS POR LAZLOW + +[CRED332] +JINGLES ADICIONALES PRODUCIDOS POR CRAIG CONNER + +[CRED333] +VOCES EN LOS ANUNCIOS: + +[CRED334] +ADAM DAVIDSON, ALEX ANTHONY, ALICE SALTZMAN, AMY SALZMAN, KATE DUKICH, + +[CRED335] +ARAN RONICLE, BARB JONES, BEN KRECH, BRIAN THOMAS, BROCK YODER, CHRIS + +[CRED336] +FERRANTE, CRAIG CONNER, DAVE RYAN, DAVID GREEN, DORIS WOO, DOUGLAS + +[CRED337] +HARRISON, ED MCMANN, FRANK CHÁVEZ, FRANK FAVA, GENE HILGREEN, GREG + +[CRED338] +SCHWEIZER, HUNTER PLATIN, JAMES FERRANTE, JEFF BERLIN, JEFF ROSA, JOE KELLY, + +[CRED339] +JOHN MAUCERI, JOSH CLARK, JULIE WEMYSS, KEVIN STRALEY, KIM GURNEY, LANCE + +[CRED340] +WILLIAMS, LAURA PATERSON, LAZLOW, LISA ORTIZ, LORNA JORDAN, LUCIEN JONES, + +[CRED341] +MAUREEN SILLIMAN, MIKE FERRANTE JR., PETE GUSTIN, PETER SILVESTRO, RAFF + +[CRED342] +CROLLA, RANDY JOHNSON, RICHARD KRUGER, RON REEVE, SHELLEY MILLER, SKY, TJ ALLARD + +[CRD344A] +AUDIO GRABADO EN DIGITAL ARTS STUDIOS, + +[CRED344] +NYC, TRACK 9 STUDIOS, NYC, + +[CRED345] +WEDDINGTON MULTIMEDIA, LOS ANGELES, + +[CRD345A] +SYNC SOUND, NYC Y RADIO LAZLOW, LONG ISLAND. + +[CRED346] +GRACIAS A AXEL ERICSON Y WON LEE EN DIGITAL ARTS, PAUL VÁSQUEZ EN TRACK 9 STUDIOS, JOHN BOWEN Y JOHN HASSLER EN SYNC SOUND + +[CRED347] +MARK LLOYD + +[CRED348] +TIM BATES + +[CRED349] +KIT BROWN + +[CRED350] +ANDY MASON + +[CRED351] +PHIL DEANE + +[CRED352] +PHIL ALEXANDER + +[CRED353] +MATT HEWITT + +[CRED354] +DENBY GRACE + +[CRED355] +ANTOINE CABROL + +[CRED356] +JONOTHAN STONES + +[CRED357] +MIKE BLACKBURN + +[CRED358] +TIM MCGAFF + +[CINCAM] +Cámara cinemática + +[RC4] +MASACRES RUMPO + +[LEGAL] +~g~¡Elimina la amenaza criminal! + +[GA_2] +Nuevo motor y trabajo de pintura. ¡Los policías no te reconocerán! + +[HELP15] +Cuando vayas a pie, pulsa ~h~~k~~PED_LOOKBEHIND~~w~ para ~h~mirar detrás~w~. Utiliza el ~h~joystick analógico derecho~w~ para ~h~mirar a tu alrededor~w~. + +[FEC_LB4] +Mirar detrás (botón R3) + +[PERPIC] +Objetos ocultos encontrados + +[CO_ONE] +Objeto oculto ~1~ de ~1~ + +[GA_21] +No puedes almacenar más coches en este garaje. + +[CHEAT1] +Truco activado + +[CHEAT2] +Truco de arma + +[CHEAT3] +Truco de salud + +[CHEAT4] +Truco de blindaje + +[CHEAT5] +Truco de nivel de se busca + +[CHEAT6] +Truco de dinero + +[CHEAT7] +Truco de tiempo + +[USJ_ALL] +¡TODAS LAS MANIOBRAS ÚNICAS COMPLETADAS! + +[JAN] +Ene + +[FEB] +Feb + +[MAR] +Mar + +[APR] +Abr + +[MAY] +May + +[JUN] +Jun + +[JUL] +Jul + +[AUG] +Ago + +[SEP] +Sep + +[OCT] +Oct + +[NOV] +Nov + +[DEC] +Dic + +[DEFDT] +--:---:---- --:--:-- + +[BONUS] +~g~BONIFICACIÓN ~1~ $ + +[HORN1] +Pulsa ~h~~k~~VEHICLE_HORN~~w~para tocar el ~h~claxon. + +[HORN2] +Pulsa ~h~~k~~VEHICLE_HORN~~w~para tocar el ~h~claxon. + +[HORN3] +Pulsa ~h~~k~~VEHICLE_HORN~~w~para tocar el ~h~claxon. + +[FEC_EXV] +Entrar y salir de vehículo + +[TAXI_M] +'TAXISTA' + +[COP_M] +'JUSTICIERO' + +[FIRE_M] +'BOMBERO' + +[AMBUL_M] +'ATS' + +[HJ_IS] +BONIFICACIÓN POR ACROBACIA: ~1~ $ + +[HJ_PIS] +BONIFICACIÓN POR ACROBACIA PERFECTA: ~1~ $ + +[HJ_DIS] +BONIFICACIÓN POR ACROBACIA DOBLE: ~1~ $ + +[HJ_PDIS] +BONIFICACIÓN POR ACROBACIA DOBLE PERFECTA: ~1~ $ + +[HJ_TIS] +BONIFICACIÓN POR ACROBACIA TRIPLE: ~1~ $ + +[HJ_PTIS] +BONIFICACIÓN POR ACROBACIA TRIPLE PERFECTA: ~1~ $ + +[HJ_QIS] +BONIFICACIÓN POR ACROBACIA CUÁDRUPLE: ~1~ $ + +[HJ_PQIS] +BONIFICACIÓN POR ACROBACIA CUÁDRUPLE PERFECTA: ~1~ $ + +[FESZ_LS] +Carga completada con éxito. + +[HELI_1A] +Prueba tus habilidades con el Sparrow, comprueba lo rápido que puedes completar el circuito. + +[HELI_1B] +¡Circuito completado! ~1~ $ + +[HELIODD] +Trabajos esporádicos del helicóptero + +[LAW] +LAS MISIONES DEL ABOGADO + +[LAW1_1] +~g~Consigue unos trapos nuevos en la tienda de ropa de Rafael. + +[LAW4_6] +¡Quemad la dirección! + +[LAW4_7] +¡Matad a los jefes! + +[LAW4_8] +Luchad, luchad, luchad, luchad. + +[LAW4_9] +¡Más vacaciones, menos trabajo! + +[LAW4_11] +¡Luchad! ¡Luchad! ¡Luchad! ¡Luchad! + +[LAW4_12] +¡Viva la revolución! + +[GENERAL] +LAS MISIONES DEL CORONEL + +[GEN3_4] +Tommy Vercetti. Vamos... + +[GEN3_13] +¿Cuál es tu problema, tío? ¡Sube al tejado al otro lado del patio antes de que aparezcan! + +[GEN3_17] +¡Mierda! ¿Intentas matarme? + +[GEN3_21] +~g~¡Tiene el dinero de Díaz! ¡Ve tras él y recupéralo! + +[GEN3_24] +~r~¡Díaz murió! ¡Has fracasado al protegerle! + +[GEN3_26] +~r~¡Le pegaste un tiro a Díaz! + +[GEN3_27] +~r~¡Disparaste a los guardaespaldas de Díaz! + +[GEN3_31] +~g~Ahora ve al punto de entrega y cuida de Díaz. + +[GEN3_32] +~g~Ve a tu posición elevada en el tejado del edificio opuesto a donde está Lance. + +[COKE] +LAS MISIONES DEL BARÓN DE LA NIEVE + +[COK1_3] +¡Espero que te caigas y te rompas el cuello! + +[COK1_6] +Estoy cansado de estos mamones. + +[COK2_7] +¿Veis esos marcadores chicos? ¡Probad a dar a las luces! + +[COK2_10] +Desde luego eres mejor disparando que charlando. + +[COK2_11] +Gracias. Eres un verdadero encanto. + +[COK2_12] +Lo sé, Tommy. + +[COK2_18] +¿Te gusta Kenny Loggins? + +[COK2_19] +Dios, ¡me encanta este disco! + +[COK2_26] +~r~¡Mataste a Lance! + +[COK3_1] +¡No dispares tío! + +[COK3_2] +¿Qué hay ahí? + +[COK3_3] +Se está llevando el barco. Gorrón. + +[COK3_4] +¡Ayuda! ¡Un tipo me está robando el barco, tío! + +[COK4_W] +¡Uugghh! Ése era el último. + +[COK4_X] +Voy a arrancarlo + +[COK4_Y] +Creo que tenemos algunos amigos nuevos. + +[COK4_2] +Sí. + +[COK4_6] +¿A dónde crees que vamos? + +[COK4_7] +¿Nos hemos perdido? + +[COK4_8] +¡Tenemos competencia! + +[COK4_9] +¡Elimínales! + +[COK4_9A] +¡Es hora del baile de Lance Vance! + +[COK4_10] +¡Ya son astillas! Y comida para los peces. + +[COK4_11] +¡Lo conseguimos! Esos otros barcos no son de la clase VIP. + +[COK4_17] +¡Se están desesperando! + +[COK4_18] +¡Mis malditos pies están mojados! ¡NOS ESTÁ ENTRANDO AGUA! + +[COK4_21] +¡Subiendo el puente! + +[COK4_22] +¡Salta, está apunto de estallar! + +[COK4_23] +Buen disparo. + +[COK4_29] +~r~¡Mataste a Lance! + +[ASS1_6] +¡Adelante Tommy, yo estaré bien! + +[ASS1_7] +¡Comeos esto, asesinos hijos de puta! + +[ASS1_8] +¡Me han dado! + +[ASS1_9] +¡Te tengo cubierto, Tommy! + +[ASS1_10] +Eh, este seto es precioso. + +[ASS1_11] +Tommy, ¿puedo tener una habitación con vistas a la bahía? + +[ASS1_12] +Unos techos altos muy bonitos... + +[ASS1_3] +¡Lance! ¡Necesito que me cubras! + +[ASS1_4] +¡Díaz debe de estar dentro! + +[ASS1_5] +¡Lance! + +[TAXWAR] +MISIONES DE LA GUERRA DE TAXIS + +[NOTAXI] +~g~Necesitas un Taxi Kaufman para activar esta misión. + +[TAXW1_5] +~g~¡Necesitas estar en un taxi Kaufman! + +[TAX2_4] +Adelante, Tommy. + +[TAX2_5] +Dale una buena paliza. + +[TAX2_6] +Ni siquiera tiene licencia. + +[TAX2_7] +Malditos servicios de limusina. + +[TAXW3_1] +~g~Ve y recoge a Mercedes. + +[RACE1] +~g~3..2..1.. ¡VAMOS VAMOS VAMOS! + +[RACE2] +~g~3 + +[RACE3] +~g~2 + +[RACE4] +~g~1 + +[RACE5] +~g~¡ADELANTE! + +[FIRST] +~b~1 + +[SECOND] +~b~2 + +[THIRD] +~b~3 + +[FOURTH] +~b~4 + +[RACETM] +~b~TIEMPO DE CARRERA: ~1~:~1~ + +[RACETM2] +~b~TIEMPO DE CARRERA: ~1~:0~1~ + +[RACEFA] +~r~¡Has fracasado en ganar la carrera! + +[HOTRNG] +HOTRING + +[BLODRNG] +BLOODRING + +[DIRTRNG] +DIRTRING + +[TEX1_5] +~r~¡Se escapó! + +[SEG3_1] +TIEMPO: + +[SEG3_2] +~g~Ve a la camioneta que contiene el Helicótero RC y las bombas por control remoto con temporizador. + +[SEG3_3] +~g~Debes emplear el Helicótero RC para transportar 4 bombas hasta 4 objetivos en lugar del edificio. + +[SERG3_5] +~g~Sólo puedes llevar una bomba en cada ocasión y no puedes recoger bombas colocadas con éxito. + +[SEG3_7] +~g~Una vez que hayas soltado con éxito la PRIMERA bomba sobre una zona del objetivo, el cronómetro de detonación se pondrá en marcha; de modo que tendrás que soltar todas las bombas dentro de este período de tiempo. + +[SEG3_8] +~g~Las 4 bombas deben estar colocadas en las 4 zonas objetivo para superar la misión y demoler el edificio. + +[SEG3_9] +~g~¡Alcanzaste el objetivo! Quedan 3. + +[SEG3_10] +~g~¡Alcanzaste el objetivo! Quedan 2. + +[SEG3_11] +~g~¡Alcanzaste el objetivo! ¡Sólo queda 1! + +[SEG3_12] +~r~¡Fallaste el blanco! ¡Ve a por una bomba! + +[SEG3_13] +~g~Deja caer la bomba en una zona del objetivo. + +[SEG3_14] +~r~Te quedaste sin tiempo y fracasaste en demoler el edificio. + +[SEG3_15] +~r~¡Tu Helicótero RC ha sido destruido! ¿Cómo vas a transportar las bombas ahora? + +[AVERY] +MISIONES DE AVERY + +[ASM] +MISIONES DE ASESINO + +[ASM_1] +MISIÓN DE ASESINO 1 + +[ASM1_1] +~g~Sr. Teal, su ayuda en la eliminación de los forasteros fue inestimablemente buena para el negocio. Tengo más trabajo para usted y más próximo a la ''intervención''. Encontrará su siguiente trabajo pegado con cinta adhesiva bajo el teléfono. + +[ASM1_2] +~g~Ve a la cabina telefónica que hay frente al centro comercial de Washington. + +[ASM1_3] +~g~Carl Pearson, un repartidor de pizza. No debe finalizar sus entregas. + +[ASM1_4] +~g~Elimina al repartidor de pizza antes de que complete sus entregas. + +[ASM_2] +MISIÓN DE ASESINO 2 + +[ASM_3] +MISIÓN DE ASESINO 3 + +[ASM3_1] +~g~Ve a coger el arma que el Sr. Black ha dejado para ti. + +[ASM3_2] +~g~No te acerques demasiado al objetivo o podría verte. + +[ASM3_3] +~g~Para un trabajo más rápido, colócate en lugares cercanos a su ubicación y evita ser visto. Esto te proporcionará una buena posición para eliminarles. + +[ASM3_4] +~g~¡Te ha visto! ¡Mejor elimínale de cualquier modo que puedas! + +[ASM3_5] +~g~Marcus Hammond está situado cerca de los anuncios de Washington. + +[ASM3_6] +~g~Franco Carter está situado en el DBP de Seguridad, cerca de Ocean Drive. + +[ASM3_7] +~g~Dick Tanner está cerca de la joyería de Vice Point. + +[ASM3_8] +~g~Nick Kong está colocado cerca de Washington Beach. + +[ASM3_9] +~g~El conductor está situado en Washington. + +[ASM3_10] +~r~Fallaste en matarles a todos. + +[ASM_4] +MISIÓN DE ASESINO 4 + +[ASM4_1] +~g~Ve a por el rifle que te han dejado entre el follaje que hay en las cercanías de la terminal del aeropuerto. + +[ASM4_2] +~g~No falles tu blanco o podrías alertar a sus guardaespaldas, y recuerda mantener las distancias para que no te detecte. + +[ASM4_3] +~g~Vigila a la mujer de la galería que hay sobre los mostradores de facturación, dentro de la terminal del aeropuerto. PERO NO LA MATES. + +[ASM4_4] +~g~Mata al hombre al que ella dé el maletín, pero sólo DESPUÉS DE QUE ÉSTE LO RECOJA. Entonces recupera el maletín y llévalo a Ammu-Nation en el centro de la ciudad. + +[ASM4_5] +~g~¡Consigue el maletín! + +[ASM4_6] +~g~Lleva al maletín a Ammu-Nation en el centro de la ciudad. + +[ASM4_7] +~r~¡Estúpido, has matado a la mujer! + +[ASM4_8] +~r~¡El blanco te oyó disparar el arma! ¡El trato queda anulado! + +[ASM4_9] +~r~¡El blanco ha embarcado en su vuelo! + +[ASM4_11] +~r~¡El blanco te ha visto! ¡El trato queda anulado! + +[ASM4_13] +~g~Te ha visto y está huyendo, ¡atrápale y consigue el maletín! + +[ASM4_14] +~g~La barra de distancia en la parte superior derecha de la pantalla te proporciona una indicación de lo cerca que estás de tu blanco. No permitas que se llene o éste te verá. + +[ASM_5] +MISIÓN DE ASESINO 5 + +[KICK] +ARRANQUE + +[KICK1_3] +~g~Número de veces que pusiste el pie: ~1~ + +[KICK1_4] +~g~Tiempo de penalización: ~1~ segundos + +[BANK] +MISIONES DEL ATRACO AL BANCO + +[BANK1] +MISIÓN DE ATRACO AL BANCO 1 + +[BANK2] +MISIÓN DE ATRACO AL BANCO 2 + +[BJM2_21] +~g~Acierta a tantos blancos como puedas mientras te quede munición. + +[BANK3] +MISIÓN DE ATRACO AL BANCO 3 + +[BJM3_1] +~g~Consigue un coche rápido y ve a la parrilla de salida. + +[BNK4_2A] +Los chicos hicieron un trabajo genial con este pequeño. + +[BNK4_3G] +¡Oh, mierda, ahora los polis andan tras nosotros! + +[BNK4_3H] +Y ni siquiera hemos llegado. + +[BNK4_3K] +Primero tendremos que despistar a los polis... + +[BNK4_3L] +Joder, Tommy, ¿intentas matarnos a todos? + +[BNK4_3N] +¡Todo lo que me importa acaba destrozado! + +[BNK4_26] +¡Maldición! ¡Aquí vienen! + +[BNK4_32] +¡Usa los explosivos para abrir las cajas de depósito! + +[BNK4_36] +¿Dónde está Cam? + +[BNK4_37] +Es historia... + +[BNK4_38] +Ése era el último. ¡Vamos! ¡Vamos! ¡Vamos! + +[BNK_39] +¡Mierda! ¿Dónde está Hilary? + +[BK4_40A] +¡Ya le daré yo miedo al abandono! + +[BNK4_42] +¡Eh, tíos! ¡Subid! ¡Yo os cubro! + +[BNK4_43] +¡Ya cubro yo nuestros culos, tú conduce! + +[BNK4_44] +¡Lo conseguimos! ¡Somos ricos! ¡Ricos! + +[BNK4_45] +Es una pena que Cam no lo consiguiese, ¡era un buen tipo! + +[BNK4_46] +Sí. Pero aún así... ¡más tajada para nosotros! + +[BNK4_47] +¡Claro que sí! ¡Sí! + +[BNK4_48] +Tommy, ¿te gustaría un masaje? + +[BNK4_49] +¡Hola, Mercedes! Sí, estoy un poco tenso... + +[BNK450A] +¿Qué te dije, Tommy? ¿Qué te dije? Maldito SWAT, será mejor que tengas cuidado cuando Kent Paul esté en la ciudad. + +[BNK450B] +Vamos, dame una tajada más grande, colega, venga. Tengo que comprarme ropa nueva. + +[BNK4_51] +Yo creo que tienes buen aspecto. + +[KENT] +MISIONES DE KENT PAUL + +[KENT1] +MISIÓN DE KENT PAUL 1 + +[COUNT] +MISIONES DE FALSIFICACIÓN + +[COUNT1] +MISIÓN DE FALSIFICACIÓN 1 + +[COUNT2] +MISIÓN DE FALSIFICACIÓN 2 + +[BIKE] +LAS MISIONES DE LA PANDILLA DE MOTEROS + +[BIKE1] +MISIÓN DE MOTORISTA 1 + +[BIKE2] +MISIÓN DE MOTORISTA 2 + +[BIKE3] +MISIÓN DE MOTORISTA 3 + +[GOAWAY1] +Vuelve cuando hayas terminado con las misiones de la banda haitiana. + +[HAIT] +LAS MISIONES DE LA BANDA HAITIANA + +[HAIT1] +MISIÓN HAITIANA 1 + +[HAIT2] +MISIÓN HAITIANA 2 + +[HAIT3] +MISIÓN HAITIANA 3 + +[HAM3_6] +~g~Utiliza el rifle de francotirador que te he dejado para cumplir tu tarea. + +[ROCK] +LAS MISIONES DE LA BANDA DE ROCK + +[ROK1_4] +~g~De acuerdo, creo que esto es lo que andabais buscando... + +[ROK1_1E] +~g~¡Costará más de lo que tienes! + +[ROK1_1F] +~g~Vuelve cuando tengas el dinero. + +[RBM2_6] +~g~¡Guau! ¡Ella es un tío, detenle! + +[ROCK3] +MISIONES DE LA BANDA DE ROCK 3 + +[RBM3_5] +~g~Lleva a los Love Fist al escenario. + +[CUBANM] +LAS MISIONES DE LA BANDA CUBANA + +[CUBAN1] +MISIÓN CUBANA 1 + +[CUBAN2] +MISIÓN CUBANA 2 + +[CUB2_10] +~r~¡Se supone que debes matar a haitianos, no a cubanos! + +[CUBAN3] +MISIÓN CUBANA 3 + +[CUBAN4] +MISIÓN CUBANA 4 + +[PROT] +MISIONES DE PROTECCIÓN + +[PORN] +MISIONES PORNO + +[PORN1] +MISIÓN PORNO 1 + +[POR1_03] +~r~¡Candy está muerta! + +[PORN2] +MISIÓN PORNO 2 + +[PORN3] +MISIÓN PORNO 3 + +[PORN4] +MISIÓN PORNO 4 + +[PHIL] +MISIONES DE PHIL + +[PHIL1] +MISIÓN DE PHIL 1 + +[PHIL2] +MISIÓN DE PHIL 2 + +[PIZ1_A] +MISIÓN DEL REPARTIDOR DE PIZZAS + +[CNTBUY1] +Compra de la imprenta: ~1~ $ + +[CARBUY] +Compra del salón de coches: ~1~ $ + +[PORNBUY] +Compra del estudio cinematográfico: ~1~ $ + +[ICEBUY] +Compra de la fábrica de helados: ~1~ $ + +[TAXIBUY] +Compra de la empresa de taxis: ~1~ $ + +[BANKBUY] +Compra del Malibú: ~1~ $ + +[BOATBUY] +Compra del astillero: ~1~ $ + +[PRNT_NO] +No puedes comprar la imprenta en este momento, vuelve más tarde. + +[CAR_NO] +No puedes comprar el salón de coches en este momento, vuelve más tarde. + +[PORN_NO] +No puedes comprar el estudio cinematográfico en este momento, vuelve más tarde. + +[ICE_NO] +No puedes comprar la fábrica de helados en este momento, vuelve más tarde. + +[TAXI_NO] +No puedes comprar la compañía de taxis en este momento, vuelve más tarde. + +[BANK_NO] +No puedes comprar el Malibú en este momento, vuelve más tarde. + +[BOAT_NO] +No puedes comprar el astillero en este momento, vuelve más tarde. + +[COL2_6] +¡Alto, cerdo imperialista americano! + +[COL2_6B] +Eso es propiedad del gobierno francés. + +[COL2_6C] +¡y se acabó! + +[COL3_A] +Thomas, aprecio que vinieses. + +[COL3_B] +Perdóname por ir directamente al asunto. + +[COL3_C] +Diaz me ha pedido que supervise una transacción de un negocio menor. + +[COL3_D] +Esperemos que vaya mejor que la última vez. + +[COL3_E] +Por eso es por lo que pensé en ti, amigo mío. + +[COL3_F] +He dejado algo de protección en el aparcamiento. + +[COL3_G] +Recógele y luego id a ver a los hombres de Diaz en el punto de entrega. + +[COL4_2] +¡No lo sé, señor! + +[COL4_5] +¡Señor, sí, señor! + +[COL4_10] +Vamos a comer unos donuts. + +[COL4_16] +¡Señor! ¡Moviendo el vehículo, señor! + +[COL4_25] +¡Iniciada autodestrucción del vehículo! + +[COL5_6] +Mercedes, esa chica me matará. + +[COL5_8] +¡Maldita cucaracha! + +[COL5_5] +¡Morid, cerdos franceses! + +[CNT2_1] +Matadle. + +[CNT2_2] +¡Conseguid las planchas! + +[CNT2_3] +¡Proteged al mensajero! + +[FINKILL] +¡De acuerdo, chicos, matadle! + +[FIN_1A] +¡Ven aquí, traidor, pedazo de mierda! + +[FIN_1B] +¡Vas a caer, mamón traicionero! + +[FIN_1C] +¡Éste es el último baile para Lance Vance! + +[FIN_2B] +¡Oh, eso crees tú! + +[FIN_2C] +¡Ya tuve suficiente de eso en la escuela! + +[FIN_3] +Ahora no hay nadie para cubrirte el trasero, ¿eh, Tommy? + +[FIN_4] +Eres historia, Tommy, historia. + +[FIN_5] +Escogiste el lado equivocado, Lance... + +[FIN_6] +Sonny está arriba con la caja fuerte y mi dinero... + +[FIN_10] +¿Sonny? ¡Sonny! ¡Voy por ti! + +[FIN_11A] +Me arrebataste quince años, Sonny... + +[FIN_11B] +¡Y ahora voy a hacértelo pagar! + +[FIN_12A] +Todavía no lo captas, ¿verdad? + +[FIN_12B] +Eres de mi propiedad, Tommy. + +[FIN_12C] +¡Esos quince años eran míos para gastarlos! + +[FIN_13] +Atrapadle chicos, nunca comprendió nada. + +[RACES_4] +3 + +[RACES_5] +2 + +[RACES_6] +1 + +[RACES_7] +¡ADELANTE! + +[RACES_9] +Tiempo: ~1~:~1~ + +[RACES] +TIEMPO: + +[RACES17] +Nuevo mejor tiempo: ~1~:~1~ + +[RACES18] +HAS GANADO: ~1~ $ + +[RACES20] +Nuevo mejor tiempo: ~1~:0~1~ + +[RACES21] +Tiempo: ~1~:0~1~ + + +[RCH1_2] +~g~Los CONTROLES están esparcidos a lo largo del aeropuerto. + +[RCH1_5] +Tiempo: + +[RCRC1_2] +~g~¡Ve a la parrilla de salida! + +[RCRC1_4] +~g~3 + +[RCRC1_5] +~g~2 + +[RCRC1_6] +~g~1 + +[RCRC1_7] +~g~¡ADELANTE! + +[RCRC1_8] +~g~Tiempo de carrera: ~1~ segundos + +[RCPL1_1] +~g~Compite en una CARRERA DE PUNTOS DE CONTROL con otros tres Barón RC. + +[RCPL1_2] +~g~Debes atravesar la ~o~CORONA DEL CENTRO ~g~para pasar con éxito un punto de control. + +[RCPL1_3] +~g~¡Ve ahora a la parrilla de salida! + +[ICC1_O] +¿Qué pasa contigo? + +[FEA_2SP] +2 altavoces + +[FEA_4SP] +Más de 2 altavoces + +[FEA_EAR] +Auriculares + +[FEA_NAH] +NO HAY HARDWARE DE AUDIO + +[FET_APP] +APLICAR + +[FES_SKN] +NOMBRE DE APARIENCIA + +[FES_DAT] +FECHA + +[FES_SET] +Utilizar apariencia + +[FET_DEF] +Restaurar valores por defecto + +[FESZ_QZ] +¿Seguro de que quieres guardar esta partida? + +[FES_SCG] +¿Guardar la partida actual? + +[FES_LCG] +¿Cargar la partida y continuar jugando? + +[FEC_FIR] +Disparar + +[FEC_NWE] +Siguiente arma + +[FEC_PWE] +Arma anterior + +[FEC_FOR] +Avanzar + +[FEC_BAC] +Retroceder + +[FEC_LEF] +Izquierda + +[FEC_RIG] +Derecha + +[FEC_ZIN] +Acercar zoom + +[FEC_ZOT] +Alejar zoom + +[FEC_EEX] +Entrar y salir + +[FEC_RAD] +Radio + +[FEC_SUB] +Misión secundaria + +[FEC_CMR] +Cambiar cámara + +[FEC_JMP] +Saltar + +[FEC_SPN] +Esprintar + +[FEC_HND] +Freno de mano + +[FEC_LOL] +Mirar hacia la izquierda + +[FEC_LOR] +Mirar hacia la derecha + +[FEC_NTR] +Siguiente objetivo + +[FEC_PTT] +Objetivo anterior + +[FEC_LBA] +Mirar detrás + +[FEC_CEN] +Centrar cámara + +[FET_CFT] +A PIE + +[FET_CCR] +EN COCHE + +[FET_CAC] +ACCIÓN + +[FEC_IBT] +- + +[FEC_MXO] +MXB1 + +[FEC_MXT] +MXB2 + +[FEC_UNB] +ILIMITADO + +[FEC_TFL] +Mirar a izquierd+Torreta L + +[FEC_TFR] +Mirar a derecha+Torreta R + +[FEC_MWF] +RUEDA DEL RATÓN ARRIBA + +[FEC_MWB] +RUEDA DEL RATÓN ABAJO + +[FEC_ORR] +o + +[FEC_NUS] +NO UTILIZADO + +[FEC_LUD] +Mirar arriba + +[FEC_LDU] +Mirar abajo + +[FEC_CMP] +COMBO: MIRAR I+d + +[LAW_1A] +law_1a + +[LAW_1B] +law_1b + +[LAW_2A] +law_2a + +[LAW_2B] +law_2b + +[FEH_STA] +ESTADÍSTICAS + +[FEH_LOA] +CARGAR + +[FEH_CON] +CONTROLES + +[FEH_AUD] +SONIDO + +[FEH_DIS] +PANTALLA + +[FEH_LAN] +IDIOMA + +[FEH_SGA] +INICIAR NUEVA PARTIDA + +[FEO_CON] +Configuración del mando + +[FEO_AUD] +Configuración de sonido + +[FEO_DIS] +Configuración de pantalla + +[FEO_LAN] +Configuración de idioma + +[FEO_PLA] +Configuración del jugador + +[FET_PS] +CONFIGURACIÓN DE APARIENCIA + +[FEA_OUT] +Salida + +[FEA_ST] +Estéreo + +[FEA_DTS] +DTS + +[FEA_RSS] +Emisora de radio + +[FEA_NON] +RADIO APAGADA + +[FEA_FM0] +WILDSTYLE + +[FEA_FM1] +FLASH FM + +[FEA_FM2] +KCHAT + +[FEA_FM3] +FEVER 105 + +[FEA_FM4] +VROCK + +[FEA_FM5] +VCPR + +[FEA_FM7] +EMOTION 98.3 + +[FEA_FM8] +WAVE 103 + +[FED_BRI] +Brillo + +[FED_TRA] +Estelas + +[FED_SUB] +Subtítulos + +[FED_WIS] +Formato 16:9 + +[FED_POS] +Posición de la pantalla + +[FED_RDR] +RADAR + +[FED_HUD] +MODO HUD + +[FED_RDB] +SOLO ICONOS + +[FE_MLG] +LEYENDA DEL MAPA + +[FEI_SCR] +Desplazarse + +[FEP_RES] +Continuar + +[FEP_STG] +Iniciar partida + +[FEP_STA] +Estadísticas + +[FEP_BRI] +Resumen + +[FEP_OPT] +Opciones + +[FEP_QUI] +Salir del juego + +[FES_LOA] +Cargar partida + +[FES_DEL] +Borrar partida + +[FEC_CSU] +Configuración del controlador + +[FEC_RED] +Redefinir controles + +[FEC_MOU] +Configuración del ratón + +[DISTGOL] +Distancia recorrida en carro de golf (millas) + +[DISTGOM] +Distancia recorrida en carro de golf (m) + +[ST_FAVR] +Estación de radio favorita + +[ST_WSTR] +estación de radio que menos te gusta + +[ST_FAVV] +Vehículo favorito + +[ST_STAR] +Número total de estrellas se busca conseguidas + +[ST_HEAD] +Número de disparos a la cabeza + +[ST_GANG] +Banda que menos te gusta + +[ST_STGN] +Número total de estrellas se busca evadidas + +[TYREPOP] +Neumáticos reventados por disparos + +[TYRESLA] +Neumáticos rajados con una navaja + +[ST_BRK] +Número de muertes en el anillo sangriento + +[ST_LTBR] +Mayor tiempo en el anillo sangriento (segs.) + +[ST_GNG1] +Cubanos + +[ST_GNG2] +Haitianos + +[ST_GNG3] +Aspirantes callejeros + +[ST_GNG4] +Pandilleros de Díaz + +[ST_GNG5] +Guardias de seguridad + +[ST_GNG6] +Banda de motoristas + +[ST_GNG7] +Banda de Vercetti + +[ST_GNG8] +Golfistas + +[FEA_FM6] +ESPANTOSO + +[ST_ASSI] +Contratos de asesinato completados + +[DISTBIK] +Dist. Recorrida en moto (millas) + +[DISTBIM] +Distancia recorrida en moto (m) + +[HOTEL] +Ocean View + +[KICK1_9] +PUNTOS DE CONTROL: + +[FIN_B6] +No tienes suficiente dinero para empezar esta misión. + +[TEX3_9] +~g~Recoge una bomba pasando con el helicóptero RC cerca de ella. + +[HELP22] +Ve a la señal de la casa verde que hay en el radar. + +[FES_FMS] +Éxito al formatear. Selecciona Aceptar para continuar. + +[FES_SSC] +Éxito al guardar. Selecciona Aceptar para continuar. + +[FES_DSC] +Éxito al borrar. Selecciona Aceptar para continuar. + +[FESZ_QC] +¿Deseas sobreescribir esta partida guardada dañada? + +[FES_CHE] +¡Atención! Se han activado uno o más trucos, esto puede afectar a tus partidas guardadas. Te recomendamos no guardar esta partida. + +[FET_SG] +GUARDAR PARTIDA + +[FEH_BRI] +INFORME + +[FEH_MAP] +MAPA + +[FEM_OK] +Aceptar + +[FEC_CRO] +Agacharse + +[FEC_CR3] +Agacharse (Botón L3) + +[FEC_SMT] +Sub-misión + +[FEC_SM3] +Sub-misión (Botón R3) + +[FEC_RSC] +Emisoras de radio + +[ST_PR01] +Aviador + +[ST_PR02] +Piloto del ejército + +[ST_PR03] +Oficial piloto + +[ST_PR04] +Cabo + +[ST_PR05] +Teniente + +[ST_PR06] +Sargento + +[ST_PR07] +Capitán + +[ST_PR08] +Biggs + +[ST_PR09] +Wedge + +[ST_PR10] +Barón Rojo + +[ST_PR11] +Ganso + +[ST_PR12] +Víbora + +[ST_PR13] +Jester + +[ST_PR14] +Chappy + +[ST_PR15] +Iceman + +[ST_PR16] +Maverick + +[ST_PR17] +Negado + +[ST_PR18] +General de la Armada aérea + +[ST_PR19] +As + +[FET_LG] +CARGAR JUEGO + +[CAR_EXP] +Vehículos destruidos + +[BOA_EXP] +Barcos destruidos + +[HEL_DST] +Aviones y helicópteros destruidos + +[STFT_01] +Tiempo más rápido de ''Moto con llantas de aleación'' + +[STFT_02] +Tiempo más rápido de ''El conductor'' + +[STFT_03] +Tiempo más rápido en el Circuito de Tierra + +[STFT_04] +Tiempo más rápido en la Carrera de Aviones RC + +[STFT_05] +Tiempo más rápido en la Carrera de Coches RC + +[STFT_06] +Tiempo más rápido Control del Helicóptero RC + +[STFT_07] +Tiempo más rápido en ''Velocidad terminal'' + +[STFT_08] +Tiempo más rápido en ''Ocean Drive'' + +[STFT_09] +Tiempo más rápido en ''Carrera por el borde'' + +[STFT_10] +Tiempo más rápido en ''Capital Cruise'' + +[STFT_11] +Tiempo más rápido en ''¡Tour!'' + +[STFT_12] +Tiempo más rápido en ''Aguante en V.C.'' + +[STHC_01] +Puntuación más alta en el campo de tiro + +[STHC_02] +Mejor porcentaje de impactos en el campo de tiro + +[STHC_03] +Ventas de droga realizadas + +[HELP24] +Ahora puedes aceptar trabajos del Coronel. + +[HELP25] +Ahora puedes aceptar trabajos de Avery Carrington. + +[HELP29] +Puedes visitar la tienda de ropa cuando no estés en una misión. + +[HELP30] +Cuando compres ropa nueva, tu nivel de se busca se establecerá en cero. + +[ASM4_24] +distancia: + +[RBM1_6] +~g~Lleva a Mercedes y el ''love juice'' a la banda en el estudio de grabación. + +[HELP31] +Para hacer una pasada, mira primero a izquierda o derecha con ~k~~VEHICLE_LOOKLEFT~ o ~k~~VEHICLE_LOOKRIGHT~. + +[HELP34] +Para realizar una pasada deberás tener un subfusil. + +[STRIP_1] +~r~No tienes dinero suficiente, tacaño miserable. + +[EXIT_1] +Pulsa ~k~~PED_SPRINT~ para salir. + +[ASM1_B] +Encontrará su siguiente trabajo pegado con cinta adhesiva bajo el teléfono. + +[ASM1_C] +Tengo más trabajo para usted que requerirá tomar ''el volante'' de la situación. + +[SCARF] +Apartamento 3c + +[LAW4_10] +¡Mamones de directores! + +[RCH1_6] +~g~Utiliza el helicóptero RC para pasar por los puntos de control dispersados por el aeropuerto. + +[RCH1_9] +~b~TIEMPO TOTAL: ~1~:~1~ + +[RCH1_10] +~b~TIEMPO TOTAL: ~1~:0~1~ + +[WHEEL01] +DOBLE BONIFICACIÓN EN DOS RUEDAS: ~1~ $ Distancia: ~1~,~1~m Tiempo: ~1~ segundos + +[WHEEL02] +DOBLE BONIFICACIÓN EN DOS RUEDAS: ~1~ $ Distancia: ~1~ pies Tiempo: ~1~ segundos + +[WHEEL03] +BONIFICACIÓN EN DOS RUEDAS: ~1~ $ Tiempo: ~1~ segundos + +[WHEEL04] +BONIFICACIÓN EN DOS RUEDAS: ~1~ $ Distancia: ~1~,~1~m + +[WHEEL05] +BONIFICACIÓN EN DOS RUEDAS: ~1~ $ Distancia: ~1~ pies + +[WHEEL06] +BONIFICACIÓN EN CABALLITO: ~1~ $ Distancia: ~1~,~1~m Tiempo: ~1~ segundos + +[WHEEL07] +BONIFICACIÓN EN CABALLITO: ~1~ $ Distancia: ~1~ pies Tiempo: ~1~ segundos + +[WHEEL08] +BONIFICACIÓN EN CABALLITO: ~1~ $ Tiempo: ~1~ segundos + +[WHEEL09] +BONIFICACIÓN EN CABALLITO: ~1~ $ Distancia: ~1~,~1~m + +[WHEEL10] +BONIFICACIÓN EN CABALLITO: ~1~ $ Distancia: ~1~ pies + +[WHEEL11] +BONIFICACIÓN EN PARADA MÁS LARGA: ~1~ $ Distancia: ~1~,~1~m Tiempo: ~1~ segundos + +[WHEEL12] +BONIFICACIÓN EN PARADA MÁS LARGA: ~1~ $ Distancia: ~1~ pies Tiempo: ~1~ segundos + +[WHEEL13] +BONIFICACIÓN EN PARADA MÁS LARGA: ~1~ $ Tiempo: ~1~ segundos + +[WHEEL14] +BONIFICACIÓN EN PARADA MÁS LARGA: ~1~ $ Distancia: ~1~,~1~m + +[WHEEL15] +BONIFICACIÓN EN PARADA MÁS LARGA: ~1~ $ Distancia: ~1~ pies + +[ROK3_72] +¡Love Fist! + +[POR1_19] +¡Eh! + +[DESPERA] +Desesperado + +[MOB_99A] +Ve hasta el teléfono público junto al centro comercial en Washington. + +[MOB_98A] +Ve hasta el teléfono público en Vice Point. + +[MOB_96A] +Ve hasta el teléfono público en la terminal del aeropuerto. + +[MOB_95A] +Ve hasta el teléfono público en Little Havana. + +[BNK1_1] +¿Le puedo ayudar, señor? + +[BNK1_2] +¡Hay un impostor! + +[BNK1_3] +¡Se ha vuelto loco! + +[BNK1_4] +¿Quién demonios eres? + +[BNK1_5] +¿Dónde está tu placa? + +[BNK1_6] +¡Ahí están! ¡Dispara a matar! + +[MOB_24A] +Hola, ¿Sr. Vercetti? + +[MOB_24B] +Sí. + +[MOB_24C] +Soy Cortez. Usted estuvo en mi fiesta. + +[MOB_24D] +Sí, lo recuerdo. + +[MOB_24E] +Sr. Vercetti, ha sido un desgraciado incidente lo que ocurrió con su operación de negocios. + +[MOB_24F] +Lo sé. + +[MOB_24G] +Quiero que sepa que tanto yo como mi gente estamos haciendo todo lo que podemos para llegar al fondo de este tema. + +[MOB_24H] +Si desea hablar conmigo de manera más privada, me podrá encontrar en el barco. Buenos días, señor. + +[BNK2_6] +¡Este tío está chalado! + +[ANGEL] +Ángel + +[CUBJET] +Jetmax Cubano + +[SANDKIN] +Sandking + +[POLMAV] +Maverick de la policía + +[BOXVILL] +Boxville + +[BENSON] +Benson + +[HOTRINA] +Corredor de Hotring + +[HOTRINB] +Corredor de Hotring + +[BLOODRA] +Coche de Bloodring + +[BLOODRB] +Coche de Bloodring + +[MAFIACR] +Yate de la Mafia + +[COP_M2] +ANTI VICIO + +[COP_M3] +TRUENO MARRÓN + +[BNK3_2] +No voy a conducir por ti, ni soñando, voy a compartir esto con el grupo. + +[FEM_SL1] +No hay archivo 1 guardado + +[FEM_SL2] +No hay archivo 2 guardado + +[FEM_SL3] +No hay archivo 3 guardado + +[FEM_SL4] +No hay archivo 4 guardado + +[FEM_SL5] +No hay archivo 5 guardado + +[FEM_SL6] +No hay archivo 6 guardado + +[FEM_SL7] +No hay archivo 7 guardado + +[FEM_SL8] +No hay archivo 8 guardado + +[FEA_CHA] +Cambiando la salida de audio a ESTÉREO. Espera... + +[FEA_CHD] +¡Aviso! Estás cambiando la salida de ESTÉREO a DTS. Espera... + +[FEI_SEL] +Selec. + +[FEI_BAC] +Atrás + +[FEI_RES] +Reanudar + +[FEI_NAV] +Navegar + +[FEI_BTX] +botón / - + +[FEI_BTT] +botón " - + +[FEI_STA] +Botón START - + +[FEI_BTD] +; = > < - + +[FEI_STO] +Parar + +[MOB_68A] +Tommy, tío, tengo una sorpresa para ti. + +[MOB_68B] +Me encuentro en el estudio de grabación con unos artistas importantes. + +[MOB_68C] +¿Por qué no te vienes por aquí? + +[MOB_68D] +Sabes que esto tiene sentido, ¿a que sí? Hasta dentro de un rato. + +[OUTFT1] +Calle + +[OUTFT2] +Etiqueta + +[OUTFT3] +Mono de trabajo + +[OUTFT4] +Club de campo + +[OUTFT5] +Habana + +[OUTFT6] +Policía + +[OUTFT7] +Atraco al banco + +[OUTFT8] +Informal + +[OUTFT9] +Sr. Vercetti + +[OUTFT10] +Chándal + +[OUTFT13] +MC Tommy + +[CAR_AS1] +CONCESIONARIO DE COCHES CONSOLIDADO + +[CAR_AS2] +~g~El Concesionario de coches Sunshine generará ahora unos ingresos de hasta ~1~ $ máximo. Asegúrate de recaudarlos regularmente. + +[BUYSAVE] +~g~A partir de ahora, podrás guardar tu partida aquí cuando no estés en una misión. + +[BUYGARG] +~g~También puedes almacenar vehículos en este garaje. + +[STRPBUY] +Adquirido el club Pole Position: ~1~ $ + +[GA_4] +Las bombas de coche son 500 $ cada una. + +[GA_5] +Tu coche ya está equipado con una bomba. + +[GA_6] { reVC update } +¡Apárcalo, actívala pulsando ~h~~k~~VEHICLE_FIREWEAPON~~w~ y SAL PITANDO! + +[GA_7] { reVC update } +¡Ármalo pulsando ~h~~k~~VEHICLE_FIREWEAPON~~w~. La bomba explotará cuando se arranque el motor. + +[GA_6B] { reVC update } +¡Apárcalo, actívala pulsando ~h~~k~~VEHICLE_FIREWEAPON~~w~ y SAL PITANDO! + +[GA_7B] { reVC update } +¡Ármalo pulsando ~h~~k~~VEHICLE_FIREWEAPON~~w~. La bomba explotará cuando se arranque el motor. + +[MOB_70A] +Tommy, soy yo, el coronel Cortez. Mira, me parece que eres la clase de hombre que lleva a cabo los trabajos. Así que ayúdame, por favor. + +[MOB_70B] +Me podrás encontrar en el barco. + +[PICK1] +¡El chaleco antibalas ha sido entregado en el hotel Ocean View! + +[PICK2] +¡La .357 ha sido entregada en el hotel Ocean View! + +[PICK3] +¡La sierra mecánica ha sido entregada en el hotel Ocean View! + +[PICK4] +¡El lanzallamas ha sido entregado en el hotel Ocean View! + +[PICK5] +¡El rifle de francotirador .308 ha sido entregado en el hotel Ocean View! + +[PICK6] +¡La Ametralladora Pesada ha sido entregada en el hotel Ocean View! + +[PICK7] +¡El lanzacohetes ha sido entregado en el hotel Ocean View! + +[PICK8] +¡El Sea Sparrow está ahora disponible en la mansión de Starfish Island! + +[PICK9] +¡El tanque está ahora disponible en los cuarteles del ejército! + +[PICK10] +¡El Hunter está ahora disponible en los cuarteles del ejército! + +[CLOTH1] +El traje de etiqueta ha sido entregado en Rafaels en Ocean Beach. + +[CLOTH2] +El traje de calle ha sido entregado en los pisos francos. + +[CLOTH3] +El mono de trabajo ha sido entregado en Tooled Up en el centro comercial de North Point. + +[CLOTH4] +El traje del club de campo ha sido entregado en el club de golf de Leaf Links. + +[CLOTH5] +El traje Havana ha sido entregado en la tienda de ropa de Little Havana. + +[CLOTH6] +El traje de policía ha sido entregado en la jefatura de policía de Washington Beach. + +[CLOTH7] +El traje informal ha sido entregado en Gash del centro comercial de North Point. + +[CLOTH8] +El traje del Sr. Vercetti ha sido entregado en Collar & Cuffs de Ocean Beach. + +[CLOTH9] +El chándal ha sido entregado en Jocksport del centro de la ciudad. + +[CLOTH10] +El traje para el atraco al banco ha sido entregado en el Club Malibu de Vice Point. + +[MOB_62A] +Tommy, soy Ricardo Díaz, quiero darte las gracias por cuidar de mí, tío. + +[MOB_62B] +He preguntado al gilipollas de Cortez, y me ha dicho que eres bueno, amigo, ¿por qué no vienes a verme? + +[MOB_62C] +Necesito un tío como tú. Todos los que tengo son gilipollas perdidos, + +[MOB_62D] +gilipollas por todas partes, tío. Yo te puedo hacer muy rico. + +[GOAWAY2] +~g~Regresa cuando hayas terminado las misiones de la banda de moteros. + +[COL2_9] +¡Estúpido americano! ¡Te han seguido hasta aquí! + +[LOADCOL] +Cargando... + +[STFT_17] +Tiempo más rápido en la ''Prueba PCJ'' + +[STFT_18] +Tiempo más rápido en el ''Trial de tierra'' + +[STFT_19] +Tiempo más rápido en la ''Pista de Pruebas'' + +[NEW_REC] +¡Nuevo récord establecido! ~w~~1~ minutos ~g~y ~w~~1~ segundos. + +[BMX_HOW] +~g~¡Da dos vueltas a la pista de tierra, ~y~pasando por ~g~los ~y~PUNTOS DE CONTROL ~g~por el camino! + +[BMXREW1] +~g~¡Cada vez que batas tu récord anterior de las dos vueltas + +[BMXREW2] +~g~conseguirás una ~y~RECOMPENSA ~g~mejor! + +[BMXRAIN] +~g~Parece lluvia... + +[ITBEG] +Al principio... + +[NBMNBUY] +Casa Swanko adquirida: ~1~ $ + +[LNKVBUY] +Apartamento en Links View adquirido: ~1~ $ + +[HYCOBUY] +Piso de Hyman adquirido: ~1~ $ + +[BUYGARS] +~g~Puedes guardar vehículos en estos garajes. + +[OCHEBUY] +Apartamento de Ocean Heights adquirido: ~1~ $ + +[WASHBUY] +1102 de la calle Washington adquirido: ~1~ $ + +[VCPTBUY] +3321 de Vice Point adquirido: ~1~ $ + +[SKUMBUY] +Chabola Skumole adquirida: ~1~ $ + +[HELP6_C] +Pulsa ~h~~k~~VEHICLE_HANDBRAKE~~w~ para usar el freno de mano del vehículo. + +[HELP2_A] +Pulsa ~h~~k~~PED_SPRINT~~w~~w~ cuando estés corriendo para ~h~esprintar. + +[HELP4_A] +Pulsa ~h~~k~~VEHICLE_ACCELERATE~~w~ para acelerar. + +[HELP5_A] +Pulsa ~h~~k~~VEHICLE_BRAKE~~w~ para frenar o para dar marcha atrás si el vehículo se ha detenido. + +[HELP8_A] +Pulsa ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ para hacer zoom con el rifle y ~h~~k~~PED_SNIPER_ZOOM_OUT~~w~ para alejar la vista. + +[PBOAT_1] { reVC update } +Pulsa ~h~ ~k~~VEHICLE_FIREWEAPON~~w~ para disparar los cañones del barco. + +[SEG3_4] { reVC update } +~g~Puedes recoger bombas simplemente pilotando tu Helicóptero RC cerca de cada una de ellas, para soltar una bomba pulsa ~h~~k~~VEHICLE_FIREWEAPON~~g~. + +[RCR1_3] { reVC update } +~g~Si quieres abandonar esta misión, pulsa ~h~~k~~VEHICLE_FIREWEAPON~~g~ para detonar tu coche RC. + +[HELP32] { reVC update } +A continuación dispara pulsando ~h~~k~~VEHICLE_FIREWEAPON~. + +[HELP33] { reVC update } +A continuación dispara pulsando ~h~~k~~VEHICLE_FIREWEAPON~. + +[TTUTOR] +Pulsa ~h~~k~~TOGGLE_SUBMISSIONS~~w~ para comenzar o cancelar las misiones de taxista. + +[TTUTOR2] +Pulsa ~h~~k~~TOGGLE_SUBMISSIONS~~w~ para comenzar o cancelar las misiones de taxista. + +[FTUTOR] +Pulsa ~h~~k~~TOGGLE_SUBMISSIONS~~w~ para comenzar o cancelar las misiones del camión de bomberos. + +[FTUTOR2] +Pulsa ~h~~k~~TOGGLE_SUBMISSIONS~~w~ para comenzar o cancelar las misiones del camión de bomberos. + +[CTUTOR] +Pulsa ~h~~k~~TOGGLE_SUBMISSIONS~~w~ para comenzar o cancelar las misiones de justiciero. + +[CTUTOR2] +Pulsa ~h~~k~~TOGGLE_SUBMISSIONS~~w~ para comenzar o cancelar las misiones de justiciero. + +[HELP8_B] +Pulsa ~h~~k~~PED_SNIPER_ZOOM_IN~ ~w~para ~h~acercar la vista ~w~con el rifle y ~h~~k~~PED_SNIPER_ZOOM_OUT~ ~w~para ~h~alejar la vista ~w~otra vez. + +[ATUTOR3] +Pulsa ~h~~k~~TOGGLE_SUBMISSIONS~~w~ para comenzar o cancelar las misiones de ATS. + +[GUN_H1] +~w~Pulsa ~h~~k~~PED_SPRINT~~w~ para comprar. ~w~Pulsa ~h~~k~~VEHICLE_ENTER_EXIT~~w~ para salir. + +[PU_CF3] { reVC update } +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para reemplazar tu arma actual en esta ranura. + +[PU_CF4] { reVC update } +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para reemplazar tu arma actual en esta ranura. + +[HELP9_B] +Pulsa ~h~~k~~PED_FIREWEAPON~ ~w~para ~h~disparar~w~ el rifle de francotirador. + +[HELP37] +Si no quieres subir al coche mientras se lo estás robando a alguien, pulsa ~h~~k~~PED_SPRINT~. + +[HELP6_A] +Pulsa ~h~~k~~VEHICLE_HANDBRAKE~~w~ para usar el freno de mano del vehículo. + +[HELP6_D] +Pulsa ~h~~k~~VEHICLE_HANDBRAKE~~w~ para usar el freno de mano del vehículo. + +[HELP26] +Pulsa ~h~~k~~VEHICLE_ENTER_EXIT~~w~ para subir o bajar de un vehículo. + +[HELP27] +Pulsa ~h~~k~~VEHICLE_TURRETUP~~w~ o ~h~~k~~VEHICLE_TURRETDOWN~~w~ para equilibrar tu peso en la moto. + +[HELP28] +Pulsa ~h~~k~~VEHICLE_TURRETUP~~w~ o ~h~~k~~VEHICLE_TURRETDOWN~~w~ para equilibrar tu peso en la moto. + +[HELP35] +Pulsa ~h~~k~~GO_LEFT~~w~ o ~h~~k~~GO_RIGHT~~w~ para conducir el vehículo. + +[HELP36] +Pulsa ~h~~k~~GO_LEFT~~w~ o ~h~~k~~GO_RIGHT~~w~ para conducir el vehículo. + +[HELP42] +Ve al ~q~icono rosa~w~ para encontrar el hotel. + +[HELP19] +Camina hasta el ~q~marcador rosa~w~ para continuar. + +[HELP1] +Párate en el centro del ~q~marcador rosa. + +[HELP12] +Camina hasta el centro del ~q~marcador rosa~w~ para activar una misión. + +[SEG3_6] +~g~Para acertar en una zona objetivo con éxito, deberás soltar una bomba en la zona representada por el ~q~marcador rosa~w~. Puedes soltar las bombas en cualquier orden. + +[S_PROMP] +Cuando no estés en una misión podrás guardar tu progreso recogiendo las ~h~cintas de cassette~w~. + +[HELP16] +Cruza la puerta principal del hotel ~h~Ocean View~w~ para entrar en el edificio. + +[HELP43] +~g~Ve al hotel ~h~Ocean View~g~ en Ocean Drive. + +[HELI_F1] +~r~¡Misión de puntos de control cancelada! + +[AMMUHLP] +Si necesitas armas pásate por ~h~Ammu-Nation~w~. Está indicado en el radar como una ~h~pistola~w~ azul. + +[HELI_1] +Control del Helicóptero de Centro de la ciudad + +[HELI_2] +Control del Helicóptero de Ocean Beach + +[HELI_3] +Control del Helicóptero de Vice Point + +[HELI_4] +Control del Helicóptero de Little Haiti + +[FST_MFR] +Emisora de radio preferida + +[FST_LFR] +Emisora de radio menos escuchada + +[FEI_HOL] +Mantener + +[FEI_ZOO] +Zoom + +[FEI_BTR] +> < - + +[FEI_NA] +N\A + +[MESA] +Mesa Grande + +[STRP_NO] +No puedes comprar el club de striptease en este momento, vuelve luego. + +[CHSE] +PERSECUCIÓN + +[NBMN_L] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la casa Swanko por ~1~ $. + +[NBMN_T] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la casa Swanko por ~1~ $. + +[NBMN_C] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la casa Swanko por ~1~ $. + +[LNKV_L] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el apartamento de Links View por ~1~ $. + +[LNKV_T] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el apartamento de Linsk View por ~1~ $. + +[LNKV_C] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el apartamento de Links View por ~1~ $. + +[HYCO_L] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el piso de Hyman por ~1~ $. + +[HYCO_T] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el piso de Hyman por ~1~ $. + +[HYCO_C] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el piso de Hyman por ~1~ $. + +[OCHE_L] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el apartamento de Ocean Heights por ~1~ $. + +[OCHE_T] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el apartamento de Ocean Heights por ~1~ $. + +[OCHE_C] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el apartamento de Ocean Heights por ~1~ $. + +[WASH_L] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el 1102 de la calle Washington por ~1~ $. + +[WASH_T] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el 1102 de la calle Washington por ~1~ $. + +[WASH_C] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el 1102 de la calle Washington por ~1~ $. + +[VCPT_L] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el 3321 de Vice Point por ~1~ $. + +[VCPT_T] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el 3321 de Vice Point por ~1~ $. + +[VCPT_C] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el 3321 de Vice Point por ~1~ $. + +[SKUM_L] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la chabola Skumole por ~1~ $. + +[SKUM_T] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~para comprar la chabola Skumole por ~1~ $. + +[SKUM_C] +Pulsa ~h~~k~~PED_ANSWER_PHONE~ ~w~ para comprar la chabola Skumole por ~1~ $. + +[PRNT_L] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la imprenta por ~1~ $. + +[PRNT_T] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la imprenta por ~1~ $. + +[PRNT_C] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la imprenta por ~1~ $. + +[CAR_L] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el concesionario de coches por ~1~ $. + +[CAR_T] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el concesionario de coches por ~1~ $. + +[CAR_C] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el concesionario de coches por ~1~ $. + +[PORN_L] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar los estudios de cine por ~1~ $. + +[PORN_T] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar los estudios de cine por ~1~ $. + +[PORN_C] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar los estudios de cine por ~1~ $. + +[ICE_L] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la fábrica de helados por ~1~ $. + +[ICE_T] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la fábrica de helados por ~1~ $. + +[ICE_C] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la fábrica de helados por ~1~ $. + +[TAXI_L] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la compañía de taxis por ~1~ $. + +[TAXI_T] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la compañía de taxis por ~1~ $. + +[TAXI_C] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la compañía de taxis por ~1~ $. + +[BANK_L] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el Malibú por ~1~ $. + +[BANK_T] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el Malibú por ~1~ $. + +[BANK_C] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el Malibú por ~1~ $. + +[BOAT_L] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el astillero por ~1~ $. + +[BOAT_T] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el astillero por ~1~ $. + +[BOAT_C] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el astillero por ~1~ $. + +[STRP_L] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el club Pole Position por ~1~ $. + +[STRP_T] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el club Pole Position por ~1~ $. + +[STRP_C] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el club Pole Position por ~1~ $. + +[STOCK] +~r~Agotado + +[HELP14] +Para encontrar el bufete del abogado, ve hacia la ~h~señal de la L~w~ que hay en el radar. + +[RAMPAGE] +¡MASACRE! + +[RAMP_F] +¡MASACRE FALLIDA! + +[RAMP_P] +¡MASACRE COMPLETADA! + +[RAMP_A] +¡TODAS LAS MASACRES COMPLETADAS! + +[PAGE_01] +¡Liquida a ~1~ criminales en 2 minutos! + +[PAGE_02] +¡Destruye ~1~ vehículos en 2 minutos! + +[PAGE_03] +¡Pasa en el coche y cárgate a ~1~ criminales en 2 minutos! + +[PAGE_04] +¡Atropella y mata a ~1~ criminales en 2 minutos! + +[PAGE_05] +¡Abate a tiros a ~1~ criminales en 2 minutos! + +[SENTXS] +Sentinel XS + +[MAP_LEG] +Leyenda + +[VCNMAV] +VCN Maverick + +[LG_01] +Posición del jugador + +[LG_02] +Avery Carrington + +[LG_03] +Contacto del motero + +[LG_04] +Coronel Cortez + +[LG_05] +Ricardo Díaz + +[LG_06] +Kent Paul + +[LG_07] +Abogado + +[LG_08] +Phil Cassidy + +[LG_09] +Astillero + +[LG_10] +Club Malibú + +[LG_11] +Cubanos + +[LG_12] +Estudio Cinematográfico + +[LG_13] +Ammu-Nation + +[LG_14] +Haitianos + +[LG_15] +Ferretería + +[LG_16] +Piso franco + +[LG_17] +Helado + +[LG_18] +Taxis Kaufman + +[LG_19] +Love Fist + +[LG_20] +Imprenta + +[LG_21] +Propiedad + +[LG_22] +Taller de Pintura + +[LG_23] +Tienda de ropa + +[LG_24] +Mansión de Tommy + +[LG_25] +Teléfono + +[LG_26] +Emisora de radio Wildstyle + +[LG_27] +Emisora de radio Flash FM + +[LG_28] +Emisora de radio Kchat + +[LG_29] +Emisora de radio Fever 105 + +[LG_30] +Emisora de radio VRock + +[LG_31] +Emisora de radio VCPR + +[LG_32] +Emisora de radio Espantoso + +[LG_33] +Emisora de radio Emotion 98.3 + +[LG_34] +Emisora de radio Wave 103 + +[LG_35] +Destino + +[LG_36] +Solarium + +[LG_37] +Club de Striptease + +[LG_38] +Objetivo + +[MAP_YAH] +ESTÁS AQUÍ + +[TAXSHRT] +~g~Puedes coger este taxi Kaufman para ir a tu destino sin conducir. Te costará 9 $. + +[FEST_HV] +Nivel más alto de misión de justiciero + +[CLOHELP] +¡Ropa limpia! + +[SUNSHIN] +Coches Sunshine + +[CHERRYP] +Helados Cherry Popper + +[KAUFCAB] +Taxis Kaufman + +[BOATYAR] +El Astillero + +[WANT_L] +Has perdido tu nivel de se busca, si cometes un crimen mientras las estrellas parpadean, tu nivel total de se busca se restablecerá. + +[FEI_BTU] +; = - + +[BOAT_AS] +~g~El astillero generará ahora unos ingresos hasta un máximo de ~1~ $. Asegúrate de recaudarlos de manera regular. + +[BOAT_A2] +ASTILLERO COMPLETADO + +[BOAT_N] +Punto de control Charlie + +[BOAT_P] +~g~Recoge los paquetes antes de que se acabe el tiempo. + +[FEI_R1B] +Botón R1 \ R2 - + +[HELP9_A] +Pulsa ~h~~k~~PED_FIREWEAPON~ ~w~para disparar el rifle de francotirador. + +[HELP21] +Pulsa ~h~~k~~VEHICLE_ENTER_EXIT~ ~w~para subir o bajar de un vehículo. + +[CREAM] +Distribución + +[UMBERTO] +Café Robina + +[PU_CF1] +Pulsa ~h~~k~~PED_ANSWER_PHONE~ ~w~para recoger esta arma. Reemplazará cualquier arma del mismo tipo que tengas. + +[FED_RDM] +MAPA E ICONOS + +[FEC_ILU] +Invertir vista en primera persona + +[NITRO] +¡Ahora todos los taxis disponen de un impulsor de salto! Simplemente toca el claxon. + +[RATNG53] +Informal + +[RATNG54] +Vergüenza + +[RATNG55] +Hacker + +[RATNG56] +Liante + +[RATNG57] +Mentiroso compulsivo + +[STHC_04] +Puntuación más alta por mantener la pelota en el aire en la playa + +[STHC_05] +Mejor resultado de Hotring + +[STFT_13] +Mejor tiempo en el Control del Helicóptero del centro + +[STFT_14] +Mejor tiempo en el Control del Helicóptero de Ocean Beach + +[STFT_15] +Mejor tiempo en el Control del Helicóptero de Vice Point + +[STFT_16] +Mejor tiempo en el Control del Helicóptero de Little Haití + +[STFT_21] +Tiempo más rápido en Hotring + +[STFT_22] +Vuelta más rápida en Hotring + +[STFT_20] +Tiempo más rápido en el ''Conocircuito'' + +[HELP44] +Detente en el ~q~marcador rosa. + +[HELP45] +Pulsa ~h~~k~~PED_DUCK~~w~ para agacharte. Esto aumentará tu puntería con las armas que llevas. + +[RCR1_5] +Carrera Bandit RC + +[RCPL1_7] +Carrera Barón RC + +[RCH1_11] +Recogidas del Raider RC + +[FEA_CTD] +¡Aviso! Esta característica requiere que el dispositivo compatible DTS esté conectado. ¿Continuar? + +[FEM_STE] +UTILIZAR ESTÉREO + +[GREET] +Saludos desde... + +[LANCE_1] +¡Venga, tío, conduce con más cuidado! + +[LANCE_2] +¡Eh, mira lo que haces! + +[LANCE_3] +¿Eh, dónde vamos ahora? + +[LANCE_4] +¿Qué estamos haciendo ahora? + +[LAW4_15] +¡Más dinero! + +[MERC_5] +Bonito coche, Mr. Vercetti. + +[MERC_26] +¡DEPRISA, DEPRISA, DEPRISA! + +[MERC_27] +Con cuidado Tommy, me arreglaron la nariz el mes pasado. + +[MERC_28] +Conduce con cuidado Tommy. + +[MERC_29] +Ve más despacio Tommy. + +[MERC_30] +Tommy, si no te importa mata a quien quieras menos a mí. + +[MERC_31] +¡Tommy, cielo, no me mates! + +[MERC_32] +¡Tommy, me alegro que robaras este coche! + +[MERC_40] +Me he divertido tanto. + +[MERC_43] +Adiós, mi ángel. + +[MERC_44] +Ahora sigue machacándote, me oyes. + +[MERC_45] +Ciao, guapetón. + +[COL5_17] +¡Oh, Dios mío, tienen un helicóptero! + +[COL5_18] +¡Dispara al helicóptero! + +[COL5_19] +Tommy, ¡hazte con ese helicóptero! + +[COL5_20] +¡Ahí viene otra vez! ¡Derriba a ese helicóptero! + +[COL5_21] +¡Mira el tamaño de ese helicóptero! + +[COL5_22] +¡Ahí viene de nuevo! + +[FEA_DSM] +¡Aviso! Esta partida guardada está configurada para utilizar DTS. Esto requiere que el dispositivo compatible DTS esté conectado. Por favor selecciona si quieres continuar usando el sistema DTS o ESTÉREO. + +[STFT_23] +Tiempo más rápido en Punto de control Charlie + +[HELP50] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para colocar la cámara detrás de ti. + +[HELP51] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para colocar la cámara detrás de ti. + +[HELP52] +Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para colocar la cámara detrás de ti. + +[HELP53] +Pulsa ~h~~k~~PED_CYCLE_WEAPON_LEFT~~w~ o ~h~~k~~PED_CYCLE_WEAPON_RIGHT~~w~ para elegir entre todas tus armas disponibles. + +[HELP46] +Hay ocho categorías de armas diferentes. + +[HELP47] +Puedes llevar una arma de cada categoría al mismo tiempo: un tipo de pistola, un tipo de rifle. + +[HELP54] +~w~Cuesta: ~1~ $. ~r~Si compras esta arma reemplazarás la actual. + +[HELP2A2] +Pulsa ~h~~k~~PED_SPRINT~~w~ cuando corras para ~h~esprintar. + +[HLPSN_A] +El rifle de francotirador te permite hacer zoom de acercamiento y disparar con más precisión a objetivos a una cierta distancia. + +[HLPSN_B] +Mantén pulsado ~h~~k~~PED_LOCK_TARGET~ ~w~para ~h~apuntar~w~ con el rifle de francotirador. + +[HLPSN_C] +Mantén pulsado ~h~~k~~PED_LOCK_TARGET~ ~w~para ~h~apuntar~w~ con el rifle de francotirador. + +[HLPSN_D] +Pulsa ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ para ~h~acercar la vista ~w~con el rifle y ~h~~k~~PED_SNIPER_ZOOM_OUT~ ~w~para ~h~alejarla~w~. + +[HLPSN_E] +Pulsa ~h~~k~~PED_FIREWEAPON~~w~ para ~h~disparar~w~ el rifle de francotirador. + +[HLPSN_F] +Pulsa ~h~~k~~PED_FIREWEAPON~~w~ para ~h~disparar~w~ el rifle de francotirador. + +[HLPSN_G] +Pulsa ~h~~k~~PED_FIREWEAPON~~w~ para ~h~disparar~w~ el rifle de francotirador. + +[PLANE_H] +Mueve ~h~~k~~VEHICLE_ACCELERATE~~w~ hacia delante para acelerar y a izquierda o derecha para girar. + +[PLANE_4] { reVC update } +{Mueve ~h~~k~~VEHICLE_ACCELERATE~~w~ hacia delante para acelerar y a izquierda o derecha para girar.} +Utiliza el joystick analógico derecho para acelerar, tira hacia atrás el joystick analógico izquierdo para ascender, empújalo hacia adelante para descender. Para girar muévelo a izquierda o derecha. + +[HELP55] +Pulsa ~h~~k~~PED_FIREWEAPON~~w~ para atacar al chef. + +[STPR_8] +Club Pole Position + +[STPR_9] +3321 de Vice Point + +[STPR_10] +Apartamento de Links View + +[STPR_11] +Casa Swanko + +[STPR_12] +1102 de Washington Street + +[STPR_13] +Apartamento de Ocean Heights + +[STPR_14] +Chabola Skumole + +[STPR_15] +Piso de Hyman + +[RCCANX] +~r~Misión de avión RC cancelada. + +[CLT_HL2] +Cuando recojas ropa, te librarás de una o dos estrellas del nivel de se busca. + +[CRED009] +DISEÑO DE MISIONES + +[CRED359] +LEE JOHNSON + +[CRED360] +HENDRIK LESSER + +[CRED361] +PASQUALE STACCHIOTTI + +[CRED362] +ENRIQUE FERNÁNDEZ + +[CRED363] +PAUL BYERS + +[CRED364] +MIKE EMENY + +[CRED365] +ROB DUNKIN + +[CRED366] +CHARLIE KINLOCH + +[CRED367] +KEVIN HOBSON + +[CRED368] +JIM CREE + +[MOB_66A] +Tommy, Tommy, ¿para qué has vuelto? + +[MOB_66B] +Ya te he dicho que no queremos verte más. + +[MOB_67A] +Tommy, creo que no deberías acercarte, ¿me oyes? + +[MOB_67B] +Los chicos haitianos no están muy contentos contigo. + +[MOB_18A] +Tommy, soy Paulo, ¿cómo estás? Vale hombre, bueno pensé que tenía que mandarte una nota. + +[MOB_18B] +Oh Dios misericordioso, hijo mío, no te vas a creer la categoría de la furcia que acabo de conocer. + +[MOB_18C] +Prostituta callejera o algo parecido, cerca de Little Havana, tío. + +[MOB_18D] +Me dijo que se llamaba Mercedes o algo así. + +[MOB_18E] +Oh Dios, tío, tienes que probar a esa palomita. + +[MOB_18F] +Sabe como ponerte a tono. Dijo que yo era el mejor tío con el que había estado en toda su vida. + +[MOB_18G] +Estate al tanto por si la ves. Hasta luego. + +[MOB_72A] +Tommy, soy yo, Lance. Mantén la boca cerrada Tommy porque no tengo ganas de hablar. + +[MOB_72B] +No estoy interesado en lo que tengas que decir. ¿Por qué debería estarlo? ¿Te preocupas tú por mí? + +[MOB_72C] +Tienes que cuidarme un poco mejor. Darme una buena tajada, ya sabes... + +[MOB_72D] +Tommy... oh, mira, tío, Lo siento. Es solo que... + +[MOB_72E] +La gente me ha estado sobreprotegiendo toda mi vida, tratándome como a un niño. + +[MOB_72F] +Mi hermano haría eso. Por favor, tío, no lo hagas. + +[MOB_72G] +Tengo que irme. + +[MOB_63A] +Tommy, soy Earnest. Earnest Kelly. + +[MOB_63B] +¿Cómo estás? + +[MOB_63C] +Estoy bien. Necesitaré un bastón para andar pero muy pronto volveré al trabajo. + +[MOB_63D] +Bien. + +[MOB_63E] +Me enteré de lo de Lance. Vaya cabroncete, ¿eh? + +[MOB_63F] +Sí. + +[MOB_63G] +No te fíes nunca de un hombre que va por la calle en pijama. Y es lo que digo yo. Menos mal que lo mataste. Espero que el mamón sufriera. + +[MOB_63H] +Creo que sí. Lo que pasa es que yo no creí que... + +[MOB_63I] +Tommy, para ser un chalado furioso, eres bastante ingenuo. Cuando vuelva a trabajar pronto te enseñaré un par de cosas sobre la vida, me oyes. + +[MOB_63J] +Tómate tu tiempo, Earnest. Cuídate. + +[MOB_16A] +Tommy, soy Paulo, ¿qué pasa amigo? + +[MOB_16B] +Qué quieres Paul. No necesito ropa de marca de imitación. + +[MOB_16C] +Muy gracioso, tío, pero sabes que no toco nada de mercancía ilegal. No, sólo llamaba para ver si puedo conseguir un papel en una de tus películas. + +[MOB_16D] +Allí en Inglaterra hice un montón de porno, colega. Soy bastante más ardiente que tú, pequeño. + +[MOB_16E] +Paul, gracias por la oferta, lo tendré en cuenta. + +[MOB_16F] +En serio, después de todo lo que he hecho por ti, no te olvides de mí. + +[MOB_16G] +Eso es lo que intento olvidar. + +[MOB_17A] +Tommy Vercetti, ¿Qué tal te va Sr. Influyente? He oído un montón de cosas sobre ti, así que ahora eres el que manda en la ciudad, eh... + +[MOB_17B] +Paul, estás borracho. + +[MOB_17C] +No, imbécil, no estoy borracho. Sólo he tomado un par de copas y alguna que otra invitación, no me he acostado en los dos últimos días, sabes. + +[MOB_17D] +En cualquier caso, no me vengas con esto. No soy tonto. ¿Quién te estableció en esta ciudad? ¿Quién? Yo. Que te enteres. + +[MOB_17F] +¿De verdad? + +[MOB_17G] +No me vengas con esto. Yo te presenté a la gente. Te enseñé cómo hacer bien los trabajos, hice muchas cosas por ti, y así es como me lo pagas. + +[MOB_17H] +Me ignoras. Ni siguieras me das acceso a nada, después de todo lo que hice por ti. ¿Quién te crees que soy? ¿Un retrasado mental o algo así? + +[MOB_17I] +Paul, tranquilízate. He estado muy ocupado, no te comportes como un idiota.. + +[MOB_17J] +No soy un idiota, colega. Eso es lo que decían en el reformatorio. ¿Estás buscando bronca? ¡Porque la vas a tener! + +[MOB_17K] +Tommy, colega. Por favor. ¡Tú eras mi gran esperanza! Por favor, ¡no te rías de mí! + +[MOB_17L] +Paul, vete a dormir un poco, de verdad. + +[MOB_73A] +Tommy, soy Steve. + +[MOB_73B] +Eh, Steve. + +[MOB_73C] +¡Eres un portento! ¡Soy un portento! La gente está loca por nosotros. Vamos a entrar en la historia, amigo. + +[MOB_73D] +Se trata de los premios más importantes. Podré llevar a mi padre a una residencia y no tener que escucharle nunca más. + +[MOB_73E] +Eso está bien, Steve. + +[MOB_73F] +Mejor que bien tío. Mejor, mucho mejor. Nunca creyó en mí. Nunca creyó que pudiera ser un artista y ahora lo he conseguido. + +[MOB_73G] +Soy el mejor director de cine porno de todos los tiempos, amigo. Solo quería decirte que ha sido un placer conocerte. + +[MOB_73H] +Gracias Steve. + +[MOB_73I] +Te quiero tío, No cambies, me oyes. + +[MOB_73J] +Te oigo. Adiós Steve. + +[BOLLOX] +Pulsa ~h~~k~~VEHICLE_HANDBRAKE~ ~w~para soltar una bomba. Pulsa ~h~~k~~VEHICLE_ENTER_EXIT~ ~w~para cancelar. + +[BRID_OP] +El temporal ha pasado, todos los puentes hacia la península están ahora abiertos. + +[BRID_CL] +Alerta de temporal: Cerrados todos los puentes hacia la península. + +[ASSET_C] +¡CLUB POLE POSITION CONSOLIDADO! + +[ASSET_D] +El club Pole Position generará ahora unos ingresos máximos de hasta ~1~ $ al día. ¡Asegúrate de recaudarlos regularmente! + +[ST_WHEE] +Mayor tiempo en caballito (seg) + +[ST_STOP] +Mayor tiempo en parada (seg) + +[ST_2WHE] +Mayor tiempo en dos ruedas (seg) + +[ST_WHED] +Mayor distancia en caballito (m) + +[ST_STOD] +Mayor distancia en parada (m) + +[ST_2WHD] +Mayor distancia en dos ruedas (m) + +[OUTFT11] +Chándal + +[OUTFT12] +Frankie + +[RELOAD] +~g~¡Has conseguido la habilidad de recargar rápidamente! + +[APACHE] +Hunter entregado en el helipuerto de Ocean Beach. + +[CRED369] +JOHN MCCARDLE + +[CRED370] +DAVID MURDOCH + +[CRED371] +CHRIS BROWN + +[CRED372] +PAUL GREEN + +[CRED373] +KYLE MILNE + +[CRED374] +KEVIN YUN + +[CRED375] +ERICK COBBS + +[CRED376] +RANDY BLAKE + +[CRED377] +BRANDON LIM + +[CRED378] +BRANDON FENOL + +[CRED379] +MICHAEL MANOLE + +[CRED380] +ALETHEIA SIMONSON + +[CRED381] +JOHN JANSEN + +[CUNTY] +¡Ropa nueva entregada en la finca de Vercetti! + +[GOODBOY] +¡Prima de 50 $ por comportamiento ejemplar! + +[NEWCONT] +¡Nuevo ~h~punto de contacto ~w~disponible en el puerto deportivo en Ocean Beach! + +[FIRELVL] +Misión de camión de bomberos nivel ~1~ + + +[HELP56] +Pulsa ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~ para ajustar la cámara. + +[HELP57] +Pulsa ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~ para ajustar la cámara. + +[HELP58] +Mientras estás apuntando, puedes pulsar ~h~~k~~PED_CYCLE_TARGET_LEFT~~w~ o ~h~~k~~PED_CYCLE_TARGET_RIGHT~~w~para cambiar de objetivo. + +[HELP59] +Mientras estás apuntando, puedes pulsar ~h~~k~~PED_CYCLE_TARGET_LEFT~~w~ o ~h~~k~~PED_CYCLE_TARGET_RIGHT~~w~para cambiar de objetivo. + +[HELP60] +Si pulsas ~h~~k~~PED_SPRINT~ ~w~ mientras estás robando un coche, no te subirás en él. + +[HELP61] +Ahora dispones de munición ilimitada y el doble de robustez en todos los vehículos. + +[FEC_LB1] +Mirar + +[FEC_LB2] +detrás + +[FEC_LB3] +Mirar detrás + +[FEC_R3] +(botón R3) + +[FEC_PED] +Controles a pie + +[FEC_VEH] +Controles en vehículo + +[FEC_FPR] +Controles en primera persona + +[FEC_CMM] +Controles comunes + +[FEC_PWL] +Ir a la izquierda + +[FEC_PWR] +Ir a la derecha + +[FEC_PWF] +Andar hacia delante + +[FEC_PWT] +Andar hacia la cámara + +[FEC_PLB] +Mirar hacia atrás. + +[FEC_PFR] +Disparar arma + +[FEC_CLE] +Cambiar Arma a la Izq. + +[FEC_CRI] +Cambiar Arma a la Dcha. + +[FEC_LKT] +Bloquear objetivo + +[FEC_PJP] +Salto Peatón + +[FEC_PSP] +Sprint Peatón + +[FEC_PSH] +Disparar Peatón + +[FEC_TLF] +Siguiente objetivo a la izq. + +[FEC_TRG] +Siguiente objetivo a la dcha. + +[FEC_CCM] +Centrar cámara detrás del jugador + +[FEC_SZI] +Acercar vista rifle francotirador + +[FEC_SZO] +Alejar vista rifle francotirador + +[FEC_LKL] +Mirar izq. en primera persona + +[FEC_LRT] +Mirar dcha. en primera persona + +[FEC_LUP] +Mirar arr. en primera persona + +[FEC_LDN] +Mirar abj. en primera persona + +[FEC_LBH] +Mirar hacia atrás en vehículo + +[FEC_LLF] +Mirar a la izq. en vehículo + +[FEC_LRG] +Mirar a la dch. en vehículo + +[FEC_HRN] +Claxon + +[FEC_HBR] +Freno de mano del vehículo + +[FEC_ACL] +Acelerar vehículo + +[FEC_BRK] +Frenar vehículo + +[FEC_TSM] +Misiones secundarias + +[FEC_CRD] +Cambiar emisora de radio + +[FEC_ENT] +Entrar/salir de vehículo + +[FEC_WPN] +Disparar arma + +[FEC_PAS] +Pausa + +[FEC_FPO] +Cambiar armas primera persona + +[FEC_SMS] +Mostrar puntero del ratón + +[FEC_CMS] +Cambiar modo cámara en todas situaciones. + +[FEC_TSS] +Capturar pantalla + +[FEC_DBG] +DEBUG MENU + +[FEC_TGD] +Cambiar mando de juego/depuración + +[FEC_TDO] +Cambiar cámara depuración No + +[FEC_IVH] +Invertir horizontalidad ratón + +[FEC_MSL] +BIR + +[FEC_MSM] +BCR + +[FEC_MSR] +BDR + +[FEC_QUE] +??? + +[FEC_TWO] +Permitidas sólo dos teclas del teclado. + +[FEC_UMS] +Por favor sólo teclas de ratón. + +[FEC_OMS] +Permitida sólo una tecla del ratón. + +[FEC_UJS] +Por favor sólo botones de joystick. + +[FEC_OJS] +Permitido sólo un botón de joystick por acción. + +[FEC_PTL] +Usar Bloquear objetivo al cambiar arma a izq. + +[FEC_PTR] +Usar Bloquear objetivo al cambiar arma a dcha. + +[FEC_LBC] +Usar Mirar a izq. con Mirar a la dcha. + +[FEC_JBO] +JOY ~1~ + +[FEC_WAR] +Aviso + +[FEC_OKK] +Aceptar + +[FEC_DLF] +Error al borrar. + +[FEC_SVU] +Error al guardar. + +[FEC_LUN] +Error al cargar. Archivo dañado, por favor bórralo. + +[FEC_PAD] +Gamepad + +[FEC_JOY] +Joystick + +[FES_CSA] +Seleccionar aspecto de la lista inferior: + +[FET_HRD] +AJUSTES POR DEFECTO RESTAURADOS + +[FET_MST] +CONDUCCIÓN CONTROLADA POR EL RATÓN + +[FEC_STR] +NUM. INICIO + +[FET_MIG] +IZQUIERDA, DERECHA, RUEDA DEL RATÓN PARA AJUSTAR + +[FET_CIG] +RETROCESO PARA QUITAR - BIR, VOLVER A CAMBIAR + +[FET_DSN] +Apariencia predeterminada del jugador.bmp + +[FET_RSO] +RESTAURADO AJUSTE ORIGINAL + +[FET_RSC] +HARDAWARE NO DISPONIBLE. RESTAURADO AJUSTE ORIGINAL + +[FEA_3DH] +HARDWARE DE SONIDO + +[FEA_SPK] +AJUSTE DE ALTAVOCES + +[FEM_LOD] +DIST. DE REPRESENTACIÓN + +[FEM_VSC] +SINCRONÍA DE IMAGEN + +[FEM_FRM] +LIMITADOR DE CUADROS + +[FEM_MM] +MENÚ PRINCIPAL + +[FED_RES] +RESOLUCIÓN DE PANTALLA + +[FET_CTL] +CONFIGURAR MANDO + +[FET_OPT] +OPCIONES + +[FEC_MSH] +SENSIBILIDAD DEL RATÓN + +[FEC_IVV] +INVERTIR VERTICALIDAD RATÓN + +[FEC_FNC] +F~1~ + +[FEC_IRT] +INSERT + +[FEC_DLL] +SUPR + +[FEC_HME] +INICIO + +[FEC_END] +FIN + +[FEC_PGU] +RE PÁG + +[FEC_PGD] +AV PÁG + +[FEC_UPA] +ARRIBA + +[FEC_DWA] +ABAJO + +[FEC_LFA] +IZDA + +[FEC_RFA] +DCHA + +[FEC_NUM] +NUM. + +[FEC_NMN] +NUM. ~1~ + +[FEC_FWS] +NUM. / + +[FEC_PLS] +NUM. + + +[FEC_MIN] +NUM. - + +[FEC_DOT] +NUM. . + +[FEC_NLK] +BLOQ NUM + +[FEC_ETR] +INTRO + +[FEC_SLK] +BLOQ DESPL + +[FEC_PSB] +PAUSA INTER + +[FEC_BSP] +RETROCESO + +[FEC_TAB] +TAB + +[FEC_CLK] +BLOQ MAYÚS + +[FEC_RTN] +INTRO + +[FEC_LSF] +MAYÚS IZQ + +[FEC_RSF] +MAYÚS DCH + +[FEC_LCT] +CTRL I + +[FEC_RCT] +CTRL D + +[FEC_LAL] +ALT I + +[FEC_RAL] +ALT D + +[FEC_LWD] +WIN I + +[FEC_RWD] +WIN D + +[FEC_WRC] +APMENÚ + +[FEC_SPC] +ESPACIO + +[WIN_TTL] +Grand Theft Auto VC + +[WIN_95] +Grand Theft Auto VC no se puede ejecutar bajo W95 + +[WIN_DX] +Grand Theft Auto VC necesita al menos la versión 8.1 de DirectX + +[FET_EIG] +NO SE PUEDE DEFINIR UN CONTROL PARA ESTA ACCIÓN + +[FET_DAM] +MODELADO ACÚSTICO DINÁMICO + +[FEQ_SRE] +¿Seguro que quieres salir? Se perderán todos los progresos desde la última partida guardada. ¿Quieres proceder? + +[FEQ_SRW] +¿Seguro que quieres salir del juego? + +[FET_QG] +SALIR DEL JUEGO + +[FEN_STA] +INICIAR JUEGO + +[FET_PAU] +MENÚ PAUSA + +[REPLAY] +VOLVER A JUGAR + +[FEC_ANS] +Acción + +[CVT_MSG] +Convertir texturas a un formato óptimo para tu tarjeta de vídeo + +[FEC_SFT] +MAYÚS + +[FEH_VMP] +VER MAPA + +[FES_DEE] +¡Error al eliminar! Por favor, inténtalo de nuevo. + +[FES_CMP] +¡Error al guardar! Por favor, inténtalo de nuevo. + +[FESZ_WR] +Guardando partida actual. Por favor, espera... + +[FELD_WR] +Cargando el juego. Por favor, espera... + +[FEDL_WR] +Eliminando partida guardada. Por favor, espera... + +[PCRESRT] +Iniciando nueva partida. Por favor, espera... + +[FET_STI] +Controles estándar + +[FET_CTI] +Controles clásicos + +[FEH_NA] +OPCIÓN NO DISPONIBLE + +[FEH_MPH] +RATÓN, CURSORES PARA MOVERSE - RE. PAG, AV. PAG, RUEDA RATÓN PARA ZOOM, L - LEYENDA + +[FEA_MP3] +REPRODUCTOR MP3 + +[NO_PCCD] +Por favor, introduce tu CD de GTA Vice City o pulsa ESC para cancelar + +[FEH_SSA] +CURSORES PARA MOVERSE - S PARA GUARDAR EN UN ARCHIVO + +[FES_CMI] +ÚLTIMA MISIÓN SUPERADA + +[FET_STS] +ESTADÍSTICAS GUARDADAS EN 'STATS.HTML' + 'STATS.TXT' + +[WIN_VDM] +Grand Theft Auto VC no pudo encontrar suficiente memoria de vídeo disponible + +[FEC_ERI] +¡Error! Una o más acciones de control no están vinculadas a ninguna tecla o botón. Comprueba que todas las acciones de control estén definidas. + +[FEC_TFU] +Torreta + Inclinar arriba + +[FEC_TFD] +Torreta + Inclinar abajo + +[FET_RIG] +ELIGE UN NUEVO CONTROL PARA ESTA ACCIÓN + +[FEA_NM3] +NO SE ENCONTRARON ARCHIVOS MP3 + +[FEA_MPB] +SUBIR VOLUMEN DE MP3 + +[FEA_MUS] +VOLUMEN DE MÚSICA + +[FEA_SFX] +VOLUMEN DE EFECTOS + +[CVT_ERR] +Te has quedado sin espacio en el disco duro. Por favor, antes de seguir consigue algo de espacio en tu disco duro. Pulsa ESC para cancelar. + +[FEA_ADP] +DETECTAR EL HARDWARE AUTOMÁTICAMENTE + +{=================================== MISSION TABLE AMBULAE ===================================} + +[ATUTOR2:AMBULAE] +~g~Lleva a los pacientes al hospital CON CUIDADO. Cada bache reduce sus posibilidades de supervivencia. + +[A_FULL:AMBULAE] +~r~¡Ambulancia llena! + +[A_FAIL2:AMBULAE] +~r~¡Tu falta de urgencia ha sido mortal para el paciente! + +[A_FAIL3:AMBULAE] +~r~¡El paciente está muerto! + +[A_PASS:AMBULAE] +¡Rescatado! + +[A_COMP2:AMBULAE] +¡Nunca te cansarás! + +[A_CANC:AMBULAE] +~r~¡Misión de ATS cancelada! + +[A_COMP3:AMBULAE] +¡Misiones de ATS completadas! ¡Nunca te cansarás cuando estés corriendo! + +[ALEVEL:AMBULAE] +Misión de ATS nivel ~1~ + +[A_FAIL1:AMBULAE] +Misión de ATS terminada. + +[A_SAVES:AMBULAE] +PERSONAS SALVADAS: ~1~ + +[A_COMP1:AMBULAE] +Misiones de ATS completas: ~1~ $ + +{=================================== MISSION TABLE ASSIN1 ===================================} + +[ASM1_5:ASSIN1] +~r~¡Completó sus entregas! + +[ASM1_6:ASSIN1] +Entregas restantes: + +[ASM1_7:ASSIN1] +~g~Carl Pearson, repartidor de pizza. Mátale antes que finalice el reparto. + +[ASM1_A:ASSIN1] +Sr. Teal, su ayuda en erradicar a esos forasteros fue inestimable para el negocio. Tengo más trabajo para usted y más cercano a la ''intervención''. + +[ASM1_D:ASSIN1] +Sr. Teal, su ayuda en erradicar a esos forasteros fue inestimable para el negocio. + +{=================================== MISSION TABLE ASSIN2 ===================================} + +[ASM2_1:ASSIN2] +~g~La Sra. Dawson dejará pronto la joyería de Vice Point. Mátala. Debe parecer un accidente de coche. + +[ASM2_3:ASSIN2] +~g~¡Va a estallar! ¡Largo de aquí! + +[ASM2_4:ASSIN2] +~r~Dañaste su coche ¡y ni siquiera está en él! ¡Ahora no se subirá ahí! + +[ASM2_5:ASSIN2] +~r~¡Se escapó! + +[ASM2_6:ASSIN2] +~r~¡Estabas demasiado cerca de la escena del ''accidente''! + +[ASM2_7:ASSIN2] +~g~¡No utilices armas! ¡Se supone que debe parecer un accidente! ¡En vez de eso, sácala de la carretera! + +[ASM2_8:ASSIN2] +~g~Debes hacer que la muerte de la Sra. Dawson parezca un accidente. No utilices ningún arma. + +[ASM2_9:ASSIN2] +~g~¡Necesitas un coche para este trabajo! + +[ASM2_10:ASSIN2] +~g~Cuando el coche explote en llamas vete lo más lejos posible de la escena del accidente. + +[ASM2_11:ASSIN2] +¡Ayúdame! + +[ASM2_12:ASSIN2] +¡Que alguien me ayude! + +[ASM2_13:ASSIN2] +¡oh, Dios mío! + +[ASM2_A:ASSIN2] +Felicidades por un trabajo bien hecho, Sr. Teal. Mi cliente está muy satisfecho. + +[ASM2_2:ASSIN2] +salud: + +{=================================== MISSION TABLE ASSIN3 ===================================} + +[ASM3_11:ASSIN3] +TIEMPO: + +[ASM3_C:ASSIN3] +Una banda europea planea atracar un banco en Vice City. A mis jefes les gustaría que esto no sucediese. + +[ASM3_D:ASSIN3] +Cada miembro de la banda tiene una tapadera mientras está aquí en Vice City. Algunos tienen trabajos sin importancia y otros están de vacaciones. + +[ASM3_E:ASSIN3] +Cada objetivo y sus posibles paraderos están pegados con cinta adhesiva bajo el teléfono. + +[ASM3_14:ASSIN3] +~g~Dick Tanner está ubicado junto al DBP de Seguridad en Ocean Drive. + +[ASM3_15:ASSIN3] +~g~Marcus Hammond y Franco Carter están ubicados junto a la joyería en Vice Point. + +[ASM3_16:ASSIN3] +~g~Nick Kong está en su barco cerca de Washington Beach. + +[ASM3_18:ASSIN3] +~g~¡No te acerque demasiado al objetivo o te descubrirá y tendrás que perseguirle hasta atraparle! + +[ASM3_19:ASSIN3] +~g~¡Te ha visto! ¡Acaba con él! + +[ASM3_20:ASSIN3] +~g~¡Te han visto! ¡Asegúrate de acabar con los dos! + +[ASM3_21:ASSIN3] +~r~¡No mataste a todos los miembros de la banda a tiempo! + +[ASM3_22:ASSIN3] +~g~¡No te acerques demasiado a los objetivos o te descubrirán e intentarán escapar. + +[ASM3_12:ASSIN3] +~g~Se ha dejado cerca una selección de armas en caso de necesidad. Dispones de ~h~9 MINUTOS ~g~para matar a todos los miembros de la banda. + +[ASM3_13:ASSIN3] +~g~Mike Griffin está trabajando en una valla de publicidad en Washington. + +[ASM3_17:ASSIN3] +~g~Charlie Dilson está dando una vuelta en su moto en Washington. + +{=================================== MISSION TABLE ASSIN4 ===================================} + +[ASM4_10:ASSIN4] +~g~¡Parece que no eres el único que persigue el maletín! ¡Llévalo deprisa a Ammu- Nation! + +[ASM4_12:ASSIN4] +Distancia: + +[ASM4_15:ASSIN4] +~g~Busca el rifle de francotirador hacia tu derecha. + +[ASM4_16:ASSIN4] +~g~Observa a la mujer de la galería, bajará por las escaleras mecánicas y preguntará a alguien la hora. + +[ASM4_17:ASSIN4] +~g~Solamente cuando la conversación haya concluido, mata a la persona con la que habló, pero no la mates a ella. + +[ASM4_18:ASSIN4] +~g~Cuando el objetivo haya muerto, recupera el maletín y llévalo a Ammu-Nation, en el centro. + +[ASM4_19:ASSIN4] +~g~Mantén tu distancia del objetivo, la barra de distancia de la parte superior de la pantalla te indica lo cerca que estás del objetivo. + +[ASM4_20:ASSIN4] +~g~No dejes que se llene o te verá. + +[ASM4_21:ASSIN4] +~g~¡Recoge el maletín! + +[ASM4_22:ASSIN4] +~g~Lleva el maletín a Ammu-Nation, en el centro. + +[ASM4_23:ASSIN4] +~g~¡Te ha visto y se escapa, atrápale y consigue el maletín! + +[ASM4_25:ASSIN4] +~r~¡Has matado a la mujer, idiota! + +[ASM4_26:ASSIN4] +~r~¡El objetivo ha subido a su avión! + +[ASM4_27:ASSIN4] +~r~¡El objetivo te ha visto! ¡Deberías mantener tu distancia! + +[ASM4_28:ASSIN4] +~r~¡El objetivo te ha visto! ¡Te ha escuchado disparar el arma! + +[ASM4_29:ASSIN4] +~r~¡Mátale sólo después que haya hablado con la mujer! + +[ASM4_A:ASSIN4] +Es hora de ir a por el pez gordo, Sr. Teal. Hay un rifle a su derecha, tras la maceta. + +[ASM4_B:ASSIN4] +Observe a la mujer que está en la galería por encima de los mostradores de facturación. Caminará por entre la gente y le preguntará a alguien la hora. + +[ASM4_C:ASSIN4] +Debe matar a esa persona, recoger el maletín y llevarlo al lugar adherido con cinta bajo el teléfono. + +{=================================== MISSION TABLE ASSIN5 ===================================} + +[ASM5_A:ASSIN5] +Un valioso canje va a tener lugar en el tejado de la compañía de helados Cherry Popper. + +[ASM5_B:ASSIN5] +Liquide a todos los implicados, robe el maletín y llévelo al helipuerto del aeropuerto. + +[ASM5_C:ASSIN5] +Hay una puerta a su izquierda que conduce a la parte trasera de la fábrica. + +[ASM5_1:ASSIN5] +~g~Entra al complejo por detrás de la compañía de helados Cherry Popper y dirígete al tejado donde va a tener lugar el trato. + +[ASM5_2:ASSIN5] +~g~Consigue el maletín y llévalo al helipuerto del aeropuerto. + +[ASM5_3:ASSIN5] +~g~¡Lleva el maletín al helipuerto del aeropuerto! + +{=================================== MISSION TABLE BANKJ1 ===================================} + +[WANTED1:BANKJ1] +~g~¡Quítate de encima a los polis y pierde tu nivel de se busca! + +[BJM1_A:BANKJ1] +¡Tommy! ¡Eh, Tommy, mira esto, es genial! ¡He hecho que nos instalen un minibar! + +[BJM1_B:BANKJ1] +Tenemos todo un bar abajo, Ken. + +[BJM1_C:BANKJ1] +Sí, sí, vale. Bueno, tengo la pizarra que pediste. + +[BJM1_D:BANKJ1] +Eso es lo bueno de estudiar derecho: te enseñan a seguir las instrucciones. + +[BJM1_E:BANKJ1] +Necesito un hombre para la caja fuerte. + +[BJM1_F:BANKJ1] +De acuerdo, bueno, déjame pensar... caja fuerte, caja fuerte, caja fuerte, caja fuerte... ¡Lo tengo! ¡Este tipo te va a encantar! + +[BJM1_G:BANKJ1] +Ah, ese idiota no. Está encerrado. + +[BJM1_H:BANKJ1] +¿Encerrado dónde? + +[BJM1_I:BANKJ1] +En la celda de una comisaría esperando el traslado. + +[BJM1_J:BANKJ1] +Creo que está a punto de conseguir la libertad condicional... + +[BJM1_1:BANKJ1] +~g~¡Libera a Cam Jones de la comisaría de policía! + +[BJM1_3:BANKJ1] +~g~Encontrarás algo útil en el vestuario de la comisaría. + +[BJM1_21:BANKJ1] +~g~Puedes encontrar la llave de las celdas en el piso de arriba de la comisaría. + +[BNK1_7:BANKJ1] +¿Cam Jones? + +[BNK1_8:BANKJ1] +¡Te voy a sacar de aquí! + +[BNK1_10:BANKJ1] +Sí, soy yo... + +[BNK1_11:BANKJ1] +¡Lo que tú digas! + +[BNK1_13:BANKJ1] +Voy a hacer un trabajo y tu serás mi revienta cajas fuertes. + +[BNK1_14:BANKJ1] +¡Eso es mejor que pudrirse en una celda! + +[BJM1_22:BANKJ1] +~g~¡Lleva a Cam de vuelta a su casa! + +[BJM1_23:BANKJ1] +~g~¡Necesitas coger la tarjeta llave primero! + +[BNK1_12:BANKJ1] +¡Piérdeles el rastro y llévame a mi casa! + +[BJM1_20:BANKJ1] +¡Guarda el arma o te enfréntate a las consecuencias! + +[BJM1_5:BANKJ1] +¡Sólo personal autorizado más allá de este punto! + +[BJM1_2:BANKJ1] +~r~¡Se supone que tenías que sacar a rastras a Cam, no matarle! + +[BJM1_4:BANKJ1] +¡Está armado! ¡Mátale! + +{=================================== MISSION TABLE BANKJ2 ===================================} + +[BJM2_A:BANKJ2] +Necesitamos a un atracador. ¿Conoces a alguno? + +[BJM2_B:BANKJ2] +Eh, Tommy, Tommy, Tommy, esta mierda te pone a tono, tío. + +[BJM2_C:BANKJ2] +¡Guauuuuu! + +[BJM2_D:BANKJ2] +¡Yo podría ser tu atracador! ¡Manos arriba! ¡Manos arriba! + +[BJM2_E:BANKJ2] +Tú no eres un atracador, tú eres un idiota. + +[BJM2_F:BANKJ2] +Ahora ponte una copa y cállate. + +[BJM2_G:BANKJ2] +Eh. ¡Quítate de mi camino! Sí, sí, sí, eh, eh. + +[BJM2_H:BANKJ2] +¿Cam, tú qué opinas? + +[BJM2_I:BANKJ2] +Bueno, el mejor tirador de esta ciudad es un tipo llamado Cassidy. + +[BJM2_J:BANKJ2] +¿Seguro? + +[BJM2_K:BANKJ2] +Sí. Un militar, o al menos él piensa que lo es. + +[BJM2_L:BANKJ2] +Dudo que estuviese alguna vez en el ejército, pero desde luego sabe manejar las armas. + +[BJM2_M:BANKJ2] +Estará en el campo de tiro. + +[BJM2_2A:BANKJ2] +¿Tú eres Phil Cassidy? + +[BJM2_2B:BANKJ2] +¿Por qué? + +[BJM2_2C:BANKJ2] +Estoy buscando a un hombre que sepa manejar un arma. Pero ahora que te veo, no estoy muy convencido. + +[BJM2_2D:BANKJ2] +Hijo, podría quitarte una mosca de la cabeza de un solo disparo a 100 metros. + +[BJM2_2E:BANKJ2] +Oh, ¿de verdad? + +[BJM2_2F:BANKJ2] +Sí. Aprendí en el ejército. + +[BJM2_2G:BANKJ2] +¿Se suele disparar a las moscas en el ejército? Me alegra no pagar impuestos. + +[BJM2_2H:BANKJ2] +¿Intentas ser gracioso? + +[BJM2_2I:BANKJ2] +~n~ + +[BJM2_2J:BANKJ2] +Vamos a disparar. + +[BJM2_1:BANKJ2] +~g~Ve a Ammu-Nation en el centro de la ciudad y habla con Phil Cassidy. + +[BJM2_3:BANKJ2] +TASA DE IMPACTOS: ~1~% + +[BJM2_4:BANKJ2] +PUNTUACIÓN PRIMERA FASE: ~1~ + +[BJM2_6:BANKJ2] +PUNTUACIÓN SEGUNDA FASE: ~1~ + +[BJM2_7:BANKJ2] +PUNTUACIÓN TOTAL DE DISPARO: ~1~ + +[BJM2_9:BANKJ2] +~g~Ve al punto de inicio de la segunda fase. + +[BJM2_11:BANKJ2] +~r~¡Phil está muerto! + +[BJM2_12:BANKJ2] +~r~¡Uno de los tiradores está muerto! + +[BJM2_14:BANKJ2] +~g~¡Pasa a la siguiente fase! + +[BJM2_15:BANKJ2] +PUNTUACIÓN: + +[BJM2_17:BANKJ2] +~g~Ve y habla con Phil. + +[BJM2_18:BANKJ2] +PUNTUACIÓN A SUPERAR: + +[BJM2_19:BANKJ2] +~g~¡Acierta a tantos blancos como puedas dentro del límite de tiempo! + +[BJM2_22:BANKJ2] +~r~¡Has abandonado el campo de tiro! + +[BJM2_23:BANKJ2] +~g~Si abandonas el campo de tiro durante la competición, fracasarás en la misión. + +[BJM2_24:BANKJ2] +~g~El objetivo más cercano vale un punto. + +[BJM2_25:BANKJ2] +~g~El objetivo del medio vale dos puntos. + +[BJM2_27:BANKJ2] +~g~Todos los objetivos de esta ronda valen un punto. + +[BNK2_2:BANKJ2] +APUNTA, 3, 2, 1... ¡FUEGO! + +[BNK2_3:BANKJ2] +¡ZONA DESPEJADA! + +[BNK2_4:BANKJ2] +¡Yupiiiiiii! + +[BNK2_5:BANKJ2] +¡No podrías darle ni a un elefante a 2 metros! + +[BNK2_7:BANKJ2] +Entonces, ¿quieres hacerme un favor y ayudarme a preparar un atraco? + +[BNK2_8:BANKJ2] +Hijo, disparando de ese modo, si me pidieras que sea tu mujer, te diría que sí. + +[BNK2_9A:BANKJ2] +Hijo, mejor será que te vayas y te metas tus ideas y palabrería donde te quepan. Eres una mierda disparando. + +[BNK2_9B:BANKJ2] +Eres una mierda disparando. + +[BJM2_28:BANKJ2] +PUNTUACIÓN RONDA TRES: ~1~ + +[BJM2_20:BANKJ2] +~g~¡La ronda se acabará cuando te quedes sin ~w~tiempo ~g~o ~w~munición~g~! + +[BJM2_26:BANKJ2] +~g~El objetivo más lejano vale tres puntos. + +[BNK2_1:BANKJ2] +MUNICIÓN CARGADA + +[RANGE_1:BANKJ2] +PUNTUACIÓN POR DISPARO: ~1~ + +[BJM2_2:BANKJ2] +~g~Para abandonar la ronda, pulsa ~h~~k~~PED_JUMPING~. + +[BJM2_N:BANKJ2] +Tranqui. + +{=================================== MISSION TABLE BANKJ3 ===================================} + +[BJM3_A:BANKJ3] +Las cosas empiezan a ir bien. + +[BJM3_B:BANKJ3] +¿Cuál es el plan, Tommy? ¿Qué pasa, amigo? + +[BJM3_C:BANKJ3] +El plan es que tú sigas haciendo el imbécil. En fin, necesitamos a un conductor. + +[BJM3_D:BANKJ3] +Tommy, yo lo haré. Sé conducir. + +[BJM3_E:BANKJ3] +Necesitamos a Hilary, señor, no a un picapleitos charlatán. + +[BJM3_F:BANKJ3] +Hilary es nuestra mejor opción. Seguro que no has visto nunca conducir a nadie tan rápido. Voy a darle un toque. + +[BJM3_G:BANKJ3] +Eh, Hil, soy Phil. ¿Cómo te va? No, no hables. Recordaremos los viejos tiempos más tarde. ¿Quieres hacerme un favor? + +[BJM3_H:BANKJ3] +Tengo a un tipo del norte... No, no, no creo que sirviese en el ejército, pero quiere un conductor. + +[BJM3_I:BANKJ3] +Por un poco de acción. De acuerdo, comprendo. + +[BJM3_J:BANKJ3] +¿Qué dijo? + +[BJM3_K:BANKJ3] +Bueno, lo hará, no hay problema. Bueno, podría haber un pequeño problema... Verás, tiene miedo al abandono. + +[BJM3_L:BANKJ3] +Y parece que no trabajará para nadie que no pueda derrotarle. Tiene algo que ver con su madre o algo así. + +[BJM3_M:BANKJ3] +De todas formas primero quiere echarte una carrera, dice que se encontrará contigo fuera... + +[BJM3_2A:BANKJ3] +¿Eres Tommy? Por supuesto que eres Tommy, quiero decir, + +[BJM3_2B:BANKJ3] +¿quién más querría hablar conmigo? + +[BJM3_2C:BANKJ3] +Vale. Esto es lo que hay. + +[BJM3_2D:BANKJ3] +Conduciré para ti solamente si me demuestras que conduces bien. + +[BJM3_2E:BANKJ3] +Déjame tirado, y nunca te perdonaré. + +[BJM3_2:BANKJ3] +~r~¡Hilary está muerto! + +[BJM3_4:BANKJ3] +~g~¡Necesitas un coche para participar! + +[BNK3_1:BANKJ3] +Vale. Te llevaré pero por favor, machácame un poco. + +[BNK3_3A:BANKJ3] +Carrera callejera ilegal en progreso en Vice Point. + +[BNK3_3B:BANKJ3] +Llamando a todos los agentes. + +[BNK3_3C:BANKJ3] +Corredores callejeros, ¡esto es ilegal y no está permitido! + +{=================================== MISSION TABLE BANKJ4 ===================================} + +[BNK4_A:BANKJ4] +~w~Como podéis ver, caballeros, éste va a ser el dinero más fácil que hayamos ganado jamás. + +[BNK4_B:BANKJ4] +~w~Tommy, de verdad, tienes que plantearte seriamente el meterte en la abogacía. + +[BNK4_C:BANKJ4] +~w~Pero, ¿qué coño te estás fumando, tío? ¡Este no es un plan sencillo! + +[BNK4_D:BANKJ4] +~w~Bueno, de todas formas, ¿quién necesita un plan sencillo? + +[BNK4_E:BANKJ4] +~w~Mira el comunismo, bien, pues ese era un plan sencillo. Y jodió bien a Rusia ¿no? + +[BNK4_F:BANKJ4] +~w~Cálmate, ¿vale? Con un equipo como éste, no va a haber problemas. + +[BNK4_G:BANKJ4] +~w~Tenemos a Cam en la caja fuerte. ¿Phil? Tú y yo nos encargaremos de la seguridad y Hilary conducirá el coche de huída. + +[BNK4_H:BANKJ4] +~w~Uh, je, je, ¿no te olvidas de alguien? ¿Alguien que te ayudó siempre en esta ciudad? ¿Alguien...? + +[BNK4_I:BANKJ4] +~w~Ken... Ken, es cierto. Aquí Ken, él nos lavará el dinero y mantendrá las bebidas frías. + +[BNK4_J:BANKJ4] +~w~No comprendo qué se supone que debo hacer aquí. + +[BNK4_K:BANKJ4] +~w~Mira, es sencillo. ¿No has visto nunca una película? + +[BNK4_L:BANKJ4] +~w~Entramos en el banco, enseñamos las armas y nos marchamos siendo hombres muy ricos. + +[P_DEAD:BANKJ4] +~r~¡Phil está muerto! + +[C_DEAD:BANKJ4] +~r~¡Cam está muerto! + +[H_DEAD:BANKJ4] +~r~¡Hilary está muerto! + +[P_HIND:BANKJ4] +~r~¡Has perdido a Phil! + +[C_HIND:BANKJ4] +~r~¡Cam se ha quedado atrás! + +[H_HIND:BANKJ4] +~r~¡Hilary ha sido abandonado! + +[GETCAR:BANKJ4] +~g~¡Sube en el coche de escapada para hacer el trabajito del banco! + +[TRASHED:BANKJ4] +~r~¡DESTROZASTE EL COCHE DE FUGA! + +[BNK4_1:BANKJ4] +Yo conduciré. + +[BNK4_2:BANKJ4] +Genial. Un pasajero. Espera a que les hable de esto al grupo. + +[BNK4_3A:BANKJ4] +¡Eh, cuidado con las ruedas, Tommy! + +[BNK4_3B:BANKJ4] +¡Tommy, Hilary está ocupando mucho espacio! + +[BNK4_3C:BANKJ4] +¡No es verdad! + +[BNK4_3D:BANKJ4] +¡Sí! + +[BNK4_3E:BANKJ4] +Eh, callaos los dos o iréis andando. + +[BNK4_3F:BANKJ4] +Sí, Hilary. + +[BNK4_3I:BANKJ4] +Por el amor de Dios, Phil, deja de agitar esa cosa. + +[BNK4_3J:BANKJ4] +¡Sí, le sacarás un ojo a alguien! + +[BNK4_3M:BANKJ4] +¡Mi pequeña! ¡Está hecha un desastre! + +[BNK4_3O:BANKJ4] +Te aferras a la ilusión de una permanencia perpetua... + +[BNK4_3P:BANKJ4] +¿Qué? + +[BNK4_3Q:BANKJ4] +Crees que todas las cosas durarán. + +[BNK4_3R:BANKJ4] +La juventud, los seres queridos, la pizza... + +[BNK4_3S:BANKJ4] +Todo pasará o terminará y debes aceptar eso. + +[BNK4_3T:BANKJ4] +Sí, tienes razón. Gracias, Cam. + +[BNK4_3U:BANKJ4] +No hay de que. + +[BNK4_3V:BANKJ4] +Eh, Tommy, ¿por qué nos hemos detenido? + +[BNK4_4A:BANKJ4] +~w~Hilary, sigue conduciendo en torno a la manzana. + +[BNK4_5:BANKJ4] +~w~De acuerdo, Tommy, de acuerdo. + +[BNK4_6:BANKJ4] +~w~¡ESTO ES UN ATRACO! + +[BNK4_7:BANKJ4] +~w~¡QUE NADIE SE MUEVA! + +[BNK4_8:BANKJ4] +~w~¡TODO EL MUNDO CONTRA ESA PARED! + +[BNK4_9:BANKJ4] +¡Phil, quédate vigilando! + +[BNK4_10:BANKJ4] +¡No problemo! + +[BNK4_11:BANKJ4] +Vamos, Cam, la cámara acorazada está arriba... + +[BK4_12A:BANKJ4] +¡Maldición! ¡Es una Flange 9000! + +[BK4_12B:BANKJ4] +Podría tardar horas en abrirla, + +[BK4_12C:BANKJ4] +o cinco minutos si encuentras al director. + +[BNK4_13:BANKJ4] +Iré a averiguar en qué agujero se ha metido. + +[BK4_14A:BANKJ4] +Phil, ¿van bien las cosas? + +[BNK4_15:BANKJ4] +Sí. Todo está muy tranquilo. + +[BNK4_16:BANKJ4] +¡Tú, ven conmigo! + +[BNK4_17:BANKJ4] +¡De acuerdo! ¡De acuerdo! ¡Por favor, no dispare! + +[BNK4_18:BANKJ4] +¡DIJE QUE NO SE MOVIESE NADIE! + +[BK4_19A:BANKJ4] +Tiene un cierre temporizado. + +[BK4_19B:BANKJ4] +¡Podríais rendiros ahora mismo! + +[BK4_20A:BANKJ4] +Diablos, puedo hacer un puente en el cierre temporizado, + +[BK4_20B:BANKJ4] +¡Así que solo necesitaremos tu código de acceso y ya está! + +[BNK4_21:BANKJ4] +Quédate aquí. Intenta algo y serás comida para los peces. ¿Lo captas? + +[BNK422A:BANKJ4] +Cam, ¿cuánto tiempo? + +[BK4_23A:BANKJ4] +¡Dame cinco minutos! + +[BK4_24A:BANKJ4] +Voy a comprobar qué tal va Phil, volveré. + +[BK4_24B:BANKJ4] +¡Te dije que no tocases esa alarma! + +[BNK4_25:BANKJ4] +¡Los de operaciones especiales estarán aquí en cualquier momento! + +[BNK4_27:BANKJ4] +¡Me vendría bien un poco de ayuda aquí, Tommy! + +[BNK4_28:BANKJ4] +¡SWAT de Vice City! ¡Estáis rodeados! + +[BNK4_29:BANKJ4] +¿Rodeados? + +[BNK4_30:BANKJ4] +La están cagando, ¡bastardos corruptos! + +[BK4_31A:BANKJ4] +¡Tommy! ¡La cámara acorazada está abierta! + +[BK4_34A:BANKJ4] +De acuerdo, tenemos el fondo de pensiones de los SWAT. ¡Salgamos de aquí! + +[BK4_34B:BANKJ4] +¡De acuerdo, vosotros lo habéis pedido! ¡Habéis tenido vuestra última oportunidad! + +[BK4_35A:BANKJ4] +¡Van a arrasar el lugar! + +[BK4_35B:BANKJ4] +¡A cubierto! + +[BNK4_94:BANKJ4] +~w~De acuerdo chicos. Ha sido tan fácil como lo habíamos planeado. + +[BM_DEAD:BANKJ4] +~r~¡Necesitas al director del banco vivo! + +[ASSET_A:BANKJ4] +¡ATRACO AL BANCO COMPLETADO! + +[ASSET_B:BANKJ4] +~g~El Club Malibú generará ahora unos ingresos máximos de hasta ~1~ $ al día. ¡Asegúrate de recaudarlos regularmente! + +[IDIOT:BANKJ4] +~r~ Eso es, ve por ahí vestido como un chiflado y llamando la atención, ¡IDIOTA! + +{=================================== MISSION TABLE BARON1 ===================================} + +[COK1_A:BARON1] +Vamos nena, vamos, sí, ¡sí! + +[COK1_B:BARON1] +¡Estúpido caballo! ¡Te cortaré la cabeza! + +[COK1_C:BARON1] +¿Quién es este gilipollas? + +[COK1_D:BARON1] +Tommy Vercetti, ¿me recuerdas? + +[COK1_E:BARON1] +Perdóname, estoy un poco alterado. ¡Nunca te fíes de un maldito caballo! + +[COK1_F:BARON1] +Hiciste un buen trabajo... ahora trabajas para mí. + +[COK1_H:BARON1] +Como dije amigo, trabajas para mí. Ahora, cállate. Algún Judas me ha traicionado. + +[COK1_I:BARON1] +Él cree que no sé cuando dinero debería sacar con esto, pero robar un 3% es tan malo como robar el 100%. + +[COK1_J:BARON1] +Nadie me hace esto a mí. ¡NADIE! + +[COK1_K:BARON1] +¡Le sigues desde su apartamento y ves a dónde va! Más tarde, le mataremos. + +[COK1_1:BARON1] +¡Mierda! + +[COK1_2:BARON1] +¡Demasiado lento, abuelo! + +[COK1_4:BARON1] +Perdedor. + +[COK1_5:BARON1] +¡Mejor sigue corriendo, gilipollas! + +[COK1_8:BARON1] +~g~¡Rápido! ¡Consigue un vehículo y síguele! + +[COK1_9:BARON1] +~r~¡Se supone que debes seguirle, no matarle! + +[COK1_10:BARON1] +~r~Ve a la casa del ladrón y descubre dónde esconde el dinero. + +[COK1_11:BARON1] +~g~Echa un vistazo por su ventana. + +[COK1_7:BARON1] +~g~¡Se ha escapado por el tejado, ve tras él pero no le mates! + +[COK1_G:BARON1] +Trabajo por dinero. + +{=================================== MISSION TABLE BARON2 ===================================} + +[COK2_A:BARON2] +¿Qué clase de tonto incompetente eres? + +[COK2_B:BARON2] +¡TONTO! ¡TONTO! ¡TONTO! ¡TONTO! + +[COK2_C:BARON2] +Tommy... + +[COK2_D:BARON2] +¿Qué pasa, Ricardo? + +[COK2_E:BARON2] +Estos idiotas... Siempre intentan joderle a uno. + +[COK2_F:BARON2] +Ese es el problema de este negocio. + +[COK2_G:BARON2] +¿Qué crees que estás haciendo? + +[COK2_H:BARON2] +Estos mamones me han fallado miserablemente. + +[COK2_I:BARON2] +Pronto cualquier madre o padre creerán que pueden vender farlopa en Vice City. + +[COK2_J:BARON2] +¿Y ahora qué, eh? ¿La apestosa Mafia? + +[COK2_K:BARON2] +Ese antro de mafiosos es una fortaleza inexpugnable desde el suelo, + +[COK2_L:BARON2] +así que Quentin... ¡Quentin! ¡QUENTIN! + +[COK2_M:BARON2] +¡Volará sobre la zona! + +[COK2_N:BARON2] +¡Hacedles desaparecer! + +[COK2_O:BARON2] +¿Qué crees que estás haciendo? + +[COK2_P:BARON2] +¿Qué haces aquí? + +[COK2_Q:BARON2] +He estado haciendo preguntas por ahí, y es obvio + +[COK2_R:BARON2] +que Diaz se saltó el trato y se cargó a mi hermano. + +[COK2_S:BARON2] +¡Y te matará a ti también! + +[COK2_T:BARON2] +¡Puedo encargarme de Diaz! + +[COK2_U:BARON2] +No. ¡Escúchame! Yo me encargaré de Diaz... + +[COK2_V:BARON2] +Está empezando a confiar en mí. + +[COK2_1:BARON2] +Hay una cosa que me mosquea... ¿por qué Quentin? + +[COK2_2:BARON2] +No sé, siempre me ha gustado... Quentin Vance... + +[COK2_3:BARON2] +¿Vance? ¿Tu nombre es Lance Vance? + +[COK2_4:BARON2] +¡Eh! ¡Ya tuve bastante con eso en la escuela! + +[COK2_5:BARON2] +¿Alguna vez has disparado uno de esos desde un helicóptero? + +[COK2_8:BARON2] +En fin, ¿a dónde nos dirigimos? + +[COK2_9:BARON2] +A Prawn Island. + +[COK2_13:BARON2] +Lance Vance. Pobre desgraciado. + +[COK2_14:BARON2] +Bien, casi hemos llegado. + +[COK2_15:BARON2] +Daremos un par de pasadas. + +[COK2_16:BARON2] +Así que elimina a tantos como puedas. + +[COK2_17:BARON2] +Después te dejaré abajo y te las arreglarás solo. + +[COK2_20:BARON2] +¡Mierda! ¡Ésto es una zona de guerra! ¡Elimina a algunos de esos pistoleros! + +[COK2_21:BARON2] +¡Nos están alcanzando, tío! + +[COK2_22:BARON2] +¡Arreglar esta cosa no sale barato! ¡Elimínalos! + +[COK2_23:BARON2] +Vale, de ahora en adelante es cosa tuya... ¡Buena suerte, hermano! + +[COK2_24:BARON2] +Helicóptero: + +[COK2_25:BARON2] +~g~Ve y recoge el dinero del tejado. + +[COK2_27:BARON2] +¡Estás en mi territorio, gilipollas! + +[COK2_28:BARON2] +¡Vas a desaparecer! + +[COK2_6:BARON2] +No. Practicaré un poco por el camino. + +[OPEN_B:BARON2] +Los controles policiales con dirección a la península han sido levantados + +{=================================== MISSION TABLE BARON3 ===================================} + +[COK3_A:BARON3] +Ahora ya no os sentís tan complacidas, ¿eh? + +[COK3_B:BARON3] +~n~ + +[COK3_C:BARON3] +¡Guau! Mira hacia donde mueves esa cosa. + +[COK3_D:BARON3] +Ya no habrá más mierda de paloma en mi coche, ¿eh, Tommy? + +[COK3_E:BARON3] +Supongo que no. + +[COK3_F:BARON3] +Tienes toda la razón. Ahora escucha, + +[COK3_G:BARON3] +¿sabes quién posee el barco más rápido de la costa este? + +[COK3_H:BARON3] +Ni idea. + +[COK3_I:BARON3] +Yo. Y quiero que siga siendo así. + +[COK3_J:BARON3] +Cada traficante de aquí a Caracas tiene un sueño, un barco más rápido. + +[COK3_K:BARON3] +Se rumorea que el astillero acaba de terminar un barco así + +[COK3_L:BARON3] +para algún mamón costarricense. + +[COK3_M:BARON3] +Y Tommy... ¡QUIERO ESE BARCO! + +[COK3_N:BARON3] +Creo que han vuelto tus palomas. + +[COK3_O:BARON3] +¡Ah! Pensé que te tenía. ¿De dónde has salido? + +[COK3_P:BARON3] +¡Palomas! ¡Boom! ¡Aaaaah! + +[COK3_5:BARON3] +~g~Localiza el interruptor para bajar el barco. + +[COK3_6:BARON3] +~g~Lleva el barco a la mansión. + +[COK3_7:BARON3] +~r~¡Destruiste el barco! + +[COK3_8:BARON3] +~g~Ve al astillero, en los muelles y roba el barco más rápido. + +[COK3_9:BARON3] +~g~Ahora sube al barco. + +{=================================== MISSION TABLE BARON4 ===================================} + +[COK4_A:BARON4] +¡Expúlsala! ¡MIERDA DE PLÁSTICO! + +[COK4_B:BARON4] +¿Por qué me haces esto? + +[COK4_C:BARON4] +¿Quién te crees que eres, maldita mierda de plástico? + +[COK4_D:BARON4] +¡QUE TE JODAN! + +[COK4_E:BARON4] +Se traga mi película favorita de El burro, ¡se muere! + +[COK4_F:BARON4] +¿Qué más podría haber hecho? + +[COK4_G:BARON4] +Probablemente no estaba enchufado. + +[COK4_H:BARON4] +¿Qué? + +[COK4_I:BARON4] +Maldición... No importa, puedo comprar cien más. + +[COK4_J:BARON4] +Ahora, Tommy, + +[COK4_K:BARON4] +cada mes un camello autónomo navega hasta Vice City y amarra su yate. + +[COK4_L:BARON4] +Vende su partida al primer barco. + +[COK4_M:BARON4] +Quiero que cojas la lancha motora + +[COK4_N:BARON4] +y que elimines a todos los otros cabrones, + +[COK4_O:BARON4] +luego me traes aquí el alijo, ¿entendido? + +[COK4_P:BARON4] +Déjame adivinar, pensaste que me podía venir bien un ángel guardián. + +[COK4_Q:BARON4] +Solo digo que tienes que dejarme participar, tío. + +[COK4_R:BARON4] +Ahora ya puedes contarme toda esa basura del 'chico duro solitario', + +[COK4_S:BARON4] +pero sé que un día te voy a salvar el culo + +[COK4_T:BARON4] +¡y probablemente querrás darme un beso! + +[COK4_U:BARON4] +Colgado. + +[COK4_V:BARON4] +~n~ + +[COK4_1:BARON4] +Así que Tommy, sabemos que fue Díaz quien jodió nuestro trato... + +[COK4_3:BARON4] +Así que, ¿por qué coño vamos por ahí haciéndole recados? + +[COK4_4:BARON4] +Cuanto más aprendamos ahora, ¡menos tendremos que aprender cuando tomemos el control de esta ciudad! + +[COK4_5:BARON4] +Me encanta tu estilo, tío. Muy fresco. + +[COK4_12:BARON4] +¡Cuidado, vienen de todas partes! + +[COK4_13:BARON4] +Ya está. ¡Dirígete hacia Díaz tan rápido como puedas! + +[COK4_14:BARON4] +¿Queréis un poco de esto? + +[COK4_15:BARON4] +¡A dormir con los peces! + +[COK4_16:BARON4] +¡Comed! ¡COMED! + +[COK4_19:BARON4] +¡Más problemas ahí adelante! + +[COK4_20:BARON4] +¡Hay más pistoleros en el malecón! + +[COK4_24:BARON4] +Buen disparo, amigo mío. Eres un verdadero y auténtico lunático de categoría A. + +[COK4_25:BARON4] +Bueno, gracias. + +[COK4_26:BARON4] +Nos vemos, Tommy. + +[COK4_27:BARON4] +Vale, Sr. Lance Vance. + +[COK4_28:BARON4] +~g~¡Ve hasta el yate antes de que lleguen los otros barcos! + +[COK4_31:BARON4] +~g~¡Ve al barco más rápido del embarcadero! + +[COK4_32:BARON4] +~r~¡Demasiado lento! + +[COK4_33:BARON4] +~r~¡Has destruido el barco! + +[COK4_34:BARON4] +~g~¡Hunde esos barcos! + +[COK4_35:BARON4] +Estado del barco: + +{=================================== MISSION TABLE BARON5 ===================================} + +[PROP_A:BARON5] +¡PROPIEDAD ADQUIRIDA! + +[COK4_30:BARON5] +~r~¡Lance está muerto! + +[ASS1_A:BARON5] +He conseguido unos juguetes nuevos. Están en el maletero. + +[ASS1_B:BARON5] +¡Mierda! ¿Dónde conseguiste todo este material? + +[ASS1_C:BARON5] +Lo he estado guardando para un momento como éste. + +[ASS1_D:BARON5] +¿Te gusta? + +[ASS1_E:BARON5] +Sí, me gusta. + +[ASS1_F:BARON5] +Estúpidos mamones... + +[ASS1_G:BARON5] +Mi hermosa casa. + +[ASS1_H:BARON5] +¡Mirad lo que le habéis hecho! + +[ASS1_I:BARON5] +¡Esto es por mi hermano! + +[ASS1_J:BARON5] +Confiaba en ti, Tommy. + +[ASS1_K:BARON5] +Te hubiese convertido en... + +[ASS1_L:BARON5] +Diga buenas noches, Sr. Díaz. + +[ASS1_1:BARON5] +Este lugar va a estar hasta arriba de capullos... ten cuidado... + +[ASS1_2:BARON5] +No te preocupes Tommy, te cubro. + +[ASS1_13:BARON5] +¡DÍAZ! ¡He venido a encargarme de tu negocio! + +[ASS1_14:BARON5] +¡TOMMY! Me has traicionado... ¡Imbécil! Voy a acabar contigo muy pronto... + +[ASS1_15:BARON5] +~g~¡Arrasa esa mansión y mata a Diaz! + +[ASS1_16:BARON5] +~g~¡Mata a Diaz! + +[ASS1_17:BARON5] +~g~Hay múltiples caminos para entrar en la mansión. + +[BUD1:BARON5] +Lance + +[ASS1_18:BARON5] +~g~La puerta está cerrada con llave, intenta otra ruta. + +[ASS1_19:BARON5] +¡Por aquí! + +[ASS1_20:BARON5] +¡Tommy, mi problema es con Quentin, no contigo, tío! + +{=================================== MISSION TABLE BIKE1 ===================================} + +[BM1_A:BIKE1] +¿Dónde está Baker? + +[BM1_B:BIKE1] +Estoy buscando al gran Mitch Baker... + +[BM1_C:BIKE1] +¿Quién le busca? + +[BM1_D:BIKE1] +Tommy Vercetti. + +[BM1_E:BIKE1] +Vercetti. + +[BM1_F:BIKE1] +No pareces policía, así que eso te concede un minuto. + +[BM1_G:BIKE1] +Habla rápido. + +[BM1_H:BIKE1] +Kent Paul dijo que podrías estar interesado en llevar la seguridad de una gira que él ha organizado. + +[BM1_I:BIKE1] +¿Kent Paul? No me extraña que te enviase en su lugar. + +[BM1_J:BIKE1] +La última vez que estuvo aquí, se marchó por la ventana sin nada, tal cual le trajo su madre al mundo. + +[BM1_K:BIKE1] +¿Estás interesado o no? + +[BM1_L:BIKE1] +Sólo hacemos favores a los nuestros. + +[BM1_M:BIKE1] +¿Cómo puedo unirme a vosotros? + +[BM1_N:BIKE1] +Este no es un club de campo, chico. ¿Sabes montar en moto? + +[BM1_O:BIKE1] +¿Sabes sentarte en un taburete y beber? + +[BM1_P:BIKE1] +Cougar, Zeppelin, id a ver cómo se le da la moto a esta nenaza... + +[BM1_2:BIKE1] +~g~¡Necesitas una Freeway o una Ángel para competir! + +[BM1_3:BIKE1] +~r~¡Los corredores han sido atacados! + +[BIKE1_1:BIKE1] +Bien, modelitos caros. Veamos qué puedes hacer. + +[BM1_1:BIKE1] +~g~Consigue una Freeway o una Ángel y ve a la parrilla de salida. + +{=================================== MISSION TABLE BIKE2 ===================================} + +[BM2_1:BIKE2] +CAOSMETRO: + +[BM2_A:BIKE2] +Te he vuelto a ganar. + +[BM2_B:BIKE2] +Eh, Vercetti. + +[BM2_C:BIKE2] +Cougar dice que sabes manejar muy bien una moto. + +[BM2_D:BIKE2] +Sí, ¿cuántos recados más voy a tener que hacer? + +[BM2_E:BIKE2] +Soy un hombre muy ocupado. + +[BM2_F:BIKE2] +Si es un combate lo que va arreglarlo todo, entonces adelante. + +[BM2_G:BIKE2] +Ser uno de nosotros no va solo de fanfarronear. Va de ser parte de una familia. + +[BM2_H:BIKE2] +Sí, he sido parte de una familia antes. No funcionó. + +[BM2_I:BIKE2] +Sí, seguro, pero esta familia cuida de los suyos. + +[BM2_J:BIKE2] +No le pedimos a un hombre que haga el trabajo sucio y luego le dejamos pasar quince años de sequía. + +[BM2_K:BIKE2] +Sí, es verdad. He hecho mis deberes. + +[BM2_L:BIKE2] +Ésta de aquí es la familia más grande de inadaptados, rebeldes y tipos duros. + +[BM2_M:BIKE2] +Diablos, algunos de nosotros incluso hemos sido traicionados por nuestro propio país. + +[BM2_N:BIKE2] +Estuve preso durante Vietnam. Un tema desagradable. + +[BM2_O:BIKE2] +Por eso es por lo que voy a pedirte que vayas a armar jaleo. + +[BM2_P:BIKE2] +Este maldito país necesita una patada en el culo, y nosotros se la vamos a dar. + +[BM2_Q:BIKE2] +¡Así que sal de ahí, pilla una moto, demuestra a esta ciudad lo cabreado que estás! + +[BM2_R:BIKE2] +De acuerdo. + +[BM2_2:BIKE2] +~g~¡Debes llenar el Caosmetro en el tiempo determinado para demostrarnos lo duro que eres! + +[BM2_3:BIKE2] +~g~Este sonido indica que has llenado una parte del contador, continúa así. + +[BM2_4:BIKE2] +~r~¡Has fracasado en llenar el Caosmetro a tiempo! + +{=================================== MISSION TABLE BIKE3 ===================================} + +[BM3_A:BIKE3] +Hola, Mitch. + +[BM3_B:BIKE3] +Bueno, si es el ''tipo duro'' Vercetti. + +[BM3_C:BIKE3] +Ahora quiero ver lo bien que sabes luchar por tu territorio. + +[BM3_D:BIKE3] +Una banda callejera local cometió el error de robar mi moto, + +[BM3_E:BIKE3] +probablemente para hacerse los machos o algo así. + +[BM3_F:BIKE3] +Los chicos y yo iríamos a darles una lección sobre el respeto a las propiedades ajenas y todo eso. + +[BM3_G:BIKE3] +En fin... + +[BM3_H:BIKE3] +Entonces empecé a pensar que esto sería una buena iniciación para ti. + +[BM3_I:BIKE3] +Devuélveme mi moto y puedes decirle a Paul que tiene su seguridad. + +[BM3_2:BIKE3] +~r~¡Se suponía que debías traer de vuelta la moto, no destruirla! + +[BM3_3:BIKE3] +~g~¡Lleva la moto de vuelta al bar! + +[BM3_4:BIKE3] +~g~¡Sube a la moto! + +[INTRUDE:BIKE3] +~g~¡Te han visto! + +[BM3_6:BIKE3] +~g~Están refugiados detrás de Ammu-Nation en la zona del centro. + +[BM3_7:BIKE3] +~g~Necesitarás una moto rápida para conseguir acceder al tejado. + +[BM3_8:BIKE3] +~g~Utiliza la moto para saltar desde esas escaleras hasta el tejado que hay en el extremo opuesto de la carretera. + +[BM3_1:BIKE3] +~g~Una banda local ha robado la Ángel de Mitch Baker. ¡Recupérala! + +[BM3_9:BIKE3] +~g~¡Recupera la Ángel de Mitch y sal de ahí! + +{=================================== MISSION TABLE BMX_1 ===================================} + +[GETBIK2:BMX_1] +¡Tienes ~1~ segundos para subirte a una moto de motocross! + +{=================================== MISSION TABLE BOATBUY ===================================} + +[DRUG_1:BOATBUY] +¿Hola? + +[DRUG_2:BOATBUY] +Apágalo. Hay un tipo ahí afuera. + +[DRUG_3:BOATBUY] +¡Eh, el tipo del traje! Supongo que eres el nuevo propietario. + +[DRUG_4:BOATBUY] +Sí. ¿Cuál de los barcos es el más rápido? + +[DRUG_5:BOATBUY] +Ya está en el agua, tío, + +[DRUG_6:BOATBUY] +Pensé que querrías probarlo. + +[DRUG_7:BOATBUY] +Tío, funciona con un motor de 300 caballos... + +[DRUG_8:BOATBUY] +y el casco es de fibra de vidrio, ¡sale disparado por las olas! + +[DRUG_9:BOATBUY] +Puede ponerse de cero a noventa tan sólo en cuatro segundos, tío... + +[DRUG_10:BOATBUY] +¡y puede llevar como veinte contenedores de la mejor hierba jamaicana justo en el casco! + +[DRUG_11:BOATBUY] +Así que adelante, tío, ¡está preparada para volar! + +[DRUG_12:BOATBUY] +Tipo trajeado, ¿tienes fuego? + +[DRUG_13:BOATBUY] +¿Colega? + +{=================================== MISSION TABLE CAP_1 ===================================} + +[CAP1_B1:CAP_1] +~g~La mafia está imponiendo impuestos a tus negocios. Encuéntrales y mátales. + +[CAP1_B2:CAP_1] +~g~¡La mafia ha saqueado el astillero! + +[CAP1_B3:CAP_1] +~g~¡La mafia ha saqueado la fábrica de helados! + +[CAP1_B4:CAP_1] +~g~¡La mafia ha saqueado el salón de autos! + +[CAP1_B5:CAP_1] +~g~¡La mafia ha saqueado la compañía de taxis! + +[CAP_01:CAP_1] +¿Cuál es la emergencia? + +[CAP_02:CAP_1] +¿QUIÉN? + +[CAP_03:CAP_1] +Tommy... unos matones mafiosos... dijeron que vendrían a recoger su parte... + +[CAP_04:CAP_1] +Dijeron que era dinero del Sr. Forello... me siento una basura. + +[CAP_05:CAP_1] +¿Forelli? ¿SONNY Forelli? + +[CAP_06:CAP_1] +Sí, ése es el tipo... creo... insistieron mucho... + +[CAP_07:CAP_1] +No te preocupes, abuelo, no estoy enfadado contigo. + +[CAP_08:CAP_1] +Llevadle al hospital. + +[CAP_09:CAP_1] +Tommy... rájale un nuevo culo a ese tipo por mí... + +[CAP_10:CAP_1] +Voy a rajarle en dos. + +[CAP1_2:CAP_1] +¡Conoces las reglas, Vercetti! + +[CAP1_3:CAP_1] +¡El Sr. Forelli envía sus saludos! + +[CAP1_4:CAP_1] +¡Es el carnicero de Harwood! + +[CAP1_5:CAP_1] +Decidle a Sonny... ¡Que permanezca alejado! + +[CAP1_6:CAP_1] +Vice City ahora es MÍA, NO suya. + +[CAP1_7:CAP_1] +¿Crees que puedes eliminarme, Vercetti? + +[CAP1_8:CAP_1] +Seguiremos yendo tras de ti hasta que mueras, Vercetti. + +[CAP1_9:CAP_1] +No tienes ni una oportunidad, mamón psicótico. + +[CAP1_10:CAP_1] +Te mataré, Vercetti. + +[CAP1_11:CAP_1] +Siempre fuiste un gilipollas. + +[CAP1_12:CAP_1] +Vas a morir, Vercetti. + +[CAP1_B6:CAP_1] +~g~Has encontrado al cobrador, remátale. + +[CAP1_B7:CAP_1] +~g~Has perdido al cobrador. + +[CAP1_B8:CAP_1] +~r~El cobrador ha saqueado todos tus negocios. + +[CAP1_B9:CAP_1] +~g~¡La mafia ha saqueado a El Malibu! + +[CAP1_B0:CAP_1] +~g~¡La mafia ha saqueado al estudio cinematográfico! + +[CAP1_C2:CAP_1] +~g~¡La mafia ha llegado al astillero! + +[CAP1_C3:CAP_1] +~g~¡La mafia ha llegado a la fábrica de helados! + +[CAP1_C4:CAP_1] +~g~¡La mafia ha llegado al concesionario de coches! + +[CAP1_C5:CAP_1] +~g~¡La mafia ha llegado a la compañía de taxis! + +[CAP1_C9:CAP_1] +~g~¡La mafia ha llegado al Malibú! + +[CAP1_C0:CAP_1] +~g~¡La mafia ha llegado al estudio cinematográfico! + +[CAP1_D2:CAP_1] +~g~¡La mafia abandona el astillero! + +[CAP1_D3:CAP_1] +~g~La mafia abandona la fábrica de helados! + +[CAP1_D4:CAP_1] +~g~¡La mafia abandona el concesionario de coches! + +[CAP1_D5:CAP_1] +~g~¡La mafia abandona la compañía de taxis! + +[CAP1_D9:CAP_1] +~g~¡La mafia abandona el Malibú! + +[CAP1_D0:CAP_1] +~g~¡La mafia abandona el estudio cinematográfico! + +[CAP1B10:CAP_1] +Has puesto límite a los recaudadores. Vienen más de camino. + +{=================================== MISSION TABLE CARBUY ===================================} + +[CAR1_1:CARBUY] +B.J. Smith. Y usted debe ser el Sr. Vercetti. + +[CAR1_2:CARBUY] +¿Le gustaría dar una vuelta? + +[CAR1_3:CARBUY] +Podría ser. + +[CAR1_4:CARBUY] +Estoy muy triste por vender el negocio. + +[CAR1_5:CARBUY] +Esta fue mi primera inversión después de convertirme en profesional. + +[CAR1_6:CARBUY] +Pero es hora de que me mude. + +[CAR1_7:CARBUY] +¿Deja la ciudad? + +[CAR1_8:CARBUY] +No demasiado deprisa, espero. + +[CAR1_9:CARBUY] +No. Me estoy retirando pero simplemente para preparar mi regreso. + +[CAR1_10:CARBUY] +El negocio no era demasiado fuerte, + +[CAR1_11:CARBUY] +y mi equipo quizás se puso demasiado + +[CAR1_12:CARBUY] +creativo con la generación de la riqueza. + +[CAR1_13:CARBUY] +Obviamente, habría podido desmantelar el negocio antes de cederlo. + +[CAR1_14:CARBUY] +Diablos, podría haber quemado el sitio si hubiese querido. + +[CAR1_15:CARBUY] +Esta es tierra para una urbanización de primera. + +[CAR1_16:CARBUY] +Oh, yo no me preocuparía por nada de eso. + +[CAR1_17:CARBUY] +Este lugar parece perfecto. + +[CAR1_18:CARBUY] +Sí, así es, ¿así que entiendo que tenemos un trato? + +{=================================== MISSION TABLE CARPAR1 ===================================} + +[MM_1_A:CARPAR1] +~g~Atraviesa ~y~5 puntos de control ~r~¡SIN ~g~aplastar ningún ~r~CONO! ~g~Puedes atravesarlos en ~y~CUALQUIER ORDEN. + +[CONE_1:CARPAR1] +~r~¡Aplastaste un cono! + +[MM_1_C:CARPAR1] +~y~PASA POR~g~ un punto de control para activar el temporizador. ~g~Cada punto de control te dará ~y~~1~ SEGUNDOS~g~. + +{=================================== MISSION TABLE COPCAR ===================================} + +[KILLS:COPCAR] +MUERTES: + +[C_BREIF:COPCAR] +~g~Sospechoso visto por última vez en la zona de ~a~. + +[C_PASS:COPCAR] +AMENAZA ELIMINADA: ~1~ $ + +[COPCART:COPCAR] +~g~Tienes ~1~ segundos para volver al vehículo de policía antes de que termine la misión. + +[C_CANC:COPCAR] +~r~¡Misión de justiciero cancelada! + +[C_TIME:COPCAR] +~r~¡Tu tiempo como representante de la ley se ha terminado! + +[C_COMP1:COPCAR] +Has completado el nivel 12 de la misión de justiciero: tu armadura personal máxima ha aumentado a 150. + +[CLEVEL:COPCAR] +Misión de justiciero nivel ~1~ + +{=================================== MISSION TABLE COUNT1 ===================================} + +[CM1_A:COUNT1] +¿Sr. Vercetti? ¿Comprando la vieja imprenta? + +[CM1_B:COUNT1] +Sí, mi viejo solía trabajar con ellas. + +[CM1_C:COUNT1] +Iba a seguirle en el negocio, pero... viví una vida diferente. + +[CM1_D:COUNT1] +¿Planea vender la vieja maquinaria o destruirla? + +[CM1_E:COUNT1] +Estoy pensando que podíamos imprimir algo, un periódico, una revista... + +[CM1_F:COUNT1] +Oh, mierda, hijo, todo eso es mierda de baja calidad. Siempre me ha apetecido imprimir dinero. No es tan difícil. + +[CM1_G:COUNT1] +Ya sabes, llevo haciéndolo a pequeña escala durante años. + +[CM1_H:COUNT1] +¿De verdad? + +[CM1_I:COUNT1] +Claro. Pero necesitamos unas planchas de buena calidad. + +[CM1_J:COUNT1] +¡Por supuesto! Hay un sindicato de la falsificación que opera ya en Florida. + +[CM1_K:COUNT1] +¿Un sindicato? + +[CM1_L:COUNT1] +Sí. He escuchado algunos rumores. + +[CM1_M:COUNT1] +Conozco a un tipo que es bueno con los rumores... + +[CM1_N:COUNT1] +Solía pasar las tardes con él, limpiando los rodillos... + +[CM1_2A:COUNT1] +¡Menudo trasero! + +[CM1_2B:COUNT1] +¡Vale chica, tú te lo pierdes! + +[CM1_2C:COUNT1] +Buenas, figura, ¿cómo va todo? + +[CM1_2D:COUNT1] +¿Qué sabes sobre falsificación? + +[CM1_2E:COUNT1] +Estoy bien, Paul, ¿qué tal tú? + +[CM1_2F:COUNT1] +¡Ven aquí! + +[CM1_2G:COUNT1] +¡Vale! ¡Vale! Obviamente eres un hombre ocupado. + +[CM1_2H:COUNT1] +Todo lo que sé sobre los asuntos turbios es que las tríadas suministran las planchas. + +[CM1_2I:COUNT1] +Tienen una compañía de envíos en los muelles, + +[CM1_2J:COUNT1] +¡el jefe sabrá cuándo vienen las próximas planchas! + +[CM1_2K:COUNT1] +Gracias, Paul. + +[CM1_2L:COUNT1] +¡Cuál es tu problema, lunático! + +[CM1_2M:COUNT1] +¡Ponme otra copa, pronto! + +[CM1_3:COUNT1] +~g~¡Te han visto! + +[CM1_5:COUNT1] +~g~¡Ve a encontrarte con Kent Paul en el Club Malibú! + +[CNT1_1:COUNT1] +¿Quién eres tú? ¡En la cara no! ¡En la cara no! + +[CM1_1:COUNT1] +~g~Ve al barco de la línea charter Libertine en el muelle. + +[CM1_2:COUNT1] +~g~El encargado de los envíos tendrá toda la información necesaria. + +[CNT1_2:COUNT1] +Vale, ¡hablaré! ¡hablaré! + +[CM1_6:COUNT1] +~g~¡Lleva toda la información de vuelta a la imprenta! + +{=================================== MISSION TABLE COUNT2 ===================================} + +[CNT2_B1:COUNT2] +De acuerdo, hoy el mensajero moverá las planchas de los muelles. + +[CNT2_B2:COUNT2] +Voy a interceptarles, coger las planchas, deshacerme de las pruebas, y volver aquí. + +[CNT2_B3:COUNT2] +Dependiendo de lo bien que vaya esto, + +[CNT2_B4:COUNT2] +podemos tener cinco minutos para imprimir el dinero antes de que el sindicato de falsificación nos encuentre, o puede que tengamos todo el año. + +[CNT2_B5:COUNT2] +De cualquier modo, quiero que los verdes salgan de las prensas cinco minutos después de que vuelva. ¿Entendido? + +[CNT2_B6:COUNT2] +No te preocupes, Tommy. Estaremos preparados. + +[CNT2_B7:COUNT2] +Los otros chicos y yo estaremos por aquí en el vecindario, en caso de que necesites que nos ocupemos de cualquier problema. + +[CNT2_B8:COUNT2] +De acuerdo, ¿todo el mundo está tranquilo? De acuerdo. Os veré luego... + +[CNT2_01:COUNT2] +~g~El ~r~mensajero~g~ con las planchas de falsificación llegará en cualquier momento a los ~y~muelles~g~ en un helicóptero. + +[CNT2_02:COUNT2] +~r~El mensajero de las planchas ha huido en el helicóptero. + +[CNT2_03:COUNT2] +~r~¡El mensajero ha llegado a su destino sano y salvo, llegas demasiado tarde! + +[CNT2_04:COUNT2] +~r~¡Has destruido las planchas en la explosión! + +[CNT2_05:COUNT2] +~g~Tienes las planchas de falsificación. Llévalas a la imprenta. + +[CNT2_06:COUNT2] +~g~El mensajero ha muerto y ha dejado caer las planchas, recógelas antes de que lo haga otro. + +[CNT2_07:COUNT2] +~g~Una de las guardias ha recogido las planchas, no la dejes escapar. + +[CNT2_08:COUNT2] +~g~El ~r~mensajero~g~ ha llegado a los muelles con las planchas. + +[CNT2_4:COUNT2] +Negocio privado. ¡No eres bienvenido! + +[CNT2_09:COUNT2] +IMPRENTA CONSOLIDADA + +[CNT2_10:COUNT2] +~g~La imprenta generará ahora unos ingresos hasta un máximo de ~1~ $. Asegúrate que lo recaudas de manera regular. + +[CNT2_11:COUNT2] +~r~¡Las planchas están en el fondo del mar! + +{=================================== MISSION TABLE CUBAN1 ===================================} + +[CUB1_A:CUBAN1] +¿Sí, tío? + +[CUB1_B:CUBAN1] +Eh, tranquilo papito, este hombre es para mí. Tú, ¿tú eres el chico? + +[CUB1_C:CUBAN1] +Oh, sí. Tú eres el chico. Eso creo, ¿sabes? + +[CUB1_D:CUBAN1] +No. No creo que lo sepa. + +[CUB1_E:CUBAN1] +¿Oh, sí? Ven aquí, tipo duro. + +[CUB1_F:CUBAN1] +¿Crees que puedes desafiarme? + +[CUB1_G:CUBAN1] +¿Crees que puedes jugar a ese estúpido juego conmigo? + +[CUB1_H:CUBAN1] +No, creo que ya lo estás haciendo tú lo suficientemente estúpido por los dos. + +[CUB1_I:CUBAN1] +Eh, te llamó tonto, hijo. + +[CUB1_J:CUBAN1] +Y yo le llamé niñita, papá. + +[CUB1_K:CUBAN1] +Mírale, vestido así. + +[CUB1_L:CUBAN1] +¿Qué es esto, un traje de noche? + +[CUB1_M:CUBAN1] +¿Eres alguna clase de tipo duro y vistes como una mujer? + +[CUB1_N:CUBAN1] +También llevas pantis como una mujer, ¿eh? + +[CUB1_O:CUBAN1] +¿Qué tienes contra las mujeres? ¿Prefieres a los hombres, grandullón? + +[CUB1_P:CUBAN1] +¡Me gustan las mujeres! ¡Me gustan las mujeres! ¡Amo a mi madre, chico! + +[CUB1_Q:CUBAN1] +De acuerdo, de acuerdo. Aceptaré tu palabra. + +[CUB1_R:CUBAN1] +¿Sabes conducir, amigo? + +[CUB1_S:CUBAN1] +Sí... como una mujer. + +[CUB1_T:CUBAN1] +Muy gracioso. Me gustas, grandullón. Quizás puedas ayudarme. + +[CUB1_U:CUBAN1] +Quizás puedas demostrar que eres un hombre. ¿Eh? + +[CUB1_V:CUBAN1] +Saca el barco. + +[CUB1_W:CUBAN1] +Demuéstrame que tienes un par de cojones, + +[CUB1_X:CUBAN1] +y no unos chiquitos de ratón. + +[CUB1_02:CUBAN1] +De acuerdo, tío, trátalo como a una mujer. + +[CUB1_03:CUBAN1] +No está mal, un hombre de verdad. + +[CUB1_04:CUBAN1] +Tío, tienes unas pelotas bien grandes, amigo. + +[CUB1_05:CUBAN1] +Amigo, eres un tío. + +[CUB1_06:CUBAN1] +¿Puedes decir que eres un machote, tío? + +[CUB1_07:CUBAN1] +Eres un ratón asustado, niñito, vete a llorar con tu mamá. + +[CUB1_08:CUBAN1] +Eres una bazofia. Andas como un tío, hablas con un tío, pero conduces como un idiota. + +[CUB1_09:CUBAN1] +Brother, eres un macho. Me gustas, tío. Me gustas un montón. + +[CUB1_10:CUBAN1] +En cualquier momento, tío. Porque tienes un par. Y todos mis amigos tienen un buen par de cojones. + +[CUB1_11:CUBAN1] +~r~¡Mataste a Rico! + +[CUB1_12:CUBAN1] +Atraviesa el primer punto de control para comenzar la prueba. + +[CUB1_14:CUBAN1] +¡Vuelve al bote! + +[CUB1_15:CUBAN1] +~r~Brother, eres demasiado lento. + +[CUB1_01:CUBAN1] +Eh, soy Rico. ¿Eres tú el tío de las pelotas grandes? + +[CUB1_13:CUBAN1] +~g~Tienes tres minutos para recorrer el circuito. + +{=================================== MISSION TABLE CUBAN2 ===================================} + +[CUB2_A:CUBAN2] +Un cafetito, por favor, Alberto... + +[CUB2_N:CUBAN2] +Sin problema, Tommy. + +[CUB2_B:CUBAN2] +¡Papá! ¡Un gran problema! + +[CUB2_O:CUBAN2] +Umberto, mi hijo, ¿qué ocurre? + +[CUB2_C:CUBAN2] +¡Los haitianos! ¡Odio a estos haitianos! + +[CUB2_D:CUBAN2] +¡Me han molestado por última vez! + +[CUB2_E:CUBAN2] +¡Nos desharemos de ellos! + +[CUB2_F:CUBAN2] +Sólo necesitamos un poco de apoyo. + +[CUB2_G:CUBAN2] +Ya he perdido algunos hermanos ahí fuera. + +[CUB2_H:CUBAN2] +¡Amigo, tú conduces bien! + +[CUB2_I:CUBAN2] +Para ser una mujer. ¿No? + +[CUB2_J:CUBAN2] +¡Éste no es momento de bromear! + +[CUB2_K:CUBAN2] +¡Vamos, maneja de nuevo para mí! + +[CUB2_L:CUBAN2] +¡Lleva a mis chicos allí y eliminaremos a esos haitianos! + +[CUB2_M:CUBAN2] +¡Se meten conmigo y se meten con el chico grande de la ciudad! + +[CUB2_01:CUBAN2] +No hay suficiente espacio, brother, necesitas un coche más grande. + +[CUB2_02:CUBAN2] +¡Necesitamos refuerzos del café! + +[CUB2_03:CUBAN2] +~g~Consigue un coche y recoge a los cubanos frente al Café Robina. + +[CUB2_04:CUBAN2] +~g~Ve y deja a los cubanos en la zona de combate. + +[CUB2_05:CUBAN2] +¡Elimina a ese francotirador cobarde! + +[CUB2_07:CUBAN2] +¡Luchan como nenas! ¡Ponte a cubierto! + +[CUB2_09:CUBAN2] +¡Francotirador en el tejado! + +[CUB2_11:CUBAN2] +~r~Estúpido, necesitábamos ese coche. + +[CUB2_12:CUBAN2] +¡Eh, amigo! ¡Me alegro de que hayas podido venir! + +[CUB2_13:CUBAN2] +¡Apestoso nido de haitianos, vamos a matarlos a todos! + +[CUB2_14:CUBAN2] +¡A LA CARGA! + +[CUB2_15:CUBAN2] +¡Ahora, hermanos, A LA CAAARGA! + +[CUB2_16:CUBAN2] +¡Tommy, hemos demostrado nuestro valor y nuestra hombría! + +[CUB2_17:CUBAN2] +¡Vamos a robar esa furgoneta llena de farlopa y pegarnos el piro! + +[CUB2_18:CUBAN2] +~g~Consigue un coche y recoge a los cubanos. + +[CUB2_19:CUBAN2] +¡Vamos a combatir como machos! + +[CUB2_21:CUBAN2] +¡Luchad como machos con un par de cojones! + +[CUB2_22:CUBAN2] +~g~Termina con el resto de los haitianos para que los cubanos puedan avanzar. + +[CUB2_23:CUBAN2] +~g~Little Haiti estará atestado de haitianos intentando desquitarse con los cubanos. ¡Ten cuidado! + +[CUB2_24:CUBAN2] +~g~Vuelve al Café Robina en la furgoneta y aparca en la parte trasera. + +[CUB2_25:CUBAN2] +¡MATA A TODOS LOS HAITIANOS! + +{=================================== MISSION TABLE CUBAN3 ===================================} + +[CUB3_A:CUBAN3] +Alberto. Un cafetito, señor. + +[CUB3_B:CUBAN3] +Papaíto, no sirvas a esta sabandija. + +[CUB3_C:CUBAN3] +¡Tienes dos caras, Tommy! + +[CUB3_D:CUBAN3] +¡Tú, guapito de cara, o eres un falso o eres un calzonazos! + +[CUB3_E:CUBAN3] +¡Los haitianos! Tío, se están riendo de mí. + +[CUB3_F:CUBAN3] +Vale, vale. ¿Qué problema tienes? + +[CUB3_G:CUBAN3] +Se están riendo de mí, Tommy. ¡De mí! + +[CUB3_H:CUBAN3] +¡Umberto Robina! ¡Están haciendo lo que quieren! + +[CUB3_I:CUBAN3] +Nadie hace lo que quiere, Umberto, hacen lo que les dejes hacer. + +[CUB3_J:CUBAN3] +¿Qué? + +[CUB3_K:CUBAN3] +¿Quieres que alguien se ocupe del asunto? + +[CUB3_L:CUBAN3] +Puedo manejarlo, pero va a costarte. + +[CUB3_M:CUBAN3] +Sé que somos hermanos y todo eso, pero esto es un negocio. + +[CUB3_N:CUBAN3] +Tommy. Eres un verdadero macho. Un tío de negocios, un caballero. + +[CUB3_O:CUBAN3] +Estos haitianos. Tienen un alijo de producto que va a venir desde alta mar, un material de primera. + +[CUB3_P:CUBAN3] +Lo agarraremos y acabaremos con ellos. + +[CUB3_Q:CUBAN3] +Tú lo agarrarás y yo cuidaré de ti. Como a un hermano. Como a un hijo. + +[CUB3_R:CUBAN3] +Creo que prefiero el dinero a que me mezas en tus rodillas, amigo. + +[CUB3_01:CUBAN3] +Eh, Rico. Bonito barco, ¿estás preparado? + +[CUB3_03:CUBAN3] +~g~Recoge todos los maletines llenos de nieve y dinero. + +[CUB3_04:CUBAN3] +~g~Lleva la nieve y la pasta de vuelta a Umberto. + +[CUB3_05:CUBAN3] +Sí, Tommy. Tienes que apuntar bien. + +[CUB3_06:CUBAN3] +Mi barco, no me sirve lleno de agujeros, ¿eh? + +[CUB3_07:CUBAN3] +~g~Ve y reúnete con Rico. Te llevará al punto de encuentro. + + +[CUB3_02:CUBAN3] +~g~¡MATA A TODOS LOS HAITIANOS DEL BARCO! +[CUB3_08:CUBAN3] +Oh, oh... Una banda de cubanos. ¡Nos atacan! + +{=================================== MISSION TABLE CUBAN4 ===================================} + +[CUB4_A:CUBAN4] +Ey, señoritas. ¿Sabéis qué voy a hacer? + +[CUB4_B:CUBAN4] +Primero voy a matar un haitiano. ¿Y después? + +[CUB4_C:CUBAN4] +Voy a hacer el amor como un hombre. + +[CUB4_D:CUBAN4] +¿Sabes chica? Algo así. + +[CUB4_E:CUBAN4] +¡Perdedor! + +[CUB4_F:CUBAN4] +Mamón. + +[CUB4_G:CUBAN4] +¡Ey, nena, no te tocaría ni con una pértiga de tres metros! + +[CUB4_H:CUBAN4] +¡A Umberto Robina le gustan las damas! ¡No una cabra con faldas! + +[CUB4_I:CUBAN4] +¡Tommy! ¡Tommy, te quiero, te quiero! ¡Vámonos! + +[CUB4_J:CUBAN4] +¿Irnos a donde? ¿No me puedo tomar primero una taza de café? + +[CUB4_K:CUBAN4] +¡No hay tiempo para café! Además, acabo de tomarme uno. + +[CUB4_L:CUBAN4] +Vamos a eliminar a los haitianos. + +[CUB4_M:CUBAN4] +Tommy, ¿qué tiene un elefante dentro de la trompa? + +[CUB4_N:CUBAN4] +¡Dos metros de mocos! ¡Ja, ja, ja! + +[CUB4_O:CUBAN4] +Lo que tú digas, Umberto. + +[CUB4_P:CUBAN4] +Tommy, ve y consíguenos un cochecito haitiano. + +[CUB4_Q:CUBAN4] +Cuando lo tengas, regresa y recoge a mi chico + +[CUB4_R:CUBAN4] +Pepe, y llévale a donde los haitianos. + +[CUB4_S:CUBAN4] +Luego te vas hasta la planta de procesamiento de los haitianos y utilizas su disolvente como explosivo. + +[CUB4_T:CUBAN4] +¡Boom! Y ¡Bye bye! + +[CUB4_U:CUBAN4] +Umberto, ¿qué hay de ti? + +[CUB4_V:CUBAN4] +Oh, voy a quedarme atrás, a vigilar el café con papá. + +[CUB4_W:CUBAN4] +No se siente muy bien, ¿sabes? + +[CUB4_02:CUBAN4] +~g~Las bombas se colocarán con un temporizador de 45 segundos. + +[CUB4_04:CUBAN4] +~r~¡Alertaste a la base, ahora no habrá modo de entrar! + +[CUB4_07:CUBAN4] +El disolvente está a la vuelta, amigo. + +[CUB4_08:CUBAN4] +Hola, amigos. + +[CUB4_09:CUBAN4] +Bueno. Haitianos putas. Muerte. + +[CUB4_10:CUBAN4] +Vamos. + +[CUB4_11:CUBAN4] +Sí, vamos. + +[CUB4_12:CUBAN4] +¡Ey, necesitamos un coche de la banda haitiana! + +[CUB4_13:CUBAN4] +¡Oye, vamos a encontrar a nuestros muchachos! + +[CUB4_14:CUBAN4] +Sigue a mis compadres. + +[CUB4_15:CUBAN4] +De acuerdo, allá vas... + +[CUB4_16:CUBAN4] +¡Voy a colocar la bomba, cubridme! + +[CUB4_17:CUBAN4] +¡CORRED! + +[CUB4_18:CUBAN4] +Tío, esta es una parte bonita de la ciudad... + +[CUB4_19:CUBAN4] +Compadre, este lugar es un vertedero. + +[CUB4_20:CUBAN4] +Yo tenía una hermosa mujer... vivía por este barrio. + +[CUB4_21:CUBAN4] +Sabes, allí hacen unas buenas pizzas. + +[CUB4_22:CUBAN4] +¡Tío! ¡Conduces como un loco! + +[CUB4_23:CUBAN4] +¿Te has perdido tío? + +[CUB4_24:CUBAN4] +Dejaste atrás a Pepe, ve a por él. + +[CUB4_03:CUBAN4] +~g~Permanece en el coche hasta que puedas aparcar sin percances dentro del recinto. + +[CUB4_26:CUBAN4] +~g~Recoge a Pepe, dirígete al norte a Little Haiti y roba un coche Voodoo. + +[CUB4_27:CUBAN4] +~g~Ve y reúnete con Rico y los otros cubanos. + +[CUB4_28:CUBAN4] +~g~Reúnete con los otros cubanos en el laboratorio de sustancias químicas haitiano. + +[CUB4_29:CUBAN4] +~g~Camina hacia cada uno de los marcadores para colocar la bomba en ese lugar. + +[CUB4_30:CUBAN4] +~g~Después de colocar las tres bombas, aléjate de la fábrica antes que salte por los aires. + +[CUB4_31:CUBAN4] +~g~¡Aléjate de la fábrica! + +[CUB4_32:CUBAN4] +~g~Aparca el coche en el marcador y bájate de él. + +[CUB4_06:CUBAN4] +~r~No te alejaste lo suficiente de la base, ¡y hemos tenido que abortar la explosión! + +{=================================== MISSION TABLE FINALE ===================================} + +[FIN1_01:FINALE] +¿Qué está pasando? + +[FIN1_02:FINALE] +¡Tommy! Oh, bien, bien. Escucha, escucha. Uh, escucha, + +[FIN1_03:FINALE] +me gusta el pescado. Me encanta el pescado. + +[FIN1_04:FINALE] +Me encantan como mascotas en peceras, o como comida en un plato, + +[FIN1_05:FINALE] +pero aunque los adoro, no quiero dormir con ellos. + +[FIN1_06:FINALE] +De acuerdo, pero ahora mismo, tus hermanos italianos van a salir de ahí para ponerme esos zapatos de cemento y yo... + +[FIN1_07:FINALE] +Cállate, Ken. Siéntate. + +[FIN1_08:FINALE] +Lance, ¿qué demonios ocurre? + +[FIN1_09:FINALE] +Es tu amigo del norte, Tommy. No está muy contento de que matases a su hombre. + +[FIN1_10:FINALE] +Vienen hoy para ver el negocio. + +[FIN1_11:FINALE] +Tardaron más de lo que pensaba... + +[FIN1_12:FINALE] +Chicos, tenemos que acabar con esto y no dejar duda de que ésta es mi operación. ¡Mía! + +[FIN1_13:FINALE] +Ken, pon tres millones de la primera tanda de dinero falsificado en maletines. + +[FIN1_14:FINALE] +Lance, reúne a los muchachos... + +[FIN2_01:FINALE] +¡Tommy! + +[FIN2_02:FINALE] +¿Qué? ¿No hay un gran abrazo para tu viejo colega? + +[FIN2_03:FINALE] +He pasado quince años fuera del negocio, + +[FIN2_04:FINALE] +estoy un poco oxidado respecto a las costumbres de la familia. + +[FIN2_05:FINALE] +Siempre enfadado, ¿eh? Tommy. + +[FIN2_06:FINALE] +No te dije que tu temperamento te metería en problemas, ¿eh? + +[FIN2_07:FINALE] +Hay tres millones en los maletines... + +[FIN2_08:FINALE] +¿Cuántos fueron? ¿Diez? No, once hombres. + +[FIN2_09:FINALE] +¡Así es como llegaron a llamarte el Carnicero de Harwood! ¡Je je je! + +[FIN2_10:FINALE] +Me enviaste a matar a un hombre, UN HOMBRE. Sabían que iba para allá. + +[FIN2_11:FINALE] +Vigila tu tono. + +[FIN2_12:FINALE] +Cualquiera pensaría que me echas la culpa por ese desafortunado conjunto de circunstancias. + +[FIN2_13:FINALE] +Simplemente coge el dinero... + +[FIN2_14:FINALE] +¿Que coja el maldito dinero? + +[FIN2_15:FINALE] +¿Sabes, Tommy? Hice lo que pude por ti, tiré de algunos hilos, pedí favores. + +[FIN2_16:FINALE] +Yo era tu amigo, Tommy. Esperaba que entraras en razón, ver qué es bueno para el negocio. + +[FIN2_17:FINALE] +Confié en ti, Tommy, y me decepcionaste. + +[FIN2_18:FINALE] +Pero al menos alguno de tu organización de mierda sabe hacer negocios, + +[FIN2_19:FINALE] +¿No es verdad, Lance? + +[FIN2_20:FINALE] +Lo siento, Tommy. Esto es Vice City. Son negocios. + +[FIN2_21:FINALE] +Nos vendiste. + +[FIN2_22:FINALE] +No. Te vendí a TI, Tommy, te vendí a TI. + +[FIN2_23:FINALE] +El dinero de verdad está arriba, en la caja fuerte. + +[FIN2_24:FINALE] +Así que Tommy, ¿cuál es el gran plan? + +[FIN2_25:FINALE] +¿Creías que solo me llevaría el dinero falso? + +[FIN2_26:FINALE] +¿Salvar el culo y huir con el rabo entre las piernas? + +[FIN2_27:FINALE] +No. + +[FIN2_28:FINALE] +Solo quería joderte antes de matarte. + +[FIN3_01:FINALE] +¿Tommy? + +[FIN3_02:FINALE] +Oh, Dios mío, ¡Tommy! ¿Qué ha sucedido? + +[FIN3_03:FINALE] +¿A ti qué te parece? + +[FIN3_04:FINALE] +¡Parece que te han arruinado el traje! + +[FIN3_05:FINALE] +¡Y Tommy, era un traje muy bonito! Tommy, ¿qué diablos ha ocurrido? + +[FIN3_06:FINALE] +Tuve un desacuerdo con un socio del negocio, ya sabes cómo son estas cosas. + +[FIN3_07:FINALE] +Tommy, cuando tengo un desacuerdo con un socio, le envío una carta muy enfadado. + +[FIN3_08:FINALE] +Quizás me mee en su buzón. No empiezo la tercera guerra mundial. + +[FIN3_09:FINALE] +¿Sabes? Quizás deberías hablar con mi psiquiatra... + +[FIN3_10:FINALE] +Ése estúpido mamón, Lance... + +[FIN3_11:FINALE] +Tommy. A mí nunca me gustó ese tipo, ¿de acuerdo? + +[FIN3_12:FINALE] +Era neurótico, era inseguro, era egocéntrico... ¡ese tipo era un gilipollas! + +[FIN3_13:FINALE] +¡Me alegro de que lo eliminases! + +[FIN3_14:FINALE] +No creo que vayamos a tener más líos desde la parte norte... + +[FIN3_15:FINALE] +porque ya no hay 'parte norte'. + +[FIN3_16:FINALE] +Ahora es todo sur. + +[FIN3_17:FINALE] +Espera, eso quiere decir lo que creo que quiere decir, Tommy, ¿nene? + +[FIN3_18:FINALE] +¿Qué crees que significa? + +[FIN3_19:FINALE] +Que estamos al mando... Quiero decir, que tú estás al mando. Oh, Tommy... + +[FIN3_20:FINALE] +¿Sabes, Ken? Este podría ser el comienzo de una hermosa relación comercial... + +[FIN3_21:FINALE] +Después de todo, eres un cómplice, traicionero, ladrón de tres al cuarto + +[FIN3_22:FINALE] +y yo soy un asesino psicópata convicto y además paso farlopa. + +[FIN3_23:FINALE] +Lo sé. ¿No es sencillamente hermoso? + +[FIN_B1:FINALE] +~g~Ve y liquida al traidor de ~y~Lance Vance~g~. + +[FIN_B2:FINALE] +~g~Liquida a ~p~Sonny~g~ y acaba con esto de una vez por todas. + +[FIN_B3:FINALE] +~g~La mafia está intentando robarte el dinero. Protege la caja fuerte. + +[FIN_B4:FINALE] +~g~Estás casi muerto, consigue ~w~salud~g~ en el piso de abajo. + +[FIN_B5:FINALE] +~g~La mafia está robándote el dinero, protege la ~c~caja fuerte~g~. + +[FIN_B7:FINALE] +~r~La mafia te ha robado todo el dinero. + +[DEFSAFE:FINALE] +~g~Regresa a la caja fuerte y protégela. + +{=================================== MISSION TABLE FIRETRK ===================================} + +[F_PASS1:FIRETRK] +¡Fuego extinguido! + +[F_FAIL2:FIRETRK] +~r~¡Llegas demasiado tarde! + +[F_CANC:FIRETRK] +~r~¡Misión de bombero cancelada! + +[F_EXTIN:FIRETRK] +FUEGOS: + +[F_START:FIRETRK] +~g~Se ha informado de un vehículo ardiendo en la zona de ~a~. Ve y extingue el fuego. + +[SIREN_1:FIRETRK] +Para activar la sirena de este vehículo, pulsa ~h~~k~~VEHICLE_HORN~~w~. + +[SIREN_2:FIRETRK] +Para activar la sirena de este vehículo, pulsa ~h~~k~~VEHICLE_HORN~~w~. + +[FIREPRO:FIRETRK] +Nivel 12 de la misión del camión de bomberos completado. ¡Ahora eres completamente ignífugo! + +[F_FAIL1:FIRETRK] +Misión de bombero terminada. + +[F_STAR1:FIRETRK] +~g~Se ha informado de que hay vehículos ardiendo en la ~a~ zona. Ve y apaga el fuego. + +[SPRAY_4:FIRETRK] { reVC update } +Pulsa ~h~~k~~VEHICLE_FIREWEAPON~ ~w~para disparar el cañón de agua y ~h~~k~~VEHICLE_TURRETLEFT~~w~ o ~h~~k~~VEHICLE_TURRETRIGHT~~w~ para apuntar con él. + +[SPRAY_1:FIRETRK] { reVC update } +Pulsa ~h~~k~~VEHICLE_FIREWEAPON~ ~w~para disparar el cañón de agua y ~h~~k~~VEHICLE_TURRETLEFT~~w~ o ~h~~k~~VEHICLE_TURRETRIGHT~~w~ para apuntar con él. + +{=================================== MISSION TABLE GENERA1 ===================================} + +[GEN1_A:GENERA1] +¡Sr. Vercetti! + +[GEN1_B:GENERA1] +Coronel. + +[GEN1_D:GENERA1] +No, gracias. + +[GEN1_E:GENERA1] +Me avergüenza admitir que una de las causas de nuestro mutuo problema parece ser la lengua larga de un hombre en el que solía confiar. + +[GEN1_F:GENERA1] +He tenido conmigo a González durante años, pero ahora su incompetencia alcanza nuevas cotas. + +[GEN1_G:GENERA1] +Lo correcto es que mates a González... + +[GEN1_H:GENERA1] +¿Lo hizo él? El dinero es lo único importante para mí. + +[GEN1_I:GENERA1] +Por tu gentileza te compensaré y después buscaremos juntos tu dinero. + +[GEN1_J:GENERA1] +Estará en su ático, probablemente medio borracho. Utiliza esto... + +[GEN1_K:GENERA1] +Utiliza esto... + +[GEN1_06:GENERA1] +¡Eh! ¡Tiene una sierra! + +[GEN1_07:GENERA1] +¡Mantente alejado de mí, cabrón miserable! + +[GEN1_08:GENERA1] +¡Maldición, he desperdiciado mi vida y mi belleza! + +[GEN1_10:GENERA1] +¡Te voy a cerrar esa bocaza tuya! + +[GEN1_11:GENERA1] +¡Deja de correr gorda bola de sebo! + +[GEN1_12:GENERA1] +¡Estate quieto y haré que sea rápido! + +[GEN1_13:GENERA1] +Deja de chillar, a nadie le importa, ¡gordo! + +[GEN1_C:GENERA1] +Gracias por venir. Por favor siéntese. ¿Langosta? + +[GEN1_05:GENERA1] +~g~¡Ve y liquida a González! + +[GEN1_09:GENERA1] +Te pagaré el doble, Tommy, ¡EL DOBLE! + +[GEN1_18:GENERA1] +~r~¡González ha conseguido llegar a la Jefatura de Policía! + +[GEN1_19:GENERA1] +~g~¡La policía de Vice City viene a por ti! + +[GEN1_20:GENERA1] +~g~Sube a un vehículo. + +[GEN1_21:GENERA1] +~g~Ve al~h~ taller de pintura~g~ de~h~ Vice Point~g~. + +[GEN1_22:GENERA1] +~g~Conduce tu vehículo hasta el interior del ~h~taller de pintura~w~ para perder tu ~h~nivel de se busca~w~, reparar y volver a pintar tu vehículo. El precio es ~h~100 $~g~. Esta vez es gratis. + +[GEN1_01:GENERA1] +Cuando estés corriendo, mantén pulsado ~h~~k~~PED_FIREWEAPON~~w~ para preparar un ataque cuerpo a cuerpo. + +[GEN1_02:GENERA1] +Cuando estés corriendo, mantén pulsado ~h~~k~~PED_FIREWEAPON~~w~ para preparar un ataque cuerpo a cuerpo. + +[GEN1_03:GENERA1] +Cuando estés corriendo, mantén pulsado ~h~~k~~PED_FIREWEAPON~~w~ para preparar un ataque cuerpo a cuerpo. + +[GEN1_14:GENERA1] +Suelta ~h~~k~~PED_FIREWEAPON~~w~ para atacar. + +[GEN1_15:GENERA1] +Suelta ~h~~k~~PED_FIREWEAPON~~w~ para atacar. + +[GEN1_16:GENERA1] +Suelta ~h~~k~~PED_FIREWEAPON~~w~ para atacar. + +[GEN1_23:GENERA1] +~g~Vuelve pasando por las puertas para regresar a la planta baja. + +{=================================== MISSION TABLE GENERA2 ===================================} + +[COL2_A:GENERA2] +¡Tommy! Ven, acércate. + +[COL2_B:GENERA2] +Qué buena pinta, ¿eh? ¿Hocico de tapir? + +[COL2_C:GENERA2] +No. No, gracias. + +[COL2_D:GENERA2] +Tommy, eres como una brisa de la pampa que me ha liberado de la peste de la corrupción, + +[COL2_E:GENERA2] +aunque, parece que debo llorar su paso y seguir con el negocio como siempre. + +[COL2_F:GENERA2] +Esto no me está acercando mucho a mi dinero... + +[COL2_G:GENERA2] +Tommy, amigo mío, ahora no estás en Liberty. Aquí hacemos las cosas de un modo diferente. + +[COL2_H:GENERA2] +Continuaré con mis investigaciones pero mientras tanto tengo un valioso trato que cerrar. + +[COL2_I:GENERA2] +Un favor para un amigo, Cortez. + +[COL2_J:GENERA2] +Eres un buen amigo, Tommy. Sabía que no me decepcionarías. + +[COL2_K:GENERA2] +Necesito que te encuentres con un mensajero que ha conseguido una tecnología valiosa para mí... + +[COL2_1:GENERA2] +La lluvia, está muy húmeda esta época del año... + +[COL2_2:GENERA2] +¿Qué? + +[COL2_3:GENERA2] +Ah, comment? + +[COL2_4:GENERA2] +Mira, me envía Cortez. Tan sólo dame los malditos chips. + +[COL2_5:GENERA2] +Oh... d'accord. + +[COL2_B1:GENERA2] +~g~Encuéntrate con el mensajero en el centro comercial. + +[COL2_B2:GENERA2] +~g~El mensajero está huyendo con los chips guía, ¡no le dejes escapar! + +[COL2_B3:GENERA2] +~g~Recupera los chips guía y llévaselos de vuelta al Coronel. + +[COL2_F1:GENERA2] +~r~¡Mataste al contacto! + +[COL2_F2:GENERA2] +~r~El mensajero está muerto. Coge los chips guía. + +[COL2_6A:GENERA2] +¡Alto, cerdo imperialista americano! Eso es propiedad del gobierno francés. ¡Y se acabó! + +[BLIPHLP:GENERA2] +Cuando el icono en el radar es un triángulo apuntando hacia arriba, indica que el objetivo está por encima del jugador. + +[COL2_F3:GENERA2] +~r~Los chips de orientación están en el fondo del mar. + +[COL2_F4:GENERA2] +~r~¡El mensajero ha escapado! Has fracasado en conseguir los chips de orientación. + +{=================================== MISSION TABLE GENERA3 ===================================} + +[GEN3_A:GENERA3] +Thomas, agradezco que hayas venido. + +[GEN3_B:GENERA3] +Perdóname por ir directamente a los negocios. + +[GEN3_C:GENERA3] +Díaz me ha pedido que supervise una transacción comercial poco importante. + +[GEN3_D:GENERA3] +Esperemos que vaya mejor que la última vez, ¿eh? + +[GEN3_E:GENERA3] +Por eso es por lo que pensé en ti, amigo mío. + +[GEN3_F:GENERA3] +He dejado protección en un aparcamiento de varias plantas. + +[GEN3_G:GENERA3] +Recógela... luego ve a vigilar a los hombres de Díaz en el punto de entrega. + +[GEN3_H:GENERA3] +Gracias, amigo. + +[GEN3_1:GENERA3] +Monopolizando toda la acción, ya veo... + +[GEN3_2:GENERA3] +Mira, ¿quieres dejar de seguirme a todas partes? ¿Por qué no vienes y me demuestras que vales para algo? + +[GEN3_3:GENERA3] +Podría hacerlo. Mi nombre es Lance, por cierto. + +[GEN3_5:GENERA3] +Tú debes de ser el nuevo matón de Cortez. + +[GEN3_6:GENERA3] +Hasta que surjan oportunidades mejor pagadas. + +[GEN3_7:GENERA3] +Estarán aquí en cualquier momento. Será mejor que consigamos una posición ventajosa... + +[GEN3_8:GENERA3] +¡De acuerdo! Yo iré al balcón, tú sube a ese tejado al otro lado del patio. + +[GEN3_9:GENERA3] +¡Estoy vivo! ¡Mamones! ¡Y todo gracias a ti! ¿Cómo te llamas? + +[GEN3_10:GENERA3] +Tommy. + +[GEN3_11:GENERA3] +Nos veremos pronto, ¡creo! + +[GEN3_12:GENERA3] +Mierda. ¿Dónde está Lance? + +[GEN3_14:GENERA3] +¡Tommy! ¡Necesito ayuda aquí! + +[GEN3_15:GENERA3] +¡No te preocupes, te tengo cubierto! + +[GEN3_16:GENERA3] +¡Los hombres de Díaz están siendo eliminados! + +[GEN3_19:GENERA3] +~g~¡Haitianos! ¡Están saboteando el trato! ¡Protege a Díaz! + +[GEN3_20:GENERA3] +~g~Ve al aparcamiento donde podrás recoger el arma que te dejó el Coronel. + +[GEN3_22:GENERA3] +Salud de Díaz: + +[GEN3_23:GENERA3] +~g~¡Has dejado a Lance atrás! ¡Ve a por él! + +[GEN3_25:GENERA3] +~r~¡Lance murió! + +[GEN3_28:GENERA3] +~g~Devuélvele el maletín a Díaz. + +[GEN3_29:GENERA3] +~g~Recoge el maletín y llévaselo a Díaz. + +[GEN3_30:GENERA3] +~r~¡Se escapó con el dinero! ¡Díaz te arrancará las pelotas por ésto! + +[GEN3_33:GENERA3] +~r~¡Mira adónde disparas! Se supone que tienes que proteger a Díaz y a sus hombres, ¡no dispararles! + +[GEN3_34:GENERA3] +~r~¡No va a haber ningún trato si disparas a los cubanos! + +[GEN3_35:GENERA3] +~g~¡Ha robado el dinero de Díaz! + +[GEN3_36:GENERA3] +~g~¡Pilla una moto, persíguele y recupera el dinero de Díaz! + +[GEN3_37:GENERA3] +~g~Aquí vienen los cubanos. Vigila el trato y asegúrate que Díaz y Lance están a salvo. + +[GEN3_38:GENERA3] +~r~¡Díaz ha muerto! ¡Has fallado en protegerle! + +[GEN3_39:GENERA3] +~g~Ve a tu posición estratégica en el piso de arriba. + +[GEN3_44:GENERA3] +~g~Ve con Lance al punto de entrega y vigila a Díaz. + +[GEN3_45:GENERA3] +Estarán aquí en cualquier minuto, mejor será que vayamos tomando posiciones estratégicas. + +[GEN3_40:GENERA3] { reVC update } +Para ~h~disparar hacia el frente ~w~sobre una ~h~moto ~w~pulsa ~h~~k~~VEHICLE_FIREWEAPON~~w~. + +[GEN3_41:GENERA3] { reVC update } +Para ~h~disparar hacia el frente ~w~sobre una ~h~moto ~w~pulsa ~h~~k~~VEHICLE_FIREWEAPON~~w~. + +[GEN3_46:GENERA3] +¡Mierda! + +[GEN3_47:GENERA3] +¡Tommy! + +[GEN3_48:GENERA3] +¡Maldición! + +[GEN3_49:GENERA3] +Salud de Lance: + +[GEN3_50:GENERA3] +~r~¡Has perdido el dinero de Díaz! ¡La próxima vez intenta no convertir mi dinero en cenizas! + +[GEN3_51:GENERA3] +¡Más condenados haitianos en una furgoneta apestosa! + +[GEN3_54:GENERA3] +¡No os quedéis ahí, panda de gilipollas, perseguid a ese haitiano cabrón! + +[GEN3_55:GENERA3] +¡Tommy! ¡Yo me quedo aquí a proteger a Díaz! + +[GEN3_18:GENERA3] +~g~Aquí llegan los cubanos, mantente cerca de Díaz. Asegúrate de que la negociación entre Díaz y Lance sea segura. + +[GEN3_56:GENERA3] +~r~¡Díaz ha caído en una emboscada y ha muerto! ¡La próxima vez no le pierdas de vista! + +[GEN3_57:GENERA3] +El Kruger es un rifle de asalto que te permite apuntar en primera persona manualmente. + +[GEN3_58:GENERA3] +Mantén pulsado ~h~~k~~PED_LOCK_TARGET~~w~ para ~h~apuntar~w~ con un rifle de asalto. + +[GEN3_59:GENERA3] +Mantén pulsado ~h~~k~~PED_LOCK_TARGET~~w~ para ~h~apuntar~w~ con un rifle de asalto. + +[GEN3_60:GENERA3] +Pulsa ~h~~k~~PED_FIREWEAPON~~w~ para ~h~disparar~w~ un rifle de asalto. + +[GEN3_61:GENERA3] +Pulsa ~h~~k~~PED_FIREWEAPON~~w~ para ~h~disparar~w~ un rifle de asalto. + +[GEN3_62:GENERA3] +Pulsa ~h~~k~~PED_FIREWEAPON~~w~ para ~h~disparar~w~ un rifle de asalto. + +[GEN3_63:GENERA3] +Además de realizar maniobras de acercamiento,~h~ las motos ~w~te permiten ~h~disparar hacia adelante~w~. + +[GEN3_64:GENERA3] { reVC update } +Para ~h~disparar hacia el frente ~w~sobre una ~h~moto ~w~pulsa ~h~~k~~VEHICLE_FIREWEAPON~~w~. + +[GEN3_65:GENERA3] { reVC update } +Para ~h~disparar hacia el frente ~w~sobre una ~h~moto ~w~pulsa ~h~~k~~VEHICLE_FIREWEAPON~~w~. + +[GEN3_66:GENERA3] { reVC update } +Para ~h~disparar hacia el frente ~w~sobre una ~h~moto ~w~pulsa ~h~~k~~VEHICLE_FIREWEAPON~~w~. + +[GEN3_67:GENERA3] +Para disparar hacia el frente en una moto deberás tener un subfusil. + +[GEN3_53:GENERA3] +¡Mi dinero! + +[GEN3_52:GENERA3] +¿Esos haitianos creen que pueden enfrentarse a RICARDO DÍAZ? + +{=================================== MISSION TABLE GENERA4 ===================================} + +[DETON:GENERA4] +DETONACIÓN: + +[COL4_3:GENERA4] +¡CONVOY, ALTO! + +[COL4_6:GENERA4] +¡NOS ESTÁN DISPARANDO! + +[COL4_7:GENERA4] +¡Civil, aléjese del tanque! + +[COL4_8:GENERA4] +¡DIJE, alejaos, INMEDIATAMENTE! + +[COL4_9:GENERA4] +¡POSICIONES DEFENSIVAS! + +[COL4_11:GENERA4] +- ¡Quite a esos civiles fuera de mi camino, soldado! - ¡Señor, sí, señor! + +[COL4_12:GENERA4] +¡Civil en el TANQUE! ¡DETENEDLE! + +[COL4_13:GENERA4] +Este es un convoy militar, no obstruyan nuestra ruta. + +[COL4_14:GENERA4] +Tírelo, soldado. + +[COL4_15:GENERA4] +- ¡Mueva ese vehículo civil fuera de nuestro camino! - ¡Señor! ¡Moviendo el vehículo, señor! + +[COL4_17:GENERA4] +¡Bien, PELOTÓN, MUÉVANSE! + +[COL4_18:GENERA4] +¡Hay alguien en el tanque, señor! + +[COL4_19:GENERA4] +- ¡Vaya a conseguir unos donuts, soldado! - ¡Señor, sí, señor! + +[COL4_20:GENERA4] +Blanco localizado, señor. + +[COL4_21:GENERA4] +¡FRANCOTIRADOR! + +[COL4_22:GENERA4] +Me voy de aquí. + +[COL4_23:GENERA4] +- ¡Objetivo completado! ¡Pelotón, rompan filas! - Vamos a comer unos cuantos donuts. + +[COL4_24:GENERA4] +¡Activado protocolo de seguridad Delta India Eco! ¡Iniciada autodestrucción del vehículo! + +[COL4_26:GENERA4] +¡Prepárate para morir, basura comunista! + +[COL4_B2:GENERA4] +~r~El tanque llegó a su destino a salvo! + +[COL4_B5:GENERA4] +~r~¡El tanque ha sido destruido! + +[COL4_01:GENERA4] +Díaz estaba complacido, y le gustaría verte de nuevo. + +[COL4_02:GENERA4] +¿Es eso algo bueno? + +[COL4_03:GENERA4] +¡Por supuesto! Aunque estoy empezando a pensar que Díaz fue el responsable de nuestra desafortunada pérdida... + +[COL4_04:GENERA4] +¿Qué te hace decir eso? + +[COL4_05:GENERA4] +Uno no esgrime acusaciones contra un hombre como Díaz... Solo estoy pensando en voz alta... + +[COL4_06:GENERA4] +No importa. Tengo una propuesta de la que te podrías beneficiar... + +[COL4_07:GENERA4] +No tengo tiempo para hacer más recados, Cortez. + +[COL4_08:GENERA4] +Yo habría pensado que un hombre con unas deudas tan grandes estaría ansioso por encontrar oportunidades. Por favor, Tommy, al menos escúchame. + +[COL4_09:GENERA4] +Adelante... + +[COL410:GENERA4] +Tengo un comprador para una pieza de equipo militar que está siendo transportada a través de la ciudad. Recógela para mí... + +[COL411:GENERA4] +y una vez que la tengas, quiero que me llames inmediatamente, entonces... + +[COL4_B4:GENERA4] +~g~El tanque está bloqueado. Busca un modo de engañar a los ocupantes. + +[COL4_1:GENERA4] +- ¿Que qué pasa con el artillero? - ¡No lo sé, señor! + +[COL4_4:GENERA4] +- Ve arriba soldado. - ¡Sí, señor! + +[COL4_B1:GENERA4] +~g~Ve y adquiere el vehículo militar que se está paseando por la ciudad. + +[COL4_B3:GENERA4] +~g~Deja el tanque en el escondite del Coronel antes que se autodestruya. + +[COL4_B6:GENERA4] +~g~¡Encuentra el modo de robar el tanque! + +[COL4_B7:GENERA4] +~g~Conduce el tanque al garaje. + +[COL4_B8:GENERA4] +~g~Bájate del tanque y sal del garaje. + +{=================================== MISSION TABLE GENERA5 ===================================} + +[COL5A_1:GENERA5] +Las circunstancias fuerzan una apresurada partida, amigo. + +[COL5A_2:GENERA5] +¿Cuál es el problema? + +[COL5A_3:GENERA5] +Los franceses quieren recuperar la tecnología de sus misiles y después del último incidente, + +[COL5A_4:GENERA5] +siento que es momento de encontrar puertos más seguros. + +[COL5A_5:GENERA5] +¿No sería más seguro volar? + +[COL5A_6:GENERA5] +Estaría muerto antes de alcanzar el mostrador de facturación de equipajes. Además, necesito sacar del país mi mercancía. + +[COL5A_7:GENERA5] +¿Necesitas otro guardaespaldas? + +[COL5A_8:GENERA5] +Tú, amigo mío, vales por diez guardaespaldas... + +[COL5B_1:GENERA5] +Thomas, me has protegido y servido bien. + +[COL5B_2:GENERA5] +Pero ahora debes dejarnos antes de que lleguemos a mar abierto. + +[COL5B_4:GENERA5] +Gracias, Coronel. + +[COL5B_5:GENERA5] +Una petición más, mientras estoy fuera, ¿podrías vigilar a Mercedes por mí? + +[COL5B_6:GENERA5] +Creo que ella puede cuidarse por sí misma, pero seguro, la vigilaré. + +[COL5B_7:GENERA5] +Gracias, amigo mío. Hasta luego. + +[COL5B_8:GENERA5] +Adiós, amigo. + +[COL5_7:GENERA5] +¡Deja de dispararme! + +[COL5_9:GENERA5] +¡Tommy, haz que dejen de dispararme! + +[COL5_10:GENERA5] +¡Tengo inmunidad diplomática! + +[COL5_11:GENERA5] +¡No disparen, soy un Coronel! + +[COL5_12:GENERA5] +Thomas, mátales, mi país te adorará. + +[COL5_13:GENERA5] +¡Tommy, los franceses nos superan! + +[COL5_14:GENERA5] +Tommy, mire a donde mire, hay franceses, ¡lo odio! + +[COL5_15:GENERA5] +Tommy, ¿cómo estás? + +[COL5_16:GENERA5] +¡Ésto es por Piaf y Gainesbourg y por vuestro estúpido pan francés! + +[COL5_1:GENERA5] +¡A babor! ¡A babor! + +[COL5_2:GENERA5] +¡Están atacando desde estribor! + +[COL5_3:GENERA5] +¡El puente está ahí delante! + +[COL5_4:GENERA5] +¡Tienen un helicóptero! + +[COL5_B1:GENERA5] +~g~Protege al Coronel y su yate a toda costa. + +[COL5_B2:GENERA5] +~g~Ponte delante y despeja la ruta del yate del Coronel. + +[COL5_B3:GENERA5] +~r~¡El Coronel está muerto! + +[COL5_B4:GENERA5] +~g~Destruye el helicóptero de ataque. + +[COL5B_3:GENERA5] +Bajaré mi lancha personal. Quédatela, amigo, en señal de mi agradecimiento. + +[COL5_B5:GENERA5] +~g~Dispara para abatir los helicópteros, no pongas en peligro el yate. + +[COL5_B6:GENERA5] +~g~Te has quedado sin munición, consigue más de las escaleras en la cubierta superior. + +[COL5_B7:GENERA5] +~g~Te estás quedando sin salud, consigue más de las escaleras en la cubierta superior. + +{=================================== MISSION TABLE HAIT1 ===================================} + +[HAM1_A:HAIT1] +¿Hola? ¿Hola? + +[HAM1_B:HAIT1] +Entra, cielo, y descansa los pies. + +[HAM1_C:HAIT1] +Tú debes de ser el gran hombre malo sobre el que ha estado hablando mi abuelo. + +[HAM1_D:HAIT1] +Me cuenta cosas sobre ti, ya sabes, cuando me visita, + +[HAM1_E:HAIT1] +y sobre los otros que te esperan. + +[HAM1_F:HAIT1] +Bien, todos llevamos muertos mucho tiempo, pero tú... + +[HAM1_G:HAIT1] +No me gustaría estar en tu pellejo. + +[HAM1_H:HAIT1] +Recibí un mensaje. Para que viniese aquí. + +[HAM1_I:HAIT1] +¿Puedes oírles? + +[HAM1_J:HAIT1] +Están diciendo tu nombre, deben quererte mucho, ¿no crees? + +[HAM1_K:HAIT1] +Ahora ayudarás a la tía Poulet y quizás ella te ayude. + +[HAM1_L:HAIT1] +Quizás ella pueda darte un pequeño talismán después de todo esto. + +[HAM1_M:HAIT1] +¿Darte algo de magia para echar el mal de ojo a la policía? + +[HAM1_N:HAIT1] +Mira, esto es todo muy... ¿darme qué? + +[HAM1_O:HAIT1] +Yo, yo, creo que recibí la dirección equivocada... + +[HAM1_P:HAIT1] +Haz esto por mí, Tommy... + +[HAM1_Q:HAIT1] +Los cubanos, tontos orgullosos y desagradables, + +[HAM1_R:HAIT1] +han estado causando problemas a mis encantadores chicos haitianos. + +[HAM1_S:HAIT1] +Ahora le han dicho a la policía donde he estado almacenando mis polvos. + +[HAM1_T:HAIT1] +Ellos piensan que es farlopa, pardillos. + +[HAM1_U:HAIT1] +Ahora sé un buen chico, Tommy, y ve a conseguir los polvos para la tía Poulet. + +[HAM1_V:HAIT1] +Sí, sí, seguro, seguro. + +[HAM1_1:HAIT1] +~g~Los polis se están acercando a nuestros escondites. Ve y consíguelos antes que ellos lo hagan. + +[HAM1_2:HAIT1] +~r~¡Los polis llegaron primero al escondite! + +[HAM1_3:HAIT1] +~g~¡Lleva este material de vuelta al escondite! + +[HAM1_4:HAIT1] +~g~¡Bien! ¡Ahora el siguiente! + +[HAM1_6:HAIT1] +~r~¡Ese almacén está destruido, idiota! + +[HAM1_7:HAIT1] +~g~¡Los polis han conseguido nuestro alijo! ¡Recupéralo antes de que se marchen! + +[HAM1_8:HAIT1] +~g~Los polis están de camino para recoger el alijo, ¡date prisa! + +[HAT_1A:HAIT1] +~g~¡No muevas ni un solo dedo! + +{=================================== MISSION TABLE HAIT2 ===================================} + +[HAT2_B1:HAIT2] +~g~Ve hasta la camioneta que contiene las bombas aéreas. + +[HAT2_B2:HAIT2] +Mata a los cubanos... + +[HAT2_B4:HAIT2] +¡Y destruye sus barcos! + +[HAT2_B5:HAIT2] +~g~Los cubanos están huyendo. ¡No les dejes escapar! + +[HAT2_B6:HAIT2] +~r~¡El avión RC está yendo demasiado lejos del alcance! + +[HAT2_B7:HAIT2] +~g~Uno de los cubanos está escapando en un coche. ¡No dejes ningún testigo! + +[HAT2_B8:HAIT2] +~r~¡No te quedan aviones RC! + +[HAT2_B9:HAIT2] +Aviones RC: + +[HAT2_1:HAIT2] +Oh. Lo siento, debo tener la dirección equivocada... + +[HAT2_2:HAIT2] +Bueno, también podrías entrar y descansar los pies y tomarte una taza de té. + +[HAT2_3:HAIT2] +¿Tienes algo ahí para mí? + +[HAT2_4:HAIT2] +Sí... + +[HAT2_5:HAIT2] +Este lugar me parece familiar. Un olor de la niñez... un deja vu... + +[HAT2_6:HAIT2] +Bien, Tommy, te voy a susurrar un pequeño recado para que lo hagas. Escúchame bien, ¿vale? + +[HAT2_7:HAIT2] +Te pareces a alguien que yo... + +[HAT2_8:HAIT2] +Los cubanos tienen barcos rápidos que usan para cruzar el charco cargados de polvo. + +[HAT2_9:HAIT2] +Es su sustento. + +[HAT2_10:HAIT2] +Mi sobrino ha estado preparando pequeñas bombas aéreas para eliminarles. + +[HAT2_11:HAIT2] +Vuela los barcos y conviértelos en madera de ataúd. + +[HAT2_12:HAIT2] +Muchas gracias por el té. + +[HAT2_B3:HAIT2] { reVC update } +Pulsa ~h~~k~~VEHICLE_FIREWEAPON~~w~ para soltar una bomba. Pulsa ~h~~k~~VEHICLE_ENTER_EXIT~~w~ para cancelar. + +{=================================== MISSION TABLE HAIT3 ===================================} + +[HAM3_A:HAIT3] +¿Hola? Hola, eh... Estaba buscando a alguien por aquí... + +[HAM3_B:HAIT3] +Pareces hambriento, Tommy, + +[HAM3_C:HAIT3] +¿Te conozco? + +[HAM3_D:HAIT3] +Silencio. + +[HAM3_E:HAIT3] +Una cosa más y te podré dejar marchar, Tommy. + +[HAM3_F:HAIT3] +Mis chicos están en guerra con los cubanos. + +[HAM3_G:HAIT3] +Pero sin armas. + +[HAM3_H:HAIT3] +Pero los cubanos tienen una sorpresa por llegar. + +[HAM3_I:HAIT3] +Mientras combaten en las calles, tú tomarás este rifle y los matarás en medio del alboroto. + +[HAM3_J:HAIT3] +Nadie te verá, nadie te oirá. + +[HAM3_K:HAIT3] +Tommy, si haces esto por mí, ya no estarás atado a los lazos de mi delantal. + +[HAM3_1:HAIT3] +~g~Debemos ganar esta batalla. Si todos los haitianos mueren, perdemos nosotros. + +[HAM3_3:HAIT3] +~g~Espero que los cubanos hagan trampas así que permanece alerta. + +[HAM3_4:HAIT3] +~r~¡Te han visto! ¡La misión ha fracasado! + +[HAM3_5:HAIT3] +~g~Debes matar a los cubanos desde lejos. No deben verte. + +[HAM3_8:HAIT3] +~g~¡Los haitianos están muriendo! Mejora tu puntería. + +[HAM3_7:HAIT3] +~g~¡Cuidado! Los cubanos han traído refuerzos. ¡¡Acaba con todos ellos!! + +[HAM3_2:HAIT3] +~r~¡Los haitianos han muerto! + +[HAM3_L:HAIT3] +Vale, tía... + +{=================================== MISSION TABLE HOTEL ===================================} + +[INTB_A:HOTEL] +¡Tommy! Tommy, ha pasado tanto tiempo. + +[INTB_B:HOTEL] +Hola, Sonny. + +[INTB_C:HOTEL] +Lo sé, lo sé. Te abruma la emoción. + +[INTB_D:HOTEL] +Quince años... si parece que fue ayer. + +[INTB_E:HOTEL] +Supongo que es cuestión de perspectiva. + +[INTB_F:HOTEL] +Eh, estar en el talego por la familia no es agradable, + +[INTB_G:HOTEL] +aunque la familia cuida de los suyos, ¿no es verdad? + +[INTB_H:HOTEL] +Y bien, cómo fue el negocio... ¿estás sentado en una pila de oro blanco? + +[INTB_I:HOTEL] +Mira, Sonny, nos tendieron una trampa. El negocio fue una emboscada. Harry y Lee están muertos. + +[INTB_J:HOTEL] +Me estás tomando el pelo, ¿no? Tommy. Dime que aún tienes el dinero. + +[INTB_K:HOTEL] +No Sonny... no tengo el dinero. + +[INTB_L:HOTEL] +Ése era mi dinero, Tommy, ¡mi dinero! + +[INTB_M:HOTEL] +¡Espero que no me andes jodiendo, Tommy, porque sabes que soy un hombre con el que no se puede jugar! + +[INTB_N:HOTEL] +Espera Sonny. + +[INTB_O:HOTEL] +Tienes mi garantía personal de que voy a conseguir recuperar el dinero y el alijo. + +[INTB_P:HOTEL] +Y te voy a enviar por correo las pelotas de los responsables. + +[INTB_Q:HOTEL] +Eh, ya lo sé. No eres tonto, Tommy, pero te prevengo, yo tampoco lo soy. + +[INTB_R:HOTEL] +Si se tratase de cualquier otro, ya estarías MUERTO. + +[INTB_S:HOTEL] +Pero como se trata de ti, como hemos hecho historia, voy a dejar que te ocupes de esto. + +[INTB_T:HOTEL] +Mira, Sonny, tienes mi palabra. + +[INTB_U:HOTEL] +Estaremos en contacto. + +{=================================== MISSION TABLE ICECRE1 ===================================} + +[ICC1_1:ICECRE1] +~g~Utiliza la furgoneta de los helados para distribuir perica en Vice City. + +[ICC1_3:ICECRE1] +~g~Recibirás dinero por cada venta que hagas, pero cuantas más transacciones hagas más atraerás la atención de la policía. + +[ICC1_4:ICECRE1] +~g~No hay ningún cliente en esta zona, inténtalo en otra. + +[ICC1_5:ICECRE1] +Negocios hechos: + +[ICC1_6:ICECRE1] +~g~Utiliza la furgoneta Mr. Whoopee para distribuir los productos Cherry Popper por Vice City. + +[ICC1_7:ICECRE1] +~g~Por cada transacción que hagas recibirás dinero, aunque cuantas más operaciones lleves a cabo más atención provocarás de la policía. + +[ICC1_8:ICECRE1] +~g~Para llevar a cabo el negocio ~h~aparca la furgoneta, ~g~y pulsa el ~h~~k~~VEHICLE_HORN~ ~g~para que suene la música que llamará la atención de la gente. + +[ICC1_9:ICECRE1] +~g~A las bandas locales no les gustará que hagas negocios en su territorio así que si haces esto, prepárate para hostilidades. + +[ICC1_10:ICECRE1] +~g~Has hecho ~1~ tratos! + +[ICC1_11:ICECRE1] +~g~Has hecho ~1~ trato! + +[ICC1_12:ICECRE1] +¡PROPIEDAD ADQUIRIDA! + +[ICC1_13:ICECRE1] +~r~¡No conseguiste ningún trato! + +[ICC1_14:ICECRE1] +FÁBRICA DE HELADOS CONSOLIDADA + +[ICC1_15:ICECRE1] +~g~La fábrica de helados generará ahora unos ingresos máximos de hasta ~1~ $. Asegúrate de recaudarlos regularmente. + +[ICC1_2:ICECRE1] +~g~Aparca la furgoneta y pulsa ~h~~k~~VEHICLE_HORN~~w~ para que suene la música indicando a los clientes que estás listo para hacer empezar. + +[ICC1_16:ICECRE1] +~g~Utiliza la furgoneta Mr. Whoopee para distribuir los productos Cherry Popper por Vice City. + +[ICE_AT1:ICECRE1] +FÁBRICA DE HELADOS CONSOLIDADA + +[ICE_AT2:ICECRE1] +~g~La fábrica Cherry Popper generará ahora unos ingresos hasta un máximo de ~1~ $. Asegúrate de recaudarlos de manera regular. + +[ICC1_17:ICECRE1] +Misión de Distribución finalizada + +[ICC1_18:ICECRE1] +Venta total de helados: ~1~ $ + +[ICC1_19:ICECRE1] +Total de negocios hechos: ~1~ + +{=================================== MISSION TABLE ICECUT ===================================} + +[ICC1_A:ICECUT] +¿Quién eres tú? + +[ICC1_B:ICECUT] +El nuevo propietario. + +[ICC1_C:ICECUT] +¿Fuiste ahora o en cualquier momento un niño? + +[ICC1_D:ICECUT] +¿De qué estás hablando? + +[ICC1_E:ICECUT] +¿Fuiste niño? + +[ICC1_F:ICECUT] +¡Sí! ¡Calma! ¿Qué pasa contigo? + +[ICC1_G:ICECUT] +Lo sabía. Un niño. + +[ICC1_H:ICECUT] +¡Un maldito, apestoso, lloriqueante, mocoso, vil, vomitivo y pequeño bebé chillón! + +[ICC1_I:ICECUT] +Bebés... Criaturas repugnantes, horribles y espantosas. + +[ICC1_J:ICECUT] +Mami no te quiere. ¡mierdecilla! + +[ICC1_K:ICECUT] +Cálmate. + +[ICC1_L:ICECUT] +ODIO los bebés, odio los niños. + +[ICC1_M:ICECUT] +Son unos pequeños guarros, llorones, mocosos, perversos y vomitivos... + +[ICC1_N:ICECUT] +¡Ya es suficiente! + +[ICC1_O:ICECUT] +¿Qué pasa contigo? + +[ICC1_P:ICECUT] +¿Fabricas helado, de acuerdo? Es básicamente para los niños. + +[ICC1_Q:ICECUT] +¿Qué clase de psicópata eres tú? + +[ICC1_R:ICECUT] +Solo para que lo entienda, ¿para qué hacer felices a los niños si los odias? + +[ICC1_S:ICECUT] +Oh, estúpido, apestoso, lloriqueante... + +[ICC1_T:ICECUT] +¡Cállate! + +[ICC1_U:ICECUT] +¡Mocoso! + +[ICC1_V:ICECUT] +El helado es una tapadera. + +[ICC1_W:ICECUT] +Distribuimos otros productos no lácteos. + +[ICC1_X:ICECUT] +Y si veo un niño, le saco provecho. + +[ICC1_Y:ICECUT] +¿Verdad, niños? Sí, sí, lo hago. Mamá no os quiere. + +[ICC1_Z:ICECUT] +¡Os ODIA! + +[ICC1_ZA:ICECUT] +¡PROPIEDAD ADQUIRIDA! + +{=================================== MISSION TABLE INTRO ===================================} + +[INT1_A:INTRO] +Tommy Vercetti... Mierda. + +[INT1_B:INTRO] +No pensé que le fueran a soltar nunca. + +[INT1_C:INTRO] +Mantuvo la cabeza gacha, ayuda a la gente a olvidar. + +[INT1_D:INTRO] +La gente lo recordará antes de lo que crees. + +[INT1_E:INTRO] +Cuando le vean paseando por las calles de su barrio. + +[INT1_F:INTRO] +Será malo para el negocio. + +[INT1_G:INTRO] +Bien, ¿qué vamos a hacer, Sonny? + +[INT1_H:INTRO] +Le tratamos como a un viejo amigo y le tenemos ocupado fuera de la ciudad. ¿De acuerdo? + +[INT1_I:INTRO] +Hemos estado hablando de expandirnos hacia el sur. ¿Verdad? + +[INT1_J:INTRO] +Hoy en día Vice City es oro de veinticuatro quilates. + +[INT1_K:INTRO] +Los colombianos, los mejicanos, demonios, + +[INT1_L:INTRO] +incluso esos refugiados cubanos se están repartiendo un buen pedazo de la acción. + +[INT1_M:INTRO] +Pero todo es por la nieve, Sonny. + +[INT1_N:INTRO] +¡Ninguna de las familias tocará esa mierda! + +[INT1_O:INTRO] +Los tiempos cambian. + +[INT1_P:INTRO] +Las familias no pueden seguir dando la espalda mientras nuestros enemigos recogen las recompensas. + +[INT1_Q:INTRO] +Así que enviaremos a alguien para que nos hagan el trabajo sucio + +[INT1_R:INTRO] +y tranquilamente nos llevaremos un buen cacho. ¿Vale? + +[INT1_S:INTRO] +¿Quién es nuestro contacto allí? + +[INT1_T:INTRO] +Ken Rosenberg, un estúpido abogado. + +[INT1_U:INTRO] +¿Cómo va a atar corto a Vercetti? + +[INT1_V:INTRO] +No necesitamos que lo haga. + +[INT1_W:INTRO] +Simplemente le soltaremos en Vice City + +[INT1_X:INTRO] +y le daremos un poco de dinero para que empiece. ¿De acuerdo? + +[INT1_Y:INTRO] +Le damos unos cuantos meses. + +[INT1_Z:INTRO] +Luego vamos, + +[INT1_A1:INTRO] +y le hacemos una pequeña visita, ¿de acuerdo? + +[INT1_A2:INTRO] +Para ver cómo le va. + +[INT2_A:INTRO] +¡Eh, eh, tíos! ¡Aquí, eh, Ken Rosenberg! ¡Je, je, genial! + +[INT2_B:INTRO] +Bueno, voy a llevaros a la reunión, ¿vale? + +[INT2_C:INTRO] +He hablado con los proveedores, y ellos están muy... + +[INT2_D:INTRO] +interesados en empezar una relación de negocios, así que... + +[INT2_E:INTRO] +si todo esto va bien, deberíamos... + +[INT2_F:INTRO] +empezar a obtener beneficios, lo cual es, ya sabes... + +[INT2_G:INTRO] +Bueno. + +[INT2_H:INTRO] +Entonces. Son hermanos, ¿vale? + +[INT2_I:INTRO] +Uno dirige el negocio, + +[INT2_J:INTRO] +y el otro se encarga de los vuelos. + +[INT2_K:INTRO] +Ahora operan fuera de Méjico + +[INT3_A:INTRO] +Bien, son los del helicóptero. + +[INT3_B:INTRO] +De acuerdo, este es el trato. + +[INT3_C:INTRO] +Quieren un intercambio directo en campo abierto. + +[INT3_D:INTRO] +¿De acuerdo? De acuerdo, permanece firme, vamos. + +[INT3_E:INTRO] +De acuerdo, ahora con cuidado. + +[INT3_F:INTRO] +Estoy aquí. ¡El coche está en marcha, nena! + +[INT3_G:INTRO] +¿Lo tienes? + +[INT3_H:INTRO] +Colombiana, 100% con un grado de pureza A, amigo. + +[INT3_I:INTRO] +¿Los verdes? + +[INT3_J:INTRO] +De diez y de veinte... usados. + +[INT3_L:INTRO] +¡Vamos, fuera de aquí! ¡Arranca! + +[INT4_A:INTRO] +¡Jodidos! ¡Estamos jodidos! + +[INT4_B:INTRO] +Muy típico, + +[INT4_C:INTRO] +saco la cabeza del agujero un jodido segundo, + +[INT4_D:INTRO] +¡Y el destino me echa la mierda a la cara! + +[INT4_E:INTRO] +Bueno, ¡jódete! + +[INT4_F:INTRO] +Cierra el pico y deja de quejarte. Estás vivo, ¿no? + +[INT4_G:INTRO] +Déjame justo aquí. + +[INT4_H:INTRO] +Ve y abandona el coche y luego vete a dormir un poco. + +[INT4_I:INTRO] +Mañana me pasaré por tu oficina y podemos empezar a solucionar esto. + +[INT4_J:INTRO] +De acuerdo, es una buena idea, iré a dormir un poco. + +[INT4_K:INTRO] +¿Qué vas a hacer tú. + +[INT4_L:INTRO] +Me las arreglaré para volver a mi hotel. + +[INT4_M:INTRO] +A despejar la mente y a pensar sobre esta mierda. + +[INT4_N:INTRO] +Vale. + +[INTRO1:INTRO] +Saco la cabeza del agujero un jodido segundo y el destino, ¡me echa la mierda en la cara! + +[INTRO2:INTRO] +Vete a dormir un poco. + +[INTRO3:INTRO] +¿Qué es lo que vas a hacer? + +[INTRO4:INTRO] +Mañana me pasaré por tu oficina y empezaremos a solucionar todo este desorden. + +[INT3_K:INTRO] +Creo que hemos llegado a un acuerdo, amigo mío. + +[INT3_M:INTRO] +Déjame ver. + +[INT2_L:INTRO] +no, no, no, espera... + +[INT3_N:INTRO] +¡Mierda! + +{=================================== MISSION TABLE KENT1 ===================================} + +[KPM1_A:KENT1] +A ver tío, te voy a salvar el culo, colega. + +[KPM1_B:KENT1] +¿De qué coño me estás hablando? + +[KPM1_C:KENT1] +Conoces a ese bastardo de Díaz, Don Farlopa. + +[KPM1_D:KENT1] +Tiene a tu colega, Lance. Dicen que tu amigo intentó atacarle por sorpresa... + +[KPM1_E:KENT1] +pero no fue lo suficientemente sorprendente, si entiendes lo que te quiero decir. + +[KPM1_F:KENT1] +¿A dónde le llevó? + +[KPM1_G:KENT1] +¡Tranquilo! Le hicieron cruzar la ciudad hasta el desguace. + +[KPM1_H:KENT1] +Maldita sea... ¡Chalado! + +[KPM1_2:KENT1] +~r~¡Se suponía que debías sacar con vida a Lance! + +[KPM1_3:KENT1] +SALUD DE LANCE: + +[RESC_1:KENT1] +¿Crees que puedes usar un arma? + +[RESC_2:KENT1] +Sí... supongo... yo también me alegro de verte. + +[RESC_3:KENT1] +Salgamos de aquí. + +[RESC_4:KENT1] +Ahí va todo mi cuidadoso plan, directo a la mierda, gracias a ti. La jodiste bien. + +[RESC_5:KENT1] +Mató a mi hermano. ¿Qué esperabas que hiciese, cortarle el césped? + +[RESC_6:KENT1] +Vamos a tener que eliminar a ese mamón de Díaz antes de que él nos elimine a nosotros. + +[RESC_7:KENT1] +Haz que te curen y encuéntrate conmigo en el puente que va a Star Island, ¿de acuerdo? + +[RESC_8:KENT1] +De acuerdo, entendido. + +[KPM1_1:KENT1] +~g~¡Lance está siendo retenido en el desguace, ve y libérale! + +[KPM1_4:KENT1] +~g~¡Lleva a Lance al hospital! + +[M_PASSN:KENT1] +¡MISIÓN SUPERADA! + +[KPM1_5:KENT1] +~g~¡Los hombres de Díaz te están buscando! Lleva a Lance al hospital. + +{=================================== MISSION TABLE KICKSTT ===================================} + +[KICK1_2:KICKSTT] +~r~¡No volviste a la moto lo suficientemente rápido! + +[KICK1_7:KICKSTT] +~r~¡Has destrozado la moto! + +[KICK1_8:KICKSTT] +~g~¡Sube a la moto! + +[KICK1_T:KICKSTT] +TIEMPO EMPLEADO: + +[KICKTM:KICKSTT] +~b~TIEMPO DE PRUEBA: ~1~:~1~ + +[KICKTM2:KICKSTT] +~b~TIEMPO DE PRUEBA: ~1~:0~1~ + +[GETBIKE:KICKSTT] +~g~Tienes ~1~ segundos para volver a subir a una moto antes de que termine la misión. + +[KICK1_1:KICKSTT] +~g~Completa el circuito lo más rápidamente posible. + +[KICK1_6:KICKSTT] +~g~¡Bien hecho! + +[KICK_10:KICKSTT] +~g~Utiliza la Sanchez para completar el circuito pasando por todos los puntos de control. + +[KICK_12:KICKSTT] +~r~¡La has cagado! + +[KICK_13:KICKSTT] +~r~¡Has tardado mucho tiempo! + +[KICK_11:KICKSTT] +~g~Para abandonar la misión, sitúate en el ~q~marcador rosa~g~. + +{=================================== MISSION TABLE LAWYER1 ===================================} + +[LAW1_A:LAWYER1] +Ve a dormir un poco, dice... + +[LAW1_B:LAWYER1] +¡Llevo sentado en esta silla toda la noche con las luces apagadas y bebiendo café! + +[LAW1_C:LAWYER1] +Esto es un desastre. ¡Estamos muy jodidos, tío! + +[LAW1_D:LAWYER1] +Estos gorilas, escúchame, van a venir aquí a arrancarme la cabeza. ¡Es ridículo! + +[LAW1_E:LAWYER1] +¡NO fui a la universidad para ésto! De acuerdo, ¿ahora qué coño vamos a hacer? + +[LAW1_F:LAWYER1] +Cállate, siéntate y relájate. Te diré lo que vamos a hacer. + +[LAW1_G:LAWYER1] +Vas a descubrir quién se llevó nuestra nieve... y después voy a cargármelos. + +[LAW1_H:LAWYER1] +Es una buena idea. Es una idea GENIAL. Déjame pensar, déjame pensar, déjame pensar. + +[LAW1_I:LAWYER1] +¡Oh! Está este Coronel retirado, el coronel Juan García Cortez. + +[LAW1_J:LAWYER1] +Él es el que me ayudó a montar esta operación + +[LAW1_K:LAWYER1] +bien lejos de los matones establecidos de Vice City. ¿Entendido? + +[LAW1_L:LAWYER1] +Bien, escucha. Está dando una fiesta en su lujoso yate en la bahía + +[LAW1_M:LAWYER1] +y todos los hombres más importantes de Vice City van a estar allí. + +[LAW1_N:LAWYER1] +Tengo invitación, por supuesto que tengo invitación, + +[LAW1_O:LAWYER1] +pero no sueñes que vaya a asomar la cabeza por esa puerta... ¡ni lo sueñes! + +[LAW1_P:LAWYER1] +¡Te lo dije, cállate! Iré yo mismo... + +[LAW1_Q:LAWYER1] +A mí también me gusta el estilo de 1978, pero esto no va a ser una fiesta con cerveza y tías en pelotas. + +[LAW1_R:LAWYER1] +Quiero decir, sin ánimo de ofender, creo que podrías hacer que la gente se volviese a mirar por razones equivocadas. + +[LAW1_S:LAWYER1] +¿Qué hay de malo con cómo voy vestido? + +[LAW1_T:LAWYER1] +Vale, mira, ten. Pásate por donde Rafael, dile que te envío yo, hará que parezcas respetable. + +[LAW1_U:LAWYER1] +Bien, ve, vamos... + +[LAWP_1:LAWYER1] +Buenas noches. + +[LAWP_2:LAWYER1] +Tengo entendido que viene de parte del Sr. Rosenberg, + +[LAWP_3:LAWYER1] +Espero que ningún problema reciente le haya afectado a su salud física, o uh, + +[LAWP_4:LAWYER1] +o mental, Sr... ¿eh? + +[LAWP_5:LAWYER1] +Vercetti. Tan sólo tiene un poco de... agorafobia. + +[LAWP_6:LAWYER1] +Excelente, excelente. ¿Y usted? + +[LAWP_7:LAWYER1] +Yo solo quiero mi farlopa. + +[LAWP_8:LAWYER1] +Ah. Ha sido un desgraciado cúmulo de circunstancias para todos los interesados. + +[LAWP_9:LAWYER1] +Por supuesto, he iniciado mis propias pesquisas + +[LAWP_10:LAWYER1] +pero un asunto tan delicado llevará tiempo. + +[LAWP_11:LAWYER1] +Quizás hablemos más tarde. ¿Eh? + +[LAWP_12:LAWYER1] +Mientras tanto, permítame presentarle a mi hija. + +[LAWP_13:LAWYER1] +¡Mercedes! + +[LAWP_14:LAWYER1] +Cara mía, ¿podrías acompañar a nuestro invitado mientras atiendo a mis obligaciones? + +[LAWP_15:LAWYER1] +Por supuesto, papá. + +[LAWP_16:LAWYER1] +Por favor, discúlpeme. + +[LAWP_17:LAWYER1] +¿Mercedes? + +[LAWP_18:LAWYER1] +Intenta acostumbrarte. + +[LAWP_19:LAWYER1] +En fin, déjame presentarte a algunos de nuestros invitados más distinguidos... + +[LAWP_20:LAWYER1] +Ese es nuestro congresista Alex Shrub con la sonriente estrella de la silicona Candy Suxxx... + +[LAWP_21:LAWYER1] +¿Conoces a mi encantadora esposa, Laura? ¿No? + +[LAWP_22:LAWYER1] +Bueno, desafortunadamente ella está en Alabama. Te presento a Candy. + +[LAWP_23:LAWYER1] +Y por ahí tenemos a la estirada estrella de las Mambas de Vice City, BJ. + +[LAWP_24:LAWYER1] +Siempre tan encantador. + +[LAWP_25:LAWYER1] +Le paré en seco, lo hice, ¡y le dejé en una silla de ruedas! + +[LAWP_26:LAWYER1] +¡Ja, ja, ésa es buena! + +[LAWP_27:LAWYER1] +Bien, ahora estoy mirando una verdadera propiedad de primera. + +[LAWP_28:LAWYER1] +Y ese anfibio al lado de la piscina es Jezz Torrent, + +[LAWP_29:LAWYER1] +cantante principal de Love Fist. + +[LAWP_30:LAWYER1] +¿Sabéis cómo juegan al ping-pong en Tailandia? + +[LAWP_31:LAWYER1] +Dejad que os lo cuente, + +[LAWP_32:LAWYER1] +¡no hace falta una pala precisamente! ¿entendéis lo que quiero decir? + +[LAWP_33:LAWYER1] +Impotente. + +[LAWP_34:LAWYER1] +Y el trío charlatán. + +[LAWP_35:LAWYER1] +Esa glándula sudorípara durmiente es la mano derecha de papá, González + +[LAWP_36:LAWYER1] +y los otros dos son el pastor Richards + +[LAWP_37:LAWYER1] +y el director de cine seudointelectual, Steve Scott. + +[LAWP_38:LAWYER1] +Pasión con las invasoras ninfómanas, + +[LAWP_39:LAWYER1] +entonces aparece el tiburón gigante y... + +[LAWP_40:LAWYER1] +¡Les arranca las pollas de un mordisco! + +[LAWP_41:LAWYER1] +Ja, bien. Nunca has visto nada igual, ¿Verdad? + +[LAWP_42:LAWYER1] +¡Coronel! + +[LAWP_43:LAWYER1] +¡Sus fiestas tienen siempre un gran éxito! + +[LAWP_44:LAWYER1] +Sólo puedo pedir disculpas por mi tardía llegada. + +[LAWP_45:LAWYER1] +Ah, de nada amigo. ¿Cómo nos encontramos? + +[LAWP_46:LAWYER1] +Nuestro negocio es muy estresante, siempre intentan desbancarnos. + +[LAWP_47:LAWYER1] +Hay un momento para recompensar a los amigos de uno y para liquidar a los enemigos, amigo. + +[LAWP_48:LAWYER1] +¿Quién es el bocazas? + +[LAWP_49:LAWYER1] +Ricardo Díaz, alias Don Farlopa. + +[LAWP_50:LAWYER1] +¡Mercedes! + +[LAWP_51:LAWYER1] +Oh, estaba a punto de llevar a mi amigo de vuelta a la ciudad. + +[LAWP_52:LAWYER1] +¡En otro momento, Ricardo! + +[LAWP_53:LAWYER1] +Salgamos de aquí. + +[LAWP_54:LAWYER1] +Mejor llévame al club Pole Position. + +[LAW1_2:LAWYER1] +~g~Ve al barco del Coronel. + +[LAW1_4:LAWYER1] +~r~¡Mataste a la hija del Coronel! + +[LAW1_5:LAWYER1] +¿Trabajarás para mi padre? + +[LAW1_6:LAWYER1] +Quizás. + +[LAW1_7:LAWYER1] +¿Te importa que pose mi mano en tu regazo? + +[LAW1_8:LAWYER1] +Quizás... + +[LAW1_9:LAWYER1] +Es tan difícil tener un padre rico y poderoso. Vamos. + +[LAW1_10:LAWYER1] +¡Nos vemos, guapo! + +[LAW1_11:LAWYER1] +Estoy seguro. + +[LAW1_12:LAWYER1] +Bonita moto. + +[LAW1_13:LAWYER1] +¡No! ¡Mi moto! + +[LAW1_3:LAWYER1] +~g~Lleva a la hija del Coronel al club Pole Position. + +[HELP20:LAWYER1] +Ve al icono de la ~h~camiseta~w~ que hay en el radar para encontrar la tienda de Rafael. + +[LAW1_14:LAWYER1] +¡Guau!, Me encantaría probar tu moto. + +[LAW1_15:LAWYER1] +Sí pequeño, pues recógela de Howlin' Pete's + +{=================================== MISSION TABLE LAWYER2 ===================================} + +[LAW2_A:LAWYER2] +¡Ah! Espero que te lo estés pasando bien. Porque yo me estoy volviendo loco de preocupación. ¿Qué has descubierto? + +[LAW2_B:LAWYER2] +Que hay más criminales en esta ciudad que en la cárcel. Necesitamos un confidente de las calles... + +[LAW2_C:LAWYER2] +Vale, déjame pensar, déjame pensar, déjame pensar... + +[LAW2_D:LAWYER2] +¡AH! ¡Lo tengo! + +[LAW2_E:LAWYER2] +Está este inglés, un baboso de la industria musical, + +[LAW2_F:LAWYER2] +se hace llamar Kent Paul. + +[LAW2_G:LAWYER2] +Bueno, tiene la cabeza tan metida en la mayoría de los culos de Vice City + +[LAW2_H:LAWYER2] +que si alguien sabe el paradero de 20 kilos de farlopa, + +[LAW2_I:LAWYER2] +es este tío, ¿de acuerdo? Siempre está en el Malibú. + +[LAW2_J:LAWYER2] +Le haré una visita. + +[LAW2B_A:LAWYER2] +¿De dónde saliste? + +[LAW2B_B:LAWYER2] +He estado buscando una chica como tú durante siglos, tía... + +[LAW2B_C:LAWYER2] +Kent Paul, tía. Por aquí soy el que manda. + +[LAW2B_D:LAWYER2] +Estoy buscando a un tipo inglés... + +[LAW2B_E:LAWYER2] +Muevo hilos, ¿entiendes lo que quiero decir? + +[LAW2B_F:LAWYER2] +Te invitaré. Lo que quieras, te lo conseguiré, chica. + +[LAW2B_G:LAWYER2] +No te preocupes por nada, tía. + +[LAW2B_H:LAWYER2] +Piérdete, guapa. + +[LAW2B_I:LAWYER2] +¡Eh, eh, eh, eh, eh! + +[LAW2B_J:LAWYER2] +¿Eres Kent Paul? Soy amigo de Rosenberg... + +[LAW2B_K:LAWYER2] +Rosenberg... Rosenberg... ¡Oh, ese chalado abogado de pacotilla! + +[LAW2B_L:LAWYER2] +¡Ese tipo podría defender a un inocente y mandarle directamente al corredor de la muerte! + +[LAW2B_M:LAWYER2] +Ponnos otra copa, hermano. + +[LAW2B_N:LAWYER2] +Todo el mundo es cómico. + +[LAW2B_O:LAWYER2] +Escúchame, he perdido veinte kilos y un montón de pasta... + +[LAW2B_P:LAWYER2] +¿Drogas, tío? Es un juego de tontos. + +[LAW2B_Q:LAWYER2] +¿Qué sabes al respecto? + +[LAW2B_R:LAWYER2] +¡Eh, eh! A lo que iba es que, + +[LAW2B_S:LAWYER2] +hay un chef-camello que tiene su negocio en la cocina de un hotel en Ocean Drive. + +[LAW2B_T:LAWYER2] +Últimamente parece muy satisfecho de sí mismo. ¿Podrías ir y tantearle? + +[LAW2B_U:LAWYER2] +Lo haré... y ya nos veremos. + +[LAW2B_V:LAWYER2] +Sí, está bien. Venga... vete, idiota. ¡Te voy a quitar las luces de un puñetazo! + +[LAW2B_W:LAWYER2] +Ponme una copa... ¡y dónde está esa zorra! + +[LAW2C_A:LAWYER2] +Oh, así se hace, tipo duro. Machácale. Eso debería hacerle muy parlanchín. + +[LAW2C_B:LAWYER2] +¿Tú también quieres un poco? + +[LAW2C_C:LAWYER2] +Eh, relájate. Quiero lo que tú quieres, hermano. + +[LAW2C_D:LAWYER2] +¿Oh, sí? ¿Y qué es eso? + +[LAW2C_E:LAWYER2] +Tu dinero... y la nieve de mi hermano muerto. Desgraciadamente, hiciste callar a nuestro contacto. + +[LAW2C_F:LAWYER2] +Los accidentes ocurren. Piérdete. + +[LAW2C_G:LAWYER2] +Eh, eh, guau. No hay necesidad de hacerte el llanero solitario conmigo. + +[LAW2C_H:LAWYER2] +Tal y como yo lo veo... somos dos hombres en una ciudad extraña. Necesitamos cubrirnos las espaldas mutuamente. + +[LAW2C_I:LAWYER2] +Mi espalda está bien, hermano... + +[LAW2C_J:LAWYER2] +¿Estás seguro de eso? Ten, toma esto... + +[LAW2C_K:LAWYER2] +¡Sígueme! + +[LAW2_1:LAWYER2] +¿Qué estás mirando? + +[LAW2_2:LAWYER2] +Mejor empieza a hablar... + +[LAW2_3:LAWYER2] +¡Oblígame, mamón! + +[LAW2_4:LAWYER2] +¡Por aquí! + +[LAW2_5:LAWYER2] +Voy a ver qué puedo averiguar. Te estaré vigilando, Tommy. + +[LAW2_6:LAWYER2] +~g~Ve al club Malibú y busca a Kent Paul. + +[LAW2_7:LAWYER2] +~g~Ve y busca al chef en Ocean Drive. + +[LAW2_10:LAWYER2] +~g~Regresa al hotel. + +[LAW2_11:LAWYER2] +~g~Recoge su teléfono móvil. + +[LAW2_12:LAWYER2] +¡Adquirido un teléfono móvil! Ahora puedes recibir llamadas telefónicas. + +[LAW2_13:LAWYER2] +~g~¡Has dejado a Lance atrás! ¡Ve y tráele! + +[LAW2_14:LAWYER2] +¡Tenemos que salir pitando de aquí! + +[GUN_2A:LAWYER2] +¡Mantén ~h~~k~~PED_LOCK_TARGET~ ~w~para ~h~apuntar automáticamente~w~ y pulsa ~h~~k~~PED_FIREWEAPON~ ~w~para ~h~disparar! + +[GUN_2C:LAWYER2] +¡Mantén ~h~~k~~PED_LOCK_TARGET~ ~w~para ~h~apuntar automáticamente~w~ y pulsa ~h~~k~~PED_FIREWEAPON~ ~w~para ~h~disparar! + +[GUN_2D:LAWYER2] +¡Mantén ~h~~k~~PED_LOCK_TARGET~ ~w~para ~h~apuntar automáticamente~w~ y pulsa ~h~~k~~PED_FIREWEAPON~ ~w~para ~h~disparar! + +[HELP17:LAWYER2] +Pulsa ~h~~k~~PED_FIREWEAPON~~w~ para atacar al chef. + +[HELP18:LAWYER2] +Pulsa ~h~~k~~PED_FIREWEAPON~~w~ para atacar al chef. + +[LAW3_11:LAWYER2] +Sitúate en el ~q~marcador rosa~w~ para ver las armas disponibles. + +[LAW3_12:LAWYER2] +Puedes seleccionar armas pulsando ~h~izquierda~w~ o ~h~derecha~w~ en los ~h~botones de dirección~w~. + +[LAW3_13:LAWYER2] +Si dispones de suficiente dinero, puedes comprar armas pulsando ~h~~k~~PED_SPRINT~~w~. + +[LAW3_14:LAWYER2] +Puedes salir de la tienda pulsando ~h~~k~~VEHICLE_ENTER_EXIT~~w~. + +[LAW3_15:LAWYER2] +Ve al ~h~icono de la pistola~w~ que hay en el radar para encontrar el ~h~Ammu-Nation~w~. + +[LAW2_15:LAWYER2] +~g~Ve a Ammu-Nation. + +[LAW2_K:LAWYER2] +Tómatelo con calma. + +[LAW2_16:LAWYER2] +Tienes que entender una cosa sobre esta ciudad. Tienes que saber protegerte. + +[LAW2_17:LAWYER2] +Venga, la tienda de armas local está a un par de manzanas de aquí. + +[LAW2_18:LAWYER2] +Tommy, cada hombre necesita un poco de rock de vez en cuando. + +[LAW2_19:LAWYER2] +Este de aquí es el club de striptease Pole Position. Quizás quieras entrar alguna vez. + +{=================================== MISSION TABLE LAWYER3 ===================================} + +[LAW3_A:LAWYER3] +¡Aagh! ¡Oh, por el amor de Dios, eres tú! Oh, Jesús, ¡voy a necesitar pantalones nuevos! + +[LAW3_B:LAWYER3] +Eh, esos psicópatas del norte han estado entrometiéndose y pronto vendrán aquí. + +[LAW3_C:LAWYER3] +Bien, ¿dónde está el maldito dinero? + +[LAW3_D:LAWYER3] +Relájate, relájate. Aún no hemos llegado a esa parte. + +[LAW3_E:LAWYER3] +Pensé que te estabas ocupando de esto, de verdad que sí. + +[LAW3_F:LAWYER3] +Y ahora esos tipos dicen que les tenemos que hacer un favor. + +[LAW3_G:LAWYER3] +Quieres decir que yo tengo que hacerles un favor. + +[LAW3_H:LAWYER3] +Oh, por supuesto, eso es lo que quiero decir. ¿Doy yo la impresión de poder intimidar a un jurado? + +[LAW3_I:LAWYER3] +No podría intimidar a un niño y créeme, lo he intentado. + +[LAW3_J:LAWYER3] +Ahora mira, es eso, o al primo de Forelli, Giorgio, le caerán cinco años por fraude. + +[LAW3_K:LAWYER3] +¡Tienes que encargarte de esos tipos! + +[LAW3_L:LAWYER3] +Comprendo. Ayuda al jurado a que cambie de idea. No te preocupes por ello. + +[LAW3_M:LAWYER3] +¡No no no no... NO! Ya intenté eso. El caso del jurado no fue tan bien, + +[LAW3_N:LAWYER3] +así que HAZ que cambien de idea. + +[LAW3_1:LAWYER3] +Giorgio envía sus saludos. + +[LAW3_2:LAWYER3] +Recuerda, ''culpable'' es una palabra fea. + +[LAW3_3:LAWYER3] +Inocente hasta que yo diga lo contrario. + +[LAW3_4:LAWYER3] +Sabes que no es culpable. + +[LAW3_5:LAWYER3] +¿Recuerdas a Giorgio? Recuerda que es inocente. + +[LAW3_6:LAWYER3] +No culpable. ¿Comprendido? Bien. + +[LAW3_8:LAWYER3] +~r~¡Mataste a un miembro del jurado! + +[LAW3_9:LAWYER3] +~g~¡Destroza el coche del miembro del jurado para sacarle de ahí! + +[HELP40:LAWYER3] +Puedes destrozar coches usando el martillo o un arma similar. + +[HELP41:LAWYER3] +o puedes aplastarlas con un vehículo + +[LAW3_20:LAWYER3] +~g~¡Destroza el coche del miembro del jurado! + +[LAW3_21:LAWYER3] +¡No puedo creer lo que está pasando! + +[LAW3_22:LAWYER3] +¡Increíble! + +[LAW3_23:LAWYER3] +¡Ya! ¡Ya! ¡Entendido! + +[LAW3_24:LAWYER3] +~g~Ese martillo será útil. + +[LAW3_7:LAWYER3] +~g~¡Ve y coacciona a los dos miembros del jurado pero NO los mates! + +[HELP23:LAWYER3] +Puedes ir al ~h~icono del martillo~w~ que hay en el radar si quieres comprar armas blancas en la ferretería. + +[LAW3_16:LAWYER3] +Estúpido cretino de Florida. + +[LAW3_17:LAWYER3] +¡Quítate de en medio! + +{=================================== MISSION TABLE LAWYER4 ===================================} + +[LAW4_A:LAWYER4] +Avery, ni que decir tiene... ¡Tommy! ¡Tommy! ¿Algún progreso? No, no, no... cuéntamelo luego, cuéntamelo luego. + +[LAW4_B:LAWYER4] +Tommy, éste es Avery Carrington... ¿Creo que os conocisteis en la fiesta? + +[LAW4_C:LAWYER4] +No en persona. + +[LAW4_D:LAWYER4] +¿Qué tal? + +[LAW4_E:LAWYER4] +Avery tiene una proposición. + +[LAW4_F:LAWYER4] +¿No tenemos otras cosas en mente? + +[LAW4_G:LAWYER4] +Estoy intentando mantener todo bajo control así que, ¿podrías darme un respiro? + +[LAW4_H:LAWYER4] +Estoy tenso como un alambre e incluso si estoy muerto al final de la semana, me gustaría pensar que no moriré pobre. + +[LAW4_I:LAWYER4] +Bien, calmaos, los dos. + +[LAW4_J:LAWYER4] +Hijo, si me ayudas, me encargaré de que cualquier capullo que te esté molestando se eche una buena siesta. + +[LAW4_K:LAWYER4] +De acuerdo, ¿qué puedo hacer por ti? + +[LAW4_L:LAWYER4] +Esta compañía de reparto tiene el almacén en unos terrenos de primera. No venderán. + +[LAW4_M:LAWYER4] +Están aguantando como una vieja rata de la pradera, así que tenemos que ir allí y ahumar a esas alimañas para hacerlas salir. + +[LAW4_N:LAWYER4] +Ve allí y alborota el avispero. + +[LAW4_O:LAWYER4] +Los de seguridad tendrán las manos ocupadas y entonces podrás colarte y dejarles fuera de combate. + +[LAW4_P:LAWYER4] +Y podrías pasarte por Rafael para cambiarte de ropa. Podrías estar allí un rato, pero sí, ve. + +[LAW4_Q:LAWYER4] +Debería ser un motín. + +[LAW4_R:LAWYER4] +Si las cosas salen como deberían, pásate alguna vez por mi oficina... + +[LAW4_1:LAWYER4] +Por favor, dispérsense. ¡La dirección discutirá cualquier queja de la forma apropiada! + +[LAW4_2:LAWYER4] +Por favor, dispérsense. ¡Vuelvan a sus casas! + +[LAW4_3:LAWYER4] +¡Por favor, dispérsense! ¡Esto no es apropiado! + +[LAW4_4:LAWYER4] +Por favor, dispérsense. Todos acabaréis en la calle. + +[LAW4_5:LAWYER4] +¡Desenfundad, chicos! ¡Vamos a romper unos cuantos cráneos comunistas! + +[LAW4_13:LAWYER4] +~g~Empieza a luchar con al menos 4 trabajadores para iniciar una revuelta. + +[LAW4_14:LAWYER4] +~g~¡Destruye las furgonetas del complejo! + +[HELP38:LAWYER4] +Si matas a alguien que lleva un arma, la dejará caer. + +[HELP39:LAWYER4] +Puedes disparar a los barriles explosivos, pero mantén la distancia. + +{=================================== MISSION TABLE MIAMI_1 ===================================} + +[T4X4_1A:MIAMI_1] +~g~Tienes ~1~ segundos para pasar por ~y~24~g~ puntos de control. ~g~Puedes atravesarlos en ~y~CUALQUIER ORDEN. + +[T4X4_1B:MIAMI_1] +~y~ATRAVIESA~g~ el primer punto de control para poner en marcha el ~r~CRONÓMETRO. + +[T4X4_1C:MIAMI_1] +¡~1~ de 24! + +[GETBIK1:MIAMI_1] +¡Tienes ~1~ segundos para subirte en una PCJ 600! + +[GETBIK3:MIAMI_1] +~r~Necesitas una PCJ 600 para realizar esta misión! + +{=================================== MISSION TABLE MM ===================================} + +[BLOD_04:MM] +ESTADO DEL COCHE: + +[BLOD_05:MM] +~g~TIEMPO OBJETIVO: ~1~ Minuto + +[BLOD_06:MM] +~g~TIEMPO OBJETIVO: ~1~ Minutos + +[BLOD_07:MM] +NUEVO Mejor Tiempo: ~1~ Segundos + +[BLOD_08:MM] +Coches destrozados: ~1~ + +[BLOD_09:MM] +~1~ $ + +[BLOD_10:MM] +¡GANADOR! + +[BLOD_01:MM] +Pasa por todos los puntos de control para aumentar tu tiempo total. + +[BLOD_02:MM] +Fracasarás si tu tiempo total llega a cero. + +[BLOD_03:MM] +¡Consigue que tu tiempo total sean superior al objetivo a superar! + +{=================================== MISSION TABLE OVALRIG ===================================} + +[HOTR_01:OVALRIG] +~g~La carrera dura 12 vueltas. Solamente los puestos 1, 2 y 3 obtienen premios en metálico. + +[HOTR_02:OVALRIG] +~g~Si destrozas tu coche serás descalificado. + +[HOTR_03:OVALRIG] +~g~Cuando tu coche sufra daños podrás repararlo en boxes. + +[HOTR_04:OVALRIG] +~g~Este es el camino para salir del estadio. + +[HOTR_05:OVALRIG] +Estado del coche: + +[HOTR_06:OVALRIG] +Vueltas: + +[HOTR_07:OVALRIG] +Nuevo mejor tiempo: ~1~:0~1~ + +[HOTR_08:OVALRIG] +Tiempo: ~1~:~1~ + +[HOTR_10:OVALRIG] +Tiempo de carrera: + +[HOTR_09:OVALRIG] +Posición: + +[HOTR_12:OVALRIG] +~r~¡Tu coche está destrozado! + +[HOTR_13:OVALRIG] +~r~¡No ganaste la carrera! + +[HOTR_14:OVALRIG] +~r~¡Has sido descalificado! + +[HOTR_15:OVALRIG] +Tiempo: ~1~:~1~ + +[HOTR_16:OVALRIG] +Tiempo: ~1~:0~1~ + +[HOTR_17:OVALRIG] +Mejor Tiempo: ~1~:~1~ + +[HOTR_18:OVALRIG] +Mejor Tiempo: ~1~:0~1~ + +[HOTR_19:OVALRIG] +Mejor Tiempo: NA + +[HOTR_20:OVALRIG] +Nuevo Mejor Tiempo: ~1~:~1~ + +[HOTR_21:OVALRIG] +Nuevo Mejor Tiempo: ~1~:0~1~ + +[HOTR_22:OVALRIG] +Mejor Resultado: NA + +[HOTR_23:OVALRIG] +Mejor resultado: 1 + +[HOTR_24:OVALRIG] +Mejor resultado: 2 + +[HOTR_25:OVALRIG] +Mejor resultado: 3 + +[HOTR_26:OVALRIG] +Mejor resultado: ~1~ + +[HOTR_27:OVALRIG] +Mejor Tiempo de Vuelta: ~1~.~1~ segundos + +[HOTR_28:OVALRIG] +Mejor Tiempo de Vuelta: ~1~.0~1~ segundos + +[HOTR_29:OVALRIG] +~1~ $ + +[HOTR_30:OVALRIG] +PUESTO: 1 + +[HOTR_31:OVALRIG] +PUESTO: 2 + +[HOTR_32:OVALRIG] +PUESTO: 3 + +[HOTR_33:OVALRIG] +Mejor tiempo de vuelta: ninguno + +[HOTR_11:OVALRIG] +Nuevo mejor tiempo de vuelta: ~1~,~1~ segundos + +[HOTR_34:OVALRIG] +Nuevo mejor tiempo de vuelta: ~1~,0~1~ segundos + +{=================================== MISSION TABLE PHIL1 ===================================} + +[PHIL1_A:PHIL1] +¿Phil? + +[PHIL1_B:PHIL1] +¡CORRE! + +[PHIL1_C:PHIL1] +¡Corre! + +[PHIL1_E:PHIL1] +Mierda Phil, ¿te bebes ese mejunje? + +[PHIL1_F:PHIL1] +Diablos, no tienes que bebértelo... + +[PHIL1_G:PHIL1] +solo tienes que olerlo para colocarte. + +[PHIL1_H:PHIL1] +Escucha, Phil, dijiste que podrías proporcionarme algo de potencia de fuego... + +[PHIL1_I:PHIL1] +Por supuesto. + +[PHIL1_J:PHIL1] +Hay un traficante de armas mejicano que me ha estado pisando el negocio últimamente. + +[PHIL1_K:PHIL1] +Suele realizar su ronda semanal a esta hora. + +[PHIL1_L:PHIL1] +Haz caer la mercancía de la parte de atrás de sus camiones antes de que acabe fuera de nuestro alcance. + +[PHIL1_M:PHIL1] +Y mientras estás en ello me estarás haciendo un favor. + +[PHIL1_N:PHIL1] +Luego acaba con él. + +[PHI1_01:PHIL1] +~g~Ve y pilla las armas de la parte trasera de los camiones del traficante. + +[PHI1_02:PHIL1] +~g~El traficante de armas ha perdido la carga. Destroza la caja y recoge el arma. + +[PHI1_03:PHIL1] +~g~Parece que han llamado pidiendo refuerzos. + +[PHI1_04:PHIL1] +~g~Ahora ve y acaba con el resto de los traficantes. + +[PHI1_HP:PHIL1] +Cuando utilices Detonador de Granadas, lanza una granada y después provoca la explosión en cualquier momento. + +[PHIL1_O:PHIL1] +¡Jooooodeeeeer! + +[PHIL1_D:PHIL1] +¡Nunca acerques una llama a uno de los alambiques de boomshine de Phil Cassidy! + +{=================================== MISSION TABLE PHIL2 ===================================} + +[PHIL2_A:PHIL2] +Hola, Phil, ¿cómo te va? + +[PHIL2_B:PHIL2] +Ehhh, Tommy. ¿Cómo estás? Hace tanto tiempo... + +[PHIL2_C:PHIL2] +Te juro que deberías dejar ese mejunje, tío. + +[PHIL2_D:PHIL2] +Huele como decapante de pintura. Hace que me ardan los ojos... + +[PHIL2_E:PHIL2] +Shhh, shhh, tú mismo, Tommy, + +[PHIL2_F:PHIL2] +y ven aquí porque hay algo que quiero enseñarte... algo. + +[PHIL2_G:PHIL2] +¡Dios! ¿Debería poder oler eso desde aquí? Me estoy mareando. + +[PHIL2_H:PHIL2] +No te preocupes por el olor, Tommy, mira esto. + +[PHIL2_I:PHIL2] +Las malditas pilas baratas o algo así. Hay más en el banco. + +[PHIL2_J:PHIL2] +¡TA-DAAA! + +[PHIL2_K:PHIL2] +¡Maldición! + +[PHI2_01:PHIL2] +~g~Rápido, lleva a Phil al hospital. + +[PHI2_03:PHIL2] +~r~¡Phil Cassidy está muerto! ¿Ahora quién va a suministrar armas en Vice City? + +[PHI2_05:PHIL2] +¡Al hospital no, tío! ¡Demasiados polis y vietcongs! + +[PHI2_06:PHIL2] +Hay un excirujano del ejército que me debe unos cuantos favores... y un cortador de césped. + +[PHI2_07:PHIL2] +Tiene un lugar llamado Little Havana, oh mira, un pez gigante. + +[PHI2_08:PHIL2] +¡Cuidado! ¡Charlies en la línea de los árboles! + +[PHI2_09:PHIL2] +¿Soy yo o las carreteras están hechas de gelatina? + +[PHI2_10:PHIL2] +Cuchara rota a madre gallina, ¿me recibes? + +[PHI2_11:PHIL2] +¡Cucharita, rita, rit, rit! + +[PHI2_12:PHIL2] +¡Viene a por mí, chico! + +[PHI2_13:PHIL2] +Hay alas de plumas negras aleteando por todas partes... + +[PHI2_14:PHIL2] +Es hermoso, tío... es hermoso... pero hace tanto frío... + +[PHI2_15:PHIL2] +10-4 tenemos un conductor borracho. + +[PHI2_04:PHIL2] +SALUD DE PHIL: + +[PHI_AS1:PHIL2] +LOCAL DE PHIL CONSOLIDADO + +[PHI_AS2:PHIL2] +~g~Nuevas armas disponibles para comprar en el Local de Phil. + +{=================================== MISSION TABLE PIZZA ===================================} + +[PIZ1_01:PIZZA] +~g~Ve a entregar estas pizzas, debes lanzar la pizza a los clientes. Pasa cerca para lanzar las pizzas. + +[PIZ1_02:PIZZA] +~g~Has lanzado todas tus pizzas, vuelve y recoge algunas más. + +[PIZ1_05:PIZZA] +~g~Tienes cinco minutos para entregar los pedidos antes de que los clientes telefoneen a otra pizzería. + +[PIZ1_07:PIZZA] +~r~¡Mataste al cliente! Estás despedido. + +[PIZ1_08:PIZZA] +~r~Te has quedado sin tiempo. Estás despedido. + +[PIZ1_09:PIZZA] +~r~¡Destruiste nuestra moto! Estás despedido. + +[PIZ1_11:PIZZA] +¡Eh! ¡Vuelve a subir a la moto! + +[PIZ1_12:PIZZA] +Pizzas restantes: + +[PIZ1_06:PIZZA] +Pulsa ~h~~k~~TOGGLE_SUBMISSIONS~~w~ cuando estés en la moto para cancelar la misión. + +[PIZ1_13:PIZZA] +Reparte éstas bien calentitas. + +[PIZ1_14:PIZZA] +Amigo, te toca repartir éstas. + +[PIZ1_15:PIZZA] +A ver, tío, repártelas rápido. + +[PIZ1_16:PIZZA] +¿Qué estás esperando, tío? Tienes pizzas que repartir. + +[PIZ1_17:PIZZA] +Ya sé que no querías ser el chico de las pizzas, vale me importa un cuerno. + +[PIZ1_18:PIZZA] +Reparte éstas. + +[PIZ1_19:PIZZA] +Hay que repartir éstas. + +[PIZ1_20:PIZZA] +Venga hombre, reparte estas cosas o estás despedido. + +[PIZ1_21:PIZZA] +Tenemos gente esperando amigo. + +[PIZ1_22:PIZZA] +¿Qué estás esperando? ¡Hay que repartir esto! + +[PIZ1_23:PIZZA] +Haz el reparto de la maldita comida hombre. + +[PIZ1_24:PIZZA] +Hay que repartir éstas, amigo. + +[PIZ1_25:PIZZA] +Tío, ¿te puedes llevar éstas? + +[PIZ1_26:PIZZA] +Oye, haz el reparto pronto, vamos amigo. + +[PIZ1_27:PIZZA] +Venga, tenemos prisa, haz ya el reparto. + +[PIZ1_28:PIZZA] +¿Otra vez tú? Bueno, reparte éstas deprisa, amigo. + +[PIZ1_29:PIZZA] +No pierdas el tiempo amigo. + +[PIZ1_30:PIZZA] +Venga ya vago hijo de puta, reparte esta mierda de una vez. + +[PIZ1_31:PIZZA] +No tendrás nunca un ascenso a menos que te muevas más rápido esta vez. + +[PIZ1_32:PIZZA] +~r~¿La pizza es demasiado caliente para encargarte de ella? + +[PIZ1_33:PIZZA] +~g~Vuelve al restaurante a por más pedidos. + +[PIZ1_34:PIZZA] +~g~Pizza repartida, aquí está tu dinero. + +[PIZ_WON:PIZZA] +Misión de Pizza Completada. Tu salud Personal máxima ha aumentado a 150. + +{=================================== MISSION TABLE PORN1 ===================================} + +[POR1_A:PORN1] +Acción. + +[POR1_B:PORN1] +¡Guau! Eso sí que es grande. + +[POR1_C:PORN1] +30 cm, eso es para pensárselo, nene. + +[POR1_D:PORN1] +¡CORTEN! ¿Quién es este idiota? ¡Tú! ¡Tú! ¿Por qué estás en el set? ¿POR QUÉ? + +[POR1_E:PORN1] +¿De qué va toda esta basura? + +[POR1_F:PORN1] +¿Alienígenas? ¿Cañas de pescar? + +[POR1_G:PORN1] +¿Quién ha visto alguna vez a un tiburón tan grande? + +[POR1_H:PORN1] +Hay que desechar todo este material. + +[POR1_I:PORN1] +¿Por qué te metiste en este negocio, mamón? + +[POR1_J:PORN1] +¿Eh? + +[POR1_K:PORN1] +¡Por los chochos, evidentemente! ¿Qué es esto? + +[POR1_L:PORN1] +Éste es mi arte. ¡SEGURIDAD! + +[POR1_M:PORN1] +Mira, pomposo gilipollas, ahora eres de mi propiedad. Yo poseo todo esto. + +[POR1_N:PORN1] +Vamos a cambiar este lugar... + +[POR1_O:PORN1] +Voy a hacerte rico. + +[POR1_P:PORN1] +Usted es... Usted... ¿Usted es Tommy Vercetti? Pero yo creí que usted era... + +[POR1_Q:PORN1] +Eso es. + +[POR1_R:PORN1] +Vamos a hacer algunos cambios por aquí y vamos a empezar a hacer dinero de verdad. + +[POR1_S:PORN1] +En realidad, alguna vez has pensado en... + +[POR1_T:PORN1] +Pero primero, vamos a necesitar unas chicas guapas de verdad... + +[POR1_U:PORN1] +Sí, las chicas están bien, pero tú... ¡guau! + +[POR1_02:PORN1] +~g~ Ve y elimina al chulo de Candy, luego vuelve y recoge a Candy. + +[POR1_04:PORN1] +Candy. Estoy buscando una estrella de cine. ¿Estás interesada? + +[POR1_05:PORN1] +¡Claro! Pero tendrás que hablar con mi agente... + +[POR1_06:PORN1] +¿Qué DEMONIOS estás haciendo? + +[POR1_07:PORN1] +¡Hoy deberías haberte quedado en casa! + +[POR1_7B:PORN1] +¿Puedes creer a este gilipollas? + +[POR1_08:PORN1] +¡Eh, Mercedes! + +[POR1_09:PORN1] +¡Eh, Tommy! ¿Quieres ir de fiesta? + +[POR1_10:PORN1] +Ahora no, preciosa. ¿Te interesa hacer algunas películas? + +[POR1_11:PORN1] +Por supuesto. Mientras que sea una peli barata y cutre. + +[POR1_13:PORN1] +~g~Lleva a las chicas de vuelta al Estudio para que conozcan a Steve. + +[POR1_14:PORN1] +¡Contratada! + +[POR1_15:PORN1] +Eh Tommy, ¿¡qué, vienes a entrar en calor!? + +[POR1_17:PORN1] +¡Guau, un tiburón genial! + +[POR1_18:PORN1] +~r~¡Mercedes está muerta! + +[POR1_20:PORN1] +Tommy, ¿a dónde vas? ¡Vuelve aquí! + +[POR1_21:PORN1] +¿A dónde vas? + +[POR1_22:PORN1] +Tommy, ¿cuando vamos a pasar algún tiempo a solas? + +[POR1_01:PORN1] +~g~¡Candy Suxxx sería perfecta para el papel principal! + +[POR1_12:PORN1] +~g~Ve con Candy a encontrarte con Mercedes. + +[POR1_16:PORN1] +Quizás después, nena... + +[POR1_24:PORN1] +~g~Vuelve y recoge a Candy. + +[POR1_25:PORN1] +~g~Te has dejado atrás a Candy, ve y tráela. + +[POR1_23:PORN1] +~g~Candy estará ocupándose de unos asuntos en el ~h~centro de la ciudad~g~. + +[POR1_26:PORN1] +~g~Aquí está Candy, parece que ha estado otra vez con el congresista Shrub. + +[POR1_27:PORN1] +Venga, vamos. + +[POR1_28:PORN1] +¡Tommy ten cuidado! ¡Aún no he asegurado mis implantes! + +[POR1_29:PORN1] +¿Y a esto le llamas conducir? + +[POR1_30:PORN1] +¡No podré hacer ningún numerito porno después de esto! + +[POR1_31:PORN1] +¿Qué? ¿Me estás intentando matar? ¡Creía que era la estrella era yo! + +{=================================== MISSION TABLE PORN2 ===================================} + +[POR2_A:PORN2] +¿Cómo va el rodaje, Steve? + +[POR2_B:PORN2] +Bien, Candy es una fuera de serie y esa chica nueva ¡es insaciable! + +[POR2_C:PORN2] +Se repasó a la mitad del reparto antes de que pudiese medir la luz. + +[POR2_D:PORN2] +De todas formas, eh, mañana vamos a exteriores a rodar las escenas del barco... + +[POR2_E:PORN2] +¿Escenas del barco, qué escenas del barco? + +[POR2_F:PORN2] +Los pescadores son presa de la pasión cuando aparece el tiburón gigante... + +[POR2_G:PORN2] +¿Qué te dije del tiburón gigante? + +[POR2_H:PORN2] +Dije, ''SIN TIBURÓN GIGANTE'', ¿de acuerdo? + +[POR2_I:PORN2] +¡Tú solo tienes que mantener las cámaras apuntando a los conejos! + +[POR2_J:PORN2] +De acuerdo, de acuerdo, eh Tommy, uno tiene que intentarlo, ¿de acuerdo? + +[POR2_K:PORN2] +¿Has hecho que impriman esos folletos? + +[POR2_L:PORN2] +Sí, pero nadie va a distribuir esas cosas, quiero decir que... + +[POR2_M:PORN2] +son demasiado... poco imaginativos. + +[POR2_N:PORN2] +No te preocupes por eso. + +[POR2_O:PORN2] +Tengo mis propias ideas para la distribución. + +[POR2_P:PORN2] +Vale. Candy, ven a mi caravana. + +[POR2_01:PORN2] +~g~Hay un hidroavión que se utilizó como atrezzo en una antigua película independiente a la vuelta de los estudios. + +[POR2_02:PORN2] +~g~Escoge uno de los puntos de control para empezar a dejar caer los folletos. + +[POR2_03:PORN2] +~g~Deja caer los folletos por todo el camino hasta el último punto de control. + +[POR2_04:PORN2] +~r~¡COMBUSTIBLE BAJO! + +[POR2_05:PORN2] +Utilízalo para distribuir los folletos por toda la ciudad. + +[DILDO:PORN2] +Combustible del Skimmer: + +[POR2_Q:PORN2] +¡Jo!, tío. + +[PORN2_9:PORN2] +~g~Tienes ~1~ segundos para volver a subir a una Skimmer antes de que termine la misión. + +{=================================== MISSION TABLE PORN3 ===================================} + +[POR3_A:PORN3] +Vale, ¿cuál es el problema ahora? + +[POR3_B:PORN3] +¡SSShhhh! + +[POR3_C:PORN3] +Bien, después de este encuentro cercano con las invasoras ninfómanas + +[POR3_D:PORN3] +nuestro héroe se encuentra incapaz de pensar en nada excepto en esta enorme montaña fálica... + +[POR3_E:PORN3] +y es entonces cuando queremos hacer la escena con la tina de puré de patatas, pero entonces... + +[POR3_F:PORN3] +¡Me importa una mierda! + +[POR3_G:PORN3] +¡Sigue, sigue! + +[POR3_H:PORN3] +Eh Tommy... + +[POR3_I:PORN3] +¿Mencionaste algo sobre un problema legal al teléfono? + +[POR3_J:PORN3] +¡Vaya que sí! El congresista Alex Shrub se ha subido al tren preelectoral y va tras el voto puritano. + +[POR3_K:PORN3] +Hay rumores de que va a apoyar medidas restrictivas, digamos, + +[POR3_L:PORN3] +para el aspecto más lujurioso de la gran industria del entretenimiento de esta nación. + +[POR3_M:PORN3] +Genial. + +[POR3_N:PORN3] +¡Candy! Tú conoces a Shrub, + +[POR3_O:PORN3] +¿llegasteis a algo pervertido? + +[POR3_P:PORN3] +¡Oh sí, oh sí, oh sí! ¡Sí sí sí SÍ! ¡OOOoooh! + +[POR3_Q:PORN3] +Por favor dime que filmaste eso. + +[POR3_R:PORN3] +¿Eso era parte del eh... o estaba hablando conmigo...? + +[POR3_S:PORN3] +Eh, uno nunca podría decirlo... De todas formas... + +[POR3_T:PORN3] +Probablemente sea mejor que la sigas después del rodaje, + +[POR3_U:PORN3] +para ver si te lleva a su nuevo nido de amor. + +[POR3_V:PORN3] +¿Tienes una cámara? + +[POR3_X:PORN3] +Vale, conseguidle una cámara. + +[POR3_02:PORN3] +~r~¡Has asesinado al Congresista! Ahora no habrá modo de que puedas chantajearle. + +[POR3_03:PORN3] +~r~Has alertado a la protección del Congresista, le sacarán de aquí inmediatamente. + +[POR3_04:PORN3] +Candy, ¿podrías llamarme Marta? + +[POR3_05:PORN3] +Oh Alex, quiero decir Marta. Lo que tú digas. + +[POR3_06:PORN3] +Marta, alguien está mirando... qué pervertido. + +[POR3_07:PORN3] +Tú, el de ahí. Dame esa cámara. + +[POR3_01:PORN3] +~g~Sigue la ~h~limusina~g~ de Candy. + +[POR3_15:PORN3] +~r~¡Has destruido la limusina de Candy! + +[POR3_17:PORN3] +~g~Regresa a la compañía de pornografía con las fotos. + +[POR3_19:PORN3] +~r~¡Te has quedado sin película! + +[POR3_21:PORN3] +~g~¡Has perdido la limusina de Candy! + +[POR3_22:PORN3] +~g~El Hotel WK Chariot frente a este balcón debería proporcionar el lugar ideal para la sesión fotográfica. + +[POR3_23:PORN3] +~g~Hay una puerta lateral que te permitirá acceder al hotel. + +[POR3_08:PORN3] +Mantén pulsado ~h~~k~~PED_LOCK_TARGET~ ~w~para~h~ enfocar~w~ con la cámara. + +[POR3_09:PORN3] +Mantén pulsado ~h~~k~~PED_LOCK_TARGET~ ~w~para~h~ enfocar~w~ con la cámara. + +[POR3_10:PORN3] +Pulsa ~h~~k~~PED_SNIPER_ZOOM_IN~ ~w~para ~h~acercar el zoom ~w~con la cámara y ~h~~k~~PED_SNIPER_ZOOM_OUT~ ~w~para ~h~alejarlo ~w~de nuevo. + +[POR3_11:PORN3] +Pulsa ~h~~k~~PED_SNIPER_ZOOM_IN~ ~w~para ~h~acercar el zoom ~w~con la cámara y ~h~~k~~PED_SNIPER_ZOOM_OUT~ ~w~para ~h~alejarlo ~w~de nuevo. + +[POR3_12:PORN3] +Pulsa ~h~~k~~PED_FIREWEAPON~ ~w~para sacar una foto. + +[POR3_13:PORN3] +Pulsa ~h~~k~~PED_FIREWEAPON~ ~w~para sacar una foto. + +[POR3_14:PORN3] +Pulsa ~h~~k~~PED_FIREWEAPON~ ~w~para sacar una foto. + +[POR3_20:PORN3] +~g~Si necesitas transporte, utiliza el ~h~Sparrow~g~ que está en la parte de atrás. + +[POR3_16:PORN3] +~g~Necesitas tres buenas fotos de Alex Shrub con Candy para chantajearle. + +[POR3_24:PORN3] +FOTOS HECHAS: + +{=================================== MISSION TABLE PORN4 ===================================} + +[POR4_A:PORN4] +Lo siento, pero no puedo tragarme eso ahora. + +[POR4_B:PORN4] +¡Oh, VENGA, cariño! + +[POR4_C:PORN4] +La tiene como un cachalote, por el amor de Dios, + +[POR4_D:PORN4] +¿cómo es que no puedes sentir el papel? + +[POR4_E:PORN4] +Pero Stevie... + +[POR4_F:PORN4] +¿Cómo está mi director estrella? + +[POR4_G:PORN4] +Oh, tío. La lucha entre la integridad artística y + +[POR4_H:PORN4] +la acción cargante y bombeante continúa sin perder intensidad. + +[POR4_I:PORN4] +Y antes de que puedas preguntarlo, sí, los cuatro vídeos serán lanzados en sus... + +[POR4_J:PORN4] +Cariño, podrías POR FAVOR mantener la anaconda dentro del plano, + +[POR4_K:PORN4] +¡cuesta por hora más de lo que cuestas tú! + +[POR4_L:PORN4] +Oh, lo siento, Steve. + +[POR4_M:PORN4] +Estaba pensando, necesitamos algún tipo de gran truco publicitario para promover de verdad el lanzamiento. + +[POR4_N:PORN4] +Algo que tenga un verdadero impacto en la ciudad, ¿tienes alguna idea? + +[POR4_O:PORN4] +Bueno, en los viejos tiempos solían organizar fiestas, + +[POR4_P:PORN4] +estrellas, limusinas, el cielo nocturno con los focos entrecruzados... + +[POR4_Q:PORN4] +¿Focos? Tengo una idea... + +[POR4_R:PORN4] +...sí, sí, sí. Los numeritos de lentejuelas, y las limusinas, oh, premieres. + +[POR4_S:PORN4] +Oh, sí, madam, por supuesto madam, + +[POR4_T:PORN4] +y la prensa, y el bombardeo de luces... + +[POR4_01:PORN4] +~g~Ve al ~y~centro~g~ y ajusta los focos sobre lo alto del edificio. + +[POR4_02:PORN4] +~g~Necesitarás una moto rápida para saltar de un tejado a otro. El guardia de seguridad normalmente lleva una ~y~PCJ 600~g~ para ir a trabajar... + +[POR4_03:PORN4] +~g~Necesitarás subir a los tejados de los edificios. Debería de haber un ascensor que lleve a una de las oficinas superiores... + +[POR4_06:PORN4] +~g~Vuelve a la oficina inferior si de nuevo necesitas acceso a los tejados. + +[POR4_07:PORN4] +~g~Necesitarás una moto para poder saltar de un edificio a otro. + +[POR4_08:PORN4] +~g~Atraviesa la ventana para empezar el recorrido. Tienes hasta las 07:00 antes de que haya demasiada luz como para subir allí sin ser visto. + +[POR4_09:PORN4] +~g~Los marcadores rosas te mostrarán a qué edificio saltar después. + +[POR4_10:PORN4] +~r~Hay demasiada luz como para subir ahí sin ser visto. + +[POR4_11:PORN4] +~g~Vuelve a la escalera si necesitas acceder de nuevo a los tejados. + +[POR4_05:PORN4] +~g~Estas escaleras llevan a una oficina en el piso inferior. + +[POR_AS1:PORN4] +ESTUDIO CINEMATOGRÁFICO CONSOLIDADO + +[POR_AS2:PORN4] +~g~Inter Global Films generará ahora unos ingresos máximos de hasta ~1~ $. Asegúrate que lo recaudas de forma regular. + +{=================================== MISSION TABLE PROT1 ===================================} + +[PRO1_B:PROT1] +No puedo soportar este look. Tommy, ¿Tú que dices? ¿Qué te parece si ponemos un bar en... + +[PRO1_D:PROT1] +Escúchame, + +[PRO1_E:PROT1] +Es hora de tomar esta ciudad. Está ahí, esperándonos. + +[PRO1_F:PROT1] +Necesitamos empezar a ampliar nuestro territorio, + +[PRO1_G:PROT1] +vamos a dejar que Vice City sepa que somos los nuevos tíos importantes de la ciudad, ¿sabes a lo que me refiero? + +[PRO1_I:PROT1] +Lo que necesitas es un frente legítimo, Tommy, una verdadera propiedad. A mí nunca me ha hecho daño. + +[PRO1_J:PROT1] +Necesitamos empezar a utilizar la fuerza o podemos despedirnos de todo el trabajo duro que hemos hecho. + +[PRO1_K:PROT1] +¡Los negociantes locales saben que Díaz está muerto, y se niegan a pagar la protección! + +[PRO1_L:PROT1] +Oh. Podríamos intentar el soborno... + +[PRO1_M:PROT1] +¿Soborno? ¡Que le jodan al soborno! Te mostraré cómo hacer que se asusten. + +[PRO1_01:PROT1] +~g~Ve a atracar los escaparates de las tiendas y tendrás a los propietarios suplicando protección. + +[PRO1_03:PROT1] +~r~Se suponía que deberías atacar y salir corriendo, no atacar y tomarte un café. + +[PRO1_04:PROT1] +¡Mi sustento, destruido! + +[PRO1_05:PROT1] +¡Arruinado... ESTOY ARRUINADO! + +[PRO1_06:PROT1] +¡Pagaré lo que sea por protección! + +[PRO1_07:PROT1] +¡Mi hermoso escaparate destruido! + +[PRO1_08:PROT1] +Mi tienda. Mi maravillosa tienda. + +[PRO1_09:PROT1] +Vercetti. Recuerda el nombre. + +[PRO1_10:PROT1] +Ahora dirijo esta ciudad. ¡YO! + +[BUYP1:PROT1] +Ahora puedes comprar propiedades en ciertas zonas de la ciudad. + +[BUYP2:PROT1] +Si ves un indicador con una casa verde, puedes comprar esa propiedad. + +[PRO1_N:PROT1] +Volveré aquí en cinco minutos... + +[PRO1_11:PROT1] +~g~Ve al ~y~Centro Comercial North Point~g~ en ~y~Vice Point~g~. + +[PRO1_12:PROT1] +~g~Destroza los escaparates de todas las tiendas y los propietarios te suplicarán para obtener nueva protección. + +[PRO1_A:PROT1] +Oh, tenemos que volver a pintar este sitio. Tenemos que hacer que parezca más viejo. + +[PRO1_C:PROT1] +Rosenberg, eres mi abogado, no mi diseñador de interiores, ¿lo entiendes? + +[BUYP3:PROT1] +Sitúate en el marcador, a continuación pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la propiedad. + +[PRO1_13:PROT1] +~g~Dispones de cinco minutos para machacarlos a todos. + +{=================================== MISSION TABLE PROT2 ===================================} + +[PRO2_A:PROT2] +¿Cuál es el problema? + +[PRO2_B:PROT2] +Un bar se niega a pagar. + +[PRO2_C:PROT2] +Creen que están protegidos por una banda de matones locales. + +[PRO2_D:PROT2] +Pero no te preocupes, Tommy, yo me encargo. + +[PRO2_E:PROT2] +¿Llamas a esto encargarte? + +[PRO2_F:PROT2] +Vosotros dos, en pie... + +[PRO2_G:PROT2] +Vamos. + +[PRO2_01:PROT2] +~g~Elimina a los guardias que vigilan el Bar Front Page y descubre quién los ha puesto ahí. + +[PRO2_10:PROT2] +~g~Dos más han huido. Sígueles la pista y acaba con ellos. + +[PRO2_11:PROT2] +Sube al coche, inútil. + +[PRO2_02:PROT2] +Tu seguridad necesita un poco más de seguridad. + +[PRO2_03:PROT2] +¡Ahh! demonios, ¡otra vez no! ¡no necesito esta basura! + +[PRO2_04:PROT2] +Estos idiotas pertenecen al DBP de Seguridad que hay a la vuelta de la esquina. + +[PRO2_05:PROT2] +¡Eh tíos! Arregladlo entre vosotros. + +[PRO2_06:PROT2] +Nos vemos en un rato. + +[PRO2_07:PROT2] +Sí, vale, lo que tú digas. + +[PRO2_08:PROT2] +~g~El DBP de Seguridad sabrá que estás de camino, ve y atrápalos antes que escapen. + +[PRO2_09:PROT2] +~g~Ve y habla con el dueño del bar Front Page. + +{=================================== MISSION TABLE PROT3 ===================================} + +[PRO3_A:PROT3] +¡Idiota! ¿En qué estabas pensando? + +[PRO3_B:PROT3] +¿Te das cuenta de lo que significa ésto? + +[PRO3_C:PROT3] +¡Podríamos estar todos hundidos! + +[PRO3_D:PROT3] +El temporizador debe haberse jodido. + +[PRO3_E:PROT3] +Ese lugar estaba preparado para estallar como una fábrica de fuegos artificiales. + +[PRO3_F:PROT3] +Entonces alguien avisó a la poli... + +[PRO3_G:PROT3] +¿Cuál es el problema, colegas? + +[PRO3_H:PROT3] +Se suponía que Mike iba a quemar algún sitio en el centro comercial, + +[PRO3_I:PROT3] +pero la cagó con los fusibles y ahora eso está plagado de polis. + +[PRO3_J:PROT3] +¡Tenemos que conseguir nuestras cosas y salir de aquí! + +[PRO3_K:PROT3] +¡Relajaos los dos, dejadme pensar un segundo! + +[PRO3_L:PROT3] +Tommy Vercetti no corta por lo sano y huye. + +[PRO3_M:PROT3] +Los polis van a repasar ese edificio con un buen cepillo de dientes, ¿verdad? + +[PRO3_N:PROT3] +Pero eso lleva tiempo. + +[PRO3_O:PROT3] +Tenemos que ir y quemar nosotros mismos ese sitio. + +[PRO3_P:PROT3] +Sí, pero... + +[PRO3_Q:PROT3] +¡Nadie excepto un poli podría acercarse a una milla de ese lugar! + +[PRO3_R:PROT3] +Así que iremos como polis. + +[PRO3_S:PROT3] +Tenemos que conseguir uniformes, y vamos a necesitar un coche patrulla. + +[PRO3_T:PROT3] +Todo gracias a ti, Mike. + +[PRO3_U:PROT3] +Lo siento. + +[PRO3_V:PROT3] +Lo capto. + +[PRO3_W:PROT3] +Lo que tenemos que hacer es atraer a los polis enseñándoles el dedo, + +[PRO3_X:PROT3] +encerrarles + +[PRO3_Y:PROT3] +y asaltarles. + +[PRO3_Z:PROT3] +Buen plan. ¡Vamos! + +[PRO3_A1:PROT3] +De acuerdo. + +[PRO3_01:PROT3] +De acuerdo, Lance, ¡vamos a por los polis! + +[PRO3_02:PROT3] +~g~ Roba el coche de policía y coloca la bomba en el Café Tarbrush del centro comercial. + +[PRO3_03:PROT3] +~g~ Has dejado atrás a Lance, vuelve y recógele. + +[PRO3_04:PROT3] +~g~ Vamos. + +[PRO3_05:PROT3] +~r~¡Has matado a Lance! + +[PRO3_07:PROT3] +~g~ Te has descubierto. ¡Date prisa y coloca la bomba! + +[PRO3_08:PROT3] +~g~Vuelve a la ~h~finca de Vercetti~g~ en ~h~Starfish Island~g~. + +[PRO3_09:PROT3] +¡Átales y amordázales! + +[PRO3_10:PROT3] +¡Me queda como un guante! + +[PRO3_11:PROT3] +Un poco ajustado en la entrepierna... + +[PRO3_12:PROT3] +Oh, sí, sí, el mío también. El mío también. + +[PRO3_13:PROT3] +¡Relájate, hermano! ¡Ningún poli conduce tan mal! + +[PRO3_14:PROT3] +Recuerda, sonríe a los otros polis. + +[PRO3_15:PROT3] +Hola, agente. Bonita placa, bonita placa. + +[PRO3_16:PROT3] +Muy desenvuelto, Lance. + +[PRO3_17:PROT3] +De acuerdo, los temporizadores están colocados, 5 segundos. + +[PRO3_18:PROT3] +¿5 segundos? ¡Tenemos que salir de aquí pitando! + +[PRO3_19:PROT3] +Ahora que los hemos enfadado de verdad. + +[PRO3_20:PROT3] +~g~ Haz que dos polis te sigan hasta el garaje. + +[PRO3_21:PROT3] +~g~Consigue un nivel de se busca suficiente para que los polis te sigan hasta el garaje. + +[PRO3_22:PROT3] +~g~¡La puerta del garaje está bloqueada! El acceso tiene que estar despejado para que se pueda cerrar. + +[PRO3_23:PROT3] +~g~Camina hacia el marcador para colocar la bomba. + +[PRO3_24:PROT3] +~g~¡Aléjate del Café! + +[PRO_AS1:PROT3] +ANILLO DE PROTECCIÓN COMPLETADO + +[PRO_AS2:PROT3] +~g~La finca de Vercetti generará ahora unos ingresos máximos de hasta ~1~ $. Asegúrate de recaudarlo regularmente. + +{=================================== MISSION TABLE RACES ===================================} + +[RACES_2:RACES] +~g~¡Necesitas un vehículo para correr, esto no es una carrera a pie! + +[RACES_3:RACES] +3... 2... 1... ¡ADELANTE! ¡ADELANTE! ¡ADELANTE! + +[RACES_8:RACES] +~r~¡No has ganado la carrera! + +[RACES00:RACES] +Carrera ~1~: + +[RACES01:RACES] +Velocidad terminal + +[RACES02:RACES] +Ocean Drive + +[RACES03:RACES] +Carrera por el borde + +[RACES04:RACES] +Capital Cruise + +[RACES05:RACES] +¡Tour! + +[RACES06:RACES] +Aguante en V.C. + +[RACES07:RACES] +Precio de entrada: ~1~ $ + +[RACES08:RACES] +Mejor tiempo: ~1~:~1~ + +[RACES09:RACES] +Mejor resultado: 1 + +[RACES10:RACES] +Mejor resultado: 2 + +[RACES11:RACES] +Mejor resultado: 3 + +[RACES12:RACES] +Mejor resultado: 4 + +[RACES13:RACES] +Longitud de pista: ~1~.~1~ km + +[RACES15:RACES] +Mejor tiempo: NA + +[RACES16:RACES] +Mejor resultado: NA + +[RACES19:RACES] +No puedes permitirte entrar en esta carrera. + +[RACES22:RACES] +Mejor tiempo: ~1~:0~1~ + +[RACES23:RACES] +Longitud de la pista: ~1~.~1~ millas + +[RACES_1:RACES] +~g~Consigue un vehículo rápido y ve a la parrilla de salida. + +[RACEHLP:RACES] +~w~Pulsa ~h~~k~~PED_SPRINT~~w~ para empezar la carrera seleccionada. Pulsa ~h~~k~~VEHICLE_ENTER_EXIT~~w~ para salir. + +{=================================== MISSION TABLE RCHELI1 ===================================} + +[WRECKED:RCHELI1] +~r~¡El vehículo está destrozado! + +[RCH1_4:RCHELI1] +Puntos de control: + +[RCH1_7:RCHELI1] +~g~Hay 20 puntos de control en total. + +[RCH1_12:RCHELI1] +~g~¡El helicóptero RC está saliendo fuera de su alcance de control! + +[RCH1_13:RCHELI1] +~r~¡El helicóptero RC se ha salido de su radio de alcance! + +[RCH1_8:RCHELI1] { reVC update } +~g~Si deseas abandonar esta misión, pulsa ~h~~k~~VEHICLE_FIREWEAPON~~g~ para detonar tu helicóptero RC. + +{=================================== MISSION TABLE RCPLNE1 ===================================} + +[RCPL1_4:RCPLNE1] +~g~Compite en una CARRERA DE PUNTOS DE CONTROL contra otros tres aviones RC. + +[RCPL1_5:RCPLNE1] +~g~Vuela a través de los puntos de control dispersados por Vice City. + +[RCPL1_6:RCPLNE1] { reVC update } +~g~Si deseas abandonar esta misión, pulsa ~h~~k~~VEHICLE_FIREWEAPON~~g~ para detonar tu avión RC. + +[RCPL1_8:RCPLNE1] +~g~¡Tu avión RC está saliendo fuera del alcance! + +[RCPL1_9:RCPLNE1] +~r~¡Tu avión RC se salió del alcance! + +{=================================== MISSION TABLE RCRACE1 ===================================} + +[RCRC1_1:RCRACE1] +~g~Compite en una CARRERA DE PUNTOS DE CONTROL contra 3 Barón RC durante dos vueltas. + +[RCRC1_3:RCRACE1] +~g~¡Vuelta final! + +[RCR1_4:RCRACE1] +vueltas restantes: + +[RCR1_1:RCRACE1] +~g~Compite en una carrera de puntos de control contra otros tres coches RC. + +[RCR1_2:RCRACE1] +~g~¡Consigue ser el primero en completar las dos vueltas del circuito para ganar! + +[RCR1_6:RCRACE1] +~g~¡Tu coche RC se está saliendo fuera del alcance! + +[RCR1_7:RCRACE1] +~r~¡Tu coche RC se salió del alcance! + +{=================================== MISSION TABLE ROCK1 ===================================} + +[RBM1_A:ROCK1] +~n~ + +[RBM1_B:ROCK1] +¡Brillante, jodidamente brillante! + +[RBM1_D:ROCK1] +¿Eh, habías conocido antes a los Love Fist? + +[RBM1_E:ROCK1] +No, pero siempre me encantó vuestra música. + +[RBM1_F:ROCK1] +Déjame presentarte a la banda. + +[RBM1_G:ROCK1] +Estos son P, Percy, Dick, y Willy está en el kaze, y ese de antes de la cabina era Jezz, + +[RBM1_H:ROCK1] +y tíos, quiero que conozcáis a un buen amigo mío. + +[RBM1_I:ROCK1] +Este es Tommy. Nos conocemos desde hace mucho. + +[RBM1_J:ROCK1] +De acuerdo, colega. + +[RBM1_K:ROCK1] +Y, eh, ¿cuál era tu nombre? + +[RBM1_L:ROCK1] +Déjalo, Jezz, lo recuerdas, + +[RBM1_M:ROCK1] +no juegues a eso conmigo, colega, + +[RBM1_N:ROCK1] +¡soy demasiado astuto para eso, encanto! + +[RBM1_O:ROCK1] +Verás, Tom, la cosa es que los chicos necesitan algo de ayuda. + +[RBM1_P:ROCK1] +No tienen muchos contactos en la zona, gente a la que preguntarle ''¿vienes mucho por aquí?'' y todo eso. + +[RBM1_Q:ROCK1] +¡Necesitamos unos tiritos, colega! + +[RBM1_R:ROCK1] +¿Vamos a pillar la vieja furia Love Fist, sabes? + +[RBM1_S:ROCK1] +Bueno, esto es Vice City, tío. ¿Cuál es el problema? + +[RBM1_U:ROCK1] +¡Love juice, tío! + +[RBM1_V:ROCK1] +¿Love juice? + +[RBM1_W:ROCK1] +Sí, dos partes de boomshine, una parte de farlopa, cinco caramelos con picapica y un litro de gasolina. + +[RBM1_X:ROCK1] +¿Puedes ayudarnos, colega? + +[RBM1_Y:ROCK1] +De verdad significaría mucho para los chicos. + +[RBM1_Z:ROCK1] +Puedes hacer eso por los muchachos, ¿verdad? + +[RBM1_7:ROCK1] +~r~¡No conseguiste a tiempo el ''love juice''! + +[RBM1_8:ROCK1] +~r~¡Mercedes está muerta! + +[RBM1_10:ROCK1] +~r~¡Idiota! ¡Has destruido el material! + +[RBM1_13:ROCK1] +~g~Lleva el ''love juice'' y a Mercedes a la banda antes de que sean requeridos en el escenario. + +[RBM1_15:ROCK1] +~r~¡Has perdido al camello, nuestra pasta y la nieve! + +[RBM1_17:ROCK1] +~g~¡Mata al camello y consigue los ingredientes! + +[MOB_07A:ROCK1] +Eh colega, a los chicos les vendría bien algo de compañía, si entiendes lo que quiero decir... + +[MOB_07B:ROCK1] +Conozco a la chica apropiada. + +[ROK1_5:ROCK1] +¡Hola, Mercedes! + +[ROK1_6:ROCK1] +Hola, Tommy. ¿Cómo estás? + +[ROK1_7:ROCK1] +Bien. Escucha, ¿te apetece acostarte con los Love Fist? + +[ROK1_8:ROCK1] +De acuerdo, pero sólo como un favor que espero que me sea devuelto... + +[RBM1_14:ROCK1] +~g~¡Necesitas un coche o una moto! + +[RBM1_1:ROCK1] +~g~Ve y recoge a Mercedes en su apartamento. + +[RBM1_12:ROCK1] +~g~Ve y reúnete con el camello para conseguir los ingredientes del ''love juice''. + +[ROK1_2:ROCK1] +YA NO ES NECESARIO + +[ROK1_3:ROCK1] +YA NO ES NECESARIO + +[MERC_39:ROCK1] +Hasta luego grandullón. + +[RBM1_C:ROCK1] +¡Eh, Tommy! Me alegro que pudieras venir. + +[RBM1_9:ROCK1] +~g~¡Ve y reúnete con el camello de los Love Fist para conseguir algo de ''love juice''! + +[ROK1_1A:ROCK1] +¿Buscas algo especial? ¡Yo tengo lo que necesitas! + +[ROK1_9:ROCK1] +¡Gracias por el dinero, mamón! + +[RBM1_T:ROCK1] +Necesitamos ''love juice'', tío, ¿sabes? + +{=================================== MISSION TABLE ROCK2 ===================================} + +[RBM2_A:ROCK2] +Tommy, tío. ¡Me alegro de verte! + +[RBM2_B:ROCK2] +¿Qué sucede? + +[RBM2_C:ROCK2] +Malas vibraciones, Tommy... + +[RBM2_E:ROCK2] +Hay un tipo, apenas le conocemos, pero él nos conoce a nosotros. + +[RBM2_F:ROCK2] +Como este tipo, que lo sabe todo de nosotros. + +[RBM2_G:ROCK2] +Sabe que a Willy le gusta ponerse ropa interior de mujeres. + +[RBM2_H:ROCK2] +¡O que a Percy le gusta Duran Duran! + +[RBM2_K:ROCK2] +Sí, bien, tu cohete del amor. Pero escucha, este tipo... + +[RBM2_L:ROCK2] +El tipo, quiere ver a los Love Fist muertos. + +[RBM2_M:ROCK2] +Muertos, Tommy. + +[RBM2_N:ROCK2] +El final Love Fist. Ya sabes lo que dicen, los buenos mueren jóvenes. + +[RBM2_O:ROCK2] +¡Pero Tommy, tienes que salvar a los Love Fist! + +[RBM2_P:ROCK2] +Tenemos que firmar en dos horas y creo... + +[RBM2_Q:ROCK2] +Los chicos creen que el sospechoso va a intentar hacer algún recibimiento infame. + +[RBM2_1:ROCK2] +~g~Conduce la limusina a la firma de autógrafos e intenta encontrar al psicópata. + +[RBM2_2:ROCK2] +~r~¡Destrozaste el coche de la banda! + +[RBM2_3:ROCK2] +~g~¡Ve a la firma de autógrafos! + +[RBM2_4:ROCK2] +~g~¡Atrapa al psicópata! ¡No le dejes escapar! + +[RBM2_5:ROCK2] +~r~¡Lo perdiste, idiota! + +[RBM2_7:ROCK2] +~r~¡Los fans han sido atacados, el psicópata no aparecerá! + +[RBM2_8:ROCK2] +~r~¡Los guardas de seguridad han sido atacados, el psicópata no aparecerá! + +[PSYCH_1:ROCK2] +¡Me encantaría ver arder a los Love Fist! + +[PSYCH_2:ROCK2] +¡Los Love Fist arruinaron mi vida! + +[RBM2_I:ROCK2] +Cállate, idiota. Sólo porque Jezz se folla ovejas. + +[RBM2_R:ROCK2] +¡Eh, cierra la boca! + +[RBM2_D:ROCK2] +Sí, no estoy de broma, es un tema muy gordo tío, muy gordo, ¿sabes? + +[RBM2_J:ROCK2] +Es lo que atrae a mi cohete del amor, ¿entiendes? + +{=================================== MISSION TABLE ROCK3 ===================================} + +[RBM3_A:ROCK3] +¡Tommy! ¡Tommy! ¡Tommy, tío, ese psicópata ha vuelto! + +[RBM3_B:ROCK3] +¿Qué está pasando? + +[RBM3_C:ROCK3] +¡Ese psicópata no dejará en paz a los Love Fist! + +[RBM3_D:ROCK3] +No le mataste, tío. Y ahora ha vuelto. + +[RBM3_E:ROCK3] +Sí, sí, sí, y la cuestión es... + +[RBM3_F:ROCK3] +La cuestión es que necesitamos alguien en quien poder confiar para que conduzca la limo, + +[RBM3_G:ROCK3] +¡porque ese chiflado sigue con las amenazas! + +[RBM3_I:ROCK3] +Aquí estamos todos acojonados, tío. + +[RBM3_J:ROCK3] +De acuerdo tíos, calmaos, yo me encargaré de esto. + +[RBM3_K:ROCK3] +Normalmente, no me ocuparía de ir por ahí haciendo de chófer para una panda de bisexuales escoceses bebidos, + +[RBM3_L:ROCK3] +pero, en vuestro caso, haré una excepción. + +[RBM3_4:ROCK3] +~r~¡Has matado a los Love Fist! + +[RBM3_6:ROCK3] +DETONACIÓN: + +[RBM3_1:ROCK3] +~g~Conduce a los Love Fist al local del concierto. + +[RBM3_2:ROCK3] +Si intentas abandonar el coche mientras la bomba está armada explotará... + +[RBM3_3:ROCK3] +Si la barra de detonación se llena completamente la bomba explotará. + +[RBM3_8:ROCK3] +Cuanto más rápido conduzcas más despacio se moverá la barra de detonación. + +[RBM3_7:ROCK3] +~g~¡BOMBA DESACTIVADA! + +[ROK3_62:ROCK3] +y pensamos que podríamos enseñarte nuestro Templo del Rock. + +[ROK3_63:ROCK3] +¡Para que te vayas acostumbrando a la furia de Love Fist! + +[ROK3_64:ROCK3] +Escúchate a ti mismo, tío. Es papel maché y cinta aislante. + +[ROK3_65:ROCK3] +Eh, para los chavales, ¡esto es un templo y nosotros somos los sacerdotes! + +[ROK3_66:ROCK3] +Si, vale, si a los chavales les gustan los sacerdotes colgados y sin oído musical, + +[ROK3_67:ROCK3] +¿quién soy yo para discutirlo? + +[ROK3_68:ROCK3] +Vaya por Dios, se está comiendo la cinta otra vez. + +[ROK3_69:ROCK3] +Con este ritmo tendremos que tocar en directo. + +[ROK3_70:ROCK3] +Oohh ¡mierda! Mis tripas... + +[ROK3_73:ROCK3] +Jezz está escuchando la cinta, + +[RBM3_9:ROCK3] +Si te paran o reduces la velocidad la barra de detonación aumentará. + +[RBM3_H:ROCK3] +¡Me estoy cagando de miedo tío. Necesito a mi, mamá! + +[ROK3_71:ROCK3] +Tenemos que seguir con esto. Gracias de nuevo Tommy, ya sabes lo que quiero decir, apúntate una, ¡adiós! + +[ROK3_1:ROCK3] +Al fin tío, hora de un merecido trago. La sala de conciertos está solo a unos cientos de metros calle abajo. + +[ROK3_2:ROCK3] +Mejor ponme uno doble. Eh Tommy, cambia los temas, tío. + +[ROK3_3:ROCK3] +Me siento perdido si la cabeza no me estalla. Ah, mira, ¿Qué es esto? Eh Tommy, pon esta cinta. + +[ROK3_4:ROCK3] +Love Fist. Vuestro tiempo contaminando las ondas se ha terminado. Os di la oportunidad de ser amigos. + +[ROK3_5:ROCK3] +Ahora os doy la oportunidad de morir. ¡Intentad aminorar la marcha y vuestra limusina explotará junto con vuestros GRANDES Y PELUDOS CULOS! + +[ROK3_6:ROCK3] +Tommy amigo, ¡tienes que salvar a la banda! Yo me estoy cansando de esto. ¡Mantén pisado el acelerador! + +[ROK3_7:ROCK3] +¡Tenemos que encontrar la bomba! ¿Podremos conducir por ahí todo el día? Sí, tenemos suficiente bebida.. + +[ROK3_8:ROCK3] +¿La bomba no estará en el motor? Tendremos que parar y cogerla. ¡Vamos a morir todos! ¡Me voy a emborrachar! + +[ROK3_9:ROCK3] +Eh, ¡amigo, aquí hay cola! ¡La respuesta no está en el mueble bar! ¡Sal de mi camino! + +[ROK3_10:ROCK3] +Eh, ¡a la botella de vodka le salen unos cables! ¡Eso no es vodka!, ¡Eso es una BOMBA! + +[ROK3_11:ROCK3] +¡Y está preparada para explotar! + +[ROK3_12:ROCK3] +Siempre me dijeron que la bebida acabaría conmigo. He visto esto en la tele. Tienes que tirar de uno de los cables. ¿De qué cable? Pues no sé tío. + +[ROK3_13:ROCK3] +No tengo ni idea. Willy, di algo. Voy a tocar el bajo en el otro mundo. + +[ROK3_14:ROCK3] +Tommy tío, sigue conduciendo deprisa, amigo. Que alguien haga algo. Si, ¡qué listo! + +[ROK3_15:ROCK3] +''Que alguien haga algo'', qué mierda es esa, he visto a niñas con más valor. Tipo duro, haz tú algo. + +[ROK3_16:ROCK3] +Mira tío, yo toco un instrumento y no tengo ni idea de desactivar bombas. Willy podrías beberte el líquido de la bomba con una pajita. + +[ROK3_17:ROCK3] +Sí, he oído que eres bueno en ese tipo de cosas. ¡Eh, aquella noche estaba muy colgado, como bien sabes! + +[ROK3_18:ROCK3] +¡Anda pásale una pajita a Willy! ¿Una pajita? ¡Este es el autobús de la gira de los Love Fist! + +[ROK3_19:ROCK3] +¿De dónde voy a sacar yo una pajita? ¿me entiendes? ¿Qué cable Tommy? El verde. No hay ninguno verde. + +[ROK3_20:ROCK3] +¿O es que este es verde? ¿Te parece verde alguno de estos cables? + +[ROK3_21:ROCK3] +¡Oh no! ¡La muerte está en las cartas! ¡Todo parece verde! Tenía que haberos dejado tirados cuando tuve la oportunidad. + +[ROK3_22:ROCK3] +Buscador de gloria. Capitalista. He estado tirando de ti durante años. Cállate. Eres un espantajo. + +[ROK3_23:ROCK3] +Una nena grande y chillona. Sí. Cállate y tira del cable. ¿Qué cable? Este... + +[ROK3_24:ROCK3] +¡NO, tío! estamos bien. ¡No hemos saltado por los aires, amigo. + +[ROK3_25:ROCK3] +Tommy, tío, chachi. Rock and roll, tío. ¿No tenemos que actuar? + +[ROK3_26:ROCK3] +¿Meter bulla? ¿Abusar de las fans? ¡LOVE FIST! + +[ROK3_27:ROCK3] +¿Has terminado con esa botella? + +{=================================== MISSION TABLE SERG1 ===================================} + +[TEX1_A:SERG1] +Entra y disfruta de los asientos de cuero, hijo. + +[TEX1_B:SERG1] +Demonios, mi padre solía decir, a caballo regalado no le mires el diente, y por Dios que nunca lo hizo. + +[TEX1_C:SERG1] +¿Te gustaría un trago del viejo Kentucky? + +[TEX1_D:SERG1] +No gracias. + +[TEX1_E:SERG1] +Alguien con la mente despejada, me gusta eso. + +[TEX1_F:SERG1] +El negocio de la propiedad no se trata solo de papeleo pretencioso. + +[TEX1_G:SERG1] +¡Se trata de tierra! ¡Y el derecho a reclamarla! ¿Me sigues, hijo? + +[TEX1_H:SERG1] +Sí. + +[TEX1_J:SERG1] +Y yo creo que eres la clase de tío para persuadirle. + +[TEX1_K:SERG1] +La persuasión es mi fuerte. + +[TEX1_L:SERG1] +Sí, estará en el club de campo, en el campo de golf. + +[TEX1_M:SERG1] +Allí no permiten armas, así que sus guardaespaldas no llevarán protección. + +[TEX1_N:SERG1] +Ve y dale una paliza inolvidable. + +[TEX1_O:SERG1] +Bien, toma... te he conseguido un carné de socio, y chico, vas a necesitar un tipo de ropa más apropiada. + +[TEX1_2:SERG1] +~g~Ahora ve al Club de golf Leaf Links. + +[TEX1_0:SERG1] +~g~El objetivo está en el campo de prácticas disfrutando de una partida de golf. ¡Asegúrate de que sea la última! + +[TEX1_3:SERG1] +¿Quién es este tipo? Chicos, ocuparos de él. + +[TEX1_6:SERG1] +¡Bonito culo nena! + +[TEX1_7:SERG1] +¿Este soy yo? + +[TEX1_I:SERG1] +Bueno, necesito algún cabrón obstinado que suelte algo de mierda. + +[TEX1_8:SERG1] +Cada vez que subas a un Caddy recibirás automáticamente un palo de golf, siempre que la ranura de armas blancas esté vacía. + +[TEX1_9:SERG1] +¡Atrapadle! + +[TEX1_10:SERG1] +¡Mata a ese psicópata! + +[TEX1_1:SERG1] +~g~Ve y consigue ropa de golf en Jocksports. + +{=================================== MISSION TABLE SERG2 ===================================} + +[TEX_2A:SERG2] +~g~¡Excelente! ¡Te han visto! + +[TEX_2B:SERG2] +~r~¡Idiota! ¡La gente debe PRESENCIAR a un cubano haciendo el trabajito! + +[TEX_2C:SERG2] +~g~¡Ve y consígue algo con los colores de la banda cubana en Little Havana! + +[TEX_2D:SERG2] +~g~¡Ahora elimina al capo de la banda haitiana en la Funeraria de Romero! + +[TEX2_A:SERG2] +Tommy, este es Donald Love. Donald, te presento a Tommy Vercetti, + +[TEX2_B:SERG2] +el último tirador más rápido en llegar a estos lares. + +[TEX2_C:SERG2] +Si... eh... + +[TEX2_D:SERG2] +Donald, ahora cállate y escucha, puede que aprendas algo. + +[TEX2_E:SERG2] +Veamos. Nada hace que los precios de las propiedades bajen más rápido que una guerra entre bandas al viejo estilo. + +[TEX2_F:SERG2] +Excepto un desastre quizás, como la peste de la que habla la biblia o algo así, + +[TEX2_G:SERG2] +aunque puede que en este caso vayamos demasiado lejos. + +[TEX2_H:SERG2] +¿Te estás enterando, capullo cuatro ojos? + +[TEX2_I:SERG2] +Hace poco murió un capo de una banda haitiana. Al parecer lo hicieron los cubanos, nadie lo sabe. + +[TEX2_J:SERG2] +¡Pero se lo haremos saber! Disfrázate de cubano + +[TEX2_K:SERG2] +y ve a reventar el funeral. Crea confusión y después le sigues bien de cerca. + +[TEX2_L:SERG2] +¿Te estás enterando, Donald? + +[TEX2_M:SERG2] +Eso debería meter al zorro en el gallinero. ¿Eh? + +[TEX2_N:SERG2] +y ahora sólo tenemos que sentarnos a esperar y ver cómo caen los precios. + +[TEXEXIT:SERG2] +~g~¡Lárgate de Little Haiti ahora! + +{=================================== MISSION TABLE SERG3 ===================================} + +[TEX3_A:SERG3] +Bien, mira, hijo. Tengo un problema y creo que podrías ayudarme con él. + +[TEX3_B:SERG3] +No soy constructor. + +[TEX3_C:SERG3] +No, estaba pensando más bien en tus habilidades para la demolición. + +[TEX3_D:SERG3] +Bien, esto de aquí es el centro empresarial tal y como se ha planeado, y éste + +[TEX3_E:SERG3] +es el solar que estamos buscando. + +[TEX3_F:SERG3] +Intentas decir que este nuevo bloque de oficinas está en medio. + +[TEX3_G:SERG3] +Lo captas rápido. + +[TEX3_H:SERG3] +Bien, voy a irme de esta ciudad durante un tiempo + +[TEX3_I:SERG3] +y si ese centro de oficinas se enfrentase a problemas estructurales repentinos e insalvables, entonces yo... + +[TEX3_J:SERG3] +¿Como buen ciudadano se sentirá obligado a intervenir y + +[TEX3_K:SERG3] +salvar la remodelación de una zona tan importante de la ciudad? + +[TEX3_L:SERG3] +¿Dónde puedo conseguir más tipos como tú? + +[TEX3_1:SERG3] +~g~Utiliza el helicóptero RC para transportar las bombas a los cuatro objetivos del edificio que debes demoler. + +[TEX3_2:SERG3] +~g~Debes colocar una bomba en cada objetivo. Puedes colocarlas en cualquier orden. + +[TEX3_3:SERG3] +~g~Para recoger una bomba, dirige el helicóptero RC hasta a ella. Solo puedes llevar las bombas de una en una. + +[TEX3_4:SERG3] { reVC update } +~g~Para soltar una bomba pulsa ~h~~k~~VEHICLE_FIREWEAPON~~g~. + +[TEX3_5:SERG3] +~g~Si fallas al colocar una bomba podrás recogerla e intentarlo de nuevo. + +[TEX3_6:SERG3] +~g~Cuando hayas recogido una bomba por primera vez, el temporizador de detonación se pondrá en marcha. + +[TEX3_7:SERG3] +~g~¡A continuación deberás conseguir colocar el resto de las bombas en 7 minutos! + +[TEX3_8:SERG3] +~g~¡Has fallado en el objetivo! ¡Recoge la bomba e inténtalo de nuevo! + +[TEX3_10:SERG3] +~g~Suelta la bomba en el objetivo. + +[TEX3_11:SERG3] +objetivos restantes: + +[TEX3_17:SERG3] +~r~¡Te has quedado sin tiempo! + +[TEX3_18:SERG3] +~r~¡Tu helicóptero RC ha sido destruido! + +[TEX3_19:SERG3] +~r~¡Has lanzado la bomba en el agua! ¡Esa no es manera de pescar! + +[TEX3_20:SERG3] +~g~¡Tu Helicóptero RC está casi fuera del alcance del control remoto! + +[TEX3_21:SERG3] +~r~¡Tu Helicóptero RC está fuera del alcance del control remoto! + +[TEX3_24:SERG3] +Pulsa ~h~~k~~VEHICLE_LOOKLEFT~ ~w~para girar el helicóptero en sentido contrario a las agujas del reloj. + +[TEX3_25:SERG3] +Pulsa ~h~~k~~VEHICLE_LOOKLEFT~ ~w~para girar el helicóptero en sentido de las agujas del reloj. + +[TEX3_27:SERG3] +~g~Una escalera central permite el acceso a todas las plantas del edificio. + +[TEX3_31:SERG3] +~r~¡Has destruido la furgoneta de TOPFUN que tenía las bombas y el helicóptero RC! + +[TEX3_32:SERG3] +Puedes ~h~mirar atrás~w~ si ~h~pulsas simultáneamente ~k~~VEHICLE_LOOKLEFT~ y ~k~~VEHICLE_LOOKRIGHT~~w~. + +[TEX3_29:SERG3] { reVC update } +~g~Para soltar una bomba pulsa ~h~~k~~VEHICLE_FIREWEAPON~~g~. + +[TEX3_26:SERG3] +Pulsa ~h~~k~~VEHICLE_BRAKE~~w~ para disminuir la velocidad del rotor y ~h~descender~w~. + +[TEX3_22:SERG3] +Pulsa ~h~~k~~VEHICLE_ACCELERATE~~w~ para aumentar la velocidad del rotor y así ~h~ascender~w~. + +[TEX3_16:SERG3] +~g~Ve a la furgoneta ~w~TOPFUN ~g~cercana al solar de construcción que se va a demoler. + +[TEX3_33:SERG3] +Una vez que recojas una bomba el radar te mostrará la posición del objetivo con relación al helicóptero RC. + +[TEX3_34:SERG3] +Un ~h~icono triangular apuntando hacia arriba ~w~indica que el objetivo está ~h~por encima ~w~del helicóptero RC. + +[TEX3_35:SERG3] +Un ~h~icono triangular apuntando hacia abajo ~w~indica que el objetivo está ~h~por debajo ~w~del helicóptero RC. + +[TEX3_36:SERG3] +Un ~h~icono cuadrado ~w~indica que el objetivo está al ~h~mismo nivel ~w~que el helicóptero RC. + +[TEX3_28:SERG3] +Para ~h~recoger una bomba~w~, simplemente dirige el helicóptero RC hasta ella. El helicóptero RC sólo puede transportar una bomba. + +[TEX3_30:SERG3] +~g~Para recoger una bomba, simplemente dirige el helicóptero RC hasta ella. El helicóptero RC sólo puede transportar una bomba. + +[TEX3_12:SERG3] +~g~¡Bomba colocada! ¡Quedan 3 objetivos más! Vuelve a por otra bomba. + +[TEX3_13:SERG3] +~g~¡Bomba colocada! ¡Quedan 2 objetivos más! Vuelve a por otra bomba. + +[TEX3_14:SERG3] +~g~¡Bomba colocada! ¡Queda 1 objetivo más! Vuelve a por otra bomba. + +[TEX3_15:SERG3] +~r~¡El temporizador de detonación se ha puesto en marcha! ~g~Debes colocar las ~w~4 bombas ~g~en el tiempo restante! + +[TEX3_37:SERG3] +Si empujas ~h~el joystick analógico derecho hacia atrás~w~, aumentarás la velocidad del rotor y así ~h~ascenderá~w~. + +[TEX3_38:SERG3] { reVC update } +{Pulsa ~h~~k~~VEHICLE_ACCELERATE~~w~ para aumentar la velocidad del rotor y así ~h~ascender~w~.} +Si empujas ~h~el joystick analógico derecho hacia adelante ~w~la velocidad del rotor aumenta, haciendo que el helicóptero ~h~descienda. + +[TEX3_39:SERG3] +~g~Para soltar una bomba pulsa ~h~~k~~VEHICLE_HANDBRAKE~~w~. + +[TEX3_40:SERG3] +~g~Para soltar una bomba pulsa ~h~~k~~VEHICLE_HANDBRAKE~~w~. + +[TEX3_23:SERG3] +Pulsa ~h~~k~~VEHICLE_TURRETUP~~w~ y ~h~~k~~VEHICLE_TURRETDOWN~~w~ para inclinar el helicóptero en la dirección deseada. + +{=================================== MISSION TABLE TAXI1 ===================================} + +[FARES:TAXI1] +CARRERAS: + +[TAXI1:TAXI1] +~g~Busca un cliente. + +[TSCORE2:TAXI1] +~1~ $ + +[IN_ROW:TAXI1] +~1~ ¡Racha de clientes! ~1~ $ + +[TAXI3:TAXI1] +~r~¡Tu pasajero huye aterrorizado! + +[TAXI7:TAXI1] +~r~Tu coche está destrozado, haz que lo reparen. + +[TAXI4:TAXI1] +¡Viaje completado! + +[TAXI5:TAXI1] +¡VIAJE RÁPIDO! + +[TAXI6:TAXI1] +Misión de taxista cancelada + +[TAXIH1:TAXI1] +Detente cerca de un peatón que esté resaltado para recogerle y llevarle a su destino antes de que se agote el tiempo. + +[FARE1:TAXI1] +~g~Destino ~w~''el club Pole Position'' ~g~en Ocean Beach. + +[FARE3:TAXI1] +~g~Destino ~w~''el club náutico'' ~g~en Ocean Beach. + +[FARE4:TAXI1] +~g~Destino ~w~''Ammu-Nation'' ~g~en Ocean Beach. + +[FARE5:TAXI1] +~g~Destino ~w~''la ferretería'' ~g~en Washington Beach. + +[FARE6:TAXI1] +~g~Destino ~w~''el centro comercial North Point'' ~g~en Vice Point. + +[MFARE1:TAXI1] +~g~Destino ~w~''Ammu-Nation'' ~g~en el centro. + +[MFARE2:TAXI1] +~g~Destino ~w~''la terminal'' ~g~en el aeropuerto Escobar International. + +[WFARE3:TAXI1] +~g~Destino ~w~''Coches Sunshine'' ~g~en Little Havana. + +[WFARE4:TAXI1] +~g~Destino ~w~''Taxis Kaufman'' ~g~en Little Haiti. + +[WFARE5:TAXI1] +~g~Destino ~w~''la ferretería'' ~g~en Little Havana. + +[WFARE6:TAXI1] +~g~Destino ~w~''Emporio Howlin Petes Bike'' ~g~en el centro. + +[FARE7:TAXI1] +~g~Destino ~w~''la joyería'' ~g~en Vice Point. + +[FARE8:TAXI1] +~g~Destino ~w~''la playa'' ~g~en Ocean Beach. + +[FARE9:TAXI1] +~g~Destino ~w~''la playa'' ~g~en Washington Beach. + +[FARE10:TAXI1] +~g~Destino ~w~''la playa'' ~g~en Vice Point. + +[FARE11:TAXI1] +~g~Destino ~w~''el hospital'' ~g~en Ocean Beach. + +[FARE12:TAXI1] +~g~Destino ~w~''el hospital'' ~g~en Vice Point. + +[FARE13:TAXI1] +~g~Destino ~w~''la comisaría de policía'' ~g~en Washington Beach. + +[FARE14:TAXI1] +~g~Destino ~w~''la comisaría de policía'' ~g~en Vice Point. + +[FARE15:TAXI1] +~g~Destino ~w~''la pizzería'' ~g~en Vice Point. + +[WFARE7:TAXI1] +~g~Destino ~w~''la comisaría de policía'' ~g~en Little Havana. + +[WFARE8:TAXI1] +~g~Destino ~w~''la comisaría de policía'' ~g~en el centro de la ciudad. + +[WFARE9:TAXI1] +~g~Destino ~w~''el hospital'' ~g~en el centro de la ciudad. + +[WFARE10:TAXI1] +~g~Destino ~w~''el hospital'' ~g~en Little Havana. + +[WFARE11:TAXI1] +~g~Destino ~w~''el estadio'' ~g~en el centro de la ciudad. + +[WFARE12:TAXI1] +~g~Destino ~w~''la pizzería'' ~g~en Little Haiti. + +[WFARE13:TAXI1] +~g~Destino ~w~''la pizzería'' ~g~en el centro de la ciudad. + +[WFARE14:TAXI1] +~g~Destino ~w~''los muelles'' ~g~en Viceport. + +[WFARE15:TAXI1] +~g~Destino ~w~''la farmacia'' ~g~en Little Haiti. + +[FARE2:TAXI1] +~g~Destino ~w~''el club Malibú'' ~g~en Vice Point. + +{=================================== MISSION TABLE TAXICUT ===================================} + +[TAXC_A:TAXICUT] +Supongo que eres el nuevo propietario. + +[TAXC_B:TAXICUT] +¿Qué eres, mafioso? ¿Del cártel? No pareces mejicano... + +[TAXC_C:TAXICUT] +En fin, será mejor que sueltes el rollo de ''las cosas van a cambiar por aquí'', + +[TAXC_D:TAXICUT] +quizás amenazar a alguno de los conductores... + +[TAXC_E:TAXICUT] +ve con calma con Ted, el de ahí, acaban de curarle la hernia. + +[TAXC_F:TAXICUT] +Bueno, sí. Las cosas van a cambiar por aquí, señora. + +[TAXC_G:TAXICUT] +Oh, mierda, hijo. Anda dejarme esto a mí... + +[TAXC_H:TAXICUT] +Llevo haciéndolo años. + +[TAXC_I:TAXICUT] +Ahora oíd esto. + +[TAXC_J:TAXICUT] +Ahora tenemos una nueva dirección y las cosas van a cambiar por aquí. + +[TAXC_K:TAXICUT] +Nuestra nueva dirección, los... + +[TAXC_L:TAXICUT] +¿De qué banda eres? + +[TAXC_M:TAXICUT] +Bueno, en realidad no formo parte de ninguna banda. + +[TAXC_N:TAXICUT] +¿Cómo coño te llamas, muchacho? + +[TAXC_O:TAXICUT] +Vercetti, Tommy Vercetti. + +[TAXC_P:TAXICUT] +Nuestra nueva administración, la banda de Vercetti, + +[TAXC_Q:TAXICUT] +se va a encargar de que no tengamos problemas. + +[TAXC_R:TAXICUT] +¿Capiche? ¡Corto! + +[TAXC_S:TAXICUT] +¿Te gustó el ''capiche''? A mí me gustó el ''capiche''. + +[TAXC_T:TAXICUT] +Así que así es como funcionaban las cosas en el pasado, + +[TAXC_U:TAXICUT] +Dirigimos la compañía como siempre. + +[TAXC_V:TAXICUT] +Si tenemos algún problema con las compañías rivales, tú les das una somanta de palos. + +[TAXC_W:TAXICUT] +Luego ellos nos la dan a nosotros, + +[TAXC_X:TAXICUT] +y después, se la vuelves a dar a ellos, + +[TAXC_Y:TAXICUT] +etcétera, etcétera. ¿Lo captas? + +[TAXC_Z:TAXICUT] +Sí, supongo... + +[TAXC_A1:TAXICUT] +Simplemente toma un taxi del garaje si tienes ganas de subir a bordo. + +{=================================== MISSION TABLE TAXIWA1 ===================================} + +[OUTTIME:TAXIWA1] +~r~¡Demasiado lento, tío, demasiado lento! + +[TAX1_1:TAXIWA1] +Tenemos un cliente importante que necesita que lo recojan en Starfish Island. ¿Alguien me recibe? + +[TAX1_2:TAXIWA1] +¡Aquí Tommy, yo lo recogeré! + +[TAX1_3:TAXIWA1] +¡Este es mi cliente, lárgate, gilipollas! + +[TAX1_4:TAXIWA1] +Venga, venga, entre, ¡rápido! + +[TAX1_5:TAXIWA1] +¡Vale, vale! ¡No me haga daño! + +[TAXW1_1:TAXIWA1] +~g~Recoge al V.I.P. en Starfish Island. + +[TAXW1_2:TAXIWA1] +~g~¡Recupera al V.I.P! ¡Destroza el otro coche! + +[TAXW1_3:TAXIWA1] +~r~¡El V.I.P. está muerto! + +[TAXW1_4:TAXIWA1] +~r~¡El V.I.P. se ha apeado! + +[TAXW1_6:TAXIWA1] +~g~¡Lleva al V.I.P. al aeropuerto! + +{=================================== MISSION TABLE TAXIWA2 ===================================} + +[TAX2_1:TAXIWA2] +Llamando a todos los taxis, estamos perdiendo clientes por toda la ciudad. ¿Qué os pasa, tíos? + +[TAX2_2:TAXIWA2] +Taxis VC sigue llevándonos la delantera. ¡Tienen tantos coches que no podemos competir! + +[TAX2_3:TAXIWA2] +Sr. Vercetti, si está ahí escuchando, ¡tiene que poner fuera de circulación unos cuantos Taxis VC antes de que nos hundan! + +[TAXW2_1:TAXIWA2] +~g~¡Destruye 3 de los taxis rivales! + +{=================================== MISSION TABLE TAXIWA3 ===================================} + +[TAX3_1:TAXIWA3] +Coche 13, tengo a una tal Señorita Cortez, preguntó especialmente por usted. + +[TAX3_2:TAXIWA3] +Vale, entendido. ¡Coche 13 en ruta! + +[TAX3_3:TAXIWA3] +No hay rastro de Mercedes... + +[TAXW3_3:TAXIWA3] +~g~Destrulle al taxi principal! + +[TAXW3_2:TAXIWA3] +~g~Permanece con vida hasta que se agote el temporizador. + +[TAX_AS1:TAXIWA3] +COMPAÑÍA DE TAXIS CONSOLIDADA + +[TAX_AS2:TAXIWA3] +~g~Los taxis Kaufman generarán ahora unos ingresos de hasta ~1~ $ máximo. Asegúrate de recaudarlos regularmente. + +[TAX3_4:TAXIWA3] +¡Es hora que el ángel guardián de Taxis Kaufman coma algo del parachoques! + +[TAX3_5:TAXIWA3] +¡Eh chico te voy a zurrar la badana! + +{ reVC updates } +{ new languages } +[FEL_JAP] +JAPONÉS + +[FEL_POL] +POLACO + +[FEL_RUS] +RUSO + +{ new display menus } +[FET_GFX] +CONFIGURACIÓN DE GRÁFICOS + +[FED_MIP] +MIPMAPPING + +[FED_AAS] +SUAVIZADO DE BORDES + +[FED_FIL] +FILTRO DE TEXTURAS + +[FED_BIL] +BILINEAL + +[FED_TRL] +TRILINEAL + +[FED_WND] +VENTANA + +[FED_FLS] +PANTALLA COMPLETA + +[FEM_CSB] +BORDES EN CINEMÁTICAS + +[FEM_SCF] +FORMATO DE IMAGEN + +[FEM_ISL] +USO DE MEMORIA + +[FEM_LOW] +BAJO + +[FEM_MED] +MEDIO + +[FEM_HIG] +ALTO + +[FEM_2PR] +ALPHA TEST TIPO PS2 + +[FEC_FRC] +CÁMARA LIBRE + +{ Linux joy detection } +[FEC_JOD] +DETECTAR JOYSTICK + +[FEC_JPR] +Pulsa cualquier botón del joystick que quieras usar con el juego para seleccionarlo. + +[FEC_JDE] +Joystick detectado + +{ mission restart } +[FET_RMS] +REPETIR MISIÓN + +[FESZ_RM] +¿REINTENTAR? + +[FED_VPL] +FLUJO DE VEHÍCULOS + +[FED_PRM] +LUCES EN PEATONES + +[FED_RGL] +BRILLO DE CARRETERAS + +[FED_CLF] +FILTRO DE COLOR + +[FED_WLM] +MAPAS DE LUZ DEL MUNDO + +[FED_MBL] +DESENFOQ. MOVIMIENTO + +[FEM_SIM] +SIMPLE + +[FEM_NRM] +NORMAL + +[FEM_MOB] +MÓVIL + +[FED_MFX] +MATFX + +[FED_NEO] +NEO + +[FEM_PS2] +PS2 + +[FEM_XBX] +XBOX + +[FEC_IVP] +INVERTIR VERTICALIDAD MANDO + +[FEM_NON] +NADA + +[FEC_DS2] +DUALSHOCK 2 + +[FEC_DS3] +DUALSHOCK 3 + +[FEC_DS4] +DUALSHOCK 4 + +[FEC_360] +MANDO DE XBOX 360 + +[FEC_ONE] +MANDO DE XBOX ONE + +[FEC_NSW] +MANDO DE NINTENDO SWITCH + +[FEC_TYP] +TIPO DE MANDO + +[FET_AGS] +AJUSTES DE MANDO + +[FEM_AUT] { aspect ratio related } +AUTO + +[FEM_PED] +PED DENSITY + +[FEM_CAR] +CAR DENSITY + +{ end of file } +[DUMMY] +THIS LABEL NEEDS TO BE HERE !!! +AS THE LAST LABEL DOES NOT GET COMPILED \ No newline at end of file diff --git a/dreamcast/Makefile b/liberty/Makefile similarity index 64% rename from dreamcast/Makefile rename to liberty/Makefile index 5370d3ce..5116de51 100644 --- a/dreamcast/Makefile +++ b/liberty/Makefile @@ -4,8 +4,11 @@ # # Put the filename of the output binary here -PROJECT_NAME = dca3 -TARGET ?= dca3.elf +PROJECT_NAME = dca-liberty +TEAM_NAME ="the gang" +DISC_SERIAL = DCA-LBRT +RELEASE_DATE = 20250401 +TARGET ?= dca-liberty.elf HAVE_CDI4DC := $(shell which cdi4dc > /dev/null 2>&1 && echo "yes" || echo "no") IS_MAC := $(shell uname -s | grep -i "darwin" > /dev/null && echo "yes" || echo "no") @@ -13,8 +16,10 @@ IS_MAC := $(shell uname -s | grep -i "darwin" > /dev/null && echo "yes" || echo MOD_NAME?= GTA_DIR?=../../gta3 +GTA_GAMEFILES_LOOSE_DIR?=../gamefiles/liberty GTA_MOD_DIR?=../../gta3_mod$(MOD_NAME) GTA_MOD_IMG_DIR?=$(GTA_MOD_DIR)/img +GTA_MOD_CUTS_DIR=$(GTA_MOD_DIR)/cuts GTA_MOD_SFX_DIR?=$(GTA_MOD_DIR)/sfx GTA_MOD_LOOSE_DIR?=$(GTA_MOD_DIR)/loose @@ -25,6 +30,9 @@ REPACK_IMG_DC_DIR?=$(REPACK_DIR)/img-dc REPACK_SFX_ORIG_DIR?=$(REPACK_DIR)/sfx-orig REPACK_SFX_DC_DIR?=$(REPACK_DIR)/sfx-dc REPACK_STREAM_DECODED_DIR?=$(REPACK_DIR)/stream-decoded +REPACK_STREAM_DEST_DIR=$(REPACK_GTA_DIR)/stream +REPACK_CUTS_ORIG_DIR?=$(REPACK_DIR)/cuts-orig +REPACK_CUTS_DC_DIR?=$(REPACK_DIR)/cuts-dc LIBS := TEXCONV_FLAGS := @@ -38,6 +46,9 @@ MKDCDISC_PAD_OPTION=-N ifeq ($(FOR_DISC),1) AUDIO_STREAM_OPTION=-q MKDCDISC_PAD_OPTION= +else ifeq ($(FOR_DISC),2) +AUDIO_STREAM_OPTION=-m +MKDCDISC_PAD_OPTION= endif all: $(TARGET) @@ -45,8 +56,8 @@ all: $(TARGET) include common.mk OBJS = $(RE3_OBJS) $(RW_OBJS) \ - ../src/audio/sampman_dc.o \ - ../src/prof/profiler.o + ../src/liberty/audio/sampman_dc.o \ + ../src/common/prof/profiler.o OBJS_TEXCONV = $(RW_OBJS:.o=.texconv.o) OBJS_TEXCONV += \ @@ -60,44 +71,51 @@ OBJS_TEXCONV += \ ../vendor/koshle/hlepvr_irq.texconv.o \ ../vendor/koshle/hlematrix3d.texconv.o \ ../vendor/librw/src/dc/vq.texconv.o \ - ../src/fakerw/fake.texconv.o \ - ../src/skel/crossplatform.texconv.o \ - ../src/rw/TxdStore.texconv.o \ - texconv.texconv.o \ + ../src/liberty/fakerw/fake.texconv.o \ + ../src/liberty/skel/crossplatform.texconv.o \ + ../src/liberty/rw/TxdStore.texconv.o \ + ../src/tools/texconv.texconv.o \ ../vendor/TriStripper/src/connectivity_graph.texconv.o \ ../vendor/TriStripper/src/policy.texconv.o \ ../vendor/TriStripper/src/tri_stripper.texconv.o \ - ../src/rw/VisibilityPlugins.texconv.o \ - ../src/rw/NodeName.texconv.o \ - ../src/animation/RpAnimBlend.texconv.o \ - ../src/animation/Bones.texconv.o \ - ../src/animation/AnimBlendAssociation.texconv.o \ - ../src/animation/AnimBlendNode.texconv.o \ - ../src/animation/AnimBlendClumpData.texconv.o \ - ../src/rw/MemoryMgr.texconv.o \ - ../src/math/Quaternion.texconv.o \ + ../src/liberty/rw/VisibilityPlugins.texconv.o \ + ../src/liberty/rw/NodeName.texconv.o \ + ../src/liberty/animation/RpAnimBlend.texconv.o \ + ../src/liberty/animation/Bones.texconv.o \ + ../src/liberty/animation/AnimBlendAssociation.texconv.o \ + ../src/liberty/animation/AnimBlendNode.texconv.o \ + ../src/liberty/animation/AnimBlendClumpData.texconv.o \ + ../src/liberty/rw/MemoryMgr.texconv.o \ + ../src/liberty/math/Quaternion.texconv.o \ + ../vendor/librw/src/ps2-x/ps2.texconv.o \ + ../vendor/librw/src/ps2-x/ps2device.texconv.o \ + ../vendor/librw/src/ps2-x/ps2raster.texconv.o \ + ../vendor/librw/src/ps2-x/ps2skin.texconv.o \ ../vendor/librw/src/d3d-x/d3d.texconv.o \ ../vendor/librw/src/d3d-x/d3d8.texconv.o \ ../vendor/librw/src/d3d-x/d3d8render.texconv.o \ + ../vendor/librw/src/bmp.texconv.o \ + ../vendor/librw/src/png.texconv.o \ + ../vendor/librw/src/lodepng/lodepng.texconv.o # Add compilation units to this list to explicity compile them with # -O3 optimizations, while the rest get the default (-Os) treatment # to conserve RAM. OBJS_O3 = \ ../vendor/librw/src/dc/rwdc.o \ - ../src/core/World.o \ - ../src/collision/Collision.o \ - ../src/math/math.o \ - ../src/math/Matrix.o \ - ../src/math/Quaternion.o \ - ../src/math/Rect.o \ - ../src/math/Vector.o \ + ../src/liberty/core/World.o \ + ../src/liberty/collision/Collision.o \ + ../src/liberty/math/math.o \ + ../src/liberty/math/Matrix.o \ + ../src/liberty/math/Quaternion.o \ + ../src/liberty/math/Rect.o \ + ../src/liberty/math/Vector.o \ ../vendor/librw/src/base.o \ - ../src/renderer/Shadows.o + ../src/liberty/renderer/Shadows.o OBJS_NO_FAST_MATH = \ - ../src/core/Cam.o \ - ../src/core/Camera.o + ../src/liberty/core/Cam.o \ + ../src/liberty/core/Camera.o KOS_CPPFLAGS += -fbuiltin -ffast-math -ffp-contract=fast \ -mfsrra -mfsca @@ -127,8 +145,8 @@ clean-texconv: -rm -f texconv clean-pvrtex: - $(MAKE) -C ./pvrtex clean - -rm -f ./pvrtex/pvrtex + $(MAKE) -C ../vendor/pvrtex clean + -rm -f ../vendor/pvrtex/pvrtex clean-objs: -rm -f $(OBJS) @@ -156,12 +174,12 @@ $(OBJS_NO_FAST_MATH): %.o: %.cpp $(TARGET): $(OBJS) kos-c++ -o $(TARGET) $(OBJS) -Wl,--gc-sections -Wl,--as-needed -Wl,-Map,output.map \ -flto=auto $(if $(WITH_IDE),-lkosfat) $(if $(WITH_SD),-lkosfat) -Wl,--build-id=sha1 - @echo && echo && echo "*** Build Completed Successfully ***" && echo && echo + @echo && echo && echo "*** Build Completed Successfully ($(TARGET)) ***" && echo && echo run: $(TARGET) $(KOS_LOADER) $(TARGET) -$(REPACK_GTA_DIR)/GTA3SF8.b: GTA3SF8.b +$(REPACK_GTA_DIR)/GTA3SF8.b: assets/GTA3SF8.b mkdir -p $(@D) cp $< $@ @@ -178,7 +196,7 @@ $(REPACK_GTA_DIR)/%.ico: %.ico .PHONY: pvrtex pvrtex: - $(MAKE) -C ./pvrtex + $(MAKE) -C ../vendor/pvrtex IP.BIN: rm -f IP.BIN @@ -216,28 +234,28 @@ $(PROJECT_NAME)-prebuilt.iso: IP.BIN 1ST_READ_PREBUILT.BIN $(REPACK_DIR)/repacke ifeq ($(HAVE_CDI4DC), yes) $(PROJECT_NAME).cdi: $(PROJECT_NAME).iso cdi4dc $(PROJECT_NAME).iso $(PROJECT_NAME).cdi > cdi.log - @echo && echo && echo "*** CDI Baked Successfully ***" && echo && echo + @echo && echo && echo "*** CDI Baked Successfully ($@) ***" && echo && echo $(PROJECT_NAME)-no-repack.cdi: $(PROJECT_NAME)-no-repack.iso cdi4dc $(PROJECT_NAME)-no-repack.iso $(PROJECT_NAME)-no-repack.cdi > cdi.log - @echo && echo && echo "*** CDI Baked Successfully ***" && echo && echo + @echo && echo && echo "*** CDI Baked Successfully ($@) ***" && echo && echo $(PROJECT_NAME)-prebuilt.cdi: $(PROJECT_NAME)-prebuilt.iso cdi4dc $(PROJECT_NAME).iso $(PROJECT_NAME).cdi > cdi.log rm 1ST_READ_PREBUILT.BIN - @echo && echo && echo "*** CDI Baked Successfully ***" && echo && echo + @echo && echo && echo "*** CDI Baked Successfully ($@) ***" && echo && echo else $(PROJECT_NAME).cdi: $(TARGET) $(REPACK_DIR)/repacked $(REPACK_GTA_DIR)/GTA3SF8.b $(REPACK_GTA_DIR)/settings.ico $(REPACK_GTA_DIR)/save.ico - mkdcdisc -e $(TARGET) -o $(PROJECT_NAME).cdi -d $(REPACK_GTA_DIR)/ $(MKDCDISC_PAD_OPTION) -n DCA3 -a "the gang" - @echo && echo && echo "*** CDI Baked Successfully ***" && echo && echo + mkdcdisc -e $(TARGET) -o $(PROJECT_NAME).cdi -d $(REPACK_GTA_DIR)/ $(MKDCDISC_PAD_OPTION) -n $(PROJECT_NAME) -a $(TEAM_NAME) -s $(DISC_SERIAL) -r $(RELEASE_DATE) + @echo && echo && echo "*** CDI Baked Successfully ($@) ***" && echo && echo $(PROJECT_NAME)-no-repack.cdi: $(TARGET) $(REPACK_GTA_DIR)/GTA3SF8.b $(REPACK_GTA_DIR)/settings.ico $(REPACK_GTA_DIR)/save.ico - mkdcdisc -e $(TARGET) -o $(PROJECT_NAME)-no-repack.cdi -d $(REPACK_GTA_DIR)/ $(MKDCDISC_PAD_OPTION) -n DCA3 -a "the gang" - @echo && echo && echo "*** CDI Baked Successfully ***" && echo && echo + mkdcdisc -e $(TARGET) -o $(PROJECT_NAME)-no-repack.cdi -d $(REPACK_GTA_DIR)/ $(MKDCDISC_PAD_OPTION) -n $(PROJECT_NAME) -a $(TEAM_NAME) -s $(DISC_SERIAL) -r $(RELEASE_DATE) + @echo && echo && echo "*** CDI Baked Successfully ($@) ***" && echo && echo $(PROJECT_NAME)-prebuilt.cdi: $(REPACK_DIR)/repacked $(REPACK_GTA_DIR)/GTA3SF8.b $(REPACK_GTA_DIR)/settings.ico $(REPACK_GTA_DIR)/save.ico - mkdcdisc -e $(TARGET) -o $(PROJECT_NAME).cdi -d $(REPACK_GTA_DIR)/ $(MKDCDISC_PAD_OPTION) -n DCA3 -a "the gang" - @echo && echo && echo "*** CDI Baked Successfully ***" && echo && echo + mkdcdisc -e $(TARGET) -o $(PROJECT_NAME).cdi -d $(REPACK_GTA_DIR)/ $(MKDCDISC_PAD_OPTION) -n $(PROJECT_NAME) -a $(TEAM_NAME) -s $(DISC_SERIAL) -r $(RELEASE_DATE) + @echo && echo && echo "*** CDI Baked Successfully ($@) ***" && echo && echo endif cdi: $(PROJECT_NAME).cdi @@ -263,23 +281,32 @@ gprof: @echo "\033[42m Profiling data saved to $(TARGET)-kernel.png \033[0m" # tools -imgtool: imgtool.cpp - $(CXX) -std=c++17 -o $@ -Og $< +imgtool: ../src/tools/imgtool.cpp + $(CXX) -std=c++17 -o $@ -O0 -g $< -extract-sfx: extract-sfx.cpp - $(CXX) -std=c++17 -o $@ -Og $< +extract-sfx: ../src/tools/extract-sfx.cpp + $(CXX) -std=c++17 -o $@ -O0 -g $< -pack-sfx: pack-sfx.cpp - $(CXX) -std=c++17 -o $@ -Og $< +pack-sfx: ../src/tools/pack-sfx.cpp + $(CXX) -std=c++17 -o $@ -O0 -g $< -aud2adpcm: aud2adpcm.c +aud2adpcm: ../src/tools/aud2adpcm.c $(CC) -o $@ -O3 -g $< -I../vendor/minimp3 texconv: $(OBJS_TEXCONV) | pvrtex # You'll have to rebuild pvrtex manually if you change it $(CXX) -o $@ $(OBJS_TEXCONV) %.texconv.o: %.cpp - $(CXX) -std=c++2a -c -O3 -g -MMD -MP -o $@ -I../vendor/koshle $(INCLUDE) -I../vendor/emu -I../vendor/crypto -I../vendor/TriStripper/include $(DEFINES) -DDC_TEXCONV -DDC_SIM -D_INC_WINDOWS $(TEXCONV_FLAGS) $< + $(CXX) -std=c++2a -c -O3 -g -MMD -MP -o $@ -I../vendor/koshle -I../vendor/librw/src $(INCLUDE) -I../vendor/emu -I../vendor/crypto -I../vendor/TriStripper/include $(DEFINES) -DDC_TEXCONV -DDC_SIM -D_INC_WINDOWS $(TEXCONV_FLAGS) $< + +animtool: ../src/tools/animtool.cpp + $(CXX) -std=c++17 -o $@ -g -O0 $< + +coltool: ../src/tools/coltool.cpp + $(CXX) -std=c++17 -o $@ -g -O0 $< + +streamheaderpack: ../src/tools/streamheaderpack.cpp + $(CXX) -std=c++17 -o $@ -g -O0 $< -include $(DEPS) @@ -320,6 +347,22 @@ TXD_OPTS_NEWS = 512 512 TXD_OPTS_SPLASH1 = 512 512 TXD_OPTS_SPLASH2 = 512 512 TXD_OPTS_SPLASH3 = 512 512 +TXD_OPTS_frontend = 512 512 \ + --delete-tex "fe_arrows4" \ + --delete-tex "fe_arrows2" \ + --delete-tex "fe_arrows1" \ + --delete-tex "fe_controllersh" \ + --delete-tex "fe_controller" \ + \ + --include-tex assets/dc_ps2d.png dc_ps2d \ + --include-tex assets/dc_ps2f.png dc_ps2f \ + --include-tex assets/dc_xboxd.png dc_xboxd \ + --include-tex assets/dc_xboxf.png dc_xboxf \ + --include-tex assets/ps4_d.png ps4_d \ + --include-tex assets/ps4_f.png ps4_f \ + --include-tex assets/xbox_d.png xbox_d \ + --include-tex assets/xbox_f.png xbox_f + DEFAULT_RES = 512 PVR_ENCODER ?= PVRTEX @@ -333,19 +376,28 @@ TEXTURE_DOWNSAMPLE_IMG ?= HALF -include sfxlooplist.mk -include wavlist.mk -include mp3list.mk +-include cuts.mk IMG_TEXTURES_DC = $(addprefix $(REPACK_IMG_DC_DIR)/, $(IMG_TEXTURES)) IMG_MODELS_DC = $(addprefix $(REPACK_IMG_DC_DIR)/, $(IMG_MODELS)) + +CUTS_IFP_DC = $(addprefix $(REPACK_CUTS_DC_DIR)/, $(CUTS_IFP)) +CUTS_MISC_DC = $(addprefix $(REPACK_CUTS_DC_DIR)/, $(CUTS_MISC)) + LOOSE_FILES_DC = $(addprefix $(REPACK_GTA_DIR)/, $(MISC_FILES)) + SFX_DC_DIR = $(REPACK_GTA_DIR)/sfx SFX_DC_RAW = $(SFX_DC_DIR)/sfx_all.raw SFX_DC_DSC = $(SFX_DC_DIR)/sfx_all.dsc -STREAM_ADPCM_DC = $(addprefix $(REPACK_GTA_DIR)/stream/, $(STREAM_WAV:.wav=.APM)) \ - $(addprefix $(REPACK_GTA_DIR)/stream/, $(STREAM_MP3:.mp3=.APM)) +STREAM_ADPCM_DC = $(addprefix $(REPACK_STREAM_DEST_DIR)/, $(STREAM_WAV:.wav=.APM)) \ + $(addprefix $(REPACK_STREAM_DEST_DIR)/, $(STREAM_MP3:.mp3=.APM)) IMG_TEXTURES_ORIG = $(addprefix $(REPACK_IMG_ORIG_DIR)/, $(IMG_TEXTURES)) IMG_MODELS_ORIG = $(addprefix $(REPACK_IMG_ORIG_DIR)/, $(IMG_MODELS)) +CUTS_IFP_ORIG = $(addprefix $(REPACK_CUTS_ORIG_DIR)/, $(CUTS_IFP)) +CUTS_MISC_ORIG = $(addprefix $(REPACK_CUTS_ORIG_DIR)/, $(CUTS_MISC)) + SFX_ORIG = $(addprefix $(REPACK_SFX_ORIG_DIR)/, $(SFX_WAV)) SFX_ORIG_LOOP = $(addprefix $(REPACK_SFX_ORIG_DIR)/, $(SFX_LOOP_WAV)) SFX_REPACK_DC_WAV = $(addprefix $(REPACK_SFX_DC_DIR)/, $(SFX_WAV) $(SFX_LOOP_WAV)) @@ -355,11 +407,13 @@ STREAM_WAV_DECODED = $(addprefix $(REPACK_STREAM_DECODED_DIR)/, $(STREAM_WAV)) .PRECIOUS: $(SFX_ORIG) $(SFX_REPACK_DC) $(STREAM_MP3_DECODED) $(STREAM_WAV_DECODED) -$(REPACK_DIR)/repacked: $(REPACK_GTA_DIR)/models/gta3.img $(REPACK_GTA_DIR)/models/gta3.dir $(LOOSE_FILES_DC) $(STREAM_ADPCM_DC) $(SFX_DC_RAW) $(SFX_DC_DSC) +$(REPACK_DIR)/repacked: $(REPACK_GTA_DIR)/models/gta3.img $(REPACK_GTA_DIR)/models/gta3.dir $(LOOSE_FILES_DC) $(STREAM_ADPCM_DC) $(SFX_DC_RAW) $(SFX_DC_DSC) streamheaderpack mkdir -p $(@D) +# $(REPACK_GTA_DIR) needed as first argument as paths in the game prefix with stream/ + ./streamheaderpack liberty $(REPACK_GTA_DIR) $(REPACK_STREAM_DEST_DIR)/hdr.bin @git archive --format zip --output "$(REPACK_GTA_DIR)/DCA3-$(GIT_VERSION).zip" HEAD @touch $@ - @echo && echo && echo "*** Repack Completed Successfully ***" && echo && echo + @echo && echo && echo "*** Repack Completed Successfully ($(PROJECT_NAME)) ***" && echo && echo $(REPACK_DIR)/unpacked: imgtool $(GTA_DIR)/models/gta3.img $(GTA_DIR)/models/gta3.dir mkdir -p $(@D) @@ -369,6 +423,15 @@ $(REPACK_DIR)/unpacked: imgtool $(GTA_DIR)/models/gta3.img $(GTA_DIR)/models/gta $(IMG_TEXTURES_ORIG) $(IMG_MODELS_ORIG): $(REPACK_DIR)/unpacked @touch $@ +$(REPACK_DIR)/unpacked-cuts: imgtool $(GTA_DIR)/anim/cuts.img $(GTA_DIR)/anim/cuts.dir + mkdir -p $(@D) + ./imgtool unpack "$(GTA_DIR)/anim/cuts" "$(REPACK_CUTS_ORIG_DIR)" + @touch $@ + +$(CUTS_IFP_ORIG) $(CUTS_MISC_ORIG): $(REPACK_DIR)/unpacked-cuts + @test -f $@ + @touch $@ + # First try the mods img directory $(REPACK_IMG_DC_DIR)/%.dff: $(GTA_MOD_IMG_DIR)/%.dff texconv @mkdir -p $(@D) @@ -401,6 +464,30 @@ $(REPACK_IMG_DC_DIR)/%.TXD: $(REPACK_IMG_ORIG_DIR)/%.TXD texconv @mkdir -p $(@D) ./texconv $< $@ $(DEFAULT_RES) $(DEFAULT_RES) -e $(PVR_ENCODER) -d $(TEXTURE_DOWNSAMPLE_IMG) + +### cuts +# First try the mods img directory +$(REPACK_CUTS_DC_DIR)/%.dat: $(GTA_MOD_CUTS_DIR)/%.dat + @mkdir -p $(@D) + cp $< $@ +$(REPACK_CUTS_DC_DIR)/%.anm: $(GTA_MOD_CUTS_DIR)/%.anm + @mkdir -p $(@D) + cp $< $@ +$(REPACK_CUTS_DC_DIR)/%.ifp: $(GTA_MOD_CUTS_DIR)/%.ifp animtool + @mkdir -p $(@D) + ./animtool $< $@ + +# if not, the extracted img directory +$(REPACK_CUTS_DC_DIR)/%.dat: $(REPACK_CUTS_ORIG_DIR)/%.dat + @mkdir -p $(@D) + cp $< $@ +$(REPACK_CUTS_DC_DIR)/%.anm: $(REPACK_CUTS_ORIG_DIR)/%.anm + @mkdir -p $(@D) + cp $< $@ +$(REPACK_CUTS_DC_DIR)/%.ifp: $(REPACK_CUTS_ORIG_DIR)/%.ifp animtool + @mkdir -p $(@D) + ./animtool $< $@ + # first try the mods loose directory $(REPACK_GTA_DIR)/%.dff: $(GTA_MOD_LOOSE_DIR)/%.dff texconv @mkdir -p $(@D) @@ -426,6 +513,14 @@ $(REPACK_GTA_DIR)/%.TXD: $(GTA_MOD_LOOSE_DIR)/%.TXD texconv @mkdir -p $(@D) ./texconv $< $@ 1024 1024 -e $(PVR_ENCODER) -d NONE +# then the gamefiles directory +$(REPACK_GTA_DIR)/%.txd: $(GTA_GAMEFILES_LOOSE_DIR)/%.txd texconv + @mkdir -p $(@D) + ./texconv $< $@ $(TXD_OPTS_$(notdir $*)) -e $(PVR_ENCODER) -d $(TEXTURE_DOWNSAMPLE_TXD) +$(REPACK_GTA_DIR)/%.TXD: $(GTA_GAMEFILES_LOOSE_DIR)/%.TXD texconv + @mkdir -p $(@D) + ./texconv $< $@ $(TXD_OPTS_$(notdir $*)) -e $(PVR_ENCODER) -d $(TEXTURE_DOWNSAMPLE_TXD) + # if not, the original files $(REPACK_GTA_DIR)/%.txd: $(GTA_DIR)/%.txd texconv @mkdir -p $(@D) @@ -434,6 +529,38 @@ $(REPACK_GTA_DIR)/%.TXD: $(GTA_DIR)/%.TXD texconv @mkdir -p $(@D) ./texconv $< $@ $(TXD_OPTS_$(notdir $*)) -e $(PVR_ENCODER) -d $(TEXTURE_DOWNSAMPLE_TXD) +# first try the mods loose directory +$(REPACK_GTA_DIR)/%.ifp: $(GTA_MOD_LOOSE_DIR)/%.ifp animtool + @mkdir -p $(@D) + ./animtool $< $@ +$(REPACK_GTA_DIR)/%.IFP: $(GTA_MOD_LOOSE_DIR)/%.IFP animtool + @mkdir -p $(@D) + ./animtool $< $@ + +#if not, the original files +$(REPACK_GTA_DIR)/%.ifp: $(GTA_DIR)/%.ifp animtool + @mkdir -p $(@D) + ./animtool $< $@ +$(REPACK_GTA_DIR)/%.IFP: $(GTA_DIR)/%.IFP animtool + @mkdir -p $(@D) + ./animtool $< $@ + +# first try the mods loose directory +$(REPACK_GTA_DIR)/%.col: $(GTA_MOD_LOOSE_DIR)/%.col coltool + @mkdir -p $(@D) + ./coltool $< $@ +$(REPACK_GTA_DIR)/%.COL: $(GTA_MOD_LOOSE_DIR)/%.COL coltool + @mkdir -p $(@D) + ./coltool $< $@ + +#if not, the original files +$(REPACK_GTA_DIR)/%.col: $(GTA_DIR)/%.col coltool + @mkdir -p $(@D) + ./coltool $< $@ +$(REPACK_GTA_DIR)/%.COL: $(GTA_DIR)/%.COL coltool + @mkdir -p $(@D) + ./coltool $< $@ + $(REPACK_DIR)/packed: $(IMG_TEXTURES_DC) $(IMG_MODELS_DC) mkdir -p $(@D) mkdir -p "$(REPACK_GTA_DIR)/models/gta3" @@ -443,6 +570,15 @@ $(REPACK_DIR)/packed: $(IMG_TEXTURES_DC) $(IMG_MODELS_DC) $(REPACK_GTA_DIR)/models/gta3.img $(REPACK_GTA_DIR)/models/gta3.dir: $(REPACK_DIR)/packed @touch $@ +$(REPACK_DIR)/packed-cuts: $(CUTS_IFP_DC) $(CUTS_MISC_DC) + mkdir -p $(@D) + mkdir -p "$(REPACK_GTA_DIR)/anim/cuts" + ./imgtool pack "$(REPACK_GTA_DIR)/anim/cuts" "$(REPACK_CUTS_DC_DIR)" + @touch $@ + +$(REPACK_GTA_DIR)/anim/cuts.img $(REPACK_GTA_DIR)/anim/cuts.dir: $(REPACK_DIR)/packed-cuts + @touch $@ + # sfx processing $(REPACK_DIR)/unpacked-sfx: extract-sfx $(GTA_DIR)/audio/sfx.SDT $(GTA_DIR)/audio/sfx.RAW mkdir -p $(@D) @@ -469,24 +605,36 @@ $(REPACK_SFX_DC_DIR)/%.pcm: $(REPACK_SFX_ORIG_DIR)/%.wav aud2adpcm # stream processing # first try the mods loose directory -$(REPACK_GTA_DIR)/stream/%.APM: $(GTA_MOD_LOOSE_DIR)/audio/%.wav aud2adpcm +$(REPACK_STREAM_DEST_DIR)/%.APM: $(GTA_MOD_LOOSE_DIR)/audio/%.wav aud2adpcm @mkdir -p $(@D) ./aud2adpcm $(AUDIO_STREAM_OPTION) $< $@ -$(REPACK_GTA_DIR)/stream/%.APM: $(GTA_MOD_LOOSE_DIR)/audio/%.mp3 aud2adpcm +$(REPACK_STREAM_DEST_DIR)/%.APM: $(GTA_MOD_LOOSE_DIR)/audio/%.mp3 aud2adpcm @mkdir -p $(@D) ./aud2adpcm $(AUDIO_STREAM_OPTION) $< $@ # then original folder -$(REPACK_GTA_DIR)/stream/%.APM: $(GTA_DIR)/audio/%.wav aud2adpcm +$(REPACK_STREAM_DEST_DIR)/%.APM: $(GTA_DIR)/audio/%.wav aud2adpcm @mkdir -p $(@D) ./aud2adpcm $(AUDIO_STREAM_OPTION) $< $@ -$(REPACK_GTA_DIR)/stream/%.APM: $(GTA_DIR)/audio/%.mp3 aud2adpcm +$(REPACK_STREAM_DEST_DIR)/%.APM: $(GTA_DIR)/audio/%.mp3 aud2adpcm @mkdir -p $(@D) ./aud2adpcm $(AUDIO_STREAM_OPTION) $< $@ -# Note: This is last so it has least priority, files should be processed if possible +# Note: These are last so they have least priority, files should be processed if possible + +# first try mods dir +$(REPACK_GTA_DIR)/%: $(GTA_MOD_LOOSE_DIR)/% + @mkdir -p $(@D) + cp $< $@ + +# then gamefiles +$(REPACK_GTA_DIR)/%: $(GTA_GAMEFILES_LOOSE_DIR)/% + @mkdir -p $(@D) + cp $< $@ + +# then original files $(REPACK_GTA_DIR)/%: $(GTA_DIR)/% @mkdir -p $(@D) cp $< $@ diff --git a/dreamcast/GTA3SF8.b b/liberty/assets/GTA3SF8.b similarity index 100% rename from dreamcast/GTA3SF8.b rename to liberty/assets/GTA3SF8.b diff --git a/liberty/assets/dc_ps2d.png b/liberty/assets/dc_ps2d.png new file mode 100644 index 00000000..c66449f9 Binary files /dev/null and b/liberty/assets/dc_ps2d.png differ diff --git a/liberty/assets/dc_ps2f.png b/liberty/assets/dc_ps2f.png new file mode 100644 index 00000000..df2b1d27 Binary files /dev/null and b/liberty/assets/dc_ps2f.png differ diff --git a/liberty/assets/dc_xboxd.png b/liberty/assets/dc_xboxd.png new file mode 100644 index 00000000..99b88447 Binary files /dev/null and b/liberty/assets/dc_xboxd.png differ diff --git a/liberty/assets/dc_xboxf.png b/liberty/assets/dc_xboxf.png new file mode 100644 index 00000000..112a5b59 Binary files /dev/null and b/liberty/assets/dc_xboxf.png differ diff --git a/liberty/assets/ps4_d.png b/liberty/assets/ps4_d.png new file mode 100644 index 00000000..b0984297 Binary files /dev/null and b/liberty/assets/ps4_d.png differ diff --git a/liberty/assets/ps4_f.png b/liberty/assets/ps4_f.png new file mode 100644 index 00000000..a1f8ac49 Binary files /dev/null and b/liberty/assets/ps4_f.png differ diff --git a/liberty/assets/xbox_d.png b/liberty/assets/xbox_d.png new file mode 100644 index 00000000..b0984297 Binary files /dev/null and b/liberty/assets/xbox_d.png differ diff --git a/liberty/assets/xbox_f.png b/liberty/assets/xbox_f.png new file mode 100644 index 00000000..6afb0d7b Binary files /dev/null and b/liberty/assets/xbox_f.png differ diff --git a/liberty/common.mk b/liberty/common.mk new file mode 100644 index 00000000..d372495a --- /dev/null +++ b/liberty/common.mk @@ -0,0 +1,401 @@ +GIT_VERSION := $(shell git describe --always --tags --long --dirty 2>/dev/null || echo "NO_GIT") +CI_JOB_ID ?= 00000000 + + +git-version/git-version.tmp: + @echo "Generating git-version.tmp with GIT_VERSION = \"$(GIT_VERSION)\"" + @mkdir -p git-version + @echo "#pragma once" > git-version/git-version.tmp + @echo "#ifndef VERSION_H" >> git-version/git-version.tmp + @echo "#define VERSION_H" >> git-version/git-version.tmp + @echo "#define GIT_VERSION \"$(GIT_VERSION)\"" >> git-version/git-version.tmp + @echo "#define CI_JOB_ID \"$(CI_JOB_ID)\"" >> git-version/git-version.tmp + @echo "#endif // VERSION_H" >> git-version/git-version.tmp + +git-version/git-version.h: git-version/git-version.tmp + @if [ ! -f git-version/git-version.h ] || ! cmp -s git-version/git-version.tmp git-version/git-version.h; then \ + echo "Updating git-version.h"; \ + cp git-version/git-version.tmp git-version/git-version.h; \ + else \ + echo "git-version.h is up to date. No change."; \ + fi + +.PHONY: git-version/git-version.tmp + +../src/liberty/skel/dc/dc.cpp: git-version/git-version.h + + +# List all of your C files here, but change the extension to ".o" +# Include "romdisk.o" if you want a rom disk. +RE3_OBJS = \ + ../src/liberty/animation/AnimBlendAssocGroup.o \ + ../src/liberty/animation/AnimBlendAssociation.o \ + ../src/liberty/animation/AnimBlendClumpData.o \ + ../src/liberty/animation/AnimBlendHierarchy.o \ + ../src/liberty/animation/AnimBlendNode.o \ + ../src/liberty/animation/AnimBlendSequence.o \ + ../src/liberty/animation/AnimManager.o \ + ../src/liberty/animation/Bones.o \ + ../src/liberty/animation/CutsceneMgr.o \ + ../src/liberty/animation/FrameUpdate.o \ + ../src/liberty/animation/RpAnimBlend.o \ + \ + ../src/liberty/buildings/Building.o \ + ../src/liberty/buildings/Treadable.o \ + \ + ../src/liberty/collision/ColBox.o \ + ../src/liberty/collision/ColLine.o \ + ../src/liberty/collision/Collision.o \ + ../src/liberty/collision/ColModel.o \ + ../src/liberty/collision/ColPoint.o \ + ../src/liberty/collision/ColSphere.o \ + ../src/liberty/collision/ColTriangle.o \ + ../src/liberty/collision/TempColModels.o \ + ../src/liberty/collision/VuCollision.o \ + \ + ../src/liberty/control/AutoPilot.o \ + ../src/liberty/control/Bridge.o \ + ../src/liberty/control/CarAI.o \ + ../src/liberty/control/CarCtrl.o \ + ../src/liberty/control/Curves.o \ + ../src/liberty/control/Darkel.o \ + ../src/liberty/control/GameLogic.o \ + ../src/liberty/control/Garages.o \ + ../src/liberty/control/NameGrid.o \ + ../src/liberty/control/OnscreenTimer.o \ + ../src/liberty/control/PathFind.o \ + ../src/liberty/control/Phones.o \ + ../src/liberty/control/Pickups.o \ + ../src/liberty/control/PowerPoints.o \ + ../src/liberty/control/Record.o \ + ../src/liberty/control/Remote.o \ + ../src/liberty/control/Replay.o \ + ../src/liberty/control/Restart.o \ + ../src/liberty/control/RoadBlocks.o \ + ../src/liberty/control/SceneEdit.o \ + ../src/liberty/control/Script.o \ + ../src/liberty/control/Script2.o \ + ../src/liberty/control/Script3.o \ + ../src/liberty/control/Script4.o \ + ../src/liberty/control/Script5.o \ + ../src/liberty/control/Script6.o \ + ../src/liberty/control/ScriptDebug.o \ + ../src/liberty/control/TrafficLights.o \ + \ + ../src/liberty/core/Accident.o \ + ../src/liberty/core/Cam.o \ + ../src/liberty/core/Camera.o \ + ../src/liberty/core/CdStreamDC.o \ + ../src/liberty/core/Clock.o \ + ../src/liberty/core/ControllerConfig.o \ + ../src/liberty/core/Debug.o \ + ../src/liberty/core/Directory.o \ + ../src/liberty/core/EventList.o \ + ../src/liberty/core/FileLoader.o \ + ../src/liberty/core/FileMgr.o \ + ../src/liberty/core/Fire.o \ + ../src/liberty/core/Frontend.o \ + ../src/liberty/core/FrontEndControls.o \ + ../src/liberty/core/Frontend_PS2.o \ + ../src/liberty/core/Game.o \ + ../src/liberty/core/IniFile.o \ + ../src/liberty/core/Lists.o \ + ../src/liberty/core/main.o \ + ../src/liberty/core/MenuScreens.o \ + ../src/liberty/core/MenuScreensCustom.o \ + ../src/liberty/core/obrstr.o \ + ../src/liberty/core/Pad.o \ + ../src/liberty/core/Placeable.o \ + ../src/liberty/core/PlayerInfo.o \ + ../src/liberty/core/Pools.o \ + ../src/liberty/core/Profile.o \ + ../src/liberty/core/Radar.o \ + ../src/liberty/core/Range2D.o \ + ../src/liberty/core/Range3D.o \ + ../src/liberty/core/re3.o \ + ../src/liberty/core/References.o \ + ../src/liberty/core/Stats.o \ + ../src/liberty/core/Streaming.o \ + ../src/liberty/core/SurfaceTable.o \ + ../src/liberty/core/timebars.o \ + ../src/liberty/core/Timer.o \ + ../src/liberty/core/TimeStep.o \ + ../src/liberty/core/User.o \ + ../src/liberty/core/Wanted.o \ + ../src/liberty/core/World.o \ + ../src/liberty/core/ZoneCull.o \ + ../src/liberty/core/Zones.o \ + \ + ../src/liberty/entities/Dummy.o \ + ../src/liberty/entities/Entity.o \ + ../src/liberty/entities/Physical.o \ + \ + ../src/liberty/fakerw/fake.o \ + \ + ../src/liberty/math/math.o \ + ../src/liberty/math/Matrix.o \ + ../src/liberty/math/Quaternion.o \ + ../src/liberty/math/Rect.o \ + ../src/liberty/math/Vector.o \ + \ + ../src/liberty/modelinfo/BaseModelInfo.o \ + ../src/liberty/modelinfo/ClumpModelInfo.o \ + ../src/liberty/modelinfo/MloModelInfo.o \ + ../src/liberty/modelinfo/ModelIndices.o \ + ../src/liberty/modelinfo/ModelInfo.o \ + ../src/liberty/modelinfo/PedModelInfo.o \ + ../src/liberty/modelinfo/SimpleModelInfo.o \ + ../src/liberty/modelinfo/TimeModelInfo.o \ + ../src/liberty/modelinfo/VehicleModelInfo.o \ + \ + ../src/liberty/objects/CutsceneHead.o \ + ../src/liberty/objects/CutsceneObject.o \ + ../src/liberty/objects/DummyObject.o \ + ../src/liberty/objects/Object.o \ + ../src/liberty/objects/ObjectData.o \ + ../src/liberty/objects/ParticleObject.o \ + ../src/liberty/objects/Projectile.o \ + \ + ../src/liberty/peds/CivilianPed.o \ + ../src/liberty/peds/CopPed.o \ + ../src/liberty/peds/EmergencyPed.o \ + ../src/liberty/peds/Gangs.o \ + ../src/liberty/peds/Ped.o \ + ../src/liberty/peds/PedAI.o \ + ../src/liberty/peds/PedChat.o \ + ../src/liberty/peds/PedDebug.o \ + ../src/liberty/peds/PedFight.o \ + ../src/liberty/peds/PedIK.o \ + ../src/liberty/peds/PedPlacement.o \ + ../src/liberty/peds/PedRoutes.o \ + ../src/liberty/peds/PedType.o \ + ../src/liberty/peds/PlayerPed.o \ + ../src/liberty/peds/Population.o \ + \ + ../src/liberty/renderer/Antennas.o \ + ../src/liberty/renderer/Clouds.o \ + ../src/liberty/renderer/Console.o \ + ../src/liberty/renderer/Coronas.o \ + ../src/liberty/renderer/Credits.o \ + ../src/liberty/renderer/Draw.o \ + ../src/liberty/renderer/Fluff.o \ + ../src/liberty/renderer/Font.o \ + ../src/liberty/renderer/Glass.o \ + ../src/liberty/renderer/Hud.o \ + ../src/liberty/renderer/Instance.o \ + ../src/liberty/renderer/Lines.o \ + ../src/liberty/renderer/MBlur.o \ + ../src/liberty/renderer/Particle.o \ + ../src/liberty/renderer/ParticleMgr.o \ + ../src/liberty/renderer/PlayerSkin.o \ + ../src/liberty/renderer/PointLights.o \ + ../src/liberty/renderer/RenderBuffer.o \ + ../src/liberty/renderer/Renderer.o \ + ../src/liberty/renderer/Rubbish.o \ + ../src/liberty/renderer/Shadows.o \ + ../src/liberty/renderer/Skidmarks.o \ + ../src/liberty/renderer/SpecialFX.o \ + ../src/liberty/renderer/Sprite.o \ + ../src/liberty/renderer/Sprite2d.o \ + ../src/liberty/renderer/TexList.o \ + ../src/liberty/renderer/Timecycle.o \ + ../src/liberty/renderer/WaterCannon.o \ + ../src/liberty/renderer/WaterLevel.o \ + ../src/liberty/renderer/Weather.o \ + \ + ../src/liberty/rw/ClumpRead.o \ + ../src/liberty/rw/Lights.o \ + ../src/liberty/rw/MemoryHeap.o \ + ../src/liberty/rw/MemoryMgr.o \ + ../src/liberty/rw/NodeName.o \ + ../src/liberty/rw/RwHelper.o \ + ../src/liberty/rw/RwMatFX.o \ + ../src/liberty/rw/RwPS2AlphaTest.o \ + ../src/liberty/rw/TexRead.o \ + ../src/liberty/rw/TexturePools.o \ + ../src/liberty/rw/TxdStore.o \ + ../src/liberty/rw/VisibilityPlugins.o \ + \ + ../src/liberty/skel/crossplatform.o \ + ../src/liberty/skel/events.o \ + ../src/liberty/skel/skeleton.o \ + ../src/liberty/skel/dc/dc.o \ + \ + ../src/liberty/text/Messages.o \ + ../src/liberty/text/Pager.o \ + ../src/liberty/text/Text.o \ + \ + ../src/liberty/vehicles/Automobile.o \ + ../src/liberty/vehicles/Boat.o \ + ../src/liberty/vehicles/CarGen.o \ + ../src/liberty/vehicles/Cranes.o \ + ../src/liberty/vehicles/DamageManager.o \ + ../src/liberty/vehicles/Door.o \ + ../src/liberty/vehicles/Floater.o \ + ../src/liberty/vehicles/HandlingMgr.o \ + ../src/liberty/vehicles/Heli.o \ + ../src/liberty/vehicles/Plane.o \ + ../src/liberty/vehicles/Train.o \ + ../src/liberty/vehicles/Transmission.o \ + ../src/liberty/vehicles/Vehicle.o \ + \ + ../src/liberty/weapons/BulletInfo.o \ + ../src/liberty/weapons/Explosion.o \ + ../src/liberty/weapons/ProjectileInfo.o \ + ../src/liberty/weapons/ShotInfo.o \ + ../src/liberty/weapons/Weapon.o \ + ../src/liberty/weapons/WeaponEffects.o \ + ../src/liberty/weapons/WeaponInfo.o \ + \ + ../src/liberty/audio/AudioCollision.o \ + ../src/liberty/audio/AudioLogic.o \ + ../src/liberty/audio/AudioManager.o \ + ../src/liberty/audio/AudioScriptObject.o \ + ../src/liberty/audio/DMAudio.o \ + ../src/liberty/audio/MusicManager.o \ + ../src/liberty/audio/PolRadio.o \ + ../src/liberty/audio/sampman_miles.o \ + ../src/liberty/audio/sampman_oal.o \ + \ + ../src/liberty/save/Date.o \ + ../src/liberty/save/GenericGameStorage.o \ + ../src/liberty/save/MemoryCard.o \ + ../src/liberty/save/PCSave.o \ + \ + ../src/liberty/extras/debugmenu.o \ + ../src/liberty/extras/frontendoption.o \ + ../src/liberty/extras/postfx.o \ + ../src/liberty/extras/screendroplets.o \ + \ + ../src/common/vmu/vmu.o \ + ../vendor/miniLZO/minilzo.o \ + \ + +# Excluded \ + ../src/liberty/extras/custompipes.o \ + ../src/liberty/extras/custompipes_d3d9.o \ + ../src/liberty/extras/custompipes_gl.o \ + ../src/liberty/core/CdStream.o \ + ../src/liberty/core/CdStreamPosix.o \ + ../src/liberty/extras \ + ../src/liberty/extras/GitSHA1.cpp.in \ + ../src/liberty/core/AnimViewer.o \ + +RW_OBJS = \ + ../vendor/librw/src/anim.o \ + ../vendor/librw/src/base.o \ + ../vendor/librw/src/camera.o \ + ../vendor/librw/src/charset.o \ + ../vendor/librw/src/clump.o \ + ../vendor/librw/src/engine.o \ + ../vendor/librw/src/error.o \ + ../vendor/librw/src/frame.o \ + ../vendor/librw/src/geometry.o \ + ../vendor/librw/src/geoplg.o \ + ../vendor/librw/src/hanim.o \ + ../vendor/librw/src/image.o \ + ../vendor/librw/src/light.o \ + ../vendor/librw/src/matfx.o \ + ../vendor/librw/src/pipeline.o \ + ../vendor/librw/src/plg.o \ + ../vendor/librw/src/prim.o \ + ../vendor/librw/src/raster.o \ + ../vendor/librw/src/render.o \ + ../vendor/librw/src/skin.o \ + ../vendor/librw/src/texture.o \ + ../vendor/librw/src/tristrip.o \ + ../vendor/librw/src/userdata.o \ + ../vendor/librw/src/uvanim.o \ + ../vendor/librw/src/world.o \ + \ + ../vendor/librw/src/dc/rwdc.o \ + ../vendor/librw/src/dc/alloc.o + +# Excluded \ + ../vendor/librw/src/d3d-x/d3d.o \ + ../vendor/librw/src/d3d-x/d3d8.o \ + ../vendor/librw/src/d3d-x/d3d8render.o \ + ../vendor/librw/src/d3d/d3d8.o \ + ../vendor/librw/src/d3d/d3d8matfx.o \ + ../vendor/librw/src/d3d/d3d8render.o \ + ../vendor/librw/src/d3d/d3d8skin.o \ + ../vendor/librw/src/d3d/d3d9.o \ + ../vendor/librw/src/d3d/d3d9matfx.o \ + ../vendor/librw/src/d3d/d3d9render.o \ + ../vendor/librw/src/d3d/d3d9skin.o \ + ../vendor/librw/src/d3d/d3d.o \ + ../vendor/librw/src/d3d/d3ddevice.o \ + ../vendor/librw/src/d3d/d3dimmed.o \ + ../vendor/librw/src/d3d/d3drender.o \ + ../vendor/librw/src/d3d/xbox.o \ + ../vendor/librw/src/d3d/xboxmatfx.o \ + ../vendor/librw/src/d3d/xboxskin.o \ + ../vendor/librw/src/d3d/xboxvfmt.o \ + \ + ../vendor/librw/src/gl/gl3.o \ + ../vendor/librw/src/gl/gl3device.o \ + ../vendor/librw/src/gl/gl3immed.o \ + ../vendor/librw/src/gl/gl3matfx.o \ + ../vendor/librw/src/gl/gl3pipe.o \ + ../vendor/librw/src/gl/gl3raster.o \ + ../vendor/librw/src/gl/gl3render.o \ + ../vendor/librw/src/gl/gl3shader.o \ + ../vendor/librw/src/gl/gl3skin.o \ + ../vendor/librw/src/gl/wdgl.o \ + ../vendor/librw/src/gl/glad/glad.cXXX \ + \ + ../vendor/librw/src/ps2/pds.o \ + ../vendor/librw/src/ps2/ps2.o \ + ../vendor/librw/src/ps2/ps2device.o \ + ../vendor/librw/src/ps2/ps2matfx.o \ + ../vendor/librw/src/ps2/ps2raster.o \ + ../vendor/librw/src/ps2/ps2skin.o \ + +INCLUDE = \ +-I../src/liberty/animation \ +-I../src/liberty/audio \ +-I../src/liberty/buildings \ +-I../src/liberty/collision \ +-I../src/liberty/control \ +-I../src/liberty/core \ +-I../src/liberty/entities \ +-I../src/liberty/extras \ +-I../src/liberty/fakerw \ +-I../src/liberty/math \ +-I../src/liberty/modelinfo \ +-I../src/liberty/objects \ +-I../src/liberty/peds \ +-I../src/liberty/renderer \ +-I../src/liberty/rw \ +-I../src/liberty/save \ +-I../src/liberty/skel \ +-I../src/liberty/text \ +-I../src/liberty/vehicles \ +-I../src/liberty/weapons \ +-I../src/liberty/audio/eax \ +-I../src/liberty/audio/oal \ +-I../src/liberty/extras/shaders \ +-I../src/liberty/extras/shaders/obj \ +-I../src/liberty/skel/glfw \ +-I../src/liberty/skel/win \ +\ +-I../vendor/librw \ +\ +-I../vendor/miniLZO \ +\ +-I../src/common \ +\ +-Igit-version + +DEFINES = -DRW_DC -DLIBRW $(if $(WITH_LOGGING),-DWITH_LOGGING) $(if $(WITH_DCLOAD),-DDC_CHDIR=/pc) \ + $(if $(WITH_BEEPS),-DWITH_BEEPS) +FLAGS = -fpermissive -Wno-sign-compare -Wno-parentheses -Wno-maybe-uninitialized \ + -Wno-format -Wno-strict-aliasing -Wno-unused-variable \ + -Wno-unused-but-set-variable -Wno-write-strings \ + -Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion \ + -Wno-multichar -Wno-unused-value -Wno-char-subscripts -Wno-reorder \ + -Wno-unused-function -Wno-class-memaccess -fno-permissive + +CPPFLAGS += $(INCLUDE) $(DEFINES) $(FLAGS) +CFLAGS += -std=gnu17 $(CPPFLAGS) +CXXFLAGS += -std=gnu++20 $(CPPFLAGS) \ No newline at end of file diff --git a/liberty/cuts.mk b/liberty/cuts.mk new file mode 100644 index 00000000..178e17b1 --- /dev/null +++ b/liberty/cuts.mk @@ -0,0 +1,286 @@ +CUTS_IFP = \ + A1_ss0.ifp \ + A2_pp.ifp \ + A3_ss.ifp \ + A4_pdr.ifp \ + A5_k2ft.ifp \ + A6_BAIT.ifp \ + A7_ETG.ifp \ + A8_PS.ifp \ + A9_ASD.ifp \ + BET.ifp \ + C1_TEX.ifp \ + D1_STOG.ifp \ + D2_KK.ifp \ + D3_ADO.ifp \ + D4_GTA2.ifp \ + D4_GTA.ifp \ + D5_ES.ifp \ + D6_STS.ifp \ + D7_MLD.ifp \ + el_ph1.ifp \ + el_ph2.ifp \ + el_ph3.ifp \ + el_ph4.ifp \ + END.ifp \ + hd_ph1.ifp \ + hd_ph2.ifp \ + hd_ph3.ifp \ + hd_ph4.ifp \ + hd_ph5.ifp \ + J0_dm2.ifp \ + J1_lfl.ifp \ + J2_kcl.ifp \ + J3_vh.ifp \ + J4_ETH.ifp \ + J5_dst.ifp \ + J6_tbj.ifp \ + JB.ifp \ + k1_kbo.ifp \ + k2_gis.ifp \ + k3_ds.ifp \ + k4_shi2.ifp \ + k4_shi.ifp \ + k5_sd.ifp \ + l1_lg.ifp \ + l2_dsb.ifp \ + l3_dm.ifp \ + l4_pap.ifp \ + l5_tfb.ifp \ + mt_ph1.ifp \ + mt_ph2.ifp \ + mt_ph3.ifp \ + mt_ph4.ifp \ + R0_PDR2.ifp \ + R1_SW.ifp \ + R2_AP.ifp \ + R3_ED.ifp \ + R4_GF.ifp \ + R5_PB.ifp \ + R6_MM.ifp \ + S0_MAS.ifp \ + S1_PF.ifp \ + S2_CTG2.ifp \ + S2_CTG.ifp \ + S3_RTC.ifp \ + S4_BDBA.ifp \ + S4_BDBB.ifp \ + S4_BDBD.ifp \ + S5_LRQb.ifp \ + S5_LRQc.ifp \ + S5_LRQ.ifp \ + t1_tol.ifp \ + t2_tpu.ifp \ + t3_mas.ifp \ + t4_tat.ifp \ + t5_bf.ifp \ + yd_ph1.ifp \ + yd_ph2.ifp \ + yd_ph3.ifp \ + yd_ph4.ifp + +CUTS_MISC = \ + a1_ss0_asuka.anm \ + a1_ss0.dat \ + a1_ss0_maria.anm \ + a1_ss0_player.anm \ + a2_pp.dat \ + a2_pp_player.anm \ + a3_ss_asuka.anm \ + a3_ss.dat \ + a3_ss_kenji.anm \ + a3_ss_player.anm \ + a4_pdr_asuka.anm \ + a4_pdr.dat \ + a4_pdr_player.anm \ + a5_k2ft.dat \ + a5_k2ft_player.anm \ + a6_bait_asuka.anm \ + a6_bait.dat \ + a6_bait_player.anm \ + a7_etg_asuka.anm \ + a7_etg.dat \ + a7_etg_player.anm \ + a8_ps_asuka.anm \ + A8_PS.dat \ + a8_ps_maria.anm \ + a8_ps_player.anm \ + a9_asd.dat \ + a9_asd_player.anm \ + bet_cat.anm \ + bet.dat \ + c1_tex_cat.anm \ + c1_tex.dat \ + d1_stog.dat \ + d1_stog_love.anm \ + d1_stog_player.anm \ + D2_KK.dat \ + d2_kk_love.anm \ + d2_kk_player.anm \ + d3_ado.dat \ + d3_ado_love.anm \ + d3_ado_player.anm \ + d4_gta2_asuka.anm \ + d4_gta2_cat.anm \ + d4_gta2.dat \ + d4_gta2_miguel.anm \ + D4_GTA.dat \ + d4_gta_love.anm \ + d4_gta_player.anm \ + d5_es.dat \ + d5_es_love.anm \ + d5_es_player.anm \ + d6_sts.dat \ + d6_sts_love.anm \ + d6_sts_player.anm \ + d7_mld.dat \ + el_ph1.dat \ + el_ph2.dat \ + el_ph3.dat \ + el_ph4.dat \ + end.dat \ + hd_ph1.dat \ + hd_ph2.dat \ + hd_ph3.dat \ + hd_ph4.dat \ + hd_ph5.dat \ + intro.dat \ + J0_dm2.dat \ + j0_dm2_joey.anm \ + j0_dm2_misty.anm \ + j0_dm2_player.anm \ + J1_lfl.dat \ + j1_lfl_joey.anm \ + j1_lfl_misty.anm \ + j1_lfl_player.anm \ + J2_KCL.dat \ + j2_kcl_joey.anm \ + j2_kcl_player.anm \ + j3_vh.dat \ + j3_vh_joey.anm \ + j3_vh_player.anm \ + J4_ETH.dat \ + j4_eth_joey.anm \ + j4_eth_player.anm \ + j4_eth_tony.anm \ + J5_dst.dat \ + j5_dst_joey.anm \ + j5_dst_player.anm \ + j6_tbj.dat \ + j6_tbj_joey.anm \ + j6_tbj_player.anm \ + jb_col1.anm \ + jb.dat \ + K1_KBO.dat \ + k1_kbo_kenji.anm \ + k1_kbo_player.anm \ + K2_GIS.dat \ + k2_gis_kenji.anm \ + k2_gis_player.anm \ + k3_ds.dat \ + k3_ds_kenji.anm \ + k3_ds_player.anm \ + k4_shi2.dat \ + k4_shi2_keeper.anm \ + k4_shi2_player.anm \ + k4_shi.dat \ + k4_shi_kenji.anm \ + k4_shi_player.anm \ + K5_SD.dat \ + k5_sd_kenji.anm \ + k5_sd_player.anm \ + L1_LG.dat \ + l1_lg_eight.anm \ + l1_lg_luigi.anm \ + l1_lg_micky.anm \ + l1_lg_player.anm \ + l2_dsb.dat \ + l2_DSB_micky.anm \ + l2_dsb_player.anm \ + L3_DM.dat \ + l3_dm_luigi.anm \ + l3_dm_micky.anm \ + l3_dm_player.anm \ + l4_pap.dat \ + l4_pap_luigi.anm \ + l4_pap_player.anm \ + L5_TFB.dat \ + l5_tfb_luigi.anm \ + l5_tfb_micky.anm \ + l5_tfb_player.anm \ + mt_ph1.dat \ + mt_ph2.dat \ + mt_ph3.dat \ + mt_ph4.dat \ + R0_PDR2.dat \ + R0_pdr2_player.anm \ + R0_pdr2_ray.anm \ + R1_SW.dat \ + R1_sw_player.anm \ + R1_sw_ray.anm \ + R2_AP.dat \ + R2_ap_player.anm \ + R2_ap_ray.anm \ + r3_ed.dat \ + R3_ed_player.anm \ + R3_ed_ray.anm \ + R4_GF.dat \ + R4_gf_player.anm \ + R4_gf_ray.anm \ + R5_PB.dat \ + R5_pb_player.anm \ + R5_pb_ray.anm \ + R6_MM.dat \ + R6_mm_player.anm \ + R6_mm_ray.anm \ + S0_MAS.dat \ + S0_mas_frank.anm \ + s0_mas_joey.anm \ + s0_mas_luigi.anm \ + s0_mas_player.anm \ + S0_mas_tony.anm \ + S1_PF.dat \ + S1_pf_frank.anm \ + S1_pf_maria.anm \ + S1_pf_player.anm \ + S2_ctg2_cat.anm \ + S2_ctg2_curly.anm \ + s2_ctg2.dat \ + S2_ctg2_miguel.anm \ + S2_CTG.dat \ + s2_ctg_frank.anm \ + S3_rtc.dat \ + S3_rtc_frank.anm \ + S4_BDBA.dat \ + S4_BDBA_eight.anm \ + S4_BDBA_player.anm \ + S4_BDBB.dat \ + S4_BDBB_eight.anm \ + S4_BDBB_player.anm \ + S4_BDBD.dat \ + S5_lrqb_asuka.anm \ + s5_lrqb.dat \ + S5_lrqb_maria.anm \ + S5_lrqc_asuka.anm \ + S5_LRQC.dat \ + S5_lrqc_maria.anm \ + S5_lrqc_player.anm \ + S5_LRQ.dat \ + S5_lrq_frank.anm \ + S5_lrq_player.anm \ + T1_TOL.dat \ + t1_tol_player.anm \ + t1_tol_tony.anm \ + T2_TPU.dat \ + t2_tpu_player.anm \ + T3_MAS.dat \ + t3_mas_player.anm \ + T4_TAT.dat \ + t4_tat_player.anm \ + T5_BF.dat \ + t5_bf_player.anm \ + t5_bf_tony.anm \ + yd_ph1.dat \ + yd_ph2.dat \ + yd_ph3.dat \ + yd_ph4.dat \ No newline at end of file diff --git a/dreamcast/gta3files.mk b/liberty/gta3files.mk similarity index 99% rename from dreamcast/gta3files.mk rename to liberty/gta3files.mk index f2aeaaf8..ee139b4f 100644 --- a/dreamcast/gta3files.mk +++ b/liberty/gta3files.mk @@ -1,3 +1,5 @@ +# models/coll/peds.col # not actually used + MISC_FILES = \ anim/cuts.dir \ anim/cuts.img \ @@ -110,7 +112,6 @@ MISC_FILES = \ models/Coll/commer.col \ models/Coll/generic.col \ models/Coll/indust.col \ - models/Coll/peds.col \ models/Coll/suburb.col \ models/Coll/vehicles.col \ models/Coll/weapons.col \ diff --git a/liberty/ip.txt b/liberty/ip.txt new file mode 100644 index 00000000..8860f97b --- /dev/null +++ b/liberty/ip.txt @@ -0,0 +1,9 @@ +Device Info : CD-ROM1/1 +Area Symbols : JUE +Peripherals : E000F10 +Product No : DCA-LBRT +Version : V2.000 +Release Date : 20250401 +Boot Filename : 1ST_READ.BIN +SW Maker Name : the gang +Game Title : dca-liberty diff --git a/dreamcast/modlist.mk b/liberty/modlist.mk similarity index 100% rename from dreamcast/modlist.mk rename to liberty/modlist.mk diff --git a/dreamcast/mp3list.mk b/liberty/mp3list.mk similarity index 100% rename from dreamcast/mp3list.mk rename to liberty/mp3list.mk diff --git a/dreamcast/sfxlist.mk b/liberty/sfxlist.mk similarity index 100% rename from dreamcast/sfxlist.mk rename to liberty/sfxlist.mk diff --git a/dreamcast/sfxlooplist.mk b/liberty/sfxlooplist.mk similarity index 91% rename from dreamcast/sfxlooplist.mk rename to liberty/sfxlooplist.mk index 2e579a85..21dd1225 100644 --- a/dreamcast/sfxlooplist.mk +++ b/liberty/sfxlooplist.mk @@ -1,4 +1,4 @@ -SFX_LOOP_WAV = \ +src/liberty/sfxlooplist.mkSFX_LOOP_WAV = \ sfx_0_loop.wav \ sfx_185_loop.wav \ sfx_19_loop.wav \ diff --git a/dreamcast/sim.mk b/liberty/sim.mk similarity index 57% rename from dreamcast/sim.mk rename to liberty/sim.mk index 9a2b78f4..75334842 100644 --- a/dreamcast/sim.mk +++ b/liberty/sim.mk @@ -1,14 +1,11 @@ -TARGET ?= dca3-sim.elf - -IS_MAC := $(shell uname -s | grep -i "darwin" > /dev/null && echo "yes" || echo "no") - +TARGET ?= dca-liberty-sim.elf all: $(TARGET) include common.mk OBJS = $(RE3_OBJS) $(RW_OBJS) \ - ../src/audio/sampman_null.o + ../src/liberty/audio/sampman_null.o OBJS_SIM=$(OBJS:.o=.sim.o) \ ../vendor/koshle/hlekos.sim.o \ @@ -37,31 +34,17 @@ DEPS_SIM=$(DEPS_SIM1:.o3=.d) CXXFLAGS+= -MMD -MP -ifeq ($(IS_MAC), yes) %.sim.o: %.c - $(CC) -c -O0 -g -fno-pic -no-pie -o $@ $(CFLAGS) -I../vendor/koshle -I../vendor/emu -U_WIN32 -UWIN32 -UWINNT -Ui386 -DDC_SIM -D_FILE_OFFSET_BITS=64 -DMACOS64 $< + $(CC) -c -O0 -g -fno-pic -no-pie -o $@ $(CFLAGS) -I../vendor/koshle -I../vendor/emu -U_WIN32 -UWIN32 -UWINNT -Ui386 -DDC_SIM -DMACOS64 $< %.sim.o: %.cpp - $(CXX) -c -O0 -g -fno-pic -no-pie -o $@ $(CXXFLAGS) -I../vendor/koshle -I../vendor/emu -U_WIN32 -UWIN32 -UWINNT -Ui386 -DDC_SIM -D_FILE_OFFSET_BITS=64 -DMACOS64 $< + $(CXX) -c -O0 -g -fno-pic -no-pie -o $@ $(CXXFLAGS) -I../vendor/koshle -I../vendor/emu -U_WIN32 -UWIN32 -UWINNT -Ui386 -DDC_SIM -DMACOS64 $< %.sim.o3: %.cpp - $(CXX) -c -O3 -g -fno-pic -no-pie -o $@ $(CXXFLAGS) -I../vendor/koshle -I../vendor/emu -U_WIN32 -UWIN32 -UWINNT -Ui386 -DDC_SIM -D_FILE_OFFSET_BITS=64 -DMACOS64 $< -else -# Using sse2 here for valgrind compatibility -%.sim.o: %.c - $(CC) -msse2 -mfpmath=sse -c -O0 -g -fno-pic -no-pie -o $@ $(CFLAGS) -I../vendor/koshle -I../vendor/emu -m32 -U_WIN32 -UWIN32 -UWINNT -Ui386 -DDC_SIM -D_FILE_OFFSET_BITS=64 $< -%.sim.o: %.cpp - $(CXX) -msse2 -mfpmath=sse -c -O0 -g -fno-pic -no-pie -o $@ $(CXXFLAGS) -I../vendor/koshle -I../vendor/emu -m32 -U_WIN32 -UWIN32 -UWINNT -Ui386 -DDC_SIM -D_FILE_OFFSET_BITS=64 $< -%.sim.o3: %.cpp - $(CXX) -msse2 -mfpmath=sse -c -O3 -g -fno-pic -no-pie -o $@ $(CXXFLAGS) -I../vendor/koshle -I../vendor/emu -m32 -U_WIN32 -UWIN32 -UWINNT -Ui386 -DDC_SIM -D_FILE_OFFSET_BITS=64 $< -endif + $(CXX) -c -O3 -g -fno-pic -no-pie -o $@ $(CXXFLAGS) -I../vendor/koshle -I../vendor/emu -U_WIN32 -UWIN32 -UWINNT -Ui386 -DDC_SIM -DMACOS64 $< clean: -rm -f $(OBJS_SIM) $(TARGET) -ifeq ($(IS_MAC), yes) $(TARGET): $(OBJS_SIM) $(CXX) -fno-pic -no-pie -o $(TARGET) $(OBJS_SIM) -lX11 -else -$(TARGET): $(OBJS_SIM) - $(CXX) -m32 -fno-pic -no-pie -o $(TARGET) $(OBJS_SIM) -lX11 -endif + -include $(DEPS_SIM) \ No newline at end of file diff --git a/dreamcast/texlist.mk b/liberty/texlist.mk similarity index 100% rename from dreamcast/texlist.mk rename to liberty/texlist.mk diff --git a/dreamcast/wavlist.mk b/liberty/wavlist.mk similarity index 100% rename from dreamcast/wavlist.mk rename to liberty/wavlist.mk diff --git a/miami/GTAVCSF8.b b/miami/GTAVCSF8.b new file mode 100644 index 00000000..13e45eed Binary files /dev/null and b/miami/GTAVCSF8.b differ diff --git a/miami/Makefile b/miami/Makefile new file mode 100644 index 00000000..584a2543 --- /dev/null +++ b/miami/Makefile @@ -0,0 +1,668 @@ +# +# Basic KallistiOS skeleton / test program +# Copyright (C)2001-2004 Megan Potter +# + +# Put the filename of the output binary here +PROJECT_NAME = dca-miami +TEAM_NAME ="the gang" +DISC_SERIAL = DCA-MIAM +RELEASE_DATE = 20250401 +TARGET ?= dca-miami.elf + +HAVE_CDI4DC := $(shell which cdi4dc > /dev/null 2>&1 && echo "yes" || echo "no") +IS_MAC := $(shell uname -s | grep -i "darwin" > /dev/null && echo "yes" || echo "no") + +MOD_NAME?= + +GTA_DIR?=../../miami +GTA_GAMEFILES_LOOSE_DIR?=../gamefiles/miami +GTA_MOD_DIR?=../../miami_mod$(MOD_NAME) +GTA_MOD_IMG_DIR?=$(GTA_MOD_DIR)/img +GTA_MOD_CUTS_DIR=$(GTA_MOD_DIR)/cuts +GTA_MOD_SFX_DIR?=$(GTA_MOD_DIR)/sfx +GTA_MOD_LOOSE_DIR?=$(GTA_MOD_DIR)/loose + +REPACK_DIR?=repack-data +REPACK_GTA_DIR?=$(REPACK_DIR)/miami +REPACK_IMG_ORIG_DIR?=$(REPACK_DIR)/miami-img-orig +REPACK_IMG_DC_DIR?=$(REPACK_DIR)/miami-img-dc +REPACK_SFX_ORIG_DIR?=$(REPACK_DIR)/miami-sfx-orig +REPACK_SFX_DC_DIR?=$(REPACK_DIR)/miami-sfx-dc +REPACK_STREAM_DECODED_DIR?=$(REPACK_DIR)/miami-stream-decoded +REPACK_STREAM_DEST_DIR=$(REPACK_GTA_DIR)/stream +REPACK_CUTS_ORIG_DIR?=$(REPACK_DIR)/miami-cuts-orig +REPACK_CUTS_DC_DIR?=$(REPACK_DIR)/miami-cuts-dc + +LIBS := +TEXCONV_FLAGS := + +ifeq ($(IS_MAC), yes) +TEXCONV_FLAGS += -DMACOS64 +endif + +AUDIO_STREAM_OPTION=-t +MKDCDISC_PAD_OPTION=-N +ifeq ($(FOR_DISC),1) +AUDIO_STREAM_OPTION=-q +MKDCDISC_PAD_OPTION= +else ifeq ($(FOR_DISC),2) +AUDIO_STREAM_OPTION=-m +MKDCDISC_PAD_OPTION= +endif + +all: $(TARGET) + +include common.mk + +OBJS = $(RE3_OBJS) $(RW_OBJS) \ + ../src/miami/audio/sampman_dc.o \ + ../src/common/prof/profiler.o + +OBJS_TEXCONV = $(RW_OBJS:.o=.texconv.o) +OBJS_TEXCONV += \ + ../vendor/koshle/hlekos.texconv.o \ + ../vendor/koshle/hlepvr_mem.texconv.o \ + ../vendor/koshle/hlepvr_prim.texconv.o \ + ../vendor/koshle/hlepvr_scene.texconv.o \ + ../vendor/koshle/hlepvr_misc.texconv.o \ + ../vendor/koshle/hlepvr_init_term.texconv.o \ + ../vendor/koshle/hlepvr_buffers.texconv.o \ + ../vendor/koshle/hlepvr_irq.texconv.o \ + ../vendor/koshle/hlematrix3d.texconv.o \ + ../vendor/librw/src/dc/vq.texconv.o \ + ../src/miami/fakerw/fake.texconv.o \ + ../src/miami/skel/crossplatform.texconv.o \ + ../src/miami/rw/TxdStore.texconv.o \ + ../src/miami/rw/RwHelper.texconv.o \ + ../src/tools/texconv.texconv.o \ + ../vendor/TriStripper/src/connectivity_graph.texconv.o \ + ../vendor/TriStripper/src/policy.texconv.o \ + ../vendor/TriStripper/src/tri_stripper.texconv.o \ + ../src/miami/rw/VisibilityPlugins.texconv.o \ + ../src/miami/rw/NodeName.texconv.o \ + ../src/miami/animation/RpAnimBlend.texconv.o \ + ../src/miami/animation/Bones.texconv.o \ + ../src/miami/animation/AnimBlendAssociation.texconv.o \ + ../src/miami/animation/AnimBlendNode.texconv.o \ + ../src/miami/animation/AnimBlendClumpData.texconv.o \ + ../src/miami/rw/MemoryMgr.texconv.o \ + ../src/miami/math/Quaternion.texconv.o \ + ../vendor/librw/src/ps2-x/ps2.texconv.o \ + ../vendor/librw/src/ps2-x/ps2device.texconv.o \ + ../vendor/librw/src/ps2-x/ps2raster.texconv.o \ + ../vendor/librw/src/ps2-x/ps2skin.texconv.o \ + ../vendor/librw/src/d3d-x/d3d.texconv.o \ + ../vendor/librw/src/d3d-x/d3d8.texconv.o \ + ../vendor/librw/src/d3d-x/d3d8render.texconv.o \ + ../vendor/librw/src/bmp.texconv.o \ + ../vendor/librw/src/png.texconv.o \ + ../vendor/librw/src/lodepng/lodepng.texconv.o + +# Add compilation units to this list to explicity compile them with +# -O3 optimizations, while the rest get the default (-Os) treatment +# to conserve RAM. +OBJS_O3 = \ + ../vendor/librw/src/dc/rwdc.o \ + ../src/miami/core/World.o \ + ../src/miami/collision/Collision.o \ + ../src/miami/math/math.o \ + ../src/miami/math/Matrix.o \ + ../src/miami/math/Quaternion.o \ + ../src/miami/math/Rect.o \ + ../src/miami/math/Vector.o \ + ../src/vendor/librw/src/base.o \ + ../src/miami/renderer/Shadows.o + +OBJS_NO_FAST_MATH = \ + ../src/miami/core/Cam.o \ + ../src/miami/core/Camera.o \ + ../src/miami/vehicles/Bike.o \ + ../src/miami/renderer/Particle.o + +KOS_CPPFLAGS += -fbuiltin -ffast-math -ffp-contract=fast \ + -mfsrra -mfsca + +ifdef KOS_BASE +include $(KOS_BASE)/Makefile.rules +else +$(warning "KOS_BASE is not set. Only prebuilt targets will work.") +endif + +DEPS = $(OBJS:.o=.d) $(OBJS_TEXCONV:.o:.d) + +CXXFLAGS += $(if $(WITH_32MB),-O3,-Os) \ + $(if $(WITH_IDE),-DWITH_IDE) \ + $(if $(WITH_PROF),-DWITH_PROF=\"$(WITH_PROF)\") \ + -MMD -MP -ffunction-sections -fdata-sections -ffast-math \ + -fmerge-all-constants -fomit-frame-pointer -ml -std=gnu++20 \ + -fno-exceptions -fno-rtti -fipa-pta -Wno-write-strings \ + -Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion \ + -Wno-multichar -Wno-unused-value -Wno-char-subscripts -Wno-reorder \ + -Wno-unused-function -Wno-class-memaccess -fno-permissive \ + -fno-asynchronous-unwind-tables -fno-enforce-eh-specs -fno-non-call-exceptions \ + -fno-strict-aliasing -fwrapv -Wno-unknown-pragmas + +clean-texconv: + -rm -f $(OBJS_TEXCONV) + -rm -f texconv + +clean-pvrtex: + $(MAKE) -C ../vendor/pvrtex clean + -rm -f ../vendor/pvrtex/pvrtex + +clean-objs: + -rm -f $(OBJS) + +clean: + -rm -f $(OBJS) + -rm -f $(OBJS_TEXCONV) + -rm -f $(TARGET) + -rm -f $(TARGET).bin + -rm -f 1ST_READ.BIN + -rm -f IP.BIN + -rm -f $(PROJECT_NAME).iso + -rm -f $(PROJECT_NAME).ds.iso + -rm -f $(PROJECT_NAME).cdi + -rm -f $(DEPS) + -rm -rf $(REPACK_DIR) + -rm -rf analyze-profile + +$(OBJS_O3): %.o: %.cpp + kos-c++ $(CXXFLAGS) $(CPPFLAGS) -O3 -c $< -o $@ + +$(OBJS_NO_FAST_MATH): %.o: %.cpp + kos-c++ $(CXXFLAGS) $(CPPFLAGS) -O3 -c $< -o $@ -fno-fast-math + +$(TARGET): $(OBJS) + kos-c++ -o $(TARGET) $(OBJS) -Wl,--gc-sections -Wl,--as-needed -Wl,-Map,output.map \ + $(if $(WITH_IDE),-lkosfat) $(if $(WITH_SD),-lkosfat) -Wl,--build-id=sha1 + @echo && echo && echo "*** Build Completed Successfully ($(TARGET)) ***" && echo && echo + +run: $(TARGET) + $(KOS_LOADER) $(TARGET) + +$(REPACK_GTA_DIR)/GTAVCSF8.b: GTAVCSF8.b + mkdir -p $(@D) + cp $< $@ + +1ST_READ.BIN: $(TARGET) + rm -f $(TARGET).bin + rm -f 1ST_READ.BIN + kos-objcopy -R .stack -O binary $(TARGET) $(TARGET).bin + $(KOS_BASE)/utils/scramble/scramble $(TARGET).bin 1ST_READ.BIN + +.PHONY: pvrtex + +pvrtex: + $(MAKE) -C ../vendor/pvrtex + +IP.BIN: + rm -f IP.BIN + $(KOS_BASE)/utils/makeip/makeip ip.txt IP.BIN + +$(PROJECT_NAME).iso: IP.BIN 1ST_READ.BIN $(REPACK_DIR)/repacked $(REPACK_GTA_DIR)/GTAVCSF8.b + rm -f $(PROJECT_NAME).iso + rm -f $(REPACK_GTA_DIR)/1ST_READ.BIN + cp 1ST_READ.BIN $(REPACK_GTA_DIR) + mkisofs -C 0,11702 -V $(PROJECT_NAME) -G IP.BIN -r -J -l -o $(PROJECT_NAME).iso $(REPACK_GTA_DIR) + +$(PROJECT_NAME)-no-repack.iso: IP.BIN 1ST_READ.BIN $(REPACK_GTA_DIR)/GTAVCSF8.b + rm -f $(PROJECT_NAME)-no-repack.iso + rm -f $(REPACK_GTA_DIR)/1ST_READ.BIN + cp 1ST_READ.BIN $(REPACK_GTA_DIR) + mkisofs -C 0,11702 -V $(PROJECT_NAME) -G IP.BIN -r -J -l -o $(PROJECT_NAME)-no-repack.iso $(REPACK_GTA_DIR) + + +$(PROJECT_NAME).ds.iso: IP.BIN 1ST_READ.BIN $(REPACK_DIR)/repacked $(REPACK_GTA_DIR)/GTAVCSF8.b + rm -f $(PROJECT_NAME).ds.iso + rm -f $(REPACK_GTA_DIR)/1ST_READ.BIN + cp $(TARGET).bin $(REPACK_GTA_DIR)/1ST_READ.BIN + mkisofs -V $(PROJECT_NAME) -G IP.BIN -r -J -l -o $(PROJECT_NAME).ds.iso $(REPACK_GTA_DIR) + +1ST_READ_PREBUILT.BIN: + kos-objcopy -R .stack -O binary $(TARGET) $(TARGET)-prebuilt.bin + $(KOS_BASE)/utils/scramble/scramble $(TARGET)-prebuilt.bin 1ST_READ_PREBUILT.BIN + mkdir -p $(REPACK_GTA_DIR) + +$(PROJECT_NAME)-prebuilt.iso: IP.BIN 1ST_READ_PREBUILT.BIN $(REPACK_DIR)/repacked $(REPACK_GTA_DIR)/GTAVCSF8.b + rm -f $(REPACK_GTA_DIR)/1ST_READ.BIN + cp 1ST_READ_PREBUILT.BIN $(REPACK_GTA_DIR)/1ST_READ.BIN + mkisofs -C 0,11702 -V $(PROJECT_NAME) -G IP.BIN -r -J -l -o $(PROJECT_NAME).iso $(REPACK_GTA_DIR) + +ifeq ($(HAVE_CDI4DC), yes) +$(PROJECT_NAME).cdi: $(PROJECT_NAME).iso + cdi4dc $(PROJECT_NAME).iso $(PROJECT_NAME).cdi > cdi.log + @echo && echo && echo "*** CDI Baked Successfully ($@) ***" && echo && echo + +$(PROJECT_NAME)-no-repack.cdi: $(PROJECT_NAME)-no-repack.iso + cdi4dc $(PROJECT_NAME)-no-repack.iso $(PROJECT_NAME)-no-repack.cdi > cdi.log + @echo && echo && echo "*** CDI Baked Successfully ($@) ***" && echo && echo + +$(PROJECT_NAME)-prebuilt.cdi: $(PROJECT_NAME)-prebuilt.iso + cdi4dc $(PROJECT_NAME).iso $(PROJECT_NAME).cdi > cdi.log + rm 1ST_READ_PREBUILT.BIN + @echo && echo && echo "*** CDI Baked Successfully ($@) ***" && echo && echo +else +$(PROJECT_NAME).cdi: $(TARGET) $(REPACK_DIR)/repacked $(REPACK_GTA_DIR)/GTAVCSF8.b + mkdcdisc -e $(TARGET) -o $(PROJECT_NAME).cdi -d $(REPACK_GTA_DIR)/ $(MKDCDISC_PAD_OPTION) -n $(PROJECT_NAME) -a $(TEAM_NAME) -s $(DISC_SERIAL) -r $(RELEASE_DATE) + @echo && echo && echo "*** CDI Baked Successfully ($@) ***" && echo && echo + +$(PROJECT_NAME)-no-repack.cdi: $(TARGET) $(REPACK_GTA_DIR)/GTAVCSF8.b + mkdcdisc -e $(TARGET) -o $(PROJECT_NAME)-no-repack.cdi -d $(REPACK_GTA_DIR)/ $(MKDCDISC_PAD_OPTION) -n $(PROJECT_NAME) -a $(TEAM_NAME) -s $(DISC_SERIAL) -r $(RELEASE_DATE) + @echo && echo && echo "*** CDI Baked Successfully ($@) ***" && echo && echo + +$(PROJECT_NAME)-prebuilt.cdi: $(REPACK_DIR)/repacked $(REPACK_GTA_DIR)/GTAVCSF8.b + mkdcdisc -e $(TARGET) -o $(PROJECT_NAME).cdi -d $(REPACK_GTA_DIR)/ $(MKDCDISC_PAD_OPTION) -n $(PROJECT_NAME) -a $(TEAM_NAME) -s $(DISC_SERIAL) -r $(RELEASE_DATE) + @echo && echo && echo "*** CDI Baked Successfully ($@) ***" && echo && echo +endif + +cdi: $(PROJECT_NAME).cdi + +cdi-no-repack: $(PROJECT_NAME)-no-repack.cdi + +dsiso: $(PROJECT_NAME).ds.iso + +cdi-prebuilt: $(PROJECT_NAME)-prebuilt.cdi + +sim: $(REPACK_DIR)/repacked + $(MAKE) -f sim.mk + +run-flycast: + flycast ./$(PROJECT_NAME).cdi + +repack-dc: $(REPACK_DIR)/repacked + +gprof: + @sh-elf-gprof $(TARGET) $(REPACK_DIR)/kernel_gmon_1.out > gprof.out + @cat gprof.out | gprof2dot | dot -Tpng -o $(TARGET)-kernel.png + @-rm -rf gprof.out + @echo "\033[42m Profiling data saved to $(TARGET)-kernel.png \033[0m" + +# tools +imgtool: ../src/tools/imgtool.cpp + $(CXX) -std=c++17 -o $@ -Og $< + +extract-sfx: ../src/tools/extract-sfx.cpp + $(CXX) -std=c++17 -o $@ -Og $< + +pack-sfx: ../src/tools/pack-sfx.cpp + $(CXX) -std=c++17 -o $@ -Og $< + +adf2mp3: ../src/tools/adf2mp3.cpp + $(CXX) -o $@ -O3 -g $< + +aud2adpcm: ../src/tools/aud2adpcm.c + $(CC) -o $@ -O3 -g $< -I../vendor/minimp3 + +texconv: $(OBJS_TEXCONV) | pvrtex # You'll have to rebuild pvrtex manually if you change it + $(CXX) -o $@ $(OBJS_TEXCONV) + +%.texconv.o: %.cpp + $(CXX) -std=c++2a -c -O0 -g -MMD -MP -o $@ -I../vendor/koshle -I../vendor/librw/src $(INCLUDE) -I../vendor/emu -I../vendor/crypto -I../vendor/TriStripper/include $(DEFINES) -DDC_TEXCONV -DDC_SIM $(TEXCONV_FLAGS) $< + +animtool: ../src/tools/animtool.cpp + $(CXX) -std=c++17 -o $@ -g -O0 $< + +coltool: ../src/tools/coltool.cpp + $(CXX) -std=c++17 -o $@ -g -O0 $< + +streamheaderpack: ../src/tools/streamheaderpack.cpp + $(CXX) -std=c++17 -o $@ -g -O0 $< + +-include $(DEPS) + +#### Repacking #### + +TXD_OPTS_fonts = 256 256 +TXD_OPTS_fronten1 = 512 512 +TXD_OPTS_fronten2 = 512 512 +# TXD_OPTS_generic = 512 512 +# TXD_OPTS_wheels = 512 512 +TXD_OPTS_hud = 128 128 +TXD_OPTS_INTRO = 512 512 +# TXD_OPTS_MISC = 512 512 +# TXD_OPTS_particle = 512 512 +TXD_OPTS_intro1 = 512 512 +TXD_OPTS_intro2 = 512 512 +TXD_OPTS_INTRO3 = 512 512 +TXD_OPTS_intro4 = 512 512 +TXD_OPTS_LOADSC0 = 512 512 +TXD_OPTS_LOADSC1 = 512 512 +TXD_OPTS_LOADSC10 = 512 512 +TXD_OPTS_LOADSC11 = 512 512 +TXD_OPTS_LOADSC12 = 512 512 +TXD_OPTS_LOADSC13 = 512 512 +TXD_OPTS_LOADSC2 = 512 512 +TXD_OPTS_LOADSC3 = 512 512 +TXD_OPTS_LOADSC4 = 512 512 +TXD_OPTS_LOADSC5 = 512 512 +TXD_OPTS_LOADSC6 = 512 512 +TXD_OPTS_LOADSC7 = 512 512 +TXD_OPTS_LOADSC8 = 512 512 +TXD_OPTS_LOADSC9 = 512 512 +TXD_OPTS_NEWS = 128 128 +TXD_OPTS_outro = 512 512 +TXD_OPTS_SPLASH1 = 512 512 +TXD_OPTS_SPLASH2 = 512 512 +TXD_OPTS_SPLASH3 = 512 512 + +DEFAULT_RES = 512 + +PVR_ENCODER ?= PVRTEX +TEXTURE_DOWNSAMPLE_TXD ?= NONE +TEXTURE_DOWNSAMPLE_IMG ?= HALF + +-include texlist.mk +-include modlist.mk +-include imgmisc.mk +-include gta3files.mk +-include sfxlist.mk +-include sfxlooplist.mk +-include wavlist.mk +-include mp3list.mk +-include cuts.mk + +IMG_TEXTURES_DC = $(addprefix $(REPACK_IMG_DC_DIR)/, $(IMG_TEXTURES)) +IMG_MODELS_DC = $(addprefix $(REPACK_IMG_DC_DIR)/, $(IMG_MODELS)) +IMG_IFP_DC = $(addprefix $(REPACK_IMG_DC_DIR)/, $(IMG_IFP)) +IMG_COL_DC = $(addprefix $(REPACK_IMG_DC_DIR)/, $(IMG_COL)) + +CUTS_IFP_DC = $(addprefix $(REPACK_CUTS_DC_DIR)/, $(CUTS_IFP)) +CUTS_MISC_DC = $(addprefix $(REPACK_CUTS_DC_DIR)/, $(CUTS_MISC)) + +LOOSE_FILES_DC = $(addprefix $(REPACK_GTA_DIR)/, $(MISC_FILES)) + +SFX_DC_DIR = $(REPACK_GTA_DIR)/sfx +SFX_DC_RAW = $(SFX_DC_DIR)/sfx_all.raw +SFX_DC_DSC = $(SFX_DC_DIR)/sfx_all.dsc +STREAM_ADPCM_DC = $(addprefix $(REPACK_STREAM_DEST_DIR)/, $(STREAM_WAV:.wav=.APM)) \ + $(addprefix $(REPACK_STREAM_DEST_DIR)/, $(STREAM_MP3:.mp3=.APM)) \ + $(addprefix $(REPACK_STREAM_DEST_DIR)/, $(STREAM_ADF:.adf=.APM)) + +IMG_TEXTURES_ORIG = $(addprefix $(REPACK_IMG_ORIG_DIR)/, $(IMG_TEXTURES)) +IMG_MODELS_ORIG = $(addprefix $(REPACK_IMG_ORIG_DIR)/, $(IMG_MODELS)) +IMG_IFP_ORIG = $(addprefix $(REPACK_IMG_ORIG_DIR)/, $(IMG_IFP)) +IMG_COL_ORIG = $(addprefix $(REPACK_IMG_ORIG_DIR)/, $(IMG_COL)) + +CUTS_IFP_ORIG = $(addprefix $(REPACK_CUTS_ORIG_DIR)/, $(CUTS_IFP)) +CUTS_MISC_ORIG = $(addprefix $(REPACK_CUTS_ORIG_DIR)/, $(CUTS_MISC)) + +SFX_ORIG = $(addprefix $(REPACK_SFX_ORIG_DIR)/, $(SFX_WAV)) +SFX_ORIG_LOOP = $(addprefix $(REPACK_SFX_ORIG_DIR)/, $(SFX_LOOP_WAV)) +SFX_REPACK_DC_WAV = $(addprefix $(REPACK_SFX_DC_DIR)/, $(SFX_WAV) $(SFX_LOOP_WAV)) +SFX_REPACK_DC = $(SFX_REPACK_DC_WAV:.wav=.pcm) +STREAM_ADF_DECODED = $(addprefix $(REPACK_STREAM_DECODED_DIR)/, $(STREAM_ADF:.adf=.mp3)) + +.PRECIOUS: $(SFX_ORIG) $(SFX_REPACK_DC) $(STREAM_ADF_DECODED) + +$(REPACK_DIR)/repacked: $(REPACK_GTA_DIR)/models/gta3.img $(REPACK_GTA_DIR)/models/gta3.dir $(REPACK_GTA_DIR)/anim/cuts.img $(REPACK_GTA_DIR)/anim/cuts.dir $(LOOSE_FILES_DC) $(STREAM_ADPCM_DC) $(SFX_DC_RAW) $(SFX_DC_DSC) streamheaderpack + mkdir -p $(@D) +# $(REPACK_GTA_DIR) needed as first argument as paths in the game prefix with stream/ + ./streamheaderpack miami $(REPACK_GTA_DIR) $(REPACK_STREAM_DEST_DIR)/hdr.bin + @git archive --format zip --output "$(REPACK_GTA_DIR)/DCA3-$(GIT_VERSION).zip" HEAD + @touch $@ + @echo && echo && echo "*** Repack Completed Successfully ($(PROJECT_NAME)) ***" && echo && echo + +$(REPACK_DIR)/unpacked: imgtool $(GTA_DIR)/models/gta3.img $(GTA_DIR)/models/gta3.dir + mkdir -p $(@D) + ./imgtool unpack "$(GTA_DIR)/models/gta3" "$(REPACK_IMG_ORIG_DIR)" + @touch $@ + +$(IMG_TEXTURES_ORIG) $(IMG_MODELS_ORIG) $(IMG_IFP_ORIG) $(IMG_COL_ORIG): $(REPACK_DIR)/unpacked + @test -f $@ + @touch $@ + +$(REPACK_DIR)/unpacked-cuts: imgtool $(GTA_DIR)/anim/cuts.img $(GTA_DIR)/anim/cuts.dir + mkdir -p $(@D) + ./imgtool unpack "$(GTA_DIR)/anim/cuts" "$(REPACK_CUTS_ORIG_DIR)" + @touch $@ + +$(CUTS_IFP_ORIG) $(CUTS_MISC_ORIG): $(REPACK_DIR)/unpacked-cuts + @test -f $@ + @touch $@ + +# First try the mods img directory +$(REPACK_IMG_DC_DIR)/%.dff: $(GTA_MOD_IMG_DIR)/%.dff texconv + @mkdir -p $(@D) + ./texconv $< $@ +$(REPACK_IMG_DC_DIR)/%.DFF: $(GTA_MOD_IMG_DIR)/%.DFF texconv + @mkdir -p $(@D) + ./texconv $< $@ + +# if not, the extracted img directory +$(REPACK_IMG_DC_DIR)/%.dff: $(REPACK_IMG_ORIG_DIR)/%.dff texconv + @mkdir -p $(@D) + ./texconv $< $@ +$(REPACK_IMG_DC_DIR)/%.DFF: $(REPACK_IMG_ORIG_DIR)/%.DFF texconv + @mkdir -p $(@D) + ./texconv $< $@ + +# first try the mods img directory. NB, the textures are not resized here, unlike normal .img textures +$(REPACK_IMG_DC_DIR)/%.txd: $(GTA_MOD_IMG_DIR)/%.txd texconv + @mkdir -p $(@D) + ./texconv $< $@ 1024 1024 -e $(PVR_ENCODER) -d NONE +$(REPACK_IMG_DC_DIR)/%.TXD: $(GTA_MOD_IMG_DIR)/%.TXD texconv + @mkdir -p $(@D) + ./texconv $< $@ 1024 1024 -e $(PVR_ENCODER) -d NONE + +# if not, the extracted img directory +$(REPACK_IMG_DC_DIR)/%.txd: $(REPACK_IMG_ORIG_DIR)/%.txd texconv + @mkdir -p $(@D) + ./texconv $< $@ $(DEFAULT_RES) $(DEFAULT_RES) -e $(PVR_ENCODER) -d $(TEXTURE_DOWNSAMPLE_IMG) +$(REPACK_IMG_DC_DIR)/%.TXD: $(REPACK_IMG_ORIG_DIR)/%.TXD texconv + @mkdir -p $(@D) + ./texconv $< $@ $(DEFAULT_RES) $(DEFAULT_RES) -e $(PVR_ENCODER) -d $(TEXTURE_DOWNSAMPLE_IMG) + +# First try the mods img directory +$(REPACK_IMG_DC_DIR)/%.col: $(GTA_MOD_IMG_DIR)/%.col coltool + @mkdir -p $(@D) + ./coltool $< $@ +$(REPACK_IMG_DC_DIR)/%.ifp: $(GTA_MOD_IMG_DIR)/%.ifp animtool + @mkdir -p $(@D) + ./animtool $< $@ + +# if not, the extracted img directory +$(REPACK_IMG_DC_DIR)/%.col: $(REPACK_IMG_ORIG_DIR)/%.col coltool + @mkdir -p $(@D) + ./coltool $< $@ +$(REPACK_IMG_DC_DIR)/%.ifp: $(REPACK_IMG_ORIG_DIR)/%.ifp animtool + @mkdir -p $(@D) + ./animtool $< $@ + +### cuts +# First try the mods img directory +$(REPACK_CUTS_DC_DIR)/%.dat: $(GTA_MOD_CUTS_DIR)/%.dat + @mkdir -p $(@D) + cp $< $@ +$(REPACK_CUTS_DC_DIR)/%.ifp: $(GTA_MOD_CUTS_DIR)/%.ifp animtool + @mkdir -p $(@D) + ./animtool $< $@ + +# if not, the extracted img directory +$(REPACK_CUTS_DC_DIR)/%.dat: $(REPACK_CUTS_ORIG_DIR)/%.dat + @mkdir -p $(@D) + cp $< $@ +$(REPACK_CUTS_DC_DIR)/%.ifp: $(REPACK_CUTS_ORIG_DIR)/%.ifp animtool + @mkdir -p $(@D) + ./animtool $< $@ + + +# first try the mods loose directory +$(REPACK_GTA_DIR)/%.dff: $(GTA_MOD_LOOSE_DIR)/%.dff texconv + @mkdir -p $(@D) + ./texconv $< $@ +$(REPACK_GTA_DIR)/%.DFF: $(GTA_MOD_LOOSE_DIR)/%.DFF texconv + @mkdir -p $(@D) + ./texconv $< $@ + +#if not, the original files +$(REPACK_GTA_DIR)/%.dff: $(GTA_DIR)/%.dff texconv + @mkdir -p $(@D) + ./texconv $< $@ +$(REPACK_GTA_DIR)/%.DFF: $(GTA_DIR)/%.DFF texconv + @mkdir -p $(@D) + ./texconv $< $@ + +# first try the mods loose directory +# Note the mods loose directory is not resized, unlike the normal .txd textures +$(REPACK_GTA_DIR)/%.txd: $(GTA_MOD_LOOSE_DIR)/%.txd texconv + @mkdir -p $(@D) + ./texconv $< $@ 1024 1024 -e $(PVR_ENCODER) -d NONE +$(REPACK_GTA_DIR)/%.TXD: $(GTA_MOD_LOOSE_DIR)/%.TXD texconv + @mkdir -p $(@D) + ./texconv $< $@ 1024 1024 -e $(PVR_ENCODER) -d NONE + +# then the gamefiles directory +$(REPACK_GTA_DIR)/%.txd: $(GTA_GAMEFILES_LOOSE_DIR)/%.txd texconv + @mkdir -p $(@D) + ./texconv $< $@ $(TXD_OPTS_$(notdir $*)) -e $(PVR_ENCODER) -d $(TEXTURE_DOWNSAMPLE_TXD) +$(REPACK_GTA_DIR)/%.TXD: $(GTA_GAMEFILES_LOOSE_DIR)/%.TXD texconv + @mkdir -p $(@D) + ./texconv $< $@ $(TXD_OPTS_$(notdir $*)) -e $(PVR_ENCODER) -d $(TEXTURE_DOWNSAMPLE_TXD) + +# if not, the original files +$(REPACK_GTA_DIR)/%.txd: $(GTA_DIR)/%.txd texconv + @mkdir -p $(@D) + ./texconv $< $@ $(TXD_OPTS_$(notdir $*)) -e $(PVR_ENCODER) -d $(TEXTURE_DOWNSAMPLE_TXD) +$(REPACK_GTA_DIR)/%.TXD: $(GTA_DIR)/%.TXD texconv + @mkdir -p $(@D) + ./texconv $< $@ $(TXD_OPTS_$(notdir $*)) -e $(PVR_ENCODER) -d $(TEXTURE_DOWNSAMPLE_TXD) + +# first try the mods loose directory +$(REPACK_GTA_DIR)/%.ifp: $(GTA_MOD_LOOSE_DIR)/%.ifp animtool + @mkdir -p $(@D) + ./animtool $< $@ +$(REPACK_GTA_DIR)/%.IFP: $(GTA_MOD_LOOSE_DIR)/%.IFP animtool + @mkdir -p $(@D) + ./animtool $< $@ + +#if not, the original files +$(REPACK_GTA_DIR)/%.ifp: $(GTA_DIR)/%.ifp animtool + @mkdir -p $(@D) + ./animtool $< $@ +$(REPACK_GTA_DIR)/%.IFP: $(GTA_DIR)/%.IFP animtool + @mkdir -p $(@D) + ./animtool $< $@ + +# first try the mods loose directory +$(REPACK_GTA_DIR)/%.col: $(GTA_MOD_LOOSE_DIR)/%.col coltool + @mkdir -p $(@D) + ./coltool $< $@ +$(REPACK_GTA_DIR)/%.COL: $(GTA_MOD_LOOSE_DIR)/%.COL coltool + @mkdir -p $(@D) + ./coltool $< $@ + +#if not, the original files +$(REPACK_GTA_DIR)/%.col: $(GTA_DIR)/%.col coltool + @mkdir -p $(@D) + ./coltool $< $@ +$(REPACK_GTA_DIR)/%.COL: $(GTA_DIR)/%.COL coltool + @mkdir -p $(@D) + ./coltool $< $@ + +$(REPACK_DIR)/packed: $(IMG_TEXTURES_DC) $(IMG_MODELS_DC) $(IMG_IFP_DC) $(IMG_COL_DC) + mkdir -p $(@D) + mkdir -p "$(REPACK_GTA_DIR)/models/gta3" + ./imgtool pack "$(REPACK_GTA_DIR)/models/gta3" "$(REPACK_IMG_DC_DIR)" + @touch $@ + +$(REPACK_GTA_DIR)/models/gta3.img $(REPACK_GTA_DIR)/models/gta3.dir: $(REPACK_DIR)/packed + @touch $@ + +$(REPACK_DIR)/packed-cuts: $(CUTS_IFP_DC) $(CUTS_MISC_DC) + mkdir -p $(@D) + mkdir -p "$(REPACK_GTA_DIR)/anim/cuts" + ./imgtool pack "$(REPACK_GTA_DIR)/anim/cuts" "$(REPACK_CUTS_DC_DIR)" + @touch $@ + +$(REPACK_GTA_DIR)/anim/cuts.img $(REPACK_GTA_DIR)/anim/cuts.dir: $(REPACK_DIR)/packed-cuts + @touch $@ + +# sfx processing +$(REPACK_DIR)/unpacked-sfx: extract-sfx $(GTA_DIR)/Audio/sfx.SDT $(GTA_DIR)/Audio/sfx.RAW + mkdir -p $(@D) + mkdir -p "$(REPACK_SFX_ORIG_DIR)" + ./extract-sfx "$(GTA_DIR)/Audio/sfx.SDT" "$(GTA_DIR)/Audio/sfx.RAW" "$(REPACK_SFX_ORIG_DIR)" + @touch $@ + +$(SFX_ORIG): $(REPACK_DIR)/unpacked-sfx + @test -f $@ + @touch $@ + +$(SFX_ORIG_LOOP): $(SFX_ORIG) + @test -f $@ + @touch $@ + +# try first mods sfx directory +$(REPACK_SFX_DC_DIR)/%.pcm: $(GTA_MOD_SFX_DIR)/%.wav aud2adpcm + @mkdir -p $(@D) + ./aud2adpcm -raw $< $@ + +# then original (extracted) sfx directory +$(REPACK_SFX_DC_DIR)/%.pcm: $(REPACK_SFX_ORIG_DIR)/%.wav aud2adpcm + @mkdir -p $(@D) + ./aud2adpcm -raw $< $@ + +# stream processing + +# first try the mods loose directory +$(REPACK_STREAM_DEST_DIR)/%.APM: $(GTA_MOD_LOOSE_DIR)/Audio/%.wav aud2adpcm + @mkdir -p $(@D) + ./aud2adpcm $(AUDIO_STREAM_OPTION) $< $@ + +$(REPACK_STREAM_DECODED_DIR)/%.mp3: $(GTA_MOD_LOOSE_DIR)/Audio/%.adf adf2mp3 + @mkdir -p $(@D) + ./adf2mp3 $< $@ + +$(REPACK_STREAM_DEST_DIR)/%.APM: $(GTA_MOD_LOOSE_DIR)/Audio/%.mp3 aud2adpcm + @mkdir -p $(@D) + ./aud2adpcm $(AUDIO_STREAM_OPTION) $< $@ + +$(REPACK_STREAM_DEST_DIR)/%.APM: $(REPACK_STREAM_DECODED_DIR)/%.mp3 aud2adpcm + @mkdir -p $(@D) + ./aud2adpcm $(AUDIO_STREAM_OPTION) $< $@ + +# then original folder +$(REPACK_STREAM_DEST_DIR)/%.APM: $(GTA_DIR)/Audio/%.wav aud2adpcm + @mkdir -p $(@D) + ./aud2adpcm $(AUDIO_STREAM_OPTION) $< $@ + +$(REPACK_STREAM_DECODED_DIR)/%.mp3: $(GTA_DIR)/Audio/%.adf adf2mp3 + @mkdir -p $(@D) + ./adf2mp3 $< $@ + +$(REPACK_STREAM_DEST_DIR)/%.APM: $(GTA_DIR)/Audio/%.mp3 aud2adpcm + @mkdir -p $(@D) + ./aud2adpcm $(AUDIO_STREAM_OPTION) $< $@ + +$(REPACK_STREAM_DEST_DIR)/%.APM: $(REPACK_STREAM_DECODED_DIR)/%.mp3 aud2adpcm + @mkdir -p $(@D) + ./aud2adpcm $(AUDIO_STREAM_OPTION) $< $@ + +# Note: These are last so they have least priority, files should be processed if possible + +# first try mods dir +$(REPACK_GTA_DIR)/%: $(GTA_MOD_LOOSE_DIR)/% + @mkdir -p $(@D) + cp $< $@ + +# then gamefiles +$(REPACK_GTA_DIR)/%: $(GTA_GAMEFILES_LOOSE_DIR)/% + @mkdir -p $(@D) + cp $< $@ + +# then original files +$(REPACK_GTA_DIR)/%: $(GTA_DIR)/% + @mkdir -p $(@D) + cp $< $@ + +$(SFX_DC_RAW): pack-sfx $(SFX_REPACK_DC) + mkdir -p $(@D) + ./pack-sfx "$(GTA_DIR)/Audio/sfx.SDT" $(SFX_DC_RAW) $(SFX_DC_DSC) $(REPACK_SFX_DC_DIR) + + $(SFX_DC_DSC): $(SFX_DC_RAW) + ls -l $@ + +analyze-profile: analyze-profile.cpp + $(CXX) -std=c++17 -O3 analyze-profile.cpp -o analyze-profile diff --git a/miami/common.mk b/miami/common.mk new file mode 100644 index 00000000..552b958e --- /dev/null +++ b/miami/common.mk @@ -0,0 +1,416 @@ +GIT_VERSION := $(shell git describe --always --tags --long --dirty 2>/dev/null || echo "NO_GIT") +CI_JOB_ID ?= 00000000 + + +git-version/git-version.tmp: + @echo "Generating git-version.tmp with GIT_VERSION = \"$(GIT_VERSION)\"" + @mkdir -p git-version + @echo "#pragma once" > git-version/git-version.tmp + @echo "#ifndef VERSION_H" >> git-version/git-version.tmp + @echo "#define VERSION_H" >> git-version/git-version.tmp + @echo "#define GIT_VERSION \"$(GIT_VERSION)\"" >> git-version/git-version.tmp + @echo "#define CI_JOB_ID \"$(CI_JOB_ID)\"" >> git-version/git-version.tmp + @echo "#endif // VERSION_H" >> git-version/git-version.tmp + +git-version/git-version.h: git-version/git-version.tmp + @if [ ! -f git-version/git-version.h ] || ! cmp -s git-version/git-version.tmp git-version/git-version.h; then \ + echo "Updating git-version.h"; \ + cp git-version/git-version.tmp git-version/git-version.h; \ + else \ + echo "git-version.h is up to date. No change."; \ + fi + +.PHONY: git-version/git-version.tmp + +../src/miami/skel/dc/dc.cpp: git-version/git-version.h + + + +# List all of your C files here, but change the extension to ".o" +# Include "romdisk.o" if you want a rom disk. +RE3_OBJS = \ + ../src/miami/animation/AnimBlendAssocGroup.o \ + ../src/miami/animation/AnimBlendAssociation.o \ + ../src/miami/animation/AnimBlendClumpData.o \ + ../src/miami/animation/AnimBlendHierarchy.o \ + ../src/miami/animation/AnimBlendNode.o \ + ../src/miami/animation/AnimBlendSequence.o \ + ../src/miami/animation/AnimManager.o \ + ../src/miami/animation/Bones.o \ + ../src/miami/animation/CutsceneMgr.o \ + ../src/miami/animation/FrameUpdate.o \ + ../src/miami/animation/RpAnimBlend.o \ + \ + ../src/miami/buildings/Building.o \ + ../src/miami/buildings/Treadable.o \ + \ + ../src/miami/collision/ColBox.o \ + ../src/miami/collision/ColLine.o \ + ../src/miami/collision/Collision.o \ + ../src/miami/collision/ColModel.o \ + ../src/miami/collision/ColPoint.o \ + ../src/miami/collision/ColSphere.o \ + ../src/miami/collision/ColStore.o \ + ../src/miami/collision/ColTriangle.o \ + ../src/miami/collision/TempColModels.o \ + ../src/miami/collision/VuCollision.o \ + \ + ../src/miami/control/AutoPilot.o \ + ../src/miami/control/Bridge.o \ + ../src/miami/control/CarAI.o \ + ../src/miami/control/CarCtrl.o \ + ../src/miami/control/Curves.o \ + ../src/miami/control/Darkel.o \ + ../src/miami/control/GameLogic.o \ + ../src/miami/control/Garages.o \ + ../src/miami/control/NameGrid.o \ + ../src/miami/control/OnscreenTimer.o \ + ../src/miami/control/PathFind.o \ + ../src/miami/control/Phones.o \ + ../src/miami/control/Pickups.o \ + ../src/miami/control/PowerPoints.o \ + ../src/miami/control/Record.o \ + ../src/miami/control/Remote.o \ + ../src/miami/control/Replay.o \ + ../src/miami/control/Restart.o \ + ../src/miami/control/RoadBlocks.o \ + ../src/miami/control/SceneEdit.o \ + ../src/miami/control/Script.o \ + ../src/miami/control/Script2.o \ + ../src/miami/control/Script3.o \ + ../src/miami/control/Script4.o \ + ../src/miami/control/Script5.o \ + ../src/miami/control/Script6.o \ + ../src/miami/control/Script7.o \ + ../src/miami/control/Script8.o \ + ../src/miami/control/SetPieces.o \ + ../src/miami/control/ScriptDebug.o \ + ../src/miami/control/TrafficLights.o \ + \ + ../src/miami/core/Accident.o \ + ../src/miami/core/Cam.o \ + ../src/miami/core/Camera.o \ + ../src/miami/core/CdStreamDC.o \ + ../src/miami/core/Clock.o \ + ../src/miami/core/ControllerConfig.o \ + ../src/miami/core/Debug.o \ + ../src/miami/core/Directory.o \ + ../src/miami/core/EventList.o \ + ../src/miami/core/FileLoader.o \ + ../src/miami/core/FileMgr.o \ + ../src/miami/core/Fire.o \ + ../src/miami/core/Frontend.o \ + ../src/miami/core/FrontEndControls.o \ + ../src/miami/core/Frontend_PS2.o \ + ../src/miami/core/Game.o \ + ../src/miami/core/IniFile.o \ + ../src/miami/core/Lists.o \ + ../src/miami/core/main.o \ + ../src/miami/core/MenuScreens.o \ + ../src/miami/core/MenuScreensCustom.o \ + ../src/miami/core/obrstr.o \ + ../src/miami/core/Pad.o \ + ../src/miami/core/Placeable.o \ + ../src/miami/core/PlayerInfo.o \ + ../src/miami/core/Pools.o \ + ../src/miami/core/Profile.o \ + ../src/miami/core/Radar.o \ + ../src/miami/core/Range2D.o \ + ../src/miami/core/Range3D.o \ + ../src/miami/core/re3.o \ + ../src/miami/core/References.o \ + ../src/miami/core/Ropes.o \ + ../src/miami/core/Stats.o \ + ../src/miami/core/Streaming.o \ + ../src/miami/core/SurfaceTable.o \ + ../src/miami/core/timebars.o \ + ../src/miami/core/Timer.o \ + ../src/miami/core/TimeStep.o \ + ../src/miami/core/User.o \ + ../src/miami/core/Wanted.o \ + ../src/miami/core/World.o \ + ../src/miami/core/ZoneCull.o \ + ../src/miami/core/Zones.o \ + \ + ../src/miami/entities/Dummy.o \ + ../src/miami/entities/Entity.o \ + ../src/miami/entities/Physical.o \ + \ + ../src/miami/fakerw/fake.o \ + \ + ../src/miami/math/math.o \ + ../src/miami/math/Matrix.o \ + ../src/miami/math/Quaternion.o \ + ../src/miami/math/Rect.o \ + ../src/miami/math/Vector.o \ + \ + ../src/miami/modelinfo/BaseModelInfo.o \ + ../src/miami/modelinfo/ClumpModelInfo.o \ + ../src/miami/modelinfo/MloModelInfo.o \ + ../src/miami/modelinfo/ModelIndices.o \ + ../src/miami/modelinfo/ModelInfo.o \ + ../src/miami/modelinfo/PedModelInfo.o \ + ../src/miami/modelinfo/SimpleModelInfo.o \ + ../src/miami/modelinfo/TimeModelInfo.o \ + ../src/miami/modelinfo/VehicleModelInfo.o \ + ../src/miami/modelinfo/WeaponModelInfo.o \ + \ + ../src/miami/objects/CutsceneObject.o \ + ../src/miami/objects/DummyObject.o \ + ../src/miami/objects/Object.o \ + ../src/miami/objects/ObjectData.o \ + ../src/miami/objects/ParticleObject.o \ + ../src/miami/objects/Projectile.o \ + ../src/miami/objects/Stinger.o \ + \ + ../src/miami/peds/CivilianPed.o \ + ../src/miami/peds/CopPed.o \ + ../src/miami/peds/EmergencyPed.o \ + ../src/miami/peds/Gangs.o \ + ../src/miami/peds/Ped.o \ + ../src/miami/peds/PedAI.o \ + ../src/miami/peds/PedChat.o \ + ../src/miami/peds/PedDebug.o \ + ../src/miami/peds/PedFight.o \ + ../src/miami/peds/PedIK.o \ + ../src/miami/peds/PedAttractor.o \ + ../src/miami/peds/PedPlacement.o \ + ../src/miami/peds/PedRoutes.o \ + ../src/miami/peds/PedType.o \ + ../src/miami/peds/PlayerPed.o \ + ../src/miami/peds/Population.o \ + \ + ../src/miami/renderer/Antennas.o \ + ../src/miami/renderer/Clouds.o \ + ../src/miami/renderer/Console.o \ + ../src/miami/renderer/Coronas.o \ + ../src/miami/renderer/Credits.o \ + ../src/miami/renderer/CutsceneShadow.o \ + ../src/miami/renderer/Draw.o \ + ../src/miami/renderer/Fluff.o \ + ../src/miami/renderer/Font.o \ + ../src/miami/renderer/Glass.o \ + ../src/miami/renderer/Hud.o \ + ../src/miami/renderer/Instance.o \ + ../src/miami/renderer/Lines.o \ + ../src/miami/renderer/MBlur.o \ + ../src/miami/renderer/Occlusion.o \ + ../src/miami/renderer/Particle.o \ + ../src/miami/renderer/ParticleMgr.o \ + ../src/miami/renderer/PlayerSkin.o \ + ../src/miami/renderer/PointLights.o \ + ../src/miami/renderer/RenderBuffer.o \ + ../src/miami/renderer/Renderer.o \ + ../src/miami/renderer/Rubbish.o \ + ../src/miami/renderer/Shadows.o \ + ../src/miami/renderer/ShadowCamera.o \ + ../src/miami/renderer/Skidmarks.o \ + ../src/miami/renderer/SpecialFX.o \ + ../src/miami/renderer/Sprite.o \ + ../src/miami/renderer/Sprite2d.o \ + ../src/miami/renderer/TexList.o \ + ../src/miami/renderer/Timecycle.o \ + ../src/miami/renderer/VarConsole.o \ + ../src/miami/renderer/WaterCannon.o \ + ../src/miami/renderer/WaterCreatures.o \ + ../src/miami/renderer/WaterLevel.o \ + ../src/miami/renderer/Weather.o \ + ../src/miami/renderer/WindModifiers.o \ + \ + ../src/miami/rw/ClumpRead.o \ + ../src/miami/rw/Lights.o \ + ../src/miami/rw/MemoryHeap.o \ + ../src/miami/rw/MemoryMgr.o \ + ../src/miami/rw/NodeName.o \ + ../src/miami/rw/RwHelper.o \ + ../src/miami/rw/RwMatFX.o \ + ../src/miami/rw/RwPS2AlphaTest.o \ + ../src/miami/rw/TexRead.o \ + ../src/miami/rw/TexturePools.o \ + ../src/miami/rw/TxdStore.o \ + ../src/miami/rw/VisibilityPlugins.o \ + \ + ../src/miami/skel/crossplatform.o \ + ../src/miami/skel/events.o \ + ../src/miami/skel/skeleton.o \ + ../src/miami/skel/dc/dc.o \ + \ + ../src/miami/text/Messages.o \ + ../src/miami/text/Pager.o \ + ../src/miami/text/Text.o \ + \ + ../src/miami/vehicles/Automobile.o \ + ../src/miami/vehicles/Boat.o \ + ../src/miami/vehicles/Bike.o \ + ../src/miami/vehicles/CarGen.o \ + ../src/miami/vehicles/Cranes.o \ + ../src/miami/vehicles/DamageManager.o \ + ../src/miami/vehicles/Door.o \ + ../src/miami/vehicles/Floater.o \ + ../src/miami/vehicles/HandlingMgr.o \ + ../src/miami/vehicles/Heli.o \ + ../src/miami/vehicles/Plane.o \ + ../src/miami/vehicles/Train.o \ + ../src/miami/vehicles/Transmission.o \ + ../src/miami/vehicles/Vehicle.o \ + \ + ../src/miami/weapons/BulletInfo.o \ + ../src/miami/weapons/Explosion.o \ + ../src/miami/weapons/ProjectileInfo.o \ + ../src/miami/weapons/ShotInfo.o \ + ../src/miami/weapons/Weapon.o \ + ../src/miami/weapons/WeaponEffects.o \ + ../src/miami/weapons/WeaponInfo.o \ + \ + ../src/miami/audio/AudioCollision.o \ + ../src/miami/audio/AudioLogic.o \ + ../src/miami/audio/AudioManager.o \ + ../src/miami/audio/AudioScriptObject.o \ + ../src/miami/audio/DMAudio.o \ + ../src/miami/audio/MusicManager.o \ + ../src/miami/audio/PolRadio.o \ + ../src/miami/audio/sampman_miles.o \ + ../src/miami/audio/sampman_oal.o \ + \ + ../src/miami/save/Date.o \ + ../src/miami/save/GenericGameStorage.o \ + ../src/miami/save/MemoryCard.o \ + ../src/miami/save/PCSave.o \ + \ + ../src/miami/extras/debugmenu.o \ + ../src/miami/extras/frontendoption.o \ + ../src/miami/extras/postfx.o \ + ../src/miami/extras/screendroplets.o \ + \ + ../vendor/miniLZO/minilzo.o \ + \ + ../src/common/vmu/vmu.o + +# Excluded \ + ../src/miami/extras/custompipes.o \ + ../src/miami/extras/custompipes_d3d9.o \ + ../src/miami/extras/custompipes_gl.o \ + ../src/miami/core/CdStream.o \ + ../src/miami/core/CdStreamPosix.o \ + ../src/miami/extras \ + ../src/miami/extras/GitSHA1.cpp.in \ + ../src/miami/core/AnimViewer.o \ + +RW_OBJS = \ + ../vendor/librw/src/anim.o \ + ../vendor/librw/src/base.o \ + ../vendor/librw/src/camera.o \ + ../vendor/librw/src/charset.o \ + ../vendor/librw/src/clump.o \ + ../vendor/librw/src/engine.o \ + ../vendor/librw/src/error.o \ + ../vendor/librw/src/frame.o \ + ../vendor/librw/src/geometry.o \ + ../vendor/librw/src/geoplg.o \ + ../vendor/librw/src/hanim.o \ + ../vendor/librw/src/image.o \ + ../vendor/librw/src/light.o \ + ../vendor/librw/src/matfx.o \ + ../vendor/librw/src/pipeline.o \ + ../vendor/librw/src/plg.o \ + ../vendor/librw/src/prim.o \ + ../vendor/librw/src/raster.o \ + ../vendor/librw/src/render.o \ + ../vendor/librw/src/skin.o \ + ../vendor/librw/src/texture.o \ + ../vendor/librw/src/tristrip.o \ + ../vendor/librw/src/userdata.o \ + ../vendor/librw/src/uvanim.o \ + ../vendor/librw/src/world.o \ + \ + ../vendor/librw/src/dc/rwdc.o \ + ../vendor/librw/src/dc/alloc.o + +# Excluded \ + ../vendor/librw/src/d3d-x/d3d.o \ + ../vendor/librw/src/d3d-x/d3d8.o \ + ../vendor/librw/src/d3d-x/d3d8render.o \ + ../vendor/librw/src/d3d/d3d8.o \ + ../vendor/librw/src/d3d/d3d8matfx.o \ + ../vendor/librw/src/d3d/d3d8render.o \ + ../vendor/librw/src/d3d/d3d8skin.o \ + ../vendor/librw/src/d3d/d3d9.o \ + ../vendor/librw/src/d3d/d3d9matfx.o \ + ../vendor/librw/src/d3d/d3d9render.o \ + ../vendor/librw/src/d3d/d3d9skin.o \ + ../vendor/librw/src/d3d/d3d.o \ + ../vendor/librw/src/d3d/d3ddevice.o \ + ../vendor/librw/src/d3d/d3dimmed.o \ + ../vendor/librw/src/d3d/d3drender.o \ + ../vendor/librw/src/d3d/xbox.o \ + ../vendor/librw/src/d3d/xboxmatfx.o \ + ../vendor/librw/src/d3d/xboxskin.o \ + ../vendor/librw/src/d3d/xboxvfmt.o \ + \ + ../vendor/librw/src/gl/gl3.o \ + ../vendor/librw/src/gl/gl3device.o \ + ../vendor/librw/src/gl/gl3immed.o \ + ../vendor/librw/src/gl/gl3matfx.o \ + ../vendor/librw/src/gl/gl3pipe.o \ + ../vendor/librw/src/gl/gl3raster.o \ + ../vendor/librw/src/gl/gl3render.o \ + ../vendor/librw/src/gl/gl3shader.o \ + ../vendor/librw/src/gl/gl3skin.o \ + ../vendor/librw/src/gl/wdgl.o \ + ../vendor/librw/src/gl/glad/glad.cXXX \ + \ + ../vendor/librw/src/ps2/pds.o \ + ../vendor/librw/src/ps2/ps2.o \ + ../vendor/librw/src/ps2/ps2device.o \ + ../vendor/librw/src/ps2/ps2matfx.o \ + ../vendor/librw/src/ps2/ps2raster.o \ + ../vendor/librw/src/ps2/ps2skin.o \ + +INCLUDE = \ +-I../src/miami/animation \ +-I../src/miami/audio \ +-I../src/miami/buildings \ +-I../src/miami/collision \ +-I../src/miami/control \ +-I../src/miami/core \ +-I../src/miami/entities \ +-I../src/miami/extras \ +-I../src/miami/fakerw \ +-I../src/miami/math \ +-I../src/miami/modelinfo \ +-I../src/miami/objects \ +-I../src/miami/peds \ +-I../src/miami/renderer \ +-I../src/miami/rw \ +-I../src/miami/save \ +-I../src/miami/skel \ +-I../src/miami/text \ +-I../src/miami/vehicles \ +-I../src/miami/weapons \ +-I../src/miami/audio/eax \ +-I../src/miami/audio/oal \ +-I../src/miami/extras/shaders \ +-I../src/miami/extras/shaders/obj \ +-I../src/miami/skel/glfw \ +-I../src/miami/skel/win \ +\ +-I../vendor/librw \ +\ +-I../vendor/miniLZO \ +\ +-I../src/common \ +\ +-Igit-version + +DEFINES = -DRW_DC -DLIBRW $(if $(WITH_LOGGING),-DWITH_LOGGING) $(if $(WITH_DCLOAD),-DDC_CHDIR=/pc) \ + $(if $(WITH_BEEPS),-DWITH_BEEPS) +FLAGS = -fpermissive -Wno-sign-compare -Wno-parentheses -Wno-maybe-uninitialized \ + -Wno-format -Wno-strict-aliasing -Wno-unused-variable \ + -Wno-unused-but-set-variable -Wno-write-strings \ + -Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion \ + -Wno-multichar -Wno-unused-value -Wno-char-subscripts -Wno-reorder \ + -Wno-unused-function -Wno-class-memaccess -fno-permissive + +CPPFLAGS += $(INCLUDE) $(DEFINES) $(FLAGS) +CFLAGS += -std=gnu17 $(CPPFLAGS) +CXXFLAGS += -std=gnu++20 $(CPPFLAGS) \ No newline at end of file diff --git a/miami/cuts.mk b/miami/cuts.mk new file mode 100644 index 00000000..f668c9d1 --- /dev/null +++ b/miami/cuts.mk @@ -0,0 +1,151 @@ +CUTS_IFP = \ + ass_1.ifp \ + ass_2.ifp \ + bank_1.ifp \ + bank_2a.ifp \ + bank_2b.ifp \ + bank_3a.ifp \ + bank_3b.ifp \ + bank_4.ifp \ + bike_1.ifp \ + bike_2.ifp \ + bike_3.ifp \ + bud_1.ifp \ + bud_2.ifp \ + bud_3.ifp \ + cap_1.ifp \ + car_1.ifp \ + cnt_1a.ifp \ + cnt_1b.ifp \ + cnt_2.ifp \ + cok_1.ifp \ + cok_2a.ifp \ + cok_2b.ifp \ + cok_3.ifp \ + cok_4a.ifp \ + cok_4a2.ifp \ + cok_4b.ifp \ + col_1.ifp \ + col_2.ifp \ + col_3a.ifp \ + col_4a.ifp \ + col_4b.ifp \ + col_5a.ifp \ + col_5b.ifp \ + cub_1.ifp \ + cub_2.ifp \ + cub_3.ifp \ + cub_4.ifp \ + drug_1.ifp \ + fin.ifp \ + fin_2.ifp \ + finale.ifp \ + hat_1.ifp \ + hat_2.ifp \ + hat_3.ifp \ + ice_1.ifp \ + int_a.ifp \ + int_b.ifp \ + int_d.ifp \ + int_m.ifp \ + int_m2.ifp \ + law_1a.ifp \ + law_1b.ifp \ + law_2a.ifp \ + law_2b.ifp \ + law_2c.ifp \ + law_3.ifp \ + law_4.ifp \ + phil_1.ifp \ + phil_2.ifp \ + porn_1.ifp \ + porn_2.ifp \ + porn_3.ifp \ + porn_4.ifp \ + resc_1a.ifp \ + rok_1.ifp \ + rok_2.ifp \ + rok_3a.ifp \ + rok_4a.ifp \ + rok_4b.ifp \ + rok_4c.ifp \ + rok_4d.ifp \ + stripa.ifp \ + tax_1.ifp \ + tex_1.ifp \ + tex_2.ifp \ + tex_3.ifp + +CUTS_MISC = \ + ass_1.dat \ + ass_2.dat \ + bank_1.dat \ + bank_2a.dat \ + bank_2b.dat \ + bank_3a.dat \ + bank_3b.dat \ + bank_4.dat \ + bike_1.dat \ + bike_2.dat \ + bike_3.dat \ + bud_1.dat \ + bud_2.dat \ + bud_3.dat \ + cap_1.dat \ + car_1.dat \ + cnt_1a.dat \ + cnt_1b.dat \ + cnt_2.dat \ + cok_1.dat \ + cok_2a.dat \ + cok_2b.dat \ + cok_3.dat \ + cok_4a.dat \ + cok_4a2.dat \ + cok_4b.dat \ + col_1.dat \ + col_2.dat \ + col_3a.dat \ + col_4a.dat \ + col_5a.dat \ + col_5b.dat \ + cub_1.dat \ + cub_2.dat \ + cub_3.dat \ + cub_4.dat \ + drug_1.dat \ + fin.dat \ + fin_2.dat \ + finale.dat \ + hat_1.dat \ + hat_2.dat \ + hat_3.dat \ + ice_1.dat \ + int_a.dat \ + int_b.dat \ + int_d.dat \ + int_m.dat \ + int_m2.dat \ + law_1a.dat \ + law_1b.dat \ + law_2a.dat \ + law_2b.dat \ + law_2c.dat \ + law_3.dat \ + law_4.dat \ + phil_1.dat \ + phil_2.dat \ + porn_1.dat \ + porn_2.dat \ + porn_3.dat \ + porn_4.dat \ + resc_1a.dat \ + rok_1.dat \ + rok_2.dat \ + rok_3a.dat \ + rok_4a.dat \ + stripa.dat \ + tax_1.dat \ + tex_1.dat \ + tex_2.dat \ + tex_3.dat \ No newline at end of file diff --git a/miami/gta3files.mk b/miami/gta3files.mk new file mode 100644 index 00000000..6bd1b88f --- /dev/null +++ b/miami/gta3files.mk @@ -0,0 +1,210 @@ +# models/coll/peds.col # not actually used + +MISC_FILES = \ + \ + txd/LOADSC8.TXD \ + txd/LOADSC6.TXD \ + txd/outro.txd \ + txd/LOADSC2.TXD \ + txd/LOADSC5.TXD \ + txd/LOADSC3.TXD \ + txd/NEWS.TXD \ + txd/LOADSC11.TXD \ + txd/LOADSC12.TXD \ + txd/LOADSC10.TXD \ + txd/LOADSC1.TXD \ + txd/LOADSC9.TXD \ + txd/LOADSC7.TXD \ + txd/SPLASH2.TXD \ + txd/LOADSC13.TXD \ + txd/INTRO3.TXD \ + txd/SPLASH3.TXD \ + txd/SPLASH1.TXD \ + txd/intro2.txd \ + txd/intro1.txd \ + txd/LOADSC0.TXD \ + txd/LOADSC4.TXD \ + txd/intro4.txd \ + \ + models/fonts.txd \ + models/hud.txd \ + models/coll/weapons.col \ + models/coll/vehicles.col \ + models/coll/generic.col \ + models/MISC.TXD \ + models/generic.txd \ + models/gta3.dir \ + models/fronten2.txd \ + models/particle.txd \ + models/fronten1.txd \ + models/INTRO.TXD \ + models/generic/wheels.TXD \ + models/generic/wheels.DFF \ + models/generic/arrow.DFF \ + models/generic/player.bmp \ + models/generic/air_vlo.DFF \ + models/generic/zonecylb.DFF \ + models/gta3.img \ + \ + anim/ped.ifp \ + \ + Icons/app.ico \ + Icons/bin.ico \ + \ + skins/texture_guide.jpg \ + \ + movies/GTAtitles.mpg \ + movies/Logo.mpg \ + \ + TEXT/german.gxt \ + TEXT/italian.gxt \ + TEXT/spanish.gxt \ + TEXT/american.gxt \ + TEXT/french.gxt \ + \ + data/weapon.dat \ + data/train.dat \ + \ + data/maps/haiti/haiti.ide \ + data/maps/haiti/haiti.col \ + data/maps/haiti/haiti.ipl \ + data/maps/generic.ide \ + \ + data/maps/hotel/hotel.ipl \ + data/maps/hotel/hotel.ide \ + data/maps/hotel/hotel.col \ + \ + data/maps/islandsf/islandsf.ide \ + data/maps/islandsf/islandsf.ipl \ + data/maps/islandsf/islandsf.col \ + \ + data/maps/washintn/washintn.ipl \ + data/maps/washintn/washintn.ide \ + data/maps/washintn/washintn.col \ + data/maps/map6.dat \ + \ + data/maps/littleha/littleha.ipl \ + data/maps/littleha/littleha.ide \ + data/maps/littleha/littleha.col \ + \ + data/maps/mall/mall.ide \ + data/maps/mall/mall.col \ + data/maps/mall/mall.ipl \ + data/maps/map7.dat \ + \ + data/maps/bar/bar.ide \ + \ + data/maps/nbeach/nbeach.ipl \ + data/maps/nbeach/nbeach.ide \ + data/maps/nbeach/nbeach.col \ + \ + data/maps/golf/golf.ide \ + data/maps/golf/golf.col \ + data/maps/golf/golf.ipl \ + data/maps/map0.dat \ + \ + data/maps/bank/bank.col \ + data/maps/bank/bank.ide \ + data/maps/bank/bank.ipl \ + \ + data/maps/nbeachw/nbeachw.ide \ + data/maps/nbeachw/nbeachw.col \ + data/maps/nbeachw/nbeachw.ipl \ + data/maps/map4.dat \ + \ + data/maps/airport/airport.ipl \ + data/maps/airport/airport.ide \ + data/maps/airport/airport.col \ + data/maps/nbeachbt/nbeachbt.col \ + data/maps/nbeachbt/nbeachbt.ide \ + data/maps/nbeachbt/nbeachbt.ipl \ + data/maps/map5.dat \ + data/maps/lawyers/lawyers.ide \ + data/maps/lawyers/lawyers.col \ + data/maps/lawyers/lawyers.ipl \ + data/maps/haitiN/haitin.ipl \ + data/maps/haitiN/haitin.col \ + data/maps/haitiN/haitiN.ide \ + data/maps/downtown/downtown.col \ + data/maps/downtown/downtown.ipl \ + data/maps/downtown/downtown.ide \ + data/maps/stadint/stadint.col \ + data/maps/stadint/stadint.ide \ + data/maps/stadint/stadint.ipl \ + data/maps/club/club.ide \ + data/maps/club/CLUB.ipl \ + data/maps/club/CLUB.col \ + data/maps/yacht/yacht.ipl \ + data/maps/yacht/yacht.col \ + data/maps/yacht/yacht.ide \ + data/maps/cull.ipl \ + data/maps/map3.dat \ + data/maps/airportN/airportN.ipl \ + data/maps/airportN/airportN.col \ + data/maps/airportN/airportN.ide \ + data/maps/oceandrv/oceandrv.ipl \ + data/maps/oceandrv/oceandrv.ide \ + data/maps/oceandrv/oceandrv.col \ + data/maps/starisl/starisl.col \ + data/maps/starisl/starisl.ide \ + data/maps/starisl/starisl.ipl \ + data/maps/map1.dat \ + data/maps/oceandn/oceandN.ide \ + data/maps/oceandn/oceandN.col \ + data/maps/oceandn/oceandN.ipl \ + data/maps/washints/washints.ipl \ + data/maps/washints/washints.col \ + data/maps/washints/washints.ide \ + data/maps/mansion/mansion.col \ + data/maps/mansion/mansion.ipl \ + data/maps/mansion/mansion.ide \ + data/maps/downtows/downtows.ipl \ + data/maps/downtows/downtows.col \ + data/maps/downtows/downtows.ide \ + data/maps/bridge/bridge.col \ + data/maps/bridge/bridge.ide \ + data/maps/bridge/bridge.ipl \ + data/maps/docks/docks.col \ + data/maps/docks/docks.ide \ + data/maps/docks/docks.ipl \ + data/maps/concerth/concerth.ipl \ + data/maps/concerth/concerth.ide \ + data/maps/concerth/concerth.col \ + data/maps/paths.ipl \ + data/maps/cisland/cisland.ide \ + data/maps/cisland/cisland.ipl \ + data/maps/cisland/cisland.col \ + data/maps/stripclb/stripclb.ide \ + data/maps/stripclb/stripclb.ipl \ + data/maps/stripclb/stripclb.col \ + data/maps/map2.dat \ + data/pedstats.dat \ + data/gta_vc.dat \ + data/occlu.ipl \ + data/handling.cfg \ + data/timecyc.dat \ + data/paths/flight3.dat \ + data/paths/spath0.dat \ + data/paths/flight2.dat \ + data/paths/flight.dat \ + data/map.zon \ + data/water.dat \ + data/particle.cfg \ + data/navig.zon \ + data/default.dat \ + data/carcols.dat \ + data/fistfite.dat \ + data/train2.dat \ + data/info.zon \ + data/ped.dat \ + data/WATERPRO.DAT \ + data/main.sc \ + data/debug.sc \ + data/object.dat \ + data/animviewer.dat \ + data/Cullzoneempty.dat \ + data/surface.dat \ + data/main.scm \ + data/default.ide \ + data/pedgrp.dat \ + data/Cullzone.dat \ No newline at end of file diff --git a/miami/imgmisc.mk b/miami/imgmisc.mk new file mode 100644 index 00000000..948a8e4f --- /dev/null +++ b/miami/imgmisc.mk @@ -0,0 +1,61 @@ +IMG_COL = \ + airport.col \ + airportN.col \ + bank.col \ + bridge.col \ + cisland.col \ + CLUB.col \ + concerth.col \ + docks.col \ + downtown.col \ + downtows.col \ + golf.col \ + haiti.col \ + haitin.col \ + hotel.col \ + islandsf.col \ + lawyers.col \ + littleha.col \ + mall.col \ + mansion.col \ + nbeachbt.col \ + nbeach.col \ + nbeachw.col \ + oceandN.col \ + oceandrv.col \ + stadint.col \ + starisl.col \ + stripclb.col \ + washintn.col \ + washints.col \ + yacht.col + +IMG_IFP = \ + baseball.ifp \ + biked.ifp \ + bikeh.ifp \ + bikes.ifp \ + bikev.ifp \ + buddy.ifp \ + chainsaw.ifp \ + coach.ifp \ + colt45.ifp \ + flame.ifp \ + grenade.ifp \ + knife.ifp \ + lance.ifp \ + m60.ifp \ + medic.ifp \ + playidles.ifp \ + python.ifp \ + rifle.ifp \ + riot.ifp \ + shotgun.ifp \ + skate.ifp \ + sniper.ifp \ + strip.ifp \ + sunbathe.ifp \ + sword.ifp \ + tec.ifp \ + uzi.ifp \ + van.ifp \ No newline at end of file diff --git a/dreamcast/ip.txt b/miami/ip.txt similarity index 65% rename from dreamcast/ip.txt rename to miami/ip.txt index 8cfb4b37..1f44b9d3 100644 --- a/dreamcast/ip.txt +++ b/miami/ip.txt @@ -1,9 +1,9 @@ Device Info : CD-ROM1/1 Area Symbols : JUE Peripherals : E000F10 -Product No : T0000 +Product No : DCA-MIAM Version : V1.000 -Release Date : 20000627 +Release Date : 20250401 Boot Filename : 1ST_READ.BIN SW Maker Name : the gang -Game Title : DCA3 +Game Title : dca-miami diff --git a/miami/modlist.mk b/miami/modlist.mk new file mode 100644 index 00000000..4dc69853 --- /dev/null +++ b/miami/modlist.mk @@ -0,0 +1,4614 @@ +IMG_MODELS = \ + 4x4finishline.dff \ + 4x4racecotop2.dff \ + 4x4racecotop.dff \ + 4x4rockstop.dff \ + 4x4signstop.dff \ + admiral.dff \ + adrenaline.dff \ + air_brway_030.dff \ + air_brway_31.dff \ + air_brway_32.dff \ + air_brway_33.dff \ + air_brway_34.dff \ + airplan.dff \ + airport_carpark0.dff \ + airportgate.dff \ + airtrain.dff \ + ak47.dff \ + ambulan.dff \ + ammunation_dt.dff \ + ammu_trannies.dff \ + ammu_windows1.dff \ + angel.dff \ + ap_06trailer01.dff \ + ap_3dbillb.dff \ + apairprtbits01.dff \ + apairprtbits05.dff \ + apairprtbits07.dff \ + apairprtbits08.dff \ + ap_barriergate1.dff \ + ap_barriergate2.dff \ + ap_barriergate3.dff \ + ap_barriergate4.dff \ + ap_billboards1.dff \ + ap_billboards2.dff \ + ap_billboards3.dff \ + ap_billboards4.dff \ + ap_billboards5.dff \ + ap_blastdef_01.dff \ + ap_blastdef_03.dff \ + ap_boardshad1.dff \ + ap_booth1_01.dff \ + ap_booth1_02.dff \ + ap_booth2_01.dff \ + ap_booth2_03.dff \ + ap_booth3_01.dff \ + ap_boxjunct1.dff \ + ap_carbush2_01.dff \ + ap_carparkterm1.dff \ + ap_concwall1.dff \ + ap_dockstairs1.dff \ + ap_dockwall1.dff \ + ap_firetrucks.dff \ + ap_flargetank1.dff \ + ap_flyingschool1.dff \ + ap_freight1_01b.dff \ + ap_freight1_01.dff \ + ap_freight2_01a.dff \ + ap_freight2_01.dff \ + ap_freight3_01a.dff \ + ap_freight3_01.dff \ + ap_freightterminal1.dff \ + ap_fuelbay1.dff \ + ap_fuelshad1.dff \ + ap_gatedisp1_01.dff \ + ap_gatedisp1_02.dff \ + ap_gatesA.dff \ + ap_gatesB.dff \ + ap_gsecurity1.dff \ + ap_gsecurity2.dff \ + ap_hangar1_01.dff \ + ap_hangar1_03.dff \ + ap_hangar1_04.dff \ + ap_hland_01.dff \ + ap_hookerinn1.dff \ + ap_hotelpool.dff \ + ap_hotroadsect1.dff \ + ap_jumbo_01.dff \ + ap_jumbo_BRIDGE.dff \ + ap_kerb1.dff \ + ap_learjet1_01.dff \ + ap_midtank1.dff \ + ap_newesca_01.dff \ + ap_newesca_02.dff \ + ap_newescaGLAS_01.dff \ + ap_newescaGLAS_02.dff \ + ap_oilpipes1.dff \ + ap_oiltank1_01.dff \ + Apoulet.dff \ + ap_planters1_01.dff \ + ap_planters2_01.dff \ + ap_pots1_01.dff \ + ap_pots2_01.dff \ + ap_radar1_01.dff \ + ap_rland01.dff \ + ap_rland02.dff \ + ap_rland03.dff \ + ap_rland04.dff \ + ap_rland05.dff \ + ap_rland06.dff \ + ap_rland07.dff \ + ap_rland08.dff \ + ap_rland10.dff \ + ap_rlandnew9.dff \ + ap_roadsect1.dff \ + ap_roadsect2a.dff \ + ap_roadsect2b.dff \ + ap_roadsect3b.dff \ + ap_roadsect3.dff \ + ap_roadsect4.dff \ + ap_roadsect5.dff \ + ap_roadsigns1_02.dff \ + ap_roadsigns1_03.dff \ + ap_runsign10_03.dff \ + ap_runsign3_01.dff \ + ap_runsign6_02.dff \ + ap_runsign7_03.dff \ + ap_runsigns5_01.dff \ + ap_screens1_01.dff \ + ap_screens2_01.dff \ + ap_seafences.dff \ + ap_seahangstuft.dff \ + ap_seaplaland1.dff \ + ap_seaplanedock1.dff \ + ap_seaplanehanger1.dff \ + ap_seaplanfence1.dff \ + ap_security1.dff \ + ap_security2.dff \ + ap_smahangar2_01.dff \ + ap_smahangar2_02.dff \ + ap_smahangar2_03.dff \ + ap_smahangar2_04.dff \ + ap_smahangar2_05.dff \ + ap_smahangar2_06.dff \ + ap_smallhangars1_01.dff \ + ap_smallhangars1_02.dff \ + ap_smallhangars1_03.dff \ + ap_smallradar1_02.dff \ + ap_smallradar1.dff \ + ap_stairsout02.dff \ + ap_stairsout03.dff \ + ap_stairsout04.dff \ + ap_stairsout1.dff \ + ap_steps1_01.dff \ + ap_steps1_02.dff \ + ap_steps1_03.dff \ + ap_steps1_04.dff \ + ap_steps1_05.dff \ + ap_steps1_06.dff \ + ap_steps1_07.dff \ + ap_steps1_08.dff \ + ap_stepsn1_01.dff \ + ap_stepsn1_02.dff \ + ap_subfraightback01.dff \ + ap_subfraightback02.dff \ + ap_subfraightback04.dff \ + ap_termAintfloor1b.dff \ + ap_termAintfloor1.dff \ + ap_termAintfloor2b.dff \ + ap_termAintfloor2.dff \ + ap_termbbase1.dff \ + ap_terminA_01.dff \ + ap_terminA2_01.dff \ + ap_terminAchek1_01.dff \ + ap_terminAchek1_02.dff \ + ap_terminAgatesA.dff \ + ap_terminAgatesB.dff \ + ap_terminAgrflo1.dff \ + ap_terminAgrflo2.dff \ + ap_terminalB1.dff \ + ap_terminA_roof1.dff \ + ap_terminA_roof2.dff \ + ap_termsign1_01.dff \ + ap_termsign1_dy.dff \ + ap_termsign1_nt.dff \ + ap_termsign2_01.dff \ + ap_termwindows1b.dff \ + ap_termwindows1.dff \ + ap_tower.dff \ + ap_trailer_01.dff \ + ap_transpol1.dff \ + ap_treesfw1_01.dff \ + ap_treeshot1_02.dff \ + ap_wallfence1.dff \ + ap_wallfence2.dff \ + ap_wallfence3.dff \ + ap_wallfence4.dff \ + ap_wallfence5.dff \ + ap_wallfence6.dff \ + ap_wallfence7.dff \ + ap_windowstruts.dff \ + arcadesjm01.dff \ + arcadesjm02.dff \ + arcadesjm03.dff \ + arcadesjm.dff \ + armour.dff \ + army.dff \ + baggage.dff \ + bank_officedesks1.dff \ + bank_other_strctr.dff \ + bank_palms02.dff \ + bank_palms09.dff \ + bank_palms21.dff \ + bank_palms23.dff \ + bank_palms28.dff \ + bank_palms32.dff \ + bank_palms33.dff \ + bank_props2.dff \ + bankrecept01.dff \ + bankrecept.dff \ + bankstructure4.dff \ + bank_table05.dff \ + bank_table10.dff \ + bank_table1.dff \ + bank_table4.dff \ + bank_vault.dff \ + banshee.dff \ + bar_barrier10b.dff \ + bar_barrier10.dff \ + bar_barrier12.dff \ + bar_barrier16.dff \ + bar_barriergate1.dff \ + bar_gatebar01.dff \ + bar_gatebox01.dff \ + barracks.dff \ + barrel1.dff \ + barrel2.dff \ + barrel3.dff \ + barrel4.dff \ + barrelexpos.dff \ + Barrierm.dff \ + barrierturn.dff \ + basketballcourt04.dff \ + basketballcourt05.dff \ + bat.dff \ + bb_fence1.dff \ + bb_pooltable1_01.dff \ + bb_roof1_01.dff \ + bb_walljunk1_01.dff \ + beachball.dff \ + beachblend01.dff \ + beachblend02.dff \ + beachblend03.dff \ + beachblend04.dff \ + beachblend05.dff \ + beachblend06.dff \ + beachblend07.dff \ + beachblend08.dff \ + beachblend09.dff \ + beachblend10.dff \ + beachblend11.dff \ + beachblend12.dff \ + beachblend13.dff \ + beachblend14.dff \ + beachblend15.dff \ + beachblend16.dff \ + beachbta.dff \ + beachbtb2.dff \ + beachbtb.dff \ + beachbt.dff \ + beach_bush02.dff \ + beach_bush04.dff \ + beach_bush06s.dff \ + beach_bush08s.dff \ + beachlo1.dff \ + beachtowel01.dff \ + beachtowel02.dff \ + beachtowel03.dff \ + beachtowel04.dff \ + bed_shop.dff \ + benson.dff \ + bfinject.dff \ + BFOBE.dff \ + bfori.dff \ + bfost.dff \ + bfotr.dff \ + bfybe.dff \ + BFYPR.dff \ + bfyri.dff \ + BFYST.dff \ + bga.dff \ + bgb.dff \ + b_hse_doors.dff \ + b_hse_ext.dff \ + b_hse_interior.dff \ + b_hse_interiorrays.dff \ + b_hse_pier.dff \ + b_hse_pierfence.dff \ + bigdollar.dff \ + biggrass2.dff \ + bighotelgrnd.dff \ + big_poly_tings_b.dff \ + big_poly_tings.dff \ + biker_bar_exterior.dff \ + bikeshop_glass2.dff \ + bikeshop_glass.dff \ + BillBd04.dff \ + BillBd1.dff \ + BillBd2.dff \ + BillBd3.dff \ + billboard.dff \ + bin1.dff \ + bka.dff \ + bkb.dff \ + BlackBag1.dff \ + BlackBag2.dff \ + bldigste1mesh.dff \ + bldngst2meshdam.dff \ + bldngst2mesh.dff \ + blimp_day.dff \ + blimp_night.dff \ + blistac.dff \ + bloodra.dff \ + bloodrb.dff \ + BMOBE.dff \ + bmodk.dff \ + bmost.dff \ + bmotr.dff \ + bmybb.dff \ + bmybe.dff \ + bmybu.dff \ + bmycr.dff \ + bmypi.dff \ + bmyri.dff \ + bmyst.dff \ + bnk_dsk_acses.dff \ + bnk_dsk_acsesno2.dff \ + bnk_ext_int.dff \ + bnk_grill01.dff \ + bnk_lft_door1.dff \ + bnk_lft_door2.dff \ + bnk_lightsmain.dff \ + bnk_maindr1.dff \ + bnk_maindr2.dff \ + bnk_main_glass.dff \ + bnk_mainnewref.dff \ + bnk_main_rework.dff \ + bnk_main_rooflights.dff \ + bnk_man_ofc01.dff \ + bnk_man_ofc.dff \ + bnk_mngr_chr.dff \ + bnk_mngr_rf_lights.dff \ + bnk_recp_lites.dff \ + bnk_scrty_tbl.dff \ + bnk_shado_2.dff \ + bnk_valt_door.dff \ + boatcranelg0.dff \ + boatcranesm0.dff \ + Boat_houseA01.dff \ + Boat_houseA.dff \ + boat_kb1.dff \ + boat_kb2.dff \ + boatpole.dff \ + bobcat.dff \ + bodyarmour.dff \ + bollard.dff \ + bollardlight.dff \ + bomb.dff \ + bonus.dff \ + boomshine.dff \ + bounca.dff \ + bouy.dff \ + boxville.dff \ + brassknuckle.dff \ + brfcase.dff \ + bribe.dff \ + briefcase.dff \ + buddy.dff \ + buddyshot.dff \ + build1.dff \ + buildblown.dff \ + buildingground40.dff \ + buildingground50.dff \ + buildingsite2.dff \ + buldingsite1.dff \ + burger.dff \ + burrito.dff \ + bus.dff \ + bussign1.dff \ + bustopm.dff \ + cabbie.dff \ + caddy.dff \ + camera.dff \ + camerapickup.dff \ + carback.dff \ + cardboardbox2.dff \ + cardboardbox4.dff \ + cardboardbox.dff \ + card.dff \ + car_fucko03.dff \ + carparkfence0.dff \ + carshowflags.dff \ + carsontrack.dff \ + carwashpole1.dff \ + carwashpole2.dff \ + carwashpole3.dff \ + carwashsign1.dff \ + Cba.dff \ + Cbb.dff \ + cb_CLUBSTAIRS.dff \ + ccfan.dff \ + cdrivra.dff \ + cdrivrb.dff \ + cdseaan.dff \ + cdseabed01.dff \ + cdseabed02.dff \ + cdseabed03.dff \ + cdseabed04.dff \ + cdseabed05.dff \ + cdseabed06.dff \ + cdseabed07.dff \ + cdseabed08.dff \ + cdseabed09.dff \ + cdseabed10.dff \ + cdseabed11.dff \ + cdseabed12.dff \ + cdseabed13.dff \ + cdseabed14.dff \ + cdseabed15.dff \ + cdseabed16.dff \ + cdseabed17.dff \ + cdseabed18.dff \ + cdseabed19.dff \ + cdseabed20.dff \ + cdseabed21.dff \ + cdseabed22.dff \ + cdseabed23.dff \ + cdseabed24.dff \ + cdseabed25.dff \ + cdseabed26.dff \ + cdseabed27.dff \ + cdseabed28.dff \ + cdseabed29.dff \ + cdseabed30.dff \ + cdseabed31.dff \ + cdseabed32.dff \ + cdseabed33.dff \ + cellphone.dff \ + cgona.dff \ + cgonb.dff \ + cgonc.dff \ + cgoona.dff \ + cgoonb.dff \ + chairsntable.dff \ + chairsntableml.dff \ + chairthings2.dff \ + chairthings.dff \ + cheetah.dff \ + chef.dff \ + chnsaw.dff \ + chopper.dff \ + chromegun.dff \ + ci_Acyclorama.dff \ + ci_alights1_01.dff \ + ci_astage.dff \ + ci_astageint.dff \ + ci_astagelight1.dff \ + ci_astagelight2.dff \ + ci_astagelights.dff \ + ci_Asteps.dff \ + ci_astudganwal.dff \ + ci_astudiogantry.dff \ + ci_astudlights.dff \ + ci_backdrop.dff \ + ci_backgateclose.dff \ + ci_backgateopen.dff \ + ci_backlot1.dff \ + ci_backlot2.dff \ + ci_backlot3.dff \ + ci_backlotprops1.dff \ + ci_backlotprops2.dff \ + ci_backlotprops3.dff \ + ci_bcyclorama1.dff \ + ci_blights1_01.dff \ + ci_boards1.dff \ + ci_boards2.dff \ + ci_bstage.dff \ + ci_bstageint.dff \ + ci_bstudganwal.dff \ + ci_bstudiogantry.dff \ + ci_bstudlights.dff \ + ci_busht_02.dff \ + ci_busht_04.dff \ + ci_busht_06.dff \ + ci_busht_08.dff \ + ci_busht_11.dff \ + ci_camera1_01.dff \ + ci_clightdesk.dff \ + ci_cstage.dff \ + ci_cstageint.dff \ + ci_dirtcorn1.dff \ + ci_dirtcorn2.dff \ + ci_dirtcorn3.dff \ + ci_dirtcorn4.dff \ + ci_dstage.dff \ + ci_dstageext.dff \ + ci_dstagelight.dff \ + ci_dstageshad.dff \ + ci_dstudganwal.dff \ + ci_dstudiogantry.dff \ + ci_dstudlights.dff \ + ci_extertank.dff \ + ci_fence2.dff \ + ci_flatprop01.dff \ + ci_flatprop02.dff \ + ci_fountain.dff \ + ci_gatesclosed.dff \ + ci_gatesopen.dff \ + ci_gateway.dff \ + ci_gobo1.dff \ + ci_graf_fountain.dff \ + ci_graf_mansion1.dff \ + ci_graf_mansion3.dff \ + ci_guardhouse1.dff \ + ci_industrial1.dff \ + ci_islandeast.dff \ + ci_islandnorth.dff \ + ci_islandwest.dff \ + ci_jawsramp1.dff \ + ci_jetygatesclosed.dff \ + ci_jetygatesopen.dff \ + ci_kerb1.dff \ + ci_lander.dff \ + ci_lights1_01.dff \ + ci_lights1_02.dff \ + ci_manintstuff.dff \ + ci_mans1grafint.dff \ + ci_mans1grndfl.dff \ + ci_mans1int.dff \ + ci_mans1istfl.dff \ + ci_mans1rail1.dff \ + ci_mans1rail.dff \ + ci_mans1roof.dff \ + ci_mans1stair1.dff \ + ci_mans1stair2.dff \ + ci_mans2rail1.dff \ + ci_manscourt.dff \ + ci_mansion1.dff \ + ci_mansion2.dff \ + ci_mansion3ballust.dff \ + ci_mansion3.dff \ + ci_mansionwall1.dff \ + ci_moonsurface.dff \ + ci_nbbridge1.dff \ + ci_nbbridge1fn2.dff \ + ci_nbbridge1fn3.dff \ + ci_notice03.dff \ + ci_offices.dff \ + ci_officewindows.dff \ + ci_palettes.dff \ + ci_planter1.dff \ + ci_planter2.dff \ + ci_poolhouse.dff \ + ci_poolsteps.dff \ + ci_poolwall1.dff \ + ci_pornobed.dff \ + ci_pornocarpet.dff \ + ci_pornset1.dff \ + ci_pornsetxtra.dff \ + ci_posters.dff \ + ci_rdsigns01.dff \ + ci_rdsigns.dff \ + ci_redhead1_01.dff \ + ci_screening2.dff \ + ci_screening.dff \ + ci_screeningstairs.dff \ + ci_seajetty.dff \ + ci_setshad1.dff \ + ci_shipset.dff \ + ci_spaceflag.dff \ + ci_stairsout01.dff \ + ci_studiowall.dff \ + ci_studwallgraf.dff \ + ci_table1_01.dff \ + ci_tankship.dff \ + ci_trees02.dff \ + ci_trees03.dff \ + ci_trees04.dff \ + ci_trees05.dff \ + ci_trees1.dff \ + ci_watertank.dff \ + ci_windmach2.dff \ + ci_windmach.dff \ + Cla.dff \ + cl_bar_temp1.dff \ + cl_bckrm_couchs.dff \ + cl_bckrm_tablewee.dff \ + cl_bckrm_tble.dff \ + Clb.dff \ + clchr.dff \ + cl_coolers.dff \ + cl_curtains.dff \ + cl_dancefloor.dff \ + cleaver.dff \ + cl_light_st.dff \ + cl_lightsuprts.dff \ + cl_main_room12.dff \ + cl_main_roomb2.dff \ + cl_main_roomb.dff \ + cl_main_room.dff \ + cl_mainwallsb.dff \ + cl_mainwalls.dff \ + cl_of_opac.dff \ + cl_ofs_props.dff \ + clothesp.dff \ + cl_railingb.dff \ + cl_railing.dff \ + cl_recessed_flr.dff \ + cl_recessedlights1.dff \ + cl_spots.dff \ + cl_stage.dff \ + cl_stage_roof.dff \ + cl_tablesetlrg.dff \ + cl_tbl_chait_tmpb.dff \ + cl_tbl_chait_tmp.dff \ + cl_tblesetsml.dff \ + clubbackroom.dff \ + clubceilingdome.dff \ + club_exterior01.dff \ + club_exterior02.dff \ + club_exterior03.dff \ + club_exterior04.dff \ + club_exterior05.dff \ + club_exterior06.dff \ + club_exterior07.dff \ + club_exterior08.dff \ + club_exterior09.dff \ + club_exterior10.dff \ + club_exterior11.dff \ + club_exterior12.dff \ + cl_yachtmdl.dff \ + cmraman.dff \ + cntrlrsac1.dff \ + coach.dff \ + coastg.dff \ + cofbarbothint.dff \ + cof_bardetail.dff \ + cof_extwindw.dff \ + cof_intfloor.dff \ + cof_seat.dff \ + cof_shad1.dff \ + cof_shad2.dff \ + cof_shad4.dff \ + cofsho_ext1.dff \ + cof_shpdetail1.dff \ + cof_xtremedet.dff \ + coke_voodoo.dff \ + coknife.dff \ + cokpoolwater.dff \ + colonel.dff \ + colphon.dff \ + colt45.dff \ + coltray.dff \ + Columbiangate.dff \ + comet.dff \ + comgate03.dff \ + comgate1closed.dff \ + comgate1.dff \ + comgate1open.dff \ + comgate2closed.dff \ + comgate2.dff \ + comgate2open.dff \ + compound_bits.dff \ + compound_fence.dff \ + concerth02.dff \ + concerth03.dff \ + concerth04.dff \ + concerth05.dff \ + concerth06.dff \ + concerth07.dff \ + concerth08.dff \ + concerth09.dff \ + concerth10.dff \ + concerth11.dff \ + concerth12.dff \ + concerth27.dff \ + concerth29.dff \ + concerth48.dff \ + concerth49.dff \ + concerth50.dff \ + concerth51.dff \ + concerth52.dff \ + concerth54.dff \ + concerth56.dff \ + const_woodfence30.dff \ + cooker1.dff \ + cop_blackpoly.dff \ + cop_cells.dff \ + Cop.dff \ + cop_dr_closed.dff \ + cop_dr_open.dff \ + cop_entrance.dff \ + cop_entrance_fake.dff \ + cop_fake_ext01.dff \ + cop_fake_ext02.dff \ + cop_fake_ext03.dff \ + cop_hallway.dff \ + cop_hallway_fake.dff \ + cop_mainroom.dff \ + cop_pillar_fake.dff \ + cop_pillar_shad.dff \ + cop_processing.dff \ + cop_waterref.dff \ + cougar.dff \ + courier.dff \ + cpyth.dff \ + craigpackage.dff \ + cranebasea0.dff \ + crewa.dff \ + crewb.dff \ + crgoshp010.dff \ + crocneon.dff \ + CSAlsb2.dff \ + CSAlscb2.dff \ + CSAlscb.dff \ + CSassA.dff \ + CSassB.dff \ + CSassC.dff \ + CSavery.dff \ + CSbigm.dff \ + CSBJ.dff \ + CSBuddy.dff \ + cs_cam.dff \ + CScamj.dff \ + CSCandy.dff \ + CScgona.dff \ + CScgonb.dff \ + CScgonc.dff \ + cschopb.dff \ + cs_chop.dff \ + CScolo.dff \ + CScougr.dff \ + CSdeal.dff \ + CSdiaz.dff \ + CSDick.dff \ + CSdirec.dff \ + CSdisp.dff \ + CSdlove.dff \ + CSDoris.dff \ + CSdwayn.dff \ + cservra.dff \ + cservrb.dff \ + CSFiller.dff \ + CSganga.dff \ + CSgangb.dff \ + CSgangc.dff \ + CSgona.dff \ + CSgonb.dff \ + CSgonc.dff \ + CSGonz.dff \ + CShlary.dff \ + CSJetro.dff \ + CSJezz.dff \ + CSJuggz.dff \ + CSKelly.dff \ + CSken.dff \ + CSkent.dff \ + CSLance.dff \ + cs_limo.dff \ + CSMaude.dff \ + CSmerc2.dff \ + CSMerc.dff \ + CSmike.dff \ + CSmitch.dff \ + CSpablo.dff \ + CSPapa.dff \ + CSPepe.dff \ + CSPercy.dff \ + CSphil.dff \ + CSplay10.dff \ + CSplay11.dff \ + CSplay2.dff \ + CSplay3.dff \ + CSplay4.dff \ + CSplay5.dff \ + CSplay6.dff \ + CSplay7.dff \ + CSplay8.dff \ + CSplay9.dff \ + CSplay.dff \ + cs_reef.dff \ + CSrich.dff \ + CSsonny.dff \ + CSumbto.dff \ + CSzepp.dff \ + cuban.dff \ + C_worker.dff \ + dcfan.dff \ + deaddodo.dff \ + deagl.dff \ + deco_buildkb09.dff \ + deco_buildkb25.dff \ + deco_buildkb29_dy.dff \ + deco_buildkb29_nt.dff \ + deco_buildkb_dy.dff \ + deco_buildkb_nt.dff \ + deco_copgrnd.dff \ + deco_polbuild.dff \ + deco_polgrnda10.dff \ + deco_polgrnda12.dff \ + deco_polgrnda13.dff \ + deco_polgrnda14.dff \ + deco_polgrnda16.dff \ + deco_polgrnda17.dff \ + deco_polgrnda7.dff \ + deco_polgrnda8.dff \ + deco_polgrnda9.dff \ + delcase.dff \ + deli_exterior_front.dff \ + deli_interior.dff \ + deluxo.dff \ + destruct03.dff \ + destruct04.dff \ + destruct05.dff \ + destruct1.dff \ + destruct2.dff \ + dgoona.dff \ + dgoonb.dff \ + dgoonc.dff \ + diaz.dff \ + dinghy.dff \ + dirtdetail2.dff \ + dispensary_gls.dff \ + divingboard.dff \ + dk_bombdoor.dff \ + dk_bridgesupp01.dff \ + dk_bridgesupp03.dff \ + dk_bridgesupp04.dff \ + dk_camjonesdoor.dff \ + dkcargohull2b.dff \ + dkcargohull2c.dff \ + dkcargohull2.dff \ + dk_cargoshp01.dff \ + dk_cargoshp03.dff \ + dk_cargoshp04.dff \ + dk_cargoshp05.dff \ + dk_cargoshp100.dff \ + dk_cargoshp10.dff \ + dk_cargoshp12.dff \ + dk_cargoshp24.dff \ + dk_cargoshp25.dff \ + dk_cargoshp28.dff \ + dk_cargoshp31.dff \ + dk_cargoshp32.dff \ + dk_cargoshp35.dff \ + dk_cargoshp40.dff \ + dk_cargoshp41.dff \ + dk_cargoshp47.dff \ + dk_cargoshp50.dff \ + dk_cargoshp51.dff \ + dk_cargoshp53.dff \ + dk_cargoshp54.dff \ + dk_cargoshp64.dff \ + dk_cargoshp65.dff \ + dk_cargoshp66.dff \ + dk_cargoshp68.dff \ + dk_cargoshp70.dff \ + dk_cargoshp71.dff \ + dk_cargoshp72.dff \ + dk_cargoshp73.dff \ + dk_cargoshp76.dff \ + dk_cargoshp95.dff \ + dk_dockbridge.dff \ + dk_dockroads01.dff \ + dk_dockroads02.dff \ + dk_dockroads03.dff \ + dk_dockroads04.dff \ + dk_dockroads05.dff \ + dk_dockroads06.dff \ + dk_dockroads08.dff \ + dkfirtpass2.dff \ + dk_gatebar01.dff \ + dk_gatebar02.dff \ + dk_gatebox01.dff \ + dkglue3.dff \ + dk_paynspray.dff \ + dk_paynspraydoor.dff \ + dk_rail01.dff \ + dk_rail02.dff \ + dk_rail03.dff \ + dk_rail04.dff \ + dk_rail05.dff \ + dk_rail06.dff \ + dk_rail07.dff \ + dk_rdsignsbr.dff \ + dk_rdsignsn.dff \ + dk_reef.dff \ + dkshipment01.dff \ + dkshipment03.dff \ + dkshipment05.dff \ + dk_shipstair.dff \ + dk_waretank.dff \ + dk_waretankdoor1.dff \ + doc_crane_cab01.dff \ + doc_crane_cab02.dff \ + doc_crane_cab03.dff \ + doc_crane_cab04.dff \ + doc_crane_cab0.dff \ + doc_craneeggs04.dff \ + doc_dockwareold.dff \ + dock_camjones.dff \ + dockcranescale01.dff \ + dockcranescale0.dff \ + dockfence.dff \ + dockfuel02.dff \ + dockfuel07.dff \ + dockgate01.dff \ + dockgate02.dff \ + dock_grassarea.dff \ + dockgrass.dff \ + dock_props01.dff \ + dock_props02.dff \ + docks10.dff \ + docks10rail.dff \ + docks21.dff \ + docks28.dff \ + docks29.dff \ + docks30.dff \ + docks31.dff \ + docks32.dff \ + docks37.dff \ + docks40.dff \ + docks42.dff \ + docks43.dff \ + docks46.dff \ + docks47.dff \ + docks48.dff \ + docks49.dff \ + docks50.dff \ + docks51.dff \ + docks52.dff \ + docks53.dff \ + docks60.dff \ + docks61.dff \ + docks62.dff \ + docks85.dff \ + docks92.dff \ + docks93.dff \ + docks95.dff \ + docks96.dff \ + docksprops04.dff \ + docksprops10.dff \ + docksprops11.dff \ + docksprops12.dff \ + docksprops13.dff \ + docksprops14.dff \ + docksprops15.dff \ + docksware01.dff \ + doc_shedbig30.dff \ + dolphin.dff \ + doontoon03.dff \ + doontoon_04_bit2.dff \ + doontoon04.dff \ + doontoon09.dff \ + doontoon10.dff \ + doontoon11.dff \ + doontoon13.dff \ + doontoon14.dff \ + doontoon16.dff \ + doontoon18.dff \ + doontoon19.dff \ + doontoon20.dff \ + doontoon21.dff \ + doontoon_22_b.dff \ + doontoon22.dff \ + doontoon23.dff \ + doontoon24.dff \ + doontoon25_alfas.dff \ + doontoon25.dff \ + doontoon26.dff \ + doontoon27.dff \ + doontoon28.dff \ + doontoon29.dff \ + doontoon30.dff \ + doontoon31.dff \ + doontoon32_alfa.dff \ + doontoon32.dff \ + doontoon34.dff \ + doontoon35.dff \ + doontoon36.dff \ + doontoon37.dff \ + doontoon38.dff \ + doontoon39.dff \ + doontoon40.dff \ + doontoon41.dff \ + doontoon44.dff \ + doontoon45.dff \ + doontoon47.dff \ + doontoon49.dff \ + doontoon50.dff \ + doontoon51.dff \ + doontoon53.dff \ + doontoon54.dff \ + doontoon55.dff \ + doontoon56.dff \ + doontoon58.dff \ + doontoon59.dff \ + doontoon61.dff \ + doontoon62.dff \ + doontoon63.dff \ + doontoon65.dff \ + doontoon66_bushes01.dff \ + doontoon66.dff \ + doontoon67.dff \ + doontoon70.dff \ + doontoon72b.dff \ + doontoon72.dff \ + doontoon73b.dff \ + doontoon73.dff \ + doontoon74.dff \ + doontoon76.dff \ + doontoon77.dff \ + doontoon_newbit.dff \ + doontoon_shops.dff \ + doontoon_top01.dff \ + doontoon_top02.dff \ + doontoon_top03.dff \ + doontoon_top04.dff \ + doontoon_top05.dff \ + doontoon_top06.dff \ + doontoon_top07.dff \ + doontoon_top08.dff \ + doontoon_top09.dff \ + doontoon_top10.dff \ + doontoon_top_bb.dff \ + doublestreetlght1.dff \ + dowbikershop.dff \ + dow_pizzaplacedt.dff \ + drchair.dff \ + drink.dff \ + drive_flowers06.dff \ + drug_blue.dff \ + drug_green.dff \ + drug_orange.dff \ + drugpak.dff \ + drug_red.dff \ + drugstoreext.dff \ + drugstoreint.dff \ + drug_white.dff \ + drug_yellow.dff \ + drycleaners.dff \ + dshotgn.dff \ + dt_20_holewall.dff \ + dtbloodring_a.dff \ + dt_bowlsign.dff \ + dt_carpsaces03.dff \ + dt_carpsaces06.dff \ + dt_carpsaces13.dff \ + dt_carpsaces14.dff \ + dt_carpsaces15.dff \ + dt_carpsaces2.dff \ + dt_carspaces1.dff \ + dt_compound_alfas.dff \ + dt_compound_esc.dff \ + dt_compound_roof.dff \ + dtdirtring_a.dff \ + dt_gspot_stairs.dff \ + dt_hotel_waldt.dff \ + dthotring_a.dff \ + dt_marscafe.dff \ + dt_nbeach01.dff \ + dt_nbeach02.dff \ + dt_nbeach03.dff \ + dt_nbeach05.dff \ + dt_nbeach06.dff \ + dt_nbeach07.dff \ + dt_nbeach08.dff \ + dtn_buildnew1.dff \ + dtn_buildnew2.dff \ + dtn_cablesa.dff \ + dtn_cablesb.dff \ + dtn_cablesc.dff \ + dtn_cablesd.dff \ + dtn_cablese.dff \ + dtn_cablesf.dff \ + dtn_comp_polysa.dff \ + dtn_hospital_fence.dff \ + dt_nitelites1.dff \ + dt_nitelites2.dff \ + dt_nitelites3.dff \ + dt_nitelites4.dff \ + dt_nitelites5.dff \ + dt_nitelites6.dff \ + dt_nitelites7.dff \ + dt_nitelites8.dff \ + dtns_kbshop1b.dff \ + dtns_kbshop1.dff \ + dtn_staddoora.dff \ + dtn_staddoorb.dff \ + dtn_stadspacesA.dff \ + dtn_stadspacesB.dff \ + dtn_veg3.dff \ + dtn_veg4.dff \ + dtoon62_spaces.dff \ + dt_roofshit1.dff \ + dt_savedra.dff \ + dt_savedrb.dff \ + dt_savedrc.dff \ + dt_savedrd.dff \ + dts_bbdoor.dff \ + dt_scabby_fence01.dff \ + dt_scabby_fence03.dff \ + dt_scabby_fence04.dff \ + dt_scabby_fence07.dff \ + dts_cop_park.dff \ + dts_glassplane_a.dff \ + dts_gs_liftdoorL.dff \ + dts_gs_liftdoorR.dff \ + dt_sheraton_fence.dff \ + dts_nitelites1.dff \ + dts_offglassa.dff \ + dts_offglassb.dff \ + dts_office2.dff \ + dt_spotlite_01.dff \ + dts_soapytitwank_a.dff \ + dts_soapytitwank_b.dff \ + dts_spot_a.dff \ + dt_stadium__alfa1.dff \ + dt_stadiumalfa2.dff \ + dt_stadium_entrance.dff \ + dt_stadiumland_a.dff \ + dt_stadiumland_b.dff \ + dt_stadiumland_c.dff \ + dt_stadiumland_d.dff \ + dt_stadium_start.dff \ + dts_telwire1.dff \ + dts_telwire2.dff \ + dts_telwire4.dff \ + dts_telwire9.dff \ + dts_tit_stares.dff \ + dt_track_flags01.dff \ + dt_tracktyres_01.dff \ + dt_tracktyres_02.dff \ + dt_tracktyres_03.dff \ + dt_tracktyres_04.dff \ + dt_tracktyres_05.dff \ + dt_tracktyres_06.dff \ + dt_VAJ.dff \ + dump1.dff \ + dwn_bikerbarfrnt.dff \ + dwnmiamilanda.dff \ + dw_scuz_kb.dff \ + dynamite.dff \ + dzplant.dff \ + Electricgate.dff \ + elevator.dff \ + enforcer.dff \ + Esc_step.dff \ + esperant.dff \ + espreso.dff \ + exhaust.dff \ + expltrk.dff \ + faggio.dff \ + faketarget.dff \ + fbicar.dff \ + fbi.dff \ + fbiranch.dff \ + fence01.dff \ + fencehaiti.dff \ + fencehaitism.dff \ + fencesmallb.dff \ + fffinhaiti.dff \ + fingers.dff \ + fire_hydrant.dff \ + fireman.dff \ + firetruk.dff \ + fireyfire01.dff \ + fireyfire.dff \ + fish1s.dff \ + fish1single.dff \ + fish2s.dff \ + fish2single.dff \ + fish3s.dff \ + fish3single.dff \ + fixedbects06.dff \ + flame.dff \ + Flame_tins.dff \ + flatbedbak.dff \ + flatbed.dff \ + floozya.dff \ + floozyb.dff \ + floozyc.dff \ + Frame1Damaged.dff \ + Frame1.dff \ + freeway.dff \ + FSfa.dff \ + fuckedcars01.dff \ + fuckedcars02.dff \ + fuckedcars03.dff \ + fuckedcars04.dff \ + fuckedcars05.dff \ + fuckedcars06.dff \ + gangb.dff \ + gangbur.dff \ + gardenbencha.dff \ + Gasgrenade.dff \ + Gash_bags1.dff \ + gashbags2.dff \ + gate4.dff \ + gate5.dff \ + gcfan.dff \ + Gda.dff \ + Gdb.dff \ + Gdyn_barrier17.dff \ + GeoSphere01.dff \ + gf_clubhouse1.dff \ + gf_clublight1_01.dff \ + gf_drdivide1_01.dff \ + gf_drivingrange1.dff \ + gf_flagstees1.dff \ + gf_flagstees2.dff \ + gf_flagstees3.dff \ + gf_flagstees42.dff \ + gf_flagstees43.dff \ + gf_flagstees4.dff \ + gf_golfwall.dff \ + gf_hedge1_01.dff \ + gf_hedge2_01.dff \ + gf_hedge3_01.dff \ + gf_hedge4_01.dff \ + gf_hedge5_01.dff \ + gf_hedge6_01.dff \ + gf_ivy_balcny_01.dff \ + gf_ivy_balcny_07.dff \ + gf_mainisl2b_01.dff \ + gf_mainisl4_01.dff \ + gf_mainisl5_01.dff \ + gf_mainisland1a_01.dff \ + gf_mainisland1b_01.dff \ + gf_mainisland1m_01.dff \ + gf_mainisland2a_01.dff \ + gf_mainisland3a_01.dff \ + gf_mainisland3b_01.dff \ + gf_mainisland6a_01.dff \ + gf_mainisland6b_01.dff \ + gf_mainisland6n_01.dff \ + gf_northbridge1.dff \ + gf_northisland1b.dff \ + gf_northisland1c.dff \ + gf_northisland1.dff \ + gf_northisland2.dff \ + gf_northisland3.dff \ + gf_pedbridge3.dff \ + gf_picket1_01.dff \ + gf_planters2.dff \ + gf_planters4.dff \ + gf_rollerjump.dff \ + gf_signPlane01.dff \ + gf_smallisland1_01.dff \ + gf_smallislebridge1.dff \ + gf_stadlight1_02.dff \ + gf_tennisfence.dff \ + gf_tree1_01.dff \ + gf_tree2_02.dff \ + gf_treesfw1_01.dff \ + gf_treesfw2_01.dff \ + gf_treesfw3_01.dff \ + gf_treesfw4_01.dff \ + gf_treesfw5_01.dff \ + gf_water01.dff \ + gf_water02.dff \ + gf_water03.dff \ + gf_woodbridge1_01.dff \ + ggneonsign2.dff \ + ggsalonsign1.dff \ + glassfx_composh.dff \ + glendale.dff \ + glue_corner01.dff \ + glue_straight01.dff \ + glue_straight02.dff \ + gluetest03.dff \ + gluetest19.dff \ + golfclub.dff \ + golf_gatesclosed.dff \ + golf_gatesopen.dff \ + golfroad1.dff \ + golfroad2.dff \ + golfroad3.dff \ + golfroad4.dff \ + golfroad5.dff \ + golfroad7.dff \ + gpass.dff \ + grassbitsjm.dff \ + grasspatch.dff \ + greenwoo.dff \ + grenade.dff \ + guesta.dff \ + guestb.dff \ + guestc.dff \ + guestd.dff \ + gunbox.dff \ + guncolt.dff \ + Gun_docs.dff \ + haicoast1.dff \ + haicoast2.dff \ + haicoast3.dff \ + haicoast4.dff \ + haicoast5.dff \ + haicoasta2.dff \ + haicoastfirst.dff \ + haigrnda1.dff \ + hai_houses27.dff \ + hai_roadsigns.dff \ + haitaxidr.dff \ + haitbuildws14.dff \ + haitgangshop.dff \ + haitgangshopdoor.dff \ + HAITI1RD.dff \ + haitibuilds.dff \ + haiticar08.dff \ + haiticar09.dff \ + haiticar10.dff \ + haiticar12.dff \ + haiticar13.dff \ + haiticent.dff \ + haitihut1.dff \ + haitihut2.dff \ + haitihut3.dff \ + haitimall.dff \ + haitinwire11.dff \ + haitinwire22.dff \ + haitinwire23.dff \ + haitiroadbuild.dff \ + haitiskyg.dff \ + haitiskyt.dff \ + haitistation01.dff \ + haititag.dff \ + haitroada.dff \ + haitroadb.dff \ + haitroadc.dff \ + haiwashpaynspray.dff \ + haiwshpnsdoor.dff \ + haiwshpspryneon1.dff \ + haiwsignday_dy.dff \ + haiwsignight_nt.dff \ + hammer.dff \ + hatrifl.dff \ + hatsog.dff \ + havanahouse01.dff \ + havanahouse02.dff \ + havanahouse03.dff \ + havanahouse04.dff \ + havanahouse06.dff \ + havanahouse07.dff \ + havanahouse09.dff \ + havanahouse10.dff \ + havanahouse11.dff \ + havanahouse12b.dff \ + havanahouse12.dff \ + havbackbit.dff \ + havbit01.dff \ + havbit02.dff \ + havbit03.dff \ + havbuild.dff \ + hav_car_show_int.dff \ + hav_car_show_out2.dff \ + hav_car_show_out.dff \ + havgangshop.dff \ + hav_garagedoor02.dff \ + hav_garagedoor03.dff \ + hav_garagedoor04.dff \ + hav_garagedoor1.dff \ + havhotel.dff \ + hdrugfactoryintb.dff \ + hdrugfactoryint.dff \ + hdrugfactorypol.dff \ + health.dff \ + Heli_box.dff \ + Helipad0.dff \ + helix_barrier.dff \ + hermes.dff \ + hfobe.dff \ + hfori.dff \ + HFOST.dff \ + HFOTR.dff \ + hfybe.dff \ + HFYBU.dff \ + HFYCG.dff \ + HFYMD.dff \ + HFYPR.dff \ + HFYRI.dff \ + HFYST.dff \ + hibita1.dff \ + hlrycar.dff \ + hmobe.dff \ + hmoca.dff \ + hmori.dff \ + hmost.dff \ + hmotr.dff \ + hmyap.dff \ + hmybe.dff \ + hmyri.dff \ + hmyst.dff \ + Hna.dff \ + Hnb.dff \ + hogans_alley_b.dff \ + hogans_alley.dff \ + hospital.dff \ + hot_bar1_01.dff \ + hot_drawers1_01.dff \ + hoteltrees01.dff \ + hoteltrees02.dff \ + hoteltrees03.dff \ + hoteltrees05.dff \ + hoteltrees06.dff \ + hot_mags1.dff \ + hotrina.dff \ + hotrinb.dff \ + hotring.dff \ + hot_room317.dff \ + hotroomfan.dff \ + hotshad1.dff \ + hot_trans1.dff \ + htable.dff \ + ht_doors.dff \ + ht_fans_dy.dff \ + ht_fans_nt.dff \ + ht_kb_couch1_dy.dff \ + ht_kb_couch1_nt.dff \ + htl_dco_chair03_dy.dff \ + htl_dco_chair03_nt.dff \ + htl_exterior03_dy.dff \ + htl_exterior03_nt.dff \ + htl_fan_rotate_dy.dff \ + htl_fan_rotate_nt.dff \ + htl_fan_static_dy.dff \ + htl_fan_static_nt.dff \ + htl_gls_1_dy.dff \ + htl_gls_1_nt.dff \ + htl_gls_2_dy.dff \ + htl_gls_2_nt.dff \ + htl_gls_3_dy.dff \ + htl_gls_3_nt.dff \ + htl_gls_lobby.dff \ + htl_lftdoor1_dy.dff \ + htl_lftdoor1_nt.dff \ + htl_maintiles_dy.dff \ + htl_maintiles_nt.dff \ + ht_mainfloor2_dy.dff \ + ht_mainfloor2_nt.dff \ + ht_mainfloor_dy.dff \ + ht_mainfloor_nt.dff \ + htnroadnew.dff \ + ht_upstairs.dff \ + ht_veg01_dy.dff \ + ht_veg01_nt.dff \ + ht_veg02_dy.dff \ + ht_veg02_nt.dff \ + ht_veg04_dy.dff \ + ht_veg04_nt.dff \ + hunter.dff \ + hvood01.dff \ + hvood.dff \ + hvoodext.dff \ + icedoor.dff \ + idaho.dff \ + IGalscb.dff \ + IGbuddy.dff \ + IGBudy2.dff \ + IGBudy3.dff \ + IGCandy.dff \ + IGColon.dff \ + IGDiaz.dff \ + IGDick.dff \ + IGGonz.dff \ + IGHlary.dff \ + IGHlry2.dff \ + IGJezz.dff \ + IGKen.dff \ + IGMerc2.dff \ + IGMerc.dff \ + IGMike2.dff \ + IGmike.dff \ + IGPercy.dff \ + IGphil2.dff \ + IGPhil3.dff \ + IGPhil.dff \ + IGSonny.dff \ + ihaitsnip.dff \ + ihaitwarhus.dff \ + ih-sjmcrush.dff \ + ih-sjmphil.dff \ + infernus.dff \ + info.dff \ + ingramsl.dff \ + instruments.dff \ + invite.dff \ + IslandLOD1.dff \ + IslandLOD2.dff \ + IslandLODbeach.dff \ + IslandLODmainland.dff \ + jellyfish01.dff \ + jellyfish.dff \ + jetmax.dff \ + jewler_opac_stf.dff \ + jewler_shop.dff \ + jfoto.dff \ + jmoto.dff \ + jump_box_02.dff \ + jump_box108.dff \ + jump_box112.dff \ + jump_box113.dff \ + jump_box114.dff \ + jump_box115.dff \ + junk_tyre.dff \ + jw_coffin.dff \ + jzgirla.dff \ + jzgirlb.dff \ + katana.dff \ + kaufman.dff \ + kb_canopy_test.dff \ + kb_chr_tbl_test.dff \ + kb_couch06.dff \ + kb_couch09.dff \ + kb_couch15.dff \ + kb_planterbox.dff \ + kb_planter+bush2.dff \ + kb_planterbush2.dff \ + kb_planter+bush.dff \ + kb_pot_1.dff \ + kentpaul.dff \ + kettle.dff \ + keycard.dff \ + KgoonA.dff \ + KgoonB.dff \ + kickbannerside4.dff \ + kickbannerstart01.dff \ + kickbus03.dff \ + kickbus04.dff \ + kickbus05.dff \ + kickcar06.dff \ + kickcar17.dff \ + kickcar23.dff \ + kickcar28.dff \ + kickcar36.dff \ + kickcar3.dff \ + kickdetail2.dff \ + kickdetail.dff \ + kickdrums05.dff \ + kickdrums07.dff \ + kickdrums16.dff \ + kickdrums1.dff \ + kickdrums2.dff \ + kickfence.dff \ + kickhay1.dff \ + kickhay2.dff \ + kickhay3.dff \ + kickobs02.dff \ + kickobs1.dff \ + kickramp02.dff \ + kickramp03.dff \ + kickramp04.dff \ + kickramp1.dff \ + kickstart.dff \ + kicktyres03.dff \ + kicktyres2.dff \ + kicktyres.dff \ + killfrenzy.dff \ + knifecur.dff \ + labiggrass01.dff \ + labiggrass02.dff \ + labiggrass.dff \ + lampost_coast.dff \ + lamppost1.dff \ + lamppost2.dff \ + lamppost3.dff \ + landjump2.dff \ + landjump.dff \ + landstal.dff \ + laser.dff \ + lawcutmapa.dff \ + lawcutmapb.dff \ + lawcutmapc.dff \ + lawdoor.dff \ + lawyer.dff \ + lcfan.dff \ + lf_banner.dff \ + lf_mediastage.dff \ + lh2glue01.dff \ + lh2glue.dff \ + lha_carfence.dff \ + lhaitcut.dff \ + lhaitnewbt.dff \ + lharoadab.dff \ + lhavnew_bush.dff \ + lhavroad1.dff \ + lhavroad2.dff \ + lhbacknbit.dff \ + lhbasket.dff \ + lhbillboard05xx.dff \ + lhblbrd1.dff \ + lhblbrd2.dff \ + lhblbrd3.dff \ + lhblbrd8.dff \ + lh_boxgirdbridge.dff \ + lhbuild034.dff \ + lhbuild1b.dff \ + lh_buildingg1.dff \ + lh_buildingg2.dff \ + lh_buildingg3.dff \ + lh_buildingw1.dff \ + lhcargo.dff \ + lh_carshow_windows.dff \ + lhcoastsky1.dff \ + lhcoastsky2.dff \ + lh_crushefuck.dff \ + lh_crushers08.dff \ + lh_crushers09.dff \ + lh_crushers10.dff \ + lh_crushers11.dff \ + lh_crushers12.dff \ + lh_crushers13.dff \ + lh_crushers14.dff \ + lh_crushers15.dff \ + lhddirt.dff \ + lhdirt2s.dff \ + lhdirtpass1.dff \ + lhdirtpassax1.dff \ + lhdirtpassz.dff \ + lh_dockposts1.dff \ + lh_dockposts2.dff \ + lh_dockposts3.dff \ + lh_dockpostsN1.dff \ + lh_dockpostsN2.dff \ + lh_dockpostsN3.dff \ + lhglue01.dff \ + lhglue3.dff \ + lhgrasspatchg.dff \ + lhgrnda1.dff \ + lh_groundstains.dff \ + lh_haiblockc1.dff \ + lh_haiblockc2.dff \ + lh_haiblockc3.dff \ + lh-hitbuild1aa.dff \ + lh_hitbuild1a.dff \ + lh_icecream01.dff \ + lh_icecream02.dff \ + lh_icedoor.dff \ + lh_imp_exp01.dff \ + lh_imp_exp02.dff \ + lh_imp_exp03.dff \ + lh_imp_exp04.dff \ + lhjetty.dff \ + lh_misc02.dff \ + lhouse_barrier1.dff \ + lhouse_barrier2.dff \ + lhouse_barrier3.dff \ + lhparkline2.dff \ + lh_phils.dff \ + lh_philsext.dff \ + lh_philsint.dff \ + lh_pianoshop.dff \ + lh_pizzaplaceb.dff \ + lh_pizzaplace.dff \ + lh_race01.dff \ + lh_race02.dff \ + lh_race03.dff \ + lh_race04.dff \ + lh_race05.dff \ + lh_race06.dff \ + lh_racemap.dff \ + lhroadc1.dff \ + lhroadc2.dff \ + lhroadc3.dff \ + lhroadc4.dff \ + lhroofst01.dff \ + lhroofst02.dff \ + lhroofst03.dff \ + lhroofst04.dff \ + lhroofst07.dff \ + lhroofst08.dff \ + lhroofst09.dff \ + lhroofst10.dff \ + lhroofst11.dff \ + lhroofst12.dff \ + lhroofst14.dff \ + lhroofst16.dff \ + lhroofst17.dff \ + lhroofst18.dff \ + lhroofst19.dff \ + lhroofstuffg.dff \ + lhroofwee.dff \ + lhsbackbit.dff \ + lh_showdoor03.dff \ + lh_showdoor1.dff \ + lhstairs7.dff \ + lhsteps3.dff \ + lh_strikethrough.dff \ + lhtankdoor.dff \ + lh_taxifirm01.dff \ + lh_taxifirm02.dff \ + lhwallbit.dff \ + lihawasteb2.dff \ + line.dff \ + linerun.dff \ + lithahousebk2.dff \ + litharoada2.dff \ + litharoada3.dff \ + litharoada4.dff \ + litharoada6.dff \ + litharoada8.dff \ + litharoada9.dff \ + lithavabit01.dff \ + lithavabit03.dff \ + lithavabit04.dff \ + lithavabit05.dff \ + lithavabit06.dff \ + lithavabit07.dff \ + lithavabit08.dff \ + lithavabit09.dff \ + lithavabit10.dff \ + lithavabit11.dff \ + lithavabit12.dff \ + lithavroad3.dff \ + lithawaste1.dff \ + lithawaste2.dff \ + lithit14.dff \ + lithitsjm.dff \ + litlhaitibuilding02.dff \ + litlhaitibuilding06.dff \ + litlhaitibuilding07.dff \ + litlhaitibuilding09.dff \ + littlehabroad.dff \ + littlehacoast02.dff \ + littlehacoast05.dff \ + littleha_hardwareb.dff \ + littleha_hardware.dff \ + littleha_police.dff \ + littlharoadgg2.dff \ + lobster.dff \ + lobtray.dff \ + LODachb1.dff \ + LODachb.dff \ + LODachbit02.dff \ + LODachbit03.dff \ + LODachbit04b.dff \ + LODachbit04.dff \ + LODachbit05.dff \ + LODachbit06.dff \ + LODach_police.dff \ + LODachroads01.dff \ + LODachroads02.dff \ + LODachroads03.dff \ + LODachroads09.dff \ + LODachsand1.dff \ + LODachsand2b.dff \ + LODachsand2.dff \ + LODachwjetty01.dff \ + LODachwjetty02.dff \ + LODacne.dff \ + LODallbit.dff \ + lodalleys1_01_dy.dff \ + lodalleys1_01_nt.dff \ + lodalleys1b_01_dy.dff \ + lodalleys1b_01_nt.dff \ + lodalleys2_01_dy.dff \ + lodalleys2_01_nt.dff \ + lodalleys3_01_dy.dff \ + lodalleys3_01_nt.dff \ + lodalleys3b_01_dy.dff \ + lodalleys3b_01_nt.dff \ + LODallroof01.dff \ + lodanahouse01.dff \ + lodanahouse02.dff \ + lodanahouse03.dff \ + lodanahouse04.dff \ + lodanahouse06.dff \ + lodanahouse07.dff \ + lodanahouse09.dff \ + lodanahouse10.dff \ + lodanahouse11.dff \ + LODanahouse12.dff \ + LODanrd1_dy.dff \ + LODanrd1_nt.dff \ + LODanrda03_dy.dff \ + LODanrda03_nt.dff \ + LODanrda04_dy.dff \ + LODanrda04_nt.dff \ + LODanrda05_dy.dff \ + LODanrda05_nt.dff \ + LODanrda06_dy.dff \ + LODanrda06_nt.dff \ + LODanroad04.dff \ + LODanroad05.dff \ + LODanrod2b_dy.dff \ + LODanrod2b_nt.dff \ + LODargohull2.dff \ + lodasket.dff \ + LODastage.dff \ + LODastageint.dff \ + LODavehotel.dff \ + lodawasteb2.dff \ + lodawyersfront.dff \ + lodbackbit.dff \ + LODbacklot1.dff \ + lod_backside.dff \ + LOD_beacon2_dy.dff \ + LOD_beacon2_nt.dff \ + LOD_beacon_dy.dff \ + LOD_beacon_nt.dff \ + lodbighotel_dy.dff \ + lodbighotel_nt.dff \ + lodbighotpool.dff \ + LODbigshops1.dff \ + LODbigshops2.dff \ + LODbillboards1.dff \ + LODbillboards2.dff \ + LODbillboards3.dff \ + LODbillboards4.dff \ + LODbillboards5.dff \ + lodbit02.dff \ + lodbit03.dff \ + LODbowlsign.dff \ + LODboxgirdbridge.dff \ + LODbphq.dff \ + LODbridgesupp01.dff \ + LODbridgesupp03.dff \ + LODbridgesupp04.dff \ + LOD_bridgew.dff \ + LODbstage.dff \ + LODbstageint.dff \ + lodbuil19wall.dff \ + lod_build01.dff \ + LODbuild01.dff \ + lod_build02.dff \ + LODbuild02.dff \ + lod_build03.dff \ + lod_build04.dff \ + lod_build05.dff \ + lodbuild.dff \ + LODbuilding01.dff \ + LODbuildingg1.dff \ + LODbuildingg2.dff \ + LODbuildingg3.dff \ + LODbuildingw1.dff \ + LODbuildkb23_dy.dff \ + LODbuildkb23_nt.dff \ + LODbuildkb27_dy.dff \ + LODbuildkb27_nt.dff \ + LODbuildkb2_dy.dff \ + LODbuildkb2_nt.dff \ + LOD_buildnew1.dff \ + LOD_buildnew2.dff \ + LODbuildws02.dff \ + LODbuildws03.dff \ + LODbuildws04.dff \ + LODbuildws05.dff \ + LODbuildws06.dff \ + LODbuildws07.dff \ + LODbuildws08.dff \ + LODbuildws09.dff \ + LODbuildws10.dff \ + LODbuildws12.dff \ + LODbuildws13.dff \ + LODbuildws14.dff \ + LODbuildws16.dff \ + LODbuildws17.dff \ + LODbuildws18.dff \ + LODbuildws19.dff \ + LODbuildws20.dff \ + LODbuildws21.dff \ + lodbuildws22.dff \ + lodbuildws2326.dff \ + LODbuildws2329.dff \ + LODbuildws23.dff \ + LODbuildws24.dff \ + LODbuildws25.dff \ + lodbuildws264.dff \ + LODbuildws26.dff \ + LODbuildws27.dff \ + LODbuildws28.dff \ + LODbuildws29.dff \ + LODbuildws30.dff \ + lodbuildws312.dff \ + lodbuildws31.dff \ + lodbuildws322.dff \ + lodbuildws32.dff \ + lodbuildws332.dff \ + lodbuildws33.dff \ + LODbuildws35.dff \ + LODbuildws36.dff \ + LODbuildws40.dff \ + LODbuildws41.dff \ + lodbuildws42.dff \ + LODbuildws43.dff \ + lodbuildws43ins.dff \ + LODbuildws44.dff \ + LODbuildws45.dff \ + LODcargoshp03.dff \ + LODcargoshp04.dff \ + LODcargoshp05.dff \ + LODcargoshp24.dff \ + LODcargoshp25.dff \ + LODcargoshp28.dff \ + LODcargoshp32.dff \ + LODcargoshp35.dff \ + LODcargoshp40.dff \ + LODcargoshp41.dff \ + LODcargoshp47.dff \ + LODcargoshp50.dff \ + LODcargoshp51.dff \ + LODcargoshp64.dff \ + LODcargoshp70.dff \ + LODcargoshp71.dff \ + LODcargoshp72.dff \ + LODcargoshp73.dff \ + LODcarparkterm1.dff \ + LOD_car_show_out.dff \ + LODcasagrande_dy.dff \ + LODcasagrande_nt.dff \ + lodcbtwbrdge.dff \ + LODchariot.dff \ + lodchariot_lghts01.dff \ + lodchariot_lghts02.dff \ + LODchbta.dff \ + LODchbtb2.dff \ + LODchbtb.dff \ + LODchbt.dff \ + LODchlo1.dff \ + LODcland01.dff \ + LODcland03.dff \ + LODcland05.dff \ + LODcland09.dff \ + LODcland10.dff \ + LODcland11.dff \ + LODcland12.dff \ + LODcland13_gf.dff \ + LODclevelander_dy.dff \ + LODclevelander_nt.dff \ + LODclubback.dff \ + LODclubhouse1.dff \ + LODclubout_dy.dff \ + LODclubout_nt.dff \ + LODcoast04.dff \ + LODcoast06.dff \ + LODcoast08.dff \ + LODcoast1.dff \ + LODcoast2.dff \ + LODcoast3.dff \ + LODcoast4.dff \ + LODcoast5.dff \ + LODcoasta2.dff \ + LODcoast.dff \ + LODcoastfirst.dff \ + LODcolony2_dy.dff \ + LODcolony2_nt.dff \ + LOD_colony_dy.dff \ + LODcolony_dy.dff \ + LOD_colony_nt.dff \ + LODcolony_nt.dff \ + LODcompound_esc.dff \ + LODcompound_roof.dff \ + LODconcwall1.dff \ + lodcpaynspray.dff \ + lodcrushbn.dff \ + LODcrushefuck.dff \ + LODcrushers08.dff \ + LODcrushers10.dff \ + LODcrushers12.dff \ + LODcrushers14.dff \ + lodcrushju.dff \ + lodcrushmnf.dff \ + LODcstage.dff \ + LODcstageint.dff \ + LODdbridge.dff \ + lodd_build2.dff \ + LODd_buildnew.dff \ + LODd_downtown02.dff \ + LODd_downtown03.dff \ + LODd_downtown04.dff \ + LODd_downtown05.dff \ + LODd_downtown06.dff \ + LODd_downtown07.dff \ + LODd_downtown08.dff \ + LODd_downtown09.dff \ + LODd_downtown10.dff \ + LODd_downtown11.dff \ + LODd_downtown12.dff \ + LODd_downtown13.dff \ + LODd_downtown14.dff \ + LODd_downtown15.dff \ + LODd_downtown16.dff \ + LODd_downtown17.dff \ + LODd_downtown_new1.dff \ + LODd_downtown_new2.dff \ + LODdingsite1.dff \ + LODdockbridge.dff \ + LODdockroads01.dff \ + LODdockroads02.dff \ + LODdockroads03.dff \ + LODdockroads04.dff \ + LODdockroads05.dff \ + LODdockroads06.dff \ + LODdockroads08.dff \ + LODdockwall1.dff \ + LOD_dockwareold.dff \ + LODdrivingrange1.dff \ + LODd_stadiuma.dff \ + LODdstageext.dff \ + LODecopromnd2.dff \ + LODecopromnd.dff \ + LODecoshop01.dff \ + LODecoshop02.dff \ + LODecoshop03.dff \ + LODer2.dff \ + LODextertank.dff \ + LODfiretrucks.dff \ + LODflargetank1.dff \ + LODflyingschool1.dff \ + LODfreightterminal1.dff \ + lod_frntstep.dff \ + LODfroad1.dff \ + LODfroad2.dff \ + LODfroad3.dff \ + LODfroad4.dff \ + LODfroad5.dff \ + LODfsthus.dff \ + lodgarage02.dff \ + lodgarage03.dff \ + lodgarage1.dff \ + LODgarden1.dff \ + LODgate03.dff \ + LODgate1.dff \ + LODgate2.dff \ + LODgatesA.dff \ + LODgatesB.dff \ + LODgolfwall.dff \ + LOD_grnd2.dff \ + LOD_grnd3.dff \ + LOD_grnd4.dff \ + LOD_grnd5.dff \ + LOD_grnd6.dff \ + LODgrnda1.dff \ + LODground1.dff \ + LODground2.dff \ + LODground3.dff \ + LODground4.dff \ + LODground5.dff \ + LODgroundplane.dff \ + LOD_grssypatch.dff \ + LODgstoreext.dff \ + LODhafinrd.dff \ + LODhahousebk2.dff \ + LODhaiblockc1.dff \ + LODhaiblockc2.dff \ + LODhaiblockc3.dff \ + lodh_ammu.dff \ + LODhangar1_01.dff \ + LODhangar1_04.dff \ + LODhangar1.dff \ + LODharoada2.dff \ + LODharoada3.dff \ + LODharoada4.dff \ + LODharoada6.dff \ + LODharoada8.dff \ + LODharoada9.dff \ + lodhavabit01.dff \ + lodhavabit04.dff \ + lodhavabit05.dff \ + lodhavabit06.dff \ + lodhavabit07.dff \ + lodhavabit08.dff \ + lodhavabit09.dff \ + lodhavabit10.dff \ + lodhavabit11.dff \ + lodhavabit12.dff \ + LODhavroad3.dff \ + lodhawaste1.dff \ + lodhawaste2.dff \ + LODhbit04bgrd1.dff \ + LODhbit04grd2.dff \ + LODh_brdgsup1.dff \ + LODhbtjetty01.dff \ + LODhbtjetty02.dff \ + LODhbuild003.dff \ + LODhbuild005.dff \ + LODhbuild013.dff \ + LODhbuild014.dff \ + LODhbuild015.dff \ + LODhbuild017.dff \ + LODhbuild018.dff \ + LODhbuild020.dff \ + LODhbuild021.dff \ + LODhbuild025.dff \ + LODhbuild030.dff \ + LODhbuild033.dff \ + LODhbuild040.dff \ + lodhbuild043.dff \ + LODhbuild050.dff \ + LODhbuild058.dff \ + LODhbuild062.dff \ + LODhbuild063.dff \ + LODhbuild066.dff \ + LODhbuild068.dff \ + LODhbuild069.dff \ + LODhbuild071.dff \ + LODhbuild072.dff \ + LODhbuild073.dff \ + LODhbuild074.dff \ + LODhbuild075.dff \ + lodhbuild0812.dff \ + lodhbuild081.dff \ + LODhbuild111.dff \ + LODhbuild112.dff \ + lodhbuild114.dff \ + LODhbuild116.dff \ + LODhbuild119.dff \ + LODhbuild120.dff \ + LODhbuild121.dff \ + LODhbuild184.dff \ + LODhbuild187.dff \ + lodhbuild192.dff \ + lodhbuild195.dff \ + LODhbuild198.dff \ + lodhbuild203.dff \ + lodhbuild213.dff \ + lodhbuild214.dff \ + lodhbuild215.dff \ + lodhbuild216.dff \ + LODh_deco01.dff \ + LODh_deco02.dff \ + LODh_deco03.dff \ + LODh_deco04.dff \ + LODh_deco05.dff \ + LODh_hardwares.dff \ + LODh_hosp02.dff \ + LODh_hosp03.dff \ + LODh_hospgrnd.dff \ + LODh_hospital.dff \ + LODhipment01.dff \ + LODhipment03.dff \ + LODhipment05.dff \ + LODhit14.dff \ + LODhitbuild1aa.dff \ + LODhitbuild1a.dff \ + LODhitsjm.dff \ + LODhland_01.dff \ + LODhnewsky1.dff \ + LODhnewsky2.dff \ + LODhnewsky3.dff \ + LODhookerinn1.dff \ + LODhoose.dff \ + LOD_hotel01.dff \ + LOD_hotel02.dff \ + LOD_hotel03.dff \ + LOD_hotel04.dff \ + LOD_hotel05.dff \ + LOD_hotel06.dff \ + LOD_hotel07.dff \ + LOD_hotel08.dff \ + LODhotel10.dff \ + LOD_hotel13.dff \ + LOD_hotel15.dff \ + LODhotel.dff \ + LODhotelgrnd.dff \ + LOD_hotelhot_dy.dff \ + LOD_hotelhot_nt.dff \ + LODhotel_waldt.dff \ + lodhotel_windows.dff \ + LOD_houses27.dff \ + lodhpaynspray.dff \ + LODh_pizzaplace.dff \ + LODhpshoutdet.dff \ + lodhsebuild1.dff \ + LOD_htlpoolbar01.dff \ + LOD_htlpoolbase01b.dff \ + LOD_htlpoolbase01.dff \ + LOD_htlpoolbase02.dff \ + LOD_htlpoolbase03.dff \ + LOD_htlpoolbase04.dff \ + LOD_htlpoolrm01.dff \ + LOD_htlpoolrm02.dff \ + LODhtooceanrd1.dff \ + LODhundermall1.dff \ + LODhwbridge.dff \ + LODiamiland048.dff \ + LODiamiland049.dff \ + LODiamiland050.dff \ + LODiamiland051.dff \ + LODiamiland052.dff \ + LODiamiland053.dff \ + LODiamiland054.dff \ + LODiamiland055.dff \ + LODiamiland056.dff \ + LODiamiland057.dff \ + LODiamiland058.dff \ + LODiamiland059.dff \ + LODiamiland060.dff \ + LODiamiland061.dff \ + LODiamiland063.dff \ + LODiamiland065.dff \ + LODiamiland068.dff \ + LODicecream01.dff \ + lodi_exterior_front.dff \ + lodighsandgrs1.dff \ + LODigste1mesh.dff \ + LODina1.dff \ + LODina2.dff \ + LODina3.dff \ + LODindustrial1.dff \ + LODinhaiti.dff \ + LODipad0.dff \ + LODislandeast.dff \ + LODislandnorth.dff \ + LODislandwest.dff \ + LODita1.dff \ + LODitcut.dff \ + LODitnewbt.dff \ + LODitsnip.dff \ + LODitwarhus.dff \ + LODjumbo_01.dff \ + LODk_camjones.dff \ + LODkcranescale01.dff \ + LODkcranescale0.dff \ + LODkfuel02.dff \ + LODk_grassarea.dff \ + LODkhus.dff \ + LODks10.dff \ + LODks21.dff \ + LODks28.dff \ + LODks29.dff \ + LODks30.dff \ + LODks31.dff \ + LODks32.dff \ + LODks37.dff \ + LODks40.dff \ + LODks42.dff \ + LODks43.dff \ + LODks46.dff \ + LODks47.dff \ + LODks48.dff \ + LODks49.dff \ + LODks50.dff \ + LODks51.dff \ + LODks52.dff \ + LODks53.dff \ + LODks60.dff \ + LODks61.dff \ + LODks62.dff \ + LODks85.dff \ + LODks92.dff \ + LODks93.dff \ + LODks95.dff \ + LODks96.dff \ + LODksprops04.dff \ + LODksprops10.dff \ + LODksprops11.dff \ + LODksprops14.dff \ + LODksprops15.dff \ + LODksware01.dff \ + LODl2.dff \ + LODlargebuild.dff \ + LODlBd1.dff \ + LODlBd2.dff \ + LODlBd3.dff \ + LODld1.dff \ + LODldblown.dff \ + LODldingground40.dff \ + LODldingground50.dff \ + LODldingsite2.dff \ + LOD_leslie_dy.dff \ + LOD_leslie_nt.dff \ + LODlhaitibuilding02.dff \ + LODlhaitibuilding06.dff \ + lodlhaitibuilding07.dff \ + LODlhaitibuilding09.dff \ + LODlhland.dff \ + LODlightext.dff \ + LODlleygb_dy.dff \ + LODlleygb_nt.dff \ + LODlleyground2.dff \ + LODlleyground4.dff \ + LODlleyground5.dff \ + LODlleyground.dff \ + LODlloutside.dff \ + LOD_macalpin_dy.dff \ + LOD_macalpin_nt.dff \ + lodmain_body.dff \ + LODmainisl2b_01.dff \ + LODmainisl4_01.dff \ + LODmainisl5_01.dff \ + LODmainisland1a_01.dff \ + LODmainisland1b_01.dff \ + LODmainisland2a_01.dff \ + LODmainisland3a_01.dff \ + LODmainisland3b_01.dff \ + LODmainisland6a_01.dff \ + LODmainisland6b_01.dff \ + LOD_majest_dy.dff \ + LODmajestic2c_dy.dff \ + LODmajestic2c_nt.dff \ + LOD_majestic2x_dy.dff \ + LOD_majestic2x_nt.dff \ + LOD_majestic_dy.dff \ + LOD_majestic_nt.dff \ + LOD_majesticz_dy.dff \ + LOD_majesticz_nt.dff \ + LOD_majest_nt.dff \ + LODmallcp_01.dff \ + LODmallcp_02.dff \ + LODmallcp_03.dff \ + LODmallcp_04.dff \ + LODmallcp_05.dff \ + lodmalsign1.dff \ + lodmalsign2.dff \ + lodmalsign3.dff \ + lodmalsign4.dff \ + lodmalsign5.dff \ + LOD_mansion01.dff \ + LOD_mansion02.dff \ + LOD_mansion03.dff \ + LOD_mansion04.dff \ + LODmansion1.dff \ + LODmansion2.dff \ + LODmansion3.dff \ + LOD_mansionbase01.dff \ + LOD_mansionbase02.dff \ + LODmarscafe.dff \ + LODmiamilanda.dff \ + LODmiland027a.dff \ + LODmiland037.dff \ + LODmiland039.dff \ + LODmiland041.dff \ + LODmiland170.dff \ + lodmiland171.dff \ + lodmiland172.dff \ + LODmiland173.dff \ + LODmiland174.dff \ + LODmiland175.dff \ + LODmiland176.dff \ + LODmiland177.dff \ + LODmiland178.dff \ + LODmiland179.dff \ + LODmiland180.dff \ + LODmiland_kb01_dy.dff \ + LODmiland_kb01_nt.dff \ + lodmiland_kb02.dff \ + LODmiland_kb03.dff \ + LODmiland_kb04.dff \ + LODmiland_kb10b.dff \ + lodmiland_kb10.dff \ + LODmiland_kb11b.dff \ + LODmiland_kb11.dff \ + LODmiland_kb12b.dff \ + LODmiland_kb12.dff \ + LODmiland_kb13.dff \ + LODmiland_ws04b.dff \ + LODmiland_ws04.dff \ + lodmiodnroadxa.dff \ + LODmrgbase1.dff \ + LODmrgbuild1.dff \ + LODmrgbuild2.dff \ + LODmrgtrees4.dff \ + LODnbbridge1.dff \ + LODnbeach01.dff \ + LODnbeach02.dff \ + LODnbeach03.dff \ + LODnbeach05.dff \ + LODnbeach06.dff \ + LODnbeach07.dff \ + LODnbeach08.dff \ + LODnbwroad01.dff \ + LODnbwroad02.dff \ + LODnbwroad03.dff \ + LODnbwroad04.dff \ + LODnbwroad05.dff \ + LODnbwroad06.dff \ + LODnbwroad07.dff \ + LODnbwroad08.dff \ + LODnd04.dff \ + LODnebasea0.dff \ + LODnetopa0.dff \ + LODnewscafe_dy.dff \ + LODnewscafe_nt.dff \ + LODngst2mesh.dff \ + LODnorthbridge1.dff \ + LODnorthisland1.dff \ + LODnorthisland2.dff \ + LODnorthisland3.dff \ + lodnorthstar.dff \ + lodnrthroad01.dff \ + lodnrthroad02.dff \ + lodnrthroad03.dff \ + lodnrthroad04.dff \ + lodnrthroad05.dff \ + lodnrthroad06.dff \ + lodnrthroad07.dff \ + lodnrthroad08.dff \ + lodnrthroad09.dff \ + lodnrthroad10.dff \ + lodnrthroad11.dff \ + lodnrthroad12.dff \ + lodnrthroad13.dff \ + lodnrthroad14.dff \ + lodnrthroad15.dff \ + lodnrthroad16.dff \ + lodnrthroad17.dff \ + lodnrthroad18.dff \ + lodnrthroad19.dff \ + lodnrthroad20.dff \ + lodnrthroad21.dff \ + lodnrthroad22.dff \ + lodnrthroad23.dff \ + lodnrthroad24.dff \ + lodnrthroad25.dff \ + lodnrthroad26.dff \ + lodnrthroad27.dff \ + lodnrthroad28.dff \ + LODntoon03.dff \ + LODntoon_04_bit2.dff \ + LODntoon04.dff \ + LODntoon09.dff \ + LODntoon10.dff \ + LODntoon11.dff \ + LODntoon13.dff \ + LODntoon14.dff \ + LODntoon16.dff \ + LODntoon18.dff \ + LODntoon19.dff \ + LODntoon20.dff \ + LODntoon21.dff \ + LODntoon22.dff \ + LODntoon23.dff \ + LODntoon24.dff \ + LODntoon25.dff \ + LODntoon26.dff \ + LODntoon27.dff \ + LODntoon28.dff \ + LODntoon29.dff \ + LODntoon30.dff \ + LODntoon31.dff \ + LODntoon32.dff \ + LODntoon34.dff \ + LODntoon35.dff \ + LODntoon36.dff \ + LODntoon37.dff \ + LODntoon38.dff \ + LODntoon39.dff \ + LODntoon40.dff \ + LODntoon41.dff \ + LODntoon44.dff \ + LODntoon45.dff \ + LODntoon47.dff \ + LODntoon49.dff \ + LODntoon50.dff \ + LODntoon51.dff \ + LODntoon53.dff \ + LODntoon54.dff \ + LODntoon55.dff \ + LODntoon56.dff \ + LODntoon58.dff \ + LODntoon59.dff \ + LODntoon61.dff \ + LODntoon62.dff \ + LODntoon63.dff \ + LODntoon65.dff \ + LODntoon66.dff \ + LODntoon67.dff \ + LODntoon70.dff \ + LODntoon72b.dff \ + LODntoon72.dff \ + LODntoon73b.dff \ + LODntoon73.dff \ + LODntoon74.dff \ + LODntoon76.dff \ + LODntoon77.dff \ + LODntoon_newbit.dff \ + LODntoon_shops.dff \ + LODntoon_top01.dff \ + LODntoon_top02.dff \ + LODntoon_top03.dff \ + LODntoon_top04.dff \ + LODntoon_top05.dff \ + LODntoon_top06.dff \ + LODntoon_top07.dff \ + LODntoon_top08.dff \ + LODntoon_top09.dff \ + LODntoon_top10.dff \ + LODntoon_top_bb.dff \ + LODoada1.dff \ + LODoadc1.dff \ + LODoadc2.dff \ + LODoadc3.dff \ + LODoadc4.dff \ + LODoadnew1.dff \ + LODoastsky1.dff \ + LODoastsky2.dff \ + lodo_buildkb09.dff \ + LODo_buildkb25.dff \ + LODo_buildkb29_dy.dff \ + LODo_buildkb29_nt.dff \ + LODo_buildkb_dy.dff \ + LODo_buildkb_nt.dff \ + LODoceanrdN1_dy.dff \ + LODoceanrdN1_nt.dff \ + LODo_copgrnd.dff \ + LODoffices.dff \ + LODo_polbuild.dff \ + LODo_polgrnda10.dff \ + LODo_polgrnda12.dff \ + LODo_polgrnda13.dff \ + LODo_polgrnda14.dff \ + LODo_polgrnda16.dff \ + LODo_polgrnda17.dff \ + LODo_polgrnda7.dff \ + LODo_polgrnda8.dff \ + LODo_polgrnda9.dff \ + LODorthmallext1.dff \ + LODorthmalllftex.dff \ + LODoshp010.dff \ + LODospgrnd.dff \ + LODotel01.dff \ + LODotel02.dff \ + LODotel03.dff \ + LODotel04.dff \ + LODotel05.dff \ + LODotel06.dff \ + LODotel07.dff \ + LODotel08.dff \ + lodotelsign35.dff \ + lodowsand1.dff \ + lodowsand2.dff \ + lodpad_grdn_2.dff \ + LODpaynspray.dff \ + LODpedbridge3.dff \ + LODpelican2_dy.dff \ + LODpelican2_nt.dff \ + LODpelican_dy.dff \ + LODpelican_nt.dff \ + LODpfronts03.dff \ + LODphils.dff \ + LODpital.dff \ + LOD_pizzaplace.dff \ + LODpizzaplace.dff \ + LOD_pizzaplacedt.dff \ + LODport_carpark0.dff \ + LODpound_bits.dff \ + LODprintworks.dff \ + LODracecotop2.dff \ + LODracecotop.dff \ + LODrbits05.dff \ + LODrbits06.dff \ + LODrbits13b.dff \ + LODrbits13c.dff \ + LODrbits13.dff \ + LODrbits17.dff \ + LODrbits22.dff \ + LODrbits24.dff \ + LODrbits45.dff \ + LODrbits46.dff \ + LODrbits49.dff \ + LODrbits50.dff \ + LODrbits51.dff \ + LODrbits52.dff \ + LODrbits54.dff \ + LODrbits55.dff \ + LODrbits56.dff \ + LODrbits57.dff \ + LODrbits58.dff \ + LODrbits59.dff \ + LODrbl02b.dff \ + LODrblocks02.dff \ + LODrblocks03.dff \ + LODrblocks04.dff \ + LODrbridge2.dff \ + LODrdepot.dff \ + lodrentrance02.dff \ + lodrentrance1.dff \ + LODrhouse.dff \ + LODrisland_road1.dff \ + LODrisland_road2.dff \ + LODrisland_road3.dff \ + LODrisland_road4.dff \ + LODrisland_road6.dff \ + LODrland01.dff \ + LODrland02.dff \ + LODrland03.dff \ + LODrland04.dff \ + LODrland05.dff \ + LODrland06.dff \ + LODrland07.dff \ + LODrland08.dff \ + LODrland10.dff \ + LODrlandnew9.dff \ + lodrnda1.dff \ + LOD_road01.dff \ + LODroad01.dff \ + lodroad01_dy.dff \ + lodroad01_nt.dff \ + LOD_road02.dff \ + LODroad02.dff \ + LOD_road03.dff \ + LODroad03.dff \ + LODroad03od.dff \ + LOD_road04.dff \ + LODroad04.dff \ + LODroad04od_dy.dff \ + LODroad04od_nt.dff \ + LOD_road05.dff \ + LODroad05.dff \ + LODroad05od.dff \ + LOD_road06.dff \ + LODroad06.dff \ + LOD_road07.dff \ + LODroad07.dff \ + LOD_road08.dff \ + LODroad08.dff \ + LODroad08_dy.dff \ + lodroad08_nt.dff \ + LOD_road09.dff \ + LODroad09.dff \ + LOD_road10.dff \ + LODroad10.dff \ + LOD_road11.dff \ + LODroad11.dff \ + LODroad12.dff \ + LOD_road12_gf.dff \ + LODroad13.dff \ + LODroad14.dff \ + LODroad15.dff \ + LODroadab.dff \ + LODroadnew.dff \ + LODroadsect1.dff \ + LODroadsect2a.dff \ + LODroadsect2b.dff \ + LODroadsect3.dff \ + LODroadsect4.dff \ + LODroadsect5.dff \ + LODroadsigns1_02.dff \ + LODroadsigns1_03.dff \ + LOD_roadswsh01.dff \ + LOD_roadswsh04.dff \ + LOD_roadswsh05.dff \ + lod_roadswsh28.dff \ + LODroadx.dff \ + LOD_rooftopstart.dff \ + LODrtrees1.dff \ + LODrtrees2.dff \ + LODrtrees3.dff \ + LODrtrees4.dff \ + LODrtrees5.dff \ + LODrwaterfrnt05.dff \ + LODrwaterfrnt06b.dff \ + LODrwaterfrnt06.dff \ + LODrwaterfrnt07b.dff \ + LODrwaterfrnt07.dff \ + LODrwaterfrnt08.dff \ + LODrwaterfrnt09b.dff \ + LODrwaterfrnt09.dff \ + LODrwaterfrnt10.dff \ + LODrwaterfrnt11.dff \ + LODrwaterfrnt12.dff \ + LODsbrgeStartf_dt.dff \ + LODsbrgeStartf_nt.dff \ + LODsbrgeStart_r_dt.dff \ + LODsbrgeStart_r_nt.dff \ + LODsbridge07_dt.dff \ + LODsbridge07_nt.dff \ + LODsbridge1_dt.dff \ + LODsbridge1_nt.dff \ + LODsbridge2_dt.dff \ + LODsbridge2_nt.dff \ + LODsbridge3_dt.dff \ + LODsbridge3_nt.dff \ + LODsbridge4_dt.dff \ + LODsbridge4_nt.dff \ + LODsbridge5_dt.dff \ + LODsbridge5_nt.dff \ + LODsbridge6_dt.dff \ + LODsbridge6_nt.dff \ + LODsbridge_dt.dff \ + LODsbridge_nt.dff \ + lodscarland226.dff \ + LODscreening2.dff \ + LODscreening.dff \ + LODseaplaland1.dff \ + LODseaplanehanger1.dff \ + LODsection.dff \ + LODse_ext.dff \ + LODse_pier.dff \ + LODse_pierfence.dff \ + LOD_shedbig30.dff \ + lodsho_ext1.dff \ + LOD_shop.dff \ + LODshopfronts01b.dff \ + LODshopfronts01.dff \ + LODshopfronts02b.dff \ + LODshopfronts02.dff \ + lodsho_recordshop1.dff \ + LODshpfrnts1grd.dff \ + LODshpfrts1bgrd.dff \ + LODsion1_A.dff \ + LODsion1_base.dff \ + LODsion1b.dff \ + LODsion1gardens.dff \ + LODsion2_A.dff \ + LODsion2_B.dff \ + LODsion2c.dff \ + LODsion3a.dff \ + LODsion3_base.dff \ + LODsion3_B.dff \ + LODsion3_C.dff \ + LODsion3gardens.dff \ + LODsjmcrush.dff \ + LODsmahangar2_01.dff \ + LODsmahangar2_02.dff \ + LODsmahangar2_03.dff \ + LODsmahangar2_04.dff \ + LODsmahangar2_05.dff \ + LODsmahangar2_06.dff \ + LODsmallhangars1_01.dff \ + LODsmallhangars1_02.dff \ + LODsmallhangars1_03.dff \ + LODsmallisland1_01.dff \ + LODsmallislebridge1.dff \ + LODsmallradar1_02.dff \ + LODsmallradar1.dff \ + LODspotlite_01.dff \ + LODstadium_entrance.dff \ + LODstadiumland_a.dff \ + LODstadiumland_b.dff \ + LODstadiumland_c.dff \ + LODstadiumland_d.dff \ + LODstadium_start.dff \ + LOD_starlite_dy.dff \ + LOD_starlite_nt.dff \ + lodsthroad01.dff \ + lodsthroad02.dff \ + lodsthroad03.dff \ + lodsthroad04.dff \ + lodsthroad05.dff \ + lodsthroad06.dff \ + lodsthroad07.dff \ + lodsthroad08.dff \ + lodsthroad09.dff \ + lodsthroad10.dff \ + lodsthroad11.dff \ + lodsthroad12.dff \ + lodsthroad13.dff \ + lodsthroad14.dff \ + lodsthroad15.dff \ + lodsthroad16.dff \ + lodsthroad17.dff \ + lodsthroad18.dff \ + lodsthroad19.dff \ + lodsthroad20.dff \ + lodsthroad21.dff \ + lodsthroad22.dff \ + lodsthroad23.dff \ + lodsthroad24.dff \ + LODstudiowall.dff \ + LODst_woodfence30.dff \ + LODtaxifirm02.dff \ + LODtbuildws14.dff \ + LODtermAintfloor1.dff \ + LODterminA_01.dff \ + LODterminalB1.dff \ + LODTI1RD.dff \ + LODtibuilds.dff \ + LODticent.dff \ + LOD_tides2_dy.dff \ + LOD_tides2_nt.dff \ + LODtides3_dy.dff \ + LODtides3_nt.dff \ + LOD_tides_dy.dff \ + LOD_tides_nt.dff \ + LODtihut1.dff \ + LODtihut2.dff \ + LODtihut3.dff \ + LODtimall.dff \ + LODtiroadbuild.dff \ + LODtiskyg.dff \ + LODtiskyt.dff \ + LODtistation01.dff \ + LODtistation.dff \ + LODtlehabroad.dff \ + LODtlehacoast02.dff \ + LODtlehacoast05.dff \ + lodtleha_hardware.dff \ + LODtleha_police.dff \ + LODtlharoadgg2.dff \ + LODtower_01.dff \ + LODtower_02.dff \ + LODtower_03.dff \ + LODtower.dff \ + LODtranspol1.dff \ + LODtroada.dff \ + LODtroadb.dff \ + LODtship_structure0.dff \ + LODuild034.dff \ + LODuild1b.dff \ + LODuilding01.dff \ + LODuilding05.dff \ + LODunation_dt.dff \ + LODvroad1.dff \ + LODvroad2.dff \ + lodwalkway2.dff \ + LODwall2.dff \ + LODwall3.dff \ + LODwall.dff \ + LODwaretank.dff \ + lodwashpaynspray.dff \ + LODwatertank.dff \ + LODwoodbridge1.dff \ + LODworksramps.dff \ + lod_wtrftr_1a.dff \ + lodxrefhirise1.dff \ + lodxrefhse1.dff \ + lodxrefhse2.dff \ + loopbig.dff \ + looplop2.dff \ + lotion.dff \ + lounger.dff \ + lounge_towel_up.dff \ + lounge_wood_dn.dff \ + lounge_wood_up.dff \ + lovefist.dff \ + lwchara.dff \ + lwcharb.dff \ + lw_floor_opac.dff \ + lw_lights.dff \ + lw_officedesk01.dff \ + lw_palm1.dff \ + lw_roombitsbb.dff \ + lw_roombitsb.dff \ + lw_roombitsc.dff \ + lw_room_main.dff \ + lw_rug.dff \ + lw_shado.dff \ + m4.dff \ + m60.dff \ + machete.dff \ + maidmalltopa.dff \ + male01.dff \ + mall2.dff \ + mall_ammu.dff \ + mall_ammuwin.dff \ + mallbushdense.dff \ + mallbushs.dff \ + mall_fans.dff \ + mall_hardwarebits.dff \ + mall_hardware.dff \ + mallint1.dff \ + mallint2.dff \ + mallint3.dff \ + mallint5.dff \ + mallint5up_b.dff \ + mallint5up.dff \ + mallint7.dff \ + mallint8.dff \ + mallint9.dff \ + malllights.dff \ + mallseats01.dff \ + mallstrut.dff \ + malltreereflect2.dff \ + malltreereflect.dff \ + malltrees01.dff \ + MALLUNDER.dff \ + Man1_baseHedge.dff \ + man1garden_hedge.dff \ + Man3_base_hedge.dff \ + man3ghedge.dff \ + Man4garagedoors.dff \ + man4ladderpool01.dff \ + manana.dff \ + man_backside2.dff \ + man_backside.dff \ + man_backside_int.dff \ + man_build01_al.dff \ + man_build01.dff \ + man_build02_al.dff \ + man_build02.dff \ + man_build03_al.dff \ + man_build03.dff \ + man_build04.dff \ + man_build04_fnce.dff \ + man_build05_al.dff \ + man_build05.dff \ + man_build_int.dff \ + man_cellar1.dff \ + man_cellar2.dff \ + man_chandeliers.dff \ + man_couch02.dff \ + man_doorclosed.dff \ + man_dooropen.dff \ + man_doorway2.dff \ + man_f_chandelier.dff \ + man_frntstep.dff \ + man_frntstepGD.dff \ + man_grnd2.dff \ + man_grnd3.dff \ + man_grnd4.dff \ + man_grnd5.dff \ + man_grnd6.dff \ + man_hall_floor.dff \ + man_hall_pillars.dff \ + man_hall_ref.dff \ + man_hall_wall1.dff \ + man_hallway1.dff \ + man_hallway2.dff \ + man_hedge1.dff \ + man_lights_pool.dff \ + man_maze.dff \ + man_monitor_base.dff \ + man_monitors02.dff \ + manmrgbase1.dff \ + manmrgbuild1.dff \ + manmrgbuild2.dff \ + man_pool.dff \ + man_pool_shado1.dff \ + man_pool_water.dff \ + Man_rubbish03.dff \ + man_safenew.dff \ + man_safenewdr.dff \ + man_safenewmny.dff \ + mansbushes2.dff \ + mansbushes.dff \ + man_scar_curtains.dff \ + man_scar_props206.dff \ + man_scar_propv.dff \ + man_scrface_walls.dff \ + man_scrfashado.dff \ + man_scrfce_chstrset.dff \ + man_scrfce_floor1.dff \ + man_sdr_folge.dff \ + man_sdr_lamps.dff \ + man_sdr_paintin.dff \ + man_sdr_props.dff \ + man_sdr_rug.dff \ + man_sdr_seating.dff \ + man_sdr_strctur.dff \ + man_sdr_tables.dff \ + man_sdr_tabls.dff \ + man_sdr_tvstf.dff \ + man_sidrm_shad.dff \ + Mansion1_A.dff \ + Mansion1_base.dff \ + mansion1b.dff \ + mansion1gardens.dff \ + Mansion2_A.dff \ + Mansion2_B.dff \ + Mansion2c.dff \ + Mansion2_C_fence.dff \ + mansion3a.dff \ + mansion3_base.dff \ + mansion3_B.dff \ + Mansion3_C.dff \ + mansion3gardens2.dff \ + mansion3gardens.dff \ + Mansion4gate.dff \ + mansionfence.dff \ + mansion_new_doors.dff \ + mans_merge03.dff \ + mans_merge07.dff \ + mans_merge09.dff \ + man_tablkb1.dff \ + man_twr_stairsB.dff \ + man_twr_stairs.dff \ + man_wtr_fill.dff \ + man_wtrftr_1a.dff \ + man_wtrftr_2a.dff \ + man_wtrftr_2.dff \ + man_wtrftr_3a.dff \ + man_wtrftr_wtr1.dff \ + man_xchandeliers.dff \ + many_polys2.dff \ + many_polys.dff \ + marina1.dff \ + marina2.dff \ + marina3.dff \ + marinafence01.dff \ + marinafence02.dff \ + marinafence03.dff \ + marinafence04.dff \ + marinafence05.dff \ + marinaplanter1.dff \ + marinaplanter2.dff \ + marinasign1.dff \ + marinasign2.dff \ + marinaveg1.dff \ + marinaveg2.dff \ + marinaveg3.dff \ + marquis.dff \ + mask.dff \ + maverick.dff \ + Mba.dff \ + mb_ammobox1.dff \ + mb_ammobox2.dff \ + mb_ammobox3.dff \ + Mbb.dff \ + mbtbemp.dff \ + mbtbful.dff \ + mcane.dff \ + mc_assaultcourse1.dff \ + mc_assaultcourse2.dff \ + mc_barrackshad.dff \ + mc_camotent1_01.dff \ + mc_camotent1_04.dff \ + mc_camotent1_05.dff \ + mc_crashbar1.dff \ + mc_crashbar2.dff \ + mc_dirtjump1.dff \ + mc_fence1.dff \ + mc_fence2.dff \ + mc_fence3.dff \ + mc_fencshad.dff \ + mc_flags1.dff \ + mc_flags2.dff \ + mc_ground1.dff \ + mc_ground2.dff \ + mc_ground3.dff \ + mc_ground4.dff \ + mc_ground5.dff \ + mc_hangar1.dff \ + mc_hangarint1.dff \ + mc_kerb_01.dff \ + mc_kerb_02.dff \ + mc_largebuild.dff \ + mc_overlights1.dff \ + mc_overlights2.dff \ + mc_shed04.dff \ + mc_shed1.dff \ + mc_tower_01.dff \ + mc_tower_02.dff \ + mc_tower_03.dff \ + mc_treesfw1_01.dff \ + mc_treesfw2_01.dff \ + mc_treesfw3_01.dff \ + mc_wall2.dff \ + mc_wall3.dff \ + mc_wall.dff \ + medic.dff \ + merced.dff \ + mesa.dff \ + mfour.dff \ + mgoona.dff \ + Miamibin.dff \ + miamifirehyd.dff \ + miamiland027a.dff \ + miamiland037.dff \ + miamiland039.dff \ + miamiland041.dff \ + miamiland170.dff \ + miamiland171.dff \ + miamiland172.dff \ + miamiland173.dff \ + miamiland174.dff \ + miamiland175.dff \ + miamiland176.dff \ + miamiland177.dff \ + miamiland178.dff \ + miamiland179.dff \ + miamiland180.dff \ + miamiland_kb01_dy.dff \ + miamiland_kb01_nt.dff \ + miamiland_kb02.dff \ + miamiland_kb03.dff \ + miamiland_kb04.dff \ + miamiland_kb10b.dff \ + miamiland_kb10.dff \ + miamiland_kb11b.dff \ + miamiland_kb11.dff \ + miamiland_kb12b.dff \ + miamiland_kb12.dff \ + miamiland_kb13.dff \ + miamiland_ws04b.dff \ + miamiland_ws04.dff \ + miamiodnroadxa.dff \ + miamphon.dff \ + midmallfloorw.dff \ + midmallflorre.dff \ + mine.dff \ + minigun2.dff \ + minigun.dff \ + missile.dff \ + mlamppost.dff \ + ml_borders.dff \ + ml_bordersdoor.dff \ + ml_borderswin01.dff \ + ml_borderswin02.dff \ + ml_cofcounter1.dff \ + ml_coffee_dsk.dff \ + ml_coffwin01.dff \ + ml_coffwin02.dff \ + ml_coffwinframe01.dff \ + ml_coffwinframe02.dff \ + ml_doorsbotl.dff \ + ml_doorsbotr.dff \ + ml_doorscarp.dff \ + ml_doorsmidl.dff \ + ml_doorsmidr.dff \ + ml_doorstopl.dff \ + ml_doorstopr.dff \ + ml_escl.dff \ + ml_gap.dff \ + ml_gapdoors.dff \ + ml_gapwindows01.dff \ + ml_gapwindows02.dff \ + ml_gapwindows03.dff \ + ml_gapwindows04.dff \ + ml_gapwindows05.dff \ + ml_gapwindows06.dff \ + ml_gapwindows07.dff \ + ml_gapwindows08.dff \ + ml_gapwindows09.dff \ + ml_gapwindows10.dff \ + ml_gapwindows11.dff \ + ml_gapwindows12.dff \ + ml_glasslft.dff \ + mlgrndmidtop.dff \ + mlgrounds.dff \ + ml_jewelcounter.dff \ + ml_jewelglass.dff \ + ml_jewellers.dff \ + ml_jewelwin01.dff \ + ml_jewelwin02.dff \ + ml_jewelwin03.dff \ + ml_jewelwinframe01.dff \ + ml_jewelwinframe02.dff \ + mlmallbush.dff \ + mlmallflag02.dff \ + mlmallflag12.dff \ + mlmallflag14.dff \ + mlmallplan02.dff \ + mlmallplan04.dff \ + mlmallplan05.dff \ + mlmallroof01.dff \ + mlmallroof.dff \ + mlmiamiland056.dff \ + mlmiamiland058.dff \ + mlmiamiland060b.dff \ + mlmiamiland060.dff \ + mlngrnd.dff \ + mlnorthmall.dff \ + mlnorthmalllft.dff \ + mlnorthmalllftex.dff \ + mlnorthmallrt.dff \ + mlnorthmalltop.dff \ + mlNpoint02.dff \ + mlNpoint04.dff \ + mlNpoint06.dff \ + mlobjects_n02.dff \ + mlobjects_n03.dff \ + mlobjects_n04.dff \ + mlobjects_n.dff \ + mlobjects_s02.dff \ + mloven02.dff \ + mloven.dff \ + ml_planterbed.dff \ + ml_record3.dff \ + ml_record.dff \ + mlrecords.dff \ + ml_recordwin01.dff \ + ml_recordwin02.dff \ + ml_recordwin03.dff \ + ml_recordwin04.dff \ + ml_recordwin05.dff \ + ml_recordwin06.dff \ + ml_tarbrushdamage.dff \ + ml_tarbrush.dff \ + ml_tartable.dff \ + ml_vegbits01.dff \ + ml_vegbits02.dff \ + ml_vegbits03.dff \ + ml_vegbits04.dff \ + ml_vegbits05.dff \ + ml_water2.dff \ + ml_water.dff \ + mna_table_scrfce.dff \ + mn_treesis_int.dff \ + mn_treesisl05.dff \ + mob_detailsb.dff \ + mob_door2.dff \ + mob_door3.dff \ + mob_mobroom2.dff \ + molotov.dff \ + moneybag.dff \ + Money.dff \ + moonbeam.dff \ + mp5lng.dff \ + mpfshrt.dff \ + mporna.dff \ + Mpostbox1.dff \ + mrwhoop.dff \ + mserver.dff \ + MTraffic1.dff \ + MTraffic2.dff \ + MTraffic3.dff \ + MTraffic4.dff \ + muck_nbeachw.dff \ + mule.dff \ + nb2glue.dff \ + nbbridgbit2.dff \ + nbchbit04bgrd1.dff \ + nbchbit04grd2.dff \ + nbchbtjetty01.dff \ + nbchbtjetty02.dff \ + nbchflw01.dff \ + nbchflw02.dff \ + nbchwbridgb.dff \ + nbchwbridge.dff \ + nbdcoshp01glss.dff \ + nbdcprmndfnc1.dff \ + nbdcprmndfnc2.dff \ + nbdecopromnd2.dff \ + nbdecopromnd.dff \ + nbdecoshop01.dff \ + nbdecoshop02.dff \ + nbdecoshop03.dff \ + nbdecoshplants.dff \ + nbeachb1.dff \ + nbeachb.dff \ + nbeachbed01.dff \ + nbeachbed02.dff \ + nbeachbit02.dff \ + nbeachbit03.dff \ + nbeachbit03rails.dff \ + nbeachbit04b.dff \ + nbeachbit04.dff \ + nbeachbit05.dff \ + nbeachbit06.dff \ + nbeachbridge.dff \ + nbeach_police02.dff \ + nbeach_police.dff \ + nbeachrdsign.dff \ + nbeachroads01.dff \ + nbeachroads02.dff \ + nbeachroads03.dff \ + nbeachroads09.dff \ + nbeachsand1.dff \ + nbeachsand2b.dff \ + nbeachsand2.dff \ + nbeachvegy1.dff \ + nbeachvegy2.dff \ + nbeachwater1.dff \ + nbeachwjetty01.dff \ + nbeachwjetty02.dff \ + nbecbtwbrdge.dff \ + nbec_divingbds.dff \ + nbechneon2.dff \ + nbecland013.dff \ + nbecland014.dff \ + nbecland01.dff \ + nbecland03.dff \ + nbecland05.dff \ + nbecland09.dff \ + nbecland10.dff \ + nbecland11.dff \ + nbecland12bball.dff \ + nbecland12.dff \ + nbecland12gates.dff \ + nbecland13_gf.dff \ + nbecnssignday_dy.dff \ + nbecnssignight_nt.dff \ + nbecpaynspray.dff \ + nbecpnsdoor.dff \ + nbecpnsneon1.dff \ + nbhospgrnd02.dff \ + nbhospgrnd.dff \ + nbhotel01.dff \ + nbhotel02.dff \ + nbhotel02glss.dff \ + nbhotel03.dff \ + nbhotel04.dff \ + nbhotel05.dff \ + nbhotel06.dff \ + nbhotel07.dff \ + nbhotel08.dff \ + nbhtel06shdw.dff \ + nbmiamiland048.dff \ + nbmiamiland049.dff \ + nbmiamiland050.dff \ + nbmiamiland051.dff \ + nbmiamiland052.dff \ + nbmiamiland053.dff \ + nbmiamiland054.dff \ + nbmiamiland055.dff \ + nbmiamiland057.dff \ + nbmiamiland059.dff \ + nbmiamiland061.dff \ + nbmiamiland063.dff \ + nbmiamiland065.dff \ + nbmiamiland068.dff \ + nbnorthmallext1.dff \ + nb_road01.dff \ + nb_road02.dff \ + nb_road03.dff \ + nb_road04.dff \ + nb_road05.dff \ + nb_road06.dff \ + nb_road07.dff \ + nb_road08.dff \ + nb_road09.dff \ + nb_road10.dff \ + nb_road11.dff \ + nb_road12.dff \ + nb_road13.dff \ + nb_road14.dff \ + nb_road15.dff \ + nbroada1.dff \ + nbroadnew1.dff \ + nbroofst11.dff \ + nbsavehotel.dff \ + nbsavhtlglass.dff \ + nbt_apartpool01.dff \ + nbt_apartpool02.dff \ + nbt_apartpool03.dff \ + nbt_balcony02.dff \ + nbt_balcony03.dff \ + nbt_balcony05.dff \ + nbt_barfence01.dff \ + nbtgardoor02.dff \ + nbtgardoor03.dff \ + nbtgardoor.dff \ + nbt_hotel0102.dff \ + nbt_hotel01.dff \ + nbt_hotel0202.dff \ + nbt_hotel02.dff \ + nbt_hotel0302.dff \ + nbt_hotel03.dff \ + nbt_hotel03glass.dff \ + nbt_hotel0402.dff \ + nbt_hotel04.dff \ + nbt_hotel05.dff \ + nbt_hotel0602.dff \ + nbt_hotel06balcony.dff \ + nbt_hotel06.dff \ + nbt_hotel0702.dff \ + nbt_hotel07.dff \ + nbt_hotel07fence.dff \ + nbt_hotel08.dff \ + nbthotel08fence02.dff \ + nbthotel10.dff \ + nbt_hotel13.dff \ + nbt_hotel15.dff \ + nbt_hoteltrees01.dff \ + nbt_hoteltrees02.dff \ + nbt_htlpiswr01.dff \ + nbt_htlpiswr02.dff \ + nbt_htlpool01.dff \ + nbt_htlpoolbar01.dff \ + nbt_htlpoolbase01b.dff \ + nbt_htlpoolbase01.dff \ + nbt_htlpoolbase02.dff \ + nbt_htlpoolbase03.dff \ + nbt_htlpoolbase04.dff \ + nbt_htlpoolrm01.dff \ + nbt_htlpoolrm02.dff \ + nbt_mans04balcony.dff \ + nbt_mansion01.dff \ + nbt_mansion02.dff \ + nbt_mansion03.dff \ + nbt_mansion0402.dff \ + nbt_mansion04.dff \ + nbt_mansionbase01.dff \ + nbt_mansionbase02.dff \ + nbt_mansiontrees01.dff \ + nbt_mansiontrees02.dff \ + nbt_mansiontrees03.dff \ + nbt_mansiontrees04.dff \ + nbt_mansiontrees05.dff \ + nbt_mnsnwtr01.dff \ + nbt_mnsnwtr02.dff \ + nbt_pizzaplace.dff \ + nbt_road01.dff \ + nbt_road02.dff \ + nbt_road03.dff \ + nbt_road04.dff \ + nbt_road05.dff \ + nbt_road06.dff \ + nbt_road07.dff \ + nbt_road08.dff \ + nbt_road09.dff \ + nbt_road10.dff \ + nbt_road11.dff \ + nbt_road12_gf.dff \ + nbtroadx.dff \ + nbt_rooftopstart.dff \ + nbtshopfronts01b.dff \ + nbtshopfronts01.dff \ + nbtshopfronts02b.dff \ + nbtshopfronts02.dff \ + nbtshopfronts02win.dff \ + nbtshpfrnts1grd.dff \ + nbtshpfrts1bgrd.dff \ + nbt_skip.dff \ + nbt_tikiprops01.dff \ + nbw_bridgew.dff \ + nbw_bush01.dff \ + nbw_bush02.dff \ + nbwbusshy2.dff \ + nbw_grssypatch.dff \ + nbwneon01.dff \ + nbwneon02.dff \ + nbw_roadsigns.dff \ + newashneon245.dff \ + newbuild01.dff \ + newbuild02.dff \ + newbuilding0102.dff \ + newbuilding01.dff \ + new_bushsm.dff \ + new_bushtest42.dff \ + new_bushtest.dff \ + new_insideb.dff \ + new_inside.dff \ + newmallcp_01.dff \ + newmallcp_02.dff \ + newmallcp_03.dff \ + newmallcp_04.dff \ + newmallcp_05.dff \ + newpools.dff \ + newramp2.dff \ + newramp.dff \ + newsstand1.dff \ + newstandnew1.dff \ + newstandnew2.dff \ + newstandnew3.dff \ + newstandnew4.dff \ + newstandnew5.dff \ + newtowerdoor1.dff \ + nhaiticar13.dff \ + nhaitiplnt14.dff \ + nhaitiplnt15.dff \ + nhaitplatdr.dff \ + nhaitplatdrr.dff \ + nhvood.dff \ + nitestick.dff \ + nmalloutside.dff \ + noparkingsign1.dff \ + npw_press1_02.dff \ + nrth1veg21.dff \ + nrth1veg37.dff \ + nrth1veg42.dff \ + nrth3veg05.dff \ + nrth3veg08.dff \ + nrth3veg16.dff \ + nrth3veg25.dff \ + nrth3veg35.dff \ + nrth3veg50.dff \ + nrth3veg59.dff \ + nrth4veg05.dff \ + nrth4veg08.dff \ + nrth4veg09.dff \ + nrth4veg212.dff \ + nrth4veg21.dff \ + nrth7veg09.dff \ + nrth7veg.dff \ + nt_aircon1_01.dff \ + nt_aircon1_02.dff \ + nt_aircon1dbl.dff \ + nt_aircon3_01.dff \ + nt_alarm1_01.dff \ + nt_alarm2_01.dff \ + nt_alarm3_01.dff \ + nt_bed1_01.dff \ + nt_cablebox1_01.dff \ + nt_cablebox2_01.dff \ + nt_cablebox3_01.dff \ + nt_cablebox4_01.dff \ + nt_cablebox5_01.dff \ + nt_cablebox6_01.dff \ + nt_couch_1.dff \ + nt_firehose_01.dff \ + nt_ladder_01.dff \ + nt_ladder_02.dff \ + nt_roadblockCI.dff \ + nt_roadblockGF.dff \ + nt_securecam1_01.dff \ + nt_securecam2_01.dff \ + nt_security1.dff \ + nt_vent1_01.dff \ + nt_vent2_01.dff \ + nt_vent3_01.dff \ + nt_wassily1_02.dff \ + nubuilding1glass.dff \ + nw_nbwroad01.dff \ + nw_nbwroad02.dff \ + nw_nbwroad03.dff \ + nw_nbwroad04.dff \ + nw_nbwroad05.dff \ + nw_nbwroad06.dff \ + nw_nbwroad07.dff \ + nw_nbwroad08.dff \ + ocdneona_nt.dff \ + ocdneonb_nt.dff \ + oceanic.dff \ + oceanrd1_dy.dff \ + oceanrd1_nt.dff \ + oceanrda03_dy.dff \ + oceanrda03_nt.dff \ + oceanrda04_dy.dff \ + oceanrda04_nt.dff \ + oceanrda05_dy.dff \ + oceanrda05_nt.dff \ + oceanrda06_dy.dff \ + oceanrda06_nt.dff \ + oceanroad04.dff \ + oceanroad05.dff \ + ocndbridge.dff \ + od2glue.dff \ + od_acne.dff \ + odalleygb_dy.dff \ + odalleygb_nt.dff \ + odalleyground2_dy.dff \ + odalleyground2_nt.dff \ + odalleyground4_dy.dff \ + odalleyground4_nt.dff \ + odalleyground5_dy.dff \ + odalleyground5_nt.dff \ + odalleyground_dy.dff \ + odalleyground_nt.dff \ + od_alleys1_01_dy.dff \ + od_alleys1_01_nt.dff \ + od_alleys1b_01_dy.dff \ + od_alleys1b_01_nt.dff \ + od_alleys2_01_dy.dff \ + od_alleys2_01_nt.dff \ + od_alleys3_01_dy.dff \ + od_alleys3_01_nt.dff \ + od_alleys3b_01_dy.dff \ + od_alleys3b_01_nt.dff \ + od_bighotcons.dff \ + od_bighotel_dy.dff \ + od_bighotel_nt.dff \ + od_bighotpool2.dff \ + od_bighotpool.dff \ + od_bighotsign01.dff \ + od_bighotsign.dff \ + od_bigshops1.dff \ + od_bigshops2.dff \ + od_bphq.dff \ + od_buildkb23_dy.dff \ + od_buildkb23_nt.dff \ + od_buildkb27_dy.dff \ + od_buildkb27_nt.dff \ + od_buildkb2_dy.dff \ + od_buildkb2_nt.dff \ + od_casagrande_dy.dff \ + od_casagrande_nt.dff \ + od_celldoor02.dff \ + od_celldoor03.dff \ + od_celldoor04.dff \ + od_celldoor1.dff \ + od_celldoor3.dff \ + od_celldoor4.dff \ + od_chariot.dff \ + od_chariotland.dff \ + od_chariot_lghts01.dff \ + od_chariot_lghts02.dff \ + od_char_signs_dy.dff \ + od_char_signs_nt.dff \ + od_clbdr_close.dff \ + od_clbdr_open.dff \ + od_clevelander_dy.dff \ + od_clevelander_nt.dff \ + od_clevelfence.dff \ + od_clubback.dff \ + od_clubdoora.dff \ + od_clubdoors.dff \ + od_clubfence_dy.dff \ + od_clubfence_nt.dff \ + od_clubfnce.dff \ + od_clubneon.dff \ + od_clubout_dy.dff \ + od_clubout_nt.dff \ + od_colony2_dy.dff \ + od_colony2_nt.dff \ + od_colony_dy.dff \ + od_colony_nt.dff \ + od_copwindows.dff \ + od_cornercrap1.dff \ + od_cornercrap2.dff \ + od_cornercrap3.dff \ + oddirtpass1.dff \ + od_dirtshad1.dff \ + od_dirtshad3.dff \ + od_dirtshad4.dff \ + od_dirty1.dff \ + od_dirty2.dff \ + od_dirty4.dff \ + od_divingbds.dff \ + od_divingbdsteps.dff \ + oddoorway2.dff \ + oddoorway.dff \ + oddoorwayS02.dff \ + oddoorwayS1.dff \ + od_fpbarsign.dff \ + od_groyne01.dff \ + odhighsandgrs1.dff \ + od_hotel_windows.dff \ + od_lhland.dff \ + od_lightbeam.dff \ + od_lightext.dff \ + od_lightint.dff \ + odlowsand1.dff \ + odlowsand2.dff \ + od_majestic2c_dy.dff \ + od_majestic2c_nt.dff \ + odnanrod2b_dy.dff \ + odnanrod2b_nt.dff \ + odn_beacon2_dy.dff \ + odn_beacon2_nt.dff \ + odn_colony_dy.dff \ + odn_colony_nt.dff \ + odndirt1.dff \ + odndirt2.dff \ + odndirt3.dff \ + odndirt4.dff \ + odndoorway3.dff \ + od_neona1.dff \ + od_neonn.dff \ + od_neons2b_nt.dff \ + od_neons3b_nt.dff \ + odneontest01.dff \ + odneontest.dff \ + od_neonx1.dff \ + od_newscafe_dy.dff \ + od_newscafe_nt.dff \ + odn_hotelhot_dy.dff \ + odn_hotelhot_nt.dff \ + odn_hotshad1.dff \ + odn_hotshad2.dff \ + odn_hotshad3.dff \ + odn_majest_dy.dff \ + odn_majestic2x_dy.dff \ + odn_majestic2x_nt.dff \ + odn_majesticz_dy.dff \ + odn_majesticz_nt.dff \ + odn_majest_nt.dff \ + odnneaon1.dff \ + odnneaon2.dff \ + odnneaon3.dff \ + odnneaon4.dff \ + odn_neonht1a.dff \ + od_northstar.dff \ + odNroad01_dy.dff \ + odNroad01_nt.dff \ + odNroad02.dff \ + odNroad03od.dff \ + odNroad04od_dy.dff \ + odNroad04od_nt.dff \ + odNroad05od.dff \ + odNroad06_dy.dff \ + odNroad06_nt.dff \ + odNroad08_dy.dff \ + odNroad08_nt.dff \ + odn_tides2_dy.dff \ + odn_tides2_nt.dff \ + odnvegbush1.dff \ + odnvegbush2b.dff \ + odnvegbush2.dff \ + odnwiret.dff \ + od_oceanrdN1_dy.dff \ + od_oceanrdN1_nt.dff \ + od_pat_hutb.dff \ + od_pat_hut.dff \ + od_pelican2_dy.dff \ + od_pelican2_nt.dff \ + od_pelican_dy.dff \ + od_pelican_nt.dff \ + odrampbit.dff \ + odrroofst02.dff \ + odrroofst06.dff \ + odrroofst07.dff \ + odrv_bushes01.dff \ + odrv_bushes.dff \ + od_tides3_dy.dff \ + od_tides3_nt.dff \ + od_tracks01.dff \ + od_tracks03.dff \ + od_tracks05.dff \ + od_tracks06.dff \ + od_tracks08.dff \ + od_tracks10.dff \ + od_tracks12.dff \ + od_tracks14.dff \ + od_vbnet.dff \ + od_walkway2.dff \ + od_walkway3.dff \ + od_wt.dff \ + od_wtlegs.dff \ + od_wtshadow.dff \ + od_wtsign.dff \ + package1.dff \ + packer.dff \ + palette.dff \ + palettejumpN04.dff \ + papermachn01.dff \ + parkbench1.dff \ + parkingmeter.dff \ + parkingmeterg.dff \ + parktable1.dff \ + patriot.dff \ + pcj600.dff \ + peren.dff \ + petrolcanm.dff \ + petrolpump.dff \ + Pga.dff \ + Pgb.dff \ + pharmacy_kb1.dff \ + pharmacy_kb2.dff \ + ph_build_dr_closed.dff \ + ph_build_dr_open.dff \ + ph_build_stairs.dff \ + pheonix.dff \ + philbmb.dff \ + phils_compnd_gate.dff \ + phnplay.dff \ + phoncol.dff \ + phonebooth1.dff \ + phonesign.dff \ + pickupmusic.dff \ + pickupsave.dff \ + pierentrance02.dff \ + pierentrance1.dff \ + pitchrb.dff \ + pizza_box1.dff \ + pizzabox.dff \ + pizzaboy.dff \ + pizza_mess2.dff \ + Pizza_mess.dff \ + plank01.dff \ + plank02.dff \ + plank1.dff \ + plankk2.dff \ + plants01.dff \ + plants04.dff \ + plants05b.dff \ + plants05.dff \ + play10.dff \ + play11.dff \ + play12.dff \ + player2.dff \ + player3.dff \ + player4.dff \ + player5.dff \ + player6.dff \ + player7.dff \ + player8.dff \ + player9.dff \ + player.dff \ + plc_stinger.dff \ + plnt_ref_bnk01.dff \ + plnt_ref_bnk02.dff \ + plyphon.dff \ + polbals.dff \ + police.DFF \ + pollstaeps1.dff \ + polmav.dff \ + Poloroids.dff \ + pony.dff \ + pooladder01.dff \ + pooladder.dff \ + poolcue.dff \ + poolq.dff \ + poolwaterwshs.dff \ + pornposters.dff \ + portcabGAZ06.dff \ + postbox1.dff \ + pot_01.dff \ + pot_02.dff \ + pot_03.dff \ + predator.dff \ + printdooor.dff \ + printra.dff \ + printrb.dff \ + printrc.dff \ + propashtray1.dff \ + propbeerglass1.dff \ + propcollecttable.dff \ + property_fsale.dff \ + property_locked.dff \ + prop_group2.dff \ + prop_group3.dff \ + props_group1.dff \ + propvodkabotl1.dff \ + propwinebotl1.dff \ + propwinebotl2.dff \ + propwineglass1.dff \ + psycho.dff \ + pw_backfence1.dff \ + pw_bayfence2_01.dff \ + pw_groundplane.dff \ + pw_paperroll_01.dff \ + pw_paperroll_02.dff \ + pw_paperroll_03.dff \ + pw_presrommshad.dff \ + pw_press1_03.dff \ + pw_priint.dff \ + pw_printworks.dff \ + pw_staffpark01.dff \ + pw_stairbarrier.dff \ + pw_stairs.dff \ + python.dff \ + rafaelsign1.dff \ + rafelneonsgn.dff \ + ramp.dff \ + rancher.dff \ + rcbandit.dff \ + rcbaron.dff \ + rcbomb.dff \ + rcgoblin.dff \ + rcraider.dff \ + rcyclbank01.dff \ + recordingstudio.dff \ + recsho_recordshop1.dff \ + reefer.dff \ + regina.dff \ + rhino.dff \ + rio.dff \ + road_downtown02.dff \ + road_downtown03.dff \ + road_downtown04.dff \ + road_downtown05.dff \ + road_downtown06.dff \ + road_downtown07.dff \ + road_downtown08.dff \ + road_downtown09.dff \ + road_downtown10.dff \ + road_downtown11.dff \ + road_downtown12.dff \ + road_downtown13.dff \ + road_downtown14.dff \ + road_downtown15.dff \ + road_downtown16.dff \ + road_downtown17.dff \ + road_downtown_new1.dff \ + road_downtown_new2.dff \ + roadsign.dff \ + road_stadiuma.dff \ + roadworkbarrier1.dff \ + rocketed_win1.dff \ + rocketla.dff \ + rockpatch03.dff \ + rockpatch1.dff \ + romero.dff \ + roofbit07.dff \ + roofbit2.dff \ + RoosbrgeStartf_dt.dff \ + RoosbrgeStartf_nt.dff \ + RoosbrgeStart_r_dt.dff \ + RoosbrgeStart_r_nt.dff \ + Roosbridge07_dt.dff \ + Roosbridge07_nt.dff \ + Roosbridge1_dt.dff \ + Roosbridge1_nt.dff \ + Roosbridge2_dt.dff \ + Roosbridge2_nt.dff \ + Roosbridge3_dt.dff \ + Roosbridge3_nt.dff \ + Roosbridge4_dt.dff \ + Roosbridge4_nt.dff \ + Roosbridge5_dt.dff \ + Roosbridge5_nt.dff \ + Roosbridge6_dt.dff \ + Roosbridge6_nt.dff \ + Roosbridge_dt.dff \ + Roosbridge_nt.dff \ + Rose_Bush01.dff \ + Rose_Bush03.dff \ + ruger.dff \ + rumpo.dff \ + runup01.dff \ + runup.dff \ + rustship_structure0.dff \ + sabre.dff \ + sabretur.dff \ + sam.dff \ + sanchez.dff \ + sandcastle1.dff \ + sandcastle2.dff \ + sandking.dff \ + satdishbig.dff \ + satdishsml.dff \ + sb.dff \ + scarbuildglas.dff \ + scarinterblood1.dff \ + scarinterior1.dff \ + scar_winglass.dff \ + scenery.dff \ + schair.dff \ + scrapgate.dff \ + screwdriver.dff \ + seabedcars04.dff \ + seabedcars.dff \ + seabedshit02.dff \ + seabedshit1.dff \ + seacorpse01.dff \ + seacorpse.dff \ + seaspar.dff \ + securica.dff \ + sentinel.dff \ + sentxs.dff \ + seveneleven.dff \ + sewer2.dff \ + sexgarden2.dff \ + sexpad_grdn_2.dff \ + sexypoolcover.dff \ + sfrenda.dff \ + sfrendb.dff \ + Sga.dff \ + Sgb.dff \ + sgc.dff \ + sgoona.dff \ + sgoonb.dff \ + shado_chuf.dff \ + shark.dff \ + shipstairs.dff \ + shootra.dff \ + shootrb.dff \ + shopfronts03.dff \ + shopwindows_dts.dff \ + shotgspa.dff \ + shotvcr.dff \ + Shovel.dff \ + shpfrnts03rail01.dff \ + shpfrnts03rail02.dff \ + signagelh.dff \ + sjmbigsig2.dff \ + sjmbigsig.dff \ + sjmbigsign01.dff \ + sjmbigsign02.dff \ + sjmbigsign.dff \ + sjmcoast04.dff \ + sjmcoast06.dff \ + sjmcoast08.dff \ + sjmcoast.dff \ + sjmcrushbn.dff \ + sjmcrushgate.dff \ + sjmcrushju.dff \ + sjmcrushlpo.dff \ + sjmcrushmnf.dff \ + sjmcrushtankr.dff \ + sjmfsthus.dff \ + sjmgrass2.dff \ + sjmgrass.dff \ + sjmhafinrd.dff \ + sjmhoose.dff \ + sjmkhus.dff \ + sjmmet2.dff \ + sjmmet.dff \ + sjmnd04.dff \ + sjmsection.dff \ + sjmtionb02.dff \ + sjmuilding01.dff \ + sjmuilding05.dff \ + S_keep.dff \ + skimmer.dff \ + smashbar.dff \ + smashbarpost.dff \ + sniper.dff \ + spad_build2.dff \ + spad_buildnew.dff \ + spad_dr_closed1.dff \ + spad_dr_closed2.dff \ + spad_dr_closed3.dff \ + spad_dr_open1.dff \ + spad_dr_open2.dff \ + spad_dr_open3.dff \ + spad_veg1.dff \ + spad_water.dff \ + spad_watr1.dff \ + spad_watr2.dff \ + spand.dff \ + spandxa.dff \ + spandxb.dff \ + sparrow.dff \ + speed.dff \ + speeder.dff \ + squalo.dff \ + stad_roada.dff \ + stad_roadb.dff \ + stad_roadc.dff \ + stad_roadd.dff \ + stallion.dff \ + stand01.dff \ + stand02.dff \ + standblack01.dff \ + standblack02.dff \ + standblack03.dff \ + stand.dff \ + starbbnet02.dff \ + starbitbush.dff \ + starbits05.dff \ + starbits06.dff \ + starbits13b.dff \ + starbits13c.dff \ + starbits13.dff \ + starbits17.dff \ + starbits20.dff \ + starbits22.dff \ + starbits24.dff \ + starbits45.dff \ + starbits46.dff \ + starbits49.dff \ + starbits50.dff \ + starbits51.dff \ + starbits52.dff \ + starbits54.dff \ + starbits55.dff \ + starbits56.dff \ + starbits57.dff \ + starbits58.dff \ + starbits59.dff \ + starbl02b.dff \ + STARBLOCKER02.dff \ + STARBLOCKER1.dff \ + starblocks02.dff \ + starblocks03.dff \ + starblocks04.dff \ + starbridge2b.dff \ + starbridge2.dff \ + stardepot.dff \ + stardepot_FENCE.dff \ + starfbed02.dff \ + starfbed03.dff \ + starfbed04.dff \ + starfbed05.dff \ + starfbed06.dff \ + stargatedetail.dff \ + StarGate.dff \ + Starhouse.dff \ + starisland_road1.dff \ + starisland_road2.dff \ + starisland_road3.dff \ + starisland_road4.dff \ + starisland_road6.dff \ + starladder.dff \ + starmainwall1.dff \ + Starrockpool.dff \ + starsbush2.dff \ + starscaffold_pole.dff \ + starwallmain2.dff \ + starwallmain4.dff \ + starwallmain5.dff \ + starwaterfrnt05.dff \ + starwaterfrnt06b.dff \ + starwaterfrnt06.dff \ + starwaterfrnt07b.dff \ + starwaterfrnt07.dff \ + starwaterfrnt08.dff \ + starwaterfrnt09b.dff \ + starwaterfrnt09.dff \ + starwaterfrnt10.dff \ + starwaterfrnt11.dff \ + starwaterfrnt12.dff \ + stationfence.dff \ + st_garden1.dff \ + Stglue01.dff \ + Stglue06.dff \ + Stglue07.dff \ + STGLUE.dff \ + stiltsville03.dff \ + stiltsville05.dff \ + stiltsville1.dff \ + stinger.dff \ + stObject23.dff \ + Stonebench1.dff \ + stPOOLw1.dff \ + stPOOLWATER02.dff \ + stPOOLWATER5.dff \ + stpoolwater.dff \ + Streetlamp1.dff \ + Streetlamp2.dff \ + stretch.dff \ + Stripa.dff \ + stripback.dff \ + stripb.dff \ + stripc.dff \ + stripclbdrclsd.dff \ + stripclbdropen.dff \ + stripinter.dff \ + stripinterfloor.dff \ + stripinterupside.dff \ + striplights.dff \ + strpbckdrclsd.dff \ + strpclubsigns1.dff \ + strtbarrier01.dff \ + stuntman03.dff \ + stuntman2.dff \ + sub_floodlite.dff \ + submarine.dff \ + sub_roadbarrier.dff \ + sub_roadleft.dff \ + sub_roadright.dff \ + subwaygate.dff \ + sutibag.dff \ + svegrgedoor.dff \ + svehsebuild1.dff \ + svntray.dff \ + swat.dff \ + swivelchair_A.dff \ + swivelchair_B.dff \ + tall_fence.dff \ + tar_civ1.dff \ + tar_civ2.dff \ + tar_downleft.dff \ + tar_downright.dff \ + tar_frame.dff \ + tar_gun1.dff \ + tar_gun2.dff \ + tar_top.dff \ + tar_upleft.dff \ + tar_upright.dff \ + taxi_d.dff \ + taxi.DFF \ + teargas.dff \ + tec9.dff \ + telewire02.dff \ + telewire03.dff \ + telewire04.dff \ + telgrphpole02.dff \ + telly.dff \ + tesad06.dff \ + tesad06sas.dff \ + tesad06ss.dff \ + test_track.dff \ + thebolla03.dff \ + thebolla04.dff \ + thebolla1.dff \ + thebolla2.dff \ + thebowl01.dff \ + thebowl03.dff \ + thebowl04.dff \ + thebowl05.dff \ + thebowl06.dff \ + thebowl07.dff \ + thebowl08.dff \ + thebowl09.dff \ + thebowl10.dff \ + thebowl11.dff \ + thebowl12.dff \ + thebowl13.dff \ + thebowl14.dff \ + thebowl15.dff \ + thebowl16.dff \ + thebowl17.dff \ + ticketbooth1a.dff \ + ticketbooth1.dff \ + ticketbooth2a.dff \ + ticketbooth2.dff \ + tiny_rock.dff \ + topfun.dff \ + toyz.dff \ + trafficcone.dff \ + trafficlight1.dff \ + trashcan.dff \ + trash.dff \ + treeshadairha01.dff \ + treeshadairha02.dff \ + treeshadairha03.dff \ + treeshadairha04.dff \ + treeshadairha05.dff \ + treeshadairha06.dff \ + treeshadairha07.dff \ + treeshadairha08.dff \ + treeshadci01.dff \ + treeshadci02.dff \ + treeshadci03.dff \ + treeshadci04.dff \ + treeshaddtn01.dff \ + treeshaddtn02.dff \ + treeshaddtn03.dff \ + treeshaddtn04.dff \ + treeshaddtn05.dff \ + treeshaddtn06.dff \ + treeshaddtn07.dff \ + treeshaddtn08.dff \ + treeshaddtn09.dff \ + treeshaddtn10.dff \ + treeshadnbt01.dff \ + treeshadnbt02.dff \ + treeshadnbt03.dff \ + treeshadnbt04.dff \ + treeshadnbt05.dff \ + treeshadnbt06.dff \ + treeshadnbt07.dff \ + treeshadowgolf01.dff \ + treeshadowgolf02.dff \ + treeshadowgolf03.dff \ + treeshadowgolf04.dff \ + treeshadowgolf05.dff \ + treeshadowgolf06.dff \ + treeshadowgolf07.dff \ + treeshadowgolf08.dff \ + treeshadstr01.dff \ + treeshadstr02.dff \ + treeshadstr03.dff \ + treeshadwn01.dff \ + treeshadwn02.dff \ + treeshadwn03.dff \ + treeshadwn04.dff \ + treeshadwn05.dff \ + treeshadws01.dff \ + treeshadws02.dff \ + treeshadws03.dff \ + treeshadws04.dff \ + treeshadws05.dff \ + treeshadws06.dff \ + Trophies.dff \ + trophy1.dff \ + tropic.dff \ + tunnelentrance.dff \ + turtle.dff \ + undermallneon.dff \ + unrocketed_win1.dff \ + uzi.dff \ + vcnmav.dff \ + veged01.dff \ + veged02.dff \ + veged.dff \ + vegeha1.dff \ + vegetationb03.dff \ + vegetationb.dff \ + veg_fern_balcny_kb1.dff \ + veg_fern_balcny_kb2.dff \ + veg_gaz.dff \ + veg_ivy_balcny_kb3.dff \ + veg_palm01.dff \ + veg_palm02.dff \ + veg_palm03.dff \ + veg_palm04.dff \ + veg_palmbig14.dff \ + veg_palmkb10.dff \ + veg_palmkb13.dff \ + veg_palmkb14.dff \ + veg_palmkb1.dff \ + veg_palmkb2.dff \ + veg_palmkb3.dff \ + veg_palmkb4.dff \ + veg_palmkb5.dff \ + veg_palmkb7.dff \ + veg_palmkb8.dff \ + veg_palmkb9.dff \ + veg_palmkbb11.dff \ + veg_palwee01.dff \ + veg_palwee02.dff \ + veg_palwee03.dff \ + veg_tree3.dff \ + veg_treea1.dff \ + veg_treea3.dff \ + veg_treeb1.dff \ + vendmach.dff \ + vice1.dff \ + vice2.dff \ + vice3.dff \ + vice4.dff \ + vice5.dff \ + vice6.dff \ + vice7.dff \ + vice8.dff \ + vicechee.dff \ + virgo.dff \ + voodoo.dff \ + vrates.dff \ + walton.dff \ + wash_ammu.dff \ + washawning1.dff \ + washawning2b.dff \ + washawninga1.dff \ + washbitsnew03.dff \ + washbitsnew04.dff \ + washbld195fenc.dff \ + wash_brdgsup1.dff \ + washbuild003.dff \ + washbuild005.dff \ + washbuild013.dff \ + washbuild014.dff \ + washbuild015.dff \ + washbuild017.dff \ + washbuild018.dff \ + washbuild020.dff \ + washbuild021.dff \ + washbuild025.dff \ + washbuild025neon.dff \ + washbuild030.dff \ + washbuild033.dff \ + washbuild039.dff \ + washbuild040.dff \ + washbuild041.dff \ + washbuild043.dff \ + washbuild05067.dff \ + washbuild050.dff \ + washbuild058.dff \ + washbuild062.dff \ + washbuild063.dff \ + washbuild066.dff \ + washbuild068.dff \ + washbuild069.dff \ + washbuild071.dff \ + washbuild072.dff \ + washbuild073.dff \ + washbuild074.dff \ + washbuild075.dff \ + washbuild0812.dff \ + washbuild081.dff \ + washbuild111.dff \ + washbuild112.dff \ + washbuild114.dff \ + washbuild116.dff \ + washbuild119.dff \ + washbuild120.dff \ + washbuild121.dff \ + washbuild184.dff \ + washbuild187.dff \ + washbuild191.dff \ + washbuild192.dff \ + washbuild194.dff \ + washbuild195.dff \ + washbuild198.dff \ + washbuild199.dff \ + washbuild200.dff \ + washbuild201.dff \ + washbuild202.dff \ + washbuild203.dff \ + washbuild206.dff \ + washbuild210.dff \ + washbuild213.dff \ + washbuild214.dff \ + washbuild215.dff \ + washbuild216.dff \ + washbuild217.dff \ + washbuild231.dff \ + washcrprkdirt1.dff \ + wash_deco012.dff \ + wash_deco01.dff \ + wash_deco022.dff \ + wash_deco02.dff \ + wash_deco032.dff \ + wash_deco03.dff \ + wash_deco042.dff \ + wash_deco04.dff \ + wash_deco05.dff \ + washdeconeon1.dff \ + washdirtbuild02.dff \ + washdirtbuild1.dff \ + washelipad.dff \ + washer.dff \ + washfence1.dff \ + washfence2.dff \ + washfntinwtr.dff \ + washgasneon01.dff \ + washgasneon02.dff \ + washgasneon.dff \ + washgaspump.dff \ + wash_hardwares.dff \ + wash_hardwindow.dff \ + wash_hosp02.dff \ + wash_hosp03.dff \ + wash_hospgrnd.dff \ + wash_hospital.dff \ + washing.dff \ + washmallwater.dff \ + washnewsky1.dff \ + washnewsky2.dff \ + washnewsky3.dff \ + washotelneon1.dff \ + washpaynspray.dff \ + wash_pizzaplace.dff \ + washpshoutdet.dff \ + washpshoutveg.dff \ + washrafalpha.dff \ + washskyplant1.dff \ + washskyplant2.dff \ + washtooceanrd1.dff \ + washundermall1.dff \ + washvegy237.dff \ + washvegy238.dff \ + washvegy239.dff \ + washvegy240.dff \ + washvegy2413.dff \ + washvegy241.dff \ + washvegy242.dff \ + washvegy2.dff \ + washvegy3.dff \ + washvegy4.dff \ + wastebin.dff \ + waterjump1.dff \ + waterjump2.dff \ + waterjumpx2.dff \ + wfobe.dff \ + wfogo.dff \ + wfori.dff \ + wfosh.dff \ + wfost.dff \ + wfotr.dff \ + wfybe.dff \ + wfybu.dff \ + wfycst.dff \ + wfyg1.dff \ + wfyg2.dff \ + wfyjg.dff \ + WFYLG.dff \ + wfypr.dff \ + wfyri.dff \ + wfysh.dff \ + wfysk.dff \ + wfyst.dff \ + wglasssmash.dff \ + wmobe.dff \ + wmobu.dff \ + wmoca.dff \ + wmogo.dff \ + wmori.dff \ + wmost.dff \ + wmotr.dff \ + wmybe.dff \ + wmybu.dff \ + wmycr.dff \ + wmycw.dff \ + wmygo.dff \ + wmyjg.dff \ + WMYLG.dff \ + wmypi.dff \ + wmyri.dff \ + wmysk.dff \ + wmyst.dff \ + woodenbox.dff \ + woshmaldirt1.dff \ + woshmallglas1.dff \ + woshmallglas2.dff \ + woshmalmarkings.dff \ + woshmalneon2.dff \ + wsgbuildwl97.dff \ + wshammuglas.dff \ + wshbldws26bit.dff \ + wshbuil19wall.dff \ + wshbuildneon9.dff \ + wshbuildws02.dff \ + wshbuildws03.dff \ + wshbuildws04.dff \ + wshbuildws05.dff \ + wshbuildws06.dff \ + wshbuildws07.dff \ + wshbuildws08.dff \ + wshbuildws09.dff \ + wshbuildws10.dff \ + wshbuildws12.dff \ + wshbuildws13.dff \ + wshbuildws14.dff \ + wshbuildws16.dff \ + wshbuildws17.dff \ + wshbuildws18.dff \ + wshbuildws19.dff \ + wshbuildws20.dff \ + wshbuildws21.dff \ + wshbuildws22.dff \ + wshbuildws2326.dff \ + wshbuildws2329.dff \ + wshbuildws23.dff \ + wshbuildws24.dff \ + wshbuildws25.dff \ + wshbuildws264.dff \ + wshbuildws26.dff \ + wshbuildws27.dff \ + wshbuildws28.dff \ + wshbuildws29.dff \ + wshbuildws30.dff \ + wshbuildws312.dff \ + wshbuildws31.dff \ + wshbuildws322.dff \ + wshbuildws32.dff \ + wshbuildws332.dff \ + wshbuildws33.dff \ + wshbuildws35.dff \ + wshbuildws36.dff \ + wshbuildws38.dff \ + wshbuildws40.dff \ + wshbuildws41.dff \ + wshbuildws42.dff \ + wshbuildws43.dff \ + wshbuildws43ins2.dff \ + wshbuildws43ins.dff \ + wshbuildws44.dff \ + wshbuildws45.dff \ + wshcarprksign.dff \ + wshchurchwal.dff \ + wshcokland.dff \ + wshcrprtstuf1.dff \ + wshcrprtstuf2.dff \ + wshgarage02.dff \ + wshgarage03.dff \ + wshgarage1.dff \ + wshhotelawn1.dff \ + wshhotelwater1.dff \ + wshmallneon.dff \ + wshmalsign1.dff \ + wshmalsign2.dff \ + wshmalsign3.dff \ + wshmalsign4.dff \ + wshmalsign5.dff \ + wshmoundlaw1.dff \ + wshneon.dff \ + wshnrthlawdoor.dff \ + wshnrthpstr02.dff \ + wshnrthpstr03.dff \ + wshnrthpstr05.dff \ + wshnrthpstr07.dff \ + wshnrthpstr1.dff \ + wshnrthroad01.dff \ + wshnrthroad02.dff \ + wshnrthroad03.dff \ + wshnrthroad04.dff \ + wshnrthroad05.dff \ + wshnrthroad06.dff \ + wshnrthroad07.dff \ + wshnrthroad08.dff \ + wshnrthroad09.dff \ + wshnrthroad10.dff \ + wshnrthroad11.dff \ + wshnrthroad12.dff \ + wshnrthroad13.dff \ + wshnrthroad14.dff \ + wshnrthroad15.dff \ + wshnrthroad16.dff \ + wshnrthroad17.dff \ + wshnrthroad18.dff \ + wshnrthroad19.dff \ + wshnrthroad20.dff \ + wshnrthroad21.dff \ + wshnrthroad22.dff \ + wshnrthroad23.dff \ + wshnrthroad24.dff \ + wshnrthroad25.dff \ + wshnrthroad26.dff \ + wshnrthroad27.dff \ + wshnrthroad28.dff \ + wshotelsign35.dff \ + wshotelveg1.dff \ + wshperdrclsd.dff \ + wshpnsdoor.dff \ + wshpnssignday_dy.dff \ + wshpnssignight_nt.dff \ + wshprkbarrier1.dff \ + wshprkbarrier2.dff \ + wshpspryneon1.dff \ + wsh_roadblock.dff \ + wsh_roadswsh01.dff \ + wsh_roadswsh04.dff \ + wsh_roadswsh05.dff \ + wsh_roadswsh28.dff \ + wshscarland226.dff \ + wshsthpstr02.dff \ + wshsthpstr03.dff \ + wshsthpstr04.dff \ + wshsthpstr05.dff \ + wshsthroad01.dff \ + wshsthroad02.dff \ + wshsthroad03.dff \ + wshsthroad04.dff \ + wshsthroad05.dff \ + wshsthroad06.dff \ + wshsthroad07.dff \ + wshsthroad08.dff \ + wshsthroad09.dff \ + wshsthroad10.dff \ + wshsthroad11.dff \ + wshsthroad12.dff \ + wshsthroad13.dff \ + wshsthroad14.dff \ + wshsthroad15.dff \ + wshsthroad16.dff \ + wshsthroad17.dff \ + wshsthroad18.dff \ + wshsthroad19.dff \ + wshsthroad20.dff \ + wshsthroad21.dff \ + wshsthroad22.dff \ + wshsthroad23.dff \ + wshsthroad24.dff \ + wshswimupbar.dff \ + wshswmupstuf.dff \ + wshtelgrphcabl01.dff \ + wshtelgrphcabl02.dff \ + wshtelgrphcabl03.dff \ + wshtelgrphcabl04.dff \ + wshtelgrphcabl05.dff \ + wshtelgrphcabl06.dff \ + wshtelgrphcabl07.dff \ + wshtelgrphcabl08.dff \ + wshtelgrphcabl09.dff \ + wshtelgrphcabl10.dff \ + wshtelgrphcabl11.dff \ + wshtelwter1.dff \ + wshundercrprk27.dff \ + wshxrefhirise1.dff \ + wshxrefhse1.dff \ + wshxrefhse2.dff \ + wslawyersfront.dff \ + wtn_roadsigns01.dff \ + xod_beacon_dy.dff \ + xod_beacon_nt.dff \ + xod_leslie_dy.dff \ + xod_leslie_nt.dff \ + xod_macalpin_dy.dff \ + xod_macalpin_nt.dff \ + xod_majestic_dy.dff \ + xod_majestic_nt.dff \ + xod_starlite_dy.dff \ + xod_starlite_nt.dff \ + xod_tides_dy.dff \ + xod_tides_nt.dff \ + xpolytrees1_dt.dff \ + xpolytrees2_dt.dff \ + xpolytrees3_dt.dff \ + xpolytrees4_dt.dff \ + yacht_chunk_kb.dff \ + yankee.dff \ + yt_doors14.dff \ + yt_gangplnk_tmp.dff \ + yt_main_body2.dff \ + yt_main_body.dff \ + yt_tmp_boat.dff \ + zebra.dff \ No newline at end of file diff --git a/miami/mp3list.mk b/miami/mp3list.mk new file mode 100644 index 00000000..27fb0ef6 --- /dev/null +++ b/miami/mp3list.mk @@ -0,0 +1,102 @@ +STREAM_ADF = \ + EMOTION.adf \ + ESPANT.adf \ + FEVER.adf \ + FLASH.adf \ + KCHAT.adf \ + VCPR.adf \ + VROCK.adf \ + WAVE.adf \ + WILD.adf + +STREAM_MP3 = \ + cnt_1b.mp3 \ + int_b.mp3 \ + rok_1.mp3 \ + phil_2.mp3 \ + cok_2b.mp3 \ + Malibu.mp3 \ + bank_1.mp3 \ + Hwater.mp3 \ + bclosed.mp3 \ + resc_1a.mp3 \ + bank_3a.mp3 \ + bike_3.mp3 \ + tex_2.mp3 \ + col_2.mp3 \ + porn_3.mp3 \ + bike_1.mp3 \ + finale.mp3 \ + ass_2.mp3 \ + law_4.mp3 \ + car_1.mp3 \ + bank_2b.mp3 \ + tex_1.mp3 \ + cok_4b.mp3 \ + miscom.mp3 \ + bank_2a.mp3 \ + law_1b.mp3 \ + MallAmb.mp3 \ + bank_3b.mp3 \ + ass_1.mp3 \ + DirtRing.mp3 \ + col_5a.mp3 \ + tax_1.mp3 \ + cnt_1a.mp3 \ + bike_2.mp3 \ + int_a.mp3 \ + bud_1.mp3 \ + cok_2a.mp3 \ + City.mp3 \ + cap_1.mp3 \ + int_m.mp3 \ + cnt_2.mp3 \ + tex_3.mp3 \ + law_2c.mp3 \ + law_2b.mp3 \ + col_3a.mp3 \ + cok_4a2.mp3 \ + rok_2.mp3 \ + ice_1.mp3 \ + hat_1.mp3 \ + hat_2.mp3 \ + cub_4.mp3 \ + Glight.mp3 \ + Strip.mp3 \ + bud_3.mp3 \ + Hbeach.mp3 \ + bopen.mp3 \ + fin2.mp3 \ + cub_3.mp3 \ + cok_4a.mp3 \ + porn_2.mp3 \ + cub_1.mp3 \ + col_4a.mp3 \ + Hotel.mp3 \ + police.mp3 \ + law_2a.mp3 \ + Law4Riot.mp3 \ + cok_1.mp3 \ + col_5b.mp3 \ + Water.mp3 \ + HCity.mp3 \ + cok_3.mp3 \ + law_1a.mp3 \ + porn_1.mp3 \ + porn_4.mp3 \ + taxi.mp3 \ + cub_2.mp3 \ + law_3.mp3 \ + hat_3.mp3 \ + phil_1.mp3 \ + bank_4.mp3 \ + BeachAmb.mp3 \ + FIST.mp3 \ + bud_2.mp3 \ + ambsil.mp3 \ + int_d.mp3 \ + fin.mp3 \ + rok_3a.mp3 \ + drug_1.mp3 \ + stripa.mp3 \ + col_1.mp3 \ No newline at end of file diff --git a/miami/sfxlist.mk b/miami/sfxlist.mk new file mode 100644 index 00000000..aae163ff --- /dev/null +++ b/miami/sfxlist.mk @@ -0,0 +1,9943 @@ +SFX_WAV = \ + sfx_0.wav \ + sfx_1000.wav \ + sfx_1001.wav \ + sfx_1002.wav \ + sfx_1003.wav \ + sfx_1004.wav \ + sfx_1005.wav \ + sfx_1006.wav \ + sfx_1007.wav \ + sfx_1008.wav \ + sfx_1009.wav \ + sfx_100.wav \ + sfx_1010.wav \ + sfx_1011.wav \ + sfx_1012.wav \ + sfx_1013.wav \ + sfx_1014.wav \ + sfx_1015.wav \ + sfx_1016.wav \ + sfx_1017.wav \ + sfx_1018.wav \ + sfx_1019.wav \ + sfx_101.wav \ + sfx_1020.wav \ + sfx_1021.wav \ + sfx_1022.wav \ + sfx_1023.wav \ + sfx_1024.wav \ + sfx_1025.wav \ + sfx_1026.wav \ + sfx_1027.wav \ + sfx_1028.wav \ + sfx_1029.wav \ + sfx_102.wav \ + sfx_1030.wav \ + sfx_1031.wav \ + sfx_1032.wav \ + sfx_1033.wav \ + sfx_1034.wav \ + sfx_1035.wav \ + sfx_1036.wav \ + sfx_1037.wav \ + sfx_1038.wav \ + sfx_1039.wav \ + sfx_103.wav \ + sfx_1040.wav \ + sfx_1041.wav \ + sfx_1042.wav \ + sfx_1043.wav \ + sfx_1044.wav \ + sfx_1045.wav \ + sfx_1046.wav \ + sfx_1047.wav \ + sfx_1048.wav \ + sfx_1049.wav \ + sfx_104.wav \ + sfx_1050.wav \ + sfx_1051.wav \ + sfx_1052.wav \ + sfx_1053.wav \ + sfx_1054.wav \ + sfx_1055.wav \ + sfx_1056.wav \ + sfx_1057.wav \ + sfx_1058.wav \ + sfx_1059.wav \ + sfx_105.wav \ + sfx_1060.wav \ + sfx_1061.wav \ + sfx_1062.wav \ + sfx_1063.wav \ + sfx_1064.wav \ + sfx_1065.wav \ + sfx_1066.wav \ + sfx_1067.wav \ + sfx_1068.wav \ + sfx_1069.wav \ + sfx_106.wav \ + sfx_1070.wav \ + sfx_1071.wav \ + sfx_1072.wav \ + sfx_1073.wav \ + sfx_1074.wav \ + sfx_1075.wav \ + sfx_1076.wav \ + sfx_1077.wav \ + sfx_1078.wav \ + sfx_1079.wav \ + sfx_107.wav \ + sfx_1080.wav \ + sfx_1081.wav \ + sfx_1082.wav \ + sfx_1083.wav \ + sfx_1084.wav \ + sfx_1085.wav \ + sfx_1086.wav \ + sfx_1087.wav \ + sfx_1088.wav \ + sfx_1089.wav \ + sfx_108.wav \ + sfx_1090.wav \ + sfx_1091.wav \ + sfx_1092.wav \ + sfx_1093.wav \ + sfx_1094.wav \ + sfx_1095.wav \ + sfx_1096.wav \ + sfx_1097.wav \ + sfx_1098.wav \ + sfx_1099.wav \ + sfx_109.wav \ + sfx_10.wav \ + sfx_1100.wav \ + sfx_1101.wav \ + sfx_1102.wav \ + sfx_1103.wav \ + sfx_1104.wav \ + sfx_1105.wav \ + sfx_1106.wav \ + sfx_1107.wav \ + sfx_1108.wav \ + sfx_1109.wav \ + sfx_110.wav \ + sfx_1110.wav \ + sfx_1111.wav \ + sfx_1112.wav \ + sfx_1113.wav \ + sfx_1114.wav \ + sfx_1115.wav \ + sfx_1116.wav \ + sfx_1117.wav \ + sfx_1118.wav \ + sfx_1119.wav \ + sfx_111.wav \ + sfx_1120.wav \ + sfx_1121.wav \ + sfx_1122.wav \ + sfx_1123.wav \ + sfx_1124.wav \ + sfx_1125.wav \ + sfx_1126.wav \ + sfx_1127.wav \ + sfx_1128.wav \ + sfx_1129.wav \ + sfx_112.wav \ + sfx_1130.wav \ + sfx_1131.wav \ + sfx_1132.wav \ + sfx_1133.wav \ + sfx_1134.wav \ + sfx_1135.wav \ + sfx_1136.wav \ + sfx_1137.wav \ + sfx_1138.wav \ + sfx_1139.wav \ + sfx_113.wav \ + sfx_1140.wav \ + sfx_1141.wav \ + sfx_1142.wav \ + sfx_1143.wav \ + sfx_1144.wav \ + sfx_1145.wav \ + sfx_1146.wav \ + sfx_1147.wav \ + sfx_1148.wav \ + sfx_1149.wav \ + sfx_114.wav \ + sfx_1150.wav \ + sfx_1151.wav \ + sfx_1152.wav \ + sfx_1153.wav \ + sfx_1154.wav \ + sfx_1155.wav \ + sfx_1156.wav \ + sfx_1157.wav \ + sfx_1158.wav \ + sfx_1159.wav \ + sfx_115.wav \ + sfx_1160.wav \ + sfx_1161.wav \ + sfx_1162.wav \ + sfx_1163.wav \ + sfx_1164.wav \ + sfx_1165.wav \ + sfx_1166.wav \ + sfx_1167.wav \ + sfx_1168.wav \ + sfx_1169.wav \ + sfx_116.wav \ + sfx_1170.wav \ + sfx_1171.wav \ + sfx_1172.wav \ + sfx_1173.wav \ + sfx_1174.wav \ + sfx_1175.wav \ + sfx_1176.wav \ + sfx_1177.wav \ + sfx_1178.wav \ + sfx_1179.wav \ + sfx_117.wav \ + sfx_1180.wav \ + sfx_1181.wav \ + sfx_1182.wav \ + sfx_1183.wav \ + sfx_1184.wav \ + sfx_1185.wav \ + sfx_1186.wav \ + sfx_1187.wav \ + sfx_1188.wav \ + sfx_1189.wav \ + sfx_118.wav \ + sfx_1190.wav \ + sfx_1191.wav \ + sfx_1192.wav \ + sfx_1193.wav \ + sfx_1194.wav \ + sfx_1195.wav \ + sfx_1196.wav \ + sfx_1197.wav \ + sfx_1198.wav \ + sfx_1199.wav \ + sfx_119.wav \ + sfx_11.wav \ + sfx_1200.wav \ + sfx_1201.wav \ + sfx_1202.wav \ + sfx_1203.wav \ + sfx_1204.wav \ + sfx_1205.wav \ + sfx_1206.wav \ + sfx_1207.wav \ + sfx_1208.wav \ + sfx_1209.wav \ + sfx_120.wav \ + sfx_1210.wav \ + sfx_1211.wav \ + sfx_1212.wav \ + sfx_1213.wav \ + sfx_1214.wav \ + sfx_1215.wav \ + sfx_1216.wav \ + sfx_1217.wav \ + sfx_1218.wav \ + sfx_1219.wav \ + sfx_121.wav \ + sfx_1220.wav \ + sfx_1221.wav \ + sfx_1222.wav \ + sfx_1223.wav \ + sfx_1224.wav \ + sfx_1225.wav \ + sfx_1226.wav \ + sfx_1227.wav \ + sfx_1228.wav \ + sfx_1229.wav \ + sfx_122.wav \ + sfx_1230.wav \ + sfx_1231.wav \ + sfx_1232.wav \ + sfx_1233.wav \ + sfx_1234.wav \ + sfx_1235.wav \ + sfx_1236.wav \ + sfx_1237.wav \ + sfx_1238.wav \ + sfx_1239.wav \ + sfx_123.wav \ + sfx_1240.wav \ + sfx_1241.wav \ + sfx_1242.wav \ + sfx_1243.wav \ + sfx_1244.wav \ + sfx_1245.wav \ + sfx_1246.wav \ + sfx_1247.wav \ + sfx_1248.wav \ + sfx_1249.wav \ + sfx_124.wav \ + sfx_1250.wav \ + sfx_1251.wav \ + sfx_1252.wav \ + sfx_1253.wav \ + sfx_1254.wav \ + sfx_1255.wav \ + sfx_1256.wav \ + sfx_1257.wav \ + sfx_1258.wav \ + sfx_1259.wav \ + sfx_125.wav \ + sfx_1260.wav \ + sfx_1261.wav \ + sfx_1262.wav \ + sfx_1263.wav \ + sfx_1264.wav \ + sfx_1265.wav \ + sfx_1266.wav \ + sfx_1267.wav \ + sfx_1268.wav \ + sfx_1269.wav \ + sfx_126.wav \ + sfx_1270.wav \ + sfx_1271.wav \ + sfx_1272.wav \ + sfx_1273.wav \ + sfx_1274.wav \ + sfx_1275.wav \ + sfx_1276.wav \ + sfx_1277.wav \ + sfx_1278.wav \ + sfx_1279.wav \ + sfx_127.wav \ + sfx_1280.wav \ + sfx_1281.wav \ + sfx_1282.wav \ + sfx_1283.wav \ + sfx_1284.wav \ + sfx_1285.wav \ + sfx_1286.wav \ + sfx_1287.wav \ + sfx_1288.wav \ + sfx_1289.wav \ + sfx_128.wav \ + sfx_1290.wav \ + sfx_1291.wav \ + sfx_1292.wav \ + sfx_1293.wav \ + sfx_1294.wav \ + sfx_1295.wav \ + sfx_1296.wav \ + sfx_1297.wav \ + sfx_1298.wav \ + sfx_1299.wav \ + sfx_129.wav \ + sfx_12.wav \ + sfx_1300.wav \ + sfx_1301.wav \ + sfx_1302.wav \ + sfx_1303.wav \ + sfx_1304.wav \ + sfx_1305.wav \ + sfx_1306.wav \ + sfx_1307.wav \ + sfx_1308.wav \ + sfx_1309.wav \ + sfx_130.wav \ + sfx_1310.wav \ + sfx_1311.wav \ + sfx_1312.wav \ + sfx_1313.wav \ + sfx_1314.wav \ + sfx_1315.wav \ + sfx_1316.wav \ + sfx_1317.wav \ + sfx_1318.wav \ + sfx_1319.wav \ + sfx_131.wav \ + sfx_1320.wav \ + sfx_1321.wav \ + sfx_1322.wav \ + sfx_1323.wav \ + sfx_1324.wav \ + sfx_1325.wav \ + sfx_1326.wav \ + sfx_1327.wav \ + sfx_1328.wav \ + sfx_1329.wav \ + sfx_132.wav \ + sfx_1330.wav \ + sfx_1331.wav \ + sfx_1332.wav \ + sfx_1333.wav \ + sfx_1334.wav \ + sfx_1335.wav \ + sfx_1336.wav \ + sfx_1337.wav \ + sfx_1338.wav \ + sfx_1339.wav \ + sfx_133.wav \ + sfx_1340.wav \ + sfx_1341.wav \ + sfx_1342.wav \ + sfx_1343.wav \ + sfx_1344.wav \ + sfx_1345.wav \ + sfx_1346.wav \ + sfx_1347.wav \ + sfx_1348.wav \ + sfx_1349.wav \ + sfx_134.wav \ + sfx_1350.wav \ + sfx_1351.wav \ + sfx_1352.wav \ + sfx_1353.wav \ + sfx_1354.wav \ + sfx_1355.wav \ + sfx_1356.wav \ + sfx_1357.wav \ + sfx_1358.wav \ + sfx_1359.wav \ + sfx_135.wav \ + sfx_1360.wav \ + sfx_1361.wav \ + sfx_1362.wav \ + sfx_1363.wav \ + sfx_1364.wav \ + sfx_1365.wav \ + sfx_1366.wav \ + sfx_1367.wav \ + sfx_1368.wav \ + sfx_1369.wav \ + sfx_136.wav \ + sfx_1370.wav \ + sfx_1371.wav \ + sfx_1372.wav \ + sfx_1373.wav \ + sfx_1374.wav \ + sfx_1375.wav \ + sfx_1376.wav \ + sfx_1377.wav \ + sfx_1378.wav \ + sfx_1379.wav \ + sfx_137.wav \ + sfx_1380.wav \ + sfx_1381.wav \ + sfx_1382.wav \ + sfx_1383.wav \ + sfx_1384.wav \ + sfx_1385.wav \ + sfx_1386.wav \ + sfx_1387.wav \ + sfx_1388.wav \ + sfx_1389.wav \ + sfx_138.wav \ + sfx_1390.wav \ + sfx_1391.wav \ + sfx_1392.wav \ + sfx_1393.wav \ + sfx_1394.wav \ + sfx_1395.wav \ + sfx_1396.wav \ + sfx_1397.wav \ + sfx_1398.wav \ + sfx_1399.wav \ + sfx_139.wav \ + sfx_13.wav \ + sfx_1400.wav \ + sfx_1401.wav \ + sfx_1402.wav \ + sfx_1403.wav \ + sfx_1404.wav \ + sfx_1405.wav \ + sfx_1406.wav \ + sfx_1407.wav \ + sfx_1408.wav \ + sfx_1409.wav \ + sfx_140.wav \ + sfx_1410.wav \ + sfx_1411.wav \ + sfx_1412.wav \ + sfx_1413.wav \ + sfx_1414.wav \ + sfx_1415.wav \ + sfx_1416.wav \ + sfx_1417.wav \ + sfx_1418.wav \ + sfx_1419.wav \ + sfx_141.wav \ + sfx_1420.wav \ + sfx_1421.wav \ + sfx_1422.wav \ + sfx_1423.wav \ + sfx_1424.wav \ + sfx_1425.wav \ + sfx_1426.wav \ + sfx_1427.wav \ + sfx_1428.wav \ + sfx_1429.wav \ + sfx_142.wav \ + sfx_1430.wav \ + sfx_1431.wav \ + sfx_1432.wav \ + sfx_1433.wav \ + sfx_1434.wav \ + sfx_1435.wav \ + sfx_1436.wav \ + sfx_1437.wav \ + sfx_1438.wav \ + sfx_1439.wav \ + sfx_143.wav \ + sfx_1440.wav \ + sfx_1441.wav \ + sfx_1442.wav \ + sfx_1443.wav \ + sfx_1444.wav \ + sfx_1445.wav \ + sfx_1446.wav \ + sfx_1447.wav \ + sfx_1448.wav \ + sfx_1449.wav \ + sfx_144.wav \ + sfx_1450.wav \ + sfx_1451.wav \ + sfx_1452.wav \ + sfx_1453.wav \ + sfx_1454.wav \ + sfx_1455.wav \ + sfx_1456.wav \ + sfx_1457.wav \ + sfx_1458.wav \ + sfx_1459.wav \ + sfx_145.wav \ + sfx_1460.wav \ + sfx_1461.wav \ + sfx_1462.wav \ + sfx_1463.wav \ + sfx_1464.wav \ + sfx_1465.wav \ + sfx_1466.wav \ + sfx_1467.wav \ + sfx_1468.wav \ + sfx_1469.wav \ + sfx_146.wav \ + sfx_1470.wav \ + sfx_1471.wav \ + sfx_1472.wav \ + sfx_1473.wav \ + sfx_1474.wav \ + sfx_1475.wav \ + sfx_1476.wav \ + sfx_1477.wav \ + sfx_1478.wav \ + sfx_1479.wav \ + sfx_147.wav \ + sfx_1480.wav \ + sfx_1481.wav \ + sfx_1482.wav \ + sfx_1483.wav \ + sfx_1484.wav \ + sfx_1485.wav \ + sfx_1486.wav \ + sfx_1487.wav \ + sfx_1488.wav \ + sfx_1489.wav \ + sfx_148.wav \ + sfx_1490.wav \ + sfx_1491.wav \ + sfx_1492.wav \ + sfx_1493.wav \ + sfx_1494.wav \ + sfx_1495.wav \ + sfx_1496.wav \ + sfx_1497.wav \ + sfx_1498.wav \ + sfx_1499.wav \ + sfx_149.wav \ + sfx_14.wav \ + sfx_1500.wav \ + sfx_1501.wav \ + sfx_1502.wav \ + sfx_1503.wav \ + sfx_1504.wav \ + sfx_1505.wav \ + sfx_1506.wav \ + sfx_1507.wav \ + sfx_1508.wav \ + sfx_1509.wav \ + sfx_150.wav \ + sfx_1510.wav \ + sfx_1511.wav \ + sfx_1512.wav \ + sfx_1513.wav \ + sfx_1514.wav \ + sfx_1515.wav \ + sfx_1516.wav \ + sfx_1517.wav \ + sfx_1518.wav \ + sfx_1519.wav \ + sfx_151.wav \ + sfx_1520.wav \ + sfx_1521.wav \ + sfx_1522.wav \ + sfx_1523.wav \ + sfx_1524.wav \ + sfx_1525.wav \ + sfx_1526.wav \ + sfx_1527.wav \ + sfx_1528.wav \ + sfx_1529.wav \ + sfx_152.wav \ + sfx_1530.wav \ + sfx_1531.wav \ + sfx_1532.wav \ + sfx_1533.wav \ + sfx_1534.wav \ + sfx_1535.wav \ + sfx_1536.wav \ + sfx_1537.wav \ + sfx_1538.wav \ + sfx_1539.wav \ + sfx_153.wav \ + sfx_1540.wav \ + sfx_1541.wav \ + sfx_1542.wav \ + sfx_1543.wav \ + sfx_1544.wav \ + sfx_1545.wav \ + sfx_1546.wav \ + sfx_1547.wav \ + sfx_1548.wav \ + sfx_1549.wav \ + sfx_154.wav \ + sfx_1550.wav \ + sfx_1551.wav \ + sfx_1552.wav \ + sfx_1553.wav \ + sfx_1554.wav \ + sfx_1555.wav \ + sfx_1556.wav \ + sfx_1557.wav \ + sfx_1558.wav \ + sfx_1559.wav \ + sfx_155.wav \ + sfx_1560.wav \ + sfx_1561.wav \ + sfx_1562.wav \ + sfx_1563.wav \ + sfx_1564.wav \ + sfx_1565.wav \ + sfx_1566.wav \ + sfx_1567.wav \ + sfx_1568.wav \ + sfx_1569.wav \ + sfx_156.wav \ + sfx_1570.wav \ + sfx_1571.wav \ + sfx_1572.wav \ + sfx_1573.wav \ + sfx_1574.wav \ + sfx_1575.wav \ + sfx_1576.wav \ + sfx_1577.wav \ + sfx_1578.wav \ + sfx_1579.wav \ + sfx_157.wav \ + sfx_1580.wav \ + sfx_1581.wav \ + sfx_1582.wav \ + sfx_1583.wav \ + sfx_1584.wav \ + sfx_1585.wav \ + sfx_1586.wav \ + sfx_1587.wav \ + sfx_1588.wav \ + sfx_1589.wav \ + sfx_158.wav \ + sfx_1590.wav \ + sfx_1591.wav \ + sfx_1592.wav \ + sfx_1593.wav \ + sfx_1594.wav \ + sfx_1595.wav \ + sfx_1596.wav \ + sfx_1597.wav \ + sfx_1598.wav \ + sfx_1599.wav \ + sfx_159.wav \ + sfx_15.wav \ + sfx_1600.wav \ + sfx_1601.wav \ + sfx_1602.wav \ + sfx_1603.wav \ + sfx_1604.wav \ + sfx_1605.wav \ + sfx_1606.wav \ + sfx_1607.wav \ + sfx_1608.wav \ + sfx_1609.wav \ + sfx_160.wav \ + sfx_1610.wav \ + sfx_1611.wav \ + sfx_1612.wav \ + sfx_1613.wav \ + sfx_1614.wav \ + sfx_1615.wav \ + sfx_1616.wav \ + sfx_1617.wav \ + sfx_1618.wav \ + sfx_1619.wav \ + sfx_161.wav \ + sfx_1620.wav \ + sfx_1621.wav \ + sfx_1622.wav \ + sfx_1623.wav \ + sfx_1624.wav \ + sfx_1625.wav \ + sfx_1626.wav \ + sfx_1627.wav \ + sfx_1628.wav \ + sfx_1629.wav \ + sfx_162.wav \ + sfx_1630.wav \ + sfx_1631.wav \ + sfx_1632.wav \ + sfx_1633.wav \ + sfx_1634.wav \ + sfx_1635.wav \ + sfx_1636.wav \ + sfx_1637.wav \ + sfx_1638.wav \ + sfx_1639.wav \ + sfx_163.wav \ + sfx_1640.wav \ + sfx_1641.wav \ + sfx_1642.wav \ + sfx_1643.wav \ + sfx_1644.wav \ + sfx_1645.wav \ + sfx_1646.wav \ + sfx_1647.wav \ + sfx_1648.wav \ + sfx_1649.wav \ + sfx_164.wav \ + sfx_1650.wav \ + sfx_1651.wav \ + sfx_1652.wav \ + sfx_1653.wav \ + sfx_1654.wav \ + sfx_1655.wav \ + sfx_1656.wav \ + sfx_1657.wav \ + sfx_1658.wav \ + sfx_1659.wav \ + sfx_165.wav \ + sfx_1660.wav \ + sfx_1661.wav \ + sfx_1662.wav \ + sfx_1663.wav \ + sfx_1664.wav \ + sfx_1665.wav \ + sfx_1666.wav \ + sfx_1667.wav \ + sfx_1668.wav \ + sfx_1669.wav \ + sfx_166.wav \ + sfx_1670.wav \ + sfx_1671.wav \ + sfx_1672.wav \ + sfx_1673.wav \ + sfx_1674.wav \ + sfx_1675.wav \ + sfx_1676.wav \ + sfx_1677.wav \ + sfx_1678.wav \ + sfx_1679.wav \ + sfx_167.wav \ + sfx_1680.wav \ + sfx_1681.wav \ + sfx_1682.wav \ + sfx_1683.wav \ + sfx_1684.wav \ + sfx_1685.wav \ + sfx_1686.wav \ + sfx_1687.wav \ + sfx_1688.wav \ + sfx_1689.wav \ + sfx_168.wav \ + sfx_1690.wav \ + sfx_1691.wav \ + sfx_1692.wav \ + sfx_1693.wav \ + sfx_1694.wav \ + sfx_1695.wav \ + sfx_1696.wav \ + sfx_1697.wav \ + sfx_1698.wav \ + sfx_1699.wav \ + sfx_169.wav \ + sfx_16.wav \ + sfx_1700.wav \ + sfx_1701.wav \ + sfx_1702.wav \ + sfx_1703.wav \ + sfx_1704.wav \ + sfx_1705.wav \ + sfx_1706.wav \ + sfx_1707.wav \ + sfx_1708.wav \ + sfx_1709.wav \ + sfx_170.wav \ + sfx_1710.wav \ + sfx_1711.wav \ + sfx_1712.wav \ + sfx_1713.wav \ + sfx_1714.wav \ + sfx_1715.wav \ + sfx_1716.wav \ + sfx_1717.wav \ + sfx_1718.wav \ + sfx_1719.wav \ + sfx_171.wav \ + sfx_1720.wav \ + sfx_1721.wav \ + sfx_1722.wav \ + sfx_1723.wav \ + sfx_1724.wav \ + sfx_1725.wav \ + sfx_1726.wav \ + sfx_1727.wav \ + sfx_1728.wav \ + sfx_1729.wav \ + sfx_172.wav \ + sfx_1730.wav \ + sfx_1731.wav \ + sfx_1732.wav \ + sfx_1733.wav \ + sfx_1734.wav \ + sfx_1735.wav \ + sfx_1736.wav \ + sfx_1737.wav \ + sfx_1738.wav \ + sfx_1739.wav \ + sfx_173.wav \ + sfx_1740.wav \ + sfx_1741.wav \ + sfx_1742.wav \ + sfx_1743.wav \ + sfx_1744.wav \ + sfx_1745.wav \ + sfx_1746.wav \ + sfx_1747.wav \ + sfx_1748.wav \ + sfx_1749.wav \ + sfx_174.wav \ + sfx_1750.wav \ + sfx_1751.wav \ + sfx_1752.wav \ + sfx_1753.wav \ + sfx_1754.wav \ + sfx_1755.wav \ + sfx_1756.wav \ + sfx_1757.wav \ + sfx_1758.wav \ + sfx_1759.wav \ + sfx_175.wav \ + sfx_1760.wav \ + sfx_1761.wav \ + sfx_1762.wav \ + sfx_1763.wav \ + sfx_1764.wav \ + sfx_1765.wav \ + sfx_1766.wav \ + sfx_1767.wav \ + sfx_1768.wav \ + sfx_1769.wav \ + sfx_176.wav \ + sfx_1770.wav \ + sfx_1771.wav \ + sfx_1772.wav \ + sfx_1773.wav \ + sfx_1774.wav \ + sfx_1775.wav \ + sfx_1776.wav \ + sfx_1777.wav \ + sfx_1778.wav \ + sfx_1779.wav \ + sfx_177.wav \ + sfx_1780.wav \ + sfx_1781.wav \ + sfx_1782.wav \ + sfx_1783.wav \ + sfx_1784.wav \ + sfx_1785.wav \ + sfx_1786.wav \ + sfx_1787.wav \ + sfx_1788.wav \ + sfx_1789.wav \ + sfx_178.wav \ + sfx_1790.wav \ + sfx_1791.wav \ + sfx_1792.wav \ + sfx_1793.wav \ + sfx_1794.wav \ + sfx_1795.wav \ + sfx_1796.wav \ + sfx_1797.wav \ + sfx_1798.wav \ + sfx_1799.wav \ + sfx_179.wav \ + sfx_17.wav \ + sfx_1800.wav \ + sfx_1801.wav \ + sfx_1802.wav \ + sfx_1803.wav \ + sfx_1804.wav \ + sfx_1805.wav \ + sfx_1806.wav \ + sfx_1807.wav \ + sfx_1808.wav \ + sfx_1809.wav \ + sfx_180.wav \ + sfx_1810.wav \ + sfx_1811.wav \ + sfx_1812.wav \ + sfx_1813.wav \ + sfx_1814.wav \ + sfx_1815.wav \ + sfx_1816.wav \ + sfx_1817.wav \ + sfx_1818.wav \ + sfx_1819.wav \ + sfx_181.wav \ + sfx_1820.wav \ + sfx_1821.wav \ + sfx_1822.wav \ + sfx_1823.wav \ + sfx_1824.wav \ + sfx_1825.wav \ + sfx_1826.wav \ + sfx_1827.wav \ + sfx_1828.wav \ + sfx_1829.wav \ + sfx_182.wav \ + sfx_1830.wav \ + sfx_1831.wav \ + sfx_1832.wav \ + sfx_1833.wav \ + sfx_1834.wav \ + sfx_1835.wav \ + sfx_1836.wav \ + sfx_1837.wav \ + sfx_1838.wav \ + sfx_1839.wav \ + sfx_183.wav \ + sfx_1840.wav \ + sfx_1841.wav \ + sfx_1842.wav \ + sfx_1843.wav \ + sfx_1844.wav \ + sfx_1845.wav \ + sfx_1846.wav \ + sfx_1847.wav \ + sfx_1848.wav \ + sfx_1849.wav \ + sfx_184.wav \ + sfx_1850.wav \ + sfx_1851.wav \ + sfx_1852.wav \ + sfx_1853.wav \ + sfx_1854.wav \ + sfx_1855.wav \ + sfx_1856.wav \ + sfx_1857.wav \ + sfx_1858.wav \ + sfx_1859.wav \ + sfx_185.wav \ + sfx_1860.wav \ + sfx_1861.wav \ + sfx_1862.wav \ + sfx_1863.wav \ + sfx_1864.wav \ + sfx_1865.wav \ + sfx_1866.wav \ + sfx_1867.wav \ + sfx_1868.wav \ + sfx_1869.wav \ + sfx_186.wav \ + sfx_1870.wav \ + sfx_1871.wav \ + sfx_1872.wav \ + sfx_1873.wav \ + sfx_1874.wav \ + sfx_1875.wav \ + sfx_1876.wav \ + sfx_1877.wav \ + sfx_1878.wav \ + sfx_1879.wav \ + sfx_187.wav \ + sfx_1880.wav \ + sfx_1881.wav \ + sfx_1882.wav \ + sfx_1883.wav \ + sfx_1884.wav \ + sfx_1885.wav \ + sfx_1886.wav \ + sfx_1887.wav \ + sfx_1888.wav \ + sfx_1889.wav \ + sfx_188.wav \ + sfx_1890.wav \ + sfx_1891.wav \ + sfx_1892.wav \ + sfx_1893.wav \ + sfx_1894.wav \ + sfx_1895.wav \ + sfx_1896.wav \ + sfx_1897.wav \ + sfx_1898.wav \ + sfx_1899.wav \ + sfx_189.wav \ + sfx_18.wav \ + sfx_1900.wav \ + sfx_1901.wav \ + sfx_1902.wav \ + sfx_1903.wav \ + sfx_1904.wav \ + sfx_1905.wav \ + sfx_1906.wav \ + sfx_1907.wav \ + sfx_1908.wav \ + sfx_1909.wav \ + sfx_190.wav \ + sfx_1910.wav \ + sfx_1911.wav \ + sfx_1912.wav \ + sfx_1913.wav \ + sfx_1914.wav \ + sfx_1915.wav \ + sfx_1916.wav \ + sfx_1917.wav \ + sfx_1918.wav \ + sfx_1919.wav \ + sfx_191.wav \ + sfx_1920.wav \ + sfx_1921.wav \ + sfx_1922.wav \ + sfx_1923.wav \ + sfx_1924.wav \ + sfx_1925.wav \ + sfx_1926.wav \ + sfx_1927.wav \ + sfx_1928.wav \ + sfx_1929.wav \ + sfx_192.wav \ + sfx_1930.wav \ + sfx_1931.wav \ + sfx_1932.wav \ + sfx_1933.wav \ + sfx_1934.wav \ + sfx_1935.wav \ + sfx_1936.wav \ + sfx_1937.wav \ + sfx_1938.wav \ + sfx_1939.wav \ + sfx_193.wav \ + sfx_1940.wav \ + sfx_1941.wav \ + sfx_1942.wav \ + sfx_1943.wav \ + sfx_1944.wav \ + sfx_1945.wav \ + sfx_1946.wav \ + sfx_1947.wav \ + sfx_1948.wav \ + sfx_1949.wav \ + sfx_194.wav \ + sfx_1950.wav \ + sfx_1951.wav \ + sfx_1952.wav \ + sfx_1953.wav \ + sfx_1954.wav \ + sfx_1955.wav \ + sfx_1956.wav \ + sfx_1957.wav \ + sfx_1958.wav \ + sfx_1959.wav \ + sfx_195.wav \ + sfx_1960.wav \ + sfx_1961.wav \ + sfx_1962.wav \ + sfx_1963.wav \ + sfx_1964.wav \ + sfx_1965.wav \ + sfx_1966.wav \ + sfx_1967.wav \ + sfx_1968.wav \ + sfx_1969.wav \ + sfx_196.wav \ + sfx_1970.wav \ + sfx_1971.wav \ + sfx_1972.wav \ + sfx_1973.wav \ + sfx_1974.wav \ + sfx_1975.wav \ + sfx_1976.wav \ + sfx_1977.wav \ + sfx_1978.wav \ + sfx_1979.wav \ + sfx_197.wav \ + sfx_1980.wav \ + sfx_1981.wav \ + sfx_1982.wav \ + sfx_1983.wav \ + sfx_1984.wav \ + sfx_1985.wav \ + sfx_1986.wav \ + sfx_1987.wav \ + sfx_1988.wav \ + sfx_1989.wav \ + sfx_198.wav \ + sfx_1990.wav \ + sfx_1991.wav \ + sfx_1992.wav \ + sfx_1993.wav \ + sfx_1994.wav \ + sfx_1995.wav \ + sfx_1996.wav \ + sfx_1997.wav \ + sfx_1998.wav \ + sfx_1999.wav \ + sfx_199.wav \ + sfx_19.wav \ + sfx_1.wav \ + sfx_2000.wav \ + sfx_2001.wav \ + sfx_2002.wav \ + sfx_2003.wav \ + sfx_2004.wav \ + sfx_2005.wav \ + sfx_2006.wav \ + sfx_2007.wav \ + sfx_2008.wav \ + sfx_2009.wav \ + sfx_200.wav \ + sfx_2010.wav \ + sfx_2011.wav \ + sfx_2012.wav \ + sfx_2013.wav \ + sfx_2014.wav \ + sfx_2015.wav \ + sfx_2016.wav \ + sfx_2017.wav \ + sfx_2018.wav \ + sfx_2019.wav \ + sfx_201.wav \ + sfx_2020.wav \ + sfx_2021.wav \ + sfx_2022.wav \ + sfx_2023.wav \ + sfx_2024.wav \ + sfx_2025.wav \ + sfx_2026.wav \ + sfx_2027.wav \ + sfx_2028.wav \ + sfx_2029.wav \ + sfx_202.wav \ + sfx_2030.wav \ + sfx_2031.wav \ + sfx_2032.wav \ + sfx_2033.wav \ + sfx_2034.wav \ + sfx_2035.wav \ + sfx_2036.wav \ + sfx_2037.wav \ + sfx_2038.wav \ + sfx_2039.wav \ + sfx_203.wav \ + sfx_2040.wav \ + sfx_2041.wav \ + sfx_2042.wav \ + sfx_2043.wav \ + sfx_2044.wav \ + sfx_2045.wav \ + sfx_2046.wav \ + sfx_2047.wav \ + sfx_2048.wav \ + sfx_2049.wav \ + sfx_204.wav \ + sfx_2050.wav \ + sfx_2051.wav \ + sfx_2052.wav \ + sfx_2053.wav \ + sfx_2054.wav \ + sfx_2055.wav \ + sfx_2056.wav \ + sfx_2057.wav \ + sfx_2058.wav \ + sfx_2059.wav \ + sfx_205.wav \ + sfx_2060.wav \ + sfx_2061.wav \ + sfx_2062.wav \ + sfx_2063.wav \ + sfx_2064.wav \ + sfx_2065.wav \ + sfx_2066.wav \ + sfx_2067.wav \ + sfx_2068.wav \ + sfx_2069.wav \ + sfx_206.wav \ + sfx_2070.wav \ + sfx_2071.wav \ + sfx_2072.wav \ + sfx_2073.wav \ + sfx_2074.wav \ + sfx_2075.wav \ + sfx_2076.wav \ + sfx_2077.wav \ + sfx_2078.wav \ + sfx_2079.wav \ + sfx_207.wav \ + sfx_2080.wav \ + sfx_2081.wav \ + sfx_2082.wav \ + sfx_2083.wav \ + sfx_2084.wav \ + sfx_2085.wav \ + sfx_2086.wav \ + sfx_2087.wav \ + sfx_2088.wav \ + sfx_2089.wav \ + sfx_208.wav \ + sfx_2090.wav \ + sfx_2091.wav \ + sfx_2092.wav \ + sfx_2093.wav \ + sfx_2094.wav \ + sfx_2095.wav \ + sfx_2096.wav \ + sfx_2097.wav \ + sfx_2098.wav \ + sfx_2099.wav \ + sfx_209.wav \ + sfx_20.wav \ + sfx_2100.wav \ + sfx_2101.wav \ + sfx_2102.wav \ + sfx_2103.wav \ + sfx_2104.wav \ + sfx_2105.wav \ + sfx_2106.wav \ + sfx_2107.wav \ + sfx_2108.wav \ + sfx_2109.wav \ + sfx_210.wav \ + sfx_2110.wav \ + sfx_2111.wav \ + sfx_2112.wav \ + sfx_2113.wav \ + sfx_2114.wav \ + sfx_2115.wav \ + sfx_2116.wav \ + sfx_2117.wav \ + sfx_2118.wav \ + sfx_2119.wav \ + sfx_211.wav \ + sfx_2120.wav \ + sfx_2121.wav \ + sfx_2122.wav \ + sfx_2123.wav \ + sfx_2124.wav \ + sfx_2125.wav \ + sfx_2126.wav \ + sfx_2127.wav \ + sfx_2128.wav \ + sfx_2129.wav \ + sfx_212.wav \ + sfx_2130.wav \ + sfx_2131.wav \ + sfx_2132.wav \ + sfx_2133.wav \ + sfx_2134.wav \ + sfx_2135.wav \ + sfx_2136.wav \ + sfx_2137.wav \ + sfx_2138.wav \ + sfx_2139.wav \ + sfx_213.wav \ + sfx_2140.wav \ + sfx_2141.wav \ + sfx_2142.wav \ + sfx_2143.wav \ + sfx_2144.wav \ + sfx_2145.wav \ + sfx_2146.wav \ + sfx_2147.wav \ + sfx_2148.wav \ + sfx_2149.wav \ + sfx_214.wav \ + sfx_2150.wav \ + sfx_2151.wav \ + sfx_2152.wav \ + sfx_2153.wav \ + sfx_2154.wav \ + sfx_2155.wav \ + sfx_2156.wav \ + sfx_2157.wav \ + sfx_2158.wav \ + sfx_2159.wav \ + sfx_215.wav \ + sfx_2160.wav \ + sfx_2161.wav \ + sfx_2162.wav \ + sfx_2163.wav \ + sfx_2164.wav \ + sfx_2165.wav \ + sfx_2166.wav \ + sfx_2167.wav \ + sfx_2168.wav \ + sfx_2169.wav \ + sfx_216.wav \ + sfx_2170.wav \ + sfx_2171.wav \ + sfx_2172.wav \ + sfx_2173.wav \ + sfx_2174.wav \ + sfx_2175.wav \ + sfx_2176.wav \ + sfx_2177.wav \ + sfx_2178.wav \ + sfx_2179.wav \ + sfx_217.wav \ + sfx_2180.wav \ + sfx_2181.wav \ + sfx_2182.wav \ + sfx_2183.wav \ + sfx_2184.wav \ + sfx_2185.wav \ + sfx_2186.wav \ + sfx_2187.wav \ + sfx_2188.wav \ + sfx_2189.wav \ + sfx_218.wav \ + sfx_2190.wav \ + sfx_2191.wav \ + sfx_2192.wav \ + sfx_2193.wav \ + sfx_2194.wav \ + sfx_2195.wav \ + sfx_2196.wav \ + sfx_2197.wav \ + sfx_2198.wav \ + sfx_2199.wav \ + sfx_219.wav \ + sfx_21.wav \ + sfx_2200.wav \ + sfx_2201.wav \ + sfx_2202.wav \ + sfx_2203.wav \ + sfx_2204.wav \ + sfx_2205.wav \ + sfx_2206.wav \ + sfx_2207.wav \ + sfx_2208.wav \ + sfx_2209.wav \ + sfx_220.wav \ + sfx_2210.wav \ + sfx_2211.wav \ + sfx_2212.wav \ + sfx_2213.wav \ + sfx_2214.wav \ + sfx_2215.wav \ + sfx_2216.wav \ + sfx_2217.wav \ + sfx_2218.wav \ + sfx_2219.wav \ + sfx_221.wav \ + sfx_2220.wav \ + sfx_2221.wav \ + sfx_2222.wav \ + sfx_2223.wav \ + sfx_2224.wav \ + sfx_2225.wav \ + sfx_2226.wav \ + sfx_2227.wav \ + sfx_2228.wav \ + sfx_2229.wav \ + sfx_222.wav \ + sfx_2230.wav \ + sfx_2231.wav \ + sfx_2232.wav \ + sfx_2233.wav \ + sfx_2234.wav \ + sfx_2235.wav \ + sfx_2236.wav \ + sfx_2237.wav \ + sfx_2238.wav \ + sfx_2239.wav \ + sfx_223.wav \ + sfx_2240.wav \ + sfx_2241.wav \ + sfx_2242.wav \ + sfx_2243.wav \ + sfx_2244.wav \ + sfx_2245.wav \ + sfx_2246.wav \ + sfx_2247.wav \ + sfx_2248.wav \ + sfx_2249.wav \ + sfx_224.wav \ + sfx_2250.wav \ + sfx_2251.wav \ + sfx_2252.wav \ + sfx_2253.wav \ + sfx_2254.wav \ + sfx_2255.wav \ + sfx_2256.wav \ + sfx_2257.wav \ + sfx_2258.wav \ + sfx_2259.wav \ + sfx_225.wav \ + sfx_2260.wav \ + sfx_2261.wav \ + sfx_2262.wav \ + sfx_2263.wav \ + sfx_2264.wav \ + sfx_2265.wav \ + sfx_2266.wav \ + sfx_2267.wav \ + sfx_2268.wav \ + sfx_2269.wav \ + sfx_226.wav \ + sfx_2270.wav \ + sfx_2271.wav \ + sfx_2272.wav \ + sfx_2273.wav \ + sfx_2274.wav \ + sfx_2275.wav \ + sfx_2276.wav \ + sfx_2277.wav \ + sfx_2278.wav \ + sfx_2279.wav \ + sfx_227.wav \ + sfx_2280.wav \ + sfx_2281.wav \ + sfx_2282.wav \ + sfx_2283.wav \ + sfx_2284.wav \ + sfx_2285.wav \ + sfx_2286.wav \ + sfx_2287.wav \ + sfx_2288.wav \ + sfx_2289.wav \ + sfx_228.wav \ + sfx_2290.wav \ + sfx_2291.wav \ + sfx_2292.wav \ + sfx_2293.wav \ + sfx_2294.wav \ + sfx_2295.wav \ + sfx_2296.wav \ + sfx_2297.wav \ + sfx_2298.wav \ + sfx_2299.wav \ + sfx_229.wav \ + sfx_22.wav \ + sfx_2300.wav \ + sfx_2301.wav \ + sfx_2302.wav \ + sfx_2303.wav \ + sfx_2304.wav \ + sfx_2305.wav \ + sfx_2306.wav \ + sfx_2307.wav \ + sfx_2308.wav \ + sfx_2309.wav \ + sfx_230.wav \ + sfx_2310.wav \ + sfx_2311.wav \ + sfx_2312.wav \ + sfx_2313.wav \ + sfx_2314.wav \ + sfx_2315.wav \ + sfx_2316.wav \ + sfx_2317.wav \ + sfx_2318.wav \ + sfx_2319.wav \ + sfx_231.wav \ + sfx_2320.wav \ + sfx_2321.wav \ + sfx_2322.wav \ + sfx_2323.wav \ + sfx_2324.wav \ + sfx_2325.wav \ + sfx_2326.wav \ + sfx_2327.wav \ + sfx_2328.wav \ + sfx_2329.wav \ + sfx_232.wav \ + sfx_2330.wav \ + sfx_2331.wav \ + sfx_2332.wav \ + sfx_2333.wav \ + sfx_2334.wav \ + sfx_2335.wav \ + sfx_2336.wav \ + sfx_2337.wav \ + sfx_2338.wav \ + sfx_2339.wav \ + sfx_233.wav \ + sfx_2340.wav \ + sfx_2341.wav \ + sfx_2342.wav \ + sfx_2343.wav \ + sfx_2344.wav \ + sfx_2345.wav \ + sfx_2346.wav \ + sfx_2347.wav \ + sfx_2348.wav \ + sfx_2349.wav \ + sfx_234.wav \ + sfx_2350.wav \ + sfx_2351.wav \ + sfx_2352.wav \ + sfx_2353.wav \ + sfx_2354.wav \ + sfx_2355.wav \ + sfx_2356.wav \ + sfx_2357.wav \ + sfx_2358.wav \ + sfx_2359.wav \ + sfx_235.wav \ + sfx_2360.wav \ + sfx_2361.wav \ + sfx_2362.wav \ + sfx_2363.wav \ + sfx_2364.wav \ + sfx_2365.wav \ + sfx_2366.wav \ + sfx_2367.wav \ + sfx_2368.wav \ + sfx_2369.wav \ + sfx_236.wav \ + sfx_2370.wav \ + sfx_2371.wav \ + sfx_2372.wav \ + sfx_2373.wav \ + sfx_2374.wav \ + sfx_2375.wav \ + sfx_2376.wav \ + sfx_2377.wav \ + sfx_2378.wav \ + sfx_2379.wav \ + sfx_237.wav \ + sfx_2380.wav \ + sfx_2381.wav \ + sfx_2382.wav \ + sfx_2383.wav \ + sfx_2384.wav \ + sfx_2385.wav \ + sfx_2386.wav \ + sfx_2387.wav \ + sfx_2388.wav \ + sfx_2389.wav \ + sfx_238.wav \ + sfx_2390.wav \ + sfx_2391.wav \ + sfx_2392.wav \ + sfx_2393.wav \ + sfx_2394.wav \ + sfx_2395.wav \ + sfx_2396.wav \ + sfx_2397.wav \ + sfx_2398.wav \ + sfx_2399.wav \ + sfx_239.wav \ + sfx_23.wav \ + sfx_2400.wav \ + sfx_2401.wav \ + sfx_2402.wav \ + sfx_2403.wav \ + sfx_2404.wav \ + sfx_2405.wav \ + sfx_2406.wav \ + sfx_2407.wav \ + sfx_2408.wav \ + sfx_2409.wav \ + sfx_240.wav \ + sfx_2410.wav \ + sfx_2411.wav \ + sfx_2412.wav \ + sfx_2413.wav \ + sfx_2414.wav \ + sfx_2415.wav \ + sfx_2416.wav \ + sfx_2417.wav \ + sfx_2418.wav \ + sfx_2419.wav \ + sfx_241.wav \ + sfx_2420.wav \ + sfx_2421.wav \ + sfx_2422.wav \ + sfx_2423.wav \ + sfx_2424.wav \ + sfx_2425.wav \ + sfx_2426.wav \ + sfx_2427.wav \ + sfx_2428.wav \ + sfx_2429.wav \ + sfx_242.wav \ + sfx_2430.wav \ + sfx_2431.wav \ + sfx_2432.wav \ + sfx_2433.wav \ + sfx_2434.wav \ + sfx_2435.wav \ + sfx_2436.wav \ + sfx_2437.wav \ + sfx_2438.wav \ + sfx_2439.wav \ + sfx_243.wav \ + sfx_2440.wav \ + sfx_2441.wav \ + sfx_2442.wav \ + sfx_2443.wav \ + sfx_2444.wav \ + sfx_2445.wav \ + sfx_2446.wav \ + sfx_2447.wav \ + sfx_2448.wav \ + sfx_2449.wav \ + sfx_244.wav \ + sfx_2450.wav \ + sfx_2451.wav \ + sfx_2452.wav \ + sfx_2453.wav \ + sfx_2454.wav \ + sfx_2455.wav \ + sfx_2456.wav \ + sfx_2457.wav \ + sfx_2458.wav \ + sfx_2459.wav \ + sfx_245.wav \ + sfx_2460.wav \ + sfx_2461.wav \ + sfx_2462.wav \ + sfx_2463.wav \ + sfx_2464.wav \ + sfx_2465.wav \ + sfx_2466.wav \ + sfx_2467.wav \ + sfx_2468.wav \ + sfx_2469.wav \ + sfx_246.wav \ + sfx_2470.wav \ + sfx_2471.wav \ + sfx_2472.wav \ + sfx_2473.wav \ + sfx_2474.wav \ + sfx_2475.wav \ + sfx_2476.wav \ + sfx_2477.wav \ + sfx_2478.wav \ + sfx_2479.wav \ + sfx_247.wav \ + sfx_2480.wav \ + sfx_2481.wav \ + sfx_2482.wav \ + sfx_2483.wav \ + sfx_2484.wav \ + sfx_2485.wav \ + sfx_2486.wav \ + sfx_2487.wav \ + sfx_2488.wav \ + sfx_2489.wav \ + sfx_248.wav \ + sfx_2490.wav \ + sfx_2491.wav \ + sfx_2492.wav \ + sfx_2493.wav \ + sfx_2494.wav \ + sfx_2495.wav \ + sfx_2496.wav \ + sfx_2497.wav \ + sfx_2498.wav \ + sfx_2499.wav \ + sfx_249.wav \ + sfx_24.wav \ + sfx_2500.wav \ + sfx_2501.wav \ + sfx_2502.wav \ + sfx_2503.wav \ + sfx_2504.wav \ + sfx_2505.wav \ + sfx_2506.wav \ + sfx_2507.wav \ + sfx_2508.wav \ + sfx_2509.wav \ + sfx_250.wav \ + sfx_2510.wav \ + sfx_2511.wav \ + sfx_2512.wav \ + sfx_2513.wav \ + sfx_2514.wav \ + sfx_2515.wav \ + sfx_2516.wav \ + sfx_2517.wav \ + sfx_2518.wav \ + sfx_2519.wav \ + sfx_251.wav \ + sfx_2520.wav \ + sfx_2521.wav \ + sfx_2522.wav \ + sfx_2523.wav \ + sfx_2524.wav \ + sfx_2525.wav \ + sfx_2526.wav \ + sfx_2527.wav \ + sfx_2528.wav \ + sfx_2529.wav \ + sfx_252.wav \ + sfx_2530.wav \ + sfx_2531.wav \ + sfx_2532.wav \ + sfx_2533.wav \ + sfx_2534.wav \ + sfx_2535.wav \ + sfx_2536.wav \ + sfx_2537.wav \ + sfx_2538.wav \ + sfx_2539.wav \ + sfx_253.wav \ + sfx_2540.wav \ + sfx_2541.wav \ + sfx_2542.wav \ + sfx_2543.wav \ + sfx_2544.wav \ + sfx_2545.wav \ + sfx_2546.wav \ + sfx_2547.wav \ + sfx_2548.wav \ + sfx_2549.wav \ + sfx_254.wav \ + sfx_2550.wav \ + sfx_2551.wav \ + sfx_2552.wav \ + sfx_2553.wav \ + sfx_2554.wav \ + sfx_2555.wav \ + sfx_2556.wav \ + sfx_2557.wav \ + sfx_2558.wav \ + sfx_2559.wav \ + sfx_255.wav \ + sfx_2560.wav \ + sfx_2561.wav \ + sfx_2562.wav \ + sfx_2563.wav \ + sfx_2564.wav \ + sfx_2565.wav \ + sfx_2566.wav \ + sfx_2567.wav \ + sfx_2568.wav \ + sfx_2569.wav \ + sfx_256.wav \ + sfx_2570.wav \ + sfx_2571.wav \ + sfx_2572.wav \ + sfx_2573.wav \ + sfx_2574.wav \ + sfx_2575.wav \ + sfx_2576.wav \ + sfx_2577.wav \ + sfx_2578.wav \ + sfx_2579.wav \ + sfx_257.wav \ + sfx_2580.wav \ + sfx_2581.wav \ + sfx_2582.wav \ + sfx_2583.wav \ + sfx_2584.wav \ + sfx_2585.wav \ + sfx_2586.wav \ + sfx_2587.wav \ + sfx_2588.wav \ + sfx_2589.wav \ + sfx_258.wav \ + sfx_2590.wav \ + sfx_2591.wav \ + sfx_2592.wav \ + sfx_2593.wav \ + sfx_2594.wav \ + sfx_2595.wav \ + sfx_2596.wav \ + sfx_2597.wav \ + sfx_2598.wav \ + sfx_2599.wav \ + sfx_259.wav \ + sfx_25.wav \ + sfx_2600.wav \ + sfx_2601.wav \ + sfx_2602.wav \ + sfx_2603.wav \ + sfx_2604.wav \ + sfx_2605.wav \ + sfx_2606.wav \ + sfx_2607.wav \ + sfx_2608.wav \ + sfx_2609.wav \ + sfx_260.wav \ + sfx_2610.wav \ + sfx_2611.wav \ + sfx_2612.wav \ + sfx_2613.wav \ + sfx_2614.wav \ + sfx_2615.wav \ + sfx_2616.wav \ + sfx_2617.wav \ + sfx_2618.wav \ + sfx_2619.wav \ + sfx_261.wav \ + sfx_2620.wav \ + sfx_2621.wav \ + sfx_2622.wav \ + sfx_2623.wav \ + sfx_2624.wav \ + sfx_2625.wav \ + sfx_2626.wav \ + sfx_2627.wav \ + sfx_2628.wav \ + sfx_2629.wav \ + sfx_262.wav \ + sfx_2630.wav \ + sfx_2631.wav \ + sfx_2632.wav \ + sfx_2633.wav \ + sfx_2634.wav \ + sfx_2635.wav \ + sfx_2636.wav \ + sfx_2637.wav \ + sfx_2638.wav \ + sfx_2639.wav \ + sfx_263.wav \ + sfx_2640.wav \ + sfx_2641.wav \ + sfx_2642.wav \ + sfx_2643.wav \ + sfx_2644.wav \ + sfx_2645.wav \ + sfx_2646.wav \ + sfx_2647.wav \ + sfx_2648.wav \ + sfx_2649.wav \ + sfx_264.wav \ + sfx_2650.wav \ + sfx_2651.wav \ + sfx_2652.wav \ + sfx_2653.wav \ + sfx_2654.wav \ + sfx_2655.wav \ + sfx_2656.wav \ + sfx_2657.wav \ + sfx_2658.wav \ + sfx_2659.wav \ + sfx_265.wav \ + sfx_2660.wav \ + sfx_2661.wav \ + sfx_2662.wav \ + sfx_2663.wav \ + sfx_2664.wav \ + sfx_2665.wav \ + sfx_2666.wav \ + sfx_2667.wav \ + sfx_2668.wav \ + sfx_2669.wav \ + sfx_266.wav \ + sfx_2670.wav \ + sfx_2671.wav \ + sfx_2672.wav \ + sfx_2673.wav \ + sfx_2674.wav \ + sfx_2675.wav \ + sfx_2676.wav \ + sfx_2677.wav \ + sfx_2678.wav \ + sfx_2679.wav \ + sfx_267.wav \ + sfx_2680.wav \ + sfx_2681.wav \ + sfx_2682.wav \ + sfx_2683.wav \ + sfx_2684.wav \ + sfx_2685.wav \ + sfx_2686.wav \ + sfx_2687.wav \ + sfx_2688.wav \ + sfx_2689.wav \ + sfx_268.wav \ + sfx_2690.wav \ + sfx_2691.wav \ + sfx_2692.wav \ + sfx_2693.wav \ + sfx_2694.wav \ + sfx_2695.wav \ + sfx_2696.wav \ + sfx_2697.wav \ + sfx_2698.wav \ + sfx_2699.wav \ + sfx_269.wav \ + sfx_26.wav \ + sfx_2700.wav \ + sfx_2701.wav \ + sfx_2702.wav \ + sfx_2703.wav \ + sfx_2704.wav \ + sfx_2705.wav \ + sfx_2706.wav \ + sfx_2707.wav \ + sfx_2708.wav \ + sfx_2709.wav \ + sfx_270.wav \ + sfx_2710.wav \ + sfx_2711.wav \ + sfx_2712.wav \ + sfx_2713.wav \ + sfx_2714.wav \ + sfx_2715.wav \ + sfx_2716.wav \ + sfx_2717.wav \ + sfx_2718.wav \ + sfx_2719.wav \ + sfx_271.wav \ + sfx_2720.wav \ + sfx_2721.wav \ + sfx_2722.wav \ + sfx_2723.wav \ + sfx_2724.wav \ + sfx_2725.wav \ + sfx_2726.wav \ + sfx_2727.wav \ + sfx_2728.wav \ + sfx_2729.wav \ + sfx_272.wav \ + sfx_2730.wav \ + sfx_2731.wav \ + sfx_2732.wav \ + sfx_2733.wav \ + sfx_2734.wav \ + sfx_2735.wav \ + sfx_2736.wav \ + sfx_2737.wav \ + sfx_2738.wav \ + sfx_2739.wav \ + sfx_273.wav \ + sfx_2740.wav \ + sfx_2741.wav \ + sfx_2742.wav \ + sfx_2743.wav \ + sfx_2744.wav \ + sfx_2745.wav \ + sfx_2746.wav \ + sfx_2747.wav \ + sfx_2748.wav \ + sfx_2749.wav \ + sfx_274.wav \ + sfx_2750.wav \ + sfx_2751.wav \ + sfx_2752.wav \ + sfx_2753.wav \ + sfx_2754.wav \ + sfx_2755.wav \ + sfx_2756.wav \ + sfx_2757.wav \ + sfx_2758.wav \ + sfx_2759.wav \ + sfx_275.wav \ + sfx_2760.wav \ + sfx_2761.wav \ + sfx_2762.wav \ + sfx_2763.wav \ + sfx_2764.wav \ + sfx_2765.wav \ + sfx_2766.wav \ + sfx_2767.wav \ + sfx_2768.wav \ + sfx_2769.wav \ + sfx_276.wav \ + sfx_2770.wav \ + sfx_2771.wav \ + sfx_2772.wav \ + sfx_2773.wav \ + sfx_2774.wav \ + sfx_2775.wav \ + sfx_2776.wav \ + sfx_2777.wav \ + sfx_2778.wav \ + sfx_2779.wav \ + sfx_277.wav \ + sfx_2780.wav \ + sfx_2781.wav \ + sfx_2782.wav \ + sfx_2783.wav \ + sfx_2784.wav \ + sfx_2785.wav \ + sfx_2786.wav \ + sfx_2787.wav \ + sfx_2788.wav \ + sfx_2789.wav \ + sfx_278.wav \ + sfx_2790.wav \ + sfx_2791.wav \ + sfx_2792.wav \ + sfx_2793.wav \ + sfx_2794.wav \ + sfx_2795.wav \ + sfx_2796.wav \ + sfx_2797.wav \ + sfx_2798.wav \ + sfx_2799.wav \ + sfx_279.wav \ + sfx_27.wav \ + sfx_2800.wav \ + sfx_2801.wav \ + sfx_2802.wav \ + sfx_2803.wav \ + sfx_2804.wav \ + sfx_2805.wav \ + sfx_2806.wav \ + sfx_2807.wav \ + sfx_2808.wav \ + sfx_2809.wav \ + sfx_280.wav \ + sfx_2810.wav \ + sfx_2811.wav \ + sfx_2812.wav \ + sfx_2813.wav \ + sfx_2814.wav \ + sfx_2815.wav \ + sfx_2816.wav \ + sfx_2817.wav \ + sfx_2818.wav \ + sfx_2819.wav \ + sfx_281.wav \ + sfx_2820.wav \ + sfx_2821.wav \ + sfx_2822.wav \ + sfx_2823.wav \ + sfx_2824.wav \ + sfx_2825.wav \ + sfx_2826.wav \ + sfx_2827.wav \ + sfx_2828.wav \ + sfx_2829.wav \ + sfx_282.wav \ + sfx_2830.wav \ + sfx_2831.wav \ + sfx_2832.wav \ + sfx_2833.wav \ + sfx_2834.wav \ + sfx_2835.wav \ + sfx_2836.wav \ + sfx_2837.wav \ + sfx_2838.wav \ + sfx_2839.wav \ + sfx_283.wav \ + sfx_2840.wav \ + sfx_2841.wav \ + sfx_2842.wav \ + sfx_2843.wav \ + sfx_2844.wav \ + sfx_2845.wav \ + sfx_2846.wav \ + sfx_2847.wav \ + sfx_2848.wav \ + sfx_2849.wav \ + sfx_284.wav \ + sfx_2850.wav \ + sfx_2851.wav \ + sfx_2852.wav \ + sfx_2853.wav \ + sfx_2854.wav \ + sfx_2855.wav \ + sfx_2856.wav \ + sfx_2857.wav \ + sfx_2858.wav \ + sfx_2859.wav \ + sfx_285.wav \ + sfx_2860.wav \ + sfx_2861.wav \ + sfx_2862.wav \ + sfx_2863.wav \ + sfx_2864.wav \ + sfx_2865.wav \ + sfx_2866.wav \ + sfx_2867.wav \ + sfx_2868.wav \ + sfx_2869.wav \ + sfx_286.wav \ + sfx_2870.wav \ + sfx_2871.wav \ + sfx_2872.wav \ + sfx_2873.wav \ + sfx_2874.wav \ + sfx_2875.wav \ + sfx_2876.wav \ + sfx_2877.wav \ + sfx_2878.wav \ + sfx_2879.wav \ + sfx_287.wav \ + sfx_2880.wav \ + sfx_2881.wav \ + sfx_2882.wav \ + sfx_2883.wav \ + sfx_2884.wav \ + sfx_2885.wav \ + sfx_2886.wav \ + sfx_2887.wav \ + sfx_2888.wav \ + sfx_2889.wav \ + sfx_288.wav \ + sfx_2890.wav \ + sfx_2891.wav \ + sfx_2892.wav \ + sfx_2893.wav \ + sfx_2894.wav \ + sfx_2895.wav \ + sfx_2896.wav \ + sfx_2897.wav \ + sfx_2898.wav \ + sfx_2899.wav \ + sfx_289.wav \ + sfx_28.wav \ + sfx_2900.wav \ + sfx_2901.wav \ + sfx_2902.wav \ + sfx_2903.wav \ + sfx_2904.wav \ + sfx_2905.wav \ + sfx_2906.wav \ + sfx_2907.wav \ + sfx_2908.wav \ + sfx_2909.wav \ + sfx_290.wav \ + sfx_2910.wav \ + sfx_2911.wav \ + sfx_2912.wav \ + sfx_2913.wav \ + sfx_2914.wav \ + sfx_2915.wav \ + sfx_2916.wav \ + sfx_2917.wav \ + sfx_2918.wav \ + sfx_2919.wav \ + sfx_291.wav \ + sfx_2920.wav \ + sfx_2921.wav \ + sfx_2922.wav \ + sfx_2923.wav \ + sfx_2924.wav \ + sfx_2925.wav \ + sfx_2926.wav \ + sfx_2927.wav \ + sfx_2928.wav \ + sfx_2929.wav \ + sfx_292.wav \ + sfx_2930.wav \ + sfx_2931.wav \ + sfx_2932.wav \ + sfx_2933.wav \ + sfx_2934.wav \ + sfx_2935.wav \ + sfx_2936.wav \ + sfx_2937.wav \ + sfx_2938.wav \ + sfx_2939.wav \ + sfx_293.wav \ + sfx_2940.wav \ + sfx_2941.wav \ + sfx_2942.wav \ + sfx_2943.wav \ + sfx_2944.wav \ + sfx_2945.wav \ + sfx_2946.wav \ + sfx_2947.wav \ + sfx_2948.wav \ + sfx_2949.wav \ + sfx_294.wav \ + sfx_2950.wav \ + sfx_2951.wav \ + sfx_2952.wav \ + sfx_2953.wav \ + sfx_2954.wav \ + sfx_2955.wav \ + sfx_2956.wav \ + sfx_2957.wav \ + sfx_2958.wav \ + sfx_2959.wav \ + sfx_295.wav \ + sfx_2960.wav \ + sfx_2961.wav \ + sfx_2962.wav \ + sfx_2963.wav \ + sfx_2964.wav \ + sfx_2965.wav \ + sfx_2966.wav \ + sfx_2967.wav \ + sfx_2968.wav \ + sfx_2969.wav \ + sfx_296.wav \ + sfx_2970.wav \ + sfx_2971.wav \ + sfx_2972.wav \ + sfx_2973.wav \ + sfx_2974.wav \ + sfx_2975.wav \ + sfx_2976.wav \ + sfx_2977.wav \ + sfx_2978.wav \ + sfx_2979.wav \ + sfx_297.wav \ + sfx_2980.wav \ + sfx_2981.wav \ + sfx_2982.wav \ + sfx_2983.wav \ + sfx_2984.wav \ + sfx_2985.wav \ + sfx_2986.wav \ + sfx_2987.wav \ + sfx_2988.wav \ + sfx_2989.wav \ + sfx_298.wav \ + sfx_2990.wav \ + sfx_2991.wav \ + sfx_2992.wav \ + sfx_2993.wav \ + sfx_2994.wav \ + sfx_2995.wav \ + sfx_2996.wav \ + sfx_2997.wav \ + sfx_2998.wav \ + sfx_2999.wav \ + sfx_299.wav \ + sfx_29.wav \ + sfx_2.wav \ + sfx_3000.wav \ + sfx_3001.wav \ + sfx_3002.wav \ + sfx_3003.wav \ + sfx_3004.wav \ + sfx_3005.wav \ + sfx_3006.wav \ + sfx_3007.wav \ + sfx_3008.wav \ + sfx_3009.wav \ + sfx_300.wav \ + sfx_3010.wav \ + sfx_3011.wav \ + sfx_3012.wav \ + sfx_3013.wav \ + sfx_3014.wav \ + sfx_3015.wav \ + sfx_3016.wav \ + sfx_3017.wav \ + sfx_3018.wav \ + sfx_3019.wav \ + sfx_301.wav \ + sfx_3020.wav \ + sfx_3021.wav \ + sfx_3022.wav \ + sfx_3023.wav \ + sfx_3024.wav \ + sfx_3025.wav \ + sfx_3026.wav \ + sfx_3027.wav \ + sfx_3028.wav \ + sfx_3029.wav \ + sfx_302.wav \ + sfx_3030.wav \ + sfx_3031.wav \ + sfx_3032.wav \ + sfx_3033.wav \ + sfx_3034.wav \ + sfx_3035.wav \ + sfx_3036.wav \ + sfx_3037.wav \ + sfx_3038.wav \ + sfx_3039.wav \ + sfx_303.wav \ + sfx_3040.wav \ + sfx_3041.wav \ + sfx_3042.wav \ + sfx_3043.wav \ + sfx_3044.wav \ + sfx_3045.wav \ + sfx_3046.wav \ + sfx_3047.wav \ + sfx_3048.wav \ + sfx_3049.wav \ + sfx_304.wav \ + sfx_3050.wav \ + sfx_3051.wav \ + sfx_3052.wav \ + sfx_3053.wav \ + sfx_3054.wav \ + sfx_3055.wav \ + sfx_3056.wav \ + sfx_3057.wav \ + sfx_3058.wav \ + sfx_3059.wav \ + sfx_305.wav \ + sfx_3060.wav \ + sfx_3061.wav \ + sfx_3062.wav \ + sfx_3063.wav \ + sfx_3064.wav \ + sfx_3065.wav \ + sfx_3066.wav \ + sfx_3067.wav \ + sfx_3068.wav \ + sfx_3069.wav \ + sfx_306.wav \ + sfx_3070.wav \ + sfx_3071.wav \ + sfx_3072.wav \ + sfx_3073.wav \ + sfx_3074.wav \ + sfx_3075.wav \ + sfx_3076.wav \ + sfx_3077.wav \ + sfx_3078.wav \ + sfx_3079.wav \ + sfx_307.wav \ + sfx_3080.wav \ + sfx_3081.wav \ + sfx_3082.wav \ + sfx_3083.wav \ + sfx_3084.wav \ + sfx_3085.wav \ + sfx_3086.wav \ + sfx_3087.wav \ + sfx_3088.wav \ + sfx_3089.wav \ + sfx_308.wav \ + sfx_3090.wav \ + sfx_3091.wav \ + sfx_3092.wav \ + sfx_3093.wav \ + sfx_3094.wav \ + sfx_3095.wav \ + sfx_3096.wav \ + sfx_3097.wav \ + sfx_3098.wav \ + sfx_3099.wav \ + sfx_309.wav \ + sfx_30.wav \ + sfx_3100.wav \ + sfx_3101.wav \ + sfx_3102.wav \ + sfx_3103.wav \ + sfx_3104.wav \ + sfx_3105.wav \ + sfx_3106.wav \ + sfx_3107.wav \ + sfx_3108.wav \ + sfx_3109.wav \ + sfx_310.wav \ + sfx_3110.wav \ + sfx_3111.wav \ + sfx_3112.wav \ + sfx_3113.wav \ + sfx_3114.wav \ + sfx_3115.wav \ + sfx_3116.wav \ + sfx_3117.wav \ + sfx_3118.wav \ + sfx_3119.wav \ + sfx_311.wav \ + sfx_3120.wav \ + sfx_3121.wav \ + sfx_3122.wav \ + sfx_3123.wav \ + sfx_3124.wav \ + sfx_3125.wav \ + sfx_3126.wav \ + sfx_3127.wav \ + sfx_3128.wav \ + sfx_3129.wav \ + sfx_312.wav \ + sfx_3130.wav \ + sfx_3131.wav \ + sfx_3132.wav \ + sfx_3133.wav \ + sfx_3134.wav \ + sfx_3135.wav \ + sfx_3136.wav \ + sfx_3137.wav \ + sfx_3138.wav \ + sfx_3139.wav \ + sfx_313.wav \ + sfx_3140.wav \ + sfx_3141.wav \ + sfx_3142.wav \ + sfx_3143.wav \ + sfx_3144.wav \ + sfx_3145.wav \ + sfx_3146.wav \ + sfx_3147.wav \ + sfx_3148.wav \ + sfx_3149.wav \ + sfx_314.wav \ + sfx_3150.wav \ + sfx_3151.wav \ + sfx_3152.wav \ + sfx_3153.wav \ + sfx_3154.wav \ + sfx_3155.wav \ + sfx_3156.wav \ + sfx_3157.wav \ + sfx_3158.wav \ + sfx_3159.wav \ + sfx_315.wav \ + sfx_3160.wav \ + sfx_3161.wav \ + sfx_3162.wav \ + sfx_3163.wav \ + sfx_3164.wav \ + sfx_3165.wav \ + sfx_3166.wav \ + sfx_3167.wav \ + sfx_3168.wav \ + sfx_3169.wav \ + sfx_316.wav \ + sfx_3170.wav \ + sfx_3171.wav \ + sfx_3172.wav \ + sfx_3173.wav \ + sfx_3174.wav \ + sfx_3175.wav \ + sfx_3176.wav \ + sfx_3177.wav \ + sfx_3178.wav \ + sfx_3179.wav \ + sfx_317.wav \ + sfx_3180.wav \ + sfx_3181.wav \ + sfx_3182.wav \ + sfx_3183.wav \ + sfx_3184.wav \ + sfx_3185.wav \ + sfx_3186.wav \ + sfx_3187.wav \ + sfx_3188.wav \ + sfx_3189.wav \ + sfx_318.wav \ + sfx_3190.wav \ + sfx_3191.wav \ + sfx_3192.wav \ + sfx_3193.wav \ + sfx_3194.wav \ + sfx_3195.wav \ + sfx_3196.wav \ + sfx_3197.wav \ + sfx_3198.wav \ + sfx_3199.wav \ + sfx_319.wav \ + sfx_31.wav \ + sfx_3200.wav \ + sfx_3201.wav \ + sfx_3202.wav \ + sfx_3203.wav \ + sfx_3204.wav \ + sfx_3205.wav \ + sfx_3206.wav \ + sfx_3207.wav \ + sfx_3208.wav \ + sfx_3209.wav \ + sfx_320.wav \ + sfx_3210.wav \ + sfx_3211.wav \ + sfx_3212.wav \ + sfx_3213.wav \ + sfx_3214.wav \ + sfx_3215.wav \ + sfx_3216.wav \ + sfx_3217.wav \ + sfx_3218.wav \ + sfx_3219.wav \ + sfx_321.wav \ + sfx_3220.wav \ + sfx_3221.wav \ + sfx_3222.wav \ + sfx_3223.wav \ + sfx_3224.wav \ + sfx_3225.wav \ + sfx_3226.wav \ + sfx_3227.wav \ + sfx_3228.wav \ + sfx_3229.wav \ + sfx_322.wav \ + sfx_3230.wav \ + sfx_3231.wav \ + sfx_3232.wav \ + sfx_3233.wav \ + sfx_3234.wav \ + sfx_3235.wav \ + sfx_3236.wav \ + sfx_3237.wav \ + sfx_3238.wav \ + sfx_3239.wav \ + sfx_323.wav \ + sfx_3240.wav \ + sfx_3241.wav \ + sfx_3242.wav \ + sfx_3243.wav \ + sfx_3244.wav \ + sfx_3245.wav \ + sfx_3246.wav \ + sfx_3247.wav \ + sfx_3248.wav \ + sfx_3249.wav \ + sfx_324.wav \ + sfx_3250.wav \ + sfx_3251.wav \ + sfx_3252.wav \ + sfx_3253.wav \ + sfx_3254.wav \ + sfx_3255.wav \ + sfx_3256.wav \ + sfx_3257.wav \ + sfx_3258.wav \ + sfx_3259.wav \ + sfx_325.wav \ + sfx_3260.wav \ + sfx_3261.wav \ + sfx_3262.wav \ + sfx_3263.wav \ + sfx_3264.wav \ + sfx_3265.wav \ + sfx_3266.wav \ + sfx_3267.wav \ + sfx_3268.wav \ + sfx_3269.wav \ + sfx_326.wav \ + sfx_3270.wav \ + sfx_3271.wav \ + sfx_3272.wav \ + sfx_3273.wav \ + sfx_3274.wav \ + sfx_3275.wav \ + sfx_3276.wav \ + sfx_3277.wav \ + sfx_3278.wav \ + sfx_3279.wav \ + sfx_327.wav \ + sfx_3280.wav \ + sfx_3281.wav \ + sfx_3282.wav \ + sfx_3283.wav \ + sfx_3284.wav \ + sfx_3285.wav \ + sfx_3286.wav \ + sfx_3287.wav \ + sfx_3288.wav \ + sfx_3289.wav \ + sfx_328.wav \ + sfx_3290.wav \ + sfx_3291.wav \ + sfx_3292.wav \ + sfx_3293.wav \ + sfx_3294.wav \ + sfx_3295.wav \ + sfx_3296.wav \ + sfx_3297.wav \ + sfx_3298.wav \ + sfx_3299.wav \ + sfx_329.wav \ + sfx_32.wav \ + sfx_3300.wav \ + sfx_3301.wav \ + sfx_3302.wav \ + sfx_3303.wav \ + sfx_3304.wav \ + sfx_3305.wav \ + sfx_3306.wav \ + sfx_3307.wav \ + sfx_3308.wav \ + sfx_3309.wav \ + sfx_330.wav \ + sfx_3310.wav \ + sfx_3311.wav \ + sfx_3312.wav \ + sfx_3313.wav \ + sfx_3314.wav \ + sfx_3315.wav \ + sfx_3316.wav \ + sfx_3317.wav \ + sfx_3318.wav \ + sfx_3319.wav \ + sfx_331.wav \ + sfx_3320.wav \ + sfx_3321.wav \ + sfx_3322.wav \ + sfx_3323.wav \ + sfx_3324.wav \ + sfx_3325.wav \ + sfx_3326.wav \ + sfx_3327.wav \ + sfx_3328.wav \ + sfx_3329.wav \ + sfx_332.wav \ + sfx_3330.wav \ + sfx_3331.wav \ + sfx_3332.wav \ + sfx_3333.wav \ + sfx_3334.wav \ + sfx_3335.wav \ + sfx_3336.wav \ + sfx_3337.wav \ + sfx_3338.wav \ + sfx_3339.wav \ + sfx_333.wav \ + sfx_3340.wav \ + sfx_3341.wav \ + sfx_3342.wav \ + sfx_3343.wav \ + sfx_3344.wav \ + sfx_3345.wav \ + sfx_3346.wav \ + sfx_3347.wav \ + sfx_3348.wav \ + sfx_3349.wav \ + sfx_334.wav \ + sfx_3350.wav \ + sfx_3351.wav \ + sfx_3352.wav \ + sfx_3353.wav \ + sfx_3354.wav \ + sfx_3355.wav \ + sfx_3356.wav \ + sfx_3357.wav \ + sfx_3358.wav \ + sfx_3359.wav \ + sfx_335.wav \ + sfx_3360.wav \ + sfx_3361.wav \ + sfx_3362.wav \ + sfx_3363.wav \ + sfx_3364.wav \ + sfx_3365.wav \ + sfx_3366.wav \ + sfx_3367.wav \ + sfx_3368.wav \ + sfx_3369.wav \ + sfx_336.wav \ + sfx_3370.wav \ + sfx_3371.wav \ + sfx_3372.wav \ + sfx_3373.wav \ + sfx_3374.wav \ + sfx_3375.wav \ + sfx_3376.wav \ + sfx_3377.wav \ + sfx_3378.wav \ + sfx_3379.wav \ + sfx_337.wav \ + sfx_3380.wav \ + sfx_3381.wav \ + sfx_3382.wav \ + sfx_3383.wav \ + sfx_3384.wav \ + sfx_3385.wav \ + sfx_3386.wav \ + sfx_3387.wav \ + sfx_3388.wav \ + sfx_3389.wav \ + sfx_338.wav \ + sfx_3390.wav \ + sfx_3391.wav \ + sfx_3392.wav \ + sfx_3393.wav \ + sfx_3394.wav \ + sfx_3395.wav \ + sfx_3396.wav \ + sfx_3397.wav \ + sfx_3398.wav \ + sfx_3399.wav \ + sfx_339.wav \ + sfx_33.wav \ + sfx_3400.wav \ + sfx_3401.wav \ + sfx_3402.wav \ + sfx_3403.wav \ + sfx_3404.wav \ + sfx_3405.wav \ + sfx_3406.wav \ + sfx_3407.wav \ + sfx_3408.wav \ + sfx_3409.wav \ + sfx_340.wav \ + sfx_3410.wav \ + sfx_3411.wav \ + sfx_3412.wav \ + sfx_3413.wav \ + sfx_3414.wav \ + sfx_3415.wav \ + sfx_3416.wav \ + sfx_3417.wav \ + sfx_3418.wav \ + sfx_3419.wav \ + sfx_341.wav \ + sfx_3420.wav \ + sfx_3421.wav \ + sfx_3422.wav \ + sfx_3423.wav \ + sfx_3424.wav \ + sfx_3425.wav \ + sfx_3426.wav \ + sfx_3427.wav \ + sfx_3428.wav \ + sfx_3429.wav \ + sfx_342.wav \ + sfx_3430.wav \ + sfx_3431.wav \ + sfx_3432.wav \ + sfx_3433.wav \ + sfx_3434.wav \ + sfx_3435.wav \ + sfx_3436.wav \ + sfx_3437.wav \ + sfx_3438.wav \ + sfx_3439.wav \ + sfx_343.wav \ + sfx_3440.wav \ + sfx_3441.wav \ + sfx_3442.wav \ + sfx_3443.wav \ + sfx_3444.wav \ + sfx_3445.wav \ + sfx_3446.wav \ + sfx_3447.wav \ + sfx_3448.wav \ + sfx_3449.wav \ + sfx_344.wav \ + sfx_3450.wav \ + sfx_3451.wav \ + sfx_3452.wav \ + sfx_3453.wav \ + sfx_3454.wav \ + sfx_3455.wav \ + sfx_3456.wav \ + sfx_3457.wav \ + sfx_3458.wav \ + sfx_3459.wav \ + sfx_345.wav \ + sfx_3460.wav \ + sfx_3461.wav \ + sfx_3462.wav \ + sfx_3463.wav \ + sfx_3464.wav \ + sfx_3465.wav \ + sfx_3466.wav \ + sfx_3467.wav \ + sfx_3468.wav \ + sfx_3469.wav \ + sfx_346.wav \ + sfx_3470.wav \ + sfx_3471.wav \ + sfx_3472.wav \ + sfx_3473.wav \ + sfx_3474.wav \ + sfx_3475.wav \ + sfx_3476.wav \ + sfx_3477.wav \ + sfx_3478.wav \ + sfx_3479.wav \ + sfx_347.wav \ + sfx_3480.wav \ + sfx_3481.wav \ + sfx_3482.wav \ + sfx_3483.wav \ + sfx_3484.wav \ + sfx_3485.wav \ + sfx_3486.wav \ + sfx_3487.wav \ + sfx_3488.wav \ + sfx_3489.wav \ + sfx_348.wav \ + sfx_3490.wav \ + sfx_3491.wav \ + sfx_3492.wav \ + sfx_3493.wav \ + sfx_3494.wav \ + sfx_3495.wav \ + sfx_3496.wav \ + sfx_3497.wav \ + sfx_3498.wav \ + sfx_3499.wav \ + sfx_349.wav \ + sfx_34.wav \ + sfx_3500.wav \ + sfx_3501.wav \ + sfx_3502.wav \ + sfx_3503.wav \ + sfx_3504.wav \ + sfx_3505.wav \ + sfx_3506.wav \ + sfx_3507.wav \ + sfx_3508.wav \ + sfx_3509.wav \ + sfx_350.wav \ + sfx_3510.wav \ + sfx_3511.wav \ + sfx_3512.wav \ + sfx_3513.wav \ + sfx_3514.wav \ + sfx_3515.wav \ + sfx_3516.wav \ + sfx_3517.wav \ + sfx_3518.wav \ + sfx_3519.wav \ + sfx_351.wav \ + sfx_3520.wav \ + sfx_3521.wav \ + sfx_3522.wav \ + sfx_3523.wav \ + sfx_3524.wav \ + sfx_3525.wav \ + sfx_3526.wav \ + sfx_3527.wav \ + sfx_3528.wav \ + sfx_3529.wav \ + sfx_352.wav \ + sfx_3530.wav \ + sfx_3531.wav \ + sfx_3532.wav \ + sfx_3533.wav \ + sfx_3534.wav \ + sfx_3535.wav \ + sfx_3536.wav \ + sfx_3537.wav \ + sfx_3538.wav \ + sfx_3539.wav \ + sfx_353.wav \ + sfx_3540.wav \ + sfx_3541.wav \ + sfx_3542.wav \ + sfx_3543.wav \ + sfx_3544.wav \ + sfx_3545.wav \ + sfx_3546.wav \ + sfx_3547.wav \ + sfx_3548.wav \ + sfx_3549.wav \ + sfx_354.wav \ + sfx_3550.wav \ + sfx_3551.wav \ + sfx_3552.wav \ + sfx_3553.wav \ + sfx_3554.wav \ + sfx_3555.wav \ + sfx_3556.wav \ + sfx_3557.wav \ + sfx_3558.wav \ + sfx_3559.wav \ + sfx_355.wav \ + sfx_3560.wav \ + sfx_3561.wav \ + sfx_3562.wav \ + sfx_3563.wav \ + sfx_3564.wav \ + sfx_3565.wav \ + sfx_3566.wav \ + sfx_3567.wav \ + sfx_3568.wav \ + sfx_3569.wav \ + sfx_356.wav \ + sfx_3570.wav \ + sfx_3571.wav \ + sfx_3572.wav \ + sfx_3573.wav \ + sfx_3574.wav \ + sfx_3575.wav \ + sfx_3576.wav \ + sfx_3577.wav \ + sfx_3578.wav \ + sfx_3579.wav \ + sfx_357.wav \ + sfx_3580.wav \ + sfx_3581.wav \ + sfx_3582.wav \ + sfx_3583.wav \ + sfx_3584.wav \ + sfx_3585.wav \ + sfx_3586.wav \ + sfx_3587.wav \ + sfx_3588.wav \ + sfx_3589.wav \ + sfx_358.wav \ + sfx_3590.wav \ + sfx_3591.wav \ + sfx_3592.wav \ + sfx_3593.wav \ + sfx_3594.wav \ + sfx_3595.wav \ + sfx_3596.wav \ + sfx_3597.wav \ + sfx_3598.wav \ + sfx_3599.wav \ + sfx_359.wav \ + sfx_35.wav \ + sfx_3600.wav \ + sfx_3601.wav \ + sfx_3602.wav \ + sfx_3603.wav \ + sfx_3604.wav \ + sfx_3605.wav \ + sfx_3606.wav \ + sfx_3607.wav \ + sfx_3608.wav \ + sfx_3609.wav \ + sfx_360.wav \ + sfx_3610.wav \ + sfx_3611.wav \ + sfx_3612.wav \ + sfx_3613.wav \ + sfx_3614.wav \ + sfx_3615.wav \ + sfx_3616.wav \ + sfx_3617.wav \ + sfx_3618.wav \ + sfx_3619.wav \ + sfx_361.wav \ + sfx_3620.wav \ + sfx_3621.wav \ + sfx_3622.wav \ + sfx_3623.wav \ + sfx_3624.wav \ + sfx_3625.wav \ + sfx_3626.wav \ + sfx_3627.wav \ + sfx_3628.wav \ + sfx_3629.wav \ + sfx_362.wav \ + sfx_3630.wav \ + sfx_3631.wav \ + sfx_3632.wav \ + sfx_3633.wav \ + sfx_3634.wav \ + sfx_3635.wav \ + sfx_3636.wav \ + sfx_3637.wav \ + sfx_3638.wav \ + sfx_3639.wav \ + sfx_363.wav \ + sfx_3640.wav \ + sfx_3641.wav \ + sfx_3642.wav \ + sfx_3643.wav \ + sfx_3644.wav \ + sfx_3645.wav \ + sfx_3646.wav \ + sfx_3647.wav \ + sfx_3648.wav \ + sfx_3649.wav \ + sfx_364.wav \ + sfx_3650.wav \ + sfx_3651.wav \ + sfx_3652.wav \ + sfx_3653.wav \ + sfx_3654.wav \ + sfx_3655.wav \ + sfx_3656.wav \ + sfx_3657.wav \ + sfx_3658.wav \ + sfx_3659.wav \ + sfx_365.wav \ + sfx_3660.wav \ + sfx_3661.wav \ + sfx_3662.wav \ + sfx_3663.wav \ + sfx_3664.wav \ + sfx_3665.wav \ + sfx_3666.wav \ + sfx_3667.wav \ + sfx_3668.wav \ + sfx_3669.wav \ + sfx_366.wav \ + sfx_3670.wav \ + sfx_3671.wav \ + sfx_3672.wav \ + sfx_3673.wav \ + sfx_3674.wav \ + sfx_3675.wav \ + sfx_3676.wav \ + sfx_3677.wav \ + sfx_3678.wav \ + sfx_3679.wav \ + sfx_367.wav \ + sfx_3680.wav \ + sfx_3681.wav \ + sfx_3682.wav \ + sfx_3683.wav \ + sfx_3684.wav \ + sfx_3685.wav \ + sfx_3686.wav \ + sfx_3687.wav \ + sfx_3688.wav \ + sfx_3689.wav \ + sfx_368.wav \ + sfx_3690.wav \ + sfx_3691.wav \ + sfx_3692.wav \ + sfx_3693.wav \ + sfx_3694.wav \ + sfx_3695.wav \ + sfx_3696.wav \ + sfx_3697.wav \ + sfx_3698.wav \ + sfx_3699.wav \ + sfx_369.wav \ + sfx_36.wav \ + sfx_3700.wav \ + sfx_3701.wav \ + sfx_3702.wav \ + sfx_3703.wav \ + sfx_3704.wav \ + sfx_3705.wav \ + sfx_3706.wav \ + sfx_3707.wav \ + sfx_3708.wav \ + sfx_3709.wav \ + sfx_370.wav \ + sfx_3710.wav \ + sfx_3711.wav \ + sfx_3712.wav \ + sfx_3713.wav \ + sfx_3714.wav \ + sfx_3715.wav \ + sfx_3716.wav \ + sfx_3717.wav \ + sfx_3718.wav \ + sfx_3719.wav \ + sfx_371.wav \ + sfx_3720.wav \ + sfx_3721.wav \ + sfx_3722.wav \ + sfx_3723.wav \ + sfx_3724.wav \ + sfx_3725.wav \ + sfx_3726.wav \ + sfx_3727.wav \ + sfx_3728.wav \ + sfx_3729.wav \ + sfx_372.wav \ + sfx_3730.wav \ + sfx_3731.wav \ + sfx_3732.wav \ + sfx_3733.wav \ + sfx_3734.wav \ + sfx_3735.wav \ + sfx_3736.wav \ + sfx_3737.wav \ + sfx_3738.wav \ + sfx_3739.wav \ + sfx_373.wav \ + sfx_3740.wav \ + sfx_3741.wav \ + sfx_3742.wav \ + sfx_3743.wav \ + sfx_3744.wav \ + sfx_3745.wav \ + sfx_3746.wav \ + sfx_3747.wav \ + sfx_3748.wav \ + sfx_3749.wav \ + sfx_374.wav \ + sfx_3750.wav \ + sfx_3751.wav \ + sfx_3752.wav \ + sfx_3753.wav \ + sfx_3754.wav \ + sfx_3755.wav \ + sfx_3756.wav \ + sfx_3757.wav \ + sfx_3758.wav \ + sfx_3759.wav \ + sfx_375.wav \ + sfx_3760.wav \ + sfx_3761.wav \ + sfx_3762.wav \ + sfx_3763.wav \ + sfx_3764.wav \ + sfx_3765.wav \ + sfx_3766.wav \ + sfx_3767.wav \ + sfx_3768.wav \ + sfx_3769.wav \ + sfx_376.wav \ + sfx_3770.wav \ + sfx_3771.wav \ + sfx_3772.wav \ + sfx_3773.wav \ + sfx_3774.wav \ + sfx_3775.wav \ + sfx_3776.wav \ + sfx_3777.wav \ + sfx_3778.wav \ + sfx_3779.wav \ + sfx_377.wav \ + sfx_3780.wav \ + sfx_3781.wav \ + sfx_3782.wav \ + sfx_3783.wav \ + sfx_3784.wav \ + sfx_3785.wav \ + sfx_3786.wav \ + sfx_3787.wav \ + sfx_3788.wav \ + sfx_3789.wav \ + sfx_378.wav \ + sfx_3790.wav \ + sfx_3791.wav \ + sfx_3792.wav \ + sfx_3793.wav \ + sfx_3794.wav \ + sfx_3795.wav \ + sfx_3796.wav \ + sfx_3797.wav \ + sfx_3798.wav \ + sfx_3799.wav \ + sfx_379.wav \ + sfx_37.wav \ + sfx_3800.wav \ + sfx_3801.wav \ + sfx_3802.wav \ + sfx_3803.wav \ + sfx_3804.wav \ + sfx_3805.wav \ + sfx_3806.wav \ + sfx_3807.wav \ + sfx_3808.wav \ + sfx_3809.wav \ + sfx_380.wav \ + sfx_3810.wav \ + sfx_3811.wav \ + sfx_3812.wav \ + sfx_3813.wav \ + sfx_3814.wav \ + sfx_3815.wav \ + sfx_3816.wav \ + sfx_3817.wav \ + sfx_3818.wav \ + sfx_3819.wav \ + sfx_381.wav \ + sfx_3820.wav \ + sfx_3821.wav \ + sfx_3822.wav \ + sfx_3823.wav \ + sfx_3824.wav \ + sfx_3825.wav \ + sfx_3826.wav \ + sfx_3827.wav \ + sfx_3828.wav \ + sfx_3829.wav \ + sfx_382.wav \ + sfx_3830.wav \ + sfx_3831.wav \ + sfx_3832.wav \ + sfx_3833.wav \ + sfx_3834.wav \ + sfx_3835.wav \ + sfx_3836.wav \ + sfx_3837.wav \ + sfx_3838.wav \ + sfx_3839.wav \ + sfx_383.wav \ + sfx_3840.wav \ + sfx_3841.wav \ + sfx_3842.wav \ + sfx_3843.wav \ + sfx_3844.wav \ + sfx_3845.wav \ + sfx_3846.wav \ + sfx_3847.wav \ + sfx_3848.wav \ + sfx_3849.wav \ + sfx_384.wav \ + sfx_3850.wav \ + sfx_3851.wav \ + sfx_3852.wav \ + sfx_3853.wav \ + sfx_3854.wav \ + sfx_3855.wav \ + sfx_3856.wav \ + sfx_3857.wav \ + sfx_3858.wav \ + sfx_3859.wav \ + sfx_385.wav \ + sfx_3860.wav \ + sfx_3861.wav \ + sfx_3862.wav \ + sfx_3863.wav \ + sfx_3864.wav \ + sfx_3865.wav \ + sfx_3866.wav \ + sfx_3867.wav \ + sfx_3868.wav \ + sfx_3869.wav \ + sfx_386.wav \ + sfx_3870.wav \ + sfx_3871.wav \ + sfx_3872.wav \ + sfx_3873.wav \ + sfx_3874.wav \ + sfx_3875.wav \ + sfx_3876.wav \ + sfx_3877.wav \ + sfx_3878.wav \ + sfx_3879.wav \ + sfx_387.wav \ + sfx_3880.wav \ + sfx_3881.wav \ + sfx_3882.wav \ + sfx_3883.wav \ + sfx_3884.wav \ + sfx_3885.wav \ + sfx_3886.wav \ + sfx_3887.wav \ + sfx_3888.wav \ + sfx_3889.wav \ + sfx_388.wav \ + sfx_3890.wav \ + sfx_3891.wav \ + sfx_3892.wav \ + sfx_3893.wav \ + sfx_3894.wav \ + sfx_3895.wav \ + sfx_3896.wav \ + sfx_3897.wav \ + sfx_3898.wav \ + sfx_3899.wav \ + sfx_389.wav \ + sfx_38.wav \ + sfx_3900.wav \ + sfx_3901.wav \ + sfx_3902.wav \ + sfx_3903.wav \ + sfx_3904.wav \ + sfx_3905.wav \ + sfx_3906.wav \ + sfx_3907.wav \ + sfx_3908.wav \ + sfx_3909.wav \ + sfx_390.wav \ + sfx_3910.wav \ + sfx_3911.wav \ + sfx_3912.wav \ + sfx_3913.wav \ + sfx_3914.wav \ + sfx_3915.wav \ + sfx_3916.wav \ + sfx_3917.wav \ + sfx_3918.wav \ + sfx_3919.wav \ + sfx_391.wav \ + sfx_3920.wav \ + sfx_3921.wav \ + sfx_3922.wav \ + sfx_3923.wav \ + sfx_3924.wav \ + sfx_3925.wav \ + sfx_3926.wav \ + sfx_3927.wav \ + sfx_3928.wav \ + sfx_3929.wav \ + sfx_392.wav \ + sfx_3930.wav \ + sfx_3931.wav \ + sfx_3932.wav \ + sfx_3933.wav \ + sfx_3934.wav \ + sfx_3935.wav \ + sfx_3936.wav \ + sfx_3937.wav \ + sfx_3938.wav \ + sfx_3939.wav \ + sfx_393.wav \ + sfx_3940.wav \ + sfx_3941.wav \ + sfx_3942.wav \ + sfx_3943.wav \ + sfx_3944.wav \ + sfx_3945.wav \ + sfx_3946.wav \ + sfx_3947.wav \ + sfx_3948.wav \ + sfx_3949.wav \ + sfx_394.wav \ + sfx_3950.wav \ + sfx_3951.wav \ + sfx_3952.wav \ + sfx_3953.wav \ + sfx_3954.wav \ + sfx_3955.wav \ + sfx_3956.wav \ + sfx_3957.wav \ + sfx_3958.wav \ + sfx_3959.wav \ + sfx_395.wav \ + sfx_3960.wav \ + sfx_3961.wav \ + sfx_3962.wav \ + sfx_3963.wav \ + sfx_3964.wav \ + sfx_3965.wav \ + sfx_3966.wav \ + sfx_3967.wav \ + sfx_3968.wav \ + sfx_3969.wav \ + sfx_396.wav \ + sfx_3970.wav \ + sfx_3971.wav \ + sfx_3972.wav \ + sfx_3973.wav \ + sfx_3974.wav \ + sfx_3975.wav \ + sfx_3976.wav \ + sfx_3977.wav \ + sfx_3978.wav \ + sfx_3979.wav \ + sfx_397.wav \ + sfx_3980.wav \ + sfx_3981.wav \ + sfx_3982.wav \ + sfx_3983.wav \ + sfx_3984.wav \ + sfx_3985.wav \ + sfx_3986.wav \ + sfx_3987.wav \ + sfx_3988.wav \ + sfx_3989.wav \ + sfx_398.wav \ + sfx_3990.wav \ + sfx_3991.wav \ + sfx_3992.wav \ + sfx_3993.wav \ + sfx_3994.wav \ + sfx_3995.wav \ + sfx_3996.wav \ + sfx_3997.wav \ + sfx_3998.wav \ + sfx_3999.wav \ + sfx_399.wav \ + sfx_39.wav \ + sfx_3.wav \ + sfx_4000.wav \ + sfx_4001.wav \ + sfx_4002.wav \ + sfx_4003.wav \ + sfx_4004.wav \ + sfx_4005.wav \ + sfx_4006.wav \ + sfx_4007.wav \ + sfx_4008.wav \ + sfx_4009.wav \ + sfx_400.wav \ + sfx_4010.wav \ + sfx_4011.wav \ + sfx_4012.wav \ + sfx_4013.wav \ + sfx_4014.wav \ + sfx_4015.wav \ + sfx_4016.wav \ + sfx_4017.wav \ + sfx_4018.wav \ + sfx_4019.wav \ + sfx_401.wav \ + sfx_4020.wav \ + sfx_4021.wav \ + sfx_4022.wav \ + sfx_4023.wav \ + sfx_4024.wav \ + sfx_4025.wav \ + sfx_4026.wav \ + sfx_4027.wav \ + sfx_4028.wav \ + sfx_4029.wav \ + sfx_402.wav \ + sfx_4030.wav \ + sfx_4031.wav \ + sfx_4032.wav \ + sfx_4033.wav \ + sfx_4034.wav \ + sfx_4035.wav \ + sfx_4036.wav \ + sfx_4037.wav \ + sfx_4038.wav \ + sfx_4039.wav \ + sfx_403.wav \ + sfx_4040.wav \ + sfx_4041.wav \ + sfx_4042.wav \ + sfx_4043.wav \ + sfx_4044.wav \ + sfx_4045.wav \ + sfx_4046.wav \ + sfx_4047.wav \ + sfx_4048.wav \ + sfx_4049.wav \ + sfx_404.wav \ + sfx_4050.wav \ + sfx_4051.wav \ + sfx_4052.wav \ + sfx_4053.wav \ + sfx_4054.wav \ + sfx_4055.wav \ + sfx_4056.wav \ + sfx_4057.wav \ + sfx_4058.wav \ + sfx_4059.wav \ + sfx_405.wav \ + sfx_4060.wav \ + sfx_4061.wav \ + sfx_4062.wav \ + sfx_4063.wav \ + sfx_4064.wav \ + sfx_4065.wav \ + sfx_4066.wav \ + sfx_4067.wav \ + sfx_4068.wav \ + sfx_4069.wav \ + sfx_406.wav \ + sfx_4070.wav \ + sfx_4071.wav \ + sfx_4072.wav \ + sfx_4073.wav \ + sfx_4074.wav \ + sfx_4075.wav \ + sfx_4076.wav \ + sfx_4077.wav \ + sfx_4078.wav \ + sfx_4079.wav \ + sfx_407.wav \ + sfx_4080.wav \ + sfx_4081.wav \ + sfx_4082.wav \ + sfx_4083.wav \ + sfx_4084.wav \ + sfx_4085.wav \ + sfx_4086.wav \ + sfx_4087.wav \ + sfx_4088.wav \ + sfx_4089.wav \ + sfx_408.wav \ + sfx_4090.wav \ + sfx_4091.wav \ + sfx_4092.wav \ + sfx_4093.wav \ + sfx_4094.wav \ + sfx_4095.wav \ + sfx_4096.wav \ + sfx_4097.wav \ + sfx_4098.wav \ + sfx_4099.wav \ + sfx_409.wav \ + sfx_40.wav \ + sfx_4100.wav \ + sfx_4101.wav \ + sfx_4102.wav \ + sfx_4103.wav \ + sfx_4104.wav \ + sfx_4105.wav \ + sfx_4106.wav \ + sfx_4107.wav \ + sfx_4108.wav \ + sfx_4109.wav \ + sfx_410.wav \ + sfx_4110.wav \ + sfx_4111.wav \ + sfx_4112.wav \ + sfx_4113.wav \ + sfx_4114.wav \ + sfx_4115.wav \ + sfx_4116.wav \ + sfx_4117.wav \ + sfx_4118.wav \ + sfx_4119.wav \ + sfx_411.wav \ + sfx_4120.wav \ + sfx_4121.wav \ + sfx_4122.wav \ + sfx_4123.wav \ + sfx_4124.wav \ + sfx_4125.wav \ + sfx_4126.wav \ + sfx_4127.wav \ + sfx_4128.wav \ + sfx_4129.wav \ + sfx_412.wav \ + sfx_4130.wav \ + sfx_4131.wav \ + sfx_4132.wav \ + sfx_4133.wav \ + sfx_4134.wav \ + sfx_4135.wav \ + sfx_4136.wav \ + sfx_4137.wav \ + sfx_4138.wav \ + sfx_4139.wav \ + sfx_413.wav \ + sfx_4140.wav \ + sfx_4141.wav \ + sfx_4142.wav \ + sfx_4143.wav \ + sfx_4144.wav \ + sfx_4145.wav \ + sfx_4146.wav \ + sfx_4147.wav \ + sfx_4148.wav \ + sfx_4149.wav \ + sfx_414.wav \ + sfx_4150.wav \ + sfx_4151.wav \ + sfx_4152.wav \ + sfx_4153.wav \ + sfx_4154.wav \ + sfx_4155.wav \ + sfx_4156.wav \ + sfx_4157.wav \ + sfx_4158.wav \ + sfx_4159.wav \ + sfx_415.wav \ + sfx_4160.wav \ + sfx_4161.wav \ + sfx_4162.wav \ + sfx_4163.wav \ + sfx_4164.wav \ + sfx_4165.wav \ + sfx_4166.wav \ + sfx_4167.wav \ + sfx_4168.wav \ + sfx_4169.wav \ + sfx_416.wav \ + sfx_4170.wav \ + sfx_4171.wav \ + sfx_4172.wav \ + sfx_4173.wav \ + sfx_4174.wav \ + sfx_4175.wav \ + sfx_4176.wav \ + sfx_4177.wav \ + sfx_4178.wav \ + sfx_4179.wav \ + sfx_417.wav \ + sfx_4180.wav \ + sfx_4181.wav \ + sfx_4182.wav \ + sfx_4183.wav \ + sfx_4184.wav \ + sfx_4185.wav \ + sfx_4186.wav \ + sfx_4187.wav \ + sfx_4188.wav \ + sfx_4189.wav \ + sfx_418.wav \ + sfx_4190.wav \ + sfx_4191.wav \ + sfx_4192.wav \ + sfx_4193.wav \ + sfx_4194.wav \ + sfx_4195.wav \ + sfx_4196.wav \ + sfx_4197.wav \ + sfx_4198.wav \ + sfx_4199.wav \ + sfx_419.wav \ + sfx_41.wav \ + sfx_4200.wav \ + sfx_4201.wav \ + sfx_4202.wav \ + sfx_4203.wav \ + sfx_4204.wav \ + sfx_4205.wav \ + sfx_4206.wav \ + sfx_4207.wav \ + sfx_4208.wav \ + sfx_4209.wav \ + sfx_420.wav \ + sfx_4210.wav \ + sfx_4211.wav \ + sfx_4212.wav \ + sfx_4213.wav \ + sfx_4214.wav \ + sfx_4215.wav \ + sfx_4216.wav \ + sfx_4217.wav \ + sfx_4218.wav \ + sfx_4219.wav \ + sfx_421.wav \ + sfx_4220.wav \ + sfx_4221.wav \ + sfx_4222.wav \ + sfx_4223.wav \ + sfx_4224.wav \ + sfx_4225.wav \ + sfx_4226.wav \ + sfx_4227.wav \ + sfx_4228.wav \ + sfx_4229.wav \ + sfx_422.wav \ + sfx_4230.wav \ + sfx_4231.wav \ + sfx_4232.wav \ + sfx_4233.wav \ + sfx_4234.wav \ + sfx_4235.wav \ + sfx_4236.wav \ + sfx_4237.wav \ + sfx_4238.wav \ + sfx_4239.wav \ + sfx_423.wav \ + sfx_4240.wav \ + sfx_4241.wav \ + sfx_4242.wav \ + sfx_4243.wav \ + sfx_4244.wav \ + sfx_4245.wav \ + sfx_4246.wav \ + sfx_4247.wav \ + sfx_4248.wav \ + sfx_4249.wav \ + sfx_424.wav \ + sfx_4250.wav \ + sfx_4251.wav \ + sfx_4252.wav \ + sfx_4253.wav \ + sfx_4254.wav \ + sfx_4255.wav \ + sfx_4256.wav \ + sfx_4257.wav \ + sfx_4258.wav \ + sfx_4259.wav \ + sfx_425.wav \ + sfx_4260.wav \ + sfx_4261.wav \ + sfx_4262.wav \ + sfx_4263.wav \ + sfx_4264.wav \ + sfx_4265.wav \ + sfx_4266.wav \ + sfx_4267.wav \ + sfx_4268.wav \ + sfx_4269.wav \ + sfx_426.wav \ + sfx_4270.wav \ + sfx_4271.wav \ + sfx_4272.wav \ + sfx_4273.wav \ + sfx_4274.wav \ + sfx_4275.wav \ + sfx_4276.wav \ + sfx_4277.wav \ + sfx_4278.wav \ + sfx_4279.wav \ + sfx_427.wav \ + sfx_4280.wav \ + sfx_4281.wav \ + sfx_4282.wav \ + sfx_4283.wav \ + sfx_4284.wav \ + sfx_4285.wav \ + sfx_4286.wav \ + sfx_4287.wav \ + sfx_4288.wav \ + sfx_4289.wav \ + sfx_428.wav \ + sfx_4290.wav \ + sfx_4291.wav \ + sfx_4292.wav \ + sfx_4293.wav \ + sfx_4294.wav \ + sfx_4295.wav \ + sfx_4296.wav \ + sfx_4297.wav \ + sfx_4298.wav \ + sfx_4299.wav \ + sfx_429.wav \ + sfx_42.wav \ + sfx_4300.wav \ + sfx_4301.wav \ + sfx_4302.wav \ + sfx_4303.wav \ + sfx_4304.wav \ + sfx_4305.wav \ + sfx_4306.wav \ + sfx_4307.wav \ + sfx_4308.wav \ + sfx_4309.wav \ + sfx_430.wav \ + sfx_4310.wav \ + sfx_4311.wav \ + sfx_4312.wav \ + sfx_4313.wav \ + sfx_4314.wav \ + sfx_4315.wav \ + sfx_4316.wav \ + sfx_4317.wav \ + sfx_4318.wav \ + sfx_4319.wav \ + sfx_431.wav \ + sfx_4320.wav \ + sfx_4321.wav \ + sfx_4322.wav \ + sfx_4323.wav \ + sfx_4324.wav \ + sfx_4325.wav \ + sfx_4326.wav \ + sfx_4327.wav \ + sfx_4328.wav \ + sfx_4329.wav \ + sfx_432.wav \ + sfx_4330.wav \ + sfx_4331.wav \ + sfx_4332.wav \ + sfx_4333.wav \ + sfx_4334.wav \ + sfx_4335.wav \ + sfx_4336.wav \ + sfx_4337.wav \ + sfx_4338.wav \ + sfx_4339.wav \ + sfx_433.wav \ + sfx_4340.wav \ + sfx_4341.wav \ + sfx_4342.wav \ + sfx_4343.wav \ + sfx_4344.wav \ + sfx_4345.wav \ + sfx_4346.wav \ + sfx_4347.wav \ + sfx_4348.wav \ + sfx_4349.wav \ + sfx_434.wav \ + sfx_4350.wav \ + sfx_4351.wav \ + sfx_4352.wav \ + sfx_4353.wav \ + sfx_4354.wav \ + sfx_4355.wav \ + sfx_4356.wav \ + sfx_4357.wav \ + sfx_4358.wav \ + sfx_4359.wav \ + sfx_435.wav \ + sfx_4360.wav \ + sfx_4361.wav \ + sfx_4362.wav \ + sfx_4363.wav \ + sfx_4364.wav \ + sfx_4365.wav \ + sfx_4366.wav \ + sfx_4367.wav \ + sfx_4368.wav \ + sfx_4369.wav \ + sfx_436.wav \ + sfx_4370.wav \ + sfx_4371.wav \ + sfx_4372.wav \ + sfx_4373.wav \ + sfx_4374.wav \ + sfx_4375.wav \ + sfx_4376.wav \ + sfx_4377.wav \ + sfx_4378.wav \ + sfx_4379.wav \ + sfx_437.wav \ + sfx_4380.wav \ + sfx_4381.wav \ + sfx_4382.wav \ + sfx_4383.wav \ + sfx_4384.wav \ + sfx_4385.wav \ + sfx_4386.wav \ + sfx_4387.wav \ + sfx_4388.wav \ + sfx_4389.wav \ + sfx_438.wav \ + sfx_4390.wav \ + sfx_4391.wav \ + sfx_4392.wav \ + sfx_4393.wav \ + sfx_4394.wav \ + sfx_4395.wav \ + sfx_4396.wav \ + sfx_4397.wav \ + sfx_4398.wav \ + sfx_4399.wav \ + sfx_439.wav \ + sfx_43.wav \ + sfx_4400.wav \ + sfx_4401.wav \ + sfx_4402.wav \ + sfx_4403.wav \ + sfx_4404.wav \ + sfx_4405.wav \ + sfx_4406.wav \ + sfx_4407.wav \ + sfx_4408.wav \ + sfx_4409.wav \ + sfx_440.wav \ + sfx_4410.wav \ + sfx_4411.wav \ + sfx_4412.wav \ + sfx_4413.wav \ + sfx_4414.wav \ + sfx_4415.wav \ + sfx_4416.wav \ + sfx_4417.wav \ + sfx_4418.wav \ + sfx_4419.wav \ + sfx_441.wav \ + sfx_4420.wav \ + sfx_4421.wav \ + sfx_4422.wav \ + sfx_4423.wav \ + sfx_4424.wav \ + sfx_4425.wav \ + sfx_4426.wav \ + sfx_4427.wav \ + sfx_4428.wav \ + sfx_4429.wav \ + sfx_442.wav \ + sfx_4430.wav \ + sfx_4431.wav \ + sfx_4432.wav \ + sfx_4433.wav \ + sfx_4434.wav \ + sfx_4435.wav \ + sfx_4436.wav \ + sfx_4437.wav \ + sfx_4438.wav \ + sfx_4439.wav \ + sfx_443.wav \ + sfx_4440.wav \ + sfx_4441.wav \ + sfx_4442.wav \ + sfx_4443.wav \ + sfx_4444.wav \ + sfx_4445.wav \ + sfx_4446.wav \ + sfx_4447.wav \ + sfx_4448.wav \ + sfx_4449.wav \ + sfx_444.wav \ + sfx_4450.wav \ + sfx_4451.wav \ + sfx_4452.wav \ + sfx_4453.wav \ + sfx_4454.wav \ + sfx_4455.wav \ + sfx_4456.wav \ + sfx_4457.wav \ + sfx_4458.wav \ + sfx_4459.wav \ + sfx_445.wav \ + sfx_4460.wav \ + sfx_4461.wav \ + sfx_4462.wav \ + sfx_4463.wav \ + sfx_4464.wav \ + sfx_4465.wav \ + sfx_4466.wav \ + sfx_4467.wav \ + sfx_4468.wav \ + sfx_4469.wav \ + sfx_446.wav \ + sfx_4470.wav \ + sfx_4471.wav \ + sfx_4472.wav \ + sfx_4473.wav \ + sfx_4474.wav \ + sfx_4475.wav \ + sfx_4476.wav \ + sfx_4477.wav \ + sfx_4478.wav \ + sfx_4479.wav \ + sfx_447.wav \ + sfx_4480.wav \ + sfx_4481.wav \ + sfx_4482.wav \ + sfx_4483.wav \ + sfx_4484.wav \ + sfx_4485.wav \ + sfx_4486.wav \ + sfx_4487.wav \ + sfx_4488.wav \ + sfx_4489.wav \ + sfx_448.wav \ + sfx_4490.wav \ + sfx_4491.wav \ + sfx_4492.wav \ + sfx_4493.wav \ + sfx_4494.wav \ + sfx_4495.wav \ + sfx_4496.wav \ + sfx_4497.wav \ + sfx_4498.wav \ + sfx_4499.wav \ + sfx_449.wav \ + sfx_44.wav \ + sfx_4500.wav \ + sfx_4501.wav \ + sfx_4502.wav \ + sfx_4503.wav \ + sfx_4504.wav \ + sfx_4505.wav \ + sfx_4506.wav \ + sfx_4507.wav \ + sfx_4508.wav \ + sfx_4509.wav \ + sfx_450.wav \ + sfx_4510.wav \ + sfx_4511.wav \ + sfx_4512.wav \ + sfx_4513.wav \ + sfx_4514.wav \ + sfx_4515.wav \ + sfx_4516.wav \ + sfx_4517.wav \ + sfx_4518.wav \ + sfx_4519.wav \ + sfx_451.wav \ + sfx_4520.wav \ + sfx_4521.wav \ + sfx_4522.wav \ + sfx_4523.wav \ + sfx_4524.wav \ + sfx_4525.wav \ + sfx_4526.wav \ + sfx_4527.wav \ + sfx_4528.wav \ + sfx_4529.wav \ + sfx_452.wav \ + sfx_4530.wav \ + sfx_4531.wav \ + sfx_4532.wav \ + sfx_4533.wav \ + sfx_4534.wav \ + sfx_4535.wav \ + sfx_4536.wav \ + sfx_4537.wav \ + sfx_4538.wav \ + sfx_4539.wav \ + sfx_453.wav \ + sfx_4540.wav \ + sfx_4541.wav \ + sfx_4542.wav \ + sfx_4543.wav \ + sfx_4544.wav \ + sfx_4545.wav \ + sfx_4546.wav \ + sfx_4547.wav \ + sfx_4548.wav \ + sfx_4549.wav \ + sfx_454.wav \ + sfx_4550.wav \ + sfx_4551.wav \ + sfx_4552.wav \ + sfx_4553.wav \ + sfx_4554.wav \ + sfx_4555.wav \ + sfx_4556.wav \ + sfx_4557.wav \ + sfx_4558.wav \ + sfx_4559.wav \ + sfx_455.wav \ + sfx_4560.wav \ + sfx_4561.wav \ + sfx_4562.wav \ + sfx_4563.wav \ + sfx_4564.wav \ + sfx_4565.wav \ + sfx_4566.wav \ + sfx_4567.wav \ + sfx_4568.wav \ + sfx_4569.wav \ + sfx_456.wav \ + sfx_4570.wav \ + sfx_4571.wav \ + sfx_4572.wav \ + sfx_4573.wav \ + sfx_4574.wav \ + sfx_4575.wav \ + sfx_4576.wav \ + sfx_4577.wav \ + sfx_4578.wav \ + sfx_4579.wav \ + sfx_457.wav \ + sfx_4580.wav \ + sfx_4581.wav \ + sfx_4582.wav \ + sfx_4583.wav \ + sfx_4584.wav \ + sfx_4585.wav \ + sfx_4586.wav \ + sfx_4587.wav \ + sfx_4588.wav \ + sfx_4589.wav \ + sfx_458.wav \ + sfx_4590.wav \ + sfx_4591.wav \ + sfx_4592.wav \ + sfx_4593.wav \ + sfx_4594.wav \ + sfx_4595.wav \ + sfx_4596.wav \ + sfx_4597.wav \ + sfx_4598.wav \ + sfx_4599.wav \ + sfx_459.wav \ + sfx_45.wav \ + sfx_4600.wav \ + sfx_4601.wav \ + sfx_4602.wav \ + sfx_4603.wav \ + sfx_4604.wav \ + sfx_4605.wav \ + sfx_4606.wav \ + sfx_4607.wav \ + sfx_4608.wav \ + sfx_4609.wav \ + sfx_460.wav \ + sfx_4610.wav \ + sfx_4611.wav \ + sfx_4612.wav \ + sfx_4613.wav \ + sfx_4614.wav \ + sfx_4615.wav \ + sfx_4616.wav \ + sfx_4617.wav \ + sfx_4618.wav \ + sfx_4619.wav \ + sfx_461.wav \ + sfx_4620.wav \ + sfx_4621.wav \ + sfx_4622.wav \ + sfx_4623.wav \ + sfx_4624.wav \ + sfx_4625.wav \ + sfx_4626.wav \ + sfx_4627.wav \ + sfx_4628.wav \ + sfx_4629.wav \ + sfx_462.wav \ + sfx_4630.wav \ + sfx_4631.wav \ + sfx_4632.wav \ + sfx_4633.wav \ + sfx_4634.wav \ + sfx_4635.wav \ + sfx_4636.wav \ + sfx_4637.wav \ + sfx_4638.wav \ + sfx_4639.wav \ + sfx_463.wav \ + sfx_4640.wav \ + sfx_4641.wav \ + sfx_4642.wav \ + sfx_4643.wav \ + sfx_4644.wav \ + sfx_4645.wav \ + sfx_4646.wav \ + sfx_4647.wav \ + sfx_4648.wav \ + sfx_4649.wav \ + sfx_464.wav \ + sfx_4650.wav \ + sfx_4651.wav \ + sfx_4652.wav \ + sfx_4653.wav \ + sfx_4654.wav \ + sfx_4655.wav \ + sfx_4656.wav \ + sfx_4657.wav \ + sfx_4658.wav \ + sfx_4659.wav \ + sfx_465.wav \ + sfx_4660.wav \ + sfx_4661.wav \ + sfx_4662.wav \ + sfx_4663.wav \ + sfx_4664.wav \ + sfx_4665.wav \ + sfx_4666.wav \ + sfx_4667.wav \ + sfx_4668.wav \ + sfx_4669.wav \ + sfx_466.wav \ + sfx_4670.wav \ + sfx_4671.wav \ + sfx_4672.wav \ + sfx_4673.wav \ + sfx_4674.wav \ + sfx_4675.wav \ + sfx_4676.wav \ + sfx_4677.wav \ + sfx_4678.wav \ + sfx_4679.wav \ + sfx_467.wav \ + sfx_4680.wav \ + sfx_4681.wav \ + sfx_4682.wav \ + sfx_4683.wav \ + sfx_4684.wav \ + sfx_4685.wav \ + sfx_4686.wav \ + sfx_4687.wav \ + sfx_4688.wav \ + sfx_4689.wav \ + sfx_468.wav \ + sfx_4690.wav \ + sfx_4691.wav \ + sfx_4692.wav \ + sfx_4693.wav \ + sfx_4694.wav \ + sfx_4695.wav \ + sfx_4696.wav \ + sfx_4697.wav \ + sfx_4698.wav \ + sfx_4699.wav \ + sfx_469.wav \ + sfx_46.wav \ + sfx_4700.wav \ + sfx_4701.wav \ + sfx_4702.wav \ + sfx_4703.wav \ + sfx_4704.wav \ + sfx_4705.wav \ + sfx_4706.wav \ + sfx_4707.wav \ + sfx_4708.wav \ + sfx_4709.wav \ + sfx_470.wav \ + sfx_4710.wav \ + sfx_4711.wav \ + sfx_4712.wav \ + sfx_4713.wav \ + sfx_4714.wav \ + sfx_4715.wav \ + sfx_4716.wav \ + sfx_4717.wav \ + sfx_4718.wav \ + sfx_4719.wav \ + sfx_471.wav \ + sfx_4720.wav \ + sfx_4721.wav \ + sfx_4722.wav \ + sfx_4723.wav \ + sfx_4724.wav \ + sfx_4725.wav \ + sfx_4726.wav \ + sfx_4727.wav \ + sfx_4728.wav \ + sfx_4729.wav \ + sfx_472.wav \ + sfx_4730.wav \ + sfx_4731.wav \ + sfx_4732.wav \ + sfx_4733.wav \ + sfx_4734.wav \ + sfx_4735.wav \ + sfx_4736.wav \ + sfx_4737.wav \ + sfx_4738.wav \ + sfx_4739.wav \ + sfx_473.wav \ + sfx_4740.wav \ + sfx_4741.wav \ + sfx_4742.wav \ + sfx_4743.wav \ + sfx_4744.wav \ + sfx_4745.wav \ + sfx_4746.wav \ + sfx_4747.wav \ + sfx_4748.wav \ + sfx_4749.wav \ + sfx_474.wav \ + sfx_4750.wav \ + sfx_4751.wav \ + sfx_4752.wav \ + sfx_4753.wav \ + sfx_4754.wav \ + sfx_4755.wav \ + sfx_4756.wav \ + sfx_4757.wav \ + sfx_4758.wav \ + sfx_4759.wav \ + sfx_475.wav \ + sfx_4760.wav \ + sfx_4761.wav \ + sfx_4762.wav \ + sfx_4763.wav \ + sfx_4764.wav \ + sfx_4765.wav \ + sfx_4766.wav \ + sfx_4767.wav \ + sfx_4768.wav \ + sfx_4769.wav \ + sfx_476.wav \ + sfx_4770.wav \ + sfx_4771.wav \ + sfx_4772.wav \ + sfx_4773.wav \ + sfx_4774.wav \ + sfx_4775.wav \ + sfx_4776.wav \ + sfx_4777.wav \ + sfx_4778.wav \ + sfx_4779.wav \ + sfx_477.wav \ + sfx_4780.wav \ + sfx_4781.wav \ + sfx_4782.wav \ + sfx_4783.wav \ + sfx_4784.wav \ + sfx_4785.wav \ + sfx_4786.wav \ + sfx_4787.wav \ + sfx_4788.wav \ + sfx_4789.wav \ + sfx_478.wav \ + sfx_4790.wav \ + sfx_4791.wav \ + sfx_4792.wav \ + sfx_4793.wav \ + sfx_4794.wav \ + sfx_4795.wav \ + sfx_4796.wav \ + sfx_4797.wav \ + sfx_4798.wav \ + sfx_4799.wav \ + sfx_479.wav \ + sfx_47.wav \ + sfx_4800.wav \ + sfx_4801.wav \ + sfx_4802.wav \ + sfx_4803.wav \ + sfx_4804.wav \ + sfx_4805.wav \ + sfx_4806.wav \ + sfx_4807.wav \ + sfx_4808.wav \ + sfx_4809.wav \ + sfx_480.wav \ + sfx_4810.wav \ + sfx_4811.wav \ + sfx_4812.wav \ + sfx_4813.wav \ + sfx_4814.wav \ + sfx_4815.wav \ + sfx_4816.wav \ + sfx_4817.wav \ + sfx_4818.wav \ + sfx_4819.wav \ + sfx_481.wav \ + sfx_4820.wav \ + sfx_4821.wav \ + sfx_4822.wav \ + sfx_4823.wav \ + sfx_4824.wav \ + sfx_4825.wav \ + sfx_4826.wav \ + sfx_4827.wav \ + sfx_4828.wav \ + sfx_4829.wav \ + sfx_482.wav \ + sfx_4830.wav \ + sfx_4831.wav \ + sfx_4832.wav \ + sfx_4833.wav \ + sfx_4834.wav \ + sfx_4835.wav \ + sfx_4836.wav \ + sfx_4837.wav \ + sfx_4838.wav \ + sfx_4839.wav \ + sfx_483.wav \ + sfx_4840.wav \ + sfx_4841.wav \ + sfx_4842.wav \ + sfx_4843.wav \ + sfx_4844.wav \ + sfx_4845.wav \ + sfx_4846.wav \ + sfx_4847.wav \ + sfx_4848.wav \ + sfx_4849.wav \ + sfx_484.wav \ + sfx_4850.wav \ + sfx_4851.wav \ + sfx_4852.wav \ + sfx_4853.wav \ + sfx_4854.wav \ + sfx_4855.wav \ + sfx_4856.wav \ + sfx_4857.wav \ + sfx_4858.wav \ + sfx_4859.wav \ + sfx_485.wav \ + sfx_4860.wav \ + sfx_4861.wav \ + sfx_4862.wav \ + sfx_4863.wav \ + sfx_4864.wav \ + sfx_4865.wav \ + sfx_4866.wav \ + sfx_4867.wav \ + sfx_4868.wav \ + sfx_4869.wav \ + sfx_486.wav \ + sfx_4870.wav \ + sfx_4871.wav \ + sfx_4872.wav \ + sfx_4873.wav \ + sfx_4874.wav \ + sfx_4875.wav \ + sfx_4876.wav \ + sfx_4877.wav \ + sfx_4878.wav \ + sfx_4879.wav \ + sfx_487.wav \ + sfx_4880.wav \ + sfx_4881.wav \ + sfx_4882.wav \ + sfx_4883.wav \ + sfx_4884.wav \ + sfx_4885.wav \ + sfx_4886.wav \ + sfx_4887.wav \ + sfx_4888.wav \ + sfx_4889.wav \ + sfx_488.wav \ + sfx_4890.wav \ + sfx_4891.wav \ + sfx_4892.wav \ + sfx_4893.wav \ + sfx_4894.wav \ + sfx_4895.wav \ + sfx_4896.wav \ + sfx_4897.wav \ + sfx_4898.wav \ + sfx_4899.wav \ + sfx_489.wav \ + sfx_48.wav \ + sfx_4900.wav \ + sfx_4901.wav \ + sfx_4902.wav \ + sfx_4903.wav \ + sfx_4904.wav \ + sfx_4905.wav \ + sfx_4906.wav \ + sfx_4907.wav \ + sfx_4908.wav \ + sfx_4909.wav \ + sfx_490.wav \ + sfx_4910.wav \ + sfx_4911.wav \ + sfx_4912.wav \ + sfx_4913.wav \ + sfx_4914.wav \ + sfx_4915.wav \ + sfx_4916.wav \ + sfx_4917.wav \ + sfx_4918.wav \ + sfx_4919.wav \ + sfx_491.wav \ + sfx_4920.wav \ + sfx_4921.wav \ + sfx_4922.wav \ + sfx_4923.wav \ + sfx_4924.wav \ + sfx_4925.wav \ + sfx_4926.wav \ + sfx_4927.wav \ + sfx_4928.wav \ + sfx_4929.wav \ + sfx_492.wav \ + sfx_4930.wav \ + sfx_4931.wav \ + sfx_4932.wav \ + sfx_4933.wav \ + sfx_4934.wav \ + sfx_4935.wav \ + sfx_4936.wav \ + sfx_4937.wav \ + sfx_4938.wav \ + sfx_4939.wav \ + sfx_493.wav \ + sfx_4940.wav \ + sfx_4941.wav \ + sfx_4942.wav \ + sfx_4943.wav \ + sfx_4944.wav \ + sfx_4945.wav \ + sfx_4946.wav \ + sfx_4947.wav \ + sfx_4948.wav \ + sfx_4949.wav \ + sfx_494.wav \ + sfx_4950.wav \ + sfx_4951.wav \ + sfx_4952.wav \ + sfx_4953.wav \ + sfx_4954.wav \ + sfx_4955.wav \ + sfx_4956.wav \ + sfx_4957.wav \ + sfx_4958.wav \ + sfx_4959.wav \ + sfx_495.wav \ + sfx_4960.wav \ + sfx_4961.wav \ + sfx_4962.wav \ + sfx_4963.wav \ + sfx_4964.wav \ + sfx_4965.wav \ + sfx_4966.wav \ + sfx_4967.wav \ + sfx_4968.wav \ + sfx_4969.wav \ + sfx_496.wav \ + sfx_4970.wav \ + sfx_4971.wav \ + sfx_4972.wav \ + sfx_4973.wav \ + sfx_4974.wav \ + sfx_4975.wav \ + sfx_4976.wav \ + sfx_4977.wav \ + sfx_4978.wav \ + sfx_4979.wav \ + sfx_497.wav \ + sfx_4980.wav \ + sfx_4981.wav \ + sfx_4982.wav \ + sfx_4983.wav \ + sfx_4984.wav \ + sfx_4985.wav \ + sfx_4986.wav \ + sfx_4987.wav \ + sfx_4988.wav \ + sfx_4989.wav \ + sfx_498.wav \ + sfx_4990.wav \ + sfx_4991.wav \ + sfx_4992.wav \ + sfx_4993.wav \ + sfx_4994.wav \ + sfx_4995.wav \ + sfx_4996.wav \ + sfx_4997.wav \ + sfx_4998.wav \ + sfx_4999.wav \ + sfx_499.wav \ + sfx_49.wav \ + sfx_4.wav \ + sfx_5000.wav \ + sfx_5001.wav \ + sfx_5002.wav \ + sfx_5003.wav \ + sfx_5004.wav \ + sfx_5005.wav \ + sfx_5006.wav \ + sfx_5007.wav \ + sfx_5008.wav \ + sfx_5009.wav \ + sfx_500.wav \ + sfx_5010.wav \ + sfx_5011.wav \ + sfx_5012.wav \ + sfx_5013.wav \ + sfx_5014.wav \ + sfx_5015.wav \ + sfx_5016.wav \ + sfx_5017.wav \ + sfx_5018.wav \ + sfx_5019.wav \ + sfx_501.wav \ + sfx_5020.wav \ + sfx_5021.wav \ + sfx_5022.wav \ + sfx_5023.wav \ + sfx_5024.wav \ + sfx_5025.wav \ + sfx_5026.wav \ + sfx_5027.wav \ + sfx_5028.wav \ + sfx_5029.wav \ + sfx_502.wav \ + sfx_5030.wav \ + sfx_5031.wav \ + sfx_5032.wav \ + sfx_5033.wav \ + sfx_5034.wav \ + sfx_5035.wav \ + sfx_5036.wav \ + sfx_5037.wav \ + sfx_5038.wav \ + sfx_5039.wav \ + sfx_503.wav \ + sfx_5040.wav \ + sfx_5041.wav \ + sfx_5042.wav \ + sfx_5043.wav \ + sfx_5044.wav \ + sfx_5045.wav \ + sfx_5046.wav \ + sfx_5047.wav \ + sfx_5048.wav \ + sfx_5049.wav \ + sfx_504.wav \ + sfx_5050.wav \ + sfx_5051.wav \ + sfx_5052.wav \ + sfx_5053.wav \ + sfx_5054.wav \ + sfx_5055.wav \ + sfx_5056.wav \ + sfx_5057.wav \ + sfx_5058.wav \ + sfx_5059.wav \ + sfx_505.wav \ + sfx_5060.wav \ + sfx_5061.wav \ + sfx_5062.wav \ + sfx_5063.wav \ + sfx_5064.wav \ + sfx_5065.wav \ + sfx_5066.wav \ + sfx_5067.wav \ + sfx_5068.wav \ + sfx_5069.wav \ + sfx_506.wav \ + sfx_5070.wav \ + sfx_5071.wav \ + sfx_5072.wav \ + sfx_5073.wav \ + sfx_5074.wav \ + sfx_5075.wav \ + sfx_5076.wav \ + sfx_5077.wav \ + sfx_5078.wav \ + sfx_5079.wav \ + sfx_507.wav \ + sfx_5080.wav \ + sfx_5081.wav \ + sfx_5082.wav \ + sfx_5083.wav \ + sfx_5084.wav \ + sfx_5085.wav \ + sfx_5086.wav \ + sfx_5087.wav \ + sfx_5088.wav \ + sfx_5089.wav \ + sfx_508.wav \ + sfx_5090.wav \ + sfx_5091.wav \ + sfx_5092.wav \ + sfx_5093.wav \ + sfx_5094.wav \ + sfx_5095.wav \ + sfx_5096.wav \ + sfx_5097.wav \ + sfx_5098.wav \ + sfx_5099.wav \ + sfx_509.wav \ + sfx_50.wav \ + sfx_5100.wav \ + sfx_5101.wav \ + sfx_5102.wav \ + sfx_5103.wav \ + sfx_5104.wav \ + sfx_5105.wav \ + sfx_5106.wav \ + sfx_5107.wav \ + sfx_5108.wav \ + sfx_5109.wav \ + sfx_510.wav \ + sfx_5110.wav \ + sfx_5111.wav \ + sfx_5112.wav \ + sfx_5113.wav \ + sfx_5114.wav \ + sfx_5115.wav \ + sfx_5116.wav \ + sfx_5117.wav \ + sfx_5118.wav \ + sfx_5119.wav \ + sfx_511.wav \ + sfx_5120.wav \ + sfx_5121.wav \ + sfx_5122.wav \ + sfx_5123.wav \ + sfx_5124.wav \ + sfx_5125.wav \ + sfx_5126.wav \ + sfx_5127.wav \ + sfx_5128.wav \ + sfx_5129.wav \ + sfx_512.wav \ + sfx_5130.wav \ + sfx_5131.wav \ + sfx_5132.wav \ + sfx_5133.wav \ + sfx_5134.wav \ + sfx_5135.wav \ + sfx_5136.wav \ + sfx_5137.wav \ + sfx_5138.wav \ + sfx_5139.wav \ + sfx_513.wav \ + sfx_5140.wav \ + sfx_5141.wav \ + sfx_5142.wav \ + sfx_5143.wav \ + sfx_5144.wav \ + sfx_5145.wav \ + sfx_5146.wav \ + sfx_5147.wav \ + sfx_5148.wav \ + sfx_5149.wav \ + sfx_514.wav \ + sfx_5150.wav \ + sfx_5151.wav \ + sfx_5152.wav \ + sfx_5153.wav \ + sfx_5154.wav \ + sfx_5155.wav \ + sfx_5156.wav \ + sfx_5157.wav \ + sfx_5158.wav \ + sfx_5159.wav \ + sfx_515.wav \ + sfx_5160.wav \ + sfx_5161.wav \ + sfx_5162.wav \ + sfx_5163.wav \ + sfx_5164.wav \ + sfx_5165.wav \ + sfx_5166.wav \ + sfx_5167.wav \ + sfx_5168.wav \ + sfx_5169.wav \ + sfx_516.wav \ + sfx_5170.wav \ + sfx_5171.wav \ + sfx_5172.wav \ + sfx_5173.wav \ + sfx_5174.wav \ + sfx_5175.wav \ + sfx_5176.wav \ + sfx_5177.wav \ + sfx_5178.wav \ + sfx_5179.wav \ + sfx_517.wav \ + sfx_5180.wav \ + sfx_5181.wav \ + sfx_5182.wav \ + sfx_5183.wav \ + sfx_5184.wav \ + sfx_5185.wav \ + sfx_5186.wav \ + sfx_5187.wav \ + sfx_5188.wav \ + sfx_5189.wav \ + sfx_518.wav \ + sfx_5190.wav \ + sfx_5191.wav \ + sfx_5192.wav \ + sfx_5193.wav \ + sfx_5194.wav \ + sfx_5195.wav \ + sfx_5196.wav \ + sfx_5197.wav \ + sfx_5198.wav \ + sfx_5199.wav \ + sfx_519.wav \ + sfx_51.wav \ + sfx_5200.wav \ + sfx_5201.wav \ + sfx_5202.wav \ + sfx_5203.wav \ + sfx_5204.wav \ + sfx_5205.wav \ + sfx_5206.wav \ + sfx_5207.wav \ + sfx_5208.wav \ + sfx_5209.wav \ + sfx_520.wav \ + sfx_5210.wav \ + sfx_5211.wav \ + sfx_5212.wav \ + sfx_5213.wav \ + sfx_5214.wav \ + sfx_5215.wav \ + sfx_5216.wav \ + sfx_5217.wav \ + sfx_5218.wav \ + sfx_5219.wav \ + sfx_521.wav \ + sfx_5220.wav \ + sfx_5221.wav \ + sfx_5222.wav \ + sfx_5223.wav \ + sfx_5224.wav \ + sfx_5225.wav \ + sfx_5226.wav \ + sfx_5227.wav \ + sfx_5228.wav \ + sfx_5229.wav \ + sfx_522.wav \ + sfx_5230.wav \ + sfx_5231.wav \ + sfx_5232.wav \ + sfx_5233.wav \ + sfx_5234.wav \ + sfx_5235.wav \ + sfx_5236.wav \ + sfx_5237.wav \ + sfx_5238.wav \ + sfx_5239.wav \ + sfx_523.wav \ + sfx_5240.wav \ + sfx_5241.wav \ + sfx_5242.wav \ + sfx_5243.wav \ + sfx_5244.wav \ + sfx_5245.wav \ + sfx_5246.wav \ + sfx_5247.wav \ + sfx_5248.wav \ + sfx_5249.wav \ + sfx_524.wav \ + sfx_5250.wav \ + sfx_5251.wav \ + sfx_5252.wav \ + sfx_5253.wav \ + sfx_5254.wav \ + sfx_5255.wav \ + sfx_5256.wav \ + sfx_5257.wav \ + sfx_5258.wav \ + sfx_5259.wav \ + sfx_525.wav \ + sfx_5260.wav \ + sfx_5261.wav \ + sfx_5262.wav \ + sfx_5263.wav \ + sfx_5264.wav \ + sfx_5265.wav \ + sfx_5266.wav \ + sfx_5267.wav \ + sfx_5268.wav \ + sfx_5269.wav \ + sfx_526.wav \ + sfx_5270.wav \ + sfx_5271.wav \ + sfx_5272.wav \ + sfx_5273.wav \ + sfx_5274.wav \ + sfx_5275.wav \ + sfx_5276.wav \ + sfx_5277.wav \ + sfx_5278.wav \ + sfx_5279.wav \ + sfx_527.wav \ + sfx_5280.wav \ + sfx_5281.wav \ + sfx_5282.wav \ + sfx_5283.wav \ + sfx_5284.wav \ + sfx_5285.wav \ + sfx_5286.wav \ + sfx_5287.wav \ + sfx_5288.wav \ + sfx_5289.wav \ + sfx_528.wav \ + sfx_5290.wav \ + sfx_5291.wav \ + sfx_5292.wav \ + sfx_5293.wav \ + sfx_5294.wav \ + sfx_5295.wav \ + sfx_5296.wav \ + sfx_5297.wav \ + sfx_5298.wav \ + sfx_5299.wav \ + sfx_529.wav \ + sfx_52.wav \ + sfx_5300.wav \ + sfx_5301.wav \ + sfx_5302.wav \ + sfx_5303.wav \ + sfx_5304.wav \ + sfx_5305.wav \ + sfx_5306.wav \ + sfx_5307.wav \ + sfx_5308.wav \ + sfx_5309.wav \ + sfx_530.wav \ + sfx_5310.wav \ + sfx_5311.wav \ + sfx_5312.wav \ + sfx_5313.wav \ + sfx_5314.wav \ + sfx_5315.wav \ + sfx_5316.wav \ + sfx_5317.wav \ + sfx_5318.wav \ + sfx_5319.wav \ + sfx_531.wav \ + sfx_5320.wav \ + sfx_5321.wav \ + sfx_5322.wav \ + sfx_5323.wav \ + sfx_5324.wav \ + sfx_5325.wav \ + sfx_5326.wav \ + sfx_5327.wav \ + sfx_5328.wav \ + sfx_5329.wav \ + sfx_532.wav \ + sfx_5330.wav \ + sfx_5331.wav \ + sfx_5332.wav \ + sfx_5333.wav \ + sfx_5334.wav \ + sfx_5335.wav \ + sfx_5336.wav \ + sfx_5337.wav \ + sfx_5338.wav \ + sfx_5339.wav \ + sfx_533.wav \ + sfx_5340.wav \ + sfx_5341.wav \ + sfx_5342.wav \ + sfx_5343.wav \ + sfx_5344.wav \ + sfx_5345.wav \ + sfx_5346.wav \ + sfx_5347.wav \ + sfx_5348.wav \ + sfx_5349.wav \ + sfx_534.wav \ + sfx_5350.wav \ + sfx_5351.wav \ + sfx_5352.wav \ + sfx_5353.wav \ + sfx_5354.wav \ + sfx_5355.wav \ + sfx_5356.wav \ + sfx_5357.wav \ + sfx_5358.wav \ + sfx_5359.wav \ + sfx_535.wav \ + sfx_5360.wav \ + sfx_5361.wav \ + sfx_5362.wav \ + sfx_5363.wav \ + sfx_5364.wav \ + sfx_5365.wav \ + sfx_5366.wav \ + sfx_5367.wav \ + sfx_5368.wav \ + sfx_5369.wav \ + sfx_536.wav \ + sfx_5370.wav \ + sfx_5371.wav \ + sfx_5372.wav \ + sfx_5373.wav \ + sfx_5374.wav \ + sfx_5375.wav \ + sfx_5376.wav \ + sfx_5377.wav \ + sfx_5378.wav \ + sfx_5379.wav \ + sfx_537.wav \ + sfx_5380.wav \ + sfx_5381.wav \ + sfx_5382.wav \ + sfx_5383.wav \ + sfx_5384.wav \ + sfx_5385.wav \ + sfx_5386.wav \ + sfx_5387.wav \ + sfx_5388.wav \ + sfx_5389.wav \ + sfx_538.wav \ + sfx_5390.wav \ + sfx_5391.wav \ + sfx_5392.wav \ + sfx_5393.wav \ + sfx_5394.wav \ + sfx_5395.wav \ + sfx_5396.wav \ + sfx_5397.wav \ + sfx_5398.wav \ + sfx_5399.wav \ + sfx_539.wav \ + sfx_53.wav \ + sfx_5400.wav \ + sfx_5401.wav \ + sfx_5402.wav \ + sfx_5403.wav \ + sfx_5404.wav \ + sfx_5405.wav \ + sfx_5406.wav \ + sfx_5407.wav \ + sfx_5408.wav \ + sfx_5409.wav \ + sfx_540.wav \ + sfx_5410.wav \ + sfx_5411.wav \ + sfx_5412.wav \ + sfx_5413.wav \ + sfx_5414.wav \ + sfx_5415.wav \ + sfx_5416.wav \ + sfx_5417.wav \ + sfx_5418.wav \ + sfx_5419.wav \ + sfx_541.wav \ + sfx_5420.wav \ + sfx_5421.wav \ + sfx_5422.wav \ + sfx_5423.wav \ + sfx_5424.wav \ + sfx_5425.wav \ + sfx_5426.wav \ + sfx_5427.wav \ + sfx_5428.wav \ + sfx_5429.wav \ + sfx_542.wav \ + sfx_5430.wav \ + sfx_5431.wav \ + sfx_5432.wav \ + sfx_5433.wav \ + sfx_5434.wav \ + sfx_5435.wav \ + sfx_5436.wav \ + sfx_5437.wav \ + sfx_5438.wav \ + sfx_5439.wav \ + sfx_543.wav \ + sfx_5440.wav \ + sfx_5441.wav \ + sfx_5442.wav \ + sfx_5443.wav \ + sfx_5444.wav \ + sfx_5445.wav \ + sfx_5446.wav \ + sfx_5447.wav \ + sfx_5448.wav \ + sfx_5449.wav \ + sfx_544.wav \ + sfx_5450.wav \ + sfx_5451.wav \ + sfx_5452.wav \ + sfx_5453.wav \ + sfx_5454.wav \ + sfx_5455.wav \ + sfx_5456.wav \ + sfx_5457.wav \ + sfx_5458.wav \ + sfx_5459.wav \ + sfx_545.wav \ + sfx_5460.wav \ + sfx_5461.wav \ + sfx_5462.wav \ + sfx_5463.wav \ + sfx_5464.wav \ + sfx_5465.wav \ + sfx_5466.wav \ + sfx_5467.wav \ + sfx_5468.wav \ + sfx_5469.wav \ + sfx_546.wav \ + sfx_5470.wav \ + sfx_5471.wav \ + sfx_5472.wav \ + sfx_5473.wav \ + sfx_5474.wav \ + sfx_5475.wav \ + sfx_5476.wav \ + sfx_5477.wav \ + sfx_5478.wav \ + sfx_5479.wav \ + sfx_547.wav \ + sfx_5480.wav \ + sfx_5481.wav \ + sfx_5482.wav \ + sfx_5483.wav \ + sfx_5484.wav \ + sfx_5485.wav \ + sfx_5486.wav \ + sfx_5487.wav \ + sfx_5488.wav \ + sfx_5489.wav \ + sfx_548.wav \ + sfx_5490.wav \ + sfx_5491.wav \ + sfx_5492.wav \ + sfx_5493.wav \ + sfx_5494.wav \ + sfx_5495.wav \ + sfx_5496.wav \ + sfx_5497.wav \ + sfx_5498.wav \ + sfx_5499.wav \ + sfx_549.wav \ + sfx_54.wav \ + sfx_5500.wav \ + sfx_5501.wav \ + sfx_5502.wav \ + sfx_5503.wav \ + sfx_5504.wav \ + sfx_5505.wav \ + sfx_5506.wav \ + sfx_5507.wav \ + sfx_5508.wav \ + sfx_5509.wav \ + sfx_550.wav \ + sfx_5510.wav \ + sfx_5511.wav \ + sfx_5512.wav \ + sfx_5513.wav \ + sfx_5514.wav \ + sfx_5515.wav \ + sfx_5516.wav \ + sfx_5517.wav \ + sfx_5518.wav \ + sfx_5519.wav \ + sfx_551.wav \ + sfx_5520.wav \ + sfx_5521.wav \ + sfx_5522.wav \ + sfx_5523.wav \ + sfx_5524.wav \ + sfx_5525.wav \ + sfx_5526.wav \ + sfx_5527.wav \ + sfx_5528.wav \ + sfx_5529.wav \ + sfx_552.wav \ + sfx_5530.wav \ + sfx_5531.wav \ + sfx_5532.wav \ + sfx_5533.wav \ + sfx_5534.wav \ + sfx_5535.wav \ + sfx_5536.wav \ + sfx_5537.wav \ + sfx_5538.wav \ + sfx_5539.wav \ + sfx_553.wav \ + sfx_5540.wav \ + sfx_5541.wav \ + sfx_5542.wav \ + sfx_5543.wav \ + sfx_5544.wav \ + sfx_5545.wav \ + sfx_5546.wav \ + sfx_5547.wav \ + sfx_5548.wav \ + sfx_5549.wav \ + sfx_554.wav \ + sfx_5550.wav \ + sfx_5551.wav \ + sfx_5552.wav \ + sfx_5553.wav \ + sfx_5554.wav \ + sfx_5555.wav \ + sfx_5556.wav \ + sfx_5557.wav \ + sfx_5558.wav \ + sfx_5559.wav \ + sfx_555.wav \ + sfx_5560.wav \ + sfx_5561.wav \ + sfx_5562.wav \ + sfx_5563.wav \ + sfx_5564.wav \ + sfx_5565.wav \ + sfx_5566.wav \ + sfx_5567.wav \ + sfx_5568.wav \ + sfx_5569.wav \ + sfx_556.wav \ + sfx_5570.wav \ + sfx_5571.wav \ + sfx_5572.wav \ + sfx_5573.wav \ + sfx_5574.wav \ + sfx_5575.wav \ + sfx_5576.wav \ + sfx_5577.wav \ + sfx_5578.wav \ + sfx_5579.wav \ + sfx_557.wav \ + sfx_5580.wav \ + sfx_5581.wav \ + sfx_5582.wav \ + sfx_5583.wav \ + sfx_5584.wav \ + sfx_5585.wav \ + sfx_5586.wav \ + sfx_5587.wav \ + sfx_5588.wav \ + sfx_5589.wav \ + sfx_558.wav \ + sfx_5590.wav \ + sfx_5591.wav \ + sfx_5592.wav \ + sfx_5593.wav \ + sfx_5594.wav \ + sfx_5595.wav \ + sfx_5596.wav \ + sfx_5597.wav \ + sfx_5598.wav \ + sfx_5599.wav \ + sfx_559.wav \ + sfx_55.wav \ + sfx_5600.wav \ + sfx_5601.wav \ + sfx_5602.wav \ + sfx_5603.wav \ + sfx_5604.wav \ + sfx_5605.wav \ + sfx_5606.wav \ + sfx_5607.wav \ + sfx_5608.wav \ + sfx_5609.wav \ + sfx_560.wav \ + sfx_5610.wav \ + sfx_5611.wav \ + sfx_5612.wav \ + sfx_5613.wav \ + sfx_5614.wav \ + sfx_5615.wav \ + sfx_5616.wav \ + sfx_5617.wav \ + sfx_5618.wav \ + sfx_5619.wav \ + sfx_561.wav \ + sfx_5620.wav \ + sfx_5621.wav \ + sfx_5622.wav \ + sfx_5623.wav \ + sfx_5624.wav \ + sfx_5625.wav \ + sfx_5626.wav \ + sfx_5627.wav \ + sfx_5628.wav \ + sfx_5629.wav \ + sfx_562.wav \ + sfx_5630.wav \ + sfx_5631.wav \ + sfx_5632.wav \ + sfx_5633.wav \ + sfx_5634.wav \ + sfx_5635.wav \ + sfx_5636.wav \ + sfx_5637.wav \ + sfx_5638.wav \ + sfx_5639.wav \ + sfx_563.wav \ + sfx_5640.wav \ + sfx_5641.wav \ + sfx_5642.wav \ + sfx_5643.wav \ + sfx_5644.wav \ + sfx_5645.wav \ + sfx_5646.wav \ + sfx_5647.wav \ + sfx_5648.wav \ + sfx_5649.wav \ + sfx_564.wav \ + sfx_5650.wav \ + sfx_5651.wav \ + sfx_5652.wav \ + sfx_5653.wav \ + sfx_5654.wav \ + sfx_5655.wav \ + sfx_5656.wav \ + sfx_5657.wav \ + sfx_5658.wav \ + sfx_5659.wav \ + sfx_565.wav \ + sfx_5660.wav \ + sfx_5661.wav \ + sfx_5662.wav \ + sfx_5663.wav \ + sfx_5664.wav \ + sfx_5665.wav \ + sfx_5666.wav \ + sfx_5667.wav \ + sfx_5668.wav \ + sfx_5669.wav \ + sfx_566.wav \ + sfx_5670.wav \ + sfx_5671.wav \ + sfx_5672.wav \ + sfx_5673.wav \ + sfx_5674.wav \ + sfx_5675.wav \ + sfx_5676.wav \ + sfx_5677.wav \ + sfx_5678.wav \ + sfx_5679.wav \ + sfx_567.wav \ + sfx_5680.wav \ + sfx_5681.wav \ + sfx_5682.wav \ + sfx_5683.wav \ + sfx_5684.wav \ + sfx_5685.wav \ + sfx_5686.wav \ + sfx_5687.wav \ + sfx_5688.wav \ + sfx_5689.wav \ + sfx_568.wav \ + sfx_5690.wav \ + sfx_5691.wav \ + sfx_5692.wav \ + sfx_5693.wav \ + sfx_5694.wav \ + sfx_5695.wav \ + sfx_5696.wav \ + sfx_5697.wav \ + sfx_5698.wav \ + sfx_5699.wav \ + sfx_569.wav \ + sfx_56.wav \ + sfx_5700.wav \ + sfx_5701.wav \ + sfx_5702.wav \ + sfx_5703.wav \ + sfx_5704.wav \ + sfx_5705.wav \ + sfx_5706.wav \ + sfx_5707.wav \ + sfx_5708.wav \ + sfx_5709.wav \ + sfx_570.wav \ + sfx_5710.wav \ + sfx_5711.wav \ + sfx_5712.wav \ + sfx_5713.wav \ + sfx_5714.wav \ + sfx_5715.wav \ + sfx_5716.wav \ + sfx_5717.wav \ + sfx_5718.wav \ + sfx_5719.wav \ + sfx_571.wav \ + sfx_5720.wav \ + sfx_5721.wav \ + sfx_5722.wav \ + sfx_5723.wav \ + sfx_5724.wav \ + sfx_5725.wav \ + sfx_5726.wav \ + sfx_5727.wav \ + sfx_5728.wav \ + sfx_5729.wav \ + sfx_572.wav \ + sfx_5730.wav \ + sfx_5731.wav \ + sfx_5732.wav \ + sfx_5733.wav \ + sfx_5734.wav \ + sfx_5735.wav \ + sfx_5736.wav \ + sfx_5737.wav \ + sfx_5738.wav \ + sfx_5739.wav \ + sfx_573.wav \ + sfx_5740.wav \ + sfx_5741.wav \ + sfx_5742.wav \ + sfx_5743.wav \ + sfx_5744.wav \ + sfx_5745.wav \ + sfx_5746.wav \ + sfx_5747.wav \ + sfx_5748.wav \ + sfx_5749.wav \ + sfx_574.wav \ + sfx_5750.wav \ + sfx_5751.wav \ + sfx_5752.wav \ + sfx_5753.wav \ + sfx_5754.wav \ + sfx_5755.wav \ + sfx_5756.wav \ + sfx_5757.wav \ + sfx_5758.wav \ + sfx_5759.wav \ + sfx_575.wav \ + sfx_5760.wav \ + sfx_5761.wav \ + sfx_5762.wav \ + sfx_5763.wav \ + sfx_5764.wav \ + sfx_5765.wav \ + sfx_5766.wav \ + sfx_5767.wav \ + sfx_5768.wav \ + sfx_5769.wav \ + sfx_576.wav \ + sfx_5770.wav \ + sfx_5771.wav \ + sfx_5772.wav \ + sfx_5773.wav \ + sfx_5774.wav \ + sfx_5775.wav \ + sfx_5776.wav \ + sfx_5777.wav \ + sfx_5778.wav \ + sfx_5779.wav \ + sfx_577.wav \ + sfx_5780.wav \ + sfx_5781.wav \ + sfx_5782.wav \ + sfx_5783.wav \ + sfx_5784.wav \ + sfx_5785.wav \ + sfx_5786.wav \ + sfx_5787.wav \ + sfx_5788.wav \ + sfx_5789.wav \ + sfx_578.wav \ + sfx_5790.wav \ + sfx_5791.wav \ + sfx_5792.wav \ + sfx_5793.wav \ + sfx_5794.wav \ + sfx_5795.wav \ + sfx_5796.wav \ + sfx_5797.wav \ + sfx_5798.wav \ + sfx_5799.wav \ + sfx_579.wav \ + sfx_57.wav \ + sfx_5800.wav \ + sfx_5801.wav \ + sfx_5802.wav \ + sfx_5803.wav \ + sfx_5804.wav \ + sfx_5805.wav \ + sfx_5806.wav \ + sfx_5807.wav \ + sfx_5808.wav \ + sfx_5809.wav \ + sfx_580.wav \ + sfx_5810.wav \ + sfx_5811.wav \ + sfx_5812.wav \ + sfx_5813.wav \ + sfx_5814.wav \ + sfx_5815.wav \ + sfx_5816.wav \ + sfx_5817.wav \ + sfx_5818.wav \ + sfx_5819.wav \ + sfx_581.wav \ + sfx_5820.wav \ + sfx_5821.wav \ + sfx_5822.wav \ + sfx_5823.wav \ + sfx_5824.wav \ + sfx_5825.wav \ + sfx_5826.wav \ + sfx_5827.wav \ + sfx_5828.wav \ + sfx_5829.wav \ + sfx_582.wav \ + sfx_5830.wav \ + sfx_5831.wav \ + sfx_5832.wav \ + sfx_5833.wav \ + sfx_5834.wav \ + sfx_5835.wav \ + sfx_5836.wav \ + sfx_5837.wav \ + sfx_5838.wav \ + sfx_5839.wav \ + sfx_583.wav \ + sfx_5840.wav \ + sfx_5841.wav \ + sfx_5842.wav \ + sfx_5843.wav \ + sfx_5844.wav \ + sfx_5845.wav \ + sfx_5846.wav \ + sfx_5847.wav \ + sfx_5848.wav \ + sfx_5849.wav \ + sfx_584.wav \ + sfx_5850.wav \ + sfx_5851.wav \ + sfx_5852.wav \ + sfx_5853.wav \ + sfx_5854.wav \ + sfx_5855.wav \ + sfx_5856.wav \ + sfx_5857.wav \ + sfx_5858.wav \ + sfx_5859.wav \ + sfx_585.wav \ + sfx_5860.wav \ + sfx_5861.wav \ + sfx_5862.wav \ + sfx_5863.wav \ + sfx_5864.wav \ + sfx_5865.wav \ + sfx_5866.wav \ + sfx_5867.wav \ + sfx_5868.wav \ + sfx_5869.wav \ + sfx_586.wav \ + sfx_5870.wav \ + sfx_5871.wav \ + sfx_5872.wav \ + sfx_5873.wav \ + sfx_5874.wav \ + sfx_5875.wav \ + sfx_5876.wav \ + sfx_5877.wav \ + sfx_5878.wav \ + sfx_5879.wav \ + sfx_587.wav \ + sfx_5880.wav \ + sfx_5881.wav \ + sfx_5882.wav \ + sfx_5883.wav \ + sfx_5884.wav \ + sfx_5885.wav \ + sfx_5886.wav \ + sfx_5887.wav \ + sfx_5888.wav \ + sfx_5889.wav \ + sfx_588.wav \ + sfx_5890.wav \ + sfx_5891.wav \ + sfx_5892.wav \ + sfx_5893.wav \ + sfx_5894.wav \ + sfx_5895.wav \ + sfx_5896.wav \ + sfx_5897.wav \ + sfx_5898.wav \ + sfx_5899.wav \ + sfx_589.wav \ + sfx_58.wav \ + sfx_5900.wav \ + sfx_5901.wav \ + sfx_5902.wav \ + sfx_5903.wav \ + sfx_5904.wav \ + sfx_5905.wav \ + sfx_5906.wav \ + sfx_5907.wav \ + sfx_5908.wav \ + sfx_5909.wav \ + sfx_590.wav \ + sfx_5910.wav \ + sfx_5911.wav \ + sfx_5912.wav \ + sfx_5913.wav \ + sfx_5914.wav \ + sfx_5915.wav \ + sfx_5916.wav \ + sfx_5917.wav \ + sfx_5918.wav \ + sfx_5919.wav \ + sfx_591.wav \ + sfx_5920.wav \ + sfx_5921.wav \ + sfx_5922.wav \ + sfx_5923.wav \ + sfx_5924.wav \ + sfx_5925.wav \ + sfx_5926.wav \ + sfx_5927.wav \ + sfx_5928.wav \ + sfx_5929.wav \ + sfx_592.wav \ + sfx_5930.wav \ + sfx_5931.wav \ + sfx_5932.wav \ + sfx_5933.wav \ + sfx_5934.wav \ + sfx_5935.wav \ + sfx_5936.wav \ + sfx_5937.wav \ + sfx_5938.wav \ + sfx_5939.wav \ + sfx_593.wav \ + sfx_5940.wav \ + sfx_5941.wav \ + sfx_5942.wav \ + sfx_5943.wav \ + sfx_5944.wav \ + sfx_5945.wav \ + sfx_5946.wav \ + sfx_5947.wav \ + sfx_5948.wav \ + sfx_5949.wav \ + sfx_594.wav \ + sfx_5950.wav \ + sfx_5951.wav \ + sfx_5952.wav \ + sfx_5953.wav \ + sfx_5954.wav \ + sfx_5955.wav \ + sfx_5956.wav \ + sfx_5957.wav \ + sfx_5958.wav \ + sfx_5959.wav \ + sfx_595.wav \ + sfx_5960.wav \ + sfx_5961.wav \ + sfx_5962.wav \ + sfx_5963.wav \ + sfx_5964.wav \ + sfx_5965.wav \ + sfx_5966.wav \ + sfx_5967.wav \ + sfx_5968.wav \ + sfx_5969.wav \ + sfx_596.wav \ + sfx_5970.wav \ + sfx_5971.wav \ + sfx_5972.wav \ + sfx_5973.wav \ + sfx_5974.wav \ + sfx_5975.wav \ + sfx_5976.wav \ + sfx_5977.wav \ + sfx_5978.wav \ + sfx_5979.wav \ + sfx_597.wav \ + sfx_5980.wav \ + sfx_5981.wav \ + sfx_5982.wav \ + sfx_5983.wav \ + sfx_5984.wav \ + sfx_5985.wav \ + sfx_5986.wav \ + sfx_5987.wav \ + sfx_5988.wav \ + sfx_5989.wav \ + sfx_598.wav \ + sfx_5990.wav \ + sfx_5991.wav \ + sfx_5992.wav \ + sfx_5993.wav \ + sfx_5994.wav \ + sfx_5995.wav \ + sfx_5996.wav \ + sfx_5997.wav \ + sfx_5998.wav \ + sfx_5999.wav \ + sfx_599.wav \ + sfx_59.wav \ + sfx_5.wav \ + sfx_6000.wav \ + sfx_6001.wav \ + sfx_6002.wav \ + sfx_6003.wav \ + sfx_6004.wav \ + sfx_6005.wav \ + sfx_6006.wav \ + sfx_6007.wav \ + sfx_6008.wav \ + sfx_6009.wav \ + sfx_600.wav \ + sfx_6010.wav \ + sfx_6011.wav \ + sfx_6012.wav \ + sfx_6013.wav \ + sfx_6014.wav \ + sfx_6015.wav \ + sfx_6016.wav \ + sfx_6017.wav \ + sfx_6018.wav \ + sfx_6019.wav \ + sfx_601.wav \ + sfx_6020.wav \ + sfx_6021.wav \ + sfx_6022.wav \ + sfx_6023.wav \ + sfx_6024.wav \ + sfx_6025.wav \ + sfx_6026.wav \ + sfx_6027.wav \ + sfx_6028.wav \ + sfx_6029.wav \ + sfx_602.wav \ + sfx_6030.wav \ + sfx_6031.wav \ + sfx_6032.wav \ + sfx_6033.wav \ + sfx_6034.wav \ + sfx_6035.wav \ + sfx_6036.wav \ + sfx_6037.wav \ + sfx_6038.wav \ + sfx_6039.wav \ + sfx_603.wav \ + sfx_6040.wav \ + sfx_6041.wav \ + sfx_6042.wav \ + sfx_6043.wav \ + sfx_6044.wav \ + sfx_6045.wav \ + sfx_6046.wav \ + sfx_6047.wav \ + sfx_6048.wav \ + sfx_6049.wav \ + sfx_604.wav \ + sfx_6050.wav \ + sfx_6051.wav \ + sfx_6052.wav \ + sfx_6053.wav \ + sfx_6054.wav \ + sfx_6055.wav \ + sfx_6056.wav \ + sfx_6057.wav \ + sfx_6058.wav \ + sfx_6059.wav \ + sfx_605.wav \ + sfx_6060.wav \ + sfx_6061.wav \ + sfx_6062.wav \ + sfx_6063.wav \ + sfx_6064.wav \ + sfx_6065.wav \ + sfx_6066.wav \ + sfx_6067.wav \ + sfx_6068.wav \ + sfx_6069.wav \ + sfx_606.wav \ + sfx_6070.wav \ + sfx_6071.wav \ + sfx_6072.wav \ + sfx_6073.wav \ + sfx_6074.wav \ + sfx_6075.wav \ + sfx_6076.wav \ + sfx_6077.wav \ + sfx_6078.wav \ + sfx_6079.wav \ + sfx_607.wav \ + sfx_6080.wav \ + sfx_6081.wav \ + sfx_6082.wav \ + sfx_6083.wav \ + sfx_6084.wav \ + sfx_6085.wav \ + sfx_6086.wav \ + sfx_6087.wav \ + sfx_6088.wav \ + sfx_6089.wav \ + sfx_608.wav \ + sfx_6090.wav \ + sfx_6091.wav \ + sfx_6092.wav \ + sfx_6093.wav \ + sfx_6094.wav \ + sfx_6095.wav \ + sfx_6096.wav \ + sfx_6097.wav \ + sfx_6098.wav \ + sfx_6099.wav \ + sfx_609.wav \ + sfx_60.wav \ + sfx_6100.wav \ + sfx_6101.wav \ + sfx_6102.wav \ + sfx_6103.wav \ + sfx_6104.wav \ + sfx_6105.wav \ + sfx_6106.wav \ + sfx_6107.wav \ + sfx_6108.wav \ + sfx_6109.wav \ + sfx_610.wav \ + sfx_6110.wav \ + sfx_6111.wav \ + sfx_6112.wav \ + sfx_6113.wav \ + sfx_6114.wav \ + sfx_6115.wav \ + sfx_6116.wav \ + sfx_6117.wav \ + sfx_6118.wav \ + sfx_6119.wav \ + sfx_611.wav \ + sfx_6120.wav \ + sfx_6121.wav \ + sfx_6122.wav \ + sfx_6123.wav \ + sfx_6124.wav \ + sfx_6125.wav \ + sfx_6126.wav \ + sfx_6127.wav \ + sfx_6128.wav \ + sfx_6129.wav \ + sfx_612.wav \ + sfx_6130.wav \ + sfx_6131.wav \ + sfx_6132.wav \ + sfx_6133.wav \ + sfx_6134.wav \ + sfx_6135.wav \ + sfx_6136.wav \ + sfx_6137.wav \ + sfx_6138.wav \ + sfx_6139.wav \ + sfx_613.wav \ + sfx_6140.wav \ + sfx_6141.wav \ + sfx_6142.wav \ + sfx_6143.wav \ + sfx_6144.wav \ + sfx_6145.wav \ + sfx_6146.wav \ + sfx_6147.wav \ + sfx_6148.wav \ + sfx_6149.wav \ + sfx_614.wav \ + sfx_6150.wav \ + sfx_6151.wav \ + sfx_6152.wav \ + sfx_6153.wav \ + sfx_6154.wav \ + sfx_6155.wav \ + sfx_6156.wav \ + sfx_6157.wav \ + sfx_6158.wav \ + sfx_6159.wav \ + sfx_615.wav \ + sfx_6160.wav \ + sfx_6161.wav \ + sfx_6162.wav \ + sfx_6163.wav \ + sfx_6164.wav \ + sfx_6165.wav \ + sfx_6166.wav \ + sfx_6167.wav \ + sfx_6168.wav \ + sfx_6169.wav \ + sfx_616.wav \ + sfx_6170.wav \ + sfx_6171.wav \ + sfx_6172.wav \ + sfx_6173.wav \ + sfx_6174.wav \ + sfx_6175.wav \ + sfx_6176.wav \ + sfx_6177.wav \ + sfx_6178.wav \ + sfx_6179.wav \ + sfx_617.wav \ + sfx_6180.wav \ + sfx_6181.wav \ + sfx_6182.wav \ + sfx_6183.wav \ + sfx_6184.wav \ + sfx_6185.wav \ + sfx_6186.wav \ + sfx_6187.wav \ + sfx_6188.wav \ + sfx_6189.wav \ + sfx_618.wav \ + sfx_6190.wav \ + sfx_6191.wav \ + sfx_6192.wav \ + sfx_6193.wav \ + sfx_6194.wav \ + sfx_6195.wav \ + sfx_6196.wav \ + sfx_6197.wav \ + sfx_6198.wav \ + sfx_6199.wav \ + sfx_619.wav \ + sfx_61.wav \ + sfx_6200.wav \ + sfx_6201.wav \ + sfx_6202.wav \ + sfx_6203.wav \ + sfx_6204.wav \ + sfx_6205.wav \ + sfx_6206.wav \ + sfx_6207.wav \ + sfx_6208.wav \ + sfx_6209.wav \ + sfx_620.wav \ + sfx_6210.wav \ + sfx_6211.wav \ + sfx_6212.wav \ + sfx_6213.wav \ + sfx_6214.wav \ + sfx_6215.wav \ + sfx_6216.wav \ + sfx_6217.wav \ + sfx_6218.wav \ + sfx_6219.wav \ + sfx_621.wav \ + sfx_6220.wav \ + sfx_6221.wav \ + sfx_6222.wav \ + sfx_6223.wav \ + sfx_6224.wav \ + sfx_6225.wav \ + sfx_6226.wav \ + sfx_6227.wav \ + sfx_6228.wav \ + sfx_6229.wav \ + sfx_622.wav \ + sfx_6230.wav \ + sfx_6231.wav \ + sfx_6232.wav \ + sfx_6233.wav \ + sfx_6234.wav \ + sfx_6235.wav \ + sfx_6236.wav \ + sfx_6237.wav \ + sfx_6238.wav \ + sfx_6239.wav \ + sfx_623.wav \ + sfx_6240.wav \ + sfx_6241.wav \ + sfx_6242.wav \ + sfx_6243.wav \ + sfx_6244.wav \ + sfx_6245.wav \ + sfx_6246.wav \ + sfx_6247.wav \ + sfx_6248.wav \ + sfx_6249.wav \ + sfx_624.wav \ + sfx_6250.wav \ + sfx_6251.wav \ + sfx_6252.wav \ + sfx_6253.wav \ + sfx_6254.wav \ + sfx_6255.wav \ + sfx_6256.wav \ + sfx_6257.wav \ + sfx_6258.wav \ + sfx_6259.wav \ + sfx_625.wav \ + sfx_6260.wav \ + sfx_6261.wav \ + sfx_6262.wav \ + sfx_6263.wav \ + sfx_6264.wav \ + sfx_6265.wav \ + sfx_6266.wav \ + sfx_6267.wav \ + sfx_6268.wav \ + sfx_6269.wav \ + sfx_626.wav \ + sfx_6270.wav \ + sfx_6271.wav \ + sfx_6272.wav \ + sfx_6273.wav \ + sfx_6274.wav \ + sfx_6275.wav \ + sfx_6276.wav \ + sfx_6277.wav \ + sfx_6278.wav \ + sfx_6279.wav \ + sfx_627.wav \ + sfx_6280.wav \ + sfx_6281.wav \ + sfx_6282.wav \ + sfx_6283.wav \ + sfx_6284.wav \ + sfx_6285.wav \ + sfx_6286.wav \ + sfx_6287.wav \ + sfx_6288.wav \ + sfx_6289.wav \ + sfx_628.wav \ + sfx_6290.wav \ + sfx_6291.wav \ + sfx_6292.wav \ + sfx_6293.wav \ + sfx_6294.wav \ + sfx_6295.wav \ + sfx_6296.wav \ + sfx_6297.wav \ + sfx_6298.wav \ + sfx_6299.wav \ + sfx_629.wav \ + sfx_62.wav \ + sfx_6300.wav \ + sfx_6301.wav \ + sfx_6302.wav \ + sfx_6303.wav \ + sfx_6304.wav \ + sfx_6305.wav \ + sfx_6306.wav \ + sfx_6307.wav \ + sfx_6308.wav \ + sfx_6309.wav \ + sfx_630.wav \ + sfx_6310.wav \ + sfx_6311.wav \ + sfx_6312.wav \ + sfx_6313.wav \ + sfx_6314.wav \ + sfx_6315.wav \ + sfx_6316.wav \ + sfx_6317.wav \ + sfx_6318.wav \ + sfx_6319.wav \ + sfx_631.wav \ + sfx_6320.wav \ + sfx_6321.wav \ + sfx_6322.wav \ + sfx_6323.wav \ + sfx_6324.wav \ + sfx_6325.wav \ + sfx_6326.wav \ + sfx_6327.wav \ + sfx_6328.wav \ + sfx_6329.wav \ + sfx_632.wav \ + sfx_6330.wav \ + sfx_6331.wav \ + sfx_6332.wav \ + sfx_6333.wav \ + sfx_6334.wav \ + sfx_6335.wav \ + sfx_6336.wav \ + sfx_6337.wav \ + sfx_6338.wav \ + sfx_6339.wav \ + sfx_633.wav \ + sfx_6340.wav \ + sfx_6341.wav \ + sfx_6342.wav \ + sfx_6343.wav \ + sfx_6344.wav \ + sfx_6345.wav \ + sfx_6346.wav \ + sfx_6347.wav \ + sfx_6348.wav \ + sfx_6349.wav \ + sfx_634.wav \ + sfx_6350.wav \ + sfx_6351.wav \ + sfx_6352.wav \ + sfx_6353.wav \ + sfx_6354.wav \ + sfx_6355.wav \ + sfx_6356.wav \ + sfx_6357.wav \ + sfx_6358.wav \ + sfx_6359.wav \ + sfx_635.wav \ + sfx_6360.wav \ + sfx_6361.wav \ + sfx_6362.wav \ + sfx_6363.wav \ + sfx_6364.wav \ + sfx_6365.wav \ + sfx_6366.wav \ + sfx_6367.wav \ + sfx_6368.wav \ + sfx_6369.wav \ + sfx_636.wav \ + sfx_6370.wav \ + sfx_6371.wav \ + sfx_6372.wav \ + sfx_6373.wav \ + sfx_6374.wav \ + sfx_6375.wav \ + sfx_6376.wav \ + sfx_6377.wav \ + sfx_6378.wav \ + sfx_6379.wav \ + sfx_637.wav \ + sfx_6380.wav \ + sfx_6381.wav \ + sfx_6382.wav \ + sfx_6383.wav \ + sfx_6384.wav \ + sfx_6385.wav \ + sfx_6386.wav \ + sfx_6387.wav \ + sfx_6388.wav \ + sfx_6389.wav \ + sfx_638.wav \ + sfx_6390.wav \ + sfx_6391.wav \ + sfx_6392.wav \ + sfx_6393.wav \ + sfx_6394.wav \ + sfx_6395.wav \ + sfx_6396.wav \ + sfx_6397.wav \ + sfx_6398.wav \ + sfx_6399.wav \ + sfx_639.wav \ + sfx_63.wav \ + sfx_6400.wav \ + sfx_6401.wav \ + sfx_6402.wav \ + sfx_6403.wav \ + sfx_6404.wav \ + sfx_6405.wav \ + sfx_6406.wav \ + sfx_6407.wav \ + sfx_6408.wav \ + sfx_6409.wav \ + sfx_640.wav \ + sfx_6410.wav \ + sfx_6411.wav \ + sfx_6412.wav \ + sfx_6413.wav \ + sfx_6414.wav \ + sfx_6415.wav \ + sfx_6416.wav \ + sfx_6417.wav \ + sfx_6418.wav \ + sfx_6419.wav \ + sfx_641.wav \ + sfx_6420.wav \ + sfx_6421.wav \ + sfx_6422.wav \ + sfx_6423.wav \ + sfx_6424.wav \ + sfx_6425.wav \ + sfx_6426.wav \ + sfx_6427.wav \ + sfx_6428.wav \ + sfx_6429.wav \ + sfx_642.wav \ + sfx_6430.wav \ + sfx_6431.wav \ + sfx_6432.wav \ + sfx_6433.wav \ + sfx_6434.wav \ + sfx_6435.wav \ + sfx_6436.wav \ + sfx_6437.wav \ + sfx_6438.wav \ + sfx_6439.wav \ + sfx_643.wav \ + sfx_6440.wav \ + sfx_6441.wav \ + sfx_6442.wav \ + sfx_6443.wav \ + sfx_6444.wav \ + sfx_6445.wav \ + sfx_6446.wav \ + sfx_6447.wav \ + sfx_6448.wav \ + sfx_6449.wav \ + sfx_644.wav \ + sfx_6450.wav \ + sfx_6451.wav \ + sfx_6452.wav \ + sfx_6453.wav \ + sfx_6454.wav \ + sfx_6455.wav \ + sfx_6456.wav \ + sfx_6457.wav \ + sfx_6458.wav \ + sfx_6459.wav \ + sfx_645.wav \ + sfx_6460.wav \ + sfx_6461.wav \ + sfx_6462.wav \ + sfx_6463.wav \ + sfx_6464.wav \ + sfx_6465.wav \ + sfx_6466.wav \ + sfx_6467.wav \ + sfx_6468.wav \ + sfx_6469.wav \ + sfx_646.wav \ + sfx_6470.wav \ + sfx_6471.wav \ + sfx_6472.wav \ + sfx_6473.wav \ + sfx_6474.wav \ + sfx_6475.wav \ + sfx_6476.wav \ + sfx_6477.wav \ + sfx_6478.wav \ + sfx_6479.wav \ + sfx_647.wav \ + sfx_6480.wav \ + sfx_6481.wav \ + sfx_6482.wav \ + sfx_6483.wav \ + sfx_6484.wav \ + sfx_6485.wav \ + sfx_6486.wav \ + sfx_6487.wav \ + sfx_6488.wav \ + sfx_6489.wav \ + sfx_648.wav \ + sfx_6490.wav \ + sfx_6491.wav \ + sfx_6492.wav \ + sfx_6493.wav \ + sfx_6494.wav \ + sfx_6495.wav \ + sfx_6496.wav \ + sfx_6497.wav \ + sfx_6498.wav \ + sfx_6499.wav \ + sfx_649.wav \ + sfx_64.wav \ + sfx_6500.wav \ + sfx_6501.wav \ + sfx_6502.wav \ + sfx_6503.wav \ + sfx_6504.wav \ + sfx_6505.wav \ + sfx_6506.wav \ + sfx_6507.wav \ + sfx_6508.wav \ + sfx_6509.wav \ + sfx_650.wav \ + sfx_6510.wav \ + sfx_6511.wav \ + sfx_6512.wav \ + sfx_6513.wav \ + sfx_6514.wav \ + sfx_6515.wav \ + sfx_6516.wav \ + sfx_6517.wav \ + sfx_6518.wav \ + sfx_6519.wav \ + sfx_651.wav \ + sfx_6520.wav \ + sfx_6521.wav \ + sfx_6522.wav \ + sfx_6523.wav \ + sfx_6524.wav \ + sfx_6525.wav \ + sfx_6526.wav \ + sfx_6527.wav \ + sfx_6528.wav \ + sfx_6529.wav \ + sfx_652.wav \ + sfx_6530.wav \ + sfx_6531.wav \ + sfx_6532.wav \ + sfx_6533.wav \ + sfx_6534.wav \ + sfx_6535.wav \ + sfx_6536.wav \ + sfx_6537.wav \ + sfx_6538.wav \ + sfx_6539.wav \ + sfx_653.wav \ + sfx_6540.wav \ + sfx_6541.wav \ + sfx_6542.wav \ + sfx_6543.wav \ + sfx_6544.wav \ + sfx_6545.wav \ + sfx_6546.wav \ + sfx_6547.wav \ + sfx_6548.wav \ + sfx_6549.wav \ + sfx_654.wav \ + sfx_6550.wav \ + sfx_6551.wav \ + sfx_6552.wav \ + sfx_6553.wav \ + sfx_6554.wav \ + sfx_6555.wav \ + sfx_6556.wav \ + sfx_6557.wav \ + sfx_6558.wav \ + sfx_6559.wav \ + sfx_655.wav \ + sfx_6560.wav \ + sfx_6561.wav \ + sfx_6562.wav \ + sfx_6563.wav \ + sfx_6564.wav \ + sfx_6565.wav \ + sfx_6566.wav \ + sfx_6567.wav \ + sfx_6568.wav \ + sfx_6569.wav \ + sfx_656.wav \ + sfx_6570.wav \ + sfx_6571.wav \ + sfx_6572.wav \ + sfx_6573.wav \ + sfx_6574.wav \ + sfx_6575.wav \ + sfx_6576.wav \ + sfx_6577.wav \ + sfx_6578.wav \ + sfx_6579.wav \ + sfx_657.wav \ + sfx_6580.wav \ + sfx_6581.wav \ + sfx_6582.wav \ + sfx_6583.wav \ + sfx_6584.wav \ + sfx_6585.wav \ + sfx_6586.wav \ + sfx_6587.wav \ + sfx_6588.wav \ + sfx_6589.wav \ + sfx_658.wav \ + sfx_6590.wav \ + sfx_6591.wav \ + sfx_6592.wav \ + sfx_6593.wav \ + sfx_6594.wav \ + sfx_6595.wav \ + sfx_6596.wav \ + sfx_6597.wav \ + sfx_6598.wav \ + sfx_6599.wav \ + sfx_659.wav \ + sfx_65.wav \ + sfx_6600.wav \ + sfx_6601.wav \ + sfx_6602.wav \ + sfx_6603.wav \ + sfx_6604.wav \ + sfx_6605.wav \ + sfx_6606.wav \ + sfx_6607.wav \ + sfx_6608.wav \ + sfx_6609.wav \ + sfx_660.wav \ + sfx_6610.wav \ + sfx_6611.wav \ + sfx_6612.wav \ + sfx_6613.wav \ + sfx_6614.wav \ + sfx_6615.wav \ + sfx_6616.wav \ + sfx_6617.wav \ + sfx_6618.wav \ + sfx_6619.wav \ + sfx_661.wav \ + sfx_6620.wav \ + sfx_6621.wav \ + sfx_6622.wav \ + sfx_6623.wav \ + sfx_6624.wav \ + sfx_6625.wav \ + sfx_6626.wav \ + sfx_6627.wav \ + sfx_6628.wav \ + sfx_6629.wav \ + sfx_662.wav \ + sfx_6630.wav \ + sfx_6631.wav \ + sfx_6632.wav \ + sfx_6633.wav \ + sfx_6634.wav \ + sfx_6635.wav \ + sfx_6636.wav \ + sfx_6637.wav \ + sfx_6638.wav \ + sfx_6639.wav \ + sfx_663.wav \ + sfx_6640.wav \ + sfx_6641.wav \ + sfx_6642.wav \ + sfx_6643.wav \ + sfx_6644.wav \ + sfx_6645.wav \ + sfx_6646.wav \ + sfx_6647.wav \ + sfx_6648.wav \ + sfx_6649.wav \ + sfx_664.wav \ + sfx_6650.wav \ + sfx_6651.wav \ + sfx_6652.wav \ + sfx_6653.wav \ + sfx_6654.wav \ + sfx_6655.wav \ + sfx_6656.wav \ + sfx_6657.wav \ + sfx_6658.wav \ + sfx_6659.wav \ + sfx_665.wav \ + sfx_6660.wav \ + sfx_6661.wav \ + sfx_6662.wav \ + sfx_6663.wav \ + sfx_6664.wav \ + sfx_6665.wav \ + sfx_6666.wav \ + sfx_6667.wav \ + sfx_6668.wav \ + sfx_6669.wav \ + sfx_666.wav \ + sfx_6670.wav \ + sfx_6671.wav \ + sfx_6672.wav \ + sfx_6673.wav \ + sfx_6674.wav \ + sfx_6675.wav \ + sfx_6676.wav \ + sfx_6677.wav \ + sfx_6678.wav \ + sfx_6679.wav \ + sfx_667.wav \ + sfx_6680.wav \ + sfx_6681.wav \ + sfx_6682.wav \ + sfx_6683.wav \ + sfx_6684.wav \ + sfx_6685.wav \ + sfx_6686.wav \ + sfx_6687.wav \ + sfx_6688.wav \ + sfx_6689.wav \ + sfx_668.wav \ + sfx_6690.wav \ + sfx_6691.wav \ + sfx_6692.wav \ + sfx_6693.wav \ + sfx_6694.wav \ + sfx_6695.wav \ + sfx_6696.wav \ + sfx_6697.wav \ + sfx_6698.wav \ + sfx_6699.wav \ + sfx_669.wav \ + sfx_66.wav \ + sfx_6700.wav \ + sfx_6701.wav \ + sfx_6702.wav \ + sfx_6703.wav \ + sfx_6704.wav \ + sfx_6705.wav \ + sfx_6706.wav \ + sfx_6707.wav \ + sfx_6708.wav \ + sfx_6709.wav \ + sfx_670.wav \ + sfx_6710.wav \ + sfx_6711.wav \ + sfx_6712.wav \ + sfx_6713.wav \ + sfx_6714.wav \ + sfx_6715.wav \ + sfx_6716.wav \ + sfx_6717.wav \ + sfx_6718.wav \ + sfx_6719.wav \ + sfx_671.wav \ + sfx_6720.wav \ + sfx_6721.wav \ + sfx_6722.wav \ + sfx_6723.wav \ + sfx_6724.wav \ + sfx_6725.wav \ + sfx_6726.wav \ + sfx_6727.wav \ + sfx_6728.wav \ + sfx_6729.wav \ + sfx_672.wav \ + sfx_6730.wav \ + sfx_6731.wav \ + sfx_6732.wav \ + sfx_6733.wav \ + sfx_6734.wav \ + sfx_6735.wav \ + sfx_6736.wav \ + sfx_6737.wav \ + sfx_6738.wav \ + sfx_6739.wav \ + sfx_673.wav \ + sfx_6740.wav \ + sfx_6741.wav \ + sfx_6742.wav \ + sfx_6743.wav \ + sfx_6744.wav \ + sfx_6745.wav \ + sfx_6746.wav \ + sfx_6747.wav \ + sfx_6748.wav \ + sfx_6749.wav \ + sfx_674.wav \ + sfx_6750.wav \ + sfx_6751.wav \ + sfx_6752.wav \ + sfx_6753.wav \ + sfx_6754.wav \ + sfx_6755.wav \ + sfx_6756.wav \ + sfx_6757.wav \ + sfx_6758.wav \ + sfx_6759.wav \ + sfx_675.wav \ + sfx_6760.wav \ + sfx_6761.wav \ + sfx_6762.wav \ + sfx_6763.wav \ + sfx_6764.wav \ + sfx_6765.wav \ + sfx_6766.wav \ + sfx_6767.wav \ + sfx_6768.wav \ + sfx_6769.wav \ + sfx_676.wav \ + sfx_6770.wav \ + sfx_6771.wav \ + sfx_6772.wav \ + sfx_6773.wav \ + sfx_6774.wav \ + sfx_6775.wav \ + sfx_6776.wav \ + sfx_6777.wav \ + sfx_6778.wav \ + sfx_6779.wav \ + sfx_677.wav \ + sfx_6780.wav \ + sfx_6781.wav \ + sfx_6782.wav \ + sfx_6783.wav \ + sfx_6784.wav \ + sfx_6785.wav \ + sfx_6786.wav \ + sfx_6787.wav \ + sfx_6788.wav \ + sfx_6789.wav \ + sfx_678.wav \ + sfx_6790.wav \ + sfx_6791.wav \ + sfx_6792.wav \ + sfx_6793.wav \ + sfx_6794.wav \ + sfx_6795.wav \ + sfx_6796.wav \ + sfx_6797.wav \ + sfx_6798.wav \ + sfx_6799.wav \ + sfx_679.wav \ + sfx_67.wav \ + sfx_6800.wav \ + sfx_6801.wav \ + sfx_6802.wav \ + sfx_6803.wav \ + sfx_6804.wav \ + sfx_6805.wav \ + sfx_6806.wav \ + sfx_6807.wav \ + sfx_6808.wav \ + sfx_6809.wav \ + sfx_680.wav \ + sfx_6810.wav \ + sfx_6811.wav \ + sfx_6812.wav \ + sfx_6813.wav \ + sfx_6814.wav \ + sfx_6815.wav \ + sfx_6816.wav \ + sfx_6817.wav \ + sfx_6818.wav \ + sfx_6819.wav \ + sfx_681.wav \ + sfx_6820.wav \ + sfx_6821.wav \ + sfx_6822.wav \ + sfx_6823.wav \ + sfx_6824.wav \ + sfx_6825.wav \ + sfx_6826.wav \ + sfx_6827.wav \ + sfx_6828.wav \ + sfx_6829.wav \ + sfx_682.wav \ + sfx_6830.wav \ + sfx_6831.wav \ + sfx_6832.wav \ + sfx_6833.wav \ + sfx_6834.wav \ + sfx_6835.wav \ + sfx_6836.wav \ + sfx_6837.wav \ + sfx_6838.wav \ + sfx_6839.wav \ + sfx_683.wav \ + sfx_6840.wav \ + sfx_6841.wav \ + sfx_6842.wav \ + sfx_6843.wav \ + sfx_6844.wav \ + sfx_6845.wav \ + sfx_6846.wav \ + sfx_6847.wav \ + sfx_6848.wav \ + sfx_6849.wav \ + sfx_684.wav \ + sfx_6850.wav \ + sfx_6851.wav \ + sfx_6852.wav \ + sfx_6853.wav \ + sfx_6854.wav \ + sfx_6855.wav \ + sfx_6856.wav \ + sfx_6857.wav \ + sfx_6858.wav \ + sfx_6859.wav \ + sfx_685.wav \ + sfx_6860.wav \ + sfx_6861.wav \ + sfx_6862.wav \ + sfx_6863.wav \ + sfx_6864.wav \ + sfx_6865.wav \ + sfx_6866.wav \ + sfx_6867.wav \ + sfx_6868.wav \ + sfx_6869.wav \ + sfx_686.wav \ + sfx_6870.wav \ + sfx_6871.wav \ + sfx_6872.wav \ + sfx_6873.wav \ + sfx_6874.wav \ + sfx_6875.wav \ + sfx_6876.wav \ + sfx_6877.wav \ + sfx_6878.wav \ + sfx_6879.wav \ + sfx_687.wav \ + sfx_6880.wav \ + sfx_6881.wav \ + sfx_6882.wav \ + sfx_6883.wav \ + sfx_6884.wav \ + sfx_6885.wav \ + sfx_6886.wav \ + sfx_6887.wav \ + sfx_6888.wav \ + sfx_6889.wav \ + sfx_688.wav \ + sfx_6890.wav \ + sfx_6891.wav \ + sfx_6892.wav \ + sfx_6893.wav \ + sfx_6894.wav \ + sfx_6895.wav \ + sfx_6896.wav \ + sfx_6897.wav \ + sfx_6898.wav \ + sfx_6899.wav \ + sfx_689.wav \ + sfx_68.wav \ + sfx_6900.wav \ + sfx_6901.wav \ + sfx_6902.wav \ + sfx_6903.wav \ + sfx_6904.wav \ + sfx_6905.wav \ + sfx_6906.wav \ + sfx_6907.wav \ + sfx_6908.wav \ + sfx_6909.wav \ + sfx_690.wav \ + sfx_6910.wav \ + sfx_6911.wav \ + sfx_6912.wav \ + sfx_6913.wav \ + sfx_6914.wav \ + sfx_6915.wav \ + sfx_6916.wav \ + sfx_6917.wav \ + sfx_6918.wav \ + sfx_6919.wav \ + sfx_691.wav \ + sfx_6920.wav \ + sfx_6921.wav \ + sfx_6922.wav \ + sfx_6923.wav \ + sfx_6924.wav \ + sfx_6925.wav \ + sfx_6926.wav \ + sfx_6927.wav \ + sfx_6928.wav \ + sfx_6929.wav \ + sfx_692.wav \ + sfx_6930.wav \ + sfx_6931.wav \ + sfx_6932.wav \ + sfx_6933.wav \ + sfx_6934.wav \ + sfx_6935.wav \ + sfx_6936.wav \ + sfx_6937.wav \ + sfx_6938.wav \ + sfx_6939.wav \ + sfx_693.wav \ + sfx_6940.wav \ + sfx_6941.wav \ + sfx_6942.wav \ + sfx_6943.wav \ + sfx_6944.wav \ + sfx_6945.wav \ + sfx_6946.wav \ + sfx_6947.wav \ + sfx_6948.wav \ + sfx_6949.wav \ + sfx_694.wav \ + sfx_6950.wav \ + sfx_6951.wav \ + sfx_6952.wav \ + sfx_6953.wav \ + sfx_6954.wav \ + sfx_6955.wav \ + sfx_6956.wav \ + sfx_6957.wav \ + sfx_6958.wav \ + sfx_6959.wav \ + sfx_695.wav \ + sfx_6960.wav \ + sfx_6961.wav \ + sfx_6962.wav \ + sfx_6963.wav \ + sfx_6964.wav \ + sfx_6965.wav \ + sfx_6966.wav \ + sfx_6967.wav \ + sfx_6968.wav \ + sfx_6969.wav \ + sfx_696.wav \ + sfx_6970.wav \ + sfx_6971.wav \ + sfx_6972.wav \ + sfx_6973.wav \ + sfx_6974.wav \ + sfx_6975.wav \ + sfx_6976.wav \ + sfx_6977.wav \ + sfx_6978.wav \ + sfx_6979.wav \ + sfx_697.wav \ + sfx_6980.wav \ + sfx_6981.wav \ + sfx_6982.wav \ + sfx_6983.wav \ + sfx_6984.wav \ + sfx_6985.wav \ + sfx_6986.wav \ + sfx_6987.wav \ + sfx_6988.wav \ + sfx_6989.wav \ + sfx_698.wav \ + sfx_6990.wav \ + sfx_6991.wav \ + sfx_6992.wav \ + sfx_6993.wav \ + sfx_6994.wav \ + sfx_6995.wav \ + sfx_6996.wav \ + sfx_6997.wav \ + sfx_6998.wav \ + sfx_6999.wav \ + sfx_699.wav \ + sfx_69.wav \ + sfx_6.wav \ + sfx_7000.wav \ + sfx_7001.wav \ + sfx_7002.wav \ + sfx_7003.wav \ + sfx_7004.wav \ + sfx_7005.wav \ + sfx_7006.wav \ + sfx_7007.wav \ + sfx_7008.wav \ + sfx_7009.wav \ + sfx_700.wav \ + sfx_7010.wav \ + sfx_7011.wav \ + sfx_7012.wav \ + sfx_7013.wav \ + sfx_7014.wav \ + sfx_7015.wav \ + sfx_7016.wav \ + sfx_7017.wav \ + sfx_7018.wav \ + sfx_7019.wav \ + sfx_701.wav \ + sfx_7020.wav \ + sfx_7021.wav \ + sfx_7022.wav \ + sfx_7023.wav \ + sfx_7024.wav \ + sfx_7025.wav \ + sfx_7026.wav \ + sfx_7027.wav \ + sfx_7028.wav \ + sfx_7029.wav \ + sfx_702.wav \ + sfx_7030.wav \ + sfx_7031.wav \ + sfx_7032.wav \ + sfx_7033.wav \ + sfx_7034.wav \ + sfx_7035.wav \ + sfx_7036.wav \ + sfx_7037.wav \ + sfx_7038.wav \ + sfx_7039.wav \ + sfx_703.wav \ + sfx_7040.wav \ + sfx_7041.wav \ + sfx_7042.wav \ + sfx_7043.wav \ + sfx_7044.wav \ + sfx_7045.wav \ + sfx_7046.wav \ + sfx_7047.wav \ + sfx_7048.wav \ + sfx_7049.wav \ + sfx_704.wav \ + sfx_7050.wav \ + sfx_7051.wav \ + sfx_7052.wav \ + sfx_7053.wav \ + sfx_7054.wav \ + sfx_7055.wav \ + sfx_7056.wav \ + sfx_7057.wav \ + sfx_7058.wav \ + sfx_7059.wav \ + sfx_705.wav \ + sfx_7060.wav \ + sfx_7061.wav \ + sfx_7062.wav \ + sfx_7063.wav \ + sfx_7064.wav \ + sfx_7065.wav \ + sfx_7066.wav \ + sfx_7067.wav \ + sfx_7068.wav \ + sfx_7069.wav \ + sfx_706.wav \ + sfx_7070.wav \ + sfx_7071.wav \ + sfx_7072.wav \ + sfx_7073.wav \ + sfx_7074.wav \ + sfx_7075.wav \ + sfx_7076.wav \ + sfx_7077.wav \ + sfx_7078.wav \ + sfx_7079.wav \ + sfx_707.wav \ + sfx_7080.wav \ + sfx_7081.wav \ + sfx_7082.wav \ + sfx_7083.wav \ + sfx_7084.wav \ + sfx_7085.wav \ + sfx_7086.wav \ + sfx_7087.wav \ + sfx_7088.wav \ + sfx_7089.wav \ + sfx_708.wav \ + sfx_7090.wav \ + sfx_7091.wav \ + sfx_7092.wav \ + sfx_7093.wav \ + sfx_7094.wav \ + sfx_7095.wav \ + sfx_7096.wav \ + sfx_7097.wav \ + sfx_7098.wav \ + sfx_7099.wav \ + sfx_709.wav \ + sfx_70.wav \ + sfx_7100.wav \ + sfx_7101.wav \ + sfx_7102.wav \ + sfx_7103.wav \ + sfx_7104.wav \ + sfx_7105.wav \ + sfx_7106.wav \ + sfx_7107.wav \ + sfx_7108.wav \ + sfx_7109.wav \ + sfx_710.wav \ + sfx_7110.wav \ + sfx_7111.wav \ + sfx_7112.wav \ + sfx_7113.wav \ + sfx_7114.wav \ + sfx_7115.wav \ + sfx_7116.wav \ + sfx_7117.wav \ + sfx_7118.wav \ + sfx_7119.wav \ + sfx_711.wav \ + sfx_7120.wav \ + sfx_7121.wav \ + sfx_7122.wav \ + sfx_7123.wav \ + sfx_7124.wav \ + sfx_7125.wav \ + sfx_7126.wav \ + sfx_7127.wav \ + sfx_7128.wav \ + sfx_7129.wav \ + sfx_712.wav \ + sfx_7130.wav \ + sfx_7131.wav \ + sfx_7132.wav \ + sfx_7133.wav \ + sfx_7134.wav \ + sfx_7135.wav \ + sfx_7136.wav \ + sfx_7137.wav \ + sfx_7138.wav \ + sfx_7139.wav \ + sfx_713.wav \ + sfx_7140.wav \ + sfx_7141.wav \ + sfx_7142.wav \ + sfx_7143.wav \ + sfx_7144.wav \ + sfx_7145.wav \ + sfx_7146.wav \ + sfx_7147.wav \ + sfx_7148.wav \ + sfx_7149.wav \ + sfx_714.wav \ + sfx_7150.wav \ + sfx_7151.wav \ + sfx_7152.wav \ + sfx_7153.wav \ + sfx_7154.wav \ + sfx_7155.wav \ + sfx_7156.wav \ + sfx_7157.wav \ + sfx_7158.wav \ + sfx_7159.wav \ + sfx_715.wav \ + sfx_7160.wav \ + sfx_7161.wav \ + sfx_7162.wav \ + sfx_7163.wav \ + sfx_7164.wav \ + sfx_7165.wav \ + sfx_7166.wav \ + sfx_7167.wav \ + sfx_7168.wav \ + sfx_7169.wav \ + sfx_716.wav \ + sfx_7170.wav \ + sfx_7171.wav \ + sfx_7172.wav \ + sfx_7173.wav \ + sfx_7174.wav \ + sfx_7175.wav \ + sfx_7176.wav \ + sfx_7177.wav \ + sfx_7178.wav \ + sfx_7179.wav \ + sfx_717.wav \ + sfx_7180.wav \ + sfx_7181.wav \ + sfx_7182.wav \ + sfx_7183.wav \ + sfx_7184.wav \ + sfx_7185.wav \ + sfx_7186.wav \ + sfx_7187.wav \ + sfx_7188.wav \ + sfx_7189.wav \ + sfx_718.wav \ + sfx_7190.wav \ + sfx_7191.wav \ + sfx_7192.wav \ + sfx_7193.wav \ + sfx_7194.wav \ + sfx_7195.wav \ + sfx_7196.wav \ + sfx_7197.wav \ + sfx_7198.wav \ + sfx_7199.wav \ + sfx_719.wav \ + sfx_71.wav \ + sfx_7200.wav \ + sfx_7201.wav \ + sfx_7202.wav \ + sfx_7203.wav \ + sfx_7204.wav \ + sfx_7205.wav \ + sfx_7206.wav \ + sfx_7207.wav \ + sfx_7208.wav \ + sfx_7209.wav \ + sfx_720.wav \ + sfx_7210.wav \ + sfx_7211.wav \ + sfx_7212.wav \ + sfx_7213.wav \ + sfx_7214.wav \ + sfx_7215.wav \ + sfx_7216.wav \ + sfx_7217.wav \ + sfx_7218.wav \ + sfx_7219.wav \ + sfx_721.wav \ + sfx_7220.wav \ + sfx_7221.wav \ + sfx_7222.wav \ + sfx_7223.wav \ + sfx_7224.wav \ + sfx_7225.wav \ + sfx_7226.wav \ + sfx_7227.wav \ + sfx_7228.wav \ + sfx_7229.wav \ + sfx_722.wav \ + sfx_7230.wav \ + sfx_7231.wav \ + sfx_7232.wav \ + sfx_7233.wav \ + sfx_7234.wav \ + sfx_7235.wav \ + sfx_7236.wav \ + sfx_7237.wav \ + sfx_7238.wav \ + sfx_7239.wav \ + sfx_723.wav \ + sfx_7240.wav \ + sfx_7241.wav \ + sfx_7242.wav \ + sfx_7243.wav \ + sfx_7244.wav \ + sfx_7245.wav \ + sfx_7246.wav \ + sfx_7247.wav \ + sfx_7248.wav \ + sfx_7249.wav \ + sfx_724.wav \ + sfx_7250.wav \ + sfx_7251.wav \ + sfx_7252.wav \ + sfx_7253.wav \ + sfx_7254.wav \ + sfx_7255.wav \ + sfx_7256.wav \ + sfx_7257.wav \ + sfx_7258.wav \ + sfx_7259.wav \ + sfx_725.wav \ + sfx_7260.wav \ + sfx_7261.wav \ + sfx_7262.wav \ + sfx_7263.wav \ + sfx_7264.wav \ + sfx_7265.wav \ + sfx_7266.wav \ + sfx_7267.wav \ + sfx_7268.wav \ + sfx_7269.wav \ + sfx_726.wav \ + sfx_7270.wav \ + sfx_7271.wav \ + sfx_7272.wav \ + sfx_7273.wav \ + sfx_7274.wav \ + sfx_7275.wav \ + sfx_7276.wav \ + sfx_7277.wav \ + sfx_7278.wav \ + sfx_7279.wav \ + sfx_727.wav \ + sfx_7280.wav \ + sfx_7281.wav \ + sfx_7282.wav \ + sfx_7283.wav \ + sfx_7284.wav \ + sfx_7285.wav \ + sfx_7286.wav \ + sfx_7287.wav \ + sfx_7288.wav \ + sfx_7289.wav \ + sfx_728.wav \ + sfx_7290.wav \ + sfx_7291.wav \ + sfx_7292.wav \ + sfx_7293.wav \ + sfx_7294.wav \ + sfx_7295.wav \ + sfx_7296.wav \ + sfx_7297.wav \ + sfx_7298.wav \ + sfx_7299.wav \ + sfx_729.wav \ + sfx_72.wav \ + sfx_7300.wav \ + sfx_7301.wav \ + sfx_7302.wav \ + sfx_7303.wav \ + sfx_7304.wav \ + sfx_7305.wav \ + sfx_7306.wav \ + sfx_7307.wav \ + sfx_7308.wav \ + sfx_7309.wav \ + sfx_730.wav \ + sfx_7310.wav \ + sfx_7311.wav \ + sfx_7312.wav \ + sfx_7313.wav \ + sfx_7314.wav \ + sfx_7315.wav \ + sfx_7316.wav \ + sfx_7317.wav \ + sfx_7318.wav \ + sfx_7319.wav \ + sfx_731.wav \ + sfx_7320.wav \ + sfx_7321.wav \ + sfx_7322.wav \ + sfx_7323.wav \ + sfx_7324.wav \ + sfx_7325.wav \ + sfx_7326.wav \ + sfx_7327.wav \ + sfx_7328.wav \ + sfx_7329.wav \ + sfx_732.wav \ + sfx_7330.wav \ + sfx_7331.wav \ + sfx_7332.wav \ + sfx_7333.wav \ + sfx_7334.wav \ + sfx_7335.wav \ + sfx_7336.wav \ + sfx_7337.wav \ + sfx_7338.wav \ + sfx_7339.wav \ + sfx_733.wav \ + sfx_7340.wav \ + sfx_7341.wav \ + sfx_7342.wav \ + sfx_7343.wav \ + sfx_7344.wav \ + sfx_7345.wav \ + sfx_7346.wav \ + sfx_7347.wav \ + sfx_7348.wav \ + sfx_7349.wav \ + sfx_734.wav \ + sfx_7350.wav \ + sfx_7351.wav \ + sfx_7352.wav \ + sfx_7353.wav \ + sfx_7354.wav \ + sfx_7355.wav \ + sfx_7356.wav \ + sfx_7357.wav \ + sfx_7358.wav \ + sfx_7359.wav \ + sfx_735.wav \ + sfx_7360.wav \ + sfx_7361.wav \ + sfx_7362.wav \ + sfx_7363.wav \ + sfx_7364.wav \ + sfx_7365.wav \ + sfx_7366.wav \ + sfx_7367.wav \ + sfx_7368.wav \ + sfx_7369.wav \ + sfx_736.wav \ + sfx_7370.wav \ + sfx_7371.wav \ + sfx_7372.wav \ + sfx_7373.wav \ + sfx_7374.wav \ + sfx_7375.wav \ + sfx_7376.wav \ + sfx_7377.wav \ + sfx_7378.wav \ + sfx_7379.wav \ + sfx_737.wav \ + sfx_7380.wav \ + sfx_7381.wav \ + sfx_7382.wav \ + sfx_7383.wav \ + sfx_7384.wav \ + sfx_7385.wav \ + sfx_7386.wav \ + sfx_7387.wav \ + sfx_7388.wav \ + sfx_7389.wav \ + sfx_738.wav \ + sfx_7390.wav \ + sfx_7391.wav \ + sfx_7392.wav \ + sfx_7393.wav \ + sfx_7394.wav \ + sfx_7395.wav \ + sfx_7396.wav \ + sfx_7397.wav \ + sfx_7398.wav \ + sfx_7399.wav \ + sfx_739.wav \ + sfx_73.wav \ + sfx_7400.wav \ + sfx_7401.wav \ + sfx_7402.wav \ + sfx_7403.wav \ + sfx_7404.wav \ + sfx_7405.wav \ + sfx_7406.wav \ + sfx_7407.wav \ + sfx_7408.wav \ + sfx_7409.wav \ + sfx_740.wav \ + sfx_7410.wav \ + sfx_7411.wav \ + sfx_7412.wav \ + sfx_7413.wav \ + sfx_7414.wav \ + sfx_7415.wav \ + sfx_7416.wav \ + sfx_7417.wav \ + sfx_7418.wav \ + sfx_7419.wav \ + sfx_741.wav \ + sfx_7420.wav \ + sfx_7421.wav \ + sfx_7422.wav \ + sfx_7423.wav \ + sfx_7424.wav \ + sfx_7425.wav \ + sfx_7426.wav \ + sfx_7427.wav \ + sfx_7428.wav \ + sfx_7429.wav \ + sfx_742.wav \ + sfx_7430.wav \ + sfx_7431.wav \ + sfx_7432.wav \ + sfx_7433.wav \ + sfx_7434.wav \ + sfx_7435.wav \ + sfx_7436.wav \ + sfx_7437.wav \ + sfx_7438.wav \ + sfx_7439.wav \ + sfx_743.wav \ + sfx_7440.wav \ + sfx_7441.wav \ + sfx_7442.wav \ + sfx_7443.wav \ + sfx_7444.wav \ + sfx_7445.wav \ + sfx_7446.wav \ + sfx_7447.wav \ + sfx_7448.wav \ + sfx_7449.wav \ + sfx_744.wav \ + sfx_7450.wav \ + sfx_7451.wav \ + sfx_7452.wav \ + sfx_7453.wav \ + sfx_7454.wav \ + sfx_7455.wav \ + sfx_7456.wav \ + sfx_7457.wav \ + sfx_7458.wav \ + sfx_7459.wav \ + sfx_745.wav \ + sfx_7460.wav \ + sfx_7461.wav \ + sfx_7462.wav \ + sfx_7463.wav \ + sfx_7464.wav \ + sfx_7465.wav \ + sfx_7466.wav \ + sfx_7467.wav \ + sfx_7468.wav \ + sfx_7469.wav \ + sfx_746.wav \ + sfx_7470.wav \ + sfx_7471.wav \ + sfx_7472.wav \ + sfx_7473.wav \ + sfx_7474.wav \ + sfx_7475.wav \ + sfx_7476.wav \ + sfx_7477.wav \ + sfx_7478.wav \ + sfx_7479.wav \ + sfx_747.wav \ + sfx_7480.wav \ + sfx_7481.wav \ + sfx_7482.wav \ + sfx_7483.wav \ + sfx_7484.wav \ + sfx_7485.wav \ + sfx_7486.wav \ + sfx_7487.wav \ + sfx_7488.wav \ + sfx_7489.wav \ + sfx_748.wav \ + sfx_7490.wav \ + sfx_7491.wav \ + sfx_7492.wav \ + sfx_7493.wav \ + sfx_7494.wav \ + sfx_7495.wav \ + sfx_7496.wav \ + sfx_7497.wav \ + sfx_7498.wav \ + sfx_7499.wav \ + sfx_749.wav \ + sfx_74.wav \ + sfx_7500.wav \ + sfx_7501.wav \ + sfx_7502.wav \ + sfx_7503.wav \ + sfx_7504.wav \ + sfx_7505.wav \ + sfx_7506.wav \ + sfx_7507.wav \ + sfx_7508.wav \ + sfx_7509.wav \ + sfx_750.wav \ + sfx_7510.wav \ + sfx_7511.wav \ + sfx_7512.wav \ + sfx_7513.wav \ + sfx_7514.wav \ + sfx_7515.wav \ + sfx_7516.wav \ + sfx_7517.wav \ + sfx_7518.wav \ + sfx_7519.wav \ + sfx_751.wav \ + sfx_7520.wav \ + sfx_7521.wav \ + sfx_7522.wav \ + sfx_7523.wav \ + sfx_7524.wav \ + sfx_7525.wav \ + sfx_7526.wav \ + sfx_7527.wav \ + sfx_7528.wav \ + sfx_7529.wav \ + sfx_752.wav \ + sfx_7530.wav \ + sfx_7531.wav \ + sfx_7532.wav \ + sfx_7533.wav \ + sfx_7534.wav \ + sfx_7535.wav \ + sfx_7536.wav \ + sfx_7537.wav \ + sfx_7538.wav \ + sfx_7539.wav \ + sfx_753.wav \ + sfx_7540.wav \ + sfx_7541.wav \ + sfx_7542.wav \ + sfx_7543.wav \ + sfx_7544.wav \ + sfx_7545.wav \ + sfx_7546.wav \ + sfx_7547.wav \ + sfx_7548.wav \ + sfx_7549.wav \ + sfx_754.wav \ + sfx_7550.wav \ + sfx_7551.wav \ + sfx_7552.wav \ + sfx_7553.wav \ + sfx_7554.wav \ + sfx_7555.wav \ + sfx_7556.wav \ + sfx_7557.wav \ + sfx_7558.wav \ + sfx_7559.wav \ + sfx_755.wav \ + sfx_7560.wav \ + sfx_7561.wav \ + sfx_7562.wav \ + sfx_7563.wav \ + sfx_7564.wav \ + sfx_7565.wav \ + sfx_7566.wav \ + sfx_7567.wav \ + sfx_7568.wav \ + sfx_7569.wav \ + sfx_756.wav \ + sfx_7570.wav \ + sfx_7571.wav \ + sfx_7572.wav \ + sfx_7573.wav \ + sfx_7574.wav \ + sfx_7575.wav \ + sfx_7576.wav \ + sfx_7577.wav \ + sfx_7578.wav \ + sfx_7579.wav \ + sfx_757.wav \ + sfx_7580.wav \ + sfx_7581.wav \ + sfx_7582.wav \ + sfx_7583.wav \ + sfx_7584.wav \ + sfx_7585.wav \ + sfx_7586.wav \ + sfx_7587.wav \ + sfx_7588.wav \ + sfx_7589.wav \ + sfx_758.wav \ + sfx_7590.wav \ + sfx_7591.wav \ + sfx_7592.wav \ + sfx_7593.wav \ + sfx_7594.wav \ + sfx_7595.wav \ + sfx_7596.wav \ + sfx_7597.wav \ + sfx_7598.wav \ + sfx_7599.wav \ + sfx_759.wav \ + sfx_75.wav \ + sfx_7600.wav \ + sfx_7601.wav \ + sfx_7602.wav \ + sfx_7603.wav \ + sfx_7604.wav \ + sfx_7605.wav \ + sfx_7606.wav \ + sfx_7607.wav \ + sfx_7608.wav \ + sfx_7609.wav \ + sfx_760.wav \ + sfx_7610.wav \ + sfx_7611.wav \ + sfx_7612.wav \ + sfx_7613.wav \ + sfx_7614.wav \ + sfx_7615.wav \ + sfx_7616.wav \ + sfx_7617.wav \ + sfx_7618.wav \ + sfx_7619.wav \ + sfx_761.wav \ + sfx_7620.wav \ + sfx_7621.wav \ + sfx_7622.wav \ + sfx_7623.wav \ + sfx_7624.wav \ + sfx_7625.wav \ + sfx_7626.wav \ + sfx_7627.wav \ + sfx_7628.wav \ + sfx_7629.wav \ + sfx_762.wav \ + sfx_7630.wav \ + sfx_7631.wav \ + sfx_7632.wav \ + sfx_7633.wav \ + sfx_7634.wav \ + sfx_7635.wav \ + sfx_7636.wav \ + sfx_7637.wav \ + sfx_7638.wav \ + sfx_7639.wav \ + sfx_763.wav \ + sfx_7640.wav \ + sfx_7641.wav \ + sfx_7642.wav \ + sfx_7643.wav \ + sfx_7644.wav \ + sfx_7645.wav \ + sfx_7646.wav \ + sfx_7647.wav \ + sfx_7648.wav \ + sfx_7649.wav \ + sfx_764.wav \ + sfx_7650.wav \ + sfx_7651.wav \ + sfx_7652.wav \ + sfx_7653.wav \ + sfx_7654.wav \ + sfx_7655.wav \ + sfx_7656.wav \ + sfx_7657.wav \ + sfx_7658.wav \ + sfx_7659.wav \ + sfx_765.wav \ + sfx_7660.wav \ + sfx_7661.wav \ + sfx_7662.wav \ + sfx_7663.wav \ + sfx_7664.wav \ + sfx_7665.wav \ + sfx_7666.wav \ + sfx_7667.wav \ + sfx_7668.wav \ + sfx_7669.wav \ + sfx_766.wav \ + sfx_7670.wav \ + sfx_7671.wav \ + sfx_7672.wav \ + sfx_7673.wav \ + sfx_7674.wav \ + sfx_7675.wav \ + sfx_7676.wav \ + sfx_7677.wav \ + sfx_7678.wav \ + sfx_7679.wav \ + sfx_767.wav \ + sfx_7680.wav \ + sfx_7681.wav \ + sfx_7682.wav \ + sfx_7683.wav \ + sfx_7684.wav \ + sfx_7685.wav \ + sfx_7686.wav \ + sfx_7687.wav \ + sfx_7688.wav \ + sfx_7689.wav \ + sfx_768.wav \ + sfx_7690.wav \ + sfx_7691.wav \ + sfx_7692.wav \ + sfx_7693.wav \ + sfx_7694.wav \ + sfx_7695.wav \ + sfx_7696.wav \ + sfx_7697.wav \ + sfx_7698.wav \ + sfx_7699.wav \ + sfx_769.wav \ + sfx_76.wav \ + sfx_7700.wav \ + sfx_7701.wav \ + sfx_7702.wav \ + sfx_7703.wav \ + sfx_7704.wav \ + sfx_7705.wav \ + sfx_7706.wav \ + sfx_7707.wav \ + sfx_7708.wav \ + sfx_7709.wav \ + sfx_770.wav \ + sfx_7710.wav \ + sfx_7711.wav \ + sfx_7712.wav \ + sfx_7713.wav \ + sfx_7714.wav \ + sfx_7715.wav \ + sfx_7716.wav \ + sfx_7717.wav \ + sfx_7718.wav \ + sfx_7719.wav \ + sfx_771.wav \ + sfx_7720.wav \ + sfx_7721.wav \ + sfx_7722.wav \ + sfx_7723.wav \ + sfx_7724.wav \ + sfx_7725.wav \ + sfx_7726.wav \ + sfx_7727.wav \ + sfx_7728.wav \ + sfx_7729.wav \ + sfx_772.wav \ + sfx_7730.wav \ + sfx_7731.wav \ + sfx_7732.wav \ + sfx_7733.wav \ + sfx_7734.wav \ + sfx_7735.wav \ + sfx_7736.wav \ + sfx_7737.wav \ + sfx_7738.wav \ + sfx_7739.wav \ + sfx_773.wav \ + sfx_7740.wav \ + sfx_7741.wav \ + sfx_7742.wav \ + sfx_7743.wav \ + sfx_7744.wav \ + sfx_7745.wav \ + sfx_7746.wav \ + sfx_7747.wav \ + sfx_7748.wav \ + sfx_7749.wav \ + sfx_774.wav \ + sfx_7750.wav \ + sfx_7751.wav \ + sfx_7752.wav \ + sfx_7753.wav \ + sfx_7754.wav \ + sfx_7755.wav \ + sfx_7756.wav \ + sfx_7757.wav \ + sfx_7758.wav \ + sfx_7759.wav \ + sfx_775.wav \ + sfx_7760.wav \ + sfx_7761.wav \ + sfx_7762.wav \ + sfx_7763.wav \ + sfx_7764.wav \ + sfx_7765.wav \ + sfx_7766.wav \ + sfx_7767.wav \ + sfx_7768.wav \ + sfx_7769.wav \ + sfx_776.wav \ + sfx_7770.wav \ + sfx_7771.wav \ + sfx_7772.wav \ + sfx_7773.wav \ + sfx_7774.wav \ + sfx_7775.wav \ + sfx_7776.wav \ + sfx_7777.wav \ + sfx_7778.wav \ + sfx_7779.wav \ + sfx_777.wav \ + sfx_7780.wav \ + sfx_7781.wav \ + sfx_7782.wav \ + sfx_7783.wav \ + sfx_7784.wav \ + sfx_7785.wav \ + sfx_7786.wav \ + sfx_7787.wav \ + sfx_7788.wav \ + sfx_7789.wav \ + sfx_778.wav \ + sfx_7790.wav \ + sfx_7791.wav \ + sfx_7792.wav \ + sfx_7793.wav \ + sfx_7794.wav \ + sfx_7795.wav \ + sfx_7796.wav \ + sfx_7797.wav \ + sfx_7798.wav \ + sfx_7799.wav \ + sfx_779.wav \ + sfx_77.wav \ + sfx_7800.wav \ + sfx_7801.wav \ + sfx_7802.wav \ + sfx_7803.wav \ + sfx_7804.wav \ + sfx_7805.wav \ + sfx_7806.wav \ + sfx_7807.wav \ + sfx_7808.wav \ + sfx_7809.wav \ + sfx_780.wav \ + sfx_7810.wav \ + sfx_7811.wav \ + sfx_7812.wav \ + sfx_7813.wav \ + sfx_7814.wav \ + sfx_7815.wav \ + sfx_7816.wav \ + sfx_7817.wav \ + sfx_7818.wav \ + sfx_7819.wav \ + sfx_781.wav \ + sfx_7820.wav \ + sfx_7821.wav \ + sfx_7822.wav \ + sfx_7823.wav \ + sfx_7824.wav \ + sfx_7825.wav \ + sfx_7826.wav \ + sfx_7827.wav \ + sfx_7828.wav \ + sfx_7829.wav \ + sfx_782.wav \ + sfx_7830.wav \ + sfx_7831.wav \ + sfx_7832.wav \ + sfx_7833.wav \ + sfx_7834.wav \ + sfx_7835.wav \ + sfx_7836.wav \ + sfx_7837.wav \ + sfx_7838.wav \ + sfx_7839.wav \ + sfx_783.wav \ + sfx_7840.wav \ + sfx_7841.wav \ + sfx_7842.wav \ + sfx_7843.wav \ + sfx_7844.wav \ + sfx_7845.wav \ + sfx_7846.wav \ + sfx_7847.wav \ + sfx_7848.wav \ + sfx_7849.wav \ + sfx_784.wav \ + sfx_7850.wav \ + sfx_7851.wav \ + sfx_7852.wav \ + sfx_7853.wav \ + sfx_7854.wav \ + sfx_7855.wav \ + sfx_7856.wav \ + sfx_7857.wav \ + sfx_7858.wav \ + sfx_7859.wav \ + sfx_785.wav \ + sfx_7860.wav \ + sfx_7861.wav \ + sfx_7862.wav \ + sfx_7863.wav \ + sfx_7864.wav \ + sfx_7865.wav \ + sfx_7866.wav \ + sfx_7867.wav \ + sfx_7868.wav \ + sfx_7869.wav \ + sfx_786.wav \ + sfx_7870.wav \ + sfx_7871.wav \ + sfx_7872.wav \ + sfx_7873.wav \ + sfx_7874.wav \ + sfx_7875.wav \ + sfx_7876.wav \ + sfx_7877.wav \ + sfx_7878.wav \ + sfx_7879.wav \ + sfx_787.wav \ + sfx_7880.wav \ + sfx_7881.wav \ + sfx_7882.wav \ + sfx_7883.wav \ + sfx_7884.wav \ + sfx_7885.wav \ + sfx_7886.wav \ + sfx_7887.wav \ + sfx_7888.wav \ + sfx_7889.wav \ + sfx_788.wav \ + sfx_7890.wav \ + sfx_7891.wav \ + sfx_7892.wav \ + sfx_7893.wav \ + sfx_7894.wav \ + sfx_7895.wav \ + sfx_7896.wav \ + sfx_7897.wav \ + sfx_7898.wav \ + sfx_7899.wav \ + sfx_789.wav \ + sfx_78.wav \ + sfx_7900.wav \ + sfx_7901.wav \ + sfx_7902.wav \ + sfx_7903.wav \ + sfx_7904.wav \ + sfx_7905.wav \ + sfx_7906.wav \ + sfx_7907.wav \ + sfx_7908.wav \ + sfx_7909.wav \ + sfx_790.wav \ + sfx_7910.wav \ + sfx_7911.wav \ + sfx_7912.wav \ + sfx_7913.wav \ + sfx_7914.wav \ + sfx_7915.wav \ + sfx_7916.wav \ + sfx_7917.wav \ + sfx_7918.wav \ + sfx_7919.wav \ + sfx_791.wav \ + sfx_7920.wav \ + sfx_7921.wav \ + sfx_7922.wav \ + sfx_7923.wav \ + sfx_7924.wav \ + sfx_7925.wav \ + sfx_7926.wav \ + sfx_7927.wav \ + sfx_7928.wav \ + sfx_7929.wav \ + sfx_792.wav \ + sfx_7930.wav \ + sfx_7931.wav \ + sfx_7932.wav \ + sfx_7933.wav \ + sfx_7934.wav \ + sfx_7935.wav \ + sfx_7936.wav \ + sfx_7937.wav \ + sfx_7938.wav \ + sfx_7939.wav \ + sfx_793.wav \ + sfx_7940.wav \ + sfx_7941.wav \ + sfx_7942.wav \ + sfx_7943.wav \ + sfx_7944.wav \ + sfx_7945.wav \ + sfx_7946.wav \ + sfx_7947.wav \ + sfx_7948.wav \ + sfx_7949.wav \ + sfx_794.wav \ + sfx_7950.wav \ + sfx_7951.wav \ + sfx_7952.wav \ + sfx_7953.wav \ + sfx_7954.wav \ + sfx_7955.wav \ + sfx_7956.wav \ + sfx_7957.wav \ + sfx_7958.wav \ + sfx_7959.wav \ + sfx_795.wav \ + sfx_7960.wav \ + sfx_7961.wav \ + sfx_7962.wav \ + sfx_7963.wav \ + sfx_7964.wav \ + sfx_7965.wav \ + sfx_7966.wav \ + sfx_7967.wav \ + sfx_7968.wav \ + sfx_7969.wav \ + sfx_796.wav \ + sfx_7970.wav \ + sfx_7971.wav \ + sfx_7972.wav \ + sfx_7973.wav \ + sfx_7974.wav \ + sfx_7975.wav \ + sfx_7976.wav \ + sfx_7977.wav \ + sfx_7978.wav \ + sfx_7979.wav \ + sfx_797.wav \ + sfx_7980.wav \ + sfx_7981.wav \ + sfx_7982.wav \ + sfx_7983.wav \ + sfx_7984.wav \ + sfx_7985.wav \ + sfx_7986.wav \ + sfx_7987.wav \ + sfx_7988.wav \ + sfx_7989.wav \ + sfx_798.wav \ + sfx_7990.wav \ + sfx_7991.wav \ + sfx_7992.wav \ + sfx_7993.wav \ + sfx_7994.wav \ + sfx_7995.wav \ + sfx_7996.wav \ + sfx_7997.wav \ + sfx_7998.wav \ + sfx_7999.wav \ + sfx_799.wav \ + sfx_79.wav \ + sfx_7.wav \ + sfx_8000.wav \ + sfx_8001.wav \ + sfx_8002.wav \ + sfx_8003.wav \ + sfx_8004.wav \ + sfx_8005.wav \ + sfx_8006.wav \ + sfx_8007.wav \ + sfx_8008.wav \ + sfx_8009.wav \ + sfx_800.wav \ + sfx_8010.wav \ + sfx_8011.wav \ + sfx_8012.wav \ + sfx_8013.wav \ + sfx_8014.wav \ + sfx_8015.wav \ + sfx_8016.wav \ + sfx_8017.wav \ + sfx_8018.wav \ + sfx_8019.wav \ + sfx_801.wav \ + sfx_8020.wav \ + sfx_8021.wav \ + sfx_8022.wav \ + sfx_8023.wav \ + sfx_8024.wav \ + sfx_8025.wav \ + sfx_8026.wav \ + sfx_8027.wav \ + sfx_8028.wav \ + sfx_8029.wav \ + sfx_802.wav \ + sfx_8030.wav \ + sfx_8031.wav \ + sfx_8032.wav \ + sfx_8033.wav \ + sfx_8034.wav \ + sfx_8035.wav \ + sfx_8036.wav \ + sfx_8037.wav \ + sfx_8038.wav \ + sfx_8039.wav \ + sfx_803.wav \ + sfx_8040.wav \ + sfx_8041.wav \ + sfx_8042.wav \ + sfx_8043.wav \ + sfx_8044.wav \ + sfx_8045.wav \ + sfx_8046.wav \ + sfx_8047.wav \ + sfx_8048.wav \ + sfx_8049.wav \ + sfx_804.wav \ + sfx_8050.wav \ + sfx_8051.wav \ + sfx_8052.wav \ + sfx_8053.wav \ + sfx_8054.wav \ + sfx_8055.wav \ + sfx_8056.wav \ + sfx_8057.wav \ + sfx_8058.wav \ + sfx_8059.wav \ + sfx_805.wav \ + sfx_8060.wav \ + sfx_8061.wav \ + sfx_8062.wav \ + sfx_8063.wav \ + sfx_8064.wav \ + sfx_8065.wav \ + sfx_8066.wav \ + sfx_8067.wav \ + sfx_8068.wav \ + sfx_8069.wav \ + sfx_806.wav \ + sfx_8070.wav \ + sfx_8071.wav \ + sfx_8072.wav \ + sfx_8073.wav \ + sfx_8074.wav \ + sfx_8075.wav \ + sfx_8076.wav \ + sfx_8077.wav \ + sfx_8078.wav \ + sfx_8079.wav \ + sfx_807.wav \ + sfx_8080.wav \ + sfx_8081.wav \ + sfx_8082.wav \ + sfx_8083.wav \ + sfx_8084.wav \ + sfx_8085.wav \ + sfx_8086.wav \ + sfx_8087.wav \ + sfx_8088.wav \ + sfx_8089.wav \ + sfx_808.wav \ + sfx_8090.wav \ + sfx_8091.wav \ + sfx_8092.wav \ + sfx_8093.wav \ + sfx_8094.wav \ + sfx_8095.wav \ + sfx_8096.wav \ + sfx_8097.wav \ + sfx_8098.wav \ + sfx_8099.wav \ + sfx_809.wav \ + sfx_80.wav \ + sfx_8100.wav \ + sfx_8101.wav \ + sfx_8102.wav \ + sfx_8103.wav \ + sfx_8104.wav \ + sfx_8105.wav \ + sfx_8106.wav \ + sfx_8107.wav \ + sfx_8108.wav \ + sfx_8109.wav \ + sfx_810.wav \ + sfx_8110.wav \ + sfx_8111.wav \ + sfx_8112.wav \ + sfx_8113.wav \ + sfx_8114.wav \ + sfx_8115.wav \ + sfx_8116.wav \ + sfx_8117.wav \ + sfx_8118.wav \ + sfx_8119.wav \ + sfx_811.wav \ + sfx_8120.wav \ + sfx_8121.wav \ + sfx_8122.wav \ + sfx_8123.wav \ + sfx_8124.wav \ + sfx_8125.wav \ + sfx_8126.wav \ + sfx_8127.wav \ + sfx_8128.wav \ + sfx_8129.wav \ + sfx_812.wav \ + sfx_8130.wav \ + sfx_8131.wav \ + sfx_8132.wav \ + sfx_8133.wav \ + sfx_8134.wav \ + sfx_8135.wav \ + sfx_8136.wav \ + sfx_8137.wav \ + sfx_8138.wav \ + sfx_8139.wav \ + sfx_813.wav \ + sfx_8140.wav \ + sfx_8141.wav \ + sfx_8142.wav \ + sfx_8143.wav \ + sfx_8144.wav \ + sfx_8145.wav \ + sfx_8146.wav \ + sfx_8147.wav \ + sfx_8148.wav \ + sfx_8149.wav \ + sfx_814.wav \ + sfx_8150.wav \ + sfx_8151.wav \ + sfx_8152.wav \ + sfx_8153.wav \ + sfx_8154.wav \ + sfx_8155.wav \ + sfx_8156.wav \ + sfx_8157.wav \ + sfx_8158.wav \ + sfx_8159.wav \ + sfx_815.wav \ + sfx_8160.wav \ + sfx_8161.wav \ + sfx_8162.wav \ + sfx_8163.wav \ + sfx_8164.wav \ + sfx_8165.wav \ + sfx_8166.wav \ + sfx_8167.wav \ + sfx_8168.wav \ + sfx_8169.wav \ + sfx_816.wav \ + sfx_8170.wav \ + sfx_8171.wav \ + sfx_8172.wav \ + sfx_8173.wav \ + sfx_8174.wav \ + sfx_8175.wav \ + sfx_8176.wav \ + sfx_8177.wav \ + sfx_8178.wav \ + sfx_8179.wav \ + sfx_817.wav \ + sfx_8180.wav \ + sfx_8181.wav \ + sfx_8182.wav \ + sfx_8183.wav \ + sfx_8184.wav \ + sfx_8185.wav \ + sfx_8186.wav \ + sfx_8187.wav \ + sfx_8188.wav \ + sfx_8189.wav \ + sfx_818.wav \ + sfx_8190.wav \ + sfx_8191.wav \ + sfx_8192.wav \ + sfx_8193.wav \ + sfx_8194.wav \ + sfx_8195.wav \ + sfx_8196.wav \ + sfx_8197.wav \ + sfx_8198.wav \ + sfx_8199.wav \ + sfx_819.wav \ + sfx_81.wav \ + sfx_8200.wav \ + sfx_8201.wav \ + sfx_8202.wav \ + sfx_8203.wav \ + sfx_8204.wav \ + sfx_8205.wav \ + sfx_8206.wav \ + sfx_8207.wav \ + sfx_8208.wav \ + sfx_8209.wav \ + sfx_820.wav \ + sfx_8210.wav \ + sfx_8211.wav \ + sfx_8212.wav \ + sfx_8213.wav \ + sfx_8214.wav \ + sfx_8215.wav \ + sfx_8216.wav \ + sfx_8217.wav \ + sfx_8218.wav \ + sfx_8219.wav \ + sfx_821.wav \ + sfx_8220.wav \ + sfx_8221.wav \ + sfx_8222.wav \ + sfx_8223.wav \ + sfx_8224.wav \ + sfx_8225.wav \ + sfx_8226.wav \ + sfx_8227.wav \ + sfx_8228.wav \ + sfx_8229.wav \ + sfx_822.wav \ + sfx_8230.wav \ + sfx_8231.wav \ + sfx_8232.wav \ + sfx_8233.wav \ + sfx_8234.wav \ + sfx_8235.wav \ + sfx_8236.wav \ + sfx_8237.wav \ + sfx_8238.wav \ + sfx_8239.wav \ + sfx_823.wav \ + sfx_8240.wav \ + sfx_8241.wav \ + sfx_8242.wav \ + sfx_8243.wav \ + sfx_8244.wav \ + sfx_8245.wav \ + sfx_8246.wav \ + sfx_8247.wav \ + sfx_8248.wav \ + sfx_8249.wav \ + sfx_824.wav \ + sfx_8250.wav \ + sfx_8251.wav \ + sfx_8252.wav \ + sfx_8253.wav \ + sfx_8254.wav \ + sfx_8255.wav \ + sfx_8256.wav \ + sfx_8257.wav \ + sfx_8258.wav \ + sfx_8259.wav \ + sfx_825.wav \ + sfx_8260.wav \ + sfx_8261.wav \ + sfx_8262.wav \ + sfx_8263.wav \ + sfx_8264.wav \ + sfx_8265.wav \ + sfx_8266.wav \ + sfx_8267.wav \ + sfx_8268.wav \ + sfx_8269.wav \ + sfx_826.wav \ + sfx_8270.wav \ + sfx_8271.wav \ + sfx_8272.wav \ + sfx_8273.wav \ + sfx_8274.wav \ + sfx_8275.wav \ + sfx_8276.wav \ + sfx_8277.wav \ + sfx_8278.wav \ + sfx_8279.wav \ + sfx_827.wav \ + sfx_8280.wav \ + sfx_8281.wav \ + sfx_8282.wav \ + sfx_8283.wav \ + sfx_8284.wav \ + sfx_8285.wav \ + sfx_8286.wav \ + sfx_8287.wav \ + sfx_8288.wav \ + sfx_8289.wav \ + sfx_828.wav \ + sfx_8290.wav \ + sfx_8291.wav \ + sfx_8292.wav \ + sfx_8293.wav \ + sfx_8294.wav \ + sfx_8295.wav \ + sfx_8296.wav \ + sfx_8297.wav \ + sfx_8298.wav \ + sfx_8299.wav \ + sfx_829.wav \ + sfx_82.wav \ + sfx_8300.wav \ + sfx_8301.wav \ + sfx_8302.wav \ + sfx_8303.wav \ + sfx_8304.wav \ + sfx_8305.wav \ + sfx_8306.wav \ + sfx_8307.wav \ + sfx_8308.wav \ + sfx_8309.wav \ + sfx_830.wav \ + sfx_8310.wav \ + sfx_8311.wav \ + sfx_8312.wav \ + sfx_8313.wav \ + sfx_8314.wav \ + sfx_8315.wav \ + sfx_8316.wav \ + sfx_8317.wav \ + sfx_8318.wav \ + sfx_8319.wav \ + sfx_831.wav \ + sfx_8320.wav \ + sfx_8321.wav \ + sfx_8322.wav \ + sfx_8323.wav \ + sfx_8324.wav \ + sfx_8325.wav \ + sfx_8326.wav \ + sfx_8327.wav \ + sfx_8328.wav \ + sfx_8329.wav \ + sfx_832.wav \ + sfx_8330.wav \ + sfx_8331.wav \ + sfx_8332.wav \ + sfx_8333.wav \ + sfx_8334.wav \ + sfx_8335.wav \ + sfx_8336.wav \ + sfx_8337.wav \ + sfx_8338.wav \ + sfx_8339.wav \ + sfx_833.wav \ + sfx_8340.wav \ + sfx_8341.wav \ + sfx_8342.wav \ + sfx_8343.wav \ + sfx_8344.wav \ + sfx_8345.wav \ + sfx_8346.wav \ + sfx_8347.wav \ + sfx_8348.wav \ + sfx_8349.wav \ + sfx_834.wav \ + sfx_8350.wav \ + sfx_8351.wav \ + sfx_8352.wav \ + sfx_8353.wav \ + sfx_8354.wav \ + sfx_8355.wav \ + sfx_8356.wav \ + sfx_8357.wav \ + sfx_8358.wav \ + sfx_8359.wav \ + sfx_835.wav \ + sfx_8360.wav \ + sfx_8361.wav \ + sfx_8362.wav \ + sfx_8363.wav \ + sfx_8364.wav \ + sfx_8365.wav \ + sfx_8366.wav \ + sfx_8367.wav \ + sfx_8368.wav \ + sfx_8369.wav \ + sfx_836.wav \ + sfx_8370.wav \ + sfx_8371.wav \ + sfx_8372.wav \ + sfx_8373.wav \ + sfx_8374.wav \ + sfx_8375.wav \ + sfx_8376.wav \ + sfx_8377.wav \ + sfx_8378.wav \ + sfx_8379.wav \ + sfx_837.wav \ + sfx_8380.wav \ + sfx_8381.wav \ + sfx_8382.wav \ + sfx_8383.wav \ + sfx_8384.wav \ + sfx_8385.wav \ + sfx_8386.wav \ + sfx_8387.wav \ + sfx_8388.wav \ + sfx_8389.wav \ + sfx_838.wav \ + sfx_8390.wav \ + sfx_8391.wav \ + sfx_8392.wav \ + sfx_8393.wav \ + sfx_8394.wav \ + sfx_8395.wav \ + sfx_8396.wav \ + sfx_8397.wav \ + sfx_8398.wav \ + sfx_8399.wav \ + sfx_839.wav \ + sfx_83.wav \ + sfx_8400.wav \ + sfx_8401.wav \ + sfx_8402.wav \ + sfx_8403.wav \ + sfx_8404.wav \ + sfx_8405.wav \ + sfx_8406.wav \ + sfx_8407.wav \ + sfx_8408.wav \ + sfx_8409.wav \ + sfx_840.wav \ + sfx_8410.wav \ + sfx_8411.wav \ + sfx_8412.wav \ + sfx_8413.wav \ + sfx_8414.wav \ + sfx_8415.wav \ + sfx_8416.wav \ + sfx_8417.wav \ + sfx_8418.wav \ + sfx_8419.wav \ + sfx_841.wav \ + sfx_8420.wav \ + sfx_8421.wav \ + sfx_8422.wav \ + sfx_8423.wav \ + sfx_8424.wav \ + sfx_8425.wav \ + sfx_8426.wav \ + sfx_8427.wav \ + sfx_8428.wav \ + sfx_8429.wav \ + sfx_842.wav \ + sfx_8430.wav \ + sfx_8431.wav \ + sfx_8432.wav \ + sfx_8433.wav \ + sfx_8434.wav \ + sfx_8435.wav \ + sfx_8436.wav \ + sfx_8437.wav \ + sfx_8438.wav \ + sfx_8439.wav \ + sfx_843.wav \ + sfx_8440.wav \ + sfx_8441.wav \ + sfx_8442.wav \ + sfx_8443.wav \ + sfx_8444.wav \ + sfx_8445.wav \ + sfx_8446.wav \ + sfx_8447.wav \ + sfx_8448.wav \ + sfx_8449.wav \ + sfx_844.wav \ + sfx_8450.wav \ + sfx_8451.wav \ + sfx_8452.wav \ + sfx_8453.wav \ + sfx_8454.wav \ + sfx_8455.wav \ + sfx_8456.wav \ + sfx_8457.wav \ + sfx_8458.wav \ + sfx_8459.wav \ + sfx_845.wav \ + sfx_8460.wav \ + sfx_8461.wav \ + sfx_8462.wav \ + sfx_8463.wav \ + sfx_8464.wav \ + sfx_8465.wav \ + sfx_8466.wav \ + sfx_8467.wav \ + sfx_8468.wav \ + sfx_8469.wav \ + sfx_846.wav \ + sfx_8470.wav \ + sfx_8471.wav \ + sfx_8472.wav \ + sfx_8473.wav \ + sfx_8474.wav \ + sfx_8475.wav \ + sfx_8476.wav \ + sfx_8477.wav \ + sfx_8478.wav \ + sfx_8479.wav \ + sfx_847.wav \ + sfx_8480.wav \ + sfx_8481.wav \ + sfx_8482.wav \ + sfx_8483.wav \ + sfx_8484.wav \ + sfx_8485.wav \ + sfx_8486.wav \ + sfx_8487.wav \ + sfx_8488.wav \ + sfx_8489.wav \ + sfx_848.wav \ + sfx_8490.wav \ + sfx_8491.wav \ + sfx_8492.wav \ + sfx_8493.wav \ + sfx_8494.wav \ + sfx_8495.wav \ + sfx_8496.wav \ + sfx_8497.wav \ + sfx_8498.wav \ + sfx_8499.wav \ + sfx_849.wav \ + sfx_84.wav \ + sfx_8500.wav \ + sfx_8501.wav \ + sfx_8502.wav \ + sfx_8503.wav \ + sfx_8504.wav \ + sfx_8505.wav \ + sfx_8506.wav \ + sfx_8507.wav \ + sfx_8508.wav \ + sfx_8509.wav \ + sfx_850.wav \ + sfx_8510.wav \ + sfx_8511.wav \ + sfx_8512.wav \ + sfx_8513.wav \ + sfx_8514.wav \ + sfx_8515.wav \ + sfx_8516.wav \ + sfx_8517.wav \ + sfx_8518.wav \ + sfx_8519.wav \ + sfx_851.wav \ + sfx_8520.wav \ + sfx_8521.wav \ + sfx_8522.wav \ + sfx_8523.wav \ + sfx_8524.wav \ + sfx_8525.wav \ + sfx_8526.wav \ + sfx_8527.wav \ + sfx_8528.wav \ + sfx_8529.wav \ + sfx_852.wav \ + sfx_8530.wav \ + sfx_8531.wav \ + sfx_8532.wav \ + sfx_8533.wav \ + sfx_8534.wav \ + sfx_8535.wav \ + sfx_8536.wav \ + sfx_8537.wav \ + sfx_8538.wav \ + sfx_8539.wav \ + sfx_853.wav \ + sfx_8540.wav \ + sfx_8541.wav \ + sfx_8542.wav \ + sfx_8543.wav \ + sfx_8544.wav \ + sfx_8545.wav \ + sfx_8546.wav \ + sfx_8547.wav \ + sfx_8548.wav \ + sfx_8549.wav \ + sfx_854.wav \ + sfx_8550.wav \ + sfx_8551.wav \ + sfx_8552.wav \ + sfx_8553.wav \ + sfx_8554.wav \ + sfx_8555.wav \ + sfx_8556.wav \ + sfx_8557.wav \ + sfx_8558.wav \ + sfx_8559.wav \ + sfx_855.wav \ + sfx_8560.wav \ + sfx_8561.wav \ + sfx_8562.wav \ + sfx_8563.wav \ + sfx_8564.wav \ + sfx_8565.wav \ + sfx_8566.wav \ + sfx_8567.wav \ + sfx_8568.wav \ + sfx_8569.wav \ + sfx_856.wav \ + sfx_8570.wav \ + sfx_8571.wav \ + sfx_8572.wav \ + sfx_8573.wav \ + sfx_8574.wav \ + sfx_8575.wav \ + sfx_8576.wav \ + sfx_8577.wav \ + sfx_8578.wav \ + sfx_8579.wav \ + sfx_857.wav \ + sfx_8580.wav \ + sfx_8581.wav \ + sfx_8582.wav \ + sfx_8583.wav \ + sfx_8584.wav \ + sfx_8585.wav \ + sfx_8586.wav \ + sfx_8587.wav \ + sfx_8588.wav \ + sfx_8589.wav \ + sfx_858.wav \ + sfx_8590.wav \ + sfx_8591.wav \ + sfx_8592.wav \ + sfx_8593.wav \ + sfx_8594.wav \ + sfx_8595.wav \ + sfx_8596.wav \ + sfx_8597.wav \ + sfx_8598.wav \ + sfx_8599.wav \ + sfx_859.wav \ + sfx_85.wav \ + sfx_8600.wav \ + sfx_8601.wav \ + sfx_8602.wav \ + sfx_8603.wav \ + sfx_8604.wav \ + sfx_8605.wav \ + sfx_8606.wav \ + sfx_8607.wav \ + sfx_8608.wav \ + sfx_8609.wav \ + sfx_860.wav \ + sfx_8610.wav \ + sfx_8611.wav \ + sfx_8612.wav \ + sfx_8613.wav \ + sfx_8614.wav \ + sfx_8615.wav \ + sfx_8616.wav \ + sfx_8617.wav \ + sfx_8618.wav \ + sfx_8619.wav \ + sfx_861.wav \ + sfx_8620.wav \ + sfx_8621.wav \ + sfx_8622.wav \ + sfx_8623.wav \ + sfx_8624.wav \ + sfx_8625.wav \ + sfx_8626.wav \ + sfx_8627.wav \ + sfx_8628.wav \ + sfx_8629.wav \ + sfx_862.wav \ + sfx_8630.wav \ + sfx_8631.wav \ + sfx_8632.wav \ + sfx_8633.wav \ + sfx_8634.wav \ + sfx_8635.wav \ + sfx_8636.wav \ + sfx_8637.wav \ + sfx_8638.wav \ + sfx_8639.wav \ + sfx_863.wav \ + sfx_8640.wav \ + sfx_8641.wav \ + sfx_8642.wav \ + sfx_8643.wav \ + sfx_8644.wav \ + sfx_8645.wav \ + sfx_8646.wav \ + sfx_8647.wav \ + sfx_8648.wav \ + sfx_8649.wav \ + sfx_864.wav \ + sfx_8650.wav \ + sfx_8651.wav \ + sfx_8652.wav \ + sfx_8653.wav \ + sfx_8654.wav \ + sfx_8655.wav \ + sfx_8656.wav \ + sfx_8657.wav \ + sfx_8658.wav \ + sfx_8659.wav \ + sfx_865.wav \ + sfx_8660.wav \ + sfx_8661.wav \ + sfx_8662.wav \ + sfx_8663.wav \ + sfx_8664.wav \ + sfx_8665.wav \ + sfx_8666.wav \ + sfx_8667.wav \ + sfx_8668.wav \ + sfx_8669.wav \ + sfx_866.wav \ + sfx_8670.wav \ + sfx_8671.wav \ + sfx_8672.wav \ + sfx_8673.wav \ + sfx_8674.wav \ + sfx_8675.wav \ + sfx_8676.wav \ + sfx_8677.wav \ + sfx_8678.wav \ + sfx_8679.wav \ + sfx_867.wav \ + sfx_8680.wav \ + sfx_8681.wav \ + sfx_8682.wav \ + sfx_8683.wav \ + sfx_8684.wav \ + sfx_8685.wav \ + sfx_8686.wav \ + sfx_8687.wav \ + sfx_8688.wav \ + sfx_8689.wav \ + sfx_868.wav \ + sfx_8690.wav \ + sfx_8691.wav \ + sfx_8692.wav \ + sfx_8693.wav \ + sfx_8694.wav \ + sfx_8695.wav \ + sfx_8696.wav \ + sfx_8697.wav \ + sfx_8698.wav \ + sfx_8699.wav \ + sfx_869.wav \ + sfx_86.wav \ + sfx_8700.wav \ + sfx_8701.wav \ + sfx_8702.wav \ + sfx_8703.wav \ + sfx_8704.wav \ + sfx_8705.wav \ + sfx_8706.wav \ + sfx_8707.wav \ + sfx_8708.wav \ + sfx_8709.wav \ + sfx_870.wav \ + sfx_8710.wav \ + sfx_8711.wav \ + sfx_8712.wav \ + sfx_8713.wav \ + sfx_8714.wav \ + sfx_8715.wav \ + sfx_8716.wav \ + sfx_8717.wav \ + sfx_8718.wav \ + sfx_8719.wav \ + sfx_871.wav \ + sfx_8720.wav \ + sfx_8721.wav \ + sfx_8722.wav \ + sfx_8723.wav \ + sfx_8724.wav \ + sfx_8725.wav \ + sfx_8726.wav \ + sfx_8727.wav \ + sfx_8728.wav \ + sfx_8729.wav \ + sfx_872.wav \ + sfx_8730.wav \ + sfx_8731.wav \ + sfx_8732.wav \ + sfx_8733.wav \ + sfx_8734.wav \ + sfx_8735.wav \ + sfx_8736.wav \ + sfx_8737.wav \ + sfx_8738.wav \ + sfx_8739.wav \ + sfx_873.wav \ + sfx_8740.wav \ + sfx_8741.wav \ + sfx_8742.wav \ + sfx_8743.wav \ + sfx_8744.wav \ + sfx_8745.wav \ + sfx_8746.wav \ + sfx_8747.wav \ + sfx_8748.wav \ + sfx_8749.wav \ + sfx_874.wav \ + sfx_8750.wav \ + sfx_8751.wav \ + sfx_8752.wav \ + sfx_8753.wav \ + sfx_8754.wav \ + sfx_8755.wav \ + sfx_8756.wav \ + sfx_8757.wav \ + sfx_8758.wav \ + sfx_8759.wav \ + sfx_875.wav \ + sfx_8760.wav \ + sfx_8761.wav \ + sfx_8762.wav \ + sfx_8763.wav \ + sfx_8764.wav \ + sfx_8765.wav \ + sfx_8766.wav \ + sfx_8767.wav \ + sfx_8768.wav \ + sfx_8769.wav \ + sfx_876.wav \ + sfx_8770.wav \ + sfx_8771.wav \ + sfx_8772.wav \ + sfx_8773.wav \ + sfx_8774.wav \ + sfx_8775.wav \ + sfx_8776.wav \ + sfx_8777.wav \ + sfx_8778.wav \ + sfx_8779.wav \ + sfx_877.wav \ + sfx_8780.wav \ + sfx_8781.wav \ + sfx_8782.wav \ + sfx_8783.wav \ + sfx_8784.wav \ + sfx_8785.wav \ + sfx_8786.wav \ + sfx_8787.wav \ + sfx_8788.wav \ + sfx_8789.wav \ + sfx_878.wav \ + sfx_8790.wav \ + sfx_8791.wav \ + sfx_8792.wav \ + sfx_8793.wav \ + sfx_8794.wav \ + sfx_8795.wav \ + sfx_8796.wav \ + sfx_8797.wav \ + sfx_8798.wav \ + sfx_8799.wav \ + sfx_879.wav \ + sfx_87.wav \ + sfx_8800.wav \ + sfx_8801.wav \ + sfx_8802.wav \ + sfx_8803.wav \ + sfx_8804.wav \ + sfx_8805.wav \ + sfx_8806.wav \ + sfx_8807.wav \ + sfx_8808.wav \ + sfx_8809.wav \ + sfx_880.wav \ + sfx_8810.wav \ + sfx_8811.wav \ + sfx_8812.wav \ + sfx_8813.wav \ + sfx_8814.wav \ + sfx_8815.wav \ + sfx_8816.wav \ + sfx_8817.wav \ + sfx_8818.wav \ + sfx_8819.wav \ + sfx_881.wav \ + sfx_8820.wav \ + sfx_8821.wav \ + sfx_8822.wav \ + sfx_8823.wav \ + sfx_8824.wav \ + sfx_8825.wav \ + sfx_8826.wav \ + sfx_8827.wav \ + sfx_8828.wav \ + sfx_8829.wav \ + sfx_882.wav \ + sfx_8830.wav \ + sfx_8831.wav \ + sfx_8832.wav \ + sfx_8833.wav \ + sfx_8834.wav \ + sfx_8835.wav \ + sfx_8836.wav \ + sfx_8837.wav \ + sfx_8838.wav \ + sfx_8839.wav \ + sfx_883.wav \ + sfx_8840.wav \ + sfx_8841.wav \ + sfx_8842.wav \ + sfx_8843.wav \ + sfx_8844.wav \ + sfx_8845.wav \ + sfx_8846.wav \ + sfx_8847.wav \ + sfx_8848.wav \ + sfx_8849.wav \ + sfx_884.wav \ + sfx_8850.wav \ + sfx_8851.wav \ + sfx_8852.wav \ + sfx_8853.wav \ + sfx_8854.wav \ + sfx_8855.wav \ + sfx_8856.wav \ + sfx_8857.wav \ + sfx_8858.wav \ + sfx_8859.wav \ + sfx_885.wav \ + sfx_8860.wav \ + sfx_8861.wav \ + sfx_8862.wav \ + sfx_8863.wav \ + sfx_8864.wav \ + sfx_8865.wav \ + sfx_8866.wav \ + sfx_8867.wav \ + sfx_8868.wav \ + sfx_8869.wav \ + sfx_886.wav \ + sfx_8870.wav \ + sfx_8871.wav \ + sfx_8872.wav \ + sfx_8873.wav \ + sfx_8874.wav \ + sfx_8875.wav \ + sfx_8876.wav \ + sfx_8877.wav \ + sfx_8878.wav \ + sfx_8879.wav \ + sfx_887.wav \ + sfx_8880.wav \ + sfx_8881.wav \ + sfx_8882.wav \ + sfx_8883.wav \ + sfx_8884.wav \ + sfx_8885.wav \ + sfx_8886.wav \ + sfx_8887.wav \ + sfx_8888.wav \ + sfx_8889.wav \ + sfx_888.wav \ + sfx_8890.wav \ + sfx_8891.wav \ + sfx_8892.wav \ + sfx_8893.wav \ + sfx_8894.wav \ + sfx_8895.wav \ + sfx_8896.wav \ + sfx_8897.wav \ + sfx_8898.wav \ + sfx_8899.wav \ + sfx_889.wav \ + sfx_88.wav \ + sfx_8900.wav \ + sfx_8901.wav \ + sfx_8902.wav \ + sfx_8903.wav \ + sfx_8904.wav \ + sfx_8905.wav \ + sfx_8906.wav \ + sfx_8907.wav \ + sfx_8908.wav \ + sfx_8909.wav \ + sfx_890.wav \ + sfx_8910.wav \ + sfx_8911.wav \ + sfx_8912.wav \ + sfx_8913.wav \ + sfx_8914.wav \ + sfx_8915.wav \ + sfx_8916.wav \ + sfx_8917.wav \ + sfx_8918.wav \ + sfx_8919.wav \ + sfx_891.wav \ + sfx_8920.wav \ + sfx_8921.wav \ + sfx_8922.wav \ + sfx_8923.wav \ + sfx_8924.wav \ + sfx_8925.wav \ + sfx_8926.wav \ + sfx_8927.wav \ + sfx_8928.wav \ + sfx_8929.wav \ + sfx_892.wav \ + sfx_8930.wav \ + sfx_8931.wav \ + sfx_8932.wav \ + sfx_8933.wav \ + sfx_8934.wav \ + sfx_8935.wav \ + sfx_8936.wav \ + sfx_8937.wav \ + sfx_8938.wav \ + sfx_8939.wav \ + sfx_893.wav \ + sfx_8940.wav \ + sfx_8941.wav \ + sfx_8942.wav \ + sfx_8943.wav \ + sfx_8944.wav \ + sfx_8945.wav \ + sfx_8946.wav \ + sfx_8947.wav \ + sfx_8948.wav \ + sfx_8949.wav \ + sfx_894.wav \ + sfx_8950.wav \ + sfx_8951.wav \ + sfx_8952.wav \ + sfx_8953.wav \ + sfx_8954.wav \ + sfx_8955.wav \ + sfx_8956.wav \ + sfx_8957.wav \ + sfx_8958.wav \ + sfx_8959.wav \ + sfx_895.wav \ + sfx_8960.wav \ + sfx_8961.wav \ + sfx_8962.wav \ + sfx_8963.wav \ + sfx_8964.wav \ + sfx_8965.wav \ + sfx_8966.wav \ + sfx_8967.wav \ + sfx_8968.wav \ + sfx_8969.wav \ + sfx_896.wav \ + sfx_8970.wav \ + sfx_8971.wav \ + sfx_8972.wav \ + sfx_8973.wav \ + sfx_8974.wav \ + sfx_8975.wav \ + sfx_8976.wav \ + sfx_8977.wav \ + sfx_8978.wav \ + sfx_8979.wav \ + sfx_897.wav \ + sfx_8980.wav \ + sfx_8981.wav \ + sfx_8982.wav \ + sfx_8983.wav \ + sfx_8984.wav \ + sfx_8985.wav \ + sfx_8986.wav \ + sfx_8987.wav \ + sfx_8988.wav \ + sfx_8989.wav \ + sfx_898.wav \ + sfx_8990.wav \ + sfx_8991.wav \ + sfx_8992.wav \ + sfx_8993.wav \ + sfx_8994.wav \ + sfx_8995.wav \ + sfx_8996.wav \ + sfx_8997.wav \ + sfx_8998.wav \ + sfx_8999.wav \ + sfx_899.wav \ + sfx_89.wav \ + sfx_8.wav \ + sfx_9000.wav \ + sfx_9001.wav \ + sfx_9002.wav \ + sfx_9003.wav \ + sfx_9004.wav \ + sfx_9005.wav \ + sfx_9006.wav \ + sfx_9007.wav \ + sfx_9008.wav \ + sfx_9009.wav \ + sfx_900.wav \ + sfx_9010.wav \ + sfx_9011.wav \ + sfx_9012.wav \ + sfx_9013.wav \ + sfx_9014.wav \ + sfx_9015.wav \ + sfx_9016.wav \ + sfx_9017.wav \ + sfx_9018.wav \ + sfx_9019.wav \ + sfx_901.wav \ + sfx_9020.wav \ + sfx_9021.wav \ + sfx_9022.wav \ + sfx_9023.wav \ + sfx_9024.wav \ + sfx_9025.wav \ + sfx_9026.wav \ + sfx_9027.wav \ + sfx_9028.wav \ + sfx_9029.wav \ + sfx_902.wav \ + sfx_9030.wav \ + sfx_9031.wav \ + sfx_9032.wav \ + sfx_9033.wav \ + sfx_9034.wav \ + sfx_9035.wav \ + sfx_9036.wav \ + sfx_9037.wav \ + sfx_9038.wav \ + sfx_9039.wav \ + sfx_903.wav \ + sfx_9040.wav \ + sfx_9041.wav \ + sfx_9042.wav \ + sfx_9043.wav \ + sfx_9044.wav \ + sfx_9045.wav \ + sfx_9046.wav \ + sfx_9047.wav \ + sfx_9048.wav \ + sfx_9049.wav \ + sfx_904.wav \ + sfx_9050.wav \ + sfx_9051.wav \ + sfx_9052.wav \ + sfx_9053.wav \ + sfx_9054.wav \ + sfx_9055.wav \ + sfx_9056.wav \ + sfx_9057.wav \ + sfx_9058.wav \ + sfx_9059.wav \ + sfx_905.wav \ + sfx_9060.wav \ + sfx_9061.wav \ + sfx_9062.wav \ + sfx_9063.wav \ + sfx_9064.wav \ + sfx_9065.wav \ + sfx_9066.wav \ + sfx_9067.wav \ + sfx_9068.wav \ + sfx_9069.wav \ + sfx_906.wav \ + sfx_9070.wav \ + sfx_9071.wav \ + sfx_9072.wav \ + sfx_9073.wav \ + sfx_9074.wav \ + sfx_9075.wav \ + sfx_9076.wav \ + sfx_9077.wav \ + sfx_9078.wav \ + sfx_9079.wav \ + sfx_907.wav \ + sfx_9080.wav \ + sfx_9081.wav \ + sfx_9082.wav \ + sfx_9083.wav \ + sfx_9084.wav \ + sfx_9085.wav \ + sfx_9086.wav \ + sfx_9087.wav \ + sfx_9088.wav \ + sfx_9089.wav \ + sfx_908.wav \ + sfx_9090.wav \ + sfx_9091.wav \ + sfx_9092.wav \ + sfx_9093.wav \ + sfx_9094.wav \ + sfx_9095.wav \ + sfx_9096.wav \ + sfx_9097.wav \ + sfx_9098.wav \ + sfx_9099.wav \ + sfx_909.wav \ + sfx_90.wav \ + sfx_9100.wav \ + sfx_9101.wav \ + sfx_9102.wav \ + sfx_9103.wav \ + sfx_9104.wav \ + sfx_9105.wav \ + sfx_9106.wav \ + sfx_9107.wav \ + sfx_9108.wav \ + sfx_9109.wav \ + sfx_910.wav \ + sfx_9110.wav \ + sfx_9111.wav \ + sfx_9112.wav \ + sfx_9113.wav \ + sfx_9114.wav \ + sfx_9115.wav \ + sfx_9116.wav \ + sfx_9117.wav \ + sfx_9118.wav \ + sfx_9119.wav \ + sfx_911.wav \ + sfx_9120.wav \ + sfx_9121.wav \ + sfx_9122.wav \ + sfx_9123.wav \ + sfx_9124.wav \ + sfx_9125.wav \ + sfx_9126.wav \ + sfx_9127.wav \ + sfx_9128.wav \ + sfx_9129.wav \ + sfx_912.wav \ + sfx_9130.wav \ + sfx_9131.wav \ + sfx_9132.wav \ + sfx_9133.wav \ + sfx_9134.wav \ + sfx_9135.wav \ + sfx_9136.wav \ + sfx_9137.wav \ + sfx_9138.wav \ + sfx_9139.wav \ + sfx_913.wav \ + sfx_9140.wav \ + sfx_9141.wav \ + sfx_9142.wav \ + sfx_9143.wav \ + sfx_9144.wav \ + sfx_9145.wav \ + sfx_9146.wav \ + sfx_9147.wav \ + sfx_9148.wav \ + sfx_9149.wav \ + sfx_914.wav \ + sfx_9150.wav \ + sfx_9151.wav \ + sfx_9152.wav \ + sfx_9153.wav \ + sfx_9154.wav \ + sfx_9155.wav \ + sfx_9156.wav \ + sfx_9157.wav \ + sfx_9158.wav \ + sfx_9159.wav \ + sfx_915.wav \ + sfx_9160.wav \ + sfx_9161.wav \ + sfx_9162.wav \ + sfx_9163.wav \ + sfx_9164.wav \ + sfx_9165.wav \ + sfx_9166.wav \ + sfx_9167.wav \ + sfx_9168.wav \ + sfx_9169.wav \ + sfx_916.wav \ + sfx_9170.wav \ + sfx_9171.wav \ + sfx_9172.wav \ + sfx_9173.wav \ + sfx_9174.wav \ + sfx_9175.wav \ + sfx_9176.wav \ + sfx_9177.wav \ + sfx_9178.wav \ + sfx_9179.wav \ + sfx_917.wav \ + sfx_9180.wav \ + sfx_9181.wav \ + sfx_9182.wav \ + sfx_9183.wav \ + sfx_9184.wav \ + sfx_9185.wav \ + sfx_9186.wav \ + sfx_9187.wav \ + sfx_9188.wav \ + sfx_9189.wav \ + sfx_918.wav \ + sfx_9190.wav \ + sfx_9191.wav \ + sfx_9192.wav \ + sfx_9193.wav \ + sfx_9194.wav \ + sfx_9195.wav \ + sfx_9196.wav \ + sfx_9197.wav \ + sfx_9198.wav \ + sfx_9199.wav \ + sfx_919.wav \ + sfx_91.wav \ + sfx_9200.wav \ + sfx_9201.wav \ + sfx_9202.wav \ + sfx_9203.wav \ + sfx_9204.wav \ + sfx_9205.wav \ + sfx_9206.wav \ + sfx_9207.wav \ + sfx_9208.wav \ + sfx_9209.wav \ + sfx_920.wav \ + sfx_9210.wav \ + sfx_9211.wav \ + sfx_9212.wav \ + sfx_9213.wav \ + sfx_9214.wav \ + sfx_9215.wav \ + sfx_9216.wav \ + sfx_9217.wav \ + sfx_9218.wav \ + sfx_9219.wav \ + sfx_921.wav \ + sfx_9220.wav \ + sfx_9221.wav \ + sfx_9222.wav \ + sfx_9223.wav \ + sfx_9224.wav \ + sfx_9225.wav \ + sfx_9226.wav \ + sfx_9227.wav \ + sfx_9228.wav \ + sfx_9229.wav \ + sfx_922.wav \ + sfx_9230.wav \ + sfx_9231.wav \ + sfx_9232.wav \ + sfx_9233.wav \ + sfx_9234.wav \ + sfx_9235.wav \ + sfx_9236.wav \ + sfx_9237.wav \ + sfx_9238.wav \ + sfx_9239.wav \ + sfx_923.wav \ + sfx_9240.wav \ + sfx_9241.wav \ + sfx_9242.wav \ + sfx_9243.wav \ + sfx_9244.wav \ + sfx_9245.wav \ + sfx_9246.wav \ + sfx_9247.wav \ + sfx_9248.wav \ + sfx_9249.wav \ + sfx_924.wav \ + sfx_9250.wav \ + sfx_9251.wav \ + sfx_9252.wav \ + sfx_9253.wav \ + sfx_9254.wav \ + sfx_9255.wav \ + sfx_9256.wav \ + sfx_9257.wav \ + sfx_9258.wav \ + sfx_9259.wav \ + sfx_925.wav \ + sfx_9260.wav \ + sfx_9261.wav \ + sfx_9262.wav \ + sfx_9263.wav \ + sfx_9264.wav \ + sfx_9265.wav \ + sfx_9266.wav \ + sfx_9267.wav \ + sfx_9268.wav \ + sfx_9269.wav \ + sfx_926.wav \ + sfx_9270.wav \ + sfx_9271.wav \ + sfx_9272.wav \ + sfx_9273.wav \ + sfx_9274.wav \ + sfx_9275.wav \ + sfx_9276.wav \ + sfx_9277.wav \ + sfx_9278.wav \ + sfx_9279.wav \ + sfx_927.wav \ + sfx_9280.wav \ + sfx_9281.wav \ + sfx_9282.wav \ + sfx_9283.wav \ + sfx_9284.wav \ + sfx_9285.wav \ + sfx_9286.wav \ + sfx_9287.wav \ + sfx_9288.wav \ + sfx_9289.wav \ + sfx_928.wav \ + sfx_9290.wav \ + sfx_9291.wav \ + sfx_9292.wav \ + sfx_9293.wav \ + sfx_9294.wav \ + sfx_9295.wav \ + sfx_9296.wav \ + sfx_9297.wav \ + sfx_9298.wav \ + sfx_9299.wav \ + sfx_929.wav \ + sfx_92.wav \ + sfx_9300.wav \ + sfx_9301.wav \ + sfx_9302.wav \ + sfx_9303.wav \ + sfx_9304.wav \ + sfx_9305.wav \ + sfx_9306.wav \ + sfx_9307.wav \ + sfx_9308.wav \ + sfx_9309.wav \ + sfx_930.wav \ + sfx_9310.wav \ + sfx_9311.wav \ + sfx_9312.wav \ + sfx_9313.wav \ + sfx_9314.wav \ + sfx_9315.wav \ + sfx_9316.wav \ + sfx_9317.wav \ + sfx_9318.wav \ + sfx_9319.wav \ + sfx_931.wav \ + sfx_9320.wav \ + sfx_9321.wav \ + sfx_9322.wav \ + sfx_9323.wav \ + sfx_9324.wav \ + sfx_9325.wav \ + sfx_9326.wav \ + sfx_9327.wav \ + sfx_9328.wav \ + sfx_9329.wav \ + sfx_932.wav \ + sfx_9330.wav \ + sfx_9331.wav \ + sfx_9332.wav \ + sfx_9333.wav \ + sfx_9334.wav \ + sfx_9335.wav \ + sfx_9336.wav \ + sfx_9337.wav \ + sfx_9338.wav \ + sfx_9339.wav \ + sfx_933.wav \ + sfx_9340.wav \ + sfx_9341.wav \ + sfx_9342.wav \ + sfx_9343.wav \ + sfx_9344.wav \ + sfx_9345.wav \ + sfx_9346.wav \ + sfx_9347.wav \ + sfx_9348.wav \ + sfx_9349.wav \ + sfx_934.wav \ + sfx_9350.wav \ + sfx_9351.wav \ + sfx_9352.wav \ + sfx_9353.wav \ + sfx_9354.wav \ + sfx_9355.wav \ + sfx_9356.wav \ + sfx_9357.wav \ + sfx_9358.wav \ + sfx_9359.wav \ + sfx_935.wav \ + sfx_9360.wav \ + sfx_9361.wav \ + sfx_9362.wav \ + sfx_9363.wav \ + sfx_9364.wav \ + sfx_9365.wav \ + sfx_9366.wav \ + sfx_9367.wav \ + sfx_9368.wav \ + sfx_9369.wav \ + sfx_936.wav \ + sfx_9370.wav \ + sfx_9371.wav \ + sfx_9372.wav \ + sfx_9373.wav \ + sfx_9374.wav \ + sfx_9375.wav \ + sfx_9376.wav \ + sfx_9377.wav \ + sfx_9378.wav \ + sfx_9379.wav \ + sfx_937.wav \ + sfx_9380.wav \ + sfx_9381.wav \ + sfx_9382.wav \ + sfx_9383.wav \ + sfx_9384.wav \ + sfx_9385.wav \ + sfx_9386.wav \ + sfx_9387.wav \ + sfx_9388.wav \ + sfx_9389.wav \ + sfx_938.wav \ + sfx_9390.wav \ + sfx_9391.wav \ + sfx_9392.wav \ + sfx_9393.wav \ + sfx_9394.wav \ + sfx_9395.wav \ + sfx_9396.wav \ + sfx_9397.wav \ + sfx_9398.wav \ + sfx_9399.wav \ + sfx_939.wav \ + sfx_93.wav \ + sfx_9400.wav \ + sfx_9401.wav \ + sfx_9402.wav \ + sfx_9403.wav \ + sfx_9404.wav \ + sfx_9405.wav \ + sfx_9406.wav \ + sfx_9407.wav \ + sfx_9408.wav \ + sfx_9409.wav \ + sfx_940.wav \ + sfx_9410.wav \ + sfx_9411.wav \ + sfx_9412.wav \ + sfx_9413.wav \ + sfx_9414.wav \ + sfx_9415.wav \ + sfx_9416.wav \ + sfx_9417.wav \ + sfx_9418.wav \ + sfx_9419.wav \ + sfx_941.wav \ + sfx_9420.wav \ + sfx_9421.wav \ + sfx_9422.wav \ + sfx_9423.wav \ + sfx_9424.wav \ + sfx_9425.wav \ + sfx_9426.wav \ + sfx_9427.wav \ + sfx_9428.wav \ + sfx_9429.wav \ + sfx_942.wav \ + sfx_9430.wav \ + sfx_9431.wav \ + sfx_9432.wav \ + sfx_9433.wav \ + sfx_9434.wav \ + sfx_9435.wav \ + sfx_9436.wav \ + sfx_9437.wav \ + sfx_9438.wav \ + sfx_9439.wav \ + sfx_943.wav \ + sfx_9440.wav \ + sfx_9441.wav \ + sfx_9442.wav \ + sfx_9443.wav \ + sfx_9444.wav \ + sfx_9445.wav \ + sfx_9446.wav \ + sfx_9447.wav \ + sfx_9448.wav \ + sfx_9449.wav \ + sfx_944.wav \ + sfx_9450.wav \ + sfx_9451.wav \ + sfx_9452.wav \ + sfx_9453.wav \ + sfx_9454.wav \ + sfx_9455.wav \ + sfx_9456.wav \ + sfx_9457.wav \ + sfx_9458.wav \ + sfx_9459.wav \ + sfx_945.wav \ + sfx_9460.wav \ + sfx_9461.wav \ + sfx_9462.wav \ + sfx_9463.wav \ + sfx_9464.wav \ + sfx_9465.wav \ + sfx_9466.wav \ + sfx_9467.wav \ + sfx_9468.wav \ + sfx_9469.wav \ + sfx_946.wav \ + sfx_9470.wav \ + sfx_9471.wav \ + sfx_9472.wav \ + sfx_9473.wav \ + sfx_9474.wav \ + sfx_9475.wav \ + sfx_9476.wav \ + sfx_9477.wav \ + sfx_9478.wav \ + sfx_9479.wav \ + sfx_947.wav \ + sfx_9480.wav \ + sfx_9481.wav \ + sfx_9482.wav \ + sfx_9483.wav \ + sfx_9484.wav \ + sfx_9485.wav \ + sfx_9486.wav \ + sfx_9487.wav \ + sfx_9488.wav \ + sfx_9489.wav \ + sfx_948.wav \ + sfx_9490.wav \ + sfx_9491.wav \ + sfx_9492.wav \ + sfx_9493.wav \ + sfx_9494.wav \ + sfx_9495.wav \ + sfx_9496.wav \ + sfx_9497.wav \ + sfx_9498.wav \ + sfx_9499.wav \ + sfx_949.wav \ + sfx_94.wav \ + sfx_9500.wav \ + sfx_9501.wav \ + sfx_9502.wav \ + sfx_9503.wav \ + sfx_9504.wav \ + sfx_9505.wav \ + sfx_9506.wav \ + sfx_9507.wav \ + sfx_9508.wav \ + sfx_9509.wav \ + sfx_950.wav \ + sfx_9510.wav \ + sfx_9511.wav \ + sfx_9512.wav \ + sfx_9513.wav \ + sfx_9514.wav \ + sfx_9515.wav \ + sfx_9516.wav \ + sfx_9517.wav \ + sfx_9518.wav \ + sfx_9519.wav \ + sfx_951.wav \ + sfx_9520.wav \ + sfx_9521.wav \ + sfx_9522.wav \ + sfx_9523.wav \ + sfx_9524.wav \ + sfx_9525.wav \ + sfx_9526.wav \ + sfx_9527.wav \ + sfx_9528.wav \ + sfx_9529.wav \ + sfx_952.wav \ + sfx_9530.wav \ + sfx_9531.wav \ + sfx_9532.wav \ + sfx_9533.wav \ + sfx_9534.wav \ + sfx_9535.wav \ + sfx_9536.wav \ + sfx_9537.wav \ + sfx_9538.wav \ + sfx_9539.wav \ + sfx_953.wav \ + sfx_9540.wav \ + sfx_9541.wav \ + sfx_9542.wav \ + sfx_9543.wav \ + sfx_9544.wav \ + sfx_9545.wav \ + sfx_9546.wav \ + sfx_9547.wav \ + sfx_9548.wav \ + sfx_9549.wav \ + sfx_954.wav \ + sfx_9550.wav \ + sfx_9551.wav \ + sfx_9552.wav \ + sfx_9553.wav \ + sfx_9554.wav \ + sfx_9555.wav \ + sfx_9556.wav \ + sfx_9557.wav \ + sfx_9558.wav \ + sfx_9559.wav \ + sfx_955.wav \ + sfx_9560.wav \ + sfx_9561.wav \ + sfx_9562.wav \ + sfx_9563.wav \ + sfx_9564.wav \ + sfx_9565.wav \ + sfx_9566.wav \ + sfx_9567.wav \ + sfx_9568.wav \ + sfx_9569.wav \ + sfx_956.wav \ + sfx_9570.wav \ + sfx_9571.wav \ + sfx_9572.wav \ + sfx_9573.wav \ + sfx_9574.wav \ + sfx_9575.wav \ + sfx_9576.wav \ + sfx_9577.wav \ + sfx_9578.wav \ + sfx_9579.wav \ + sfx_957.wav \ + sfx_9580.wav \ + sfx_9581.wav \ + sfx_9582.wav \ + sfx_9583.wav \ + sfx_9584.wav \ + sfx_9585.wav \ + sfx_9586.wav \ + sfx_9587.wav \ + sfx_9588.wav \ + sfx_9589.wav \ + sfx_958.wav \ + sfx_9590.wav \ + sfx_9591.wav \ + sfx_9592.wav \ + sfx_9593.wav \ + sfx_9594.wav \ + sfx_9595.wav \ + sfx_9596.wav \ + sfx_9597.wav \ + sfx_9598.wav \ + sfx_9599.wav \ + sfx_959.wav \ + sfx_95.wav \ + sfx_9600.wav \ + sfx_9601.wav \ + sfx_9602.wav \ + sfx_9603.wav \ + sfx_9604.wav \ + sfx_9605.wav \ + sfx_9606.wav \ + sfx_9607.wav \ + sfx_9608.wav \ + sfx_9609.wav \ + sfx_960.wav \ + sfx_9610.wav \ + sfx_9611.wav \ + sfx_9612.wav \ + sfx_9613.wav \ + sfx_9614.wav \ + sfx_9615.wav \ + sfx_9616.wav \ + sfx_9617.wav \ + sfx_9618.wav \ + sfx_9619.wav \ + sfx_961.wav \ + sfx_9620.wav \ + sfx_9621.wav \ + sfx_9622.wav \ + sfx_9623.wav \ + sfx_9624.wav \ + sfx_9625.wav \ + sfx_9626.wav \ + sfx_9627.wav \ + sfx_9628.wav \ + sfx_9629.wav \ + sfx_962.wav \ + sfx_9630.wav \ + sfx_9631.wav \ + sfx_9632.wav \ + sfx_9633.wav \ + sfx_9634.wav \ + sfx_9635.wav \ + sfx_9636.wav \ + sfx_9637.wav \ + sfx_9638.wav \ + sfx_9639.wav \ + sfx_963.wav \ + sfx_9640.wav \ + sfx_9641.wav \ + sfx_9642.wav \ + sfx_9643.wav \ + sfx_9644.wav \ + sfx_9645.wav \ + sfx_9646.wav \ + sfx_9647.wav \ + sfx_9648.wav \ + sfx_9649.wav \ + sfx_964.wav \ + sfx_9650.wav \ + sfx_9651.wav \ + sfx_9652.wav \ + sfx_9653.wav \ + sfx_9654.wav \ + sfx_9655.wav \ + sfx_9656.wav \ + sfx_9657.wav \ + sfx_9658.wav \ + sfx_9659.wav \ + sfx_965.wav \ + sfx_9660.wav \ + sfx_9661.wav \ + sfx_9662.wav \ + sfx_9663.wav \ + sfx_9664.wav \ + sfx_9665.wav \ + sfx_9666.wav \ + sfx_9667.wav \ + sfx_9668.wav \ + sfx_9669.wav \ + sfx_966.wav \ + sfx_9670.wav \ + sfx_9671.wav \ + sfx_9672.wav \ + sfx_9673.wav \ + sfx_9674.wav \ + sfx_9675.wav \ + sfx_9676.wav \ + sfx_9677.wav \ + sfx_9678.wav \ + sfx_9679.wav \ + sfx_967.wav \ + sfx_9680.wav \ + sfx_9681.wav \ + sfx_9682.wav \ + sfx_9683.wav \ + sfx_9684.wav \ + sfx_9685.wav \ + sfx_9686.wav \ + sfx_9687.wav \ + sfx_9688.wav \ + sfx_9689.wav \ + sfx_968.wav \ + sfx_9690.wav \ + sfx_9691.wav \ + sfx_9692.wav \ + sfx_9693.wav \ + sfx_9694.wav \ + sfx_9695.wav \ + sfx_9696.wav \ + sfx_9697.wav \ + sfx_9698.wav \ + sfx_9699.wav \ + sfx_969.wav \ + sfx_96.wav \ + sfx_9700.wav \ + sfx_9701.wav \ + sfx_9702.wav \ + sfx_9703.wav \ + sfx_9704.wav \ + sfx_9705.wav \ + sfx_9706.wav \ + sfx_9707.wav \ + sfx_9708.wav \ + sfx_9709.wav \ + sfx_970.wav \ + sfx_9710.wav \ + sfx_9711.wav \ + sfx_9712.wav \ + sfx_9713.wav \ + sfx_9714.wav \ + sfx_9715.wav \ + sfx_9716.wav \ + sfx_9717.wav \ + sfx_9718.wav \ + sfx_9719.wav \ + sfx_971.wav \ + sfx_9720.wav \ + sfx_9721.wav \ + sfx_9722.wav \ + sfx_9723.wav \ + sfx_9724.wav \ + sfx_9725.wav \ + sfx_9726.wav \ + sfx_9727.wav \ + sfx_9728.wav \ + sfx_9729.wav \ + sfx_972.wav \ + sfx_9730.wav \ + sfx_9731.wav \ + sfx_9732.wav \ + sfx_9733.wav \ + sfx_9734.wav \ + sfx_9735.wav \ + sfx_9736.wav \ + sfx_9737.wav \ + sfx_9738.wav \ + sfx_9739.wav \ + sfx_973.wav \ + sfx_9740.wav \ + sfx_9741.wav \ + sfx_9742.wav \ + sfx_9743.wav \ + sfx_9744.wav \ + sfx_9745.wav \ + sfx_9746.wav \ + sfx_9747.wav \ + sfx_9748.wav \ + sfx_9749.wav \ + sfx_974.wav \ + sfx_9750.wav \ + sfx_9751.wav \ + sfx_9752.wav \ + sfx_9753.wav \ + sfx_9754.wav \ + sfx_9755.wav \ + sfx_9756.wav \ + sfx_9757.wav \ + sfx_9758.wav \ + sfx_9759.wav \ + sfx_975.wav \ + sfx_9760.wav \ + sfx_9761.wav \ + sfx_9762.wav \ + sfx_9763.wav \ + sfx_9764.wav \ + sfx_9765.wav \ + sfx_9766.wav \ + sfx_9767.wav \ + sfx_9768.wav \ + sfx_9769.wav \ + sfx_976.wav \ + sfx_9770.wav \ + sfx_9771.wav \ + sfx_9772.wav \ + sfx_9773.wav \ + sfx_9774.wav \ + sfx_9775.wav \ + sfx_9776.wav \ + sfx_9777.wav \ + sfx_9778.wav \ + sfx_9779.wav \ + sfx_977.wav \ + sfx_9780.wav \ + sfx_9781.wav \ + sfx_9782.wav \ + sfx_9783.wav \ + sfx_9784.wav \ + sfx_9785.wav \ + sfx_9786.wav \ + sfx_9787.wav \ + sfx_9788.wav \ + sfx_9789.wav \ + sfx_978.wav \ + sfx_9790.wav \ + sfx_9791.wav \ + sfx_9792.wav \ + sfx_9793.wav \ + sfx_9794.wav \ + sfx_9795.wav \ + sfx_9796.wav \ + sfx_9797.wav \ + sfx_9798.wav \ + sfx_9799.wav \ + sfx_979.wav \ + sfx_97.wav \ + sfx_9800.wav \ + sfx_9801.wav \ + sfx_9802.wav \ + sfx_9803.wav \ + sfx_9804.wav \ + sfx_9805.wav \ + sfx_9806.wav \ + sfx_9807.wav \ + sfx_9808.wav \ + sfx_9809.wav \ + sfx_980.wav \ + sfx_9810.wav \ + sfx_9811.wav \ + sfx_9812.wav \ + sfx_9813.wav \ + sfx_9814.wav \ + sfx_9815.wav \ + sfx_9816.wav \ + sfx_9817.wav \ + sfx_9818.wav \ + sfx_9819.wav \ + sfx_981.wav \ + sfx_9820.wav \ + sfx_9821.wav \ + sfx_9822.wav \ + sfx_9823.wav \ + sfx_9824.wav \ + sfx_9825.wav \ + sfx_9826.wav \ + sfx_9827.wav \ + sfx_9828.wav \ + sfx_9829.wav \ + sfx_982.wav \ + sfx_9830.wav \ + sfx_9831.wav \ + sfx_9832.wav \ + sfx_9833.wav \ + sfx_9834.wav \ + sfx_9835.wav \ + sfx_9836.wav \ + sfx_9837.wav \ + sfx_9838.wav \ + sfx_9839.wav \ + sfx_983.wav \ + sfx_9840.wav \ + sfx_9841.wav \ + sfx_9842.wav \ + sfx_9843.wav \ + sfx_9844.wav \ + sfx_9845.wav \ + sfx_9846.wav \ + sfx_9847.wav \ + sfx_9848.wav \ + sfx_9849.wav \ + sfx_984.wav \ + sfx_9850.wav \ + sfx_9851.wav \ + sfx_9852.wav \ + sfx_9853.wav \ + sfx_9854.wav \ + sfx_9855.wav \ + sfx_9856.wav \ + sfx_9857.wav \ + sfx_9858.wav \ + sfx_9859.wav \ + sfx_985.wav \ + sfx_9860.wav \ + sfx_9861.wav \ + sfx_9862.wav \ + sfx_9863.wav \ + sfx_9864.wav \ + sfx_9865.wav \ + sfx_9866.wav \ + sfx_9867.wav \ + sfx_9868.wav \ + sfx_9869.wav \ + sfx_986.wav \ + sfx_9870.wav \ + sfx_9871.wav \ + sfx_9872.wav \ + sfx_9873.wav \ + sfx_9874.wav \ + sfx_9875.wav \ + sfx_9876.wav \ + sfx_9877.wav \ + sfx_9878.wav \ + sfx_9879.wav \ + sfx_987.wav \ + sfx_9880.wav \ + sfx_9881.wav \ + sfx_9882.wav \ + sfx_9883.wav \ + sfx_9884.wav \ + sfx_9885.wav \ + sfx_9886.wav \ + sfx_9887.wav \ + sfx_9888.wav \ + sfx_9889.wav \ + sfx_988.wav \ + sfx_9890.wav \ + sfx_9891.wav \ + sfx_9892.wav \ + sfx_9893.wav \ + sfx_9894.wav \ + sfx_9895.wav \ + sfx_9896.wav \ + sfx_9897.wav \ + sfx_9898.wav \ + sfx_9899.wav \ + sfx_989.wav \ + sfx_98.wav \ + sfx_9900.wav \ + sfx_9901.wav \ + sfx_9902.wav \ + sfx_9903.wav \ + sfx_9904.wav \ + sfx_9905.wav \ + sfx_9906.wav \ + sfx_9907.wav \ + sfx_9908.wav \ + sfx_9909.wav \ + sfx_990.wav \ + sfx_9910.wav \ + sfx_9911.wav \ + sfx_9912.wav \ + sfx_9913.wav \ + sfx_9914.wav \ + sfx_9915.wav \ + sfx_9916.wav \ + sfx_9917.wav \ + sfx_9918.wav \ + sfx_9919.wav \ + sfx_991.wav \ + sfx_9920.wav \ + sfx_9921.wav \ + sfx_9922.wav \ + sfx_9923.wav \ + sfx_9924.wav \ + sfx_9925.wav \ + sfx_9926.wav \ + sfx_9927.wav \ + sfx_9928.wav \ + sfx_9929.wav \ + sfx_992.wav \ + sfx_9930.wav \ + sfx_9931.wav \ + sfx_9932.wav \ + sfx_9933.wav \ + sfx_9934.wav \ + sfx_9935.wav \ + sfx_9936.wav \ + sfx_9937.wav \ + sfx_9938.wav \ + sfx_9939.wav \ + sfx_993.wav \ + sfx_9940.wav \ + sfx_994.wav \ + sfx_995.wav \ + sfx_996.wav \ + sfx_997.wav \ + sfx_998.wav \ + sfx_999.wav \ + sfx_99.wav \ + sfx_9.wav + \ No newline at end of file diff --git a/miami/sfxlooplist.mk b/miami/sfxlooplist.mk new file mode 100644 index 00000000..65ea8ab2 --- /dev/null +++ b/miami/sfxlooplist.mk @@ -0,0 +1,34 @@ +SFX_LOOP_WAV = \ + sfx_0_loop.wav \ + sfx_176_loop.wav \ + sfx_1_loop.wav \ + sfx_25_loop.wav \ + sfx_287_loop.wav \ + sfx_2_loop.wav \ + sfx_331_loop.wav \ + sfx_384_loop.wav \ + sfx_387_loop.wav \ + sfx_390_loop.wav \ + sfx_393_loop.wav \ + sfx_396_loop.wav \ + sfx_399_loop.wav \ + sfx_3_loop.wav \ + sfx_402_loop.wav \ + sfx_408_loop.wav \ + sfx_411_loop.wav \ + sfx_414_loop.wav \ + sfx_419_loop.wav \ + sfx_420_loop.wav \ + sfx_422_loop.wav \ + sfx_423_loop.wav \ + sfx_432_loop.wav \ + sfx_436_loop.wav \ + sfx_440_loop.wav \ + sfx_444_loop.wav \ + sfx_4_loop.wav \ + sfx_5_loop.wav \ + sfx_62_loop.wav \ + sfx_6_loop.wav \ + sfx_7_loop.wav \ + sfx_82_loop.wav + \ No newline at end of file diff --git a/miami/sim.mk b/miami/sim.mk new file mode 100644 index 00000000..ced1fe22 --- /dev/null +++ b/miami/sim.mk @@ -0,0 +1,50 @@ +TARGET ?= dca-miami-sim.elf + +all: $(TARGET) + +include common.mk + +OBJS = $(RE3_OBJS) $(RW_OBJS) \ + ../src/miami/audio/sampman_null.o + +OBJS_SIM=$(OBJS:.o=.sim.o) \ + ../vendor/koshle/hlekos.sim.o \ + ../vendor/koshle/hlematrix3d.sim.o \ + ../vendor/koshle/hlepvr_mem.sim.o \ + ../vendor/koshle/hlepvr_prim.sim.o \ + ../vendor/koshle/hlepvr_scene.sim.o \ + ../vendor/koshle/hlepvr_misc.sim.o \ + ../vendor/koshle/hlepvr_init_term.sim.o \ + ../vendor/koshle/hlepvr_buffers.sim.o \ + ../vendor/koshle/hlepvr_irq.sim.o \ + ../vendor/koshle/hlepvr_fog.sim.o \ + \ + ../vendor/emu/emu/window.sim.o \ + \ + ../vendor/emu/lxdream/tacore.sim.o3 \ + \ + ../vendor/emu/refsw/pvr_mem.sim.o3 \ + ../vendor/emu/refsw/pvr_regs.sim.o3 \ + ../vendor/emu/refsw/refsw_lists.sim.o3 \ + ../vendor/emu/refsw/refsw_tile.sim.o3 \ + ../vendor/emu/refsw/TexUtils.sim.o3 \ + +DEPS_SIM1=$(OBJS_SIM:.o=.d) +DEPS_SIM=$(DEPS_SIM1:.o3=.d) + +CXXFLAGS+= -MMD -MP + +%.sim.o: %.c + $(CC) -c -O0 -g -fno-pic -no-pie -o $@ $(CFLAGS) -I../vendor/koshle -I../vendor/emu -U_WIN32 -UWIN32 -UWINNT -Ui386 -DDC_SIM -DMACOS64 $< +%.sim.o: %.cpp + $(CXX) -c -O0 -g -fno-pic -no-pie -o $@ $(CXXFLAGS) -I../vendor/koshle -I../vendor/emu -U_WIN32 -UWIN32 -UWINNT -Ui386 -DDC_SIM -DMACOS64 $< +%.sim.o3: %.cpp + $(CXX) -c -O3 -g -fno-pic -no-pie -o $@ $(CXXFLAGS) -I../vendor/koshle -I../vendor/emu -U_WIN32 -UWIN32 -UWINNT -Ui386 -DDC_SIM -DMACOS64 $< + +clean: + -rm -f $(OBJS_SIM) $(TARGET) + +$(TARGET): $(OBJS_SIM) + $(CXX) -fno-pic -no-pie -o $(TARGET) $(OBJS_SIM) -lX11 + +-include $(DEPS_SIM) \ No newline at end of file diff --git a/miami/texlist.mk b/miami/texlist.mk new file mode 100644 index 00000000..a7093251 --- /dev/null +++ b/miami/texlist.mk @@ -0,0 +1,1362 @@ +IMG_TEXTURES = \ + admiral.txd \ + air_carshow.txd \ + airgrndb.txd \ + airplan.txd \ + airporterminal.txd \ + airport.txd \ + airsignn.txd \ + airstuff2.txd \ + airtrain.txd \ + ak47.txd \ + alleyprop.txd \ + ambulan.txd \ + angel.txd \ + ap_airstuff1.txd \ + ap_billboard.txd \ + apbitsN.txd \ + ap_build1.txd \ + ap_build2.txd \ + ap_build3.txd \ + ap_build4.txd \ + ap_buildings2.txd \ + ap_carpark3.txd \ + apcarparkN.txd \ + ap_checkpoints.txd \ + apchecpointN.txd \ + ap_ci_indus2.txd \ + ap_clothes.txd \ + ap_francisland.txd \ + ap_fuel1.txd \ + ap_fuel2.txd \ + ap_fuel3.txd \ + ap_ground1.txd \ + apgroundN.txd \ + apJumboN.txd \ + ap_jumbos.txd \ + ap_learjets.txd \ + ap_LODbit.txd \ + ap_LOD.txd \ + ap_misc1bit.txd \ + ap_misc1.txd \ + ap_newprops1opac.txd \ + Apoulet.txd \ + ap_roadsect1.txd \ + aproadsN.txd \ + ap_roads.txd \ + ap_runsigns1.txd \ + ap_seaplanprop.txd \ + ap_seasplane.txd \ + ap_terminAint.txd \ + ap_terminal1boards.txd \ + ap_terminal1.txd \ + ap_termwindows.txd \ + ap_viceairport.txd \ + armour.txd \ + army.txd \ + baggage.txd \ + bakstgstuff.txd \ + bank_ext_int.txd \ + banshee.txd \ + barracks.txd \ + barrelexpos.txd \ + barrierm.txd \ + barrier.txd \ + bat.txd \ + bb_barroomtrans.txd \ + bb_barroom.txd \ + bb_exterior.txd \ + beachball.txd \ + bed.txd \ + benches.txd \ + benchm.txd \ + benson.txd \ + bfinject.txd \ + BFOBE.txd \ + bfori.txd \ + bfost.txd \ + bfotr.txd \ + bfybe.txd \ + BFYPR.txd \ + bfyri.txd \ + BFYST.txd \ + bga.txd \ + bgb.txd \ + bilbrd1.txd \ + BillBrd.txd \ + bitcrusher5.txd \ + bka.txd \ + bkb.txd \ + blistac.txd \ + bloodra.txd \ + bloodrb.txd \ + BMOBE.txd \ + bmodk.txd \ + bmost.txd \ + bmotr.txd \ + bmybb.txd \ + bmybe.txd \ + bmybu.txd \ + bmycr.txd \ + bmypi.txd \ + bmyri.txd \ + bmyst.txd \ + bnk_propy.txd \ + boathouse.txd \ + bobcat.txd \ + bomb.txd \ + bonus.txd \ + bounca.txd \ + boxes.txd \ + boxville.txd \ + brassknuckle.txd \ + breifcase.txd \ + brfcase.txd \ + bridge.txd \ + buddyshot.txd \ + buddy.txd \ + buildinghan.txd \ + buildingsite2.txd \ + buildsite.txd \ + buoy.txd \ + burger.txd \ + burrito.txd \ + bustopm.txd \ + bus.txd \ + bwidge.txd \ + cabbie.txd \ + caddy.txd \ + camera.txd \ + canopy.txd \ + carback.txd \ + card.txd \ + carwreck.txd \ + Cba.txd \ + Cbb.txd \ + ccfan.txd \ + cdrivra.txd \ + cdrivrb.txd \ + cellphone.txd \ + centralresac1.txd \ + cgona.txd \ + cgonb.txd \ + cgonc.txd \ + cgoona.txd \ + cgoonb.txd \ + chairsnstufh.txd \ + chairsntable.txd \ + cheetah.txd \ + chef.txd \ + chem2.txd \ + chem.txd \ + chnsaw.txd \ + chopper.txd \ + chromegun.txd \ + ci_backlot.txd \ + ci_ballust.txd \ + ci_dirtcorn.txd \ + ci_filmstuff1.txd \ + ci_industrial.txd \ + ci_island1.txd \ + ci_island3.txd \ + ci_LOD.txd \ + ci_mansion1.txd \ + ci_mansion2.txd \ + ci_mansionint1.txd \ + ci_mansrails.txd \ + ci_pornset.txd \ + ci_posters.txd \ + ci_redhead.txd \ + ci_setstuff1.txd \ + ci_setstuff2.txd \ + ci_setstuff4.txd \ + ci_shadcay.txd \ + ci_shadnit.txd \ + ci_studio1.txd \ + ci_studio2.txd \ + ci_studio3.txd \ + ci_studio4.txd \ + ci_studio5.txd \ + ci_studio6.txd \ + ci_studionew1.txd \ + Cla.txd \ + Clb.txd \ + clchr.txd \ + cleaver.txd \ + cl_ext1.txd \ + cl_ext2.txd \ + cl_ext3.txd \ + cl_vegbits.txd \ + cl_yacht.txd \ + cmraman.txd \ + coach.txd \ + coastg.txd \ + coffeshpext.txd \ + cofintout.txd \ + cofshad.txd \ + cofshopintdet.txd \ + cofshopint.txd \ + cofshopwind.txd \ + coknife.txd \ + colonel.txd \ + colphon.txd \ + colt45.txd \ + coltray.txd \ + comet.txd \ + compound_fence.txd \ + copdoorstuff.txd \ + cop_fake_ext02.txd \ + cop_fake_ext03.txd \ + cop_fake_ext04.txd \ + cop_oceandrv.txd \ + copshop.txd \ + cop.txd \ + corpsedrown.txd \ + courier.txd \ + cpyth.txd \ + craigpackage.txd \ + cranes.txd \ + crate.txd \ + crewa.txd \ + crewb.txd \ + crusher1.txd \ + crusher2.txd \ + crusher3.txd \ + crusherbn.txd \ + crusherb.txd \ + CSAlsb2.txd \ + CSAlscb2.txd \ + CSAlscb.txd \ + CSassA.txd \ + CSassB.txd \ + CSassC.txd \ + CSavery.txd \ + CSbigm.txd \ + CSBJ.txd \ + CSBuddy.txd \ + CScamj.txd \ + cs_cam.txd \ + CSCandy.txd \ + CScgona.txd \ + CScgonb.txd \ + CScgonc.txd \ + cschopb.txd \ + cs_chop.txd \ + CScolo.txd \ + CScougr.txd \ + CSdeal.txd \ + CSdiaz.txd \ + CSDick.txd \ + CSdirec.txd \ + CSdisp.txd \ + CSdlove.txd \ + CSDoris.txd \ + CSdwayn.txd \ + cservra.txd \ + cservrb.txd \ + CSFiller.txd \ + CSganga.txd \ + CSgangb.txd \ + CSgangc.txd \ + CSgona.txd \ + CSgonb.txd \ + CSgonc.txd \ + CSGonz.txd \ + CShlary.txd \ + CSJetro.txd \ + CSJezz.txd \ + CSJuggz.txd \ + CSKelly.txd \ + CSkent.txd \ + CSken.txd \ + CSLance.txd \ + cs_limo.txd \ + CSMaude.txd \ + CSmerc2.txd \ + CSMerc.txd \ + CSmike.txd \ + CSmitch.txd \ + CSpablo.txd \ + CSPapa.txd \ + CSPepe.txd \ + CSPercy.txd \ + CSphil.txd \ + CSplay10.txd \ + CSplay11.txd \ + CSplay2.txd \ + CSplay3.txd \ + CSplay4.txd \ + CSplay5.txd \ + CSplay6.txd \ + CSplay7.txd \ + CSplay8.txd \ + CSplay9.txd \ + CSplay.txd \ + cs_reef.txd \ + CSrich.txd \ + CSsonny.txd \ + CSumbto.txd \ + CSzepp.txd \ + cuban.txd \ + C_worker.txd \ + dcfan.txd \ + deaddodo.txd \ + deagl.txd \ + delcase.txd \ + deliext.txd \ + deliint.txd \ + deluxo.txd \ + destructo.txd \ + dgoona.txd \ + dgoonb.txd \ + dgoonc.txd \ + diaz.txd \ + dinghy.txd \ + dkcargoshp.txd \ + dkglue.txd \ + dk_houses.txd \ + dk_midbuilds.txd \ + dk_safecrack.txd \ + doccrane.txd \ + docentrance.txd \ + docfactories.txd \ + dock_bridge.txd \ + dockfence.txd \ + dockgrass.txd \ + dockgrnd.txd \ + docklight.txd \ + dockroads.txd \ + docksb.txd \ + dock_ships.txd \ + dock_shipsunder.txd \ + docks.txd \ + dolphin.txd \ + drchair.txd \ + drink.txd \ + drubish.txd \ + drugfactint.txd \ + drugpak.txd \ + drycleaners.txd \ + dshotgn.txd \ + dt_ammu3.txd \ + dt_ammu.txd \ + dt_blimp.txd \ + dt_bloodring.txd \ + dt_bluebuild.txd \ + dt_bridges.txd \ + dt_brij_LOD.txd \ + dt_compoundroof.txd \ + dt_cube_build.txd \ + dt_CUNT2.txd \ + dt_CUNT.txd \ + dt_dirtrng.txd \ + dt_egg1.txd \ + dt_firestation2.txd \ + dt_firestation.txd \ + dt_hole_build.txd \ + dt_hospital.txd \ + dt_hotring.txd \ + dt_lites.txd \ + dt_LOD2.txd \ + dt_LOD.txd \ + dt_modern1.txd \ + dt_modern2.txd \ + dt_nbeach.txd \ + dtn_cables2.txd \ + dtn_cables.txd \ + dt_newtest1.txd \ + dt_newtest2.txd \ + dtn_ground2.txd \ + dtn_ground.txd \ + dtn_roads.txd \ + dt_ntrack.txd \ + dtn_veg.txd \ + dt_qbert.txd \ + dts_bikebar_ext.txd \ + dts_copshop.txd \ + dt_scyscrap2.txd \ + dts_grassbits.txd \ + dts_ground.txd \ + dt_sheraton.txd \ + dt_shops130.txd \ + dt_shops13.txd \ + dt_shops14.txd \ + dt_shops180.txd \ + dt_shops182.txd \ + dt_shops188.txd \ + dt_shops18.txd \ + dt_shops1.txd \ + dt_shops2.txd \ + dt_shops3.txd \ + dt_shops5.txd \ + dt_shops6.txd \ + dt_shops.txd \ + dts_LODbig.txd \ + dts_LODsmall2.txd \ + dts_LODsmall.txd \ + dts_nbeach.txd \ + dts_nitelites.txd \ + dts_office.txd \ + dts_roads.txd \ + dts_shops1.txd \ + dts_shops_new1.txd \ + dts_shops.txd \ + dt_stadium.txd \ + dt_tall_build01.txd \ + dt_topshops1100.txd \ + dt_topshops130.txd \ + dt_topshops1.txd \ + dt_topshops4.txd \ + dt_topshops50.txd \ + dt_topshops5.txd \ + dt_topshops6.txd \ + dt_topshops.txd \ + dt_VCnews.txd \ + dynamite.txd \ + dynbarrels.txd \ + dynbuket.txd \ + dyncones.txd \ + dyn_drugs.txd \ + dynhydrent.txd \ + dynjunk.txd \ + dyn_keycard.txd \ + dynnewstnd2.txd \ + dynnewstnd.txd \ + dynphnm.txd \ + dynphn.txd \ + dynpostbx.txd \ + dynrecycle.txd \ + dynsigns.txd \ + dyntraffic.txd \ + dyn_trash.txd \ + dzplant.txd \ + electricgate.txd \ + enforcer.txd \ + escstep.txd \ + esperant.txd \ + espreso.txd \ + expltrk.txd \ + faggio.txd \ + fanani.txd \ + fbicar.txd \ + fbiranch.txd \ + fbi.txd \ + fencehaiti.txd \ + fences.txd \ + fffinhaiti.txd \ + fingers.txd \ + fireman.txd \ + firem.txd \ + firetruk.txd \ + fish1.txd \ + fish2.txd \ + fish3.txd \ + flame.txd \ + flatbedbak.txd \ + flatbed.txd \ + floozya.txd \ + floozyb.txd \ + floozyc.txd \ + foodcourt.txd \ + freeway.txd \ + FSfa.txd \ + fuckedcarlh.txd \ + funhiati.txd \ + gangb.txd \ + gangbur.txd \ + gangshop.txd \ + gasgren.txd \ + gcfan.txd \ + Gda.txd \ + Gdb.txd \ + Generic.txd \ + gfflag.txd \ + gf_hedge1.txd \ + gf_LOD.txd \ + gf_nbtecland.txd \ + gf_northbridge.txd \ + gfnorthisland3.txd \ + gfpalms.txd \ + gf_roadblock.txd \ + gfwater.txd \ + glendale.txd \ + golf3.txd \ + golf_bridge.txd \ + golf_clubhouse1.txd \ + golf_clubhouse2.txd \ + golfclub.txd \ + gpass.txd \ + grasspatch.txd \ + greenwoo.txd \ + grenade.txd \ + gtatrees.txd \ + guesta.txd \ + guestb.txd \ + guestc.txd \ + guestd.txd \ + gunbox.txd \ + guncolt.txd \ + habuildblok.txd \ + hahospital.txd \ + hai_carshowint.txd \ + hai_hoj.txd \ + haitbuild41.txd \ + haitbuildb.txd \ + haitbuildsea1.txd \ + haitbuildsea2.txd \ + haitcontext.txd \ + haitcont.txd \ + haitiblk1sj.txd \ + haitinwire.txd \ + haitisig.txd \ + haititag.txd \ + haitplntn.txd \ + haitplnt.txd \ + haitrdbuilf.txd \ + haiwash.txd \ + hammer.txd \ + hatrifl.txd \ + havbasketball.txd \ + hav_health.txd \ + hav_police.txd \ + helipad.txd \ + helixbarrier.txd \ + hermes.txd \ + hfobe.txd \ + hfori.txd \ + HFOST.txd \ + HFOTR.txd \ + hfybe.txd \ + HFYBU.txd \ + HFYCG.txd \ + HFYMD.txd \ + HFYPR.txd \ + HFYRI.txd \ + HFYST.txd \ + h_haibloce.txd \ + hi_cutbnk_alp.txd \ + hi_cutbnkchr.txd \ + hi_cutbnkcoch.txd \ + hi_cutbnkmain3.txd \ + hi_cutbnkmainnw.txd \ + hi_cutbnkrecpt.txd \ + hi_cutbnkvault.txd \ + hi_cutclof_opa.txd \ + hi_cutclubbar.txd \ + hi_cutcluboffice.txd \ + hi_cutclwaltst.txd \ + hi_cutcouch.txd \ + hi_cuthoomintshad.txd \ + hi_cuthotel1.txd \ + hi_cuthtl2.txd \ + hi_cuthtl4.txd \ + hi_cuthtlalp.txd \ + hi_cutint1.txd \ + hi_cutintprops.txd \ + hi_cutlaw1.txd \ + hi_cutlaw2.txd \ + hi_cutlaw3b.txd \ + hi_cutlaw3c.txd \ + hi_cutlaw3.txd \ + hi_cutlawshad.txd \ + hi_cutmaincl.txd \ + hi_cutmanshad.txd \ + hi_cutmngof.txd \ + hi_cutpropy.txd \ + hi_cutrshad.txd \ + hi_cutsafehoos.txd \ + hi_cutsafe.txd \ + hi_cutsdrm_1.txd \ + hi_cutsdrm_2.txd \ + hi_cutsdrm_3.txd \ + hi_cuttblprop.txd \ + hlrycar.txd \ + hmobe.txd \ + hmoca.txd \ + hmori.txd \ + hmost.txd \ + hmotr.txd \ + hmyap.txd \ + hmybe.txd \ + hmyri.txd \ + hmyst.txd \ + Hna.txd \ + Hnb.txd \ + hotelroomint2.txd \ + hotelroomint.txd \ + hot_mobint.txd \ + hotrina.txd \ + hotrinb.txd \ + hotring.txd \ + hot_roomtrans.txd \ + htable.txd \ + htdoorhtlalp.txd \ + htl_ext03.txd \ + htlpoolbase.txd \ + hunter.txd \ + icons1.txd \ + icons2.txd \ + icons3.txd \ + icons4.txd \ + icons5.txd \ + icons6.txd \ + icons7.txd \ + icons8.txd \ + icons9.txd \ + icons.txd \ + idaho.txd \ + IGalscb.txd \ + IGbuddy.txd \ + IGBudy2.txd \ + IGBudy3.txd \ + IGCandy.txd \ + IGColon.txd \ + IGDiaz.txd \ + IGDick.txd \ + IGGonz.txd \ + IGHlary.txd \ + IGHlry2.txd \ + IGJezz.txd \ + IGKen.txd \ + IGMerc2.txd \ + IGMerc.txd \ + IGMike2.txd \ + IGmike.txd \ + IGPercy.txd \ + IGphil2.txd \ + IGPhil3.txd \ + IGPhil.txd \ + IGSonny.txd \ + ihaitcutmf.txd \ + ihsnidxc.txd \ + infernus.txd \ + ingramsl.txd \ + intstairod.txd \ + invite.txd \ + islandlolodb.txd \ + islandlolodm.txd \ + islandmanext1.txd \ + jellyfish1.txd \ + jellyfish2.txd \ + jetmax.txd \ + jfoto.txd \ + jmoto.txd \ + jumpbox.txd \ + junk.txd \ + jwcoffin.txd \ + jwlr.txd \ + jzgirla.txd \ + jzgirlb.txd \ + katana.txd \ + kaufman.txd \ + kentpaul.txd \ + kettle.txd \ + KgoonA.txd \ + KgoonB.txd \ + kickstart.txd \ + knifecur.txd \ + landjump.txd \ + landstal.txd \ + laser.txd \ + lawcutext.txd \ + lawdoor.txd \ + lawyer.txd \ + lcfan.txd \ + lf_mediastage.txd \ + lhaith9.txd \ + lhaiths.txd \ + lhaithutn.txd \ + lhaitiblk1fg.txd \ + lhaitiblk2.txd \ + lhaitiblk4sj.txd \ + lhaitiblk6f.txd \ + lhaitiblk6k.txd \ + lhaitiblkhuty.txd \ + lhaitiblkplat.txd \ + lhaitiblkplt2.txd \ + lhaitiblksick.txd \ + lhalleysect.txd \ + lhbillbrd.txd \ + lhblbrd2.txd \ + lhblbrd3.txd \ + lhblbrd8.txd \ + lhbuild3.txd \ + lhcabbyext.txd \ + lhcabby.txd \ + lh_dockstuff1.txd \ + lh_dockstuffN1.txd \ + lhfuckercars.txd \ + lhgangshop.txd \ + lh_garagebits.txd \ + lhgardens.txd \ + lhglue.txd \ + lh_haibljki.txd \ + lh_haiblockc1.txd \ + lh_haiblockc2.txd \ + lhhotelbg.txd \ + lh_imp_exp1.txd \ + lh_imp_exp2.txd \ + lh_imp_exp3.txd \ + lh_imp_exp4.txd \ + lhjetty.txd \ + lhmachine1mn.txd \ + lhmachine1m.txd \ + lhparkline.txd \ + lh_PWbuild1grnd.txd \ + lh_PWbuild1.txd \ + lh_PWbuild2.txd \ + lh_PWbuild3.txd \ + LH_PWInterior2.txd \ + LH_PWInterior.txd \ + lhrace1.txd \ + lhrace2.txd \ + lhrace3.txd \ + lhrace4.txd \ + lhrace5.txd \ + lhrace6.txd \ + lh_racemap.txd \ + lhroadsign.txd \ + lh_roads.txd \ + lhrooft2.txd \ + lhrooft.txd \ + lh_taxi.txd \ + linerun.txd \ + lithablk12.txd \ + lithablk22.txd \ + lithablk4.txd \ + lithablk9.txd \ + lithahotel.txd \ + lithahouse.txd \ + lithawaste.txd \ + litthabg1.txd \ + litthablk6.txd \ + littlehab2.txd \ + littlhab1.txd \ + littlhablk7.txd \ + lobster.txd \ + lobtray.txd \ + LODairpN.txd \ + LODbridge.txd \ + LODbuildws07.txd \ + lod_docks2.txd \ + lod_docks.txd \ + LODhaitibig.txd \ + LODhaitinbig.txd \ + LODhaitin.txd \ + LODhaiti.txd \ + LODhavanabrg.txd \ + LODhavana.txd \ + lodhavbig.txd \ + lod_mall.txd \ + lod_man_grnds.txd \ + LODnbchw.txd \ + LODnbeachbtbig.txd \ + LODnbeach.txd \ + LODnbeachwbig.txd \ + LODodriveN.txd \ + LODodrive.txd \ + lod_starbig.txd \ + lod_starsmall.txd \ + LODwashnbrdge.txd \ + LODwashsth.txd \ + LODwshsth99.txd \ + LODwsouth08.txd \ + LODxrefhot.txd \ + lofbanner.txd \ + lounger.txd \ + lovefist.txd \ + luggage.txd \ + lwchara.txd \ + lwcharb.txd \ + m4.txd \ + m60.txd \ + machete.txd \ + mainclub2.txd \ + male01.txd \ + mall_ammu.txd \ + mallcarpark.txd \ + mallext.txd \ + mall_ground.txd \ + mallitems.txd \ + mallplants.txd \ + mall_props.txd \ + mallroof.txd \ + malltotal.txd \ + mall_veg.txd \ + manana.txd \ + manbackside.txd \ + man_cellar.txd \ + manf.txd \ + man_grnds_kb.txd \ + man_hall_2.txd \ + man_hall.txd \ + mannewdoors.txd \ + man_pool1.txd \ + man_pool2.txd \ + man_pool_shado.txd \ + man_props.txd \ + man_scarface.txd \ + mansion_details2.txd \ + mansion_details.txd \ + mansiondoor.txd \ + mansionfence.txd \ + mansion_new.txd \ + mansion_Rubbish.txd \ + mansionwall.txd \ + man_twr.txd \ + man_veg_ext.txd \ + man_wtr_ftr.txd \ + marinacpark.txd \ + marquis.txd \ + maverick.txd \ + Mba.txd \ + Mbb.txd \ + mbtbemp.txd \ + mbtbful.txd \ + mcane.txd \ + mcompounda1.txd \ + medic.txd \ + merced.txd \ + mesa.txd \ + metal.txd \ + mfour.txd \ + mgoona.txd \ + miamibin.txd \ + militcompound3.txd \ + mine.txd \ + minigun2.txd \ + minigun.txd \ + missile.txd \ + mitraffic.txd \ + ml_books.txd \ + ml_clothes.txd \ + ml_coffee.txd \ + ml_jewellers.txd \ + ml_tuneless.txd \ + molotov.txd \ + moonbeam.txd \ + mp5lng.txd \ + mpfshrt.txd \ + mporna.txd \ + mrwhoop.txd \ + mserver.txd \ + mule.txd \ + nairport.txd \ + nb_bridge1.txd \ + nbchbtjetty.txd \ + nbchdrtrack.txd \ + nbchflw.txd \ + nbdcprmndfnc.txd \ + nbdecopromnd.txd \ + nbdecoshop01.txd \ + nbdecoshop02.txd \ + nbdecoshop03.txd \ + nbeachbit03rails.txd \ + nbeachbridge.txd \ + nbeachgen.txd \ + nbeach_lod.txd \ + nbeachneon.txd \ + nbeachpayns.txd \ + nbeachrdsign.txd \ + nbeachwgen.txd \ + nbecbtwbrdge.txd \ + nbecdiving.txd \ + nbecland12bball.txd \ + nbecland12gates.txd \ + nbglue.txd \ + nbhospital.txd \ + nbhotel01.txd \ + nbhotel02.txd \ + nbhotel03.txd \ + nbhotel04.txd \ + nbhotel05.txd \ + nbhotel06.txd \ + nbhotel07.txd \ + nbhotel08.txd \ + nb_police.txd \ + nbrooft.txd \ + nbsavehotel.txd \ + nbt_apartpool01.txd \ + nbt_apartpool.txd \ + nbt_barfence01.txd \ + nbtecland.txd \ + nbt_hotel01.txd \ + nbt_hotel02.txd \ + nbt_hotel03glass.txd \ + nbt_hotel03.txd \ + nbt_hotel04.txd \ + nbt_hotel05.txd \ + nbt_hotel06balcony.txd \ + nbt_hotel06.txd \ + nbt_hotel07fence.txd \ + nbt_hotel07.txd \ + nbthotel08fence.txd \ + nbthotel08.txd \ + nbt_htlpoolbar01.txd \ + nbt_htlpoolbase.txd \ + nbt_htlpoolrm.txd \ + nbt_mans04balcony.txd \ + nbt_mansion01.txd \ + nbt_mansion02.txd \ + nbt_mansion03.txd \ + nbt_mansion04.txd \ + nbt_mansionbase01.txd \ + nbt_mansionbase02.txd \ + nbt_mnsnwtr.txd \ + nbt_pizzaplace.txd \ + nbtroofnext.txd \ + nbtrooftop.txd \ + nbtshopfronts02.txd \ + nbtshops.txd \ + nbtsmallres.txd \ + nbwbridge.txd \ + nbw_grssypatch.txd \ + nbw_hotel06balcony.txd \ + nbw_hotel06.txd \ + nbwjetty.txd \ + nbw_pinkblock.txd \ + nbwtoolshp5.txd \ + ncarwreck.txd \ + neonnd.txd \ + newashbuild1.txd \ + newbuilding01.txd \ + newrampn.txd \ + newramp.txd \ + newroof.txd \ + newspadbuild.txd \ + newstand.txd \ + nitestick.txd \ + northbuild.txd \ + nubuilding1glass.txd \ + oceanic.txd \ + oceanstrip10.txd \ + oceanstrip2.txd \ + ocmiamistrip12.txd \ + ocmiamistrip16.txd \ + ocmiamistrip5.txd \ + ocmiamistrip6.txd \ + ocmiamistrip7.txd \ + ocmiamistrip8.txd \ + ocmiamistrip9.txd \ + ocmiamistrip9X.txd \ + ocmiamistrip.txd \ + ocnmiamistrip.txd \ + od_beachstuff.txd \ + od_bighotel.txd \ + od_bighotpool.txd \ + od_bigshops2.txd \ + od_bphq.txd \ + od_chariot.txd \ + od_clubback.txd \ + od_clubout.txd \ + od_cop_interior.txd \ + od_fence.txd \ + od_hotels1.txd \ + od_lighthousein.txd \ + od_lighthouse.txd \ + od_neons1.txd \ + odneontest.txd \ + odnglue.txd \ + odnhotelblk.txd \ + odnhotelspr.txd \ + odnroads.txd \ + odnrooft.txd \ + odnwire.txd \ + od_watertower.txd \ + od_waterwrx.txd \ + package1.txd \ + packer.txd \ + patriot.txd \ + pcj600.txd \ + peren.txd \ + petrolcan.txd \ + petrol.txd \ + Pga.txd \ + Pgb.txd \ + pharmacyint.txd \ + pheonix.txd \ + philbmb.txd \ + philinter.txd \ + philss.txd \ + phnplay.txd \ + phoncol.txd \ + pitchrb.txd \ + pizzaboy.txd \ + play10.txd \ + play11.txd \ + play12.txd \ + player2.txd \ + player3.txd \ + player4.txd \ + player5.txd \ + player6.txd \ + player7.txd \ + player8.txd \ + player9.txd \ + player.txd \ + plc_stinger.txd \ + plyphon.txd \ + polbals.txd \ + policechair.txd \ + police.txd \ + polmav.txd \ + pony.txd \ + poolcue.txd \ + poolq.txd \ + portabarrier.txd \ + predator.txd \ + printra.txd \ + printrb.txd \ + printrc.txd \ + propbarstuff.txd \ + prop_pizzabox.txd \ + psycho.txd \ + python.txd \ + radar00.txd \ + radar01.txd \ + radar02.txd \ + radar03.txd \ + radar04.txd \ + radar05.txd \ + radar06.txd \ + radar07.txd \ + radar08.txd \ + radar09.txd \ + radar10.txd \ + radar11.txd \ + radar12.txd \ + radar13.txd \ + radar14.txd \ + radar15.txd \ + radar16.txd \ + radar17.txd \ + radar18.txd \ + radar19.txd \ + radar20.txd \ + radar21.txd \ + radar22.txd \ + radar23.txd \ + radar24.txd \ + radar25.txd \ + radar26.txd \ + radar27.txd \ + radar28.txd \ + radar29.txd \ + radar30.txd \ + radar31.txd \ + radar32.txd \ + radar33.txd \ + radar34.txd \ + radar35.txd \ + radar36.txd \ + radar37.txd \ + radar38.txd \ + radar39.txd \ + radar40.txd \ + radar41.txd \ + radar42.txd \ + radar43.txd \ + radar44.txd \ + radar45.txd \ + radar46.txd \ + radar47.txd \ + radar48.txd \ + radar49.txd \ + radar50.txd \ + radar51.txd \ + radar52.txd \ + radar53.txd \ + radar54.txd \ + radar55.txd \ + radar56.txd \ + radar57.txd \ + radar58.txd \ + radar59.txd \ + radar60.txd \ + radar61.txd \ + radar62.txd \ + radar63.txd \ + rafneonsign.txd \ + ramp2.txd \ + rancher.txd \ + rcbandit.txd \ + rcbaron.txd \ + rcbomb.txd \ + rcgoblin.txd \ + rcraider.txd \ + recstudio2.txd \ + recstudio.txd \ + reefer.txd \ + regina.txd \ + rhino.txd \ + rio.txd \ + roadsign.txd \ + rocketla.txd \ + rock.txd \ + rocky.txd \ + romero.txd \ + roofbit.txd \ + ruger.txd \ + rumpo.txd \ + sabretur.txd \ + sabre.txd \ + sam.txd \ + sanchez.txd \ + sandcastle.txd \ + sandking.txd \ + satdish.txd \ + scarinterior.txd \ + scenery.txd \ + schair.txd \ + screwdriver.txd \ + seabed.txd \ + seaspar.txd \ + securica.txd \ + sentinel.txd \ + sentxs.txd \ + seveneleven.txd \ + sfrenda.txd \ + sfrendb.txd \ + Sga.txd \ + Sgb.txd \ + sgc.txd \ + sgoona.txd \ + sgoonb.txd \ + sharedalleyod.txd \ + shared_beach.txd \ + sharedbiggrass.txd \ + shark.txd \ + shipthisgamenow.txd \ + shootra.txd \ + shootrb.txd \ + shopfronts03.txd \ + shotgspa.txd \ + shotvcr.txd \ + signs.txd \ + sjmahaitiground.txd \ + sjmbncrusher5.txd \ + sjmh1haitiroad.txd \ + sjmh5haitiroad.txd \ + sjmhahaitiground.txd \ + sjmhbghaitiground.txd \ + sjmjcrusher4.txd \ + sjmroadhaitiN.txd \ + sjmsprayn.txd \ + sjmspray.txd \ + S_keep.txd \ + skimmer.txd \ + smashbarr.txd \ + sniper.txd \ + spad_buildnew.txd \ + spad_main.txd \ + spad_opac.txd \ + spad_shado.txd \ + spad_water.txd \ + spand.txd \ + spandxa.txd \ + spandxb.txd \ + sparrow.txd \ + speeder.txd \ + speed.txd \ + squalo.txd \ + ssjmcrusher5.txd \ + stagebar.txd \ + stagestuff.txd \ + stallion.txd \ + standext.txd \ + sta_pinkblock.txd \ + star1b.txd \ + star1.txd \ + star2.txd \ + star3.txd \ + star4.txd \ + star5.txd \ + starbits06.txd \ + starbits17.txd \ + starbits22.txd \ + starbits24.txd \ + starbits49.txd \ + starbits50.txd \ + starbits51.txd \ + starbridge.txd \ + starbuildsite.txd \ + stardepot.txd \ + starf.txd \ + StarLD.txd \ + starmansionst.txd \ + starpool.txd \ + starroad.txd \ + starvillas01.txd \ + starwaterfrnt01.txd \ + stilty.txd \ + stinger.txd \ + stmansion.txd \ + streetlights.txd \ + stretch.txd \ + Stripa.txd \ + stripback.txd \ + stripb.txd \ + stripc.txd \ + stripinter.txd \ + striplights.txd \ + strpclbsgns.txd \ + stunt.txd \ + subcrates.txd \ + submarine.txd \ + svntray.txd \ + swashlandb.txd \ + swashnwbuild5.txd \ + swat.txd \ + swimupbar.txd \ + swshneon.txd \ + swshtelgrph.txd \ + targets.txd \ + taxi_d.txd \ + taxi.txd \ + teargas.txd \ + tec9.txd \ + telegraph.txd \ + telephait.txd \ + telly.txd \ + tikiprops01.txd \ + tit_wank.txd \ + topfun.txd \ + towerdoor.txd \ + transbits2.txd \ + trash.txd \ + treeshad.txd \ + tropic.txd \ + tunnel.txd \ + turtle.txd \ + uzi.txd \ + vcnmav.txd \ + vegetation.txd \ + vend.txd \ + vice1.txd \ + vice2.txd \ + vice3.txd \ + vice4.txd \ + vice5.txd \ + vice6.txd \ + vice7.txd \ + vice8.txd \ + vicechee.txd \ + virgo.txd \ + voodoo.txd \ + walton.txd \ + wash_ammu.txd \ + washbballcrt.txd \ + washbuild120.txd \ + washbuild124.txd \ + washbuild13.txd \ + washbuild15.txd \ + washbuild1.txd \ + washbuild41.txd \ + washbuild43.txd \ + washbuild44.txd \ + washbuild5.txd \ + washbuild6.txd \ + washbuild7.txd \ + washcorn2.txd \ + washcpark2.txd \ + washfence128.txd \ + wash_hosp.txd \ + washing.txd \ + washlandb.txd \ + washland.txd \ + washmallnew.txd \ + washmallpark.txd \ + washmarina.txd \ + washmiamistrip11.txd \ + washmiamistrip13.txd \ + washmiamistrip2.txd \ + washmiamistrip3.txd \ + washmiamistrip4.txd \ + washnewbuild1.txd \ + washnewbuild21.txd \ + washnewbuild23.txd \ + washnewbuild24.txd \ + washnewbuild25.txd \ + washnewbuild2.txd \ + washnewbuild3.txd \ + washnewbuild5.txd \ + washnewbuild6.txd \ + washnrthpstr1.txd \ + washotel2.txd \ + washrestone.txd \ + washroads.txd \ + washsfehse.txd \ + washshops1.txd \ + washsky1.txd \ + washsthpstr.txd \ + washwater.txd \ + wastebin.txd \ + wa_tool2.txd \ + wfobe.txd \ + wfogo.txd \ + wfori.txd \ + wfosh.txd \ + wfost.txd \ + wfotr.txd \ + wfybe.txd \ + wfybu.txd \ + wfycst.txd \ + wfyg1.txd \ + wfyg2.txd \ + wfyjg.txd \ + WFYLG.txd \ + wfypr.txd \ + wfyri.txd \ + wfysh.txd \ + wfysk.txd \ + wfyst.txd \ + wglass.txd \ + wire.txd \ + wmobe.txd \ + wmobu.txd \ + wmoca.txd \ + wmogo.txd \ + wmori.txd \ + wmost.txd \ + wmotr.txd \ + wmybe.txd \ + wmybu.txd \ + wmycr.txd \ + wmycw.txd \ + wmygo.txd \ + wmyjg.txd \ + WMYLG.txd \ + wmypi.txd \ + wmyri.txd \ + wmysk.txd \ + wmyst.txd \ + woodpanels.txd \ + woshmalglas.txd \ + wshbuildirt.txd \ + wshbuildws40.txd \ + wshcoklnd.txd \ + wshcrprkdrt.txd \ + wshcrprkstuf.txd \ + wshgarage1.txd \ + wshmolmrk.txd \ + wshneon.txd \ + wshotelsign35.txd \ + wshrdblck.txd \ + wshsprayshp.txd \ + wshsthbuild1.txd \ + wshstripint.txd \ + wshtelgrph.txd \ + wshxrefhirise.txd \ + wshxrefhse2.txd \ + wshxrefhse.txd \ + wshxreflod.txd \ + wshxrefpump.txd \ + wslawyers.txd \ + xrefhotels.txd \ + yacht_kb.txd \ + yacht_lod.txd \ + yachttings.txd \ + yacht.txd \ + yankee.txd \ + yt_launch.txd \ + zebra.txd \ No newline at end of file diff --git a/miami/wavlist.mk b/miami/wavlist.mk new file mode 100644 index 00000000..af3e4a6c --- /dev/null +++ b/miami/wavlist.mk @@ -0,0 +1,1121 @@ +STREAM_WAV = \ + phil1_3.wav \ + bnk4_3k.wav \ + rok3_4.wav \ + shaft05.wav \ + mob_58c.wav \ + mob_72d.wav \ + cok4_6.wav \ + bnk2_4.wav \ + cap1_4.wav \ + bike1_3.wav \ + finkill.wav \ + resc_1.wav \ + fin_11b.wav \ + mob_05c.wav \ + col4_14.wav \ + mob_20a.wav \ + mob_71g.wav \ + mob_58a.wav \ + bust_14.wav \ + gimme13.wav \ + col5_19.wav \ + bnk4_28.wav \ + law1_8.wav \ + col1_1.wav \ + col5_8.wav \ + cub4_9.wav \ + mob_70a.wav \ + STRIP_3.wav \ + bud1_3.wav \ + law4_1d.wav \ + rok3_71.wav \ + fin_1c.wav \ + law3_16.wav \ + mob_35c.wav \ + bnk4_3t.wav \ + fin_13.wav \ + job2_4.wav \ + prn1_12.wav \ + cok4_10.wav \ + bnk4_29.wav \ + bust_16.wav \ + mob_10a.wav \ + col3_19.wav \ + race4.wav \ + mob_61b.wav \ + phil2_4.wav \ + law3_10.wav \ + mob_71i.wav \ + bnk2_7.wav \ + mob_24b.wav \ + col2_2.wav \ + mob_25c.wav \ + OOH2.wav \ + race12.wav \ + bnk4_3p.wav \ + mono_7.wav \ + mob_63f.wav \ + CameraR.wav \ + bjm1_5.wav \ + cok2_4.wav \ + mob_02a.wav \ + bust_24.wav \ + porn3_3.wav \ + law2_5.wav \ + race10.wav \ + porn1_9.wav \ + mob_66a.wav \ + fud_19.wav \ + race7.wav \ + bnk1_10.wav \ + mob_17e.wav \ + piss_06.wav \ + ass_10.wav \ + star_3.wav \ + mob_43a.wav \ + cok1_4.wav \ + bud3_9b.wav \ + race9.wav \ + fin_2c.wav \ + band_01.wav \ + hot2.wav \ + bnk1_5.wav \ + bnk3_3b.wav \ + tex1_5.wav \ + ass_8.wav \ + mob_10e.wav \ + mob_63g.wav \ + bnk4_6.wav \ + mob_16d.wav \ + fin_10.wav \ + col3_17.wav \ + mob_24e.wav \ + bnk4_21.wav \ + mob_52C.wav \ + mob_52b.wav \ + mob_52h.wav \ + mob_58b.wav \ + bk4_19b.wav \ + col3_14.wav \ + col5_18.wav \ + rok3_1.wav \ + rok3_19.wav \ + bnk4_3s.wav \ + mob_57a.wav \ + fud_10.wav \ + cub4_10.wav \ + mob_45g.wav \ + mob_68c.wav \ + star_1.wav \ + law2_10.wav \ + piss_03.wav \ + race14.wav \ + col3_13.wav \ + law2_9.wav \ + bust_17.wav \ + mob_36a.wav \ + col5_14.wav \ + bud1_10.wav \ + rok1_5.wav \ + mob_40a.wav \ + col2_13.wav \ + col1_3.wav \ + bk4_24b.wav \ + tax3_4.wav \ + bnk4_8.wav \ + fin_3.wav \ + cub1_9.wav \ + cok2_8c.wav \ + rok3_67.wav \ + mob_34b.wav \ + bnk4_25.wav \ + col4_1.wav \ + cub4_1.wav \ + bud1_4.wav \ + col1_4.wav \ + mob_08a.wav \ + ass_11.wav \ + cub1_3.wav \ + bud1_5.wav \ + mob_72f.wav \ + cok2_2.wav \ + cub1_6.wav \ + bud2_3.wav \ + LiftBel.wav \ + golf_2.wav \ + rok3_25.wav \ + col3_9.wav \ + hot8.wav \ + cub1_7.wav \ + cok2_14.wav \ + cok2_7c.wav \ + mob_45f.wav \ + col5_11.wav \ + mob_34d.wav \ + rok3_10.wav \ + cok4_23.wav \ + mono_3.wav \ + law4_4.wav \ + fud_14.wav \ + mob_34a.wav \ + col4_20.wav \ + bnk422b.wav \ + BAR_6.wav \ + mob_33a.wav \ + tax1_1.wav \ + mob_68d.wav \ + resc_8.wav \ + gimme04.wav \ + mob_95a.wav \ + fud_16.wav \ + bnk4_3q.wav \ + rok3_9.wav \ + mob_24a.wav \ + burg_12.wav \ + cok4_4.wav \ + mob_56d.wav \ + fin_2b.wav \ + prn1_21.wav \ + tex3_5.wav \ + col2_6a.wav \ + mob_06c.wav \ + col4_2.wav \ + cok210a.wav \ + bud1_9.wav \ + bnk4_3i.wav \ + bnk4_2.wav \ + mob_16b.wav \ + cap1_8.wav \ + cnt1_4.wav \ + mob_41h.wav \ + mob_29c.wav \ + mob_30e.wav \ + intro1.wav \ + mob_10d.wav \ + rok3_69.wav \ + job3_3.wav \ + tax1_3.wav \ + merc_39.wav \ + cub2_3b.wav \ + mob_63e.wav \ + cub2_5.wav \ + cub2_3a.wav \ + race1.wav \ + star_4.wav \ + ass_9.wav \ + STRIP_8.wav \ + job2_5.wav \ + cnt1_3.wav \ + col4_24.wav \ + bud1_1.wav \ + mob_45d.wav \ + bnk1_2.wav \ + tax2_1.wav \ + prn1_14.wav \ + BikeRev.wav \ + mob_09c.wav \ + mob_40g.wav \ + bnk2_1.wav \ + mob_04c.wav \ + AirHornR.wav \ + mob_99a.wav \ + cub1_2.wav \ + job1_1d.wav \ + SnipSCRL.wav \ + bnk4_42.wav \ + mob_55c.wav \ + law3_2.wav \ + bud3_8b.wav \ + mob_11f.wav \ + bnk4_7.wav \ + rok3_64.wav \ + mob_56e.wav \ + mono11.wav \ + band_02.wav \ + mob_26e.wav \ + cub4_6.wav \ + mob_14e.wav \ + mob_09a.wav \ + mob_63h.wav \ + burg_09.wav \ + bust_26.wav \ + mob_05a.wav \ + col1_5.wav \ + STRIP_7.wav \ + hot1.wav \ + mob_71f.wav \ + col3_11.wav \ + mob_71l.wav \ + mob_42a.wav \ + col4_18.wav \ + piss_13.wav \ + mob_03e.wav \ + shark_4.wav \ + mob_55d.wav \ + phil2_2.wav \ + law4_3.wav \ + mob_45l.wav \ + mob_73f.wav \ + mob_29a.wav \ + STRIP_2.wav \ + col2_16.wav \ + col3_18.wav \ + mob_05b.wav \ + law1_9.wav \ + tex1_6.wav \ + mob_09e.wav \ + tax2_4.wav \ + burg_01.wav \ + bnk4_38.wav \ + golf_1.wav \ + law2_3.wav \ + BAR_3.wav \ + phil2_1.wav \ + prn1_15.wav \ + col5_7.wav \ + gimme05.wav \ + bud3_8a.wav \ + cub4_8.wav \ + cub2_3c.wav \ + col3_4.wav \ + cub1_1.wav \ + mob_14a.wav \ + bud3_9a.wav \ + gimme11.wav \ + mob_73c.wav \ + cok4_27.wav \ + lanamu2.wav \ + crust08.wav \ + band_07.wav \ + hot7.wav \ + cub3_2.wav \ + mob_41f.wav \ + bnk4_3m.wav \ + bud3_1b.wav \ + mob_11c.wav \ + mob_63c.wav \ + bnk4_3f.wav \ + tex2_1.wav \ + rok3_63.wav \ + mob_10b.wav \ + lanstp2.wav \ + mob_01a.wav \ + col5_15.wav \ + mob_62b.wav \ + mob_71e.wav \ + cub1_5.wav \ + col3_24.wav \ + mob_58d.wav \ + bud3_6.wav \ + col5_6.wav \ + mob_30f.wav \ + job2_7.wav \ + prn1_13.wav \ + bk4_14a.wav \ + mob_45c.wav \ + bnk4_1.wav \ + tax3_3.wav \ + mob_03a.wav \ + tex1_3.wav \ + mob_14c.wav \ + crust03.wav \ + fud_04.wav \ + bnk4_37.wav \ + mob_11a.wav \ + cnt2_4.wav \ + law3_25.wav \ + mob_17g.wav \ + bnk4_32.wav \ + mob_71c.wav \ + Cheer1.wav \ + bnk4_4a.wav \ + burg_10.wav \ + cub1_8.wav \ + mob_36c.wav \ + shark_3.wav \ + cok4_2.wav \ + mob_40b.wav \ + mob_11e.wav \ + mob_57e.wav \ + band_05.wav \ + job4_2.wav \ + cub3_4.wav \ + porn1_8.wav \ + cub4_14.wav \ + bnk1_7.wav \ + mob_40c.wav \ + race11.wav \ + crust07.wav \ + col1_8.wav \ + burg_05.wav \ + gimme15.wav \ + piss_12.wav \ + bk4_34a.wav \ + BAR_2.wav \ + bnk4_9.wav \ + mob_40e.wav \ + mob_73i.wav \ + cok2_13.wav \ + piss_04.wav \ + mob_71h.wav \ + tax2_7.wav \ + bud3_1c.wav \ + cub4_5.wav \ + mob_54e.wav \ + bk4_31a.wav \ + law3_11.wav \ + bust_05.wav \ + cub2_9.wav \ + mob_46e.wav \ + race6.wav \ + col3_20.wav \ + mob_56c.wav \ + cok210b.wav \ + mob_29b.wav \ + mob_20c.wav \ + resc_5.wav \ + cub4_12.wav \ + cub2_7.wav \ + bk4_19a.wav \ + bust_02.wav \ + mob_36b.wav \ + mob_29e.wav \ + LiftOp.wav \ + tex3_2.wav \ + cub1_4.wav \ + bnk2_3.wav \ + rok3_16.wav \ + col3_15.wav \ + burg_06.wav \ + rok3_21.wav \ + rok3_27.wav \ + col5_5.wav \ + piss_15.wav \ + cnt1_1.wav \ + cok4_18.wav \ + mob_20e.wav \ + cok4_3.wav \ + bnk4_48.wav \ + bnk4_95.wav \ + prn1_19.wav \ + mono_8.wav \ + job2_6.wav \ + bnk4_3v.wav \ + bk4_23c.wav \ + rok2_01.wav \ + mob_45k.wav \ + bnk4_13.wav \ + mob_54c.wav \ + bnk4_18.wav \ + cok3_3.wav \ + tax1_5.wav \ + prn1_11.wav \ + law2_6.wav \ + STRIP_5.wav \ + mob_30b.wav \ + intro4.wav \ + bnk4_11.wav \ + job5_3.wav \ + cub4_13.wav \ + mob_73a.wav \ + mob_68b.wav \ + mob_46b.wav \ + cub4_2.wav \ + mob_96a.wav \ + col3_25.wav \ + col4_19.wav \ + col5_3.wav \ + col4_21.wav \ + cok4_17.wav \ + bnk1_1.wav \ + mob_41d.wav \ + bust_21.wav \ + rok3_8.wav \ + resc_7.wav \ + tex3_4.wav \ + fin_5.wav \ + hot11.wav \ + col2_12.wav \ + bust_10.wav \ + phil2_7.wav \ + bk4_31b.wav \ + hot15.wav \ + bnk3_4b.wav \ + job2_8.wav \ + mob_63b.wav \ + cap1_10.wav \ + race13.wav \ + bnk450b.wav \ + cub3_3.wav \ + law4_9.wav \ + cok2_9.wav \ + col3_2a.wav \ + job2_9.wav \ + mob_26a.wav \ + cok3_2.wav \ + Cheer2.wav \ + rok3_3.wav \ + cub3_1.wav \ + law4_1a.wav \ + cub1_10.wav \ + mob_62a.wav \ + bust_23.wav \ + mob_43e.wav \ + mob_24f.wav \ + law1_6.wav \ + bust_15.wav \ + law2_4.wav \ + rok3_22.wav \ + rok3_73.wav \ + mob_18b.wav \ + gimme03.wav \ + mob_24d.wav \ + cok2_6.wav \ + col4_12.wav \ + cok2_21.wav \ + bnk3_3a.wav \ + cub4_7.wav \ + cap1_3.wav \ + cub2_11.wav \ + mob_63i.wav \ + tax3_1.wav \ + mob_14f.wav \ + mob_04a.wav \ + crust04.wav \ + cok4_15.wav \ + mob_52a.wav \ + rok3_65.wav \ + mob_09d.wav \ + col5_1.wav \ + job4_3.wav \ + psych_2.wav \ + rok1_1b.wav \ + mob_68a.wav \ + prn1_18.wav \ + AirHornL.wav \ + hot12.wav \ + mob_72a.wav \ + phil2_8.wav \ + law1_4.wav \ + piss_14.wav \ + law1_7.wav \ + mob_18c.wav \ + race15.wav \ + shark_1.wav \ + col3_8.wav \ + bk4_23a.wav \ + bnk4_39.wav \ + mob_30d.wav \ + tex1_4.wav \ + mob_43f.wav \ + tax1_2.wav \ + prn1_16.wav \ + cok2_8a.wav \ + bnk2_8.wav \ + mob_09f.wav \ + crust05.wav \ + mob_66b.wav \ + mob_40h.wav \ + mob_30c.wav \ + tex3_3.wav \ + cok2_1.wav \ + law1_5.wav \ + bud2_5.wav \ + mob_17j.wav \ + bnk3_2.wav \ + prn1_20.wav \ + mob_46h.wav \ + mob_16a.wav \ + shark_5.wav \ + mob_26c.wav \ + mob_20b.wav \ + cok2_7b.wav \ + rok1_7.wav \ + mob_11b.wav \ + cok2_20.wav \ + col1_7.wav \ + mob_47a.wav \ + col2_14.wav \ + gimme07.wav \ + mob_46d.wav \ + bnk2_5.wav \ + col5_4.wav \ + law3_21.wav \ + tax2_2.wav \ + bud3_1a.wav \ + col4_16.wav \ + mono15.wav \ + job1_1c.wav \ + STRIP_1.wav \ + mob_71b.wav \ + mob_55a.wav \ + rok1_8.wav \ + phil2_6.wav \ + mob_52g.wav \ + lanamu1.wav \ + porn1_5.wav \ + bust_22.wav \ + mob_46c.wav \ + bk4_40b.wav \ + mob_34c.wav \ + mob_35d.wav \ + bk4_20b.wav \ + bnk1_4.wav \ + col5_10.wav \ + fin_12c.wav \ + mob_17a.wav \ + tex3_7.wav \ + cok212b.wav \ + gimme12.wav \ + mob_71n.wav \ + col1_2.wav \ + porn1_3.wav \ + bnk4_44.wav \ + CarRev.wav \ + mono14.wav \ + fud_18.wav \ + mono_5.wav \ + fud_12.wav \ + mob_08e.wav \ + mob_08f.wav \ + rok3_7.wav \ + rok3_5.wav \ + cub4_11.wav \ + mob_29d.wav \ + mob_55b.wav \ + cok4_9a.wav \ + mob_16e.wav \ + rok3_13.wav \ + cnt2_2.wav \ + fud_02.wav \ + mob_63d.wav \ + rok3_12.wav \ + ass_7.wav \ + fud_08.wav \ + STRIP_6.wav \ + law2_8.wav \ + hot13.wav \ + fud_07.wav \ + gimme10.wav \ + CameraL.wav \ + bnk1_8.wav \ + col5_2.wav \ + mob_57b.wav \ + bust_27.wav \ + mob_16c.wav \ + bud2_6.wav \ + fud_01.wav \ + bud1_2.wav \ + golf_3.wav \ + InLift.wav \ + mob_17b.wav \ + burg_08.wav \ + band_06.wav \ + Cheer3.wav \ + cok4_26.wav \ + col4_7.wav \ + burg_11.wav \ + mob_41a.wav \ + mob_71d.wav \ + col4_3.wav \ + bust_03.wav \ + bnk4_3o.wav \ + mob_25a.wav \ + law3_4.wav \ + mob_73d.wav \ + mob_18d.wav \ + bnk3_3c.wav \ + col5_13.wav \ + BAR_8.wav \ + law4_5.wav \ + mob_05d.wav \ + mob_40i.wav \ + bnk4_27.wav \ + col3_5.wav \ + rok3_18.wav \ + mob_04b.wav \ + mob_04d.wav \ + bk4_23d.wav \ + fud_17.wav \ + hot10.wav \ + bnk4_98.wav \ + fin_1b.wav \ + cok1_1.wav \ + mob_71a.wav \ + bk4_12a.wav \ + mob_08g.wav \ + mob_11d.wav \ + law1_2.wav \ + resc_2.wav \ + bnk4_45.wav \ + law3_22.wav \ + col3_3.wav \ + col5_22.wav \ + mob_26b.wav \ + col4_4.wav \ + cok2_15.wav \ + rok3_26.wav \ + mob_63j.wav \ + col3_7.wav \ + mob_45h.wav \ + col5_21.wav \ + cok4_19.wav \ + porn3_2.wav \ + shaft03.wav \ + mob_45e.wav \ + bnk4_4b.wav \ + mob_35a.wav \ + law2_1.wav \ + bud3_8c.wav \ + rok3_14.wav \ + bnk4_3h.wav \ + burg_07.wav \ + col2_5.wav \ + bk4_24a.wav \ + col3_2b.wav \ + bnk4_26.wav \ + bust_12.wav \ + bnk4_30.wav \ + mob_43c.wav \ + bike1_2.wav \ + mob_40d.wav \ + rok3_62.wav \ + cnt2_3.wav \ + bnk4_36.wav \ + hot14.wav \ + resc_4.wav \ + bnk2_9.wav \ + col4_15.wav \ + mob_18f.wav \ + bnk4_43.wav \ + law3_19.wav \ + piss_16.wav \ + rok3_2.wav \ + law2_2.wav \ + job3_1.wav \ + cap1_2.wav \ + piss_11.wav \ + mob_55f.wav \ + col4_13.wav \ + mob_03d.wav \ + cok4_25.wav \ + rok1_9.wav \ + band_08.wav \ + mono_2.wav \ + cub2_10.wav \ + fud_05.wav \ + mob_20d.wav \ + bnk4_3e.wav \ + bnk4_10.wav \ + hot5.wav \ + mob_56b.wav \ + cok4_13.wav \ + ass_1.wav \ + BK4_40a.wav \ + bnk4_5.wav \ + bnk4_46.wav \ + porn1_2.wav \ + race5.wav \ + gimme02.wav \ + job4_1.wav \ + bust_09.wav \ + mob_45b.wav \ + law2_7.wav \ + bnk4_15.wav \ + mob_35b.wav \ + cub4_4.wav \ + col3_16.wav \ + law3_1.wav \ + mob_58f.wav \ + bjm1_4.wav \ + bnk1_13.wav \ + fud_15.wav \ + cok4_8.wav \ + bud3_5.wav \ + col5_17.wav \ + col4_11.wav \ + PHIL211.wav \ + cok1_3.wav \ + Cheer4.wav \ + mob_46g.wav \ + mob_08b.wav \ + burg_02.wav \ + mono_4.wav \ + mob_14g.wav \ + law3_13.wav \ + col2_4.wav \ + phil2_5.wav \ + ass_12.wav \ + prn1_17.wav \ + rok3_66.wav \ + mob_17k.wav \ + cok210c.wav \ + bud3_9c.wav \ + col4_6.wav \ + crust02.wav \ + col2_3.wav \ + intro3.wav \ + col4_8.wav \ + mob_72e.wav \ + bike1_1.wav \ + mob_41e.wav \ + mob_14d.wav \ + mob_17h.wav \ + cub4_15.wav \ + mob_52d.wav \ + mob_72b.wav \ + mob_42b.wav \ + col2_11.wav \ + piss_10.wav \ + col3_23.wav \ + col5_20.wav \ + mob_18e.wav \ + band_04.wav \ + mob_71k.wav \ + mob_07b.wav \ + bnk4_3c.wav \ + rok3_15.wav \ + ass_14.wav \ + bnk4_49.wav \ + piss_17.wav \ + mob_63a.wav \ + ass_5.wav \ + bud3_3.wav \ + piss_08.wav \ + law4_7.wav \ + bnk1_11.wav \ + bust_07.wav \ + cub4_3.wav \ + mob_71m.wav \ + LiftCl.wav \ + mob_17l.wav \ + shaft06.wav \ + cok2_8d.wav \ + SnipShort.wav \ + resc_6.wav \ + fud_06.wav \ + bnk4_47.wav \ + mob_43g.wav \ + piss_09.wav \ + bk4_12b.wav \ + mob_01b.wav \ + bud3_2.wav \ + fud_13.wav \ + burg_04.wav \ + mono16.wav \ + piss_01.wav \ + fin_6.wav \ + rok3_70.wav \ + mob_06a.wav \ + piss_19.wav \ + MobR1.wav \ + mob_72g.wav \ + mob_41c.wav \ + mob_45m.wav \ + tax3_2.wav \ + bnk4_3b.wav \ + cap1_6.wav \ + col3_12.wav \ + phil1_2.wav \ + bk4_23b.wav \ + mob_29g.wav \ + mob_41b.wav \ + mob_42d.wav \ + mob_41g.wav \ + bnk3_1.wav \ + bnk3_4c.wav \ + bust_08.wav \ + mob_56a.wav \ + shaft07.wav \ + col4_10.wav \ + prn1_3a.wav \ + col1_6.wav \ + mob_72c.wav \ + law4_6.wav \ + ass_13.wav \ + law3_6.wav \ + mob_30a.wav \ + cok4_12.wav \ + bnk1_6.wav \ + mob_40f.wav \ + law3_23.wav \ + rok3_68.wav \ + bk4_34b.wav \ + piss_02.wav \ + tex3_8.wav \ + phil210.wav \ + race2.wav \ + law3_24.wav \ + BAR_5.wav \ + col4_9.wav \ + porn1_7.wav \ + band_03.wav \ + law3_14.wav \ + cok212a.wav \ + bnk4_99.wav \ + cok2_16.wav \ + bust_28.wav \ + law1_10.wav \ + shaft08.wav \ + bnk4_94.wav \ + crust06.wav \ + bud2_7.wav \ + bnk4_3d.wav \ + mob_18g.wav \ + ass_4.wav \ + bnk450a.wav \ + law3_18.wav \ + mob_54b.wav \ + cnt1_2.wav \ + col3_21.wav \ + hat_1a.wav \ + mob_45n.wav \ + bnk4_97.wav \ + bnk422a.wav \ + bnk4_17.wav \ + phil2_9.wav \ + col4_25.wav \ + mob_08d.wav \ + mono_1.wav \ + law1_3.wav \ + tex3_1.wav \ + col4_5.wav \ + tax2_3.wav \ + SFX_01.wav \ + cok4_21.wav \ + cok4_9.wav \ + psych_1.wav \ + mob_58g.wav \ + gimme14.wav \ + law1_1.wav \ + resc_9.wav \ + bnk3_4a.wav \ + cok1_6.wav \ + cub2_6.wav \ + cok1_5.wav \ + mob_71j.wav \ + cok4_7.wav \ + job3_2.wav \ + col2_9.wav \ + col3_10.wav \ + mob_06b.wav \ + mob_33c.wav \ + mob_02b.wav \ + cap1_5.wav \ + bust_01.wav \ + col3_6.wav \ + bust_18.wav \ + bnk4_51.wav \ + col4_26.wav \ + cap1_12.wav \ + mob_54a.wav \ + bust_13.wav \ + prn1_10.wav \ + hot9.wav \ + mob_43b.wav \ + bust_25.wav \ + mob_57d.wav \ + tex1_2.wav \ + mob_14h.wav \ + gimme09.wav \ + bnk4_3g.wav \ + rok3_23.wav \ + bnk1_14.wav \ + resc_10.wav \ + fud_09.wav \ + job1_1b.wav \ + mob_02c.wav \ + cok4_14.wav \ + bnk4_41.wav \ + mob_17i.wav \ + cok1_2.wav \ + col2_10.wav \ + ass_6.wav \ + gimme08.wav \ + porn1_1.wav \ + mob_54d.wav \ + col3_2.wav \ + hot3.wav \ + job2_1b.wav \ + cok4_22.wav \ + cok2_3.wav \ + mob_57c.wav \ + mob_17c.wav \ + mob_70b.wav \ + mob_62d.wav \ + crust09.wav \ + job2_3.wav \ + porn3_4.wav \ + mob_46f.wav \ + cok4_11.wav \ + job2_2.wav \ + tax3_5.wav \ + col4_17.wav \ + law4_10.wav \ + cok4_20.wav \ + BK4_35b.wav \ + col2_15.wav \ + bud2_4.wav \ + mob_42e.wav \ + shaft04.wav \ + col2_7.wav \ + col5_9.wav \ + race3.wav \ + hot4.wav \ + lanstp1.wav \ + tax2_5.wav \ + mob_45j.wav \ + bud2_1.wav \ + STRIP_4.wav \ + mob_25d.wav \ + mob_16g.wav \ + col2_8.wav \ + BAR_4.wav \ + mob_03b.wav \ + race8.wav \ + bud3_4.wav \ + mob_43h.wav \ + cub4_16.wav \ + tex1_1.wav \ + job5_2.wav \ + fud_03.wav \ + mob_42c.wav \ + ass_2.wav \ + law4_1b.wav \ + star_2.wav \ + LiftRun.wav \ + col4_22.wav \ + piss_18.wav \ + cok2_8b.wav \ + col2_1.wav \ + cok4_24.wav \ + mob_61a.wav \ + mob_07a.wav \ + cok4_5.wav \ + cap1_7.wav \ + fin_12a.wav \ + shaft02.wav \ + rok3_11.wav \ + fud_20.wav \ + bnk1_3.wav \ + law3_20.wav \ + shaft01.wav \ + mob_14b.wav \ + bnk1_12.wav \ + bust_11.wav \ + bnk4_16.wav \ + cnt1_5.wav \ + mob_73g.wav \ + BK4_35a.wav \ + law3_12.wav \ + bust_19.wav \ + mob_26d.wav \ + col5_12.wav \ + bnk2_2.wav \ + cnt2_1.wav \ + cub2_4a.wav \ + law3_5.wav \ + mob_24c.wav \ + mob_33b.wav \ + cok2_7a.wav \ + cok4_1.wav \ + mob_98a.wav \ + cok2_5.wav \ + rok1_6.wav \ + mob_03c.wav \ + mob_25b.wav \ + rok3_6.wav \ + law3_3.wav \ + SFX_02.wav \ + mob_62c.wav \ + fud_11.wav \ + tax1_4.wav \ + law4_8.wav \ + porn1_4.wav \ + cub2_8.wav \ + cok3_1.wav \ + OOH1.wav \ + mob_43d.wav \ + mono_9.wav \ + piss_05.wav \ + mono10.wav \ + mob_45a.wav \ + mob_24g.wav \ + piss_07.wav \ + bud3_1.wav \ + mob_18a.wav \ + bud2_2.wav \ + mob_33d.wav \ + mob_52f.wav \ + resc_3.wav \ + bnk4_3u.wav \ + law3_17.wav \ + hot6.wav \ + STRIP_9.wav \ + mob_29f.wav \ + cap1_9.wav \ + bud3_7.wav \ + mono13.wav \ + mono_6.wav \ + job5_1.wav \ + bust_06.wav \ + gimme01.wav \ + mono12.wav \ + mob_01c.wav \ + mob_52e.wav \ + crust01.wav \ + shark_2.wav \ + cok4_16.wav \ + rok3_20.wav \ + BAR_1.wav \ + mob_08c.wav \ + bust_20.wav \ + mob_17d.wav \ + bust_04.wav \ + cub2_1.wav \ + ass_3.wav \ + col3_1.wav \ + fin_12b.wav \ + BlowRoof.wav \ + col4_23.wav \ + mob_16f.wav \ + bnk2_6.wav \ + mob_55e.wav \ + rok3_24.wav \ + mob_09b.wav \ + tex3_6.wav \ + cok3_4.wav \ + pager.wav \ + fin_11a.wav \ + col5_16.wav \ + porn3_1.wav \ + mob_56f.wav \ + mob_10c.wav \ + mob_24h.wav \ + mob_58e.wav \ + fin_1a.wav \ + BAR_7.wav \ + cub4_5a.wav \ + tax2_6.wav \ + phil2_3.wav \ + mob_45i.wav \ + porn1_6.wav \ + bk4_14b.wav \ + intro2.wav \ + fin_4.wav \ + law4_1c.wav \ + rok1_1a.wav \ + cub2_2.wav \ + burg_03.wav \ + bnk4_3j.wav \ + rok3_17.wav \ + bk4_20a.wav \ + gimme06.wav \ + cap1_11.wav \ + bk4_12c.wav \ + mob_46a.wav \ + bnk4_3r.wav \ + bnk4_3a.wav \ + bjm1_20.wav \ + bnk4_96.wav \ No newline at end of file diff --git a/premake-vs2015.cmd b/premake-vs2015.cmd deleted file mode 100644 index fc1bd295..00000000 --- a/premake-vs2015.cmd +++ /dev/null @@ -1 +0,0 @@ -premake5 vs2015 --with-librw diff --git a/premake-vs2017.cmd b/premake-vs2017.cmd deleted file mode 100644 index f3562da6..00000000 --- a/premake-vs2017.cmd +++ /dev/null @@ -1 +0,0 @@ -premake5 vs2017 --with-librw diff --git a/premake-vs2019.cmd b/premake-vs2019.cmd deleted file mode 100644 index 9971831a..00000000 --- a/premake-vs2019.cmd +++ /dev/null @@ -1 +0,0 @@ -premake5 vs2019 --with-librw diff --git a/premake5.exe b/premake5.exe deleted file mode 100644 index a8483721..00000000 Binary files a/premake5.exe and /dev/null differ diff --git a/premake5.lua b/premake5.lua deleted file mode 100644 index ebbcf4a5..00000000 --- a/premake5.lua +++ /dev/null @@ -1,477 +0,0 @@ -newoption { - trigger = "glfwdir64", - value = "PATH", - description = "Directory of glfw", - default = "vendor/glfw-3.3.2.bin.WIN64", -} - -newoption { - trigger = "glfwdir32", - value = "PATH", - description = "Directory of glfw", - default = "vendor/glfw-3.3.2.bin.WIN32", -} - -newoption { - trigger = "with-asan", - description = "Build with address sanitizer" -} - -newoption { - trigger = "with-librw", - description = "Build and use librw from this solution" -} - -newoption { - trigger = "with-opus", - description = "Build with opus" -} - -newoption { - trigger = "with-lto", - description = "Build with link time optimization" -} - -newoption { - trigger = "no-git-hash", - description = "Don't print git commit hash into binary" -} - -newoption { - trigger = "no-full-paths", - description = "Don't print full paths into binary" -} - -require("autoconf") - -if(_OPTIONS["with-librw"]) then - Librw = "vendor/librw" -else - Librw = os.getenv("LIBRW") or "vendor/librw" -end - -function getsys(a) - if a == 'windows' then - return 'win' - end - return a -end - -function getarch(a) - if a == 'x86_64' then - return 'amd64' - elseif a == 'ARM' then - return 'arm' - elseif a == 'ARM64' then - return 'arm64' - end - return a -end - -workspace "re3" - language "C++" - configurations { "Debug", "Release" } - startproject "re3" - location "build" - symbols "Full" - staticruntime "off" - - if _OPTIONS["with-asan"] then - buildoptions { "-fsanitize=address -g3 -fno-omit-frame-pointer" } - linkoptions { "-fsanitize=address" } - end - - filter { "system:windows" } - configurations { "Vanilla" } - platforms { - "win-x86-RW33_d3d8-mss", - "win-x86-librw_d3d9-mss", - "win-x86-librw_gl3_glfw-mss", - "win-x86-RW33_d3d8-oal", - "win-x86-librw_d3d9-oal", - "win-x86-librw_gl3_glfw-oal", - "win-amd64-librw_d3d9-oal", - "win-amd64-librw_gl3_glfw-oal", - } - - filter { "system:linux" } - platforms { - "linux-x86-librw_gl3_glfw-oal", - "linux-amd64-librw_gl3_glfw-oal", - "linux-arm-librw_gl3_glfw-oal", - "linux-arm64-librw_gl3_glfw-oal", - } - - filter { "system:bsd" } - platforms { - "bsd-x86-librw_gl3_glfw-oal", - "bsd-amd64-librw_gl3_glfw-oal", - "bsd-arm-librw_gl3_glfw-oal", - "bsd-arm64-librw_gl3_glfw-oal" - } - - filter { "system:macosx" } - platforms { - "macosx-arm64-librw_gl3_glfw-oal", - "macosx-amd64-librw_gl3_glfw-oal", - } - - filter "configurations:Debug" - defines { "DEBUG" } - - filter "configurations:not Debug" - defines { "NDEBUG" } - optimize "Speed" - if(_OPTIONS["with-lto"]) then - flags { "LinkTimeOptimization" } - end - - filter { "platforms:win*" } - system "windows" - - filter { "platforms:linux*" } - system "linux" - - filter { "platforms:bsd*" } - system "bsd" - - filter { "platforms:macosx*" } - system "macosx" - - filter { "platforms:*x86*" } - architecture "x86" - - filter { "platforms:*amd64*" } - architecture "amd64" - - filter { "platforms:*arm*" } - architecture "ARM" - - filter { "platforms:macosx-arm64-*", "files:**.cpp"} - buildoptions { "-target", "arm64-apple-macos11", "-std=gnu++14" } - - filter { "platforms:macosx-arm64-*", "files:**.c"} - buildoptions { "-target", "arm64-apple-macos11" } - - filter { "platforms:macosx-amd64-*", "files:**.cpp"} - buildoptions { "-target", "x86_64-apple-macos10.12", "-std=gnu++14" } - - filter { "platforms:macosx-amd64-*", "files:**.c"} - buildoptions { "-target", "x86_64-apple-macos10.12" } - - filter { "platforms:*librw_d3d9*" } - defines { "RW_D3D9" } - if(not _OPTIONS["with-librw"]) then - libdirs { path.join(Librw, "lib/win-%{getarch(cfg.architecture)}-d3d9/%{cfg.buildcfg}") } - end - - filter "platforms:*librw_gl3_glfw*" - defines { "RW_GL3" } - if(not _OPTIONS["with-librw"]) then - libdirs { path.join(Librw, "lib/%{getsys(cfg.system)}-%{getarch(cfg.architecture)}-gl3/%{cfg.buildcfg}") } - end - - filter "platforms:*x86-librw_gl3_glfw*" - includedirs { path.join(_OPTIONS["glfwdir32"], "include") } - - filter "platforms:*amd64-librw_gl3_glfw*" - includedirs { path.join(_OPTIONS["glfwdir64"], "include") } - - filter {} - - function setpaths (gamepath, exepath) - if (gamepath) then - postbuildcommands { - '{COPYFILE} "%{cfg.buildtarget.abspath}" "' .. gamepath .. '%{cfg.buildtarget.name}"' - } - debugdir (gamepath) - if (exepath) then - -- Used VS variable $(TargetFileName) because it doesn't accept premake tokens. Does debugcommand even work outside VS?? - debugcommand (gamepath .. "$(TargetFileName)") - dir, file = exepath:match'(.*/)(.*)' - debugdir (gamepath .. (dir or "")) - end - end - end - -if(_OPTIONS["with-librw"]) then -project "librw" - kind "StaticLib" - targetname "rw" - targetdir(path.join(Librw, "lib/%{cfg.platform}/%{cfg.buildcfg}")) - files { path.join(Librw, "src/*.*") } - files { path.join(Librw, "src/*/*.*") } - files { path.join(Librw, "src/gl/*/*.*") } - - filter { "platforms:*x86*" } - architecture "x86" - - filter { "platforms:*amd64*" } - architecture "amd64" - - filter "platforms:win*" - defines { "_CRT_SECURE_NO_WARNINGS", "_CRT_NONSTDC_NO_DEPRECATE" } - staticruntime "on" - buildoptions { "/Zc:sizedDealloc-" } - - filter "platforms:bsd*" - includedirs { "/usr/local/include" } - libdirs { "/usr/local/lib" } - - -- Support MacPorts and Homebrew - filter "platforms:macosx-arm64-*" - includedirs { "/opt/local/include" } - includedirs {"/opt/homebrew/include" } - libdirs { "/opt/local/lib" } - libdirs { "/opt/homebrew/lib" } - - filter "platforms:macosx-amd64-*" - includedirs { "/opt/local/include" } - includedirs {"/usr/local/include" } - libdirs { "/opt/local/lib" } - libdirs { "/usr/local/lib" } - - filter "platforms:*gl3_glfw*" - staticruntime "off" - - filter "platforms:*RW33*" - flags { "ExcludeFromBuild" } - filter {} -end - -local function addSrcFiles( prefix ) - return prefix .. "/*cpp", prefix .. "/*.h", prefix .. "/*.c", prefix .. "/*.ico", prefix .. "/*.rc" -end - -project "re3" - kind "WindowedApp" - targetname "re3" - targetdir "bin/%{cfg.platform}/%{cfg.buildcfg}" - - if(_OPTIONS["with-librw"]) then - dependson "librw" - end - - files { addSrcFiles("src") } - files { addSrcFiles("src/animation") } - files { addSrcFiles("src/audio") } - files { addSrcFiles("src/audio/eax") } - files { addSrcFiles("src/audio/oal") } - files { addSrcFiles("src/buildings") } - files { addSrcFiles("src/collision") } - files { addSrcFiles("src/control") } - files { addSrcFiles("src/core") } - files { addSrcFiles("src/entities") } - files { addSrcFiles("src/math") } - files { addSrcFiles("src/modelinfo") } - files { addSrcFiles("src/objects") } - files { addSrcFiles("src/peds") } - files { addSrcFiles("src/renderer") } - files { addSrcFiles("src/rw") } - files { addSrcFiles("src/save") } - files { addSrcFiles("src/skel") } - files { addSrcFiles("src/skel/glfw") } - files { addSrcFiles("src/text") } - files { addSrcFiles("src/vehicles") } - files { addSrcFiles("src/weapons") } - files { addSrcFiles("src/extras") } - if(not _OPTIONS["no-git-hash"]) then - files { "src/extras/GitSHA1.cpp" } -- this won't be in repo in first build - else - removefiles { "src/extras/GitSHA1.cpp" } -- but it will be everytime after - end - - includedirs { "src" } - includedirs { "src/animation" } - includedirs { "src/audio" } - includedirs { "src/audio/eax" } - includedirs { "src/audio/oal" } - includedirs { "src/buildings" } - includedirs { "src/collision" } - includedirs { "src/control" } - includedirs { "src/core" } - includedirs { "src/entities" } - includedirs { "src/math" } - includedirs { "src/modelinfo" } - includedirs { "src/objects" } - includedirs { "src/peds" } - includedirs { "src/renderer" } - includedirs { "src/rw" } - includedirs { "src/save/" } - includedirs { "src/skel/" } - includedirs { "src/skel/glfw" } - includedirs { "src/text" } - includedirs { "src/vehicles" } - includedirs { "src/weapons" } - includedirs { "src/extras" } - - if(not _OPTIONS["no-git-hash"]) then - defines { "USE_OUR_VERSIONING" } - end - - if _OPTIONS["with-opus"] then - includedirs { "vendor/ogg/include" } - includedirs { "vendor/opus/include" } - includedirs { "vendor/opusfile/include" } - end - - filter "configurations:Vanilla" - defines { "VANILLA_DEFINES" } - - filter "platforms:*mss" - defines { "AUDIO_MSS" } - includedirs { "vendor/milessdk/include" } - libdirs { "vendor/milessdk/lib" } - - if _OPTIONS["with-opus"] then - filter "platforms:win*" - libdirs { "vendor/ogg/win32/VS2015/Win32/%{cfg.buildcfg}" } - libdirs { "vendor/opus/win32/VS2015/Win32/%{cfg.buildcfg}" } - libdirs { "vendor/opusfile/win32/VS2015/Win32/Release-NoHTTP" } - filter {} - defines { "AUDIO_OPUS" } - end - - filter "platforms:*oal" - defines { "AUDIO_OAL" } - - filter {} - if(os.getenv("GTA_III_RE_DIR")) then - setpaths(os.getenv("GTA_III_RE_DIR") .. "/", "%(cfg.buildtarget.name)") - end - - filter "platforms:win*" - files { addSrcFiles("src/skel/win") } - includedirs { "src/skel/win" } - buildoptions { "/Zc:sizedDealloc-" } - linkoptions "/SAFESEH:NO" - characterset ("MBCS") - targetextension ".exe" - if(_OPTIONS["no-full-paths"]) then - usefullpaths "off" - linkoptions "/PDBALTPATH:%_PDB%" - end - if(_OPTIONS["with-librw"]) then - -- external librw is dynamic - staticruntime "on" - end - if(not _OPTIONS["no-git-hash"]) then - prebuildcommands { '"%{prj.location}..\\printHash.bat" "%{prj.location}..\\src\\extras\\GitSHA1.cpp"' } - end - - filter "platforms:not win*" - if(not _OPTIONS["no-git-hash"]) then - prebuildcommands { '"%{prj.location}/../printHash.sh" "%{prj.location}/../src/extras/GitSHA1.cpp"' } - end - - filter "platforms:win*glfw*" - staticruntime "off" - - filter "platforms:*glfw*" - premake.modules.autoconf.parameters = "-lglfw -lX11" - autoconfigure { - -- iterates all configs and runs on them - ["dontWrite"] = function (cfg) - check_symbol_exists(cfg, "haveX11", "glfwGetX11Display", { "X11/Xlib.h", "X11/XKBlib.h", "GLFW/glfw3.h", "GLFW/glfw3native.h" }, "GLFW_EXPOSE_NATIVE_X11") - if cfg.autoconf["haveX11"] ~= nil and cfg.autoconf["haveX11"] == 1 then - table.insert(cfg.links, "X11") - table.insert(cfg.defines, "GET_KEYBOARD_INPUT_FROM_X11") - end - end - } - - filter "platforms:win*oal" - includedirs { "vendor/openal-soft/include" } - includedirs { "vendor/libsndfile/include" } - includedirs { "vendor/mpg123/include" } - - filter "platforms:win-x86*oal" - libdirs { "vendor/mpg123/lib/Win32" } - libdirs { "vendor/libsndfile/lib/Win32" } - libdirs { "vendor/openal-soft/libs/Win32" } - - filter "platforms:win-amd64*oal" - libdirs { "vendor/mpg123/lib/Win64" } - libdirs { "vendor/libsndfile/lib/Win64" } - libdirs { "vendor/openal-soft/libs/Win64" } - - filter "platforms:linux*oal" - links { "openal", "mpg123", "sndfile", "pthread" } - - filter "platforms:bsd*oal" - links { "openal", "mpg123", "sndfile", "pthread" } - - filter "platforms:macosx*oal" - links { "openal", "mpg123", "sndfile", "pthread" } - - filter "platforms:macosx-arm64-*oal" - includedirs { "/opt/homebrew/opt/openal-soft/include" } - libdirs { "/opt/homebrew/opt/openal-soft/lib" } - - filter "platforms:macosx-amd64-*oal" - includedirs { "/usr/local/opt/openal-soft/include" } - libdirs { "/usr/local/opt/openal-soft/lib" } - - if _OPTIONS["with-opus"] then - filter {} - links { "libogg" } - links { "opus" } - links { "opusfile" } - end - - filter "platforms:*RW33*" - includedirs { "sdk/rwsdk/include/d3d8" } - libdirs { "sdk/rwsdk/lib/d3d8/release" } - links { "rwcore", "rpworld", "rpmatfx", "rpskin", "rphanim", "rtbmp", "rtquat", "rtcharse", "rpanisot" } - defines { "RWLIBS" } - linkoptions "/SECTION:_rwcseg,ER!W /MERGE:_rwcseg=.text" - - filter "platforms:*librw*" - defines { "LIBRW" } - files { addSrcFiles("src/fakerw") } - includedirs { "src/fakerw" } - includedirs { Librw } - if(_OPTIONS["with-librw"]) then - libdirs { "vendor/librw/lib/%{cfg.platform}/%{cfg.buildcfg}" } - end - links { "rw" } - - filter "platforms:*d3d9*" - defines { "USE_D3D9" } - links { "d3d9" } - - filter "platforms:*x86*d3d*" - includedirs { "sdk/dx8sdk/include" } - libdirs { "sdk/dx8sdk/lib" } - - filter "platforms:win-x86*gl3_glfw*" - libdirs { path.join(_OPTIONS["glfwdir32"], "lib-" .. string.gsub(_ACTION or '', "vs", "vc")) } - links { "opengl32", "glfw3" } - - filter "platforms:win-amd64*gl3_glfw*" - libdirs { path.join(_OPTIONS["glfwdir64"], "lib-" .. string.gsub(_ACTION or '', "vs", "vc")) } - links { "opengl32", "glfw3" } - - filter "platforms:linux*gl3_glfw*" - links { "GL", "glfw" } - - filter "platforms:bsd*gl3_glfw*" - links { "GL", "glfw", "sysinfo" } - includedirs { "/usr/local/include" } - libdirs { "/usr/local/lib" } - - filter "platforms:macosx-arm64-*gl3_glfw*" - links { "glfw" } - linkoptions { "-framework OpenGL" } - includedirs { "/opt/local/include" } - includedirs {"/opt/homebrew/include" } - libdirs { "/opt/local/lib" } - libdirs { "/opt/homebrew/lib" } - - filter "platforms:macosx-amd64-*gl3_glfw*" - links { "glfw" } - linkoptions { "-framework OpenGL" } - includedirs { "/opt/local/include" } - includedirs {"/usr/local/include" } - libdirs { "/opt/local/lib" } - libdirs { "/usr/local/lib" } diff --git a/premake5Linux b/premake5Linux deleted file mode 100755 index 1ca75167..00000000 Binary files a/premake5Linux and /dev/null differ diff --git a/printHash.bat b/printHash.bat deleted file mode 100644 index ef1cd9d6..00000000 --- a/printHash.bat +++ /dev/null @@ -1,26 +0,0 @@ -@echo off - -REM creates version.h with HEAD commit hash -REM params: $1=full path to output file (usually points version.h) - -setlocal enableextensions enabledelayedexpansion - -cd /d "%~dp0" - -break> %1 - - %1 - -where git -if "%errorlevel%" == "0" ( goto :havegit ) else ( goto :writeending ) - -:havegit -for /f %%v in ('git rev-parse --short HEAD') do set version=%%v -> %1 - -:writeending - -echo ^" >> %1 -echo const char* g_GIT_SHA1 = GIT_SHA1; >> %1 - -EXIT /B \ No newline at end of file diff --git a/printHash.sh b/printHash.sh deleted file mode 100755 index 213d9353..00000000 --- a/printHash.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env sh -if [ -z "${1}" ] - then - printf "%s\n" "Input the path to the file for writing the commit hash to." - else - printf "%s" "#define GIT_SHA1 \"" > $1 - - if (command -v "git" >/dev/null) then - git rev-parse --short HEAD | tr -d '\n' >> $1 - fi - - printf "%s\n" "\"" >> $1 - printf "%s\n" "const char* g_GIT_SHA1 = GIT_SHA1;" >> $1 -fi diff --git a/public/index.html b/public/index.html deleted file mode 100644 index bb91a4cd..00000000 --- a/public/index.html +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - DCA3 - A port of RE3 for Dreamcast - - - - - - - - - -
-
-

DCA3

-

The first-ever port of Grand Theft Auto III to the SEGA Dreamcast, built from the REGTA reverse engineering project.

- Download Now -
-
- - -
-

Key Features

-
-
-

Faithful Port

-

Experience GTA3 as it was meant to be, now on the Dreamcast with okay visuals and sluggish gameplay.

-
-
-

Open Source

-

Built on the REGTA reverse engineering project, ensuring accuracy and community-driven development.

-
-
-

First Release

-

Celebrating our first release with not very much optimized performance and compatibility for Dreamcast hardware.

-
-
-
- - -
-

Download DCA3

-

Get started with the first release of and bring GTA3 to your Dreamcast. You'll need a PC copy of GTA3 to create your .cdi

- Download Version 1.0 -
- - -
-

No rights reserved. Made with love for the Dreamcast community.

-
- - - - - diff --git a/src/animation/AnimBlendNode.cpp b/src/animation/AnimBlendNode.cpp deleted file mode 100644 index 8c3a6bc7..00000000 --- a/src/animation/AnimBlendNode.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include "common.h" - -#include "AnimBlendAssociation.h" -#include "AnimBlendNode.h" - -void -CAnimBlendNode::Init(void) -{ - frameA = -1; - frameB = -1; - remainingTime = 0.0f; - sequence = nil; - association = nil; -} - -bool -CAnimBlendNode::Update(CVector &trans, CQuaternion &rot, float weight) -{ - bool looped = false; - - trans = CVector(0.0f, 0.0f, 0.0f); - rot = CQuaternion(0.0f, 0.0f, 0.0f, 0.0f); - - if(association->IsRunning()){ - remainingTime -= association->timeStep; - if(remainingTime <= 0.0f) - looped = NextKeyFrame(); - } - - float blend = association->GetBlendAmount(weight); - if(blend > 0.0f){ - float kfAdt = sequence->GetDeltaTime(frameA); - float t = kfAdt == 0.0f ? 0.0f : (kfAdt - remainingTime)/kfAdt; - if(sequence->type & CAnimBlendSequence::KF_TRANS){ - auto kfAt = sequence->GetTranslation(frameA); - auto kfBt = sequence->GetTranslation(frameB); - trans = kfBt + t*(kfAt - kfBt); - trans *= blend; - } - if(sequence->type & CAnimBlendSequence::KF_ROT){ - auto kfAr = sequence->GetRotation(frameA); - auto kfBr = sequence->GetRotation(frameB); - rot.Slerp(kfBr, kfAr, theta, invSin, t); - rot *= blend; - } - } - - return looped; -} - -bool -CAnimBlendNode::NextKeyFrame(void) -{ - bool looped; - - if(sequence->numFrames <= 1) - return false; - - looped = false; - frameB = frameA; - - // Advance as long as we have to - while(remainingTime <= 0.0f){ - frameA++; - - if(frameA >= sequence->numFrames){ - // reached end of animation - if(!association->IsRepeating()){ - frameA--; - remainingTime = 0.0f; - return false; - } - looped = true; - frameA = 0; - } - - remainingTime += sequence->GetDeltaTime(frameA); - } - - frameB = frameA - 1; - if(frameB < 0) - frameB += sequence->numFrames; - - CalcDeltas(); - return looped; -} - -// Set animation to time t -bool -CAnimBlendNode::FindKeyFrame(float t) -{ - if(sequence->numFrames < 1) - return false; - - frameA = 0; - frameB = frameA; - - if(sequence->numFrames >= 2){ - frameA++; - - // advance until t is between frameB and frameA - while(t > sequence->GetDeltaTime(frameA)){ - t -= sequence->GetDeltaTime(frameA); - frameB = frameA++; - if(frameA >= sequence->numFrames){ - // reached end of animation - if(!association->IsRepeating()) - return false; - frameA = 0; - frameB = 0; - } - } - - remainingTime = sequence->GetDeltaTime(frameA) - t; - } - - CalcDeltas(); - return true; -} - -void -CAnimBlendNode::CalcDeltas(void) -{ - if((sequence->type & CAnimBlendSequence::KF_ROT) == 0) - return; - auto kfAr = sequence->GetRotation(frameA); - auto kfBr = sequence->GetRotation(frameB); - float cos = DotProduct(kfAr, kfBr); - if(cos > 1.0f) - cos = 1.0f; - theta = Acos(cos); - invSin = theta == 0.0f ? 0.0f : 1.0f/Sin(theta); -} - -void -CAnimBlendNode::GetCurrentTranslation(CVector &trans, float weight) -{ - trans = CVector(0.0f, 0.0f, 0.0f); - - float blend = association->GetBlendAmount(weight); - if(blend > 0.0f){ - auto kfAdt = sequence->GetDeltaTime(frameA); - float t = (kfAdt - remainingTime)/kfAdt; - if(sequence->type & CAnimBlendSequence::KF_TRANS){ - auto kfAt = sequence->GetTranslation(frameA); - auto kfBt = sequence->GetTranslation(frameB); - trans = kfBt + t*(kfAt - kfBt); - trans *= blend; - } - } -} - -void -CAnimBlendNode::GetEndTranslation(CVector &trans, float weight) -{ - trans = CVector(0.0f, 0.0f, 0.0f); - - float blend = association->GetBlendAmount(weight); - if(blend > 0.0f){ - if(sequence->type & CAnimBlendSequence::KF_TRANS) - trans = sequence->GetTranslation(sequence->numFrames-1) * blend; - } -} diff --git a/src/animation/AnimBlendSequence.h b/src/animation/AnimBlendSequence.h deleted file mode 100644 index bedc85ee..00000000 --- a/src/animation/AnimBlendSequence.h +++ /dev/null @@ -1,166 +0,0 @@ -#pragma once - -#include "Quaternion.h" - -#ifdef MoveMemory -#undef MoveMemory // windows shit -#endif - -// TODO: put them somewhere else? -static int16 checked_f2i16(float f) { - assert(f >= -32768 && f <= 32767); - return f; -} - -static uint16 checked_f2u16(float f) { - assert(f >= 0 && f <= 65535); - return f; -} - -#define KF_MINDELTA (1/256.f) - -struct KeyFrame { - int16 rot[4]; // 4096 - uint16 dltTime; // 256 - - CQuaternion rotation_() { - return { rot[0] * (1/4096.f), rot[1] * (1/4096.f), rot[2] * (1/4096.f), rot[3] * (1/4096.f) }; - } - - void rotation_(const CQuaternion& q) { - rot[0] = checked_f2i16(q.x * 4096.0f); - rot[1] = checked_f2i16(q.y * 4096.0f); - rot[2] = checked_f2i16(q.z * 4096.0f); - rot[3] = checked_f2i16(q.w * 4096.0f); - } - - float deltaTime_() { - return dltTime * (1/256.0f); - } - - void deltaTime_(float t) { - dltTime = checked_f2u16(t * 256); // always round down - } -}; - -struct KeyFrameTransUncompressed : KeyFrame { - // Some animations use bigger range, eg during the intro - CVector trans; - CVector translation_() { - return trans; - } - - void translation_(const CVector &v) { - trans = v; - } -}; - -struct KeyFrameTransCompressed : KeyFrame { - int16 trans[3]; // 128 - - CVector translation_() { - return { trans[0] * (1/128.f), trans[1] * (1/128.f), trans[2] * (1/128.f)}; - } - - void translation_(const CVector &v) { - trans[0] = checked_f2i16(v.x * 128.f); - trans[1] = checked_f2i16(v.y * 128.f); - trans[2] = checked_f2i16(v.z * 128.f); - } -}; - - -// The sequence of key frames of one animated node -class CAnimBlendSequence -{ -public: - enum { - KF_ROT = 1, - KF_TRANS = 2, - KF_COMPRESSED = 4, // only applicable for KF_TRANS - }; - int32 type; - char name[24]; - int32 numFrames; -#ifdef PED_SKIN - int16 boneTag; -#endif - void *keyFrames; - - CAnimBlendSequence(void); - virtual ~CAnimBlendSequence(void); - void SetName(char *name); - void SetNumFrames(int numFrames, bool translation, bool compress); - void RemoveQuaternionFlips(void); - - - void SetTranslation(int n, const CVector &v) { - if (type & KF_COMPRESSED) { - ((KeyFrameTransCompressed*)keyFrames)[n].translation_(v); - } else if (type & KF_TRANS) { - ((KeyFrameTransUncompressed*)keyFrames)[n].translation_(v); - } else { - assert(false && "SetTranslation called on sequence without translation"); - } - } - - CVector GetTranslation(int n) { - if (type & KF_COMPRESSED) { - return ((KeyFrameTransCompressed*)keyFrames)[n].translation_(); - } else if (type & KF_TRANS) { - return ((KeyFrameTransUncompressed*)keyFrames)[n].translation_(); - } else { - assert(false && "GetTranslation called on sequence without translation"); - } - } - - void SetRotation(int n, const CQuaternion &q) { - if (type & KF_COMPRESSED) { - ((KeyFrameTransCompressed*)keyFrames)[n].rotation_(q); - } else if (type & KF_TRANS) { - ((KeyFrameTransUncompressed*)keyFrames)[n].rotation_(q); - } else { - ((KeyFrame*)keyFrames)[n].rotation_(q); - } - } - - CQuaternion GetRotation(int n) { - if (type & KF_COMPRESSED) { - return ((KeyFrameTransCompressed*)keyFrames)[n].rotation_(); - } else if (type & KF_TRANS) { - return ((KeyFrameTransUncompressed*)keyFrames)[n].rotation_(); - } else { - return ((KeyFrame*)keyFrames)[n].rotation_(); - } - } - - void SetDeltaTime(int n, float t) { - if (type & KF_COMPRESSED) { - ((KeyFrameTransCompressed*)keyFrames)[n].deltaTime_(t); - } else if (type & KF_TRANS) { - ((KeyFrameTransUncompressed*)keyFrames)[n].deltaTime_(t); - } else { - ((KeyFrame*)keyFrames)[n].deltaTime_(t); - } - } - - float GetDeltaTime(int n) { - if (type & KF_COMPRESSED) { - return ((KeyFrameTransCompressed*)keyFrames)[n].deltaTime_(); - } else if (type & KF_TRANS) { - return ((KeyFrameTransUncompressed*)keyFrames)[n].deltaTime_(); - } else { - return ((KeyFrame*)keyFrames)[n].deltaTime_(); - } - } - - bool HasTranslation(void) { return !!(type & KF_TRANS); } - bool MoveMemory(void); - -#ifdef PED_SKIN - void SetBoneTag(int tag) { boneTag = tag; } -#endif -}; -#ifndef PED_SKIN -VALIDATE_SIZE(CAnimBlendSequence, 0x2C); -#endif diff --git a/src/prof/profiler.cpp b/src/common/prof/profiler.cpp similarity index 100% rename from src/prof/profiler.cpp rename to src/common/prof/profiler.cpp diff --git a/src/prof/profiler.h b/src/common/prof/profiler.h similarity index 100% rename from src/prof/profiler.h rename to src/common/prof/profiler.h diff --git a/src/vmu/vmu.cpp b/src/common/vmu/vmu.cpp similarity index 98% rename from src/vmu/vmu.cpp rename to src/common/vmu/vmu.cpp index e063012c..fb83c5cb 100644 --- a/src/vmu/vmu.cpp +++ b/src/common/vmu/vmu.cpp @@ -10,7 +10,7 @@ # include # endif -extern bool _dcAudioInitialized; +extern bool _bSampmanInitialised; // ====== STATIC METHODS ===== @@ -86,7 +86,7 @@ void VmuProfiler::run() { #ifdef DC_SH4 if(auto *dev = maple_enum_type(0, MAPLE_FUNC_MEMCARD); - dev && _dcAudioInitialized && updated_) + dev && _bSampmanInitialised && updated_) { pvr_stats_t pvrStats; pvr_get_stats(&pvrStats); uint32_t sramStats = snd_mem_available(); diff --git a/src/vmu/vmu.h b/src/common/vmu/vmu.h similarity index 100% rename from src/vmu/vmu.h rename to src/common/vmu/vmu.h diff --git a/src/CMakeLists.txt b/src/liberty/CMakeLists.txt similarity index 100% rename from src/CMakeLists.txt rename to src/liberty/CMakeLists.txt diff --git a/src/animation/AnimBlendAssocGroup.cpp b/src/liberty/animation/AnimBlendAssocGroup.cpp similarity index 100% rename from src/animation/AnimBlendAssocGroup.cpp rename to src/liberty/animation/AnimBlendAssocGroup.cpp diff --git a/src/animation/AnimBlendAssocGroup.h b/src/liberty/animation/AnimBlendAssocGroup.h similarity index 100% rename from src/animation/AnimBlendAssocGroup.h rename to src/liberty/animation/AnimBlendAssocGroup.h diff --git a/src/animation/AnimBlendAssociation.cpp b/src/liberty/animation/AnimBlendAssociation.cpp similarity index 98% rename from src/animation/AnimBlendAssociation.cpp rename to src/liberty/animation/AnimBlendAssociation.cpp index fcaa84fa..f1b5face 100644 --- a/src/animation/AnimBlendAssociation.cpp +++ b/src/liberty/animation/AnimBlendAssociation.cpp @@ -55,6 +55,8 @@ void CAnimBlendAssociation::FreeAnimBlendNodeArray(void) { assert(nodes != nil); + for(unsigned i = 0; i < numNodes; i++) + nodes[i].Destroy(); RwFreeAlign(nodes); } diff --git a/src/animation/AnimBlendAssociation.h b/src/liberty/animation/AnimBlendAssociation.h similarity index 100% rename from src/animation/AnimBlendAssociation.h rename to src/liberty/animation/AnimBlendAssociation.h diff --git a/src/animation/AnimBlendClumpData.cpp b/src/liberty/animation/AnimBlendClumpData.cpp similarity index 100% rename from src/animation/AnimBlendClumpData.cpp rename to src/liberty/animation/AnimBlendClumpData.cpp diff --git a/src/animation/AnimBlendClumpData.h b/src/liberty/animation/AnimBlendClumpData.h similarity index 100% rename from src/animation/AnimBlendClumpData.h rename to src/liberty/animation/AnimBlendClumpData.h diff --git a/src/liberty/animation/AnimBlendHierarchy.cpp b/src/liberty/animation/AnimBlendHierarchy.cpp new file mode 100644 index 00000000..462c387a --- /dev/null +++ b/src/liberty/animation/AnimBlendHierarchy.cpp @@ -0,0 +1,57 @@ +#include "common.h" + +#include "AnimBlendSequence.h" +#include "AnimBlendHierarchy.h" + +CAnimBlendHierarchy::CAnimBlendHierarchy(void) +{ + sequences = nil; + numSequences = 0; + totalLength = 0.0f; +} + +void +CAnimBlendHierarchy::Shutdown(void) +{ + RemoveAnimSequences(); +} + +void +CAnimBlendHierarchy::SetName(char *name) +{ + strncpy(this->name, name, 24); +} + +void +CAnimBlendHierarchy::CalcTotalTime(void) +{ + int i, j; + totalLength = 0.0f; + + for(i = 0; i < numSequences; i++){ +#ifdef FIX_BUGS + if(sequences[i].numFrames == 0) + continue; +#endif + float seqTime = sequences[i].GetEndTime(); + totalLength = Max(totalLength, seqTime); + } +} + +void +CAnimBlendHierarchy::RemoveAnimSequences(void) +{ + delete[] sequences; + numSequences = 0; +} + +#ifdef USE_CUSTOM_ALLOCATOR +void +CAnimBlendHierarchy::MoveMemory(bool onlyone) +{ + int i; + for(i = 0; i < numSequences; i++) + if(sequences[i].MoveMemory() && onlyone) + return; +} +#endif diff --git a/src/animation/AnimBlendHierarchy.h b/src/liberty/animation/AnimBlendHierarchy.h similarity index 100% rename from src/animation/AnimBlendHierarchy.h rename to src/liberty/animation/AnimBlendHierarchy.h diff --git a/src/animation/AnimBlendList.h b/src/liberty/animation/AnimBlendList.h similarity index 100% rename from src/animation/AnimBlendList.h rename to src/liberty/animation/AnimBlendList.h diff --git a/src/liberty/animation/AnimBlendNode.cpp b/src/liberty/animation/AnimBlendNode.cpp new file mode 100644 index 00000000..c54a83a6 --- /dev/null +++ b/src/liberty/animation/AnimBlendNode.cpp @@ -0,0 +1,189 @@ +#include "common.h" + +#include "AnimBlendAssociation.h" +#include "AnimBlendNode.h" + +void +CAnimBlendNode::Init(void) +{ + frameA = -1; + remainingTime = 0.0f; + sequence = nil; + association = nil; + player = nil; +} + +void CAnimBlendNode::Destroy(void) { + if (player) { + delete player; + player = nil; + } +} + +bool +CAnimBlendNode::Update(CVector &trans, CQuaternion &rot, float weight) +{ + assert (player && player->keyFrames == sequence->keyFrames); + + bool looped = false; + + trans = CVector(0.0f, 0.0f, 0.0f); + rot = CQuaternion(0.0f, 0.0f, 0.0f, 0.0f); + + if(association->IsRunning()){ + remainingTime -= association->timeStep; + if(remainingTime <= 0.0f) + looped = NextKeyFrame(); + } + + float blend = association->GetBlendAmount(weight); + if(blend > 0.0f){ + float kfAdt = player->GetNextTimeDelta(); + float t = kfAdt == 0.0f ? 0.0f : (kfAdt - remainingTime)/kfAdt; + if(player->type & CAnimBlendSequence::KF_TRANS){ + auto kfdAt = player->GetNextTranslationDelta(); + auto kfBt = player->GetPrevTranslation(); + trans = kfBt + t*kfdAt; + trans *= blend; + } + if(player->type & CAnimBlendSequence::KF_ROT){ + auto kfAr = player->GetNextRotation(); + auto kfBr = player->GetPrevRotation(); + rot.Slerp(kfBr, kfAr, theta, invSin, t); + rot *= blend; + } + } + + return looped; +} + +bool +CAnimBlendNode::NextKeyFrame(void) +{ + assert(player != nil); + bool looped; + + if(player->numFrames <= 1) + return false; + + looped = false; + + // Advance as long as we have to + while(remainingTime <= 0.0f){ + frameA++; + + if(frameA >= player->numFrames){ + // reached end of animation + if(!association->IsRepeating()){ + frameA--; + remainingTime = 0.0f; + assert(frameA == player->curFrame); + CalcDeltas(); + return false; + } + looped = true; + frameA = 0; + } + player->AdvanceFrame(); + remainingTime += player->GetNextTimeDelta(); + } + + assert(frameA == player->curFrame); + CalcDeltas(); + return looped; +} + +// Set animation to time t +bool +CAnimBlendNode::FindKeyFrame(float t) +{ + if (player == nil) { + player = new CAnimBlendPlayer(); + player->Init(sequence->keyFrames, sequence->type, sequence->numFrames); + } + if(player->numFrames < 1) + return false; + + frameA = 0; + player->SeekToStart(); + + assert (player->keyFrames == sequence->keyFrames); + + if(player->numFrames == 1){ + remainingTime = 0.0f; + }else{ + // advance until t is between frameB and frameA + frameA++; + player->AdvanceFrame(); + while (t > player->GetNextTimeDelta()) { + t -= player->GetNextTimeDelta(); + if (frameA + 1 >= player->numFrames) { + // reached end of animation + if (!association->IsRepeating()) { + assert(frameA == player->curFrame); + CalcDeltas(); + remainingTime = 0.0f; + return false; + } + // Frame 0 is effectively skipped here + // Looks like an re3 / game bug? + frameA = 0; + player->SeekToStart(); + } + frameA++; + player->AdvanceFrame(); + } + + remainingTime = player->GetNextTimeDelta() - t; + } + + assert(frameA == player->curFrame); + CalcDeltas(); + return true; +} + +void +CAnimBlendNode::CalcDeltas(void) +{ + if((player->type & CAnimBlendSequence::KF_ROT) == 0) + return; + auto kfAr = player->GetNextRotation(); + auto kfBr = player->GetPrevRotation(); + float cos = DotProduct(kfAr, kfBr); + if(cos > 1.0f) + cos = 1.0f; + theta = Acos(cos); + invSin = theta == 0.0f ? 0.0f : 1.0f/Sin(theta); +} + +void +CAnimBlendNode::GetCurrentTranslation(CVector &trans, float weight) +{ + trans = CVector(0.0f, 0.0f, 0.0f); + + float blend = association->GetBlendAmount(weight); + if(blend > 0.0f){ + auto kfAdt = player->GetNextTimeDelta(); + float t = kfAdt == 0.0f ? 0.0f : (kfAdt - remainingTime)/kfAdt; + if(player->type & CAnimBlendSequence::KF_TRANS){ + auto kfdAt = player->GetNextTranslationDelta(); + auto kfBt = player->GetPrevTranslation(); + trans = kfBt + t*kfdAt; + trans *= blend; + } + } +} + +void +CAnimBlendNode::GetEndTranslation(CVector &trans, float weight) +{ + trans = CVector(0.0f, 0.0f, 0.0f); + + float blend = association->GetBlendAmount(weight); + if(blend > 0.0f){ + if(player->type & CAnimBlendSequence::KF_TRANS){ + CVector pos = sequence->GetEndTranslation(); + trans = pos * blend; + } + } +} diff --git a/src/animation/AnimBlendNode.h b/src/liberty/animation/AnimBlendNode.h similarity index 94% rename from src/animation/AnimBlendNode.h rename to src/liberty/animation/AnimBlendNode.h index 89924d6a..71ea6b0c 100644 --- a/src/animation/AnimBlendNode.h +++ b/src/liberty/animation/AnimBlendNode.h @@ -13,12 +13,13 @@ public: float invSin; // 1/Sin(theta) // indices into array in sequence int32 frameA; // next key frame - int32 frameB; // previous key frame float remainingTime; // time until frames have to advance + CAnimBlendPlayer* player; CAnimBlendSequence *sequence; CAnimBlendAssociation *association; void Init(void); + void Destroy(void); bool Update(CVector &trans, CQuaternion &rot, float weight); bool NextKeyFrame(void); bool FindKeyFrame(float t); diff --git a/src/animation/AnimBlendSequence.cpp b/src/liberty/animation/AnimBlendSequence.cpp similarity index 53% rename from src/animation/AnimBlendSequence.cpp rename to src/liberty/animation/AnimBlendSequence.cpp index 0718f37c..200f7c77 100644 --- a/src/animation/AnimBlendSequence.cpp +++ b/src/liberty/animation/AnimBlendSequence.cpp @@ -25,45 +25,6 @@ CAnimBlendSequence::SetName(char *name) strncpy(this->name, name, 24); } -void -CAnimBlendSequence::SetNumFrames(int numFrames, bool translation, bool compress) -{ - int sz; - - if(translation){ - type |= KF_ROT | KF_TRANS; - if (compress) { - type |= KF_COMPRESSED; - sz = sizeof(KeyFrameTransCompressed); - } else { - sz = sizeof(KeyFrameTransUncompressed); - } - }else{ - sz = sizeof(KeyFrame); - type |= KF_ROT; - } - keyFrames = RwMalloc(sz * numFrames); - this->numFrames = numFrames; -} - -void -CAnimBlendSequence::RemoveQuaternionFlips(void) -{ - int i; - CQuaternion last; - - if(numFrames < 2) - return; - - last = GetRotation(0); - for(i = 1; i < numFrames; i++){ - auto KFr = GetRotation(i); - if(DotProduct(last, KFr) < 0.0f) - SetRotation(i, -KFr); - last = GetRotation(i); - } -} - #ifdef USE_CUSTOM_ALLOCATOR bool CAnimBlendSequence::MoveMemory(void) diff --git a/src/liberty/animation/AnimBlendSequence.h b/src/liberty/animation/AnimBlendSequence.h new file mode 100644 index 00000000..2fefc63b --- /dev/null +++ b/src/liberty/animation/AnimBlendSequence.h @@ -0,0 +1,402 @@ +#pragma once + +#include "Quaternion.h" + +#ifdef MoveMemory +#undef MoveMemory // windows shit +#endif +struct CAnimBlendPlayer { + enum { + KF_ROT = 1, + KF_TRANS = 2, + + FLAGS_HAS_ROT_Y = 1 << 8, + FLAGS_HAS_ROT_P = 1 << 9, + FLAGS_HAS_ROT_R = 1 << 10, + + FLAGS_HAS_TRANS_X = 1 << 11, + FLAGS_HAS_TRANS_Y = 1 << 12, + FLAGS_HAS_TRANS_Z = 1 << 13, + FLAGS_HAS_TRANS_ANY = 7 << 11, + FLAGS_HAS_TRANS_LARGE = 1 << 14, + FLAGS_QUAT0_NEG = 1 << 15 + }; + + int32 type; + void* keyFrames; + int32 curFrame; + int32 numFrames; + CQuaternion currentRotation; + CQuaternion nextRotation; + CVector currentTranslation; + CVector nextTranslation; + + unsigned readOffset; + unsigned readOffset_initial; + + uint16_t predicted_y, predicted_p, predicted_r; + float predicted_tx = 0, predicted_ty = 0, predicted_tz = 0; + float nextDeltaTime; + + template + T read_unaligned(uint32_t ro) { + T rv; + for (unsigned i = 0; i < sizeof(T); i++) { + ((uint8_t*)&rv)[i] = ((uint8_t*)keyFrames)[ro]; + ro++; + } + readOffset = ro; + return rv; + } + template + __always_inline T read() { + if (!(readOffset & (sizeof(T) -1))) { + return read_aligned(); + } else { + return read_unaligned(readOffset); + } + } + + template + __always_inline T read_aligned() { + T rv; + rv = *(T*)((uint8_t*)keyFrames + readOffset); + readOffset += sizeof(T); + return rv; + } + + __always_inline CQuaternion fromSphericalFixed(uint16_t y, uint16_t p, uint16_t r) { + CQuaternion q; + #if !defined(DC_SH4) + q.w = cos((y / 65536.0f) * 2 * M_PI) * cos((p / 65536.0f) * 2 * M_PI); + q.x = cos((y / 65536.0f) * 2 * M_PI) * sin((p / 65536.0f) * 2 * M_PI); + q.y = sin((y / 65536.0f) * 2 * M_PI) * cos((r / 65536.0f) * 2 * M_PI); + q.z = sin((y / 65536.0f) * 2 * M_PI) * sin((r / 65536.0f) * 2 * M_PI); + #else + register float __ys __asm__("fr0"); + register float __yc __asm__("fr1"); + register float __ps __asm__("fr2"); + register float __pc __asm__("fr3"); + register float __rs __asm__("fr4"); + register float __rc __asm__("fr5"); + + __asm__ __volatile__( + R"( + lds %[y],fpul + fsca fpul, dr0 + lds %[p],fpul + fsca fpul, dr2 + lds %[r],fpul + fsca fpul, dr4 + )" + : "=f" (__ys), "=f" (__yc), "=f" (__ps), "=f" (__pc), "=f" (__rs), "=f" (__rc) + : "0" (__ys), "1" (__yc), "2" (__ps), "3" (__pc), "4" (__rs), "5" (__rc), [y]"r"(y), [p]"r"(p), [r]"r"(r)); + + q.w = __yc * __pc; + q.x = __yc * __ps; + q.y = __ys * __rc; + q.z = __ys * __rs; + #endif + return q; + } + + void AdvanceFrame() { + if (++curFrame == numFrames){ + currentRotation = nextRotation; + currentTranslation = nextTranslation; + SeekToStart(); + return; + } + + // rotation + { + currentRotation = nextRotation; + + // For rotation Y: + if (type & FLAGS_HAS_ROT_Y) { + uint8_t byteVal = read(); + if (byteVal == 128) { + predicted_y = read(); + } else { + int8_t diff = static_cast(byteVal); + predicted_y += diff * 8; + } + } + // For rotation P: + if (type & FLAGS_HAS_ROT_P) { + uint8_t byteVal = read(); + if (byteVal == 128) { + predicted_p = read(); + } else { + int8_t diff = static_cast(byteVal); + predicted_p += diff * 8; + } + } + // For rotation R: + if (type & FLAGS_HAS_ROT_R) { + uint8_t byteVal = read(); + if (byteVal == 128) { + predicted_r = read(); + } else { + int8_t diff = static_cast(byteVal); + predicted_r += diff * 8; + } + } + + nextRotation = fromSphericalFixed(predicted_y, predicted_p, predicted_r); + } + + // translation + if (type & KF_TRANS) { + currentTranslation = nextTranslation; + if (type & FLAGS_HAS_TRANS_X) { + uint8_t byteVal = read(); + if (byteVal == 128) { + uint16_t diff = read(); + if (diff != 32768) { + predicted_tx += static_cast(diff) / 128.f; + } else { + predicted_tx = read(); + } + } else { + int8_t diff = static_cast(byteVal); + predicted_tx += diff / 127.f; + } + } + // Translation Y: + if (type & FLAGS_HAS_TRANS_Y) { + uint8_t byteVal = read(); + if (byteVal == 128) { + uint16_t diff = read(); + if (diff != 32768) { + predicted_ty += static_cast(diff) / 128.f; + } else { + predicted_ty = read(); + } + } else { + int8_t diff = static_cast(byteVal); + predicted_ty += diff / 127.f; + } + } + // Translation Z: + if (type & FLAGS_HAS_TRANS_Z) { + uint8_t byteVal = read(); + if (byteVal == 128) { + uint16_t diff = read(); + if (diff != 32768) { + predicted_tz += static_cast(diff) / 128.f; + } else { + predicted_tz = read(); + } + } else { + int8_t diff = static_cast(byteVal); + predicted_tz += diff / 127.f; + } + } + + nextTranslation = { predicted_tx, predicted_ty, predicted_tz }; + } + + // time delta + quaternion flips + { + uint8_t byteValPacked = read(); + uint8_t byteVal = byteValPacked & 127; + float diff; + if (byteVal == 127) { + uint16_t fixed_diff = read(); + diff = fixed_diff / 256.f; + } else { + diff = byteVal / 256.f; + } + nextDeltaTime = diff; + + if (byteValPacked & 128) { + nextRotation = -nextRotation; + } + } + } + + CQuaternion GetPrevRotation() { + return currentRotation; + } + CQuaternion GetNextRotation() { + return nextRotation; + } + float GetNextTimeDelta() { + return nextDeltaTime; + } + + CVector GetPrevTranslation() { + return currentTranslation; + } + CVector GetNextTranslationDelta() { + return nextTranslation - currentTranslation; + } + + void Init(void* kf, int32 tp, int nF) { + keyFrames = kf; + type = tp; + numFrames = nF; + + SeekToStart(); + currentTranslation = nextTranslation; + currentRotation = nextRotation; + } + + void SeekToStart() { + readOffset = 0; + float startTime = read_aligned(); + float endTime = read_aligned(); + + if (type & KF_TRANS) { + CVector startTranslation; + if (type & FLAGS_HAS_TRANS_LARGE) { + startTranslation.x = read_aligned(); + startTranslation.y = read_aligned(); + startTranslation.z = read_aligned(); + predicted_tx = startTranslation.x; + predicted_ty = startTranslation.y; + predicted_tz = startTranslation.z; + + CVector endTranslation; + // Read final translation (may be used for verification or ignored) + endTranslation.x = read_aligned(); + endTranslation.y = read_aligned(); + endTranslation.z = read_aligned(); + } else { + startTranslation.x = read_aligned() / 128.f; + startTranslation.y = read_aligned() / 128.f; + startTranslation.z = read_aligned() / 128.f; + predicted_tx = startTranslation.x; + predicted_ty = startTranslation.y; + predicted_tz = startTranslation.z; + + CVector endTranslation; + // Read final translation (for completeness) + endTranslation.x = read_aligned() / 128.f; + endTranslation.y = read_aligned() / 128.f; + endTranslation.z = read_aligned() / 128.f; + } + + nextTranslation = startTranslation; + } else { + CVector startTranslation = { 0, 0, 0 }; + CVector endTranslation = { 0, 0, 0 }; + nextTranslation = startTranslation; + } + + predicted_y = read_aligned(); + predicted_p = read_aligned(); + predicted_r = read_aligned(); + nextRotation = fromSphericalFixed(predicted_y, predicted_p, predicted_r); + + if (type & FLAGS_QUAT0_NEG) { + nextRotation = -nextRotation; + } + + nextDeltaTime = startTime; + curFrame = 0; + } +}; + + +// The sequence of key frames of one animated node +class CAnimBlendSequence +{ + template + __always_inline T read_aligned(uint32_t &readOffset) { + T rv; + rv = *(T*)((uint8_t*)keyFrames + readOffset); + readOffset += sizeof(T); + return rv; + } +public: + enum { + KF_ROT = 1, + KF_TRANS = 2, + + FLAGS_HAS_ROT_Y = 1 << 8, + FLAGS_HAS_ROT_P = 1 << 9, + FLAGS_HAS_ROT_R = 1 << 10, + + FLAGS_HAS_TRANS_X = 1 << 11, + FLAGS_HAS_TRANS_Y = 1 << 12, + FLAGS_HAS_TRANS_Z = 1 << 13, + FLAGS_HAS_TRANS_ANY = 7 << 11, + FLAGS_HAS_TRANS_LARGE = 1 << 14, + FLAGS_QUAT0_NEG = 1 << 15 + }; + int32 type; + char name[24]; + int32 numFrames; +#ifdef PED_SKIN + int16 boneTag; +#endif + void *keyFrames; + + + struct InitData { + CVector startTranslation, endTranslation; + float endTime; + }; + + __always_inline InitData GetInitData() { + InitData rv; + uint32_t readOffset = 0; + + float startTime = read_aligned(readOffset); + rv.endTime = read_aligned(readOffset); + + if (type & KF_TRANS) { + if (type & FLAGS_HAS_TRANS_LARGE) { + rv.startTranslation.x = read_aligned(readOffset); + rv.startTranslation.y = read_aligned(readOffset); + rv.startTranslation.z = read_aligned(readOffset); + + // Read final translation (may be used for verification or ignored) + rv.endTranslation.x = read_aligned(readOffset); + rv.endTranslation.y = read_aligned(readOffset); + rv.endTranslation.z = read_aligned(readOffset); + } else { + rv.startTranslation.x = read_aligned(readOffset) / 128.f; + rv.startTranslation.y = read_aligned(readOffset) / 128.f; + rv.startTranslation.z = read_aligned(readOffset) / 128.f; + + // Read final translation (for completeness) + rv.endTranslation.x = read_aligned(readOffset) / 128.f; + rv.endTranslation.y = read_aligned(readOffset) / 128.f; + rv.endTranslation.z = read_aligned(readOffset) / 128.f; + } + } else { + rv.startTranslation = { 0, 0, 0 }; + rv.endTranslation = { 0, 0, 0 }; + } + + return rv; + } + + + CVector GetStartTranslation() { + return GetInitData().startTranslation; + } + float GetEndTime() { + return GetInitData().endTime; + } + CVector GetEndTranslation() { + return GetInitData().endTranslation; + } + + CAnimBlendSequence(void); + virtual ~CAnimBlendSequence(void); + void SetName(char *name); + + bool HasTranslation(void) { return !!(type & KF_TRANS); } + bool MoveMemory(void); + +#ifdef PED_SKIN + void SetBoneTag(int tag) { boneTag = tag; } +#endif +}; +#ifndef PED_SKIN +VALIDATE_SIZE(CAnimBlendSequence, 0x2C); +#endif diff --git a/src/animation/AnimManager.cpp b/src/liberty/animation/AnimManager.cpp similarity index 87% rename from src/animation/AnimManager.cpp rename to src/liberty/animation/AnimManager.cpp index b3889328..ec146853 100644 --- a/src/animation/AnimManager.cpp +++ b/src/liberty/animation/AnimManager.cpp @@ -780,134 +780,70 @@ CAnimManager::LoadAnimFile(int fd, bool compress) char ident[4]; uint32 size; }; - IfpHeader anpk, info, name, dgan, cpan, anim; - int numANPK; + IfpHeader anpv; char buf[256]; - int i, j, k, l; + int j, k, l; float *fbuf = (float*)buf; - CFileMgr::Read(fd, (char*)&anpk, sizeof(IfpHeader)); - if(!CGeneral::faststrncmp(anpk.ident, "ANLF", 4)) { - ROUNDSIZE(anpk.size); - CFileMgr::Read(fd, buf, anpk.size); - numANPK = *(int*)buf; - } else if(!CGeneral::faststrncmp(anpk.ident, "ANPK", 4)) { - CFileMgr::Seek(fd, -8, 1); - numANPK = 1; - } + CFileMgr::Read(fd, (char*)&anpv, sizeof(IfpHeader)); + assert(memcmp(anpv.ident, "ANPV", 4) == 0); + + // block name + CFileMgr::Read(fd, buf, anpv.size); + CAnimBlock *animBlock = &ms_aAnimBlocks[ms_numAnimBlocks++]; + strncpy(animBlock->name, buf, 24); + int32_t numAnims; + CFileMgr::Read(fd, (char*)&numAnims, sizeof(numAnims)); + animBlock->numAnims = numAnims; - for(i = 0; i < numANPK; i++){ - // block name - CFileMgr::Read(fd, (char*)&anpk, sizeof(IfpHeader)); - ROUNDSIZE(anpk.size); - CFileMgr::Read(fd, (char*)&info, sizeof(IfpHeader)); - ROUNDSIZE(info.size); - CFileMgr::Read(fd, buf, info.size); - CAnimBlock *animBlock = &ms_aAnimBlocks[ms_numAnimBlocks++]; - strncpy(animBlock->name, buf+4, 24); - animBlock->numAnims = *(int*)buf; + animBlock->firstIndex = ms_numAnimations; - animBlock->firstIndex = ms_numAnimations; + for(j = 0; j < animBlock->numAnims; j++){ + CAnimBlendHierarchy *hier = &ms_aAnimations[ms_numAnimations++]; - for(j = 0; j < animBlock->numAnims; j++){ - CAnimBlendHierarchy *hier = &ms_aAnimations[ms_numAnimations++]; + // animation name + int32_t animNameLength; + CFileMgr::Read(fd, (char*)&animNameLength, sizeof(animNameLength)); + CFileMgr::Read(fd, buf, animNameLength); + hier->SetName(buf); - // animation name - CFileMgr::Read(fd, (char*)&name, sizeof(IfpHeader)); - ROUNDSIZE(name.size); - CFileMgr::Read(fd, buf, name.size); - hier->SetName(buf); + int32_t numSeqs; + CFileMgr::Read(fd, (char*)&numSeqs, sizeof(animNameLength)); + hier->numSequences = numSeqs; + hier->sequences = new CAnimBlendSequence[hier->numSequences]; - // DG info has number of nodes/sequences - CFileMgr::Read(fd, (char*)&dgan, sizeof(IfpHeader)); - ROUNDSIZE(dgan.size); - CFileMgr::Read(fd, (char*)&info, sizeof(IfpHeader)); - ROUNDSIZE(info.size); - CFileMgr::Read(fd, buf, info.size); - hier->numSequences = *(int*)buf; - hier->sequences = new CAnimBlendSequence[hier->numSequences]; + CAnimBlendSequence *seq = hier->sequences; + for(k = 0; k < hier->numSequences; k++, seq++){ + // Each node has a name and key frames + int32_t seqNameLength; + CFileMgr::Read(fd, (char*)&seqNameLength, sizeof(seqNameLength)); + CFileMgr::Read(fd, buf, seqNameLength); + seq->SetName(buf); - CAnimBlendSequence *seq = hier->sequences; - for(k = 0; k < hier->numSequences; k++, seq++){ - // Each node has a name and key frames - CFileMgr::Read(fd, (char*)&cpan, sizeof(IfpHeader)); - ROUNDSIZE(dgan.size); - CFileMgr::Read(fd, (char*)&anim, sizeof(IfpHeader)); - ROUNDSIZE(anim.size); - CFileMgr::Read(fd, buf, anim.size); - int numFrames = *(int*)(buf+28); - seq->SetName(buf); + int32_t numFrames; + CFileMgr::Read(fd, (char*)&numFrames, sizeof(numFrames)); + seq->numFrames = numFrames; + int32_t boneTag; + CFileMgr::Read(fd, (char*)&boneTag, sizeof(boneTag)); + #ifdef PED_SKIN - if(anim.size == 44) - seq->SetBoneTag(*(int*)(buf+40)); + seq->SetBoneTag(boneTag); #endif - if(numFrames == 0) - continue; + if(numFrames == 0) + continue; - bool hasScale = false; - bool hasTranslation = false; - CFileMgr::Read(fd, (char*)&info, sizeof(info)); - if(!CGeneral::faststrncmp(info.ident, "KRTS", 4)) { - hasScale = true; - seq->SetNumFrames(numFrames, true, compress); - }else if(!CGeneral::faststrncmp(info.ident, "KRT0", 4)) { - hasTranslation = true; - seq->SetNumFrames(numFrames, true, compress); - }else if(!CGeneral::faststrncmp(info.ident, "KR00", 4)){ - seq->SetNumFrames(numFrames, false, compress); - } + uint32_t dataSize; + CFileMgr::Read(fd, (char*)&dataSize, sizeof(dataSize)); + uint16_t flags; + CFileMgr::Read(fd, (char*)&flags, sizeof(flags)); - float *frameTimes = (float*)RwMalloc(sizeof(float) * numFrames); - - for(l = 0; l < numFrames; l++){ - if(hasScale){ - CFileMgr::Read(fd, buf, 0x2C); - CQuaternion rot(fbuf[0], fbuf[1], fbuf[2], fbuf[3]); - rot.Invert(); - CVector trans(fbuf[4], fbuf[5], fbuf[6]); - - seq->SetRotation(l, rot); - seq->SetTranslation(l, trans); - // scaling ignored - frameTimes[l] = fbuf[10]; // absolute time here - }else if(hasTranslation){ - CFileMgr::Read(fd, buf, 0x20); - CQuaternion rot(fbuf[0], fbuf[1], fbuf[2], fbuf[3]); - rot.Invert(); - CVector trans(fbuf[4], fbuf[5], fbuf[6]); - - seq->SetRotation(l, rot); - seq->SetTranslation(l, trans); - frameTimes[l] = fbuf[7]; // absolute time here - }else{ - CFileMgr::Read(fd, buf, 0x14); - CQuaternion rot(fbuf[0], fbuf[1], fbuf[2], fbuf[3]); - rot.Invert(); - - seq->SetRotation(l, rot); - frameTimes[l] = fbuf[4]; // absolute time here - } - } - - // convert absolute time to deltas - float running_sum = 0.0f; - for (l = 0; l < numFrames; l++) { - auto dt = frameTimes[l] - running_sum; - seq->SetDeltaTime(l, dt); - assert(seq->GetDeltaTime(l) <= dt); - running_sum += seq->GetDeltaTime(l); - // if (seq->GetDeltaTime(l) == 0.0f && dt != 0.0f) { - // seq->SetDeltaTime(l, KF_MINDELTA); - // } - - // assert(seq->GetDeltaTime(l) != 0.0f || dt == 0.0f); - } - RwFree(frameTimes); - } - - hier->RemoveQuaternionFlips(); - hier->CalcTotalTime(); + seq->keyFrames = RwMalloc(dataSize); + assert(seq->keyFrames); + CFileMgr::Read(fd, (char*)seq->keyFrames, dataSize - sizeof(flags)); + seq->type = flags; } + + hier->CalcTotalTime(); } } diff --git a/src/animation/AnimManager.h b/src/liberty/animation/AnimManager.h similarity index 100% rename from src/animation/AnimManager.h rename to src/liberty/animation/AnimManager.h diff --git a/src/animation/AnimationId.h b/src/liberty/animation/AnimationId.h similarity index 100% rename from src/animation/AnimationId.h rename to src/liberty/animation/AnimationId.h diff --git a/src/animation/Bones.cpp b/src/liberty/animation/Bones.cpp similarity index 100% rename from src/animation/Bones.cpp rename to src/liberty/animation/Bones.cpp diff --git a/src/animation/Bones.h b/src/liberty/animation/Bones.h similarity index 100% rename from src/animation/Bones.h rename to src/liberty/animation/Bones.h diff --git a/src/animation/CutsceneMgr.cpp b/src/liberty/animation/CutsceneMgr.cpp similarity index 99% rename from src/animation/CutsceneMgr.cpp rename to src/liberty/animation/CutsceneMgr.cpp index ade0bba9..98823224 100644 --- a/src/animation/CutsceneMgr.cpp +++ b/src/liberty/animation/CutsceneMgr.cpp @@ -267,7 +267,7 @@ CCutsceneMgr::SetupCutsceneToStart(void) assert(RwObjectGetType(ms_pCutsceneObjects[i]->m_rwObject) == rpCLUMP); if (CAnimBlendAssociation *pAnimBlendAssoc = RpAnimBlendClumpGetFirstAssociation((RpClump*)ms_pCutsceneObjects[i]->m_rwObject)) { assert(pAnimBlendAssoc->hierarchy->sequences[0].HasTranslation()); - ms_pCutsceneObjects[i]->SetPosition(ms_cutsceneOffset + pAnimBlendAssoc->hierarchy->sequences[0].GetTranslation(0)); + ms_pCutsceneObjects[i]->SetPosition(ms_cutsceneOffset + pAnimBlendAssoc->hierarchy->sequences[0].GetStartTranslation()); CWorld::Add(ms_pCutsceneObjects[i]); pAnimBlendAssoc->SetRun(); } else { diff --git a/src/animation/CutsceneMgr.h b/src/liberty/animation/CutsceneMgr.h similarity index 100% rename from src/animation/CutsceneMgr.h rename to src/liberty/animation/CutsceneMgr.h diff --git a/src/animation/FrameUpdate.cpp b/src/liberty/animation/FrameUpdate.cpp similarity index 100% rename from src/animation/FrameUpdate.cpp rename to src/liberty/animation/FrameUpdate.cpp diff --git a/src/animation/RpAnimBlend.cpp b/src/liberty/animation/RpAnimBlend.cpp similarity index 100% rename from src/animation/RpAnimBlend.cpp rename to src/liberty/animation/RpAnimBlend.cpp diff --git a/src/animation/RpAnimBlend.h b/src/liberty/animation/RpAnimBlend.h similarity index 100% rename from src/animation/RpAnimBlend.h rename to src/liberty/animation/RpAnimBlend.h diff --git a/src/audio/AudioCollision.cpp b/src/liberty/audio/AudioCollision.cpp similarity index 100% rename from src/audio/AudioCollision.cpp rename to src/liberty/audio/AudioCollision.cpp diff --git a/src/audio/AudioCollision.h b/src/liberty/audio/AudioCollision.h similarity index 100% rename from src/audio/AudioCollision.h rename to src/liberty/audio/AudioCollision.h diff --git a/src/audio/AudioLogic.cpp b/src/liberty/audio/AudioLogic.cpp similarity index 99% rename from src/audio/AudioLogic.cpp rename to src/liberty/audio/AudioLogic.cpp index 4624d83a..e9834efa 100644 --- a/src/audio/AudioLogic.cpp +++ b/src/liberty/audio/AudioLogic.cpp @@ -3789,6 +3789,10 @@ cAudioManager::ProcessPedOneShots(cPedParams ¶ms) if (iSound > 60) iSound = 21; } + // In some cases the left and right channels have different loop points + // This looks like a data file issue where the left and right channels have different loop points + // This is a hot fix to always have the correct loop point for each channel individually + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) AddSampleToRequestedQueue(); } } @@ -6302,7 +6306,7 @@ cPedComments::Process() { case LOADING_STATUS_NOT_LOADED: SampleManager.LoadPedComment(sampleIndex); -#ifdef GTA_PS2 // on PC ped comment is loaded at once +#if defined(GTA_PS2) || defined(RW_DC) // on PC ped comment is loaded at once break; #endif case LOADING_STATUS_LOADED: diff --git a/src/audio/AudioManager.cpp b/src/liberty/audio/AudioManager.cpp similarity index 100% rename from src/audio/AudioManager.cpp rename to src/liberty/audio/AudioManager.cpp diff --git a/src/audio/AudioManager.h b/src/liberty/audio/AudioManager.h similarity index 100% rename from src/audio/AudioManager.h rename to src/liberty/audio/AudioManager.h diff --git a/src/audio/AudioSamples.h b/src/liberty/audio/AudioSamples.h similarity index 100% rename from src/audio/AudioSamples.h rename to src/liberty/audio/AudioSamples.h diff --git a/src/audio/AudioScriptObject.cpp b/src/liberty/audio/AudioScriptObject.cpp similarity index 100% rename from src/audio/AudioScriptObject.cpp rename to src/liberty/audio/AudioScriptObject.cpp diff --git a/src/audio/AudioScriptObject.h b/src/liberty/audio/AudioScriptObject.h similarity index 100% rename from src/audio/AudioScriptObject.h rename to src/liberty/audio/AudioScriptObject.h diff --git a/src/audio/DMAudio.cpp b/src/liberty/audio/DMAudio.cpp similarity index 100% rename from src/audio/DMAudio.cpp rename to src/liberty/audio/DMAudio.cpp diff --git a/src/audio/DMAudio.h b/src/liberty/audio/DMAudio.h similarity index 100% rename from src/audio/DMAudio.h rename to src/liberty/audio/DMAudio.h diff --git a/src/audio/MusicManager.cpp b/src/liberty/audio/MusicManager.cpp similarity index 100% rename from src/audio/MusicManager.cpp rename to src/liberty/audio/MusicManager.cpp diff --git a/src/audio/MusicManager.h b/src/liberty/audio/MusicManager.h similarity index 100% rename from src/audio/MusicManager.h rename to src/liberty/audio/MusicManager.h diff --git a/src/audio/PolRadio.cpp b/src/liberty/audio/PolRadio.cpp similarity index 100% rename from src/audio/PolRadio.cpp rename to src/liberty/audio/PolRadio.cpp diff --git a/src/audio/PolRadio.h b/src/liberty/audio/PolRadio.h similarity index 100% rename from src/audio/PolRadio.h rename to src/liberty/audio/PolRadio.h diff --git a/src/audio/audio_enums.h b/src/liberty/audio/audio_enums.h similarity index 100% rename from src/audio/audio_enums.h rename to src/liberty/audio/audio_enums.h diff --git a/src/audio/eax/eax-util.cpp b/src/liberty/audio/eax/eax-util.cpp similarity index 100% rename from src/audio/eax/eax-util.cpp rename to src/liberty/audio/eax/eax-util.cpp diff --git a/src/audio/eax/eax-util.h b/src/liberty/audio/eax/eax-util.h similarity index 100% rename from src/audio/eax/eax-util.h rename to src/liberty/audio/eax/eax-util.h diff --git a/src/audio/eax/eax.h b/src/liberty/audio/eax/eax.h similarity index 100% rename from src/audio/eax/eax.h rename to src/liberty/audio/eax/eax.h diff --git a/src/audio/oal/aldlist.cpp b/src/liberty/audio/oal/aldlist.cpp similarity index 100% rename from src/audio/oal/aldlist.cpp rename to src/liberty/audio/oal/aldlist.cpp diff --git a/src/audio/oal/aldlist.h b/src/liberty/audio/oal/aldlist.h similarity index 100% rename from src/audio/oal/aldlist.h rename to src/liberty/audio/oal/aldlist.h diff --git a/src/audio/oal/channel.cpp b/src/liberty/audio/oal/channel.cpp similarity index 100% rename from src/audio/oal/channel.cpp rename to src/liberty/audio/oal/channel.cpp diff --git a/src/audio/oal/channel.h b/src/liberty/audio/oal/channel.h similarity index 100% rename from src/audio/oal/channel.h rename to src/liberty/audio/oal/channel.h diff --git a/src/audio/oal/oal_utils.cpp b/src/liberty/audio/oal/oal_utils.cpp similarity index 100% rename from src/audio/oal/oal_utils.cpp rename to src/liberty/audio/oal/oal_utils.cpp diff --git a/src/audio/oal/oal_utils.h b/src/liberty/audio/oal/oal_utils.h similarity index 100% rename from src/audio/oal/oal_utils.h rename to src/liberty/audio/oal/oal_utils.h diff --git a/src/audio/oal/stream.cpp b/src/liberty/audio/oal/stream.cpp similarity index 100% rename from src/audio/oal/stream.cpp rename to src/liberty/audio/oal/stream.cpp diff --git a/src/audio/oal/stream.h b/src/liberty/audio/oal/stream.h similarity index 100% rename from src/audio/oal/stream.h rename to src/liberty/audio/oal/stream.h diff --git a/src/audio/sampman.h b/src/liberty/audio/sampman.h similarity index 83% rename from src/audio/sampman.h rename to src/liberty/audio/sampman.h index 70f1b47a..00c445ae 100644 --- a/src/audio/sampman.h +++ b/src/liberty/audio/sampman.h @@ -174,6 +174,10 @@ public: void UpdateEffectsVolume(void); +#ifdef DC_SH4 + void UpdateChannelVolume(uint32 nChannel); +#endif + void SetEffectsMasterVolume(uint8 nVolume); void SetMusicMasterVolume (uint8 nVolume); void SetEffectsFadeVolume (uint8 nVolume); @@ -474,6 +478,7 @@ static char PS2StreamedNameTable[][25]= }; #endif +#ifdef PC_AUDIO_PATHS static char StreamedNameTable[][25] = { "AUDIO\\HEAD.WAV", @@ -673,204 +678,6 @@ static char StreamedNameTable[][25] = "AUDIO\\k1_b.WAV", "AUDIO\\cat1.WAV" }; +#endif -static char DCStreamedNameTable[][25] = -{ - "stream/HEAD.APM", - "stream/CLASS.APM", - "stream/KJAH.APM", - "stream/RISE.APM", - "stream/LIPS.APM", - "stream/GAME.APM", - "stream/MSX.APM", - "stream/FLASH.APM", - "stream/CHAT.APM", - "stream/HEAD.APM", - "stream/POLICE.APM", - "stream/CITY.APM", - "stream/WATER.APM", - "stream/COMOPEN.APM", - "stream/SUBOPEN.APM", - "stream/JB.APM", - "stream/BET.APM", - "stream/L1_LG.APM", - "stream/L2_DSB.APM", - "stream/L3_DM.APM", - "stream/L4_PAP.APM", - "stream/L5_TFB.APM", - "stream/J0_DM2.APM", - "stream/J1_LFL.APM", - "stream/J2_KCL.APM", - "stream/J3_VH.APM", - "stream/J4_ETH.APM", - "stream/J5_DST.APM", - "stream/J6_TBJ.APM", - "stream/T1_TOL.APM", - "stream/T2_TPU.APM", - "stream/T3_MAS.APM", - "stream/T4_TAT.APM", - "stream/T5_BF.APM", - "stream/S0_MAS.APM", - "stream/S1_PF.APM", - "stream/S2_CTG.APM", - "stream/S3_RTC.APM", - "stream/S5_LRQ.APM", - "stream/S4_BDBA.APM", - "stream/S4_BDBB.APM", - "stream/S2_CTG2.APM", - "stream/S4_BDBD.APM", - "stream/S5_LRQB.APM", - "stream/S5_LRQC.APM", - "stream/A1_SSO.APM", - "stream/A2_PP.APM", - "stream/A3_SS.APM", - "stream/A4_PDR.APM", - "stream/A5_K2FT.APM", - "stream/K1_KBO.APM", - "stream/K2_GIS.APM", - "stream/K3_DS.APM", - "stream/K4_SHI.APM", - "stream/K5_SD.APM", - "stream/R0_PDR2.APM", - "stream/R1_SW.APM", - "stream/R2_AP.APM", - "stream/R3_ED.APM", - "stream/R4_GF.APM", - "stream/R5_PB.APM", - "stream/R6_MM.APM", - "stream/D1_STOG.APM", - "stream/D2_KK.APM", - "stream/D3_ADO.APM", - "stream/D5_ES.APM", - "stream/D7_MLD.APM", - "stream/D4_GTA.APM", - "stream/D4_GTA2.APM", - "stream/D6_STS.APM", - "stream/A6_BAIT.APM", - "stream/A7_ETG.APM", - "stream/A8_PS.APM", - "stream/A9_ASD.APM", - "stream/K4_SHI2.APM", - "stream/C1_TEX.APM", - "stream/EL_PH1.APM", - "stream/EL_PH2.APM", - "stream/EL_PH3.APM", - "stream/EL_PH4.APM", - "stream/YD_PH1.APM", - "stream/YD_PH2.APM", - "stream/YD_PH3.APM", - "stream/YD_PH4.APM", - "stream/HD_PH1.APM", - "stream/HD_PH2.APM", - "stream/HD_PH3.APM", - "stream/HD_PH4.APM", - "stream/HD_PH5.APM", - "stream/MT_PH1.APM", - "stream/MT_PH2.APM", - "stream/MT_PH3.APM", - "stream/MT_PH4.APM", - "stream/MISCOM.APM", - "stream/END.APM", - "stream/lib_a1.APM", - "stream/lib_a2.APM", - "stream/lib_a.APM", - "stream/lib_b.APM", - "stream/lib_c.APM", - "stream/lib_d.APM", - "stream/l2_a.APM", - "stream/j4t_1.APM", - "stream/j4t_2.APM", - "stream/j4t_3.APM", - "stream/j4t_4.APM", - "stream/j4_a.APM", - "stream/j4_b.APM", - "stream/j4_c.APM", - "stream/j4_d.APM", - "stream/j4_e.APM", - "stream/j4_f.APM", - "stream/j6_1.APM", - "stream/j6_a.APM", - "stream/j6_b.APM", - "stream/j6_c.APM", - "stream/j6_d.APM", - "stream/t4_a.APM", - "stream/s1_a.APM", - "stream/s1_a1.APM", - "stream/s1_b.APM", - "stream/s1_c.APM", - "stream/s1_c1.APM", - "stream/s1_d.APM", - "stream/s1_e.APM", - "stream/s1_f.APM", - "stream/s1_g.APM", - "stream/s1_h.APM", - "stream/s1_i.APM", - "stream/s1_j.APM", - "stream/s1_k.APM", - "stream/s1_l.APM", - "stream/s3_a.APM", - "stream/s3_b.APM", - "stream/el3_a.APM", - "stream/mf1_a.APM", - "stream/mf2_a.APM", - "stream/mf3_a.APM", - "stream/mf3_b.APM", - "stream/mf3_b1.APM", - "stream/mf3_c.APM", - "stream/mf4_a.APM", - "stream/mf4_b.APM", - "stream/mf4_c.APM", - "stream/a1_a.APM", - "stream/a3_a.APM", - "stream/a5_a.APM", - "stream/a4_a.APM", - "stream/a4_b.APM", - "stream/a4_c.APM", - "stream/a4_d.APM", - "stream/k1_a.APM", - "stream/k3_a.APM", - "stream/r1_a.APM", - "stream/r2_a.APM", - "stream/r2_b.APM", - "stream/r2_c.APM", - "stream/r2_d.APM", - "stream/r2_e.APM", - "stream/r2_f.APM", - "stream/r2_g.APM", - "stream/r2_h.APM", - "stream/r5_a.APM", - "stream/r6_a.APM", - "stream/r6_a1.APM", - "stream/r6_b.APM", - "stream/lo2_a.APM", - "stream/lo6_a.APM", - "stream/yd2_a.APM", - "stream/yd2_b.APM", - "stream/yd2_c.APM", - "stream/yd2_c1.APM", - "stream/yd2_d.APM", - "stream/yd2_e.APM", - "stream/yd2_f.APM", - "stream/yd2_g.APM", - "stream/yd2_h.APM", - "stream/yd2_ass.APM", - "stream/yd2_ok.APM", - "stream/h5_a.APM", - "stream/h5_b.APM", - "stream/h5_c.APM", - "stream/ammu_a.APM", - "stream/ammu_b.APM", - "stream/ammu_c.APM", - "stream/door_1.APM", - "stream/door_2.APM", - "stream/door_3.APM", - "stream/door_4.APM", - "stream/door_5.APM", - "stream/door_6.APM", - "stream/t3_a.APM", - "stream/t3_b.APM", - "stream/t3_c.APM", - "stream/k1_b.APM", - "stream/cat1.APM" -}; #endif \ No newline at end of file diff --git a/src/audio/sampman_dc.cpp b/src/liberty/audio/sampman_dc.cpp similarity index 93% rename from src/audio/sampman_dc.cpp rename to src/liberty/audio/sampman_dc.cpp index 0f48a4b8..e9e90650 100644 --- a/src/audio/sampman_dc.cpp +++ b/src/liberty/audio/sampman_dc.cpp @@ -15,6 +15,7 @@ #define debugf(...) // dbglog(DBG_CRITICAL, __VA_ARGS__) #include "sampman.h" +#include "sampman_dc_streams.h" #include "AudioManager.h" #include "MusicManager.h" #include "Frontend.h" @@ -151,7 +152,6 @@ void aica_snd_sfx_freq_vol(int chn, int freq, int vol) { cSampleManager SampleManager; bool8 _bSampmanInitialised = FALSE; -bool _dcAudioInitialized = false; uint32 BankStartOffset[MAX_SFX_BANKS]; char SampleBankDescFilename[] = "sfx/sfx_all.dsc"; @@ -166,8 +166,14 @@ struct sfx_bank { std::map sfx_banks; int nPedSlotSfx[MAX_PEDSFX]; +uint32 nPedSlotSfxReqId[MAX_PEDSFX]; uintptr_t nPedSlotSfxAddr[MAX_PEDSFX]; uint8_t nCurrentPedSlot; +file_t fdPedSfx; +volatile uint32 nPedSfxReqReadId = 1; +volatile uint32 nPedSfxReqNextId = 1; + +static int32 DCStreamedLength[TOTAL_STREAMED_SOUNDS]; struct WavHeader { // RIFF Header @@ -442,23 +448,36 @@ cSampleManager::Initialise(void) } }); + nPedSfxReqNextId = 1; + nPedSfxReqReadId = 1; for ( int32 i = 0; i < MAX_PEDSFX; i++ ) { nPedSlotSfx[i] = -1; + nPedSlotSfxReqId[i] = 0; nPedSlotSfxAddr[i] = snd_mem_malloc(PED_BLOCKSIZE_ADPCM); debugf("PedSlot %d buffer: %p\n", i, (void*)nPedSlotSfxAddr[i]); } nCurrentPedSlot = 0; - _dcAudioInitialized = true; + fdPedSfx = fs_open(SampleBankDataFilename, O_RDONLY); + + assert(fdPedSfx >= 0); + + file_t fd = fs_open("stream/hdr.bin", O_RDONLY); + assert(fd >= 0); + static_assert(sizeof(DCStreamedLength) == TOTAL_STREAMED_SOUNDS*sizeof(int32)); + assert(fs_read(fd, DCStreamedLength, sizeof(DCStreamedLength)) == sizeof(DCStreamedLength)); + fs_close(fd); + + _bSampmanInitialised = true; return TRUE; } void cSampleManager::Terminate(void) { - + fs_close(fdPedSfx); } bool8 cSampleManager::CheckForAnAudioFileOnCD(void) @@ -474,7 +493,14 @@ char cSampleManager::GetCDAudioDriveLetter(void) void cSampleManager::UpdateEffectsVolume(void) { - // TODO + if(_bSampmanInitialised) { + std::lock_guard lk(channel_mtx); + for (int i = 0; i < MAXCHANNELS+MAX2DCHANNELS; i++) { + if (channels[i].ch != -1) { + UpdateChannelVolume(i); + } + } + } } @@ -637,7 +663,7 @@ cSampleManager::IsPedCommentLoaded(uint32 nComment) slot += ARRAY_SIZE(nPedSlotSfx); #endif if ( nComment == nPedSlotSfx[slot] ) - return LOADING_STATUS_LOADED; + return nPedSlotSfxReqId[slot] <= nPedSfxReqReadId ? LOADING_STATUS_LOADED : LOADING_STATUS_LOADING; } return LOADING_STATUS_NOT_LOADED; @@ -698,27 +724,28 @@ cSampleManager::LoadPedComment(uint32 nComment) assert(m_aSamples[nComment].nByteSize < PED_BLOCKSIZE_ADPCM); - file_t fd = fs_open(SampleBankDataFilename, O_RDONLY); - - assert(fd >= 0); - debugf("Loading ped comment %d, offset: %d, size: %d\n", nComment, m_aSamples[nComment].nFileOffset, m_aSamples[nComment].nByteSize); - fs_seek(fd, m_aSamples[nComment].nFileOffset, SEEK_SET); + CdStreamQueueAudioRead(nComment, (void*)nPedSlotSfxAddr[nCurrentPedSlot], m_aSamples[nComment].nByteSize, m_aSamples[nComment].nFileOffset, [](AudioReadCmd* cmd) { + debugf("Loading ped comment %d, offset: %d, size: %d\n", nComment, m_aSamples[nComment].nFileOffset, m_aSamples[nComment].nByteSize); + fs_seek(fdPedSfx, cmd->seek, SEEK_SET); - // TODO: When we can dma directly to AICA, we can use this instead - // fs_read(fd, SPU_BASE_U8 + nPedSlotSfxAddr[nCurrentPedSlot], sizeof(nPedSlotSfxAddr)); + // TODO: When we can dma directly to AICA, we can use this instead + // fs_read(fdPedSfx, SPU_BASE_U8 + (uintptr_t)cmd->dest, cmd->size); - void* stagingBuffer = memalign(32, m_aSamples[nComment].nByteSize); - assert(stagingBuffer != 0); - debugf("Allocated %d bytes at %p\n", m_aSamples[nComment].nByteSize, stagingBuffer); - int rs = fs_read(fd, stagingBuffer, m_aSamples[nComment].nByteSize); - debugf("Read %d bytes, expected %d\n", rs, m_aSamples[nComment].nByteSize); - assert(rs == m_aSamples[nComment].nByteSize); + void* stagingBuffer = memalign(32, cmd->size); + assert(stagingBuffer != 0); + debugf("Allocated %d bytes at %p\n", cmd->size, stagingBuffer); + int rs = fs_read(fdPedSfx, stagingBuffer, cmd->size); + debugf("Read %d bytes, expected %d\n", rs, cmd->size); + assert(rs == cmd->size); - fs_close(fd); + spu_memload((uintptr_t)cmd->dest, stagingBuffer, cmd->size); + free(stagingBuffer); + nPedSfxReqReadId = nPedSfxReqReadId + 1; + }); - spu_memload(nPedSlotSfxAddr[nCurrentPedSlot], stagingBuffer, m_aSamples[nComment].nByteSize); - free(stagingBuffer); + nPedSlotSfxReqId[nCurrentPedSlot] = ++nPedSfxReqNextId; + nPedSlotSfx[nCurrentPedSlot] = nComment; if ( ++nCurrentPedSlot >= MAX_PEDSFX ) @@ -839,9 +866,10 @@ cSampleManager::InitialiseChannel(uint32 nChannel, uint32 nSfx, uint8 nBank) return TRUE; } -void updateVol(uint32 nChannel) { +void cSampleManager::UpdateChannelVolume(uint32 nChannel) { auto newVol = channels[nChannel].emittingVol * channels[nChannel].attenuationVol / 255; + newVol = m_nEffectsFadeVolume * newVol * m_nEffectsVolume >> 14; // newVol = 255; // printf("updateVol(nChannel: %d) vol: %d, newVol: %d\n", nChannel, channels[nChannel].vol, newVol); if (channels[nChannel].vol != newVol) { @@ -864,7 +892,7 @@ cSampleManager::SetChannelVolume(uint32 nChannel, uint32 nVolume) channels[nChannel].emittingVol = linearlize_volume(nVolume);// nVolume * 255 / MAX_VOLUME; channels[nChannel].attenuationVol = 255; - updateVol(nChannel); + UpdateChannelVolume(nChannel); verbosef("SetChannelVolume(nChannel: %d) vol: %d\n", nChannel, nVolume); } @@ -1120,7 +1148,8 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream) debugf("StartStreamedFile(%d, %d, %d)\n", nFile, nPos, nStream); uint32_t seek_aligned = 0; if (nPos) { - uint32 seek_bytes = nPos * streams[nStream].rate / (streams[nStream].stereo ? 1000: 2000); + uint64_t seek_bytes = (uint64_t)nPos * streams[nStream].rate / (streams[nStream].stereo ? 1000: 2000); + assert(seek_bytes <= INT32_MAX); seek_aligned = seek_bytes & ~(streams[nStream].stereo ? (STREAM_STAGING_READ_SIZE_STEREO-1) : (STREAM_STAGING_READ_SIZE_MONO-1)); } PreloadStreamedFile(nFile, nStream, seek_aligned); @@ -1152,7 +1181,9 @@ cSampleManager::GetStreamedFilePosition(uint8 nStream) ASSERT( nStream < MAX_STREAMS ); int32 rv = 0; - return streams[nStream].played_samples * 1000 / streams[nStream].rate; + int64_t rv64 = (int64_t)streams[nStream].played_samples * 1000 / streams[nStream].rate; + assert(rv64 <= INT32_MAX); + rv = (int32)rv64; // if(streams[nStream].fd >= 0) { // rv = fs_tell(streams[nStream].fd); // } @@ -1167,6 +1198,7 @@ cSampleManager::SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, uint8 nEffect if (nVolume > MAX_VOLUME) nVolume = MAX_VOLUME; nVolume = linearlize_volume(nVolume); //nVolume * 255 / MAX_VOLUME; + nVolume = m_nMusicFadeVolume * nVolume * m_nMusicVolume >> 14; if (streams[nStream].vol != nVolume || streams[nStream].nPan != nPan) { streams[nStream].vol = nVolume; streams[nStream].nPan = nPan; @@ -1189,18 +1221,10 @@ int32 cSampleManager::GetStreamedFileLength(uint8 nFile) { ASSERT( nFile < TOTAL_STREAMED_SOUNDS ); - int32 rv = 1; // Look in MusicManager.cpp:268 - file_t fd = fs_open(DCStreamedNameTable[nFile], O_RDONLY); - assert(fd >= 0); - WavHeader hdr; - assert(fs_read(fd, &hdr, sizeof(hdr)) == sizeof(hdr)); - - rv = hdr.dataSize * 2000 / hdr.numOfChan / hdr.samplesPerSec; - - fs_close(fd); + auto rv = DCStreamedLength[nFile]; debugf("GetStreamedFileLength: %d %d\n", nFile, rv); - return rv <= 0 ? 1 : rv; + return rv; } bool8 @@ -1275,9 +1299,17 @@ void cSampleManager::SetChannelEmittingVolume(uint32 nChannel, uint32 nVolume) if (nVolume > MAX_VOLUME) nVolume = MAX_VOLUME; + // reduce channel volume when JB.MP3 or S4_BDBD.MP3 playing + if ( MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE + && MusicManager.GetNextTrack() != STREAMED_SOUND_NEWS_INTRO + && MusicManager.GetNextTrack() != STREAMED_SOUND_CUTSCENE_SAL4_BDBD ) + { + nVolume /= 4; + } + std::lock_guard lk(channel_mtx); channels[nChannel].emittingVol = linearlize_volume(nVolume); // nVolume * 255 / MAX_VOLUME; - updateVol(nChannel); + UpdateChannelVolume(nChannel); } float calculatePan(float x, float z) { @@ -1322,7 +1354,7 @@ void cSampleManager::SetChannel3DPosition (uint32 nChannel, float fX, float channels[nChannel].fY = fY; channels[nChannel].fZ = fZ; channels[nChannel].attenuationVol = calculateAttenuation(channels[nChannel].fX, channels[nChannel].fY, channels[nChannel].fZ, channels[nChannel].distMin, channels[nChannel].distMax) * 255; - updateVol(nChannel); + UpdateChannelVolume(nChannel); } SetChannelPan(nChannel, calculatePan(-fX, fZ) * 63 + 64); @@ -1335,7 +1367,7 @@ void cSampleManager::SetChannel3DDistances (uint32 nChannel, float fMax, floa channels[nChannel].distMin = fMin; channels[nChannel].distMax = fMax; channels[nChannel].attenuationVol = calculateAttenuation(channels[nChannel].fX, channels[nChannel].fY, channels[nChannel].fZ, channels[nChannel].distMin, channels[nChannel].distMax) * 255; - updateVol(nChannel); + UpdateChannelVolume(nChannel); } #endif diff --git a/src/liberty/audio/sampman_dc_streams.h b/src/liberty/audio/sampman_dc_streams.h new file mode 100644 index 00000000..696b07e0 --- /dev/null +++ b/src/liberty/audio/sampman_dc_streams.h @@ -0,0 +1,200 @@ + +static char DCStreamedNameTable[][25] = +{ + "stream/HEAD.APM", + "stream/CLASS.APM", + "stream/KJAH.APM", + "stream/RISE.APM", + "stream/LIPS.APM", + "stream/GAME.APM", + "stream/MSX.APM", + "stream/FLASH.APM", + "stream/CHAT.APM", + "stream/HEAD.APM", + "stream/POLICE.APM", + "stream/CITY.APM", + "stream/WATER.APM", + "stream/COMOPEN.APM", + "stream/SUBOPEN.APM", + "stream/JB.APM", + "stream/BET.APM", + "stream/L1_LG.APM", + "stream/L2_DSB.APM", + "stream/L3_DM.APM", + "stream/L4_PAP.APM", + "stream/L5_TFB.APM", + "stream/J0_DM2.APM", + "stream/J1_LFL.APM", + "stream/J2_KCL.APM", + "stream/J3_VH.APM", + "stream/J4_ETH.APM", + "stream/J5_DST.APM", + "stream/J6_TBJ.APM", + "stream/T1_TOL.APM", + "stream/T2_TPU.APM", + "stream/T3_MAS.APM", + "stream/T4_TAT.APM", + "stream/T5_BF.APM", + "stream/S0_MAS.APM", + "stream/S1_PF.APM", + "stream/S2_CTG.APM", + "stream/S3_RTC.APM", + "stream/S5_LRQ.APM", + "stream/S4_BDBA.APM", + "stream/S4_BDBB.APM", + "stream/S2_CTG2.APM", + "stream/S4_BDBD.APM", + "stream/S5_LRQB.APM", + "stream/S5_LRQC.APM", + "stream/A1_SSO.APM", + "stream/A2_PP.APM", + "stream/A3_SS.APM", + "stream/A4_PDR.APM", + "stream/A5_K2FT.APM", + "stream/K1_KBO.APM", + "stream/K2_GIS.APM", + "stream/K3_DS.APM", + "stream/K4_SHI.APM", + "stream/K5_SD.APM", + "stream/R0_PDR2.APM", + "stream/R1_SW.APM", + "stream/R2_AP.APM", + "stream/R3_ED.APM", + "stream/R4_GF.APM", + "stream/R5_PB.APM", + "stream/R6_MM.APM", + "stream/D1_STOG.APM", + "stream/D2_KK.APM", + "stream/D3_ADO.APM", + "stream/D5_ES.APM", + "stream/D7_MLD.APM", + "stream/D4_GTA.APM", + "stream/D4_GTA2.APM", + "stream/D6_STS.APM", + "stream/A6_BAIT.APM", + "stream/A7_ETG.APM", + "stream/A8_PS.APM", + "stream/A9_ASD.APM", + "stream/K4_SHI2.APM", + "stream/C1_TEX.APM", + "stream/EL_PH1.APM", + "stream/EL_PH2.APM", + "stream/EL_PH3.APM", + "stream/EL_PH4.APM", + "stream/YD_PH1.APM", + "stream/YD_PH2.APM", + "stream/YD_PH3.APM", + "stream/YD_PH4.APM", + "stream/HD_PH1.APM", + "stream/HD_PH2.APM", + "stream/HD_PH3.APM", + "stream/HD_PH4.APM", + "stream/HD_PH5.APM", + "stream/MT_PH1.APM", + "stream/MT_PH2.APM", + "stream/MT_PH3.APM", + "stream/MT_PH4.APM", + "stream/MISCOM.APM", + "stream/END.APM", + "stream/lib_a1.APM", + "stream/lib_a2.APM", + "stream/lib_a.APM", + "stream/lib_b.APM", + "stream/lib_c.APM", + "stream/lib_d.APM", + "stream/l2_a.APM", + "stream/j4t_1.APM", + "stream/j4t_2.APM", + "stream/j4t_3.APM", + "stream/j4t_4.APM", + "stream/j4_a.APM", + "stream/j4_b.APM", + "stream/j4_c.APM", + "stream/j4_d.APM", + "stream/j4_e.APM", + "stream/j4_f.APM", + "stream/j6_1.APM", + "stream/j6_a.APM", + "stream/j6_b.APM", + "stream/j6_c.APM", + "stream/j6_d.APM", + "stream/t4_a.APM", + "stream/s1_a.APM", + "stream/s1_a1.APM", + "stream/s1_b.APM", + "stream/s1_c.APM", + "stream/s1_c1.APM", + "stream/s1_d.APM", + "stream/s1_e.APM", + "stream/s1_f.APM", + "stream/s1_g.APM", + "stream/s1_h.APM", + "stream/s1_i.APM", + "stream/s1_j.APM", + "stream/s1_k.APM", + "stream/s1_l.APM", + "stream/s3_a.APM", + "stream/s3_b.APM", + "stream/el3_a.APM", + "stream/mf1_a.APM", + "stream/mf2_a.APM", + "stream/mf3_a.APM", + "stream/mf3_b.APM", + "stream/mf3_b1.APM", + "stream/mf3_c.APM", + "stream/mf4_a.APM", + "stream/mf4_b.APM", + "stream/mf4_c.APM", + "stream/a1_a.APM", + "stream/a3_a.APM", + "stream/a5_a.APM", + "stream/a4_a.APM", + "stream/a4_b.APM", + "stream/a4_c.APM", + "stream/a4_d.APM", + "stream/k1_a.APM", + "stream/k3_a.APM", + "stream/r1_a.APM", + "stream/r2_a.APM", + "stream/r2_b.APM", + "stream/r2_c.APM", + "stream/r2_d.APM", + "stream/r2_e.APM", + "stream/r2_f.APM", + "stream/r2_g.APM", + "stream/r2_h.APM", + "stream/r5_a.APM", + "stream/r6_a.APM", + "stream/r6_a1.APM", + "stream/r6_b.APM", + "stream/lo2_a.APM", + "stream/lo6_a.APM", + "stream/yd2_a.APM", + "stream/yd2_b.APM", + "stream/yd2_c.APM", + "stream/yd2_c1.APM", + "stream/yd2_d.APM", + "stream/yd2_e.APM", + "stream/yd2_f.APM", + "stream/yd2_g.APM", + "stream/yd2_h.APM", + "stream/yd2_ass.APM", + "stream/yd2_ok.APM", + "stream/h5_a.APM", + "stream/h5_b.APM", + "stream/h5_c.APM", + "stream/ammu_a.APM", + "stream/ammu_b.APM", + "stream/ammu_c.APM", + "stream/door_1.APM", + "stream/door_2.APM", + "stream/door_3.APM", + "stream/door_4.APM", + "stream/door_5.APM", + "stream/door_6.APM", + "stream/t3_a.APM", + "stream/t3_b.APM", + "stream/t3_c.APM", + "stream/k1_b.APM", + "stream/cat1.APM" +}; \ No newline at end of file diff --git a/src/audio/sampman_miles.cpp b/src/liberty/audio/sampman_miles.cpp similarity index 100% rename from src/audio/sampman_miles.cpp rename to src/liberty/audio/sampman_miles.cpp diff --git a/src/audio/sampman_null.cpp b/src/liberty/audio/sampman_null.cpp similarity index 100% rename from src/audio/sampman_null.cpp rename to src/liberty/audio/sampman_null.cpp diff --git a/src/audio/sampman_oal.cpp b/src/liberty/audio/sampman_oal.cpp similarity index 100% rename from src/audio/sampman_oal.cpp rename to src/liberty/audio/sampman_oal.cpp diff --git a/src/audio/soundlist.h b/src/liberty/audio/soundlist.h similarity index 100% rename from src/audio/soundlist.h rename to src/liberty/audio/soundlist.h diff --git a/src/buildings/Building.cpp b/src/liberty/buildings/Building.cpp similarity index 100% rename from src/buildings/Building.cpp rename to src/liberty/buildings/Building.cpp diff --git a/src/buildings/Building.h b/src/liberty/buildings/Building.h similarity index 100% rename from src/buildings/Building.h rename to src/liberty/buildings/Building.h diff --git a/src/buildings/Solid.h b/src/liberty/buildings/Solid.h similarity index 100% rename from src/buildings/Solid.h rename to src/liberty/buildings/Solid.h diff --git a/src/buildings/Treadable.cpp b/src/liberty/buildings/Treadable.cpp similarity index 100% rename from src/buildings/Treadable.cpp rename to src/liberty/buildings/Treadable.cpp diff --git a/src/buildings/Treadable.h b/src/liberty/buildings/Treadable.h similarity index 100% rename from src/buildings/Treadable.h rename to src/liberty/buildings/Treadable.h diff --git a/src/collision/ColBox.cpp b/src/liberty/collision/ColBox.cpp similarity index 100% rename from src/collision/ColBox.cpp rename to src/liberty/collision/ColBox.cpp diff --git a/src/collision/ColBox.h b/src/liberty/collision/ColBox.h similarity index 100% rename from src/collision/ColBox.h rename to src/liberty/collision/ColBox.h diff --git a/src/collision/ColLine.cpp b/src/liberty/collision/ColLine.cpp similarity index 100% rename from src/collision/ColLine.cpp rename to src/liberty/collision/ColLine.cpp diff --git a/src/collision/ColLine.h b/src/liberty/collision/ColLine.h similarity index 100% rename from src/collision/ColLine.h rename to src/liberty/collision/ColLine.h diff --git a/src/collision/ColModel.cpp b/src/liberty/collision/ColModel.cpp similarity index 100% rename from src/collision/ColModel.cpp rename to src/liberty/collision/ColModel.cpp diff --git a/src/collision/ColModel.h b/src/liberty/collision/ColModel.h similarity index 100% rename from src/collision/ColModel.h rename to src/liberty/collision/ColModel.h diff --git a/src/collision/ColPoint.cpp b/src/liberty/collision/ColPoint.cpp similarity index 100% rename from src/collision/ColPoint.cpp rename to src/liberty/collision/ColPoint.cpp diff --git a/src/collision/ColPoint.h b/src/liberty/collision/ColPoint.h similarity index 100% rename from src/collision/ColPoint.h rename to src/liberty/collision/ColPoint.h diff --git a/src/collision/ColSphere.cpp b/src/liberty/collision/ColSphere.cpp similarity index 100% rename from src/collision/ColSphere.cpp rename to src/liberty/collision/ColSphere.cpp diff --git a/src/collision/ColSphere.h b/src/liberty/collision/ColSphere.h similarity index 100% rename from src/collision/ColSphere.h rename to src/liberty/collision/ColSphere.h diff --git a/src/collision/ColTriangle.cpp b/src/liberty/collision/ColTriangle.cpp similarity index 92% rename from src/collision/ColTriangle.cpp rename to src/liberty/collision/ColTriangle.cpp index c025924a..f90f393c 100644 --- a/src/collision/ColTriangle.cpp +++ b/src/liberty/collision/ColTriangle.cpp @@ -2,7 +2,7 @@ #include "ColTriangle.h" void -CColTriangle::Set(const CompressedVector *, int a, int b, int c, uint8 surf, uint8 piece) +CColTriangle::Set(const CompressedVector *, uint16 a, uint16 b, uint16 c, uint8 surf, uint8 piece) { this->a = a; this->b = b; diff --git a/src/collision/ColTriangle.h b/src/liberty/collision/ColTriangle.h similarity index 94% rename from src/collision/ColTriangle.h rename to src/liberty/collision/ColTriangle.h index 03299463..500f8baf 100644 --- a/src/collision/ColTriangle.h +++ b/src/liberty/collision/ColTriangle.h @@ -18,7 +18,7 @@ struct CColTriangle uint16 c; uint8 surface; - void Set(const CompressedVector *v, int a, int b, int c, uint8 surf, uint8 piece); + void Set(const CompressedVector *v, uint16 a, uint16 b, uint16 c, uint8 surf, uint8 piece); }; struct CColTrianglePlane diff --git a/src/collision/Collision.cpp b/src/liberty/collision/Collision.cpp similarity index 100% rename from src/collision/Collision.cpp rename to src/liberty/collision/Collision.cpp diff --git a/src/collision/Collision.h b/src/liberty/collision/Collision.h similarity index 100% rename from src/collision/Collision.h rename to src/liberty/collision/Collision.h diff --git a/src/collision/CompressedVector.h b/src/liberty/collision/CompressedVector.h similarity index 85% rename from src/collision/CompressedVector.h rename to src/liberty/collision/CompressedVector.h index caf07664..3945ebf2 100644 --- a/src/collision/CompressedVector.h +++ b/src/liberty/collision/CompressedVector.h @@ -5,7 +5,7 @@ struct CompressedVector #ifdef COMPRESSED_COL_VECTORS int16 x, y, z; CVector Get(void) const { return CVector(x, y, z)/128.0f; }; - void Set(float x, float y, float z) { this->x = lroundf(x*128.0f); this->y = lroundf(y*128.0f); this->z = lroundf(z*128.0f); }; + void SetFixed(int16 x, int16 y, int16 z) { this->x = x; this->y = y; this->z = z; }; #ifdef GTA_PS2 void Unpack(uint128 &qword) const { __asm__ volatile ( diff --git a/src/collision/TempColModels.cpp b/src/liberty/collision/TempColModels.cpp similarity index 100% rename from src/collision/TempColModels.cpp rename to src/liberty/collision/TempColModels.cpp diff --git a/src/collision/TempColModels.h b/src/liberty/collision/TempColModels.h similarity index 100% rename from src/collision/TempColModels.h rename to src/liberty/collision/TempColModels.h diff --git a/src/collision/VuCollision.cpp b/src/liberty/collision/VuCollision.cpp similarity index 100% rename from src/collision/VuCollision.cpp rename to src/liberty/collision/VuCollision.cpp diff --git a/src/collision/VuCollision.h b/src/liberty/collision/VuCollision.h similarity index 100% rename from src/collision/VuCollision.h rename to src/liberty/collision/VuCollision.h diff --git a/src/collision/vu0Collision.dsm b/src/liberty/collision/vu0Collision.dsm similarity index 100% rename from src/collision/vu0Collision.dsm rename to src/liberty/collision/vu0Collision.dsm diff --git a/src/collision/vu0Collision_1.s b/src/liberty/collision/vu0Collision_1.s similarity index 100% rename from src/collision/vu0Collision_1.s rename to src/liberty/collision/vu0Collision_1.s diff --git a/src/collision/vu0Collision_2.s b/src/liberty/collision/vu0Collision_2.s similarity index 100% rename from src/collision/vu0Collision_2.s rename to src/liberty/collision/vu0Collision_2.s diff --git a/src/control/AutoPilot.cpp b/src/liberty/control/AutoPilot.cpp similarity index 100% rename from src/control/AutoPilot.cpp rename to src/liberty/control/AutoPilot.cpp diff --git a/src/control/AutoPilot.h b/src/liberty/control/AutoPilot.h similarity index 100% rename from src/control/AutoPilot.h rename to src/liberty/control/AutoPilot.h diff --git a/src/control/Bridge.cpp b/src/liberty/control/Bridge.cpp similarity index 100% rename from src/control/Bridge.cpp rename to src/liberty/control/Bridge.cpp diff --git a/src/control/Bridge.h b/src/liberty/control/Bridge.h similarity index 100% rename from src/control/Bridge.h rename to src/liberty/control/Bridge.h diff --git a/src/control/CarAI.cpp b/src/liberty/control/CarAI.cpp similarity index 100% rename from src/control/CarAI.cpp rename to src/liberty/control/CarAI.cpp diff --git a/src/control/CarAI.h b/src/liberty/control/CarAI.h similarity index 100% rename from src/control/CarAI.h rename to src/liberty/control/CarAI.h diff --git a/src/control/CarCtrl.cpp b/src/liberty/control/CarCtrl.cpp similarity index 100% rename from src/control/CarCtrl.cpp rename to src/liberty/control/CarCtrl.cpp diff --git a/src/control/CarCtrl.h b/src/liberty/control/CarCtrl.h similarity index 100% rename from src/control/CarCtrl.h rename to src/liberty/control/CarCtrl.h diff --git a/src/control/Curves.cpp b/src/liberty/control/Curves.cpp similarity index 100% rename from src/control/Curves.cpp rename to src/liberty/control/Curves.cpp diff --git a/src/control/Curves.h b/src/liberty/control/Curves.h similarity index 100% rename from src/control/Curves.h rename to src/liberty/control/Curves.h diff --git a/src/control/Darkel.cpp b/src/liberty/control/Darkel.cpp similarity index 100% rename from src/control/Darkel.cpp rename to src/liberty/control/Darkel.cpp diff --git a/src/control/Darkel.h b/src/liberty/control/Darkel.h similarity index 100% rename from src/control/Darkel.h rename to src/liberty/control/Darkel.h diff --git a/src/control/GameLogic.cpp b/src/liberty/control/GameLogic.cpp similarity index 100% rename from src/control/GameLogic.cpp rename to src/liberty/control/GameLogic.cpp diff --git a/src/control/GameLogic.h b/src/liberty/control/GameLogic.h similarity index 100% rename from src/control/GameLogic.h rename to src/liberty/control/GameLogic.h diff --git a/src/control/Garages.cpp b/src/liberty/control/Garages.cpp similarity index 100% rename from src/control/Garages.cpp rename to src/liberty/control/Garages.cpp diff --git a/src/control/Garages.h b/src/liberty/control/Garages.h similarity index 100% rename from src/control/Garages.h rename to src/liberty/control/Garages.h diff --git a/src/control/NameGrid.cpp b/src/liberty/control/NameGrid.cpp similarity index 100% rename from src/control/NameGrid.cpp rename to src/liberty/control/NameGrid.cpp diff --git a/src/control/NameGrid.h b/src/liberty/control/NameGrid.h similarity index 100% rename from src/control/NameGrid.h rename to src/liberty/control/NameGrid.h diff --git a/src/control/OnscreenTimer.cpp b/src/liberty/control/OnscreenTimer.cpp similarity index 100% rename from src/control/OnscreenTimer.cpp rename to src/liberty/control/OnscreenTimer.cpp diff --git a/src/control/OnscreenTimer.h b/src/liberty/control/OnscreenTimer.h similarity index 100% rename from src/control/OnscreenTimer.h rename to src/liberty/control/OnscreenTimer.h diff --git a/src/control/PathFind.cpp b/src/liberty/control/PathFind.cpp similarity index 100% rename from src/control/PathFind.cpp rename to src/liberty/control/PathFind.cpp diff --git a/src/control/PathFind.h b/src/liberty/control/PathFind.h similarity index 100% rename from src/control/PathFind.h rename to src/liberty/control/PathFind.h diff --git a/src/control/Phones.cpp b/src/liberty/control/Phones.cpp similarity index 100% rename from src/control/Phones.cpp rename to src/liberty/control/Phones.cpp diff --git a/src/control/Phones.h b/src/liberty/control/Phones.h similarity index 100% rename from src/control/Phones.h rename to src/liberty/control/Phones.h diff --git a/src/control/Pickups.cpp b/src/liberty/control/Pickups.cpp similarity index 100% rename from src/control/Pickups.cpp rename to src/liberty/control/Pickups.cpp diff --git a/src/control/Pickups.h b/src/liberty/control/Pickups.h similarity index 100% rename from src/control/Pickups.h rename to src/liberty/control/Pickups.h diff --git a/src/control/PowerPoints.cpp b/src/liberty/control/PowerPoints.cpp similarity index 100% rename from src/control/PowerPoints.cpp rename to src/liberty/control/PowerPoints.cpp diff --git a/src/control/PowerPoints.h b/src/liberty/control/PowerPoints.h similarity index 100% rename from src/control/PowerPoints.h rename to src/liberty/control/PowerPoints.h diff --git a/src/control/Record.cpp b/src/liberty/control/Record.cpp similarity index 100% rename from src/control/Record.cpp rename to src/liberty/control/Record.cpp diff --git a/src/control/Record.h b/src/liberty/control/Record.h similarity index 100% rename from src/control/Record.h rename to src/liberty/control/Record.h diff --git a/src/control/Remote.cpp b/src/liberty/control/Remote.cpp similarity index 100% rename from src/control/Remote.cpp rename to src/liberty/control/Remote.cpp diff --git a/src/control/Remote.h b/src/liberty/control/Remote.h similarity index 100% rename from src/control/Remote.h rename to src/liberty/control/Remote.h diff --git a/src/control/Replay.cpp b/src/liberty/control/Replay.cpp similarity index 100% rename from src/control/Replay.cpp rename to src/liberty/control/Replay.cpp diff --git a/src/control/Replay.h b/src/liberty/control/Replay.h similarity index 100% rename from src/control/Replay.h rename to src/liberty/control/Replay.h diff --git a/src/control/Restart.cpp b/src/liberty/control/Restart.cpp similarity index 100% rename from src/control/Restart.cpp rename to src/liberty/control/Restart.cpp diff --git a/src/control/Restart.h b/src/liberty/control/Restart.h similarity index 100% rename from src/control/Restart.h rename to src/liberty/control/Restart.h diff --git a/src/control/RoadBlocks.cpp b/src/liberty/control/RoadBlocks.cpp similarity index 100% rename from src/control/RoadBlocks.cpp rename to src/liberty/control/RoadBlocks.cpp diff --git a/src/control/RoadBlocks.h b/src/liberty/control/RoadBlocks.h similarity index 100% rename from src/control/RoadBlocks.h rename to src/liberty/control/RoadBlocks.h diff --git a/src/control/SceneEdit.cpp b/src/liberty/control/SceneEdit.cpp similarity index 100% rename from src/control/SceneEdit.cpp rename to src/liberty/control/SceneEdit.cpp diff --git a/src/control/SceneEdit.h b/src/liberty/control/SceneEdit.h similarity index 100% rename from src/control/SceneEdit.h rename to src/liberty/control/SceneEdit.h diff --git a/src/control/Script.cpp b/src/liberty/control/Script.cpp similarity index 100% rename from src/control/Script.cpp rename to src/liberty/control/Script.cpp diff --git a/src/control/Script.h b/src/liberty/control/Script.h similarity index 100% rename from src/control/Script.h rename to src/liberty/control/Script.h diff --git a/src/control/Script2.cpp b/src/liberty/control/Script2.cpp similarity index 100% rename from src/control/Script2.cpp rename to src/liberty/control/Script2.cpp diff --git a/src/control/Script3.cpp b/src/liberty/control/Script3.cpp similarity index 100% rename from src/control/Script3.cpp rename to src/liberty/control/Script3.cpp diff --git a/src/control/Script4.cpp b/src/liberty/control/Script4.cpp similarity index 99% rename from src/control/Script4.cpp rename to src/liberty/control/Script4.cpp index 4e798be3..dc592d8f 100644 --- a/src/control/Script4.cpp +++ b/src/liberty/control/Script4.cpp @@ -1721,8 +1721,8 @@ int8 CRunningScript::ProcessCommands900To999(int32 command) return 0; int attempts; int model = -1; - int index = CGeneral::GetRandomNumberInRange(0, 50); - for (attempts = 0; attempts < 50; attempts++) { + int index = CGeneral::GetRandomNumberInRange(0, MAXVEHICLESLOADED); + for (attempts = 0; attempts < MAXVEHICLESLOADED; attempts++) { if (model != -1) break; model = CStreaming::ms_vehiclesLoaded[index]; diff --git a/src/control/Script5.cpp b/src/liberty/control/Script5.cpp similarity index 100% rename from src/control/Script5.cpp rename to src/liberty/control/Script5.cpp diff --git a/src/control/Script6.cpp b/src/liberty/control/Script6.cpp similarity index 100% rename from src/control/Script6.cpp rename to src/liberty/control/Script6.cpp diff --git a/src/control/ScriptCommands.h b/src/liberty/control/ScriptCommands.h similarity index 100% rename from src/control/ScriptCommands.h rename to src/liberty/control/ScriptCommands.h diff --git a/src/control/ScriptDebug.cpp b/src/liberty/control/ScriptDebug.cpp similarity index 100% rename from src/control/ScriptDebug.cpp rename to src/liberty/control/ScriptDebug.cpp diff --git a/src/control/TrafficLights.cpp b/src/liberty/control/TrafficLights.cpp similarity index 100% rename from src/control/TrafficLights.cpp rename to src/liberty/control/TrafficLights.cpp diff --git a/src/control/TrafficLights.h b/src/liberty/control/TrafficLights.h similarity index 100% rename from src/control/TrafficLights.h rename to src/liberty/control/TrafficLights.h diff --git a/src/core/Accident.cpp b/src/liberty/core/Accident.cpp similarity index 100% rename from src/core/Accident.cpp rename to src/liberty/core/Accident.cpp diff --git a/src/core/Accident.h b/src/liberty/core/Accident.h similarity index 100% rename from src/core/Accident.h rename to src/liberty/core/Accident.h diff --git a/src/core/AnimViewer.cpp b/src/liberty/core/AnimViewer.cpp similarity index 100% rename from src/core/AnimViewer.cpp rename to src/liberty/core/AnimViewer.cpp diff --git a/src/core/AnimViewer.h b/src/liberty/core/AnimViewer.h similarity index 100% rename from src/core/AnimViewer.h rename to src/liberty/core/AnimViewer.h diff --git a/src/core/Cam.cpp b/src/liberty/core/Cam.cpp similarity index 100% rename from src/core/Cam.cpp rename to src/liberty/core/Cam.cpp diff --git a/src/core/Camera.cpp b/src/liberty/core/Camera.cpp similarity index 100% rename from src/core/Camera.cpp rename to src/liberty/core/Camera.cpp diff --git a/src/core/Camera.h b/src/liberty/core/Camera.h similarity index 100% rename from src/core/Camera.h rename to src/liberty/core/Camera.h diff --git a/src/core/CdStream.cpp b/src/liberty/core/CdStream.cpp similarity index 100% rename from src/core/CdStream.cpp rename to src/liberty/core/CdStream.cpp diff --git a/src/core/CdStream.h b/src/liberty/core/CdStream.h similarity index 86% rename from src/core/CdStream.h rename to src/liberty/core/CdStream.h index 420a3c8d..0a84d01a 100644 --- a/src/core/CdStream.h +++ b/src/liberty/core/CdStream.h @@ -1,4 +1,5 @@ #pragma once +#include #define CDSTREAM_SECTOR_SIZE 2048 @@ -43,7 +44,14 @@ char *CdStreamGetImageName(int32 cd); void CdStreamRemoveImages(void); int32 CdStreamGetNumImages(void); -void CdStreamQueueAudioRead(int fd, void* pBuffer, size_t bytes, size_t seek); +struct AudioReadCmd { + void* dest; + int fd; + size_t size; + size_t seek; + std::function callback; +}; +void CdStreamQueueAudioRead(int fd, void* pBuffer, size_t bytes, size_t seek, std::function callback = nullptr); void CdStreamDiscardAudioRead(int fd); #ifdef FLUSHABLE_STREAMING diff --git a/src/core/CdStreamDC.cpp b/src/liberty/core/CdStreamDC.cpp similarity index 98% rename from src/core/CdStreamDC.cpp rename to src/liberty/core/CdStreamDC.cpp index 04c02327..ab83f872 100644 --- a/src/core/CdStreamDC.cpp +++ b/src/liberty/core/CdStreamDC.cpp @@ -26,12 +26,6 @@ #include "CdStream.h" #include "rwcore.h" #include "MemoryMgr.h" -struct AudioReadCmd { - void* dest; - int fd; - size_t size; - size_t seek; -}; #define CDDEBUG(f, ...) debug ("%s: " f "\n", "cdvd_stream", ## __VA_ARGS__) #define CDTRACE(f, ...) printf("%s: " f "\n", "cdvd_stream", ## __VA_ARGS__) @@ -489,8 +483,16 @@ std::vector pendingAudioReads; std::mutex pendingAudioReadsMutex; #endif // Will replace a previous read request for the same file descriptor -void CdStreamQueueAudioRead(int fd, void* pBuffer, size_t bytes, size_t seek) { +void CdStreamQueueAudioRead(int fd, void* pBuffer, size_t bytes, size_t seek, std::function callback) { AudioReadCmd cmd = { pBuffer, fd, bytes, seek}; + if (!callback) { + cmd.callback = [](AudioReadCmd* cmd){ + lseek(cmd->fd, cmd->seek, SEEK_SET); + read(cmd->fd, cmd->dest, cmd->size); + }; + } else { + cmd.callback = callback; + } { #if !defined(DC_SH4) std::lock_guard lock(pendingAudioReadsMutex); @@ -514,6 +516,7 @@ void CdStreamQueueAudioRead(int fd, void* pBuffer, size_t bytes, size_t seek) { sem_post(gCdStreamSema); } + void CdStreamDiscardAudioRead(int fd) { #if !defined(DC_SH4) std::lock_guard lock(pendingAudioReadsMutex); @@ -564,8 +567,7 @@ int read_loop(int fd, void* pBuffer, size_t bytes) { total_read += read_bytes; auto cmd = CdStreamNextAudioRead(); while (cmd.fd != -1) { - lseek(cmd.fd, cmd.seek, SEEK_SET); - read(cmd.fd, cmd.dest, cmd.size); + cmd.callback(&cmd); cmd = CdStreamNextAudioRead(); } } @@ -581,8 +583,7 @@ void *CdStreamThread(void *param) auto cmd = CdStreamNextAudioRead(); while (cmd.fd != -1) { - lseek(cmd.fd, cmd.seek, SEEK_SET); - read(cmd.fd, cmd.dest, cmd.size); + cmd.callback(&cmd); cmd = CdStreamNextAudioRead(); } diff --git a/src/core/CdStreamPosix.cpp b/src/liberty/core/CdStreamPosix.cpp similarity index 100% rename from src/core/CdStreamPosix.cpp rename to src/liberty/core/CdStreamPosix.cpp diff --git a/src/core/Clock.cpp b/src/liberty/core/Clock.cpp similarity index 100% rename from src/core/Clock.cpp rename to src/liberty/core/Clock.cpp diff --git a/src/core/Clock.h b/src/liberty/core/Clock.h similarity index 100% rename from src/core/Clock.h rename to src/liberty/core/Clock.h diff --git a/src/core/ControllerConfig.cpp b/src/liberty/core/ControllerConfig.cpp old mode 100644 new mode 100755 similarity index 87% rename from src/core/ControllerConfig.cpp rename to src/liberty/core/ControllerConfig.cpp index dcaaa8f2..fc9e20e3 --- a/src/core/ControllerConfig.cpp +++ b/src/liberty/core/ControllerConfig.cpp @@ -2054,21 +2054,60 @@ wchar *CControllerConfigManager::GetControllerSettingTextWithOrderNumber(e_Contr static const char* Dreamcast_DPad_Down = "D-Pad Down"; static const char* Dreamcast_DPad_Left = "D-Pad Left"; static const char* Dreamcast_DPad_Right = "D-Pad Right"; - static const char* Dreamcast_LefAnalog_Up = "Joystick Up"; - static const char* Dreamcast_LefAnalog_Down = "Joystick Down"; - static const char* Dreamcast_LefAnalog_Left = "Joystick Left"; - static const char* Dreamcast_LefAnalog_Right = "Joystick Right"; - static const char* Dreamcast_NotAssigned = "Not Assigned"; + static const char* Dreamcast_Stick_Up = "Joystick Up"; + static const char* Dreamcast_Stick_Down = "Joystick Down"; + static const char* Dreamcast_Stick_Left = "Joystick Left"; + static const char* Dreamcast_Stick_Right = "Joystick Right"; + static const char* Dreamcast_VehicleLookLeft = "A + Joystick Left"; + static const char* Dreamcast_VehicleLookRight = "A + Joystick Right"; + static const char* Dreamcast_CenterCamera = "Double click X"; + static const char* Dreamcast_CameraUp = "X + Joystick Up"; + static const char* Dreamcast_CameraDown = "X + Joystick Down"; + static const char* Dreamcast_CameraLeft = "X + Joystick Left"; + static const char* Dreamcast_CameraRight = "X + Joystick Right"; + static const char* Dreamcast_LookBehind = "Right trigger + Left trigger"; + static const char* Dreamcast_NotAssigned = "Not Assigned"; - if (CMenuManager::m_ControlMethod == CONTROL_CLASSIC) // CONTROL_CLASSIC + //Dual sticks + static const char* LefAnalog_Up = "Left Stick Up"; + static const char* LefAnalog_Down = "Left Stick Down"; + static const char* LefAnalog_Left = "Left Stick Left"; + static const char* LefAnalog_Right = "Left Stick Right"; + + static const char* RightAnalog_Up = "Right Stick Up"; + static const char* RightAnalog_Down = "Right Stick Down"; + static const char* RightAnalog_Left = "Right Stick Left"; + static const char* RightAnalog_Right = "Right Stick Right"; + + //Xbox Specific + static const char* Xbox_VehicleLookLeft = "LB"; + static const char* Xbox_VehicleLookRight = "RB"; + static const char* Xbox_VehicleLookBehind = "RB + LB"; + static const char* Xbox_Back = "Back"; + + + //PS2 Specific + static const char* Dreamcast_PS2_VehicleLookLeft = "B + Joystick Left"; + static const char* Dreamcast_PS2_VehicleLookRight = "B + Joystick Right"; + static const char* PS2_L1 = "L1"; + static const char* PS2_R1 = "R1"; + static const char* PS2_L2 = "L2"; + static const char* PS2_R2 = "R2"; + static const char* PS2_Cross = "Cross"; + static const char* PS2_Square = "Square"; + static const char* PS2_Circle = "Circle"; + static const char* PS2_Triangle = "Triangle"; + + + if ((CPad::GetPad(0)->Mode == 0) && (CPad::GetPad(0)->IsDualAnalog == 0)) // Standard DC controller, Xbox like bindings { switch (action) { case VEHICLE_LOOKLEFT: - for (int i = 0; (ActionText[i] = Dreamcast_DPad_Left[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Dreamcast_VehicleLookLeft[i]) != '\0' && i < iLimitCopy; i++); break; case VEHICLE_LOOKRIGHT: - for (int i = 0; (ActionText[i] = Dreamcast_DPad_Right[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Dreamcast_VehicleLookRight[i]) != '\0' && i < iLimitCopy; i++); break; case VEHICLE_HORN: for (int i = 0; (ActionText[i] = Dreamcast_DPad_Down[i]) != '\0' && i < iLimitCopy; i++); @@ -2083,28 +2122,28 @@ wchar *CControllerConfigManager::GetControllerSettingTextWithOrderNumber(e_Contr for (int i = 0; (ActionText[i] = Dreamcast_RightTrigger[i]) != '\0' && i < iLimitCopy; i++); break; case VEHICLE_CHANGE_RADIO_STATION: - for (int i = 0; (ActionText[i] = Dreamcast_DPad_Right[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Left[i]) != '\0' && i < iLimitCopy; i++); break; case VEHICLE_BRAKE: for (int i = 0; (ActionText[i] = Dreamcast_LeftTrigger[i]) != '\0' && i < iLimitCopy; i++); break; case TOGGLE_SUBMISSIONS: - for (int i = 0; (ActionText[i] = Dreamcast_DPad_Left[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Right[i]) != '\0' && i < iLimitCopy; i++); break; case GO_LEFT: - for (int i = 0; (ActionText[i] = Dreamcast_LefAnalog_Left[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Dreamcast_Stick_Left[i]) != '\0' && i < iLimitCopy; i++); break; case GO_RIGHT: - for (int i = 0; (ActionText[i] = Dreamcast_LefAnalog_Right[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Dreamcast_Stick_Right[i]) != '\0' && i < iLimitCopy; i++); break; case GO_FORWARD: - for (int i = 0; (ActionText[i] = Dreamcast_LefAnalog_Up[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Dreamcast_Stick_Up[i]) != '\0' && i < iLimitCopy; i++); break; case GO_BACK: - for (int i = 0; (ActionText[i] = Dreamcast_LefAnalog_Down[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Dreamcast_Stick_Down[i]) != '\0' && i < iLimitCopy; i++); break; case PED_LOOKBEHIND: - for (int i = 0; (ActionText[i] = Dreamcast_LefAnalog_Down[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Down[i]) != '\0' && i < iLimitCopy; i++); break; case PED_FIREWEAPON: for (int i = 0; (ActionText[i] = Dreamcast_RightTrigger[i]) != '\0' && i < iLimitCopy; i++); @@ -2122,22 +2161,22 @@ wchar *CControllerConfigManager::GetControllerSettingTextWithOrderNumber(e_Contr for (int i = 0; (ActionText[i] = Dreamcast_LeftTrigger[i]) != '\0' && i < iLimitCopy; i++); break; case PED_JUMPING: - for (int i = 0; (ActionText[i] = Dreamcast_X[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Dreamcast_B[i]) != '\0' && i < iLimitCopy; i++); break; case PED_SPRINT: for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); break; case PED_CYCLE_TARGET_LEFT: - for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Left[i]) != '\0' && i < iLimitCopy; i++); break; case PED_CYCLE_TARGET_RIGHT: - for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Right[i]) != '\0' && i < iLimitCopy; i++); break; case PED_CENTER_CAMERA_BEHIND_PLAYER: - for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Dreamcast_CenterCamera[i]) != '\0' && i < iLimitCopy; i++); break; case PED_SNIPER_ZOOM_IN: - for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Dreamcast_X[i]) != '\0' && i < iLimitCopy; i++); break; case PED_SNIPER_ZOOM_OUT: for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); @@ -2170,7 +2209,7 @@ wchar *CControllerConfigManager::GetControllerSettingTextWithOrderNumber(e_Contr for (int i = 0; (ActionText[i] = Dreamcast_DPad_Up[i]) != '\0' && i < iLimitCopy; i++); break; case VEHICLE_LOOKBEHIND: - for (int i = 0; (ActionText[i] = Dreamcast_DPad_Down[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Dreamcast_LookBehind[i]) != '\0' && i < iLimitCopy; i++); break; case NETWORK_TALK: // Not Used on Dreamcast for (int i = 0; (ActionText[i] = Dreamcast_NotAssigned[i]) != '\0' && i < iLimitCopy; i++); @@ -2198,15 +2237,15 @@ wchar *CControllerConfigManager::GetControllerSettingTextWithOrderNumber(e_Contr break; } } - else // CONTROL_STANDARD + if ((CPad::GetPad(0)->Mode == 0) && (CPad::GetPad(0)->IsDualAnalog == 1)) // Dual analog, Xbox like bindings { switch (action) { case VEHICLE_LOOKLEFT: - for (int i = 0; (ActionText[i] = Dreamcast_DPad_Left[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Xbox_VehicleLookLeft[i]) != '\0' && i < iLimitCopy; i++); break; case VEHICLE_LOOKRIGHT: - for (int i = 0; (ActionText[i] = Dreamcast_DPad_Right[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Xbox_VehicleLookRight[i]) != '\0' && i < iLimitCopy; i++); break; case VEHICLE_HORN: for (int i = 0; (ActionText[i] = Dreamcast_DPad_Down[i]) != '\0' && i < iLimitCopy; i++); @@ -2230,19 +2269,19 @@ wchar *CControllerConfigManager::GetControllerSettingTextWithOrderNumber(e_Contr for (int i = 0; (ActionText[i] = Dreamcast_DPad_Left[i]) != '\0' && i < iLimitCopy; i++); break; case GO_LEFT: - for (int i = 0; (ActionText[i] = Dreamcast_LefAnalog_Left[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = LefAnalog_Left[i]) != '\0' && i < iLimitCopy; i++); break; case GO_RIGHT: - for (int i = 0; (ActionText[i] = Dreamcast_LefAnalog_Right[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = LefAnalog_Right[i]) != '\0' && i < iLimitCopy; i++); break; case GO_FORWARD: - for (int i = 0; (ActionText[i] = Dreamcast_LefAnalog_Up[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = LefAnalog_Up[i]) != '\0' && i < iLimitCopy; i++); break; case GO_BACK: - for (int i = 0; (ActionText[i] = Dreamcast_LefAnalog_Down[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = LefAnalog_Down[i]) != '\0' && i < iLimitCopy; i++); break; case PED_LOOKBEHIND: - for (int i = 0; (ActionText[i] = Dreamcast_LefAnalog_Down[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Down[i]) != '\0' && i < iLimitCopy; i++); break; case PED_FIREWEAPON: for (int i = 0; (ActionText[i] = Dreamcast_RightTrigger[i]) != '\0' && i < iLimitCopy; i++); @@ -2266,17 +2305,155 @@ wchar *CControllerConfigManager::GetControllerSettingTextWithOrderNumber(e_Contr for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); break; case PED_CYCLE_TARGET_LEFT: - for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Left[i]) != '\0' && i < iLimitCopy; i++); break; case PED_CYCLE_TARGET_RIGHT: - for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Right[i]) != '\0' && i < iLimitCopy; i++); break; case PED_CENTER_CAMERA_BEHIND_PLAYER: - for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + for (int i = 0; (ActionText[i] = Dreamcast_CenterCamera[i]) != '\0' && i < iLimitCopy; i++); break; case PED_SNIPER_ZOOM_IN: + for (int i = 0; (ActionText[i] = Dreamcast_X[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_SNIPER_ZOOM_OUT: for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); break; + case PED_1RST_PERSON_LOOK_LEFT: + for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_1RST_PERSON_LOOK_RIGHT: + for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_1RST_PERSON_LOOK_UP: + for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_1RST_PERSON_LOOK_DOWN: + for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_TURRETLEFT: + for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_TURRETRIGHT: + for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_TURRETUP: + for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_TURRETDOWN: + for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + break; + case CAMERA_CHANGE_VIEW_ALL_SITUATIONS: + for (int i = 0; (ActionText[i] = Xbox_Back[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_LOOKBEHIND: + for (int i = 0; (ActionText[i] = Xbox_VehicleLookBehind[i]) != '\0' && i < iLimitCopy; i++); + break; + case NETWORK_TALK: // Not Used on Dreamcast + for (int i = 0; (ActionText[i] = Dreamcast_NotAssigned[i]) != '\0' && i < iLimitCopy; i++); + break; + case _CONTROLLERACTION_36: // What is that??? + for (int i = 0; (ActionText[i] = Dreamcast_NotAssigned[i]) != '\0' && i < iLimitCopy; i++); + break; + case TOGGLE_DPAD: // Not Used on Dreamcast + for (int i = 0; (ActionText[i] = Dreamcast_NotAssigned[i]) != '\0' && i < iLimitCopy; i++); + break; + case SWITCH_DEBUG_CAM_ON: // Not Used on Dreamcast + for (int i = 0; (ActionText[i] = Dreamcast_NotAssigned[i]) != '\0' && i < iLimitCopy; i++); + break; + case TAKE_SCREEN_SHOT: // Not Used on Dreamcast + for (int i = 0; (ActionText[i] = Dreamcast_NotAssigned[i]) != '\0' && i < iLimitCopy; i++); + break; + case SHOW_MOUSE_POINTER_TOGGLE: // Not Used on Dreamcast + for (int i = 0; (ActionText[i] = Dreamcast_NotAssigned[i]) != '\0' && i < iLimitCopy; i++); + break; + case MAX_CONTROLLERACTIONS: // Not Used on Dreamcast + for (int i = 0; (ActionText[i] = Dreamcast_NotAssigned[i]) != '\0' && i < iLimitCopy; i++); + break; + default: + for (int i = 0; (ActionText[i] = Dreamcast_NotAssigned[i]) != '\0' && i < iLimitCopy; i++); + break; + } + } + if ((CPad::GetPad(0)->Mode == 1) && (CPad::GetPad(0)->IsDualAnalog == 0)) // Standard DC controller, PS2 like bindings + { + switch (action) + { + case VEHICLE_LOOKLEFT: + for (int i = 0; (ActionText[i] = Dreamcast_PS2_VehicleLookLeft[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_LOOKRIGHT: + for (int i = 0; (ActionText[i] = Dreamcast_PS2_VehicleLookRight[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_HORN: + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Down[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_HANDBRAKE: + for (int i = 0; (ActionText[i] = Dreamcast_RightTrigger[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_ENTER_EXIT: + for (int i = 0; (ActionText[i] = Dreamcast_Y[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_ACCELERATE: + for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_CHANGE_RADIO_STATION: + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Left[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_BRAKE: + for (int i = 0; (ActionText[i] = Dreamcast_X[i]) != '\0' && i < iLimitCopy; i++); + break; + case TOGGLE_SUBMISSIONS: + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Right[i]) != '\0' && i < iLimitCopy; i++); + break; + case GO_LEFT: + for (int i = 0; (ActionText[i] = Dreamcast_Stick_Left[i]) != '\0' && i < iLimitCopy; i++); + break; + case GO_RIGHT: + for (int i = 0; (ActionText[i] = Dreamcast_Stick_Right[i]) != '\0' && i < iLimitCopy; i++); + break; + case GO_FORWARD: + for (int i = 0; (ActionText[i] = Dreamcast_Stick_Up[i]) != '\0' && i < iLimitCopy; i++); + break; + case GO_BACK: + for (int i = 0; (ActionText[i] = Dreamcast_Stick_Down[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_LOOKBEHIND: + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Down[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_FIREWEAPON: + for (int i = 0; (ActionText[i] = Dreamcast_B[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_FIREWEAPON: + for (int i = 0; (ActionText[i] = Dreamcast_B[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_CYCLE_WEAPON_LEFT: + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Left[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_CYCLE_WEAPON_RIGHT: + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Right[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_LOCK_TARGET: + for (int i = 0; (ActionText[i] = Dreamcast_RightTrigger[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_JUMPING: + for (int i = 0; (ActionText[i] = Dreamcast_X[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_SPRINT: + for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_CYCLE_TARGET_LEFT: + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Left[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_CYCLE_TARGET_RIGHT: + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Right[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_CENTER_CAMERA_BEHIND_PLAYER: + for (int i = 0; (ActionText[i] = Dreamcast_CenterCamera[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_SNIPER_ZOOM_IN: + for (int i = 0; (ActionText[i] = Dreamcast_X[i]) != '\0' && i < iLimitCopy; i++); + break; case PED_SNIPER_ZOOM_OUT: for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); break; @@ -2308,8 +2485,147 @@ wchar *CControllerConfigManager::GetControllerSettingTextWithOrderNumber(e_Contr for (int i = 0; (ActionText[i] = Dreamcast_DPad_Up[i]) != '\0' && i < iLimitCopy; i++); break; case VEHICLE_LOOKBEHIND: + for (int i = 0; (ActionText[i] = Dreamcast_LookBehind[i]) != '\0' && i < iLimitCopy; i++); + break; + case NETWORK_TALK: // Not Used on Dreamcast + for (int i = 0; (ActionText[i] = Dreamcast_NotAssigned[i]) != '\0' && i < iLimitCopy; i++); + break; + case _CONTROLLERACTION_36: // What is that??? + for (int i = 0; (ActionText[i] = Dreamcast_NotAssigned[i]) != '\0' && i < iLimitCopy; i++); + break; + case TOGGLE_DPAD: // Not Used on Dreamcast + for (int i = 0; (ActionText[i] = Dreamcast_NotAssigned[i]) != '\0' && i < iLimitCopy; i++); + break; + case SWITCH_DEBUG_CAM_ON: // Not Used on Dreamcast + for (int i = 0; (ActionText[i] = Dreamcast_NotAssigned[i]) != '\0' && i < iLimitCopy; i++); + break; + case TAKE_SCREEN_SHOT: // Not Used on Dreamcast + for (int i = 0; (ActionText[i] = Dreamcast_NotAssigned[i]) != '\0' && i < iLimitCopy; i++); + break; + case SHOW_MOUSE_POINTER_TOGGLE: // Not Used on Dreamcast + for (int i = 0; (ActionText[i] = Dreamcast_NotAssigned[i]) != '\0' && i < iLimitCopy; i++); + break; + case MAX_CONTROLLERACTIONS: // Not Used on Dreamcast + for (int i = 0; (ActionText[i] = Dreamcast_NotAssigned[i]) != '\0' && i < iLimitCopy; i++); + break; + default: + for (int i = 0; (ActionText[i] = Dreamcast_NotAssigned[i]) != '\0' && i < iLimitCopy; i++); + break; + } + } + + if ((CPad::GetPad(0)->Mode == 1) && (CPad::GetPad(0)->IsDualAnalog == 1)) // Dual Stick, PS2 like bindings + { + switch (action) + { + case VEHICLE_LOOKLEFT: + for (int i = 0; (ActionText[i] = PS2_L2[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_LOOKRIGHT: + for (int i = 0; (ActionText[i] = PS2_R2[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_HORN: for (int i = 0; (ActionText[i] = Dreamcast_DPad_Down[i]) != '\0' && i < iLimitCopy; i++); break; + case VEHICLE_HANDBRAKE: + for (int i = 0; (ActionText[i] = PS2_R1[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_ENTER_EXIT: + for (int i = 0; (ActionText[i] = PS2_Triangle[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_ACCELERATE: + for (int i = 0; (ActionText[i] = PS2_Cross[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_CHANGE_RADIO_STATION: + for (int i = 0; (ActionText[i] = PS2_L1[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_BRAKE: + for (int i = 0; (ActionText[i] = PS2_Square[i]) != '\0' && i < iLimitCopy; i++); + break; + case TOGGLE_SUBMISSIONS: + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Right[i]) != '\0' && i < iLimitCopy; i++); + break; + case GO_LEFT: + for (int i = 0; (ActionText[i] = LefAnalog_Left[i]) != '\0' && i < iLimitCopy; i++); + break; + case GO_RIGHT: + for (int i = 0; (ActionText[i] = LefAnalog_Right[i]) != '\0' && i < iLimitCopy; i++); + break; + case GO_FORWARD: + for (int i = 0; (ActionText[i] = LefAnalog_Up[i]) != '\0' && i < iLimitCopy; i++); + break; + case GO_BACK: + for (int i = 0; (ActionText[i] = LefAnalog_Down[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_LOOKBEHIND: + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Down[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_FIREWEAPON: + for (int i = 0; (ActionText[i] = PS2_Circle[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_FIREWEAPON: + for (int i = 0; (ActionText[i] = PS2_Circle[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_CYCLE_WEAPON_LEFT: + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Left[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_CYCLE_WEAPON_RIGHT: + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Right[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_LOCK_TARGET: + for (int i = 0; (ActionText[i] = PS2_R1[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_JUMPING: + for (int i = 0; (ActionText[i] = PS2_Square[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_SPRINT: + for (int i = 0; (ActionText[i] = PS2_Circle[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_CYCLE_TARGET_LEFT: + for (int i = 0; (ActionText[i] = PS2_L2[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_CYCLE_TARGET_RIGHT: + for (int i = 0; (ActionText[i] = PS2_R2[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_CENTER_CAMERA_BEHIND_PLAYER: + for (int i = 0; (ActionText[i] = Dreamcast_CenterCamera[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_SNIPER_ZOOM_IN: + for (int i = 0; (ActionText[i] = PS2_Square[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_SNIPER_ZOOM_OUT: + for (int i = 0; (ActionText[i] = PS2_Cross[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_1RST_PERSON_LOOK_LEFT: + for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_1RST_PERSON_LOOK_RIGHT: + for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_1RST_PERSON_LOOK_UP: + for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + break; + case PED_1RST_PERSON_LOOK_DOWN: + for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_TURRETLEFT: + for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_TURRETRIGHT: + for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_TURRETUP: + for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_TURRETDOWN: + for (int i = 0; (ActionText[i] = Dreamcast_A[i]) != '\0' && i < iLimitCopy; i++); + break; + case CAMERA_CHANGE_VIEW_ALL_SITUATIONS: + for (int i = 0; (ActionText[i] = Dreamcast_DPad_Up[i]) != '\0' && i < iLimitCopy; i++); + break; + case VEHICLE_LOOKBEHIND: + for (int i = 0; (ActionText[i] = Dreamcast_LookBehind[i]) != '\0' && i < iLimitCopy; i++); + break; case NETWORK_TALK: // Not Used on Dreamcast for (int i = 0; (ActionText[i] = Dreamcast_NotAssigned[i]) != '\0' && i < iLimitCopy; i++); break; diff --git a/src/core/ControllerConfig.h b/src/liberty/core/ControllerConfig.h similarity index 100% rename from src/core/ControllerConfig.h rename to src/liberty/core/ControllerConfig.h diff --git a/src/core/Crime.h b/src/liberty/core/Crime.h similarity index 100% rename from src/core/Crime.h rename to src/liberty/core/Crime.h diff --git a/src/core/Debug.cpp b/src/liberty/core/Debug.cpp similarity index 100% rename from src/core/Debug.cpp rename to src/liberty/core/Debug.cpp diff --git a/src/core/Debug.h b/src/liberty/core/Debug.h similarity index 100% rename from src/core/Debug.h rename to src/liberty/core/Debug.h diff --git a/src/core/Directory.cpp b/src/liberty/core/Directory.cpp similarity index 100% rename from src/core/Directory.cpp rename to src/liberty/core/Directory.cpp diff --git a/src/core/Directory.h b/src/liberty/core/Directory.h similarity index 100% rename from src/core/Directory.h rename to src/liberty/core/Directory.h diff --git a/src/core/EventList.cpp b/src/liberty/core/EventList.cpp similarity index 100% rename from src/core/EventList.cpp rename to src/liberty/core/EventList.cpp diff --git a/src/core/EventList.h b/src/liberty/core/EventList.h similarity index 100% rename from src/core/EventList.h rename to src/liberty/core/EventList.h diff --git a/src/core/FileLoader.cpp b/src/liberty/core/FileLoader.cpp similarity index 98% rename from src/core/FileLoader.cpp rename to src/liberty/core/FileLoader.cpp index 8a362c11..8ebfe11c 100644 --- a/src/core/FileLoader.cpp +++ b/src/liberty/core/FileLoader.cpp @@ -215,7 +215,7 @@ CFileLoader::LoadCollisionFile(const char *filename) fd = CFileMgr::OpenFile(filename, "rb"); while(CFileMgr::Read(fd, (char*)&header, sizeof(header))){ - assert(header.ident == 'LLOC'); + assert(header.ident == 'CLOC'); CFileMgr::Read(fd, (char*)work_buff, header.size); memcpy(modelname, work_buff, 24); @@ -295,14 +295,8 @@ CFileLoader::LoadCollisionModel(uint8 *buf, CColModel &model, char *modelname) model.vertices = (CompressedVector*)RwMalloc(numVertices*sizeof(CompressedVector)); REGISTER_MEMPTR(&model.vertices); for(i = 0; i < numVertices; i++){ - model.vertices[i].Set(*(float*)buf, *(float*)(buf+4), *(float*)(buf+8)); - if(lroundf(Abs(*(float*)buf)) >= 256 || - lroundf(Abs(*(float*)(buf+4))) >= 256 || - lroundf(Abs(*(float*)(buf+8))) >= 256) { - dbglog(DBG_CRITICAL, "%s:Collision volume too big\n", modelname); - assert(false && "Collision volume too big"); - } - buf += 12; + model.vertices[i].SetFixed(*(int16*)buf, *(int16*)(buf+2), *(int16*)(buf+4)); + buf += 6; } }else model.vertices = nil; @@ -313,8 +307,11 @@ CFileLoader::LoadCollisionModel(uint8 *buf, CColModel &model, char *modelname) model.triangles = (CColTriangle*)RwMalloc(model.numTriangles*sizeof(CColTriangle)); REGISTER_MEMPTR(&model.triangles); for(i = 0; i < model.numTriangles; i++){ - model.triangles[i].Set(model.vertices, *(int32*)buf, *(int32*)(buf+4), *(int32*)(buf+8), buf[12], buf[13]); - buf += 16; + model.triangles[i].Set(model.vertices, *(uint16*)buf, *(uint16*)(buf+2), *(uint16*)(buf+4), buf[6], buf[7]); + buf += 8; + assert(model.triangles[i].a < numVertices); + assert(model.triangles[i].b < numVertices); + assert(model.triangles[i].c < numVertices); } }else model.triangles = nil; diff --git a/src/core/FileLoader.h b/src/liberty/core/FileLoader.h similarity index 100% rename from src/core/FileLoader.h rename to src/liberty/core/FileLoader.h diff --git a/src/core/FileMgr.cpp b/src/liberty/core/FileMgr.cpp similarity index 100% rename from src/core/FileMgr.cpp rename to src/liberty/core/FileMgr.cpp diff --git a/src/core/FileMgr.h b/src/liberty/core/FileMgr.h similarity index 100% rename from src/core/FileMgr.h rename to src/liberty/core/FileMgr.h diff --git a/src/core/Fire.cpp b/src/liberty/core/Fire.cpp similarity index 100% rename from src/core/Fire.cpp rename to src/liberty/core/Fire.cpp diff --git a/src/core/Fire.h b/src/liberty/core/Fire.h similarity index 100% rename from src/core/Fire.h rename to src/liberty/core/Fire.h diff --git a/src/core/FrontEndControls.cpp b/src/liberty/core/FrontEndControls.cpp similarity index 100% rename from src/core/FrontEndControls.cpp rename to src/liberty/core/FrontEndControls.cpp diff --git a/src/core/FrontEndControls.h b/src/liberty/core/FrontEndControls.h similarity index 100% rename from src/core/FrontEndControls.h rename to src/liberty/core/FrontEndControls.h diff --git a/src/core/Frontend.cpp b/src/liberty/core/Frontend.cpp similarity index 90% rename from src/core/Frontend.cpp rename to src/liberty/core/Frontend.cpp index 93305817..e6546b30 100644 --- a/src/core/Frontend.cpp +++ b/src/liberty/core/Frontend.cpp @@ -235,12 +235,26 @@ const char* FrontendFilenames[][2] = { {"fe_iconaudio", "" }, {"fe_icondisplay", "" }, {"fe_iconlanguage", "" }, + +#ifdef RW_DC + {"dc_xboxf",""}, + {"dc_xboxd",""}, + {"dc_ps2f",""}, + {"dc_ps2d",""}, + {"xbox_f",""}, + {"xbox_d",""}, + {"ps2_f",""}, + {"ps2_d",""}, + +#else {"fe_controller", "" }, {"fe_controllersh", "" }, {"fe_arrows1", "" }, {"fe_arrows2", "" }, {"fe_arrows3", "" }, {"fe_arrows4", "" }, + +#endif {"fe_radio1", "" }, {"fe_radio2", "" }, {"fe_radio3", "" }, @@ -1118,6 +1132,46 @@ CMenuManager::Draw() #ifdef PS2_SAVE_DIALOG if(!m_bRenderGameInMenu) #endif + +#ifdef RW_DC + if (aScreens[m_nCurrScreen].m_ScreenName[0] != '\0') { + + SET_FONT_FOR_MENU_HEADER + + if (strcmp("FET_AGS", aScreens[m_nCurrScreen].m_ScreenName) == 0) + { + wchar *PageName = nil; + char asciiTemp[32]; + wchar unicodeTemp[64]; + + sprintf(asciiTemp, "CONTROLLER SETUP"); + AsciiToUnicode(asciiTemp, unicodeTemp); + PageName = unicodeTemp; + + CFont::PrintString(PAGE_NAME_X(MENUHEADER_POS_X), SCREEN_SCALE_FROM_BOTTOM(MENUHEADER_POS_Y), PageName); + } + else if (strcmp("FET_CTL", aScreens[m_nCurrScreen].m_ScreenName) == 0) + { + wchar *PageName = nil; + char asciiTemp[32]; + wchar unicodeTemp[64]; + + sprintf(asciiTemp, "KEYBOARD MOUSE SETUP"); + AsciiToUnicode(asciiTemp, unicodeTemp); + PageName = unicodeTemp; + + CFont::PrintString(PAGE_NAME_X(MENUHEADER_POS_X), SCREEN_SCALE_FROM_BOTTOM(MENUHEADER_POS_Y), PageName); + } + else + { + CFont::PrintString(PAGE_NAME_X(MENUHEADER_POS_X), SCREEN_SCALE_FROM_BOTTOM(MENUHEADER_POS_Y), TheText.Get(aScreens[m_nCurrScreen].m_ScreenName)); + } + + // Weird place to put that. + nextYToUse += 24.0f + 10.0f; + } + +#else if (aScreens[m_nCurrScreen].m_ScreenName[0] != '\0') { SET_FONT_FOR_MENU_HEADER @@ -1126,6 +1180,7 @@ CMenuManager::Draw() // Weird place to put that. nextYToUse += 24.0f + 10.0f; } +#endif CFont::SetFontStyle(FONT_LOCALE(FONT_BANK)); CFont::SetScale(MENU_X(MENUACTION_SCALE_MULT * MENU_TEXT_SIZE_X), MENU_Y(MENUACTION_SCALE_MULT * MENU_TEXT_SIZE_Y)); @@ -1380,7 +1435,29 @@ CMenuManager::Draw() sprintf(gString, "FEM_SL%d", i); leftText = TheText.Get(gString); } - } else { + } + else if ((strcmp(aScreens[m_nCurrScreen].m_aEntries[i].m_EntryName, "FET_AGS")) == 0) + { + //wchar *rightText = nil; + char asciiTemp[32]; + wchar unicodeTemp[64]; + + sprintf(asciiTemp, "CONTROLLER SETUP"); + AsciiToUnicode(asciiTemp, unicodeTemp); + leftText = unicodeTemp; + } + else if ((strcmp(aScreens[m_nCurrScreen].m_aEntries[i].m_EntryName, "FET_CTL")) == 0) + { + //wchar *rightText = nil; + char asciiTemp[32]; + wchar unicodeTemp[64]; + + sprintf(asciiTemp, "KEYBOARD MOUSE SETUP"); + AsciiToUnicode(asciiTemp, unicodeTemp); + leftText = unicodeTemp; + } + + else { leftText = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[i].m_EntryName); } @@ -1461,10 +1538,16 @@ CMenuManager::Draw() case MENUACTION_CTRLCONFIG: switch (CPad::GetPad(0)->Mode) { case 0: - rightText = TheText.Get("FEC_CF1"); + //rightText = TheText.Get("FEC_CF1"); + sprintf(asciiTemp, "XBOX LIKE"); + AsciiToUnicode(asciiTemp, unicodeTemp); + rightText = unicodeTemp; break; case 1: - rightText = TheText.Get("FEC_CF2"); + //rightText = TheText.Get("FEC_CF2"); + sprintf(asciiTemp, "PS2 LIKE"); + AsciiToUnicode(asciiTemp, unicodeTemp); + rightText = unicodeTemp; break; case 2: rightText = TheText.Get("FEC_CF3"); @@ -1933,7 +2016,7 @@ CMenuManager::Draw() char strver[200]; wchar ustr[200]; - snprintf(strver, sizeof(strver), "DCA3: %s", getExecutableTag()); + snprintf(strver, sizeof(strver), "dca-liberty: %s", getExecutableTag()); AsciiToUnicode(strver, ustr); CFont::SetScale(MENU_X(MENU_TEXT_SIZE_X*3/4), MENU_Y(MENU_TEXT_SIZE_Y*3/4)); @@ -3725,7 +3808,11 @@ CMenuManager::LoadController(int8 type) } // Unload current textures +#ifdef RW_DC + for (int i = DC_XBOXF; i <= PS2_D; i++) +#else for (int i = FE_CONTROLLER; i <= FE_ARROWS4; i++) +#endif m_aFrontEndSprites[i].Delete(); // Unload txd @@ -3758,7 +3845,11 @@ CMenuManager::LoadController(int8 type) assert(txdSlot != -1); // Load new textures CTxdStore::SetCurrentTxd(txdSlot); +#ifdef RW_DC + for (int i = DC_XBOXF; i <= PS2_D; i++) { +#else for (int i = FE_CONTROLLER; i <= FE_ARROWS4; i++) { +#endif m_aFrontEndSprites[i].SetTexture(FrontendFilenames[i][0], FrontendFilenames[i][1]); m_aFrontEndSprites[i].SetAddressing(rwTEXTUREADDRESSBORDER); } @@ -5388,10 +5479,12 @@ CMenuManager::ProcessButtonPresses(void) #ifdef FIX_BUGS case MENUACTION_CTRLCONFIG: CPad::GetPad(0)->Mode += changeValueBy; - if (CPad::GetPad(0)->Mode > 3) + //if (CPad::GetPad(0)->Mode > 3) + if (CPad::GetPad(0)->Mode > 1) CPad::GetPad(0)->Mode = 0; else if (CPad::GetPad(0)->Mode < 0) - CPad::GetPad(0)->Mode = 3; + //CPad::GetPad(0)->Mode = 3; + CPad::GetPad(0)->Mode = 1; SaveSettings(); DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SETTING_CHANGE, 0); break; @@ -5541,7 +5634,8 @@ CMenuManager::ProcessOnOffMenuOptions() #ifndef FIX_BUGS case MENUACTION_CTRLCONFIG: CPad::GetPad(0)->Mode++; - if (CPad::GetPad(0)->Mode > 3) + //if (CPad::GetPad(0)->Mode > 3) + if (CPad::GetPad(0)->Mode > 1) CPad::GetPad(0)->Mode = 0; DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SETTING_CHANGE, 0); break; @@ -5883,7 +5977,6 @@ CMenuManager::PrintController(void) #define X(f) ((f)*scale + centerX) #define Y(f) ((f)*scale + centerY) - m_aFrontEndSprites[FE_CONTROLLERSH].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X((CONTROLLER_SIZE_X + 4.8f) * scale), MENU_Y((CONTROLLER_SIZE_Y + 4.8f) * scale), CRGBA(0, 0, 0, 255)); m_aFrontEndSprites[FE_CONTROLLER].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, 255)); if (m_DisplayControllerOnFoot) { @@ -6174,7 +6267,373 @@ CMenuManager::PrintController(void) #undef X #undef Y } + +#elif RW_DC +void +CMenuManager::PrintController(void) +{ + // FIX: Originally this function doesn't have StretchX/Y, everything had constant pixel size (due to screen was abandoned early?) + // Also texts and their alignment were very bad, so I tried to make them readable (commented out the original code, and marked the ones I added with X) + + switch (CPad::GetPad(0)->Mode) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + if (m_DisplayControllerOnFoot) + m_aFrontEndSprites[XBOX_F].Draw(MENU_X_LEFT_ALIGNED(65.0f), MENU_Y(75.0f), MENU_X(512.0f), MENU_Y(256.0f), CRGBA(255, 255, 255, 255)); + else + m_aFrontEndSprites[XBOX_D].Draw(MENU_X_LEFT_ALIGNED(65.0f), MENU_Y(75.0f), MENU_X(512.0f), MENU_Y(256.0f), CRGBA(255, 255, 255, 255)); + + } + else + { + if (m_DisplayControllerOnFoot) + m_aFrontEndSprites[DC_XBOXF].Draw(MENU_X_LEFT_ALIGNED(65.0f), MENU_Y(75.0f), MENU_X(512.0f), MENU_Y(256.0f), CRGBA(255, 255, 255, 255)); + else + m_aFrontEndSprites[DC_XBOXD].Draw(MENU_X_LEFT_ALIGNED(65.0f), MENU_Y(75.0f), MENU_X(512.0f), MENU_Y(256.0f), CRGBA(255, 255, 255, 255)); + } + break; + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + if (m_DisplayControllerOnFoot) + m_aFrontEndSprites[PS2_F].Draw(MENU_X_LEFT_ALIGNED(65.0f), MENU_Y(75.0f), MENU_X(512.0f), MENU_Y(256.0f), CRGBA(255, 255, 255, 255)); + else + m_aFrontEndSprites[PS2_D].Draw(MENU_X_LEFT_ALIGNED(65.0f), MENU_Y(75.0f), MENU_X(512.0f), MENU_Y(256.0f), CRGBA(255, 255, 255, 255)); + } + else + { + if (m_DisplayControllerOnFoot) + m_aFrontEndSprites[DC_PS2F].Draw(MENU_X_LEFT_ALIGNED(65.0f), MENU_Y(75.0f), MENU_X(512.0f), MENU_Y(256.0f), CRGBA(255, 255, 255, 255)); + else + m_aFrontEndSprites[DC_PS2D].Draw(MENU_X_LEFT_ALIGNED(65.0f), MENU_Y(75.0f), MENU_X(512.0f), MENU_Y(256.0f), CRGBA(255, 255, 255, 255)); + } + break; + } + + // CFont::SetFontStyle(FONT_LOCALE(FONT_BANK)); // X + + // // CFont::SetScale(0.4f, 0.4f); + // CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE), MENU_Y(SMALLESTTEXT_Y_SCALE)); // X + + // // CFont::SetColor(CRGBA(128, 128, 128, FadeIn(255))); + // CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(255))); // X + // CFont::SetDropShadowPosition(1); // X + // CFont::SetColor(CRGBA(255, 255, 255, FadeIn(255))); // X + + // if (m_DisplayControllerOnFoot) { + // switch (CPad::GetPad(0)->Mode) { + // case 0: + // if (CPad::GetPad(0)->IsDualAnalog) + // { + // CFont::SetRightJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(210.0f), MENU_Y(146.0f), TheText.Get("FEC_CWL")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(185.0f), TheText.Get("FEC_LOF")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(225.0f), TheText.Get("FEC_MOV")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(257.0f), TheText.Get("FEC_MOV")); + // CFont::SetJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(263.0f), MENU_Y(301.0f), TheText.Get("FEC_CAM")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(290.0f), MENU_Y(288.0f), TheText.Get("FEC_PAU")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(344.0f), MENU_Y(146.0f), TheText.Get("FEC_CWR")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(185.0f), TheText.Get("FEC_TAR")); + // CFont::SetRightJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(304.0f), MENU_Y(178.0f), TheText.Get("FEC_JUM")); + // CFont::SetJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(212.0f), TheText.Get("FEC_ENV")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(225.0f), TheText.Get("FEC_ATT")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(238.0f), TheText.Get("FEC_RUN")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(254.0f), TheText.Get("FEC_FPC")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(269.0f), TheText.Get("FEC_LB3")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(282.0f), TheText.Get("FEC_R3")); + // } + // else + // { + // // CFont::SetRightJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(202.0f), MENU_Y(220.0f), TheText.Get("FEC_CWL")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(202.0f), MENU_Y(232.0f), TheText.Get("FEC_LB")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(202.0f), MENU_Y(206.0f), TheText.Get("FEC_CAM")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(202.0f), MENU_Y(176.0f), TheText.Get("FEC_MOV")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(247.0f), MENU_Y(101.0f), TheText.Get("FEC_TAR")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(297.0f), MENU_Y(89.0f), TheText.Get("FEC_CWR")); + // // CFont::SetJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(438.45f), MENU_Y(257.0f), TheText.Get("FEC_PAU")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(344.0f), MENU_Y(89.0f), TheText.Get("FEC_CMR")); + // // CFont::SetJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(438.45f), MENU_Y(195.0f), TheText.Get("FEC_JUM")); + // // CFont::SetJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(438.45f), MENU_Y(178.0f), TheText.Get("FEC_ENV")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(438.45f), MENU_Y(212.0f), TheText.Get("FEC_RUN")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(394.0f), MENU_Y(101.0f), TheText.Get("FEC_ATT")); + + // //CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(254.0f), TheText.Get("FEC_FPC")); + // //CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(269.0f), TheText.Get("FEC_LB3")); + // //CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(282.0f), TheText.Get("FEC_R3")); + // } + // break; + // case 1: + // if (CPad::GetPad(0)->IsDualAnalog) + // { + // CFont::SetRightJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(210.0f), MENU_Y(146.0f), TheText.Get("FEC_CWL")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(185.0f), TheText.Get("FEC_LOF")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(225.0f), TheText.Get("FEC_MOV")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(257.0f), TheText.Get("FEC_MOV")); + // CFont::SetJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(263.0f), MENU_Y(301.0f), TheText.Get("FEC_CAM")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(290.0f), MENU_Y(288.0f), TheText.Get("FEC_PAU")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(344.0f), MENU_Y(146.0f), TheText.Get("FEC_CWR")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(185.0f), TheText.Get("FEC_TAR")); + // CFont::SetRightJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(304.0f), MENU_Y(178.0f), TheText.Get("FEC_JUM")); + // CFont::SetJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(212.0f), TheText.Get("FEC_ENV")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(225.0f), TheText.Get("FEC_ATT")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(238.0f), TheText.Get("FEC_RUN")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(254.0f), TheText.Get("FEC_FPC")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(269.0f), TheText.Get("FEC_LB3")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(282.0f), TheText.Get("FEC_R3")); + // } + // else + // { + // CFont::SetRightJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(202.0f), MENU_Y(220.0f), TheText.Get("FEC_CWL")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(202.0f), MENU_Y(232.0f), TheText.Get("FEC_LB")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(202.0f), MENU_Y(206.0f), TheText.Get("FEC_CAM")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(202.0f), MENU_Y(176.0f), TheText.Get("FEC_MOV")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(247.0f), MENU_Y(101.0f), TheText.Get("FEC_CMR")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(297.0f), MENU_Y(89.0f), TheText.Get("FEC_CWR")); + // CFont::SetJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(438.45f), MENU_Y(257.0f), TheText.Get("FEC_PAU")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(344.0f), MENU_Y(89.0f), TheText.Get("FEC_JUM")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(438.45f), MENU_Y(178.0f), TheText.Get("FEC_ENV")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(438.45f), MENU_Y(212.0f), TheText.Get("FEC_RUN")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(394.0f), MENU_Y(101.0f), TheText.Get("FEC_TAR")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(438.45f), MENU_Y(195.0f), TheText.Get("FEC_ATT")); + + // //CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(254.0f), TheText.Get("FEC_FPC")); + // //CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(269.0f), TheText.Get("FEC_LB3")); + // //CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(282.0f), TheText.Get("FEC_R3")); + // } + // break; + + // // CFont::SetRightJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(210.0f), MENU_Y(146.0f), TheText.Get("FEC_CWL")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(185.0f), TheText.Get("FEC_LOF")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(225.0f), TheText.Get("FEC_CAM")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(257.0f), TheText.Get("FEC_MOV")); + // // CFont::SetJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(263.0f), MENU_Y(301.0f), TheText.Get("FEC_NA")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(290.0f), MENU_Y(288.0f), TheText.Get("FEC_PAU")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(344.0f), MENU_Y(146.0f), TheText.Get("FEC_CWR")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(185.0f), TheText.Get("FEC_TAR")); + // // CFont::SetRightJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(304.0f), MENU_Y(178.0f), TheText.Get("FEC_JUM")); + // // CFont::SetJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(212.0f), TheText.Get("FEC_ENV")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(225.0f), TheText.Get("FEC_ATT")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(238.0f), TheText.Get("FEC_RUN")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(254.0f), TheText.Get("FEC_FPC")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(269.0f), TheText.Get("FEC_LB3")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(282.0f), TheText.Get("FEC_R3")); + // // break; + // // case 2: + // // CFont::SetRightJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(210.0f), MENU_Y(146.0f), TheText.Get("FEC_CWL")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(185.0f), TheText.Get("FEC_ENV")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(225.0f), TheText.Get("FEC_MOV")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(257.0f), TheText.Get("FEC_MOV")); + // // CFont::SetJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(263.0f), MENU_Y(301.0f), TheText.Get("FEC_CAM")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(290.0f), MENU_Y(288.0f), TheText.Get("FEC_PAU")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(344.0f), MENU_Y(146.0f), TheText.Get("FEC_CWR")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(185.0f), TheText.Get("FEC_TAR")); + // // CFont::SetRightJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(304.0f), MENU_Y(178.0f), TheText.Get("FEC_JUM")); + // // CFont::SetJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(212.0f), TheText.Get("FEC_LOF")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(225.0f), TheText.Get("FEC_RUN")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(238.0f), TheText.Get("FEC_ATT")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(254.0f), TheText.Get("FEC_FPC")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(269.0f), TheText.Get("FEC_LB3")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(282.0f), TheText.Get("FEC_R3")); + // // break; + // // case 3: + // // CFont::SetRightJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(210.0f), MENU_Y(146.0f), TheText.Get("FEC_CWL")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(185.0f), TheText.Get("FEC_TAR")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(225.0f), TheText.Get("FEC_NA")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(257.0f), TheText.Get("FEC_MOV")); + // // CFont::SetJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(263.0f), MENU_Y(301.0f), TheText.Get("FEC_CAM")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(290.0f), MENU_Y(288.0f), TheText.Get("FEC_PAU")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(344.0f), MENU_Y(146.0f), TheText.Get("FEC_CWR")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(185.0f), TheText.Get("FEC_TAR")); + // // CFont::SetRightJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(304.0f), MENU_Y(178.0f), TheText.Get("FEC_JUM")); + // // CFont::SetJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(212.0f), TheText.Get("FEC_LOF")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(225.0f), TheText.Get("FEC_RUN")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(238.0f), TheText.Get("FEC_ATT")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(254.0f), TheText.Get("FEC_FPC")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(269.0f), TheText.Get("FEC_LB3")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(282.0f), TheText.Get("FEC_R3")); + // // break; + // default: + // return; + // } + // } else { + // switch (CPad::GetPad(0)->Mode) { + // case 0: + // // CFont::SetRightJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(210.0f), MENU_Y(146.0f), TheText.Get("FEC_LL")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(185.0f), TheText.Get("FEC_RSC")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(225.0f), TheText.Get("FEC_VES")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(257.0f), TheText.Get("FEC_VES")); + // // CFont::SetJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(263.0f), MENU_Y(301.0f), TheText.Get("FEC_HO3")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(290.0f), MENU_Y(288.0f), TheText.Get("FEC_CAM")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(344.0f), MENU_Y(146.0f), TheText.Get("FEC_PAU")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(185.0f), TheText.Get("FEC_LB")); + // // CFont::SetRightJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(304.0f), MENU_Y(178.0f), TheText.Get("FEC_LR")); + // // CFont::SetJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(212.0f), TheText.Get("FEC_HAB")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(225.0f), TheText.Get("FEC_BRA")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(238.0f), TheText.Get("FEC_EXV")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(254.0f), TheText.Get("FEC_CAW")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(269.0f), TheText.Get("FEC_ACC")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(282.0f), TheText.Get("FEC_TUC")); + // // // FIX: Coordinates of this line is undefined in PC... + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(304.0f), TheText.Get("FEC_SM3")); + // // break; + + // if (CPad::GetPad(0)->IsDualAnalog) + // { + // CFont::SetRightJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(210.0f), MENU_Y(146.0f), TheText.Get("FEC_CWL")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(185.0f), TheText.Get("FEC_LOF")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(225.0f), TheText.Get("FEC_MOV")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(257.0f), TheText.Get("FEC_MOV")); + // CFont::SetJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(263.0f), MENU_Y(301.0f), TheText.Get("FEC_CAM")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(290.0f), MENU_Y(288.0f), TheText.Get("FEC_PAU")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(344.0f), MENU_Y(146.0f), TheText.Get("FEC_CWR")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(185.0f), TheText.Get("FEC_TAR")); + // CFont::SetRightJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(304.0f), MENU_Y(178.0f), TheText.Get("FEC_JUM")); + // CFont::SetJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(212.0f), TheText.Get("FEC_ENV")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(225.0f), TheText.Get("FEC_ATT")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(238.0f), TheText.Get("FEC_RUN")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(254.0f), TheText.Get("FEC_FPC")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(269.0f), TheText.Get("FEC_LB3")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(282.0f), TheText.Get("FEC_R3")); + // } + // else + // { + // CFont::SetRightJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(202.0f), MENU_Y(220.0f), TheText.Get("FEC_SUB")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(202.0f), MENU_Y(232.0f), TheText.Get("FEC_HRN")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(202.0f), MENU_Y(206.0f), TheText.Get("FEC_CAM")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(202.0f), MENU_Y(176.0f), TheText.Get("FEC_VES")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(247.0f), MENU_Y(101.0f), TheText.Get("FEC_BRA")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(297.0f), MENU_Y(89.0f), TheText.Get("FEC_RSC")); + // CFont::SetJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(438.45f), MENU_Y(257.0f), TheText.Get("FEC_PAU")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(344.0f), MENU_Y(89.0f), TheText.Get("FEC_CMR")); + // CFont::SetJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(438.45f), MENU_Y(195.0f), TheText.Get("FEC_HAB")); + // CFont::SetJustifyOn(); // X + + // CFont::PrintString(MENU_X_LEFT_ALIGNED(438.45f), MENU_Y(212.0f), TheText.Get("FEC_PFR")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(394.0f), MENU_Y(101.0f), TheText.Get("FEC_ACC")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(438.45f), MENU_Y(178.0f), TheText.Get("FEC_EXV")); + + // //CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(254.0f), TheText.Get("FEC_FPC")); + // //CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(269.0f), TheText.Get("FEC_LB3")); + // //CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(282.0f), TheText.Get("FEC_R3")); + // } + + + // case 1: + // CFont::SetRightJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(210.0f), MENU_Y(146.0f), TheText.Get("FEC_LL")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(185.0f), TheText.Get("FEC_HOR")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(225.0f), TheText.Get("FEC_CAM")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(257.0f), TheText.Get("FEC_VES")); + // CFont::SetJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(263.0f), MENU_Y(301.0f), TheText.Get("FEC_NA")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(290.0f), MENU_Y(288.0f), TheText.Get("FEC_RSC")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(344.0f), MENU_Y(146.0f), TheText.Get("FEC_PAU")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(185.0f), TheText.Get("FEC_LB")); + // CFont::SetRightJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(304.0f), MENU_Y(178.0f), TheText.Get("FEC_LR")); + // CFont::SetJustifyOn(); // X + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(212.0f), TheText.Get("FEC_HAB")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(225.0f), TheText.Get("FEC_BRA")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(238.0f), TheText.Get("FEC_EXV")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(254.0f), TheText.Get("FEC_CAW")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(269.0f), TheText.Get("FEC_ACC")); + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(282.0f), TheText.Get("FEC_TUC")); + // // FIX: Coordinates of this line is undefined in PC... + // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(304.0f), TheText.Get("FEC_SM3")); + // break; + // // case 2: + // // CFont::SetRightJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(210.0f), MENU_Y(146.0f), TheText.Get("FEC_LL")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(185.0f), TheText.Get("FEC_EXV")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(225.0f), TheText.Get("FEC_VES")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(257.0f), TheText.Get("FEC_VES")); + // // CFont::SetJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(263.0f), MENU_Y(301.0f), TheText.Get("FEC_RS3")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(290.0f), MENU_Y(288.0f), TheText.Get("FEC_CAM")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(344.0f), MENU_Y(146.0f), TheText.Get("FEC_PAU")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(185.0f), TheText.Get("FEC_LB")); + // // CFont::SetRightJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(304.0f), MENU_Y(178.0f), TheText.Get("FEC_LR")); + // // CFont::SetJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(212.0f), TheText.Get("FEC_HOR")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(225.0f), TheText.Get("FEC_BRA")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(238.0f), TheText.Get("FEC_HAB")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(254.0f), TheText.Get("FEC_CAW")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(269.0f), TheText.Get("FEC_ACC")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(282.0f), TheText.Get("FEC_TUC")); + // // // FIX: Coordinates of this line is undefined in PC... + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(304.0f), TheText.Get("FEC_SM3")); + // // break; + // // case 3: + // // CFont::SetRightJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(210.0f), MENU_Y(146.0f), TheText.Get("FEC_LL")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(185.0f), TheText.Get("FEC_HAB")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(225.0f), TheText.Get("FEC_TUC")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(156.0f), MENU_Y(257.0f), TheText.Get("FEC_VES")); + // // CFont::SetJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(263.0f), MENU_Y(301.0f), TheText.Get("FEC_HO3")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(290.0f), MENU_Y(288.0f), TheText.Get("FEC_CAM")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(344.0f), MENU_Y(146.0f), TheText.Get("FEC_PAU")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(185.0f), TheText.Get("FEC_LB")); + // // CFont::SetRightJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(304.0f), MENU_Y(178.0f), TheText.Get("FEC_LR")); + // // CFont::SetJustifyOn(); // X + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(212.0f), TheText.Get("FEC_CAW")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(225.0f), TheText.Get("FEC_SMT")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(238.0f), TheText.Get("FEC_EXV")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(254.0f), TheText.Get("FEC_RSC")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(269.0f), TheText.Get("FEC_NA")); + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(282.0f), TheText.Get("FEC_ACC")); + // // // FIX: Coordinates of this line is undefined in PC... + // // CFont::PrintString(MENU_X_LEFT_ALIGNED(398.0f), MENU_Y(304.0f), TheText.Get("FEC_BRA")); + // // break; + // default: + // return; + // } + // } + + // CFont::SetDropShadowPosition(0); // X +} #else + void CMenuManager::PrintController(void) { @@ -6395,6 +6854,7 @@ CMenuManager::PrintController(void) CFont::SetDropShadowPosition(0); // X } + #endif #ifdef MENU_MAP diff --git a/src/core/Frontend.h b/src/liberty/core/Frontend.h similarity index 99% rename from src/core/Frontend.h rename to src/liberty/core/Frontend.h index dbb86a8d..7af71bbe 100644 --- a/src/core/Frontend.h +++ b/src/liberty/core/Frontend.h @@ -105,12 +105,23 @@ enum eFrontendSprites FE_ICONAUDIO, FE_ICONDISPLAY, FE_ICONLANGUAGE, +#ifdef RW_DC + DC_XBOXF, + DC_XBOXD, + DC_PS2F, + DC_PS2D, + XBOX_F, + XBOX_D, + PS2_F, + PS2_D, +#else FE_CONTROLLER, FE_CONTROLLERSH, FE_ARROWS1, FE_ARROWS2, FE_ARROWS3, FE_ARROWS4, +#endif FE_RADIO1, FE_RADIO2, FE_RADIO3, diff --git a/src/core/FrontendTriggers.h b/src/liberty/core/FrontendTriggers.h similarity index 100% rename from src/core/FrontendTriggers.h rename to src/liberty/core/FrontendTriggers.h diff --git a/src/core/Frontend_PS2.cpp b/src/liberty/core/Frontend_PS2.cpp similarity index 100% rename from src/core/Frontend_PS2.cpp rename to src/liberty/core/Frontend_PS2.cpp diff --git a/src/core/Frontend_PS2.h b/src/liberty/core/Frontend_PS2.h similarity index 100% rename from src/core/Frontend_PS2.h rename to src/liberty/core/Frontend_PS2.h diff --git a/src/core/Game.cpp b/src/liberty/core/Game.cpp similarity index 100% rename from src/core/Game.cpp rename to src/liberty/core/Game.cpp diff --git a/src/core/Game.h b/src/liberty/core/Game.h similarity index 100% rename from src/core/Game.h rename to src/liberty/core/Game.h diff --git a/src/core/General.h b/src/liberty/core/General.h similarity index 100% rename from src/core/General.h rename to src/liberty/core/General.h diff --git a/src/core/IniFile.cpp b/src/liberty/core/IniFile.cpp similarity index 100% rename from src/core/IniFile.cpp rename to src/liberty/core/IniFile.cpp diff --git a/src/core/IniFile.h b/src/liberty/core/IniFile.h similarity index 100% rename from src/core/IniFile.h rename to src/liberty/core/IniFile.h diff --git a/src/core/Lists.cpp b/src/liberty/core/Lists.cpp similarity index 100% rename from src/core/Lists.cpp rename to src/liberty/core/Lists.cpp diff --git a/src/core/Lists.h b/src/liberty/core/Lists.h similarity index 100% rename from src/core/Lists.h rename to src/liberty/core/Lists.h diff --git a/src/core/MenuScreens.cpp b/src/liberty/core/MenuScreens.cpp similarity index 100% rename from src/core/MenuScreens.cpp rename to src/liberty/core/MenuScreens.cpp diff --git a/src/core/MenuScreensCustom.cpp b/src/liberty/core/MenuScreensCustom.cpp similarity index 100% rename from src/core/MenuScreensCustom.cpp rename to src/liberty/core/MenuScreensCustom.cpp index abd7694d..23a9b7e2 100644 --- a/src/core/MenuScreensCustom.cpp +++ b/src/liberty/core/MenuScreensCustom.cpp @@ -902,11 +902,11 @@ CMenuScreenCustom aScreens[MENUPAGES] = { #ifndef GTA_HANDHELD MENUACTION_SCREENRES, "FED_RES", { nil, SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS }, #endif + MULTISAMPLING_SELECTOR MENUACTION_WIDESCREEN, "FED_WIS", { nil, SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS }, VIDEOMODE_SELECTOR MENUACTION_FRAMESYNC, "FEM_VSC", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, MENUACTION_FRAMELIMIT, "FEM_FRM", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, - MULTISAMPLING_SELECTOR ISLAND_LOADING_SELECTOR DUALPASS_SELECTOR #ifdef EXTENDED_COLOURFILTER diff --git a/src/core/Pad.cpp b/src/liberty/core/Pad.cpp similarity index 99% rename from src/core/Pad.cpp rename to src/liberty/core/Pad.cpp index 9469c3d7..bc4bd8ef 100755 --- a/src/core/Pad.cpp +++ b/src/liberty/core/Pad.cpp @@ -2785,22 +2785,22 @@ bool CPad::WeaponJustDown(void) case 0: //Xbox Mode if (CPad::GetPad(0)->IsDualAnalog) { - if (NewState.RightTrigger > 128) + if (NewState.RightTrigger > 128 && !(OldState.RightTrigger > 128)) return true; } else { - if (NewState.RightTrigger > 128) + if (NewState.RightTrigger > 128 && !(OldState.RightTrigger > 128)) return true; } case 1: //PS2 Mode if (CPad::GetPad(0)->IsDualAnalog) { - return NewState.B; + return NewState.B && !OldState.B; } else { - return NewState.B; + return NewState.B && !OldState.B; } } @@ -3211,6 +3211,7 @@ bool CPad::JumpJustDown(void) } } + return false; #else return !!(NewState.Square && !OldState.Square); #endif @@ -3297,6 +3298,8 @@ bool CPad::ShiftTargetLeftJustDown(void) return !!(NewState.DPadLeft && !OldState.DPadLeft); } } + + return false; #else return !!(NewState.LeftShoulder2 && !OldState.LeftShoulder2); @@ -3331,6 +3334,8 @@ bool CPad::ShiftTargetRightJustDown(void) return !!(NewState.DPadRight && !OldState.DPadRight); } } + + return false; #else return !!(NewState.RightShoulder2 && !OldState.RightShoulder2); diff --git a/src/core/Pad.h b/src/liberty/core/Pad.h similarity index 100% rename from src/core/Pad.h rename to src/liberty/core/Pad.h diff --git a/src/core/Placeable.cpp b/src/liberty/core/Placeable.cpp similarity index 100% rename from src/core/Placeable.cpp rename to src/liberty/core/Placeable.cpp diff --git a/src/core/Placeable.h b/src/liberty/core/Placeable.h similarity index 100% rename from src/core/Placeable.h rename to src/liberty/core/Placeable.h diff --git a/src/core/PlayerInfo.cpp b/src/liberty/core/PlayerInfo.cpp similarity index 100% rename from src/core/PlayerInfo.cpp rename to src/liberty/core/PlayerInfo.cpp diff --git a/src/core/PlayerInfo.h b/src/liberty/core/PlayerInfo.h similarity index 100% rename from src/core/PlayerInfo.h rename to src/liberty/core/PlayerInfo.h diff --git a/src/core/Pools.cpp b/src/liberty/core/Pools.cpp similarity index 100% rename from src/core/Pools.cpp rename to src/liberty/core/Pools.cpp diff --git a/src/core/Pools.h b/src/liberty/core/Pools.h similarity index 100% rename from src/core/Pools.h rename to src/liberty/core/Pools.h diff --git a/src/core/Profile.cpp b/src/liberty/core/Profile.cpp similarity index 100% rename from src/core/Profile.cpp rename to src/liberty/core/Profile.cpp diff --git a/src/core/Profile.h b/src/liberty/core/Profile.h similarity index 100% rename from src/core/Profile.h rename to src/liberty/core/Profile.h diff --git a/src/core/Radar.cpp b/src/liberty/core/Radar.cpp similarity index 97% rename from src/core/Radar.cpp rename to src/liberty/core/Radar.cpp index a40a421e..c348078f 100644 --- a/src/core/Radar.cpp +++ b/src/liberty/core/Radar.cpp @@ -1114,13 +1114,12 @@ CRadar::LoadTextures() WaypointSprite.SetTexture("radar_waypoint"); if(!WaypointSprite.m_pTexture) { // create the texture if it's missing in TXD -#define WAYPOINT_R (255) -#define WAYPOINT_G (72) -#define WAYPOINT_B (77) +#define WAYPOINT_R (255>>4) +#define WAYPOINT_G (72>>4) +#define WAYPOINT_B (77>>4) - RwRaster *raster = RwRasterCreate(16, 16, 0, rwRASTERTYPETEXTURE | rwRASTERFORMAT8888); + RwRaster *raster = RwRasterCreate(16, 16, 16, rwRASTERTYPETEXTURE | rwRASTERFORMAT4444); - #if defined(RW_DC) RwUInt16 *pixels = (RwUInt16 *)RwRasterLock(raster, 0, rwRASTERLOCKWRITE); for(int x = 0; x < 16; x++) for(int y = 0; y < 16; y++) @@ -1132,31 +1131,10 @@ CRadar::LoadTextures() || (x2 < 1 && y2 == 1)) // one pixel on each side of second to first/last line is transparent pixels[x + y * 16] = 0; else if((x2 == 2 && y2 >= 2)|| (y2 == 2 && x2 >= 2) )// colored square inside - pixels[x + y * 16] = (WAYPOINT_B>>4) | ((WAYPOINT_G>>4) << 4) | ((WAYPOINT_R>>4) << 8) | (15 << 12); + pixels[x + y * 16] = WAYPOINT_B | (WAYPOINT_G << 4) | (WAYPOINT_R << 8) | (15 << 12); else pixels[x + y * 16] = 0xF000; // black } - #else - RwUInt32 *pixels = (RwUInt32 *)RwRasterLock(raster, 0, rwRASTERLOCKWRITE); - for(int x = 0; x < 16; x++) - for(int y = 0; y < 16; y++) - { - int x2 = x < 8 ? x : 7 - (x & 7); - int y2 = y < 8 ? y : 7 - (y & 7); - if ((y2 >= 4 && x2 >= 4) // square in the center is transparent - || (x2 < 2 && y2 == 0) // two pixels on each side of first/last line are transparent - || (x2 < 1 && y2 == 1)) // one pixel on each side of second to first/last line is transparent - pixels[x + y * 16] = 0; - else if((x2 == 2 && y2 >= 2)|| (y2 == 2 && x2 >= 2) )// colored square inside -#ifdef RW_GL3 - pixels[x + y * 16] = WAYPOINT_R | (WAYPOINT_G << 8) | (WAYPOINT_B << 16) | (255 << 24); -#else - pixels[x + y * 16] = WAYPOINT_B | (WAYPOINT_G << 8) | (WAYPOINT_R << 16) | (255 << 24); -#endif - else - pixels[x + y * 16] = 0xFF000000; // black - } - #endif RwRasterUnlock(raster); WaypointSprite.m_pTexture = RwTextureCreate(raster); RwTextureSetFilterMode(WaypointSprite.m_pTexture, rwFILTERLINEAR); diff --git a/src/core/Radar.h b/src/liberty/core/Radar.h similarity index 100% rename from src/core/Radar.h rename to src/liberty/core/Radar.h diff --git a/src/core/Range2D.cpp b/src/liberty/core/Range2D.cpp similarity index 100% rename from src/core/Range2D.cpp rename to src/liberty/core/Range2D.cpp diff --git a/src/core/Range2D.h b/src/liberty/core/Range2D.h similarity index 100% rename from src/core/Range2D.h rename to src/liberty/core/Range2D.h diff --git a/src/core/Range3D.cpp b/src/liberty/core/Range3D.cpp similarity index 100% rename from src/core/Range3D.cpp rename to src/liberty/core/Range3D.cpp diff --git a/src/core/Range3D.h b/src/liberty/core/Range3D.h similarity index 100% rename from src/core/Range3D.h rename to src/liberty/core/Range3D.h diff --git a/src/core/References.cpp b/src/liberty/core/References.cpp similarity index 100% rename from src/core/References.cpp rename to src/liberty/core/References.cpp diff --git a/src/core/References.h b/src/liberty/core/References.h similarity index 100% rename from src/core/References.h rename to src/liberty/core/References.h diff --git a/src/core/Stats.cpp b/src/liberty/core/Stats.cpp similarity index 100% rename from src/core/Stats.cpp rename to src/liberty/core/Stats.cpp diff --git a/src/core/Stats.h b/src/liberty/core/Stats.h similarity index 100% rename from src/core/Stats.h rename to src/liberty/core/Stats.h diff --git a/src/core/Streaming.cpp b/src/liberty/core/Streaming.cpp similarity index 99% rename from src/core/Streaming.cpp rename to src/liberty/core/Streaming.cpp index d9f22245..b6dedc32 100644 --- a/src/core/Streaming.cpp +++ b/src/liberty/core/Streaming.cpp @@ -1164,6 +1164,12 @@ bool re3RemoveLeastUsedModel() { return CStreaming::RemoveLeastUsedModel(); } +bool re3EmergencyRemoveModel() { + auto usedmem = CStreaming::ms_memoryUsed; + CStreaming::DeleteRwObjectsBehindCamera(CStreaming::ms_memoryUsed-1); + return usedmem != CStreaming::ms_memoryUsed; +} + bool CStreaming::RemoveLeastUsedModel(void) { diff --git a/src/core/Streaming.h b/src/liberty/core/Streaming.h similarity index 100% rename from src/core/Streaming.h rename to src/liberty/core/Streaming.h diff --git a/src/core/SurfaceTable.cpp b/src/liberty/core/SurfaceTable.cpp similarity index 100% rename from src/core/SurfaceTable.cpp rename to src/liberty/core/SurfaceTable.cpp diff --git a/src/core/SurfaceTable.h b/src/liberty/core/SurfaceTable.h similarity index 100% rename from src/core/SurfaceTable.h rename to src/liberty/core/SurfaceTable.h diff --git a/src/core/TimeStep.cpp b/src/liberty/core/TimeStep.cpp similarity index 100% rename from src/core/TimeStep.cpp rename to src/liberty/core/TimeStep.cpp diff --git a/src/core/TimeStep.h b/src/liberty/core/TimeStep.h similarity index 100% rename from src/core/TimeStep.h rename to src/liberty/core/TimeStep.h diff --git a/src/core/Timer.cpp b/src/liberty/core/Timer.cpp similarity index 100% rename from src/core/Timer.cpp rename to src/liberty/core/Timer.cpp diff --git a/src/core/Timer.h b/src/liberty/core/Timer.h similarity index 100% rename from src/core/Timer.h rename to src/liberty/core/Timer.h diff --git a/src/core/User.cpp b/src/liberty/core/User.cpp similarity index 100% rename from src/core/User.cpp rename to src/liberty/core/User.cpp diff --git a/src/core/User.h b/src/liberty/core/User.h similarity index 100% rename from src/core/User.h rename to src/liberty/core/User.h diff --git a/src/core/Wanted.cpp b/src/liberty/core/Wanted.cpp similarity index 100% rename from src/core/Wanted.cpp rename to src/liberty/core/Wanted.cpp diff --git a/src/core/Wanted.h b/src/liberty/core/Wanted.h similarity index 100% rename from src/core/Wanted.h rename to src/liberty/core/Wanted.h diff --git a/src/core/World.cpp b/src/liberty/core/World.cpp similarity index 100% rename from src/core/World.cpp rename to src/liberty/core/World.cpp diff --git a/src/core/World.h b/src/liberty/core/World.h similarity index 100% rename from src/core/World.h rename to src/liberty/core/World.h diff --git a/src/core/ZoneCull.cpp b/src/liberty/core/ZoneCull.cpp similarity index 100% rename from src/core/ZoneCull.cpp rename to src/liberty/core/ZoneCull.cpp diff --git a/src/core/ZoneCull.h b/src/liberty/core/ZoneCull.h similarity index 100% rename from src/core/ZoneCull.h rename to src/liberty/core/ZoneCull.h diff --git a/src/core/Zones.cpp b/src/liberty/core/Zones.cpp similarity index 100% rename from src/core/Zones.cpp rename to src/liberty/core/Zones.cpp diff --git a/src/core/Zones.h b/src/liberty/core/Zones.h similarity index 100% rename from src/core/Zones.h rename to src/liberty/core/Zones.h diff --git a/src/core/common.h b/src/liberty/core/common.h similarity index 98% rename from src/core/common.h rename to src/liberty/core/common.h index f1b9cada..661499d6 100644 --- a/src/core/common.h +++ b/src/liberty/core/common.h @@ -3,7 +3,7 @@ #define _CRT_SECURE_NO_WARNINGS #define _USE_MATH_DEFINES -#include "common_defines.h" +#include "src/common_defines.h" #ifdef __MWERKS__ #define __STDC_LIMIT_MACROS // so we get UINT32_MAX etc @@ -187,8 +187,8 @@ __always_inline uint32 ldb(uint32 p, uint32 s, uint32 w) #define SCREEN_HEIGHT ((float)448) #endif #else -#define SCREEN_WIDTH ((float)640) -#define SCREEN_HEIGHT ((float)480) +#define SCREEN_WIDTH ((float)RsGlobal.width) +#define SCREEN_HEIGHT ((float)RsGlobal.height) #endif #define SCREEN_HEIGHT_PAL ((float)512) diff --git a/src/core/config.h b/src/liberty/core/config.h similarity index 98% rename from src/core/config.h rename to src/liberty/core/config.h index 1da628f8..e8fa4443 100644 --- a/src/core/config.h +++ b/src/liberty/core/config.h @@ -212,7 +212,7 @@ enum Config { // these are placed here to work with VANILLA_DEFINES for compatibility #define NO_CDCHECK // skip audio CD check -#define DEFAULT_NATIVE_RESOLUTION // Set default video mode to your native resolution (fixes Windows 10 launch) +// #define DEFAULT_NATIVE_RESOLUTION // Set default video mode to your native resolution (fixes Windows 10 launch) #ifdef VANILLA_DEFINES #if !defined(_WIN32) || defined(__LP64__) || defined(_WIN64) @@ -308,10 +308,10 @@ enum Config { // NB: keep this enabled unless your map IDEs have these flags baked in #define ASPECT_RATIO_SCALE // Not just makes everything scale with aspect ratio, also adds support for all aspect ratios #define PROPER_SCALING // use original DEFAULT_SCREEN_WIDTH/DEFAULT_SCREEN_HEIGHT from PS2 instead of PC(R* changed HEIGHT here to make radar look better, but broke other hud elements aspect ratio). -#define DEFAULT_NATIVE_RESOLUTION // Set default video mode to your native resolution (fixes Windows 10 launch) +// #define DEFAULT_NATIVE_RESOLUTION // Set default video mode to your native resolution (fixes Windows 10 launch) // #define USE_TXD_CDIMAGE // generate and load textures from txd.img // #define PS2_ALPHA_TEST // emulate ps2 alpha test -#define IMPROVED_VIDEOMODE // save and load videomode parameters instead of a magic number +// #define IMPROVED_VIDEOMODE // save and load videomode parameters instead of a magic number // #define DISABLE_LOADING_SCREEN // disable the loading screen which vastly improves the loading time #ifdef DISABLE_LOADING_SCREEN // enable the PC splash diff --git a/src/core/main.cpp b/src/liberty/core/main.cpp similarity index 99% rename from src/core/main.cpp rename to src/liberty/core/main.cpp index 4b536d3d..9f80c19b 100644 --- a/src/core/main.cpp +++ b/src/liberty/core/main.cpp @@ -163,36 +163,36 @@ Error(char *fmt, ...) void ValidateVersion() { - int32 file = CFileMgr::OpenFile("models\\coll\\peds.col", "rb"); - char buff[128]; + // int32 file = CFileMgr::OpenFile("models\\coll\\peds.col", "rb"); + // char buff[128]; - if ( file != -1 ) - { - CFileMgr::Seek(file, 100, SEEK_SET); + // if ( file != -1 ) + // { + // CFileMgr::Seek(file, 100, SEEK_SET); - for ( int i = 0; i < 128; i++ ) - { - CFileMgr::Read(file, &buff[i], sizeof(char)); - buff[i] -= 23; - if ( buff[i] == '\0' ) - break; - CFileMgr::Seek(file, 99, SEEK_CUR); - } + // for ( int i = 0; i < 128; i++ ) + // { + // CFileMgr::Read(file, &buff[i], sizeof(char)); + // buff[i] -= 23; + // if ( buff[i] == '\0' ) + // break; + // CFileMgr::Seek(file, 99, SEEK_CUR); + // } - if ( !strncmp(buff, "grandtheftauto3", 15) ) - { - strncpy(version_name, &buff[15], 64); - CFileMgr::CloseFile(file); - return; - } - } + // if ( !strncmp(buff, "grandtheftauto3", 15) ) + // { + // strncpy(version_name, &buff[15], 64); + // CFileMgr::CloseFile(file); + // return; + // } + // } - LoadingScreen("Invalid version", NULL, NULL); + // LoadingScreen("Invalid version", NULL, NULL); - while(true) - { - ; - } + // while(true) + // { + // ; + // } } bool diff --git a/src/core/main.h b/src/liberty/core/main.h similarity index 100% rename from src/core/main.h rename to src/liberty/core/main.h diff --git a/src/core/obrstr.cpp b/src/liberty/core/obrstr.cpp similarity index 100% rename from src/core/obrstr.cpp rename to src/liberty/core/obrstr.cpp diff --git a/src/core/obrstr.h b/src/liberty/core/obrstr.h similarity index 100% rename from src/core/obrstr.h rename to src/liberty/core/obrstr.h diff --git a/src/core/re3.cpp b/src/liberty/core/re3.cpp similarity index 99% rename from src/core/re3.cpp rename to src/liberty/core/re3.cpp index 0a1f8629..4f3e50ef 100644 --- a/src/core/re3.cpp +++ b/src/liberty/core/re3.cpp @@ -56,7 +56,7 @@ #include #endif -#include "../vmu/vmu.h" +#include "vmu/vmu.h" #ifdef RWLIBS extern "C" int vsprintf(char* const _Buffer, char const* const _Format, va_list _ArgList); diff --git a/src/core/templates.h b/src/liberty/core/templates.h similarity index 100% rename from src/core/templates.h rename to src/liberty/core/templates.h diff --git a/src/core/timebars.cpp b/src/liberty/core/timebars.cpp similarity index 100% rename from src/core/timebars.cpp rename to src/liberty/core/timebars.cpp diff --git a/src/core/timebars.h b/src/liberty/core/timebars.h similarity index 100% rename from src/core/timebars.h rename to src/liberty/core/timebars.h diff --git a/src/entities/Dummy.cpp b/src/liberty/entities/Dummy.cpp similarity index 100% rename from src/entities/Dummy.cpp rename to src/liberty/entities/Dummy.cpp diff --git a/src/entities/Dummy.h b/src/liberty/entities/Dummy.h similarity index 100% rename from src/entities/Dummy.h rename to src/liberty/entities/Dummy.h diff --git a/src/entities/Entity.cpp b/src/liberty/entities/Entity.cpp similarity index 99% rename from src/entities/Entity.cpp rename to src/liberty/entities/Entity.cpp index c38f12c7..7659f9de 100644 --- a/src/entities/Entity.cpp +++ b/src/liberty/entities/Entity.cpp @@ -83,6 +83,11 @@ CEntity::~CEntity(void) ResolveReferences(); } +bool +CEntity::IsFence(void) { + return IsObject() && ::IsFence(static_cast(this)->GetModelIndex()); +} + void CEntity::SetModelIndex(uint32 id) { diff --git a/src/entities/Entity.h b/src/liberty/entities/Entity.h similarity index 99% rename from src/entities/Entity.h rename to src/liberty/entities/Entity.h index 6174b61d..10b81c18 100644 --- a/src/entities/Entity.h +++ b/src/liberty/entities/Entity.h @@ -131,6 +131,7 @@ public: bool IsPed(void) { return m_type == ENTITY_TYPE_PED; } bool IsObject(void) { return m_type == ENTITY_TYPE_OBJECT; } bool IsDummy(void) { return m_type == ENTITY_TYPE_DUMMY; } + bool IsFence(void); RpAtomic *GetAtomic(void) { assert(RwObjectGetType(m_rwObject) == rpATOMIC); diff --git a/src/entities/Physical.cpp b/src/liberty/entities/Physical.cpp similarity index 99% rename from src/entities/Physical.cpp rename to src/liberty/entities/Physical.cpp index 32a3df3b..ee1335ee 100644 --- a/src/entities/Physical.cpp +++ b/src/liberty/entities/Physical.cpp @@ -59,7 +59,7 @@ CPhysical::CPhysical(void) bInfiniteMass = false; bIsInWater = false; bHitByTrain = false; - bSkipLineCol = false; + bSkipLineCol = IsFence(); m_fDistanceTravelled = 0.0f; m_treadable[PATH_CAR] = nil; @@ -1461,6 +1461,10 @@ CPhysical::ProcessCollisionSectorList(CPtrList *lists) skipCollision = false; altcollision = false; + if(A->IsFence() && B->IsFence()) { + skipCollision = true; + A->bSkipLineCol = true; + } if(B->IsBuilding()) skipCollision = false; else if(IsStreetLight(A->GetModelIndex()) && @@ -1922,12 +1926,12 @@ CPhysical::ProcessCollision(void) n = NUMSTEPS(0.09f); step = savedTimeStep / n; } - }else if(responsecase == COLLRESPONSE_SMALLBOX || responsecase == COLLRESPONSE_FENCEPART){ + }else if(responsecase == COLLRESPONSE_SMALLBOX){ if(distSq >= sq(0.15f)){ n = NUMSTEPS(0.15f); step = savedTimeStep / n; } - }else{ + }else if(responsecase != COLLRESPONSE_FENCEPART){ if(distSq >= sq(0.3f)){ n = NUMSTEPS(0.3f); step = savedTimeStep / n; diff --git a/src/entities/Physical.h b/src/liberty/entities/Physical.h similarity index 100% rename from src/entities/Physical.h rename to src/liberty/entities/Physical.h diff --git a/src/extras/GitSHA1.cpp.in b/src/liberty/extras/GitSHA1.cpp.in similarity index 100% rename from src/extras/GitSHA1.cpp.in rename to src/liberty/extras/GitSHA1.cpp.in diff --git a/src/extras/GitSHA1.h b/src/liberty/extras/GitSHA1.h similarity index 100% rename from src/extras/GitSHA1.h rename to src/liberty/extras/GitSHA1.h diff --git a/src/extras/arrow.inc b/src/liberty/extras/arrow.inc similarity index 100% rename from src/extras/arrow.inc rename to src/liberty/extras/arrow.inc diff --git a/src/extras/cursor.inc b/src/liberty/extras/cursor.inc similarity index 100% rename from src/extras/cursor.inc rename to src/liberty/extras/cursor.inc diff --git a/src/extras/custompipes.cpp b/src/liberty/extras/custompipes.cpp similarity index 100% rename from src/extras/custompipes.cpp rename to src/liberty/extras/custompipes.cpp diff --git a/src/extras/custompipes.h b/src/liberty/extras/custompipes.h similarity index 100% rename from src/extras/custompipes.h rename to src/liberty/extras/custompipes.h diff --git a/src/extras/custompipes_d3d9.cpp b/src/liberty/extras/custompipes_d3d9.cpp similarity index 100% rename from src/extras/custompipes_d3d9.cpp rename to src/liberty/extras/custompipes_d3d9.cpp diff --git a/src/extras/custompipes_gl.cpp b/src/liberty/extras/custompipes_gl.cpp similarity index 100% rename from src/extras/custompipes_gl.cpp rename to src/liberty/extras/custompipes_gl.cpp diff --git a/src/extras/debugmenu.cpp b/src/liberty/extras/debugmenu.cpp similarity index 100% rename from src/extras/debugmenu.cpp rename to src/liberty/extras/debugmenu.cpp diff --git a/src/extras/debugmenu.h b/src/liberty/extras/debugmenu.h similarity index 100% rename from src/extras/debugmenu.h rename to src/liberty/extras/debugmenu.h diff --git a/src/extras/frontendoption.cpp b/src/liberty/extras/frontendoption.cpp similarity index 100% rename from src/extras/frontendoption.cpp rename to src/liberty/extras/frontendoption.cpp diff --git a/src/extras/frontendoption.h b/src/liberty/extras/frontendoption.h similarity index 100% rename from src/extras/frontendoption.h rename to src/liberty/extras/frontendoption.h diff --git a/src/liberty/extras/ini.h b/src/liberty/extras/ini.h new file mode 100644 index 00000000..f14489e6 --- /dev/null +++ b/src/liberty/extras/ini.h @@ -0,0 +1,857 @@ +/* + * The MIT License (MIT) + * Copyright (c) 2018 Danijel Durakovic + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/////////////////////////////////////////////////////////////////////////////// +// +// /mINI/ v0.9.10 +// An INI file reader and writer for the modern age. +// +/////////////////////////////////////////////////////////////////////////////// +// +// A tiny utility library for manipulating INI files with a straightforward +// API and a minimal footprint. It conforms to the (somewhat) standard INI +// format - sections and keys are case insensitive and all leading and +// trailing whitespace is ignored. Comments are lines that begin with a +// semicolon. Trailing comments are allowed on section lines. +// +// Files are read on demand, upon which data is kept in memory and the file +// is closed. This utility supports lazy writing, which only writes changes +// and updates to a file and preserves custom formatting and comments. A lazy +// write invoked by a write() call will read the output file, find what +// changes have been made and update the file accordingly. If you only need to +// generate files, use generate() instead. Section and key order is preserved +// on read, write and insert. +// +/////////////////////////////////////////////////////////////////////////////// +// +// /* BASIC USAGE EXAMPLE: */ +// +// /* read from file */ +// mINI::INIFile file("myfile.ini"); +// mINI::INIStructure ini; +// file.read(ini); +// +// /* read value; gets a reference to actual value in the structure. +// if key or section don't exist, a new empty value will be created */ +// std::string& value = ini["section"]["key"]; +// +// /* read value safely; gets a copy of value in the structure. +// does not alter the structure */ +// std::string value = ini.get("section").get("key"); +// +// /* set or update values */ +// ini["section"]["key"] = "value"; +// +// /* set multiple values */ +// ini["section2"].set({ +// {"key1", "value1"}, +// {"key2", "value2"} +// }); +// +// /* write updates back to file, preserving comments and formatting */ +// file.write(ini); +// +// /* or generate a file (overwrites the original) */ +// file.generate(ini); +// +/////////////////////////////////////////////////////////////////////////////// +// +// Long live the INI file!!! +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef MINI_INI_H_ +#define MINI_INI_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef RW_DC +#include +#endif + +namespace mINI +{ + namespace INIStringUtil + { + const char* const whitespaceDelimiters = " \t\n\r\f\v"; + inline void trim(std::string& str) + { + str.erase(str.find_last_not_of(whitespaceDelimiters) + 1); + str.erase(0, str.find_first_not_of(whitespaceDelimiters)); + } +#ifndef MINI_CASE_SENSITIVE + inline void toLower(std::string& str) + { + std::transform(str.begin(), str.end(), str.begin(), [](const char c) { + return static_cast(std::tolower(c)); + }); + } +#endif + inline void replace(std::string& str, std::string const& a, std::string const& b) + { + if (!a.empty()) + { + std::size_t pos = 0; + while ((pos = str.find(a, pos)) != std::string::npos) + { + str.replace(pos, a.size(), b); + pos += b.size(); + } + } + } +#ifdef _WIN32 + const char* const endl = "\r\n"; +#else + const char* const endl = "\n"; +#endif + }; + + template + class INIMap + { + private: + using T_DataIndexMap = std::unordered_map; + using T_DataItem = std::pair; + using T_DataContainer = std::vector; + using T_MultiArgs = typename std::vector>; + + T_DataIndexMap dataIndexMap; + T_DataContainer data; + + inline std::size_t setEmpty(std::string& key) + { + std::size_t index = data.size(); + dataIndexMap[key] = index; + data.emplace_back(key, T()); + return index; + } + + public: + using const_iterator = typename T_DataContainer::const_iterator; + + INIMap() { } + + INIMap(INIMap const& other) + { + std::size_t data_size = other.data.size(); + for (std::size_t i = 0; i < data_size; ++i) + { + auto const& key = other.data[i].first; + auto const& obj = other.data[i].second; + data.emplace_back(key, obj); + } + dataIndexMap = T_DataIndexMap(other.dataIndexMap); + } + + T& operator[](std::string key) + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + auto it = dataIndexMap.find(key); + bool hasIt = (it != dataIndexMap.end()); + std::size_t index = (hasIt) ? it->second : setEmpty(key); + return data[index].second; + } + T get(std::string key) const + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + auto it = dataIndexMap.find(key); + if (it == dataIndexMap.end()) + { + return T(); + } + return T(data[it->second].second); + } + bool has(std::string key) const + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + return (dataIndexMap.count(key) == 1); + } + void set(std::string key, T obj) + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + auto it = dataIndexMap.find(key); + if (it != dataIndexMap.end()) + { + data[it->second].second = obj; + } + else + { + dataIndexMap[key] = data.size(); + data.emplace_back(key, obj); + } + } + void set(T_MultiArgs const& multiArgs) + { + for (auto const& it : multiArgs) + { + auto const& key = it.first; + auto const& obj = it.second; + set(key, obj); + } + } + bool remove(std::string key) + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + auto it = dataIndexMap.find(key); + if (it != dataIndexMap.end()) + { + std::size_t index = it->second; + data.erase(data.begin() + index); + dataIndexMap.erase(it); + for (auto& it2 : dataIndexMap) + { + auto& vi = it2.second; + if (vi > index) + { + vi--; + } + } + return true; + } + return false; + } + void clear() + { + data.clear(); + dataIndexMap.clear(); + } + std::size_t size() const + { + return data.size(); + } + const_iterator begin() const { return data.begin(); } + const_iterator end() const { return data.end(); } + }; + + using INIStructure = INIMap>; + + namespace INIParser + { + using T_ParseValues = std::pair; + + enum class PDataType : char + { + PDATA_NONE, + PDATA_COMMENT, + PDATA_SECTION, + PDATA_KEYVALUE, + PDATA_UNKNOWN + }; + + inline PDataType parseLine(std::string line, T_ParseValues& parseData) + { + parseData.first.clear(); + parseData.second.clear(); + INIStringUtil::trim(line); + if (line.empty()) + { + return PDataType::PDATA_NONE; + } + char firstCharacter = line[0]; + if (firstCharacter == ';') + { + return PDataType::PDATA_COMMENT; + } + if (firstCharacter == '[') + { + auto commentAt = line.find_first_of(';'); + if (commentAt != std::string::npos) + { + line = line.substr(0, commentAt); + } + auto closingBracketAt = line.find_last_of(']'); + if (closingBracketAt != std::string::npos) + { + auto section = line.substr(1, closingBracketAt - 1); + INIStringUtil::trim(section); + parseData.first = section; + return PDataType::PDATA_SECTION; + } + } + auto lineNorm = line; + INIStringUtil::replace(lineNorm, "\\=", " "); + auto equalsAt = lineNorm.find_first_of('='); + if (equalsAt != std::string::npos) + { + auto key = line.substr(0, equalsAt); + INIStringUtil::trim(key); + INIStringUtil::replace(key, "\\=", "="); + auto value = line.substr(equalsAt + 1); + INIStringUtil::trim(value); + parseData.first = key; + parseData.second = value; + return PDataType::PDATA_KEYVALUE; + } + return PDataType::PDATA_UNKNOWN; + } + }; + + class INIReader + { + public: + using T_LineData = std::vector; + using T_LineDataPtr = std::shared_ptr; + + private: + std::ifstream fileReadStream; + T_LineDataPtr lineData; + std::size_t start_offt; + + T_LineData readFile() + { + std::string fileContents; + fileReadStream.seekg(0, std::ios::end); + fileContents.resize(fileReadStream.tellg()); + fileReadStream.seekg(0, std::ios::beg); + std::size_t fileSize = fileContents.size(); + fileReadStream.read(const_cast(fileContents.c_str()), fileSize); + fileReadStream.close(); + T_LineData output; + if (fileSize == 0) + { + return output; + } + std::string buffer; + buffer.reserve(50); + std::cout << "PRE READ! "<< fileSize << fileContents << std::endl; +#ifdef RW_DC + { + vmu_pkg_t vmu_pkg; + if(vmu_pkg_parse(reinterpret_cast(const_cast(fileContents.c_str())), &vmu_pkg) != 0) { + std::cout << "FUCKIN REESTED!" << std::endl; + start_offt = 0; + } else { + start_offt = reinterpret_cast(vmu_pkg.data) + - reinterpret_cast(fileContents.c_str()); + } + } +#endif + std::cout << "POST READ!" << std::endl; + for (std::size_t i = start_offt; i < fileSize; ++i) + { + char& c = fileContents[i]; + if (c == '\n') + { + output.emplace_back(buffer); + buffer.clear(); + continue; + } + if (c != '\0' && c != '\r') + { + buffer += c; + } + } + output.emplace_back(buffer); + return output; + } + + public: + INIReader(std::string const& filename, bool keepLineData = false) + { + std::cout << "CREATING INI READER" << std::endl; + fileReadStream.open(filename, std::ios::in | std::ios::binary); + if (keepLineData) + { + lineData = std::make_shared(); + } + + start_offt = 0; + } + ~INIReader() { } + + bool operator>>(INIStructure& data) + { + if (!fileReadStream.is_open()) + { + return false; + } + T_LineData fileLines = readFile(); + std::string section; + bool inSection = false; + INIParser::T_ParseValues parseData; + for (auto const& line : fileLines) + { + auto parseResult = INIParser::parseLine(line, parseData); + if (parseResult == INIParser::PDataType::PDATA_SECTION) + { + inSection = true; + data[section = parseData.first]; + } + else if (inSection && parseResult == INIParser::PDataType::PDATA_KEYVALUE) + { + auto const& key = parseData.first; + auto const& value = parseData.second; + data[section][key] = value; + } + if (lineData && parseResult != INIParser::PDataType::PDATA_UNKNOWN) + { + if (parseResult == INIParser::PDataType::PDATA_KEYVALUE && !inSection) + { + continue; + } + lineData->emplace_back(line); + } + } + return true; + } + T_LineDataPtr getLines() + { + return lineData; + } + }; + + class INIGenerator + { + private: + std::ofstream fileWriteStream; + std::stringstream memStream; + inline static std::ios_base::iostate lastError_; + public: + bool prettyPrint = false; + + INIGenerator(std::string const& filename) + : fileWriteStream(filename, std::ios::out | std::ios::binary) + { + lastError_ = fileWriteStream.rdstate(); + std::cerr << "INIGenerator::INIGenerator(): " << fileWriteStream.rdstate() << std::endl; + } + + ~INIGenerator() + { + std::cerr << "INIGenerator::~INIGenerator()" << std::endl; + memStream << "FUCKINGBITCH!"; + std::string str = memStream.str(); + const char *buf = str.c_str(); + int buf_size = str.length(); + size_t stream_size = memStream.tellp(); + + if(!fileWriteStream.good()) { + std::cerr << "NO GOOD!" << std::endl; + return; + } + +#ifdef RW_DC + uint8_t *data; + uint8_t icon_buf[512 * 1]; + vmu_pkg_t vmu_pkg = { + .desc_short = "DCA3 Settings", + .desc_long = "DCA3 Settings", + .app_id = "DCA3", + .icon_cnt = 1, + .icon_anim_speed = 0, + .data_len = buf_size, + .icon_data = icon_buf, + .data = reinterpret_cast(buf), + }; + + //std::cout << "LOADING ICON" << std::endl; + if (vmu_pkg_load_icon(&vmu_pkg, "settings.ico") < 0) { + std::cerr << "FAILED TO LOAD ICON!" << std::endl; + vmu_pkg.icon_cnt = 0; + } + + //std::cout << "BUILDING PACKAGE" << std::endl; + int pkg_size = 0; + if(vmu_pkg_build(&vmu_pkg, &data, &pkg_size) < 0) { + std::cerr << "FAILED TO BUILD PACKAGE!" << std::endl; + lastError_ = std::ios_base::badbit; + return; + } + + buf = reinterpret_cast(data); + std::cout << "WRITING: " << std::string(buf, buf + pkg_size) << std::endl; +#endif + + fileWriteStream.write(buf, pkg_size); + lastError_ = fileWriteStream.rdstate(); + +#ifdef RW_DC + free(data); +#endif + std::cerr << "WROTE: " << lastError_ << std::endl; + std::cerr << "STREAM_SIZE: " << stream_size << std::endl; + std::cerr << "BUF_SIZE: " << buf_size << std::endl; + std::cerr << "PKG_SIZE: " << pkg_size << std::endl; + } + + bool operator<<(const std::string& str) + { + memStream << str; + return true; + } + + bool operator<<(INIStructure const& data) + { + if (!data.size()) + { + return true; + } + auto it = data.begin(); + for (;;) + { + auto const& section = it->first; + auto const& collection = it->second; + memStream + << "[" + << section + << "]"; + if (collection.size()) + { + memStream << INIStringUtil::endl; + auto it2 = collection.begin(); + for (;;) + { + auto key = it2->first; + INIStringUtil::replace(key, "=", "\\="); + auto value = it2->second; + INIStringUtil::trim(value); + memStream + << key + << ((prettyPrint) ? " = " : "=") + << value; + if (++it2 == collection.end()) + { + break; + } + memStream << INIStringUtil::endl; + } + } + if (++it == data.end()) + { + break; + } + memStream << INIStringUtil::endl; + if (prettyPrint) + { + memStream << INIStringUtil::endl; + } + } + return true; + } + + operator bool() const + { + return fileWriteStream.rdstate() & fileWriteStream.goodbit; + } + + static std::ios_base::iostate lastError() { return lastError_; } + }; + + class INIWriter + { + private: + using T_LineData = std::vector; + using T_LineDataPtr = std::shared_ptr; + + std::string filename; + + T_LineData getLazyOutput(T_LineDataPtr const& lineData, INIStructure& data, INIStructure& original) + { + T_LineData output; + INIParser::T_ParseValues parseData; + std::string sectionCurrent; + bool parsingSection = false; + bool continueToNextSection = false; + bool discardNextEmpty = false; + bool writeNewKeys = false; + std::size_t lastKeyLine = 0; + for (auto line = lineData->begin(); line != lineData->end(); ++line) + { + if (!writeNewKeys) + { + auto parseResult = INIParser::parseLine(*line, parseData); + if (parseResult == INIParser::PDataType::PDATA_SECTION) + { + if (parsingSection) + { + writeNewKeys = true; + parsingSection = false; + --line; + continue; + } + sectionCurrent = parseData.first; + if (data.has(sectionCurrent)) + { + parsingSection = true; + continueToNextSection = false; + discardNextEmpty = false; + output.emplace_back(*line); + lastKeyLine = output.size(); + } + else + { + continueToNextSection = true; + discardNextEmpty = true; + continue; + } + } + else if (parseResult == INIParser::PDataType::PDATA_KEYVALUE) + { + if (continueToNextSection) + { + continue; + } + if (data.has(sectionCurrent)) + { + auto& collection = data[sectionCurrent]; + auto const& key = parseData.first; + auto const& value = parseData.second; + if (collection.has(key)) + { + auto outputValue = collection[key]; + if (value == outputValue) + { + output.emplace_back(*line); + } + else + { + INIStringUtil::trim(outputValue); + auto lineNorm = *line; + INIStringUtil::replace(lineNorm, "\\=", " "); + auto equalsAt = lineNorm.find_first_of('='); + auto valueAt = lineNorm.find_first_not_of( + INIStringUtil::whitespaceDelimiters, + equalsAt + 1 + ); + std::string outputLine = line->substr(0, valueAt); + if (prettyPrint && equalsAt + 1 == valueAt) + { + outputLine += " "; + } + outputLine += outputValue; + output.emplace_back(outputLine); + } + lastKeyLine = output.size(); + } + } + } + else + { + if (discardNextEmpty && line->empty()) + { + discardNextEmpty = false; + } + else if (parseResult != INIParser::PDataType::PDATA_UNKNOWN) + { + output.emplace_back(*line); + } + } + } + if (writeNewKeys || std::next(line) == lineData->end()) + { + T_LineData linesToAdd; + if (data.has(sectionCurrent) && original.has(sectionCurrent)) + { + auto const& collection = data[sectionCurrent]; + auto const& collectionOriginal = original[sectionCurrent]; + for (auto const& it : collection) + { + auto key = it.first; + if (collectionOriginal.has(key)) + { + continue; + } + auto value = it.second; + INIStringUtil::replace(key, "=", "\\="); + INIStringUtil::trim(value); + linesToAdd.emplace_back( + key + ((prettyPrint) ? " = " : "=") + value + ); + } + } + if (!linesToAdd.empty()) + { + output.insert( + output.begin() + lastKeyLine, + linesToAdd.begin(), + linesToAdd.end() + ); + } + if (writeNewKeys) + { + writeNewKeys = false; + --line; + } + } + } + for (auto const& it : data) + { + auto const& section = it.first; + if (original.has(section)) + { + continue; + } + if (prettyPrint && output.size() > 0 && !output.back().empty()) + { + output.emplace_back(); + } + output.emplace_back("[" + section + "]"); + auto const& collection = it.second; + for (auto const& it2 : collection) + { + auto key = it2.first; + auto value = it2.second; + INIStringUtil::replace(key, "=", "\\="); + INIStringUtil::trim(value); + output.emplace_back( + key + ((prettyPrint) ? " = " : "=") + value + ); + } + } + return output; + } + + public: + bool prettyPrint = false; + + INIWriter(std::string const& filename) + : filename(filename) + { + } + ~INIWriter() { } + + bool operator<<(INIStructure& data) + { + struct stat buf; + bool fileExists = (stat(filename.c_str(), &buf) == 0); + + if (!fileExists) + { + if(INIGenerator generator(filename); generator) { + generator.prettyPrint = prettyPrint; + generator << data; + } + return INIGenerator::lastError() & std::ios_base::goodbit; + } + + INIStructure originalData; + T_LineDataPtr lineData; + bool readSuccess = false; + { + INIReader reader(filename, true); + if ((readSuccess = reader >> originalData)) + { + lineData = reader.getLines(); + } + } + if (!readSuccess) + { + return false; + } + T_LineData output = getLazyOutput(lineData, data, originalData); + { + if (INIGenerator generator(filename); generator && output.size()) + { + generator.prettyPrint = prettyPrint; + auto line = output.begin(); + for (;;) + { + generator << *line; + if (++line == output.end()) + { + break; + } + generator << INIStringUtil::endl; + } + } + return INIGenerator::lastError() & std::ios_base::goodbit; + } + return false; + } + }; + + class INIFile + { + private: + std::string filename; + + public: + INIFile(std::string const& filename) + : filename(filename) + { } + + ~INIFile() { } + + bool read(INIStructure& data) const + { + if (data.size()) + { + data.clear(); + } + if (filename.empty()) + { + return false; + } + INIReader reader(filename); + return reader >> data; + } + bool generate(INIStructure const& data, bool pretty = false) const + { + if (filename.empty()) + { + return false; + } + if(INIGenerator generator(filename); generator) { + generator.prettyPrint = pretty; + generator << data; + } + return INIGenerator::lastError() & std::ios_base::goodbit; + } + bool write(INIStructure& data, bool pretty = false) const + { + if (filename.empty()) + { + return false; + } + INIWriter writer(filename); + writer.prettyPrint = pretty; + return writer << data; + } + }; +} + +#endif // MINI_INI_H_ diff --git a/src/extras/postfx.cpp b/src/liberty/extras/postfx.cpp similarity index 100% rename from src/extras/postfx.cpp rename to src/liberty/extras/postfx.cpp diff --git a/src/extras/postfx.h b/src/liberty/extras/postfx.h similarity index 100% rename from src/extras/postfx.h rename to src/liberty/extras/postfx.h diff --git a/src/extras/re3_inttypes.h b/src/liberty/extras/re3_inttypes.h similarity index 100% rename from src/extras/re3_inttypes.h rename to src/liberty/extras/re3_inttypes.h diff --git a/src/extras/screendroplets.cpp b/src/liberty/extras/screendroplets.cpp similarity index 100% rename from src/extras/screendroplets.cpp rename to src/liberty/extras/screendroplets.cpp diff --git a/src/extras/screendroplets.h b/src/liberty/extras/screendroplets.h similarity index 100% rename from src/extras/screendroplets.h rename to src/liberty/extras/screendroplets.h diff --git a/src/extras/shaders/colourfilterIII.frag b/src/liberty/extras/shaders/colourfilterIII.frag similarity index 100% rename from src/extras/shaders/colourfilterIII.frag rename to src/liberty/extras/shaders/colourfilterIII.frag diff --git a/src/extras/shaders/colourfilterIII_PS.hlsl b/src/liberty/extras/shaders/colourfilterIII_PS.hlsl similarity index 100% rename from src/extras/shaders/colourfilterIII_PS.hlsl rename to src/liberty/extras/shaders/colourfilterIII_PS.hlsl diff --git a/src/extras/shaders/contrast.frag b/src/liberty/extras/shaders/contrast.frag similarity index 100% rename from src/extras/shaders/contrast.frag rename to src/liberty/extras/shaders/contrast.frag diff --git a/src/extras/shaders/contrastPS.hlsl b/src/liberty/extras/shaders/contrastPS.hlsl similarity index 100% rename from src/extras/shaders/contrastPS.hlsl rename to src/liberty/extras/shaders/contrastPS.hlsl diff --git a/src/extras/shaders/default_UV2.vert b/src/liberty/extras/shaders/default_UV2.vert similarity index 100% rename from src/extras/shaders/default_UV2.vert rename to src/liberty/extras/shaders/default_UV2.vert diff --git a/src/extras/shaders/default_UV2_VS.hlsl b/src/liberty/extras/shaders/default_UV2_VS.hlsl similarity index 100% rename from src/extras/shaders/default_UV2_VS.hlsl rename to src/liberty/extras/shaders/default_UV2_VS.hlsl diff --git a/src/extras/shaders/im2d.vert b/src/liberty/extras/shaders/im2d.vert similarity index 100% rename from src/extras/shaders/im2d.vert rename to src/liberty/extras/shaders/im2d.vert diff --git a/src/extras/shaders/im2d_UV2.vert b/src/liberty/extras/shaders/im2d_UV2.vert similarity index 100% rename from src/extras/shaders/im2d_UV2.vert rename to src/liberty/extras/shaders/im2d_UV2.vert diff --git a/src/extras/shaders/lighting.h b/src/liberty/extras/shaders/lighting.h similarity index 100% rename from src/extras/shaders/lighting.h rename to src/liberty/extras/shaders/lighting.h diff --git a/src/extras/shaders/make_glsl.sh b/src/liberty/extras/shaders/make_glsl.sh similarity index 100% rename from src/extras/shaders/make_glsl.sh rename to src/liberty/extras/shaders/make_glsl.sh diff --git a/src/extras/shaders/make_hlsl.cmd b/src/liberty/extras/shaders/make_hlsl.cmd similarity index 100% rename from src/extras/shaders/make_hlsl.cmd rename to src/liberty/extras/shaders/make_hlsl.cmd diff --git a/src/extras/shaders/makeinc_glsl.sh b/src/liberty/extras/shaders/makeinc_glsl.sh similarity index 100% rename from src/extras/shaders/makeinc_glsl.sh rename to src/liberty/extras/shaders/makeinc_glsl.sh diff --git a/src/extras/shaders/makeinc_hlsl.sh b/src/liberty/extras/shaders/makeinc_hlsl.sh similarity index 100% rename from src/extras/shaders/makeinc_hlsl.sh rename to src/liberty/extras/shaders/makeinc_hlsl.sh diff --git a/src/extras/shaders/neoGloss.frag b/src/liberty/extras/shaders/neoGloss.frag similarity index 100% rename from src/extras/shaders/neoGloss.frag rename to src/liberty/extras/shaders/neoGloss.frag diff --git a/src/extras/shaders/neoGloss.vert b/src/liberty/extras/shaders/neoGloss.vert similarity index 100% rename from src/extras/shaders/neoGloss.vert rename to src/liberty/extras/shaders/neoGloss.vert diff --git a/src/extras/shaders/neoGloss_PS.hlsl b/src/liberty/extras/shaders/neoGloss_PS.hlsl similarity index 100% rename from src/extras/shaders/neoGloss_PS.hlsl rename to src/liberty/extras/shaders/neoGloss_PS.hlsl diff --git a/src/extras/shaders/neoGloss_VS.hlsl b/src/liberty/extras/shaders/neoGloss_VS.hlsl similarity index 100% rename from src/extras/shaders/neoGloss_VS.hlsl rename to src/liberty/extras/shaders/neoGloss_VS.hlsl diff --git a/src/extras/shaders/neoRim.vert b/src/liberty/extras/shaders/neoRim.vert similarity index 100% rename from src/extras/shaders/neoRim.vert rename to src/liberty/extras/shaders/neoRim.vert diff --git a/src/extras/shaders/neoRimSkin.vert b/src/liberty/extras/shaders/neoRimSkin.vert similarity index 100% rename from src/extras/shaders/neoRimSkin.vert rename to src/liberty/extras/shaders/neoRimSkin.vert diff --git a/src/extras/shaders/neoRimSkin_VS.hlsl b/src/liberty/extras/shaders/neoRimSkin_VS.hlsl similarity index 100% rename from src/extras/shaders/neoRimSkin_VS.hlsl rename to src/liberty/extras/shaders/neoRimSkin_VS.hlsl diff --git a/src/extras/shaders/neoRim_VS.hlsl b/src/liberty/extras/shaders/neoRim_VS.hlsl similarity index 100% rename from src/extras/shaders/neoRim_VS.hlsl rename to src/liberty/extras/shaders/neoRim_VS.hlsl diff --git a/src/extras/shaders/neoVehicle.frag b/src/liberty/extras/shaders/neoVehicle.frag similarity index 100% rename from src/extras/shaders/neoVehicle.frag rename to src/liberty/extras/shaders/neoVehicle.frag diff --git a/src/extras/shaders/neoVehicle.vert b/src/liberty/extras/shaders/neoVehicle.vert similarity index 100% rename from src/extras/shaders/neoVehicle.vert rename to src/liberty/extras/shaders/neoVehicle.vert diff --git a/src/extras/shaders/neoVehicle_PS.hlsl b/src/liberty/extras/shaders/neoVehicle_PS.hlsl similarity index 100% rename from src/extras/shaders/neoVehicle_PS.hlsl rename to src/liberty/extras/shaders/neoVehicle_PS.hlsl diff --git a/src/extras/shaders/neoVehicle_VS.hlsl b/src/liberty/extras/shaders/neoVehicle_VS.hlsl similarity index 100% rename from src/extras/shaders/neoVehicle_VS.hlsl rename to src/liberty/extras/shaders/neoVehicle_VS.hlsl diff --git a/src/extras/shaders/neoWorldIII.frag b/src/liberty/extras/shaders/neoWorldIII.frag similarity index 100% rename from src/extras/shaders/neoWorldIII.frag rename to src/liberty/extras/shaders/neoWorldIII.frag diff --git a/src/extras/shaders/neoWorldIII_PS.hlsl b/src/liberty/extras/shaders/neoWorldIII_PS.hlsl similarity index 100% rename from src/extras/shaders/neoWorldIII_PS.hlsl rename to src/liberty/extras/shaders/neoWorldIII_PS.hlsl diff --git a/src/extras/shaders/obj/colourfilterIII_PS.cso b/src/liberty/extras/shaders/obj/colourfilterIII_PS.cso similarity index 100% rename from src/extras/shaders/obj/colourfilterIII_PS.cso rename to src/liberty/extras/shaders/obj/colourfilterIII_PS.cso diff --git a/src/extras/shaders/obj/colourfilterIII_PS.inc b/src/liberty/extras/shaders/obj/colourfilterIII_PS.inc similarity index 100% rename from src/extras/shaders/obj/colourfilterIII_PS.inc rename to src/liberty/extras/shaders/obj/colourfilterIII_PS.inc diff --git a/src/extras/shaders/obj/colourfilterIII_frag.inc b/src/liberty/extras/shaders/obj/colourfilterIII_frag.inc similarity index 100% rename from src/extras/shaders/obj/colourfilterIII_frag.inc rename to src/liberty/extras/shaders/obj/colourfilterIII_frag.inc diff --git a/src/extras/shaders/obj/contrastPS.cso b/src/liberty/extras/shaders/obj/contrastPS.cso similarity index 100% rename from src/extras/shaders/obj/contrastPS.cso rename to src/liberty/extras/shaders/obj/contrastPS.cso diff --git a/src/extras/shaders/obj/contrastPS.inc b/src/liberty/extras/shaders/obj/contrastPS.inc similarity index 100% rename from src/extras/shaders/obj/contrastPS.inc rename to src/liberty/extras/shaders/obj/contrastPS.inc diff --git a/src/extras/shaders/obj/contrast_frag.inc b/src/liberty/extras/shaders/obj/contrast_frag.inc similarity index 100% rename from src/extras/shaders/obj/contrast_frag.inc rename to src/liberty/extras/shaders/obj/contrast_frag.inc diff --git a/src/extras/shaders/obj/default_UV2_VS.cso b/src/liberty/extras/shaders/obj/default_UV2_VS.cso similarity index 100% rename from src/extras/shaders/obj/default_UV2_VS.cso rename to src/liberty/extras/shaders/obj/default_UV2_VS.cso diff --git a/src/extras/shaders/obj/default_UV2_VS.inc b/src/liberty/extras/shaders/obj/default_UV2_VS.inc similarity index 100% rename from src/extras/shaders/obj/default_UV2_VS.inc rename to src/liberty/extras/shaders/obj/default_UV2_VS.inc diff --git a/src/extras/shaders/obj/default_UV2_vert.inc b/src/liberty/extras/shaders/obj/default_UV2_vert.inc similarity index 100% rename from src/extras/shaders/obj/default_UV2_vert.inc rename to src/liberty/extras/shaders/obj/default_UV2_vert.inc diff --git a/src/extras/shaders/obj/im2d_UV2_vert.inc b/src/liberty/extras/shaders/obj/im2d_UV2_vert.inc similarity index 100% rename from src/extras/shaders/obj/im2d_UV2_vert.inc rename to src/liberty/extras/shaders/obj/im2d_UV2_vert.inc diff --git a/src/extras/shaders/obj/im2d_vert.inc b/src/liberty/extras/shaders/obj/im2d_vert.inc similarity index 100% rename from src/extras/shaders/obj/im2d_vert.inc rename to src/liberty/extras/shaders/obj/im2d_vert.inc diff --git a/src/extras/shaders/obj/neoGloss_PS.cso b/src/liberty/extras/shaders/obj/neoGloss_PS.cso similarity index 100% rename from src/extras/shaders/obj/neoGloss_PS.cso rename to src/liberty/extras/shaders/obj/neoGloss_PS.cso diff --git a/src/extras/shaders/obj/neoGloss_PS.inc b/src/liberty/extras/shaders/obj/neoGloss_PS.inc similarity index 100% rename from src/extras/shaders/obj/neoGloss_PS.inc rename to src/liberty/extras/shaders/obj/neoGloss_PS.inc diff --git a/src/extras/shaders/obj/neoGloss_VS.cso b/src/liberty/extras/shaders/obj/neoGloss_VS.cso similarity index 100% rename from src/extras/shaders/obj/neoGloss_VS.cso rename to src/liberty/extras/shaders/obj/neoGloss_VS.cso diff --git a/src/extras/shaders/obj/neoGloss_VS.inc b/src/liberty/extras/shaders/obj/neoGloss_VS.inc similarity index 100% rename from src/extras/shaders/obj/neoGloss_VS.inc rename to src/liberty/extras/shaders/obj/neoGloss_VS.inc diff --git a/src/extras/shaders/obj/neoGloss_frag.inc b/src/liberty/extras/shaders/obj/neoGloss_frag.inc similarity index 100% rename from src/extras/shaders/obj/neoGloss_frag.inc rename to src/liberty/extras/shaders/obj/neoGloss_frag.inc diff --git a/src/extras/shaders/obj/neoGloss_vert.inc b/src/liberty/extras/shaders/obj/neoGloss_vert.inc similarity index 100% rename from src/extras/shaders/obj/neoGloss_vert.inc rename to src/liberty/extras/shaders/obj/neoGloss_vert.inc diff --git a/src/extras/shaders/obj/neoRimSkin_VS.cso b/src/liberty/extras/shaders/obj/neoRimSkin_VS.cso similarity index 100% rename from src/extras/shaders/obj/neoRimSkin_VS.cso rename to src/liberty/extras/shaders/obj/neoRimSkin_VS.cso diff --git a/src/extras/shaders/obj/neoRimSkin_VS.inc b/src/liberty/extras/shaders/obj/neoRimSkin_VS.inc similarity index 100% rename from src/extras/shaders/obj/neoRimSkin_VS.inc rename to src/liberty/extras/shaders/obj/neoRimSkin_VS.inc diff --git a/src/extras/shaders/obj/neoRimSkin_vert.inc b/src/liberty/extras/shaders/obj/neoRimSkin_vert.inc similarity index 100% rename from src/extras/shaders/obj/neoRimSkin_vert.inc rename to src/liberty/extras/shaders/obj/neoRimSkin_vert.inc diff --git a/src/extras/shaders/obj/neoRim_VS.cso b/src/liberty/extras/shaders/obj/neoRim_VS.cso similarity index 100% rename from src/extras/shaders/obj/neoRim_VS.cso rename to src/liberty/extras/shaders/obj/neoRim_VS.cso diff --git a/src/extras/shaders/obj/neoRim_VS.inc b/src/liberty/extras/shaders/obj/neoRim_VS.inc similarity index 100% rename from src/extras/shaders/obj/neoRim_VS.inc rename to src/liberty/extras/shaders/obj/neoRim_VS.inc diff --git a/src/extras/shaders/obj/neoRim_vert.inc b/src/liberty/extras/shaders/obj/neoRim_vert.inc similarity index 100% rename from src/extras/shaders/obj/neoRim_vert.inc rename to src/liberty/extras/shaders/obj/neoRim_vert.inc diff --git a/src/extras/shaders/obj/neoVehicle_PS.cso b/src/liberty/extras/shaders/obj/neoVehicle_PS.cso similarity index 100% rename from src/extras/shaders/obj/neoVehicle_PS.cso rename to src/liberty/extras/shaders/obj/neoVehicle_PS.cso diff --git a/src/extras/shaders/obj/neoVehicle_PS.inc b/src/liberty/extras/shaders/obj/neoVehicle_PS.inc similarity index 100% rename from src/extras/shaders/obj/neoVehicle_PS.inc rename to src/liberty/extras/shaders/obj/neoVehicle_PS.inc diff --git a/src/extras/shaders/obj/neoVehicle_VS.cso b/src/liberty/extras/shaders/obj/neoVehicle_VS.cso similarity index 100% rename from src/extras/shaders/obj/neoVehicle_VS.cso rename to src/liberty/extras/shaders/obj/neoVehicle_VS.cso diff --git a/src/extras/shaders/obj/neoVehicle_VS.inc b/src/liberty/extras/shaders/obj/neoVehicle_VS.inc similarity index 100% rename from src/extras/shaders/obj/neoVehicle_VS.inc rename to src/liberty/extras/shaders/obj/neoVehicle_VS.inc diff --git a/src/extras/shaders/obj/neoVehicle_frag.inc b/src/liberty/extras/shaders/obj/neoVehicle_frag.inc similarity index 100% rename from src/extras/shaders/obj/neoVehicle_frag.inc rename to src/liberty/extras/shaders/obj/neoVehicle_frag.inc diff --git a/src/extras/shaders/obj/neoVehicle_vert.inc b/src/liberty/extras/shaders/obj/neoVehicle_vert.inc similarity index 100% rename from src/extras/shaders/obj/neoVehicle_vert.inc rename to src/liberty/extras/shaders/obj/neoVehicle_vert.inc diff --git a/src/extras/shaders/obj/neoWorldIII_PS.cso b/src/liberty/extras/shaders/obj/neoWorldIII_PS.cso similarity index 100% rename from src/extras/shaders/obj/neoWorldIII_PS.cso rename to src/liberty/extras/shaders/obj/neoWorldIII_PS.cso diff --git a/src/extras/shaders/obj/neoWorldIII_PS.inc b/src/liberty/extras/shaders/obj/neoWorldIII_PS.inc similarity index 100% rename from src/extras/shaders/obj/neoWorldIII_PS.inc rename to src/liberty/extras/shaders/obj/neoWorldIII_PS.inc diff --git a/src/extras/shaders/obj/neoWorldIII_frag.inc b/src/liberty/extras/shaders/obj/neoWorldIII_frag.inc similarity index 100% rename from src/extras/shaders/obj/neoWorldIII_frag.inc rename to src/liberty/extras/shaders/obj/neoWorldIII_frag.inc diff --git a/src/extras/shaders/obj/screenDroplet_PS.cso b/src/liberty/extras/shaders/obj/screenDroplet_PS.cso similarity index 100% rename from src/extras/shaders/obj/screenDroplet_PS.cso rename to src/liberty/extras/shaders/obj/screenDroplet_PS.cso diff --git a/src/extras/shaders/obj/screenDroplet_PS.inc b/src/liberty/extras/shaders/obj/screenDroplet_PS.inc similarity index 100% rename from src/extras/shaders/obj/screenDroplet_PS.inc rename to src/liberty/extras/shaders/obj/screenDroplet_PS.inc diff --git a/src/extras/shaders/obj/screenDroplet_frag.inc b/src/liberty/extras/shaders/obj/screenDroplet_frag.inc similarity index 100% rename from src/extras/shaders/obj/screenDroplet_frag.inc rename to src/liberty/extras/shaders/obj/screenDroplet_frag.inc diff --git a/src/extras/shaders/obj/simple_frag.inc b/src/liberty/extras/shaders/obj/simple_frag.inc similarity index 100% rename from src/extras/shaders/obj/simple_frag.inc rename to src/liberty/extras/shaders/obj/simple_frag.inc diff --git a/src/extras/shaders/screenDroplet.frag b/src/liberty/extras/shaders/screenDroplet.frag similarity index 100% rename from src/extras/shaders/screenDroplet.frag rename to src/liberty/extras/shaders/screenDroplet.frag diff --git a/src/extras/shaders/screenDroplet_PS.hlsl b/src/liberty/extras/shaders/screenDroplet_PS.hlsl similarity index 100% rename from src/extras/shaders/screenDroplet_PS.hlsl rename to src/liberty/extras/shaders/screenDroplet_PS.hlsl diff --git a/src/extras/shaders/simple.frag b/src/liberty/extras/shaders/simple.frag similarity index 100% rename from src/extras/shaders/simple.frag rename to src/liberty/extras/shaders/simple.frag diff --git a/src/extras/shaders/standardConstants.h b/src/liberty/extras/shaders/standardConstants.h similarity index 100% rename from src/extras/shaders/standardConstants.h rename to src/liberty/extras/shaders/standardConstants.h diff --git a/src/fakerw/fake.cpp b/src/liberty/fakerw/fake.cpp similarity index 100% rename from src/fakerw/fake.cpp rename to src/liberty/fakerw/fake.cpp diff --git a/src/fakerw/rpanisot.h b/src/liberty/fakerw/rpanisot.h similarity index 100% rename from src/fakerw/rpanisot.h rename to src/liberty/fakerw/rpanisot.h diff --git a/src/fakerw/rphanim.h b/src/liberty/fakerw/rphanim.h similarity index 100% rename from src/fakerw/rphanim.h rename to src/liberty/fakerw/rphanim.h diff --git a/src/fakerw/rpmatfx.h b/src/liberty/fakerw/rpmatfx.h similarity index 100% rename from src/fakerw/rpmatfx.h rename to src/liberty/fakerw/rpmatfx.h diff --git a/src/fakerw/rpskin.h b/src/liberty/fakerw/rpskin.h similarity index 100% rename from src/fakerw/rpskin.h rename to src/liberty/fakerw/rpskin.h diff --git a/src/fakerw/rpworld.h b/src/liberty/fakerw/rpworld.h similarity index 100% rename from src/fakerw/rpworld.h rename to src/liberty/fakerw/rpworld.h diff --git a/src/fakerw/rtbmp.h b/src/liberty/fakerw/rtbmp.h similarity index 100% rename from src/fakerw/rtbmp.h rename to src/liberty/fakerw/rtbmp.h diff --git a/src/fakerw/rtcharse.h b/src/liberty/fakerw/rtcharse.h similarity index 100% rename from src/fakerw/rtcharse.h rename to src/liberty/fakerw/rtcharse.h diff --git a/src/fakerw/rtpng.h b/src/liberty/fakerw/rtpng.h similarity index 100% rename from src/fakerw/rtpng.h rename to src/liberty/fakerw/rtpng.h diff --git a/src/fakerw/rtquat.h b/src/liberty/fakerw/rtquat.h similarity index 100% rename from src/fakerw/rtquat.h rename to src/liberty/fakerw/rtquat.h diff --git a/src/liberty/fakerw/rwcore.h b/src/liberty/fakerw/rwcore.h new file mode 100644 index 00000000..3430a5ae --- /dev/null +++ b/src/liberty/fakerw/rwcore.h @@ -0,0 +1,423 @@ +#pragma once + +#define RWCORE_H // needed by CVector + +#include + +#include + +/* + *********************************************** + * + * RwIm2D and RwIm3D + * + *********************************************** + */ + +typedef rw::RWDEVICE::Im2DVertex RwIm2DVertex; +typedef rw::RWDEVICE::Im3DVertex RwIm3DVertex; +typedef RwUInt16 RwImVertexIndex; + +enum RwIm3DTransformFlags +{ + rwIM3D_VERTEXUV = rw::im3d::VERTEXUV, + rwIM3D_ALLOPAQUE = rw::im3d::ALLOPAQUE, + rwIM3D_NOCLIP = rw::im3d::NOCLIP, + rwIM3D_VERTEXXYZ = rw::im3d::VERTEXXYZ, + rwIM3D_VERTEXRGBA = rw::im3d::VERTEXRGBA, +}; + +void RwIm2DVertexSetCameraX(RwIm2DVertex *vert, RwReal camx); +void RwIm2DVertexSetCameraY(RwIm2DVertex *vert, RwReal camy); +void RwIm2DVertexSetCameraZ(RwIm2DVertex *vert, RwReal camz); +void RwIm2DVertexSetRecipCameraZ(RwIm2DVertex *vert, RwReal recipz); +void RwIm2DVertexSetScreenX(RwIm2DVertex *vert, RwReal scrnx); +void RwIm2DVertexSetScreenY(RwIm2DVertex *vert, RwReal scrny); +void RwIm2DVertexSetScreenZ(RwIm2DVertex *vert, RwReal scrnz); +void RwIm2DVertexSetU(RwIm2DVertex *vert, RwReal texU, RwReal recipz); +void RwIm2DVertexSetV(RwIm2DVertex *vert, RwReal texV, RwReal recipz); +void RwIm2DVertexSetIntRGBA(RwIm2DVertex *vert, RwUInt8 red, RwUInt8 green, RwUInt8 blue, RwUInt8 alpha); + +RwReal RwIm2DGetNearScreenZ(void); +RwReal RwIm2DGetFarScreenZ(void); +RwBool RwIm2DRenderLine(RwIm2DVertex *vertices, RwInt32 numVertices, RwInt32 vert1, RwInt32 vert2); +RwBool RwIm2DRenderTriangle(RwIm2DVertex *vertices, RwInt32 numVertices, RwInt32 vert1, RwInt32 vert2, RwInt32 vert3 ); +RwBool RwIm2DRenderPrimitive(RwPrimitiveType primType, RwIm2DVertex *vertices, RwInt32 numVertices); +RwBool RwIm2DRenderIndexedPrimitive(RwPrimitiveType primType, RwIm2DVertex *vertices, RwInt32 numVertices, RwImVertexIndex *indices, RwInt32 numIndices); + + +void RwIm3DVertexSetPos(RwIm3DVertex *vert, RwReal x, RwReal y, RwReal z); +void RwIm3DVertexSetU(RwIm3DVertex *vert, RwReal u); +void RwIm3DVertexSetV(RwIm3DVertex *vert, RwReal v); +void RwIm3DVertexSetRGBA(RwIm3DVertex *vert, RwUInt8 r, RwUInt8 g, RwUInt8 b, RwUInt8 a); + +void *RwIm3DTransform(RwIm3DVertex *pVerts, RwUInt32 numVerts, RwMatrix *ltm, RwUInt32 flags); +RwBool RwIm3DEnd(void); +RwBool RwIm3DRenderLine(RwInt32 vert1, RwInt32 vert2); +RwBool RwIm3DRenderTriangle(RwInt32 vert1, RwInt32 vert2, RwInt32 vert3); +RwBool RwIm3DRenderIndexedPrimitive(RwPrimitiveType primType, RwImVertexIndex *indices, RwInt32 numIndices); +RwBool RwIm3DRenderPrimitive(RwPrimitiveType primType); + + +/* + *********************************************** + * + * RwRaster + * + *********************************************** + */ + +//struct RwRaster; +typedef rw::Raster RwRaster; + +enum RwRasterType +{ + rwRASTERTYPENORMAL = rw::Raster::NORMAL, + rwRASTERTYPEZBUFFER = rw::Raster::ZBUFFER, + rwRASTERTYPECAMERA = rw::Raster::CAMERA, + rwRASTERTYPETEXTURE = rw::Raster::TEXTURE, + rwRASTERTYPECAMERATEXTURE = rw::Raster::CAMERATEXTURE, + rwRASTERTYPEMASK = 0x07, + rwRASTERDONTALLOCATE = rw::Raster::DONTALLOCATE, +}; + +enum RwRasterFormat +{ + rwRASTERFORMATDEFAULT = rw::Raster::DEFAULT, + rwRASTERFORMAT1555 = rw::Raster::C1555, + rwRASTERFORMAT565 = rw::Raster::C565, + rwRASTERFORMAT4444 = rw::Raster::C4444, + rwRASTERFORMATLUM8 = rw::Raster::LUM8, + rwRASTERFORMAT8888 = rw::Raster::C8888, + rwRASTERFORMAT888 = rw::Raster::C888, + rwRASTERFORMAT16 = rw::Raster::D16, + rwRASTERFORMAT24 = rw::Raster::D24, + rwRASTERFORMAT32 = rw::Raster::D32, + rwRASTERFORMAT555 = rw::Raster::C555, + + rwRASTERFORMATAUTOMIPMAP = rw::Raster::AUTOMIPMAP, + rwRASTERFORMATPAL8 = rw::Raster::PAL8, + rwRASTERFORMATPAL4 = rw::Raster::PAL4, + rwRASTERFORMATMIPMAP = rw::Raster::MIPMAP, + + rwRASTERFORMATPIXELFORMATMASK = 0x0f00, + rwRASTERFORMATMASK = 0xff00 +}; + +enum RwRasterLockMode +{ + rwRASTERLOCKWRITE = rw::Raster::LOCKWRITE, + rwRASTERLOCKREAD = rw::Raster::LOCKREAD, + rwRASTERLOCKNOFETCH = rw::Raster::LOCKNOFETCH, + rwRASTERLOCKRAW = rw::Raster::LOCKRAW, +}; + +enum RwRasterFlipMode +{ + rwRASTERFLIPDONTWAIT = 0, + rwRASTERFLIPWAITVSYNC = 1, +}; + +RwRaster *RwRasterCreate(RwInt32 width, RwInt32 height, RwInt32 depth, RwInt32 flags); +RwBool RwRasterDestroy(RwRaster * raster); +RwInt32 RwRasterGetWidth(const RwRaster *raster); +RwInt32 RwRasterGetHeight(const RwRaster *raster); +RwInt32 RwRasterGetStride(const RwRaster *raster); +RwInt32 RwRasterGetDepth(const RwRaster *raster); +RwInt32 RwRasterGetFormat(const RwRaster *raster); +RwInt32 RwRasterGetType(const RwRaster *raster); +RwRaster *RwRasterGetParent(const RwRaster *raster); +RwRaster *RwRasterGetOffset(RwRaster *raster, RwInt16 *xOffset, RwInt16 *yOffset); +RwInt32 RwRasterGetNumLevels(RwRaster * raster); +RwRaster *RwRasterSubRaster(RwRaster * subRaster, RwRaster * raster, RwRect * rect); +RwRaster *RwRasterRenderFast(RwRaster * raster, RwInt32 x, RwInt32 y); +RwRaster *RwRasterRender(RwRaster * raster, RwInt32 x, RwInt32 y); +RwRaster *RwRasterRenderScaled(RwRaster * raster, RwRect * rect); +RwRaster *RwRasterPushContext(RwRaster * raster); +RwRaster *RwRasterPopContext(void); +RwRaster *RwRasterGetCurrentContext(void); +RwBool RwRasterClear(RwInt32 pixelValue); +RwBool RwRasterClearRect(RwRect * rpRect, RwInt32 pixelValue); +RwRaster *RwRasterShowRaster(RwRaster * raster, void *dev, RwUInt32 flags); +RwUInt8 *RwRasterLock(RwRaster * raster, RwUInt8 level, RwInt32 lockMode); +RwRaster *RwRasterUnlock(RwRaster * raster); +RwUInt8 *RwRasterLockPalette(RwRaster * raster, RwInt32 lockMode); +RwRaster *RwRasterUnlockPalette(RwRaster * raster); +RwInt32 RwRasterRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); +RwInt32 RwRasterGetPluginOffset(RwUInt32 pluginID); +RwBool RwRasterValidatePlugins(const RwRaster * raster); + + +/* + *********************************************** + * + * RwImage + * + *********************************************** + */ + +//struct RwImage; +typedef rw::Image RwImage; + +RwImage *RwImageCreate(RwInt32 width, RwInt32 height, RwInt32 depth); +RwBool RwImageDestroy(RwImage * image); +RwImage *RwImageAllocatePixels(RwImage * image); +RwImage *RwImageFreePixels(RwImage * image); +RwImage *RwImageCopy(RwImage * destImage, const RwImage * sourceImage); +RwImage *RwImageResize(RwImage * image, RwInt32 width, RwInt32 height); +RwImage *RwImageApplyMask(RwImage * image, const RwImage * mask); +RwImage *RwImageMakeMask(RwImage * image); +RwImage *RwImageReadMaskedImage(const RwChar * imageName, const RwChar * maskname); +RwImage *RwImageRead(const RwChar * imageName); +RwImage *RwImageWrite(RwImage * image, const RwChar * imageName); +RwChar *RwImageGetPath(void); +const RwChar *RwImageSetPath(const RwChar * path); +RwImage *RwImageSetStride(RwImage * image, RwInt32 stride); +RwImage *RwImageSetPixels(RwImage * image, RwUInt8 * pixels); +RwImage *RwImageSetPalette(RwImage * image, RwRGBA * palette); +RwInt32 RwImageGetWidth(const RwImage * image); +RwInt32 RwImageGetHeight(const RwImage * image); +RwInt32 RwImageGetDepth(const RwImage * image); +RwInt32 RwImageGetStride(const RwImage * image); +RwUInt8 *RwImageGetPixels(const RwImage * image); +RwRGBA *RwImageGetPalette(const RwImage * image); +RwUInt32 RwRGBAToPixel(RwRGBA * rgbIn, RwInt32 rasterFormat); +RwRGBA *RwRGBASetFromPixel(RwRGBA * rgbOut, RwUInt32 pixelValue, RwInt32 rasterFormat); +RwBool RwImageSetGamma(RwReal gammaValue); +RwReal RwImageGetGamma(void); +RwImage *RwImageGammaCorrect(RwImage * image); +RwRGBA *RwRGBAGammaCorrect(RwRGBA * rgb); +RwInt32 RwImageRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); +RwInt32 RwImageGetPluginOffset(RwUInt32 pluginID); +RwBool RwImageValidatePlugins(const RwImage * image); +//RwBool RwImageRegisterImageFormat(const RwChar * extension, RwImageCallBackRead imageRead, RwImageCallBackWrite imageWrite); +const RwChar *RwImageFindFileType(const RwChar * imageName); +RwInt32 RwImageStreamGetSize(const RwImage * image); +RwImage *RwImageStreamRead(RwStream * stream); +const RwImage *RwImageStreamWrite(const RwImage * image, RwStream * stream); + + +/* + *********************************************** + * + * RwTexture + * + *********************************************** + */ + +//struct RwTexture; +typedef rw::Texture RwTexture; +//struct RwTexDictionary; +typedef rw::TexDictionary RwTexDictionary; + +typedef RwTexture *(*RwTextureCallBackRead)(const RwChar *name, const RwChar *maskName); +typedef RwTexture *(*RwTextureCallBack)(RwTexture *texture, void *pData); +typedef RwTexDictionary *(*RwTexDictionaryCallBack)(RwTexDictionary *dict, void *data); +typedef RwRaster *(*RwTextureCallBackMipmapGeneration)(RwRaster * raster, RwImage * image); +typedef RwBool (*RwTextureCallBackMipmapName)(RwChar *name, RwChar *maskName, RwUInt8 mipLevel, RwInt32 format); + +RwTexture *RwTextureCreate(RwRaster * raster); +RwBool RwTextureDestroy(RwTexture * texture); +RwTexture *RwTextureAddRef(RwTexture *texture); +RwBool RwTextureSetMipmapping(RwBool enable); +RwBool RwTextureGetMipmapping(void); +RwBool RwTextureSetAutoMipmapping(RwBool enable); +RwBool RwTextureGetAutoMipmapping(void); +RwBool RwTextureSetMipmapGenerationCallBack(RwTextureCallBackMipmapGeneration callback); +RwTextureCallBackMipmapGeneration RwTextureGetMipmapGenerationCallBack(void); +RwBool RwTextureSetMipmapNameCallBack(RwTextureCallBackMipmapName callback); +RwTextureCallBackMipmapName RwTextureGetMipmapNameCallBack(void); +RwBool RwTextureGenerateMipmapName(RwChar * name, RwChar * maskName, RwUInt8 mipLevel, RwInt32 format); +RwBool RwTextureRasterGenerateMipmaps(RwRaster * raster, RwImage * image); +RwTextureCallBackRead RwTextureGetReadCallBack(void); +RwBool RwTextureSetReadCallBack(RwTextureCallBackRead fpCallBack); +RwTexture *RwTextureSetName(RwTexture * texture, const RwChar * name); +RwTexture *RwTextureSetMaskName(RwTexture * texture, const RwChar * maskName); +RwChar *RwTextureGetName(RwTexture *texture); +RwChar *RwTextureGetMaskName(RwTexture *texture); +RwTexture *RwTextureSetRaster(RwTexture * texture, RwRaster * raster); +RwTexture *RwTextureRead(const RwChar * name, const RwChar * maskName); +RwRaster *RwTextureGetRaster(const RwTexture *texture); +RwInt32 RwTextureRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); +RwInt32 RwTextureGetPluginOffset(RwUInt32 pluginID); +RwBool RwTextureValidatePlugins(const RwTexture * texture); + +RwTexDictionary *RwTextureGetDictionary(RwTexture *texture); +RwTexture *RwTextureSetFilterMode(RwTexture *texture, RwTextureFilterMode filtering); +RwTextureFilterMode RwTextureGetFilterMode(const RwTexture *texture); +RwTexture *RwTextureSetAddressing(RwTexture *texture, RwTextureAddressMode addressing); +RwTexture *RwTextureSetAddressingU(RwTexture *texture, RwTextureAddressMode addressing); +RwTexture *RwTextureSetAddressingV(RwTexture *texture, RwTextureAddressMode addressing); +RwTextureAddressMode RwTextureGetAddressing(const RwTexture *texture); +RwTextureAddressMode RwTextureGetAddressingU(const RwTexture *texture); +RwTextureAddressMode RwTextureGetAddressingV(const RwTexture *texture); + +void _rwD3D8TexDictionaryEnableRasterFormatConversion(bool enable); + +// hack for reading native textures +RwBool rwNativeTextureHackRead(RwStream *stream, RwTexture **tex, RwInt32 size); + + +RwTexDictionary *RwTexDictionaryCreate(void); +RwBool RwTexDictionaryDestroy(RwTexDictionary * dict); +RwTexture *RwTexDictionaryAddTexture(RwTexDictionary * dict, RwTexture * texture); +RwTexture *RwTexDictionaryRemoveTexture(RwTexture * texture); +RwTexture *RwTexDictionaryFindNamedTexture(RwTexDictionary * dict, const RwChar * name); +RwTexDictionary *RwTexDictionaryGetCurrent(void); +RwTexDictionary *RwTexDictionarySetCurrent(RwTexDictionary * dict); +const RwTexDictionary *RwTexDictionaryForAllTextures(const RwTexDictionary * dict, RwTextureCallBack fpCallBack, void *pData); +RwBool RwTexDictionaryForAllTexDictionaries(RwTexDictionaryCallBack fpCallBack, void *pData); +RwInt32 RwTexDictionaryRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); +RwInt32 RwTexDictionaryGetPluginOffset(RwUInt32 pluginID); +RwBool RwTexDictionaryValidatePlugins(const RwTexDictionary * dict); +RwUInt32 RwTexDictionaryStreamGetSize(const RwTexDictionary *texDict); +RwTexDictionary *RwTexDictionaryStreamRead(RwStream *stream); +const RwTexDictionary *RwTexDictionaryStreamWrite(const RwTexDictionary *texDict, RwStream *stream); + +/* RwImage/RwRaster */ + +RwImage *RwImageSetFromRaster(RwImage *image, RwRaster *raster); +RwRaster *RwRasterSetFromImage(RwRaster *raster, RwImage *image); +RwRGBA *RwRGBAGetRasterPixel(RwRGBA *rgbOut, RwRaster *raster, RwInt32 x, RwInt32 y); +RwRaster *RwRasterRead(const RwChar *filename); +RwRaster *RwRasterReadMaskedRaster(const RwChar *filename, const RwChar *maskname); +RwImage *RwImageFindRasterFormat(RwImage *ipImage,RwInt32 nRasterType, RwInt32 *npWidth,RwInt32 *npHeight, RwInt32 *npDepth,RwInt32 *npFormat); + + +/* + *********************************************** + * + * RwFrame + * + *********************************************** + */ + +//struct RwFrame; +typedef rw::Frame RwFrame; + +typedef RwFrame *(*RwFrameCallBack)(RwFrame *frame, void *data); + + +RwFrame *RwFrameForAllObjects(RwFrame * frame, RwObjectCallBack callBack, void *data); +RwFrame *RwFrameTranslate(RwFrame * frame, const RwV3d * v, RwOpCombineType combine); +RwFrame *RwFrameRotate(RwFrame * frame, const RwV3d * axis, RwReal angle, RwOpCombineType combine); +RwFrame *RwFrameScale(RwFrame * frame, const RwV3d * v, RwOpCombineType combine); +RwFrame *RwFrameTransform(RwFrame * frame, const RwMatrix * m, RwOpCombineType combine); +RwFrame *RwFrameOrthoNormalize(RwFrame * frame); +RwFrame *RwFrameSetIdentity(RwFrame * frame); +RwFrame *RwFrameCloneHierarchy(RwFrame * root); +RwBool RwFrameDestroyHierarchy(RwFrame * frame); +RwFrame *RwFrameForAllChildren(RwFrame * frame, RwFrameCallBack callBack, void *data); +RwFrame *RwFrameRemoveChild(RwFrame * child); +RwFrame *RwFrameAddChild(RwFrame * parent, RwFrame * child); +RwFrame *RwFrameGetParent(const RwFrame * frame); +RwFrame *RwFrameGetRoot(const RwFrame * frame); +RwMatrix *RwFrameGetLTM(RwFrame * frame); +RwMatrix *RwFrameGetMatrix(RwFrame * frame); +RwFrame *RwFrameUpdateObjects(RwFrame * frame); +RwFrame *RwFrameCreate(void); +RwBool RwFrameInit(RwFrame *frame); +RwBool RwFrameDeInit(RwFrame *frame); +RwBool RwFrameDestroy(RwFrame * frame); +void _rwFrameInit(RwFrame *frame); +void _rwFrameDeInit(RwFrame *frame); +RwBool RwFrameDirty(const RwFrame * frame); +RwInt32 RwFrameCount(RwFrame * frame); +RwBool RwFrameSetStaticPluginsSize(RwInt32 size); +RwInt32 RwFrameRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); +RwInt32 RwFrameGetPluginOffset(RwUInt32 pluginID); +RwBool RwFrameValidatePlugins(const RwFrame * frame); +RwFrame *_rwFrameCloneAndLinkClones(RwFrame * root); +RwFrame *_rwFramePurgeClone(RwFrame *root); + +RwInt32 RwFrameRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); +RwInt32 RwFrameSetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); + +typedef rw::FrameList_ rwFrameList; +rwFrameList *rwFrameListInitialize(rwFrameList *frameList, RwFrame *frame); +RwBool rwFrameListFindFrame(const rwFrameList *frameList, const RwFrame *frame, RwInt32 *npIndex); +rwFrameList *rwFrameListDeinitialize(rwFrameList *frameList); +RwUInt32 rwFrameListStreamGetSize(const rwFrameList *frameList); +rwFrameList *rwFrameListStreamRead(RwStream *stream, rwFrameList *fl); +const rwFrameList *rwFrameListStreamWrite(const rwFrameList *frameList, RwStream *stream); + + +typedef rw::BBox RwBBox; + +/* + *********************************************** + * + * RwCamera + * + *********************************************** + */ + +//struct RwCamera; +typedef rw::Camera RwCamera; + +typedef RwCamera *(*RwCameraCallBack)(RwCamera *camera, void *data); + +enum RwCameraClearMode +{ + rwCAMERACLEARIMAGE = 0x1, + rwCAMERACLEARZ = 0x2, + rwCAMERACLEARSTENCIL = 0x4 +}; + +enum RwCameraProjection +{ + rwNACAMERAPROJECTION = 0, + rwPERSPECTIVE = 1, + rwPARALLEL = 2 +}; + +enum RwFrustumTestResult +{ + rwSPHEREOUTSIDE = 0, + rwSPHEREBOUNDARY = 1, + rwSPHEREINSIDE = 2, +}; + +RwCamera *RwCameraBeginUpdate(RwCamera * camera); +RwCamera *RwCameraEndUpdate(RwCamera * camera); +RwCamera *RwCameraClear(RwCamera * camera, RwRGBA * colour, RwInt32 clearMode); +RwCamera *RwCameraShowRaster(RwCamera * camera, void *pDev, RwUInt32 flags); +RwBool RwCameraDestroy(RwCamera * camera); +RwCamera *RwCameraCreate(void); +RwCamera *RwCameraClone(RwCamera * camera); +RwCamera *RwCameraSetViewOffset(RwCamera *camera, const RwV2d *offset); +RwCamera *RwCameraSetViewWindow(RwCamera *camera, const RwV2d *viewWindow); +RwCamera *RwCameraSetProjection(RwCamera *camera, RwCameraProjection projection); +RwCamera *RwCameraSetNearClipPlane(RwCamera *camera, RwReal nearClip); +RwCamera *RwCameraSetFarClipPlane(RwCamera *camera, RwReal farClip); +RwInt32 RwCameraRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); +RwInt32 RwCameraGetPluginOffset(RwUInt32 pluginID); +RwBool RwCameraValidatePlugins(const RwCamera * camera); +RwFrustumTestResult RwCameraFrustumTestSphere(const RwCamera * camera, const RwSphere * sphere); +const RwV2d *RwCameraGetViewOffset(const RwCamera *camera); +RwCamera *RwCameraSetRaster(RwCamera *camera, RwRaster *raster); +RwRaster *RwCameraGetRaster(const RwCamera *camera); +RwCamera *RwCameraSetZRaster(RwCamera *camera, RwRaster *zRaster); +RwRaster *RwCameraGetZRaster(const RwCamera *camera); +RwReal RwCameraGetNearClipPlane(const RwCamera *camera); +RwReal RwCameraGetFarClipPlane(const RwCamera *camera); +RwCamera *RwCameraSetFogDistance(RwCamera *camera, RwReal fogDistance); +RwReal RwCameraGetFogDistance(const RwCamera *camera); +RwCamera *RwCameraGetCurrentCamera(void); +RwCameraProjection RwCameraGetProjection(const RwCamera *camera); +const RwV2d *RwCameraGetViewWindow(const RwCamera *camera); +RwMatrix *RwCameraGetViewMatrix(RwCamera *camera); +RwCamera *RwCameraSetFrame(RwCamera *camera, RwFrame *frame); +RwFrame *RwCameraGetFrame(const RwCamera *camera); + + +/* + * + * D3D-engine specific stuff + * + */ + +void RwD3D8EngineSetRefreshRate(RwUInt32 refreshRate); +RwBool RwD3D8DeviceSupportsDXTTexture(void); +void RwD3D8EngineSetMultiSamplingLevels(RwUInt32 level); +RwUInt32 RwD3D8EngineGetMaxMultiSamplingLevels(void); diff --git a/src/fakerw/rwplcore.h b/src/liberty/fakerw/rwplcore.h similarity index 100% rename from src/fakerw/rwplcore.h rename to src/liberty/fakerw/rwplcore.h diff --git a/src/math/Matrix.cpp b/src/liberty/math/Matrix.cpp similarity index 93% rename from src/math/Matrix.cpp rename to src/liberty/math/Matrix.cpp index 88433b26..41265e87 100644 --- a/src/math/Matrix.cpp +++ b/src/liberty/math/Matrix.cpp @@ -282,7 +282,7 @@ CMatrix::SetRotate(float xAngle, float yAngle, float zAngle) void CMatrix::RotateX(float x) { -#ifdef DC_SH4 +#if 0 && defined(DC_SH4) // this is bugged and does not yield correct results mat_load(reinterpret_cast(this)); mat_rotate_x(x); mat_store(reinterpret_cast(this)); @@ -312,7 +312,7 @@ CMatrix::RotateX(float x) void CMatrix::RotateY(float y) { -#ifdef DC_SH4 +#if 0 && defined(DC_SH4) // this is bugged and does not yield correct results mat_load(reinterpret_cast(this)); mat_rotate_y(y); mat_store(reinterpret_cast(this)); @@ -342,7 +342,7 @@ CMatrix::RotateY(float y) void CMatrix::RotateZ(float z) { -#ifdef DC_SH4 +#if 0 && defined(DC_SH4) // this is bugged and does not yield correct results mat_load(reinterpret_cast(this)); mat_rotate_z(z); mat_store(reinterpret_cast(this)); @@ -372,7 +372,7 @@ CMatrix::RotateZ(float z) void CMatrix::Rotate(float x, float y, float z) { -#ifdef DC_SH4 +#if 0 && defined(DC_SH4) // this is bugged and does not yield correct results mat_load(reinterpret_cast(this)); mat_rotate(x, y, z); mat_store(reinterpret_cast(this)); @@ -404,6 +404,20 @@ CMatrix::Rotate(float x, float y, float z) float z2 = sZ * sY - (cZ * sX) * cY; float z3 = cX * cY; + #if !defined(DC_TEXCONV) && !defined(DC_SIM) + this->rx = fipr(x1, y1, z1, 0, rx, ry, rz, 0); + this->ry = fipr(x2, y2, z2, 0, rx, ry, rz, 0); + this->rz = fipr(x3, y3, z3, 0, rx, ry, rz, 0); + this->fx = fipr(x1, y1, z1, 0, ux, uy, uz, 0); + this->fy = fipr(x2, y2, z2, 0, ux, uy, uz, 0); + this->fz = fipr(x3, y3, z3, 0, ux, uy, uz, 0); + this->ux = fipr(x1, y1, z1, 0, ax, ay, az, 0); + this->uy = fipr(x2, y2, z2, 0, ax, ay, az, 0); + this->uz = fipr(x3, y3, z3, 0, ax, ay, az, 0); + this->px = fipr(x1, y1, z1, 0, px, py, pz, 0); + this->py = fipr(x2, y2, z2, 0, px, py, pz, 0); + this->pz = fipr(x3, y3, z3, 0, px, py, pz, 0); + #else this->rx = x1 * rx + y1 * ry + z1 * rz; this->ry = x2 * rx + y2 * ry + z2 * rz; this->rz = x3 * rx + y3 * ry + z3 * rz; @@ -416,6 +430,7 @@ CMatrix::Rotate(float x, float y, float z) this->px = x1 * px + y1 * py + z1 * pz; this->py = x2 * px + y2 * py + z2 * pz; this->pz = x3 * px + y3 * py + z3 * pz; + #endif #endif } diff --git a/src/math/Matrix.h b/src/liberty/math/Matrix.h similarity index 100% rename from src/math/Matrix.h rename to src/liberty/math/Matrix.h diff --git a/src/math/Quaternion.cpp b/src/liberty/math/Quaternion.cpp similarity index 100% rename from src/math/Quaternion.cpp rename to src/liberty/math/Quaternion.cpp diff --git a/src/math/Quaternion.h b/src/liberty/math/Quaternion.h similarity index 100% rename from src/math/Quaternion.h rename to src/liberty/math/Quaternion.h diff --git a/src/math/Rect.cpp b/src/liberty/math/Rect.cpp similarity index 100% rename from src/math/Rect.cpp rename to src/liberty/math/Rect.cpp diff --git a/src/math/Rect.h b/src/liberty/math/Rect.h similarity index 100% rename from src/math/Rect.h rename to src/liberty/math/Rect.h diff --git a/src/math/Vector.cpp b/src/liberty/math/Vector.cpp similarity index 100% rename from src/math/Vector.cpp rename to src/liberty/math/Vector.cpp diff --git a/src/math/Vector.h b/src/liberty/math/Vector.h similarity index 100% rename from src/math/Vector.h rename to src/liberty/math/Vector.h diff --git a/src/math/Vector2D.h b/src/liberty/math/Vector2D.h similarity index 100% rename from src/math/Vector2D.h rename to src/liberty/math/Vector2D.h diff --git a/src/math/VuVector.h b/src/liberty/math/VuVector.h similarity index 100% rename from src/math/VuVector.h rename to src/liberty/math/VuVector.h diff --git a/src/math/math.cpp b/src/liberty/math/math.cpp similarity index 100% rename from src/math/math.cpp rename to src/liberty/math/math.cpp diff --git a/src/math/maths.h b/src/liberty/math/maths.h similarity index 98% rename from src/math/maths.h rename to src/liberty/math/maths.h index 35e34806..9959bf61 100644 --- a/src/math/maths.h +++ b/src/liberty/math/maths.h @@ -1,6 +1,6 @@ #pragma once -#include "common_defines.h" +#include "src/common_defines.h" #include #include diff --git a/src/modelinfo/BaseModelInfo.cpp b/src/liberty/modelinfo/BaseModelInfo.cpp similarity index 100% rename from src/modelinfo/BaseModelInfo.cpp rename to src/liberty/modelinfo/BaseModelInfo.cpp diff --git a/src/modelinfo/BaseModelInfo.h b/src/liberty/modelinfo/BaseModelInfo.h similarity index 100% rename from src/modelinfo/BaseModelInfo.h rename to src/liberty/modelinfo/BaseModelInfo.h diff --git a/src/modelinfo/ClumpModelInfo.cpp b/src/liberty/modelinfo/ClumpModelInfo.cpp similarity index 100% rename from src/modelinfo/ClumpModelInfo.cpp rename to src/liberty/modelinfo/ClumpModelInfo.cpp diff --git a/src/modelinfo/ClumpModelInfo.h b/src/liberty/modelinfo/ClumpModelInfo.h similarity index 100% rename from src/modelinfo/ClumpModelInfo.h rename to src/liberty/modelinfo/ClumpModelInfo.h diff --git a/src/modelinfo/MloModelInfo.cpp b/src/liberty/modelinfo/MloModelInfo.cpp similarity index 100% rename from src/modelinfo/MloModelInfo.cpp rename to src/liberty/modelinfo/MloModelInfo.cpp diff --git a/src/modelinfo/MloModelInfo.h b/src/liberty/modelinfo/MloModelInfo.h similarity index 100% rename from src/modelinfo/MloModelInfo.h rename to src/liberty/modelinfo/MloModelInfo.h diff --git a/src/modelinfo/ModelIndices.cpp b/src/liberty/modelinfo/ModelIndices.cpp similarity index 100% rename from src/modelinfo/ModelIndices.cpp rename to src/liberty/modelinfo/ModelIndices.cpp diff --git a/src/modelinfo/ModelIndices.h b/src/liberty/modelinfo/ModelIndices.h similarity index 100% rename from src/modelinfo/ModelIndices.h rename to src/liberty/modelinfo/ModelIndices.h diff --git a/src/modelinfo/ModelInfo.cpp b/src/liberty/modelinfo/ModelInfo.cpp similarity index 100% rename from src/modelinfo/ModelInfo.cpp rename to src/liberty/modelinfo/ModelInfo.cpp diff --git a/src/modelinfo/ModelInfo.h b/src/liberty/modelinfo/ModelInfo.h similarity index 100% rename from src/modelinfo/ModelInfo.h rename to src/liberty/modelinfo/ModelInfo.h diff --git a/src/modelinfo/PedModelInfo.cpp b/src/liberty/modelinfo/PedModelInfo.cpp similarity index 100% rename from src/modelinfo/PedModelInfo.cpp rename to src/liberty/modelinfo/PedModelInfo.cpp diff --git a/src/modelinfo/PedModelInfo.h b/src/liberty/modelinfo/PedModelInfo.h similarity index 100% rename from src/modelinfo/PedModelInfo.h rename to src/liberty/modelinfo/PedModelInfo.h diff --git a/src/modelinfo/SimpleModelInfo.cpp b/src/liberty/modelinfo/SimpleModelInfo.cpp similarity index 100% rename from src/modelinfo/SimpleModelInfo.cpp rename to src/liberty/modelinfo/SimpleModelInfo.cpp diff --git a/src/modelinfo/SimpleModelInfo.h b/src/liberty/modelinfo/SimpleModelInfo.h similarity index 100% rename from src/modelinfo/SimpleModelInfo.h rename to src/liberty/modelinfo/SimpleModelInfo.h diff --git a/src/modelinfo/TimeModelInfo.cpp b/src/liberty/modelinfo/TimeModelInfo.cpp similarity index 100% rename from src/modelinfo/TimeModelInfo.cpp rename to src/liberty/modelinfo/TimeModelInfo.cpp diff --git a/src/modelinfo/TimeModelInfo.h b/src/liberty/modelinfo/TimeModelInfo.h similarity index 100% rename from src/modelinfo/TimeModelInfo.h rename to src/liberty/modelinfo/TimeModelInfo.h diff --git a/src/modelinfo/VehicleModelInfo.cpp b/src/liberty/modelinfo/VehicleModelInfo.cpp similarity index 100% rename from src/modelinfo/VehicleModelInfo.cpp rename to src/liberty/modelinfo/VehicleModelInfo.cpp diff --git a/src/modelinfo/VehicleModelInfo.h b/src/liberty/modelinfo/VehicleModelInfo.h similarity index 100% rename from src/modelinfo/VehicleModelInfo.h rename to src/liberty/modelinfo/VehicleModelInfo.h diff --git a/src/modelinfo/XtraCompsModelInfo.h b/src/liberty/modelinfo/XtraCompsModelInfo.h similarity index 100% rename from src/modelinfo/XtraCompsModelInfo.h rename to src/liberty/modelinfo/XtraCompsModelInfo.h diff --git a/src/objects/CutsceneHead.cpp b/src/liberty/objects/CutsceneHead.cpp similarity index 100% rename from src/objects/CutsceneHead.cpp rename to src/liberty/objects/CutsceneHead.cpp diff --git a/src/objects/CutsceneHead.h b/src/liberty/objects/CutsceneHead.h similarity index 100% rename from src/objects/CutsceneHead.h rename to src/liberty/objects/CutsceneHead.h diff --git a/src/objects/CutsceneObject.cpp b/src/liberty/objects/CutsceneObject.cpp similarity index 100% rename from src/objects/CutsceneObject.cpp rename to src/liberty/objects/CutsceneObject.cpp diff --git a/src/objects/CutsceneObject.h b/src/liberty/objects/CutsceneObject.h similarity index 100% rename from src/objects/CutsceneObject.h rename to src/liberty/objects/CutsceneObject.h diff --git a/src/objects/DummyObject.cpp b/src/liberty/objects/DummyObject.cpp similarity index 100% rename from src/objects/DummyObject.cpp rename to src/liberty/objects/DummyObject.cpp diff --git a/src/objects/DummyObject.h b/src/liberty/objects/DummyObject.h similarity index 100% rename from src/objects/DummyObject.h rename to src/liberty/objects/DummyObject.h diff --git a/src/objects/Object.cpp b/src/liberty/objects/Object.cpp similarity index 100% rename from src/objects/Object.cpp rename to src/liberty/objects/Object.cpp diff --git a/src/objects/Object.h b/src/liberty/objects/Object.h similarity index 100% rename from src/objects/Object.h rename to src/liberty/objects/Object.h diff --git a/src/objects/ObjectData.cpp b/src/liberty/objects/ObjectData.cpp similarity index 100% rename from src/objects/ObjectData.cpp rename to src/liberty/objects/ObjectData.cpp diff --git a/src/objects/ObjectData.h b/src/liberty/objects/ObjectData.h similarity index 100% rename from src/objects/ObjectData.h rename to src/liberty/objects/ObjectData.h diff --git a/src/objects/ParticleObject.cpp b/src/liberty/objects/ParticleObject.cpp similarity index 100% rename from src/objects/ParticleObject.cpp rename to src/liberty/objects/ParticleObject.cpp diff --git a/src/objects/ParticleObject.h b/src/liberty/objects/ParticleObject.h similarity index 100% rename from src/objects/ParticleObject.h rename to src/liberty/objects/ParticleObject.h diff --git a/src/objects/Projectile.cpp b/src/liberty/objects/Projectile.cpp similarity index 100% rename from src/objects/Projectile.cpp rename to src/liberty/objects/Projectile.cpp diff --git a/src/objects/Projectile.h b/src/liberty/objects/Projectile.h similarity index 100% rename from src/objects/Projectile.h rename to src/liberty/objects/Projectile.h diff --git a/src/peds/CivilianPed.cpp b/src/liberty/peds/CivilianPed.cpp similarity index 100% rename from src/peds/CivilianPed.cpp rename to src/liberty/peds/CivilianPed.cpp diff --git a/src/peds/CivilianPed.h b/src/liberty/peds/CivilianPed.h similarity index 100% rename from src/peds/CivilianPed.h rename to src/liberty/peds/CivilianPed.h diff --git a/src/peds/CopPed.cpp b/src/liberty/peds/CopPed.cpp similarity index 100% rename from src/peds/CopPed.cpp rename to src/liberty/peds/CopPed.cpp diff --git a/src/peds/CopPed.h b/src/liberty/peds/CopPed.h similarity index 100% rename from src/peds/CopPed.h rename to src/liberty/peds/CopPed.h diff --git a/src/peds/DummyPed.h b/src/liberty/peds/DummyPed.h similarity index 100% rename from src/peds/DummyPed.h rename to src/liberty/peds/DummyPed.h diff --git a/src/peds/EmergencyPed.cpp b/src/liberty/peds/EmergencyPed.cpp similarity index 100% rename from src/peds/EmergencyPed.cpp rename to src/liberty/peds/EmergencyPed.cpp diff --git a/src/peds/EmergencyPed.h b/src/liberty/peds/EmergencyPed.h similarity index 100% rename from src/peds/EmergencyPed.h rename to src/liberty/peds/EmergencyPed.h diff --git a/src/peds/Gangs.cpp b/src/liberty/peds/Gangs.cpp similarity index 100% rename from src/peds/Gangs.cpp rename to src/liberty/peds/Gangs.cpp diff --git a/src/peds/Gangs.h b/src/liberty/peds/Gangs.h similarity index 100% rename from src/peds/Gangs.h rename to src/liberty/peds/Gangs.h diff --git a/src/peds/Ped.cpp b/src/liberty/peds/Ped.cpp similarity index 100% rename from src/peds/Ped.cpp rename to src/liberty/peds/Ped.cpp diff --git a/src/peds/Ped.h b/src/liberty/peds/Ped.h similarity index 100% rename from src/peds/Ped.h rename to src/liberty/peds/Ped.h diff --git a/src/peds/PedAI.cpp b/src/liberty/peds/PedAI.cpp similarity index 99% rename from src/peds/PedAI.cpp rename to src/liberty/peds/PedAI.cpp index b6e575c9..0fe9542f 100644 --- a/src/peds/PedAI.cpp +++ b/src/liberty/peds/PedAI.cpp @@ -4249,7 +4249,7 @@ CPed::SetAnimOffsetForEnterOrExitVehicle(void) if (!seq->HasTranslation()) vecPedDraggedOutCarAnimOffset = CVector(0.0f, 0.0f, 0.0f); else { - vecPedDraggedOutCarAnimOffset = seq->GetTranslation(seq->numFrames - 1); + vecPedDraggedOutCarAnimOffset = seq->GetEndTranslation(); } } @@ -4260,7 +4260,7 @@ CPed::SetAnimOffsetForEnterOrExitVehicle(void) if (!seq->HasTranslation()) vecPedCarDoorAnimOffset = CVector(0.0f, 0.0f, 0.0f); else { - vecPedCarDoorAnimOffset = seq->GetTranslation(seq->numFrames - 1); + vecPedCarDoorAnimOffset = seq->GetEndTranslation(); } } @@ -4271,7 +4271,7 @@ CPed::SetAnimOffsetForEnterOrExitVehicle(void) if (!seq->HasTranslation()) vecPedCarDoorLoAnimOffset = CVector(0.0f, 0.0f, 0.0f); else { - vecPedCarDoorLoAnimOffset = seq->GetTranslation(seq->numFrames - 1); + vecPedCarDoorLoAnimOffset = seq->GetEndTranslation(); } } @@ -4282,7 +4282,7 @@ CPed::SetAnimOffsetForEnterOrExitVehicle(void) if (!seq->HasTranslation()) vecPedQuickDraggedOutCarAnimOffset = CVector(0.0f, 0.0f, 0.0f); else { - vecPedQuickDraggedOutCarAnimOffset = seq->GetTranslation(seq->numFrames - 1); + vecPedQuickDraggedOutCarAnimOffset = seq->GetEndTranslation(); } } @@ -4293,7 +4293,7 @@ CPed::SetAnimOffsetForEnterOrExitVehicle(void) if (!seq->HasTranslation()) vecPedVanRearDoorAnimOffset = CVector(0.0f, 0.0f, 0.0f); else { - vecPedVanRearDoorAnimOffset = seq->GetTranslation(seq->numFrames - 1); + vecPedVanRearDoorAnimOffset = seq->GetEndTranslation(); } } @@ -4304,7 +4304,7 @@ CPed::SetAnimOffsetForEnterOrExitVehicle(void) if (!seq->HasTranslation()) vecPedTrainDoorAnimOffset = CVector(0.0f, 0.0f, 0.0f); else { - vecPedTrainDoorAnimOffset = seq->GetTranslation(seq->numFrames - 1); + vecPedTrainDoorAnimOffset = seq->GetEndTranslation(); } } } diff --git a/src/peds/PedChat.cpp b/src/liberty/peds/PedChat.cpp similarity index 100% rename from src/peds/PedChat.cpp rename to src/liberty/peds/PedChat.cpp diff --git a/src/peds/PedDebug.cpp b/src/liberty/peds/PedDebug.cpp similarity index 100% rename from src/peds/PedDebug.cpp rename to src/liberty/peds/PedDebug.cpp diff --git a/src/peds/PedFight.cpp b/src/liberty/peds/PedFight.cpp similarity index 100% rename from src/peds/PedFight.cpp rename to src/liberty/peds/PedFight.cpp diff --git a/src/peds/PedIK.cpp b/src/liberty/peds/PedIK.cpp similarity index 100% rename from src/peds/PedIK.cpp rename to src/liberty/peds/PedIK.cpp diff --git a/src/peds/PedIK.h b/src/liberty/peds/PedIK.h similarity index 100% rename from src/peds/PedIK.h rename to src/liberty/peds/PedIK.h diff --git a/src/peds/PedPlacement.cpp b/src/liberty/peds/PedPlacement.cpp similarity index 100% rename from src/peds/PedPlacement.cpp rename to src/liberty/peds/PedPlacement.cpp diff --git a/src/peds/PedPlacement.h b/src/liberty/peds/PedPlacement.h similarity index 100% rename from src/peds/PedPlacement.h rename to src/liberty/peds/PedPlacement.h diff --git a/src/peds/PedRoutes.cpp b/src/liberty/peds/PedRoutes.cpp similarity index 100% rename from src/peds/PedRoutes.cpp rename to src/liberty/peds/PedRoutes.cpp diff --git a/src/peds/PedRoutes.h b/src/liberty/peds/PedRoutes.h similarity index 100% rename from src/peds/PedRoutes.h rename to src/liberty/peds/PedRoutes.h diff --git a/src/peds/PedType.cpp b/src/liberty/peds/PedType.cpp similarity index 100% rename from src/peds/PedType.cpp rename to src/liberty/peds/PedType.cpp diff --git a/src/peds/PedType.h b/src/liberty/peds/PedType.h similarity index 100% rename from src/peds/PedType.h rename to src/liberty/peds/PedType.h diff --git a/src/peds/PlayerPed.cpp b/src/liberty/peds/PlayerPed.cpp similarity index 100% rename from src/peds/PlayerPed.cpp rename to src/liberty/peds/PlayerPed.cpp diff --git a/src/peds/PlayerPed.h b/src/liberty/peds/PlayerPed.h similarity index 100% rename from src/peds/PlayerPed.h rename to src/liberty/peds/PlayerPed.h diff --git a/src/peds/Population.cpp b/src/liberty/peds/Population.cpp similarity index 100% rename from src/peds/Population.cpp rename to src/liberty/peds/Population.cpp diff --git a/src/peds/Population.h b/src/liberty/peds/Population.h similarity index 100% rename from src/peds/Population.h rename to src/liberty/peds/Population.h diff --git a/src/renderer/2dEffect.h b/src/liberty/renderer/2dEffect.h similarity index 100% rename from src/renderer/2dEffect.h rename to src/liberty/renderer/2dEffect.h diff --git a/src/renderer/Antennas.cpp b/src/liberty/renderer/Antennas.cpp similarity index 100% rename from src/renderer/Antennas.cpp rename to src/liberty/renderer/Antennas.cpp diff --git a/src/renderer/Antennas.h b/src/liberty/renderer/Antennas.h similarity index 100% rename from src/renderer/Antennas.h rename to src/liberty/renderer/Antennas.h diff --git a/src/renderer/Clouds.cpp b/src/liberty/renderer/Clouds.cpp similarity index 99% rename from src/renderer/Clouds.cpp rename to src/liberty/renderer/Clouds.cpp index ac0a2ce0..f91999db 100644 --- a/src/renderer/Clouds.cpp +++ b/src/liberty/renderer/Clouds.cpp @@ -261,7 +261,7 @@ CClouds::Render(void) szx*55.0f, szy*55.0f, tr, tg, tb, br, bg, bb, 0.0f, -1.0f, 1.0f/screenpos.z, - (uint16)IndividualRotation/65336.0f * 6.28f + ms_cameraRoll, + (uint16)IndividualRotation/65536.0f * 6.28f + ms_cameraRoll, //was: 65336 fluffyalpha); bCloudOnScreen[i] = true; }else diff --git a/src/renderer/Clouds.h b/src/liberty/renderer/Clouds.h similarity index 100% rename from src/renderer/Clouds.h rename to src/liberty/renderer/Clouds.h diff --git a/src/renderer/Console.cpp b/src/liberty/renderer/Console.cpp similarity index 100% rename from src/renderer/Console.cpp rename to src/liberty/renderer/Console.cpp diff --git a/src/renderer/Console.h b/src/liberty/renderer/Console.h similarity index 100% rename from src/renderer/Console.h rename to src/liberty/renderer/Console.h diff --git a/src/renderer/Coronas.cpp b/src/liberty/renderer/Coronas.cpp similarity index 100% rename from src/renderer/Coronas.cpp rename to src/liberty/renderer/Coronas.cpp diff --git a/src/renderer/Coronas.h b/src/liberty/renderer/Coronas.h similarity index 100% rename from src/renderer/Coronas.h rename to src/liberty/renderer/Coronas.h diff --git a/src/renderer/Credits.cpp b/src/liberty/renderer/Credits.cpp similarity index 100% rename from src/renderer/Credits.cpp rename to src/liberty/renderer/Credits.cpp diff --git a/src/renderer/Credits.h b/src/liberty/renderer/Credits.h similarity index 100% rename from src/renderer/Credits.h rename to src/liberty/renderer/Credits.h diff --git a/src/renderer/Draw.cpp b/src/liberty/renderer/Draw.cpp similarity index 100% rename from src/renderer/Draw.cpp rename to src/liberty/renderer/Draw.cpp diff --git a/src/renderer/Draw.h b/src/liberty/renderer/Draw.h similarity index 100% rename from src/renderer/Draw.h rename to src/liberty/renderer/Draw.h diff --git a/src/renderer/Fluff.cpp b/src/liberty/renderer/Fluff.cpp similarity index 100% rename from src/renderer/Fluff.cpp rename to src/liberty/renderer/Fluff.cpp diff --git a/src/renderer/Fluff.h b/src/liberty/renderer/Fluff.h similarity index 100% rename from src/renderer/Fluff.h rename to src/liberty/renderer/Fluff.h diff --git a/src/renderer/Font.cpp b/src/liberty/renderer/Font.cpp similarity index 100% rename from src/renderer/Font.cpp rename to src/liberty/renderer/Font.cpp diff --git a/src/renderer/Font.h b/src/liberty/renderer/Font.h similarity index 100% rename from src/renderer/Font.h rename to src/liberty/renderer/Font.h diff --git a/src/renderer/Glass.cpp b/src/liberty/renderer/Glass.cpp similarity index 100% rename from src/renderer/Glass.cpp rename to src/liberty/renderer/Glass.cpp diff --git a/src/renderer/Glass.h b/src/liberty/renderer/Glass.h similarity index 100% rename from src/renderer/Glass.h rename to src/liberty/renderer/Glass.h diff --git a/src/renderer/Hud.cpp b/src/liberty/renderer/Hud.cpp similarity index 100% rename from src/renderer/Hud.cpp rename to src/liberty/renderer/Hud.cpp diff --git a/src/renderer/Hud.h b/src/liberty/renderer/Hud.h similarity index 100% rename from src/renderer/Hud.h rename to src/liberty/renderer/Hud.h diff --git a/src/renderer/Instance.cpp b/src/liberty/renderer/Instance.cpp similarity index 100% rename from src/renderer/Instance.cpp rename to src/liberty/renderer/Instance.cpp diff --git a/src/renderer/Instance.h b/src/liberty/renderer/Instance.h similarity index 100% rename from src/renderer/Instance.h rename to src/liberty/renderer/Instance.h diff --git a/src/renderer/Lines.cpp b/src/liberty/renderer/Lines.cpp similarity index 100% rename from src/renderer/Lines.cpp rename to src/liberty/renderer/Lines.cpp diff --git a/src/renderer/Lines.h b/src/liberty/renderer/Lines.h similarity index 100% rename from src/renderer/Lines.h rename to src/liberty/renderer/Lines.h diff --git a/src/renderer/MBlur.cpp b/src/liberty/renderer/MBlur.cpp similarity index 100% rename from src/renderer/MBlur.cpp rename to src/liberty/renderer/MBlur.cpp diff --git a/src/renderer/MBlur.h b/src/liberty/renderer/MBlur.h similarity index 100% rename from src/renderer/MBlur.h rename to src/liberty/renderer/MBlur.h diff --git a/src/renderer/Particle.cpp b/src/liberty/renderer/Particle.cpp similarity index 100% rename from src/renderer/Particle.cpp rename to src/liberty/renderer/Particle.cpp diff --git a/src/renderer/Particle.h b/src/liberty/renderer/Particle.h similarity index 100% rename from src/renderer/Particle.h rename to src/liberty/renderer/Particle.h diff --git a/src/renderer/ParticleMgr.cpp b/src/liberty/renderer/ParticleMgr.cpp similarity index 100% rename from src/renderer/ParticleMgr.cpp rename to src/liberty/renderer/ParticleMgr.cpp diff --git a/src/renderer/ParticleMgr.h b/src/liberty/renderer/ParticleMgr.h similarity index 100% rename from src/renderer/ParticleMgr.h rename to src/liberty/renderer/ParticleMgr.h diff --git a/src/renderer/ParticleType.h b/src/liberty/renderer/ParticleType.h similarity index 100% rename from src/renderer/ParticleType.h rename to src/liberty/renderer/ParticleType.h diff --git a/src/renderer/PlayerSkin.cpp b/src/liberty/renderer/PlayerSkin.cpp similarity index 100% rename from src/renderer/PlayerSkin.cpp rename to src/liberty/renderer/PlayerSkin.cpp diff --git a/src/renderer/PlayerSkin.h b/src/liberty/renderer/PlayerSkin.h similarity index 100% rename from src/renderer/PlayerSkin.h rename to src/liberty/renderer/PlayerSkin.h diff --git a/src/renderer/PointLights.cpp b/src/liberty/renderer/PointLights.cpp similarity index 100% rename from src/renderer/PointLights.cpp rename to src/liberty/renderer/PointLights.cpp diff --git a/src/renderer/PointLights.h b/src/liberty/renderer/PointLights.h similarity index 100% rename from src/renderer/PointLights.h rename to src/liberty/renderer/PointLights.h diff --git a/src/renderer/RenderBuffer.cpp b/src/liberty/renderer/RenderBuffer.cpp similarity index 100% rename from src/renderer/RenderBuffer.cpp rename to src/liberty/renderer/RenderBuffer.cpp diff --git a/src/renderer/RenderBuffer.h b/src/liberty/renderer/RenderBuffer.h similarity index 100% rename from src/renderer/RenderBuffer.h rename to src/liberty/renderer/RenderBuffer.h diff --git a/src/renderer/Renderer.cpp b/src/liberty/renderer/Renderer.cpp similarity index 100% rename from src/renderer/Renderer.cpp rename to src/liberty/renderer/Renderer.cpp diff --git a/src/renderer/Renderer.h b/src/liberty/renderer/Renderer.h similarity index 100% rename from src/renderer/Renderer.h rename to src/liberty/renderer/Renderer.h diff --git a/src/renderer/Rubbish.cpp b/src/liberty/renderer/Rubbish.cpp similarity index 100% rename from src/renderer/Rubbish.cpp rename to src/liberty/renderer/Rubbish.cpp diff --git a/src/renderer/Rubbish.h b/src/liberty/renderer/Rubbish.h similarity index 100% rename from src/renderer/Rubbish.h rename to src/liberty/renderer/Rubbish.h diff --git a/src/renderer/Shadows.cpp b/src/liberty/renderer/Shadows.cpp similarity index 100% rename from src/renderer/Shadows.cpp rename to src/liberty/renderer/Shadows.cpp diff --git a/src/renderer/Shadows.h b/src/liberty/renderer/Shadows.h similarity index 100% rename from src/renderer/Shadows.h rename to src/liberty/renderer/Shadows.h diff --git a/src/renderer/Skidmarks.cpp b/src/liberty/renderer/Skidmarks.cpp similarity index 100% rename from src/renderer/Skidmarks.cpp rename to src/liberty/renderer/Skidmarks.cpp diff --git a/src/renderer/Skidmarks.h b/src/liberty/renderer/Skidmarks.h similarity index 100% rename from src/renderer/Skidmarks.h rename to src/liberty/renderer/Skidmarks.h diff --git a/src/renderer/SpecialFX.cpp b/src/liberty/renderer/SpecialFX.cpp similarity index 100% rename from src/renderer/SpecialFX.cpp rename to src/liberty/renderer/SpecialFX.cpp diff --git a/src/renderer/SpecialFX.h b/src/liberty/renderer/SpecialFX.h similarity index 100% rename from src/renderer/SpecialFX.h rename to src/liberty/renderer/SpecialFX.h diff --git a/src/renderer/Sprite.cpp b/src/liberty/renderer/Sprite.cpp similarity index 100% rename from src/renderer/Sprite.cpp rename to src/liberty/renderer/Sprite.cpp diff --git a/src/renderer/Sprite.h b/src/liberty/renderer/Sprite.h similarity index 100% rename from src/renderer/Sprite.h rename to src/liberty/renderer/Sprite.h diff --git a/src/renderer/Sprite2d.cpp b/src/liberty/renderer/Sprite2d.cpp similarity index 98% rename from src/renderer/Sprite2d.cpp rename to src/liberty/renderer/Sprite2d.cpp index 5214ef4e..c0976fdf 100644 --- a/src/renderer/Sprite2d.cpp +++ b/src/liberty/renderer/Sprite2d.cpp @@ -177,8 +177,8 @@ CSprite2d::SetVertices(const CRect &r, const CRGBA &c0, const CRGBA &c1, const C z = RwCameraGetFarClipPlane(Scene.camera); }else{ screenz = RwIm2DGetNearScreenZ(); - z = 1.0f/RecipNearClip; RecipNearClip *= 1.1f; + z = 1.0f/RecipNearClip; } recipz = 1.0f/z; float offset = 1.0f/1024.0f; @@ -231,9 +231,9 @@ CSprite2d::SetVertices(const CRect &r, const CRGBA &c0, const CRGBA &c1, const C float screenz, z, recipz; screenz = RwIm2DGetNearScreenZ(); + RecipNearClip *= 1.1f; z = 1.0f/RecipNearClip; recipz = 1.0f/z; - RecipNearClip *= 1.1f; // This is what we draw: // 0---1 @@ -286,6 +286,7 @@ CSprite2d::SetVertices(float x1, float y1, float x2, float y2, float x3, float y screenz = RwIm2DGetNearScreenZ(); recipz = RecipNearClip; RecipNearClip *= 1.1f; + z = 1/RecipNearClip; RwIm2DVertexSetScreenX(&maVertices[0], x3); RwIm2DVertexSetScreenY(&maVertices[0], y3); @@ -333,7 +334,7 @@ CSprite2d::SetVertices(int n, float *positions, float *uvs, const CRGBA &col) screenz = RwIm2DGetNearScreenZ(); recipz = RecipNearClip; RecipNearClip *= 1.1f; - z = RwCameraGetNearClipPlane(Scene.camera); // not done by game + z = 1/RecipNearClip; for(i = 0; i < n; i++){ @@ -357,7 +358,7 @@ CSprite2d::SetMaskVertices(int n, float *positions) screenz = RwIm2DGetNearScreenZ(); recipz = RecipNearClip; RecipNearClip *= 1.1f; - z = RwCameraGetNearClipPlane(Scene.camera); // not done by game + z = 1/RecipNearClip; for(i = 0; i < n; i++){ RwIm2DVertexSetScreenX(&maVertices[i], positions[i*2 + 0]); @@ -382,7 +383,7 @@ CSprite2d::SetVertices(RwIm2DVertex *verts, const CRect &r, const CRGBA &c0, con screenz = RwIm2DGetNearScreenZ(); recipz = RecipNearClip; RecipNearClip *= 1.1f; - z = RwCameraGetNearClipPlane(Scene.camera); // not done by game + z = 1/RecipNearClip; RwIm2DVertexSetScreenX(&verts[0], r.left); RwIm2DVertexSetScreenY(&verts[0], r.top); diff --git a/src/renderer/Sprite2d.h b/src/liberty/renderer/Sprite2d.h similarity index 100% rename from src/renderer/Sprite2d.h rename to src/liberty/renderer/Sprite2d.h diff --git a/src/renderer/TexList.cpp b/src/liberty/renderer/TexList.cpp similarity index 100% rename from src/renderer/TexList.cpp rename to src/liberty/renderer/TexList.cpp diff --git a/src/renderer/TexList.h b/src/liberty/renderer/TexList.h similarity index 100% rename from src/renderer/TexList.h rename to src/liberty/renderer/TexList.h diff --git a/src/renderer/Timecycle.cpp b/src/liberty/renderer/Timecycle.cpp similarity index 100% rename from src/renderer/Timecycle.cpp rename to src/liberty/renderer/Timecycle.cpp diff --git a/src/renderer/Timecycle.h b/src/liberty/renderer/Timecycle.h similarity index 100% rename from src/renderer/Timecycle.h rename to src/liberty/renderer/Timecycle.h diff --git a/src/renderer/WaterCannon.cpp b/src/liberty/renderer/WaterCannon.cpp similarity index 100% rename from src/renderer/WaterCannon.cpp rename to src/liberty/renderer/WaterCannon.cpp diff --git a/src/renderer/WaterCannon.h b/src/liberty/renderer/WaterCannon.h similarity index 100% rename from src/renderer/WaterCannon.h rename to src/liberty/renderer/WaterCannon.h diff --git a/src/renderer/WaterLevel.cpp b/src/liberty/renderer/WaterLevel.cpp similarity index 99% rename from src/renderer/WaterLevel.cpp rename to src/liberty/renderer/WaterLevel.cpp index 37f38b9a..945abc2c 100644 --- a/src/renderer/WaterLevel.cpp +++ b/src/liberty/renderer/WaterLevel.cpp @@ -1479,7 +1479,8 @@ CWaterLevel::AllocateBoatWakeArray() apGeomArray[geom] = RpGeometryCreate(9*9, 8*8*2, rpGEOMETRYTRISTRIP | rpGEOMETRYPRELIT | rpGEOMETRYMODULATEMATERIALCOLOR - | rpGEOMETRYTEXTURED); + | rpGEOMETRYTEXTURED + | rw::Geometry::HAS_TRIANGLES/* RW_DC specific */); ASSERT(apGeomArray[geom] != nil); RpTriangle *geomTriangles = RpGeometryGetTriangles(apGeomArray[geom]); diff --git a/src/renderer/WaterLevel.h b/src/liberty/renderer/WaterLevel.h similarity index 100% rename from src/renderer/WaterLevel.h rename to src/liberty/renderer/WaterLevel.h diff --git a/src/renderer/Weather.cpp b/src/liberty/renderer/Weather.cpp similarity index 100% rename from src/renderer/Weather.cpp rename to src/liberty/renderer/Weather.cpp diff --git a/src/renderer/Weather.h b/src/liberty/renderer/Weather.h similarity index 100% rename from src/renderer/Weather.h rename to src/liberty/renderer/Weather.h diff --git a/src/rw/ClumpRead.cpp b/src/liberty/rw/ClumpRead.cpp similarity index 100% rename from src/rw/ClumpRead.cpp rename to src/liberty/rw/ClumpRead.cpp diff --git a/src/rw/Lights.cpp b/src/liberty/rw/Lights.cpp similarity index 100% rename from src/rw/Lights.cpp rename to src/liberty/rw/Lights.cpp diff --git a/src/rw/Lights.h b/src/liberty/rw/Lights.h similarity index 100% rename from src/rw/Lights.h rename to src/liberty/rw/Lights.h diff --git a/src/rw/MemoryHeap.cpp b/src/liberty/rw/MemoryHeap.cpp similarity index 100% rename from src/rw/MemoryHeap.cpp rename to src/liberty/rw/MemoryHeap.cpp diff --git a/src/rw/MemoryHeap.h b/src/liberty/rw/MemoryHeap.h similarity index 100% rename from src/rw/MemoryHeap.h rename to src/liberty/rw/MemoryHeap.h diff --git a/src/rw/MemoryMgr.cpp b/src/liberty/rw/MemoryMgr.cpp similarity index 100% rename from src/rw/MemoryMgr.cpp rename to src/liberty/rw/MemoryMgr.cpp diff --git a/src/rw/MemoryMgr.h b/src/liberty/rw/MemoryMgr.h similarity index 100% rename from src/rw/MemoryMgr.h rename to src/liberty/rw/MemoryMgr.h diff --git a/src/rw/NodeName.cpp b/src/liberty/rw/NodeName.cpp similarity index 100% rename from src/rw/NodeName.cpp rename to src/liberty/rw/NodeName.cpp diff --git a/src/rw/NodeName.h b/src/liberty/rw/NodeName.h similarity index 100% rename from src/rw/NodeName.h rename to src/liberty/rw/NodeName.h diff --git a/src/rw/RwHelper.cpp b/src/liberty/rw/RwHelper.cpp similarity index 100% rename from src/rw/RwHelper.cpp rename to src/liberty/rw/RwHelper.cpp diff --git a/src/rw/RwHelper.h b/src/liberty/rw/RwHelper.h similarity index 100% rename from src/rw/RwHelper.h rename to src/liberty/rw/RwHelper.h diff --git a/src/rw/RwMatFX.cpp b/src/liberty/rw/RwMatFX.cpp similarity index 100% rename from src/rw/RwMatFX.cpp rename to src/liberty/rw/RwMatFX.cpp diff --git a/src/rw/RwPS2AlphaTest.cpp b/src/liberty/rw/RwPS2AlphaTest.cpp similarity index 100% rename from src/rw/RwPS2AlphaTest.cpp rename to src/liberty/rw/RwPS2AlphaTest.cpp diff --git a/src/rw/TexRead.cpp b/src/liberty/rw/TexRead.cpp similarity index 100% rename from src/rw/TexRead.cpp rename to src/liberty/rw/TexRead.cpp diff --git a/src/rw/TexturePools.cpp b/src/liberty/rw/TexturePools.cpp similarity index 100% rename from src/rw/TexturePools.cpp rename to src/liberty/rw/TexturePools.cpp diff --git a/src/rw/TexturePools.h b/src/liberty/rw/TexturePools.h similarity index 100% rename from src/rw/TexturePools.h rename to src/liberty/rw/TexturePools.h diff --git a/src/rw/TxdStore.cpp b/src/liberty/rw/TxdStore.cpp similarity index 100% rename from src/rw/TxdStore.cpp rename to src/liberty/rw/TxdStore.cpp diff --git a/src/rw/TxdStore.h b/src/liberty/rw/TxdStore.h similarity index 100% rename from src/rw/TxdStore.h rename to src/liberty/rw/TxdStore.h diff --git a/src/rw/VisibilityPlugins.cpp b/src/liberty/rw/VisibilityPlugins.cpp similarity index 100% rename from src/rw/VisibilityPlugins.cpp rename to src/liberty/rw/VisibilityPlugins.cpp diff --git a/src/rw/VisibilityPlugins.h b/src/liberty/rw/VisibilityPlugins.h similarity index 100% rename from src/rw/VisibilityPlugins.h rename to src/liberty/rw/VisibilityPlugins.h diff --git a/src/save/Date.cpp b/src/liberty/save/Date.cpp similarity index 100% rename from src/save/Date.cpp rename to src/liberty/save/Date.cpp diff --git a/src/save/Date.h b/src/liberty/save/Date.h similarity index 100% rename from src/save/Date.h rename to src/liberty/save/Date.h diff --git a/src/save/GenericGameStorage.cpp b/src/liberty/save/GenericGameStorage.cpp similarity index 96% rename from src/save/GenericGameStorage.cpp rename to src/liberty/save/GenericGameStorage.cpp index 84238082..e7770058 100644 --- a/src/save/GenericGameStorage.cpp +++ b/src/liberty/save/GenericGameStorage.cpp @@ -38,7 +38,7 @@ #include "World.h" #include "Zones.h" -#include "../vmu/vmu.h" +#include "vmu/vmu.h" #define BLOCK_COUNT 20 #define SIZE_OF_SIMPLEVARS 0xBC @@ -74,6 +74,10 @@ uint32 TimeToStayFadedBeforeFadeOut = 1750; do {\ size = C_PcSave::PcClassLoadRoutine(file, work_buff); \ if (!size) {\ + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_READ; \ + if (!CloseFile(file)) { \ + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_CLOSE; \ + } \ return false; \ } \ buf = work_buff;\ @@ -95,8 +99,12 @@ do {\ MakeSpaceForSizeInBufferPointer(presize, buf, postsize);\ save_func(buf, &size);\ CopySizeAndPreparePointer(presize, buf, postsize, reserved, size);\ - if (!PcSaveHelper.PcClassSaveRoutine(file, work_buff, buf - work_buff))\ + if (!PcSaveHelper.PcClassSaveRoutine(file, work_buff, buf - work_buff)) { \ + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; \ + if (!CloseFile(file)) \ + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_CLOSE; \ return false;\ + } \ totalSize += buf - work_buff;\ } while (0) @@ -195,8 +203,12 @@ GenericSave(int file) postsize = buf; CTheScripts::SaveAllScripts(buf, &size); CopySizeAndPreparePointer(presize, buf, postsize, reserved, size); - if (!PcSaveHelper.PcClassSaveRoutine(file, work_buff, buf - work_buff)) + if (!PcSaveHelper.PcClassSaveRoutine(file, work_buff, buf - work_buff)) { + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; + if (!CloseFile(file)) + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_CLOSE; return false; + } totalSize = buf - work_buff; @@ -232,15 +244,19 @@ GenericSave(int file) if (size > sizeof(work_buff)) size = sizeof(work_buff); if (size > 4) { - if (!PcSaveHelper.PcClassSaveRoutine(file, work_buff, size)) + if (!PcSaveHelper.PcClassSaveRoutine(file, work_buff, size)) { + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; + if (!CloseFile(file)) + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_CLOSE; return false; + } totalSize += size; } } // Write checksum and close - CFileMgr::Write(file, (const char *) &CheckSum, sizeof(CheckSum)); - if (CFileMgr::GetErrorReadWrite(file)) { + bool err = CFileMgr::Write(file, (const char *) &CheckSum, sizeof(CheckSum)) != sizeof(CheckSum); + if (err || CFileMgr::GetErrorReadWrite(file)) { PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; if (!CloseFile(file)) PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_CLOSE; @@ -269,9 +285,18 @@ GenericLoad() CDate dummy; // unused CPad::ResetCheats(); file = CFileMgr::OpenFile(LoadFileName, "rb"); - assert(file != 0); + if (file == 0) { + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_OPEN; + return false; + } size = C_PcSave::PcClassLoadRoutine(file, work_buff); - assert(size != 0); + if (!size) { + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_READ; + if (!CloseFile(file)) { + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_CLOSE; + } + return false; + } buf = (work_buff + 0x40); ReadDataFromBufferPointer(buf, saveSize); #ifdef MISSION_REPLAY // a hack to keep compatibility but get new data from save @@ -563,10 +588,9 @@ RestoreForStartLoad() } uint32_t size = C_PcSave::PcClassLoadRoutine(file, work_buff); - assert(size != 0); uint8 *buf = work_buff; - if (CFileMgr::GetErrorReadWrite(file)) { + if (size == 0 || CFileMgr::GetErrorReadWrite(file)) { PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_READ; if (!CloseFile(file)) PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_CLOSE; diff --git a/src/save/GenericGameStorage.h b/src/liberty/save/GenericGameStorage.h similarity index 100% rename from src/save/GenericGameStorage.h rename to src/liberty/save/GenericGameStorage.h diff --git a/src/save/MemoryCard.cpp b/src/liberty/save/MemoryCard.cpp similarity index 100% rename from src/save/MemoryCard.cpp rename to src/liberty/save/MemoryCard.cpp diff --git a/src/save/MemoryCard.h b/src/liberty/save/MemoryCard.h similarity index 100% rename from src/save/MemoryCard.h rename to src/liberty/save/MemoryCard.h diff --git a/src/save/PCSave.cpp b/src/liberty/save/PCSave.cpp similarity index 80% rename from src/save/PCSave.cpp rename to src/liberty/save/PCSave.cpp index e8ff416a..2a16fd7d 100644 --- a/src/save/PCSave.cpp +++ b/src/liberty/save/PCSave.cpp @@ -15,7 +15,7 @@ #include "minilzo.h" #include "main.h" -#include "../vmu/vmu.h" +#include "vmu/vmu.h" const char* _psGetUserFilesFolder(); @@ -61,8 +61,10 @@ C_PcSave::SaveSlot(int32 slot) #endif DoGameSpecificStuffBeforeSave(); if (GenericSave(file)) { - if (!!CFileMgr::CloseFile(file)) + if (!!CFileMgr::CloseFile(file)) { nErrorCode = SAVESTATUS_ERR_SAVE_CLOSE; + return false; + } return true; } @@ -74,23 +76,26 @@ C_PcSave::SaveSlot(int32 slot) uint32_t C_PcSave::PcClassLoadRoutine(int32 file, uint8 *data) { uint32 size; - CFileMgr::Read(file, (char*)&size, sizeof(size)); + bool err = CFileMgr::Read(file, (char*)&size, sizeof(size)) != sizeof(size); + if (err) { + return 0; + } assert(data == work_buff); if (!(size & 0x80000000)) { assert(align4bytes(size) == size); - CFileMgr::Read(file, (char*)data, align4bytes(size)); - if (CFileMgr::GetErrorReadWrite(file)) { + err = CFileMgr::Read(file, (char*)data, align4bytes(size)) != align4bytes(size); + if (err || CFileMgr::GetErrorReadWrite(file)) { return 0; } return size; } else { size &= ~0x80000000; uint8* compressed = (uint8*)malloc(size); - CFileMgr::Read(file, (const char*)compressed, size); - if (CFileMgr::GetErrorReadWrite(file)) { + err = CFileMgr::Read(file, (const char*)compressed, size) != size; + if (err || CFileMgr::GetErrorReadWrite(file)) { free(compressed); return 0; } @@ -120,26 +125,36 @@ C_PcSave::PcClassSaveRoutine(int32 file, uint8 *data, uint32 size) if (crv == LZO_E_OK) { uint32_t compressed_size32 = compressed_size | 0x80000000; - CFileMgr::Write(file, (const char*)&compressed_size32, sizeof(compressed_size32)); - if (CFileMgr::GetErrorReadWrite(file)) { + bool err = CFileMgr::Write(file, (const char*)&compressed_size32, sizeof(compressed_size32)) != sizeof(compressed_size32); + if (err || CFileMgr::GetErrorReadWrite(file)) { free(compressed); nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(ValidSaveName) - 1); return false; } - CFileMgr::Write(file, (const char*)compressed, compressed_size); + err = CFileMgr::Write(file, (const char*)compressed, compressed_size) != compressed_size; free(compressed); - } else if (crv == LZO_E_NOT_COMPRESSIBLE) { - free(compressed); - uint32_t compressed_size32 = size; - CFileMgr::Write(file, (const char*)&compressed_size32, sizeof(compressed_size32)); - if (CFileMgr::GetErrorReadWrite(file)) { + if (err || CFileMgr::GetErrorReadWrite(file)) { + nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; + strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(ValidSaveName) - 1); + return false; + } + } else if (crv == LZO_E_NOT_COMPRESSIBLE) { + free(compressed); + uint32_t compressed_size32 = size; + bool err = CFileMgr::Write(file, (const char*)&compressed_size32, sizeof(compressed_size32)) != sizeof(compressed_size32); + if (err || CFileMgr::GetErrorReadWrite(file)) { + nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; + strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(ValidSaveName) - 1); + return false; + } + err = CFileMgr::Write(file, (const char*)data, align4bytes(size)) != align4bytes(size); + if (err || CFileMgr::GetErrorReadWrite(file)) { nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(ValidSaveName) - 1); return false; } - CFileMgr::Write(file, (const char*)data, align4bytes(size)); } else { free(compressed); return false; diff --git a/src/save/PCSave.h b/src/liberty/save/PCSave.h similarity index 100% rename from src/save/PCSave.h rename to src/liberty/save/PCSave.h diff --git a/src/save/SaveBuf.h b/src/liberty/save/SaveBuf.h similarity index 100% rename from src/save/SaveBuf.h rename to src/liberty/save/SaveBuf.h diff --git a/src/skel/crossplatform.cpp b/src/liberty/skel/crossplatform.cpp similarity index 98% rename from src/skel/crossplatform.cpp rename to src/liberty/skel/crossplatform.cpp index 564a7e5d..5d56ba4a 100644 --- a/src/skel/crossplatform.cpp +++ b/src/liberty/skel/crossplatform.cpp @@ -53,9 +53,13 @@ HANDLE FindFirstFile(const char* pathname, WIN32_FIND_DATA* firstfile) { if (realFolder) free(realFolder); - HANDLE d; - if ((d = (HANDLE)opendir(firstfile->folder)) == NULL || !FindNextFile(d, firstfile)) - return NULL; + HANDLE d; + if ((d = (HANDLE)opendir(firstfile->folder)) == NULL || !FindNextFile(d, firstfile)) { + if (d != NULL) { + closedir((DIR*)d); + } + return NULL; + } return d; } diff --git a/src/skel/crossplatform.h b/src/liberty/skel/crossplatform.h similarity index 100% rename from src/skel/crossplatform.h rename to src/liberty/skel/crossplatform.h diff --git a/src/skel/dc/dc.cpp b/src/liberty/skel/dc/dc.cpp similarity index 97% rename from src/skel/dc/dc.cpp rename to src/liberty/skel/dc/dc.cpp index 4f2e8ccb..d3e3bde9 100644 --- a/src/skel/dc/dc.cpp +++ b/src/liberty/skel/dc/dc.cpp @@ -1,6 +1,6 @@ #if defined RW_DC -#include "../vmu/vmu.h" +#include "vmu/vmu.h" #include #include #include @@ -8,7 +8,7 @@ #if !defined(DC_SIM) #include KOS_INIT_FLAGS(INIT_IRQ | INIT_CONTROLLER | INIT_CDROM | INIT_VMU); -#include "../prof/profiler.h" +#include "prof/profiler.h" #endif #ifdef _WIN32 @@ -62,7 +62,7 @@ long _dwOperatingSystemVersion; #include "AnimViewer.h" #include "Font.h" #include "MemoryMgr.h" -#include "../../dreamcast/git-version.h" +#include "git-version.h" #include "dc.h" #include @@ -2028,65 +2028,10 @@ __attribute__((noinline)) void stacktrace() { #include #include -extern "C" { - extern const unsigned char _build_id_start[]; - extern const unsigned char _build_id_end[]; -} - -std::string getBuildId() -{ - // Pointer to the start of the .note.gnu.build-id section - const unsigned char *p = _build_id_start; - - // Parse the ELF note header - struct NoteHeader { - uint32_t n_namesz; - uint32_t n_descsz; - uint32_t n_type; - }; - - // Read header fields (be careful with endianness if needed) - const auto* note = reinterpret_cast(p); - - // Move p beyond the note header - p += sizeof(NoteHeader); - - // Skip the "name" field + alignment (e.g. "GNU\0") - // name is note->n_namesz bytes, then align up to 4 bytes - auto nameBytes = (note->n_namesz + 3u) & ~3u; - p += nameBytes; - - // Now p should point to the actual build-id bytes, which are note->n_descsz in length. - const unsigned char* buildId = p; - auto buildIdSize = note->n_descsz; - - // Convert it to a hex string - std::ostringstream oss; - oss << std::hex << std::setfill('0'); - for (uint32_t i = 0; i < buildIdSize; i++) { - oss << std::setw(2) << static_cast(buildId[i]); - } - - return oss.str(); -} -#else -std::string getBuildId() { - return "non-dreamcast-build"; -} #endif -const char* getSourceId() { - return GIT_VERSION; -} - -const char* getCIJobId() { - return CI_JOB_ID; -} - -static std::string executableTag = getBuildId().substr(0, 10) + ":" + getSourceId() + ":" + getCIJobId(); - const char* getExecutableTag() { - return executableTag.c_str(); + return GIT_VERSION ":" CI_JOB_ID; } int diff --git a/src/skel/dc/dc.h b/src/liberty/skel/dc/dc.h similarity index 100% rename from src/skel/dc/dc.h rename to src/liberty/skel/dc/dc.h diff --git a/src/skel/events.cpp b/src/liberty/skel/events.cpp similarity index 100% rename from src/skel/events.cpp rename to src/liberty/skel/events.cpp diff --git a/src/skel/events.h b/src/liberty/skel/events.h similarity index 100% rename from src/skel/events.h rename to src/liberty/skel/events.h diff --git a/src/skel/glfw/glfw.cpp b/src/liberty/skel/glfw/glfw.cpp similarity index 100% rename from src/skel/glfw/glfw.cpp rename to src/liberty/skel/glfw/glfw.cpp diff --git a/src/skel/platform.h b/src/liberty/skel/platform.h similarity index 100% rename from src/skel/platform.h rename to src/liberty/skel/platform.h diff --git a/src/skel/skeleton.cpp b/src/liberty/skel/skeleton.cpp similarity index 100% rename from src/skel/skeleton.cpp rename to src/liberty/skel/skeleton.cpp diff --git a/src/skel/skeleton.h b/src/liberty/skel/skeleton.h similarity index 100% rename from src/skel/skeleton.h rename to src/liberty/skel/skeleton.h diff --git a/src/skel/win/gta3.ico b/src/liberty/skel/win/gta3.ico similarity index 100% rename from src/skel/win/gta3.ico rename to src/liberty/skel/win/gta3.ico diff --git a/src/skel/win/resource.h b/src/liberty/skel/win/resource.h similarity index 100% rename from src/skel/win/resource.h rename to src/liberty/skel/win/resource.h diff --git a/src/skel/win/win.cpp b/src/liberty/skel/win/win.cpp similarity index 100% rename from src/skel/win/win.cpp rename to src/liberty/skel/win/win.cpp diff --git a/src/skel/win/win.h b/src/liberty/skel/win/win.h similarity index 100% rename from src/skel/win/win.h rename to src/liberty/skel/win/win.h diff --git a/src/skel/win/win.rc b/src/liberty/skel/win/win.rc similarity index 100% rename from src/skel/win/win.rc rename to src/liberty/skel/win/win.rc diff --git a/src/text/Messages.cpp b/src/liberty/text/Messages.cpp similarity index 100% rename from src/text/Messages.cpp rename to src/liberty/text/Messages.cpp diff --git a/src/text/Messages.h b/src/liberty/text/Messages.h similarity index 100% rename from src/text/Messages.h rename to src/liberty/text/Messages.h diff --git a/src/text/Pager.cpp b/src/liberty/text/Pager.cpp similarity index 100% rename from src/text/Pager.cpp rename to src/liberty/text/Pager.cpp diff --git a/src/text/Pager.h b/src/liberty/text/Pager.h similarity index 100% rename from src/text/Pager.h rename to src/liberty/text/Pager.h diff --git a/src/text/Text.cpp b/src/liberty/text/Text.cpp similarity index 100% rename from src/text/Text.cpp rename to src/liberty/text/Text.cpp diff --git a/src/text/Text.h b/src/liberty/text/Text.h similarity index 100% rename from src/text/Text.h rename to src/liberty/text/Text.h diff --git a/src/vehicles/Automobile.cpp b/src/liberty/vehicles/Automobile.cpp similarity index 100% rename from src/vehicles/Automobile.cpp rename to src/liberty/vehicles/Automobile.cpp diff --git a/src/vehicles/Automobile.h b/src/liberty/vehicles/Automobile.h similarity index 100% rename from src/vehicles/Automobile.h rename to src/liberty/vehicles/Automobile.h diff --git a/src/vehicles/Bike.h b/src/liberty/vehicles/Bike.h similarity index 100% rename from src/vehicles/Bike.h rename to src/liberty/vehicles/Bike.h diff --git a/src/vehicles/Boat.cpp b/src/liberty/vehicles/Boat.cpp similarity index 100% rename from src/vehicles/Boat.cpp rename to src/liberty/vehicles/Boat.cpp diff --git a/src/vehicles/Boat.h b/src/liberty/vehicles/Boat.h similarity index 100% rename from src/vehicles/Boat.h rename to src/liberty/vehicles/Boat.h diff --git a/src/vehicles/CarGen.cpp b/src/liberty/vehicles/CarGen.cpp similarity index 100% rename from src/vehicles/CarGen.cpp rename to src/liberty/vehicles/CarGen.cpp diff --git a/src/vehicles/CarGen.h b/src/liberty/vehicles/CarGen.h similarity index 100% rename from src/vehicles/CarGen.h rename to src/liberty/vehicles/CarGen.h diff --git a/src/vehicles/Cranes.cpp b/src/liberty/vehicles/Cranes.cpp similarity index 100% rename from src/vehicles/Cranes.cpp rename to src/liberty/vehicles/Cranes.cpp diff --git a/src/vehicles/Cranes.h b/src/liberty/vehicles/Cranes.h similarity index 100% rename from src/vehicles/Cranes.h rename to src/liberty/vehicles/Cranes.h diff --git a/src/vehicles/DamageManager.cpp b/src/liberty/vehicles/DamageManager.cpp similarity index 100% rename from src/vehicles/DamageManager.cpp rename to src/liberty/vehicles/DamageManager.cpp diff --git a/src/vehicles/DamageManager.h b/src/liberty/vehicles/DamageManager.h similarity index 100% rename from src/vehicles/DamageManager.h rename to src/liberty/vehicles/DamageManager.h diff --git a/src/vehicles/Door.cpp b/src/liberty/vehicles/Door.cpp similarity index 100% rename from src/vehicles/Door.cpp rename to src/liberty/vehicles/Door.cpp diff --git a/src/vehicles/Door.h b/src/liberty/vehicles/Door.h similarity index 100% rename from src/vehicles/Door.h rename to src/liberty/vehicles/Door.h diff --git a/src/vehicles/Floater.cpp b/src/liberty/vehicles/Floater.cpp similarity index 100% rename from src/vehicles/Floater.cpp rename to src/liberty/vehicles/Floater.cpp diff --git a/src/vehicles/Floater.h b/src/liberty/vehicles/Floater.h similarity index 100% rename from src/vehicles/Floater.h rename to src/liberty/vehicles/Floater.h diff --git a/src/vehicles/HandlingMgr.cpp b/src/liberty/vehicles/HandlingMgr.cpp similarity index 100% rename from src/vehicles/HandlingMgr.cpp rename to src/liberty/vehicles/HandlingMgr.cpp diff --git a/src/vehicles/HandlingMgr.h b/src/liberty/vehicles/HandlingMgr.h similarity index 100% rename from src/vehicles/HandlingMgr.h rename to src/liberty/vehicles/HandlingMgr.h diff --git a/src/vehicles/Heli.cpp b/src/liberty/vehicles/Heli.cpp similarity index 100% rename from src/vehicles/Heli.cpp rename to src/liberty/vehicles/Heli.cpp diff --git a/src/vehicles/Heli.h b/src/liberty/vehicles/Heli.h similarity index 100% rename from src/vehicles/Heli.h rename to src/liberty/vehicles/Heli.h diff --git a/src/vehicles/Plane.cpp b/src/liberty/vehicles/Plane.cpp similarity index 100% rename from src/vehicles/Plane.cpp rename to src/liberty/vehicles/Plane.cpp diff --git a/src/vehicles/Plane.h b/src/liberty/vehicles/Plane.h similarity index 100% rename from src/vehicles/Plane.h rename to src/liberty/vehicles/Plane.h diff --git a/src/vehicles/Train.cpp b/src/liberty/vehicles/Train.cpp similarity index 100% rename from src/vehicles/Train.cpp rename to src/liberty/vehicles/Train.cpp diff --git a/src/vehicles/Train.h b/src/liberty/vehicles/Train.h similarity index 100% rename from src/vehicles/Train.h rename to src/liberty/vehicles/Train.h diff --git a/src/vehicles/Transmission.cpp b/src/liberty/vehicles/Transmission.cpp similarity index 100% rename from src/vehicles/Transmission.cpp rename to src/liberty/vehicles/Transmission.cpp diff --git a/src/vehicles/Transmission.h b/src/liberty/vehicles/Transmission.h similarity index 100% rename from src/vehicles/Transmission.h rename to src/liberty/vehicles/Transmission.h diff --git a/src/vehicles/Vehicle.cpp b/src/liberty/vehicles/Vehicle.cpp similarity index 100% rename from src/vehicles/Vehicle.cpp rename to src/liberty/vehicles/Vehicle.cpp diff --git a/src/vehicles/Vehicle.h b/src/liberty/vehicles/Vehicle.h similarity index 100% rename from src/vehicles/Vehicle.h rename to src/liberty/vehicles/Vehicle.h diff --git a/src/weapons/BulletInfo.cpp b/src/liberty/weapons/BulletInfo.cpp similarity index 89% rename from src/weapons/BulletInfo.cpp rename to src/liberty/weapons/BulletInfo.cpp index bfe27e18..7b187d7c 100644 --- a/src/weapons/BulletInfo.cpp +++ b/src/liberty/weapons/BulletInfo.cpp @@ -126,6 +126,28 @@ void CBulletInfo::Update(void) } pPed->InflictDamage(pBullet->m_pSource, pBullet->m_eWeaponType, pBullet->m_nDamage, (ePedPieceTypes)point.pieceB, pPed->GetLocalDirection(pPed->GetPosition() - point.point)); CEventList::RegisterEvent(pPed->m_nPedType == PEDTYPE_COP ? EVENT_SHOOT_COP : EVENT_SHOOT_PED, EVENT_ENTITY_PED, pPed, (CPed*)pBullet->m_pSource, 1000); + + if (CGame::nastyGame) { + CVector vecParticleDirection = (point.point - pPed->GetPosition()) * 0.01f; + vecParticleDirection.z = 0.01f; + if (pPed->GetIsOnScreen()) { + for (int j = 0; j < NUM_PED_BLOOD_PARTICLES; j++) + CParticle::AddParticle(PARTICLE_BLOOD_SMALL, point.point + BLOOD_PARTICLE_OFFSET, vecParticleDirection); + } + if (pPed->GetPedState() == PED_DEAD) { + CAnimBlendAssociation* pAnim; + if (RpAnimBlendClumpGetFirstAssociation(pPed->GetClump(), ASSOC_FRONTAL)) + pAnim = CAnimManager::BlendAnimation(pPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR_FRONT, 8.0f); + else + pAnim = CAnimManager::BlendAnimation(pPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR, 8.0f); + if (pAnim) { + pAnim->SetCurrentTime(0.0f); + pAnim->flags |= ASSOC_RUNNING; + pAnim->flags &= ~ASSOC_FADEOUTWHENDONE; + } + } + } + pBullet->m_bInUse = false; #ifdef SQUEEZE_PERFORMANCE bulletInfoInUse--; @@ -136,31 +158,6 @@ void CBulletInfo::Update(void) bAddSound = false; } } - if (CGame::nastyGame) { - CVector vecParticleDirection = (point.point - pPed->GetPosition()) * 0.01f; - vecParticleDirection.z = 0.01f; - if (pPed->GetIsOnScreen()) { - for (int j = 0; j < NUM_PED_BLOOD_PARTICLES; j++) - CParticle::AddParticle(PARTICLE_BLOOD_SMALL, point.point + BLOOD_PARTICLE_OFFSET, vecParticleDirection); - } - if (pPed->GetPedState() == PED_DEAD) { - CAnimBlendAssociation* pAnim; - if (RpAnimBlendClumpGetFirstAssociation(pPed->GetClump(), ASSOC_FRONTAL)) - pAnim = CAnimManager::BlendAnimation(pPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR_FRONT, 8.0f); - else - pAnim = CAnimManager::BlendAnimation(pPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR, 8.0f); - if (pAnim) { - pAnim->SetCurrentTime(0.0f); - pAnim->flags |= ASSOC_RUNNING; - pAnim->flags &= ~ASSOC_FADEOUTWHENDONE; - } - } - pBullet->m_bInUse = false; -#ifdef SQUEEZE_PERFORMANCE - bulletInfoInUse--; -#endif - vecNewPos = point.point; - } } else if (pHitEntity->IsVehicle()) { CVehicle* pVehicle = (CVehicle*)pHitEntity; diff --git a/src/weapons/BulletInfo.h b/src/liberty/weapons/BulletInfo.h similarity index 100% rename from src/weapons/BulletInfo.h rename to src/liberty/weapons/BulletInfo.h diff --git a/src/weapons/Explosion.cpp b/src/liberty/weapons/Explosion.cpp similarity index 100% rename from src/weapons/Explosion.cpp rename to src/liberty/weapons/Explosion.cpp diff --git a/src/weapons/Explosion.h b/src/liberty/weapons/Explosion.h similarity index 100% rename from src/weapons/Explosion.h rename to src/liberty/weapons/Explosion.h diff --git a/src/weapons/ProjectileInfo.cpp b/src/liberty/weapons/ProjectileInfo.cpp similarity index 100% rename from src/weapons/ProjectileInfo.cpp rename to src/liberty/weapons/ProjectileInfo.cpp diff --git a/src/weapons/ProjectileInfo.h b/src/liberty/weapons/ProjectileInfo.h similarity index 100% rename from src/weapons/ProjectileInfo.h rename to src/liberty/weapons/ProjectileInfo.h diff --git a/src/weapons/ShotInfo.cpp b/src/liberty/weapons/ShotInfo.cpp similarity index 100% rename from src/weapons/ShotInfo.cpp rename to src/liberty/weapons/ShotInfo.cpp diff --git a/src/weapons/ShotInfo.h b/src/liberty/weapons/ShotInfo.h similarity index 100% rename from src/weapons/ShotInfo.h rename to src/liberty/weapons/ShotInfo.h diff --git a/src/weapons/Weapon.cpp b/src/liberty/weapons/Weapon.cpp similarity index 100% rename from src/weapons/Weapon.cpp rename to src/liberty/weapons/Weapon.cpp diff --git a/src/weapons/Weapon.h b/src/liberty/weapons/Weapon.h similarity index 100% rename from src/weapons/Weapon.h rename to src/liberty/weapons/Weapon.h diff --git a/src/weapons/WeaponEffects.cpp b/src/liberty/weapons/WeaponEffects.cpp similarity index 100% rename from src/weapons/WeaponEffects.cpp rename to src/liberty/weapons/WeaponEffects.cpp diff --git a/src/weapons/WeaponEffects.h b/src/liberty/weapons/WeaponEffects.h similarity index 100% rename from src/weapons/WeaponEffects.h rename to src/liberty/weapons/WeaponEffects.h diff --git a/src/weapons/WeaponInfo.cpp b/src/liberty/weapons/WeaponInfo.cpp similarity index 100% rename from src/weapons/WeaponInfo.cpp rename to src/liberty/weapons/WeaponInfo.cpp diff --git a/src/weapons/WeaponInfo.h b/src/liberty/weapons/WeaponInfo.h similarity index 100% rename from src/weapons/WeaponInfo.h rename to src/liberty/weapons/WeaponInfo.h diff --git a/src/weapons/WeaponType.h b/src/liberty/weapons/WeaponType.h similarity index 100% rename from src/weapons/WeaponType.h rename to src/liberty/weapons/WeaponType.h diff --git a/src/miami/CMakeLists.txt b/src/miami/CMakeLists.txt new file mode 100644 index 00000000..b605d45b --- /dev/null +++ b/src/miami/CMakeLists.txt @@ -0,0 +1,170 @@ +find_package(Threads REQUIRED) +set(THREADS_PREFER_PTHREAD_FLAG ON) + +file(GLOB_RECURSE ${PROJECT}_SOURCES "*.cpp" "*.h" "*.rc") + +function(header_directories RETURN_LIST) + file(GLOB_RECURSE ALL_SRCS *.h *.cpp *.c) + set(RELDIRS) + foreach(SRC ${ALL_SRCS}) + file(RELATIVE_PATH RELSRC "${CMAKE_CURRENT_SOURCE_DIR}" "${SRC}") + get_filename_component(RELDIR "${RELSRC}" DIRECTORY) + list(APPEND RELDIRS ${RELDIR}) + endforeach() + list(REMOVE_DUPLICATES RELDIRS) + set(${RETURN_LIST} ${RELDIRS} PARENT_SCOPE) +endfunction() + +header_directories(${PROJECT}_INCLUDES) + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/extras/GitSHA1.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/extras/GitSHA1.cpp" @ONLY) +list(APPEND ${PROJECT}_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/extras/GitSHA1.cpp") + +add_executable(${EXECUTABLE} WIN32 + ${${PROJECT}_SOURCES} +) + +target_link_libraries(${EXECUTABLE} PRIVATE + librw::librw + Threads::Threads +) + +target_include_directories(${EXECUTABLE} + PRIVATE + $ + $ +) + +target_compile_definitions(${EXECUTABLE} + PRIVATE + $,DEBUG,NDEBUG> + LIBRW + CMAKE_NO_AUTOLINK +) + +if(LIBRW_PLATFORM_D3D9) + target_compile_definitions(${EXECUTABLE} + PUBLIC + USE_D3D9 + ) +endif() + +target_compile_definitions(${EXECUTABLE} PRIVATE CMAKE_BUILD) +target_compile_definitions(${EXECUTABLE} PRIVATE USE_OUR_VERSIONING) + +if(${PROJECT}_AUDIO STREQUAL "OAL") + find_package(OpenAL REQUIRED) + if(TARGET OpenAL::OpenAL) + target_link_libraries(${EXECUTABLE} PRIVATE OpenAL::OpenAL) + else() + target_include_directories(${EXECUTABLE} PRIVATE ${OPENAL_INCLUDE_DIR}) + target_link_libraries(${EXECUTABLE} PRIVATE ${OPENAL_LIBRARY}) + target_compile_definitions(${EXECUTABLE} PRIVATE ${OPENAL_DEFINITIONS}) + endif() + target_compile_definitions(${EXECUTABLE} PRIVATE AUDIO_OAL) +elseif(${PROJECT}_AUDIO STREQUAL "MSS") + find_package(MilesSDK REQUIRED) + target_compile_definitions(${EXECUTABLE} PRIVATE AUDIO_MSS) + target_link_libraries(${EXECUTABLE} PRIVATE MilesSDK::MilesSDK) +endif() + +find_package(mpg123 REQUIRED) +target_link_libraries(${EXECUTABLE} PRIVATE + MPG123::libmpg123 +) +if(${PROJECT}_WITH_OPUS) + find_package(opusfile REQUIRED) + target_link_libraries(${EXECUTABLE} PRIVATE + opusfile::opusfile + ) + target_compile_definitions(${EXECUTABLE} PRIVATE AUDIO_OPUS) +endif() +if(${PROJECT}_WITH_LIBSNDFILE) + find_package(SndFile REQUIRED) + target_link_libraries(${EXECUTABLE} PRIVATE + SndFile::SndFile + ) + target_compile_definitions(${EXECUTABLE} PRIVATE AUDIO_OAL_USE_SNDFILE) +endif() + +target_compile_definitions(${EXECUTABLE} PRIVATE ) + +option(${PROJECT}_WITH_SANITIZERS "Use UB sanitizers (better crash log)" OFF) +option(${PROJECT}_WITH_ASAN "Use Address sanitizer (better crash log)" OFF) + +if(${PROJECT}_WITH_SANITIZERS) + target_compile_options(${EXECUTABLE} PUBLIC + -fsanitize=undefined,float-divide-by-zero,integer,implicit-conversion,implicit-integer-truncation,implicit-integer-arithmetic-value-change,local-bounds,nullability + -g3 -fno-omit-frame-pointer) + target_link_options(${EXECUTABLE} PUBLIC -fsanitize=undefined,float-divide-by-zero,integer,implicit-conversion,implicit-integer-truncation,implicit-integer-arithmetic-value-change,local-bounds,nullability) +endif() + +if(${PROJECT}_WITH_ASAN) + target_compile_options(${EXECUTABLE} PUBLIC -fsanitize=address -g3 -fno-omit-frame-pointer) + target_link_options(${EXECUTABLE} PUBLIC -fsanitize=address) +endif() + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + target_compile_options(${EXECUTABLE} + PRIVATE + "-Wall" + ) + if (NOT LIBRW_PLATFORM_PS2) + target_compile_options(${EXECUTABLE} + PRIVATE + -Wextra + -Wdouble-promotion + -Wpedantic + ) + endif() +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + target_compile_options(${EXECUTABLE} + PUBLIC + /Zc:sizedDealloc- + ) +endif() + +if(NINTENDO_SWITCH) + set(${PROJECT}_C_CXX_EXTENSIONS ON) +else() + set(${PROJECT}_C_CXX_EXTENSIONS OFF) +endif() + +if(LIBRW_PLATFORM_GL3 AND LIBRW_GL3_GFXLIB STREQUAL "GLFW") + include(CheckSymbolExists) + + set(CMAKE_REQUIRED_LIBRARIES glfw) + set(CMAKE_REQUIRED_DEFINITIONS -DGLFW_EXPOSE_NATIVE_X11) + check_symbol_exists(glfwGetX11Display "GLFW/glfw3.h;GLFW/glfw3native.h" GLFW_HAS_X11) + unset(CMAKE_REQUIRED_DEFINITIONS) + unset(CMAKE_REQUIRED_LIBRARIES) + + if (GLFW_HAS_X11) + find_package(X11 REQUIRED) + target_link_libraries(${EXECUTABLE} PRIVATE X11::X11) + target_compile_definitions(${EXECUTABLE} PRIVATE GET_KEYBOARD_INPUT_FROM_X11) + endif (GLFW_HAS_X11) +endif() + +set_target_properties(${EXECUTABLE} + PROPERTIES + C_STANDARD 11 + C_EXTENSIONS ${${PROJECT}_C_CXX_EXTENSIONS} + C_STANDARD_REQUIRED ON + CXX_STANDARD 11 + CXX_EXTENSIONS ${${PROJECT}_C_CXX_EXTENSIONS} + CXX_STANDARD_REQUIRED ON +) + +if(${PROJECT}_INSTALL) + install( + TARGETS ${EXECUTABLE} + EXPORT ${EXECUTABLE}-targets + RUNTIME DESTINATION "." + ) + if(MSVC) + install(FILES $ DESTINATION "." OPTIONAL) + endif() +endif() + +reVC_platform_target(${EXECUTABLE} INSTALL) diff --git a/src/miami/animation/AnimBlendAssocGroup.cpp b/src/miami/animation/AnimBlendAssocGroup.cpp new file mode 100644 index 00000000..941258f8 --- /dev/null +++ b/src/miami/animation/AnimBlendAssocGroup.cpp @@ -0,0 +1,187 @@ +#include "common.h" + +#if defined _WIN32 && !defined __MINGW32__ +#if defined __MWERKS__ +#include +#else +#include "ctype.h" +#endif +#else +#include +#endif + +#include "General.h" +#include "RwHelper.h" +#include "ModelIndices.h" +#include "ModelInfo.h" +#include "AnimManager.h" +#include "RpAnimBlend.h" +#include "AnimBlendAssociation.h" +#include "AnimBlendAssocGroup.h" + +CAnimBlendAssocGroup::CAnimBlendAssocGroup(void) +{ + animBlock = nil; + assocList = nil; + numAssociations = 0; + firstAnimId = 0; + groupId = -1; +} + +CAnimBlendAssocGroup::~CAnimBlendAssocGroup(void) +{ + DestroyAssociations(); +} + +void +CAnimBlendAssocGroup::DestroyAssociations(void) +{ + if(assocList){ + delete[] assocList; + assocList = nil; + numAssociations = 0; + } +} + +CAnimBlendAssociation* +CAnimBlendAssocGroup::GetAnimation(uint32 id) +{ + return &assocList[id - firstAnimId]; +} + +CAnimBlendAssociation* +CAnimBlendAssocGroup::GetAnimation(const char *name) +{ + int i; + for(i = 0; i < numAssociations; i++) + if(!CGeneral::faststricmp(assocList[i].hierarchy->name, name)) + return &assocList[i]; + debug("\n\nCan't find the fucking animation %s\n\n\n", name); + return nil; +} + + +CAnimBlendAssociation* +CAnimBlendAssocGroup::CopyAnimation(uint32 id) +{ + CAnimBlendAssociation *anim = GetAnimation(id); + if(anim == nil) + return nil; + + return new CAnimBlendAssociation(*anim); +} + +CAnimBlendAssociation* +CAnimBlendAssocGroup::CopyAnimation(const char *name) +{ + CAnimBlendAssociation *anim = GetAnimation(name); + if(anim == nil) + return nil; + + return new CAnimBlendAssociation(*anim); +} + +bool +strcmpIgnoringDigits(const char *s1, const char *s2) +{ + char c1, c2; + + for(;;){ + c1 = *s1; + c2 = *s2; + if(c1) s1++; + if(c2) s2++; + if(c1 == '\0' && c2 == '\0') return true; +#ifndef ASCII_STRCMP + if(iswdigit(c1) && iswdigit(c2)) +#else + if(__ascii_iswdigit(c1) && __ascii_iswdigit(c2)) +#endif + continue; +#ifndef ASCII_STRCMP + c1 = toupper(c1); + c2 = toupper(c2); +#else + c1 = __ascii_toupper(c1); + c2 = __ascii_toupper(c2); +#endif + + if(c1 && c2 && c1 != c2) + return false; + } +} + +CBaseModelInfo* +GetModelFromName(const char *name) +{ + int i; + CBaseModelInfo *mi; + char playername[32]; + + if(strncasecmp(name, "CSplay", 6) == 0 && + strncasecmp(CModelInfo::GetModelInfo(MI_PLAYER)->GetModelName(), "ig", 2) == 0){ + strcpy(playername, CModelInfo::GetModelInfo(MI_PLAYER)->GetModelName()); + playername[0] = 'C'; + playername[1] = 'S'; + name = playername; + } + + for(i = 0; i < MODELINFOSIZE; i++){ + mi = CModelInfo::GetModelInfo(i); + if(mi && mi->GetRwObject() && RwObjectGetType(mi->GetRwObject()) == rpCLUMP && + strcmpIgnoringDigits(mi->GetModelName(), name)) + return mi; + } + return nil; +} + +void +CAnimBlendAssocGroup::CreateAssociations(const char *name) +{ + int i; + CAnimBlock *animBlock; + + DestroyAssociations(); + + animBlock = CAnimManager::GetAnimationBlock(name); + assocList = new CAnimBlendAssociation[animBlock->numAnims]; + numAssociations = 0; + + for(i = 0; i < animBlock->numAnims; i++){ + CAnimBlendHierarchy *anim = CAnimManager::GetAnimation(animBlock->firstIndex + i); + CBaseModelInfo *model = GetModelFromName(anim->name); + if(model){ + debug("Associated anim %s with model %s\n", anim->name, model->GetModelName()); + RpClump *clump = (RpClump*)model->CreateInstance(); + RpAnimBlendClumpInit(clump); + assocList[i].Init(clump, anim); + if(IsClumpSkinned(clump)) + RpClumpForAllAtomics(clump, AtomicRemoveAnimFromSkinCB, nil); + RpClumpDestroy(clump); + assocList[i].animId = firstAnimId + i; + assocList[i].groupId = groupId; + }else + debug("\n\nCANNOT FIND MODELINFO WITH NAME %s\n\n\n", anim->name); + } + numAssociations = animBlock->numAnims; +} + +// Create associations from hierarchies for a given clump +void +CAnimBlendAssocGroup::CreateAssociations(const char *blockName, RpClump *clump, const char **animNames, int numAssocs) +{ + int i; + + DestroyAssociations(); + + animBlock = CAnimManager::GetAnimationBlock(blockName); + assocList = new CAnimBlendAssociation[numAssocs]; + + numAssociations = 0; + for(i = 0; i < numAssocs; i++){ + assocList[i].Init(clump, CAnimManager::GetAnimation(animNames[i], animBlock)); + assocList[i].animId = firstAnimId + i; + assocList[i].groupId = groupId; + } + numAssociations = numAssocs; +} diff --git a/src/miami/animation/AnimBlendAssocGroup.h b/src/miami/animation/AnimBlendAssocGroup.h new file mode 100644 index 00000000..86f0ca18 --- /dev/null +++ b/src/miami/animation/AnimBlendAssocGroup.h @@ -0,0 +1,24 @@ +#pragma once + +class CAnimBlendAssociation; +struct CAnimBlock; + +class CAnimBlendAssocGroup +{ +public: + CAnimBlock *animBlock; + CAnimBlendAssociation *assocList; + int32 numAssociations; + int32 firstAnimId; + int32 groupId; // id of self in ms_aAnimAssocGroups + + CAnimBlendAssocGroup(void); + ~CAnimBlendAssocGroup(void); + void DestroyAssociations(void); + CAnimBlendAssociation *GetAnimation(uint32 id); + CAnimBlendAssociation *GetAnimation(const char *name); + CAnimBlendAssociation *CopyAnimation(uint32 id); + CAnimBlendAssociation *CopyAnimation(const char *name); + void CreateAssociations(const char *name); + void CreateAssociations(const char *blockName, RpClump *clump, const char **animNames, int numAssocs); +}; diff --git a/src/miami/animation/AnimBlendAssociation.cpp b/src/miami/animation/AnimBlendAssociation.cpp new file mode 100644 index 00000000..99e44311 --- /dev/null +++ b/src/miami/animation/AnimBlendAssociation.cpp @@ -0,0 +1,233 @@ +#include "common.h" + +#include "AnimBlendHierarchy.h" +#include "AnimBlendClumpData.h" +#include "RpAnimBlend.h" +#include "AnimManager.h" +#include "AnimBlendAssociation.h" +#include "MemoryMgr.h" + +CAnimBlendAssociation::CAnimBlendAssociation(void) +{ + groupId = -1; + nodes = nil; + blendAmount = 1.0f; + blendDelta = 0.0f; + currentTime = 0.0f; + speed = 1.0f; + timeStep = 0.0f; + animId = -1; + flags = 0; + callbackType = CB_NONE; + link.Init(); +} + +CAnimBlendAssociation::CAnimBlendAssociation(CAnimBlendAssociation &other) +{ + nodes = nil; + blendAmount = 1.0f; + blendDelta = 0.0f; + currentTime = 0.0f; + speed = 1.0f; + timeStep = 0.0f; + callbackType = CB_NONE; + link.Init(); + Init(other); +} + +CAnimBlendAssociation::~CAnimBlendAssociation(void) +{ + FreeAnimBlendNodeArray(); + link.Remove(); +} + +void +CAnimBlendAssociation::AllocateAnimBlendNodeArray(int n) +{ + int i; + + nodes = (CAnimBlendNode*)RwMallocAlign(n*sizeof(CAnimBlendNode), 64); + for(i = 0; i < n; i++) + nodes[i].Init(); +} + +void +CAnimBlendAssociation::FreeAnimBlendNodeArray(void) +{ + if(nodes) { + for(unsigned i = 0; i < numNodes; i++) + nodes[i].Destroy(); + RwFreeAlign(nodes); + } +} + +void +CAnimBlendAssociation::Init(RpClump *clump, CAnimBlendHierarchy *hier) +{ + int i; + AnimBlendFrameData *frame; + + CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); + numNodes = clumpData->numFrames; + AllocateAnimBlendNodeArray(numNodes); + for(i = 0; i < numNodes; i++) + nodes[i].association = this; + hierarchy = hier; + + // Init every node from a sequence and a Clump frame + // NB: This is where the order of nodes is defined + for(i = 0; i < hier->numSequences; i++){ + CAnimBlendSequence *seq = &hier->sequences[i]; + if(seq->boneTag == -1) + frame = RpAnimBlendClumpFindFrame(clump, seq->name); + else + frame = RpAnimBlendClumpFindBone(clump, seq->boneTag); + if(frame && seq->numFrames > 0) { + nodes[frame - clumpData->frames].sequence = seq; + } + } +} + +void +CAnimBlendAssociation::Init(CAnimBlendAssociation &assoc) +{ + int i; + + hierarchy = assoc.hierarchy; + numNodes = assoc.numNodes; + flags = assoc.flags; + animId = assoc.animId; + groupId = assoc.groupId; + AllocateAnimBlendNodeArray(numNodes); + for(i = 0; i < numNodes; i++){ + nodes[i] = assoc.nodes[i]; + nodes[i].association = this; + } +} + +void +CAnimBlendAssociation::SetBlend(float amount, float delta) +{ + blendAmount = amount; + blendDelta = delta; +} + +void +CAnimBlendAssociation::SetFinishCallback(void (*cb)(CAnimBlendAssociation*, void*), void *arg) +{ + callbackType = CB_FINISH; + callback = cb; + callbackArg = arg; +} + +void +CAnimBlendAssociation::SetDeleteCallback(void (*cb)(CAnimBlendAssociation*, void*), void *arg) +{ + callbackType = CB_DELETE; + callback = cb; + callbackArg = arg; +} + +#if defined(DC_TEXCONV) +void +CAnimBlendAssociation::SetCurrentTime(float time) { + assert("false" && "Must not reach here"); +} +#else +void +CAnimBlendAssociation::SetCurrentTime(float time) +{ + int i; + + for(currentTime = time; currentTime >= hierarchy->totalLength; currentTime -= hierarchy->totalLength) + if (!IsRepeating()) { + currentTime = hierarchy->totalLength; + break; + } + + { + for(i = 0; i < numNodes; i++) + if(nodes[i].sequence) + nodes[i].FindKeyFrame(currentTime); + } +} +#endif + +void +CAnimBlendAssociation::SyncAnimation(CAnimBlendAssociation *other) +{ + SetCurrentTime(other->currentTime/other->hierarchy->totalLength * hierarchy->totalLength); +} + +void +CAnimBlendAssociation::Start(float time) +{ + flags |= ASSOC_RUNNING; + SetCurrentTime(time); +} + +void +CAnimBlendAssociation::UpdateTimeStep(float timeDelta, float relSpeed) +{ + if(IsRunning()) + timeStep = (flags & ASSOC_MOVEMENT ? relSpeed*hierarchy->totalLength : speed) * timeDelta; +} + +bool +CAnimBlendAssociation::UpdateTime(float timeDelta, float relSpeed) +{ + if(!IsRunning()) + return true; + if(currentTime >= hierarchy->totalLength){ + flags &= ~ASSOC_RUNNING; + return true; + } + + currentTime += timeStep; + + if(currentTime >= hierarchy->totalLength){ + // Ran past end + + if(IsRepeating()) + currentTime -= hierarchy->totalLength; + else{ + currentTime = hierarchy->totalLength; + if(flags & ASSOC_FADEOUTWHENDONE){ + flags |= ASSOC_DELETEFADEDOUT; + blendDelta = -4.0f; + } + if(callbackType == CB_FINISH){ + callbackType = CB_NONE; + callback(this, callbackArg); + } + } + } + return true; +} + +// return whether we still exist after this function +bool +CAnimBlendAssociation::UpdateBlend(float timeDelta) +{ + blendAmount += blendDelta * timeDelta; + + if(blendAmount <= 0.0f && blendDelta < 0.0f){ + // We're faded out and are not fading in + blendAmount = 0.0f; + blendDelta = Max(0.0f, blendDelta); + if(flags & ASSOC_DELETEFADEDOUT){ + if(callbackType == CB_FINISH || callbackType == CB_DELETE) + callback(this, callbackArg); + delete this; + return false; + } + } + + if(blendAmount > 1.0f){ + // Maximally faded in, clamp values + blendAmount = 1.0f; + blendDelta = Min(0.0f, blendDelta); + } + + return true; +} diff --git a/src/miami/animation/AnimBlendAssociation.h b/src/miami/animation/AnimBlendAssociation.h new file mode 100644 index 00000000..dbfcb722 --- /dev/null +++ b/src/miami/animation/AnimBlendAssociation.h @@ -0,0 +1,93 @@ +#pragma once + +#include "AnimBlendList.h" +#include "AnimBlendNode.h" +#include "AnimBlendHierarchy.h" + +enum { + ASSOC_RUNNING = 1, + ASSOC_REPEAT = 2, + ASSOC_DELETEFADEDOUT = 4, + ASSOC_FADEOUTWHENDONE = 8, + ASSOC_PARTIAL = 0x10, + ASSOC_MOVEMENT = 0x20, // ??? + ASSOC_HAS_TRANSLATION = 0x40, + ASSOC_HAS_X_TRANSLATION = 0x80, // for 2d velocity extraction + ASSOC_WALK = 0x100, // for CPed::PlayFootSteps(void) + ASSOC_IDLE = 0x200, // only xpress scratch has it by default, but game adds it to player's idle animations later + ASSOC_NOWALK = 0x400, // see CPed::PlayFootSteps(void) + ASSOC_BLOCK = 0x800, // unused in assoc description, blocks other anims from being played + ASSOC_FRONTAL = 0x1000, // anims that we fall to front + ASSOC_DRIVING = 0x2000, // new in VC +}; + +// Anim hierarchy associated with a clump +// Holds the interpolated state of all nodes. +// Also used as template for other clumps. +class CAnimBlendAssociation +{ +public: + enum { + // callbackType + CB_NONE, + CB_FINISH, + CB_DELETE + }; + + CAnimBlendLink link; + + int16 numNodes; // taken from CAnimBlendClumpData::numFrames + int16 groupId; // ID of CAnimBlendAssocGroup this is in + // NB: Order of these depends on order of nodes in Clump this was built from + CAnimBlendNode *nodes; + CAnimBlendHierarchy *hierarchy; + float blendAmount; + float blendDelta; // how much blendAmount changes over time + float currentTime; + float speed; + float timeStep; + int16 animId; + int16 flags; + int32 callbackType; + void (*callback)(CAnimBlendAssociation*, void*); + void *callbackArg; + + bool IsRunning(void) { return !!(flags & ASSOC_RUNNING); } + bool IsRepeating(void) { return !!(flags & ASSOC_REPEAT); } + bool IsPartial(void) { return !!(flags & ASSOC_PARTIAL); } + bool IsMovement(void) { return !!(flags & ASSOC_MOVEMENT); } + bool HasTranslation(void) { return !!(flags & ASSOC_HAS_TRANSLATION); } + bool HasXTranslation(void) { return !!(flags & ASSOC_HAS_X_TRANSLATION); } + + float GetBlendAmount(float weight) { return IsPartial() ? blendAmount : blendAmount*weight; } + CAnimBlendNode *GetNode(int i) { return &nodes[i]; } + + CAnimBlendAssociation(void); + CAnimBlendAssociation(CAnimBlendAssociation &other); +#ifndef FIX_BUGS + virtual +#endif + ~CAnimBlendAssociation(void); + void AllocateAnimBlendNodeArray(int n); + void FreeAnimBlendNodeArray(void); + void Init(RpClump *clump, CAnimBlendHierarchy *hier); + void Init(CAnimBlendAssociation &assoc); + void SetBlend(float amount, float delta); + void SetFinishCallback(void (*callback)(CAnimBlendAssociation*, void*), void *arg); + void SetDeleteCallback(void (*callback)(CAnimBlendAssociation*, void*), void *arg); + void SetCurrentTime(float time); + void SyncAnimation(CAnimBlendAssociation *other); + void Start(float time); + void UpdateTimeStep(float timeDelta, float relSpeed); + bool UpdateTime(float timeDelta, float relSpeed); + bool UpdateBlend(float timeDelta); + + void SetRun(void) { flags |= ASSOC_RUNNING; } + + float GetTimeLeft() { return hierarchy->totalLength - currentTime; } + float GetProgress() { return currentTime / hierarchy->totalLength; } + + static CAnimBlendAssociation *FromLink(CAnimBlendLink *l) { + return (CAnimBlendAssociation*)((uint8*)l - offsetof(CAnimBlendAssociation, link)); + } +}; diff --git a/src/miami/animation/AnimBlendClumpData.cpp b/src/miami/animation/AnimBlendClumpData.cpp new file mode 100644 index 00000000..b333a449 --- /dev/null +++ b/src/miami/animation/AnimBlendClumpData.cpp @@ -0,0 +1,36 @@ +#include "common.h" + +#include "AnimBlendClumpData.h" +#include "MemoryMgr.h" + +CAnimBlendClumpData::CAnimBlendClumpData(void) +{ + numFrames = 0; + velocity2d = nil; + frames = nil; + link.Init(); +} + +CAnimBlendClumpData::~CAnimBlendClumpData(void) +{ + link.Remove(); + if(frames) + RwFreeAlign(frames); +} + +void +CAnimBlendClumpData::SetNumberOfFrames(int n) +{ + if(frames) + RwFreeAlign(frames); + numFrames = n; + frames = (AnimBlendFrameData*)RwMallocAlign(numFrames * sizeof(AnimBlendFrameData), 64); +} + +void +CAnimBlendClumpData::ForAllFrames(void (*cb)(AnimBlendFrameData*, void*), void *arg) +{ + int i; + for(i = 0; i < numFrames; i++) + cb(&frames[i], arg); +} diff --git a/src/miami/animation/AnimBlendClumpData.h b/src/miami/animation/AnimBlendClumpData.h new file mode 100644 index 00000000..60bb783f --- /dev/null +++ b/src/miami/animation/AnimBlendClumpData.h @@ -0,0 +1,44 @@ +#pragma once + +#include "AnimBlendList.h" + + +struct AnimBlendFrameData +{ + enum { + IGNORE_ROTATION = 2, + IGNORE_TRANSLATION = 4, + VELOCITY_EXTRACTION = 8, + VELOCITY_EXTRACTION_3D = 0x10, + UPDATE_KEYFRAMES = 0x20 + }; + + uint8 flag; + RwV3d resetPos; + union { + RwFrame *frame; + RpHAnimStdInterpFrame *hanimFrame; + }; + int32 nodeID; +}; +VALIDATE_SIZE(AnimBlendFrameData, 0x18); + + +class CAnimBlendClumpData +{ +public: + CAnimBlendLink link; + int32 numFrames; + union { + CVector2D *velocity2d; + CVector *velocity3d; + }; + // order of frames is determined by RW hierarchy + AnimBlendFrameData *frames; + + CAnimBlendClumpData(void); + ~CAnimBlendClumpData(void); + void SetNumberOfFrames(int n); + void SetNumberOfBones(int n) { SetNumberOfFrames(n); } + void ForAllFrames(void (*cb)(AnimBlendFrameData*, void*), void *arg); +}; diff --git a/src/animation/AnimBlendHierarchy.cpp b/src/miami/animation/AnimBlendHierarchy.cpp similarity index 75% rename from src/animation/AnimBlendHierarchy.cpp rename to src/miami/animation/AnimBlendHierarchy.cpp index e87de493..ee7413fc 100644 --- a/src/animation/AnimBlendHierarchy.cpp +++ b/src/miami/animation/AnimBlendHierarchy.cpp @@ -2,6 +2,7 @@ #include "AnimBlendSequence.h" #include "AnimBlendHierarchy.h" +#include "AnimManager.h" CAnimBlendHierarchy::CAnimBlendHierarchy(void) { @@ -14,6 +15,7 @@ void CAnimBlendHierarchy::Shutdown(void) { RemoveAnimSequences(); + totalLength = 0.0f; } void @@ -29,29 +31,24 @@ CAnimBlendHierarchy::CalcTotalTime(void) totalLength = 0.0f; for(i = 0; i < numSequences; i++){ - float seqTime = 0.0f; - for(j = 0; j < sequences[i].numFrames; j++) - seqTime += sequences[i].GetDeltaTime(j); +#ifdef FIX_BUGS + if(sequences[i].numFrames == 0) + continue; +#endif + float seqTime = sequences[i].GetEndTime(); totalLength = Max(totalLength, seqTime); } } -void -CAnimBlendHierarchy::RemoveQuaternionFlips(void) -{ - int i; - - for(i = 0; i < numSequences; i++) - sequences[i].RemoveQuaternionFlips(); -} - void CAnimBlendHierarchy::RemoveAnimSequences(void) { delete[] sequences; + sequences = nil; numSequences = 0; } + #ifdef USE_CUSTOM_ALLOCATOR void CAnimBlendHierarchy::MoveMemory(bool onlyone) diff --git a/src/miami/animation/AnimBlendHierarchy.h b/src/miami/animation/AnimBlendHierarchy.h new file mode 100644 index 00000000..d363122e --- /dev/null +++ b/src/miami/animation/AnimBlendHierarchy.h @@ -0,0 +1,30 @@ +#pragma once + +#include "templates.h" + +#ifdef MoveMemory +#undef MoveMemory // windows shit +#endif + +class CAnimBlendSequence; + +// A collection of sequences +class CAnimBlendHierarchy +{ +public: + char name[24]; + CAnimBlendSequence *sequences; + int16 numSequences; + float totalLength; + + CAnimBlendHierarchy(void); + void Shutdown(void); + void SetName(char *name); + void CalcTotalTime(void); + void RemoveAnimSequences(void); + void Uncompress(void); + void RemoveUncompressedData(void); + void MoveMemory(bool onlyone = false); +}; + +VALIDATE_SIZE(CAnimBlendHierarchy, 0x28); \ No newline at end of file diff --git a/src/miami/animation/AnimBlendList.h b/src/miami/animation/AnimBlendList.h new file mode 100644 index 00000000..018b5988 --- /dev/null +++ b/src/miami/animation/AnimBlendList.h @@ -0,0 +1,28 @@ +#pragma once + +// name made up +class CAnimBlendLink +{ +public: + CAnimBlendLink *next; + CAnimBlendLink *prev; + + void Init(void){ + next = nil; + prev = nil; + } + void Prepend(CAnimBlendLink *link){ + if(next) + next->prev = link; + link->next = next; + link->prev = this; + next = link; + } + void Remove(void){ + if(prev) + prev->next = next; + if(next) + next->prev = prev; + Init(); + } +}; diff --git a/src/miami/animation/AnimBlendNode.cpp b/src/miami/animation/AnimBlendNode.cpp new file mode 100644 index 00000000..27873e5a --- /dev/null +++ b/src/miami/animation/AnimBlendNode.cpp @@ -0,0 +1,190 @@ +#include "common.h" + +#include "AnimBlendAssociation.h" +#include "AnimBlendNode.h" + +void +CAnimBlendNode::Init(void) +{ + frameA = -1; + remainingTime = 0.0f; + sequence = nil; + association = nil; + player = nil; +} + +void CAnimBlendNode::Destroy(void) { + if (player) { + delete player; + player = nil; + } +} + +bool +CAnimBlendNode::Update(CVector &trans, CQuaternion &rot, float weight) +{ + assert (player && player->keyFrames == sequence->keyFrames); + + bool looped = false; + + trans = CVector(0.0f, 0.0f, 0.0f); + rot = CQuaternion(0.0f, 0.0f, 0.0f, 0.0f); + + if(association->IsRunning()){ + remainingTime -= association->timeStep; + if(remainingTime <= 0.0f) + looped = NextKeyFrame(); + } + + float blend = association->GetBlendAmount(weight); + if(blend > 0.0f){ + float kfAdt = player->GetNextTimeDelta(); + float t = kfAdt == 0.0f ? 0.0f : (kfAdt - remainingTime)/kfAdt; + if(player->type & CAnimBlendSequence::KF_TRANS){ + auto kfdAt = player->GetNextTranslationDelta(); + auto kfBt = player->GetPrevTranslation(); + trans = kfBt + t*kfdAt; + trans *= blend; + } + if(player->type & CAnimBlendSequence::KF_ROT){ + auto kfAr = player->GetNextRotation(); + auto kfBr = player->GetPrevRotation(); + rot.Slerp(kfBr, kfAr, theta, invSin, t); + rot *= blend; + } + } + + return looped; +} + +bool +CAnimBlendNode::NextKeyFrame(void) +{ + assert(player != nil); + bool looped; + + if(player->numFrames <= 1) + return false; + + looped = false; + + // Advance as long as we have to + while(remainingTime <= 0.0f){ + frameA++; + + if(frameA >= player->numFrames){ + // reached end of animation + if(!association->IsRepeating()){ + frameA--; + remainingTime = 0.0f; + assert(frameA == player->curFrame); + CalcDeltas(); + return false; + } + looped = true; + frameA = 0; + } + player->AdvanceFrame(); + remainingTime += player->GetNextTimeDelta(); + } + + assert(frameA == player->curFrame); + CalcDeltas(); + return looped; +} + +// Set animation to time t +bool +CAnimBlendNode::FindKeyFrame(float t) +{ + if (player == nil) { + player = new CAnimBlendPlayer(); + player->Init(sequence->keyFrames, sequence->type, sequence->numFrames); + } + if(player->numFrames < 1) + return false; + + frameA = 0; + player->SeekToStart(); + + assert (player->keyFrames == sequence->keyFrames); + + if(player->numFrames == 1){ + remainingTime = 0.0f; + }else{ + // advance until t is between frameB and frameA + frameA++; + player->AdvanceFrame(); + while (t > player->GetNextTimeDelta()) { + t -= player->GetNextTimeDelta(); + if (frameA + 1 >= player->numFrames) { + // reached end of animation + if (!association->IsRepeating()) { + assert(frameA == player->curFrame); + CalcDeltas(); + remainingTime = 0.0f; + return false; + } + // Frame 0 is effectively skipped here + // Looks like an re3 / game bug? + frameA = 0; + player->SeekToStart(); + } + frameA++; + player->AdvanceFrame(); + } + + remainingTime = player->GetNextTimeDelta() - t; + } + + assert(frameA == player->curFrame); + CalcDeltas(); + return true; +} + +void +CAnimBlendNode::CalcDeltas(void) +{ + if((player->type & CAnimBlendSequence::KF_ROT) == 0) + return; + auto kfAr = player->GetNextRotation(); + auto kfBr = player->GetPrevRotation(); + float cos = DotProduct(kfAr, kfBr); + if(cos > 1.0f) + cos = 1.0f; + theta = Acos(cos); + invSin = theta == 0.0f ? 0.0f : 1.0f/Sin(theta); +} + +void +CAnimBlendNode::GetCurrentTranslation(CVector &trans, float weight) +{ + trans = CVector(0.0f, 0.0f, 0.0f); + + float blend = association->GetBlendAmount(weight); + if(blend > 0.0f){ + auto kfAdt = player->GetNextTimeDelta(); + float t = kfAdt == 0.0f ? 0.0f : (kfAdt - remainingTime)/kfAdt; + if(player->type & CAnimBlendSequence::KF_TRANS){ + auto kfdAt = player->GetNextTranslationDelta(); + auto kfBt = player->GetPrevTranslation(); + trans = kfBt + t*kfdAt; + trans *= blend; + } + } +} + + +void +CAnimBlendNode::GetEndTranslation(CVector &trans, float weight) +{ + trans = CVector(0.0f, 0.0f, 0.0f); + + float blend = association->GetBlendAmount(weight); + if(blend > 0.0f){ + if(player->type & CAnimBlendSequence::KF_TRANS){ + CVector pos = sequence->GetEndTranslation(); + trans = pos * blend; + } + } +} \ No newline at end of file diff --git a/src/miami/animation/AnimBlendNode.h b/src/miami/animation/AnimBlendNode.h new file mode 100644 index 00000000..71ea6b0c --- /dev/null +++ b/src/miami/animation/AnimBlendNode.h @@ -0,0 +1,32 @@ +#pragma once + +#include "AnimBlendSequence.h" + +class CAnimBlendAssociation; + +// The interpolated state between two key frames in a sequence +class CAnimBlendNode +{ +public: + // for slerp + float theta; // angle between quaternions + float invSin; // 1/Sin(theta) + // indices into array in sequence + int32 frameA; // next key frame + float remainingTime; // time until frames have to advance + CAnimBlendPlayer* player; + CAnimBlendSequence *sequence; + CAnimBlendAssociation *association; + + void Init(void); + void Destroy(void); + bool Update(CVector &trans, CQuaternion &rot, float weight); + bool NextKeyFrame(void); + bool FindKeyFrame(float t); + void CalcDeltas(void); + void GetCurrentTranslation(CVector &trans, float weight); + void GetEndTranslation(CVector &trans, float weight); +}; + + +VALIDATE_SIZE(CAnimBlendNode, 0x1C); diff --git a/src/miami/animation/AnimBlendSequence.cpp b/src/miami/animation/AnimBlendSequence.cpp new file mode 100644 index 00000000..8262252d --- /dev/null +++ b/src/miami/animation/AnimBlendSequence.cpp @@ -0,0 +1,46 @@ +#include "common.h" + +#include "AnimBlendSequence.h" +#include "MemoryHeap.h" + +CAnimBlendSequence::CAnimBlendSequence(void) +{ + type = 0; + numFrames = 0; + keyFrames = nil; + boneTag = -1; +} + +CAnimBlendSequence::~CAnimBlendSequence(void) +{ + if(keyFrames) + RwFree(keyFrames); +} + +void +CAnimBlendSequence::SetName(char *name) +{ + strncpy(this->name, name, 24); +} + +#ifdef USE_CUSTOM_ALLOCATOR +bool +CAnimBlendSequence::MoveMemory(void) +{ + if(keyFrames){ + void *newaddr = gMainHeap.MoveMemory(keyFrames); + if(newaddr != keyFrames){ + keyFrames = newaddr; + return true; + } + }else if(keyFramesCompressed){ + void *newaddr = gMainHeap.MoveMemory(keyFramesCompressed); + if(newaddr != keyFramesCompressed){ + keyFramesCompressed = newaddr; + return true; + } + } + return false; +} +#endif + diff --git a/src/miami/animation/AnimBlendSequence.h b/src/miami/animation/AnimBlendSequence.h new file mode 100644 index 00000000..65b51259 --- /dev/null +++ b/src/miami/animation/AnimBlendSequence.h @@ -0,0 +1,398 @@ +#pragma once + +#include + +#include "Quaternion.h" + +#ifdef MoveMemory +#undef MoveMemory // windows shit +#endif + +struct CAnimBlendPlayer { + enum { + KF_ROT = 1, + KF_TRANS = 2, + + FLAGS_HAS_ROT_Y = 1 << 8, + FLAGS_HAS_ROT_P = 1 << 9, + FLAGS_HAS_ROT_R = 1 << 10, + + FLAGS_HAS_TRANS_X = 1 << 11, + FLAGS_HAS_TRANS_Y = 1 << 12, + FLAGS_HAS_TRANS_Z = 1 << 13, + FLAGS_HAS_TRANS_ANY = 7 << 11, + FLAGS_HAS_TRANS_LARGE = 1 << 14, + FLAGS_QUAT0_NEG = 1 << 15 + }; + + int32 type; + void* keyFrames; + int32 curFrame; + int32 numFrames; + CQuaternion currentRotation; + CQuaternion nextRotation; + CVector currentTranslation; + CVector nextTranslation; + + unsigned readOffset; + unsigned readOffset_initial; + + uint16_t predicted_y, predicted_p, predicted_r; + float predicted_tx = 0, predicted_ty = 0, predicted_tz = 0; + float nextDeltaTime; + + template + T read_unaligned(uint32_t ro) { + T rv; + for (unsigned i = 0; i < sizeof(T); i++) { + ((uint8_t*)&rv)[i] = ((uint8_t*)keyFrames)[ro]; + ro++; + } + readOffset = ro; + return rv; + } + template + __always_inline T read() { + if (!(readOffset & (sizeof(T) -1))) { + return read_aligned(); + } else { + return read_unaligned(readOffset); + } + } + + template + __always_inline T read_aligned() { + T rv; + rv = *(T*)((uint8_t*)keyFrames + readOffset); + readOffset += sizeof(T); + return rv; + } + + __always_inline CQuaternion fromSphericalFixed(uint16_t y, uint16_t p, uint16_t r) { + CQuaternion q; + #if !defined(DC_SH4) + q.w = cos((y / 65536.0f) * 2 * M_PI) * cos((p / 65536.0f) * 2 * M_PI); + q.x = cos((y / 65536.0f) * 2 * M_PI) * sin((p / 65536.0f) * 2 * M_PI); + q.y = sin((y / 65536.0f) * 2 * M_PI) * cos((r / 65536.0f) * 2 * M_PI); + q.z = sin((y / 65536.0f) * 2 * M_PI) * sin((r / 65536.0f) * 2 * M_PI); + #else + register float __ys __asm__("fr0"); + register float __yc __asm__("fr1"); + register float __ps __asm__("fr2"); + register float __pc __asm__("fr3"); + register float __rs __asm__("fr4"); + register float __rc __asm__("fr5"); + + __asm__ __volatile__( + R"( + lds %[y],fpul + fsca fpul, dr0 + lds %[p],fpul + fsca fpul, dr2 + lds %[r],fpul + fsca fpul, dr4 + )" + : "=f" (__ys), "=f" (__yc), "=f" (__ps), "=f" (__pc), "=f" (__rs), "=f" (__rc) + : "0" (__ys), "1" (__yc), "2" (__ps), "3" (__pc), "4" (__rs), "5" (__rc), [y]"r"(y), [p]"r"(p), [r]"r"(r)); + + q.w = __yc * __pc; + q.x = __yc * __ps; + q.y = __ys * __rc; + q.z = __ys * __rs; + #endif + return q; + } + + void AdvanceFrame() { + if (++curFrame == numFrames){ + currentRotation = nextRotation; + currentTranslation = nextTranslation; + SeekToStart(); + return; + } + + // rotation + { + currentRotation = nextRotation; + + // For rotation Y: + if (type & FLAGS_HAS_ROT_Y) { + uint8_t byteVal = read(); + if (byteVal == 128) { + predicted_y = read(); + } else { + int8_t diff = static_cast(byteVal); + predicted_y += diff * 8; + } + } + // For rotation P: + if (type & FLAGS_HAS_ROT_P) { + uint8_t byteVal = read(); + if (byteVal == 128) { + predicted_p = read(); + } else { + int8_t diff = static_cast(byteVal); + predicted_p += diff * 8; + } + } + // For rotation R: + if (type & FLAGS_HAS_ROT_R) { + uint8_t byteVal = read(); + if (byteVal == 128) { + predicted_r = read(); + } else { + int8_t diff = static_cast(byteVal); + predicted_r += diff * 8; + } + } + + nextRotation = fromSphericalFixed(predicted_y, predicted_p, predicted_r); + } + + // translation + if (type & KF_TRANS) { + currentTranslation = nextTranslation; + if (type & FLAGS_HAS_TRANS_X) { + uint8_t byteVal = read(); + if (byteVal == 128) { + uint16_t diff = read(); + if (diff != 32768) { + predicted_tx += static_cast(diff) / 128.f; + } else { + predicted_tx = read(); + } + } else { + int8_t diff = static_cast(byteVal); + predicted_tx += diff / 127.f; + } + } + // Translation Y: + if (type & FLAGS_HAS_TRANS_Y) { + uint8_t byteVal = read(); + if (byteVal == 128) { + uint16_t diff = read(); + if (diff != 32768) { + predicted_ty += static_cast(diff) / 128.f; + } else { + predicted_ty = read(); + } + } else { + int8_t diff = static_cast(byteVal); + predicted_ty += diff / 127.f; + } + } + // Translation Z: + if (type & FLAGS_HAS_TRANS_Z) { + uint8_t byteVal = read(); + if (byteVal == 128) { + uint16_t diff = read(); + if (diff != 32768) { + predicted_tz += static_cast(diff) / 128.f; + } else { + predicted_tz = read(); + } + } else { + int8_t diff = static_cast(byteVal); + predicted_tz += diff / 127.f; + } + } + + nextTranslation = { predicted_tx, predicted_ty, predicted_tz }; + } + + // time delta + quaternion flips + { + uint8_t byteValPacked = read(); + uint8_t byteVal = byteValPacked & 127; + float diff; + if (byteVal == 127) { + uint16_t fixed_diff = read(); + diff = fixed_diff / 256.f; + } else { + diff = byteVal / 256.f; + } + nextDeltaTime = diff; + + if (byteValPacked & 128) { + nextRotation = -nextRotation; + } + } + } + + CQuaternion GetPrevRotation() { + return currentRotation; + } + CQuaternion GetNextRotation() { + return nextRotation; + } + float GetNextTimeDelta() { + return nextDeltaTime; + } + + CVector GetPrevTranslation() { + return currentTranslation; + } + CVector GetNextTranslationDelta() { + return nextTranslation - currentTranslation; + } + + void Init(void* kf, int32 tp, int nF) { + keyFrames = kf; + type = tp; + numFrames = nF; + + SeekToStart(); + currentTranslation = nextTranslation; + currentRotation = nextRotation; + } + + void SeekToStart() { + readOffset = 0; + float startTime = read_aligned(); + float endTime = read_aligned(); + + if (type & KF_TRANS) { + CVector startTranslation; + if (type & FLAGS_HAS_TRANS_LARGE) { + startTranslation.x = read_aligned(); + startTranslation.y = read_aligned(); + startTranslation.z = read_aligned(); + predicted_tx = startTranslation.x; + predicted_ty = startTranslation.y; + predicted_tz = startTranslation.z; + + CVector endTranslation; + // Read final translation (may be used for verification or ignored) + endTranslation.x = read_aligned(); + endTranslation.y = read_aligned(); + endTranslation.z = read_aligned(); + } else { + startTranslation.x = read_aligned() / 128.f; + startTranslation.y = read_aligned() / 128.f; + startTranslation.z = read_aligned() / 128.f; + predicted_tx = startTranslation.x; + predicted_ty = startTranslation.y; + predicted_tz = startTranslation.z; + + CVector endTranslation; + // Read final translation (for completeness) + endTranslation.x = read_aligned() / 128.f; + endTranslation.y = read_aligned() / 128.f; + endTranslation.z = read_aligned() / 128.f; + } + + nextTranslation = startTranslation; + } else { + CVector startTranslation = { 0, 0, 0 }; + CVector endTranslation = { 0, 0, 0 }; + nextTranslation = startTranslation; + } + + predicted_y = read_aligned(); + predicted_p = read_aligned(); + predicted_r = read_aligned(); + nextRotation = fromSphericalFixed(predicted_y, predicted_p, predicted_r); + + if (type & FLAGS_QUAT0_NEG) { + nextRotation = -nextRotation; + } + + nextDeltaTime = startTime; + curFrame = 0; + } +}; + +// The sequence of key frames of one animated node +class CAnimBlendSequence +{ + template + __always_inline T read_aligned(uint32_t &readOffset) { + T rv; + rv = *(T*)((uint8_t*)keyFrames + readOffset); + readOffset += sizeof(T); + return rv; + } + +public: + enum { + KF_ROT = 1, + KF_TRANS = 2, + + FLAGS_HAS_ROT_Y = 1 << 8, + FLAGS_HAS_ROT_P = 1 << 9, + FLAGS_HAS_ROT_R = 1 << 10, + + FLAGS_HAS_TRANS_X = 1 << 11, + FLAGS_HAS_TRANS_Y = 1 << 12, + FLAGS_HAS_TRANS_Z = 1 << 13, + FLAGS_HAS_TRANS_ANY = 7 << 11, + FLAGS_HAS_TRANS_LARGE = 1 << 14, + FLAGS_QUAT0_NEG = 1 << 15 + }; + int32 type; + char name[24]; + int32 numFrames; + int16 boneTag; + void *keyFrames; + + struct InitData { + CVector startTranslation, endTranslation; + float endTime; + }; + + __always_inline InitData GetInitData() { + InitData rv; + uint32_t readOffset = 0; + + float startTime = read_aligned(readOffset); + rv.endTime = read_aligned(readOffset); + + if (type & KF_TRANS) { + if (type & FLAGS_HAS_TRANS_LARGE) { + rv.startTranslation.x = read_aligned(readOffset); + rv.startTranslation.y = read_aligned(readOffset); + rv.startTranslation.z = read_aligned(readOffset); + + // Read final translation (may be used for verification or ignored) + rv.endTranslation.x = read_aligned(readOffset); + rv.endTranslation.y = read_aligned(readOffset); + rv.endTranslation.z = read_aligned(readOffset); + } else { + rv.startTranslation.x = read_aligned(readOffset) / 128.f; + rv.startTranslation.y = read_aligned(readOffset) / 128.f; + rv.startTranslation.z = read_aligned(readOffset) / 128.f; + + // Read final translation (for completeness) + rv.endTranslation.x = read_aligned(readOffset) / 128.f; + rv.endTranslation.y = read_aligned(readOffset) / 128.f; + rv.endTranslation.z = read_aligned(readOffset) / 128.f; + } + } else { + rv.startTranslation = { 0, 0, 0 }; + rv.endTranslation = { 0, 0, 0 }; + } + + return rv; + } + + + CVector GetStartTranslation() { + return GetInitData().startTranslation; + } + float GetEndTime() { + return GetInitData().endTime; + } + CVector GetEndTranslation() { + return GetInitData().endTranslation; + } + + CAnimBlendSequence(void); + virtual ~CAnimBlendSequence(void); + void SetName(char *name); + + bool HasTranslation(void) { return !!(type & KF_TRANS); } + bool MoveMemory(void); + + void SetBoneTag(int tag) { boneTag = tag; } +}; +VALIDATE_SIZE(CAnimBlendSequence, 0x30); diff --git a/src/miami/animation/AnimManager.cpp b/src/miami/animation/AnimManager.cpp new file mode 100644 index 00000000..73388016 --- /dev/null +++ b/src/miami/animation/AnimManager.cpp @@ -0,0 +1,1338 @@ +#include "common.h" + +#include "General.h" +#include "RwHelper.h" +#include "ModelInfo.h" +#include "ModelIndices.h" +#include "FileMgr.h" +#include "RpAnimBlend.h" +#include "AnimBlendClumpData.h" +#include "AnimBlendAssociation.h" +#include "AnimBlendAssocGroup.h" +#include "AnimManager.h" +#include "Streaming.h" + +CAnimBlock CAnimManager::ms_aAnimBlocks[NUMANIMBLOCKS]; +CAnimBlendHierarchy CAnimManager::ms_aAnimations[NUMANIMATIONS]; +int32 CAnimManager::ms_numAnimBlocks; +int32 CAnimManager::ms_numAnimations; +CAnimBlendAssocGroup *CAnimManager::ms_aAnimAssocGroups; + +AnimAssocDesc aStdAnimDescs[] = { + { ANIM_STD_WALK, ASSOC_REPEAT | ASSOC_MOVEMENT | ASSOC_HAS_TRANSLATION | ASSOC_WALK }, + { ANIM_STD_RUN, ASSOC_REPEAT | ASSOC_MOVEMENT | ASSOC_HAS_TRANSLATION | ASSOC_WALK }, + { ANIM_STD_RUNFAST, ASSOC_REPEAT | ASSOC_MOVEMENT | ASSOC_HAS_TRANSLATION | ASSOC_WALK }, + { ANIM_STD_IDLE, ASSOC_REPEAT }, + { ANIM_STD_STARTWALK, ASSOC_HAS_TRANSLATION }, + { ANIM_STD_RUNSTOP1, ASSOC_DELETEFADEDOUT | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_RUNSTOP2, ASSOC_DELETEFADEDOUT | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_IDLE_CAM, ASSOC_REPEAT | ASSOC_PARTIAL }, + { ANIM_STD_IDLE_HBHB, ASSOC_REPEAT | ASSOC_PARTIAL }, + { ANIM_STD_IDLE_TIRED, ASSOC_REPEAT }, + { ANIM_STD_IDLE_BIGGUN, ASSOC_REPEAT | ASSOC_PARTIAL }, + { ANIM_STD_CHAT, ASSOC_REPEAT | ASSOC_PARTIAL }, + { ANIM_STD_HAILTAXI, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_KO_FRONT, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_FRONTAL }, + { ANIM_STD_KO_LEFT, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_FRONTAL }, + { ANIM_STD_KO_BACK, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_FRONTAL }, + { ANIM_STD_KO_RIGHT, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_FRONTAL }, + { ANIM_STD_KO_SHOT_FACE, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_FRONTAL }, + { ANIM_STD_KO_SHOT_STOMACH, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_KO_SHOT_ARM_L, ASSOC_PARTIAL | ASSOC_FRONTAL }, + { ANIM_STD_KO_SHOT_ARM_R, ASSOC_PARTIAL | ASSOC_FRONTAL }, + { ANIM_STD_KO_SHOT_LEG_L, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_KO_SHOT_LEG_R, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_SPINFORWARD_LEFT, ASSOC_PARTIAL | ASSOC_FRONTAL }, + { ANIM_STD_SPINFORWARD_RIGHT, ASSOC_PARTIAL | ASSOC_FRONTAL }, + { ANIM_STD_HIGHIMPACT_FRONT, ASSOC_PARTIAL }, + { ANIM_STD_HIGHIMPACT_LEFT, ASSOC_PARTIAL }, + { ANIM_STD_HIGHIMPACT_BACK, ASSOC_PARTIAL | ASSOC_FRONTAL }, + { ANIM_STD_HIGHIMPACT_RIGHT, ASSOC_PARTIAL }, + { ANIM_STD_HITBYGUN_FRONT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_NOWALK }, + { ANIM_STD_HITBYGUN_LEFT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_NOWALK }, + { ANIM_STD_HITBYGUN_BACK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_NOWALK }, + { ANIM_STD_HITBYGUN_RIGHT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_NOWALK }, + { ANIM_STD_HIT_FRONT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_HIT_LEFT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_HIT_BACK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_HIT_RIGHT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_HIT_FLOOR, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, + { ANIM_STD_HIT_BODYBLOW, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_HIT_CHEST, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_HIT_HEAD, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_HIT_WALK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_HIT_WALL, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_HIT_FLOOR_FRONT, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_FRONTAL }, + { ANIM_STD_HIT_BEHIND, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_FIGHT_IDLE, ASSOC_REPEAT }, + { ANIM_STD_FIGHT_2IDLE, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_FIGHT_SHUFFLE_F, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_FIGHT_BODYBLOW, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_FIGHT_HEAD, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_FIGHT_KICK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_FIGHT_KNEE, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_FIGHT_LHOOK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_FIGHT_PUNCH, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_FIGHT_ROUNDHOUSE, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_FIGHT_LONGKICK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_PARTIAL_PUNCH, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_NOWALK }, + { ANIM_STD_FIGHT_JAB, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_FIGHT_ELBOW_L, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_FIGHT_ELBOW_R, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_FIGHT_BKICK_L, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_FIGHT_BKICK_R, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_DETONATE, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_PUNCH, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_PARTIALPUNCH, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_KICKGROUND, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_THROW_UNDER, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_FIGHT_SHUFFLE_B, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_JACKEDCAR_RHS, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, + { ANIM_STD_JACKEDCAR_LO_RHS, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, + { ANIM_STD_JACKEDCAR_LHS, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, + { ANIM_STD_JACKEDCAR_LO_LHS, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, + { ANIM_STD_QUICKJACK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_QUICKJACKED, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, + { ANIM_STD_CAR_ALIGN_DOOR_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_ALIGNHI_DOOR_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_OPEN_DOOR_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CARDOOR_LOCKED_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_PULL_OUT_PED_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_PULL_OUT_PED_LO_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_GET_IN_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_GET_IN_LO_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_CLOSE_DOOR_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_CLOSE_DOOR_LO_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LO_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_JUMP_IN_LO_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_GETOUT_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_GETOUT_LO_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_CLOSE_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_ALIGN_DOOR_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_ALIGNHI_DOOR_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_OPEN_DOOR_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CARDOOR_LOCKED_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_PULL_OUT_PED_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_PULL_OUT_PED_LO_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_GET_IN_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_GET_IN_LO_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_CLOSE_DOOR_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_CLOSE_DOOR_LO_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_SHUFFLE_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_SHUFFLE_LO_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_SIT, ASSOC_DELETEFADEDOUT}, + { ANIM_STD_CAR_SIT_LO, ASSOC_DELETEFADEDOUT}, + { ANIM_STD_CAR_SIT_P, ASSOC_DELETEFADEDOUT}, + { ANIM_STD_CAR_SIT_P_LO, ASSOC_DELETEFADEDOUT}, + { ANIM_STD_CAR_DRIVE_LEFT, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_STD_CAR_DRIVE_RIGHT, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_STD_CAR_DRIVE_LEFT_LO, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_STD_CAR_DRIVE_RIGHT_LO, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_STD_CAR_DRIVEBY_LEFT, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_STD_CAR_DRIVEBY_RIGHT, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_STD_CAR_DRIVEBY_LEFT_LO, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_STD_CAR_DRIVEBY_RIGHT_LO, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_STD_CAR_LOOKBEHIND, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_STD_BOAT_DRIVE, ASSOC_DELETEFADEDOUT | ASSOC_DRIVING }, + { ANIM_STD_BOAT_DRIVE_LEFT, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_STD_BOAT_DRIVE_RIGHT, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_STD_BOAT_LOOKBEHIND, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_STD_BIKE_PICKUP_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_BIKE_PICKUP_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_BIKE_PULLUP_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_BIKE_PULLUP_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_BIKE_ELBOW_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_BIKE_ELBOW_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_BIKE_FALLOFF, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_BIKE_FALLBACK, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_GETOUT_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_GETOUT_LO_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_CLOSE_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CAR_HOOKERTALK, ASSOC_REPEAT | ASSOC_PARTIAL }, + { ANIM_STD_TRAIN_GETIN, ASSOC_PARTIAL }, + { ANIM_STD_TRAIN_GETOUT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CRAWLOUT_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_CRAWLOUT_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_ROLLOUT_LHS, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_HAS_X_TRANSLATION }, + { ANIM_STD_ROLLOUT_RHS, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_HAS_X_TRANSLATION }, + { ANIM_STD_GET_UP, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_GET_UP_LEFT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_GET_UP_RIGHT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_GET_UP_FRONT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_JUMP_LAUNCH, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_JUMP_GLIDE, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, + { ANIM_STD_JUMP_LAND, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_FALL, ASSOC_REPEAT | ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, + { ANIM_STD_FALL_GLIDE, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, + { ANIM_STD_FALL_LAND, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_FALL_COLLAPSE, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_FALL_ONBACK, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, + { ANIM_STD_FALL_ONFRONT, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_FRONTAL }, + { ANIM_STD_EVADE_STEP, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_EVADE_DIVE, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_FRONTAL }, + { ANIM_STD_XPRESS_SCRATCH, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_IDLE }, + { ANIM_STD_ROADCROSS, ASSOC_REPEAT | ASSOC_PARTIAL }, + { ANIM_STD_TURN180, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_ARREST, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_DROWN, ASSOC_PARTIAL }, + { ANIM_STD_DUCK_DOWN, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, + { ANIM_STD_DUCK_LOW, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, + { ANIM_STD_DUCK_WEAPON, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, + { ANIM_STD_RBLOCK_SHOOT, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, + { ANIM_STD_HANDSUP, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_HANDSCOWER, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_STD_PARTIAL_FUCKU, ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_NOWALK }, + { ANIM_STD_PHONE_IN, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_PHONE_OUT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_PHONE_TALK, ASSOC_REPEAT | ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, + { ANIM_STD_SEAT_DOWN, ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_SEAT_UP, ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_SEAT_IDLE, ASSOC_REPEAT | ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_SEAT_RVRS, ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_ATM, ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_ABSEIL, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, +}; +AnimAssocDesc aVanAnimDescs[] = { + { ANIM_STD_VAN_OPEN_DOOR_REAR_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_VAN_GET_IN_REAR_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_VAN_CLOSE_DOOR_REAR_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_VAN_GET_OUT_REAR_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_VAN_OPEN_DOOR_REAR_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_VAN_GET_IN_REAR_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_VAN_CLOSE_DOOR_REAR_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_VAN_GET_OUT_REAR_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, +}; +AnimAssocDesc aCoachAnimDescs[] = { + { ANIM_STD_COACH_OPEN_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_COACH_OPEN_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_COACH_GET_IN_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_COACH_GET_IN_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STD_COACH_GET_OUT_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, +}; +AnimAssocDesc aBikeAnimDescs[] = { + { ANIM_BIKE_RIDE, ASSOC_DELETEFADEDOUT}, + { ANIM_BIKE_READY, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_BIKE_LEFT, ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_BIKE_RIGHT, ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_BIKE_LEANB, ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_BIKE_LEANF, ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_BIKE_WALKBACK, ASSOC_REPEAT | ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_BIKE_JUMPON_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_BIKE_JUMPON_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_BIKE_KICK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_BIKE_HIT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_BIKE_GETOFF_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_BIKE_GETOFF_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_BIKE_GETOFF_BACK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, + { ANIM_BIKE_DRIVEBY_LHS, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_BIKE_DRIVEBY_RHS, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_BIKE_DRIVEBY_FORWARD, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, + { ANIM_BIKE_RIDE_P, ASSOC_DELETEFADEDOUT | ASSOC_DRIVING }, +}; +AnimAssocDesc aMeleeAnimDescs[] = { + { ANIM_MELEE_ATTACK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_MELEE_ATTACK_2ND, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_MELEE_ATTACK_START, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_NOWALK }, + { ANIM_MELEE_IDLE_FIGHTMODE, ASSOC_REPEAT }, + { ANIM_MELEE_ATTACK_FINISH, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, +}; +AnimAssocDesc aSwingAnimDescs[] = { + { ANIM_MELEE_ATTACK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_MELEE_ATTACK_2ND, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_MELEE_ATTACK_START, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_MELEE_IDLE_FIGHTMODE, ASSOC_REPEAT }, + { ANIM_MELEE_ATTACK_FINISH, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, +}; +AnimAssocDesc aWeaponAnimDescs[] = { + { ANIM_WEAPON_FIRE, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_WEAPON_CROUCHFIRE, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_WEAPON_RELOAD, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_WEAPON_CROUCHRELOAD, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_WEAPON_FIRE_3RD, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, +}; +AnimAssocDesc aMedicAnimDescs[] = { + { ANIM_MEDIC_CPR, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, +}; +AnimAssocDesc aSunbatheAnimDescs[] = { + { ANIM_SUNBATHE_IDLE, ASSOC_REPEAT | ASSOC_PARTIAL }, + { ANIM_SUNBATHE_DOWN, ASSOC_REPEAT | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_HAS_X_TRANSLATION }, + { ANIM_SUNBATHE_UP, ASSOC_REPEAT | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_HAS_X_TRANSLATION }, + { ANIM_SUNBATHE_ESCAPE, ASSOC_REPEAT | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_HAS_X_TRANSLATION }, +}; +AnimAssocDesc aPlayerIdleAnimDescs[] = { + { ANIM_PLAYER_IDLE1, ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_PLAYER_IDLE2, ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_PLAYER_IDLE3, ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_PLAYER_IDLE4, ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, +}; +AnimAssocDesc aRiotAnimDescs[] = { + { ANIM_RIOT_ANGRY, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_RIOT_ANGRY_B, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_RIOT_CHANT, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_RIOT_PUNCHES, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_RIOT_SHOUT, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_RIOT_CHALLENGE, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_RIOT_FUCKYOU, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, +}; +AnimAssocDesc aStripAnimDescs[] = { + { ANIM_STRIP_A, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STRIP_B, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STRIP_C, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STRIP_D, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STRIP_E, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STRIP_F, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, + { ANIM_STRIP_G, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, +}; +#ifdef PC_PLAYER_CONTROLS +AnimAssocDesc aStdAnimDescsSide[] = { + { ANIM_STD_WALK, ASSOC_REPEAT | ASSOC_MOVEMENT | ASSOC_HAS_TRANSLATION | ASSOC_HAS_X_TRANSLATION | ASSOC_WALK }, + { ANIM_STD_RUN, ASSOC_REPEAT | ASSOC_MOVEMENT | ASSOC_HAS_TRANSLATION | ASSOC_HAS_X_TRANSLATION | ASSOC_WALK }, + { ANIM_STD_RUNFAST, ASSOC_REPEAT | ASSOC_MOVEMENT | ASSOC_HAS_TRANSLATION | ASSOC_HAS_X_TRANSLATION | ASSOC_WALK }, + { ANIM_STD_IDLE, ASSOC_REPEAT }, + { ANIM_STD_STARTWALK, ASSOC_HAS_TRANSLATION | ASSOC_HAS_X_TRANSLATION }, +}; +#endif +char const* aStdAnimations[] = { + "walk_civi", + "run_civi", + "sprint_panic", + "idle_stance", + "walk_start", + "run_stop", + "run_stopR", + "idle_hbhb", + "idle_hbhb", + "idle_tired", + "idle_armed", + "idle_chat", + "idle_taxi", + "KO_shot_front", + "KO_shot_front", + "KO_shot_front", + "KO_shot_front", + "KO_shot_face", + "KO_shot_stom", + "KO_shot_arml", + "KO_shot_armR", + "KO_shot_legl", + "KO_shot_legR", + "KD_left", + "KD_right", + "KO_skid_front", + "KO_spin_R", + "KO_skid_back", + "KO_spin_L", + "SHOT_partial", + "SHOT_leftP", + "SHOT_partial", + "SHOT_rightP", + "HIT_front", + "HIT_L", + "HIT_back", + "HIT_R", + "FLOOR_hit", + "HIT_bodyblow", + "HIT_chest", + "HIT_head", + "HIT_walk", + "HIT_wall", + "FLOOR_hit_f", + "HIT_behind", + "FIGHTIDLE", + "FIGHT2IDLE", + "FIGHTsh_F", + "FIGHTbodyblow", + "FIGHThead", + "FIGHTkick", + "FIGHTknee", + "FIGHTLhook", + "FIGHTpunch", + "FIGHTrndhse", + "FIGHTlngkck", + "FIGHTppunch", + "FIGHTjab", + "FIGHTelbowL", + "FIGHTelbowR", + "FIGHTbkickL", + "FIGHTbkickR", + "bomber", + "punchR", + "FIGHTppunch", + "KICK_floor", + "WEAPON_throwu", + "FIGHTsh_back", + "car_jackedRHS", + "car_LjackedRHS", + "car_jackedLHS", + "car_LjackedLHS", + "CAR_Qjack", + "CAR_Qjacked", + "CAR_align_LHS", + "CAR_alignHI_LHS", + "CAR_open_LHS", + "CAR_doorlocked_LHS", + "CAR_pullout_LHS", + "CAR_pulloutL_LHS", + "CAR_getin_LHS", + "CAR_getinL_LHS", + "CAR_closedoor_LHS", + "CAR_closedoorL_LHS", + "CAR_rolldoor", + "CAR_rolldoorLO", + "CAR_jumpin_LHS", + "CAR_getout_LHS", + "CAR_getoutL_LHS", + "CAR_close_LHS", + "CAR_align_RHS", + "CAR_alignHI_RHS", + "CAR_open_RHS", + "CAR_doorlocked_RHS", + "CAR_pullout_RHS", + "CAR_pulloutL_RHS", + "CAR_getin_RHS", + "CAR_getinL_RHS", + "CAR_closedoor_RHS", + "CAR_closedoorL_RHS", + "CAR_shuffle_RHS", + "CAR_Lshuffle_RHS", + "CAR_sit", + "CAR_Lsit", + "CAR_sitp", + "CAR_sitpLO", + "DRIVE_L", + "Drive_R", + "Drive_LO_l", + "Drive_LO_R", + "Driveby_L", + "Driveby_R", + "DrivebyL_L", + "DrivebyL_R", + "CAR_LB", + "DRIVE_BOAT", + "DRIVE_BOAT_L", + "DRIVE_BOAT_R", + "DRIVE_BOAT_back", + "BIKE_pickupR", + "BIKE_pickupL", + "BIKE_pullupR", + "BIKE_pullupL", + "BIKE_elbowR", + "BIKE_elbowL", + "BIKE_fall_off", + "BIKE_fallR", + "CAR_getout_RHS", + "CAR_getoutL_RHS", + "CAR_close_RHS", + "car_hookertalk", + "idle_stance", + "idle_stance", + "CAR_crawloutRHS", + "CAR_crawloutRHS", + "CAR_rollout_LHS", + "CAR_rollout_LHS", + "Getup", + "Getup", + "Getup", + "Getup_front", + "JUMP_launch", + "JUMP_glide", + "JUMP_land", + "FALL_fall", + "FALL_glide", + "FALL_land", + "FALL_collapse", + "FALL_back", + "FALL_front", + "EV_step", + "EV_dive", + "XPRESSscratch", + "roadcross", + "TURN_180", + "ARRESTgun", + "DROWN", + "DUCK_down", + "DUCK_low", + "WEAPON_crouch", + "RBLOCK_Cshoot", + "handsup", + "handsCOWER", + "FUCKU", + "PHONE_in", + "PHONE_out", + "PHONE_talk", + "SEAT_down", + "SEAT_up", + "SEAT_idle", + "SEAT_down", + "ATM", + "abseil", +}; +char const* aVanAnimations[] = { + "VAN_openL", + "VAN_getinL", + "VAN_closeL", + "VAN_getoutL", + "VAN_open", + "VAN_getin", + "VAN_close", + "VAN_getout", +}; +char const* aCoachAnimations[] = { + "COACH_opnL", + "COACH_opnL", + "COACH_inL", + "COACH_inL", + "COACH_outL", +}; +char const* aBikesAnimations[] = { + "BIKEs_Ride", + "BIKEs_Still", + "BIKEs_Left", + "BIKEs_Right", + "BIKEs_Back", + "BIKEs_Fwd", + "BIKEs_pushes", + "BIKEs_jumponR", + "BIKEs_jumponL", + "BIKEs_kick", + "BIKEs_hit", + "BIKEs_getoffRHS", + "BIKEs_getoffLHS", + "BIKEs_getoffBACK", + "BIKEs_drivebyLHS", + "BIKEs_drivebyRHS", + "BIKEs_drivebyFT", + "BIKEs_passenger", +}; +char const* aBikevAnimations[] = { + "BIKEv_Ride", + "BIKEv_Still", + "BIKEv_Left", + "BIKEv_Right", + "BIKEv_Back", + "BIKEv_Fwd", + "BIKEv_pushes", + "BIKEv_jumponR", + "BIKEv_jumponL", + "BIKEv_kick", + "BIKEv_hit", + "BIKEv_getoffRHS", + "BIKEv_getoffLHS", + "BIKEv_getoffBACK", + "BIKEv_drivebyLHS", + "BIKEv_drivebyRHS", + "BIKEv_drivebyFT", + "BIKEv_passenger", +}; +char const* aBikehAnimations[] = { + "BIKEh_Ride", + "BIKEh_Still", + "BIKEh_Left", + "BIKEh_Right", + "BIKEh_Back", + "BIKEh_Fwd", + "BIKEh_pushes", + "BIKEh_jumponR", + "BIKEh_jumponL", + "BIKEh_kick", + "BIKEh_hit", + "BIKEh_getoffRHS", + "BIKEh_getoffLHS", + "BIKEh_getoffBACK", + "BIKEh_drivebyLHS", + "BIKEh_drivebyRHS", + "BIKEh_drivebyFT", + "BIKEh_passenger", +}; +char const* aBikedAnimations[] = { + "BIKEd_Ride", + "BIKEd_Still", + "BIKEd_Left", + "BIKEd_Right", + "BIKEd_Back", + "BIKEd_Fwd", + "BIKEd_pushes", + "BIKEd_jumponR", + "BIKEd_jumponL", + "BIKEd_kick", + "BIKEd_hit", + "BIKEd_getoffRHS", + "BIKEd_getoffLHS", + "BIKEd_getoffBACK", + "BIKEd_drivebyLHS", + "BIKEd_drivebyRHS", + "BIKEd_drivebyFT", + "BIKEd_passenger", +}; +char const* aUnarmedAnimations[] = { + "punchR", + "KICK_floor", + "FIGHTppunch", +}; +char const* aScrewdriverAnimations[] = { + "FIGHTbodyblow", + "FIGHTbodyblow", + "FIGHTppunch", + "FIGHTIDLE", + "FIGHTbodyblow", +}; +char const* aKnifeAnimations[] = { + "WEAPON_knife_1", + "WEAPON_knife_2", + "knife_part", + "WEAPON_knifeidle", + "WEAPON_knife_3", +}; +char const* aBaseballbatAnimations[] = { + "WEAPON_bat_h", + "WEAPON_bat_v", + "BAT_PART", + "WEAPON_bat_h", + "WEAPON_golfclub", +}; +char const* aGolfclubAnimations[] = { + "WEAPON_bat_h", + "WEAPON_golfclub", + "BAT_PART", + "WEAPON_bat_h", + "WEAPON_bat_v", +}; +char const* aChainsawAnimations[] = { + "WEAPON_csaw", + "WEAPON_csawlo", + "csaw_part", +}; +char const* aPythonAnimations[] = { + "python_fire", + "python_crouchfire", + "python_reload", + "python_crouchreload", +}; +char const* aColtAnimations[] = { + "colt45_fire", + "colt45_crouchfire", + "colt45_reload", + "colt45_crouchreload", + "colt45_cop", +}; +char const* aShotgunAnimations[] = { + "shotgun_fire", + "shotgun_crouchfire", +}; +char const* aBuddyAnimations[] = { + "buddy_fire", + "buddy_crouchfire", +}; +char const* aTecAnimations[] = { + "TEC_fire", + "TEC_crouchfire", + "TEC_reload", + "TEC_crouchreload", +}; +char const* aUziAnimations[] = { + "UZI_fire", + "UZI_crouchfire", + "UZI_reload", + "UZI_crouchreload", +}; +char const* aRifleAnimations[] = { + "RIFLE_fire", + "RIFLE_crouchfire", + "RIFLE_load", + "RIFLE_crouchload", +}; +char const* aM60Animations[] = { + "M60_fire", + "M60_fire", + "M60_reload", +}; +char const* aSniperAnimations[] = { + "WEAPON_sniper", +}; +char const* aThrowAnimations[] = { + "WEAPON_throw", + "WEAPON_throwu", + "WEAPON_start_throw", +}; +char const* aFlamethrowerAnimations[] = { + "FLAME_fire", +}; +char const* aMedicAnimations[] = { + "CPR", +}; +char const* aSunbatheAnimations[] = { + "bather", + "batherdown", + "batherup", + "batherscape", +}; +char const* aPlayerIdleAnimations[] = { + "stretch", + "time", + "shldr", + "strleg", +}; +char const* aRiotAnimations[] = { + "riot_angry", + "riot_angry_b", + "riot_chant", + "riot_punches", + "riot_shout", + "riot_challenge", + "riot_fuku", +}; +char const* aStripAnimations[] = { + "strip_A", + "strip_B", + "strip_C", + "strip_D", + "strip_E", + "strip_F", + "strip_G", +}; +char const* aLanceAnimations[] = { + "lance", +}; +char const* aPlayerAnimations[] = { + "walk_player", + "run_player", + "SPRINT_civi", + "IDLE_STANCE", + "walk_start", +}; +char const* aPlayerWithRocketAnimations[] = { + "walk_rocket", + "run_rocket", + "run_rocket", + "idle_rocket", + "walk_start_rocket", +}; +char const* aPlayer1ArmedAnimations[] = { + "walk_player", + "run_1armed", + "SPRINT_civi", + "IDLE_STANCE", + "walk_start", +}; +char const* aPlayer2ArmedAnimations[] = { + "walk_armed", + "run_armed", + "run_armed", + "idle_armed", + "walk_start_armed", +}; +char const* aPlayerBBBatAnimations[] = { + "walk_player", + "run_player", + "run_player", + "IDLE_STANCE", + "walk_start", +}; +char const* aPlayerChainsawAnimations[] = { + "walk_csaw", + "run_csaw", + "run_csaw", + "IDLE_csaw", + "walk_start_csaw", +}; +char const* aShuffleAnimations[] = { + "WALK_shuffle", + "RUN_civi", + "SPRINT_civi", + "IDLE_STANCE", +}; +char const* aOldAnimations[] = { + "walk_old", + "run_civi", + "sprint_civi", + "idle_stance", +}; +char const* aGang1Animations[] = { + "walk_gang1", + "run_gang1", + "sprint_civi", + "idle_stance", +}; +char const* aGang2Animations[] = { + "walk_gang2", + "run_gang1", + "sprint_civi", + "idle_stance", +}; +char const* aFatAnimations[] = { + "walk_fat", + "run_civi", + "woman_runpanic", + "idle_stance", +}; +char const* aOldFatAnimations[] = { + "walk_fatold", + "run_fatold", + "woman_runpanic", + "idle_stance", +}; +char const* aJoggerAnimations[] = { + "JOG_maleA", + "run_civi", + "sprint_civi", + "idle_stance", +}; +char const* aStdWomanAnimations[] = { + "woman_walknorm", + "woman_run", + "woman_runpanic", + "woman_idlestance", +}; +char const* aWomanShopAnimations[] = { + "woman_walkshop", + "woman_run", + "woman_run", + "woman_idlestance", +}; +char const* aBusyWomanAnimations[] = { + "woman_walkbusy", + "woman_run", + "woman_runpanic", + "woman_idlestance", +}; +char const* aSexyWomanAnimations[] = { + "woman_walksexy", + "woman_run", + "woman_runpanic", + "woman_idlestance", +}; +char const* aFatWomanAnimations[] = { + "walk_fat", + "woman_run", + "woman_runpanic", + "woman_idlestance", +}; +char const* aOldWomanAnimations[] = { + "woman_walkold", + "woman_run", + "woman_runpanic", + "woman_idlestance", +}; +char const* aJoggerWomanAnimations[] = { + "JOG_maleB", + "woman_run", + "woman_runpanic", + "woman_idlestance", +}; +char const* aPanicChunkyAnimations[] = { + "run_fatold", + "woman_runpanic", + "woman_runpanic", + "idle_stance", +}; +char const* aSkateAnimations[] = { + "skate_run", + "skate_sprint", + "skate_sprint", + "skate_idle", +}; +#ifdef PC_PLAYER_CONTROLS +char const* aPlayerStrafeBackAnimations[] = { + "walk_back", + "run_back", + "run_back", + "IDLE_STANCE", + "walk_start_back", +}; +char const* aPlayerStrafeLeftAnimations[] = { + "walk_left", + "run_left", + "run_left", + "IDLE_STANCE", + "walk_start_left", +}; +char const* aPlayerStrafeRightAnimations[] = { + "walk_right", + "run_right", + "run_right", + "IDLE_STANCE", + "walk_start_right", +}; +char const* aRocketStrafeBackAnimations[] = { + "walk_rocket_back", + "run_rocket_back", + "run_rocket_back", + "idle_rocket", + "walkst_rocket_back", +}; +char const* aRocketStrafeLeftAnimations[] = { + "walk_rocket_left", + "run_rocket_left", + "run_rocket_left", + "idle_rocket", + "walkst_rocket_left", +}; +char const* aRocketStrafeRightAnimations[] = { + "walk_rocket_right", + "run_rocket_right", + "run_rocket_right", + "idle_rocket", + "walkst_rocket_right", +}; +char const* aChainsawStrafeBackAnimations[] = { + "walk_csaw_back", + "run_csaw_back", + "run_csaw_back", + "idle_csaw", + "walkst_csaw_back", +}; +char const* aChainsawStrafeLeftAnimations[] = { + "walk_csaw_left", + "run_csaw_left", + "run_csaw_left", + "idle_csaw", + "walkst_csaw_left", +}; +char const* aChainsawStrafeRightAnimations[] = { + "walk_csaw_right", + "run_csaw_right", + "run_csaw_right", + "idle_csaw", + "walkst_csaw_right", +}; +#endif + +#define awc(a) ARRAY_SIZE(a), a +const AnimAssocDefinition CAnimManager::ms_aAnimAssocDefinitions[NUM_ANIM_ASSOC_GROUPS] = { + { "man", "ped", MI_COP, awc(aStdAnimations), aStdAnimDescs }, + { "van", "van", MI_COP, awc(aVanAnimations), aVanAnimDescs }, + { "coach", "coach", MI_COP, awc(aCoachAnimations), aCoachAnimDescs }, + { "bikes", "bikes", MI_COP, awc(aBikesAnimations), aBikeAnimDescs }, + { "bikev", "bikev", MI_COP, awc(aBikevAnimations), aBikeAnimDescs }, + { "bikeh", "bikeh", MI_COP, awc(aBikehAnimations), aBikeAnimDescs }, + { "biked", "biked", MI_COP, awc(aBikedAnimations), aBikeAnimDescs }, + { "unarmed", "ped", MI_COP, awc(aUnarmedAnimations), aMeleeAnimDescs }, + { "screwdrv", "ped", MI_COP, awc(aScrewdriverAnimations), aMeleeAnimDescs }, + { "knife", "knife", MI_COP, awc(aKnifeAnimations), aMeleeAnimDescs }, + { "baseball", "baseball", MI_COP, awc(aBaseballbatAnimations), aSwingAnimDescs }, + { "golfclub", "baseball", MI_COP, awc(aGolfclubAnimations), aSwingAnimDescs }, + { "chainsaw", "chainsaw", MI_COP, awc(aChainsawAnimations), aMeleeAnimDescs }, + { "python", "python", MI_COP, awc(aPythonAnimations), aWeaponAnimDescs }, + { "colt45", "colt45", MI_COP, awc(aColtAnimations), aWeaponAnimDescs }, + { "shotgun", "shotgun", MI_COP, awc(aShotgunAnimations), aWeaponAnimDescs }, + { "buddy", "buddy", MI_COP, awc(aBuddyAnimations), aWeaponAnimDescs }, + { "tec", "tec", MI_COP, awc(aTecAnimations), aWeaponAnimDescs }, + { "uzi", "uzi", MI_COP, awc(aUziAnimations), aWeaponAnimDescs }, + { "rifle", "rifle", MI_COP, awc(aRifleAnimations), aWeaponAnimDescs }, + { "m60", "m60", MI_COP, awc(aM60Animations), aWeaponAnimDescs }, + { "sniper", "sniper", MI_COP, awc(aSniperAnimations), aWeaponAnimDescs }, + { "grenade", "grenade", MI_COP, awc(aThrowAnimations), aWeaponAnimDescs }, + { "flame", "flame", MI_COP, awc(aFlamethrowerAnimations), aWeaponAnimDescs }, + { "medic", "medic", MI_COP, awc(aMedicAnimations), aMedicAnimDescs }, + { "sunbathe", "sunbathe", MI_COP, 1, aSunbatheAnimations, aSunbatheAnimDescs }, // NB: not using awc here! + { "playidles", "playidles", MI_COP, awc(aPlayerIdleAnimations), aPlayerIdleAnimDescs }, + { "riot", "riot", MI_COP, awc(aRiotAnimations), aRiotAnimDescs }, + { "strip", "strip", MI_COP, awc(aStripAnimations), aStripAnimDescs }, + { "lance", "lance", MI_COP, awc(aLanceAnimations), aSunbatheAnimDescs }, + { "player", "ped", MI_COP, awc(aPlayerAnimations), aStdAnimDescs }, + { "playerrocket", "ped", MI_COP, awc(aPlayerWithRocketAnimations), aStdAnimDescs }, + { "player1armed", "ped", MI_COP, awc(aPlayer1ArmedAnimations), aStdAnimDescs }, + { "player2armed", "ped", MI_COP, awc(aPlayer2ArmedAnimations), aStdAnimDescs }, + { "playerBBBat", "ped", MI_COP, awc(aPlayerBBBatAnimations), aStdAnimDescs }, + { "playercsaw", "ped", MI_COP, awc(aPlayerChainsawAnimations), aStdAnimDescs }, + { "shuffle", "ped", MI_COP, awc(aShuffleAnimations), aStdAnimDescs }, + { "oldman", "ped", MI_COP, awc(aOldAnimations), aStdAnimDescs }, + { "gang1", "ped", MI_COP, awc(aGang1Animations), aStdAnimDescs }, + { "gang2", "ped", MI_COP, awc(aGang2Animations), aStdAnimDescs }, + { "fatman", "ped", MI_COP, awc(aFatAnimations), aStdAnimDescs }, + { "oldfatman", "ped", MI_COP, awc(aOldFatAnimations), aStdAnimDescs }, + { "jogger", "ped", MI_COP, awc(aJoggerAnimations), aStdAnimDescs }, + { "woman", "ped", MI_COP, awc(aStdWomanAnimations), aStdAnimDescs }, + { "shopping", "ped", MI_COP, awc(aWomanShopAnimations), aStdAnimDescs }, + { "busywoman", "ped", MI_COP, awc(aBusyWomanAnimations), aStdAnimDescs }, + { "sexywoman", "ped", MI_COP, awc(aSexyWomanAnimations), aStdAnimDescs }, + { "fatwoman", "ped", MI_COP, awc(aFatWomanAnimations), aStdAnimDescs }, + { "oldwoman", "ped", MI_COP, awc(aOldWomanAnimations), aStdAnimDescs }, + { "jogwoman", "ped", MI_COP, awc(aJoggerWomanAnimations), aStdAnimDescs }, + { "panicchunky", "ped", MI_COP, awc(aPanicChunkyAnimations), aStdAnimDescs }, + { "skate", "skate", MI_COP, awc(aSkateAnimations), aStdAnimDescs }, +#ifdef PC_PLAYER_CONTROLS + { "playerback", "ped", MI_COP, awc(aPlayerStrafeBackAnimations), aStdAnimDescs }, + { "playerleft", "ped", MI_COP, awc(aPlayerStrafeLeftAnimations), aStdAnimDescsSide }, + { "playerright", "ped", MI_COP, awc(aPlayerStrafeRightAnimations), aStdAnimDescsSide }, + { "rocketback", "ped", MI_COP, awc(aRocketStrafeBackAnimations), aStdAnimDescs }, + { "rocketleft", "ped", MI_COP, awc(aRocketStrafeLeftAnimations), aStdAnimDescsSide }, + { "rocketright", "ped", MI_COP, awc(aRocketStrafeRightAnimations), aStdAnimDescsSide }, + { "csawback", "ped", MI_COP, awc(aChainsawStrafeBackAnimations), aStdAnimDescs }, + { "csawleft", "ped", MI_COP, awc(aChainsawStrafeLeftAnimations), aStdAnimDescsSide }, + { "csawright", "ped", MI_COP, awc(aChainsawStrafeRightAnimations), aStdAnimDescsSide }, +#endif +}; +#undef awc + +void +CAnimManager::Initialise(void) +{ + ms_numAnimations = 0; + ms_numAnimBlocks = 0; +} + +void +CAnimManager::Shutdown(void) +{ + int i; + + for(i = 0; i < NUMANIMBLOCKS; i++) + CStreaming::RemoveAnim(i); + + for(i = 0; i < ms_numAnimations; i++) + ms_aAnimations[i].Shutdown(); + + + delete[] ms_aAnimAssocGroups; +} + + +CAnimBlock* +CAnimManager::GetAnimationBlock(const char *name) +{ + int i; + + for(i = 0; i < ms_numAnimBlocks; i++) + if(strcasecmp(ms_aAnimBlocks[i].name, name) == 0) + return &ms_aAnimBlocks[i]; + return nil; +} + +int32 +CAnimManager::GetAnimationBlockIndex(const char *name) +{ + int i; + + for(i = 0; i < ms_numAnimBlocks; i++) + if(strcasecmp(ms_aAnimBlocks[i].name, name) == 0) + return i; + return -1; +} + +int32 +CAnimManager::RegisterAnimBlock(const char *name) +{ + CAnimBlock *animBlock = GetAnimationBlock(name); + if(animBlock == nil){ + animBlock = &ms_aAnimBlocks[ms_numAnimBlocks++]; + strncpy(animBlock->name, name, MAX_ANIMBLOCK_NAME); + animBlock->numAnims = 0; + assert(animBlock->refCount == 0); + } + return animBlock - ms_aAnimBlocks; +} + +int32 +CAnimManager::GetNumRefsToAnimBlock(int32 block) +{ + return ms_aAnimBlocks[block].refCount; +} + +void +CAnimManager::AddAnimBlockRef(int32 block) +{ + ms_aAnimBlocks[block].refCount++; +} + +void +CAnimManager::RemoveAnimBlockRefWithoutDelete(int32 block) +{ + ms_aAnimBlocks[block].refCount--; +} + +void +CAnimManager::RemoveAnimBlockRef(int32 block) +{ + ms_aAnimBlocks[block].refCount--; + if(ms_aAnimBlocks[block].refCount == 0) + CStreaming::RemoveAnim(block); +} + +void +CAnimManager::RemoveAnimBlock(int32 block) +{ + int i; + CAnimBlock *animblock; + + animblock = &ms_aAnimBlocks[block]; + debug("Removing ANIMS %s\n", animblock->name); + for(i = 0; i < NUM_ANIM_ASSOC_GROUPS; i++) + if(ms_aAnimAssocGroups[i].animBlock == animblock) + ms_aAnimAssocGroups[i].DestroyAssociations(); + for(i = 0; i < animblock->numAnims; i++) + ms_aAnimations[animblock->firstIndex + i].Shutdown(); + animblock->isLoaded = false; + animblock->refCount = 0; +} + +CAnimBlendHierarchy* +CAnimManager::GetAnimation(const char *name, CAnimBlock *animBlock) +{ + int i; + CAnimBlendHierarchy *hier = &ms_aAnimations[animBlock->firstIndex]; + + for(i = 0; i < animBlock->numAnims; i++){ + if(strcasecmp(hier->name, name) == 0) + return hier; + hier++; + } + return nil; +} + +const char* +CAnimManager::GetAnimGroupName(AssocGroupId groupId) +{ + return ms_aAnimAssocDefinitions[groupId].name; +} + +CAnimBlendAssociation* +CAnimManager::CreateAnimAssociation(AssocGroupId groupId, AnimationId animId) +{ + return ms_aAnimAssocGroups[groupId].CopyAnimation(animId); +} + +CAnimBlendAssociation* +CAnimManager::GetAnimAssociation(AssocGroupId groupId, AnimationId animId) +{ + return ms_aAnimAssocGroups[groupId].GetAnimation(animId); +} + +CAnimBlendAssociation* +CAnimManager::GetAnimAssociation(AssocGroupId groupId, const char *name) +{ + return ms_aAnimAssocGroups[groupId].GetAnimation(name); +} + +CAnimBlendAssociation* +CAnimManager::AddAnimation(RpClump *clump, AssocGroupId groupId, AnimationId animId) +{ + CAnimBlendAssociation *anim = CreateAnimAssociation(groupId, animId); + CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); + if(anim->IsMovement()){ + CAnimBlendAssociation *syncanim = nil; + CAnimBlendLink *link; + for(link = clumpData->link.next; link; link = link->next){ + syncanim = CAnimBlendAssociation::FromLink(link); + if(syncanim->IsMovement()) + break; + } + if(link){ + anim->SyncAnimation(syncanim); + anim->flags |= ASSOC_RUNNING; + }else + anim->Start(0.0f); + }else + anim->Start(0.0f); + + clumpData->link.Prepend(&anim->link); + return anim; +} + +CAnimBlendAssociation* +CAnimManager::AddAnimationAndSync(RpClump *clump, CAnimBlendAssociation *syncanim, AssocGroupId groupId, AnimationId animId) +{ + CAnimBlendAssociation *anim = CreateAnimAssociation(groupId, animId); + CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); + if (anim->IsMovement() && syncanim){ + anim->SyncAnimation(syncanim); + anim->flags |= ASSOC_RUNNING; + }else + anim->Start(0.0f); + + clumpData->link.Prepend(&anim->link); + return anim; +} + +CAnimBlendAssociation* +CAnimManager::BlendAnimation(RpClump *clump, AssocGroupId groupId, AnimationId animId, float delta) +{ + int removePrevAnim = 0; + CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); + CAnimBlendAssociation *anim = GetAnimAssociation(groupId, animId); + bool isMovement = anim->IsMovement(); + bool isPartial = anim->IsPartial(); + CAnimBlendLink *link; + CAnimBlendAssociation *found = nil, *movementAnim = nil; + for(link = clumpData->link.next; link; link = link->next){ + anim = CAnimBlendAssociation::FromLink(link); + if(isMovement && anim->IsMovement()) + movementAnim = anim; + if(anim->animId == animId) + found = anim; + else{ + if(isPartial == anim->IsPartial()){ + if(anim->blendAmount > 0.0f){ + float blendDelta = -delta*anim->blendAmount; + if(blendDelta < anim->blendDelta || !isPartial) + anim->blendDelta = blendDelta; + }else{ + anim->blendDelta = -1.0f; + } + anim->flags |= ASSOC_DELETEFADEDOUT; + removePrevAnim = 1; + } + } + } + if(found){ + found->blendDelta = (1.0f - found->blendAmount)*delta; + if(!found->IsRunning() && found->currentTime == found->hierarchy->totalLength) + found->Start(0.0f); + }else{ + found = AddAnimationAndSync(clump, movementAnim, groupId, animId); + if(!removePrevAnim && !isPartial){ + found->blendAmount = 1.0f; + return found; + } + found->blendAmount = 0.0f; + found->blendDelta = delta; + } + return found; +} + +void +CAnimManager::LoadAnimFiles(void) +{ + LoadAnimFile("ANIM\\PED.IFP"); + ms_aAnimAssocGroups = new CAnimBlendAssocGroup[NUM_ANIM_ASSOC_GROUPS]; + CreateAnimAssocGroups(); +} + +void +CAnimManager::CreateAnimAssocGroups(void) +{ + int i, j; + + for(i = 0; i < NUM_ANIM_ASSOC_GROUPS; i++){ + CAnimBlock *block = GetAnimationBlock(ms_aAnimAssocDefinitions[i].blockName); + if(block == nil || !block->isLoaded || ms_aAnimAssocGroups[i].assocList) + continue; + + CBaseModelInfo *mi = CModelInfo::GetModelInfo(ms_aAnimAssocDefinitions[i].modelIndex); + RpClump *clump = (RpClump*)mi->CreateInstance(); + RpAnimBlendClumpInit(clump); + CAnimBlendAssocGroup *group = &ms_aAnimAssocGroups[i]; + const AnimAssocDefinition *def = &ms_aAnimAssocDefinitions[i]; + group->groupId = i; + group->firstAnimId = def->animDescs[0].animId; + group->CreateAssociations(def->blockName, clump, def->animNames, def->numAnims); + for(j = 0; j < group->numAssociations; j++) + // GetAnimation(i) in III (but it's in LoadAnimFiles), GetAnimation(group->animDesc[j].animId) in VC + group->GetAnimation(def->animDescs[j].animId)->flags |= def->animDescs[j].flags; + if(IsClumpSkinned(clump)) + RpClumpForAllAtomics(clump, AtomicRemoveAnimFromSkinCB, nil); + RpClumpDestroy(clump); + } +} + +void +CAnimManager::LoadAnimFile(const char *filename) +{ + RwStream *stream; + stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, filename); + assert(stream); + LoadAnimFile(stream, true); + RwStreamClose(stream, nil); +} + +void +CAnimManager::LoadAnimFile(RwStream *stream, bool compress, char (*uncompressedAnims)[32]) +{ + #define ROUNDSIZE(x) if((x) & 3) (x) += 4 - ((x)&3) + struct IfpHeader { + char ident[4]; + uint32 size; + }; + IfpHeader anpv; + char buf[256]; + int j, k, l; + float *fbuf = (float*)buf; + + RwStreamRead(stream, &anpv, sizeof(IfpHeader)); + assert(memcmp(anpv.ident, "ANPV", 4) == 0); + + // block name + RwStreamRead(stream, buf, anpv.size); + int32_t numAnims; + RwStreamRead(stream, &numAnims, sizeof(numAnims)); + CAnimBlock *animBlock = GetAnimationBlock(buf); + if(animBlock){ + if(animBlock->numAnims == 0){ + animBlock->numAnims = numAnims; + animBlock->firstIndex = ms_numAnimations; + } + }else{ + animBlock = &ms_aAnimBlocks[ms_numAnimBlocks++]; + strncpy(animBlock->name, buf, MAX_ANIMBLOCK_NAME); + animBlock->numAnims = numAnims; + animBlock->firstIndex = ms_numAnimations; + } + + debug("Loading ANIMS %s\n", animBlock->name); + + animBlock->isLoaded = true; + + int animIndex = animBlock->firstIndex; + for(j = 0; j < animBlock->numAnims; j++){ + assert(animIndex < ARRAY_SIZE(ms_aAnimations)); + CAnimBlendHierarchy *hier = &ms_aAnimations[animIndex++]; + + int32_t animNameLength; + // animation name + RwStreamRead(stream, &animNameLength, sizeof(animNameLength)); + RwStreamRead(stream, buf, animNameLength); + hier->SetName(buf); + + int32_t numSeqs; + RwStreamRead(stream, &numSeqs, sizeof(numSeqs)); + hier->numSequences = numSeqs; + hier->sequences = new CAnimBlendSequence[hier->numSequences]; + + CAnimBlendSequence *seq = hier->sequences; + for(k = 0; k < hier->numSequences; k++, seq++){ + // Each node has a name and key frames + int32_t seqNameLength; + RwStreamRead(stream, &seqNameLength, sizeof(seqNameLength)); + RwStreamRead(stream, buf, seqNameLength); + seq->SetName(buf); + + int32_t numFrames; + RwStreamRead(stream, &numFrames, sizeof(numFrames)); + seq->numFrames = numFrames; + int32_t boneTag; + RwStreamRead(stream, &boneTag, sizeof(boneTag)); + seq->SetBoneTag(boneTag); + + if(numFrames == 0) + continue; + + uint32_t dataSize; + RwStreamRead(stream, &dataSize, sizeof(dataSize)); + uint16_t flags; + RwStreamRead(stream, &flags, sizeof(flags)); + + seq->keyFrames = RwMalloc(dataSize); + assert(seq->keyFrames); + RwStreamRead(stream, seq->keyFrames, dataSize - sizeof(flags)); + seq->type = flags; + + if(strstr(seq->name, "L Toe")) + debug("anim %s has toe keyframes\n", hier->name); // BUG: seq->name + } + hier->CalcTotalTime(); + } + if(animIndex > ms_numAnimations) + ms_numAnimations = animIndex; +} + +void +CAnimManager::RemoveLastAnimFile(void) +{ + int i; + ms_numAnimBlocks--; + ms_numAnimations = ms_aAnimBlocks[ms_numAnimBlocks].firstIndex; + for(i = 0; i < ms_aAnimBlocks[ms_numAnimBlocks].numAnims; i++) + ms_aAnimations[ms_aAnimBlocks[ms_numAnimBlocks].firstIndex + i].Shutdown(); + ms_aAnimBlocks[ms_numAnimBlocks].isLoaded = false; +} diff --git a/src/miami/animation/AnimManager.h b/src/miami/animation/AnimManager.h new file mode 100644 index 00000000..f1a58b1a --- /dev/null +++ b/src/miami/animation/AnimManager.h @@ -0,0 +1,143 @@ +#pragma once + +#include "AnimBlendHierarchy.h" +#include "AnimationId.h" + +enum AssocGroupId +{ + ASSOCGRP_STD, + ASSOCGRP_VAN, + ASSOCGRP_COACH, + ASSOCGRP_BIKE_STANDARD, + ASSOCGRP_BIKE_VESPA, + ASSOCGRP_BIKE_HARLEY, + ASSOCGRP_BIKE_DIRT, + ASSOCGRP_UNARMED, + ASSOCGRP_SCREWDRIVER, + ASSOCGRP_KNIFE, + ASSOCGRP_BASEBALLBAT, + ASSOCGRP_GOLFCLUB, + ASSOCGRP_CHAINSAW, + ASSOCGRP_PYTHON, + ASSOCGRP_COLT, + ASSOCGRP_SHOTGUN, + ASSOCGRP_BUDDY, + ASSOCGRP_TEC, + ASSOCGRP_UZI, + ASSOCGRP_RIFLE, + ASSOCGRP_M60, + ASSOCGRP_SNIPER, + ASSOCGRP_THROW, + ASSOCGRP_FLAMETHROWER, + ASSOCGRP_MEDIC, + ASSOCGRP_SUNBATHE, + ASSOCGRP_PLAYER_IDLE, + ASSOCGRP_RIOT, + ASSOCGRP_STRIP, + ASSOCGRP_LANCE, + ASSOCGRP_PLAYER, + ASSOCGRP_PLAYERROCKET, + ASSOCGRP_PLAYER1ARMED, + ASSOCGRP_PLAYER2ARMED, + ASSOCGRP_PLAYERBBBAT, + ASSOCGRP_PLAYERCHAINSAW, + ASSOCGRP_SHUFFLE, + ASSOCGRP_OLD, + ASSOCGRP_GANG1, + ASSOCGRP_GANG2, + ASSOCGRP_FAT, + ASSOCGRP_OLDFAT, + ASSOCGRP_JOGGER, + ASSOCGRP_WOMAN, + ASSOCGRP_WOMANSHOP, + ASSOCGRP_BUSYWOMAN, + ASSOCGRP_SEXYWOMAN, + ASSOCGRP_FATWOMAN, + ASSOCGRP_OLDWOMAN, + ASSOCGRP_JOGWOMAN, + ASSOCGRP_PANICCHUNKY, + ASSOCGRP_SKATE, +#ifdef PC_PLAYER_CONTROLS + ASSOCGRP_PLAYERBACK, + ASSOCGRP_PLAYERLEFT, + ASSOCGRP_PLAYERRIGHT, + ASSOCGRP_ROCKETBACK, + ASSOCGRP_ROCKETLEFT, + ASSOCGRP_ROCKETRIGHT, + ASSOCGRP_CHAINSAWBACK, + ASSOCGRP_CHAINSAWLEFT, + ASSOCGRP_CHAINSAWRIGHT, +#endif + + NUM_ANIM_ASSOC_GROUPS +}; + +class CAnimBlendAssociation; +class CAnimBlendAssocGroup; + +#define MAX_ANIMBLOCK_NAME 20 + +// A block of hierarchies +struct CAnimBlock +{ + char name[MAX_ANIMBLOCK_NAME]; + bool isLoaded; + int16 refCount; + int32 firstIndex; // first animtion in ms_aAnimations + int32 numAnims; +}; + +struct AnimAssocDesc +{ + int32 animId; + int32 flags; +}; + +struct AnimAssocDefinition +{ + char const *name; + char const *blockName; + int32 modelIndex; + int32 numAnims; + char const **animNames; + AnimAssocDesc *animDescs; +}; + +class CAnimManager +{ + static const AnimAssocDefinition ms_aAnimAssocDefinitions[NUM_ANIM_ASSOC_GROUPS]; + static CAnimBlock ms_aAnimBlocks[NUMANIMBLOCKS]; + static CAnimBlendHierarchy ms_aAnimations[NUMANIMATIONS]; + static int32 ms_numAnimBlocks; + static int32 ms_numAnimations; + static CAnimBlendAssocGroup *ms_aAnimAssocGroups; + static CLinkList ms_animCache; +public: + + static void Initialise(void); + static void Shutdown(void); + static CAnimBlock *GetAnimationBlock(int32 block) { return &ms_aAnimBlocks[block]; } + static CAnimBlock *GetAnimationBlock(const char *name); + static int32 GetAnimationBlockIndex(const char *name); + static int32 RegisterAnimBlock(const char *name); + static int32 GetNumRefsToAnimBlock(int32 block); + static void AddAnimBlockRef(int32 block); + static void RemoveAnimBlockRefWithoutDelete(int32 block); + static void RemoveAnimBlockRef(int32 block); + static void RemoveAnimBlock(int32 block); + static CAnimBlendHierarchy *GetAnimation(const char *name, CAnimBlock *animBlock); + static CAnimBlendHierarchy *GetAnimation(int32 n) { return &ms_aAnimations[n]; } + static const char *GetAnimGroupName(AssocGroupId groupId); + static CAnimBlendAssociation *CreateAnimAssociation(AssocGroupId groupId, AnimationId animId); + static CAnimBlendAssociation *GetAnimAssociation(AssocGroupId groupId, AnimationId animId); + static CAnimBlendAssociation *GetAnimAssociation(AssocGroupId groupId, const char *name); + static CAnimBlendAssociation *AddAnimation(RpClump *clump, AssocGroupId groupId, AnimationId animId); + static CAnimBlendAssociation *AddAnimationAndSync(RpClump *clump, CAnimBlendAssociation *syncanim, AssocGroupId groupId, AnimationId animId); + static CAnimBlendAssociation *BlendAnimation(RpClump *clump, AssocGroupId groupId, AnimationId animId, float delta); + static void LoadAnimFiles(void); + static void LoadAnimFile(const char *filename); + static void LoadAnimFile(RwStream *stream, bool compress, char (*uncompressedAnims)[32] = nil); + static void CreateAnimAssocGroups(void); + static void RemoveLastAnimFile(void); + static CAnimBlendAssocGroup* GetAnimAssocGroups(void) { return ms_aAnimAssocGroups; } +}; diff --git a/src/miami/animation/AnimationId.h b/src/miami/animation/AnimationId.h new file mode 100644 index 00000000..0b5d8d8e --- /dev/null +++ b/src/miami/animation/AnimationId.h @@ -0,0 +1,287 @@ +#pragma once + +enum AnimationId +{ + ANIM_STD_WALK, + ANIM_STD_RUN, + ANIM_STD_RUNFAST, + ANIM_STD_IDLE, + ANIM_STD_STARTWALK, + ANIM_STD_RUNSTOP1, + ANIM_STD_RUNSTOP2, + ANIM_STD_IDLE_CAM, + ANIM_STD_IDLE_HBHB, + ANIM_STD_IDLE_TIRED, + ANIM_STD_IDLE_BIGGUN, + ANIM_STD_CHAT, + ANIM_STD_HAILTAXI, + ANIM_STD_KO_FRONT, + ANIM_STD_KO_LEFT, + ANIM_STD_KO_BACK, + ANIM_STD_KO_RIGHT, + ANIM_STD_KO_SHOT_FACE, + ANIM_STD_KO_SHOT_STOMACH, + ANIM_STD_KO_SHOT_ARM_L, + ANIM_STD_KO_SHOT_ARM_R, + ANIM_STD_KO_SHOT_LEG_L, + ANIM_STD_KO_SHOT_LEG_R, + ANIM_STD_SPINFORWARD_LEFT, + ANIM_STD_SPINFORWARD_RIGHT, + ANIM_STD_HIGHIMPACT_FRONT, + ANIM_STD_HIGHIMPACT_LEFT, + ANIM_STD_HIGHIMPACT_BACK, + ANIM_STD_HIGHIMPACT_RIGHT, + ANIM_STD_HITBYGUN_FRONT, + ANIM_STD_HITBYGUN_LEFT, + ANIM_STD_HITBYGUN_BACK, + ANIM_STD_HITBYGUN_RIGHT, + ANIM_STD_HIT_FRONT, + ANIM_STD_HIT_LEFT, + ANIM_STD_HIT_BACK, + ANIM_STD_HIT_RIGHT, + ANIM_STD_HIT_FLOOR, + + /* names made up */ + ANIM_STD_HIT_BODYBLOW, + ANIM_STD_HIT_CHEST, + ANIM_STD_HIT_HEAD, + ANIM_STD_HIT_WALK, + /**/ + + ANIM_STD_HIT_WALL, + ANIM_STD_HIT_FLOOR_FRONT, + ANIM_STD_HIT_BEHIND, + ANIM_STD_FIGHT_IDLE, + ANIM_STD_FIGHT_2IDLE, + ANIM_STD_FIGHT_SHUFFLE_F, + + /* names made up */ + ANIM_STD_FIGHT_BODYBLOW, + ANIM_STD_FIGHT_HEAD, + ANIM_STD_FIGHT_KICK, + ANIM_STD_FIGHT_KNEE, + ANIM_STD_FIGHT_LHOOK, + ANIM_STD_FIGHT_PUNCH, + ANIM_STD_FIGHT_ROUNDHOUSE, + ANIM_STD_FIGHT_LONGKICK, + /**/ + + ANIM_STD_PARTIAL_PUNCH, + + /* names made up */ + ANIM_STD_FIGHT_JAB, + ANIM_STD_FIGHT_ELBOW_L, + ANIM_STD_FIGHT_ELBOW_R, + ANIM_STD_FIGHT_BKICK_L, + ANIM_STD_FIGHT_BKICK_R, + /**/ + + ANIM_STD_DETONATE, + ANIM_STD_PUNCH, + ANIM_STD_PARTIALPUNCH, + ANIM_STD_KICKGROUND, + + ANIM_STD_THROW_UNDER, + ANIM_STD_FIGHT_SHUFFLE_B, + + ANIM_STD_JACKEDCAR_RHS, + ANIM_STD_JACKEDCAR_LO_RHS, + ANIM_STD_JACKEDCAR_LHS, + ANIM_STD_JACKEDCAR_LO_LHS, + ANIM_STD_QUICKJACK, + ANIM_STD_QUICKJACKED, + ANIM_STD_CAR_ALIGN_DOOR_LHS, + ANIM_STD_CAR_ALIGNHI_DOOR_LHS, + ANIM_STD_CAR_OPEN_DOOR_LHS, + ANIM_STD_CARDOOR_LOCKED_LHS, + ANIM_STD_CAR_PULL_OUT_PED_LHS, + ANIM_STD_CAR_PULL_OUT_PED_LO_LHS, + ANIM_STD_CAR_GET_IN_LHS, + ANIM_STD_CAR_GET_IN_LO_LHS, + ANIM_STD_CAR_CLOSE_DOOR_LHS, + ANIM_STD_CAR_CLOSE_DOOR_LO_LHS, + ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LHS, + ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LO_LHS, + ANIM_STD_CAR_JUMP_IN_LO_LHS, + ANIM_STD_GETOUT_LHS, + ANIM_STD_GETOUT_LO_LHS, + ANIM_STD_CAR_CLOSE_LHS, + ANIM_STD_CAR_ALIGN_DOOR_RHS, + ANIM_STD_CAR_ALIGNHI_DOOR_RHS, + ANIM_STD_CAR_OPEN_DOOR_RHS, + ANIM_STD_CARDOOR_LOCKED_RHS, + ANIM_STD_CAR_PULL_OUT_PED_RHS, + ANIM_STD_CAR_PULL_OUT_PED_LO_RHS, + ANIM_STD_CAR_GET_IN_RHS, + ANIM_STD_CAR_GET_IN_LO_RHS, + ANIM_STD_CAR_CLOSE_DOOR_RHS, + ANIM_STD_CAR_CLOSE_DOOR_LO_RHS, + ANIM_STD_CAR_SHUFFLE_RHS, + ANIM_STD_CAR_SHUFFLE_LO_RHS, + ANIM_STD_CAR_SIT, + ANIM_STD_CAR_SIT_LO, + ANIM_STD_CAR_SIT_P, + ANIM_STD_CAR_SIT_P_LO, + ANIM_STD_CAR_DRIVE_LEFT, + ANIM_STD_CAR_DRIVE_RIGHT, + ANIM_STD_CAR_DRIVE_LEFT_LO, + ANIM_STD_CAR_DRIVE_RIGHT_LO, + ANIM_STD_CAR_DRIVEBY_LEFT, + ANIM_STD_CAR_DRIVEBY_RIGHT, + ANIM_STD_CAR_DRIVEBY_LEFT_LO, + ANIM_STD_CAR_DRIVEBY_RIGHT_LO, + ANIM_STD_CAR_LOOKBEHIND, + ANIM_STD_BOAT_DRIVE, + ANIM_STD_BOAT_DRIVE_LEFT, + ANIM_STD_BOAT_DRIVE_RIGHT, + ANIM_STD_BOAT_LOOKBEHIND, + + ANIM_STD_BIKE_PICKUP_LHS, + ANIM_STD_BIKE_PICKUP_RHS, + ANIM_STD_BIKE_PULLUP_LHS, + ANIM_STD_BIKE_PULLUP_RHS, + ANIM_STD_BIKE_ELBOW_LHS, + ANIM_STD_BIKE_ELBOW_RHS, + ANIM_STD_BIKE_FALLOFF, + ANIM_STD_BIKE_FALLBACK, + + ANIM_STD_GETOUT_RHS, + ANIM_STD_GETOUT_LO_RHS, + ANIM_STD_CAR_CLOSE_RHS, + ANIM_STD_CAR_HOOKERTALK, + + ANIM_STD_TRAIN_GETIN, + ANIM_STD_TRAIN_GETOUT, + + ANIM_STD_CRAWLOUT_LHS, + ANIM_STD_CRAWLOUT_RHS, + ANIM_STD_ROLLOUT_LHS, + ANIM_STD_ROLLOUT_RHS, + + ANIM_STD_GET_UP, + ANIM_STD_GET_UP_LEFT, + ANIM_STD_GET_UP_RIGHT, + ANIM_STD_GET_UP_FRONT, + ANIM_STD_JUMP_LAUNCH, + ANIM_STD_JUMP_GLIDE, + ANIM_STD_JUMP_LAND, + ANIM_STD_FALL, + ANIM_STD_FALL_GLIDE, + ANIM_STD_FALL_LAND, + ANIM_STD_FALL_COLLAPSE, + ANIM_STD_FALL_ONBACK, + ANIM_STD_FALL_ONFRONT, + + ANIM_STD_EVADE_STEP, + ANIM_STD_EVADE_DIVE, + ANIM_STD_XPRESS_SCRATCH, + ANIM_STD_ROADCROSS, + ANIM_STD_TURN180, + ANIM_STD_ARREST, + ANIM_STD_DROWN, + ANIM_STD_DUCK_DOWN, + ANIM_STD_DUCK_LOW, + + ANIM_STD_DUCK_WEAPON, + + ANIM_STD_RBLOCK_SHOOT, + ANIM_STD_HANDSUP, + ANIM_STD_HANDSCOWER, + ANIM_STD_PARTIAL_FUCKU, + ANIM_STD_PHONE_IN, + ANIM_STD_PHONE_OUT, + ANIM_STD_PHONE_TALK, + + ANIM_STD_SEAT_DOWN, + ANIM_STD_SEAT_UP, + ANIM_STD_SEAT_IDLE, + ANIM_STD_SEAT_RVRS, + ANIM_STD_ATM, + ANIM_STD_ABSEIL, + + ANIM_STD_NUM, + + ANIM_STD_VAN_OPEN_DOOR_REAR_LHS, + ANIM_STD_VAN_GET_IN_REAR_LHS, + ANIM_STD_VAN_CLOSE_DOOR_REAR_LHS, + ANIM_STD_VAN_GET_OUT_REAR_LHS, + ANIM_STD_VAN_OPEN_DOOR_REAR_RHS, + ANIM_STD_VAN_GET_IN_REAR_RHS, + ANIM_STD_VAN_CLOSE_DOOR_REAR_RHS, + ANIM_STD_VAN_GET_OUT_REAR_RHS, + + ANIM_STD_COACH_OPEN_LHS, + ANIM_STD_COACH_OPEN_RHS, + ANIM_STD_COACH_GET_IN_LHS, + ANIM_STD_COACH_GET_IN_RHS, + ANIM_STD_COACH_GET_OUT_LHS, + + ANIM_BIKE_RIDE, + ANIM_BIKE_READY, + ANIM_BIKE_LEFT, + ANIM_BIKE_RIGHT, + ANIM_BIKE_LEANB, + ANIM_BIKE_LEANF, + ANIM_BIKE_WALKBACK, + ANIM_BIKE_JUMPON_LHS, + ANIM_BIKE_JUMPON_RHS, + ANIM_BIKE_KICK, + ANIM_BIKE_HIT, + ANIM_BIKE_GETOFF_LHS, + ANIM_BIKE_GETOFF_RHS, + ANIM_BIKE_GETOFF_BACK, + ANIM_BIKE_DRIVEBY_LHS, + ANIM_BIKE_DRIVEBY_RHS, + ANIM_BIKE_DRIVEBY_FORWARD, + ANIM_BIKE_RIDE_P, + + ANIM_ATTACK_1, + ANIM_ATTACK_2, + ANIM_ATTACK_EXTRA1, + ANIM_ATTACK_EXTRA2, + ANIM_ATTACK_3, + + // our synonyms... because originals are hard to understand + ANIM_WEAPON_FIRE = ANIM_ATTACK_1, + ANIM_WEAPON_CROUCHFIRE, + ANIM_WEAPON_FIRE_2ND = ANIM_WEAPON_CROUCHFIRE, + ANIM_WEAPON_RELOAD, + ANIM_WEAPON_CROUCHRELOAD, + ANIM_WEAPON_FIRE_3RD, + ANIM_THROWABLE_THROW = ANIM_ATTACK_1, + ANIM_THROWABLE_THROWU, + ANIM_THROWABLE_START_THROW, + ANIM_MELEE_ATTACK = ANIM_ATTACK_1, + ANIM_MELEE_ATTACK_2ND, + ANIM_MELEE_ATTACK_START, + ANIM_MELEE_IDLE_FIGHTMODE, + ANIM_MELEE_ATTACK_FINISH, + + ANIM_SUNBATHE_IDLE, + ANIM_SUNBATHE_DOWN, + ANIM_SUNBATHE_UP, + ANIM_SUNBATHE_ESCAPE, + + ANIM_MEDIC_CPR, + + ANIM_PLAYER_IDLE1, + ANIM_PLAYER_IDLE2, + ANIM_PLAYER_IDLE3, + ANIM_PLAYER_IDLE4, + + ANIM_RIOT_ANGRY, + ANIM_RIOT_ANGRY_B, + ANIM_RIOT_CHANT, + ANIM_RIOT_PUNCHES, + ANIM_RIOT_SHOUT, + ANIM_RIOT_CHALLENGE, + ANIM_RIOT_FUCKYOU, + + ANIM_STRIP_A, + ANIM_STRIP_B, + ANIM_STRIP_C, + ANIM_STRIP_D, + ANIM_STRIP_E, + ANIM_STRIP_F, + ANIM_STRIP_G, +}; \ No newline at end of file diff --git a/src/miami/animation/Bones.cpp b/src/miami/animation/Bones.cpp new file mode 100644 index 00000000..87f3b6e7 --- /dev/null +++ b/src/miami/animation/Bones.cpp @@ -0,0 +1,59 @@ +#include "common.h" +#include "PedModelInfo.h" +#include "Bones.h" + +int +ConvertPedNode2BoneTag(int node) +{ + switch(node){ + case PED_MID: return BONE_spine1; + case PED_HEAD: return BONE_head; + case PED_UPPERARML: return BONE_l_upperarm; + case PED_UPPERARMR: return BONE_r_upperarm; + case PED_HANDL: return BONE_l_hand; + case PED_HANDR: return BONE_r_hand; + case PED_UPPERLEGL: return BONE_l_thigh; + case PED_UPPERLEGR: return BONE_r_thigh; + case PED_FOOTL: return BONE_l_foot; + case PED_FOOTR: return BONE_r_foot; + case PED_LOWERLEGR: return BONE_r_calf; + case PED_LOWERLEGL: return BONE_l_calf; + case PED_FOREARML: return BONE_l_forearm; + case PED_FOREARMR: return BONE_r_forearm; + case PED_CLAVICLEL: return BONE_l_clavicle; + case PED_CLAVICLER: return BONE_r_clavicle; + case PED_NECK: return BONE_neck; + } + assert(0 && "this node has no bone"); + return -1; +} + +const char* +ConvertBoneTag2BoneName(int tag) +{ + switch(tag){ + case BONE_root: return "Root"; + case BONE_pelvis: return "Pelvis"; + case BONE_spine: return "Spine"; + case BONE_spine1: return "Spine1"; + case BONE_neck: return "Neck"; + case BONE_head: return "Head"; + case BONE_r_clavicle: return "Bip01 R Clavicle"; + case BONE_r_upperarm: return "R UpperArm"; + case BONE_r_forearm: return "R Forearm"; + case BONE_r_hand: return "R Hand"; + case BONE_r_finger: return "R Fingers"; + case BONE_l_clavicle: return "Bip01 L Clavicle"; + case BONE_l_upperarm: return "L UpperArm"; + case BONE_l_forearm: return "L Forearm"; + case BONE_l_hand: return "L Hand"; + case BONE_l_finger: return "L Fingers"; + case BONE_l_thigh: return "L Thigh"; + case BONE_l_calf: return "L Calf"; + case BONE_l_foot: return "L Foot"; + case BONE_r_thigh: return "R Thigh"; + case BONE_r_calf: return "R Calf"; + case BONE_r_foot: return "R Foot"; + } + return nil; +} diff --git a/src/miami/animation/Bones.h b/src/miami/animation/Bones.h new file mode 100644 index 00000000..e133fd7f --- /dev/null +++ b/src/miami/animation/Bones.h @@ -0,0 +1,30 @@ +#pragma once + +enum BoneTag +{ + BONE_root = 0, + BONE_pelvis = 1, + BONE_spine = 2, + BONE_spine1 = 3, + BONE_neck = 4, + BONE_head = 5, + BONE_l_clavicle = 31, + BONE_l_upperarm = 32, + BONE_l_forearm = 33, + BONE_l_hand = 34, + BONE_l_finger = 35, + BONE_r_clavicle = 21, + BONE_r_upperarm = 22, + BONE_r_forearm = 23, + BONE_r_hand = 24, + BONE_r_finger = 25, + BONE_l_thigh = 41, + BONE_l_calf = 42, + BONE_l_foot = 43, + BONE_r_thigh = 51, + BONE_r_calf = 52, + BONE_r_foot = 53, +}; + +int ConvertPedNode2BoneTag(int node); +const char *ConvertBoneTag2BoneName(int tag); diff --git a/src/miami/animation/CutsceneMgr.cpp b/src/miami/animation/CutsceneMgr.cpp new file mode 100644 index 00000000..b40a8906 --- /dev/null +++ b/src/miami/animation/CutsceneMgr.cpp @@ -0,0 +1,669 @@ +#include "common.h" + +#include "General.h" +#include "CutsceneMgr.h" +#include "Directory.h" +#include "Camera.h" +#include "Streaming.h" +#include "FileMgr.h" +#include "main.h" +#include "AnimManager.h" +#include "AnimBlendAssociation.h" +#include "AnimBlendAssocGroup.h" +#include "AnimBlendClumpData.h" +#include "Pad.h" +#include "DMAudio.h" +#include "World.h" +#include "PlayerPed.h" +#include "Wanted.h" +#include "RpAnimBlend.h" +#include "ModelIndices.h" +#include "TempColModels.h" +#include "ColStore.h" +#include "Radar.h" +#include "Pools.h" + +const struct { + const char *szTrackName; + int iTrackId; +} musicNameIdAssoc[] = { + { "ASS_1", STREAMED_SOUND_CUTSCENE_ASS_1 }, + { "ASS_2", STREAMED_SOUND_CUTSCENE_ASS_2 }, + { "BANK_1", STREAMED_SOUND_CUTSCENE_BANK_1 }, + { "BANK_2A", STREAMED_SOUND_CUTSCENE_BANK_2A }, + { "BANK_2B", STREAMED_SOUND_CUTSCENE_BANK_2B }, + { "BANK_3A", STREAMED_SOUND_CUTSCENE_BANK_3A }, + { "BANK_3B", STREAMED_SOUND_CUTSCENE_BANK_3B }, + { "BANK_4", STREAMED_SOUND_CUTSCENE_BANK_4 }, + { "BIKE_1", STREAMED_SOUND_CUTSCENE_BIKE_1 }, + { "BIKE_2", STREAMED_SOUND_CUTSCENE_BIKE_2 }, + { "BIKE_3", STREAMED_SOUND_CUTSCENE_BIKE_3 }, + { "BUD_1", STREAMED_SOUND_CUTSCENE_BUD_1 }, + { "BUD_2", STREAMED_SOUND_CUTSCENE_BUD_2 }, + { "BUD_3", STREAMED_SOUND_CUTSCENE_BUD_3 }, + { "CAP_1", STREAMED_SOUND_CUTSCENE_CAP_1 }, + { "CAR_1", STREAMED_SOUND_CUTSCENE_CAR_1 }, + { "CNT_1A", STREAMED_SOUND_CUTSCENE_CNT_1A }, + { "CNT_1B", STREAMED_SOUND_CUTSCENE_CNT_1B }, + { "CNT_2", STREAMED_SOUND_CUTSCENE_CNT_2 }, + { "COK_1", STREAMED_SOUND_CUTSCENE_COK_1 }, + { "COK_2A", STREAMED_SOUND_CUTSCENE_COK_2A }, + { "COK_2B", STREAMED_SOUND_CUTSCENE_COK_2B }, + { "COK_3", STREAMED_SOUND_CUTSCENE_COK_3 }, + { "COK_4A", STREAMED_SOUND_CUTSCENE_COK_4A }, + { "COK_4A2", STREAMED_SOUND_CUTSCENE_COK_4A2 }, + { "COK_4B", STREAMED_SOUND_CUTSCENE_COK_4B }, + { "COL_1", STREAMED_SOUND_CUTSCENE_COL_1 }, + { "COL_2", STREAMED_SOUND_CUTSCENE_COL_2 }, + { "COL_3A", STREAMED_SOUND_CUTSCENE_COL_3A }, + { "COL_4A", STREAMED_SOUND_CUTSCENE_COL_4A }, + { "COL_5A", STREAMED_SOUND_CUTSCENE_COL_5A }, + { "COL_5B", STREAMED_SOUND_CUTSCENE_COL_5B }, + { "CUB_1", STREAMED_SOUND_CUTSCENE_CUB_1 }, + { "CUB_2", STREAMED_SOUND_CUTSCENE_CUB_2 }, + { "CUB_3", STREAMED_SOUND_CUTSCENE_CUB_3 }, + { "CUB_4", STREAMED_SOUND_CUTSCENE_CUB_4 }, + { "DRUG_1", STREAMED_SOUND_CUTSCENE_DRUG_1 }, + { "FIN", STREAMED_SOUND_CUTSCENE_FIN }, + { "FIN_2", STREAMED_SOUND_CUTSCENE_FIN2 }, + { "FINALE", STREAMED_SOUND_CUTSCENE_FINALE }, + { "HAT_1", STREAMED_SOUND_CUTSCENE_HAT_1 }, + { "HAT_2", STREAMED_SOUND_CUTSCENE_HAT_2 }, + { "HAT_3", STREAMED_SOUND_CUTSCENE_HAT_3 }, + { "ICE_1", STREAMED_SOUND_CUTSCENE_ICE_1 }, + { "INT_A", STREAMED_SOUND_CUTSCENE_INT_A }, + { "INT_B", STREAMED_SOUND_CUTSCENE_INT_B }, + { "INT_D", STREAMED_SOUND_CUTSCENE_INT_D }, + { "INT_M", STREAMED_SOUND_CUTSCENE_INT_M }, + { "LAW_1A", STREAMED_SOUND_CUTSCENE_LAW_1A }, + { "LAW_1B", STREAMED_SOUND_CUTSCENE_LAW_1B }, + { "LAW_2A", STREAMED_SOUND_CUTSCENE_LAW_2A }, + { "LAW_2B", STREAMED_SOUND_CUTSCENE_LAW_2B }, + { "LAW_2C", STREAMED_SOUND_CUTSCENE_LAW_2C }, + { "LAW_3", STREAMED_SOUND_CUTSCENE_LAW_3 }, + { "LAW_4", STREAMED_SOUND_CUTSCENE_LAW_4 }, + { "PHIL_1", STREAMED_SOUND_CUTSCENE_PHIL_1 }, + { "PHIL_2", STREAMED_SOUND_CUTSCENE_PHIL_2 }, + { "PORN_1", STREAMED_SOUND_CUTSCENE_PORN_1 }, + { "PORN_2", STREAMED_SOUND_CUTSCENE_PORN_2 }, + { "PORN_3", STREAMED_SOUND_CUTSCENE_PORN_3 }, + { "PORN_4", STREAMED_SOUND_CUTSCENE_PORN_4 }, + { "RESC_1A", STREAMED_SOUND_CUTSCENE_RESC_1A }, + { "ROK_1", STREAMED_SOUND_CUTSCENE_ROK_1 }, + { "ROK_2", STREAMED_SOUND_CUTSCENE_ROK_2 }, + { "ROK_3A", STREAMED_SOUND_CUTSCENE_ROK_3A }, + { "STRIPA", STREAMED_SOUND_CUTSCENE_STRIPA }, + { "TAX_1", STREAMED_SOUND_CUTSCENE_TAX_1 }, + { "TEX_1", STREAMED_SOUND_CUTSCENE_TEX_1 }, + { "TEX_2", STREAMED_SOUND_CUTSCENE_TEX_2 }, + { "TEX_3", STREAMED_SOUND_CUTSCENE_TEX_3 }, + { "GSPOT", STREAMED_SOUND_CUTSCENE_GLIGHT }, + { "FIST", STREAMED_SOUND_CUTSCENE_FIST }, + { "EL_PH1", STREAMED_SOUND_CUTSCENE_ELBURRO1_PH1 }, + { "EL_PH2", STREAMED_SOUND_CUTSCENE_ELBURRO2_PH2 }, + { NULL, 0 } +}; + +int +FindCutsceneAudioTrackId(const char *szCutsceneName) +{ + for (int i = 0; musicNameIdAssoc[i].szTrackName; i++) { + if (!CGeneral::faststricmp(musicNameIdAssoc[i].szTrackName, szCutsceneName)) + return musicNameIdAssoc[i].iTrackId; + } + return -1; +} + +bool CCutsceneMgr::ms_running; +bool CCutsceneMgr::ms_cutsceneProcessing; +CDirectory *CCutsceneMgr::ms_pCutsceneDir; +CCutsceneObject *CCutsceneMgr::ms_pCutsceneObjects[NUMCUTSCENEOBJECTS]; +int32 CCutsceneMgr::ms_numCutsceneObjs; +bool CCutsceneMgr::ms_loaded; +bool CCutsceneMgr::ms_animLoaded; +bool CCutsceneMgr::ms_useLodMultiplier; +char CCutsceneMgr::ms_cutsceneName[CUTSCENENAMESIZE]; +CAnimBlendAssocGroup CCutsceneMgr::ms_cutsceneAssociations; +CVector CCutsceneMgr::ms_cutsceneOffset; +float CCutsceneMgr::ms_cutsceneTimer; +bool CCutsceneMgr::ms_wasCutsceneSkipped; +uint32 CCutsceneMgr::ms_cutsceneLoadStatus; +bool CCutsceneMgr::ms_useCutsceneShadows = true; + +bool bCamLoaded; +bool bIsEverythingRemovedFromTheWorldForTheBiggestFuckoffCutsceneEver; // pls don't shrink the name :P +int32 NumberOfSavedWeapons; +eWeaponType SavedWeaponIDs[TOTAL_WEAPON_SLOTS]; +int32 SavedWeaponAmmo[TOTAL_WEAPON_SLOTS]; +char uncompressedAnims[8][32]; +uint32 numUncompressedAnims; + + +RpAtomic * +CalculateBoundingSphereRadiusCB(RpAtomic *atomic, void *data) +{ + float radius = RpAtomicGetBoundingSphere(atomic)->radius; + RwV3d center = RpAtomicGetBoundingSphere(atomic)->center; + + for (RwFrame *frame = RpAtomicGetFrame(atomic); RwFrameGetParent(frame); frame = RwFrameGetParent(frame)) + RwV3dTransformPoints(¢er, ¢er, 1, RwFrameGetMatrix(frame)); + + float size = RwV3dLength(¢er) + radius; + if (size > *(float *)data) + *(float *)data = size; + return atomic; +} + +void +CCutsceneMgr::Initialise(void) +{ + ms_numCutsceneObjs = 0; + ms_loaded = false; + ms_wasCutsceneSkipped = false; + ms_running = false; + ms_useLodMultiplier = false; + ms_animLoaded = false; + ms_cutsceneProcessing = false; + + ms_pCutsceneDir = new CDirectory(CUTSCENEDIRSIZE); + ms_pCutsceneDir->ReadDirFile("ANIM\\CUTS.DIR"); + + numUncompressedAnims = 0; + uncompressedAnims[0][0] = '\0'; +} + +void +CCutsceneMgr::Shutdown(void) +{ + delete ms_pCutsceneDir; +} + +void +CCutsceneMgr::LoadCutsceneData(const char *szCutsceneName) +{ + int file; + uint32 size; + uint32 offset; + CPlayerPed *pPlayerPed; + + ms_cutsceneProcessing = true; + ms_wasCutsceneSkipped = false; + CTimer::Suspend(); + if (!bIsEverythingRemovedFromTheWorldForTheBiggestFuckoffCutsceneEver) + CStreaming::RemoveCurrentZonesModels(); + + ms_pCutsceneDir->numEntries = 0; + ms_pCutsceneDir->ReadDirFile("ANIM\\CUTS.DIR"); + + CStreaming::RemoveUnusedModelsInLoadedList(); + CGame::DrasticTidyUpMemory(true); + + strcpy(ms_cutsceneName, szCutsceneName); + + RwStream *stream; + stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, "ANIM\\CUTS.IMG"); + assert(stream); + + // Load animations + sprintf(gString, "%s.IFP", szCutsceneName); + if (ms_pCutsceneDir->FindItem(gString, offset, size)) { + CStreaming::MakeSpaceFor(size << 11); + CStreaming::ImGonnaUseStreamingMemory(); + RwStreamSkip(stream, offset << 11); + CAnimManager::LoadAnimFile(stream, true, uncompressedAnims); + ms_cutsceneAssociations.CreateAssociations(szCutsceneName); + CStreaming::IHaveUsedStreamingMemory(); + ms_animLoaded = true; + } else { + ms_animLoaded = false; + } + RwStreamClose(stream, nil); + + // Load camera data + file = CFileMgr::OpenFile("ANIM\\CUTS.IMG", "rb"); + sprintf(gString, "%s.DAT", szCutsceneName); + if (ms_pCutsceneDir->FindItem(gString, offset, size)) { + CStreaming::ImGonnaUseStreamingMemory(); + CFileMgr::Seek(file, offset << 11, SEEK_SET); + TheCamera.LoadPathSplines(file); + CStreaming::IHaveUsedStreamingMemory(); + bCamLoaded = true; + } else { + bCamLoaded = false; + } + + CFileMgr::CloseFile(file); + + if (CGeneral::faststricmp(ms_cutsceneName, "finale")) { + DMAudio.ChangeMusicMode(MUSICMODE_CUTSCENE); + int trackId = FindCutsceneAudioTrackId(szCutsceneName); + if (trackId != -1) { + printf("Start preload audio %s\n", szCutsceneName); + DMAudio.PreloadCutSceneMusic(trackId); + printf("End preload audio %s\n", szCutsceneName); + } + } + + ms_cutsceneTimer = 0.0f; + ms_loaded = true; + ms_cutsceneOffset = CVector(0.0f, 0.0f, 0.0f); + + pPlayerPed = FindPlayerPed(); + pPlayerPed->m_pWanted->ClearQdCrimes(); + pPlayerPed->bIsVisible = false; + pPlayerPed->m_fCurrentStamina = pPlayerPed->m_fMaxStamina; + CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_CUTSCENE); + CWorld::Players[CWorld::PlayerInFocus].MakePlayerSafe(true); + + CTimer::Resume(); +} + +void +CCutsceneMgr::FinishCutscene() +{ + ms_wasCutsceneSkipped = true; + if (bCamLoaded) { + CCutsceneMgr::ms_cutsceneTimer = TheCamera.GetCutSceneFinishTime() * 0.001f; + TheCamera.FinishCutscene(); + } + + FindPlayerPed()->bIsVisible = true; + CWorld::Players[CWorld::PlayerInFocus].MakePlayerSafe(false); +} + +void +CCutsceneMgr::SetupCutsceneToStart(void) +{ + if (bCamLoaded) { + TheCamera.SetCamCutSceneOffSet(ms_cutsceneOffset); + TheCamera.TakeControlWithSpline(JUMP_CUT); + TheCamera.SetWideScreenOn(); + } + + ms_cutsceneOffset.z++; + + for (int i = ms_numCutsceneObjs - 1; i >= 0; i--) { + assert(RwObjectGetType(ms_pCutsceneObjects[i]->m_rwObject) == rpCLUMP); + if (CAnimBlendAssociation *pAnimBlendAssoc = RpAnimBlendClumpGetFirstAssociation((RpClump*)ms_pCutsceneObjects[i]->m_rwObject)) { + assert(pAnimBlendAssoc->hierarchy->sequences[0].HasTranslation()); + if (ms_pCutsceneObjects[i]->m_pAttachTo != nil) { + pAnimBlendAssoc->flags &= (~ASSOC_HAS_TRANSLATION); + } else { + ms_pCutsceneObjects[i]->SetPosition(ms_cutsceneOffset + pAnimBlendAssoc->hierarchy->sequences[0].GetStartTranslation()); + } + pAnimBlendAssoc->SetRun(); + } else { + ms_pCutsceneObjects[i]->SetPosition(ms_cutsceneOffset); + } + CWorld::Add(ms_pCutsceneObjects[i]); + if (RwObjectGetType(ms_pCutsceneObjects[i]->m_rwObject) == rpCLUMP) { + ms_pCutsceneObjects[i]->UpdateRpHAnim(); + } + } + + CTimer::Update(); + CTimer::Update(); + ms_running = true; + ms_cutsceneTimer = 0.0f; +} + +void +CCutsceneMgr::SetCutsceneAnim(const char *animName, CObject *pObject) +{ + CAnimBlendAssociation *pNewAnim; + CAnimBlendClumpData *pAnimBlendClumpData; + + assert(RwObjectGetType(pObject->m_rwObject) == rpCLUMP); + debug("Give cutscene anim %s\n", animName); + RpAnimBlendClumpRemoveAllAssociations((RpClump*)pObject->m_rwObject); + + pNewAnim = ms_cutsceneAssociations.GetAnimation(animName); + if (!pNewAnim) { + debug("\n\nHaven't I told you I can't find the fucking animation %s\n\n\n", animName); + return; + } + + CStreaming::ImGonnaUseStreamingMemory(); + pNewAnim = ms_cutsceneAssociations.CopyAnimation(animName); + CStreaming::IHaveUsedStreamingMemory(); + + pNewAnim->SetCurrentTime(0.0f); + pNewAnim->flags |= ASSOC_HAS_TRANSLATION; + pNewAnim->flags &= ~ASSOC_RUNNING; + + pAnimBlendClumpData = *RPANIMBLENDCLUMPDATA(pObject->m_rwObject); + pAnimBlendClumpData->link.Prepend(&pNewAnim->link); +} + +void +CCutsceneMgr::SetCutsceneAnimToLoop(const char* animName) +{ + ms_cutsceneAssociations.GetAnimation(animName)->flags |= ASSOC_REPEAT; +} + +CCutsceneHead * +CCutsceneMgr::AddCutsceneHead(CObject *pObject, int modelId) +{ + return nil; +} + +void UpdateCutsceneObjectBoundingBox(RpClump* clump, int modelId) +{ + if (modelId >= MI_CUTOBJ01 && modelId <= MI_CUTOBJ05) { + CColModel* pColModel = &CTempColModels::ms_colModelCutObj[modelId - MI_CUTOBJ01]; + float radius = 0.0f; + RpClumpForAllAtomics(clump, CalculateBoundingSphereRadiusCB, &radius); + pColModel->boundingSphere.radius = radius; + pColModel->boundingBox.min = CVector(-radius, -radius, -radius); + pColModel->boundingBox.max = CVector(radius, radius, radius); + } +} + +CCutsceneObject * +CCutsceneMgr::CreateCutsceneObject(int modelId) +{ + CBaseModelInfo *pModelInfo; + CColModel *pColModel; + CCutsceneObject *pCutsceneObject; + + CStreaming::ImGonnaUseStreamingMemory(); + debug("Created cutscene object %s\n", CModelInfo::GetModelInfo(modelId)->GetModelName()); + if (modelId >= MI_CUTOBJ01 && modelId <= MI_CUTOBJ05) { + pModelInfo = CModelInfo::GetModelInfo(modelId); + pColModel = &CTempColModels::ms_colModelCutObj[modelId - MI_CUTOBJ01]; + pModelInfo->SetColModel(pColModel); + UpdateCutsceneObjectBoundingBox((RpClump*)pModelInfo->GetRwObject(), modelId); + } else if (modelId >= MI_SPECIAL01 && modelId <= MI_SPECIAL21) { + pModelInfo = CModelInfo::GetModelInfo(modelId); + if (pModelInfo->GetColModel() == &CTempColModels::ms_colModelPed1) { + CColModel *colModel = new CColModel(); + colModel->boundingSphere.radius = 2.0f; + colModel->boundingSphere.center = CVector(0.0f, 0.0f, 0.0f); + pModelInfo->SetColModel(colModel, true); + } + pColModel = pModelInfo->GetColModel(); + float radius = 2.0f; + pColModel->boundingSphere.radius = radius; + pColModel->boundingBox.min = CVector(-radius, -radius, -radius); + pColModel->boundingBox.max = CVector(radius, radius, radius); + } + + pCutsceneObject = new CCutsceneObject(); + pCutsceneObject->SetModelIndex(modelId); + if (ms_useCutsceneShadows) + pCutsceneObject->CreateShadow(); + ms_pCutsceneObjects[ms_numCutsceneObjs++] = pCutsceneObject; + CStreaming::IHaveUsedStreamingMemory(); + return pCutsceneObject; +} + +void +CCutsceneMgr::DeleteCutsceneData(void) +{ + if (!ms_loaded) return; + CTimer::Suspend(); + + ms_cutsceneProcessing = false; + ms_useLodMultiplier = false; + ms_useCutsceneShadows = true; + + for (--ms_numCutsceneObjs; ms_numCutsceneObjs >= 0; ms_numCutsceneObjs--) { + CWorld::Remove(ms_pCutsceneObjects[ms_numCutsceneObjs]); + ms_pCutsceneObjects[ms_numCutsceneObjs]->DeleteRwObject(); + delete ms_pCutsceneObjects[ms_numCutsceneObjs]; + ms_pCutsceneObjects[ms_numCutsceneObjs] = nil; + } + ms_numCutsceneObjs = 0; + + for (int i = MI_SPECIAL01; i < MI_SPECIAL21; i++) { + CBaseModelInfo *minfo = CModelInfo::GetModelInfo(i); + CColModel *colModel = minfo->GetColModel(); + if (colModel != &CTempColModels::ms_colModelPed1) { + delete colModel; + minfo->SetColModel(&CTempColModels::ms_colModelPed1); + } + } + + if (ms_animLoaded) + CAnimManager::RemoveLastAnimFile(); + + ms_animLoaded = false; + numUncompressedAnims = 0; + uncompressedAnims[0][0] = '\0'; + + if (bCamLoaded) { + TheCamera.RestoreWithJumpCut(); + TheCamera.SetWideScreenOff(); + TheCamera.DeleteCutSceneCamDataMemory(); + } + ms_running = false; + ms_loaded = false; + + FindPlayerPed()->bIsVisible = true; + CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_CUTSCENE); + CWorld::Players[CWorld::PlayerInFocus].MakePlayerSafe(false); + + if (CGeneral::faststricmp(ms_cutsceneName, "finale")) { + DMAudio.StopCutSceneMusic(); + DMAudio.ChangeMusicMode(MUSICMODE_GAME); + } + + CStreaming::ms_disableStreaming = false; + CWorld::bProcessCutsceneOnly = false; + + if(bCamLoaded) + CGame::DrasticTidyUpMemory(TheCamera.GetScreenFadeStatus() == FADE_2); + + CPad::GetPad(0)->Clear(false); + if (bIsEverythingRemovedFromTheWorldForTheBiggestFuckoffCutsceneEver) { + CStreaming::LoadInitialPeds(); + CStreaming::LoadInitialWeapons(); + CStreaming::LoadInitialVehicles(); + bIsEverythingRemovedFromTheWorldForTheBiggestFuckoffCutsceneEver = false; + + CPlayerPed *pPlayerPed = FindPlayerPed(); + for (int i = 0; i < NumberOfSavedWeapons; i++) { + int32 weaponModelId = CWeaponInfo::GetWeaponInfo(SavedWeaponIDs[i])->m_nModelId; + uint8 flags = CStreaming::ms_aInfoForModel[weaponModelId].m_flags; + CStreaming::RequestModel(weaponModelId, STREAMFLAGS_DONT_REMOVE); + CStreaming::LoadAllRequestedModels(false); + if (CWeaponInfo::GetWeaponInfo(SavedWeaponIDs[i])->m_nModel2Id != -1) { + CStreaming::RequestModel(CWeaponInfo::GetWeaponInfo(SavedWeaponIDs[i])->m_nModel2Id, 0); + CStreaming::LoadAllRequestedModels(false); + } + if (!(flags & STREAMFLAGS_DONT_REMOVE)) + CStreaming::SetModelIsDeletable(weaponModelId); + pPlayerPed->GiveWeapon(SavedWeaponIDs[i], SavedWeaponAmmo[i], true); + } + NumberOfSavedWeapons = 0; + } + + CTimer::Resume(); +} + +void +CCutsceneMgr::Update(void) +{ + enum { + CUTSCENE_LOADING_0 = 0, + CUTSCENE_LOADING_AUDIO, + CUTSCENE_LOADING_2, + CUTSCENE_LOADING_3, + CUTSCENE_LOADING_4 + }; + + switch (ms_cutsceneLoadStatus) { + case CUTSCENE_LOADING_AUDIO: + SetupCutsceneToStart(); + if (CGeneral::faststricmp(ms_cutsceneName, "finale")) + DMAudio.PlayPreloadedCutSceneMusic(); + ms_cutsceneLoadStatus++; + break; + case CUTSCENE_LOADING_2: + case CUTSCENE_LOADING_3: + ms_cutsceneLoadStatus++; + break; + case CUTSCENE_LOADING_4: + ms_cutsceneLoadStatus = CUTSCENE_LOADING_0; + break; + default: + break; + } + + if (!ms_running) return; + + ms_cutsceneTimer += CTimer::GetTimeStepNonClippedInSeconds(); + + for (int i = 0; i < ms_numCutsceneObjs; i++) { + int modelId = ms_pCutsceneObjects[i]->GetModelIndex(); + if (modelId >= MI_CUTOBJ01 && modelId <= MI_CUTOBJ05) + UpdateCutsceneObjectBoundingBox(ms_pCutsceneObjects[i]->GetClump(), modelId); + + if (ms_pCutsceneObjects[i]->m_pAttachTo != nil && modelId >= MI_SPECIAL01 && modelId <= MI_SPECIAL21) + UpdateCutsceneObjectBoundingBox(ms_pCutsceneObjects[i]->GetClump(), modelId); + } + + if (bCamLoaded) + if (CGeneral::faststricmp(ms_cutsceneName, "finale") && TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_FLYBY && ms_cutsceneLoadStatus == CUTSCENE_LOADING_0) { + if (CPad::GetPad(0)->GetCrossJustDown() + || (CGame::playingIntro && CPad::GetPad(0)->GetStartJustDown()) + || CPad::GetPad(0)->GetLeftMouseJustDown() + || CPad::GetPad(0)->GetEnterJustDown() + || CPad::GetPad(0)->GetCharJustDown(' ')) + FinishCutscene(); + } +} + +bool CCutsceneMgr::HasCutsceneFinished(void) { return !bCamLoaded || TheCamera.GetPositionAlongSpline() == 1.0f; } + +void +CCutsceneMgr::LoadAnimationUncompressed(char const* name) +{ + strcpy(uncompressedAnims[numUncompressedAnims], name); + + // Because that's how CAnimManager knows the end of array + ++numUncompressedAnims; + assert(numUncompressedAnims < ARRAY_SIZE(uncompressedAnims)); + uncompressedAnims[numUncompressedAnims][0] = '\0'; +} + +void +CCutsceneMgr::AttachObjectToParent(CObject *pObject, CEntity *pAttachTo) +{ + ((CCutsceneObject*)pObject)->m_pAttachmentObject = nil; + ((CCutsceneObject*)pObject)->m_pAttachTo = RpClumpGetFrame(pAttachTo->GetClump()); + + debug("Attach %s to %s\n", CModelInfo::GetModelInfo(pObject->GetModelIndex())->GetModelName(), CModelInfo::GetModelInfo(pAttachTo->GetModelIndex())->GetModelName()); +} + +void +CCutsceneMgr::AttachObjectToFrame(CObject *pObject, CEntity *pAttachTo, const char *frame) +{ + ((CCutsceneObject*)pObject)->m_pAttachmentObject = nil; + ((CCutsceneObject*)pObject)->m_pAttachTo = RpAnimBlendClumpFindFrame(pAttachTo->GetClump(), frame)->frame; + debug("Attach %s to component %s of %s\n", + CModelInfo::GetModelInfo(pObject->GetModelIndex())->GetModelName(), + frame, + CModelInfo::GetModelInfo(pAttachTo->GetModelIndex())->GetModelName()); + if (RwObjectGetType(pObject->m_rwObject) == rpCLUMP) { + RpClump *clump = (RpClump*)pObject->m_rwObject; + if (IsClumpSkinned(clump)) + RpAtomicGetBoundingSphere(GetFirstAtomic(clump))->radius *= 1.1f; + } +} + +void +CCutsceneMgr::AttachObjectToBone(CObject *pObject, CObject *pAttachTo, int bone) +{ + RpHAnimHierarchy *hanim = GetAnimHierarchyFromSkinClump(pAttachTo->GetClump()); + RwInt32 id = RpHAnimIDGetIndex(hanim, bone); + RwMatrix *matrixArray = RpHAnimHierarchyGetMatrixArray(hanim); + ((CCutsceneObject*)pObject)->m_pAttachmentObject = pAttachTo; + ((CCutsceneObject*)pObject)->m_pAttachTo = &matrixArray[id]; + debug("Attach %s to %s\n", + CModelInfo::GetModelInfo(pObject->GetModelIndex())->GetModelName(), + CModelInfo::GetModelInfo(pAttachTo->GetModelIndex())->GetModelName()); +} + +void +CCutsceneMgr::RemoveEverythingFromTheWorldForTheBiggestFuckoffCutsceneEver() +{ + CStreaming::ms_disableStreaming = true; + CColStore::RemoveAllCollision(); + CWorld::bProcessCutsceneOnly = true; + ms_cutsceneProcessing = true; + + for (int i = CPools::GetPedPool()->GetSize() - 1; i >= 0; i--) { + CPed *pPed = CPools::GetPedPool()->GetSlot(i); + if (pPed) { + if (!pPed->IsPlayer() && pPed->CanBeDeleted()) { + CWorld::Remove(pPed); + delete pPed; + } + } + } + + for (int i = CPools::GetVehiclePool()->GetSize() - 1; i >= 0; i--) { + CVehicle *pVehicle = CPools::GetVehiclePool()->GetSlot(i); + if (pVehicle) { + if (pVehicle->CanBeDeleted()) { + CWorld::Remove(pVehicle); + delete pVehicle; + } + } + } + + bIsEverythingRemovedFromTheWorldForTheBiggestFuckoffCutsceneEver = true; + CStreaming::RemoveCurrentZonesModels(); + CStreaming::SetModelIsDeletable(MI_MALE01); + CStreaming::SetModelTxdIsDeletable(MI_MALE01); + CStreaming::SetModelIsDeletable(MI_TAXI_D); + CStreaming::SetModelTxdIsDeletable(MI_TAXI_D); + CStreaming::SetModelIsDeletable(MI_NIGHTSTICK); + CStreaming::SetModelTxdIsDeletable(MI_NIGHTSTICK); + CStreaming::SetModelIsDeletable(MI_MISSILE); + CStreaming::SetModelTxdIsDeletable(MI_MISSILE); + CStreaming::SetModelIsDeletable(MI_POLICE); + CStreaming::SetModelTxdIsDeletable(MI_POLICE); + + while (CStreaming::RemoveLoadedVehicle()) ; + + CRadar::RemoveRadarSections(); + + for (int i = CPools::GetDummyPool()->GetSize() - 1; i >= 0; i--) { + CDummy* pDummy = CPools::GetDummyPool()->GetSlot(i); + if (pDummy) + pDummy->DeleteRwObject(); + } + + for (int i = CPools::GetObjectPool()->GetSize() - 1; i >= 0; i--) { + CObject* pObject = CPools::GetObjectPool()->GetSlot(i); + if (pObject) + pObject->DeleteRwObject(); + } + + for (int i = CPools::GetBuildingPool()->GetSize() - 1; i >= 0; i--) { + CBuilding* pBuilding = CPools::GetBuildingPool()->GetSlot(i); + if (pBuilding && pBuilding->m_rwObject != nil && pBuilding->bIsBIGBuilding && pBuilding->bStreamBIGBuilding) { + if (pBuilding->bIsBIGBuilding) + CStreaming::RequestModel(pBuilding->GetModelIndex(), 0); + if (!pBuilding->bImBeingRendered) + pBuilding->DeleteRwObject(); + } + } + + CPlayerPed *pPlayerPed = FindPlayerPed(); + pPlayerPed->RemoveWeaponAnims(0, -1000.0f); + NumberOfSavedWeapons = 0; + + for (int i = 0; i < TOTAL_WEAPON_SLOTS; i++) { + if (pPlayerPed->m_weapons[i].m_eWeaponType != WEAPONTYPE_UNARMED) { + SavedWeaponIDs[NumberOfSavedWeapons] = pPlayerPed->m_weapons[i].m_eWeaponType; + SavedWeaponAmmo[NumberOfSavedWeapons] = pPlayerPed->m_weapons[i].m_nAmmoTotal; + NumberOfSavedWeapons++; + } + } + + pPlayerPed->ClearWeapons(); + CGame::DrasticTidyUpMemory(true); +} \ No newline at end of file diff --git a/src/miami/animation/CutsceneMgr.h b/src/miami/animation/CutsceneMgr.h new file mode 100644 index 00000000..51ef6c04 --- /dev/null +++ b/src/miami/animation/CutsceneMgr.h @@ -0,0 +1,61 @@ +#pragma once +#include "CutsceneObject.h" + +#define CUTSCENENAMESIZE 8 + +class CDirectory; +class CAnimBlendAssocGroup; +class CCutsceneHead; + +class CCutsceneMgr +{ + static bool ms_running; + static CCutsceneObject *ms_pCutsceneObjects[NUMCUTSCENEOBJECTS]; + + static int32 ms_numCutsceneObjs; + static bool ms_loaded; + static bool ms_animLoaded; + static bool ms_useLodMultiplier; + + static char ms_cutsceneName[CUTSCENENAMESIZE]; + static CAnimBlendAssocGroup ms_cutsceneAssociations; + static CVector ms_cutsceneOffset; + static float ms_cutsceneTimer; + static bool ms_wasCutsceneSkipped; + static bool ms_cutsceneProcessing; + static bool ms_useCutsceneShadows; +public: + static CDirectory *ms_pCutsceneDir; + static uint32 ms_cutsceneLoadStatus; + + static void StartCutsceneProcessing() { ms_cutsceneProcessing = true; } + static bool IsRunning(void) { return ms_running; } + static bool HasLoaded(void) { return ms_loaded; } + static bool IsCutsceneProcessing(void) { return ms_cutsceneProcessing; } + static bool WasCutsceneSkipped(void) { return ms_wasCutsceneSkipped; } + static bool UseLodMultiplier(void) { return ms_useLodMultiplier; } + static CCutsceneObject* GetCutsceneObject(int id) { return ms_pCutsceneObjects[id]; } + static int GetCutsceneTimeInMilleseconds(void) { return 1000.0f * ms_cutsceneTimer; } + static char *GetCutsceneName(void) { return ms_cutsceneName; } + static void SetCutsceneOffset(const CVector& vec) { ms_cutsceneOffset = vec; } + static bool HasCutsceneFinished(void); + + static void Initialise(void); + static void Shutdown(void); + static void LoadCutsceneData(const char *szCutsceneName); + static void FinishCutscene(void); + static void SetupCutsceneToStart(void); + static void SetCutsceneAnim(const char *animName, CObject *pObject); + static void SetCutsceneAnimToLoop(const char *animName); + static CCutsceneHead *AddCutsceneHead(CObject *pObject, int modelId); + static CCutsceneObject *CreateCutsceneObject(int modelId); + static void DeleteCutsceneData(void); + static void LoadAnimationUncompressed(char const*); + static void Update(void); + + static void AttachObjectToParent(CObject *pObject, CEntity *pAttachTo); + static void AttachObjectToFrame(CObject *pObject, CEntity *pAttachTo, const char *frame); + static void AttachObjectToBone(CObject *pObject, CObject *pAttachTo, int frame); + static void RemoveEverythingFromTheWorldForTheBiggestFuckoffCutsceneEver(); + static void DisableCutsceneShadows() { ms_useCutsceneShadows = false; } +}; diff --git a/src/miami/animation/FrameUpdate.cpp b/src/miami/animation/FrameUpdate.cpp new file mode 100644 index 00000000..727f2692 --- /dev/null +++ b/src/miami/animation/FrameUpdate.cpp @@ -0,0 +1,446 @@ +#include "common.h" + +#include "NodeName.h" +#include "VisibilityPlugins.h" +#include "AnimBlendClumpData.h" +#include "AnimBlendAssociation.h" +#include "RpAnimBlend.h" + +CAnimBlendClumpData *gpAnimBlendClump; + +// PS2 names without "NonSkinned" +void FrameUpdateCallBackNonSkinned(AnimBlendFrameData *frame, void *arg); +void FrameUpdateCallBackWithVelocityExtractionNonSkinned(AnimBlendFrameData *frame, void *arg); +void FrameUpdateCallBackWith3dVelocityExtractionNonSkinned(AnimBlendFrameData *frame, void *arg); + +void FrameUpdateCallBackSkinned(AnimBlendFrameData *frame, void *arg); +void FrameUpdateCallBackWithVelocityExtractionSkinned(AnimBlendFrameData *frame, void *arg); +void FrameUpdateCallBackWith3dVelocityExtractionSkinned(AnimBlendFrameData *frame, void *arg); + +void +FrameUpdateCallBackNonSkinned(AnimBlendFrameData *frame, void *arg) +{ + CVector vec, pos(0.0f, 0.0f, 0.0f); + CQuaternion q, rot(0.0f, 0.0f, 0.0f, 0.0f); + float totalBlendAmount = 0.0f; + RwMatrix *mat = RwFrameGetMatrix(frame->frame); + CAnimBlendNode **node; + AnimBlendFrameUpdateData *updateData = (AnimBlendFrameUpdateData*)arg; + + if(frame->flag & AnimBlendFrameData::VELOCITY_EXTRACTION && + gpAnimBlendClump->velocity2d){ + if(frame->flag & AnimBlendFrameData::VELOCITY_EXTRACTION_3D) + FrameUpdateCallBackWith3dVelocityExtractionNonSkinned(frame, arg); + else + FrameUpdateCallBackWithVelocityExtractionNonSkinned(frame, arg); + return; + } + + if(updateData->foobar) + for(node = updateData->nodes; *node; node++) + if((*node)->sequence && (*node)->association->IsPartial()) + totalBlendAmount += (*node)->association->blendAmount; + + for(node = updateData->nodes; *node; node++){ + if((*node)->sequence){ + (*node)->Update(vec, q, 1.0f-totalBlendAmount); + if((*node)->sequence->HasTranslation()) + pos += vec; +#ifdef FIX_BUGS + if(DotProduct(rot, q) < 0.0f) + rot -= q; + else +#endif + rot += q; + } + ++*node; + } + + if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){ + RwMatrixSetIdentity(mat); + rot.Normalise(); + rot.Get(mat); + } + + if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){ + mat->pos.x = pos.x; + mat->pos.y = pos.y; + mat->pos.z = pos.z; + mat->pos.x += frame->resetPos.x; + mat->pos.y += frame->resetPos.y; + mat->pos.z += frame->resetPos.z; + } + RwMatrixUpdate(mat); +} + +void +FrameUpdateCallBackWithVelocityExtractionNonSkinned(AnimBlendFrameData *frame, void *arg) +{ + CVector vec, pos(0.0f, 0.0f, 0.0f); + CQuaternion q, rot(0.0f, 0.0f, 0.0f, 0.0f); + float totalBlendAmount = 0.0f; + float transx = 0.0f, transy = 0.0f; + float curx = 0.0f, cury = 0.0f; + float endx = 0.0f, endy = 0.0f; + bool looped = false; + RwMatrix *mat = RwFrameGetMatrix(frame->frame); + CAnimBlendNode **node; + AnimBlendFrameUpdateData *updateData = (AnimBlendFrameUpdateData*)arg; + + if(updateData->foobar) + for(node = updateData->nodes; *node; node++) + if((*node)->sequence && (*node)->association->IsPartial()) + totalBlendAmount += (*node)->association->blendAmount; + + for(node = updateData->nodes; *node; node++) + if((*node)->sequence && (*node)->sequence->HasTranslation()){ + if((*node)->association->HasTranslation()){ + (*node)->GetCurrentTranslation(vec, 1.0f-totalBlendAmount); + cury += vec.y; + if((*node)->association->HasXTranslation()) + curx += vec.x; + } + } + + for(node = updateData->nodes; *node; node++){ + if((*node)->sequence){ + bool nodelooped = (*node)->Update(vec, q, 1.0f-totalBlendAmount); +#ifdef FIX_BUGS + if(DotProduct(rot, q) < 0.0f) + rot -= q; + else +#endif + rot += q; + if((*node)->sequence->HasTranslation()){ + pos += vec; + if((*node)->association->HasTranslation()){ + transy += vec.y; + if((*node)->association->HasXTranslation()) + transx += vec.x; + looped |= nodelooped; + if(nodelooped){ + (*node)->GetEndTranslation(vec, 1.0f-totalBlendAmount); + endy += vec.y; + if((*node)->association->HasXTranslation()) + endx += vec.x; + } + } + } + } + ++*node; + } + + if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){ + RwMatrixSetIdentity(mat); + rot.Normalise(); + rot.Get(mat); + } + + if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){ + gpAnimBlendClump->velocity2d->x = transx - curx; + gpAnimBlendClump->velocity2d->y = transy - cury; + if(looped){ + gpAnimBlendClump->velocity2d->x += endx; + gpAnimBlendClump->velocity2d->y += endy; + } + mat->pos.x = pos.x - transx; + mat->pos.y = pos.y - transy; + mat->pos.z = pos.z; + if(mat->pos.z >= -0.8f) { + if(mat->pos.z < -0.4f) + mat->pos.z += (2.5f * mat->pos.z + 2.0f) * frame->resetPos.z; + else + mat->pos.z += frame->resetPos.z; + } + mat->pos.x += frame->resetPos.x; + mat->pos.y += frame->resetPos.y; + } + RwMatrixUpdate(mat); +} + +// original code uses do loops? +void +FrameUpdateCallBackWith3dVelocityExtractionNonSkinned(AnimBlendFrameData *frame, void *arg) +{ + CVector vec, pos(0.0f, 0.0f, 0.0f); + CQuaternion q, rot(0.0f, 0.0f, 0.0f, 0.0f); + float totalBlendAmount = 0.0f; + CVector trans(0.0f, 0.0f, 0.0f); + CVector cur(0.0f, 0.0f, 0.0f); + CVector end(0.0f, 0.0f, 0.0f); + bool looped = false; + RwMatrix *mat = RwFrameGetMatrix(frame->frame); + CAnimBlendNode **node; + AnimBlendFrameUpdateData *updateData = (AnimBlendFrameUpdateData*)arg; + + if(updateData->foobar) + for(node = updateData->nodes; *node; node++) + if((*node)->sequence && (*node)->association->IsPartial()) + totalBlendAmount += (*node)->association->blendAmount; + + for(node = updateData->nodes; *node; node++) + if((*node)->sequence && (*node)->sequence->HasTranslation()){ + if((*node)->association->HasTranslation()){ + (*node)->GetCurrentTranslation(vec, 1.0f-totalBlendAmount); + cur += vec; + } + } + + for(node = updateData->nodes; *node; node++){ + if((*node)->sequence){ + bool nodelooped = (*node)->Update(vec, q, 1.0f-totalBlendAmount); +#ifdef FIX_BUGS + if(DotProduct(rot, q) < 0.0f) + rot -= q; + else +#endif + rot += q; + if((*node)->sequence->HasTranslation()){ + pos += vec; + if((*node)->association->HasTranslation()){ + trans += vec; + looped |= nodelooped; + if(nodelooped){ + (*node)->GetEndTranslation(vec, 1.0f-totalBlendAmount); + end += vec; + } + } + } + } + ++*node; + } + + if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){ + RwMatrixSetIdentity(mat); + rot.Normalise(); + rot.Get(mat); + } + + if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){ + *gpAnimBlendClump->velocity3d = trans - cur; + if(looped) + *gpAnimBlendClump->velocity3d += end; + mat->pos.x = (pos - trans).x + frame->resetPos.x; + mat->pos.y = (pos - trans).y + frame->resetPos.y; + mat->pos.z = (pos - trans).z + frame->resetPos.z; + } + RwMatrixUpdate(mat); +} + +void +FrameUpdateCallBackSkinned(AnimBlendFrameData *frame, void *arg) +{ + CVector vec, pos(0.0f, 0.0f, 0.0f); + float transBlendAmount = 0.0f; + CQuaternion q, rot(0.0f, 0.0f, 0.0f, 0.0f); + float totalBlendAmount = 0.0f; + RpHAnimStdInterpFrame *xform = frame->hanimFrame; + CAnimBlendNode **node; + AnimBlendFrameUpdateData *updateData = (AnimBlendFrameUpdateData*)arg; + + if(frame->flag & AnimBlendFrameData::VELOCITY_EXTRACTION && + gpAnimBlendClump->velocity2d){ + if(frame->flag & AnimBlendFrameData::VELOCITY_EXTRACTION_3D) + FrameUpdateCallBackWith3dVelocityExtractionSkinned(frame, arg); + else + FrameUpdateCallBackWithVelocityExtractionSkinned(frame, arg); + return; + } + + if(updateData->foobar) + for(node = updateData->nodes; *node; node++) + if((*node)->sequence && (*node)->association->IsPartial()) + totalBlendAmount += (*node)->association->blendAmount; + + for(node = updateData->nodes; *node; node++){ + if((*node)->sequence){ + (*node)->Update(vec, q, 1.0f-totalBlendAmount); + if((*node)->sequence->HasTranslation()){ + pos += vec; + transBlendAmount += (*node)->association->blendAmount; + } + if(DotProduct(rot, q) < 0.0f) + rot -= q; + else + rot += q; + } + ++*node; + } + + if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){ + rot.Normalise(); + xform->q.imag.x = rot.x; + xform->q.imag.y = rot.y; + xform->q.imag.z = rot.z; + xform->q.real = rot.w; + } + + if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){ + xform->t.x = transBlendAmount*pos.x; + xform->t.y = transBlendAmount*pos.y; + xform->t.z = transBlendAmount*pos.z; + xform->t.x += (1.0f-transBlendAmount)*frame->resetPos.x; + xform->t.y += (1.0f-transBlendAmount)*frame->resetPos.y; + xform->t.z += (1.0f-transBlendAmount)*frame->resetPos.z; + } +} + +void +FrameUpdateCallBackWithVelocityExtractionSkinned(AnimBlendFrameData *frame, void *arg) +{ + CVector vec, pos(0.0f, 0.0f, 0.0f); + CQuaternion q, rot(0.0f, 0.0f, 0.0f, 0.0f); + float totalBlendAmount = 0.0f; + float transx = 0.0f, transy = 0.0f; + float curx = 0.0f, cury = 0.0f; + float endx = 0.0f, endy = 0.0f; + bool looped = false; + RpHAnimStdInterpFrame *xform = frame->hanimFrame; + CAnimBlendNode **node; + AnimBlendFrameUpdateData *updateData = (AnimBlendFrameUpdateData*)arg; + + if(updateData->foobar) + for(node = updateData->nodes; *node; node++) + if((*node)->sequence && (*node)->association->IsPartial()) + totalBlendAmount += (*node)->association->blendAmount; + + for(node = updateData->nodes; *node; node++) + if((*node)->sequence && (*node)->sequence->HasTranslation()){ + if((*node)->association->HasTranslation()){ + (*node)->GetCurrentTranslation(vec, 1.0f-totalBlendAmount); + cury += vec.y; + if((*node)->association->HasXTranslation()) + curx += vec.x; + } + } + + for(node = updateData->nodes; *node; node++){ + if((*node)->sequence){ + bool nodelooped = (*node)->Update(vec, q, 1.0f-totalBlendAmount); + if(DotProduct(rot, q) < 0.0f) + rot -= q; + else + rot += q; + if((*node)->sequence->HasTranslation()){ + pos += vec; + if((*node)->association->HasTranslation()){ + transy += vec.y; + if((*node)->association->HasXTranslation()) + transx += vec.x; + looped |= nodelooped; + if(nodelooped){ + (*node)->GetEndTranslation(vec, 1.0f-totalBlendAmount); + endy += vec.y; + if((*node)->association->HasXTranslation()) + endx += vec.x; + } + } + } + } + ++*node; + } + + if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){ + rot.Normalise(); + xform->q.imag.x = rot.x; + xform->q.imag.y = rot.y; + xform->q.imag.z = rot.z; + xform->q.real = rot.w; + } + + if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){ + gpAnimBlendClump->velocity2d->x = transx - curx; + gpAnimBlendClump->velocity2d->y = transy - cury; + if(looped){ + gpAnimBlendClump->velocity2d->x += endx; + gpAnimBlendClump->velocity2d->y += endy; + } + xform->t.x = pos.x - transx; + xform->t.y = pos.y - transy; + xform->t.z = pos.z; + if(xform->t.z >= -0.8f) { + if(xform->t.z < -0.4f) + xform->t.z += (2.5f * xform->t.z + 2.0f) * frame->resetPos.z; + else + xform->t.z += frame->resetPos.z; + } + xform->t.x += frame->resetPos.x; + xform->t.y += frame->resetPos.y; + } +} + +void +FrameUpdateCallBackWith3dVelocityExtractionSkinned(AnimBlendFrameData *frame, void *arg) +{ + CVector vec, pos(0.0f, 0.0f, 0.0f); + CQuaternion q, rot(0.0f, 0.0f, 0.0f, 0.0f); + float totalBlendAmount = 0.0f; + CVector trans(0.0f, 0.0f, 0.0f); + CVector cur(0.0f, 0.0f, 0.0f); + CVector end(0.0f, 0.0f, 0.0f); + bool looped = false; + RpHAnimStdInterpFrame *xform = frame->hanimFrame; + CAnimBlendNode **node; + AnimBlendFrameUpdateData *updateData = (AnimBlendFrameUpdateData*)arg; + + if(updateData->foobar) + for(node = updateData->nodes; *node; node++) + if((*node)->sequence && (*node)->association->IsPartial()) + totalBlendAmount += (*node)->association->blendAmount; + + for(node = updateData->nodes; *node; node++) + if((*node)->sequence && (*node)->sequence->HasTranslation()){ + if((*node)->association->HasTranslation()){ + (*node)->GetCurrentTranslation(vec, 1.0f-totalBlendAmount); + cur += vec; + } + } + + for(node = updateData->nodes; *node; node++){ + if((*node)->sequence){ + bool nodelooped = (*node)->Update(vec, q, 1.0f-totalBlendAmount); +#ifdef FIX_BUGS + if(DotProduct(rot, q) < 0.0f) + rot -= q; + else +#endif + rot += q; + if((*node)->sequence->HasTranslation()){ + pos += vec; + if((*node)->association->HasTranslation()){ + trans += vec; + looped |= nodelooped; + if(nodelooped){ + (*node)->GetEndTranslation(vec, 1.0f-totalBlendAmount); + end += vec; + } + } + } + } + ++*node; + } + + if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){ + rot.Normalise(); + xform->q.imag.x = rot.x; + xform->q.imag.y = rot.y; + xform->q.imag.z = rot.z; + xform->q.real = rot.w; + } + + if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){ + *gpAnimBlendClump->velocity3d = trans - cur; + if(looped) + *gpAnimBlendClump->velocity3d += end; + xform->t.x = (pos - trans).x + frame->resetPos.x; + xform->t.y = (pos - trans).y + frame->resetPos.y; + xform->t.z = (pos - trans).z + frame->resetPos.z; + } +} + +void +FrameUpdateCallBackOffscreen(AnimBlendFrameData *frame, void *arg) +{ + if(frame->flag & AnimBlendFrameData::VELOCITY_EXTRACTION && gpAnimBlendClump->velocity2d) + FrameUpdateCallBackWithVelocityExtractionSkinned(frame, arg); +} \ No newline at end of file diff --git a/src/miami/animation/RpAnimBlend.cpp b/src/miami/animation/RpAnimBlend.cpp new file mode 100644 index 00000000..933dac69 --- /dev/null +++ b/src/miami/animation/RpAnimBlend.cpp @@ -0,0 +1,518 @@ +#include "common.h" + +#include "RwHelper.h" +#include "General.h" +#include "NodeName.h" +#include "VisibilityPlugins.h" +#include "Bones.h" +#include "AnimBlendClumpData.h" +#include "AnimBlendHierarchy.h" +#include "AnimBlendAssociation.h" +#include "AnimManager.h" +#include "RpAnimBlend.h" +#include "PedModelInfo.h" + +RwInt32 ClumpOffset; + +enum +{ + ID_RPANIMBLEND = MAKECHUNKID(rwVENDORID_ROCKSTAR, 0xFD), +}; + +void* +AnimBlendClumpCreate(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject) +{ + *RWPLUGINOFFSET(CAnimBlendClumpData*, object, offsetInObject) = nil; + return object; +} + +void* +AnimBlendClumpDestroy(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject) +{ + CAnimBlendClumpData *data; + data = *RPANIMBLENDCLUMPDATA(object); + if(data){ + RpAnimBlendClumpRemoveAllAssociations((RpClump*)object); + delete data; + *RPANIMBLENDCLUMPDATA(object) = nil; + } + return object; +} + +void *AnimBlendClumpCopy(void *dstObject, const void *srcObject, RwInt32 offsetInObject, RwInt32 sizeInObject) { return nil; } + +bool +RpAnimBlendPluginAttach(void) +{ + ClumpOffset = RpClumpRegisterPlugin(sizeof(CAnimBlendClumpData*), ID_RPANIMBLEND, + AnimBlendClumpCreate, AnimBlendClumpDestroy, AnimBlendClumpCopy); + return ClumpOffset >= 0; +} + +CAnimBlendAssociation* +RpAnimBlendGetNextAssociation(CAnimBlendAssociation *assoc) +{ + if(assoc->link.next) + return CAnimBlendAssociation::FromLink(assoc->link.next); + return nil; +} + +CAnimBlendAssociation* +RpAnimBlendGetNextAssociation(CAnimBlendAssociation *assoc, uint32 mask) +{ + CAnimBlendLink *link; + for(link = assoc->link.next; link; link = link->next){ + assoc = CAnimBlendAssociation::FromLink(link); + if(assoc->flags & mask) + return assoc; + } + return nil; +} + +void +RpAnimBlendAllocateData(RpClump *clump) +{ + *RPANIMBLENDCLUMPDATA(clump) = new CAnimBlendClumpData; +} + + +void +RpAnimBlendClumpSetBlendDeltas(RpClump *clump, uint32 mask, float delta) +{ + CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); + for(CAnimBlendLink *link = clumpData->link.next; link; link = link->next){ + CAnimBlendAssociation *assoc = CAnimBlendAssociation::FromLink(link); + if(mask == 0 || (assoc->flags & mask)) + assoc->blendDelta = delta; + } +} + +void +RpAnimBlendClumpRemoveAllAssociations(RpClump *clump) +{ + RpAnimBlendClumpRemoveAssociations(clump, 0); +} + +void +RpAnimBlendClumpRemoveAssociations(RpClump *clump, uint32 mask) +{ + CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); + CAnimBlendLink *next; + for(CAnimBlendLink *link = clumpData->link.next; link; link = next){ + next = link->next; + CAnimBlendAssociation *assoc = CAnimBlendAssociation::FromLink(link); + if(mask == 0 || (assoc->flags & mask)) + if(assoc) + delete assoc; + } +} + +RwFrame* +FrameForAllChildrenCountCallBack(RwFrame *frame, void *data) +{ + int *numFrames = (int*)data; + (*numFrames)++; + RwFrameForAllChildren(frame, FrameForAllChildrenCountCallBack, data); + return frame; +} + +RwFrame* +FrameForAllChildrenFillFrameArrayCallBack(RwFrame *frame, void *data) +{ + AnimBlendFrameData **frames = (AnimBlendFrameData**)data; + (*frames)->frame = frame; + (*frames)++; + RwFrameForAllChildren(frame, FrameForAllChildrenFillFrameArrayCallBack, frames); + return frame; +} + +// FrameInitCallBack on PS2 +void +FrameInitCBnonskin(AnimBlendFrameData *frameData, void*) +{ + frameData->flag = 0; + frameData->resetPos = *RwMatrixGetPos(RwFrameGetMatrix(frameData->frame)); +} + +void +FrameInitCBskin(AnimBlendFrameData *frameData, void*) +{ + frameData->flag = 0; +} + +void +RpAnimBlendClumpInitSkinned(RpClump *clump) +{ + int i; + RwV3d boneTab[64]; + CAnimBlendClumpData *clumpData; + RpAtomic *atomic; + RpSkin *skin; + RpHAnimHierarchy *hier; + int numBones; + + RpAnimBlendAllocateData(clump); + clumpData = *RPANIMBLENDCLUMPDATA(clump); + atomic = GetFirstAtomic(clump); + assert(atomic); + skin = RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic)); + assert(skin); + numBones = RpSkinGetNumBones(skin); + clumpData->SetNumberOfBones(numBones); + hier = GetAnimHierarchyFromSkinClump(clump); + assert(hier); + memset(boneTab, 0, sizeof(boneTab)); + SkinGetBonePositionsToTable(clump, boneTab); + + AnimBlendFrameData *frames = clumpData->frames; + for(i = 0; i < numBones; i++){ + frames[i].nodeID = HIERNODEID(hier, i); + frames[i].resetPos = boneTab[i]; +#ifdef LIBRW + frames[i].hanimFrame = (RpHAnimStdInterpFrame*)rpHANIMHIERARCHYGETINTERPFRAME(hier, i); +#else + frames[i].hanimFrame = (RpHAnimStdInterpFrame*)rtANIMGETINTERPFRAME(hier->currentAnim, i); +#endif + } + clumpData->ForAllFrames(FrameInitCBskin, nil); + clumpData->frames[0].flag |= AnimBlendFrameData::VELOCITY_EXTRACTION; +} + +void +RpAnimBlendClumpInitNotSkinned(RpClump *clump) +{ + int numFrames = 0; + CAnimBlendClumpData *clumpData; + RwFrame *root; + AnimBlendFrameData *frames; + + RpAnimBlendAllocateData(clump); + clumpData = *RPANIMBLENDCLUMPDATA(clump); + root = RpClumpGetFrame(clump); + RwFrameForAllChildren(root, FrameForAllChildrenCountCallBack, &numFrames); + clumpData->SetNumberOfFrames(numFrames); + frames = clumpData->frames; + RwFrameForAllChildren(root, FrameForAllChildrenFillFrameArrayCallBack, &frames); + clumpData->ForAllFrames(FrameInitCBnonskin, nil); + clumpData->frames[0].flag |= AnimBlendFrameData::VELOCITY_EXTRACTION; +} + +void +RpAnimBlendClumpInit(RpClump *clump) +{ + if(IsClumpSkinned(clump)) + RpAnimBlendClumpInitSkinned(clump); + else + RpAnimBlendClumpInitNotSkinned(clump); +} + +bool +RpAnimBlendClumpIsInitialized(RpClump *clump) +{ + CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); + return clumpData && clumpData->numFrames != 0; +} + +CAnimBlendAssociation* +RpAnimBlendClumpGetAssociation(RpClump *clump, uint32 id) +{ + CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); + + if(clumpData == nil) return nil; + + for(CAnimBlendLink *link = clumpData->link.next; link; link = link->next){ + CAnimBlendAssociation *assoc = CAnimBlendAssociation::FromLink(link); + if(assoc->animId == id) + return assoc; + } + return nil; +} + +CAnimBlendAssociation* +RpAnimBlendClumpGetMainAssociation(RpClump *clump, CAnimBlendAssociation **assocRet, float *blendRet) +{ + CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); + + if(clumpData == nil) return nil; + + CAnimBlendAssociation *mainAssoc = nil; + CAnimBlendAssociation *secondAssoc = nil; + float mainBlend = 0.0f; + float secondBlend = 0.0f; + for(CAnimBlendLink *link = clumpData->link.next; link; link = link->next){ + CAnimBlendAssociation *assoc = CAnimBlendAssociation::FromLink(link); + + if(assoc->IsPartial()) + continue; + + if(assoc->blendAmount > mainBlend){ + secondBlend = mainBlend; + mainBlend = assoc->blendAmount; + + secondAssoc = mainAssoc; + mainAssoc = assoc; + }else if(assoc->blendAmount > secondBlend){ + secondBlend = assoc->blendAmount; + secondAssoc = assoc; + } + } + if(assocRet) *assocRet = secondAssoc; + if(blendRet) *blendRet = secondBlend; + return mainAssoc; +} + +CAnimBlendAssociation* +RpAnimBlendClumpGetMainPartialAssociation(RpClump *clump) +{ + CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); + + if(clumpData == nil) return nil; + + CAnimBlendAssociation *mainAssoc = nil; + float mainBlend = 0.0f; + for(CAnimBlendLink *link = clumpData->link.next; link; link = link->next){ + CAnimBlendAssociation *assoc = CAnimBlendAssociation::FromLink(link); + + if(!assoc->IsPartial()) + continue; + + if(assoc->blendAmount > mainBlend){ + mainBlend = assoc->blendAmount; + mainAssoc = assoc; + } + } + return mainAssoc; +} + +CAnimBlendAssociation* +RpAnimBlendClumpGetMainAssociation_N(RpClump *clump, int n) +{ + int i; + CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); + + if(clumpData == nil) return nil; + + i = 0; + for(CAnimBlendLink *link = clumpData->link.next; link; link = link->next){ + CAnimBlendAssociation *assoc = CAnimBlendAssociation::FromLink(link); + + if(assoc->IsPartial()) + continue; + + if(i == n) + return assoc; + i++; + } + return nil; +} + +CAnimBlendAssociation* +RpAnimBlendClumpGetMainPartialAssociation_N(RpClump *clump, int n) +{ + int i; + CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); + + if(clumpData == nil) return nil; + + i = 0; + for(CAnimBlendLink *link = clumpData->link.next; link; link = link->next){ + CAnimBlendAssociation *assoc = CAnimBlendAssociation::FromLink(link); + + if(!assoc->IsPartial()) + continue; + + if(i == n) + return assoc; + i++; + } + return nil; +} + +CAnimBlendAssociation* +RpAnimBlendClumpGetFirstAssociation(RpClump *clump, uint32 mask) +{ + CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); + + if(clumpData == nil) return nil; + + for(CAnimBlendLink *link = clumpData->link.next; link; link = link->next){ + CAnimBlendAssociation *assoc = CAnimBlendAssociation::FromLink(link); + if(assoc->flags & mask) + return assoc; + } + return nil; +} + +CAnimBlendAssociation* +RpAnimBlendClumpGetFirstAssociation(RpClump *clump) +{ + CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); + if(!RpAnimBlendClumpIsInitialized(clump)) + return nil; + if(clumpData->link.next) + return CAnimBlendAssociation::FromLink(clumpData->link.next); + return nil; +} + +// FillFrameArrayCallBack on PS2 +void +FillFrameArrayCBnonskin(AnimBlendFrameData *frame, void *arg) +{ + AnimBlendFrameData **frames = (AnimBlendFrameData**)arg; + frames[CVisibilityPlugins::GetFrameHierarchyId(frame->frame)] = frame; +} + +void +RpAnimBlendClumpFillFrameArraySkin(RpClump *clump, AnimBlendFrameData **frames) +{ + int i; + CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); + RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(clump); + for(i = PED_MID; i < PED_NODE_MAX; i++) + frames[i] = &clumpData->frames[RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(i))]; +} + +void +RpAnimBlendClumpFillFrameArray(RpClump *clump, AnimBlendFrameData **frames) +{ + if(IsClumpSkinned(clump)) + RpAnimBlendClumpFillFrameArraySkin(clump, frames); + else + (*RPANIMBLENDCLUMPDATA(clump))->ForAllFrames(FillFrameArrayCBnonskin, frames); +} + +AnimBlendFrameData *pFrameDataFound; + +void +FrameFindByNameCBnonskin(AnimBlendFrameData *frame, void *arg) +{ + char *nodename = GetFrameNodeName(frame->frame); + if(!CGeneral::faststricmp(nodename, (char*)arg)) + pFrameDataFound = frame; +} + +void +FrameFindByNameCBskin(AnimBlendFrameData *frame, void *arg) +{ + const char *name = ConvertBoneTag2BoneName(frame->nodeID); + if(name && CGeneral::faststricmp(name, (char*)arg) == 0) + pFrameDataFound = frame; +} + +void +FrameFindByBoneCB(AnimBlendFrameData *frame, void *arg) +{ + if(frame->nodeID == (int32)(uintptr)arg) + pFrameDataFound = frame; +} + +AnimBlendFrameData* +RpAnimBlendClumpFindFrame(RpClump *clump, const char *name) +{ + pFrameDataFound = nil; + if(IsClumpSkinned(clump)) + (*RPANIMBLENDCLUMPDATA(clump))->ForAllFrames(FrameFindByNameCBskin, (void*)name); + else + (*RPANIMBLENDCLUMPDATA(clump))->ForAllFrames(FrameFindByNameCBnonskin, (void*)name); + return pFrameDataFound; +} + +AnimBlendFrameData* +RpAnimBlendClumpFindBone(RpClump *clump, uint32 boneTag) +{ + pFrameDataFound = nil; + (*RPANIMBLENDCLUMPDATA(clump))->ForAllFrames(FrameFindByBoneCB, (void*)boneTag); + return pFrameDataFound; +} + +#if !defined(DC_TEXCONV) +void +RpAnimBlendNodeUpdateKeyframes(AnimBlendFrameData *frames, AnimBlendFrameUpdateData *updateData, int32 numNodes) +{ + CAnimBlendNode **node; + int i; + + for(node = updateData->nodes; *node; node++){ + CAnimBlendAssociation *a = (*node)->association; + for(i = 0; i < numNodes; i++) + if((frames[i].flag & AnimBlendFrameData::VELOCITY_EXTRACTION) == 0 || + gpAnimBlendClump->velocity2d == nil){ + if((*node)[i].sequence) + (*node)[i].FindKeyFrame(a->currentTime - a->timeStep); + } + } +} +#else +void +RpAnimBlendNodeUpdateKeyframes(AnimBlendFrameData *frames, AnimBlendFrameUpdateData *updateData, int32 numNodes) { assert(false); } +#endif + +#if !defined(DC_TEXCONV) +// TODO: +// CAnimBlendClumpData::LoadFramesIntoSPR +// CAnimBlendClumpData::ForAllFramesInSPR +void +RpAnimBlendClumpUpdateAnimations(RpClump *clump, float timeDelta, bool doRender) +{ + int i; + CAnimBlendAssociation *assoc; + AnimBlendFrameUpdateData updateData; + float totalLength = 0.0f; + float totalBlend = 0.0f; + CAnimBlendLink *link, *next; + CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); + gpAnimBlendClump = clumpData; + + if(clumpData->link.next == nil) + return; + + // Update blend and get node array + i = 0; + updateData.foobar = 0; + for(link = clumpData->link.next; link; link = next){ + next = link->next; + assoc = CAnimBlendAssociation::FromLink(link); + if(assoc->UpdateBlend(timeDelta)){ + if(assoc->hierarchy->sequences){ + if(i < 11) + updateData.nodes[i++] = assoc->GetNode(0); + if(assoc->flags & ASSOC_MOVEMENT){ + totalLength += assoc->hierarchy->totalLength/assoc->speed * assoc->blendAmount; + totalBlend += assoc->blendAmount; + }else + updateData.foobar = 1; + }else + debug("anim %s is not loaded\n", assoc->hierarchy->name); + } + } + + for(link = clumpData->link.next; link; link = link->next){ + assoc = CAnimBlendAssociation::FromLink(link); + assoc->UpdateTimeStep(timeDelta, totalLength == 0.0f ? 1.0f : totalBlend/totalLength); + } + + updateData.nodes[i] = nil; + + if(doRender){ + if(clumpData->frames[0].flag & AnimBlendFrameData::UPDATE_KEYFRAMES) + RpAnimBlendNodeUpdateKeyframes(clumpData->frames, &updateData, clumpData->numFrames); + if(IsClumpSkinned(clump)) + clumpData->ForAllFrames(FrameUpdateCallBackSkinned, &updateData); + else + clumpData->ForAllFrames(FrameUpdateCallBackNonSkinned, &updateData); + clumpData->frames[0].flag &= ~AnimBlendFrameData::UPDATE_KEYFRAMES; + }else{ + clumpData->ForAllFrames(FrameUpdateCallBackOffscreen, &updateData); + clumpData->frames[0].flag |= AnimBlendFrameData::UPDATE_KEYFRAMES; + } + + for(link = clumpData->link.next; link; link = link->next){ + assoc = CAnimBlendAssociation::FromLink(link); + assoc->UpdateTime(timeDelta, totalLength == 0.0f ? 1.0f : totalBlend/totalLength); + } + RwFrameUpdateObjects(RpClumpGetFrame(clump)); +} +#else +void +RpAnimBlendClumpUpdateAnimations(RpClump *clump, float timeDelta, bool doRender) { assert(false); } +#endif diff --git a/src/miami/animation/RpAnimBlend.h b/src/miami/animation/RpAnimBlend.h new file mode 100644 index 00000000..d0f7a114 --- /dev/null +++ b/src/miami/animation/RpAnimBlend.h @@ -0,0 +1,45 @@ +#pragma once + +class CAnimBlendNode; +class CAnimBlendAssociation; +class CAnimBlendClumpData; +struct AnimBlendFrameData; + +struct AnimBlendFrameUpdateData +{ + int foobar; // TODO: figure out what this actually means + CAnimBlendNode *nodes[16]; +}; + +extern RwInt32 ClumpOffset; +#define RPANIMBLENDCLUMPDATA(o) (RWPLUGINOFFSET(CAnimBlendClumpData*, o, ClumpOffset)) + +bool RpAnimBlendPluginAttach(void); +CAnimBlendAssociation *RpAnimBlendGetNextAssociation(CAnimBlendAssociation *assoc); +CAnimBlendAssociation *RpAnimBlendGetNextAssociation(CAnimBlendAssociation *assoc, uint32 mask); +void RpAnimBlendAllocateData(RpClump *clump); + +void RpAnimBlendClumpSetBlendDeltas(RpClump *clump, uint32 mask, float delta); +void RpAnimBlendClumpRemoveAllAssociations(RpClump *clump); +void RpAnimBlendClumpRemoveAssociations(RpClump *clump, uint32 mask); +void RpAnimBlendClumpInit(RpClump *clump); +bool RpAnimBlendClumpIsInitialized(RpClump *clump); +void RpAnimBlendClumpFillFrameArray(RpClump* clump, AnimBlendFrameData** frames); +AnimBlendFrameData *RpAnimBlendClumpFindFrame(RpClump *clump, const char *name); +AnimBlendFrameData *RpAnimBlendClumpFindBone(RpClump *clump, uint32 boneTag); +void FillFrameArrayCallBack(AnimBlendFrameData *frame, void *arg); +CAnimBlendAssociation *RpAnimBlendClumpGetAssociation(RpClump *clump, uint32 id); +CAnimBlendAssociation *RpAnimBlendClumpGetMainAssociation(RpClump *clump, CAnimBlendAssociation **assocRet, float *blendRet); +CAnimBlendAssociation *RpAnimBlendClumpGetMainPartialAssociation(RpClump *clump); +CAnimBlendAssociation *RpAnimBlendClumpGetMainAssociation_N(RpClump *clump, int n); +CAnimBlendAssociation *RpAnimBlendClumpGetMainPartialAssociation_N(RpClump *clump, int n); +CAnimBlendAssociation *RpAnimBlendClumpGetFirstAssociation(RpClump *clump, uint32 mask); +CAnimBlendAssociation *RpAnimBlendClumpGetFirstAssociation(RpClump *clump); +void RpAnimBlendNodeUpdateKeyframes(AnimBlendFrameData *frames, AnimBlendFrameUpdateData *updateData, int32 numNodes); +void RpAnimBlendClumpUpdateAnimations(RpClump* clump, float timeDelta, bool doRender = true); + + +extern CAnimBlendClumpData *gpAnimBlendClump; +void FrameUpdateCallBackNonSkinned(AnimBlendFrameData *frame, void *arg); +void FrameUpdateCallBackSkinned(AnimBlendFrameData *frame, void *arg); +void FrameUpdateCallBackOffscreen(AnimBlendFrameData *frame, void *arg); diff --git a/src/miami/audio/AudioCollision.cpp b/src/miami/audio/AudioCollision.cpp new file mode 100644 index 00000000..02330756 --- /dev/null +++ b/src/miami/audio/AudioCollision.cpp @@ -0,0 +1,405 @@ +#include "common.h" + +#include "DMAudio.h" +#include "Entity.h" +#include "AudioCollision.h" +#include "AudioManager.h" +#include "AudioSamples.h" +#include "SurfaceTable.h" +#include "sampman.h" + +void +cAudioManager::ReportCollision(CEntity *entity1, CEntity *entity2, uint8 surface1, uint8 surface2, float collisionPower, + float velocity) +{ + float distSquared; + CVector v1; + CVector v2; + + if(!m_bIsInitialised || m_nCollisionEntity < 0 || m_bIsPaused || + (velocity < 0.0016f && collisionPower < 0.01f)) + return; + + if(entity1->IsBuilding()) { + v1 = v2 = entity2->GetPosition(); + } else if(entity2->IsBuilding()) { + v1 = v2 = entity1->GetPosition(); + } else { + v1 = entity1->GetPosition(); + v2 = entity2->GetPosition(); + } + CVector pos = (v1 + v2) * 0.5f; + distSquared = GetDistanceSquared(pos); + if(distSquared < SQR(COLLISION_MAX_DIST)) { + m_sCollisionManager.m_sQueue.m_pEntity1 = entity1; + m_sCollisionManager.m_sQueue.m_pEntity2 = entity2; + m_sCollisionManager.m_sQueue.m_bSurface1 = surface1; + m_sCollisionManager.m_sQueue.m_bSurface2 = surface2; + m_sCollisionManager.m_sQueue.m_fIntensity1 = collisionPower; + m_sCollisionManager.m_sQueue.m_fIntensity2 = velocity; + m_sCollisionManager.m_sQueue.m_vecPosition = pos; + m_sCollisionManager.m_sQueue.m_fDistance = distSquared; + m_sCollisionManager.AddCollisionToRequestedQueue(); + } +} + +void +cAudioCollisionManager::AddCollisionToRequestedQueue() +{ + uint32 collisionsIndex; + uint32 i; + + + if (m_bCollisionsInQueue < NUMAUDIOCOLLISIONS) + collisionsIndex = m_bCollisionsInQueue++; + else { + collisionsIndex = m_bIndicesTable[NUMAUDIOCOLLISIONS - 1]; + if (m_sQueue.m_fDistance >= m_asCollisions1[collisionsIndex].m_fDistance) return; + } + + m_asCollisions1[collisionsIndex] = m_sQueue; + + i = 0; + if(collisionsIndex) { + while(m_asCollisions1[m_bIndicesTable[i]].m_fDistance <= m_asCollisions1[collisionsIndex].m_fDistance) { + if(++i >= collisionsIndex) { + m_bIndicesTable[i] = collisionsIndex; + return; + } + } + memmove(&m_bIndicesTable[i + 1], &m_bIndicesTable[i], NUMAUDIOCOLLISIONS - 1 - i); + } + m_bIndicesTable[i] = collisionsIndex; +} + +void +cAudioManager::ServiceCollisions() +{ + int i, j; + bool8 abRepeatedCollision1[NUMAUDIOCOLLISIONS]; + bool8 abRepeatedCollision2[NUMAUDIOCOLLISIONS]; + + m_sQueueSample.m_nEntityIndex = m_nCollisionEntity; + + for (int i = 0; i < NUMAUDIOCOLLISIONS; i++) + abRepeatedCollision1[i] = abRepeatedCollision2[i] = FALSE; + + for (i = 0; i < m_sCollisionManager.m_bCollisionsInQueue; i++) { + for (j = 0; j < NUMAUDIOCOLLISIONS; j++) { + int index = m_sCollisionManager.m_bIndicesTable[i]; + if ((m_sCollisionManager.m_asCollisions1[index].m_pEntity1 == m_sCollisionManager.m_asCollisions2[j].m_pEntity1) + && (m_sCollisionManager.m_asCollisions1[index].m_pEntity2 == m_sCollisionManager.m_asCollisions2[j].m_pEntity2) + && (m_sCollisionManager.m_asCollisions1[index].m_bSurface1 == m_sCollisionManager.m_asCollisions2[j].m_bSurface1) + && (m_sCollisionManager.m_asCollisions1[index].m_bSurface2 == m_sCollisionManager.m_asCollisions2[j].m_bSurface2) + ) { + abRepeatedCollision1[index] = TRUE; + abRepeatedCollision2[j] = TRUE; + m_sCollisionManager.m_asCollisions1[index].m_nBaseVolume = ++m_sCollisionManager.m_asCollisions2[j].m_nBaseVolume; + SetUpLoopingCollisionSound(m_sCollisionManager.m_asCollisions1[index], j); + break; + } + } + } + + for (i = 0; i < NUMAUDIOCOLLISIONS; i++) { + if (!abRepeatedCollision2[i]) { + m_sCollisionManager.m_asCollisions2[i].m_pEntity1 = nil; + m_sCollisionManager.m_asCollisions2[i].m_pEntity2 = nil; + m_sCollisionManager.m_asCollisions2[i].m_bSurface1 = SURFACE_DEFAULT; + m_sCollisionManager.m_asCollisions2[i].m_bSurface2 = SURFACE_DEFAULT; + m_sCollisionManager.m_asCollisions2[i].m_fIntensity2 = 0.0f; + m_sCollisionManager.m_asCollisions2[i].m_fIntensity1 = 0.0f; + m_sCollisionManager.m_asCollisions2[i].m_vecPosition = CVector(0.0f, 0.0f, 0.0f); + m_sCollisionManager.m_asCollisions2[i].m_fDistance = 0.0f; + } + } + + for (i = 0; i < m_sCollisionManager.m_bCollisionsInQueue; i++) { + int index = m_sCollisionManager.m_bIndicesTable[i]; + if (!abRepeatedCollision1[index]) { + for (j = 0; j < NUMAUDIOCOLLISIONS; j++) { + if (!abRepeatedCollision2[j]) { + m_sCollisionManager.m_asCollisions2[j].m_nBaseVolume = 1; + m_sCollisionManager.m_asCollisions2[j].m_pEntity1 = m_sCollisionManager.m_asCollisions1[index].m_pEntity1; + m_sCollisionManager.m_asCollisions2[j].m_pEntity2 = m_sCollisionManager.m_asCollisions1[index].m_pEntity2; + m_sCollisionManager.m_asCollisions2[j].m_bSurface1 = m_sCollisionManager.m_asCollisions1[index].m_bSurface1; + m_sCollisionManager.m_asCollisions2[j].m_bSurface2 = m_sCollisionManager.m_asCollisions1[index].m_bSurface2; + break; + } + } + SetUpOneShotCollisionSound(m_sCollisionManager.m_asCollisions1[index]); + SetUpLoopingCollisionSound(m_sCollisionManager.m_asCollisions1[index], j); + } + } + + for (int i = 0; i < NUMAUDIOCOLLISIONS; i++) + m_sCollisionManager.m_bIndicesTable[i] = NUMAUDIOCOLLISIONS; + m_sCollisionManager.m_bCollisionsInQueue = 0; +} + +static const uint32 gOneShotCol[] = {SFX_COL_TARMAC_1, + SFX_COL_TARMAC_1, + SFX_COL_GRASS_1, + SFX_COL_GRAVEL_1, + SFX_COL_MUD_1, + SFX_COL_TARMAC_1, + SFX_COL_CAR_1, + SFX_COL_GRASS_1, + SFX_COL_SCAFFOLD_POLE_1, + SFX_COL_GARAGE_DOOR_1, + SFX_COL_CAR_PANEL_1, + SFX_COL_THICK_METAL_PLATE_1, + SFX_COL_SCAFFOLD_POLE_1, + SFX_COL_LAMP_POST_1, + SFX_COL_HYDRANT_1, + SFX_COL_HYDRANT_1, + SFX_COL_METAL_CHAIN_FENCE_1, + SFX_COL_PED_1, + SFX_COL_SAND_1, + SFX_SPLASH_1, + SFX_COL_WOOD_CRATES_1, + SFX_COL_WOOD_BENCH_1, + SFX_COL_WOOD_SOLID_1, + SFX_COL_GRASS_1, + SFX_COL_GRASS_1, + SFX_COL_VEG_1, + SFX_COL_TARMAC_1, + SFX_COL_CONTAINER_1, + SFX_COL_NEWS_VENDOR_1, + SFX_TYRE_BUMP, + SFX_COL_CARDBOARD_1, + SFX_COL_TARMAC_1, + SFX_COL_GATE, + SFX_COL_SAND_1, + SFX_COL_TARMAC_1 }; + +void +cAudioManager::SetUpOneShotCollisionSound(const cAudioCollision &col) +{ + uint16 s1; + uint16 s2; + + uint32 emittingVol; + float ratio; + + static uint16 counter = 28; + + for(int32 i = 0; i < 2; i++) { + if(i) { + s1 = col.m_bSurface2; + s2 = col.m_bSurface1; + } else { + s1 = col.m_bSurface1; + s2 = col.m_bSurface2; + } + ratio = GetCollisionOneShotRatio(s1, col.m_fIntensity1); + if(s1 == SURFACE_CAR && s2 == SURFACE_PED) ratio /= 4.0f; + if(s1 == SURFACE_CAR && ratio < 0.6f) { + s1 = SURFACE_CAR_PANEL; + ratio = Min(1.f, 2.f * ratio); + } + emittingVol = 40 * ratio; + if(emittingVol) { + m_sQueueSample.m_fDistance = Sqrt(col.m_fDistance); + m_sQueueSample.m_nVolume = + ComputeVolume(emittingVol, COLLISION_MAX_DIST, m_sQueueSample.m_fDistance); + if(m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nSampleIndex = gOneShotCol[s1]; + switch(m_sQueueSample.m_nSampleIndex) { + case SFX_COL_TARMAC_1: + m_sQueueSample.m_nSampleIndex += m_anRandomTable[3] % 5; + break; + case SFX_COL_CAR_PANEL_1: + m_sQueueSample.m_nSampleIndex += m_anRandomTable[0] % 6; + break; + case SFX_COL_LAMP_POST_1: + m_sQueueSample.m_nSampleIndex += m_anRandomTable[1] % 2; + break; + case SFX_COL_METAL_CHAIN_FENCE_1: + m_sQueueSample.m_nSampleIndex += m_anRandomTable[3] % 4; + break; + case SFX_COL_PED_1: + m_sQueueSample.m_nSampleIndex += m_anRandomTable[4] % 2; + break; + case SFX_COL_WOOD_CRATES_1: + m_sQueueSample.m_nSampleIndex += m_anRandomTable[4] % 4; + break; + case SFX_COL_WOOD_BENCH_1: + m_sQueueSample.m_nSampleIndex += m_anRandomTable[1] % 4; + break; + case SFX_COL_VEG_1: + m_sQueueSample.m_nSampleIndex += m_anRandomTable[2] % 5; + break; + case SFX_COL_NEWS_VENDOR_1: + m_sQueueSample.m_nSampleIndex += m_anRandomTable[2] % 3; + break; + case SFX_COL_CAR_1: + m_sQueueSample.m_nSampleIndex += m_anRandomTable[1] % 5; + break; + case SFX_COL_CARDBOARD_1: + m_sQueueSample.m_nSampleIndex += m_anRandomTable[3] % 2; + break; + default: break; + } + switch(s1) { + case SURFACE_GLASS: m_sQueueSample.m_nFrequency = 13500; break; + case SURFACE_GIRDER: m_sQueueSample.m_nFrequency = 8819; break; + case SURFACE_WATER: + m_sQueueSample.m_nFrequency = + 2 * SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + break; + case SURFACE_RUBBER: m_sQueueSample.m_nFrequency = 6000; break; + case SURFACE_PLASTIC: m_sQueueSample.m_nFrequency = 8000; break; + default: + m_sQueueSample.m_nFrequency = + SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + break; + } + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 16); + m_sQueueSample.m_nCounter = counter++; + if(counter >= 255) counter = 28; + m_sQueueSample.m_vecPos = col.m_vecPosition; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 11; + m_sQueueSample.m_nLoopCount = 1; + SET_EMITTING_VOLUME(emittingVol); + RESET_LOOP_OFFSETS + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_MaxDistance = COLLISION_MAX_DIST; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + } +} + +void +cAudioManager::SetUpLoopingCollisionSound(const cAudioCollision &col, uint8 counter) +{ + bool8 distCalculated = FALSE; + if(col.m_fIntensity2 > 0.0016f) { + uint8 emittingVol = SetLoopingCollisionRequestedSfxFreqAndGetVol(col); + if(emittingVol) { + CalculateDistance(distCalculated, m_sQueueSample.m_fDistance); + m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, COLLISION_MAX_DIST, m_sQueueSample.m_fDistance); + if(m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = counter; + m_sQueueSample.m_vecPos = col.m_vecPosition; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 7; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(emittingVol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_MaxDistance = COLLISION_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 5; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + } +} + +uint32 +cAudioManager::SetLoopingCollisionRequestedSfxFreqAndGetVol(const cAudioCollision &audioCollision) +{ + uint8 surface1 = audioCollision.m_bSurface1; + uint8 surface2 = audioCollision.m_bSurface2; + int32 vol; + float ratio; + + if(surface1 == SURFACE_GRASS || surface2 == SURFACE_GRASS || surface1 == SURFACE_HEDGE || + surface2 == SURFACE_HEDGE) { + ratio = GetCollisionRatio(audioCollision.m_fIntensity2, 0.0001f, 0.09f, 0.0899f); + m_sQueueSample.m_nSampleIndex = SFX_RAIN; + m_sQueueSample.m_nFrequency = 13000.f * ratio + 35000; + vol = 50.f * ratio; + } else if(surface1 == SURFACE_WATER || surface2 == SURFACE_WATER) { + ratio = GetCollisionRatio(audioCollision.m_fIntensity2, 0.0001f, 0.09f, 0.0899f); + m_sQueueSample.m_nSampleIndex = SFX_BOAT_WATER_LOOP; + m_sQueueSample.m_nFrequency = 6050.f * ratio + 16000; + vol = 30.f * ratio; + } else if(surface1 == SURFACE_GRAVEL || surface2 == SURFACE_GRAVEL || surface1 == SURFACE_MUD_DRY || surface2 == SURFACE_MUD_DRY || + surface1 == SURFACE_SAND || surface2 == SURFACE_SAND || surface1 == SURFACE_SAND_BEACH || surface2 == SURFACE_SAND_BEACH) { + ratio = GetCollisionRatio(audioCollision.m_fIntensity2, 0.0001f, 0.09f, 0.0899f); + m_sQueueSample.m_nSampleIndex = SFX_GRAVEL_SKID; + m_sQueueSample.m_nFrequency = 6000.f * ratio + 10000; + vol = 50.f * ratio; + } else if(surface1 == SURFACE_PED || surface2 == SURFACE_PED) { + return 0; + } else { + ratio = GetCollisionRatio(audioCollision.m_fIntensity2, 0.0001f, 0.09f, 0.0899f); + m_sQueueSample.m_nSampleIndex = SFX_SCRAPE_CAR_1; + m_sQueueSample.m_nFrequency = 10000.f * ratio + 10000; + vol = 40.f * ratio; + } + if(audioCollision.m_nBaseVolume < 2) vol = audioCollision.m_nBaseVolume * vol / 2; + return vol; +} + +float +cAudioManager::GetCollisionOneShotRatio(uint32 a, float b) +{ + switch(a) { + case SURFACE_DEFAULT: + case SURFACE_TARMAC: + case SURFACE_PAVEMENT: + case SURFACE_STEEP_CLIFF: + case SURFACE_TRANSPARENT_STONE: + case SURFACE_CONCRETE_BEACH: return GetCollisionRatio(b, 10.f, 60.f, 50.f); + case SURFACE_GRASS: + case SURFACE_GRAVEL: + case SURFACE_MUD_DRY: + case SURFACE_CARDBOARDBOX: return GetCollisionRatio(b, 0.f, 2.f, 2.f); + case SURFACE_CAR: return GetCollisionRatio(b, 6.f, 50.f, 44.f); + case SURFACE_GLASS: + case SURFACE_METAL_CHAIN_FENCE: return GetCollisionRatio(b, 0.1f, 10.f, 9.9f); + case SURFACE_TRANSPARENT_CLOTH: + case SURFACE_THICK_METAL_PLATE: return GetCollisionRatio(b, 30.f, 130.f, 100.f); + case SURFACE_GARAGE_DOOR: return GetCollisionRatio(b, 20.f, 100.f, 80.f); + case SURFACE_CAR_PANEL: return GetCollisionRatio(b, 0.f, 4.f, 4.f); + case SURFACE_SCAFFOLD_POLE: + case SURFACE_METAL_GATE: + case SURFACE_LAMP_POST: return GetCollisionRatio(b, 1.f, 10.f, 9.f); + case SURFACE_FIRE_HYDRANT: return GetCollisionRatio(b, 1.f, 15.f, 14.f); + case SURFACE_GIRDER: return GetCollisionRatio(b, 8.f, 50.f, 42.f); + case SURFACE_PED: return GetCollisionRatio(b, 0.f, 20.f, 20.f); + case SURFACE_SAND: + case SURFACE_WATER: + case SURFACE_RUBBER: + case SURFACE_WHEELBASE: + case SURFACE_SAND_BEACH: return GetCollisionRatio(b, 0.f, 10.f, 10.f); + case SURFACE_WOOD_CRATES: return GetCollisionRatio(b, 1.f, 4.f, 3.f); + case SURFACE_WOOD_BENCH: return GetCollisionRatio(b, 0.1f, 5.f, 4.9f); + case SURFACE_WOOD_SOLID: return GetCollisionRatio(b, 0.1f, 40.f, 39.9f); + case SURFACE_PLASTIC: return GetCollisionRatio(b, 0.1f, 4.f, 3.9f); + case SURFACE_HEDGE: return GetCollisionRatio(b, 0.f, 0.5f, 0.5f); + case SURFACE_CONTAINER: return GetCollisionRatio(b, 4.f, 40.f, 36.f); + case SURFACE_NEWS_VENDOR: return GetCollisionRatio(b, 0.f, 5.f, 5.f); + default: break; + } + + return 0.f; +} + +float +cAudioManager::GetCollisionLoopingRatio(uint32 a, uint32 b, float c) +{ + return GetCollisionRatio(c, 0.0f, 0.02f, 0.02f); +} + +float +cAudioManager::GetCollisionRatio(float a, float b, float c, float d) +{ + float e; + e = a; + if(a <= b) return 0.0f; + if(c <= a) e = c; + return (e - b) / d; +} diff --git a/src/miami/audio/AudioCollision.h b/src/miami/audio/AudioCollision.h new file mode 100644 index 00000000..31be0334 --- /dev/null +++ b/src/miami/audio/AudioCollision.h @@ -0,0 +1,57 @@ +#pragma once + +#define NUMAUDIOCOLLISIONS 10 + +class CEntity; + +class cAudioCollision +{ +public: + CEntity *m_pEntity1; + CEntity *m_pEntity2; + uint8 m_bSurface1; + uint8 m_bSurface2; + float m_fIntensity1; + float m_fIntensity2; + CVector m_vecPosition; + float m_fDistance; + int32 m_nBaseVolume; + + cAudioCollision() { Reset(); } + + void Reset() + { + m_pEntity1 = nil; + m_pEntity2 = nil; + m_bSurface1 = 0; + m_bSurface2 = 0; + m_fIntensity1 = m_fIntensity2 = 0.0f; + m_vecPosition = CVector(0.0f, 0.0f, 0.0f); + m_fDistance = 0.0f; + } +}; + +VALIDATE_SIZE(cAudioCollision, 40); + +class cAudioCollisionManager +{ +public: + cAudioCollision m_asCollisions1[NUMAUDIOCOLLISIONS]; + cAudioCollision m_asCollisions2[NUMAUDIOCOLLISIONS]; + uint8 m_bIndicesTable[NUMAUDIOCOLLISIONS]; + uint8 m_bCollisionsInQueue; + cAudioCollision m_sQueue; + + cAudioCollisionManager() + { + m_sQueue.Reset(); + + for(int i = 0; i < NUMAUDIOCOLLISIONS; i++) + m_bIndicesTable[i] = NUMAUDIOCOLLISIONS; + + m_bCollisionsInQueue = 0; + } + void AddCollisionToRequestedQueue(); +}; + +VALIDATE_SIZE(cAudioCollisionManager, 0x354); diff --git a/src/miami/audio/AudioLogic.cpp b/src/miami/audio/AudioLogic.cpp new file mode 100644 index 00000000..56271fcf --- /dev/null +++ b/src/miami/audio/AudioLogic.cpp @@ -0,0 +1,10279 @@ +#include "common.h" + +#include "AudioManager.h" +#include "audio_enums.h" + +#include "Automobile.h" +#include "Boat.h" +#include "Bridge.h" +#include "Camera.h" +#include "Cranes.h" +#include "DMAudio.h" +#include "Entity.h" +#include "Explosion.h" +#include "Fire.h" +#include "Garages.h" +#include "General.h" +#include "HandlingMgr.h" +#include "Heli.h" +#include "ModelIndices.h" +#include "MusicManager.h" +#include "Pad.h" +#include "ParticleObject.h" +#include "Ped.h" +#include "Physical.h" +#include "Placeable.h" +#include "Plane.h" +#include "PlayerPed.h" +#include "Pools.h" +#include "Projectile.h" +#include "ProjectileInfo.h" +#include "Replay.h" +#include "Stats.h" +#include "SurfaceTable.h" +#include "Train.h" +#include "Transmission.h" +#include "Vehicle.h" +#include "WaterCannon.h" +#include "Weather.h" +#include "ZoneCull.h" +#include "sampman.h" +#include "Bike.h" +#include "WindModifiers.h" +#include "Fluff.h" +#include "Script.h" +#include "Wanted.h" +#include "debugmenu.h" + +#ifndef GTA_PS2 +#define CHANNEL_PLAYER_VEHICLE_ENGINE m_nActiveSamples +#endif + +void +cAudioManager::PreInitialiseGameSpecificSetup() +{ + unsigned DynamicBankCount = SFX_BANK_PED_COMMENTS; // first non dynamic bank + BankStartOffset[SFX_BANK_0] = SAMPLEBANK_START; + for (unsigned i = SFX_BANK_1; i < DynamicBankCount; i++) { + BankStartOffset[i] = (SAMPLEBANK_END - SAMPLEBANK_START) / DynamicBankCount + BankStartOffset[i - 1]; + } +#ifdef GTA_PS2 + BankStartOffset[SAMPLEBANK_CAR_PACARD] = SFX_CAR_ACCEL_1; + BankStartOffset[SAMPLEBANK_CAR_PATHFINDER] = SFX_CAR_ACCEL_2; + BankStartOffset[SAMPLEBANK_CAR_PORSCHE] = SFX_CAR_ACCEL_3; + BankStartOffset[SAMPLEBANK_CAR_SPIDER] = SFX_CAR_ACCEL_4; + BankStartOffset[SAMPLEBANK_CAR_MERC] = SFX_CAR_ACCEL_5; + BankStartOffset[SAMPLEBANK_CAR_MACKTRUCK] = SFX_CAR_ACCEL_6; + BankStartOffset[SAMPLEBANK_CAR_HOTROD] = SFX_CAR_ACCEL_7; + BankStartOffset[SAMPLEBANK_CAR_COBRA] = SFX_CAR_ACCEL_8; + BankStartOffset[SAMPLEBANK_CAR_NONE] = SFX_CAR_ACCEL_9; + BankStartOffset[SAMPLEBANK_FRONTEND] = SFX_PAGE_CHANGE_AND_BACK_LEFT; + BankStartOffset[SAMPLEBANK_TRAIN] = SFX_TRAIN_STATION_AMBIENCE_LOOP; + BankStartOffset[SAMPLEBANK_BUILDING_CLUB_1] = SFX_CLUB_1; + BankStartOffset[SAMPLEBANK_BUILDING_CLUB_2] = SFX_CLUB_2; + BankStartOffset[SAMPLEBANK_BUILDING_CLUB_3] = SFX_CLUB_3; + BankStartOffset[SAMPLEBANK_BUILDING_CLUB_4] = SFX_CLUB_4; + BankStartOffset[SAMPLEBANK_BUILDING_CLUB_5] = SFX_CLUB_5; + BankStartOffset[SAMPLEBANK_BUILDING_CLUB_6] = SFX_CLUB_6; + BankStartOffset[SAMPLEBANK_BUILDING_CLUB_7] = SFX_CLUB_7; + BankStartOffset[SAMPLEBANK_BUILDING_CLUB_8] = SFX_CLUB_8; + BankStartOffset[SAMPLEBANK_BUILDING_CLUB_9] = SFX_CLUB_9; + BankStartOffset[SAMPLEBANK_BUILDING_CLUB_10] = SFX_CLUB_10; + BankStartOffset[SAMPLEBANK_BUILDING_CLUB_11] = SFX_CLUB_11; + BankStartOffset[SAMPLEBANK_BUILDING_CLUB_12] = SFX_CLUB_12; + BankStartOffset[SAMPLEBANK_BUILDING_CLUB_RAGGA] = SFX_CLUB_RAGGA; + BankStartOffset[SAMPLEBANK_BUILDING_STRIP_CLUB_1] = SFX_STRIP_CLUB_1; + BankStartOffset[SAMPLEBANK_BUILDING_STRIP_CLUB_2] = SFX_STRIP_CLUB_2; + BankStartOffset[SAMPLEBANK_BUILDING_WORKSHOP] = SFX_WORKSHOP_1; + BankStartOffset[SAMPLEBANK_BUILDING_PIANO_BAR] = SFX_PIANO_BAR_1; + BankStartOffset[SAMPLEBANK_BUILDING_SAWMILL] = SFX_SAWMILL_LOOP; + BankStartOffset[SAMPLEBANK_BUILDING_DOG_FOOD_FACTORY] = SFX_DOG_FOOD_FACTORY; + BankStartOffset[SAMPLEBANK_BUILDING_LAUNDERETTE] = SFX_LAUNDERETTE_LOOP; + BankStartOffset[SAMPLEBANK_BUILDING_RESTAURANT_CHINATOWN] = SFX_RESTAURANT_CHINATOWN; + BankStartOffset[SAMPLEBANK_BUILDING_RESTAURANT_ITALY] = SFX_RESTAURANT_ITALY; + BankStartOffset[SAMPLEBANK_BUILDING_RESTAURANT_GENERIC_1] = SFX_RESTAURANT_GENERIC_1; + BankStartOffset[SAMPLEBANK_BUILDING_RESTAURANT_GENERIC_2] = SFX_RESTAURANT_GENERIC_2; + BankStartOffset[SAMPLEBANK_BUILDING_AIRPORT] = SFX_AIRPORT_ANNOUNCEMENT_1; + BankStartOffset[SAMPLEBANK_BUILDING_SHOP] = SFX_SHOP_LOOP; + BankStartOffset[SAMPLEBANK_BUILDING_CINEMA] = SFX_CINEMA_BASS_1; + BankStartOffset[SAMPLEBANK_BUILDING_DOCKS] = SFX_DOCKS_FOGHORN; + BankStartOffset[SAMPLEBANK_BUILDING_HOME] = SFX_HOME_1; + BankStartOffset[SAMPLEBANK_BUILDING_PORN_1] = SFX_PORN_1_LOOP; + BankStartOffset[SAMPLEBANK_BUILDING_PORN_2] = SFX_PORN_2_LOOP; + BankStartOffset[SAMPLEBANK_BUILDING_PORN_3] = SFX_PORN_3_LOOP; + BankStartOffset[SAMPLEBANK_BUILDING_POLICE_BALL] = SFX_POLICE_BALL_1; + BankStartOffset[SAMPLEBANK_BUILDING_BANK_ALARM] = SFX_BANK_ALARM_1; + BankStartOffset[SAMPLEBANK_BUILDING_RAVE_INDUSTRIAL] = SFX_RAVE_INDUSTRIAL; + BankStartOffset[SAMPLEBANK_BUILDING_RAVE_COMMERCIAL] = SFX_RAVE_COMMERCIAL; + BankStartOffset[SAMPLEBANK_BUILDING_RAVE_SUBURBAN] = SFX_RAVE_SUBURBAN; + BankStartOffset[SAMPLEBANK_BUILDING_RAVE_COMMERCIAL_2] = SFX_RAVE_COMMERCIAL_2; + BankStartOffset[SAMPLEBANK_BUILDING_39] = SFX_CLUB_1_1; + BankStartOffset[SAMPLEBANK_BUILDING_40] = SFX_CLUB_1_2; + BankStartOffset[SAMPLEBANK_BUILDING_41] = SFX_CLUB_1_3; + BankStartOffset[SAMPLEBANK_BUILDING_42] = SFX_CLUB_1_4; + BankStartOffset[SAMPLEBANK_BUILDING_43] = SFX_CLUB_1_5; + BankStartOffset[SAMPLEBANK_BUILDING_44] = SFX_CLUB_1_6; + BankStartOffset[SAMPLEBANK_BUILDING_45] = SFX_CLUB_1_7; + BankStartOffset[SAMPLEBANK_BUILDING_46] = SFX_CLUB_1_8; + BankStartOffset[SAMPLEBANK_BUILDING_47] = SFX_CLUB_1_9; + BankStartOffset[SAMPLEBANK_EXTRAS] = SFX_EXPLOSION_1; +#endif // GTA_PS2 + BankStartOffset[SFX_BANK_PED_COMMENTS] = SAMPLEBANK_PED_START; +} + +void +cAudioManager::PostInitialiseGameSpecificSetup() +{ + m_nFireAudioEntity = CreateEntity(AUDIOTYPE_FIRE, &gFireManager); + if (m_nFireAudioEntity >= 0) + SetEntityStatus(m_nFireAudioEntity, TRUE); + + m_nCollisionEntity = CreateEntity(AUDIOTYPE_COLLISION, (void *)1); + if (m_nCollisionEntity >= 0) + SetEntityStatus(m_nCollisionEntity, TRUE); + + m_nFrontEndEntity = CreateEntity(AUDIOTYPE_FRONTEND, (void *)1); + if (m_nFrontEndEntity >= 0) + SetEntityStatus(m_nFrontEndEntity, TRUE); + + m_nProjectileEntity = CreateEntity(AUDIOTYPE_PROJECTILE, (void *)1); + if (m_nProjectileEntity >= 0) + SetEntityStatus(m_nProjectileEntity, TRUE); + + m_nWaterCannonEntity = CreateEntity(AUDIOTYPE_WATERCANNON, (void *)1); + if (m_nWaterCannonEntity >= 0) + SetEntityStatus(m_nWaterCannonEntity, TRUE); + + m_nPoliceChannelEntity = CreateEntity(AUDIOTYPE_POLICERADIO, (void *)1); + if (m_nPoliceChannelEntity >= 0) + SetEntityStatus(m_nPoliceChannelEntity, TRUE); + + m_nEscalatorEntity = CreateEntity(AUDIOTYPE_ESCALATOR, (void*)1); + if (m_nEscalatorEntity >= 0) + SetEntityStatus(m_nEscalatorEntity, TRUE); + + m_nExtraSoundsEntity = CreateEntity(AUDIOTYPE_EXTRA_SOUNDS, (void*)1); + if (m_nExtraSoundsEntity >= 0) + SetEntityStatus(m_nExtraSoundsEntity, TRUE); + +#ifdef GTA_BRIDGE + m_nBridgeEntity = CreateEntity(AUDIOTYPE_BRIDGE, (void*)1); + if (m_nBridgeEntity >= 0) + SetEntityStatus(m_nBridgeEntity, TRUE); +#endif // GTA_BRIDGE + + for (int i = 0; i < MISSION_AUDIO_SLOTS; i++) { + m_nMissionAudioSampleIndex[i] = NO_SAMPLE; + m_nMissionAudioLoadingStatus[i] = LOADING_STATUS_NOT_LOADED; + m_nMissionAudioPlayStatus[i] = PLAY_STATUS_STOPPED; + m_bIsMissionAudioPlaying[i] = FALSE; + m_bIsMissionAudioAllowedToPlay[i] = FALSE; + m_bIsMissionAudio2D[i] = TRUE; + m_nMissionAudioFramesToPlay[i] = 0; + m_bIsMissionAudioPhoneCall[i] = FALSE; + m_nGlobalSfxVolumeMultiplier = 127; + } + + ResetAudioLogicTimers(CTimer::GetTimeInMilliseconds()); + m_bIsPlayerShutUp = FALSE; + m_nPlayerMood = PLAYER_MOOD_CALM; + m_nPlayerMoodTimer = 0; +} + +void +cAudioManager::PreTerminateGameSpecificShutdown() +{ +#ifdef GTA_BRIDGE + if (m_nBridgeEntity >= 0) { + DestroyEntity(m_nBridgeEntity); + m_nBridgeEntity = AEHANDLE_NONE; + } +#endif + if (m_nEscalatorEntity >= 0) { + DestroyEntity(m_nEscalatorEntity); + m_nEscalatorEntity = AEHANDLE_NONE; + } + if (m_nExtraSoundsEntity >= 0) { + DestroyEntity(m_nExtraSoundsEntity); + m_nExtraSoundsEntity = AEHANDLE_NONE; + } + if (m_nPoliceChannelEntity >= 0) { + DestroyEntity(m_nPoliceChannelEntity); + m_nPoliceChannelEntity = AEHANDLE_NONE; + } + if (m_nWaterCannonEntity >= 0) { + DestroyEntity(m_nWaterCannonEntity); + m_nWaterCannonEntity = AEHANDLE_NONE; + } + if (m_nFireAudioEntity >= 0) { + DestroyEntity(m_nFireAudioEntity); + m_nFireAudioEntity = AEHANDLE_NONE; + } + if (m_nCollisionEntity >= 0) { + DestroyEntity(m_nCollisionEntity); + m_nCollisionEntity = AEHANDLE_NONE; + } + if (m_nFrontEndEntity >= 0) { + DestroyEntity(m_nFrontEndEntity); + m_nFrontEndEntity = AEHANDLE_NONE; + } + if (m_nProjectileEntity >= 0) { + DestroyEntity(m_nProjectileEntity); + m_nProjectileEntity = AEHANDLE_NONE; + } +} + +void +cAudioManager::PostTerminateGameSpecificShutdown() +{ + ; +} + +void +cAudioManager::ResetAudioLogicTimers(uint32 timer) +{ + for (uint32 i = 0; i < m_nAudioEntitiesCount; i++) { + if (m_asAudioEntities[m_aAudioEntityOrderList[i]].m_nType == AUDIOTYPE_PHYSICAL) { + CPed *ped = (CPed *)m_asAudioEntities[m_aAudioEntityOrderList[i]].m_pEntity; + if (ped->IsPed()) { + ped->m_lastSoundStart = timer; + ped->m_soundStart = timer + m_anRandomTable[0] % 3000; + } + } + } + ClearMissionAudio(0); + ClearMissionAudio(1); + SampleManager.StopChannel(CHANNEL_POLICE_RADIO); +} + +void +cAudioManager::ProcessReverb() +{ +#ifdef AUDIO_REVERB +#ifdef FIX_BUGS + const uint32 numChannels = NUM_CHANNELS_GENERIC; +#else + const uint32 numChannels = NUM_CHANNELS_GENERIC+1; +#endif + + if (SampleManager.UpdateReverb() && m_bDynamicAcousticModelingStatus) { +#ifndef GTA_PS2 + for (uint32 i = 0; i < numChannels; i++) { + if (m_asActiveSamples[i].m_bReverb) + SampleManager.SetChannelReverbFlag(i, TRUE); + } +#endif + } +#endif // AUDIO_REVERB +} + +float +cAudioManager::GetDistanceSquared(const CVector &v) +{ + const CVector &c = TheCamera.GetPosition(); + return sq(v.x - c.x) + sq(v.y - c.y) + sq((v.z - c.z) * 0.2f); +} + +void +cAudioManager::CalculateDistance(bool8 &distCalculated, float dist) +{ + if (!distCalculated) { + m_sQueueSample.m_fDistance = Sqrt(dist); + distCalculated = TRUE; + } +} + +CVehicle * +cAudioManager::FindVehicleOfPlayer() +{ + CVehicle* vehicle = FindPlayerVehicle(); + CPlayerPed* ped = FindPlayerPed(); + if (vehicle == nil && ped != nil) { + CEntity *attachedTo = ped->m_attachedTo; + if (attachedTo && attachedTo->IsVehicle()) + vehicle = (CVehicle*)attachedTo; + } + return vehicle; +} + +void +cAudioManager::ProcessSpecial() +{ + CPlayerPed *playerPed; + CVehicle *remoteVehicle; + + if (m_bIsPaused) { + if (!m_bWasPaused) { + SampleManager.SetEffectsFadeVolume(MAX_VOLUME); + SampleManager.SetMusicFadeVolume(MAX_VOLUME); + } + } else { + if (!CReplay::IsPlayingBack()) + ProcessPlayerMood(); + remoteVehicle = CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle; + playerPed = FindPlayerPed(); + if (playerPed) { + if (playerPed->m_audioEntityId >= 0 && m_asAudioEntities[playerPed->m_audioEntityId].m_bIsUsed) { + if(!playerPed->EnteringCar() && !playerPed->bInVehicle && !remoteVehicle) + SampleManager.StopChannel(CHANNEL_PLAYER_VEHICLE_ENGINE); + } + } + } +} + +void +cAudioManager::ProcessEntity(int32 id) +{ + if (m_asAudioEntities[id].m_bStatus) { + m_sQueueSample.m_nEntityIndex = id; + switch (m_asAudioEntities[id].m_nType) { + case AUDIOTYPE_PHYSICAL: + if (!m_bIsPaused) { + SET_SOUND_REVERB(TRUE); + ProcessPhysical(id); + } + break; + case AUDIOTYPE_EXPLOSION: + if (!m_bIsPaused) { + SET_SOUND_REVERB(TRUE); + ProcessExplosions(id); + } + break; + case AUDIOTYPE_FIRE: + if (!m_bIsPaused) { + SET_SOUND_REVERB(TRUE); + ProcessFires(id); + } + break; + case AUDIOTYPE_WEATHER: + if (!m_bIsPaused) { + SET_SOUND_REVERB(TRUE); + if(CGame::currArea == AREA_MAIN_MAP || CGame::currArea == AREA_EVERYWHERE) + ProcessWeather(id); + } + break; +/* case AUDIOTYPE_CRANE: + if (!m_bIsPaused) { + SET_SOUND_REVERB(TRUE); + ProcessCrane(); + } + break;*/ + case AUDIOTYPE_SCRIPTOBJECT: + if (!m_bIsPaused) { + SET_SOUND_REVERB(TRUE); + ProcessScriptObject(id); + } + break; +#ifdef GTA_BRIDGE + case AUDIOTYPE_BRIDGE: + if (!m_bIsPaused) { + SET_SOUND_REVERB(TRUE); + ProcessBridge(); + } + break; +#endif + case AUDIOTYPE_FRONTEND: + SET_SOUND_REVERB(FALSE); + ProcessFrontEnd(); + break; + case AUDIOTYPE_PROJECTILE: + if (!m_bIsPaused) { + SET_SOUND_REVERB(TRUE); + ProcessProjectiles(); + } + break; + case AUDIOTYPE_GARAGE: + if (!m_bIsPaused) + ProcessGarages(); + break; + case AUDIOTYPE_FIREHYDRANT: + if (!m_bIsPaused) { + SET_SOUND_REVERB(TRUE); + ProcessFireHydrant(); + } + break; + case AUDIOTYPE_WATERCANNON: + if (!m_bIsPaused) { + SET_SOUND_REVERB(TRUE); + ProcessWaterCannon(id); + } + break; + case AUDIOTYPE_ESCALATOR: + if (!m_bIsPaused) { + SET_SOUND_REVERB(TRUE); + ProcessEscalators(); + } + break; + case AUDIOTYPE_EXTRA_SOUNDS: + if (!m_bIsPaused) { + SET_SOUND_REVERB(TRUE); + ProcessExtraSounds(); + } + break; + default: + return; + } + } +} + +void +cAudioManager::ProcessPhysical(int32 id) +{ + CPhysical *entity = (CPhysical *)m_asAudioEntities[id].m_pEntity; + if (entity) { + switch (entity->GetType()) { + case ENTITY_TYPE_VEHICLE: + ProcessVehicle((CVehicle *)m_asAudioEntities[id].m_pEntity); + break; + case ENTITY_TYPE_PED: + ProcessPed((CPhysical *)m_asAudioEntities[id].m_pEntity); + break; + default: + return; + } + } +} + +enum +{ + CAR_HELI_MAX_DIST = 250, + CAR_HELI_ENGINE_MAX_DIST = 140, + CAR_HELI_ENGINE_START_MAX_DIST = 30, + CAR_HELI_ENGINE_START_VOLUME = 70, + CAR_HELI_SEAPLANE_MAX_DIST = 20, + CAR_HELI_SEAPLANE_VOLUME = 100, + CAR_HELI_REAR_MAX_DIST = 27, + CAR_HELI_REAR_VOLUME = 25, + + FLAT_TYRE_MAX_DIST = 60, + FLAT_TYRE_VOLUME = 100, + + + RAIN_ON_VEHICLE_MAX_DIST = 22, + RAIN_ON_VEHICLE_VOLUME = 30, + + REVERSE_GEAR_MAX_DIST = 30, + REVERSE_GEAR_VOLUME = 24, + + MODEL_CAR_ENGINE_MAX_DIST = 35, + MODEL_CAR_ENGINE_VOLUME = 90, + MODEL_HELI_ENGINE_VOLUME = 70, + + VEHICLE_ROAD_NOISE_MAX_DIST = 95, + VEHICLE_ROAD_NOISE_VOLUME = 30, + + WET_ROAD_NOISE_MAX_DIST = 30, + WET_ROAD_NOISE_VOLUME = 23, + + VEHICLE_ENGINE_MAX_DIST = 50, + VEHICLE_ENGINE_BASE_VOLUME = 75, + VEHICLE_ENGINE_FULL_VOLUME = 90, + + CESNA_IDLE_MAX_DIST = 200, + CESNA_REV_MAX_DIST = 90, + CESNA_VOLUME = 80, + + PLAYER_VEHICLE_ENGINE_VOLUME = 120, + + VEHICLE_SKIDDING_MAX_DIST = 40, + VEHICLE_SKIDDING_VOLUME = 50, + + VEHICLE_HORN_MAX_DIST = 40, + VEHICLE_HORN_VOLUME = 80, + + VEHICLE_SIREN_MAX_DIST = 110, + VEHICLE_SIREN_VOLUME = 80, + + VEHICLE_REVERSE_WARNING_MAX_DIST = 50, + VEHICLE_REVERSE_WARNING_VOLUME = 60, + + VEHICLE_DOORS_MAX_DIST = 40, + VEHICLE_DOORS_VOLUME = 100, + + AIR_BRAKES_MAX_DIST = 30, + AIR_BRAKES_VOLUME = 70, + + ENGINE_DAMAGE_MAX_DIST = 40, + ENGINE_DAMAGE_VOLUME = 6, + ENGINE_DAMAGE_ON_FIRE_VOLUME = 60, + + CAR_BOMB_TICK_MAX_DIST = 40, + CAR_BOMB_TICK_VOLUME = 60, + + VEHICLE_ONE_SHOT_HELI_BLADE_MAX_DIST = 35, + VEHICLE_ONE_SHOT_HELI_BLADE_VOLUME = 70, + + VEHICLE_ONE_SHOT_CAR_TYRE_POP_MAX_DIST = 60, + VEHICLE_ONE_SHOT_CAR_TYRE_POP_VOLUME = 117, + + VEHICLE_ONE_SHOT_DOOR_MAX_DIST = 50, + VEHICLE_ONE_SHOT_DOOR_OPEN_VOLUME = 122, + VEHICLE_ONE_SHOT_DOOR_CLOSE_VOLUME = 117, + + VEHICLE_ONE_SHOT_WINDSHIELD_CRACK_MAX_DIST = 40, + VEHICLE_ONE_SHOT_WINDSHIELD_CRACK_VOLUME = 80, + + VEHICLE_ONE_SHOT_CAR_JUMP_MAX_DIST = 35, + VEHICLE_ONE_SHOT_CAR_JUMP_VOLUME = 80, + + VEHICLE_ONE_SHOT_CAR_ENGINE_START_MAX_DIST = 40, + VEHICLE_ONE_SHOT_CAR_ENGINE_START_VOLUME = 60, + + VEHICLE_ONE_SHOT_CAR_LIGHT_BREAK_VOLUME = 30, + + VEHICLE_ONE_SHOT_CAR_HYDRAULIC_MAX_DIST = 35, + VEHICLE_ONE_SHOT_CAR_HYDRAULIC_VOLUME = 55, + + VEHICLE_ONE_SHOT_CAR_SPLASH_MAX_DIST = 60, + VEHICLE_ONE_SHOT_CAR_SPLASH_VOLUME = 35, + + VEHICLE_ONE_SHOT_BOAT_SLOWDOWN_MAX_DIST = 50, + + VEHICLE_ONE_SHOT_TRAIN_DOOR_MAX_DIST = 35, + VEHICLE_ONE_SHOT_TRAIN_DOOR_VOLUME = 70, + + VEHICLE_ONE_SHOT_CAR_TANK_TURRET_MAX_DIST = 40, + VEHICLE_ONE_SHOT_CAR_TANK_TURRET_VOLUME = 90, + + VEHICLE_ONE_SHOT_CAR_BOMB_TICK_MAX_DIST = 30, + VEHICLE_ONE_SHOT_CAR_BOMB_TICK_VOLUME = CAR_BOMB_TICK_VOLUME, + + VEHICLE_ONE_SHOT_PLANE_ON_GROUND_MAX_DIST = 180, + VEHICLE_ONE_SHOT_PLANE_ON_GROUND_VOLUME = 75, + + VEHICLE_ONE_SHOT_WEAPON_SHOT_FIRED_MAX_DIST = 120, + VEHICLE_ONE_SHOT_WEAPON_SHOT_FIRED_VOLUME = 65, + + VEHICLE_ONE_SHOT_WEAPON_HIT_VEHICLE_MAX_DIST = 40, + VEHICLE_ONE_SHOT_WEAPON_HIT_VEHICLE_VOLUME = 90, + + VEHICLE_ONE_SHOT_BOMB_ARMED_MAX_DIST = 50, + VEHICLE_ONE_SHOT_BOMB_ARMED_VOLUME = 50, + + VEHICLE_ONE_SHOT_WATER_FALL_MAX_DIST = 40, + VEHICLE_ONE_SHOT_WATER_FALL_VOLUME = 90, + + VEHICLE_ONE_SHOT_SPLATTER_MAX_DIST = 40, + VEHICLE_ONE_SHOT_SPLATTER_VOLUME = 55, + + VEHICLE_ONE_SHOT_CAR_PED_COLLISION_MAX_DIST = 40, + + TRAIN_NOISE_FAR_MAX_DIST = 300, + TRAIN_NOISE_NEAR_MAX_DIST = 70, + TRAIN_NOISE_VOLUME = 75, + + BOAT_ENGINE_MAX_DIST = 90, + BOAT_ENGINE_REEFER_IDLE_VOLUME = 80, + + BOAT_ENGINE_LOW_ACCEL_VOLUME = 45, + BOAT_ENGINE_HIGH_ACCEL_MIN_VOLUME = 15, + BOAT_ENGINE_HIGH_ACCEL_VOLUME_MULT = 105, + + BOAT_MOVING_OVER_WATER_MAX_DIST = 50, + BOAT_MOVING_OVER_WATER_VOLUME = 30, + + JUMBO_MAX_DIST = 440, + JUMBO_RUMBLE_SOUND_MAX_DIST = 240, + JUMBO_ENGINE_SOUND_MAX_DIST = 180, + JUMBO_WHINE_SOUND_MAX_DIST = 170, + + PED_ONE_SHOT_SHIRT_FLAP_MAX_DIST = 15, + PED_ONE_SHOT_SHIRT_FLAP_VOLUME = 90, + + PED_ONE_SHOT_MINIGUN_MAX_DIST = 150, + PED_ONE_SHOT_MINIGUN_VOLUME = MAX_VOLUME, + + PED_ONE_SHOT_SKATING_MAX_DIST = 20, + PED_ONE_SHOT_SKATING_VOLUME = 70, + + PED_ONE_SHOT_STEP_MAX_DIST = 20, + PED_ONE_SHOT_STEP_VOLUME = 45, + + PED_ONE_SHOT_FALL_MAX_DIST = 30, + PED_ONE_SHOT_FALL_VOLUME = 80, + + PED_ONE_SHOT_PUNCH_MAX_DIST = 30, + PED_ONE_SHOT_PUNCH_VOLUME = 100, + + PED_ONE_SHOT_WEAPON_COLT45_VOLUME = 90, + PED_ONE_SHOT_WEAPON_UZI_VOLUME = 70, + PED_ONE_SHOT_WEAPON_SHOTGUN_VOLUME = 100, + PED_ONE_SHOT_WEAPON_M4_VOLUME = 90, + PED_ONE_SHOT_WEAPON_M16_VOLUME = MAX_VOLUME, + PED_ONE_SHOT_WEAPON_SNIPERRIFLE_VOLUME = 110, + PED_ONE_SHOT_WEAPON_ROCKETLAUNCHER_VOLUME = 80, + + PED_ONE_SHOT_WEAPON_FLAMETHROWER_MAX_DIST = 60, + PED_ONE_SHOT_WEAPON_FLAMETHROWER_VOLUME = 90, + + PED_ONE_SHOT_WEAPON_RELOAD_MAX_DIST = 30, + PED_ONE_SHOT_WEAPON_RELOAD_VOLUME = 75, + + PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST = 120, + PED_ONE_SHOT_WEAPON_BULLET_ECHO_VOLUME = 80, + + PED_ONE_SHOT_WEAPON_FLAMETHROWER_FIRE_MAX_DIST = 60, + PED_ONE_SHOT_WEAPON_FLAMETHROWER_FIRE_VOLUME = 70, + + PED_ONE_SHOT_WEAPON_CHAINSAW_MAX_DIST = 60, + PED_ONE_SHOT_WEAPON_CHAINSAW_IDLE_MAX_DIST = 50, + PED_ONE_SHOT_WEAPON_CHAINSAW_VOLUME = 100, + + PED_ONE_SHOT_WEAPON_HIT_PED_MAX_DIST = 30, + PED_ONE_SHOT_WEAPON_HIT_PED_VOLUME = 90, + + PED_ONE_SHOT_SPLASH_MAX_DIST = 40, + PED_ONE_SHOT_SPLASH_PED_VOLUME = 70, + + PED_COMMENT_MAX_DIST = 40, + PED_COMMENT_POLICE_HELI_MAX_DIST = 400, + + EXPLOSION_DEFAULT_MAX_DIST = 200, + EXPLOSION_MOLOTOV_MAX_DIST = 150, + EXPLOSION_MINE_MAX_DIST = 200, + + FIRE_DEFAULT_MAX_DIST = 80, + FIRE_DEFAULT_VOLUME = 80, + FIRE_BUILDING_MAX_DIST = 80, + FIRE_BUILDING_VOLUME = 100, + FIRE_PED_MAX_DIST = 25, + FIRE_PED_VOLUME = 60, + FIRE_EXTINGUISH_VOLUME = 100, + + WATER_CANNON_MAX_DIST = 30, + WATER_CANNON_VOLUME = 50, + + SCRIPT_OBJECT_SEAPLANE_LOW_FUEL_MAX_DIST = 1000, + SCRIPT_OBJECT_SEAPLANE_LOW_FUEL_VOLUME = 100, + + SCRIPT_OBJECT_WILLIE_CARD_SWIPE_MAX_DIST = 40, + SCRIPT_OBJECT_WILLIE_CARD_SWIPE_VOLUME = 70, + + SCRIPT_OBJECT_GATE_MAX_DIST = 40, + + SCRIPT_OBJECT_BULLET_HIT_GROUND_MAX_DIST = 50, + SCRIPT_OBJECT_BULLET_HIT_GROUND_VOLUME = 90, + + SCRIPT_OBJECT_PAYPHONE_RINGING_MAX_DIST = 80, + SCRIPT_OBJECT_PAYPHONE_RINGING_VOLUME = 80, + + SCRIPT_OBJECT_GLASS_BREAK_MAX_DIST = 60, + SCRIPT_OBJECT_GLASS_BREAK_LONG_VOLUME = 70, + SCRIPT_OBJECT_GLASS_BREAK_SHORT_VOLUME = 60, + + SCRIPT_OBJECT_GLASS_LIGHT_BREAK_MAX_DIST = 55, + SCRIPT_OBJECT_GLASS_LIGHT_BREAK_VOLUME = 25, + + SCRIPT_OBJECT_BOX_DESTROYED_MAX_DIST = 60, + SCRIPT_OBJECT_BOX_DESTROYED_VOLUME = 80, + + SCRIPT_OBJECT_METAL_COLLISION_VOLUME = 70, + SCRIPT_OBJECT_TIRE_COLLISION_VOLUME = 60, + SCRIPT_OBJECT_HIT_BALL_VOLUME = 60, + + SCRIPT_OBJECT_GUNSHELL_MAX_DIST = 20, + SCRIPT_OBJECT_GUNSHELL_VOLUME = 30, + + SCRIPT_OBJECT_POLICE_CELL_DOOR_CLUNK_MAX_DIST = 40, + SCRIPT_OBJECT_POLICE_CELL_DOOR_CLUNK_VOLUME = 60, + + SCRIPT_OBJECT_GARAGE_DOOR_CLUNK_MAX_DIST = 80, + SCRIPT_OBJECT_GARAGE_DOOR_CLUNK_VOLUME = 60, + + //SCRIPT_OBJECT_SHORT_MAX_DIST = 30, + SCRIPT_OBJECT_LONG_MAX_DIST = 80, + SCRIPT_OBJECT_DEFAULT_VOLUME = MAX_VOLUME, + SCRIPT_OBJECT_BANK_ALARM_VOLUME = 90, + SCRIPT_OBJECT_SNORING_MAX_DIST = 6, + SCRIPT_OBJECT_SNORING_VOLUME = 25, + SCRIPT_OBJECT_GARAGE_DOOR_SLIDING_VOLUME = 90, + SCRIPT_OBJECT_SHOOTING_RANGE_TARGET_MOVING_MAX_DIST = 40, + SCRIPT_OBJECT_SHOOTING_RANGE_TARGET_MOVING_VOLUME = 60, + SCRIPT_OBJECT_NEW_WATERFALL_VOLUME = 30, + + FRONTEND_VOLUME = 127, + + //CRANE_MAX_DIST = 80, + //CRANE_VOLUME = 100, + + PROJECTILE_ROCKET_MAX_DIST = 90, + PROJECTILE_ROCKET_VOLUME = MAX_VOLUME, + + PROJECTILE_MOLOTOV_MAX_DIST = 30, + PROJECTILE_MOLOTOV_VOLUME = 50, + + PROJECTILE_TEARGAS_MAX_DIST = 40, + PROJECTILE_TEARGAS_VOLUME = 80, + + ESCALATOR_MAX_DIST = 30, + ESCALATOR_VOLUME = 26, + + ARCADE_MAX_DIST = 18, + ARCADE_VOLUME = 50, + + GARAGES_MAX_DIST = 80, + GARAGES_VOLUME = 90, + GARAGES_DOOR_VOLUME = 60, + + FIRE_HYDRANT_MAX_DIST = 35, + FIRE_HYDRANT_VOLUME = 40, + + BRIDGE_MOTOR_MAX_DIST = 400, + BRIDGE_MOTOR_VOLUME = MAX_VOLUME, + BRIDGE_MAX_DIST = BRIDGE_MOTOR_MAX_DIST + 50, + + BRIDGE_WARNING_VOLUME = 100, + + MISSION_AUDIO_MAX_DIST = 80, + MISSION_AUDIO_VOLUME = 80, +}; + +#pragma region VEHICLE AUDIO +enum eVehicleModel { + LANDSTAL, + IDAHO, + STINGER, + LINERUN, + PEREN, + SENTINEL, + RIO, + FIRETRUK, + TRASH, + STRETCH, + MANANA, + INFERNUS, + VOODOO, + PONY, + MULE, + CHEETAH, + AMBULAN, + FBICAR, + MOONBEAM, + ESPERANT, + TAXI, + WASHING, + BOBCAT, + MRWHOOP, + BFINJECT, + HUNTER, + POLICE, + ENFORCER, + SECURICA, + BANSHEE, + PREDATOR, + BUS, + RHINO, + BARRACKS, + CUBAN, + CHOPPER, + ANGEL, + COACH, + CABBIE, + STALLION, + RUMPO, + RCBANDIT, + ROMERO, + PACKER, + SENTXS, + ADMIRAL, + SQUALO, + SEASPAR, + PIZZABOY, + GANGBUR, + AIRTRAIN, + DEADDODO, + SPEEDER, + REEFER, + TROPIC, + FLATBED, + YANKEE, + CADDY, + ZEBRA, + TOPFUN, + SKIMMER, + PCJ600, + FAGGIO, + FREEWAY, + RCBARON, + RCRAIDER, + GLENDALE, + OCEANIC, + SANCHEZ, + SPARROW, + PATRIOT, + LOVEFIST, + COASTG, + DINGHY, + HERMES, + SABRE, + SABRETUR, + PHEONIX, + WALTON, + REGINA, + COMET, + DELUXO, + BURRITO, + SPAND, + MARQUIS, + BAGGAGE, + KAUFMAN, + MAVERICK, + VCNMAV, + RANCHER, + FBIRANCH, + VIRGO, + GREENWOO, + JETMAX, + HOTRING, + SANDKING, + BLISTAC, + POLMAV, + BOXVILLE, + BENSON, + MESA, + RCGOBLIN, + HOTRINA, + HOTRINB, + BLOODRA, + BLOODRB, + VICECHEE, + CAR237, + CAR238, + CAR239, + MAX_CARS, + + // HACK so this compiles + // TODO(MIAMI): check it out + DODO = -1 +}; + +enum +{ + OLD_DOOR = 0, + NEW_DOOR, + TRUCK_DOOR, + BUS_DOOR, +}; + + +struct tVehicleSampleData { + eSfxSample m_nAccelerationSampleIndex; + uint8 m_nBank; + eSfxSample m_nHornSample; + int32 m_nHornFrequency; + uint8 m_nSirenOrAlarmSample; + int32 m_nSirenOrAlarmFrequency; + uint8 m_bDoorType; +}; + +const tVehicleSampleData aVehicleSettings[MAX_CARS] = { + {SFX_CAR_REV_10, SFX_BANK_PATHFINDER, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9935, OLD_DOOR}, + {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_56CHEV, 11487, SFX_CAR_HORN_JEEP, 9900, OLD_DOOR}, + {SFX_CAR_REV_2, SFX_BANK_PORSCHE, SFX_CAR_HORN_PORSCHE, 11025, SFX_CAR_HORN_JEEP, 9890, NEW_DOOR}, + {SFX_CAR_REV_5, SFX_BANK_TRUCK, SFX_CAR_HORN_TRUCK, 29711, SFX_CAR_HORN_JEEP, 9960, TRUCK_DOOR}, + {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_56CHEV, 12893, SFX_CAR_HORN_JEEP, 9500, OLD_DOOR}, + {SFX_CAR_REV_4, SFX_BANK_MERC, SFX_CAR_HORN_BMW328, 10706, SFX_CAR_HORN_JEEP, 9600, NEW_DOOR}, + {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_TRUCK, 29711, SFX_CAR_HORN_JEEP, 9700, NEW_DOOR}, + {SFX_CAR_REV_5, SFX_BANK_TRUCK, SFX_CAR_HORN_TRUCK, 29711, SFX_POLICE_SIREN_SLOW, 10588, TRUCK_DOOR}, + {SFX_CAR_REV_5, SFX_BANK_TRUCK, SFX_CAR_HORN_TRUCK, 31478, SFX_CAR_HORN_JEEP, 9800, TRUCK_DOOR}, + {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_BMW328, 9538, SFX_CAR_HORN_JEEP, 9900, NEW_DOOR}, + {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_56CHEV, 10842, SFX_CAR_HORN_JEEP, 10000, OLD_DOOR}, + {SFX_CAR_REV_7, SFX_BANK_COBRA, SFX_CAR_HORN_BMW328, 12017, SFX_CAR_HORN_JEEP, 9900, NEW_DOOR}, + {SFX_CAR_REV_9, SFX_BANK_CADILLAC, SFX_CAR_HORN_JEEP, 22293, SFX_CAR_HORN_JEEP, 9800, NEW_DOOR}, + {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_BUS2, 18000, SFX_CAR_HORN_JEEP, 9700, OLD_DOOR}, + {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_BUS, 18286, SFX_CAR_HORN_JEEP, 9600, OLD_DOOR}, + {SFX_CAR_REV_2, SFX_BANK_PORSCHE, SFX_CAR_HORN_PORSCHE, 11025, SFX_CAR_HORN_JEEP, 9500, NEW_DOOR}, + {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_JEEP, 22295, SFX_AMBULANCE_SIREN_SLOW, 12688, OLD_DOOR}, + {SFX_CAR_REV_4, SFX_BANK_MERC, SFX_CAR_HORN_PORSCHE, 9271, SFX_POLICE_SIREN_SLOW, 11471, NEW_DOOR}, + {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_56CHEV, 12170, SFX_CAR_HORN_JEEP, 9400, OLD_DOOR}, + {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_BMW328, 11000, SFX_CAR_HORN_JEEP, 9300, OLD_DOOR}, + {SFX_CAR_REV_10, SFX_BANK_PATHFINDER, SFX_CAR_HORN_BMW328, 10796, SFX_CAR_HORN_JEEP, 9200, NEW_DOOR}, + {SFX_CAR_REV_4, SFX_BANK_MERC, SFX_CAR_HORN_BMW328, 10500, SFX_CAR_HORN_JEEP, 9100, NEW_DOOR}, + {SFX_CAR_REV_10, SFX_BANK_PATHFINDER, SFX_CAR_HORN_PICKUP, 10924, SFX_CAR_HORN_JEEP, 9000, OLD_DOOR}, + {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_PICKUP, 11025, SFX_ICE_CREAM_TUNE, 11025, OLD_DOOR}, + {SFX_CAR_REV_6, SFX_BANK_HOTROD, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9100, OLD_DOOR}, + {SFX_HELI_APACHE_1, SFX_BANK_HELI_APACHE, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9200, NEW_DOOR}, + {SFX_CAR_REV_4, SFX_BANK_MERC, SFX_CAR_HORN_BMW328, 10706, SFX_POLICE_SIREN_SLOW, 10511, NEW_DOOR}, + {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_BUS, 17260, SFX_POLICE_SIREN_SLOW, 11029, OLD_DOOR}, + {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_PICKUP, 8670, SFX_CAR_HORN_JEEP, 9300, OLD_DOOR}, + {SFX_CAR_REV_7, SFX_BANK_COBRA, SFX_CAR_HORN_PORSCHE, 10400, SFX_CAR_HORN_JEEP, 9400, NEW_DOOR}, + {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_POLICE_SIREN_SLOW, 11912, NEW_DOOR}, + {SFX_CAR_REV_5, SFX_BANK_TRUCK, SFX_CAR_HORN_BUS2, 11652, SFX_CAR_HORN_JEEP, 9500, BUS_DOOR}, + {SFX_CAR_REV_5, SFX_BANK_TRUCK, SFX_CAR_HORN_TRUCK, 29711, SFX_CAR_HORN_JEEP, 9600, TRUCK_DOOR}, + {SFX_CAR_REV_5, SFX_BANK_TRUCK, SFX_CAR_HORN_TRUCK, 28043, SFX_CAR_HORN_JEEP, 9700, TRUCK_DOOR}, + {SFX_CAR_REV_6, SFX_BANK_HOTROD, SFX_CAR_HORN_JEEP, 25400, SFX_CAR_HORN_JEEP, 9800, OLD_DOOR}, + {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9900, NEW_DOOR}, + {SFX_CAR_REV_17, SFX_BANK_VTWIN, SFX_CAR_HORN_JEEP, 26313, SFX_CAR_HORN_JEEP, 10000, NEW_DOOR}, + {SFX_CAR_REV_5, SFX_BANK_TRUCK, SFX_CAR_HORN_BUS, 16291, SFX_CAR_HORN_JEEP, 10100, BUS_DOOR}, + {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_56CHEV, 10842, SFX_CAR_HORN_JEEP, 9900, OLD_DOOR}, + {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_56CHEV, 10233, SFX_CAR_HORN_JEEP, 9800, NEW_DOOR}, + {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_PICKUP, 8670, SFX_CAR_HORN_JEEP, 9700, OLD_DOOR}, + {SFX_RC_REV, SFX_BANK_RC, SFX_CAR_HORN_PICKUP, 20000, SFX_CAR_HORN_JEEP, 9600, NEW_DOOR}, + {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_JEEP, 21043, SFX_CAR_HORN_JEEP, 9500, NEW_DOOR}, + {SFX_CAR_REV_5, SFX_BANK_TRUCK, SFX_CAR_HORN_TRUCK, 29000, SFX_CAR_HORN_JEEP, 9400, TRUCK_DOOR}, + {SFX_CAR_REV_1, CAR_SFX_BANKS_OFFSET, SFX_CAR_HORN_BMW328, 9003, SFX_CAR_HORN_JEEP, 9300, NEW_DOOR}, + {SFX_CAR_REV_4, SFX_BANK_MERC, SFX_CAR_HORN_PORSCHE, 12375, SFX_CAR_HORN_JEEP, 9200, NEW_DOOR}, + {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_BUS2, 15554, SFX_CAR_HORN_JEEP, 9100, NEW_DOOR}, + {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_BUS2, 13857, SFX_CAR_HORN_JEEP, 9000, TRUCK_DOOR}, + {SFX_MOPED_REV, SFX_BANK_MOPED, SFX_CAR_HORN_JEEP, 30000, SFX_CAR_HORN_JEEP, 9100, NEW_DOOR}, + {SFX_CAR_REV_7, SFX_BANK_COBRA, SFX_CAR_HORN_JEEP, 22043, SFX_CAR_HORN_JEEP, 9200, OLD_DOOR}, + {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 21043, SFX_CAR_HORN_JEEP, 9300, NEW_DOOR}, + {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 21043, SFX_CAR_HORN_JEEP, 9400, NEW_DOOR}, + {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 21043, SFX_CAR_HORN_JEEP, 9500, NEW_DOOR}, + {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 21043, SFX_CAR_HORN_JEEP, 9600, NEW_DOOR}, + {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 21043, SFX_CAR_HORN_JEEP, 9700, NEW_DOOR}, + {SFX_CAR_REV_5, SFX_BANK_TRUCK, SFX_CAR_HORN_TRUCK, 28043, SFX_CAR_HORN_JEEP, 9800, TRUCK_DOOR}, + {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_BUS, 18286, SFX_CAR_HORN_JEEP, 9900, OLD_DOOR}, + {SFX_CAR_REV_12, SFX_BANK_GOLF_CART, SFX_CAR_HORN_JEEP, 28500, SFX_CAR_HORN_JEEP, 9800, NEW_DOOR}, + {SFX_CAR_REV_1, CAR_SFX_BANKS_OFFSET, SFX_CAR_HORN_56CHEV, 10842, SFX_CAR_HORN_JEEP, 9700, OLD_DOOR}, + {SFX_CAR_REV_8, SFX_BANK_PONTIAC_SLOW, SFX_CAR_HORN_BUS2, 18000, SFX_CAR_HORN_JEEP, 9700, OLD_DOOR}, + {SFX_SEAPLANE_PRO1, SFX_BANK_PLANE_SEAPLANE, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9700, NEW_DOOR}, + {SFX_CAR_REV_20, SFX_BANK_SPORTS_BIKE, SFX_CAR_HORN_JEEP, 27000, SFX_CAR_HORN_JEEP, 9600, NEW_DOOR}, + {SFX_MOPED_REV, SFX_BANK_MOPED, SFX_CAR_HORN_JEEP, 31000, SFX_CAR_HORN_JEEP, 9500, NEW_DOOR}, + {SFX_CAR_REV_17, SFX_BANK_VTWIN, SFX_CAR_HORN_PICKUP, 11000, SFX_CAR_HORN_JEEP, 9400, NEW_DOOR}, + {SFX_RC_REV, SFX_BANK_RC, SFX_CAR_HORN_JEEP, 30000, SFX_CAR_HORN_JEEP, 15000, NEW_DOOR}, + {SFX_CAR_RC_HELI, SFX_BANK_RC_HELI, SFX_CAR_HORN_JEEP, 30000, SFX_CAR_HORN_JEEP, 15000, NEW_DOOR}, + {SFX_CAR_REV_9, SFX_BANK_CADILLAC, SFX_CAR_HORN_56CHEV, 10300, SFX_CAR_HORN_JEEP, 9100, OLD_DOOR}, + {SFX_CAR_REV_9, SFX_BANK_CADILLAC, SFX_CAR_HORN_56CHEV, 10500, SFX_CAR_HORN_JEEP, 9000, OLD_DOOR}, + {SFX_CAR_REV_19, SFX_BANK_HONDA250, SFX_CAR_HORN_JEEP, 30000, SFX_CAR_HORN_JEEP, 9000, NEW_DOOR}, + {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9100, TRUCK_DOOR}, + {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_TRUCK, 28000, SFX_CAR_HORN_JEEP, 9200, TRUCK_DOOR}, + {SFX_CAR_REV_7, SFX_BANK_COBRA, SFX_CAR_HORN_PICKUP, 11200, SFX_CAR_HORN_JEEP, 9300, NEW_DOOR}, + {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9400, NEW_DOOR}, + {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9500, NEW_DOOR}, + {SFX_CAR_REV_9, SFX_BANK_CADILLAC, SFX_CAR_HORN_56CHEV, 10700, SFX_CAR_HORN_JEEP, 9600, OLD_DOOR}, + {SFX_CAR_REV_1, CAR_SFX_BANKS_OFFSET, SFX_CAR_HORN_BMW328, 9000, SFX_CAR_HORN_JEEP, 9700, OLD_DOOR}, + {SFX_CAR_REV_6, SFX_BANK_HOTROD, SFX_CAR_HORN_BMW328, 9200, SFX_CAR_HORN_JEEP, 9800, OLD_DOOR}, + {SFX_CAR_REV_7, SFX_BANK_COBRA, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9900, NEW_DOOR}, + {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_56CHEV, 10540, SFX_CAR_HORN_JEEP, 9935, TRUCK_DOOR}, + {SFX_CAR_REV_8, SFX_BANK_PONTIAC_SLOW, SFX_CAR_HORN_PICKUP, 11000, SFX_CAR_HORN_JEEP, 9700, NEW_DOOR}, + {SFX_CAR_REV_2, SFX_BANK_PORSCHE, SFX_CAR_HORN_BMW328, 9500, SFX_CAR_HORN_JEEP, 9800, NEW_DOOR}, + {SFX_CAR_REV_7, SFX_BANK_COBRA, SFX_CAR_HORN_BMW328, 9700, SFX_CAR_HORN_JEEP, 9700, NEW_DOOR}, + {SFX_CAR_REV_8, SFX_BANK_PONTIAC_SLOW, SFX_CAR_HORN_BUS2, 18000, SFX_CAR_HORN_JEEP, 9600, OLD_DOOR}, + {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_BUS, 18000, SFX_CAR_HORN_JEEP, 9500, TRUCK_DOOR}, + {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9400, NEW_DOOR}, + {SFX_CAR_REV_8, SFX_BANK_PONTIAC_SLOW, SFX_CAR_HORN_JEEP, 27513, SFX_CAR_HORN_JEEP, 9300, NEW_DOOR}, + {SFX_CAR_REV_8, SFX_BANK_PONTIAC_SLOW, SFX_CAR_HORN_56CHEV, 10700, SFX_CAR_HORN_JEEP, 9200, OLD_DOOR}, + {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9100, TRUCK_DOOR}, + {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9000, TRUCK_DOOR}, + {SFX_CAR_REV_10, SFX_BANK_PATHFINDER, SFX_CAR_HORN_BUS2, 18000, SFX_CAR_HORN_JEEP, 9100, TRUCK_DOOR}, + {SFX_CAR_REV_1, CAR_SFX_BANKS_OFFSET, SFX_CAR_HORN_BUS2, 17900, SFX_POLICE_SIREN_SLOW, 10511, TRUCK_DOOR}, + {SFX_CAR_REV_4, SFX_BANK_MERC, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9200, NEW_DOOR}, + {SFX_CAR_REV_8, SFX_BANK_PONTIAC_SLOW, SFX_CAR_HORN_BMW328, 9600, SFX_CAR_HORN_JEEP, 9300, NEW_DOOR}, + {SFX_CAR_REV_4, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9400, NEW_DOOR}, + {SFX_CAR_REV_7, SFX_BANK_COBRA, SFX_CAR_HORN_PORSCHE, 10000, SFX_CAR_HORN_JEEP, 9500, OLD_DOOR}, + {SFX_CAR_REV_6, SFX_BANK_HOTROD, SFX_CAR_HORN_PORSCHE, 10500, SFX_CAR_HORN_JEEP, 9600, OLD_DOOR}, + {SFX_CAR_REV_10, SFX_BANK_PATHFINDER, SFX_CAR_HORN_JEEP, 25513, SFX_CAR_HORN_JEEP, 9700, NEW_DOOR}, + {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9800, NEW_DOOR}, + {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9900, NEW_DOOR}, + {SFX_CAR_REV_10, SFX_BANK_PATHFINDER, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9800, NEW_DOOR}, + {SFX_CAR_REV_1, CAR_SFX_BANKS_OFFSET, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9700, NEW_DOOR}, + {SFX_CAR_RC_HELI, SFX_BANK_RC_HELI, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9600, NEW_DOOR}, + {SFX_CAR_REV_6, SFX_BANK_HOTROD, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9700, NEW_DOOR}, + {SFX_CAR_REV_7, SFX_BANK_COBRA, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9600, NEW_DOOR}, + {SFX_CAR_REV_1, CAR_SFX_BANKS_OFFSET, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9500, NEW_DOOR}, + {SFX_CAR_REV_9, SFX_BANK_CADILLAC, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9400, NEW_DOOR}, + {SFX_CAR_REV_2, SFX_BANK_PORSCHE, SFX_CAR_HORN_PORSCHE, 11025, SFX_POLICE_SIREN_SLOW, 11000, NEW_DOOR}, + {SFX_CAR_REV_1, CAR_SFX_BANKS_OFFSET, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9200, NEW_DOOR}, + {SFX_CAR_REV_1, CAR_SFX_BANKS_OFFSET, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9300, NEW_DOOR}, + {SFX_CAR_REV_1, CAR_SFX_BANKS_OFFSET, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9400, NEW_DOOR} }; + +bool8 bPlayerJustEnteredCar; + +Const static bool8 HornPattern[8][44] = { + {FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, + FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE}, + {FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, + TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE}, + {FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, + FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE}, + {FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, + TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE}, + {FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, + FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}, + {FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, + FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}, + {FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, + TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE}, + {FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, + FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE}, +}; + +void +cAudioManager::ProcessVehicle(CVehicle* veh) +{ + cVehicleParams params; + + if (FindVehicleOfPlayer() != veh) { + switch (CGame::currArea) + { + case AREA_OVALRING: + case AREA_BLOOD: + case AREA_DIRT: + case AREA_EVERYWHERE: + case AREA_MALL: + case AREA_MAIN_MAP: + break; + default: + return; + } + } + + m_sQueueSample.m_vecPos = veh->GetPosition(); + params.m_bDistanceCalculated = FALSE; + params.m_pVehicle = veh; + params.m_fDistance = GetDistanceSquared(m_sQueueSample.m_vecPos); + params.m_pTransmission = veh->pHandling != nil ? &veh->pHandling->Transmission : nil; + params.m_nIndex = veh->m_modelIndex - MI_FIRST_VEHICLE; + if (veh->GetStatus() == STATUS_SIMPLE) + params.m_fVelocityChange = veh->AutoPilot.m_fMaxTrafficSpeed * 0.02f; + else + params.m_fVelocityChange = DotProduct(veh->m_vecMoveSpeed, veh->GetForward()); + params.m_VehicleType = veh->m_vehType; + + if (CGame::currArea == AREA_MALL && FindVehicleOfPlayer() != veh) { + ProcessVehicleOneShots(params); + ProcessVehicleSirenOrAlarm(params); + ProcessEngineDamage(params); + return; + } + switch (params.m_VehicleType) { + case VEHICLE_TYPE_CAR: + UpdateGasPedalAudio(veh, params.m_VehicleType); + if (veh->m_modelIndex == MI_RCBANDIT || veh->m_modelIndex == MI_RCBARON) { + ProcessModelVehicle(params); + ProcessEngineDamage(params); + } else if (veh->m_modelIndex == MI_RCRAIDER || veh->m_modelIndex == MI_RCGOBLIN) { + ProcessModelHeliVehicle(params); + ProcessEngineDamage(params); + } else { + switch (veh->GetVehicleAppearance()) { + case VEHICLE_APPEARANCE_HELI: + ProcessCarHeli(params); + ProcessVehicleFlatTyre(params); + ProcessEngineDamage(params); + break; + case VEHICLE_APPEARANCE_BOAT: + case VEHICLE_APPEARANCE_PLANE: + break; + default: + if (ProcessVehicleRoadNoise(params)) { + ProcessReverseGear(params); + if (CWeather::WetRoads > 0.0f) + ProcessWetRoadNoise(params); + ProcessVehicleSkidding(params); + ProcessVehicleFlatTyre(params); + ProcessVehicleHorn(params); + ProcessVehicleSirenOrAlarm(params); + if (UsesReverseWarning(params.m_nIndex)) + ProcessVehicleReverseWarning(params); + if(HasAirBrakes(params.m_nIndex)) + ProcessAirBrakes(params); + ProcessCarBombTick(params); + ProcessVehicleEngine(params); + ProcessEngineDamage(params); + ProcessVehicleDoors(params); + } + break; + } + } + ProcessVehicleOneShots(params); + ((CAutomobile*)veh)->m_fVelocityChangeForAudio = params.m_fVelocityChange; + break; + case VEHICLE_TYPE_BOAT: + if (veh->m_modelIndex == MI_SKIMMER) + ProcessCarHeli(params); + else + ProcessBoatEngine(params); + ProcessBoatMovingOverWater(params); + ProcessVehicleOneShots(params); + break; + case VEHICLE_TYPE_HELI: + ProcessCarHeli(params); + ProcessVehicleOneShots(params); + break; + case VEHICLE_TYPE_PLANE: + ProcessPlane(params); + ProcessVehicleOneShots(params); + ProcessVehicleFlatTyre(params); + break; + case VEHICLE_TYPE_BIKE: + UpdateGasPedalAudio(veh, params.m_VehicleType); + if (ProcessVehicleRoadNoise(params)) { + if (CWeather::WetRoads > 0.0f) + ProcessWetRoadNoise(params); + ProcessVehicleSkidding(params); + ProcessVehicleHorn(params); + ProcessVehicleSirenOrAlarm(params); + ProcessCarBombTick(params); + ProcessEngineDamage(params); + ProcessVehicleEngine(params); + ProcessVehicleFlatTyre(params); + } + ProcessVehicleOneShots(params); + ((CBike*)veh)->m_fVelocityChangeForAudio = params.m_fVelocityChange; + break; + default: + break; + } + ProcessRainOnVehicle(params); +} + +bool8 +cAudioManager::ProcessCarHeli(cVehicleParams& params) +{ + CAutomobile* automobile; + CBoat* boat; + + uint8 Vol; + int16 brakeState; + int16 accelerateState; + uint32 freq; + float propellerSpeed; + float freqModifier; //may be relate to angle with horison + float cameraAngle; + bool8 distanceCalculatedOld; + float distanceOld; + CVector vecPosOld; + + float volumeModifier;//TODO find better name + bool8 hunterBool; + CMatrix cameraMatrix; + + boat = nil; + automobile = nil; + hunterBool = FALSE; + + static uint32 freqFrontPrev = 14287; + static uint32 freqPropellerPrev = 7143; + static uint32 freqSkimmerPrev = 14287; + + + if (params.m_fDistance < SQR(CAR_HELI_MAX_DIST)) { + if (FindPlayerVehicle() == params.m_pVehicle) { + accelerateState = Pads[0].GetAccelerate(); + brakeState = Pads[0].GetBrake(); + } else { + accelerateState = params.m_pVehicle->m_fGasPedal * 255.0f; + brakeState = params.m_pVehicle->m_fBrakePedal * 255.0f; + } + cameraMatrix = TheCamera.GetMatrix(); + freqModifier = DotProduct(cameraMatrix.GetUp(), CVector(0.0f, 1.0f, 0.0f)); + freqModifier = ABS(freqModifier); + cameraAngle = (DotProduct(params.m_pVehicle->GetMatrix().GetForward(), cameraMatrix.GetForward()) + 1.0f) / 2.0f; + if (params.m_pVehicle->GetModelIndex() == MI_SKIMMER) { + boat = (CBoat*)params.m_pVehicle; + propellerSpeed = boat->m_fMovingSpeed * 50.0f / 11.0f; + } else if (params.m_VehicleType == VEHICLE_TYPE_HELI) { + propellerSpeed = 1.0f; + } else { + automobile = (CAutomobile*)params.m_pVehicle; + propellerSpeed = automobile->m_aWheelSpeed[1] * 50.0f / 11.0f; + } + + if (propellerSpeed == 0.0f) + return TRUE; + + propellerSpeed = Min(1.0f, propellerSpeed); + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + + + //sound on long distances + if (m_sQueueSample.m_fDistance < 40.0f) { + if (m_sQueueSample.m_fDistance < 25.0f) + Vol = 0; + else + Vol = (m_sQueueSample.m_fDistance - 25.0f) * (75.0f * propellerSpeed) / 15.0f; + } else + Vol = propellerSpeed * 75.0f; + if (Vol > 0) { + m_sQueueSample.m_nVolume = ComputeVolume(Vol, CAR_HELI_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 88; + if (boat != nil) { + m_sQueueSample.m_nSampleIndex = SFX_SEAPLANE_PRO3; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + if (accelerateState > 0 || brakeState > 0) + m_sQueueSample.m_nFrequency = 4600 + Min(1.0f, (Max(accelerateState, brakeState) / 255.0f) * freqModifier) * 563; + else + m_sQueueSample.m_nFrequency = 3651 + Min(1.0f, freqModifier) * 949; + } else { + m_sQueueSample.m_nSampleIndex = SFX_HELI_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + } + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 2; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 6.0f; + m_sQueueSample.m_MaxDistance = CAR_HELI_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 5; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + + CVector backPropellerPos; + if (automobile != nil) + automobile->GetComponentWorldPosition(CAR_BOOT, backPropellerPos); + else if (params.m_VehicleType == VEHICLE_TYPE_HELI) +#ifdef FIX_BUGS + backPropellerPos = +#endif + params.m_pVehicle->GetMatrix() * CVector(0.0f, -10.0f, 0.0f); + else + backPropellerPos = m_sQueueSample.m_vecPos; + + if (params.m_fDistance < SQR(CAR_HELI_ENGINE_MAX_DIST)) { + if (propellerSpeed < 0.4f) + volumeModifier = 0.0f; + else + volumeModifier = (propellerSpeed - 0.4f) * 5.0f / 3.0f; + if (!boat) { + freq = Min(1300, 7000 * freqModifier); + if (FindPlayerVehicle() == params.m_pVehicle && (accelerateState > 0 || brakeState > 0) && freq < 1300)//unnesesary freqModifier alredy <= 1300 + freq = 1300; + if (params.m_pVehicle->GetModelIndex() == MI_HUNTER) + hunterBool = TRUE; + } + + //sound from front of helicopter + Vol = (1.0f - cameraAngle) * volumeModifier * MAX_VOLUME; + if (!boat) { + if (accelerateState > 0 || brakeState > 0) + m_sQueueSample.m_nFrequency = 18000 + Min(1.0f, freqModifier * (Max(accelerateState, brakeState) / 255.0f)) * 2204; + else + m_sQueueSample.m_nFrequency = 14287 + Min(1.0f, freqModifier) * 3713; + if (propellerSpeed < 1.0f) + m_sQueueSample.m_nFrequency = (propellerSpeed + 1.0f) * (m_sQueueSample.m_nFrequency >> 1); + m_sQueueSample.m_nFrequency = Clamp2(m_sQueueSample.m_nFrequency, freqFrontPrev, 197); + freqFrontPrev = m_sQueueSample.m_nFrequency; + } + + m_sQueueSample.m_nVolume = ComputeVolume(Vol, CAR_HELI_ENGINE_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 3; + if (hunterBool) { + m_sQueueSample.m_nSampleIndex = SFX_HELI_APACHE_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = (volumeModifier + 1.0f) * 16000 + freq; + } else if (boat) { + m_sQueueSample.m_nSampleIndex = SFX_SEAPLANE_PRO1; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + } else { + m_sQueueSample.m_nSampleIndex = SFX_CAR_HELI_MAI; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = (volumeModifier + 1.0f) * 16000 + freq; + } + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 6.0f; + m_sQueueSample.m_MaxDistance = CAR_HELI_ENGINE_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 5; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + + + //engine starting sound + if (boat == nil && params.m_VehicleType != VEHICLE_TYPE_HELI && m_sQueueSample.m_fDistance < CAR_HELI_ENGINE_START_MAX_DIST && automobile->bEngineOn && propellerSpeed < 1.0f) { //strange way to check if automobile != nil + Vol = (1.0f - propellerSpeed / 2.0f) * CAR_HELI_ENGINE_START_VOLUME; + m_sQueueSample.m_nVolume = ComputeVolume(Vol, CAR_HELI_ENGINE_START_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + if (hunterBool) { + m_sQueueSample.m_nSampleIndex = SFX_HELI_APACHE_4; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = 3000 * propellerSpeed + 30000; + } else { + m_sQueueSample.m_nSampleIndex = SFX_CAR_HELI_STA; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = 3000 * propellerSpeed + 6000; + } + m_sQueueSample.m_nCounter = 12; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 6.0f; + m_sQueueSample.m_MaxDistance = CAR_HELI_ENGINE_START_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 30; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + + //after accel rotor sound + + Vol = ((cameraAngle + 1.0f) * volumeModifier * MAX_VOLUME) / 2.0f; + if (boat) { + if (accelerateState > 0 || brakeState > 0) + m_sQueueSample.m_nFrequency = 9000 + Min(1.0f, (Max(accelerateState, brakeState) / 255) * freqModifier) * 1102; + else + m_sQueueSample.m_nFrequency = 7143 + Min(1.0f, freqModifier) * 1857; + + if (propellerSpeed < 1.0f) + m_sQueueSample.m_nFrequency = (propellerSpeed + 1) * (m_sQueueSample.m_nFrequency >> 1); + + m_sQueueSample.m_nFrequency = Clamp2(m_sQueueSample.m_nFrequency, freqPropellerPrev, 98); + freqPropellerPrev = m_sQueueSample.m_nFrequency; + } + m_sQueueSample.m_nVolume = ComputeVolume(Vol, CAR_HELI_ENGINE_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 1; + if (hunterBool) { + m_sQueueSample.m_nSampleIndex = SFX_HELI_APACHE_2; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = (volumeModifier + 1) * 16000 + freq; + } else if (boat) { + m_sQueueSample.m_nSampleIndex = SFX_SEAPLANE_PRO2; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + } else { + m_sQueueSample.m_nSampleIndex = SFX_CAR_HELI_MAI2; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = (volumeModifier + 1) * 16000 + freq; + } + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 6.0f; + m_sQueueSample.m_MaxDistance = CAR_HELI_ENGINE_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 5; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + + if (boat) { + if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_FIXED && m_sQueueSample.m_fDistance < CAR_HELI_SEAPLANE_MAX_DIST && propellerSpeed > 0.0f) { + m_sQueueSample.m_nVolume = ComputeVolume(propellerSpeed * CAR_HELI_SEAPLANE_VOLUME, CAR_HELI_SEAPLANE_MAX_DIST, m_sQueueSample.m_fDistance); + if (accelerateState > 0 || brakeState > 0) + m_sQueueSample.m_nFrequency = 18000 + Min(1.0f, (Max(accelerateState, brakeState) / 255.0f) * freqModifier) * 2204; + else + m_sQueueSample.m_nFrequency = 14287 + Min(1.0f, freqModifier) * 3713; + if (propellerSpeed < 1.0f) + m_sQueueSample.m_nFrequency = (propellerSpeed + 1) * (m_sQueueSample.m_nFrequency >> 1); + m_sQueueSample.m_nFrequency = Clamp2(m_sQueueSample.m_nFrequency, freqSkimmerPrev, 197); + freqSkimmerPrev = m_sQueueSample.m_nFrequency; + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nSampleIndex = SFX_SEAPLANE_PRO4; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 12; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(propellerSpeed * CAR_HELI_SEAPLANE_VOLUME); + SET_LOOP_OFFSETS(SFX_SEAPLANE_PRO4) + m_sQueueSample.m_fSpeedMultiplier = 5.0f; + m_sQueueSample.m_MaxDistance = CAR_HELI_SEAPLANE_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 7; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + } else { + //vacuum cleaner sound + vecPosOld = m_sQueueSample.m_vecPos; + distanceCalculatedOld = params.m_bDistanceCalculated; + distanceOld = params.m_fDistance; + + m_sQueueSample.m_vecPos = backPropellerPos; + params.m_bDistanceCalculated = FALSE; + params.m_fDistance = GetDistanceSquared(m_sQueueSample.m_vecPos); + if (params.m_fDistance < SQR(CAR_HELI_REAR_MAX_DIST)) { + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + m_sQueueSample.m_nVolume = ComputeVolume(volumeModifier * CAR_HELI_REAR_VOLUME, CAR_HELI_REAR_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 2; + m_sQueueSample.m_nSampleIndex = hunterBool ? SFX_HELI_APACHE_3 : SFX_CAR_HELI_REA; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_nFrequency = (volumeModifier + 1.0f) * 16000; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(volumeModifier * CAR_HELI_REAR_VOLUME); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 6.0f; + m_sQueueSample.m_MaxDistance = CAR_HELI_REAR_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 5; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + + m_sQueueSample.m_vecPos = vecPosOld; + params.m_bDistanceCalculated = distanceCalculatedOld; + params.m_fDistance = distanceOld; + } + } + return TRUE; + } + return FALSE; +} + +void +cAudioManager::ProcessRainOnVehicle(cVehicleParams& params) +{ + if (params.m_fDistance < SQR(RAIN_ON_VEHICLE_MAX_DIST) && CWeather::Rain > 0.01f && (!CCullZones::CamNoRain() || !CCullZones::PlayerNoRain())) { + CVehicle *veh = params.m_pVehicle; + veh->m_bRainAudioCounter++; + if (veh->m_bRainAudioCounter >= 2) { + veh->m_bRainAudioCounter = 0; + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + uint8 Vol = RAIN_ON_VEHICLE_VOLUME * CWeather::Rain; + m_sQueueSample.m_nVolume = ComputeVolume(Vol, RAIN_ON_VEHICLE_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = veh->m_bRainSamplesCounter++; + if (veh->m_bRainSamplesCounter > 4) + veh->m_bRainSamplesCounter = 68; + m_sQueueSample.m_nSampleIndex = (m_anRandomTable[1] & 3) + SFX_CAR_RAIN_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 9; + m_sQueueSample.m_nFrequency = m_anRandomTable[1] % 4000 + 28000; + m_sQueueSample.m_nLoopCount = 1; + SET_EMITTING_VOLUME(Vol); + RESET_LOOP_OFFSETS + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = RAIN_ON_VEHICLE_MAX_DIST; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REVERB(FALSE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + } +} + +bool8 +cAudioManager::ProcessReverseGear(cVehicleParams& params) +{ + CVehicle *veh; + CAutomobile *automobile; + uint8 Vol; + float modificator; + + if (params.m_fDistance < SQR(REVERSE_GEAR_MAX_DIST)) { + veh = params.m_pVehicle; + if (veh->GetModelIndex() == MI_CADDY) + return TRUE; + if (veh->bEngineOn && (veh->m_fGasPedal < 0.0f || veh->m_nCurrentGear == 0)) { + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + automobile = (CAutomobile*)params.m_pVehicle; + if (automobile->m_nDriveWheelsOnGround > 0) + modificator = params.m_fVelocityChange / params.m_pTransmission->fMaxReverseVelocity; + else { + if (automobile->m_nDriveWheelsOnGroundPrev > 0) + automobile->m_fGasPedalAudio *= 0.4f; + modificator = automobile->m_fGasPedalAudio; + } + modificator = ABS(modificator); + Vol = (REVERSE_GEAR_VOLUME * modificator); + m_sQueueSample.m_nVolume = ComputeVolume(Vol, REVERSE_GEAR_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + if (params.m_pVehicle->m_fGasPedal < 0.0f) { + m_sQueueSample.m_nCounter = 61; + m_sQueueSample.m_nSampleIndex = SFX_REVERSE_GEAR; + } else { + m_sQueueSample.m_nCounter = 62; + m_sQueueSample.m_nSampleIndex = SFX_REVERSE_GEAR_2; + } + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nFrequency = (6000 * modificator) + 7000; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 3.0f; + m_sQueueSample.m_MaxDistance = REVERSE_GEAR_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 5; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + return TRUE; + } + return FALSE; +} + +void +cAudioManager::ProcessModelHeliVehicle(cVehicleParams& params) +{ + static uint32 prevFreq = 22050; + + uint32 freq; + bool8 isPlayerVeh; + int16 acceletateState; + int16 brakeState; + + if (params.m_fDistance < SQR(MODEL_CAR_ENGINE_MAX_DIST)) { + if (FindPlayerVehicle() == params.m_pVehicle) + isPlayerVeh = TRUE; + else + #ifdef FIX_BUGS + isPlayerVeh = CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle == params.m_pVehicle; + #else + isPlayerVeh = CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle != nil; + #endif + if (isPlayerVeh) { + brakeState = Pads[0].GetBrake(); + acceletateState = Max(Pads[0].GetAccelerate(), Abs(Pads[0].GetCarGunUpDown()) * 2); + } else { + acceletateState = 255.0f * params.m_pVehicle->m_fGasPedal; + brakeState = 255.0f * params.m_pVehicle->m_fBrakePedal; + } + if (acceletateState < brakeState) + acceletateState = brakeState; + freq = Clamp2(5 * acceletateState + 22050, (int)prevFreq, 30); + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + m_sQueueSample.m_nVolume = ComputeVolume(MODEL_HELI_ENGINE_VOLUME, MODEL_CAR_ENGINE_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 2; + m_sQueueSample.m_nSampleIndex = SFX_CAR_RC_HELI; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nFrequency = freq; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(MODEL_HELI_ENGINE_VOLUME); + SET_LOOP_OFFSETS(SFX_CAR_RC_HELI) + m_sQueueSample.m_fSpeedMultiplier = 3.0f; + m_sQueueSample.m_MaxDistance = MODEL_CAR_ENGINE_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 4; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + if (isPlayerVeh) + prevFreq = freq; + } +} + +void +cAudioManager::ProcessModelVehicle(cVehicleParams& params) +{ + static uint32 prevFreq = 14000; + static uint8 LastVol = 0; + + uint32 freq; + int16 acceletateState; + int16 brakeState; + uint8 Vol; + bool8 isPlayerVeh; + bool8 vehSlowdown; + + if (params.m_fDistance < SQR(MODEL_CAR_ENGINE_MAX_DIST)) { + if (FindPlayerVehicle() == params.m_pVehicle) + isPlayerVeh = TRUE; + else + #ifdef FIX_BUGS + isPlayerVeh = CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle == params.m_pVehicle; + #else + isPlayerVeh = CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle != nil; + #endif + if (params.m_pVehicle->m_modelIndex == MI_RCBANDIT) { + if (((CAutomobile*)params.m_pVehicle)->m_nDriveWheelsOnGround != 0) { + Vol = Min(127, 127.0f * Abs(params.m_fVelocityChange) * 3.0f); + freq = 8000.0f * Abs(params.m_fVelocityChange) + 14000; + } else { + Vol = 127; + freq = 25000; + } + if (isPlayerVeh) { + Vol = Clamp2(Vol, LastVol, 7); + freq = Clamp2(freq, prevFreq, 800); + } + if (Vol > 0) { + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + m_sQueueSample.m_nVolume = ComputeVolume(Vol, MODEL_CAR_ENGINE_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 2; + m_sQueueSample.m_nSampleIndex = SFX_RC_REV; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_nFrequency = freq; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(SFX_RC_REV) + m_sQueueSample.m_fSpeedMultiplier = 3.0f; + m_sQueueSample.m_MaxDistance = MODEL_CAR_ENGINE_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 4; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + if (isPlayerVeh) { + prevFreq = freq; + LastVol = Vol; + } + } else if (params.m_pVehicle != nil) { + if (isPlayerVeh) { + acceletateState = Pads[0].GetAccelerate(); + brakeState = Pads[0].GetBrake(); + } else { + acceletateState = 255.0f * params.m_pVehicle->m_fGasPedal; + brakeState = 255.0f * params.m_pVehicle->m_fBrakePedal; + } + if (acceletateState < brakeState) + acceletateState = brakeState; + if (acceletateState <= 0) { + vehSlowdown = TRUE; + Vol = 127; + freq = 18000; + } else { + vehSlowdown = FALSE; + Vol = Min(127, (127 * acceletateState / 255) * 3.0f * Abs(params.m_fVelocityChange)); + freq = Min(22000, (8000 * acceletateState / 255 + 14000) * 3.0f * Abs(params.m_fVelocityChange)); + } + if (isPlayerVeh && !vehSlowdown) { + Vol = Clamp2(Vol, LastVol, 7); + freq = Clamp2(freq, prevFreq, 800); + } + if (!vehSlowdown) + #ifdef THIS_IS_STUPID + freq += 8000.0f * Abs(DotProduct(params.m_pVehicle->GetUp(), CVector(0.0f, 1.0f, 0.0f))); + #else + freq += 8000.0f * Abs(params.m_pVehicle->GetUp().y); + #endif + if (params.m_pVehicle->bIsDrowning) + Vol >>= 2; + if (Vol > 0) { + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + m_sQueueSample.m_nVolume = ComputeVolume(Vol, MODEL_CAR_ENGINE_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + if (vehSlowdown) { + m_sQueueSample.m_nCounter = 0; + m_sQueueSample.m_nSampleIndex = SFX_RC_IDLE; + m_sQueueSample.m_nFramesToPlay = 6; + } else { + m_sQueueSample.m_nCounter = 2; + m_sQueueSample.m_nSampleIndex = SFX_RC_REV; + m_sQueueSample.m_nFramesToPlay = 4; + } + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nFrequency = freq; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 3.0f; + m_sQueueSample.m_MaxDistance = MODEL_CAR_ENGINE_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + if (isPlayerVeh) { + if (vehSlowdown) { + prevFreq = freq; + LastVol = Vol; + } + } + } + } +} + +bool8 +cAudioManager::ProcessVehicleFlatTyre(cVehicleParams& params) +{ + CAutomobile* automobile; + CBike* bike; + bool8 wheelBurst; + uint8 Vol; + + float modifier; + + if (params.m_fDistance < SQR(FLAT_TYRE_MAX_DIST)) { + switch (params.m_VehicleType) { + case VEHICLE_TYPE_CAR: + automobile = (CAutomobile*)params.m_pVehicle; + wheelBurst = FALSE; + for (int i = 0; i < 4; i++) + if (automobile->Damage.GetWheelStatus(i) == WHEEL_STATUS_BURST && automobile->m_aWheelTimer[i] > 0.0f) + wheelBurst = TRUE; + if (!wheelBurst) + return TRUE; + break; + case VEHICLE_TYPE_BIKE: + bike = (CBike*)params.m_pVehicle; + wheelBurst = FALSE; + for(int i = 0; i < 2; i++) + if (bike->m_wheelStatus[i] == WHEEL_STATUS_BURST && bike->m_aWheelTimer[i] > 0.0f) + wheelBurst = TRUE; + if (!wheelBurst) + return TRUE; + break; + default: + return TRUE; + } + modifier = Min(1.0f, Abs(params.m_fVelocityChange) / (0.3f * params.m_pTransmission->fMaxVelocity)); + if (modifier > 0.01f) { + Vol = (FLAT_TYRE_VOLUME * modifier); + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + m_sQueueSample.m_nVolume = ComputeVolume(Vol, FLAT_TYRE_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 95; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 5; + m_sQueueSample.m_nSampleIndex = SFX_TYRE_BURST_L; + m_sQueueSample.m_nFrequency = (5500.0f * modifier) + 8000; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(SFX_TYRE_BURST_L) + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_MaxDistance = FLAT_TYRE_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 3; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + return TRUE; + } + return FALSE; +} + +bool8 +cAudioManager::ProcessVehicleRoadNoise(cVehicleParams& params) +{ + uint8 Vol; + uint32 freq; + float multiplier; + int sampleFreq; + float velocity; + uint8 wheelsOnGround; + + if (params.m_fDistance < SQR(VEHICLE_ROAD_NOISE_MAX_DIST)) { + switch (params.m_VehicleType) { + case VEHICLE_TYPE_CAR: + wheelsOnGround = ((CAutomobile*)params.m_pVehicle)->m_nWheelsOnGround; + break; + case VEHICLE_TYPE_BIKE: + wheelsOnGround = ((CBike*)params.m_pVehicle)->m_nWheelsOnGround; + break; + default: + wheelsOnGround = 4; + break; + } + if ((params.m_pTransmission != nil) && (wheelsOnGround > 0)) { + velocity = Abs(params.m_fVelocityChange); + if (velocity > 0.0f) { + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + Vol = VEHICLE_ROAD_NOISE_VOLUME * Min(1.0f, velocity / (0.5f * params.m_pTransmission->fMaxVelocity)); + m_sQueueSample.m_nVolume = ComputeVolume(Vol, VEHICLE_ROAD_NOISE_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 0; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 3; + if (params.m_pVehicle->m_nSurfaceTouched == SURFACE_WATER) { + m_sQueueSample.m_nSampleIndex = SFX_BOAT_WATER_LOOP; + freq = 6050 * Vol / VEHICLE_ROAD_NOISE_VOLUME + 16000; + } else { + m_sQueueSample.m_nSampleIndex = SFX_ROAD_NOISE; + multiplier = (m_sQueueSample.m_fDistance / VEHICLE_ROAD_NOISE_MAX_DIST) * 0.5f; + sampleFreq = SampleManager.GetSampleBaseFrequency(SFX_ROAD_NOISE); + freq = (sampleFreq * multiplier) + ((3 * sampleFreq) >> 2); + } + m_sQueueSample.m_nFrequency = freq; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 6.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ROAD_NOISE_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 4; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + } + return TRUE; + } + return FALSE; +} + +bool8 +cAudioManager::ProcessWetRoadNoise(cVehicleParams& params) +{ + float relativeVelocity; + uint8 Vol; + float multiplier; + int freq; + float velChange; + uint8 wheelsOnGround; + + if (params.m_fDistance < SQR(WET_ROAD_NOISE_MAX_DIST)) { + switch (params.m_VehicleType) { + case VEHICLE_TYPE_CAR: + wheelsOnGround = ((CAutomobile*)params.m_pVehicle)->m_nWheelsOnGround; + break; + case VEHICLE_TYPE_BIKE: + wheelsOnGround = ((CBike*)params.m_pVehicle)->m_nWheelsOnGround; + break; + default: + wheelsOnGround = 4; + break; + } + if ((params.m_pTransmission != nil) && (wheelsOnGround > 0)) { + velChange = Abs(params.m_fVelocityChange); + if (velChange > 0.0f) { + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + relativeVelocity = Min(1.0f, velChange / (0.5f * params.m_pTransmission->fMaxVelocity)); + Vol = WET_ROAD_NOISE_VOLUME * relativeVelocity * CWeather::WetRoads; + m_sQueueSample.m_nVolume = ComputeVolume(Vol, WET_ROAD_NOISE_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 1; + m_sQueueSample.m_nSampleIndex = SFX_ROAD_NOISE; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 3; + multiplier = (m_sQueueSample.m_fDistance / WET_ROAD_NOISE_MAX_DIST) * 0.5f; + freq = SampleManager.GetSampleBaseFrequency(SFX_ROAD_NOISE); + m_sQueueSample.m_nFrequency = freq + freq * multiplier; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 6.0f; + m_sQueueSample.m_MaxDistance = WET_ROAD_NOISE_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 4; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + } + return TRUE; + } + return FALSE; +} + +bool8 +cAudioManager::ProcessVehicleEngine(cVehicleParams& params) +{ + CAutomobile *automobile; + float relativeGearChange; +#ifdef FIX_BUGS + uint32 freq = 0; // uninitialized variable +#else + uint32 freq; +#endif + uint8 Vol; + cTransmission *transmission; + uint8 currentGear; + float modificator; + uint8 wheelsOnGround; + uint8 wheelsOnGroundPrev; + CBike *bike; + tWheelState *wheelState; + float *gasPedalAudioPtr; + + bool8 isMoped = FALSE; + bool8 isGolfCart = FALSE; + float traction = 0.0f; + if (params.m_fDistance < SQR(VEHICLE_ENGINE_MAX_DIST)) { + if (FindPlayerVehicle() == params.m_pVehicle && params.m_pVehicle->GetStatus() == STATUS_WRECKED) { + SampleManager.StopChannel(CHANNEL_PLAYER_VEHICLE_ENGINE); + return TRUE; + } + if (params.m_pVehicle->bEngineOn) { + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + if (FindPlayerVehicle() == params.m_pVehicle && params.m_pVehicle->GetModelIndex() != MI_CADDY) + ProcessPlayersVehicleEngine(params, params.m_pVehicle); + else { + transmission = params.m_pTransmission; + if (transmission != nil) { + switch (params.m_pVehicle->GetModelIndex()) { + case MI_PIZZABOY: + case MI_FAGGIO: + isMoped = TRUE; + currentGear = transmission->nNumberOfGears; + break; + case MI_CADDY: + currentGear = transmission->nNumberOfGears; + isGolfCart = TRUE; + break; + default: + currentGear = params.m_pVehicle->m_nCurrentGear; + break; + } + switch (params.m_VehicleType) { + case VEHICLE_TYPE_CAR: + automobile = (CAutomobile*)params.m_pVehicle; + wheelsOnGround = automobile->m_nDriveWheelsOnGround; + wheelsOnGroundPrev = automobile->m_nDriveWheelsOnGroundPrev; + wheelState = automobile->m_aWheelState; + gasPedalAudioPtr = &automobile->m_fGasPedalAudio; + break; + case VEHICLE_TYPE_BIKE: + bike = (CBike*)params.m_pVehicle; + wheelsOnGround = bike->m_nDriveWheelsOnGround; + wheelsOnGroundPrev = bike->m_nDriveWheelsOnGroundPrev; + wheelState = bike->m_aWheelState; + gasPedalAudioPtr = &bike->m_fGasPedalAudio; + break; + default: + debug(" ** AUDIOLOG: Unrecognised vehicle type %d in ProcessVehicleEngine() * \n", params.m_VehicleType); + return TRUE; + } + if (wheelsOnGround > 0) { + if (params.m_pVehicle->bIsHandbrakeOn && (!isMoped || !isGolfCart)) { // what a weird check + if (params.m_fVelocityChange == 0.0f) + traction = 0.9f; + } else if (params.m_pVehicle->GetStatus() == STATUS_SIMPLE || isMoped || isGolfCart) { + traction = 0.0f; + } else { + switch (transmission->nDriveType) { + case '4': + if (params.m_VehicleType == VEHICLE_TYPE_BIKE) { + for (uint8 i = 0; i < 2; i++) + if (wheelState[i] == WHEEL_STATE_SPINNING) + traction += 0.1f; + } else { + for (uint8 i = 0; i < 4; i++) + if (wheelState[i] == WHEEL_STATE_SPINNING) + traction += 0.05f; + } + break; + case 'F': + if (params.m_VehicleType == VEHICLE_TYPE_BIKE) { + if (wheelState[BIKEWHEEL_FRONT] == WHEEL_STATE_SPINNING) + traction += 0.2f; + } else { + if (wheelState[CARWHEEL_FRONT_LEFT] == WHEEL_STATE_SPINNING) + traction += 0.1f; + if (wheelState[CARWHEEL_FRONT_RIGHT] == WHEEL_STATE_SPINNING) + traction += 0.1f; + } + break; + case 'R': + if (params.m_VehicleType == VEHICLE_TYPE_BIKE) { + if (wheelState[BIKEWHEEL_REAR] == WHEEL_STATE_SPINNING) + traction += 0.2f; + } else { + if (wheelState[CARWHEEL_REAR_LEFT] == WHEEL_STATE_SPINNING) + traction += 0.1f; + if (wheelState[CARWHEEL_REAR_RIGHT] == WHEEL_STATE_SPINNING) + traction += 0.1f; + } + break; + default: + break; + } + } + if (transmission->fMaxVelocity > 0.0f) { + if (isMoped || isGolfCart) + modificator = Min(1.0f, Abs(params.m_fVelocityChange / transmission->fMaxVelocity > 1.0f)); + else { + if (currentGear > 0) { + relativeGearChange = Min(1.0f, + params.m_fVelocityChange - transmission->Gears[currentGear].fShiftDownVelocity) / transmission->fMaxVelocity * 2.5f; + if (traction == 0.0f && params.m_pVehicle->GetStatus() != STATUS_SIMPLE && + params.m_fVelocityChange < transmission->Gears[1].fShiftUpVelocity) + traction = 0.7f; + modificator = traction * *gasPedalAudioPtr * 0.95f + (1.0f - traction) * relativeGearChange; + } else { + modificator = Min(1.0f, + 1.0f - Abs((params.m_fVelocityChange - transmission->Gears[0].fShiftDownVelocity) / transmission->fMaxReverseVelocity)); + } + } + } else + modificator = 0.0f; + } else { + if (wheelsOnGroundPrev > 0) + *gasPedalAudioPtr *= 0.4f; + modificator = *gasPedalAudioPtr; + } + if (currentGear == 0 || wheelsOnGround > 0) { + if (params.m_VehicleType == VEHICLE_TYPE_BIKE) + freq = 22050; + else + freq = 13000 * modificator + 14000; + } else + freq = 1200 * currentGear + 18000 * modificator + 14000; + if (modificator < 0.75f) + Vol = modificator / 0.75f * (VEHICLE_ENGINE_FULL_VOLUME - VEHICLE_ENGINE_BASE_VOLUME) + VEHICLE_ENGINE_BASE_VOLUME; + else + Vol = VEHICLE_ENGINE_FULL_VOLUME; + } else { + modificator = 0.0f; + Vol = VEHICLE_ENGINE_BASE_VOLUME; + } + if (params.m_pVehicle->bIsDrowning) + Vol >>= 2; + if (isGolfCart) { + Vol = 100 * modificator; + freq = 2130 * modificator + 4270; + m_sQueueSample.m_nCounter = 2; + } + m_sQueueSample.m_nVolume = ComputeVolume(Vol, VEHICLE_ENGINE_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + if (!isGolfCart) { + if (params.m_pVehicle->GetStatus() == STATUS_SIMPLE) { + if (modificator < 0.02f) { + m_sQueueSample.m_nSampleIndex = aVehicleSettings[params.m_nIndex].m_nBank - CAR_SFX_BANKS_OFFSET + SFX_CAR_IDLE_1; + freq = 10000 * modificator + 22050; + m_sQueueSample.m_nCounter = 52; + } else { + m_sQueueSample.m_nSampleIndex = aVehicleSettings[params.m_nIndex].m_nAccelerationSampleIndex; + m_sQueueSample.m_nCounter = 2; + } + } else { + if (params.m_pVehicle->m_fGasPedal < 0.02f) { + m_sQueueSample.m_nSampleIndex = aVehicleSettings[params.m_nIndex].m_nBank - CAR_SFX_BANKS_OFFSET + SFX_CAR_IDLE_1; + freq = 10000 * modificator + 22050; + m_sQueueSample.m_nCounter = 52; + } else { + m_sQueueSample.m_nSampleIndex = aVehicleSettings[params.m_nIndex].m_nAccelerationSampleIndex; + m_sQueueSample.m_nCounter = 2; + } + } + } + if (isGolfCart) { + if (FindVehicleOfPlayer() == params.m_pVehicle) + m_sQueueSample.m_nSampleIndex = SFX_CAR_AFTER_ACCEL_12; + else + m_sQueueSample.m_nSampleIndex = SFX_CAR_REV_12; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; // TODO: PS2 bank + m_sQueueSample.m_nFrequency = freq + 20 * m_sQueueSample.m_nEntityIndex % 100; + } else { + m_sQueueSample.m_nBankIndex = SFX_BANK_0; // TODO: PS2 bank + m_sQueueSample.m_nFrequency = freq + 100 * m_sQueueSample.m_nEntityIndex % 1000; + } + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 3; + if (m_sQueueSample.m_nSampleIndex == SFX_CAR_IDLE_5 || m_sQueueSample.m_nSampleIndex == SFX_CAR_REV_5) + m_sQueueSample.m_nFrequency >>= 1; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 6.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ENGINE_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 8; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + } + return TRUE; + } + return FALSE; +} + +void +cAudioManager::UpdateGasPedalAudio(CVehicle* veh, int vehType) +{ + float gasPedal = Abs(veh->m_fGasPedal); + float* gasPealAudioPtr; + + switch(vehType) { + case VEHICLE_TYPE_CAR: gasPealAudioPtr = &((CAutomobile *)veh)->m_fGasPedalAudio; break; + case VEHICLE_TYPE_BIKE: gasPealAudioPtr = &((CBike *)veh)->m_fGasPedalAudio; break; + default: return; + } + if (*gasPealAudioPtr < gasPedal) + *gasPealAudioPtr = Min(*gasPealAudioPtr + 0.09f, gasPedal); + else + *gasPealAudioPtr = Max(*gasPealAudioPtr - 0.07f, gasPedal); +} + +void +cAudioManager::PlayerJustGotInCar() +{ + if (m_bIsInitialised) + bPlayerJustEnteredCar = TRUE; +} + +void +cAudioManager::PlayerJustLeftCar(void) +{ + // UNUSED: This is a perfectly empty function. +} + +void +cAudioManager::AddPlayerCarSample(uint8 Vol, uint32 freq, uint32 sample, uint8 bank, uint8 counter, bool8 bLooping) +{ + m_sQueueSample.m_nVolume = ComputeVolume(Vol, VEHICLE_ENGINE_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = counter; + m_sQueueSample.m_nSampleIndex = sample; +#ifdef GTA_PS2 + m_sQueueSample.m_nBankIndex = bank; +#else + m_sQueueSample.m_nBankIndex = SFX_BANK_0; +#endif // GTA_PS2 + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 0; + m_sQueueSample.m_nFrequency = freq; + if (bLooping) { + m_sQueueSample.m_nLoopCount = 0; + m_sQueueSample.m_nFramesToPlay = 8; + } else + m_sQueueSample.m_nLoopCount = 1; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 6.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ENGINE_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } +} + +void +cAudioManager::ProcessCesna(cVehicleParams ¶ms) +{ + if(params.m_fDistance < SQR(CESNA_IDLE_MAX_DIST)) { + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + m_sQueueSample.m_nVolume = ComputeVolume(CESNA_VOLUME, CESNA_IDLE_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 52; + m_sQueueSample.m_nSampleIndex = SFX_CESNA_IDLE; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 0; + m_sQueueSample.m_nFrequency = 12500; + m_sQueueSample.m_nLoopCount = 0; + m_sQueueSample.m_nFramesToPlay = 8; + SET_EMITTING_VOLUME(CESNA_VOLUME); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_MaxDistance = CESNA_IDLE_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + if(params.m_fDistance < SQR(CESNA_REV_MAX_DIST)) { + m_sQueueSample.m_nVolume = ComputeVolume(CESNA_VOLUME, CESNA_REV_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 2; + m_sQueueSample.m_nSampleIndex = SFX_CESNA_REV; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nFrequency = 25000; + m_sQueueSample.m_nLoopCount = 0; + m_sQueueSample.m_nFramesToPlay = 4; + SET_EMITTING_VOLUME(CESNA_VOLUME); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_MaxDistance = CESNA_REV_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + } +} + +void +cAudioManager::ProcessPlayersVehicleEngine(cVehicleParams& params, CVehicle* veh) +{ + static int32 GearFreqAdj[] = { 6000, 6000, 3400, 1200, 0, -1000 }; + + float relativeVelocityChange; + float accelerationMultipler; + uint8 wheelInUseCounter; + float time; + int baseFreq; + uint8 vol; + int32 freq; + + int freqModifier; + uint32 soundOffset; + uint8 engineSoundType; + int16 accelerateState; + int16 brakeState; + bool8 channelUsed; + + uint8 currentGear; + float gasPedalAudio; + CVector pos; + bool8 slowingDown; + + tWheelState *wheelState; + CAutomobile *automobile; + CBike *bike; + float *gasPedalAudioPtr; + + uint32 gearSoundLength; + uint8 wheelsOnGround; + uint8 wheelsOnGroundPrev; + + float velocityChangeForAudio; + bool8 noGearBox; + bool8 stuckInSand; + + static int16 LastAccel = 0; + static uint8 CurrentPretendGear = 1; + static bool8 bLostTractionLastFrame = FALSE; + static bool8 bHandbrakeOnLastFrame = FALSE; + static uint32 nCruising = 0; + static bool8 bAccelSampleStopped = TRUE; + + bool8 lostTraction = FALSE; + bool8 isMoped = FALSE; + bool8 processedAccelSampleStopped = FALSE; + static uint32 gearSoundStartTime = CTimer::GetTimeInMilliseconds(); +#ifdef GTA_PS2 + uint8 nChannel = m_bIsSurround ? CHANNEL_DTS_PLAYER_VEHICLE_ENGINE : CHANNEL_PLAYER_VEHICLE_ENGINE; +#else + uint8 nChannel = CHANNEL_PLAYER_VEHICLE_ENGINE; +#endif + if (bPlayerJustEnteredCar) { + bAccelSampleStopped = TRUE; + bPlayerJustEnteredCar = FALSE; + nCruising = 0; + LastAccel = 0; + bLostTractionLastFrame = FALSE; + CurrentPretendGear = 1; + bHandbrakeOnLastFrame = FALSE; + } +#ifdef FIX_BUGS // fix acceleration sound on exiting the vehicle + if (CReplay::IsPlayingBack() || FindPlayerPed()->GetPedState() == PED_EXIT_CAR) { +#else + if (CReplay::IsPlayingBack()) { +#endif + accelerateState = (255 * Clamp(params.m_pVehicle->m_fGasPedal, 0.0f, 1.0f)); + brakeState = (255 * Clamp(params.m_pVehicle->m_fBrakePedal, 0.0f, 1.0f)); + } else { + accelerateState = Pads[0].GetAccelerate(); + brakeState = Pads[0].GetBrake(); + } + slowingDown = params.m_fVelocityChange < -0.001f; + channelUsed = SampleManager.GetChannelUsedFlag(nChannel); + if (params.m_pVehicle->GetModelIndex() == MI_PIZZABOY || params.m_pVehicle->GetModelIndex() == MI_FAGGIO) { + CurrentPretendGear = params.m_pTransmission->nNumberOfGears; + currentGear = CurrentPretendGear; + if (params.m_pVehicle->bIsHandbrakeOn) { + brakeState = 0; + nCruising = 0; + LastAccel = 0; + accelerateState = 0; + } else + nCruising = 1; + isMoped = TRUE; + } else + currentGear = params.m_pVehicle->m_nCurrentGear; + + switch (params.m_VehicleType) { + case VEHICLE_TYPE_CAR: + automobile = (CAutomobile*)params.m_pVehicle; + wheelsOnGround = automobile->m_nDriveWheelsOnGround; + wheelsOnGroundPrev = automobile->m_nDriveWheelsOnGroundPrev; + gasPedalAudioPtr = &automobile->m_fGasPedalAudio; + wheelState = automobile->m_aWheelState; + velocityChangeForAudio = automobile->m_fVelocityChangeForAudio; + break; + case VEHICLE_TYPE_BIKE: + bike = (CBike*)params.m_pVehicle; + wheelsOnGround = bike->m_nDriveWheelsOnGround; + wheelsOnGroundPrev = bike->m_nDriveWheelsOnGroundPrev; + gasPedalAudioPtr = &bike->m_fGasPedalAudio; + wheelState = bike->m_aWheelState; + velocityChangeForAudio = bike->m_fVelocityChangeForAudio; + break; + default: + debug(" ** AUDIOLOG: Unrecognised vehicle type %d in ProcessVehicleEngine() * \n", params.m_VehicleType); + return; + } + if (!isMoped) { + switch (params.m_pTransmission->nDriveType) { + case '4': + if (params.m_VehicleType != VEHICLE_TYPE_BIKE) { + wheelInUseCounter = 0; + for (uint8 i = 0; i < 4; i++) { + if (wheelState[i] != WHEEL_STATE_NORMAL) + wheelInUseCounter++; + } + if (wheelInUseCounter > 2) + lostTraction = TRUE; + } + break; + case 'F': + if (params.m_VehicleType == VEHICLE_TYPE_BIKE) { + if (wheelState[BIKEWHEEL_FRONT] != WHEEL_STATE_NORMAL) + lostTraction = TRUE; + } else { + if ((wheelState[CARWHEEL_FRONT_LEFT] != WHEEL_STATE_NORMAL || wheelState[CARWHEEL_FRONT_RIGHT] != WHEEL_STATE_NORMAL) && + (wheelState[CARWHEEL_REAR_LEFT] != WHEEL_STATE_NORMAL || wheelState[CARWHEEL_REAR_RIGHT] != WHEEL_STATE_NORMAL)) + lostTraction = TRUE; + } + break; + case 'R': + if (params.m_VehicleType == VEHICLE_TYPE_BIKE) { + if (wheelState[BIKEWHEEL_REAR] != WHEEL_STATE_NORMAL) + lostTraction = TRUE; + } else { + if (wheelState[CARWHEEL_REAR_LEFT] != WHEEL_STATE_NORMAL || wheelState[CARWHEEL_REAR_RIGHT] != WHEEL_STATE_NORMAL) + lostTraction = TRUE; + } + break; + default: + break; + } + } + if (params.m_fVelocityChange != 0.0f) { + time = params.m_pVehicle->m_vecMoveSpeed.z / params.m_fVelocityChange; + if (time > 0.0f) + freqModifier = -(Min(0.2f, time) * 3000 * 5); + else + freqModifier = -(Max(-0.2f, time) * 3000 * 5); + if (slowingDown) + freqModifier = -freqModifier; + } else + freqModifier = 0; + + if (params.m_VehicleType == VEHICLE_TYPE_BIKE && bike->bExtraSpeed) + freqModifier += 1400; + + gearSoundLength = 0; + engineSoundType = aVehicleSettings[params.m_nIndex].m_nBank; + soundOffset = 3 * (engineSoundType - CAR_SFX_BANKS_OFFSET); + noGearBox = FALSE; + switch (engineSoundType) { + case SFX_BANK_PONTIAC: + gearSoundLength = 2526; + break; + case SFX_BANK_PORSCHE: + gearSoundLength = 3587; + break; + case SFX_BANK_SPIDER: + gearSoundLength = 4898; + break; + case SFX_BANK_MERC: + gearSoundLength = 4003; + break; + case SFX_BANK_TRUCK: + gearSoundLength = 6289; + break; + case SFX_BANK_HOTROD: + gearSoundLength = 2766; + break; + case SFX_BANK_COBRA: + gearSoundLength = 3523; + break; + case SFX_BANK_PONTIAC_SLOW: + gearSoundLength = 2773; + break; + case SFX_BANK_CADILLAC: + gearSoundLength = 2560; + break; + case SFX_BANK_PATHFINDER: + gearSoundLength = 4228; + break; + case SFX_BANK_PACARD: + gearSoundLength = 4648; + break; + case SFX_BANK_VTWIN: + gearSoundLength = 3480; + break; + case SFX_BANK_HONDA250: + gearSoundLength = 2380; + break; + case SFX_BANK_SPORTS_BIKE: + gearSoundLength = 2410; + break; + default: + noGearBox = TRUE; + break; + } + if (channelUsed && nCruising == 0 && !noGearBox) { + gearSoundLength -= 1000; + if (CTimer::GetTimeInMilliseconds() - gearSoundStartTime > gearSoundLength) { + channelUsed = FALSE; + gearSoundStartTime = CTimer::GetTimeInMilliseconds(); + } + } else + gearSoundStartTime = CTimer::GetTimeInMilliseconds(); + relativeVelocityChange = 2.0f * params.m_fVelocityChange / params.m_pTransmission->fMaxVelocity; + accelerationMultipler = Clamp(relativeVelocityChange, 0.0f, 1.0f); + gasPedalAudio = accelerationMultipler; + switch (engineSoundType) { + case SFX_BANK_MOPED: + soundOffset++; + break; + case SFX_BANK_HONDA250: + soundOffset += 2; + break; + case SFX_BANK_SPORTS_BIKE: + soundOffset += 3; + break; + default: + break; + } + if (accelerateState > 0) { + if (nCruising > 0) { +PlayCruising: + bAccelSampleStopped = TRUE; + SampleManager.StopChannel(nChannel); + if (!isMoped && (accelerateState < 150 || wheelsOnGround == 0 || brakeState > 0 || params.m_pVehicle->bIsHandbrakeOn + || lostTraction || currentGear < params.m_pTransmission->nNumberOfGears - 1)) { + nCruising = 0; + } else { + if (accelerateState < 220 || params.m_fVelocityChange + 0.001f < velocityChangeForAudio) { + if (nCruising > 3) + nCruising--; + } else + if (nCruising < 800) + nCruising++; + freq = 27 * nCruising + freqModifier + 22050; + if (engineSoundType == SFX_BANK_TRUCK) + freq >>= 1; + AddPlayerCarSample(PLAYER_VEHICLE_ENGINE_VOLUME, freq, soundOffset + SFX_CAR_AFTER_ACCEL_1, engineSoundType, 64, TRUE); + } + } else { + stuckInSand = params.m_VehicleType == VEHICLE_TYPE_CAR && ((CAutomobile*)params.m_pVehicle)->bStuckInSand; + if (accelerateState < 150 || wheelsOnGround == 0 || params.m_pVehicle->bIsHandbrakeOn || lostTraction + || (currentGear < 2 && params.m_fVelocityChange - velocityChangeForAudio < 0.01f) || brakeState > 0) { + if (((wheelsOnGround == 0 || params.m_pVehicle->bIsHandbrakeOn || lostTraction) && !stuckInSand) || brakeState > 0) { + if (wheelsOnGround == 0 && wheelsOnGroundPrev != 0 || (params.m_pVehicle->bIsHandbrakeOn && !bHandbrakeOnLastFrame || lostTraction && !bLostTractionLastFrame) + && wheelsOnGround != 0) { + *gasPedalAudioPtr *= 0.6f; + } + freqModifier = 0; + if (engineSoundType == SFX_BANK_GOLF_CART || engineSoundType == SFX_BANK_CAR_CHAINSAW) + baseFreq = (15000 * *gasPedalAudioPtr) + 14000; + else + baseFreq = (25000 * *gasPedalAudioPtr) + 14000; + vol = (25 * *gasPedalAudioPtr) + 60; + } else { + baseFreq = (8000 * accelerationMultipler) + 16000; + vol = (25 * accelerationMultipler) + 60; + *gasPedalAudioPtr = accelerationMultipler; + } + freq = freqModifier + baseFreq; + if (engineSoundType == SFX_BANK_TRUCK) + freq >>= 1; + if (channelUsed) { + SampleManager.StopChannel(nChannel); + bAccelSampleStopped = TRUE; + } + if (params.m_pVehicle->bIsDrowning) + vol >>= 2; + AddPlayerCarSample(vol, freq, engineSoundType - CAR_SFX_BANKS_OFFSET + SFX_CAR_REV_1, SFX_BANK_0, 2, TRUE); + } else { + TranslateEntity(&m_sQueueSample.m_vecPos, &pos); +#ifndef EXTERNAL_3D_SOUND + m_sQueueSample.m_nPan = ComputePan(m_sQueueSample.m_fDistance, &pos); +#endif + if (bAccelSampleStopped) { + if (CurrentPretendGear != 1 || currentGear != 2) + CurrentPretendGear = Max(1, currentGear - 1); + processedAccelSampleStopped = TRUE; + bAccelSampleStopped = FALSE; + } + + if (!channelUsed) { + if (!processedAccelSampleStopped) { + if (CurrentPretendGear < params.m_pTransmission->nNumberOfGears - 1) + CurrentPretendGear++; + else { + nCruising = 1; + params.m_pVehicle->bAudioChangingGear = TRUE; + goto PlayCruising; + } + } + + gearSoundStartTime = CTimer::GetTimeInMilliseconds(); + params.m_pVehicle->bAudioChangingGear = TRUE; +#ifdef GTA_PS2 + SampleManager.InitialiseChannel(nChannel, soundOffset + SFX_CAR_ACCEL_1, SFX_BANK_0); +#else + if (!SampleManager.InitialiseChannel(CHANNEL_PLAYER_VEHICLE_ENGINE, soundOffset + SFX_CAR_ACCEL_1, SFX_BANK_0)) + return; +#endif + SampleManager.SetChannelLoopCount(CHANNEL_PLAYER_VEHICLE_ENGINE, 1); +#ifndef GTA_PS2 + SampleManager.SetChannelLoopPoints(CHANNEL_PLAYER_VEHICLE_ENGINE, 0, -1); +#endif + } + +#ifdef EXTERNAL_3D_SOUND + SampleManager.SetChannelEmittingVolume(CHANNEL_PLAYER_VEHICLE_ENGINE, PLAYER_VEHICLE_ENGINE_VOLUME); + SampleManager.SetChannel3DPosition(CHANNEL_PLAYER_VEHICLE_ENGINE, pos.x, pos.y, pos.z); + SampleManager.SetChannel3DDistances(CHANNEL_PLAYER_VEHICLE_ENGINE, VEHICLE_ENGINE_MAX_DIST, VEHICLE_ENGINE_MAX_DIST / 4.0f); +#else + SampleManager.SetChannelVolume(nChannel, ComputeVolume(PLAYER_VEHICLE_ENGINE_VOLUME, VEHICLE_ENGINE_MAX_DIST, m_sQueueSample.m_fDistance)); + SampleManager.SetChannelPan(nChannel, m_sQueueSample.m_nPan); +#endif + freq = GearFreqAdj[CurrentPretendGear] + freqModifier + 22050; + if (engineSoundType == SFX_BANK_TRUCK) + freq >>= 1; +#ifdef USE_TIME_SCALE_FOR_AUDIO + SampleManager.SetChannelFrequency(nChannel, freq * CTimer::GetTimeScale()); +#else + SampleManager.SetChannelFrequency(nChannel, freq); +#endif + if (!channelUsed) { +#ifdef AUDIO_REVERB + SampleManager.SetChannelReverbFlag(nChannel, m_bDynamicAcousticModelingStatus != FALSE); +#endif + SampleManager.StartChannel(nChannel); + } + } + } + } else { + if (slowingDown) { + if (channelUsed) { + SampleManager.StopChannel(nChannel); + bAccelSampleStopped = TRUE; + } + if (wheelsOnGround == 0 || params.m_pVehicle->bIsHandbrakeOn || lostTraction) + gasPedalAudio = *gasPedalAudioPtr; + else if (params.m_VehicleType == VEHICLE_TYPE_BIKE) + gasPedalAudio = 0.0f; + else + gasPedalAudio = Min(1.0f, params.m_fVelocityChange / params.m_pTransmission->fMaxReverseVelocity); + *gasPedalAudioPtr = Max(0.0f, gasPedalAudio); + } else if (LastAccel > 0) { + if (channelUsed) { + SampleManager.StopChannel(nChannel); + bAccelSampleStopped = TRUE; + } + nCruising = 0; + if (wheelsOnGround == 0 + || params.m_pVehicle->bIsHandbrakeOn + || lostTraction + || params.m_fVelocityChange < 0.01f && *gasPedalAudioPtr > 0.2f) { + if (params.m_pVehicle->GetModelIndex() == MI_PIZZABOY || params.m_pVehicle->GetModelIndex() == MI_FAGGIO) { + gasPedalAudio = 0.0f; + } else { + *gasPedalAudioPtr *= 0.6f; + gasPedalAudio = *gasPedalAudioPtr; + } + } + if (gasPedalAudio > 0.05f) { + freq = (5000 * ((gasPedalAudio - 0.05f) / 0.95f)) + 19000; + vol = (25 * ((gasPedalAudio - 0.05f) / 0.95f)) + 40; + if (params.m_pVehicle->bIsDrowning) + vol >>= 2; + if (engineSoundType == SFX_BANK_TRUCK) + freq >>= 1; + AddPlayerCarSample(vol, freq, soundOffset + SFX_CAR_FINGER_OFF_ACCEL_1, engineSoundType, 63, FALSE); + } + } + freq = (10000 * gasPedalAudio) + 22050; + vol = 110 - (40 * gasPedalAudio); + if (engineSoundType == SFX_BANK_TRUCK) + freq >>= 1; + if (params.m_pVehicle->bIsDrowning) + vol >>= 2; + AddPlayerCarSample(vol, freq, engineSoundType - CAR_SFX_BANKS_OFFSET + SFX_CAR_IDLE_1, SFX_BANK_0, 52, TRUE); + + CurrentPretendGear = Max(1, currentGear); + } + LastAccel = accelerateState; + + bHandbrakeOnLastFrame = !!params.m_pVehicle->bIsHandbrakeOn; + bLostTractionLastFrame = lostTraction; +} + +bool8 +cAudioManager::ProcessVehicleSkidding(cVehicleParams& params) +{ + CAutomobile *automobile; + CBike *bike; + uint8 numWheels; + uint8 wheelsOnGround; + float gasPedalAudio; + tWheelState* wheelStateArr; + + + cTransmission *transmission; + uint8 Vol; + float newSkidVal = 0.0f; + float skidVal = 0.0f; + + if (params.m_fDistance < SQR(VEHICLE_SKIDDING_MAX_DIST)) { + switch (params.m_VehicleType) { + case VEHICLE_TYPE_CAR: + automobile = (CAutomobile*)params.m_pVehicle; + numWheels = 4; + wheelStateArr = automobile->m_aWheelState; + wheelsOnGround = automobile->m_nWheelsOnGround; + gasPedalAudio = automobile->m_fGasPedalAudio; + break; + case VEHICLE_TYPE_BIKE: + bike = (CBike*)params.m_pVehicle; + numWheels = 2; + wheelStateArr = bike->m_aWheelState; + wheelsOnGround = bike->m_nWheelsOnGround; + gasPedalAudio = bike->m_fGasPedalAudio; + break; + default: + debug("\n * AUDIOLOG: ProcessVehicleSkidding() Unsupported vehicle type %d * \n", params.m_VehicleType); + return TRUE; + } + if (wheelsOnGround > 0) { + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + + for (uint8 i = 0; i < numWheels; i++) { + if (wheelStateArr[i] == WHEEL_STATE_NORMAL) + continue; + transmission = params.m_pTransmission; + switch (transmission->nDriveType) { + case '4': + newSkidVal = GetVehicleDriveWheelSkidValue(params.m_pVehicle, wheelStateArr[i], gasPedalAudio, transmission, params.m_fVelocityChange); + break; + case 'F': + if (i == CARWHEEL_FRONT_LEFT || i == CARWHEEL_FRONT_RIGHT) + newSkidVal = GetVehicleDriveWheelSkidValue(params.m_pVehicle, wheelStateArr[i], gasPedalAudio, transmission, params.m_fVelocityChange); + else + newSkidVal = GetVehicleNonDriveWheelSkidValue(params.m_pVehicle, wheelStateArr[i], transmission, params.m_fVelocityChange); + break; + case 'R': + if (i == CARWHEEL_REAR_LEFT || i == CARWHEEL_REAR_RIGHT) + newSkidVal = GetVehicleDriveWheelSkidValue(params.m_pVehicle, wheelStateArr[i], gasPedalAudio, transmission, params.m_fVelocityChange); + else + newSkidVal = GetVehicleNonDriveWheelSkidValue(params.m_pVehicle, wheelStateArr[i], transmission, params.m_fVelocityChange); + break; + default: + break; + } + skidVal = Max(skidVal, newSkidVal); + } + + if (skidVal > 0.0f) { + Vol = VEHICLE_SKIDDING_VOLUME * skidVal; + m_sQueueSample.m_nVolume = ComputeVolume(Vol, VEHICLE_SKIDDING_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 3; + switch (params.m_pVehicle->m_nSurfaceTouched) { + case SURFACE_GRASS: + case SURFACE_HEDGE: + m_sQueueSample.m_nSampleIndex = SFX_RAIN; + Vol >>= 2; + m_sQueueSample.m_nFrequency = 13000 * skidVal + 35000; + m_sQueueSample.m_nVolume >>= 2; + if (m_sQueueSample.m_nVolume == 0) + return TRUE; + break; + case SURFACE_GRAVEL: + case SURFACE_MUD_DRY: + case SURFACE_SAND: + case SURFACE_WATER: + case SURFACE_SAND_BEACH: + m_sQueueSample.m_nSampleIndex = SFX_GRAVEL_SKID; + m_sQueueSample.m_nFrequency = 6000 * skidVal + 10000; + break; + + default: + m_sQueueSample.m_nSampleIndex = SFX_SKID; + m_sQueueSample.m_nFrequency = 5000 * skidVal + 11000; + if (params.m_VehicleType == VEHICLE_TYPE_BIKE) + m_sQueueSample.m_nFrequency += 2000; + break; + } + + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 8; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 3.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_SKIDDING_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 3; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + } + return TRUE; + } + return FALSE; +} + +float +cAudioManager::GetVehicleDriveWheelSkidValue(CVehicle *veh, tWheelState wheelState, float gasPedalAudio, cTransmission *transmission, float velocityChange) +{ + float relativeVelChange = 0.0f; + float velChange; + float relativeVel; + + switch (wheelState) + { + case WHEEL_STATE_SPINNING: + if (gasPedalAudio > 0.4f) + relativeVelChange = (gasPedalAudio - 0.4f) * (5.0f / 3.0f) * 0.75f; + break; + case WHEEL_STATE_SKIDDING: + relativeVelChange = Min(1.0f, Abs(velocityChange) / transmission->fMaxVelocity); + break; + case WHEEL_STATE_FIXED: + relativeVel = gasPedalAudio; + if (relativeVel > 0.4f) + relativeVel = (gasPedalAudio - 0.4f) * (5.0f / 3.0f); + + velChange = Abs(velocityChange); + if (velChange > 0.04f) + relativeVelChange = Min(1.0f, velChange / transmission->fMaxVelocity); + if (relativeVel > relativeVelChange) + relativeVelChange = relativeVel; + + break; + default: + break; + } + + return Max(relativeVelChange, Min(1.0f, Abs(veh->m_vecTurnSpeed.z) * 20.0f)); +} + +float +cAudioManager::GetVehicleNonDriveWheelSkidValue(CVehicle *veh, tWheelState wheelState, cTransmission *transmission, float velocityChange) +{ + float relativeVelChange = 0.0f; + + if (wheelState == WHEEL_STATE_SKIDDING) + relativeVelChange = Min(1.0f, Abs(velocityChange) / transmission->fMaxVelocity); + + return Max(relativeVelChange, Min(1.0f, Abs(veh->m_vecTurnSpeed.z) * 20.0f)); +} + +bool8 +cAudioManager::ProcessVehicleHorn(cVehicleParams& params) +{ + if (params.m_fDistance < SQR(VEHICLE_HORN_MAX_DIST)) { + if (params.m_pVehicle->m_bSirenOrAlarm && UsesSirenSwitching(params)) + return TRUE; + + if (params.m_pVehicle->GetModelIndex() == MI_MRWHOOP) + return TRUE; + + if (params.m_pVehicle->IsAlarmOn()) + return TRUE; + + if (params.m_pVehicle->m_nCarHornTimer > 0) { + if (params.m_pVehicle->GetStatus() != STATUS_PLAYER) { + params.m_pVehicle->m_nCarHornTimer = Min(44, params.m_pVehicle->m_nCarHornTimer); + if (params.m_pVehicle->m_nCarHornTimer == 44) + params.m_pVehicle->m_nCarHornPattern = (m_FrameCounter + m_sQueueSample.m_nEntityIndex) & 7; + + if (!HornPattern[params.m_pVehicle->m_nCarHornPattern][44 - params.m_pVehicle->m_nCarHornTimer]) + return TRUE; + } + + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + m_sQueueSample.m_nVolume = ComputeVolume(params.m_pVehicle->bIsDrowning ? VEHICLE_HORN_VOLUME / 4 : VEHICLE_HORN_VOLUME, VEHICLE_HORN_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 4; + m_sQueueSample.m_nSampleIndex = aVehicleSettings[params.m_nIndex].m_nHornSample; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 2; + m_sQueueSample.m_nFrequency = aVehicleSettings[params.m_nIndex].m_nHornFrequency; + m_sQueueSample.m_nLoopCount = 0; +#ifdef FIX_BUGS + SET_EMITTING_VOLUME(params.m_pVehicle->bIsDrowning ? VEHICLE_HORN_VOLUME / 4 : VEHICLE_HORN_VOLUME); +#else + SET_EMITTING_VOLUME(VEHICLE_HORN_VOLUME); +#endif + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 5.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_HORN_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 4; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + return TRUE; + } + return FALSE; +} + +bool8 +cAudioManager::UsesSiren(cVehicleParams& params) +{ + return params.m_pVehicle->UsesSiren(); +} + +bool8 +cAudioManager::UsesSirenSwitching(cVehicleParams& params) +{ + if (params.m_nIndex == FIRETRUK || params.m_nIndex == MRWHOOP) + return FALSE; + return UsesSiren(params); +} + +bool8 +cAudioManager::ProcessVehicleSirenOrAlarm(cVehicleParams& params) +{ + uint8 Vol; + + if (params.m_fDistance < SQR(VEHICLE_SIREN_MAX_DIST)) { + CVehicle *veh = params.m_pVehicle; + if (veh->m_bSirenOrAlarm || veh->IsAlarmOn()) { + if (veh->IsAlarmOn()) { + if (CTimer::GetTimeInMilliseconds() > veh->m_nCarHornTimer) + veh->m_nCarHornTimer = CTimer::GetTimeInMilliseconds() + 750; + + if (veh->m_nCarHornTimer < CTimer::GetTimeInMilliseconds() + 375) + return TRUE; + } + + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + Vol = veh->bIsDrowning ? VEHICLE_SIREN_VOLUME / 4 : VEHICLE_SIREN_VOLUME; + m_sQueueSample.m_nVolume = ComputeVolume(Vol, VEHICLE_SIREN_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 5; + if (UsesSiren(params)) { + if (params.m_pVehicle->GetStatus() == STATUS_ABANDONED) + return TRUE; + if (veh->m_nCarHornTimer > 0 && params.m_nIndex != FIRETRUK && params.m_nIndex != MRWHOOP) { + m_sQueueSample.m_nSampleIndex = SFX_SIREN_FAST; + if (params.m_nIndex == FBIRANCH) + m_sQueueSample.m_nFrequency = 12668; + else + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_SIREN_FAST); + m_sQueueSample.m_nCounter = 60; + } else if (params.m_nIndex == VICECHEE) { + m_sQueueSample.m_nSampleIndex = SFX_POLICE_SIREN_SLOW; + m_sQueueSample.m_nFrequency = 11440; + } else { + m_sQueueSample.m_nSampleIndex = aVehicleSettings[params.m_nIndex].m_nSirenOrAlarmSample; + m_sQueueSample.m_nFrequency = aVehicleSettings[params.m_nIndex].m_nSirenOrAlarmFrequency; + } + } else { + m_sQueueSample.m_nSampleIndex = aVehicleSettings[params.m_nIndex].m_nHornSample; + m_sQueueSample.m_nFrequency = aVehicleSettings[params.m_nIndex].m_nHornFrequency; + } + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 7.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_SIREN_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 5; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + return TRUE; + } + return FALSE; +} + +bool8 +cAudioManager::UsesReverseWarning(uint32 model) +{ + return model == LINERUN || model == FIRETRUK || model == BUS || model == COACH || model == PACKER || model == FLATBED; +} + +bool8 +cAudioManager::ProcessVehicleReverseWarning(cVehicleParams& params) +{ + CVehicle *veh = params.m_pVehicle; + + if (params.m_fDistance < SQR(VEHICLE_REVERSE_WARNING_MAX_DIST)) { + if (veh->bEngineOn && veh->m_fGasPedal < 0.0f) { + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + m_sQueueSample.m_nVolume = ComputeVolume(veh->bIsDrowning ? VEHICLE_REVERSE_WARNING_VOLUME / 4 : VEHICLE_REVERSE_WARNING_VOLUME, VEHICLE_REVERSE_WARNING_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 12; + m_sQueueSample.m_nSampleIndex = SFX_REVERSE_WARNING; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 2; + m_sQueueSample.m_nFrequency = (100 * m_sQueueSample.m_nEntityIndex % 1024) + SampleManager.GetSampleBaseFrequency(SFX_REVERSE_WARNING); + m_sQueueSample.m_nLoopCount = 0; +#ifdef FIX_BUGS + SET_EMITTING_VOLUME(veh->bIsDrowning ? VEHICLE_REVERSE_WARNING_VOLUME / 4 : VEHICLE_REVERSE_WARNING_VOLUME); +#else + SET_EMITTING_VOLUME(VEHICLE_REVERSE_WARNING_VOLUME); +#endif + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 3.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_REVERSE_WARNING_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 3; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + return TRUE; + } + return FALSE; +} + +bool8 +cAudioManager::ProcessVehicleDoors(cVehicleParams& params) +{ + CAutomobile *automobile; + int8 doorState; + uint8 Vol; + float velocity; + + if (params.m_fDistance < SQR(VEHICLE_DOORS_MAX_DIST)) { + automobile = (CAutomobile *)params.m_pVehicle; + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + for (uint8 i = 0; i < ARRAY_SIZE(automobile->Doors); i++) { + if (automobile->Damage.GetDoorStatus(i) == DOOR_STATUS_SWINGING) { + doorState = automobile->Doors[i].m_nDoorState; + if (doorState == DOORST_OPEN || doorState == DOORST_CLOSED) { + velocity = Min(0.3f, Abs(automobile->Doors[i].m_fAngVel)); + if (velocity > 0.0035f) { + Vol = (VEHICLE_DOORS_VOLUME * velocity / 0.3f); + m_sQueueSample.m_nVolume = ComputeVolume(Vol, VEHICLE_DOORS_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = i + 6; + m_sQueueSample.m_nSampleIndex = m_anRandomTable[1] % 6 + SFX_COL_CAR_PANEL_1; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex) + RandomDisplacement(1000); + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 10; + m_sQueueSample.m_nLoopCount = 1; + SET_EMITTING_VOLUME(Vol); + RESET_LOOP_OFFSETS + m_sQueueSample.m_fSpeedMultiplier = 1.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_DOORS_MAX_DIST; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(TRUE); + AddSampleToRequestedQueue(); + } + } + } + } + } + return TRUE; + } + return FALSE; +} + +bool8 +cAudioManager::ProcessAirBrakes(cVehicleParams& params) +{ + CAutomobile *automobile; + uint8 Vol; + + if (params.m_fDistance < SQR(AIR_BRAKES_MAX_DIST)) { + automobile = (CAutomobile *)params.m_pVehicle; + if (automobile->bEngineOn && (automobile->m_fVelocityChangeForAudio >= 0.025f && params.m_fVelocityChange < 0.025f || + automobile->m_fVelocityChangeForAudio <= -0.025f && params.m_fVelocityChange > 0.025f)) { + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + Vol = m_anRandomTable[0] % 10 + AIR_BRAKES_VOLUME; + m_sQueueSample.m_nVolume = ComputeVolume(Vol, AIR_BRAKES_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 13; + m_sQueueSample.m_nSampleIndex = SFX_AIR_BRAKES; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_AIR_BRAKES); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 4); + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 10; + m_sQueueSample.m_nLoopCount = 1; + SET_EMITTING_VOLUME(Vol); + RESET_LOOP_OFFSETS + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = AIR_BRAKES_MAX_DIST; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + return TRUE; + } + return FALSE; +} + +bool8 +cAudioManager::HasAirBrakes(uint32 model) +{ + return model == LINERUN || model == FIRETRUK || model == TRASH || model == BUS || model == BARRACKS + || model == COACH || model == PACKER || model == FLATBED; +} + +bool8 +cAudioManager::ProcessEngineDamage(cVehicleParams& params) +{ + uint8 Vol; + + if (params.m_fDistance < SQR(ENGINE_DAMAGE_MAX_DIST)) { + if (params.m_pVehicle->m_modelIndex == MI_CADDY) + return TRUE; + if (params.m_pVehicle->GetStatus() == STATUS_WRECKED || params.m_pVehicle->m_fHealth >= 390) + return TRUE; + if (params.m_pVehicle->m_fHealth < 250.0f) { + Vol = 60; + m_sQueueSample.m_nSampleIndex = SFX_CAR_ON_FIRE; + m_sQueueSample.m_nPriority = 7; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_CAR_ON_FIRE); + } else { + Vol = 30; + m_sQueueSample.m_nSampleIndex = SFX_PALM_TREE_LO; + m_sQueueSample.m_nPriority = 7; + m_sQueueSample.m_nFrequency = 27000; + } + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + if (params.m_pVehicle->bIsDrowning) + Vol >>= 2; + m_sQueueSample.m_nVolume = ComputeVolume(Vol, ENGINE_DAMAGE_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 28; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_MaxDistance = ENGINE_DAMAGE_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 3; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + return TRUE; + } + return FALSE; +} + +bool8 +cAudioManager::ProcessCarBombTick(cVehicleParams& params) +{ + uint8 bombType; + + if (params.m_fDistance < SQR(CAR_BOMB_TICK_MAX_DIST)) { + if (params.m_pVehicle->bEngineOn) { + switch (params.m_VehicleType) { + case VEHICLE_TYPE_CAR: + bombType = params.m_pVehicle->m_bombType; + break; + case VEHICLE_TYPE_BIKE: + bombType = params.m_pVehicle->m_bombType; + break; + default: + debug("\n * AUDIOLOG: ProcessCarBombTick() Unsupported vehicle type %d * \n", params.m_VehicleType); + return TRUE; + } + if (bombType == CARBOMB_TIMEDACTIVE) { + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + m_sQueueSample.m_nVolume = ComputeVolume(CAR_BOMB_TICK_VOLUME, CAR_BOMB_TICK_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 35; + m_sQueueSample.m_nSampleIndex = SFX_COUNTDOWN; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 0; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_COUNTDOWN); + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(CAR_BOMB_TICK_VOLUME); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_MaxDistance = CAR_BOMB_TICK_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 3; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + } + return TRUE; + } + return FALSE; +} + +void +cAudioManager::ProcessVehicleOneShots(cVehicleParams& params) +{ + int16 event; + uint8 Vol; + float eventRelVol; + float eventVol; + bool8 bLoop; + bool8 stereo; + float maxDist; + static uint8 WaveIndex = 41; + static uint8 GunIndex = 53; + cPedParams pedParams; + static uint32 WaterFallFrame = 0; + + for (uint16 i = 0; i < m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_AudioEvents; i++) { + bLoop = FALSE; + stereo = FALSE; + SET_SOUND_REFLECTION(FALSE); + event = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i]; + switch (event) { + case SOUND_HELI_BLADE: + { + static uint8 HeliIndex = 89; + eventRelVol = ((CAutomobile*)params.m_pVehicle)->m_aWheelSpeed[1] * 50.0f / 11.0f; + if (eventRelVol < 0.2f || eventRelVol == 1.0f) + continue; + Vol = (1.0f - eventRelVol) * VEHICLE_ONE_SHOT_HELI_BLADE_VOLUME; + maxDist = SQR(VEHICLE_ONE_SHOT_HELI_BLADE_MAX_DIST); + m_sQueueSample.m_nSampleIndex = SFX_CAR_HELI_ROT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = HeliIndex++; + if (HeliIndex > 90) + HeliIndex = 89; + m_sQueueSample.m_nFrequency = (8000 * eventRelVol) + 16000; + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.m_nPriority = 2; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_HELI_BLADE_MAX_DIST; + break; + } + case SOUND_CAR_WINDSHIELD_CRACK: + maxDist = SQR(VEHICLE_ONE_SHOT_WINDSHIELD_CRACK_MAX_DIST); + m_sQueueSample.m_nSampleIndex = SFX_GLASS_CRACK; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 68; + Vol = m_anRandomTable[1] % 30 + VEHICLE_ONE_SHOT_WINDSHIELD_CRACK_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_GLASS_CRACK); + m_sQueueSample.m_nPriority = 5; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_WINDSHIELD_CRACK_MAX_DIST; + break; + case SOUND_CAR_DOOR_OPEN_BONNET: + case SOUND_CAR_DOOR_OPEN_BUMPER: + case SOUND_CAR_DOOR_OPEN_FRONT_LEFT: + case SOUND_CAR_DOOR_OPEN_FRONT_RIGHT: + case SOUND_CAR_DOOR_OPEN_BACK_LEFT: + case SOUND_CAR_DOOR_OPEN_BACK_RIGHT: + maxDist = SQR(VEHICLE_ONE_SHOT_DOOR_MAX_DIST); + Vol = m_anRandomTable[1] % (MAX_VOLUME - VEHICLE_ONE_SHOT_DOOR_CLOSE_VOLUME) + VEHICLE_ONE_SHOT_DOOR_CLOSE_VOLUME; + switch (aVehicleSettings[params.m_nIndex].m_bDoorType) { + case OLD_DOOR: + m_sQueueSample.m_nSampleIndex = SFX_OLD_CAR_DOOR_OPEN; + break; + case NEW_DOOR: + default: + m_sQueueSample.m_nSampleIndex = SFX_NEW_CAR_DOOR_OPEN; + break; + case TRUCK_DOOR: + m_sQueueSample.m_nSampleIndex = SFX_TRUCK_DOOR_OPEN; + break; + case BUS_DOOR: + m_sQueueSample.m_nSampleIndex = SFX_AIR_BRAKES; + break; + } + m_sQueueSample.m_nBankIndex = SFX_BANK_0; +#ifdef THIS_IS_STUPID + m_sQueueSample.m_nCounter = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i] + 10; +#else + m_sQueueSample.m_nCounter = event + 10; +#endif + if (params.m_pVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI) + m_sQueueSample.m_nFrequency = 23459; + else + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_DOOR_MAX_DIST; + SET_SOUND_REFLECTION(TRUE); + break; + case SOUND_CAR_DOOR_CLOSE_BONNET: + case SOUND_CAR_DOOR_CLOSE_BUMPER: + case SOUND_CAR_DOOR_CLOSE_FRONT_LEFT: + case SOUND_CAR_DOOR_CLOSE_FRONT_RIGHT: + case SOUND_CAR_DOOR_CLOSE_BACK_LEFT: + case SOUND_CAR_DOOR_CLOSE_BACK_RIGHT: + maxDist = SQR(VEHICLE_ONE_SHOT_DOOR_MAX_DIST); + Vol = m_anRandomTable[2] % (MAX_VOLUME - VEHICLE_ONE_SHOT_DOOR_OPEN_VOLUME) + VEHICLE_ONE_SHOT_DOOR_OPEN_VOLUME; + switch (aVehicleSettings[params.m_nIndex].m_bDoorType) { + case OLD_DOOR: + m_sQueueSample.m_nSampleIndex = SFX_OLD_CAR_DOOR_CLOSE; + break; + case NEW_DOOR: + default: + m_sQueueSample.m_nSampleIndex = SFX_NEW_CAR_DOOR_CLOSE; + break; + case TRUCK_DOOR: + m_sQueueSample.m_nSampleIndex = SFX_TRUCK_DOOR_CLOSE; + break; + case BUS_DOOR: + m_sQueueSample.m_nSampleIndex = SFX_AIR_BRAKES; + break; + } + m_sQueueSample.m_nBankIndex = SFX_BANK_0; +#ifdef THIS_IS_STUPID + m_sQueueSample.m_nCounter = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i] + 22; +#else + m_sQueueSample.m_nCounter = event + 22; +#endif + if (params.m_pVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI) + m_sQueueSample.m_nFrequency = 28062; + else + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_DOOR_MAX_DIST; + SET_SOUND_REFLECTION(TRUE); + break; + case SOUND_CAR_ENGINE_START: + if (params.m_pVehicle->GetVehicleAppearance() != VEHICLE_APPEARANCE_CAR + || params.m_pVehicle->m_modelIndex == MI_CADDY) + continue; + Vol = VEHICLE_ONE_SHOT_CAR_ENGINE_START_VOLUME; + maxDist = SQR(VEHICLE_ONE_SHOT_CAR_ENGINE_START_MAX_DIST); + m_sQueueSample.m_nSampleIndex = SFX_CAR_STARTER; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 33; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_CAR_STARTER); + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_CAR_ENGINE_START_MAX_DIST; + SET_SOUND_REFLECTION(TRUE); + break; + case SOUND_WEAPON_HIT_VEHICLE: + m_sQueueSample.m_nSampleIndex = m_anRandomTable[m_sQueueSample.m_nEntityIndex % ARRAY_SIZE(m_anRandomTable)] % 6 + SFX_BULLET_CAR_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 34; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.m_nPriority = 7; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_WEAPON_HIT_VEHICLE_MAX_DIST; + maxDist = SQR(VEHICLE_ONE_SHOT_WEAPON_HIT_VEHICLE_MAX_DIST); + Vol = m_anRandomTable[3] % 20 + VEHICLE_ONE_SHOT_WEAPON_HIT_VEHICLE_VOLUME; + break; + case SOUND_BOMB_TIMED_ACTIVATED: + case SOUND_91: + case SOUND_BOMB_ONIGNITION_ACTIVATED: + case SOUND_BOMB_TICK: + m_sQueueSample.m_nSampleIndex = SFX_ARM_BOMB; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 36; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_ARM_BOMB); + m_sQueueSample.m_nPriority = 0; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_BOMB_ARMED_MAX_DIST; + SET_SOUND_REFLECTION(TRUE); + Vol = VEHICLE_ONE_SHOT_BOMB_ARMED_VOLUME; + maxDist = SQR(VEHICLE_ONE_SHOT_BOMB_ARMED_MAX_DIST); + break; + case SOUND_CAR_LIGHT_BREAK: + m_sQueueSample.m_nSampleIndex = SFX_GLASS_SHARD_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 37; + m_sQueueSample.m_nFrequency = 9 * SampleManager.GetSampleBaseFrequency(SFX_GLASS_SHARD_1) / 10; + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 3); + m_sQueueSample.m_nPriority = 5; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_WINDSHIELD_CRACK_MAX_DIST; + maxDist = SQR(VEHICLE_ONE_SHOT_WINDSHIELD_CRACK_MAX_DIST); + Vol = m_anRandomTable[4] % 10 + VEHICLE_ONE_SHOT_CAR_LIGHT_BREAK_VOLUME; + break; + case SOUND_PLANE_ON_GROUND: + m_sQueueSample.m_nSampleIndex = SFX_JUMBO_LAND_WHEELS; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 81; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_JUMBO_LAND_WHEELS); + m_sQueueSample.m_nPriority = 2; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_PLANE_ON_GROUND_MAX_DIST; + maxDist = SQR(VEHICLE_ONE_SHOT_PLANE_ON_GROUND_MAX_DIST); + Vol = m_anRandomTable[4] % 25 + VEHICLE_ONE_SHOT_PLANE_ON_GROUND_VOLUME; + break; + case SOUND_CAR_JERK: + m_sQueueSample.m_nSampleIndex = SFX_SHAG_SUSPENSION; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 87; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_SHAG_SUSPENSION); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 3); + m_sQueueSample.m_nPriority = 5; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_CAR_HYDRAULIC_MAX_DIST; + maxDist = SQR(VEHICLE_ONE_SHOT_CAR_HYDRAULIC_MAX_DIST); + Vol = m_anRandomTable[1] % 15 + VEHICLE_ONE_SHOT_CAR_HYDRAULIC_VOLUME; + break; + case SOUND_CAR_HYDRAULIC_1: + case SOUND_CAR_HYDRAULIC_2: + if (event == SOUND_CAR_HYDRAULIC_1) + m_sQueueSample.m_nFrequency = 15600; + else + m_sQueueSample.m_nFrequency = 13118; + m_sQueueSample.m_nSampleIndex = SFX_SUSPENSION_FAST_MOVE; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 51; + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 3); + m_sQueueSample.m_nPriority = 5; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_CAR_HYDRAULIC_MAX_DIST; + maxDist = SQR(VEHICLE_ONE_SHOT_CAR_HYDRAULIC_MAX_DIST); + Vol = m_anRandomTable[0] % 15 + VEHICLE_ONE_SHOT_CAR_HYDRAULIC_VOLUME; + break; + case SOUND_CAR_HYDRAULIC_3: + m_sQueueSample.m_nSampleIndex = SFX_SUSPENSION_SLOW_MOVE_LOOP; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 86; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_SUSPENSION_SLOW_MOVE_LOOP); + m_sQueueSample.m_nPriority = 5; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_CAR_HYDRAULIC_MAX_DIST; + m_sQueueSample.m_nFramesToPlay = 7; + bLoop = TRUE; + maxDist = SQR(VEHICLE_ONE_SHOT_CAR_HYDRAULIC_MAX_DIST); + Vol = m_anRandomTable[0] % 15 + VEHICLE_ONE_SHOT_CAR_HYDRAULIC_VOLUME; + break; + case SOUND_WATER_FALL: + if (m_FrameCounter <= WaterFallFrame) + continue; + WaterFallFrame = m_FrameCounter + 6; + m_sQueueSample.m_nSampleIndex = SFX_SPLASH_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 15; + m_sQueueSample.m_nFrequency = RandomDisplacement(1000) + 16000; + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_WATER_FALL_MAX_DIST; + maxDist = SQR(VEHICLE_ONE_SHOT_WATER_FALL_MAX_DIST); + SET_SOUND_REFLECTION(TRUE); + Vol = m_anRandomTable[4] % 20 + VEHICLE_ONE_SHOT_WATER_FALL_VOLUME; + break; + case SOUND_CAR_BOMB_TICK: + m_sQueueSample.m_nSampleIndex = SFX_BOMB_BEEP; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 80; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_BOMB_BEEP); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_CAR_BOMB_TICK_MAX_DIST; + maxDist = SQR(VEHICLE_ONE_SHOT_CAR_BOMB_TICK_MAX_DIST); + SET_SOUND_REFLECTION(TRUE); + Vol = VEHICLE_ONE_SHOT_CAR_BOMB_TICK_VOLUME; + break; + case SOUND_CAR_SPLASH: + eventVol = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; + if (eventVol <= 150) + continue; + if (eventVol > 800) + m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i] = 800; + eventRelVol = (m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i] - 150) / (800 - 150); + m_sQueueSample.m_nSampleIndex = (m_anRandomTable[0] & 1) + SFX_BOAT_SPLASH_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = WaveIndex++; + if (WaveIndex > 46) + WaveIndex = 41; + m_sQueueSample.m_nFrequency = (7000 * eventRelVol) + 6000; + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_CAR_SPLASH_MAX_DIST; + Vol = (VEHICLE_ONE_SHOT_CAR_SPLASH_VOLUME * eventRelVol); + maxDist = SQR(VEHICLE_ONE_SHOT_CAR_SPLASH_MAX_DIST); + break; + case SOUND_CAR_JUMP: + case SOUND_CAR_JUMP_2: + { + static uint8 iWheelIndex = 82; + maxDist = SQR(VEHICLE_ONE_SHOT_CAR_JUMP_MAX_DIST); +#ifdef THIS_IS_STUPID + if (m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i] == SOUND_CAR_JUMP_2) { +#else + if (event == SOUND_CAR_JUMP_2) { +#endif + m_sQueueSample.m_nSampleIndex = SFX_TYRE_BURST_B; + Vol = Max(50, 2 * (60 * m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i])); + } else { + m_sQueueSample.m_nSampleIndex = SFX_TYRE_BUMP; + Vol = Max(VEHICLE_ONE_SHOT_CAR_JUMP_VOLUME, 2 * (100 * m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i])); + } + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iWheelIndex++; + if (iWheelIndex > 85) + iWheelIndex = 82; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_TYRE_BUMP); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 4); + if (params.m_VehicleType == VEHICLE_TYPE_BIKE) + m_sQueueSample.m_nFrequency <<= 1; + m_sQueueSample.m_nPriority = 6; + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_CAR_JUMP_MAX_DIST; + break; + } + case SOUND_CAR_TYRE_POP: + { + static uint8 WheelIndex = 91; + m_sQueueSample.m_nSampleIndex = SFX_TYRE_BURST; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = WheelIndex++; + if (WheelIndex > 94) + WheelIndex = 91; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_TYRE_BURST); + m_sQueueSample.m_nFrequency += RandomDisplacement(2000); + m_sQueueSample.m_nPriority = 2; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_CAR_TYRE_POP_MAX_DIST; + maxDist = SQR(VEHICLE_ONE_SHOT_CAR_TYRE_POP_MAX_DIST); + Vol = m_anRandomTable[4] % (MAX_VOLUME-VEHICLE_ONE_SHOT_CAR_TYRE_POP_VOLUME) + VEHICLE_ONE_SHOT_CAR_TYRE_POP_VOLUME; + break; + } + case SOUND_WEAPON_SHOT_FIRED: + switch (params.m_pVehicle->m_modelIndex) { + case MI_HUNTER: + case MI_CHOPPER: + case MI_SEASPAR: + case MI_SPARROW: + case MI_MAVERICK: + case MI_VCNMAV: + if (params.m_pVehicle->m_modelIndex == MI_HUNTER) { + if (Pads[0].GetHandBrake() == 0) { + if (FindVehicleOfPlayer() != params.m_pVehicle) { + m_sQueueSample.m_nSampleIndex = SFX_M60_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + } else { + m_sQueueSample.m_nSampleIndex = SFX_ROCKET_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + } + } else { + m_sQueueSample.m_nSampleIndex = SFX_M60_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + } + } else { + m_sQueueSample.m_nSampleIndex = SFX_M60_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + } + maxDist = SQR(VEHICLE_ONE_SHOT_WEAPON_SHOT_FIRED_MAX_DIST); + m_sQueueSample.m_nCounter = GunIndex++; + Vol = MAX_VOLUME; + if (GunIndex > 58) + GunIndex = 53; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_M60_LEFT); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 4); + m_sQueueSample.m_nPriority = 2; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_WEAPON_SHOT_FIRED_MAX_DIST; + SET_SOUND_REFLECTION(TRUE); + stereo = TRUE; + break; + default: + { + maxDist = SQR(VEHICLE_ONE_SHOT_WEAPON_SHOT_FIRED_MAX_DIST); +#ifdef FIX_BUGS + uint32 sampleIndex; + uint32 frequency; + CPed *pPed = params.m_pVehicle->pDriver; + if(!pPed) + continue; + if(!pPed->HasWeaponSlot(WEAPONSLOT_SUBMACHINEGUN) || (params.m_pVehicle->GetModelIndex() == MI_PREDATOR && !pPed->IsPedDoingDriveByShooting())) { + sampleIndex = SFX_UZI_LEFT; + frequency = SampleManager.GetSampleBaseFrequency(sampleIndex); + frequency += RandomDisplacement(frequency >> 5); + } else + switch(pPed->GetWeapon(WEAPONSLOT_SUBMACHINEGUN).m_eWeaponType) { + case WEAPONTYPE_TEC9: + sampleIndex = SFX_TEC_LEFT; + frequency = RandomDisplacement(500) + 17000; + break; + case WEAPONTYPE_SILENCED_INGRAM: + sampleIndex = SFX_TEC_LEFT; + frequency = RandomDisplacement(1000) + 34000; + break; + case WEAPONTYPE_MP5: + sampleIndex = SFX_MP5_LEFT; + frequency = SampleManager.GetSampleBaseFrequency(sampleIndex); + frequency += RandomDisplacement(frequency >> 5); + break; + default: + sampleIndex = SFX_UZI_LEFT; + frequency = SampleManager.GetSampleBaseFrequency(sampleIndex); + frequency += RandomDisplacement(frequency >> 5); + break; + } + m_sQueueSample.m_nSampleIndex = sampleIndex; +#else + m_sQueueSample.m_nSampleIndex = SFX_UZI_LEFT; +#endif + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = GunIndex++; + Vol = m_anRandomTable[2] % 15 + VEHICLE_ONE_SHOT_WEAPON_SHOT_FIRED_VOLUME; + if(GunIndex > 58) GunIndex = 53; +#ifdef FIX_BUGS + m_sQueueSample.m_nFrequency = frequency; +#else + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_UZI_LEFT); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 4); +#endif + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_WEAPON_SHOT_FIRED_MAX_DIST; + SET_SOUND_REFLECTION(TRUE); + break; + } + } + break; +#ifdef GTA_TRAIN + case SOUND_TRAIN_DOOR_CLOSE: + case SOUND_TRAIN_DOOR_OPEN: + m_sQueueSample.m_nSampleIndex = SFX_AIR_BRAKES; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 59; + m_sQueueSample.m_nFrequency = RandomDisplacement(1000) + 11025; + m_sQueueSample.m_nPriority = 5; + m_sQueueSample.m_fSpeedMultiplier = 5.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_TRAIN_DOOR_MAX_DIST; + maxDist = SQR(VEHICLE_ONE_SHOT_TRAIN_DOOR_MAX_DIST); + Vol = m_anRandomTable[1] % 20 + VEHICLE_ONE_SHOT_TRAIN_DOOR_VOLUME; + break; +#endif + case SOUND_SPLATTER: + { + static uint8 CrunchOffset = 0; + m_sQueueSample.m_nSampleIndex = CrunchOffset + SFX_PED_CRUNCH_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 48; + m_sQueueSample.m_nFrequency = RandomDisplacement(6000) + 16000; + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_SPLATTER_MAX_DIST; + CrunchOffset++; + maxDist = SQR(VEHICLE_ONE_SHOT_SPLATTER_MAX_DIST); + Vol = m_anRandomTable[4] % 20 + VEHICLE_ONE_SHOT_SPLATTER_VOLUME; + CrunchOffset %= 2; + SET_SOUND_REFLECTION(TRUE); + break; + } + case SOUND_CAR_PED_COLLISION: + eventVol = Min(20.0f, m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]); + Vol = Min(MAX_VOLUME, (3 * (eventVol / 20 * MAX_VOLUME)) / 2); + if (Vol == 0) + continue; + + m_sQueueSample.m_nSampleIndex = SFX_FIGHT_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 50; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 4); + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_CAR_PED_COLLISION_MAX_DIST; + maxDist = SQR(VEHICLE_ONE_SHOT_CAR_PED_COLLISION_MAX_DIST); + break; + case SOUND_PED_HELI_PLAYER_FOUND: + pedParams.m_bDistanceCalculated = params.m_bDistanceCalculated; + pedParams.m_fDistance = params.m_fDistance; + SetupPedComments(pedParams, SOUND_PED_HELI_PLAYER_FOUND); + continue; + case SOUND_PED_VCPA_PLAYER_FOUND: + pedParams.m_bDistanceCalculated = params.m_bDistanceCalculated; + pedParams.m_fDistance = params.m_fDistance; + SetupPedComments(pedParams, SOUND_PED_VCPA_PLAYER_FOUND); + continue; + case SOUND_CAR_TANK_TURRET_ROTATE: + eventVol = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; + if (eventVol > 96.0f / 2500.0f) + eventVol = 96.0f / 2500.0f; + m_sQueueSample.m_nSampleIndex = SFX_TANK_TURRET; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 79; + m_sQueueSample.m_nFrequency = (3000 * eventVol / (96.0f / 2500.0f)) + 9000; + m_sQueueSample.m_nPriority = 2; + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_nFramesToPlay = 3; + m_sQueueSample.m_MaxDistance = VEHICLE_ONE_SHOT_CAR_TANK_TURRET_MAX_DIST; + Vol = (37 * eventVol / (96.0f / 2500.0f)) + VEHICLE_ONE_SHOT_CAR_TANK_TURRET_VOLUME; + maxDist = SQR(VEHICLE_ONE_SHOT_CAR_TANK_TURRET_MAX_DIST); + bLoop = TRUE; + break; + default: + continue; + } + if (params.m_fDistance < maxDist) { + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + m_sQueueSample.m_nVolume = ComputeVolume(Vol, m_sQueueSample.m_MaxDistance, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + if (bLoop) { + m_sQueueSample.m_nLoopCount = 0; + m_sQueueSample.m_bStatic = FALSE; + } else { + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_bStatic = TRUE; + } + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + SET_EMITTING_VOLUME(Vol); + SET_SOUND_REVERB(TRUE); + if (stereo) { + if(m_sQueueSample.m_fDistance < 0.2f * m_sQueueSample.m_MaxDistance) { + m_sQueueSample.m_bIs2D = TRUE; + m_sQueueSample.m_nPan = 0; + } else { + stereo = FALSE; + m_sQueueSample.m_bIs2D = FALSE; + } + } else m_sQueueSample.m_bIs2D = FALSE; + AddSampleToRequestedQueue(); + if (stereo) { + m_sQueueSample.m_nPan = 127; + m_sQueueSample.m_nSampleIndex++; + m_sQueueSample.m_nCounter = GunIndex++; + if (GunIndex > 58) + GunIndex = 53; + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + continue; + + } + } + } +} + +#ifdef GTA_TRAIN +bool8 +cAudioManager::ProcessTrainNoise(cVehicleParams& params) +{ + CTrain *train; + uint8 Vol; + float speedMultipler; + + if (params.m_fDistance < SQR(TRAIN_NOISE_FAR_MAX_DIST)){ + if (params.m_fVelocityChange > 0.0f) { + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + train = (CTrain *)params.m_pVehicle; + speedMultipler = Min(1.0f, train->m_fSpeed * 250.0f / 51.0f); + Vol = (TRAIN_NOISE_VOLUME * speedMultipler); + if (train->m_fWagonPosition == 0.0f) { + m_sQueueSample.m_nVolume = ComputeVolume(Vol, TRAIN_NOISE_FAR_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 32; + m_sQueueSample.m_nSampleIndex = SFX_TRAIN_FAR; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 2; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_TRAIN_FAR); + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 3.0f; + m_sQueueSample.m_MaxDistance = TRAIN_NOISE_FAR_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 3; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + if (params.m_fDistance < SQR(TRAIN_NOISE_NEAR_MAX_DIST)) { + m_sQueueSample.m_nVolume = ComputeVolume(Vol, TRAIN_NOISE_NEAR_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 33; + m_sQueueSample.m_nSampleIndex = SFX_TRAIN_NEAR; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 5; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_TRAIN_NEAR) + 100 * m_sQueueSample.m_nEntityIndex % 987; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 6.0f; + m_sQueueSample.m_MaxDistance = TRAIN_NOISE_NEAR_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 3; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + } + return TRUE; + } + return FALSE; +} +#endif +bool8 +cAudioManager::ProcessBoatEngine(cVehicleParams& params) +{ + CBoat *boat; + float padRelativeAccerate; + + bool8 isV12 = FALSE; + static uint32 LastFreq = 2000; + static uint8 LastVol = 0; + + if (params.m_fDistance < SQR(BOAT_ENGINE_MAX_DIST)) { + boat = (CBoat *)params.m_pVehicle; + if(boat->GetStatus() == STATUS_WRECKED) + return TRUE; + + float freqModificator; + float volModificator; + uint8 BaseVol; + uint32 BaseFreq; + + switch(boat->GetModelIndex()) { + case MI_RIO: + freqModificator = 490; + volModificator = 60; + BaseVol = 20; + BaseFreq = 1888; + break; + case MI_PREDATOR: + case MI_SQUALO: + case MI_SPEEDER: + case MI_COASTG: + case MI_DINGHY: + case MI_JETMAX: + freqModificator = 6000; + volModificator = 60; + isV12 = TRUE; + BaseFreq = 9000; + BaseVol = 20; + break; + case MI_REEFER: + freqModificator = 715; + volModificator = 80; + BaseVol = 0; + BaseFreq = 3775; + break; + case MI_TROPIC: + case MI_MARQUIS: + freqModificator = 463; + volModificator = 60; + BaseVol = 20; + BaseFreq = 1782; + break; + default: + return TRUE; + } + + bool8 bIsPlayerVeh; + + if(FindPlayerVehicle() == params.m_pVehicle) { + padRelativeAccerate = Max(Pads[0].GetAccelerate(), Pads[0].GetBrake()) / 255.0f; + bIsPlayerVeh = TRUE; + } else { + padRelativeAccerate = Max(params.m_pVehicle->m_fGasPedal, params.m_pVehicle->m_fBrakePedal); + bIsPlayerVeh = FALSE; + } + + uint32 Freq = BaseFreq + (padRelativeAccerate * freqModificator); + uint8 Vol = BaseVol + (padRelativeAccerate * volModificator); + + if(!boat->bPropellerInWater) + Freq = (9 * Freq) / 8; + + if(bIsPlayerVeh) { + if(Freq > LastFreq) { + if(isV12) + Freq = Min(Freq, LastFreq + 100); + else + Freq = Min(Freq, LastFreq + 15); + } else { + if(isV12) + Freq = Max(Freq, LastFreq - 100); + else + Freq = Max(Freq, LastFreq - 15); + } + if(Vol > LastVol) + Vol = Min(Vol, LastVol + 3); + else + Vol = Max(Vol, LastVol - 3); + } + + if (Vol > 0) { + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + m_sQueueSample.m_nVolume = ComputeVolume(Vol, BOAT_ENGINE_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nFrequency = Freq; + m_sQueueSample.m_nCounter = 40; + if (isV12) + m_sQueueSample.m_nSampleIndex = SFX_BOAT_V12_LOOP; + else + m_sQueueSample.m_nSampleIndex = SFX_BOAT_CRUISER_LOOP; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_MaxDistance = BOAT_ENGINE_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 7; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + + if(boat->GetModelIndex() == MI_REEFER) { + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + m_sQueueSample.m_nVolume = ComputeVolume(BOAT_ENGINE_REEFER_IDLE_VOLUME, BOAT_ENGINE_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nFrequency = 6000; + m_sQueueSample.m_nCounter = 39; + m_sQueueSample.m_nSampleIndex = SFX_FISHING_BOAT_IDLE; + m_sQueueSample.m_nFrequency += (m_sQueueSample.m_nEntityIndex * 65536) % 1000; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(BOAT_ENGINE_REEFER_IDLE_VOLUME); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_MaxDistance = BOAT_ENGINE_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 7; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + if(bIsPlayerVeh) { + LastFreq = Freq; + LastVol = Vol; + } + return TRUE; + } + return FALSE; +} + +bool8 +cAudioManager::ProcessBoatMovingOverWater(cVehicleParams& params) +{ + float velocityChange; + uint8 Vol; + float multiplier; + + if (params.m_fDistance < SQR(BOAT_MOVING_OVER_WATER_MAX_DIST)) { + velocityChange = Abs(params.m_fVelocityChange); + if (velocityChange > 0.0005f && ((CBoat*)params.m_pVehicle)->bBoatInWater) { + velocityChange = Min(0.75f, velocityChange); + multiplier = (velocityChange - 0.0005f) / (1499.0f / 2000.0f); + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + Vol = (BOAT_MOVING_OVER_WATER_VOLUME * multiplier); + m_sQueueSample.m_nVolume = ComputeVolume(Vol, BOAT_MOVING_OVER_WATER_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 38; + m_sQueueSample.m_nSampleIndex = SFX_BOAT_WATER_LOOP; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nFrequency = (6050 * multiplier) + 16000; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_MaxDistance = BOAT_MOVING_OVER_WATER_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 6; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + return TRUE; + } + return FALSE; +} + +void +cAudioManager::ProcessPlane(cVehicleParams& params) +{ + switch (params.m_nIndex) { + case AIRTRAIN: + ProcessJumbo(params); + break; + case DEADDODO: + ProcessCesna(params); + break; + default: + break; + } +} + +#pragma region JUMBO +uint8 gJumboVolOffsetPercentage; + +void +DoJumboVolOffset() +{ + if (!(AudioManager.m_FrameCounter % (AudioManager.m_anRandomTable[0] % 6 + 3))) + gJumboVolOffsetPercentage = AudioManager.m_anRandomTable[1] % 60; +} + +void +cAudioManager::ProcessJumbo(cVehicleParams& params) +{ + CPlane *plane; + float position; + + if (params.m_fDistance < SQR(JUMBO_MAX_DIST)) { + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + plane = (CPlane*)params.m_pVehicle; + DoJumboVolOffset(); + position = PlanePathPosition[plane->m_nPlaneId]; + if (position > TakeOffPoint) { + if (300.0f + TakeOffPoint < position) { + if (LandingPoint - 350.0f < position) { + if (position > LandingPoint) { + if (plane->m_fSpeed > 0.103344f) + ProcessJumboDecel(plane); + else + ProcessJumboTaxi(); + } + else + ProcessJumboLanding(plane); + } + else + ProcessJumboFlying(); + } else + ProcessJumboTakeOff(plane); + } else { + if (plane->m_fSpeed > 0.103344f) + ProcessJumboAccel(plane); + else + ProcessJumboTaxi(); + } + } +} + +void +cAudioManager::ProcessJumboTaxi() +{ + if (SetupJumboFlySound(20)) { + if (SetupJumboTaxiSound(75)) + SetupJumboWhineSound(18, 29500); + } +} + +void +cAudioManager::ProcessJumboAccel(CPlane *plane) +{ + uint32 engineFreq; + uint8 vol; + float modificator; + float freqMult; + + if (SetupJumboFlySound(20)) { + modificator = Min(1.0f, (plane->m_fSpeed - 0.103344f) * 1.6760077f); + if (SetupJumboRumbleSound(MAX_VOLUME * modificator) && SetupJumboTaxiSound((1.0f - modificator) * 75)) { + if (modificator >= 0.2f) { + freqMult = 1; + engineFreq = 22050; + vol = MAX_VOLUME; + } else { + freqMult = modificator * 5; + vol = freqMult * MAX_VOLUME; + engineFreq = freqMult * 6050 + 16000; + } + SetupJumboEngineSound(vol, engineFreq); + SetupJumboWhineSound(18, 14600 * freqMult + 29500); + } + } +} + +void +cAudioManager::ProcessJumboTakeOff(CPlane *plane) +{ + float modificator = (PlanePathPosition[plane->m_nPlaneId] - TakeOffPoint) / 300; + if (SetupJumboFlySound((107 * modificator) + 20) && SetupJumboRumbleSound(MAX_VOLUME * (1.0f - modificator))) { + if (SetupJumboEngineSound(MAX_VOLUME, 22050)) + SetupJumboWhineSound(18 * (1.0f - modificator), 44100); + } +} + +void +cAudioManager::ProcessJumboFlying() +{ + if (SetupJumboFlySound(MAX_VOLUME)) + SetupJumboEngineSound(63, 22050); +} + +void +cAudioManager::ProcessJumboLanding(CPlane *plane) +{ + float modificator = (LandingPoint - PlanePathPosition[plane->m_nPlaneId]) / 350; + if (SetupJumboFlySound(107 * modificator + 20)) { + if (SetupJumboTaxiSound(75 * (1.0f - modificator))) { + SetupJumboEngineSound(MAX_VOLUME, 22050); + SetupJumboWhineSound(18 * (1.0f - modificator), 14600 * modificator + 29500); + } + } +} + +void +cAudioManager::ProcessJumboDecel(CPlane *plane) +{ + if (SetupJumboFlySound(20) && SetupJumboTaxiSound(75)) { + float modificator = Min(1.0f, (plane->m_fSpeed - 0.103344f) * 1.6760077f); + SetupJumboEngineSound(MAX_VOLUME * modificator, 6050* modificator + 16000); + SetupJumboWhineSound(18, 29500); + } +} + +bool8 +cAudioManager::SetupJumboTaxiSound(uint8 vol) +{ + if (m_sQueueSample.m_fDistance < JUMBO_ENGINE_SOUND_MAX_DIST) { + uint8 Vol = (vol >> 1) + ((vol >> 1) * m_sQueueSample.m_fDistance / JUMBO_ENGINE_SOUND_MAX_DIST); + + if (m_sQueueSample.m_fDistance / JUMBO_ENGINE_SOUND_MAX_DIST < 0.7f) + Vol -= Vol * gJumboVolOffsetPercentage / 100; + m_sQueueSample.m_nVolume = ComputeVolume(Vol, JUMBO_ENGINE_SOUND_MAX_DIST, m_sQueueSample.m_fDistance); + + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 1; + m_sQueueSample.m_nSampleIndex = SFX_JUMBO_TAXI; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_nFrequency = GetJumboTaxiFreq(); + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_MaxDistance = JUMBO_ENGINE_SOUND_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 4; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + return TRUE; + } + return FALSE; +} + +bool8 +cAudioManager::SetupJumboWhineSound(uint8 Vol, uint32 freq) +{ + if (m_sQueueSample.m_fDistance < JUMBO_WHINE_SOUND_MAX_DIST) { + m_sQueueSample.m_nVolume = ComputeVolume(Vol, JUMBO_WHINE_SOUND_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 2; + m_sQueueSample.m_nSampleIndex = SFX_JUMBO_WHINE; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_nFrequency = freq; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_MaxDistance = JUMBO_WHINE_SOUND_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 4; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + return TRUE; + } + return FALSE; +} + +bool8 +cAudioManager::SetupJumboEngineSound(uint8 Vol, uint32 freq) +{ + if (m_sQueueSample.m_fDistance < JUMBO_ENGINE_SOUND_MAX_DIST) { + uint8 FinalVol = Vol - gJumboVolOffsetPercentage / 100; + m_sQueueSample.m_nVolume = ComputeVolume(FinalVol, JUMBO_ENGINE_SOUND_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 3; + m_sQueueSample.m_nSampleIndex = SFX_JUMBO_ENGINE; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_nFrequency = freq; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(FinalVol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_MaxDistance = JUMBO_ENGINE_SOUND_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 4; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + return TRUE; + } + return FALSE; +} + +bool8 +cAudioManager::SetupJumboFlySound(uint8 Vol) +{ + if (m_sQueueSample.m_fDistance < JUMBO_MAX_DIST) { + m_sQueueSample.m_nVolume = ComputeVolume(Vol, JUMBO_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 0; + m_sQueueSample.m_nSampleIndex = SFX_JUMBO_DIST_FLY; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_JUMBO_DIST_FLY); + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_MaxDistance = JUMBO_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 5; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + return TRUE; + } + return FALSE; +} + +bool8 +cAudioManager::SetupJumboRumbleSound(uint8 Vol) +{ + if (m_sQueueSample.m_fDistance < JUMBO_RUMBLE_SOUND_MAX_DIST) { + m_sQueueSample.m_nVolume = ComputeVolume(Vol, JUMBO_RUMBLE_SOUND_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 5; + m_sQueueSample.m_nSampleIndex = SFX_JUMBO_RUMBLE; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = TRUE; + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_JUMBO_RUMBLE); + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_MaxDistance = JUMBO_RUMBLE_SOUND_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 12; + m_sQueueSample.m_nPan = 0; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + m_sQueueSample.m_nCounter = 6; + m_sQueueSample.m_nSampleIndex = SFX_JUMBO_RUMBLE; + m_sQueueSample.m_nFrequency += 200; + m_sQueueSample.m_nPan = 127; + AddSampleToRequestedQueue(); + } + return TRUE; + } + return FALSE; +} + +int32 +cAudioManager::GetJumboTaxiFreq() +{ + return (1.0f / 180 * 10950 * m_sQueueSample.m_fDistance) + 22050; // todo port fix to re3 +} + +#pragma endregion Some jumbo crap + +#pragma endregion All the vehicle audio code + +#pragma region PED AUDIO +void +cAudioManager::ProcessPed(CPhysical *ped) +{ + cPedParams params; + + m_sQueueSample.m_vecPos = ped->GetPosition(); + + params.m_bDistanceCalculated = FALSE; + params.m_pPed = (CPed *)ped; + params.m_fDistance = GetDistanceSquared(m_sQueueSample.m_vecPos); + ProcessPedOneShots(params); +} + +void +cAudioManager::ProcessPedOneShots(cPedParams ¶ms) +{ + uint8 Vol; + uint32 sampleIndex; + + CPed *ped = params.m_pPed; + + bool8 narrowSoundRange; + int16 sound; + bool8 stereo; + CWeapon *weapon; +#ifdef FIX_BUGS + float maxDist = 0.0f; // uninitialized variable +#else + float maxDist; +#endif + + static uint8 iSound = 21; + static uint32 iSplashFrame = 0; + + weapon = params.m_pPed->GetWeapon(); + for (uint32 i = 0; i < m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_AudioEvents; i++) { + stereo = FALSE; + narrowSoundRange = FALSE; + SET_SOUND_REFLECTION(FALSE); + sound = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i]; + switch (sound) { + case SOUND_SHIRT_WIND_FLAP: + if (params.m_pPed->IsPlayer() && params.m_pPed->m_pMyVehicle) { + if (m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i] > 0.0f) { + if (m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i] > 1.0f) + m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i] = 1.0f; + + Vol = PED_ONE_SHOT_SHIRT_FLAP_VOLUME * m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; + + switch (params.m_pPed->m_pMyVehicle->GetModelIndex()) + { + case MI_ANGEL: + case MI_FREEWAY: + m_sQueueSample.m_nSampleIndex = SFX_CAR_WIND_17; + break; + case MI_PCJ600: + m_sQueueSample.m_nSampleIndex = SFX_CAR_WIND_20; + break; + case MI_SANCHEZ: + m_sQueueSample.m_nSampleIndex = SFX_CAR_WIND_19; + break; + case MI_PIZZABOY: + case MI_FAGGIO: + m_sQueueSample.m_nSampleIndex = SFX_CAR_WIND_18; + break; + default: + continue; + }; + + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 71; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 3.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_SHIRT_FLAP_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_SHIRT_FLAP_MAX_DIST); + m_sQueueSample.m_nLoopCount = 0; + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 3; + } + } + continue; + case SOUND_WEAPON_MINIGUN_ATTACK: + m_sQueueSample.m_nSampleIndex = SFX_MINIGUN_FIRE_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 68; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_MINIGUN_FIRE_LEFT); + m_sQueueSample.m_nPriority = 2; + m_sQueueSample.m_fSpeedMultiplier = 3.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_MINIGUN_MAX_DIST; + Vol = PED_ONE_SHOT_MINIGUN_VOLUME; + maxDist = SQR(PED_ONE_SHOT_MINIGUN_MAX_DIST); + m_sQueueSample.m_nLoopCount = 0; + SET_LOOP_OFFSETS(SFX_MINIGUN_FIRE_LEFT) + SET_EMITTING_VOLUME(PED_ONE_SHOT_MINIGUN_VOLUME); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 3; + break; + case SOUND_WEAPON_MINIGUN_2: + m_sQueueSample.m_nSampleIndex = SFX_MINIGUN_FIRE_RIGHT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 69; + m_sQueueSample.m_nFrequency = 18569; + m_sQueueSample.m_nPriority = 2; + m_sQueueSample.m_fSpeedMultiplier = 3.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_MINIGUN_MAX_DIST; + Vol = PED_ONE_SHOT_MINIGUN_VOLUME * m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; + maxDist = SQR(PED_ONE_SHOT_MINIGUN_MAX_DIST); + m_sQueueSample.m_nLoopCount = 0; + SET_LOOP_OFFSETS(SFX_MINIGUN_FIRE_RIGHT) + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 3; + break; + case SOUND_WEAPON_MINIGUN_3: + m_sQueueSample.m_nSampleIndex = SFX_MINIGUN_STOP; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 69; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_MINIGUN_STOP); + m_sQueueSample.m_nPriority = 4; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_MINIGUN_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_MINIGUN_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = PED_ONE_SHOT_MINIGUN_VOLUME; + SET_EMITTING_VOLUME(PED_ONE_SHOT_MINIGUN_VOLUME); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REFLECTION(TRUE); + break; + case SOUND_SKATING: + { + uint32 soundParams = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; + uint8 param1 = soundParams & 0xFF; + uint32 param2 = soundParams >> 8; + m_sQueueSample.m_nSampleIndex = (m_anRandomTable[3] & 1) + SFX_SKATE_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound; + stereo = TRUE; + iSound++; + m_sQueueSample.m_nFrequency = m_anRandomTable[1] % 1000 + 17000; + if (param2 == 0) + m_sQueueSample.m_nFrequency = (3 * m_sQueueSample.m_nFrequency) >> 2; + m_sQueueSample.m_nPriority = 6; + m_sQueueSample.m_fSpeedMultiplier = 3.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_SKATING_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_SKATING_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = (m_anRandomTable[2] % 20 + PED_ONE_SHOT_SKATING_VOLUME) * param1 / MAX_VOLUME; + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REFLECTION(TRUE); + break; + } + case SOUND_FALL_LAND: + case SOUND_FALL_COLLAPSE: + if (ped->bIsInTheAir) + continue; + maxDist = SQR(PED_ONE_SHOT_FALL_MAX_DIST); + Vol = m_anRandomTable[3] % 20 + PED_ONE_SHOT_FALL_VOLUME; + if (ped->m_nSurfaceTouched == SURFACE_WATER) + m_sQueueSample.m_nSampleIndex = (m_anRandomTable[3] % 4) + SFX_FOOTSTEP_WATER_1; + else if (sound == SOUND_FALL_LAND) + m_sQueueSample.m_nSampleIndex = SFX_BODY_LAND; + else + m_sQueueSample.m_nSampleIndex = SFX_BODY_LAND_AND_FALL; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 1; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 17); + m_sQueueSample.m_nPriority = 2; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_FALL_MAX_DIST; + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REFLECTION(TRUE); + break; + case SOUND_STEP_START: + case SOUND_STEP_END: + if (params.m_pPed->bIsInTheAir) + continue; + Vol = m_anRandomTable[3] % 15 + PED_ONE_SHOT_STEP_VOLUME; + if (FindPlayerPed() != m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_pEntity) + Vol >>= 1; + maxDist = SQR(PED_ONE_SHOT_STEP_MAX_DIST); + switch (params.m_pPed->m_nSurfaceTouched) { + case SURFACE_GRASS: + sampleIndex = m_anRandomTable[1] % 5 + SFX_FOOTSTEP_GRASS_1; + break; + case SURFACE_GRAVEL: + case SURFACE_MUD_DRY: + sampleIndex = m_anRandomTable[4] % 5 + SFX_FOOTSTEP_GRAVEL_1; + break; + case SURFACE_CAR: + case SURFACE_GARAGE_DOOR: + case SURFACE_CAR_PANEL: + case SURFACE_THICK_METAL_PLATE: + case SURFACE_SCAFFOLD_POLE: + case SURFACE_LAMP_POST: + case SURFACE_FIRE_HYDRANT: + case SURFACE_GIRDER: + case SURFACE_METAL_CHAIN_FENCE: + case SURFACE_CONTAINER: + case SURFACE_NEWS_VENDOR: + sampleIndex = m_anRandomTable[0] % 5 + SFX_FOOTSTEP_METAL_1; + break; + case SURFACE_SAND: + sampleIndex = (m_anRandomTable[4] & 3) + SFX_FOOTSTEP_SAND_1; + break; + case SURFACE_WATER: + sampleIndex = (m_anRandomTable[3] & 3) + SFX_FOOTSTEP_WATER_1; + break; + case SURFACE_WOOD_CRATES: + case SURFACE_WOOD_BENCH: + case SURFACE_WOOD_SOLID: + sampleIndex = m_anRandomTable[2] % 5 + SFX_FOOTSTEP_WOOD_1; + break; + case SURFACE_HEDGE: + sampleIndex = m_anRandomTable[2] % 5 + SFX_COL_VEG_1; + break; + default: + sampleIndex = m_anRandomTable[2] % 5 + SFX_FOOTSTEP_CONCRETE_1; + break; + } + m_sQueueSample.m_nSampleIndex = sampleIndex; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i] - SOUND_STEP_START + 1; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 17); + switch (params.m_pPed->m_nMoveState) { + case PEDMOVE_WALK: + Vol >>= 2; + m_sQueueSample.m_nFrequency = 9 * m_sQueueSample.m_nFrequency / 10; + break; + case PEDMOVE_RUN: + Vol >>= 1; + m_sQueueSample.m_nFrequency = 11 * m_sQueueSample.m_nFrequency / 10; + break; + case PEDMOVE_SPRINT: + m_sQueueSample.m_nFrequency = 12 * m_sQueueSample.m_nFrequency / 10; + break; + default: + break; + } + m_sQueueSample.m_nPriority = 5; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_STEP_MAX_DIST; + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REFLECTION(TRUE); + break; + case SOUND_WEAPON_AK47_BULLET_ECHO: + { + uint32 weaponType = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; + switch (weaponType) { + case WEAPONTYPE_SPAS12_SHOTGUN: + m_sQueueSample.m_nSampleIndex = SFX_SPAS12_TAIL_LEFT; + break; + case WEAPONTYPE_M60: + case WEAPONTYPE_HELICANNON: + m_sQueueSample.m_nSampleIndex = SFX_M60_TAIL_LEFT; + case WEAPONTYPE_UZI: + case WEAPONTYPE_MP5: + m_sQueueSample.m_nSampleIndex = SFX_UZI_END_LEFT; + break; + case WEAPONTYPE_TEC9: + case WEAPONTYPE_SILENCED_INGRAM: + m_sQueueSample.m_nSampleIndex = SFX_TEC_TAIL; + break; + case WEAPONTYPE_M4: + case WEAPONTYPE_RUGER: + case WEAPONTYPE_SNIPERRIFLE: + case WEAPONTYPE_LASERSCOPE: + m_sQueueSample.m_nSampleIndex = SFX_RUGER_TAIL; + break; + break; + default: + continue; + } + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound++; + narrowSoundRange = TRUE; + switch (weaponType) { + case WEAPONTYPE_SILENCED_INGRAM: + m_sQueueSample.m_nFrequency = 26000; + break; + case WEAPONTYPE_TEC9: + m_sQueueSample.m_nFrequency = 13000; + break; + case WEAPONTYPE_M4: + m_sQueueSample.m_nFrequency = 15600; + break; + case WEAPONTYPE_LASERSCOPE: + m_sQueueSample.m_nFrequency = 7904; + break; + case WEAPONTYPE_SNIPERRIFLE: + m_sQueueSample.m_nFrequency = 9959; + break; + default: + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + break; + } + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 4); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = m_anRandomTable[4] % 10 + PED_ONE_SHOT_WEAPON_BULLET_ECHO_VOLUME; + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REFLECTION(TRUE); + break; + } + case SOUND_WEAPON_FLAMETHROWER_FIRE: + m_sQueueSample.m_nSampleIndex = SFX_FLAMETHROWER_START_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound++; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_FLAMETHROWER_START_LEFT); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 4); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_FLAMETHROWER_FIRE_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_FLAMETHROWER_FIRE_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = PED_ONE_SHOT_WEAPON_FLAMETHROWER_FIRE_VOLUME; + SET_EMITTING_VOLUME(PED_ONE_SHOT_WEAPON_FLAMETHROWER_FIRE_VOLUME); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + break; + case SOUND_WEAPON_SHOT_FIRED: + weapon = ped->GetWeapon(); + if (!weapon) + continue; + switch (weapon->m_eWeaponType) { + case WEAPONTYPE_PYTHON: + m_sQueueSample.m_nSampleIndex = SFX_PYTHON_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound++; + narrowSoundRange = TRUE; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_PYTHON_LEFT); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = MAX_VOLUME; + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REFLECTION(TRUE); + stereo = TRUE; + break; + case WEAPONTYPE_COLT45: + m_sQueueSample.m_nSampleIndex = SFX_COLT45_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound++; + narrowSoundRange = TRUE; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_COLT45_LEFT); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = m_anRandomTable[1] % 10 + PED_ONE_SHOT_WEAPON_COLT45_VOLUME; + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REFLECTION(TRUE); + stereo = TRUE; + break; + case WEAPONTYPE_ROCKET: + case WEAPONTYPE_ROCKETLAUNCHER: + m_sQueueSample.m_nSampleIndex = SFX_ROCKET_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound++; + narrowSoundRange = TRUE; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_ROCKET_LEFT); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = m_anRandomTable[0] % 20 + PED_ONE_SHOT_WEAPON_ROCKETLAUNCHER_VOLUME; + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REFLECTION(TRUE); + stereo = TRUE; + break; + case WEAPONTYPE_FLAMETHROWER: + m_sQueueSample.m_nSampleIndex = SFX_FLAMETHROWER_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = 9; + Vol = PED_ONE_SHOT_WEAPON_FLAMETHROWER_VOLUME; + m_sQueueSample.m_nFrequency = (10 * m_sQueueSample.m_nEntityIndex % 2048) + SampleManager.GetSampleBaseFrequency(SFX_FLAMETHROWER_LEFT); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_FLAMETHROWER_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_FLAMETHROWER_MAX_DIST); + m_sQueueSample.m_nLoopCount = 0; + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + SET_EMITTING_VOLUME(PED_ONE_SHOT_WEAPON_FLAMETHROWER_VOLUME); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 6; + stereo = TRUE; + break; + case WEAPONTYPE_M60: + case WEAPONTYPE_HELICANNON: + m_sQueueSample.m_nSampleIndex = SFX_M60_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound++; + narrowSoundRange = TRUE; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_M60_LEFT); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = PED_ONE_SHOT_WEAPON_M16_VOLUME; + SET_EMITTING_VOLUME(PED_ONE_SHOT_WEAPON_M16_VOLUME); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + stereo = TRUE; + break; + case WEAPONTYPE_MP5: + m_sQueueSample.m_nSampleIndex = SFX_MP5_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound++; + narrowSoundRange = TRUE; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_MP5_LEFT); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = m_anRandomTable[3] % 15 + PED_ONE_SHOT_WEAPON_UZI_VOLUME; + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + stereo = TRUE; + break; + case WEAPONTYPE_TEC9: + m_sQueueSample.m_nSampleIndex = SFX_TEC_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound++; + narrowSoundRange = TRUE; + m_sQueueSample.m_nFrequency = RandomDisplacement(500) + 17000; + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = m_anRandomTable[3] % 15 + PED_ONE_SHOT_WEAPON_UZI_VOLUME; + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + stereo = TRUE; + break; + case WEAPONTYPE_SILENCED_INGRAM: + m_sQueueSample.m_nSampleIndex = SFX_TEC_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound++; + narrowSoundRange = TRUE; + m_sQueueSample.m_nFrequency = RandomDisplacement(1000) + 34000; + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = m_anRandomTable[3] % 15 + PED_ONE_SHOT_WEAPON_UZI_VOLUME; + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + stereo = TRUE; + break; + case WEAPONTYPE_RUGER: + m_sQueueSample.m_nSampleIndex = SFX_RUGER_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound++; + narrowSoundRange = TRUE; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_RUGER_LEFT); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = m_anRandomTable[3] % 15 + PED_ONE_SHOT_WEAPON_M4_VOLUME; + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + stereo = TRUE; + break; + case WEAPONTYPE_M4: + m_sQueueSample.m_nSampleIndex = SFX_RUGER_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound++; + narrowSoundRange = TRUE; + m_sQueueSample.m_nFrequency = RandomDisplacement(1000) + 43150; + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = m_anRandomTable[3] % 15 + PED_ONE_SHOT_WEAPON_M4_VOLUME; + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + stereo = TRUE; + break; + case WEAPONTYPE_UZI: + case WEAPONTYPE_MINIGUN: + m_sQueueSample.m_nSampleIndex = SFX_UZI_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound++; + narrowSoundRange = TRUE; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_UZI_LEFT); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = m_anRandomTable[3] % 15 + PED_ONE_SHOT_WEAPON_UZI_VOLUME; + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + stereo = TRUE; + break; + case WEAPONTYPE_SNIPERRIFLE: + case WEAPONTYPE_LASERSCOPE: + m_sQueueSample.m_nSampleIndex = SFX_SNIPER_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound++; + narrowSoundRange = TRUE; + if (weapon->m_eWeaponType == WEAPONTYPE_SNIPERRIFLE) + m_sQueueSample.m_nFrequency = 25472; + else + m_sQueueSample.m_nFrequency = 20182; + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = m_anRandomTable[4] % 10 + PED_ONE_SHOT_WEAPON_SNIPERRIFLE_VOLUME; + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REFLECTION(TRUE); + stereo = TRUE; + break; + case WEAPONTYPE_SPAS12_SHOTGUN: + m_sQueueSample.m_nSampleIndex = SFX_SPAS12_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound++; + narrowSoundRange = TRUE; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_SPAS12_LEFT); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = m_anRandomTable[2] % 10 + PED_ONE_SHOT_WEAPON_SHOTGUN_VOLUME; + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REFLECTION(TRUE); + stereo = TRUE; + break; + case WEAPONTYPE_SHOTGUN: + case WEAPONTYPE_STUBBY_SHOTGUN: + m_sQueueSample.m_nSampleIndex = SFX_SHOTGUN_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound++; + narrowSoundRange = TRUE; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_SHOTGUN_LEFT); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_BULLET_ECHO_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = m_anRandomTable[2] % 10 + PED_ONE_SHOT_WEAPON_SHOTGUN_VOLUME; + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REFLECTION(TRUE); + stereo = TRUE; + break; + default: + continue; + } + break; + case SOUND_WEAPON_RELOAD: + switch ((int32)m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]) { + case WEAPONTYPE_COLT45: + case WEAPONTYPE_PYTHON: + m_sQueueSample.m_nSampleIndex = SFX_PISTOL_RELOAD; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_PISTOL_RELOAD) + RandomDisplacement(300); + break; + case WEAPONTYPE_TEC9: + case WEAPONTYPE_UZI: + case WEAPONTYPE_SILENCED_INGRAM: + case WEAPONTYPE_MP5: + case WEAPONTYPE_M4: + case WEAPONTYPE_M60: + case WEAPONTYPE_HELICANNON: + m_sQueueSample.m_nSampleIndex = SFX_AK47_RELOAD; + m_sQueueSample.m_nFrequency = 39243; + break; + case WEAPONTYPE_SHOTGUN: + case WEAPONTYPE_SPAS12_SHOTGUN: + case WEAPONTYPE_STUBBY_SHOTGUN: + case WEAPONTYPE_RUGER: + m_sQueueSample.m_nSampleIndex = SFX_AK47_RELOAD; + m_sQueueSample.m_nFrequency = 30290; + break; + case WEAPONTYPE_ROCKET: + case WEAPONTYPE_ROCKETLAUNCHER: + m_sQueueSample.m_nSampleIndex = SFX_ROCKET_RELOAD; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_ROCKET_RELOAD); + break; + case WEAPONTYPE_SNIPERRIFLE: + case WEAPONTYPE_LASERSCOPE: + m_sQueueSample.m_nSampleIndex = SFX_RIFLE_RELOAD; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_RIFLE_RELOAD); + break; + default: + continue; + } + Vol = PED_ONE_SHOT_WEAPON_RELOAD_VOLUME; + m_sQueueSample.m_nCounter = iSound++; + narrowSoundRange = TRUE; + m_sQueueSample.m_nFrequency += RandomDisplacement(300); + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nPriority = 5; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_RELOAD_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_RELOAD_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + SET_EMITTING_VOLUME(PED_ONE_SHOT_WEAPON_RELOAD_VOLUME); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REFLECTION(TRUE); + break; + case SOUND_WEAPON_HIT_PED: + m_sQueueSample.m_nSampleIndex = SFX_BULLET_PED; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound++; + narrowSoundRange = TRUE; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_BULLET_PED); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 3); + m_sQueueSample.m_nPriority = 7; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_HIT_PED_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_HIT_PED_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = m_anRandomTable[0] % 20 + PED_ONE_SHOT_WEAPON_HIT_PED_VOLUME; + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + break; + case SOUND_WEAPON_CHAINSAW_MADECONTACT: + if (FindVehicleOfPlayer()) + continue; + if ((int32)m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i] != ENTITY_TYPE_PED) + ReportCollision(params.m_pPed, params.m_pPed, SURFACE_CAR, SURFACE_TARMAC, 0.0f, 0.09f); + m_sQueueSample.m_nSampleIndex = SFX_CAR_CHAINSAW_ATTACK; +#ifdef GTA_PS2 + m_sQueueSample.m_nBankIndex = SFX_BANK_CAR_CHAINSAW; +#else + m_sQueueSample.m_nBankIndex = SFX_BANK_0; +#endif + m_sQueueSample.m_nCounter = 68; + m_sQueueSample.m_nFrequency = RandomDisplacement(500) + 22000; + m_sQueueSample.m_nPriority = 2; + m_sQueueSample.m_fSpeedMultiplier = 3.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_CHAINSAW_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_CHAINSAW_MAX_DIST); + m_sQueueSample.m_nLoopCount = 0; + Vol = PED_ONE_SHOT_WEAPON_CHAINSAW_VOLUME; + SET_LOOP_OFFSETS(SFX_CAR_CHAINSAW_ATTACK) + SET_EMITTING_VOLUME(PED_ONE_SHOT_WEAPON_CHAINSAW_VOLUME); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 5; + break; + case SOUND_WEAPON_CHAINSAW_ATTACK: + if (FindVehicleOfPlayer()) + continue; + m_sQueueSample.m_nSampleIndex = SFX_CAR_CHAINSAW_ATTACK; +#ifdef GTA_PS2 + m_sQueueSample.m_nBankIndex = SFX_BANK_CAR_CHAINSAW; +#else + m_sQueueSample.m_nBankIndex = SFX_BANK_0; +#endif + m_sQueueSample.m_nCounter = 68; + m_sQueueSample.m_nFrequency = 27000; + m_sQueueSample.m_nPriority = 2; + m_sQueueSample.m_fSpeedMultiplier = 3.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_CHAINSAW_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_CHAINSAW_MAX_DIST); + m_sQueueSample.m_nLoopCount = 0; + Vol = PED_ONE_SHOT_WEAPON_CHAINSAW_VOLUME; + SET_LOOP_OFFSETS(SFX_CAR_CHAINSAW_ATTACK) + SET_EMITTING_VOLUME(PED_ONE_SHOT_WEAPON_CHAINSAW_VOLUME); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 5; + break; + case SOUND_WEAPON_CHAINSAW_IDLE: + if (FindVehicleOfPlayer()) + continue; + m_sQueueSample.m_nSampleIndex = SFX_CAR_CHAINSAW_IDLE; +#ifdef GTA_PS2 + m_sQueueSample.m_nBankIndex = SFX_BANK_CAR_CHAINSAW; +#else + m_sQueueSample.m_nBankIndex = SFX_BANK_0; +#endif + m_sQueueSample.m_nCounter = 70; + m_sQueueSample.m_nFrequency = 27000; + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 3.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_CHAINSAW_IDLE_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_WEAPON_CHAINSAW_IDLE_MAX_DIST); + m_sQueueSample.m_nLoopCount = 0; + Vol = PED_ONE_SHOT_WEAPON_CHAINSAW_VOLUME; + SET_LOOP_OFFSETS(SFX_CAR_CHAINSAW_IDLE) + SET_EMITTING_VOLUME(PED_ONE_SHOT_WEAPON_CHAINSAW_VOLUME); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 5; + break; + case SOUND_WEAPON_BAT_ATTACK: + case SOUND_WEAPON_KNIFE_ATTACK: + { + uint32 soundParams = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; // wtf? storing int as float + uint8 damagerType = soundParams & 0xFF; + uint32 weaponType = soundParams >> 8; + if (damagerType == ENTITY_TYPE_PED) { + switch (weaponType) { + case WEAPONTYPE_SCREWDRIVER: + case WEAPONTYPE_KNIFE: + case WEAPONTYPE_CLEAVER: + case WEAPONTYPE_MACHETE: + case WEAPONTYPE_KATANA: + if (sound == SOUND_WEAPON_KNIFE_ATTACK) + m_sQueueSample.m_nSampleIndex = SFX_KNIFE_SLASH; + else + m_sQueueSample.m_nSampleIndex = SFX_KNIFE_STAB; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + break; + case WEAPONTYPE_HAMMER: + m_sQueueSample.m_nSampleIndex = m_anRandomTable[3] % 2 + SFX_HAMMER_HIT_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + break; + default: + m_sQueueSample.m_nSampleIndex = SFX_BAT_HIT_LEFT; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = RandomDisplacement(2000) + 22000; + stereo = TRUE; + break; + } + } + else { + m_sQueueSample.m_nSampleIndex = m_anRandomTable[4] % 6 + SFX_COL_CAR_PANEL_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 4); + } + m_sQueueSample.m_nCounter = iSound++; + narrowSoundRange = TRUE; + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_PUNCH_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_PUNCH_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = m_anRandomTable[2] % 20 + PED_ONE_SHOT_PUNCH_VOLUME; + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REFLECTION(TRUE); + break; + } + case SOUND_FIGHT_37: + m_sQueueSample.m_nSampleIndex = SFX_FIGHT_1; + m_sQueueSample.m_nFrequency = 18000; + goto AddFightSound; + case SOUND_FIGHT_38: + m_sQueueSample.m_nSampleIndex = SFX_FIGHT_1; + m_sQueueSample.m_nFrequency = 16500; + goto AddFightSound; + case SOUND_FIGHT_39: + m_sQueueSample.m_nSampleIndex = SFX_FIGHT_1; + m_sQueueSample.m_nFrequency = 20000; + goto AddFightSound; + case SOUND_FIGHT_40: + case SOUND_186: + m_sQueueSample.m_nSampleIndex = SFX_FIGHT_2; + m_sQueueSample.m_nFrequency = 18000; + goto AddFightSound; + case SOUND_FIGHT_41: + m_sQueueSample.m_nSampleIndex = SFX_FIGHT_2; + m_sQueueSample.m_nFrequency = 16500; + goto AddFightSound; + case SOUND_FIGHT_42: + m_sQueueSample.m_nSampleIndex = SFX_FIGHT_2; + m_sQueueSample.m_nFrequency = 20000; + goto AddFightSound; + case SOUND_FIGHT_43: + m_sQueueSample.m_nSampleIndex = SFX_FIGHT_4; + m_sQueueSample.m_nFrequency = 18000; + goto AddFightSound; + case SOUND_FIGHT_44: + m_sQueueSample.m_nSampleIndex = SFX_FIGHT_4; + m_sQueueSample.m_nFrequency = 16500; + goto AddFightSound; + case SOUND_FIGHT_45: + m_sQueueSample.m_nSampleIndex = SFX_FIGHT_4; + m_sQueueSample.m_nFrequency = 20000; + goto AddFightSound; + case SOUND_FIGHT_46: + case SOUND_187: + m_sQueueSample.m_nSampleIndex = SFX_FIGHT_5; + m_sQueueSample.m_nFrequency = 18000; + goto AddFightSound; + case SOUND_FIGHT_47: + m_sQueueSample.m_nSampleIndex = SFX_FIGHT_5; + m_sQueueSample.m_nFrequency = 16500; + goto AddFightSound; + case SOUND_FIGHT_48: + m_sQueueSample.m_nSampleIndex = SFX_FIGHT_5; + m_sQueueSample.m_nFrequency = 20000; + AddFightSound: + { + uint32 soundParams = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; // wtf? storing int as float + uint8 damagerType = soundParams & 0xFF; + uint32 weaponType = soundParams >> 8; + + if (damagerType == ENTITY_TYPE_PED) { + if (weaponType == WEAPONTYPE_BRASSKNUCKLE) { + CPed* ped = params.m_pPed; + uint32 fightMove = ped->m_curFightMove; + if (fightMove == FIGHTMOVE_BACKLEFT || fightMove == FIGHTMOVE_STDPUNCH || fightMove == FIGHTMOVE_PUNCH || + ped->m_nPedState == PED_ATTACK) { + CEntity* damageEntity = ped->m_pDamageEntity; + if (!damageEntity) + m_sQueueSample.m_nSampleIndex = m_anRandomTable[3] % 2 + SFX_HAMMER_HIT_1; + else if (damageEntity->GetType() != ENTITY_TYPE_PED) + m_sQueueSample.m_nSampleIndex = m_anRandomTable[3] % 2 + SFX_HAMMER_HIT_1; + else if (((CPed*)damageEntity)->m_curFightMove != FIGHTMOVE_HITHEAD) + m_sQueueSample.m_nSampleIndex = m_anRandomTable[3] % 2 + SFX_HAMMER_HIT_1; + else + m_sQueueSample.m_nSampleIndex = SFX_HAMMER_HIT_1; + } + } + } + else { + m_sQueueSample.m_nSampleIndex = m_anRandomTable[4] % 6 + SFX_COL_CAR_PANEL_1; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 16); + } + } + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound; + narrowSoundRange = TRUE; + iSound++; + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_PUNCH_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_PUNCH_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = m_anRandomTable[3] % 26 + PED_ONE_SHOT_PUNCH_VOLUME; + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REFLECTION(TRUE); + break; + case SOUND_SPLASH: + if (m_FrameCounter <= iSplashFrame) + continue; + iSplashFrame = m_FrameCounter + 6; + m_sQueueSample.m_nSampleIndex = SFX_SPLASH_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound++; + narrowSoundRange = TRUE; + m_sQueueSample.m_nFrequency = RandomDisplacement(1400) + 20000; + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_SPLASH_MAX_DIST; + maxDist = SQR(PED_ONE_SHOT_SPLASH_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + Vol = m_anRandomTable[2] % 30 + PED_ONE_SHOT_SPLASH_PED_VOLUME; + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REFLECTION(TRUE); + break; + case SOUND_MELEE_ATTACK_START: + { + uint32 weaponType = ((uint32)m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]) >> 8; + switch (weaponType) + { + case WEAPONTYPE_SCREWDRIVER: + case WEAPONTYPE_KNIFE: + case WEAPONTYPE_CLEAVER: + case WEAPONTYPE_MACHETE: + case WEAPONTYPE_KATANA: + m_sQueueSample.m_nSampleIndex = SFX_KNIFE_SWING; + break; + default: + m_sQueueSample.m_nSampleIndex = SFX_GOLF_CLUB_SWING; + break; + } + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nCounter = iSound++; + narrowSoundRange = TRUE; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 4); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = PED_ONE_SHOT_WEAPON_HIT_PED_MAX_DIST; + if (weaponType == WEAPONTYPE_UNARMED || weaponType == WEAPONTYPE_BRASSKNUCKLE) + Vol = m_anRandomTable[1] % 10 + 35; + else + Vol = m_anRandomTable[2] % 20 + 70; + maxDist = SQR(PED_ONE_SHOT_WEAPON_HIT_PED_MAX_DIST); + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + SET_EMITTING_VOLUME(Vol); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REFLECTION(TRUE); + break; + } + default: + SetupPedComments(params, sound); + continue; + } + + if (narrowSoundRange && iSound > 60) + iSound = 21; + if (params.m_fDistance < maxDist) { + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + m_sQueueSample.m_nVolume = ComputeVolume(Vol, m_sQueueSample.m_MaxDistance, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + if (stereo) { + if (m_sQueueSample.m_fDistance < 0.2f * m_sQueueSample.m_MaxDistance) { + m_sQueueSample.m_bIs2D = TRUE; + m_sQueueSample.m_nPan = 0; + } else + stereo = FALSE; + } + SET_SOUND_REVERB(TRUE); + AddSampleToRequestedQueue(); + if (stereo) { + m_sQueueSample.m_nPan = 127; + m_sQueueSample.m_nSampleIndex++; + if (m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i] == SOUND_WEAPON_SHOT_FIRED && + weapon->m_eWeaponType == WEAPONTYPE_FLAMETHROWER) + m_sQueueSample.m_nCounter++; + else { + m_sQueueSample.m_nCounter = iSound++; + if (iSound > 60) + iSound = 21; + } + AddSampleToRequestedQueue(); + } + } + } + } +} + +void +cAudioManager::SetPedTalkingStatus(CPed *ped, bool8 status) +{ + if (ped != nil) + ped->m_canTalk = status; +} + +void +cAudioManager::SetPlayersMood(uint8 mood, uint32 time) +{ + if (!m_bIsInitialised) return; + + if (mood < MAX_PLAYER_MOODS) { + m_nPlayerMood = mood; + m_nPlayerMoodTimer = CTimer::GetTimeInMilliseconds() + time; + } + +} + +void +cAudioManager::ProcessPlayerMood() +{ + CPlayerPed *playerPed; + uint32 curTime = CTimer::GetTimeInMilliseconds(); + + if (m_nPlayerMoodTimer > curTime) + return; + + playerPed = FindPlayerPed(); + if (!playerPed) + return; + + if (playerPed->m_pWanted->GetWantedLevel() > 3) + m_nPlayerMood = PLAYER_MOOD_ANGRY; + else if (playerPed->m_pWanted->GetWantedLevel() > 1) + m_nPlayerMood = PLAYER_MOOD_PISSED_OFF; + else if (CTheScripts::GetLastMissionPassedTime() != -1) { + if (curTime >= CTheScripts::GetLastMissionPassedTime()) { + if (curTime < CTheScripts::GetLastMissionPassedTime() + 180000) + m_nPlayerMood = PLAYER_MOOD_WISECRACKING; + else + m_nPlayerMood = PLAYER_MOOD_CALM; + } else + CTheScripts::GetLastMissionPassedTime() = curTime; + } else + m_nPlayerMood = PLAYER_MOOD_CALM; +} + +void +cAudioManager::SetupPedComments(cPedParams ¶ms, uint16 sound) +{ + CPed *ped = params.m_pPed; + uint8 Vol; + float maxDist; + tPedComment pedComment; + + if(ped != nil) { + if(!ped->m_canTalk) return; + m_bGenericSfx = FALSE; + pedComment.m_nSampleIndex = GetPedCommentSfx(ped, sound); + if(pedComment.m_nSampleIndex == NO_SAMPLE) + return; + maxDist = PED_COMMENT_MAX_DIST; + } else { + m_bGenericSfx = TRUE; + switch(sound) { + case SOUND_PED_HELI_PLAYER_FOUND: + maxDist = PED_COMMENT_POLICE_HELI_MAX_DIST; + pedComment.m_nSampleIndex = m_anRandomTable[m_sQueueSample.m_nEntityIndex % 4] % 20 + SFX_POLICE_HELI_1; + break; + case SOUND_PED_VCPA_PLAYER_FOUND: + maxDist = PED_COMMENT_POLICE_HELI_MAX_DIST; +#ifdef FIX_BUGS + pedComment.m_nSampleIndex = m_anRandomTable[m_sQueueSample.m_nEntityIndex % 4] % 23 + SFX_POLICE_BOAT_1; +#else + pedComment.m_nSampleIndex = m_anRandomTable[m_sQueueSample.m_nEntityIndex % 4] % 29 + SFX_POLICE_BOAT_1; +#endif + break; + case SOUND_INJURED_PED_MALE_OUCH: + maxDist = PED_COMMENT_MAX_DIST; + pedComment.m_nSampleIndex = m_anRandomTable[m_sQueueSample.m_nEntityIndex % 4] % 41 + SFX_GENERIC_MALE_GRUNT_1; + break; + case SOUND_INJURED_PED_FEMALE: + maxDist = PED_COMMENT_MAX_DIST; + pedComment.m_nSampleIndex = m_anRandomTable[m_sQueueSample.m_nEntityIndex % 4] % 33 + SFX_GENERIC_FEMALE_GRUNT_1; + break; + default: + return; + } + } + + if(params.m_fDistance < SQR(maxDist)) { + CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); + if(CWorld::GetIsLineOfSightClear(TheCamera.GetPosition(), m_sQueueSample.m_vecPos, true, false, false, false, false, false)) + Vol = PED_COMMENT_VOLUME; + else + Vol = PED_COMMENT_VOLUME_BEHIND_WALL; + m_sQueueSample.m_nVolume = ComputeVolume(Vol, maxDist, m_sQueueSample.m_fDistance); + pedComment.m_nLoadingTimeout = 10; + if (m_sQueueSample.m_nVolume > 0) { + pedComment.m_nEntityIndex = m_sQueueSample.m_nEntityIndex; + pedComment.m_vecPos = m_sQueueSample.m_vecPos; + pedComment.m_fDistance = m_sQueueSample.m_fDistance; + pedComment.m_nVolume = m_sQueueSample.m_nVolume; +#if defined(EXTERNAL_3D_SOUND) && defined(FIX_BUGS) + pedComment.m_nEmittingVolume = Vol; +#endif + m_sPedComments.Add(&pedComment); + } + } +} + +uint32 +cAudioManager::GetPedCommentSfx(CPed *ped, uint16 sound) +{ + if(ped->m_nPedState != PED_FALL || sound == SOUND_PED_DAMAGE || sound == SOUND_PED_HIT || sound == SOUND_PED_LAND) { + if(ped->m_getUpTimer == UINT32_MAX || ped->m_getUpTimer > CTimer::GetTimeInMilliseconds()) { + if(sound != SOUND_PED_DAMAGE && sound != SOUND_PED_HIT && sound != SOUND_PED_LAND) return NO_SAMPLE; + } + if(ped->IsPlayer()) return GetPlayerTalkSfx(ped, sound); + switch(ped->GetModelIndex()) { + case MI_PLAYER: return GetPlayerTalkSfx(ped, sound); + case MI_COP: return GetCopTalkSfx(ped, sound); + case MI_SWAT: return GetSwatTalkSfx(ped, sound); + case MI_FBI: return GetFBITalkSfx(ped, sound); + case MI_ARMY: return GetArmyTalkSfx(ped, sound); + case MI_MEDIC: return GetMedicTalkSfx(ped, sound); + case MI_FIREMAN: return GetFiremanTalkSfx(ped, sound); + case MI_MALE01: return GetDefaultTalkSfx(ped, sound); + case MI_HFYST: return GetHFYSTTalkSfx(ped, sound); + case MI_HFOST: return GetHFOSTTalkSfx(ped, sound); + case MI_HMYST: return GetHMYSTTalkSfx(ped, sound); + case MI_HMOST: return GetHMOSTTalkSfx(ped, sound); + case MI_HFYRI: return GetHFYRITalkSfx(ped, sound); + case MI_HFORI: return GetHFORITalkSfx(ped, sound); + case MI_HMYRI: return GetHMYRITalkSfx(ped, sound); + case MI_HMORI: return GetHMORITalkSfx(ped, sound); + case MI_HFYBE: return GetHFYBETalkSfx(ped, sound); + case MI_HFOBE: return GetHFOBETalkSfx(ped, sound); + case MI_HMYBE: return GetHMYBETalkSfx(ped, sound); + case MI_HMOBE: return GetHMOBETalkSfx(ped, sound); + case MI_HFYBU: return GetHFYBUTalkSfx(ped, sound); + case MI_HFYMD: return GetHFYMDTalkSfx(ped, sound); + case MI_HFYCG: return GetHFYCGTalkSfx(ped, sound); + case MI_HFYPR: return GetHFYPRTalkSfx(ped, sound); + case MI_HFOTR: return GetHFOTRTalkSfx(ped, sound); + case MI_HMOTR: return GetHMOTRTalkSfx(ped, sound); + case MI_HMYAP: return GetHMYAPTalkSfx(ped, sound); + case MI_HMOCA: return GetHMOCATalkSfx(ped, sound); + case MI_BMODK: return GetBMODKTalkSfx(ped, sound); + case MI_BMYKR: return GetBMYCRTalkSfx(ped, sound); + case MI_BFYST: return GetBFYSTTalkSfx(ped, sound); + case MI_BFOST: return GetBFOSTTalkSfx(ped, sound); + case MI_BMYST: return GetBMYSTTalkSfx(ped, sound); + case MI_BMOST: return GetBMOSTTalkSfx(ped, sound); + case MI_BFYRI: return GetBFYRITalkSfx(ped, sound); + case MI_BFORI: return GetBFORITalkSfx(ped, sound); + case MI_BMYRI: return GetBMYRITalkSfx(ped, sound); + case MI_BFYBE: return GetBFYBETalkSfx(ped, sound); + case MI_BMYBE: return GetBMYBETalkSfx(ped, sound); + case MI_BFOBE: return GetBFOBETalkSfx(ped, sound); + case MI_BMOBE: return GetBMOBETalkSfx(ped, sound); + case MI_BMYBU: return GetBMYBUTalkSfx(ped, sound); + case MI_BFYPR: return GetBFYPRTalkSfx(ped, sound); + case MI_BFOTR: return GetBFOTRTalkSfx(ped, sound); + case MI_BMOTR: return GetBMOTRTalkSfx(ped, sound); + case MI_BMYPI: return GetBMYPITalkSfx(ped, sound); + case MI_BMYBB: return GetBMYBBTalkSfx(ped, sound); + case MI_WMYCR: return GetWMYCRTalkSfx(ped, sound); + case MI_WFYST: return GetWFYSTTalkSfx(ped, sound); + case MI_WFOST: return GetWFOSTTalkSfx(ped, sound); + case MI_WMYST: return GetWMYSTTalkSfx(ped, sound); + case MI_WMOST: return GetWMOSTTalkSfx(ped, sound); + case MI_WFYRI: return GetWFYRITalkSfx(ped, sound); + case MI_WFORI: return GetWFORITalkSfx(ped, sound); + case MI_WMYRI: return GetWMYRITalkSfx(ped, sound); + case MI_WMORI: return GetWMORITalkSfx(ped, sound); + case MI_WFYBE: return GetWFYBETalkSfx(ped, sound); + case MI_WMYBE: return GetWMYBETalkSfx(ped, sound); + case MI_WFOBE: return GetWFOBETalkSfx(ped, sound); + case MI_WMOBE: return GetWMOBETalkSfx(ped, sound); + case MI_WMYCW: return GetWMYCWTalkSfx(ped, sound); + case MI_WMYGO: return GetWMYGOTalkSfx(ped, sound); + case MI_WFOGO: return GetWFOGOTalkSfx(ped, sound); + case MI_WMOGO: return GetWMOGOTalkSfx(ped, sound); + case MI_WFYLG: return GetWFYLGTalkSfx(ped, sound); + case MI_WMYLG: return GetWMYLGTalkSfx(ped, sound); + case MI_WFYBU: return GetWFYBUTalkSfx(ped, sound); + case MI_WMYBU: return GetWMYBUTalkSfx(ped, sound); + case MI_WMOBU: return GetWMOBUTalkSfx(ped, sound); + case MI_WFYPR: return GetWFYPRTalkSfx(ped, sound); + case MI_WFOTR: return GetWFOTRTalkSfx(ped, sound); + case MI_WMOTR: return GetWMOTRTalkSfx(ped, sound); + case MI_WMYPI: return GetWMYPITalkSfx(ped, sound); + case MI_WMOCA: return GetWMOCATalkSfx(ped, sound); + case MI_WFYJG: return GetWFYJGTalkSfx(ped, sound); + case MI_WMYJG: return GetWMYJGTalkSfx(ped, sound); + case MI_WFYSK: return GetWFYSKTalkSfx(ped, sound); + case MI_WMYSK: return GetWMYSKTalkSfx(ped, sound); + case MI_WFYSH: return GetWFYSHTalkSfx(ped, sound); + case MI_WFOSH: return GetWFOSHTalkSfx(ped, sound); + case MI_JFOTO: return GetJFOTOTalkSfx(ped, sound); + case MI_JMOTO: return GetJMOTOTalkSfx(ped, sound); + case MI_CBA: + case MI_CBB: return GetCBTalkSfx(ped, sound); + case MI_HNA: + case MI_HNB: return GetHNTalkSfx(ped, sound); + case MI_SGA: + case MI_SGB: return GetSGTalkSfx(ped, sound); + case MI_CLA: + case MI_CLB: return GetCLTalkSfx(ped, sound); + case MI_GDA: + case MI_GDB: return GetGDTalkSfx(ped, sound); + case MI_BKA: + case MI_BKB: return GetBKTalkSfx(ped, sound); + case MI_PGA: + case MI_PGB: return GetPGTalkSfx(ped, sound); + case MI_VICE1: + case MI_VICE2: + case MI_VICE3: + case MI_VICE4: + case MI_VICE5: + case MI_VICE7: + case MI_VICE8: return GetViceWhiteTalkSfx(ped, sound); + case MI_VICE6: return GetViceBlackTalkSfx(ped, sound); + case MI_WFYG1: return GetWFYG1TalkSfx(ped, sound); + case MI_WFYG2: return GetWFYG2TalkSfx(ped, sound); + case MI_SPECIAL01: + case MI_SPECIAL02: + case MI_SPECIAL03: + case MI_SPECIAL04: + case MI_SPECIAL05: + case MI_SPECIAL06: + case MI_SPECIAL07: + case MI_SPECIAL08: + case MI_SPECIAL09: + case MI_SPECIAL10: + case MI_SPECIAL11: + case MI_SPECIAL12: + case MI_SPECIAL13: + case MI_SPECIAL14: + case MI_SPECIAL15: + case MI_SPECIAL16: + case MI_SPECIAL17: + case MI_SPECIAL18: + case MI_SPECIAL19: + case MI_SPECIAL20: + case MI_SPECIAL21: return GetSpecialCharacterTalkSfx(ped, ped->GetModelIndex(), sound); + default: return GetGenericMaleTalkSfx(ped, sound); + } + } + + return NO_SAMPLE; +} + +void +cAudioManager::GetPhrase(uint32 &phrase, uint32 &prevPhrase, uint32 sample, uint32 maxOffset) +{ + phrase = sample + m_anRandomTable[m_sQueueSample.m_nEntityIndex & 3] % maxOffset; + + // check if the same sfx like last time, if yes, then try use next one, + // if exceeded range, then choose first available sample + if (phrase == prevPhrase && ++phrase >= sample + maxOffset) + phrase = sample; + prevPhrase = phrase; +} + +#pragma region PED_COMMENTS + +#define cooldown_phrase(count) static uint8 cooldown = 0;\ +if (cooldown != 0) {\ + if (++cooldown == count) cooldown = 0;\ + return NO_SAMPLE;\ +}\ +cooldown = 1; + +uint32 +cAudioManager::GetPlayerTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + if(m_bIsPlayerShutUp) return NO_SAMPLE; + switch(sound) { + case SOUND_PED_DEATH: return SFX_PLAYER_DEATH; + case SOUND_PED_DAMAGE: + case SOUND_PED_BULLET_HIT: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_HIT_BULLET_1, 33); break; + case SOUND_PED_HIT: + case SOUND_PED_DEFEND: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_HIT_FIST_1, 42); break; + case SOUND_PED_LAND: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_HIT_GROUND_1, 35); break; + case SOUND_PED_BURNING: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_ON_FIRE_1, 16); break; + case SOUND_PED_PLAYER_REACTTOCOP: + switch(m_nPlayerMood) { + case PLAYER_MOOD_ANGRY: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_ANGRY_BUSTED_1, 38); + break; + case PLAYER_MOOD_WISECRACKING: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_WISECRACKING_BUSTED_1, 20); + break; + default: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_CALM_BUSTED_1, 22); + break; + } + break; + case SOUND_PED_ON_FIRE: { + cooldown_phrase(8); + switch(m_nPlayerMood) { + case PLAYER_MOOD_PISSED_OFF: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_PISSED_OFF_SHOOT_1, 29); + break; + case PLAYER_MOOD_ANGRY: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_ANGRY_SHOOT_1, 39); + break; + case PLAYER_MOOD_WISECRACKING: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_WISECRACKING_SHOOT_1, 9); + break; + default: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_CALM_SHOOT_1, 35); + break; + } + break; + } + case SOUND_PED_AIMING: { + cooldown_phrase(8); + switch(m_nPlayerMood) { + case PLAYER_MOOD_PISSED_OFF: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_PISSED_OFF_PULL_GUN_1, 25); + break; + case PLAYER_MOOD_ANGRY: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_ANGRY_PULL_GUN_1, 52); + break; + case PLAYER_MOOD_WISECRACKING: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_WISECRACKING_PULL_GUN_1, 19); + break; + default: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_CALM_PULL_GUN_1, 39); + break; + } + break; + } + case SOUND_PED_CAR_JACKING: { + cooldown_phrase(4); + switch(m_nPlayerMood) { + case PLAYER_MOOD_PISSED_OFF: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_PISSED_OFF_JACKING_1, 36); + break; + case PLAYER_MOOD_ANGRY: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_ANGRY_JACKING_1, 43); + break; + case PLAYER_MOOD_WISECRACKING: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_WISECRACKING_JACKING_1, 18); + break; + default: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_CALM_JACKING_1, 40); + break; + } + break; + } + case SOUND_PED_MUGGING: { + cooldown_phrase(8); + switch(m_nPlayerMood) { + case PLAYER_MOOD_PISSED_OFF: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_1, 25); + break; + case PLAYER_MOOD_ANGRY: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_ANGRY_PICK_UP_CASH_1, 12); + break; + case PLAYER_MOOD_WISECRACKING: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_WISECRACKING_PICK_UP_CASH_1, 23); + break; + default: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_CALM_PICK_UP_CASH_1, 11); + break; + } + break; + } + case SOUND_PED_CAR_JACKED: { + cooldown_phrase(4); + switch(m_nPlayerMood) { + case PLAYER_MOOD_PISSED_OFF: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_PISSED_OFF_JACKED_1, 21); + break; + case PLAYER_MOOD_ANGRY: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_ANGRY_JACKED_1, 33); + break; + case PLAYER_MOOD_WISECRACKING: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_WISECRACKING_JACKED_1, 18); + break; + default: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_CALM_JACKED_1, 24); + break; + } + break; + } + case SOUND_PED_PLAYER_AFTERSEX: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_AFTERSEX_1, 18); break; + case SOUND_PED_PLAYER_BEFORESEX: + switch(m_nPlayerMood) { + case PLAYER_MOOD_ANGRY: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_ANGRY_SEX_1, 18); + break; + case PLAYER_MOOD_WISECRACKING: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_WISECRACKING_SEX_1, 10); + break; + default: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_CALM_SEX_1, 8); + break; + } + break; + case SOUND_PED_PLAYER_FARFROMCOPS: { + cooldown_phrase(4); + switch(m_nPlayerMood) { + case PLAYER_MOOD_ANGRY: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_ANGRY_CHASED_1, 9); + break; + case PLAYER_MOOD_WISECRACKING: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_WISECRACKING_CHASED_1, 7); + break; + default: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_CALM_CHASED_1, 20); + break; + } + break; + } + case SOUND_PED_ATTACK: { + cooldown_phrase(4); + switch(m_nPlayerMood) { + case PLAYER_MOOD_PISSED_OFF: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_PISSED_OFF_FIGHT_1, 61); + break; + case PLAYER_MOOD_ANGRY: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_ANGRY_FIGHT_1, 61); + break; + case PLAYER_MOOD_WISECRACKING: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_WISECRACKING_FIGHT_1, 27); + break; + default: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_CALM_FIGHT_1, 47); + break; + } + break; + } + case SOUND_PED_CRASH_VEHICLE: + case SOUND_PED_CRASH_CAR: + case SOUND_PED_ANNOYED_DRIVER: { + cooldown_phrase(4); + switch(m_nPlayerMood) { + case PLAYER_MOOD_PISSED_OFF: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_PISSED_OFF_CRASH_1, 44); + break; + case PLAYER_MOOD_ANGRY: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_ANGRY_CRASH_1, 41); + break; + case PLAYER_MOOD_WISECRACKING: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_WISECRACKING_CRASH_1, 19); + break; + default: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_CALM_CRASH_1, 43); + break; + } + break; + } + case SOUND_PED_SOLICIT: { + cooldown_phrase(4); + switch(m_nPlayerMood) { + case PLAYER_MOOD_PISSED_OFF: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_PISSED_OFF_PICK_UP_HOOKER_1, 17); + break; + case PLAYER_MOOD_ANGRY: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_ANGRY_PICK_UP_HOOKER_1, 6); + break; + case PLAYER_MOOD_WISECRACKING: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_WISECRACKING_PICK_UP_HOOKER_1, 11); + break; + default: + GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_CALM_PICK_UP_HOOKER_1, 22); + break; + } + break; + } + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetGenericMaleTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + m_bGenericSfx = TRUE; + switch(sound) { + case SOUND_PED_DEATH: GetPhrase(sfx, ped->m_lastComment, SFX_GENERIC_MALE_DEATH_1, 41); break; + case SOUND_PED_BULLET_HIT: + case SOUND_PED_DEFEND: GetPhrase(sfx, ped->m_lastComment, SFX_GENERIC_MALE_GRUNT_1, 41); break; + case SOUND_PED_BURNING: GetPhrase(sfx, ped->m_lastComment, SFX_GENERIC_MALE_FIRE_1, 32); break; + case SOUND_PED_FLEE_SPRINT: GetPhrase(sfx, ped->m_lastComment, SFX_GENERIC_MALE_PANIC_1, 35); break; + default: return NO_SAMPLE; + } + return sfx; +} + +uint32 +cAudioManager::GetGenericFemaleTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + m_bGenericSfx = TRUE; + switch(sound) { + case SOUND_PED_DEATH: GetPhrase(sfx, ped->m_lastComment, SFX_GENERIC_FEMALE_DEATH_1, 22); break; + case SOUND_PED_BULLET_HIT: + case SOUND_PED_DEFEND: GetPhrase(sfx, ped->m_lastComment, SFX_GENERIC_FEMALE_GRUNT_1, 33); break; + case SOUND_PED_BURNING: GetPhrase(sfx, ped->m_lastComment, SFX_GENERIC_FEMALE_FIRE_1, 17); break; + case SOUND_PED_FLEE_SPRINT: GetPhrase(sfx, ped->m_lastComment, SFX_GENERIC_FEMALE_PANIC_1, 27); break; + default: return NO_SAMPLE; + } + return sfx; +} + +uint32 +cAudioManager::GetDefaultTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_DEFAULT_VOICE_GUN_PANIC_1, 12); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_DEFAULT_VOICE_JACKED_1, 12); break; +#ifdef FIX_BUGS + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_DEFAULT_VOICE_JACKING_1, 13); break; +#endif + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_DEFAULT_VOICE_MUGGED_1, 4); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_DEFAULT_VOICE_SAVED_1, 4); break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_DEFAULT_VOICE_TAXI_1, 5); break; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_DEFAULT_VOICE_FIGHT_1, 16); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_DEFAULT_VOICE_DODGE_1, 19); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_DEFAULT_VOICE_RUN_1, 19); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_DEFAULT_VOICE_GENERIC_CRASH_1, 13); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_DEFAULT_VOICE_CAR_CRASH_1, 15); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_DEFAULT_VOICE_BLOCKED_1, 16); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_DEFAULT_VOICE_LOST_1, 5); break; +#ifdef FIX_BUGS + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_DEFAULT_VOICE_EYEING_1, 6); break; +#else + case SOUND_PED_CHAT_SEXY_FEMALE: GetPhrase(sfx, ped->m_lastComment, SFX_DEFAULT_VOICE_EYEING_1, 6); break; +#endif + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_DEFAULT_VOICE_SHOCKED_1, 6); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_DEFAULT_VOICE_BUMP_1, 25); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_DEFAULT_VOICE_CHAT_1, 25); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetCopTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + PedState objective; + switch(sound) { + case SOUND_PED_ARREST_COP: GetPhrase(sfx, ped->m_lastComment, SFX_COP_VOICE_1_ARREST_1, 4); break; + case SOUND_PED_PULLOUTWEAPON: GetPhrase(sfx, ped->m_lastComment, SFX_COP_VOICE_1_PULLOUTWEAPON_1, 3); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_COP_VOICE_1_SAVED_1, 2); break; + case SOUND_PED_COP_TARGETING: GetPhrase(sfx, ped->m_lastComment, SFX_COP_VOICE_1_COP_TARGETING_1, 4); break; + case SOUND_PED_COP_MANYCOPSAROUND: GetPhrase(sfx, ped->m_lastComment, SFX_COP_VOICE_1_COP_MANYCOPSAROUND_1, 2); break; + case SOUND_PED_GUNAIMEDAT2: GetPhrase(sfx, ped->m_lastComment, SFX_COP_VOICE_1_GUNAIMEDAT2_1, 2); break; + case SOUND_PED_COP_ALONE: GetPhrase(sfx, ped->m_lastComment, SFX_COP_VOICE_1_COP_ALONE_1, 4); break; + case SOUND_PED_GUNAIMEDAT3: GetPhrase(sfx, ped->m_lastComment, SFX_COP_VOICE_1_GUNAIMEDAT2_1, 2); break; + case SOUND_PED_COP_ASK_FOR_ID: { + cooldown_phrase(4); + GetPhrase(sfx, ped->m_lastComment, SFX_COP_VOICE_1_COP_ASK_FOR_ID_1, 2); + break; + } + case SOUND_PED_COP_LITTLECOPSAROUND: + objective = FindPlayerPed()->m_nPedState; + if(objective == PED_ARRESTED || objective == PED_DEAD || objective == PED_DIE) return NO_SAMPLE; + GetPhrase(sfx, ped->m_lastComment, SFX_COP_VOICE_1_COP_LITTLECOPSAROUND_1, 4); + break; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_COP_VOICE_1_FIGHT_1, 4); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_COP_VOICE_1_DODGE_1, 3); break; +#ifdef FIX_BUGS + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_COP_VOICE_1_CAR_CRASH_1, 4); break; +#endif + case SOUND_PED_PED_COLLISION: + if(FindPlayerPed()->m_pWanted->GetWantedLevel() > 0) + GetPhrase(sfx, ped->m_lastComment, SFX_COP_VOICE_1_BUMP_1, 5); + else + return NO_SAMPLE; + break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return (SFX_COP_VOICE_2_ARREST_1 - SFX_COP_VOICE_1_ARREST_1) * (m_sQueueSample.m_nEntityIndex % 5) + sfx; +} + +uint32 +cAudioManager::GetSwatTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + switch(sound) { + case SOUND_PED_COP_HELIPILOTPHRASE: GetPhrase(sfx, ped->m_lastComment, SFX_SWAT_VOICE_1_COP_HELIPILOTPHRASE_1, 7); break; + case SOUND_PED_COP_TARGETING: GetPhrase(sfx, ped->m_lastComment, SFX_SWAT_VOICE_1_COP_TARGETING_1, 4); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_SWAT_VOICE_1_DODGE_1, 3); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + sfx += (SFX_SWAT_VOICE_2_DODGE_1 - SFX_SWAT_VOICE_1_DODGE_1) * (m_sQueueSample.m_nEntityIndex % 3); + return sfx; +} + +uint32 +cAudioManager::GetFBITalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + switch(sound) { +#ifdef FIX_BUGS + case SOUND_PED_COP_TARGETING: GetPhrase(sfx, ped->m_lastComment, SFX_FBI_VOICE_1_COP_TARGETING_1, 6); break; +#else + case SOUND_PED_COP_TARGETING: GetPhrase(sfx, ped->m_lastComment, SFX_FBI_VOICE_1_COP_TARGETING_1, 4); break; +#endif + case SOUND_PED_COP_MANYCOPSAROUND: GetPhrase(sfx, ped->m_lastComment, SFX_FBI_VOICE_1_COP_MANYCOPSAROUND_1, 3); break; + case SOUND_PED_GUNAIMEDAT2: sfx = SFX_FBI_VOICE_1_GUNAIMEDAT2_1; break; + case SOUND_PED_GUNAIMEDAT3: GetPhrase(sfx, ped->m_lastComment, SFX_FBI_VOICE_1_GUNAIMEDAT3_1, 4); break; + case SOUND_PED_CRASH_VEHICLE: + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_FBI_VOICE_1_CAR_CRASH_1, 4); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } +#ifdef FIX_BUGS + sfx += (SFX_FBI_VOICE_2_GUNAIMEDAT3_1 - SFX_FBI_VOICE_1_GUNAIMEDAT3_1) * (m_sQueueSample.m_nEntityIndex % 3); +#else + sfx += 16 * (m_sQueueSample.m_nEntityIndex % 3); +#endif + return sfx; +} + +uint32 +cAudioManager::GetArmyTalkSfx(CPed *ped, uint16 sound) +{ + return GetGenericMaleTalkSfx(ped, sound); +} + +uint32 +cAudioManager::GetMedicTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + switch(sound) { + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_MEDIC_VOICE_1_FIGHT_1, 6); break; + case SOUND_PED_HEALING: GetPhrase(sfx, ped->m_lastComment, SFX_MEDIC_VOICE_1_AT_VICTIM_1, 17); break; + case SOUND_PED_LEAVE_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_MEDIC_VOICE_1_GET_OUT_VAN_CHAT_1, 10); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + sfx += (SFX_MEDIC_VOICE_2_FIGHT_1 - SFX_MEDIC_VOICE_1_FIGHT_1) * (m_sQueueSample.m_nEntityIndex % 2); + return sfx; +} + +uint32 +cAudioManager::GetFiremanTalkSfx(CPed *ped, uint16 sound) +{ + return GetGenericMaleTalkSfx(ped, sound); +} + +uint32 +cAudioManager::GetWFYG1TalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG1_GUN_COOL_1, 6); break; + case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG1_MUGGING_1, 2); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG1_JACKED_1, 5); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG1_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_WFYG1_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_WFYG1_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG1_FIGHT_1, 4); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG1_DODGE_1, 9); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG1_RUN_1, 2); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG1_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG1_CAR_CRASH_1, 9); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG1_BLOCKED_1, 7); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG1_LOST_1, 3); break; + case SOUND_PED_CHAT_SEXY_FEMALE: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG1_EYEING_1, 2); break; + case SOUND_PED_CHAT_EVENT: return SFX_WFYG1_SHOCKED_1; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG1_BUMP_1, 11); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG1_CHAT_1, 10); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWFYG2TalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG2_GUN_COOL_1, 3); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG2_JACKED_1, 5); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG2_MUGGED_1, 2); break; +#ifdef FIX_BUGS + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG2_TAXI_1, 2); break; +#else + case SOUND_PED_TAXI_WAIT: return SFX_WFYG2_TAXI_1; +#endif + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG2_FIGHT_1, 5); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG2_DODGE_1, 8); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG2_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG2_CAR_CRASH_1, 9); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG2_BLOCKED_1, 5); break; + case SOUND_PED_WAIT_DOUBLEBACK: return SFX_WFYG2_LOST_1; + case SOUND_PED_CHAT_SEXY_FEMALE: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG2_EYEING_1, 4); break; + case SOUND_PED_CHAT_EVENT: return SFX_WFYG2_SHOCKED_1; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG2_BUMP_1, 11); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WFYG2_CHAT_1, 9); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + + return sfx; +} + +uint32 +cAudioManager::GetHFYSTTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_HFYST_GUN_COOL_1, 5); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_HFYST_JACKING_1, 4); break; + case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, SFX_HFYST_MUGGING_1, 4); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_HFYST_JACKED_1, 6); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_HFYST_MUGGED_1, 2); break; + case SOUND_PED_TAXI_WAIT: return SFX_HFYST_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_HFYST_FIGHT_1, 7); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HFYST_DODGE_1, 10); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_HFYST_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_HFYST_CAR_CRASH_1, 8); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_HFYST_BLOCKED_1, 7); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_HFYST_LOST_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HFYST_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_HFYST_CHAT_1, 9); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetHFOSTTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_HFOST_GUN_COOL_1, 6); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_HFOST_JACKED_1, 8); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_HFOST_MUGGED_1, 3); break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_HFOST_TAXI_1, 2); break; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_HFOST_FIGHT_1, 8); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HFOST_DODGE_1, 10); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_HFOST_GENERIC_CRASH_1, 11); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_HFOST_CAR_CRASH_1, 8); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_HFOST_BLOCKED_1, 9); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_HFOST_LOST_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HFOST_BUMP_1, 12); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_HFOST_CHAT_1, 11); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetHMYSTTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_HMYST_GUN_PANIC_1, 6); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_HMYST_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_HMYST_TAXI_1; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HMYST_DODGE_1, 6); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_HMYST_RUN_1, 4); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_HMYST_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_HMYST_EYEING_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_HMYST_SHOCKED_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HMYST_BUMP_1, 13); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_HMYST_CHAT_1, 11); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetHMOSTTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_HMOST_GUN_COOL_1, 5); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_HMOST_JACKING_1, 3); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_HMOST_JACKED_1, 6); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_HMOST_MUGGED_1, 2); break; + case SOUND_PED_TAXI_WAIT: return SFX_HMOST_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_HMOST_FIGHT_1, 8); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HMOST_DODGE_1, 9); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_HMOST_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_HMOST_CAR_CRASH_1, 7); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_HMOST_BLOCKED_1, 7); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_HMOST_LOST_1, 2); break; + case SOUND_PED_CHAT_SEXY_MALE: return SFX_HMOST_EYEING_1; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HMOST_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_HMOST_CHAT_1, 11); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetHFYRITalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_HFYRI_GUN_PANIC_1, 5); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_HFYRI_JACKED_1, 6); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_HFYRI_MUGGED_1, 4); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_HFYRI_SAVED_1, 2); break; + case SOUND_PED_TAXI_WAIT: return SFX_HFYRI_TAXI_1; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HFYRI_DODGE_1, 10); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_HFYRI_RUN_1, 4); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_HFYRI_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_HFYRI_CAR_CRASH_1, 8); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_HFYRI_BLOCKED_1, 8); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_HFYRI_LOST_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_HFYRI_SHOCKED_1, 3); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HFYRI_BUMP_1, 9); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetHFORITalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_HFORI_GUN_PANIC_1, 6); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_HFORI_JACKED_1, 9); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_HFORI_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_HFORI_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_HFORI_TAXI_1; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HFORI_DODGE_1, 6); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_HFORI_RUN_1, 4); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_HFORI_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_HFORI_CAR_CRASH_1, 7); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_HFORI_BLOCKED_1, 6); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_HFORI_LOST_1, 2); break; + case SOUND_PED_CHAT_SEXY_FEMALE: GetPhrase(sfx, ped->m_lastComment, SFX_HFORI_EYEING_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_HFORI_SHOCKED_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HFORI_BUMP_1, 10); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetHMYRITalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_HMYRI_GUN_PANIC_1, 7); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_HMYRI_JACKING_1, 3); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_HMYRI_JACKED_1, 8); break; + case SOUND_PED_ROBBED: return SFX_HMYRI_MUGGED_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_HMYRI_FIGHT_1, 5); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HMYRI_DODGE_1, 9); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_HMYRI_GENERIC_CRASH_1, 12); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_HMYRI_CAR_CRASH_1, 8); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_HMYRI_BLOCKED_1, 7); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_HMYRI_SHOCKED_1, 3); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HMYRI_BUMP_1, 10); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetHMORITalkSfx(CPed *ped, uint16 sound) +{ + + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_HMORI_GUN_PANIC_1, 5); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_HMORI_JACKED_1, 8); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_HMORI_MUGGED_1, 3); break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_HMORI_TAXI_1, 2); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HMORI_DODGE_1, 7); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_HMORI_RUN_1, 6); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_HMORI_GENERIC_CRASH_1, 11); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_HMORI_CAR_CRASH_1, 6); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_HMORI_BLOCKED_1, 8); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_HMORI_LOST_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HMORI_BUMP_1, 11); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_HMORI_CHAT_1, 8); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetHFYBETalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBE_GUN_PANIC_1, 7); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBE_JACKED_1, 7); break; + case SOUND_PED_TAXI_WAIT: return SFX_HFYBE_TAXI_1; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBE_DODGE_1, 11); break; +#ifdef FIX_BUGS // assumption + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBE_RUN_1, 7); break; +#endif + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBE_GENERIC_CRASH_1, 8); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBE_CAR_CRASH_1, 6); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBE_LOST_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBE_SHOCKED_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBE_BUMP_1, 8); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBE_CHAT_1, 10); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetHFOBETalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_HFOBE_GUN_PANIC_1, 5); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_HFOBE_JACKED_1, 6); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_HFOBE_SAVED_1; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_HFOBE_TAXI_1, 2); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HFOBE_DODGE_1, 7); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_HFOBE_RUN_1, 4); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_HFOBE_GENERIC_CRASH_1, 5); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_HFOBE_CAR_CRASH_1, 6); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_HFOBE_BLOCKED_1, 6); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_HFOBE_LOST_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_HFOBE_SHOCKED_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HFOBE_BUMP_1, 11); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_HFOBE_CHAT_1, 10); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetHMYBETalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_HMYBE_GUN_PANIC_1, 6); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_HMYBE_JACKED_1, 12); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_HMYBE_SAVED_1; + case SOUND_PED_INNOCENT: GetPhrase(sfx, ped->m_lastComment, SFX_HMYBE_INNOCENT_1, 4); break; + case SOUND_PED_TAXI_WAIT: return SFX_HMYBE_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_HMYBE_FIGHT_1, 8); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HMYBE_DODGE_1, 7); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_HMYBE_GENERIC_CRASH_1, 10); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_HMYBE_CAR_CRASH_1, 7); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_HMYBE_LOST_1, 3); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_HMYBE_EYEING_1, 5); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_HMYBE_SHOCKED_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HMYBE_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_HMYBE_CHAT_1, 10); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetHMOBETalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_HMOBE_GUN_PANIC_1, 3); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_HMOBE_JACKED_1, 6); break; + case SOUND_PED_INNOCENT: GetPhrase(sfx, ped->m_lastComment, SFX_HMOBE_INNOCENT_1, 3); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HMOBE_DODGE_1, 9); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_HMOBE_BLOCKED_1, 10); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_HMOBE_EYEING_1, 4); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HMOBE_BUMP_1, 8); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + + return sfx; +} + +uint32 +cAudioManager::GetHFYBUTalkSfx(CPed *ped, uint16 sound) +{ + + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBU_GUN_PANIC_1, 5); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBU_JACKING_1, 3); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBU_JACKED_1, 6); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBU_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_HFYBU_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_HFYBU_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBU_FIGHT_1, 7); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBU_DODGE_1, 10); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBU_GENERIC_CRASH_1, 12); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBU_CAR_CRASH_1, 8); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBU_BLOCKED_1, 8); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBU_LOST_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HFYBU_BUMP_1, 11); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetHFYMDTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_HFYMD_GUN_PANIC_1, 5); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_HFYMD_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_HFYMD_SAVED_1, 3); break; +#ifdef FIX_BUGS + case SOUND_PED_TAXI_WAIT: return SFX_HFYMD_TAXI_1; +#else + case SOUND_PED_TAXI_WAIT: return SFX_BFOBE_TAXI_1; +#endif + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_HFYMD_FIGHT_1, 9); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HFYMD_DODGE_1, 8); break; + case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, SFX_HFYMD_SOLICIT_1, 15); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HFYMD_BUMP_1, 9); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetHFYCGTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_HFYCG_GUN_PANIC_1, 5); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_HFYCG_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_HFYCG_SAVED_1; +#ifdef FIX_BUGS + case SOUND_PED_TAXI_WAIT: return SFX_HFYCG_TAXI_1; +#else + case SOUND_PED_TAXI_WAIT: return SFX_BFOBE_TAXI_1; +#endif + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HFYCG_DODGE_1, 8); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_HFYCG_RUN_1, 4); break; + case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, SFX_HFYCG_SOLICIT_1, 14); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HFYCG_BUMP_1, 9); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetHFYPRTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_HFYPR_GUN_COOL_1, 6); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_HFYPR_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_HFYPR_SAVED_1; + case SOUND_PED_PLAYER_BEFORESEX: GetPhrase(sfx, ped->m_lastComment, SFX_HFYPR_FUCKING_1, 8); break; + case SOUND_PED_TAXI_WAIT: return SFX_HFYPR_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_HFYPR_FIGHT_1, 10); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HFYPR_DODGE_1, 9); break; + case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, SFX_HFYPR_SOLICIT_1, 14); break; + case SOUND_PED_CHAT_SEXY_FEMALE: GetPhrase(sfx, ped->m_lastComment, SFX_HFYPR_EYEING_1, 3); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HFYPR_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_HFYPR_CHAT_1, 12); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetHFOTRTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_HFOTR_GUN_COOL_1, 5); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_HFOTR_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_HFOTR_SAVED_1; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_HFOTR_TAXI_1, 2); break; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_HFOTR_FIGHT_1, 6); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HFOTR_DODGE_1, 8); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_HFOTR_SHOCKED_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HFOTR_BUMP_1, 11); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_HFOTR_CHAT_1, 12); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetHMOTRTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_HMOTR_GUN_COOL_1, 6); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_HMOTR_SAVED_1, 2); break; + case SOUND_PED_TAXI_WAIT: return SFX_HMOTR_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_HMOTR_FIGHT_1, 7); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HMOTR_DODGE_1, 11); break; + case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, SFX_HMOTR_SOLICIT_1, 8); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_HMOTR_SHOCKED_1, 3); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HMOTR_BUMP_1, 8); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_HMOTR_CHAT_1, 9); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetHMOCATalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_HMOCA_GUN_PANIC_1, 5); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_HMOCA_JACKING_1, 11); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_HMOCA_JACKED_1, 10); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_HMOCA_MUGGED_1, 7); break; + case SOUND_PED_TAXI_WAIT: return SFX_HMOCA_TAXI_1; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_HMOCA_RUN_1, 2); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_HMOCA_CAR_CRASH_1, 8); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_HMOCA_BLOCKED_1, 8); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_HMOCA_EYEING_1, 2); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_HMOCA_CHAT_1, 10); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetBMYCRTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_BMYCR_GUN_COOL_1, 6); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_BMYCR_JACKING_1, 12); break; + case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, SFX_BMYCR_MUGGING_1, 6); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_BMYCR_JACKED_1, 6); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_BMYCR_MUGGED_1, 3); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_BMYCR_SAVED_1, 2); break; + case SOUND_PED_INNOCENT: GetPhrase(sfx, ped->m_lastComment, SFX_BMYCR_INNOCENT_1, 4); break; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_BMYCR_FIGHT_1, 8); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BMYCR_DODGE_1, 8); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_BMYCR_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_BMYCR_CAR_CRASH_1, 9); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_BMYCR_BLOCKED_1, 12); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_BMYCR_EYEING_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_BMYCR_BUMP_1, 11); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetBFYSTTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_BFYST_GUN_PANIC_1, 4); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_BFYST_JACKED_1, 5); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_BFYST_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_BFYST_SAVED_1, 2); break; + case SOUND_PED_TAXI_WAIT: return SFX_BFYST_TAXI_1; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BFYST_DODGE_1, 9); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_BFYST_RUN_1, 6); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_BFYST_GENERIC_CRASH_1, 8); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_BFYST_CAR_CRASH_1, 9); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_BFYST_BLOCKED_1, 8); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_BFYST_LOST_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_BFYST_BUMP_1, 9); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_BFYST_CHAT_1, 9); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetBFOSTTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_BFOST_GUN_PANIC_1, 5); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_BFOST_JACKED_1, 8); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_BFOST_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_BFOST_SAVED_1, 2); break; + case SOUND_PED_TAXI_WAIT: return SFX_BFOST_TAXI_1; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BFOST_DODGE_1, 11); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_BFOST_RUN_1, 4); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_BFOST_GENERIC_CRASH_1, 8); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_BFOST_CAR_CRASH_1, 8); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_BFOST_BLOCKED_1, 7); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_BFOST_LOST_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_BFOST_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_BFOST_CHAT_1, 10); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetBMYSTTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_BMYST_GUN_COOL_1, 6); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_BMYST_JACKING_1, 4); break; + case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, SFX_BMYST_MUGGING_1, 4); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_BMYST_JACKED_1, 8); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_BMYST_MUGGED_1, 2); break; +#ifdef FIX_BUGS + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_BMYST_TAXI_1, 2); break; +#else + case SOUND_PED_TAXI_WAIT: return SFX_BMYST_TAXI_1; +#endif + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_BMYST_FIGHT_1, 6); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BMYST_DODGE_1, 8); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_BMYST_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_BMYST_CAR_CRASH_1, 9); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_BMYST_BLOCKED_1, 8); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_BMYST_BUMP_1, 11); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_BMYST_CHAT_1, 12); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetBMOSTTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_BMOST_GUN_PANIC_1, 9); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_BMOST_MUGGED_1, 4); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_BMOST_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_BMOST_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_BMOST_FIGHT_1, 7); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BMOST_DODGE_1, 8); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_BMOST_GENERIC_CRASH_1, 13); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_BMOST_CAR_CRASH_1, 8); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_BMOST_LOST_1, 6); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_BMOST_EYEING_1, 6); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_BMOST_BUMP_1, 17); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_BMOST_CHAT_1, 18); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetBFYRITalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_BFYRI_GUN_PANIC_1, 4); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_BFYRI_JACKING_1, 4); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_BFYRI_JACKED_1, 8); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_BFYRI_MUGGED_1, 3); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_BFYRI_SAVED_1, 2); break; + case SOUND_PED_TAXI_WAIT: return SFX_BFYRI_TAXI_1; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BFYRI_DODGE_1, 8); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_BFYRI_RUN_1, 6); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_BFYRI_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_BFYRI_CAR_CRASH_1, 8); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_BFYRI_BLOCKED_1, 9); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_BFYRI_LOST_1, 2); break; + case SOUND_PED_CHAT_SEXY_FEMALE: GetPhrase(sfx, ped->m_lastComment, SFX_BFYRI_EYEING_1, 3); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_BFYRI_SHOCKED_1, 4); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_BFYRI_BUMP_1, 9); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetBFORITalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_BFORI_GUN_PANIC_1, 5); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_BFORI_JACKED_1, 4); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_BFORI_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_BFORI_SAVED_1; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_BFORI_TAXI_1, 2); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BFORI_DODGE_1, 9); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_BFORI_RUN_1, 4); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_BFORI_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_BFORI_CAR_CRASH_1, 7); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_BFORI_BLOCKED_1, 8); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_BFORI_LOST_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_BFORI_SHOCKED_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_BFORI_BUMP_1, 9); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetBMYRITalkSfx(CPed *ped, uint16 sound) +{ + + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_BMYRI_GUN_PANIC_1, 7); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_BMYRI_JACKED_1, 4); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_BMYRI_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_BMYRI_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_BMYRI_TAXI_1; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BMYRI_DODGE_1, 8); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_BMYRI_RUN_1, 4); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_BMYRI_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_BMYRI_CAR_CRASH_1, 7); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_BMYRI_BLOCKED_1, 6); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_BMYRI_LOST_1, 2); break; + case SOUND_PED_CHAT_SEXY_MALE: return SFX_BMYRI_EYEING_1; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_BMYRI_SHOCKED_1, 3); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_BMYRI_BUMP_1, 7); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetBFYBETalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_BFYBE_GUN_COOL_1, 6); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_BFYBE_JACKED_1, 8); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_BFYBE_MUGGED_1, 5); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_BFYBE_SAVED_1, 2); break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_BFYBE_TAXI_1, 3); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BFYBE_DODGE_1, 10); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_BFYBE_RUN_1, 6); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_BFYBE_GENERIC_CRASH_1, 8); break; +#ifdef FIX_BUGS + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_BFYBE_CAR_CRASH_1, 10); break; +#else + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_BFYBE_CAR_CRASH_1, 8); break; +#endif + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_BFYBE_BLOCKED_1, 12); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_BFYBE_LOST_1, 4); break; +#ifdef FIX_BUGS + case SOUND_PED_CHAT_SEXY_FEMALE: GetPhrase(sfx, ped->m_lastComment, SFX_BFYBE_EYEING_1, 4); break; +#else + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_BFYBE_EYEING_1, 4); break; +#endif + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_BFYBE_SHOCKED_1, 4); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_BFYBE_CHAT_1, 16); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetBMYBETalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBE_GUN_COOL_1, 4); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBE_JACKING_1, 3); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBE_JACKED_1, 6); break; + case SOUND_PED_ROBBED: return SFX_BMYBE_MUGGED_1; + case SOUND_PED_ACCIDENTREACTION1: return SFX_BMYBE_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_BMYBE_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBE_FIGHT_1, 8); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBE_DODGE_1, 10); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBE_GENERIC_CRASH_1, 8); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBE_CAR_CRASH_1, 8); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBE_BLOCKED_1, 8); break; + case SOUND_PED_WAIT_DOUBLEBACK: return SFX_BMYBE_LOST_1; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBE_EYEING_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBE_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBE_CHAT_1, 10); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetBFOBETalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_BFOBE_GUN_PANIC_1, 5); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_BFOBE_JACKING_1, 4); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_BFOBE_JACKED_1, 5); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_BFOBE_MUGGED_1, 2); break; + case SOUND_PED_TAXI_WAIT: return SFX_BFOBE_TAXI_1; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BFOBE_DODGE_1, 9); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_BFOBE_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_BFOBE_CAR_CRASH_1, 7); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_BFOBE_BLOCKED_1, 8); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_BFOBE_SHOCKED_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_BFOBE_BUMP_1, 8); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_BFOBE_CHAT_1, 8); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetBMOBETalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_BMOBE_GUN_PANIC_1, 5); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_BMOBE_JACKED_1, 6); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_BMOBE_MUGGED_1, 4); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_BMOBE_SAVED_1, 3); break; + case SOUND_PED_TAXI_WAIT: return SFX_BMOBE_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_BMOBE_FIGHT_1, 10); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BMOBE_DODGE_1, 11); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_BMOBE_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_BMOBE_CAR_CRASH_1, 9); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_BMOBE_SHOCKED_1, 3); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_BMOBE_BUMP_1, 5); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_BMOBE_CHAT_1, 10); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetBMYBUTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBU_GUN_PANIC_1, 5); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBU_JACKED_1, 6); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBU_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_BMYBU_SAVED_1; + case SOUND_PED_INNOCENT: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBU_INNOCENT_1, 2); break; + case SOUND_PED_TAXI_WAIT: return SFX_BMYBU_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBU_FIGHT_1, 5); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBU_DODGE_1, 10); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBU_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBU_CAR_CRASH_1, 7); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBU_BLOCKED_1, 8); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBU_EYEING_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBU_SHOCKED_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBU_BUMP_1, 7); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetBFYPRTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_BFYPR_GUN_COOL_1, 5); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_BFYPR_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_BFYPR_SAVED_1; + case SOUND_PED_PLAYER_BEFORESEX: GetPhrase(sfx, ped->m_lastComment, SFX_BFYPR_FUCKING_1, 7); break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_BFYPR_TAXI_1, 2); break; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_BFYPR_FIGHT_1, 7); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BFYPR_DODGE_1, 7); break; + case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, SFX_BFYPR_SOLICIT_1, 13); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_BFYPR_SHOCKED_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_BFYPR_BUMP_1, 11); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_BFYPR_CHAT_1, 13); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetBFOTRTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_BFOTR_GUN_COOL_1, 6); break; + case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, SFX_BFOTR_MUGGING_1, 3); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_BFOTR_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_BFOTR_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_BFOTR_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_BFOTR_FIGHT_1, 6); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BFOTR_DODGE_1, 9); break; + case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, SFX_BFOTR_SOLICIT_1, 5); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_BFOTR_SHOCKED_1, 3); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_BFOTR_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_BFOTR_CHAT_1, 15); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetBMOTRTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_BMOTR_GUN_COOL_1, 5); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_BMOTR_SAVED_1, 1); break; + case SOUND_PED_INNOCENT: GetPhrase(sfx, ped->m_lastComment, SFX_BMOTR_INNOCENT_1, 4); break; + case SOUND_PED_TAXI_WAIT: return SFX_BMOTR_TAXI_1; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BMOTR_DODGE_1, 11); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_BMOTR_RUN_1, 7); break; + case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, SFX_BMOTR_SOLICIT_1, 7); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_BMOTR_EYEING_1, 3); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_BMOTR_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_BMOTR_CHAT_1, 10); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetBMYPITalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_BMYPI_GUN_COOL_1, 5); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_BMYPI_JACKING_1, 4); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_BMYPI_JACKED_1, 6); break; + case SOUND_PED_ROBBED: return SFX_BMYPI_MUGGED_1; + case SOUND_PED_ACCIDENTREACTION1: return SFX_BMYPI_SAVED_1; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_BMYPI_TAXI_1, 2); break; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_BMYPI_FIGHT_1, 8); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BMYPI_DODGE_1, 10); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_BMYPI_GENERIC_CRASH_1, 13); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_BMYPI_CAR_CRASH_1, 5); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_BMYPI_BLOCKED_1, 6); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_BMYPI_EYEING_1, 4); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_BMYPI_BUMP_1, 9); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetBMYBBTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBB_GUN_COOL_1, 5); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBB_JACKING_1, 9); break; + case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBB_MUGGING_1, 8); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBB_JACKED_1, 11); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBB_MUGGED_1, 5); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBB_SAVED_1, 6); break; + case SOUND_PED_INNOCENT: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBB_INNOCENT_1, 4); break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBB_TAXI_1, 3); break; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBB_FIGHT_1, 12); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBB_DODGE_1, 18); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBB_GENERIC_CRASH_1, 9); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBB_CAR_CRASH_1, 9); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBB_BLOCKED_1, 13); break; + case SOUND_PED_JEER: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBB_JEER_1, 16); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBB_LOST_1, 2); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBB_EYEING_1, 16); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBB_SHOCKED_1, 6); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBB_BUMP_1, 17); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_BMYBB_CHAT_1, 21); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWMYCRTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCR_GUN_COOL_1, 5); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCR_JACKING_1, 6); break; + case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCR_MUGGING_1, 5); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCR_MUGGED_1, 3); break; + case SOUND_PED_TAXI_WAIT: return SFX_WMYCR_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCR_FIGHT_1, 7); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCR_DODGE_1, 10); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCR_GENERIC_CRASH_1, 9); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCR_CAR_CRASH_1, 9); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCR_BUMP_1, 18); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWFYSTTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_WFYST_GUN_COOL_1, 5); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_WFYST_JACKING_1, 4); break; + case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, SFX_WFYST_MUGGING_1, 4); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WFYST_JACKED_1, 6); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WFYST_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_WFYST_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_WFYST_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_WFYST_FIGHT_1, 7); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WFYST_DODGE_1, 10); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WFYST_GENERIC_CRASH_1, 8); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WFYST_CAR_CRASH_1, 8); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WFYST_BLOCKED_1, 6); break; + case SOUND_PED_WAIT_DOUBLEBACK: return SFX_WFYST_LOST_1; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WFYST_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WFYST_CHAT_1, 10); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWFYSKTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WFYSK_GUN_PANIC_1, 5); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WFYSK_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_WFYSK_SAVED_1, 2); break; + case SOUND_PED_TAXI_WAIT: return SFX_WFYSK_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_WFYSK_FIGHT_1, 11); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WFYSK_DODGE_1, 9); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WFYSK_BLOCKED_1, 11); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WFYSK_BUMP_1, 18); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWMYSKTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WMYSK_GUN_PANIC_1, 5); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WMYSK_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_WMYSK_SAVED_1, 2); break; + case SOUND_PED_INNOCENT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYSK_INNOCENT_1, 3); break; + case SOUND_PED_TAXI_WAIT: return SFX_WMYSK_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_WMYSK_FIGHT_1, 5); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYSK_DODGE_1, 10); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_WMYSK_LOST_1, 2); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYSK_EYEING_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYSK_SHOCKED_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WMYSK_BUMP_1, 14); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYSK_CHAT_1, 13); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWFOSTTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WFOST_GUN_PANIC_1, 4); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WFOST_JACKED_1, 8); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WFOST_MUGGED_1, 5); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_WFOST_SAVED_1, 4); break; + case SOUND_PED_TAXI_WAIT: return SFX_WFOST_TAXI_1; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WFOST_DODGE_1, 12); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WFOST_RUN_1, 7); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WFOST_GENERIC_CRASH_1, 10); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WFOST_CAR_CRASH_1, 11); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WFOST_BLOCKED_1, 12); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_WFOST_LOST_1, 3); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WFOST_BUMP_1, 19); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WFOST_CHAT_1, 16); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWMYSTTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WMYST_GUN_PANIC_1, 5); break; + case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, SFX_WMYST_MUGGING_1, 5); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WMYST_JACKED_1, 5); break; + case SOUND_PED_ROBBED: return SFX_WMYST_MUGGED_1; + case SOUND_PED_ACCIDENTREACTION1: return SFX_WMYST_SAVED_1; + case SOUND_PED_INNOCENT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYST_INNOCENT_1, 3); break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYST_TAXI_1, 2); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYST_DODGE_1, 10); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WMYST_RUN_1, 7); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYST_GENERIC_CRASH_1, 5); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WMYST_CAR_CRASH_1, 8); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WMYST_BLOCKED_1, 8); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_WMYST_LOST_1, 2); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYST_EYEING_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WMYST_BUMP_1, 11); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYST_CHAT_1, 10); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWMOSTTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_WMOST_GUN_COOL_1, 5); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WMOST_JACKED_1, 4); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WMOST_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_WMOST_SAVED_1; + case SOUND_PED_INNOCENT: GetPhrase(sfx, ped->m_lastComment, SFX_WMOST_INNOCENT_1, 3); break; + case SOUND_PED_TAXI_WAIT: return SFX_WMOST_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_WMOST_FIGHT_1, 8); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WMOST_DODGE_1, 8); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WMOST_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WMOST_CAR_CRASH_1, 7); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WMOST_BLOCKED_1, 8); break; + case SOUND_PED_JEER: GetPhrase(sfx, ped->m_lastComment, SFX_WMOST_JEER_1, 4); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_WMOST_LOST_1, 2); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_WMOST_EYEING_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WMOST_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WMOST_CHAT_1, 9); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWFYRITalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WFYRI_GUN_PANIC_1, 5); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WFYRI_JACKED_1, 7); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WFYRI_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_WFYRI_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_WFYRI_TAXI_1; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WFYRI_DODGE_1, 9); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WFYRI_RUN_1, 5); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WFYRI_GENERIC_CRASH_1, 8); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WFYRI_CAR_CRASH_1, 9); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WFYRI_BLOCKED_1, 8); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_WFYRI_LOST_1, 2); break; + case SOUND_PED_CHAT_SEXY_FEMALE: GetPhrase(sfx, ped->m_lastComment, SFX_WFYRI_EYEING_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_WFYRI_SHOCKED_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WFYRI_BUMP_1, 10); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWFORITalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WFORI_GUN_PANIC_1, 6); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WFORI_JACKED_1, 6); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WFORI_MUGGED_1, 3); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_WFORI_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_WFORI_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_WFORI_FIGHT_1, 7); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WFORI_DODGE_1, 11); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WFORI_GENERIC_CRASH_1, 8); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WFORI_CAR_CRASH_1, 10); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WFORI_BLOCKED_1, 7); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_WFORI_LOST_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_WFORI_SHOCKED_1, 3); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WFORI_BUMP_1, 11); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWMYRITalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WMYRI_GUN_PANIC_1, 8); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WMYRI_JACKED_1, 8); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_WMYRI_SAVED_1; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYRI_TAXI_1, 2); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYRI_DODGE_1, 9); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WMYRI_RUN_1, 5); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYRI_GENERIC_CRASH_1, 11); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WMYRI_CAR_CRASH_1, 9); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WMYRI_BLOCKED_1, 10); break; + case SOUND_PED_WAIT_DOUBLEBACK: return SFX_WMYRI_LOST_1; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYRI_EYEING_1, 3); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYRI_SHOCKED_1, 4); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WMYRI_BUMP_1, 8); break; +#ifdef FIX_BUGS + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYRI_CHAT_1, 10); break; +#endif + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWMORITalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WMORI_GUN_PANIC_1, 9); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WMORI_JACKED_1, 6); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WMORI_MUGGED_1, 4); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_WMORI_SAVED_1, 2); break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_WMORI_TAXI_1, 2); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WMORI_DODGE_1, 10); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WMORI_RUN_1, 12); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WMORI_GENERIC_CRASH_1, 8); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WMORI_CAR_CRASH_1, 6); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WMORI_BLOCKED_1, 10); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_WMORI_LOST_1, 2); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_WMORI_EYEING_1, 3); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_WMORI_SHOCKED_1, 4); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WMORI_BUMP_1, 14); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWFYBETalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WFYBE_GUN_PANIC_1, 5); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WFYBE_JACKED_1, 4); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_WFYBE_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_WFYBE_TAXI_1; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WFYBE_DODGE_1, 8); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WFYBE_RUN_1, 5); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WFYBE_GENERIC_CRASH_1, 6); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WFYBE_CAR_CRASH_1, 6); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WFYBE_BLOCKED_1, 7); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_WFYBE_SHOCKED_1, 3); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WFYBE_BUMP_1, 11); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WFYBE_CHAT_1, 10); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWMYBETalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBE_GUN_PANIC_1, 8); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBE_JACKING_1, 3); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBE_JACKED_1, 7); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBE_DODGE_1, 12); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBE_RUN_1, 5); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBE_GENERIC_CRASH_1, 8); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBE_CAR_CRASH_1, 8); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBE_BLOCKED_1, 9); break; + case SOUND_PED_JEER: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBE_JEER_1, 7); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBE_LOST_1, 3); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBE_EYEING_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBE_SHOCKED_1, 6); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBE_BUMP_1, 14); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBE_CHAT_1, 11); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWFOBETalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WFOBE_GUN_PANIC_1, 5); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WFOBE_JACKED_1, 4); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_WFOBE_SAVED_1, 3); break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_WFOBE_TAXI_1, 2); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WFOBE_DODGE_1, 8); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WFOBE_RUN_1, 7); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WFOBE_GENERIC_CRASH_1, 10); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WFOBE_CAR_CRASH_1, 7); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WFOBE_BLOCKED_1, 8); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_WFOBE_SHOCKED_1, 3); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WFOBE_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WFOBE_CHAT_1, 10); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWMOBETalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBE_GUN_PANIC_1, 5); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBE_JACKING_1, 4); break; + case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBE_MUGGING_1, 6); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBE_JACKED_1, 8); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBE_SAVED_1, 2); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBE_DODGE_1, 8); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBE_RUN_1, 4); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBE_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBE_CAR_CRASH_1, 8); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBE_BLOCKED_1, 6); break; + case SOUND_PED_JEER: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBE_JEER_1, 16); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBE_EYEING_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBE_SHOCKED_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBE_BUMP_1, 12); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBE_CHAT_1, 10); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWMYCWTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCW_GUN_PANIC_1, 6); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCW_JACKING_1, 4); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCW_JACKED_1, 6); break; + case SOUND_PED_ROBBED: return SFX_WMYCW_MUGGED_1; +#ifdef FIX_BUGS + case SOUND_PED_INNOCENT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCW_INNOCENT_1, 3); break; +#endif + case SOUND_PED_TAXI_WAIT: return SFX_WMYCW_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCW_FIGHT_1, 8); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCW_DODGE_1, 10); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCW_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCW_CAR_CRASH_1, 9); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCW_BLOCKED_1, 9); break; + case SOUND_PED_JEER: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCW_JEER_1, 5); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCW_LOST_1, 2); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCW_EYEING_1, 3); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCW_BUMP_1, 9); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYCW_CHAT_1, 15); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWMYGOTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WMYGO_GUN_PANIC_1, 5); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WMYGO_JACKED_1, 6); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WMYGO_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_WMYGO_SAVED_1; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYGO_TAXI_1, 3); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYGO_DODGE_1, 11); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WMYGO_RUN_1, 6); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYGO_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WMYGO_CAR_CRASH_1, 7); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYGO_EYEING_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYGO_SHOCKED_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WMYGO_BUMP_1, 9); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYGO_CHAT_1, 10); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWFOGOTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WFOGO_GUN_PANIC_1, 5); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WFOGO_JACKED_1, 6); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WFOGO_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_WFOGO_SAVED_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_WFOGO_FIGHT_1, 14); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WFOGO_DODGE_1, 9); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WFOGO_RUN_1, 2); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WFOGO_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WFOGO_CAR_CRASH_1, 8); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_WFOGO_SHOCKED_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WFOGO_BUMP_1, 8); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WFOGO_CHAT_1, 11); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWMOGOTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WMOGO_GUN_PANIC_1, 5); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WMOGO_JACKED_1, 6); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_WMOGO_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_WMOGO_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_WMOGO_FIGHT_1, 13); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WMOGO_DODGE_1, 12); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WMOGO_RUN_1, 5); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WMOGO_GENERIC_CRASH_1, 8); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WMOGO_CAR_CRASH_1, 9); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_WMOGO_EYEING_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_WMOGO_SHOCKED_1, 3); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WMOGO_BUMP_1, 8); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WMOGO_CHAT_1, 9); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWFYLGTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_WFYLG_GUN_COOL_1, 5); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_WFYLG_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_WFYLG_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_WFYLG_FIGHT_1, 7); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WFYLG_DODGE_1, 8); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WFYLG_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WFYLG_CHAT_1, 10); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWMYLGTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_WMYLG_GUN_COOL_1, 6); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_WMYLG_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_WMYLG_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_WMYLG_FIGHT_1, 7); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYLG_DODGE_1, 9); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WMYLG_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYLG_CHAT_1, 10); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWFYBUTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WFYBU_GUN_PANIC_1, 8); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WFYBU_JACKED_1, 8); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WFYBU_MUGGED_1, 4); break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_WFYBU_TAXI_1, 2); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WFYBU_RUN_1, 8); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WFYBU_GENERIC_CRASH_1, 8); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WFYBU_CAR_CRASH_1, 9); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_WFYBU_SHOCKED_1, 3); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WFYBU_BUMP_1, 21); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWMYBUTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBU_GUN_PANIC_1, 6); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBU_JACKED_1, 5); break; + case SOUND_PED_ROBBED: return SFX_WMYBU_MUGGED_1; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBU_SAVED_1, 2); break; + case SOUND_PED_INNOCENT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBU_INNOCENT_1, 2); break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBU_TAXI_1, 2); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBU_DODGE_1, 10); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBU_RUN_1, 3); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBU_GENERIC_CRASH_1, 5); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBU_CAR_CRASH_1, 9); break; +#ifdef FIX_BUGS + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBU_BLOCKED_1, 9); break; +#else + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBE_BLOCKED_1, 6); break; +#endif + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBU_LOST_1, 5); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBU_EYEING_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBU_SHOCKED_1, 5); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBU_BUMP_1, 11); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYBU_CHAT_1, 10); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWMOBUTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBU_GUN_PANIC_1, 6); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBU_JACKED_1, 7); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBU_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBU_SAVED_1, 3); break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBU_TAXI_1, 2); break; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBU_FIGHT_1, 3); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBU_DODGE_1, 8); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBU_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBU_CAR_CRASH_1, 7); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBU_BLOCKED_1, 7); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBU_LOST_1, 3); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBU_EYEING_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WMOBU_BUMP_1, 10); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWFYPRTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_WFYPR_GUN_COOL_1, 6); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WFYPR_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_WFYPR_SAVED_1; + case SOUND_PED_PLAYER_BEFORESEX: GetPhrase(sfx, ped->m_lastComment, SFX_WFYPR_FUCKING_1, 5); break; + case SOUND_PED_TAXI_WAIT: return SFX_WFYPR_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_WFYPR_FIGHT_1, 9); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WFYPR_DODGE_1, 10); break; + case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, SFX_WFYPR_SOLICIT_1, 15); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WFYPR_BUMP_1, 11); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WFYPR_CHAT_1, 14); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWFOTRTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_WFOTR_GUN_COOL_1, 6); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_WFOTR_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_WFOTR_TAXI_1; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WFOTR_DODGE_1, 9); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WFOTR_RUN_1, 6); break; + case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, SFX_WFOTR_SOLICIT_1, 9); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WFOTR_BUMP_1, 11); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WFOTR_CHAT_1, 9); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWMOTRTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_WMOTR_GUN_COOL_1, 5); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_WMOTR_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_WMOTR_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_WMOTR_FIGHT_1, 6); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WMOTR_DODGE_1, 17); break; + case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, SFX_WMOTR_SOLICIT_1, 7); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_WMOTR_EYEING_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_WMOTR_SHOCKED_1, 3); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WMOTR_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WMOTR_CHAT_1, 13); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWMYPITalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_WMYPI_GUN_COOL_1, 5); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_WMYPI_JACKING_1, 4); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WMYPI_JACKED_1, 6); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WMYPI_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_WMYPI_SAVED_1, 2); break; + case SOUND_PED_INNOCENT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYPI_INNOCENT_1, 2); break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_WMYPI_TAXI_1, 4); break; +#ifdef FIX_BUGS + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_WMYPI_FIGHT_1, 9); break; +#else + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_WMYPI_FIGHT_1, 7); break; +#endif + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYPI_DODGE_1, 8); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYPI_GENERIC_CRASH_1, 8); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WMYPI_CAR_CRASH_1, 8); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WMYPI_BLOCKED_1, 8); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYPI_EYEING_1, 6); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WMYPI_BUMP_1, 10); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWMOCATalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WMOCA_GUN_PANIC_1, 6); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_WMOCA_JACKING_1, 11); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_WMOCA_JACKED_1, 10); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WMOCA_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_WMOCA_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_WMOCA_TAXI_1; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_WMOCA_FIGHT_1, 8); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WMOCA_DODGE_1, 10); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_WMOCA_GENERIC_CRASH_1, 9); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_WMOCA_CAR_CRASH_1, 10); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WMOCA_BLOCKED_1, 12); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_WMOCA_EYEING_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WMOCA_BUMP_1, 6); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWFYSHTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_WFYSH_GUN_COOL_1, 9); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WFYSH_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_WFYSH_SAVED_1, 4); break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_WFYSH_TAXI_1, 2); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WFYSH_DODGE_1, 11); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WFYSH_RUN_1, 11); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_WFYSH_LOST_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_WFYSH_SHOCKED_1, 5); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WFYSH_BUMP_1, 12); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WFYSH_CHAT_1, 10); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWFOSHTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_WFOSH_GUN_COOL_1, 10); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WFOSH_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_WFOSH_SAVED_1, 3); break; + case SOUND_PED_TAXI_WAIT: return SFX_WFOSH_TAXI_1; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WFOSH_DODGE_1, 10); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WFOSH_RUN_1, 9); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_WFOSH_LOST_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_WFOSH_SHOCKED_1, 5); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WFOSH_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_WFOSH_CHAT_1, 9); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetJFOTOTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_JFOTO_GUN_PANIC_1, 4); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_JFOTO_JACKED_1, 5); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_JFOTO_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_JFOTO_SAVED_1, 2); break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_JFOTO_TAXI_1, 2); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_JFOTO_DODGE_1, 9); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_JFOTO_RUN_1, 5); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_JFOTO_GENERIC_CRASH_1, 6); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_JFOTO_CAR_CRASH_1, 8); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_JFOTO_BLOCKED_1, 8); break; + case SOUND_PED_WAIT_DOUBLEBACK: return SFX_JFOTO_LOST_1; + case SOUND_PED_CHAT_EVENT: return SFX_JFOTO_SHOCKED_1; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_JFOTO_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_JFOTO_CHAT_1, 13); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetJMOTOTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_JMOTO_GUN_PANIC_1, 4); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_JMOTO_JACKED_1, 4); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_JMOTO_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_JMOTO_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_JMOTO_TAXI_1; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_JMOTO_DODGE_1, 6); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_JMOTO_RUN_1, 4); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_JMOTO_GENERIC_CRASH_1, 6); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_JMOTO_CAR_CRASH_1, 6); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_JMOTO_BLOCKED_1, 8); break; + case SOUND_PED_WAIT_DOUBLEBACK: return SFX_JMOTO_LOST_1; + case SOUND_PED_CHAT_EVENT: return SFX_JMOTO_SHOCKED_1; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_JMOTO_BUMP_1, 8); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_JMOTO_CHAT_1, 7); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetHNTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_HAITIAN_GANG_1_GUN_COOL_1, 5); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_HAITIAN_GANG_1_JACKING_1, 4); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_HAITIAN_GANG_1_JACKED_1, 6); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_HAITIAN_GANG_1_MUGGED_1, 3); break; + case SOUND_PED_ACCIDENTREACTION1: sfx = SFX_HAITIAN_GANG_1_SAVED_1; break; + case SOUND_PED_TAXI_WAIT: sfx = SFX_HAITIAN_GANG_1_TAXI_1; break; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_HAITIAN_GANG_1_FIGHT_1, 10); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HAITIAN_GANG_1_DODGE_1, 10); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_HAITIAN_GANG_1_GENERIC_CRASH_1, 9); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_HAITIAN_GANG_1_CAR_CRASH_1, 9); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_HAITIAN_GANG_1_BLOCKED_1, 9); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_HAITIAN_GANG_1_LOST_1, 4); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_HAITIAN_GANG_1_EYEING_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HAITIAN_GANG_1_BUMP_1, 12); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_HAITIAN_GANG_1_CHAT_1, 14); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return (SFX_HAITIAN_GANG_2_BLOCKED_1 - SFX_HAITIAN_GANG_1_BLOCKED_1) * (m_sQueueSample.m_nEntityIndex % 3) + sfx; +} + +uint32 +cAudioManager::GetBKTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_BIKER_GANG_1_GUN_COOL_1, 5); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_BIKER_GANG_1_JACKING_1, 4); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_BIKER_GANG_1_JACKED_1, 8); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_BIKER_GANG_1_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: sfx = SFX_BIKER_GANG_1_SAVED_1; break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_BIKER_GANG_1_TAXI_1, 2); break; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_BIKER_GANG_1_FIGHT_1, 9); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BIKER_GANG_1_DODGE_1, 9); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_BIKER_GANG_1_GENERIC_CRASH_1, 8); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_BIKER_GANG_1_BLOCKED_1, 10); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_BIKER_GANG_1_LOST_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_BIKER_GANG_1_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_BIKER_GANG_1_CHAT_1, 12); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return (SFX_BIKER_GANG_2_BLOCKED_1 - SFX_BIKER_GANG_1_BLOCKED_1) * (m_sQueueSample.m_nEntityIndex % 3) + sfx; +} + +uint32 +cAudioManager::GetCBTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_GANG_1_GUN_COOL_1, 5); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_GANG_1_JACKING_1, 5); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_GANG_1_JACKED_1, 4); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_GANG_1_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: sfx = SFX_CUBAN_GANG_1_SAVED_1; break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_GANG_1_TAXI_1, 2); break; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_GANG_1_FIGHT_1, 9); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_GANG_1_DODGE_1, 9); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_GANG_1_GENERIC_CRASH_1, 8); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_GANG_1_CAR_CRASH_1, 8); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_GANG_1_BLOCKED_1, 8); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_GANG_1_LOST_1, 2); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_GANG_1_EYEING_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_GANG_1_BUMP_1, 11); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_GANG_1_CHAT_1, 10); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return (SFX_CUBAN_GANG_2_BLOCKED_1 - SFX_CUBAN_GANG_1_BLOCKED_1) * (m_sQueueSample.m_nEntityIndex % 3) + sfx; +} + +uint32 +cAudioManager::GetSGTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_STREET_GANG_1_GUN_COOL_1, 5); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_STREET_GANG_1_JACKING_1, 5); break; + case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, SFX_STREET_GANG_1_MUGGING_1, 5); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_STREET_GANG_1_JACKED_1, 5); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_STREET_GANG_1_MUGGED_1, 3); break; + case SOUND_PED_ACCIDENTREACTION1: sfx = SFX_STREET_GANG_1_SAVED_1; break; + case SOUND_PED_TAXI_WAIT: sfx = SFX_STREET_GANG_1_TAXI_1; break; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_STREET_GANG_1_FIGHT_1, 10); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_STREET_GANG_1_DODGE_1, 9); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_STREET_GANG_1_GENERIC_CRASH_1, 6); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_STREET_GANG_1_CAR_CRASH_1, 6); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_STREET_GANG_1_BLOCKED_1, 8); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_STREET_GANG_1_LOST_1, 2); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_STREET_GANG_1_EYEING_1, 3); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_STREET_GANG_1_SHOCKED_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_STREET_GANG_1_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_STREET_GANG_1_CHAT_1, 12); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + if(ped->GetModelIndex() == MI_SGB) sfx += (SFX_STREET_GANG_2_BLOCKED_1 - SFX_STREET_GANG_1_BLOCKED_1); + return sfx; +} + +uint32 +cAudioManager::GetCLTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_LORD_GANG_1_GUN_COOL_1, 5); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_LORD_GANG_1_JACKING_1, 5); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_LORD_GANG_1_JACKED_1, 6); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_LORD_GANG_1_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: sfx = SFX_CUBAN_LORD_GANG_1_SAVED_1; break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_LORD_GANG_1_TAXI_1, 2); break; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_LORD_GANG_1_FIGHT_1, 10); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_LORD_GANG_1_DODGE_1, 13); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_LORD_GANG_1_GENERIC_CRASH_1, 8); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_LORD_GANG_1_CAR_CRASH_1, 10); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_LORD_GANG_1_BLOCKED_1, 10); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_LORD_GANG_1_LOST_1, 2); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_LORD_GANG_1_EYEING_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_LORD_GANG_1_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_CUBAN_LORD_GANG_1_CHAT_1, 10); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return (SFX_CUBAN_LORD_GANG_2_BLOCKED_1 - SFX_CUBAN_LORD_GANG_1_BLOCKED_1) * (m_sQueueSample.m_nEntityIndex % 3) + sfx; +} + +uint32 +cAudioManager::GetGDTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_GUARD_DUTY_1_GUN_COOL_1, 6); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_GUARD_DUTY_1_SAVED_1, 2); break; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_GUARD_DUTY_1_FIGHT_1, 7); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_GUARD_DUTY_1_DODGE_1, 9); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_GUARD_DUTY_1_LOST_1, 2); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_GUARD_DUTY_1_EYEING_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_GUARD_DUTY_1_SHOCKED_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_GUARD_DUTY_1_BUMP_1, 10); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_GUARD_DUTY_1_CHAT_1, 10); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return (SFX_GUARD_DUTY_2_BUMP_1 - SFX_GUARD_DUTY_1_BUMP_1) * (m_sQueueSample.m_nEntityIndex % 3) + sfx; +} + +uint32 +cAudioManager::GetPGTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_GANG_1_GUN_COOL_1, 4); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_GANG_1_JACKING_1, 5); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_GANG_1_JACKED_1, 5); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_GANG_1_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: sfx = SFX_PLAYER_GANG_1_SAVED_1; break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_GANG_1_TAXI_1, 2); break; + case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_GANG_1_FIGHT_1, 5); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_GANG_1_DODGE_1, 7); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_GANG_1_GENERIC_CRASH_1, 5); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_GANG_1_CAR_CRASH_1, 5); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_GANG_1_BLOCKED_1, 10); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_GANG_1_LOST_1, 2); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_GANG_1_EYEING_1, 2); break; + case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_GANG_1_SHOCKED_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_GANG_1_BUMP_1, 5); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_PLAYER_GANG_1_CHAT_1, 8); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return (SFX_PLAYER_GANG_2_BLOCKED_1 - SFX_PLAYER_GANG_1_BLOCKED_1) * (m_sQueueSample.m_nEntityIndex % 3) + sfx; +} + +uint32 +cAudioManager::GetViceWhiteTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + switch(sound) { + case SOUND_PED_ARREST_COP: GetPhrase(sfx, ped->m_lastComment, SFX_VICE_VOICE_1_ARREST_1, 3); break; + case SOUND_PED_MIAMIVICE_EXITING_CAR: sfx = SFX_VICE_VOICE_1_MIAMIVICE_EXITING_CAR_1; break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + sfx += (SFX_VICE_VOICE_2_ARREST_1-SFX_VICE_VOICE_1_ARREST_1) * (m_sQueueSample.m_nEntityIndex % 5); + return sfx; +} + +uint32 +cAudioManager::GetViceBlackTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + switch(sound) { + case SOUND_PED_ARREST_COP: GetPhrase(sfx, ped->m_lastComment, SFX_VICE_VOICE_6_ARREST_1, 3); break; + case SOUND_PED_MIAMIVICE_EXITING_CAR: return SFX_VICE_VOICE_6_MIAMIVICE_EXITING_CAR_1; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetBMODKTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_BMODK_GUN_PANIC_1, 4); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_BMODK_JACKED_1, 9); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_BMODK_MUGGED_1, 2); break; + case SOUND_PED_INNOCENT: GetPhrase(sfx, ped->m_lastComment, SFX_BMODK_INNOCENT_1, 3); break; + case SOUND_PED_TAXI_WAIT: return SFX_BMODK_TAXI_1; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_BMODK_DODGE_1, 7); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_BMODK_RUN_1, 4); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_BMODK_GENERIC_CRASH_1, 7); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_BMODK_CAR_CRASH_1, 10); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_BMODK_BLOCKED_1, 8); break; + case SOUND_PED_147: // this is some cut behaviour, the guy was selling something + GetPhrase(sfx, ped->m_lastComment, SFX_BMODK_UNK_147_1, 11); + // what is this? some sort of censorship? + switch(sfx) { + case SFX_BMODK_UNK_147_5: + case SFX_BMODK_UNK_147_6: + case SFX_BMODK_UNK_147_7: GetPhrase(sfx, ped->m_lastComment, SFX_BMODK_UNK_147_1, 4); break; + default: break; + } + break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_BMODK_BUMP_1, 10); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetHMYAPTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_HMYAP_GUN_PANIC_1, 7); break; + case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, SFX_HMYAP_JACKING_1, 4); break; + case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, SFX_HMYAP_JACKED_1, 7); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_HMYAP_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, SFX_HMYAP_SAVED_1, 2); break; + case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, SFX_HMYAP_TAXI_1, 2); break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_HMYAP_DODGE_1, 9); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_HMYAP_RUN_1, 6); break; + case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, SFX_HMYAP_GENERIC_CRASH_1, 6); break; + case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, SFX_HMYAP_CAR_CRASH_1, 9); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_HMYAP_BLOCKED_1, 9); break; + case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, SFX_HMYAP_LOST_1, 2); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_HMYAP_EYEING_1, 3); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_HMYAP_BUMP_1, 11); break; + case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, SFX_HMYAP_CHAT_1, 9); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWFYJGTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WFYJG_GUN_PANIC_1, 4); break; + case SOUND_PED_ACCIDENTREACTION1: sfx = SFX_WFYJG_SAVED_1; break; + case SOUND_PED_TAXI_WAIT: sfx = SFX_WFYJG_TAXI_1; break; + case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, SFX_WFYJG_DODGE_1, 8); break; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WFYJG_RUN_1, 6); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WFYJG_BUMP_1, 12); break; + default: return GetGenericFemaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetWMYJGTalkSfx(CPed *ped, uint16 sound) +{ + uint32 sfx; + + switch(sound) { + case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, SFX_WMYJG_GUN_PANIC_1, 4); break; + case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, SFX_WMYJG_MUGGED_1, 2); break; + case SOUND_PED_ACCIDENTREACTION1: return SFX_WMYJG_SAVED_1; + case SOUND_PED_TAXI_WAIT: return SFX_WMYJG_TAXI_1; + case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, SFX_WMYJG_RUN_1, 5); break; + case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, SFX_WMYJG_BLOCKED_1, 10); break; + case SOUND_PED_CHAT_SEXY_MALE: GetPhrase(sfx, ped->m_lastComment, SFX_WMYJG_EYEING_1, 2); break; + case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, SFX_WMYJG_BUMP_1, 10); break; + default: return GetGenericMaleTalkSfx(ped, sound); + } + return sfx; +} + +uint32 +cAudioManager::GetSpecialCharacterTalkSfx(CPed *ped, int32 model, uint16 sound) +{ + return NO_SAMPLE; +} + +void +cAudioManager::DebugPlayPedComment(int32 sound) +{ + tPedComment pedComment; + + pedComment.m_nSampleIndex = sound; + pedComment.m_nLoadingTimeout = 10; + pedComment.m_nEntityIndex = 0; + pedComment.m_fDistance = 0.0f; + pedComment.m_nVolume = 99; +#if defined(EXTERNAL_3D_SOUND) && defined(FIX_BUGS) + pedComment.m_nEmittingVolume = 99; +#endif + + pedComment.m_vecPos = CWorld::Players[0].m_pPed->GetPosition(); + + m_sPedComments.Add(&pedComment); +} + +#ifdef DEBUGMENU +uint32 nDebugPlayPedComment = SAMPLEBANK_PED_START; + +void DebugMenuPlayPedComment() +{ + AudioManager.DebugPlayPedComment(nDebugPlayPedComment); +} + +SETTWEAKPATH("Audio"); +TWEAKUINT32N(nDebugPlayPedComment, SAMPLEBANK_PED_START, SAMPLEBANK_PED_END, 1, "Ped Comment ID"); +TWEAKFUNCN(DebugMenuPlayPedComment, "Play Ped Comment"); + +#endif + +void +cPedComments::Add(tPedComment *com) +{ + uint8 index; + + // copypasted priority check from cAudioManager::AddSampleToRequestedQueue + + if (m_nPedCommentCount[m_nActiveQueue] >= NUM_PED_COMMENTS_SLOTS) { + index = m_aPedCommentOrderList[m_nActiveQueue][NUM_PED_COMMENTS_SLOTS - 1]; + if (m_aPedCommentQueue[m_nActiveQueue][index].m_nVolume > com->m_nVolume) + return; + } else + index = m_nPedCommentCount[m_nActiveQueue]++; + + m_aPedCommentQueue[m_nActiveQueue][index] = *com; + + // this bit is basically copypasted cAudioManager::AddDetailsToRequestedOrderList + uint8 i = 0; + if (index != 0) { + for (i = 0; i < index; i++) { + if (m_aPedCommentQueue[m_nActiveQueue][m_aPedCommentOrderList[m_nActiveQueue][i]].m_nVolume < m_aPedCommentQueue[m_nActiveQueue][index].m_nVolume) + break; + } + + if (i < index) + memmove(&m_aPedCommentOrderList[m_nActiveQueue][i + 1], &m_aPedCommentOrderList[m_nActiveQueue][i], NUM_PED_COMMENTS_SLOTS - 1 - i); + } + + m_aPedCommentOrderList[m_nActiveQueue][i] = index; +} + +void +cPedComments::Process() +{ + uint32 sampleIndex; + uint8 queue; + bool8 bIsPlayerComment; + static uint8 counter = 0; + static uint32 prevSamples[10] = { NO_SAMPLE, NO_SAMPLE, NO_SAMPLE, NO_SAMPLE, NO_SAMPLE, NO_SAMPLE, NO_SAMPLE, NO_SAMPLE, NO_SAMPLE, NO_SAMPLE }; + + if (AudioManager.m_bIsPaused) return; + + if (m_nPedCommentCount[m_nActiveQueue]) { + for(int i = 0; i < ARRAY_SIZE(prevSamples); i++) { + if(m_aPedCommentQueue[m_nActiveQueue][m_aPedCommentOrderList[m_nActiveQueue][0]].m_nSampleIndex == + prevSamples[(counter + 1 + i) % ARRAY_SIZE(prevSamples)]) { + m_aPedCommentQueue[m_nActiveQueue][m_aPedCommentOrderList[m_nActiveQueue][0]].m_nLoadingTimeout = -1; + goto PedCommentAlreadyAdded; + } + } +#if defined(GTA_PS2) || defined(FIX_BUGS) + uint8 IsLoadedResult; + sampleIndex = m_aPedCommentQueue[m_nActiveQueue][m_aPedCommentOrderList[m_nActiveQueue][0]].m_nSampleIndex; + if (sampleIndex >= PLAYER_COMMENTS_START && sampleIndex <= PLAYER_COMMENTS_END) { + IsLoadedResult = SampleManager.IsMissionAudioLoaded(MISSION_AUDIO_PLAYER_COMMENT, sampleIndex); + bIsPlayerComment = TRUE; + } else { + IsLoadedResult = SampleManager.IsPedCommentLoaded(sampleIndex); + bIsPlayerComment = FALSE; + } + switch(IsLoadedResult) { +#else + switch(SampleManager.IsPedCommentLoaded(sampleIndex)) { +#endif + case LOADING_STATUS_NOT_LOADED: +#if defined(GTA_PC) && !defined(FIX_BUGS) + if(!m_bDelay) +#endif +#if defined(GTA_PS2) || defined(FIX_BUGS) + if (bIsPlayerComment) + SampleManager.LoadMissionAudio(MISSION_AUDIO_PLAYER_COMMENT, sampleIndex); + else +#endif + SampleManager.LoadPedComment(sampleIndex); + break; + case LOADING_STATUS_LOADED: + AudioManager.m_sQueueSample.m_nEntityIndex = m_aPedCommentQueue[m_nActiveQueue][m_aPedCommentOrderList[m_nActiveQueue][0]].m_nEntityIndex; + AudioManager.m_sQueueSample.m_nCounter = 0; + AudioManager.m_sQueueSample.m_nSampleIndex = sampleIndex; + AudioManager.m_sQueueSample.m_nBankIndex = SFX_BANK_PED_COMMENTS; + AudioManager.m_sQueueSample.m_nPriority = 3; + AudioManager.m_sQueueSample.m_nVolume = m_aPedCommentQueue[m_nActiveQueue][m_aPedCommentOrderList[m_nActiveQueue][0]].m_nVolume; + AudioManager.m_sQueueSample.m_fDistance = m_aPedCommentQueue[m_nActiveQueue][m_aPedCommentOrderList[m_nActiveQueue][0]].m_fDistance; + AudioManager.m_sQueueSample.m_nLoopCount = 1; +#ifndef GTA_PS2 + AudioManager.m_sQueueSample.m_nLoopStart = 0; + AudioManager.m_sQueueSample.m_nLoopEnd = -1; +#endif +#ifdef EXTERNAL_3D_SOUND + #ifdef FIX_BUGS + AudioManager.m_sQueueSample.m_nEmittingVolume = m_aPedCommentQueue[m_nActiveQueue][m_aPedCommentOrderList[m_nActiveQueue][0]].m_nEmittingVolume; + #else + AudioManager.m_sQueueSample.m_nEmittingVolume = MAX_VOLUME; + #endif // FIX_BUGS +#endif // EXTERNAL_3D_SOUND +#ifdef ATTACH_RELEASING_SOUNDS_TO_ENTITIES + // let's disable doppler because if sounds funny as the sound moves + // originally position of ped comment doesn't change so this has no effect anyway + AudioManager.m_sQueueSample.m_fSpeedMultiplier = 0.0f; +#else + AudioManager.m_sQueueSample.m_fSpeedMultiplier = 3.0f; +#endif + AudioManager.m_sQueueSample.m_MaxDistance = PED_COMMENT_MAX_DIST; + AudioManager.m_sQueueSample.m_bStatic = TRUE; + AudioManager.m_sQueueSample.m_vecPos = m_aPedCommentQueue[m_nActiveQueue][m_aPedCommentOrderList[m_nActiveQueue][0]].m_vecPos; +#ifdef AUDIO_REVERB + AudioManager.m_sQueueSample.m_bReverb = TRUE; +#endif // AUDIO_REVERB +#ifdef AUDIO_REFLECTIONS + AudioManager.m_sQueueSample.m_bReflections = TRUE; +#endif // AUDIO_REFLECTIONS + AudioManager.m_sQueueSample.m_bIs2D = FALSE; +#ifdef FIX_BUGS + if((sampleIndex >= SFX_POLICE_BOAT_1 && sampleIndex <= SFX_POLICE_BOAT_23) || + (sampleIndex >= SFX_POLICE_HELI_1 && sampleIndex <= SFX_POLICE_HELI_20)) + AudioManager.m_sQueueSample.m_MaxDistance = PED_COMMENT_POLICE_HELI_MAX_DIST; + #ifndef ATTACH_RELEASING_SOUNDS_TO_ENTITIES + else if (sampleIndex >= PLAYER_COMMENTS_START && sampleIndex <= PLAYER_COMMENTS_END) { // check if player sfx + AudioManager.m_sQueueSample.m_bIs2D = TRUE; + AudioManager.m_sQueueSample.m_nPan = 63; + } + #endif // ATTACH_RELEASING_SOUNDS_TO_ENTITIES +#endif // FIX_BUGS + AudioManager.m_sQueueSample.m_nFrequency = + SampleManager.GetSampleBaseFrequency(AudioManager.m_sQueueSample.m_nSampleIndex) + AudioManager.RandomDisplacement(750); +#ifndef USE_TIME_SCALE_FOR_AUDIO + if (CTimer::GetIsSlowMotionActive()) + AudioManager.m_sQueueSample.m_nFrequency >>= 1; +#endif + m_aPedCommentQueue[m_nActiveQueue][m_aPedCommentOrderList[m_nActiveQueue][0]].m_nLoadingTimeout = -1; + prevSamples[counter++] = sampleIndex; + if(counter == 10) counter = 0; + AudioManager.AddSampleToRequestedQueue(); +#if defined(GTA_PC) && !defined(FIX_BUGS) + m_nDelayTimer = CTimer::GetTimeInMilliseconds(); + m_bDelay = TRUE; +#endif + break; + case LOADING_STATUS_LOADING: break; + default: break; + } + } + +PedCommentAlreadyAdded: + // Switch queue + if (m_nActiveQueue == 0) { + queue = 0; + m_nActiveQueue = 1; + } else { + queue = 1; + m_nActiveQueue = 0; + } + for (uint8 i = 0; i < m_nPedCommentCount[queue]; i++) { + if (m_aPedCommentQueue[queue][m_aPedCommentOrderList[queue][i]].m_nLoadingTimeout > 0) { + m_aPedCommentQueue[queue][m_aPedCommentOrderList[queue][i]].m_nLoadingTimeout--; + Add(&m_aPedCommentQueue[queue][m_aPedCommentOrderList[queue][i]]); + } + } + + // clear queue + for (uint8 i = 0; i < NUM_PED_COMMENTS_SLOTS; i++) + m_aPedCommentOrderList[queue][i] = NUM_PED_COMMENTS_SLOTS; + m_nPedCommentCount[queue] = 0; +#if defined(GTA_PC) && !defined(FIX_BUGS) + if(m_bDelay) + if(CTimer::GetTimeInMilliseconds() - m_nDelayTimer > 6000) m_bDelay = FALSE; +#endif +} + +#undef cooldown_phrase + +#pragma endregion + +#pragma endregion All the ped audio code + +void +cAudioManager::ProcessExplosions(int32 id) +{ + uint8 type; + float distSquared; + + for (uint8 i = 0; i < ARRAY_SIZE(gaExplosion); i++) { + if (CExplosion::DoesExplosionMakeSound(i) && CExplosion::GetExplosionActiveCounter(i) == 1) { + CExplosion::ResetExplosionActiveCounter(i); + type = CExplosion::GetExplosionType(i); + switch (type) { + case EXPLOSION_GRENADE: + case EXPLOSION_ROCKET: + case EXPLOSION_BARREL: + case EXPLOSION_TANK_GRENADE: + m_sQueueSample.m_MaxDistance = EXPLOSION_DEFAULT_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_EXPLOSION_2; + m_sQueueSample.m_nFrequency = RandomDisplacement(1000) + 19000; + m_sQueueSample.m_nPriority = 0; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + SET_SOUND_REFLECTION(TRUE); + break; + case EXPLOSION_MINE: + case EXPLOSION_HELI_BOMB: + m_sQueueSample.m_MaxDistance = EXPLOSION_MINE_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_ROCKET_LEFT; + m_sQueueSample.m_nFrequency = RandomDisplacement(1000) + 12347; + m_sQueueSample.m_nPriority = 0; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + SET_SOUND_REFLECTION(TRUE); + break; + case EXPLOSION_MOLOTOV: + m_sQueueSample.m_MaxDistance = EXPLOSION_MOLOTOV_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_EXPLOSION_3; + m_sQueueSample.m_nFrequency = RandomDisplacement(1000) + 19000; + m_sQueueSample.m_nPriority = 0; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + SET_SOUND_REFLECTION(FALSE); + break; + default: + m_sQueueSample.m_MaxDistance = EXPLOSION_DEFAULT_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_EXPLOSION_1; + m_sQueueSample.m_nFrequency = RandomDisplacement(1000) + 19500; + if (type == EXPLOSION_HELI) + m_sQueueSample.m_nFrequency = 8 * m_sQueueSample.m_nFrequency / 10; //same *= 8 / 10; + m_sQueueSample.m_nPriority = 0; + m_sQueueSample.m_nBankIndex = SFX_BANK_GENERIC_EXTRA; +#ifdef FIX_BUGS + SET_SOUND_REFLECTION(TRUE); +#endif + break; + } + m_sQueueSample.m_vecPos = *CExplosion::GetExplosionPosition(i); + distSquared = GetDistanceSquared(m_sQueueSample.m_vecPos); + if (distSquared < SQR(m_sQueueSample.m_MaxDistance)) { + m_sQueueSample.m_fDistance = Sqrt(distSquared); + m_sQueueSample.m_nVolume = ComputeVolume(MAX_VOLUME, m_sQueueSample.m_MaxDistance, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = i; + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_bStatic = TRUE; + SET_EMITTING_VOLUME(MAX_VOLUME); + RESET_LOOP_OFFSETS + SET_SOUND_REVERB(TRUE); + AddSampleToRequestedQueue(); + } + } + } + } +} + +void +cAudioManager::ProcessFires(int32 id) +{ + CEntity *entity; + uint8 Vol; + float distSquared; + + for (uint8 i = 0; i < NUM_FIRES; i++) { + if (gFireManager.m_aFires[i].m_bIsOngoing && gFireManager.m_aFires[i].m_bAudioSet) { + entity = gFireManager.m_aFires[i].m_pEntity; + if (entity) { + switch (entity->GetType()) { + case ENTITY_TYPE_BUILDING: + m_sQueueSample.m_MaxDistance = FIRE_BUILDING_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_CAR_ON_FIRE; + Vol = FIRE_BUILDING_VOLUME; + m_sQueueSample.m_nFrequency = 8 * SampleManager.GetSampleBaseFrequency(SFX_CAR_ON_FIRE) / 10; + m_sQueueSample.m_nFrequency += i * (m_sQueueSample.m_nFrequency >> 6); + m_sQueueSample.m_nPriority = 6; + break; + case ENTITY_TYPE_PED: + m_sQueueSample.m_MaxDistance = FIRE_PED_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_PED_ON_FIRE; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_PED_ON_FIRE); + Vol = FIRE_PED_VOLUME; + m_sQueueSample.m_nFrequency += i * (m_sQueueSample.m_nFrequency >> 6); + m_sQueueSample.m_nPriority = 10; + break; + default: + m_sQueueSample.m_MaxDistance = FIRE_DEFAULT_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_CAR_ON_FIRE; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_CAR_ON_FIRE); + m_sQueueSample.m_nFrequency += i * (m_sQueueSample.m_nFrequency >> 6); + Vol = FIRE_DEFAULT_VOLUME; + m_sQueueSample.m_nPriority = 8; + } + } else { + m_sQueueSample.m_MaxDistance = FIRE_DEFAULT_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_CAR_ON_FIRE; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_CAR_ON_FIRE); + Vol = FIRE_DEFAULT_VOLUME; + m_sQueueSample.m_nPriority = 8; + } + m_sQueueSample.m_vecPos = gFireManager.m_aFires[i].m_vecPos; + distSquared = GetDistanceSquared(m_sQueueSample.m_vecPos); + if (distSquared < SQR(m_sQueueSample.m_MaxDistance)) { + m_sQueueSample.m_fDistance = Sqrt(distSquared); + m_sQueueSample.m_nVolume = ComputeVolume(Vol, m_sQueueSample.m_MaxDistance, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = i; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_nFramesToPlay = 10; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nLoopCount = 0; + m_sQueueSample.m_bStatic = FALSE; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + if (gFireManager.m_aFires[i].m_bExtinguishedWithWater) { + gFireManager.m_aFires[i].m_bExtinguishedWithWater = FALSE; + Vol = FIRE_EXTINGUISH_VOLUME * gFireManager.m_aFires[i].m_fWaterExtinguishCountdown; + m_sQueueSample.m_nVolume = ComputeVolume(Vol, m_sQueueSample.m_MaxDistance, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nSampleIndex = SFX_JUMBO_TAXI; + m_sQueueSample.m_nFrequency = 19591; + m_sQueueSample.m_nFrequency += i * (m_sQueueSample.m_nFrequency >> 6); + m_sQueueSample.m_nPriority = 9; + m_sQueueSample.m_nCounter = i + 40; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_nFramesToPlay = 10; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nLoopCount = 0; + m_sQueueSample.m_bStatic = FALSE; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + } + } +} + +void +cAudioManager::ProcessWaterCannon(int32 id) +{ + for (uint32 i = 0; i < NUM_WATERCANNONS; i++) { + if (CWaterCannons::aCannons[i].m_nId) { + m_sQueueSample.m_vecPos = CWaterCannons::aCannons[0].m_avecPos[CWaterCannons::aCannons[i].m_nCur]; + float distSquared = GetDistanceSquared(m_sQueueSample.m_vecPos); + if (distSquared < SQR(WATER_CANNON_MAX_DIST)) { + m_sQueueSample.m_fDistance = Sqrt(distSquared); + m_sQueueSample.m_nVolume = ComputeVolume(WATER_CANNON_VOLUME, WATER_CANNON_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_MaxDistance = WATER_CANNON_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_JUMBO_TAXI; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = 15591; + m_sQueueSample.m_nPriority = 5; + m_sQueueSample.m_nCounter = i; + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_nFramesToPlay = 8; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nLoopCount = 0; + m_sQueueSample.m_bStatic = FALSE; + SET_EMITTING_VOLUME(WATER_CANNON_VOLUME); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + } + } +} + +#pragma region SCRIPT_OBJECTS +void +cAudioManager::ProcessScriptObject(int32 id) +{ + if (MusicManager.m_nMusicMode == MUSICMODE_GAME) { + cAudioScriptObject *entity = (cAudioScriptObject*)m_asAudioEntities[id].m_pEntity; + if (entity != nil) { + m_sQueueSample.m_vecPos = entity->Posn; + if (m_asAudioEntities[id].m_AudioEvents == 1) + ProcessOneShotScriptObject(m_asAudioEntities[id].m_awAudioEvent[0]); + else + ProcessLoopingScriptObject(entity->AudioId); + } + } +} + +void +cAudioManager::ProcessOneShotScriptObject(uint8 sound) +{ + CPlayerPed *playerPed; + uint8 Vol; + float distSquared; + + static uint8 iSound = 0; + + switch (sound) { + case SCRIPT_SOUND_SEAPLANE_LOW_FUEL: + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_SEAPLANE_LOW_FUEL_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_SEAPLANE_LOW; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + Vol = SCRIPT_OBJECT_SEAPLANE_LOW_FUEL_VOLUME; +#ifdef FIX_BUGS + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_SEAPLANE_LOW); +#else + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_CAR_HORN_JEEP); +#endif + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_bIs2D = TRUE; + SET_SOUND_REFLECTION(FALSE); + break; + case SCRIPT_SOUND_WILLIE_CARD_SWIPE: + Vol = SCRIPT_OBJECT_WILLIE_CARD_SWIPE_VOLUME; + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_WILLIE_CARD_SWIPE_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_BOMB_BEEP; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = 20159; + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_fSpeedMultiplier = 1.0f; + m_sQueueSample.m_bIs2D = FALSE; + SET_SOUND_REFLECTION(FALSE); + break; + case SCRIPT_SOUND_BOX_DESTROYED_1: + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_BOX_DESTROYED_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_WOODEN_BOX_SMASH; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = RandomDisplacement(1500) + 18600; + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_bIs2D = FALSE; + SET_SOUND_REFLECTION(TRUE); + Vol = m_anRandomTable[2] % 20 + SCRIPT_OBJECT_BOX_DESTROYED_VOLUME; + break; + case SCRIPT_SOUND_BOX_DESTROYED_2: + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_BOX_DESTROYED_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_CARDBOARD_BOX_SMASH; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = RandomDisplacement(1500) + 18600; + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_bIs2D = FALSE; + SET_SOUND_REFLECTION(TRUE); + Vol = m_anRandomTable[2] % 20 + SCRIPT_OBJECT_BOX_DESTROYED_VOLUME; + break; + case SCRIPT_SOUND_METAL_COLLISION: + m_sQueueSample.m_MaxDistance = COLLISION_MAX_DIST; + m_sQueueSample.m_nSampleIndex = m_anRandomTable[3] % 5 + SFX_COL_CAR_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 4); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_bIs2D = FALSE; + SET_SOUND_REFLECTION(TRUE); + Vol = m_anRandomTable[2] % 30 + SCRIPT_OBJECT_METAL_COLLISION_VOLUME; + break; + case SCRIPT_SOUND_TIRE_COLLISION: + m_sQueueSample.m_MaxDistance = COLLISION_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_TYRE_BUMP; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 4); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_bIs2D = FALSE; + SET_SOUND_REFLECTION(TRUE); + Vol = m_anRandomTable[2] % 30 + SCRIPT_OBJECT_TIRE_COLLISION_VOLUME; + break; + case SCRIPT_SOUND_HIT_BALL: + m_sQueueSample.m_MaxDistance = COLLISION_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_HIT_BALL; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 4); + m_sQueueSample.m_nPriority = 5; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_bIs2D = FALSE; + SET_SOUND_REFLECTION(TRUE); + Vol = m_anRandomTable[2] % 30 + SCRIPT_OBJECT_HIT_BALL_VOLUME; + break; + case SCRIPT_SOUND_GUNSHELL_DROP: + playerPed = FindPlayerPed(); + if (playerPed) { + switch (playerPed->m_nSurfaceTouched) { + case SURFACE_GRASS: + case SURFACE_GRAVEL: + case SURFACE_MUD_DRY: + case SURFACE_TRANSPARENT_CLOTH: + case SURFACE_PED: + case SURFACE_SAND: + case SURFACE_RUBBER: + case SURFACE_HEDGE: + case SURFACE_SAND_BEACH: + m_sQueueSample.m_nSampleIndex = SFX_BULLET_SHELL_HIT_GROUND_2; + m_sQueueSample.m_nFrequency = RandomDisplacement(600) + 10600; + m_sQueueSample.m_nPriority = 18; + break; + case SURFACE_WATER: + return; + default: + m_sQueueSample.m_nSampleIndex = SFX_BULLET_SHELL_HIT_GROUND_1; + m_sQueueSample.m_nFrequency = RandomDisplacement(1500) + 30000; + m_sQueueSample.m_nPriority = 15; + break; + } + } else { + m_sQueueSample.m_nSampleIndex = SFX_BULLET_SHELL_HIT_GROUND_1; + m_sQueueSample.m_nFrequency = RandomDisplacement(1500) + 30000; + m_sQueueSample.m_nPriority = 15; + } + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_GUNSHELL_MAX_DIST; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_bIs2D = FALSE; + Vol = m_anRandomTable[2] % 20 + SCRIPT_OBJECT_GUNSHELL_VOLUME; + break; + case SCRIPT_SOUND_GUNSHELL_DROP_SOFT: + m_sQueueSample.m_nSampleIndex = SFX_BULLET_SHELL_HIT_GROUND_2; + m_sQueueSample.m_nFrequency = RandomDisplacement(500) + 11000; + m_sQueueSample.m_nPriority = 18; + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_GUNSHELL_MAX_DIST; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_bIs2D = FALSE; + Vol = m_anRandomTable[2] % 20 + SCRIPT_OBJECT_GUNSHELL_VOLUME; + break; + case SCRIPT_SOUND_SHOOTING_RANGE_TARGET_HIT: + case SCRIPT_SOUND_BULLET_HIT_GROUND_1: + case SCRIPT_SOUND_BULLET_HIT_GROUND_2: + case SCRIPT_SOUND_BULLET_HIT_GROUND_3: + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_BULLET_HIT_GROUND_MAX_DIST; + m_sQueueSample.m_nSampleIndex = m_anRandomTable[iSound % 5] % 3 + SFX_BULLET_WALL_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.m_nPriority = 9; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_bIs2D = FALSE; + Vol = m_anRandomTable[2] % 20 + SCRIPT_OBJECT_BULLET_HIT_GROUND_VOLUME; + break; + case SCRIPT_SOUND_PAYPHONE_RINGING: + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_PAYPHONE_RINGING_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_PHONE_RING; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + Vol = SCRIPT_OBJECT_PAYPHONE_RINGING_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_PHONE_RING); + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_bIs2D = FALSE; + SET_SOUND_REFLECTION(FALSE); + break; + case SCRIPT_SOUND_GLASS_BREAK_L: + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_GLASS_BREAK_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_GLASS_SMASH; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + Vol = SCRIPT_OBJECT_GLASS_BREAK_LONG_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_GLASS_SMASH); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_bIs2D = FALSE; + break; + case SCRIPT_SOUND_GLASS_BREAK_S: + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_GLASS_BREAK_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_GLASS_SMASH; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + Vol = SCRIPT_OBJECT_GLASS_BREAK_SHORT_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_GLASS_SMASH); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_bIs2D = FALSE; + break; + case SCRIPT_SOUND_GLASS_CRACK: + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_GLASS_BREAK_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_GLASS_CRACK; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + Vol = SCRIPT_OBJECT_GLASS_BREAK_LONG_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_GLASS_CRACK); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_bIs2D = FALSE; + SET_SOUND_REFLECTION(TRUE); + break; + case SCRIPT_SOUND_GLASS_LIGHT_BREAK: + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_GLASS_LIGHT_BREAK_MAX_DIST; + m_sQueueSample.m_nSampleIndex = (m_anRandomTable[4] & 3) + SFX_GLASS_SHARD_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = RandomDisplacement(2000) + 19000; + m_sQueueSample.m_nPriority = 9; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_bIs2D = FALSE; + Vol = RandomDisplacement(11) + SCRIPT_OBJECT_GLASS_LIGHT_BREAK_VOLUME; + break; + case SCRIPT_SOUND_MALE_AMBULANCE_OUCH: + { + cPedParams pedParams; + pedParams.m_fDistance = GetDistanceSquared(m_sQueueSample.m_vecPos); + SetupPedComments(pedParams, SOUND_INJURED_PED_MALE_OUCH); + return; + } + case SCRIPT_SOUND_FEMALE_AMBULANCE_OUCH: + { + cPedParams pedParams; + pedParams.m_fDistance = GetDistanceSquared(m_sQueueSample.m_vecPos); + SetupPedComments(pedParams, SOUND_INJURED_PED_FEMALE); + return; + } + case SCRIPT_SOUND_POLICE_CELL_DOOR_CLUNK: + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_POLICE_CELL_DOOR_CLUNK_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_COL_GATE; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = 10600; + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.m_nPriority = 3; + Vol = SCRIPT_OBJECT_POLICE_CELL_DOOR_CLUNK_VOLUME; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_bIs2D = FALSE; + SET_SOUND_REFLECTION(TRUE); + break; + case SCRIPT_SOUND_GARAGE_DOOR_CLUNK: + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_GARAGE_DOOR_CLUNK_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_COL_CAR_PANEL_2; // huh? + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = 22000; + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + m_sQueueSample.m_nPriority = 4; + Vol = SCRIPT_OBJECT_GARAGE_DOOR_CLUNK_VOLUME; + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_bIs2D = FALSE; + SET_SOUND_REFLECTION(TRUE); + break; + default: + return; + } + + distSquared = GetDistanceSquared(m_sQueueSample.m_vecPos); + if (distSquared < SQR(m_sQueueSample.m_MaxDistance)) { + m_sQueueSample.m_fDistance = Sqrt(distSquared); + m_sQueueSample.m_nVolume = ComputeVolume(Vol, m_sQueueSample.m_MaxDistance, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = iSound++; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_bStatic = TRUE; + SET_EMITTING_VOLUME(Vol); + RESET_LOOP_OFFSETS + SET_SOUND_REVERB(TRUE); + AddSampleToRequestedQueue(); + } + } +} + +void +cAudioManager::ProcessLoopingScriptObject(uint8 sound) +{ + uint8 Vol; + float distSquared; + + switch(sound) { + case SCRIPT_SOUND_NEW_BUILDING_BAR_1: + m_sQueueSample.m_nSampleIndex = SFX_BUILDING_BAR_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_BAR_1; + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_LONG_MAX_DIST; + Vol = SCRIPT_OBJECT_DEFAULT_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nFramesToPlay = 15; + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_bIs2D = FALSE; + break; + case SCRIPT_SOUND_NEW_BUILDING_BAR_2: + m_sQueueSample.m_nSampleIndex = SFX_BUILDING_BAR_2; + m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_BAR_2; + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_LONG_MAX_DIST; + Vol = SCRIPT_OBJECT_DEFAULT_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nFramesToPlay = 15; + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_bIs2D = FALSE; + break; + case SCRIPT_SOUND_NEW_BUILDING_BAR_3: + m_sQueueSample.m_nSampleIndex = SFX_BUILDING_BAR_3; + m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_BAR_3; + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_LONG_MAX_DIST; + Vol = SCRIPT_OBJECT_DEFAULT_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nFramesToPlay = 15; + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_bIs2D = FALSE; + break; + case SCRIPT_SOUND_NEW_BUILDING_BAR_4: + m_sQueueSample.m_nSampleIndex = SFX_BUILDING_BAR_4; + m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_BAR_4; + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_LONG_MAX_DIST; + Vol = SCRIPT_OBJECT_DEFAULT_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nFramesToPlay = 15; + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_bIs2D = FALSE; + break; + case SCRIPT_SOUND_NEW_BUILDING_MALIBU_1: + if(MusicManager.m_nPlayingTrack == STREAMED_SOUND_MALIBU_AMBIENT) return; + m_sQueueSample.m_nSampleIndex = SFX_BUILDING_MALIBU_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_MALIBU_1; + MusicManager.SetMalibuClubTrackPos(SCRIPT_SOUND_NEW_BUILDING_MALIBU_1); + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_LONG_MAX_DIST; + Vol = SCRIPT_OBJECT_DEFAULT_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nFramesToPlay = 15; + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_bIs2D = FALSE; + break; + case SCRIPT_SOUND_NEW_BUILDING_MALIBU_2: + if(MusicManager.m_nPlayingTrack == STREAMED_SOUND_MALIBU_AMBIENT) return; + m_sQueueSample.m_nSampleIndex = SFX_BUILDING_MALIBU_2; + m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_MALIBU_2; + MusicManager.SetMalibuClubTrackPos(SCRIPT_SOUND_NEW_BUILDING_MALIBU_2); + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_LONG_MAX_DIST; + Vol = SCRIPT_OBJECT_DEFAULT_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nFramesToPlay = 15; + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_bIs2D = FALSE; + break; + case SCRIPT_SOUND_NEW_BUILDING_MALIBU_3: + if(MusicManager.m_nPlayingTrack == STREAMED_SOUND_MALIBU_AMBIENT) return; + m_sQueueSample.m_nSampleIndex = SFX_BUILDING_MALIBU_3; + m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_MALIBU_3; + MusicManager.SetMalibuClubTrackPos(SCRIPT_SOUND_NEW_BUILDING_MALIBU_3); + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_LONG_MAX_DIST; + Vol = SCRIPT_OBJECT_DEFAULT_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nFramesToPlay = 15; + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_bIs2D = FALSE; + break; + case SCRIPT_SOUND_NEW_BUILDING_STRIP_1: + if(MusicManager.m_nPlayingTrack == STREAMED_SOUND_STRIPCLUB_AMBIENT) return; + m_sQueueSample.m_nSampleIndex = SFX_BUILDING_STRIP_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_STRIP_1; + MusicManager.SetStripClubTrackPos(SCRIPT_SOUND_NEW_BUILDING_STRIP_1); + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_LONG_MAX_DIST; + Vol = SCRIPT_OBJECT_DEFAULT_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nFramesToPlay = 15; + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_bIs2D = FALSE; + break; + case SCRIPT_SOUND_NEW_BUILDING_STRIP_2: + if(MusicManager.m_nPlayingTrack == STREAMED_SOUND_STRIPCLUB_AMBIENT) return; + m_sQueueSample.m_nSampleIndex = SFX_BUILDING_STRIP_2; + m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_STRIP_2; + MusicManager.SetStripClubTrackPos(SCRIPT_SOUND_NEW_BUILDING_STRIP_2); + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_LONG_MAX_DIST; + Vol = SCRIPT_OBJECT_DEFAULT_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nFramesToPlay = 15; + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_bIs2D = FALSE; + break; + case SCRIPT_SOUND_NEW_BUILDING_STRIP_3: + if(MusicManager.m_nPlayingTrack == STREAMED_SOUND_STRIPCLUB_AMBIENT) return; + m_sQueueSample.m_nSampleIndex = SFX_BUILDING_STRIP_3; + m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_STRIP_3; + MusicManager.SetStripClubTrackPos(SCRIPT_SOUND_NEW_BUILDING_STRIP_3); + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_LONG_MAX_DIST; + Vol = SCRIPT_OBJECT_DEFAULT_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nFramesToPlay = 15; + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_bIs2D = FALSE; + break; + case SCRIPT_SOUND_NEW_BUILDING_CHURCH: + m_sQueueSample.m_nSampleIndex = SFX_BUILDING_CHURCH; + m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_CHURCH; + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_LONG_MAX_DIST; + Vol = SCRIPT_OBJECT_DEFAULT_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nFramesToPlay = 15; + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_bIs2D = FALSE; + break; + case SCRIPT_SOUND_SNORING_LOOP: + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_SNORING_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_BUILDING_SNORE; + m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_SNORING; + Vol = SCRIPT_OBJECT_SNORING_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_BUILDING_SNORE); + m_sQueueSample.m_nPriority = 6; + m_sQueueSample.m_nFramesToPlay = 3; + m_sQueueSample.m_fSpeedMultiplier = 3.0f; + m_sQueueSample.m_bIs2D = FALSE; + break; + case SCRIPT_SOUND_BANK_ALARM_LOOP: + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_LONG_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_BUILDINGS_BANK_ALARM; + m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_BANK_ALARM; + Vol = SCRIPT_OBJECT_BANK_ALARM_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_BUILDINGS_BANK_ALARM); + m_sQueueSample.m_nPriority = 2; + m_sQueueSample.m_nFramesToPlay = 3; + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_bIs2D = FALSE; + break; + case SCRIPT_SOUND_POLICE_CELL_DOOR_SLIDING_LOOP: + case SCRIPT_SOUND_GARAGE_DOOR_SLIDING_LOOP: + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_LONG_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_GARAGE_DOOR_LOOP; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + Vol = SCRIPT_OBJECT_GARAGE_DOOR_SLIDING_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_GARAGE_DOOR_LOOP); + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_nFramesToPlay = 3; + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_bIs2D = FALSE; + break; + case SCRIPT_SOUND_SHOOTING_RANGE_TARGET_MOVING_LOOP: + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_SHOOTING_RANGE_TARGET_MOVING_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_TANK_TURRET; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + Vol = SCRIPT_OBJECT_SHOOTING_RANGE_TARGET_MOVING_VOLUME; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_TANK_TURRET); + m_sQueueSample.m_nPriority = 4; + m_sQueueSample.m_nFramesToPlay = 3; + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_bIs2D = FALSE; + break; + case SCRIPT_SOUND_NEW_WATERFALL: + Vol = SCRIPT_OBJECT_NEW_WATERFALL_VOLUME; + m_sQueueSample.m_MaxDistance = SCRIPT_OBJECT_LONG_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_BOAT_WATER_LOOP; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = 20812; + m_sQueueSample.m_nPriority = 4; + m_sQueueSample.m_nFramesToPlay = 9; + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_bIs2D = FALSE; + break; + default: return; + } + + distSquared = GetDistanceSquared(m_sQueueSample.m_vecPos); + if(distSquared < SQR(m_sQueueSample.m_MaxDistance)) { + m_sQueueSample.m_fDistance = Sqrt(distSquared); + m_sQueueSample.m_nVolume = ComputeVolume(Vol, m_sQueueSample.m_MaxDistance, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nLoopCount = 0; + m_sQueueSample.m_bStatic = FALSE; + SET_SOUND_REVERB(TRUE); + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } +} +#pragma endregion All the code for script object audio on the map + +void +cAudioManager::ProcessWeather(int32 id) +{ + uint8 Vol; + float x; + float y; + float modifier; + float wind; + + static uint8 iSound = 0; + + if (m_asAudioEntities[id].m_AudioEvents > 0 && m_asAudioEntities[id].m_awAudioEvent[0] == SOUND_LIGHTNING) { + if (m_asAudioEntities[id].m_afVolume[0] < 10) { + m_sQueueSample.m_nSampleIndex = SFX_EXPLOSION_2; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = RandomDisplacement(500) + 4000; + Vol = (m_asAudioEntities[id].m_afVolume[0] * 10.0f * 0.1f); + Vol += 35; + } else { + m_sQueueSample.m_nSampleIndex = SFX_EXPLOSION_1; + m_sQueueSample.m_nBankIndex = SFX_BANK_GENERIC_EXTRA; + m_sQueueSample.m_nFrequency = RandomDisplacement(500) + 4000; + Vol = ((m_asAudioEntities[id].m_afVolume[0] - 10.0f) * 10.0f * 0.1f); + Vol += 40; + } + m_sQueueSample.m_nVolume = Vol; + if (TheCamera.SoundDistUp < 20.0f) + m_sQueueSample.m_nVolume >>= 1; + if (iSound == 4) + iSound = 0; + m_sQueueSample.m_nCounter = iSound++; + m_sQueueSample.m_nPriority = 0; + m_sQueueSample.m_nPan = (m_anRandomTable[2] % 16) + 55; + m_sQueueSample.m_bIs2D = TRUE; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_bStatic = TRUE; + SET_EMITTING_VOLUME(m_sQueueSample.m_nVolume); + RESET_LOOP_OFFSETS + SET_SOUND_REVERB(FALSE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + if (CWeather::Rain > 0.0f && (!CCullZones::CamNoRain() || !CCullZones::PlayerNoRain())) { + m_sQueueSample.m_nSampleIndex = SFX_RAIN; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_RAIN); + m_sQueueSample.m_nVolume = (uint8)(25.0f * CWeather::Rain); + m_sQueueSample.m_nCounter = 4; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nPriority = 0; + m_sQueueSample.m_nPan = 63; + m_sQueueSample.m_bIs2D = TRUE; + m_sQueueSample.m_nLoopCount = 0; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 30; + SET_SOUND_REVERB(FALSE); + SET_EMITTING_VOLUME(m_sQueueSample.m_nVolume); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + x = 0.0f; + y = 0.0f; + CWindModifiers::FindWindModifier(TheCamera.GetPosition(), &x, &y); + modifier = Max(Abs(x), Abs(y)) * 10.0f; + modifier = Min(1.0f, modifier); + wind = Max(CWeather::Wind, modifier); + if (wind > 0.0f && CObject::fDistToNearestTree < 75.0f) { + m_sQueueSample.m_nSampleIndex = SFX_PALM_TREE_LO; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_PALM_TREE_LO); + m_sQueueSample.m_nVolume = (m_anRandomTable[1] % 10 + 45.0f) * (75.0f - CObject::fDistToNearestTree) / 75.0f * wind; + m_sQueueSample.m_nCounter = 5; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_nPan = 63; + m_sQueueSample.m_bIs2D = TRUE; + m_sQueueSample.m_nLoopCount = 0; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 7; + SET_SOUND_REVERB(FALSE); + SET_EMITTING_VOLUME(m_sQueueSample.m_nVolume); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + CObject::fDistToNearestTree = 999999.9f; + } +} + +void +cAudioManager::ProcessFrontEnd() +{ + bool8 stereo; + bool8 processedPickup; + bool8 processedMission; + bool8 staticFreq; + bool8 center; + int16 sample; + + static uint8 iSound = 0; + static uint32 cPickupNextFrame = 0; + static uint32 cPartMisComNextFrame = 0; + static uint32 radioDial = SFX_RADIO_DIAL_1; + + for (uint32 i = 0; i < m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_AudioEvents; i++) { + staticFreq = FALSE; + processedPickup = FALSE; + center = FALSE; + processedMission = FALSE; + stereo = FALSE; + switch (m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i]) { + case SOUND_WEAPON_SNIPER_SHOT_NO_ZOOM: + m_sQueueSample.m_nSampleIndex = SFX_ERROR_FIRE_RIFLE; + break; + case SOUND_WEAPON_ROCKET_SHOT_NO_ZOOM: + m_sQueueSample.m_nSampleIndex = SFX_ERROR_FIRE_ROCKET_LAUNCHER; + break; + case SOUND_GARAGE_NO_MONEY: + case SOUND_GARAGE_BAD_VEHICLE: + case SOUND_GARAGE_BOMB_ALREADY_SET: + m_sQueueSample.m_nSampleIndex = SFX_WEAPON_LEFT; + stereo = TRUE; + staticFreq = TRUE; + center = TRUE; + break; + case SOUND_GARAGE_OPENING: + case SOUND_71: //case SOUND_41: + case SOUND_GARAGE_VEHICLE_DECLINED: + case SOUND_GARAGE_VEHICLE_ACCEPTED: + case SOUND_EVIDENCE_PICKUP: + case SOUND_UNLOAD_GOLD: + stereo = TRUE; + processedPickup = TRUE; + m_sQueueSample.m_nSampleIndex = SFX_MONEY_LEFT; + break; + case SOUND_GARAGE_BOMB1_SET: + case SOUND_GARAGE_BOMB2_SET: + case SOUND_GARAGE_BOMB3_SET: + case SOUND_PICKUP_WEAPON_BOUGHT: + case SOUND_PICKUP_WEAPON: + center = TRUE; + processedPickup = TRUE; + m_sQueueSample.m_nSampleIndex = SFX_WEAPON_LEFT; + stereo = TRUE; + break; + case SOUND_PICKUP_HEALTH: + case SOUND_81: //case SOUND_4B: + case SOUND_PICKUP_ADRENALINE: + case SOUND_PICKUP_ARMOUR: + stereo = TRUE; + processedPickup = TRUE; + m_sQueueSample.m_nSampleIndex = SFX_MONEY_LEFT; + break; + case SOUND_80: + stereo = TRUE; + processedPickup = TRUE; + m_sQueueSample.m_nSampleIndex = SFX_WEAPON_LEFT; + center = TRUE; + staticFreq = TRUE; + break; + case SOUND_PICKUP_BONUS: + case SOUND_FRONTEND_MENU_STARTING: + case SOUND_HUD: + stereo = TRUE; + m_sQueueSample.m_nSampleIndex = SFX_INFO_LEFT; + center = TRUE; + break; + case SOUND_PICKUP_MONEY: + stereo = TRUE; + processedPickup = TRUE; + m_sQueueSample.m_nSampleIndex = SFX_MONEY_LEFT; + break; + case SOUND_PICKUP_HIDDEN_PACKAGE: + case SOUND_PICKUP_PACMAN_PILL: + case SOUND_PICKUP_PACMAN_PACKAGE: + case SOUND_PICKUP_FLOAT_PACKAGE: + center = TRUE; + processedPickup = TRUE; + m_sQueueSample.m_nSampleIndex = SFX_PART_MISSION_COMPLETE_LEFT; + stereo = TRUE; + break; + case SOUND_RACE_START_3: + case SOUND_RACE_START_2: + case SOUND_RACE_START_1: + case SOUND_PART_MISSION_COMPLETE: + stereo = TRUE; + m_sQueueSample.m_nSampleIndex = SFX_PART_MISSION_COMPLETE_LEFT; + processedMission = TRUE; + center = TRUE; + break; + case SOUND_RACE_START_GO: + stereo = TRUE; + m_sQueueSample.m_nSampleIndex = SFX_GO_LEFT; + center = TRUE; + break; + case SOUND_CLOCK_TICK: + m_sQueueSample.m_nSampleIndex = SFX_TIMER; + break; + case SOUND_FRONTEND_RADIO_TURN_OFF: + case SOUND_FRONTEND_RADIO_TURN_ON: + m_sQueueSample.m_nSampleIndex = SFX_RADIO_CLICK; + break; + case SOUND_FRONTEND_HURRICANE: + m_sQueueSample.m_nSampleIndex = SFX_HURRICANE_MA; + break; + case SOUND_BULLETTRACE_1: + case SOUND_BULLETTRACE_2: + m_sQueueSample.m_nSampleIndex = (m_anRandomTable[0] % 2) + SFX_BULLET_PASS_1; + break; + case SOUND_AMMUNATION_IMRAN_ARM_BOMB: + m_sQueueSample.m_nSampleIndex = SFX_ARM_BOMB; + break; + case SOUND_RADIO_CHANGE: + m_sQueueSample.m_nSampleIndex = (m_anRandomTable[1] % 2) ? radioDial + 1 : radioDial + 2; + if (m_sQueueSample.m_nSampleIndex > SFX_RADIO_DIAL_12) + m_sQueueSample.m_nSampleIndex -= 12; + radioDial = m_sQueueSample.m_nSampleIndex; + break; + case SOUND_FRONTEND_HIGHLIGHT_OPTION: + stereo = TRUE; + m_sQueueSample.m_nSampleIndex = SFX_FE_HIGHLIGHT_LEFT; + break; + case SOUND_FRONTEND_ENTER_OR_ADJUST: + stereo = TRUE; + m_sQueueSample.m_nSampleIndex = SFX_FE_SELECT_LEFT; + break; + case SOUND_FRONTEND_BACK: + stereo = TRUE; + m_sQueueSample.m_nSampleIndex = SFX_FE_BACK_LEFT; + break; + case SOUND_FRONTEND_FAIL: + stereo = TRUE; + m_sQueueSample.m_nSampleIndex = SFX_FE_ERROR_LEFT; + break; + case SOUND_FRONTEND_AUDIO_TEST: + m_sQueueSample.m_nSampleIndex = m_anRandomTable[0] % 3 + SFX_FE_NOISE_BURST_1; + break; + default: + continue; + } + + if (processedPickup) { + if (m_FrameCounter <= cPickupNextFrame) + continue; + cPickupNextFrame = m_FrameCounter + 5; + } else if (processedMission) { + if (m_FrameCounter <= cPartMisComNextFrame) + continue; + cPartMisComNextFrame = m_FrameCounter + 5; + } + + sample = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i]; + + if (sample == SOUND_FRONTEND_RADIO_TURN_OFF) + m_sQueueSample.m_nFrequency = 28509; + else if (sample == SOUND_FRONTEND_RADIO_TURN_ON) + m_sQueueSample.m_nFrequency = 32000; + else if (sample == SOUND_BULLETTRACE_1 || sample == SOUND_BULLETTRACE_2) { + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + } else if (staticFreq) + m_sQueueSample.m_nFrequency = 5382; + else + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + + m_sQueueSample.m_nVolume = FRONTEND_VOLUME; + if (m_sQueueSample.m_nSampleIndex == SFX_HURRICANE_MA && CWeather::Wind > 1.0f) + m_sQueueSample.m_nVolume = (CWeather::Wind - 1.0f) * m_sQueueSample.m_nVolume; + m_sQueueSample.m_nCounter = iSound++; + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_bStatic = TRUE; + m_sQueueSample.m_nBankIndex = SFX_BANK_FRONT_END_MENU; + m_sQueueSample.m_nPriority = 0; + m_sQueueSample.m_bIs2D = TRUE; + SET_EMITTING_VOLUME(m_sQueueSample.m_nVolume); + RESET_LOOP_OFFSETS + if (stereo) { + m_sQueueSample.m_nPan = 0; + m_sQueueSample.m_fDistance = 1.0f; + } else { + sample = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i]; + if (sample == SOUND_BULLETTRACE_1) { + m_sQueueSample.m_nPan = 20; + m_sQueueSample.m_nVolume = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; + m_sQueueSample.m_nPriority = 10; + m_sQueueSample.m_fDistance = 100.0f; + } else if (sample == SOUND_BULLETTRACE_2) { + m_sQueueSample.m_nPan = 107; + m_sQueueSample.m_nVolume = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; + m_sQueueSample.m_nPriority = 10; + m_sQueueSample.m_fDistance = 100.0f; + } else { + m_sQueueSample.m_nPan = 63; + m_sQueueSample.m_fDistance = 1.0f; + } + } + SET_SOUND_REVERB(FALSE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + if (stereo) { + m_sQueueSample.m_nSampleIndex++; + m_sQueueSample.m_nCounter = iSound++; + m_sQueueSample.m_nPan = 127 - m_sQueueSample.m_nPan; + AddSampleToRequestedQueue(); + } + if (center) { + m_sQueueSample.m_nSampleIndex++; + m_sQueueSample.m_nCounter = iSound++; + m_sQueueSample.m_nPan = 63; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + AddSampleToRequestedQueue(); + } + } +} + +/*void +cAudioManager::ProcessCrane() +{ + CCrane *crane = (CCrane *)m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_pEntity; + float distSquared; + bool8 distCalculated = FALSE; + + if (crane) { + if (crane->m_nCraneStatus == CCrane::ACTIVATED) { + if (crane->m_nCraneState != CCrane::IDLE) { + m_sQueueSample.m_vecPos = crane->m_pCraneEntity->GetPosition(); + distSquared = GetDistanceSquared(m_sQueueSample.m_vecPos); + if (distSquared < SQR(CRANE_MAX_DIST)) { + CalculateDistance(distCalculated, distSquared); + m_sQueueSample.m_nVolume = ComputeVolume(CRANE_VOLUME, CRANE_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 0; + m_sQueueSample.m_nSampleIndex = SFX_CRANE_MAGNET; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 2; + m_sQueueSample.m_nFrequency = 6000; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(CRANE_VOLUME); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_MaxDistance = CRANE_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 3; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + if (m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_AudioEvents > 0) { + m_sQueueSample.m_nCounter = 1; + m_sQueueSample.m_nSampleIndex = SFX_COL_CAR_2; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_COL_CAR_2); + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(TRUE); + AddSampleToRequestedQueue(); + } + } + } + } + } +}*/ + +void +cAudioManager::ProcessProjectiles() +{ + uint8 Vol; + + for (uint8 i = 0; i < NUM_PROJECTILES; i++) { + if (CProjectileInfo::GetProjectileInfo(i)->m_bInUse) { + switch (CProjectileInfo::GetProjectileInfo(i)->m_eWeaponType) { + case WEAPONTYPE_TEARGAS: + Vol = PROJECTILE_TEARGAS_VOLUME; + m_sQueueSample.m_MaxDistance = PROJECTILE_TEARGAS_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_PALM_TREE_LO; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = 13879; + m_sQueueSample.m_nPriority = 7; + break; + case WEAPONTYPE_ROCKET: + Vol = PROJECTILE_ROCKET_VOLUME; + m_sQueueSample.m_MaxDistance = PROJECTILE_ROCKET_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_ROCKET_FLY; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_ROCKET_FLY); + m_sQueueSample.m_nPriority = 3; + break; + case WEAPONTYPE_MOLOTOV: + Vol = PROJECTILE_MOLOTOV_VOLUME; + m_sQueueSample.m_MaxDistance = PROJECTILE_MOLOTOV_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_PED_ON_FIRE; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = 32 * SampleManager.GetSampleBaseFrequency(SFX_PED_ON_FIRE) / 25; + m_sQueueSample.m_nPriority = 7; + break; + default: + continue; + } + m_sQueueSample.m_fSpeedMultiplier = 4.0f; + m_sQueueSample.m_nFramesToPlay = 3; + m_sQueueSample.m_vecPos = CProjectileInfo::ms_apProjectile[i]->GetPosition(); + float distSquared = GetDistanceSquared(m_sQueueSample.m_vecPos); + if (distSquared < SQR(m_sQueueSample.m_MaxDistance)) { + m_sQueueSample.m_fDistance = Sqrt(distSquared); + m_sQueueSample.m_nVolume = ComputeVolume(Vol, m_sQueueSample.m_MaxDistance, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = i; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(Vol); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_bStatic = FALSE; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + } + } +} + +void +cAudioManager::ProcessEscalators() +{ + float distance; + + for (int i = 0; i < CEscalators::NumEscalators; i++) { + if (!CEscalators::GetEscalator(i).IsActive()) + continue; + m_sQueueSample.m_vecPos = CEscalators::GetEscalator(i).GetPosition(); + distance = GetDistanceSquared(m_sQueueSample.m_vecPos); + if (distance < SQR(ESCALATOR_MAX_DIST)) { + m_sQueueSample.m_fDistance = Sqrt(distance); + m_sQueueSample.m_nVolume = ComputeVolume(ESCALATOR_VOLUME, ESCALATOR_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nSampleIndex = SFX_BOAT_V12_LOOP; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = i * 50 % 250 + 3973; + m_sQueueSample.m_nPriority = 3; + m_sQueueSample.m_fSpeedMultiplier = 3.0f; + m_sQueueSample.m_nFramesToPlay = 5; + m_sQueueSample.m_MaxDistance = ESCALATOR_MAX_DIST; + m_sQueueSample.m_nCounter = i; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(ESCALATOR_VOLUME); + SET_LOOP_OFFSETS(SFX_BOAT_V12_LOOP) + SET_SOUND_REVERB(TRUE); + m_sQueueSample.m_bStatic = FALSE; + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + } +} + +//positon of arcade machines +CVector aVecExtraSoundPosition[] = { CVector(-1042.546f, 88.794f, 11.324f), CVector(-1004.476f, 181.697f, 11.324f) }; + +void +cAudioManager::ProcessExtraSounds() +{ + float distance; + + for (int i = 0; i < ARRAY_SIZE(aVecExtraSoundPosition); i++) { + m_sQueueSample.m_vecPos = aVecExtraSoundPosition[i]; + distance = GetDistanceSquared(m_sQueueSample.m_vecPos); + if (distance < SQR(ARCADE_MAX_DIST)) { + m_sQueueSample.m_fDistance = Sqrt(distance); + m_sQueueSample.m_nVolume = ComputeVolume(ARCADE_VOLUME, ARCADE_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = i; + m_sQueueSample.m_nSampleIndex = SFX_ARCADE; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_ARCADE); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nLoopCount = 0; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nPriority = 4; + m_sQueueSample.m_fSpeedMultiplier = 3.0f; + SET_EMITTING_VOLUME(ARCADE_VOLUME); + SET_LOOP_OFFSETS(SFX_ARCADE) + SET_SOUND_REVERB(TRUE); + m_sQueueSample.m_MaxDistance = ARCADE_MAX_DIST; + SET_SOUND_REFLECTION(FALSE); + m_sQueueSample.m_nFramesToPlay = 3; + AddSampleToRequestedQueue(); + } + } + } +} + +void +cAudioManager::ProcessGarages() +{ + CEntity *entity; + uint8 state; + uint32 sampleIndex; + uint8 j; + float distSquared; + bool8 distCalculated; + + static uint8 iSound = 32; + +#ifdef FIX_BUGS + for (uint32 i = 0; i < CGarages::NumGarages; i++) { +#else + for (uint8 i = 0; i < CGarages::NumGarages; i++) { +#endif + if (CGarages::aGarages[i].m_eGarageType == GARAGE_NONE) + continue; + entity = CGarages::aGarages[i].m_pDoor1; + if (entity == nil) + continue; + m_sQueueSample.m_vecPos = entity->GetPosition(); + distCalculated = FALSE; + distSquared = GetDistanceSquared(m_sQueueSample.m_vecPos); + if (distSquared < SQR(GARAGES_MAX_DIST)) { + state = CGarages::aGarages[i].m_eGarageState; + if (state == GS_OPENING || state == GS_CLOSING || state == GS_AFTERDROPOFF) { + CalculateDistance(distCalculated, distSquared); + m_sQueueSample.m_nVolume = ComputeVolume(GARAGES_VOLUME, GARAGES_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + if (CGarages::aGarages[i].m_eGarageType == GARAGE_CRUSHER) { + if (CGarages::aGarages[i].m_eGarageState == GS_AFTERDROPOFF) { + if (m_FrameCounter & 1) { + if (m_anRandomTable[1] & 1) + sampleIndex = m_anRandomTable[2] % 5 + SFX_COL_CAR_1; + else + sampleIndex = m_anRandomTable[2] % 6 + SFX_COL_CAR_PANEL_1; + m_sQueueSample.m_nSampleIndex = sampleIndex; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex) >> 1; + m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency >> 4); + m_sQueueSample.m_nLoopCount = 1; + m_sQueueSample.m_bStatic = TRUE; + m_sQueueSample.m_nCounter = iSound++; + if (iSound < 32) + iSound = 32; + } else + goto CheckGarageEvents; // premature exit to go straight to the for loop + } else { + m_sQueueSample.m_nSampleIndex = SFX_FISHING_BOAT_IDLE; + m_sQueueSample.m_nFrequency = 6543; + + m_sQueueSample.m_nCounter = i; + m_sQueueSample.m_nLoopCount = 0; + m_sQueueSample.m_nFramesToPlay = 3; + m_sQueueSample.m_bStatic = FALSE; + } + } else { + m_sQueueSample.m_nSampleIndex = SFX_GARAGE_DOOR_LOOP; + m_sQueueSample.m_nFrequency = 13961; + + m_sQueueSample.m_nCounter = i; + m_sQueueSample.m_nLoopCount = 0; + m_sQueueSample.m_nFramesToPlay = 3; + m_sQueueSample.m_bStatic = FALSE; + } + + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 3; + SET_EMITTING_VOLUME(GARAGES_VOLUME); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_MaxDistance = GARAGES_MAX_DIST; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } + } +CheckGarageEvents: + for (j = 0; j < m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_AudioEvents; j++) { + switch (m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[j]) { + case SOUND_GARAGE_DOOR_CLOSED: + case SOUND_GARAGE_DOOR_OPENED: + if (distSquared < SQR(GARAGES_MAX_DIST)) { + CalculateDistance(distCalculated, distSquared); + m_sQueueSample.m_nVolume = ComputeVolume(GARAGES_DOOR_VOLUME, GARAGES_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + if (CGarages::aGarages[i].m_eGarageType == GARAGE_CRUSHER) { + m_sQueueSample.m_nSampleIndex = SFX_COL_CAR_PANEL_2; + m_sQueueSample.m_nFrequency = 6735; + } else if (m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[j] == SOUND_GARAGE_DOOR_OPENED) { + m_sQueueSample.m_nSampleIndex = SFX_COL_CAR_PANEL_2; + m_sQueueSample.m_nFrequency = 22000; + } else { + m_sQueueSample.m_nSampleIndex = SFX_COL_GARAGE_DOOR_1; + m_sQueueSample.m_nFrequency = 18000; + } + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_nPriority = 4; + SET_EMITTING_VOLUME(GARAGES_DOOR_VOLUME); + m_sQueueSample.m_fSpeedMultiplier = 0.0f; + m_sQueueSample.m_MaxDistance = GARAGES_MAX_DIST; + SET_SOUND_REVERB(TRUE); + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_bStatic = TRUE; + m_sQueueSample.m_nLoopCount = 1; + RESET_LOOP_OFFSETS + m_sQueueSample.m_nCounter = iSound++; + if (iSound < 32) + iSound = 32; + SET_SOUND_REFLECTION(TRUE); + AddSampleToRequestedQueue(); + } + } + break; + default: + break; + } + } + } +} + +void +cAudioManager::ProcessFireHydrant() +{ + float distSquared; + bool8 distCalculated = FALSE; + + m_sQueueSample.m_vecPos = ((CParticleObject*)m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_pEntity)->GetPosition(); + distSquared = GetDistanceSquared(m_sQueueSample.m_vecPos); + if (distSquared < SQR(FIRE_HYDRANT_MAX_DIST)) { + CalculateDistance(distCalculated, distSquared); + m_sQueueSample.m_nVolume = ComputeVolume(FIRE_HYDRANT_VOLUME, FIRE_HYDRANT_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 0; + m_sQueueSample.m_nSampleIndex = SFX_JUMBO_TAXI; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 4; + m_sQueueSample.m_nFrequency = 15591; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(FIRE_HYDRANT_VOLUME); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_MaxDistance = FIRE_HYDRANT_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 3; + SET_SOUND_REVERB(TRUE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } +} +#ifdef GTA_BRIDGE +#pragma region BRIDGE +void +cAudioManager::ProcessBridge() +{ + float dist; + bool8 distCalculated = FALSE; + + if (CBridge::pLiftRoad) { + m_sQueueSample.m_vecPos = CBridge::pLiftRoad->GetPosition(); + dist = GetDistanceSquared(m_sQueueSample.m_vecPos); + if (dist < SQR(BRIDGE_MAX_DIST)) { + CalculateDistance(distCalculated, dist); + switch (CBridge::State) { + case STATE_BRIDGE_LOCKED: + case STATE_LIFT_PART_IS_UP: + case STATE_LIFT_PART_ABOUT_TO_MOVE_UP: + ProcessBridgeWarning(); + break; + case STATE_LIFT_PART_MOVING_DOWN: + case STATE_LIFT_PART_MOVING_UP: + ProcessBridgeWarning(); + ProcessBridgeMotor(); + break; + default: + break; + } + ProcessBridgeOneShots(); + } + } +} + +void +cAudioManager::ProcessBridgeWarning() +{ + if (!CStats::CommercialPassed) + return; + + if (m_sQueueSample.m_fDistance < BRIDGE_MAX_DIST) { + m_sQueueSample.m_nVolume = ComputeVolume(BRIDGE_WARNING_VOLUME, BRIDGE_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 0; + m_sQueueSample.m_nSampleIndex = SFX_BRIDGE_OPEN_WARNING; + m_sQueueSample.m_nBankIndex = SFX_BANK_GENERIC_EXTRA; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_BRIDGE_OPEN_WARNING); + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(BRIDGE_WARNING_VOLUME); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_MaxDistance = BRIDGE_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 8; + SET_SOUND_REVERB(FALSE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } +} + +void +cAudioManager::ProcessBridgeMotor() +{ + if (m_sQueueSample.m_fDistance < BRIDGE_MOTOR_MAX_DIST) { + m_sQueueSample.m_nVolume = ComputeVolume(BRIDGE_MOTOR_VOLUME, BRIDGE_MOTOR_MAX_DIST, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 1; + m_sQueueSample.m_nSampleIndex = SFX_FISHING_BOAT_IDLE; // todo check sfx name + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_nFrequency = 5500; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(BRIDGE_MOTOR_VOLUME); + SET_LOOP_OFFSETS(m_sQueueSample.m_nSampleIndex) + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_MaxDistance = BRIDGE_MOTOR_MAX_DIST; + m_sQueueSample.m_bStatic = FALSE; + m_sQueueSample.m_nFramesToPlay = 3; + SET_SOUND_REVERB(FALSE); + AddSampleToRequestedQueue(); + } + } +} + +void +cAudioManager::ProcessBridgeOneShots() +{ + float maxDist; + + if (CBridge::State == STATE_LIFT_PART_IS_UP && CBridge::OldState == STATE_LIFT_PART_MOVING_UP) { + maxDist = BRIDGE_MOTOR_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_COL_CONTAINER_1; + } else if (CBridge::State == STATE_LIFT_PART_IS_DOWN && CBridge::OldState == STATE_LIFT_PART_MOVING_DOWN) { + maxDist = BRIDGE_MOTOR_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_COL_CONTAINER_1; + } else if (CBridge::State == STATE_LIFT_PART_MOVING_UP && CBridge::OldState == STATE_LIFT_PART_ABOUT_TO_MOVE_UP) { + maxDist = BRIDGE_MOTOR_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_COL_CONTAINER_1; + } else if (CBridge::State == STATE_LIFT_PART_MOVING_DOWN && CBridge::OldState == STATE_LIFT_PART_IS_UP) { + maxDist = BRIDGE_MOTOR_MAX_DIST; + m_sQueueSample.m_nSampleIndex = SFX_COL_CONTAINER_1; + } else return; + + if (m_sQueueSample.m_fDistance < maxDist) { + m_sQueueSample.m_nVolume = ComputeVolume(BRIDGE_MOTOR_VOLUME, maxDist, m_sQueueSample.m_fDistance); + if (m_sQueueSample.m_nVolume > 0) { + m_sQueueSample.m_nCounter = 2; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = FALSE; + m_sQueueSample.m_nPriority = 1; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); + m_sQueueSample.m_nLoopCount = 1; + SET_EMITTING_VOLUME(BRIDGE_MOTOR_VOLUME); + RESET_LOOP_OFFSETS + m_sQueueSample.m_fSpeedMultiplier = 2.0f; + m_sQueueSample.m_MaxDistance = maxDist; + m_sQueueSample.m_bStatic = TRUE; + SET_SOUND_REVERB(FALSE); + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); + } + } +} +#pragma endregion +#endif + +#pragma region MISSION_AUDIO +bool8 g_bMissionAudioLoadFailed[MISSION_AUDIO_SLOTS]; + +struct MissionAudioData { + const char *m_pName; + uint32 m_nId; +}; + +Const MissionAudioData MissionAudioNameSfxAssoc[] = { + {"mobring", STREAMED_SOUND_MISSION_MOBR1}, {"pagring", STREAMED_SOUND_MISSION_PAGER}, {"carrev", STREAMED_SOUND_MISSION_CARREV}, + {"bikerev", STREAMED_SOUND_MISSION_BIKEREV}, {"liftop", STREAMED_SOUND_MISSION_LIFTOP}, {"liftcl", STREAMED_SOUND_MISSION_LIFTCL}, + {"liftrun", STREAMED_SOUND_MISSION_LIFTRUN}, {"liftbel", STREAMED_SOUND_MISSION_LIFTBEL}, {"inlift", STREAMED_SOUND_MISSION_INLIFT}, + {"caml", STREAMED_SOUND_MISSION_CAMERAL}, {"camr", STREAMED_SOUND_MISSION_CAMERAR}, {"cheer1", STREAMED_SOUND_MISSION_CHEER1}, + {"cheer2", STREAMED_SOUND_MISSION_CHEER2}, {"cheer3", STREAMED_SOUND_MISSION_CHEER3}, {"cheer4", STREAMED_SOUND_MISSION_CHEER4}, + {"ooh1", STREAMED_SOUND_MISSION_OOH1}, {"ooh2", STREAMED_SOUND_MISSION_OOH2}, {"race1", STREAMED_SOUND_MISSION_RACE1}, + {"race2", STREAMED_SOUND_MISSION_RACE2}, {"race3", STREAMED_SOUND_MISSION_RACE3}, {"race4", STREAMED_SOUND_MISSION_RACE4}, + {"race5", STREAMED_SOUND_MISSION_RACE5}, {"race6", STREAMED_SOUND_MISSION_RACE6}, {"race7", STREAMED_SOUND_MISSION_RACE7}, + {"race8", STREAMED_SOUND_MISSION_RACE8}, {"race9", STREAMED_SOUND_MISSION_RACE9}, {"race10", STREAMED_SOUND_MISSION_RACE10}, + {"race11", STREAMED_SOUND_MISSION_RACE11}, {"race12", STREAMED_SOUND_MISSION_RACE12}, {"race13", STREAMED_SOUND_MISSION_RACE13}, + {"race14", STREAMED_SOUND_MISSION_RACE14}, {"race15", STREAMED_SOUND_MISSION_RACE15}, {"hot1", STREAMED_SOUND_MISSION_HOT1}, + {"hot2", STREAMED_SOUND_MISSION_HOT2}, {"hot3", STREAMED_SOUND_MISSION_HOT3}, {"hot4", STREAMED_SOUND_MISSION_HOT4}, + {"hot5", STREAMED_SOUND_MISSION_HOT5}, {"hot6", STREAMED_SOUND_MISSION_HOT6}, {"hot7", STREAMED_SOUND_MISSION_HOT7}, + {"hot8", STREAMED_SOUND_MISSION_HOT8}, {"hot9", STREAMED_SOUND_MISSION_HOT9}, {"hot10", STREAMED_SOUND_MISSION_HOT10}, + {"hot11", STREAMED_SOUND_MISSION_HOT11}, {"hot12", STREAMED_SOUND_MISSION_HOT12}, {"hot13", STREAMED_SOUND_MISSION_HOT13}, + {"hot14", STREAMED_SOUND_MISSION_HOT14}, {"hot15", STREAMED_SOUND_MISSION_HOT15}, {"lanstp1", STREAMED_SOUND_MISSION_LANSTP1}, + {"lanstp2", STREAMED_SOUND_MISSION_LANSTP2}, {"lanamu1", STREAMED_SOUND_MISSION_LANAMU1}, {"lanamu2", STREAMED_SOUND_MISSION_LANAMU2}, + {"airhrnl", STREAMED_SOUND_MISSION_AIRHORNL}, {"airhrnr", STREAMED_SOUND_MISSION_AIRHORNR}, {"sniper", STREAMED_SOUND_MISSION_SNIPSCRL}, + {"snipsh", STREAMED_SOUND_MISSION_SNIPSHORT}, {"bloroof", STREAMED_SOUND_MISSION_BLOWROOF}, {"sfx_01", STREAMED_SOUND_MISSION_SFX_01}, + {"sfx_02", STREAMED_SOUND_MISSION_SFX_02}, {"LAW1_1", STREAMED_SOUND_MISSION_LAW1_1}, {"LAW1_2", STREAMED_SOUND_MISSION_LAW1_2}, + {"LAW1_3", STREAMED_SOUND_MISSION_LAW1_3}, {"LAW1_4", STREAMED_SOUND_MISSION_LAW1_4}, {"LAW1_5", STREAMED_SOUND_MISSION_LAW1_5}, + {"LAW1_6", STREAMED_SOUND_MISSION_LAW1_6}, {"LAW1_7", STREAMED_SOUND_MISSION_LAW1_7}, {"LAW1_8", STREAMED_SOUND_MISSION_LAW1_8}, + {"LAW1_9", STREAMED_SOUND_MISSION_LAW1_9}, {"LAW1_10", STREAMED_SOUND_MISSION_LAW1_10}, {"LAW2_1", STREAMED_SOUND_MISSION_LAW2_1}, + {"LAW2_2", STREAMED_SOUND_MISSION_LAW2_2}, {"LAW2_3", STREAMED_SOUND_MISSION_LAW2_3}, {"LAW2_4", STREAMED_SOUND_MISSION_LAW2_4}, + {"LAW2_5", STREAMED_SOUND_MISSION_LAW2_5}, {"LAW2_6", STREAMED_SOUND_MISSION_LAW2_6}, {"LAW2_7", STREAMED_SOUND_MISSION_LAW2_7}, + {"LAW2_8", STREAMED_SOUND_MISSION_LAW2_8}, {"LAW2_9", STREAMED_SOUND_MISSION_LAW2_9}, {"LAW2_10", STREAMED_SOUND_MISSION_LAW2_10}, + {"LAW3_1", STREAMED_SOUND_MISSION_LAW3_1}, {"LAW3_2", STREAMED_SOUND_MISSION_LAW3_2}, {"LAW3_3", STREAMED_SOUND_MISSION_LAW3_3}, + {"LAW3_4", STREAMED_SOUND_MISSION_LAW3_4}, {"LAW3_5", STREAMED_SOUND_MISSION_LAW3_5}, {"LAW3_6", STREAMED_SOUND_MISSION_LAW3_6}, + {"LAW3_10", STREAMED_SOUND_MISSION_LAW3_10}, {"LAW3_11", STREAMED_SOUND_MISSION_LAW3_11}, {"LAW3_12", STREAMED_SOUND_MISSION_LAW3_12}, + {"LAW3_13", STREAMED_SOUND_MISSION_LAW3_13}, {"LAW3_14", STREAMED_SOUND_MISSION_LAW3_14}, {"LAW3_16", STREAMED_SOUND_MISSION_LAW3_16}, + {"LAW3_17", STREAMED_SOUND_MISSION_LAW3_17}, {"LAW3_18", STREAMED_SOUND_MISSION_LAW3_18}, {"LAW3_19", STREAMED_SOUND_MISSION_LAW3_19}, + {"LAW3_20", STREAMED_SOUND_MISSION_LAW3_20}, {"LAW3_21", STREAMED_SOUND_MISSION_LAW3_21}, {"LAW3_22", STREAMED_SOUND_MISSION_LAW3_22}, + {"LAW3_23", STREAMED_SOUND_MISSION_LAW3_23}, {"LAW3_24", STREAMED_SOUND_MISSION_LAW3_24}, {"LAW3_25", STREAMED_SOUND_MISSION_LAW3_25}, + {"LAW4_1a", STREAMED_SOUND_MISSION_LAW4_1A}, {"LAW4_1b", STREAMED_SOUND_MISSION_LAW4_1B}, {"LAW4_1c", STREAMED_SOUND_MISSION_LAW4_1C}, + {"LAW4_1d", STREAMED_SOUND_MISSION_LAW4_1D}, {"LAW4_10", STREAMED_SOUND_MISSION_LAW4_10}, {"LAW4_3", STREAMED_SOUND_MISSION_LAW4_3}, + {"LAW4_4", STREAMED_SOUND_MISSION_LAW4_4}, {"LAW4_5", STREAMED_SOUND_MISSION_LAW4_5}, {"LAW4_6", STREAMED_SOUND_MISSION_LAW4_6}, + {"LAW4_7", STREAMED_SOUND_MISSION_LAW4_7}, {"LAW4_8", STREAMED_SOUND_MISSION_LAW4_8}, {"LAW4_9", STREAMED_SOUND_MISSION_LAW4_9}, + {"COL1_1", STREAMED_SOUND_MISSION_COL1_1}, {"COL1_2", STREAMED_SOUND_MISSION_COL1_2}, {"COL1_3", STREAMED_SOUND_MISSION_COL1_3}, + {"COL1_4", STREAMED_SOUND_MISSION_COL1_4}, {"COL1_5", STREAMED_SOUND_MISSION_COL1_5}, {"COL1_6", STREAMED_SOUND_MISSION_COL1_6}, + {"COL1_7", STREAMED_SOUND_MISSION_COL1_7}, {"COL1_8", STREAMED_SOUND_MISSION_COL1_8}, {"COL2_1", STREAMED_SOUND_MISSION_COL2_1}, + {"COL2_2", STREAMED_SOUND_MISSION_COL2_2}, {"COL2_3", STREAMED_SOUND_MISSION_COL2_3}, {"COL2_4", STREAMED_SOUND_MISSION_COL2_4}, + {"COL2_5", STREAMED_SOUND_MISSION_COL2_5}, {"COL2_6a", STREAMED_SOUND_MISSION_COL2_6A}, {"COL2_7", STREAMED_SOUND_MISSION_COL2_7}, + {"COL2_8", STREAMED_SOUND_MISSION_COL2_8}, {"COL2_9", STREAMED_SOUND_MISSION_COL2_9}, {"COL2_10", STREAMED_SOUND_MISSION_COL2_10}, + {"COL2_11", STREAMED_SOUND_MISSION_COL2_11}, {"COL2_12", STREAMED_SOUND_MISSION_COL2_12}, {"COL2_13", STREAMED_SOUND_MISSION_COL2_13}, + {"COL2_14", STREAMED_SOUND_MISSION_COL2_14}, {"COL2_15", STREAMED_SOUND_MISSION_COL2_15}, {"COL2_16", STREAMED_SOUND_MISSION_COL2_16}, + {"COL3_1", STREAMED_SOUND_MISSION_COL3_1}, {"COL3_2", STREAMED_SOUND_MISSION_COL3_2}, {"COL3_2a", STREAMED_SOUND_MISSION_COL3_2A}, + {"COL3_2b", STREAMED_SOUND_MISSION_COL3_2B}, {"COL3_3", STREAMED_SOUND_MISSION_COL3_3}, {"COL3_4", STREAMED_SOUND_MISSION_COL3_4}, + {"COL3_5", STREAMED_SOUND_MISSION_COL3_5}, {"COL3_6", STREAMED_SOUND_MISSION_COL3_6}, {"COL3_7", STREAMED_SOUND_MISSION_COL3_7}, + {"COL3_8", STREAMED_SOUND_MISSION_COL3_8}, {"COL3_9", STREAMED_SOUND_MISSION_COL3_9}, {"COL3_10", STREAMED_SOUND_MISSION_COL3_10}, + {"COL3_11", STREAMED_SOUND_MISSION_COL3_11}, {"COL3_12", STREAMED_SOUND_MISSION_COL3_12}, {"COL3_13", STREAMED_SOUND_MISSION_COL3_13}, + {"COL3_14", STREAMED_SOUND_MISSION_COL3_14}, {"COL3_15", STREAMED_SOUND_MISSION_COL3_15}, {"COL3_16", STREAMED_SOUND_MISSION_COL3_16}, + {"COL3_17", STREAMED_SOUND_MISSION_COL3_17}, {"COL3_18", STREAMED_SOUND_MISSION_COL3_18}, {"COL3_19", STREAMED_SOUND_MISSION_COL3_19}, + {"COL3_20", STREAMED_SOUND_MISSION_COL3_20}, {"COL3_21", STREAMED_SOUND_MISSION_COL3_21}, {"COL3_23", STREAMED_SOUND_MISSION_COL3_23}, + {"COL3_24", STREAMED_SOUND_MISSION_COL3_24}, {"COL3_25", STREAMED_SOUND_MISSION_COL3_25}, {"COL4_1", STREAMED_SOUND_MISSION_COL4_1}, + {"COL4_2", STREAMED_SOUND_MISSION_COL4_2}, {"COL4_3", STREAMED_SOUND_MISSION_COL4_3}, {"COL4_4", STREAMED_SOUND_MISSION_COL4_4}, + {"COL4_5", STREAMED_SOUND_MISSION_COL4_5}, {"COL4_6", STREAMED_SOUND_MISSION_COL4_6}, {"COL4_7", STREAMED_SOUND_MISSION_COL4_7}, + {"COL4_8", STREAMED_SOUND_MISSION_COL4_8}, {"COL4_9", STREAMED_SOUND_MISSION_COL4_9}, {"COL4_10", STREAMED_SOUND_MISSION_COL4_10}, + {"COL4_11", STREAMED_SOUND_MISSION_COL4_11}, {"COL4_12", STREAMED_SOUND_MISSION_COL4_12}, {"COL4_13", STREAMED_SOUND_MISSION_COL4_13}, + {"COL4_14", STREAMED_SOUND_MISSION_COL4_14}, {"COL4_15", STREAMED_SOUND_MISSION_COL4_15}, {"COL4_16", STREAMED_SOUND_MISSION_COL4_16}, + {"COL4_17", STREAMED_SOUND_MISSION_COL4_17}, {"COL4_18", STREAMED_SOUND_MISSION_COL4_18}, {"COL4_19", STREAMED_SOUND_MISSION_COL4_19}, + {"COL4_20", STREAMED_SOUND_MISSION_COL4_20}, {"COL4_21", STREAMED_SOUND_MISSION_COL4_21}, {"COL4_22", STREAMED_SOUND_MISSION_COL4_22}, + {"COL4_23", STREAMED_SOUND_MISSION_COL4_23}, {"COL4_24", STREAMED_SOUND_MISSION_COL4_24}, {"COL4_25", STREAMED_SOUND_MISSION_COL4_25}, + {"COL4_26", STREAMED_SOUND_MISSION_COL4_26}, {"COL5_1", STREAMED_SOUND_MISSION_COL5_1}, {"COL5_2", STREAMED_SOUND_MISSION_COL5_2}, + {"COL5_3", STREAMED_SOUND_MISSION_COL5_3}, {"COL5_4", STREAMED_SOUND_MISSION_COL5_4}, {"COL5_5", STREAMED_SOUND_MISSION_COL5_5}, + {"COL5_6", STREAMED_SOUND_MISSION_COL5_6}, {"COL5_7", STREAMED_SOUND_MISSION_COL5_7}, {"COL5_8", STREAMED_SOUND_MISSION_COL5_8}, + {"COL5_9", STREAMED_SOUND_MISSION_COL5_9}, {"COL5_10", STREAMED_SOUND_MISSION_COL5_10}, {"COL5_11", STREAMED_SOUND_MISSION_COL5_11}, + {"COL5_12", STREAMED_SOUND_MISSION_COL5_12}, {"COL5_13", STREAMED_SOUND_MISSION_COL5_13}, {"COL5_14", STREAMED_SOUND_MISSION_COL5_14}, + {"COL5_15", STREAMED_SOUND_MISSION_COL5_15}, {"COL5_16", STREAMED_SOUND_MISSION_COL5_16}, {"COL5_17", STREAMED_SOUND_MISSION_COL5_17}, + {"COL5_18", STREAMED_SOUND_MISSION_COL5_18}, {"COL5_19", STREAMED_SOUND_MISSION_COL5_19}, {"COL5_20", STREAMED_SOUND_MISSION_COL5_20}, + {"COL5_21", STREAMED_SOUND_MISSION_COL5_21}, {"COL5_22", STREAMED_SOUND_MISSION_COL5_22}, {"COK1_1", STREAMED_SOUND_MISSION_COK1_1}, + {"COK1_2", STREAMED_SOUND_MISSION_COK1_2}, {"COK1_3", STREAMED_SOUND_MISSION_COK1_3}, {"COK1_4", STREAMED_SOUND_MISSION_COK1_4}, + {"COK1_5", STREAMED_SOUND_MISSION_COK1_5}, {"COK1_6", STREAMED_SOUND_MISSION_COK1_6}, {"COK2_1", STREAMED_SOUND_MISSION_COK2_1}, + {"COK2_2", STREAMED_SOUND_MISSION_COK2_2}, {"COK2_3", STREAMED_SOUND_MISSION_COK2_3}, {"COK2_4", STREAMED_SOUND_MISSION_COK2_4}, + {"COK2_5", STREAMED_SOUND_MISSION_COK2_5}, {"COK2_6", STREAMED_SOUND_MISSION_COK2_6}, {"COK2_7a", STREAMED_SOUND_MISSION_COK2_7A}, + {"COK2_7b", STREAMED_SOUND_MISSION_COK2_7B}, {"COK2_7c", STREAMED_SOUND_MISSION_COK2_7C}, {"COK2_8a", STREAMED_SOUND_MISSION_COK2_8A}, + {"COK2_8b", STREAMED_SOUND_MISSION_COK2_8B}, {"COK2_8c", STREAMED_SOUND_MISSION_COK2_8C}, {"COK2_8d", STREAMED_SOUND_MISSION_COK2_8D}, + {"COK2_9", STREAMED_SOUND_MISSION_COK2_9}, {"COK210a", STREAMED_SOUND_MISSION_COK210A}, {"COK210b", STREAMED_SOUND_MISSION_COK210B}, + {"COK210c", STREAMED_SOUND_MISSION_COK210C}, {"COK212a", STREAMED_SOUND_MISSION_COK212A}, {"COK212b", STREAMED_SOUND_MISSION_COK212B}, + {"COK2_13", STREAMED_SOUND_MISSION_COK2_13}, {"COK2_14", STREAMED_SOUND_MISSION_COK2_14}, {"COK2_15", STREAMED_SOUND_MISSION_COK2_15}, + {"COK2_16", STREAMED_SOUND_MISSION_COK2_16}, {"COK2_20", STREAMED_SOUND_MISSION_COK2_20}, {"COK2_21", STREAMED_SOUND_MISSION_COK2_21}, + {"COK2_22", STREAMED_SOUND_MISSION_COK2_22}, {"COK3_1", STREAMED_SOUND_MISSION_COK3_1}, {"COK3_2", STREAMED_SOUND_MISSION_COK3_2}, + {"COK3_3", STREAMED_SOUND_MISSION_COK3_3}, {"COK3_4", STREAMED_SOUND_MISSION_COK3_4}, {"COK4_1", STREAMED_SOUND_MISSION_COK4_1}, + {"COK4_2", STREAMED_SOUND_MISSION_COK4_2}, {"COK4_3", STREAMED_SOUND_MISSION_COK4_3}, {"COK4_4", STREAMED_SOUND_MISSION_COK4_4}, + {"COK4_5", STREAMED_SOUND_MISSION_COK4_5}, {"COK4_6", STREAMED_SOUND_MISSION_COK4_6}, {"COK4_7", STREAMED_SOUND_MISSION_COK4_7}, + {"COK4_8", STREAMED_SOUND_MISSION_COK4_8}, {"COK4_9", STREAMED_SOUND_MISSION_COK4_9}, {"COK4_9A", STREAMED_SOUND_MISSION_COK4_9A}, + {"COK4_10", STREAMED_SOUND_MISSION_COK4_10}, {"COK4_11", STREAMED_SOUND_MISSION_COK4_11}, {"COK4_12", STREAMED_SOUND_MISSION_COK4_12}, + {"COK4_13", STREAMED_SOUND_MISSION_COK4_13}, {"COK4_14", STREAMED_SOUND_MISSION_COK4_14}, {"COK4_15", STREAMED_SOUND_MISSION_COK4_15}, + {"COK4_16", STREAMED_SOUND_MISSION_COK4_16}, {"COK4_17", STREAMED_SOUND_MISSION_COK4_17}, {"COK4_18", STREAMED_SOUND_MISSION_COK4_18}, + {"COK4_19", STREAMED_SOUND_MISSION_COK4_19}, {"COK4_20", STREAMED_SOUND_MISSION_COK4_20}, {"COK4_21", STREAMED_SOUND_MISSION_COK4_21}, + {"COK4_22", STREAMED_SOUND_MISSION_COK4_22}, {"COK4_23", STREAMED_SOUND_MISSION_COK4_23}, {"COK4_24", STREAMED_SOUND_MISSION_COK4_24}, + {"COK4_25", STREAMED_SOUND_MISSION_COK4_25}, {"COK4_26", STREAMED_SOUND_MISSION_COK4_26}, {"COK4_27", STREAMED_SOUND_MISSION_COK4_27}, + {"RESC_1", STREAMED_SOUND_MISSION_RESC_1}, {"RESC_2", STREAMED_SOUND_MISSION_RESC_2}, {"RESC_3", STREAMED_SOUND_MISSION_RESC_3}, + {"RESC_4", STREAMED_SOUND_MISSION_RESC_4}, {"RESC_5", STREAMED_SOUND_MISSION_RESC_5}, {"RESC_6", STREAMED_SOUND_MISSION_RESC_6}, + {"RESC_7", STREAMED_SOUND_MISSION_RESC_7}, {"RESC_8", STREAMED_SOUND_MISSION_RESC_8}, {"RESC_9", STREAMED_SOUND_MISSION_RESC_9}, + {"RESC_10", STREAMED_SOUND_MISSION_RESC_10}, {"ASS_1", STREAMED_SOUND_MISSION_ASS_1}, {"ASS_2", STREAMED_SOUND_MISSION_ASS_2}, + {"ASS_3", STREAMED_SOUND_MISSION_ASS_3}, {"ASS_4", STREAMED_SOUND_MISSION_ASS_4}, {"ASS_5", STREAMED_SOUND_MISSION_ASS_5}, + {"ASS_6", STREAMED_SOUND_MISSION_ASS_6}, {"ASS_7", STREAMED_SOUND_MISSION_ASS_7}, {"ASS_8", STREAMED_SOUND_MISSION_ASS_8}, + {"ASS_9", STREAMED_SOUND_MISSION_ASS_9}, {"ASS_10", STREAMED_SOUND_MISSION_ASS_10}, {"ASS_11", STREAMED_SOUND_MISSION_ASS_11}, + {"ASS_12", STREAMED_SOUND_MISSION_ASS_12}, {"ASS_13", STREAMED_SOUND_MISSION_ASS_13}, {"ASS_14", STREAMED_SOUND_MISSION_ASS_14}, + {"BUD1_1", STREAMED_SOUND_MISSION_BUD1_1}, {"BUD1_2", STREAMED_SOUND_MISSION_BUD1_2}, {"BUD1_3", STREAMED_SOUND_MISSION_BUD1_3}, + {"BUD1_4", STREAMED_SOUND_MISSION_BUD1_4}, {"BUD1_5", STREAMED_SOUND_MISSION_BUD1_5}, {"BUD1_9", STREAMED_SOUND_MISSION_BUD1_9}, + {"BUD1_10", STREAMED_SOUND_MISSION_BUD1_10}, {"BUD2_1", STREAMED_SOUND_MISSION_BUD2_1}, {"BUD2_2", STREAMED_SOUND_MISSION_BUD2_2}, + {"BUD2_3", STREAMED_SOUND_MISSION_BUD2_3}, {"BUD2_4", STREAMED_SOUND_MISSION_BUD2_4}, {"BUD2_5", STREAMED_SOUND_MISSION_BUD2_5}, + {"BUD2_6", STREAMED_SOUND_MISSION_BUD2_6}, {"BUD2_7", STREAMED_SOUND_MISSION_BUD2_7}, {"BUD3_1a", STREAMED_SOUND_MISSION_BUD3_1A}, + {"BUD3_1b", STREAMED_SOUND_MISSION_BUD3_1B}, {"BUD3_1", STREAMED_SOUND_MISSION_BUD3_1}, {"BUD3_2", STREAMED_SOUND_MISSION_BUD3_2}, + {"BUD3_3", STREAMED_SOUND_MISSION_BUD3_3}, {"BUD3_4", STREAMED_SOUND_MISSION_BUD3_4}, {"BUD3_1c", STREAMED_SOUND_MISSION_BUD3_1C}, + {"BUD3_5", STREAMED_SOUND_MISSION_BUD3_5}, {"BUD3_6", STREAMED_SOUND_MISSION_BUD3_6}, {"BUD3_7", STREAMED_SOUND_MISSION_BUD3_7}, + {"BUD3_8a", STREAMED_SOUND_MISSION_BUD3_8A}, {"BUD3_8b", STREAMED_SOUND_MISSION_BUD3_8B}, {"BUD3_8c", STREAMED_SOUND_MISSION_BUD3_8C}, + {"BUD3_9a", STREAMED_SOUND_MISSION_BUD3_9A}, {"BUD3_9b", STREAMED_SOUND_MISSION_BUD3_9B}, {"BUD3_9c", STREAMED_SOUND_MISSION_BUD3_9C}, + {"CAP1_2", STREAMED_SOUND_MISSION_CAP1_2}, {"CAP1_3", STREAMED_SOUND_MISSION_CAP1_3}, {"CAP1_4", STREAMED_SOUND_MISSION_CAP1_4}, + {"CAP1_5", STREAMED_SOUND_MISSION_CAP1_5}, {"CAP1_6", STREAMED_SOUND_MISSION_CAP1_6}, {"CAP1_7", STREAMED_SOUND_MISSION_CAP1_7}, + {"CAP1_8", STREAMED_SOUND_MISSION_CAP1_8}, {"CAP1_9", STREAMED_SOUND_MISSION_CAP1_9}, {"CAP1_10", STREAMED_SOUND_MISSION_CAP1_10}, + {"CAP1_11", STREAMED_SOUND_MISSION_CAP1_11}, {"CAP1_12", STREAMED_SOUND_MISSION_CAP1_12}, {"FINKILL", STREAMED_SOUND_MISSION_FINKILL}, + {"FIN_1a", STREAMED_SOUND_MISSION_FIN_1A}, {"FIN_1b", STREAMED_SOUND_MISSION_FIN_1B}, {"FIN_1c", STREAMED_SOUND_MISSION_FIN_1C}, + {"FIN_2b", STREAMED_SOUND_MISSION_FIN_2B}, {"FIN_2c", STREAMED_SOUND_MISSION_FIN_2C}, {"FIN_3", STREAMED_SOUND_MISSION_FIN_3}, + {"FIN_4", STREAMED_SOUND_MISSION_FIN_4}, {"FIN_5", STREAMED_SOUND_MISSION_FIN_5}, {"FIN_6", STREAMED_SOUND_MISSION_FIN_6}, + {"FIN_10", STREAMED_SOUND_MISSION_FIN_10}, {"FIN_11a", STREAMED_SOUND_MISSION_FIN_11A}, {"FIN_11b", STREAMED_SOUND_MISSION_FIN_11B}, + {"FIN_12a", STREAMED_SOUND_MISSION_FIN_12A}, {"FIN_12b", STREAMED_SOUND_MISSION_FIN_12B}, {"FIN_12c", STREAMED_SOUND_MISSION_FIN_12C}, + {"FIN_13", STREAMED_SOUND_MISSION_FIN_13}, {"BNK1_1", STREAMED_SOUND_MISSION_BNK1_1}, {"BNK1_2", STREAMED_SOUND_MISSION_BNK1_2}, + {"BNK1_3", STREAMED_SOUND_MISSION_BNK1_3}, {"BNK1_4", STREAMED_SOUND_MISSION_BNK1_4}, {"BNK1_5", STREAMED_SOUND_MISSION_BNK1_5}, + {"BNK1_6", STREAMED_SOUND_MISSION_BNK1_6}, {"BNK1_7", STREAMED_SOUND_MISSION_BNK1_7}, {"BNK1_8", STREAMED_SOUND_MISSION_BNK1_8}, + {"BNK1_10", STREAMED_SOUND_MISSION_BNK1_10}, {"BNK1_11", STREAMED_SOUND_MISSION_BNK1_11}, {"BNK1_12", STREAMED_SOUND_MISSION_BNK1_12}, + {"BNK1_13", STREAMED_SOUND_MISSION_BNK1_13}, {"BNK1_14", STREAMED_SOUND_MISSION_BNK1_14}, {"BNK2_1", STREAMED_SOUND_MISSION_BNK2_1}, + {"BNK2_2", STREAMED_SOUND_MISSION_BNK2_2}, {"BNK2_3", STREAMED_SOUND_MISSION_BNK2_3}, {"BNK2_4", STREAMED_SOUND_MISSION_BNK2_4}, + {"BNK2_5", STREAMED_SOUND_MISSION_BNK2_5}, {"BNK2_6", STREAMED_SOUND_MISSION_BNK2_6}, {"BNK2_7", STREAMED_SOUND_MISSION_BNK2_7}, + {"BNK2_8", STREAMED_SOUND_MISSION_BNK2_8}, {"BNK2_9", STREAMED_SOUND_MISSION_BNK2_9}, {"BNK3_1", STREAMED_SOUND_MISSION_BNK3_1}, + {"BNK3_2", STREAMED_SOUND_MISSION_BNK3_2}, {"BNK3_3a", STREAMED_SOUND_MISSION_BNK3_3A}, {"BNK3_3b", STREAMED_SOUND_MISSION_BNK3_3B}, + {"BNK3_3c", STREAMED_SOUND_MISSION_BNK3_3C}, {"BNK3_4a", STREAMED_SOUND_MISSION_BNK3_4A}, {"BNK3_4b", STREAMED_SOUND_MISSION_BNK3_4B}, + {"BNK3_4c", STREAMED_SOUND_MISSION_BNK3_4C}, {"BNK4_1", STREAMED_SOUND_MISSION_BNK4_1}, {"BNK4_2", STREAMED_SOUND_MISSION_BNK4_2}, + {"BNK4_3A", STREAMED_SOUND_MISSION_BNK4_3A}, {"BNK4_3B", STREAMED_SOUND_MISSION_BNK4_3B}, {"BNK4_3C", STREAMED_SOUND_MISSION_BNK4_3C}, + {"BNK4_3D", STREAMED_SOUND_MISSION_BNK4_3D}, {"BNK4_3E", STREAMED_SOUND_MISSION_BNK4_3E}, {"BNK4_3F", STREAMED_SOUND_MISSION_BNK4_3F}, + {"BNK4_3G", STREAMED_SOUND_MISSION_BNK4_3G}, {"BNK4_3H", STREAMED_SOUND_MISSION_BNK4_3H}, {"BNK4_3I", STREAMED_SOUND_MISSION_BNK4_3I}, + {"BNK4_3J", STREAMED_SOUND_MISSION_BNK4_3J}, {"BNK4_3K", STREAMED_SOUND_MISSION_BNK4_3K}, {"BNK4_3M", STREAMED_SOUND_MISSION_BNK4_3M}, + {"BNK4_3O", STREAMED_SOUND_MISSION_BNK4_3O}, {"BNK4_3P", STREAMED_SOUND_MISSION_BNK4_3P}, {"BNK4_3Q", STREAMED_SOUND_MISSION_BNK4_3Q}, + {"BNK4_3R", STREAMED_SOUND_MISSION_BNK4_3R}, {"BNK4_3S", STREAMED_SOUND_MISSION_BNK4_3S}, {"BNK4_3T", STREAMED_SOUND_MISSION_BNK4_3T}, + {"BNK4_3U", STREAMED_SOUND_MISSION_BNK4_3U}, {"BNK4_3V", STREAMED_SOUND_MISSION_BNK4_3V}, {"BNK4_4a", STREAMED_SOUND_MISSION_BNK4_4A}, + {"BNK4_4b", STREAMED_SOUND_MISSION_BNK4_4B}, {"BNK4_5", STREAMED_SOUND_MISSION_BNK4_5}, {"BNK4_6", STREAMED_SOUND_MISSION_BNK4_6}, + {"BNK4_7", STREAMED_SOUND_MISSION_BNK4_7}, {"BNK4_8", STREAMED_SOUND_MISSION_BNK4_8}, {"BNK4_9", STREAMED_SOUND_MISSION_BNK4_9}, + {"BNK4_10", STREAMED_SOUND_MISSION_BNK4_10}, {"BNK4_11", STREAMED_SOUND_MISSION_BNK4_11}, {"BK4_12a", STREAMED_SOUND_MISSION_BK4_12A}, + {"BK4_12b", STREAMED_SOUND_MISSION_BK4_12B}, {"BK4_12c", STREAMED_SOUND_MISSION_BK4_12C}, {"BNK4_13", STREAMED_SOUND_MISSION_BNK4_13}, + {"BK4_14a", STREAMED_SOUND_MISSION_BK4_14A}, {"BK4_14b", STREAMED_SOUND_MISSION_BK4_14B}, {"BNK4_15", STREAMED_SOUND_MISSION_BNK4_15}, + {"BNK4_16", STREAMED_SOUND_MISSION_BNK4_16}, {"BNK4_17", STREAMED_SOUND_MISSION_BNK4_17}, {"BNK4_18", STREAMED_SOUND_MISSION_BNK4_18}, + {"BK4_19a", STREAMED_SOUND_MISSION_BK4_19A}, {"BK4_19b", STREAMED_SOUND_MISSION_BK4_19B}, {"BK4_20a", STREAMED_SOUND_MISSION_BK4_20A}, + {"BK4_20b", STREAMED_SOUND_MISSION_BK4_20B}, {"BNK4_21", STREAMED_SOUND_MISSION_BNK4_21}, {"BNK422a", STREAMED_SOUND_MISSION_BNK422A}, + {"BNK422b", STREAMED_SOUND_MISSION_BNK422B}, {"BK4_23a", STREAMED_SOUND_MISSION_BK4_23A}, {"BK4_23b", STREAMED_SOUND_MISSION_BK4_23B}, + {"BK4_23c", STREAMED_SOUND_MISSION_BK4_23C}, {"BK4_23d", STREAMED_SOUND_MISSION_BK4_23D}, {"BK4_24a", STREAMED_SOUND_MISSION_BK4_24A}, + {"BK4_24b", STREAMED_SOUND_MISSION_BK4_24B}, {"BNK4_25", STREAMED_SOUND_MISSION_BNK4_25}, {"BNK4_26", STREAMED_SOUND_MISSION_BNK4_26}, + {"BNK4_27", STREAMED_SOUND_MISSION_BNK4_27}, {"BNK4_28", STREAMED_SOUND_MISSION_BNK4_28}, {"BNK4_29", STREAMED_SOUND_MISSION_BNK4_29}, + {"BNK4_30", STREAMED_SOUND_MISSION_BNK4_30}, {"BK4_31a", STREAMED_SOUND_MISSION_BK4_31A}, {"BK4_31b", STREAMED_SOUND_MISSION_BK4_31B}, + {"BNK4_32", STREAMED_SOUND_MISSION_BNK4_32}, {"BK4_34a", STREAMED_SOUND_MISSION_BK4_34A}, {"BK4_34b", STREAMED_SOUND_MISSION_BK4_34B}, + {"BK4_35a", STREAMED_SOUND_MISSION_BK4_35A}, {"BK4_35b", STREAMED_SOUND_MISSION_BK4_35B}, {"BNK4_36", STREAMED_SOUND_MISSION_BNK4_36}, + {"BNK4_37", STREAMED_SOUND_MISSION_BNK4_37}, {"BNK4_38", STREAMED_SOUND_MISSION_BNK4_38}, {"BNK_39", STREAMED_SOUND_MISSION_BNK4_39}, + {"BK4_40a", STREAMED_SOUND_MISSION_BK4_40A}, {"BK4_40b", STREAMED_SOUND_MISSION_BK4_40B}, {"BNK4_41", STREAMED_SOUND_MISSION_BNK4_41}, + {"BNK4_42", STREAMED_SOUND_MISSION_BNK4_42}, {"BNK4_43", STREAMED_SOUND_MISSION_BNK4_43}, {"BNK4_44", STREAMED_SOUND_MISSION_BNK4_44}, + {"BNK4_45", STREAMED_SOUND_MISSION_BNK4_45}, {"BNK4_46", STREAMED_SOUND_MISSION_BNK4_46}, {"BNK4_47", STREAMED_SOUND_MISSION_BNK4_47}, + {"BNK4_48", STREAMED_SOUND_MISSION_BNK4_48}, {"BNK4_49", STREAMED_SOUND_MISSION_BNK4_49}, {"BNK450A", STREAMED_SOUND_MISSION_BNK450A}, + {"BNK450B", STREAMED_SOUND_MISSION_BNK450B}, {"BNK4_51", STREAMED_SOUND_MISSION_BNK4_51}, {"BNK4_94", STREAMED_SOUND_MISSION_BNK4_94}, + {"BNK4_95", STREAMED_SOUND_MISSION_BNK4_95}, {"BNK4_96", STREAMED_SOUND_MISSION_BNK4_96}, {"BNK4_97", STREAMED_SOUND_MISSION_BNK4_97}, + {"BNK4_98", STREAMED_SOUND_MISSION_BNK4_98}, {"BNK4_99", STREAMED_SOUND_MISSION_BNK4_99}, {"CNT1_1", STREAMED_SOUND_MISSION_CNT1_1}, + {"CNT1_2", STREAMED_SOUND_MISSION_CNT1_2}, {"CNT1_3", STREAMED_SOUND_MISSION_CNT1_3}, {"CNT1_4", STREAMED_SOUND_MISSION_CNT1_4}, + {"CNT1_5", STREAMED_SOUND_MISSION_CNT1_5}, {"CNT2_1", STREAMED_SOUND_MISSION_CNT2_1}, {"CNT2_2", STREAMED_SOUND_MISSION_CNT2_2}, + {"CNT2_3", STREAMED_SOUND_MISSION_CNT2_3}, {"CNT2_4", STREAMED_SOUND_MISSION_CNT2_4}, {"PORN1_1", STREAMED_SOUND_MISSION_PORN1_1}, + {"PORN1_2", STREAMED_SOUND_MISSION_PORN1_2}, {"PORN1_3", STREAMED_SOUND_MISSION_PORN1_3}, {"PRN1_3A", STREAMED_SOUND_MISSION_PRN1_3A}, + {"PORN1_4", STREAMED_SOUND_MISSION_PORN1_4}, {"PORN1_5", STREAMED_SOUND_MISSION_PORN1_5}, {"PORN1_6", STREAMED_SOUND_MISSION_PORN1_6}, + {"PORN1_7", STREAMED_SOUND_MISSION_PORN1_7}, {"PORN1_8", STREAMED_SOUND_MISSION_PORN1_8}, {"PORN1_9", STREAMED_SOUND_MISSION_PORN1_9}, + {"PRN1_10", STREAMED_SOUND_MISSION_PRN1_10}, {"PRN1_11", STREAMED_SOUND_MISSION_PRN1_11}, {"PRN1_12", STREAMED_SOUND_MISSION_PRN1_12}, + {"PRN1_13", STREAMED_SOUND_MISSION_PRN1_13}, {"PRN1_14", STREAMED_SOUND_MISSION_PRN1_14}, {"PRN1_15", STREAMED_SOUND_MISSION_PRN1_15}, + {"PRN1_16", STREAMED_SOUND_MISSION_PRN1_16}, {"PRN1_17", STREAMED_SOUND_MISSION_PRN1_17}, {"PRN1_18", STREAMED_SOUND_MISSION_PRN1_18}, + {"PRN1_19", STREAMED_SOUND_MISSION_PRN1_19}, {"PRN1_20", STREAMED_SOUND_MISSION_PRN1_20}, {"PRN1_21", STREAMED_SOUND_MISSION_PRN1_21}, + {"PORN3_1", STREAMED_SOUND_MISSION_PORN3_1}, {"PORN3_2", STREAMED_SOUND_MISSION_PORN3_2}, {"PORN3_3", STREAMED_SOUND_MISSION_PORN3_3}, + {"PORN3_4", STREAMED_SOUND_MISSION_PORN3_4}, {"TAX1_1", STREAMED_SOUND_MISSION_TAX1_1}, {"TAX1_2", STREAMED_SOUND_MISSION_TAX1_2}, + {"TAX1_3", STREAMED_SOUND_MISSION_TAX1_3}, {"TAX1_4", STREAMED_SOUND_MISSION_TAX1_4}, {"TAX1_5", STREAMED_SOUND_MISSION_TAX1_5}, + {"TAX2_1", STREAMED_SOUND_MISSION_TAX2_1}, {"TAX2_2", STREAMED_SOUND_MISSION_TAX2_2}, {"TAX2_3", STREAMED_SOUND_MISSION_TAX2_3}, + {"TAX2_4", STREAMED_SOUND_MISSION_TAX2_4}, {"TAX2_5", STREAMED_SOUND_MISSION_TAX2_5}, {"TAX2_6", STREAMED_SOUND_MISSION_TAX2_6}, + {"TAX2_7", STREAMED_SOUND_MISSION_TAX2_7}, {"TAX3_1", STREAMED_SOUND_MISSION_TAX3_1}, {"TAX3_2", STREAMED_SOUND_MISSION_TAX3_2}, + {"TAX3_3", STREAMED_SOUND_MISSION_TAX3_3}, {"TAX3_4", STREAMED_SOUND_MISSION_TAX3_4}, {"TAX3_5", STREAMED_SOUND_MISSION_TAX3_5}, + {"TEX1_1", STREAMED_SOUND_MISSION_TEX1_1}, {"TEX1_2", STREAMED_SOUND_MISSION_TEX1_2}, {"TEX1_3", STREAMED_SOUND_MISSION_TEX1_3}, + {"TEX1_4", STREAMED_SOUND_MISSION_TEX1_4}, {"TEX1_5", STREAMED_SOUND_MISSION_TEX1_5}, {"TEX1_6", STREAMED_SOUND_MISSION_TEX1_6}, + {"TEX2_1", STREAMED_SOUND_MISSION_TEX2_1}, {"TEX3_1", STREAMED_SOUND_MISSION_TEX3_1}, {"TEX3_2", STREAMED_SOUND_MISSION_TEX3_2}, + {"TEX3_3", STREAMED_SOUND_MISSION_TEX3_3}, {"TEX3_4", STREAMED_SOUND_MISSION_TEX3_4}, {"TEX3_5", STREAMED_SOUND_MISSION_TEX3_5}, + {"TEX3_6", STREAMED_SOUND_MISSION_TEX3_6}, {"TEX3_7", STREAMED_SOUND_MISSION_TEX3_7}, {"TEX3_8", STREAMED_SOUND_MISSION_TEX3_8}, + {"PHIL1_2", STREAMED_SOUND_MISSION_PHIL1_2}, {"PHIL1_3", STREAMED_SOUND_MISSION_PHIL1_3}, {"PHIL2_1", STREAMED_SOUND_MISSION_PHIL2_1}, + {"PHIL2_2", STREAMED_SOUND_MISSION_PHIL2_2}, {"PHIL2_3", STREAMED_SOUND_MISSION_PHIL2_3}, {"PHIL2_4", STREAMED_SOUND_MISSION_PHIL2_4}, + {"PHIL2_5", STREAMED_SOUND_MISSION_PHIL2_5}, {"PHIL2_6", STREAMED_SOUND_MISSION_PHIL2_6}, {"PHIL2_7", STREAMED_SOUND_MISSION_PHIL2_7}, + {"PHIL2_8", STREAMED_SOUND_MISSION_PHIL2_8}, {"PHIL2_9", STREAMED_SOUND_MISSION_PHIL2_9}, {"PHIL210", STREAMED_SOUND_MISSION_PHIL210}, + {"PHIL211", STREAMED_SOUND_MISSION_PHIL211}, {"BIKE1_1", STREAMED_SOUND_MISSION_BIKE1_1}, {"BIKE1_2", STREAMED_SOUND_MISSION_BIKE1_2}, + {"BIKE1_3", STREAMED_SOUND_MISSION_BIKE1_3}, {"ROK1_1a", STREAMED_SOUND_MISSION_ROK1_1A}, {"ROK1_1b", STREAMED_SOUND_MISSION_ROK1_1B}, + {"ROK1_5", STREAMED_SOUND_MISSION_ROK1_5}, {"ROK1_6", STREAMED_SOUND_MISSION_ROK1_6}, {"ROK1_7", STREAMED_SOUND_MISSION_ROK1_7}, + {"ROK1_8", STREAMED_SOUND_MISSION_ROK1_8}, {"ROK1_9", STREAMED_SOUND_MISSION_ROK1_9}, {"PSYCH_1", STREAMED_SOUND_MISSION_PSYCH_1}, + {"PSYCH_2", STREAMED_SOUND_MISSION_PSYCH_2}, {"ROK2_01", STREAMED_SOUND_MISSION_ROK2_01}, {"ROK3_1", STREAMED_SOUND_MISSION_ROK3_1}, + {"ROK3_2", STREAMED_SOUND_MISSION_ROK3_2}, {"ROK3_3", STREAMED_SOUND_MISSION_ROK3_3}, {"ROK3_4", STREAMED_SOUND_MISSION_ROK3_4}, + {"ROK3_5", STREAMED_SOUND_MISSION_ROK3_5}, {"ROK3_6", STREAMED_SOUND_MISSION_ROK3_6}, {"ROK3_7", STREAMED_SOUND_MISSION_ROK3_7}, + {"ROK3_8", STREAMED_SOUND_MISSION_ROK3_8}, {"ROK3_9", STREAMED_SOUND_MISSION_ROK3_9}, {"ROK3_10", STREAMED_SOUND_MISSION_ROK3_10}, + {"ROK3_11", STREAMED_SOUND_MISSION_ROK3_11}, {"ROK3_12", STREAMED_SOUND_MISSION_ROK3_12}, {"ROK3_13", STREAMED_SOUND_MISSION_ROK3_13}, + {"ROK3_14", STREAMED_SOUND_MISSION_ROK3_14}, {"ROK3_15", STREAMED_SOUND_MISSION_ROK3_15}, {"ROK3_16", STREAMED_SOUND_MISSION_ROK3_16}, + {"ROK3_17", STREAMED_SOUND_MISSION_ROK3_17}, {"ROK3_18", STREAMED_SOUND_MISSION_ROK3_18}, {"ROK3_19", STREAMED_SOUND_MISSION_ROK3_19}, + {"ROK3_20", STREAMED_SOUND_MISSION_ROK3_20}, {"ROK3_21", STREAMED_SOUND_MISSION_ROK3_21}, {"ROK3_22", STREAMED_SOUND_MISSION_ROK3_22}, + {"ROK3_23", STREAMED_SOUND_MISSION_ROK3_23}, {"ROK3_24", STREAMED_SOUND_MISSION_ROK3_24}, {"ROK3_25", STREAMED_SOUND_MISSION_ROK3_25}, + {"ROK3_26", STREAMED_SOUND_MISSION_ROK3_26}, {"ROK3_27", STREAMED_SOUND_MISSION_ROK3_27}, {"ROK3_62", STREAMED_SOUND_MISSION_ROK3_62}, + {"ROK3_63", STREAMED_SOUND_MISSION_ROK3_63}, {"ROK3_64", STREAMED_SOUND_MISSION_ROK3_64}, {"ROK3_65", STREAMED_SOUND_MISSION_ROK3_65}, + {"ROK3_66", STREAMED_SOUND_MISSION_ROK3_66}, {"ROK3_67", STREAMED_SOUND_MISSION_ROK3_67}, {"ROK3_68", STREAMED_SOUND_MISSION_ROK3_68}, + {"ROK3_69", STREAMED_SOUND_MISSION_ROK3_69}, {"ROK3_70", STREAMED_SOUND_MISSION_ROK3_70}, {"ROK3_71", STREAMED_SOUND_MISSION_ROK3_71}, + {"ROK3_73", STREAMED_SOUND_MISSION_ROK3_73}, {"HAT_1a", STREAMED_SOUND_MISSION_HAT_1A}, {"intro1", STREAMED_SOUND_MISSION_INTRO1}, + {"intro2", STREAMED_SOUND_MISSION_INTRO2}, {"intro3", STREAMED_SOUND_MISSION_INTRO3}, {"intro4", STREAMED_SOUND_MISSION_INTRO4}, + {"CUB1_1", STREAMED_SOUND_MISSION_CUB1_1}, {"CUB1_2", STREAMED_SOUND_MISSION_CUB1_2}, {"CUB1_3", STREAMED_SOUND_MISSION_CUB1_3}, + {"CUB1_4", STREAMED_SOUND_MISSION_CUB1_4}, {"CUB1_5", STREAMED_SOUND_MISSION_CUB1_5}, {"CUB1_6", STREAMED_SOUND_MISSION_CUB1_6}, + {"CUB1_7", STREAMED_SOUND_MISSION_CUB1_7}, {"CUB1_8", STREAMED_SOUND_MISSION_CUB1_8}, {"CUB1_9", STREAMED_SOUND_MISSION_CUB1_9}, + {"CUB1_10", STREAMED_SOUND_MISSION_CUB1_10}, {"CUB2_1", STREAMED_SOUND_MISSION_CUB2_1}, {"CUB2_2", STREAMED_SOUND_MISSION_CUB2_2}, + {"CUB2_3a", STREAMED_SOUND_MISSION_CUB2_3A}, {"CUB2_3b", STREAMED_SOUND_MISSION_CUB2_3B}, {"CUB2_3c", STREAMED_SOUND_MISSION_CUB2_3C}, + {"CUB2_4a", STREAMED_SOUND_MISSION_CUB2_4A}, {"CUB2_5", STREAMED_SOUND_MISSION_CUB2_5}, {"CUB2_6", STREAMED_SOUND_MISSION_CUB2_6}, + {"CUB2_7", STREAMED_SOUND_MISSION_CUB2_7}, {"CUB2_8", STREAMED_SOUND_MISSION_CUB2_8}, {"CUB2_9", STREAMED_SOUND_MISSION_CUB2_9}, + {"CUB2_10", STREAMED_SOUND_MISSION_CUB2_10}, {"CUB2_11", STREAMED_SOUND_MISSION_CUB2_11}, {"CUB3_1", STREAMED_SOUND_MISSION_CUB3_1}, + {"CUB3_2", STREAMED_SOUND_MISSION_CUB3_2}, {"CUB3_3", STREAMED_SOUND_MISSION_CUB3_3}, {"CUB3_4", STREAMED_SOUND_MISSION_CUB3_4}, + {"CUB4_1", STREAMED_SOUND_MISSION_CUB4_1}, {"CUB4_2", STREAMED_SOUND_MISSION_CUB4_2}, {"CUB4_3", STREAMED_SOUND_MISSION_CUB4_3}, + {"CUB4_4", STREAMED_SOUND_MISSION_CUB4_4}, {"CUB4_5", STREAMED_SOUND_MISSION_CUB4_5}, {"CUB4_5A", STREAMED_SOUND_MISSION_CUB4_5A}, + {"CUB4_6", STREAMED_SOUND_MISSION_CUB4_6}, {"CUB4_7", STREAMED_SOUND_MISSION_CUB4_7}, {"CUB4_8", STREAMED_SOUND_MISSION_CUB4_8}, + {"CUB4_9", STREAMED_SOUND_MISSION_CUB4_9}, {"CUB4_10", STREAMED_SOUND_MISSION_CUB4_10}, {"CUB4_11", STREAMED_SOUND_MISSION_CUB4_11}, + {"CUB4_12", STREAMED_SOUND_MISSION_CUB4_12}, {"CUB4_13", STREAMED_SOUND_MISSION_CUB4_13}, {"CUB4_14", STREAMED_SOUND_MISSION_CUB4_14}, + {"CUB4_15", STREAMED_SOUND_MISSION_CUB4_15}, {"CUB4_16", STREAMED_SOUND_MISSION_CUB4_16}, {"golf_1", STREAMED_SOUND_MISSION_GOLF_1}, + {"golf_2", STREAMED_SOUND_MISSION_GOLF_2}, {"golf_3", STREAMED_SOUND_MISSION_GOLF_3}, {"bar_1", STREAMED_SOUND_MISSION_BAR_1}, + {"bar_2", STREAMED_SOUND_MISSION_BAR_2}, {"bar_3", STREAMED_SOUND_MISSION_BAR_3}, {"bar_4", STREAMED_SOUND_MISSION_BAR_4}, + {"bar_5", STREAMED_SOUND_MISSION_BAR_5}, {"bar_6", STREAMED_SOUND_MISSION_BAR_6}, {"bar_7", STREAMED_SOUND_MISSION_BAR_7}, + {"bar_8", STREAMED_SOUND_MISSION_BAR_8}, {"strip_1", STREAMED_SOUND_MISSION_STRIP_1}, {"strip_2", STREAMED_SOUND_MISSION_STRIP_2}, + {"strip_3", STREAMED_SOUND_MISSION_STRIP_3}, {"strip_4", STREAMED_SOUND_MISSION_STRIP_4}, {"strip_5", STREAMED_SOUND_MISSION_STRIP_5}, + {"strip_6", STREAMED_SOUND_MISSION_STRIP_6}, {"strip_7", STREAMED_SOUND_MISSION_STRIP_7}, {"strip_8", STREAMED_SOUND_MISSION_STRIP_8}, + {"strip_9", STREAMED_SOUND_MISSION_STRIP_9}, {"star_1", STREAMED_SOUND_MISSION_STAR_1}, {"star_2", STREAMED_SOUND_MISSION_STAR_2}, + {"star_3", STREAMED_SOUND_MISSION_STAR_3}, {"star_4", STREAMED_SOUND_MISSION_STAR_4}, {"mob_01a", STREAMED_SOUND_MISSION_MOB_01A}, + {"mob_01b", STREAMED_SOUND_MISSION_MOB_01B}, {"mob_01c", STREAMED_SOUND_MISSION_MOB_01C}, {"mob_02a", STREAMED_SOUND_MISSION_MOB_02A}, + {"mob_02b", STREAMED_SOUND_MISSION_MOB_02B}, {"mob_02c", STREAMED_SOUND_MISSION_MOB_02C}, {"mob_03a", STREAMED_SOUND_MISSION_MOB_03A}, + {"mob_03b", STREAMED_SOUND_MISSION_MOB_03B}, {"mob_03c", STREAMED_SOUND_MISSION_MOB_03C}, {"mob_03d", STREAMED_SOUND_MISSION_MOB_03D}, + {"mob_03e", STREAMED_SOUND_MISSION_MOB_03E}, {"shark_1", STREAMED_SOUND_MISSION_SHARK_1}, {"shark_2", STREAMED_SOUND_MISSION_SHARK_2}, + {"shark_3", STREAMED_SOUND_MISSION_SHARK_3}, {"shark_4", STREAMED_SOUND_MISSION_SHARK_4}, {"shark_5", STREAMED_SOUND_MISSION_SHARK_5}, + {"mob_04a", STREAMED_SOUND_MISSION_MOB_04A}, {"mob_04b", STREAMED_SOUND_MISSION_MOB_04B}, {"mob_04c", STREAMED_SOUND_MISSION_MOB_04C}, + {"mob_04d", STREAMED_SOUND_MISSION_MOB_04D}, {"mob_05a", STREAMED_SOUND_MISSION_MOB_05A}, {"mob_05b", STREAMED_SOUND_MISSION_MOB_05B}, + {"mob_05c", STREAMED_SOUND_MISSION_MOB_05C}, {"mob_05d", STREAMED_SOUND_MISSION_MOB_05D}, {"mob_06a", STREAMED_SOUND_MISSION_MOB_06A}, + {"mob_06b", STREAMED_SOUND_MISSION_MOB_06B}, {"mob_06c", STREAMED_SOUND_MISSION_MOB_06C}, {"mob_07a", STREAMED_SOUND_MISSION_MOB_07A}, + {"mob_07b", STREAMED_SOUND_MISSION_MOB_07B}, {"mob_08a", STREAMED_SOUND_MISSION_MOB_08A}, {"mob_08b", STREAMED_SOUND_MISSION_MOB_08B}, + {"mob_08c", STREAMED_SOUND_MISSION_MOB_08C}, {"mob_08d", STREAMED_SOUND_MISSION_MOB_08D}, {"mob_08e", STREAMED_SOUND_MISSION_MOB_08E}, + {"mob_08f", STREAMED_SOUND_MISSION_MOB_08F}, {"mob_08g", STREAMED_SOUND_MISSION_MOB_08G}, {"mob_09a", STREAMED_SOUND_MISSION_MOB_09A}, + {"mob_09b", STREAMED_SOUND_MISSION_MOB_09B}, {"mob_09c", STREAMED_SOUND_MISSION_MOB_09C}, {"mob_09d", STREAMED_SOUND_MISSION_MOB_09D}, + {"mob_09e", STREAMED_SOUND_MISSION_MOB_09E}, {"mob_09f", STREAMED_SOUND_MISSION_MOB_09F}, {"mob_10a", STREAMED_SOUND_MISSION_MOB_10A}, + {"mob_10b", STREAMED_SOUND_MISSION_MOB_10B}, {"mob_10c", STREAMED_SOUND_MISSION_MOB_10C}, {"mob_10d", STREAMED_SOUND_MISSION_MOB_10D}, + {"mob_10e", STREAMED_SOUND_MISSION_MOB_10E}, {"mob_11a", STREAMED_SOUND_MISSION_MOB_11A}, {"mob_11b", STREAMED_SOUND_MISSION_MOB_11B}, + {"mob_11c", STREAMED_SOUND_MISSION_MOB_11C}, {"mob_11d", STREAMED_SOUND_MISSION_MOB_11D}, {"mob_11e", STREAMED_SOUND_MISSION_MOB_11E}, + {"mob_11f", STREAMED_SOUND_MISSION_MOB_11F}, {"mob_14a", STREAMED_SOUND_MISSION_MOB_14A}, {"mob_14b", STREAMED_SOUND_MISSION_MOB_14B}, + {"mob_14c", STREAMED_SOUND_MISSION_MOB_14C}, {"mob_14d", STREAMED_SOUND_MISSION_MOB_14D}, {"mob_14e", STREAMED_SOUND_MISSION_MOB_14E}, + {"mob_14f", STREAMED_SOUND_MISSION_MOB_14F}, {"mob_14g", STREAMED_SOUND_MISSION_MOB_14G}, {"mob_14h", STREAMED_SOUND_MISSION_MOB_14H}, + {"mob_16a", STREAMED_SOUND_MISSION_MOB_16A}, {"mob_16b", STREAMED_SOUND_MISSION_MOB_16B}, {"mob_16c", STREAMED_SOUND_MISSION_MOB_16C}, + {"mob_16d", STREAMED_SOUND_MISSION_MOB_16D}, {"mob_16e", STREAMED_SOUND_MISSION_MOB_16E}, {"mob_16f", STREAMED_SOUND_MISSION_MOB_16F}, + {"mob_16g", STREAMED_SOUND_MISSION_MOB_16G}, {"mob_17a", STREAMED_SOUND_MISSION_MOB_17A}, {"mob_17b", STREAMED_SOUND_MISSION_MOB_17B}, + {"mob_17c", STREAMED_SOUND_MISSION_MOB_17C}, {"mob_17d", STREAMED_SOUND_MISSION_MOB_17D}, {"mob_17e", STREAMED_SOUND_MISSION_MOB_17E}, + {"mob_17g", STREAMED_SOUND_MISSION_MOB_17G}, {"mob_17h", STREAMED_SOUND_MISSION_MOB_17H}, {"mob_17i", STREAMED_SOUND_MISSION_MOB_17I}, + {"mob_17j", STREAMED_SOUND_MISSION_MOB_17J}, {"mob_17k", STREAMED_SOUND_MISSION_MOB_17K}, {"mob_17l", STREAMED_SOUND_MISSION_MOB_17L}, + {"mob_18a", STREAMED_SOUND_MISSION_MOB_18A}, {"mob_18b", STREAMED_SOUND_MISSION_MOB_18B}, {"mob_18c", STREAMED_SOUND_MISSION_MOB_18C}, + {"mob_18d", STREAMED_SOUND_MISSION_MOB_18D}, {"mob_18e", STREAMED_SOUND_MISSION_MOB_18E}, {"mob_18f", STREAMED_SOUND_MISSION_MOB_18F}, + {"mob_18g", STREAMED_SOUND_MISSION_MOB_18G}, {"mob_20a", STREAMED_SOUND_MISSION_MOB_20A}, {"mob_20b", STREAMED_SOUND_MISSION_MOB_20B}, + {"mob_20c", STREAMED_SOUND_MISSION_MOB_20C}, {"mob_20d", STREAMED_SOUND_MISSION_MOB_20D}, {"mob_20e", STREAMED_SOUND_MISSION_MOB_20E}, + {"mob_24a", STREAMED_SOUND_MISSION_MOB_24A}, {"mob_24b", STREAMED_SOUND_MISSION_MOB_24B}, {"mob_24c", STREAMED_SOUND_MISSION_MOB_24C}, + {"mob_24d", STREAMED_SOUND_MISSION_MOB_24D}, {"mob_24e", STREAMED_SOUND_MISSION_MOB_24E}, {"mob_24f", STREAMED_SOUND_MISSION_MOB_24F}, + {"mob_24g", STREAMED_SOUND_MISSION_MOB_24G}, {"mob_24h", STREAMED_SOUND_MISSION_MOB_24H}, {"mob_25a", STREAMED_SOUND_MISSION_MOB_25A}, + {"mob_25b", STREAMED_SOUND_MISSION_MOB_25B}, {"mob_25c", STREAMED_SOUND_MISSION_MOB_25C}, {"mob_25d", STREAMED_SOUND_MISSION_MOB_25D}, + {"mob_26a", STREAMED_SOUND_MISSION_MOB_26A}, {"mob_26b", STREAMED_SOUND_MISSION_MOB_26B}, {"mob_26c", STREAMED_SOUND_MISSION_MOB_26C}, + {"mob_26d", STREAMED_SOUND_MISSION_MOB_26D}, {"mob_26e", STREAMED_SOUND_MISSION_MOB_26E}, {"mob_29a", STREAMED_SOUND_MISSION_MOB_29A}, + {"mob_29b", STREAMED_SOUND_MISSION_MOB_29B}, {"mob_29c", STREAMED_SOUND_MISSION_MOB_29C}, {"mob_29d", STREAMED_SOUND_MISSION_MOB_29D}, + {"mob_29e", STREAMED_SOUND_MISSION_MOB_29E}, {"mob_29f", STREAMED_SOUND_MISSION_MOB_29F}, {"mob_29g", STREAMED_SOUND_MISSION_MOB_29G}, + {"mob_30a", STREAMED_SOUND_MISSION_MOB_30A}, {"mob_30b", STREAMED_SOUND_MISSION_MOB_30B}, {"mob_30c", STREAMED_SOUND_MISSION_MOB_30C}, + {"mob_30d", STREAMED_SOUND_MISSION_MOB_30D}, {"mob_30e", STREAMED_SOUND_MISSION_MOB_30E}, {"mob_30f", STREAMED_SOUND_MISSION_MOB_30F}, + {"mob_33a", STREAMED_SOUND_MISSION_MOB_33A}, {"mob_33b", STREAMED_SOUND_MISSION_MOB_33B}, {"mob_33c", STREAMED_SOUND_MISSION_MOB_33C}, + {"mob_33d", STREAMED_SOUND_MISSION_MOB_33D}, {"mob_34a", STREAMED_SOUND_MISSION_MOB_34A}, {"mob_34b", STREAMED_SOUND_MISSION_MOB_34B}, + {"mob_34c", STREAMED_SOUND_MISSION_MOB_34C}, {"mob_34d", STREAMED_SOUND_MISSION_MOB_34D}, {"mob_35a", STREAMED_SOUND_MISSION_MOB_35A}, + {"mob_35b", STREAMED_SOUND_MISSION_MOB_35B}, {"mob_35c", STREAMED_SOUND_MISSION_MOB_35C}, {"mob_35d", STREAMED_SOUND_MISSION_MOB_35D}, + {"mob_36a", STREAMED_SOUND_MISSION_MOB_36A}, {"mob_36b", STREAMED_SOUND_MISSION_MOB_36B}, {"mob_36c", STREAMED_SOUND_MISSION_MOB_36C}, + {"mob_40a", STREAMED_SOUND_MISSION_MOB_40A}, {"mob_40b", STREAMED_SOUND_MISSION_MOB_40B}, {"mob_40c", STREAMED_SOUND_MISSION_MOB_40C}, + {"mob_40d", STREAMED_SOUND_MISSION_MOB_40D}, {"mob_40e", STREAMED_SOUND_MISSION_MOB_40E}, {"mob_40f", STREAMED_SOUND_MISSION_MOB_40F}, + {"mob_40g", STREAMED_SOUND_MISSION_MOB_40G}, {"mob_40h", STREAMED_SOUND_MISSION_MOB_40H}, {"mob_40i", STREAMED_SOUND_MISSION_MOB_40I}, + {"mob_41a", STREAMED_SOUND_MISSION_MOB_41A}, {"mob_41b", STREAMED_SOUND_MISSION_MOB_41B}, {"mob_41c", STREAMED_SOUND_MISSION_MOB_41C}, + {"mob_41d", STREAMED_SOUND_MISSION_MOB_41D}, {"mob_41e", STREAMED_SOUND_MISSION_MOB_41E}, {"mob_41f", STREAMED_SOUND_MISSION_MOB_41F}, + {"mob_41g", STREAMED_SOUND_MISSION_MOB_41G}, {"mob_41h", STREAMED_SOUND_MISSION_MOB_41H}, {"mob_42a", STREAMED_SOUND_MISSION_MOB_42A}, + {"mob_42b", STREAMED_SOUND_MISSION_MOB_42B}, {"mob_42c", STREAMED_SOUND_MISSION_MOB_42C}, {"mob_42d", STREAMED_SOUND_MISSION_MOB_42D}, + {"mob_42e", STREAMED_SOUND_MISSION_MOB_42E}, {"mob_43a", STREAMED_SOUND_MISSION_MOB_43A}, {"mob_43b", STREAMED_SOUND_MISSION_MOB_43B}, + {"mob_43c", STREAMED_SOUND_MISSION_MOB_43C}, {"mob_43d", STREAMED_SOUND_MISSION_MOB_43D}, {"mob_43e", STREAMED_SOUND_MISSION_MOB_43E}, + {"mob_43f", STREAMED_SOUND_MISSION_MOB_43F}, {"mob_43g", STREAMED_SOUND_MISSION_MOB_43G}, {"mob_43h", STREAMED_SOUND_MISSION_MOB_43H}, + {"mob_45a", STREAMED_SOUND_MISSION_MOB_45A}, {"mob_45b", STREAMED_SOUND_MISSION_MOB_45B}, {"mob_45c", STREAMED_SOUND_MISSION_MOB_45C}, + {"mob_45d", STREAMED_SOUND_MISSION_MOB_45D}, {"mob_45e", STREAMED_SOUND_MISSION_MOB_45E}, {"mob_45f", STREAMED_SOUND_MISSION_MOB_45F}, + {"mob_45g", STREAMED_SOUND_MISSION_MOB_45G}, {"mob_45h", STREAMED_SOUND_MISSION_MOB_45H}, {"mob_45i", STREAMED_SOUND_MISSION_MOB_45I}, + {"mob_45j", STREAMED_SOUND_MISSION_MOB_45J}, {"mob_45k", STREAMED_SOUND_MISSION_MOB_45K}, {"mob_45l", STREAMED_SOUND_MISSION_MOB_45L}, + {"mob_45m", STREAMED_SOUND_MISSION_MOB_45M}, {"mob_45n", STREAMED_SOUND_MISSION_MOB_45N}, {"mob_46a", STREAMED_SOUND_MISSION_MOB_46A}, + {"mob_46b", STREAMED_SOUND_MISSION_MOB_46B}, {"mob_46c", STREAMED_SOUND_MISSION_MOB_46C}, {"mob_46d", STREAMED_SOUND_MISSION_MOB_46D}, + {"mob_46e", STREAMED_SOUND_MISSION_MOB_46E}, {"mob_46f", STREAMED_SOUND_MISSION_MOB_46F}, {"mob_46g", STREAMED_SOUND_MISSION_MOB_46G}, + {"mob_46h", STREAMED_SOUND_MISSION_MOB_46H}, {"mob_47a", STREAMED_SOUND_MISSION_MOB_47A}, {"mob_52a", STREAMED_SOUND_MISSION_MOB_52A}, + {"mob_52b", STREAMED_SOUND_MISSION_MOB_52B}, {"mob_52c", STREAMED_SOUND_MISSION_MOB_52C}, {"mob_52d", STREAMED_SOUND_MISSION_MOB_52D}, + {"mob_52e", STREAMED_SOUND_MISSION_MOB_52E}, {"mob_52f", STREAMED_SOUND_MISSION_MOB_52F}, {"mob_52g", STREAMED_SOUND_MISSION_MOB_52G}, + {"mob_52h", STREAMED_SOUND_MISSION_MOB_52H}, {"mob_54a", STREAMED_SOUND_MISSION_MOB_54A}, {"mob_54b", STREAMED_SOUND_MISSION_MOB_54B}, + {"mob_54c", STREAMED_SOUND_MISSION_MOB_54C}, {"mob_54d", STREAMED_SOUND_MISSION_MOB_54D}, {"mob_54e", STREAMED_SOUND_MISSION_MOB_54E}, + {"mob_55a", STREAMED_SOUND_MISSION_MOB_55A}, {"mob_55b", STREAMED_SOUND_MISSION_MOB_55B}, {"mob_55c", STREAMED_SOUND_MISSION_MOB_55C}, + {"mob_55d", STREAMED_SOUND_MISSION_MOB_55D}, {"mob_55e", STREAMED_SOUND_MISSION_MOB_55E}, {"mob_55f", STREAMED_SOUND_MISSION_MOB_55F}, + {"mob_56a", STREAMED_SOUND_MISSION_MOB_56A}, {"mob_56b", STREAMED_SOUND_MISSION_MOB_56B}, {"mob_56c", STREAMED_SOUND_MISSION_MOB_56C}, + {"mob_56d", STREAMED_SOUND_MISSION_MOB_56D}, {"mob_56e", STREAMED_SOUND_MISSION_MOB_56E}, {"mob_56f", STREAMED_SOUND_MISSION_MOB_56F}, + {"mob_57a", STREAMED_SOUND_MISSION_MOB_57A}, {"mob_57b", STREAMED_SOUND_MISSION_MOB_57B}, {"mob_57c", STREAMED_SOUND_MISSION_MOB_57C}, + {"mob_57d", STREAMED_SOUND_MISSION_MOB_57D}, {"mob_57e", STREAMED_SOUND_MISSION_MOB_57E}, {"mob_58a", STREAMED_SOUND_MISSION_MOB_58A}, + {"mob_58b", STREAMED_SOUND_MISSION_MOB_58B}, {"mob_58c", STREAMED_SOUND_MISSION_MOB_58C}, {"mob_58d", STREAMED_SOUND_MISSION_MOB_58D}, + {"mob_58e", STREAMED_SOUND_MISSION_MOB_58E}, {"mob_58f", STREAMED_SOUND_MISSION_MOB_58F}, {"mob_58g", STREAMED_SOUND_MISSION_MOB_58G}, + {"mob_61a", STREAMED_SOUND_MISSION_MOB_61A}, {"mob_61b", STREAMED_SOUND_MISSION_MOB_61B}, {"mob_62a", STREAMED_SOUND_MISSION_MOB_62A}, + {"mob_62b", STREAMED_SOUND_MISSION_MOB_62B}, {"mob_62c", STREAMED_SOUND_MISSION_MOB_62C}, {"mob_62d", STREAMED_SOUND_MISSION_MOB_62D}, + {"mob_63a", STREAMED_SOUND_MISSION_MOB_63A}, {"mob_63b", STREAMED_SOUND_MISSION_MOB_63B}, {"mob_63c", STREAMED_SOUND_MISSION_MOB_63C}, + {"mob_63d", STREAMED_SOUND_MISSION_MOB_63D}, {"mob_63e", STREAMED_SOUND_MISSION_MOB_63E}, {"mob_63f", STREAMED_SOUND_MISSION_MOB_63F}, + {"mob_63g", STREAMED_SOUND_MISSION_MOB_63G}, {"mob_63h", STREAMED_SOUND_MISSION_MOB_63H}, {"mob_63i", STREAMED_SOUND_MISSION_MOB_63I}, + {"mob_63j", STREAMED_SOUND_MISSION_MOB_63J}, {"mob_66a", STREAMED_SOUND_MISSION_MOB_66A}, {"mob_66b", STREAMED_SOUND_MISSION_MOB_66B}, + {"mob_68a", STREAMED_SOUND_MISSION_MOB_68A}, {"mob_68b", STREAMED_SOUND_MISSION_MOB_68B}, {"mob_68c", STREAMED_SOUND_MISSION_MOB_68C}, + {"mob_68d", STREAMED_SOUND_MISSION_MOB_68D}, {"mob_70a", STREAMED_SOUND_MISSION_MOB_70A}, {"mob_70b", STREAMED_SOUND_MISSION_MOB_70B}, + {"mob_71a", STREAMED_SOUND_MISSION_MOB_71A}, {"mob_71b", STREAMED_SOUND_MISSION_MOB_71B}, {"mob_71c", STREAMED_SOUND_MISSION_MOB_71C}, + {"mob_71d", STREAMED_SOUND_MISSION_MOB_71D}, {"mob_71e", STREAMED_SOUND_MISSION_MOB_71E}, {"mob_71f", STREAMED_SOUND_MISSION_MOB_71F}, + {"mob_71g", STREAMED_SOUND_MISSION_MOB_71G}, {"mob_71h", STREAMED_SOUND_MISSION_MOB_71H}, {"mob_71i", STREAMED_SOUND_MISSION_MOB_71I}, + {"mob_71j", STREAMED_SOUND_MISSION_MOB_71J}, {"mob_71k", STREAMED_SOUND_MISSION_MOB_71K}, {"mob_71l", STREAMED_SOUND_MISSION_MOB_71L}, + {"mob_71m", STREAMED_SOUND_MISSION_MOB_71M}, {"mob_71n", STREAMED_SOUND_MISSION_MOB_71N}, {"mob_72a", STREAMED_SOUND_MISSION_MOB_72A}, + {"mob_72b", STREAMED_SOUND_MISSION_MOB_72B}, {"mob_72c", STREAMED_SOUND_MISSION_MOB_72C}, {"mob_72d", STREAMED_SOUND_MISSION_MOB_72D}, + {"mob_72e", STREAMED_SOUND_MISSION_MOB_72E}, {"mob_72f", STREAMED_SOUND_MISSION_MOB_72F}, {"mob_72g", STREAMED_SOUND_MISSION_MOB_72G}, + {"mob_73a", STREAMED_SOUND_MISSION_MOB_73A}, {"mob_73c", STREAMED_SOUND_MISSION_MOB_73C}, {"mob_73d", STREAMED_SOUND_MISSION_MOB_73D}, + {"mob_73f", STREAMED_SOUND_MISSION_MOB_73F}, {"mob_73g", STREAMED_SOUND_MISSION_MOB_73G}, {"mob_73i", STREAMED_SOUND_MISSION_MOB_73I}, + {"mob_95a", STREAMED_SOUND_MISSION_MOB_95A}, {"mob_96a", STREAMED_SOUND_MISSION_MOB_96A}, {"mob_98a", STREAMED_SOUND_MISSION_MOB_98A}, + {"mob_99a", STREAMED_SOUND_MISSION_MOB_99A}, {"job1_1b", STREAMED_SOUND_MISSION_JOB1_1B}, {"job1_1c", STREAMED_SOUND_MISSION_JOB1_1C}, + {"job1_1d", STREAMED_SOUND_MISSION_JOB1_1D}, {"job2_1b", STREAMED_SOUND_MISSION_JOB2_1B}, {"job2_2", STREAMED_SOUND_MISSION_JOB2_2}, + {"job2_3", STREAMED_SOUND_MISSION_JOB2_3}, {"job2_4", STREAMED_SOUND_MISSION_JOB2_4}, {"job2_5", STREAMED_SOUND_MISSION_JOB2_5}, + {"job2_6", STREAMED_SOUND_MISSION_JOB2_6}, {"job2_7", STREAMED_SOUND_MISSION_JOB2_7}, {"job2_8", STREAMED_SOUND_MISSION_JOB2_8}, + {"job2_9", STREAMED_SOUND_MISSION_JOB2_9}, {"job3_1", STREAMED_SOUND_MISSION_JOB3_1}, {"job3_2", STREAMED_SOUND_MISSION_JOB3_2}, + {"job3_3", STREAMED_SOUND_MISSION_JOB3_3}, {"job4_1", STREAMED_SOUND_MISSION_JOB4_1}, {"job4_2", STREAMED_SOUND_MISSION_JOB4_2}, + {"job4_3", STREAMED_SOUND_MISSION_JOB4_3}, {"job5_1", STREAMED_SOUND_MISSION_JOB5_1}, {"job5_2", STREAMED_SOUND_MISSION_JOB5_2}, + {"job5_3", STREAMED_SOUND_MISSION_JOB5_3}, {"bjm1_20", STREAMED_SOUND_MISSION_BJM1_20}, {"bjm1_4", STREAMED_SOUND_MISSION_BJM1_4}, + {"bjm1_5", STREAMED_SOUND_MISSION_BJM1_5}, {"merc_39", STREAMED_SOUND_MISSION_MERC_39}, {"mono_1", STREAMED_SOUND_MISSION_MONO_1}, + {"mono_2", STREAMED_SOUND_MISSION_MONO_2}, {"mono_3", STREAMED_SOUND_MISSION_MONO_3}, {"mono_4", STREAMED_SOUND_MISSION_MONO_4}, + {"mono_5", STREAMED_SOUND_MISSION_MONO_5}, {"mono_6", STREAMED_SOUND_MISSION_MONO_6}, {"mono_7", STREAMED_SOUND_MISSION_MONO_7}, + {"mono_8", STREAMED_SOUND_MISSION_MONO_8}, {"mono_9", STREAMED_SOUND_MISSION_MONO_9}, {"mono10", STREAMED_SOUND_MISSION_MONO10}, + {"mono11", STREAMED_SOUND_MISSION_MONO11}, {"mono12", STREAMED_SOUND_MISSION_MONO12}, {"mono13", STREAMED_SOUND_MISSION_MONO13}, + {"mono14", STREAMED_SOUND_MISSION_MONO14}, {"mono15", STREAMED_SOUND_MISSION_MONO15}, {"mono16", STREAMED_SOUND_MISSION_MONO16}, + {"fud_01", STREAMED_SOUND_MISSION_FUD_01}, {"fud_02", STREAMED_SOUND_MISSION_FUD_02}, {"fud_03", STREAMED_SOUND_MISSION_FUD_03}, + {"fud_04", STREAMED_SOUND_MISSION_FUD_04}, {"fud_05", STREAMED_SOUND_MISSION_FUD_05}, {"fud_06", STREAMED_SOUND_MISSION_FUD_06}, + {"fud_07", STREAMED_SOUND_MISSION_FUD_07}, {"fud_08", STREAMED_SOUND_MISSION_FUD_08}, {"fud_09", STREAMED_SOUND_MISSION_FUD_09}, + {"fud_10", STREAMED_SOUND_MISSION_FUD_10}, {"fud_11", STREAMED_SOUND_MISSION_FUD_11}, {"fud_12", STREAMED_SOUND_MISSION_FUD_12}, + {"fud_13", STREAMED_SOUND_MISSION_FUD_13}, {"fud_14", STREAMED_SOUND_MISSION_FUD_14}, {"fud_15", STREAMED_SOUND_MISSION_FUD_15}, + {"fud_16", STREAMED_SOUND_MISSION_FUD_16}, {"fud_17", STREAMED_SOUND_MISSION_FUD_17}, {"fud_18", STREAMED_SOUND_MISSION_FUD_18}, + {"fud_19", STREAMED_SOUND_MISSION_FUD_19}, {"fud_20", STREAMED_SOUND_MISSION_FUD_20}, {"burg_01", STREAMED_SOUND_MISSION_BURG_01}, + {"burg_02", STREAMED_SOUND_MISSION_BURG_02}, {"burg_03", STREAMED_SOUND_MISSION_BURG_03}, {"burg_04", STREAMED_SOUND_MISSION_BURG_04}, + {"burg_05", STREAMED_SOUND_MISSION_BURG_05}, {"burg_06", STREAMED_SOUND_MISSION_BURG_06}, {"burg_07", STREAMED_SOUND_MISSION_BURG_07}, + {"burg_08", STREAMED_SOUND_MISSION_BURG_08}, {"burg_09", STREAMED_SOUND_MISSION_BURG_09}, {"burg_10", STREAMED_SOUND_MISSION_BURG_10}, + {"burg_11", STREAMED_SOUND_MISSION_BURG_11}, {"burg_12", STREAMED_SOUND_MISSION_BURG_12}, {"crust01", STREAMED_SOUND_MISSION_CRUST01}, + {"crust02", STREAMED_SOUND_MISSION_CRUST02}, {"crust03", STREAMED_SOUND_MISSION_CRUST03}, {"crust04", STREAMED_SOUND_MISSION_CRUST04}, + {"crust05", STREAMED_SOUND_MISSION_CRUST05}, {"crust06", STREAMED_SOUND_MISSION_CRUST06}, {"crust07", STREAMED_SOUND_MISSION_CRUST07}, + {"crust08", STREAMED_SOUND_MISSION_CRUST08}, {"crust09", STREAMED_SOUND_MISSION_CRUST09}, {"band_01", STREAMED_SOUND_MISSION_BAND_01}, + {"band_02", STREAMED_SOUND_MISSION_BAND_02}, {"band_03", STREAMED_SOUND_MISSION_BAND_03}, {"band_04", STREAMED_SOUND_MISSION_BAND_04}, + {"band_05", STREAMED_SOUND_MISSION_BAND_05}, {"band_06", STREAMED_SOUND_MISSION_BAND_06}, {"band_07", STREAMED_SOUND_MISSION_BAND_07}, + {"band_08", STREAMED_SOUND_MISSION_BAND_08}, {"shaft01", STREAMED_SOUND_MISSION_SHAFT01}, {"shaft02", STREAMED_SOUND_MISSION_SHAFT02}, + {"shaft03", STREAMED_SOUND_MISSION_SHAFT03}, {"shaft04", STREAMED_SOUND_MISSION_SHAFT04}, {"shaft05", STREAMED_SOUND_MISSION_SHAFT05}, + {"shaft06", STREAMED_SOUND_MISSION_SHAFT06}, {"shaft07", STREAMED_SOUND_MISSION_SHAFT07}, {"shaft08", STREAMED_SOUND_MISSION_SHAFT08}, + {"piss_01", STREAMED_SOUND_MISSION_PISS_01}, {"piss_02", STREAMED_SOUND_MISSION_PISS_02}, {"piss_03", STREAMED_SOUND_MISSION_PISS_03}, + {"piss_04", STREAMED_SOUND_MISSION_PISS_04}, {"piss_05", STREAMED_SOUND_MISSION_PISS_05}, {"piss_06", STREAMED_SOUND_MISSION_PISS_06}, + {"piss_07", STREAMED_SOUND_MISSION_PISS_07}, {"piss_08", STREAMED_SOUND_MISSION_PISS_08}, {"piss_09", STREAMED_SOUND_MISSION_PISS_09}, + {"piss_10", STREAMED_SOUND_MISSION_PISS_10}, {"piss_11", STREAMED_SOUND_MISSION_PISS_11}, {"piss_12", STREAMED_SOUND_MISSION_PISS_12}, + {"piss_13", STREAMED_SOUND_MISSION_PISS_13}, {"piss_14", STREAMED_SOUND_MISSION_PISS_14}, {"piss_15", STREAMED_SOUND_MISSION_PISS_15}, + {"piss_16", STREAMED_SOUND_MISSION_PISS_16}, {"piss_17", STREAMED_SOUND_MISSION_PISS_17}, {"piss_18", STREAMED_SOUND_MISSION_PISS_18}, + {"piss_19", STREAMED_SOUND_MISSION_PISS_19}, {"gimme01", STREAMED_SOUND_MISSION_GIMME01}, {"gimme02", STREAMED_SOUND_MISSION_GIMME02}, + {"gimme03", STREAMED_SOUND_MISSION_GIMME03}, {"gimme04", STREAMED_SOUND_MISSION_GIMME04}, {"gimme05", STREAMED_SOUND_MISSION_GIMME05}, + {"gimme06", STREAMED_SOUND_MISSION_GIMME06}, {"gimme07", STREAMED_SOUND_MISSION_GIMME07}, {"gimme08", STREAMED_SOUND_MISSION_GIMME08}, + {"gimme09", STREAMED_SOUND_MISSION_GIMME09}, {"gimme10", STREAMED_SOUND_MISSION_GIMME10}, {"gimme11", STREAMED_SOUND_MISSION_GIMME11}, + {"gimme12", STREAMED_SOUND_MISSION_GIMME12}, {"gimme13", STREAMED_SOUND_MISSION_GIMME13}, {"gimme14", STREAMED_SOUND_MISSION_GIMME14}, + {"gimme15", STREAMED_SOUND_MISSION_GIMME15}, {"bust_01", STREAMED_SOUND_MISSION_BUST_01}, {"bust_02", STREAMED_SOUND_MISSION_BUST_02}, + {"bust_03", STREAMED_SOUND_MISSION_BUST_03}, {"bust_04", STREAMED_SOUND_MISSION_BUST_04}, {"bust_05", STREAMED_SOUND_MISSION_BUST_05}, + {"bust_06", STREAMED_SOUND_MISSION_BUST_06}, {"bust_07", STREAMED_SOUND_MISSION_BUST_07}, {"bust_08", STREAMED_SOUND_MISSION_BUST_08}, + {"bust_09", STREAMED_SOUND_MISSION_BUST_09}, {"bust_10", STREAMED_SOUND_MISSION_BUST_10}, {"bust_11", STREAMED_SOUND_MISSION_BUST_11}, + {"bust_12", STREAMED_SOUND_MISSION_BUST_12}, {"bust_13", STREAMED_SOUND_MISSION_BUST_13}, {"bust_14", STREAMED_SOUND_MISSION_BUST_14}, + {"bust_15", STREAMED_SOUND_MISSION_BUST_15}, {"bust_16", STREAMED_SOUND_MISSION_BUST_16}, {"bust_17", STREAMED_SOUND_MISSION_BUST_17}, + {"bust_18", STREAMED_SOUND_MISSION_BUST_18}, {"bust_19", STREAMED_SOUND_MISSION_BUST_19}, {"bust_20", STREAMED_SOUND_MISSION_BUST_20}, + {"bust_21", STREAMED_SOUND_MISSION_BUST_21}, {"bust_22", STREAMED_SOUND_MISSION_BUST_22}, {"bust_23", STREAMED_SOUND_MISSION_BUST_23}, + {"bust_24", STREAMED_SOUND_MISSION_BUST_24}, {"bust_25", STREAMED_SOUND_MISSION_BUST_25}, {"bust_26", STREAMED_SOUND_MISSION_BUST_26}, + {"bust_27", STREAMED_SOUND_MISSION_BUST_27}, {"bust_28", STREAMED_SOUND_MISSION_BUST_28}, {nil, 0} }; + +uint32 +FindMissionAudioSfx(const char *name) +{ + for (uint32 i = 0; MissionAudioNameSfxAssoc[i].m_pName != nil; i++) { + if (!CGeneral::faststricmp(MissionAudioNameSfxAssoc[i].m_pName, name)) + return MissionAudioNameSfxAssoc[i].m_nId; + } + debug("Can't find mission audio %s", name); + return NO_SAMPLE; +} + +const char * +cAudioManager::GetMissionAudioLoadedLabel(uint8 slot) +{ + if (m_bIsInitialised && slot < MISSION_AUDIO_SLOTS && m_nMissionAudioSampleIndex[slot] != NO_SAMPLE) { + for (uint32 i = 0; MissionAudioNameSfxAssoc[i].m_pName != nil; i++) { + if (m_nMissionAudioSampleIndex[slot] == MissionAudioNameSfxAssoc[i].m_nId) + return MissionAudioNameSfxAssoc[i].m_pName; + } + } + +#ifdef THIS_IS_STUPID + return MissionAudioNameSfxAssoc[0].m_pName; // yeah this is dumb +#else + return ""; +#endif +} + +bool8 +cAudioManager::MissionScriptAudioUsesPoliceChannel(uint32 soundMission) +{ + return FALSE; +} + +void +cAudioManager::PreloadMissionAudio(uint8 slot, Const char *name) +{ + if (m_bIsInitialised && slot < MISSION_AUDIO_SLOTS) { + uint32 missionAudioSfx = FindMissionAudioSfx(name); + if (missionAudioSfx != NO_SAMPLE) { + m_nMissionAudioSampleIndex[slot] = missionAudioSfx; + m_nMissionAudioLoadingStatus[slot] = LOADING_STATUS_NOT_LOADED; + m_nMissionAudioPlayStatus[slot] = PLAY_STATUS_STOPPED; + m_bIsMissionAudioPlaying[slot] = FALSE; + m_nMissionAudioFramesToPlay[slot] = m_nTimeSpent * SampleManager.GetStreamedFileLength(missionAudioSfx) / 1000; + m_nMissionAudioFramesToPlay[slot] *= 4; + m_bIsMissionAudioAllowedToPlay[slot] = FALSE; + m_bIsMissionAudio2D[slot] = TRUE; + g_bMissionAudioLoadFailed[slot] = FALSE; + } + } +} + +uint8 +cAudioManager::GetMissionAudioLoadingStatus(uint8 slot) +{ + if (m_bIsInitialised && slot < MISSION_AUDIO_SLOTS) + return m_nMissionAudioLoadingStatus[slot]; + + return LOADING_STATUS_LOADED; +} + +void +cAudioManager::SetMissionAudioLocation(uint8 slot, float x, float y, float z) +{ + if (m_bIsInitialised && slot < MISSION_AUDIO_SLOTS) { + m_bIsMissionAudio2D[slot] = FALSE; + m_vecMissionAudioPosition[slot] = CVector(x, y, z); + } +} + +void +cAudioManager::PlayLoadedMissionAudio(uint8 slot) +{ + if (m_bIsInitialised && slot < MISSION_AUDIO_SLOTS && m_nMissionAudioSampleIndex[slot] != NO_SAMPLE && m_nMissionAudioLoadingStatus[slot] == LOADING_STATUS_LOADED && + m_nMissionAudioPlayStatus[slot] == PLAY_STATUS_STOPPED) + m_bIsMissionAudioAllowedToPlay[slot] = TRUE; +} + +bool8 +cAudioManager::ShouldDuckMissionAudio(uint8 slot) +{ + if (IsMissionAudioSamplePlaying(slot)) + return m_nMissionAudioSampleIndex[slot] != STREAMED_SOUND_MISSION_ROK2_01; + return FALSE; +} + +bool8 +cAudioManager::IsMissionAudioSamplePlaying(uint8 slot) +{ + if (m_bIsInitialised) { + if (slot < MISSION_AUDIO_SLOTS) + return m_nMissionAudioPlayStatus[slot] == PLAY_STATUS_PLAYING; + else + return TRUE; + } + + static uint32 cPretendFrame[MISSION_AUDIO_SLOTS] = { 1, 1 }; + + return (cPretendFrame[slot]++ % 64) != 0; +} + +bool8 +cAudioManager::IsMissionAudioSampleFinished(uint8 slot) +{ + if (m_bIsInitialised) { + if (slot < MISSION_AUDIO_SLOTS) + return m_nMissionAudioPlayStatus[slot] == PLAY_STATUS_FINISHED; + else + return TRUE; + } + + static uint32 cPretendFrame[MISSION_AUDIO_SLOTS] = { 1, 1 }; + + return (cPretendFrame[slot]++ % 64) == 0; +} + +void +cAudioManager::ClearMissionAudio(uint8 slot) +{ + if (m_bIsInitialised && slot < MISSION_AUDIO_SLOTS) { + m_nMissionAudioSampleIndex[slot] = NO_SAMPLE; + m_nMissionAudioLoadingStatus[slot] = LOADING_STATUS_NOT_LOADED; + m_nMissionAudioPlayStatus[slot] = PLAY_STATUS_STOPPED; + m_bIsMissionAudioPlaying[slot] = FALSE; + m_bIsMissionAudioAllowedToPlay[slot] = FALSE; + m_bIsMissionAudio2D[slot] = TRUE; + m_nMissionAudioFramesToPlay[slot] = 0; + m_bIsMissionAudioPhoneCall[slot] = FALSE; + SampleManager.StopStreamedFile(slot + 1); + } +} + +void +cAudioManager::ProcessMissionAudioSlot(uint8 slot) +{ + float dist; + uint8 Vol; + uint8 pan; + float distSquared; + CVector vec; + + static uint8 nCheckPlayingDelay[MISSION_AUDIO_SLOTS] = { 0, 0 }; + static uint8 nFramesUntilFailedLoad[MISSION_AUDIO_SLOTS] = { 0, 0 }; + static uint8 nFramesForPretendPlaying[MISSION_AUDIO_SLOTS] = { 0, 0 }; + + if (m_nMissionAudioSampleIndex[slot] != NO_SAMPLE) { + switch (m_nMissionAudioLoadingStatus[slot]) { + case LOADING_STATUS_NOT_LOADED: + SampleManager.PreloadStreamedFile(m_nMissionAudioSampleIndex[slot], slot + 1); + m_nMissionAudioLoadingStatus[slot] = LOADING_STATUS_LOADED; + nFramesUntilFailedLoad[slot] = 0; + break; + case LOADING_STATUS_LOADING: + if (++nFramesUntilFailedLoad[slot] >= 120) { + nFramesForPretendPlaying[slot] = 0; + g_bMissionAudioLoadFailed[slot] = TRUE; + nFramesUntilFailedLoad[slot] = 0; + m_nMissionAudioLoadingStatus[slot] = LOADING_STATUS_LOADED; + } + return; + default: + return; + case LOADING_STATUS_LOADED: + if (!m_bIsMissionAudioAllowedToPlay[slot]) + break; + if (g_bMissionAudioLoadFailed[slot]) { + if (m_bTimerJustReset) { + ClearMissionAudio(slot); + SampleManager.StopStreamedFile(slot + 1); + nFramesForPretendPlaying[slot] = 0; + nCheckPlayingDelay[slot] = 0; + nFramesUntilFailedLoad[slot] = 0; + } else if (!m_bIsPaused) { + if (++nFramesForPretendPlaying[slot] >= 90) { + m_nMissionAudioPlayStatus[slot] = PLAY_STATUS_FINISHED; + m_nMissionAudioSampleIndex[slot] = NO_SAMPLE; + } else + m_nMissionAudioPlayStatus[slot] = PLAY_STATUS_PLAYING; + } + break; + } + switch (m_nMissionAudioPlayStatus[slot]) { + case PLAY_STATUS_STOPPED: + if (MissionScriptAudioUsesPoliceChannel(m_nMissionAudioSampleIndex[slot])) + SetMissionScriptPoliceAudio(m_nMissionAudioSampleIndex[slot]); + else { + if (m_bIsPaused) + SampleManager.PauseStream(TRUE, slot + 1); + if (m_bIsMissionAudio2D[slot]) { + if (m_nMissionAudioSampleIndex[slot] == STREAMED_SOUND_MISSION_CAMERAL) + SampleManager.SetStreamedVolumeAndPan(MISSION_AUDIO_VOLUME, 0, TRUE, slot + 1); + else if (m_nMissionAudioSampleIndex[slot] == STREAMED_SOUND_MISSION_CAMERAR) + SampleManager.SetStreamedVolumeAndPan(MISSION_AUDIO_VOLUME, 127, TRUE, slot + 1); + else + SampleManager.SetStreamedVolumeAndPan(MISSION_AUDIO_VOLUME, 63, TRUE, slot + 1); + } else { + distSquared = GetDistanceSquared(m_vecMissionAudioPosition[slot]); + if (distSquared < SQR(MISSION_AUDIO_MAX_DIST)) { + if (distSquared > 0.0f) { + dist = Sqrt(distSquared); + Vol = ComputeVolume(MISSION_AUDIO_VOLUME, MISSION_AUDIO_MAX_DIST, dist); + } else + Vol = MISSION_AUDIO_VOLUME; + TranslateEntity(&m_vecMissionAudioPosition[slot], &vec); + pan = ComputePan(MISSION_AUDIO_MAX_DIST, &vec); + } else { + Vol = 0; + pan = 63; + } + SampleManager.SetStreamedVolumeAndPan(Vol, pan, TRUE, slot + 1); + } + SampleManager.StartPreloadedStreamedFile(slot + 1); + } + m_nMissionAudioPlayStatus[slot] = PLAY_STATUS_PLAYING; + nCheckPlayingDelay[slot] = 30; + if (m_nMissionAudioSampleIndex[slot] >= STREAMED_SOUND_MISSION_MOB_01A && m_nMissionAudioSampleIndex[slot] <= STREAMED_SOUND_MISSION_MOB_99A) + m_bIsMissionAudioPhoneCall[slot] = TRUE; + break; + case PLAY_STATUS_PLAYING: + if (m_bTimerJustReset) { + ClearMissionAudio(slot); + SampleManager.StopStreamedFile(slot + 1); + return; + } + if (MissionScriptAudioUsesPoliceChannel(m_nMissionAudioSampleIndex[slot])) { + if (!m_bIsPaused) { + if (nCheckPlayingDelay[slot] > 0) { + nCheckPlayingDelay[slot]--; + } else if ((g_bMissionAudioLoadFailed[slot] && m_nMissionAudioFramesToPlay[slot]-- == 0) || GetMissionScriptPoliceAudioPlayingStatus() == PLAY_STATUS_FINISHED) { + m_nMissionAudioPlayStatus[slot] = PLAY_STATUS_FINISHED; + if (m_nMissionAudioSampleIndex[slot] >= STREAMED_SOUND_MISSION_MOB_01A && m_nMissionAudioSampleIndex[slot] <= STREAMED_SOUND_MISSION_MOB_99A) + m_bIsMissionAudioPhoneCall[slot] = FALSE; + m_nMissionAudioSampleIndex[slot] = NO_SAMPLE; + SampleManager.StopStreamedFile(slot + 1); + m_nMissionAudioFramesToPlay[slot] = 0; + } + } + } else if (m_bIsMissionAudioPlaying[slot]) { + if (!SampleManager.IsStreamPlaying(slot + 1) && !m_bIsPaused && !m_bWasPaused) { + if (m_nMissionAudioSampleIndex[slot] == STREAMED_SOUND_MISSION_ROK2_01) + m_nMissionAudioPlayStatus[slot] = PLAY_STATUS_STOPPED; + else { + m_nMissionAudioPlayStatus[slot] = PLAY_STATUS_FINISHED; + if (m_nMissionAudioSampleIndex[slot] >= STREAMED_SOUND_MISSION_MOB_01A && m_nMissionAudioSampleIndex[slot] <= STREAMED_SOUND_MISSION_MOB_99A) + m_bIsMissionAudioPhoneCall[slot] = FALSE; + m_nMissionAudioSampleIndex[slot] = NO_SAMPLE; + SampleManager.StopStreamedFile(slot + 1); + m_nMissionAudioFramesToPlay[slot] = 0; + } + } else { + if (m_bIsPaused) + SampleManager.PauseStream(TRUE, slot + 1); + else { + SampleManager.PauseStream(FALSE, slot + 1); + if (!m_bIsMissionAudio2D[slot]) { + distSquared = GetDistanceSquared(m_vecMissionAudioPosition[slot]); + if (distSquared < SQR(MISSION_AUDIO_MAX_DIST)) { + // BUG? Why MAX_VOLUME instead of MISSION_AUDIO_VOLUME? + if (distSquared > 0.0f) { + dist = Sqrt(distSquared); + Vol = ComputeVolume(MAX_VOLUME, MISSION_AUDIO_MAX_DIST, dist); + } else + Vol = MAX_VOLUME; + TranslateEntity(&m_vecMissionAudioPosition[slot], &vec); + pan = ComputePan(MISSION_AUDIO_MAX_DIST, &vec); + } else { + Vol = 0; + pan = 63; + } + SampleManager.SetStreamedVolumeAndPan(Vol, pan, TRUE, slot + 1); + } + } + } + } else { + if (m_bIsPaused) + break; + if (nCheckPlayingDelay[slot]-- > 0) { + if (!SampleManager.IsStreamPlaying(slot + 1)) + break; + nCheckPlayingDelay[slot] = 0; + } + m_bIsMissionAudioPlaying[slot] = TRUE; + } + break; + default: + break; + } + break; + } + } +} + +void +cAudioManager::ProcessMissionAudio() +{ + if (!m_bIsInitialised) return; + + for (int i = 0; i < MISSION_AUDIO_SLOTS; i++) + ProcessMissionAudioSlot(i); + + if (m_bIsMissionAudioPhoneCall[0] || m_bIsMissionAudioPhoneCall[1]) + m_nGlobalSfxVolumeMultiplier = 64; + else if (m_nGlobalSfxVolumeMultiplier < 127) { + m_nGlobalSfxVolumeMultiplier += 5; + if (m_nGlobalSfxVolumeMultiplier > 127) + m_nGlobalSfxVolumeMultiplier = 127; + } +} +#pragma endregion All the mission audio stuff diff --git a/src/miami/audio/AudioManager.cpp b/src/miami/audio/AudioManager.cpp new file mode 100644 index 00000000..51f1946f --- /dev/null +++ b/src/miami/audio/AudioManager.cpp @@ -0,0 +1,1434 @@ +#include "common.h" + +#include "AudioManager.h" +#include "audio_enums.h" + +#include "AudioScriptObject.h" +#include "MusicManager.h" +#include "Timer.h" +#include "DMAudio.h" +#include "sampman.h" +#include "Camera.h" +#include "World.h" +#include "ZoneCull.h" + +cAudioManager AudioManager; + +#define SPEED_OF_SOUND 343.f +#define TIME_SPENT 40 + +cAudioManager::cAudioManager() +{ + m_bIsInitialised = FALSE; + m_bIsSurround = TRUE; + m_nChannelOffset = 0; + m_fSpeedOfSound = SPEED_OF_SOUND / TIME_SPENT; + m_nTimeSpent = TIME_SPENT; + m_nActiveSamples = NUM_CHANNELS_GENERIC; + m_nActiveQueue = 1; + ClearRequestedQueue(); + m_nActiveQueue = 0; + ClearRequestedQueue(); + ClearActiveSamples(); + GenerateIntegerRandomNumberTable(); + m_bDoubleVolume = FALSE; +#ifdef AUDIO_REFLECTIONS + m_bDynamicAcousticModelingStatus = TRUE; +#endif + + for (uint32 i = 0; i < NUM_AUDIOENTITIES; i++) { + m_asAudioEntities[i].m_bIsUsed = FALSE; + m_aAudioEntityOrderList[i] = NUM_AUDIOENTITIES; + } + m_nAudioEntitiesCount = 0; + m_FrameCounter = 0; + m_bReduceReleasingPriority = FALSE; + m_bTimerJustReset = FALSE; + m_nTimer = 0; +} + +cAudioManager::~cAudioManager() +{ + if (m_bIsInitialised) + Terminate(); +} + +void +cAudioManager::Initialise() +{ + if (!m_bIsInitialised) { + PreInitialiseGameSpecificSetup(); + m_bIsInitialised = SampleManager.Initialise(); + if (m_bIsInitialised) { +#ifdef EXTERNAL_3D_SOUND + m_nActiveSamples = SampleManager.GetMaximumSupportedChannels(); + if (m_nActiveSamples <= 1) { + Terminate(); + } else { + m_nActiveSamples--; +#else + { + m_nActiveSamples = NUM_CHANNELS_GENERIC; +#endif + PostInitialiseGameSpecificSetup(); + InitialisePoliceRadioZones(); + InitialisePoliceRadio(); + MusicManager.Initialise(); + } + } + } +} + +void +cAudioManager::Terminate() +{ + if (m_bIsInitialised) { + MusicManager.Terminate(); + + for (uint32 i = 0; i < NUM_AUDIOENTITIES; i++) { + m_asAudioEntities[i].m_bIsUsed = FALSE; + m_aAudioEntityOrderList[i] = ARRAY_SIZE(m_aAudioEntityOrderList); + } + + m_nAudioEntitiesCount = 0; + m_sAudioScriptObjectManager.m_nScriptObjectEntityTotal = 0; + PreTerminateGameSpecificShutdown(); + + for (uint32 i = 0; i < MAX_SFX_BANKS; i++) { + if (SampleManager.IsSampleBankLoaded(i)) + SampleManager.UnloadSampleBank(i); + } + + SampleManager.Terminate(); + + m_bIsInitialised = FALSE; + PostTerminateGameSpecificShutdown(); + } +} + +void +cAudioManager::Service() +{ + GenerateIntegerRandomNumberTable(); + if (m_bTimerJustReset) { + ResetAudioLogicTimers(m_nTimer); + MusicManager.ResetTimers(m_nTimer); + m_bTimerJustReset = FALSE; + } + if (m_bIsInitialised) { + m_bWasPaused = m_bIsPaused; + m_bIsPaused = CTimer::GetIsUserPaused(); +#ifdef AUDIO_REFLECTIONS + UpdateReflections(); +#endif + ServiceSoundEffects(); + MusicManager.Service(); + } +} + +int32 +cAudioManager::CreateEntity(eAudioType type, void *entity) +{ + if (!m_bIsInitialised) + return AEHANDLE_ERROR_NOAUDIOSYS; + if (!entity) + return AEHANDLE_ERROR_NOENTITY; + if (type >= TOTAL_AUDIO_TYPES) + return AEHANDLE_ERROR_BADAUDIOTYPE; + +#ifdef FIX_BUGS + // since sound could still play after entity deletion let's make sure we don't override one that is in use + // find all the free entity IDs that are being used by queued samples + int32 stillUsedEntities[NUM_CHANNELS_GENERIC * NUM_SOUND_QUEUES]; + uint32 stillUsedEntitiesCount = 0; + + for (uint8 i = 0; i < NUM_SOUND_QUEUES; i++) + for (uint8 j = 0; j < m_nRequestedCount[i]; j++) { + tSound &sound = m_aRequestedQueue[i][m_aRequestedOrderList[i][j]]; + if (sound.m_nEntityIndex < 0) continue; + if (!m_asAudioEntities[sound.m_nEntityIndex].m_bIsUsed) { + bool found = false; + for (uint8 k = 0; k < stillUsedEntitiesCount; k++) { + if (stillUsedEntities[k] == sound.m_nEntityIndex) { + found = true; + break; + } + } + if (!found) + stillUsedEntities[stillUsedEntitiesCount++] = sound.m_nEntityIndex; + } + } +#endif + + for (uint32 i = 0; i < NUM_AUDIOENTITIES; i++) { + if (!m_asAudioEntities[i].m_bIsUsed) { +#ifdef FIX_BUGS + // skip if ID is still used by queued sample + bool skip = false; + for (uint8 j = 0; j < stillUsedEntitiesCount; j++) { + if (stillUsedEntities[j] == i) { + //debug("audio entity %i still used, skipping\n", i); + skip = true; + break; + } + } + if (skip) + continue; +#endif + + m_asAudioEntities[i].m_bIsUsed = TRUE; + m_asAudioEntities[i].m_bStatus = FALSE; + m_asAudioEntities[i].m_nType = type; + m_asAudioEntities[i].m_pEntity = entity; + m_asAudioEntities[i].m_awAudioEvent[0] = SOUND_NO_SOUND; + m_asAudioEntities[i].m_awAudioEvent[1] = SOUND_NO_SOUND; + m_asAudioEntities[i].m_awAudioEvent[2] = SOUND_NO_SOUND; + m_asAudioEntities[i].m_awAudioEvent[3] = SOUND_NO_SOUND; + m_asAudioEntities[i].m_AudioEvents = 0; + m_aAudioEntityOrderList[m_nAudioEntitiesCount++] = i; + return i; + } + } + return AEHANDLE_ERROR_NOFREESLOT; +} + +void +cAudioManager::DestroyEntity(int32 id) +{ + if (m_bIsInitialised && id >= 0 && id < NUM_AUDIOENTITIES && m_asAudioEntities[id].m_bIsUsed) { + m_asAudioEntities[id].m_bIsUsed = FALSE; + for (uint32 i = 0; i < m_nAudioEntitiesCount; ++i) { + if (id == m_aAudioEntityOrderList[i]) { + if (i < NUM_AUDIOENTITIES - 1) + memmove(&m_aAudioEntityOrderList[i], &m_aAudioEntityOrderList[i + 1], sizeof(uint32) * (m_nAudioEntitiesCount - (i + 1))); + m_aAudioEntityOrderList[--m_nAudioEntitiesCount] = NUM_AUDIOENTITIES; + return; + } + } + } +} + +bool8 +cAudioManager::GetEntityStatus(int32 id) +{ + if (m_bIsInitialised && id >= 0 && id < NUM_AUDIOENTITIES && m_asAudioEntities[id].m_bIsUsed) + return m_asAudioEntities[id].m_bStatus; + return FALSE; +} + +void +cAudioManager::SetEntityStatus(int32 id, bool8 status) +{ + if (m_bIsInitialised && id >= 0 && id < NUM_AUDIOENTITIES && m_asAudioEntities[id].m_bIsUsed) + m_asAudioEntities[id].m_bStatus = status; +} + +void * +cAudioManager::GetEntityPointer(int32 id) +{ + if (m_bIsInitialised && id >= 0 && id < NUM_AUDIOENTITIES && m_asAudioEntities[id].m_bIsUsed) + return m_asAudioEntities[id].m_pEntity; + return NULL; +} + +static Const uint8 OneShotPriority[] = { + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 2, 5, 5, 3, 5, 2, 2, 1, 1, 3, 1, 3, 3, 1, 1, 1, 1, 4, 4, 4, 3, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 1, 1, 1, 1, 3, 4, 2, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 3, 1, 1, 1, 9, 0, 0, 0, 1, 2, 2, 0, 0, 2, 3, 3, 3, 5, 1, 1, + 1, 1, 1, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 4, 7, 1, 4, 3, 4, 2, 2, 2, 3, 1, 2, 1, 3, 5, 3, 4, 6, 4, 6, 3, 0, 0, 0, 0, 0, + 0, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, 0, 0, 0, 0, 0, 3, 3, 1, 0 + }; + +void +cAudioManager::PlayOneShot(int32 index, uint16 sound, float vol) +{ + + if (m_bIsInitialised) { + if (index >= 0 && index < NUM_AUDIOENTITIES) { + tAudioEntity &entity = m_asAudioEntities[index]; + if (entity.m_bIsUsed) { + if (sound < SOUND_TOTAL_SOUNDS) { + if (entity.m_nType == AUDIOTYPE_SCRIPTOBJECT) { + if (m_sAudioScriptObjectManager.m_nScriptObjectEntityTotal < ARRAY_SIZE(m_sAudioScriptObjectManager.m_anScriptObjectEntityIndices)) { + entity.m_awAudioEvent[0] = sound; + entity.m_AudioEvents = 1; + m_sAudioScriptObjectManager.m_anScriptObjectEntityIndices[m_sAudioScriptObjectManager.m_nScriptObjectEntityTotal++] = index; + } + } else { + int32 i = 0; + while (TRUE) { + if (i >= entity.m_AudioEvents) { + if (entity.m_AudioEvents < NUM_AUDIOENTITY_EVENTS) { + entity.m_awAudioEvent[i] = sound; + entity.m_afVolume[i] = vol; + entity.m_AudioEvents++; + } + return; + } + if (OneShotPriority[entity.m_awAudioEvent[i]] > OneShotPriority[sound]) + break; + i++; + } + if (i < NUM_AUDIOENTITY_EVENTS - 1) { + memmove(&entity.m_awAudioEvent[i + 1], &entity.m_awAudioEvent[i], (NUM_AUDIOENTITY_EVENTS - 1 - i) * sizeof(int16)); + memmove(&entity.m_afVolume[i + 1], &entity.m_afVolume[i], (NUM_AUDIOENTITY_EVENTS - 1 - i) * sizeof(float)); + } + entity.m_awAudioEvent[i] = sound; + entity.m_afVolume[i] = vol; + if (entity.m_AudioEvents < NUM_AUDIOENTITY_EVENTS) + entity.m_AudioEvents++; + } + } + } + } + } +} + +void +cAudioManager::SetEffectsMasterVolume(uint8 volume) +{ + SampleManager.SetEffectsMasterVolume(volume); +} + +void +cAudioManager::SetMusicMasterVolume(uint8 volume) +{ + SampleManager.SetMusicMasterVolume(volume); +} + +void +cAudioManager::SetMP3BoostVolume(uint8 volume) +{ + SampleManager.SetMP3BoostVolume(volume); +} + +void +cAudioManager::SetEffectsFadeVol(uint8 volume) +{ + SampleManager.SetEffectsFadeVolume(volume); +} + +void +cAudioManager::SetMusicFadeVol(uint8 volume) +{ + SampleManager.SetMusicFadeVolume(volume); +} + +void +cAudioManager::SetOutputMode(bool8 surround) +{ + // on ps2 this calls another method of cAudioManager to set DTS mode on or off +} + +void +cAudioManager::ResetTimers(uint32 time) +{ + if (m_bIsInitialised) { + m_bTimerJustReset = TRUE; + m_nTimer = time; + ClearRequestedQueue(); + if (m_nActiveQueue) { + m_nActiveQueue = 0; + ClearRequestedQueue(); + m_nActiveQueue = 1; + } else { + m_nActiveQueue = 1; + ClearRequestedQueue(); + m_nActiveQueue = 0; + } + ClearActiveSamples(); + ClearMissionAudio(0); + ClearMissionAudio(1); + SampleManager.StopChannel(CHANNEL_POLICE_RADIO); + SampleManager.SetEffectsFadeVolume(0); + SampleManager.SetMusicFadeVolume(0); + MusicManager.ResetMusicAfterReload(); + m_bIsPlayerShutUp = FALSE; +#ifdef AUDIO_OAL + SampleManager.Service(); +#endif + } +} + +void +cAudioManager::DestroyAllGameCreatedEntities() +{ + cAudioScriptObject *entity; + + if (m_bIsInitialised) { + for (uint32 i = 0; i < NUM_AUDIOENTITIES; i++) { + if (m_asAudioEntities[i].m_bIsUsed) { + switch (m_asAudioEntities[i].m_nType) { + case AUDIOTYPE_PHYSICAL: + case AUDIOTYPE_EXPLOSION: + case AUDIOTYPE_WEATHER: + //case AUDIOTYPE_CRANE: + case AUDIOTYPE_GARAGE: + case AUDIOTYPE_FIREHYDRANT: + DestroyEntity(i); + break; + case AUDIOTYPE_SCRIPTOBJECT: + entity = (cAudioScriptObject *)m_asAudioEntities[i].m_pEntity; + if (entity) { + delete entity; + m_asAudioEntities[i].m_pEntity = nil; + } + DestroyEntity(i); + break; + default: + break; + } + } + } + m_sAudioScriptObjectManager.m_nScriptObjectEntityTotal = 0; + } +} + +#ifdef GTA_PC + +uint8 +cAudioManager::GetNum3DProvidersAvailable() +{ +#ifdef EXTERNAL_3D_SOUND + if (m_bIsInitialised) + return SampleManager.GetNum3DProvidersAvailable(); +#endif + return 0; +} + +char * +cAudioManager::Get3DProviderName(uint8 id) +{ +#ifndef EXTERNAL_3D_SOUND + return nil; +#else + if (!m_bIsInitialised) + return nil; +#ifdef AUDIO_OAL + id = Clamp(id, 0, SampleManager.GetNum3DProvidersAvailable() - 1); +#else + // We don't want that either since it will crash the game, but skipping for now + if (id >= SampleManager.GetNum3DProvidersAvailable()) + return nil; +#endif + return SampleManager.Get3DProviderName(id); +#endif +} + +int8 +cAudioManager::GetCurrent3DProviderIndex() +{ +#ifdef EXTERNAL_3D_SOUND + if (m_bIsInitialised) + return SampleManager.GetCurrent3DProviderIndex(); +#endif + + return -1; +} + +int8 +cAudioManager::AutoDetect3DProviders() +{ +#ifdef EXTERNAL_3D_SOUND + if (m_bIsInitialised) + return SampleManager.AutoDetect3DProviders(); +#endif + + return -1; +} + +int8 +cAudioManager::SetCurrent3DProvider(uint8 which) +{ +#ifndef EXTERNAL_3D_SOUND + return -1; +#else + if (!m_bIsInitialised) + return -1; + for (uint8 i = 0; i < m_nActiveSamples + 1; i++) + SampleManager.StopChannel(i); + ClearRequestedQueue(); + if (m_nActiveQueue == 0) + m_nActiveQueue = 1; + else + m_nActiveQueue = 0; + ClearRequestedQueue(); + ClearActiveSamples(); + int8 current = SampleManager.SetCurrent3DProvider(which); + if (current > 0) { + m_nActiveSamples = SampleManager.GetMaximumSupportedChannels(); + if (m_nActiveSamples > 1) + m_nActiveSamples--; + } + return current; +#endif +} + +void +cAudioManager::SetSpeakerConfig(int32 conf) +{ +#ifdef EXTERNAL_3D_SOUND + SampleManager.SetSpeakerConfig(conf); +#endif +} + +bool8 +cAudioManager::IsMP3RadioChannelAvailable() +{ + if (m_bIsInitialised) + return SampleManager.IsMP3RadioChannelAvailable(); + + return FALSE; +} + +void +cAudioManager::ReleaseDigitalHandle() +{ + if (m_bIsInitialised) + SampleManager.ReleaseDigitalHandle(); +} + +void +cAudioManager::ReacquireDigitalHandle() +{ + if (m_bIsInitialised) + SampleManager.ReacquireDigitalHandle(); +} + +#ifdef AUDIO_REFLECTIONS +void +cAudioManager::SetDynamicAcousticModelingStatus(bool8 status) +{ + m_bDynamicAcousticModelingStatus = status; +} +#endif + +bool8 +cAudioManager::CheckForAnAudioFileOnCD() +{ + return SampleManager.CheckForAnAudioFileOnCD(); +} + +char +cAudioManager::GetCDAudioDriveLetter() +{ + if (m_bIsInitialised) + return SampleManager.GetCDAudioDriveLetter(); + return '\0'; +} + +bool8 +cAudioManager::IsAudioInitialised() +{ + return m_bIsInitialised; +} + +#endif // GTA_PC + +void +cAudioManager::ServiceSoundEffects() +{ +#ifdef FIX_BUGS + if(CTimer::GetLogicalFramesPassed() != 0) +#endif + m_bReduceReleasingPriority = (m_FrameCounter++ % 5) == 0; + if (m_bIsPaused && !m_bWasPaused) { +#ifdef GTA_PS2 + if (m_bIsSurround) { + for (uint32 i = 0; i < NUM_CHANNELS_DTS_GENERIC; i++) + SampleManager.StopChannel(i); + + SampleManager.SetChannelFrequency(CHANNEL_DTS_POLICE_RADIO, 0); + SampleManager.SetChannelFrequency(CHANNEL_DTS_MISSION_AUDIO_1, 0); + SampleManager.SetChannelFrequency(CHANNEL_DTS_MISSION_AUDIO_2, 0); + SampleManager.SetChannelFrequency(CHANNEL_DTS_PLAYER_VEHICLE_ENGINE, 0); + } else { + for (uint32 i = 0; i < NUM_CHANNELS_GENERIC; i++) + SampleManager.StopChannel(i); + + SampleManager.SetChannelFrequency(CHANNEL_POLICE_RADIO, 0); + SampleManager.SetChannelFrequency(CHANNEL_MISSION_AUDIO_1, 0); + SampleManager.SetChannelFrequency(CHANNEL_MISSION_AUDIO_2, 0); + SampleManager.SetChannelFrequency(CHANNEL_PLAYER_VEHICLE_ENGINE, 0); + } +#else + for (uint32 i = 0; i < NUM_CHANNELS; i++) + SampleManager.StopChannel(i); +#endif + ClearRequestedQueue(); + if (m_nActiveQueue) { + m_nActiveQueue = 0; + ClearRequestedQueue(); + m_nActiveQueue = 1; + } else { + m_nActiveQueue = 1; + ClearRequestedQueue(); + m_nActiveQueue = 0; + } + ClearActiveSamples(); + } + m_nActiveQueue = m_nActiveQueue == 1 ? 0 : 1; +#ifdef AUDIO_REVERB + if(m_bIsSurround) ProcessReverb(); +#endif + ProcessSpecial(); + ClearRequestedQueue(); + InterrogateAudioEntities(); + m_sPedComments.Process(); + ServicePoliceRadio(); + ServiceCollisions(); + AddReleasingSounds(); + ProcessMissionAudio(); +#ifdef EXTERNAL_3D_SOUND + AdjustSamplesVolume(); +#endif + ProcessActiveQueues(); +#ifdef AUDIO_OAL + SampleManager.Service(); +#endif + for (int32 i = 0; i < m_sAudioScriptObjectManager.m_nScriptObjectEntityTotal; i++) { + cAudioScriptObject *object = (cAudioScriptObject *)m_asAudioEntities[m_sAudioScriptObjectManager.m_anScriptObjectEntityIndices[i]].m_pEntity; + delete object; + m_asAudioEntities[m_sAudioScriptObjectManager.m_anScriptObjectEntityIndices[i]].m_pEntity = nil; + DestroyEntity(m_sAudioScriptObjectManager.m_anScriptObjectEntityIndices[i]); + } + m_sAudioScriptObjectManager.m_nScriptObjectEntityTotal = 0; +} + +uint8 +cAudioManager::ComputeVolume(uint8 emittingVolume, float maxDistance, float distance) +{ + float minDistance; + uint8 newEmittingVolume; + + if (maxDistance <= 0.0f) + return 0; + + minDistance = maxDistance / 5.0f; + if (minDistance > distance) + return emittingVolume; + + newEmittingVolume = emittingVolume * SQR((maxDistance - minDistance - (distance - minDistance)) + / (maxDistance - minDistance)); + return Min(127, newEmittingVolume); +} + +void +cAudioManager::TranslateEntity(Const CVector *in, CVector *out) +{ + *out = MultiplyInverse(TheCamera.GetMatrix(), *in); +} + +Const static uint8 PanTable[64] = { 0, 3, 8, 12, 16, 19, 22, 24, 26, 28, 30, 31, 33, 34, 36, 37, 39, 40, 41, 42, 44, 45, 46, 47, 48, 49, 49, 50, 51, 52, 53, 53, + 54, 55, 55, 56, 56, 57, 57, 58, 58, 58, 59, 59, 59, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63}; + +int32 +cAudioManager::ComputeFrontRearMix(float dist, CVector *vec) +{ + int32 index = vec->y / (dist / 64.0f); + index = Min(63, ABS(index)); + + if (vec->y > 0.0f) + return Max(0, 63 - (int8)PanTable[index]); + return Min(127, PanTable[index] + 63); +} + +int32 +cAudioManager::ComputePan(float dist, CVector *vec) +{ + int32 index = vec->x / (dist / 64.0f); + index = Min(63, ABS(index)); + + if (vec->x > 0.0f) + return Max(20, 63 - (int8)PanTable[index]); + return Min(107, PanTable[index] + 63); +} + +uint32 +cAudioManager::ComputeDopplerEffectedFrequency(uint32 oldFreq, float position1, float position2, float speedMultiplier) +{ + uint32 newFreq = oldFreq; + if (!TheCamera.Get_Just_Switched_Status() && speedMultiplier != 0.0f) { + float dist = position2 - position1; + if (dist != 0.0f) { + float speedOfSource = (dist / m_nTimeSpent) * speedMultiplier; + if (m_fSpeedOfSound > Abs(speedOfSource)) { + speedOfSource = Clamp2(speedOfSource, 0.0f, 1.5f); + newFreq = (oldFreq * m_fSpeedOfSound) / (speedOfSource + m_fSpeedOfSound); + } + } + } + return newFreq; +} + +int32 +cAudioManager::RandomDisplacement(uint32 seed) +{ + int32 value; + + static bool8 bPos = TRUE; + static uint32 Adjustment = 0; + + if (seed == 0) + return 0; + + value = m_anRandomTable[(Adjustment + seed) % 5] % seed; + Adjustment += value; + + if (value % 2) + bPos = !bPos; + + if (!bPos) + value = -value; + return value; +} + +void +cAudioManager::InterrogateAudioEntities() +{ + for (uint32 i = 0; i < m_nAudioEntitiesCount; i++) { + ProcessEntity(m_aAudioEntityOrderList[i]); + m_asAudioEntities[m_aAudioEntityOrderList[i]].m_AudioEvents = 0; + } +} + +void +cAudioManager::AddSampleToRequestedQueue() +{ + uint32 finalPriority; + uint8 sampleIndex; +#ifdef AUDIO_REFLECTIONS + bool8 bReflections; +#endif + + if (m_sQueueSample.m_nSampleIndex < TOTAL_AUDIO_SAMPLES) { + finalPriority = m_sQueueSample.m_nPriority * (MAX_VOLUME - m_sQueueSample.m_nVolume); + sampleIndex = m_nRequestedCount[m_nActiveQueue]; + if (sampleIndex >= m_nActiveSamples) { + sampleIndex = m_aRequestedOrderList[m_nActiveQueue][m_nActiveSamples - 1]; + if (m_aRequestedQueue[m_nActiveQueue][sampleIndex].m_nFinalPriority <= finalPriority) + return; + } else + m_nRequestedCount[m_nActiveQueue]++; + m_sQueueSample.m_nFinalPriority = finalPriority; + m_sQueueSample.m_bIsPlayingFinished = FALSE; +#ifdef AUDIO_REFLECTIONS + if (m_sQueueSample.m_bIs2D || CCullZones::InRoomForAudio()) { + m_sQueueSample.m_bReflections = FALSE; + m_sQueueSample.m_nReflectionDelay = 0; + } + if (m_bDynamicAcousticModelingStatus && m_sQueueSample.m_nLoopCount > 0) { + bReflections = m_sQueueSample.m_bReflections; + } else { + bReflections = FALSE; + m_sQueueSample.m_nReflectionDelay = 0; + } + m_sQueueSample.m_bReflections = FALSE; + + if ( m_bIsSurround && m_sQueueSample.m_bIs2D ) + m_sQueueSample.m_nFrontRearPan = 30; +#ifdef AUDIO_REVERB + if (!m_bDynamicAcousticModelingStatus) + m_sQueueSample.m_bReverb = FALSE; +#endif +#endif + + m_aRequestedQueue[m_nActiveQueue][sampleIndex] = m_sQueueSample; + + AddDetailsToRequestedOrderList(sampleIndex); +#ifdef AUDIO_REFLECTIONS + if (bReflections) + AddReflectionsToRequestedQueue(); +#endif + } +} + +void +cAudioManager::AddDetailsToRequestedOrderList(uint8 sample) +{ + uint32 i = 0; + if (sample > 0) { + for (; i < sample; i++) { + if (m_aRequestedQueue[m_nActiveQueue][m_aRequestedOrderList[m_nActiveQueue][i]].m_nFinalPriority > + m_aRequestedQueue[m_nActiveQueue][sample].m_nFinalPriority) + break; + } + if (i < sample) + memmove(&m_aRequestedOrderList[m_nActiveQueue][i + 1], &m_aRequestedOrderList[m_nActiveQueue][i], m_nActiveSamples - i - 1); + } + m_aRequestedOrderList[m_nActiveQueue][i] = sample; +} + +#ifdef AUDIO_REFLECTIONS +void +cAudioManager::AddReflectionsToRequestedQueue() +{ +#ifdef FIX_BUGS + uint32 oldFreq = 0; +#else + uint32 oldFreq; +#endif + float reflectionDistance; + int32 noise; + uint8 emittingVolume; + + uint32 oldCounter = m_sQueueSample.m_nCounter; + float oldDist = m_sQueueSample.m_fDistance; + CVector oldPos = m_sQueueSample.m_vecPos; +#ifndef USE_TIME_SCALE_FOR_AUDIO + if ( CTimer::GetIsSlowMotionActive() ) { + emittingVolume = m_sQueueSample.m_nVolume; + oldFreq = m_sQueueSample.m_nFrequency; + } else +#endif + emittingVolume = (9 * m_sQueueSample.m_nVolume) >> 4; + m_sQueueSample.m_MaxDistance /= 2.0f; + + uint32 halfOldFreq = oldFreq >> 1; + + for (uint32 i = 0; i < ARRAY_SIZE(m_afReflectionsDistances); i++) { +#ifndef USE_TIME_SCALE_FOR_AUDIO + if ( CTimer::GetIsSlowMotionActive() ) + m_afReflectionsDistances[i] = (m_anRandomTable[i % 4] % 3) * 100.0f / 8.0f; +#endif + + reflectionDistance = m_afReflectionsDistances[i]; + if (reflectionDistance > 0.0f && reflectionDistance < 100.0f && reflectionDistance < m_sQueueSample.m_MaxDistance) { +#ifndef USE_TIME_SCALE_FOR_AUDIO + m_sQueueSample.m_nReflectionDelay = CTimer::GetIsSlowMotionActive() ? (reflectionDistance * 800.0f / 1029.0f) : (reflectionDistance * 500.0f / 1029.0f); +#else + m_sQueueSample.m_nReflectionDelay = reflectionDistance * 500.0f / 1029.0f; +#endif + if (m_sQueueSample.m_nReflectionDelay > 3) { + m_sQueueSample.m_fDistance = m_afReflectionsDistances[i]; + SET_EMITTING_VOLUME(emittingVolume); + m_sQueueSample.m_nVolume = ComputeVolume(emittingVolume, m_sQueueSample.m_MaxDistance, m_sQueueSample.m_fDistance); + + if (m_sQueueSample.m_nVolume > emittingVolume >> 4) { + m_sQueueSample.m_nCounter = oldCounter + ((i + 1) << 8); + if (m_sQueueSample.m_nLoopCount > 0) { +#ifndef USE_TIME_SCALE_FOR_AUDIO + if ( CTimer::GetIsSlowMotionActive() ) { + m_sQueueSample.m_nFrequency = halfOldFreq + ((halfOldFreq * i) / ARRAY_SIZE(m_afReflectionsDistances)); + } else +#endif + { + noise = RandomDisplacement(m_sQueueSample.m_nFrequency >> 5); + if (noise > 0) + m_sQueueSample.m_nFrequency -= noise; + else + m_sQueueSample.m_nFrequency += noise; + } + } + m_sQueueSample.m_nPriority += 20; + m_sQueueSample.m_vecPos = m_avecReflectionsPos[i]; + AddSampleToRequestedQueue(); + } + } + } + } + m_sQueueSample.m_vecPos = oldPos; + m_sQueueSample.m_fDistance = oldDist; +} + +void +cAudioManager::UpdateReflections() +{ + CVector camPos; + CColPoint colpoint; + CEntity *ent; + +#if GTA_VERSION < GTAVC_PC_10 + if (m_FrameCounter % 8 == 0) { + camPos = TheCamera.GetPosition(); + m_avecReflectionsPos[0] = camPos; + m_avecReflectionsPos[0].y += 50.0f; + if (CWorld::ProcessLineOfSight(camPos, m_avecReflectionsPos[0], colpoint, ent, true, false, false, true, false, true, true)) + m_afReflectionsDistances[0] = Distance(camPos, colpoint.point); + else + m_afReflectionsDistances[0] = 50.0f; + } else if ((m_FrameCounter + 1) % 8 == 0) { + camPos = TheCamera.GetPosition(); + m_avecReflectionsPos[1] = camPos; + m_avecReflectionsPos[1].y -= 50.0f; + if (CWorld::ProcessLineOfSight(camPos, m_avecReflectionsPos[1], colpoint, ent, true, false, false, true, false, true, true)) + m_afReflectionsDistances[1] = Distance(camPos, colpoint.point); + else + m_afReflectionsDistances[1] = 50.0f; + } else if ((m_FrameCounter + 2) % 8 == 0) { + camPos = TheCamera.GetPosition(); + m_avecReflectionsPos[2] = camPos; + m_avecReflectionsPos[2].x -= 50.0f; + if (CWorld::ProcessLineOfSight(camPos, m_avecReflectionsPos[2], colpoint, ent, true, false, false, true, false, true, true)) + m_afReflectionsDistances[2] = Distance(camPos, colpoint.point); + else + m_afReflectionsDistances[2] = 50.0f; + } else if ((m_FrameCounter + 3) % 8 == 0) { + camPos = TheCamera.GetPosition(); + m_avecReflectionsPos[3] = camPos; + m_avecReflectionsPos[3].x += 50.0f; + if (CWorld::ProcessLineOfSight(camPos, m_avecReflectionsPos[3], colpoint, ent, true, false, false, true, false, true, true)) + m_afReflectionsDistances[3] = Distance(camPos, colpoint.point); + else + m_afReflectionsDistances[3] = 50.0f; + } else if ((m_FrameCounter + 4) % 8 == 0) { + camPos = TheCamera.GetPosition(); + m_avecReflectionsPos[4] = camPos; + m_avecReflectionsPos[4].z += 50.0f; + if (CWorld::ProcessVerticalLine(camPos, m_avecReflectionsPos[4].z, colpoint, ent, true, false, false, false, true, false, nil)) + m_afReflectionsDistances[4] = colpoint.point.z - camPos.z; + else + m_afReflectionsDistances[4] = 50.0f; + } +#else + if (m_FrameCounter % 8 == 0) { + camPos = TheCamera.GetPosition(); + m_avecReflectionsPos[0] = camPos; + m_avecReflectionsPos[0].y += 100.f; + if (CWorld::ProcessLineOfSight(camPos, m_avecReflectionsPos[0], colpoint, ent, true, false, false, true, false, true, true)) + m_afReflectionsDistances[0] = Distance(camPos, colpoint.point); + else + m_afReflectionsDistances[0] = 100.0f; + } else if ((m_FrameCounter + 1) % 8 == 0) { + camPos = TheCamera.GetPosition(); + m_avecReflectionsPos[1] = camPos; + m_avecReflectionsPos[1].y -= 100.0f; + if (CWorld::ProcessLineOfSight(camPos, m_avecReflectionsPos[1], colpoint, ent, true, false, false, true, false, true, true)) + m_afReflectionsDistances[1] = Distance(camPos, colpoint.point); + else + m_afReflectionsDistances[1] = 100.0f; + } else if ((m_FrameCounter + 2) % 8 == 0) { + camPos = TheCamera.GetPosition(); + m_avecReflectionsPos[2] = camPos; + m_avecReflectionsPos[2].x -= 100.0f; + if (CWorld::ProcessLineOfSight(camPos, m_avecReflectionsPos[2], colpoint, ent, true, false, false, true, false, true, true)) + m_afReflectionsDistances[2] = Distance(camPos, colpoint.point); + else + m_afReflectionsDistances[2] = 100.0f; + } else if ((m_FrameCounter + 3) % 8 == 0) { + camPos = TheCamera.GetPosition(); + m_avecReflectionsPos[3] = camPos; + m_avecReflectionsPos[3].x += 100.0f; + if (CWorld::ProcessLineOfSight(camPos, m_avecReflectionsPos[3], colpoint, ent, true, false, false, true, false, true, true)) + m_afReflectionsDistances[3] = Distance(camPos, colpoint.point); + else + m_afReflectionsDistances[3] = 100.0f; + } else if ((m_FrameCounter + 4) % 8 == 0) { + camPos = TheCamera.GetPosition(); + camPos.y += 1.0f; + m_avecReflectionsPos[4] = camPos; + m_avecReflectionsPos[4].z += 100.0f; + if (CWorld::ProcessVerticalLine(camPos, m_avecReflectionsPos[4].z, colpoint, ent, true, false, false, false, true, false, nil)) + m_afReflectionsDistances[4] = colpoint.point.z - camPos.z; + else + m_afReflectionsDistances[4] = 100.0f; + } else if ((m_FrameCounter + 5) % 8 == 0) { + camPos = TheCamera.GetPosition(); + camPos.y -= 1.0f; + m_avecReflectionsPos[5] = camPos; + m_avecReflectionsPos[5].z += 100.0f; + if (CWorld::ProcessVerticalLine(camPos, m_avecReflectionsPos[5].z, colpoint, ent, true, false, false, false, true, false, nil)) + m_afReflectionsDistances[5] = colpoint.point.z - camPos.z; + else + m_afReflectionsDistances[5] = 100.0f; + } else if ((m_FrameCounter + 6) % 8 == 0) { + camPos = TheCamera.GetPosition(); + camPos.x -= 1.0f; + m_avecReflectionsPos[6] = camPos; + m_avecReflectionsPos[6].z += 100.0f; + if (CWorld::ProcessVerticalLine(camPos, m_avecReflectionsPos[6].z, colpoint, ent, true, false, false, false, true, false, nil)) + m_afReflectionsDistances[6] = colpoint.point.z - camPos.z; + else + m_afReflectionsDistances[6] = 100.0f; + } else if ((m_FrameCounter + 7) % 8 == 0) { + camPos = TheCamera.GetPosition(); + camPos.x += 1.0f; + m_avecReflectionsPos[7] = camPos; + m_avecReflectionsPos[7].z += 100.0f; + if (CWorld::ProcessVerticalLine(camPos, m_avecReflectionsPos[7].z, colpoint, ent, true, false, false, false, true, false, nil)) + m_afReflectionsDistances[7] = colpoint.point.z - camPos.z; + else + m_afReflectionsDistances[7] = 100.0f; + } +#endif +} +#endif // AUDIO_REFLECTIONS + +void +cAudioManager::AddReleasingSounds() +{ + // in case someone would want to increase it +#ifdef FIX_BUGS + bool8 toProcess[NUM_CHANNELS_GENERIC]; +#else + bool8 toProcess[44]; +#endif + + uint8 queue = m_nActiveQueue == 0 ? 1 : 0; + + for (uint8 i = 0; i < m_nRequestedCount[queue]; i++) { + tSound &sample = m_aRequestedQueue[queue][m_aRequestedOrderList[queue][i]]; + if (sample.m_bIsPlayingFinished) + continue; + + toProcess[i] = FALSE; + for (uint8 j = 0; j < m_nRequestedCount[m_nActiveQueue]; j++) { + if (sample.m_nEntityIndex == m_aRequestedQueue[m_nActiveQueue][m_aRequestedOrderList[m_nActiveQueue][j]].m_nEntityIndex && + sample.m_nCounter == m_aRequestedQueue[m_nActiveQueue][m_aRequestedOrderList[m_nActiveQueue][j]].m_nCounter) { + toProcess[i] = TRUE; + break; + } + } + if(!toProcess[i]) { +#ifdef AUDIO_REFLECTIONS + if(sample.m_nCounter <= 255 || sample.m_nReflectionDelay == 0) // check if not delayed reflection +#endif + { +#ifdef ATTACH_RELEASING_SOUNDS_TO_ENTITIES + if (sample.m_nCounter <= 255 && !sample.m_bIs2D) { // check if not reflection and is a 3D sound + CEntity* entity = (CEntity*)GetEntityPointer(sample.m_nEntityIndex); + if (entity && m_asAudioEntities[sample.m_nEntityIndex].m_nType == AUDIOTYPE_PHYSICAL) { + sample.m_vecPos = entity->GetPosition(); + float oldDistance = sample.m_fDistance; + sample.m_fDistance = Sqrt(GetDistanceSquared(sample.m_vecPos)); + if (sample.m_nSampleIndex >= SAMPLEBANK_PED_START && sample.m_nSampleIndex <= SAMPLEBANK_PED_END) { // check if it's ped comment + uint8 vol; + if (CWorld::GetIsLineOfSightClear(TheCamera.GetPosition(), sample.m_vecPos, true, false, false, false, false, false)) + vol = PED_COMMENT_VOLUME; + else + vol = PED_COMMENT_VOLUME_BEHIND_WALL; +#ifdef EXTERNAL_3D_SOUND + sample.m_nEmittingVolume = vol; +#endif + sample.m_nVolume = ComputeVolume(vol, sample.m_MaxDistance, sample.m_fDistance); + } else { + // calculate new volume with changed distance + float volumeDiff = sq((sample.m_MaxDistance - sample.m_fDistance) / (sample.m_MaxDistance - oldDistance)); + if (volumeDiff > 0.0f) { + uint8 newVolume = volumeDiff * sample.m_nVolume; + if (sample.m_nVolumeChange > 0) + sample.m_nVolumeChange = volumeDiff * sample.m_nVolumeChange; +#if defined(FIX_BUGS) && defined(EXTERNAL_3D_SOUND) + if (sample.m_nEmittingVolumeChange > 0) + sample.m_nEmittingVolumeChange = volumeDiff * sample.m_nEmittingVolumeChange; +#endif + sample.m_nVolume = Min(MAX_VOLUME, newVolume); + } + } + if (sample.m_nVolume == 0) + sample.m_nFramesToPlay = 0; + } + } +#endif +#ifdef FIX_BUGS + if (sample.m_nFramesToPlay <= 0) + continue; + if (sample.m_nLoopCount == 0) { + if (sample.m_nVolumeChange == -1) { + sample.m_nVolumeChange = sample.m_nVolume / sample.m_nFramesToPlay; + if (sample.m_nVolumeChange <= 0) + sample.m_nVolumeChange = 1; +#ifdef EXTERNAL_3D_SOUND + sample.m_nEmittingVolumeChange = sample.m_nEmittingVolume / sample.m_nFramesToPlay; + if (sample.m_nEmittingVolumeChange <= 0) + sample.m_nEmittingVolumeChange = 1; +#endif + } + if (sample.m_nVolume <= sample.m_nVolumeChange * CTimer::GetTimeStepFix()) { + sample.m_nFramesToPlay = 0; + continue; + } + sample.m_nVolume -= sample.m_nVolumeChange * CTimer::GetTimeStepFix(); +#ifdef EXTERNAL_3D_SOUND + if (sample.m_nEmittingVolume <= sample.m_nEmittingVolumeChange * CTimer::GetTimeStepFix()) { + sample.m_nFramesToPlay = 0; + continue; + } + sample.m_nEmittingVolume -= sample.m_nEmittingVolumeChange * CTimer::GetTimeStepFix(); +#endif + } + sample.m_nFramesToPlay -= CTimer::GetTimeStepFix(); + if (sample.m_nFramesToPlay < 0) + sample.m_nFramesToPlay = 0; +#else + if (sample.m_nFramesToPlay == 0) + continue; + if (sample.m_nLoopCount == 0) { + if (sample.m_nVolumeChange == -1) { + sample.m_nVolumeChange = sample.m_nVolume / sample.m_nFramesToPlay; + if (sample.m_nVolumeChange <= 0) + sample.m_nVolumeChange = 1; + } + if (sample.m_nVolume <= sample.m_nVolumeChange) { + sample.m_nFramesToPlay = 0; + continue; + } + sample.m_nVolume -= sample.m_nVolumeChange; + } + sample.m_nFramesToPlay--; +#endif + if (m_bReduceReleasingPriority) { + if (sample.m_nPriority < 20) + sample.m_nPriority++; + } + sample.m_bStatic = FALSE; + } + memcpy(&m_sQueueSample, &sample, sizeof(tSound)); + AddSampleToRequestedQueue(); + } + } +} + +void +cAudioManager::ProcessActiveQueues() +{ + bool8 flag; + float position2; + float position1; + + uint32 samplesPerFrame; + uint32 samplesToPlay; + +#ifdef EXTERNAL_3D_SOUND + float x; + float usedX; + float usedY; + float usedZ; +#endif + + uint8 vol; + uint8 emittingVol; + CVector position; + + bool8 isPhoneCall; + uint8 channelOffset = 0; + +#ifdef EXTERNAL_3D_SOUND + #define WORKING_VOLUME_FIELD m_nEmittingVolume +#else + #define WORKING_VOLUME_FIELD m_nVolume +#endif + +#ifdef USE_TIME_SCALE_FOR_AUDIO + float timeScale = m_bIsPaused ? 1.0f : CTimer::GetTimeScale(); +#endif + + for (uint8 i = 0; i < m_nActiveSamples; i++) { + m_aRequestedQueue[m_nActiveQueue][i].m_bIsBeingPlayed = FALSE; + m_asActiveSamples[i].m_bIsBeingPlayed = FALSE; + } + + for (uint8 i = 0; i < m_nRequestedCount[m_nActiveQueue]; i++) { + tSound &sample = m_aRequestedQueue[m_nActiveQueue][m_aRequestedOrderList[m_nActiveQueue][i]]; + if (sample.m_nSampleIndex != NO_SAMPLE) { + for (uint8 j = 0; j < m_nActiveSamples; j++) { + if (sample.m_nEntityIndex == m_asActiveSamples[j].m_nEntityIndex && sample.m_nCounter == m_asActiveSamples[j].m_nCounter && + sample.m_nSampleIndex == m_asActiveSamples[j].m_nSampleIndex) { + if (sample.m_nLoopCount > 0) { + if (m_FrameCounter & 1) + flag = !!(j & 1); + else + flag = !(j & 1); + + if (flag && !SampleManager.GetChannelUsedFlag(j)) { + sample.m_bIsPlayingFinished = TRUE; + m_asActiveSamples[j].m_bIsPlayingFinished = TRUE; + m_asActiveSamples[j].m_nSampleIndex = NO_SAMPLE; + m_asActiveSamples[j].m_nEntityIndex = AEHANDLE_NONE; + continue; + } + if (sample.m_nFramesToPlay == 0) + sample.m_nFramesToPlay = 1; + } + sample.m_bIsBeingPlayed = TRUE; + m_asActiveSamples[j].m_bIsBeingPlayed = TRUE; + sample.m_nVolumeChange = -1; + if (!sample.m_bStatic) { + if (sample.m_bIs2D) { + emittingVol = m_bDoubleVolume ? 2 * Min(63, sample.WORKING_VOLUME_FIELD) : sample.WORKING_VOLUME_FIELD; +#ifdef USE_TIME_SCALE_FOR_AUDIO + SampleManager.SetChannelFrequency(j, sample.m_nFrequency * timeScale); +#else + SampleManager.SetChannelFrequency(j, sample.m_nFrequency); +#endif +#ifdef EXTERNAL_3D_SOUND + SampleManager.SetChannelEmittingVolume(j, emittingVol); +#else + SampleManager.SetChannelPan(j, sample.m_nPan); + SampleManager.SetChannelVolume(j, emittingVol); +#endif + } else { + position2 = sample.m_fDistance; + position1 = m_asActiveSamples[j].m_fDistance; + m_asActiveSamples[j].m_fDistance = sample.m_fDistance; + sample.m_nFrequency = ComputeDopplerEffectedFrequency(sample.m_nFrequency, position1, position2, sample.m_fSpeedMultiplier); + if (sample.m_nFrequency != m_asActiveSamples[j].m_nFrequency) { + uint32 freq = Clamp2((int32)sample.m_nFrequency, (int32)m_asActiveSamples[j].m_nFrequency, 6000); + m_asActiveSamples[j].m_nFrequency = freq; +#ifdef USE_TIME_SCALE_FOR_AUDIO + SampleManager.SetChannelFrequency(j, freq * timeScale); +#else + SampleManager.SetChannelFrequency(j, freq); +#endif + } +#ifdef EXTERNAL_3D_SOUND + if (sample.m_nEmittingVolume != m_asActiveSamples[j].m_nEmittingVolume) { + vol = Clamp2((int8)sample.m_nEmittingVolume, (int8)m_asActiveSamples[j].m_nEmittingVolume, 10); +#else + if (sample.m_nVolume != m_asActiveSamples[j].m_nVolume) { + vol = Clamp2((int8)sample.m_nVolume, (int8)m_asActiveSamples[j].m_nVolume, 10); +#endif + emittingVol = m_bDoubleVolume ? 2 * Min(63, vol) : vol; + + isPhoneCall = FALSE; + for (int32 k = 0; k < MISSION_AUDIO_SLOTS; k++) { + if (m_bIsMissionAudioPhoneCall[k]) { + isPhoneCall = TRUE; + break; + } + } + if (isPhoneCall) { + emittingVol = (emittingVol * m_nGlobalSfxVolumeMultiplier) / 127; + } else { + if (m_nGlobalSfxVolumeMultiplier < 127) + emittingVol = (emittingVol * m_nGlobalSfxVolumeMultiplier) / 127; + } + +#ifdef EXTERNAL_3D_SOUND + SampleManager.SetChannelEmittingVolume(j, emittingVol); + m_asActiveSamples[j].m_nEmittingVolume = vol; +#else + SampleManager.SetChannelVolume(j, emittingVol); + m_asActiveSamples[j].m_nVolume = vol; +#endif + } + TranslateEntity(&sample.m_vecPos, &position); +#ifdef EXTERNAL_3D_SOUND + SampleManager.SetChannel3DPosition(j, position.x, position.y, position.z); + SampleManager.SetChannel3DDistances(j, sample.m_MaxDistance, 0.25f * sample.m_MaxDistance); +#else + sample.m_nPan = ComputePan(sample.m_fDistance, &position); + SampleManager.SetChannelPan(j, sample.m_nPan); +#endif + } +#if !defined(GTA_PS2) || defined(AUDIO_REVERB) + SampleManager.SetChannelReverbFlag(j, sample.m_bReverb); +#endif + break; //continue for i + } + sample.m_bIsBeingPlayed = FALSE; + m_asActiveSamples[j].m_bIsBeingPlayed = FALSE; + //continue for j + } + } + } + } + for (uint8 i = 0; i < m_nActiveSamples; i++) { + if (m_asActiveSamples[i].m_nSampleIndex != NO_SAMPLE && !m_asActiveSamples[i].m_bIsBeingPlayed) { + SampleManager.StopChannel(i); + m_asActiveSamples[i].m_nSampleIndex = NO_SAMPLE; + m_asActiveSamples[i].m_nEntityIndex = AEHANDLE_NONE; + } + } + for (uint8 i = 0; i < m_nRequestedCount[m_nActiveQueue]; i++) { + tSound &sample = m_aRequestedQueue[m_nActiveQueue][m_aRequestedOrderList[m_nActiveQueue][i]]; + if (!sample.m_bIsBeingPlayed && !sample.m_bIsPlayingFinished && m_asAudioEntities[sample.m_nEntityIndex].m_bIsUsed && sample.m_nSampleIndex < NO_SAMPLE) { +#ifdef AUDIO_REFLECTIONS + if (sample.m_nCounter > 255 && sample.m_nLoopCount > 0 && sample.m_nReflectionDelay > 0) { // check if reflection + sample.m_nReflectionDelay--; + sample.m_nFramesToPlay = 1; + } else +#endif + { + for (uint8 j = 0; j < m_nActiveSamples; j++) { + uint8 k = (j + m_nChannelOffset) % m_nActiveSamples; + if (!m_asActiveSamples[k].m_bIsBeingPlayed) { + if (sample.m_nLoopCount > 0) { + samplesPerFrame = sample.m_nFrequency / m_nTimeSpent; + samplesToPlay = sample.m_nLoopCount * SampleManager.GetSampleLength(sample.m_nSampleIndex); + if (samplesPerFrame == 0) + continue; + sample.m_nFramesToPlay = samplesToPlay / samplesPerFrame + 1; + } + memcpy(&m_asActiveSamples[k], &sample, sizeof(tSound)); + if (!m_asActiveSamples[k].m_bIs2D) { + TranslateEntity(&m_asActiveSamples[k].m_vecPos, &position); +#ifndef EXTERNAL_3D_SOUND + m_asActiveSamples[j].m_nPan = ComputePan(m_asActiveSamples[j].m_fDistance, &position); +#endif + } + emittingVol = m_bDoubleVolume ? 2 * Min(63, m_asActiveSamples[j].WORKING_VOLUME_FIELD) : m_asActiveSamples[j].WORKING_VOLUME_FIELD; +#ifdef GTA_PS2 + { + SampleManager.InitialiseChannel(k, m_asActiveSamples[k].m_nSampleIndex, m_asActiveSamples[k].m_nBankIndex); +#else + if (SampleManager.InitialiseChannel(k, m_asActiveSamples[k].m_nSampleIndex, m_asActiveSamples[k].m_nBankIndex)) { +#endif +#ifdef USE_TIME_SCALE_FOR_AUDIO + SampleManager.SetChannelFrequency(k, m_asActiveSamples[k].m_nFrequency * timeScale); +#else + SampleManager.SetChannelFrequency(k, m_asActiveSamples[k].m_nFrequency); +#endif + isPhoneCall = FALSE; + for (int32 l = 0; l < MISSION_AUDIO_SLOTS; l++) { + if (m_bIsMissionAudioPhoneCall[l]) { + isPhoneCall = TRUE; + break; + } + } + if (!isPhoneCall || m_asActiveSamples[k].m_bIs2D) { + if (m_nGlobalSfxVolumeMultiplier < 127) + emittingVol = (emittingVol * m_nGlobalSfxVolumeMultiplier) / 127; + vol = emittingVol; + } else { + vol = (emittingVol * m_nGlobalSfxVolumeMultiplier) / 127; + } +#ifdef EXTERNAL_3D_SOUND + SampleManager.SetChannelEmittingVolume(k, vol); +#else + SampleManager.SetChannelVolume(j, emittingVol); + SampleManager.SetChannelPan(j, m_asActiveSamples[j].m_nPan); +#endif +#ifndef GTA_PS2 + SampleManager.SetChannelLoopPoints(k, m_asActiveSamples[k].m_nLoopStart, m_asActiveSamples[k].m_nLoopEnd); +#endif + SampleManager.SetChannelLoopCount(k, m_asActiveSamples[k].m_nLoopCount); +#if !defined(GTA_PS2) || defined(AUDIO_REVERB) + SampleManager.SetChannelReverbFlag(k, m_asActiveSamples[k].m_bReverb); +#endif +#ifdef EXTERNAL_3D_SOUND + if (m_asActiveSamples[k].m_bIs2D) { + uint8 offset = m_asActiveSamples[k].m_nPan; + if (offset == 63) + x = 0.0f; + else if (offset >= 63) + x = (offset - 63) * 1000.0f / 63; + else + x = -(63 - offset) * 1000.0f / 63; //same like line below + usedX = x; + usedY = 0.0f; + usedZ = 0.0f; + m_asActiveSamples[k].m_MaxDistance = 100000.0f; + } else { + usedX = position.x; + usedY = position.y; + usedZ = position.z; + } + SampleManager.SetChannel3DPosition(k, usedX, usedY, usedZ); + SampleManager.SetChannel3DDistances(k, m_asActiveSamples[k].m_MaxDistance, 0.25f * m_asActiveSamples[k].m_MaxDistance); +#endif + SampleManager.StartChannel(k); + } + m_asActiveSamples[k].m_bIsBeingPlayed = TRUE; + channelOffset++; + sample.m_bIsBeingPlayed = TRUE; + sample.m_nVolumeChange = -1; + break; + } + } + } + } + } + +#ifdef GTA_PS2 + m_nChannelOffset += channelOffset; +#endif + m_nChannelOffset %= m_nActiveSamples; + +#ifdef USE_TIME_SCALE_FOR_AUDIO + for (uint8 i = 0; i < m_nActiveSamples; i++) { + if (m_asActiveSamples[i].m_nSampleIndex != NO_SAMPLE && m_asActiveSamples[i].m_bIsBeingPlayed) + SampleManager.SetChannelFrequency(i, m_asActiveSamples[i].m_nFrequency * timeScale); + } +#endif + +#undef WORKING_VOLUME_FIELD +} + +void +cAudioManager::ClearRequestedQueue() +{ + for (uint8 i = 0; i < m_nActiveSamples; i++) + m_aRequestedOrderList[m_nActiveQueue][i] = m_nActiveSamples; + m_nRequestedCount[m_nActiveQueue] = 0; +} + +void +cAudioManager::ClearActiveSamples() +{ + for (uint8 i = 0; i < m_nActiveSamples; i++) { + m_asActiveSamples[i].m_nEntityIndex = AEHANDLE_NONE; + m_asActiveSamples[i].m_nCounter = 0; + m_asActiveSamples[i].m_nSampleIndex = NO_SAMPLE; + m_asActiveSamples[i].m_nBankIndex = INVALID_SFX_BANK; + m_asActiveSamples[i].m_bIs2D = FALSE; + m_asActiveSamples[i].m_nPriority = 5; + m_asActiveSamples[i].m_nFrequency = 0; + m_asActiveSamples[i].m_nVolume = 0; +#ifdef EXTERNAL_3D_SOUND + m_asActiveSamples[i].m_nEmittingVolume = 0; +#endif + m_asActiveSamples[i].m_fDistance = 0.0f; + m_asActiveSamples[i].m_bIsBeingPlayed = FALSE; + m_asActiveSamples[i].m_bIsPlayingFinished = FALSE; + m_asActiveSamples[i].m_nLoopCount = 1; +#ifndef GTA_PS2 + m_asActiveSamples[i].m_nLoopStart = 0; + m_asActiveSamples[i].m_nLoopEnd = -1; +#endif + m_asActiveSamples[i].m_fSpeedMultiplier = 0.0f; + m_asActiveSamples[i].m_MaxDistance = 200.0f; + m_asActiveSamples[i].m_nPan = 63; + m_asActiveSamples[i].m_bStatic = FALSE; + m_asActiveSamples[i].m_nFinalPriority = 0; + m_asActiveSamples[i].m_nFramesToPlay = 0; + m_asActiveSamples[i].m_nVolumeChange = -1; + m_asActiveSamples[i].m_vecPos = CVector(0.0f, 0.0f, 0.0f); +#ifdef AUDIO_REVERB + m_asActiveSamples[i].m_bReverb = FALSE; +#endif // AUDIO_REVERB +#ifdef AUDIO_REFLECTIONS + m_asActiveSamples[i].m_nReflectionDelay = 0; + m_asActiveSamples[i].m_bReflections = FALSE; +#endif // AUDIO_REFLECTIONS + } +} + +void +cAudioManager::GenerateIntegerRandomNumberTable() +{ + for (uint32 i = 0; i < ARRAY_SIZE(m_anRandomTable); i++) + m_anRandomTable[i] = myrand(); +} + +#ifdef GTA_PS2 +void +cAudioManager::LoadBankIfNecessary(uint8 bank) +{ + if(!SampleManager.IsSampleBankLoaded(bank)) + SampleManager.LoadSampleBank(bank); +} +#endif + +#ifdef EXTERNAL_3D_SOUND +void +cAudioManager::AdjustSamplesVolume() +{ + for (uint8 i = 0; i < m_nRequestedCount[m_nActiveQueue]; i++) { + tSound *pSample = &m_aRequestedQueue[m_nActiveQueue][m_aRequestedOrderList[m_nActiveQueue][i]]; + + if (!pSample->m_bIs2D) + pSample->m_nEmittingVolume = ComputeEmittingVolume(pSample->m_nEmittingVolume, pSample->m_MaxDistance, pSample->m_fDistance); + } +} + +uint8 +cAudioManager::ComputeEmittingVolume(uint8 emittingVolume, float maxDistance, float distance) +{ + float minDistance = maxDistance / 4.0f; + float diffDistance = maxDistance - minDistance; + if (distance > diffDistance) + return (minDistance - (distance - diffDistance)) * (float)emittingVolume / minDistance; + return emittingVolume; +} +#endif diff --git a/src/miami/audio/AudioManager.h b/src/miami/audio/AudioManager.h new file mode 100644 index 00000000..49edcfec --- /dev/null +++ b/src/miami/audio/AudioManager.h @@ -0,0 +1,649 @@ +#pragma once + +#include "audio_enums.h" +#include "AudioCollision.h" +#include "PolRadio.h" +#include "VehicleModelInfo.h" +#include "Vehicle.h" + +class tSound +{ +public: + int32 m_nEntityIndex; // audio entity index + uint32 m_nCounter; // I'm not sure what this is but it looks like a virtual counter to determine the same sound in queue + // Values higher than 255 are used by reflections + uint32 m_nSampleIndex; // An index of sample from AudioSamples.h + uint8 m_nBankIndex; // A sound bank index. IDK what's the point of it here since samples are hardcoded anyway + bool8 m_bIs2D; // If TRUE then sound is played in 2D space (such as frontend or police radio) + uint32 m_nPriority; // The multiplier for the sound priority (see m_nFinalPriority below). Lesser value means higher priority + uint32 m_nFrequency; // Sound frequency, plain and simple + uint8 m_nVolume; // Sound volume (0..127), only used as an actual volume without EXTERNAL_3D_SOUND (see m_nEmittingVolume) + float m_fDistance; // Distance to camera (useless if m_bIs2D == TRUE) + uint32 m_nLoopCount; // 0 - always loop, 1 - don't loop, other values never seen +#ifndef GTA_PS2 + // Loop offsets + uint32 m_nLoopStart; + int32 m_nLoopEnd; +#endif +#ifdef EXTERNAL_3D_SOUND + uint8 m_nEmittingVolume; // The volume in 3D space, provided to 3D audio engine +#endif + float m_fSpeedMultiplier; // Used for doppler effect. 0.0f - unaffected by doppler + float m_MaxDistance; // The maximum distance at which sound could be heard. Minimum distance = MaxDistance / 5 or MaxDistance / 4 in case of emitting volume (useless if m_bIs2D == TRUE) + bool8 m_bStatic; // If TRUE then sound parameters cannot be changed during playback (frequency, position, etc.) + CVector m_vecPos; // Position of sound in 3D space. Unused if m_bIs2D == TRUE +#if !defined(GTA_PS2) || defined(AUDIO_REVERB) // GTA_PS2 because this field exists on mobile but not on PS2 + bool8 m_bReverb; // Toggles reverb effect +#endif +#ifdef AUDIO_REFLECTIONS + uint8 m_nReflectionDelay; // Number of frames before reflection could be played. This is calculated internally by AudioManager and shouldn't be set by queued sample + bool8 m_bReflections; // Add sound reflections +#endif + uint8 m_nPan; // Sound panning (0-127). Controls the volume of the playback coming from left and right speaker. Calculated internally unless m_bIs2D==TRUE. + // 0 = L 100% R 0% + // 63 = L 100% R 100% + // 127 = L 0% R 100% + uint8 m_nFrontRearPan; // Used on PS2 for surround panning +#ifndef FIX_BUGS + uint32 m_nFramesToPlay; // Number of frames the sound would be played (if it stops being queued). + // This one is being set by queued sample for looping sounds, otherwise calculated inside AudioManager +#else + float m_nFramesToPlay; // Made into float for high fps fix +#endif + + // all fields below are internal to AudioManager calculations and aren't set by queued sample + bool8 m_bIsBeingPlayed; // Set to TRUE when the sound was added or changed on current frame to avoid it being overwritten + bool8 m_bIsPlayingFinished; // Not sure about the name. Set to TRUE when sampman channel becomes free + uint32 m_nFinalPriority; // Actual value used to compare priority, calculated using volume and m_nPriority. Lesser value means higher priority + int8 m_nVolumeChange; // How much m_nVolume should reduce per each frame. +#if defined(FIX_BUGS) && defined(EXTERNAL_3D_SOUND) + int8 m_nEmittingVolumeChange; // same as above but for m_nEmittingVolume +#endif +}; + +VALIDATE_SIZE(tSound, 96); + +class CPhysical; +class CAutomobile; + +class tAudioEntity +{ +public: + eAudioType m_nType; + void *m_pEntity; + bool8 m_bIsUsed; + bool8 m_bStatus; + int16 m_awAudioEvent[NUM_AUDIOENTITY_EVENTS]; + float m_afVolume[NUM_AUDIOENTITY_EVENTS]; + uint8 m_AudioEvents; +}; + +VALIDATE_SIZE(tAudioEntity, 40); + +class tPedComment +{ +public: + uint32 m_nSampleIndex; + int32 m_nEntityIndex; + CVector m_vecPos; + float m_fDistance; + uint8 m_nVolume; + int8 m_nLoadingTimeout; // how many iterations we gonna wait until dropping the sample if it's still not loaded (only useful on PS2) +#if defined(EXTERNAL_3D_SOUND) && defined(FIX_BUGS) + uint8 m_nEmittingVolume; +#endif +}; + +VALIDATE_SIZE(tPedComment, 28); + +class cPedComments +{ +public: + tPedComment m_aPedCommentQueue[NUM_SOUND_QUEUES][NUM_PED_COMMENTS_SLOTS]; + uint8 m_aPedCommentOrderList[NUM_SOUND_QUEUES][NUM_PED_COMMENTS_SLOTS]; + uint8 m_nPedCommentCount[NUM_SOUND_QUEUES]; + uint8 m_nActiveQueue; +#ifdef GTA_PC + bool8 m_bDelay; + uint32 m_nDelayTimer; +#endif + + cPedComments() + { + for (int i = 0; i < NUM_PED_COMMENTS_SLOTS; i++) + for (int j = 0; j < NUM_SOUND_QUEUES; j++) { + m_aPedCommentQueue[j][i].m_nLoadingTimeout = -1; + m_aPedCommentOrderList[j][i] = NUM_PED_COMMENTS_SLOTS; + } + + for (int i = 0; i < NUM_SOUND_QUEUES; i++) + m_nPedCommentCount[i] = 0; + m_nActiveQueue = 0; + } + void Add(tPedComment *com); + void Process(); +}; + +VALIDATE_SIZE(cPedComments, 0x490); + +#define MISSION_AUDIO_SLOTS (2) + +// name made up +class cAudioScriptObjectManager +{ +public: + int32 m_anScriptObjectEntityIndices[NUM_SCRIPT_MAX_ENTITIES]; + int32 m_nScriptObjectEntityTotal; + + cAudioScriptObjectManager() { m_nScriptObjectEntityTotal = 0; } + ~cAudioScriptObjectManager() { m_nScriptObjectEntityTotal = 0; } +}; + + +class cTransmission; +class CEntity; +class CPlane; +class CVehicle; +class CPed; + +class cPedParams +{ +public: + bool8 m_bDistanceCalculated; + float m_fDistance; + CPed *m_pPed; + + cPedParams() + { + m_bDistanceCalculated = false; + m_fDistance = 0.0f; + m_pPed = nil; + } +}; + +class cVehicleParams +{ +public: + int32 m_VehicleType; + bool8 m_bDistanceCalculated; + float m_fDistance; + CVehicle *m_pVehicle; + cTransmission *m_pTransmission; + uint32 m_nIndex; + float m_fVelocityChange; + + cVehicleParams() + { + m_VehicleType = -1; + m_bDistanceCalculated = false; + m_fDistance = 0.0f; + m_pVehicle = nil; + m_pTransmission = nil; + m_nIndex = 0; + m_fVelocityChange = 0.0f; + } +}; + +VALIDATE_SIZE(cVehicleParams, 0x1C); + +#if GTA_VERSION < GTAVC_PC_10 +enum { + /* + REFLECTION_YMAX = 0, top + REFLECTION_YMIN = 1, bottom + REFLECTION_XMIN = 2, left + REFLECTION_XMAX = 3, right + REFLECTION_ZMAX = 4, + */ + + REFLECTION_TOP = 0, + REFLECTION_BOTTOM, + REFLECTION_LEFT, + REFLECTION_RIGHT, + REFLECTION_UP, + MAX_REFLECTIONS, +}; +#else +enum { + REFLECTION_NORTH = 0, + REFLECTION_SOUTH, + REFLECTION_WEST, + REFLECTION_EAST, + REFLECTION_CEIL_NORTH, + REFLECTION_CEIL_SOUTH, + REFLECTION_CEIL_WEST, + REFLECTION_CEIL_EAST, + MAX_REFLECTIONS, +}; +#endif + +enum PLAY_STATUS { PLAY_STATUS_STOPPED = 0, PLAY_STATUS_PLAYING, PLAY_STATUS_FINISHED }; +enum LOADING_STATUS { LOADING_STATUS_NOT_LOADED = 0, LOADING_STATUS_LOADED, LOADING_STATUS_LOADING }; + +class cAudioManager +{ +public: + bool8 m_bIsInitialised; + bool8 m_bIsSurround; // used on PS2 + bool8 m_bReduceReleasingPriority; + uint8 m_nActiveSamples; + bool8 m_bDoubleVolume; // unused + bool8 m_bDynamicAcousticModelingStatus; + uint8 m_nChannelOffset; + float m_fSpeedOfSound; + bool8 m_bTimerJustReset; + uint32 m_nTimer; + tSound m_sQueueSample; + uint8 m_nActiveQueue; + tSound m_aRequestedQueue[NUM_SOUND_QUEUES][NUM_CHANNELS_GENERIC]; + uint8 m_aRequestedOrderList[NUM_SOUND_QUEUES][NUM_CHANNELS_GENERIC]; + uint8 m_nRequestedCount[NUM_SOUND_QUEUES]; + tSound m_asActiveSamples[NUM_CHANNELS_GENERIC]; + tAudioEntity m_asAudioEntities[NUM_AUDIOENTITIES]; + uint32 m_aAudioEntityOrderList[NUM_AUDIOENTITIES]; + uint32 m_nAudioEntitiesCount; +#ifdef AUDIO_REFLECTIONS + CVector m_avecReflectionsPos[MAX_REFLECTIONS]; + float m_afReflectionsDistances[MAX_REFLECTIONS]; +#endif + cAudioScriptObjectManager m_sAudioScriptObjectManager; + + // miami + bool8 m_bIsPlayerShutUp; + uint8 m_nPlayerMood; + uint32 m_nPlayerMoodTimer; + uint32 field_4B38_vc; + bool8 m_bGenericSfx; + + cPedComments m_sPedComments; + int32 m_nFireAudioEntity; + int32 m_nWaterCannonEntity; + int32 m_nPoliceChannelEntity; + cPoliceRadioQueue m_sPoliceRadioQueue; + cAMCrime m_aCrimes[10]; + int32 m_nFrontEndEntity; + int32 m_nCollisionEntity; + cAudioCollisionManager m_sCollisionManager; + int32 m_nProjectileEntity; + int32 m_nEscalatorEntity; + int32 m_nExtraSoundsEntity; +#ifdef GTA_BRIDGE + int32 m_nBridgeEntity; +#endif + + // Mission audio stuff + // So instead of making an array of struct they've added [MISSION_AUDIO_SLOTS] to every field... + // Only someone with a VERY EXTRAORDINARY mind could have come up with that + CVector m_vecMissionAudioPosition[MISSION_AUDIO_SLOTS]; + bool8 m_bIsMissionAudio2D[MISSION_AUDIO_SLOTS]; + uint32 m_nMissionAudioSampleIndex[MISSION_AUDIO_SLOTS]; + uint8 m_nMissionAudioLoadingStatus[MISSION_AUDIO_SLOTS]; + uint8 m_nMissionAudioPlayStatus[MISSION_AUDIO_SLOTS]; + bool8 m_bIsMissionAudioPlaying[MISSION_AUDIO_SLOTS]; + int32 m_nMissionAudioFramesToPlay[MISSION_AUDIO_SLOTS]; // possibly unsigned + bool8 m_bIsMissionAudioAllowedToPlay[MISSION_AUDIO_SLOTS]; + bool8 m_bIsMissionAudioPhoneCall[MISSION_AUDIO_SLOTS]; + uint8 m_nGlobalSfxVolumeMultiplier; // used to lower sfx volume during phone calls + + int32 m_anRandomTable[5]; + uint8 m_nTimeSpent; + bool8 m_bIsPaused; + bool8 m_bWasPaused; + uint32 m_FrameCounter; + + cAudioManager(); + ~cAudioManager(); + + void Initialise(); + void Terminate(); + void Service(); + int32 CreateEntity(eAudioType type, void *entity); + void DestroyEntity(int32 id); // inlined in vc + bool8 GetEntityStatus(int32 id); + void SetEntityStatus(int32 id, bool8 status); + void *GetEntityPointer(int32 id); + void PlayOneShot(int32 index, uint16 sound, float vol); + void SetEffectsMasterVolume(uint8 volume); + void SetMusicMasterVolume(uint8 volume); + void SetMP3BoostVolume(uint8 volume); + void SetEffectsFadeVol(uint8 volume); + void SetMusicFadeVol(uint8 volume); + void SetOutputMode(bool8 surround); + void ResetTimers(uint32 time); + void DestroyAllGameCreatedEntities(); + +#ifdef GTA_PC + uint8 GetNum3DProvidersAvailable(); + char *Get3DProviderName(uint8 id); + int8 GetCurrent3DProviderIndex(); + int8 AutoDetect3DProviders(); + int8 SetCurrent3DProvider(uint8 which); + void SetSpeakerConfig(int32 conf); + bool8 IsMP3RadioChannelAvailable(); + void ReleaseDigitalHandle(); + void ReacquireDigitalHandle(); +#ifdef AUDIO_REFLECTIONS + void SetDynamicAcousticModelingStatus(bool8 status); +#endif + bool8 CheckForAnAudioFileOnCD(); + char GetCDAudioDriveLetter(); + bool8 IsAudioInitialised(); +#endif + + void ServiceSoundEffects(); + uint8 ComputeVolume(uint8 emittingVolume, float maxDistance, float distance); + void TranslateEntity(Const CVector *v1, CVector *v2); + int32 ComputeFrontRearMix(float, CVector *); + int32 ComputePan(float, CVector *); + uint32 ComputeDopplerEffectedFrequency(uint32 oldFreq, float position1, float position2, float speedMultiplier); + int32 RandomDisplacement(uint32 seed); + void InterrogateAudioEntities(); // inlined + void AddSampleToRequestedQueue(); + void AddDetailsToRequestedOrderList(uint8 sample); // inlined in vc +#ifdef AUDIO_REFLECTIONS + void AddReflectionsToRequestedQueue(); + void UpdateReflections(); +#endif + void AddReleasingSounds(); + void ProcessActiveQueues(); + void ClearRequestedQueue(); // inlined in vc + void ClearActiveSamples(); + void GenerateIntegerRandomNumberTable(); +#ifdef GTA_PS2 + void LoadBankIfNecessary(uint8 bank); // this is used only on PS2 but technically not a platform code +#endif + +#ifdef EXTERNAL_3D_SOUND // actually must have been && AUDIO_MSS as well + void AdjustSamplesVolume(); // inlined + uint8 ComputeEmittingVolume(uint8 emittingVolume, float maxDistance, float distance); // inlined +#endif + + // audio logic + void PreInitialiseGameSpecificSetup(); + void PostInitialiseGameSpecificSetup(); + void PreTerminateGameSpecificShutdown(); + void PostTerminateGameSpecificShutdown(); + void ResetAudioLogicTimers(uint32 timer); + void ProcessReverb(); + float GetDistanceSquared(const CVector &v); // inlined in vc + void CalculateDistance(bool8 &condition, float dist); + CVehicle *FindVehicleOfPlayer(); + void ProcessSpecial(); + void ProcessEntity(int32 sound); + void ProcessPhysical(int32 id); + + // vehicles + void ProcessVehicle(CVehicle *vehicle); + bool8 ProcessCarHeli(cVehicleParams ¶ms); + void ProcessRainOnVehicle(cVehicleParams ¶ms); + bool8 ProcessReverseGear(cVehicleParams ¶ms); + void ProcessModelHeliVehicle(cVehicleParams ¶ms); + void ProcessModelVehicle(cVehicleParams ¶ms); + bool8 ProcessVehicleFlatTyre(cVehicleParams ¶ms); + bool8 ProcessVehicleRoadNoise(cVehicleParams ¶ms); + bool8 ProcessWetRoadNoise(cVehicleParams ¶ms); + bool8 ProcessVehicleEngine(cVehicleParams ¶ms); + void UpdateGasPedalAudio(CVehicle *veh, int vehType); + void PlayerJustGotInCar(); + void PlayerJustLeftCar(); + void AddPlayerCarSample(uint8 emittingVolume, uint32 freq, uint32 sample, uint8 bank, uint8 counter, bool8 notLooping); + void ProcessCesna(cVehicleParams ¶ms); + void ProcessPlayersVehicleEngine(cVehicleParams ¶ms, CVehicle *veh); + bool8 ProcessVehicleSkidding(cVehicleParams ¶ms); + float GetVehicleDriveWheelSkidValue(CVehicle *veh, tWheelState wheelState, float gasPedalAudio, cTransmission *transmission, float velocityChange); + float GetVehicleNonDriveWheelSkidValue(CVehicle *veh, tWheelState wheelState, cTransmission *transmission, float velocityChange); + bool8 ProcessVehicleHorn(cVehicleParams ¶ms); + bool8 UsesSiren(cVehicleParams ¶ms); + bool8 UsesSirenSwitching(cVehicleParams ¶ms); + bool8 ProcessVehicleSirenOrAlarm(cVehicleParams ¶ms); + bool8 UsesReverseWarning(uint32 model); + bool8 ProcessVehicleReverseWarning(cVehicleParams ¶ms); + bool8 ProcessVehicleDoors(cVehicleParams ¶ms); + bool8 ProcessAirBrakes(cVehicleParams ¶ms); + bool8 HasAirBrakes(uint32 model); + bool8 ProcessEngineDamage(cVehicleParams ¶ms); + bool8 ProcessCarBombTick(cVehicleParams ¶ms); + void ProcessVehicleOneShots(cVehicleParams ¶ms); +#ifdef GTA_TRAIN + bool8 ProcessTrainNoise(cVehicleParams ¶ms); +#endif + bool8 ProcessBoatEngine(cVehicleParams ¶ms); + bool8 ProcessBoatMovingOverWater(cVehicleParams ¶ms); + void ProcessPlane(cVehicleParams ¶ms); + void ProcessJumbo(cVehicleParams ¶ms); + void ProcessJumboTaxi(); + void ProcessJumboAccel(CPlane *plane); + void ProcessJumboTakeOff(CPlane *plane); + void ProcessJumboFlying(); + void ProcessJumboLanding(CPlane *plane); + void ProcessJumboDecel(CPlane *plane); + bool8 SetupJumboTaxiSound(uint8 vol); + bool8 SetupJumboWhineSound(uint8 emittingVol, uint32 freq); + bool8 SetupJumboEngineSound(uint8 vol, uint32 freq); + bool8 SetupJumboFlySound(uint8 emittingVol); + bool8 SetupJumboRumbleSound(uint8 emittingVol); + int32 GetJumboTaxiFreq(); // inlined in vc + + // peds + void ProcessPed(CPhysical *ped); + void ProcessPedOneShots(cPedParams ¶ms); + void SetPedTalkingStatus(CPed *ped, bool8 status); + void SetPlayersMood(uint8 mood, uint32 time); + void ProcessPlayerMood(); + + // ped comments + void SetupPedComments(cPedParams ¶ms, uint16 sound); + uint32 GetPedCommentSfx(CPed *ped, uint16 sound); + void GetPhrase(uint32 &phrase, uint32 &prevPhrase, uint32 sample, uint32 maxOffset); + uint32 GetPlayerTalkSfx(CPed *ped, uint16 sound); + uint32 GetGenericMaleTalkSfx(CPed *ped, uint16 sound); // inlined in vc + uint32 GetGenericFemaleTalkSfx(CPed *ped, uint16 sound); // inlined in vc + uint32 GetDefaultTalkSfx(CPed *ped, uint16 sound); + uint32 GetCopTalkSfx(CPed *ped, uint16 sound); + uint32 GetSwatTalkSfx(CPed *ped, uint16 sound); + uint32 GetFBITalkSfx(CPed *ped, uint16 sound); + uint32 GetArmyTalkSfx(CPed *ped, uint16 sound); + uint32 GetMedicTalkSfx(CPed *ped, uint16 sound); + uint32 GetFiremanTalkSfx(CPed *ped, uint16 sound); + uint32 GetWFYG1TalkSfx(CPed *ped, uint16 sound); + uint32 GetWFYG2TalkSfx(CPed *ped, uint16 sound); + uint32 GetHFYSTTalkSfx(CPed *ped, uint16 sound); + uint32 GetHFOSTTalkSfx(CPed *ped, uint16 sound); + uint32 GetHMYSTTalkSfx(CPed *ped, uint16 sound); + uint32 GetHMOSTTalkSfx(CPed *ped, uint16 sound); + uint32 GetHFYRITalkSfx(CPed *ped, uint16 sound); + uint32 GetHFORITalkSfx(CPed *ped, uint16 sound); + uint32 GetHMYRITalkSfx(CPed *ped, uint16 sound); + uint32 GetHMORITalkSfx(CPed *ped, uint16 sound); + uint32 GetHFYBETalkSfx(CPed *ped, uint16 sound); + uint32 GetHFOBETalkSfx(CPed *ped, uint16 sound); + uint32 GetHMYBETalkSfx(CPed *ped, uint16 sound); + uint32 GetHMOBETalkSfx(CPed *ped, uint16 sound); + uint32 GetHFYBUTalkSfx(CPed *ped, uint16 sound); + uint32 GetHFYMDTalkSfx(CPed *ped, uint16 sound); + uint32 GetHFYCGTalkSfx(CPed *ped, uint16 sound); + uint32 GetHFYPRTalkSfx(CPed *ped, uint16 sound); + uint32 GetHFOTRTalkSfx(CPed *ped, uint16 sound); + uint32 GetHMOTRTalkSfx(CPed *ped, uint16 sound); + uint32 GetHMOCATalkSfx(CPed *ped, uint16 sound); + uint32 GetBMYCRTalkSfx(CPed *ped, uint16 sound); + uint32 GetBFYSTTalkSfx(CPed *ped, uint16 sound); + uint32 GetBFOSTTalkSfx(CPed *ped, uint16 sound); + uint32 GetBMYSTTalkSfx(CPed *ped, uint16 sound); + uint32 GetBMOSTTalkSfx(CPed *ped, uint16 sound); + uint32 GetBFYRITalkSfx(CPed *ped, uint16 sound); + uint32 GetBFORITalkSfx(CPed *ped, uint16 sound); + uint32 GetBMYRITalkSfx(CPed *ped, uint16 sound); + uint32 GetBFYBETalkSfx(CPed *ped, uint16 sound); + uint32 GetBMYBETalkSfx(CPed *ped, uint16 sound); + uint32 GetBFOBETalkSfx(CPed *ped, uint16 sound); + uint32 GetBMOBETalkSfx(CPed *ped, uint16 sound); + uint32 GetBMYBUTalkSfx(CPed *ped, uint16 sound); + uint32 GetBFYPRTalkSfx(CPed *ped, uint16 sound); + uint32 GetBFOTRTalkSfx(CPed *ped, uint16 sound); + uint32 GetBMOTRTalkSfx(CPed *ped, uint16 sound); + uint32 GetBMYPITalkSfx(CPed *ped, uint16 sound); + uint32 GetBMYBBTalkSfx(CPed *ped, uint16 sound); + uint32 GetWMYCRTalkSfx(CPed *ped, uint16 sound); + uint32 GetWFYSTTalkSfx(CPed *ped, uint16 sound); + uint32 GetWFYSKTalkSfx(CPed *ped, uint16 sound); + uint32 GetWMYSKTalkSfx(CPed *ped, uint16 sound); + uint32 GetWFOSTTalkSfx(CPed *ped, uint16 sound); + uint32 GetWMYSTTalkSfx(CPed *ped, uint16 sound); + uint32 GetWMOSTTalkSfx(CPed *ped, uint16 sound); + uint32 GetWFYRITalkSfx(CPed *ped, uint16 sound); + uint32 GetWFORITalkSfx(CPed *ped, uint16 sound); + uint32 GetWMYRITalkSfx(CPed *ped, uint16 sound); + uint32 GetWMORITalkSfx(CPed *ped, uint16 sound); + uint32 GetWFYBETalkSfx(CPed *ped, uint16 sound); + uint32 GetWMYBETalkSfx(CPed *ped, uint16 sound); + uint32 GetWFOBETalkSfx(CPed *ped, uint16 sound); + uint32 GetWMOBETalkSfx(CPed *ped, uint16 sound); + uint32 GetWMYCWTalkSfx(CPed *ped, uint16 sound); + uint32 GetWMYGOTalkSfx(CPed *ped, uint16 sound); + uint32 GetWFOGOTalkSfx(CPed *ped, uint16 sound); + uint32 GetWMOGOTalkSfx(CPed *ped, uint16 sound); + uint32 GetWFYLGTalkSfx(CPed *ped, uint16 sound); + uint32 GetWMYLGTalkSfx(CPed *ped, uint16 sound); + uint32 GetWFYBUTalkSfx(CPed *ped, uint16 sound); + uint32 GetWMYBUTalkSfx(CPed *ped, uint16 sound); + uint32 GetWMOBUTalkSfx(CPed *ped, uint16 sound); + uint32 GetWFYPRTalkSfx(CPed *ped, uint16 sound); + uint32 GetWFOTRTalkSfx(CPed *ped, uint16 sound); + uint32 GetWMOTRTalkSfx(CPed *ped, uint16 sound); + uint32 GetWMYPITalkSfx(CPed *ped, uint16 sound); + uint32 GetWMOCATalkSfx(CPed *ped, uint16 sound); + uint32 GetWFYSHTalkSfx(CPed *ped, uint16 sound); + uint32 GetWFOSHTalkSfx(CPed *ped, uint16 sound); + uint32 GetJFOTOTalkSfx(CPed *ped, uint16 sound); + uint32 GetJMOTOTalkSfx(CPed *ped, uint16 sound); + uint32 GetHNTalkSfx(CPed *ped, uint16 sound); + uint32 GetBKTalkSfx(CPed *ped, uint16 sound); + uint32 GetCBTalkSfx(CPed *ped, uint16 sound); + uint32 GetSGTalkSfx(CPed *ped, uint16 sound); + uint32 GetCLTalkSfx(CPed *ped, uint16 sound); + uint32 GetGDTalkSfx(CPed *ped, uint16 sound); + uint32 GetPGTalkSfx(CPed *ped, uint16 sound); + uint32 GetViceWhiteTalkSfx(CPed *ped, uint16 sound); + uint32 GetViceBlackTalkSfx(CPed *ped, uint16 sound); + uint32 GetBMODKTalkSfx(CPed *ped, uint16 sound); + uint32 GetHMYAPTalkSfx(CPed *ped, uint16 sound); + uint32 GetWFYJGTalkSfx(CPed *ped, uint16 sound); + uint32 GetWMYJGTalkSfx(CPed *ped, uint16 sound); + uint32 GetSpecialCharacterTalkSfx(CPed *ped, int32 model, uint16 sound); + + void DebugPlayPedComment(int32 sound); + + // particles + void ProcessExplosions(int32 explosion); + void ProcessFires(int32 entity); + void ProcessWaterCannon(int32); + + // script objects + void ProcessScriptObject(int32 id); + void ProcessOneShotScriptObject(uint8 sound); + void ProcessLoopingScriptObject(uint8 sound); + + // misc + void ProcessWeather(int32 id); + void ProcessFrontEnd(); + //void ProcessCrane(); + void ProcessProjectiles(); + void ProcessEscalators(); + void ProcessExtraSounds(); + void ProcessGarages(); + void ProcessFireHydrant(); + +#ifdef GTA_BRIDGE + void ProcessBridge(); + void ProcessBridgeWarning(); + void ProcessBridgeMotor(); + void ProcessBridgeOneShots(); +#endif + + // mission audio + const char *GetMissionAudioLoadedLabel(uint8 slot); + bool8 MissionScriptAudioUsesPoliceChannel(uint32 soundMission); + void PreloadMissionAudio(uint8 slot, Const char *name); + uint8 GetMissionAudioLoadingStatus(uint8 slot); + void SetMissionAudioLocation(uint8 slot, float x, float y, float z); + void PlayLoadedMissionAudio(uint8 slot); + bool8 ShouldDuckMissionAudio(uint8 slot); + bool8 IsMissionAudioSamplePlaying(uint8 slot); + bool8 IsMissionAudioSampleFinished(uint8 slot); + void ClearMissionAudio(uint8 slot); // inlined in vc + void ProcessMissionAudioSlot(uint8 slot); + void ProcessMissionAudio(); + + // police radio + void InitialisePoliceRadioZones(); + void InitialisePoliceRadio(); + void ResetPoliceRadio(); + void SetMissionScriptPoliceAudio(uint32 sfx); // inlined and optimized + int8 GetMissionScriptPoliceAudioPlayingStatus(); + void DoPoliceRadioCrackle(); + void ServicePoliceRadio(); + void ServicePoliceRadioChannel(uint8 wantedLevel); + bool8 SetupCrimeReport(); + void SetupSuspectLastSeenReport(); + void ReportCrime(eCrimeType crime, const CVector &pos); + void PlaySuspectLastSeen(float x, float y, float z); + void AgeCrimes(); // inlined in vc + + // collision stuff + void ReportCollision(CEntity *entity1, CEntity *entity2, uint8 surface1, uint8 surface2, float collisionPower, float intensity2); + void ServiceCollisions(); + void SetUpOneShotCollisionSound(const cAudioCollision &col); + void SetUpLoopingCollisionSound(const cAudioCollision &col, uint8 counter); + uint32 SetLoopingCollisionRequestedSfxFreqAndGetVol(const cAudioCollision &audioCollision); + float GetCollisionOneShotRatio(uint32 a, float b); + float GetCollisionLoopingRatio(uint32 a, uint32 b, float c); // not used + float GetCollisionRatio(float a, float b, float c, float d); // inlined in vc + + float Sqrt(float v) const { return v <= 0.0f ? 0.0f : ::Sqrt(v); } +}; + +/* + Manual loop points are not on PS2 so let's have these macros to avoid massive ifndefs. + Setting these manually was pointless anyway since they never change from sdt values. + What were they thinking? +*/ +#ifndef GTA_PS2 +#define RESET_LOOP_OFFSETS \ + m_sQueueSample.m_nLoopStart = 0; \ + m_sQueueSample.m_nLoopEnd = -1; +#define SET_LOOP_OFFSETS(sample) \ + m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(sample); \ + m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(sample); +#else +#define RESET_LOOP_OFFSETS +#define SET_LOOP_OFFSETS(sample) +#endif +#ifdef EXTERNAL_3D_SOUND +#define SET_EMITTING_VOLUME(vol) m_sQueueSample.m_nEmittingVolume = vol +#else +#define SET_EMITTING_VOLUME(vol) +#endif +#ifdef AUDIO_REFLECTIONS +#define SET_SOUND_REFLECTION(b) m_sQueueSample.m_bReflections = b +#else +#define SET_SOUND_REFLECTION(b) +#endif +#ifdef AUDIO_REVERB +#define SET_SOUND_REVERB(b) m_sQueueSample.m_bReverb = b +#else +#define SET_SOUND_REVERB(b) +#endif + +#if defined(AUDIO_MSS) && !defined(PS2_AUDIO_CHANNELS) +static_assert(sizeof(cAudioManager) == 0x5558, "cAudioManager: error"); +#endif + +extern cAudioManager AudioManager; + +enum +{ + PED_COMMENT_VOLUME = 127, + PED_COMMENT_VOLUME_BEHIND_WALL = 31, + COLLISION_MAX_DIST = 60, +}; diff --git a/src/miami/audio/AudioSamples.h b/src/miami/audio/AudioSamples.h new file mode 100644 index 00000000..365ca282 --- /dev/null +++ b/src/miami/audio/AudioSamples.h @@ -0,0 +1,10130 @@ +#pragma once + +#include "common.h" + +#define FIRST_PLAYER_COMMENT(e) PLAYER_COMMENTS_START, e = PLAYER_COMMENTS_START +#define LAST_PLAYER_COMMENT(e) e, PLAYER_COMMENTS_END = e + +enum eSfxSample +{ + SFX_CAR_HORN_JEEP = 0, + SFX_CAR_HORN_BMW328, + SFX_CAR_HORN_BUS, + SFX_CAR_HORN_BUS2, + SFX_CAR_HORN_56CHEV, + SFX_CAR_HORN_PICKUP, + SFX_CAR_HORN_PORSCHE, + SFX_CAR_HORN_TRUCK, + + SFX_CAR_HELI_MAI, // 8 + SFX_CAR_HELI_MAI2, // 9 + SFX_CAR_HELI_REA, // 10 + SFX_CAR_HELI_STA, // 11 + SFX_CAR_HELI_ROT, // 12 + SFX_CAR_HELI_FAR, // 13 + SFX_CAR_HELI_ROL, // 14 + + SFX_OLD_CAR_DOOR_OPEN, + SFX_OLD_CAR_DOOR_CLOSE, + SFX_NEW_CAR_DOOR_OPEN, + SFX_NEW_CAR_DOOR_CLOSE, + SFX_TRUCK_DOOR_OPEN, + SFX_TRUCK_DOOR_CLOSE, + SFX_REVERSE_GEAR, + SFX_REVERSE_GEAR_2, + SFX_CAR_STARTER, // 23 + SFX_ROAD_NOISE, // 24 + SFX_SKID, // 25 + SFX_GRAVEL_SKID, // 26 + SFX_POLICE_SIREN_SLOW, + SFX_SIREN_FAST, // 28 + SFX_AMBULANCE_SIREN_SLOW, + SFX_REVERSE_WARNING, + SFX_ICE_CREAM_TUNE, + SFX_AIR_BRAKES, // 32 + SFX_TYRE_BUMP, // 33 + SFX_TYRE_BURST_B, // 34 + SFX_TYRE_BURST, // 35 + SFX_TYRE_BURST_L, // 36 + SFX_PALM_TREE_LO, // 37 + SFX_BULLET_PASS_1, // 38 + SFX_BULLET_PASS_2, // 39 + SFX_SKATE_1, // 40 + SFX_SKATE_2, // 41 + SFX_FOOTSTEP_CONCRETE_1, + SFX_FOOTSTEP_CONCRETE_2, + SFX_FOOTSTEP_CONCRETE_3, + SFX_FOOTSTEP_CONCRETE_4, + SFX_FOOTSTEP_CONCRETE_5, + SFX_EXPLOSION_1, // 47 + SFX_EXPLOSION_2, // 48 + SFX_EXPLOSION_3, // 49 + SFX_COLT45_LEFT, // 50 + SFX_COLT45_RIGHT, // 51 + SFX_AK47_LEFT, // 52 + SFX_AK47_RIGHT, // 53 + SFX_UZI_LEFT, // 54 + SFX_UZI_RIGHT, // 55 + SFX_UZI_END_LEFT, // 56 + SFX_SNIPER_LEFT, // 57 + SFX_SNIPER_RIGHT, // 58 + SFX_ROCKET_LEFT, // 59 + SFX_ROCKET_RIGHT, // 60 + SFX_ROCKET_FLY, // 61 + SFX_FLAMETHROWER_LEFT, // 62 + SFX_FLAMETHROWER_RIGHT, // 63 + SFX_FLAMETHROWER_START_LEFT, // 64 + SFX_FLAMETHROWER_START_RIGHT, // 65 + SFX_SHOTGUN_LEFT, // 66 + SFX_SHOTGUN_RIGH, // 67 + SFX_M60_LEFT, // 68 + SFX_M60_RIGHT, // 69 + SFX_M60_TAIL_LEFT, // 70 + SFX_TEC_LEFT, // 71 + SFX_TEC_RIGHT, // 72 + SFX_TEC_TAIL, // 73 + SFX_RUGER_LEFT, // 74 + SFX_RUGER_RIGHT, // 75 + SFX_RUGER_TAIL, // 76 + SFX_PISTOL_RELOAD, // 77 + SFX_AK47_RELOAD, // 78 + SFX_ROCKET_RELOAD, // 79 + SFX_RIFLE_RELOAD, // 80 + SFX_GOLF_CLUB_SWING, // 81 + SFX_MINIGUN_FIRE_LEFT, // 82 + SFX_MINIGUN_FIRE_RIGHT, // 83 + SFX_MINIGUN_STOP, // 84 + SFX_SPAS12_LEFT, // 85 + SFX_SPAS12_RIGHT, // 86 + SFX_SPAS12_TAIL_LEFT, // 87 + SFX_PYTHON_LEFT, // 88 + SFX_PYTHON_RIGHT, // 89 + SFX_MP5_LEFT, // 90 + SFX_MP5_RIGHT, // 91 + SFX_COL_TARMAC_1, // 92 + SFX_COL_TARMAC_2, // 93 + SFX_COL_TARMAC_3, // 94 + SFX_COL_TARMAC_4, // 95 + SFX_COL_TARMAC_5, // 96 + SFX_COL_GRASS_1, + SFX_COL_GRAVEL_1, + SFX_COL_MUD_1, + SFX_COL_GARAGE_DOOR_1, + SFX_COL_CAR_PANEL_1, + SFX_COL_CAR_PANEL_2, + SFX_COL_CAR_PANEL_3, + SFX_COL_CAR_PANEL_4, + SFX_COL_CAR_PANEL_5, + SFX_COL_CAR_PANEL_6, + SFX_COL_THICK_METAL_PLATE_1, + SFX_COL_SCAFFOLD_POLE_1, + SFX_COL_LAMP_POST_1, + SFX_COL_HYDRANT_1, + SFX_COL_METAL_CHAIN_FENCE_1, + SFX_COL_METAL_CHAIN_FENCE_2, + SFX_COL_METAL_CHAIN_FENCE_3, + SFX_COL_METAL_CHAIN_FENCE_4, + SFX_COL_PED_1, // 115 + SFX_COL_PED_2, // 116 + SFX_COL_SAND_1, + SFX_COL_WOOD_CRATES_1, + SFX_COL_WOOD_CRATES_2, + SFX_COL_WOOD_CRATES_3, + SFX_COL_WOOD_CRATES_4, + SFX_COL_WOOD_BENCH_1, + SFX_COL_WOOD_BENCH_2, + SFX_COL_WOOD_BENCH_3, + SFX_COL_WOOD_BENCH_4, + SFX_COL_WOOD_SOLID_1, + SFX_COL_VEG_1, // 127 + SFX_COL_VEG_2, // 128 + SFX_COL_VEG_3, // 129 + SFX_COL_VEG_4, // 130 + SFX_COL_VEG_5, // 131 + SFX_COL_CONTAINER_1, + SFX_COL_NEWS_VENDOR_1, + SFX_COL_NEWS_VENDOR_2, + SFX_COL_NEWS_VENDOR_3, + SFX_COL_CAR_1, // 136 + SFX_COL_CAR_2, // 137 + SFX_COL_CAR_3, // 138 + SFX_COL_CAR_4, // 139 + SFX_COL_CAR_5, // 140 + SFX_COL_CARDBOARD_1, + SFX_COL_CARDBOARD_2, + SFX_COL_GATE, // 143 + SFX_SCRAPE_CAR_1, // 144 + SFX_CRATE_SMASH, + SFX_GLASS_CRACK, // 146 + SFX_GLASS_SMASH, // 147 + SFX_GLASS_SHARD_1, + SFX_GLASS_SHARD_2, + SFX_GLASS_SHARD_3, + SFX_GLASS_SHARD_4, + SFX_PED_ON_FIRE, // 152 + SFX_CAR_ON_FIRE, // 153 + SFX_RAIN, // 154 + SFX_HURRICANE_MA, // 155 + SFX_BULLET_SHELL_HIT_GROUND_1, + SFX_BULLET_SHELL_HIT_GROUND_2, + SFX_BULLET_PED, // 158 + SFX_BULLET_CAR_1, // 159 + SFX_BULLET_CAR_2, // 160 + SFX_BULLET_CAR_3, // 161 + SFX_BULLET_WALL_1, // 162 + SFX_BULLET_WALL_2, // 163 + SFX_BULLET_WALL_3, // 164 + SFX_BAT_HIT_LEFT, // 165 + SFX_BAT_HIT_RIGH, // 166 + SFX_FIGHT_1, // 167 + SFX_FIGHT_2, // 168 + SFX_FIGHT_4, // 169 + SFX_FIGHT_5, // 170 + SFX_KNIFE_SWING, // 171 + SFX_KNIFE_SLASH, // 172 + SFX_KNIFE_STAB, // 173 + SFX_HAMMER_HIT_1, // 174 + SFX_HAMMER_HIT_2, // 175 + SFX_GARAGE_DOOR_LOOP, // 176 + SFX_COUNTDOWN, // 177 + SFX_ARM_BOMB, // 178 + SFX_POLICE_RADIO_CRACKLE, // 179 + + SFX_WEVE_GOT, + SFX_THERES, + SFX_RESPOND_TO, + SFX_A_10, + SFX_IN, + SFX_NORTH, + SFX_EAST, + SFX_SOUTH, + SFX_WEST, + SFX_CENTRAL, + SFX_POLICE_RADIO_MESSAGE_NOISE_1, + SFX_POLICE_RADIO_SUSPECT, + SFX_POLICE_RADIO_LAST_SEEN, + SFX_POLICE_RADIO_ON_FOOT, + SFX_POLICE_RADIO_IN_A, + SFX_POLICE_RADIO_DARK, + SFX_POLICE_RADIO_LIGHT, + SFX_POLICE_RADIO_BRIGHT, + + SFX_CRIME_1, + SFX_CRIME_2, + SFX_CRIME_3, + SFX_CRIME_4, + SFX_CRIME_5, + SFX_CRIME_6, + SFX_CRIME_7, + SFX_CRIME_8, + SFX_CRIME_9, + SFX_CRIME_10, + SFX_CRIME_11, + SFX_CRIME_12, + SFX_POLICE_RADIO_VICE_CITY, + SFX_POLICE_RADIO_VICE_CITY_BEACH, + SFX_POLICE_RADIO_VICE_CITY_MAINLAND, + SFX_POLICE_RADIO_OCEAN_BEACH, //??? + SFX_POLICE_RADIO_WASHINGTON_BEACH, + SFX_POLICE_RADIO_VICE_POINT, + SFX_POLICE_RADIO_LEAF_LINKS, + SFX_POLICE_RADIO_STARFISH_ISLAND, //??????????? + SFX_POLICE_RADIO_VICEPORT, + SFX_POLICE_RADIO_LITTLE_HAVANA, + SFX_POLICE_RADIO_LITTLE_HAITI, + SFX_POLICE_RADIO_PRAWN_ISLAND, //??????????? IS THAT HOW SHE PRONOUNCES ISLAND? + SFX_POLICE_RADIO_DOWNTOWN, + SFX_POLICE_RADIO_ESCOBAR_INTERNATIONAL, + SFX_POLICE_RADIO_BLACK, + SFX_POLICE_RADIO_WHITE, + SFX_POLICE_RADIO_BLUE, + SFX_POLICE_RADIO_RED, + SFX_POLICE_RADIO_PURPLE, + SFX_POLICE_RADIO_YELLOW, + SFX_POLICE_RADIO_GREY, + SFX_POLICE_RADIO_ORANGE, + SFX_POLICE_RADIO_GREEN, + SFX_POLICE_RADIO_SILVER, + SFX_POLICE_RADIO_AMBULANCE, + SFX_POLICE_RADIO_TUDOOR, + SFX_POLICE_RADIO_TRUCK, + SFX_POLICE_RADIO_FIRE_TRUCK, + SFX_POLICE_RADIO_PICKUP, + SFX_POLICE_RADIO_POLICE_CAR, + SFX_POLICE_RADIO_BOAT, + SFX_POLICE_RADIO_BUGGY, + SFX_POLICE_RADIO_BUS, + SFX_POLICE_RADIO_COACH, + SFX_POLICE_RADIO_CRUISER, + SFX_POLICE_RADIO_DINGHY, + SFX_POLICE_RADIO_GARBAGE_TRUCK, + SFX_POLICE_RADIO_GOLF_CART, + SFX_POLICE_RADIO_HEARSE, + SFX_POLICE_RADIO_HELICOPTER, + SFX_POLICE_RADIO_ICE_CREAM_VAN, + SFX_POLICE_RADIO_LOWRIDER, + SFX_POLICE_RADIO_MOPED, + SFX_POLICE_RADIO_MOTOBIKE, + SFX_POLICE_RADIO_OFFROAD, + SFX_POLICE_RADIO_PLANE, + SFX_POLICE_RADIO_RIG, + SFX_POLICE_RADIO_SEDAN, + SFX_POLICE_RADIO_SPEEDBOAT, + SFX_POLICE_RADIO_SPORTS_CAR, + SFX_POLICE_RADIO_STATION_WAGON, + SFX_POLICE_RADIO_STRETCH, + SFX_POLICE_RADIO_SWAT_VAN, + SFX_POLICE_RADIO_TANK, + SFX_POLICE_RADIO_TAXI, + SFX_POLICE_RADIO_VAN, + + SFX_HELI_1, // 198 + SFX_PHONE_RING, // 199 + SFX_CAR_REV_1, // PONT + SFX_CAR_REV_2, // PORSHE + SFX_CAR_REV_3, // SPIDER + SFX_CAR_REV_4, // MERC + SFX_CAR_REV_5, // TRUC + SFX_CAR_REV_6, // HOTROD + SFX_CAR_REV_7, // COBRA + SFX_CAR_REV_8, // PONT2 + SFX_CAR_REV_9, // CADI + SFX_CAR_REV_10, // PATHFINDER + SFX_CAR_REV_11, // PACARD + SFX_CAR_REV_12, // GOLFCART + SFX_CAR_REV_13, // SFX_CAR_IDLE_GOL + SFX_CAR_REV_14, // SFX_CAR_IDLE_GOL + SFX_CAR_REV_15, // SFX_CAR_IDLE_GOL + SFX_CAR_REV_16, // SFX_CAR_IDLE_GOL + SFX_CAR_REV_17, // VTWI + SFX_MOPED_REV, // just moped + SFX_CAR_REV_19, // HOND(A) + SFX_CAR_REV_20, // SPOR(TCAR) + SFX_CAR_IDLE_1, // PONT + SFX_CAR_IDLE_2, // PORSHE + SFX_CAR_IDLE_3, // SPIDER + SFX_CAR_IDLE_4, // MERC + SFX_CAR_IDLE_5, // TRUC + SFX_CAR_IDLE_6, // HOTROD + SFX_CAR_IDLE_7, // COBRA + SFX_CAR_IDLE_8, // PONT2 + SFX_CAR_IDLE_9, // CADI + SFX_CAR_IDLE_10, // PATHFINDER + SFX_CAR_IDLE_11, // PACARD + SFX_CAR_IDLE_12, // GOLFCART + SFX_CAR_IDLE_13, // SFX_CAR_IDLE_GOL + SFX_CAR_IDLE_14, // SFX_CAR_IDLE_GOL + SFX_CAR_IDLE_15, // SFX_CAR_IDLE_GOL + SFX_CAR_IDLE_16, // SFX_CAR_IDLE_GOL + SFX_CAR_IDLE_17, // VTWI + SFX_MOPED_IDLE, // 237 + SFX_CAR_IDLE_19, // HOND(A) + SFX_CAR_IDLE_20, // SPOR(TCAR) + SFX_JUMBO_DIST_FLY, + SFX_JUMBO_TAXI, // 241 + SFX_JUMBO_WHINE, // 242 + SFX_JUMBO_ENGINE, // 243 + SFX_JUMBO_RUMBLE, // 244 + SFX_JUMBO_LAND_WHEELS, + SFX_BOAT_CRUISER_LOOP, // 246 + SFX_BOAT_V12_LOOP, // 247 + SFX_BOAT_WATER_LOOP, + SFX_BOAT_SPLASH_1, + SFX_BOAT_SPLASH_2, + SFX_FISHING_BOAT_IDLE, + SFX_CAR_RAIN_1, // 252 + SFX_CAR_RAIN_2, // 253 + SFX_CAR_RAIN_3, // 254 + SFX_CAR_RAIN_4, // 255 + SFX_SPLASH_1, // 256 + SFX_PED_CRUNCH_1, // 257 + SFX_PED_CRUNCH_2, // 258 + SFX_WOODEN_BOX_SMASH, + SFX_CARDBOARD_BOX_SMASH, + SFX_ERROR_FIRE_ROCKET_LAUNCHER, + SFX_ERROR_FIRE_RIFLE, + SFX_TANK_TURRET, // 263 + SFX_BODY_LAND_AND_FALL, + SFX_BODY_LAND, // 265 + SFX_BOMB_BEEP, // 266 + SFX_TIMER_BEEP, // 267 + SFX_SUSPENSION_FAST_MOVE, + SFX_SUSPENSION_SLOW_MOVE_LOOP, + SFX_SHAG_SUSPENSION, + SFX_HIT_BALL, // 271 + SFX_ARCADE, // 272 + SFX_CESNA_IDLE, // 273 + SFX_CESNA_REV, // 274 + SFX_RADIO_CLICK, // 275 + SFX_RADIO_DIAL_1, // 276 + SFX_RADIO_DIAL_2, // 277 + SFX_RADIO_DIAL_3, // 278 + + // pc only + SFX_RADIO_DIAL_4, + SFX_RADIO_DIAL_5, + SFX_RADIO_DIAL_6, + SFX_RADIO_DIAL_7, + SFX_RADIO_DIAL_8, + SFX_RADIO_DIAL_9, + SFX_RADIO_DIAL_10, + SFX_RADIO_DIAL_11, + SFX_RADIO_DIAL_12, + + SFX_INFO_LEFT, // 279 + SFX_INFO_RIGHT, // 280 + SFX_INFO_CENTRE, // 281 + SFX_MONEY_LEFT, // 282 + SFX_MONEY_RIGHT, // 283 + SFX_WEAPON_LEFT, // 284 + SFX_WEAPON_RIGHT, // 285 + SFX_WEAPON_CENTRE, // 286 + SFX_PART_MISSION_COMPLETE_LEFT, // 287 + SFX_PART_MISSION_COMPLETE_RIGHT, // 288 + SFX_PART_MISSION_COMPLETE_CENTRE, // 289 + SFX_GO_LEFT, // 290 + SFX_GO_RIGHT, // 291 + SFX_GO_CENTRE, // 292 + SFX_TIMER, // 293 + SFX_EMPTY, // 294 + + SFX_FE_HIGHLIGHT_LEFT, // + SFX_FE_HIGHLIGHT_RIGHT, // + SFX_FE_SELECT_LEFT, // + SFX_FE_SELECT_RIGHT, // + SFX_FE_BACK_LEFT, // + SFX_FE_BACK_RIGHT, // + SFX_FE_ERROR_LEFT, // + SFX_FE_ERROR_RIGHT, // + SFX_FE_NOISE_BURST_1, + SFX_FE_NOISE_BURST_2, + SFX_FE_NOISE_BURST_3, + + SFX_CAR_ACCEL_1, + SFX_CAR_AFTER_ACCEL_1, + SFX_CAR_FINGER_OFF_ACCEL_1, + + SFX_CAR_ACCEL_2, + SFX_CAR_AFTER_ACCEL_2, + SFX_CAR_FINGER_OFF_ACCEL_2, + + SFX_CAR_ACCEL_3, + SFX_CAR_AFTER_ACCEL_3, + SFX_CAR_FINGER_OFF_ACCEL_3, + + SFX_CAR_ACCEL_4, + SFX_CAR_AFTER_ACCEL_4, + SFX_CAR_FINGER_OFF_ACCEL_4, + + SFX_CAR_ACCEL_5, + SFX_CAR_AFTER_ACCEL_5, + SFX_CAR_FINGER_OFF_ACCEL_5, + + SFX_CAR_ACCEL_6, + SFX_CAR_AFTER_ACCEL_6, + SFX_CAR_FINGER_OFF_ACCEL_6, + + SFX_CAR_ACCEL_7, + SFX_CAR_AFTER_ACCEL_7, + SFX_CAR_FINGER_OFF_ACCEL_7, + + SFX_CAR_ACCEL_8, + SFX_CAR_AFTER_ACCEL_8, + SFX_CAR_FINGER_OFF_ACCEL_8, + + SFX_CAR_ACCEL_9, + SFX_CAR_AFTER_ACCEL_9, + SFX_CAR_FINGER_OFF_ACCEL_9, + + SFX_CAR_ACCEL_10, + SFX_CAR_AFTER_ACCEL_10, + SFX_CAR_FINGER_OFF_ACCEL_10, + + SFX_CAR_ACCEL_11, + SFX_CAR_AFTER_ACCEL_11, + SFX_CAR_FINGER_OFF_ACCEL_11, + + SFX_CAR_ACCEL_12, + SFX_CAR_AFTER_ACCEL_12, + SFX_CAR_FINGER_OFF_ACCEL_12, + + // some CHAINSAW STUFF + SFX_CAR_CHAINSAW_IDLE, + SFX_CAR_CHAINSAW_ATTACK, + SFX_CAR_CHAINSAW_EMPTY, // unused + + SFX_RC_IDLE, // 10976 + SFX_RC_REV, // 10977 + SFX_RC_EMPTY, // 10978 + + SFX_CAR_RC_HELI, // 10979 + SFX_CAR_AFTER_ACCEL_15, // empty + SFX_CAR_FINGER_OFF_ACCEL_15, // empty + + SFX_CAR_ACCEL_16, // empty + SFX_CAR_AFTER_ACCEL_16, // empty + SFX_CAR_FINGER_OFF_ACCEL_16, // empty + + // bike stuff apparently + SFX_CAR_ACCEL_17, + SFX_CAR_AFTER_ACCEL_17, + SFX_CAR_FINGER_OFF_ACCEL_17, + SFX_CAR_WIND_17, + + SFX_CAR_ACCEL_18, + SFX_CAR_AFTER_ACCEL_18, + SFX_CAR_FINGER_OFF_ACCEL_18, + SFX_CAR_WIND_18, + + SFX_CAR_ACCEL_19, + SFX_CAR_AFTER_ACCEL_19, + SFX_CAR_FINGER_OFF_ACCEL_19, + SFX_CAR_WIND_19, + + SFX_CAR_ACCEL_20, + SFX_CAR_AFTER_ACCEL_20, + SFX_CAR_FINGER_OFF_ACCEL_20, + SFX_CAR_WIND_20, + + // some emptinnes here + SFX_CAR_ACCEL_21, + SFX_CAR_AFTER_ACCEL_21, + SFX_CAR_FINGER_OFF_ACCEL_21, + SFX_CAR_ACCEL_22, + SFX_CAR_AFTER_ACCEL_22, + SFX_CAR_FINGER_OFF_ACCEL_22, + + SFX_HELI_APACHE_1, + SFX_HELI_APACHE_2, + SFX_HELI_APACHE_3, + SFX_HELI_APACHE_4, + + // something padded for more heli? + SFX_HELI_UNUSED_1, + SFX_HELI_UNUSED_2, + SFX_HELI_UNUSED_3, + SFX_HELI_UNUSED_4, + + SFX_SEAPLANE_PRO1, // 11018 + SFX_SEAPLANE_PRO2, // 11019 + SFX_SEAPLANE_PRO3, // 11020 + SFX_SEAPLANE_PRO4, // 11021 + // low fuel + SFX_SEAPLANE_LOW, // 11022 + + // something padded for more plane? + SFX_PLANE_UNUSED_1, + SFX_PLANE_UNUSED_2, + SFX_PLANE_UNUSED_3, + SFX_PLANE_UNUSED_4, + + // script objects + SFX_BUILDINGS_BANK_ALARM, // 11027 + SFX_BUILDING_SNORE, // 11028 + SFX_BUILDING_BAR_1, // 11029 + SFX_BUILDING_BAR_2, // 11030 + SFX_BUILDING_BAR_3, // 11031 + SFX_BUILDING_BAR_4, // 11032 + SFX_BUILDING_MALIBU_1, // 11033 + SFX_BUILDING_MALIBU_2, // 11034 + SFX_BUILDING_MALIBU_3, // 11035 + SFX_BUILDING_STRIP_1, // 11036 + SFX_BUILDING_STRIP_2, // 11037 + SFX_BUILDING_STRIP_3, // 11038 + SFX_BUILDING_CHURCH, // 11039 + SFX_BUILDING_FAN_1, // 11040 + SFX_BUILDING_FAN_2, // 11041 + SFX_BUILDING_FAN_3, // 11042 + SFX_BUILDING_FAN_4, // 11043 + SFX_BUILDING_INSECTS_1, // 11044 + SFX_BUILDING_INSECTS_2, // 11045 + SFX_BUILDING_INSECTS_3, // 11046 + SFX_BUILDING_INSECTS_4, // 11047 + SFX_BUILDING_INSECTS_5, // 11048 + SFX_CLUB_1, // 11049 + SFX_CLUB_2, // 11050 + SFX_CLUB_3, // 11051 + SFX_CLUB_4, // 11052 + + SFX_FOOTSTEP_GRASS_1, + SFX_FOOTSTEP_GRASS_2, + SFX_FOOTSTEP_GRASS_3, + SFX_FOOTSTEP_GRASS_4, + SFX_FOOTSTEP_GRASS_5, + SFX_FOOTSTEP_GRAVEL_1, + SFX_FOOTSTEP_GRAVEL_2, + SFX_FOOTSTEP_GRAVEL_3, + SFX_FOOTSTEP_GRAVEL_4, + SFX_FOOTSTEP_GRAVEL_5, + SFX_FOOTSTEP_WOOD_1, + SFX_FOOTSTEP_WOOD_2, + SFX_FOOTSTEP_WOOD_3, + SFX_FOOTSTEP_WOOD_4, + SFX_FOOTSTEP_WOOD_5, + SFX_FOOTSTEP_METAL_1, + SFX_FOOTSTEP_METAL_2, + SFX_FOOTSTEP_METAL_3, + SFX_FOOTSTEP_METAL_4, + SFX_FOOTSTEP_METAL_5, + SFX_FOOTSTEP_WATER_1, + SFX_FOOTSTEP_WATER_2, + SFX_FOOTSTEP_WATER_3, + SFX_FOOTSTEP_WATER_4, + SFX_FOOTSTEP_SAND_1, + SFX_FOOTSTEP_SAND_2, + SFX_FOOTSTEP_SAND_3, + SFX_FOOTSTEP_SAND_4, + + // ped comments + + SFX_BMYBB_BLOCKED_1, + SFX_BMYBB_BLOCKED_2, + SFX_BMYBB_BLOCKED_3, + SFX_BMYBB_BLOCKED_4, + SFX_BMYBB_BLOCKED_5, + SFX_BMYBB_BLOCKED_6, + SFX_BMYBB_BLOCKED_7, + SFX_BMYBB_BLOCKED_8, + SFX_BMYBB_BLOCKED_9, + SFX_BMYBB_BLOCKED_10, + SFX_BMYBB_BLOCKED_11, + SFX_BMYBB_BLOCKED_12, + SFX_BMYBB_BLOCKED_13, + SFX_BMYBB_BUMP_1, + SFX_BMYBB_BUMP_2, + SFX_BMYBB_BUMP_3, + SFX_BMYBB_BUMP_4, + SFX_BMYBB_BUMP_5, + SFX_BMYBB_BUMP_6, + SFX_BMYBB_BUMP_7, + SFX_BMYBB_BUMP_8, + SFX_BMYBB_BUMP_9, + SFX_BMYBB_BUMP_10, + SFX_BMYBB_BUMP_11, + SFX_BMYBB_BUMP_12, + SFX_BMYBB_BUMP_13, + SFX_BMYBB_BUMP_14, + SFX_BMYBB_BUMP_15, + SFX_BMYBB_BUMP_16, + SFX_BMYBB_BUMP_17, + SFX_BMYBB_CAR_CRASH_1, + SFX_BMYBB_CAR_CRASH_2, + SFX_BMYBB_CAR_CRASH_3, + SFX_BMYBB_CAR_CRASH_4, + SFX_BMYBB_CAR_CRASH_5, + SFX_BMYBB_CAR_CRASH_6, + SFX_BMYBB_CAR_CRASH_7, + SFX_BMYBB_CAR_CRASH_8, + SFX_BMYBB_CAR_CRASH_9, + SFX_BMYBB_CHAT_1, + SFX_BMYBB_CHAT_2, + SFX_BMYBB_CHAT_3, + SFX_BMYBB_CHAT_4, + SFX_BMYBB_CHAT_5, + SFX_BMYBB_CHAT_6, + SFX_BMYBB_CHAT_7, + SFX_BMYBB_CHAT_8, + SFX_BMYBB_CHAT_9, + SFX_BMYBB_CHAT_10, + SFX_BMYBB_CHAT_11, + SFX_BMYBB_CHAT_12, + SFX_BMYBB_CHAT_13, + SFX_BMYBB_CHAT_14, + SFX_BMYBB_CHAT_15, + SFX_BMYBB_CHAT_16, + SFX_BMYBB_CHAT_17, + SFX_BMYBB_CHAT_18, + SFX_BMYBB_CHAT_19, + SFX_BMYBB_CHAT_20, + SFX_BMYBB_CHAT_21, + SFX_BMYBB_DODGE_1, + SFX_BMYBB_DODGE_2, + SFX_BMYBB_DODGE_3, + SFX_BMYBB_DODGE_4, + SFX_BMYBB_DODGE_5, + SFX_BMYBB_DODGE_6, + SFX_BMYBB_DODGE_7, + SFX_BMYBB_DODGE_8, + SFX_BMYBB_DODGE_9, + SFX_BMYBB_DODGE_10, + SFX_BMYBB_DODGE_11, + SFX_BMYBB_DODGE_12, + SFX_BMYBB_DODGE_13, + SFX_BMYBB_DODGE_14, + SFX_BMYBB_DODGE_15, + SFX_BMYBB_DODGE_16, + SFX_BMYBB_DODGE_17, + SFX_BMYBB_DODGE_18, + SFX_BMYBB_EYEING_1, + SFX_BMYBB_EYEING_2, + SFX_BMYBB_EYEING_3, + SFX_BMYBB_EYEING_4, + SFX_BMYBB_EYEING_5, + SFX_BMYBB_EYEING_6, + SFX_BMYBB_EYEING_7, + SFX_BMYBB_EYEING_8, + SFX_BMYBB_EYEING_9, + SFX_BMYBB_EYEING_10, + SFX_BMYBB_EYEING_11, + SFX_BMYBB_EYEING_12, + SFX_BMYBB_EYEING_13, + SFX_BMYBB_EYEING_14, + SFX_BMYBB_EYEING_15, + SFX_BMYBB_EYEING_16, + SFX_BMYBB_FIGHT_1, + SFX_BMYBB_FIGHT_2, + SFX_BMYBB_FIGHT_3, + SFX_BMYBB_FIGHT_4, + SFX_BMYBB_FIGHT_5, + SFX_BMYBB_FIGHT_6, + SFX_BMYBB_FIGHT_7, + SFX_BMYBB_FIGHT_8, + SFX_BMYBB_FIGHT_9, + SFX_BMYBB_FIGHT_10, + SFX_BMYBB_FIGHT_11, + SFX_BMYBB_FIGHT_12, + SFX_BMYBB_GENERIC_CRASH_1, + SFX_BMYBB_GENERIC_CRASH_2, + SFX_BMYBB_GENERIC_CRASH_3, + SFX_BMYBB_GENERIC_CRASH_4, + SFX_BMYBB_GENERIC_CRASH_5, + SFX_BMYBB_GENERIC_CRASH_6, + SFX_BMYBB_GENERIC_CRASH_7, + SFX_BMYBB_GENERIC_CRASH_8, + SFX_BMYBB_GENERIC_CRASH_9, + SFX_BMYBB_GUN_COOL_1, + SFX_BMYBB_GUN_COOL_2, + SFX_BMYBB_GUN_COOL_3, + SFX_BMYBB_GUN_COOL_4, + SFX_BMYBB_GUN_COOL_5, + SFX_BMYBB_INNOCENT_1, + SFX_BMYBB_INNOCENT_2, + SFX_BMYBB_INNOCENT_3, + SFX_BMYBB_INNOCENT_4, + SFX_BMYBB_JACKED_1, + SFX_BMYBB_JACKED_2, + SFX_BMYBB_JACKED_3, + SFX_BMYBB_JACKED_4, + SFX_BMYBB_JACKED_5, + SFX_BMYBB_JACKED_6, + SFX_BMYBB_JACKED_7, + SFX_BMYBB_JACKED_8, + SFX_BMYBB_JACKED_9, + SFX_BMYBB_JACKED_10, + SFX_BMYBB_JACKED_11, + SFX_BMYBB_JACKING_1, + SFX_BMYBB_JACKING_2, + SFX_BMYBB_JACKING_3, + SFX_BMYBB_JACKING_4, + SFX_BMYBB_JACKING_5, + SFX_BMYBB_JACKING_6, + SFX_BMYBB_JACKING_7, + SFX_BMYBB_JACKING_8, + SFX_BMYBB_JACKING_9, + SFX_BMYBB_JEER_1, + SFX_BMYBB_JEER_2, + SFX_BMYBB_JEER_3, + SFX_BMYBB_JEER_4, + SFX_BMYBB_JEER_5, + SFX_BMYBB_JEER_6, + SFX_BMYBB_JEER_7, + SFX_BMYBB_JEER_8, + SFX_BMYBB_JEER_9, + SFX_BMYBB_JEER_10, + SFX_BMYBB_JEER_11, + SFX_BMYBB_JEER_12, + SFX_BMYBB_JEER_13, + SFX_BMYBB_JEER_14, + SFX_BMYBB_JEER_15, + SFX_BMYBB_JEER_16, + SFX_BMYBB_LOST_1, + SFX_BMYBB_LOST_2, + SFX_BMYBB_MUGGED_1, + SFX_BMYBB_MUGGED_2, + SFX_BMYBB_MUGGED_3, + SFX_BMYBB_MUGGED_4, + SFX_BMYBB_MUGGED_5, + SFX_BMYBB_MUGGING_1, + SFX_BMYBB_MUGGING_2, + SFX_BMYBB_MUGGING_3, + SFX_BMYBB_MUGGING_4, + SFX_BMYBB_MUGGING_5, + SFX_BMYBB_MUGGING_6, + SFX_BMYBB_MUGGING_7, + SFX_BMYBB_MUGGING_8, + SFX_BMYBB_SAVED_1, + SFX_BMYBB_SAVED_2, + SFX_BMYBB_SAVED_3, + SFX_BMYBB_SAVED_4, + SFX_BMYBB_SAVED_5, + SFX_BMYBB_SAVED_6, + SFX_BMYBB_SHOCKED_1, + SFX_BMYBB_SHOCKED_2, + SFX_BMYBB_SHOCKED_3, + SFX_BMYBB_SHOCKED_4, + SFX_BMYBB_SHOCKED_5, + SFX_BMYBB_SHOCKED_6, + SFX_BMYBB_TAXI_1, + SFX_BMYBB_TAXI_2, + SFX_BMYBB_TAXI_3, + + SFX_POLICE_BOAT_1, + SFX_POLICE_BOAT_2, + SFX_POLICE_BOAT_3, + SFX_POLICE_BOAT_4, + SFX_POLICE_BOAT_5, + SFX_POLICE_BOAT_6, + SFX_POLICE_BOAT_7, + SFX_POLICE_BOAT_8, + SFX_POLICE_BOAT_9, + SFX_POLICE_BOAT_10, + SFX_POLICE_BOAT_11, + SFX_POLICE_BOAT_12, + SFX_POLICE_BOAT_13, + SFX_POLICE_BOAT_14, + SFX_POLICE_BOAT_15, + SFX_POLICE_BOAT_16, + SFX_POLICE_BOAT_17, + SFX_POLICE_BOAT_18, + SFX_POLICE_BOAT_19, + SFX_POLICE_BOAT_20, + SFX_POLICE_BOAT_21, + SFX_POLICE_BOAT_22, + SFX_POLICE_BOAT_23, + + SFX_POLICE_HELI_1, + SFX_POLICE_HELI_2, + SFX_POLICE_HELI_3, + SFX_POLICE_HELI_4, + SFX_POLICE_HELI_5, + SFX_POLICE_HELI_6, + SFX_POLICE_HELI_7, + SFX_POLICE_HELI_8, + SFX_POLICE_HELI_9, + SFX_POLICE_HELI_10, + SFX_POLICE_HELI_11, + SFX_POLICE_HELI_12, + SFX_POLICE_HELI_13, + SFX_POLICE_HELI_14, + SFX_POLICE_HELI_15, + SFX_POLICE_HELI_16, + SFX_POLICE_HELI_17, + SFX_POLICE_HELI_18, + SFX_POLICE_HELI_19, + SFX_POLICE_HELI_20, + + SFX_JFOTO_BLOCKED_1, + SFX_JFOTO_BLOCKED_2, + SFX_JFOTO_BLOCKED_3, + SFX_JFOTO_BLOCKED_4, + SFX_JFOTO_BLOCKED_5, + SFX_JFOTO_BLOCKED_6, + SFX_JFOTO_BLOCKED_7, + SFX_JFOTO_BLOCKED_8, + + SFX_JFOTO_BUMP_1, + SFX_JFOTO_BUMP_2, + SFX_JFOTO_BUMP_3, + SFX_JFOTO_BUMP_4, + SFX_JFOTO_BUMP_5, + SFX_JFOTO_BUMP_6, + SFX_JFOTO_BUMP_7, + SFX_JFOTO_BUMP_8, + SFX_JFOTO_BUMP_9, + SFX_JFOTO_BUMP_10, + + SFX_JFOTO_CAR_CRASH_1, + SFX_JFOTO_CAR_CRASH_2, + SFX_JFOTO_CAR_CRASH_3, + SFX_JFOTO_CAR_CRASH_4, + SFX_JFOTO_CAR_CRASH_5, + SFX_JFOTO_CAR_CRASH_6, + SFX_JFOTO_CAR_CRASH_7, + SFX_JFOTO_CAR_CRASH_8, + + SFX_JFOTO_CHAT_1, + SFX_JFOTO_CHAT_2, + SFX_JFOTO_CHAT_3, + SFX_JFOTO_CHAT_4, + SFX_JFOTO_CHAT_5, + SFX_JFOTO_CHAT_6, + SFX_JFOTO_CHAT_7, + SFX_JFOTO_CHAT_8, + SFX_JFOTO_CHAT_9, + SFX_JFOTO_CHAT_10, + SFX_JFOTO_CHAT_11, + SFX_JFOTO_CHAT_12, + SFX_JFOTO_CHAT_13, + + SFX_JFOTO_DODGE_1, + SFX_JFOTO_DODGE_2, + SFX_JFOTO_DODGE_3, + SFX_JFOTO_DODGE_4, + SFX_JFOTO_DODGE_5, + SFX_JFOTO_DODGE_6, + SFX_JFOTO_DODGE_7, + SFX_JFOTO_DODGE_8, + SFX_JFOTO_DODGE_9, + + SFX_JFOTO_GENERIC_CRASH_1, + SFX_JFOTO_GENERIC_CRASH_2, + SFX_JFOTO_GENERIC_CRASH_3, + SFX_JFOTO_GENERIC_CRASH_4, + SFX_JFOTO_GENERIC_CRASH_5, + SFX_JFOTO_GENERIC_CRASH_6, + SFX_JFOTO_GUN_PANIC_1, + SFX_JFOTO_GUN_PANIC_2, + SFX_JFOTO_GUN_PANIC_3, + SFX_JFOTO_GUN_PANIC_4, + SFX_JFOTO_JACKED_1, + SFX_JFOTO_JACKED_2, + SFX_JFOTO_JACKED_3, + SFX_JFOTO_JACKED_4, + SFX_JFOTO_JACKED_5, + SFX_JFOTO_LOST_1, + SFX_JFOTO_MUGGED_1, + SFX_JFOTO_MUGGED_2, + SFX_JFOTO_RUN_1, + SFX_JFOTO_RUN_2, + SFX_JFOTO_RUN_3, + SFX_JFOTO_RUN_4, + SFX_JFOTO_RUN_5, + SFX_JFOTO_SAVED_1, + SFX_JFOTO_SAVED_2, + SFX_JFOTO_SHOCKED_1, + SFX_JFOTO_TAXI_1, + SFX_JFOTO_TAXI_2, + + SFX_JMOTO_BLOCKED_1, + SFX_JMOTO_BLOCKED_2, + SFX_JMOTO_BLOCKED_3, + SFX_JMOTO_BLOCKED_4, + SFX_JMOTO_BLOCKED_5, + SFX_JMOTO_BLOCKED_6, + SFX_JMOTO_BLOCKED_7, + SFX_JMOTO_BLOCKED_8, + SFX_JMOTO_BUMP_1, + SFX_JMOTO_BUMP_2, + SFX_JMOTO_BUMP_3, + SFX_JMOTO_BUMP_4, + SFX_JMOTO_BUMP_5, + SFX_JMOTO_BUMP_6, + SFX_JMOTO_BUMP_7, + SFX_JMOTO_BUMP_8, + SFX_JMOTO_CAR_CRASH_1, + SFX_JMOTO_CAR_CRASH_2, + SFX_JMOTO_CAR_CRASH_3, + SFX_JMOTO_CAR_CRASH_4, + SFX_JMOTO_CAR_CRASH_5, + SFX_JMOTO_CAR_CRASH_6, + SFX_JMOTO_CHAT_1, + SFX_JMOTO_CHAT_2, + SFX_JMOTO_CHAT_3, + SFX_JMOTO_CHAT_4, + SFX_JMOTO_CHAT_5, + SFX_JMOTO_CHAT_6, + SFX_JMOTO_CHAT_7, + SFX_JMOTO_DODGE_1, + SFX_JMOTO_DODGE_2, + SFX_JMOTO_DODGE_3, + SFX_JMOTO_DODGE_4, + SFX_JMOTO_DODGE_5, + SFX_JMOTO_DODGE_6, + SFX_JMOTO_GENERIC_CRASH_1, + SFX_JMOTO_GENERIC_CRASH_2, + SFX_JMOTO_GENERIC_CRASH_3, + SFX_JMOTO_GENERIC_CRASH_4, + SFX_JMOTO_GENERIC_CRASH_5, + SFX_JMOTO_GENERIC_CRASH_6, + SFX_JMOTO_GUN_PANIC_1, + SFX_JMOTO_GUN_PANIC_2, + SFX_JMOTO_GUN_PANIC_3, + SFX_JMOTO_GUN_PANIC_4, + SFX_JMOTO_JACKED_1, + SFX_JMOTO_JACKED_2, + SFX_JMOTO_JACKED_3, + SFX_JMOTO_JACKED_4, + SFX_JMOTO_LOST_1, + SFX_JMOTO_MUGGED_1, + SFX_JMOTO_MUGGED_2, + SFX_JMOTO_RUN_1, + SFX_JMOTO_RUN_2, + SFX_JMOTO_RUN_3, + SFX_JMOTO_RUN_4, + SFX_JMOTO_SAVED_1, + SFX_JMOTO_SHOCKED_1, + SFX_JMOTO_TAXI_1, + + SFX_BMYBE_BLOCKED_1, + SFX_BMYBE_BLOCKED_2, + SFX_BMYBE_BLOCKED_3, + SFX_BMYBE_BLOCKED_4, + SFX_BMYBE_BLOCKED_5, + SFX_BMYBE_BLOCKED_6, + SFX_BMYBE_BLOCKED_7, + SFX_BMYBE_BLOCKED_8, + SFX_BMYBE_BUMP_1, + SFX_BMYBE_BUMP_2, + SFX_BMYBE_BUMP_3, + SFX_BMYBE_BUMP_4, + SFX_BMYBE_BUMP_5, + SFX_BMYBE_BUMP_6, + SFX_BMYBE_BUMP_7, + SFX_BMYBE_BUMP_8, + SFX_BMYBE_BUMP_9, + SFX_BMYBE_BUMP_10, + SFX_BMYBE_CAR_CRASH_1, + SFX_BMYBE_CAR_CRASH_2, + SFX_BMYBE_CAR_CRASH_3, + SFX_BMYBE_CAR_CRASH_4, + SFX_BMYBE_CAR_CRASH_5, + SFX_BMYBE_CAR_CRASH_6, + SFX_BMYBE_CAR_CRASH_7, + SFX_BMYBE_CAR_CRASH_8, + SFX_BMYBE_CHAT_1, + SFX_BMYBE_CHAT_2, + SFX_BMYBE_CHAT_3, + SFX_BMYBE_CHAT_4, + SFX_BMYBE_CHAT_5, + SFX_BMYBE_CHAT_6, + SFX_BMYBE_CHAT_7, + SFX_BMYBE_CHAT_8, + SFX_BMYBE_CHAT_9, + SFX_BMYBE_CHAT_10, + SFX_BMYBE_DODGE_1, + SFX_BMYBE_DODGE_2, + SFX_BMYBE_DODGE_3, + SFX_BMYBE_DODGE_4, + SFX_BMYBE_DODGE_5, + SFX_BMYBE_DODGE_6, + SFX_BMYBE_DODGE_7, + SFX_BMYBE_DODGE_8, + SFX_BMYBE_DODGE_9, + SFX_BMYBE_DODGE_10, + SFX_BMYBE_EYEING_1, + SFX_BMYBE_EYEING_2, + SFX_BMYBE_FIGHT_1, + SFX_BMYBE_FIGHT_2, + SFX_BMYBE_FIGHT_3, + SFX_BMYBE_FIGHT_4, + SFX_BMYBE_FIGHT_5, + SFX_BMYBE_FIGHT_6, + SFX_BMYBE_FIGHT_7, + SFX_BMYBE_FIGHT_8, + SFX_BMYBE_GENERIC_CRASH_1, + SFX_BMYBE_GENERIC_CRASH_2, + SFX_BMYBE_GENERIC_CRASH_3, + SFX_BMYBE_GENERIC_CRASH_4, + SFX_BMYBE_GENERIC_CRASH_5, + SFX_BMYBE_GENERIC_CRASH_6, + SFX_BMYBE_GENERIC_CRASH_7, + SFX_BMYBE_GENERIC_CRASH_8, + SFX_BMYBE_GUN_COOL_1, + SFX_BMYBE_GUN_COOL_2, + SFX_BMYBE_GUN_COOL_3, + SFX_BMYBE_GUN_COOL_4, + SFX_BMYBE_JACKED_1, + SFX_BMYBE_JACKED_2, + SFX_BMYBE_JACKED_3, + SFX_BMYBE_JACKED_4, + SFX_BMYBE_JACKED_5, + SFX_BMYBE_JACKED_6, + SFX_BMYBE_JACKING_1, + SFX_BMYBE_JACKING_2, + SFX_BMYBE_JACKING_3, + SFX_BMYBE_LOST_1, + SFX_BMYBE_MUGGED_1, + SFX_BMYBE_SAVED_1, + SFX_BMYBE_TAXI_1, + + SFX_HFOBE_BLOCKED_1, + SFX_HFOBE_BLOCKED_2, + SFX_HFOBE_BLOCKED_3, + SFX_HFOBE_BLOCKED_4, + SFX_HFOBE_BLOCKED_5, + SFX_HFOBE_BLOCKED_6, + SFX_HFOBE_BUMP_1, + SFX_HFOBE_BUMP_2, + SFX_HFOBE_BUMP_3, + SFX_HFOBE_BUMP_4, + SFX_HFOBE_BUMP_5, + SFX_HFOBE_BUMP_6, + SFX_HFOBE_BUMP_7, + SFX_HFOBE_BUMP_8, + SFX_HFOBE_BUMP_9, + SFX_HFOBE_BUMP_10, + SFX_HFOBE_BUMP_11, + SFX_HFOBE_CAR_CRASH_1, + SFX_HFOBE_CAR_CRASH_2, + SFX_HFOBE_CAR_CRASH_3, + SFX_HFOBE_CAR_CRASH_4, + SFX_HFOBE_CAR_CRASH_5, + SFX_HFOBE_CAR_CRASH_6, + SFX_HFOBE_CHAT_1, + SFX_HFOBE_CHAT_2, + SFX_HFOBE_CHAT_3, + SFX_HFOBE_CHAT_4, + SFX_HFOBE_CHAT_5, + SFX_HFOBE_CHAT_6, + SFX_HFOBE_CHAT_7, + SFX_HFOBE_CHAT_8, + SFX_HFOBE_CHAT_9, + SFX_HFOBE_CHAT_10, + SFX_HFOBE_DODGE_1, + SFX_HFOBE_DODGE_2, + SFX_HFOBE_DODGE_3, + SFX_HFOBE_DODGE_4, + SFX_HFOBE_DODGE_5, + SFX_HFOBE_DODGE_6, + SFX_HFOBE_DODGE_7, + SFX_HFOBE_GENERIC_CRASH_1, + SFX_HFOBE_GENERIC_CRASH_2, + SFX_HFOBE_GENERIC_CRASH_3, + SFX_HFOBE_GENERIC_CRASH_4, + SFX_HFOBE_GENERIC_CRASH_5, + SFX_HFOBE_GUN_PANIC_1, + SFX_HFOBE_GUN_PANIC_2, + SFX_HFOBE_GUN_PANIC_3, + SFX_HFOBE_GUN_PANIC_4, + SFX_HFOBE_GUN_PANIC_5, + SFX_HFOBE_JACKED_1, + SFX_HFOBE_JACKED_2, + SFX_HFOBE_JACKED_3, + SFX_HFOBE_JACKED_4, + SFX_HFOBE_JACKED_5, + SFX_HFOBE_JACKED_6, + SFX_HFOBE_LOST_1, + SFX_HFOBE_LOST_2, + SFX_HFOBE_RUN_1, + SFX_HFOBE_RUN_2, + SFX_HFOBE_RUN_3, + SFX_HFOBE_RUN_4, + SFX_HFOBE_SAVED_1, + SFX_HFOBE_SHOCKED_1, + SFX_HFOBE_SHOCKED_2, + SFX_HFOBE_TAXI_1, + SFX_HFOBE_TAXI_2, + + SFX_STREET_GANG_1_BLOCKED_1, + SFX_STREET_GANG_1_BLOCKED_2, + SFX_STREET_GANG_1_BLOCKED_3, + SFX_STREET_GANG_1_BLOCKED_4, + SFX_STREET_GANG_1_BLOCKED_5, + SFX_STREET_GANG_1_BLOCKED_6, + SFX_STREET_GANG_1_BLOCKED_7, + SFX_STREET_GANG_1_BLOCKED_8, + SFX_STREET_GANG_1_BUMP_1, + SFX_STREET_GANG_1_BUMP_2, + SFX_STREET_GANG_1_BUMP_3, + SFX_STREET_GANG_1_BUMP_4, + SFX_STREET_GANG_1_BUMP_5, + SFX_STREET_GANG_1_BUMP_6, + SFX_STREET_GANG_1_BUMP_7, + SFX_STREET_GANG_1_BUMP_8, + SFX_STREET_GANG_1_BUMP_9, + SFX_STREET_GANG_1_BUMP_10, + SFX_STREET_GANG_1_CAR_CRASH_1, + SFX_STREET_GANG_1_CAR_CRASH_2, + SFX_STREET_GANG_1_CAR_CRASH_3, + SFX_STREET_GANG_1_CAR_CRASH_4, + SFX_STREET_GANG_1_CAR_CRASH_5, + SFX_STREET_GANG_1_CAR_CRASH_6, + SFX_STREET_GANG_1_CHAT_1, + SFX_STREET_GANG_1_CHAT_2, + SFX_STREET_GANG_1_CHAT_3, + SFX_STREET_GANG_1_CHAT_4, + SFX_STREET_GANG_1_CHAT_5, + SFX_STREET_GANG_1_CHAT_6, + SFX_STREET_GANG_1_CHAT_7, + SFX_STREET_GANG_1_CHAT_8, + SFX_STREET_GANG_1_CHAT_9, + SFX_STREET_GANG_1_CHAT_10, + SFX_STREET_GANG_1_CHAT_11, + SFX_STREET_GANG_1_CHAT_12, + SFX_STREET_GANG_1_DODGE_1, + SFX_STREET_GANG_1_DODGE_2, + SFX_STREET_GANG_1_DODGE_3, + SFX_STREET_GANG_1_DODGE_4, + SFX_STREET_GANG_1_DODGE_5, + SFX_STREET_GANG_1_DODGE_6, + SFX_STREET_GANG_1_DODGE_7, + SFX_STREET_GANG_1_DODGE_8, + SFX_STREET_GANG_1_DODGE_9, + SFX_STREET_GANG_1_EYEING_1, + SFX_STREET_GANG_1_EYEING_2, + SFX_STREET_GANG_1_EYEING_3, + SFX_STREET_GANG_1_FIGHT_1, + SFX_STREET_GANG_1_FIGHT_2, + SFX_STREET_GANG_1_FIGHT_3, + SFX_STREET_GANG_1_FIGHT_4, + SFX_STREET_GANG_1_FIGHT_5, + SFX_STREET_GANG_1_FIGHT_6, + SFX_STREET_GANG_1_FIGHT_7, + SFX_STREET_GANG_1_FIGHT_8, + SFX_STREET_GANG_1_FIGHT_9, + SFX_STREET_GANG_1_FIGHT_10, + SFX_STREET_GANG_1_GENERIC_CRASH_1, + SFX_STREET_GANG_1_GENERIC_CRASH_2, + SFX_STREET_GANG_1_GENERIC_CRASH_3, + SFX_STREET_GANG_1_GENERIC_CRASH_4, + SFX_STREET_GANG_1_GENERIC_CRASH_5, + SFX_STREET_GANG_1_GENERIC_CRASH_6, + SFX_STREET_GANG_1_GUN_COOL_1, + SFX_STREET_GANG_1_GUN_COOL_2, + SFX_STREET_GANG_1_GUN_COOL_3, + SFX_STREET_GANG_1_GUN_COOL_4, + SFX_STREET_GANG_1_GUN_COOL_5, + SFX_STREET_GANG_1_JACKED_1, + SFX_STREET_GANG_1_JACKED_2, + SFX_STREET_GANG_1_JACKED_3, + SFX_STREET_GANG_1_JACKED_4, + SFX_STREET_GANG_1_JACKED_5, + SFX_STREET_GANG_1_JACKING_1, + SFX_STREET_GANG_1_JACKING_2, + SFX_STREET_GANG_1_JACKING_3, + SFX_STREET_GANG_1_JACKING_4, + SFX_STREET_GANG_1_JACKING_5, + SFX_STREET_GANG_1_LOST_1, + SFX_STREET_GANG_1_LOST_2, + SFX_STREET_GANG_1_MUGGED_1, + SFX_STREET_GANG_1_MUGGED_2, + SFX_STREET_GANG_1_MUGGED_3, + SFX_STREET_GANG_1_MUGGING_1, + SFX_STREET_GANG_1_MUGGING_2, + SFX_STREET_GANG_1_MUGGING_3, + SFX_STREET_GANG_1_MUGGING_4, + SFX_STREET_GANG_1_MUGGING_5, + SFX_STREET_GANG_1_SAVED_1, + SFX_STREET_GANG_1_SHOCKED_1, + SFX_STREET_GANG_1_SHOCKED_2, + SFX_STREET_GANG_1_TAXI_1, + + SFX_STREET_GANG_2_BLOCKED_1, + SFX_STREET_GANG_2_BLOCKED_2, + SFX_STREET_GANG_2_BLOCKED_3, + SFX_STREET_GANG_2_BLOCKED_4, + SFX_STREET_GANG_2_BLOCKED_5, + SFX_STREET_GANG_2_BLOCKED_6, + SFX_STREET_GANG_2_BLOCKED_7, + SFX_STREET_GANG_2_BLOCKED_8, + SFX_STREET_GANG_2_BUMP_1, + SFX_STREET_GANG_2_BUMP_2, + SFX_STREET_GANG_2_BUMP_3, + SFX_STREET_GANG_2_BUMP_4, + SFX_STREET_GANG_2_BUMP_5, + SFX_STREET_GANG_2_BUMP_6, + SFX_STREET_GANG_2_BUMP_7, + SFX_STREET_GANG_2_BUMP_8, + SFX_STREET_GANG_2_BUMP_9, + SFX_STREET_GANG_2_BUMP_10, + SFX_STREET_GANG_2_CAR_CRASH_1, + SFX_STREET_GANG_2_CAR_CRASH_2, + SFX_STREET_GANG_2_CAR_CRASH_3, + SFX_STREET_GANG_2_CAR_CRASH_4, + SFX_STREET_GANG_2_CAR_CRASH_5, + SFX_STREET_GANG_2_CAR_CRASH_6, + SFX_STREET_GANG_2_CHAT_1, + SFX_STREET_GANG_2_CHAT_2, + SFX_STREET_GANG_2_CHAT_3, + SFX_STREET_GANG_2_CHAT_4, + SFX_STREET_GANG_2_CHAT_5, + SFX_STREET_GANG_2_CHAT_6, + SFX_STREET_GANG_2_CHAT_7, + SFX_STREET_GANG_2_CHAT_8, + SFX_STREET_GANG_2_CHAT_9, + SFX_STREET_GANG_2_CHAT_10, + SFX_STREET_GANG_2_CHAT_11, + SFX_STREET_GANG_2_CHAT_12, + SFX_STREET_GANG_2_DODGE_1, + SFX_STREET_GANG_2_DODGE_2, + SFX_STREET_GANG_2_DODGE_3, + SFX_STREET_GANG_2_DODGE_4, + SFX_STREET_GANG_2_DODGE_5, + SFX_STREET_GANG_2_DODGE_6, + SFX_STREET_GANG_2_DODGE_7, + SFX_STREET_GANG_2_DODGE_8, + SFX_STREET_GANG_2_DODGE_9, + SFX_STREET_GANG_2_EYEING_1, + SFX_STREET_GANG_2_EYEING_2, + SFX_STREET_GANG_2_EYEING_3, + SFX_STREET_GANG_2_FIGHT_1, + SFX_STREET_GANG_2_FIGHT_2, + SFX_STREET_GANG_2_FIGHT_3, + SFX_STREET_GANG_2_FIGHT_4, + SFX_STREET_GANG_2_FIGHT_5, + SFX_STREET_GANG_2_FIGHT_6, + SFX_STREET_GANG_2_FIGHT_7, + SFX_STREET_GANG_2_FIGHT_8, + SFX_STREET_GANG_2_FIGHT_9, + SFX_STREET_GANG_2_FIGHT_10, + SFX_STREET_GANG_2_GENERIC_CRASH_1, + SFX_STREET_GANG_2_GENERIC_CRASH_2, + SFX_STREET_GANG_2_GENERIC_CRASH_3, + SFX_STREET_GANG_2_GENERIC_CRASH_4, + SFX_STREET_GANG_2_GENERIC_CRASH_5, + SFX_STREET_GANG_2_GENERIC_CRASH_6, + SFX_STREET_GANG_2_GUN_COOL_1, + SFX_STREET_GANG_2_GUN_COOL_2, + SFX_STREET_GANG_2_GUN_COOL_3, + SFX_STREET_GANG_2_GUN_COOL_4, + SFX_STREET_GANG_2_GUN_COOL_5, + SFX_STREET_GANG_2_JACKED_1, + SFX_STREET_GANG_2_JACKED_2, + SFX_STREET_GANG_2_JACKED_3, + SFX_STREET_GANG_2_JACKED_4, + SFX_STREET_GANG_2_JACKED_5, + SFX_STREET_GANG_2_JACKING_1, + SFX_STREET_GANG_2_JACKING_2, + SFX_STREET_GANG_2_JACKING_3, + SFX_STREET_GANG_2_JACKING_4, + SFX_STREET_GANG_2_JACKING_5, + SFX_STREET_GANG_2_LOST_1, + SFX_STREET_GANG_2_LOST_2, + SFX_STREET_GANG_2_MUGGED_1, + SFX_STREET_GANG_2_MUGGED_2, + SFX_STREET_GANG_2_MUGGED_3, + SFX_STREET_GANG_2_MUGGING_1, + SFX_STREET_GANG_2_MUGGING_2, + SFX_STREET_GANG_2_MUGGING_3, + SFX_STREET_GANG_2_MUGGING_4, + SFX_STREET_GANG_2_MUGGING_5, + SFX_STREET_GANG_2_SAVED_1, + SFX_STREET_GANG_2_SHOCKED_1, + SFX_STREET_GANG_2_SHOCKED_2, + SFX_STREET_GANG_2_TAXI_1, + + SFX_CUBAN_LORD_GANG_1_BLOCKED_1, + SFX_CUBAN_LORD_GANG_1_BLOCKED_2, + SFX_CUBAN_LORD_GANG_1_BLOCKED_3, + SFX_CUBAN_LORD_GANG_1_BLOCKED_4, + SFX_CUBAN_LORD_GANG_1_BLOCKED_5, + SFX_CUBAN_LORD_GANG_1_BLOCKED_6, + SFX_CUBAN_LORD_GANG_1_BLOCKED_7, + SFX_CUBAN_LORD_GANG_1_BLOCKED_8, + SFX_CUBAN_LORD_GANG_1_BLOCKED_9, + SFX_CUBAN_LORD_GANG_1_BLOCKED_10, + SFX_CUBAN_LORD_GANG_1_BUMP_1, + SFX_CUBAN_LORD_GANG_1_BUMP_2, + SFX_CUBAN_LORD_GANG_1_BUMP_3, + SFX_CUBAN_LORD_GANG_1_BUMP_4, + SFX_CUBAN_LORD_GANG_1_BUMP_5, + SFX_CUBAN_LORD_GANG_1_BUMP_6, + SFX_CUBAN_LORD_GANG_1_BUMP_7, + SFX_CUBAN_LORD_GANG_1_BUMP_8, + SFX_CUBAN_LORD_GANG_1_BUMP_9, + SFX_CUBAN_LORD_GANG_1_BUMP_10, + SFX_CUBAN_LORD_GANG_1_CAR_CRASH_1, + SFX_CUBAN_LORD_GANG_1_CAR_CRASH_2, + SFX_CUBAN_LORD_GANG_1_CAR_CRASH_3, + SFX_CUBAN_LORD_GANG_1_CAR_CRASH_4, + SFX_CUBAN_LORD_GANG_1_CAR_CRASH_5, + SFX_CUBAN_LORD_GANG_1_CAR_CRASH_6, + SFX_CUBAN_LORD_GANG_1_CAR_CRASH_7, + SFX_CUBAN_LORD_GANG_1_CAR_CRASH_8, + SFX_CUBAN_LORD_GANG_1_CAR_CRASH_9, + SFX_CUBAN_LORD_GANG_1_CAR_CRASH_10, + SFX_CUBAN_LORD_GANG_1_CHAT_1, + SFX_CUBAN_LORD_GANG_1_CHAT_2, + SFX_CUBAN_LORD_GANG_1_CHAT_3, + SFX_CUBAN_LORD_GANG_1_CHAT_4, + SFX_CUBAN_LORD_GANG_1_CHAT_5, + SFX_CUBAN_LORD_GANG_1_CHAT_6, + SFX_CUBAN_LORD_GANG_1_CHAT_7, + SFX_CUBAN_LORD_GANG_1_CHAT_8, + SFX_CUBAN_LORD_GANG_1_CHAT_9, + SFX_CUBAN_LORD_GANG_1_CHAT_10, + SFX_CUBAN_LORD_GANG_1_DODGE_1, + SFX_CUBAN_LORD_GANG_1_DODGE_2, + SFX_CUBAN_LORD_GANG_1_DODGE_3, + SFX_CUBAN_LORD_GANG_1_DODGE_4, + SFX_CUBAN_LORD_GANG_1_DODGE_5, + SFX_CUBAN_LORD_GANG_1_DODGE_6, + SFX_CUBAN_LORD_GANG_1_DODGE_7, + SFX_CUBAN_LORD_GANG_1_DODGE_8, + SFX_CUBAN_LORD_GANG_1_DODGE_9, + SFX_CUBAN_LORD_GANG_1_DODGE_10, + SFX_CUBAN_LORD_GANG_1_DODGE_11, + SFX_CUBAN_LORD_GANG_1_DODGE_12, + SFX_CUBAN_LORD_GANG_1_DODGE_13, + SFX_CUBAN_LORD_GANG_1_EYEING_1, + SFX_CUBAN_LORD_GANG_1_EYEING_2, + SFX_CUBAN_LORD_GANG_1_FIGHT_1, + SFX_CUBAN_LORD_GANG_1_FIGHT_2, + SFX_CUBAN_LORD_GANG_1_FIGHT_3, + SFX_CUBAN_LORD_GANG_1_FIGHT_4, + SFX_CUBAN_LORD_GANG_1_FIGHT_5, + SFX_CUBAN_LORD_GANG_1_FIGHT_6, + SFX_CUBAN_LORD_GANG_1_FIGHT_7, + SFX_CUBAN_LORD_GANG_1_FIGHT_8, + SFX_CUBAN_LORD_GANG_1_FIGHT_9, + SFX_CUBAN_LORD_GANG_1_FIGHT_10, + SFX_CUBAN_LORD_GANG_1_GENERIC_CRASH_1, + SFX_CUBAN_LORD_GANG_1_GENERIC_CRASH_2, + SFX_CUBAN_LORD_GANG_1_GENERIC_CRASH_3, + SFX_CUBAN_LORD_GANG_1_GENERIC_CRASH_4, + SFX_CUBAN_LORD_GANG_1_GENERIC_CRASH_5, + SFX_CUBAN_LORD_GANG_1_GENERIC_CRASH_6, + SFX_CUBAN_LORD_GANG_1_GENERIC_CRASH_7, + SFX_CUBAN_LORD_GANG_1_GENERIC_CRASH_8, + SFX_CUBAN_LORD_GANG_1_GUN_COOL_1, + SFX_CUBAN_LORD_GANG_1_GUN_COOL_2, + SFX_CUBAN_LORD_GANG_1_GUN_COOL_3, + SFX_CUBAN_LORD_GANG_1_GUN_COOL_4, + SFX_CUBAN_LORD_GANG_1_GUN_COOL_5, + SFX_CUBAN_LORD_GANG_1_JACKED_1, + SFX_CUBAN_LORD_GANG_1_JACKED_2, + SFX_CUBAN_LORD_GANG_1_JACKED_3, + SFX_CUBAN_LORD_GANG_1_JACKED_4, + SFX_CUBAN_LORD_GANG_1_JACKED_5, + SFX_CUBAN_LORD_GANG_1_JACKED_6, + SFX_CUBAN_LORD_GANG_1_JACKING_1, + SFX_CUBAN_LORD_GANG_1_JACKING_2, + SFX_CUBAN_LORD_GANG_1_JACKING_3, + SFX_CUBAN_LORD_GANG_1_JACKING_4, + SFX_CUBAN_LORD_GANG_1_JACKING_5, + SFX_CUBAN_LORD_GANG_1_LOST_1, + SFX_CUBAN_LORD_GANG_1_LOST_2, + SFX_CUBAN_LORD_GANG_1_MUGGED_1, + SFX_CUBAN_LORD_GANG_1_MUGGED_2, + SFX_CUBAN_LORD_GANG_1_SAVED_1, + SFX_CUBAN_LORD_GANG_1_TAXI_1, + SFX_CUBAN_LORD_GANG_1_TAXI_2, + SFX_CUBAN_LORD_GANG_2_BLOCKED_1, + SFX_CUBAN_LORD_GANG_2_BLOCKED_2, + SFX_CUBAN_LORD_GANG_2_BLOCKED_3, + SFX_CUBAN_LORD_GANG_2_BLOCKED_4, + SFX_CUBAN_LORD_GANG_2_BLOCKED_5, + SFX_CUBAN_LORD_GANG_2_BLOCKED_6, + SFX_CUBAN_LORD_GANG_2_BLOCKED_7, + SFX_CUBAN_LORD_GANG_2_BLOCKED_8, + SFX_CUBAN_LORD_GANG_2_BLOCKED_9, + SFX_CUBAN_LORD_GANG_2_BLOCKED_10, + SFX_CUBAN_LORD_GANG_2_BUMP_1, + SFX_CUBAN_LORD_GANG_2_BUMP_2, + SFX_CUBAN_LORD_GANG_2_BUMP_3, + SFX_CUBAN_LORD_GANG_2_BUMP_4, + SFX_CUBAN_LORD_GANG_2_BUMP_5, + SFX_CUBAN_LORD_GANG_2_BUMP_6, + SFX_CUBAN_LORD_GANG_2_BUMP_7, + SFX_CUBAN_LORD_GANG_2_BUMP_8, + SFX_CUBAN_LORD_GANG_2_BUMP_9, + SFX_CUBAN_LORD_GANG_2_BUMP_10, + SFX_CUBAN_LORD_GANG_2_CAR_CRASH_1, + SFX_CUBAN_LORD_GANG_2_CAR_CRASH_2, + SFX_CUBAN_LORD_GANG_2_CAR_CRASH_3, + SFX_CUBAN_LORD_GANG_2_CAR_CRASH_4, + SFX_CUBAN_LORD_GANG_2_CAR_CRASH_5, + SFX_CUBAN_LORD_GANG_2_CAR_CRASH_6, + SFX_CUBAN_LORD_GANG_2_CAR_CRASH_7, + SFX_CUBAN_LORD_GANG_2_CAR_CRASH_8, + SFX_CUBAN_LORD_GANG_2_CAR_CRASH_9, + SFX_CUBAN_LORD_GANG_2_CAR_CRASH_10, + SFX_CUBAN_LORD_GANG_2_CHAT_1, + SFX_CUBAN_LORD_GANG_2_CHAT_2, + SFX_CUBAN_LORD_GANG_2_CHAT_3, + SFX_CUBAN_LORD_GANG_2_CHAT_4, + SFX_CUBAN_LORD_GANG_2_CHAT_5, + SFX_CUBAN_LORD_GANG_2_CHAT_6, + SFX_CUBAN_LORD_GANG_2_CHAT_7, + SFX_CUBAN_LORD_GANG_2_CHAT_8, + SFX_CUBAN_LORD_GANG_2_CHAT_9, + SFX_CUBAN_LORD_GANG_2_CHAT_10, + SFX_CUBAN_LORD_GANG_2_DODGE_1, + SFX_CUBAN_LORD_GANG_2_DODGE_2, + SFX_CUBAN_LORD_GANG_2_DODGE_3, + SFX_CUBAN_LORD_GANG_2_DODGE_4, + SFX_CUBAN_LORD_GANG_2_DODGE_5, + SFX_CUBAN_LORD_GANG_2_DODGE_6, + SFX_CUBAN_LORD_GANG_2_DODGE_7, + SFX_CUBAN_LORD_GANG_2_DODGE_8, + SFX_CUBAN_LORD_GANG_2_DODGE_9, + SFX_CUBAN_LORD_GANG_2_DODGE_10, + SFX_CUBAN_LORD_GANG_2_DODGE_11, + SFX_CUBAN_LORD_GANG_2_DODGE_12, + SFX_CUBAN_LORD_GANG_2_DODGE_13, + SFX_CUBAN_LORD_GANG_2_EYEING_1, + SFX_CUBAN_LORD_GANG_2_EYEING_2, + SFX_CUBAN_LORD_GANG_2_FIGHT_1, + SFX_CUBAN_LORD_GANG_2_FIGHT_2, + SFX_CUBAN_LORD_GANG_2_FIGHT_3, + SFX_CUBAN_LORD_GANG_2_FIGHT_4, + SFX_CUBAN_LORD_GANG_2_FIGHT_5, + SFX_CUBAN_LORD_GANG_2_FIGHT_6, + SFX_CUBAN_LORD_GANG_2_FIGHT_7, + SFX_CUBAN_LORD_GANG_2_FIGHT_8, + SFX_CUBAN_LORD_GANG_2_FIGHT_9, + SFX_CUBAN_LORD_GANG_2_FIGHT_10, + SFX_CUBAN_LORD_GANG_2_GENERIC_CRASH_1, + SFX_CUBAN_LORD_GANG_2_GENERIC_CRASH_2, + SFX_CUBAN_LORD_GANG_2_GENERIC_CRASH_3, + SFX_CUBAN_LORD_GANG_2_GENERIC_CRASH_4, + SFX_CUBAN_LORD_GANG_2_GENERIC_CRASH_5, + SFX_CUBAN_LORD_GANG_2_GENERIC_CRASH_6, + SFX_CUBAN_LORD_GANG_2_GENERIC_CRASH_7, + SFX_CUBAN_LORD_GANG_2_GENERIC_CRASH_8, + SFX_CUBAN_LORD_GANG_2_GUN_COOL_1, + SFX_CUBAN_LORD_GANG_2_GUN_COOL_2, + SFX_CUBAN_LORD_GANG_2_GUN_COOL_3, + SFX_CUBAN_LORD_GANG_2_GUN_COOL_4, + SFX_CUBAN_LORD_GANG_2_GUN_COOL_5, + SFX_CUBAN_LORD_GANG_2_JACKED_1, + SFX_CUBAN_LORD_GANG_2_JACKED_2, + SFX_CUBAN_LORD_GANG_2_JACKED_3, + SFX_CUBAN_LORD_GANG_2_JACKED_4, + SFX_CUBAN_LORD_GANG_2_JACKED_5, + SFX_CUBAN_LORD_GANG_2_JACKED_6, + SFX_CUBAN_LORD_GANG_2_JACKING_1, + SFX_CUBAN_LORD_GANG_2_JACKING_2, + SFX_CUBAN_LORD_GANG_2_JACKING_3, + SFX_CUBAN_LORD_GANG_2_JACKING_4, + SFX_CUBAN_LORD_GANG_2_JACKING_5, + SFX_CUBAN_LORD_GANG_2_LOST_1, + SFX_CUBAN_LORD_GANG_2_LOST_2, + SFX_CUBAN_LORD_GANG_2_MUGGED_1, + SFX_CUBAN_LORD_GANG_2_MUGGED_2, + SFX_CUBAN_LORD_GANG_2_SAVED_1, + SFX_CUBAN_LORD_GANG_2_TAXI_1, + SFX_CUBAN_LORD_GANG_2_TAXI_2, + SFX_CUBAN_LORD_GANG_3_BLOCKED_1, + SFX_CUBAN_LORD_GANG_3_BLOCKED_2, + SFX_CUBAN_LORD_GANG_3_BLOCKED_3, + SFX_CUBAN_LORD_GANG_3_BLOCKED_4, + SFX_CUBAN_LORD_GANG_3_BLOCKED_5, + SFX_CUBAN_LORD_GANG_3_BLOCKED_6, + SFX_CUBAN_LORD_GANG_3_BLOCKED_7, + SFX_CUBAN_LORD_GANG_3_BLOCKED_8, + SFX_CUBAN_LORD_GANG_3_BLOCKED_9, + SFX_CUBAN_LORD_GANG_3_BLOCKED_10, + SFX_CUBAN_LORD_GANG_3_BUMP_1, + SFX_CUBAN_LORD_GANG_3_BUMP_2, + SFX_CUBAN_LORD_GANG_3_BUMP_3, + SFX_CUBAN_LORD_GANG_3_BUMP_4, + SFX_CUBAN_LORD_GANG_3_BUMP_5, + SFX_CUBAN_LORD_GANG_3_BUMP_6, + SFX_CUBAN_LORD_GANG_3_BUMP_7, + SFX_CUBAN_LORD_GANG_3_BUMP_8, + SFX_CUBAN_LORD_GANG_3_BUMP_9, + SFX_CUBAN_LORD_GANG_3_BUMP_10, + SFX_CUBAN_LORD_GANG_3_CAR_CRASH_1, + SFX_CUBAN_LORD_GANG_3_CAR_CRASH_2, + SFX_CUBAN_LORD_GANG_3_CAR_CRASH_3, + SFX_CUBAN_LORD_GANG_3_CAR_CRASH_4, + SFX_CUBAN_LORD_GANG_3_CAR_CRASH_5, + SFX_CUBAN_LORD_GANG_3_CAR_CRASH_6, + SFX_CUBAN_LORD_GANG_3_CAR_CRASH_7, + SFX_CUBAN_LORD_GANG_3_CAR_CRASH_8, + SFX_CUBAN_LORD_GANG_3_CAR_CRASH_9, + SFX_CUBAN_LORD_GANG_3_CAR_CRASH_10, + SFX_CUBAN_LORD_GANG_3_CHAT_1, + SFX_CUBAN_LORD_GANG_3_CHAT_2, + SFX_CUBAN_LORD_GANG_3_CHAT_3, + SFX_CUBAN_LORD_GANG_3_CHAT_4, + SFX_CUBAN_LORD_GANG_3_CHAT_5, + SFX_CUBAN_LORD_GANG_3_CHAT_6, + SFX_CUBAN_LORD_GANG_3_CHAT_7, + SFX_CUBAN_LORD_GANG_3_CHAT_8, + SFX_CUBAN_LORD_GANG_3_CHAT_9, + SFX_CUBAN_LORD_GANG_3_CHAT_10, + SFX_CUBAN_LORD_GANG_3_DODGE_1, + SFX_CUBAN_LORD_GANG_3_DODGE_2, + SFX_CUBAN_LORD_GANG_3_DODGE_3, + SFX_CUBAN_LORD_GANG_3_DODGE_4, + SFX_CUBAN_LORD_GANG_3_DODGE_5, + SFX_CUBAN_LORD_GANG_3_DODGE_6, + SFX_CUBAN_LORD_GANG_3_DODGE_7, + SFX_CUBAN_LORD_GANG_3_DODGE_8, + SFX_CUBAN_LORD_GANG_3_DODGE_9, + SFX_CUBAN_LORD_GANG_3_DODGE_10, + SFX_CUBAN_LORD_GANG_3_DODGE_11, + SFX_CUBAN_LORD_GANG_3_DODGE_12, + SFX_CUBAN_LORD_GANG_3_DODGE_13, + SFX_CUBAN_LORD_GANG_3_EYEING_1, + SFX_CUBAN_LORD_GANG_3_EYEING_2, + SFX_CUBAN_LORD_GANG_3_FIGHT_1, + SFX_CUBAN_LORD_GANG_3_FIGHT_2, + SFX_CUBAN_LORD_GANG_3_FIGHT_3, + SFX_CUBAN_LORD_GANG_3_FIGHT_4, + SFX_CUBAN_LORD_GANG_3_FIGHT_5, + SFX_CUBAN_LORD_GANG_3_FIGHT_6, + SFX_CUBAN_LORD_GANG_3_FIGHT_7, + SFX_CUBAN_LORD_GANG_3_FIGHT_8, + SFX_CUBAN_LORD_GANG_3_FIGHT_9, + SFX_CUBAN_LORD_GANG_3_FIGHT_10, + SFX_CUBAN_LORD_GANG_3_GENERIC_CRASH_1, + SFX_CUBAN_LORD_GANG_3_GENERIC_CRASH_2, + SFX_CUBAN_LORD_GANG_3_GENERIC_CRASH_3, + SFX_CUBAN_LORD_GANG_3_GENERIC_CRASH_4, + SFX_CUBAN_LORD_GANG_3_GENERIC_CRASH_5, + SFX_CUBAN_LORD_GANG_3_GENERIC_CRASH_6, + SFX_CUBAN_LORD_GANG_3_GENERIC_CRASH_7, + SFX_CUBAN_LORD_GANG_3_GENERIC_CRASH_8, + SFX_CUBAN_LORD_GANG_3_GUN_COOL_1, + SFX_CUBAN_LORD_GANG_3_GUN_COOL_2, + SFX_CUBAN_LORD_GANG_3_GUN_COOL_3, + SFX_CUBAN_LORD_GANG_3_GUN_COOL_4, + SFX_CUBAN_LORD_GANG_3_GUN_COOL_5, + SFX_CUBAN_LORD_GANG_3_JACKED_1, + SFX_CUBAN_LORD_GANG_3_JACKED_2, + SFX_CUBAN_LORD_GANG_3_JACKED_3, + SFX_CUBAN_LORD_GANG_3_JACKED_4, + SFX_CUBAN_LORD_GANG_3_JACKED_5, + SFX_CUBAN_LORD_GANG_3_JACKED_6, + SFX_CUBAN_LORD_GANG_3_JACKING_1, + SFX_CUBAN_LORD_GANG_3_JACKING_2, + SFX_CUBAN_LORD_GANG_3_JACKING_3, + SFX_CUBAN_LORD_GANG_3_JACKING_4, + SFX_CUBAN_LORD_GANG_3_JACKING_5, + SFX_CUBAN_LORD_GANG_3_LOST_1, + SFX_CUBAN_LORD_GANG_3_LOST_2, + SFX_CUBAN_LORD_GANG_3_MUGGED_1, + SFX_CUBAN_LORD_GANG_3_MUGGED_2, + SFX_CUBAN_LORD_GANG_3_SAVED_1, + SFX_CUBAN_LORD_GANG_3_TAXI_1, + SFX_CUBAN_LORD_GANG_3_TAXI_2, + + SFX_PLAYER_GANG_1_BLOCKED_1, + SFX_PLAYER_GANG_1_BLOCKED_2, + SFX_PLAYER_GANG_1_BLOCKED_3, + SFX_PLAYER_GANG_1_BLOCKED_4, + SFX_PLAYER_GANG_1_BLOCKED_5, + SFX_PLAYER_GANG_1_BLOCKED_6, + SFX_PLAYER_GANG_1_BLOCKED_7, + SFX_PLAYER_GANG_1_BLOCKED_8, + SFX_PLAYER_GANG_1_BLOCKED_9, + SFX_PLAYER_GANG_1_BLOCKED_10, + SFX_PLAYER_GANG_1_BUMP_1, + SFX_PLAYER_GANG_1_BUMP_2, + SFX_PLAYER_GANG_1_BUMP_3, + SFX_PLAYER_GANG_1_BUMP_4, + SFX_PLAYER_GANG_1_BUMP_5, + SFX_PLAYER_GANG_1_CAR_CRASH_1, + SFX_PLAYER_GANG_1_CAR_CRASH_2, + SFX_PLAYER_GANG_1_CAR_CRASH_3, + SFX_PLAYER_GANG_1_CAR_CRASH_4, + SFX_PLAYER_GANG_1_CAR_CRASH_5, + SFX_PLAYER_GANG_1_CHAT_1, + SFX_PLAYER_GANG_1_CHAT_2, + SFX_PLAYER_GANG_1_CHAT_3, + SFX_PLAYER_GANG_1_CHAT_4, + SFX_PLAYER_GANG_1_CHAT_5, + SFX_PLAYER_GANG_1_CHAT_6, + SFX_PLAYER_GANG_1_CHAT_7, + SFX_PLAYER_GANG_1_CHAT_8, + SFX_PLAYER_GANG_1_DODGE_1, + SFX_PLAYER_GANG_1_DODGE_2, + SFX_PLAYER_GANG_1_DODGE_3, + SFX_PLAYER_GANG_1_DODGE_4, + SFX_PLAYER_GANG_1_DODGE_5, + SFX_PLAYER_GANG_1_DODGE_6, + SFX_PLAYER_GANG_1_DODGE_7, + SFX_PLAYER_GANG_1_EYEING_1, + SFX_PLAYER_GANG_1_EYEING_2, + SFX_PLAYER_GANG_1_FIGHT_1, + SFX_PLAYER_GANG_1_FIGHT_2, + SFX_PLAYER_GANG_1_FIGHT_3, + SFX_PLAYER_GANG_1_FIGHT_4, + SFX_PLAYER_GANG_1_FIGHT_5, + SFX_PLAYER_GANG_1_GENERIC_CRASH_1, + SFX_PLAYER_GANG_1_GENERIC_CRASH_2, + SFX_PLAYER_GANG_1_GENERIC_CRASH_3, + SFX_PLAYER_GANG_1_GENERIC_CRASH_4, + SFX_PLAYER_GANG_1_GENERIC_CRASH_5, + SFX_PLAYER_GANG_1_GUN_COOL_1, + SFX_PLAYER_GANG_1_GUN_COOL_2, + SFX_PLAYER_GANG_1_GUN_COOL_3, + SFX_PLAYER_GANG_1_GUN_COOL_4, + SFX_PLAYER_GANG_1_JACKED_1, + SFX_PLAYER_GANG_1_JACKED_2, + SFX_PLAYER_GANG_1_JACKED_3, + SFX_PLAYER_GANG_1_JACKED_4, + SFX_PLAYER_GANG_1_JACKED_5, + SFX_PLAYER_GANG_1_JACKING_1, + SFX_PLAYER_GANG_1_JACKING_2, + SFX_PLAYER_GANG_1_JACKING_3, + SFX_PLAYER_GANG_1_JACKING_4, + SFX_PLAYER_GANG_1_JACKING_5, + SFX_PLAYER_GANG_1_LOST_1, + SFX_PLAYER_GANG_1_LOST_2, + SFX_PLAYER_GANG_1_MUGGED_1, + SFX_PLAYER_GANG_1_MUGGED_2, + SFX_PLAYER_GANG_1_SAVED_1, + SFX_PLAYER_GANG_1_SHOCKED_1, + SFX_PLAYER_GANG_1_SHOCKED_2, + SFX_PLAYER_GANG_1_TAXI_1, + SFX_PLAYER_GANG_1_TAXI_2, + SFX_PLAYER_GANG_2_BLOCKED_1, + SFX_PLAYER_GANG_2_BLOCKED_2, + SFX_PLAYER_GANG_2_BLOCKED_3, + SFX_PLAYER_GANG_2_BLOCKED_4, + SFX_PLAYER_GANG_2_BLOCKED_5, + SFX_PLAYER_GANG_2_BLOCKED_6, + SFX_PLAYER_GANG_2_BLOCKED_7, + SFX_PLAYER_GANG_2_BLOCKED_8, + SFX_PLAYER_GANG_2_BLOCKED_9, + SFX_PLAYER_GANG_2_BLOCKED_10, + SFX_PLAYER_GANG_2_BUMP_1, + SFX_PLAYER_GANG_2_BUMP_2, + SFX_PLAYER_GANG_2_BUMP_3, + SFX_PLAYER_GANG_2_BUMP_4, + SFX_PLAYER_GANG_2_BUMP_5, + SFX_PLAYER_GANG_2_CAR_CRASH_1, + SFX_PLAYER_GANG_2_CAR_CRASH_2, + SFX_PLAYER_GANG_2_CAR_CRASH_3, + SFX_PLAYER_GANG_2_CAR_CRASH_4, + SFX_PLAYER_GANG_2_CAR_CRASH_5, + SFX_PLAYER_GANG_2_CHAT_1, + SFX_PLAYER_GANG_2_CHAT_2, + SFX_PLAYER_GANG_2_CHAT_3, + SFX_PLAYER_GANG_2_CHAT_4, + SFX_PLAYER_GANG_2_CHAT_5, + SFX_PLAYER_GANG_2_CHAT_6, + SFX_PLAYER_GANG_2_CHAT_7, + SFX_PLAYER_GANG_2_CHAT_8, + SFX_PLAYER_GANG_2_DODGE_1, + SFX_PLAYER_GANG_2_DODGE_2, + SFX_PLAYER_GANG_2_DODGE_3, + SFX_PLAYER_GANG_2_DODGE_4, + SFX_PLAYER_GANG_2_DODGE_5, + SFX_PLAYER_GANG_2_DODGE_6, + SFX_PLAYER_GANG_2_DODGE_7, + SFX_PLAYER_GANG_2_EYEING_1, + SFX_PLAYER_GANG_2_EYEING_2, + SFX_PLAYER_GANG_2_FIGHT_1, + SFX_PLAYER_GANG_2_FIGHT_2, + SFX_PLAYER_GANG_2_FIGHT_3, + SFX_PLAYER_GANG_2_FIGHT_4, + SFX_PLAYER_GANG_2_FIGHT_5, + SFX_PLAYER_GANG_2_GENERIC_CRASH_1, + SFX_PLAYER_GANG_2_GENERIC_CRASH_2, + SFX_PLAYER_GANG_2_GENERIC_CRASH_3, + SFX_PLAYER_GANG_2_GENERIC_CRASH_4, + SFX_PLAYER_GANG_2_GENERIC_CRASH_5, + SFX_PLAYER_GANG_2_GUN_COOL_1, + SFX_PLAYER_GANG_2_GUN_COOL_2, + SFX_PLAYER_GANG_2_GUN_COOL_3, + SFX_PLAYER_GANG_2_GUN_COOL_4, + SFX_PLAYER_GANG_2_JACKED_1, + SFX_PLAYER_GANG_2_JACKED_2, + SFX_PLAYER_GANG_2_JACKED_3, + SFX_PLAYER_GANG_2_JACKED_4, + SFX_PLAYER_GANG_2_JACKED_5, + SFX_PLAYER_GANG_2_JACKING_1, + SFX_PLAYER_GANG_2_JACKING_2, + SFX_PLAYER_GANG_2_JACKING_3, + SFX_PLAYER_GANG_2_JACKING_4, + SFX_PLAYER_GANG_2_JACKING_5, + SFX_PLAYER_GANG_2_LOST_1, + SFX_PLAYER_GANG_2_LOST_2, + SFX_PLAYER_GANG_2_MUGGED_1, + SFX_PLAYER_GANG_2_MUGGED_2, + SFX_PLAYER_GANG_2_SAVED_1, + SFX_PLAYER_GANG_2_SHOCKED_1, + SFX_PLAYER_GANG_2_SHOCKED_2, + SFX_PLAYER_GANG_2_TAXI_1, + SFX_PLAYER_GANG_2_TAXI_2, + SFX_PLAYER_GANG_3_BLOCKED_1, + SFX_PLAYER_GANG_3_BLOCKED_2, + SFX_PLAYER_GANG_3_BLOCKED_3, + SFX_PLAYER_GANG_3_BLOCKED_4, + SFX_PLAYER_GANG_3_BLOCKED_5, + SFX_PLAYER_GANG_3_BLOCKED_6, + SFX_PLAYER_GANG_3_BLOCKED_7, + SFX_PLAYER_GANG_3_BLOCKED_8, + SFX_PLAYER_GANG_3_BLOCKED_9, + SFX_PLAYER_GANG_3_BLOCKED_10, + SFX_PLAYER_GANG_3_BUMP_1, + SFX_PLAYER_GANG_3_BUMP_2, + SFX_PLAYER_GANG_3_BUMP_3, + SFX_PLAYER_GANG_3_BUMP_4, + SFX_PLAYER_GANG_3_BUMP_5, + SFX_PLAYER_GANG_3_CAR_CRASH_1, + SFX_PLAYER_GANG_3_CAR_CRASH_2, + SFX_PLAYER_GANG_3_CAR_CRASH_3, + SFX_PLAYER_GANG_3_CAR_CRASH_4, + SFX_PLAYER_GANG_3_CAR_CRASH_5, + SFX_PLAYER_GANG_3_CHAT_1, + SFX_PLAYER_GANG_3_CHAT_2, + SFX_PLAYER_GANG_3_CHAT_3, + SFX_PLAYER_GANG_3_CHAT_4, + SFX_PLAYER_GANG_3_CHAT_5, + SFX_PLAYER_GANG_3_CHAT_6, + SFX_PLAYER_GANG_3_CHAT_7, + SFX_PLAYER_GANG_3_CHAT_8, + SFX_PLAYER_GANG_3_DODGE_1, + SFX_PLAYER_GANG_3_DODGE_2, + SFX_PLAYER_GANG_3_DODGE_3, + SFX_PLAYER_GANG_3_DODGE_4, + SFX_PLAYER_GANG_3_DODGE_5, + SFX_PLAYER_GANG_3_DODGE_6, + SFX_PLAYER_GANG_3_DODGE_7, + SFX_PLAYER_GANG_3_EYEING_1, + SFX_PLAYER_GANG_3_EYEING_2, + SFX_PLAYER_GANG_3_FIGHT_1, + SFX_PLAYER_GANG_3_FIGHT_2, + SFX_PLAYER_GANG_3_FIGHT_3, + SFX_PLAYER_GANG_3_FIGHT_4, + SFX_PLAYER_GANG_3_FIGHT_5, + SFX_PLAYER_GANG_3_GENERIC_CRASH_1, + SFX_PLAYER_GANG_3_GENERIC_CRASH_2, + SFX_PLAYER_GANG_3_GENERIC_CRASH_3, + SFX_PLAYER_GANG_3_GENERIC_CRASH_4, + SFX_PLAYER_GANG_3_GENERIC_CRASH_5, + SFX_PLAYER_GANG_3_GUN_COOL_1, + SFX_PLAYER_GANG_3_GUN_COOL_2, + SFX_PLAYER_GANG_3_GUN_COOL_3, + SFX_PLAYER_GANG_3_GUN_COOL_4, + SFX_PLAYER_GANG_3_JACKED_1, + SFX_PLAYER_GANG_3_JACKED_2, + SFX_PLAYER_GANG_3_JACKED_3, + SFX_PLAYER_GANG_3_JACKED_4, + SFX_PLAYER_GANG_3_JACKED_5, + SFX_PLAYER_GANG_3_JACKING_1, + SFX_PLAYER_GANG_3_JACKING_2, + SFX_PLAYER_GANG_3_JACKING_3, + SFX_PLAYER_GANG_3_JACKING_4, + SFX_PLAYER_GANG_3_JACKING_5, + SFX_PLAYER_GANG_3_LOST_1, + SFX_PLAYER_GANG_3_LOST_2, + SFX_PLAYER_GANG_3_MUGGED_1, + SFX_PLAYER_GANG_3_MUGGED_2, + SFX_PLAYER_GANG_3_SAVED_1, + SFX_PLAYER_GANG_3_SHOCKED_1, + SFX_PLAYER_GANG_3_SHOCKED_2, + SFX_PLAYER_GANG_3_TAXI_1, + SFX_PLAYER_GANG_3_TAXI_2, + + SFX_GUARD_DUTY_1_BUMP_1, + SFX_GUARD_DUTY_1_BUMP_2, + SFX_GUARD_DUTY_1_BUMP_3, + SFX_GUARD_DUTY_1_BUMP_4, + SFX_GUARD_DUTY_1_BUMP_5, + SFX_GUARD_DUTY_1_BUMP_6, + SFX_GUARD_DUTY_1_BUMP_7, + SFX_GUARD_DUTY_1_BUMP_8, + SFX_GUARD_DUTY_1_BUMP_9, + SFX_GUARD_DUTY_1_BUMP_10, + SFX_GUARD_DUTY_1_CHAT_1, + SFX_GUARD_DUTY_1_CHAT_2, + SFX_GUARD_DUTY_1_CHAT_3, + SFX_GUARD_DUTY_1_CHAT_4, + SFX_GUARD_DUTY_1_CHAT_5, + SFX_GUARD_DUTY_1_CHAT_6, + SFX_GUARD_DUTY_1_CHAT_7, + SFX_GUARD_DUTY_1_CHAT_8, + SFX_GUARD_DUTY_1_CHAT_9, + SFX_GUARD_DUTY_1_CHAT_10, + SFX_GUARD_DUTY_1_DODGE_1, + SFX_GUARD_DUTY_1_DODGE_2, + SFX_GUARD_DUTY_1_DODGE_3, + SFX_GUARD_DUTY_1_DODGE_4, + SFX_GUARD_DUTY_1_DODGE_5, + SFX_GUARD_DUTY_1_DODGE_6, + SFX_GUARD_DUTY_1_DODGE_7, + SFX_GUARD_DUTY_1_DODGE_8, + SFX_GUARD_DUTY_1_DODGE_9, + SFX_GUARD_DUTY_1_EYEING_1, + SFX_GUARD_DUTY_1_EYEING_2, + SFX_GUARD_DUTY_1_FIGHT_1, + SFX_GUARD_DUTY_1_FIGHT_2, + SFX_GUARD_DUTY_1_FIGHT_3, + SFX_GUARD_DUTY_1_FIGHT_4, + SFX_GUARD_DUTY_1_FIGHT_5, + SFX_GUARD_DUTY_1_FIGHT_6, + SFX_GUARD_DUTY_1_FIGHT_7, + SFX_GUARD_DUTY_1_GUN_COOL_1, + SFX_GUARD_DUTY_1_GUN_COOL_2, + SFX_GUARD_DUTY_1_GUN_COOL_3, + SFX_GUARD_DUTY_1_GUN_COOL_4, + SFX_GUARD_DUTY_1_GUN_COOL_5, + SFX_GUARD_DUTY_1_GUN_COOL_6, + SFX_GUARD_DUTY_1_LOST_1, + SFX_GUARD_DUTY_1_LOST_2, + SFX_GUARD_DUTY_1_SAVED_1, + SFX_GUARD_DUTY_1_SAVED_2, + SFX_GUARD_DUTY_1_SHOCKED_1, + SFX_GUARD_DUTY_1_SHOCKED_2, + + SFX_GUARD_DUTY_2_BUMP_1, + SFX_GUARD_DUTY_2_BUMP_2, + SFX_GUARD_DUTY_2_BUMP_3, + SFX_GUARD_DUTY_2_BUMP_4, + SFX_GUARD_DUTY_2_BUMP_5, + SFX_GUARD_DUTY_2_BUMP_6, + SFX_GUARD_DUTY_2_BUMP_7, + SFX_GUARD_DUTY_2_BUMP_8, + SFX_GUARD_DUTY_2_BUMP_9, + SFX_GUARD_DUTY_2_BUMP_10, + SFX_GUARD_DUTY_2_CHAT_1, + SFX_GUARD_DUTY_2_CHAT_2, + SFX_GUARD_DUTY_2_CHAT_3, + SFX_GUARD_DUTY_2_CHAT_4, + SFX_GUARD_DUTY_2_CHAT_5, + SFX_GUARD_DUTY_2_CHAT_6, + SFX_GUARD_DUTY_2_CHAT_7, + SFX_GUARD_DUTY_2_CHAT_8, + SFX_GUARD_DUTY_2_CHAT_9, + SFX_GUARD_DUTY_2_CHAT_10, + SFX_GUARD_DUTY_2_DODGE_1, + SFX_GUARD_DUTY_2_DODGE_2, + SFX_GUARD_DUTY_2_DODGE_3, + SFX_GUARD_DUTY_2_DODGE_4, + SFX_GUARD_DUTY_2_DODGE_5, + SFX_GUARD_DUTY_2_DODGE_6, + SFX_GUARD_DUTY_2_DODGE_7, + SFX_GUARD_DUTY_2_DODGE_8, + SFX_GUARD_DUTY_2_DODGE_9, + SFX_GUARD_DUTY_2_EYEING_1, + SFX_GUARD_DUTY_2_EYEING_2, + SFX_GUARD_DUTY_2_FIGHT_1, + SFX_GUARD_DUTY_2_FIGHT_2, + SFX_GUARD_DUTY_2_FIGHT_3, + SFX_GUARD_DUTY_2_FIGHT_4, + SFX_GUARD_DUTY_2_FIGHT_5, + SFX_GUARD_DUTY_2_FIGHT_6, + SFX_GUARD_DUTY_2_FIGHT_7, + SFX_GUARD_DUTY_2_GUN_COOL_1, + SFX_GUARD_DUTY_2_GUN_COOL_2, + SFX_GUARD_DUTY_2_GUN_COOL_3, + SFX_GUARD_DUTY_2_GUN_COOL_4, + SFX_GUARD_DUTY_2_GUN_COOL_5, + SFX_GUARD_DUTY_2_GUN_COOL_6, + SFX_GUARD_DUTY_2_LOST_1, + SFX_GUARD_DUTY_2_LOST_2, + SFX_GUARD_DUTY_2_SAVED_1, + SFX_GUARD_DUTY_2_SAVED_2, + SFX_GUARD_DUTY_2_SHOCKED_1, + SFX_GUARD_DUTY_2_SHOCKED_2, + + SFX_GUARD_DUTY_3_BUMP_1, + SFX_GUARD_DUTY_3_BUMP_2, + SFX_GUARD_DUTY_3_BUMP_3, + SFX_GUARD_DUTY_3_BUMP_4, + SFX_GUARD_DUTY_3_BUMP_5, + SFX_GUARD_DUTY_3_BUMP_6, + SFX_GUARD_DUTY_3_BUMP_7, + SFX_GUARD_DUTY_3_BUMP_8, + SFX_GUARD_DUTY_3_BUMP_9, + SFX_GUARD_DUTY_3_BUMP_10, + SFX_GUARD_DUTY_3_CHAT_1, + SFX_GUARD_DUTY_3_CHAT_2, + SFX_GUARD_DUTY_3_CHAT_3, + SFX_GUARD_DUTY_3_CHAT_4, + SFX_GUARD_DUTY_3_CHAT_5, + SFX_GUARD_DUTY_3_CHAT_6, + SFX_GUARD_DUTY_3_CHAT_7, + SFX_GUARD_DUTY_3_CHAT_8, + SFX_GUARD_DUTY_3_CHAT_9, + SFX_GUARD_DUTY_3_CHAT_10, + SFX_GUARD_DUTY_3_DODGE_1, + SFX_GUARD_DUTY_3_DODGE_2, + SFX_GUARD_DUTY_3_DODGE_3, + SFX_GUARD_DUTY_3_DODGE_4, + SFX_GUARD_DUTY_3_DODGE_5, + SFX_GUARD_DUTY_3_DODGE_6, + SFX_GUARD_DUTY_3_DODGE_7, + SFX_GUARD_DUTY_3_DODGE_8, + SFX_GUARD_DUTY_3_DODGE_9, + SFX_GUARD_DUTY_3_EYEING_1, + SFX_GUARD_DUTY_3_EYEING_2, + SFX_GUARD_DUTY_3_FIGHT_1, + SFX_GUARD_DUTY_3_FIGHT_2, + SFX_GUARD_DUTY_3_FIGHT_3, + SFX_GUARD_DUTY_3_FIGHT_4, + SFX_GUARD_DUTY_3_FIGHT_5, + SFX_GUARD_DUTY_3_FIGHT_6, + SFX_GUARD_DUTY_3_FIGHT_7, + SFX_GUARD_DUTY_3_GUN_COOL_1, + SFX_GUARD_DUTY_3_GUN_COOL_2, + SFX_GUARD_DUTY_3_GUN_COOL_3, + SFX_GUARD_DUTY_3_GUN_COOL_4, + SFX_GUARD_DUTY_3_GUN_COOL_5, + SFX_GUARD_DUTY_3_GUN_COOL_6, + SFX_GUARD_DUTY_3_LOST_1, + SFX_GUARD_DUTY_3_LOST_2, + SFX_GUARD_DUTY_3_SAVED_1, + SFX_GUARD_DUTY_3_SAVED_2, + SFX_GUARD_DUTY_3_SHOCKED_1, + SFX_GUARD_DUTY_3_SHOCKED_2, + + SFX_VICE_VOICE_1_ARREST_1, + SFX_VICE_VOICE_1_ARREST_2, + SFX_VICE_VOICE_1_ARREST_3, + SFX_VICE_VOICE_1_MIAMIVICE_EXITING_CAR_1, + + SFX_VICE_VOICE_2_ARREST_1, + SFX_VICE_VOICE_2_ARREST_2, + SFX_VICE_VOICE_2_ARREST_3, + SFX_VICE_VOICE_2_MIAMIVICE_EXITING_CAR_1, + + SFX_VICE_VOICE_3_ARREST_1, + SFX_VICE_VOICE_3_ARREST_2, + SFX_VICE_VOICE_3_ARREST_3, + SFX_VICE_VOICE_3_MIAMIVICE_EXITING_CAR_1, + + SFX_VICE_VOICE_4_ARREST_1, + SFX_VICE_VOICE_4_ARREST_2, + SFX_VICE_VOICE_4_ARREST_3, + SFX_VICE_VOICE_4_MIAMIVICE_EXITING_CAR_1, + + SFX_VICE_VOICE_5_ARREST_1, + SFX_VICE_VOICE_5_ARREST_2, + SFX_VICE_VOICE_5_ARREST_3, + SFX_VICE_VOICE_5_MIAMIVICE_EXITING_CAR_1, + + SFX_VICE_VOICE_6_ARREST_1, + SFX_VICE_VOICE_6_ARREST_2, + SFX_VICE_VOICE_6_ARREST_3, + SFX_VICE_VOICE_6_MIAMIVICE_EXITING_CAR_1, + + SFX_DEFAULT_VOICE_BLOCKED_1, + SFX_DEFAULT_VOICE_BLOCKED_2, + SFX_DEFAULT_VOICE_BLOCKED_3, + SFX_DEFAULT_VOICE_BLOCKED_4, + SFX_DEFAULT_VOICE_BLOCKED_5, + SFX_DEFAULT_VOICE_BLOCKED_6, + SFX_DEFAULT_VOICE_BLOCKED_7, + SFX_DEFAULT_VOICE_BLOCKED_8, + SFX_DEFAULT_VOICE_BLOCKED_9, + SFX_DEFAULT_VOICE_BLOCKED_10, + SFX_DEFAULT_VOICE_BLOCKED_11, + SFX_DEFAULT_VOICE_BLOCKED_12, + SFX_DEFAULT_VOICE_BLOCKED_13, + SFX_DEFAULT_VOICE_BLOCKED_14, + SFX_DEFAULT_VOICE_BLOCKED_15, + SFX_DEFAULT_VOICE_BLOCKED_16, + SFX_DEFAULT_VOICE_BUMP_1, + SFX_DEFAULT_VOICE_BUMP_2, + SFX_DEFAULT_VOICE_BUMP_3, + SFX_DEFAULT_VOICE_BUMP_4, + SFX_DEFAULT_VOICE_BUMP_5, + SFX_DEFAULT_VOICE_BUMP_6, + SFX_DEFAULT_VOICE_BUMP_7, + SFX_DEFAULT_VOICE_BUMP_8, + SFX_DEFAULT_VOICE_BUMP_9, + SFX_DEFAULT_VOICE_BUMP_10, + SFX_DEFAULT_VOICE_BUMP_11, + SFX_DEFAULT_VOICE_BUMP_12, + SFX_DEFAULT_VOICE_BUMP_13, + SFX_DEFAULT_VOICE_BUMP_14, + SFX_DEFAULT_VOICE_BUMP_15, + SFX_DEFAULT_VOICE_BUMP_16, + SFX_DEFAULT_VOICE_BUMP_17, + SFX_DEFAULT_VOICE_BUMP_18, + SFX_DEFAULT_VOICE_BUMP_19, + SFX_DEFAULT_VOICE_BUMP_20, + SFX_DEFAULT_VOICE_BUMP_21, + SFX_DEFAULT_VOICE_BUMP_22, + SFX_DEFAULT_VOICE_BUMP_23, + SFX_DEFAULT_VOICE_BUMP_24, + SFX_DEFAULT_VOICE_BUMP_25, + SFX_DEFAULT_VOICE_CAR_CRASH_1, + SFX_DEFAULT_VOICE_CAR_CRASH_2, + SFX_DEFAULT_VOICE_CAR_CRASH_3, + SFX_DEFAULT_VOICE_CAR_CRASH_4, + SFX_DEFAULT_VOICE_CAR_CRASH_5, + SFX_DEFAULT_VOICE_CAR_CRASH_6, + SFX_DEFAULT_VOICE_CAR_CRASH_7, + SFX_DEFAULT_VOICE_CAR_CRASH_8, + SFX_DEFAULT_VOICE_CAR_CRASH_9, + SFX_DEFAULT_VOICE_CAR_CRASH_10, + SFX_DEFAULT_VOICE_CAR_CRASH_11, + SFX_DEFAULT_VOICE_CAR_CRASH_12, + SFX_DEFAULT_VOICE_CAR_CRASH_13, + SFX_DEFAULT_VOICE_CAR_CRASH_14, + SFX_DEFAULT_VOICE_CAR_CRASH_15, + SFX_DEFAULT_VOICE_CHAT_1, + SFX_DEFAULT_VOICE_CHAT_2, + SFX_DEFAULT_VOICE_CHAT_3, + SFX_DEFAULT_VOICE_CHAT_4, + SFX_DEFAULT_VOICE_CHAT_5, + SFX_DEFAULT_VOICE_CHAT_6, + SFX_DEFAULT_VOICE_CHAT_7, + SFX_DEFAULT_VOICE_CHAT_8, + SFX_DEFAULT_VOICE_CHAT_9, + SFX_DEFAULT_VOICE_CHAT_10, + SFX_DEFAULT_VOICE_CHAT_11, + SFX_DEFAULT_VOICE_CHAT_12, + SFX_DEFAULT_VOICE_CHAT_13, + SFX_DEFAULT_VOICE_CHAT_14, + SFX_DEFAULT_VOICE_CHAT_15, + SFX_DEFAULT_VOICE_CHAT_16, + SFX_DEFAULT_VOICE_CHAT_17, + SFX_DEFAULT_VOICE_CHAT_18, + SFX_DEFAULT_VOICE_CHAT_19, + SFX_DEFAULT_VOICE_CHAT_20, + SFX_DEFAULT_VOICE_CHAT_21, + SFX_DEFAULT_VOICE_CHAT_22, + SFX_DEFAULT_VOICE_CHAT_23, + SFX_DEFAULT_VOICE_CHAT_24, + SFX_DEFAULT_VOICE_CHAT_25, + SFX_DEFAULT_VOICE_DODGE_1, + SFX_DEFAULT_VOICE_DODGE_2, + SFX_DEFAULT_VOICE_DODGE_3, + SFX_DEFAULT_VOICE_DODGE_4, + SFX_DEFAULT_VOICE_DODGE_5, + SFX_DEFAULT_VOICE_DODGE_6, + SFX_DEFAULT_VOICE_DODGE_7, + SFX_DEFAULT_VOICE_DODGE_8, + SFX_DEFAULT_VOICE_DODGE_9, + SFX_DEFAULT_VOICE_DODGE_10, + SFX_DEFAULT_VOICE_DODGE_11, + SFX_DEFAULT_VOICE_DODGE_12, + SFX_DEFAULT_VOICE_DODGE_13, + SFX_DEFAULT_VOICE_DODGE_14, + SFX_DEFAULT_VOICE_DODGE_15, + SFX_DEFAULT_VOICE_DODGE_16, + SFX_DEFAULT_VOICE_DODGE_17, + SFX_DEFAULT_VOICE_DODGE_18, + SFX_DEFAULT_VOICE_DODGE_19, + SFX_DEFAULT_VOICE_EYEING_1, + SFX_DEFAULT_VOICE_EYEING_2, + SFX_DEFAULT_VOICE_EYEING_3, + SFX_DEFAULT_VOICE_EYEING_4, + SFX_DEFAULT_VOICE_EYEING_5, + SFX_DEFAULT_VOICE_EYEING_6, + SFX_DEFAULT_VOICE_FIGHT_1, + SFX_DEFAULT_VOICE_FIGHT_2, + SFX_DEFAULT_VOICE_FIGHT_3, + SFX_DEFAULT_VOICE_FIGHT_4, + SFX_DEFAULT_VOICE_FIGHT_5, + SFX_DEFAULT_VOICE_FIGHT_6, + SFX_DEFAULT_VOICE_FIGHT_7, + SFX_DEFAULT_VOICE_FIGHT_8, + SFX_DEFAULT_VOICE_FIGHT_9, + SFX_DEFAULT_VOICE_FIGHT_10, + SFX_DEFAULT_VOICE_FIGHT_11, + SFX_DEFAULT_VOICE_FIGHT_12, + SFX_DEFAULT_VOICE_FIGHT_13, + SFX_DEFAULT_VOICE_FIGHT_14, + SFX_DEFAULT_VOICE_FIGHT_15, + SFX_DEFAULT_VOICE_FIGHT_16, + SFX_DEFAULT_VOICE_GENERIC_CRASH_1, + SFX_DEFAULT_VOICE_GENERIC_CRASH_2, + SFX_DEFAULT_VOICE_GENERIC_CRASH_3, + SFX_DEFAULT_VOICE_GENERIC_CRASH_4, + SFX_DEFAULT_VOICE_GENERIC_CRASH_5, + SFX_DEFAULT_VOICE_GENERIC_CRASH_6, + SFX_DEFAULT_VOICE_GENERIC_CRASH_7, + SFX_DEFAULT_VOICE_GENERIC_CRASH_8, + SFX_DEFAULT_VOICE_GENERIC_CRASH_9, + SFX_DEFAULT_VOICE_GENERIC_CRASH_10, + SFX_DEFAULT_VOICE_GENERIC_CRASH_11, + SFX_DEFAULT_VOICE_GENERIC_CRASH_12, + SFX_DEFAULT_VOICE_GENERIC_CRASH_13, + SFX_DEFAULT_VOICE_GUN_PANIC_1, + SFX_DEFAULT_VOICE_GUN_PANIC_2, + SFX_DEFAULT_VOICE_GUN_PANIC_3, + SFX_DEFAULT_VOICE_GUN_PANIC_4, + SFX_DEFAULT_VOICE_GUN_PANIC_5, + SFX_DEFAULT_VOICE_GUN_PANIC_6, + SFX_DEFAULT_VOICE_GUN_PANIC_7, + SFX_DEFAULT_VOICE_GUN_PANIC_8, + SFX_DEFAULT_VOICE_GUN_PANIC_9, + SFX_DEFAULT_VOICE_GUN_PANIC_10, + SFX_DEFAULT_VOICE_GUN_PANIC_11, + SFX_DEFAULT_VOICE_GUN_PANIC_12, + SFX_DEFAULT_VOICE_JACKED_1, + SFX_DEFAULT_VOICE_JACKED_2, + SFX_DEFAULT_VOICE_JACKED_3, + SFX_DEFAULT_VOICE_JACKED_4, + SFX_DEFAULT_VOICE_JACKED_5, + SFX_DEFAULT_VOICE_JACKED_6, + SFX_DEFAULT_VOICE_JACKED_7, + SFX_DEFAULT_VOICE_JACKED_8, + SFX_DEFAULT_VOICE_JACKED_9, + SFX_DEFAULT_VOICE_JACKED_10, + SFX_DEFAULT_VOICE_JACKED_11, + SFX_DEFAULT_VOICE_JACKED_12, + SFX_DEFAULT_VOICE_JACKING_1, + SFX_DEFAULT_VOICE_JACKING_2, + SFX_DEFAULT_VOICE_JACKING_3, + SFX_DEFAULT_VOICE_JACKING_4, + SFX_DEFAULT_VOICE_JACKING_5, + SFX_DEFAULT_VOICE_JACKING_6, + SFX_DEFAULT_VOICE_JACKING_7, + SFX_DEFAULT_VOICE_JACKING_8, + SFX_DEFAULT_VOICE_JACKING_9, + SFX_DEFAULT_VOICE_JACKING_10, + SFX_DEFAULT_VOICE_JACKING_11, + SFX_DEFAULT_VOICE_JACKING_12, + SFX_DEFAULT_VOICE_JACKING_13, + SFX_DEFAULT_VOICE_LOST_1, + SFX_DEFAULT_VOICE_LOST_2, + SFX_DEFAULT_VOICE_LOST_3, + SFX_DEFAULT_VOICE_LOST_4, + SFX_DEFAULT_VOICE_LOST_5, + SFX_DEFAULT_VOICE_MUGGED_1, + SFX_DEFAULT_VOICE_MUGGED_2, + SFX_DEFAULT_VOICE_MUGGED_3, + SFX_DEFAULT_VOICE_MUGGED_4, + SFX_DEFAULT_VOICE_RUN_1, + SFX_DEFAULT_VOICE_RUN_2, + SFX_DEFAULT_VOICE_RUN_3, + SFX_DEFAULT_VOICE_RUN_4, + SFX_DEFAULT_VOICE_RUN_5, + SFX_DEFAULT_VOICE_RUN_6, + SFX_DEFAULT_VOICE_RUN_7, + SFX_DEFAULT_VOICE_RUN_8, + SFX_DEFAULT_VOICE_RUN_9, + SFX_DEFAULT_VOICE_RUN_10, + SFX_DEFAULT_VOICE_RUN_11, + SFX_DEFAULT_VOICE_RUN_12, + SFX_DEFAULT_VOICE_RUN_13, + SFX_DEFAULT_VOICE_RUN_14, + SFX_DEFAULT_VOICE_RUN_15, + SFX_DEFAULT_VOICE_RUN_16, + SFX_DEFAULT_VOICE_RUN_17, + SFX_DEFAULT_VOICE_RUN_18, + SFX_DEFAULT_VOICE_RUN_19, + SFX_DEFAULT_VOICE_SAVED_1, + SFX_DEFAULT_VOICE_SAVED_2, + SFX_DEFAULT_VOICE_SAVED_3, + SFX_DEFAULT_VOICE_SAVED_4, + SFX_DEFAULT_VOICE_SHOCKED_1, + SFX_DEFAULT_VOICE_SHOCKED_2, + SFX_DEFAULT_VOICE_SHOCKED_3, + SFX_DEFAULT_VOICE_SHOCKED_4, + SFX_DEFAULT_VOICE_SHOCKED_5, + SFX_DEFAULT_VOICE_SHOCKED_6, + SFX_DEFAULT_VOICE_TAXI_1, + SFX_DEFAULT_VOICE_TAXI_2, + SFX_DEFAULT_VOICE_TAXI_3, + SFX_DEFAULT_VOICE_TAXI_4, + SFX_DEFAULT_VOICE_TAXI_5, + + SFX_CUBAN_GANG_1_BLOCKED_1, + SFX_CUBAN_GANG_1_BLOCKED_2, + SFX_CUBAN_GANG_1_BLOCKED_3, + SFX_CUBAN_GANG_1_BLOCKED_4, + SFX_CUBAN_GANG_1_BLOCKED_5, + SFX_CUBAN_GANG_1_BLOCKED_6, + SFX_CUBAN_GANG_1_BLOCKED_7, + SFX_CUBAN_GANG_1_BLOCKED_8, + SFX_CUBAN_GANG_1_BUMP_1, + SFX_CUBAN_GANG_1_BUMP_2, + SFX_CUBAN_GANG_1_BUMP_3, + SFX_CUBAN_GANG_1_BUMP_4, + SFX_CUBAN_GANG_1_BUMP_5, + SFX_CUBAN_GANG_1_BUMP_6, + SFX_CUBAN_GANG_1_BUMP_7, + SFX_CUBAN_GANG_1_BUMP_8, + SFX_CUBAN_GANG_1_BUMP_9, + SFX_CUBAN_GANG_1_BUMP_10, + SFX_CUBAN_GANG_1_BUMP_11, + SFX_CUBAN_GANG_1_CAR_CRASH_1, + SFX_CUBAN_GANG_1_CAR_CRASH_2, + SFX_CUBAN_GANG_1_CAR_CRASH_3, + SFX_CUBAN_GANG_1_CAR_CRASH_4, + SFX_CUBAN_GANG_1_CAR_CRASH_5, + SFX_CUBAN_GANG_1_CAR_CRASH_6, + SFX_CUBAN_GANG_1_CAR_CRASH_7, + SFX_CUBAN_GANG_1_CAR_CRASH_8, + SFX_CUBAN_GANG_1_CHAT_1, + SFX_CUBAN_GANG_1_CHAT_2, + SFX_CUBAN_GANG_1_CHAT_3, + SFX_CUBAN_GANG_1_CHAT_4, + SFX_CUBAN_GANG_1_CHAT_5, + SFX_CUBAN_GANG_1_CHAT_6, + SFX_CUBAN_GANG_1_CHAT_7, + SFX_CUBAN_GANG_1_CHAT_8, + SFX_CUBAN_GANG_1_CHAT_9, + SFX_CUBAN_GANG_1_CHAT_10, + SFX_CUBAN_GANG_1_DODGE_1, + SFX_CUBAN_GANG_1_DODGE_2, + SFX_CUBAN_GANG_1_DODGE_3, + SFX_CUBAN_GANG_1_DODGE_4, + SFX_CUBAN_GANG_1_DODGE_5, + SFX_CUBAN_GANG_1_DODGE_6, + SFX_CUBAN_GANG_1_DODGE_7, + SFX_CUBAN_GANG_1_DODGE_8, + SFX_CUBAN_GANG_1_DODGE_9, + SFX_CUBAN_GANG_1_EYEING_1, + SFX_CUBAN_GANG_1_EYEING_2, + SFX_CUBAN_GANG_1_FIGHT_1, + SFX_CUBAN_GANG_1_FIGHT_2, + SFX_CUBAN_GANG_1_FIGHT_3, + SFX_CUBAN_GANG_1_FIGHT_4, + SFX_CUBAN_GANG_1_FIGHT_5, + SFX_CUBAN_GANG_1_FIGHT_6, + SFX_CUBAN_GANG_1_FIGHT_7, + SFX_CUBAN_GANG_1_FIGHT_8, + SFX_CUBAN_GANG_1_FIGHT_9, + SFX_CUBAN_GANG_1_GENERIC_CRASH_1, + SFX_CUBAN_GANG_1_GENERIC_CRASH_2, + SFX_CUBAN_GANG_1_GENERIC_CRASH_3, + SFX_CUBAN_GANG_1_GENERIC_CRASH_4, + SFX_CUBAN_GANG_1_GENERIC_CRASH_5, + SFX_CUBAN_GANG_1_GENERIC_CRASH_6, + SFX_CUBAN_GANG_1_GENERIC_CRASH_7, + SFX_CUBAN_GANG_1_GENERIC_CRASH_8, + SFX_CUBAN_GANG_1_GUN_COOL_1, + SFX_CUBAN_GANG_1_GUN_COOL_2, + SFX_CUBAN_GANG_1_GUN_COOL_3, + SFX_CUBAN_GANG_1_GUN_COOL_4, + SFX_CUBAN_GANG_1_GUN_COOL_5, + SFX_CUBAN_GANG_1_JACKED_1, + SFX_CUBAN_GANG_1_JACKED_2, + SFX_CUBAN_GANG_1_JACKED_3, + SFX_CUBAN_GANG_1_JACKED_4, + SFX_CUBAN_GANG_1_JACKING_1, + SFX_CUBAN_GANG_1_JACKING_2, + SFX_CUBAN_GANG_1_JACKING_3, + SFX_CUBAN_GANG_1_JACKING_4, + SFX_CUBAN_GANG_1_JACKING_5, + SFX_CUBAN_GANG_1_LOST_1, + SFX_CUBAN_GANG_1_LOST_2, + SFX_CUBAN_GANG_1_MUGGED_1, + SFX_CUBAN_GANG_1_MUGGED_2, + SFX_CUBAN_GANG_1_SAVED_1, + SFX_CUBAN_GANG_1_TAXI_1, + SFX_CUBAN_GANG_1_TAXI_2, + SFX_CUBAN_GANG_2_BLOCKED_1, + SFX_CUBAN_GANG_2_BLOCKED_2, + SFX_CUBAN_GANG_2_BLOCKED_3, + SFX_CUBAN_GANG_2_BLOCKED_4, + SFX_CUBAN_GANG_2_BLOCKED_5, + SFX_CUBAN_GANG_2_BLOCKED_6, + SFX_CUBAN_GANG_2_BLOCKED_7, + SFX_CUBAN_GANG_2_BLOCKED_8, + SFX_CUBAN_GANG_2_BUMP_1, + SFX_CUBAN_GANG_2_BUMP_2, + SFX_CUBAN_GANG_2_BUMP_3, + SFX_CUBAN_GANG_2_BUMP_4, + SFX_CUBAN_GANG_2_BUMP_5, + SFX_CUBAN_GANG_2_BUMP_6, + SFX_CUBAN_GANG_2_BUMP_7, + SFX_CUBAN_GANG_2_BUMP_8, + SFX_CUBAN_GANG_2_BUMP_9, + SFX_CUBAN_GANG_2_BUMP_10, + SFX_CUBAN_GANG_2_BUMP_11, + SFX_CUBAN_GANG_2_CAR_CRASH_1, + SFX_CUBAN_GANG_2_CAR_CRASH_2, + SFX_CUBAN_GANG_2_CAR_CRASH_3, + SFX_CUBAN_GANG_2_CAR_CRASH_4, + SFX_CUBAN_GANG_2_CAR_CRASH_5, + SFX_CUBAN_GANG_2_CAR_CRASH_6, + SFX_CUBAN_GANG_2_CAR_CRASH_7, + SFX_CUBAN_GANG_2_CAR_CRASH_8, + SFX_CUBAN_GANG_2_CHAT_1, + SFX_CUBAN_GANG_2_CHAT_2, + SFX_CUBAN_GANG_2_CHAT_3, + SFX_CUBAN_GANG_2_CHAT_4, + SFX_CUBAN_GANG_2_CHAT_5, + SFX_CUBAN_GANG_2_CHAT_6, + SFX_CUBAN_GANG_2_CHAT_7, + SFX_CUBAN_GANG_2_CHAT_8, + SFX_CUBAN_GANG_2_CHAT_9, + SFX_CUBAN_GANG_2_CHAT_10, + SFX_CUBAN_GANG_2_DODGE_1, + SFX_CUBAN_GANG_2_DODGE_2, + SFX_CUBAN_GANG_2_DODGE_3, + SFX_CUBAN_GANG_2_DODGE_4, + SFX_CUBAN_GANG_2_DODGE_5, + SFX_CUBAN_GANG_2_DODGE_6, + SFX_CUBAN_GANG_2_DODGE_7, + SFX_CUBAN_GANG_2_DODGE_8, + SFX_CUBAN_GANG_2_DODGE_9, + SFX_CUBAN_GANG_2_EYEING_1, + SFX_CUBAN_GANG_2_EYEING_2, + SFX_CUBAN_GANG_2_FIGHT_1, + SFX_CUBAN_GANG_2_FIGHT_2, + SFX_CUBAN_GANG_2_FIGHT_3, + SFX_CUBAN_GANG_2_FIGHT_4, + SFX_CUBAN_GANG_2_FIGHT_5, + SFX_CUBAN_GANG_2_FIGHT_6, + SFX_CUBAN_GANG_2_FIGHT_7, + SFX_CUBAN_GANG_2_FIGHT_8, + SFX_CUBAN_GANG_2_FIGHT_9, + SFX_CUBAN_GANG_2_GENERIC_CRASH_1, + SFX_CUBAN_GANG_2_GENERIC_CRASH_2, + SFX_CUBAN_GANG_2_GENERIC_CRASH_3, + SFX_CUBAN_GANG_2_GENERIC_CRASH_4, + SFX_CUBAN_GANG_2_GENERIC_CRASH_5, + SFX_CUBAN_GANG_2_GENERIC_CRASH_6, + SFX_CUBAN_GANG_2_GENERIC_CRASH_7, + SFX_CUBAN_GANG_2_GENERIC_CRASH_8, + SFX_CUBAN_GANG_2_GUN_COOL_1, + SFX_CUBAN_GANG_2_GUN_COOL_2, + SFX_CUBAN_GANG_2_GUN_COOL_3, + SFX_CUBAN_GANG_2_GUN_COOL_4, + SFX_CUBAN_GANG_2_GUN_COOL_5, + SFX_CUBAN_GANG_2_JACKED_1, + SFX_CUBAN_GANG_2_JACKED_2, + SFX_CUBAN_GANG_2_JACKED_3, + SFX_CUBAN_GANG_2_JACKED_4, + SFX_CUBAN_GANG_2_JACKING_1, + SFX_CUBAN_GANG_2_JACKING_2, + SFX_CUBAN_GANG_2_JACKING_3, + SFX_CUBAN_GANG_2_JACKING_4, + SFX_CUBAN_GANG_2_JACKING_5, + SFX_CUBAN_GANG_2_LOST_1, + SFX_CUBAN_GANG_2_LOST_2, + SFX_CUBAN_GANG_2_MUGGED_1, + SFX_CUBAN_GANG_2_MUGGED_2, + SFX_CUBAN_GANG_2_SAVED_1, + SFX_CUBAN_GANG_2_TAXI_1, + SFX_CUBAN_GANG_2_TAXI_2, + SFX_CUBAN_GANG_3_BLOCKED_1, + SFX_CUBAN_GANG_3_BLOCKED_2, + SFX_CUBAN_GANG_3_BLOCKED_3, + SFX_CUBAN_GANG_3_BLOCKED_4, + SFX_CUBAN_GANG_3_BLOCKED_5, + SFX_CUBAN_GANG_3_BLOCKED_6, + SFX_CUBAN_GANG_3_BLOCKED_7, + SFX_CUBAN_GANG_3_BLOCKED_8, + SFX_CUBAN_GANG_3_BUMP_1, + SFX_CUBAN_GANG_3_BUMP_2, + SFX_CUBAN_GANG_3_BUMP_3, + SFX_CUBAN_GANG_3_BUMP_4, + SFX_CUBAN_GANG_3_BUMP_5, + SFX_CUBAN_GANG_3_BUMP_6, + SFX_CUBAN_GANG_3_BUMP_7, + SFX_CUBAN_GANG_3_BUMP_8, + SFX_CUBAN_GANG_3_BUMP_9, + SFX_CUBAN_GANG_3_BUMP_10, + SFX_CUBAN_GANG_3_BUMP_11, + SFX_CUBAN_GANG_3_CAR_CRASH_1, + SFX_CUBAN_GANG_3_CAR_CRASH_2, + SFX_CUBAN_GANG_3_CAR_CRASH_3, + SFX_CUBAN_GANG_3_CAR_CRASH_4, + SFX_CUBAN_GANG_3_CAR_CRASH_5, + SFX_CUBAN_GANG_3_CAR_CRASH_6, + SFX_CUBAN_GANG_3_CAR_CRASH_7, + SFX_CUBAN_GANG_3_CAR_CRASH_8, + SFX_CUBAN_GANG_3_CHAT_1, + SFX_CUBAN_GANG_3_CHAT_2, + SFX_CUBAN_GANG_3_CHAT_3, + SFX_CUBAN_GANG_3_CHAT_4, + SFX_CUBAN_GANG_3_CHAT_5, + SFX_CUBAN_GANG_3_CHAT_6, + SFX_CUBAN_GANG_3_CHAT_7, + SFX_CUBAN_GANG_3_CHAT_8, + SFX_CUBAN_GANG_3_CHAT_9, + SFX_CUBAN_GANG_3_CHAT_10, + SFX_CUBAN_GANG_3_DODGE_1, + SFX_CUBAN_GANG_3_DODGE_2, + SFX_CUBAN_GANG_3_DODGE_3, + SFX_CUBAN_GANG_3_DODGE_4, + SFX_CUBAN_GANG_3_DODGE_5, + SFX_CUBAN_GANG_3_DODGE_6, + SFX_CUBAN_GANG_3_DODGE_7, + SFX_CUBAN_GANG_3_DODGE_8, + SFX_CUBAN_GANG_3_DODGE_9, + SFX_CUBAN_GANG_3_EYEING_1, + SFX_CUBAN_GANG_3_EYEING_2, + SFX_CUBAN_GANG_3_FIGHT_1, + SFX_CUBAN_GANG_3_FIGHT_2, + SFX_CUBAN_GANG_3_FIGHT_3, + SFX_CUBAN_GANG_3_FIGHT_4, + SFX_CUBAN_GANG_3_FIGHT_5, + SFX_CUBAN_GANG_3_FIGHT_6, + SFX_CUBAN_GANG_3_FIGHT_7, + SFX_CUBAN_GANG_3_FIGHT_8, + SFX_CUBAN_GANG_3_FIGHT_9, + SFX_CUBAN_GANG_3_GENERIC_CRASH_1, + SFX_CUBAN_GANG_3_GENERIC_CRASH_2, + SFX_CUBAN_GANG_3_GENERIC_CRASH_3, + SFX_CUBAN_GANG_3_GENERIC_CRASH_4, + SFX_CUBAN_GANG_3_GENERIC_CRASH_5, + SFX_CUBAN_GANG_3_GENERIC_CRASH_6, + SFX_CUBAN_GANG_3_GENERIC_CRASH_7, + SFX_CUBAN_GANG_3_GENERIC_CRASH_8, + SFX_CUBAN_GANG_3_GUN_COOL_1, + SFX_CUBAN_GANG_3_GUN_COOL_2, + SFX_CUBAN_GANG_3_GUN_COOL_3, + SFX_CUBAN_GANG_3_GUN_COOL_4, + SFX_CUBAN_GANG_3_GUN_COOL_5, + SFX_CUBAN_GANG_3_JACKED_1, + SFX_CUBAN_GANG_3_JACKED_2, + SFX_CUBAN_GANG_3_JACKED_3, + SFX_CUBAN_GANG_3_JACKED_4, + SFX_CUBAN_GANG_3_JACKING_1, + SFX_CUBAN_GANG_3_JACKING_2, + SFX_CUBAN_GANG_3_JACKING_3, + SFX_CUBAN_GANG_3_JACKING_4, + SFX_CUBAN_GANG_3_JACKING_5, + SFX_CUBAN_GANG_3_LOST_1, + SFX_CUBAN_GANG_3_LOST_2, + SFX_CUBAN_GANG_3_MUGGED_1, + SFX_CUBAN_GANG_3_MUGGED_2, + SFX_CUBAN_GANG_3_SAVED_1, + SFX_CUBAN_GANG_3_TAXI_1, + SFX_CUBAN_GANG_3_TAXI_2, + + SFX_BIKER_GANG_1_BLOCKED_1, + SFX_BIKER_GANG_1_BLOCKED_2, + SFX_BIKER_GANG_1_BLOCKED_3, + SFX_BIKER_GANG_1_BLOCKED_4, + SFX_BIKER_GANG_1_BLOCKED_5, + SFX_BIKER_GANG_1_BLOCKED_6, + SFX_BIKER_GANG_1_BLOCKED_7, + SFX_BIKER_GANG_1_BLOCKED_8, + SFX_BIKER_GANG_1_BLOCKED_9, + SFX_BIKER_GANG_1_BLOCKED_10, + SFX_BIKER_GANG_1_BUMP_1, + SFX_BIKER_GANG_1_BUMP_2, + SFX_BIKER_GANG_1_BUMP_3, + SFX_BIKER_GANG_1_BUMP_4, + SFX_BIKER_GANG_1_BUMP_5, + SFX_BIKER_GANG_1_BUMP_6, + SFX_BIKER_GANG_1_BUMP_7, + SFX_BIKER_GANG_1_BUMP_8, + SFX_BIKER_GANG_1_BUMP_9, + SFX_BIKER_GANG_1_BUMP_10, + SFX_BIKER_GANG_1_CHAT_1, + SFX_BIKER_GANG_1_CHAT_2, + SFX_BIKER_GANG_1_CHAT_3, + SFX_BIKER_GANG_1_CHAT_4, + SFX_BIKER_GANG_1_CHAT_5, + SFX_BIKER_GANG_1_CHAT_6, + SFX_BIKER_GANG_1_CHAT_7, + SFX_BIKER_GANG_1_CHAT_8, + SFX_BIKER_GANG_1_CHAT_9, + SFX_BIKER_GANG_1_CHAT_10, + SFX_BIKER_GANG_1_CHAT_11, + SFX_BIKER_GANG_1_CHAT_12, + SFX_BIKER_GANG_1_DODGE_1, + SFX_BIKER_GANG_1_DODGE_2, + SFX_BIKER_GANG_1_DODGE_3, + SFX_BIKER_GANG_1_DODGE_4, + SFX_BIKER_GANG_1_DODGE_5, + SFX_BIKER_GANG_1_DODGE_6, + SFX_BIKER_GANG_1_DODGE_7, + SFX_BIKER_GANG_1_DODGE_8, + SFX_BIKER_GANG_1_DODGE_9, + SFX_BIKER_GANG_1_FIGHT_1, + SFX_BIKER_GANG_1_FIGHT_2, + SFX_BIKER_GANG_1_FIGHT_3, + SFX_BIKER_GANG_1_FIGHT_4, + SFX_BIKER_GANG_1_FIGHT_5, + SFX_BIKER_GANG_1_FIGHT_6, + SFX_BIKER_GANG_1_FIGHT_7, + SFX_BIKER_GANG_1_FIGHT_8, + SFX_BIKER_GANG_1_FIGHT_9, + SFX_BIKER_GANG_1_GENERIC_CRASH_1, + SFX_BIKER_GANG_1_GENERIC_CRASH_2, + SFX_BIKER_GANG_1_GENERIC_CRASH_3, + SFX_BIKER_GANG_1_GENERIC_CRASH_4, + SFX_BIKER_GANG_1_GENERIC_CRASH_5, + SFX_BIKER_GANG_1_GENERIC_CRASH_6, + SFX_BIKER_GANG_1_GENERIC_CRASH_7, + SFX_BIKER_GANG_1_GENERIC_CRASH_8, + SFX_BIKER_GANG_1_GUN_COOL_1, + SFX_BIKER_GANG_1_GUN_COOL_2, + SFX_BIKER_GANG_1_GUN_COOL_3, + SFX_BIKER_GANG_1_GUN_COOL_4, + SFX_BIKER_GANG_1_GUN_COOL_5, + SFX_BIKER_GANG_1_JACKED_1, + SFX_BIKER_GANG_1_JACKED_2, + SFX_BIKER_GANG_1_JACKED_3, + SFX_BIKER_GANG_1_JACKED_4, + SFX_BIKER_GANG_1_JACKED_5, + SFX_BIKER_GANG_1_JACKED_6, + SFX_BIKER_GANG_1_JACKED_7, + SFX_BIKER_GANG_1_JACKED_8, + SFX_BIKER_GANG_1_JACKING_1, + SFX_BIKER_GANG_1_JACKING_2, + SFX_BIKER_GANG_1_JACKING_3, + SFX_BIKER_GANG_1_JACKING_4, + SFX_BIKER_GANG_1_LOST_1, + SFX_BIKER_GANG_1_LOST_2, + SFX_BIKER_GANG_1_MUGGED_1, + SFX_BIKER_GANG_1_MUGGED_2, + SFX_BIKER_GANG_1_SAVED_1, + SFX_BIKER_GANG_1_TAXI_1, + SFX_BIKER_GANG_1_TAXI_2, + + SFX_BIKER_GANG_2_BLOCKED_1, + SFX_BIKER_GANG_2_BLOCKED_2, + SFX_BIKER_GANG_2_BLOCKED_3, + SFX_BIKER_GANG_2_BLOCKED_4, + SFX_BIKER_GANG_2_BLOCKED_5, + SFX_BIKER_GANG_2_BLOCKED_6, + SFX_BIKER_GANG_2_BLOCKED_7, + SFX_BIKER_GANG_2_BLOCKED_8, + SFX_BIKER_GANG_2_BLOCKED_9, + SFX_BIKER_GANG_2_BLOCKED_10, + SFX_BIKER_GANG_2_BUMP_1, + SFX_BIKER_GANG_2_BUMP_2, + SFX_BIKER_GANG_2_BUMP_3, + SFX_BIKER_GANG_2_BUMP_4, + SFX_BIKER_GANG_2_BUMP_5, + SFX_BIKER_GANG_2_BUMP_6, + SFX_BIKER_GANG_2_BUMP_7, + SFX_BIKER_GANG_2_BUMP_8, + SFX_BIKER_GANG_2_BUMP_9, + SFX_BIKER_GANG_2_BUMP_10, + SFX_BIKER_GANG_2_CHAT_1, + SFX_BIKER_GANG_2_CHAT_2, + SFX_BIKER_GANG_2_CHAT_3, + SFX_BIKER_GANG_2_CHAT_4, + SFX_BIKER_GANG_2_CHAT_5, + SFX_BIKER_GANG_2_CHAT_6, + SFX_BIKER_GANG_2_CHAT_7, + SFX_BIKER_GANG_2_CHAT_8, + SFX_BIKER_GANG_2_CHAT_9, + SFX_BIKER_GANG_2_CHAT_10, + SFX_BIKER_GANG_2_CHAT_11, + SFX_BIKER_GANG_2_CHAT_12, + SFX_BIKER_GANG_2_DODGE_1, + SFX_BIKER_GANG_2_DODGE_2, + SFX_BIKER_GANG_2_DODGE_3, + SFX_BIKER_GANG_2_DODGE_4, + SFX_BIKER_GANG_2_DODGE_5, + SFX_BIKER_GANG_2_DODGE_6, + SFX_BIKER_GANG_2_DODGE_7, + SFX_BIKER_GANG_2_DODGE_8, + SFX_BIKER_GANG_2_DODGE_9, + SFX_BIKER_GANG_2_FIGHT_1, + SFX_BIKER_GANG_2_FIGHT_2, + SFX_BIKER_GANG_2_FIGHT_3, + SFX_BIKER_GANG_2_FIGHT_4, + SFX_BIKER_GANG_2_FIGHT_5, + SFX_BIKER_GANG_2_FIGHT_6, + SFX_BIKER_GANG_2_FIGHT_7, + SFX_BIKER_GANG_2_FIGHT_8, + SFX_BIKER_GANG_2_FIGHT_9, + SFX_BIKER_GANG_2_GENERIC_CRASH_1, + SFX_BIKER_GANG_2_GENERIC_CRASH_2, + SFX_BIKER_GANG_2_GENERIC_CRASH_3, + SFX_BIKER_GANG_2_GENERIC_CRASH_4, + SFX_BIKER_GANG_2_GENERIC_CRASH_5, + SFX_BIKER_GANG_2_GENERIC_CRASH_6, + SFX_BIKER_GANG_2_GENERIC_CRASH_7, + SFX_BIKER_GANG_2_GENERIC_CRASH_8, + SFX_BIKER_GANG_2_GUN_COOL_1, + SFX_BIKER_GANG_2_GUN_COOL_2, + SFX_BIKER_GANG_2_GUN_COOL_3, + SFX_BIKER_GANG_2_GUN_COOL_4, + SFX_BIKER_GANG_2_GUN_COOL_5, + SFX_BIKER_GANG_2_JACKED_1, + SFX_BIKER_GANG_2_JACKED_2, + SFX_BIKER_GANG_2_JACKED_3, + SFX_BIKER_GANG_2_JACKED_4, + SFX_BIKER_GANG_2_JACKED_5, + SFX_BIKER_GANG_2_JACKED_6, + SFX_BIKER_GANG_2_JACKED_7, + SFX_BIKER_GANG_2_JACKED_8, + SFX_BIKER_GANG_2_JACKING_1, + SFX_BIKER_GANG_2_JACKING_2, + SFX_BIKER_GANG_2_JACKING_3, + SFX_BIKER_GANG_2_JACKING_4, + SFX_BIKER_GANG_2_LOST_1, + SFX_BIKER_GANG_2_LOST_2, + SFX_BIKER_GANG_2_MUGGED_1, + SFX_BIKER_GANG_2_MUGGED_2, + SFX_BIKER_GANG_2_SAVED_1, + SFX_BIKER_GANG_2_TAXI_1, + SFX_BIKER_GANG_2_TAXI_2, + + SFX_BIKER_GANG_3_BLOCKED_1, + SFX_BIKER_GANG_3_BLOCKED_2, + SFX_BIKER_GANG_3_BLOCKED_3, + SFX_BIKER_GANG_3_BLOCKED_4, + SFX_BIKER_GANG_3_BLOCKED_5, + SFX_BIKER_GANG_3_BLOCKED_6, + SFX_BIKER_GANG_3_BLOCKED_7, + SFX_BIKER_GANG_3_BLOCKED_8, + SFX_BIKER_GANG_3_BLOCKED_9, + SFX_BIKER_GANG_3_BLOCKED_10, + SFX_BIKER_GANG_3_BUMP_1, + SFX_BIKER_GANG_3_BUMP_2, + SFX_BIKER_GANG_3_BUMP_3, + SFX_BIKER_GANG_3_BUMP_4, + SFX_BIKER_GANG_3_BUMP_5, + SFX_BIKER_GANG_3_BUMP_6, + SFX_BIKER_GANG_3_BUMP_7, + SFX_BIKER_GANG_3_BUMP_8, + SFX_BIKER_GANG_3_BUMP_9, + SFX_BIKER_GANG_3_BUMP_10, + SFX_BIKER_GANG_3_CHAT_1, + SFX_BIKER_GANG_3_CHAT_2, + SFX_BIKER_GANG_3_CHAT_3, + SFX_BIKER_GANG_3_CHAT_4, + SFX_BIKER_GANG_3_CHAT_5, + SFX_BIKER_GANG_3_CHAT_6, + SFX_BIKER_GANG_3_CHAT_7, + SFX_BIKER_GANG_3_CHAT_8, + SFX_BIKER_GANG_3_CHAT_9, + SFX_BIKER_GANG_3_CHAT_10, + SFX_BIKER_GANG_3_CHAT_11, + SFX_BIKER_GANG_3_CHAT_12, + SFX_BIKER_GANG_3_DODGE_1, + SFX_BIKER_GANG_3_DODGE_2, + SFX_BIKER_GANG_3_DODGE_3, + SFX_BIKER_GANG_3_DODGE_4, + SFX_BIKER_GANG_3_DODGE_5, + SFX_BIKER_GANG_3_DODGE_6, + SFX_BIKER_GANG_3_DODGE_7, + SFX_BIKER_GANG_3_DODGE_8, + SFX_BIKER_GANG_3_DODGE_9, + SFX_BIKER_GANG_3_FIGHT_1, + SFX_BIKER_GANG_3_FIGHT_2, + SFX_BIKER_GANG_3_FIGHT_3, + SFX_BIKER_GANG_3_FIGHT_4, + SFX_BIKER_GANG_3_FIGHT_5, + SFX_BIKER_GANG_3_FIGHT_6, + SFX_BIKER_GANG_3_FIGHT_7, + SFX_BIKER_GANG_3_FIGHT_8, + SFX_BIKER_GANG_3_FIGHT_9, + SFX_BIKER_GANG_3_GENERIC_CRASH_1, + SFX_BIKER_GANG_3_GENERIC_CRASH_2, + SFX_BIKER_GANG_3_GENERIC_CRASH_3, + SFX_BIKER_GANG_3_GENERIC_CRASH_4, + SFX_BIKER_GANG_3_GENERIC_CRASH_5, + SFX_BIKER_GANG_3_GENERIC_CRASH_6, + SFX_BIKER_GANG_3_GENERIC_CRASH_7, + SFX_BIKER_GANG_3_GENERIC_CRASH_8, + SFX_BIKER_GANG_3_GUN_COOL_1, + SFX_BIKER_GANG_3_GUN_COOL_2, + SFX_BIKER_GANG_3_GUN_COOL_3, + SFX_BIKER_GANG_3_GUN_COOL_4, + SFX_BIKER_GANG_3_GUN_COOL_5, + SFX_BIKER_GANG_3_JACKED_1, + SFX_BIKER_GANG_3_JACKED_2, + SFX_BIKER_GANG_3_JACKED_3, + SFX_BIKER_GANG_3_JACKED_4, + SFX_BIKER_GANG_3_JACKED_5, + SFX_BIKER_GANG_3_JACKED_6, + SFX_BIKER_GANG_3_JACKED_7, + SFX_BIKER_GANG_3_JACKED_8, + SFX_BIKER_GANG_3_JACKING_1, + SFX_BIKER_GANG_3_JACKING_2, + SFX_BIKER_GANG_3_JACKING_3, + SFX_BIKER_GANG_3_JACKING_4, + SFX_BIKER_GANG_3_LOST_1, + SFX_BIKER_GANG_3_LOST_2, + SFX_BIKER_GANG_3_MUGGED_1, + SFX_BIKER_GANG_3_MUGGED_2, + SFX_BIKER_GANG_3_SAVED_1, + SFX_BIKER_GANG_3_TAXI_1, + SFX_BIKER_GANG_3_TAXI_2, + + SFX_HAITIAN_GANG_1_BLOCKED_1, + SFX_HAITIAN_GANG_1_BLOCKED_2, + SFX_HAITIAN_GANG_1_BLOCKED_3, + SFX_HAITIAN_GANG_1_BLOCKED_4, + SFX_HAITIAN_GANG_1_BLOCKED_5, + SFX_HAITIAN_GANG_1_BLOCKED_6, + SFX_HAITIAN_GANG_1_BLOCKED_7, + SFX_HAITIAN_GANG_1_BLOCKED_8, + SFX_HAITIAN_GANG_1_BLOCKED_9, + SFX_HAITIAN_GANG_1_BUMP_1, + SFX_HAITIAN_GANG_1_BUMP_2, + SFX_HAITIAN_GANG_1_BUMP_3, + SFX_HAITIAN_GANG_1_BUMP_4, + SFX_HAITIAN_GANG_1_BUMP_5, + SFX_HAITIAN_GANG_1_BUMP_6, + SFX_HAITIAN_GANG_1_BUMP_7, + SFX_HAITIAN_GANG_1_BUMP_8, + SFX_HAITIAN_GANG_1_BUMP_9, + SFX_HAITIAN_GANG_1_BUMP_10, + SFX_HAITIAN_GANG_1_BUMP_11, + SFX_HAITIAN_GANG_1_BUMP_12, + SFX_HAITIAN_GANG_1_CAR_CRASH_1, + SFX_HAITIAN_GANG_1_CAR_CRASH_2, + SFX_HAITIAN_GANG_1_CAR_CRASH_3, + SFX_HAITIAN_GANG_1_CAR_CRASH_4, + SFX_HAITIAN_GANG_1_CAR_CRASH_5, + SFX_HAITIAN_GANG_1_CAR_CRASH_6, + SFX_HAITIAN_GANG_1_CAR_CRASH_7, + SFX_HAITIAN_GANG_1_CAR_CRASH_8, + SFX_HAITIAN_GANG_1_CAR_CRASH_9, + SFX_HAITIAN_GANG_1_CHAT_1, + SFX_HAITIAN_GANG_1_CHAT_2, + SFX_HAITIAN_GANG_1_CHAT_3, + SFX_HAITIAN_GANG_1_CHAT_4, + SFX_HAITIAN_GANG_1_CHAT_5, + SFX_HAITIAN_GANG_1_CHAT_6, + SFX_HAITIAN_GANG_1_CHAT_7, + SFX_HAITIAN_GANG_1_CHAT_8, + SFX_HAITIAN_GANG_1_CHAT_9, + SFX_HAITIAN_GANG_1_CHAT_10, + SFX_HAITIAN_GANG_1_CHAT_11, + SFX_HAITIAN_GANG_1_CHAT_12, + SFX_HAITIAN_GANG_1_CHAT_13, + SFX_HAITIAN_GANG_1_CHAT_14, + SFX_HAITIAN_GANG_1_DODGE_1, + SFX_HAITIAN_GANG_1_DODGE_2, + SFX_HAITIAN_GANG_1_DODGE_3, + SFX_HAITIAN_GANG_1_DODGE_4, + SFX_HAITIAN_GANG_1_DODGE_5, + SFX_HAITIAN_GANG_1_DODGE_6, + SFX_HAITIAN_GANG_1_DODGE_7, + SFX_HAITIAN_GANG_1_DODGE_8, + SFX_HAITIAN_GANG_1_DODGE_9, + SFX_HAITIAN_GANG_1_DODGE_10, + SFX_HAITIAN_GANG_1_EYEING_1, + SFX_HAITIAN_GANG_1_EYEING_2, + SFX_HAITIAN_GANG_1_FIGHT_1, + SFX_HAITIAN_GANG_1_FIGHT_2, + SFX_HAITIAN_GANG_1_FIGHT_3, + SFX_HAITIAN_GANG_1_FIGHT_4, + SFX_HAITIAN_GANG_1_FIGHT_5, + SFX_HAITIAN_GANG_1_FIGHT_6, + SFX_HAITIAN_GANG_1_FIGHT_7, + SFX_HAITIAN_GANG_1_FIGHT_8, + SFX_HAITIAN_GANG_1_FIGHT_9, + SFX_HAITIAN_GANG_1_FIGHT_10, + SFX_HAITIAN_GANG_1_GENERIC_CRASH_1, + SFX_HAITIAN_GANG_1_GENERIC_CRASH_2, + SFX_HAITIAN_GANG_1_GENERIC_CRASH_3, + SFX_HAITIAN_GANG_1_GENERIC_CRASH_4, + SFX_HAITIAN_GANG_1_GENERIC_CRASH_5, + SFX_HAITIAN_GANG_1_GENERIC_CRASH_6, + SFX_HAITIAN_GANG_1_GENERIC_CRASH_7, + SFX_HAITIAN_GANG_1_GENERIC_CRASH_8, + SFX_HAITIAN_GANG_1_GENERIC_CRASH_9, + SFX_HAITIAN_GANG_1_GUN_COOL_1, + SFX_HAITIAN_GANG_1_GUN_COOL_2, + SFX_HAITIAN_GANG_1_GUN_COOL_3, + SFX_HAITIAN_GANG_1_GUN_COOL_4, + SFX_HAITIAN_GANG_1_GUN_COOL_5, + SFX_HAITIAN_GANG_1_JACKED_1, + SFX_HAITIAN_GANG_1_JACKED_2, + SFX_HAITIAN_GANG_1_JACKED_3, + SFX_HAITIAN_GANG_1_JACKED_4, + SFX_HAITIAN_GANG_1_JACKED_5, + SFX_HAITIAN_GANG_1_JACKED_6, + SFX_HAITIAN_GANG_1_JACKING_1, + SFX_HAITIAN_GANG_1_JACKING_2, + SFX_HAITIAN_GANG_1_JACKING_3, + SFX_HAITIAN_GANG_1_JACKING_4, + SFX_HAITIAN_GANG_1_LOST_1, + SFX_HAITIAN_GANG_1_LOST_2, + SFX_HAITIAN_GANG_1_LOST_3, + SFX_HAITIAN_GANG_1_LOST_4, + SFX_HAITIAN_GANG_1_MUGGED_1, + SFX_HAITIAN_GANG_1_MUGGED_2, + SFX_HAITIAN_GANG_1_MUGGED_3, + SFX_HAITIAN_GANG_1_SAVED_1, + SFX_HAITIAN_GANG_1_TAXI_1, + + + SFX_HAITIAN_GANG_2_BLOCKED_1, + SFX_HAITIAN_GANG_2_BLOCKED_2, + SFX_HAITIAN_GANG_2_BLOCKED_3, + SFX_HAITIAN_GANG_2_BLOCKED_4, + SFX_HAITIAN_GANG_2_BLOCKED_5, + SFX_HAITIAN_GANG_2_BLOCKED_6, + SFX_HAITIAN_GANG_2_BLOCKED_7, + SFX_HAITIAN_GANG_2_BLOCKED_8, + SFX_HAITIAN_GANG_2_BLOCKED_9, + SFX_HAITIAN_GANG_2_BUMP_1, + SFX_HAITIAN_GANG_2_BUMP_2, + SFX_HAITIAN_GANG_2_BUMP_3, + SFX_HAITIAN_GANG_2_BUMP_4, + SFX_HAITIAN_GANG_2_BUMP_5, + SFX_HAITIAN_GANG_2_BUMP_6, + SFX_HAITIAN_GANG_2_BUMP_7, + SFX_HAITIAN_GANG_2_BUMP_8, + SFX_HAITIAN_GANG_2_BUMP_9, + SFX_HAITIAN_GANG_2_BUMP_10, + SFX_HAITIAN_GANG_2_BUMP_11, + SFX_HAITIAN_GANG_2_BUMP_12, + SFX_HAITIAN_GANG_2_CAR_CRASH_1, + SFX_HAITIAN_GANG_2_CAR_CRASH_2, + SFX_HAITIAN_GANG_2_CAR_CRASH_3, + SFX_HAITIAN_GANG_2_CAR_CRASH_4, + SFX_HAITIAN_GANG_2_CAR_CRASH_5, + SFX_HAITIAN_GANG_2_CAR_CRASH_6, + SFX_HAITIAN_GANG_2_CAR_CRASH_7, + SFX_HAITIAN_GANG_2_CAR_CRASH_8, + SFX_HAITIAN_GANG_2_CAR_CRASH_9, + SFX_HAITIAN_GANG_2_CHAT_1, + SFX_HAITIAN_GANG_2_CHAT_2, + SFX_HAITIAN_GANG_2_CHAT_3, + SFX_HAITIAN_GANG_2_CHAT_4, + SFX_HAITIAN_GANG_2_CHAT_5, + SFX_HAITIAN_GANG_2_CHAT_6, + SFX_HAITIAN_GANG_2_CHAT_7, + SFX_HAITIAN_GANG_2_CHAT_8, + SFX_HAITIAN_GANG_2_CHAT_9, + SFX_HAITIAN_GANG_2_CHAT_10, + SFX_HAITIAN_GANG_2_CHAT_11, + SFX_HAITIAN_GANG_2_CHAT_12, + SFX_HAITIAN_GANG_2_CHAT_13, + SFX_HAITIAN_GANG_2_CHAT_14, + SFX_HAITIAN_GANG_2_DODGE_1, + SFX_HAITIAN_GANG_2_DODGE_2, + SFX_HAITIAN_GANG_2_DODGE_3, + SFX_HAITIAN_GANG_2_DODGE_4, + SFX_HAITIAN_GANG_2_DODGE_5, + SFX_HAITIAN_GANG_2_DODGE_6, + SFX_HAITIAN_GANG_2_DODGE_7, + SFX_HAITIAN_GANG_2_DODGE_8, + SFX_HAITIAN_GANG_2_DODGE_9, + SFX_HAITIAN_GANG_2_DODGE_10, + SFX_HAITIAN_GANG_2_EYEING_1, + SFX_HAITIAN_GANG_2_EYEING_2, + SFX_HAITIAN_GANG_2_FIGHT_1, + SFX_HAITIAN_GANG_2_FIGHT_2, + SFX_HAITIAN_GANG_2_FIGHT_3, + SFX_HAITIAN_GANG_2_FIGHT_4, + SFX_HAITIAN_GANG_2_FIGHT_5, + SFX_HAITIAN_GANG_2_FIGHT_6, + SFX_HAITIAN_GANG_2_FIGHT_7, + SFX_HAITIAN_GANG_2_FIGHT_8, + SFX_HAITIAN_GANG_2_FIGHT_9, + SFX_HAITIAN_GANG_2_FIGHT_10, + SFX_HAITIAN_GANG_2_GENERIC_CRASH_1, + SFX_HAITIAN_GANG_2_GENERIC_CRASH_2, + SFX_HAITIAN_GANG_2_GENERIC_CRASH_3, + SFX_HAITIAN_GANG_2_GENERIC_CRASH_4, + SFX_HAITIAN_GANG_2_GENERIC_CRASH_5, + SFX_HAITIAN_GANG_2_GENERIC_CRASH_6, + SFX_HAITIAN_GANG_2_GENERIC_CRASH_7, + SFX_HAITIAN_GANG_2_GENERIC_CRASH_8, + SFX_HAITIAN_GANG_2_GENERIC_CRASH_9, + SFX_HAITIAN_GANG_2_GUN_COOL_1, + SFX_HAITIAN_GANG_2_GUN_COOL_2, + SFX_HAITIAN_GANG_2_GUN_COOL_3, + SFX_HAITIAN_GANG_2_GUN_COOL_4, + SFX_HAITIAN_GANG_2_GUN_COOL_5, + SFX_HAITIAN_GANG_2_JACKED_1, + SFX_HAITIAN_GANG_2_JACKED_2, + SFX_HAITIAN_GANG_2_JACKED_3, + SFX_HAITIAN_GANG_2_JACKED_4, + SFX_HAITIAN_GANG_2_JACKED_5, + SFX_HAITIAN_GANG_2_JACKED_6, + SFX_HAITIAN_GANG_2_JACKING_1, + SFX_HAITIAN_GANG_2_JACKING_2, + SFX_HAITIAN_GANG_2_JACKING_3, + SFX_HAITIAN_GANG_2_JACKING_4, + SFX_HAITIAN_GANG_2_LOST_1, + SFX_HAITIAN_GANG_2_LOST_2, + SFX_HAITIAN_GANG_2_LOST_3, + SFX_HAITIAN_GANG_2_LOST_4, + SFX_HAITIAN_GANG_2_MUGGED_1, + SFX_HAITIAN_GANG_2_MUGGED_2, + SFX_HAITIAN_GANG_2_MUGGED_3, + SFX_HAITIAN_GANG_2_SAVED_1, + SFX_HAITIAN_GANG_2_TAXI_1, + + SFX_HAITIAN_GANG_3_BLOCKED_1, + SFX_HAITIAN_GANG_3_BLOCKED_2, + SFX_HAITIAN_GANG_3_BLOCKED_3, + SFX_HAITIAN_GANG_3_BLOCKED_4, + SFX_HAITIAN_GANG_3_BLOCKED_5, + SFX_HAITIAN_GANG_3_BLOCKED_6, + SFX_HAITIAN_GANG_3_BLOCKED_7, + SFX_HAITIAN_GANG_3_BLOCKED_8, + SFX_HAITIAN_GANG_3_BLOCKED_9, + SFX_HAITIAN_GANG_3_BUMP_1, + SFX_HAITIAN_GANG_3_BUMP_2, + SFX_HAITIAN_GANG_3_BUMP_3, + SFX_HAITIAN_GANG_3_BUMP_4, + SFX_HAITIAN_GANG_3_BUMP_5, + SFX_HAITIAN_GANG_3_BUMP_6, + SFX_HAITIAN_GANG_3_BUMP_7, + SFX_HAITIAN_GANG_3_BUMP_8, + SFX_HAITIAN_GANG_3_BUMP_9, + SFX_HAITIAN_GANG_3_BUMP_10, + SFX_HAITIAN_GANG_3_BUMP_11, + SFX_HAITIAN_GANG_3_BUMP_12, + SFX_HAITIAN_GANG_3_CAR_CRASH_1, + SFX_HAITIAN_GANG_3_CAR_CRASH_2, + SFX_HAITIAN_GANG_3_CAR_CRASH_3, + SFX_HAITIAN_GANG_3_CAR_CRASH_4, + SFX_HAITIAN_GANG_3_CAR_CRASH_5, + SFX_HAITIAN_GANG_3_CAR_CRASH_6, + SFX_HAITIAN_GANG_3_CAR_CRASH_7, + SFX_HAITIAN_GANG_3_CAR_CRASH_8, + SFX_HAITIAN_GANG_3_CAR_CRASH_9, + SFX_HAITIAN_GANG_3_CHAT_1, + SFX_HAITIAN_GANG_3_CHAT_2, + SFX_HAITIAN_GANG_3_CHAT_3, + SFX_HAITIAN_GANG_3_CHAT_4, + SFX_HAITIAN_GANG_3_CHAT_5, + SFX_HAITIAN_GANG_3_CHAT_6, + SFX_HAITIAN_GANG_3_CHAT_7, + SFX_HAITIAN_GANG_3_CHAT_8, + SFX_HAITIAN_GANG_3_CHAT_9, + SFX_HAITIAN_GANG_3_CHAT_10, + SFX_HAITIAN_GANG_3_CHAT_11, + SFX_HAITIAN_GANG_3_CHAT_12, + SFX_HAITIAN_GANG_3_CHAT_13, + SFX_HAITIAN_GANG_3_CHAT_14, + SFX_HAITIAN_GANG_3_DODGE_1, + SFX_HAITIAN_GANG_3_DODGE_2, + SFX_HAITIAN_GANG_3_DODGE_3, + SFX_HAITIAN_GANG_3_DODGE_4, + SFX_HAITIAN_GANG_3_DODGE_5, + SFX_HAITIAN_GANG_3_DODGE_6, + SFX_HAITIAN_GANG_3_DODGE_7, + SFX_HAITIAN_GANG_3_DODGE_8, + SFX_HAITIAN_GANG_3_DODGE_9, + SFX_HAITIAN_GANG_3_DODGE_10, + SFX_HAITIAN_GANG_3_EYEING_1, + SFX_HAITIAN_GANG_3_EYEING_2, + SFX_HAITIAN_GANG_3_FIGHT_1, + SFX_HAITIAN_GANG_3_FIGHT_2, + SFX_HAITIAN_GANG_3_FIGHT_3, + SFX_HAITIAN_GANG_3_FIGHT_4, + SFX_HAITIAN_GANG_3_FIGHT_5, + SFX_HAITIAN_GANG_3_FIGHT_6, + SFX_HAITIAN_GANG_3_FIGHT_7, + SFX_HAITIAN_GANG_3_FIGHT_8, + SFX_HAITIAN_GANG_3_FIGHT_9, + SFX_HAITIAN_GANG_3_FIGHT_10, + SFX_HAITIAN_GANG_3_GENERIC_CRASH_1, + SFX_HAITIAN_GANG_3_GENERIC_CRASH_2, + SFX_HAITIAN_GANG_3_GENERIC_CRASH_3, + SFX_HAITIAN_GANG_3_GENERIC_CRASH_4, + SFX_HAITIAN_GANG_3_GENERIC_CRASH_5, + SFX_HAITIAN_GANG_3_GENERIC_CRASH_6, + SFX_HAITIAN_GANG_3_GENERIC_CRASH_7, + SFX_HAITIAN_GANG_3_GENERIC_CRASH_8, + SFX_HAITIAN_GANG_3_GENERIC_CRASH_9, + SFX_HAITIAN_GANG_3_GUN_COOL_1, + SFX_HAITIAN_GANG_3_GUN_COOL_2, + SFX_HAITIAN_GANG_3_GUN_COOL_3, + SFX_HAITIAN_GANG_3_GUN_COOL_4, + SFX_HAITIAN_GANG_3_GUN_COOL_5, + SFX_HAITIAN_GANG_3_JACKED_1, + SFX_HAITIAN_GANG_3_JACKED_2, + SFX_HAITIAN_GANG_3_JACKED_3, + SFX_HAITIAN_GANG_3_JACKED_4, + SFX_HAITIAN_GANG_3_JACKED_5, + SFX_HAITIAN_GANG_3_JACKED_6, + SFX_HAITIAN_GANG_3_JACKING_1, + SFX_HAITIAN_GANG_3_JACKING_2, + SFX_HAITIAN_GANG_3_JACKING_3, + SFX_HAITIAN_GANG_3_JACKING_4, + SFX_HAITIAN_GANG_3_LOST_1, + SFX_HAITIAN_GANG_3_LOST_2, + SFX_HAITIAN_GANG_3_LOST_3, + SFX_HAITIAN_GANG_3_LOST_4, + SFX_HAITIAN_GANG_3_MUGGED_1, + SFX_HAITIAN_GANG_3_MUGGED_2, + SFX_HAITIAN_GANG_3_MUGGED_3, + SFX_HAITIAN_GANG_3_SAVED_1, + SFX_HAITIAN_GANG_3_TAXI_1, + + SFX_GENERIC_FEMALE_FIRE_1, + SFX_GENERIC_FEMALE_FIRE_2, + SFX_GENERIC_FEMALE_FIRE_3, + SFX_GENERIC_FEMALE_FIRE_4, + SFX_GENERIC_FEMALE_FIRE_5, + SFX_GENERIC_FEMALE_FIRE_6, + SFX_GENERIC_FEMALE_FIRE_7, + SFX_GENERIC_FEMALE_FIRE_8, + SFX_GENERIC_FEMALE_FIRE_9, + SFX_GENERIC_FEMALE_FIRE_10, + SFX_GENERIC_FEMALE_FIRE_11, + SFX_GENERIC_FEMALE_FIRE_12, + SFX_GENERIC_FEMALE_FIRE_13, + SFX_GENERIC_FEMALE_FIRE_14, + SFX_GENERIC_FEMALE_FIRE_15, + SFX_GENERIC_FEMALE_FIRE_16, + SFX_GENERIC_FEMALE_FIRE_17, + SFX_GENERIC_FEMALE_DEATH_1, + SFX_GENERIC_FEMALE_DEATH_2, + SFX_GENERIC_FEMALE_DEATH_3, + SFX_GENERIC_FEMALE_DEATH_4, + SFX_GENERIC_FEMALE_DEATH_5, + SFX_GENERIC_FEMALE_DEATH_6, + SFX_GENERIC_FEMALE_DEATH_7, + SFX_GENERIC_FEMALE_DEATH_8, + SFX_GENERIC_FEMALE_DEATH_9, + SFX_GENERIC_FEMALE_DEATH_10, + SFX_GENERIC_FEMALE_DEATH_11, + SFX_GENERIC_FEMALE_DEATH_12, + SFX_GENERIC_FEMALE_DEATH_13, + SFX_GENERIC_FEMALE_DEATH_14, + SFX_GENERIC_FEMALE_DEATH_15, + SFX_GENERIC_FEMALE_DEATH_16, + SFX_GENERIC_FEMALE_DEATH_17, + SFX_GENERIC_FEMALE_DEATH_18, + SFX_GENERIC_FEMALE_DEATH_19, + SFX_GENERIC_FEMALE_DEATH_20, + SFX_GENERIC_FEMALE_DEATH_21, + SFX_GENERIC_FEMALE_DEATH_22, + SFX_GENERIC_FEMALE_GRUNT_1, + SFX_GENERIC_FEMALE_GRUNT_2, + SFX_GENERIC_FEMALE_GRUNT_3, + SFX_GENERIC_FEMALE_GRUNT_4, + SFX_GENERIC_FEMALE_GRUNT_5, + SFX_GENERIC_FEMALE_GRUNT_6, + SFX_GENERIC_FEMALE_GRUNT_7, + SFX_GENERIC_FEMALE_GRUNT_8, + SFX_GENERIC_FEMALE_GRUNT_9, + SFX_GENERIC_FEMALE_GRUNT_10, + SFX_GENERIC_FEMALE_GRUNT_11, + SFX_GENERIC_FEMALE_GRUNT_12, + SFX_GENERIC_FEMALE_GRUNT_13, + SFX_GENERIC_FEMALE_GRUNT_14, + SFX_GENERIC_FEMALE_GRUNT_15, + SFX_GENERIC_FEMALE_GRUNT_16, + SFX_GENERIC_FEMALE_GRUNT_17, + SFX_GENERIC_FEMALE_GRUNT_18, + SFX_GENERIC_FEMALE_GRUNT_19, + SFX_GENERIC_FEMALE_GRUNT_20, + SFX_GENERIC_FEMALE_GRUNT_21, + SFX_GENERIC_FEMALE_GRUNT_22, + SFX_GENERIC_FEMALE_GRUNT_23, + SFX_GENERIC_FEMALE_GRUNT_24, + SFX_GENERIC_FEMALE_GRUNT_25, + SFX_GENERIC_FEMALE_GRUNT_26, + SFX_GENERIC_FEMALE_GRUNT_27, + SFX_GENERIC_FEMALE_GRUNT_28, + SFX_GENERIC_FEMALE_GRUNT_29, + SFX_GENERIC_FEMALE_GRUNT_30, + SFX_GENERIC_FEMALE_GRUNT_31, + SFX_GENERIC_FEMALE_GRUNT_32, + SFX_GENERIC_FEMALE_GRUNT_33, + SFX_GENERIC_FEMALE_PANIC_1, + SFX_GENERIC_FEMALE_PANIC_2, + SFX_GENERIC_FEMALE_PANIC_3, + SFX_GENERIC_FEMALE_PANIC_4, + SFX_GENERIC_FEMALE_PANIC_5, + SFX_GENERIC_FEMALE_PANIC_6, + SFX_GENERIC_FEMALE_PANIC_7, + SFX_GENERIC_FEMALE_PANIC_8, + SFX_GENERIC_FEMALE_PANIC_9, + SFX_GENERIC_FEMALE_PANIC_10, + SFX_GENERIC_FEMALE_PANIC_11, + SFX_GENERIC_FEMALE_PANIC_12, + SFX_GENERIC_FEMALE_PANIC_13, + SFX_GENERIC_FEMALE_PANIC_14, + SFX_GENERIC_FEMALE_PANIC_15, + SFX_GENERIC_FEMALE_PANIC_16, + SFX_GENERIC_FEMALE_PANIC_17, + SFX_GENERIC_FEMALE_PANIC_18, + SFX_GENERIC_FEMALE_PANIC_19, + SFX_GENERIC_FEMALE_PANIC_20, + SFX_GENERIC_FEMALE_PANIC_21, + SFX_GENERIC_FEMALE_PANIC_22, + SFX_GENERIC_FEMALE_PANIC_23, + SFX_GENERIC_FEMALE_PANIC_24, + SFX_GENERIC_FEMALE_PANIC_25, + SFX_GENERIC_FEMALE_PANIC_26, + SFX_GENERIC_FEMALE_PANIC_27, + + SFX_GENERIC_MALE_FIRE_1, + SFX_GENERIC_MALE_FIRE_2, + SFX_GENERIC_MALE_FIRE_3, + SFX_GENERIC_MALE_FIRE_4, + SFX_GENERIC_MALE_FIRE_5, + SFX_GENERIC_MALE_FIRE_6, + SFX_GENERIC_MALE_FIRE_7, + SFX_GENERIC_MALE_FIRE_8, + SFX_GENERIC_MALE_FIRE_9, + SFX_GENERIC_MALE_FIRE_10, + SFX_GENERIC_MALE_FIRE_11, + SFX_GENERIC_MALE_FIRE_12, + SFX_GENERIC_MALE_FIRE_13, + SFX_GENERIC_MALE_FIRE_14, + SFX_GENERIC_MALE_FIRE_15, + SFX_GENERIC_MALE_FIRE_16, + SFX_GENERIC_MALE_FIRE_17, + SFX_GENERIC_MALE_FIRE_18, + SFX_GENERIC_MALE_FIRE_19, + SFX_GENERIC_MALE_FIRE_20, + SFX_GENERIC_MALE_FIRE_21, + SFX_GENERIC_MALE_FIRE_22, + SFX_GENERIC_MALE_FIRE_23, + SFX_GENERIC_MALE_FIRE_24, + SFX_GENERIC_MALE_FIRE_25, + SFX_GENERIC_MALE_FIRE_26, + SFX_GENERIC_MALE_FIRE_27, + SFX_GENERIC_MALE_FIRE_28, + SFX_GENERIC_MALE_FIRE_29, + SFX_GENERIC_MALE_FIRE_30, + SFX_GENERIC_MALE_FIRE_31, + SFX_GENERIC_MALE_FIRE_32, + SFX_GENERIC_MALE_DEATH_1, + SFX_GENERIC_MALE_DEATH_2, + SFX_GENERIC_MALE_DEATH_3, + SFX_GENERIC_MALE_DEATH_4, + SFX_GENERIC_MALE_DEATH_5, + SFX_GENERIC_MALE_DEATH_6, + SFX_GENERIC_MALE_DEATH_7, + SFX_GENERIC_MALE_DEATH_8, + SFX_GENERIC_MALE_DEATH_9, + SFX_GENERIC_MALE_DEATH_10, + SFX_GENERIC_MALE_DEATH_11, + SFX_GENERIC_MALE_DEATH_12, + SFX_GENERIC_MALE_DEATH_13, + SFX_GENERIC_MALE_DEATH_14, + SFX_GENERIC_MALE_DEATH_15, + SFX_GENERIC_MALE_DEATH_16, + SFX_GENERIC_MALE_DEATH_17, + SFX_GENERIC_MALE_DEATH_18, + SFX_GENERIC_MALE_DEATH_19, + SFX_GENERIC_MALE_DEATH_20, + SFX_GENERIC_MALE_DEATH_21, + SFX_GENERIC_MALE_DEATH_22, + SFX_GENERIC_MALE_DEATH_23, + SFX_GENERIC_MALE_DEATH_24, + SFX_GENERIC_MALE_DEATH_25, + SFX_GENERIC_MALE_DEATH_26, + SFX_GENERIC_MALE_DEATH_27, + SFX_GENERIC_MALE_DEATH_28, + SFX_GENERIC_MALE_DEATH_29, + SFX_GENERIC_MALE_DEATH_30, + SFX_GENERIC_MALE_DEATH_31, + SFX_GENERIC_MALE_DEATH_32, + SFX_GENERIC_MALE_DEATH_33, + SFX_GENERIC_MALE_DEATH_34, + SFX_GENERIC_MALE_DEATH_35, + SFX_GENERIC_MALE_DEATH_36, + SFX_GENERIC_MALE_DEATH_37, + SFX_GENERIC_MALE_DEATH_38, + SFX_GENERIC_MALE_DEATH_39, + SFX_GENERIC_MALE_DEATH_40, + SFX_GENERIC_MALE_DEATH_41, + SFX_GENERIC_MALE_GRUNT_1, + SFX_GENERIC_MALE_GRUNT_2, + SFX_GENERIC_MALE_GRUNT_3, + SFX_GENERIC_MALE_GRUNT_4, + SFX_GENERIC_MALE_GRUNT_5, + SFX_GENERIC_MALE_GRUNT_6, + SFX_GENERIC_MALE_GRUNT_7, + SFX_GENERIC_MALE_GRUNT_8, + SFX_GENERIC_MALE_GRUNT_9, + SFX_GENERIC_MALE_GRUNT_10, + SFX_GENERIC_MALE_GRUNT_11, + SFX_GENERIC_MALE_GRUNT_12, + SFX_GENERIC_MALE_GRUNT_13, + SFX_GENERIC_MALE_GRUNT_14, + SFX_GENERIC_MALE_GRUNT_15, + SFX_GENERIC_MALE_GRUNT_16, + SFX_GENERIC_MALE_GRUNT_17, + SFX_GENERIC_MALE_GRUNT_18, + SFX_GENERIC_MALE_GRUNT_19, + SFX_GENERIC_MALE_GRUNT_20, + SFX_GENERIC_MALE_GRUNT_21, + SFX_GENERIC_MALE_GRUNT_22, + SFX_GENERIC_MALE_GRUNT_23, + SFX_GENERIC_MALE_GRUNT_24, + SFX_GENERIC_MALE_GRUNT_25, + SFX_GENERIC_MALE_GRUNT_26, + SFX_GENERIC_MALE_GRUNT_27, + SFX_GENERIC_MALE_GRUNT_28, + SFX_GENERIC_MALE_GRUNT_29, + SFX_GENERIC_MALE_GRUNT_30, + SFX_GENERIC_MALE_GRUNT_31, + SFX_GENERIC_MALE_GRUNT_32, + SFX_GENERIC_MALE_GRUNT_33, + SFX_GENERIC_MALE_GRUNT_34, + SFX_GENERIC_MALE_GRUNT_35, + SFX_GENERIC_MALE_GRUNT_36, + SFX_GENERIC_MALE_GRUNT_37, + SFX_GENERIC_MALE_GRUNT_38, + SFX_GENERIC_MALE_GRUNT_39, + SFX_GENERIC_MALE_GRUNT_40, + SFX_GENERIC_MALE_GRUNT_41, + SFX_GENERIC_MALE_PANIC_1, + SFX_GENERIC_MALE_PANIC_2, + SFX_GENERIC_MALE_PANIC_3, + SFX_GENERIC_MALE_PANIC_4, + SFX_GENERIC_MALE_PANIC_5, + SFX_GENERIC_MALE_PANIC_6, + SFX_GENERIC_MALE_PANIC_7, + SFX_GENERIC_MALE_PANIC_8, + SFX_GENERIC_MALE_PANIC_9, + SFX_GENERIC_MALE_PANIC_10, + SFX_GENERIC_MALE_PANIC_11, + SFX_GENERIC_MALE_PANIC_12, + SFX_GENERIC_MALE_PANIC_13, + SFX_GENERIC_MALE_PANIC_14, + SFX_GENERIC_MALE_PANIC_15, + SFX_GENERIC_MALE_PANIC_16, + SFX_GENERIC_MALE_PANIC_17, + SFX_GENERIC_MALE_PANIC_18, + SFX_GENERIC_MALE_PANIC_19, + SFX_GENERIC_MALE_PANIC_20, + SFX_GENERIC_MALE_PANIC_21, + SFX_GENERIC_MALE_PANIC_22, + SFX_GENERIC_MALE_PANIC_23, + SFX_GENERIC_MALE_PANIC_24, + SFX_GENERIC_MALE_PANIC_25, + SFX_GENERIC_MALE_PANIC_26, + SFX_GENERIC_MALE_PANIC_27, + SFX_GENERIC_MALE_PANIC_28, + SFX_GENERIC_MALE_PANIC_29, + SFX_GENERIC_MALE_PANIC_30, + SFX_GENERIC_MALE_PANIC_31, + SFX_GENERIC_MALE_PANIC_32, + SFX_GENERIC_MALE_PANIC_33, + SFX_GENERIC_MALE_PANIC_34, + SFX_GENERIC_MALE_PANIC_35, + + SFX_MEDIC_VOICE_1_FIGHT_1, + SFX_MEDIC_VOICE_1_FIGHT_2, + SFX_MEDIC_VOICE_1_FIGHT_3, + SFX_MEDIC_VOICE_1_FIGHT_4, + SFX_MEDIC_VOICE_1_FIGHT_5, + SFX_MEDIC_VOICE_1_FIGHT_6, + SFX_MEDIC_VOICE_1_GET_OUT_VAN_CHAT_1, + SFX_MEDIC_VOICE_1_GET_OUT_VAN_CHAT_2, + SFX_MEDIC_VOICE_1_GET_OUT_VAN_CHAT_3, + SFX_MEDIC_VOICE_1_GET_OUT_VAN_CHAT_4, + SFX_MEDIC_VOICE_1_GET_OUT_VAN_CHAT_5, + SFX_MEDIC_VOICE_1_GET_OUT_VAN_CHAT_6, + SFX_MEDIC_VOICE_1_GET_OUT_VAN_CHAT_7, + SFX_MEDIC_VOICE_1_GET_OUT_VAN_CHAT_8, + SFX_MEDIC_VOICE_1_GET_OUT_VAN_CHAT_9, + SFX_MEDIC_VOICE_1_GET_OUT_VAN_CHAT_10, + SFX_MEDIC_VOICE_1_AT_VICTIM_1, + SFX_MEDIC_VOICE_1_AT_VICTIM_2, + SFX_MEDIC_VOICE_1_AT_VICTIM_3, + SFX_MEDIC_VOICE_1_AT_VICTIM_4, + SFX_MEDIC_VOICE_1_AT_VICTIM_5, + SFX_MEDIC_VOICE_1_AT_VICTIM_6, + SFX_MEDIC_VOICE_1_AT_VICTIM_7, + SFX_MEDIC_VOICE_1_AT_VICTIM_8, + SFX_MEDIC_VOICE_1_AT_VICTIM_9, + SFX_MEDIC_VOICE_1_AT_VICTIM_10, + SFX_MEDIC_VOICE_1_AT_VICTIM_11, + SFX_MEDIC_VOICE_1_AT_VICTIM_12, + SFX_MEDIC_VOICE_1_AT_VICTIM_13, + SFX_MEDIC_VOICE_1_AT_VICTIM_14, + SFX_MEDIC_VOICE_1_AT_VICTIM_15, + SFX_MEDIC_VOICE_1_AT_VICTIM_16, + SFX_MEDIC_VOICE_1_AT_VICTIM_17, + + SFX_MEDIC_VOICE_2_FIGHT_1, + SFX_MEDIC_VOICE_2_FIGHT_2, + SFX_MEDIC_VOICE_2_FIGHT_3, + SFX_MEDIC_VOICE_2_FIGHT_4, + SFX_MEDIC_VOICE_2_FIGHT_5, + SFX_MEDIC_VOICE_2_FIGHT_6, + SFX_MEDIC_VOICE_2_GET_OUT_VAN_CHAT_1, + SFX_MEDIC_VOICE_2_GET_OUT_VAN_CHAT_2, + SFX_MEDIC_VOICE_2_GET_OUT_VAN_CHAT_3, + SFX_MEDIC_VOICE_2_GET_OUT_VAN_CHAT_4, + SFX_MEDIC_VOICE_2_GET_OUT_VAN_CHAT_5, + SFX_MEDIC_VOICE_2_GET_OUT_VAN_CHAT_6, + SFX_MEDIC_VOICE_2_GET_OUT_VAN_CHAT_7, + SFX_MEDIC_VOICE_2_GET_OUT_VAN_CHAT_8, + SFX_MEDIC_VOICE_2_GET_OUT_VAN_CHAT_9, + SFX_MEDIC_VOICE_2_GET_OUT_VAN_CHAT_10, + SFX_MEDIC_VOICE_2_AT_VICTIM_1, + SFX_MEDIC_VOICE_2_AT_VICTIM_2, + SFX_MEDIC_VOICE_2_AT_VICTIM_3, + SFX_MEDIC_VOICE_2_AT_VICTIM_4, + SFX_MEDIC_VOICE_2_AT_VICTIM_5, + SFX_MEDIC_VOICE_2_AT_VICTIM_6, + SFX_MEDIC_VOICE_2_AT_VICTIM_7, + SFX_MEDIC_VOICE_2_AT_VICTIM_8, + SFX_MEDIC_VOICE_2_AT_VICTIM_9, + SFX_MEDIC_VOICE_2_AT_VICTIM_10, + SFX_MEDIC_VOICE_2_AT_VICTIM_11, + SFX_MEDIC_VOICE_2_AT_VICTIM_12, + SFX_MEDIC_VOICE_2_AT_VICTIM_13, + SFX_MEDIC_VOICE_2_AT_VICTIM_14, + SFX_MEDIC_VOICE_2_AT_VICTIM_15, + SFX_MEDIC_VOICE_2_AT_VICTIM_16, + SFX_MEDIC_VOICE_2_AT_VICTIM_17, + + SFX_FBI_VOICE_1_GUNAIMEDAT3_1, + SFX_FBI_VOICE_1_GUNAIMEDAT3_2, + SFX_FBI_VOICE_1_GUNAIMEDAT3_3, + SFX_FBI_VOICE_1_GUNAIMEDAT3_4, + SFX_FBI_VOICE_1_CAR_CRASH_1, + SFX_FBI_VOICE_1_CAR_CRASH_2, + SFX_FBI_VOICE_1_CAR_CRASH_3, + SFX_FBI_VOICE_1_CAR_CRASH_4, + SFX_FBI_VOICE_1_GUNAIMEDAT2_1, + SFX_FBI_VOICE_1_COP_MANYCOPSAROUND_1, + SFX_FBI_VOICE_1_COP_MANYCOPSAROUND_2, + SFX_FBI_VOICE_1_COP_MANYCOPSAROUND_3, + SFX_FBI_VOICE_1_COP_TARGETING_1, + SFX_FBI_VOICE_1_COP_TARGETING_2, + SFX_FBI_VOICE_1_COP_TARGETING_3, + SFX_FBI_VOICE_1_COP_TARGETING_4, + SFX_FBI_VOICE_1_COP_TARGETING_5, + SFX_FBI_VOICE_1_COP_TARGETING_6, + + SFX_FBI_VOICE_2_GUNAIMEDAT3_1, + SFX_FBI_VOICE_2_GUNAIMEDAT3_2, + SFX_FBI_VOICE_2_GUNAIMEDAT3_3, + SFX_FBI_VOICE_2_GUNAIMEDAT3_4, + SFX_FBI_VOICE_2_CAR_CRASH_1, + SFX_FBI_VOICE_2_CAR_CRASH_2, + SFX_FBI_VOICE_2_CAR_CRASH_3, + SFX_FBI_VOICE_2_CAR_CRASH_4, + SFX_FBI_VOICE_2_GUNAIMEDAT2_1, + SFX_FBI_VOICE_2_COP_MANYCOPSAROUND_1, + SFX_FBI_VOICE_2_COP_MANYCOPSAROUND_2, + SFX_FBI_VOICE_2_COP_MANYCOPSAROUND_3, + SFX_FBI_VOICE_2_COP_TARGETING_1, + SFX_FBI_VOICE_2_COP_TARGETING_2, + SFX_FBI_VOICE_2_COP_TARGETING_3, + SFX_FBI_VOICE_2_COP_TARGETING_4, + SFX_FBI_VOICE_2_COP_TARGETING_5, + SFX_FBI_VOICE_2_COP_TARGETING_6, + + SFX_FBI_VOICE_3_GUNAIMEDAT3_1, + SFX_FBI_VOICE_3_GUNAIMEDAT3_2, + SFX_FBI_VOICE_3_GUNAIMEDAT3_3, + SFX_FBI_VOICE_3_GUNAIMEDAT3_4, + SFX_FBI_VOICE_3_CAR_CRASH_1, + SFX_FBI_VOICE_3_CAR_CRASH_2, + SFX_FBI_VOICE_3_CAR_CRASH_3, + SFX_FBI_VOICE_3_CAR_CRASH_4, + SFX_FBI_VOICE_3_GUNAIMEDAT2_1, + SFX_FBI_VOICE_3_COP_MANYCOPSAROUND_1, + SFX_FBI_VOICE_3_COP_MANYCOPSAROUND_2, + SFX_FBI_VOICE_3_COP_MANYCOPSAROUND_3, + SFX_FBI_VOICE_3_COP_TARGETING_1, + SFX_FBI_VOICE_3_COP_TARGETING_2, + SFX_FBI_VOICE_3_COP_TARGETING_3, + SFX_FBI_VOICE_3_COP_TARGETING_4, + SFX_FBI_VOICE_3_COP_TARGETING_5, + SFX_FBI_VOICE_3_COP_TARGETING_6, + + SFX_SWAT_VOICE_1_DODGE_1, + SFX_SWAT_VOICE_1_DODGE_2, + SFX_SWAT_VOICE_1_DODGE_3, + SFX_SWAT_VOICE_1_COP_HELIPILOTPHRASE_1, + SFX_SWAT_VOICE_1_COP_HELIPILOTPHRASE_2, + SFX_SWAT_VOICE_1_COP_HELIPILOTPHRASE_3, + SFX_SWAT_VOICE_1_COP_HELIPILOTPHRASE_4, + SFX_SWAT_VOICE_1_COP_HELIPILOTPHRASE_5, + SFX_SWAT_VOICE_1_COP_HELIPILOTPHRASE_6, + SFX_SWAT_VOICE_1_COP_HELIPILOTPHRASE_7, + SFX_SWAT_VOICE_1_COP_TARGETING_1, + SFX_SWAT_VOICE_1_COP_TARGETING_2, + SFX_SWAT_VOICE_1_COP_TARGETING_3, + SFX_SWAT_VOICE_1_COP_TARGETING_4, + + SFX_SWAT_VOICE_2_DODGE_1, + SFX_SWAT_VOICE_2_DODGE_2, + SFX_SWAT_VOICE_2_DODGE_3, + SFX_SWAT_VOICE_2_COP_HELIPILOTPHRASE_1, + SFX_SWAT_VOICE_2_COP_HELIPILOTPHRASE_2, + SFX_SWAT_VOICE_2_COP_HELIPILOTPHRASE_3, + SFX_SWAT_VOICE_2_COP_HELIPILOTPHRASE_4, + SFX_SWAT_VOICE_2_COP_HELIPILOTPHRASE_5, + SFX_SWAT_VOICE_2_COP_HELIPILOTPHRASE_6, + SFX_SWAT_VOICE_2_COP_HELIPILOTPHRASE_7, + SFX_SWAT_VOICE_2_COP_TARGETING_1, + SFX_SWAT_VOICE_2_COP_TARGETING_2, + SFX_SWAT_VOICE_2_COP_TARGETING_3, + SFX_SWAT_VOICE_2_COP_TARGETING_4, + + SFX_SWAT_VOICE_3_DODGE_1, + SFX_SWAT_VOICE_3_DODGE_2, + SFX_SWAT_VOICE_3_DODGE_3, + SFX_SWAT_VOICE_3_COP_HELIPILOTPHRASE_1, + SFX_SWAT_VOICE_3_COP_HELIPILOTPHRASE_2, + SFX_SWAT_VOICE_3_COP_HELIPILOTPHRASE_3, + SFX_SWAT_VOICE_3_COP_HELIPILOTPHRASE_4, + SFX_SWAT_VOICE_3_COP_HELIPILOTPHRASE_5, + SFX_SWAT_VOICE_3_COP_HELIPILOTPHRASE_6, + SFX_SWAT_VOICE_3_COP_HELIPILOTPHRASE_7, + SFX_SWAT_VOICE_3_COP_TARGETING_1, + SFX_SWAT_VOICE_3_COP_TARGETING_2, + SFX_SWAT_VOICE_3_COP_TARGETING_3, + SFX_SWAT_VOICE_3_COP_TARGETING_4, + + + SFX_WFYG1_BLOCKED_1, + SFX_WFYG1_BLOCKED_2, + SFX_WFYG1_BLOCKED_3, + SFX_WFYG1_BLOCKED_4, + SFX_WFYG1_BLOCKED_5, + SFX_WFYG1_BLOCKED_6, + SFX_WFYG1_BLOCKED_7, + SFX_WFYG1_BUMP_1, + SFX_WFYG1_BUMP_2, + SFX_WFYG1_BUMP_3, + SFX_WFYG1_BUMP_4, + SFX_WFYG1_BUMP_5, + SFX_WFYG1_BUMP_6, + SFX_WFYG1_BUMP_7, + SFX_WFYG1_BUMP_8, + SFX_WFYG1_BUMP_9, + SFX_WFYG1_BUMP_10, + SFX_WFYG1_BUMP_11, + SFX_WFYG1_CAR_CRASH_1, + SFX_WFYG1_CAR_CRASH_2, + SFX_WFYG1_CAR_CRASH_3, + SFX_WFYG1_CAR_CRASH_4, + SFX_WFYG1_CAR_CRASH_5, + SFX_WFYG1_CAR_CRASH_6, + SFX_WFYG1_CAR_CRASH_7, + SFX_WFYG1_CAR_CRASH_8, + SFX_WFYG1_CAR_CRASH_9, + SFX_WFYG1_CHAT_1, + SFX_WFYG1_CHAT_2, + SFX_WFYG1_CHAT_3, + SFX_WFYG1_CHAT_4, + SFX_WFYG1_CHAT_5, + SFX_WFYG1_CHAT_6, + SFX_WFYG1_CHAT_7, + SFX_WFYG1_CHAT_8, + SFX_WFYG1_CHAT_9, + SFX_WFYG1_CHAT_10, + SFX_WFYG1_DODGE_1, + SFX_WFYG1_DODGE_2, + SFX_WFYG1_DODGE_3, + SFX_WFYG1_DODGE_4, + SFX_WFYG1_DODGE_5, + SFX_WFYG1_DODGE_6, + SFX_WFYG1_DODGE_7, + SFX_WFYG1_DODGE_8, + SFX_WFYG1_DODGE_9, + SFX_WFYG1_EYEING_1, + SFX_WFYG1_EYEING_2, + SFX_WFYG1_FIGHT_1, + SFX_WFYG1_FIGHT_2, + SFX_WFYG1_FIGHT_3, + SFX_WFYG1_FIGHT_4, + SFX_WFYG1_GENERIC_CRASH_1, + SFX_WFYG1_GENERIC_CRASH_2, + SFX_WFYG1_GENERIC_CRASH_3, + SFX_WFYG1_GENERIC_CRASH_4, + SFX_WFYG1_GENERIC_CRASH_5, + SFX_WFYG1_GENERIC_CRASH_6, + SFX_WFYG1_GENERIC_CRASH_7, + SFX_WFYG1_GUN_COOL_1, + SFX_WFYG1_GUN_COOL_2, + SFX_WFYG1_GUN_COOL_3, + SFX_WFYG1_GUN_COOL_4, + SFX_WFYG1_GUN_COOL_5, + SFX_WFYG1_GUN_COOL_6, + SFX_WFYG1_JACKED_1, + SFX_WFYG1_JACKED_2, + SFX_WFYG1_JACKED_3, + SFX_WFYG1_JACKED_4, + SFX_WFYG1_JACKED_5, + SFX_WFYG1_LOST_1, + SFX_WFYG1_LOST_2, + SFX_WFYG1_LOST_3, + SFX_WFYG1_MUGGED_1, + SFX_WFYG1_MUGGED_2, + SFX_WFYG1_MUGGING_1, + SFX_WFYG1_MUGGING_2, + SFX_WFYG1_RUN_1, + SFX_WFYG1_RUN_2, + SFX_WFYG1_SAVED_1, + SFX_WFYG1_SHOCKED_1, + SFX_WFYG1_TAXI_1, + + SFX_WFYG2_BLOCKED_1, + SFX_WFYG2_BLOCKED_2, + SFX_WFYG2_BLOCKED_3, + SFX_WFYG2_BLOCKED_4, + SFX_WFYG2_BLOCKED_5, + SFX_WFYG2_BUMP_1, + SFX_WFYG2_BUMP_2, + SFX_WFYG2_BUMP_3, + SFX_WFYG2_BUMP_4, + SFX_WFYG2_BUMP_5, + SFX_WFYG2_BUMP_6, + SFX_WFYG2_BUMP_7, + SFX_WFYG2_BUMP_8, + SFX_WFYG2_BUMP_9, + SFX_WFYG2_BUMP_10, + SFX_WFYG2_BUMP_11, + SFX_WFYG2_CAR_CRASH_1, + SFX_WFYG2_CAR_CRASH_2, + SFX_WFYG2_CAR_CRASH_3, + SFX_WFYG2_CAR_CRASH_4, + SFX_WFYG2_CAR_CRASH_5, + SFX_WFYG2_CAR_CRASH_6, + SFX_WFYG2_CAR_CRASH_7, + SFX_WFYG2_CAR_CRASH_8, + SFX_WFYG2_CAR_CRASH_9, + SFX_WFYG2_CHAT_1, + SFX_WFYG2_CHAT_2, + SFX_WFYG2_CHAT_3, + SFX_WFYG2_CHAT_4, + SFX_WFYG2_CHAT_5, + SFX_WFYG2_CHAT_6, + SFX_WFYG2_CHAT_7, + SFX_WFYG2_CHAT_8, + SFX_WFYG2_CHAT_9, + SFX_WFYG2_DODGE_1, + SFX_WFYG2_DODGE_2, + SFX_WFYG2_DODGE_3, + SFX_WFYG2_DODGE_4, + SFX_WFYG2_DODGE_5, + SFX_WFYG2_DODGE_6, + SFX_WFYG2_DODGE_7, + SFX_WFYG2_DODGE_8, + SFX_WFYG2_EYEING_1, + SFX_WFYG2_EYEING_2, + SFX_WFYG2_EYEING_3, + SFX_WFYG2_EYEING_4, + SFX_WFYG2_FIGHT_1, + SFX_WFYG2_FIGHT_2, + SFX_WFYG2_FIGHT_3, + SFX_WFYG2_FIGHT_4, + SFX_WFYG2_FIGHT_5, + SFX_WFYG2_GENERIC_CRASH_1, + SFX_WFYG2_GENERIC_CRASH_2, + SFX_WFYG2_GENERIC_CRASH_3, + SFX_WFYG2_GENERIC_CRASH_4, + SFX_WFYG2_GENERIC_CRASH_5, + SFX_WFYG2_GENERIC_CRASH_6, + SFX_WFYG2_GENERIC_CRASH_7, + SFX_WFYG2_GUN_COOL_1, + SFX_WFYG2_GUN_COOL_2, + SFX_WFYG2_GUN_COOL_3, + SFX_WFYG2_JACKED_1, + SFX_WFYG2_JACKED_2, + SFX_WFYG2_JACKED_3, + SFX_WFYG2_JACKED_4, + SFX_WFYG2_JACKED_5, + SFX_WFYG2_LOST_1, + SFX_WFYG2_MUGGED_1, + SFX_WFYG2_MUGGED_2, + SFX_WFYG2_SHOCKED_1, + SFX_WFYG2_TAXI_1, + SFX_WFYG2_TAXI_2, // unused + + SFX_HMOCA_BLOCKED_1, + SFX_HMOCA_BLOCKED_2, + SFX_HMOCA_BLOCKED_3, + SFX_HMOCA_BLOCKED_4, + SFX_HMOCA_BLOCKED_5, + SFX_HMOCA_BLOCKED_6, + SFX_HMOCA_BLOCKED_7, + SFX_HMOCA_BLOCKED_8, + SFX_HMOCA_CAR_CRASH_1, + SFX_HMOCA_CAR_CRASH_2, + SFX_HMOCA_CAR_CRASH_3, + SFX_HMOCA_CAR_CRASH_4, + SFX_HMOCA_CAR_CRASH_5, + SFX_HMOCA_CAR_CRASH_6, + SFX_HMOCA_CAR_CRASH_7, + SFX_HMOCA_CAR_CRASH_8, + SFX_HMOCA_CHAT_1, + SFX_HMOCA_CHAT_2, + SFX_HMOCA_CHAT_3, + SFX_HMOCA_CHAT_4, + SFX_HMOCA_CHAT_5, + SFX_HMOCA_CHAT_6, + SFX_HMOCA_CHAT_7, + SFX_HMOCA_CHAT_8, + SFX_HMOCA_CHAT_9, + SFX_HMOCA_CHAT_10, + SFX_HMOCA_EYEING_1, + SFX_HMOCA_EYEING_2, + SFX_HMOCA_GUN_PANIC_1, + SFX_HMOCA_GUN_PANIC_2, + SFX_HMOCA_GUN_PANIC_3, + SFX_HMOCA_GUN_PANIC_4, + SFX_HMOCA_GUN_PANIC_5, + SFX_HMOCA_JACKED_1, + SFX_HMOCA_JACKED_2, + SFX_HMOCA_JACKED_3, + SFX_HMOCA_JACKED_4, + SFX_HMOCA_JACKED_5, + SFX_HMOCA_JACKED_6, + SFX_HMOCA_JACKED_7, + SFX_HMOCA_JACKED_8, + SFX_HMOCA_JACKED_9, + SFX_HMOCA_JACKED_10, + SFX_HMOCA_JACKING_1, + SFX_HMOCA_JACKING_2, + SFX_HMOCA_JACKING_3, + SFX_HMOCA_JACKING_4, + SFX_HMOCA_JACKING_5, + SFX_HMOCA_JACKING_6, + SFX_HMOCA_JACKING_7, + SFX_HMOCA_JACKING_8, + SFX_HMOCA_JACKING_9, + SFX_HMOCA_JACKING_10, + SFX_HMOCA_JACKING_11, + SFX_HMOCA_MUGGED_1, + SFX_HMOCA_MUGGED_2, + SFX_HMOCA_MUGGED_3, + SFX_HMOCA_MUGGED_4, + SFX_HMOCA_MUGGED_5, + SFX_HMOCA_MUGGED_6, + SFX_HMOCA_MUGGED_7, + SFX_HMOCA_RUN_1, + SFX_HMOCA_RUN_2, + SFX_HMOCA_TAXI_1, + + SFX_WFOSH_BUMP_1, + SFX_WFOSH_BUMP_2, + SFX_WFOSH_BUMP_3, + SFX_WFOSH_BUMP_4, + SFX_WFOSH_BUMP_5, + SFX_WFOSH_BUMP_6, + SFX_WFOSH_BUMP_7, + SFX_WFOSH_BUMP_8, + SFX_WFOSH_BUMP_9, + SFX_WFOSH_BUMP_10, + SFX_WFOSH_CHAT_1, + SFX_WFOSH_CHAT_2, + SFX_WFOSH_CHAT_3, + SFX_WFOSH_CHAT_4, + SFX_WFOSH_CHAT_5, + SFX_WFOSH_CHAT_6, + SFX_WFOSH_CHAT_7, + SFX_WFOSH_CHAT_8, + SFX_WFOSH_CHAT_9, + SFX_WFOSH_DODGE_1, + SFX_WFOSH_DODGE_2, + SFX_WFOSH_DODGE_3, + SFX_WFOSH_DODGE_4, + SFX_WFOSH_DODGE_5, + SFX_WFOSH_DODGE_6, + SFX_WFOSH_DODGE_7, + SFX_WFOSH_DODGE_8, + SFX_WFOSH_DODGE_9, + SFX_WFOSH_DODGE_10, + SFX_WFOSH_GUN_COOL_1, + SFX_WFOSH_GUN_COOL_2, + SFX_WFOSH_GUN_COOL_3, + SFX_WFOSH_GUN_COOL_4, + SFX_WFOSH_GUN_COOL_5, + SFX_WFOSH_GUN_COOL_6, + SFX_WFOSH_GUN_COOL_7, + SFX_WFOSH_GUN_COOL_8, + SFX_WFOSH_GUN_COOL_9, + SFX_WFOSH_GUN_COOL_10, + SFX_WFOSH_LOST_1, + SFX_WFOSH_LOST_2, + SFX_WFOSH_MUGGED_1, + SFX_WFOSH_MUGGED_2, + SFX_WFOSH_RUN_1, + SFX_WFOSH_RUN_2, + SFX_WFOSH_RUN_3, + SFX_WFOSH_RUN_4, + SFX_WFOSH_RUN_5, + SFX_WFOSH_RUN_6, + SFX_WFOSH_RUN_7, + SFX_WFOSH_RUN_8, + SFX_WFOSH_RUN_9, + SFX_WFOSH_SAVED_1, + SFX_WFOSH_SAVED_2, + SFX_WFOSH_SAVED_3, + SFX_WFOSH_SHOCKED_1, + SFX_WFOSH_SHOCKED_2, + SFX_WFOSH_SHOCKED_3, + SFX_WFOSH_SHOCKED_4, + SFX_WFOSH_SHOCKED_5, + SFX_WFOSH_TAXI_1, + SFX_WFYSK_BLOCKED_1, + SFX_WFYSK_BLOCKED_2, + SFX_WFYSK_BLOCKED_3, + SFX_WFYSK_BLOCKED_4, + SFX_WFYSK_BLOCKED_5, + SFX_WFYSK_BLOCKED_6, + SFX_WFYSK_BLOCKED_7, + SFX_WFYSK_BLOCKED_8, + SFX_WFYSK_BLOCKED_9, + SFX_WFYSK_BLOCKED_10, + SFX_WFYSK_BLOCKED_11, + SFX_WFYSK_BUMP_1, + SFX_WFYSK_BUMP_2, + SFX_WFYSK_BUMP_3, + SFX_WFYSK_BUMP_4, + SFX_WFYSK_BUMP_5, + SFX_WFYSK_BUMP_6, + SFX_WFYSK_BUMP_7, + SFX_WFYSK_BUMP_8, + SFX_WFYSK_BUMP_9, + SFX_WFYSK_BUMP_10, + SFX_WFYSK_BUMP_11, + SFX_WFYSK_BUMP_12, + SFX_WFYSK_BUMP_13, + SFX_WFYSK_BUMP_14, + SFX_WFYSK_BUMP_15, + SFX_WFYSK_BUMP_16, + SFX_WFYSK_BUMP_17, + SFX_WFYSK_BUMP_18, + SFX_WFYSK_DODGE_1, + SFX_WFYSK_DODGE_2, + SFX_WFYSK_DODGE_3, + SFX_WFYSK_DODGE_4, + SFX_WFYSK_DODGE_5, + SFX_WFYSK_DODGE_6, + SFX_WFYSK_DODGE_7, + SFX_WFYSK_DODGE_8, + SFX_WFYSK_DODGE_9, + SFX_WFYSK_FIGHT_1, + SFX_WFYSK_FIGHT_2, + SFX_WFYSK_FIGHT_3, + SFX_WFYSK_FIGHT_4, + SFX_WFYSK_FIGHT_5, + SFX_WFYSK_FIGHT_6, + SFX_WFYSK_FIGHT_7, + SFX_WFYSK_FIGHT_8, + SFX_WFYSK_FIGHT_9, + SFX_WFYSK_FIGHT_10, + SFX_WFYSK_FIGHT_11, + SFX_WFYSK_GUN_PANIC_1, + SFX_WFYSK_GUN_PANIC_2, + SFX_WFYSK_GUN_PANIC_3, + SFX_WFYSK_GUN_PANIC_4, + SFX_WFYSK_GUN_PANIC_5, + SFX_WFYSK_MUGGED_1, + SFX_WFYSK_MUGGED_2, + SFX_WFYSK_SAVED_1, + SFX_WFYSK_SAVED_2, + SFX_WFYSK_TAXI_1, + SFX_WMYLG_BUMP_1, + SFX_WMYLG_BUMP_2, + SFX_WMYLG_BUMP_3, + SFX_WMYLG_BUMP_4, + SFX_WMYLG_BUMP_5, + SFX_WMYLG_BUMP_6, + SFX_WMYLG_BUMP_7, + SFX_WMYLG_BUMP_8, + SFX_WMYLG_BUMP_9, + SFX_WMYLG_BUMP_10, + SFX_WMYLG_CHAT_1, + SFX_WMYLG_CHAT_2, + SFX_WMYLG_CHAT_3, + SFX_WMYLG_CHAT_4, + SFX_WMYLG_CHAT_5, + SFX_WMYLG_CHAT_6, + SFX_WMYLG_CHAT_7, + SFX_WMYLG_CHAT_8, + SFX_WMYLG_CHAT_9, + SFX_WMYLG_CHAT_10, + SFX_WMYLG_DODGE_1, + SFX_WMYLG_DODGE_2, + SFX_WMYLG_DODGE_3, + SFX_WMYLG_DODGE_4, + SFX_WMYLG_DODGE_5, + SFX_WMYLG_DODGE_6, + SFX_WMYLG_DODGE_7, + SFX_WMYLG_DODGE_8, + SFX_WMYLG_DODGE_9, + SFX_WMYLG_FIGHT_1, + SFX_WMYLG_FIGHT_2, + SFX_WMYLG_FIGHT_3, + SFX_WMYLG_FIGHT_4, + SFX_WMYLG_FIGHT_5, + SFX_WMYLG_FIGHT_6, + SFX_WMYLG_FIGHT_7, + SFX_WMYLG_GUN_COOL_1, + SFX_WMYLG_GUN_COOL_2, + SFX_WMYLG_GUN_COOL_3, + SFX_WMYLG_GUN_COOL_4, + SFX_WMYLG_GUN_COOL_5, + SFX_WMYLG_GUN_COOL_6, + SFX_WMYLG_SAVED_1, + SFX_WMYLG_TAXI_1, + + SFX_WMOBE_BLOCKED_1, + SFX_WMOBE_BLOCKED_2, + SFX_WMOBE_BLOCKED_3, + SFX_WMOBE_BLOCKED_4, + SFX_WMOBE_BLOCKED_5, + SFX_WMOBE_BLOCKED_6, + SFX_WMOBE_BUMP_1, + SFX_WMOBE_BUMP_2, + SFX_WMOBE_BUMP_3, + SFX_WMOBE_BUMP_4, + SFX_WMOBE_BUMP_5, + SFX_WMOBE_BUMP_6, + SFX_WMOBE_BUMP_7, + SFX_WMOBE_BUMP_8, + SFX_WMOBE_BUMP_9, + SFX_WMOBE_BUMP_10, + SFX_WMOBE_BUMP_11, + SFX_WMOBE_BUMP_12, + SFX_WMOBE_CAR_CRASH_1, + SFX_WMOBE_CAR_CRASH_2, + SFX_WMOBE_CAR_CRASH_3, + SFX_WMOBE_CAR_CRASH_4, + SFX_WMOBE_CAR_CRASH_5, + SFX_WMOBE_CAR_CRASH_6, + SFX_WMOBE_CAR_CRASH_7, + SFX_WMOBE_CAR_CRASH_8, + SFX_WMOBE_CHAT_1, + SFX_WMOBE_CHAT_2, + SFX_WMOBE_CHAT_3, + SFX_WMOBE_CHAT_4, + SFX_WMOBE_CHAT_5, + SFX_WMOBE_CHAT_6, + SFX_WMOBE_CHAT_7, + SFX_WMOBE_CHAT_8, + SFX_WMOBE_CHAT_9, + SFX_WMOBE_CHAT_10, + SFX_WMOBE_DODGE_1, + SFX_WMOBE_DODGE_2, + SFX_WMOBE_DODGE_3, + SFX_WMOBE_DODGE_4, + SFX_WMOBE_DODGE_5, + SFX_WMOBE_DODGE_6, + SFX_WMOBE_DODGE_7, + SFX_WMOBE_DODGE_8, + SFX_WMOBE_EYEING_1, + SFX_WMOBE_EYEING_2, + SFX_WMOBE_GENERIC_CRASH_1, + SFX_WMOBE_GENERIC_CRASH_2, + SFX_WMOBE_GENERIC_CRASH_3, + SFX_WMOBE_GENERIC_CRASH_4, + SFX_WMOBE_GENERIC_CRASH_5, + SFX_WMOBE_GENERIC_CRASH_6, + SFX_WMOBE_GENERIC_CRASH_7, + SFX_WMOBE_GUN_PANIC_1, + SFX_WMOBE_GUN_PANIC_2, + SFX_WMOBE_GUN_PANIC_3, + SFX_WMOBE_GUN_PANIC_4, + SFX_WMOBE_GUN_PANIC_5, + SFX_WMOBE_JACKED_1, + SFX_WMOBE_JACKED_2, + SFX_WMOBE_JACKED_3, + SFX_WMOBE_JACKED_4, + SFX_WMOBE_JACKED_5, + SFX_WMOBE_JACKED_6, + SFX_WMOBE_JACKED_7, + SFX_WMOBE_JACKED_8, + SFX_WMOBE_JACKING_1, + SFX_WMOBE_JACKING_2, + SFX_WMOBE_JACKING_3, + SFX_WMOBE_JACKING_4, + SFX_WMOBE_JEER_1, + SFX_WMOBE_JEER_2, + SFX_WMOBE_JEER_3, + SFX_WMOBE_JEER_4, + SFX_WMOBE_JEER_5, + SFX_WMOBE_JEER_6, + SFX_WMOBE_JEER_7, + SFX_WMOBE_JEER_8, + SFX_WMOBE_JEER_9, + SFX_WMOBE_JEER_10, + SFX_WMOBE_JEER_11, + SFX_WMOBE_JEER_12, + SFX_WMOBE_JEER_13, + SFX_WMOBE_JEER_14, + SFX_WMOBE_JEER_15, + SFX_WMOBE_JEER_16, + SFX_WMOBE_MUGGING_1, + SFX_WMOBE_MUGGING_2, + SFX_WMOBE_MUGGING_3, + SFX_WMOBE_MUGGING_4, + SFX_WMOBE_MUGGING_5, + SFX_WMOBE_MUGGING_6, + SFX_WMOBE_RUN_1, + SFX_WMOBE_RUN_2, + SFX_WMOBE_RUN_3, + SFX_WMOBE_RUN_4, + SFX_WMOBE_SAVED_1, + SFX_WMOBE_SAVED_2, + SFX_WMOBE_SHOCKED_1, + SFX_WMOBE_SHOCKED_2, + + + + SFX_WMYBU_BLOCKED_1, + SFX_WMYBU_BLOCKED_2, + SFX_WMYBU_BLOCKED_3, + SFX_WMYBU_BLOCKED_4, + SFX_WMYBU_BLOCKED_5, + SFX_WMYBU_BLOCKED_6, + SFX_WMYBU_BLOCKED_7, + SFX_WMYBU_BLOCKED_8, + SFX_WMYBU_BLOCKED_9, + SFX_WMYBU_BUMP_1, + SFX_WMYBU_BUMP_2, + SFX_WMYBU_BUMP_3, + SFX_WMYBU_BUMP_4, + SFX_WMYBU_BUMP_5, + SFX_WMYBU_BUMP_6, + SFX_WMYBU_BUMP_7, + SFX_WMYBU_BUMP_8, + SFX_WMYBU_BUMP_9, + SFX_WMYBU_BUMP_10, + SFX_WMYBU_BUMP_11, + SFX_WMYBU_CAR_CRASH_1, + SFX_WMYBU_CAR_CRASH_2, + SFX_WMYBU_CAR_CRASH_3, + SFX_WMYBU_CAR_CRASH_4, + SFX_WMYBU_CAR_CRASH_5, + SFX_WMYBU_CAR_CRASH_6, + SFX_WMYBU_CAR_CRASH_7, + SFX_WMYBU_CAR_CRASH_8, + SFX_WMYBU_CAR_CRASH_9, + SFX_WMYBU_CHAT_1, + SFX_WMYBU_CHAT_2, + SFX_WMYBU_CHAT_3, + SFX_WMYBU_CHAT_4, + SFX_WMYBU_CHAT_5, + SFX_WMYBU_CHAT_6, + SFX_WMYBU_CHAT_7, + SFX_WMYBU_CHAT_8, + SFX_WMYBU_CHAT_9, + SFX_WMYBU_CHAT_10, + SFX_WMYBU_DODGE_1, + SFX_WMYBU_DODGE_2, + SFX_WMYBU_DODGE_3, + SFX_WMYBU_DODGE_4, + SFX_WMYBU_DODGE_5, + SFX_WMYBU_DODGE_6, + SFX_WMYBU_DODGE_7, + SFX_WMYBU_DODGE_8, + SFX_WMYBU_DODGE_9, + SFX_WMYBU_DODGE_10, + SFX_WMYBU_EYEING_1, + SFX_WMYBU_EYEING_2, + SFX_WMYBU_GENERIC_CRASH_1, + SFX_WMYBU_GENERIC_CRASH_2, + SFX_WMYBU_GENERIC_CRASH_3, + SFX_WMYBU_GENERIC_CRASH_4, + SFX_WMYBU_GENERIC_CRASH_5, + SFX_WMYBU_GUN_PANIC_1, + SFX_WMYBU_GUN_PANIC_2, + SFX_WMYBU_GUN_PANIC_3, + SFX_WMYBU_GUN_PANIC_4, + SFX_WMYBU_GUN_PANIC_5, + SFX_WMYBU_GUN_PANIC_6, + SFX_WMYBU_INNOCENT_1, + SFX_WMYBU_INNOCENT_2, + SFX_WMYBU_JACKED_1, + SFX_WMYBU_JACKED_2, + SFX_WMYBU_JACKED_3, + SFX_WMYBU_JACKED_4, + SFX_WMYBU_JACKED_5, + SFX_WMYBU_LOST_1, + SFX_WMYBU_LOST_2, + SFX_WMYBU_LOST_3, + SFX_WMYBU_LOST_4, + SFX_WMYBU_LOST_5, + SFX_WMYBU_MUGGED_1, + SFX_WMYBU_RUN_1, + SFX_WMYBU_RUN_2, + SFX_WMYBU_RUN_3, + SFX_WMYBU_SAVED_1, + SFX_WMYBU_SAVED_2, + SFX_WMYBU_SHOCKED_1, + SFX_WMYBU_SHOCKED_2, + SFX_WMYBU_SHOCKED_3, + SFX_WMYBU_SHOCKED_4, + SFX_WMYBU_SHOCKED_5, + SFX_WMYBU_TAXI_1, + SFX_WMYBU_TAXI_2, + + SFX_WMYST_BLOCKED_1, + SFX_WMYST_BLOCKED_2, + SFX_WMYST_BLOCKED_3, + SFX_WMYST_BLOCKED_4, + SFX_WMYST_BLOCKED_5, + SFX_WMYST_BLOCKED_6, + SFX_WMYST_BLOCKED_7, + SFX_WMYST_BLOCKED_8, + SFX_WMYST_BUMP_1, + SFX_WMYST_BUMP_2, + SFX_WMYST_BUMP_3, + SFX_WMYST_BUMP_4, + SFX_WMYST_BUMP_5, + SFX_WMYST_BUMP_6, + SFX_WMYST_BUMP_7, + SFX_WMYST_BUMP_8, + SFX_WMYST_BUMP_9, + SFX_WMYST_BUMP_10, + SFX_WMYST_BUMP_11, + SFX_WMYST_CAR_CRASH_1, + SFX_WMYST_CAR_CRASH_2, + SFX_WMYST_CAR_CRASH_3, + SFX_WMYST_CAR_CRASH_4, + SFX_WMYST_CAR_CRASH_5, + SFX_WMYST_CAR_CRASH_6, + SFX_WMYST_CAR_CRASH_7, + SFX_WMYST_CAR_CRASH_8, + SFX_WMYST_CHAT_1, + SFX_WMYST_CHAT_2, + SFX_WMYST_CHAT_3, + SFX_WMYST_CHAT_4, + SFX_WMYST_CHAT_5, + SFX_WMYST_CHAT_6, + SFX_WMYST_CHAT_7, + SFX_WMYST_CHAT_8, + SFX_WMYST_CHAT_9, + SFX_WMYST_CHAT_10, + SFX_WMYST_DODGE_1, + SFX_WMYST_DODGE_2, + SFX_WMYST_DODGE_3, + SFX_WMYST_DODGE_4, + SFX_WMYST_DODGE_5, + SFX_WMYST_DODGE_6, + SFX_WMYST_DODGE_7, + SFX_WMYST_DODGE_8, + SFX_WMYST_DODGE_9, + SFX_WMYST_DODGE_10, + SFX_WMYST_EYEING_1, + SFX_WMYST_EYEING_2, + SFX_WMYST_GENERIC_CRASH_1, + SFX_WMYST_GENERIC_CRASH_2, + SFX_WMYST_GENERIC_CRASH_3, + SFX_WMYST_GENERIC_CRASH_4, + SFX_WMYST_GENERIC_CRASH_5, + SFX_WMYST_GUN_PANIC_1, + SFX_WMYST_GUN_PANIC_2, + SFX_WMYST_GUN_PANIC_3, + SFX_WMYST_GUN_PANIC_4, + SFX_WMYST_GUN_PANIC_5, + SFX_WMYST_INNOCENT_1, + SFX_WMYST_INNOCENT_2, + SFX_WMYST_INNOCENT_3, + SFX_WMYST_JACKED_1, + SFX_WMYST_JACKED_2, + SFX_WMYST_JACKED_3, + SFX_WMYST_JACKED_4, + SFX_WMYST_JACKED_5, + SFX_WMYST_LOST_1, + SFX_WMYST_LOST_2, + SFX_WMYST_MUGGED_1, + SFX_WMYST_MUGGING_1, + SFX_WMYST_MUGGING_2, + SFX_WMYST_MUGGING_3, + SFX_WMYST_MUGGING_4, + SFX_WMYST_MUGGING_5, + SFX_WMYST_RUN_1, + SFX_WMYST_RUN_2, + SFX_WMYST_RUN_3, + SFX_WMYST_RUN_4, + SFX_WMYST_RUN_5, + SFX_WMYST_RUN_6, + SFX_WMYST_RUN_7, + SFX_WMYST_SAVED_1, + SFX_WMYST_TAXI_1, + SFX_WMYST_TAXI_2, + + SFX_BMYPI_BLOCKED_1, + SFX_BMYPI_BLOCKED_2, + SFX_BMYPI_BLOCKED_3, + SFX_BMYPI_BLOCKED_4, + SFX_BMYPI_BLOCKED_5, + SFX_BMYPI_BLOCKED_6, + SFX_BMYPI_BUMP_1, + SFX_BMYPI_BUMP_2, + SFX_BMYPI_BUMP_3, + SFX_BMYPI_BUMP_4, + SFX_BMYPI_BUMP_5, + SFX_BMYPI_BUMP_6, + SFX_BMYPI_BUMP_7, + SFX_BMYPI_BUMP_8, + SFX_BMYPI_BUMP_9, + SFX_BMYPI_CAR_CRASH_1, + SFX_BMYPI_CAR_CRASH_2, + SFX_BMYPI_CAR_CRASH_3, + SFX_BMYPI_CAR_CRASH_4, + SFX_BMYPI_CAR_CRASH_5, + SFX_BMYPI_DODGE_1, + SFX_BMYPI_DODGE_2, + SFX_BMYPI_DODGE_3, + SFX_BMYPI_DODGE_4, + SFX_BMYPI_DODGE_5, + SFX_BMYPI_DODGE_6, + SFX_BMYPI_DODGE_7, + SFX_BMYPI_DODGE_8, + SFX_BMYPI_DODGE_9, + SFX_BMYPI_DODGE_10, + SFX_BMYPI_EYEING_1, + SFX_BMYPI_EYEING_2, + SFX_BMYPI_EYEING_3, + SFX_BMYPI_EYEING_4, + SFX_BMYPI_FIGHT_1, + SFX_BMYPI_FIGHT_2, + SFX_BMYPI_FIGHT_3, + SFX_BMYPI_FIGHT_4, + SFX_BMYPI_FIGHT_5, + SFX_BMYPI_FIGHT_6, + SFX_BMYPI_FIGHT_7, + SFX_BMYPI_FIGHT_8, + SFX_BMYPI_GENERIC_CRASH_1, + SFX_BMYPI_GENERIC_CRASH_2, + SFX_BMYPI_GENERIC_CRASH_3, + SFX_BMYPI_GENERIC_CRASH_4, + SFX_BMYPI_GENERIC_CRASH_5, + SFX_BMYPI_GENERIC_CRASH_6, + SFX_BMYPI_GENERIC_CRASH_7, + SFX_BMYPI_GENERIC_CRASH_8, + SFX_BMYPI_GENERIC_CRASH_9, + SFX_BMYPI_GENERIC_CRASH_10, + SFX_BMYPI_GENERIC_CRASH_11, + SFX_BMYPI_GENERIC_CRASH_12, + SFX_BMYPI_GENERIC_CRASH_13, + SFX_BMYPI_GUN_COOL_1, + SFX_BMYPI_GUN_COOL_2, + SFX_BMYPI_GUN_COOL_3, + SFX_BMYPI_GUN_COOL_4, + SFX_BMYPI_GUN_COOL_5, + SFX_BMYPI_JACKED_1, + SFX_BMYPI_JACKED_2, + SFX_BMYPI_JACKED_3, + SFX_BMYPI_JACKED_4, + SFX_BMYPI_JACKED_5, + SFX_BMYPI_JACKED_6, + SFX_BMYPI_JACKING_1, + SFX_BMYPI_JACKING_2, + SFX_BMYPI_JACKING_3, + SFX_BMYPI_JACKING_4, + SFX_BMYPI_MUGGED_1, + SFX_BMYPI_SAVED_1, + SFX_BMYPI_TAXI_1, + SFX_BMYPI_TAXI_2, + + SFX_WFYPR_BUMP_1, + SFX_WFYPR_BUMP_2, + SFX_WFYPR_BUMP_3, + SFX_WFYPR_BUMP_4, + SFX_WFYPR_BUMP_5, + SFX_WFYPR_BUMP_6, + SFX_WFYPR_BUMP_7, + SFX_WFYPR_BUMP_8, + SFX_WFYPR_BUMP_9, + SFX_WFYPR_BUMP_10, + SFX_WFYPR_BUMP_11, + SFX_WFYPR_CHAT_1, + SFX_WFYPR_CHAT_2, + SFX_WFYPR_CHAT_3, + SFX_WFYPR_CHAT_4, + SFX_WFYPR_CHAT_5, + SFX_WFYPR_CHAT_6, + SFX_WFYPR_CHAT_7, + SFX_WFYPR_CHAT_8, + SFX_WFYPR_CHAT_9, + SFX_WFYPR_CHAT_10, + SFX_WFYPR_CHAT_11, + SFX_WFYPR_CHAT_12, + SFX_WFYPR_CHAT_13, + SFX_WFYPR_CHAT_14, + SFX_WFYPR_DODGE_1, + SFX_WFYPR_DODGE_2, + SFX_WFYPR_DODGE_3, + SFX_WFYPR_DODGE_4, + SFX_WFYPR_DODGE_5, + SFX_WFYPR_DODGE_6, + SFX_WFYPR_DODGE_7, + SFX_WFYPR_DODGE_8, + SFX_WFYPR_DODGE_9, + SFX_WFYPR_DODGE_10, + SFX_WFYPR_FIGHT_1, + SFX_WFYPR_FIGHT_2, + SFX_WFYPR_FIGHT_3, + SFX_WFYPR_FIGHT_4, + SFX_WFYPR_FIGHT_5, + SFX_WFYPR_FIGHT_6, + SFX_WFYPR_FIGHT_7, + SFX_WFYPR_FIGHT_8, + SFX_WFYPR_FIGHT_9, + SFX_WFYPR_FUCKING_1, + SFX_WFYPR_FUCKING_2, + SFX_WFYPR_FUCKING_3, + SFX_WFYPR_FUCKING_4, + SFX_WFYPR_FUCKING_5, + SFX_WFYPR_GUN_COOL_1, + SFX_WFYPR_GUN_COOL_2, + SFX_WFYPR_GUN_COOL_3, + SFX_WFYPR_GUN_COOL_4, + SFX_WFYPR_GUN_COOL_5, + SFX_WFYPR_GUN_COOL_6, + SFX_WFYPR_MUGGED_1, + SFX_WFYPR_MUGGED_2, + SFX_WFYPR_SAVED_1, + SFX_WFYPR_SOLICIT_1, + SFX_WFYPR_SOLICIT_2, + SFX_WFYPR_SOLICIT_3, + SFX_WFYPR_SOLICIT_4, + SFX_WFYPR_SOLICIT_5, + SFX_WFYPR_SOLICIT_6, + SFX_WFYPR_SOLICIT_7, + SFX_WFYPR_SOLICIT_8, + SFX_WFYPR_SOLICIT_9, + SFX_WFYPR_SOLICIT_10, + SFX_WFYPR_SOLICIT_11, + SFX_WFYPR_SOLICIT_12, + SFX_WFYPR_SOLICIT_13, + SFX_WFYPR_SOLICIT_14, + SFX_WFYPR_SOLICIT_15, + SFX_WFYPR_TAXI_1, + + SFX_WMYRI_BLOCKED_1, + SFX_WMYRI_BLOCKED_2, + SFX_WMYRI_BLOCKED_3, + SFX_WMYRI_BLOCKED_4, + SFX_WMYRI_BLOCKED_5, + SFX_WMYRI_BLOCKED_6, + SFX_WMYRI_BLOCKED_7, + SFX_WMYRI_BLOCKED_8, + SFX_WMYRI_BLOCKED_9, + SFX_WMYRI_BLOCKED_10, + SFX_WMYRI_BUMP_1, + SFX_WMYRI_BUMP_2, + SFX_WMYRI_BUMP_3, + SFX_WMYRI_BUMP_4, + SFX_WMYRI_BUMP_5, + SFX_WMYRI_BUMP_6, + SFX_WMYRI_BUMP_7, + SFX_WMYRI_BUMP_8, + SFX_WMYRI_CAR_CRASH_1, + SFX_WMYRI_CAR_CRASH_2, + SFX_WMYRI_CAR_CRASH_3, + SFX_WMYRI_CAR_CRASH_4, + SFX_WMYRI_CAR_CRASH_5, + SFX_WMYRI_CAR_CRASH_6, + SFX_WMYRI_CAR_CRASH_7, + SFX_WMYRI_CAR_CRASH_8, + SFX_WMYRI_CAR_CRASH_9, + SFX_WMYRI_CHAT_1, + SFX_WMYRI_CHAT_2, + SFX_WMYRI_CHAT_3, + SFX_WMYRI_CHAT_4, + SFX_WMYRI_CHAT_5, + SFX_WMYRI_CHAT_6, + SFX_WMYRI_CHAT_7, + SFX_WMYRI_CHAT_8, + SFX_WMYRI_CHAT_9, + SFX_WMYRI_CHAT_10, + SFX_WMYRI_DODGE_1, + SFX_WMYRI_DODGE_2, + SFX_WMYRI_DODGE_3, + SFX_WMYRI_DODGE_4, + SFX_WMYRI_DODGE_5, + SFX_WMYRI_DODGE_6, + SFX_WMYRI_DODGE_7, + SFX_WMYRI_DODGE_8, + SFX_WMYRI_DODGE_9, + SFX_WMYRI_EYEING_1, + SFX_WMYRI_EYEING_2, + SFX_WMYRI_EYEING_3, + SFX_WMYRI_GENERIC_CRASH_1, + SFX_WMYRI_GENERIC_CRASH_2, + SFX_WMYRI_GENERIC_CRASH_3, + SFX_WMYRI_GENERIC_CRASH_4, + SFX_WMYRI_GENERIC_CRASH_5, + SFX_WMYRI_GENERIC_CRASH_6, + SFX_WMYRI_GENERIC_CRASH_7, + SFX_WMYRI_GENERIC_CRASH_8, + SFX_WMYRI_GENERIC_CRASH_9, + SFX_WMYRI_GENERIC_CRASH_10, + SFX_WMYRI_GENERIC_CRASH_11, + SFX_WMYRI_GUN_PANIC_1, + SFX_WMYRI_GUN_PANIC_2, + SFX_WMYRI_GUN_PANIC_3, + SFX_WMYRI_GUN_PANIC_4, + SFX_WMYRI_GUN_PANIC_5, + SFX_WMYRI_GUN_PANIC_6, + SFX_WMYRI_GUN_PANIC_7, + SFX_WMYRI_GUN_PANIC_8, + SFX_WMYRI_JACKED_1, + SFX_WMYRI_JACKED_2, + SFX_WMYRI_JACKED_3, + SFX_WMYRI_JACKED_4, + SFX_WMYRI_JACKED_5, + SFX_WMYRI_JACKED_6, + SFX_WMYRI_JACKED_7, + SFX_WMYRI_JACKED_8, + SFX_WMYRI_LOST_1, + SFX_WMYRI_RUN_1, + SFX_WMYRI_RUN_2, + SFX_WMYRI_RUN_3, + SFX_WMYRI_RUN_4, + SFX_WMYRI_RUN_5, + SFX_WMYRI_SAVED_1, + SFX_WMYRI_SHOCKED_1, + SFX_WMYRI_SHOCKED_2, + SFX_WMYRI_SHOCKED_3, + SFX_WMYRI_SHOCKED_4, + SFX_WMYRI_TAXI_1, + SFX_WMYRI_TAXI_2, + + SFX_BMOST_BUMP_1, + SFX_BMOST_BUMP_2, + SFX_BMOST_BUMP_3, + SFX_BMOST_BUMP_4, + SFX_BMOST_BUMP_5, + SFX_BMOST_BUMP_6, + SFX_BMOST_BUMP_7, + SFX_BMOST_BUMP_8, + SFX_BMOST_BUMP_9, + SFX_BMOST_BUMP_10, + SFX_BMOST_BUMP_11, + SFX_BMOST_BUMP_12, + SFX_BMOST_BUMP_13, + SFX_BMOST_BUMP_14, + SFX_BMOST_BUMP_15, + SFX_BMOST_BUMP_16, + SFX_BMOST_BUMP_17, + SFX_BMOST_CAR_CRASH_1, + SFX_BMOST_CAR_CRASH_2, + SFX_BMOST_CAR_CRASH_3, + SFX_BMOST_CAR_CRASH_4, + SFX_BMOST_CAR_CRASH_5, + SFX_BMOST_CAR_CRASH_6, + SFX_BMOST_CAR_CRASH_7, + SFX_BMOST_CAR_CRASH_8, + SFX_BMOST_CHAT_1, + SFX_BMOST_CHAT_2, + SFX_BMOST_CHAT_3, + SFX_BMOST_CHAT_4, + SFX_BMOST_CHAT_5, + SFX_BMOST_CHAT_6, + SFX_BMOST_CHAT_7, + SFX_BMOST_CHAT_8, + SFX_BMOST_CHAT_9, + SFX_BMOST_CHAT_10, + SFX_BMOST_CHAT_11, + SFX_BMOST_CHAT_12, + SFX_BMOST_CHAT_13, + SFX_BMOST_CHAT_14, + SFX_BMOST_CHAT_15, + SFX_BMOST_CHAT_16, + SFX_BMOST_CHAT_17, + SFX_BMOST_CHAT_18, + SFX_BMOST_DODGE_1, + SFX_BMOST_DODGE_2, + SFX_BMOST_DODGE_3, + SFX_BMOST_DODGE_4, + SFX_BMOST_DODGE_5, + SFX_BMOST_DODGE_6, + SFX_BMOST_DODGE_7, + SFX_BMOST_DODGE_8, + SFX_BMOST_EYEING_1, + SFX_BMOST_EYEING_2, + SFX_BMOST_EYEING_3, + SFX_BMOST_EYEING_4, + SFX_BMOST_EYEING_5, + SFX_BMOST_EYEING_6, + SFX_BMOST_FIGHT_1, + SFX_BMOST_FIGHT_2, + SFX_BMOST_FIGHT_3, + SFX_BMOST_FIGHT_4, + SFX_BMOST_FIGHT_5, + SFX_BMOST_FIGHT_6, + SFX_BMOST_FIGHT_7, + SFX_BMOST_GENERIC_CRASH_1, + SFX_BMOST_GENERIC_CRASH_2, + SFX_BMOST_GENERIC_CRASH_3, + SFX_BMOST_GENERIC_CRASH_4, + SFX_BMOST_GENERIC_CRASH_5, + SFX_BMOST_GENERIC_CRASH_6, + SFX_BMOST_GENERIC_CRASH_7, + SFX_BMOST_GENERIC_CRASH_8, + SFX_BMOST_GENERIC_CRASH_9, + SFX_BMOST_GENERIC_CRASH_10, + SFX_BMOST_GENERIC_CRASH_11, + SFX_BMOST_GENERIC_CRASH_12, + SFX_BMOST_GENERIC_CRASH_13, + SFX_BMOST_GUN_PANIC_1, + SFX_BMOST_GUN_PANIC_2, + SFX_BMOST_GUN_PANIC_3, + SFX_BMOST_GUN_PANIC_4, + SFX_BMOST_GUN_PANIC_5, + SFX_BMOST_GUN_PANIC_6, + SFX_BMOST_GUN_PANIC_7, + SFX_BMOST_GUN_PANIC_8, + SFX_BMOST_GUN_PANIC_9, + SFX_BMOST_LOST_1, + SFX_BMOST_LOST_2, + SFX_BMOST_LOST_3, + SFX_BMOST_LOST_4, + SFX_BMOST_LOST_5, + SFX_BMOST_LOST_6, + SFX_BMOST_MUGGED_1, + SFX_BMOST_MUGGED_2, + SFX_BMOST_MUGGED_3, + SFX_BMOST_MUGGED_4, + SFX_BMOST_SAVED_1, + SFX_BMOST_TAXI_1, + + SFX_HFOST_BLOCKED_1, + SFX_HFOST_BLOCKED_2, + SFX_HFOST_BLOCKED_3, + SFX_HFOST_BLOCKED_4, + SFX_HFOST_BLOCKED_5, + SFX_HFOST_BLOCKED_6, + SFX_HFOST_BLOCKED_7, + SFX_HFOST_BLOCKED_8, + SFX_HFOST_BLOCKED_9, + SFX_HFOST_BUMP_1, + SFX_HFOST_BUMP_2, + SFX_HFOST_BUMP_3, + SFX_HFOST_BUMP_4, + SFX_HFOST_BUMP_5, + SFX_HFOST_BUMP_6, + SFX_HFOST_BUMP_7, + SFX_HFOST_BUMP_8, + SFX_HFOST_BUMP_9, + SFX_HFOST_BUMP_10, + SFX_HFOST_BUMP_11, + SFX_HFOST_BUMP_12, + SFX_HFOST_CAR_CRASH_1, + SFX_HFOST_CAR_CRASH_2, + SFX_HFOST_CAR_CRASH_3, + SFX_HFOST_CAR_CRASH_4, + SFX_HFOST_CAR_CRASH_5, + SFX_HFOST_CAR_CRASH_6, + SFX_HFOST_CAR_CRASH_7, + SFX_HFOST_CAR_CRASH_8, + SFX_HFOST_CHAT_1, + SFX_HFOST_CHAT_2, + SFX_HFOST_CHAT_3, + SFX_HFOST_CHAT_4, + SFX_HFOST_CHAT_5, + SFX_HFOST_CHAT_6, + SFX_HFOST_CHAT_7, + SFX_HFOST_CHAT_8, + SFX_HFOST_CHAT_9, + SFX_HFOST_CHAT_10, + SFX_HFOST_CHAT_11, + SFX_HFOST_DODGE_1, + SFX_HFOST_DODGE_2, + SFX_HFOST_DODGE_3, + SFX_HFOST_DODGE_4, + SFX_HFOST_DODGE_5, + SFX_HFOST_DODGE_6, + SFX_HFOST_DODGE_7, + SFX_HFOST_DODGE_8, + SFX_HFOST_DODGE_9, + SFX_HFOST_DODGE_10, + SFX_HFOST_FIGHT_1, + SFX_HFOST_FIGHT_2, + SFX_HFOST_FIGHT_3, + SFX_HFOST_FIGHT_4, + SFX_HFOST_FIGHT_5, + SFX_HFOST_FIGHT_6, + SFX_HFOST_FIGHT_7, + SFX_HFOST_FIGHT_8, + SFX_HFOST_GENERIC_CRASH_1, + SFX_HFOST_GENERIC_CRASH_2, + SFX_HFOST_GENERIC_CRASH_3, + SFX_HFOST_GENERIC_CRASH_4, + SFX_HFOST_GENERIC_CRASH_5, + SFX_HFOST_GENERIC_CRASH_6, + SFX_HFOST_GENERIC_CRASH_7, + SFX_HFOST_GENERIC_CRASH_8, + SFX_HFOST_GENERIC_CRASH_9, + SFX_HFOST_GENERIC_CRASH_10, + SFX_HFOST_GENERIC_CRASH_11, + SFX_HFOST_GUN_COOL_1, + SFX_HFOST_GUN_COOL_2, + SFX_HFOST_GUN_COOL_3, + SFX_HFOST_GUN_COOL_4, + SFX_HFOST_GUN_COOL_5, + SFX_HFOST_GUN_COOL_6, + SFX_HFOST_JACKED_1, + SFX_HFOST_JACKED_2, + SFX_HFOST_JACKED_3, + SFX_HFOST_JACKED_4, + SFX_HFOST_JACKED_5, + SFX_HFOST_JACKED_6, + SFX_HFOST_JACKED_7, + SFX_HFOST_JACKED_8, + SFX_HFOST_LOST_1, + SFX_HFOST_LOST_2, + SFX_HFOST_MUGGED_1, + SFX_HFOST_MUGGED_2, + SFX_HFOST_MUGGED_3, + SFX_HFOST_TAXI_1, + SFX_HFOST_TAXI_2, + + SFX_HMORI_BLOCKED_1, + SFX_HMORI_BLOCKED_2, + SFX_HMORI_BLOCKED_3, + SFX_HMORI_BLOCKED_4, + SFX_HMORI_BLOCKED_5, + SFX_HMORI_BLOCKED_6, + SFX_HMORI_BLOCKED_7, + SFX_HMORI_BLOCKED_8, + SFX_HMORI_BUMP_1, + SFX_HMORI_BUMP_2, + SFX_HMORI_BUMP_3, + SFX_HMORI_BUMP_4, + SFX_HMORI_BUMP_5, + SFX_HMORI_BUMP_6, + SFX_HMORI_BUMP_7, + SFX_HMORI_BUMP_8, + SFX_HMORI_BUMP_9, + SFX_HMORI_BUMP_10, + SFX_HMORI_BUMP_11, + SFX_HMORI_CAR_CRASH_1, + SFX_HMORI_CAR_CRASH_2, + SFX_HMORI_CAR_CRASH_3, + SFX_HMORI_CAR_CRASH_4, + SFX_HMORI_CAR_CRASH_5, + SFX_HMORI_CAR_CRASH_6, + SFX_HMORI_CHAT_1, + SFX_HMORI_CHAT_2, + SFX_HMORI_CHAT_3, + SFX_HMORI_CHAT_4, + SFX_HMORI_CHAT_5, + SFX_HMORI_CHAT_6, + SFX_HMORI_CHAT_7, + SFX_HMORI_CHAT_8, + SFX_HMORI_DODGE_1, + SFX_HMORI_DODGE_2, + SFX_HMORI_DODGE_3, + SFX_HMORI_DODGE_4, + SFX_HMORI_DODGE_5, + SFX_HMORI_DODGE_6, + SFX_HMORI_DODGE_7, + SFX_HMORI_GENERIC_CRASH_1, + SFX_HMORI_GENERIC_CRASH_2, + SFX_HMORI_GENERIC_CRASH_3, + SFX_HMORI_GENERIC_CRASH_4, + SFX_HMORI_GENERIC_CRASH_5, + SFX_HMORI_GENERIC_CRASH_6, + SFX_HMORI_GENERIC_CRASH_7, + SFX_HMORI_GENERIC_CRASH_8, + SFX_HMORI_GENERIC_CRASH_9, + SFX_HMORI_GENERIC_CRASH_10, + SFX_HMORI_GENERIC_CRASH_11, + SFX_HMORI_GUN_PANIC_1, + SFX_HMORI_GUN_PANIC_2, + SFX_HMORI_GUN_PANIC_3, + SFX_HMORI_GUN_PANIC_4, + SFX_HMORI_GUN_PANIC_5, + SFX_HMORI_JACKED_1, + SFX_HMORI_JACKED_2, + SFX_HMORI_JACKED_3, + SFX_HMORI_JACKED_4, + SFX_HMORI_JACKED_5, + SFX_HMORI_JACKED_6, + SFX_HMORI_JACKED_7, + SFX_HMORI_JACKED_8, + SFX_HMORI_LOST_1, + SFX_HMORI_LOST_2, + SFX_HMORI_MUGGED_1, + SFX_HMORI_MUGGED_2, + SFX_HMORI_MUGGED_3, + SFX_HMORI_RUN_1, + SFX_HMORI_RUN_2, + SFX_HMORI_RUN_3, + SFX_HMORI_RUN_4, + SFX_HMORI_RUN_5, + SFX_HMORI_RUN_6, + SFX_HMORI_TAXI_1, + SFX_HMORI_TAXI_2, + + SFX_HMOTR_BUMP_1, + SFX_HMOTR_BUMP_2, + SFX_HMOTR_BUMP_3, + SFX_HMOTR_BUMP_4, + SFX_HMOTR_BUMP_5, + SFX_HMOTR_BUMP_6, + SFX_HMOTR_BUMP_7, + SFX_HMOTR_BUMP_8, + SFX_HMOTR_CHAT_1, + SFX_HMOTR_CHAT_2, + SFX_HMOTR_CHAT_3, + SFX_HMOTR_CHAT_4, + SFX_HMOTR_CHAT_5, + SFX_HMOTR_CHAT_6, + SFX_HMOTR_CHAT_7, + SFX_HMOTR_CHAT_8, + SFX_HMOTR_CHAT_9, + SFX_HMOTR_DODGE_1, + SFX_HMOTR_DODGE_2, + SFX_HMOTR_DODGE_3, + SFX_HMOTR_DODGE_4, + SFX_HMOTR_DODGE_5, + SFX_HMOTR_DODGE_6, + SFX_HMOTR_DODGE_7, + SFX_HMOTR_DODGE_8, + SFX_HMOTR_DODGE_9, + SFX_HMOTR_DODGE_10, + SFX_HMOTR_DODGE_11, + SFX_HMOTR_FIGHT_1, + SFX_HMOTR_FIGHT_2, + SFX_HMOTR_FIGHT_3, + SFX_HMOTR_FIGHT_4, + SFX_HMOTR_FIGHT_5, + SFX_HMOTR_FIGHT_6, + SFX_HMOTR_FIGHT_7, + SFX_HMOTR_GUN_COOL_1, + SFX_HMOTR_GUN_COOL_2, + SFX_HMOTR_GUN_COOL_3, + SFX_HMOTR_GUN_COOL_4, + SFX_HMOTR_GUN_COOL_5, + SFX_HMOTR_GUN_COOL_6, + SFX_HMOTR_SAVED_1, + SFX_HMOTR_SAVED_2, + SFX_HMOTR_SHOCKED_1, + SFX_HMOTR_SHOCKED_2, + SFX_HMOTR_SHOCKED_3, + SFX_HMOTR_SOLICIT_1, + SFX_HMOTR_SOLICIT_2, + SFX_HMOTR_SOLICIT_3, + SFX_HMOTR_SOLICIT_4, + SFX_HMOTR_SOLICIT_5, + SFX_HMOTR_SOLICIT_6, + SFX_HMOTR_SOLICIT_7, + SFX_HMOTR_SOLICIT_8, + SFX_HMOTR_TAXI_1, + + SFX_HMYAP_BLOCKED_1, + SFX_HMYAP_BLOCKED_2, + SFX_HMYAP_BLOCKED_3, + SFX_HMYAP_BLOCKED_4, + SFX_HMYAP_BLOCKED_5, + SFX_HMYAP_BLOCKED_6, + SFX_HMYAP_BLOCKED_7, + SFX_HMYAP_BLOCKED_8, + SFX_HMYAP_BLOCKED_9, + SFX_HMYAP_BUMP_1, + SFX_HMYAP_BUMP_2, + SFX_HMYAP_BUMP_3, + SFX_HMYAP_BUMP_4, + SFX_HMYAP_BUMP_5, + SFX_HMYAP_BUMP_6, + SFX_HMYAP_BUMP_7, + SFX_HMYAP_BUMP_8, + SFX_HMYAP_BUMP_9, + SFX_HMYAP_BUMP_10, + SFX_HMYAP_BUMP_11, + SFX_HMYAP_CAR_CRASH_1, + SFX_HMYAP_CAR_CRASH_2, + SFX_HMYAP_CAR_CRASH_3, + SFX_HMYAP_CAR_CRASH_4, + SFX_HMYAP_CAR_CRASH_5, + SFX_HMYAP_CAR_CRASH_6, + SFX_HMYAP_CAR_CRASH_7, + SFX_HMYAP_CAR_CRASH_8, + SFX_HMYAP_CAR_CRASH_9, + SFX_HMYAP_CHAT_1, + SFX_HMYAP_CHAT_2, + SFX_HMYAP_CHAT_3, + SFX_HMYAP_CHAT_4, + SFX_HMYAP_CHAT_5, + SFX_HMYAP_CHAT_6, + SFX_HMYAP_CHAT_7, + SFX_HMYAP_CHAT_8, + SFX_HMYAP_CHAT_9, + SFX_HMYAP_DODGE_1, + SFX_HMYAP_DODGE_2, + SFX_HMYAP_DODGE_3, + SFX_HMYAP_DODGE_4, + SFX_HMYAP_DODGE_5, + SFX_HMYAP_DODGE_6, + SFX_HMYAP_DODGE_7, + SFX_HMYAP_DODGE_8, + SFX_HMYAP_DODGE_9, + SFX_HMYAP_EYEING_1, + SFX_HMYAP_EYEING_2, + SFX_HMYAP_EYEING_3, + SFX_HMYAP_GENERIC_CRASH_1, + SFX_HMYAP_GENERIC_CRASH_2, + SFX_HMYAP_GENERIC_CRASH_3, + SFX_HMYAP_GENERIC_CRASH_4, + SFX_HMYAP_GENERIC_CRASH_5, + SFX_HMYAP_GENERIC_CRASH_6, + SFX_HMYAP_GUN_PANIC_1, + SFX_HMYAP_GUN_PANIC_2, + SFX_HMYAP_GUN_PANIC_3, + SFX_HMYAP_GUN_PANIC_4, + SFX_HMYAP_GUN_PANIC_5, + SFX_HMYAP_GUN_PANIC_6, + SFX_HMYAP_GUN_PANIC_7, + SFX_HMYAP_JACKED_1, + SFX_HMYAP_JACKED_2, + SFX_HMYAP_JACKED_3, + SFX_HMYAP_JACKED_4, + SFX_HMYAP_JACKED_5, + SFX_HMYAP_JACKED_6, + SFX_HMYAP_JACKED_7, + SFX_HMYAP_JACKING_1, + SFX_HMYAP_JACKING_2, + SFX_HMYAP_JACKING_3, + SFX_HMYAP_JACKING_4, + SFX_HMYAP_LOST_1, + SFX_HMYAP_LOST_2, + SFX_HMYAP_MUGGED_1, + SFX_HMYAP_MUGGED_2, + SFX_HMYAP_RUN_1, + SFX_HMYAP_RUN_2, + SFX_HMYAP_RUN_3, + SFX_HMYAP_RUN_4, + SFX_HMYAP_RUN_5, + SFX_HMYAP_RUN_6, + SFX_HMYAP_SAVED_1, + SFX_HMYAP_SAVED_2, + SFX_HMYAP_TAXI_1, + SFX_HMYAP_TAXI_2, + + SFX_HFOTR_BUMP_1, + SFX_HFOTR_BUMP_2, + SFX_HFOTR_BUMP_3, + SFX_HFOTR_BUMP_4, + SFX_HFOTR_BUMP_5, + SFX_HFOTR_BUMP_6, + SFX_HFOTR_BUMP_7, + SFX_HFOTR_BUMP_8, + SFX_HFOTR_BUMP_9, + SFX_HFOTR_BUMP_10, + SFX_HFOTR_BUMP_11, + SFX_HFOTR_CHAT_1, + SFX_HFOTR_CHAT_2, + SFX_HFOTR_CHAT_3, + SFX_HFOTR_CHAT_4, + SFX_HFOTR_CHAT_5, + SFX_HFOTR_CHAT_6, + SFX_HFOTR_CHAT_7, + SFX_HFOTR_CHAT_8, + SFX_HFOTR_CHAT_9, + SFX_HFOTR_CHAT_10, + SFX_HFOTR_CHAT_11, + SFX_HFOTR_CHAT_12, + SFX_HFOTR_DODGE_1, + SFX_HFOTR_DODGE_2, + SFX_HFOTR_DODGE_3, + SFX_HFOTR_DODGE_4, + SFX_HFOTR_DODGE_5, + SFX_HFOTR_DODGE_6, + SFX_HFOTR_DODGE_7, + SFX_HFOTR_DODGE_8, + SFX_HFOTR_FIGHT_1, + SFX_HFOTR_FIGHT_2, + SFX_HFOTR_FIGHT_3, + SFX_HFOTR_FIGHT_4, + SFX_HFOTR_FIGHT_5, + SFX_HFOTR_FIGHT_6, + SFX_HFOTR_GUN_COOL_1, + SFX_HFOTR_GUN_COOL_2, + SFX_HFOTR_GUN_COOL_3, + SFX_HFOTR_GUN_COOL_4, + SFX_HFOTR_GUN_COOL_5, + SFX_HFOTR_MUGGED_1, + SFX_HFOTR_MUGGED_2, + SFX_HFOTR_SAVED_1, + SFX_HFOTR_SHOCKED_1, + SFX_HFOTR_SHOCKED_2, + SFX_HFOTR_TAXI_1, + SFX_HFOTR_TAXI_2, + + SFX_HMOBE_BLOCKED_1, + SFX_HMOBE_BLOCKED_2, + SFX_HMOBE_BLOCKED_3, + SFX_HMOBE_BLOCKED_4, + SFX_HMOBE_BLOCKED_5, + SFX_HMOBE_BLOCKED_6, + SFX_HMOBE_BLOCKED_7, + SFX_HMOBE_BLOCKED_8, + SFX_HMOBE_BLOCKED_9, + SFX_HMOBE_BLOCKED_10, + SFX_HMOBE_BUMP_1, + SFX_HMOBE_BUMP_2, + SFX_HMOBE_BUMP_3, + SFX_HMOBE_BUMP_4, + SFX_HMOBE_BUMP_5, + SFX_HMOBE_BUMP_6, + SFX_HMOBE_BUMP_7, + SFX_HMOBE_BUMP_8, + SFX_HMOBE_DODGE_1, + SFX_HMOBE_DODGE_2, + SFX_HMOBE_DODGE_3, + SFX_HMOBE_DODGE_4, + SFX_HMOBE_DODGE_5, + SFX_HMOBE_DODGE_6, + SFX_HMOBE_DODGE_7, + SFX_HMOBE_DODGE_8, + SFX_HMOBE_DODGE_9, + SFX_HMOBE_EYEING_1, + SFX_HMOBE_EYEING_2, + SFX_HMOBE_EYEING_3, + SFX_HMOBE_EYEING_4, + SFX_HMOBE_GUN_PANIC_1, + SFX_HMOBE_GUN_PANIC_2, + SFX_HMOBE_GUN_PANIC_3, + SFX_HMOBE_INNOCENT_1, + SFX_HMOBE_INNOCENT_2, + SFX_HMOBE_INNOCENT_3, + SFX_HMOBE_JACKED_1, + SFX_HMOBE_JACKED_2, + SFX_HMOBE_JACKED_3, + SFX_HMOBE_JACKED_4, + SFX_HMOBE_JACKED_5, + SFX_HMOBE_JACKED_6, + + SFX_HFYBU_BLOCKED_1, + SFX_HFYBU_BLOCKED_2, + SFX_HFYBU_BLOCKED_3, + SFX_HFYBU_BLOCKED_4, + SFX_HFYBU_BLOCKED_5, + SFX_HFYBU_BLOCKED_6, + SFX_HFYBU_BLOCKED_7, + SFX_HFYBU_BLOCKED_8, + SFX_HFYBU_BUMP_1, + SFX_HFYBU_BUMP_2, + SFX_HFYBU_BUMP_3, + SFX_HFYBU_BUMP_4, + SFX_HFYBU_BUMP_5, + SFX_HFYBU_BUMP_6, + SFX_HFYBU_BUMP_7, + SFX_HFYBU_BUMP_8, + SFX_HFYBU_BUMP_9, + SFX_HFYBU_BUMP_10, + SFX_HFYBU_BUMP_11, + SFX_HFYBU_CAR_CRASH_1, + SFX_HFYBU_CAR_CRASH_2, + SFX_HFYBU_CAR_CRASH_3, + SFX_HFYBU_CAR_CRASH_4, + SFX_HFYBU_CAR_CRASH_5, + SFX_HFYBU_CAR_CRASH_6, + SFX_HFYBU_CAR_CRASH_7, + SFX_HFYBU_CAR_CRASH_8, + SFX_HFYBU_DODGE_1, + SFX_HFYBU_DODGE_2, + SFX_HFYBU_DODGE_3, + SFX_HFYBU_DODGE_4, + SFX_HFYBU_DODGE_5, + SFX_HFYBU_DODGE_6, + SFX_HFYBU_DODGE_7, + SFX_HFYBU_DODGE_8, + SFX_HFYBU_DODGE_9, + SFX_HFYBU_DODGE_10, + SFX_HFYBU_FIGHT_1, + SFX_HFYBU_FIGHT_2, + SFX_HFYBU_FIGHT_3, + SFX_HFYBU_FIGHT_4, + SFX_HFYBU_FIGHT_5, + SFX_HFYBU_FIGHT_6, + SFX_HFYBU_FIGHT_7, + SFX_HFYBU_GENERIC_CRASH_1, + SFX_HFYBU_GENERIC_CRASH_2, + SFX_HFYBU_GENERIC_CRASH_3, + SFX_HFYBU_GENERIC_CRASH_4, + SFX_HFYBU_GENERIC_CRASH_5, + SFX_HFYBU_GENERIC_CRASH_6, + SFX_HFYBU_GENERIC_CRASH_7, + SFX_HFYBU_GENERIC_CRASH_8, + SFX_HFYBU_GENERIC_CRASH_9, + SFX_HFYBU_GENERIC_CRASH_10, + SFX_HFYBU_GENERIC_CRASH_11, + SFX_HFYBU_GENERIC_CRASH_12, + SFX_HFYBU_GUN_PANIC_1, + SFX_HFYBU_GUN_PANIC_2, + SFX_HFYBU_GUN_PANIC_3, + SFX_HFYBU_GUN_PANIC_4, + SFX_HFYBU_GUN_PANIC_5, + SFX_HFYBU_JACKED_1, + SFX_HFYBU_JACKED_2, + SFX_HFYBU_JACKED_3, + SFX_HFYBU_JACKED_4, + SFX_HFYBU_JACKED_5, + SFX_HFYBU_JACKED_6, + SFX_HFYBU_JACKING_1, + SFX_HFYBU_JACKING_2, + SFX_HFYBU_JACKING_3, + SFX_HFYBU_LOST_1, + SFX_HFYBU_LOST_2, + SFX_HFYBU_MUGGED_1, + SFX_HFYBU_MUGGED_2, + SFX_HFYBU_SAVED_1, + SFX_HFYBU_TAXI_1, + + SFX_HFYCG_BUMP_1, + SFX_HFYCG_BUMP_2, + SFX_HFYCG_BUMP_3, + SFX_HFYCG_BUMP_4, + SFX_HFYCG_BUMP_5, + SFX_HFYCG_BUMP_6, + SFX_HFYCG_BUMP_7, + SFX_HFYCG_BUMP_8, + SFX_HFYCG_BUMP_9, + SFX_HFYCG_DODGE_1, + SFX_HFYCG_DODGE_2, + SFX_HFYCG_DODGE_3, + SFX_HFYCG_DODGE_4, + SFX_HFYCG_DODGE_5, + SFX_HFYCG_DODGE_6, + SFX_HFYCG_DODGE_7, + SFX_HFYCG_DODGE_8, + SFX_HFYCG_GUN_PANIC_1, + SFX_HFYCG_GUN_PANIC_2, + SFX_HFYCG_GUN_PANIC_3, + SFX_HFYCG_GUN_PANIC_4, + SFX_HFYCG_GUN_PANIC_5, + SFX_HFYCG_MUGGED_1, + SFX_HFYCG_MUGGED_2, + SFX_HFYCG_RUN_1, + SFX_HFYCG_RUN_2, + SFX_HFYCG_RUN_3, + SFX_HFYCG_RUN_4, + SFX_HFYCG_SAVED_1, + SFX_HFYCG_SOLICIT_1, + SFX_HFYCG_SOLICIT_2, + SFX_HFYCG_SOLICIT_3, + SFX_HFYCG_SOLICIT_4, + SFX_HFYCG_SOLICIT_5, + SFX_HFYCG_SOLICIT_6, + SFX_HFYCG_SOLICIT_7, + SFX_HFYCG_SOLICIT_8, + SFX_HFYCG_SOLICIT_9, + SFX_HFYCG_SOLICIT_10, + SFX_HFYCG_SOLICIT_11, + SFX_HFYCG_SOLICIT_12, + SFX_HFYCG_SOLICIT_13, + SFX_HFYCG_SOLICIT_14, + SFX_HFYCG_TAXI_1, + + SFX_HMYBE_BUMP_1, + SFX_HMYBE_BUMP_2, + SFX_HMYBE_BUMP_3, + SFX_HMYBE_BUMP_4, + SFX_HMYBE_BUMP_5, + SFX_HMYBE_BUMP_6, + SFX_HMYBE_BUMP_7, + SFX_HMYBE_BUMP_8, + SFX_HMYBE_BUMP_9, + SFX_HMYBE_BUMP_10, + SFX_HMYBE_CAR_CRASH_1, + SFX_HMYBE_CAR_CRASH_2, + SFX_HMYBE_CAR_CRASH_3, + SFX_HMYBE_CAR_CRASH_4, + SFX_HMYBE_CAR_CRASH_5, + SFX_HMYBE_CAR_CRASH_6, + SFX_HMYBE_CAR_CRASH_7, + SFX_HMYBE_CHAT_1, + SFX_HMYBE_CHAT_2, + SFX_HMYBE_CHAT_3, + SFX_HMYBE_CHAT_4, + SFX_HMYBE_CHAT_5, + SFX_HMYBE_CHAT_6, + SFX_HMYBE_CHAT_7, + SFX_HMYBE_CHAT_8, + SFX_HMYBE_CHAT_9, + SFX_HMYBE_CHAT_10, + SFX_HMYBE_DODGE_1, + SFX_HMYBE_DODGE_2, + SFX_HMYBE_DODGE_3, + SFX_HMYBE_DODGE_4, + SFX_HMYBE_DODGE_5, + SFX_HMYBE_DODGE_6, + SFX_HMYBE_DODGE_7, + SFX_HMYBE_EYEING_1, + SFX_HMYBE_EYEING_2, + SFX_HMYBE_EYEING_3, + SFX_HMYBE_EYEING_4, + SFX_HMYBE_EYEING_5, + SFX_HMYBE_FIGHT_1, + SFX_HMYBE_FIGHT_2, + SFX_HMYBE_FIGHT_3, + SFX_HMYBE_FIGHT_4, + SFX_HMYBE_FIGHT_5, + SFX_HMYBE_FIGHT_6, + SFX_HMYBE_FIGHT_7, + SFX_HMYBE_FIGHT_8, + SFX_HMYBE_GENERIC_CRASH_1, + SFX_HMYBE_GENERIC_CRASH_2, + SFX_HMYBE_GENERIC_CRASH_3, + SFX_HMYBE_GENERIC_CRASH_4, + SFX_HMYBE_GENERIC_CRASH_5, + SFX_HMYBE_GENERIC_CRASH_6, + SFX_HMYBE_GENERIC_CRASH_7, + SFX_HMYBE_GENERIC_CRASH_8, + SFX_HMYBE_GENERIC_CRASH_9, + SFX_HMYBE_GENERIC_CRASH_10, + SFX_HMYBE_GUN_PANIC_1, + SFX_HMYBE_GUN_PANIC_2, + SFX_HMYBE_GUN_PANIC_3, + SFX_HMYBE_GUN_PANIC_4, + SFX_HMYBE_GUN_PANIC_5, + SFX_HMYBE_GUN_PANIC_6, + SFX_HMYBE_INNOCENT_1, + SFX_HMYBE_INNOCENT_2, + SFX_HMYBE_INNOCENT_3, + SFX_HMYBE_INNOCENT_4, + SFX_HMYBE_JACKED_1, + SFX_HMYBE_JACKED_2, + SFX_HMYBE_JACKED_3, + SFX_HMYBE_JACKED_4, + SFX_HMYBE_JACKED_5, + SFX_HMYBE_JACKED_6, + SFX_HMYBE_JACKED_7, + SFX_HMYBE_JACKED_8, + SFX_HMYBE_JACKED_9, + SFX_HMYBE_JACKED_10, + SFX_HMYBE_JACKED_11, + SFX_HMYBE_JACKED_12, + SFX_HMYBE_LOST_1, + SFX_HMYBE_LOST_2, + SFX_HMYBE_LOST_3, + SFX_HMYBE_SAVED_1, + SFX_HMYBE_SHOCKED_1, + SFX_HMYBE_SHOCKED_2, + SFX_HMYBE_TAXI_1, + + SFX_WMOGO_BUMP_1, + SFX_WMOGO_BUMP_2, + SFX_WMOGO_BUMP_3, + SFX_WMOGO_BUMP_4, + SFX_WMOGO_BUMP_5, + SFX_WMOGO_BUMP_6, + SFX_WMOGO_BUMP_7, + SFX_WMOGO_BUMP_8, + SFX_WMOGO_CAR_CRASH_1, + SFX_WMOGO_CAR_CRASH_2, + SFX_WMOGO_CAR_CRASH_3, + SFX_WMOGO_CAR_CRASH_4, + SFX_WMOGO_CAR_CRASH_5, + SFX_WMOGO_CAR_CRASH_6, + SFX_WMOGO_CAR_CRASH_7, + SFX_WMOGO_CAR_CRASH_8, + SFX_WMOGO_CAR_CRASH_9, + SFX_WMOGO_CHAT_1, + SFX_WMOGO_CHAT_2, + SFX_WMOGO_CHAT_3, + SFX_WMOGO_CHAT_4, + SFX_WMOGO_CHAT_5, + SFX_WMOGO_CHAT_6, + SFX_WMOGO_CHAT_7, + SFX_WMOGO_CHAT_8, + SFX_WMOGO_CHAT_9, + SFX_WMOGO_DODGE_1, + SFX_WMOGO_DODGE_2, + SFX_WMOGO_DODGE_3, + SFX_WMOGO_DODGE_4, + SFX_WMOGO_DODGE_5, + SFX_WMOGO_DODGE_6, + SFX_WMOGO_DODGE_7, + SFX_WMOGO_DODGE_8, + SFX_WMOGO_DODGE_9, + SFX_WMOGO_DODGE_10, + SFX_WMOGO_DODGE_11, + SFX_WMOGO_DODGE_12, + SFX_WMOGO_EYEING_1, + SFX_WMOGO_EYEING_2, + SFX_WMOGO_FIGHT_1, + SFX_WMOGO_FIGHT_2, + SFX_WMOGO_FIGHT_3, + SFX_WMOGO_FIGHT_4, + SFX_WMOGO_FIGHT_5, + SFX_WMOGO_FIGHT_6, + SFX_WMOGO_FIGHT_7, + SFX_WMOGO_FIGHT_8, + SFX_WMOGO_FIGHT_9, + SFX_WMOGO_FIGHT_10, + SFX_WMOGO_FIGHT_11, + SFX_WMOGO_FIGHT_12, + SFX_WMOGO_FIGHT_13, + SFX_WMOGO_GENERIC_CRASH_1, + SFX_WMOGO_GENERIC_CRASH_2, + SFX_WMOGO_GENERIC_CRASH_3, + SFX_WMOGO_GENERIC_CRASH_4, + SFX_WMOGO_GENERIC_CRASH_5, + SFX_WMOGO_GENERIC_CRASH_6, + SFX_WMOGO_GENERIC_CRASH_7, + SFX_WMOGO_GENERIC_CRASH_8, + SFX_WMOGO_GUN_PANIC_1, + SFX_WMOGO_GUN_PANIC_2, + SFX_WMOGO_GUN_PANIC_3, + SFX_WMOGO_GUN_PANIC_4, + SFX_WMOGO_GUN_PANIC_5, + SFX_WMOGO_JACKED_1, + SFX_WMOGO_JACKED_2, + SFX_WMOGO_JACKED_3, + SFX_WMOGO_JACKED_4, + SFX_WMOGO_JACKED_5, + SFX_WMOGO_JACKED_6, + SFX_WMOGO_RUN_1, + SFX_WMOGO_RUN_2, + SFX_WMOGO_RUN_3, + SFX_WMOGO_RUN_4, + SFX_WMOGO_RUN_5, + SFX_WMOGO_SAVED_1, + SFX_WMOGO_SHOCKED_1, + SFX_WMOGO_SHOCKED_2, + SFX_WMOGO_SHOCKED_3, + SFX_WMOGO_TAXI_1, + + SFX_WMYCR_BUMP_1, + SFX_WMYCR_BUMP_2, + SFX_WMYCR_BUMP_3, + SFX_WMYCR_BUMP_4, + SFX_WMYCR_BUMP_5, + SFX_WMYCR_BUMP_6, + SFX_WMYCR_BUMP_7, + SFX_WMYCR_BUMP_8, + SFX_WMYCR_BUMP_9, + SFX_WMYCR_BUMP_10, + SFX_WMYCR_BUMP_11, + SFX_WMYCR_BUMP_12, + SFX_WMYCR_BUMP_13, + SFX_WMYCR_BUMP_14, + SFX_WMYCR_BUMP_15, + SFX_WMYCR_BUMP_16, + SFX_WMYCR_BUMP_17, + SFX_WMYCR_BUMP_18, + SFX_WMYCR_CAR_CRASH_1, + SFX_WMYCR_CAR_CRASH_2, + SFX_WMYCR_CAR_CRASH_3, + SFX_WMYCR_CAR_CRASH_4, + SFX_WMYCR_CAR_CRASH_5, + SFX_WMYCR_CAR_CRASH_6, + SFX_WMYCR_CAR_CRASH_7, + SFX_WMYCR_CAR_CRASH_8, + SFX_WMYCR_CAR_CRASH_9, + SFX_WMYCR_DODGE_1, + SFX_WMYCR_DODGE_2, + SFX_WMYCR_DODGE_3, + SFX_WMYCR_DODGE_4, + SFX_WMYCR_DODGE_5, + SFX_WMYCR_DODGE_6, + SFX_WMYCR_DODGE_7, + SFX_WMYCR_DODGE_8, + SFX_WMYCR_DODGE_9, + SFX_WMYCR_DODGE_10, + SFX_WMYCR_FIGHT_1, + SFX_WMYCR_FIGHT_2, + SFX_WMYCR_FIGHT_3, + SFX_WMYCR_FIGHT_4, + SFX_WMYCR_FIGHT_5, + SFX_WMYCR_FIGHT_6, + SFX_WMYCR_FIGHT_7, + SFX_WMYCR_GENERIC_CRASH_1, + SFX_WMYCR_GENERIC_CRASH_2, + SFX_WMYCR_GENERIC_CRASH_3, + SFX_WMYCR_GENERIC_CRASH_4, + SFX_WMYCR_GENERIC_CRASH_5, + SFX_WMYCR_GENERIC_CRASH_6, + SFX_WMYCR_GENERIC_CRASH_7, + SFX_WMYCR_GENERIC_CRASH_8, + SFX_WMYCR_GENERIC_CRASH_9, + SFX_WMYCR_GUN_COOL_1, + SFX_WMYCR_GUN_COOL_2, + SFX_WMYCR_GUN_COOL_3, + SFX_WMYCR_GUN_COOL_4, + SFX_WMYCR_GUN_COOL_5, + SFX_WMYCR_JACKING_1, + SFX_WMYCR_JACKING_2, + SFX_WMYCR_JACKING_3, + SFX_WMYCR_JACKING_4, + SFX_WMYCR_JACKING_5, + SFX_WMYCR_JACKING_6, + SFX_WMYCR_MUGGED_1, + SFX_WMYCR_MUGGED_2, + SFX_WMYCR_MUGGED_3, + SFX_WMYCR_MUGGING_1, + SFX_WMYCR_MUGGING_2, + SFX_WMYCR_MUGGING_3, + SFX_WMYCR_MUGGING_4, + SFX_WMYCR_MUGGING_5, + SFX_WMYCR_TAXI_1, + + SFX_WMYJG_BLOCKED_1, + SFX_WMYJG_BLOCKED_2, + SFX_WMYJG_BLOCKED_3, + SFX_WMYJG_BLOCKED_4, + SFX_WMYJG_BLOCKED_5, + SFX_WMYJG_BLOCKED_6, + SFX_WMYJG_BLOCKED_7, + SFX_WMYJG_BLOCKED_8, + SFX_WMYJG_BLOCKED_9, + SFX_WMYJG_BLOCKED_10, + SFX_WMYJG_BUMP_1, + SFX_WMYJG_BUMP_2, + SFX_WMYJG_BUMP_3, + SFX_WMYJG_BUMP_4, + SFX_WMYJG_BUMP_5, + SFX_WMYJG_BUMP_6, + SFX_WMYJG_BUMP_7, + SFX_WMYJG_BUMP_8, + SFX_WMYJG_BUMP_9, + SFX_WMYJG_BUMP_10, + SFX_WMYJG_EYEING_1, + SFX_WMYJG_EYEING_2, + SFX_WMYJG_GUN_PANIC_1, + SFX_WMYJG_GUN_PANIC_2, + SFX_WMYJG_GUN_PANIC_3, + SFX_WMYJG_GUN_PANIC_4, + SFX_WMYJG_MUGGED_1, + SFX_WMYJG_MUGGED_2, + SFX_WMYJG_RUN_1, + SFX_WMYJG_RUN_2, + SFX_WMYJG_RUN_3, + SFX_WMYJG_RUN_4, + SFX_WMYJG_RUN_5, + SFX_WMYJG_SAVED_1, + SFX_WMYJG_TAXI_1, + + SFX_WMOST_BLOCKED_1, + SFX_WMOST_BLOCKED_2, + SFX_WMOST_BLOCKED_3, + SFX_WMOST_BLOCKED_4, + SFX_WMOST_BLOCKED_5, + SFX_WMOST_BLOCKED_6, + SFX_WMOST_BLOCKED_7, + SFX_WMOST_BLOCKED_8, + SFX_WMOST_BUMP_1, + SFX_WMOST_BUMP_2, + SFX_WMOST_BUMP_3, + SFX_WMOST_BUMP_4, + SFX_WMOST_BUMP_5, + SFX_WMOST_BUMP_6, + SFX_WMOST_BUMP_7, + SFX_WMOST_BUMP_8, + SFX_WMOST_BUMP_9, + SFX_WMOST_BUMP_10, + SFX_WMOST_CAR_CRASH_1, + SFX_WMOST_CAR_CRASH_2, + SFX_WMOST_CAR_CRASH_3, + SFX_WMOST_CAR_CRASH_4, + SFX_WMOST_CAR_CRASH_5, + SFX_WMOST_CAR_CRASH_6, + SFX_WMOST_CAR_CRASH_7, + SFX_WMOST_CHAT_1, + SFX_WMOST_CHAT_2, + SFX_WMOST_CHAT_3, + SFX_WMOST_CHAT_4, + SFX_WMOST_CHAT_5, + SFX_WMOST_CHAT_6, + SFX_WMOST_CHAT_7, + SFX_WMOST_CHAT_8, + SFX_WMOST_CHAT_9, + SFX_WMOST_DODGE_1, + SFX_WMOST_DODGE_2, + SFX_WMOST_DODGE_3, + SFX_WMOST_DODGE_4, + SFX_WMOST_DODGE_5, + SFX_WMOST_DODGE_6, + SFX_WMOST_DODGE_7, + SFX_WMOST_DODGE_8, + SFX_WMOST_EYEING_1, + SFX_WMOST_EYEING_2, + SFX_WMOST_FIGHT_1, + SFX_WMOST_FIGHT_2, + SFX_WMOST_FIGHT_3, + SFX_WMOST_FIGHT_4, + SFX_WMOST_FIGHT_5, + SFX_WMOST_FIGHT_6, + SFX_WMOST_FIGHT_7, + SFX_WMOST_FIGHT_8, + SFX_WMOST_GENERIC_CRASH_1, + SFX_WMOST_GENERIC_CRASH_2, + SFX_WMOST_GENERIC_CRASH_3, + SFX_WMOST_GENERIC_CRASH_4, + SFX_WMOST_GENERIC_CRASH_5, + SFX_WMOST_GENERIC_CRASH_6, + SFX_WMOST_GENERIC_CRASH_7, + SFX_WMOST_GUN_COOL_1, + SFX_WMOST_GUN_COOL_2, + SFX_WMOST_GUN_COOL_3, + SFX_WMOST_GUN_COOL_4, + SFX_WMOST_GUN_COOL_5, + SFX_WMOST_INNOCENT_1, + SFX_WMOST_INNOCENT_2, + SFX_WMOST_INNOCENT_3, + SFX_WMOST_JACKED_1, + SFX_WMOST_JACKED_2, + SFX_WMOST_JACKED_3, + SFX_WMOST_JACKED_4, + SFX_WMOST_JEER_1, + SFX_WMOST_JEER_2, + SFX_WMOST_JEER_3, + SFX_WMOST_JEER_4, + SFX_WMOST_LOST_1, + SFX_WMOST_LOST_2, + SFX_WMOST_MUGGED_1, + SFX_WMOST_MUGGED_2, + SFX_WMOST_SAVED_1, + SFX_WMOST_TAXI_1, + + SFX_BFOTR_BUMP_1, + SFX_BFOTR_BUMP_2, + SFX_BFOTR_BUMP_3, + SFX_BFOTR_BUMP_4, + SFX_BFOTR_BUMP_5, + SFX_BFOTR_BUMP_6, + SFX_BFOTR_BUMP_7, + SFX_BFOTR_BUMP_8, + SFX_BFOTR_BUMP_9, + SFX_BFOTR_BUMP_10, + SFX_BFOTR_CHAT_1, + SFX_BFOTR_CHAT_2, + SFX_BFOTR_CHAT_3, + SFX_BFOTR_CHAT_4, + SFX_BFOTR_CHAT_5, + SFX_BFOTR_CHAT_6, + SFX_BFOTR_CHAT_7, + SFX_BFOTR_CHAT_8, + SFX_BFOTR_CHAT_9, + SFX_BFOTR_CHAT_10, + SFX_BFOTR_CHAT_11, + SFX_BFOTR_CHAT_12, + SFX_BFOTR_CHAT_13, + SFX_BFOTR_CHAT_14, + SFX_BFOTR_CHAT_15, + SFX_BFOTR_DODGE_1, + SFX_BFOTR_DODGE_2, + SFX_BFOTR_DODGE_3, + SFX_BFOTR_DODGE_4, + SFX_BFOTR_DODGE_5, + SFX_BFOTR_DODGE_6, + SFX_BFOTR_DODGE_7, + SFX_BFOTR_DODGE_8, + SFX_BFOTR_DODGE_9, + SFX_BFOTR_FIGHT_1, + SFX_BFOTR_FIGHT_2, + SFX_BFOTR_FIGHT_3, + SFX_BFOTR_FIGHT_4, + SFX_BFOTR_FIGHT_5, + SFX_BFOTR_FIGHT_6, + SFX_BFOTR_GUN_COOL_1, + SFX_BFOTR_GUN_COOL_2, + SFX_BFOTR_GUN_COOL_3, + SFX_BFOTR_GUN_COOL_4, + SFX_BFOTR_GUN_COOL_5, + SFX_BFOTR_GUN_COOL_6, + SFX_BFOTR_MUGGED_1, + SFX_BFOTR_MUGGED_2, + SFX_BFOTR_MUGGING_1, + SFX_BFOTR_MUGGING_2, + SFX_BFOTR_MUGGING_3, + SFX_BFOTR_SAVED_1, + SFX_BFOTR_SHOCKED_1, + SFX_BFOTR_SHOCKED_2, + SFX_BFOTR_SHOCKED_3, + SFX_BFOTR_SOLICIT_1, + SFX_BFOTR_SOLICIT_2, + SFX_BFOTR_SOLICIT_3, + SFX_BFOTR_SOLICIT_4, + SFX_BFOTR_SOLICIT_5, + SFX_BFOTR_TAXI_1, + + SFX_WFYRI_BLOCKED_1, + SFX_WFYRI_BLOCKED_2, + SFX_WFYRI_BLOCKED_3, + SFX_WFYRI_BLOCKED_4, + SFX_WFYRI_BLOCKED_5, + SFX_WFYRI_BLOCKED_6, + SFX_WFYRI_BLOCKED_7, + SFX_WFYRI_BLOCKED_8, + SFX_WFYRI_BUMP_1, + SFX_WFYRI_BUMP_2, + SFX_WFYRI_BUMP_3, + SFX_WFYRI_BUMP_4, + SFX_WFYRI_BUMP_5, + SFX_WFYRI_BUMP_6, + SFX_WFYRI_BUMP_7, + SFX_WFYRI_BUMP_8, + SFX_WFYRI_BUMP_9, + SFX_WFYRI_BUMP_10, + SFX_WFYRI_CAR_CRASH_1, + SFX_WFYRI_CAR_CRASH_2, + SFX_WFYRI_CAR_CRASH_3, + SFX_WFYRI_CAR_CRASH_4, + SFX_WFYRI_CAR_CRASH_5, + SFX_WFYRI_CAR_CRASH_6, + SFX_WFYRI_CAR_CRASH_7, + SFX_WFYRI_CAR_CRASH_8, + SFX_WFYRI_CAR_CRASH_9, + SFX_WFYRI_DODGE_1, + SFX_WFYRI_DODGE_2, + SFX_WFYRI_DODGE_3, + SFX_WFYRI_DODGE_4, + SFX_WFYRI_DODGE_5, + SFX_WFYRI_DODGE_6, + SFX_WFYRI_DODGE_7, + SFX_WFYRI_DODGE_8, + SFX_WFYRI_DODGE_9, + SFX_WFYRI_EYEING_1, + SFX_WFYRI_EYEING_2, + SFX_WFYRI_GENERIC_CRASH_1, + SFX_WFYRI_GENERIC_CRASH_2, + SFX_WFYRI_GENERIC_CRASH_3, + SFX_WFYRI_GENERIC_CRASH_4, + SFX_WFYRI_GENERIC_CRASH_5, + SFX_WFYRI_GENERIC_CRASH_6, + SFX_WFYRI_GENERIC_CRASH_7, + SFX_WFYRI_GENERIC_CRASH_8, + SFX_WFYRI_GUN_PANIC_1, + SFX_WFYRI_GUN_PANIC_2, + SFX_WFYRI_GUN_PANIC_3, + SFX_WFYRI_GUN_PANIC_4, + SFX_WFYRI_GUN_PANIC_5, + SFX_WFYRI_JACKED_1, + SFX_WFYRI_JACKED_2, + SFX_WFYRI_JACKED_3, + SFX_WFYRI_JACKED_4, + SFX_WFYRI_JACKED_5, + SFX_WFYRI_JACKED_6, + SFX_WFYRI_JACKED_7, + SFX_WFYRI_LOST_1, + SFX_WFYRI_LOST_2, + SFX_WFYRI_MUGGED_1, + SFX_WFYRI_MUGGED_2, + SFX_WFYRI_RUN_1, + SFX_WFYRI_RUN_2, + SFX_WFYRI_RUN_3, + SFX_WFYRI_RUN_4, + SFX_WFYRI_RUN_5, + SFX_WFYRI_SAVED_1, + SFX_WFYRI_SHOCKED_1, + SFX_WFYRI_SHOCKED_2, + SFX_WFYRI_TAXI_1, + SFX_BFYPR_BUMP_1, + SFX_BFYPR_BUMP_2, + SFX_BFYPR_BUMP_3, + SFX_BFYPR_BUMP_4, + SFX_BFYPR_BUMP_5, + SFX_BFYPR_BUMP_6, + SFX_BFYPR_BUMP_7, + SFX_BFYPR_BUMP_8, + SFX_BFYPR_BUMP_9, + SFX_BFYPR_BUMP_10, + SFX_BFYPR_BUMP_11, + SFX_BFYPR_CHAT_1, + SFX_BFYPR_CHAT_2, + SFX_BFYPR_CHAT_3, + SFX_BFYPR_CHAT_4, + SFX_BFYPR_CHAT_5, + SFX_BFYPR_CHAT_6, + SFX_BFYPR_CHAT_7, + SFX_BFYPR_CHAT_8, + SFX_BFYPR_CHAT_9, + SFX_BFYPR_CHAT_10, + SFX_BFYPR_CHAT_11, + SFX_BFYPR_CHAT_12, + SFX_BFYPR_CHAT_13, + SFX_BFYPR_DODGE_1, + SFX_BFYPR_DODGE_2, + SFX_BFYPR_DODGE_3, + SFX_BFYPR_DODGE_4, + SFX_BFYPR_DODGE_5, + SFX_BFYPR_DODGE_6, + SFX_BFYPR_DODGE_7, + SFX_BFYPR_FIGHT_1, + SFX_BFYPR_FIGHT_2, + SFX_BFYPR_FIGHT_3, + SFX_BFYPR_FIGHT_4, + SFX_BFYPR_FIGHT_5, + SFX_BFYPR_FIGHT_6, + SFX_BFYPR_FIGHT_7, + SFX_BFYPR_FUCKING_1, + SFX_BFYPR_FUCKING_2, + SFX_BFYPR_FUCKING_3, + SFX_BFYPR_FUCKING_4, + SFX_BFYPR_FUCKING_5, + SFX_BFYPR_FUCKING_6, + SFX_BFYPR_FUCKING_7, + SFX_BFYPR_GUN_COOL_1, + SFX_BFYPR_GUN_COOL_2, + SFX_BFYPR_GUN_COOL_3, + SFX_BFYPR_GUN_COOL_4, + SFX_BFYPR_GUN_COOL_5, + SFX_BFYPR_MUGGED_1, + SFX_BFYPR_MUGGED_2, + SFX_BFYPR_SAVED_1, + SFX_BFYPR_SHOCKED_1, + SFX_BFYPR_SHOCKED_2, + SFX_BFYPR_SOLICIT_1, + SFX_BFYPR_SOLICIT_2, + SFX_BFYPR_SOLICIT_3, + SFX_BFYPR_SOLICIT_4, + SFX_BFYPR_SOLICIT_5, + SFX_BFYPR_SOLICIT_6, + SFX_BFYPR_SOLICIT_7, + SFX_BFYPR_SOLICIT_8, + SFX_BFYPR_SOLICIT_9, + SFX_BFYPR_SOLICIT_10, + SFX_BFYPR_SOLICIT_11, + SFX_BFYPR_SOLICIT_12, + SFX_BFYPR_SOLICIT_13, + SFX_BFYPR_TAXI_1, + SFX_BFYPR_TAXI_2, + + SFX_BMYRI_BLOCKED_1, + SFX_BMYRI_BLOCKED_2, + SFX_BMYRI_BLOCKED_3, + SFX_BMYRI_BLOCKED_4, + SFX_BMYRI_BLOCKED_5, + SFX_BMYRI_BLOCKED_6, + SFX_BMYRI_BUMP_1, + SFX_BMYRI_BUMP_2, + SFX_BMYRI_BUMP_3, + SFX_BMYRI_BUMP_4, + SFX_BMYRI_BUMP_5, + SFX_BMYRI_BUMP_6, + SFX_BMYRI_BUMP_7, + SFX_BMYRI_CAR_CRASH_1, + SFX_BMYRI_CAR_CRASH_2, + SFX_BMYRI_CAR_CRASH_3, + SFX_BMYRI_CAR_CRASH_4, + SFX_BMYRI_CAR_CRASH_5, + SFX_BMYRI_CAR_CRASH_6, + SFX_BMYRI_CAR_CRASH_7, + SFX_BMYRI_DODGE_1, + SFX_BMYRI_DODGE_2, + SFX_BMYRI_DODGE_3, + SFX_BMYRI_DODGE_4, + SFX_BMYRI_DODGE_5, + SFX_BMYRI_DODGE_6, + SFX_BMYRI_DODGE_7, + SFX_BMYRI_DODGE_8, + SFX_BMYRI_EYEING_1, + SFX_BMYRI_GENERIC_CRASH_1, + SFX_BMYRI_GENERIC_CRASH_2, + SFX_BMYRI_GENERIC_CRASH_3, + SFX_BMYRI_GENERIC_CRASH_4, + SFX_BMYRI_GENERIC_CRASH_5, + SFX_BMYRI_GENERIC_CRASH_6, + SFX_BMYRI_GENERIC_CRASH_7, + SFX_BMYRI_GUN_PANIC_1, + SFX_BMYRI_GUN_PANIC_2, + SFX_BMYRI_GUN_PANIC_3, + SFX_BMYRI_GUN_PANIC_4, + SFX_BMYRI_GUN_PANIC_5, + SFX_BMYRI_GUN_PANIC_6, + SFX_BMYRI_GUN_PANIC_7, + SFX_BMYRI_JACKED_1, + SFX_BMYRI_JACKED_2, + SFX_BMYRI_JACKED_3, + SFX_BMYRI_JACKED_4, + SFX_BMYRI_LOST_1, + SFX_BMYRI_LOST_2, + SFX_BMYRI_MUGGED_1, + SFX_BMYRI_MUGGED_2, + SFX_BMYRI_RUN_1, + SFX_BMYRI_RUN_2, + SFX_BMYRI_RUN_3, + SFX_BMYRI_RUN_4, + SFX_BMYRI_SAVED_1, + SFX_BMYRI_SHOCKED_1, + SFX_BMYRI_SHOCKED_2, + SFX_BMYRI_SHOCKED_3, + SFX_BMYRI_TAXI_1, + SFX_BMYBU_BLOCKED_1, + SFX_BMYBU_BLOCKED_2, + SFX_BMYBU_BLOCKED_3, + SFX_BMYBU_BLOCKED_4, + SFX_BMYBU_BLOCKED_5, + SFX_BMYBU_BLOCKED_6, + SFX_BMYBU_BLOCKED_7, + SFX_BMYBU_BLOCKED_8, + SFX_BMYBU_BUMP_1, + SFX_BMYBU_BUMP_2, + SFX_BMYBU_BUMP_3, + SFX_BMYBU_BUMP_4, + SFX_BMYBU_BUMP_5, + SFX_BMYBU_BUMP_6, + SFX_BMYBU_BUMP_7, + SFX_BMYBU_CAR_CRASH_1, + SFX_BMYBU_CAR_CRASH_2, + SFX_BMYBU_CAR_CRASH_3, + SFX_BMYBU_CAR_CRASH_4, + SFX_BMYBU_CAR_CRASH_5, + SFX_BMYBU_CAR_CRASH_6, + SFX_BMYBU_CAR_CRASH_7, + SFX_BMYBU_DODGE_1, + SFX_BMYBU_DODGE_2, + SFX_BMYBU_DODGE_3, + SFX_BMYBU_DODGE_4, + SFX_BMYBU_DODGE_5, + SFX_BMYBU_DODGE_6, + SFX_BMYBU_DODGE_7, + SFX_BMYBU_DODGE_8, + SFX_BMYBU_DODGE_9, + SFX_BMYBU_DODGE_10, + SFX_BMYBU_EYEING_1, + SFX_BMYBU_EYEING_2, + SFX_BMYBU_FIGHT_1, + SFX_BMYBU_FIGHT_2, + SFX_BMYBU_FIGHT_3, + SFX_BMYBU_FIGHT_4, + SFX_BMYBU_FIGHT_5, + SFX_BMYBU_GENERIC_CRASH_1, + SFX_BMYBU_GENERIC_CRASH_2, + SFX_BMYBU_GENERIC_CRASH_3, + SFX_BMYBU_GENERIC_CRASH_4, + SFX_BMYBU_GENERIC_CRASH_5, + SFX_BMYBU_GENERIC_CRASH_6, + SFX_BMYBU_GENERIC_CRASH_7, + SFX_BMYBU_GUN_PANIC_1, + SFX_BMYBU_GUN_PANIC_2, + SFX_BMYBU_GUN_PANIC_3, + SFX_BMYBU_GUN_PANIC_4, + SFX_BMYBU_GUN_PANIC_5, + SFX_BMYBU_INNOCENT_1, + SFX_BMYBU_INNOCENT_2, + SFX_BMYBU_JACKED_1, + SFX_BMYBU_JACKED_2, + SFX_BMYBU_JACKED_3, + SFX_BMYBU_JACKED_4, + SFX_BMYBU_JACKED_5, + SFX_BMYBU_JACKED_6, + SFX_BMYBU_MUGGED_1, + SFX_BMYBU_MUGGED_2, + SFX_BMYBU_SAVED_1, + SFX_BMYBU_SHOCKED_1, + SFX_BMYBU_SHOCKED_2, + SFX_BMYBU_TAXI_1, + + SFX_WMYSK_BUMP_1, + SFX_WMYSK_BUMP_2, + SFX_WMYSK_BUMP_3, + SFX_WMYSK_BUMP_4, + SFX_WMYSK_BUMP_5, + SFX_WMYSK_BUMP_6, + SFX_WMYSK_BUMP_7, + SFX_WMYSK_BUMP_8, + SFX_WMYSK_BUMP_9, + SFX_WMYSK_BUMP_10, + SFX_WMYSK_BUMP_11, + SFX_WMYSK_BUMP_12, + SFX_WMYSK_BUMP_13, + SFX_WMYSK_BUMP_14, + SFX_WMYSK_CHAT_1, + SFX_WMYSK_CHAT_2, + SFX_WMYSK_CHAT_3, + SFX_WMYSK_CHAT_4, + SFX_WMYSK_CHAT_5, + SFX_WMYSK_CHAT_6, + SFX_WMYSK_CHAT_7, + SFX_WMYSK_CHAT_8, + SFX_WMYSK_CHAT_9, + SFX_WMYSK_CHAT_10, + SFX_WMYSK_CHAT_11, + SFX_WMYSK_CHAT_12, + SFX_WMYSK_CHAT_13, + SFX_WMYSK_DODGE_1, + SFX_WMYSK_DODGE_2, + SFX_WMYSK_DODGE_3, + SFX_WMYSK_DODGE_4, + SFX_WMYSK_DODGE_5, + SFX_WMYSK_DODGE_6, + SFX_WMYSK_DODGE_7, + SFX_WMYSK_DODGE_8, + SFX_WMYSK_DODGE_9, + SFX_WMYSK_DODGE_10, + SFX_WMYSK_EYEING_1, + SFX_WMYSK_EYEING_2, + SFX_WMYSK_FIGHT_1, + SFX_WMYSK_FIGHT_2, + SFX_WMYSK_FIGHT_3, + SFX_WMYSK_FIGHT_4, + SFX_WMYSK_FIGHT_5, + SFX_WMYSK_GUN_PANIC_1, + SFX_WMYSK_GUN_PANIC_2, + SFX_WMYSK_GUN_PANIC_3, + SFX_WMYSK_GUN_PANIC_4, + SFX_WMYSK_GUN_PANIC_5, + SFX_WMYSK_INNOCENT_1, + SFX_WMYSK_INNOCENT_2, + SFX_WMYSK_INNOCENT_3, + SFX_WMYSK_LOST_1, + SFX_WMYSK_LOST_2, + SFX_WMYSK_MUGGED_1, + SFX_WMYSK_MUGGED_2, + SFX_WMYSK_SAVED_1, + SFX_WMYSK_SAVED_2, + SFX_WMYSK_SHOCKED_1, + SFX_WMYSK_SHOCKED_2, + SFX_WMYSK_TAXI_1, + + SFX_WMYCW_BLOCKED_1, + SFX_WMYCW_BLOCKED_2, + SFX_WMYCW_BLOCKED_3, + SFX_WMYCW_BLOCKED_4, + SFX_WMYCW_BLOCKED_5, + SFX_WMYCW_BLOCKED_6, + SFX_WMYCW_BLOCKED_7, + SFX_WMYCW_BLOCKED_8, + SFX_WMYCW_BLOCKED_9, + SFX_WMYCW_BUMP_1, + SFX_WMYCW_BUMP_2, + SFX_WMYCW_BUMP_3, + SFX_WMYCW_BUMP_4, + SFX_WMYCW_BUMP_5, + SFX_WMYCW_BUMP_6, + SFX_WMYCW_BUMP_7, + SFX_WMYCW_BUMP_8, + SFX_WMYCW_BUMP_9, + SFX_WMYCW_CAR_CRASH_1, + SFX_WMYCW_CAR_CRASH_2, + SFX_WMYCW_CAR_CRASH_3, + SFX_WMYCW_CAR_CRASH_4, + SFX_WMYCW_CAR_CRASH_5, + SFX_WMYCW_CAR_CRASH_6, + SFX_WMYCW_CAR_CRASH_7, + SFX_WMYCW_CAR_CRASH_8, + SFX_WMYCW_CAR_CRASH_9, + SFX_WMYCW_CHAT_1, + SFX_WMYCW_CHAT_2, + SFX_WMYCW_CHAT_3, + SFX_WMYCW_CHAT_4, + SFX_WMYCW_CHAT_5, + SFX_WMYCW_CHAT_6, + SFX_WMYCW_CHAT_7, + SFX_WMYCW_CHAT_8, + SFX_WMYCW_CHAT_9, + SFX_WMYCW_CHAT_10, + SFX_WMYCW_CHAT_11, + SFX_WMYCW_CHAT_12, + SFX_WMYCW_CHAT_13, + SFX_WMYCW_CHAT_14, + SFX_WMYCW_CHAT_15, + SFX_WMYCW_DODGE_1, + SFX_WMYCW_DODGE_2, + SFX_WMYCW_DODGE_3, + SFX_WMYCW_DODGE_4, + SFX_WMYCW_DODGE_5, + SFX_WMYCW_DODGE_6, + SFX_WMYCW_DODGE_7, + SFX_WMYCW_DODGE_8, + SFX_WMYCW_DODGE_9, + SFX_WMYCW_DODGE_10, + SFX_WMYCW_EYEING_1, + SFX_WMYCW_EYEING_2, + SFX_WMYCW_EYEING_3, + SFX_WMYCW_FIGHT_1, + SFX_WMYCW_FIGHT_2, + SFX_WMYCW_FIGHT_3, + SFX_WMYCW_FIGHT_4, + SFX_WMYCW_FIGHT_5, + SFX_WMYCW_FIGHT_6, + SFX_WMYCW_FIGHT_7, + SFX_WMYCW_FIGHT_8, + SFX_WMYCW_GENERIC_CRASH_1, + SFX_WMYCW_GENERIC_CRASH_2, + SFX_WMYCW_GENERIC_CRASH_3, + SFX_WMYCW_GENERIC_CRASH_4, + SFX_WMYCW_GENERIC_CRASH_5, + SFX_WMYCW_GENERIC_CRASH_6, + SFX_WMYCW_GENERIC_CRASH_7, + SFX_WMYCW_GUN_PANIC_1, + SFX_WMYCW_GUN_PANIC_2, + SFX_WMYCW_GUN_PANIC_3, + SFX_WMYCW_GUN_PANIC_4, + SFX_WMYCW_GUN_PANIC_5, + SFX_WMYCW_GUN_PANIC_6, + SFX_WMYCW_INNOCENT_1, + SFX_WMYCW_INNOCENT_2, + SFX_WMYCW_INNOCENT_3, + SFX_WMYCW_JACKED_1, + SFX_WMYCW_JACKED_2, + SFX_WMYCW_JACKED_3, + SFX_WMYCW_JACKED_4, + SFX_WMYCW_JACKED_5, + SFX_WMYCW_JACKED_6, + SFX_WMYCW_JEER_1, + SFX_WMYCW_JEER_2, + SFX_WMYCW_JEER_3, + SFX_WMYCW_JEER_4, + SFX_WMYCW_JEER_5, + SFX_WMYCW_JACKING_1, + SFX_WMYCW_JACKING_2, + SFX_WMYCW_JACKING_3, + SFX_WMYCW_JACKING_4, + SFX_WMYCW_LOST_1, + SFX_WMYCW_LOST_2, + SFX_WMYCW_MUGGED_1, + SFX_WMYCW_TAXI_1, + + SFX_HFYST_BLOCKED_1, + SFX_HFYST_BLOCKED_2, + SFX_HFYST_BLOCKED_3, + SFX_HFYST_BLOCKED_4, + SFX_HFYST_BLOCKED_5, + SFX_HFYST_BLOCKED_6, + SFX_HFYST_BLOCKED_7, + SFX_HFYST_BUMP_1, + SFX_HFYST_BUMP_2, + SFX_HFYST_BUMP_3, + SFX_HFYST_BUMP_4, + SFX_HFYST_BUMP_5, + SFX_HFYST_BUMP_6, + SFX_HFYST_BUMP_7, + SFX_HFYST_BUMP_8, + SFX_HFYST_BUMP_9, + SFX_HFYST_BUMP_10, + SFX_HFYST_CAR_CRASH_1, + SFX_HFYST_CAR_CRASH_2, + SFX_HFYST_CAR_CRASH_3, + SFX_HFYST_CAR_CRASH_4, + SFX_HFYST_CAR_CRASH_5, + SFX_HFYST_CAR_CRASH_6, + SFX_HFYST_CAR_CRASH_7, + SFX_HFYST_CAR_CRASH_8, + SFX_HFYST_CHAT_1, + SFX_HFYST_CHAT_2, + SFX_HFYST_CHAT_3, + SFX_HFYST_CHAT_4, + SFX_HFYST_CHAT_5, + SFX_HFYST_CHAT_6, + SFX_HFYST_CHAT_7, + SFX_HFYST_CHAT_8, + SFX_HFYST_CHAT_9, + SFX_HFYST_DODGE_1, + SFX_HFYST_DODGE_2, + SFX_HFYST_DODGE_3, + SFX_HFYST_DODGE_4, + SFX_HFYST_DODGE_5, + SFX_HFYST_DODGE_6, + SFX_HFYST_DODGE_7, + SFX_HFYST_DODGE_8, + SFX_HFYST_DODGE_9, + SFX_HFYST_DODGE_10, + SFX_HFYST_FIGHT_1, + SFX_HFYST_FIGHT_2, + SFX_HFYST_FIGHT_3, + SFX_HFYST_FIGHT_4, + SFX_HFYST_FIGHT_5, + SFX_HFYST_FIGHT_6, + SFX_HFYST_FIGHT_7, + SFX_HFYST_GENERIC_CRASH_1, + SFX_HFYST_GENERIC_CRASH_2, + SFX_HFYST_GENERIC_CRASH_3, + SFX_HFYST_GENERIC_CRASH_4, + SFX_HFYST_GENERIC_CRASH_5, + SFX_HFYST_GENERIC_CRASH_6, + SFX_HFYST_GENERIC_CRASH_7, + SFX_HFYST_GUN_COOL_1, + SFX_HFYST_GUN_COOL_2, + SFX_HFYST_GUN_COOL_3, + SFX_HFYST_GUN_COOL_4, + SFX_HFYST_GUN_COOL_5, + SFX_HFYST_JACKED_1, + SFX_HFYST_JACKED_2, + SFX_HFYST_JACKED_3, + SFX_HFYST_JACKED_4, + SFX_HFYST_JACKED_5, + SFX_HFYST_JACKED_6, + SFX_HFYST_JACKING_1, + SFX_HFYST_JACKING_2, + SFX_HFYST_JACKING_3, + SFX_HFYST_JACKING_4, + SFX_HFYST_LOST_1, + SFX_HFYST_LOST_2, + SFX_HFYST_MUGGED_1, + SFX_HFYST_MUGGED_2, + SFX_HFYST_MUGGING_1, + SFX_HFYST_MUGGING_2, + SFX_HFYST_MUGGING_3, + SFX_HFYST_MUGGING_4, + SFX_HFYST_TAXI_1, + + SFX_HMOST_BLOCKED_1, + SFX_HMOST_BLOCKED_2, + SFX_HMOST_BLOCKED_3, + SFX_HMOST_BLOCKED_4, + SFX_HMOST_BLOCKED_5, + SFX_HMOST_BLOCKED_6, + SFX_HMOST_BLOCKED_7, + SFX_HMOST_BUMP_1, + SFX_HMOST_BUMP_2, + SFX_HMOST_BUMP_3, + SFX_HMOST_BUMP_4, + SFX_HMOST_BUMP_5, + SFX_HMOST_BUMP_6, + SFX_HMOST_BUMP_7, + SFX_HMOST_BUMP_8, + SFX_HMOST_BUMP_9, + SFX_HMOST_BUMP_10, + SFX_HMOST_CAR_CRASH_1, + SFX_HMOST_CAR_CRASH_2, + SFX_HMOST_CAR_CRASH_3, + SFX_HMOST_CAR_CRASH_4, + SFX_HMOST_CAR_CRASH_5, + SFX_HMOST_CAR_CRASH_6, + SFX_HMOST_CAR_CRASH_7, + SFX_HMOST_CHAT_1, + SFX_HMOST_CHAT_2, + SFX_HMOST_CHAT_3, + SFX_HMOST_CHAT_4, + SFX_HMOST_CHAT_5, + SFX_HMOST_CHAT_6, + SFX_HMOST_CHAT_7, + SFX_HMOST_CHAT_8, + SFX_HMOST_CHAT_9, + SFX_HMOST_CHAT_10, + SFX_HMOST_CHAT_11, + SFX_HMOST_DODGE_1, + SFX_HMOST_DODGE_2, + SFX_HMOST_DODGE_3, + SFX_HMOST_DODGE_4, + SFX_HMOST_DODGE_5, + SFX_HMOST_DODGE_6, + SFX_HMOST_DODGE_7, + SFX_HMOST_DODGE_8, + SFX_HMOST_DODGE_9, + SFX_HMOST_EYEING_1, + SFX_HMOST_FIGHT_1, + SFX_HMOST_FIGHT_2, + SFX_HMOST_FIGHT_3, + SFX_HMOST_FIGHT_4, + SFX_HMOST_FIGHT_5, + SFX_HMOST_FIGHT_6, + SFX_HMOST_FIGHT_7, + SFX_HMOST_FIGHT_8, + SFX_HMOST_GENERIC_CRASH_1, + SFX_HMOST_GENERIC_CRASH_2, + SFX_HMOST_GENERIC_CRASH_3, + SFX_HMOST_GENERIC_CRASH_4, + SFX_HMOST_GENERIC_CRASH_5, + SFX_HMOST_GENERIC_CRASH_6, + SFX_HMOST_GENERIC_CRASH_7, + SFX_HMOST_GUN_COOL_1, + SFX_HMOST_GUN_COOL_2, + SFX_HMOST_GUN_COOL_3, + SFX_HMOST_GUN_COOL_4, + SFX_HMOST_GUN_COOL_5, + SFX_HMOST_JACKED_1, + SFX_HMOST_JACKED_2, + SFX_HMOST_JACKED_3, + SFX_HMOST_JACKED_4, + SFX_HMOST_JACKED_5, + SFX_HMOST_JACKED_6, + SFX_HMOST_JACKING_1, + SFX_HMOST_JACKING_2, + SFX_HMOST_JACKING_3, + SFX_HMOST_LOST_1, + SFX_HMOST_LOST_2, + SFX_HMOST_MUGGED_1, + SFX_HMOST_MUGGED_2, + SFX_HMOST_TAXI_1, + + SFX_HMYRI_BLOCKED_1, + SFX_HMYRI_BLOCKED_2, + SFX_HMYRI_BLOCKED_3, + SFX_HMYRI_BLOCKED_4, + SFX_HMYRI_BLOCKED_5, + SFX_HMYRI_BLOCKED_6, + SFX_HMYRI_BLOCKED_7, + SFX_HMYRI_BUMP_1, + SFX_HMYRI_BUMP_2, + SFX_HMYRI_BUMP_3, + SFX_HMYRI_BUMP_4, + SFX_HMYRI_BUMP_5, + SFX_HMYRI_BUMP_6, + SFX_HMYRI_BUMP_7, + SFX_HMYRI_BUMP_8, + SFX_HMYRI_BUMP_9, + SFX_HMYRI_BUMP_10, + SFX_HMYRI_CAR_CRASH_1, + SFX_HMYRI_CAR_CRASH_2, + SFX_HMYRI_CAR_CRASH_3, + SFX_HMYRI_CAR_CRASH_4, + SFX_HMYRI_CAR_CRASH_5, + SFX_HMYRI_CAR_CRASH_6, + SFX_HMYRI_CAR_CRASH_7, + SFX_HMYRI_CAR_CRASH_8, + SFX_HMYRI_DODGE_1, + SFX_HMYRI_DODGE_2, + SFX_HMYRI_DODGE_3, + SFX_HMYRI_DODGE_4, + SFX_HMYRI_DODGE_5, + SFX_HMYRI_DODGE_6, + SFX_HMYRI_DODGE_7, + SFX_HMYRI_DODGE_8, + SFX_HMYRI_DODGE_9, + SFX_HMYRI_FIGHT_1, + SFX_HMYRI_FIGHT_2, + SFX_HMYRI_FIGHT_3, + SFX_HMYRI_FIGHT_4, + SFX_HMYRI_FIGHT_5, + SFX_HMYRI_GENERIC_CRASH_1, + SFX_HMYRI_GENERIC_CRASH_2, + SFX_HMYRI_GENERIC_CRASH_3, + SFX_HMYRI_GENERIC_CRASH_4, + SFX_HMYRI_GENERIC_CRASH_5, + SFX_HMYRI_GENERIC_CRASH_6, + SFX_HMYRI_GENERIC_CRASH_7, + SFX_HMYRI_GENERIC_CRASH_8, + SFX_HMYRI_GENERIC_CRASH_9, + SFX_HMYRI_GENERIC_CRASH_10, + SFX_HMYRI_GENERIC_CRASH_11, + SFX_HMYRI_GENERIC_CRASH_12, + SFX_HMYRI_GUN_PANIC_1, + SFX_HMYRI_GUN_PANIC_2, + SFX_HMYRI_GUN_PANIC_3, + SFX_HMYRI_GUN_PANIC_4, + SFX_HMYRI_GUN_PANIC_5, + SFX_HMYRI_GUN_PANIC_6, + SFX_HMYRI_GUN_PANIC_7, + SFX_HMYRI_JACKED_1, + SFX_HMYRI_JACKED_2, + SFX_HMYRI_JACKED_3, + SFX_HMYRI_JACKED_4, + SFX_HMYRI_JACKED_5, + SFX_HMYRI_JACKED_6, + SFX_HMYRI_JACKED_7, + SFX_HMYRI_JACKED_8, + SFX_HMYRI_JACKING_1, + SFX_HMYRI_JACKING_2, + SFX_HMYRI_JACKING_3, + SFX_HMYRI_MUGGED_1, + SFX_HMYRI_SHOCKED_1, + SFX_HMYRI_SHOCKED_2, + SFX_HMYRI_SHOCKED_3, + + SFX_HFYPR_BUMP_1, + SFX_HFYPR_BUMP_2, + SFX_HFYPR_BUMP_3, + SFX_HFYPR_BUMP_4, + SFX_HFYPR_BUMP_5, + SFX_HFYPR_BUMP_6, + SFX_HFYPR_BUMP_7, + SFX_HFYPR_BUMP_8, + SFX_HFYPR_BUMP_9, + SFX_HFYPR_BUMP_10, + SFX_HFYPR_CHAT_1, + SFX_HFYPR_CHAT_2, + SFX_HFYPR_CHAT_3, + SFX_HFYPR_CHAT_4, + SFX_HFYPR_CHAT_5, + SFX_HFYPR_CHAT_6, + SFX_HFYPR_CHAT_7, + SFX_HFYPR_CHAT_8, + SFX_HFYPR_CHAT_9, + SFX_HFYPR_CHAT_10, + SFX_HFYPR_CHAT_11, + SFX_HFYPR_CHAT_12, + SFX_HFYPR_DODGE_1, + SFX_HFYPR_DODGE_2, + SFX_HFYPR_DODGE_3, + SFX_HFYPR_DODGE_4, + SFX_HFYPR_DODGE_5, + SFX_HFYPR_DODGE_6, + SFX_HFYPR_DODGE_7, + SFX_HFYPR_DODGE_8, + SFX_HFYPR_DODGE_9, + SFX_HFYPR_EYEING_1, + SFX_HFYPR_EYEING_2, + SFX_HFYPR_EYEING_3, + SFX_HFYPR_FIGHT_1, + SFX_HFYPR_FIGHT_2, + SFX_HFYPR_FIGHT_3, + SFX_HFYPR_FIGHT_4, + SFX_HFYPR_FIGHT_5, + SFX_HFYPR_FIGHT_6, + SFX_HFYPR_FIGHT_7, + SFX_HFYPR_FIGHT_8, + SFX_HFYPR_FIGHT_9, + SFX_HFYPR_FIGHT_10, + SFX_HFYPR_FUCKING_1, + SFX_HFYPR_FUCKING_2, + SFX_HFYPR_FUCKING_3, + SFX_HFYPR_FUCKING_4, + SFX_HFYPR_FUCKING_5, + SFX_HFYPR_FUCKING_6, + SFX_HFYPR_FUCKING_7, + SFX_HFYPR_FUCKING_8, + SFX_HFYPR_GUN_COOL_1, + SFX_HFYPR_GUN_COOL_2, + SFX_HFYPR_GUN_COOL_3, + SFX_HFYPR_GUN_COOL_4, + SFX_HFYPR_GUN_COOL_5, + SFX_HFYPR_GUN_COOL_6, + SFX_HFYPR_MUGGED_1, + SFX_HFYPR_MUGGED_2, + SFX_HFYPR_SAVED_1, + SFX_HFYPR_SOLICIT_1, + SFX_HFYPR_SOLICIT_2, + SFX_HFYPR_SOLICIT_3, + SFX_HFYPR_SOLICIT_4, + SFX_HFYPR_SOLICIT_5, + SFX_HFYPR_SOLICIT_6, + SFX_HFYPR_SOLICIT_7, + SFX_HFYPR_SOLICIT_8, + SFX_HFYPR_SOLICIT_9, + SFX_HFYPR_SOLICIT_10, + SFX_HFYPR_SOLICIT_11, + SFX_HFYPR_SOLICIT_12, + SFX_HFYPR_SOLICIT_13, + SFX_HFYPR_SOLICIT_14, + SFX_HFYPR_TAXI_1, + + SFX_HFYMD_BUMP_1, + SFX_HFYMD_BUMP_2, + SFX_HFYMD_BUMP_3, + SFX_HFYMD_BUMP_4, + SFX_HFYMD_BUMP_5, + SFX_HFYMD_BUMP_6, + SFX_HFYMD_BUMP_7, + SFX_HFYMD_BUMP_8, + SFX_HFYMD_BUMP_9, + SFX_HFYMD_DODGE_1, + SFX_HFYMD_DODGE_2, + SFX_HFYMD_DODGE_3, + SFX_HFYMD_DODGE_4, + SFX_HFYMD_DODGE_5, + SFX_HFYMD_DODGE_6, + SFX_HFYMD_DODGE_7, + SFX_HFYMD_DODGE_8, + SFX_HFYMD_FIGHT_1, + SFX_HFYMD_FIGHT_2, + SFX_HFYMD_FIGHT_3, + SFX_HFYMD_FIGHT_4, + SFX_HFYMD_FIGHT_5, + SFX_HFYMD_FIGHT_6, + SFX_HFYMD_FIGHT_7, + SFX_HFYMD_FIGHT_8, + SFX_HFYMD_FIGHT_9, + SFX_HFYMD_GUN_PANIC_1, + SFX_HFYMD_GUN_PANIC_2, + SFX_HFYMD_GUN_PANIC_3, + SFX_HFYMD_GUN_PANIC_4, + SFX_HFYMD_GUN_PANIC_5, + SFX_HFYMD_MUGGED_1, + SFX_HFYMD_MUGGED_2, + SFX_HFYMD_SAVED_1, + SFX_HFYMD_SAVED_2, + SFX_HFYMD_SAVED_3, + SFX_HFYMD_SOLICIT_1, + SFX_HFYMD_SOLICIT_2, + SFX_HFYMD_SOLICIT_3, + SFX_HFYMD_SOLICIT_4, + SFX_HFYMD_SOLICIT_5, + SFX_HFYMD_SOLICIT_6, + SFX_HFYMD_SOLICIT_7, + SFX_HFYMD_SOLICIT_8, + SFX_HFYMD_SOLICIT_9, + SFX_HFYMD_SOLICIT_10, + SFX_HFYMD_SOLICIT_11, + SFX_HFYMD_SOLICIT_12, + SFX_HFYMD_SOLICIT_13, + SFX_HFYMD_SOLICIT_14, + SFX_HFYMD_SOLICIT_15, + SFX_HFYMD_TAXI_1, + + SFX_WFOBE_BLOCKED_1, + SFX_WFOBE_BLOCKED_2, + SFX_WFOBE_BLOCKED_3, + SFX_WFOBE_BLOCKED_4, + SFX_WFOBE_BLOCKED_5, + SFX_WFOBE_BLOCKED_6, + SFX_WFOBE_BLOCKED_7, + SFX_WFOBE_BLOCKED_8, + SFX_WFOBE_BUMP_1, + SFX_WFOBE_BUMP_2, + SFX_WFOBE_BUMP_3, + SFX_WFOBE_BUMP_4, + SFX_WFOBE_BUMP_5, + SFX_WFOBE_BUMP_6, + SFX_WFOBE_BUMP_7, + SFX_WFOBE_BUMP_8, + SFX_WFOBE_BUMP_9, + SFX_WFOBE_BUMP_10, + SFX_WFOBE_CAR_CRASH_1, + SFX_WFOBE_CAR_CRASH_2, + SFX_WFOBE_CAR_CRASH_3, + SFX_WFOBE_CAR_CRASH_4, + SFX_WFOBE_CAR_CRASH_5, + SFX_WFOBE_CAR_CRASH_6, + SFX_WFOBE_CAR_CRASH_7, + SFX_WFOBE_CHAT_1, + SFX_WFOBE_CHAT_2, + SFX_WFOBE_CHAT_3, + SFX_WFOBE_CHAT_4, + SFX_WFOBE_CHAT_5, + SFX_WFOBE_CHAT_6, + SFX_WFOBE_CHAT_7, + SFX_WFOBE_CHAT_8, + SFX_WFOBE_CHAT_9, + SFX_WFOBE_CHAT_10, + SFX_WFOBE_DODGE_1, + SFX_WFOBE_DODGE_2, + SFX_WFOBE_DODGE_3, + SFX_WFOBE_DODGE_4, + SFX_WFOBE_DODGE_5, + SFX_WFOBE_DODGE_6, + SFX_WFOBE_DODGE_7, + SFX_WFOBE_DODGE_8, + SFX_WFOBE_GENERIC_CRASH_1, + SFX_WFOBE_GENERIC_CRASH_2, + SFX_WFOBE_GENERIC_CRASH_3, + SFX_WFOBE_GENERIC_CRASH_4, + SFX_WFOBE_GENERIC_CRASH_5, + SFX_WFOBE_GENERIC_CRASH_6, + SFX_WFOBE_GENERIC_CRASH_7, + SFX_WFOBE_GENERIC_CRASH_8, + SFX_WFOBE_GENERIC_CRASH_9, + SFX_WFOBE_GENERIC_CRASH_10, + SFX_WFOBE_GUN_PANIC_1, + SFX_WFOBE_GUN_PANIC_2, + SFX_WFOBE_GUN_PANIC_3, + SFX_WFOBE_GUN_PANIC_4, + SFX_WFOBE_GUN_PANIC_5, + SFX_WFOBE_JACKED_1, + SFX_WFOBE_JACKED_2, + SFX_WFOBE_JACKED_3, + SFX_WFOBE_JACKED_4, + SFX_WFOBE_RUN_1, + SFX_WFOBE_RUN_2, + SFX_WFOBE_RUN_3, + SFX_WFOBE_RUN_4, + SFX_WFOBE_RUN_5, + SFX_WFOBE_RUN_6, + SFX_WFOBE_RUN_7, + SFX_WFOBE_SAVED_1, + SFX_WFOBE_SAVED_2, + SFX_WFOBE_SAVED_3, + SFX_WFOBE_SHOCKED_1, + SFX_WFOBE_SHOCKED_2, + SFX_WFOBE_SHOCKED_3, + SFX_WFOBE_TAXI_1, + SFX_WFOBE_TAXI_2, + + SFX_BFYRI_BLOCKED_1, + SFX_BFYRI_BLOCKED_2, + SFX_BFYRI_BLOCKED_3, + SFX_BFYRI_BLOCKED_4, + SFX_BFYRI_BLOCKED_5, + SFX_BFYRI_BLOCKED_6, + SFX_BFYRI_BLOCKED_7, + SFX_BFYRI_BLOCKED_8, + SFX_BFYRI_BLOCKED_9, + SFX_BFYRI_BUMP_1, + SFX_BFYRI_BUMP_2, + SFX_BFYRI_BUMP_3, + SFX_BFYRI_BUMP_4, + SFX_BFYRI_BUMP_5, + SFX_BFYRI_BUMP_6, + SFX_BFYRI_BUMP_7, + SFX_BFYRI_BUMP_8, + SFX_BFYRI_BUMP_9, + SFX_BFYRI_CAR_CRASH_1, + SFX_BFYRI_CAR_CRASH_2, + SFX_BFYRI_CAR_CRASH_3, + SFX_BFYRI_CAR_CRASH_4, + SFX_BFYRI_CAR_CRASH_5, + SFX_BFYRI_CAR_CRASH_6, + SFX_BFYRI_CAR_CRASH_7, + SFX_BFYRI_CAR_CRASH_8, + SFX_BFYRI_DODGE_1, + SFX_BFYRI_DODGE_2, + SFX_BFYRI_DODGE_3, + SFX_BFYRI_DODGE_4, + SFX_BFYRI_DODGE_5, + SFX_BFYRI_DODGE_6, + SFX_BFYRI_DODGE_7, + SFX_BFYRI_DODGE_8, + SFX_BFYRI_EYEING_1, + SFX_BFYRI_EYEING_2, + SFX_BFYRI_EYEING_3, + SFX_BFYRI_GENERIC_CRASH_1, + SFX_BFYRI_GENERIC_CRASH_2, + SFX_BFYRI_GENERIC_CRASH_3, + SFX_BFYRI_GENERIC_CRASH_4, + SFX_BFYRI_GENERIC_CRASH_5, + SFX_BFYRI_GENERIC_CRASH_6, + SFX_BFYRI_GENERIC_CRASH_7, + SFX_BFYRI_GUN_PANIC_1, + SFX_BFYRI_GUN_PANIC_2, + SFX_BFYRI_GUN_PANIC_3, + SFX_BFYRI_GUN_PANIC_4, + SFX_BFYRI_JACKED_1, + SFX_BFYRI_JACKED_2, + SFX_BFYRI_JACKED_3, + SFX_BFYRI_JACKED_4, + SFX_BFYRI_JACKED_5, + SFX_BFYRI_JACKED_6, + SFX_BFYRI_JACKED_7, + SFX_BFYRI_JACKED_8, + SFX_BFYRI_JACKING_1, + SFX_BFYRI_JACKING_2, + SFX_BFYRI_JACKING_3, + SFX_BFYRI_JACKING_4, + SFX_BFYRI_LOST_1, + SFX_BFYRI_LOST_2, + SFX_BFYRI_MUGGED_1, + SFX_BFYRI_MUGGED_2, + SFX_BFYRI_MUGGED_3, + SFX_BFYRI_RUN_1, + SFX_BFYRI_RUN_2, + SFX_BFYRI_RUN_3, + SFX_BFYRI_RUN_4, + SFX_BFYRI_RUN_5, + SFX_BFYRI_RUN_6, + SFX_BFYRI_SAVED_1, + SFX_BFYRI_SAVED_2, + SFX_BFYRI_SHOCKED_1, + SFX_BFYRI_SHOCKED_2, + SFX_BFYRI_SHOCKED_3, + SFX_BFYRI_SHOCKED_4, + SFX_BFYRI_TAXI_1, + + SFX_BFYBE_BLOCKED_1, + SFX_BFYBE_BLOCKED_2, + SFX_BFYBE_BLOCKED_3, + SFX_BFYBE_BLOCKED_4, + SFX_BFYBE_BLOCKED_5, + SFX_BFYBE_BLOCKED_6, + SFX_BFYBE_BLOCKED_7, + SFX_BFYBE_BLOCKED_8, + SFX_BFYBE_BLOCKED_9, + SFX_BFYBE_BLOCKED_10, + SFX_BFYBE_BLOCKED_11, + SFX_BFYBE_BLOCKED_12, + SFX_BFYBE_CAR_CRASH_1, + SFX_BFYBE_CAR_CRASH_2, + SFX_BFYBE_CAR_CRASH_3, + SFX_BFYBE_CAR_CRASH_4, + SFX_BFYBE_CAR_CRASH_5, + SFX_BFYBE_CAR_CRASH_6, + SFX_BFYBE_CAR_CRASH_7, + SFX_BFYBE_CAR_CRASH_8, + SFX_BFYBE_CAR_CRASH_9, + SFX_BFYBE_CAR_CRASH_10, + SFX_BFYBE_CHAT_1, + SFX_BFYBE_CHAT_2, + SFX_BFYBE_CHAT_3, + SFX_BFYBE_CHAT_4, + SFX_BFYBE_CHAT_5, + SFX_BFYBE_CHAT_6, + SFX_BFYBE_CHAT_7, + SFX_BFYBE_CHAT_8, + SFX_BFYBE_CHAT_9, + SFX_BFYBE_CHAT_10, + SFX_BFYBE_CHAT_11, + SFX_BFYBE_CHAT_12, + SFX_BFYBE_CHAT_13, + SFX_BFYBE_CHAT_14, + SFX_BFYBE_CHAT_15, + SFX_BFYBE_CHAT_16, + SFX_BFYBE_DODGE_1, + SFX_BFYBE_DODGE_2, + SFX_BFYBE_DODGE_3, + SFX_BFYBE_DODGE_4, + SFX_BFYBE_DODGE_5, + SFX_BFYBE_DODGE_6, + SFX_BFYBE_DODGE_7, + SFX_BFYBE_DODGE_8, + SFX_BFYBE_DODGE_9, + SFX_BFYBE_DODGE_10, + SFX_BFYBE_EYEING_1, + SFX_BFYBE_EYEING_2, + SFX_BFYBE_EYEING_3, + SFX_BFYBE_EYEING_4, + SFX_BFYBE_GENERIC_CRASH_1, + SFX_BFYBE_GENERIC_CRASH_2, + SFX_BFYBE_GENERIC_CRASH_3, + SFX_BFYBE_GENERIC_CRASH_4, + SFX_BFYBE_GENERIC_CRASH_5, + SFX_BFYBE_GENERIC_CRASH_6, + SFX_BFYBE_GENERIC_CRASH_7, + SFX_BFYBE_GENERIC_CRASH_8, + SFX_BFYBE_GUN_COOL_1, + SFX_BFYBE_GUN_COOL_2, + SFX_BFYBE_GUN_COOL_3, + SFX_BFYBE_GUN_COOL_4, + SFX_BFYBE_GUN_COOL_5, + SFX_BFYBE_GUN_COOL_6, + SFX_BFYBE_JACKED_1, + SFX_BFYBE_JACKED_2, + SFX_BFYBE_JACKED_3, + SFX_BFYBE_JACKED_4, + SFX_BFYBE_JACKED_5, + SFX_BFYBE_JACKED_6, + SFX_BFYBE_JACKED_7, + SFX_BFYBE_JACKED_8, + SFX_BFYBE_LOST_1, + SFX_BFYBE_LOST_2, + SFX_BFYBE_LOST_3, + SFX_BFYBE_LOST_4, + SFX_BFYBE_MUGGED_1, + SFX_BFYBE_MUGGED_2, + SFX_BFYBE_MUGGED_3, + SFX_BFYBE_MUGGED_4, + SFX_BFYBE_MUGGED_5, + SFX_BFYBE_RUN_1, + SFX_BFYBE_RUN_2, + SFX_BFYBE_RUN_3, + SFX_BFYBE_RUN_4, + SFX_BFYBE_RUN_5, + SFX_BFYBE_RUN_6, + SFX_BFYBE_SAVED_1, + SFX_BFYBE_SAVED_2, + SFX_BFYBE_SHOCKED_1, + SFX_BFYBE_SHOCKED_2, + SFX_BFYBE_SHOCKED_3, + SFX_BFYBE_SHOCKED_4, + SFX_BFYBE_TAXI_1, + SFX_BFYBE_TAXI_2, + SFX_BFYBE_TAXI_3, + + SFX_BMOTR_BUMP_1, + SFX_BMOTR_BUMP_2, + SFX_BMOTR_BUMP_3, + SFX_BMOTR_BUMP_4, + SFX_BMOTR_BUMP_5, + SFX_BMOTR_BUMP_6, + SFX_BMOTR_BUMP_7, + SFX_BMOTR_BUMP_8, + SFX_BMOTR_BUMP_9, + SFX_BMOTR_BUMP_10, + SFX_BMOTR_CHAT_1, + SFX_BMOTR_CHAT_2, + SFX_BMOTR_CHAT_3, + SFX_BMOTR_CHAT_4, + SFX_BMOTR_CHAT_5, + SFX_BMOTR_CHAT_6, + SFX_BMOTR_CHAT_7, + SFX_BMOTR_CHAT_8, + SFX_BMOTR_CHAT_9, + SFX_BMOTR_CHAT_10, + SFX_BMOTR_DODGE_1, + SFX_BMOTR_DODGE_2, + SFX_BMOTR_DODGE_3, + SFX_BMOTR_DODGE_4, + SFX_BMOTR_DODGE_5, + SFX_BMOTR_DODGE_6, + SFX_BMOTR_DODGE_7, + SFX_BMOTR_DODGE_8, + SFX_BMOTR_DODGE_9, + SFX_BMOTR_DODGE_10, + SFX_BMOTR_DODGE_11, + SFX_BMOTR_EYEING_1, + SFX_BMOTR_EYEING_2, + SFX_BMOTR_EYEING_3, + SFX_BMOTR_GUN_COOL_1, + SFX_BMOTR_GUN_COOL_2, + SFX_BMOTR_GUN_COOL_3, + SFX_BMOTR_GUN_COOL_4, + SFX_BMOTR_GUN_COOL_5, + SFX_BMOTR_INNOCENT_1, + SFX_BMOTR_INNOCENT_2, + SFX_BMOTR_INNOCENT_3, + SFX_BMOTR_INNOCENT_4, + SFX_BMOTR_RUN_1, + SFX_BMOTR_RUN_2, + SFX_BMOTR_RUN_3, + SFX_BMOTR_RUN_4, + SFX_BMOTR_RUN_5, + SFX_BMOTR_RUN_6, + SFX_BMOTR_RUN_7, + SFX_BMOTR_SAVED_1, + SFX_BMOTR_SOLICIT_1, + SFX_BMOTR_SOLICIT_2, + SFX_BMOTR_SOLICIT_3, + SFX_BMOTR_SOLICIT_4, + SFX_BMOTR_SOLICIT_5, + SFX_BMOTR_SOLICIT_6, + SFX_BMOTR_SOLICIT_7, + SFX_BMOTR_TAXI_1, + + SFX_BMYST_BLOCKED_1, + SFX_BMYST_BLOCKED_2, + SFX_BMYST_BLOCKED_3, + SFX_BMYST_BLOCKED_4, + SFX_BMYST_BLOCKED_5, + SFX_BMYST_BLOCKED_6, + SFX_BMYST_BLOCKED_7, + SFX_BMYST_BLOCKED_8, + SFX_BMYST_BUMP_1, + SFX_BMYST_BUMP_2, + SFX_BMYST_BUMP_3, + SFX_BMYST_BUMP_4, + SFX_BMYST_BUMP_5, + SFX_BMYST_BUMP_6, + SFX_BMYST_BUMP_7, + SFX_BMYST_BUMP_8, + SFX_BMYST_BUMP_9, + SFX_BMYST_BUMP_10, + SFX_BMYST_BUMP_11, + SFX_BMYST_CAR_CRASH_1, + SFX_BMYST_CAR_CRASH_2, + SFX_BMYST_CAR_CRASH_3, + SFX_BMYST_CAR_CRASH_4, + SFX_BMYST_CAR_CRASH_5, + SFX_BMYST_CAR_CRASH_6, + SFX_BMYST_CAR_CRASH_7, + SFX_BMYST_CAR_CRASH_8, + SFX_BMYST_CAR_CRASH_9, + SFX_BMYST_CHAT_1, + SFX_BMYST_CHAT_2, + SFX_BMYST_CHAT_3, + SFX_BMYST_CHAT_4, + SFX_BMYST_CHAT_5, + SFX_BMYST_CHAT_6, + SFX_BMYST_CHAT_7, + SFX_BMYST_CHAT_8, + SFX_BMYST_CHAT_9, + SFX_BMYST_CHAT_10, + SFX_BMYST_CHAT_11, + SFX_BMYST_CHAT_12, + SFX_BMYST_DODGE_1, + SFX_BMYST_DODGE_2, + SFX_BMYST_DODGE_3, + SFX_BMYST_DODGE_4, + SFX_BMYST_DODGE_5, + SFX_BMYST_DODGE_6, + SFX_BMYST_DODGE_7, + SFX_BMYST_DODGE_8, + SFX_BMYST_FIGHT_1, + SFX_BMYST_FIGHT_2, + SFX_BMYST_FIGHT_3, + SFX_BMYST_FIGHT_4, + SFX_BMYST_FIGHT_5, + SFX_BMYST_FIGHT_6, + SFX_BMYST_GENERIC_CRASH_1, + SFX_BMYST_GENERIC_CRASH_2, + SFX_BMYST_GENERIC_CRASH_3, + SFX_BMYST_GENERIC_CRASH_4, + SFX_BMYST_GENERIC_CRASH_5, + SFX_BMYST_GENERIC_CRASH_6, + SFX_BMYST_GENERIC_CRASH_7, + SFX_BMYST_GUN_COOL_1, + SFX_BMYST_GUN_COOL_2, + SFX_BMYST_GUN_COOL_3, + SFX_BMYST_GUN_COOL_4, + SFX_BMYST_GUN_COOL_5, + SFX_BMYST_GUN_COOL_6, + SFX_BMYST_JACKED_1, + SFX_BMYST_JACKED_2, + SFX_BMYST_JACKED_3, + SFX_BMYST_JACKED_4, + SFX_BMYST_JACKED_5, + SFX_BMYST_JACKED_6, + SFX_BMYST_JACKED_7, + SFX_BMYST_JACKED_8, + SFX_BMYST_JACKING_1, + SFX_BMYST_JACKING_2, + SFX_BMYST_JACKING_3, + SFX_BMYST_JACKING_4, + SFX_BMYST_MUGGED_1, + SFX_BMYST_MUGGED_2, + SFX_BMYST_MUGGING_1, + SFX_BMYST_MUGGING_2, + SFX_BMYST_MUGGING_3, + SFX_BMYST_MUGGING_4, + SFX_BMYST_TAXI_1, + SFX_BMYST_TAXI_2, + + SFX_WMYPI_BLOCKED_1, + SFX_WMYPI_BLOCKED_2, + SFX_WMYPI_BLOCKED_3, + SFX_WMYPI_BLOCKED_4, + SFX_WMYPI_BLOCKED_5, + SFX_WMYPI_BLOCKED_6, + SFX_WMYPI_BLOCKED_7, + SFX_WMYPI_BLOCKED_8, + SFX_WMYPI_BUMP_1, + SFX_WMYPI_BUMP_2, + SFX_WMYPI_BUMP_3, + SFX_WMYPI_BUMP_4, + SFX_WMYPI_BUMP_5, + SFX_WMYPI_BUMP_6, + SFX_WMYPI_BUMP_7, + SFX_WMYPI_BUMP_8, + SFX_WMYPI_BUMP_9, + SFX_WMYPI_BUMP_10, + SFX_WMYPI_CAR_CRASH_1, + SFX_WMYPI_CAR_CRASH_2, + SFX_WMYPI_CAR_CRASH_3, + SFX_WMYPI_CAR_CRASH_4, + SFX_WMYPI_CAR_CRASH_5, + SFX_WMYPI_CAR_CRASH_6, + SFX_WMYPI_CAR_CRASH_7, + SFX_WMYPI_CAR_CRASH_8, + SFX_WMYPI_DODGE_1, + SFX_WMYPI_DODGE_2, + SFX_WMYPI_DODGE_3, + SFX_WMYPI_DODGE_4, + SFX_WMYPI_DODGE_5, + SFX_WMYPI_DODGE_6, + SFX_WMYPI_DODGE_7, + SFX_WMYPI_DODGE_8, + SFX_WMYPI_EYEING_1, + SFX_WMYPI_EYEING_2, + SFX_WMYPI_EYEING_3, + SFX_WMYPI_EYEING_4, + SFX_WMYPI_EYEING_5, + SFX_WMYPI_EYEING_6, + SFX_WMYPI_FIGHT_1, + SFX_WMYPI_FIGHT_2, + SFX_WMYPI_FIGHT_3, + SFX_WMYPI_FIGHT_4, + SFX_WMYPI_FIGHT_5, + SFX_WMYPI_FIGHT_6, + SFX_WMYPI_FIGHT_7, + SFX_WMYPI_FIGHT_8, + SFX_WMYPI_FIGHT_9, + SFX_WMYPI_GENERIC_CRASH_1, + SFX_WMYPI_GENERIC_CRASH_2, + SFX_WMYPI_GENERIC_CRASH_3, + SFX_WMYPI_GENERIC_CRASH_4, + SFX_WMYPI_GENERIC_CRASH_5, + SFX_WMYPI_GENERIC_CRASH_6, + SFX_WMYPI_GENERIC_CRASH_7, + SFX_WMYPI_GENERIC_CRASH_8, + SFX_WMYPI_GUN_COOL_1, + SFX_WMYPI_GUN_COOL_2, + SFX_WMYPI_GUN_COOL_3, + SFX_WMYPI_GUN_COOL_4, + SFX_WMYPI_GUN_COOL_5, + SFX_WMYPI_INNOCENT_1, + SFX_WMYPI_INNOCENT_2, + SFX_WMYPI_JACKED_1, + SFX_WMYPI_JACKED_2, + SFX_WMYPI_JACKED_3, + SFX_WMYPI_JACKED_4, + SFX_WMYPI_JACKED_5, + SFX_WMYPI_JACKED_6, + SFX_WMYPI_JACKING_1, + SFX_WMYPI_JACKING_2, + SFX_WMYPI_JACKING_3, + SFX_WMYPI_JACKING_4, + SFX_WMYPI_MUGGED_1, + SFX_WMYPI_MUGGED_2, + SFX_WMYPI_SAVED_1, + SFX_WMYPI_SAVED_2, + SFX_WMYPI_TAXI_1, + SFX_WMYPI_TAXI_2, + SFX_WMYPI_TAXI_3, + SFX_WMYPI_TAXI_4, + + SFX_BMYCR_BLOCKED_1, + SFX_BMYCR_BLOCKED_2, + SFX_BMYCR_BLOCKED_3, + SFX_BMYCR_BLOCKED_4, + SFX_BMYCR_BLOCKED_5, + SFX_BMYCR_BLOCKED_6, + SFX_BMYCR_BLOCKED_7, + SFX_BMYCR_BLOCKED_8, + SFX_BMYCR_BLOCKED_9, + SFX_BMYCR_BLOCKED_10, + SFX_BMYCR_BLOCKED_11, + SFX_BMYCR_BLOCKED_12, + SFX_BMYCR_BUMP_1, + SFX_BMYCR_BUMP_2, + SFX_BMYCR_BUMP_3, + SFX_BMYCR_BUMP_4, + SFX_BMYCR_BUMP_5, + SFX_BMYCR_BUMP_6, + SFX_BMYCR_BUMP_7, + SFX_BMYCR_BUMP_8, + SFX_BMYCR_BUMP_9, + SFX_BMYCR_BUMP_10, + SFX_BMYCR_BUMP_11, + SFX_BMYCR_CAR_CRASH_1, + SFX_BMYCR_CAR_CRASH_2, + SFX_BMYCR_CAR_CRASH_3, + SFX_BMYCR_CAR_CRASH_4, + SFX_BMYCR_CAR_CRASH_5, + SFX_BMYCR_CAR_CRASH_6, + SFX_BMYCR_CAR_CRASH_7, + SFX_BMYCR_CAR_CRASH_8, + SFX_BMYCR_CAR_CRASH_9, + SFX_BMYCR_DODGE_1, + SFX_BMYCR_DODGE_2, + SFX_BMYCR_DODGE_3, + SFX_BMYCR_DODGE_4, + SFX_BMYCR_DODGE_5, + SFX_BMYCR_DODGE_6, + SFX_BMYCR_DODGE_7, + SFX_BMYCR_DODGE_8, + SFX_BMYCR_EYEING_1, + SFX_BMYCR_EYEING_2, + SFX_BMYCR_FIGHT_1, + SFX_BMYCR_FIGHT_2, + SFX_BMYCR_FIGHT_3, + SFX_BMYCR_FIGHT_4, + SFX_BMYCR_FIGHT_5, + SFX_BMYCR_FIGHT_6, + SFX_BMYCR_FIGHT_7, + SFX_BMYCR_FIGHT_8, + SFX_BMYCR_GENERIC_CRASH_1, + SFX_BMYCR_GENERIC_CRASH_2, + SFX_BMYCR_GENERIC_CRASH_3, + SFX_BMYCR_GENERIC_CRASH_4, + SFX_BMYCR_GENERIC_CRASH_5, + SFX_BMYCR_GENERIC_CRASH_6, + SFX_BMYCR_GENERIC_CRASH_7, + SFX_BMYCR_GUN_COOL_1, + SFX_BMYCR_GUN_COOL_2, + SFX_BMYCR_GUN_COOL_3, + SFX_BMYCR_GUN_COOL_4, + SFX_BMYCR_GUN_COOL_5, + SFX_BMYCR_GUN_COOL_6, + SFX_BMYCR_INNOCENT_1, + SFX_BMYCR_INNOCENT_2, + SFX_BMYCR_INNOCENT_3, + SFX_BMYCR_INNOCENT_4, + SFX_BMYCR_JACKED_1, + SFX_BMYCR_JACKED_2, + SFX_BMYCR_JACKED_3, + SFX_BMYCR_JACKED_4, + SFX_BMYCR_JACKED_5, + SFX_BMYCR_JACKED_6, + SFX_BMYCR_JACKING_1, + SFX_BMYCR_JACKING_2, + SFX_BMYCR_JACKING_3, + SFX_BMYCR_JACKING_4, + SFX_BMYCR_JACKING_5, + SFX_BMYCR_JACKING_6, + SFX_BMYCR_JACKING_7, + SFX_BMYCR_JACKING_8, + SFX_BMYCR_JACKING_9, + SFX_BMYCR_JACKING_10, + SFX_BMYCR_JACKING_11, + SFX_BMYCR_JACKING_12, + SFX_BMYCR_MUGGED_1, + SFX_BMYCR_MUGGED_2, + SFX_BMYCR_MUGGED_3, + SFX_BMYCR_MUGGING_1, + SFX_BMYCR_MUGGING_2, + SFX_BMYCR_MUGGING_3, + SFX_BMYCR_MUGGING_4, + SFX_BMYCR_MUGGING_5, + SFX_BMYCR_MUGGING_6, + SFX_BMYCR_SAVED_1, + SFX_BMYCR_SAVED_2, + + SFX_WMORI_BLOCKED_1, + SFX_WMORI_BLOCKED_2, + SFX_WMORI_BLOCKED_3, + SFX_WMORI_BLOCKED_4, + SFX_WMORI_BLOCKED_5, + SFX_WMORI_BLOCKED_6, + SFX_WMORI_BLOCKED_7, + SFX_WMORI_BLOCKED_8, + SFX_WMORI_BLOCKED_9, + SFX_WMORI_BLOCKED_10, + SFX_WMORI_BUMP_1, + SFX_WMORI_BUMP_2, + SFX_WMORI_BUMP_3, + SFX_WMORI_BUMP_4, + SFX_WMORI_BUMP_5, + SFX_WMORI_BUMP_6, + SFX_WMORI_BUMP_7, + SFX_WMORI_BUMP_8, + SFX_WMORI_BUMP_9, + SFX_WMORI_BUMP_10, + SFX_WMORI_BUMP_11, + SFX_WMORI_BUMP_12, + SFX_WMORI_BUMP_13, + SFX_WMORI_BUMP_14, + SFX_WMORI_CAR_CRASH_1, + SFX_WMORI_CAR_CRASH_2, + SFX_WMORI_CAR_CRASH_3, + SFX_WMORI_CAR_CRASH_4, + SFX_WMORI_CAR_CRASH_5, + SFX_WMORI_CAR_CRASH_6, + SFX_WMORI_DODGE_1, + SFX_WMORI_DODGE_2, + SFX_WMORI_DODGE_3, + SFX_WMORI_DODGE_4, + SFX_WMORI_DODGE_5, + SFX_WMORI_DODGE_6, + SFX_WMORI_DODGE_7, + SFX_WMORI_DODGE_8, + SFX_WMORI_DODGE_9, + SFX_WMORI_DODGE_10, + SFX_WMORI_EYEING_1, + SFX_WMORI_EYEING_2, + SFX_WMORI_EYEING_3, + SFX_WMORI_GENERIC_CRASH_1, + SFX_WMORI_GENERIC_CRASH_2, + SFX_WMORI_GENERIC_CRASH_3, + SFX_WMORI_GENERIC_CRASH_4, + SFX_WMORI_GENERIC_CRASH_5, + SFX_WMORI_GENERIC_CRASH_6, + SFX_WMORI_GENERIC_CRASH_7, + SFX_WMORI_GENERIC_CRASH_8, + SFX_WMORI_GUN_PANIC_1, + SFX_WMORI_GUN_PANIC_2, + SFX_WMORI_GUN_PANIC_3, + SFX_WMORI_GUN_PANIC_4, + SFX_WMORI_GUN_PANIC_5, + SFX_WMORI_GUN_PANIC_6, + SFX_WMORI_GUN_PANIC_7, + SFX_WMORI_GUN_PANIC_8, + SFX_WMORI_GUN_PANIC_9, + SFX_WMORI_JACKED_1, + SFX_WMORI_JACKED_2, + SFX_WMORI_JACKED_3, + SFX_WMORI_JACKED_4, + SFX_WMORI_JACKED_5, + SFX_WMORI_JACKED_6, + SFX_WMORI_LOST_1, + SFX_WMORI_LOST_2, + SFX_WMORI_MUGGED_1, + SFX_WMORI_MUGGED_2, + SFX_WMORI_MUGGED_3, + SFX_WMORI_MUGGED_4, + SFX_WMORI_RUN_1, + SFX_WMORI_RUN_2, + SFX_WMORI_RUN_3, + SFX_WMORI_RUN_4, + SFX_WMORI_RUN_5, + SFX_WMORI_RUN_6, + SFX_WMORI_RUN_7, + SFX_WMORI_RUN_8, + SFX_WMORI_RUN_9, + SFX_WMORI_RUN_10, + SFX_WMORI_RUN_11, + SFX_WMORI_RUN_12, + SFX_WMORI_SAVED_1, + SFX_WMORI_SAVED_2, + SFX_WMORI_SHOCKED_1, + SFX_WMORI_SHOCKED_2, + SFX_WMORI_SHOCKED_3, + SFX_WMORI_SHOCKED_4, + SFX_WMORI_TAXI_1, + SFX_WMORI_TAXI_2, + + SFX_WMOBU_BLOCKED_1, + SFX_WMOBU_BLOCKED_2, + SFX_WMOBU_BLOCKED_3, + SFX_WMOBU_BLOCKED_4, + SFX_WMOBU_BLOCKED_5, + SFX_WMOBU_BLOCKED_6, + SFX_WMOBU_BLOCKED_7, + SFX_WMOBU_BUMP_1, + SFX_WMOBU_BUMP_2, + SFX_WMOBU_BUMP_3, + SFX_WMOBU_BUMP_4, + SFX_WMOBU_BUMP_5, + SFX_WMOBU_BUMP_6, + SFX_WMOBU_BUMP_7, + SFX_WMOBU_BUMP_8, + SFX_WMOBU_BUMP_9, + SFX_WMOBU_BUMP_10, + SFX_WMOBU_CAR_CRASH_1, + SFX_WMOBU_CAR_CRASH_2, + SFX_WMOBU_CAR_CRASH_3, + SFX_WMOBU_CAR_CRASH_4, + SFX_WMOBU_CAR_CRASH_5, + SFX_WMOBU_CAR_CRASH_6, + SFX_WMOBU_CAR_CRASH_7, + SFX_WMOBU_DODGE_1, + SFX_WMOBU_DODGE_2, + SFX_WMOBU_DODGE_3, + SFX_WMOBU_DODGE_4, + SFX_WMOBU_DODGE_5, + SFX_WMOBU_DODGE_6, + SFX_WMOBU_DODGE_7, + SFX_WMOBU_DODGE_8, + SFX_WMOBU_EYEING_1, + SFX_WMOBU_EYEING_2, + SFX_WMOBU_FIGHT_1, + SFX_WMOBU_FIGHT_2, + SFX_WMOBU_FIGHT_3, + SFX_WMOBU_GENERIC_CRASH_1, + SFX_WMOBU_GENERIC_CRASH_2, + SFX_WMOBU_GENERIC_CRASH_3, + SFX_WMOBU_GENERIC_CRASH_4, + SFX_WMOBU_GENERIC_CRASH_5, + SFX_WMOBU_GENERIC_CRASH_6, + SFX_WMOBU_GENERIC_CRASH_7, + SFX_WMOBU_GUN_PANIC_1, + SFX_WMOBU_GUN_PANIC_2, + SFX_WMOBU_GUN_PANIC_3, + SFX_WMOBU_GUN_PANIC_4, + SFX_WMOBU_GUN_PANIC_5, + SFX_WMOBU_GUN_PANIC_6, + SFX_WMOBU_JACKED_1, + SFX_WMOBU_JACKED_2, + SFX_WMOBU_JACKED_3, + SFX_WMOBU_JACKED_4, + SFX_WMOBU_JACKED_5, + SFX_WMOBU_JACKED_6, + SFX_WMOBU_JACKED_7, + SFX_WMOBU_LOST_1, + SFX_WMOBU_LOST_2, + SFX_WMOBU_LOST_3, + SFX_WMOBU_MUGGED_1, + SFX_WMOBU_MUGGED_2, + SFX_WMOBU_SAVED_1, + SFX_WMOBU_SAVED_2, + SFX_WMOBU_SAVED_3, + SFX_WMOBU_TAXI_1, + SFX_WMOBU_TAXI_2, + + SFX_BMODK_BLOCKED_1, + SFX_BMODK_BLOCKED_2, + SFX_BMODK_BLOCKED_3, + SFX_BMODK_BLOCKED_4, + SFX_BMODK_BLOCKED_5, + SFX_BMODK_BLOCKED_6, + SFX_BMODK_BLOCKED_7, + SFX_BMODK_BLOCKED_8, + SFX_BMODK_BUMP_1, + SFX_BMODK_BUMP_2, + SFX_BMODK_BUMP_3, + SFX_BMODK_BUMP_4, + SFX_BMODK_BUMP_5, + SFX_BMODK_BUMP_6, + SFX_BMODK_BUMP_7, + SFX_BMODK_BUMP_8, + SFX_BMODK_BUMP_9, + SFX_BMODK_BUMP_10, + SFX_BMODK_CAR_CRASH_1, + SFX_BMODK_CAR_CRASH_2, + SFX_BMODK_CAR_CRASH_3, + SFX_BMODK_CAR_CRASH_4, + SFX_BMODK_CAR_CRASH_5, + SFX_BMODK_CAR_CRASH_6, + SFX_BMODK_CAR_CRASH_7, + SFX_BMODK_CAR_CRASH_8, + SFX_BMODK_CAR_CRASH_9, + SFX_BMODK_CAR_CRASH_10, + SFX_BMODK_UNK, // UNUSED + SFX_BMODK_UNK_147_1, + SFX_BMODK_UNK_147_2, + SFX_BMODK_UNK_147_3, + SFX_BMODK_UNK_147_4, + SFX_BMODK_UNK_147_5, + SFX_BMODK_UNK_147_6, + SFX_BMODK_UNK_147_7, + SFX_BMODK_UNK_147_8, + SFX_BMODK_UNK_147_9, + SFX_BMODK_UNK_147_10, + SFX_BMODK_UNK_147_11, + SFX_BMODK_UNK_147_12, + SFX_BMODK_DODGE_1, + SFX_BMODK_DODGE_2, + SFX_BMODK_DODGE_3, + SFX_BMODK_DODGE_4, + SFX_BMODK_DODGE_5, + SFX_BMODK_DODGE_6, + SFX_BMODK_DODGE_7, + SFX_BMODK_GENERIC_CRASH_1, + SFX_BMODK_GENERIC_CRASH_2, + SFX_BMODK_GENERIC_CRASH_3, + SFX_BMODK_GENERIC_CRASH_4, + SFX_BMODK_GENERIC_CRASH_5, + SFX_BMODK_GENERIC_CRASH_6, + SFX_BMODK_GENERIC_CRASH_7, + SFX_BMODK_GUN_PANIC_1, + SFX_BMODK_GUN_PANIC_2, + SFX_BMODK_GUN_PANIC_3, + SFX_BMODK_GUN_PANIC_4, + SFX_BMODK_INNOCENT_1, + SFX_BMODK_INNOCENT_2, + SFX_BMODK_INNOCENT_3, + SFX_BMODK_JACKED_1, + SFX_BMODK_JACKED_2, + SFX_BMODK_JACKED_3, + SFX_BMODK_JACKED_4, + SFX_BMODK_JACKED_5, + SFX_BMODK_JACKED_6, + SFX_BMODK_JACKED_7, + SFX_BMODK_JACKED_8, + SFX_BMODK_JACKED_9, + SFX_BMODK_MUGGED_1, + SFX_BMODK_MUGGED_2, + SFX_BMODK_RUN_1, + SFX_BMODK_RUN_2, + SFX_BMODK_RUN_3, + SFX_BMODK_RUN_4, + SFX_BMODK_TAXI_1, + + SFX_HFYBE_BUMP_1, + SFX_HFYBE_BUMP_2, + SFX_HFYBE_BUMP_3, + SFX_HFYBE_BUMP_4, + SFX_HFYBE_BUMP_5, + SFX_HFYBE_BUMP_6, + SFX_HFYBE_BUMP_7, + SFX_HFYBE_BUMP_8, + SFX_HFYBE_CAR_CRASH_1, + SFX_HFYBE_CAR_CRASH_2, + SFX_HFYBE_CAR_CRASH_3, + SFX_HFYBE_CAR_CRASH_4, + SFX_HFYBE_CAR_CRASH_5, + SFX_HFYBE_CAR_CRASH_6, + SFX_HFYBE_CHAT_1, + SFX_HFYBE_CHAT_2, + SFX_HFYBE_CHAT_3, + SFX_HFYBE_CHAT_4, + SFX_HFYBE_CHAT_5, + SFX_HFYBE_CHAT_6, + SFX_HFYBE_CHAT_7, + SFX_HFYBE_CHAT_8, + SFX_HFYBE_CHAT_9, + SFX_HFYBE_CHAT_10, + SFX_HFYBE_DODGE_1, + SFX_HFYBE_DODGE_2, + SFX_HFYBE_DODGE_3, + SFX_HFYBE_DODGE_4, + SFX_HFYBE_DODGE_5, + SFX_HFYBE_DODGE_6, + SFX_HFYBE_DODGE_7, + SFX_HFYBE_DODGE_8, + SFX_HFYBE_DODGE_9, + SFX_HFYBE_DODGE_10, + SFX_HFYBE_DODGE_11, + SFX_HFYBE_GENERIC_CRASH_1, + SFX_HFYBE_GENERIC_CRASH_2, + SFX_HFYBE_GENERIC_CRASH_3, + SFX_HFYBE_GENERIC_CRASH_4, + SFX_HFYBE_GENERIC_CRASH_5, + SFX_HFYBE_GENERIC_CRASH_6, + SFX_HFYBE_GENERIC_CRASH_7, + SFX_HFYBE_GENERIC_CRASH_8, + SFX_HFYBE_GUN_PANIC_1, + SFX_HFYBE_GUN_PANIC_2, + SFX_HFYBE_GUN_PANIC_3, + SFX_HFYBE_GUN_PANIC_4, + SFX_HFYBE_GUN_PANIC_5, + SFX_HFYBE_GUN_PANIC_6, + SFX_HFYBE_GUN_PANIC_7, + SFX_HFYBE_JACKED_1, + SFX_HFYBE_JACKED_2, + SFX_HFYBE_JACKED_3, + SFX_HFYBE_JACKED_4, + SFX_HFYBE_JACKED_5, + SFX_HFYBE_JACKED_6, + SFX_HFYBE_JACKED_7, + SFX_HFYBE_LOST_1, + SFX_HFYBE_LOST_2, + + // this is a guess, idk what she's saying + SFX_HFYBE_RUN_1, + SFX_HFYBE_RUN_2, + SFX_HFYBE_RUN_3, + SFX_HFYBE_RUN_4, + SFX_HFYBE_RUN_5, + SFX_HFYBE_RUN_6, + SFX_HFYBE_RUN_7, + + SFX_HFYBE_SHOCKED_1, + SFX_HFYBE_SHOCKED_2, + SFX_HFYBE_TAXI_1, + + SFX_HFYRI_BLOCKED_1, + SFX_HFYRI_BLOCKED_2, + SFX_HFYRI_BLOCKED_3, + SFX_HFYRI_BLOCKED_4, + SFX_HFYRI_BLOCKED_5, + SFX_HFYRI_BLOCKED_6, + SFX_HFYRI_BLOCKED_7, + SFX_HFYRI_BLOCKED_8, + SFX_HFYRI_BUMP_1, + SFX_HFYRI_BUMP_2, + SFX_HFYRI_BUMP_3, + SFX_HFYRI_BUMP_4, + SFX_HFYRI_BUMP_5, + SFX_HFYRI_BUMP_6, + SFX_HFYRI_BUMP_7, + SFX_HFYRI_BUMP_8, + SFX_HFYRI_BUMP_9, + SFX_HFYRI_CAR_CRASH_1, + SFX_HFYRI_CAR_CRASH_2, + SFX_HFYRI_CAR_CRASH_3, + SFX_HFYRI_CAR_CRASH_4, + SFX_HFYRI_CAR_CRASH_5, + SFX_HFYRI_CAR_CRASH_6, + SFX_HFYRI_CAR_CRASH_7, + SFX_HFYRI_CAR_CRASH_8, + SFX_HFYRI_DODGE_1, + SFX_HFYRI_DODGE_2, + SFX_HFYRI_DODGE_3, + SFX_HFYRI_DODGE_4, + SFX_HFYRI_DODGE_5, + SFX_HFYRI_DODGE_6, + SFX_HFYRI_DODGE_7, + SFX_HFYRI_DODGE_8, + SFX_HFYRI_DODGE_9, + SFX_HFYRI_DODGE_10, + SFX_HFYRI_GENERIC_CRASH_1, + SFX_HFYRI_GENERIC_CRASH_2, + SFX_HFYRI_GENERIC_CRASH_3, + SFX_HFYRI_GENERIC_CRASH_4, + SFX_HFYRI_GENERIC_CRASH_5, + SFX_HFYRI_GENERIC_CRASH_6, + SFX_HFYRI_GENERIC_CRASH_7, + SFX_HFYRI_GUN_PANIC_1, + SFX_HFYRI_GUN_PANIC_2, + SFX_HFYRI_GUN_PANIC_3, + SFX_HFYRI_GUN_PANIC_4, + SFX_HFYRI_GUN_PANIC_5, + SFX_HFYRI_JACKED_1, + SFX_HFYRI_JACKED_2, + SFX_HFYRI_JACKED_3, + SFX_HFYRI_JACKED_4, + SFX_HFYRI_JACKED_5, + SFX_HFYRI_JACKED_6, + SFX_HFYRI_LOST_1, + SFX_HFYRI_LOST_2, + SFX_HFYRI_MUGGED_1, + SFX_HFYRI_MUGGED_2, + SFX_HFYRI_MUGGED_3, + SFX_HFYRI_MUGGED_4, + SFX_HFYRI_RUN_1, + SFX_HFYRI_RUN_2, + SFX_HFYRI_RUN_3, + SFX_HFYRI_RUN_4, + SFX_HFYRI_SAVED_1, + SFX_HFYRI_SAVED_2, + SFX_HFYRI_SHOCKED_1, + SFX_HFYRI_SHOCKED_2, + SFX_HFYRI_SHOCKED_3, + SFX_HFYRI_TAXI_1, + SFX_BFOST_BLOCKED_1, + SFX_BFOST_BLOCKED_2, + SFX_BFOST_BLOCKED_3, + SFX_BFOST_BLOCKED_4, + SFX_BFOST_BLOCKED_5, + SFX_BFOST_BLOCKED_6, + SFX_BFOST_BLOCKED_7, + SFX_BFOST_BUMP_1, + SFX_BFOST_BUMP_2, + SFX_BFOST_BUMP_3, + SFX_BFOST_BUMP_4, + SFX_BFOST_BUMP_5, + SFX_BFOST_BUMP_6, + SFX_BFOST_BUMP_7, + SFX_BFOST_BUMP_8, + SFX_BFOST_BUMP_9, + SFX_BFOST_BUMP_10, + SFX_BFOST_CAR_CRASH_1, + SFX_BFOST_CAR_CRASH_2, + SFX_BFOST_CAR_CRASH_3, + SFX_BFOST_CAR_CRASH_4, + SFX_BFOST_CAR_CRASH_5, + SFX_BFOST_CAR_CRASH_6, + SFX_BFOST_CAR_CRASH_7, + SFX_BFOST_CAR_CRASH_8, + SFX_BFOST_CHAT_1, + SFX_BFOST_CHAT_2, + SFX_BFOST_CHAT_3, + SFX_BFOST_CHAT_4, + SFX_BFOST_CHAT_5, + SFX_BFOST_CHAT_6, + SFX_BFOST_CHAT_7, + SFX_BFOST_CHAT_8, + SFX_BFOST_CHAT_9, + SFX_BFOST_CHAT_10, + SFX_BFOST_DODGE_1, + SFX_BFOST_DODGE_2, + SFX_BFOST_DODGE_3, + SFX_BFOST_DODGE_4, + SFX_BFOST_DODGE_5, + SFX_BFOST_DODGE_6, + SFX_BFOST_DODGE_7, + SFX_BFOST_DODGE_8, + SFX_BFOST_DODGE_9, + SFX_BFOST_DODGE_10, + SFX_BFOST_DODGE_11, + SFX_BFOST_GENERIC_CRASH_1, + SFX_BFOST_GENERIC_CRASH_2, + SFX_BFOST_GENERIC_CRASH_3, + SFX_BFOST_GENERIC_CRASH_4, + SFX_BFOST_GENERIC_CRASH_5, + SFX_BFOST_GENERIC_CRASH_6, + SFX_BFOST_GENERIC_CRASH_7, + SFX_BFOST_GENERIC_CRASH_8, + SFX_BFOST_GUN_PANIC_1, + SFX_BFOST_GUN_PANIC_2, + SFX_BFOST_GUN_PANIC_3, + SFX_BFOST_GUN_PANIC_4, + SFX_BFOST_GUN_PANIC_5, + SFX_BFOST_JACKED_1, + SFX_BFOST_JACKED_2, + SFX_BFOST_JACKED_3, + SFX_BFOST_JACKED_4, + SFX_BFOST_JACKED_5, + SFX_BFOST_JACKED_6, + SFX_BFOST_JACKED_7, + SFX_BFOST_JACKED_8, + SFX_BFOST_LOST_1, + SFX_BFOST_LOST_2, + SFX_BFOST_MUGGED_1, + SFX_BFOST_MUGGED_2, + SFX_BFOST_RUN_1, + SFX_BFOST_RUN_2, + SFX_BFOST_RUN_3, + SFX_BFOST_RUN_4, + SFX_BFOST_SAVED_1, + SFX_BFOST_SAVED_2, + SFX_BFOST_TAXI_1, + SFX_BFORI_BLOCKED_1, + SFX_BFORI_BLOCKED_2, + SFX_BFORI_BLOCKED_3, + SFX_BFORI_BLOCKED_4, + SFX_BFORI_BLOCKED_5, + SFX_BFORI_BLOCKED_6, + SFX_BFORI_BLOCKED_7, + SFX_BFORI_BLOCKED_8, + SFX_BFORI_BUMP_1, + SFX_BFORI_BUMP_2, + SFX_BFORI_BUMP_3, + SFX_BFORI_BUMP_4, + SFX_BFORI_BUMP_5, + SFX_BFORI_BUMP_6, + SFX_BFORI_BUMP_7, + SFX_BFORI_BUMP_8, + SFX_BFORI_BUMP_9, + SFX_BFORI_CAR_CRASH_1, + SFX_BFORI_CAR_CRASH_2, + SFX_BFORI_CAR_CRASH_3, + SFX_BFORI_CAR_CRASH_4, + SFX_BFORI_CAR_CRASH_5, + SFX_BFORI_CAR_CRASH_6, + SFX_BFORI_CAR_CRASH_7, + SFX_BFORI_DODGE_1, + SFX_BFORI_DODGE_2, + SFX_BFORI_DODGE_3, + SFX_BFORI_DODGE_4, + SFX_BFORI_DODGE_5, + SFX_BFORI_DODGE_6, + SFX_BFORI_DODGE_7, + SFX_BFORI_DODGE_8, + SFX_BFORI_DODGE_9, + SFX_BFORI_GENERIC_CRASH_1, + SFX_BFORI_GENERIC_CRASH_2, + SFX_BFORI_GENERIC_CRASH_3, + SFX_BFORI_GENERIC_CRASH_4, + SFX_BFORI_GENERIC_CRASH_5, + SFX_BFORI_GENERIC_CRASH_6, + SFX_BFORI_GENERIC_CRASH_7, + SFX_BFORI_GUN_PANIC_1, + SFX_BFORI_GUN_PANIC_2, + SFX_BFORI_GUN_PANIC_3, + SFX_BFORI_GUN_PANIC_4, + SFX_BFORI_GUN_PANIC_5, + SFX_BFORI_JACKED_1, + SFX_BFORI_JACKED_2, + SFX_BFORI_JACKED_3, + SFX_BFORI_JACKED_4, + SFX_BFORI_LOST_1, + SFX_BFORI_LOST_2, + SFX_BFORI_MUGGED_1, + SFX_BFORI_MUGGED_2, + SFX_BFORI_RUN_1, + SFX_BFORI_RUN_2, + SFX_BFORI_RUN_3, + SFX_BFORI_RUN_4, + SFX_BFORI_SAVED_1, + SFX_BFORI_SHOCKED_1, + SFX_BFORI_SHOCKED_2, + SFX_BFORI_TAXI_1, + SFX_BFORI_TAXI_2, + + SFX_BFYST_BLOCKED_1, + SFX_BFYST_BLOCKED_2, + SFX_BFYST_BLOCKED_3, + SFX_BFYST_BLOCKED_4, + SFX_BFYST_BLOCKED_5, + SFX_BFYST_BLOCKED_6, + SFX_BFYST_BLOCKED_7, + SFX_BFYST_BLOCKED_8, + SFX_BFYST_BUMP_1, + SFX_BFYST_BUMP_2, + SFX_BFYST_BUMP_3, + SFX_BFYST_BUMP_4, + SFX_BFYST_BUMP_5, + SFX_BFYST_BUMP_6, + SFX_BFYST_BUMP_7, + SFX_BFYST_BUMP_8, + SFX_BFYST_BUMP_9, + SFX_BFYST_CAR_CRASH_1, + SFX_BFYST_CAR_CRASH_2, + SFX_BFYST_CAR_CRASH_3, + SFX_BFYST_CAR_CRASH_4, + SFX_BFYST_CAR_CRASH_5, + SFX_BFYST_CAR_CRASH_6, + SFX_BFYST_CAR_CRASH_7, + SFX_BFYST_CAR_CRASH_8, + SFX_BFYST_CAR_CRASH_9, + SFX_BFYST_CHAT_1, + SFX_BFYST_CHAT_2, + SFX_BFYST_CHAT_3, + SFX_BFYST_CHAT_4, + SFX_BFYST_CHAT_5, + SFX_BFYST_CHAT_6, + SFX_BFYST_CHAT_7, + SFX_BFYST_CHAT_8, + SFX_BFYST_CHAT_9, + SFX_BFYST_DODGE_1, + SFX_BFYST_DODGE_2, + SFX_BFYST_DODGE_3, + SFX_BFYST_DODGE_4, + SFX_BFYST_DODGE_5, + SFX_BFYST_DODGE_6, + SFX_BFYST_DODGE_7, + SFX_BFYST_DODGE_8, + SFX_BFYST_DODGE_9, + SFX_BFYST_GENERIC_CRASH_1, + SFX_BFYST_GENERIC_CRASH_2, + SFX_BFYST_GENERIC_CRASH_3, + SFX_BFYST_GENERIC_CRASH_4, + SFX_BFYST_GENERIC_CRASH_5, + SFX_BFYST_GENERIC_CRASH_6, + SFX_BFYST_GENERIC_CRASH_7, + SFX_BFYST_GENERIC_CRASH_8, + SFX_BFYST_GUN_PANIC_1, + SFX_BFYST_GUN_PANIC_2, + SFX_BFYST_GUN_PANIC_3, + SFX_BFYST_GUN_PANIC_4, + SFX_BFYST_JACKED_1, + SFX_BFYST_JACKED_2, + SFX_BFYST_JACKED_3, + SFX_BFYST_JACKED_4, + SFX_BFYST_JACKED_5, + SFX_BFYST_LOST_1, + SFX_BFYST_LOST_2, + SFX_BFYST_MUGGED_1, + SFX_BFYST_MUGGED_2, + SFX_BFYST_RUN_1, + SFX_BFYST_RUN_2, + SFX_BFYST_RUN_3, + SFX_BFYST_RUN_4, + SFX_BFYST_RUN_5, + SFX_BFYST_RUN_6, + SFX_BFYST_SAVED_1, + SFX_BFYST_SAVED_2, + SFX_BFYST_TAXI_1, + + SFX_HFORI_BLOCKED_1, + SFX_HFORI_BLOCKED_2, + SFX_HFORI_BLOCKED_3, + SFX_HFORI_BLOCKED_4, + SFX_HFORI_BLOCKED_5, + SFX_HFORI_BLOCKED_6, + SFX_HFORI_BUMP_1, + SFX_HFORI_BUMP_2, + SFX_HFORI_BUMP_3, + SFX_HFORI_BUMP_4, + SFX_HFORI_BUMP_5, + SFX_HFORI_BUMP_6, + SFX_HFORI_BUMP_7, + SFX_HFORI_BUMP_8, + SFX_HFORI_BUMP_9, + SFX_HFORI_BUMP_10, + SFX_HFORI_CAR_CRASH_1, + SFX_HFORI_CAR_CRASH_2, + SFX_HFORI_CAR_CRASH_3, + SFX_HFORI_CAR_CRASH_4, + SFX_HFORI_CAR_CRASH_5, + SFX_HFORI_CAR_CRASH_6, + SFX_HFORI_CAR_CRASH_7, + SFX_HFORI_DODGE_1, + SFX_HFORI_DODGE_2, + SFX_HFORI_DODGE_3, + SFX_HFORI_DODGE_4, + SFX_HFORI_DODGE_5, + SFX_HFORI_DODGE_6, + SFX_HFORI_EYEING_1, + SFX_HFORI_EYEING_2, + SFX_HFORI_GENERIC_CRASH_1, + SFX_HFORI_GENERIC_CRASH_2, + SFX_HFORI_GENERIC_CRASH_3, + SFX_HFORI_GENERIC_CRASH_4, + SFX_HFORI_GENERIC_CRASH_5, + SFX_HFORI_GENERIC_CRASH_6, + SFX_HFORI_GENERIC_CRASH_7, + SFX_HFORI_GUN_PANIC_1, + SFX_HFORI_GUN_PANIC_2, + SFX_HFORI_GUN_PANIC_3, + SFX_HFORI_GUN_PANIC_4, + SFX_HFORI_GUN_PANIC_5, + SFX_HFORI_GUN_PANIC_6, + SFX_HFORI_JACKED_1, + SFX_HFORI_JACKED_2, + SFX_HFORI_JACKED_3, + SFX_HFORI_JACKED_4, + SFX_HFORI_JACKED_5, + SFX_HFORI_JACKED_6, + SFX_HFORI_JACKED_7, + SFX_HFORI_JACKED_8, + SFX_HFORI_JACKED_9, + SFX_HFORI_LOST_1, + SFX_HFORI_LOST_2, + SFX_HFORI_MUGGED_1, + SFX_HFORI_MUGGED_2, + SFX_HFORI_RUN_1, + SFX_HFORI_RUN_2, + SFX_HFORI_RUN_3, + SFX_HFORI_RUN_4, + SFX_HFORI_SAVED_1, + SFX_HFORI_SHOCKED_1, + SFX_HFORI_SHOCKED_2, + SFX_HFORI_TAXI_1, + + SFX_WFYBU_BUMP_1, + SFX_WFYBU_BUMP_2, + SFX_WFYBU_BUMP_3, + SFX_WFYBU_BUMP_4, + SFX_WFYBU_BUMP_5, + SFX_WFYBU_BUMP_6, + SFX_WFYBU_BUMP_7, + SFX_WFYBU_BUMP_8, + SFX_WFYBU_BUMP_9, + SFX_WFYBU_BUMP_10, + SFX_WFYBU_BUMP_11, + SFX_WFYBU_BUMP_12, + SFX_WFYBU_BUMP_13, + SFX_WFYBU_BUMP_14, + SFX_WFYBU_BUMP_15, + SFX_WFYBU_BUMP_16, + SFX_WFYBU_BUMP_17, + SFX_WFYBU_BUMP_18, + SFX_WFYBU_BUMP_19, + SFX_WFYBU_BUMP_20, + SFX_WFYBU_BUMP_21, + SFX_WFYBU_CAR_CRASH_1, + SFX_WFYBU_CAR_CRASH_2, + SFX_WFYBU_CAR_CRASH_3, + SFX_WFYBU_CAR_CRASH_4, + SFX_WFYBU_CAR_CRASH_5, + SFX_WFYBU_CAR_CRASH_6, + SFX_WFYBU_CAR_CRASH_7, + SFX_WFYBU_CAR_CRASH_8, + SFX_WFYBU_CAR_CRASH_9, + SFX_WFYBU_GENERIC_CRASH_1, + SFX_WFYBU_GENERIC_CRASH_2, + SFX_WFYBU_GENERIC_CRASH_3, + SFX_WFYBU_GENERIC_CRASH_4, + SFX_WFYBU_GENERIC_CRASH_5, + SFX_WFYBU_GENERIC_CRASH_6, + SFX_WFYBU_GENERIC_CRASH_7, + SFX_WFYBU_GENERIC_CRASH_8, + SFX_WFYBU_GUN_PANIC_1, + SFX_WFYBU_GUN_PANIC_2, + SFX_WFYBU_GUN_PANIC_3, + SFX_WFYBU_GUN_PANIC_4, + SFX_WFYBU_GUN_PANIC_5, + SFX_WFYBU_GUN_PANIC_6, + SFX_WFYBU_GUN_PANIC_7, + SFX_WFYBU_GUN_PANIC_8, + SFX_WFYBU_JACKED_1, + SFX_WFYBU_JACKED_2, + SFX_WFYBU_JACKED_3, + SFX_WFYBU_JACKED_4, + SFX_WFYBU_JACKED_5, + SFX_WFYBU_JACKED_6, + SFX_WFYBU_JACKED_7, + SFX_WFYBU_JACKED_8, + SFX_WFYBU_MUGGED_1, + SFX_WFYBU_MUGGED_2, + SFX_WFYBU_MUGGED_3, + SFX_WFYBU_MUGGED_4, + SFX_WFYBU_RUN_1, + SFX_WFYBU_RUN_2, + SFX_WFYBU_RUN_3, + SFX_WFYBU_RUN_4, + SFX_WFYBU_RUN_5, + SFX_WFYBU_RUN_6, + SFX_WFYBU_RUN_7, + SFX_WFYBU_RUN_8, + SFX_WFYBU_SHOCKED_1, + SFX_WFYBU_SHOCKED_2, + SFX_WFYBU_SHOCKED_3, + SFX_WFYBU_TAXI_1, + SFX_WFYBU_TAXI_2, + + SFX_WFOTR_BUMP_1, + SFX_WFOTR_BUMP_2, + SFX_WFOTR_BUMP_3, + SFX_WFOTR_BUMP_4, + SFX_WFOTR_BUMP_5, + SFX_WFOTR_BUMP_6, + SFX_WFOTR_BUMP_7, + SFX_WFOTR_BUMP_8, + SFX_WFOTR_BUMP_9, + SFX_WFOTR_BUMP_10, + SFX_WFOTR_BUMP_11, + SFX_WFOTR_CHAT_1, + SFX_WFOTR_CHAT_2, + SFX_WFOTR_CHAT_3, + SFX_WFOTR_CHAT_4, + SFX_WFOTR_CHAT_5, + SFX_WFOTR_CHAT_6, + SFX_WFOTR_CHAT_7, + SFX_WFOTR_CHAT_8, + SFX_WFOTR_CHAT_9, + SFX_WFOTR_DODGE_1, + SFX_WFOTR_DODGE_2, + SFX_WFOTR_DODGE_3, + SFX_WFOTR_DODGE_4, + SFX_WFOTR_DODGE_5, + SFX_WFOTR_DODGE_6, + SFX_WFOTR_DODGE_7, + SFX_WFOTR_DODGE_8, + SFX_WFOTR_DODGE_9, + SFX_WFOTR_GUN_COOL_1, + SFX_WFOTR_GUN_COOL_2, + SFX_WFOTR_GUN_COOL_3, + SFX_WFOTR_GUN_COOL_4, + SFX_WFOTR_GUN_COOL_5, + SFX_WFOTR_GUN_COOL_6, + SFX_WFOTR_RUN_1, + SFX_WFOTR_RUN_2, + SFX_WFOTR_RUN_3, + SFX_WFOTR_RUN_4, + SFX_WFOTR_RUN_5, + SFX_WFOTR_RUN_6, + SFX_WFOTR_SAVED_1, + SFX_WFOTR_SOLICIT_1, + SFX_WFOTR_SOLICIT_2, + SFX_WFOTR_SOLICIT_3, + SFX_WFOTR_SOLICIT_4, + SFX_WFOTR_SOLICIT_5, + SFX_WFOTR_SOLICIT_6, + SFX_WFOTR_SOLICIT_7, + SFX_WFOTR_SOLICIT_8, + SFX_WFOTR_SOLICIT_9, + SFX_WFOTR_TAXI_1, + + SFX_WFYJG_BUMP_1, + SFX_WFYJG_BUMP_2, + SFX_WFYJG_BUMP_3, + SFX_WFYJG_BUMP_4, + SFX_WFYJG_BUMP_5, + SFX_WFYJG_BUMP_6, + SFX_WFYJG_BUMP_7, + SFX_WFYJG_BUMP_8, + SFX_WFYJG_BUMP_9, + SFX_WFYJG_BUMP_10, + SFX_WFYJG_BUMP_11, + SFX_WFYJG_BUMP_12, + SFX_WFYJG_DODGE_1, + SFX_WFYJG_DODGE_2, + SFX_WFYJG_DODGE_3, + SFX_WFYJG_DODGE_4, + SFX_WFYJG_DODGE_5, + SFX_WFYJG_DODGE_6, + SFX_WFYJG_DODGE_7, + SFX_WFYJG_DODGE_8, + SFX_WFYJG_GUN_PANIC_1, + SFX_WFYJG_GUN_PANIC_2, + SFX_WFYJG_GUN_PANIC_3, + SFX_WFYJG_GUN_PANIC_4, + SFX_WFYJG_RUN_1, + SFX_WFYJG_RUN_2, + SFX_WFYJG_RUN_3, + SFX_WFYJG_RUN_4, + SFX_WFYJG_RUN_5, + SFX_WFYJG_RUN_6, + SFX_WFYJG_SAVED_1, + SFX_WFYJG_TAXI_1, + + SFX_WFYSH_BUMP_1, + SFX_WFYSH_BUMP_2, + SFX_WFYSH_BUMP_3, + SFX_WFYSH_BUMP_4, + SFX_WFYSH_BUMP_5, + SFX_WFYSH_BUMP_6, + SFX_WFYSH_BUMP_7, + SFX_WFYSH_BUMP_8, + SFX_WFYSH_BUMP_9, + SFX_WFYSH_BUMP_10, + SFX_WFYSH_BUMP_11, + SFX_WFYSH_BUMP_12, + SFX_WFYSH_CHAT_1, + SFX_WFYSH_CHAT_2, + SFX_WFYSH_CHAT_3, + SFX_WFYSH_CHAT_4, + SFX_WFYSH_CHAT_5, + SFX_WFYSH_CHAT_6, + SFX_WFYSH_CHAT_7, + SFX_WFYSH_CHAT_8, + SFX_WFYSH_CHAT_9, + SFX_WFYSH_CHAT_10, + SFX_WFYSH_DODGE_1, + SFX_WFYSH_DODGE_2, + SFX_WFYSH_DODGE_3, + SFX_WFYSH_DODGE_4, + SFX_WFYSH_DODGE_5, + SFX_WFYSH_DODGE_6, + SFX_WFYSH_DODGE_7, + SFX_WFYSH_DODGE_8, + SFX_WFYSH_DODGE_9, + SFX_WFYSH_DODGE_10, + SFX_WFYSH_DODGE_11, + SFX_WFYSH_GUN_COOL_1, + SFX_WFYSH_GUN_COOL_2, + SFX_WFYSH_GUN_COOL_3, + SFX_WFYSH_GUN_COOL_4, + SFX_WFYSH_GUN_COOL_5, + SFX_WFYSH_GUN_COOL_6, + SFX_WFYSH_GUN_COOL_7, + SFX_WFYSH_GUN_COOL_8, + SFX_WFYSH_GUN_COOL_9, + SFX_WFYSH_LOST_1, + SFX_WFYSH_LOST_2, + SFX_WFYSH_MUGGED_1, + SFX_WFYSH_MUGGED_2, + SFX_WFYSH_RUN_1, + SFX_WFYSH_RUN_2, + SFX_WFYSH_RUN_3, + SFX_WFYSH_RUN_4, + SFX_WFYSH_RUN_5, + SFX_WFYSH_RUN_6, + SFX_WFYSH_RUN_7, + SFX_WFYSH_RUN_8, + SFX_WFYSH_RUN_9, + SFX_WFYSH_RUN_10, + SFX_WFYSH_RUN_11, + SFX_WFYSH_SAVED_1, + SFX_WFYSH_SAVED_2, + SFX_WFYSH_SAVED_3, + SFX_WFYSH_SAVED_4, + SFX_WFYSH_SHOCKED_1, + SFX_WFYSH_SHOCKED_2, + SFX_WFYSH_SHOCKED_3, + SFX_WFYSH_SHOCKED_4, + SFX_WFYSH_SHOCKED_5, + SFX_WFYSH_TAXI_1, + SFX_WFYSH_TAXI_2, + + SFX_WMOTR_BUMP_1, + SFX_WMOTR_BUMP_2, + SFX_WMOTR_BUMP_3, + SFX_WMOTR_BUMP_4, + SFX_WMOTR_BUMP_5, + SFX_WMOTR_BUMP_6, + SFX_WMOTR_BUMP_7, + SFX_WMOTR_BUMP_8, + SFX_WMOTR_BUMP_9, + SFX_WMOTR_BUMP_10, + SFX_WMOTR_CHAT_1, + SFX_WMOTR_CHAT_2, + SFX_WMOTR_CHAT_3, + SFX_WMOTR_CHAT_4, + SFX_WMOTR_CHAT_5, + SFX_WMOTR_CHAT_6, + SFX_WMOTR_CHAT_7, + SFX_WMOTR_CHAT_8, + SFX_WMOTR_CHAT_9, + SFX_WMOTR_CHAT_10, + SFX_WMOTR_CHAT_11, + SFX_WMOTR_CHAT_12, + SFX_WMOTR_CHAT_13, + SFX_WMOTR_DODGE_1, + SFX_WMOTR_DODGE_2, + SFX_WMOTR_DODGE_3, + SFX_WMOTR_DODGE_4, + SFX_WMOTR_DODGE_5, + SFX_WMOTR_DODGE_6, + SFX_WMOTR_DODGE_7, + SFX_WMOTR_DODGE_8, + SFX_WMOTR_DODGE_9, + SFX_WMOTR_DODGE_10, + SFX_WMOTR_DODGE_11, + SFX_WMOTR_DODGE_12, + SFX_WMOTR_DODGE_13, + SFX_WMOTR_DODGE_14, + SFX_WMOTR_DODGE_15, + SFX_WMOTR_DODGE_16, + SFX_WMOTR_DODGE_17, + SFX_WMOTR_EYEING_1, + SFX_WMOTR_EYEING_2, + SFX_WMOTR_FIGHT_1, + SFX_WMOTR_FIGHT_2, + SFX_WMOTR_FIGHT_3, + SFX_WMOTR_FIGHT_4, + SFX_WMOTR_FIGHT_5, + SFX_WMOTR_FIGHT_6, + SFX_WMOTR_GUN_COOL_1, + SFX_WMOTR_GUN_COOL_2, + SFX_WMOTR_GUN_COOL_3, + SFX_WMOTR_GUN_COOL_4, + SFX_WMOTR_GUN_COOL_5, + SFX_WMOTR_SAVED_1, + SFX_WMOTR_SHOCKED_1, + SFX_WMOTR_SHOCKED_2, + SFX_WMOTR_SHOCKED_3, + SFX_WMOTR_SOLICIT_1, + SFX_WMOTR_SOLICIT_2, + SFX_WMOTR_SOLICIT_3, + SFX_WMOTR_SOLICIT_4, + SFX_WMOTR_SOLICIT_5, + SFX_WMOTR_SOLICIT_6, + SFX_WMOTR_SOLICIT_7, + SFX_WMOTR_TAXI_1, + + SFX_BMOBE_BUMP_1, + SFX_BMOBE_BUMP_2, + SFX_BMOBE_BUMP_3, + SFX_BMOBE_BUMP_4, + SFX_BMOBE_BUMP_5, + SFX_BMOBE_CAR_CRASH_1, + SFX_BMOBE_CAR_CRASH_2, + SFX_BMOBE_CAR_CRASH_3, + SFX_BMOBE_CAR_CRASH_4, + SFX_BMOBE_CAR_CRASH_5, + SFX_BMOBE_CAR_CRASH_6, + SFX_BMOBE_CAR_CRASH_7, + SFX_BMOBE_CAR_CRASH_8, + SFX_BMOBE_CAR_CRASH_9, + SFX_BMOBE_CHAT_1, + SFX_BMOBE_CHAT_2, + SFX_BMOBE_CHAT_3, + SFX_BMOBE_CHAT_4, + SFX_BMOBE_CHAT_5, + SFX_BMOBE_CHAT_6, + SFX_BMOBE_CHAT_7, + SFX_BMOBE_CHAT_8, + SFX_BMOBE_CHAT_9, + SFX_BMOBE_CHAT_10, + SFX_BMOBE_DODGE_1, + SFX_BMOBE_DODGE_2, + SFX_BMOBE_DODGE_3, + SFX_BMOBE_DODGE_4, + SFX_BMOBE_DODGE_5, + SFX_BMOBE_DODGE_6, + SFX_BMOBE_DODGE_7, + SFX_BMOBE_DODGE_8, + SFX_BMOBE_DODGE_9, + SFX_BMOBE_DODGE_10, + SFX_BMOBE_DODGE_11, + SFX_BMOBE_FIGHT_1, + SFX_BMOBE_FIGHT_2, + SFX_BMOBE_FIGHT_3, + SFX_BMOBE_FIGHT_4, + SFX_BMOBE_FIGHT_5, + SFX_BMOBE_FIGHT_6, + SFX_BMOBE_FIGHT_7, + SFX_BMOBE_FIGHT_8, + SFX_BMOBE_FIGHT_9, + SFX_BMOBE_FIGHT_10, + SFX_BMOBE_GENERIC_CRASH_1, + SFX_BMOBE_GENERIC_CRASH_2, + SFX_BMOBE_GENERIC_CRASH_3, + SFX_BMOBE_GENERIC_CRASH_4, + SFX_BMOBE_GENERIC_CRASH_5, + SFX_BMOBE_GENERIC_CRASH_6, + SFX_BMOBE_GENERIC_CRASH_7, + SFX_BMOBE_GUN_PANIC_1, + SFX_BMOBE_GUN_PANIC_2, + SFX_BMOBE_GUN_PANIC_3, + SFX_BMOBE_GUN_PANIC_4, + SFX_BMOBE_GUN_PANIC_5, + SFX_BMOBE_JACKED_1, + SFX_BMOBE_JACKED_2, + SFX_BMOBE_JACKED_3, + SFX_BMOBE_JACKED_4, + SFX_BMOBE_JACKED_5, + SFX_BMOBE_JACKED_6, + SFX_BMOBE_MUGGED_1, + SFX_BMOBE_MUGGED_2, + SFX_BMOBE_MUGGED_3, + SFX_BMOBE_MUGGED_4, + SFX_BMOBE_SAVED_1, + SFX_BMOBE_SAVED_2, + SFX_BMOBE_SAVED_3, + SFX_BMOBE_SHOCKED_1, + SFX_BMOBE_SHOCKED_2, + SFX_BMOBE_SHOCKED_3, + SFX_BMOBE_TAXI_1, + + SFX_WMYGO_BUMP_1, + SFX_WMYGO_BUMP_2, + SFX_WMYGO_BUMP_3, + SFX_WMYGO_BUMP_4, + SFX_WMYGO_BUMP_5, + SFX_WMYGO_BUMP_6, + SFX_WMYGO_BUMP_7, + SFX_WMYGO_BUMP_8, + SFX_WMYGO_BUMP_9, + SFX_WMYGO_CAR_CRASH_1, + SFX_WMYGO_CAR_CRASH_2, + SFX_WMYGO_CAR_CRASH_3, + SFX_WMYGO_CAR_CRASH_4, + SFX_WMYGO_CAR_CRASH_5, + SFX_WMYGO_CAR_CRASH_6, + SFX_WMYGO_CAR_CRASH_7, + SFX_WMYGO_CHAT_1, + SFX_WMYGO_CHAT_2, + SFX_WMYGO_CHAT_3, + SFX_WMYGO_CHAT_4, + SFX_WMYGO_CHAT_5, + SFX_WMYGO_CHAT_6, + SFX_WMYGO_CHAT_7, + SFX_WMYGO_CHAT_8, + SFX_WMYGO_CHAT_9, + SFX_WMYGO_CHAT_10, + SFX_WMYGO_DODGE_1, + SFX_WMYGO_DODGE_2, + SFX_WMYGO_DODGE_3, + SFX_WMYGO_DODGE_4, + SFX_WMYGO_DODGE_5, + SFX_WMYGO_DODGE_6, + SFX_WMYGO_DODGE_7, + SFX_WMYGO_DODGE_8, + SFX_WMYGO_DODGE_9, + SFX_WMYGO_DODGE_10, + SFX_WMYGO_DODGE_11, + SFX_WMYGO_EYEING_1, + SFX_WMYGO_EYEING_2, + SFX_WMYGO_GENERIC_CRASH_1, + SFX_WMYGO_GENERIC_CRASH_2, + SFX_WMYGO_GENERIC_CRASH_3, + SFX_WMYGO_GENERIC_CRASH_4, + SFX_WMYGO_GENERIC_CRASH_5, + SFX_WMYGO_GENERIC_CRASH_6, + SFX_WMYGO_GENERIC_CRASH_7, + SFX_WMYGO_GUN_PANIC_1, + SFX_WMYGO_GUN_PANIC_2, + SFX_WMYGO_GUN_PANIC_3, + SFX_WMYGO_GUN_PANIC_4, + SFX_WMYGO_GUN_PANIC_5, + SFX_WMYGO_JACKED_1, + SFX_WMYGO_JACKED_2, + SFX_WMYGO_JACKED_3, + SFX_WMYGO_JACKED_4, + SFX_WMYGO_JACKED_5, + SFX_WMYGO_JACKED_6, + SFX_WMYGO_MUGGED_1, + SFX_WMYGO_MUGGED_2, + SFX_WMYGO_RUN_1, + SFX_WMYGO_RUN_2, + SFX_WMYGO_RUN_3, + SFX_WMYGO_RUN_4, + SFX_WMYGO_RUN_5, + SFX_WMYGO_RUN_6, + SFX_WMYGO_SAVED_1, + SFX_WMYGO_SHOCKED_1, + SFX_WMYGO_SHOCKED_2, + SFX_WMYGO_TAXI_1, + SFX_WMYGO_TAXI_2, + SFX_WMYGO_TAXI_3, + + SFX_WFYBE_BLOCKED_1, + SFX_WFYBE_BLOCKED_2, + SFX_WFYBE_BLOCKED_3, + SFX_WFYBE_BLOCKED_4, + SFX_WFYBE_BLOCKED_5, + SFX_WFYBE_BLOCKED_6, + SFX_WFYBE_BLOCKED_7, + SFX_WFYBE_BUMP_1, + SFX_WFYBE_BUMP_2, + SFX_WFYBE_BUMP_3, + SFX_WFYBE_BUMP_4, + SFX_WFYBE_BUMP_5, + SFX_WFYBE_BUMP_6, + SFX_WFYBE_BUMP_7, + SFX_WFYBE_BUMP_8, + SFX_WFYBE_BUMP_9, + SFX_WFYBE_BUMP_10, + SFX_WFYBE_BUMP_11, + SFX_WFYBE_CAR_CRASH_1, + SFX_WFYBE_CAR_CRASH_2, + SFX_WFYBE_CAR_CRASH_3, + SFX_WFYBE_CAR_CRASH_4, + SFX_WFYBE_CAR_CRASH_5, + SFX_WFYBE_CAR_CRASH_6, + SFX_WFYBE_CHAT_1, + SFX_WFYBE_CHAT_2, + SFX_WFYBE_CHAT_3, + SFX_WFYBE_CHAT_4, + SFX_WFYBE_CHAT_5, + SFX_WFYBE_CHAT_6, + SFX_WFYBE_CHAT_7, + SFX_WFYBE_CHAT_8, + SFX_WFYBE_CHAT_9, + SFX_WFYBE_CHAT_10, + SFX_WFYBE_DODGE_1, + SFX_WFYBE_DODGE_2, + SFX_WFYBE_DODGE_3, + SFX_WFYBE_DODGE_4, + SFX_WFYBE_DODGE_5, + SFX_WFYBE_DODGE_6, + SFX_WFYBE_DODGE_7, + SFX_WFYBE_DODGE_8, + SFX_WFYBE_GENERIC_CRASH_1, + SFX_WFYBE_GENERIC_CRASH_2, + SFX_WFYBE_GENERIC_CRASH_3, + SFX_WFYBE_GENERIC_CRASH_4, + SFX_WFYBE_GENERIC_CRASH_5, + SFX_WFYBE_GENERIC_CRASH_6, + SFX_WFYBE_GUN_PANIC_1, + SFX_WFYBE_GUN_PANIC_2, + SFX_WFYBE_GUN_PANIC_3, + SFX_WFYBE_GUN_PANIC_4, + SFX_WFYBE_GUN_PANIC_5, + SFX_WFYBE_JACKED_1, + SFX_WFYBE_JACKED_2, + SFX_WFYBE_JACKED_3, + SFX_WFYBE_JACKED_4, + SFX_WFYBE_RUN_1, + SFX_WFYBE_RUN_2, + SFX_WFYBE_RUN_3, + SFX_WFYBE_RUN_4, + SFX_WFYBE_RUN_5, + SFX_WFYBE_SAVED_1, + SFX_WFYBE_SHOCKED_1, + SFX_WFYBE_SHOCKED_2, + SFX_WFYBE_SHOCKED_3, + SFX_WFYBE_TAXI_1, + + SFX_WFORI_BLOCKED_1, + SFX_WFORI_BLOCKED_2, + SFX_WFORI_BLOCKED_3, + SFX_WFORI_BLOCKED_4, + SFX_WFORI_BLOCKED_5, + SFX_WFORI_BLOCKED_6, + SFX_WFORI_BLOCKED_7, + SFX_WFORI_BUMP_1, + SFX_WFORI_BUMP_2, + SFX_WFORI_BUMP_3, + SFX_WFORI_BUMP_4, + SFX_WFORI_BUMP_5, + SFX_WFORI_BUMP_6, + SFX_WFORI_BUMP_7, + SFX_WFORI_BUMP_8, + SFX_WFORI_BUMP_9, + SFX_WFORI_BUMP_10, + SFX_WFORI_BUMP_11, + SFX_WFORI_CAR_CRASH_1, + SFX_WFORI_CAR_CRASH_2, + SFX_WFORI_CAR_CRASH_3, + SFX_WFORI_CAR_CRASH_4, + SFX_WFORI_CAR_CRASH_5, + SFX_WFORI_CAR_CRASH_6, + SFX_WFORI_CAR_CRASH_7, + SFX_WFORI_CAR_CRASH_8, + SFX_WFORI_CAR_CRASH_9, + SFX_WFORI_CAR_CRASH_10, + SFX_WFORI_DODGE_1, + SFX_WFORI_DODGE_2, + SFX_WFORI_DODGE_3, + SFX_WFORI_DODGE_4, + SFX_WFORI_DODGE_5, + SFX_WFORI_DODGE_6, + SFX_WFORI_DODGE_7, + SFX_WFORI_DODGE_8, + SFX_WFORI_DODGE_9, + SFX_WFORI_DODGE_10, + SFX_WFORI_DODGE_11, + SFX_WFORI_FIGHT_1, + SFX_WFORI_FIGHT_2, + SFX_WFORI_FIGHT_3, + SFX_WFORI_FIGHT_4, + SFX_WFORI_FIGHT_5, + SFX_WFORI_FIGHT_6, + SFX_WFORI_FIGHT_7, + SFX_WFORI_GENERIC_CRASH_1, + SFX_WFORI_GENERIC_CRASH_2, + SFX_WFORI_GENERIC_CRASH_3, + SFX_WFORI_GENERIC_CRASH_4, + SFX_WFORI_GENERIC_CRASH_5, + SFX_WFORI_GENERIC_CRASH_6, + SFX_WFORI_GENERIC_CRASH_7, + SFX_WFORI_GENERIC_CRASH_8, + SFX_WFORI_GUN_PANIC_1, + SFX_WFORI_GUN_PANIC_2, + SFX_WFORI_GUN_PANIC_3, + SFX_WFORI_GUN_PANIC_4, + SFX_WFORI_GUN_PANIC_5, + SFX_WFORI_GUN_PANIC_6, + SFX_WFORI_JACKED_1, + SFX_WFORI_JACKED_2, + SFX_WFORI_JACKED_3, + SFX_WFORI_JACKED_4, + SFX_WFORI_JACKED_5, + SFX_WFORI_JACKED_6, + SFX_WFORI_LOST_1, + SFX_WFORI_LOST_2, + SFX_WFORI_MUGGED_1, + SFX_WFORI_MUGGED_2, + SFX_WFORI_MUGGED_3, + SFX_WFORI_SAVED_1, + SFX_WFORI_SHOCKED_1, + SFX_WFORI_SHOCKED_2, + SFX_WFORI_SHOCKED_3, + SFX_WFORI_TAXI_1, + + SFX_WFOGO_BUMP_1, + SFX_WFOGO_BUMP_2, + SFX_WFOGO_BUMP_3, + SFX_WFOGO_BUMP_4, + SFX_WFOGO_BUMP_5, + SFX_WFOGO_BUMP_6, + SFX_WFOGO_BUMP_7, + SFX_WFOGO_BUMP_8, + SFX_WFOGO_CAR_CRASH_1, + SFX_WFOGO_CAR_CRASH_2, + SFX_WFOGO_CAR_CRASH_3, + SFX_WFOGO_CAR_CRASH_4, + SFX_WFOGO_CAR_CRASH_5, + SFX_WFOGO_CAR_CRASH_6, + SFX_WFOGO_CAR_CRASH_7, + SFX_WFOGO_CAR_CRASH_8, + SFX_WFOGO_CHAT_1, + SFX_WFOGO_CHAT_2, + SFX_WFOGO_CHAT_3, + SFX_WFOGO_CHAT_4, + SFX_WFOGO_CHAT_5, + SFX_WFOGO_CHAT_6, + SFX_WFOGO_CHAT_7, + SFX_WFOGO_CHAT_8, + SFX_WFOGO_CHAT_9, + SFX_WFOGO_CHAT_10, + SFX_WFOGO_CHAT_11, + SFX_WFOGO_DODGE_1, + SFX_WFOGO_DODGE_2, + SFX_WFOGO_DODGE_3, + SFX_WFOGO_DODGE_4, + SFX_WFOGO_DODGE_5, + SFX_WFOGO_DODGE_6, + SFX_WFOGO_DODGE_7, + SFX_WFOGO_DODGE_8, + SFX_WFOGO_DODGE_9, + SFX_WFOGO_FIGHT_1, + SFX_WFOGO_FIGHT_2, + SFX_WFOGO_FIGHT_3, + SFX_WFOGO_FIGHT_4, + SFX_WFOGO_FIGHT_5, + SFX_WFOGO_FIGHT_6, + SFX_WFOGO_FIGHT_7, + SFX_WFOGO_FIGHT_8, + SFX_WFOGO_FIGHT_9, + SFX_WFOGO_FIGHT_10, + SFX_WFOGO_FIGHT_11, + SFX_WFOGO_FIGHT_12, + SFX_WFOGO_FIGHT_13, + SFX_WFOGO_FIGHT_14, + SFX_WFOGO_GENERIC_CRASH_1, + SFX_WFOGO_GENERIC_CRASH_2, + SFX_WFOGO_GENERIC_CRASH_3, + SFX_WFOGO_GENERIC_CRASH_4, + SFX_WFOGO_GENERIC_CRASH_5, + SFX_WFOGO_GENERIC_CRASH_6, + SFX_WFOGO_GENERIC_CRASH_7, + SFX_WFOGO_GUN_PANIC_1, + SFX_WFOGO_GUN_PANIC_2, + SFX_WFOGO_GUN_PANIC_3, + SFX_WFOGO_GUN_PANIC_4, + SFX_WFOGO_GUN_PANIC_5, + SFX_WFOGO_JACKED_1, + SFX_WFOGO_JACKED_2, + SFX_WFOGO_JACKED_3, + SFX_WFOGO_JACKED_4, + SFX_WFOGO_JACKED_5, + SFX_WFOGO_JACKED_6, + SFX_WFOGO_MUGGED_1, + SFX_WFOGO_MUGGED_2, + SFX_WFOGO_RUN_1, + SFX_WFOGO_RUN_2, + SFX_WFOGO_SAVED_1, + SFX_WFOGO_SHOCKED_1, + SFX_WFOGO_SHOCKED_2, + + SFX_HMYST_BUMP_1, + SFX_HMYST_BUMP_2, + SFX_HMYST_BUMP_3, + SFX_HMYST_BUMP_4, + SFX_HMYST_BUMP_5, + SFX_HMYST_BUMP_6, + SFX_HMYST_BUMP_7, + SFX_HMYST_BUMP_8, + SFX_HMYST_BUMP_9, + SFX_HMYST_BUMP_10, + SFX_HMYST_BUMP_11, + SFX_HMYST_BUMP_12, + SFX_HMYST_BUMP_13, + SFX_HMYST_CHAT_1, + SFX_HMYST_CHAT_2, + SFX_HMYST_CHAT_3, + SFX_HMYST_CHAT_4, + SFX_HMYST_CHAT_5, + SFX_HMYST_CHAT_6, + SFX_HMYST_CHAT_7, + SFX_HMYST_CHAT_8, + SFX_HMYST_CHAT_9, + SFX_HMYST_CHAT_10, + SFX_HMYST_CHAT_11, + SFX_HMYST_DODGE_1, + SFX_HMYST_DODGE_2, + SFX_HMYST_DODGE_3, + SFX_HMYST_DODGE_4, + SFX_HMYST_DODGE_5, + SFX_HMYST_DODGE_6, + SFX_HMYST_EYEING_1, + SFX_HMYST_EYEING_2, + SFX_HMYST_GENERIC_CRASH_1, + SFX_HMYST_GENERIC_CRASH_2, + SFX_HMYST_GENERIC_CRASH_3, + SFX_HMYST_GENERIC_CRASH_4, + SFX_HMYST_GENERIC_CRASH_5, + SFX_HMYST_GENERIC_CRASH_6, + SFX_HMYST_GENERIC_CRASH_7, + SFX_HMYST_GUN_PANIC_1, + SFX_HMYST_GUN_PANIC_2, + SFX_HMYST_GUN_PANIC_3, + SFX_HMYST_GUN_PANIC_4, + SFX_HMYST_GUN_PANIC_5, + SFX_HMYST_GUN_PANIC_6, + SFX_HMYST_RUN_1, + SFX_HMYST_RUN_2, + SFX_HMYST_RUN_3, + SFX_HMYST_RUN_4, + SFX_HMYST_SAVED_1, + SFX_HMYST_SHOCKED_1, + SFX_HMYST_SHOCKED_2, + SFX_HMYST_TAXI_1, + + SFX_WMOCA_BLOCKED_1, + SFX_WMOCA_BLOCKED_2, + SFX_WMOCA_BLOCKED_3, + SFX_WMOCA_BLOCKED_4, + SFX_WMOCA_BLOCKED_5, + SFX_WMOCA_BLOCKED_6, + SFX_WMOCA_BLOCKED_7, + SFX_WMOCA_BLOCKED_8, + SFX_WMOCA_BLOCKED_9, + SFX_WMOCA_BLOCKED_10, + SFX_WMOCA_BLOCKED_11, + SFX_WMOCA_BLOCKED_12, + SFX_WMOCA_BUMP_1, + SFX_WMOCA_BUMP_2, + SFX_WMOCA_BUMP_3, + SFX_WMOCA_BUMP_4, + SFX_WMOCA_BUMP_5, + SFX_WMOCA_BUMP_6, + SFX_WMOCA_CAR_CRASH_1, + SFX_WMOCA_CAR_CRASH_2, + SFX_WMOCA_CAR_CRASH_3, + SFX_WMOCA_CAR_CRASH_4, + SFX_WMOCA_CAR_CRASH_5, + SFX_WMOCA_CAR_CRASH_6, + SFX_WMOCA_CAR_CRASH_7, + SFX_WMOCA_CAR_CRASH_8, + SFX_WMOCA_CAR_CRASH_9, + SFX_WMOCA_CAR_CRASH_10, + SFX_WMOCA_DODGE_1, + SFX_WMOCA_DODGE_2, + SFX_WMOCA_DODGE_3, + SFX_WMOCA_DODGE_4, + SFX_WMOCA_DODGE_5, + SFX_WMOCA_DODGE_6, + SFX_WMOCA_DODGE_7, + SFX_WMOCA_DODGE_8, + SFX_WMOCA_DODGE_9, + SFX_WMOCA_DODGE_10, + SFX_WMOCA_EYEING_1, + SFX_WMOCA_EYEING_2, + SFX_WMOCA_FIGHT_1, + SFX_WMOCA_FIGHT_2, + SFX_WMOCA_FIGHT_3, + SFX_WMOCA_FIGHT_4, + SFX_WMOCA_FIGHT_5, + SFX_WMOCA_FIGHT_6, + SFX_WMOCA_FIGHT_7, + SFX_WMOCA_FIGHT_8, + SFX_WMOCA_GENERIC_CRASH_1, + SFX_WMOCA_GENERIC_CRASH_2, + SFX_WMOCA_GENERIC_CRASH_3, + SFX_WMOCA_GENERIC_CRASH_4, + SFX_WMOCA_GENERIC_CRASH_5, + SFX_WMOCA_GENERIC_CRASH_6, + SFX_WMOCA_GENERIC_CRASH_7, + SFX_WMOCA_GENERIC_CRASH_8, + SFX_WMOCA_GENERIC_CRASH_9, + SFX_WMOCA_GUN_PANIC_1, + SFX_WMOCA_GUN_PANIC_2, + SFX_WMOCA_GUN_PANIC_3, + SFX_WMOCA_GUN_PANIC_4, + SFX_WMOCA_GUN_PANIC_5, + SFX_WMOCA_GUN_PANIC_6, + SFX_WMOCA_JACKED_1, + SFX_WMOCA_JACKED_2, + SFX_WMOCA_JACKED_3, + SFX_WMOCA_JACKED_4, + SFX_WMOCA_JACKED_5, + SFX_WMOCA_JACKED_6, + SFX_WMOCA_JACKED_7, + SFX_WMOCA_JACKED_8, + SFX_WMOCA_JACKED_9, + SFX_WMOCA_JACKED_10, + SFX_WMOCA_JACKING_1, + SFX_WMOCA_JACKING_2, + SFX_WMOCA_JACKING_3, + SFX_WMOCA_JACKING_4, + SFX_WMOCA_JACKING_5, + SFX_WMOCA_JACKING_6, + SFX_WMOCA_JACKING_7, + SFX_WMOCA_JACKING_8, + SFX_WMOCA_JACKING_9, + SFX_WMOCA_JACKING_10, + SFX_WMOCA_JACKING_11, + SFX_WMOCA_MUGGED_1, + SFX_WMOCA_MUGGED_2, + SFX_WMOCA_SAVED_1, + SFX_WMOCA_TAXI_1, + + SFX_WMYBE_BLOCKED_1, + SFX_WMYBE_BLOCKED_2, + SFX_WMYBE_BLOCKED_3, + SFX_WMYBE_BLOCKED_4, + SFX_WMYBE_BLOCKED_5, + SFX_WMYBE_BLOCKED_6, + SFX_WMYBE_BLOCKED_7, + SFX_WMYBE_BLOCKED_8, + SFX_WMYBE_BLOCKED_9, + SFX_WMYBE_BUMP_1, + SFX_WMYBE_BUMP_2, + SFX_WMYBE_BUMP_3, + SFX_WMYBE_BUMP_4, + SFX_WMYBE_BUMP_5, + SFX_WMYBE_BUMP_6, + SFX_WMYBE_BUMP_7, + SFX_WMYBE_BUMP_8, + SFX_WMYBE_BUMP_9, + SFX_WMYBE_BUMP_10, + SFX_WMYBE_BUMP_11, + SFX_WMYBE_BUMP_12, + SFX_WMYBE_BUMP_13, + SFX_WMYBE_BUMP_14, + SFX_WMYBE_CAR_CRASH_1, + SFX_WMYBE_CAR_CRASH_2, + SFX_WMYBE_CAR_CRASH_3, + SFX_WMYBE_CAR_CRASH_4, + SFX_WMYBE_CAR_CRASH_5, + SFX_WMYBE_CAR_CRASH_6, + SFX_WMYBE_CAR_CRASH_7, + SFX_WMYBE_CAR_CRASH_8, + SFX_WMYBE_CHAT_1, + SFX_WMYBE_CHAT_2, + SFX_WMYBE_CHAT_3, + SFX_WMYBE_CHAT_4, + SFX_WMYBE_CHAT_5, + SFX_WMYBE_CHAT_6, + SFX_WMYBE_CHAT_7, + SFX_WMYBE_CHAT_8, + SFX_WMYBE_CHAT_9, + SFX_WMYBE_CHAT_10, + SFX_WMYBE_CHAT_11, + SFX_WMYBE_DODGE_1, + SFX_WMYBE_DODGE_2, + SFX_WMYBE_DODGE_3, + SFX_WMYBE_DODGE_4, + SFX_WMYBE_DODGE_5, + SFX_WMYBE_DODGE_6, + SFX_WMYBE_DODGE_7, + SFX_WMYBE_DODGE_8, + SFX_WMYBE_DODGE_9, + SFX_WMYBE_DODGE_10, + SFX_WMYBE_DODGE_11, + SFX_WMYBE_DODGE_12, + SFX_WMYBE_EYEING_1, + SFX_WMYBE_EYEING_2, + SFX_WMYBE_GENERIC_CRASH_1, + SFX_WMYBE_GENERIC_CRASH_2, + SFX_WMYBE_GENERIC_CRASH_3, + SFX_WMYBE_GENERIC_CRASH_4, + SFX_WMYBE_GENERIC_CRASH_5, + SFX_WMYBE_GENERIC_CRASH_6, + SFX_WMYBE_GENERIC_CRASH_7, + SFX_WMYBE_GENERIC_CRASH_8, + SFX_WMYBE_GUN_PANIC_1, + SFX_WMYBE_GUN_PANIC_2, + SFX_WMYBE_GUN_PANIC_3, + SFX_WMYBE_GUN_PANIC_4, + SFX_WMYBE_GUN_PANIC_5, + SFX_WMYBE_GUN_PANIC_6, + SFX_WMYBE_GUN_PANIC_7, + SFX_WMYBE_GUN_PANIC_8, + SFX_WMYBE_JACKED_1, + SFX_WMYBE_JACKED_2, + SFX_WMYBE_JACKED_3, + SFX_WMYBE_JACKED_4, + SFX_WMYBE_JACKED_5, + SFX_WMYBE_JACKED_6, + SFX_WMYBE_JACKED_7, + SFX_WMYBE_JACKING_1, + SFX_WMYBE_JACKING_2, + SFX_WMYBE_JACKING_3, + SFX_WMYBE_JEER_1, + SFX_WMYBE_JEER_2, + SFX_WMYBE_JEER_3, + SFX_WMYBE_JEER_4, + SFX_WMYBE_JEER_5, + SFX_WMYBE_JEER_6, + SFX_WMYBE_JEER_7, + SFX_WMYBE_LOST_1, + SFX_WMYBE_LOST_2, + SFX_WMYBE_LOST_3, + SFX_WMYBE_RUN_1, + SFX_WMYBE_RUN_2, + SFX_WMYBE_RUN_3, + SFX_WMYBE_RUN_4, + SFX_WMYBE_RUN_5, + SFX_WMYBE_SHOCKED_1, + SFX_WMYBE_SHOCKED_2, + SFX_WMYBE_SHOCKED_3, + SFX_WMYBE_SHOCKED_4, + SFX_WMYBE_SHOCKED_5, + SFX_WMYBE_SHOCKED_6, + + SFX_BFOBE_BLOCKED_1, + SFX_BFOBE_BLOCKED_2, + SFX_BFOBE_BLOCKED_3, + SFX_BFOBE_BLOCKED_4, + SFX_BFOBE_BLOCKED_5, + SFX_BFOBE_BLOCKED_6, + SFX_BFOBE_BLOCKED_7, + SFX_BFOBE_BLOCKED_8, + SFX_BFOBE_BUMP_1, + SFX_BFOBE_BUMP_2, + SFX_BFOBE_BUMP_3, + SFX_BFOBE_BUMP_4, + SFX_BFOBE_BUMP_5, + SFX_BFOBE_BUMP_6, + SFX_BFOBE_BUMP_7, + SFX_BFOBE_BUMP_8, + SFX_BFOBE_CAR_CRASH_1, + SFX_BFOBE_CAR_CRASH_2, + SFX_BFOBE_CAR_CRASH_3, + SFX_BFOBE_CAR_CRASH_4, + SFX_BFOBE_CAR_CRASH_5, + SFX_BFOBE_CAR_CRASH_6, + SFX_BFOBE_CAR_CRASH_7, + SFX_BFOBE_CHAT_1, + SFX_BFOBE_CHAT_2, + SFX_BFOBE_CHAT_3, + SFX_BFOBE_CHAT_4, + SFX_BFOBE_CHAT_5, + SFX_BFOBE_CHAT_6, + SFX_BFOBE_CHAT_7, + SFX_BFOBE_CHAT_8, + SFX_BFOBE_DODGE_1, + SFX_BFOBE_DODGE_2, + SFX_BFOBE_DODGE_3, + SFX_BFOBE_DODGE_4, + SFX_BFOBE_DODGE_5, + SFX_BFOBE_DODGE_6, + SFX_BFOBE_DODGE_7, + SFX_BFOBE_DODGE_8, + SFX_BFOBE_DODGE_9, + SFX_BFOBE_GENERIC_CRASH_1, + SFX_BFOBE_GENERIC_CRASH_2, + SFX_BFOBE_GENERIC_CRASH_3, + SFX_BFOBE_GENERIC_CRASH_4, + SFX_BFOBE_GENERIC_CRASH_5, + SFX_BFOBE_GENERIC_CRASH_6, + SFX_BFOBE_GENERIC_CRASH_7, + SFX_BFOBE_GUN_PANIC_1, + SFX_BFOBE_GUN_PANIC_2, + SFX_BFOBE_GUN_PANIC_3, + SFX_BFOBE_GUN_PANIC_4, + SFX_BFOBE_GUN_PANIC_5, + SFX_BFOBE_JACKED_1, + SFX_BFOBE_JACKED_2, + SFX_BFOBE_JACKED_3, + SFX_BFOBE_JACKED_4, + SFX_BFOBE_JACKED_5, + SFX_BFOBE_JACKING_1, + SFX_BFOBE_JACKING_2, + SFX_BFOBE_JACKING_3, + SFX_BFOBE_JACKING_4, + SFX_BFOBE_MUGGED_1, + SFX_BFOBE_MUGGED_2, + SFX_BFOBE_SHOCKED_1, + SFX_BFOBE_SHOCKED_2, + SFX_BFOBE_TAXI_1, + + SFX_WFYLG_BUMP_1, + SFX_WFYLG_BUMP_2, + SFX_WFYLG_BUMP_3, + SFX_WFYLG_BUMP_4, + SFX_WFYLG_BUMP_5, + SFX_WFYLG_BUMP_6, + SFX_WFYLG_BUMP_7, + SFX_WFYLG_BUMP_8, + SFX_WFYLG_BUMP_9, + SFX_WFYLG_BUMP_10, + SFX_WFYLG_CHAT_1, + SFX_WFYLG_CHAT_2, + SFX_WFYLG_CHAT_3, + SFX_WFYLG_CHAT_4, + SFX_WFYLG_CHAT_5, + SFX_WFYLG_CHAT_6, + SFX_WFYLG_CHAT_7, + SFX_WFYLG_CHAT_8, + SFX_WFYLG_CHAT_9, + SFX_WFYLG_CHAT_10, + SFX_WFYLG_DODGE_1, + SFX_WFYLG_DODGE_2, + SFX_WFYLG_DODGE_3, + SFX_WFYLG_DODGE_4, + SFX_WFYLG_DODGE_5, + SFX_WFYLG_DODGE_6, + SFX_WFYLG_DODGE_7, + SFX_WFYLG_DODGE_8, + SFX_WFYLG_FIGHT_1, + SFX_WFYLG_FIGHT_2, + SFX_WFYLG_FIGHT_3, + SFX_WFYLG_FIGHT_4, + SFX_WFYLG_FIGHT_5, + SFX_WFYLG_FIGHT_6, + SFX_WFYLG_FIGHT_7, + SFX_WFYLG_GUN_COOL_1, + SFX_WFYLG_GUN_COOL_2, + SFX_WFYLG_GUN_COOL_3, + SFX_WFYLG_GUN_COOL_4, + SFX_WFYLG_GUN_COOL_5, + SFX_WFYLG_SAVED_1, + SFX_WFYLG_TAXI_1, + + SFX_WFOST_BLOCKED_1, + SFX_WFOST_BLOCKED_2, + SFX_WFOST_BLOCKED_3, + SFX_WFOST_BLOCKED_4, + SFX_WFOST_BLOCKED_5, + SFX_WFOST_BLOCKED_6, + SFX_WFOST_BLOCKED_7, + SFX_WFOST_BLOCKED_8, + SFX_WFOST_BLOCKED_9, + SFX_WFOST_BLOCKED_10, + SFX_WFOST_BLOCKED_11, + SFX_WFOST_BLOCKED_12, + SFX_WFOST_BUMP_1, + SFX_WFOST_BUMP_2, + SFX_WFOST_BUMP_3, + SFX_WFOST_BUMP_4, + SFX_WFOST_BUMP_5, + SFX_WFOST_BUMP_6, + SFX_WFOST_BUMP_7, + SFX_WFOST_BUMP_8, + SFX_WFOST_BUMP_9, + SFX_WFOST_BUMP_10, + SFX_WFOST_BUMP_11, + SFX_WFOST_BUMP_12, + SFX_WFOST_BUMP_13, + SFX_WFOST_BUMP_14, + SFX_WFOST_BUMP_15, + SFX_WFOST_BUMP_16, + SFX_WFOST_BUMP_17, + SFX_WFOST_BUMP_18, + SFX_WFOST_BUMP_19, + SFX_WFOST_CAR_CRASH_1, + SFX_WFOST_CAR_CRASH_2, + SFX_WFOST_CAR_CRASH_3, + SFX_WFOST_CAR_CRASH_4, + SFX_WFOST_CAR_CRASH_5, + SFX_WFOST_CAR_CRASH_6, + SFX_WFOST_CAR_CRASH_7, + SFX_WFOST_CAR_CRASH_8, + SFX_WFOST_CAR_CRASH_9, + SFX_WFOST_CAR_CRASH_10, + SFX_WFOST_CAR_CRASH_11, + SFX_WFOST_CHAT_1, + SFX_WFOST_CHAT_2, + SFX_WFOST_CHAT_3, + SFX_WFOST_CHAT_4, + SFX_WFOST_CHAT_5, + SFX_WFOST_CHAT_6, + SFX_WFOST_CHAT_7, + SFX_WFOST_CHAT_8, + SFX_WFOST_CHAT_9, + SFX_WFOST_CHAT_10, + SFX_WFOST_CHAT_11, + SFX_WFOST_CHAT_12, + SFX_WFOST_CHAT_13, + SFX_WFOST_CHAT_14, + SFX_WFOST_CHAT_15, + SFX_WFOST_CHAT_16, + SFX_WFOST_DODGE_1, + SFX_WFOST_DODGE_2, + SFX_WFOST_DODGE_3, + SFX_WFOST_DODGE_4, + SFX_WFOST_DODGE_5, + SFX_WFOST_DODGE_6, + SFX_WFOST_DODGE_7, + SFX_WFOST_DODGE_8, + SFX_WFOST_DODGE_9, + SFX_WFOST_DODGE_10, + SFX_WFOST_DODGE_11, + SFX_WFOST_DODGE_12, + SFX_WFOST_GENERIC_CRASH_1, + SFX_WFOST_GENERIC_CRASH_2, + SFX_WFOST_GENERIC_CRASH_3, + SFX_WFOST_GENERIC_CRASH_4, + SFX_WFOST_GENERIC_CRASH_5, + SFX_WFOST_GENERIC_CRASH_6, + SFX_WFOST_GENERIC_CRASH_7, + SFX_WFOST_GENERIC_CRASH_8, + SFX_WFOST_GENERIC_CRASH_9, + SFX_WFOST_GENERIC_CRASH_10, + SFX_WFOST_GUN_PANIC_1, + SFX_WFOST_GUN_PANIC_2, + SFX_WFOST_GUN_PANIC_3, + SFX_WFOST_GUN_PANIC_4, + SFX_WFOST_JACKED_1, + SFX_WFOST_JACKED_2, + SFX_WFOST_JACKED_3, + SFX_WFOST_JACKED_4, + SFX_WFOST_JACKED_5, + SFX_WFOST_JACKED_6, + SFX_WFOST_JACKED_7, + SFX_WFOST_JACKED_8, + SFX_WFOST_LOST_1, + SFX_WFOST_LOST_2, + SFX_WFOST_LOST_3, + SFX_WFOST_MUGGED_1, + SFX_WFOST_MUGGED_2, + SFX_WFOST_MUGGED_3, + SFX_WFOST_MUGGED_4, + SFX_WFOST_MUGGED_5, + SFX_WFOST_RUN_1, + SFX_WFOST_RUN_2, + SFX_WFOST_RUN_3, + SFX_WFOST_RUN_4, + SFX_WFOST_RUN_5, + SFX_WFOST_RUN_6, + SFX_WFOST_RUN_7, + SFX_WFOST_SAVED_1, + SFX_WFOST_SAVED_2, + SFX_WFOST_SAVED_3, + SFX_WFOST_SAVED_4, + SFX_WFOST_TAXI_1, + + SFX_WFYST_BLOCKED_1, + SFX_WFYST_BLOCKED_2, + SFX_WFYST_BLOCKED_3, + SFX_WFYST_BLOCKED_4, + SFX_WFYST_BLOCKED_5, + SFX_WFYST_BLOCKED_6, + SFX_WFYST_BUMP_1, + SFX_WFYST_BUMP_2, + SFX_WFYST_BUMP_3, + SFX_WFYST_BUMP_4, + SFX_WFYST_BUMP_5, + SFX_WFYST_BUMP_6, + SFX_WFYST_BUMP_7, + SFX_WFYST_BUMP_8, + SFX_WFYST_BUMP_9, + SFX_WFYST_BUMP_10, + SFX_WFYST_CAR_CRASH_1, + SFX_WFYST_CAR_CRASH_2, + SFX_WFYST_CAR_CRASH_3, + SFX_WFYST_CAR_CRASH_4, + SFX_WFYST_CAR_CRASH_5, + SFX_WFYST_CAR_CRASH_6, + SFX_WFYST_CAR_CRASH_7, + SFX_WFYST_CAR_CRASH_8, + SFX_WFYST_CHAT_1, + SFX_WFYST_CHAT_2, + SFX_WFYST_CHAT_3, + SFX_WFYST_CHAT_4, + SFX_WFYST_CHAT_5, + SFX_WFYST_CHAT_6, + SFX_WFYST_CHAT_7, + SFX_WFYST_CHAT_8, + SFX_WFYST_CHAT_9, + SFX_WFYST_CHAT_10, + SFX_WFYST_DODGE_1, + SFX_WFYST_DODGE_2, + SFX_WFYST_DODGE_3, + SFX_WFYST_DODGE_4, + SFX_WFYST_DODGE_5, + SFX_WFYST_DODGE_6, + SFX_WFYST_DODGE_7, + SFX_WFYST_DODGE_8, + SFX_WFYST_DODGE_9, + SFX_WFYST_DODGE_10, + SFX_WFYST_FIGHT_1, + SFX_WFYST_FIGHT_2, + SFX_WFYST_FIGHT_3, + SFX_WFYST_FIGHT_4, + SFX_WFYST_FIGHT_5, + SFX_WFYST_FIGHT_6, + SFX_WFYST_FIGHT_7, + SFX_WFYST_GENERIC_CRASH_1, + SFX_WFYST_GENERIC_CRASH_2, + SFX_WFYST_GENERIC_CRASH_3, + SFX_WFYST_GENERIC_CRASH_4, + SFX_WFYST_GENERIC_CRASH_5, + SFX_WFYST_GENERIC_CRASH_6, + SFX_WFYST_GENERIC_CRASH_7, + SFX_WFYST_GENERIC_CRASH_8, + SFX_WFYST_GUN_COOL_1, + SFX_WFYST_GUN_COOL_2, + SFX_WFYST_GUN_COOL_3, + SFX_WFYST_GUN_COOL_4, + SFX_WFYST_GUN_COOL_5, + SFX_WFYST_JACKED_1, + SFX_WFYST_JACKED_2, + SFX_WFYST_JACKED_3, + SFX_WFYST_JACKED_4, + SFX_WFYST_JACKED_5, + SFX_WFYST_JACKED_6, + SFX_WFYST_JACKING_1, + SFX_WFYST_JACKING_2, + SFX_WFYST_JACKING_3, + SFX_WFYST_JACKING_4, + SFX_WFYST_LOST_1, + SFX_WFYST_MUGGED_1, + SFX_WFYST_MUGGED_2, + SFX_WFYST_MUGGING_1, + SFX_WFYST_MUGGING_2, + SFX_WFYST_MUGGING_3, + SFX_WFYST_MUGGING_4, + SFX_WFYST_SAVED_1, + SFX_WFYST_TAXI_1, + + SFX_COP_VOICE_1_ARREST_1, + SFX_COP_VOICE_1_ARREST_2, + SFX_COP_VOICE_1_ARREST_3, + SFX_COP_VOICE_1_ARREST_4, + SFX_COP_VOICE_1_PULLOUTWEAPON_1, + SFX_COP_VOICE_1_PULLOUTWEAPON_2, + SFX_COP_VOICE_1_PULLOUTWEAPON_3, + SFX_COP_VOICE_1_BUMP_1, + SFX_COP_VOICE_1_BUMP_2, + SFX_COP_VOICE_1_BUMP_3, + SFX_COP_VOICE_1_BUMP_4, + SFX_COP_VOICE_1_BUMP_5, + SFX_COP_VOICE_1_COP_LITTLECOPSAROUND_1, + SFX_COP_VOICE_1_COP_LITTLECOPSAROUND_2, + SFX_COP_VOICE_1_COP_LITTLECOPSAROUND_3, + SFX_COP_VOICE_1_COP_LITTLECOPSAROUND_4, + SFX_COP_VOICE_1_GUNAIMEDAT3_1, + SFX_COP_VOICE_1_GUNAIMEDAT3_2, + SFX_COP_VOICE_1_CAR_CRASH_1, + SFX_COP_VOICE_1_CAR_CRASH_2, + SFX_COP_VOICE_1_CAR_CRASH_3, + SFX_COP_VOICE_1_CAR_CRASH_4, + SFX_COP_VOICE_1_DODGE_1, + SFX_COP_VOICE_1_DODGE_2, + SFX_COP_VOICE_1_DODGE_3, + SFX_COP_VOICE_1_FIGHT_1, + SFX_COP_VOICE_1_FIGHT_2, + SFX_COP_VOICE_1_FIGHT_3, + SFX_COP_VOICE_1_FIGHT_4, + SFX_COP_VOICE_1_GUNAIMEDAT2_1, + SFX_COP_VOICE_1_GUNAIMEDAT2_2, + SFX_COP_VOICE_1_SAVED_1, + SFX_COP_VOICE_1_SAVED_2, + SFX_COP_VOICE_1_COP_ASK_FOR_ID_1, + SFX_COP_VOICE_1_COP_ASK_FOR_ID_2, + SFX_COP_VOICE_1_COP_ALONE_1, + SFX_COP_VOICE_1_COP_ALONE_2, + SFX_COP_VOICE_1_COP_ALONE_3, + SFX_COP_VOICE_1_COP_ALONE_4, + SFX_COP_VOICE_1_COP_MANYCOPSAROUND_1, + SFX_COP_VOICE_1_COP_MANYCOPSAROUND_2, + SFX_COP_VOICE_1_COP_TARGETING_1, + SFX_COP_VOICE_1_COP_TARGETING_2, + SFX_COP_VOICE_1_COP_TARGETING_3, + SFX_COP_VOICE_1_COP_TARGETING_4, + + SFX_COP_VOICE_2_ARREST_1, + SFX_COP_VOICE_2_ARREST_2, + SFX_COP_VOICE_2_ARREST_3, + SFX_COP_VOICE_2_ARREST_4, + SFX_COP_VOICE_2_PULLOUTWEAPON_1, + SFX_COP_VOICE_2_PULLOUTWEAPON_2, + SFX_COP_VOICE_2_PULLOUTWEAPON_3, + SFX_COP_VOICE_2_BUMP_1, + SFX_COP_VOICE_2_BUMP_2, + SFX_COP_VOICE_2_BUMP_3, + SFX_COP_VOICE_2_BUMP_4, + SFX_COP_VOICE_2_BUMP_5, + SFX_COP_VOICE_2_COP_LITTLECOPSAROUND_1, + SFX_COP_VOICE_2_COP_LITTLECOPSAROUND_2, + SFX_COP_VOICE_2_COP_LITTLECOPSAROUND_3, + SFX_COP_VOICE_2_COP_LITTLECOPSAROUND_4, + SFX_COP_VOICE_2_GUNAIMEDAT3_1, + SFX_COP_VOICE_2_GUNAIMEDAT3_2, + SFX_COP_VOICE_2_CAR_CRASH_1, + SFX_COP_VOICE_2_CAR_CRASH_2, + SFX_COP_VOICE_2_CAR_CRASH_3, + SFX_COP_VOICE_2_CAR_CRASH_4, + SFX_COP_VOICE_2_DODGE_1, + SFX_COP_VOICE_2_DODGE_2, + SFX_COP_VOICE_2_DODGE_3, + SFX_COP_VOICE_2_FIGHT_1, + SFX_COP_VOICE_2_FIGHT_2, + SFX_COP_VOICE_2_FIGHT_3, + SFX_COP_VOICE_2_FIGHT_4, + SFX_COP_VOICE_2_GUNAIMEDAT2_1, + SFX_COP_VOICE_2_GUNAIMEDAT2_2, + SFX_COP_VOICE_2_SAVED_1, + SFX_COP_VOICE_2_SAVED_2, + SFX_COP_VOICE_2_COP_ASK_FOR_ID_1, + SFX_COP_VOICE_2_COP_ASK_FOR_ID_2, + SFX_COP_VOICE_2_COP_ALONE_1, + SFX_COP_VOICE_2_COP_ALONE_2, + SFX_COP_VOICE_2_COP_ALONE_3, + SFX_COP_VOICE_2_COP_ALONE_4, + SFX_COP_VOICE_2_COP_MANYCOPSAROUND_1, + SFX_COP_VOICE_2_COP_MANYCOPSAROUND_2, + SFX_COP_VOICE_2_COP_TARGETING_1, + SFX_COP_VOICE_2_COP_TARGETING_2, + SFX_COP_VOICE_2_COP_TARGETING_3, + SFX_COP_VOICE_2_COP_TARGETING_4, + + SFX_COP_VOICE_3_ARREST_1, + SFX_COP_VOICE_3_ARREST_2, + SFX_COP_VOICE_3_ARREST_3, + SFX_COP_VOICE_3_ARREST_4, + SFX_COP_VOICE_3_PULLOUTWEAPON_1, + SFX_COP_VOICE_3_PULLOUTWEAPON_2, + SFX_COP_VOICE_3_PULLOUTWEAPON_3, + SFX_COP_VOICE_3_BUMP_1, + SFX_COP_VOICE_3_BUMP_2, + SFX_COP_VOICE_3_BUMP_3, + SFX_COP_VOICE_3_BUMP_4, + SFX_COP_VOICE_3_BUMP_5, + SFX_COP_VOICE_3_COP_LITTLECOPSAROUND_1, + SFX_COP_VOICE_3_COP_LITTLECOPSAROUND_2, + SFX_COP_VOICE_3_COP_LITTLECOPSAROUND_3, + SFX_COP_VOICE_3_COP_LITTLECOPSAROUND_4, + SFX_COP_VOICE_3_GUNAIMEDAT3_1, + SFX_COP_VOICE_3_GUNAIMEDAT3_2, + SFX_COP_VOICE_3_CAR_CRASH_1, + SFX_COP_VOICE_3_CAR_CRASH_2, + SFX_COP_VOICE_3_CAR_CRASH_3, + SFX_COP_VOICE_3_CAR_CRASH_4, + SFX_COP_VOICE_3_DODGE_1, + SFX_COP_VOICE_3_DODGE_2, + SFX_COP_VOICE_3_DODGE_3, + SFX_COP_VOICE_3_FIGHT_1, + SFX_COP_VOICE_3_FIGHT_2, + SFX_COP_VOICE_3_FIGHT_3, + SFX_COP_VOICE_3_FIGHT_4, + SFX_COP_VOICE_3_GUNAIMEDAT2_1, + SFX_COP_VOICE_3_GUNAIMEDAT2_2, + SFX_COP_VOICE_3_SAVED_1, + SFX_COP_VOICE_3_SAVED_2, + SFX_COP_VOICE_3_COP_ASK_FOR_ID_1, + SFX_COP_VOICE_3_COP_ASK_FOR_ID_2, + SFX_COP_VOICE_3_COP_ALONE_1, + SFX_COP_VOICE_3_COP_ALONE_2, + SFX_COP_VOICE_3_COP_ALONE_3, + SFX_COP_VOICE_3_COP_ALONE_4, + SFX_COP_VOICE_3_COP_MANYCOPSAROUND_1, + SFX_COP_VOICE_3_COP_MANYCOPSAROUND_2, + SFX_COP_VOICE_3_COP_TARGETING_1, + SFX_COP_VOICE_3_COP_TARGETING_2, + SFX_COP_VOICE_3_COP_TARGETING_3, + SFX_COP_VOICE_3_COP_TARGETING_4, + + SFX_COP_VOICE_4_ARREST_1, + SFX_COP_VOICE_4_ARREST_2, + SFX_COP_VOICE_4_ARREST_3, + SFX_COP_VOICE_4_ARREST_4, + SFX_COP_VOICE_4_PULLOUTWEAPON_1, + SFX_COP_VOICE_4_PULLOUTWEAPON_2, + SFX_COP_VOICE_4_PULLOUTWEAPON_3, + SFX_COP_VOICE_4_BUMP_1, + SFX_COP_VOICE_4_BUMP_2, + SFX_COP_VOICE_4_BUMP_3, + SFX_COP_VOICE_4_BUMP_4, + SFX_COP_VOICE_4_BUMP_5, + SFX_COP_VOICE_4_COP_LITTLECOPSAROUND_1, + SFX_COP_VOICE_4_COP_LITTLECOPSAROUND_2, + SFX_COP_VOICE_4_COP_LITTLECOPSAROUND_3, + SFX_COP_VOICE_4_COP_LITTLECOPSAROUND_4, + SFX_COP_VOICE_4_GUNAIMEDAT3_1, + SFX_COP_VOICE_4_GUNAIMEDAT3_2, + SFX_COP_VOICE_4_CAR_CRASH_1, + SFX_COP_VOICE_4_CAR_CRASH_2, + SFX_COP_VOICE_4_CAR_CRASH_3, + SFX_COP_VOICE_4_CAR_CRASH_4, + SFX_COP_VOICE_4_DODGE_1, + SFX_COP_VOICE_4_DODGE_2, + SFX_COP_VOICE_4_DODGE_3, + SFX_COP_VOICE_4_FIGHT_1, + SFX_COP_VOICE_4_FIGHT_2, + SFX_COP_VOICE_4_FIGHT_3, + SFX_COP_VOICE_4_FIGHT_4, + SFX_COP_VOICE_4_GUNAIMEDAT2_1, + SFX_COP_VOICE_4_GUNAIMEDAT2_2, + SFX_COP_VOICE_4_SAVED_1, + SFX_COP_VOICE_4_SAVED_2, + SFX_COP_VOICE_4_COP_ASK_FOR_ID_1, + SFX_COP_VOICE_4_COP_ASK_FOR_ID_2, + SFX_COP_VOICE_4_COP_ALONE_1, + SFX_COP_VOICE_4_COP_ALONE_2, + SFX_COP_VOICE_4_COP_ALONE_3, + SFX_COP_VOICE_4_COP_ALONE_4, + SFX_COP_VOICE_4_COP_MANYCOPSAROUND_1, + SFX_COP_VOICE_4_COP_MANYCOPSAROUND_2, + SFX_COP_VOICE_4_COP_TARGETING_1, + SFX_COP_VOICE_4_COP_TARGETING_2, + SFX_COP_VOICE_4_COP_TARGETING_3, + SFX_COP_VOICE_4_COP_TARGETING_4, + + SFX_COP_VOICE_5_ARREST_1, + SFX_COP_VOICE_5_ARREST_2, + SFX_COP_VOICE_5_ARREST_3, + SFX_COP_VOICE_5_ARREST_4, + SFX_COP_VOICE_5_PULLOUTWEAPON_1, + SFX_COP_VOICE_5_PULLOUTWEAPON_2, + SFX_COP_VOICE_5_PULLOUTWEAPON_3, + SFX_COP_VOICE_5_BUMP_1, + SFX_COP_VOICE_5_BUMP_2, + SFX_COP_VOICE_5_BUMP_3, + SFX_COP_VOICE_5_BUMP_4, + SFX_COP_VOICE_5_BUMP_5, + SFX_COP_VOICE_5_COP_LITTLECOPSAROUND_1, + SFX_COP_VOICE_5_COP_LITTLECOPSAROUND_2, + SFX_COP_VOICE_5_COP_LITTLECOPSAROUND_3, + SFX_COP_VOICE_5_COP_LITTLECOPSAROUND_4, + SFX_COP_VOICE_5_GUNAIMEDAT3_1, + SFX_COP_VOICE_5_GUNAIMEDAT3_2, + SFX_COP_VOICE_5_CAR_CRASH_1, + SFX_COP_VOICE_5_CAR_CRASH_2, + SFX_COP_VOICE_5_CAR_CRASH_3, + SFX_COP_VOICE_5_CAR_CRASH_4, + SFX_COP_VOICE_5_DODGE_1, + SFX_COP_VOICE_5_DODGE_2, + SFX_COP_VOICE_5_DODGE_3, + SFX_COP_VOICE_5_FIGHT_1, + SFX_COP_VOICE_5_FIGHT_2, + SFX_COP_VOICE_5_FIGHT_3, + SFX_COP_VOICE_5_FIGHT_4, + SFX_COP_VOICE_5_GUNAIMEDAT2_1, + SFX_COP_VOICE_5_GUNAIMEDAT2_2, + SFX_COP_VOICE_5_SAVED_1, + SFX_COP_VOICE_5_SAVED_2, + SFX_COP_VOICE_5_COP_ASK_FOR_ID_1, + SFX_COP_VOICE_5_COP_ASK_FOR_ID_2, + SFX_COP_VOICE_5_COP_ALONE_1, + SFX_COP_VOICE_5_COP_ALONE_2, + SFX_COP_VOICE_5_COP_ALONE_3, + SFX_COP_VOICE_5_COP_ALONE_4, + SFX_COP_VOICE_5_COP_MANYCOPSAROUND_1, + SFX_COP_VOICE_5_COP_MANYCOPSAROUND_2, + SFX_COP_VOICE_5_COP_TARGETING_1, + SFX_COP_VOICE_5_COP_TARGETING_2, + SFX_COP_VOICE_5_COP_TARGETING_3, + SFX_COP_VOICE_5_COP_TARGETING_4, + + FIRST_PLAYER_COMMENT(SFX_PLAYER_ANGRY_BUSTED_1), + SFX_PLAYER_ANGRY_BUSTED_2, + SFX_PLAYER_ANGRY_BUSTED_3, + SFX_PLAYER_ANGRY_BUSTED_4, + SFX_PLAYER_ANGRY_BUSTED_5, + SFX_PLAYER_ANGRY_BUSTED_6, + SFX_PLAYER_ANGRY_BUSTED_7, + SFX_PLAYER_ANGRY_BUSTED_8, + SFX_PLAYER_ANGRY_BUSTED_9, + SFX_PLAYER_ANGRY_BUSTED_10, + SFX_PLAYER_ANGRY_BUSTED_11, + SFX_PLAYER_ANGRY_BUSTED_12, + SFX_PLAYER_ANGRY_BUSTED_13, + SFX_PLAYER_ANGRY_BUSTED_14, + SFX_PLAYER_ANGRY_BUSTED_15, + SFX_PLAYER_ANGRY_BUSTED_16, + SFX_PLAYER_ANGRY_BUSTED_17, + SFX_PLAYER_ANGRY_BUSTED_18, + SFX_PLAYER_ANGRY_BUSTED_19, + SFX_PLAYER_ANGRY_BUSTED_20, + SFX_PLAYER_ANGRY_BUSTED_21, + SFX_PLAYER_ANGRY_BUSTED_22, + SFX_PLAYER_ANGRY_BUSTED_23, + SFX_PLAYER_ANGRY_BUSTED_24, + SFX_PLAYER_ANGRY_BUSTED_25, + SFX_PLAYER_ANGRY_BUSTED_26, + SFX_PLAYER_ANGRY_BUSTED_27, + SFX_PLAYER_ANGRY_BUSTED_28, + SFX_PLAYER_ANGRY_BUSTED_29, + SFX_PLAYER_ANGRY_BUSTED_30, + SFX_PLAYER_ANGRY_BUSTED_31, + SFX_PLAYER_ANGRY_BUSTED_32, + SFX_PLAYER_ANGRY_BUSTED_33, + SFX_PLAYER_ANGRY_BUSTED_34, + SFX_PLAYER_ANGRY_BUSTED_35, + SFX_PLAYER_ANGRY_BUSTED_36, + SFX_PLAYER_ANGRY_BUSTED_37, + SFX_PLAYER_ANGRY_BUSTED_38, + SFX_PLAYER_ANGRY_CHASED_1, + SFX_PLAYER_ANGRY_CHASED_2, + SFX_PLAYER_ANGRY_CHASED_3, + SFX_PLAYER_ANGRY_CHASED_4, + SFX_PLAYER_ANGRY_CHASED_5, + SFX_PLAYER_ANGRY_CHASED_6, + SFX_PLAYER_ANGRY_CHASED_7, + SFX_PLAYER_ANGRY_CHASED_8, + SFX_PLAYER_ANGRY_CHASED_9, + SFX_PLAYER_ANGRY_CRASH_1, + SFX_PLAYER_ANGRY_CRASH_2, + SFX_PLAYER_ANGRY_CRASH_3, + SFX_PLAYER_ANGRY_CRASH_4, + SFX_PLAYER_ANGRY_CRASH_5, + SFX_PLAYER_ANGRY_CRASH_6, + SFX_PLAYER_ANGRY_CRASH_7, + SFX_PLAYER_ANGRY_CRASH_8, + SFX_PLAYER_ANGRY_CRASH_9, + SFX_PLAYER_ANGRY_CRASH_10, + SFX_PLAYER_ANGRY_CRASH_11, + SFX_PLAYER_ANGRY_CRASH_12, + SFX_PLAYER_ANGRY_CRASH_13, + SFX_PLAYER_ANGRY_CRASH_14, + SFX_PLAYER_ANGRY_CRASH_15, + SFX_PLAYER_ANGRY_CRASH_16, + SFX_PLAYER_ANGRY_CRASH_17, + SFX_PLAYER_ANGRY_CRASH_18, + SFX_PLAYER_ANGRY_CRASH_19, + SFX_PLAYER_ANGRY_CRASH_20, + SFX_PLAYER_ANGRY_CRASH_21, + SFX_PLAYER_ANGRY_CRASH_22, + SFX_PLAYER_ANGRY_CRASH_23, + SFX_PLAYER_ANGRY_CRASH_24, + SFX_PLAYER_ANGRY_CRASH_25, + SFX_PLAYER_ANGRY_CRASH_26, + SFX_PLAYER_ANGRY_CRASH_27, + SFX_PLAYER_ANGRY_CRASH_28, + SFX_PLAYER_ANGRY_CRASH_29, + SFX_PLAYER_ANGRY_CRASH_30, + SFX_PLAYER_ANGRY_CRASH_31, + SFX_PLAYER_ANGRY_CRASH_32, + SFX_PLAYER_ANGRY_CRASH_33, + SFX_PLAYER_ANGRY_CRASH_34, + SFX_PLAYER_ANGRY_CRASH_35, + SFX_PLAYER_ANGRY_CRASH_36, + SFX_PLAYER_ANGRY_CRASH_37, + SFX_PLAYER_ANGRY_CRASH_38, + SFX_PLAYER_ANGRY_CRASH_39, + SFX_PLAYER_ANGRY_CRASH_40, + SFX_PLAYER_ANGRY_CRASH_41, + SFX_PLAYER_ANGRY_FIGHT_1, + SFX_PLAYER_ANGRY_FIGHT_2, + SFX_PLAYER_ANGRY_FIGHT_3, + SFX_PLAYER_ANGRY_FIGHT_4, + SFX_PLAYER_ANGRY_FIGHT_5, + SFX_PLAYER_ANGRY_FIGHT_6, + SFX_PLAYER_ANGRY_FIGHT_7, + SFX_PLAYER_ANGRY_FIGHT_8, + SFX_PLAYER_ANGRY_FIGHT_9, + SFX_PLAYER_ANGRY_FIGHT_10, + SFX_PLAYER_ANGRY_FIGHT_11, + SFX_PLAYER_ANGRY_FIGHT_12, + SFX_PLAYER_ANGRY_FIGHT_13, + SFX_PLAYER_ANGRY_FIGHT_14, + SFX_PLAYER_ANGRY_FIGHT_15, + SFX_PLAYER_ANGRY_FIGHT_16, + SFX_PLAYER_ANGRY_FIGHT_17, + SFX_PLAYER_ANGRY_FIGHT_18, + SFX_PLAYER_ANGRY_FIGHT_19, + SFX_PLAYER_ANGRY_FIGHT_20, + SFX_PLAYER_ANGRY_FIGHT_21, + SFX_PLAYER_ANGRY_FIGHT_22, + SFX_PLAYER_ANGRY_FIGHT_23, + SFX_PLAYER_ANGRY_FIGHT_24, + SFX_PLAYER_ANGRY_FIGHT_25, + SFX_PLAYER_ANGRY_FIGHT_26, + SFX_PLAYER_ANGRY_FIGHT_27, + SFX_PLAYER_ANGRY_FIGHT_28, + SFX_PLAYER_ANGRY_FIGHT_29, + SFX_PLAYER_ANGRY_FIGHT_30, + SFX_PLAYER_ANGRY_FIGHT_31, + SFX_PLAYER_ANGRY_FIGHT_32, + SFX_PLAYER_ANGRY_FIGHT_33, + SFX_PLAYER_ANGRY_FIGHT_34, + SFX_PLAYER_ANGRY_FIGHT_35, + SFX_PLAYER_ANGRY_FIGHT_36, + SFX_PLAYER_ANGRY_FIGHT_37, + SFX_PLAYER_ANGRY_FIGHT_38, + SFX_PLAYER_ANGRY_FIGHT_39, + SFX_PLAYER_ANGRY_FIGHT_40, + SFX_PLAYER_ANGRY_FIGHT_41, + SFX_PLAYER_ANGRY_FIGHT_42, + SFX_PLAYER_ANGRY_FIGHT_43, + SFX_PLAYER_ANGRY_FIGHT_44, + SFX_PLAYER_ANGRY_FIGHT_45, + SFX_PLAYER_ANGRY_FIGHT_46, + SFX_PLAYER_ANGRY_FIGHT_47, + SFX_PLAYER_ANGRY_FIGHT_48, + SFX_PLAYER_ANGRY_FIGHT_49, + SFX_PLAYER_ANGRY_FIGHT_50, + SFX_PLAYER_ANGRY_FIGHT_51, + SFX_PLAYER_ANGRY_FIGHT_52, + SFX_PLAYER_ANGRY_FIGHT_53, + SFX_PLAYER_ANGRY_FIGHT_54, + SFX_PLAYER_ANGRY_FIGHT_55, + SFX_PLAYER_ANGRY_FIGHT_56, + SFX_PLAYER_ANGRY_FIGHT_57, + SFX_PLAYER_ANGRY_FIGHT_58, + SFX_PLAYER_ANGRY_FIGHT_59, + SFX_PLAYER_ANGRY_FIGHT_60, + SFX_PLAYER_ANGRY_FIGHT_61, + SFX_PLAYER_ANGRY_JACKED_1, + SFX_PLAYER_ANGRY_JACKED_2, + SFX_PLAYER_ANGRY_JACKED_3, + SFX_PLAYER_ANGRY_JACKED_4, + SFX_PLAYER_ANGRY_JACKED_5, + SFX_PLAYER_ANGRY_JACKED_6, + SFX_PLAYER_ANGRY_JACKED_7, + SFX_PLAYER_ANGRY_JACKED_8, + SFX_PLAYER_ANGRY_JACKED_9, + SFX_PLAYER_ANGRY_JACKED_10, + SFX_PLAYER_ANGRY_JACKED_11, + SFX_PLAYER_ANGRY_JACKED_12, + SFX_PLAYER_ANGRY_JACKED_13, + SFX_PLAYER_ANGRY_JACKED_14, + SFX_PLAYER_ANGRY_JACKED_15, + SFX_PLAYER_ANGRY_JACKED_16, + SFX_PLAYER_ANGRY_JACKED_17, + SFX_PLAYER_ANGRY_JACKED_18, + SFX_PLAYER_ANGRY_JACKED_19, + SFX_PLAYER_ANGRY_JACKED_20, + SFX_PLAYER_ANGRY_JACKED_21, + SFX_PLAYER_ANGRY_JACKED_22, + SFX_PLAYER_ANGRY_JACKED_23, + SFX_PLAYER_ANGRY_JACKED_24, + SFX_PLAYER_ANGRY_JACKED_25, + SFX_PLAYER_ANGRY_JACKED_26, + SFX_PLAYER_ANGRY_JACKED_27, + SFX_PLAYER_ANGRY_JACKED_28, + SFX_PLAYER_ANGRY_JACKED_29, + SFX_PLAYER_ANGRY_JACKED_30, + SFX_PLAYER_ANGRY_JACKED_31, + SFX_PLAYER_ANGRY_JACKED_32, + SFX_PLAYER_ANGRY_JACKED_33, + SFX_PLAYER_ANGRY_JACKING_1, + SFX_PLAYER_ANGRY_JACKING_2, + SFX_PLAYER_ANGRY_JACKING_3, + SFX_PLAYER_ANGRY_JACKING_4, + SFX_PLAYER_ANGRY_JACKING_5, + SFX_PLAYER_ANGRY_JACKING_6, + SFX_PLAYER_ANGRY_JACKING_7, + SFX_PLAYER_ANGRY_JACKING_8, + SFX_PLAYER_ANGRY_JACKING_9, + SFX_PLAYER_ANGRY_JACKING_10, + SFX_PLAYER_ANGRY_JACKING_11, + SFX_PLAYER_ANGRY_JACKING_12, + SFX_PLAYER_ANGRY_JACKING_13, + SFX_PLAYER_ANGRY_JACKING_14, + SFX_PLAYER_ANGRY_JACKING_15, + SFX_PLAYER_ANGRY_JACKING_16, + SFX_PLAYER_ANGRY_JACKING_17, + SFX_PLAYER_ANGRY_JACKING_18, + SFX_PLAYER_ANGRY_JACKING_19, + SFX_PLAYER_ANGRY_JACKING_20, + SFX_PLAYER_ANGRY_JACKING_21, + SFX_PLAYER_ANGRY_JACKING_22, + SFX_PLAYER_ANGRY_JACKING_23, + SFX_PLAYER_ANGRY_JACKING_24, + SFX_PLAYER_ANGRY_JACKING_25, + SFX_PLAYER_ANGRY_JACKING_26, + SFX_PLAYER_ANGRY_JACKING_27, + SFX_PLAYER_ANGRY_JACKING_28, + SFX_PLAYER_ANGRY_JACKING_29, + SFX_PLAYER_ANGRY_JACKING_30, + SFX_PLAYER_ANGRY_JACKING_31, + SFX_PLAYER_ANGRY_JACKING_32, + SFX_PLAYER_ANGRY_JACKING_33, + SFX_PLAYER_ANGRY_JACKING_34, + SFX_PLAYER_ANGRY_JACKING_35, + SFX_PLAYER_ANGRY_JACKING_36, + SFX_PLAYER_ANGRY_JACKING_37, + SFX_PLAYER_ANGRY_JACKING_38, + SFX_PLAYER_ANGRY_JACKING_39, + SFX_PLAYER_ANGRY_JACKING_40, + SFX_PLAYER_ANGRY_JACKING_41, + SFX_PLAYER_ANGRY_JACKING_42, + SFX_PLAYER_ANGRY_JACKING_43, + SFX_PLAYER_ANGRY_PICK_UP_CASH_1, + SFX_PLAYER_ANGRY_PICK_UP_CASH_2, + SFX_PLAYER_ANGRY_PICK_UP_CASH_3, + SFX_PLAYER_ANGRY_PICK_UP_CASH_4, + SFX_PLAYER_ANGRY_PICK_UP_CASH_5, + SFX_PLAYER_ANGRY_PICK_UP_CASH_6, + SFX_PLAYER_ANGRY_PICK_UP_CASH_7, + SFX_PLAYER_ANGRY_PICK_UP_CASH_8, + SFX_PLAYER_ANGRY_PICK_UP_CASH_9, + SFX_PLAYER_ANGRY_PICK_UP_CASH_10, + SFX_PLAYER_ANGRY_PICK_UP_CASH_11, + SFX_PLAYER_ANGRY_PICK_UP_CASH_12, + SFX_PLAYER_ANGRY_PICK_UP_HOOKER_1, + SFX_PLAYER_ANGRY_PICK_UP_HOOKER_2, + SFX_PLAYER_ANGRY_PICK_UP_HOOKER_3, + SFX_PLAYER_ANGRY_PICK_UP_HOOKER_4, + SFX_PLAYER_ANGRY_PICK_UP_HOOKER_5, + SFX_PLAYER_ANGRY_PICK_UP_HOOKER_6, + SFX_PLAYER_ANGRY_PULL_GUN_1, + SFX_PLAYER_ANGRY_PULL_GUN_2, + SFX_PLAYER_ANGRY_PULL_GUN_3, + SFX_PLAYER_ANGRY_PULL_GUN_4, + SFX_PLAYER_ANGRY_PULL_GUN_5, + SFX_PLAYER_ANGRY_PULL_GUN_6, + SFX_PLAYER_ANGRY_PULL_GUN_7, + SFX_PLAYER_ANGRY_PULL_GUN_8, + SFX_PLAYER_ANGRY_PULL_GUN_9, + SFX_PLAYER_ANGRY_PULL_GUN_10, + SFX_PLAYER_ANGRY_PULL_GUN_11, + SFX_PLAYER_ANGRY_PULL_GUN_12, + SFX_PLAYER_ANGRY_PULL_GUN_13, + SFX_PLAYER_ANGRY_PULL_GUN_14, + SFX_PLAYER_ANGRY_PULL_GUN_15, + SFX_PLAYER_ANGRY_PULL_GUN_16, + SFX_PLAYER_ANGRY_PULL_GUN_17, + SFX_PLAYER_ANGRY_PULL_GUN_18, + SFX_PLAYER_ANGRY_PULL_GUN_19, + SFX_PLAYER_ANGRY_PULL_GUN_20, + SFX_PLAYER_ANGRY_PULL_GUN_21, + SFX_PLAYER_ANGRY_PULL_GUN_22, + SFX_PLAYER_ANGRY_PULL_GUN_23, + SFX_PLAYER_ANGRY_PULL_GUN_24, + SFX_PLAYER_ANGRY_PULL_GUN_25, + SFX_PLAYER_ANGRY_PULL_GUN_26, + SFX_PLAYER_ANGRY_PULL_GUN_27, + SFX_PLAYER_ANGRY_PULL_GUN_28, + SFX_PLAYER_ANGRY_PULL_GUN_29, + SFX_PLAYER_ANGRY_PULL_GUN_30, + SFX_PLAYER_ANGRY_PULL_GUN_31, + SFX_PLAYER_ANGRY_PULL_GUN_32, + SFX_PLAYER_ANGRY_PULL_GUN_33, + SFX_PLAYER_ANGRY_PULL_GUN_34, + SFX_PLAYER_ANGRY_PULL_GUN_35, + SFX_PLAYER_ANGRY_PULL_GUN_36, + SFX_PLAYER_ANGRY_PULL_GUN_37, + SFX_PLAYER_ANGRY_PULL_GUN_38, + SFX_PLAYER_ANGRY_PULL_GUN_39, + SFX_PLAYER_ANGRY_PULL_GUN_40, + SFX_PLAYER_ANGRY_PULL_GUN_41, + SFX_PLAYER_ANGRY_PULL_GUN_42, + SFX_PLAYER_ANGRY_PULL_GUN_43, + SFX_PLAYER_ANGRY_PULL_GUN_44, + SFX_PLAYER_ANGRY_PULL_GUN_45, + SFX_PLAYER_ANGRY_PULL_GUN_46, + SFX_PLAYER_ANGRY_PULL_GUN_47, + SFX_PLAYER_ANGRY_PULL_GUN_48, + SFX_PLAYER_ANGRY_PULL_GUN_49, + SFX_PLAYER_ANGRY_PULL_GUN_50, + SFX_PLAYER_ANGRY_PULL_GUN_51, + SFX_PLAYER_ANGRY_PULL_GUN_52, + SFX_PLAYER_ANGRY_SEX_1, + SFX_PLAYER_ANGRY_SEX_2, + SFX_PLAYER_ANGRY_SEX_3, + SFX_PLAYER_ANGRY_SEX_4, + SFX_PLAYER_ANGRY_SEX_5, + SFX_PLAYER_ANGRY_SEX_6, + SFX_PLAYER_ANGRY_SEX_7, + SFX_PLAYER_ANGRY_SEX_8, + SFX_PLAYER_ANGRY_SEX_9, + SFX_PLAYER_ANGRY_SEX_10, + SFX_PLAYER_ANGRY_SEX_11, + SFX_PLAYER_ANGRY_SEX_12, + SFX_PLAYER_ANGRY_SEX_13, + SFX_PLAYER_ANGRY_SEX_14, + SFX_PLAYER_ANGRY_SEX_15, + SFX_PLAYER_ANGRY_SEX_16, + SFX_PLAYER_ANGRY_SEX_17, + SFX_PLAYER_ANGRY_SEX_18, + SFX_PLAYER_ANGRY_SHOOT_1, + SFX_PLAYER_ANGRY_SHOOT_2, + SFX_PLAYER_ANGRY_SHOOT_3, + SFX_PLAYER_ANGRY_SHOOT_4, + SFX_PLAYER_ANGRY_SHOOT_5, + SFX_PLAYER_ANGRY_SHOOT_6, + SFX_PLAYER_ANGRY_SHOOT_7, + SFX_PLAYER_ANGRY_SHOOT_8, + SFX_PLAYER_ANGRY_SHOOT_9, + SFX_PLAYER_ANGRY_SHOOT_10, + SFX_PLAYER_ANGRY_SHOOT_11, + SFX_PLAYER_ANGRY_SHOOT_12, + SFX_PLAYER_ANGRY_SHOOT_13, + SFX_PLAYER_ANGRY_SHOOT_14, + SFX_PLAYER_ANGRY_SHOOT_15, + SFX_PLAYER_ANGRY_SHOOT_16, + SFX_PLAYER_ANGRY_SHOOT_17, + SFX_PLAYER_ANGRY_SHOOT_18, + SFX_PLAYER_ANGRY_SHOOT_19, + SFX_PLAYER_ANGRY_SHOOT_20, + SFX_PLAYER_ANGRY_SHOOT_21, + SFX_PLAYER_ANGRY_SHOOT_22, + SFX_PLAYER_ANGRY_SHOOT_23, + SFX_PLAYER_ANGRY_SHOOT_24, + SFX_PLAYER_ANGRY_SHOOT_25, + SFX_PLAYER_ANGRY_SHOOT_26, + SFX_PLAYER_ANGRY_SHOOT_27, + SFX_PLAYER_ANGRY_SHOOT_28, + SFX_PLAYER_ANGRY_SHOOT_29, + SFX_PLAYER_ANGRY_SHOOT_30, + SFX_PLAYER_ANGRY_SHOOT_31, + SFX_PLAYER_ANGRY_SHOOT_32, + SFX_PLAYER_ANGRY_SHOOT_33, + SFX_PLAYER_ANGRY_SHOOT_34, + SFX_PLAYER_ANGRY_SHOOT_35, + SFX_PLAYER_ANGRY_SHOOT_36, + SFX_PLAYER_ANGRY_SHOOT_37, + SFX_PLAYER_ANGRY_SHOOT_38, + SFX_PLAYER_ANGRY_SHOOT_39, + + SFX_PLAYER_CALM_BUSTED_1, + SFX_PLAYER_CALM_BUSTED_2, + SFX_PLAYER_CALM_BUSTED_3, + SFX_PLAYER_CALM_BUSTED_4, + SFX_PLAYER_CALM_BUSTED_5, + SFX_PLAYER_CALM_BUSTED_6, + SFX_PLAYER_CALM_BUSTED_7, + SFX_PLAYER_CALM_BUSTED_8, + SFX_PLAYER_CALM_BUSTED_9, + SFX_PLAYER_CALM_BUSTED_10, + SFX_PLAYER_CALM_BUSTED_11, + SFX_PLAYER_CALM_BUSTED_12, + SFX_PLAYER_CALM_BUSTED_13, + SFX_PLAYER_CALM_BUSTED_14, + SFX_PLAYER_CALM_BUSTED_15, + SFX_PLAYER_CALM_BUSTED_16, + SFX_PLAYER_CALM_BUSTED_17, + SFX_PLAYER_CALM_BUSTED_18, + SFX_PLAYER_CALM_BUSTED_19, + SFX_PLAYER_CALM_BUSTED_20, + SFX_PLAYER_CALM_BUSTED_21, + SFX_PLAYER_CALM_BUSTED_22, + SFX_PLAYER_CALM_CHASED_1, + SFX_PLAYER_CALM_CHASED_2, + SFX_PLAYER_CALM_CHASED_3, + SFX_PLAYER_CALM_CHASED_4, + SFX_PLAYER_CALM_CHASED_5, + SFX_PLAYER_CALM_CHASED_6, + SFX_PLAYER_CALM_CHASED_7, + SFX_PLAYER_CALM_CHASED_8, + SFX_PLAYER_CALM_CHASED_9, + SFX_PLAYER_CALM_CHASED_10, + SFX_PLAYER_CALM_CHASED_11, + SFX_PLAYER_CALM_CHASED_12, + SFX_PLAYER_CALM_CHASED_13, + SFX_PLAYER_CALM_CHASED_14, + SFX_PLAYER_CALM_CHASED_15, + SFX_PLAYER_CALM_CHASED_16, + SFX_PLAYER_CALM_CHASED_17, + SFX_PLAYER_CALM_CHASED_18, + SFX_PLAYER_CALM_CHASED_19, + SFX_PLAYER_CALM_CHASED_20, + SFX_PLAYER_CALM_CRASH_1, + SFX_PLAYER_CALM_CRASH_2, + SFX_PLAYER_CALM_CRASH_3, + SFX_PLAYER_CALM_CRASH_4, + SFX_PLAYER_CALM_CRASH_5, + SFX_PLAYER_CALM_CRASH_6, + SFX_PLAYER_CALM_CRASH_7, + SFX_PLAYER_CALM_CRASH_8, + SFX_PLAYER_CALM_CRASH_9, + SFX_PLAYER_CALM_CRASH_10, + SFX_PLAYER_CALM_CRASH_11, + SFX_PLAYER_CALM_CRASH_12, + SFX_PLAYER_CALM_CRASH_13, + SFX_PLAYER_CALM_CRASH_14, + SFX_PLAYER_CALM_CRASH_15, + SFX_PLAYER_CALM_CRASH_16, + SFX_PLAYER_CALM_CRASH_17, + SFX_PLAYER_CALM_CRASH_18, + SFX_PLAYER_CALM_CRASH_19, + SFX_PLAYER_CALM_CRASH_20, + SFX_PLAYER_CALM_CRASH_21, + SFX_PLAYER_CALM_CRASH_22, + SFX_PLAYER_CALM_CRASH_23, + SFX_PLAYER_CALM_CRASH_24, + SFX_PLAYER_CALM_CRASH_25, + SFX_PLAYER_CALM_CRASH_26, + SFX_PLAYER_CALM_CRASH_27, + SFX_PLAYER_CALM_CRASH_28, + SFX_PLAYER_CALM_CRASH_29, + SFX_PLAYER_CALM_CRASH_30, + SFX_PLAYER_CALM_CRASH_31, + SFX_PLAYER_CALM_CRASH_32, + SFX_PLAYER_CALM_CRASH_33, + SFX_PLAYER_CALM_CRASH_34, + SFX_PLAYER_CALM_CRASH_35, + SFX_PLAYER_CALM_CRASH_36, + SFX_PLAYER_CALM_CRASH_37, + SFX_PLAYER_CALM_CRASH_38, + SFX_PLAYER_CALM_CRASH_39, + SFX_PLAYER_CALM_CRASH_40, + SFX_PLAYER_CALM_CRASH_41, + SFX_PLAYER_CALM_CRASH_42, + SFX_PLAYER_CALM_CRASH_43, + SFX_PLAYER_CALM_FIGHT_1, + SFX_PLAYER_CALM_FIGHT_2, + SFX_PLAYER_CALM_FIGHT_3, + SFX_PLAYER_CALM_FIGHT_4, + SFX_PLAYER_CALM_FIGHT_5, + SFX_PLAYER_CALM_FIGHT_6, + SFX_PLAYER_CALM_FIGHT_7, + SFX_PLAYER_CALM_FIGHT_8, + SFX_PLAYER_CALM_FIGHT_9, + SFX_PLAYER_CALM_FIGHT_10, + SFX_PLAYER_CALM_FIGHT_11, + SFX_PLAYER_CALM_FIGHT_12, + SFX_PLAYER_CALM_FIGHT_13, + SFX_PLAYER_CALM_FIGHT_14, + SFX_PLAYER_CALM_FIGHT_15, + SFX_PLAYER_CALM_FIGHT_16, + SFX_PLAYER_CALM_FIGHT_17, + SFX_PLAYER_CALM_FIGHT_18, + SFX_PLAYER_CALM_FIGHT_19, + SFX_PLAYER_CALM_FIGHT_20, + SFX_PLAYER_CALM_FIGHT_21, + SFX_PLAYER_CALM_FIGHT_22, + SFX_PLAYER_CALM_FIGHT_23, + SFX_PLAYER_CALM_FIGHT_24, + SFX_PLAYER_CALM_FIGHT_25, + SFX_PLAYER_CALM_FIGHT_26, + SFX_PLAYER_CALM_FIGHT_27, + SFX_PLAYER_CALM_FIGHT_28, + SFX_PLAYER_CALM_FIGHT_29, + SFX_PLAYER_CALM_FIGHT_30, + SFX_PLAYER_CALM_FIGHT_31, + SFX_PLAYER_CALM_FIGHT_32, + SFX_PLAYER_CALM_FIGHT_33, + SFX_PLAYER_CALM_FIGHT_34, + SFX_PLAYER_CALM_FIGHT_35, + SFX_PLAYER_CALM_FIGHT_36, + SFX_PLAYER_CALM_FIGHT_37, + SFX_PLAYER_CALM_FIGHT_38, + SFX_PLAYER_CALM_FIGHT_39, + SFX_PLAYER_CALM_FIGHT_40, + SFX_PLAYER_CALM_FIGHT_41, + SFX_PLAYER_CALM_FIGHT_42, + SFX_PLAYER_CALM_FIGHT_43, + SFX_PLAYER_CALM_FIGHT_44, + SFX_PLAYER_CALM_FIGHT_45, + SFX_PLAYER_CALM_FIGHT_46, + SFX_PLAYER_CALM_FIGHT_47, + SFX_PLAYER_CALM_JACKED_1, + SFX_PLAYER_CALM_JACKED_2, + SFX_PLAYER_CALM_JACKED_3, + SFX_PLAYER_CALM_JACKED_4, + SFX_PLAYER_CALM_JACKED_5, + SFX_PLAYER_CALM_JACKED_6, + SFX_PLAYER_CALM_JACKED_7, + SFX_PLAYER_CALM_JACKED_8, + SFX_PLAYER_CALM_JACKED_9, + SFX_PLAYER_CALM_JACKED_10, + SFX_PLAYER_CALM_JACKED_11, + SFX_PLAYER_CALM_JACKED_12, + SFX_PLAYER_CALM_JACKED_13, + SFX_PLAYER_CALM_JACKED_14, + SFX_PLAYER_CALM_JACKED_15, + SFX_PLAYER_CALM_JACKED_16, + SFX_PLAYER_CALM_JACKED_17, + SFX_PLAYER_CALM_JACKED_18, + SFX_PLAYER_CALM_JACKED_19, + SFX_PLAYER_CALM_JACKED_20, + SFX_PLAYER_CALM_JACKED_21, + SFX_PLAYER_CALM_JACKED_22, + SFX_PLAYER_CALM_JACKED_23, + SFX_PLAYER_CALM_JACKED_24, + SFX_PLAYER_CALM_JACKING_1, + SFX_PLAYER_CALM_JACKING_2, + SFX_PLAYER_CALM_JACKING_3, + SFX_PLAYER_CALM_JACKING_4, + SFX_PLAYER_CALM_JACKING_5, + SFX_PLAYER_CALM_JACKING_6, + SFX_PLAYER_CALM_JACKING_7, + SFX_PLAYER_CALM_JACKING_8, + SFX_PLAYER_CALM_JACKING_9, + SFX_PLAYER_CALM_JACKING_10, + SFX_PLAYER_CALM_JACKING_11, + SFX_PLAYER_CALM_JACKING_12, + SFX_PLAYER_CALM_JACKING_13, + SFX_PLAYER_CALM_JACKING_14, + SFX_PLAYER_CALM_JACKING_15, + SFX_PLAYER_CALM_JACKING_16, + SFX_PLAYER_CALM_JACKING_17, + SFX_PLAYER_CALM_JACKING_18, + SFX_PLAYER_CALM_JACKING_19, + SFX_PLAYER_CALM_JACKING_20, + SFX_PLAYER_CALM_JACKING_21, + SFX_PLAYER_CALM_JACKING_22, + SFX_PLAYER_CALM_JACKING_23, + SFX_PLAYER_CALM_JACKING_24, + SFX_PLAYER_CALM_JACKING_25, + SFX_PLAYER_CALM_JACKING_26, + SFX_PLAYER_CALM_JACKING_27, + SFX_PLAYER_CALM_JACKING_28, + SFX_PLAYER_CALM_JACKING_29, + SFX_PLAYER_CALM_JACKING_30, + SFX_PLAYER_CALM_JACKING_31, + SFX_PLAYER_CALM_JACKING_32, + SFX_PLAYER_CALM_JACKING_33, + SFX_PLAYER_CALM_JACKING_34, + SFX_PLAYER_CALM_JACKING_35, + SFX_PLAYER_CALM_JACKING_36, + SFX_PLAYER_CALM_JACKING_37, + SFX_PLAYER_CALM_JACKING_38, + SFX_PLAYER_CALM_JACKING_39, + SFX_PLAYER_CALM_JACKING_40, + SFX_PLAYER_CALM_PICK_UP_CASH_1, + SFX_PLAYER_CALM_PICK_UP_CASH_2, + SFX_PLAYER_CALM_PICK_UP_CASH_3, + SFX_PLAYER_CALM_PICK_UP_CASH_4, + SFX_PLAYER_CALM_PICK_UP_CASH_5, + SFX_PLAYER_CALM_PICK_UP_CASH_6, + SFX_PLAYER_CALM_PICK_UP_CASH_7, + SFX_PLAYER_CALM_PICK_UP_CASH_8, + SFX_PLAYER_CALM_PICK_UP_CASH_9, + SFX_PLAYER_CALM_PICK_UP_CASH_10, + SFX_PLAYER_CALM_PICK_UP_CASH_11, + SFX_PLAYER_CALM_PICK_UP_HOOKER_1, + SFX_PLAYER_CALM_PICK_UP_HOOKER_2, + SFX_PLAYER_CALM_PICK_UP_HOOKER_3, + SFX_PLAYER_CALM_PICK_UP_HOOKER_4, + SFX_PLAYER_CALM_PICK_UP_HOOKER_5, + SFX_PLAYER_CALM_PICK_UP_HOOKER_6, + SFX_PLAYER_CALM_PICK_UP_HOOKER_7, + SFX_PLAYER_CALM_PICK_UP_HOOKER_8, + SFX_PLAYER_CALM_PICK_UP_HOOKER_9, + SFX_PLAYER_CALM_PICK_UP_HOOKER_10, + SFX_PLAYER_CALM_PICK_UP_HOOKER_11, + SFX_PLAYER_CALM_PICK_UP_HOOKER_12, + SFX_PLAYER_CALM_PICK_UP_HOOKER_13, + SFX_PLAYER_CALM_PICK_UP_HOOKER_14, + SFX_PLAYER_CALM_PICK_UP_HOOKER_15, + SFX_PLAYER_CALM_PICK_UP_HOOKER_16, + SFX_PLAYER_CALM_PICK_UP_HOOKER_17, + SFX_PLAYER_CALM_PICK_UP_HOOKER_18, + SFX_PLAYER_CALM_PICK_UP_HOOKER_19, + SFX_PLAYER_CALM_PICK_UP_HOOKER_20, + SFX_PLAYER_CALM_PICK_UP_HOOKER_21, + SFX_PLAYER_CALM_PICK_UP_HOOKER_22, + SFX_PLAYER_CALM_PULL_GUN_1, + SFX_PLAYER_CALM_PULL_GUN_2, + SFX_PLAYER_CALM_PULL_GUN_3, + SFX_PLAYER_CALM_PULL_GUN_4, + SFX_PLAYER_CALM_PULL_GUN_5, + SFX_PLAYER_CALM_PULL_GUN_6, + SFX_PLAYER_CALM_PULL_GUN_7, + SFX_PLAYER_CALM_PULL_GUN_8, + SFX_PLAYER_CALM_PULL_GUN_9, + SFX_PLAYER_CALM_PULL_GUN_10, + SFX_PLAYER_CALM_PULL_GUN_11, + SFX_PLAYER_CALM_PULL_GUN_12, + SFX_PLAYER_CALM_PULL_GUN_13, + SFX_PLAYER_CALM_PULL_GUN_14, + SFX_PLAYER_CALM_PULL_GUN_15, + SFX_PLAYER_CALM_PULL_GUN_16, + SFX_PLAYER_CALM_PULL_GUN_17, + SFX_PLAYER_CALM_PULL_GUN_18, + SFX_PLAYER_CALM_PULL_GUN_19, + SFX_PLAYER_CALM_PULL_GUN_20, + SFX_PLAYER_CALM_PULL_GUN_21, + SFX_PLAYER_CALM_PULL_GUN_22, + SFX_PLAYER_CALM_PULL_GUN_23, + SFX_PLAYER_CALM_PULL_GUN_24, + SFX_PLAYER_CALM_PULL_GUN_25, + SFX_PLAYER_CALM_PULL_GUN_26, + SFX_PLAYER_CALM_PULL_GUN_27, + SFX_PLAYER_CALM_PULL_GUN_28, + SFX_PLAYER_CALM_PULL_GUN_29, + SFX_PLAYER_CALM_PULL_GUN_30, + SFX_PLAYER_CALM_PULL_GUN_31, + SFX_PLAYER_CALM_PULL_GUN_32, + SFX_PLAYER_CALM_PULL_GUN_33, + SFX_PLAYER_CALM_PULL_GUN_34, + SFX_PLAYER_CALM_PULL_GUN_35, + SFX_PLAYER_CALM_PULL_GUN_36, + SFX_PLAYER_CALM_PULL_GUN_37, + SFX_PLAYER_CALM_PULL_GUN_38, + SFX_PLAYER_CALM_PULL_GUN_39, + SFX_PLAYER_CALM_SEX_1, + SFX_PLAYER_CALM_SEX_2, + SFX_PLAYER_CALM_SEX_3, + SFX_PLAYER_CALM_SEX_4, + SFX_PLAYER_CALM_SEX_5, + SFX_PLAYER_CALM_SEX_6, + SFX_PLAYER_CALM_SEX_7, + SFX_PLAYER_CALM_SEX_8, + SFX_PLAYER_CALM_SHOOT_1, + SFX_PLAYER_CALM_SHOOT_2, + SFX_PLAYER_CALM_SHOOT_3, + SFX_PLAYER_CALM_SHOOT_4, + SFX_PLAYER_CALM_SHOOT_5, + SFX_PLAYER_CALM_SHOOT_6, + SFX_PLAYER_CALM_SHOOT_7, + SFX_PLAYER_CALM_SHOOT_8, + SFX_PLAYER_CALM_SHOOT_9, + SFX_PLAYER_CALM_SHOOT_10, + SFX_PLAYER_CALM_SHOOT_11, + SFX_PLAYER_CALM_SHOOT_12, + SFX_PLAYER_CALM_SHOOT_13, + SFX_PLAYER_CALM_SHOOT_14, + SFX_PLAYER_CALM_SHOOT_15, + SFX_PLAYER_CALM_SHOOT_16, + SFX_PLAYER_CALM_SHOOT_17, + SFX_PLAYER_CALM_SHOOT_18, + SFX_PLAYER_CALM_SHOOT_19, + SFX_PLAYER_CALM_SHOOT_20, + SFX_PLAYER_CALM_SHOOT_21, + SFX_PLAYER_CALM_SHOOT_22, + SFX_PLAYER_CALM_SHOOT_23, + SFX_PLAYER_CALM_SHOOT_24, + SFX_PLAYER_CALM_SHOOT_25, + SFX_PLAYER_CALM_SHOOT_26, + SFX_PLAYER_CALM_SHOOT_27, + SFX_PLAYER_CALM_SHOOT_28, + SFX_PLAYER_CALM_SHOOT_29, + SFX_PLAYER_CALM_SHOOT_30, + SFX_PLAYER_CALM_SHOOT_31, + SFX_PLAYER_CALM_SHOOT_32, + SFX_PLAYER_CALM_SHOOT_33, + SFX_PLAYER_CALM_SHOOT_34, + SFX_PLAYER_CALM_SHOOT_35, + + SFX_PLAYER_PISSED_OFF_CRASH_1, + SFX_PLAYER_PISSED_OFF_CRASH_2, + SFX_PLAYER_PISSED_OFF_CRASH_3, + SFX_PLAYER_PISSED_OFF_CRASH_4, + SFX_PLAYER_PISSED_OFF_CRASH_5, + SFX_PLAYER_PISSED_OFF_CRASH_6, + SFX_PLAYER_PISSED_OFF_CRASH_7, + SFX_PLAYER_PISSED_OFF_CRASH_8, + SFX_PLAYER_PISSED_OFF_CRASH_9, + SFX_PLAYER_PISSED_OFF_CRASH_10, + SFX_PLAYER_PISSED_OFF_CRASH_11, + SFX_PLAYER_PISSED_OFF_CRASH_12, + SFX_PLAYER_PISSED_OFF_CRASH_13, + SFX_PLAYER_PISSED_OFF_CRASH_14, + SFX_PLAYER_PISSED_OFF_CRASH_15, + SFX_PLAYER_PISSED_OFF_CRASH_16, + SFX_PLAYER_PISSED_OFF_CRASH_17, + SFX_PLAYER_PISSED_OFF_CRASH_18, + SFX_PLAYER_PISSED_OFF_CRASH_19, + SFX_PLAYER_PISSED_OFF_CRASH_20, + SFX_PLAYER_PISSED_OFF_CRASH_21, + SFX_PLAYER_PISSED_OFF_CRASH_22, + SFX_PLAYER_PISSED_OFF_CRASH_23, + SFX_PLAYER_PISSED_OFF_CRASH_24, + SFX_PLAYER_PISSED_OFF_CRASH_25, + SFX_PLAYER_PISSED_OFF_CRASH_26, + SFX_PLAYER_PISSED_OFF_CRASH_27, + SFX_PLAYER_PISSED_OFF_CRASH_28, + SFX_PLAYER_PISSED_OFF_CRASH_29, + SFX_PLAYER_PISSED_OFF_CRASH_30, + SFX_PLAYER_PISSED_OFF_CRASH_31, + SFX_PLAYER_PISSED_OFF_CRASH_32, + SFX_PLAYER_PISSED_OFF_CRASH_33, + SFX_PLAYER_PISSED_OFF_CRASH_34, + SFX_PLAYER_PISSED_OFF_CRASH_35, + SFX_PLAYER_PISSED_OFF_CRASH_36, + SFX_PLAYER_PISSED_OFF_CRASH_37, + SFX_PLAYER_PISSED_OFF_CRASH_38, + SFX_PLAYER_PISSED_OFF_CRASH_39, + SFX_PLAYER_PISSED_OFF_CRASH_40, + SFX_PLAYER_PISSED_OFF_CRASH_41, + SFX_PLAYER_PISSED_OFF_CRASH_42, + SFX_PLAYER_PISSED_OFF_CRASH_43, + SFX_PLAYER_PISSED_OFF_CRASH_44, + SFX_PLAYER_PISSED_OFF_FIGHT_1, + SFX_PLAYER_PISSED_OFF_FIGHT_2, + SFX_PLAYER_PISSED_OFF_FIGHT_3, + SFX_PLAYER_PISSED_OFF_FIGHT_4, + SFX_PLAYER_PISSED_OFF_FIGHT_5, + SFX_PLAYER_PISSED_OFF_FIGHT_6, + SFX_PLAYER_PISSED_OFF_FIGHT_7, + SFX_PLAYER_PISSED_OFF_FIGHT_8, + SFX_PLAYER_PISSED_OFF_FIGHT_9, + SFX_PLAYER_PISSED_OFF_FIGHT_10, + SFX_PLAYER_PISSED_OFF_FIGHT_11, + SFX_PLAYER_PISSED_OFF_FIGHT_12, + SFX_PLAYER_PISSED_OFF_FIGHT_13, + SFX_PLAYER_PISSED_OFF_FIGHT_14, + SFX_PLAYER_PISSED_OFF_FIGHT_15, + SFX_PLAYER_PISSED_OFF_FIGHT_16, + SFX_PLAYER_PISSED_OFF_FIGHT_17, + SFX_PLAYER_PISSED_OFF_FIGHT_18, + SFX_PLAYER_PISSED_OFF_FIGHT_19, + SFX_PLAYER_PISSED_OFF_FIGHT_20, + SFX_PLAYER_PISSED_OFF_FIGHT_21, + SFX_PLAYER_PISSED_OFF_FIGHT_22, + SFX_PLAYER_PISSED_OFF_FIGHT_23, + SFX_PLAYER_PISSED_OFF_FIGHT_24, + SFX_PLAYER_PISSED_OFF_FIGHT_25, + SFX_PLAYER_PISSED_OFF_FIGHT_26, + SFX_PLAYER_PISSED_OFF_FIGHT_27, + SFX_PLAYER_PISSED_OFF_FIGHT_28, + SFX_PLAYER_PISSED_OFF_FIGHT_29, + SFX_PLAYER_PISSED_OFF_FIGHT_30, + SFX_PLAYER_PISSED_OFF_FIGHT_31, + SFX_PLAYER_PISSED_OFF_FIGHT_32, + SFX_PLAYER_PISSED_OFF_FIGHT_33, + SFX_PLAYER_PISSED_OFF_FIGHT_34, + SFX_PLAYER_PISSED_OFF_FIGHT_35, + SFX_PLAYER_PISSED_OFF_FIGHT_36, + SFX_PLAYER_PISSED_OFF_FIGHT_37, + SFX_PLAYER_PISSED_OFF_FIGHT_38, + SFX_PLAYER_PISSED_OFF_FIGHT_39, + SFX_PLAYER_PISSED_OFF_FIGHT_40, + SFX_PLAYER_PISSED_OFF_FIGHT_41, + SFX_PLAYER_PISSED_OFF_FIGHT_42, + SFX_PLAYER_PISSED_OFF_FIGHT_43, + SFX_PLAYER_PISSED_OFF_FIGHT_44, + SFX_PLAYER_PISSED_OFF_FIGHT_45, + SFX_PLAYER_PISSED_OFF_FIGHT_46, + SFX_PLAYER_PISSED_OFF_FIGHT_47, + SFX_PLAYER_PISSED_OFF_FIGHT_48, + SFX_PLAYER_PISSED_OFF_FIGHT_49, + SFX_PLAYER_PISSED_OFF_FIGHT_50, + SFX_PLAYER_PISSED_OFF_FIGHT_51, + SFX_PLAYER_PISSED_OFF_FIGHT_52, + SFX_PLAYER_PISSED_OFF_FIGHT_53, + SFX_PLAYER_PISSED_OFF_FIGHT_54, + SFX_PLAYER_PISSED_OFF_FIGHT_55, + SFX_PLAYER_PISSED_OFF_FIGHT_56, + SFX_PLAYER_PISSED_OFF_FIGHT_57, + SFX_PLAYER_PISSED_OFF_FIGHT_58, + SFX_PLAYER_PISSED_OFF_FIGHT_59, + SFX_PLAYER_PISSED_OFF_FIGHT_60, + SFX_PLAYER_PISSED_OFF_FIGHT_61, + SFX_PLAYER_PISSED_OFF_JACKED_1, + SFX_PLAYER_PISSED_OFF_JACKED_2, + SFX_PLAYER_PISSED_OFF_JACKED_3, + SFX_PLAYER_PISSED_OFF_JACKED_4, + SFX_PLAYER_PISSED_OFF_JACKED_5, + SFX_PLAYER_PISSED_OFF_JACKED_6, + SFX_PLAYER_PISSED_OFF_JACKED_7, + SFX_PLAYER_PISSED_OFF_JACKED_8, + SFX_PLAYER_PISSED_OFF_JACKED_9, + SFX_PLAYER_PISSED_OFF_JACKED_10, + SFX_PLAYER_PISSED_OFF_JACKED_11, + SFX_PLAYER_PISSED_OFF_JACKED_12, + SFX_PLAYER_PISSED_OFF_JACKED_13, + SFX_PLAYER_PISSED_OFF_JACKED_14, + SFX_PLAYER_PISSED_OFF_JACKED_15, + SFX_PLAYER_PISSED_OFF_JACKED_16, + SFX_PLAYER_PISSED_OFF_JACKED_17, + SFX_PLAYER_PISSED_OFF_JACKED_18, + SFX_PLAYER_PISSED_OFF_JACKED_19, + SFX_PLAYER_PISSED_OFF_JACKED_20, + SFX_PLAYER_PISSED_OFF_JACKED_21, + SFX_PLAYER_PISSED_OFF_JACKING_1, + SFX_PLAYER_PISSED_OFF_JACKING_2, + SFX_PLAYER_PISSED_OFF_JACKING_3, + SFX_PLAYER_PISSED_OFF_JACKING_4, + SFX_PLAYER_PISSED_OFF_JACKING_5, + SFX_PLAYER_PISSED_OFF_JACKING_6, + SFX_PLAYER_PISSED_OFF_JACKING_7, + SFX_PLAYER_PISSED_OFF_JACKING_8, + SFX_PLAYER_PISSED_OFF_JACKING_9, + SFX_PLAYER_PISSED_OFF_JACKING_10, + SFX_PLAYER_PISSED_OFF_JACKING_11, + SFX_PLAYER_PISSED_OFF_JACKING_12, + SFX_PLAYER_PISSED_OFF_JACKING_13, + SFX_PLAYER_PISSED_OFF_JACKING_14, + SFX_PLAYER_PISSED_OFF_JACKING_15, + SFX_PLAYER_PISSED_OFF_JACKING_16, + SFX_PLAYER_PISSED_OFF_JACKING_17, + SFX_PLAYER_PISSED_OFF_JACKING_18, + SFX_PLAYER_PISSED_OFF_JACKING_19, + SFX_PLAYER_PISSED_OFF_JACKING_20, + SFX_PLAYER_PISSED_OFF_JACKING_21, + SFX_PLAYER_PISSED_OFF_JACKING_22, + SFX_PLAYER_PISSED_OFF_JACKING_23, + SFX_PLAYER_PISSED_OFF_JACKING_24, + SFX_PLAYER_PISSED_OFF_JACKING_25, + SFX_PLAYER_PISSED_OFF_JACKING_26, + SFX_PLAYER_PISSED_OFF_JACKING_27, + SFX_PLAYER_PISSED_OFF_JACKING_28, + SFX_PLAYER_PISSED_OFF_JACKING_29, + SFX_PLAYER_PISSED_OFF_JACKING_30, + SFX_PLAYER_PISSED_OFF_JACKING_31, + SFX_PLAYER_PISSED_OFF_JACKING_32, + SFX_PLAYER_PISSED_OFF_JACKING_33, + SFX_PLAYER_PISSED_OFF_JACKING_34, + SFX_PLAYER_PISSED_OFF_JACKING_35, + SFX_PLAYER_PISSED_OFF_JACKING_36, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_1, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_2, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_3, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_4, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_5, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_6, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_7, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_8, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_9, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_10, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_11, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_12, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_13, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_14, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_15, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_16, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_17, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_18, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_19, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_20, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_21, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_22, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_23, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_24, + SFX_PLAYER_PISSED_OFF_PICK_UP_CASH_25, + SFX_PLAYER_PISSED_OFF_PICK_UP_HOOKER_1, + SFX_PLAYER_PISSED_OFF_PICK_UP_HOOKER_2, + SFX_PLAYER_PISSED_OFF_PICK_UP_HOOKER_3, + SFX_PLAYER_PISSED_OFF_PICK_UP_HOOKER_4, + SFX_PLAYER_PISSED_OFF_PICK_UP_HOOKER_5, + SFX_PLAYER_PISSED_OFF_PICK_UP_HOOKER_6, + SFX_PLAYER_PISSED_OFF_PICK_UP_HOOKER_7, + SFX_PLAYER_PISSED_OFF_PICK_UP_HOOKER_8, + SFX_PLAYER_PISSED_OFF_PICK_UP_HOOKER_9, + SFX_PLAYER_PISSED_OFF_PICK_UP_HOOKER_10, + SFX_PLAYER_PISSED_OFF_PICK_UP_HOOKER_11, + SFX_PLAYER_PISSED_OFF_PICK_UP_HOOKER_12, + SFX_PLAYER_PISSED_OFF_PICK_UP_HOOKER_13, + SFX_PLAYER_PISSED_OFF_PICK_UP_HOOKER_14, + SFX_PLAYER_PISSED_OFF_PICK_UP_HOOKER_15, + SFX_PLAYER_PISSED_OFF_PICK_UP_HOOKER_16, + SFX_PLAYER_PISSED_OFF_PICK_UP_HOOKER_17, + SFX_PLAYER_PISSED_OFF_PULL_GUN_1, + SFX_PLAYER_PISSED_OFF_PULL_GUN_2, + SFX_PLAYER_PISSED_OFF_PULL_GUN_3, + SFX_PLAYER_PISSED_OFF_PULL_GUN_4, + SFX_PLAYER_PISSED_OFF_PULL_GUN_5, + SFX_PLAYER_PISSED_OFF_PULL_GUN_6, + SFX_PLAYER_PISSED_OFF_PULL_GUN_7, + SFX_PLAYER_PISSED_OFF_PULL_GUN_8, + SFX_PLAYER_PISSED_OFF_PULL_GUN_9, + SFX_PLAYER_PISSED_OFF_PULL_GUN_10, + SFX_PLAYER_PISSED_OFF_PULL_GUN_11, + SFX_PLAYER_PISSED_OFF_PULL_GUN_12, + SFX_PLAYER_PISSED_OFF_PULL_GUN_13, + SFX_PLAYER_PISSED_OFF_PULL_GUN_14, + SFX_PLAYER_PISSED_OFF_PULL_GUN_15, + SFX_PLAYER_PISSED_OFF_PULL_GUN_16, + SFX_PLAYER_PISSED_OFF_PULL_GUN_17, + SFX_PLAYER_PISSED_OFF_PULL_GUN_18, + SFX_PLAYER_PISSED_OFF_PULL_GUN_19, + SFX_PLAYER_PISSED_OFF_PULL_GUN_20, + SFX_PLAYER_PISSED_OFF_PULL_GUN_21, + SFX_PLAYER_PISSED_OFF_PULL_GUN_22, + SFX_PLAYER_PISSED_OFF_PULL_GUN_23, + SFX_PLAYER_PISSED_OFF_PULL_GUN_24, + SFX_PLAYER_PISSED_OFF_PULL_GUN_25, + SFX_PLAYER_PISSED_OFF_SHOOT_1, + SFX_PLAYER_PISSED_OFF_SHOOT_2, + SFX_PLAYER_PISSED_OFF_SHOOT_3, + SFX_PLAYER_PISSED_OFF_SHOOT_4, + SFX_PLAYER_PISSED_OFF_SHOOT_5, + SFX_PLAYER_PISSED_OFF_SHOOT_6, + SFX_PLAYER_PISSED_OFF_SHOOT_7, + SFX_PLAYER_PISSED_OFF_SHOOT_8, + SFX_PLAYER_PISSED_OFF_SHOOT_9, + SFX_PLAYER_PISSED_OFF_SHOOT_10, + SFX_PLAYER_PISSED_OFF_SHOOT_11, + SFX_PLAYER_PISSED_OFF_SHOOT_12, + SFX_PLAYER_PISSED_OFF_SHOOT_13, + SFX_PLAYER_PISSED_OFF_SHOOT_14, + SFX_PLAYER_PISSED_OFF_SHOOT_15, + SFX_PLAYER_PISSED_OFF_SHOOT_16, + SFX_PLAYER_PISSED_OFF_SHOOT_17, + SFX_PLAYER_PISSED_OFF_SHOOT_18, + SFX_PLAYER_PISSED_OFF_SHOOT_19, + SFX_PLAYER_PISSED_OFF_SHOOT_20, + SFX_PLAYER_PISSED_OFF_SHOOT_21, + SFX_PLAYER_PISSED_OFF_SHOOT_22, + SFX_PLAYER_PISSED_OFF_SHOOT_23, + SFX_PLAYER_PISSED_OFF_SHOOT_24, + SFX_PLAYER_PISSED_OFF_SHOOT_25, + SFX_PLAYER_PISSED_OFF_SHOOT_26, + SFX_PLAYER_PISSED_OFF_SHOOT_27, + SFX_PLAYER_PISSED_OFF_SHOOT_28, + SFX_PLAYER_PISSED_OFF_SHOOT_29, + + SFX_PLAYER_WISECRACKING_BUSTED_1, + SFX_PLAYER_WISECRACKING_BUSTED_2, + SFX_PLAYER_WISECRACKING_BUSTED_3, + SFX_PLAYER_WISECRACKING_BUSTED_4, + SFX_PLAYER_WISECRACKING_BUSTED_5, + SFX_PLAYER_WISECRACKING_BUSTED_6, + SFX_PLAYER_WISECRACKING_BUSTED_7, + SFX_PLAYER_WISECRACKING_BUSTED_8, + SFX_PLAYER_WISECRACKING_BUSTED_9, + SFX_PLAYER_WISECRACKING_BUSTED_10, + SFX_PLAYER_WISECRACKING_BUSTED_11, + SFX_PLAYER_WISECRACKING_BUSTED_12, + SFX_PLAYER_WISECRACKING_BUSTED_13, + SFX_PLAYER_WISECRACKING_BUSTED_14, + SFX_PLAYER_WISECRACKING_BUSTED_15, + SFX_PLAYER_WISECRACKING_BUSTED_16, + SFX_PLAYER_WISECRACKING_BUSTED_17, + SFX_PLAYER_WISECRACKING_BUSTED_18, + SFX_PLAYER_WISECRACKING_BUSTED_19, + SFX_PLAYER_WISECRACKING_BUSTED_20, + SFX_PLAYER_WISECRACKING_CHASED_1, + SFX_PLAYER_WISECRACKING_CHASED_2, + SFX_PLAYER_WISECRACKING_CHASED_3, + SFX_PLAYER_WISECRACKING_CHASED_4, + SFX_PLAYER_WISECRACKING_CHASED_5, + SFX_PLAYER_WISECRACKING_CHASED_6, + SFX_PLAYER_WISECRACKING_CHASED_7, + SFX_PLAYER_WISECRACKING_CRASH_1, + SFX_PLAYER_WISECRACKING_CRASH_2, + SFX_PLAYER_WISECRACKING_CRASH_3, + SFX_PLAYER_WISECRACKING_CRASH_4, + SFX_PLAYER_WISECRACKING_CRASH_5, + SFX_PLAYER_WISECRACKING_CRASH_6, + SFX_PLAYER_WISECRACKING_CRASH_7, + SFX_PLAYER_WISECRACKING_CRASH_8, + SFX_PLAYER_WISECRACKING_CRASH_9, + SFX_PLAYER_WISECRACKING_CRASH_10, + SFX_PLAYER_WISECRACKING_CRASH_11, + SFX_PLAYER_WISECRACKING_CRASH_12, + SFX_PLAYER_WISECRACKING_CRASH_13, + SFX_PLAYER_WISECRACKING_CRASH_14, + SFX_PLAYER_WISECRACKING_CRASH_15, + SFX_PLAYER_WISECRACKING_CRASH_16, + SFX_PLAYER_WISECRACKING_CRASH_17, + SFX_PLAYER_WISECRACKING_CRASH_18, + SFX_PLAYER_WISECRACKING_CRASH_19, + SFX_PLAYER_WISECRACKING_FIGHT_1, + SFX_PLAYER_WISECRACKING_FIGHT_2, + SFX_PLAYER_WISECRACKING_FIGHT_3, + SFX_PLAYER_WISECRACKING_FIGHT_4, + SFX_PLAYER_WISECRACKING_FIGHT_5, + SFX_PLAYER_WISECRACKING_FIGHT_6, + SFX_PLAYER_WISECRACKING_FIGHT_7, + SFX_PLAYER_WISECRACKING_FIGHT_8, + SFX_PLAYER_WISECRACKING_FIGHT_9, + SFX_PLAYER_WISECRACKING_FIGHT_10, + SFX_PLAYER_WISECRACKING_FIGHT_11, + SFX_PLAYER_WISECRACKING_FIGHT_12, + SFX_PLAYER_WISECRACKING_FIGHT_13, + SFX_PLAYER_WISECRACKING_FIGHT_14, + SFX_PLAYER_WISECRACKING_FIGHT_15, + SFX_PLAYER_WISECRACKING_FIGHT_16, + SFX_PLAYER_WISECRACKING_FIGHT_17, + SFX_PLAYER_WISECRACKING_FIGHT_18, + SFX_PLAYER_WISECRACKING_FIGHT_19, + SFX_PLAYER_WISECRACKING_FIGHT_20, + SFX_PLAYER_WISECRACKING_FIGHT_21, + SFX_PLAYER_WISECRACKING_FIGHT_22, + SFX_PLAYER_WISECRACKING_FIGHT_23, + SFX_PLAYER_WISECRACKING_FIGHT_24, + SFX_PLAYER_WISECRACKING_FIGHT_25, + SFX_PLAYER_WISECRACKING_FIGHT_26, + SFX_PLAYER_WISECRACKING_FIGHT_27, + SFX_PLAYER_WISECRACKING_JACKED_1, + SFX_PLAYER_WISECRACKING_JACKED_2, + SFX_PLAYER_WISECRACKING_JACKED_3, + SFX_PLAYER_WISECRACKING_JACKED_4, + SFX_PLAYER_WISECRACKING_JACKED_5, + SFX_PLAYER_WISECRACKING_JACKED_6, + SFX_PLAYER_WISECRACKING_JACKED_7, + SFX_PLAYER_WISECRACKING_JACKED_8, + SFX_PLAYER_WISECRACKING_JACKED_9, + SFX_PLAYER_WISECRACKING_JACKED_10, + SFX_PLAYER_WISECRACKING_JACKED_11, + SFX_PLAYER_WISECRACKING_JACKED_12, + SFX_PLAYER_WISECRACKING_JACKED_13, + SFX_PLAYER_WISECRACKING_JACKED_14, + SFX_PLAYER_WISECRACKING_JACKED_15, + SFX_PLAYER_WISECRACKING_JACKED_16, + SFX_PLAYER_WISECRACKING_JACKED_17, + SFX_PLAYER_WISECRACKING_JACKED_18, + SFX_PLAYER_WISECRACKING_JACKING_1, + SFX_PLAYER_WISECRACKING_JACKING_2, + SFX_PLAYER_WISECRACKING_JACKING_3, + SFX_PLAYER_WISECRACKING_JACKING_4, + SFX_PLAYER_WISECRACKING_JACKING_5, + SFX_PLAYER_WISECRACKING_JACKING_6, + SFX_PLAYER_WISECRACKING_JACKING_7, + SFX_PLAYER_WISECRACKING_JACKING_8, + SFX_PLAYER_WISECRACKING_JACKING_9, + SFX_PLAYER_WISECRACKING_JACKING_10, + SFX_PLAYER_WISECRACKING_JACKING_11, + SFX_PLAYER_WISECRACKING_JACKING_12, + SFX_PLAYER_WISECRACKING_JACKING_13, + SFX_PLAYER_WISECRACKING_JACKING_14, + SFX_PLAYER_WISECRACKING_JACKING_15, + SFX_PLAYER_WISECRACKING_JACKING_16, + SFX_PLAYER_WISECRACKING_JACKING_17, + SFX_PLAYER_WISECRACKING_JACKING_18, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_1, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_2, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_3, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_4, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_5, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_6, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_7, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_8, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_9, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_10, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_11, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_12, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_13, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_14, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_15, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_16, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_17, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_18, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_19, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_20, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_21, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_22, + SFX_PLAYER_WISECRACKING_PICK_UP_CASH_23, + SFX_PLAYER_WISECRACKING_PICK_UP_HOOKER_1, + SFX_PLAYER_WISECRACKING_PICK_UP_HOOKER_2, + SFX_PLAYER_WISECRACKING_PICK_UP_HOOKER_3, + SFX_PLAYER_WISECRACKING_PICK_UP_HOOKER_4, + SFX_PLAYER_WISECRACKING_PICK_UP_HOOKER_5, + SFX_PLAYER_WISECRACKING_PICK_UP_HOOKER_6, + SFX_PLAYER_WISECRACKING_PICK_UP_HOOKER_7, + SFX_PLAYER_WISECRACKING_PICK_UP_HOOKER_8, + SFX_PLAYER_WISECRACKING_PICK_UP_HOOKER_9, + SFX_PLAYER_WISECRACKING_PICK_UP_HOOKER_10, + SFX_PLAYER_WISECRACKING_PICK_UP_HOOKER_11, + SFX_PLAYER_WISECRACKING_PULL_GUN_1, + SFX_PLAYER_WISECRACKING_PULL_GUN_2, + SFX_PLAYER_WISECRACKING_PULL_GUN_3, + SFX_PLAYER_WISECRACKING_PULL_GUN_4, + SFX_PLAYER_WISECRACKING_PULL_GUN_5, + SFX_PLAYER_WISECRACKING_PULL_GUN_6, + SFX_PLAYER_WISECRACKING_PULL_GUN_7, + SFX_PLAYER_WISECRACKING_PULL_GUN_8, + SFX_PLAYER_WISECRACKING_PULL_GUN_9, + SFX_PLAYER_WISECRACKING_PULL_GUN_10, + SFX_PLAYER_WISECRACKING_PULL_GUN_11, + SFX_PLAYER_WISECRACKING_PULL_GUN_12, + SFX_PLAYER_WISECRACKING_PULL_GUN_13, + SFX_PLAYER_WISECRACKING_PULL_GUN_14, + SFX_PLAYER_WISECRACKING_PULL_GUN_15, + SFX_PLAYER_WISECRACKING_PULL_GUN_16, + SFX_PLAYER_WISECRACKING_PULL_GUN_17, + SFX_PLAYER_WISECRACKING_PULL_GUN_18, + SFX_PLAYER_WISECRACKING_PULL_GUN_19, + SFX_PLAYER_WISECRACKING_SEX_1, + SFX_PLAYER_WISECRACKING_SEX_2, + SFX_PLAYER_WISECRACKING_SEX_3, + SFX_PLAYER_WISECRACKING_SEX_4, + SFX_PLAYER_WISECRACKING_SEX_5, + SFX_PLAYER_WISECRACKING_SEX_6, + SFX_PLAYER_WISECRACKING_SEX_7, + SFX_PLAYER_WISECRACKING_SEX_8, + SFX_PLAYER_WISECRACKING_SEX_9, + SFX_PLAYER_WISECRACKING_SEX_10, + SFX_PLAYER_WISECRACKING_SHOOT_1, + SFX_PLAYER_WISECRACKING_SHOOT_2, + SFX_PLAYER_WISECRACKING_SHOOT_3, + SFX_PLAYER_WISECRACKING_SHOOT_4, + SFX_PLAYER_WISECRACKING_SHOOT_5, + SFX_PLAYER_WISECRACKING_SHOOT_6, + SFX_PLAYER_WISECRACKING_SHOOT_7, + SFX_PLAYER_WISECRACKING_SHOOT_8, + SFX_PLAYER_WISECRACKING_SHOOT_9, + SFX_PLAYER_DEATH, + SFX_PLAYER_AFTERSEX_1, + SFX_PLAYER_AFTERSEX_2, + SFX_PLAYER_AFTERSEX_3, + SFX_PLAYER_AFTERSEX_4, + SFX_PLAYER_AFTERSEX_5, + SFX_PLAYER_AFTERSEX_6, + SFX_PLAYER_AFTERSEX_7, + SFX_PLAYER_AFTERSEX_8, + SFX_PLAYER_AFTERSEX_9, + SFX_PLAYER_AFTERSEX_10, + SFX_PLAYER_AFTERSEX_11, + SFX_PLAYER_AFTERSEX_12, + SFX_PLAYER_AFTERSEX_13, + SFX_PLAYER_AFTERSEX_14, + SFX_PLAYER_AFTERSEX_15, + SFX_PLAYER_AFTERSEX_16, + SFX_PLAYER_AFTERSEX_17, + SFX_PLAYER_AFTERSEX_18, + SFX_PLAYER_HIT_BULLET_1, + SFX_PLAYER_HIT_BULLET_2, + SFX_PLAYER_HIT_BULLET_3, + SFX_PLAYER_HIT_BULLET_4, + SFX_PLAYER_HIT_BULLET_5, + SFX_PLAYER_HIT_BULLET_6, + SFX_PLAYER_HIT_BULLET_7, + SFX_PLAYER_HIT_BULLET_8, + SFX_PLAYER_HIT_BULLET_9, + SFX_PLAYER_HIT_BULLET_10, + SFX_PLAYER_HIT_BULLET_11, + SFX_PLAYER_HIT_BULLET_12, + SFX_PLAYER_HIT_BULLET_13, + SFX_PLAYER_HIT_BULLET_14, + SFX_PLAYER_HIT_BULLET_15, + SFX_PLAYER_HIT_BULLET_16, + SFX_PLAYER_HIT_BULLET_17, + SFX_PLAYER_HIT_BULLET_18, + SFX_PLAYER_HIT_BULLET_19, + SFX_PLAYER_HIT_BULLET_20, + SFX_PLAYER_HIT_BULLET_21, + SFX_PLAYER_HIT_BULLET_22, + SFX_PLAYER_HIT_BULLET_23, + SFX_PLAYER_HIT_BULLET_24, + SFX_PLAYER_HIT_BULLET_25, + SFX_PLAYER_HIT_BULLET_26, + SFX_PLAYER_HIT_BULLET_27, + SFX_PLAYER_HIT_BULLET_28, + SFX_PLAYER_HIT_BULLET_29, + SFX_PLAYER_HIT_BULLET_30, + SFX_PLAYER_HIT_BULLET_31, + SFX_PLAYER_HIT_BULLET_32, + SFX_PLAYER_HIT_BULLET_33, + SFX_PLAYER_HIT_GROUND_1, + SFX_PLAYER_HIT_GROUND_2, + SFX_PLAYER_HIT_GROUND_3, + SFX_PLAYER_HIT_GROUND_4, + SFX_PLAYER_HIT_GROUND_5, + SFX_PLAYER_HIT_GROUND_6, + SFX_PLAYER_HIT_GROUND_7, + SFX_PLAYER_HIT_GROUND_8, + SFX_PLAYER_HIT_GROUND_9, + SFX_PLAYER_HIT_GROUND_10, + SFX_PLAYER_HIT_GROUND_11, + SFX_PLAYER_HIT_GROUND_12, + SFX_PLAYER_HIT_GROUND_13, + SFX_PLAYER_HIT_GROUND_14, + SFX_PLAYER_HIT_GROUND_15, + SFX_PLAYER_HIT_GROUND_16, + SFX_PLAYER_HIT_GROUND_17, + SFX_PLAYER_HIT_GROUND_18, + SFX_PLAYER_HIT_GROUND_19, + SFX_PLAYER_HIT_GROUND_20, + SFX_PLAYER_HIT_GROUND_21, + SFX_PLAYER_HIT_GROUND_22, + SFX_PLAYER_HIT_GROUND_23, + SFX_PLAYER_HIT_GROUND_24, + SFX_PLAYER_HIT_GROUND_25, + SFX_PLAYER_HIT_GROUND_26, + SFX_PLAYER_HIT_GROUND_27, + SFX_PLAYER_HIT_GROUND_28, + SFX_PLAYER_HIT_GROUND_29, + SFX_PLAYER_HIT_GROUND_30, + SFX_PLAYER_HIT_GROUND_31, + SFX_PLAYER_HIT_GROUND_32, + SFX_PLAYER_HIT_GROUND_33, + SFX_PLAYER_HIT_GROUND_34, + SFX_PLAYER_HIT_GROUND_35, + SFX_PLAYER_HIT_FIST_1, + SFX_PLAYER_HIT_FIST_2, + SFX_PLAYER_HIT_FIST_3, + SFX_PLAYER_HIT_FIST_4, + SFX_PLAYER_HIT_FIST_5, + SFX_PLAYER_HIT_FIST_6, + SFX_PLAYER_HIT_FIST_7, + SFX_PLAYER_HIT_FIST_8, + SFX_PLAYER_HIT_FIST_9, + SFX_PLAYER_HIT_FIST_10, + SFX_PLAYER_HIT_FIST_11, + SFX_PLAYER_HIT_FIST_12, + SFX_PLAYER_HIT_FIST_13, + SFX_PLAYER_HIT_FIST_14, + SFX_PLAYER_HIT_FIST_15, + SFX_PLAYER_HIT_FIST_16, + SFX_PLAYER_HIT_FIST_17, + SFX_PLAYER_HIT_FIST_18, + SFX_PLAYER_HIT_FIST_19, + SFX_PLAYER_HIT_FIST_20, + SFX_PLAYER_HIT_FIST_21, + SFX_PLAYER_HIT_FIST_22, + SFX_PLAYER_HIT_FIST_23, + SFX_PLAYER_HIT_FIST_24, + SFX_PLAYER_HIT_FIST_25, + SFX_PLAYER_HIT_FIST_26, + SFX_PLAYER_HIT_FIST_27, + SFX_PLAYER_HIT_FIST_28, + SFX_PLAYER_HIT_FIST_29, + SFX_PLAYER_HIT_FIST_30, + SFX_PLAYER_HIT_FIST_31, + SFX_PLAYER_HIT_FIST_32, + SFX_PLAYER_HIT_FIST_33, + SFX_PLAYER_HIT_FIST_34, + SFX_PLAYER_HIT_FIST_35, + SFX_PLAYER_HIT_FIST_36, + SFX_PLAYER_HIT_FIST_37, + SFX_PLAYER_HIT_FIST_38, + SFX_PLAYER_HIT_FIST_39, + SFX_PLAYER_HIT_FIST_40, + SFX_PLAYER_HIT_FIST_41, + SFX_PLAYER_HIT_FIST_42, + SFX_PLAYER_ON_FIRE_1, + SFX_PLAYER_ON_FIRE_2, + SFX_PLAYER_ON_FIRE_3, + SFX_PLAYER_ON_FIRE_4, + SFX_PLAYER_ON_FIRE_5, + SFX_PLAYER_ON_FIRE_6, + SFX_PLAYER_ON_FIRE_7, + SFX_PLAYER_ON_FIRE_8, + SFX_PLAYER_ON_FIRE_9, + SFX_PLAYER_ON_FIRE_10, + SFX_PLAYER_ON_FIRE_11, + SFX_PLAYER_ON_FIRE_12, + SFX_PLAYER_ON_FIRE_13, + SFX_PLAYER_ON_FIRE_14, + SFX_PLAYER_ON_FIRE_15, + LAST_PLAYER_COMMENT(SFX_PLAYER_ON_FIRE_16), + TOTAL_AUDIO_SAMPLES, + NO_SAMPLE, + + // shorthands + SAMPLEBANK_START = SFX_CAR_HORN_JEEP, + SAMPLEBANK_END = SFX_FOOTSTEP_SAND_4, + SAMPLEBANK_MAX = SFX_FOOTSTEP_SAND_4 + 1, + SAMPLEBANK_PED_START = SFX_FOOTSTEP_SAND_4 + 1, + SAMPLEBANK_PED_END = SFX_PLAYER_ON_FIRE_16, + SAMPLEBANK_PED_MAX = SAMPLEBANK_PED_END + 1, +}; diff --git a/src/miami/audio/AudioScriptObject.cpp b/src/miami/audio/AudioScriptObject.cpp new file mode 100644 index 00000000..ac4bebcb --- /dev/null +++ b/src/miami/audio/AudioScriptObject.cpp @@ -0,0 +1,103 @@ +#include "common.h" + +#include "AudioScriptObject.h" +#include "Pools.h" +#include "DMAudio.h" +#include "SaveBuf.h" + +cAudioScriptObject::cAudioScriptObject() +{ + Reset(); +}; + +cAudioScriptObject::~cAudioScriptObject() +{ + Reset(); +}; + +void +cAudioScriptObject::Reset() +{ + AudioId = SCRIPT_SOUND_INVALID; + Posn = CVector(0.0f, 0.0f, 0.0f); + AudioEntity = AEHANDLE_NONE; +} + +void * +cAudioScriptObject::operator new(size_t sz) throw() +{ + return CPools::GetAudioScriptObjectPool()->New(); +} + +void * +cAudioScriptObject::operator new(size_t sz, int handle) throw() +{ + return CPools::GetAudioScriptObjectPool()->New(handle); +} + +void +cAudioScriptObject::operator delete(void *p, size_t sz) throw() +{ + CPools::GetAudioScriptObjectPool()->Delete((cAudioScriptObject *)p); +} + +void +cAudioScriptObject::operator delete(void *p, int handle) throw() +{ + CPools::GetAudioScriptObjectPool()->Delete((cAudioScriptObject *)p); +} + +void +cAudioScriptObject::LoadAllAudioScriptObjects(uint8 *buf, uint32 size) +{ + INITSAVEBUF + + CheckSaveHeader(buf, 'A', 'U', 'D', '\0', size - SAVE_HEADER_SIZE); + + int32 pool_size; + ReadSaveBuf(&pool_size, buf); + for (int32 i = 0; i < pool_size; i++) { + int32 handle; + ReadSaveBuf(&handle, buf); + cAudioScriptObject *p = new(handle) cAudioScriptObject; + assert(p != nil); + ReadSaveBuf(p, buf); + p->AudioEntity = DMAudio.CreateLoopingScriptObject(p); + } + + VALIDATESAVEBUF(size); +} + +void +cAudioScriptObject::SaveAllAudioScriptObjects(uint8 *buf, uint32 *size) +{ + INITSAVEBUF + + int32 pool_size = CPools::GetAudioScriptObjectPool()->GetNoOfUsedSpaces(); + *size = SAVE_HEADER_SIZE + sizeof(int32) + pool_size * (sizeof(cAudioScriptObject) + sizeof(int32)); + WriteSaveHeader(buf, 'A', 'U', 'D', '\0', *size - SAVE_HEADER_SIZE); + WriteSaveBuf(buf, pool_size); + + int32 i = CPools::GetAudioScriptObjectPool()->GetSize(); + while (i--) { + cAudioScriptObject *p = CPools::GetAudioScriptObjectPool()->GetSlot(i); + if (p != nil) { + WriteSaveBuf(buf, CPools::GetAudioScriptObjectPool()->GetIndex(p)); + WriteSaveBuf(buf, *p); + } + } + + VALIDATESAVEBUF(*size); +} + +void +PlayOneShotScriptObject(uint8 id, CVector const &pos) +{ + if (!DMAudio.IsAudioInitialised()) return; + + cAudioScriptObject *audioScriptObject = new cAudioScriptObject(); + audioScriptObject->Posn = pos; + audioScriptObject->AudioId = id; + audioScriptObject->AudioEntity = AEHANDLE_NONE; + DMAudio.CreateOneShotScriptObject(audioScriptObject); +} diff --git a/src/miami/audio/AudioScriptObject.h b/src/miami/audio/AudioScriptObject.h new file mode 100644 index 00000000..b9a7e61b --- /dev/null +++ b/src/miami/audio/AudioScriptObject.h @@ -0,0 +1,26 @@ +#pragma once + +class cAudioScriptObject +{ +public: + int16 AudioId; + CVector Posn; + int32 AudioEntity; + + cAudioScriptObject(); + ~cAudioScriptObject(); + + void Reset(); /// ok + + static void* operator new(size_t) throw(); + static void* operator new(size_t, int) throw(); + static void operator delete(void*, size_t) throw(); + static void operator delete(void*, int) throw(); + + static void LoadAllAudioScriptObjects(uint8 *buf, uint32 size); + static void SaveAllAudioScriptObjects(uint8 *buf, uint32 *size); +}; + +VALIDATE_SIZE(cAudioScriptObject, 20); + +extern void PlayOneShotScriptObject(uint8 id, CVector const &pos); \ No newline at end of file diff --git a/src/miami/audio/DMAudio.cpp b/src/miami/audio/DMAudio.cpp new file mode 100644 index 00000000..68e879e6 --- /dev/null +++ b/src/miami/audio/DMAudio.cpp @@ -0,0 +1,408 @@ +#include "common.h" + +#include "DMAudio.h" +#include "MusicManager.h" +#include "AudioManager.h" +#include "AudioScriptObject.h" +#include "sampman.h" + +cDMAudio DMAudio; + +void +cDMAudio::Initialise(void) +{ + AudioManager.Initialise(); +} + +void +cDMAudio::Terminate(void) +{ + AudioManager.Terminate(); +} + +void +cDMAudio::Service(void) +{ + AudioManager.Service(); +} + +int32 +cDMAudio::CreateEntity(eAudioType type, void *UID) +{ + return AudioManager.CreateEntity(type, (CPhysical *)UID); +} + +void +cDMAudio::DestroyEntity(int32 audioEntity) +{ + AudioManager.DestroyEntity(audioEntity); +} + +bool8 +cDMAudio::GetEntityStatus(int32 audioEntity) +{ + return AudioManager.GetEntityStatus(audioEntity); +} + +void +cDMAudio::SetEntityStatus(int32 audioEntity, bool8 status) +{ + AudioManager.SetEntityStatus(audioEntity, status); +} + +void +cDMAudio::PlayOneShot(int32 audioEntity, uint16 oneShot, float volume) +{ + AudioManager.PlayOneShot(audioEntity, oneShot, volume); +} + +void +cDMAudio::DestroyAllGameCreatedEntities(void) +{ + AudioManager.DestroyAllGameCreatedEntities(); +} + +void +cDMAudio::SetOutputMode(bool8 surround) +{ + AudioManager.SetOutputMode(surround); +} + +void +cDMAudio::SetMP3BoostVolume(uint8 volume) +{ + uint8 vol = volume; + if (vol > MAX_VOLUME) vol = MAX_VOLUME; + + AudioManager.SetMP3BoostVolume(vol); +} + +void +cDMAudio::SetEffectsMasterVolume(uint8 volume) +{ + uint8 vol = volume; + if ( vol > MAX_VOLUME ) vol = MAX_VOLUME; + + AudioManager.SetEffectsMasterVolume(vol); +} + +void +cDMAudio::SetMusicMasterVolume(uint8 volume) +{ + uint8 vol = volume; + if ( vol > MAX_VOLUME ) vol = MAX_VOLUME; + + AudioManager.SetMusicMasterVolume(vol); +} + +void +cDMAudio::SetEffectsFadeVol(uint8 volume) +{ + uint8 vol = volume; + if ( vol > MAX_VOLUME ) vol = MAX_VOLUME; + + AudioManager.SetEffectsFadeVol(vol); +} + +void +cDMAudio::SetMusicFadeVol(uint8 volume) +{ + uint8 vol = volume; + if ( vol > MAX_VOLUME ) vol = MAX_VOLUME; + + AudioManager.SetMusicFadeVol(vol); +} + +uint8 +cDMAudio::GetNum3DProvidersAvailable(void) +{ + return AudioManager.GetNum3DProvidersAvailable(); +} + +char * +cDMAudio::Get3DProviderName(uint8 id) +{ + return AudioManager.Get3DProviderName(id); +} + +int8 cDMAudio::AutoDetect3DProviders(void) +{ + return AudioManager.AutoDetect3DProviders(); +} + +int8 +cDMAudio::GetCurrent3DProviderIndex(void) +{ + return AudioManager.GetCurrent3DProviderIndex(); +} + +int8 +cDMAudio::SetCurrent3DProvider(uint8 which) +{ + return AudioManager.SetCurrent3DProvider(which); +} + +void +cDMAudio::SetSpeakerConfig(int32 config) +{ + AudioManager.SetSpeakerConfig(config); +} + +bool8 +cDMAudio::IsMP3RadioChannelAvailable(void) +{ + return AudioManager.IsMP3RadioChannelAvailable(); +} + +void +cDMAudio::ReleaseDigitalHandle(void) +{ + AudioManager.ReleaseDigitalHandle(); +} + +void +cDMAudio::ReacquireDigitalHandle(void) +{ + AudioManager.ReacquireDigitalHandle(); +} + +void +cDMAudio::SetDynamicAcousticModelingStatus(bool8 status) +{ +#ifdef AUDIO_REFLECTIONS + AudioManager.SetDynamicAcousticModelingStatus(status); +#endif +} + +bool8 +cDMAudio::CheckForAnAudioFileOnCD(void) +{ + return AudioManager.CheckForAnAudioFileOnCD(); +} + +char +cDMAudio::GetCDAudioDriveLetter(void) +{ + return AudioManager.GetCDAudioDriveLetter(); +} + +bool8 +cDMAudio::IsAudioInitialised(void) +{ + return AudioManager.IsAudioInitialised(); +} + +void +cDMAudio::ResetPoliceRadio() +{ + AudioManager.ResetPoliceRadio(); +} + +void +cDMAudio::ReportCrime(eCrimeType crime, const CVector &pos) +{ + AudioManager.ReportCrime(crime, pos); +} + +int32 +cDMAudio::CreateLoopingScriptObject(cAudioScriptObject *scriptObject) +{ + int32 audioEntity = AudioManager.CreateEntity(AUDIOTYPE_SCRIPTOBJECT, scriptObject); + + if ( AEHANDLE_IS_OK(audioEntity) ) + AudioManager.SetEntityStatus(audioEntity, TRUE); + + return audioEntity; +} + +void +cDMAudio::DestroyLoopingScriptObject(int32 audioEntity) +{ + AudioManager.DestroyEntity(audioEntity); +} + +void +cDMAudio::CreateOneShotScriptObject(cAudioScriptObject *scriptObject) +{ + int32 audioEntity = AudioManager.CreateEntity(AUDIOTYPE_SCRIPTOBJECT, scriptObject); + + if ( AEHANDLE_IS_OK(audioEntity) ) + { + AudioManager.SetEntityStatus(audioEntity, TRUE); + AudioManager.PlayOneShot(audioEntity, scriptObject->AudioId, 0.0f); + } +} + +void +cDMAudio::PlaySuspectLastSeen(float x, float y, float z) +{ + AudioManager.PlaySuspectLastSeen(x, y, z); +} + +void +cDMAudio::ReportCollision(CEntity *entityA, CEntity *entityB, uint8 surfaceTypeA, uint8 surfaceTypeB, float collisionPower, float velocity) +{ + AudioManager.ReportCollision(entityA, entityB, surfaceTypeA, surfaceTypeB, collisionPower, velocity); +} + +void +cDMAudio::PlayFrontEndSound(uint16 frontend, uint32 volume) +{ + AudioManager.PlayOneShot(AudioManager.m_nFrontEndEntity, frontend, (float)volume); +} + +void +cDMAudio::PlayRadioAnnouncement(uint32 announcement) +{ + MusicManager.PlayAnnouncement(announcement); +} + +void +cDMAudio::PlayFrontEndTrack(uint32 track, bool8 frontendFlag) +{ + MusicManager.PlayFrontEndTrack(track, frontendFlag); +} + +void +cDMAudio::StopFrontEndTrack(void) +{ + MusicManager.StopFrontEndTrack(); +} + +void +cDMAudio::ResetTimers(uint32 time) +{ + AudioManager.ResetTimers(time); +} + +void +cDMAudio::ChangeMusicMode(uint8 mode) +{ + MusicManager.ChangeMusicMode(mode); +} + +void +cDMAudio::PreloadCutSceneMusic(uint32 track) +{ + MusicManager.PreloadCutSceneMusic(track); +} + +void +cDMAudio::PlayPreloadedCutSceneMusic(void) +{ + MusicManager.PlayPreloadedCutSceneMusic(); +} + +void +cDMAudio::StopCutSceneMusic(void) +{ + MusicManager.StopCutSceneMusic(); +} + +void +cDMAudio::PreloadMissionAudio(uint8 slot, Const char *missionAudio) +{ + AudioManager.PreloadMissionAudio(slot, missionAudio); +} + +uint8 +cDMAudio::GetMissionAudioLoadingStatus(uint8 slot) +{ + return AudioManager.GetMissionAudioLoadingStatus(slot); +} + +void +cDMAudio::SetMissionAudioLocation(uint8 slot, float x, float y, float z) +{ + AudioManager.SetMissionAudioLocation(slot, x, y, z); +} + +void +cDMAudio::PlayLoadedMissionAudio(uint8 slot) +{ + AudioManager.PlayLoadedMissionAudio(slot); +} + +bool8 +cDMAudio::IsMissionAudioSamplePlaying(uint8 slot) +{ + return AudioManager.IsMissionAudioSamplePlaying(slot); +} + +bool8 +cDMAudio::IsMissionAudioSampleFinished(uint8 slot) +{ + return AudioManager.IsMissionAudioSampleFinished(slot); +} + +void +cDMAudio::ClearMissionAudio(uint8 slot) +{ + AudioManager.ClearMissionAudio(slot); +} + +const char * +cDMAudio::GetMissionAudioLoadedLabel(uint8 slot) +{ + return AudioManager.GetMissionAudioLoadedLabel(slot); +} + +uint8 +cDMAudio::GetRadioInCar(void) +{ + return MusicManager.GetRadioInCar(); +} + +void +cDMAudio::SetRadioInCar(uint32 radio) +{ + MusicManager.SetRadioInCar(radio); +} + +void +cDMAudio::SetRadioChannel(uint32 radio, int32 pos) +{ + MusicManager.SetRadioChannelByScript(radio, pos); +} + +void +cDMAudio::SetStartingTrackPositions(bool8 isStartGame) +{ + MusicManager.SetStartingTrackPositions(isStartGame); +} + +float * +cDMAudio::GetListenTimeArray() +{ + return MusicManager.GetListenTimeArray(); +} + +uint32 +cDMAudio::GetFavouriteRadioStation() +{ + return MusicManager.GetFavouriteRadioStation(); +} + +int32 +cDMAudio::GetRadioPosition(uint32 station) +{ + return MusicManager.GetRadioPosition(station); +} + +void +cDMAudio::SetPedTalkingStatus(CPed *ped, bool8 status) +{ + return AudioManager.SetPedTalkingStatus(ped, status); +} + +void +cDMAudio::SetPlayersMood(uint8 mood, uint32 time) +{ + return AudioManager.SetPlayersMood(mood, time); +} + +void +cDMAudio::ShutUpPlayerTalking(bool8 state) +{ + AudioManager.m_bIsPlayerShutUp = state; +} \ No newline at end of file diff --git a/src/miami/audio/DMAudio.h b/src/miami/audio/DMAudio.h new file mode 100644 index 00000000..110cb2db --- /dev/null +++ b/src/miami/audio/DMAudio.h @@ -0,0 +1,107 @@ +#pragma once + +#include "audio_enums.h" +#include "soundlist.h" +#include "Crime.h" + +#define AEHANDLE_IS_FAILED(h) ((h)<0) +#define AEHANDLE_IS_OK(h) ((h)>=0) + +#define NO_AUDIO_PROVIDER -3 +#define AUDIO_PROVIDER_NOT_DETERMINED -99 + +class cAudioScriptObject; +class CEntity; + +class cDMAudio +{ +public: + ~cDMAudio() + { } + + void Initialise(void); + void Terminate(void); + void Service(void); + + int32 CreateEntity(eAudioType type, void *UID); + void DestroyEntity(int32 audioEntity); + bool8 GetEntityStatus(int32 audioEntity); + void SetEntityStatus(int32 audioEntity, bool8 status); + void PlayOneShot(int32 audioEntity, uint16 oneShot, float volume); + void DestroyAllGameCreatedEntities(void); + + void SetOutputMode(bool8 surround); + void SetMP3BoostVolume(uint8 volume); + void SetEffectsMasterVolume(uint8 volume); + void SetMusicMasterVolume(uint8 volume); + void SetEffectsFadeVol(uint8 volume); + void SetMusicFadeVol(uint8 volume); + + uint8 GetNum3DProvidersAvailable(void); + char *Get3DProviderName(uint8 id); + + int8 AutoDetect3DProviders(void); + + int8 GetCurrent3DProviderIndex(void); + int8 SetCurrent3DProvider(uint8 which); + + void SetSpeakerConfig(int32 config); + + bool8 IsMP3RadioChannelAvailable(void); + + void ReleaseDigitalHandle(void); + void ReacquireDigitalHandle(void); + + void SetDynamicAcousticModelingStatus(bool8 status); + + bool8 CheckForAnAudioFileOnCD(void); + + char GetCDAudioDriveLetter(void); + bool8 IsAudioInitialised(void); + + void ResetPoliceRadio(); + void ReportCrime(eCrimeType crime, CVector const &pos); + + int32 CreateLoopingScriptObject(cAudioScriptObject *scriptObject); + void DestroyLoopingScriptObject(int32 audioEntity); + void CreateOneShotScriptObject(cAudioScriptObject *scriptObject); + + void PlaySuspectLastSeen(float x, float y, float z); + + void ReportCollision(CEntity *entityA, CEntity *entityB, uint8 surfaceTypeA, uint8 surfaceTypeB, float collisionPower, float velocity); + + void PlayFrontEndSound(uint16 frontend, uint32 volume); + void PlayRadioAnnouncement(uint32 announcement); + void PlayFrontEndTrack(uint32 track, bool8 frontendFlag); + void StopFrontEndTrack(void); + + void ResetTimers(uint32 time); + + void ChangeMusicMode(uint8 mode); + + void PreloadCutSceneMusic(uint32 track); + void PlayPreloadedCutSceneMusic(void); + void StopCutSceneMusic(void); + + void PreloadMissionAudio(uint8 slot, Const char *missionAudio); + uint8 GetMissionAudioLoadingStatus(uint8 slot); + void SetMissionAudioLocation(uint8 slot, float x, float y, float z); + void PlayLoadedMissionAudio(uint8 slot); + bool8 IsMissionAudioSamplePlaying(uint8 slot); + bool8 IsMissionAudioSampleFinished(uint8 slot); + void ClearMissionAudio(uint8 slot); + const char *GetMissionAudioLoadedLabel(uint8 slot); + + uint8 GetRadioInCar(void); + void SetRadioInCar(uint32 radio); + void SetRadioChannel(uint32 radio, int32 pos); + + void SetStartingTrackPositions(bool8 isStartGame); + float *GetListenTimeArray(); + uint32 GetFavouriteRadioStation(); + int32 GetRadioPosition(uint32 station); + void SetPedTalkingStatus(class CPed *ped, bool8 status); + void SetPlayersMood(uint8 mood, uint32 time); + void ShutUpPlayerTalking(bool8 state); +}; +extern cDMAudio DMAudio; diff --git a/src/miami/audio/MusicManager.cpp b/src/miami/audio/MusicManager.cpp new file mode 100644 index 00000000..c97b9c24 --- /dev/null +++ b/src/miami/audio/MusicManager.cpp @@ -0,0 +1,1423 @@ +#include "common.h" +#include +#include "soundlist.h" +#include "MusicManager.h" +#include "AudioManager.h" +#include "ControllerConfig.h" +#include "Camera.h" +#include "Font.h" +#include "Hud.h" +#include "ModelIndices.h" +#include "Replay.h" +#include "Pad.h" +#include "Text.h" +#include "Timer.h" +#include "World.h" +#include "sampman.h" +#include "Stats.h" +#include "Script.h" +#include "ZoneCull.h" +#include "Weather.h" +#include "DMAudio.h" +#include "GenericGameStorage.h" + +#if !defined FIX_BUGS && (defined RADIO_SCROLL_TO_PREV_STATION || defined RADIO_OFF_TEXT) +static_assert(false, "R*'s radio implementation is quite buggy, RADIO_SCROLL_TO_PREV_STATION and RADIO_OFF_TEXT won't work without FIX_BUGS"); +#endif + +cMusicManager MusicManager; +int32 gNumRetunePresses; +int32 gRetuneCounter; +bool8 g_bAnnouncementReadPosAlready; +uint8 RadioStaticCounter = 5; +uint32 RadioStaticTimer; + +CVector vecRiotPosition(300.7f, -322.0f, 12.0f); + +uint32 NewGameRadioTimers[10] = +{ + 948160, + 452150, + 2438150, + 3538230, + 3513100, + 4246050, + 1418050, + 3178240, + 471210, + 0 +}; + +cMusicManager::cMusicManager() +{ + m_bIsInitialised = FALSE; + m_bDisabled = FALSE; + m_nFrontendTrack = NO_TRACK; + m_nPlayingTrack = NO_TRACK; + m_nUpcomingMusicMode = MUSICMODE_DISABLED; + m_nMusicMode = MUSICMODE_DISABLED; + m_bSetNextStation = FALSE; + + for (int i = 0; i < NUM_RADIOS; i++) + aListenTimeArray[i] = 0.0f; + + m_nLastTrackServiceTime = 0.0f; + m_nVolumeLatency = 0; + m_nCurrentVolume = 0; + m_nMaxVolume = 0; + m_nAnnouncement = NO_TRACK; + m_bAnnouncementInProgress = FALSE; +} + +void +cMusicManager::ResetMusicAfterReload() +{ + float afRadioTime[NUM_RADIOS]; + + m_bRadioSetByScript = FALSE; + m_nRadioStationScript = WILDSTYLE; + m_nRadioPosition = -1; + m_nAnnouncement = NO_TRACK; + m_bAnnouncementInProgress = FALSE; + m_bSetNextStation = FALSE; + RadioStaticTimer = 0; + gNumRetunePresses = 0; + gRetuneCounter = 0; + m_nFrontendTrack = NO_TRACK; + m_nPlayingTrack = NO_TRACK; + m_FrontendLoopFlag = FALSE; + m_bTrackChangeStarted = FALSE; + m_nNextTrack = NO_TRACK; + m_nNextLoopFlag = FALSE; + m_bVerifyNextTrackStartedToPlay = FALSE; + m_bGameplayAllowsRadio = FALSE; + m_bRadioStreamReady = FALSE; + nFramesSinceCutsceneEnded = -1; + m_bUserResumedGame = FALSE; + m_bMusicModeChangeStarted = FALSE; + m_bEarlyFrontendTrack = FALSE; + m_nVolumeLatency = 0; + m_nCurrentVolume = 0; + m_nMaxVolume = 0; + + bool8 bRadioWasEverListened = FALSE; + + for (int i = 0; i < NUM_RADIOS; i++) { + afRadioTime[i] = CStats::GetFavoriteRadioStationList(i); + if (!bRadioWasEverListened && afRadioTime[i] != 0.0f) + bRadioWasEverListened = TRUE; + } + + if (!bRadioWasEverListened) return; + + for (int i = 0; i < NUM_RADIOS; i++) { + aListenTimeArray[i] = afRadioTime[i]; + int32 trackPos = GetSavedRadioStationPosition(i); + if (trackPos != -1) { + if (trackPos > m_aTracks[i].m_nLength) { + debug("Radio Track %d saved position is %d, Length is only %d\n", i, trackPos, m_aTracks[i].m_nLength); + trackPos %= m_aTracks[i].m_nLength; + } + m_aTracks[i].m_nPosition = trackPos; + m_aTracks[i].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); + } + } +} + +void +cMusicManager::SetStartingTrackPositions(bool8 isNewGameTimer) +{ + int pos; + + if (IsInitialised()) { + time_t timevalue = time(0); + if (timevalue == -1) { + pos = AudioManager.m_anRandomTable[0]; + } else { + tm* pTm = localtime(&timevalue); + if (pTm->tm_sec == 0) + pTm->tm_sec = AudioManager.m_anRandomTable[0]; + if (pTm->tm_min == 0) + pTm->tm_min = AudioManager.m_anRandomTable[1]; + if (pTm->tm_hour == 0) + pTm->tm_hour = AudioManager.m_anRandomTable[2]; + if (pTm->tm_mday == 0) + pTm->tm_mday = AudioManager.m_anRandomTable[3]; + if (pTm->tm_mon == 0) + pTm->tm_mon = AudioManager.m_anRandomTable[4]; + if (pTm->tm_year == 0) + pTm->tm_year = AudioManager.m_anRandomTable[3]; + if (pTm->tm_wday == 0) + pTm->tm_wday = AudioManager.m_anRandomTable[2]; + pos = pTm->tm_yday + * pTm->tm_wday + * pTm->tm_year + * pTm->tm_mon + * pTm->tm_mday + * pTm->tm_hour * pTm->tm_hour + * pTm->tm_min * pTm->tm_min + * pTm->tm_sec * pTm->tm_sec * pTm->tm_sec * pTm->tm_sec; + } + + for (int i = 0; i < TOTAL_STREAMED_SOUNDS; i++) { + m_aTracks[i].m_nLength = SampleManager.GetStreamedFileLength(i); + + if (i < STREAMED_SOUND_CITY_AMBIENT && isNewGameTimer) + m_aTracks[i].m_nPosition = NewGameRadioTimers[i]; + else if (i < STREAMED_SOUND_ANNOUNCE_BRIDGE_CLOSED) + m_aTracks[i].m_nPosition = (pos * AudioManager.m_anRandomTable[i % 5]) % m_aTracks[i].m_nLength; + else + m_aTracks[i].m_nPosition = 0; + + m_aTracks[i].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); + } + } +} + +bool8 +cMusicManager::Initialise() +{ + if (!IsInitialised()) { + m_bIsInitialised = TRUE; + SetStartingTrackPositions(FALSE); + m_bResetTimers = FALSE; + m_nResetTime = 0; + m_bRadioSetByScript = FALSE; + m_nRadioStationScript = WILDSTYLE; + m_nRadioPosition = -1; + m_nRadioInCar = NO_TRACK; + gRetuneCounter = 0; + gNumRetunePresses = 0; + m_nFrontendTrack = NO_TRACK; + m_nPlayingTrack = NO_TRACK; + m_nUpcomingMusicMode = MUSICMODE_DISABLED; + m_nMusicMode = MUSICMODE_DISABLED; + m_FrontendLoopFlag = FALSE; + m_bTrackChangeStarted = FALSE; + m_nNextTrack = NO_TRACK; + m_nNextLoopFlag = FALSE; + m_bVerifyNextTrackStartedToPlay = FALSE; + m_bGameplayAllowsRadio = FALSE; + m_bRadioStreamReady = FALSE; + nFramesSinceCutsceneEnded = -1; + m_bUserResumedGame = FALSE; + m_bMusicModeChangeStarted = FALSE; + m_nMusicModeToBeSet = MUSICMODE_DISABLED; + m_bEarlyFrontendTrack = FALSE; + m_nVolumeLatency = 0; + m_nCurrentVolume = 0; + m_nMaxVolume = 0; + } + return m_bIsInitialised; +} + +void +cMusicManager::Terminate() +{ + if (!IsInitialised()) return; + + if (SampleManager.IsStreamPlaying()) { + SampleManager.StopStreamedFile(); + m_nPlayingTrack = NO_TRACK; + } + m_bIsInitialised = FALSE; +} + +void +cMusicManager::SetRadioChannelByScript(uint32 station, int32 pos) +{ + if (m_bIsInitialised) { + if (station == STREAMED_SOUND_RADIO_MP3_PLAYER) + station = STREAMED_SOUND_CITY_AMBIENT; + if (station <= STREAMED_SOUND_RADIO_POLICE) { + m_bRadioSetByScript = TRUE; + m_nRadioStationScript = station; + m_nRadioPosition = pos == -1 ? -1 : pos % m_aTracks[station].m_nLength; + } + } +} + +bool8 +cMusicManager::PlayerInCar() +{ + CVehicle *vehicle = AudioManager.FindVehicleOfPlayer(); + if(!vehicle) + return FALSE; + + int32 State = FindPlayerPed()->m_nPedState; + + if(State == PED_DRAG_FROM_CAR || State == PED_EXIT_CAR || State == PED_ARRESTED) + return FALSE; + + if (vehicle->GetStatus() == STATUS_WRECKED) + return FALSE; + + return TRUE; +} + +uint32 +cMusicManager::GetRadioInCar(void) +{ + if (!m_bIsInitialised) return WILDSTYLE; + if (PlayerInCar()) { + CVehicle* veh = AudioManager.FindVehicleOfPlayer(); + if (veh != nil) { + if (UsesPoliceRadio(veh) || UsesTaxiRadio(veh)) { + if (m_nRadioInCar == NO_TRACK || (CReplay::IsPlayingBack() && !AudioManager.m_bIsPaused)) + return STREAMED_SOUND_RADIO_POLICE; + return m_nRadioInCar; + } + else return veh->m_nRadioStation; + } + } + + if (m_nRadioInCar == NO_TRACK || (CReplay::IsPlayingBack() && !AudioManager.m_bIsPaused)) + return RADIO_OFF; + return m_nRadioInCar; +} + +void +cMusicManager::SetRadioInCar(uint32 station) +{ + if (m_bIsInitialised) { + if (!PlayerInCar()) { + m_nRadioInCar = station; + return; + } + CVehicle* veh = AudioManager.FindVehicleOfPlayer(); + if (veh == nil) return; + if (UsesPoliceRadio(veh) || UsesTaxiRadio(veh)) + m_nRadioInCar = station; + else + veh->m_nRadioStation = station; + } +} + +void +cMusicManager::RecordRadioStats() +{ + if (m_nPlayingTrack < NUM_RADIOS) { + double time /*Rusty*/ = CTimer::GetTimeInMillisecondsPauseMode(); + if (time > m_nLastTrackServiceTime) + aListenTimeArray[m_nPlayingTrack] += time - m_nLastTrackServiceTime; + } +} + +void +cMusicManager::ChangeMusicMode(uint8 mode) +{ + if (!IsInitialised()) return; + + switch (mode) + { + case MUSICMODE_FRONTEND: + m_nUpcomingMusicMode = MUSICMODE_FRONTEND; + +#ifdef PAUSE_RADIO_IN_FRONTEND + // rewind those streams we weren't listening right now + for( uint32 i = STREAMED_SOUND_RADIO_WILD; i < STREAMED_SOUND_CUTSCENE_ASS_1; i++ ) { + m_aTracks[i].m_nPosition = GetTrackStartPos(i); + m_aTracks[i].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); + } +#endif + + break; + case MUSICMODE_GAME: m_nUpcomingMusicMode = MUSICMODE_GAME; break; + case MUSICMODE_CUTSCENE: + m_nUpcomingMusicMode = MUSICMODE_CUTSCENE; + if (SampleManager.IsStreamPlaying()) { + if (m_nPlayingTrack != NO_TRACK) { + RecordRadioStats(); + m_aTracks[m_nPlayingTrack].m_nPosition = SampleManager.GetStreamedFilePosition(); + m_aTracks[m_nPlayingTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); + } + } + SampleManager.StopStreamedFile(); + while (SampleManager.IsStreamPlaying()) + SampleManager.StopStreamedFile(); + m_nMusicMode = m_nUpcomingMusicMode; + m_bMusicModeChangeStarted = FALSE; + m_bTrackChangeStarted = FALSE; + m_nNextTrack = NO_TRACK; + m_nNextLoopFlag = FALSE; + m_bVerifyNextTrackStartedToPlay = FALSE; + m_nPlayingTrack = NO_TRACK; + m_nFrontendTrack = NO_TRACK; + m_bAnnouncementInProgress = FALSE; + m_nAnnouncement = NO_TRACK; + g_bAnnouncementReadPosAlready = FALSE; + break; + case MUSICMODE_DISABLE: m_nUpcomingMusicMode = MUSICMODE_DISABLED; break; + default: return; + } +} + +void +cMusicManager::ResetTimers(uint32 time) +{ + m_bResetTimers = TRUE; + m_nResetTime = time; +} + +void +cMusicManager::Service() +{ + if (m_bResetTimers) { + m_bResetTimers = FALSE; + m_nLastTrackServiceTime = m_nResetTime; + } + + static bool8 bRadioStatsRecorded = FALSE; + + if (!m_bIsInitialised || m_bDisabled) return; + + if (!m_bMusicModeChangeStarted) + m_nMusicModeToBeSet = m_nUpcomingMusicMode; + if (m_nMusicModeToBeSet == m_nMusicMode) { + if (!AudioManager.m_bIsPaused || AudioManager.m_bWasPaused || m_nMusicMode != MUSICMODE_FRONTEND) + { + switch (m_nMusicMode) + { + case MUSICMODE_FRONTEND: ServiceFrontEndMode(); break; + case MUSICMODE_GAME: ServiceGameMode(); break; + case MUSICMODE_CUTSCENE: SampleManager.SetStreamedVolumeAndPan(MAX_VOLUME, 63, TRUE); break; + } + } + else + m_nMusicMode = MUSICMODE_DISABLED; + } else { + m_bMusicModeChangeStarted = TRUE; + if (!m_bUserResumedGame && !AudioManager.m_bIsPaused && AudioManager.m_bWasPaused) + m_bUserResumedGame = TRUE; + if (AudioManager.m_FrameCounter % 4 == 0) { + gNumRetunePresses = 0; + gRetuneCounter = 0; + m_bSetNextStation = FALSE; + if (SampleManager.IsStreamPlaying()) { + if (m_nPlayingTrack != NO_TRACK && !bRadioStatsRecorded) + { + RecordRadioStats(); + m_aTracks[m_nPlayingTrack].m_nPosition = SampleManager.GetStreamedFilePosition(); + m_aTracks[m_nPlayingTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); + bRadioStatsRecorded = TRUE; + } + SampleManager.StopStreamedFile(); + } else { + bRadioStatsRecorded = FALSE; + m_nMusicMode = m_nMusicModeToBeSet; + m_bMusicModeChangeStarted = FALSE; + m_bTrackChangeStarted = FALSE; + m_nNextTrack = NO_TRACK; + m_nNextLoopFlag = FALSE; + m_bVerifyNextTrackStartedToPlay = FALSE; + m_nPlayingTrack = NO_TRACK; + if (m_bEarlyFrontendTrack) + m_bEarlyFrontendTrack = FALSE; + else + m_nFrontendTrack = NO_TRACK; + } + } + } +} + +void +cMusicManager::ServiceFrontEndMode() +{ + static bool8 bRadioStatsRecorded = FALSE; + +#ifdef PAUSE_RADIO_IN_FRONTEND + // pause radio + for (uint32 i = STREAMED_SOUND_RADIO_WILD; i < STREAMED_SOUND_CUTSCENE_ASS_1; i++) + m_aTracks[i].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); +#endif + + if (m_bAnnouncementInProgress) { + SampleManager.StopStreamedFile(); + if (SampleManager.IsStreamPlaying()) + return; + g_bAnnouncementReadPosAlready = FALSE; + m_nAnnouncement = NO_TRACK; + m_bAnnouncementInProgress = FALSE; + m_nNextTrack = NO_TRACK; + m_nFrontendTrack = NO_TRACK; + m_nPlayingTrack = NO_TRACK; + } + + if (AudioManager.m_FrameCounter % 4 != 0) return; + + if (!m_bTrackChangeStarted && !m_bVerifyNextTrackStartedToPlay) { + m_nNextTrack = m_nFrontendTrack; + m_nNextLoopFlag = m_FrontendLoopFlag; + } + + if (m_nNextTrack == m_nPlayingTrack) { + if (SampleManager.IsStreamPlaying()) { + if (m_nVolumeLatency > 0) m_nVolumeLatency--; + else { + if (m_nCurrentVolume < m_nMaxVolume) + m_nCurrentVolume = Min(m_nMaxVolume, m_nCurrentVolume + 6); + SampleManager.SetStreamedVolumeAndPan(m_nCurrentVolume, 63, FALSE); + } + } else { + if (m_nPlayingTrack == STREAMED_SOUND_RADIO_MP3_PLAYER) + SampleManager.StartStreamedFile(STREAMED_SOUND_RADIO_MP3_PLAYER, 0); + else if (m_nPlayingTrack == STREAMED_SOUND_MISSION_COMPLETED && !AudioManager.m_bIsPaused) + ChangeMusicMode(MUSICMODE_GAME); + } + } else { + m_bTrackChangeStarted = TRUE; + if (m_bVerifyNextTrackStartedToPlay || !SampleManager.IsStreamPlaying()) { + bRadioStatsRecorded = FALSE; + if (SampleManager.IsStreamPlaying() || m_nNextTrack == NO_TRACK) { + m_nPlayingTrack = m_nNextTrack; + m_bVerifyNextTrackStartedToPlay = FALSE; + m_bTrackChangeStarted = FALSE; + } else { + uint32 trackStartPos = (m_nNextTrack > STREAMED_SOUND_RADIO_POLICE) ? 0 : GetTrackStartPos(m_nNextTrack); + if (m_nNextTrack != NO_TRACK) { + SampleManager.SetStreamedFileLoopFlag(m_nNextLoopFlag); + SampleManager.StartStreamedFile(m_nNextTrack, trackStartPos); + m_nVolumeLatency = 3; + m_nCurrentVolume = 0; + m_nMaxVolume = 100; + SampleManager.SetStreamedVolumeAndPan(m_nCurrentVolume, 63, FALSE); + if (m_nNextTrack < STREAMED_SOUND_CITY_AMBIENT) + m_nLastTrackServiceTime = CTimer::GetTimeInMillisecondsPauseMode(); + m_bVerifyNextTrackStartedToPlay = TRUE; + } + } + } else { + if (m_nPlayingTrack != NO_TRACK && !bRadioStatsRecorded) { + m_aTracks[m_nPlayingTrack].m_nPosition = SampleManager.GetStreamedFilePosition(); + m_aTracks[m_nPlayingTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); + RecordRadioStats(); + bRadioStatsRecorded = TRUE; + } + SampleManager.SetStreamedVolumeAndPan(0, 63, FALSE); + SampleManager.StopStreamedFile(); + } + } +} + +void +cMusicManager::ServiceGameMode() +{ + CPed *ped = FindPlayerPed(); + CVehicle *vehicle = AudioManager.FindVehicleOfPlayer(); + m_bRadioStreamReady = m_bGameplayAllowsRadio; + m_bGameplayAllowsRadio = FALSE; + + switch (CGame::currArea) + { + case AREA_HOTEL: + case AREA_MALL: + case AREA_STRIP_CLUB: + case AREA_DIRT: + case AREA_BLOOD: + case AREA_OVALRING: + case AREA_MALIBU_CLUB: + m_bGameplayAllowsRadio = FALSE; + break; + default: + if (SampleManager.GetMusicVolume()) { + if (PlayerInCar()) + m_bGameplayAllowsRadio = TRUE; + } else + m_bGameplayAllowsRadio = FALSE; + break; + } + + if (!m_bGameplayAllowsRadio) { + nFramesSinceCutsceneEnded = -1; + gNumRetunePresses = 0; + gRetuneCounter = 0; + m_bSetNextStation = FALSE; + } else if (ped) { + if(!ped->DyingOrDead() && vehicle) { +#ifdef GTA_PC + if (SampleManager.IsMP3RadioChannelAvailable() + && vehicle->m_nRadioStation < USERTRACK + && ControlsManager.GetIsKeyboardKeyJustDown(rsF9)) + { + if (!UsesPoliceRadio(vehicle) && !UsesTaxiRadio(vehicle)) { + gNumRetunePresses = 0; + gRetuneCounter = 20; + RadioStaticCounter = 0; + if (vehicle->m_nRadioStation < USERTRACK) + { + do + ++gNumRetunePresses; + while (gNumRetunePresses + vehicle->m_nRadioStation < USERTRACK); + } + } + } +#endif + if (CPad::GetPad(0)->ChangeStationJustDown()) + { + if (!UsesPoliceRadio(vehicle) && !UsesTaxiRadio(vehicle)) { + gNumRetunePresses++; + gRetuneCounter = 20; + RadioStaticCounter = 0; + } + } +#ifdef RADIO_SCROLL_TO_PREV_STATION + else if(!CPad::GetPad(0)->ArePlayerControlsDisabled() && (CPad::GetPad(0)->GetMouseWheelDownJustDown() || CPad::GetPad(0)->GetMouseWheelUpJustDown())) { + if(!UsesPoliceRadio(vehicle) && !UsesTaxiRadio(vehicle)) { + int scrollNext = ControlsManager.GetControllerKeyAssociatedWithAction(VEHICLE_CHANGE_RADIO_STATION, MOUSE); + int scrollPrev = scrollNext == rsMOUSEWHEELUPBUTTON ? rsMOUSEWHEELDOWNBUTTON + : scrollNext == rsMOUSEWHEELDOWNBUTTON ? rsMOUSEWHEELUPBUTTON : -1; + + if(scrollPrev != -1 && !ControlsManager.IsAnyVehicleActionAssignedToMouseKey(scrollPrev)) { + gNumRetunePresses--; + gRetuneCounter = 20; + RadioStaticCounter = 0; + int track = gNumRetunePresses + vehicle->m_nRadioStation; + while(track < 0) track += NUM_RADIOS + 1; + while(track >= NUM_RADIOS + 1) track -= NUM_RADIOS + 1; + if(!DMAudio.IsMP3RadioChannelAvailable() && track == USERTRACK) gNumRetunePresses--; + } + } + } +#endif + } + } + + if (m_bUserResumedGame) + { + m_bRadioStreamReady = FALSE; + m_bUserResumedGame = FALSE; + } + if (m_nPlayingTrack == NO_TRACK && m_nFrontendTrack == NO_TRACK) + m_bRadioStreamReady = FALSE; + + if (m_bGameplayAllowsRadio) + { + if (!m_bRadioStreamReady) + { + if(vehicle == nil) { + m_nFrontendTrack = STREAMED_SOUND_RADIO_WAVE; // huh? + return; + } + if(m_bRadioSetByScript) { + if(UsesPoliceRadio(vehicle)) + m_nFrontendTrack = STREAMED_SOUND_RADIO_POLICE; + else if(UsesTaxiRadio(vehicle)) + m_nFrontendTrack = STREAMED_SOUND_RADIO_TAXI; + else { + m_nFrontendTrack = m_nRadioStationScript; + vehicle->m_nRadioStation = m_nRadioStationScript; + } + if(m_nRadioPosition != -1) { + m_aTracks[m_nFrontendTrack].m_nPosition = m_nRadioPosition; + m_aTracks[m_nFrontendTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); + } + m_bRadioSetByScript = FALSE; + return; + } + + // This starts the radio when you enter the car. + m_nFrontendTrack = GetCarTuning(); + return; + } + if (m_nAnnouncement < NO_TRACK) { + if ((m_bAnnouncementInProgress || m_nFrontendTrack == m_nPlayingTrack) && ServiceAnnouncement()) { + if (m_bAnnouncementInProgress) { + m_bSetNextStation = FALSE; + gNumRetunePresses = 0; + gRetuneCounter = 0; + return; + } + if(m_nAnnouncement == NO_TRACK) { + m_nNextTrack = NO_TRACK; + m_nFrontendTrack = GetCarTuning(); + m_bSetNextStation = FALSE; + gRetuneCounter = 0; + gNumRetunePresses = 0; + } + } + } + if (!m_bAnnouncementInProgress + && m_nAnnouncement == NO_TRACK + && m_nPlayingTrack == STREAMED_SOUND_RADIO_MP3_PLAYER + && !SampleManager.IsStreamPlaying()) + { + SampleManager.StartStreamedFile(STREAMED_SOUND_RADIO_MP3_PLAYER, 0); + } + + if (!m_bRadioSetByScript) + { + // Because when you switch radio back and forth, gNumRetunePresses will be 0 but gRetuneCounter won't. +#ifdef RADIO_SCROLL_TO_PREV_STATION + if(gRetuneCounter != 0) { + if(gRetuneCounter > 1) + gRetuneCounter--; + else if(gRetuneCounter == 1) { + m_bSetNextStation = TRUE; + gRetuneCounter = 0; + } + } +#else + if (gNumRetunePresses != 0) + { + if (--gRetuneCounter == 0) + { + m_bSetNextStation = TRUE; + gRetuneCounter = 0; + } + } +#endif + if (gRetuneCounter) + { + int32 station = gNumRetunePresses + vehicle->m_nRadioStation; +#ifdef RADIO_SCROLL_TO_PREV_STATION + while (station < 0) station += NUM_RADIOS + 1; +#endif + while (station >= NUM_RADIOS + 1) station -= NUM_RADIOS + 1; + + // Scrolling back won't hit here, so increasing isn't problem + if (!DMAudio.IsMP3RadioChannelAvailable() && station == USERTRACK) + { + ++gNumRetunePresses; + station = RADIO_OFF; + } + if (station == RADIO_OFF) + { + if (gRetuneCounter == 19) // One less then what switching radio sets, so runs right after turning off radio + { + AudioManager.PlayOneShot(AudioManager.m_nFrontEndEntity, SOUND_FRONTEND_RADIO_TURN_OFF, 0.0f); + RadioStaticCounter = 5; + } + } + else + { +#ifdef RADIO_SCROLL_TO_PREV_STATION + if (vehicle->m_nRadioStation == RADIO_OFF && gRetuneCounter == 19) // Right after turning on the radio +#else + if (station == 0 && gRetuneCounter == 19) // Right after turning on the radio +#endif + AudioManager.PlayOneShot(AudioManager.m_nFrontEndEntity, SOUND_FRONTEND_RADIO_TURN_ON, 0.0f); + AudioManager.DoPoliceRadioCrackle(); + } + } + if (RadioStaticCounter < 2 && CTimer::GetTimeInMilliseconds() > RadioStaticTimer + 800) + { + AudioManager.PlayOneShot(AudioManager.m_nFrontEndEntity, SOUND_RADIO_CHANGE, 0.0f); + RadioStaticCounter++; + RadioStaticTimer = CTimer::GetTimeInMilliseconds(); + } + if (m_bSetNextStation) + m_nFrontendTrack = GetNextCarTuning(); + if (m_nFrontendTrack >= STREAMED_SOUND_CITY_AMBIENT && m_nFrontendTrack <= STREAMED_SOUND_AMBSIL_AMBIENT) + SetUpCorrectAmbienceTrack(); + ServiceTrack(vehicle, ped); + if (m_bSetNextStation) + m_bSetNextStation = FALSE; + return; + } + if (UsesPoliceRadio(vehicle)) + m_nFrontendTrack = STREAMED_SOUND_RADIO_POLICE; + else if (UsesTaxiRadio(vehicle)) + m_nFrontendTrack = STREAMED_SOUND_RADIO_TAXI; + else { + m_nFrontendTrack = m_nRadioStationScript; + vehicle->m_nRadioStation = m_nRadioStationScript; + } + + if (m_nRadioPosition != -1) { + m_aTracks[m_nFrontendTrack].m_nPosition = m_nRadioPosition; + m_aTracks[m_nFrontendTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); + } + + gRetuneCounter = 0; + gNumRetunePresses = 0; + m_bSetNextStation = FALSE; + m_bRadioSetByScript = FALSE; + if (m_nFrontendTrack >= STREAMED_SOUND_CITY_AMBIENT && m_nFrontendTrack <= STREAMED_SOUND_AMBSIL_AMBIENT) + SetUpCorrectAmbienceTrack(); + ServiceTrack(vehicle, ped); + if (m_bSetNextStation) + m_bSetNextStation = FALSE; + return; + } + + if (m_bAnnouncementInProgress) + { + SampleManager.StopStreamedFile(); + if (SampleManager.IsStreamPlaying()) + return; + g_bAnnouncementReadPosAlready = FALSE; + m_nAnnouncement = NO_TRACK; + m_bAnnouncementInProgress = FALSE; + m_nNextTrack = NO_TRACK; + m_nFrontendTrack = NO_TRACK; + m_nPlayingTrack = NO_TRACK; + } + SetUpCorrectAmbienceTrack(); + ServiceTrack(nil, ped); +} + +void +cMusicManager::SetUpCorrectAmbienceTrack() +{ + switch (CGame::currArea) + { + case AREA_MAIN_MAP: + case AREA_EVERYWHERE: + if (CTheScripts::RiotIntensity != 0 && ((TheCamera.GetPosition() - vecRiotPosition).MagnitudeSqr() < SQR(65.0f))) + m_nFrontendTrack = STREAMED_SOUND_LAW4RIOT_AMBIENT; + else if (TheCamera.DistanceToWater <= 90.0f) { + if (CCullZones::bAtBeachForAudio) { + if (CWeather::OldWeatherType != WEATHER_HURRICANE && CWeather::NewWeatherType != WEATHER_HURRICANE || CWeather::Wind <= 1.0f) + m_nFrontendTrack = STREAMED_SOUND_BEACH_AMBIENT; + else + m_nFrontendTrack = STREAMED_SOUND_HAVANA_BEACH_AMBIENT; + } + else if (CWeather::OldWeatherType != WEATHER_HURRICANE && CWeather::NewWeatherType != WEATHER_HURRICANE || CWeather::Wind <= 1.0f) + m_nFrontendTrack = STREAMED_SOUND_WATER_AMBIENT; + else + m_nFrontendTrack = STREAMED_SOUND_HAVANA_WATER_AMBIENT; + } + else if (CWeather::OldWeatherType != WEATHER_HURRICANE && CWeather::NewWeatherType != WEATHER_HURRICANE || CWeather::Wind <= 1.0f) + m_nFrontendTrack = STREAMED_SOUND_CITY_AMBIENT; + else + m_nFrontendTrack = STREAMED_SOUND_HAVANA_CITY_AMBIENT; + break; + case AREA_HOTEL: + m_nFrontendTrack = STREAMED_SOUND_HOTEL_AMBIENT; + break; + case AREA_MALL: + m_nFrontendTrack = STREAMED_SOUND_MALL_AMBIENT; + break; + case AREA_STRIP_CLUB: + m_nFrontendTrack = STREAMED_SOUND_STRIPCLUB_AMBIENT; + break; + case AREA_DIRT: + case AREA_BLOOD: + case AREA_OVALRING: + m_nFrontendTrack = STREAMED_SOUND_DIRTRING_AMBIENT; + break; + case AREA_MALIBU_CLUB: + m_nFrontendTrack = STREAMED_SOUND_MALIBU_AMBIENT; + break; + case AREA_MANSION: + case AREA_BANK: + case AREA_LAWYERS: + case AREA_COFFEE_SHOP: + case AREA_CONCERT_HALL: + case AREA_STUDIO: + case AREA_RIFLE_RANGE: + case AREA_BIKER_BAR: + case AREA_POLICE_STATION: + m_nFrontendTrack = STREAMED_SOUND_AMBSIL_AMBIENT; + break; + } +} + +float +GetHeightScale() +{ + if (TheCamera.GetPosition().z > 20.0f) { + if (TheCamera.GetPosition().z < 50.0f) + return 1.0f - (TheCamera.GetPosition().z - 20.0f) / 30.0f; + return 0.0f; + } + return 1.0f; +} + +void +cMusicManager::ComputeAmbienceVol(bool8 reset, uint8& outVolume) +{ + static float fVol = 0.0f; + + float fHeightScale = GetHeightScale(); + + if (CTheScripts::RiotIntensity > 0) { + float distToRiotSq = (TheCamera.GetPosition() - vecRiotPosition).MagnitudeSqr(); + if (distToRiotSq < SQR(100.0f)) { + if (distToRiotSq >= SQR(65.0f)) + outVolume = (Sqrt(distToRiotSq) - 65.0f) / 35.0f * (127.0f * fHeightScale); + else if (distToRiotSq >= SQR(20.0f)) + outVolume = (CTheScripts::RiotIntensity * (1.0f - (Sqrt(distToRiotSq) - 20.0f) / 45.0f) * (127.0f * fHeightScale)) / MAX_VOLUME; + else + outVolume = (CTheScripts::RiotIntensity * (127.0f * fHeightScale)) / MAX_VOLUME; + return; + } + } + + if (reset) + fVol = 0.0f; + else if (fVol < 60.0f) { + if ((m_nPlayingTrack >= STREAMED_SOUND_HAVANA_CITY_AMBIENT) && (m_nPlayingTrack <= STREAMED_SOUND_HAVANA_BEACH_AMBIENT)) + fVol += 20.0f; + else + fVol += 1.0f; + fVol = Min(fVol, 60.0f); + } + + if ((m_nPlayingTrack >= STREAMED_SOUND_MALL_AMBIENT) && (m_nPlayingTrack <= STREAMED_SOUND_AMBSIL_AMBIENT)) { + outVolume = fVol; + return; + } + + if (CWeather::OldWeatherType == WEATHER_HURRICANE || CWeather::NewWeatherType == WEATHER_HURRICANE) { + if (CWeather::Wind > 1.0f) { + outVolume = (CWeather::Wind - 1.0f) * fVol; + return; + } + fVol = (1.0f - CWeather::Wind) * fVol; + } + + if (TheCamera.DistanceToWater > 140.0f) { + outVolume = fVol; + return; + } + + if (TheCamera.DistanceToWater > 90.0f) { + outVolume = ((TheCamera.DistanceToWater - 90.0f) / 50.0f * fVol * fHeightScale); + return; + } + + if (TheCamera.DistanceToWater > 40.0f) { + outVolume = fVol; + return; + } + + outVolume = (90.0f - fHeightScale) / 50.0f * fVol; +} + +bool8 +cMusicManager::ServiceAnnouncement() +{ + if (m_bAnnouncementInProgress) { + if (SampleManager.IsStreamPlaying()) + m_nPlayingTrack = m_nNextTrack; + else if (m_nPlayingTrack != NO_TRACK) { + m_nAnnouncement = NO_TRACK; + m_bAnnouncementInProgress = FALSE; + m_nPlayingTrack = NO_TRACK; + } + return TRUE; + } else if (SampleManager.IsStreamPlaying()) { + if (m_nPlayingTrack != NO_TRACK && !g_bAnnouncementReadPosAlready) { + RecordRadioStats(); + m_aTracks[m_nPlayingTrack].m_nPosition = SampleManager.GetStreamedFilePosition(); + g_bAnnouncementReadPosAlready = TRUE; + m_aTracks[m_nPlayingTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); + } + SampleManager.StopStreamedFile(); + } else { + g_bAnnouncementReadPosAlready = FALSE; + m_nPlayingTrack = NO_TRACK; + m_nNextTrack = m_nAnnouncement; + SampleManager.SetStreamedFileLoopFlag(FALSE); + SampleManager.StartStreamedFile(m_nNextTrack, 0); + SampleManager.SetStreamedVolumeAndPan(MAX_VOLUME, 63, FALSE); + m_bAnnouncementInProgress = TRUE; + } + + return TRUE; +} + +void +cMusicManager::ServiceTrack(CVehicle *veh, CPed *ped) +{ + static bool8 bRadioStatsRecorded = FALSE; + static bool8 bRadioStatsRecorded2 = FALSE; + uint8 volume; + if (!m_bTrackChangeStarted) + m_nNextTrack = m_nFrontendTrack; + if (gRetuneCounter != 0 || m_bSetNextStation) { + if (SampleManager.IsStreamPlaying()) { + if (m_nPlayingTrack != NO_TRACK && !bRadioStatsRecorded) { + m_aTracks[m_nPlayingTrack].m_nPosition = SampleManager.GetStreamedFilePosition(); + m_aTracks[m_nPlayingTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); + RecordRadioStats(); + bRadioStatsRecorded = TRUE; + } + SampleManager.SetStreamedVolumeAndPan(0, 63, FALSE); + SampleManager.StopStreamedFile(); + } + return; + } + + if (bRadioStatsRecorded) { + bRadioStatsRecorded = FALSE; + m_nPlayingTrack = NO_TRACK; + } + + if (m_nNextTrack != m_nPlayingTrack) + { + m_bTrackChangeStarted = TRUE; + SampleManager.SetStreamedVolumeAndPan(0, 63, FALSE); + if (!(AudioManager.m_FrameCounter & 1)) { + if (m_bVerifyNextTrackStartedToPlay || !SampleManager.IsStreamPlaying()) { + bRadioStatsRecorded2 = FALSE; + if (SampleManager.IsStreamPlaying()) { + m_nPlayingTrack = m_nNextTrack; + m_bVerifyNextTrackStartedToPlay = FALSE; + m_bTrackChangeStarted = FALSE; + if (veh) { +#ifdef FIX_BUGS + if (m_nPlayingTrack >= STREAMED_SOUND_CITY_AMBIENT && m_nPlayingTrack <= STREAMED_SOUND_AMBSIL_AMBIENT) + veh->m_nRadioStation = RADIO_OFF; + else if (m_nPlayingTrack < STREAMED_SOUND_CITY_AMBIENT) + veh->m_nRadioStation = m_nPlayingTrack; +#else + if (veh->m_nRadioStation >= STREAMED_SOUND_CITY_AMBIENT && veh->m_nRadioStation <= STREAMED_SOUND_AMBSIL_AMBIENT) + veh->m_nRadioStation = RADIO_OFF; + else + veh->m_nRadioStation = m_nPlayingTrack; +#endif + } + } else { + uint32 pos = GetTrackStartPos(m_nNextTrack); + if (m_nNextTrack != NO_TRACK) { + SampleManager.SetStreamedFileLoopFlag(TRUE); + SampleManager.StartStreamedFile(m_nNextTrack, pos); + if (m_nFrontendTrack < STREAMED_SOUND_CITY_AMBIENT || m_nFrontendTrack > STREAMED_SOUND_AMBSIL_AMBIENT) + { + m_nVolumeLatency = 10; + m_nCurrentVolume = 0; + m_nMaxVolume = 100; + SampleManager.SetStreamedVolumeAndPan(m_nCurrentVolume, 63, FALSE); + } + else + { + ComputeAmbienceVol(TRUE, volume); + SampleManager.SetStreamedVolumeAndPan(volume, 63, TRUE); + } + if (m_nNextTrack < STREAMED_SOUND_CITY_AMBIENT) + m_nLastTrackServiceTime = CTimer::GetTimeInMillisecondsPauseMode(); + m_bVerifyNextTrackStartedToPlay = TRUE; + } + } + } else { + if (m_nPlayingTrack == NO_TRACK) + debug("m_nPlayingTrack == NO_TRACK, yet track playing - tidying up\n"); + else if (!bRadioStatsRecorded2) + { + m_aTracks[m_nPlayingTrack].m_nPosition = SampleManager.GetStreamedFilePosition(); + m_aTracks[m_nPlayingTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); + bRadioStatsRecorded2 = TRUE; + RecordRadioStats(); + if (m_nPlayingTrack >= STREAMED_SOUND_HAVANA_CITY_AMBIENT && m_nPlayingTrack <= STREAMED_SOUND_HAVANA_BEACH_AMBIENT) + { + if (m_nNextTrack >= STREAMED_SOUND_HAVANA_CITY_AMBIENT && m_nNextTrack <= STREAMED_SOUND_HAVANA_BEACH_AMBIENT) + AudioManager.PlayOneShot(AudioManager.m_nFrontEndEntity, SOUND_FRONTEND_HURRICANE, 0.0); + } + } + SampleManager.SetStreamedVolumeAndPan(0, 63, FALSE); + SampleManager.StopStreamedFile(); + } + } + return; + } + + if (m_nPlayingTrack >= STREAMED_SOUND_CITY_AMBIENT && m_nPlayingTrack <= STREAMED_SOUND_AMBSIL_AMBIENT) + { + ComputeAmbienceVol(FALSE, volume); + SampleManager.SetStreamedVolumeAndPan(volume, 63, TRUE); + return; + } + if (CTimer::GetIsSlowMotionActive()) + { + if (TheCamera.pTargetEntity) + { + float DistToTargetSq = (TheCamera.pTargetEntity->GetPosition() - TheCamera.GetPosition()).MagnitudeSqr(); + if (DistToTargetSq >= SQR(55.0f)) + { + SampleManager.SetStreamedVolumeAndPan(0, 63, FALSE); + } + else if (DistToTargetSq >= SQR(10.0f)) + { + volume = (45.0f - (Sqrt(DistToTargetSq) - 10.0f)) / 45.0f * m_nCurrentVolume; + if (AudioManager.ShouldDuckMissionAudio(0) || AudioManager.ShouldDuckMissionAudio(1)) + volume /= 4; + + uint8 pan = 0; + if (volume > 0) + { + CVector panVec; + AudioManager.TranslateEntity(&TheCamera.pTargetEntity->GetPosition(), &panVec); + pan = AudioManager.ComputePan(55.0f, &panVec); + } + if (gRetuneCounter != 0) + volume = 0; + SampleManager.SetStreamedVolumeAndPan(volume, pan, FALSE); + } + else if (AudioManager.ShouldDuckMissionAudio(0) || AudioManager.ShouldDuckMissionAudio(1)) + SampleManager.SetStreamedVolumeAndPan(m_nCurrentVolume, 63, FALSE); + else if (gRetuneCounter != 0) + SampleManager.SetStreamedVolumeAndPan(0, 63, FALSE); + else + SampleManager.SetStreamedVolumeAndPan(m_nCurrentVolume, 63, FALSE); + } + } else if (AudioManager.ShouldDuckMissionAudio(0) || AudioManager.ShouldDuckMissionAudio(1)) { + SampleManager.SetStreamedVolumeAndPan(Min(m_nCurrentVolume, 25), 63, FALSE); + nFramesSinceCutsceneEnded = 0; + } else { + if (nFramesSinceCutsceneEnded == -1) + volume = m_nCurrentVolume; + else if (nFramesSinceCutsceneEnded < 20) + { + volume = Min(m_nCurrentVolume, 25); + nFramesSinceCutsceneEnded++; + } + else if (nFramesSinceCutsceneEnded < 40) + { + volume = Min(m_nCurrentVolume, 3 * (nFramesSinceCutsceneEnded - 20) + 25); + nFramesSinceCutsceneEnded++; + } + else + { + volume = m_nCurrentVolume; + nFramesSinceCutsceneEnded = -1; + } + if (gRetuneCounter != 0) + volume = 0; + SampleManager.SetStreamedVolumeAndPan(volume, 63, FALSE); + } + if (m_nVolumeLatency > 0) + m_nVolumeLatency--; + else if (m_nCurrentVolume < m_nMaxVolume) + m_nCurrentVolume = Min(m_nMaxVolume, m_nCurrentVolume + 6); +} + +void +cMusicManager::PreloadCutSceneMusic(uint32 track) +{ + if (IsInitialised() && !m_bDisabled && track < TOTAL_STREAMED_SOUNDS && m_nMusicMode == MUSICMODE_CUTSCENE) { + AudioManager.ResetPoliceRadio(); + while (SampleManager.IsStreamPlaying()) + SampleManager.StopStreamedFile(); + SampleManager.PreloadStreamedFile(track); + SampleManager.SetStreamedVolumeAndPan(MAX_VOLUME, 63, TRUE); + m_nPlayingTrack = track; + } +} + +void +cMusicManager::PlayPreloadedCutSceneMusic(void) +{ + if (IsInitialised() && !m_bDisabled && m_nMusicMode == MUSICMODE_CUTSCENE) + SampleManager.StartPreloadedStreamedFile(); +} + +void +cMusicManager::StopCutSceneMusic(void) +{ + if (IsInitialised() && !m_bDisabled && m_nMusicMode == MUSICMODE_CUTSCENE) { + SampleManager.StopStreamedFile(); + m_nPlayingTrack = NO_TRACK; + } +} + +void +cMusicManager::PlayFrontEndTrack(uint32 track, bool8 loopFlag) +{ + if (IsInitialised() && !m_bDisabled && track < TOTAL_STREAMED_SOUNDS && (m_nUpcomingMusicMode == MUSICMODE_FRONTEND || m_nMusicMode == MUSICMODE_FRONTEND)) + { + m_nFrontendTrack = track; + m_FrontendLoopFlag = loopFlag; + if (m_nMusicMode != MUSICMODE_FRONTEND) + m_bEarlyFrontendTrack = TRUE; + } +} + +void +cMusicManager::StopFrontEndTrack() +{ + if (m_nUpcomingMusicMode == MUSICMODE_FRONTEND || m_nMusicMode == MUSICMODE_FRONTEND) + m_nFrontendTrack = NO_TRACK; +} + +void +cMusicManager::PlayAnnouncement(uint32 announcement) +{ + if (IsInitialised() && !m_bDisabled && !m_bAnnouncementInProgress) + m_nAnnouncement = announcement; +} + +uint32 +cMusicManager::GetNextCarTuning() +{ + CVehicle *veh = AudioManager.FindVehicleOfPlayer(); + if (veh == nil) return STREAMED_SOUND_CITY_AMBIENT; + if (UsesPoliceRadio(veh)) return STREAMED_SOUND_RADIO_POLICE; + if (UsesTaxiRadio(veh)) return STREAMED_SOUND_RADIO_TAXI; + if (gNumRetunePresses != 0) { +#ifdef RADIO_SCROLL_TO_PREV_STATION + // m_nRadioStation is unsigned, so... + int station = veh->m_nRadioStation + gNumRetunePresses; + while(station < 0) station += NUM_RADIOS + 1; + while(station >= NUM_RADIOS + 1) station -= NUM_RADIOS + 1; + veh->m_nRadioStation = station; +#else + veh->m_nRadioStation += gNumRetunePresses; + while(veh->m_nRadioStation >= NUM_RADIOS + 1) + veh->m_nRadioStation -= NUM_RADIOS + 1; +#endif + DMAudio.IsMP3RadioChannelAvailable(); // woof, just call and do nothing =P they manipulate gNumRetunePresses on DisplayRadioStationName in this case + gNumRetunePresses = 0; + } + return veh->m_nRadioStation; +} + +uint32 +cMusicManager::GetCarTuning() +{ + CVehicle* veh = AudioManager.FindVehicleOfPlayer(); + if (veh == nil) return STREAMED_SOUND_CITY_AMBIENT; + if (UsesPoliceRadio(veh)) return STREAMED_SOUND_RADIO_POLICE; + if (UsesTaxiRadio(veh)) return STREAMED_SOUND_RADIO_TAXI; + if (veh->m_nRadioStation == USERTRACK && !SampleManager.IsMP3RadioChannelAvailable()) + veh->m_nRadioStation = AudioManager.m_anRandomTable[2] % USERTRACK; + return veh->m_nRadioStation; +} + +float* +cMusicManager::GetListenTimeArray() +{ + return aListenTimeArray; +} + +uint32 +cMusicManager::GetTrackStartPos(uint32 track) +{ + if (!IsInitialised()) return 0; + + uint32 pos = m_aTracks[track].m_nPosition; + if (CTimer::GetTimeInMillisecondsPauseMode() > m_aTracks[track].m_nLastPosCheckTimer) + pos += Min(CTimer::GetTimeInMillisecondsPauseMode() - m_aTracks[track].m_nLastPosCheckTimer, 270000); + else + m_aTracks[track].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); + + if (pos > m_aTracks[track].m_nLength) + pos %= m_aTracks[track].m_nLength; + return pos; +} + +uint32 +cMusicManager::GetRadioPosition(uint32 station) +{ + if (station < NUM_RADIOS) + return GetTrackStartPos(station); + return 0; +} + +uint32 +cMusicManager::GetFavouriteRadioStation() +{ + uint32 favstation = 0; + + for (int i = 1; i < NUM_RADIOS; i++) { + if (aListenTimeArray[i] > aListenTimeArray[favstation]) + favstation = i; + } + + return favstation; +} + +bool8 +cMusicManager::CheckForMusicInterruptions() +{ + return (m_nPlayingTrack == STREAMED_SOUND_MISSION_COMPLETED) || (m_nPlayingTrack == STREAMED_SOUND_CUTSCENE_FINALE); +} + +void +cMusicManager::SetMalibuClubTrackPos(uint8 scriptObject) +{ + if (!IsInitialised()) + m_aTracks[STREAMED_SOUND_MALIBU_AMBIENT].m_nPosition = 8640; + if (m_nNextTrack != STREAMED_SOUND_MALIBU_AMBIENT && m_nPlayingTrack != STREAMED_SOUND_MALIBU_AMBIENT) { + switch (scriptObject) + { + case SCRIPT_SOUND_NEW_BUILDING_MALIBU_1: + m_aTracks[STREAMED_SOUND_MALIBU_AMBIENT].m_nPosition = (AudioManager.m_anRandomTable[0] % 128) + 8640; + break; + case SCRIPT_SOUND_NEW_BUILDING_MALIBU_2: + m_aTracks[STREAMED_SOUND_MALIBU_AMBIENT].m_nPosition = (AudioManager.m_anRandomTable[0] % 128) + 286720; + break; + case SCRIPT_SOUND_NEW_BUILDING_MALIBU_3: + m_aTracks[STREAMED_SOUND_MALIBU_AMBIENT].m_nPosition = (AudioManager.m_anRandomTable[0] % 128) + 509120; + break; + } + m_aTracks[STREAMED_SOUND_MALIBU_AMBIENT].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); + } +} + +void +cMusicManager::SetStripClubTrackPos(uint8 scriptObject) +{ + if (!IsInitialised()) + m_aTracks[STREAMED_SOUND_STRIPCLUB_AMBIENT].m_nPosition = 0; + if (m_nNextTrack != STREAMED_SOUND_STRIPCLUB_AMBIENT && m_nPlayingTrack != STREAMED_SOUND_STRIPCLUB_AMBIENT) + { + switch (scriptObject) + { + case SCRIPT_SOUND_NEW_BUILDING_STRIP_1: + m_aTracks[STREAMED_SOUND_STRIPCLUB_AMBIENT].m_nPosition = AudioManager.m_anRandomTable[0] % 128; + break; + case SCRIPT_SOUND_NEW_BUILDING_STRIP_2: + m_aTracks[STREAMED_SOUND_STRIPCLUB_AMBIENT].m_nPosition = (AudioManager.m_anRandomTable[0] % 128) + 320200; + break; + case SCRIPT_SOUND_NEW_BUILDING_STRIP_3: + m_aTracks[STREAMED_SOUND_STRIPCLUB_AMBIENT].m_nPosition = (AudioManager.m_anRandomTable[0] % 128) + 672000; + break; + } + m_aTracks[STREAMED_SOUND_STRIPCLUB_AMBIENT].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); + } +} + +void +cMusicManager::DisplayRadioStationName() +{ + uint8 gStreamedSound; + static wchar *pCurrentStation = nil; + static uint8 cDisplay = 0; + + if(!CTimer::GetIsPaused() && !TheCamera.m_WideScreenOn && PlayerInCar() && + !CReplay::IsPlayingBack()) { + CVehicle *vehicle = AudioManager.FindVehicleOfPlayer(); + + if (vehicle) + { + // Prev scroll needs it to be signed, and m_nFrontendTrack can be NO_TRACK thus FIX_BUGS +#if defined RADIO_SCROLL_TO_PREV_STATION || defined FIX_BUGS + int track; +#else + uint8 track; +#endif + gStreamedSound = vehicle->m_nRadioStation; + if (gStreamedSound >= STREAMED_SOUND_CITY_AMBIENT && gStreamedSound <= STREAMED_SOUND_AMBSIL_AMBIENT) + gStreamedSound = RADIO_OFF; + if (gNumRetunePresses != 0) + { + track = gNumRetunePresses + gStreamedSound; +#ifdef RADIO_SCROLL_TO_PREV_STATION + while (track < 0) track += NUM_RADIOS + 1; +#endif + while (track >= NUM_RADIOS + 1) track -= NUM_RADIOS + 1; + + // On scrolling back we handle this condition on key press. No need to change this. + if (!DMAudio.IsMP3RadioChannelAvailable() && track == USERTRACK) + gNumRetunePresses++; + } + else +#ifdef RADIO_OFF_TEXT + track = GetCarTuning(); // gStreamedSound or veh->m_nRadioStation would also work, but these don't cover police/taxi radios +#else + track = m_nFrontendTrack; +#endif + wchar* string = nil; + switch (track) { + case WILDSTYLE: string = TheText.Get("FEA_FM0"); break; + case FLASH_FM: string = TheText.Get("FEA_FM1"); break; + case KCHAT: string = TheText.Get("FEA_FM2"); break; + case FEVER: string = TheText.Get("FEA_FM3"); break; + case V_ROCK: string = TheText.Get("FEA_FM4"); break; + case VCPR: string = TheText.Get("FEA_FM5"); break; + case RADIO_ESPANTOSO: string = TheText.Get("FEA_FM6"); break; + case EMOTION: string = TheText.Get("FEA_FM7"); break; + case WAVE: string = TheText.Get("FEA_FM8"); break; + case USERTRACK: + if (!SampleManager.IsMP3RadioChannelAvailable()) + return; + string = TheText.Get("FEA_MP3"); break; +#ifdef RADIO_OFF_TEXT + case RADIO_OFF: { + // Otherwise RADIO OFF will be seen after pausing-resuming game and Mission Complete text + if (!m_bRadioStreamReady || !m_bGameplayAllowsRadio) + return; + + extern wchar WideErrorString[]; + + string = TheText.Get("FEA_NON"); + if (string == WideErrorString) { + pCurrentStation = nil; + return; + } + break; + } +#endif + default: return; + }; + + if (pCurrentStation != string) { + pCurrentStation = string; + cDisplay = 60; + } + else { + if (cDisplay == 0) return; +#ifdef FIX_BUGS + cDisplay -= CTimer::GetLogicalFramesPassed(); +#else + cDisplay--; +#endif + } + + CFont::SetJustifyOff(); + CFont::SetBackgroundOff(); + CFont::SetScale(SCREEN_SCALE_X(0.8f), SCREEN_SCALE_Y(1.35f)); + CFont::SetPropOn(); + CFont::SetFontStyle(FONT_STANDARD); + CFont::SetCentreOn(); + CFont::SetCentreSize(SCREEN_STRETCH_X(DEFAULT_SCREEN_WIDTH)); + CFont::SetColor(CRGBA(0, 0, 0, 255)); + CFont::PrintString(SCREEN_WIDTH / 2 + SCREEN_SCALE_X(2.0f), SCREEN_SCALE_Y(22.0f) + SCREEN_SCALE_Y(2.0f), pCurrentStation); + + if (gNumRetunePresses) + CFont::SetColor(CRGBA(102, 133, 143, 255)); + else + CFont::SetColor(CRGBA(147, 196, 211, 255)); + + CFont::PrintString(SCREEN_WIDTH / 2, SCREEN_SCALE_Y(22.0f), pCurrentStation); + CFont::DrawFonts(); + } + } + // Always show station text after entering car. Same behaviour as III and SA. +#ifdef FIX_BUGS + else + pCurrentStation = nil; +#endif +} + +bool8 +cMusicManager::UsesPoliceRadio(CVehicle *veh) +{ + switch (veh->GetModelIndex()) + { + case MI_VCNMAV: + case MI_POLMAV: + case MI_COASTG: + case MI_RHINO: + case MI_BARRACKS: + return TRUE; + case MI_MRWHOOP: + case MI_HUNTER: + return FALSE; + } + return veh->UsesSiren(); +} + +bool8 +cMusicManager::UsesTaxiRadio(CVehicle *veh) +{ + if (veh->GetModelIndex() != MI_KAUFMAN) return FALSE; + return CTheScripts::bPlayerHasMetDebbieHarry; +} + +void +cMusicManager::ServiceAmbience() +{ +} + +bool8 +cMusicManager::ChangeRadioChannel() +{ + return TRUE; +} + +// these two are empty +void cMusicManager::Enable() {} +void cMusicManager::Disable() {} diff --git a/src/miami/audio/MusicManager.h b/src/miami/audio/MusicManager.h new file mode 100644 index 00000000..52254b19 --- /dev/null +++ b/src/miami/audio/MusicManager.h @@ -0,0 +1,113 @@ +#pragma once + +#include "audio_enums.h" + +class tStreamedSample +{ +public: + uint32 m_nLength; + uint32 m_nPosition; + uint32 m_nLastPosCheckTimer; +}; + +class CVehicle; +class CPed; + +class cMusicManager +{ +public: + bool8 m_bIsInitialised; + bool8 m_bDisabled; + bool8 m_bSetNextStation; + uint8 m_nVolumeLatency; + uint8 m_nCurrentVolume; + uint8 m_nMaxVolume; + uint32 m_nAnnouncement; + bool8 m_bAnnouncementInProgress; + tStreamedSample m_aTracks[TOTAL_STREAMED_SOUNDS]; + bool8 m_bResetTimers; + uint32 m_nResetTime; + bool8 m_bRadioSetByScript; + uint8 m_nRadioStationScript; + int32 m_nRadioPosition; + uint32 m_nRadioInCar; + uint32 m_nFrontendTrack; + uint32 m_nPlayingTrack; + uint8 m_nUpcomingMusicMode; + uint8 m_nMusicMode; + bool8 m_FrontendLoopFlag; + bool8 m_bTrackChangeStarted; + uint32 m_nNextTrack; + bool8 m_nNextLoopFlag; + bool8 m_bVerifyNextTrackStartedToPlay; + bool8 m_bGameplayAllowsRadio; + bool8 m_bRadioStreamReady; + int8 nFramesSinceCutsceneEnded; + bool8 m_bUserResumedGame; + bool8 m_bMusicModeChangeStarted; + uint8 m_nMusicModeToBeSet; + bool8 m_bEarlyFrontendTrack; + float aListenTimeArray[NUM_RADIOS]; + float m_nLastTrackServiceTime; + +public: + cMusicManager(); + bool8 IsInitialised() { return m_bIsInitialised; } + uint8 GetMusicMode() { return m_nMusicMode; } + uint32 GetCurrentTrack() { return m_nPlayingTrack; } + + void ResetMusicAfterReload(); + void SetStartingTrackPositions(bool8 isNewGameTimer); + bool8 Initialise(); + void Terminate(); + + void ChangeMusicMode(uint8 mode); + void StopFrontEndTrack(); + + bool8 PlayerInCar(); + void DisplayRadioStationName(); + + void PlayAnnouncement(uint32); + void PlayFrontEndTrack(uint32, bool8); + void PreloadCutSceneMusic(uint32); + void PlayPreloadedCutSceneMusic(void); + void StopCutSceneMusic(void); + uint32 GetRadioInCar(void); + void SetRadioInCar(uint32); + void SetRadioChannelByScript(uint32, int32); + + void ResetTimers(uint32); + void Service(); + void ServiceFrontEndMode(); + void ServiceGameMode(); + void ServiceAmbience(); + void ServiceTrack(CVehicle *veh, CPed *ped); + + bool8 UsesPoliceRadio(CVehicle *veh); + bool8 UsesTaxiRadio(CVehicle *veh); + uint32 GetTrackStartPos(uint32 track); + + void ComputeAmbienceVol(bool8 reset, uint8& outVolume); + bool8 ServiceAnnouncement(); + + uint32 GetCarTuning(); + uint32 GetNextCarTuning(); + bool8 ChangeRadioChannel(); + void RecordRadioStats(); + void SetUpCorrectAmbienceTrack(); + float *GetListenTimeArray(); + uint32 GetRadioPosition(uint32 station); + uint32 GetFavouriteRadioStation(); + void SetMalibuClubTrackPos(uint8 pos); + void SetStripClubTrackPos(uint8 pos); + bool8 CheckForMusicInterruptions(); + + void Enable(); + void Disable(); +}; + +VALIDATE_SIZE(cMusicManager, 0x95C); + +extern cMusicManager MusicManager; +extern bool8 g_bAnnouncementReadPosAlready; // we have a symbol of this so it was declared in .h +float GetHeightScale(); diff --git a/src/miami/audio/PolRadio.cpp b/src/miami/audio/PolRadio.cpp new file mode 100644 index 00000000..26ade81e --- /dev/null +++ b/src/miami/audio/PolRadio.cpp @@ -0,0 +1,750 @@ +#include "common.h" + +#include "DMAudio.h" + +#include "AudioManager.h" + +#include "AudioSamples.h" +#include "MusicManager.h" +#include "PlayerPed.h" +#include "PolRadio.h" +#include "Replay.h" +#include "Vehicle.h" +#include "World.h" +#include "Zones.h" +#include "sampman.h" +#include "Wanted.h" + +struct tPoliceRadioZone { + char m_aName[8]; + uint32 m_nSampleIndex; + int32 field_12; +}; + +tPoliceRadioZone ZoneSfx[NUMAUDIOZONES]; + +uint32 g_nMissionAudioSfx = TOTAL_AUDIO_SAMPLES; +int8 g_nMissionAudioPlayingStatus = PLAY_STATUS_FINISHED; +bool8 gSpecialSuspectLastSeenReport; +uint32 gMinTimeToNextReport[NUM_CRIME_TYPES]; + +void +cAudioManager::InitialisePoliceRadioZones() +{ + for (int32 i = 0; i < NUMAUDIOZONES; i++) + memset(ZoneSfx[i].m_aName, 0, 8); + +#define SETZONESFX(i, name, sample) \ + strcpy(ZoneSfx[i].m_aName, name); \ + ZoneSfx[i].m_nSampleIndex = sample; + + SETZONESFX(0, "VICE_C", SFX_POLICE_RADIO_VICE_CITY); + SETZONESFX(1, "IND_ZON", SFX_POLICE_RADIO_VICE_CITY_BEACH); + SETZONESFX(2, "COM_ZON", SFX_POLICE_RADIO_VICE_CITY_MAINLAND); + SETZONESFX(3, "BEACH1", SFX_POLICE_RADIO_OCEAN_BEACH); + SETZONESFX(4, "BEACH2", SFX_POLICE_RADIO_WASHINGTON_BEACH); + SETZONESFX(5, "BEACH3", SFX_POLICE_RADIO_VICE_POINT); + SETZONESFX(6, "GOLFC", SFX_POLICE_RADIO_LEAF_LINKS); + SETZONESFX(7, "STARI", SFX_POLICE_RADIO_STARFISH_ISLAND); + SETZONESFX(8, "DOCKS", SFX_POLICE_RADIO_VICEPORT); + SETZONESFX(9, "HAVANA", SFX_POLICE_RADIO_LITTLE_HAVANA); + SETZONESFX(10, "HAITI", SFX_POLICE_RADIO_LITTLE_HAITI); + SETZONESFX(11, "PORNI", SFX_POLICE_RADIO_PRAWN_ISLAND); + SETZONESFX(12, "DTOWN", SFX_POLICE_RADIO_DOWNTOWN); + SETZONESFX(13, "A_PORT", SFX_POLICE_RADIO_ESCOBAR_INTERNATIONAL); + +#undef SETZONESFX +} + +void +cAudioManager::InitialisePoliceRadio() +{ + m_sPoliceRadioQueue.Reset(); + for (int32 i = 0; i < ARRAY_SIZE(m_aCrimes); i++) + m_aCrimes[i].type = CRIME_NONE; +#if !defined(GTA_PS2) || defined(AUDIO_REVERB) + SampleManager.SetChannelReverbFlag(CHANNEL_POLICE_RADIO, FALSE); +#endif + gSpecialSuspectLastSeenReport = FALSE; + for (int32 i = 0; i < ARRAY_SIZE(gMinTimeToNextReport); i++) + gMinTimeToNextReport[i] = m_FrameCounter; +} + +void +cAudioManager::ResetPoliceRadio() +{ + if (!m_bIsInitialised) return; + if (SampleManager.GetChannelUsedFlag(CHANNEL_POLICE_RADIO)) SampleManager.StopChannel(CHANNEL_POLICE_RADIO); + InitialisePoliceRadio(); +} + +void +cAudioManager::SetMissionScriptPoliceAudio(uint32 sfx) +{ + if (!m_bIsInitialised) return; + if (g_nMissionAudioPlayingStatus != PLAY_STATUS_PLAYING) { + g_nMissionAudioPlayingStatus = PLAY_STATUS_STOPPED; + g_nMissionAudioSfx = sfx; + } +} + +int8 +cAudioManager::GetMissionScriptPoliceAudioPlayingStatus() +{ + return g_nMissionAudioPlayingStatus; +} + +void +cAudioManager::DoPoliceRadioCrackle() +{ + m_sQueueSample.m_nEntityIndex = m_nPoliceChannelEntity; + m_sQueueSample.m_nCounter = 0; + m_sQueueSample.m_nSampleIndex = SFX_POLICE_RADIO_CRACKLE; + m_sQueueSample.m_nBankIndex = SFX_BANK_0; + m_sQueueSample.m_bIs2D = TRUE; + m_sQueueSample.m_nPriority = 10; + m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_POLICE_RADIO_CRACKLE); + m_sQueueSample.m_nVolume = m_anRandomTable[2] % 20 + 15; + m_sQueueSample.m_nLoopCount = 0; + SET_EMITTING_VOLUME(m_sQueueSample.m_nVolume); + SET_LOOP_OFFSETS(SFX_POLICE_RADIO_CRACKLE) + m_sQueueSample.m_bStatic = FALSE; + SET_SOUND_REVERB(FALSE); + m_sQueueSample.m_nPan = 63; + m_sQueueSample.m_nFramesToPlay = 3; + SET_SOUND_REFLECTION(FALSE); + AddSampleToRequestedQueue(); +} + +void +cAudioManager::ServicePoliceRadio() +{ + int32 wantedLevel = 0; // uninitialized variable + static uint32 nLastSeen = 300; + + if(!m_bIsInitialised) return; + + if(!m_bIsPaused) { + bool8 crimeReport = SetupCrimeReport(); +#ifdef FIX_BUGS // Crash at 0x5fe6ef + if(CReplay::IsPlayingBack() || !FindPlayerPed() || !FindPlayerPed()->m_pWanted) + return; +#endif + CPlayerPed *playerPed = FindPlayerPed(); + if (playerPed) { + wantedLevel = playerPed->m_pWanted->GetWantedLevel(); + if (!crimeReport) { + if (wantedLevel != 0) { + if (nLastSeen != 0) +#ifdef FIX_BUGS + nLastSeen -= CTimer::GetLogicalFramesPassed(); +#else + nLastSeen--; +#endif + else { + nLastSeen = m_anRandomTable[1] % 1000 + 2000; + SetupSuspectLastSeenReport(); + } + } + } + } + } + ServicePoliceRadioChannel(wantedLevel); +} + +void +cAudioManager::ServicePoliceRadioChannel(uint8 wantedLevel) +{ + bool8 processed = FALSE; + uint32 sample; + uint32 freq; + + static int cWait = 0; + static bool8 bChannelOpen = FALSE; + static uint8 bMissionAudioPhysicalPlayingStatus = PLAY_STATUS_STOPPED; + static uint32 PoliceChannelFreq = 22050; + + if (!m_bIsInitialised) return; + + if (m_bIsPaused) { + if (SampleManager.GetChannelUsedFlag(CHANNEL_POLICE_RADIO)) SampleManager.StopChannel(CHANNEL_POLICE_RADIO); + if (g_nMissionAudioSfx != NO_SAMPLE && bMissionAudioPhysicalPlayingStatus == PLAY_STATUS_PLAYING && + SampleManager.IsStreamPlaying(1)) { + SampleManager.PauseStream(TRUE, 1); + } + } else { + if (m_bWasPaused && g_nMissionAudioSfx != NO_SAMPLE && + bMissionAudioPhysicalPlayingStatus == PLAY_STATUS_PLAYING) { + SampleManager.PauseStream(FALSE, 1); + } + if (m_sPoliceRadioQueue.m_nSamplesInQueue == 0) bChannelOpen = FALSE; + if (cWait) { +#ifdef FIX_BUGS + cWait -= CTimer::GetLogicalFramesPassed(); +#else + --cWait; +#endif + return; + } + if (g_nMissionAudioSfx != NO_SAMPLE && !bChannelOpen) { + if (g_nMissionAudioPlayingStatus != PLAY_STATUS_STOPPED) { + if (g_nMissionAudioPlayingStatus == PLAY_STATUS_PLAYING && bMissionAudioPhysicalPlayingStatus == PLAY_STATUS_STOPPED && + SampleManager.IsStreamPlaying(1)) { + bMissionAudioPhysicalPlayingStatus = PLAY_STATUS_PLAYING; + } + if (bMissionAudioPhysicalPlayingStatus == PLAY_STATUS_PLAYING) { + if (SampleManager.IsStreamPlaying(1)) { + DoPoliceRadioCrackle(); + } else { + bMissionAudioPhysicalPlayingStatus = PLAY_STATUS_FINISHED; + g_nMissionAudioPlayingStatus = PLAY_STATUS_FINISHED; + g_nMissionAudioSfx = NO_SAMPLE; + cWait = 30; + } + return; + } + } else if (!SampleManager.GetChannelUsedFlag(CHANNEL_POLICE_RADIO)) { + SampleManager.PreloadStreamedFile(g_nMissionAudioSfx, 1); + SampleManager.SetStreamedVolumeAndPan(MAX_VOLUME, 63, TRUE, 1); + SampleManager.StartPreloadedStreamedFile(1); + g_nMissionAudioPlayingStatus = PLAY_STATUS_PLAYING; + bMissionAudioPhysicalPlayingStatus = PLAY_STATUS_STOPPED; + return; + } + } + if (bChannelOpen) DoPoliceRadioCrackle(); + if ((g_nMissionAudioSfx == NO_SAMPLE || g_nMissionAudioPlayingStatus != PLAY_STATUS_PLAYING) && + !SampleManager.GetChannelUsedFlag(CHANNEL_POLICE_RADIO) && m_sPoliceRadioQueue.m_nSamplesInQueue != 0) { + sample = m_sPoliceRadioQueue.Remove(); + if (wantedLevel == 0) { + if (gSpecialSuspectLastSeenReport) { + gSpecialSuspectLastSeenReport = FALSE; + } else if (sample == SFX_POLICE_RADIO_MESSAGE_NOISE_1) { + bChannelOpen = FALSE; + processed = TRUE; + } + } + if (sample == NO_SAMPLE) { + if (!processed) cWait = 30; + } else { + SampleManager.InitialiseChannel(CHANNEL_POLICE_RADIO, sample, SFX_BANK_0); + switch (sample) { + case SFX_POLICE_RADIO_MESSAGE_NOISE_1: + freq = m_anRandomTable[4] % 2000 + 10025; + bChannelOpen = bChannelOpen == FALSE; + break; + default: freq = SampleManager.GetSampleBaseFrequency(sample); break; + } + PoliceChannelFreq = freq; +#ifdef USE_TIME_SCALE_FOR_AUDIO + SampleManager.SetChannelFrequency(CHANNEL_POLICE_RADIO, freq * CTimer::GetTimeScale()); +#else + SampleManager.SetChannelFrequency(CHANNEL_POLICE_RADIO, freq); +#endif + SampleManager.SetChannelVolume(CHANNEL_POLICE_RADIO, 100); + SampleManager.SetChannelPan(CHANNEL_POLICE_RADIO, 63); + SampleManager.SetChannelLoopCount(CHANNEL_POLICE_RADIO, 1); +#ifndef GTA_PS2 + SampleManager.SetChannelLoopPoints(CHANNEL_POLICE_RADIO, 0, -1); +#endif + SampleManager.StartChannel(CHANNEL_POLICE_RADIO); + } + if (processed) ResetPoliceRadio(); + } + } +} + +bool8 +cAudioManager::SetupCrimeReport() +{ + int16 audioZoneId; + CZone *zone; + float rangeX; + float rangeY; + float halfX; + float halfY; + float quarterX; + float quarterY; + int i; + uint32 sampleIndex; + bool8 processed = FALSE; + + if (MusicManager.m_nMusicMode == MUSICMODE_CUTSCENE) return FALSE; + + if (POLICE_RADIO_QUEUE_MAX_SAMPLES - m_sPoliceRadioQueue.m_nSamplesInQueue <= 9) { + AgeCrimes(); + return TRUE; + } + + for (i = 0; i < ARRAY_SIZE(m_aCrimes); i++) { + if (m_aCrimes[i].type != CRIME_NONE) + break; + } + + if (i == ARRAY_SIZE(m_aCrimes)) return FALSE; + audioZoneId = CTheZones::FindAudioZone(&m_aCrimes[i].position); + if (audioZoneId >= 0 && audioZoneId < NUMAUDIOZONES) { + zone = CTheZones::GetAudioZone(audioZoneId); + for (int j = 0; j < NUMAUDIOZONES; j++) { + if (strcmp(zone->name, ZoneSfx[j].m_aName) == 0) { + sampleIndex = ZoneSfx[j].m_nSampleIndex; + m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_MESSAGE_NOISE_1); + m_sPoliceRadioQueue.Add(m_anRandomTable[0] % 3 + SFX_WEVE_GOT); + m_sPoliceRadioQueue.Add(SFX_A_10); + switch (m_aCrimes[i].type) { + case CRIME_PED_BURNED: + case CRIME_HIT_PED_NASTYWEAPON: + m_aCrimes[i].type = CRIME_HIT_PED; + break; + case CRIME_COP_BURNED: + case CRIME_HIT_COP_NASTYWEAPON: + m_aCrimes[i].type = CRIME_HIT_COP; + break; + case CRIME_VEHICLE_BURNED: m_aCrimes[i].type = CRIME_STEAL_CAR; break; + case CRIME_DESTROYED_CESSNA: m_aCrimes[i].type = CRIME_SHOOT_HELI; break; + case CRIME_EXPLOSION: m_aCrimes[i].type = CRIME_STEAL_CAR; break; // huh? + default: break; + } +#ifdef FIX_BUGS + m_sPoliceRadioQueue.Add(m_aCrimes[i].type + SFX_CRIME_1 - 1); +#else + m_sPoliceRadioQueue.Add(m_aCrimes[i].type + SFX_CRIME_1); +#endif + m_sPoliceRadioQueue.Add(SFX_IN); + rangeX = zone->maxx - zone->minx; + rangeY = zone->maxy - zone->miny; + halfX = 0.5f * rangeX + zone->minx; + halfY = 0.5f * rangeY + zone->miny; + quarterX = 0.25f * rangeX; + quarterY = 0.25f * rangeY; + + if (m_aCrimes[i].position.y > halfY + quarterY) { + m_sPoliceRadioQueue.Add(SFX_NORTH); + processed = TRUE; + } else if (m_aCrimes[i].position.y < halfY - quarterY) { + m_sPoliceRadioQueue.Add(SFX_SOUTH); + processed = TRUE; + } + + if (m_aCrimes[i].position.x > halfX + quarterX) + m_sPoliceRadioQueue.Add(SFX_EAST); + else if (m_aCrimes[i].position.x < halfX - quarterX) + m_sPoliceRadioQueue.Add(SFX_WEST); + else if (!processed) + m_sPoliceRadioQueue.Add(SFX_CENTRAL); + + m_sPoliceRadioQueue.Add(sampleIndex); + m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_MESSAGE_NOISE_1); + m_sPoliceRadioQueue.Add(NO_SAMPLE); + break; + } + } + } + m_aCrimes[i].type = CRIME_NONE; + AgeCrimes(); + return TRUE; +} + +Const uint32 gCarColourTable[][3] = { + {NO_SAMPLE, SFX_POLICE_RADIO_BLACK, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_WHITE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_BLUE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_RED, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_BLUE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_PURPLE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_YELLOW, NO_SAMPLE}, + {SFX_POLICE_RADIO_BRIGHT, SFX_POLICE_RADIO_BLUE, NO_SAMPLE}, + {SFX_POLICE_RADIO_LIGHT, SFX_POLICE_RADIO_BLUE, SFX_POLICE_RADIO_GREY}, + {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_RED, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_RED, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_RED, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_RED, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_RED, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_RED, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_RED, NO_SAMPLE}, + {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_ORANGE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_ORANGE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_ORANGE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_ORANGE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_ORANGE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_ORANGE, NO_SAMPLE}, + {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_YELLOW, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_YELLOW, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_YELLOW, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_YELLOW, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_YELLOW, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_YELLOW, NO_SAMPLE}, + {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_GREEN, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_GREEN, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_GREEN, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_GREEN, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_GREEN, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_GREEN, NO_SAMPLE}, + {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_BLUE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_BLUE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_BLUE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_BLUE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_BLUE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_BLUE, NO_SAMPLE}, + {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_PURPLE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_PURPLE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_PURPLE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_PURPLE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_PURPLE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_PURPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_SILVER, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_SILVER, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_SILVER, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_SILVER, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_SILVER, NO_SAMPLE}, + {NO_SAMPLE, SFX_POLICE_RADIO_SILVER, NO_SAMPLE}, + {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, + {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE} +}; + +void +cAudioManager::SetupSuspectLastSeenReport() +{ + CVehicle *veh; + uint8 color1; + uint32 main_color; + uint32 sample; + + uint32 color_pre_modifier; + uint32 color_post_modifier; + + if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE) { + veh = FindVehicleOfPlayer(); + if (veh != nil) { + if (POLICE_RADIO_QUEUE_MAX_SAMPLES - m_sPoliceRadioQueue.m_nSamplesInQueue > 9) { + color1 = veh->m_currentColour1; + if (color1 >= ARRAY_SIZE(gCarColourTable)) { + debug("\n *** UNKNOWN CAR COLOUR %d *** ", color1); + } else { + main_color = gCarColourTable[color1][1]; + color_pre_modifier = gCarColourTable[color1][0]; + color_post_modifier = gCarColourTable[color1][2]; + switch (veh->GetModelIndex()) { + case MI_LANDSTAL: + case MI_PATRIOT: + case MI_RANCHER: + case MI_FBIRANCH: + case MI_SANDKING: + sample = SFX_POLICE_RADIO_OFFROAD; + break; + case MI_IDAHO: + case MI_MANANA: + case MI_ESPERANT: + case MI_CUBAN: + case MI_STALLION: + case MI_SABRE: + case MI_SABRETUR: + case MI_VIRGO: + case MI_BLISTAC: + sample = SFX_POLICE_RADIO_TUDOOR; + break; + case MI_STINGER: + case MI_INFERNUS: + case MI_CHEETAH: + case MI_BANSHEE: + case MI_PHEONIX: + case MI_COMET: + case MI_DELUXO: + case MI_HOTRING: + sample = SFX_POLICE_RADIO_SPORTS_CAR; + break; + case MI_LINERUN: + sample = SFX_POLICE_RADIO_RIG; + break; + case MI_PEREN: + case MI_REGINA: + sample = SFX_POLICE_RADIO_STATION_WAGON; + break; + case MI_SENTINEL: + case MI_FBICAR: + case MI_WASHING: + case MI_SENTXS: + case MI_ADMIRAL: + case MI_GLENDALE: + case MI_OCEANIC: + case MI_HERMES: + case MI_GREENWOO: + sample = SFX_POLICE_RADIO_SEDAN; + break; + case MI_RIO: + sample = SFX_POLICE_RADIO_CRUISER; + break; + case MI_FIRETRUCK: + sample = SFX_POLICE_RADIO_FIRE_TRUCK; + break; + case MI_TRASH: + sample = SFX_POLICE_RADIO_GARBAGE_TRUCK; + break; + case MI_STRETCH: + case MI_LOVEFIST: + sample = SFX_POLICE_RADIO_STRETCH; + break; + case MI_VOODOO: + sample = SFX_POLICE_RADIO_LOWRIDER; + break; + case MI_PONY: + case MI_MOONBEAM: + case MI_SECURICA: + case MI_RUMPO: + case MI_GANGBUR: + case MI_YANKEE: + case MI_TOPFUN: + case MI_BURRITO: + case MI_SPAND: + sample = SFX_POLICE_RADIO_VAN; + break; + case MI_MULE: + case MI_BARRACKS: + case MI_PACKER: + case MI_FLATBED: + sample = SFX_POLICE_RADIO_TRUCK; + break; + case MI_AMBULAN: + sample = SFX_POLICE_RADIO_AMBULANCE; + break; + case MI_TAXI: + case MI_CABBIE: + case MI_ZEBRA: + case MI_KAUFMAN: + sample = SFX_POLICE_RADIO_TAXI; + break; + case MI_BOBCAT: + case MI_WALTON: + sample = SFX_POLICE_RADIO_PICKUP; + break; + case MI_MRWHOOP: + sample = SFX_POLICE_RADIO_ICE_CREAM_VAN; + break; + case MI_BFINJECT: + sample = SFX_POLICE_RADIO_BUGGY; + break; + case MI_HUNTER: + case MI_CHOPPER: + case MI_SEASPAR: + case MI_SPARROW: + case MI_MAVERICK: + case MI_VCNMAV: + case MI_POLMAV: + sample = SFX_POLICE_RADIO_HELICOPTER; + break; + case MI_POLICE: + sample = SFX_POLICE_RADIO_POLICE_CAR; + break; + case MI_ENFORCER: + sample = SFX_POLICE_RADIO_SWAT_VAN; + break; + case MI_PREDATOR: + case MI_SQUALO: + case MI_SPEEDER: + sample = SFX_POLICE_RADIO_SPEEDBOAT; + break; + case MI_BUS: + sample = SFX_POLICE_RADIO_BUS; + break; + case MI_RHINO: + sample = SFX_POLICE_RADIO_TANK; + break; + case MI_ANGEL: + case MI_PCJ600: + case MI_FREEWAY: + case MI_SANCHEZ: + sample = SFX_POLICE_RADIO_MOTOBIKE; + break; + case MI_COACH: + sample = SFX_POLICE_RADIO_COACH; + break; + case MI_ROMERO: + sample = SFX_POLICE_RADIO_HEARSE; + break; + case MI_PIZZABOY: + case MI_FAGGIO: + sample = SFX_POLICE_RADIO_MOPED; + break; + case MI_DEADDODO: + case MI_SKIMMER: + sample = SFX_POLICE_RADIO_PLANE; + break; + case MI_REEFER: + case MI_TROPIC: + case MI_COASTG: + case MI_MARQUIS: + case MI_JETMAX: + sample = SFX_POLICE_RADIO_BOAT; + break; + case MI_CADDY: + sample = SFX_POLICE_RADIO_GOLF_CART; + break; + case MI_DINGHY: + sample = SFX_POLICE_RADIO_DINGHY; + break; + default: + //debug("\n *** UNKNOWN CAR MODEL INDEX %d *** ", veh->GetModelIndex()); + return; + } + m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_MESSAGE_NOISE_1); + m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_SUSPECT); + if (m_anRandomTable[3] % 2) + m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_LAST_SEEN); + m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_IN_A); + if (color_pre_modifier != NO_SAMPLE) + m_sPoliceRadioQueue.Add(color_pre_modifier); + if (main_color != NO_SAMPLE) + m_sPoliceRadioQueue.Add(main_color); + if (color_post_modifier != NO_SAMPLE) + m_sPoliceRadioQueue.Add(color_post_modifier); + m_sPoliceRadioQueue.Add(sample); + m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_MESSAGE_NOISE_1); + m_sPoliceRadioQueue.Add(NO_SAMPLE); + } + } + } else if (POLICE_RADIO_QUEUE_MAX_SAMPLES - m_sPoliceRadioQueue.m_nSamplesInQueue > 4) { + m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_MESSAGE_NOISE_1); + m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_SUSPECT); + m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_ON_FOOT); + m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_MESSAGE_NOISE_1); + m_sPoliceRadioQueue.Add(NO_SAMPLE); + } + } +} + +void +cAudioManager::ReportCrime(eCrimeType type, const CVector &pos) +{ + int32 lastCrime = ARRAY_SIZE(m_aCrimes); + if (m_bIsInitialised && MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE && FindPlayerPed()->m_pWanted->GetWantedLevel() > 0 && + (type > CRIME_NONE || type < NUM_CRIME_TYPES) && m_FrameCounter >= gMinTimeToNextReport[type]) { + for (int32 i = 0; i < ARRAY_SIZE(m_aCrimes); i++) { + if (m_aCrimes[i].type != CRIME_NONE) { + if (m_aCrimes[i].type == type) { + m_aCrimes[i].position = pos; + m_aCrimes[i].timer = 0; + return; + } + } else + lastCrime = i; + } + + if (lastCrime < ARRAY_SIZE(m_aCrimes)) { + m_aCrimes[lastCrime].type = type; + m_aCrimes[lastCrime].position = pos; + m_aCrimes[lastCrime].timer = 0; + gMinTimeToNextReport[type] = m_FrameCounter + 500; + } + } +} + +void +cAudioManager::PlaySuspectLastSeen(float x, float y, float z) +{ + int16 audioZone; + CZone *zone; + float rangeX; + float rangeY; + float halfX; + float halfY; + float quarterX; + float quarterY; + uint32 sample; + bool8 processed = FALSE; + CVector vec = CVector(x, y, z); + + if (!m_bIsInitialised) return; + + if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE && POLICE_RADIO_QUEUE_MAX_SAMPLES - m_sPoliceRadioQueue.m_nSamplesInQueue > 9) { + audioZone = CTheZones::FindAudioZone(&vec); + if (audioZone >= 0 && audioZone < NUMAUDIOZONES) { + zone = CTheZones::GetAudioZone(audioZone); + for (int i = 0; i < NUMAUDIOZONES; i++) { + if (strcmp(zone->name, ZoneSfx[i].m_aName) == 0) { + sample = ZoneSfx[i].m_nSampleIndex; + m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_MESSAGE_NOISE_1); + m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_SUSPECT); + m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_LAST_SEEN); + m_sPoliceRadioQueue.Add(SFX_IN); + rangeX = zone->maxx - zone->minx; + rangeY = zone->maxy - zone->miny; + halfX = 0.5f * rangeX + zone->minx; + halfY = 0.5f * rangeY + zone->miny; + quarterX = 0.25f * rangeX; + quarterY = 0.25f * rangeY; + + if (vec.y > halfY + quarterY) { + m_sPoliceRadioQueue.Add(SFX_NORTH); + processed = TRUE; + } else if (vec.y < halfY - quarterY) { + m_sPoliceRadioQueue.Add(SFX_SOUTH); + processed = TRUE; + } + + if (vec.x > halfX + quarterX) + m_sPoliceRadioQueue.Add(SFX_EAST); + else if (vec.x < halfX - quarterX) + m_sPoliceRadioQueue.Add(SFX_WEST); + else if (!processed) + m_sPoliceRadioQueue.Add(SFX_CENTRAL); + m_sPoliceRadioQueue.Add(sample); + m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_MESSAGE_NOISE_1); + m_sPoliceRadioQueue.Add(NO_SAMPLE); + gSpecialSuspectLastSeenReport = TRUE; + break; + } + } + } + } +} + +void +cAudioManager::AgeCrimes() +{ + for (uint8 i = 0; i < ARRAY_SIZE(m_aCrimes); i++) { + if (m_aCrimes[i].type != CRIME_NONE) { + if (++m_aCrimes[i].timer > 1200) m_aCrimes[i].type = CRIME_NONE; + } + } +} diff --git a/src/miami/audio/PolRadio.h b/src/miami/audio/PolRadio.h new file mode 100644 index 00000000..55b2c9f7 --- /dev/null +++ b/src/miami/audio/PolRadio.h @@ -0,0 +1,67 @@ +#pragma once + +#include "Crime.h" +#include "AudioSamples.h" + +struct cAMCrime { + int32 type; + CVector position; + uint16 timer; + + cAMCrime() + { + type = CRIME_NONE; + position = CVector(0.0f, 0.0f, 0.0f); + timer = 0; + } +}; + +VALIDATE_SIZE(cAMCrime, 20); + +#define POLICE_RADIO_QUEUE_MAX_SAMPLES 60 + +class cPoliceRadioQueue +{ +public: + uint32 m_aSamples[POLICE_RADIO_QUEUE_MAX_SAMPLES]; + uint8 m_nSamplesInQueue; + uint8 m_nAddOffset; + uint8 m_nRemoveOffset; + + cPoliceRadioQueue() + { + Reset(); + } + + void Reset() + { + m_nAddOffset = 0; + m_nRemoveOffset = 0; + m_nSamplesInQueue = 0; + } + + bool8 Add(uint32 sample) + { + if (m_nSamplesInQueue != POLICE_RADIO_QUEUE_MAX_SAMPLES) { + m_aSamples[m_nAddOffset] = sample; + m_nSamplesInQueue++; + m_nAddOffset = (m_nAddOffset + 1) % POLICE_RADIO_QUEUE_MAX_SAMPLES; + return TRUE; + } + return FALSE; + } + + uint32 Remove() + { + if (m_nSamplesInQueue != 0) { + uint32 sample = m_aSamples[m_nRemoveOffset]; + m_nSamplesInQueue--; + m_nRemoveOffset = (m_nRemoveOffset + 1) % POLICE_RADIO_QUEUE_MAX_SAMPLES; + return sample; + } + return NO_SAMPLE; + } + +}; + +VALIDATE_SIZE(cPoliceRadioQueue, 244); diff --git a/src/miami/audio/audio_enums.h b/src/miami/audio/audio_enums.h new file mode 100644 index 00000000..22b425e7 --- /dev/null +++ b/src/miami/audio/audio_enums.h @@ -0,0 +1,1336 @@ +#pragma once + +enum eRadioStation +{ + WILDSTYLE, + FLASH_FM, + KCHAT, + FEVER, + V_ROCK, + VCPR, + RADIO_ESPANTOSO, + EMOTION, + WAVE, + USERTRACK, + NUM_RADIOS = 10, + POLICE_RADIO = 10, + RADIO_OFF = 10, + //TAXI_RADIO, +}; + +enum eMusicMode +{ + MUSICMODE_FRONTEND = 0, + MUSICMODE_GAME, + MUSICMODE_CUTSCENE, + MUSICMODE_DISABLE, + MUSICMODE_DISABLED, +}; + +enum ePlayerMood +{ + PLAYER_MOOD_CALM = 0, + PLAYER_MOOD_PISSED_OFF, + PLAYER_MOOD_ANGRY, + PLAYER_MOOD_WISECRACKING, + MAX_PLAYER_MOODS, +}; + +enum eStreamedSounds +{ + STREAMED_SOUND_RADIO_WILD, + STREAMED_SOUND_RADIO_FLASH, + STREAMED_SOUND_RADIO_KCHAT, + STREAMED_SOUND_RADIO_FEVER, + STREAMED_SOUND_RADIO_VROCK, + STREAMED_SOUND_RADIO_VCPR, + STREAMED_SOUND_RADIO_ESPANTOSO, + STREAMED_SOUND_RADIO_EMOTION, + STREAMED_SOUND_RADIO_WAVE, + STREAMED_SOUND_RADIO_MP3_PLAYER, + STREAMED_SOUND_CITY_AMBIENT, + STREAMED_SOUND_WATER_AMBIENT, + STREAMED_SOUND_BEACH_AMBIENT, + STREAMED_SOUND_HAVANA_CITY_AMBIENT, + STREAMED_SOUND_HAVANA_WATER_AMBIENT, + STREAMED_SOUND_HAVANA_BEACH_AMBIENT, + STREAMED_SOUND_MALL_AMBIENT, + STREAMED_SOUND_STRIPCLUB_AMBIENT, + STREAMED_SOUND_MALIBU_AMBIENT, + STREAMED_SOUND_HOTEL_AMBIENT, + STREAMED_SOUND_DIRTRING_AMBIENT, + STREAMED_SOUND_LAW4RIOT_AMBIENT, + STREAMED_SOUND_AMBSIL_AMBIENT, + STREAMED_SOUND_RADIO_POLICE, + STREAMED_SOUND_RADIO_TAXI, + STREAMED_SOUND_ANNOUNCE_BRIDGE_CLOSED, + STREAMED_SOUND_ANNOUNCE_BRIDGE_OPEN, + STREAMED_SOUND_CUTSCENE_ASS_1, + STREAMED_SOUND_CUTSCENE_ASS_2, + STREAMED_SOUND_CUTSCENE_BANK_1, + STREAMED_SOUND_CUTSCENE_BANK_2A, + STREAMED_SOUND_CUTSCENE_BANK_2B, + STREAMED_SOUND_CUTSCENE_BANK_3A, + STREAMED_SOUND_CUTSCENE_BANK_3B, + STREAMED_SOUND_CUTSCENE_BANK_4, + STREAMED_SOUND_CUTSCENE_BIKE_1, + STREAMED_SOUND_CUTSCENE_BIKE_2, + STREAMED_SOUND_CUTSCENE_BIKE_3, + STREAMED_SOUND_CUTSCENE_BUD_1, + STREAMED_SOUND_CUTSCENE_BUD_2, + STREAMED_SOUND_CUTSCENE_BUD_3, + STREAMED_SOUND_CUTSCENE_CAP_1, + STREAMED_SOUND_CUTSCENE_CAR_1, + STREAMED_SOUND_CUTSCENE_CNT_1A, + STREAMED_SOUND_CUTSCENE_CNT_1B, + STREAMED_SOUND_CUTSCENE_CNT_2, + STREAMED_SOUND_CUTSCENE_COK_1, + STREAMED_SOUND_CUTSCENE_COK_2A, + STREAMED_SOUND_CUTSCENE_COK_2B, + STREAMED_SOUND_CUTSCENE_COK_3, + STREAMED_SOUND_CUTSCENE_COK_4A, + STREAMED_SOUND_CUTSCENE_COK_4A2, + STREAMED_SOUND_CUTSCENE_COK_4B, + STREAMED_SOUND_CUTSCENE_COL_1, + STREAMED_SOUND_CUTSCENE_COL_2, + STREAMED_SOUND_CUTSCENE_COL_3A, + STREAMED_SOUND_CUTSCENE_COL_4A, + STREAMED_SOUND_CUTSCENE_COL_5A, + STREAMED_SOUND_CUTSCENE_COL_5B, + STREAMED_SOUND_CUTSCENE_CUB_1, + STREAMED_SOUND_CUTSCENE_CUB_2, + STREAMED_SOUND_CUTSCENE_CUB_3, + STREAMED_SOUND_CUTSCENE_CUB_4, + STREAMED_SOUND_CUTSCENE_DRUG_1, + STREAMED_SOUND_CUTSCENE_FIN, + STREAMED_SOUND_CUTSCENE_FIN2, + STREAMED_SOUND_CUTSCENE_FINALE, + STREAMED_SOUND_CUTSCENE_HAT_1, + STREAMED_SOUND_CUTSCENE_HAT_2, + STREAMED_SOUND_CUTSCENE_HAT_3, + STREAMED_SOUND_CUTSCENE_ICE_1, + STREAMED_SOUND_CUTSCENE_INT_A, + STREAMED_SOUND_CUTSCENE_INT_B, + STREAMED_SOUND_CUTSCENE_INT_D, + STREAMED_SOUND_CUTSCENE_INT_M, + STREAMED_SOUND_CUTSCENE_LAW_1A, + STREAMED_SOUND_CUTSCENE_LAW_1B, + STREAMED_SOUND_CUTSCENE_LAW_2A, + STREAMED_SOUND_CUTSCENE_LAW_2B, + STREAMED_SOUND_CUTSCENE_LAW_2C, + STREAMED_SOUND_CUTSCENE_LAW_3, + STREAMED_SOUND_CUTSCENE_LAW_4, + STREAMED_SOUND_CUTSCENE_PHIL_1, + STREAMED_SOUND_CUTSCENE_PHIL_2, + STREAMED_SOUND_CUTSCENE_PORN_1, + STREAMED_SOUND_CUTSCENE_PORN_2, + STREAMED_SOUND_CUTSCENE_PORN_3, + STREAMED_SOUND_CUTSCENE_PORN_4, + STREAMED_SOUND_CUTSCENE_RESC_1A, + STREAMED_SOUND_CUTSCENE_ROK_1, + STREAMED_SOUND_CUTSCENE_ROK_2, + STREAMED_SOUND_CUTSCENE_ROK_3A, + STREAMED_SOUND_CUTSCENE_STRIPA, + STREAMED_SOUND_CUTSCENE_TAX_1, + STREAMED_SOUND_CUTSCENE_TEX_1, + STREAMED_SOUND_CUTSCENE_TEX_2, + STREAMED_SOUND_CUTSCENE_TEX_3, + STREAMED_SOUND_CUTSCENE_GLIGHT, + STREAMED_SOUND_CUTSCENE_FIST, + STREAMED_SOUND_CUTSCENE_ELBURRO1_PH1, + STREAMED_SOUND_CUTSCENE_ELBURRO2_PH2, + STREAMED_SOUND_MISSION_COMPLETED, + STREAMED_SOUND_MISSION_COMPLETED4, + STREAMED_SOUND_MISSION_MOBR1, + STREAMED_SOUND_MISSION_PAGER, + STREAMED_SOUND_MISSION_CARREV, + STREAMED_SOUND_MISSION_BIKEREV, + STREAMED_SOUND_MISSION_LIFTOP, + STREAMED_SOUND_MISSION_LIFTCL, + STREAMED_SOUND_MISSION_LIFTRUN, + STREAMED_SOUND_MISSION_LIFTBEL, + STREAMED_SOUND_MISSION_INLIFT, + STREAMED_SOUND_MISSION_SFX_01, + STREAMED_SOUND_MISSION_SFX_02, + STREAMED_SOUND_MISSION_CAMERAL, + STREAMED_SOUND_MISSION_CAMERAR, + STREAMED_SOUND_MISSION_CHEER1, + STREAMED_SOUND_MISSION_CHEER2, + STREAMED_SOUND_MISSION_CHEER3, + STREAMED_SOUND_MISSION_CHEER4, + STREAMED_SOUND_MISSION_OOH1, + STREAMED_SOUND_MISSION_OOH2, + STREAMED_SOUND_MISSION_RACE1, + STREAMED_SOUND_MISSION_RACE2, + STREAMED_SOUND_MISSION_RACE3, + STREAMED_SOUND_MISSION_RACE4, + STREAMED_SOUND_MISSION_RACE5, + STREAMED_SOUND_MISSION_RACE6, + STREAMED_SOUND_MISSION_RACE7, + STREAMED_SOUND_MISSION_RACE8, + STREAMED_SOUND_MISSION_RACE9, + STREAMED_SOUND_MISSION_RACE10, + STREAMED_SOUND_MISSION_RACE11, + STREAMED_SOUND_MISSION_RACE12, + STREAMED_SOUND_MISSION_RACE13, + STREAMED_SOUND_MISSION_RACE14, + STREAMED_SOUND_MISSION_RACE15, + STREAMED_SOUND_MISSION_HOT1, + STREAMED_SOUND_MISSION_HOT2, + STREAMED_SOUND_MISSION_HOT3, + STREAMED_SOUND_MISSION_HOT4, + STREAMED_SOUND_MISSION_HOT5, + STREAMED_SOUND_MISSION_HOT6, + STREAMED_SOUND_MISSION_HOT7, + STREAMED_SOUND_MISSION_HOT8, + STREAMED_SOUND_MISSION_HOT9, + STREAMED_SOUND_MISSION_HOT10, + STREAMED_SOUND_MISSION_HOT11, + STREAMED_SOUND_MISSION_HOT12, + STREAMED_SOUND_MISSION_HOT13, + STREAMED_SOUND_MISSION_HOT14, + STREAMED_SOUND_MISSION_HOT15, + STREAMED_SOUND_MISSION_LANSTP1, + STREAMED_SOUND_MISSION_LANSTP2, + STREAMED_SOUND_MISSION_LANAMU1, + STREAMED_SOUND_MISSION_LANAMU2, + STREAMED_SOUND_MISSION_AIRHORNL, + STREAMED_SOUND_MISSION_AIRHORNR, + STREAMED_SOUND_MISSION_SNIPSCRL, + STREAMED_SOUND_MISSION_SNIPSHORT, + STREAMED_SOUND_MISSION_BLOWROOF, + STREAMED_SOUND_MISSION_ASS_1, + STREAMED_SOUND_MISSION_ASS_2, + STREAMED_SOUND_MISSION_ASS_3, + STREAMED_SOUND_MISSION_ASS_4, + STREAMED_SOUND_MISSION_ASS_5, + STREAMED_SOUND_MISSION_ASS_6, + STREAMED_SOUND_MISSION_ASS_7, + STREAMED_SOUND_MISSION_ASS_8, + STREAMED_SOUND_MISSION_ASS_9, + STREAMED_SOUND_MISSION_ASS_10, + STREAMED_SOUND_MISSION_ASS_11, + STREAMED_SOUND_MISSION_ASS_12, + STREAMED_SOUND_MISSION_ASS_13, + STREAMED_SOUND_MISSION_ASS_14, + STREAMED_SOUND_MISSION_BIKE1_1, + STREAMED_SOUND_MISSION_BIKE1_2, + STREAMED_SOUND_MISSION_BIKE1_3, + STREAMED_SOUND_MISSION_BNK1_1, + STREAMED_SOUND_MISSION_BNK1_2, + STREAMED_SOUND_MISSION_BNK1_3, + STREAMED_SOUND_MISSION_BNK1_4, + STREAMED_SOUND_MISSION_BNK1_5, + STREAMED_SOUND_MISSION_BNK1_6, + STREAMED_SOUND_MISSION_BNK1_7, + STREAMED_SOUND_MISSION_BNK1_8, + STREAMED_SOUND_MISSION_BNK1_10, + STREAMED_SOUND_MISSION_BNK1_11, + STREAMED_SOUND_MISSION_BNK1_12, + STREAMED_SOUND_MISSION_BNK1_13, + STREAMED_SOUND_MISSION_BNK1_14, + STREAMED_SOUND_MISSION_BNK2_1, + STREAMED_SOUND_MISSION_BNK2_2, + STREAMED_SOUND_MISSION_BNK2_3, + STREAMED_SOUND_MISSION_BNK2_4, + STREAMED_SOUND_MISSION_BNK2_5, + STREAMED_SOUND_MISSION_BNK2_6, + STREAMED_SOUND_MISSION_BNK2_7, + STREAMED_SOUND_MISSION_BNK2_8, + STREAMED_SOUND_MISSION_BNK2_9, + STREAMED_SOUND_MISSION_BNK3_1, + STREAMED_SOUND_MISSION_BNK3_2, + STREAMED_SOUND_MISSION_BNK3_3A, + STREAMED_SOUND_MISSION_BNK3_3B, + STREAMED_SOUND_MISSION_BNK3_3C, + STREAMED_SOUND_MISSION_BNK3_4A, + STREAMED_SOUND_MISSION_BNK3_4B, + STREAMED_SOUND_MISSION_BNK3_4C, + STREAMED_SOUND_MISSION_BNK4_1, + STREAMED_SOUND_MISSION_BNK4_2, + STREAMED_SOUND_MISSION_BNK4_3A, + STREAMED_SOUND_MISSION_BNK4_3B, + STREAMED_SOUND_MISSION_BNK4_3C, + STREAMED_SOUND_MISSION_BNK4_3D, + STREAMED_SOUND_MISSION_BNK4_3E, + STREAMED_SOUND_MISSION_BNK4_3F, + STREAMED_SOUND_MISSION_BNK4_3G, + STREAMED_SOUND_MISSION_BNK4_3H, + STREAMED_SOUND_MISSION_BNK4_3I, + STREAMED_SOUND_MISSION_BNK4_3J, + STREAMED_SOUND_MISSION_BNK4_3K, + STREAMED_SOUND_MISSION_BNK4_3M, + STREAMED_SOUND_MISSION_BNK4_3O, + STREAMED_SOUND_MISSION_BNK4_3P, + STREAMED_SOUND_MISSION_BNK4_3Q, + STREAMED_SOUND_MISSION_BNK4_3R, + STREAMED_SOUND_MISSION_BNK4_3S, + STREAMED_SOUND_MISSION_BNK4_3T, + STREAMED_SOUND_MISSION_BNK4_3U, + STREAMED_SOUND_MISSION_BNK4_3V, + STREAMED_SOUND_MISSION_BNK4_4A, + STREAMED_SOUND_MISSION_BNK4_4B, + STREAMED_SOUND_MISSION_BNK4_5, + STREAMED_SOUND_MISSION_BNK4_6, + STREAMED_SOUND_MISSION_BNK4_7, + STREAMED_SOUND_MISSION_BNK4_8, + STREAMED_SOUND_MISSION_BNK4_9, + STREAMED_SOUND_MISSION_BNK4_10, + STREAMED_SOUND_MISSION_BNK4_11, + STREAMED_SOUND_MISSION_BK4_12A, + STREAMED_SOUND_MISSION_BK4_12B, + STREAMED_SOUND_MISSION_BK4_12C, + STREAMED_SOUND_MISSION_BNK4_13, + STREAMED_SOUND_MISSION_BK4_14A, + STREAMED_SOUND_MISSION_BK4_14B, + STREAMED_SOUND_MISSION_BNK4_15, + STREAMED_SOUND_MISSION_BNK4_16, + STREAMED_SOUND_MISSION_BNK4_17, + STREAMED_SOUND_MISSION_BNK4_18, + STREAMED_SOUND_MISSION_BK4_19A, + STREAMED_SOUND_MISSION_BK4_19B, + STREAMED_SOUND_MISSION_BK4_20A, + STREAMED_SOUND_MISSION_BK4_20B, + STREAMED_SOUND_MISSION_BNK4_21, + STREAMED_SOUND_MISSION_BNK422A, + STREAMED_SOUND_MISSION_BNK422B, + STREAMED_SOUND_MISSION_BK4_23A, + STREAMED_SOUND_MISSION_BK4_23B, + STREAMED_SOUND_MISSION_BK4_23C, + STREAMED_SOUND_MISSION_BK4_23D, + STREAMED_SOUND_MISSION_BK4_24A, + STREAMED_SOUND_MISSION_BK4_24B, + STREAMED_SOUND_MISSION_BNK4_25, + STREAMED_SOUND_MISSION_BNK4_26, + STREAMED_SOUND_MISSION_BNK4_27, + STREAMED_SOUND_MISSION_BNK4_28, + STREAMED_SOUND_MISSION_BNK4_29, + STREAMED_SOUND_MISSION_BNK4_30, + STREAMED_SOUND_MISSION_BK4_31A, + STREAMED_SOUND_MISSION_BK4_31B, + STREAMED_SOUND_MISSION_BNK4_32, + STREAMED_SOUND_MISSION_BK4_34A, + STREAMED_SOUND_MISSION_BK4_34B, + STREAMED_SOUND_MISSION_BK4_35A, + STREAMED_SOUND_MISSION_BK4_35B, + STREAMED_SOUND_MISSION_BNK4_36, + STREAMED_SOUND_MISSION_BNK4_37, + STREAMED_SOUND_MISSION_BNK4_38, + STREAMED_SOUND_MISSION_BNK4_39, + STREAMED_SOUND_MISSION_BK4_40A, + STREAMED_SOUND_MISSION_BK4_40B, + STREAMED_SOUND_MISSION_BNK4_41, + STREAMED_SOUND_MISSION_BNK4_42, + STREAMED_SOUND_MISSION_BNK4_43, + STREAMED_SOUND_MISSION_BNK4_44, + STREAMED_SOUND_MISSION_BNK4_45, + STREAMED_SOUND_MISSION_BNK4_46, + STREAMED_SOUND_MISSION_BNK4_47, + STREAMED_SOUND_MISSION_BNK4_48, + STREAMED_SOUND_MISSION_BNK4_49, + STREAMED_SOUND_MISSION_BNK450A, + STREAMED_SOUND_MISSION_BNK450B, + STREAMED_SOUND_MISSION_BNK4_51, + STREAMED_SOUND_MISSION_BNK4_94, + STREAMED_SOUND_MISSION_BNK4_95, + STREAMED_SOUND_MISSION_BNK4_96, + STREAMED_SOUND_MISSION_BNK4_97, + STREAMED_SOUND_MISSION_BNK4_98, + STREAMED_SOUND_MISSION_BNK4_99, + STREAMED_SOUND_MISSION_BUD1_1, + STREAMED_SOUND_MISSION_BUD1_2, + STREAMED_SOUND_MISSION_BUD1_3, + STREAMED_SOUND_MISSION_BUD1_4, + STREAMED_SOUND_MISSION_BUD1_5, + STREAMED_SOUND_MISSION_BUD1_9, + STREAMED_SOUND_MISSION_BUD1_10, + STREAMED_SOUND_MISSION_BUD2_1, + STREAMED_SOUND_MISSION_BUD2_2, + STREAMED_SOUND_MISSION_BUD2_3, + STREAMED_SOUND_MISSION_BUD2_4, + STREAMED_SOUND_MISSION_BUD2_5, + STREAMED_SOUND_MISSION_BUD2_6, + STREAMED_SOUND_MISSION_BUD2_7, + STREAMED_SOUND_MISSION_BUD3_1, + STREAMED_SOUND_MISSION_BUD3_1A, + STREAMED_SOUND_MISSION_BUD3_1B, + STREAMED_SOUND_MISSION_BUD3_1C, + STREAMED_SOUND_MISSION_BUD3_2, + STREAMED_SOUND_MISSION_BUD3_3, + STREAMED_SOUND_MISSION_BUD3_4, + STREAMED_SOUND_MISSION_BUD3_5, + STREAMED_SOUND_MISSION_BUD3_6, + STREAMED_SOUND_MISSION_BUD3_7, + STREAMED_SOUND_MISSION_BUD3_8A, + STREAMED_SOUND_MISSION_BUD3_8B, + STREAMED_SOUND_MISSION_BUD3_8C, + STREAMED_SOUND_MISSION_BUD3_9A, + STREAMED_SOUND_MISSION_BUD3_9B, + STREAMED_SOUND_MISSION_BUD3_9C, + STREAMED_SOUND_MISSION_CAP1_2, + STREAMED_SOUND_MISSION_CAP1_3, + STREAMED_SOUND_MISSION_CAP1_4, + STREAMED_SOUND_MISSION_CAP1_5, + STREAMED_SOUND_MISSION_CAP1_6, + STREAMED_SOUND_MISSION_CAP1_7, + STREAMED_SOUND_MISSION_CAP1_8, + STREAMED_SOUND_MISSION_CAP1_9, + STREAMED_SOUND_MISSION_CAP1_10, + STREAMED_SOUND_MISSION_CAP1_11, + STREAMED_SOUND_MISSION_CAP1_12, + STREAMED_SOUND_MISSION_CNT1_1, + STREAMED_SOUND_MISSION_CNT1_2, + STREAMED_SOUND_MISSION_CNT1_3, + STREAMED_SOUND_MISSION_CNT1_4, + STREAMED_SOUND_MISSION_CNT1_5, + STREAMED_SOUND_MISSION_CNT2_1, + STREAMED_SOUND_MISSION_CNT2_2, + STREAMED_SOUND_MISSION_CNT2_3, + STREAMED_SOUND_MISSION_CNT2_4, + STREAMED_SOUND_MISSION_COK1_1, + STREAMED_SOUND_MISSION_COK1_2, + STREAMED_SOUND_MISSION_COK1_3, + STREAMED_SOUND_MISSION_COK1_4, + STREAMED_SOUND_MISSION_COK1_5, + STREAMED_SOUND_MISSION_COK1_6, + STREAMED_SOUND_MISSION_COK2_1, + STREAMED_SOUND_MISSION_COK2_2, + STREAMED_SOUND_MISSION_COK2_3, + STREAMED_SOUND_MISSION_COK2_4, + STREAMED_SOUND_MISSION_COK2_5, + STREAMED_SOUND_MISSION_COK2_6, + STREAMED_SOUND_MISSION_COK2_7A, + STREAMED_SOUND_MISSION_COK2_7B, + STREAMED_SOUND_MISSION_COK2_7C, + STREAMED_SOUND_MISSION_COK2_8A, + STREAMED_SOUND_MISSION_COK2_8B, + STREAMED_SOUND_MISSION_COK2_8C, + STREAMED_SOUND_MISSION_COK2_8D, + STREAMED_SOUND_MISSION_COK2_9, + STREAMED_SOUND_MISSION_COK210A, + STREAMED_SOUND_MISSION_COK210B, + STREAMED_SOUND_MISSION_COK210C, + STREAMED_SOUND_MISSION_COK212A, + STREAMED_SOUND_MISSION_COK212B, + STREAMED_SOUND_MISSION_COK2_13, + STREAMED_SOUND_MISSION_COK2_14, + STREAMED_SOUND_MISSION_COK2_15, + STREAMED_SOUND_MISSION_COK2_16, + STREAMED_SOUND_MISSION_COK2_20, + STREAMED_SOUND_MISSION_COK2_21, + STREAMED_SOUND_MISSION_COK2_22, + STREAMED_SOUND_MISSION_COK3_1, + STREAMED_SOUND_MISSION_COK3_2, + STREAMED_SOUND_MISSION_COK3_3, + STREAMED_SOUND_MISSION_COK3_4, + STREAMED_SOUND_MISSION_COK4_1, + STREAMED_SOUND_MISSION_COK4_2, + STREAMED_SOUND_MISSION_COK4_3, + STREAMED_SOUND_MISSION_COK4_4, + STREAMED_SOUND_MISSION_COK4_5, + STREAMED_SOUND_MISSION_COK4_6, + STREAMED_SOUND_MISSION_COK4_7, + STREAMED_SOUND_MISSION_COK4_8, + STREAMED_SOUND_MISSION_COK4_9, + STREAMED_SOUND_MISSION_COK4_9A, + STREAMED_SOUND_MISSION_COK4_10, + STREAMED_SOUND_MISSION_COK4_11, + STREAMED_SOUND_MISSION_COK4_12, + STREAMED_SOUND_MISSION_COK4_13, + STREAMED_SOUND_MISSION_COK4_14, + STREAMED_SOUND_MISSION_COK4_15, + STREAMED_SOUND_MISSION_COK4_16, + STREAMED_SOUND_MISSION_COK4_17, + STREAMED_SOUND_MISSION_COK4_18, + STREAMED_SOUND_MISSION_COK4_19, + STREAMED_SOUND_MISSION_COK4_20, + STREAMED_SOUND_MISSION_COK4_21, + STREAMED_SOUND_MISSION_COK4_22, + STREAMED_SOUND_MISSION_COK4_23, + STREAMED_SOUND_MISSION_COK4_24, + STREAMED_SOUND_MISSION_COK4_25, + STREAMED_SOUND_MISSION_COK4_26, + STREAMED_SOUND_MISSION_COK4_27, + STREAMED_SOUND_MISSION_COL1_1, + STREAMED_SOUND_MISSION_COL1_2, + STREAMED_SOUND_MISSION_COL1_3, + STREAMED_SOUND_MISSION_COL1_4, + STREAMED_SOUND_MISSION_COL1_5, + STREAMED_SOUND_MISSION_COL1_6, + STREAMED_SOUND_MISSION_COL1_7, + STREAMED_SOUND_MISSION_COL1_8, + STREAMED_SOUND_MISSION_COL2_1, + STREAMED_SOUND_MISSION_COL2_2, + STREAMED_SOUND_MISSION_COL2_3, + STREAMED_SOUND_MISSION_COL2_4, + STREAMED_SOUND_MISSION_COL2_5, + STREAMED_SOUND_MISSION_COL2_6A, + STREAMED_SOUND_MISSION_COL2_7, + STREAMED_SOUND_MISSION_COL2_8, + STREAMED_SOUND_MISSION_COL2_9, + STREAMED_SOUND_MISSION_COL2_10, + STREAMED_SOUND_MISSION_COL2_11, + STREAMED_SOUND_MISSION_COL2_12, + STREAMED_SOUND_MISSION_COL2_13, + STREAMED_SOUND_MISSION_COL2_14, + STREAMED_SOUND_MISSION_COL2_15, + STREAMED_SOUND_MISSION_COL2_16, + STREAMED_SOUND_MISSION_COL3_1, + STREAMED_SOUND_MISSION_COL3_2, + STREAMED_SOUND_MISSION_COL3_2A, + STREAMED_SOUND_MISSION_COL3_2B, + STREAMED_SOUND_MISSION_COL3_3, + STREAMED_SOUND_MISSION_COL3_4, + STREAMED_SOUND_MISSION_COL3_5, + STREAMED_SOUND_MISSION_COL3_6, + STREAMED_SOUND_MISSION_COL3_7, + STREAMED_SOUND_MISSION_COL3_8, + STREAMED_SOUND_MISSION_COL3_9, + STREAMED_SOUND_MISSION_COL3_10, + STREAMED_SOUND_MISSION_COL3_11, + STREAMED_SOUND_MISSION_COL3_12, + STREAMED_SOUND_MISSION_COL3_13, + STREAMED_SOUND_MISSION_COL3_14, + STREAMED_SOUND_MISSION_COL3_15, + STREAMED_SOUND_MISSION_COL3_16, + STREAMED_SOUND_MISSION_COL3_17, + STREAMED_SOUND_MISSION_COL3_18, + STREAMED_SOUND_MISSION_COL3_19, + STREAMED_SOUND_MISSION_COL3_20, + STREAMED_SOUND_MISSION_COL3_21, + STREAMED_SOUND_MISSION_COL3_23, + STREAMED_SOUND_MISSION_COL3_24, + STREAMED_SOUND_MISSION_COL3_25, + STREAMED_SOUND_MISSION_COL4_1, + STREAMED_SOUND_MISSION_COL4_2, + STREAMED_SOUND_MISSION_COL4_3, + STREAMED_SOUND_MISSION_COL4_4, + STREAMED_SOUND_MISSION_COL4_5, + STREAMED_SOUND_MISSION_COL4_6, + STREAMED_SOUND_MISSION_COL4_7, + STREAMED_SOUND_MISSION_COL4_8, + STREAMED_SOUND_MISSION_COL4_9, + STREAMED_SOUND_MISSION_COL4_10, + STREAMED_SOUND_MISSION_COL4_11, + STREAMED_SOUND_MISSION_COL4_12, + STREAMED_SOUND_MISSION_COL4_13, + STREAMED_SOUND_MISSION_COL4_14, + STREAMED_SOUND_MISSION_COL4_15, + STREAMED_SOUND_MISSION_COL4_16, + STREAMED_SOUND_MISSION_COL4_17, + STREAMED_SOUND_MISSION_COL4_18, + STREAMED_SOUND_MISSION_COL4_19, + STREAMED_SOUND_MISSION_COL4_20, + STREAMED_SOUND_MISSION_COL4_21, + STREAMED_SOUND_MISSION_COL4_22, + STREAMED_SOUND_MISSION_COL4_23, + STREAMED_SOUND_MISSION_COL4_24, + STREAMED_SOUND_MISSION_COL4_25, + STREAMED_SOUND_MISSION_COL4_26, + STREAMED_SOUND_MISSION_COL5_1, + STREAMED_SOUND_MISSION_COL5_2, + STREAMED_SOUND_MISSION_COL5_3, + STREAMED_SOUND_MISSION_COL5_4, + STREAMED_SOUND_MISSION_COL5_5, + STREAMED_SOUND_MISSION_COL5_6, + STREAMED_SOUND_MISSION_COL5_7, + STREAMED_SOUND_MISSION_COL5_8, + STREAMED_SOUND_MISSION_COL5_9, + STREAMED_SOUND_MISSION_COL5_10, + STREAMED_SOUND_MISSION_COL5_11, + STREAMED_SOUND_MISSION_COL5_12, + STREAMED_SOUND_MISSION_COL5_13, + STREAMED_SOUND_MISSION_COL5_14, + STREAMED_SOUND_MISSION_COL5_15, + STREAMED_SOUND_MISSION_COL5_16, + STREAMED_SOUND_MISSION_COL5_17, + STREAMED_SOUND_MISSION_COL5_18, + STREAMED_SOUND_MISSION_COL5_19, + STREAMED_SOUND_MISSION_COL5_20, + STREAMED_SOUND_MISSION_COL5_21, + STREAMED_SOUND_MISSION_COL5_22, + STREAMED_SOUND_MISSION_CUB1_1, + STREAMED_SOUND_MISSION_CUB1_2, + STREAMED_SOUND_MISSION_CUB1_3, + STREAMED_SOUND_MISSION_CUB1_4, + STREAMED_SOUND_MISSION_CUB1_5, + STREAMED_SOUND_MISSION_CUB1_6, + STREAMED_SOUND_MISSION_CUB1_7, + STREAMED_SOUND_MISSION_CUB1_8, + STREAMED_SOUND_MISSION_CUB1_9, + STREAMED_SOUND_MISSION_CUB1_10, + STREAMED_SOUND_MISSION_CUB2_1, + STREAMED_SOUND_MISSION_CUB2_2, + STREAMED_SOUND_MISSION_CUB2_3A, + STREAMED_SOUND_MISSION_CUB2_3B, + STREAMED_SOUND_MISSION_CUB2_3C, + STREAMED_SOUND_MISSION_CUB2_4A, + STREAMED_SOUND_MISSION_CUB2_5, + STREAMED_SOUND_MISSION_CUB2_6, + STREAMED_SOUND_MISSION_CUB2_7, + STREAMED_SOUND_MISSION_CUB2_8, + STREAMED_SOUND_MISSION_CUB2_9, + STREAMED_SOUND_MISSION_CUB2_10, + STREAMED_SOUND_MISSION_CUB2_11, + STREAMED_SOUND_MISSION_CUB3_1, + STREAMED_SOUND_MISSION_CUB3_2, + STREAMED_SOUND_MISSION_CUB3_3, + STREAMED_SOUND_MISSION_CUB3_4, + STREAMED_SOUND_MISSION_CUB4_1, + STREAMED_SOUND_MISSION_CUB4_2, + STREAMED_SOUND_MISSION_CUB4_3, + STREAMED_SOUND_MISSION_CUB4_4, + STREAMED_SOUND_MISSION_CUB4_5, + STREAMED_SOUND_MISSION_CUB4_5A, + STREAMED_SOUND_MISSION_CUB4_6, + STREAMED_SOUND_MISSION_CUB4_7, + STREAMED_SOUND_MISSION_CUB4_8, + STREAMED_SOUND_MISSION_CUB4_9, + STREAMED_SOUND_MISSION_CUB4_10, + STREAMED_SOUND_MISSION_CUB4_11, + STREAMED_SOUND_MISSION_CUB4_12, + STREAMED_SOUND_MISSION_CUB4_13, + STREAMED_SOUND_MISSION_CUB4_14, + STREAMED_SOUND_MISSION_CUB4_15, + STREAMED_SOUND_MISSION_CUB4_16, + STREAMED_SOUND_MISSION_GOLF_1, + STREAMED_SOUND_MISSION_GOLF_2, + STREAMED_SOUND_MISSION_GOLF_3, + STREAMED_SOUND_MISSION_BAR_1, + STREAMED_SOUND_MISSION_BAR_2, + STREAMED_SOUND_MISSION_BAR_3, + STREAMED_SOUND_MISSION_BAR_4, + STREAMED_SOUND_MISSION_BAR_5, + STREAMED_SOUND_MISSION_BAR_6, + STREAMED_SOUND_MISSION_BAR_7, + STREAMED_SOUND_MISSION_BAR_8, + STREAMED_SOUND_MISSION_STRIP_1, + STREAMED_SOUND_MISSION_STRIP_2, + STREAMED_SOUND_MISSION_STRIP_3, + STREAMED_SOUND_MISSION_STRIP_4, + STREAMED_SOUND_MISSION_STRIP_5, + STREAMED_SOUND_MISSION_STRIP_6, + STREAMED_SOUND_MISSION_STRIP_7, + STREAMED_SOUND_MISSION_STRIP_8, + STREAMED_SOUND_MISSION_STRIP_9, + STREAMED_SOUND_MISSION_STAR_1, + STREAMED_SOUND_MISSION_STAR_2, + STREAMED_SOUND_MISSION_STAR_3, + STREAMED_SOUND_MISSION_STAR_4, + STREAMED_SOUND_MISSION_FIN_1A, + STREAMED_SOUND_MISSION_FIN_1B, + STREAMED_SOUND_MISSION_FIN_1C, + STREAMED_SOUND_MISSION_FIN_2B, + STREAMED_SOUND_MISSION_FIN_2C, + STREAMED_SOUND_MISSION_FIN_3, + STREAMED_SOUND_MISSION_FIN_4, + STREAMED_SOUND_MISSION_FIN_5, + STREAMED_SOUND_MISSION_FIN_6, + STREAMED_SOUND_MISSION_FIN_10, + STREAMED_SOUND_MISSION_FIN_11A, + STREAMED_SOUND_MISSION_FIN_11B, + STREAMED_SOUND_MISSION_FIN_12A, + STREAMED_SOUND_MISSION_FIN_12B, + STREAMED_SOUND_MISSION_FIN_12C, + STREAMED_SOUND_MISSION_FIN_13, + STREAMED_SOUND_MISSION_FINKILL, + STREAMED_SOUND_MISSION_LAW1_1, + STREAMED_SOUND_MISSION_LAW1_2, + STREAMED_SOUND_MISSION_LAW1_3, + STREAMED_SOUND_MISSION_LAW1_4, + STREAMED_SOUND_MISSION_LAW1_5, + STREAMED_SOUND_MISSION_LAW1_6, + STREAMED_SOUND_MISSION_LAW1_7, + STREAMED_SOUND_MISSION_LAW1_8, + STREAMED_SOUND_MISSION_LAW1_9, + STREAMED_SOUND_MISSION_LAW1_10, + STREAMED_SOUND_MISSION_LAW2_1, + STREAMED_SOUND_MISSION_LAW2_2, + STREAMED_SOUND_MISSION_LAW2_3, + STREAMED_SOUND_MISSION_LAW2_4, + STREAMED_SOUND_MISSION_LAW2_5, + STREAMED_SOUND_MISSION_LAW2_6, + STREAMED_SOUND_MISSION_LAW2_7, + STREAMED_SOUND_MISSION_LAW2_8, + STREAMED_SOUND_MISSION_LAW2_9, + STREAMED_SOUND_MISSION_LAW2_10, + STREAMED_SOUND_MISSION_LAW3_1, + STREAMED_SOUND_MISSION_LAW3_2, + STREAMED_SOUND_MISSION_LAW3_3, + STREAMED_SOUND_MISSION_LAW3_4, + STREAMED_SOUND_MISSION_LAW3_5, + STREAMED_SOUND_MISSION_LAW3_6, + STREAMED_SOUND_MISSION_LAW3_10, + STREAMED_SOUND_MISSION_LAW3_11, + STREAMED_SOUND_MISSION_LAW3_12, + STREAMED_SOUND_MISSION_LAW3_13, + STREAMED_SOUND_MISSION_LAW3_14, + STREAMED_SOUND_MISSION_LAW3_16, + STREAMED_SOUND_MISSION_LAW3_17, + STREAMED_SOUND_MISSION_LAW3_18, + STREAMED_SOUND_MISSION_LAW3_19, + STREAMED_SOUND_MISSION_LAW3_20, + STREAMED_SOUND_MISSION_LAW3_21, + STREAMED_SOUND_MISSION_LAW3_22, + STREAMED_SOUND_MISSION_LAW3_23, + STREAMED_SOUND_MISSION_LAW3_24, + STREAMED_SOUND_MISSION_LAW3_25, + STREAMED_SOUND_MISSION_LAW4_1A, + STREAMED_SOUND_MISSION_LAW4_1B, + STREAMED_SOUND_MISSION_LAW4_1C, + STREAMED_SOUND_MISSION_LAW4_1D, + STREAMED_SOUND_MISSION_LAW4_10, + STREAMED_SOUND_MISSION_LAW4_3, + STREAMED_SOUND_MISSION_LAW4_4, + STREAMED_SOUND_MISSION_LAW4_5, + STREAMED_SOUND_MISSION_LAW4_6, + STREAMED_SOUND_MISSION_LAW4_7, + STREAMED_SOUND_MISSION_LAW4_8, + STREAMED_SOUND_MISSION_LAW4_9, + STREAMED_SOUND_MISSION_PHIL1_2, + STREAMED_SOUND_MISSION_PHIL1_3, + STREAMED_SOUND_MISSION_PHIL2_1, + STREAMED_SOUND_MISSION_PHIL2_2, + STREAMED_SOUND_MISSION_PHIL2_3, + STREAMED_SOUND_MISSION_PHIL2_4, + STREAMED_SOUND_MISSION_PHIL2_5, + STREAMED_SOUND_MISSION_PHIL2_6, + STREAMED_SOUND_MISSION_PHIL2_7, + STREAMED_SOUND_MISSION_PHIL2_8, + STREAMED_SOUND_MISSION_PHIL2_9, + STREAMED_SOUND_MISSION_PHIL210, + STREAMED_SOUND_MISSION_PHIL211, + STREAMED_SOUND_MISSION_PORN1_1, + STREAMED_SOUND_MISSION_PORN1_2, + STREAMED_SOUND_MISSION_PORN1_3, + STREAMED_SOUND_MISSION_PRN1_3A, + STREAMED_SOUND_MISSION_PORN1_4, + STREAMED_SOUND_MISSION_PORN1_5, + STREAMED_SOUND_MISSION_PORN1_6, + STREAMED_SOUND_MISSION_PORN1_7, + STREAMED_SOUND_MISSION_PORN1_8, + STREAMED_SOUND_MISSION_PORN1_9, + STREAMED_SOUND_MISSION_PRN1_10, + STREAMED_SOUND_MISSION_PRN1_11, + STREAMED_SOUND_MISSION_PRN1_12, + STREAMED_SOUND_MISSION_PRN1_13, + STREAMED_SOUND_MISSION_PRN1_14, + STREAMED_SOUND_MISSION_PRN1_15, + STREAMED_SOUND_MISSION_PRN1_16, + STREAMED_SOUND_MISSION_PRN1_17, + STREAMED_SOUND_MISSION_PRN1_18, + STREAMED_SOUND_MISSION_PRN1_19, + STREAMED_SOUND_MISSION_PRN1_20, + STREAMED_SOUND_MISSION_PRN1_21, + STREAMED_SOUND_MISSION_PORN3_1, + STREAMED_SOUND_MISSION_PORN3_2, + STREAMED_SOUND_MISSION_PORN3_3, + STREAMED_SOUND_MISSION_PORN3_4, + STREAMED_SOUND_MISSION_PSYCH_1, + STREAMED_SOUND_MISSION_PSYCH_2, + STREAMED_SOUND_MISSION_ROK2_01, + STREAMED_SOUND_MISSION_ROK3_1, + STREAMED_SOUND_MISSION_ROK3_2, + STREAMED_SOUND_MISSION_ROK3_3, + STREAMED_SOUND_MISSION_ROK3_4, + STREAMED_SOUND_MISSION_ROK3_5, + STREAMED_SOUND_MISSION_ROK3_6, + STREAMED_SOUND_MISSION_ROK3_7, + STREAMED_SOUND_MISSION_ROK3_8, + STREAMED_SOUND_MISSION_ROK3_9, + STREAMED_SOUND_MISSION_ROK3_10, + STREAMED_SOUND_MISSION_ROK3_11, + STREAMED_SOUND_MISSION_ROK3_12, + STREAMED_SOUND_MISSION_ROK3_13, + STREAMED_SOUND_MISSION_ROK3_14, + STREAMED_SOUND_MISSION_ROK3_15, + STREAMED_SOUND_MISSION_ROK3_16, + STREAMED_SOUND_MISSION_ROK3_17, + STREAMED_SOUND_MISSION_ROK3_18, + STREAMED_SOUND_MISSION_ROK3_19, + STREAMED_SOUND_MISSION_ROK3_20, + STREAMED_SOUND_MISSION_ROK3_21, + STREAMED_SOUND_MISSION_ROK3_22, + STREAMED_SOUND_MISSION_ROK3_23, + STREAMED_SOUND_MISSION_ROK3_24, + STREAMED_SOUND_MISSION_ROK3_25, + STREAMED_SOUND_MISSION_ROK3_26, + STREAMED_SOUND_MISSION_ROK3_27, + STREAMED_SOUND_MISSION_ROK3_62, + STREAMED_SOUND_MISSION_ROK3_63, + STREAMED_SOUND_MISSION_ROK3_64, + STREAMED_SOUND_MISSION_ROK3_65, + STREAMED_SOUND_MISSION_ROK3_66, + STREAMED_SOUND_MISSION_ROK3_67, + STREAMED_SOUND_MISSION_ROK3_68, + STREAMED_SOUND_MISSION_ROK3_69, + STREAMED_SOUND_MISSION_ROK3_70, + STREAMED_SOUND_MISSION_ROK3_71, + STREAMED_SOUND_MISSION_ROK3_73, + STREAMED_SOUND_MISSION_RESC_1, + STREAMED_SOUND_MISSION_RESC_2, + STREAMED_SOUND_MISSION_RESC_3, + STREAMED_SOUND_MISSION_RESC_4, + STREAMED_SOUND_MISSION_RESC_5, + STREAMED_SOUND_MISSION_RESC_6, + STREAMED_SOUND_MISSION_RESC_7, + STREAMED_SOUND_MISSION_RESC_8, + STREAMED_SOUND_MISSION_RESC_9, + STREAMED_SOUND_MISSION_RESC_10, + STREAMED_SOUND_MISSION_ROK1_1A, + STREAMED_SOUND_MISSION_ROK1_1B, + STREAMED_SOUND_MISSION_ROK1_5, + STREAMED_SOUND_MISSION_ROK1_6, + STREAMED_SOUND_MISSION_ROK1_7, + STREAMED_SOUND_MISSION_ROK1_8, + STREAMED_SOUND_MISSION_ROK1_9, + STREAMED_SOUND_MISSION_TAX1_1, + STREAMED_SOUND_MISSION_TAX1_2, + STREAMED_SOUND_MISSION_TAX1_3, + STREAMED_SOUND_MISSION_TAX1_4, + STREAMED_SOUND_MISSION_TAX1_5, + STREAMED_SOUND_MISSION_TAX2_1, + STREAMED_SOUND_MISSION_TAX2_2, + STREAMED_SOUND_MISSION_TAX2_3, + STREAMED_SOUND_MISSION_TAX2_4, + STREAMED_SOUND_MISSION_TAX2_5, + STREAMED_SOUND_MISSION_TAX2_6, + STREAMED_SOUND_MISSION_TAX2_7, + STREAMED_SOUND_MISSION_TAX3_1, + STREAMED_SOUND_MISSION_TAX3_2, + STREAMED_SOUND_MISSION_TAX3_3, + STREAMED_SOUND_MISSION_TAX3_4, + STREAMED_SOUND_MISSION_TAX3_5, + STREAMED_SOUND_MISSION_TEX1_1, + STREAMED_SOUND_MISSION_TEX1_2, + STREAMED_SOUND_MISSION_TEX1_3, + STREAMED_SOUND_MISSION_TEX1_4, + STREAMED_SOUND_MISSION_TEX1_5, + STREAMED_SOUND_MISSION_TEX1_6, + STREAMED_SOUND_MISSION_TEX2_1, + STREAMED_SOUND_MISSION_TEX3_1, + STREAMED_SOUND_MISSION_TEX3_2, + STREAMED_SOUND_MISSION_TEX3_3, + STREAMED_SOUND_MISSION_TEX3_4, + STREAMED_SOUND_MISSION_TEX3_5, + STREAMED_SOUND_MISSION_TEX3_6, + STREAMED_SOUND_MISSION_TEX3_7, + STREAMED_SOUND_MISSION_TEX3_8, + STREAMED_SOUND_MISSION_HAT_1A, + STREAMED_SOUND_MISSION_INTRO1, + STREAMED_SOUND_MISSION_INTRO2, + STREAMED_SOUND_MISSION_INTRO3, + STREAMED_SOUND_MISSION_INTRO4, + STREAMED_SOUND_MISSION_MOB_01A, + STREAMED_SOUND_MISSION_MOB_01B, + STREAMED_SOUND_MISSION_MOB_01C, + STREAMED_SOUND_MISSION_MOB_02A, + STREAMED_SOUND_MISSION_MOB_02B, + STREAMED_SOUND_MISSION_MOB_02C, + STREAMED_SOUND_MISSION_MOB_03A, + STREAMED_SOUND_MISSION_MOB_03B, + STREAMED_SOUND_MISSION_MOB_03C, + STREAMED_SOUND_MISSION_MOB_03D, + STREAMED_SOUND_MISSION_MOB_03E, + STREAMED_SOUND_MISSION_SHARK_1, + STREAMED_SOUND_MISSION_SHARK_2, + STREAMED_SOUND_MISSION_SHARK_3, + STREAMED_SOUND_MISSION_SHARK_4, + STREAMED_SOUND_MISSION_SHARK_5, + STREAMED_SOUND_MISSION_MOB_04A, + STREAMED_SOUND_MISSION_MOB_04B, + STREAMED_SOUND_MISSION_MOB_04C, + STREAMED_SOUND_MISSION_MOB_04D, + STREAMED_SOUND_MISSION_MOB_05A, + STREAMED_SOUND_MISSION_MOB_05B, + STREAMED_SOUND_MISSION_MOB_05C, + STREAMED_SOUND_MISSION_MOB_05D, + STREAMED_SOUND_MISSION_MOB_06A, + STREAMED_SOUND_MISSION_MOB_06B, + STREAMED_SOUND_MISSION_MOB_06C, + STREAMED_SOUND_MISSION_MOB_07A, + STREAMED_SOUND_MISSION_MOB_07B, + STREAMED_SOUND_MISSION_MOB_08A, + STREAMED_SOUND_MISSION_MOB_08B, + STREAMED_SOUND_MISSION_MOB_08C, + STREAMED_SOUND_MISSION_MOB_08D, + STREAMED_SOUND_MISSION_MOB_08E, + STREAMED_SOUND_MISSION_MOB_08F, + STREAMED_SOUND_MISSION_MOB_08G, + STREAMED_SOUND_MISSION_MOB_09A, + STREAMED_SOUND_MISSION_MOB_09B, + STREAMED_SOUND_MISSION_MOB_09C, + STREAMED_SOUND_MISSION_MOB_09D, + STREAMED_SOUND_MISSION_MOB_09E, + STREAMED_SOUND_MISSION_MOB_09F, + STREAMED_SOUND_MISSION_MOB_10A, + STREAMED_SOUND_MISSION_MOB_10B, + STREAMED_SOUND_MISSION_MOB_10C, + STREAMED_SOUND_MISSION_MOB_10D, + STREAMED_SOUND_MISSION_MOB_10E, + STREAMED_SOUND_MISSION_MOB_11A, + STREAMED_SOUND_MISSION_MOB_11B, + STREAMED_SOUND_MISSION_MOB_11C, + STREAMED_SOUND_MISSION_MOB_11D, + STREAMED_SOUND_MISSION_MOB_11E, + STREAMED_SOUND_MISSION_MOB_11F, + STREAMED_SOUND_MISSION_MOB_14A, + STREAMED_SOUND_MISSION_MOB_14B, + STREAMED_SOUND_MISSION_MOB_14C, + STREAMED_SOUND_MISSION_MOB_14D, + STREAMED_SOUND_MISSION_MOB_14E, + STREAMED_SOUND_MISSION_MOB_14F, + STREAMED_SOUND_MISSION_MOB_14G, + STREAMED_SOUND_MISSION_MOB_14H, + STREAMED_SOUND_MISSION_MOB_16A, + STREAMED_SOUND_MISSION_MOB_16B, + STREAMED_SOUND_MISSION_MOB_16C, + STREAMED_SOUND_MISSION_MOB_16D, + STREAMED_SOUND_MISSION_MOB_16E, + STREAMED_SOUND_MISSION_MOB_16F, + STREAMED_SOUND_MISSION_MOB_16G, + STREAMED_SOUND_MISSION_MOB_17A, + STREAMED_SOUND_MISSION_MOB_17B, + STREAMED_SOUND_MISSION_MOB_17C, + STREAMED_SOUND_MISSION_MOB_17D, + STREAMED_SOUND_MISSION_MOB_17E, + STREAMED_SOUND_MISSION_MOB_17G, + STREAMED_SOUND_MISSION_MOB_17H, + STREAMED_SOUND_MISSION_MOB_17I, + STREAMED_SOUND_MISSION_MOB_17J, + STREAMED_SOUND_MISSION_MOB_17K, + STREAMED_SOUND_MISSION_MOB_17L, + STREAMED_SOUND_MISSION_MOB_18A, + STREAMED_SOUND_MISSION_MOB_18B, + STREAMED_SOUND_MISSION_MOB_18C, + STREAMED_SOUND_MISSION_MOB_18D, + STREAMED_SOUND_MISSION_MOB_18E, + STREAMED_SOUND_MISSION_MOB_18F, + STREAMED_SOUND_MISSION_MOB_18G, + STREAMED_SOUND_MISSION_MOB_20A, + STREAMED_SOUND_MISSION_MOB_20B, + STREAMED_SOUND_MISSION_MOB_20C, + STREAMED_SOUND_MISSION_MOB_20D, + STREAMED_SOUND_MISSION_MOB_20E, + STREAMED_SOUND_MISSION_MOB_24A, + STREAMED_SOUND_MISSION_MOB_24B, + STREAMED_SOUND_MISSION_MOB_24C, + STREAMED_SOUND_MISSION_MOB_24D, + STREAMED_SOUND_MISSION_MOB_24E, + STREAMED_SOUND_MISSION_MOB_24F, + STREAMED_SOUND_MISSION_MOB_24G, + STREAMED_SOUND_MISSION_MOB_24H, + STREAMED_SOUND_MISSION_MOB_25A, + STREAMED_SOUND_MISSION_MOB_25B, + STREAMED_SOUND_MISSION_MOB_25C, + STREAMED_SOUND_MISSION_MOB_25D, + STREAMED_SOUND_MISSION_MOB_26A, + STREAMED_SOUND_MISSION_MOB_26B, + STREAMED_SOUND_MISSION_MOB_26C, + STREAMED_SOUND_MISSION_MOB_26D, + STREAMED_SOUND_MISSION_MOB_26E, + STREAMED_SOUND_MISSION_MOB_29A, + STREAMED_SOUND_MISSION_MOB_29B, + STREAMED_SOUND_MISSION_MOB_29C, + STREAMED_SOUND_MISSION_MOB_29D, + STREAMED_SOUND_MISSION_MOB_29E, + STREAMED_SOUND_MISSION_MOB_29F, + STREAMED_SOUND_MISSION_MOB_29G, + STREAMED_SOUND_MISSION_MOB_30A, + STREAMED_SOUND_MISSION_MOB_30B, + STREAMED_SOUND_MISSION_MOB_30C, + STREAMED_SOUND_MISSION_MOB_30D, + STREAMED_SOUND_MISSION_MOB_30E, + STREAMED_SOUND_MISSION_MOB_30F, + STREAMED_SOUND_MISSION_MOB_33A, + STREAMED_SOUND_MISSION_MOB_33B, + STREAMED_SOUND_MISSION_MOB_33C, + STREAMED_SOUND_MISSION_MOB_33D, + STREAMED_SOUND_MISSION_MOB_34A, + STREAMED_SOUND_MISSION_MOB_34B, + STREAMED_SOUND_MISSION_MOB_34C, + STREAMED_SOUND_MISSION_MOB_34D, + STREAMED_SOUND_MISSION_MOB_35A, + STREAMED_SOUND_MISSION_MOB_35B, + STREAMED_SOUND_MISSION_MOB_35C, + STREAMED_SOUND_MISSION_MOB_35D, + STREAMED_SOUND_MISSION_MOB_36A, + STREAMED_SOUND_MISSION_MOB_36B, + STREAMED_SOUND_MISSION_MOB_36C, + STREAMED_SOUND_MISSION_MOB_40A, + STREAMED_SOUND_MISSION_MOB_40B, + STREAMED_SOUND_MISSION_MOB_40C, + STREAMED_SOUND_MISSION_MOB_40D, + STREAMED_SOUND_MISSION_MOB_40E, + STREAMED_SOUND_MISSION_MOB_40F, + STREAMED_SOUND_MISSION_MOB_40G, + STREAMED_SOUND_MISSION_MOB_40H, + STREAMED_SOUND_MISSION_MOB_40I, + STREAMED_SOUND_MISSION_MOB_41A, + STREAMED_SOUND_MISSION_MOB_41B, + STREAMED_SOUND_MISSION_MOB_41C, + STREAMED_SOUND_MISSION_MOB_41D, + STREAMED_SOUND_MISSION_MOB_41E, + STREAMED_SOUND_MISSION_MOB_41F, + STREAMED_SOUND_MISSION_MOB_41G, + STREAMED_SOUND_MISSION_MOB_41H, + STREAMED_SOUND_MISSION_MOB_42A, + STREAMED_SOUND_MISSION_MOB_42B, + STREAMED_SOUND_MISSION_MOB_42C, + STREAMED_SOUND_MISSION_MOB_42D, + STREAMED_SOUND_MISSION_MOB_42E, + STREAMED_SOUND_MISSION_MOB_43A, + STREAMED_SOUND_MISSION_MOB_43B, + STREAMED_SOUND_MISSION_MOB_43C, + STREAMED_SOUND_MISSION_MOB_43D, + STREAMED_SOUND_MISSION_MOB_43E, + STREAMED_SOUND_MISSION_MOB_43F, + STREAMED_SOUND_MISSION_MOB_43G, + STREAMED_SOUND_MISSION_MOB_43H, + STREAMED_SOUND_MISSION_MOB_45A, + STREAMED_SOUND_MISSION_MOB_45B, + STREAMED_SOUND_MISSION_MOB_45C, + STREAMED_SOUND_MISSION_MOB_45D, + STREAMED_SOUND_MISSION_MOB_45E, + STREAMED_SOUND_MISSION_MOB_45F, + STREAMED_SOUND_MISSION_MOB_45G, + STREAMED_SOUND_MISSION_MOB_45H, + STREAMED_SOUND_MISSION_MOB_45I, + STREAMED_SOUND_MISSION_MOB_45J, + STREAMED_SOUND_MISSION_MOB_45K, + STREAMED_SOUND_MISSION_MOB_45L, + STREAMED_SOUND_MISSION_MOB_45M, + STREAMED_SOUND_MISSION_MOB_45N, + STREAMED_SOUND_MISSION_MOB_46A, + STREAMED_SOUND_MISSION_MOB_46B, + STREAMED_SOUND_MISSION_MOB_46C, + STREAMED_SOUND_MISSION_MOB_46D, + STREAMED_SOUND_MISSION_MOB_46E, + STREAMED_SOUND_MISSION_MOB_46F, + STREAMED_SOUND_MISSION_MOB_46G, + STREAMED_SOUND_MISSION_MOB_46H, + STREAMED_SOUND_MISSION_MOB_47A, + STREAMED_SOUND_MISSION_MOB_52A, + STREAMED_SOUND_MISSION_MOB_52B, + STREAMED_SOUND_MISSION_MOB_52C, + STREAMED_SOUND_MISSION_MOB_52D, + STREAMED_SOUND_MISSION_MOB_52E, + STREAMED_SOUND_MISSION_MOB_52F, + STREAMED_SOUND_MISSION_MOB_52G, + STREAMED_SOUND_MISSION_MOB_52H, + STREAMED_SOUND_MISSION_MOB_54A, + STREAMED_SOUND_MISSION_MOB_54B, + STREAMED_SOUND_MISSION_MOB_54C, + STREAMED_SOUND_MISSION_MOB_54D, + STREAMED_SOUND_MISSION_MOB_54E, + STREAMED_SOUND_MISSION_MOB_55A, + STREAMED_SOUND_MISSION_MOB_55B, + STREAMED_SOUND_MISSION_MOB_55C, + STREAMED_SOUND_MISSION_MOB_55D, + STREAMED_SOUND_MISSION_MOB_55E, + STREAMED_SOUND_MISSION_MOB_55F, + STREAMED_SOUND_MISSION_MOB_56A, + STREAMED_SOUND_MISSION_MOB_56B, + STREAMED_SOUND_MISSION_MOB_56C, + STREAMED_SOUND_MISSION_MOB_56D, + STREAMED_SOUND_MISSION_MOB_56E, + STREAMED_SOUND_MISSION_MOB_56F, + STREAMED_SOUND_MISSION_MOB_57A, + STREAMED_SOUND_MISSION_MOB_57B, + STREAMED_SOUND_MISSION_MOB_57C, + STREAMED_SOUND_MISSION_MOB_57D, + STREAMED_SOUND_MISSION_MOB_57E, + STREAMED_SOUND_MISSION_MOB_58A, + STREAMED_SOUND_MISSION_MOB_58B, + STREAMED_SOUND_MISSION_MOB_58C, + STREAMED_SOUND_MISSION_MOB_58D, + STREAMED_SOUND_MISSION_MOB_58E, + STREAMED_SOUND_MISSION_MOB_58F, + STREAMED_SOUND_MISSION_MOB_58G, + STREAMED_SOUND_MISSION_MOB_61A, + STREAMED_SOUND_MISSION_MOB_61B, + STREAMED_SOUND_MISSION_MOB_62A, + STREAMED_SOUND_MISSION_MOB_62B, + STREAMED_SOUND_MISSION_MOB_62C, + STREAMED_SOUND_MISSION_MOB_62D, + STREAMED_SOUND_MISSION_MOB_63A, + STREAMED_SOUND_MISSION_MOB_63B, + STREAMED_SOUND_MISSION_MOB_63C, + STREAMED_SOUND_MISSION_MOB_63D, + STREAMED_SOUND_MISSION_MOB_63E, + STREAMED_SOUND_MISSION_MOB_63F, + STREAMED_SOUND_MISSION_MOB_63G, + STREAMED_SOUND_MISSION_MOB_63H, + STREAMED_SOUND_MISSION_MOB_63I, + STREAMED_SOUND_MISSION_MOB_63J, + STREAMED_SOUND_MISSION_MOB_66A, + STREAMED_SOUND_MISSION_MOB_66B, + STREAMED_SOUND_MISSION_MOB_68A, + STREAMED_SOUND_MISSION_MOB_68B, + STREAMED_SOUND_MISSION_MOB_68C, + STREAMED_SOUND_MISSION_MOB_68D, + STREAMED_SOUND_MISSION_MOB_70A, + STREAMED_SOUND_MISSION_MOB_70B, + STREAMED_SOUND_MISSION_MOB_71A, + STREAMED_SOUND_MISSION_MOB_71B, + STREAMED_SOUND_MISSION_MOB_71C, + STREAMED_SOUND_MISSION_MOB_71D, + STREAMED_SOUND_MISSION_MOB_71E, + STREAMED_SOUND_MISSION_MOB_71F, + STREAMED_SOUND_MISSION_MOB_71G, + STREAMED_SOUND_MISSION_MOB_71H, + STREAMED_SOUND_MISSION_MOB_71I, + STREAMED_SOUND_MISSION_MOB_71J, + STREAMED_SOUND_MISSION_MOB_71K, + STREAMED_SOUND_MISSION_MOB_71L, + STREAMED_SOUND_MISSION_MOB_71M, + STREAMED_SOUND_MISSION_MOB_71N, + STREAMED_SOUND_MISSION_MOB_72A, + STREAMED_SOUND_MISSION_MOB_72B, + STREAMED_SOUND_MISSION_MOB_72C, + STREAMED_SOUND_MISSION_MOB_72D, + STREAMED_SOUND_MISSION_MOB_72E, + STREAMED_SOUND_MISSION_MOB_72F, + STREAMED_SOUND_MISSION_MOB_72G, + STREAMED_SOUND_MISSION_MOB_73A, + STREAMED_SOUND_MISSION_MOB_73C, + STREAMED_SOUND_MISSION_MOB_73D, + STREAMED_SOUND_MISSION_MOB_73F, + STREAMED_SOUND_MISSION_MOB_73G, + STREAMED_SOUND_MISSION_MOB_73I, + STREAMED_SOUND_MISSION_MOB_95A, + STREAMED_SOUND_MISSION_MOB_96A, + STREAMED_SOUND_MISSION_MOB_98A, + STREAMED_SOUND_MISSION_MOB_99A, + STREAMED_SOUND_MISSION_JOB1_1B, + STREAMED_SOUND_MISSION_JOB1_1C, + STREAMED_SOUND_MISSION_JOB1_1D, + STREAMED_SOUND_MISSION_JOB2_1B, + STREAMED_SOUND_MISSION_JOB2_2, + STREAMED_SOUND_MISSION_JOB2_3, + STREAMED_SOUND_MISSION_JOB2_4, + STREAMED_SOUND_MISSION_JOB2_5, + STREAMED_SOUND_MISSION_JOB2_6, + STREAMED_SOUND_MISSION_JOB2_7, + STREAMED_SOUND_MISSION_JOB2_8, + STREAMED_SOUND_MISSION_JOB2_9, + STREAMED_SOUND_MISSION_JOB3_1, + STREAMED_SOUND_MISSION_JOB3_2, + STREAMED_SOUND_MISSION_JOB3_3, + STREAMED_SOUND_MISSION_JOB4_1, + STREAMED_SOUND_MISSION_JOB4_2, + STREAMED_SOUND_MISSION_JOB4_3, + STREAMED_SOUND_MISSION_JOB5_1, + STREAMED_SOUND_MISSION_JOB5_2, + STREAMED_SOUND_MISSION_JOB5_3, + STREAMED_SOUND_MISSION_BJM1_20, + STREAMED_SOUND_MISSION_BJM1_4, + STREAMED_SOUND_MISSION_BJM1_5, + STREAMED_SOUND_MISSION_MERC_39, + STREAMED_SOUND_MISSION_MONO_1, + STREAMED_SOUND_MISSION_MONO_2, + STREAMED_SOUND_MISSION_MONO_3, + STREAMED_SOUND_MISSION_MONO_4, + STREAMED_SOUND_MISSION_MONO_5, + STREAMED_SOUND_MISSION_MONO_6, + STREAMED_SOUND_MISSION_MONO_7, + STREAMED_SOUND_MISSION_MONO_8, + STREAMED_SOUND_MISSION_MONO_9, + STREAMED_SOUND_MISSION_MONO10, + STREAMED_SOUND_MISSION_MONO11, + STREAMED_SOUND_MISSION_MONO12, + STREAMED_SOUND_MISSION_MONO13, + STREAMED_SOUND_MISSION_MONO14, + STREAMED_SOUND_MISSION_MONO15, + STREAMED_SOUND_MISSION_MONO16, + STREAMED_SOUND_MISSION_FUD_01, + STREAMED_SOUND_MISSION_FUD_02, + STREAMED_SOUND_MISSION_FUD_03, + STREAMED_SOUND_MISSION_FUD_04, + STREAMED_SOUND_MISSION_FUD_05, + STREAMED_SOUND_MISSION_FUD_06, + STREAMED_SOUND_MISSION_FUD_07, + STREAMED_SOUND_MISSION_FUD_08, + STREAMED_SOUND_MISSION_FUD_09, + STREAMED_SOUND_MISSION_FUD_10, + STREAMED_SOUND_MISSION_FUD_11, + STREAMED_SOUND_MISSION_FUD_12, + STREAMED_SOUND_MISSION_FUD_13, + STREAMED_SOUND_MISSION_FUD_14, + STREAMED_SOUND_MISSION_FUD_15, + STREAMED_SOUND_MISSION_FUD_16, + STREAMED_SOUND_MISSION_FUD_17, + STREAMED_SOUND_MISSION_FUD_18, + STREAMED_SOUND_MISSION_FUD_19, + STREAMED_SOUND_MISSION_FUD_20, + STREAMED_SOUND_MISSION_BURG_01, + STREAMED_SOUND_MISSION_BURG_02, + STREAMED_SOUND_MISSION_BURG_03, + STREAMED_SOUND_MISSION_BURG_04, + STREAMED_SOUND_MISSION_BURG_05, + STREAMED_SOUND_MISSION_BURG_06, + STREAMED_SOUND_MISSION_BURG_07, + STREAMED_SOUND_MISSION_BURG_08, + STREAMED_SOUND_MISSION_BURG_09, + STREAMED_SOUND_MISSION_BURG_10, + STREAMED_SOUND_MISSION_BURG_11, + STREAMED_SOUND_MISSION_BURG_12, + STREAMED_SOUND_MISSION_CRUST01, + STREAMED_SOUND_MISSION_CRUST02, + STREAMED_SOUND_MISSION_CRUST03, + STREAMED_SOUND_MISSION_CRUST04, + STREAMED_SOUND_MISSION_CRUST05, + STREAMED_SOUND_MISSION_CRUST06, + STREAMED_SOUND_MISSION_CRUST07, + STREAMED_SOUND_MISSION_CRUST08, + STREAMED_SOUND_MISSION_CRUST09, + STREAMED_SOUND_MISSION_BAND_01, + STREAMED_SOUND_MISSION_BAND_02, + STREAMED_SOUND_MISSION_BAND_03, + STREAMED_SOUND_MISSION_BAND_04, + STREAMED_SOUND_MISSION_BAND_05, + STREAMED_SOUND_MISSION_BAND_06, + STREAMED_SOUND_MISSION_BAND_07, + STREAMED_SOUND_MISSION_BAND_08, + STREAMED_SOUND_MISSION_SHAFT01, + STREAMED_SOUND_MISSION_SHAFT02, + STREAMED_SOUND_MISSION_SHAFT03, + STREAMED_SOUND_MISSION_SHAFT04, + STREAMED_SOUND_MISSION_SHAFT05, + STREAMED_SOUND_MISSION_SHAFT06, + STREAMED_SOUND_MISSION_SHAFT07, + STREAMED_SOUND_MISSION_SHAFT08, + STREAMED_SOUND_MISSION_PISS_01, + STREAMED_SOUND_MISSION_PISS_02, + STREAMED_SOUND_MISSION_PISS_03, + STREAMED_SOUND_MISSION_PISS_04, + STREAMED_SOUND_MISSION_PISS_05, + STREAMED_SOUND_MISSION_PISS_06, + STREAMED_SOUND_MISSION_PISS_07, + STREAMED_SOUND_MISSION_PISS_08, + STREAMED_SOUND_MISSION_PISS_09, + STREAMED_SOUND_MISSION_PISS_10, + STREAMED_SOUND_MISSION_PISS_11, + STREAMED_SOUND_MISSION_PISS_12, + STREAMED_SOUND_MISSION_PISS_13, + STREAMED_SOUND_MISSION_PISS_14, + STREAMED_SOUND_MISSION_PISS_15, + STREAMED_SOUND_MISSION_PISS_16, + STREAMED_SOUND_MISSION_PISS_17, + STREAMED_SOUND_MISSION_PISS_18, + STREAMED_SOUND_MISSION_PISS_19, + STREAMED_SOUND_MISSION_GIMME01, + STREAMED_SOUND_MISSION_GIMME02, + STREAMED_SOUND_MISSION_GIMME03, + STREAMED_SOUND_MISSION_GIMME04, + STREAMED_SOUND_MISSION_GIMME05, + STREAMED_SOUND_MISSION_GIMME06, + STREAMED_SOUND_MISSION_GIMME07, + STREAMED_SOUND_MISSION_GIMME08, + STREAMED_SOUND_MISSION_GIMME09, + STREAMED_SOUND_MISSION_GIMME10, + STREAMED_SOUND_MISSION_GIMME11, + STREAMED_SOUND_MISSION_GIMME12, + STREAMED_SOUND_MISSION_GIMME13, + STREAMED_SOUND_MISSION_GIMME14, + STREAMED_SOUND_MISSION_GIMME15, + STREAMED_SOUND_MISSION_BUST_01, + STREAMED_SOUND_MISSION_BUST_02, + STREAMED_SOUND_MISSION_BUST_03, + STREAMED_SOUND_MISSION_BUST_04, + STREAMED_SOUND_MISSION_BUST_05, + STREAMED_SOUND_MISSION_BUST_06, + STREAMED_SOUND_MISSION_BUST_07, + STREAMED_SOUND_MISSION_BUST_08, + STREAMED_SOUND_MISSION_BUST_09, + STREAMED_SOUND_MISSION_BUST_10, + STREAMED_SOUND_MISSION_BUST_11, + STREAMED_SOUND_MISSION_BUST_12, + STREAMED_SOUND_MISSION_BUST_13, + STREAMED_SOUND_MISSION_BUST_14, + STREAMED_SOUND_MISSION_BUST_15, + STREAMED_SOUND_MISSION_BUST_16, + STREAMED_SOUND_MISSION_BUST_17, + STREAMED_SOUND_MISSION_BUST_18, + STREAMED_SOUND_MISSION_BUST_19, + STREAMED_SOUND_MISSION_BUST_20, + STREAMED_SOUND_MISSION_BUST_21, + STREAMED_SOUND_MISSION_BUST_22, + STREAMED_SOUND_MISSION_BUST_23, + STREAMED_SOUND_MISSION_BUST_24, + STREAMED_SOUND_MISSION_BUST_25, + STREAMED_SOUND_MISSION_BUST_26, + STREAMED_SOUND_MISSION_BUST_27, + STREAMED_SOUND_MISSION_BUST_28, + TOTAL_STREAMED_SOUNDS, + NO_TRACK, +}; + +enum AudioEntityHandle { + AEHANDLE_NONE = -5, + AEHANDLE_ERROR_NOAUDIOSYS = -4, + AEHANDLE_ERROR_NOFREESLOT = -3, + AEHANDLE_ERROR_NOENTITY = -2, + AEHANDLE_ERROR_BADAUDIOTYPE = -1, +}; + +enum eAudioType +{ + AUDIOTYPE_PHYSICAL = 0, + AUDIOTYPE_EXPLOSION, + AUDIOTYPE_FIRE, + AUDIOTYPE_WEATHER, + AUDIOTYPE_SCRIPTOBJECT, +#ifdef GTA_BRIDGE + AUDIOTYPE_BRIDGE, +#endif + AUDIOTYPE_COLLISION, + AUDIOTYPE_FRONTEND, + AUDIOTYPE_PROJECTILE, + AUDIOTYPE_GARAGE, + AUDIOTYPE_FIREHYDRANT, + AUDIOTYPE_WATERCANNON, + AUDIOTYPE_ESCALATOR, + AUDIOTYPE_EXTRA_SOUNDS, + AUDIOTYPE_POLICERADIO, + TOTAL_AUDIO_TYPES, +}; + +#ifdef GTA_PS2 +enum +{ + NUM_CHANNELS_GENERIC = 42, + CHANNEL_POLICE_RADIO = NUM_CHANNELS_GENERIC, + CHANNEL_MISSION_AUDIO_1, + CHANNEL_MISSION_AUDIO_2, + CHANNEL_PLAYER_VEHICLE_ENGINE, + NUM_CHANNELS, + + NUM_CHANNELS_DTS_GENERIC = 18, + CHANNEL_DTS_POLICE_RADIO = NUM_CHANNELS_DTS_GENERIC, + CHANNEL_DTS_MISSION_AUDIO_1, + CHANNEL_DTS_MISSION_AUDIO_2, + CHANNEL_DTS_PLAYER_VEHICLE_ENGINE, +}; +#else +enum +{ +#ifdef PS2_AUDIO_CHANNELS + NUM_CHANNELS_GENERIC = 42, +#else + NUM_CHANNELS_GENERIC = 27, +#endif + CHANNEL_POLICE_RADIO, + NUM_CHANNELS +}; +#endif + +enum +{ + MISSION_AUDIO_SLOT_1, + MISSION_AUDIO_SLOT_2, + MISSION_AUDIO_POLRADIO_CRIME_OR_COLOR, + MISSION_AUDIO_POLRADIO_AREA_OR_CAR, + MISSION_AUDIO_PLAYER_COMMENT, + MISSION_AUDIO_COUNT +}; diff --git a/src/miami/audio/eax/eax-util.cpp b/src/miami/audio/eax/eax-util.cpp new file mode 100644 index 00000000..42eef738 --- /dev/null +++ b/src/miami/audio/eax/eax-util.cpp @@ -0,0 +1,706 @@ +/***********************************************************************************************\ +* * +* EAX-UTIL.CPP - utilities for EAX 3.0 * +* Function declaration for EAX Morphing * +* String names of the all the presets defined in eax-util.h * +* Arrays grouping together all the EAX presets in a scenario * +* * +************************************************************************************************/ + +#include "eax-util.h" +#include + +// Function prototypes used by EAX3ListenerInterpolate +void Clamp(EAXVECTOR *eaxVector); +bool CheckEAX3LP(LPEAXLISTENERPROPERTIES lpEAX3LP); + + +/***********************************************************************************************\ +* +* Definition of the EAXMorph function - EAX3ListenerInterpolate +* +\***********************************************************************************************/ + +/* + EAX3ListenerInterpolate + lpStart - Initial EAX 3 Listener parameters + lpFinish - Final EAX 3 Listener parameters + flRatio - Ratio Destination : Source (0.0 == Source, 1.0 == Destination) + lpResult - Interpolated EAX 3 Listener parameters + bCheckValues - Check EAX 3.0 parameters are in range, default = false (no checking) +*/ +bool EAX3ListenerInterpolate(LPEAXLISTENERPROPERTIES lpStart, LPEAXLISTENERPROPERTIES lpFinish, + float flRatio, LPEAXLISTENERPROPERTIES lpResult, bool bCheckValues) +{ + EAXVECTOR StartVector, FinalVector; + + float flInvRatio; + + if (bCheckValues) + { + if (!CheckEAX3LP(lpStart)) + return false; + + if (!CheckEAX3LP(lpFinish)) + return false; + } + + if (flRatio >= 1.0f) + { + memcpy(lpResult, lpFinish, sizeof(EAXLISTENERPROPERTIES)); + return true; + } + else if (flRatio <= 0.0f) + { + memcpy(lpResult, lpStart, sizeof(EAXLISTENERPROPERTIES)); + return true; + } + + flInvRatio = (1.0f - flRatio); + + // Environment + lpResult->ulEnvironment = 26; // (UNDEFINED environment) + + // Environment Size + if (lpStart->flEnvironmentSize == lpFinish->flEnvironmentSize) + lpResult->flEnvironmentSize = lpStart->flEnvironmentSize; + else + lpResult->flEnvironmentSize = (float)exp( (log(lpStart->flEnvironmentSize) * flInvRatio) + (log(lpFinish->flEnvironmentSize) * flRatio) ); + + // Environment Diffusion + if (lpStart->flEnvironmentDiffusion == lpFinish->flEnvironmentDiffusion) + lpResult->flEnvironmentDiffusion = lpStart->flEnvironmentDiffusion; + else + lpResult->flEnvironmentDiffusion = (lpStart->flEnvironmentDiffusion * flInvRatio) + (lpFinish->flEnvironmentDiffusion * flRatio); + + // Room + if (lpStart->lRoom == lpFinish->lRoom) + lpResult->lRoom = lpStart->lRoom; + else + lpResult->lRoom = (int)( ((float)lpStart->lRoom * flInvRatio) + ((float)lpFinish->lRoom * flRatio) ); + + // Room HF + if (lpStart->lRoomHF == lpFinish->lRoomHF) + lpResult->lRoomHF = lpStart->lRoomHF; + else + lpResult->lRoomHF = (int)( ((float)lpStart->lRoomHF * flInvRatio) + ((float)lpFinish->lRoomHF * flRatio) ); + + // Room LF + if (lpStart->lRoomLF == lpFinish->lRoomLF) + lpResult->lRoomLF = lpStart->lRoomLF; + else + lpResult->lRoomLF = (int)( ((float)lpStart->lRoomLF * flInvRatio) + ((float)lpFinish->lRoomLF * flRatio) ); + + // Decay Time + if (lpStart->flDecayTime == lpFinish->flDecayTime) + lpResult->flDecayTime = lpStart->flDecayTime; + else + lpResult->flDecayTime = (float)exp( (log(lpStart->flDecayTime) * flInvRatio) + (log(lpFinish->flDecayTime) * flRatio) ); + + // Decay HF Ratio + if (lpStart->flDecayHFRatio == lpFinish->flDecayHFRatio) + lpResult->flDecayHFRatio = lpStart->flDecayHFRatio; + else + lpResult->flDecayHFRatio = (float)exp( (log(lpStart->flDecayHFRatio) * flInvRatio) + (log(lpFinish->flDecayHFRatio) * flRatio) ); + + // Decay LF Ratio + if (lpStart->flDecayLFRatio == lpFinish->flDecayLFRatio) + lpResult->flDecayLFRatio = lpStart->flDecayLFRatio; + else + lpResult->flDecayLFRatio = (float)exp( (log(lpStart->flDecayLFRatio) * flInvRatio) + (log(lpFinish->flDecayLFRatio) * flRatio) ); + + // Reflections + if (lpStart->lReflections == lpFinish->lReflections) + lpResult->lReflections = lpStart->lReflections; + else + lpResult->lReflections = (int)( ((float)lpStart->lReflections * flInvRatio) + ((float)lpFinish->lReflections * flRatio) ); + + // Reflections Delay + if (lpStart->flReflectionsDelay == lpFinish->flReflectionsDelay) + lpResult->flReflectionsDelay = lpStart->flReflectionsDelay; + else + lpResult->flReflectionsDelay = (float)exp( (log(lpStart->flReflectionsDelay+0.0001f) * flInvRatio) + (log(lpFinish->flReflectionsDelay+0.0001f) * flRatio) ); + + // Reflections Pan + + // To interpolate the vector correctly we need to ensure that both the initial and final vectors vectors are clamped to a length of 1.0f + StartVector = lpStart->vReflectionsPan; + FinalVector = lpFinish->vReflectionsPan; + + Clamp(&StartVector); + Clamp(&FinalVector); + + if (lpStart->vReflectionsPan.x == lpFinish->vReflectionsPan.x) + lpResult->vReflectionsPan.x = lpStart->vReflectionsPan.x; + else + lpResult->vReflectionsPan.x = FinalVector.x + (flInvRatio * (StartVector.x - FinalVector.x)); + + if (lpStart->vReflectionsPan.y == lpFinish->vReflectionsPan.y) + lpResult->vReflectionsPan.y = lpStart->vReflectionsPan.y; + else + lpResult->vReflectionsPan.y = FinalVector.y + (flInvRatio * (StartVector.y - FinalVector.y)); + + if (lpStart->vReflectionsPan.z == lpFinish->vReflectionsPan.z) + lpResult->vReflectionsPan.z = lpStart->vReflectionsPan.z; + else + lpResult->vReflectionsPan.z = FinalVector.z + (flInvRatio * (StartVector.z - FinalVector.z)); + + // Reverb + if (lpStart->lReverb == lpFinish->lReverb) + lpResult->lReverb = lpStart->lReverb; + else + lpResult->lReverb = (int)( ((float)lpStart->lReverb * flInvRatio) + ((float)lpFinish->lReverb * flRatio) ); + + // Reverb Delay + if (lpStart->flReverbDelay == lpFinish->flReverbDelay) + lpResult->flReverbDelay = lpStart->flReverbDelay; + else + lpResult->flReverbDelay = (float)exp( (log(lpStart->flReverbDelay+0.0001f) * flInvRatio) + (log(lpFinish->flReverbDelay+0.0001f) * flRatio) ); + + // Reverb Pan + + // To interpolate the vector correctly we need to ensure that both the initial and final vectors are clamped to a length of 1.0f + StartVector = lpStart->vReverbPan; + FinalVector = lpFinish->vReverbPan; + + Clamp(&StartVector); + Clamp(&FinalVector); + + if (lpStart->vReverbPan.x == lpFinish->vReverbPan.x) + lpResult->vReverbPan.x = lpStart->vReverbPan.x; + else + lpResult->vReverbPan.x = FinalVector.x + (flInvRatio * (StartVector.x - FinalVector.x)); + + if (lpStart->vReverbPan.y == lpFinish->vReverbPan.y) + lpResult->vReverbPan.y = lpStart->vReverbPan.y; + else + lpResult->vReverbPan.y = FinalVector.y + (flInvRatio * (StartVector.y - FinalVector.y)); + + if (lpStart->vReverbPan.z == lpFinish->vReverbPan.z) + lpResult->vReverbPan.z = lpStart->vReverbPan.z; + else + lpResult->vReverbPan.z = FinalVector.z + (flInvRatio * (StartVector.z - FinalVector.z)); + + // Echo Time + if (lpStart->flEchoTime == lpFinish->flEchoTime) + lpResult->flEchoTime = lpStart->flEchoTime; + else + lpResult->flEchoTime = (float)exp( (log(lpStart->flEchoTime) * flInvRatio) + (log(lpFinish->flEchoTime) * flRatio) ); + + // Echo Depth + if (lpStart->flEchoDepth == lpFinish->flEchoDepth) + lpResult->flEchoDepth = lpStart->flEchoDepth; + else + lpResult->flEchoDepth = (lpStart->flEchoDepth * flInvRatio) + (lpFinish->flEchoDepth * flRatio); + + // Modulation Time + if (lpStart->flModulationTime == lpFinish->flModulationTime) + lpResult->flModulationTime = lpStart->flModulationTime; + else + lpResult->flModulationTime = (float)exp( (log(lpStart->flModulationTime) * flInvRatio) + (log(lpFinish->flModulationTime) * flRatio) ); + + // Modulation Depth + if (lpStart->flModulationDepth == lpFinish->flModulationDepth) + lpResult->flModulationDepth = lpStart->flModulationDepth; + else + lpResult->flModulationDepth = (lpStart->flModulationDepth * flInvRatio) + (lpFinish->flModulationDepth * flRatio); + + // Air Absorption HF + if (lpStart->flAirAbsorptionHF == lpFinish->flAirAbsorptionHF) + lpResult->flAirAbsorptionHF = lpStart->flAirAbsorptionHF; + else + lpResult->flAirAbsorptionHF = (lpStart->flAirAbsorptionHF * flInvRatio) + (lpFinish->flAirAbsorptionHF * flRatio); + + // HF Reference + if (lpStart->flHFReference == lpFinish->flHFReference) + lpResult->flHFReference = lpStart->flHFReference; + else + lpResult->flHFReference = (float)exp( (log(lpStart->flHFReference) * flInvRatio) + (log(lpFinish->flHFReference) * flRatio) ); + + // LF Reference + if (lpStart->flLFReference == lpFinish->flLFReference) + lpResult->flLFReference = lpStart->flLFReference; + else + lpResult->flLFReference = (float)exp( (log(lpStart->flLFReference) * flInvRatio) + (log(lpFinish->flLFReference) * flRatio) ); + + // Room Rolloff Factor + if (lpStart->flRoomRolloffFactor == lpFinish->flRoomRolloffFactor) + lpResult->flRoomRolloffFactor = lpStart->flRoomRolloffFactor; + else + lpResult->flRoomRolloffFactor = (lpStart->flRoomRolloffFactor * flInvRatio) + (lpFinish->flRoomRolloffFactor * flRatio); + + // Flags + lpResult->ulFlags = (lpStart->ulFlags & lpFinish->ulFlags); + + // Clamp Delays + if (lpResult->flReflectionsDelay > EAXLISTENER_MAXREFLECTIONSDELAY) + lpResult->flReflectionsDelay = EAXLISTENER_MAXREFLECTIONSDELAY; + + if (lpResult->flReverbDelay > EAXLISTENER_MAXREVERBDELAY) + lpResult->flReverbDelay = EAXLISTENER_MAXREVERBDELAY; + + return true; +} + + +/* + CheckEAX3LP + Checks that the parameters in the EAX 3 Listener Properties structure are in-range +*/ +bool CheckEAX3LP(LPEAXLISTENERPROPERTIES lpEAX3LP) +{ + if ( (lpEAX3LP->lRoom < EAXLISTENER_MINROOM) || (lpEAX3LP->lRoom > EAXLISTENER_MAXROOM) ) + return false; + + if ( (lpEAX3LP->lRoomHF < EAXLISTENER_MINROOMHF) || (lpEAX3LP->lRoomHF > EAXLISTENER_MAXROOMHF) ) + return false; + + if ( (lpEAX3LP->lRoomLF < EAXLISTENER_MINROOMLF) || (lpEAX3LP->lRoomLF > EAXLISTENER_MAXROOMLF) ) + return false; + + if ( (lpEAX3LP->ulEnvironment < EAXLISTENER_MINENVIRONMENT) || (lpEAX3LP->ulEnvironment > EAXLISTENER_MAXENVIRONMENT) ) + return false; + + if ( (lpEAX3LP->flEnvironmentSize < EAXLISTENER_MINENVIRONMENTSIZE) || (lpEAX3LP->flEnvironmentSize > EAXLISTENER_MAXENVIRONMENTSIZE) ) + return false; + + if ( (lpEAX3LP->flEnvironmentDiffusion < EAXLISTENER_MINENVIRONMENTDIFFUSION) || (lpEAX3LP->flEnvironmentDiffusion > EAXLISTENER_MAXENVIRONMENTDIFFUSION) ) + return false; + + if ( (lpEAX3LP->flDecayTime < EAXLISTENER_MINDECAYTIME) || (lpEAX3LP->flDecayTime > EAXLISTENER_MAXDECAYTIME) ) + return false; + + if ( (lpEAX3LP->flDecayHFRatio < EAXLISTENER_MINDECAYHFRATIO) || (lpEAX3LP->flDecayHFRatio > EAXLISTENER_MAXDECAYHFRATIO) ) + return false; + + if ( (lpEAX3LP->flDecayLFRatio < EAXLISTENER_MINDECAYLFRATIO) || (lpEAX3LP->flDecayLFRatio > EAXLISTENER_MAXDECAYLFRATIO) ) + return false; + + if ( (lpEAX3LP->lReflections < EAXLISTENER_MINREFLECTIONS) || (lpEAX3LP->lReflections > EAXLISTENER_MAXREFLECTIONS) ) + return false; + + if ( (lpEAX3LP->flReflectionsDelay < EAXLISTENER_MINREFLECTIONSDELAY) || (lpEAX3LP->flReflectionsDelay > EAXLISTENER_MAXREFLECTIONSDELAY) ) + return false; + + if ( (lpEAX3LP->lReverb < EAXLISTENER_MINREVERB) || (lpEAX3LP->lReverb > EAXLISTENER_MAXREVERB) ) + return false; + + if ( (lpEAX3LP->flReverbDelay < EAXLISTENER_MINREVERBDELAY) || (lpEAX3LP->flReverbDelay > EAXLISTENER_MAXREVERBDELAY) ) + return false; + + if ( (lpEAX3LP->flEchoTime < EAXLISTENER_MINECHOTIME) || (lpEAX3LP->flEchoTime > EAXLISTENER_MAXECHOTIME) ) + return false; + + if ( (lpEAX3LP->flEchoDepth < EAXLISTENER_MINECHODEPTH) || (lpEAX3LP->flEchoDepth > EAXLISTENER_MAXECHODEPTH) ) + return false; + + if ( (lpEAX3LP->flModulationTime < EAXLISTENER_MINMODULATIONTIME) || (lpEAX3LP->flModulationTime > EAXLISTENER_MAXMODULATIONTIME) ) + return false; + + if ( (lpEAX3LP->flModulationDepth < EAXLISTENER_MINMODULATIONDEPTH) || (lpEAX3LP->flModulationDepth > EAXLISTENER_MAXMODULATIONDEPTH) ) + return false; + + if ( (lpEAX3LP->flAirAbsorptionHF < EAXLISTENER_MINAIRABSORPTIONHF) || (lpEAX3LP->flAirAbsorptionHF > EAXLISTENER_MAXAIRABSORPTIONHF) ) + return false; + + if ( (lpEAX3LP->flHFReference < EAXLISTENER_MINHFREFERENCE) || (lpEAX3LP->flHFReference > EAXLISTENER_MAXHFREFERENCE) ) + return false; + + if ( (lpEAX3LP->flLFReference < EAXLISTENER_MINLFREFERENCE) || (lpEAX3LP->flLFReference > EAXLISTENER_MAXLFREFERENCE) ) + return false; + + if ( (lpEAX3LP->flRoomRolloffFactor < EAXLISTENER_MINROOMROLLOFFFACTOR) || (lpEAX3LP->flRoomRolloffFactor > EAXLISTENER_MAXROOMROLLOFFFACTOR) ) + return false; + + if (lpEAX3LP->ulFlags & EAXLISTENERFLAGS_RESERVED) + return false; + + return true; +} + +/* + Clamp + Clamps the length of the vector to 1.0f +*/ +void Clamp(EAXVECTOR *eaxVector) +{ + float flMagnitude; + float flInvMagnitude; + + flMagnitude = (float)sqrt((eaxVector->x*eaxVector->x) + (eaxVector->y*eaxVector->y) + (eaxVector->z*eaxVector->z)); + + if (flMagnitude <= 1.0f) + return; + + flInvMagnitude = 1.0f / flMagnitude; + + eaxVector->x *= flInvMagnitude; + eaxVector->y *= flInvMagnitude; + eaxVector->z *= flInvMagnitude; +} + + +/***********************************************************************************************\ +* +* To assist those developers wishing to add EAX effects to their level editors, each of the + +* List of string names of the various EAX 3.0 presets defined in eax-util.h +* Arrays to group together presets of the same scenario +* +\***********************************************************************************************/ + + +////////////////////////////////////////////////////// +// Array of scenario names // +////////////////////////////////////////////////////// + +const char* EAX30_SCENARIO_NAMES[] = +{ + "Castle", + "Factory", + "IcePalace", + "SpaceStation", + "WoodenShip", + "Sports", + "Prefab", + "Domes and Pipes", + "Outdoors", + "Mood", + "Driving", + "City", + "Miscellaneous", + "Original" +}; + +////////////////////////////////////////////////////// +// Array of standardised location names // +////////////////////////////////////////////////////// + +const char* EAX30_LOCATION_NAMES[] = +{ + "Hall", + "Large Room", + "Medium Room", + "Small Room", + "Cupboard", + "Alcove", + "Long Passage", + "Short Passage", + "Courtyard" +}; + +////////////////////////////////////////////////////// +// Standardised Location effects can be accessed // +// from a matrix // +////////////////////////////////////////////////////// + +EAXLISTENERPROPERTIES EAX30_STANDARD_PRESETS[EAX30_NUM_STANDARD_SCENARIOS][EAX30_NUM_LOCATIONS]= +{ + {EAX30_PRESET_CASTLE_HALL, EAX30_PRESET_CASTLE_LARGEROOM, EAX30_PRESET_CASTLE_MEDIUMROOM, EAX30_PRESET_CASTLE_SMALLROOM, EAX30_PRESET_CASTLE_CUPBOARD, EAX30_PRESET_CASTLE_ALCOVE, EAX30_PRESET_CASTLE_LONGPASSAGE, EAX30_PRESET_CASTLE_SHORTPASSAGE, EAX30_PRESET_CASTLE_COURTYARD}, + {EAX30_PRESET_FACTORY_HALL, EAX30_PRESET_FACTORY_LARGEROOM, EAX30_PRESET_FACTORY_MEDIUMROOM, EAX30_PRESET_FACTORY_SMALLROOM, EAX30_PRESET_FACTORY_CUPBOARD, EAX30_PRESET_FACTORY_ALCOVE, EAX30_PRESET_FACTORY_LONGPASSAGE, EAX30_PRESET_FACTORY_SHORTPASSAGE, EAX30_PRESET_FACTORY_COURTYARD}, + {EAX30_PRESET_ICEPALACE_HALL, EAX30_PRESET_ICEPALACE_LARGEROOM, EAX30_PRESET_ICEPALACE_MEDIUMROOM, EAX30_PRESET_ICEPALACE_SMALLROOM, EAX30_PRESET_ICEPALACE_CUPBOARD, EAX30_PRESET_ICEPALACE_ALCOVE, EAX30_PRESET_ICEPALACE_LONGPASSAGE, EAX30_PRESET_ICEPALACE_SHORTPASSAGE, EAX30_PRESET_ICEPALACE_COURTYARD}, + {EAX30_PRESET_SPACESTATION_HALL,EAX30_PRESET_SPACESTATION_LARGEROOM,EAX30_PRESET_SPACESTATION_MEDIUMROOM, EAX30_PRESET_SPACESTATION_SMALLROOM,EAX30_PRESET_SPACESTATION_CUPBOARD, EAX30_PRESET_SPACESTATION_ALCOVE, EAX30_PRESET_SPACESTATION_LONGPASSAGE, EAX30_PRESET_SPACESTATION_SHORTPASSAGE, EAX30_PRESET_SPACESTATION_HALL}, + {EAX30_PRESET_WOODEN_HALL, EAX30_PRESET_WOODEN_LARGEROOM, EAX30_PRESET_WOODEN_MEDIUMROOM, EAX30_PRESET_WOODEN_SMALLROOM, EAX30_PRESET_WOODEN_CUPBOARD, EAX30_PRESET_WOODEN_ALCOVE, EAX30_PRESET_WOODEN_LONGPASSAGE, EAX30_PRESET_WOODEN_SHORTPASSAGE, EAX30_PRESET_WOODEN_COURTYARD}, +}; + + +/********************************************************************************************************/ + +////////////////////////////////////////////////////// +// Array of original environment names // +////////////////////////////////////////////////////// + +const char* EAX30_ORIGINAL_PRESET_NAMES[] = +{ + "Generic", + "Padded Cell", + "Room", + "Bathroom", + "Living Room", + "Stone Room", + "Auditorium", + "Concert Hall", + "Cave", + "Arena", + "Hangar", + "Carpetted Hallway", + "Hallway", + "Stone Corridor", + "Alley", + "Forest", + "City", + "Mountains", + "Quarry", + "Plain", + "Parking Lot", + "Sewer Pipe", + "Underwater", + "Drugged", + "Dizzy", + "Psychotic" +}; + +////////////////////////////////////////////////////// +// Sports effects matrix // +////////////////////////////////////////////////////// + +EAXLISTENERPROPERTIES EAX30_ORIGINAL_PRESETS[] = +{ + EAX30_PRESET_GENERIC, + EAX30_PRESET_PADDEDCELL, + EAX30_PRESET_ROOM, + EAX30_PRESET_BATHROOM, + EAX30_PRESET_LIVINGROOM, + EAX30_PRESET_STONEROOM, + EAX30_PRESET_AUDITORIUM, + EAX30_PRESET_CONCERTHALL, + EAX30_PRESET_CAVE, + EAX30_PRESET_ARENA, + EAX30_PRESET_HANGAR, + EAX30_PRESET_CARPETTEDHALLWAY, + EAX30_PRESET_HALLWAY, + EAX30_PRESET_STONECORRIDOR, + EAX30_PRESET_ALLEY, + EAX30_PRESET_FOREST, + EAX30_PRESET_CITY, + EAX30_PRESET_MOUNTAINS, + EAX30_PRESET_QUARRY, + EAX30_PRESET_PLAIN, + EAX30_PRESET_PARKINGLOT, + EAX30_PRESET_SEWERPIPE, + EAX30_PRESET_UNDERWATER, + EAX30_PRESET_DRUGGED, + EAX30_PRESET_DIZZY, + EAX30_PRESET_PSYCHOTIC +}; + +/********************************************************************************************************/ + +////////////////////////////////////////////////////// +// Array of sport environment names // +////////////////////////////////////////////////////// + +const char* EAX30_SPORTS_PRESET_NAMES[] = +{ + "Empty Stadium", + "Full Stadium", + "Stadium Tannoy", + "Squash Court", + "Small Swimming Pool", + "Large Swimming Pool", + "Gymnasium" +}; + +////////////////////////////////////////////////////// +// Sports effects matrix // +////////////////////////////////////////////////////// + +EAXLISTENERPROPERTIES EAX30_SPORTS_PRESETS[] = +{ + EAX30_PRESET_SPORT_EMPTYSTADIUM, + EAX30_PRESET_SPORT_FULLSTADIUM, + EAX30_PRESET_SPORT_STADIUMTANNOY, + EAX30_PRESET_SPORT_SQUASHCOURT, + EAX30_PRESET_SPORT_SMALLSWIMMINGPOOL, + EAX30_PRESET_SPORT_LARGESWIMMINGPOOL, + EAX30_PRESET_SPORT_GYMNASIUM +}; + +/********************************************************************************************************/ + +////////////////////////////////////////////////////// +// Array of prefab environment names // +////////////////////////////////////////////////////// + +const char* EAX30_PREFAB_PRESET_NAMES[] = +{ + "Workshop", + "School Room", + "Practise Room", + "Outhouse", + "Caravan" +}; + +////////////////////////////////////////////////////// +// Prefab effects matrix // +////////////////////////////////////////////////////// + +EAXLISTENERPROPERTIES EAX30_PREFAB_PRESETS[] = +{ + EAX30_PRESET_PREFAB_WORKSHOP, + EAX30_PRESET_PREFAB_SCHOOLROOM, + EAX30_PRESET_PREFAB_PRACTISEROOM, + EAX30_PRESET_PREFAB_OUTHOUSE, + EAX30_PRESET_PREFAB_CARAVAN +}; + +/********************************************************************************************************/ + +////////////////////////////////////////////////////// +// Array of Domes & Pipes environment names // +////////////////////////////////////////////////////// + +const char* EAX30_DOMESNPIPES_PRESET_NAMES[] = +{ + "Domed Tomb", + "Saint Paul's Dome", + "Small Pipe", + "Long Thin Pipe", + "Large Pipe", + "Resonant Pipe" +}; + +////////////////////////////////////////////////////// +// Domes & Pipes effects matrix // +////////////////////////////////////////////////////// + +EAXLISTENERPROPERTIES EAX30_DOMESNPIPES_PRESETS[] = +{ + EAX30_PRESET_DOME_TOMB, + EAX30_PRESET_DOME_SAINTPAULS, + EAX30_PRESET_PIPE_SMALL, + EAX30_PRESET_PIPE_LONGTHIN, + EAX30_PRESET_PIPE_LARGE, + EAX30_PRESET_PIPE_RESONANT +}; + +/********************************************************************************************************/ + +////////////////////////////////////////////////////// +// Array of Outdoors environment names // +////////////////////////////////////////////////////// + +const char* EAX30_OUTDOORS_PRESET_NAMES[] = +{ + "Backyard", + "Rolling Plains", + "Deep Canyon", + "Creek", + "Valley" +}; + +////////////////////////////////////////////////////// +// Outdoors effects matrix // +////////////////////////////////////////////////////// + +EAXLISTENERPROPERTIES EAX30_OUTDOORS_PRESETS[] = +{ + EAX30_PRESET_OUTDOORS_BACKYARD, + EAX30_PRESET_OUTDOORS_ROLLINGPLAINS, + EAX30_PRESET_OUTDOORS_DEEPCANYON, + EAX30_PRESET_OUTDOORS_CREEK, + EAX30_PRESET_OUTDOORS_VALLEY +}; + +/********************************************************************************************************/ + +////////////////////////////////////////////////////// +// Array of Mood environment names // +////////////////////////////////////////////////////// + +const char* EAX30_MOOD_PRESET_NAMES[] = +{ + "Heaven", + "Hell", + "Memory" +}; + +////////////////////////////////////////////////////// +// Mood effects matrix // +////////////////////////////////////////////////////// + +EAXLISTENERPROPERTIES EAX30_MOOD_PRESETS[] = +{ + EAX30_PRESET_MOOD_HEAVEN, + EAX30_PRESET_MOOD_HELL, + EAX30_PRESET_MOOD_MEMORY +}; + +/********************************************************************************************************/ + +////////////////////////////////////////////////////// +// Array of driving environment names // +////////////////////////////////////////////////////// + +const char* EAX30_DRIVING_PRESET_NAMES[] = +{ + "Race Commentator", + "Pit Garage", + "In-car (Stripped out racer)", + "In-car (Sportscar)", + "In-car (Luxury)", + "Full Grandstand", + "Empty Grandstand", + "Tunnel" +}; + +////////////////////////////////////////////////////// +// Driving effects matrix // +////////////////////////////////////////////////////// + +EAXLISTENERPROPERTIES EAX30_DRIVING_PRESETS[] = +{ + EAX30_PRESET_DRIVING_COMMENTATOR, + EAX30_PRESET_DRIVING_PITGARAGE, + EAX30_PRESET_DRIVING_INCAR_RACER, + EAX30_PRESET_DRIVING_INCAR_SPORTS, + EAX30_PRESET_DRIVING_INCAR_LUXURY, + EAX30_PRESET_DRIVING_FULLGRANDSTAND, + EAX30_PRESET_DRIVING_EMPTYGRANDSTAND, + EAX30_PRESET_DRIVING_TUNNEL +}; + +/********************************************************************************************************/ + +////////////////////////////////////////////////////// +// Array of City environment names // +////////////////////////////////////////////////////// + +const char* EAX30_CITY_PRESET_NAMES[] = +{ + "City Streets", + "Subway", + "Museum", + "Library", + "Underpass", + "Abandoned City" +}; + +////////////////////////////////////////////////////// +// City effects matrix // +////////////////////////////////////////////////////// + +EAXLISTENERPROPERTIES EAX30_CITY_PRESETS[] = +{ + EAX30_PRESET_CITY_STREETS, + EAX30_PRESET_CITY_SUBWAY, + EAX30_PRESET_CITY_MUSEUM, + EAX30_PRESET_CITY_LIBRARY, + EAX30_PRESET_CITY_UNDERPASS, + EAX30_PRESET_CITY_ABANDONED +}; + +/********************************************************************************************************/ + +////////////////////////////////////////////////////// +// Array of Misc environment names // +////////////////////////////////////////////////////// + +const char* EAX30_MISC_PRESET_NAMES[] = +{ + "Dusty Box Room", + "Chapel", + "Small Water Room" +}; + +////////////////////////////////////////////////////// +// Misc effects matrix // +////////////////////////////////////////////////////// + +EAXLISTENERPROPERTIES EAX30_MISC_PRESETS[] = +{ + EAX30_PRESET_DUSTYROOM, + EAX30_PRESET_CHAPEL, + EAX30_PRESET_SMALLWATERROOM +}; + diff --git a/src/miami/audio/eax/eax-util.h b/src/miami/audio/eax/eax-util.h new file mode 100644 index 00000000..441f0115 --- /dev/null +++ b/src/miami/audio/eax/eax-util.h @@ -0,0 +1,765 @@ +/*******************************************************************\ +* * +* EAX-UTIL.H - utilities for Environmental Audio Extensions v. 3.0 * +* Definitions of the Original 26 EAX Presets * +* Definitions for some new EAX Presets * +* Definitions of some Material Presets * +* Function declaration for EAX Morphing * +* * +\*******************************************************************/ + +#ifndef EAXUTIL_INCLUDED +#define EAXUTIL_INCLUDED + +#include + +/*********************************************************************************************** +* Function : EAX3ListenerInterpolate +* Params : lpStart - Initial EAX 3 Listener parameters +* : lpFinish - Final EAX 3 Listener parameters +* : flRatio - Ratio Destination : Source (0.0 == Source, 1.0 == Destination) +* : lpResult - Interpolated EAX 3 Listener parameters +* : bCheckValues - Check EAX 3.0 parameters are in range, + - default == false (no checking) +************************************************************************************************/ +bool EAX3ListenerInterpolate(EAXLISTENERPROPERTIES *lpStartEAX3LP, EAXLISTENERPROPERTIES *lpFinishEAX3LP, + float flRatio, EAXLISTENERPROPERTIES *lpResultEAX3LP, bool bCheckValues = false); + + +/***********************************************************************************************\ +* +* Legacy environment presets for use with DSPROPERTY_EAXLISTENER_ALLPARAMETERS. +* Each array conforms to the DSPROPSETID_EAX30_ListenerProperties structure defined in EAX.H. +* +************************************************************************************************/ + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define EAX30_PRESET_GENERIC \ + {0, 7.5f, 1.000f, -1000, -100, 0, 1.49f, 0.83f, 1.00f, -2602, 0.007f, 0.00f,0.00f,0.00f, 200, 0.011f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_PADDEDCELL \ + {1, 1.4f, 1.000f, -1000, -6000, 0, 0.17f, 0.10f, 1.00f, -1204, 0.001f, 0.00f,0.00f,0.00f, 207, 0.002f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_ROOM \ + {2, 1.9f, 1.000f, -1000, -454, 0, 0.40f, 0.83f, 1.00f, -1646, 0.002f, 0.00f,0.00f,0.00f, 53, 0.003f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_BATHROOM \ + {3, 1.4f, 1.000f, -1000, -1200, 0, 1.49f, 0.54f, 1.00f, -370, 0.007f, 0.00f,0.00f,0.00f, 1030, 0.011f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_LIVINGROOM \ + {4, 2.5f, 1.000f, -1000, -6000, 0, 0.50f, 0.10f, 1.00f, -1376, 0.003f, 0.00f,0.00f,0.00f, -1104, 0.004f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_STONEROOM \ + {5, 11.6f, 1.000f, -1000, -300, 0, 2.31f, 0.64f, 1.00f, -711, 0.012f, 0.00f,0.00f,0.00f, 83, 0.017f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_AUDITORIUM \ + {6, 21.6f, 1.000f, -1000, -476, 0, 4.32f, 0.59f, 1.00f, -789, 0.020f, 0.00f,0.00f,0.00f, -289, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_CONCERTHALL \ + {7, 19.6f, 1.000f, -1000, -500, 0, 3.92f, 0.70f, 1.00f, -1230, 0.020f, 0.00f,0.00f,0.00f, -02, 0.029f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_CAVE \ + {8, 14.6f, 1.000f, -1000, 0, 0, 2.91f, 1.30f, 1.00f, -602, 0.015f, 0.00f,0.00f,0.00f, -302, 0.022f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } +#define EAX30_PRESET_ARENA \ + {9, 36.2f, 1.000f, -1000, -698, 0, 7.24f, 0.33f, 1.00f, -1166, 0.020f, 0.00f,0.00f,0.00f, 16, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_HANGAR \ + {10, 50.3f, 1.000f, -1000, -1000, 0, 10.05f, 0.23f, 1.00f, -602, 0.020f, 0.00f,0.00f,0.00f, 198, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_CARPETTEDHALLWAY \ + {11, 1.9f, 1.000f, -1000, -4000, 0, 0.30f, 0.10f, 1.00f, -1831, 0.002f, 0.00f,0.00f,0.00f, -1630, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_HALLWAY \ + {12, 1.8f, 1.000f, -1000, -300, 0, 1.49f, 0.59f, 1.00f, -1219, 0.007f, 0.00f,0.00f,0.00f, 441, 0.011f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_STONECORRIDOR \ + {13, 13.5f, 1.000f, -1000, -237, 0, 2.70f, 0.79f, 1.00f, -1214, 0.013f, 0.00f,0.00f,0.00f, 395, 0.020f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_ALLEY \ + {14, 7.5f, 0.300f, -1000, -270, 0, 1.49f, 0.86f, 1.00f, -1204, 0.007f, 0.00f,0.00f,0.00f, -4, 0.011f, 0.00f,0.00f,0.00f, 0.125f, 0.950f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_FOREST \ + {15, 38.0f, 0.300f, -1000, -3300, 0, 1.49f, 0.54f, 1.00f, -2560, 0.162f, 0.00f,0.00f,0.00f, -229, 0.088f, 0.00f,0.00f,0.00f, 0.125f, 1.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_CITY \ + {16, 7.5f, 0.500f, -1000, -800, 0, 1.49f, 0.67f, 1.00f, -2273, 0.007f, 0.00f,0.00f,0.00f, -1691, 0.011f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_MOUNTAINS \ + {17, 100.0f, 0.270f, -1000, -2500, 0, 1.49f, 0.21f, 1.00f, -2780, 0.300f, 0.00f,0.00f,0.00f, -1434, 0.100f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } +#define EAX30_PRESET_QUARRY \ + {18, 17.5f, 1.000f, -1000, -1000, 0, 1.49f, 0.83f, 1.00f, -10000, 0.061f, 0.00f,0.00f,0.00f, 500, 0.025f, 0.00f,0.00f,0.00f, 0.125f, 0.700f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_PLAIN \ + {19, 42.5f, 0.210f, -1000, -2000, 0, 1.49f, 0.50f, 1.00f, -2466, 0.179f, 0.00f,0.00f,0.00f, -1926, 0.100f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_PARKINGLOT \ + {20, 8.3f, 1.000f, -1000, 0, 0, 1.65f, 1.50f, 1.00f, -1363, 0.008f, 0.00f,0.00f,0.00f, -1153, 0.012f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } +#define EAX30_PRESET_SEWERPIPE \ + {21, 1.7f, 0.800f, -1000, -1000, 0, 2.81f, 0.14f, 1.00f, 429, 0.014f, 0.00f,0.00f,0.00f, 1023, 0.021f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_UNDERWATER \ + {22, 1.8f, 1.000f, -1000, -4000, 0, 1.49f, 0.10f, 1.00f, -449, 0.007f, 0.00f,0.00f,0.00f, 1700, 0.011f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 1.180f, 0.348f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_DRUGGED \ + {23, 1.9f, 0.500f, -1000, 0, 0, 8.39f, 1.39f, 1.00f, -115, 0.002f, 0.00f,0.00f,0.00f, 985, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 1.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } +#define EAX30_PRESET_DIZZY \ + {24, 1.8f, 0.600f, -1000, -400, 0, 17.23f, 0.56f, 1.00f, -1713, 0.020f, 0.00f,0.00f,0.00f, -613, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 0.810f, 0.310f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } +#define EAX30_PRESET_PSYCHOTIC \ + {25, 1.0f, 0.500f, -1000, -151, 0, 7.56f, 0.91f, 1.00f, -626, 0.020f, 0.00f,0.00f,0.00f, 774, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 4.000f, 1.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } + + +/***********************************************************************************************\ +* +* New environment presets for use with DSPROPERTY_EAXLISTENER_ALLPARAMETERS. +* Each array conforms to the DSPROPSETID_EAX30_ListenerProperties structure defined in EAX.H. +* +************************************************************************************************/ + +// STANDARDISED-LOCATION SCENARIOS + +// CASTLE PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define EAX30_PRESET_CASTLE_SMALLROOM \ + { 26, 8.3f, 0.890f, -1100, -800, -2000, 1.22f, 0.83f, 0.31f, -100, 0.022f, 0.00f,0.00f,0.00f, 0, 0.011f, 0.00f,0.00f,0.00f, 0.138f, 0.080f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } +#define EAX30_PRESET_CASTLE_SHORTPASSAGE \ + { 26, 8.3f, 0.890f, -1000, -1000, -2000, 2.32f, 0.83f, 0.31f, -100, 0.007f, 0.00f,0.00f,0.00f, -500, 0.023f, 0.00f,0.00f,0.00f, 0.138f, 0.080f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } +#define EAX30_PRESET_CASTLE_MEDIUMROOM \ + { 26, 8.3f, 0.930f, -1000, -1100, -2000, 2.04f, 0.83f, 0.46f, -300, 0.022f, 0.00f,0.00f,0.00f, -200, 0.011f, 0.00f,0.00f,0.00f, 0.155f, 0.030f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } +#define EAX30_PRESET_CASTLE_LONGPASSAGE \ + { 26, 8.3f, 0.890f, -1000, -800, -2000, 3.42f, 0.83f, 0.31f, -200, 0.007f, 0.00f,0.00f,0.00f, -600, 0.023f, 0.00f,0.00f,0.00f, 0.138f, 0.080f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } +#define EAX30_PRESET_CASTLE_LARGEROOM \ + { 26, 8.3f, 0.820f, -1000, -1100, -1800, 2.53f, 0.83f, 0.50f, -900, 0.034f, 0.00f,0.00f,0.00f, -400, 0.016f, 0.00f,0.00f,0.00f, 0.185f, 0.070f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } +#define EAX30_PRESET_CASTLE_HALL \ + { 26, 8.3f, 0.810f, -1000, -1100, -1500, 3.14f, 0.79f, 0.62f, -1300, 0.056f, 0.00f,0.00f,0.00f, -500, 0.024f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } +#define EAX30_PRESET_CASTLE_CUPBOARD \ + { 26, 8.3f, 0.890f, -1000, -1100, -2000, 0.67f, 0.87f, 0.31f, 300, 0.010f, 0.00f,0.00f,0.00f, 300, 0.007f, 0.00f,0.00f,0.00f, 0.138f, 0.080f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } +#define EAX30_PRESET_CASTLE_COURTYARD \ + { 26, 8.3f, 0.420f, -1100, -700, -900, 2.13f, 0.61f, 0.23f, -2300, 0.112f, 0.00f,0.00f,0.00f, -1500, 0.036f, 0.00f,0.00f,0.00f, 0.250f, 0.370f, 0.250f, 0.000f, -0.0f, 5000.0f, 250.0f, 0.00f, 0x1f } +#define EAX30_PRESET_CASTLE_ALCOVE \ + { 26, 8.3f, 0.890f, -1000, -600, -2000, 1.64f, 0.87f, 0.31f, -100, 0.007f, 0.00f,0.00f,0.00f, -500, 0.034f, 0.00f,0.00f,0.00f, 0.138f, 0.080f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } + + +// FACTORY PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define EAX30_PRESET_FACTORY_ALCOVE \ + { 26, 1.8f, 0.590f, -1200, -200, -600, 3.14f, 0.65f, 1.31f, 300, 0.010f, 0.00f,0.00f,0.00f, -1200, 0.038f, 0.00f,0.00f,0.00f, 0.114f, 0.100f, 0.250f, 0.000f, -0.0f, 3762.6f, 362.5f, 0.00f, 0x20 } +#define EAX30_PRESET_FACTORY_SHORTPASSAGE \ + { 26, 1.8f, 0.640f, -1200, -200, -600, 2.53f, 0.65f, 1.31f, 0, 0.010f, 0.00f,0.00f,0.00f, -600, 0.038f, 0.00f,0.00f,0.00f, 0.135f, 0.230f, 0.250f, 0.000f, -0.0f, 3762.6f, 362.5f, 0.00f, 0x20 } +#define EAX30_PRESET_FACTORY_MEDIUMROOM \ + { 26, 1.9f, 0.820f, -1200, -200, -600, 2.76f, 0.65f, 1.31f, -1100, 0.022f, 0.00f,0.00f,0.00f, -400, 0.023f, 0.00f,0.00f,0.00f, 0.174f, 0.070f, 0.250f, 0.000f, -0.0f, 3762.6f, 362.5f, 0.00f, 0x20 } +#define EAX30_PRESET_FACTORY_LONGPASSAGE \ + { 26, 1.8f, 0.640f, -1200, -200, -600, 4.06f, 0.65f, 1.31f, 0, 0.020f, 0.00f,0.00f,0.00f, -900, 0.037f, 0.00f,0.00f,0.00f, 0.135f, 0.230f, 0.250f, 0.000f, -0.0f, 3762.6f, 362.5f, 0.00f, 0x20 } +#define EAX30_PRESET_FACTORY_LARGEROOM \ + { 26, 1.9f, 0.750f, -1200, -300, -400, 4.24f, 0.51f, 1.31f, -1500, 0.039f, 0.00f,0.00f,0.00f, -600, 0.023f, 0.00f,0.00f,0.00f, 0.231f, 0.070f, 0.250f, 0.000f, -0.0f, 3762.6f, 362.5f, 0.00f, 0x20 } +#define EAX30_PRESET_FACTORY_HALL \ + { 26, 1.9f, 0.750f, -1000, -300, -400, 7.43f, 0.51f, 1.31f, -2400, 0.073f, 0.00f,0.00f,0.00f, -500, 0.027f, 0.00f,0.00f,0.00f, 0.250f, 0.070f, 0.250f, 0.000f, -0.0f, 3762.6f, 362.5f, 0.00f, 0x20 } +#define EAX30_PRESET_FACTORY_CUPBOARD \ + { 26, 1.7f, 0.630f, -1200, -200, -600, 0.49f, 0.65f, 1.31f, 200, 0.010f, 0.00f,0.00f,0.00f, 200, 0.032f, 0.00f,0.00f,0.00f, 0.107f, 0.070f, 0.250f, 0.000f, -0.0f, 3762.6f, 362.5f, 0.00f, 0x20 } +#define EAX30_PRESET_FACTORY_COURTYARD \ + { 26, 1.7f, 0.570f, -1000, -1000, -400, 2.32f, 0.29f, 0.56f, -2400, 0.090f, 0.00f,0.00f,0.00f, -2000, 0.039f, 0.00f,0.00f,0.00f, 0.250f, 0.290f, 0.250f, 0.000f, -0.0f, 3762.6f, 362.5f, 0.00f, 0x20 } +#define EAX30_PRESET_FACTORY_SMALLROOM \ + { 26, 1.8f, 0.820f, -1200, -200, -600, 1.72f, 0.65f, 1.31f, -300, 0.010f, 0.00f,0.00f,0.00f, -200, 0.024f, 0.00f,0.00f,0.00f, 0.119f, 0.070f, 0.250f, 0.000f, -0.0f, 3762.6f, 362.5f, 0.00f, 0x20 } + +// ICE PALACE PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define EAX30_PRESET_ICEPALACE_ALCOVE \ + { 26, 2.7f, 0.840f, -1000, -500, -1100, 2.76f, 1.46f, 0.28f, 100, 0.010f, 0.00f,0.00f,0.00f, -1200, 0.030f, 0.00f,0.00f,0.00f, 0.161f, 0.090f, 0.250f, 0.000f, -0.0f, 12428.5f, 99.6f, 0.00f, 0x20 } +#define EAX30_PRESET_ICEPALACE_SHORTPASSAGE \ + { 26, 2.7f, 0.750f, -1000, -500, -1100, 1.79f, 1.46f, 0.28f, -600, 0.010f, 0.00f,0.00f,0.00f, -700, 0.019f, 0.00f,0.00f,0.00f, 0.177f, 0.090f, 0.250f, 0.000f, -0.0f, 12428.5f, 99.6f, 0.00f, 0x20 } +#define EAX30_PRESET_ICEPALACE_MEDIUMROOM \ + { 26, 2.7f, 0.870f, -1000, -500, -700, 2.22f, 1.53f, 0.32f, -800, 0.039f, 0.00f,0.00f,0.00f, -1200, 0.027f, 0.00f,0.00f,0.00f, 0.186f, 0.120f, 0.250f, 0.000f, -0.0f, 12428.5f, 99.6f, 0.00f, 0x20 } +#define EAX30_PRESET_ICEPALACE_LONGPASSAGE \ + { 26, 2.7f, 0.770f, -1000, -500, -800, 3.01f, 1.46f, 0.28f, -200, 0.012f, 0.00f,0.00f,0.00f, -800, 0.025f, 0.00f,0.00f,0.00f, 0.186f, 0.040f, 0.250f, 0.000f, -0.0f, 12428.5f, 99.6f, 0.00f, 0x20 } +#define EAX30_PRESET_ICEPALACE_LARGEROOM \ + { 26, 2.9f, 0.810f, -1000, -500, -700, 3.14f, 1.53f, 0.32f, -1200, 0.039f, 0.00f,0.00f,0.00f, -1300, 0.027f, 0.00f,0.00f,0.00f, 0.214f, 0.110f, 0.250f, 0.000f, -0.0f, 12428.5f, 99.6f, 0.00f, 0x20 } +#define EAX30_PRESET_ICEPALACE_HALL \ + { 26, 2.9f, 0.760f, -1000, -700, -500, 5.49f, 1.53f, 0.38f, -1900, 0.054f, 0.00f,0.00f,0.00f, -1400, 0.052f, 0.00f,0.00f,0.00f, 0.226f, 0.110f, 0.250f, 0.000f, -0.0f, 12428.5f, 99.6f, 0.00f, 0x20 } +#define EAX30_PRESET_ICEPALACE_CUPBOARD \ + { 26, 2.7f, 0.830f, -1000, -600, -1300, 0.76f, 1.53f, 0.26f, 100, 0.012f, 0.00f,0.00f,0.00f, 100, 0.016f, 0.00f,0.00f,0.00f, 0.143f, 0.080f, 0.250f, 0.000f, -0.0f, 12428.5f, 99.6f, 0.00f, 0x20 } +#define EAX30_PRESET_ICEPALACE_COURTYARD \ + { 26, 2.9f, 0.590f, -1000, -1100, -1000, 2.04f, 1.20f, 0.38f, -2000, 0.073f, 0.00f,0.00f,0.00f, -2200, 0.043f, 0.00f,0.00f,0.00f, 0.235f, 0.480f, 0.250f, 0.000f, -0.0f, 12428.5f, 99.6f, 0.00f, 0x20 } +#define EAX30_PRESET_ICEPALACE_SMALLROOM \ + { 26, 2.7f, 0.840f, -1000, -500, -1100, 1.51f, 1.53f, 0.27f, -100, 0.010f, 0.00f,0.00f,0.00f, -900, 0.011f, 0.00f,0.00f,0.00f, 0.164f, 0.140f, 0.250f, 0.000f, -0.0f, 12428.5f, 99.6f, 0.00f, 0x20 } + +// SPACE STATION PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define EAX30_PRESET_SPACESTATION_ALCOVE \ + { 26, 1.5f, 0.780f, -1100, -300, -100, 1.16f, 0.81f, 0.55f, 300, 0.007f, 0.00f,0.00f,0.00f, -500, 0.018f, 0.00f,0.00f,0.00f, 0.192f, 0.210f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } +#define EAX30_PRESET_SPACESTATION_MEDIUMROOM \ + { 26, 1.5f, 0.750f, -1000, -400, -100, 3.01f, 0.50f, 0.55f, -1000, 0.034f, 0.00f,0.00f,0.00f, -700, 0.035f, 0.00f,0.00f,0.00f, 0.209f, 0.310f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } +#define EAX30_PRESET_SPACESTATION_SHORTPASSAGE \ + { 26, 1.5f, 0.870f, -1000, -400, -100, 3.57f, 0.50f, 0.55f, 0, 0.012f, 0.00f,0.00f,0.00f, -600, 0.016f, 0.00f,0.00f,0.00f, 0.172f, 0.200f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } +#define EAX30_PRESET_SPACESTATION_LONGPASSAGE \ + { 26, 1.9f, 0.820f, -1000, -400, -100, 4.62f, 0.62f, 0.55f, 0, 0.012f, 0.00f,0.00f,0.00f, -800, 0.031f, 0.00f,0.00f,0.00f, 0.250f, 0.230f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } +#define EAX30_PRESET_SPACESTATION_LARGEROOM \ + { 26, 1.8f, 0.810f, -1000, -400, -100, 3.89f, 0.38f, 0.61f, -1200, 0.056f, 0.00f,0.00f,0.00f, -800, 0.035f, 0.00f,0.00f,0.00f, 0.233f, 0.280f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } +#define EAX30_PRESET_SPACESTATION_HALL \ + { 26, 1.9f, 0.870f, -1000, -400, -100, 7.11f, 0.38f, 0.61f, -1500, 0.100f, 0.00f,0.00f,0.00f, -1000, 0.047f, 0.00f,0.00f,0.00f, 0.250f, 0.250f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } +#define EAX30_PRESET_SPACESTATION_CUPBOARD \ + { 26, 1.4f, 0.560f, -1000, -300, -100, 0.79f, 0.81f, 0.55f, 200, 0.007f, 0.00f,0.00f,0.00f, 400, 0.018f, 0.00f,0.00f,0.00f, 0.181f, 0.310f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } +#define EAX30_PRESET_SPACESTATION_SMALLROOM \ + { 26, 1.5f, 0.700f, -1000, -300, -100, 1.72f, 0.82f, 0.55f, -400, 0.007f, 0.00f,0.00f,0.00f, -500, 0.013f, 0.00f,0.00f,0.00f, 0.188f, 0.260f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } + +// WOODEN GALLEON PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define EAX30_PRESET_WOODEN_ALCOVE \ + { 26, 7.5f, 1.000f, -1100, -1800, -1000, 1.22f, 0.62f, 0.91f, -100, 0.012f, 0.00f,0.00f,0.00f, -600, 0.024f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } +#define EAX30_PRESET_WOODEN_SHORTPASSAGE \ + { 26, 7.5f, 1.000f, -1100, -1800, -1000, 1.45f, 0.50f, 0.87f, -300, 0.012f, 0.00f,0.00f,0.00f, -700, 0.024f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } +#define EAX30_PRESET_WOODEN_MEDIUMROOM \ + { 26, 7.5f, 1.000f, -1200, -2000, -1100, 1.07f, 0.42f, 0.82f, -300, 0.039f, 0.00f,0.00f,0.00f, -400, 0.029f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } +#define EAX30_PRESET_WOODEN_LONGPASSAGE \ + { 26, 7.5f, 1.000f, -1100, -2000, -1000, 1.79f, 0.40f, 0.79f, -200, 0.020f, 0.00f,0.00f,0.00f, -1000, 0.036f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } +#define EAX30_PRESET_WOODEN_LARGEROOM \ + { 26, 7.5f, 1.000f, -1200, -2100, -1100, 1.45f, 0.33f, 0.82f, -300, 0.056f, 0.00f,0.00f,0.00f, -500, 0.049f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } +#define EAX30_PRESET_WOODEN_HALL \ + { 26, 7.5f, 1.000f, -1200, -2200, -1100, 1.95f, 0.30f, 0.82f, -300, 0.068f, 0.00f,0.00f,0.00f, -500, 0.063f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } +#define EAX30_PRESET_WOODEN_CUPBOARD \ + { 26, 7.5f, 1.000f, -1000, -1700, -1000, 0.56f, 0.46f, 0.91f, -100, 0.012f, 0.00f,0.00f,0.00f, -100, 0.028f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } +#define EAX30_PRESET_WOODEN_SMALLROOM \ + { 26, 7.5f, 1.000f, -1200, -1900, -1000, 0.79f, 0.32f, 0.87f, -200, 0.032f, 0.00f,0.00f,0.00f, -300, 0.029f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } +#define EAX30_PRESET_WOODEN_COURTYARD \ + { 26, 7.5f, 0.650f, -1700, -2200, -1000, 1.79f, 0.35f, 0.79f, -700, 0.063f, 0.00f,0.00f,0.00f, -2300, 0.032f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } + + +// OTHER SCENARIOS + +// SPORTS PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define EAX30_PRESET_SPORT_EMPTYSTADIUM \ + { 26, 7.2f, 1.000f, -1300, -700, -200, 6.26f, 0.51f, 1.10f, -2400, 0.183f, 0.00f,0.00f,0.00f, -1100, 0.038f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x20 } +#define EAX30_PRESET_SPORT_SQUASHCOURT \ + { 26, 7.5f, 0.750f, -1100, -1000, -200, 2.22f, 0.91f, 1.16f, -700, 0.007f, 0.00f,0.00f,0.00f, -300, 0.011f, 0.00f,0.00f,0.00f, 0.126f, 0.190f, 0.250f, 0.000f, -0.0f, 7176.9f, 211.2f, 0.00f, 0x20 } +#define EAX30_PRESET_SPORT_SMALLSWIMMINGPOOL \ + { 26, 36.2f, 0.700f, -1400, -200, -100, 2.76f, 1.25f, 1.14f, -400, 0.020f, 0.00f,0.00f,0.00f, -300, 0.030f, 0.00f,0.00f,0.00f, 0.179f, 0.150f, 0.895f, 0.190f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x0 } +#define EAX30_PRESET_SPORT_LARGESWIMMINGPOOL\ + { 26, 36.2f, 0.820f, -1200, -200, 0, 5.49f, 1.31f, 1.14f, -700, 0.039f, 0.00f,0.00f,0.00f, -800, 0.049f, 0.00f,0.00f,0.00f, 0.222f, 0.550f, 1.159f, 0.210f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x0 } +#define EAX30_PRESET_SPORT_GYMNASIUM \ + { 26, 7.5f, 0.810f, -1200, -700, -100, 3.14f, 1.06f, 1.35f, -800, 0.029f, 0.00f,0.00f,0.00f, -700, 0.045f, 0.00f,0.00f,0.00f, 0.146f, 0.140f, 0.250f, 0.000f, -0.0f, 7176.9f, 211.2f, 0.00f, 0x20 } +#define EAX30_PRESET_SPORT_FULLSTADIUM \ + { 26, 7.2f, 1.000f, -1300, -2300, -200, 5.25f, 0.17f, 0.80f, -2000, 0.188f, 0.00f,0.00f,0.00f, -1300, 0.038f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x20 } +#define EAX30_PRESET_SPORT_STADIUMTANNOY \ + { 26, 3.0f, 0.780f, -900, -500, -600, 2.53f, 0.88f, 0.68f, -1100, 0.230f, 0.00f,0.00f,0.00f, -600, 0.063f, 0.00f,0.00f,0.00f, 0.250f, 0.200f, 0.250f, 0.000f, -0.0f, 5000.0f, 250.0f, 0.00f, 0x20 } + +// PREFAB PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define EAX30_PRESET_PREFAB_WORKSHOP \ + { 26, 1.9f, 1.000f, -1000, -1700, -800, 0.76f, 1.00f, 1.00f, 0, 0.012f, 0.00f,0.00f,0.00f, -200, 0.012f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x0 } +#define EAX30_PRESET_PREFAB_SCHOOLROOM \ + { 26, 1.86f, 0.690f, -1100, -400, -600, 0.98f, 0.45f, 0.18f, 300, 0.017f, 0.00f,0.00f,0.00f, 0, 0.015f, 0.00f,0.00f,0.00f, 0.095f, 0.140f, 0.250f, 0.000f, -0.0f, 7176.9f, 211.2f, 0.00f, 0x20 } +#define EAX30_PRESET_PREFAB_PRACTISEROOM \ + { 26, 1.86f, 0.870f, -1000, -800, -600, 1.12f, 0.56f, 0.18f, 200, 0.010f, 0.00f,0.00f,0.00f, -200, 0.011f, 0.00f,0.00f,0.00f, 0.095f, 0.140f, 0.250f, 0.000f, -0.0f, 7176.9f, 211.2f, 0.00f, 0x20 } +#define EAX30_PRESET_PREFAB_OUTHOUSE \ + { 26, 80.3f, 0.820f, -1100, -1900, -1600, 1.38f, 0.38f, 0.35f, -100, 0.024f, 0.00f,0.00f,-0.00f, -800, 0.044f, 0.00f,0.00f,0.00f, 0.121f, 0.170f, 0.250f, 0.000f, -0.0f, 2854.4f, 107.5f, 0.00f, 0x0 } +#define EAX30_PRESET_PREFAB_CARAVAN \ + { 26, 8.3f, 1.000f, -1000, -2100, -1800, 0.43f, 1.50f, 1.00f, 0, 0.012f, 0.00f,0.00f,0.00f, 400, 0.012f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } + // for US developers, a caravan is the same as a trailer =o) + + +// DOME AND PIPE PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define EAX30_PRESET_DOME_TOMB \ + { 26, 51.8f, 0.790f, -1000, -900, -1300, 4.18f, 0.21f, 0.10f, -825, 0.030f, 0.00f,0.00f,0.00f, -125, 0.022f, 0.00f,0.00f,0.00f, 0.177f, 0.190f, 0.250f, 0.000f, -5.0f, 2854.4f, 20.0f, 0.00f, 0x0 } +#define EAX30_PRESET_PIPE_SMALL \ + { 26, 50.3f, 1.000f, -1000, -900, -1300, 5.04f, 0.10f, 0.10f, -600, 0.032f, 0.00f,0.00f,0.00f, 400, 0.015f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 2854.4f, 20.0f, 0.00f, 0x3f } +#define EAX30_PRESET_DOME_SAINTPAULS \ + { 26, 50.3f, 0.870f, -1000, -900, -1300, 10.48f, 0.19f, 0.10f, -1500, 0.090f, 0.00f,0.00f,0.00f, -500, 0.042f, 0.00f,0.00f,0.00f, 0.250f, 0.120f, 0.250f, 0.000f, -5.0f, 2854.4f, 20.0f, 0.00f, 0x3f } +#define EAX30_PRESET_PIPE_LONGTHIN \ + { 26, 1.6f, 0.910f, -1200, -700, -1100, 9.21f, 0.18f, 0.10f, -300, 0.010f, 0.00f,0.00f,0.00f, -1000, 0.022f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 2854.4f, 20.0f, 0.00f, 0x0 } +#define EAX30_PRESET_PIPE_LARGE \ + { 26, 50.3f, 1.000f, -1000, -900, -1300, 8.45f, 0.10f, 0.10f, -800, 0.046f, 0.00f,0.00f,0.00f, 0, 0.032f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 2854.4f, 20.0f, 0.00f, 0x3f } +#define EAX30_PRESET_PIPE_RESONANT \ + { 26, 1.3f, 0.910f, -1200, -700, -1100, 6.81f, 0.18f, 0.10f, -300, 0.010f, 0.00f,0.00f,0.00f, -700, 0.022f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 2854.4f, 20.0f, 0.00f, 0x0 } + +// OUTDOORS PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define EAX30_PRESET_OUTDOORS_BACKYARD \ + { 26, 80.3f, 0.450f, -1100, -1200, -600, 1.12f, 0.34f, 0.46f, -1100, 0.049f, 0.00f,0.00f,-0.00f, -1300, 0.023f, 0.00f,0.00f,0.00f, 0.218f, 0.340f, 0.250f, 0.000f, -5.0f, 4399.1f, 242.9f, 0.00f, 0x0 } +#define EAX30_PRESET_OUTDOORS_ROLLINGPLAINS \ + { 26, 80.3f, 0.000f, -1100, -3900, -400, 2.13f, 0.21f, 0.46f, -2000, 0.300f, 0.00f,0.00f,-0.00f, -1500, 0.019f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 0.250f, 0.000f, -5.0f, 4399.1f, 242.9f, 0.00f, 0x0 } +#define EAX30_PRESET_OUTDOORS_DEEPCANYON \ + { 26, 80.3f, 0.740f, -1100, -1500, -400, 3.89f, 0.21f, 0.46f, -2000, 0.193f, 0.00f,0.00f,-0.00f, -1100, 0.019f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 0.250f, 0.000f, -5.0f, 4399.1f, 242.9f, 0.00f, 0x0 } +#define EAX30_PRESET_OUTDOORS_CREEK \ + { 26, 80.3f, 0.350f, -1100, -1500, -600, 2.13f, 0.21f, 0.46f, -1700, 0.115f, 0.00f,0.00f,-0.00f, -1100, 0.031f, 0.00f,0.00f,0.00f, 0.218f, 0.340f, 0.250f, 0.000f, -5.0f, 4399.1f, 242.9f, 0.00f, 0x0 } +#define EAX30_PRESET_OUTDOORS_VALLEY \ + { 26, 80.3f, 0.280f, -1100, -3100, -1600, 2.88f, 0.26f, 0.35f, -3200, 0.163f, 0.00f,0.00f,-0.00f, -1000, 0.100f, 0.00f,0.00f,0.00f, 0.250f, 0.340f, 0.250f, 0.000f, -0.0f, 2854.4f, 107.5f, 0.00f, 0x0 } + + +// MOOD PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define EAX30_PRESET_MOOD_HEAVEN \ + { 26, 19.6f, 0.940f, -1000, -200, -700, 5.04f, 1.12f, 0.56f, -1230, 0.020f, 0.00f,0.00f,0.00f, -200, 0.029f, 0.00f,0.00f,0.00f, 0.250f, 0.080f, 2.742f, 0.050f, -2.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_MOOD_HELL \ + { 26, 100.0f, 0.570f, -1000, -900, -700, 3.57f, 0.49f, 2.00f, -10000, 0.020f, 0.00f,0.00f,0.00f, 100, 0.030f, 0.00f,0.00f,0.00f, 0.110f, 0.040f, 2.109f, 0.520f, -5.0f, 5000.0f, 139.5f, 0.00f, 0x40 } +#define EAX30_PRESET_MOOD_MEMORY \ + { 26, 8.0f, 0.850f, -1000, -400, -900, 4.06f, 0.82f, 0.56f, -2800, 0.000f, 0.00f,0.00f,0.00f, -500, 0.000f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.474f, 0.450f, -2.0f, 5000.0f, 250.0f, 0.00f, 0x0 } + +// DRIVING SIMULATION PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define EAX30_PRESET_DRIVING_COMMENTATOR \ + { 26, 3.0f, 0.000f, -900, -500, -600, 2.42f, 0.88f, 0.68f, -1400, 0.093f, 0.00f,0.00f,0.00f, -1200, 0.017f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 0.250f, 0.000f, -0.0f, 5000.0f, 250.0f, 0.00f, 0x20 } +#define EAX30_PRESET_DRIVING_PITGARAGE \ + { 26, 1.9f, 0.590f, -1400, -300, -500, 1.72f, 0.93f, 0.87f, -500, 0.000f, 0.00f,0.00f,0.00f, 0, 0.016f, 0.00f,0.00f,0.00f, 0.250f, 0.110f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x0 } +#define EAX30_PRESET_DRIVING_INCAR_RACER \ + { 26, 1.1f, 0.800f, -700, 0, -200, 0.17f, 2.00f, 0.41f, 500, 0.007f, 0.00f,0.00f,0.00f, -500, 0.015f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -0.0f, 10268.2f, 251.0f, 0.00f, 0x20 } +#define EAX30_PRESET_DRIVING_INCAR_SPORTS \ + { 26, 1.1f, 0.800f, -900, -400, 0, 0.17f, 0.75f, 0.41f, 0, 0.010f, 0.00f,0.00f,0.00f, -600, 0.000f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -0.0f, 10268.2f, 251.0f, 0.00f, 0x20 } +#define EAX30_PRESET_DRIVING_INCAR_LUXURY \ + { 26, 1.6f, 1.000f, -800, -2000, -600, 0.13f, 0.41f, 0.46f, -200, 0.010f, 0.00f,0.00f,0.00f, 300, 0.010f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -0.0f, 10268.2f, 251.0f, 0.00f, 0x20 } +#define EAX30_PRESET_DRIVING_FULLGRANDSTAND \ + { 26, 8.3f, 1.000f, -1100, -1100, -400, 3.01f, 1.37f, 1.28f, -900, 0.090f, 0.00f,0.00f,0.00f, -1700, 0.049f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 10420.2f, 250.0f, 0.00f, 0x1f } +#define EAX30_PRESET_DRIVING_EMPTYGRANDSTAND \ + { 26, 8.3f, 1.000f, -700, 0, -200, 4.62f, 1.75f, 1.40f, -1363, 0.090f, 0.00f,0.00f,0.00f, -1900, 0.049f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 10420.2f, 250.0f, 0.00f, 0x1f } +#define EAX30_PRESET_DRIVING_TUNNEL \ + { 26, 3.1f, 0.810f, -900, -800, -100, 3.42f, 0.94f, 1.31f, -300, 0.051f, 0.00f,0.00f,0.00f, -500, 0.047f, 0.00f,0.00f,0.00f, 0.214f, 0.050f, 0.250f, 0.000f, -0.0f, 5000.0f, 155.3f, 0.00f, 0x20 } + +// CITY PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define EAX30_PRESET_CITY_STREETS \ + { 26, 3.0f, 0.780f, -1100, -300, -100, 1.79f, 1.12f, 0.91f, -1700, 0.046f, 0.00f,0.00f,0.00f, -2800, 0.028f, 0.00f,0.00f,0.00f, 0.250f, 0.200f, 0.250f, 0.000f, -0.0f, 5000.0f, 250.0f, 0.00f, 0x20 } +#define EAX30_PRESET_CITY_SUBWAY \ + { 26, 3.0f, 0.740f, -1100, -300, -100, 3.01f, 1.23f, 0.91f, -700, 0.046f, 0.00f,0.00f,0.00f, -1000, 0.028f, 0.00f,0.00f,0.00f, 0.125f, 0.210f, 0.250f, 0.000f, -0.0f, 5000.0f, 250.0f, 0.00f, 0x20 } +#define EAX30_PRESET_CITY_MUSEUM \ + { 26, 80.3f, 0.820f, -1100, -1500, -1500, 3.28f, 1.40f, 0.57f, -1600, 0.039f, 0.00f,0.00f,-0.00f, -600, 0.034f, 0.00f,0.00f,0.00f, 0.130f, 0.170f, 0.250f, 0.000f, -0.0f, 2854.4f, 107.5f, 0.00f, 0x0 } +#define EAX30_PRESET_CITY_LIBRARY \ + { 26, 80.3f, 0.820f, -1100, -1100, -2100, 2.76f, 0.89f, 0.41f, -1100, 0.029f, 0.00f,0.00f,-0.00f, -500, 0.020f, 0.00f,0.00f,0.00f, 0.130f, 0.170f, 0.250f, 0.000f, -0.0f, 2854.4f, 107.5f, 0.00f, 0x0 } +#define EAX30_PRESET_CITY_UNDERPASS \ + { 26, 3.0f, 0.820f, -1500, -700, -100, 3.57f, 1.12f, 0.91f, -1500, 0.059f, 0.00f,0.00f,0.00f, -1100, 0.037f, 0.00f,0.00f,0.00f, 0.250f, 0.140f, 0.250f, 0.000f, -0.0f, 5000.0f, 250.0f, 0.00f, 0x20 } +#define EAX30_PRESET_CITY_ABANDONED \ + { 26, 3.0f, 0.690f, -1100, -200, -100, 3.28f, 1.17f, 0.91f, -1400, 0.044f, 0.00f,0.00f,0.00f, -2400, 0.024f, 0.00f,0.00f,0.00f, 0.250f, 0.200f, 0.250f, 0.000f, -0.0f, 5000.0f, 250.0f, 0.00f, 0x20 } + +// MISC ROOMS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define EAX30_PRESET_DUSTYROOM \ + { 26, 1.8f, 0.560f, -1100, -200, -300, 1.79f, 0.38f, 0.21f, -600, 0.002f, 0.00f,0.00f,0.00f, 200, 0.006f, 0.00f,0.00f,0.00f, 0.202f, 0.050f, 0.250f, 0.000f, -3.0f, 13046.0f, 163.3f, 0.00f, 0x20 } +#define EAX30_PRESET_CHAPEL \ + { 26, 19.6f, 0.840f, -1000, -500, 0, 4.62f, 0.64f, 1.23f, -700, 0.032f, 0.00f,0.00f,0.00f, -800, 0.049f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.110f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define EAX30_PRESET_SMALLWATERROOM \ + { 26, 36.2f, 0.700f, -1200, -698, 0, 1.51f, 1.25f, 1.14f, -100, 0.020f, 0.00f,0.00f,0.00f, 200, 0.030f, 0.00f,0.00f,0.00f, 0.179f, 0.150f, 0.895f, 0.190f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x0 } + +/********************************************************************************************************/ + +////////////////////////////////////////////////////// +// Effect Scenarios enumerated // +////////////////////////////////////////////////////// + +typedef enum +{ + EAX30_SCENARIO_CASTLE = 0, + EAX30_SCENARIO_FACTORY, + EAX30_SCENARIO_ICEPALACE, + EAX30_SCENARIO_SPACESTATION, + EAX30_SCENARIO_WOODGALLEON, + EAX30_SCENARIO_SPORTS, + EAX30_SCENARIO_PREFAB, + EAX30_SCENARIO_DOMESNPIPES, + EAX30_SCENARIO_OUTDOORS, + EAX30_SCENARIO_MOOD, + EAX30_SCENARIO_DRIVING, + EAX30_SCENARIO_CITY, + EAX30_SCENARIO_MISC, + EAX30_SCENARIO_ORIGINAL +} +EAX30_SCENARIO; + +////////////////////////////////////////////////////// +// Number of Effect Scenarios // +////////////////////////////////////////////////////// + +#define EAX30_NUM_SCENARIOS 14 + +////////////////////////////////////////////////////// +// Number of Effect Scenarios with standardised // +// locations // +////////////////////////////////////////////////////// + +#define EAX30_NUM_STANDARD_SCENARIOS 5 + +////////////////////////////////////////////////////// +// Array of scenario names // +////////////////////////////////////////////////////// + +extern const char* EAX30_SCENARIO_NAMES[]; + +////////////////////////////////////////////////////// +// Standardised Locations enumerated // +////////////////////////////////////////////////////// + +typedef enum +{ + EAX30_LOCATION_HALL = 0, + EAX30_LOCATION_LARGEROOM, + EAX30_LOCATION_MEDIUMROOM, + EAX30_LOCATION_SMALLROOM, + EAX30_LOCATION_CUPBOARD, + EAX30_LOCATION_ALCOVE, + EAX30_LOCATION_LONGPASSAGE, + EAX30_LOCATION_SHORTPASSAGE, + EAX30_LOCATION_COURTYARD +} +EAX30_LOCATION; + +////////////////////////////////////////////////////// +// Number of Standardised Locations // +////////////////////////////////////////////////////// + +#define EAX30_NUM_LOCATIONS 9 + +////////////////////////////////////////////////////// +// Array of standardised location names // +////////////////////////////////////////////////////// + +extern const char* EAX30_LOCATION_NAMES[]; + +////////////////////////////////////////////////////// +// Number of effects in each scenario // +////////////////////////////////////////////////////// + +#define EAX30_NUM_ORIGINAL_PRESETS 26 +#define EAX30_NUM_CASTLE_PRESETS EAX30_NUM_LOCATIONS +#define EAX30_NUM_FACTORY_PRESETS EAX30_NUM_LOCATIONS +#define EAX30_NUM_ICEPALACE_PRESETS EAX30_NUM_LOCATIONS +#define EAX30_NUM_SPACESTATION_PRESETS EAX30_NUM_LOCATIONS +#define EAX30_NUM_WOODGALLEON_PRESETS EAX30_NUM_LOCATIONS +#define EAX30_NUM_SPORTS_PRESETS 7 +#define EAX30_NUM_PREFAB_PRESETS 5 +#define EAX30_NUM_DOMESNPIPES_PRESETS 6 +#define EAX30_NUM_OUTDOORS_PRESETS 5 +#define EAX30_NUM_MOOD_PRESETS 3 +#define EAX30_NUM_DRIVING_PRESETS 8 +#define EAX30_NUM_CITY_PRESETS 6 +#define EAX30_NUM_MISC_PRESETS 3 + +////////////////////////////////////////////////////// +// Standardised Location effects can be accessed // +// from a matrix // +////////////////////////////////////////////////////// + +extern EAXLISTENERPROPERTIES EAX30_STANDARD_PRESETS[EAX30_NUM_STANDARD_SCENARIOS][EAX30_NUM_LOCATIONS]; + +/********************************************************************************************************/ + +////////////////////////////////////////////////////// +// Original Preset effects enumerated // +////////////////////////////////////////////////////// + +typedef enum +{ + ORIGINAL_GENERIC = 0, + ORIGINAL_PADDEDCELL, + ORIGINAL_ROOM, + ORIGINAL_BATHROOM, + ORIGINAL_LIVINGROOM, + ORIGINAL_STONEROOM, + ORIGINAL_AUDITORIUM, + ORIGINAL_CONCERTHALL, + ORIGINAL_CAVE, + ORIGINAL_ARENA, + ORIGINAL_HANGAR, + ORIGINAL_CARPETTEDHALLWAY, + ORIGINAL_HALLWAY, + ORIGINAL_STONECORRIDOR, + ORIGINAL_ALLEY, + ORIGINAL_FOREST, + ORIGINAL_CITY, + ORIGINAL_MOUNTAINS, + ORIGINAL_QUARRY, + ORIGINAL_PLAIN, + ORIGINAL_PARKINGLOT, + ORIGINAL_SEWERPIPE, + ORIGINAL_UNDERWATER, + ORIGINAL_DRUGGED, + ORIGINAL_DIZZY, + ORIGINAL_PSYCHOTIC +} +EAX30_ORIGINAL_PRESET_ENUMS; + +////////////////////////////////////////////////////// +// Array of original environment names // +////////////////////////////////////////////////////// + +extern const char* EAX30_ORIGINAL_PRESET_NAMES[]; + +////////////////////////////////////////////////////// +// Original effects matrix // +////////////////////////////////////////////////////// + +extern EAXLISTENERPROPERTIES EAX30_ORIGINAL_PRESETS[]; + +/********************************************************************************************************/ + +////////////////////////////////////////////////////// +// Sports scenario effects enumerated // +////////////////////////////////////////////////////// + +typedef enum +{ + SPORT_EMPTYSTADIUM=0, + SPORT_FULLSTADIUM, + SPORT_STADIUMTANNOY, + SPORT_SQUASHCOURT, + SPORT_SMALLSWIMMINGPOOL, + SPORT_LARGESWIMMINGPOOL, + SPORT_GYMNASIUM +} +EAX30_SPORTS_PRESET_ENUMS; + +////////////////////////////////////////////////////// +// Array of sport environment names // +////////////////////////////////////////////////////// + +extern const char* EAX30_SPORTS_PRESET_NAMES[]; + +////////////////////////////////////////////////////// +// Sports effects matrix // +////////////////////////////////////////////////////// + +extern EAXLISTENERPROPERTIES EAX30_SPORTS_PRESETS[]; + +/********************************************************************************************************/ + +////////////////////////////////////////////////////// +// Prefab scenario effects enumerated // +////////////////////////////////////////////////////// + +typedef enum +{ + PREFAB_WORKSHOP, + PREFAB_SCHOOLROOM, + PREFAB_PRACTISEROOM, + PREFAB_OUTHOUSE, + PREFAB_CARAVAN +} +EAX30_PREFAB_PRESET_ENUMS; + +////////////////////////////////////////////////////// +// Array of prefab environment names // +////////////////////////////////////////////////////// + +extern const char* EAX30_PREFAB_PRESET_NAMES[]; + +////////////////////////////////////////////////////// +// Prefab effects matrix // +////////////////////////////////////////////////////// + +extern EAXLISTENERPROPERTIES EAX30_PREFAB_PRESETS[]; + +/********************************************************************************************************/ + +////////////////////////////////////////////////////// +// Domes & Pipes effects enumerated // +////////////////////////////////////////////////////// + +typedef enum +{ + DOME_TOMB, + DOME_SAINTPAULS, + PIPE_SMALL, + PIPE_LONGTHIN, + PIPE_LARGE, + PIPE_RESONANT +} +EAX30_DOMESNPIPES_PRESET_ENUMS; + +////////////////////////////////////////////////////// +// Array of Domes & Pipes environment names // +////////////////////////////////////////////////////// + +extern const char* EAX30_DOMESNPIPES_PRESET_NAMES[]; + +////////////////////////////////////////////////////// +// Domes & Pipes effects matrix // +////////////////////////////////////////////////////// + +extern EAXLISTENERPROPERTIES EAX30_DOMESNPIPES_PRESETS[]; + +/********************************************************************************************************/ + +////////////////////////////////////////////////////// +// Outdoors scenario effects enumerated // +////////////////////////////////////////////////////// + +typedef enum +{ + OUTDOORS_BACKYARD, + OUTDOORS_ROLLINGPLAINS, + OUTDOORS_DEEPCANYON, + OUTDOORS_CREEK, + OUTDOORS_VALLEY +} +EAX30_OUTDOORS_PRESET_ENUMS; + +////////////////////////////////////////////////////// +// Array of Outdoors environment names // +////////////////////////////////////////////////////// + +extern const char* EAX30_OUTDOORS_PRESET_NAMES[]; + +////////////////////////////////////////////////////// +// Outdoors effects matrix // +////////////////////////////////////////////////////// + +extern EAXLISTENERPROPERTIES EAX30_OUTDOORS_PRESETS[]; + +/********************************************************************************************************/ + +////////////////////////////////////////////////////// +// Mood scenario effects enumerated // +////////////////////////////////////////////////////// + +typedef enum +{ + MOOD_HEAVEN, + MOOD_HELL, + MOOD_MEMORY +} +EAX30_MOOD_PRESET_ENUMS; + +////////////////////////////////////////////////////// +// Array of Mood environment names // +////////////////////////////////////////////////////// + +extern const char* EAX30_MOOD_PRESET_NAMES[]; + +////////////////////////////////////////////////////// +// Mood effects matrix // +////////////////////////////////////////////////////// + +extern EAXLISTENERPROPERTIES EAX30_MOOD_PRESETS[]; + +/********************************************************************************************************/ + +////////////////////////////////////////////////////// +// Driving scenario effects enumerated // +////////////////////////////////////////////////////// + +typedef enum +{ + DRIVING_COMMENTATOR, + DRIVING_PITGARAGE, + DRIVING_INCAR_RACER, + DRIVING_INCAR_SPORTS, + DRIVING_INCAR_LUXURY, + DRIVING_FULLGRANDSTAND, + DRIVING_EMPTYGRANDSTAND, + DRIVING_TUNNEL +} +EAX30_DRIVING_PRESET_ENUMS; + +////////////////////////////////////////////////////// +// Array of driving environment names // +////////////////////////////////////////////////////// + +extern const char* EAX30_DRIVING_PRESET_NAMES[]; + +////////////////////////////////////////////////////// +// Driving effects matrix // +////////////////////////////////////////////////////// + +extern EAXLISTENERPROPERTIES EAX30_DRIVING_PRESETS[]; + +/********************************************************************************************************/ + +////////////////////////////////////////////////////// +// City scenario effects enumerated // +////////////////////////////////////////////////////// + +typedef enum +{ + CITY_STREETS, + CITY_SUBWAY, + CITY_MUSEUM, + CITY_LIBRARY, + CITY_UNDERPASS, + CITY_ABANDONED +} +EAX30_CITY_PRESET_ENUMS; + +////////////////////////////////////////////////////// +// Array of City environment names // +////////////////////////////////////////////////////// + +extern const char* EAX30_CITY_PRESET_NAMES[]; + +////////////////////////////////////////////////////// +// City effects matrix // +////////////////////////////////////////////////////// + +extern EAXLISTENERPROPERTIES EAX30_CITY_PRESETS[]; + +/********************************************************************************************************/ + +////////////////////////////////////////////////////// +// Misc scenario effects enumerated // +////////////////////////////////////////////////////// + +typedef enum +{ + DUSTYROOM, + CHAPEL, + SMALLWATERROOM +} +EAX30_MISC_PRESET_ENUMS; + + +////////////////////////////////////////////////////// +// Array of Misc environment names // +////////////////////////////////////////////////////// + +extern const char* EAX30_MISC_PRESET_NAMES[]; + +////////////////////////////////////////////////////// +// Misc effects matrix // +////////////////////////////////////////////////////// + +extern EAXLISTENERPROPERTIES EAX30_MISC_PRESETS[]; + + +/***********************************************************************************************\ +* +* Material transmission presets +* +* Three values in this order :- +* +* 1. Occlusion (or Obstruction) +* 2. Occlusion LF Ratio (or Obstruction LF Ratio) +* 3. Occlusion Room Ratio +* +************************************************************************************************/ + + +// Single window material preset +#define EAX_MATERIAL_SINGLEWINDOW (-2800) +#define EAX_MATERIAL_SINGLEWINDOWLF 0.71f +#define EAX_MATERIAL_SINGLEWINDOWROOMRATIO 0.43f + +// Double window material preset +#define EAX_MATERIAL_DOUBLEWINDOW (-5000) +#define EAX_MATERIAL_DOUBLEWINDOWLF 0.40f +#define EAX_MATERIAL_DOUBLEWINDOWROOMRATIO 0.24f + +// Thin door material preset +#define EAX_MATERIAL_THINDOOR (-1800) +#define EAX_MATERIAL_THINDOORLF 0.66f +#define EAX_MATERIAL_THINDOORROOMRATIO 0.66f + +// Thick door material preset +#define EAX_MATERIAL_THICKDOOR (-4400) +#define EAX_MATERIAL_THICKDOORLF 0.64f +#define EAX_MATERIAL_THICKDOORROOMRATIO 0.27f + +// Wood wall material preset +#define EAX_MATERIAL_WOODWALL (-4000) +#define EAX_MATERIAL_WOODWALLLF 0.50f +#define EAX_MATERIAL_WOODWALLROOMRATIO 0.30f + +// Brick wall material preset +#define EAX_MATERIAL_BRICKWALL (-5000) +#define EAX_MATERIAL_BRICKWALLLF 0.60f +#define EAX_MATERIAL_BRICKWALLROOMRATIO 0.24f + +// Stone wall material preset +#define EAX_MATERIAL_STONEWALL (-6000) +#define EAX_MATERIAL_STONEWALLLF 0.68f +#define EAX_MATERIAL_STONEWALLROOMRATIO 0.20f + +// Curtain material preset +#define EAX_MATERIAL_CURTAIN (-1200) +#define EAX_MATERIAL_CURTAINLF 0.15f +#define EAX_MATERIAL_CURTAINROOMRATIO 1.00f + + +#endif // EAXUTIL_INCLUDED diff --git a/src/miami/audio/eax/eax.h b/src/miami/audio/eax/eax.h new file mode 100644 index 00000000..b2210936 --- /dev/null +++ b/src/miami/audio/eax/eax.h @@ -0,0 +1,536 @@ +/*******************************************************************\ +* * +* EAX.H - Environmental Audio Extensions version 3.0 * +* for OpenAL and DirectSound3D * +* * +********************************************************************/ + +#ifndef EAX_H_INCLUDED +#define EAX_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#ifndef AUDIO_OAL + #include + + /* + * EAX Wrapper Interface (using Direct X 7) {4FF53B81-1CE0-11d3-AAB8-00A0C95949D5} + */ + DEFINE_GUID(CLSID_EAXDirectSound, + 0x4ff53b81, + 0x1ce0, + 0x11d3, + 0xaa, 0xb8, 0x0, 0xa0, 0xc9, 0x59, 0x49, 0xd5); + + /* + * EAX Wrapper Interface (using Direct X 8) {CA503B60-B176-11d4-A094-D0C0BF3A560C} + */ + DEFINE_GUID(CLSID_EAXDirectSound8, + 0xca503b60, + 0xb176, + 0x11d4, + 0xa0, 0x94, 0xd0, 0xc0, 0xbf, 0x3a, 0x56, 0xc); + + + +#ifdef DIRECTSOUND_VERSION +#if DIRECTSOUND_VERSION >= 0x0800 + __declspec(dllimport) HRESULT WINAPI EAXDirectSoundCreate8(GUID*, LPDIRECTSOUND8*, IUnknown FAR *); + typedef HRESULT (FAR PASCAL *LPEAXDIRECTSOUNDCREATE8)(GUID*, LPDIRECTSOUND8*, IUnknown FAR*); +#endif +#endif + + __declspec(dllimport) HRESULT WINAPI EAXDirectSoundCreate(GUID*, LPDIRECTSOUND*, IUnknown FAR *); + typedef HRESULT (FAR PASCAL *LPEAXDIRECTSOUNDCREATE)(GUID*, LPDIRECTSOUND*, IUnknown FAR*); + + __declspec(dllimport) void CDECL GetCurrentVersion(LPDWORD major, LPDWORD minor); + typedef void (CDECL *LPGETCURRENTVERSION)(LPDWORD major, LPDWORD minor); + + +#else // AUDIO_OAL + #include + #include + + #ifndef GUID_DEFINED + #define GUID_DEFINED + typedef struct _GUID + { + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; + } GUID; + #endif // !GUID_DEFINED + + #ifndef DEFINE_GUID + #ifndef INITGUID + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const GUID /*FAR*/ name + #else + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + #endif // INITGUID + #endif // DEFINE_GUID + + + /* + * EAX OpenAL Extension + */ + typedef ALenum (*EAXSet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); + typedef ALenum (*EAXGet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); +#endif + +#pragma pack(push, 4) + +/* + * EAX 3.0 listener property set {A8FA6880-B476-11d3-BDB9-00C0F02DDF87} + */ +DEFINE_GUID(DSPROPSETID_EAX30_ListenerProperties, + 0xa8fa6882, + 0xb476, + 0x11d3, + 0xbd, 0xb9, 0x00, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_ListenerProperties DSPROPSETID_EAX30_ListenerProperties + +typedef enum +{ + DSPROPERTY_EAXLISTENER_NONE, + DSPROPERTY_EAXLISTENER_ALLPARAMETERS, + DSPROPERTY_EAXLISTENER_ENVIRONMENT, + DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, + DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION, + DSPROPERTY_EAXLISTENER_ROOM, + DSPROPERTY_EAXLISTENER_ROOMHF, + DSPROPERTY_EAXLISTENER_ROOMLF, + DSPROPERTY_EAXLISTENER_DECAYTIME, + DSPROPERTY_EAXLISTENER_DECAYHFRATIO, + DSPROPERTY_EAXLISTENER_DECAYLFRATIO, + DSPROPERTY_EAXLISTENER_REFLECTIONS, + DSPROPERTY_EAXLISTENER_REFLECTIONSDELAY, + DSPROPERTY_EAXLISTENER_REFLECTIONSPAN, + DSPROPERTY_EAXLISTENER_REVERB, + DSPROPERTY_EAXLISTENER_REVERBDELAY, + DSPROPERTY_EAXLISTENER_REVERBPAN, + DSPROPERTY_EAXLISTENER_ECHOTIME, + DSPROPERTY_EAXLISTENER_ECHODEPTH, + DSPROPERTY_EAXLISTENER_MODULATIONTIME, + DSPROPERTY_EAXLISTENER_MODULATIONDEPTH, + DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF, + DSPROPERTY_EAXLISTENER_HFREFERENCE, + DSPROPERTY_EAXLISTENER_LFREFERENCE, + DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXLISTENER_FLAGS +} DSPROPERTY_EAX_LISTENERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXLISTENER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXLISTENER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXLISTENER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXLISTENER_NONE | \ + DSPROPERTY_EAXLISTENER_IMMEDIATE) + +typedef struct _EAXVECTOR { + float x; + float y; + float z; +} EAXVECTOR; + +// Use this structure for DSPROPERTY_EAXLISTENER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all times and delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myListener.lRoom = -1000; +// myListener.lRoomHF = -100; +// ... +// myListener.dwFlags = myFlags /* see EAXLISTENERFLAGS below */ ; +// instead of: +// myListener = { -1000, -100, ... , 0x00000009 }; +// If you want to save and load presets in binary form, you +// should define your own structure to insure future compatibility. +// +typedef struct _EAXLISTENERPROPERTIES +{ + unsigned long ulEnvironment; // sets all listener properties + float flEnvironmentSize; // environment size in meters + float flEnvironmentDiffusion; // environment diffusion + long lRoom; // room effect level (at mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lRoomLF; // relative room effect level at low frequencies + float flDecayTime; // reverberation decay time at mid frequencies + float flDecayHFRatio; // high-frequency to mid-frequency decay time ratio + float flDecayLFRatio; // low-frequency to mid-frequency decay time ratio + long lReflections; // early reflections level relative to room effect + float flReflectionsDelay; // initial reflection delay time + EAXVECTOR vReflectionsPan; // early reflections panning vector + long lReverb; // late reverberation level relative to room effect + float flReverbDelay; // late reverberation delay time relative to initial reflection + EAXVECTOR vReverbPan; // late reverberation panning vector + float flEchoTime; // echo time + float flEchoDepth; // echo depth + float flModulationTime; // modulation time + float flModulationDepth; // modulation depth + float flAirAbsorptionHF; // change in level per meter at high frequencies + float flHFReference; // reference high frequency + float flLFReference; // reference low frequency + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + unsigned long ulFlags; // modifies the behavior of properties +} EAXLISTENERPROPERTIES, *LPEAXLISTENERPROPERTIES; + +// used by DSPROPERTY_EAXLISTENER_ENVIRONMENT +enum +{ + EAX_ENVIRONMENT_GENERIC, + EAX_ENVIRONMENT_PADDEDCELL, + EAX_ENVIRONMENT_ROOM, + EAX_ENVIRONMENT_BATHROOM, + EAX_ENVIRONMENT_LIVINGROOM, + EAX_ENVIRONMENT_STONEROOM, + EAX_ENVIRONMENT_AUDITORIUM, + EAX_ENVIRONMENT_CONCERTHALL, + EAX_ENVIRONMENT_CAVE, + EAX_ENVIRONMENT_ARENA, + EAX_ENVIRONMENT_HANGAR, + EAX_ENVIRONMENT_CARPETEDHALLWAY, + EAX_ENVIRONMENT_HALLWAY, + EAX_ENVIRONMENT_STONECORRIDOR, + EAX_ENVIRONMENT_ALLEY, + EAX_ENVIRONMENT_FOREST, + EAX_ENVIRONMENT_CITY, + EAX_ENVIRONMENT_MOUNTAINS, + EAX_ENVIRONMENT_QUARRY, + EAX_ENVIRONMENT_PLAIN, + EAX_ENVIRONMENT_PARKINGLOT, + EAX_ENVIRONMENT_SEWERPIPE, + EAX_ENVIRONMENT_UNDERWATER, + EAX_ENVIRONMENT_DRUGGED, + EAX_ENVIRONMENT_DIZZY, + EAX_ENVIRONMENT_PSYCHOTIC, + + EAX_ENVIRONMENT_UNDEFINED, + + EAX_ENVIRONMENT_COUNT +}; + +// Used by DSPROPERTY_EAXLISTENER_FLAGS +// +// Note: The number and order of flags may change in future EAX versions. +// It is recommended to use the flag defines as follows: +// myFlags = EAXLISTENERFLAGS_DECAYTIMESCALE | EAXLISTENERFLAGS_REVERBSCALE; +// instead of: +// myFlags = 0x00000009; +// +// These flags determine what properties are affected by environment size. +#define EAXLISTENERFLAGS_DECAYTIMESCALE 0x00000001 // reverberation decay time +#define EAXLISTENERFLAGS_REFLECTIONSSCALE 0x00000002 // reflection level +#define EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE 0x00000004 // initial reflection delay time +#define EAXLISTENERFLAGS_REVERBSCALE 0x00000008 // reflections level +#define EAXLISTENERFLAGS_REVERBDELAYSCALE 0x00000010 // late reverberation delay time +#define EAXLISTENERFLAGS_ECHOTIMESCALE 0x00000040 // echo time +#define EAXLISTENERFLAGS_MODULATIONTIMESCALE 0x00000080 // modulation time + +// This flag limits high-frequency decay time according to air absorption. +#define EAXLISTENERFLAGS_DECAYHFLIMIT 0x00000020 + +#define EAXLISTENERFLAGS_RESERVED 0xFFFFFF00 // reserved future use + +// Property ranges and defaults: + +#define EAXLISTENER_MINENVIRONMENT 0 +#define EAXLISTENER_MAXENVIRONMENT (EAX_ENVIRONMENT_COUNT-1) +#define EAXLISTENER_DEFAULTENVIRONMENT EAX_ENVIRONMENT_GENERIC + +#define EAXLISTENER_MINENVIRONMENTSIZE 1.0f +#define EAXLISTENER_MAXENVIRONMENTSIZE 100.0f +#define EAXLISTENER_DEFAULTENVIRONMENTSIZE 7.5f + +#define EAXLISTENER_MINENVIRONMENTDIFFUSION 0.0f +#define EAXLISTENER_MAXENVIRONMENTDIFFUSION 1.0f +#define EAXLISTENER_DEFAULTENVIRONMENTDIFFUSION 1.0f + +#define EAXLISTENER_MINROOM (-10000) +#define EAXLISTENER_MAXROOM 0 +#define EAXLISTENER_DEFAULTROOM (-1000) + +#define EAXLISTENER_MINROOMHF (-10000) +#define EAXLISTENER_MAXROOMHF 0 +#define EAXLISTENER_DEFAULTROOMHF (-100) + +#define EAXLISTENER_MINROOMLF (-10000) +#define EAXLISTENER_MAXROOMLF 0 +#define EAXLISTENER_DEFAULTROOMLF 0 + +#define EAXLISTENER_MINDECAYTIME 0.1f +#define EAXLISTENER_MAXDECAYTIME 20.0f +#define EAXLISTENER_DEFAULTDECAYTIME 1.49f + +#define EAXLISTENER_MINDECAYHFRATIO 0.1f +#define EAXLISTENER_MAXDECAYHFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYHFRATIO 0.83f + +#define EAXLISTENER_MINDECAYLFRATIO 0.1f +#define EAXLISTENER_MAXDECAYLFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYLFRATIO 1.00f + +#define EAXLISTENER_MINREFLECTIONS (-10000) +#define EAXLISTENER_MAXREFLECTIONS 1000 +#define EAXLISTENER_DEFAULTREFLECTIONS (-2602) + +#define EAXLISTENER_MINREFLECTIONSDELAY 0.0f +#define EAXLISTENER_MAXREFLECTIONSDELAY 0.3f +#define EAXLISTENER_DEFAULTREFLECTIONSDELAY 0.007f + +#define EAXLISTENER_MINREVERB (-10000) +#define EAXLISTENER_MAXREVERB 2000 +#define EAXLISTENER_DEFAULTREVERB 200 + +#define EAXLISTENER_MINREVERBDELAY 0.0f +#define EAXLISTENER_MAXREVERBDELAY 0.1f +#define EAXLISTENER_DEFAULTREVERBDELAY 0.011f + +#define EAXLISTENER_MINECHOTIME 0.075f +#define EAXLISTENER_MAXECHOTIME 0.25f +#define EAXLISTENER_DEFAULTECHOTIME 0.25f + +#define EAXLISTENER_MINECHODEPTH 0.0f +#define EAXLISTENER_MAXECHODEPTH 1.0f +#define EAXLISTENER_DEFAULTECHODEPTH 0.0f + +#define EAXLISTENER_MINMODULATIONTIME 0.04f +#define EAXLISTENER_MAXMODULATIONTIME 4.0f +#define EAXLISTENER_DEFAULTMODULATIONTIME 0.25f + +#define EAXLISTENER_MINMODULATIONDEPTH 0.0f +#define EAXLISTENER_MAXMODULATIONDEPTH 1.0f +#define EAXLISTENER_DEFAULTMODULATIONDEPTH 0.0f + +#define EAXLISTENER_MINAIRABSORPTIONHF (-100.0f) +#define EAXLISTENER_MAXAIRABSORPTIONHF 0.0f +#define EAXLISTENER_DEFAULTAIRABSORPTIONHF (-5.0f) + +#define EAXLISTENER_MINHFREFERENCE 1000.0f +#define EAXLISTENER_MAXHFREFERENCE 20000.0f +#define EAXLISTENER_DEFAULTHFREFERENCE 5000.0f + +#define EAXLISTENER_MINLFREFERENCE 20.0f +#define EAXLISTENER_MAXLFREFERENCE 1000.0f +#define EAXLISTENER_DEFAULTLFREFERENCE 250.0f + +#define EAXLISTENER_MINROOMROLLOFFFACTOR 0.0f +#define EAXLISTENER_MAXROOMROLLOFFFACTOR 10.0f +#define EAXLISTENER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXLISTENER_DEFAULTFLAGS (EAXLISTENERFLAGS_DECAYTIMESCALE | \ + EAXLISTENERFLAGS_REFLECTIONSSCALE | \ + EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE | \ + EAXLISTENERFLAGS_REVERBSCALE | \ + EAXLISTENERFLAGS_REVERBDELAYSCALE | \ + EAXLISTENERFLAGS_DECAYHFLIMIT) + + + +/* +* EAX 3.0 buffer property set {A8FA6881-B476-11d3-BDB9-00C0F02DDF87} +*/ +DEFINE_GUID(DSPROPSETID_EAX30_BufferProperties, + 0xa8fa6881, + 0xb476, + 0x11d3, + 0xbd, 0xb9, 0x0, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_BufferProperties DSPROPSETID_EAX30_BufferProperties +#define DSPROPSETID_EAX_SourceProperties DSPROPSETID_EAX30_BufferProperties + +typedef enum +{ + DSPROPERTY_EAXBUFFER_NONE, + DSPROPERTY_EAXBUFFER_ALLPARAMETERS, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONPARAMETERS, + DSPROPERTY_EAXBUFFER_OCCLUSIONPARAMETERS, + DSPROPERTY_EAXBUFFER_EXCLUSIONPARAMETERS, + DSPROPERTY_EAXBUFFER_DIRECT, + DSPROPERTY_EAXBUFFER_DIRECTHF, + DSPROPERTY_EAXBUFFER_ROOM, + DSPROPERTY_EAXBUFFER_ROOMHF, + DSPROPERTY_EAXBUFFER_OBSTRUCTION, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSION, + DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONDIRECTRATIO, + DSPROPERTY_EAXBUFFER_EXCLUSION, + DSPROPERTY_EAXBUFFER_EXCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OUTSIDEVOLUMEHF, + DSPROPERTY_EAXBUFFER_DOPPLERFACTOR, + DSPROPERTY_EAXBUFFER_ROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_AIRABSORPTIONFACTOR, + DSPROPERTY_EAXBUFFER_FLAGS +} DSPROPERTY_EAX_BUFFERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXBUFFER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXBUFFER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXBUFFER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXBUFFER_NONE | \ + DSPROPERTY_EAXBUFFER_IMMEDIATE) + +// Use this structure for DSPROPERTY_EAXBUFFER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myBuffer.lDirect = 0; +// myBuffer.lDirectHF = -200; +// ... +// myBuffer.dwFlags = myFlags /* see EAXBUFFERFLAGS below */ ; +// instead of: +// myBuffer = { 0, -200, ... , 0x00000003 }; +// +typedef struct _EAXBUFFERPROPERTIES +{ + long lDirect; // direct path level (at low and mid frequencies) + long lDirectHF; // relative direct path level at high frequencies + long lRoom; // room effect level (at low and mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lObstruction; // main obstruction control (attenuation at high frequencies) + float flObstructionLFRatio; // obstruction low-frequency level re. main control + long lOcclusion; // main occlusion control (attenuation at high frequencies) + float flOcclusionLFRatio; // occlusion low-frequency level re. main control + float flOcclusionRoomRatio; // relative occlusion control for room effect + float flOcclusionDirectRatio; // relative occlusion control for direct path + long lExclusion; // main exlusion control (attenuation at high frequencies) + float flExclusionLFRatio; // exclusion low-frequency level re. main control + long lOutsideVolumeHF; // outside sound cone level at high frequencies + float flDopplerFactor; // like DS3D flDopplerFactor but per source + float flRolloffFactor; // like DS3D flRolloffFactor but per source + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + float flAirAbsorptionFactor; // multiplies DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF + unsigned long ulFlags; // modifies the behavior of properties +} EAXBUFFERPROPERTIES, *LPEAXBUFFERPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_OBSTRUCTION, +typedef struct _EAXOBSTRUCTIONPROPERTIES +{ + long lObstruction; + float flObstructionLFRatio; +} EAXOBSTRUCTIONPROPERTIES, *LPEAXOBSTRUCTIONPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_OCCLUSION +typedef struct _EAXOCCLUSIONPROPERTIES +{ + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +} EAXOCCLUSIONPROPERTIES, *LPEAXOCCLUSIONPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_EXCLUSION +typedef struct _EAXEXCLUSIONPROPERTIES +{ + long lExclusion; + float flExclusionLFRatio; +} EAXEXCLUSIONPROPERTIES, *LPEAXEXCLUSIONPROPERTIES; + +// Used by DSPROPERTY_EAXBUFFER_FLAGS +// TRUE: value is computed automatically - property is an offset +// FALSE: value is used directly +// +// Note: The number and order of flags may change in future EAX versions. +// To insure future compatibility, use flag defines as follows: +// myFlags = EAXBUFFERFLAGS_DIRECTHFAUTO | EAXBUFFERFLAGS_ROOMAUTO; +// instead of: +// myFlags = 0x00000003; +// +#define EAXBUFFERFLAGS_DIRECTHFAUTO 0x00000001 // affects DSPROPERTY_EAXBUFFER_DIRECTHF +#define EAXBUFFERFLAGS_ROOMAUTO 0x00000002 // affects DSPROPERTY_EAXBUFFER_ROOM +#define EAXBUFFERFLAGS_ROOMHFAUTO 0x00000004 // affects DSPROPERTY_EAXBUFFER_ROOMHF + +#define EAXBUFFERFLAGS_RESERVED 0xFFFFFFF8 // reserved future use + +// Property ranges and defaults: + +#define EAXBUFFER_MINDIRECT (-10000) +#define EAXBUFFER_MAXDIRECT 1000 +#define EAXBUFFER_DEFAULTDIRECT 0 + +#define EAXBUFFER_MINDIRECTHF (-10000) +#define EAXBUFFER_MAXDIRECTHF 0 +#define EAXBUFFER_DEFAULTDIRECTHF 0 + +#define EAXBUFFER_MINROOM (-10000) +#define EAXBUFFER_MAXROOM 1000 +#define EAXBUFFER_DEFAULTROOM 0 + +#define EAXBUFFER_MINROOMHF (-10000) +#define EAXBUFFER_MAXROOMHF 0 +#define EAXBUFFER_DEFAULTROOMHF 0 + +#define EAXBUFFER_MINOBSTRUCTION (-10000) +#define EAXBUFFER_MAXOBSTRUCTION 0 +#define EAXBUFFER_DEFAULTOBSTRUCTION 0 + +#define EAXBUFFER_MINOBSTRUCTIONLFRATIO 0.0f +#define EAXBUFFER_MAXOBSTRUCTIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOBSTRUCTIONLFRATIO 0.0f + +#define EAXBUFFER_MINOCCLUSION (-10000) +#define EAXBUFFER_MAXOCCLUSION 0 +#define EAXBUFFER_DEFAULTOCCLUSION 0 + +#define EAXBUFFER_MINOCCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOCCLUSIONLFRATIO 0.25f + +#define EAXBUFFER_MINOCCLUSIONROOMRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONROOMRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONROOMRATIO 1.5f + +#define EAXBUFFER_MINOCCLUSIONDIRECTRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONDIRECTRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONDIRECTRATIO 1.0f + +#define EAXBUFFER_MINEXCLUSION (-10000) +#define EAXBUFFER_MAXEXCLUSION 0 +#define EAXBUFFER_DEFAULTEXCLUSION 0 + +#define EAXBUFFER_MINEXCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXEXCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTEXCLUSIONLFRATIO 1.0f + +#define EAXBUFFER_MINOUTSIDEVOLUMEHF (-10000) +#define EAXBUFFER_MAXOUTSIDEVOLUMEHF 0 +#define EAXBUFFER_DEFAULTOUTSIDEVOLUMEHF 0 + +#define EAXBUFFER_MINDOPPLERFACTOR 0.0f +#define EAXBUFFER_MAXDOPPLERFACTOR 10.f +#define EAXBUFFER_DEFAULTDOPPLERFACTOR 0.0f + +#define EAXBUFFER_MINROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINROOMROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROOMROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINAIRABSORPTIONFACTOR 0.0f +#define EAXBUFFER_MAXAIRABSORPTIONFACTOR 10.0f +#define EAXBUFFER_DEFAULTAIRABSORPTIONFACTOR 1.0f + +#define EAXBUFFER_DEFAULTFLAGS (EAXBUFFERFLAGS_DIRECTHFAUTO | \ + EAXBUFFERFLAGS_ROOMAUTO | \ + EAXBUFFERFLAGS_ROOMHFAUTO ) + +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif diff --git a/src/miami/audio/oal/aldlist.cpp b/src/miami/audio/oal/aldlist.cpp new file mode 100644 index 00000000..6024adf2 --- /dev/null +++ b/src/miami/audio/oal/aldlist.cpp @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2006, Creative Labs Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of conditions and + * the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the distribution. + * * Neither the name of Creative Labs Inc. nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "aldlist.h" + +#ifdef AUDIO_OAL +/* + * Init call + */ +ALDeviceList::ALDeviceList() +{ + char *devices; + int index; + const char *defaultDeviceName; + const char *actualDeviceName; + + // DeviceInfo vector stores, for each enumerated device, it's device name, selection status, spec version #, and extension support + nNumOfDevices = 0; + + defaultDeviceIndex = 0; + + if (alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT")) { + devices = (char *)alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER); + defaultDeviceName = (char *)alcGetString(NULL, ALC_DEFAULT_ALL_DEVICES_SPECIFIER); + + index = 0; + // go through device list (each device terminated with a single NULL, list terminated with double NULL) + while (*devices != '\0') { + if (strcmp(defaultDeviceName, devices) == 0) { + defaultDeviceIndex = index; + } + ALCdevice *device = alcOpenDevice(devices); + if (device) { + ALCcontext *context = alcCreateContext(device, NULL); + if (context) { + alcMakeContextCurrent(context); + // if new actual device name isn't already in the list, then add it... + actualDeviceName = alcGetString(device, ALC_ALL_DEVICES_SPECIFIER); + if ((actualDeviceName != NULL) && (strlen(actualDeviceName) > 0)) { + ALDEVICEINFO &ALDeviceInfo = aDeviceInfo[nNumOfDevices++]; + ALDeviceInfo.bSelected = true; + ALDeviceInfo.SetName(actualDeviceName); + alcGetIntegerv(device, ALC_MAJOR_VERSION, sizeof(int), &ALDeviceInfo.iMajorVersion); + alcGetIntegerv(device, ALC_MINOR_VERSION, sizeof(int), &ALDeviceInfo.iMinorVersion); + + // Check for ALC Extensions + if (alcIsExtensionPresent(device, "ALC_EXT_CAPTURE") == AL_TRUE) + ALDeviceInfo.Extensions |= ADEXT_EXT_CAPTURE; + if (alcIsExtensionPresent(device, "ALC_EXT_EFX") == AL_TRUE) + ALDeviceInfo.Extensions |= ADEXT_EXT_EFX; + + // Check for AL Extensions + if (alIsExtensionPresent("AL_EXT_OFFSET") == AL_TRUE) + ALDeviceInfo.Extensions |= ADEXT_EXT_OFFSET; + + if (alIsExtensionPresent("AL_EXT_LINEAR_DISTANCE") == AL_TRUE) + ALDeviceInfo.Extensions |= ADEXT_EXT_LINEAR_DISTANCE; + if (alIsExtensionPresent("AL_EXT_EXPONENT_DISTANCE") == AL_TRUE) + ALDeviceInfo.Extensions |= ADEXT_EXT_EXPONENT_DISTANCE; + + if (alIsExtensionPresent("EAX2.0") == AL_TRUE) + ALDeviceInfo.Extensions |= ADEXT_EAX2; + if (alIsExtensionPresent("EAX3.0") == AL_TRUE) + ALDeviceInfo.Extensions |= ADEXT_EAX3; + if (alIsExtensionPresent("EAX4.0") == AL_TRUE) + ALDeviceInfo.Extensions |= ADEXT_EAX4; + if (alIsExtensionPresent("EAX5.0") == AL_TRUE) + ALDeviceInfo.Extensions |= ADEXT_EAX5; + + if (alIsExtensionPresent("EAX-RAM") == AL_TRUE) + ALDeviceInfo.Extensions |= ADEXT_EAX_RAM; + + // Get Source Count + ALDeviceInfo.uiSourceCount = GetMaxNumSources(); + } + alcMakeContextCurrent(NULL); + alcDestroyContext(context); + } + alcCloseDevice(device); + } + devices += strlen(devices) + 1; + index += 1; + } + } + + ResetFilters(); +} + +/* + * Exit call + */ +ALDeviceList::~ALDeviceList() +{ +} + +/* + * Returns the number of devices in the complete device list + */ +unsigned int ALDeviceList::GetNumDevices() +{ + return nNumOfDevices; +} + +/* + * Returns the device name at an index in the complete device list + */ +const char * ALDeviceList::GetDeviceName(unsigned int index) +{ + if (index < GetNumDevices()) + return aDeviceInfo[index].strDeviceName; + else + return NULL; +} + +/* + * Returns the major and minor version numbers for a device at a specified index in the complete list + */ +void ALDeviceList::GetDeviceVersion(unsigned int index, int *major, int *minor) +{ + if (index < GetNumDevices()) { + if (major) + *major = aDeviceInfo[index].iMajorVersion; + if (minor) + *minor = aDeviceInfo[index].iMinorVersion; + } + return; +} + +/* + * Returns the maximum number of Sources that can be generate on the given device + */ +unsigned int ALDeviceList::GetMaxNumSources(unsigned int index) +{ + if (index < GetNumDevices()) + return aDeviceInfo[index].uiSourceCount; + else + return 0; +} + +/* + * Checks if the extension is supported on the given device + */ +bool ALDeviceList::IsExtensionSupported(int index, unsigned short ext) +{ + return !!(aDeviceInfo[index].Extensions & ext); +} + +/* + * returns the index of the default device in the complete device list + */ +int ALDeviceList::GetDefaultDevice() +{ + return defaultDeviceIndex; +} + +/* + * Deselects devices which don't have the specified minimum version + */ +void ALDeviceList::FilterDevicesMinVer(int major, int minor) +{ + int dMajor, dMinor; + for (unsigned int i = 0; i < nNumOfDevices; i++) { + GetDeviceVersion(i, &dMajor, &dMinor); + if ((dMajor < major) || ((dMajor == major) && (dMinor < minor))) { + aDeviceInfo[i].bSelected = false; + } + } +} + +/* + * Deselects devices which don't have the specified maximum version + */ +void ALDeviceList::FilterDevicesMaxVer(int major, int minor) +{ + int dMajor, dMinor; + for (unsigned int i = 0; i < nNumOfDevices; i++) { + GetDeviceVersion(i, &dMajor, &dMinor); + if ((dMajor > major) || ((dMajor == major) && (dMinor > minor))) { + aDeviceInfo[i].bSelected = false; + } + } +} + +/* + * Deselects device which don't support the given extension name + */ +void +ALDeviceList::FilterDevicesExtension(unsigned short ext) +{ + for (unsigned int i = 0; i < nNumOfDevices; i++) { + if (!IsExtensionSupported(i, ext)) + aDeviceInfo[i].bSelected = false; + } +} + +/* + * Resets all filtering, such that all devices are in the list + */ +void ALDeviceList::ResetFilters() +{ + for (unsigned int i = 0; i < GetNumDevices(); i++) { + aDeviceInfo[i].bSelected = true; + } + filterIndex = 0; +} + +/* + * Gets index of first filtered device + */ +int ALDeviceList::GetFirstFilteredDevice() +{ + unsigned int i; + + for (i = 0; i < GetNumDevices(); i++) { + if (aDeviceInfo[i].bSelected == true) { + break; + } + } + filterIndex = i + 1; + return i; +} + +/* + * Gets index of next filtered device + */ +int ALDeviceList::GetNextFilteredDevice() +{ + unsigned int i; + + for (i = filterIndex; i < GetNumDevices(); i++) { + if (aDeviceInfo[i].bSelected == true) { + break; + } + } + filterIndex = i + 1; + return i; +} + +/* + * Internal function to detemine max number of Sources that can be generated + */ +unsigned int ALDeviceList::GetMaxNumSources() +{ + ALuint uiSources[256]; + unsigned int iSourceCount = 0; + + // Clear AL Error Code + alGetError(); + + // Generate up to 256 Sources, checking for any errors + for (iSourceCount = 0; iSourceCount < 256; iSourceCount++) + { + alGenSources(1, &uiSources[iSourceCount]); + if (alGetError() != AL_NO_ERROR) + break; + } + + // Release the Sources + alDeleteSources(iSourceCount, uiSources); + if (alGetError() != AL_NO_ERROR) + { + for (unsigned int i = 0; i < 256; i++) + { + alDeleteSources(1, &uiSources[i]); + } + } + + return iSourceCount; +} +#endif diff --git a/src/miami/audio/oal/aldlist.h b/src/miami/audio/oal/aldlist.h new file mode 100644 index 00000000..3ed12d84 --- /dev/null +++ b/src/miami/audio/oal/aldlist.h @@ -0,0 +1,82 @@ +#ifndef ALDEVICELIST_H +#define ALDEVICELIST_H + +#include "oal_utils.h" + +#ifdef AUDIO_OAL +#pragma warning(disable: 4786) //disable warning "identifier was truncated to '255' characters in the browser information" + +enum +{ + ADEXT_EXT_CAPTURE = (1 << 0), + ADEXT_EXT_EFX = (1 << 1), + ADEXT_EXT_OFFSET = (1 << 2), + ADEXT_EXT_LINEAR_DISTANCE = (1 << 3), + ADEXT_EXT_EXPONENT_DISTANCE = (1 << 4), + ADEXT_EAX2 = (1 << 5), + ADEXT_EAX3 = (1 << 6), + ADEXT_EAX4 = (1 << 7), + ADEXT_EAX5 = (1 << 8), + ADEXT_EAX_RAM = (1 << 9), +}; + +struct ALDEVICEINFO { + char *strDeviceName; + int iMajorVersion; + int iMinorVersion; + unsigned int uiSourceCount; + unsigned short Extensions; + bool bSelected; + + ALDEVICEINFO() : iMajorVersion(0), iMinorVersion(0), uiSourceCount(0), bSelected(false) + { + strDeviceName = NULL; + Extensions = 0; + } + + ~ALDEVICEINFO() + { + delete[] strDeviceName; + strDeviceName = NULL; + } + + void SetName(const char *name) + { + if(strDeviceName) delete[] strDeviceName; + strDeviceName = new char[strlen(name) + 1]; + strcpy(strDeviceName, name); + } +}; + +typedef ALDEVICEINFO *LPALDEVICEINFO; + +class ALDeviceList +{ +private: + ALDEVICEINFO aDeviceInfo[64]; + unsigned int nNumOfDevices; + int defaultDeviceIndex; + int filterIndex; + +public: + ALDeviceList (); + ~ALDeviceList (); + unsigned int GetNumDevices(); + const char *GetDeviceName(unsigned int index); + void GetDeviceVersion(unsigned int index, int *major, int *minor); + unsigned int GetMaxNumSources(unsigned int index); + bool IsExtensionSupported(int index, unsigned short ext); + int GetDefaultDevice(); + void FilterDevicesMinVer(int major, int minor); + void FilterDevicesMaxVer(int major, int minor); + void FilterDevicesExtension(unsigned short ext); + void ResetFilters(); + int GetFirstFilteredDevice(); + int GetNextFilteredDevice(); + +private: + unsigned int GetMaxNumSources(); +}; +#endif + +#endif // ALDEVICELIST_H diff --git a/src/miami/audio/oal/channel.cpp b/src/miami/audio/oal/channel.cpp new file mode 100644 index 00000000..04e7e529 --- /dev/null +++ b/src/miami/audio/oal/channel.cpp @@ -0,0 +1,295 @@ +#include "common.h" + +#ifdef AUDIO_OAL +#include "channel.h" +#include "sampman.h" + +#ifndef _WIN32 +#include +#endif + +extern bool IsFXSupported(); + +ALuint alSources[NUM_CHANNELS]; +ALuint alFilters[NUM_CHANNELS]; +ALuint alBuffers[NUM_CHANNELS]; +bool bChannelsCreated = false; + +int32 CChannel::channelsThatNeedService = 0; + +uint8 tempStereoBuffer[PED_BLOCKSIZE * 2]; + +void +CChannel::InitChannels() +{ + alGenSources(NUM_CHANNELS, alSources); + alGenBuffers(NUM_CHANNELS, alBuffers); + if (IsFXSupported()) + alGenFilters(NUM_CHANNELS, alFilters); + bChannelsCreated = true; +} + +void +CChannel::DestroyChannels() +{ + if (bChannelsCreated) + { + alDeleteSources(NUM_CHANNELS, alSources); + memset(alSources, 0, sizeof(alSources)); + alDeleteBuffers(NUM_CHANNELS, alBuffers); + memset(alBuffers, 0, sizeof(alBuffers)); + if (IsFXSupported()) + { + alDeleteFilters(NUM_CHANNELS, alFilters); + memset(alFilters, 0, sizeof(alFilters)); + } + bChannelsCreated = false; + } +} + + +CChannel::CChannel() +{ + Data = nil; + DataSize = 0; + bIs2D = false; + SetDefault(); +} + +void CChannel::SetDefault() +{ + Pitch = 1.0f; + Gain = 1.0f; + Mix = 0.0f; + + Position[0] = 0.0f; Position[1] = 0.0f; Position[2] = 0.0f; + Distances[0] = 0.0f; Distances[1] = FLT_MAX; + + LoopCount = 1; + LastProcessedOffset = UINT32_MAX; + LoopPoints[0] = 0; LoopPoints[1] = -1; + + Frequency = MAX_FREQ; +} + +void CChannel::Reset() +{ + // Here is safe because ctor don't call this + if (LoopCount > 1) + channelsThatNeedService--; + + ClearBuffer(); + SetDefault(); +} + +void CChannel::Init(uint32 _id, bool Is2D) +{ + id = _id; + if ( HasSource() ) + { + alSourcei(alSources[id], AL_SOURCE_RELATIVE, AL_TRUE); + if ( IsFXSupported() ) + alSource3i(alSources[id], AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); + + if ( Is2D ) + { + bIs2D = true; + alSource3f(alSources[id], AL_POSITION, 0.0f, 0.0f, 0.0f); + alSourcef(alSources[id], AL_GAIN, 1.0f); + } + } +} + +void CChannel::Term() +{ + Stop(); + if ( HasSource() ) + { + if ( IsFXSupported() ) + { + alSource3i(alSources[id], AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); + } + } +} + +void CChannel::Start() +{ + if ( !HasSource() ) return; + if ( !Data ) return; + + if ( bIs2D ) + { + // convert mono data to stereo + int16 *monoData = (int16*)Data; + int16 *stereoData = (int16*)tempStereoBuffer; + for (size_t i = 0; i < DataSize / 2; i++) + { + *(stereoData++) = *monoData; + *(stereoData++) = *(monoData++); + } + alBufferData(alBuffers[id], AL_FORMAT_STEREO16, tempStereoBuffer, DataSize * 2, Frequency); + } + else + alBufferData(alBuffers[id], AL_FORMAT_MONO16, Data, DataSize, Frequency); + if ( LoopPoints[0] != 0 && LoopPoints[0] != -1 ) + alBufferiv(alBuffers[id], AL_LOOP_POINTS_SOFT, LoopPoints); + alSourcei(alSources[id], AL_BUFFER, alBuffers[id]); + alSourcePlay(alSources[id]); +} + +void CChannel::Stop() +{ + if ( HasSource() ) + alSourceStop(alSources[id]); + + Reset(); +} + +bool CChannel::HasSource() +{ + return alSources[id] != AL_NONE; +} + +bool CChannel::IsUsed() +{ + if ( HasSource() ) + { + ALint sourceState; + alGetSourcei(alSources[id], AL_SOURCE_STATE, &sourceState); + return sourceState == AL_PLAYING; + } + return false; +} + +void CChannel::SetPitch(float pitch) +{ + if ( !HasSource() ) return; + alSourcef(alSources[id], AL_PITCH, pitch); +} + +void CChannel::SetGain(float gain) +{ + if ( !HasSource() ) return; + alSourcef(alSources[id], AL_GAIN, gain); +} + +void CChannel::SetVolume(int32 vol) +{ + SetGain(ALfloat(vol) / MAX_VOLUME); +} + +void CChannel::SetSampleData(void *_data, size_t _DataSize, int32 freq) +{ + Data = _data; + DataSize = _DataSize; + Frequency = freq; +} + +void CChannel::SetCurrentFreq(uint32 freq) +{ + SetPitch(ALfloat(freq) / Frequency); +} + +void CChannel::SetLoopCount(int32 count) +{ + if ( !HasSource() ) return; + + // 0: loop indefinitely, 1: play one time, 2: play two times etc... + // only > 1 needs manual processing + + if (LoopCount > 1 && count < 2) + channelsThatNeedService--; + else if (LoopCount < 2 && count > 1) + channelsThatNeedService++; + + alSourcei(alSources[id], AL_LOOPING, count == 1 ? AL_FALSE : AL_TRUE); + LoopCount = count; +} + +bool CChannel::Update() +{ + if (!HasSource()) return false; + if (LoopCount < 2) return false; + + ALint state; + alGetSourcei(alSources[id], AL_SOURCE_STATE, &state); + if (state == AL_STOPPED) { + debug("Looping channels(%d in this case) shouldn't report AL_STOPPED, but nvm\n", id); + SetLoopCount(1); + return true; + } + + assert(channelsThatNeedService > 0 && "Ref counting is broken"); + + ALint offset; + alGetSourcei(alSources[id], AL_SAMPLE_OFFSET, &offset); + + // Rewound + if (offset < LastProcessedOffset) { + LoopCount--; + if (LoopCount == 1) { + // Playing last tune... + channelsThatNeedService--; + alSourcei(alSources[id], AL_LOOPING, AL_FALSE); + } + } + LastProcessedOffset = offset; + return true; +} + +void CChannel::SetLoopPoints(ALint start, ALint end) +{ + LoopPoints[0] = start; + LoopPoints[1] = end; +} + +void CChannel::SetPosition(float x, float y, float z) +{ + if ( !HasSource() ) return; + alSource3f(alSources[id], AL_POSITION, x, y, z); +} + +void CChannel::SetDistances(float max, float min) +{ + if ( !HasSource() ) return; + alSourcef (alSources[id], AL_MAX_DISTANCE, max); + alSourcef (alSources[id], AL_REFERENCE_DISTANCE, min); + alSourcef (alSources[id], AL_MAX_GAIN, 1.0f); + alSourcef (alSources[id], AL_ROLLOFF_FACTOR, 1.0f); +} + +void CChannel::SetPan(int32 pan) +{ + SetPosition((pan-63)/64.0f, 0.0f, Sqrt(1.0f-SQR((pan-63)/64.0f))); +} + +void CChannel::ClearBuffer() +{ + if ( !HasSource() ) return; + alSourcei(alSources[id], AL_LOOPING, AL_FALSE); + alSourcei(alSources[id], AL_BUFFER, AL_NONE); + Data = nil; + DataSize = 0; +} + +void CChannel::SetReverbMix(ALuint slot, float mix) +{ + if ( !IsFXSupported() ) return; + if ( !HasSource() ) return; + if ( alFilters[id] == AL_FILTER_NULL ) return; + + Mix = mix; + EAX3_SetReverbMix(alFilters[id], mix); + alSource3i(alSources[id], AL_AUXILIARY_SEND_FILTER, slot, 0, alFilters[id]); +} + +void CChannel::UpdateReverb(ALuint slot) +{ + if ( !IsFXSupported() ) return; + if ( !HasSource() ) return; + if ( alFilters[id] == AL_FILTER_NULL ) return; + EAX3_SetReverbMix(alFilters[id], Mix); + alSource3i(alSources[id], AL_AUXILIARY_SEND_FILTER, slot, 0, alFilters[id]); +} + +#endif diff --git a/src/miami/audio/oal/channel.h b/src/miami/audio/oal/channel.h new file mode 100644 index 00000000..872646c8 --- /dev/null +++ b/src/miami/audio/oal/channel.h @@ -0,0 +1,55 @@ +#pragma once + +#ifdef AUDIO_OAL +#include "oal/oal_utils.h" +#include +#include +#include + + +class CChannel +{ + uint32 id; + float Pitch, Gain; + float Mix; + void *Data; + size_t DataSize; + int32 Frequency; + float Position[3]; + float Distances[2]; + int32 LoopCount; + ALint LoopPoints[2]; + ALint LastProcessedOffset; + bool bIs2D; +public: + static int32 channelsThatNeedService; + + static void InitChannels(); + static void DestroyChannels(); + + CChannel(); + void SetDefault(); + void Reset(); + void Init(uint32 _id, bool Is2D = false); + void Term(); + void Start(); + void Stop(); + bool HasSource(); + bool IsUsed(); + void SetPitch(float pitch); + void SetGain(float gain); + void SetVolume(int32 vol); + void SetSampleData(void *_data, size_t _DataSize, int32 freq); + void SetCurrentFreq(uint32 freq); + void SetLoopCount(int32 count); + void SetLoopPoints(ALint start, ALint end); + void SetPosition(float x, float y, float z); + void SetDistances(float max, float min); + void SetPan(int32 pan); + void ClearBuffer(); + void SetReverbMix(ALuint slot, float mix); + void UpdateReverb(ALuint slot); + bool Update(); +}; + +#endif \ No newline at end of file diff --git a/src/miami/audio/oal/oal_utils.cpp b/src/miami/audio/oal/oal_utils.cpp new file mode 100644 index 00000000..e4cb0b77 --- /dev/null +++ b/src/miami/audio/oal/oal_utils.cpp @@ -0,0 +1,181 @@ +#include "common.h" +#include "oal_utils.h" + +#ifdef AUDIO_OAL + +/* + * When linking to a static openal-soft library, + * the extension function inside the openal library conflict with the variables here. + * Therefore declare these re3 owned symbols in a private namespace. + */ + +namespace re3_openal { + +LPALGENEFFECTS alGenEffects; +LPALDELETEEFFECTS alDeleteEffects; +LPALISEFFECT alIsEffect; +LPALEFFECTI alEffecti; +LPALEFFECTIV alEffectiv; +LPALEFFECTF alEffectf; +LPALEFFECTFV alEffectfv; +LPALGETEFFECTI alGetEffecti; +LPALGETEFFECTIV alGetEffectiv; +LPALGETEFFECTF alGetEffectf; +LPALGETEFFECTFV alGetEffectfv; +LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots; +LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots; +LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot; +LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti; +LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv; +LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf; +LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv; +LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti; +LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv; +LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf; +LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv; +LPALGENFILTERS alGenFilters; +LPALDELETEFILTERS alDeleteFilters; +LPALISFILTER alIsFilter; +LPALFILTERI alFilteri; +LPALFILTERIV alFilteriv; +LPALFILTERF alFilterf; +LPALFILTERFV alFilterfv; +LPALGETFILTERI alGetFilteri; +LPALGETFILTERIV alGetFilteriv; +LPALGETFILTERF alGetFilterf; +LPALGETFILTERFV alGetFilterfv; + +} + +using namespace re3_openal; + +void EFXInit() +{ + /* Define a macro to help load the function pointers. */ +#define LOAD_PROC(T, x) ((x) = (T)alGetProcAddress(#x)) + LOAD_PROC(LPALGENEFFECTS, alGenEffects); + LOAD_PROC(LPALDELETEEFFECTS, alDeleteEffects); + LOAD_PROC(LPALISEFFECT, alIsEffect); + LOAD_PROC(LPALEFFECTI, alEffecti); + LOAD_PROC(LPALEFFECTIV, alEffectiv); + LOAD_PROC(LPALEFFECTF, alEffectf); + LOAD_PROC(LPALEFFECTFV, alEffectfv); + LOAD_PROC(LPALGETEFFECTI, alGetEffecti); + LOAD_PROC(LPALGETEFFECTIV, alGetEffectiv); + LOAD_PROC(LPALGETEFFECTF, alGetEffectf); + LOAD_PROC(LPALGETEFFECTFV, alGetEffectfv); + + LOAD_PROC(LPALGENFILTERS, alGenFilters); + LOAD_PROC(LPALDELETEFILTERS, alDeleteFilters); + LOAD_PROC(LPALISFILTER, alIsFilter); + LOAD_PROC(LPALFILTERI, alFilteri); + LOAD_PROC(LPALFILTERIV, alFilteriv); + LOAD_PROC(LPALFILTERF, alFilterf); + LOAD_PROC(LPALFILTERFV, alFilterfv); + LOAD_PROC(LPALGETFILTERI, alGetFilteri); + LOAD_PROC(LPALGETFILTERIV, alGetFilteriv); + LOAD_PROC(LPALGETFILTERF, alGetFilterf); + LOAD_PROC(LPALGETFILTERFV, alGetFilterfv); + + LOAD_PROC(LPALGENAUXILIARYEFFECTSLOTS, alGenAuxiliaryEffectSlots); + LOAD_PROC(LPALDELETEAUXILIARYEFFECTSLOTS, alDeleteAuxiliaryEffectSlots); + LOAD_PROC(LPALISAUXILIARYEFFECTSLOT, alIsAuxiliaryEffectSlot); + LOAD_PROC(LPALAUXILIARYEFFECTSLOTI, alAuxiliaryEffectSloti); + LOAD_PROC(LPALAUXILIARYEFFECTSLOTIV, alAuxiliaryEffectSlotiv); + LOAD_PROC(LPALAUXILIARYEFFECTSLOTF, alAuxiliaryEffectSlotf); + LOAD_PROC(LPALAUXILIARYEFFECTSLOTFV, alAuxiliaryEffectSlotfv); + LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTI, alGetAuxiliaryEffectSloti); + LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTIV, alGetAuxiliaryEffectSlotiv); + LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTF, alGetAuxiliaryEffectSlotf); + LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTFV, alGetAuxiliaryEffectSlotfv); +#undef LOAD_PROC +} + +void SetEffectsLevel(ALuint uiFilter, float level) +{ + alFilteri(uiFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS); + alFilterf(uiFilter, AL_LOWPASS_GAIN, 1.0f); + alFilterf(uiFilter, AL_LOWPASS_GAINHF, level); +} + +static inline float gain_to_mB(float gain) +{ + return (gain > 1e-5f) ? (float)(log10f(gain) * 2000.0f) : -10000l; +} + +static inline float mB_to_gain(float millibels) +{ + return (millibels > -10000.0f) ? powf(10.0f, millibels/2000.0f) : 0.0f; +} + +static inline float clampF(float val, float minval, float maxval) +{ + if(val >= maxval) return maxval; + if(val <= minval) return minval; + return val; +} + +void EAX3_Set(ALuint effect, const EAXLISTENERPROPERTIES *props) +{ + alEffecti (effect, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); + alEffectf (effect, AL_EAXREVERB_DENSITY, clampF(powf(props->flEnvironmentSize, 3.0f) / 16.0f, 0.0f, 1.0f)); + alEffectf (effect, AL_EAXREVERB_DIFFUSION, props->flEnvironmentDiffusion); + alEffectf (effect, AL_EAXREVERB_GAIN, mB_to_gain((float)props->lRoom)); + alEffectf (effect, AL_EAXREVERB_GAINHF, mB_to_gain((float)props->lRoomHF)); + alEffectf (effect, AL_EAXREVERB_GAINLF, mB_to_gain((float)props->lRoomLF)); + alEffectf (effect, AL_EAXREVERB_DECAY_TIME, props->flDecayTime); + alEffectf (effect, AL_EAXREVERB_DECAY_HFRATIO, props->flDecayHFRatio); + alEffectf (effect, AL_EAXREVERB_DECAY_LFRATIO, props->flDecayLFRatio); + alEffectf (effect, AL_EAXREVERB_REFLECTIONS_GAIN, clampF(mB_to_gain((float)props->lReflections), AL_EAXREVERB_MIN_REFLECTIONS_GAIN, AL_EAXREVERB_MAX_REFLECTIONS_GAIN)); + alEffectf (effect, AL_EAXREVERB_REFLECTIONS_DELAY, props->flReflectionsDelay); + alEffectfv(effect, AL_EAXREVERB_REFLECTIONS_PAN, &props->vReflectionsPan.x); + alEffectf (effect, AL_EAXREVERB_LATE_REVERB_GAIN, clampF(mB_to_gain((float)props->lReverb), AL_EAXREVERB_MIN_LATE_REVERB_GAIN, AL_EAXREVERB_MAX_LATE_REVERB_GAIN)); + alEffectf (effect, AL_EAXREVERB_LATE_REVERB_DELAY, props->flReverbDelay); + alEffectfv(effect, AL_EAXREVERB_LATE_REVERB_PAN, &props->vReverbPan.x); + alEffectf (effect, AL_EAXREVERB_ECHO_TIME, props->flEchoTime); + alEffectf (effect, AL_EAXREVERB_ECHO_DEPTH, props->flEchoDepth); + alEffectf (effect, AL_EAXREVERB_MODULATION_TIME, props->flModulationTime); + alEffectf (effect, AL_EAXREVERB_MODULATION_DEPTH, props->flModulationDepth); + alEffectf (effect, AL_EAXREVERB_AIR_ABSORPTION_GAINHF, clampF(mB_to_gain(props->flAirAbsorptionHF), AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF, AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF)); + alEffectf (effect, AL_EAXREVERB_HFREFERENCE, props->flHFReference); + alEffectf (effect, AL_EAXREVERB_LFREFERENCE, props->flLFReference); + alEffectf (effect, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, props->flRoomRolloffFactor); + alEffecti (effect, AL_EAXREVERB_DECAY_HFLIMIT, (props->ulFlags&EAXLISTENERFLAGS_DECAYHFLIMIT) ? AL_TRUE : AL_FALSE); +} + +void EFX_Set(ALuint effect, const EAXLISTENERPROPERTIES *props) +{ + alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_REVERB); + + alEffectf(effect, AL_REVERB_DENSITY, clampF(powf(props->flEnvironmentSize, 3.0f) / 16.0f, 0.0f, 1.0f)); + alEffectf(effect, AL_REVERB_DIFFUSION, props->flEnvironmentDiffusion); + alEffectf(effect, AL_REVERB_GAIN, mB_to_gain((float)props->lRoom)); + alEffectf(effect, AL_REVERB_GAINHF, mB_to_gain((float)props->lRoomHF)); + alEffectf(effect, AL_REVERB_DECAY_TIME, props->flDecayTime); + alEffectf(effect, AL_REVERB_DECAY_HFRATIO, props->flDecayHFRatio); + alEffectf(effect, AL_REVERB_REFLECTIONS_GAIN, clampF(mB_to_gain((float)props->lReflections), AL_EAXREVERB_MIN_REFLECTIONS_GAIN, AL_EAXREVERB_MAX_REFLECTIONS_GAIN)); + alEffectf(effect, AL_REVERB_REFLECTIONS_DELAY, props->flReflectionsDelay); + alEffectf(effect, AL_REVERB_LATE_REVERB_GAIN, clampF(mB_to_gain((float)props->lReverb), AL_EAXREVERB_MIN_LATE_REVERB_GAIN, AL_EAXREVERB_MAX_LATE_REVERB_GAIN)); + alEffectf(effect, AL_REVERB_LATE_REVERB_DELAY, props->flReverbDelay); + alEffectf(effect, AL_REVERB_AIR_ABSORPTION_GAINHF, clampF(mB_to_gain(props->flAirAbsorptionHF), AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF, AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF)); + alEffectf(effect, AL_REVERB_ROOM_ROLLOFF_FACTOR, props->flRoomRolloffFactor); + alEffecti(effect, AL_REVERB_DECAY_HFLIMIT, (props->ulFlags&EAXLISTENERFLAGS_DECAYHFLIMIT) ? AL_TRUE : AL_FALSE); +} + +void EAX3_SetReverbMix(ALuint filter, float mix) +{ + //long vol=(long)linear_to_dB(mix); + //DSPROPERTY_EAXBUFFER_ROOMHF, + //DSPROPERTY_EAXBUFFER_ROOM, + //DSPROPERTY_EAXBUFFER_REVERBMIX, + + long mbvol = gain_to_mB(mix); + float mb = mbvol; + float mbhf = mbvol; + + alFilteri(filter, AL_FILTER_TYPE, AL_FILTER_LOWPASS); + alFilterf(filter, AL_LOWPASS_GAIN, mB_to_gain(Min(mb, 0.0f))); + alFilterf(filter, AL_LOWPASS_GAINHF, mB_to_gain(mbhf)); +} + +#endif \ No newline at end of file diff --git a/src/miami/audio/oal/oal_utils.h b/src/miami/audio/oal/oal_utils.h new file mode 100644 index 00000000..f0fa090a --- /dev/null +++ b/src/miami/audio/oal/oal_utils.h @@ -0,0 +1,54 @@ +#pragma once + +#ifdef AUDIO_OAL +#include "eax.h" +#include "AL/efx.h" + + +void EFXInit(); +void EAX3_Set(ALuint effect, const EAXLISTENERPROPERTIES *props); +void EFX_Set(ALuint effect, const EAXLISTENERPROPERTIES *props); +void EAX3_SetReverbMix(ALuint filter, float mix); +void SetEffectsLevel(ALuint uiFilter, float level); + +namespace re3_openal { + +extern LPALGENEFFECTS alGenEffects; +extern LPALDELETEEFFECTS alDeleteEffects; +extern LPALISEFFECT alIsEffect; +extern LPALEFFECTI alEffecti; +extern LPALEFFECTIV alEffectiv; +extern LPALEFFECTF alEffectf; +extern LPALEFFECTFV alEffectfv; +extern LPALGETEFFECTI alGetEffecti; +extern LPALGETEFFECTIV alGetEffectiv; +extern LPALGETEFFECTF alGetEffectf; +extern LPALGETEFFECTFV alGetEffectfv; +extern LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots; +extern LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots; +extern LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot; +extern LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti; +extern LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv; +extern LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf; +extern LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv; +extern LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti; +extern LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv; +extern LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf; +extern LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv; +extern LPALGENFILTERS alGenFilters; +extern LPALDELETEFILTERS alDeleteFilters; +extern LPALISFILTER alIsFilter; +extern LPALFILTERI alFilteri; +extern LPALFILTERIV alFilteriv; +extern LPALFILTERF alFilterf; +extern LPALFILTERFV alFilterfv; +extern LPALGETFILTERI alGetFilteri; +extern LPALGETFILTERIV alGetFilteriv; +extern LPALGETFILTERF alGetFilterf; +extern LPALGETFILTERFV alGetFilterfv; + +} + +using namespace re3_openal; + +#endif diff --git a/src/miami/audio/oal/stream.cpp b/src/miami/audio/oal/stream.cpp new file mode 100644 index 00000000..3789573e --- /dev/null +++ b/src/miami/audio/oal/stream.cpp @@ -0,0 +1,1831 @@ +#include "common.h" + +#ifdef AUDIO_OAL + +#if defined _MSC_VER && !defined CMAKE_NO_AUTOLINK +#ifdef AUDIO_OAL_USE_SNDFILE +#pragma comment( lib, "libsndfile-1.lib" ) +#endif +#ifdef AUDIO_OAL_USE_MPG123 +#pragma comment( lib, "libmpg123-0.lib" ) +#endif +#endif +#ifdef AUDIO_OAL_USE_SNDFILE +#include +#endif +#ifdef AUDIO_OAL_USE_MPG123 +#include +#endif +#ifdef AUDIO_OAL_USE_OPUS +#include +#endif + +#include +#include + +#ifdef MULTITHREADED_AUDIO +#include +#include +#include +#include +#include "MusicManager.h" +#include "stream.h" + +std::thread gAudioThread; +std::mutex gAudioThreadQueueMutex; +std::condition_variable gAudioThreadCv; +bool gAudioThreadTerm = false; +std::queue gStreamsToProcess; // values are not unique, we will handle that ourself +std::queue> gStreamsToClose; +#else +#include "stream.h" +#endif + +#include "sampman.h" + +#ifndef _WIN32 +#include "crossplatform.h" +#endif + +/* +As we ran onto an issue of having different volume levels for mono streams +and stereo streams we are now handling all the stereo panning ourselves. +Each stream now has two sources - one panned to the left and one to the right, +and uses two separate buffers to store data for each individual channel. +For that we also have to reshuffle all decoded PCM stereo data from LRLRLRLR to +LLLLRRRR (handled by CSortStereoBuffer). +*/ + +class CSortStereoBuffer +{ + uint16* PcmBuf; + size_t BufSize; +//#ifdef MULTITHREADED_AUDIO +// std::mutex Mutex; +//#endif + +public: + CSortStereoBuffer() : PcmBuf(nil), BufSize(0) {} + ~CSortStereoBuffer() + { + if (PcmBuf) + free(PcmBuf); + } + + uint16* GetBuffer(size_t size) + { + if (size == 0) return nil; + if (!PcmBuf) + { + BufSize = size; + PcmBuf = (uint16*)malloc(BufSize); + } + else if (BufSize < size) + { + BufSize = size; + PcmBuf = (uint16*)realloc(PcmBuf, size); + } + return PcmBuf; + } + + void SortStereo(void* buf, size_t size) + { +//#ifdef MULTITHREADED_AUDIO +// std::lock_guard lock(Mutex); +//#endif + uint16* InBuf = (uint16*)buf; + uint16* OutBuf = GetBuffer(size); + + if (!OutBuf) return; + + size_t rightStart = size / 4; + for (size_t i = 0; i < size / 4; i++) + { + OutBuf[i] = InBuf[i*2]; + OutBuf[i+rightStart] = InBuf[i*2+1]; + } + + memcpy(InBuf, OutBuf, size); + } + +}; + +CSortStereoBuffer SortStereoBuffer; + +class CImaADPCMDecoder +{ + const uint16 StepTable[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, + 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, + 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, + 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, + 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, + 32767 + }; + + int16 Sample, StepIndex; + +public: + CImaADPCMDecoder() + { + Init(0, 0); + } + + void Init(int16 _Sample, int16 _StepIndex) + { + Sample = _Sample; + StepIndex = _StepIndex; + } + + void Decode(uint8 *inbuf, int16 *_outbuf, size_t size) + { + int16* outbuf = _outbuf; + for (size_t i = 0; i < size; i++) + { + *(outbuf++) = DecodeSample(inbuf[i] & 0xF); + *(outbuf++) = DecodeSample(inbuf[i] >> 4); + } + } + + int16 DecodeSample(uint8 adpcm) + { + uint16 step = StepTable[StepIndex]; + + if (adpcm & 4) + StepIndex += ((adpcm & 3) + 1) * 2; + else + StepIndex--; + + StepIndex = Clamp(StepIndex, 0, 88); + + int delta = step >> 3; + if (adpcm & 1) delta += step >> 2; + if (adpcm & 2) delta += step >> 1; + if (adpcm & 4) delta += step; + if (adpcm & 8) delta = -delta; + + int newSample = Sample + delta; + Sample = Clamp(newSample, -32768, 32767); + return Sample; + } +}; + +class CWavFile : public IDecoder +{ + enum + { + WAVEFMT_PCM = 1, + WAVEFMT_IMA_ADPCM = 0x11, + WAVEFMT_XBOX_ADPCM = 0x69, + }; + + struct tDataHeader + { + uint32 ID; + uint32 Size; + }; + + struct tFormatHeader + { + uint16 AudioFormat; + uint16 NumChannels; + uint32 SampleRate; + uint32 ByteRate; + uint16 BlockAlign; + uint16 BitsPerSample; + uint16 extra[2]; // adpcm only + + tFormatHeader() { memset(this, 0, sizeof(*this)); } + }; + + FILE *m_pFile; + bool m_bIsOpen; + + tFormatHeader m_FormatHeader; + + uint32 m_DataStartOffset; // TODO: 64 bit? + uint32 m_nSampleCount; + uint32 m_nSamplesPerBlock; + + // ADPCM things + uint8 *m_pAdpcmBuffer; + int16 **m_ppPcmBuffers; + CImaADPCMDecoder *m_pAdpcmDecoders; + + void Close() + { + if (m_pFile) { + fclose(m_pFile); + m_pFile = nil; + } + delete[] m_pAdpcmBuffer; + delete[] m_ppPcmBuffers; + delete[] m_pAdpcmDecoders; + } + + uint32 GetCurrentSample() const + { + // TODO: 64 bit? + uint32 FilePos = ftell(m_pFile); + if (FilePos <= m_DataStartOffset) + return 0; + return (FilePos - m_DataStartOffset) / m_FormatHeader.BlockAlign * m_nSamplesPerBlock; + } + +public: + CWavFile(const char* path) : m_bIsOpen(false), m_DataStartOffset(0), m_nSampleCount(0), m_nSamplesPerBlock(0), m_pAdpcmBuffer(nil), m_ppPcmBuffers(nil), m_pAdpcmDecoders(nil) + { + m_pFile = fopen(path, "rb"); + if (!m_pFile) return; + +#define CLOSE_ON_ERROR(op)\ + if (op) { \ + Close(); \ + return; \ + } + + tDataHeader DataHeader; + + CLOSE_ON_ERROR(fread(&DataHeader, sizeof(DataHeader), 1, m_pFile) == 0); + CLOSE_ON_ERROR(DataHeader.ID != 'FFIR'); + + // TODO? validate filesizes + + int WAVE; + CLOSE_ON_ERROR(fread(&WAVE, 4, 1, m_pFile) == 0); + CLOSE_ON_ERROR(WAVE != 'EVAW') + CLOSE_ON_ERROR(fread(&DataHeader, sizeof(DataHeader), 1, m_pFile) == 0); + CLOSE_ON_ERROR(DataHeader.ID != ' tmf'); + + CLOSE_ON_ERROR(fread(&m_FormatHeader, Min(DataHeader.Size, sizeof(tFormatHeader)), 1, m_pFile) == 0); + CLOSE_ON_ERROR(DataHeader.Size > sizeof(tFormatHeader)); + + switch (m_FormatHeader.AudioFormat) + { + case WAVEFMT_XBOX_ADPCM: + m_FormatHeader.AudioFormat = WAVEFMT_IMA_ADPCM; + case WAVEFMT_IMA_ADPCM: + m_nSamplesPerBlock = (m_FormatHeader.BlockAlign / m_FormatHeader.NumChannels - 4) * 2 + 1; + m_pAdpcmBuffer = new uint8[m_FormatHeader.BlockAlign]; + m_ppPcmBuffers = new int16*[m_FormatHeader.NumChannels]; + m_pAdpcmDecoders = new CImaADPCMDecoder[m_FormatHeader.NumChannels]; + break; + case WAVEFMT_PCM: + m_nSamplesPerBlock = 1; + if (m_FormatHeader.BitsPerSample != 16) + { + debug("Unsupported PCM (%d bits), only signed 16-bit is supported (%s)\n", m_FormatHeader.BitsPerSample, path); + Close(); + return; + } + break; + default: + debug("Unsupported wav format 0x%x (%s)\n", m_FormatHeader.AudioFormat, path); + Close(); + return; + } + + while (true) { + CLOSE_ON_ERROR(fread(&DataHeader, sizeof(DataHeader), 1, m_pFile) == 0); + if (DataHeader.ID == 'atad') + break; + fseek(m_pFile, DataHeader.Size, SEEK_CUR); + // TODO? validate data size + // maybe check if there no extreme custom headers that might break this + } + + m_DataStartOffset = ftell(m_pFile); + m_nSampleCount = DataHeader.Size / m_FormatHeader.BlockAlign * m_nSamplesPerBlock; + + m_bIsOpen = true; +#undef CLOSE_ON_ERROR + } + + void FileOpen() + { + } + + ~CWavFile() + { + Close(); + } + + bool IsOpened() + { + return m_bIsOpen; + } + + + uint32 GetSampleSize() + { + return sizeof(uint16); + } + + uint32 GetSampleCount() + { + return m_nSampleCount; + } + + uint32 GetSampleRate() + { + return m_FormatHeader.SampleRate; + } + + uint32 GetChannels() + { + return m_FormatHeader.NumChannels; + } + + void Seek(uint32 milliseconds) + { + if (!IsOpened()) return; + fseek(m_pFile, m_DataStartOffset + ms2samples(milliseconds) / m_nSamplesPerBlock * m_FormatHeader.BlockAlign, SEEK_SET); + } + + uint32 Tell() + { + if (!IsOpened()) return 0; + return samples2ms(GetCurrentSample()); + } + +#define SAMPLES_IN_LINE (8) + + uint32 Decode(void* buffer) + { + if (!IsOpened()) return 0; + + if (m_FormatHeader.AudioFormat == WAVEFMT_PCM) + { + // just read the file and sort the samples + uint32 size = fread(buffer, 1, GetBufferSize(), m_pFile); + if (m_FormatHeader.NumChannels == 2) + SortStereoBuffer.SortStereo(buffer, size); + return size; + } + else if (m_FormatHeader.AudioFormat == WAVEFMT_IMA_ADPCM) + { + // trim the buffer size if we're at the end of our file + uint32 nMaxSamples = GetBufferSamples() / m_FormatHeader.NumChannels; + uint32 nSamplesLeft = m_nSampleCount - GetCurrentSample(); + nMaxSamples = Min(nMaxSamples, nSamplesLeft); + + // align sample count to our block + nMaxSamples = nMaxSamples / m_nSamplesPerBlock * m_nSamplesPerBlock; + + // count the size of output buffer + uint32 OutBufSizePerChannel = nMaxSamples * GetSampleSize(); + uint32 OutBufSize = OutBufSizePerChannel * m_FormatHeader.NumChannels; + + // calculate the pointers to individual channel buffers + for (uint32 i = 0; i < m_FormatHeader.NumChannels; i++) + m_ppPcmBuffers[i] = (int16*)((int8*)buffer + OutBufSizePerChannel * i); + + uint32 samplesRead = 0; + while (samplesRead < nMaxSamples) + { + // read the file + uint8 *pAdpcmBuf = m_pAdpcmBuffer; + if (fread(m_pAdpcmBuffer, 1, m_FormatHeader.BlockAlign, m_pFile) == 0) + return 0; + + // get the first sample in adpcm block and initialise the decoder(s) + for (uint32 i = 0; i < m_FormatHeader.NumChannels; i++) + { + int16 Sample = *(int16*)pAdpcmBuf; + pAdpcmBuf += sizeof(int16); + int16 Step = *(int16*)pAdpcmBuf; + pAdpcmBuf += sizeof(int16); + m_pAdpcmDecoders[i].Init(Sample, Step); + *(m_ppPcmBuffers[i]) = Sample; + m_ppPcmBuffers[i]++; + } + samplesRead++; + + // decode the rest of the block + for (uint32 s = 1; s < m_nSamplesPerBlock; s += SAMPLES_IN_LINE) + { + for (uint32 i = 0; i < m_FormatHeader.NumChannels; i++) + { + m_pAdpcmDecoders[i].Decode(pAdpcmBuf, m_ppPcmBuffers[i], SAMPLES_IN_LINE / 2); + pAdpcmBuf += SAMPLES_IN_LINE / 2; + m_ppPcmBuffers[i] += SAMPLES_IN_LINE; + } + samplesRead += SAMPLES_IN_LINE; + } + } + return OutBufSize; + } + return 0; + } +}; + +#ifdef AUDIO_OAL_USE_SNDFILE +class CSndFile : public IDecoder +{ + SNDFILE *m_pfSound; + SF_INFO m_soundInfo; +public: + CSndFile(const char *path) : + m_pfSound(nil) + { + memset(&m_soundInfo, 0, sizeof(m_soundInfo)); + m_pfSound = sf_open(path, SFM_READ, &m_soundInfo); + } + + void FileOpen() + { + } + + ~CSndFile() + { + if ( m_pfSound ) + { + sf_close(m_pfSound); + m_pfSound = nil; + } + } + + bool IsOpened() + { + return m_pfSound != nil; + } + + uint32 GetSampleSize() + { + return sizeof(uint16); + } + + uint32 GetSampleCount() + { + return m_soundInfo.frames; + } + + uint32 GetSampleRate() + { + return m_soundInfo.samplerate; + } + + uint32 GetChannels() + { + return m_soundInfo.channels; + } + + void Seek(uint32 milliseconds) + { + if ( !IsOpened() ) return; + sf_seek(m_pfSound, ms2samples(milliseconds), SF_SEEK_SET); + } + + uint32 Tell() + { + if ( !IsOpened() ) return 0; + return samples2ms(sf_seek(m_pfSound, 0, SF_SEEK_CUR)); + } + + uint32 Decode(void *buffer) + { + if ( !IsOpened() ) return 0; + + size_t size = sf_read_short(m_pfSound, (short*)buffer, GetBufferSamples()) * GetSampleSize(); + if (GetChannels()==2) + SortStereoBuffer.SortStereo(buffer, size); + return size; + } +}; +#endif + +#ifdef AUDIO_OAL_USE_MPG123 + +class CMP3File : public IDecoder +{ +protected: + mpg123_handle *m_pMH; + bool m_bOpened; + uint32 m_nRate; + uint32 m_nChannels; + const char* m_pPath; + bool m_bFileNotOpenedYet; + + CMP3File() : + m_pMH(nil), + m_bOpened(false), + m_nRate(0), + m_bFileNotOpenedYet(false), + m_nChannels(0) {} +public: + CMP3File(const char *path) : + m_pMH(nil), + m_bOpened(false), + m_nRate(0), + m_nChannels(0), + m_pPath(path), + m_bFileNotOpenedYet(false) + { + m_pMH = mpg123_new(nil, nil); + if ( m_pMH ) + { + mpg123_param(m_pMH, MPG123_FLAGS, MPG123_SEEKBUFFER | MPG123_GAPLESS, 0.0); + + m_bOpened = true; + m_bFileNotOpenedYet = true; + // It's possible to move this to audioFileOpsThread(), but effect isn't noticable + probably not compatible with our current cutscene audio handling +#if 1 + FileOpen(); +#endif + } + } + + void FileOpen() + { + if(!m_bFileNotOpenedYet) return; + + long rate = 0; + int channels = 0; + int encoding = 0; + m_bOpened = mpg123_open(m_pMH, m_pPath) == MPG123_OK + && mpg123_getformat(m_pMH, &rate, &channels, &encoding) == MPG123_OK; + + m_nRate = rate; + m_nChannels = channels; + + if(IsOpened()) { + mpg123_format_none(m_pMH); + mpg123_format(m_pMH, rate, channels, encoding); + } + m_bFileNotOpenedYet = false; + } + + ~CMP3File() + { + if ( m_pMH ) + { + mpg123_close(m_pMH); + mpg123_delete(m_pMH); + m_pMH = nil; + } + } + + bool IsOpened() + { + return m_bOpened; + } + + uint32 GetSampleSize() + { + return sizeof(uint16); + } + + uint32 GetSampleCount() + { + if ( !IsOpened() || m_bFileNotOpenedYet ) return 0; + return mpg123_length(m_pMH); + } + + uint32 GetSampleRate() + { + return m_nRate; + } + + uint32 GetChannels() + { + return m_nChannels; + } + + void Seek(uint32 milliseconds) + { + if ( !IsOpened() || m_bFileNotOpenedYet ) return; + mpg123_seek(m_pMH, ms2samples(milliseconds), SEEK_SET); + } + + uint32 Tell() + { + if ( !IsOpened() || m_bFileNotOpenedYet ) return 0; + return samples2ms(mpg123_tell(m_pMH)); + } + + uint32 Decode(void *buffer) + { + if ( !IsOpened() || m_bFileNotOpenedYet ) return 0; + + size_t size; + int err = mpg123_read(m_pMH, (unsigned char *)buffer, GetBufferSize(), &size); +#if defined(__LP64__) || defined(_WIN64) + assert("We can't handle audio files more then 2 GB yet :shrug:" && (size < UINT32_MAX)); +#endif + if (err != MPG123_OK && err != MPG123_DONE) return 0; + if (GetChannels() == 2) + SortStereoBuffer.SortStereo(buffer, size); + return (uint32)size; + } +}; + +class CADFFile : public CMP3File +{ + static ssize_t r_read(void* fh, void* buf, size_t size) + { + size_t bytesRead = fread(buf, 1, size, (FILE*)fh); + uint8* _buf = (uint8*)buf; + for (size_t i = 0; i < size; i++) + _buf[i] ^= 0x22; + return bytesRead; + } + static off_t r_seek(void* fh, off_t pos, int seekType) + { + fseek((FILE*)fh, pos, seekType); + return ftell((FILE*)fh); + } + static void r_close(void* fh) + { + fclose((FILE*)fh); + } +public: + CADFFile(const char* path) + { + m_pMH = mpg123_new(nil, nil); + if (m_pMH) + { + mpg123_param(m_pMH, MPG123_FLAGS, MPG123_SEEKBUFFER | MPG123_GAPLESS, 0.0); + + m_bOpened = true; + m_bFileNotOpenedYet = true; + m_pPath = path; + // It's possible to move this to audioFileOpsThread(), but effect isn't noticable + probably not compatible with our current cutscene audio handling +#if 1 + FileOpen(); +#endif + + } + } + + void FileOpen() + { + if(!m_bFileNotOpenedYet) return; + + long rate = 0; + int channels = 0; + int encoding = 0; + + FILE *f = fopen(m_pPath, "rb"); + + m_bOpened = f && mpg123_replace_reader_handle(m_pMH, r_read, r_seek, r_close) == MPG123_OK + && mpg123_open_handle(m_pMH, f) == MPG123_OK && mpg123_getformat(m_pMH, &rate, &channels, &encoding) == MPG123_OK; + + m_nRate = rate; + m_nChannels = channels; + + if(IsOpened()) { + mpg123_format_none(m_pMH); + mpg123_format(m_pMH, rate, channels, encoding); + } + + m_bFileNotOpenedYet = false; + } +}; + +#endif +#define VAG_LINE_SIZE (0x10) +#define VAG_SAMPLES_IN_LINE (28) + +class CVagDecoder +{ + const double f[5][2] = { { 0.0, 0.0 }, + { 60.0 / 64.0, 0.0 }, + { 115.0 / 64.0, -52.0 / 64.0 }, + { 98.0 / 64.0, -55.0 / 64.0 }, + { 122.0 / 64.0, -60.0 / 64.0 } }; + + double s_1; + double s_2; +public: + CVagDecoder() + { + ResetState(); + } + + void ResetState() + { + s_1 = s_2 = 0.0; + } + + static short quantize(double sample) + { + int a = int(sample + 0.5); + return short(Clamp(a, -32768, 32767)); + } + + void Decode(void* _inbuf, int16* _outbuf, size_t size) + { + uint8* inbuf = (uint8*)_inbuf; + int16* outbuf = _outbuf; + size &= ~(VAG_LINE_SIZE - 1); + + while (size > 0) { + double samples[VAG_SAMPLES_IN_LINE]; + + int predict_nr, shift_factor, flags; + predict_nr = *(inbuf++); + shift_factor = predict_nr & 0xf; + predict_nr >>= 4; + flags = *(inbuf++); + if (flags == 7) // TODO: ignore? + break; + for (int i = 0; i < VAG_SAMPLES_IN_LINE; i += 2) { + int d = *(inbuf++); + int16 s = int16((d & 0xf) << 12); + samples[i] = (double)(s >> shift_factor); + s = int16((d & 0xf0) << 8); + samples[i + 1] = (double)(s >> shift_factor); + } + + for (int i = 0; i < VAG_SAMPLES_IN_LINE; i++) { + samples[i] = samples[i] + s_1 * f[predict_nr][0] + s_2 * f[predict_nr][1]; + s_2 = s_1; + s_1 = samples[i]; + *(outbuf++) = quantize(samples[i] + 0.5); + } + size -= VAG_LINE_SIZE; + } + } +}; + +#define VB_BLOCK_SIZE (0x2000) +#define NUM_VAG_LINES_IN_BLOCK (VB_BLOCK_SIZE / VAG_LINE_SIZE) +#define NUM_VAG_SAMPLES_IN_BLOCK (NUM_VAG_LINES_IN_BLOCK * VAG_SAMPLES_IN_LINE) + +class CVbFile : public IDecoder +{ + FILE *m_pFile; + CVagDecoder *m_pVagDecoders; + + size_t m_FileSize; + size_t m_nNumberOfBlocks; + + uint32 m_nSampleRate; + uint8 m_nChannels; + bool m_bBlockRead; + uint16 m_LineInBlock; + size_t m_CurrentBlock; + + uint8 **m_ppVagBuffers; // buffers that cache actual ADPCM file data + int16 **m_ppPcmBuffers; + + void ReadBlock(int32 block = -1) + { + // just read next block if -1 + if (block != -1) + fseek(m_pFile, block * m_nChannels * VB_BLOCK_SIZE, SEEK_SET); + + for (int i = 0; i < m_nChannels; i++) + fread(m_ppVagBuffers[i], VB_BLOCK_SIZE, 1, m_pFile); + m_bBlockRead = true; + } + +public: + CVbFile(const char* path, uint32 nSampleRate = 32000, uint8 nChannels = 2) : m_nSampleRate(nSampleRate), m_nChannels(nChannels), m_pVagDecoders(nil), m_ppVagBuffers(nil), m_ppPcmBuffers(nil), + m_FileSize(0), m_nNumberOfBlocks(0), m_bBlockRead(false), m_LineInBlock(0), m_CurrentBlock(0) + { + m_pFile = fopen(path, "rb"); + if (!m_pFile) return; + + fseek(m_pFile, 0, SEEK_END); + m_FileSize = ftell(m_pFile); + fseek(m_pFile, 0, SEEK_SET); + + m_nNumberOfBlocks = m_FileSize / (nChannels * VB_BLOCK_SIZE); + m_pVagDecoders = new CVagDecoder[nChannels]; + m_ppVagBuffers = new uint8*[nChannels]; + m_ppPcmBuffers = new int16*[nChannels]; + for (uint8 i = 0; i < nChannels; i++) + m_ppVagBuffers[i] = new uint8[VB_BLOCK_SIZE]; + } + + void FileOpen() + { + } + + ~CVbFile() + { + if (m_pFile) + { + fclose(m_pFile); + + delete[] m_pVagDecoders; + for (int i = 0; i < m_nChannels; i++) + delete[] m_ppVagBuffers[i]; + delete[] m_ppVagBuffers; + delete[] m_ppPcmBuffers; + } + } + + bool IsOpened() + { + return m_pFile != nil; + } + + uint32 GetSampleSize() + { + return sizeof(uint16); + } + + uint32 GetSampleCount() + { + if (!IsOpened()) return 0; + return m_nNumberOfBlocks * NUM_VAG_LINES_IN_BLOCK * VAG_SAMPLES_IN_LINE; + } + + uint32 GetSampleRate() + { + return m_nSampleRate; + } + + uint32 GetChannels() + { + return m_nChannels; + } + + void Seek(uint32 milliseconds) + { + if (!IsOpened()) return; + uint32 samples = ms2samples(milliseconds); + + // find the block of our sample + uint32 block = samples / NUM_VAG_SAMPLES_IN_BLOCK; + if (block > m_nNumberOfBlocks) + { + samples = 0; + block = 0; + } + if (block != m_CurrentBlock) + m_bBlockRead = false; + + // find a line of our sample within our block + uint32 remainingSamples = samples - block * NUM_VAG_SAMPLES_IN_BLOCK; + uint32 newLine = remainingSamples / VAG_SAMPLES_IN_LINE / VAG_LINE_SIZE; + + if (m_CurrentBlock != block || m_LineInBlock != newLine) + { + m_CurrentBlock = block; + m_LineInBlock = newLine; + for (uint32 i = 0; i < GetChannels(); i++) + m_pVagDecoders[i].ResetState(); + } + + } + + uint32 Tell() + { + if (!IsOpened()) return 0; + uint32 pos = (m_CurrentBlock * NUM_VAG_LINES_IN_BLOCK + m_LineInBlock) * VAG_SAMPLES_IN_LINE; + return samples2ms(pos); + } + + uint32 Decode(void* buffer) + { + if (!IsOpened()) return 0; + + if (m_CurrentBlock >= m_nNumberOfBlocks) return 0; + + // cache current ADPCM block + if (!m_bBlockRead) + ReadBlock(m_CurrentBlock); + + // trim the buffer size if we're at the end of our file + int numberOfRequiredLines = GetBufferSamples() / m_nChannels / VAG_SAMPLES_IN_LINE; + int numberOfRemainingLines = (m_nNumberOfBlocks - m_CurrentBlock) * NUM_VAG_LINES_IN_BLOCK - m_LineInBlock; + int bufSizePerChannel = Min(numberOfRequiredLines, numberOfRemainingLines) * VAG_SAMPLES_IN_LINE * GetSampleSize(); + + // calculate the pointers to individual channel buffers + for (uint32 i = 0; i < m_nChannels; i++) + m_ppPcmBuffers[i] = (int16*)((int8*)buffer + bufSizePerChannel * i); + + int size = 0; + while (size < bufSizePerChannel) + { + // decode the VAG lines + for (uint32 i = 0; i < m_nChannels; i++) + { + m_pVagDecoders[i].Decode(m_ppVagBuffers[i] + m_LineInBlock * VAG_LINE_SIZE, m_ppPcmBuffers[i], VAG_LINE_SIZE); + m_ppPcmBuffers[i] += VAG_SAMPLES_IN_LINE; + } + size += VAG_SAMPLES_IN_LINE * GetSampleSize(); + m_LineInBlock++; + + // block is over, read the next block + if (m_LineInBlock >= NUM_VAG_LINES_IN_BLOCK) + { + m_CurrentBlock++; + if (m_CurrentBlock >= m_nNumberOfBlocks) // end of file + break; + m_LineInBlock = 0; + ReadBlock(); + } + } + + return bufSizePerChannel * m_nChannels; + } +}; +#ifdef AUDIO_OAL_USE_OPUS +class COpusFile : public IDecoder +{ + OggOpusFile *m_FileH; + bool m_bOpened; + uint32 m_nRate; + uint32 m_nChannels; +public: + COpusFile(const char *path) : m_FileH(nil), + m_bOpened(false), + m_nRate(0), + m_nChannels(0) + { + int ret; + m_FileH = op_open_file(path, &ret); + + if (m_FileH) { + m_nChannels = op_head(m_FileH, 0)->channel_count; + m_nRate = 48000; + const OpusTags *tags = op_tags(m_FileH, 0); + for (int i = 0; i < tags->comments; i++) { + if (strncmp(tags->user_comments[i], "SAMPLERATE", sizeof("SAMPLERATE")-1) == 0) + { + sscanf(tags->user_comments[i], "SAMPLERATE=%i", &m_nRate); + break; + } + } + + m_bOpened = true; + } + } + + void FileOpen() + { + } + + ~COpusFile() + { + if (m_FileH) + { + op_free(m_FileH); + m_FileH = nil; + } + } + + bool IsOpened() + { + return m_bOpened; + } + + uint32 GetSampleSize() + { + return sizeof(uint16); + } + + uint32 GetSampleCount() + { + if ( !IsOpened() ) return 0; + return op_pcm_total(m_FileH, 0); + } + + uint32 GetSampleRate() + { + return m_nRate; + } + + uint32 GetChannels() + { + return m_nChannels; + } + + void Seek(uint32 milliseconds) + { + if ( !IsOpened() ) return; + op_pcm_seek(m_FileH, ms2samples(milliseconds) / GetChannels()); + } + + uint32 Tell() + { + if ( !IsOpened() ) return 0; + return samples2ms(op_pcm_tell(m_FileH) * GetChannels()); + } + + uint32 Decode(void *buffer) + { + if ( !IsOpened() ) return 0; + + int size = op_read(m_FileH, (opus_int16 *)buffer, GetBufferSamples(), NULL); + + if (size < 0) + return 0; + + if (GetChannels() == 2) + SortStereoBuffer.SortStereo(buffer, size * m_nChannels * GetSampleSize()); + + return size * m_nChannels * GetSampleSize(); + } +}; +#endif + + +// For multi-thread: Someone always acquire stream's mutex before entering here +void +CStream::BuffersShouldBeFilled() +{ +#ifdef MULTITHREADED_AUDIO + if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE) { + std::queue> tempQueue; + for(int i = 0; i < NUM_STREAMBUFFERS / 2; i++) { + tempQueue.push(std::pair(m_alBuffers[i * 2], m_alBuffers[i * 2 + 1])); + } + m_fillBuffers.swap(tempQueue); + + FlagAsToBeProcessed(); + + m_bActive = true; // to allow Update() to queue the filled buffers & play + return; + } + std::queue>().swap(m_fillBuffers); +#endif + if ( FillBuffers() != 0 ) + { + SetPlay(true); + } +} + +// returns whether it's queued (not on multi-thread) +bool +CStream::BufferShouldBeFilledAndQueued(std::pair* bufs) +{ +#ifdef MULTITHREADED_AUDIO + if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE) + m_fillBuffers.push(*bufs); + else +#endif + { + ALuint alBuffers[2] = {(*bufs).first, (*bufs).second}; // left - right + if (FillBuffer(alBuffers)) { + alSourceQueueBuffers(m_pAlSources[0], 1, &alBuffers[0]); + alSourceQueueBuffers(m_pAlSources[1], 1, &alBuffers[1]); + return true; + } + } + return false; +} + +#ifdef MULTITHREADED_AUDIO +void +CStream::FlagAsToBeProcessed(bool close) +{ + if (!close && MusicManager.m_nMusicMode == MUSICMODE_CUTSCENE) + return; + + gAudioThreadQueueMutex.lock(); + if (close) + gStreamsToClose.push(std::pair(m_pSoundFile ? m_pSoundFile : nil, m_pBuffer ? m_pBuffer : nil)); + else + gStreamsToProcess.push(this); + + gAudioThreadQueueMutex.unlock(); + + gAudioThreadCv.notify_one(); +} + +void audioFileOpsThread() +{ + do + { + CStream *stream; + { + // Just a semaphore + std::unique_lock queueMutex(gAudioThreadQueueMutex); + gAudioThreadCv.wait(queueMutex, [] { return gStreamsToProcess.size() > 0 || gStreamsToClose.size() > 0 || gAudioThreadTerm; }); + if (gAudioThreadTerm) + return; + + if (!gStreamsToClose.empty()) { + auto streamToClose = gStreamsToClose.front(); + gStreamsToClose.pop(); + if (streamToClose.first) { // pSoundFile + delete streamToClose.first; + } + + if (streamToClose.second) { // pBuffer + free(streamToClose.second); + } + } + + if (!gStreamsToProcess.empty()) { + stream = gStreamsToProcess.front(); + gStreamsToProcess.pop(); + } else + continue; + } + + std::unique_lock lock(stream->m_mutex); + + std::pair buffers, *lastBufAddr; + bool insertBufsAfterCheck = false; + + do { + if (!stream->IsOpened()) { + break; + } + + if (stream->m_bReset) + break; + + // We gave up this idea for now + /* + stream->m_pSoundFile->FileOpen(); + + // Deffered allocation, do it now + if (stream->m_pBuffer == nil) { + stream->m_pBuffer = malloc(stream->m_pSoundFile->GetBufferSize()); + ASSERT(stream->m_pBuffer != nil); + } + */ + + if (stream->m_bDoSeek) { + stream->m_bDoSeek = false; + int pos = stream->m_SeekPos; + lock.unlock(); + stream->m_pSoundFile->Seek(pos); + lock.lock(); + + continue; // let's do the checks again, make sure we didn't miss anything while Seeking + } + + if (insertBufsAfterCheck) { + stream->m_queueBuffers.push(buffers); + insertBufsAfterCheck = false; + } + + if (!stream->m_fillBuffers.empty()) { + lastBufAddr = &stream->m_fillBuffers.front(); + buffers = *lastBufAddr; + lock.unlock(); + + ALuint alBuffers[2] = {buffers.first, buffers.second}; // left - right + bool filled = stream->FillBuffer(alBuffers); + + lock.lock(); + + // Make sure queue isn't touched after we released mutex + if (!stream->m_fillBuffers.empty() && lastBufAddr == &stream->m_fillBuffers.front()) { + stream->m_fillBuffers.pop(); + if (filled) + insertBufsAfterCheck = true; // Also make sure stream's properties aren't changed. So make one more pass, and push it to m_queueBuffers only if it pass checks again. + } + } else + break; + + } while (true); + + } while(true); +} +#endif + +void CStream::Initialise() +{ +#ifdef AUDIO_OAL_USE_MPG123 + mpg123_init(); +#endif +#ifdef MULTITHREADED_AUDIO + gAudioThread = std::thread(audioFileOpsThread); +#endif +} + +void CStream::Terminate() +{ +#ifdef AUDIO_OAL_USE_MPG123 + mpg123_exit(); +#endif +#ifdef MULTITHREADED_AUDIO + gAudioThreadQueueMutex.lock(); + gAudioThreadTerm = true; + gAudioThreadQueueMutex.unlock(); + + gAudioThreadCv.notify_one(); + gAudioThread.join(); +#endif +} + +CStream::CStream(ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS]) : + m_pAlSources(sources), + m_alBuffers(buffers), + m_pBuffer(nil), + m_bPaused(false), + m_bActive(false), +#ifdef MULTITHREADED_AUDIO + m_bIExist(false), + m_bDoSeek(false), + m_SeekPos(0), +#endif + m_pSoundFile(nil), + m_bReset(false), + m_nVolume(0), + m_nPan(0), + m_nPosBeforeReset(0), + m_nLoopCount(1) + +{ +} + +bool CStream::Open(const char* filename, uint32 overrideSampleRate) +{ + if (IsOpened()) return false; + +#ifdef MULTITHREADED_AUDIO + std::unique_lock lock(m_mutex); + + m_bDoSeek = false; + m_SeekPos = 0; +#endif + + m_bPaused = false; + m_bActive = false; + m_bReset = false; + m_nVolume = 0; + m_nPan = 0; + m_nPosBeforeReset = 0; + m_nLoopCount = 1; + +// Be case-insensitive on linux (from https://github.com/OneSadCookie/fcaseopen/) +#if !defined(_WIN32) + char *real = casepath(filename); + if (real) { + strcpy(m_aFilename, real); + free(real); + } else { +#else + { +#endif + strcpy(m_aFilename, filename); + } + + DEV("Stream %s\n", m_aFilename); + + if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".wav")], ".wav")) +#ifdef AUDIO_OAL_USE_SNDFILE + m_pSoundFile = new CSndFile(m_aFilename); +#else + m_pSoundFile = new CWavFile(m_aFilename); +#endif +#ifdef AUDIO_OAL_USE_MPG123 + else if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".mp3")], ".mp3")) + m_pSoundFile = new CMP3File(m_aFilename); + else if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".adf")], ".adf")) + m_pSoundFile = new CADFFile(m_aFilename); +#endif + else if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".vb")], ".VB")) + m_pSoundFile = new CVbFile(m_aFilename, overrideSampleRate); +#ifdef AUDIO_OAL_USE_OPUS + else if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".opus")], ".opus")) + m_pSoundFile = new COpusFile(m_aFilename); +#endif + else + m_pSoundFile = nil; + + if ( m_pSoundFile && m_pSoundFile->IsOpened() ) + { + uint32 bufSize = m_pSoundFile->GetBufferSize(); + if(bufSize != 0) { // Otherwise it's deferred + m_pBuffer = malloc(bufSize); + ASSERT(m_pBuffer != nil); + + DEV("AvgSamplesPerSec: %d\n", m_pSoundFile->GetAvgSamplesPerSec()); + DEV("SampleCount: %d\n", m_pSoundFile->GetSampleCount()); + DEV("SampleRate: %d\n", m_pSoundFile->GetSampleRate()); + DEV("Channels: %d\n", m_pSoundFile->GetChannels()); + DEV("Buffer Samples: %d\n", m_pSoundFile->GetBufferSamples()); + DEV("Buffer sec: %f\n", (float(m_pSoundFile->GetBufferSamples()) / float(m_pSoundFile->GetChannels())/ float(m_pSoundFile->GetSampleRate()))); + DEV("Length MS: %02d:%02d\n", (m_pSoundFile->GetLength() / 1000) / 60, (m_pSoundFile->GetLength() / 1000) % 60); + } +#ifdef MULTITHREADED_AUDIO + m_bIExist = true; +#endif + return true; + } + return false; +} + +CStream::~CStream() +{ + assert(!IsOpened()); +} + +void CStream::Close() +{ + if(!IsOpened()) return; + +#ifdef MULTITHREADED_AUDIO + { + std::lock_guard lock(m_mutex); + + Stop(); + ClearBuffers(); + m_bIExist = false; + std::queue>().swap(m_fillBuffers); + tsQueue>().swapNts(m_queueBuffers); // TSness not required, mutex is acquired + } + + FlagAsToBeProcessed(true); +#else + + Stop(); + ClearBuffers(); + + if ( m_pSoundFile ) + { + delete m_pSoundFile; + m_pSoundFile = nil; + } + + if ( m_pBuffer ) + { + free(m_pBuffer); + m_pBuffer = nil; + } +#endif +} + +bool CStream::HasSource() +{ + return (m_pAlSources[0] != AL_NONE) && (m_pAlSources[1] != AL_NONE); +} + +// m_bIExist only written in main thread, thus mutex is not needed on main thread +bool CStream::IsOpened() +{ +#ifdef MULTITHREADED_AUDIO + return m_bIExist; +#else + return m_pSoundFile && m_pSoundFile->IsOpened(); +#endif +} + +bool CStream::IsPlaying() +{ + if ( !HasSource() || !IsOpened() ) return false; + + if ( !m_bPaused ) + { + ALint sourceState[2]; + alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState[0]); + alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState[1]); + if (sourceState[0] == AL_PLAYING || sourceState[1] == AL_PLAYING) + return true; + +#ifdef MULTITHREADED_AUDIO + std::lock_guard lock(m_mutex); + + // Streams are designed in such a way that m_fillBuffers and m_queueBuffers will be *always* filled if audio is playing, and mutex is acquired + if (!m_fillBuffers.empty() || !m_queueBuffers.emptyNts()) + return true; +#endif + } + + return false; +} + +void CStream::Pause() +{ + if ( !HasSource() ) return; + ALint sourceState = AL_PAUSED; + alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState); + if (sourceState != AL_PAUSED) + alSourcePause(m_pAlSources[0]); + alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState); + if (sourceState != AL_PAUSED) + alSourcePause(m_pAlSources[1]); +} + +void CStream::SetPause(bool bPause) +{ + if ( !HasSource() ) return; + if ( bPause ) + { + Pause(); + m_bPaused = true; + } + else + { + if (m_bPaused) + SetPlay(true); + m_bPaused = false; + } +} + +void CStream::SetPitch(float pitch) +{ + if ( !HasSource() ) return; + alSourcef(m_pAlSources[0], AL_PITCH, pitch); + alSourcef(m_pAlSources[1], AL_PITCH, pitch); +} + +void CStream::SetGain(float gain) +{ + if ( !HasSource() ) return; + alSourcef(m_pAlSources[0], AL_GAIN, gain); + alSourcef(m_pAlSources[1], AL_GAIN, gain); +} + +void CStream::SetPosition(int i, float x, float y, float z) +{ + if ( !HasSource() ) return; + alSource3f(m_pAlSources[i], AL_POSITION, x, y, z); +} + +void CStream::SetVolume(uint32 nVol) +{ + m_nVolume = nVol; + SetGain(ALfloat(nVol) / MAX_VOLUME); +} + +void CStream::SetPan(uint8 nPan) +{ + m_nPan = Clamp((int8)nPan - 63, 0, 63); + SetPosition(0, (m_nPan - 63) / 64.0f, 0.0f, Sqrt(1.0f - SQR((m_nPan - 63) / 64.0f))); + + m_nPan = Clamp((int8)nPan + 64, 64, 127); + SetPosition(1, (m_nPan - 63) / 64.0f, 0.0f, Sqrt(1.0f - SQR((m_nPan - 63) / 64.0f))); + + m_nPan = nPan; +} + +// Should only be called if source is stopped +void CStream::SetPosMS(uint32 nPos) +{ + if ( !IsOpened() ) return; + +#ifdef MULTITHREADED_AUDIO + std::lock_guard lock(m_mutex); + + std::queue>().swap(m_fillBuffers); + tsQueue>().swapNts(m_queueBuffers); // TSness not required, second thread always access it when stream mutex acquired + + if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE) { + m_bDoSeek = true; + m_SeekPos = nPos; + } else +#endif + { + m_pSoundFile->Seek(nPos); + } + ClearBuffers(); + + // adding to gStreamsToProcess not needed, someone always calls Start() / BuffersShouldBeFilled() after SetPosMS +} + +uint32 CStream::GetPosMS() +{ + if ( !HasSource() ) return 0; + if ( !IsOpened() ) return 0; + + // Deferred init causes division by zero + if (m_pSoundFile->GetChannels() == 0) + return 0; + + ALint offset; + //alGetSourcei(m_alSource, AL_SAMPLE_OFFSET, &offset); + alGetSourcei(m_pAlSources[0], AL_BYTE_OFFSET, &offset); + + //std::lock_guard lock(m_mutex); + + return m_pSoundFile->Tell() + - m_pSoundFile->samples2ms(m_pSoundFile->GetBufferSamples() * (NUM_STREAMBUFFERS/2-1)) / m_pSoundFile->GetChannels() + + m_pSoundFile->samples2ms(offset/m_pSoundFile->GetSampleSize()) / m_pSoundFile->GetChannels(); +} + +uint32 CStream::GetLengthMS() +{ + if ( !IsOpened() ) return 0; + return m_pSoundFile->GetLength(); +} + +bool CStream::FillBuffer(ALuint *alBuffer) +{ +#ifndef MULTITHREADED_AUDIO + if ( !HasSource() ) + return false; + if ( !IsOpened() ) + return false; + if ( !(alBuffer[0] != AL_NONE && alIsBuffer(alBuffer[0])) ) + return false; + if ( !(alBuffer[1] != AL_NONE && alIsBuffer(alBuffer[1])) ) + return false; +#endif + + uint32 size = m_pSoundFile->Decode(m_pBuffer); + if( size == 0 ) + return false; + + uint32 channelSize = size / m_pSoundFile->GetChannels(); + + alBufferData(alBuffer[0], AL_FORMAT_MONO16, m_pBuffer, channelSize, m_pSoundFile->GetSampleRate()); + // TODO: use just one buffer if we play mono + if (m_pSoundFile->GetChannels() == 1) + alBufferData(alBuffer[1], AL_FORMAT_MONO16, m_pBuffer, channelSize, m_pSoundFile->GetSampleRate()); + else + alBufferData(alBuffer[1], AL_FORMAT_MONO16, (uint8*)m_pBuffer + channelSize, channelSize, m_pSoundFile->GetSampleRate()); + return true; +} + +#ifdef MULTITHREADED_AUDIO +bool CStream::QueueBuffers() +{ + bool buffersQueued = false; + std::pair buffers; + while (m_queueBuffers.peekPop(&buffers)) // beware: m_queueBuffers is tsQueue + { + ALuint leftBuf = buffers.first; + ALuint rightBuf = buffers.second; + + alSourceQueueBuffers(m_pAlSources[0], 1, &leftBuf); + alSourceQueueBuffers(m_pAlSources[1], 1, &rightBuf); + + buffersQueued = true; + } + return buffersQueued; +} +#endif + +// Only used in single-threaded audio or cutscene audio +int32 CStream::FillBuffers() +{ + int32 i = 0; + for ( i = 0; i < NUM_STREAMBUFFERS/2; i++ ) + { + if ( !FillBuffer(&m_alBuffers[i*2]) ) + break; + alSourceQueueBuffers(m_pAlSources[0], 1, &m_alBuffers[i*2]); + alSourceQueueBuffers(m_pAlSources[1], 1, &m_alBuffers[i*2+1]); + } + + return i; +} + +void CStream::ClearBuffers() +{ + if ( !HasSource() ) return; + + ALint buffersQueued[2]; + alGetSourcei(m_pAlSources[0], AL_BUFFERS_QUEUED, &buffersQueued[0]); + alGetSourcei(m_pAlSources[1], AL_BUFFERS_QUEUED, &buffersQueued[1]); + + ALuint value; + while (buffersQueued[0]--) + alSourceUnqueueBuffers(m_pAlSources[0], 1, &value); + while (buffersQueued[1]--) + alSourceUnqueueBuffers(m_pAlSources[1], 1, &value); +} + +bool CStream::Setup(bool imSureQueueIsEmpty, bool lock) +{ + if ( IsOpened() ) + { +#ifdef MULTITHREADED_AUDIO + if (lock) + m_mutex.lock(); +#endif + + if (!imSureQueueIsEmpty) { + Stop(); + ClearBuffers(); + } +#ifdef MULTITHREADED_AUDIO + if (MusicManager.m_nMusicMode == MUSICMODE_CUTSCENE) { + m_pSoundFile->Seek(0); + } else { + m_bDoSeek = true; + m_SeekPos = 0; + } + + if (lock) + m_mutex.unlock(); +#else + m_pSoundFile->Seek(0); +#endif + + //SetPosition(0.0f, 0.0f, 0.0f); + SetPitch(1.0f); + //SetPan(m_nPan); + //SetVolume(100); + } + + return IsOpened(); +} + +void CStream::SetLoopCount(int32 count) +{ + if ( !HasSource() ) return; + + m_nLoopCount = count; +} + +void CStream::SetPlay(bool state) +{ + if ( !HasSource() ) return; + if ( state ) + { + ALint sourceState = AL_PLAYING; + alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState); + if (sourceState != AL_PLAYING ) + alSourcePlay(m_pAlSources[0]); + + sourceState = AL_PLAYING; + alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState); + if (sourceState != AL_PLAYING) + alSourcePlay(m_pAlSources[1]); + + m_bActive = true; + } + else + { + ALint sourceState = AL_STOPPED; + alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState); + if (sourceState != AL_STOPPED) + alSourceStop(m_pAlSources[0]); + + sourceState = AL_STOPPED; + alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState); + if (sourceState != AL_STOPPED) + alSourceStop(m_pAlSources[1]); + + m_bActive = false; + } +} + +void CStream::Start() +{ + if ( !HasSource() ) return; + +#ifdef MULTITHREADED_AUDIO + std::lock_guard lock(m_mutex); + tsQueue>().swapNts(m_queueBuffers); // TSness not required, second thread always access it when stream mutex acquired +#endif + BuffersShouldBeFilled(); +} + +void CStream::Stop() +{ + if ( !HasSource() ) return; + SetPlay(false); +} + +void CStream::Update() +{ + if ( !IsOpened() ) + return; + + if ( !HasSource() ) + return; + + if ( m_bReset ) + return; + + if ( !m_bPaused ) + { + + bool buffersQueuedAndStarted = false; + bool buffersQueuedButNotStarted = false; +#ifdef MULTITHREADED_AUDIO + // Put it in here because we need totalBuffers after queueing to decide when to loop audio + if (m_bActive) + { + buffersQueuedAndStarted = QueueBuffers(); + if(buffersQueuedAndStarted) { + SetPlay(true); + } + } +#endif + + ALint totalBuffers[2] = {0, 0}; + ALint buffersProcessed[2] = {0, 0}; + + // Relying a lot on left buffer states in here + + do + { + //alSourcef(m_pAlSources[0], AL_ROLLOFF_FACTOR, 0.0f); + alGetSourcei(m_pAlSources[0], AL_BUFFERS_QUEUED, &totalBuffers[0]); + alGetSourcei(m_pAlSources[0], AL_BUFFERS_PROCESSED, &buffersProcessed[0]); + //alSourcef(m_pAlSources[1], AL_ROLLOFF_FACTOR, 0.0f); + alGetSourcei(m_pAlSources[1], AL_BUFFERS_QUEUED, &totalBuffers[1]); + alGetSourcei(m_pAlSources[1], AL_BUFFERS_PROCESSED, &buffersProcessed[1]); + } while (buffersProcessed[0] != buffersProcessed[1]); + + assert(buffersProcessed[0] == buffersProcessed[1]); + + // Correcting OpenAL concepts here: + // AL_BUFFERS_QUEUED = Number of *all* buffers in queue, including processed, processing and pending + // AL_BUFFERS_PROCESSED = Index of the buffer being processing right now. Buffers coming after that(have greater index) are pending buffers. + // which means: totalBuffers[0] - buffersProcessed[0] = pending buffers + + // We should wait queue to be cleared to loop track, because position calculation relies on queue. + if (m_nLoopCount != 1 && m_bActive && totalBuffers[0] == 0) + { +#ifdef MULTITHREADED_AUDIO + std::lock_guard lock(m_mutex); + + if (m_fillBuffers.empty() && m_queueBuffers.emptyNts()) // we already acquired stream mutex, which is enough for second thread. thus Nts variant +#endif + { + Setup(true, false); + BuffersShouldBeFilled(); // will also call SetPlay(true) + if (m_nLoopCount != 0) + m_nLoopCount--; + } + } + else + { + static std::queue> tempFillBuffer; + + while ( buffersProcessed[0]-- ) + { + ALuint buffer[2]; + + alSourceUnqueueBuffers(m_pAlSources[0], 1, &buffer[0]); + alSourceUnqueueBuffers(m_pAlSources[1], 1, &buffer[1]); + + if (m_bActive) + { + tempFillBuffer.push(std::pair(buffer[0], buffer[1])); + } + } + + if (m_bActive && buffersProcessed[1]) + { +#ifdef MULTITHREADED_AUDIO + m_mutex.lock(); +#endif + while (!tempFillBuffer.empty()) { + auto elem = tempFillBuffer.front(); + tempFillBuffer.pop(); + buffersQueuedButNotStarted = BufferShouldBeFilledAndQueued(&elem); + } +#ifdef MULTITHREADED_AUDIO + m_mutex.unlock(); + FlagAsToBeProcessed(); +#endif + + } + } + + // Source may be starved to audio and stopped itself + if (m_bActive && !buffersQueuedAndStarted && (buffersQueuedButNotStarted || (totalBuffers[1] - buffersProcessed[1] != 0))) + SetPlay(true); + } +} + +void CStream::ProviderInit() +{ + if ( m_bReset ) + { + if ( Setup(true, false) ) // lock not needed, thread can't process streams with m_bReset set + { + SetPan(m_nPan); + SetVolume(m_nVolume); + SetLoopCount(m_nLoopCount); + SetPosMS(m_nPosBeforeReset); +#ifdef MULTITHREADED_AUDIO + std::unique_lock lock(m_mutex); +#endif + if(m_bActive) + BuffersShouldBeFilled(); + + if (m_bPaused) + Pause(); + + m_bReset = false; + + } else { +#ifdef MULTITHREADED_AUDIO + std::unique_lock lock(m_mutex); +#endif + m_bReset = false; + } + } +} + +void CStream::ProviderTerm() +{ +#ifdef MULTITHREADED_AUDIO + std::lock_guard lock(m_mutex); + + // unlike Close() we will reuse this stream, so clearing queues are important. + std::queue>().swap(m_fillBuffers); + tsQueue>().swapNts(m_queueBuffers); // stream mutex is already acquired, thus Nts variant +#endif + m_bReset = true; + m_nPosBeforeReset = GetPosMS(); + + Stop(); + ClearBuffers(); +} + +#endif diff --git a/src/miami/audio/oal/stream.h b/src/miami/audio/oal/stream.h new file mode 100644 index 00000000..f0456925 --- /dev/null +++ b/src/miami/audio/oal/stream.h @@ -0,0 +1,192 @@ +#pragma once + +#ifdef AUDIO_OAL +#include + +#define NUM_STREAMBUFFERS 8 + +class IDecoder +{ +public: + virtual ~IDecoder() { } + + virtual bool IsOpened() = 0; + virtual void FileOpen() = 0; + + virtual uint32 GetSampleSize() = 0; + virtual uint32 GetSampleCount() = 0; + virtual uint32 GetSampleRate() = 0; + virtual uint32 GetChannels() = 0; + + uint32 GetAvgSamplesPerSec() + { + return GetChannels() * GetSampleRate(); + } + + uint32 ms2samples(uint32 ms) + { + return float(ms) / 1000.0f * float(GetSampleRate()); + } + + uint32 samples2ms(uint32 sm) + { + return float(sm) * 1000.0f / float(GetSampleRate()); + } + + uint32 GetBufferSamples() + { + //return (GetAvgSamplesPerSec() >> 2) - (GetSampleCount() % GetChannels()); + return (GetAvgSamplesPerSec() / 4); // 250ms + } + + uint32 GetBufferSize() + { + return GetBufferSamples() * GetSampleSize(); + } + + virtual void Seek(uint32 milliseconds) = 0; + virtual uint32 Tell() = 0; + + uint32 GetLength() + { + FileOpen(); // abort deferred init, we need length now - game has to cache audio file sizes + return float(GetSampleCount()) * 1000.0f / float(GetSampleRate()); + } + + virtual uint32 Decode(void *buffer) = 0; +}; +#ifdef MULTITHREADED_AUDIO +template class tsQueue +{ +public: + tsQueue() : count(0) { } + + void push(const T &value) + { + std::lock_guard lock(m_mutex); + m_queue.push(value); + count++; + } + + bool peekPop(T *retVal) + { + std::lock_guard lock(m_mutex); + if (count == 0) + return false; + + *retVal = m_queue.front(); + m_queue.pop(); + count--; + return true; + } + + void swapNts(tsQueue &replaceWith) + { + m_queue.swap(replaceWith.m_queue); + replaceWith.count = count; + } + + /* + void swapTs(tsQueue &replaceWith) + { + std::lock_guard lock(m_mutex); + std::lock_guard lock2(replaceWith.m_mutex); + swapNts(replaceWith); + } + */ + + bool emptyNts() + { + return count == 0; + } + + /* + bool emptyTs() + { + std::lock_guard lock(m_mutex); + return emptyNts(); + } + */ + + std::queue m_queue; + int count; + mutable std::mutex m_mutex; +}; +#endif +class CStream +{ + char m_aFilename[128]; + ALuint *m_pAlSources; + ALuint (&m_alBuffers)[NUM_STREAMBUFFERS]; + + bool m_bPaused; + bool m_bActive; + +public: +#ifdef MULTITHREADED_AUDIO + std::mutex m_mutex; + std::queue> m_fillBuffers; // left and right buffer + tsQueue> m_queueBuffers; +// std::condition_variable m_closeCv; + bool m_bDoSeek; + uint32 m_SeekPos; + bool m_bIExist; +#endif + + void *m_pBuffer; + + bool m_bReset; + uint32 m_nVolume; + uint8 m_nPan; + uint32 m_nPosBeforeReset; + int32 m_nLoopCount; + + IDecoder *m_pSoundFile; + + void BuffersShouldBeFilled(); // all + bool BufferShouldBeFilledAndQueued(std::pair*); // two (left-right) +#ifdef MULTITHREADED_AUDIO + void FlagAsToBeProcessed(bool close = false); + bool QueueBuffers(); +#endif + + bool HasSource(); + void SetPosition(int i, float x, float y, float z); + void SetPitch(float pitch); + void SetGain(float gain); + void Pause(); + void SetPlay(bool state); + + bool FillBuffer(ALuint *alBuffer); + int32 FillBuffers(); + void ClearBuffers(); +//public: + static void Initialise(); + static void Terminate(); + + CStream(ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS]); + ~CStream(); + void Delete(); + bool Open(const char *filename, uint32 overrideSampleRate = 32000); + void Close(); + + bool IsOpened(); + bool IsPlaying(); + void SetPause (bool bPause); + void SetVolume(uint32 nVol); + void SetPan (uint8 nPan); + void SetPosMS (uint32 nPos); + uint32 GetPosMS(); + uint32 GetLengthMS(); + + bool Setup(bool imSureQueueIsEmpty = false, bool lock = true); + void Start(); + void Stop(); + void Update(void); + void SetLoopCount(int32); + + void ProviderInit(); + void ProviderTerm(); +}; + +#endif diff --git a/src/miami/audio/sampman.h b/src/miami/audio/sampman.h new file mode 100644 index 00000000..40cb8e0b --- /dev/null +++ b/src/miami/audio/sampman.h @@ -0,0 +1,2730 @@ +#pragma once +#include "AudioSamples.h" +#include "audio_enums.h" + +#define MAX_VOLUME 127 +#define MAX_FREQ DIGITALRATE + +struct tSample { + uint32 nFileOffset; // in bytes + uint32 nByteSize; // in bytes (*2 for adpcm samples) + uint32 nFrequency; // in hz + uint32 nLoopStartSample;// in samples, 0 if no separate loop data + uint32 nLoopFileOffset; // in bytes, 0 if none + uint32 nLoopByteSize; // in bytes, 0 if none (*2 for adpcm samples) +}; + +#ifdef GTA_PS2 +#define PS2BANK(e) e +#else +#define PS2BANK(e) e = SFX_BANK_0 +#endif // GTA_PS2 + + +enum +{ + SFX_BANK_0, +#ifdef GTA_PS2 + SFX_BANK_GENERIC_EXTRA, + SFX_BANK_PED_COMMENTS, + SFX_BANK_FRONT_END_MENU, +#else + SFX_BANK_GENERIC_EXTRA = SFX_BANK_0, + SFX_BANK_FRONT_END_MENU = SFX_BANK_0, + SFX_BANK_1, + SFX_BANK_63 = 63, + + SFX_BANK_PED_COMMENTS, + MAX_SFX_BANKS, + INVALID_SFX_BANK, +#endif + + CAR_SFX_BANKS_OFFSET, + SFX_BANK_PONTIAC = CAR_SFX_BANKS_OFFSET, + SFX_BANK_PORSCHE, + SFX_BANK_SPIDER, + SFX_BANK_MERC, + SFX_BANK_TRUCK, + SFX_BANK_HOTROD, + SFX_BANK_COBRA, + SFX_BANK_PONTIAC_SLOW, + SFX_BANK_CADILLAC, + SFX_BANK_PATHFINDER, + SFX_BANK_PACARD, + SFX_BANK_GOLF_CART, + SFX_BANK_CAR_CHAINSAW, + SFX_BANK_RC, + SFX_BANK_RC_HELI, + SFX_BANK_CAR_UNUSED_4, + + // bikes + SFX_BANK_VTWIN, + SFX_BANK_MOPED, + SFX_BANK_HONDA250, + SFX_BANK_SPORTS_BIKE, + SFX_BANK_BIKE_UNUSED_1, + SFX_BANK_BIKE_UNUSED_2, + SFX_BANK_BIKE_UNUSED_3, + SFX_BANK_BIKE_UNUSED_4, + SFX_BANK_BIKE_UNUSED_5, + SFX_BANK_BIKE_UNUSED_6, + + // heli + SFX_BANK_HELI_APACHE, + SFX_BANK_HELI_UNUSED_1, + SFX_BANK_HELI_UNUSED_2, + SFX_BANK_HELI_UNUSED_3, + SFX_BANK_HELI_UNUSED_4, + + // plane + SFX_BANK_PLANE_SEAPLANE, + SFX_BANK_PLANE_UNUSED_1, + SFX_BANK_PLANE_UNUSED_2, + SFX_BANK_PLANE_UNUSED_3, + SFX_BANK_PLANE_UNUSED_4, + PS2BANK(SFX_BANK_BUILDING_BANK_ALARM), + PS2BANK(SFX_BANK_BUILDING_SNORING), + PS2BANK(SFX_BANK_BUILDING_BAR_1), + PS2BANK(SFX_BANK_BUILDING_BAR_2), + PS2BANK(SFX_BANK_BUILDING_BAR_3), + PS2BANK(SFX_BANK_BUILDING_BAR_4), + PS2BANK(SFX_BANK_BUILDING_MALIBU_1), + PS2BANK(SFX_BANK_BUILDING_MALIBU_2), + PS2BANK(SFX_BANK_BUILDING_MALIBU_3), + PS2BANK(SFX_BANK_BUILDING_STRIP_1), + PS2BANK(SFX_BANK_BUILDING_STRIP_2), + PS2BANK(SFX_BANK_BUILDING_STRIP_3), + PS2BANK(SFX_BANK_BUILDING_CHURCH), + PS2BANK(SFX_BANK_BUILDING_FAN_1), + PS2BANK(SFX_BANK_BUILDING_FAN_2), + PS2BANK(SFX_BANK_BUILDING_INSECT_1), + PS2BANK(SFX_BANK_BUILDING_INSECT_2), + PS2BANK(SFX_BANK_BUILDING_18), + PS2BANK(SFX_BANK_BUILDING_19), + PS2BANK(SFX_BANK_BUILDING_20), + PS2BANK(SFX_BANK_BUILDING_21), + PS2BANK(SFX_BANK_FOOTSTEPS_GRASS), + PS2BANK(SFX_BANK_FOOTSTEPS_GRAVEL), + PS2BANK(SFX_BANK_FOOTSTEPS_WOOD), + PS2BANK(SFX_BANK_FOOTSTEPS_METAL), + PS2BANK(SFX_BANK_FOOTSTEPS_WATER), + PS2BANK(SFX_BANK_FOOTSTEPS_SAND), +#ifdef GTA_PS2 + MAX_SFX_BANKS, + INVALID_SFX_BANK +#endif +}; +#define MAX_PEDSFX 7 +#define PED_BLOCKSIZE 79000 +#define PED_BLOCKSIZE_ADPCM (PED_BLOCKSIZE/4) + +#define MAXPROVIDERS 64 + +#ifdef EXTERNAL_3D_SOUND +#define MAXCHANNELS (NUM_CHANNELS_GENERIC+1) +#define MAXCHANNELS_SURROUND (MAXCHANNELS-4) +#define MAX2DCHANNELS 1 +#else +#define MAXCHANNELS 0 +#define MAXCHANNELS_SURROUND 0 +#define MAX2DCHANNELS NUM_CHANNELS +#endif + +#define MAX_STREAMS 3 + +#define DIGITALRATE 32000 +#define DIGITALBITS 16 +#define DIGITALCHANNELS 2 + +#ifdef FIX_BUGS +#define MAX_DIGITAL_MIXER_CHANNELS (MAXCHANNELS+MAX_STREAMS*2+MAX2DCHANNELS) +#else +#define MAX_DIGITAL_MIXER_CHANNELS (MAXCHANNELS+MAX_STREAMS*2) +#endif + +static_assert( NUM_CHANNELS == MAXCHANNELS + MAX2DCHANNELS, "The number of channels doesn't match with an enum" ); + +class cSampleManager +{ + uint8 m_nEffectsVolume; + uint8 m_nMusicVolume; + uint8 m_nMP3BoostVolume; + uint8 m_nEffectsFadeVolume; + uint8 m_nMusicFadeVolume; + bool8 m_nMonoMode; + char m_szCDRomRootPath[80]; + bool8 m_bInitialised; + uint8 m_nNumberOfProviders; + char *m_aAudioProviders[MAXPROVIDERS]; + tSample m_aSamples[TOTAL_AUDIO_SAMPLES]; + char m_MiscomPath[260]; + char m_WavFilesPath[260]; + char m_MP3FilesPath[188]; + void *m_aChannels[18]; + +public: + + + + cSampleManager(void); + ~cSampleManager(void); + +#ifdef EXTERNAL_3D_SOUND + void SetSpeakerConfig(int32 nConfig); + uint32 GetMaximumSupportedChannels(void); + + uint32 GetNum3DProvidersAvailable(void); + void SetNum3DProvidersAvailable(uint32 num); + + char *Get3DProviderName(uint8 id); + void Set3DProviderName(uint8 id, char *name); + + int8 GetCurrent3DProviderIndex(void); + int8 SetCurrent3DProvider(uint8 which); + + int8 AutoDetect3DProviders(); +#endif + + bool8 IsMP3RadioChannelAvailable(void); + + void ReleaseDigitalHandle (void); + void ReacquireDigitalHandle(void); + + bool8 Initialise(void); + void Terminate (void); + + bool8 CheckForAnAudioFileOnCD(void); + char GetCDAudioDriveLetter (void); + + void UpdateEffectsVolume(void); + +#ifdef DC_SH4 + void UpdateChannelVolume(uint32 nChannel); +#endif + + void SetEffectsMasterVolume(uint8 nVolume); + void SetMusicMasterVolume (uint8 nVolume); + void SetMP3BoostVolume (uint8 nVolume); + void SetEffectsFadeVolume (uint8 nVolume); + void SetMusicFadeVolume (uint8 nVolume); + void SetMonoMode (bool8 nMode); + + bool8 LoadSampleBank (uint8 nBank); + void UnloadSampleBank (uint8 nBank); + void UnloadUnusedSampleBank(); + int8 IsSampleBankLoaded(uint8 nBank); + +#if defined (GTA_PS2) || defined (FIX_BUGS) + uint8 IsMissionAudioLoaded(uint8 nSlot, uint32 nSample); + bool8 LoadMissionAudio (uint8 nSlot, uint32 nSample); +#endif + + uint8 IsPedCommentLoaded(uint32 nComment); + bool8 LoadPedComment (uint32 nComment); + int32 GetBankContainingSound(uint32 offset); + + int32 _GetPedCommentSlot(uint32 nComment); + + uint32 GetSampleBaseFrequency (uint32 nSample); + uint32 GetSampleLoopStartOffset(uint32 nSample); + int32 GetSampleLoopEndOffset (uint32 nSample); + uint32 GetSampleLength (uint32 nSample); + + bool8 UpdateReverb(void); + + void SetChannelReverbFlag (uint32 nChannel, bool8 nReverbFlag); + bool8 InitialiseChannel (uint32 nChannel, uint32 nSfx, uint8 nBank); +#ifdef EXTERNAL_3D_SOUND + void SetChannelEmittingVolume(uint32 nChannel, uint32 nVolume); + void SetChannel3DPosition (uint32 nChannel, float fX, float fY, float fZ); + void SetChannel3DDistances (uint32 nChannel, float fMax, float fMin); +#endif + void SetChannelVolume (uint32 nChannel, uint32 nVolume); + void SetChannelPan (uint32 nChannel, uint32 nPan); + void SetChannelFrequency (uint32 nChannel, uint32 nFreq); + void SetChannelLoopPoints (uint32 nChannel, uint32 nLoopStart, int32 nLoopEnd); + void SetChannelLoopCount (uint32 nChannel, uint32 nLoopCount); + bool8 GetChannelUsedFlag (uint32 nChannel); + void StartChannel (uint32 nChannel); + void StopChannel (uint32 nChannel); + + void PreloadStreamedFile (uint32 nFile, uint8 nStream = 0, uint32_t seek_bytes_aligned = 0); + void PauseStream (bool8 nPauseFlag, uint8 nStream = 0); + void StartPreloadedStreamedFile (uint8 nStream = 0); + bool8 StartStreamedFile (uint32 nFile, uint32 nPos, uint8 nStream = 0); + void StopStreamedFile (uint8 nStream = 0); + int32 GetStreamedFilePosition (uint8 nStream = 0); + void SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, bool8 nEffectFlag, uint8 nStream = 0); + int32 GetStreamedFileLength (uint8 nStream = 0); + bool8 IsStreamPlaying (uint8 nStream = 0); + void SetStreamedFileLoopFlag (bool8 nLoopFlag, uint8 nStream = 0); +#ifdef AUDIO_OAL + void Service(void); +#endif + bool8 InitialiseSampleBanks(void); + + uint8 GetMusicVolume() const { return m_nMusicVolume; } +}; + +extern cSampleManager SampleManager; +extern uint32 BankStartOffset[MAX_SFX_BANKS]; + +#ifdef PS2_AUDIO_PATHS +static char PS2StreamedNameTable[][40] = +{ + "AUDIO\\MUSIC\\WILD.VB", + "AUDIO\\MUSIC\\FLASH.VB", + "AUDIO\\MUSIC\\KCHAT.VB", // 16 khz + "AUDIO\\MUSIC\\FEVER.VB", + "AUDIO\\MUSIC\\VROCK.VB", + "AUDIO\\MUSIC\\VCPR.VB", // 16 khz + "AUDIO\\MUSIC\\ESPANT.VB", + "AUDIO\\MUSIC\\EMOTION.VB", + "AUDIO\\MUSIC\\WAVE.VB", + "AUDIO\\MUSIC\\MISCOM.VB", + "AUDIO\\MUSIC\\CITY.VB", + "AUDIO\\MUSIC\\WATER.VB", + "AUDIO\\MUSIC\\BEACHAMB.VB", + "AUDIO\\MUSIC\\HCITY.VB", + "AUDIO\\MUSIC\\HWATER.VB", + "AUDIO\\MUSIC\\HBEACH.VB", + "AUDIO\\MUSIC\\MALLAMB.VB", + "AUDIO\\MUSIC\\STRIP.VB", + "AUDIO\\MUSIC\\MALIBU.VB", + "AUDIO\\MUSIC\\HOTEL.VB", + "AUDIO\\MUSIC\\DIRTRING.VB", + "AUDIO\\MUSIC\\LAW4RIOT.VB", + "AUDIO\\MUSIC\\AMBSIL.VB", + "AUDIO\\MUSIC\\POLICE.VB", // 16 khz + "AUDIO\\MUSIC\\TAXI.VB", + "AUDIO\\MUSIC\\BCLOSED.VB", + "AUDIO\\MUSIC\\BOPEN.VB", + "AUDIO\\CUTSCENE\\ASS\\ASS_1.VB", + "AUDIO\\CUTSCENE\\ASS\\ASS_2.VB", + "AUDIO\\CUTSCENE\\BANK\\BANK_1.VB", + "AUDIO\\CUTSCENE\\BANK\\BANK_2A.VB", + "AUDIO\\CUTSCENE\\BANK\\BANK_2B.VB", + "AUDIO\\CUTSCENE\\BANK\\BANK_3A.VB", + "AUDIO\\CUTSCENE\\BANK\\BANK_3B.VB", + "AUDIO\\CUTSCENE\\BANK\\BANK_4.VB", + "AUDIO\\CUTSCENE\\BIKE\\BIKE_1.VB", + "AUDIO\\CUTSCENE\\BIKE\\BIKE_2.VB", + "AUDIO\\CUTSCENE\\BIKE\\BIKE_3.VB", + "AUDIO\\CUTSCENE\\BUD\\BUD_1.VB", + "AUDIO\\CUTSCENE\\BUD\\BUD_2.VB", + "AUDIO\\CUTSCENE\\BUD\\BUD_3.VB", + "AUDIO\\CUTSCENE\\CAP\\CAP_1.VB", + "AUDIO\\CUTSCENE\\CAR\\CAR_1.VB", + "AUDIO\\CUTSCENE\\CNT\\CNT_1A.VB", + "AUDIO\\CUTSCENE\\CNT\\CNT_1B.VB", + "AUDIO\\CUTSCENE\\CNT\\CNT_2.VB", + "AUDIO\\CUTSCENE\\COK\\COK_1.VB", + "AUDIO\\CUTSCENE\\COK\\COK_2A.VB", + "AUDIO\\CUTSCENE\\COK\\COK_2B.VB", + "AUDIO\\CUTSCENE\\COK\\COK_3.VB", + "AUDIO\\CUTSCENE\\COK\\COK_4A.VB", + "AUDIO\\CUTSCENE\\COK\\COK_4A2.VB", + "AUDIO\\CUTSCENE\\COK\\COK_4B.VB", + "AUDIO\\CUTSCENE\\COL\\COL_1.VB", + "AUDIO\\CUTSCENE\\COL\\COL_2.VB", + "AUDIO\\CUTSCENE\\COL\\COL_3A.VB", + "AUDIO\\CUTSCENE\\COL\\COL_4A.VB", + "AUDIO\\CUTSCENE\\COL\\COL_5A.VB", + "AUDIO\\CUTSCENE\\COL\\COL_5B.VB", + "AUDIO\\CUTSCENE\\CUB\\CUB_1.VB", + "AUDIO\\CUTSCENE\\CUB\\CUB_2.VB", + "AUDIO\\CUTSCENE\\CUB\\CUB_3.VB", + "AUDIO\\CUTSCENE\\CUB\\CUB_4.VB", + "AUDIO\\CUTSCENE\\DRUG\\DRUG_1.VB", + "AUDIO\\CUTSCENE\\FIN\\FIN.VB", + "AUDIO\\CUTSCENE\\FIN\\FIN2.VB", + "AUDIO\\CUTSCENE\\FINALE\\FINALE.VB", + "AUDIO\\CUTSCENE\\HAT\\HAT_1.VB", + "AUDIO\\CUTSCENE\\HAT\\HAT_2.VB", + "AUDIO\\CUTSCENE\\HAT\\HAT_3.VB", + "AUDIO\\CUTSCENE\\ICE\\ICE_1.VB", + "AUDIO\\CUTSCENE\\INT\\INT_A.VB", + "AUDIO\\CUTSCENE\\INT\\INT_B.VB", + "AUDIO\\CUTSCENE\\INT\\INT_D.VB", + "AUDIO\\CUTSCENE\\INT\\INT_M.VB", + "AUDIO\\CUTSCENE\\LAW\\LAW_1A.VB", + "AUDIO\\CUTSCENE\\LAW\\LAW_1B.VB", + "AUDIO\\CUTSCENE\\LAW\\LAW_2A.VB", + "AUDIO\\CUTSCENE\\LAW\\LAW_2B.VB", + "AUDIO\\CUTSCENE\\LAW\\LAW_2C.VB", + "AUDIO\\CUTSCENE\\LAW\\LAW_3.VB", + "AUDIO\\CUTSCENE\\LAW\\LAW_4.VB", + "AUDIO\\CUTSCENE\\PHIL\\PHIL_1.VB", + "AUDIO\\CUTSCENE\\PHIL\\PHIL_2.VB", + "AUDIO\\CUTSCENE\\PORN\\PORN_1.VB", + "AUDIO\\CUTSCENE\\PORN\\PORN_2.VB", + "AUDIO\\CUTSCENE\\PORN\\PORN_3.VB", + "AUDIO\\CUTSCENE\\PORN\\PORN_4.VB", + "AUDIO\\CUTSCENE\\RESC\\RESC_1A.VB", + "AUDIO\\CUTSCENE\\ROK\\ROK_1.VB", + "AUDIO\\CUTSCENE\\ROK\\ROK_2.VB", + "AUDIO\\CUTSCENE\\ROK\\ROK_3A.VB", + "AUDIO\\CUTSCENE\\STRIPA\\STRIPA.VB", + "AUDIO\\CUTSCENE\\TAX\\TAX_1.VB", + "AUDIO\\CUTSCENE\\TEX\\TEX_1.VB", + "AUDIO\\CUTSCENE\\TEX\\TEX_2.VB", + "AUDIO\\CUTSCENE\\TEX\\TEX_3.VB", + "AUDIO\\MUSIC\\GLIGHT.VB", + "AUDIO\\MUSIC\\FIST.VB", + "AUDIO\\MUSIC\\MISCOM.VB", + "AUDIO\\MUSIC\\MISCOM.VB", + "AUDIO\\MUSIC\\MISCOM.VB", + "AUDIO\\MUSIC\\MISCOM.VB", + "AUDIO\\MOBR1.WAV", + "AUDIO\\PAGER.WAV", + "AUDIO\\CARREV.WAV", + "AUDIO\\BIKEREV.WAV", + "AUDIO\\LIFTOP.WAV", + "AUDIO\\LIFTCL.WAV", + "AUDIO\\LIFTRUN.WAV", + "AUDIO\\LIFTBEL.WAV", + "AUDIO\\INLIFT.WAV", + "AUDIO\\SFX_01.WAV", + "AUDIO\\SFX_02.WAV", + "AUDIO\\CAMERAL.WAV", + "AUDIO\\CAMERAR.WAV", + "AUDIO\\CHEER1.WAV", + "AUDIO\\CHEER2.WAV", + "AUDIO\\CHEER3.WAV", + "AUDIO\\CHEER4.WAV", + "AUDIO\\OOH1.WAV", + "AUDIO\\OOH2.WAV", + "AUDIO\\RACE1.WAV", + "AUDIO\\RACE2.WAV", + "AUDIO\\RACE3.WAV", + "AUDIO\\RACE4.WAV", + "AUDIO\\RACE5.WAV", + "AUDIO\\RACE6.WAV", + "AUDIO\\RACE7.WAV", + "AUDIO\\RACE8.WAV", + "AUDIO\\RACE9.WAV", + "AUDIO\\RACE10.WAV", + "AUDIO\\RACE11.WAV", + "AUDIO\\RACE12.WAV", + "AUDIO\\RACE13.WAV", + "AUDIO\\RACE14.WAV", + "AUDIO\\RACE15.WAV", + "AUDIO\\HOT1.WAV", + "AUDIO\\HOT2.WAV", + "AUDIO\\HOT3.WAV", + "AUDIO\\HOT4.WAV", + "AUDIO\\HOT5.WAV", + "AUDIO\\HOT6.WAV", + "AUDIO\\HOT7.WAV", + "AUDIO\\HOT8.WAV", + "AUDIO\\HOT9.WAV", + "AUDIO\\HOT10.WAV", + "AUDIO\\HOT11.WAV", + "AUDIO\\HOT12.WAV", + "AUDIO\\HOT13.WAV", + "AUDIO\\HOT14.WAV", + "AUDIO\\HOT15.WAV", + "AUDIO\\LANSTP1.WAV", + "AUDIO\\LANSTP2.WAV", + "AUDIO\\LANAMU1.WAV", + "AUDIO\\LANAMU2.WAV", + "AUDIO\\AIRHORNL.WAV", + "AUDIO\\AIRHORNR.WAV", + "AUDIO\\SNIPSCRL.WAV", + "AUDIO\\SNIPSHORT.WAV", + "AUDIO\\BLOWROOF.WAV", + "AUDIO\\ASS_1.WAV", + "AUDIO\\ASS_2.WAV", + "AUDIO\\ASS_3.WAV", + "AUDIO\\ASS_4.WAV", + "AUDIO\\ASS_5.WAV", + "AUDIO\\ASS_6.WAV", + "AUDIO\\ASS_7.WAV", + "AUDIO\\ASS_8.WAV", + "AUDIO\\ASS_9.WAV", + "AUDIO\\ASS_10.WAV", + "AUDIO\\ASS_11.WAV", + "AUDIO\\ASS_12.WAV", + "AUDIO\\ASS_13.WAV", + "AUDIO\\ASS_14.WAV", + "AUDIO\\BIKE1_1.WAV", + "AUDIO\\BIKE1_2.WAV", + "AUDIO\\BIKE1_3.WAV", + "AUDIO\\BNK1_1.WAV", + "AUDIO\\BNK1_2.WAV", + "AUDIO\\BNK1_3.WAV", + "AUDIO\\BNK1_4.WAV", + "AUDIO\\BNK1_5.WAV", + "AUDIO\\BNK1_6.WAV", + "AUDIO\\BNK1_7.WAV", + "AUDIO\\BNK1_8.WAV", + "AUDIO\\BNK1_10.WAV", + "AUDIO\\BNK1_11.WAV", + "AUDIO\\BNK1_12.WAV", + "AUDIO\\BNK1_13.WAV", + "AUDIO\\BNK1_14.WAV", + "AUDIO\\BNK2_1.WAV", + "AUDIO\\BNK2_2.WAV", + "AUDIO\\BNK2_3.WAV", + "AUDIO\\BNK2_4.WAV", + "AUDIO\\BNK2_5.WAV", + "AUDIO\\BNK2_6.WAV", + "AUDIO\\BNK2_7.WAV", + "AUDIO\\BNK2_8.WAV", + "AUDIO\\BNK2_9.WAV", + "AUDIO\\BNK3_1.WAV", + "AUDIO\\BNK3_2.WAV", + "AUDIO\\BNK3_3A.WAV", + "AUDIO\\BNK3_3B.WAV", + "AUDIO\\BNK3_3C.WAV", + "AUDIO\\BNK3_4A.WAV", + "AUDIO\\BNK3_4B.WAV", + "AUDIO\\BNK3_4C.WAV", + "AUDIO\\BNK4_1.WAV", + "AUDIO\\BNK4_2.WAV", + "AUDIO\\BNK4_3A.WAV", + "AUDIO\\BNK4_3B.WAV", + "AUDIO\\BNK4_3C.WAV", + "AUDIO\\BNK4_3D.WAV", + "AUDIO\\BNK4_3E.WAV", + "AUDIO\\BNK4_3F.WAV", + "AUDIO\\BNK4_3G.WAV", + "AUDIO\\BNK4_3H.WAV", + "AUDIO\\BNK4_3I.WAV", + "AUDIO\\BNK4_3J.WAV", + "AUDIO\\BNK4_3K.WAV", + "AUDIO\\BNK4_3M.WAV", + "AUDIO\\BNK4_3O.WAV", + "AUDIO\\BNK4_3P.WAV", + "AUDIO\\BNK4_3Q.WAV", + "AUDIO\\BNK4_3R.WAV", + "AUDIO\\BNK4_3S.WAV", + "AUDIO\\BNK4_3T.WAV", + "AUDIO\\BNK4_3U.WAV", + "AUDIO\\BNK4_3V.WAV", + "AUDIO\\BNK4_4A.WAV", + "AUDIO\\BNK4_4B.WAV", + "AUDIO\\BNK4_5.WAV", + "AUDIO\\BNK4_6.WAV", + "AUDIO\\BNK4_7.WAV", + "AUDIO\\BNK4_8.WAV", + "AUDIO\\BNK4_9.WAV", + "AUDIO\\BNK4_10.WAV", + "AUDIO\\BNK4_11.WAV", + "AUDIO\\BK4_12A.WAV", + "AUDIO\\BK4_12B.WAV", + "AUDIO\\BK4_12C.WAV", + "AUDIO\\BNK4_13.WAV", + "AUDIO\\BK4_14A.WAV", + "AUDIO\\BK4_14B.WAV", + "AUDIO\\BNK4_15.WAV", + "AUDIO\\BNK4_16.WAV", + "AUDIO\\BNK4_17.WAV", + "AUDIO\\BNK4_18.WAV", + "AUDIO\\BK4_19A.WAV", + "AUDIO\\BK4_19B.WAV", + "AUDIO\\BK4_20A.WAV", + "AUDIO\\BK4_20B.WAV", + "AUDIO\\BNK4_21.WAV", + "AUDIO\\BNK422A.WAV", + "AUDIO\\BNK422B.WAV", + "AUDIO\\BK4_23A.WAV", + "AUDIO\\BK4_23B.WAV", + "AUDIO\\BK4_23C.WAV", + "AUDIO\\BK4_23D.WAV", + "AUDIO\\BK4_24A.WAV", + "AUDIO\\BK4_24B.WAV", + "AUDIO\\BNK4_25.WAV", + "AUDIO\\BNK4_26.WAV", + "AUDIO\\BNK4_27.WAV", + "AUDIO\\BNK4_28.WAV", + "AUDIO\\BNK4_29.WAV", + "AUDIO\\BNK4_30.WAV", + "AUDIO\\BK4_31A.WAV", + "AUDIO\\BK4_31B.WAV", + "AUDIO\\BNK4_32.WAV", + "AUDIO\\BK4_34A.WAV", + "AUDIO\\BK4_34B.WAV", + "AUDIO\\BK4_35A.WAV", + "AUDIO\\BK4_35B.WAV", + "AUDIO\\BNK4_36.WAV", + "AUDIO\\BNK4_37.WAV", + "AUDIO\\BNK4_38.WAV", + "AUDIO\\BNK4_39.WAV", + "AUDIO\\BK4_40A.WAV", + "AUDIO\\BK4_40B.WAV", + "AUDIO\\BNK4_41.WAV", + "AUDIO\\BNK4_42.WAV", + "AUDIO\\BNK4_43.WAV", + "AUDIO\\BNK4_44.WAV", + "AUDIO\\BNK4_45.WAV", + "AUDIO\\BNK4_46.WAV", + "AUDIO\\BNK4_47.WAV", + "AUDIO\\BNK4_48.WAV", + "AUDIO\\BNK4_49.WAV", + "AUDIO\\BNK450A.WAV", + "AUDIO\\BNK450B.WAV", + "AUDIO\\BNK4_51.WAV", + "AUDIO\\BNK4_94.WAV", + "AUDIO\\BNK4_95.WAV", + "AUDIO\\BNK4_96.WAV", + "AUDIO\\BNK4_97.WAV", + "AUDIO\\BNK4_98.WAV", + "AUDIO\\BNK4_99.WAV", + "AUDIO\\BUD1_1.WAV", + "AUDIO\\BUD1_2.WAV", + "AUDIO\\BUD1_3.WAV", + "AUDIO\\BUD1_4.WAV", + "AUDIO\\BUD1_5.WAV", + "AUDIO\\BUD1_9.WAV", + "AUDIO\\BUD1_10.WAV", + "AUDIO\\BUD2_1.WAV", + "AUDIO\\BUD2_2.WAV", + "AUDIO\\BUD2_3.WAV", + "AUDIO\\BUD2_4.WAV", + "AUDIO\\BUD2_5.WAV", + "AUDIO\\BUD2_6.WAV", + "AUDIO\\BUD2_7.WAV", + "AUDIO\\BUD3_1.WAV", + "AUDIO\\BUD3_1A.WAV", + "AUDIO\\BUD3_1B.WAV", + "AUDIO\\BUD3_1C.WAV", + "AUDIO\\BUD3_2.WAV", + "AUDIO\\BUD3_3.WAV", + "AUDIO\\BUD3_4.WAV", + "AUDIO\\BUD3_5.WAV", + "AUDIO\\BUD3_6.WAV", + "AUDIO\\BUD3_7.WAV", + "AUDIO\\BUD3_8A.WAV", + "AUDIO\\BUD3_8B.WAV", + "AUDIO\\BUD3_8C.WAV", + "AUDIO\\BUD3_9A.WAV", + "AUDIO\\BUD3_9B.WAV", + "AUDIO\\BUD3_9C.WAV", + "AUDIO\\CAP1_2.WAV", + "AUDIO\\CAP1_3.WAV", + "AUDIO\\CAP1_4.WAV", + "AUDIO\\CAP1_5.WAV", + "AUDIO\\CAP1_6.WAV", + "AUDIO\\CAP1_7.WAV", + "AUDIO\\CAP1_8.WAV", + "AUDIO\\CAP1_9.WAV", + "AUDIO\\CAP1_10.WAV", + "AUDIO\\CAP1_11.WAV", + "AUDIO\\CAP1_12.WAV", + "AUDIO\\CNT1_1.WAV", + "AUDIO\\CNT1_2.WAV", + "AUDIO\\CNT1_3.WAV", + "AUDIO\\CNT1_4.WAV", + "AUDIO\\CNT1_5.WAV", + "AUDIO\\CNT2_1.WAV", + "AUDIO\\CNT2_2.WAV", + "AUDIO\\CNT2_3.WAV", + "AUDIO\\CNT2_4.WAV", + "AUDIO\\COK1_1.WAV", + "AUDIO\\COK1_2.WAV", + "AUDIO\\COK1_3.WAV", + "AUDIO\\COK1_4.WAV", + "AUDIO\\COK1_5.WAV", + "AUDIO\\COK1_6.WAV", + "AUDIO\\COK2_1.WAV", + "AUDIO\\COK2_2.WAV", + "AUDIO\\COK2_3.WAV", + "AUDIO\\COK2_4.WAV", + "AUDIO\\COK2_5.WAV", + "AUDIO\\COK2_6.WAV", + "AUDIO\\COK2_7A.WAV", + "AUDIO\\COK2_7B.WAV", + "AUDIO\\COK2_7C.WAV", + "AUDIO\\COK2_8A.WAV", + "AUDIO\\COK2_8B.WAV", + "AUDIO\\COK2_8C.WAV", + "AUDIO\\COK2_8D.WAV", + "AUDIO\\COK2_9.WAV", + "AUDIO\\COK210A.WAV", + "AUDIO\\COK210B.WAV", + "AUDIO\\COK210C.WAV", + "AUDIO\\COK212A.WAV", + "AUDIO\\COK212B.WAV", + "AUDIO\\COK2_13.WAV", + "AUDIO\\COK2_14.WAV", + "AUDIO\\COK2_15.WAV", + "AUDIO\\COK2_16.WAV", + "AUDIO\\COK2_20.WAV", + "AUDIO\\COK2_21.WAV", + "AUDIO\\COK2_2.WAV", // this is probably a typo of COK2_22 + "AUDIO\\COK3_1.WAV", + "AUDIO\\COK3_2.WAV", + "AUDIO\\COK3_3.WAV", + "AUDIO\\COK3_4.WAV", + "AUDIO\\COK4_1.WAV", + "AUDIO\\COK4_2.WAV", + "AUDIO\\COK4_3.WAV", + "AUDIO\\COK4_4.WAV", + "AUDIO\\COK4_5.WAV", + "AUDIO\\COK4_6.WAV", + "AUDIO\\COK4_7.WAV", + "AUDIO\\COK4_8.WAV", + "AUDIO\\COK4_9.WAV", + "AUDIO\\COK4_9A.WAV", + "AUDIO\\COK4_10.WAV", + "AUDIO\\COK4_11.WAV", + "AUDIO\\COK4_12.WAV", + "AUDIO\\COK4_13.WAV", + "AUDIO\\COK4_14.WAV", + "AUDIO\\COK4_15.WAV", + "AUDIO\\COK4_16.WAV", + "AUDIO\\COK4_17.WAV", + "AUDIO\\COK4_18.WAV", + "AUDIO\\COK4_19.WAV", + "AUDIO\\COK4_20.WAV", + "AUDIO\\COK4_21.WAV", + "AUDIO\\COK4_22.WAV", + "AUDIO\\COK4_23.WAV", + "AUDIO\\COK4_24.WAV", + "AUDIO\\COK4_25.WAV", + "AUDIO\\COK4_26.WAV", + "AUDIO\\COK4_27.WAV", + "AUDIO\\COL1_1.WAV", + "AUDIO\\COL1_2.WAV", + "AUDIO\\COL1_3.WAV", + "AUDIO\\COL1_4.WAV", + "AUDIO\\COL1_5.WAV", + "AUDIO\\COL1_6.WAV", + "AUDIO\\COL1_7.WAV", + "AUDIO\\COL1_8.WAV", + "AUDIO\\COL2_1.WAV", + "AUDIO\\COL2_2.WAV", + "AUDIO\\COL2_3.WAV", + "AUDIO\\COL2_4.WAV", + "AUDIO\\COL2_5.WAV", + "AUDIO\\COL2_6A.WAV", + "AUDIO\\COL2_7.WAV", + "AUDIO\\COL2_8.WAV", + "AUDIO\\COL2_9.WAV", + "AUDIO\\COL2_10.WAV", + "AUDIO\\COL2_11.WAV", + "AUDIO\\COL2_12.WAV", + "AUDIO\\COL2_13.WAV", + "AUDIO\\COL2_14.WAV", + "AUDIO\\COL2_15.WAV", + "AUDIO\\COL2_16.WAV", + "AUDIO\\COL3_1.WAV", + "AUDIO\\COL3_2.WAV", + "AUDIO\\COL3_2A.WAV", + "AUDIO\\COL3_2B.WAV", + "AUDIO\\COL3_3.WAV", + "AUDIO\\COL3_4.WAV", + "AUDIO\\COL3_5.WAV", + "AUDIO\\COL3_6.WAV", + "AUDIO\\COL3_7.WAV", + "AUDIO\\COL3_8.WAV", + "AUDIO\\COL3_9.WAV", + "AUDIO\\COL3_10.WAV", + "AUDIO\\COL3_11.WAV", + "AUDIO\\COL3_12.WAV", + "AUDIO\\COL3_13.WAV", + "AUDIO\\COL3_14.WAV", + "AUDIO\\COL3_15.WAV", + "AUDIO\\COL3_16.WAV", + "AUDIO\\COL3_17.WAV", + "AUDIO\\COL3_18.WAV", + "AUDIO\\COL3_19.WAV", + "AUDIO\\COL3_20.WAV", + "AUDIO\\COL3_21.WAV", + "AUDIO\\COL3_23.WAV", + "AUDIO\\COL3_24.WAV", + "AUDIO\\COL3_25.WAV", + "AUDIO\\COL4_1.WAV", + "AUDIO\\COL4_2.WAV", + "AUDIO\\COL4_3.WAV", + "AUDIO\\COL4_4.WAV", + "AUDIO\\COL4_5.WAV", + "AUDIO\\COL4_6.WAV", + "AUDIO\\COL4_7.WAV", + "AUDIO\\COL4_8.WAV", + "AUDIO\\COL4_9.WAV", + "AUDIO\\COL4_10.WAV", + "AUDIO\\COL4_11.WAV", + "AUDIO\\COL4_12.WAV", + "AUDIO\\COL4_13.WAV", + "AUDIO\\COL4_14.WAV", + "AUDIO\\COL4_15.WAV", + "AUDIO\\COL4_16.WAV", + "AUDIO\\COL4_17.WAV", + "AUDIO\\COL4_18.WAV", + "AUDIO\\COL4_19.WAV", + "AUDIO\\COL4_20.WAV", + "AUDIO\\COL4_21.WAV", + "AUDIO\\COL4_22.WAV", + "AUDIO\\COL4_23.WAV", + "AUDIO\\COL4_24.WAV", + "AUDIO\\COL4_25.WAV", + "AUDIO\\COL4_26.WAV", + "AUDIO\\COL5_1.WAV", + "AUDIO\\COL5_2.WAV", + "AUDIO\\COL5_3.WAV", + "AUDIO\\COL5_4.WAV", + "AUDIO\\COL5_5.WAV", + "AUDIO\\COL5_6.WAV", + "AUDIO\\COL5_7.WAV", + "AUDIO\\COL5_8.WAV", + "AUDIO\\COL5_9.WAV", + "AUDIO\\COL5_10.WAV", + "AUDIO\\COL5_11.WAV", + "AUDIO\\COL5_12.WAV", + "AUDIO\\COL5_13.WAV", + "AUDIO\\COL5_14.WAV", + "AUDIO\\COL5_15.WAV", + "AUDIO\\COL5_16.WAV", + "AUDIO\\COL5_17.WAV", + "AUDIO\\COL5_18.WAV", + "AUDIO\\COL5_19.WAV", + "AUDIO\\COL5_20.WAV", + "AUDIO\\COL5_21.WAV", + "AUDIO\\COL5_22.WAV", + "AUDIO\\CUB1_1.WAV", + "AUDIO\\CUB1_2.WAV", + "AUDIO\\CUB1_3.WAV", + "AUDIO\\CUB1_4.WAV", + "AUDIO\\CUB1_5.WAV", + "AUDIO\\CUB1_6.WAV", + "AUDIO\\CUB1_7.WAV", + "AUDIO\\CUB1_8.WAV", + "AUDIO\\CUB1_9.WAV", + "AUDIO\\CUB1_10.WAV", + "AUDIO\\CUB2_1.WAV", + "AUDIO\\CUB2_2.WAV", + "AUDIO\\CUB2_3A.WAV", + "AUDIO\\CUB2_3B.WAV", + "AUDIO\\CUB2_3C.WAV", + "AUDIO\\CUB2_4A.WAV", + "AUDIO\\CUB2_5.WAV", + "AUDIO\\CUB2_6.WAV", + "AUDIO\\CUB2_7.WAV", + "AUDIO\\CUB2_8.WAV", + "AUDIO\\CUB2_9.WAV", + "AUDIO\\CUB2_10.WAV", + "AUDIO\\CUB2_11.WAV", + "AUDIO\\CUB3_1.WAV", + "AUDIO\\CUB3_2.WAV", + "AUDIO\\CUB3_3.WAV", + "AUDIO\\CUB3_4.WAV", + "AUDIO\\CUB4_1.WAV", + "AUDIO\\CUB4_2.WAV", + "AUDIO\\CUB4_3.WAV", + "AUDIO\\CUB4_4.WAV", + "AUDIO\\CUB4_5.WAV", + "AUDIO\\CUB4_5A.WAV", + "AUDIO\\CUB4_6.WAV", + "AUDIO\\CUB4_7.WAV", + "AUDIO\\CUB4_8.WAV", + "AUDIO\\CUB4_9.WAV", + "AUDIO\\CUB4_10.WAV", + "AUDIO\\CUB4_11.WAV", + "AUDIO\\CUB4_12.WAV", + "AUDIO\\CUB4_13.WAV", + "AUDIO\\CUB4_14.WAV", + "AUDIO\\CUB4_15.WAV", + "AUDIO\\CUB4_16.WAV", + "AUDIO\\GOLF_1.WAV", + "AUDIO\\GOLF_2.WAV", + "AUDIO\\GOLF_3.WAV", + "AUDIO\\BAR_1.WAV", + "AUDIO\\BAR_2.WAV", + "AUDIO\\BAR_3.WAV", + "AUDIO\\BAR_4.WAV", + "AUDIO\\BAR_5.WAV", + "AUDIO\\BAR_6.WAV", + "AUDIO\\BAR_7.WAV", + "AUDIO\\BAR_8.WAV", + "AUDIO\\STRIP_1.WAV", + "AUDIO\\STRIP_2.WAV", + "AUDIO\\STRIP_3.WAV", + "AUDIO\\STRIP_4.WAV", + "AUDIO\\STRIP_5.WAV", + "AUDIO\\STRIP_6.WAV", + "AUDIO\\STRIP_7.WAV", + "AUDIO\\STRIP_8.WAV", + "AUDIO\\STRIP_9.WAV", + "AUDIO\\STAR_1.WAV", + "AUDIO\\STAR_2.WAV", + "AUDIO\\STAR_3.WAV", + "AUDIO\\STAR_4.WAV", + "AUDIO\\FIN_1A.WAV", + "AUDIO\\FIN_1B.WAV", + "AUDIO\\FIN_1C.WAV", + "AUDIO\\FIN_2B.WAV", + "AUDIO\\FIN_2C.WAV", + "AUDIO\\FIN_3.WAV", + "AUDIO\\FIN_4.WAV", + "AUDIO\\FIN_5.WAV", + "AUDIO\\FIN_6.WAV", + "AUDIO\\FIN_10.WAV", + "AUDIO\\FIN_11A.WAV", + "AUDIO\\FIN_11B.WAV", + "AUDIO\\FIN_12A.WAV", + "AUDIO\\FIN_12B.WAV", + "AUDIO\\FIN_12C.WAV", + "AUDIO\\FIN_13.WAV", + "AUDIO\\FINKILL.WAV", + "AUDIO\\LAW1_1.WAV", + "AUDIO\\LAW1_2.WAV", + "AUDIO\\LAW1_3.WAV", + "AUDIO\\LAW1_4.WAV", + "AUDIO\\LAW1_5.WAV", + "AUDIO\\LAW1_6.WAV", + "AUDIO\\LAW1_7.WAV", + "AUDIO\\LAW1_8.WAV", + "AUDIO\\LAW1_9.WAV", + "AUDIO\\LAW1_10.WAV", + "AUDIO\\LAW2_1.WAV", + "AUDIO\\LAW2_2.WAV", + "AUDIO\\LAW2_3.WAV", + "AUDIO\\LAW2_4.WAV", + "AUDIO\\LAW2_5.WAV", + "AUDIO\\LAW2_6.WAV", + "AUDIO\\LAW2_7.WAV", + "AUDIO\\LAW2_8.WAV", + "AUDIO\\LAW2_9.WAV", + "AUDIO\\LAW2_10.WAV", + "AUDIO\\LAW3_1.WAV", + "AUDIO\\LAW3_2.WAV", + "AUDIO\\LAW3_3.WAV", + "AUDIO\\LAW3_4.WAV", + "AUDIO\\LAW3_5.WAV", + "AUDIO\\LAW3_6.WAV", + "AUDIO\\LAW3_10.WAV", + "AUDIO\\LAW3_11.WAV", + "AUDIO\\LAW3_12.WAV", + "AUDIO\\LAW3_13.WAV", + "AUDIO\\LAW3_14.WAV", + "AUDIO\\LAW3_16.WAV", + "AUDIO\\LAW3_17.WAV", + "AUDIO\\LAW3_18.WAV", + "AUDIO\\LAW3_19.WAV", + "AUDIO\\LAW3_20.WAV", + "AUDIO\\LAW3_21.WAV", + "AUDIO\\LAW3_22.WAV", + "AUDIO\\LAW3_23.WAV", + "AUDIO\\LAW3_24.WAV", + "AUDIO\\LAW3_25.WAV", + "AUDIO\\LAW4_1A.WAV", + "AUDIO\\LAW4_1B.WAV", + "AUDIO\\LAW4_1C.WAV", + "AUDIO\\LAW4_1D.WAV", + "AUDIO\\LAW4_10.WAV", + "AUDIO\\LAW4_3.WAV", + "AUDIO\\LAW4_4.WAV", + "AUDIO\\LAW4_5.WAV", + "AUDIO\\LAW4_6.WAV", + "AUDIO\\LAW4_7.WAV", + "AUDIO\\LAW4_8.WAV", + "AUDIO\\LAW4_9.WAV", + "AUDIO\\PHIL1_2.WAV", + "AUDIO\\PHIL1_3.WAV", + "AUDIO\\PHIL2_1.WAV", + "AUDIO\\PHIL2_2.WAV", + "AUDIO\\PHIL2_3.WAV", + "AUDIO\\PHIL2_4.WAV", + "AUDIO\\PHIL2_5.WAV", + "AUDIO\\PHIL2_6.WAV", + "AUDIO\\PHIL2_7.WAV", + "AUDIO\\PHIL2_8.WAV", + "AUDIO\\PHIL2_9.WAV", + "AUDIO\\PHIL210.WAV", + "AUDIO\\PHIL211.WAV", + "AUDIO\\PORN1_1.WAV", + "AUDIO\\PORN1_2.WAV", + "AUDIO\\PORN1_3.WAV", + "AUDIO\\PRN1_3A.WAV", + "AUDIO\\PORN1_4.WAV", + "AUDIO\\PORN1_5.WAV", + "AUDIO\\PORN1_6.WAV", + "AUDIO\\PORN1_7.WAV", + "AUDIO\\PORN1_8.WAV", + "AUDIO\\PORN1_9.WAV", + "AUDIO\\PRN1_10.WAV", + "AUDIO\\PRN1_11.WAV", + "AUDIO\\PRN1_12.WAV", + "AUDIO\\PRN1_13.WAV", + "AUDIO\\PRN1_14.WAV", + "AUDIO\\PRN1_15.WAV", + "AUDIO\\PRN1_16.WAV", + "AUDIO\\PRN1_17.WAV", + "AUDIO\\PRN1_18.WAV", + "AUDIO\\PRN1_19.WAV", + "AUDIO\\PRN1_20.WAV", + "AUDIO\\PRN1_21.WAV", + "AUDIO\\PORN3_1.WAV", + "AUDIO\\PORN3_2.WAV", + "AUDIO\\PORN3_3.WAV", + "AUDIO\\PORN3_4.WAV", + "AUDIO\\PSYCH_1.WAV", + "AUDIO\\PSYCH_2.WAV", + "AUDIO\\ROK2_01.WAV", + "AUDIO\\ROK3_1.WAV", + "AUDIO\\ROK3_2.WAV", + "AUDIO\\ROK3_3.WAV", + "AUDIO\\ROK3_4.WAV", + "AUDIO\\ROK3_5.WAV", + "AUDIO\\ROK3_6.WAV", + "AUDIO\\ROK3_7.WAV", + "AUDIO\\ROK3_8.WAV", + "AUDIO\\ROK3_9.WAV", + "AUDIO\\ROK3_10.WAV", + "AUDIO\\ROK3_11.WAV", + "AUDIO\\ROK3_12.WAV", + "AUDIO\\ROK3_13.WAV", + "AUDIO\\ROK3_14.WAV", + "AUDIO\\ROK3_15.WAV", + "AUDIO\\ROK3_16.WAV", + "AUDIO\\ROK3_17.WAV", + "AUDIO\\ROK3_18.WAV", + "AUDIO\\ROK3_19.WAV", + "AUDIO\\ROK3_20.WAV", + "AUDIO\\ROK3_21.WAV", + "AUDIO\\ROK3_22.WAV", + "AUDIO\\ROK3_23.WAV", + "AUDIO\\ROK3_24.WAV", + "AUDIO\\ROK3_25.WAV", + "AUDIO\\ROK3_26.WAV", + "AUDIO\\ROK3_27.WAV", + "AUDIO\\ROK3_62.WAV", + "AUDIO\\ROK3_63.WAV", + "AUDIO\\ROK3_64.WAV", + "AUDIO\\ROK3_65.WAV", + "AUDIO\\ROK3_66.WAV", + "AUDIO\\ROK3_67.WAV", + "AUDIO\\ROK3_68.WAV", + "AUDIO\\ROK3_69.WAV", + "AUDIO\\ROK3_70.WAV", + "AUDIO\\ROK3_71.WAV", + "AUDIO\\ROK3_73.WAV", + "AUDIO\\RESC_1.WAV", + "AUDIO\\RESC_2.WAV", + "AUDIO\\RESC_3.WAV", + "AUDIO\\RESC_4.WAV", + "AUDIO\\RESC_5.WAV", + "AUDIO\\RESC_6.WAV", + "AUDIO\\RESC_7.WAV", + "AUDIO\\RESC_8.WAV", + "AUDIO\\RESC_9.WAV", + "AUDIO\\RESC_10.WAV", + "AUDIO\\ROK1_1A.WAV", + "AUDIO\\ROK1_1B.WAV", + "AUDIO\\ROK1_5.WAV", + "AUDIO\\ROK1_6.WAV", + "AUDIO\\ROK1_7.WAV", + "AUDIO\\ROK1_8.WAV", + "AUDIO\\ROK1_9.WAV", + "AUDIO\\TAX1_1.WAV", + "AUDIO\\TAX1_2.WAV", + "AUDIO\\TAX1_3.WAV", + "AUDIO\\TAX1_4.WAV", + "AUDIO\\TAX1_5.WAV", + "AUDIO\\TAX2_1.WAV", + "AUDIO\\TAX2_2.WAV", + "AUDIO\\TAX2_3.WAV", + "AUDIO\\TAX2_4.WAV", + "AUDIO\\TAX2_5.WAV", + "AUDIO\\TAX2_6.WAV", + "AUDIO\\TAX2_7.WAV", + "AUDIO\\TAX3_1.WAV", + "AUDIO\\TAX3_2.WAV", + "AUDIO\\TAX3_3.WAV", + "AUDIO\\TAX3_4.WAV", + "AUDIO\\TAX3_5.WAV", + "AUDIO\\TEX1_1.WAV", + "AUDIO\\TEX1_2.WAV", + "AUDIO\\TEX1_3.WAV", + "AUDIO\\TEX1_4.WAV", + "AUDIO\\TEX1_5.WAV", + "AUDIO\\TEX1_6.WAV", + "AUDIO\\TEX2_1.WAV", + "AUDIO\\TEX3_1.WAV", + "AUDIO\\TEX3_2.WAV", + "AUDIO\\TEX3_3.WAV", + "AUDIO\\TEX3_4.WAV", + "AUDIO\\TEX3_5.WAV", + "AUDIO\\TEX3_6.WAV", + "AUDIO\\TEX3_7.WAV", + "AUDIO\\TEX3_8.WAV", + "AUDIO\\HAT_1A.WAV", + "AUDIO\\INTRO1.WAV", + "AUDIO\\INTRO2.WAV", + "AUDIO\\INTRO3.WAV", + "AUDIO\\INTRO4.WAV", + "AUDIO\\MOB_01A.WAV", + "AUDIO\\MOB_01B.WAV", + "AUDIO\\MOB_01C.WAV", + "AUDIO\\MOB_02A.WAV", + "AUDIO\\MOB_02B.WAV", + "AUDIO\\MOB_02C.WAV", + "AUDIO\\MOB_03A.WAV", + "AUDIO\\MOB_03B.WAV", + "AUDIO\\MOB_03C.WAV", + "AUDIO\\MOB_03D.WAV", + "AUDIO\\MOB_03E.WAV", + "AUDIO\\SHARK_1.WAV", + "AUDIO\\SHARK_2.WAV", + "AUDIO\\SHARK_3.WAV", + "AUDIO\\SHARK_4.WAV", + "AUDIO\\SHARK_5.WAV", + "AUDIO\\MOB_04A.WAV", + "AUDIO\\MOB_04B.WAV", + "AUDIO\\MOB_04C.WAV", + "AUDIO\\MOB_04D.WAV", + "AUDIO\\MOB_05A.WAV", + "AUDIO\\MOB_05B.WAV", + "AUDIO\\MOB_05C.WAV", + "AUDIO\\MOB_05D.WAV", + "AUDIO\\MOB_06A.WAV", + "AUDIO\\MOB_06B.WAV", + "AUDIO\\MOB_06C.WAV", + "AUDIO\\MOB_07A.WAV", + "AUDIO\\MOB_07B.WAV", + "AUDIO\\MOB_08A.WAV", + "AUDIO\\MOB_08B.WAV", + "AUDIO\\MOB_08C.WAV", + "AUDIO\\MOB_08D.WAV", + "AUDIO\\MOB_08E.WAV", + "AUDIO\\MOB_08F.WAV", + "AUDIO\\MOB_08G.WAV", + "AUDIO\\MOB_09A.WAV", + "AUDIO\\MOB_09B.WAV", + "AUDIO\\MOB_09C.WAV", + "AUDIO\\MOB_09D.WAV", + "AUDIO\\MOB_09E.WAV", + "AUDIO\\MOB_09F.WAV", + "AUDIO\\MOB_10A.WAV", + "AUDIO\\MOB_10B.WAV", + "AUDIO\\MOB_10C.WAV", + "AUDIO\\MOB_10D.WAV", + "AUDIO\\MOB_10E.WAV", + "AUDIO\\MOB_11A.WAV", + "AUDIO\\MOB_11B.WAV", + "AUDIO\\MOB_11C.WAV", + "AUDIO\\MOB_11D.WAV", + "AUDIO\\MOB_11E.WAV", + "AUDIO\\MOB_11F.WAV", + "AUDIO\\MOB_14A.WAV", + "AUDIO\\MOB_14B.WAV", + "AUDIO\\MOB_14C.WAV", + "AUDIO\\MOB_14D.WAV", + "AUDIO\\MOB_14E.WAV", + "AUDIO\\MOB_14F.WAV", + "AUDIO\\MOB_14G.WAV", + "AUDIO\\MOB_14H.WAV", + "AUDIO\\MOB_16A.WAV", + "AUDIO\\MOB_16B.WAV", + "AUDIO\\MOB_16C.WAV", + "AUDIO\\MOB_16D.WAV", + "AUDIO\\MOB_16E.WAV", + "AUDIO\\MOB_16F.WAV", + "AUDIO\\MOB_16G.WAV", + "AUDIO\\MOB_17A.WAV", + "AUDIO\\MOB_17B.WAV", + "AUDIO\\MOB_17C.WAV", + "AUDIO\\MOB_17D.WAV", + "AUDIO\\MOB_17E.WAV", + "AUDIO\\MOB_17G.WAV", + "AUDIO\\MOB_17H.WAV", + "AUDIO\\MOB_17I.WAV", + "AUDIO\\MOB_17J.WAV", + "AUDIO\\MOB_17K.WAV", + "AUDIO\\MOB_17L.WAV", + "AUDIO\\MOB_18A.WAV", + "AUDIO\\MOB_18B.WAV", + "AUDIO\\MOB_18C.WAV", + "AUDIO\\MOB_18D.WAV", + "AUDIO\\MOB_18E.WAV", + "AUDIO\\MOB_18F.WAV", + "AUDIO\\MOB_18G.WAV", + "AUDIO\\MOB_20A.WAV", + "AUDIO\\MOB_20B.WAV", + "AUDIO\\MOB_20C.WAV", + "AUDIO\\MOB_20D.WAV", + "AUDIO\\MOB_20E.WAV", + "AUDIO\\MOB_24A.WAV", + "AUDIO\\MOB_24B.WAV", + "AUDIO\\MOB_24C.WAV", + "AUDIO\\MOB_24D.WAV", + "AUDIO\\MOB_24E.WAV", + "AUDIO\\MOB_24F.WAV", + "AUDIO\\MOB_24G.WAV", + "AUDIO\\MOB_24H.WAV", + "AUDIO\\MOB_25A.WAV", + "AUDIO\\MOB_25B.WAV", + "AUDIO\\MOB_25C.WAV", + "AUDIO\\MOB_25D.WAV", + "AUDIO\\MOB_26A.WAV", + "AUDIO\\MOB_26B.WAV", + "AUDIO\\MOB_26C.WAV", + "AUDIO\\MOB_26D.WAV", + "AUDIO\\MOB_26E.WAV", + "AUDIO\\MOB_29A.WAV", + "AUDIO\\MOB_29B.WAV", + "AUDIO\\MOB_29C.WAV", + "AUDIO\\MOB_29D.WAV", + "AUDIO\\MOB_29E.WAV", + "AUDIO\\MOB_29F.WAV", + "AUDIO\\MOB_29G.WAV", + "AUDIO\\MOB_30A.WAV", + "AUDIO\\MOB_30B.WAV", + "AUDIO\\MOB_30C.WAV", + "AUDIO\\MOB_30D.WAV", + "AUDIO\\MOB_30E.WAV", + "AUDIO\\MOB_30F.WAV", + "AUDIO\\MOB_33A.WAV", + "AUDIO\\MOB_33B.WAV", + "AUDIO\\MOB_33C.WAV", + "AUDIO\\MOB_33D.WAV", + "AUDIO\\MOB_34A.WAV", + "AUDIO\\MOB_34B.WAV", + "AUDIO\\MOB_34C.WAV", + "AUDIO\\MOB_34D.WAV", + "AUDIO\\MOB_35A.WAV", + "AUDIO\\MOB_35B.WAV", + "AUDIO\\MOB_35C.WAV", + "AUDIO\\MOB_35D.WAV", + "AUDIO\\MOB_36A.WAV", + "AUDIO\\MOB_36B.WAV", + "AUDIO\\MOB_36C.WAV", + "AUDIO\\MOB_40A.WAV", + "AUDIO\\MOB_40B.WAV", + "AUDIO\\MOB_40C.WAV", + "AUDIO\\MOB_40D.WAV", + "AUDIO\\MOB_40E.WAV", + "AUDIO\\MOB_40F.WAV", + "AUDIO\\MOB_40G.WAV", + "AUDIO\\MOB_40H.WAV", + "AUDIO\\MOB_40I.WAV", + "AUDIO\\MOB_41A.WAV", + "AUDIO\\MOB_41B.WAV", + "AUDIO\\MOB_41C.WAV", + "AUDIO\\MOB_41D.WAV", + "AUDIO\\MOB_41E.WAV", + "AUDIO\\MOB_41F.WAV", + "AUDIO\\MOB_41G.WAV", + "AUDIO\\MOB_41H.WAV", + "AUDIO\\MOB_42A.WAV", + "AUDIO\\MOB_42B.WAV", + "AUDIO\\MOB_42C.WAV", + "AUDIO\\MOB_42D.WAV", + "AUDIO\\MOB_42E.WAV", + "AUDIO\\MOB_43A.WAV", + "AUDIO\\MOB_43B.WAV", + "AUDIO\\MOB_43C.WAV", + "AUDIO\\MOB_43D.WAV", + "AUDIO\\MOB_43E.WAV", + "AUDIO\\MOB_43F.WAV", + "AUDIO\\MOB_43G.WAV", + "AUDIO\\MOB_43H.WAV", + "AUDIO\\MOB_45A.WAV", + "AUDIO\\MOB_45B.WAV", + "AUDIO\\MOB_45C.WAV", + "AUDIO\\MOB_45D.WAV", + "AUDIO\\MOB_45E.WAV", + "AUDIO\\MOB_45F.WAV", + "AUDIO\\MOB_45G.WAV", + "AUDIO\\MOB_45H.WAV", + "AUDIO\\MOB_45I.WAV", + "AUDIO\\MOB_45J.WAV", + "AUDIO\\MOB_45K.WAV", + "AUDIO\\MOB_45L.WAV", + "AUDIO\\MOB_45M.WAV", + "AUDIO\\MOB_45N.WAV", + "AUDIO\\MOB_46A.WAV", + "AUDIO\\MOB_46B.WAV", + "AUDIO\\MOB_46C.WAV", + "AUDIO\\MOB_46D.WAV", + "AUDIO\\MOB_46E.WAV", + "AUDIO\\MOB_46F.WAV", + "AUDIO\\MOB_46G.WAV", + "AUDIO\\MOB_46H.WAV", + "AUDIO\\MOB_47A.WAV", + "AUDIO\\MOB_52A.WAV", + "AUDIO\\MOB_52B.WAV", + "AUDIO\\MOB_52C.WAV", + "AUDIO\\MOB_52D.WAV", + "AUDIO\\MOB_52E.WAV", + "AUDIO\\MOB_52F.WAV", + "AUDIO\\MOB_52G.WAV", + "AUDIO\\MOB_52H.WAV", + "AUDIO\\MOB_54A.WAV", + "AUDIO\\MOB_54B.WAV", + "AUDIO\\MOB_54C.WAV", + "AUDIO\\MOB_54D.WAV", + "AUDIO\\MOB_54E.WAV", + "AUDIO\\MOB_55A.WAV", + "AUDIO\\MOB_55B.WAV", + "AUDIO\\MOB_55C.WAV", + "AUDIO\\MOB_55D.WAV", + "AUDIO\\MOB_55E.WAV", + "AUDIO\\MOB_55F.WAV", + "AUDIO\\MOB_56A.WAV", + "AUDIO\\MOB_56B.WAV", + "AUDIO\\MOB_56C.WAV", + "AUDIO\\MOB_56D.WAV", + "AUDIO\\MOB_56E.WAV", + "AUDIO\\MOB_56F.WAV", + "AUDIO\\MOB_57A.WAV", + "AUDIO\\MOB_57B.WAV", + "AUDIO\\MOB_57C.WAV", + "AUDIO\\MOB_57D.WAV", + "AUDIO\\MOB_57E.WAV", + "AUDIO\\MOB_58A.WAV", + "AUDIO\\MOB_58B.WAV", + "AUDIO\\MOB_58C.WAV", + "AUDIO\\MOB_58D.WAV", + "AUDIO\\MOB_58E.WAV", + "AUDIO\\MOB_58F.WAV", + "AUDIO\\MOB_58G.WAV", + "AUDIO\\MOB_61A.WAV", + "AUDIO\\MOB_61B.WAV", + "AUDIO\\MOB_62A.WAV", + "AUDIO\\MOB_62B.WAV", + "AUDIO\\MOB_62C.WAV", + "AUDIO\\MOB_62D.WAV", + "AUDIO\\MOB_63A.WAV", + "AUDIO\\MOB_63B.WAV", + "AUDIO\\MOB_63C.WAV", + "AUDIO\\MOB_63D.WAV", + "AUDIO\\MOB_63E.WAV", + "AUDIO\\MOB_63F.WAV", + "AUDIO\\MOB_63G.WAV", + "AUDIO\\MOB_63H.WAV", + "AUDIO\\MOB_63I.WAV", + "AUDIO\\MOB_63J.WAV", + "AUDIO\\MOB_66A.WAV", + "AUDIO\\MOB_66B.WAV", + "AUDIO\\MOB_68A.WAV", + "AUDIO\\MOB_68B.WAV", + "AUDIO\\MOB_68C.WAV", + "AUDIO\\MOB_68D.WAV", + "AUDIO\\MOB_70A.WAV", + "AUDIO\\MOB_70B.WAV", + "AUDIO\\MOB_71A.WAV", + "AUDIO\\MOB_71B.WAV", + "AUDIO\\MOB_71C.WAV", + "AUDIO\\MOB_71D.WAV", + "AUDIO\\MOB_71E.WAV", + "AUDIO\\MOB_71F.WAV", + "AUDIO\\MOB_71G.WAV", + "AUDIO\\MOB_71H.WAV", + "AUDIO\\MOB_71I.WAV", + "AUDIO\\MOB_71J.WAV", + "AUDIO\\MOB_71K.WAV", + "AUDIO\\MOB_71L.WAV", + "AUDIO\\MOB_71M.WAV", + "AUDIO\\MOB_71N.WAV", + "AUDIO\\MOB_72A.WAV", + "AUDIO\\MOB_72B.WAV", + "AUDIO\\MOB_72C.WAV", + "AUDIO\\MOB_72D.WAV", + "AUDIO\\MOB_72E.WAV", + "AUDIO\\MOB_72F.WAV", + "AUDIO\\MOB_72G.WAV", + "AUDIO\\MOB_73A.WAV", + "AUDIO\\MOB_73C.WAV", + "AUDIO\\MOB_73D.WAV", + "AUDIO\\MOB_73F.WAV", + "AUDIO\\MOB_73G.WAV", + "AUDIO\\MOB_73I.WAV", + "AUDIO\\MOB_95A.WAV", + "AUDIO\\MOB_96A.WAV", + "AUDIO\\MOB_98A.WAV", + "AUDIO\\MOB_99A.WAV", + "AUDIO\\JOB1_1B.WAV", + "AUDIO\\JOB1_1C.WAV", + "AUDIO\\JOB1_1D.WAV", + "AUDIO\\JOB2_1B.WAV", + "AUDIO\\JOB2_2.WAV", + "AUDIO\\JOB2_3.WAV", + "AUDIO\\JOB2_4.WAV", + "AUDIO\\JOB2_5.WAV", + "AUDIO\\JOB2_6.WAV", + "AUDIO\\JOB2_7.WAV", + "AUDIO\\JOB2_8.WAV", + "AUDIO\\JOB2_9.WAV", + "AUDIO\\JOB3_1.WAV", + "AUDIO\\JOB3_2.WAV", + "AUDIO\\JOB3_3.WAV", + "AUDIO\\JOB4_1.WAV", + "AUDIO\\JOB4_2.WAV", + "AUDIO\\JOB4_3.WAV", + "AUDIO\\JOB5_1.WAV", + "AUDIO\\JOB5_2.WAV", + "AUDIO\\JOB5_3.WAV", + "AUDIO\\BJM1_20.WAV", + "AUDIO\\BJM1_4.WAV", + "AUDIO\\BJM1_5.WAV", + "AUDIO\\MERC_39.WAV", + "AUDIO\\MONO_1.WAV", + "AUDIO\\MONO_2.WAV", + "AUDIO\\MONO_3.WAV", + "AUDIO\\MONO_4.WAV", + "AUDIO\\MONO_5.WAV", + "AUDIO\\MONO_6.WAV", + "AUDIO\\MONO_7.WAV", + "AUDIO\\MONO_8.WAV", + "AUDIO\\MONO_9.WAV", + "AUDIO\\MONO10.WAV", + "AUDIO\\MONO11.WAV", + "AUDIO\\MONO12.WAV", + "AUDIO\\MONO13.WAV", + "AUDIO\\MONO14.WAV", + "AUDIO\\MONO15.WAV", + "AUDIO\\MONO16.WAV", + "AUDIO\\FUD_01.WAV", + "AUDIO\\FUD_02.WAV", + "AUDIO\\FUD_03.WAV", + "AUDIO\\FUD_04.WAV", + "AUDIO\\FUD_05.WAV", + "AUDIO\\FUD_06.WAV", + "AUDIO\\FUD_07.WAV", + "AUDIO\\FUD_08.WAV", + "AUDIO\\FUD_09.WAV", + "AUDIO\\FUD_10.WAV", + "AUDIO\\FUD_11.WAV", + "AUDIO\\FUD_12.WAV", + "AUDIO\\FUD_13.WAV", + "AUDIO\\FUD_14.WAV", + "AUDIO\\FUD_15.WAV", + "AUDIO\\FUD_16.WAV", + "AUDIO\\FUD_17.WAV", + "AUDIO\\FUD_18.WAV", + "AUDIO\\FUD_19.WAV", + "AUDIO\\FUD_20.WAV", + "AUDIO\\BURG_01.WAV", + "AUDIO\\BURG_02.WAV", + "AUDIO\\BURG_03.WAV", + "AUDIO\\BURG_04.WAV", + "AUDIO\\BURG_05.WAV", + "AUDIO\\BURG_06.WAV", + "AUDIO\\BURG_07.WAV", + "AUDIO\\BURG_08.WAV", + "AUDIO\\BURG_09.WAV", + "AUDIO\\BURG_10.WAV", + "AUDIO\\BURG_11.WAV", + "AUDIO\\BURG_12.WAV", + "AUDIO\\CRUST01.WAV", + "AUDIO\\CRUST02.WAV", + "AUDIO\\CRUST03.WAV", + "AUDIO\\CRUST04.WAV", + "AUDIO\\CRUST05.WAV", + "AUDIO\\CRUST06.WAV", + "AUDIO\\CRUST07.WAV", + "AUDIO\\CRUST08.WAV", + "AUDIO\\CRUST09.WAV", + "AUDIO\\BAND_01.WAV", + "AUDIO\\BAND_02.WAV", + "AUDIO\\BAND_03.WAV", + "AUDIO\\BAND_04.WAV", + "AUDIO\\BAND_05.WAV", + "AUDIO\\BAND_06.WAV", + "AUDIO\\BAND_07.WAV", + "AUDIO\\BAND_08.WAV", + "AUDIO\\SHAFT01.WAV", + "AUDIO\\SHAFT02.WAV", + "AUDIO\\SHAFT03.WAV", + "AUDIO\\SHAFT04.WAV", + "AUDIO\\SHAFT05.WAV", + "AUDIO\\SHAFT06.WAV", + "AUDIO\\SHAFT07.WAV", + "AUDIO\\SHAFT08.WAV", + "AUDIO\\PISS_01.WAV", + "AUDIO\\PISS_02.WAV", + "AUDIO\\PISS_03.WAV", + "AUDIO\\PISS_04.WAV", + "AUDIO\\PISS_05.WAV", + "AUDIO\\PISS_06.WAV", + "AUDIO\\PISS_07.WAV", + "AUDIO\\PISS_08.WAV", + "AUDIO\\PISS_09.WAV", + "AUDIO\\PISS_10.WAV", + "AUDIO\\PISS_11.WAV", + "AUDIO\\PISS_12.WAV", + "AUDIO\\PISS_13.WAV", + "AUDIO\\PISS_14.WAV", + "AUDIO\\PISS_15.WAV", + "AUDIO\\PISS_16.WAV", + "AUDIO\\PISS_17.WAV", + "AUDIO\\PISS_18.WAV", + "AUDIO\\PISS_19.WAV", + "AUDIO\\GIMME01.WAV", + "AUDIO\\GIMME02.WAV", + "AUDIO\\GIMME03.WAV", + "AUDIO\\GIMME04.WAV", + "AUDIO\\GIMME05.WAV", + "AUDIO\\GIMME06.WAV", + "AUDIO\\GIMME07.WAV", + "AUDIO\\GIMME08.WAV", + "AUDIO\\GIMME09.WAV", + "AUDIO\\GIMME10.WAV", + "AUDIO\\GIMME11.WAV", + "AUDIO\\GIMME12.WAV", + "AUDIO\\GIMME13.WAV", + "AUDIO\\GIMME14.WAV", + "AUDIO\\GIMME15.WAV", + "AUDIO\\BUST_01.WAV", + "AUDIO\\BUST_02.WAV", + "AUDIO\\BUST_03.WAV", + "AUDIO\\BUST_04.WAV", + "AUDIO\\BUST_05.WAV", + "AUDIO\\BUST_06.WAV", + "AUDIO\\BUST_07.WAV", + "AUDIO\\BUST_08.WAV", + "AUDIO\\BUST_09.WAV", + "AUDIO\\BUST_10.WAV", + "AUDIO\\BUST_11.WAV", + "AUDIO\\BUST_12.WAV", + "AUDIO\\BUST_13.WAV", + "AUDIO\\BUST_14.WAV", + "AUDIO\\BUST_15.WAV", + "AUDIO\\BUST_16.WAV", + "AUDIO\\BUST_17.WAV", + "AUDIO\\BUST_18.WAV", + "AUDIO\\BUST_19.WAV", + "AUDIO\\BUST_20.WAV", + "AUDIO\\BUST_21.WAV", + "AUDIO\\BUST_22.WAV", + "AUDIO\\BUST_23.WAV", + "AUDIO\\BUST_24.WAV", + "AUDIO\\BUST_25.WAV", + "AUDIO\\BUST_26.WAV", + "AUDIO\\BUST_27.WAV", + "AUDIO\\BUST_28.WAV", +}; +#endif + +#ifdef PC_AUDIO_PATHS +static char StreamedNameTable[][25] = +{ + "AUDIO\\WILD.ADF", + "AUDIO\\FLASH.ADF", + "AUDIO\\KCHAT.ADF", + "AUDIO\\FEVER.ADF", + "AUDIO\\VROCK.ADF", + "AUDIO\\VCPR.ADF", + "AUDIO\\ESPANT.ADF", + "AUDIO\\EMOTION.ADF", + "AUDIO\\WAVE.ADF", + "AUDIO\\MISCOM.MP3", + "AUDIO\\CITY.MP3", + "AUDIO\\WATER.MP3", + "AUDIO\\BEACHAMB.MP3", + "AUDIO\\HCITY.MP3", + "AUDIO\\HWATER.MP3", + "AUDIO\\HBEACH.MP3", + "AUDIO\\MALLAMB.MP3", + "AUDIO\\STRIP.MP3", + "AUDIO\\MALIBU.MP3", + "AUDIO\\HOTEL.MP3", + "AUDIO\\DIRTRING.MP3", + "AUDIO\\LAW4RIOT.MP3", + "AUDIO\\AMBSIL.MP3", + "AUDIO\\POLICE.MP3", + "AUDIO\\TAXI.MP3", + "AUDIO\\BCLOSED.MP3", + "AUDIO\\BOPEN.MP3", + "AUDIO\\ASS_1.MP3", + "AUDIO\\ASS_2.MP3", + "AUDIO\\BANK_1.MP3", + "AUDIO\\BANK_2A.MP3", + "AUDIO\\BANK_2B.MP3", + "AUDIO\\BANK_3A.MP3", + "AUDIO\\BANK_3B.MP3", + "AUDIO\\BANK_4.MP3", + "AUDIO\\BIKE_1.MP3", + "AUDIO\\BIKE_2.MP3", + "AUDIO\\BIKE_3.MP3", + "AUDIO\\BUD_1.MP3", + "AUDIO\\BUD_2.MP3", + "AUDIO\\BUD_3.MP3", + "AUDIO\\CAP_1.MP3", + "AUDIO\\CAR_1.MP3", + "AUDIO\\CNT_1A.MP3", + "AUDIO\\CNT_1B.MP3", + "AUDIO\\CNT_2.MP3", + "AUDIO\\COK_1.MP3", + "AUDIO\\COK_2A.MP3", + "AUDIO\\COK_2B.MP3", + "AUDIO\\COK_3.MP3", + "AUDIO\\COK_4A.MP3", + "AUDIO\\COK_4A2.MP3", + "AUDIO\\COK_4B.MP3", + "AUDIO\\COL_1.MP3", + "AUDIO\\COL_2.MP3", + "AUDIO\\COL_3A.MP3", + "AUDIO\\COL_4A.MP3", + "AUDIO\\COL_5A.MP3", + "AUDIO\\COL_5B.MP3", + "AUDIO\\CUB_1.MP3", + "AUDIO\\CUB_2.MP3", + "AUDIO\\CUB_3.MP3", + "AUDIO\\CUB_4.MP3", + "AUDIO\\DRUG_1.MP3", + "AUDIO\\FIN.MP3", + "AUDIO\\FIN2.MP3", + "AUDIO\\FINALE.MP3", + "AUDIO\\HAT_1.MP3", + "AUDIO\\HAT_2.MP3", + "AUDIO\\HAT_3.MP3", + "AUDIO\\ICE_1.MP3", + "AUDIO\\INT_A.MP3", + "AUDIO\\INT_B.MP3", + "AUDIO\\INT_D.MP3", + "AUDIO\\INT_M.MP3", + "AUDIO\\LAW_1A.MP3", + "AUDIO\\LAW_1B.MP3", + "AUDIO\\LAW_2A.MP3", + "AUDIO\\LAW_2B.MP3", + "AUDIO\\LAW_2C.MP3", + "AUDIO\\LAW_3.MP3", + "AUDIO\\LAW_4.MP3", + "AUDIO\\PHIL_1.MP3", + "AUDIO\\PHIL_2.MP3", + "AUDIO\\PORN_1.MP3", + "AUDIO\\PORN_2.MP3", + "AUDIO\\PORN_3.MP3", + "AUDIO\\PORN_4.MP3", + "AUDIO\\RESC_1A.MP3", + "AUDIO\\ROK_1.MP3", + "AUDIO\\ROK_2.MP3", + "AUDIO\\ROK_3A.MP3", + "AUDIO\\STRIPA.MP3", + "AUDIO\\TAX_1.MP3", + "AUDIO\\TEX_1.MP3", + "AUDIO\\TEX_2.MP3", + "AUDIO\\TEX_3.MP3", + "AUDIO\\GLIGHT.MP3", + "AUDIO\\FIST.MP3", + "AUDIO\\MISCOM.MP3", + "AUDIO\\MISCOM.MP3", + "AUDIO\\MISCOM.MP3", + "AUDIO\\MISCOM.MP3", + "AUDIO\\MOBR1.WAV", + "AUDIO\\PAGER.WAV", + "AUDIO\\CARREV.WAV", + "AUDIO\\BIKEREV.WAV", + "AUDIO\\LIFTOP.WAV", + "AUDIO\\LIFTCL.WAV", + "AUDIO\\LIFTRUN.WAV", + "AUDIO\\LIFTBEL.WAV", + "AUDIO\\INLIFT.WAV", + "AUDIO\\SFX_01.WAV", + "AUDIO\\SFX_02.WAV", + "AUDIO\\CAMERAL.WAV", + "AUDIO\\CAMERAR.WAV", + "AUDIO\\CHEER1.WAV", + "AUDIO\\CHEER2.WAV", + "AUDIO\\CHEER3.WAV", + "AUDIO\\CHEER4.WAV", + "AUDIO\\OOH1.WAV", + "AUDIO\\OOH2.WAV", + "AUDIO\\RACE1.WAV", + "AUDIO\\RACE2.WAV", + "AUDIO\\RACE3.WAV", + "AUDIO\\RACE4.WAV", + "AUDIO\\RACE5.WAV", + "AUDIO\\RACE6.WAV", + "AUDIO\\RACE7.WAV", + "AUDIO\\RACE8.WAV", + "AUDIO\\RACE9.WAV", + "AUDIO\\RACE10.WAV", + "AUDIO\\RACE11.WAV", + "AUDIO\\RACE12.WAV", + "AUDIO\\RACE13.WAV", + "AUDIO\\RACE14.WAV", + "AUDIO\\RACE15.WAV", + "AUDIO\\HOT1.WAV", + "AUDIO\\HOT2.WAV", + "AUDIO\\HOT3.WAV", + "AUDIO\\HOT4.WAV", + "AUDIO\\HOT5.WAV", + "AUDIO\\HOT6.WAV", + "AUDIO\\HOT7.WAV", + "AUDIO\\HOT8.WAV", + "AUDIO\\HOT9.WAV", + "AUDIO\\HOT10.WAV", + "AUDIO\\HOT11.WAV", + "AUDIO\\HOT12.WAV", + "AUDIO\\HOT13.WAV", + "AUDIO\\HOT14.WAV", + "AUDIO\\HOT15.WAV", + "AUDIO\\LANSTP1.WAV", + "AUDIO\\LANSTP2.WAV", + "AUDIO\\LANAMU1.WAV", + "AUDIO\\LANAMU2.WAV", + "AUDIO\\AIRHORNL.WAV", + "AUDIO\\AIRHORNR.WAV", + "AUDIO\\SNIPSCRL.WAV", + "AUDIO\\SNIPSHORT.WAV", + "AUDIO\\BLOWROOF.WAV", + "AUDIO\\ASS_1.WAV", + "AUDIO\\ASS_2.WAV", + "AUDIO\\ASS_3.WAV", + "AUDIO\\ASS_4.WAV", + "AUDIO\\ASS_5.WAV", + "AUDIO\\ASS_6.WAV", + "AUDIO\\ASS_7.WAV", + "AUDIO\\ASS_8.WAV", + "AUDIO\\ASS_9.WAV", + "AUDIO\\ASS_10.WAV", + "AUDIO\\ASS_11.WAV", + "AUDIO\\ASS_12.WAV", + "AUDIO\\ASS_13.WAV", + "AUDIO\\ASS_14.WAV", + "AUDIO\\BIKE1_1.WAV", + "AUDIO\\BIKE1_2.WAV", + "AUDIO\\BIKE1_3.WAV", + "AUDIO\\BNK1_1.WAV", + "AUDIO\\BNK1_2.WAV", + "AUDIO\\BNK1_3.WAV", + "AUDIO\\BNK1_4.WAV", + "AUDIO\\BNK1_5.WAV", + "AUDIO\\BNK1_6.WAV", + "AUDIO\\BNK1_7.WAV", + "AUDIO\\BNK1_8.WAV", + "AUDIO\\BNK1_10.WAV", + "AUDIO\\BNK1_11.WAV", + "AUDIO\\BNK1_12.WAV", + "AUDIO\\BNK1_13.WAV", + "AUDIO\\BNK1_14.WAV", + "AUDIO\\BNK2_1.WAV", + "AUDIO\\BNK2_2.WAV", + "AUDIO\\BNK2_3.WAV", + "AUDIO\\BNK2_4.WAV", + "AUDIO\\BNK2_5.WAV", + "AUDIO\\BNK2_6.WAV", + "AUDIO\\BNK2_7.WAV", + "AUDIO\\BNK2_8.WAV", + "AUDIO\\BNK2_9.WAV", + "AUDIO\\BNK3_1.WAV", + "AUDIO\\BNK3_2.WAV", + "AUDIO\\BNK3_3A.WAV", + "AUDIO\\BNK3_3B.WAV", + "AUDIO\\BNK3_3C.WAV", + "AUDIO\\BNK3_4A.WAV", + "AUDIO\\BNK3_4B.WAV", + "AUDIO\\BNK3_4C.WAV", + "AUDIO\\BNK4_1.WAV", + "AUDIO\\BNK4_2.WAV", + "AUDIO\\BNK4_3A.WAV", + "AUDIO\\BNK4_3B.WAV", + "AUDIO\\BNK4_3C.WAV", + "AUDIO\\BNK4_3D.WAV", + "AUDIO\\BNK4_3E.WAV", + "AUDIO\\BNK4_3F.WAV", + "AUDIO\\BNK4_3G.WAV", + "AUDIO\\BNK4_3H.WAV", + "AUDIO\\BNK4_3I.WAV", + "AUDIO\\BNK4_3J.WAV", + "AUDIO\\BNK4_3K.WAV", + "AUDIO\\BNK4_3M.WAV", + "AUDIO\\BNK4_3O.WAV", + "AUDIO\\BNK4_3P.WAV", + "AUDIO\\BNK4_3Q.WAV", + "AUDIO\\BNK4_3R.WAV", + "AUDIO\\BNK4_3S.WAV", + "AUDIO\\BNK4_3T.WAV", + "AUDIO\\BNK4_3U.WAV", + "AUDIO\\BNK4_3V.WAV", + "AUDIO\\BNK4_4A.WAV", + "AUDIO\\BNK4_4B.WAV", + "AUDIO\\BNK4_5.WAV", + "AUDIO\\BNK4_6.WAV", + "AUDIO\\BNK4_7.WAV", + "AUDIO\\BNK4_8.WAV", + "AUDIO\\BNK4_9.WAV", + "AUDIO\\BNK4_10.WAV", + "AUDIO\\BNK4_11.WAV", + "AUDIO\\BK4_12A.WAV", + "AUDIO\\BK4_12B.WAV", + "AUDIO\\BK4_12C.WAV", + "AUDIO\\BNK4_13.WAV", + "AUDIO\\BK4_14A.WAV", + "AUDIO\\BK4_14B.WAV", + "AUDIO\\BNK4_15.WAV", + "AUDIO\\BNK4_16.WAV", + "AUDIO\\BNK4_17.WAV", + "AUDIO\\BNK4_18.WAV", + "AUDIO\\BK4_19A.WAV", + "AUDIO\\BK4_19B.WAV", + "AUDIO\\BK4_20A.WAV", + "AUDIO\\BK4_20B.WAV", + "AUDIO\\BNK4_21.WAV", + "AUDIO\\BNK422A.WAV", + "AUDIO\\BNK422B.WAV", + "AUDIO\\BK4_23A.WAV", + "AUDIO\\BK4_23B.WAV", + "AUDIO\\BK4_23C.WAV", + "AUDIO\\BK4_23D.WAV", + "AUDIO\\BK4_24A.WAV", + "AUDIO\\BK4_24B.WAV", + "AUDIO\\BNK4_25.WAV", + "AUDIO\\BNK4_26.WAV", + "AUDIO\\BNK4_27.WAV", + "AUDIO\\BNK4_28.WAV", + "AUDIO\\BNK4_29.WAV", + "AUDIO\\BNK4_30.WAV", + "AUDIO\\BK4_31A.WAV", + "AUDIO\\BK4_31B.WAV", + "AUDIO\\BNK4_32.WAV", + "AUDIO\\BK4_34A.WAV", + "AUDIO\\BK4_34B.WAV", + "AUDIO\\BK4_35A.WAV", + "AUDIO\\BK4_35B.WAV", + "AUDIO\\BNK4_36.WAV", + "AUDIO\\BNK4_37.WAV", + "AUDIO\\BNK4_38.WAV", + "AUDIO\\BNK4_39.WAV", + "AUDIO\\BK4_40A.WAV", + "AUDIO\\BK4_40B.WAV", + "AUDIO\\BNK4_41.WAV", + "AUDIO\\BNK4_42.WAV", + "AUDIO\\BNK4_43.WAV", + "AUDIO\\BNK4_44.WAV", + "AUDIO\\BNK4_45.WAV", + "AUDIO\\BNK4_46.WAV", + "AUDIO\\BNK4_47.WAV", + "AUDIO\\BNK4_48.WAV", + "AUDIO\\BNK4_49.WAV", + "AUDIO\\BNK450A.WAV", + "AUDIO\\BNK450B.WAV", + "AUDIO\\BNK4_51.WAV", + "AUDIO\\BNK4_94.WAV", + "AUDIO\\BNK4_95.WAV", + "AUDIO\\BNK4_96.WAV", + "AUDIO\\BNK4_97.WAV", + "AUDIO\\BNK4_98.WAV", + "AUDIO\\BNK4_99.WAV", + "AUDIO\\BUD1_1.WAV", + "AUDIO\\BUD1_2.WAV", + "AUDIO\\BUD1_3.WAV", + "AUDIO\\BUD1_4.WAV", + "AUDIO\\BUD1_5.WAV", + "AUDIO\\BUD1_9.WAV", + "AUDIO\\BUD1_10.WAV", + "AUDIO\\BUD2_1.WAV", + "AUDIO\\BUD2_2.WAV", + "AUDIO\\BUD2_3.WAV", + "AUDIO\\BUD2_4.WAV", + "AUDIO\\BUD2_5.WAV", + "AUDIO\\BUD2_6.WAV", + "AUDIO\\BUD2_7.WAV", + "AUDIO\\BUD3_1.WAV", + "AUDIO\\BUD3_1A.WAV", + "AUDIO\\BUD3_1B.WAV", + "AUDIO\\BUD3_1C.WAV", + "AUDIO\\BUD3_2.WAV", + "AUDIO\\BUD3_3.WAV", + "AUDIO\\BUD3_4.WAV", + "AUDIO\\BUD3_5.WAV", + "AUDIO\\BUD3_6.WAV", + "AUDIO\\BUD3_7.WAV", + "AUDIO\\BUD3_8A.WAV", + "AUDIO\\BUD3_8B.WAV", + "AUDIO\\BUD3_8C.WAV", + "AUDIO\\BUD3_9A.WAV", + "AUDIO\\BUD3_9B.WAV", + "AUDIO\\BUD3_9C.WAV", + "AUDIO\\CAP1_2.WAV", + "AUDIO\\CAP1_3.WAV", + "AUDIO\\CAP1_4.WAV", + "AUDIO\\CAP1_5.WAV", + "AUDIO\\CAP1_6.WAV", + "AUDIO\\CAP1_7.WAV", + "AUDIO\\CAP1_8.WAV", + "AUDIO\\CAP1_9.WAV", + "AUDIO\\CAP1_10.WAV", + "AUDIO\\CAP1_11.WAV", + "AUDIO\\CAP1_12.WAV", + "AUDIO\\CNT1_1.WAV", + "AUDIO\\CNT1_2.WAV", + "AUDIO\\CNT1_3.WAV", + "AUDIO\\CNT1_4.WAV", + "AUDIO\\CNT1_5.WAV", + "AUDIO\\CNT2_1.WAV", + "AUDIO\\CNT2_2.WAV", + "AUDIO\\CNT2_3.WAV", + "AUDIO\\CNT2_4.WAV", + "AUDIO\\COK1_1.WAV", + "AUDIO\\COK1_2.WAV", + "AUDIO\\COK1_3.WAV", + "AUDIO\\COK1_4.WAV", + "AUDIO\\COK1_5.WAV", + "AUDIO\\COK1_6.WAV", + "AUDIO\\COK2_1.WAV", + "AUDIO\\COK2_2.WAV", + "AUDIO\\COK2_3.WAV", + "AUDIO\\COK2_4.WAV", + "AUDIO\\COK2_5.WAV", + "AUDIO\\COK2_6.WAV", + "AUDIO\\COK2_7A.WAV", + "AUDIO\\COK2_7B.WAV", + "AUDIO\\COK2_7C.WAV", + "AUDIO\\COK2_8A.WAV", + "AUDIO\\COK2_8B.WAV", + "AUDIO\\COK2_8C.WAV", + "AUDIO\\COK2_8D.WAV", + "AUDIO\\COK2_9.WAV", + "AUDIO\\COK210A.WAV", + "AUDIO\\COK210B.WAV", + "AUDIO\\COK210C.WAV", + "AUDIO\\COK212A.WAV", + "AUDIO\\COK212B.WAV", + "AUDIO\\COK2_13.WAV", + "AUDIO\\COK2_14.WAV", + "AUDIO\\COK2_15.WAV", + "AUDIO\\COK2_16.WAV", + "AUDIO\\COK2_20.WAV", + "AUDIO\\COK2_21.WAV", + "AUDIO\\COK2_2.WAV", // this is probably a typo of COK2_22 + "AUDIO\\COK3_1.WAV", + "AUDIO\\COK3_2.WAV", + "AUDIO\\COK3_3.WAV", + "AUDIO\\COK3_4.WAV", + "AUDIO\\COK4_1.WAV", + "AUDIO\\COK4_2.WAV", + "AUDIO\\COK4_3.WAV", + "AUDIO\\COK4_4.WAV", + "AUDIO\\COK4_5.WAV", + "AUDIO\\COK4_6.WAV", + "AUDIO\\COK4_7.WAV", + "AUDIO\\COK4_8.WAV", + "AUDIO\\COK4_9.WAV", + "AUDIO\\COK4_9A.WAV", + "AUDIO\\COK4_10.WAV", + "AUDIO\\COK4_11.WAV", + "AUDIO\\COK4_12.WAV", + "AUDIO\\COK4_13.WAV", + "AUDIO\\COK4_14.WAV", + "AUDIO\\COK4_15.WAV", + "AUDIO\\COK4_16.WAV", + "AUDIO\\COK4_17.WAV", + "AUDIO\\COK4_18.WAV", + "AUDIO\\COK4_19.WAV", + "AUDIO\\COK4_20.WAV", + "AUDIO\\COK4_21.WAV", + "AUDIO\\COK4_22.WAV", + "AUDIO\\COK4_23.WAV", + "AUDIO\\COK4_24.WAV", + "AUDIO\\COK4_25.WAV", + "AUDIO\\COK4_26.WAV", + "AUDIO\\COK4_27.WAV", + "AUDIO\\COL1_1.WAV", + "AUDIO\\COL1_2.WAV", + "AUDIO\\COL1_3.WAV", + "AUDIO\\COL1_4.WAV", + "AUDIO\\COL1_5.WAV", + "AUDIO\\COL1_6.WAV", + "AUDIO\\COL1_7.WAV", + "AUDIO\\COL1_8.WAV", + "AUDIO\\COL2_1.WAV", + "AUDIO\\COL2_2.WAV", + "AUDIO\\COL2_3.WAV", + "AUDIO\\COL2_4.WAV", + "AUDIO\\COL2_5.WAV", + "AUDIO\\COL2_6A.WAV", + "AUDIO\\COL2_7.WAV", + "AUDIO\\COL2_8.WAV", + "AUDIO\\COL2_9.WAV", + "AUDIO\\COL2_10.WAV", + "AUDIO\\COL2_11.WAV", + "AUDIO\\COL2_12.WAV", + "AUDIO\\COL2_13.WAV", + "AUDIO\\COL2_14.WAV", + "AUDIO\\COL2_15.WAV", + "AUDIO\\COL2_16.WAV", + "AUDIO\\COL3_1.WAV", + "AUDIO\\COL3_2.WAV", + "AUDIO\\COL3_2A.WAV", + "AUDIO\\COL3_2B.WAV", + "AUDIO\\COL3_3.WAV", + "AUDIO\\COL3_4.WAV", + "AUDIO\\COL3_5.WAV", + "AUDIO\\COL3_6.WAV", + "AUDIO\\COL3_7.WAV", + "AUDIO\\COL3_8.WAV", + "AUDIO\\COL3_9.WAV", + "AUDIO\\COL3_10.WAV", + "AUDIO\\COL3_11.WAV", + "AUDIO\\COL3_12.WAV", + "AUDIO\\COL3_13.WAV", + "AUDIO\\COL3_14.WAV", + "AUDIO\\COL3_15.WAV", + "AUDIO\\COL3_16.WAV", + "AUDIO\\COL3_17.WAV", + "AUDIO\\COL3_18.WAV", + "AUDIO\\COL3_19.WAV", + "AUDIO\\COL3_20.WAV", + "AUDIO\\COL3_21.WAV", + "AUDIO\\COL3_23.WAV", + "AUDIO\\COL3_24.WAV", + "AUDIO\\COL3_25.WAV", + "AUDIO\\COL4_1.WAV", + "AUDIO\\COL4_2.WAV", + "AUDIO\\COL4_3.WAV", + "AUDIO\\COL4_4.WAV", + "AUDIO\\COL4_5.WAV", + "AUDIO\\COL4_6.WAV", + "AUDIO\\COL4_7.WAV", + "AUDIO\\COL4_8.WAV", + "AUDIO\\COL4_9.WAV", + "AUDIO\\COL4_10.WAV", + "AUDIO\\COL4_11.WAV", + "AUDIO\\COL4_12.WAV", + "AUDIO\\COL4_13.WAV", + "AUDIO\\COL4_14.WAV", + "AUDIO\\COL4_15.WAV", + "AUDIO\\COL4_16.WAV", + "AUDIO\\COL4_17.WAV", + "AUDIO\\COL4_18.WAV", + "AUDIO\\COL4_19.WAV", + "AUDIO\\COL4_20.WAV", + "AUDIO\\COL4_21.WAV", + "AUDIO\\COL4_22.WAV", + "AUDIO\\COL4_23.WAV", + "AUDIO\\COL4_24.WAV", + "AUDIO\\COL4_25.WAV", + "AUDIO\\COL4_26.WAV", + "AUDIO\\COL5_1.WAV", + "AUDIO\\COL5_2.WAV", + "AUDIO\\COL5_3.WAV", + "AUDIO\\COL5_4.WAV", + "AUDIO\\COL5_5.WAV", + "AUDIO\\COL5_6.WAV", + "AUDIO\\COL5_7.WAV", + "AUDIO\\COL5_8.WAV", + "AUDIO\\COL5_9.WAV", + "AUDIO\\COL5_10.WAV", + "AUDIO\\COL5_11.WAV", + "AUDIO\\COL5_12.WAV", + "AUDIO\\COL5_13.WAV", + "AUDIO\\COL5_14.WAV", + "AUDIO\\COL5_15.WAV", + "AUDIO\\COL5_16.WAV", + "AUDIO\\COL5_17.WAV", + "AUDIO\\COL5_18.WAV", + "AUDIO\\COL5_19.WAV", + "AUDIO\\COL5_20.WAV", + "AUDIO\\COL5_21.WAV", + "AUDIO\\COL5_22.WAV", + "AUDIO\\CUB1_1.WAV", + "AUDIO\\CUB1_2.WAV", + "AUDIO\\CUB1_3.WAV", + "AUDIO\\CUB1_4.WAV", + "AUDIO\\CUB1_5.WAV", + "AUDIO\\CUB1_6.WAV", + "AUDIO\\CUB1_7.WAV", + "AUDIO\\CUB1_8.WAV", + "AUDIO\\CUB1_9.WAV", + "AUDIO\\CUB1_10.WAV", + "AUDIO\\CUB2_1.WAV", + "AUDIO\\CUB2_2.WAV", + "AUDIO\\CUB2_3A.WAV", + "AUDIO\\CUB2_3B.WAV", + "AUDIO\\CUB2_3C.WAV", + "AUDIO\\CUB2_4A.WAV", + "AUDIO\\CUB2_5.WAV", + "AUDIO\\CUB2_6.WAV", + "AUDIO\\CUB2_7.WAV", + "AUDIO\\CUB2_8.WAV", + "AUDIO\\CUB2_9.WAV", + "AUDIO\\CUB2_10.WAV", + "AUDIO\\CUB2_11.WAV", + "AUDIO\\CUB3_1.WAV", + "AUDIO\\CUB3_2.WAV", + "AUDIO\\CUB3_3.WAV", + "AUDIO\\CUB3_4.WAV", + "AUDIO\\CUB4_1.WAV", + "AUDIO\\CUB4_2.WAV", + "AUDIO\\CUB4_3.WAV", + "AUDIO\\CUB4_4.WAV", + "AUDIO\\CUB4_5.WAV", + "AUDIO\\CUB4_5A.WAV", + "AUDIO\\CUB4_6.WAV", + "AUDIO\\CUB4_7.WAV", + "AUDIO\\CUB4_8.WAV", + "AUDIO\\CUB4_9.WAV", + "AUDIO\\CUB4_10.WAV", + "AUDIO\\CUB4_11.WAV", + "AUDIO\\CUB4_12.WAV", + "AUDIO\\CUB4_13.WAV", + "AUDIO\\CUB4_14.WAV", + "AUDIO\\CUB4_15.WAV", + "AUDIO\\CUB4_16.WAV", + "AUDIO\\GOLF_1.WAV", + "AUDIO\\GOLF_2.WAV", + "AUDIO\\GOLF_3.WAV", + "AUDIO\\BAR_1.WAV", + "AUDIO\\BAR_2.WAV", + "AUDIO\\BAR_3.WAV", + "AUDIO\\BAR_4.WAV", + "AUDIO\\BAR_5.WAV", + "AUDIO\\BAR_6.WAV", + "AUDIO\\BAR_7.WAV", + "AUDIO\\BAR_8.WAV", + "AUDIO\\STRIP_1.WAV", + "AUDIO\\STRIP_2.WAV", + "AUDIO\\STRIP_3.WAV", + "AUDIO\\STRIP_4.WAV", + "AUDIO\\STRIP_5.WAV", + "AUDIO\\STRIP_6.WAV", + "AUDIO\\STRIP_7.WAV", + "AUDIO\\STRIP_8.WAV", + "AUDIO\\STRIP_9.WAV", + "AUDIO\\STAR_1.WAV", + "AUDIO\\STAR_2.WAV", + "AUDIO\\STAR_3.WAV", + "AUDIO\\STAR_4.WAV", + "AUDIO\\FIN_1A.WAV", + "AUDIO\\FIN_1B.WAV", + "AUDIO\\FIN_1C.WAV", + "AUDIO\\FIN_2B.WAV", + "AUDIO\\FIN_2C.WAV", + "AUDIO\\FIN_3.WAV", + "AUDIO\\FIN_4.WAV", + "AUDIO\\FIN_5.WAV", + "AUDIO\\FIN_6.WAV", + "AUDIO\\FIN_10.WAV", + "AUDIO\\FIN_11A.WAV", + "AUDIO\\FIN_11B.WAV", + "AUDIO\\FIN_12A.WAV", + "AUDIO\\FIN_12B.WAV", + "AUDIO\\FIN_12C.WAV", + "AUDIO\\FIN_13.WAV", + "AUDIO\\FINKILL.WAV", + "AUDIO\\LAW1_1.WAV", + "AUDIO\\LAW1_2.WAV", + "AUDIO\\LAW1_3.WAV", + "AUDIO\\LAW1_4.WAV", + "AUDIO\\LAW1_5.WAV", + "AUDIO\\LAW1_6.WAV", + "AUDIO\\LAW1_7.WAV", + "AUDIO\\LAW1_8.WAV", + "AUDIO\\LAW1_9.WAV", + "AUDIO\\LAW1_10.WAV", + "AUDIO\\LAW2_1.WAV", + "AUDIO\\LAW2_2.WAV", + "AUDIO\\LAW2_3.WAV", + "AUDIO\\LAW2_4.WAV", + "AUDIO\\LAW2_5.WAV", + "AUDIO\\LAW2_6.WAV", + "AUDIO\\LAW2_7.WAV", + "AUDIO\\LAW2_8.WAV", + "AUDIO\\LAW2_9.WAV", + "AUDIO\\LAW2_10.WAV", + "AUDIO\\LAW3_1.WAV", + "AUDIO\\LAW3_2.WAV", + "AUDIO\\LAW3_3.WAV", + "AUDIO\\LAW3_4.WAV", + "AUDIO\\LAW3_5.WAV", + "AUDIO\\LAW3_6.WAV", + "AUDIO\\LAW3_10.WAV", + "AUDIO\\LAW3_11.WAV", + "AUDIO\\LAW3_12.WAV", + "AUDIO\\LAW3_13.WAV", + "AUDIO\\LAW3_14.WAV", + "AUDIO\\LAW3_16.WAV", + "AUDIO\\LAW3_17.WAV", + "AUDIO\\LAW3_18.WAV", + "AUDIO\\LAW3_19.WAV", + "AUDIO\\LAW3_20.WAV", + "AUDIO\\LAW3_21.WAV", + "AUDIO\\LAW3_22.WAV", + "AUDIO\\LAW3_23.WAV", + "AUDIO\\LAW3_24.WAV", + "AUDIO\\LAW3_25.WAV", + "AUDIO\\LAW4_1A.WAV", + "AUDIO\\LAW4_1B.WAV", + "AUDIO\\LAW4_1C.WAV", + "AUDIO\\LAW4_1D.WAV", + "AUDIO\\LAW4_10.WAV", + "AUDIO\\LAW4_3.WAV", + "AUDIO\\LAW4_4.WAV", + "AUDIO\\LAW4_5.WAV", + "AUDIO\\LAW4_6.WAV", + "AUDIO\\LAW4_7.WAV", + "AUDIO\\LAW4_8.WAV", + "AUDIO\\LAW4_9.WAV", + "AUDIO\\PHIL1_2.WAV", + "AUDIO\\PHIL1_3.WAV", + "AUDIO\\PHIL2_1.WAV", + "AUDIO\\PHIL2_2.WAV", + "AUDIO\\PHIL2_3.WAV", + "AUDIO\\PHIL2_4.WAV", + "AUDIO\\PHIL2_5.WAV", + "AUDIO\\PHIL2_6.WAV", + "AUDIO\\PHIL2_7.WAV", + "AUDIO\\PHIL2_8.WAV", + "AUDIO\\PHIL2_9.WAV", + "AUDIO\\PHIL210.WAV", + "AUDIO\\PHIL211.WAV", + "AUDIO\\PORN1_1.WAV", + "AUDIO\\PORN1_2.WAV", + "AUDIO\\PORN1_3.WAV", + "AUDIO\\PRN1_3A.WAV", + "AUDIO\\PORN1_4.WAV", + "AUDIO\\PORN1_5.WAV", + "AUDIO\\PORN1_6.WAV", + "AUDIO\\PORN1_7.WAV", + "AUDIO\\PORN1_8.WAV", + "AUDIO\\PORN1_9.WAV", + "AUDIO\\PRN1_10.WAV", + "AUDIO\\PRN1_11.WAV", + "AUDIO\\PRN1_12.WAV", + "AUDIO\\PRN1_13.WAV", + "AUDIO\\PRN1_14.WAV", + "AUDIO\\PRN1_15.WAV", + "AUDIO\\PRN1_16.WAV", + "AUDIO\\PRN1_17.WAV", + "AUDIO\\PRN1_18.WAV", + "AUDIO\\PRN1_19.WAV", + "AUDIO\\PRN1_20.WAV", + "AUDIO\\PRN1_21.WAV", + "AUDIO\\PORN3_1.WAV", + "AUDIO\\PORN3_2.WAV", + "AUDIO\\PORN3_3.WAV", + "AUDIO\\PORN3_4.WAV", + "AUDIO\\PSYCH_1.WAV", + "AUDIO\\PSYCH_2.WAV", + "AUDIO\\ROK2_01.WAV", + "AUDIO\\ROK3_1.WAV", + "AUDIO\\ROK3_2.WAV", + "AUDIO\\ROK3_3.WAV", + "AUDIO\\ROK3_4.WAV", + "AUDIO\\ROK3_5.WAV", + "AUDIO\\ROK3_6.WAV", + "AUDIO\\ROK3_7.WAV", + "AUDIO\\ROK3_8.WAV", + "AUDIO\\ROK3_9.WAV", + "AUDIO\\ROK3_10.WAV", + "AUDIO\\ROK3_11.WAV", + "AUDIO\\ROK3_12.WAV", + "AUDIO\\ROK3_13.WAV", + "AUDIO\\ROK3_14.WAV", + "AUDIO\\ROK3_15.WAV", + "AUDIO\\ROK3_16.WAV", + "AUDIO\\ROK3_17.WAV", + "AUDIO\\ROK3_18.WAV", + "AUDIO\\ROK3_19.WAV", + "AUDIO\\ROK3_20.WAV", + "AUDIO\\ROK3_21.WAV", + "AUDIO\\ROK3_22.WAV", + "AUDIO\\ROK3_23.WAV", + "AUDIO\\ROK3_24.WAV", + "AUDIO\\ROK3_25.WAV", + "AUDIO\\ROK3_26.WAV", + "AUDIO\\ROK3_27.WAV", + "AUDIO\\ROK3_62.WAV", + "AUDIO\\ROK3_63.WAV", + "AUDIO\\ROK3_64.WAV", + "AUDIO\\ROK3_65.WAV", + "AUDIO\\ROK3_66.WAV", + "AUDIO\\ROK3_67.WAV", + "AUDIO\\ROK3_68.WAV", + "AUDIO\\ROK3_69.WAV", + "AUDIO\\ROK3_70.WAV", + "AUDIO\\ROK3_71.WAV", + "AUDIO\\ROK3_73.WAV", + "AUDIO\\RESC_1.WAV", + "AUDIO\\RESC_2.WAV", + "AUDIO\\RESC_3.WAV", + "AUDIO\\RESC_4.WAV", + "AUDIO\\RESC_5.WAV", + "AUDIO\\RESC_6.WAV", + "AUDIO\\RESC_7.WAV", + "AUDIO\\RESC_8.WAV", + "AUDIO\\RESC_9.WAV", + "AUDIO\\RESC_10.WAV", + "AUDIO\\ROK1_1A.WAV", + "AUDIO\\ROK1_1B.WAV", + "AUDIO\\ROK1_5.WAV", + "AUDIO\\ROK1_6.WAV", + "AUDIO\\ROK1_7.WAV", + "AUDIO\\ROK1_8.WAV", + "AUDIO\\ROK1_9.WAV", + "AUDIO\\TAX1_1.WAV", + "AUDIO\\TAX1_2.WAV", + "AUDIO\\TAX1_3.WAV", + "AUDIO\\TAX1_4.WAV", + "AUDIO\\TAX1_5.WAV", + "AUDIO\\TAX2_1.WAV", + "AUDIO\\TAX2_2.WAV", + "AUDIO\\TAX2_3.WAV", + "AUDIO\\TAX2_4.WAV", + "AUDIO\\TAX2_5.WAV", + "AUDIO\\TAX2_6.WAV", + "AUDIO\\TAX2_7.WAV", + "AUDIO\\TAX3_1.WAV", + "AUDIO\\TAX3_2.WAV", + "AUDIO\\TAX3_3.WAV", + "AUDIO\\TAX3_4.WAV", + "AUDIO\\TAX3_5.WAV", + "AUDIO\\TEX1_1.WAV", + "AUDIO\\TEX1_2.WAV", + "AUDIO\\TEX1_3.WAV", + "AUDIO\\TEX1_4.WAV", + "AUDIO\\TEX1_5.WAV", + "AUDIO\\TEX1_6.WAV", + "AUDIO\\TEX2_1.WAV", + "AUDIO\\TEX3_1.WAV", + "AUDIO\\TEX3_2.WAV", + "AUDIO\\TEX3_3.WAV", + "AUDIO\\TEX3_4.WAV", + "AUDIO\\TEX3_5.WAV", + "AUDIO\\TEX3_6.WAV", + "AUDIO\\TEX3_7.WAV", + "AUDIO\\TEX3_8.WAV", + "AUDIO\\HAT_1A.WAV", + "AUDIO\\INTRO1.WAV", + "AUDIO\\INTRO2.WAV", + "AUDIO\\INTRO3.WAV", + "AUDIO\\INTRO4.WAV", + "AUDIO\\MOB_01A.WAV", + "AUDIO\\MOB_01B.WAV", + "AUDIO\\MOB_01C.WAV", + "AUDIO\\MOB_02A.WAV", + "AUDIO\\MOB_02B.WAV", + "AUDIO\\MOB_02C.WAV", + "AUDIO\\MOB_03A.WAV", + "AUDIO\\MOB_03B.WAV", + "AUDIO\\MOB_03C.WAV", + "AUDIO\\MOB_03D.WAV", + "AUDIO\\MOB_03E.WAV", + "AUDIO\\SHARK_1.WAV", + "AUDIO\\SHARK_2.WAV", + "AUDIO\\SHARK_3.WAV", + "AUDIO\\SHARK_4.WAV", + "AUDIO\\SHARK_5.WAV", + "AUDIO\\MOB_04A.WAV", + "AUDIO\\MOB_04B.WAV", + "AUDIO\\MOB_04C.WAV", + "AUDIO\\MOB_04D.WAV", + "AUDIO\\MOB_05A.WAV", + "AUDIO\\MOB_05B.WAV", + "AUDIO\\MOB_05C.WAV", + "AUDIO\\MOB_05D.WAV", + "AUDIO\\MOB_06A.WAV", + "AUDIO\\MOB_06B.WAV", + "AUDIO\\MOB_06C.WAV", + "AUDIO\\MOB_07A.WAV", + "AUDIO\\MOB_07B.WAV", + "AUDIO\\MOB_08A.WAV", + "AUDIO\\MOB_08B.WAV", + "AUDIO\\MOB_08C.WAV", + "AUDIO\\MOB_08D.WAV", + "AUDIO\\MOB_08E.WAV", + "AUDIO\\MOB_08F.WAV", + "AUDIO\\MOB_08G.WAV", + "AUDIO\\MOB_09A.WAV", + "AUDIO\\MOB_09B.WAV", + "AUDIO\\MOB_09C.WAV", + "AUDIO\\MOB_09D.WAV", + "AUDIO\\MOB_09E.WAV", + "AUDIO\\MOB_09F.WAV", + "AUDIO\\MOB_10A.WAV", + "AUDIO\\MOB_10B.WAV", + "AUDIO\\MOB_10C.WAV", + "AUDIO\\MOB_10D.WAV", + "AUDIO\\MOB_10E.WAV", + "AUDIO\\MOB_11A.WAV", + "AUDIO\\MOB_11B.WAV", + "AUDIO\\MOB_11C.WAV", + "AUDIO\\MOB_11D.WAV", + "AUDIO\\MOB_11E.WAV", + "AUDIO\\MOB_11F.WAV", + "AUDIO\\MOB_14A.WAV", + "AUDIO\\MOB_14B.WAV", + "AUDIO\\MOB_14C.WAV", + "AUDIO\\MOB_14D.WAV", + "AUDIO\\MOB_14E.WAV", + "AUDIO\\MOB_14F.WAV", + "AUDIO\\MOB_14G.WAV", + "AUDIO\\MOB_14H.WAV", + "AUDIO\\MOB_16A.WAV", + "AUDIO\\MOB_16B.WAV", + "AUDIO\\MOB_16C.WAV", + "AUDIO\\MOB_16D.WAV", + "AUDIO\\MOB_16E.WAV", + "AUDIO\\MOB_16F.WAV", + "AUDIO\\MOB_16G.WAV", + "AUDIO\\MOB_17A.WAV", + "AUDIO\\MOB_17B.WAV", + "AUDIO\\MOB_17C.WAV", + "AUDIO\\MOB_17D.WAV", + "AUDIO\\MOB_17E.WAV", + "AUDIO\\MOB_17G.WAV", + "AUDIO\\MOB_17H.WAV", + "AUDIO\\MOB_17I.WAV", + "AUDIO\\MOB_17J.WAV", + "AUDIO\\MOB_17K.WAV", + "AUDIO\\MOB_17L.WAV", + "AUDIO\\MOB_18A.WAV", + "AUDIO\\MOB_18B.WAV", + "AUDIO\\MOB_18C.WAV", + "AUDIO\\MOB_18D.WAV", + "AUDIO\\MOB_18E.WAV", + "AUDIO\\MOB_18F.WAV", + "AUDIO\\MOB_18G.WAV", + "AUDIO\\MOB_20A.WAV", + "AUDIO\\MOB_20B.WAV", + "AUDIO\\MOB_20C.WAV", + "AUDIO\\MOB_20D.WAV", + "AUDIO\\MOB_20E.WAV", + "AUDIO\\MOB_24A.WAV", + "AUDIO\\MOB_24B.WAV", + "AUDIO\\MOB_24C.WAV", + "AUDIO\\MOB_24D.WAV", + "AUDIO\\MOB_24E.WAV", + "AUDIO\\MOB_24F.WAV", + "AUDIO\\MOB_24G.WAV", + "AUDIO\\MOB_24H.WAV", + "AUDIO\\MOB_25A.WAV", + "AUDIO\\MOB_25B.WAV", + "AUDIO\\MOB_25C.WAV", + "AUDIO\\MOB_25D.WAV", + "AUDIO\\MOB_26A.WAV", + "AUDIO\\MOB_26B.WAV", + "AUDIO\\MOB_26C.WAV", + "AUDIO\\MOB_26D.WAV", + "AUDIO\\MOB_26E.WAV", + "AUDIO\\MOB_29A.WAV", + "AUDIO\\MOB_29B.WAV", + "AUDIO\\MOB_29C.WAV", + "AUDIO\\MOB_29D.WAV", + "AUDIO\\MOB_29E.WAV", + "AUDIO\\MOB_29F.WAV", + "AUDIO\\MOB_29G.WAV", + "AUDIO\\MOB_30A.WAV", + "AUDIO\\MOB_30B.WAV", + "AUDIO\\MOB_30C.WAV", + "AUDIO\\MOB_30D.WAV", + "AUDIO\\MOB_30E.WAV", + "AUDIO\\MOB_30F.WAV", + "AUDIO\\MOB_33A.WAV", + "AUDIO\\MOB_33B.WAV", + "AUDIO\\MOB_33C.WAV", + "AUDIO\\MOB_33D.WAV", + "AUDIO\\MOB_34A.WAV", + "AUDIO\\MOB_34B.WAV", + "AUDIO\\MOB_34C.WAV", + "AUDIO\\MOB_34D.WAV", + "AUDIO\\MOB_35A.WAV", + "AUDIO\\MOB_35B.WAV", + "AUDIO\\MOB_35C.WAV", + "AUDIO\\MOB_35D.WAV", + "AUDIO\\MOB_36A.WAV", + "AUDIO\\MOB_36B.WAV", + "AUDIO\\MOB_36C.WAV", + "AUDIO\\MOB_40A.WAV", + "AUDIO\\MOB_40B.WAV", + "AUDIO\\MOB_40C.WAV", + "AUDIO\\MOB_40D.WAV", + "AUDIO\\MOB_40E.WAV", + "AUDIO\\MOB_40F.WAV", + "AUDIO\\MOB_40G.WAV", + "AUDIO\\MOB_40H.WAV", + "AUDIO\\MOB_40I.WAV", + "AUDIO\\MOB_41A.WAV", + "AUDIO\\MOB_41B.WAV", + "AUDIO\\MOB_41C.WAV", + "AUDIO\\MOB_41D.WAV", + "AUDIO\\MOB_41E.WAV", + "AUDIO\\MOB_41F.WAV", + "AUDIO\\MOB_41G.WAV", + "AUDIO\\MOB_41H.WAV", + "AUDIO\\MOB_42A.WAV", + "AUDIO\\MOB_42B.WAV", + "AUDIO\\MOB_42C.WAV", + "AUDIO\\MOB_42D.WAV", + "AUDIO\\MOB_42E.WAV", + "AUDIO\\MOB_43A.WAV", + "AUDIO\\MOB_43B.WAV", + "AUDIO\\MOB_43C.WAV", + "AUDIO\\MOB_43D.WAV", + "AUDIO\\MOB_43E.WAV", + "AUDIO\\MOB_43F.WAV", + "AUDIO\\MOB_43G.WAV", + "AUDIO\\MOB_43H.WAV", + "AUDIO\\MOB_45A.WAV", + "AUDIO\\MOB_45B.WAV", + "AUDIO\\MOB_45C.WAV", + "AUDIO\\MOB_45D.WAV", + "AUDIO\\MOB_45E.WAV", + "AUDIO\\MOB_45F.WAV", + "AUDIO\\MOB_45G.WAV", + "AUDIO\\MOB_45H.WAV", + "AUDIO\\MOB_45I.WAV", + "AUDIO\\MOB_45J.WAV", + "AUDIO\\MOB_45K.WAV", + "AUDIO\\MOB_45L.WAV", + "AUDIO\\MOB_45M.WAV", + "AUDIO\\MOB_45N.WAV", + "AUDIO\\MOB_46A.WAV", + "AUDIO\\MOB_46B.WAV", + "AUDIO\\MOB_46C.WAV", + "AUDIO\\MOB_46D.WAV", + "AUDIO\\MOB_46E.WAV", + "AUDIO\\MOB_46F.WAV", + "AUDIO\\MOB_46G.WAV", + "AUDIO\\MOB_46H.WAV", + "AUDIO\\MOB_47A.WAV", + "AUDIO\\MOB_52A.WAV", + "AUDIO\\MOB_52B.WAV", + "AUDIO\\MOB_52C.WAV", + "AUDIO\\MOB_52D.WAV", + "AUDIO\\MOB_52E.WAV", + "AUDIO\\MOB_52F.WAV", + "AUDIO\\MOB_52G.WAV", + "AUDIO\\MOB_52H.WAV", + "AUDIO\\MOB_54A.WAV", + "AUDIO\\MOB_54B.WAV", + "AUDIO\\MOB_54C.WAV", + "AUDIO\\MOB_54D.WAV", + "AUDIO\\MOB_54E.WAV", + "AUDIO\\MOB_55A.WAV", + "AUDIO\\MOB_55B.WAV", + "AUDIO\\MOB_55C.WAV", + "AUDIO\\MOB_55D.WAV", + "AUDIO\\MOB_55E.WAV", + "AUDIO\\MOB_55F.WAV", + "AUDIO\\MOB_56A.WAV", + "AUDIO\\MOB_56B.WAV", + "AUDIO\\MOB_56C.WAV", + "AUDIO\\MOB_56D.WAV", + "AUDIO\\MOB_56E.WAV", + "AUDIO\\MOB_56F.WAV", + "AUDIO\\MOB_57A.WAV", + "AUDIO\\MOB_57B.WAV", + "AUDIO\\MOB_57C.WAV", + "AUDIO\\MOB_57D.WAV", + "AUDIO\\MOB_57E.WAV", + "AUDIO\\MOB_58A.WAV", + "AUDIO\\MOB_58B.WAV", + "AUDIO\\MOB_58C.WAV", + "AUDIO\\MOB_58D.WAV", + "AUDIO\\MOB_58E.WAV", + "AUDIO\\MOB_58F.WAV", + "AUDIO\\MOB_58G.WAV", + "AUDIO\\MOB_61A.WAV", + "AUDIO\\MOB_61B.WAV", + "AUDIO\\MOB_62A.WAV", + "AUDIO\\MOB_62B.WAV", + "AUDIO\\MOB_62C.WAV", + "AUDIO\\MOB_62D.WAV", + "AUDIO\\MOB_63A.WAV", + "AUDIO\\MOB_63B.WAV", + "AUDIO\\MOB_63C.WAV", + "AUDIO\\MOB_63D.WAV", + "AUDIO\\MOB_63E.WAV", + "AUDIO\\MOB_63F.WAV", + "AUDIO\\MOB_63G.WAV", + "AUDIO\\MOB_63H.WAV", + "AUDIO\\MOB_63I.WAV", + "AUDIO\\MOB_63J.WAV", + "AUDIO\\MOB_66A.WAV", + "AUDIO\\MOB_66B.WAV", + "AUDIO\\MOB_68A.WAV", + "AUDIO\\MOB_68B.WAV", + "AUDIO\\MOB_68C.WAV", + "AUDIO\\MOB_68D.WAV", + "AUDIO\\MOB_70A.WAV", + "AUDIO\\MOB_70B.WAV", + "AUDIO\\MOB_71A.WAV", + "AUDIO\\MOB_71B.WAV", + "AUDIO\\MOB_71C.WAV", + "AUDIO\\MOB_71D.WAV", + "AUDIO\\MOB_71E.WAV", + "AUDIO\\MOB_71F.WAV", + "AUDIO\\MOB_71G.WAV", + "AUDIO\\MOB_71H.WAV", + "AUDIO\\MOB_71I.WAV", + "AUDIO\\MOB_71J.WAV", + "AUDIO\\MOB_71K.WAV", + "AUDIO\\MOB_71L.WAV", + "AUDIO\\MOB_71M.WAV", + "AUDIO\\MOB_71N.WAV", + "AUDIO\\MOB_72A.WAV", + "AUDIO\\MOB_72B.WAV", + "AUDIO\\MOB_72C.WAV", + "AUDIO\\MOB_72D.WAV", + "AUDIO\\MOB_72E.WAV", + "AUDIO\\MOB_72F.WAV", + "AUDIO\\MOB_72G.WAV", + "AUDIO\\MOB_73A.WAV", + "AUDIO\\MOB_73C.WAV", + "AUDIO\\MOB_73D.WAV", + "AUDIO\\MOB_73F.WAV", + "AUDIO\\MOB_73G.WAV", + "AUDIO\\MOB_73I.WAV", + "AUDIO\\MOB_95A.WAV", + "AUDIO\\MOB_96A.WAV", + "AUDIO\\MOB_98A.WAV", + "AUDIO\\MOB_99A.WAV", + "AUDIO\\JOB1_1B.WAV", + "AUDIO\\JOB1_1C.WAV", + "AUDIO\\JOB1_1D.WAV", + "AUDIO\\JOB2_1B.WAV", + "AUDIO\\JOB2_2.WAV", + "AUDIO\\JOB2_3.WAV", + "AUDIO\\JOB2_4.WAV", + "AUDIO\\JOB2_5.WAV", + "AUDIO\\JOB2_6.WAV", + "AUDIO\\JOB2_7.WAV", + "AUDIO\\JOB2_8.WAV", + "AUDIO\\JOB2_9.WAV", + "AUDIO\\JOB3_1.WAV", + "AUDIO\\JOB3_2.WAV", + "AUDIO\\JOB3_3.WAV", + "AUDIO\\JOB4_1.WAV", + "AUDIO\\JOB4_2.WAV", + "AUDIO\\JOB4_3.WAV", + "AUDIO\\JOB5_1.WAV", + "AUDIO\\JOB5_2.WAV", + "AUDIO\\JOB5_3.WAV", + "AUDIO\\BJM1_20.WAV", + "AUDIO\\BJM1_4.WAV", + "AUDIO\\BJM1_5.WAV", + "AUDIO\\MERC_39.WAV", + "AUDIO\\MONO_1.WAV", + "AUDIO\\MONO_2.WAV", + "AUDIO\\MONO_3.WAV", + "AUDIO\\MONO_4.WAV", + "AUDIO\\MONO_5.WAV", + "AUDIO\\MONO_6.WAV", + "AUDIO\\MONO_7.WAV", + "AUDIO\\MONO_8.WAV", + "AUDIO\\MONO_9.WAV", + "AUDIO\\MONO10.WAV", + "AUDIO\\MONO11.WAV", + "AUDIO\\MONO12.WAV", + "AUDIO\\MONO13.WAV", + "AUDIO\\MONO14.WAV", + "AUDIO\\MONO15.WAV", + "AUDIO\\MONO16.WAV", + "AUDIO\\FUD_01.WAV", + "AUDIO\\FUD_02.WAV", + "AUDIO\\FUD_03.WAV", + "AUDIO\\FUD_04.WAV", + "AUDIO\\FUD_05.WAV", + "AUDIO\\FUD_06.WAV", + "AUDIO\\FUD_07.WAV", + "AUDIO\\FUD_08.WAV", + "AUDIO\\FUD_09.WAV", + "AUDIO\\FUD_10.WAV", + "AUDIO\\FUD_11.WAV", + "AUDIO\\FUD_12.WAV", + "AUDIO\\FUD_13.WAV", + "AUDIO\\FUD_14.WAV", + "AUDIO\\FUD_15.WAV", + "AUDIO\\FUD_16.WAV", + "AUDIO\\FUD_17.WAV", + "AUDIO\\FUD_18.WAV", + "AUDIO\\FUD_19.WAV", + "AUDIO\\FUD_20.WAV", + "AUDIO\\BURG_01.WAV", + "AUDIO\\BURG_02.WAV", + "AUDIO\\BURG_03.WAV", + "AUDIO\\BURG_04.WAV", + "AUDIO\\BURG_05.WAV", + "AUDIO\\BURG_06.WAV", + "AUDIO\\BURG_07.WAV", + "AUDIO\\BURG_08.WAV", + "AUDIO\\BURG_09.WAV", + "AUDIO\\BURG_10.WAV", + "AUDIO\\BURG_11.WAV", + "AUDIO\\BURG_12.WAV", + "AUDIO\\CRUST01.WAV", + "AUDIO\\CRUST02.WAV", + "AUDIO\\CRUST03.WAV", + "AUDIO\\CRUST04.WAV", + "AUDIO\\CRUST05.WAV", + "AUDIO\\CRUST06.WAV", + "AUDIO\\CRUST07.WAV", + "AUDIO\\CRUST08.WAV", + "AUDIO\\CRUST09.WAV", + "AUDIO\\BAND_01.WAV", + "AUDIO\\BAND_02.WAV", + "AUDIO\\BAND_03.WAV", + "AUDIO\\BAND_04.WAV", + "AUDIO\\BAND_05.WAV", + "AUDIO\\BAND_06.WAV", + "AUDIO\\BAND_07.WAV", + "AUDIO\\BAND_08.WAV", + "AUDIO\\SHAFT01.WAV", + "AUDIO\\SHAFT02.WAV", + "AUDIO\\SHAFT03.WAV", + "AUDIO\\SHAFT04.WAV", + "AUDIO\\SHAFT05.WAV", + "AUDIO\\SHAFT06.WAV", + "AUDIO\\SHAFT07.WAV", + "AUDIO\\SHAFT08.WAV", + "AUDIO\\PISS_01.WAV", + "AUDIO\\PISS_02.WAV", + "AUDIO\\PISS_03.WAV", + "AUDIO\\PISS_04.WAV", + "AUDIO\\PISS_05.WAV", + "AUDIO\\PISS_06.WAV", + "AUDIO\\PISS_07.WAV", + "AUDIO\\PISS_08.WAV", + "AUDIO\\PISS_09.WAV", + "AUDIO\\PISS_10.WAV", + "AUDIO\\PISS_11.WAV", + "AUDIO\\PISS_12.WAV", + "AUDIO\\PISS_13.WAV", + "AUDIO\\PISS_14.WAV", + "AUDIO\\PISS_15.WAV", + "AUDIO\\PISS_16.WAV", + "AUDIO\\PISS_17.WAV", + "AUDIO\\PISS_18.WAV", + "AUDIO\\PISS_19.WAV", + "AUDIO\\GIMME01.WAV", + "AUDIO\\GIMME02.WAV", + "AUDIO\\GIMME03.WAV", + "AUDIO\\GIMME04.WAV", + "AUDIO\\GIMME05.WAV", + "AUDIO\\GIMME06.WAV", + "AUDIO\\GIMME07.WAV", + "AUDIO\\GIMME08.WAV", + "AUDIO\\GIMME09.WAV", + "AUDIO\\GIMME10.WAV", + "AUDIO\\GIMME11.WAV", + "AUDIO\\GIMME12.WAV", + "AUDIO\\GIMME13.WAV", + "AUDIO\\GIMME14.WAV", + "AUDIO\\GIMME15.WAV", + "AUDIO\\BUST_01.WAV", + "AUDIO\\BUST_02.WAV", + "AUDIO\\BUST_03.WAV", + "AUDIO\\BUST_04.WAV", + "AUDIO\\BUST_05.WAV", + "AUDIO\\BUST_06.WAV", + "AUDIO\\BUST_07.WAV", + "AUDIO\\BUST_08.WAV", + "AUDIO\\BUST_09.WAV", + "AUDIO\\BUST_10.WAV", + "AUDIO\\BUST_11.WAV", + "AUDIO\\BUST_12.WAV", + "AUDIO\\BUST_13.WAV", + "AUDIO\\BUST_14.WAV", + "AUDIO\\BUST_15.WAV", + "AUDIO\\BUST_16.WAV", + "AUDIO\\BUST_17.WAV", + "AUDIO\\BUST_18.WAV", + "AUDIO\\BUST_19.WAV", + "AUDIO\\BUST_20.WAV", + "AUDIO\\BUST_21.WAV", + "AUDIO\\BUST_22.WAV", + "AUDIO\\BUST_23.WAV", + "AUDIO\\BUST_24.WAV", + "AUDIO\\BUST_25.WAV", + "AUDIO\\BUST_26.WAV", + "AUDIO\\BUST_27.WAV", + "AUDIO\\BUST_28.WAV", +}; +#endif \ No newline at end of file diff --git a/src/miami/audio/sampman_dc.cpp b/src/miami/audio/sampman_dc.cpp new file mode 100644 index 00000000..0f0cad55 --- /dev/null +++ b/src/miami/audio/sampman_dc.cpp @@ -0,0 +1,1479 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "crossplatform.h" + +#if !defined(AUDIO_OAL) && !defined(AUDIO_MSS) +#define verbosef(...) // dbglog(DBG_CRITICAL, __VA_ARGS__) +#define debugf(...) // dbglog(DBG_CRITICAL, __VA_ARGS__) + +#include "sampman.h" +#include "sampman_dc_streams.h" +#include "AudioManager.h" +#include "MusicManager.h" +#include "Frontend.h" +#include "Timer.h" + +#include +#include +#include + +#include +#include + +#include "CdStream.h" + +#define STREAM_STAGING_BUFFER_SIZE 16384 +#define STREAM_STAGING_READ_SIZE_STEREO 16384 +#define STREAM_STAGING_READ_SIZE_MONO (STREAM_STAGING_READ_SIZE_STEREO / 2) +#define STREAM_CHANNEL_BUFFER_SIZE (STREAM_STAGING_READ_SIZE_MONO * 2) // lower and upper halves +#define STREAM_CHANNEL_SAMPLE_COUNT (STREAM_CHANNEL_BUFFER_SIZE * 2) // 4 bit adpcm + +#define SPU_RAM_UNCACHED_BASE_U8 ((uint8_t *)SPU_RAM_UNCACHED_BASE) +// ************************************************************************************************ +// Begin AICA Driver stuff + +#define AICA_MEM_CHANNELS 0x020000 /* 64 * 16*4 = 4K */ + +/* Quick access to the AICA channels */ +#define AICA_CHANNEL(x) (AICA_MEM_CHANNELS + (x) * sizeof(aica_channel_t)) + +int aica_play_chn(int chn, int size, uint32_t aica_buffer, int fmt, int vol, int pan, int loop, int freq) { + // assert(size <= 65534); + // We gotta fix this at some point + if (size >= 65535) { + debugf("aica_play_chn: size too large for %p, %d, truncating to 65534\n", (void*)aica_buffer, size); + size = 65534; + } + + AICA_CMDSTR_CHANNEL(tmp, cmd, chan); + cmd->cmd = AICA_CMD_CHAN; + cmd->timestamp = 0; + cmd->size = AICA_CMDSTR_CHANNEL_SIZE; + cmd->cmd_id = chn; + chan->cmd = AICA_CH_CMD_START; + chan->base = aica_buffer; + chan->type = fmt; + chan->length = size; + chan->loop = loop; + chan->loopstart = 0; + chan->loopend = size; + chan->freq = freq; + chan->vol = vol; + chan->pan = pan; + snd_sh4_to_aica(tmp, cmd->size); + return chn; +} + +void aica_stop_chn(int chn) { + AICA_CMDSTR_CHANNEL(tmp, cmd, chan); + + cmd->cmd = AICA_CMD_CHAN; + cmd->timestamp = 0; + cmd->size = AICA_CMDSTR_CHANNEL_SIZE; + cmd->cmd_id = chn; + chan->cmd = AICA_CH_CMD_STOP; + snd_sh4_to_aica(tmp, cmd->size); +} + +void aica_volpan_chn(int chn, int vol, int pan) { + AICA_CMDSTR_CHANNEL(tmp, cmd, chan); + + cmd->cmd = AICA_CMD_CHAN; + cmd->timestamp = 0; + cmd->size = AICA_CMDSTR_CHANNEL_SIZE; + cmd->cmd_id = chn; + chan->cmd = AICA_CH_CMD_UPDATE | AICA_CH_UPDATE_SET_PAN | AICA_CH_UPDATE_SET_VOL; + chan->vol = vol; + chan->pan = pan; + snd_sh4_to_aica(tmp, cmd->size); +} + + +void aica_snd_sfx_volume(int chn, int vol) { + AICA_CMDSTR_CHANNEL(tmp, cmd, chan); + + cmd->cmd = AICA_CMD_CHAN; + cmd->timestamp = 0; + cmd->size = AICA_CMDSTR_CHANNEL_SIZE; + cmd->cmd_id = chn; + chan->cmd = AICA_CH_CMD_UPDATE | AICA_CH_UPDATE_SET_VOL; + chan->vol = vol; + snd_sh4_to_aica(tmp, cmd->size); +} + +void aica_snd_sfx_pan(int chn, int pan) { + AICA_CMDSTR_CHANNEL(tmp, cmd, chan); + + cmd->cmd = AICA_CMD_CHAN; + cmd->timestamp = 0; + cmd->size = AICA_CMDSTR_CHANNEL_SIZE; + cmd->cmd_id = chn; + chan->cmd = AICA_CH_CMD_UPDATE | AICA_CH_UPDATE_SET_PAN; + chan->pan = pan; + snd_sh4_to_aica(tmp, cmd->size); +} + +void aica_snd_sfx_freq(int chn, int freq) { + AICA_CMDSTR_CHANNEL(tmp, cmd, chan); + + cmd->cmd = AICA_CMD_CHAN; + cmd->timestamp = 0; + cmd->size = AICA_CMDSTR_CHANNEL_SIZE; + cmd->cmd_id = chn; + chan->cmd = AICA_CH_CMD_UPDATE | AICA_CH_UPDATE_SET_FREQ; + chan->freq = freq; + snd_sh4_to_aica(tmp, cmd->size); +} + + +void aica_snd_sfx_freq_vol(int chn, int freq, int vol) { + AICA_CMDSTR_CHANNEL(tmp, cmd, chan); + + cmd->cmd = AICA_CMD_CHAN; + cmd->timestamp = 0; + cmd->size = AICA_CMDSTR_CHANNEL_SIZE; + cmd->cmd_id = chn; + chan->cmd = AICA_CH_CMD_UPDATE | AICA_CH_UPDATE_SET_FREQ | AICA_CH_UPDATE_SET_VOL; + chan->freq = freq; + chan->vol = vol; + snd_sh4_to_aica(tmp, cmd->size); +} + +// End of aica Driver stuff +// ************************************************************************************************ + +cSampleManager SampleManager; +bool8 _bSampmanInitialised = FALSE; + +uint32 BankStartOffset[MAX_SFX_BANKS]; +char SampleBankDescFilename[] = "sfx/sfx_all.dsc"; +char SampleBankDataFilename[] = "sfx/sfx_all.raw"; + +struct sfx_bank { + uintptr_t base; + std::vector effects; + std::vector effects_loop; +}; + +std::map sfx_banks; + +int nPedSlotSfx[MAX_PEDSFX]; +uint32 nPedSlotSfxReqId[MAX_PEDSFX]; +uintptr_t nPedSlotSfxAddr[MAX_PEDSFX]; +uint8_t nCurrentPedSlot; +file_t fdPedSfx; +volatile uint32 nPedSfxReqReadId = 1; +volatile uint32 nPedSfxReqNextId = 1; + +#ifdef FIX_BUGS +uint32 gPlayerTalkSfx = UINT32_MAX; +uintptr_t gPlayerTalkData = 0; +uint32 gPlayerTalkReqId = 0; +#endif + +static int32 DCStreamedLength[TOTAL_STREAMED_SOUNDS]; + +struct WavHeader { + // RIFF Header + char riff[4]; // RIFF Header Magic header + uint32_t chunkSize; // RIFF Chunk Size + char wave[4]; // WAVE Header + // "fmt" sub-chunk + char fmt[4]; // FMT header + uint32_t subchunk1Size; // Size of the fmt chunk + uint16_t audioFormat; // Audio format 1=PCM, other values indicate compression + uint16_t numOfChan; // Number of channels 1=Mono, 2=Stereo + uint32_t samplesPerSec; // Sampling Frequency in Hz + uint32_t bytesPerSec; // bytes per second + uint16_t blockAlign; // 2=16-bit mono, 4=16-bit stereo + uint16_t bitsPerSample; // Number of bits per sample + // "data" sub-chunk + char data[4]; // "data" string + uint32_t dataSize; // Size of the data section +}; + +static inline uint8_t linearlize_volume(uint8_t vol) { + // uint8_t rv = powf(10.0f, (vol - MAX_VOLUME) / 42.0f) * 255; + uint8_t rv = vol * 255 / MAX_VOLUME; + // verbosef("linearlize_volume(%d) = %d\n", vol, rv); + return rv; +} +cSampleManager::cSampleManager(void) +{ + ; +} + +cSampleManager::~cSampleManager(void) +{ + +} + +bool8 +cSampleManager::IsMP3RadioChannelAvailable(void) +{ + return FALSE; +} + + +void cSampleManager::ReleaseDigitalHandle(void) +{ +} + +void cSampleManager::ReacquireDigitalHandle(void) +{ +} + +struct alignas(32) stream_info { + uint8_t buffer[STREAM_STAGING_BUFFER_SIZE + 128]; + std::mutex mtx; + file_t fd; + uint32_t aica_buffers[2]; // left, right + int mapped_ch[2]; // left, right + int rate; + int total_samples; + int played_samples; + int file_offset; + int vol; + uint8_t nPan; + uint8_t pan[2]; + + bool stereo; + bool playing; + bool next_is_upper_half; + bool first_refill; + bool paused; +}; + +stream_info streams[MAX_STREAMS]; + + +std::mutex channel_mtx; +static struct { + uintptr_t ptr; + uintptr_t ptr_loop; // or 0 if no loop + int nSfx; + int freq; + float distMin; + float distMax; + float fX; + float fY; + float fZ; + uint8_t attenuationVol; + uint8_t emittingVol; + uint8_t vol; + uint8_t pan; + char ch; + char mapped_ch; + bool loop; + bool in_hnd_loop; + int8_t nBank; +} channels[MAXCHANNELS+MAX2DCHANNELS]; + + +// static void * fill_audio_cb (snd_stream_hnd_t hnd, int smp_req, int *smp_recv) { +// auto si = (stream_info*)snd_stream_get_userdata(hnd); + +// assert(si->hnd == hnd); + +// verbosef("fill_audio_cb: %p %d\n", si, smp_req); + +// size_t read_bytes = 0; + +// { +// std::lock_guard lk(si->mtx); +// if (si->playing) { +// read_bytes = fs_read(si->fd, si->buffer, smp_req); +// if (read_bytes <= 0) { +// verbosef("fill_audio_cb: %p stream end\n", si); +// *smp_recv = 0; +// return NULL; +// } +// } +// } + +// *smp_recv = read_bytes; + +// if(si->seek_buf > 0) { +// size_t seek_bytes = si->seek_buf; +// si->seek_buf = 0; + +// while(seek_bytes & 3) { +// ++seek_bytes; +// } +// if(seek_bytes > 128) { +// seek_bytes = 128; +// } +// memset(si->buffer + sizeof(si->buffer) - seek_bytes, 0, seek_bytes); +// return si->buffer + seek_bytes; +// } + +// return si->buffer; +// } + +std::thread snd_thread; +bool8 +cSampleManager::Initialise(void) +{ + auto init = snd_init(); + assert(init >= 0); + // snd_stream_init_ex(2, STREAM_BUFFER_SIZE); + + for (int i = 0; i< MAX_STREAMS; i++) { + streams[i].mapped_ch[0] = snd_sfx_chn_alloc(); + streams[i].mapped_ch[1] = snd_sfx_chn_alloc(); + streams[i].aica_buffers[0] = snd_mem_malloc(STREAM_CHANNEL_BUFFER_SIZE); + streams[i].aica_buffers[1] = snd_mem_malloc(STREAM_CHANNEL_BUFFER_SIZE); + debugf("Stream %d mapped to: %d, %d\n", i, streams[i].mapped_ch[0], streams[i].mapped_ch[1]); + debugf("Stream %d buffers: %p, %p\n", i, (void*)streams[i].aica_buffers[0], (void*)streams[i].aica_buffers[1]); + assert(streams[i].mapped_ch[0] != -1); + assert(streams[i].mapped_ch[1] != -1); + streams[i].fd = -1; + + streams[i].vol = 255; + streams[i].nPan = 63; + streams[i].pan[0] = 128; + streams[i].pan[1] = 128; + } + + for (int i = 0; i < (MAXCHANNELS+MAX2DCHANNELS); i++) { + channels[i].mapped_ch = snd_sfx_chn_alloc(); + debugf("Channel %d mapped to %d\n", i, channels[i].mapped_ch); + assert(channels[i].mapped_ch != -1); + } + + if (!InitialiseSampleBanks()) + return FALSE; + + snd_thread = std::thread([]() { + for(;;) { + { + std::lock_guard lk(channel_mtx); + for (int i = 0; i < MAXCHANNELS+MAX2DCHANNELS; i++) { + if (channels[i].ch != -1) { + assert(channels[i].nSfx != -1); + + uint16_t channel_pos = (g2_read_32(SPU_RAM_UNCACHED_BASE + AICA_CHANNEL(channels[i].ch) + offsetof(aica_channel_t, pos)) & 0xffff); + // verbosef("Channel %d pos: %d\n", i, channel_pos); + if (!channels[i].loop) { + auto channel_looped = g2_read_32(SPU_RAM_UNCACHED_BASE + AICA_CHANNEL(channels[i].ch) + offsetof(aica_channel_t, looped)); + // the looped flag is set even for one shots and is a reliable way to know if the channel has finished playing + if (channel_looped) { + debugf("Auto stopping channel: %d -> %d\n", i, channels[i].ch); + channels[i].ch = -1; + } + } else if (channels[i].ptr_loop && !channels[i].in_hnd_loop) { + if (channel_pos >= SampleManager.m_aSamples[channels[i].nSfx].nLoopStartSample) { + channels[i].in_hnd_loop = true; + debugf("Starting loop section: for sfx_%d_loop.wav\n", channels[i].nSfx); + snd_sfx_stop(channels[i].ch); + channels[i].ch = aica_play_chn(channels[i].mapped_ch, SampleManager.m_aSamples[channels[i].nSfx].nLoopByteSize * 2, channels[i].ptr_loop, 2 /* ADPCM */, channels[i].vol, channels[i].pan, 1, channels[i].freq); + } + } + } + } + } + + for (int i = 0; i< MAX_STREAMS; i++) { + size_t do_read = 0; + { + std::lock_guard lk(streams[i].mtx); + if (streams[i].playing) { + // get channel pos + uint32_t channel_pos = g2_read_32(SPU_RAM_UNCACHED_BASE + AICA_CHANNEL(streams[i].mapped_ch[0]) + offsetof(aica_channel_t, pos)) & 0xffff; + uint32_t logical_pos = channel_pos; + if (logical_pos > STREAM_CHANNEL_SAMPLE_COUNT/2) { + logical_pos -= STREAM_CHANNEL_SAMPLE_COUNT/2; + } + verbosef("Stream %d pos: %d, log: %d, rem: %d\n", i, channel_pos, logical_pos, streams[i].played_samples); + + bool can_refill = (streams[i].played_samples + STREAM_CHANNEL_SAMPLE_COUNT/2) < streams[i].total_samples; + bool can_fetch = (streams[i].played_samples + STREAM_CHANNEL_SAMPLE_COUNT/2 + STREAM_CHANNEL_SAMPLE_COUNT/2 + STREAM_CHANNEL_SAMPLE_COUNT/2) < streams[i].total_samples; + // copy over data if needed from staging + if (channel_pos >= STREAM_CHANNEL_SAMPLE_COUNT/2 && !streams[i].next_is_upper_half) { + streams[i].next_is_upper_half = true; + if (can_refill) { // could we need a refill? + verbosef("Filling channel %d with lower half\n", i); + // fill lower half + spu_memload(streams[i].aica_buffers[0], streams[i].buffer, STREAM_CHANNEL_BUFFER_SIZE/2); + if (streams[i].stereo) { + spu_memload(streams[i].aica_buffers[1], streams[i].buffer + STREAM_STAGING_READ_SIZE_MONO, STREAM_CHANNEL_BUFFER_SIZE/2); + } + // queue next read to staging if any + if (can_fetch) { + do_read = streams[i].stereo ? STREAM_STAGING_READ_SIZE_STEREO : STREAM_STAGING_READ_SIZE_MONO; + } + } + assert(streams[i].first_refill == false); + streams[i].played_samples += STREAM_CHANNEL_SAMPLE_COUNT/2; + } else if (channel_pos < STREAM_CHANNEL_SAMPLE_COUNT/2 && streams[i].next_is_upper_half) { + streams[i].next_is_upper_half = false; + if (can_refill) { // could we need a refill? + verbosef("Filling channel %d with upper half\n", i); + // fill upper half + spu_memload(streams[i].aica_buffers[0] + STREAM_CHANNEL_BUFFER_SIZE/2, streams[i].buffer, STREAM_CHANNEL_BUFFER_SIZE/2); + if (streams[i].stereo) { + spu_memload(streams[i].aica_buffers[1] + STREAM_CHANNEL_BUFFER_SIZE/2, streams[i].buffer + STREAM_STAGING_READ_SIZE_MONO, STREAM_CHANNEL_BUFFER_SIZE/2); + } + // queue next read to staging, if any + if (can_fetch) { + do_read = streams[i].stereo ? STREAM_STAGING_READ_SIZE_STEREO : STREAM_STAGING_READ_SIZE_MONO; + } + } + if (streams[i].first_refill) { + streams[i].first_refill = false; + } else { + streams[i].played_samples += STREAM_CHANNEL_SAMPLE_COUNT/2; + } + } + // if end of file, stop + if ((streams[i].played_samples + logical_pos) > streams[i].total_samples) { + // stop channel + debugf("Auto stopping stream: %d -> {%d, %d}, %d total\n", i, streams[i].mapped_ch[0], streams[i].mapped_ch[1], streams[i].total_samples); + aica_stop_chn(streams[i].mapped_ch[0]); + aica_stop_chn(streams[i].mapped_ch[1]); + streams[i].playing = false; + } + } + + if (do_read) { + debugf("Queueing stream read: %d, file: %d, buffer: %p, size: %d, tell: %d\n", i, streams[i].fd, streams[i].buffer, do_read, fs_tell(streams[i].fd)); + CdStreamQueueAudioRead(streams[i].fd, streams[i].buffer, do_read, streams[i].file_offset); + streams[i].file_offset += do_read; + } + } + } + thd_sleep(50); + } + }); + + nPedSfxReqNextId = 1; + nPedSfxReqReadId = 1; + for ( int32 i = 0; i < MAX_PEDSFX; i++ ) + { + nPedSlotSfx[i] = -1; + nPedSlotSfxReqId[i] = 0; + nPedSlotSfxAddr[i] = snd_mem_malloc(PED_BLOCKSIZE_ADPCM); + debugf("PedSlot %d buffer: %p\n", i, (void*)nPedSlotSfxAddr[i]); + } + + nCurrentPedSlot = 0; + + fdPedSfx = fs_open(SampleBankDataFilename, O_RDONLY); + + assert(fdPedSfx >= 0); + + file_t fd = fs_open("stream/hdr.bin", O_RDONLY); + assert(fd >= 0); + static_assert(sizeof(DCStreamedLength) == TOTAL_STREAMED_SOUNDS*sizeof(int32)); + assert(fs_read(fd, DCStreamedLength, sizeof(DCStreamedLength)) == sizeof(DCStreamedLength)); + fs_close(fd); + + _bSampmanInitialised = true; + return TRUE; +} + +void +cSampleManager::Terminate(void) +{ + fs_close(fdPedSfx); +} + +bool8 cSampleManager::CheckForAnAudioFileOnCD(void) +{ + return TRUE; +} + +char cSampleManager::GetCDAudioDriveLetter(void) +{ + return '\0'; +} + +void +cSampleManager::UpdateEffectsVolume(void) +{ + if(_bSampmanInitialised) { + std::lock_guard lk(channel_mtx); + for (int i = 0; i < MAXCHANNELS+MAX2DCHANNELS; i++) { + if (channels[i].ch != -1) { + UpdateChannelVolume(i); + } + } + } +} + + +void +cSampleManager::SetEffectsMasterVolume(uint8 nVolume) +{ + m_nEffectsVolume = nVolume; + UpdateEffectsVolume(); +} + +void +cSampleManager::SetMusicMasterVolume(uint8 nVolume) +{ + m_nMusicVolume = nVolume; +} + +void +cSampleManager::SetMP3BoostVolume(uint8 nVolume) +{ + // m_nMP3BoostVolume = nVolume; +} + +void +cSampleManager::SetEffectsFadeVolume(uint8 nVolume) +{ + m_nEffectsFadeVolume = nVolume; + UpdateEffectsVolume(); +} + +void +cSampleManager::SetMusicFadeVolume(uint8 nVolume) +{ + m_nMusicFadeVolume = nVolume; +} + +void +cSampleManager::SetMonoMode(uint8 nMode) +{ +} + +bool8 +cSampleManager::LoadSampleBank(uint8 nBank) +{ + ASSERT( nBank < MAX_SFX_BANKS ); + ASSERT( nBank != SFX_BANK_PED_COMMENTS ); + verbosef("LoadSampleBank(%d)\n", nBank); + + auto it = sfx_banks.find(nBank); + + if (it == sfx_banks.end()) { + debugf("Loading bank %d\n", nBank); + sfx_bank bank; + int firstSfx = BankStartOffset[nBank]; + int nextBankSfx = BankStartOffset[nBank+1]; + assert(firstSfx= 0); + // this is very wasteful and temporary + void* stagingBuffer = memalign(32, 8 * 2048); + assert(stagingBuffer != 0); + + // Ideally, we'd suspend the CdStream thingy here or read via that instead + uintptr_t loadOffset = bank.base; + fs_seek(fd, fileStart, SEEK_SET); + + while (fileSize > 0) { + size_t readSize = fileSize > 8 * 2048 ? 8 * 2048 : fileSize; + int rs = fs_read(fd, stagingBuffer, readSize); + debugf("Read %d bytes, expected %d\n", rs, readSize); + assert(rs == readSize); + spu_memload(loadOffset, stagingBuffer, readSize); + loadOffset += readSize; + fileSize -= readSize; + debugf("Loaded %d bytes, %d remaining\n", readSize, fileSize); + } + fs_close(fd); + free(stagingBuffer); + + + for (int nSfx = BankStartOffset[nBank]; nSfx < BankStartOffset[nBank+1]; nSfx++) { + bank.effects.push_back(m_aSamples[nSfx].nFileOffset - fileStart + bank.base); + if (m_aSamples[nSfx].nLoopStartSample) { + bank.effects_loop.push_back(m_aSamples[nSfx].nLoopFileOffset - fileStart + bank.base); + } else { + bank.effects_loop.push_back(0); + } + debugf("Loaded sfx %d at %p, loop at %p\n", nSfx, (void*)bank.effects.back(), (void*)bank.effects_loop.back()); + } + + debugf("Loaded bank %d\n", nBank); + sfx_banks[nBank] = bank; + } + return TRUE; +} + +void +cSampleManager::UnloadUnusedSampleBank() +{ + assert(--sfx_banks.end() != sfx_banks.end()); + auto bank = (--sfx_banks.end())->first; + assert(bank != SFX_BANK_0); // can't unload bank 0 for OOM + UnloadSampleBank(bank); +} + +void +cSampleManager::UnloadSampleBank(uint8 nBank) +{ + ASSERT( nBank < MAX_SFX_BANKS ); + + verbosef("UnloadSampleBank(%d)\n", nBank); + + auto it = sfx_banks.find(nBank); + if (it != sfx_banks.end()) { + debugf("Unloading bank %d\n", nBank); + { + std::lock_guard lk(channel_mtx); + for (int i = 0; i < MAXCHANNELS+MAX2DCHANNELS; i++) { + if (channels[i].nBank == nBank) { + dbglog(DBG_CRITICAL, "Warning: Channel %d is in unloaded nBank %d\n", i, nBank); + if (channels[i].ch != -1) { + dbglog(DBG_CRITICAL, "Warning: Channel %d was activelly playing from bank %d\n", i, nBank); + snd_sfx_stop(channels[i].ch); + } + channels[i].ch = -1; + channels[i].nSfx = -1; + channels[i].nBank = -1; + channels[i].ptr = 0; + channels[i].ptr_loop = 0; + } + } + } + snd_mem_free(it->second.base); + sfx_banks.erase(it); + } +} + +int8 +cSampleManager::IsSampleBankLoaded(uint8 nBank) +{ + ASSERT( nBank < MAX_SFX_BANKS ); + + return LOADING_STATUS_LOADED; +} + +#ifdef FIX_BUGS +uint8 +cSampleManager::IsMissionAudioLoaded(uint8 nSlot, uint32 nSample) +{ + ASSERT(nSlot == MISSION_AUDIO_PLAYER_COMMENT); // only MISSION_AUDIO_PLAYER_COMMENT is supported on PC + + if (nSample == gPlayerTalkSfx) + return gPlayerTalkReqId <= nPedSfxReqReadId ? LOADING_STATUS_LOADED : LOADING_STATUS_LOADING; + else + return LOADING_STATUS_NOT_LOADED; +} + +bool8 +cSampleManager::LoadMissionAudio(uint8 nSlot, uint32 nSample) +{ + ASSERT(nSlot == MISSION_AUDIO_PLAYER_COMMENT); // only MISSION_AUDIO_PLAYER_COMMENT is supported on PC + ASSERT(nSample < TOTAL_AUDIO_SAMPLES); + + debugf("Loading mission audio comment %d, offset: %d, size: %d\n", nSample, m_aSamples[nSample].nFileOffset, m_aSamples[nSample].nByteSize); + CdStreamQueueAudioRead(nSample, (void*)gPlayerTalkData, m_aSamples[nSample].nByteSize, m_aSamples[nSample].nFileOffset, [](AudioReadCmd* cmd) { + fs_seek(fdPedSfx, cmd->seek, SEEK_SET); + + // TODO: When we can dma directly to AICA, we can use this instead + // fs_read(fdPedSfx, SPU_BASE_U8 + (uintptr_t)cmd->dest, cmd->size); + + void* stagingBuffer = memalign(32, cmd->size); + assert(stagingBuffer != 0); + debugf("Allocated %d bytes at %p\n", cmd->size, stagingBuffer); + int rs = fs_read(fdPedSfx, stagingBuffer, cmd->size); + debugf("Read %d bytes, expected %d\n", rs, cmd->size); + assert(rs == cmd->size); + + spu_memload((uintptr_t)cmd->dest, stagingBuffer, cmd->size); + free(stagingBuffer); + nPedSfxReqReadId = nPedSfxReqReadId + 1; + }); + + gPlayerTalkReqId = ++nPedSfxReqNextId; + + gPlayerTalkSfx = nSample; + + return TRUE; +} +#endif + +uint8 +cSampleManager::IsPedCommentLoaded(uint32 nComment) +{ + int8 slot; + + for ( int32 i = 0; i < _TODOCONST(3); i++ ) + { + slot = nCurrentPedSlot - i - 1; +#ifdef FIX_BUGS + if (slot < 0) + slot += ARRAY_SIZE(nPedSlotSfx); +#endif + if ( nComment == nPedSlotSfx[slot] ) + return nPedSlotSfxReqId[slot] <= nPedSfxReqReadId ? LOADING_STATUS_LOADED : LOADING_STATUS_LOADING; + } + + return LOADING_STATUS_NOT_LOADED; +} + + +int32 +cSampleManager::_GetPedCommentSlot(uint32 nComment) +{ + int8 slot; + + for ( int32 i = 0; i < _TODOCONST(3); i++ ) + { + slot = nCurrentPedSlot - i - 1; +#ifdef FIX_BUGS + if (slot < 0) + slot += ARRAY_SIZE(nPedSlotSfx); +#endif + if ( nComment == nPedSlotSfx[slot] ) + return slot; + } + + return -1; +} + +bool8 +cSampleManager::LoadPedComment(uint32 nComment) +{ + verbosef("LoadPedComment %ld\n", nComment); + + ASSERT( nComment < TOTAL_AUDIO_SAMPLES ); + ASSERT (nComment >= SAMPLEBANK_PED_START && nComment < SAMPLEBANK_PED_MAX); + + if ( CTimer::GetIsCodePaused() ) + return FALSE; + + // no talking peds during cutsenes or the game end + if ( MusicManager.IsInitialised() ) + { + switch ( MusicManager.GetMusicMode() ) + { + case MUSICMODE_CUTSCENE: + { + return FALSE; + + break; + } + } + } + + assert(m_aSamples[nComment].nByteSize < PED_BLOCKSIZE_ADPCM); + + debugf("Loading ped comment %d, offset: %d, size: %d\n", nComment, m_aSamples[nComment].nFileOffset, m_aSamples[nComment].nByteSize); + CdStreamQueueAudioRead(nComment, (void*)nPedSlotSfxAddr[nCurrentPedSlot], m_aSamples[nComment].nByteSize, m_aSamples[nComment].nFileOffset, [](AudioReadCmd* cmd) { + fs_seek(fdPedSfx, cmd->seek, SEEK_SET); + + + // TODO: When we can dma directly to AICA, we can use this instead + // fs_read(fdPedSfx, SPU_BASE_U8 + (uintptr_t)cmd->dest, cmd->size); + + void* stagingBuffer = memalign(32, cmd->size); + assert(stagingBuffer != 0); + debugf("Allocated %d bytes at %p\n", cmd->size, stagingBuffer); + int rs = fs_read(fdPedSfx, stagingBuffer, cmd->size); + debugf("Read %d bytes, expected %d\n", rs, cmd->size); + assert(rs == cmd->size); + + spu_memload((uintptr_t)cmd->dest, stagingBuffer, cmd->size); + free(stagingBuffer); + nPedSfxReqReadId = nPedSfxReqReadId + 1; + }); + + nPedSlotSfxReqId[nCurrentPedSlot] = ++nPedSfxReqNextId; + + nPedSlotSfx[nCurrentPedSlot] = nComment; + + if ( ++nCurrentPedSlot >= MAX_PEDSFX ) + nCurrentPedSlot = 0; + + return TRUE; +} + +int32 +cSampleManager::GetBankContainingSound(uint32 nSfx) +{ + assert(nSfx < TOTAL_AUDIO_SAMPLES); + + for (int i = MAX_SFX_BANKS-1; i >= 0; i--) + { + if ( nSfx >= BankStartOffset[i] ) + return i; + } + + return INVALID_SFX_BANK; +} + +uint32 +cSampleManager::GetSampleBaseFrequency(uint32 nSample) +{ + ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); + return m_aSamples[nSample].nFrequency; +} + +// now in samples +uint32 +cSampleManager::GetSampleLoopStartOffset(uint32 nSample) +{ + ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); + return m_aSamples[nSample].nLoopStartSample; +} + +// always loop to end +int32 +cSampleManager::GetSampleLoopEndOffset(uint32 nSample) +{ + ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); + return -1; +} + +uint32 +cSampleManager::GetSampleLength(uint32 nSample) +{ + ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); + return m_aSamples[nSample].nByteSize * 2; // adcpcm is 4 bit +} + +bool8 cSampleManager::UpdateReverb(void) +{ + return FALSE; +} + +void +cSampleManager::SetChannelReverbFlag(uint32 nChannel, bool8 nReverbFlag) +{ + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); +} + +bool8 +cSampleManager::InitialiseChannel(uint32 nChannel, uint32 nSfx, uint8 nBank) +{ + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); + // printf("InitialiseChannel(nChannel: %d, nSfx: %d, nBank: %d)\n", nChannel, nSfx, nBank); + StopChannel(nChannel); + nBank = GetBankContainingSound(nSfx); + if (nBank != SFX_BANK_PED_COMMENTS) { + // Load the bank here, without holding channel_mtx as it may call UnloadSampleBank that locks it + LoadSampleBank(nBank); + } + + std::lock_guard lk(channel_mtx); + + verbosef("InitialiseChannel %ld %ld %d\n", nChannel, nSfx, nBank); + + if (nBank == SFX_BANK_PED_COMMENTS) { + +#ifdef FIX_BUGS + if ( nSfx >= PLAYER_COMMENTS_START && nSfx <= PLAYER_COMMENTS_END ) + { + if ( IsMissionAudioLoaded(MISSION_AUDIO_PLAYER_COMMENT, nSfx) != LOADING_STATUS_LOADED ) + return FALSE; + + debugf("Channel %d is using mission audio comment %d, buffer %p, samples: %d\n", nChannel, nSfx, channels[nChannel].ptr, m_aSamples[nSfx].nByteSize*2); + + channels[nChannel].ptr = gPlayerTalkData; + } else +#endif + { + int32 i; + for ( i = 0; i < _TODOCONST(3); i++ ) + { + int32 slot = nCurrentPedSlot - i - 1; + #ifdef FIX_BUGS + if (slot < 0) + slot += ARRAY_SIZE(nPedSlotSfx); + #endif + if ( nSfx == nPedSlotSfx[slot] ) + { + channels[nChannel].ptr = nPedSlotSfxAddr[slot]; + break; + } + } + + if (i == _TODOCONST(3)) + return FALSE; + debugf("Channel %d is using ped comment %d, buffer %p, samples: %d\n", nChannel, nSfx, channels[nChannel].ptr, m_aSamples[nSfx].nByteSize*2); + } + } else { + channels[nChannel].ptr = sfx_banks[nBank].effects[nSfx - BankStartOffset[nBank]]; + } + + channels[nChannel].nSfx = nSfx; + channels[nChannel].nBank = nBank; + + if (m_aSamples[nSfx].nLoopStartSample != 0) { + channels[nChannel].ptr_loop = sfx_banks[nBank].effects_loop[nSfx - BankStartOffset[nBank]]; + } else { + channels[nChannel].ptr_loop = 0; + } + + channels[nChannel].freq = m_aSamples[nSfx].nFrequency; + channels[nChannel].attenuationVol = 255; + // channels[nChannel].vol = 255; + // channels[nChannel].pan = 128; + channels[nChannel].loop = 0; + + return TRUE; +} + +void cSampleManager::UpdateChannelVolume(uint32 nChannel) { + + auto newVol = channels[nChannel].emittingVol * channels[nChannel].attenuationVol / 255; + newVol = m_nEffectsFadeVolume * newVol * m_nEffectsVolume >> 14; + // newVol = 255; + // printf("updateVol(nChannel: %d) vol: %d, newVol: %d\n", nChannel, channels[nChannel].vol, newVol); + if (channels[nChannel].vol != newVol) { + channels[nChannel].vol = newVol; + // printf("updateVol(nChannel: %d) vol: %d\n", nChannel, channels[nChannel].vol); + if (channels[nChannel].ch != -1) { + aica_snd_sfx_volume(channels[nChannel].ch, channels[nChannel].vol); + } + } +} + +void +cSampleManager::SetChannelVolume(uint32 nChannel, uint32 nVolume) +{ + // ASSERT( nChannel >= MAXCHANNELS ); + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); + if (nVolume > MAX_VOLUME) + nVolume = MAX_VOLUME; + std::lock_guard lk(channel_mtx); + channels[nChannel].emittingVol = linearlize_volume(nVolume);// nVolume * 255 / MAX_VOLUME; + channels[nChannel].attenuationVol = 255; + + UpdateChannelVolume(nChannel); + verbosef("SetChannelVolume(nChannel: %d) vol: %d\n", nChannel, nVolume); +} + +void +cSampleManager::SetChannelPan(uint32 nChannel, uint32 gta_pan) +{ + // ASSERT( nChannel >= MAXCHANNELS ); + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); + std::lock_guard lk(channel_mtx); + // DC uses logarithmic pan + auto linear_gain = 1-abs(gta_pan-64.0f)/64.0f; + + auto db = 20.0f * std::log10(linear_gain); + if (db < -45) { + db = -45; + } + + db = fabs(db); + auto aica_paned_att = db / 0.355; + + int aica_pan; + + if (gta_pan < 64) { + aica_pan = 128 - aica_paned_att; + } else { + aica_pan = 128 + aica_paned_att; + } + + // nPan = nPan * 2; // 64 is center, for dc it is 128 + if (channels[nChannel].pan != aica_pan) { + channels[nChannel].pan = aica_pan; + if (channels[nChannel].ch != -1) { + aica_snd_sfx_pan(channels[nChannel].ch, channels[nChannel].pan); + } + } + // printf("SetChannelPan(nChannel: %d) pan: %d\n", nChannel, nPan); +} + +void +cSampleManager::SetChannelFrequency(uint32 nChannel, uint32 nFreq) +{ + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); + // printf("SetChannelFrequency(nChannel: %d) freq: %d\n", nChannel, nFreq); + std::lock_guard lk(channel_mtx); + if (channels[nChannel].freq != nFreq) { + channels[nChannel].freq = nFreq; + if (channels[nChannel].ch != -1) { + aica_snd_sfx_freq(channels[nChannel].ch, channels[nChannel].freq); + } + } +} + +void +cSampleManager::SetChannelLoopPoints(uint32 nChannel, uint32 nLoopStart, int32 nLoopEnd) +{ + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); + std::lock_guard lk(channel_mtx); + assert(channels[nChannel].nSfx != -1); + assert(m_aSamples[channels[nChannel].nSfx].nLoopStartSample == nLoopStart && -1 == nLoopEnd); +} + +void +cSampleManager::SetChannelLoopCount(uint32 nChannel, uint32 nLoopCount) +{ + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); + std::lock_guard lk(channel_mtx); + assert(nLoopCount == 0 || nLoopCount == 1); + channels[nChannel].loop = nLoopCount == 0; + // printf("SetChannelLoopCount(nChannel: %d) loop: %d\n", nChannel, nLoopCount); +} + +bool8 +cSampleManager::GetChannelUsedFlag(uint32 nChannel) +{ + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); + std::lock_guard lk(channel_mtx); + return channels[nChannel].ch != -1; +} + +void +cSampleManager::StartChannel(uint32 nChannel) +{ + StopChannel(nChannel); + std::lock_guard lk(channel_mtx); + // printf("StartChannel(nChannel: %d) vol: %d, pan: %d, freq: %d, loop: %d, sfx: %d\n", nChannel, channels[nChannel].vol, channels[nChannel].pan, channels[nChannel].freq, channels[nChannel].loop, channels[nChannel].nSfx); + channels[nChannel].in_hnd_loop = false; + assert(channels[nChannel].nSfx != -1); + channels[nChannel].ch = aica_play_chn( + channels[nChannel].mapped_ch, + m_aSamples[channels[nChannel].nSfx].nByteSize * 2, + channels[nChannel].ptr, + 2 /* ADPCM */, + channels[nChannel].vol, + channels[nChannel].pan, + channels[nChannel].loop, + channels[nChannel].freq + ); + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); +} + +void +cSampleManager::StopChannel(uint32 nChannel) +{ + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); + std::lock_guard lk(channel_mtx); + // printf("StopChannel(nChannel: %d)\n", nChannel); + if (channels[nChannel].ch != -1) { + snd_sfx_stop(channels[nChannel].ch); + channels[nChannel].ch = -1; + } +} + +void +cSampleManager::PreloadStreamedFile(uint32 nFile, uint8 nStream, uint32_t seek_bytes_aligned) +{ + ASSERT( nStream < MAX_STREAMS ); + ASSERT( nFile < TOTAL_STREAMED_SOUNDS ); + + file_t f = fs_open(DCStreamedNameTable[nFile], O_RDONLY); + debugf("PreloadStreamedFile(%p, %d, %d) is %s\n", f, nFile, nStream, DCStreamedNameTable[nFile]); + assert(f >= 0 ); + WavHeader hdr; + assert(fs_read(f, &hdr, sizeof(hdr)) == sizeof(hdr)); + + { + std::lock_guard lk(streams[nStream].mtx); + + // Stop if playing + // Keep in sync with StopStreamedFile + + if (streams[nStream].playing) { + streams[nStream].playing = false; + aica_stop_chn(streams[nStream].mapped_ch[0]); + aica_stop_chn(streams[nStream].mapped_ch[1]); + } + + if (streams[nStream].fd >= 0) { + CdStreamDiscardAudioRead(streams[nStream].fd); + fs_close(streams[nStream].fd); + } + streams[nStream].fd = -1; + + streams[nStream].rate = hdr.samplesPerSec; + streams[nStream].stereo = hdr.numOfChan == 2; + streams[nStream].fd = f; + streams[nStream].playing = false; + streams[nStream].total_samples = hdr.dataSize * 2 / hdr.numOfChan; + streams[nStream].played_samples = 0; + streams[nStream].next_is_upper_half = true; + streams[nStream].first_refill = true; + + debugf("PreloadStreamedFile: %s: stream: %d, freq: %d, chans: %d, byte size: %d, played samples: %d\n", DCStreamedNameTable[nFile], nStream, hdr.samplesPerSec, hdr.numOfChan, hdr.dataSize, streams[nStream].played_samples); + + + // How to avoid the lock? + if (seek_bytes_aligned) { + streams[nStream].played_samples = seek_bytes_aligned * (streams[nStream].stereo ? 1 : 2); + debugf("Seeking aligned to: %d, played_samples: %d\n", seek_bytes_aligned, streams[nStream].played_samples); + fs_seek(streams[nStream].fd, 2048 + seek_bytes_aligned, SEEK_SET); + } else { + fs_seek(f, 2048, SEEK_SET); + } + + #if 0 + // Read directly in the future + fs_read(f, SPU_RAM_UNCACHED_BASE_U8 + streams[nStream].aica_buffers[0], STREAM_STAGING_READ_SIZE_MONO); + if (streams[nStream].stereo) { + fs_read(f, SPU_RAM_UNCACHED_BASE_U8 + streams[nStream].aica_buffers[1], STREAM_STAGING_READ_SIZE_MONO); + } + #else + // Stage to memory + fs_read(f, streams[nStream].buffer, streams[nStream].stereo ? STREAM_STAGING_READ_SIZE_STEREO : STREAM_STAGING_READ_SIZE_MONO); + spu_memload(streams[nStream].aica_buffers[0], streams[nStream].buffer, STREAM_CHANNEL_BUFFER_SIZE/2); + if (streams[nStream].stereo) { + spu_memload(streams[nStream].aica_buffers[1], streams[nStream].buffer + STREAM_STAGING_READ_SIZE_MONO, STREAM_CHANNEL_BUFFER_SIZE/2); + } + #endif + + if (streams[nStream].total_samples > STREAM_CHANNEL_SAMPLE_COUNT/2) { + // If more than one buffer, prefetch the next one + fs_read(f, streams[nStream].buffer, streams[nStream].stereo ? STREAM_STAGING_READ_SIZE_STEREO : STREAM_STAGING_READ_SIZE_MONO); + } + + streams[nStream].file_offset = fs_tell(f); + } + + verbosef("PreloadStreamedFile: %p %d - %s, %d, %d, \n", f, nFile, DCStreamedNameTable[nFile], streams[nStream].rate, streams[nStream].stereo); +} + +// we can't really pause a stream, so we just make it go very slow with zero volume +void +cSampleManager::PauseStream(bool8 nPauseFlag, uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); + { + std::lock_guard lk(streams[nStream].mtx); + + if (nPauseFlag != streams[nStream].paused) { + streams[nStream].paused = nPauseFlag; + if(nPauseFlag) { + // rate of 0 is 172 samples/second. not ideal but gets the job done. + aica_snd_sfx_freq_vol(streams[nStream].mapped_ch[0], 0, 0); + aica_snd_sfx_freq_vol(streams[nStream].mapped_ch[1], 0, 0); + } else { + aica_snd_sfx_freq_vol(streams[nStream].mapped_ch[0], streams[nStream].rate, streams[nStream].vol); + aica_snd_sfx_freq_vol(streams[nStream].mapped_ch[1], streams[nStream].rate, streams[nStream].vol); + } + } + } +} + +void +cSampleManager::StartPreloadedStreamedFile(uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); + debugf("StartPreloadedStreamedFile(%d)\n", nStream); + std::lock_guard lk(streams[nStream].mtx); + if (streams[nStream].playing) { + return; + } + debugf("StartPreloadedStreamedFile(%d) - actually starting stream\n", nStream); + // int aica_play_chn(int chn, int size, uint32_t aica_buffer, int fmt, int vol, int pan, int loop, int freq) + aica_play_chn( + streams[nStream].mapped_ch[0], + STREAM_CHANNEL_SAMPLE_COUNT, + streams[nStream].aica_buffers[0], + 3 /* adpcm long stream */, + streams[nStream].vol, + streams[nStream].pan[0], + 1, + streams[nStream].rate + ); + + aica_play_chn( + streams[nStream].mapped_ch[1], + STREAM_CHANNEL_SAMPLE_COUNT, + streams[nStream].aica_buffers[streams[nStream].stereo ? 1 : 0], + 3 /* adpcm long stream */, + streams[nStream].vol, + streams[nStream].pan[1], + 1, + streams[nStream].rate + ); + + streams[nStream].playing = true; + // printf("StartPreloadedStreamedFile(%d)\n", nStream); + // { + // std::lock_guard lk(streams[nStream].mtx); + // streams[nStream].playing = true; + // if (streams[nStream].active) { + // snd_stream_stop(streams[nStream].hnd); + // } + // streams[nStream].active = true; + // } + // snd_stream_start_adpcm(streams[nStream].hnd, streams[nStream].rate, streams[nStream].stereo); + // snd_stream_volume(streams[nStream].hnd, streams[nStream].vol); + verbosef("StartPreloadedStreamedFile(%d)\n", nStream); +} + +bool8 +cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); + debugf("StartStreamedFile(%d, %d, %d)\n", nFile, nPos, nStream); + uint32_t seek_aligned = 0; + if (nPos) { + uint64_t seek_bytes = (uint64_t)nPos * streams[nStream].rate / (streams[nStream].stereo ? 1000: 2000); + assert(seek_bytes <= INT32_MAX); + seek_aligned = seek_bytes & ~(streams[nStream].stereo ? (STREAM_STAGING_READ_SIZE_STEREO-1) : (STREAM_STAGING_READ_SIZE_MONO-1)); + } + PreloadStreamedFile(nFile, nStream, seek_aligned); + StartPreloadedStreamedFile(nStream); + return TRUE; +} + +void +cSampleManager::StopStreamedFile(uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); + verbosef("StopStreamedFile(%d)\n", nStream); + std::lock_guard lk(streams[nStream].mtx); + + // Keep in sync with PreloadStreamedFile + streams[nStream].playing = false; + + aica_stop_chn(streams[nStream].mapped_ch[0]); + aica_stop_chn(streams[nStream].mapped_ch[1]); + + if (streams[nStream].fd >= 0) { + CdStreamDiscardAudioRead(streams[nStream].fd); + fs_close(streams[nStream].fd); + } + streams[nStream].fd = -1; +} + +int32 +cSampleManager::GetStreamedFilePosition(uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); + int32 rv; + + if (streams[nStream].fd >= 0) { + int64_t rv64 = (int64_t)streams[nStream].played_samples * 1000 / streams[nStream].rate; + assert(rv64 <= INT32_MAX); + rv = (int32)rv64; + } else { + rv = 0; + } + + debugf("GetStreamedFilePosition: %d %d\n", nStream, rv); + return rv; +} + +void +cSampleManager::SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, uint8 nEffectFlag, uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); + if (nVolume > MAX_VOLUME) + nVolume = MAX_VOLUME; + nVolume = linearlize_volume(nVolume); //nVolume * 255 / MAX_VOLUME; + nVolume = m_nMusicFadeVolume * nVolume * m_nMusicVolume >> 14; + if (streams[nStream].vol != nVolume || streams[nStream].nPan != nPan) { + streams[nStream].vol = nVolume; + streams[nStream].nPan = nPan; + + auto lpan = (int8_t)nPan - 63; + lpan = lpan < 0 ? 0 : lpan > 63 ? 63 : lpan; + + auto rpan = (int8_t)nPan + 63; + rpan = rpan < 64 ? 64 : rpan > 127 ? 127 : rpan; + + streams[nStream].pan[0] = lpan * 2; + streams[nStream].pan[1] = rpan * 2; + aica_volpan_chn(streams[nStream].mapped_ch[0], streams[nStream].vol, streams[nStream].pan[0]); + aica_volpan_chn(streams[nStream].mapped_ch[1], streams[nStream].vol, streams[nStream].pan[1]); + } + // verbosef("SetStreamedVolumeAndPan: %d %d %d %d\n", nStream, nVolume, nPan, nEffectFlag); +} + +int32 +cSampleManager::GetStreamedFileLength(uint8 nFile) +{ + ASSERT( nFile < TOTAL_STREAMED_SOUNDS ); + + auto rv = DCStreamedLength[nFile]; + + debugf("GetStreamedFileLength: %d %d\n", nFile, rv); + + return rv; +} + +bool8 +cSampleManager::IsStreamPlaying(uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); + std::lock_guard lk(streams[nStream].mtx); + return streams[nStream].playing && !streams[nStream].paused; +} + +bool8 +cSampleManager::InitialiseSampleBanks(void) +{ + file_t fd = fs_open(SampleBankDescFilename, O_RDONLY); + if (fd < 0) + return FALSE; + + size_t rs = fs_read(fd, m_aSamples, sizeof(tSample)*TOTAL_AUDIO_SAMPLES); + + if (rs != sizeof(tSample)*TOTAL_AUDIO_SAMPLES) { + fs_close(fd); + return FALSE; + } + + fs_close(fd); + + for (int i = 0; i < (MAXCHANNELS+MAX2DCHANNELS); i++) { + channels[i].ptr = 0; + channels[i].ptr_loop = 0; + channels[i].ch = -1; + channels[i].nSfx = -1; + channels[i].nBank = -1; + } + +#ifdef FIX_BUGS + // Find biggest player comment + uint32 nMaxPlayerSize = 0; + for (uint32 i = PLAYER_COMMENTS_START; i <= PLAYER_COMMENTS_END; i++) + nMaxPlayerSize = Max(nMaxPlayerSize, m_aSamples[i].nByteSize); + + debugf("Max player comment size: %d\n", nMaxPlayerSize); + gPlayerTalkData = snd_mem_malloc(nMaxPlayerSize); + ASSERT(gPlayerTalkData != 0); + + gPlayerTalkReqId = 0; +#endif + + LoadSampleBank(SFX_BANK_0); + + return TRUE; +} + +#if defined(EXTERNAL_3D_SOUND) +void cSampleManager::SetSpeakerConfig(int32 nConfig) { + +} +uint32 cSampleManager::GetMaximumSupportedChannels(void) { + return MAXCHANNELS; +} + +uint32 cSampleManager::GetNum3DProvidersAvailable(void) { + return 32; +} +void cSampleManager::SetNum3DProvidersAvailable(uint32 num) { + +} + +char *cSampleManager::Get3DProviderName(uint8 id) { + return "dummy"; +} +void cSampleManager::Set3DProviderName(uint8 id, char *name) { + +} + +int8 cSampleManager::GetCurrent3DProviderIndex(void) { + return 0; +} +int8 cSampleManager::SetCurrent3DProvider(uint8 which) { + return 0; +} + +int8 +cSampleManager::AutoDetect3DProviders() +{ + return 0; +} + +void cSampleManager::SetChannelEmittingVolume(uint32 nChannel, uint32 nVolume) { + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); + // printf("SetChannelEmittingVolume(nChannel: %d) vol: %d\n", nChannel, nVolume); + if (nVolume > MAX_VOLUME) + nVolume = MAX_VOLUME; + + if (MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE ) { + if (MusicManager.GetCurrentTrack() == STREAMED_SOUND_CUTSCENE_FINALE) + nVolume = 0; + else + nVolume >>= 2; + } + + std::lock_guard lk(channel_mtx); + channels[nChannel].emittingVol = linearlize_volume(nVolume); // nVolume * 255 / MAX_VOLUME; + UpdateChannelVolume(nChannel); +} + +float calculatePan(float x, float z) { + // Azimuth angle (in radians), range from -PI to +PI + float theta = Atan2(x, z); + + // Use the sine function for smooth panning + float pan = Sin(theta); // Use sine to map -PI to +PI to -1.0 to +1.0 + + return pan; +} + +float calculateAttenuation(float x, float y, float z, float mindist, float maxdist) { + // Distance calculation (Euclidean distance) + float distance = Sqrt(x * x + y * y + z * z); + + // If distance is less than or equal to mindist, return full volume (attenuation = 1) + if (distance <= mindist) { + return 1.0f; + } + + // If distance is greater than or equal to maxdist, return no sound (attenuation = 0) + if (distance >= maxdist) { + return 0.0f; + } + + // Calculate attenuation using inverse distance model and rolloff factor + // Attenuation = (referenceDistance / distance) ^ rolloffFactor + float attenuation = (mindist / distance); // rolloffFactor = 1.0f + + return attenuation; +} + + + +void cSampleManager::SetChannel3DPosition (uint32 nChannel, float fX, float fY, float fZ) { + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); + // printf("SetChannel3DPosition(nChannel: %d) x: %f, y: %f, z: %f\n", nChannel, fX, fY, fZ); + { + std::lock_guard lk(channel_mtx); + channels[nChannel].fX = fX; + channels[nChannel].fY = fY; + channels[nChannel].fZ = fZ; + channels[nChannel].attenuationVol = calculateAttenuation(channels[nChannel].fX, channels[nChannel].fY, channels[nChannel].fZ, channels[nChannel].distMin, channels[nChannel].distMax) * 255; + UpdateChannelVolume(nChannel); + } + + SetChannelPan(nChannel, calculatePan(-fX, fZ) * 63 + 64); + +} +void cSampleManager::SetChannel3DDistances (uint32 nChannel, float fMax, float fMin) { + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); + std::lock_guard lk(channel_mtx); + // printf("SetChannel3DDistances(nChannel: %d) min: %f, max: %f\n", nChannel, fMin, fMax); + channels[nChannel].distMin = fMin; + channels[nChannel].distMax = fMax; + channels[nChannel].attenuationVol = calculateAttenuation(channels[nChannel].fX, channels[nChannel].fY, channels[nChannel].fZ, channels[nChannel].distMin, channels[nChannel].distMax) * 255; + UpdateChannelVolume(nChannel); +} +#endif + +void +cSampleManager::SetStreamedFileLoopFlag(bool8 nLoopFlag, uint8 nChannel) +{ + // nStreamLoopedFlag[nChannel] = nLoopFlag; +} + +#endif diff --git a/src/miami/audio/sampman_dc_streams.h b/src/miami/audio/sampman_dc_streams.h new file mode 100644 index 00000000..74ca20f6 --- /dev/null +++ b/src/miami/audio/sampman_dc_streams.h @@ -0,0 +1,1228 @@ + +static char DCStreamedNameTable[][25] = +{ + "stream/WILD.APM", + "stream/FLASH.APM", + "stream/KCHAT.APM", + "stream/FEVER.APM", + "stream/VROCK.APM", + "stream/VCPR.APM", + "stream/ESPANT.APM", + "stream/EMOTION.APM", + "stream/WAVE.APM", + "stream/MISCOM.APM", + "stream/CITY.APM", + "stream/WATER.APM", + "stream/BEACHAMB.APM", + "stream/HCITY.APM", + "stream/HWATER.APM", + "stream/HBEACH.APM", + "stream/MALLAMB.APM", + "stream/STRIP.APM", + "stream/MALIBU.APM", + "stream/HOTEL.APM", + "stream/DIRTRING.APM", + "stream/LAW4RIOT.APM", + "stream/AMBSIL.APM", + "stream/POLICE.APM", + "stream/TAXI.APM", + "stream/BCLOSED.APM", + "stream/BOPEN.APM", + "stream/ASS_1.APM", + "stream/ASS_2.APM", + "stream/BANK_1.APM", + "stream/BANK_2A.APM", + "stream/BANK_2B.APM", + "stream/BANK_3A.APM", + "stream/BANK_3B.APM", + "stream/BANK_4.APM", + "stream/BIKE_1.APM", + "stream/BIKE_2.APM", + "stream/BIKE_3.APM", + "stream/BUD_1.APM", + "stream/BUD_2.APM", + "stream/BUD_3.APM", + "stream/CAP_1.APM", + "stream/CAR_1.APM", + "stream/CNT_1A.APM", + "stream/CNT_1B.APM", + "stream/CNT_2.APM", + "stream/COK_1.APM", + "stream/COK_2A.APM", + "stream/COK_2B.APM", + "stream/COK_3.APM", + "stream/COK_4A.APM", + "stream/COK_4A2.APM", + "stream/COK_4B.APM", + "stream/COL_1.APM", + "stream/COL_2.APM", + "stream/COL_3A.APM", + "stream/COL_4A.APM", + "stream/COL_5A.APM", + "stream/COL_5B.APM", + "stream/CUB_1.APM", + "stream/CUB_2.APM", + "stream/CUB_3.APM", + "stream/CUB_4.APM", + "stream/DRUG_1.APM", + "stream/FIN.APM", + "stream/FIN2.APM", + "stream/FINALE.APM", + "stream/HAT_1.APM", + "stream/HAT_2.APM", + "stream/HAT_3.APM", + "stream/ICE_1.APM", + "stream/INT_A.APM", + "stream/INT_B.APM", + "stream/INT_D.APM", + "stream/INT_M.APM", + "stream/LAW_1A.APM", + "stream/LAW_1B.APM", + "stream/LAW_2A.APM", + "stream/LAW_2B.APM", + "stream/LAW_2C.APM", + "stream/LAW_3.APM", + "stream/LAW_4.APM", + "stream/PHIL_1.APM", + "stream/PHIL_2.APM", + "stream/PORN_1.APM", + "stream/PORN_2.APM", + "stream/PORN_3.APM", + "stream/PORN_4.APM", + "stream/RESC_1A.APM", + "stream/ROK_1.APM", + "stream/ROK_2.APM", + "stream/ROK_3A.APM", + "stream/STRIPA.APM", + "stream/TAX_1.APM", + "stream/TEX_1.APM", + "stream/TEX_2.APM", + "stream/TEX_3.APM", + "stream/GLIGHT.APM", + "stream/FIST.APM", + "stream/MISCOM.APM", + "stream/MISCOM.APM", + "stream/MISCOM.APM", + "stream/MISCOM.APM", + "stream/MOBR1.APM", + "stream/PAGER.APM", + "stream/CARREV.APM", + "stream/BIKEREV.APM", + "stream/LIFTOP.APM", + "stream/LIFTCL.APM", + "stream/LIFTRUN.APM", + "stream/LIFTBEL.APM", + "stream/INLIFT.APM", + "stream/SFX_01.APM", + "stream/SFX_02.APM", + "stream/CAMERAL.APM", + "stream/CAMERAR.APM", + "stream/CHEER1.APM", + "stream/CHEER2.APM", + "stream/CHEER3.APM", + "stream/CHEER4.APM", + "stream/OOH1.APM", + "stream/OOH2.APM", + "stream/RACE1.APM", + "stream/RACE2.APM", + "stream/RACE3.APM", + "stream/RACE4.APM", + "stream/RACE5.APM", + "stream/RACE6.APM", + "stream/RACE7.APM", + "stream/RACE8.APM", + "stream/RACE9.APM", + "stream/RACE10.APM", + "stream/RACE11.APM", + "stream/RACE12.APM", + "stream/RACE13.APM", + "stream/RACE14.APM", + "stream/RACE15.APM", + "stream/HOT1.APM", + "stream/HOT2.APM", + "stream/HOT3.APM", + "stream/HOT4.APM", + "stream/HOT5.APM", + "stream/HOT6.APM", + "stream/HOT7.APM", + "stream/HOT8.APM", + "stream/HOT9.APM", + "stream/HOT10.APM", + "stream/HOT11.APM", + "stream/HOT12.APM", + "stream/HOT13.APM", + "stream/HOT14.APM", + "stream/HOT15.APM", + "stream/LANSTP1.APM", + "stream/LANSTP2.APM", + "stream/LANAMU1.APM", + "stream/LANAMU2.APM", + "stream/AIRHORNL.APM", + "stream/AIRHORNR.APM", + "stream/SNIPSCRL.APM", + "stream/SNIPSHORT.APM", + "stream/BLOWROOF.APM", + "stream/ASS_1.APM", + "stream/ASS_2.APM", + "stream/ASS_3.APM", + "stream/ASS_4.APM", + "stream/ASS_5.APM", + "stream/ASS_6.APM", + "stream/ASS_7.APM", + "stream/ASS_8.APM", + "stream/ASS_9.APM", + "stream/ASS_10.APM", + "stream/ASS_11.APM", + "stream/ASS_12.APM", + "stream/ASS_13.APM", + "stream/ASS_14.APM", + "stream/BIKE1_1.APM", + "stream/BIKE1_2.APM", + "stream/BIKE1_3.APM", + "stream/BNK1_1.APM", + "stream/BNK1_2.APM", + "stream/BNK1_3.APM", + "stream/BNK1_4.APM", + "stream/BNK1_5.APM", + "stream/BNK1_6.APM", + "stream/BNK1_7.APM", + "stream/BNK1_8.APM", + "stream/BNK1_10.APM", + "stream/BNK1_11.APM", + "stream/BNK1_12.APM", + "stream/BNK1_13.APM", + "stream/BNK1_14.APM", + "stream/BNK2_1.APM", + "stream/BNK2_2.APM", + "stream/BNK2_3.APM", + "stream/BNK2_4.APM", + "stream/BNK2_5.APM", + "stream/BNK2_6.APM", + "stream/BNK2_7.APM", + "stream/BNK2_8.APM", + "stream/BNK2_9.APM", + "stream/BNK3_1.APM", + "stream/BNK3_2.APM", + "stream/BNK3_3A.APM", + "stream/BNK3_3B.APM", + "stream/BNK3_3C.APM", + "stream/BNK3_4A.APM", + "stream/BNK3_4B.APM", + "stream/BNK3_4C.APM", + "stream/BNK4_1.APM", + "stream/BNK4_2.APM", + "stream/BNK4_3A.APM", + "stream/BNK4_3B.APM", + "stream/BNK4_3C.APM", + "stream/BNK4_3D.APM", + "stream/BNK4_3E.APM", + "stream/BNK4_3F.APM", + "stream/BNK4_3G.APM", + "stream/BNK4_3H.APM", + "stream/BNK4_3I.APM", + "stream/BNK4_3J.APM", + "stream/BNK4_3K.APM", + "stream/BNK4_3M.APM", + "stream/BNK4_3O.APM", + "stream/BNK4_3P.APM", + "stream/BNK4_3Q.APM", + "stream/BNK4_3R.APM", + "stream/BNK4_3S.APM", + "stream/BNK4_3T.APM", + "stream/BNK4_3U.APM", + "stream/BNK4_3V.APM", + "stream/BNK4_4A.APM", + "stream/BNK4_4B.APM", + "stream/BNK4_5.APM", + "stream/BNK4_6.APM", + "stream/BNK4_7.APM", + "stream/BNK4_8.APM", + "stream/BNK4_9.APM", + "stream/BNK4_10.APM", + "stream/BNK4_11.APM", + "stream/BK4_12A.APM", + "stream/BK4_12B.APM", + "stream/BK4_12C.APM", + "stream/BNK4_13.APM", + "stream/BK4_14A.APM", + "stream/BK4_14B.APM", + "stream/BNK4_15.APM", + "stream/BNK4_16.APM", + "stream/BNK4_17.APM", + "stream/BNK4_18.APM", + "stream/BK4_19A.APM", + "stream/BK4_19B.APM", + "stream/BK4_20A.APM", + "stream/BK4_20B.APM", + "stream/BNK4_21.APM", + "stream/BNK422A.APM", + "stream/BNK422B.APM", + "stream/BK4_23A.APM", + "stream/BK4_23B.APM", + "stream/BK4_23C.APM", + "stream/BK4_23D.APM", + "stream/BK4_24A.APM", + "stream/BK4_24B.APM", + "stream/BNK4_25.APM", + "stream/BNK4_26.APM", + "stream/BNK4_27.APM", + "stream/BNK4_28.APM", + "stream/BNK4_29.APM", + "stream/BNK4_30.APM", + "stream/BK4_31A.APM", + "stream/BK4_31B.APM", + "stream/BNK4_32.APM", + "stream/BK4_34A.APM", + "stream/BK4_34B.APM", + "stream/BK4_35A.APM", + "stream/BK4_35B.APM", + "stream/BNK4_36.APM", + "stream/BNK4_37.APM", + "stream/BNK4_38.APM", + "stream/BNK4_39.APM", + "stream/BK4_40A.APM", + "stream/BK4_40B.APM", + "stream/BNK4_41.APM", + "stream/BNK4_42.APM", + "stream/BNK4_43.APM", + "stream/BNK4_44.APM", + "stream/BNK4_45.APM", + "stream/BNK4_46.APM", + "stream/BNK4_47.APM", + "stream/BNK4_48.APM", + "stream/BNK4_49.APM", + "stream/BNK450A.APM", + "stream/BNK450B.APM", + "stream/BNK4_51.APM", + "stream/BNK4_94.APM", + "stream/BNK4_95.APM", + "stream/BNK4_96.APM", + "stream/BNK4_97.APM", + "stream/BNK4_98.APM", + "stream/BNK4_99.APM", + "stream/BUD1_1.APM", + "stream/BUD1_2.APM", + "stream/BUD1_3.APM", + "stream/BUD1_4.APM", + "stream/BUD1_5.APM", + "stream/BUD1_9.APM", + "stream/BUD1_10.APM", + "stream/BUD2_1.APM", + "stream/BUD2_2.APM", + "stream/BUD2_3.APM", + "stream/BUD2_4.APM", + "stream/BUD2_5.APM", + "stream/BUD2_6.APM", + "stream/BUD2_7.APM", + "stream/BUD3_1.APM", + "stream/BUD3_1A.APM", + "stream/BUD3_1B.APM", + "stream/BUD3_1C.APM", + "stream/BUD3_2.APM", + "stream/BUD3_3.APM", + "stream/BUD3_4.APM", + "stream/BUD3_5.APM", + "stream/BUD3_6.APM", + "stream/BUD3_7.APM", + "stream/BUD3_8A.APM", + "stream/BUD3_8B.APM", + "stream/BUD3_8C.APM", + "stream/BUD3_9A.APM", + "stream/BUD3_9B.APM", + "stream/BUD3_9C.APM", + "stream/CAP1_2.APM", + "stream/CAP1_3.APM", + "stream/CAP1_4.APM", + "stream/CAP1_5.APM", + "stream/CAP1_6.APM", + "stream/CAP1_7.APM", + "stream/CAP1_8.APM", + "stream/CAP1_9.APM", + "stream/CAP1_10.APM", + "stream/CAP1_11.APM", + "stream/CAP1_12.APM", + "stream/CNT1_1.APM", + "stream/CNT1_2.APM", + "stream/CNT1_3.APM", + "stream/CNT1_4.APM", + "stream/CNT1_5.APM", + "stream/CNT2_1.APM", + "stream/CNT2_2.APM", + "stream/CNT2_3.APM", + "stream/CNT2_4.APM", + "stream/COK1_1.APM", + "stream/COK1_2.APM", + "stream/COK1_3.APM", + "stream/COK1_4.APM", + "stream/COK1_5.APM", + "stream/COK1_6.APM", + "stream/COK2_1.APM", + "stream/COK2_2.APM", + "stream/COK2_3.APM", + "stream/COK2_4.APM", + "stream/COK2_5.APM", + "stream/COK2_6.APM", + "stream/COK2_7A.APM", + "stream/COK2_7B.APM", + "stream/COK2_7C.APM", + "stream/COK2_8A.APM", + "stream/COK2_8B.APM", + "stream/COK2_8C.APM", + "stream/COK2_8D.APM", + "stream/COK2_9.APM", + "stream/COK210A.APM", + "stream/COK210B.APM", + "stream/COK210C.APM", + "stream/COK212A.APM", + "stream/COK212B.APM", + "stream/COK2_13.APM", + "stream/COK2_14.APM", + "stream/COK2_15.APM", + "stream/COK2_16.APM", + "stream/COK2_20.APM", + "stream/COK2_21.APM", + "stream/COK2_2.APM", // this is probably a typo of COK2_22 + "stream/COK3_1.APM", + "stream/COK3_2.APM", + "stream/COK3_3.APM", + "stream/COK3_4.APM", + "stream/COK4_1.APM", + "stream/COK4_2.APM", + "stream/COK4_3.APM", + "stream/COK4_4.APM", + "stream/COK4_5.APM", + "stream/COK4_6.APM", + "stream/COK4_7.APM", + "stream/COK4_8.APM", + "stream/COK4_9.APM", + "stream/COK4_9A.APM", + "stream/COK4_10.APM", + "stream/COK4_11.APM", + "stream/COK4_12.APM", + "stream/COK4_13.APM", + "stream/COK4_14.APM", + "stream/COK4_15.APM", + "stream/COK4_16.APM", + "stream/COK4_17.APM", + "stream/COK4_18.APM", + "stream/COK4_19.APM", + "stream/COK4_20.APM", + "stream/COK4_21.APM", + "stream/COK4_22.APM", + "stream/COK4_23.APM", + "stream/COK4_24.APM", + "stream/COK4_25.APM", + "stream/COK4_26.APM", + "stream/COK4_27.APM", + "stream/COL1_1.APM", + "stream/COL1_2.APM", + "stream/COL1_3.APM", + "stream/COL1_4.APM", + "stream/COL1_5.APM", + "stream/COL1_6.APM", + "stream/COL1_7.APM", + "stream/COL1_8.APM", + "stream/COL2_1.APM", + "stream/COL2_2.APM", + "stream/COL2_3.APM", + "stream/COL2_4.APM", + "stream/COL2_5.APM", + "stream/COL2_6A.APM", + "stream/COL2_7.APM", + "stream/COL2_8.APM", + "stream/COL2_9.APM", + "stream/COL2_10.APM", + "stream/COL2_11.APM", + "stream/COL2_12.APM", + "stream/COL2_13.APM", + "stream/COL2_14.APM", + "stream/COL2_15.APM", + "stream/COL2_16.APM", + "stream/COL3_1.APM", + "stream/COL3_2.APM", + "stream/COL3_2A.APM", + "stream/COL3_2B.APM", + "stream/COL3_3.APM", + "stream/COL3_4.APM", + "stream/COL3_5.APM", + "stream/COL3_6.APM", + "stream/COL3_7.APM", + "stream/COL3_8.APM", + "stream/COL3_9.APM", + "stream/COL3_10.APM", + "stream/COL3_11.APM", + "stream/COL3_12.APM", + "stream/COL3_13.APM", + "stream/COL3_14.APM", + "stream/COL3_15.APM", + "stream/COL3_16.APM", + "stream/COL3_17.APM", + "stream/COL3_18.APM", + "stream/COL3_19.APM", + "stream/COL3_20.APM", + "stream/COL3_21.APM", + "stream/COL3_23.APM", + "stream/COL3_24.APM", + "stream/COL3_25.APM", + "stream/COL4_1.APM", + "stream/COL4_2.APM", + "stream/COL4_3.APM", + "stream/COL4_4.APM", + "stream/COL4_5.APM", + "stream/COL4_6.APM", + "stream/COL4_7.APM", + "stream/COL4_8.APM", + "stream/COL4_9.APM", + "stream/COL4_10.APM", + "stream/COL4_11.APM", + "stream/COL4_12.APM", + "stream/COL4_13.APM", + "stream/COL4_14.APM", + "stream/COL4_15.APM", + "stream/COL4_16.APM", + "stream/COL4_17.APM", + "stream/COL4_18.APM", + "stream/COL4_19.APM", + "stream/COL4_20.APM", + "stream/COL4_21.APM", + "stream/COL4_22.APM", + "stream/COL4_23.APM", + "stream/COL4_24.APM", + "stream/COL4_25.APM", + "stream/COL4_26.APM", + "stream/COL5_1.APM", + "stream/COL5_2.APM", + "stream/COL5_3.APM", + "stream/COL5_4.APM", + "stream/COL5_5.APM", + "stream/COL5_6.APM", + "stream/COL5_7.APM", + "stream/COL5_8.APM", + "stream/COL5_9.APM", + "stream/COL5_10.APM", + "stream/COL5_11.APM", + "stream/COL5_12.APM", + "stream/COL5_13.APM", + "stream/COL5_14.APM", + "stream/COL5_15.APM", + "stream/COL5_16.APM", + "stream/COL5_17.APM", + "stream/COL5_18.APM", + "stream/COL5_19.APM", + "stream/COL5_20.APM", + "stream/COL5_21.APM", + "stream/COL5_22.APM", + "stream/CUB1_1.APM", + "stream/CUB1_2.APM", + "stream/CUB1_3.APM", + "stream/CUB1_4.APM", + "stream/CUB1_5.APM", + "stream/CUB1_6.APM", + "stream/CUB1_7.APM", + "stream/CUB1_8.APM", + "stream/CUB1_9.APM", + "stream/CUB1_10.APM", + "stream/CUB2_1.APM", + "stream/CUB2_2.APM", + "stream/CUB2_3A.APM", + "stream/CUB2_3B.APM", + "stream/CUB2_3C.APM", + "stream/CUB2_4A.APM", + "stream/CUB2_5.APM", + "stream/CUB2_6.APM", + "stream/CUB2_7.APM", + "stream/CUB2_8.APM", + "stream/CUB2_9.APM", + "stream/CUB2_10.APM", + "stream/CUB2_11.APM", + "stream/CUB3_1.APM", + "stream/CUB3_2.APM", + "stream/CUB3_3.APM", + "stream/CUB3_4.APM", + "stream/CUB4_1.APM", + "stream/CUB4_2.APM", + "stream/CUB4_3.APM", + "stream/CUB4_4.APM", + "stream/CUB4_5.APM", + "stream/CUB4_5A.APM", + "stream/CUB4_6.APM", + "stream/CUB4_7.APM", + "stream/CUB4_8.APM", + "stream/CUB4_9.APM", + "stream/CUB4_10.APM", + "stream/CUB4_11.APM", + "stream/CUB4_12.APM", + "stream/CUB4_13.APM", + "stream/CUB4_14.APM", + "stream/CUB4_15.APM", + "stream/CUB4_16.APM", + "stream/GOLF_1.APM", + "stream/GOLF_2.APM", + "stream/GOLF_3.APM", + "stream/BAR_1.APM", + "stream/BAR_2.APM", + "stream/BAR_3.APM", + "stream/BAR_4.APM", + "stream/BAR_5.APM", + "stream/BAR_6.APM", + "stream/BAR_7.APM", + "stream/BAR_8.APM", + "stream/STRIP_1.APM", + "stream/STRIP_2.APM", + "stream/STRIP_3.APM", + "stream/STRIP_4.APM", + "stream/STRIP_5.APM", + "stream/STRIP_6.APM", + "stream/STRIP_7.APM", + "stream/STRIP_8.APM", + "stream/STRIP_9.APM", + "stream/STAR_1.APM", + "stream/STAR_2.APM", + "stream/STAR_3.APM", + "stream/STAR_4.APM", + "stream/FIN_1A.APM", + "stream/FIN_1B.APM", + "stream/FIN_1C.APM", + "stream/FIN_2B.APM", + "stream/FIN_2C.APM", + "stream/FIN_3.APM", + "stream/FIN_4.APM", + "stream/FIN_5.APM", + "stream/FIN_6.APM", + "stream/FIN_10.APM", + "stream/FIN_11A.APM", + "stream/FIN_11B.APM", + "stream/FIN_12A.APM", + "stream/FIN_12B.APM", + "stream/FIN_12C.APM", + "stream/FIN_13.APM", + "stream/FINKILL.APM", + "stream/LAW1_1.APM", + "stream/LAW1_2.APM", + "stream/LAW1_3.APM", + "stream/LAW1_4.APM", + "stream/LAW1_5.APM", + "stream/LAW1_6.APM", + "stream/LAW1_7.APM", + "stream/LAW1_8.APM", + "stream/LAW1_9.APM", + "stream/LAW1_10.APM", + "stream/LAW2_1.APM", + "stream/LAW2_2.APM", + "stream/LAW2_3.APM", + "stream/LAW2_4.APM", + "stream/LAW2_5.APM", + "stream/LAW2_6.APM", + "stream/LAW2_7.APM", + "stream/LAW2_8.APM", + "stream/LAW2_9.APM", + "stream/LAW2_10.APM", + "stream/LAW3_1.APM", + "stream/LAW3_2.APM", + "stream/LAW3_3.APM", + "stream/LAW3_4.APM", + "stream/LAW3_5.APM", + "stream/LAW3_6.APM", + "stream/LAW3_10.APM", + "stream/LAW3_11.APM", + "stream/LAW3_12.APM", + "stream/LAW3_13.APM", + "stream/LAW3_14.APM", + "stream/LAW3_16.APM", + "stream/LAW3_17.APM", + "stream/LAW3_18.APM", + "stream/LAW3_19.APM", + "stream/LAW3_20.APM", + "stream/LAW3_21.APM", + "stream/LAW3_22.APM", + "stream/LAW3_23.APM", + "stream/LAW3_24.APM", + "stream/LAW3_25.APM", + "stream/LAW4_1A.APM", + "stream/LAW4_1B.APM", + "stream/LAW4_1C.APM", + "stream/LAW4_1D.APM", + "stream/LAW4_10.APM", + "stream/LAW4_3.APM", + "stream/LAW4_4.APM", + "stream/LAW4_5.APM", + "stream/LAW4_6.APM", + "stream/LAW4_7.APM", + "stream/LAW4_8.APM", + "stream/LAW4_9.APM", + "stream/PHIL1_2.APM", + "stream/PHIL1_3.APM", + "stream/PHIL2_1.APM", + "stream/PHIL2_2.APM", + "stream/PHIL2_3.APM", + "stream/PHIL2_4.APM", + "stream/PHIL2_5.APM", + "stream/PHIL2_6.APM", + "stream/PHIL2_7.APM", + "stream/PHIL2_8.APM", + "stream/PHIL2_9.APM", + "stream/PHIL210.APM", + "stream/PHIL211.APM", + "stream/PORN1_1.APM", + "stream/PORN1_2.APM", + "stream/PORN1_3.APM", + "stream/PRN1_3A.APM", + "stream/PORN1_4.APM", + "stream/PORN1_5.APM", + "stream/PORN1_6.APM", + "stream/PORN1_7.APM", + "stream/PORN1_8.APM", + "stream/PORN1_9.APM", + "stream/PRN1_10.APM", + "stream/PRN1_11.APM", + "stream/PRN1_12.APM", + "stream/PRN1_13.APM", + "stream/PRN1_14.APM", + "stream/PRN1_15.APM", + "stream/PRN1_16.APM", + "stream/PRN1_17.APM", + "stream/PRN1_18.APM", + "stream/PRN1_19.APM", + "stream/PRN1_20.APM", + "stream/PRN1_21.APM", + "stream/PORN3_1.APM", + "stream/PORN3_2.APM", + "stream/PORN3_3.APM", + "stream/PORN3_4.APM", + "stream/PSYCH_1.APM", + "stream/PSYCH_2.APM", + "stream/ROK2_01.APM", + "stream/ROK3_1.APM", + "stream/ROK3_2.APM", + "stream/ROK3_3.APM", + "stream/ROK3_4.APM", + "stream/ROK3_5.APM", + "stream/ROK3_6.APM", + "stream/ROK3_7.APM", + "stream/ROK3_8.APM", + "stream/ROK3_9.APM", + "stream/ROK3_10.APM", + "stream/ROK3_11.APM", + "stream/ROK3_12.APM", + "stream/ROK3_13.APM", + "stream/ROK3_14.APM", + "stream/ROK3_15.APM", + "stream/ROK3_16.APM", + "stream/ROK3_17.APM", + "stream/ROK3_18.APM", + "stream/ROK3_19.APM", + "stream/ROK3_20.APM", + "stream/ROK3_21.APM", + "stream/ROK3_22.APM", + "stream/ROK3_23.APM", + "stream/ROK3_24.APM", + "stream/ROK3_25.APM", + "stream/ROK3_26.APM", + "stream/ROK3_27.APM", + "stream/ROK3_62.APM", + "stream/ROK3_63.APM", + "stream/ROK3_64.APM", + "stream/ROK3_65.APM", + "stream/ROK3_66.APM", + "stream/ROK3_67.APM", + "stream/ROK3_68.APM", + "stream/ROK3_69.APM", + "stream/ROK3_70.APM", + "stream/ROK3_71.APM", + "stream/ROK3_73.APM", + "stream/RESC_1.APM", + "stream/RESC_2.APM", + "stream/RESC_3.APM", + "stream/RESC_4.APM", + "stream/RESC_5.APM", + "stream/RESC_6.APM", + "stream/RESC_7.APM", + "stream/RESC_8.APM", + "stream/RESC_9.APM", + "stream/RESC_10.APM", + "stream/ROK1_1A.APM", + "stream/ROK1_1B.APM", + "stream/ROK1_5.APM", + "stream/ROK1_6.APM", + "stream/ROK1_7.APM", + "stream/ROK1_8.APM", + "stream/ROK1_9.APM", + "stream/TAX1_1.APM", + "stream/TAX1_2.APM", + "stream/TAX1_3.APM", + "stream/TAX1_4.APM", + "stream/TAX1_5.APM", + "stream/TAX2_1.APM", + "stream/TAX2_2.APM", + "stream/TAX2_3.APM", + "stream/TAX2_4.APM", + "stream/TAX2_5.APM", + "stream/TAX2_6.APM", + "stream/TAX2_7.APM", + "stream/TAX3_1.APM", + "stream/TAX3_2.APM", + "stream/TAX3_3.APM", + "stream/TAX3_4.APM", + "stream/TAX3_5.APM", + "stream/TEX1_1.APM", + "stream/TEX1_2.APM", + "stream/TEX1_3.APM", + "stream/TEX1_4.APM", + "stream/TEX1_5.APM", + "stream/TEX1_6.APM", + "stream/TEX2_1.APM", + "stream/TEX3_1.APM", + "stream/TEX3_2.APM", + "stream/TEX3_3.APM", + "stream/TEX3_4.APM", + "stream/TEX3_5.APM", + "stream/TEX3_6.APM", + "stream/TEX3_7.APM", + "stream/TEX3_8.APM", + "stream/HAT_1A.APM", + "stream/INTRO1.APM", + "stream/INTRO2.APM", + "stream/INTRO3.APM", + "stream/INTRO4.APM", + "stream/MOB_01A.APM", + "stream/MOB_01B.APM", + "stream/MOB_01C.APM", + "stream/MOB_02A.APM", + "stream/MOB_02B.APM", + "stream/MOB_02C.APM", + "stream/MOB_03A.APM", + "stream/MOB_03B.APM", + "stream/MOB_03C.APM", + "stream/MOB_03D.APM", + "stream/MOB_03E.APM", + "stream/SHARK_1.APM", + "stream/SHARK_2.APM", + "stream/SHARK_3.APM", + "stream/SHARK_4.APM", + "stream/SHARK_5.APM", + "stream/MOB_04A.APM", + "stream/MOB_04B.APM", + "stream/MOB_04C.APM", + "stream/MOB_04D.APM", + "stream/MOB_05A.APM", + "stream/MOB_05B.APM", + "stream/MOB_05C.APM", + "stream/MOB_05D.APM", + "stream/MOB_06A.APM", + "stream/MOB_06B.APM", + "stream/MOB_06C.APM", + "stream/MOB_07A.APM", + "stream/MOB_07B.APM", + "stream/MOB_08A.APM", + "stream/MOB_08B.APM", + "stream/MOB_08C.APM", + "stream/MOB_08D.APM", + "stream/MOB_08E.APM", + "stream/MOB_08F.APM", + "stream/MOB_08G.APM", + "stream/MOB_09A.APM", + "stream/MOB_09B.APM", + "stream/MOB_09C.APM", + "stream/MOB_09D.APM", + "stream/MOB_09E.APM", + "stream/MOB_09F.APM", + "stream/MOB_10A.APM", + "stream/MOB_10B.APM", + "stream/MOB_10C.APM", + "stream/MOB_10D.APM", + "stream/MOB_10E.APM", + "stream/MOB_11A.APM", + "stream/MOB_11B.APM", + "stream/MOB_11C.APM", + "stream/MOB_11D.APM", + "stream/MOB_11E.APM", + "stream/MOB_11F.APM", + "stream/MOB_14A.APM", + "stream/MOB_14B.APM", + "stream/MOB_14C.APM", + "stream/MOB_14D.APM", + "stream/MOB_14E.APM", + "stream/MOB_14F.APM", + "stream/MOB_14G.APM", + "stream/MOB_14H.APM", + "stream/MOB_16A.APM", + "stream/MOB_16B.APM", + "stream/MOB_16C.APM", + "stream/MOB_16D.APM", + "stream/MOB_16E.APM", + "stream/MOB_16F.APM", + "stream/MOB_16G.APM", + "stream/MOB_17A.APM", + "stream/MOB_17B.APM", + "stream/MOB_17C.APM", + "stream/MOB_17D.APM", + "stream/MOB_17E.APM", + "stream/MOB_17G.APM", + "stream/MOB_17H.APM", + "stream/MOB_17I.APM", + "stream/MOB_17J.APM", + "stream/MOB_17K.APM", + "stream/MOB_17L.APM", + "stream/MOB_18A.APM", + "stream/MOB_18B.APM", + "stream/MOB_18C.APM", + "stream/MOB_18D.APM", + "stream/MOB_18E.APM", + "stream/MOB_18F.APM", + "stream/MOB_18G.APM", + "stream/MOB_20A.APM", + "stream/MOB_20B.APM", + "stream/MOB_20C.APM", + "stream/MOB_20D.APM", + "stream/MOB_20E.APM", + "stream/MOB_24A.APM", + "stream/MOB_24B.APM", + "stream/MOB_24C.APM", + "stream/MOB_24D.APM", + "stream/MOB_24E.APM", + "stream/MOB_24F.APM", + "stream/MOB_24G.APM", + "stream/MOB_24H.APM", + "stream/MOB_25A.APM", + "stream/MOB_25B.APM", + "stream/MOB_25C.APM", + "stream/MOB_25D.APM", + "stream/MOB_26A.APM", + "stream/MOB_26B.APM", + "stream/MOB_26C.APM", + "stream/MOB_26D.APM", + "stream/MOB_26E.APM", + "stream/MOB_29A.APM", + "stream/MOB_29B.APM", + "stream/MOB_29C.APM", + "stream/MOB_29D.APM", + "stream/MOB_29E.APM", + "stream/MOB_29F.APM", + "stream/MOB_29G.APM", + "stream/MOB_30A.APM", + "stream/MOB_30B.APM", + "stream/MOB_30C.APM", + "stream/MOB_30D.APM", + "stream/MOB_30E.APM", + "stream/MOB_30F.APM", + "stream/MOB_33A.APM", + "stream/MOB_33B.APM", + "stream/MOB_33C.APM", + "stream/MOB_33D.APM", + "stream/MOB_34A.APM", + "stream/MOB_34B.APM", + "stream/MOB_34C.APM", + "stream/MOB_34D.APM", + "stream/MOB_35A.APM", + "stream/MOB_35B.APM", + "stream/MOB_35C.APM", + "stream/MOB_35D.APM", + "stream/MOB_36A.APM", + "stream/MOB_36B.APM", + "stream/MOB_36C.APM", + "stream/MOB_40A.APM", + "stream/MOB_40B.APM", + "stream/MOB_40C.APM", + "stream/MOB_40D.APM", + "stream/MOB_40E.APM", + "stream/MOB_40F.APM", + "stream/MOB_40G.APM", + "stream/MOB_40H.APM", + "stream/MOB_40I.APM", + "stream/MOB_41A.APM", + "stream/MOB_41B.APM", + "stream/MOB_41C.APM", + "stream/MOB_41D.APM", + "stream/MOB_41E.APM", + "stream/MOB_41F.APM", + "stream/MOB_41G.APM", + "stream/MOB_41H.APM", + "stream/MOB_42A.APM", + "stream/MOB_42B.APM", + "stream/MOB_42C.APM", + "stream/MOB_42D.APM", + "stream/MOB_42E.APM", + "stream/MOB_43A.APM", + "stream/MOB_43B.APM", + "stream/MOB_43C.APM", + "stream/MOB_43D.APM", + "stream/MOB_43E.APM", + "stream/MOB_43F.APM", + "stream/MOB_43G.APM", + "stream/MOB_43H.APM", + "stream/MOB_45A.APM", + "stream/MOB_45B.APM", + "stream/MOB_45C.APM", + "stream/MOB_45D.APM", + "stream/MOB_45E.APM", + "stream/MOB_45F.APM", + "stream/MOB_45G.APM", + "stream/MOB_45H.APM", + "stream/MOB_45I.APM", + "stream/MOB_45J.APM", + "stream/MOB_45K.APM", + "stream/MOB_45L.APM", + "stream/MOB_45M.APM", + "stream/MOB_45N.APM", + "stream/MOB_46A.APM", + "stream/MOB_46B.APM", + "stream/MOB_46C.APM", + "stream/MOB_46D.APM", + "stream/MOB_46E.APM", + "stream/MOB_46F.APM", + "stream/MOB_46G.APM", + "stream/MOB_46H.APM", + "stream/MOB_47A.APM", + "stream/MOB_52A.APM", + "stream/MOB_52B.APM", + "stream/MOB_52C.APM", + "stream/MOB_52D.APM", + "stream/MOB_52E.APM", + "stream/MOB_52F.APM", + "stream/MOB_52G.APM", + "stream/MOB_52H.APM", + "stream/MOB_54A.APM", + "stream/MOB_54B.APM", + "stream/MOB_54C.APM", + "stream/MOB_54D.APM", + "stream/MOB_54E.APM", + "stream/MOB_55A.APM", + "stream/MOB_55B.APM", + "stream/MOB_55C.APM", + "stream/MOB_55D.APM", + "stream/MOB_55E.APM", + "stream/MOB_55F.APM", + "stream/MOB_56A.APM", + "stream/MOB_56B.APM", + "stream/MOB_56C.APM", + "stream/MOB_56D.APM", + "stream/MOB_56E.APM", + "stream/MOB_56F.APM", + "stream/MOB_57A.APM", + "stream/MOB_57B.APM", + "stream/MOB_57C.APM", + "stream/MOB_57D.APM", + "stream/MOB_57E.APM", + "stream/MOB_58A.APM", + "stream/MOB_58B.APM", + "stream/MOB_58C.APM", + "stream/MOB_58D.APM", + "stream/MOB_58E.APM", + "stream/MOB_58F.APM", + "stream/MOB_58G.APM", + "stream/MOB_61A.APM", + "stream/MOB_61B.APM", + "stream/MOB_62A.APM", + "stream/MOB_62B.APM", + "stream/MOB_62C.APM", + "stream/MOB_62D.APM", + "stream/MOB_63A.APM", + "stream/MOB_63B.APM", + "stream/MOB_63C.APM", + "stream/MOB_63D.APM", + "stream/MOB_63E.APM", + "stream/MOB_63F.APM", + "stream/MOB_63G.APM", + "stream/MOB_63H.APM", + "stream/MOB_63I.APM", + "stream/MOB_63J.APM", + "stream/MOB_66A.APM", + "stream/MOB_66B.APM", + "stream/MOB_68A.APM", + "stream/MOB_68B.APM", + "stream/MOB_68C.APM", + "stream/MOB_68D.APM", + "stream/MOB_70A.APM", + "stream/MOB_70B.APM", + "stream/MOB_71A.APM", + "stream/MOB_71B.APM", + "stream/MOB_71C.APM", + "stream/MOB_71D.APM", + "stream/MOB_71E.APM", + "stream/MOB_71F.APM", + "stream/MOB_71G.APM", + "stream/MOB_71H.APM", + "stream/MOB_71I.APM", + "stream/MOB_71J.APM", + "stream/MOB_71K.APM", + "stream/MOB_71L.APM", + "stream/MOB_71M.APM", + "stream/MOB_71N.APM", + "stream/MOB_72A.APM", + "stream/MOB_72B.APM", + "stream/MOB_72C.APM", + "stream/MOB_72D.APM", + "stream/MOB_72E.APM", + "stream/MOB_72F.APM", + "stream/MOB_72G.APM", + "stream/MOB_73A.APM", + "stream/MOB_73C.APM", + "stream/MOB_73D.APM", + "stream/MOB_73F.APM", + "stream/MOB_73G.APM", + "stream/MOB_73I.APM", + "stream/MOB_95A.APM", + "stream/MOB_96A.APM", + "stream/MOB_98A.APM", + "stream/MOB_99A.APM", + "stream/JOB1_1B.APM", + "stream/JOB1_1C.APM", + "stream/JOB1_1D.APM", + "stream/JOB2_1B.APM", + "stream/JOB2_2.APM", + "stream/JOB2_3.APM", + "stream/JOB2_4.APM", + "stream/JOB2_5.APM", + "stream/JOB2_6.APM", + "stream/JOB2_7.APM", + "stream/JOB2_8.APM", + "stream/JOB2_9.APM", + "stream/JOB3_1.APM", + "stream/JOB3_2.APM", + "stream/JOB3_3.APM", + "stream/JOB4_1.APM", + "stream/JOB4_2.APM", + "stream/JOB4_3.APM", + "stream/JOB5_1.APM", + "stream/JOB5_2.APM", + "stream/JOB5_3.APM", + "stream/BJM1_20.APM", + "stream/BJM1_4.APM", + "stream/BJM1_5.APM", + "stream/MERC_39.APM", + "stream/MONO_1.APM", + "stream/MONO_2.APM", + "stream/MONO_3.APM", + "stream/MONO_4.APM", + "stream/MONO_5.APM", + "stream/MONO_6.APM", + "stream/MONO_7.APM", + "stream/MONO_8.APM", + "stream/MONO_9.APM", + "stream/MONO10.APM", + "stream/MONO11.APM", + "stream/MONO12.APM", + "stream/MONO13.APM", + "stream/MONO14.APM", + "stream/MONO15.APM", + "stream/MONO16.APM", + "stream/FUD_01.APM", + "stream/FUD_02.APM", + "stream/FUD_03.APM", + "stream/FUD_04.APM", + "stream/FUD_05.APM", + "stream/FUD_06.APM", + "stream/FUD_07.APM", + "stream/FUD_08.APM", + "stream/FUD_09.APM", + "stream/FUD_10.APM", + "stream/FUD_11.APM", + "stream/FUD_12.APM", + "stream/FUD_13.APM", + "stream/FUD_14.APM", + "stream/FUD_15.APM", + "stream/FUD_16.APM", + "stream/FUD_17.APM", + "stream/FUD_18.APM", + "stream/FUD_19.APM", + "stream/FUD_20.APM", + "stream/BURG_01.APM", + "stream/BURG_02.APM", + "stream/BURG_03.APM", + "stream/BURG_04.APM", + "stream/BURG_05.APM", + "stream/BURG_06.APM", + "stream/BURG_07.APM", + "stream/BURG_08.APM", + "stream/BURG_09.APM", + "stream/BURG_10.APM", + "stream/BURG_11.APM", + "stream/BURG_12.APM", + "stream/CRUST01.APM", + "stream/CRUST02.APM", + "stream/CRUST03.APM", + "stream/CRUST04.APM", + "stream/CRUST05.APM", + "stream/CRUST06.APM", + "stream/CRUST07.APM", + "stream/CRUST08.APM", + "stream/CRUST09.APM", + "stream/BAND_01.APM", + "stream/BAND_02.APM", + "stream/BAND_03.APM", + "stream/BAND_04.APM", + "stream/BAND_05.APM", + "stream/BAND_06.APM", + "stream/BAND_07.APM", + "stream/BAND_08.APM", + "stream/SHAFT01.APM", + "stream/SHAFT02.APM", + "stream/SHAFT03.APM", + "stream/SHAFT04.APM", + "stream/SHAFT05.APM", + "stream/SHAFT06.APM", + "stream/SHAFT07.APM", + "stream/SHAFT08.APM", + "stream/PISS_01.APM", + "stream/PISS_02.APM", + "stream/PISS_03.APM", + "stream/PISS_04.APM", + "stream/PISS_05.APM", + "stream/PISS_06.APM", + "stream/PISS_07.APM", + "stream/PISS_08.APM", + "stream/PISS_09.APM", + "stream/PISS_10.APM", + "stream/PISS_11.APM", + "stream/PISS_12.APM", + "stream/PISS_13.APM", + "stream/PISS_14.APM", + "stream/PISS_15.APM", + "stream/PISS_16.APM", + "stream/PISS_17.APM", + "stream/PISS_18.APM", + "stream/PISS_19.APM", + "stream/GIMME01.APM", + "stream/GIMME02.APM", + "stream/GIMME03.APM", + "stream/GIMME04.APM", + "stream/GIMME05.APM", + "stream/GIMME06.APM", + "stream/GIMME07.APM", + "stream/GIMME08.APM", + "stream/GIMME09.APM", + "stream/GIMME10.APM", + "stream/GIMME11.APM", + "stream/GIMME12.APM", + "stream/GIMME13.APM", + "stream/GIMME14.APM", + "stream/GIMME15.APM", + "stream/BUST_01.APM", + "stream/BUST_02.APM", + "stream/BUST_03.APM", + "stream/BUST_04.APM", + "stream/BUST_05.APM", + "stream/BUST_06.APM", + "stream/BUST_07.APM", + "stream/BUST_08.APM", + "stream/BUST_09.APM", + "stream/BUST_10.APM", + "stream/BUST_11.APM", + "stream/BUST_12.APM", + "stream/BUST_13.APM", + "stream/BUST_14.APM", + "stream/BUST_15.APM", + "stream/BUST_16.APM", + "stream/BUST_17.APM", + "stream/BUST_18.APM", + "stream/BUST_19.APM", + "stream/BUST_20.APM", + "stream/BUST_21.APM", + "stream/BUST_22.APM", + "stream/BUST_23.APM", + "stream/BUST_24.APM", + "stream/BUST_25.APM", + "stream/BUST_26.APM", + "stream/BUST_27.APM", + "stream/BUST_28.APM", +}; \ No newline at end of file diff --git a/src/miami/audio/sampman_miles.cpp b/src/miami/audio/sampman_miles.cpp new file mode 100644 index 00000000..9f8e8a9c --- /dev/null +++ b/src/miami/audio/sampman_miles.cpp @@ -0,0 +1,2599 @@ +#define WITHWINDOWS +#include "common.h" + +#ifdef AUDIO_MSS +#include +#include + +#include + +#include "eax.h" +#include "eax-util.h" +#include "mss.h" + +#include "sampman.h" +#include "AudioManager.h" +#include "MusicManager.h" +#include "Frontend.h" +#include "Timer.h" +#include "crossplatform.h" + +#pragma comment( lib, "mss32.lib" ) + +cSampleManager SampleManager; +uint32 BankStartOffset[MAX_SFX_BANKS]; +/////////////////////////////////////////////////////////////// + +char SampleBankDescFilename[] = "AUDIO\\SFX.SDT"; +char SampleBankDataFilename[] = "AUDIO\\SFX.RAW"; + +FILE *fpSampleDescHandle; +FILE *fpSampleDataHandle; +int8 gBankLoaded [MAX_SFX_BANKS]; +int32 nSampleBankDiscStartOffset [MAX_SFX_BANKS]; +int32 nSampleBankSize [MAX_SFX_BANKS]; +int32 nSampleBankMemoryStartAddress[MAX_SFX_BANKS]; +int32 _nSampleDataEndOffset; + +int32 nPedSlotSfx [MAX_PEDSFX]; +int32 nPedSlotSfxAddr[MAX_PEDSFX]; +uint8 nCurrentPedSlot; + +#ifdef FIX_BUGS +uint32 gPlayerTalkSfx = UINT32_MAX; +void *gPlayerTalkData = 0; +#endif + +uint8 nChannelVolume[MAXCHANNELS+MAX2DCHANNELS]; + +uint32 nStreamLength[TOTAL_STREAMED_SOUNDS]; + +/////////////////////////////////////////////////////////////// +struct tMP3Entry +{ + char aFilename[MAX_PATH]; + + uint32 nTrackLength; + uint32 nTrackStreamPos; + + tMP3Entry *pNext; + char *pLinkPath; +}; + +uint32 nNumMP3s; +tMP3Entry *_pMP3List; +char _mp3DirectoryPath[MAX_PATH]; +HSTREAM mp3Stream [MAX_STREAMS]; +int8 nStreamPan [MAX_STREAMS]; +int8 nStreamVolume[MAX_STREAMS]; +bool8 nStreamLoopedFlag[MAX_STREAMS]; +uint32 _CurMP3Index; +int32 _CurMP3Pos; +bool8 _bIsMp3Active; +/////////////////////////////////////////////////////////////// + + +bool8 _bSampmanInitialised = FALSE; +#ifdef EXTERNAL_3D_SOUND +// +// Miscellaneous globals / defines + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS + +EAXLISTENERPROPERTIES StartEAX3 = + {26, 1.7f, 0.8f, -1000, -1000, -100, 4.42f, 0.14f, 1.00f, 429, 0.014f, 0.00f,0.00f,0.00f, 1023, 0.021f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 2727.1f, 250.0f, 0.00f, 0x3f }; + +EAXLISTENERPROPERTIES FinishEAX3 = + {26, 100.0f, 1.0f, 0, -1000, -2200, 20.0f, 1.39f, 1.00f, 1000, 0.069f, 0.00f,0.00f,0.00f, 400, 0.100f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 3.982f, 0.000f, -18.0f, 3530.8f, 417.9f, 6.70f, 0x3f }; + +EAXLISTENERPROPERTIES EAX3Params; + +S32 prevprovider=-1; +S32 curprovider=-1; +S32 usingEAX=0; +S32 usingEAX3=0; +HPROVIDER opened_provider=0; +H3DSAMPLE opened_samples[MAXCHANNELS] = {0}; +#endif +HSAMPLE opened_2dsamples[MAX2DCHANNELS] = {0}; +HDIGDRIVER DIG; +#ifdef EXTERNAL_3D_SOUND +S32 speaker_type=0; + +U32 _maxSamples; +float _fPrevEaxRatioDestination; +bool8 _usingMilesFast2D; +float _fEffectsLevel; + + +struct +{ + HPROVIDER id; + char name[80]; +}providers[MAXPROVIDERS]; + +typedef struct provider_stuff +{ + char* name; + HPROVIDER id; +} provider_stuff; + + +static int __cdecl comp(const provider_stuff*s1,const provider_stuff*s2) +{ + return( _stricmp(s1->name,s2->name) ); +} + +static void +add_providers() +{ + provider_stuff pi[MAXPROVIDERS]; + U32 n,i,j; + + SampleManager.SetNum3DProvidersAvailable(0); + + HPROENUM next = HPROENUM_FIRST; + + n=0; + while (AIL_enumerate_3D_providers(&next, &pi[n].id, &pi[n].name) && (n MAXCHANNELS ) + _maxSamples = MAXCHANNELS; + + SampleManager.SetSpeakerConfig(speaker_type); + + //obtain a 3D sample handles + for ( U32 i = 0; i < _maxSamples; ++i ) + { + opened_samples[i] = AIL_allocate_3D_sample_handle(opened_provider); + if ( opened_samples[i] != NULL ) + AIL_set_3D_sample_effects_level(opened_samples[i], 0.0f); + } + + return TRUE; + } + } + + return FALSE; +} +#endif + +U32 RadioHandlers[9]; + +U32 WINAPI vfs_open_callback(char const* Filename, U32* FileHandle) +{ + *FileHandle = (U32)fopen(Filename, "rb"); + + // couldn't they just use stricmp once? and strlen? this is very inefficient + if ((strcmp(Filename + strlen(Filename) - 4, ".adf") == 0) || (strcmp(Filename + strlen(Filename) - 4, ".ADF") == 0)) { + for (int i = 0; i < ARRAY_SIZE(RadioHandlers); i++) { + if (RadioHandlers[i] == NULL) { + RadioHandlers[i] = *FileHandle; + break; + } + } + strcpy((char*)Filename + strlen(Filename) - 4, ".mp3"); + } + return *FileHandle; +} + +void WINAPI vfs_close_callback(U32 FileHandle) +{ + for (int i = 0; i < ARRAY_SIZE(RadioHandlers); i++) { + if (RadioHandlers[i] == FileHandle) { + RadioHandlers[i] = NULL; + break; + } + } + fclose((FILE*)FileHandle); +} + +S32 WINAPI vfs_seek_callback(U32 FileHandle, S32 Offset, U32 Type) +{ + fseek((FILE*)FileHandle, Offset, Type); + return ftell((FILE*)FileHandle); +} + +U32 WINAPI vfs_read_callback(U32 FileHandle, void* Buffer, U32 Bytes) +{ + fread(Buffer, Bytes, 1, (FILE*)FileHandle); + uint8* _Buffer = (uint8*)Buffer; + + for (int i = 0; i < ARRAY_SIZE(RadioHandlers); i++) { + if (FileHandle == RadioHandlers[i]) { + for (U32 k = 0; k < Bytes; k++) + _Buffer[k] ^= 0x22; + break; + } + } + return Bytes; +} + +cSampleManager::cSampleManager(void) : + m_nNumberOfProviders(0) +{ + ; + + AIL_set_file_callbacks(vfs_open_callback, vfs_close_callback, vfs_seek_callback, vfs_read_callback); +} + +cSampleManager::~cSampleManager(void) +{ + +} + +#ifdef EXTERNAL_3D_SOUND +void +cSampleManager::SetSpeakerConfig(int32 which) +{ + switch ( which ) + { + case 1: + speaker_type=AIL_3D_2_SPEAKER; + break; + + case 2: + speaker_type=AIL_3D_HEADPHONE; + break; + + case 3: + speaker_type=AIL_3D_4_SPEAKER; + break; + + default: + return; + break; + } + + if (opened_provider) + AIL_set_3D_speaker_type(opened_provider, speaker_type); +} + +uint32 +cSampleManager::GetMaximumSupportedChannels(void) +{ + if ( _maxSamples > MAXCHANNELS ) + return MAXCHANNELS; + + return _maxSamples; +} + +uint32 cSampleManager::GetNum3DProvidersAvailable() +{ + return m_nNumberOfProviders; +} + +void cSampleManager::SetNum3DProvidersAvailable(uint32 num) +{ + m_nNumberOfProviders = num; +} + +char *cSampleManager::Get3DProviderName(uint8 id) +{ + return m_aAudioProviders[id]; +} + +void cSampleManager::Set3DProviderName(uint8 id, char *name) +{ + m_aAudioProviders[id] = name; +} + +int8 +cSampleManager::GetCurrent3DProviderIndex(void) +{ + return curprovider; +} + +int8 +cSampleManager::SetCurrent3DProvider(uint8 nProvider) +{ + S32 savedprovider = curprovider; + + if ( nProvider < m_nNumberOfProviders ) + { + if ( set_new_provider(nProvider) ) + return curprovider; + else if ( savedprovider != -1 && savedprovider < m_nNumberOfProviders && set_new_provider(savedprovider) ) + return curprovider; + else + return -1; + } + else + return curprovider; +} + +int8 +cSampleManager::AutoDetect3DProviders() +{ + if (!AudioManager.IsAudioInitialised()) + return -1; + + int eax = -1, eax2 = -1, eax3 = -1, ds3dh = -1, ds3ds = -1; + + for (uint32 i = 0; i < GetNum3DProvidersAvailable(); i++) + { + char* providername = Get3DProviderName(i); + + if (!strcasecmp(providername, "CREATIVE LABS EAX (TM)")) { + AudioManager.SetCurrent3DProvider(i); + if (GetCurrent3DProviderIndex() == i) + eax = i; + } + + if (!strcasecmp(providername, "CREATIVE LABS EAX 2 (TM)")) { + AudioManager.SetCurrent3DProvider(i); + if (GetCurrent3DProviderIndex() == i) + eax2 = i; + } + + if (!strcasecmp(providername, "CREATIVE LABS EAX 3 (TM)")) { + AudioManager.SetCurrent3DProvider(i); + if (GetCurrent3DProviderIndex() == i) { + eax3 = i; + } + } + + if (!strcasecmp(providername, "DIRECTSOUND3D HARDWARE SUPPORT")) { + AudioManager.SetCurrent3DProvider(i); + if (GetCurrent3DProviderIndex() == i) + ds3dh = i; + } + + if (!strcasecmp(providername, "DIRECTSOUND3D SOFTWARE EMULATION")) { + AudioManager.SetCurrent3DProvider(i); + if (GetCurrent3DProviderIndex() == i) + ds3ds = i; + } + } + + if (eax3 != -1) + return eax3; + if (eax2 != -1) + return eax2; + if (eax != -1) + return eax; + if (ds3dh != -1) + return ds3dh; + if (ds3ds != -1) + return ds3ds; + return -1; +} +#endif + +static bool8 +_ResolveLink(char const *path, char *out) +{ + IShellLink* psl; + WIN32_FIND_DATA fd; + char filepath[MAX_PATH]; + + CoInitialize(NULL); + + if (SUCCEEDED( CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl ) )) + { + IPersistFile *ppf; + + if (SUCCEEDED(psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf))) + { + WCHAR wpath[MAX_PATH]; + + MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, MAX_PATH); + + if (SUCCEEDED(ppf->Load(wpath, STGM_READ))) + { + /* Resolve the link */ + if (SUCCEEDED(psl->Resolve(NULL, SLR_ANY_MATCH|SLR_NO_UI|SLR_NOSEARCH))) + { + strcpy(filepath, path); + + if (SUCCEEDED(psl->GetPath(filepath, MAX_PATH, &fd, SLGP_UNCPRIORITY))) + { + OutputDebugString(fd.cFileName); + + strcpy(out, filepath); + // FIX: Release the objects. Taken from SA. +#ifdef FIX_BUGS + ppf->Release(); + psl->Release(); +#endif + return TRUE; + } + } + } + + ppf->Release(); + } + psl->Release(); + } + + return FALSE; +} + +static void +_FindMP3s(void) +{ + tMP3Entry *pList; + bool8 bShortcut; + bool8 bInitFirstEntry; + HANDLE hFind; + char path[MAX_PATH]; + char filepath[MAX_PATH*2]; + S32 total_ms; + WIN32_FIND_DATA fd; + + + if ( GetCurrentDirectory(MAX_PATH, _mp3DirectoryPath) == 0 ) + { + GetLastError(); + return; + } + + OutputDebugString("Finding MP3s..."); + strcpy(path, _mp3DirectoryPath); + strcat(path, "\\MP3\\"); + + strcpy(_mp3DirectoryPath, path); + OutputDebugString(_mp3DirectoryPath); + + strcat(path, "*"); + + hFind = FindFirstFile(path, &fd); + + if ( hFind == INVALID_HANDLE_VALUE ) + { + GetLastError(); + return; + } + + strcpy(filepath, _mp3DirectoryPath); + strcat(filepath, fd.cFileName); + + int32 filepathlen = strlen(filepath); + + if ( filepathlen <= 0) + { + FindClose(hFind); + return; + } + + if ( filepathlen > 4 ) + { + if ( !strcmp(&filepath[filepathlen - 4], ".lnk") ) + { + if ( _ResolveLink(filepath, filepath) ) + { + OutputDebugString("Resolving Link"); + OutputDebugString(filepath); + } + + bShortcut = TRUE; + } + else + bShortcut = FALSE; + } + + mp3Stream[0] = AIL_open_stream(DIG, filepath, 0); + if ( mp3Stream[0] ) + { + AIL_stream_ms_position(mp3Stream[0], &total_ms, NULL); + + AIL_close_stream(mp3Stream[0]); + mp3Stream[0] = NULL; + + OutputDebugString(fd.cFileName); + + _pMP3List = new tMP3Entry; + + if ( _pMP3List == NULL ) + { + FindClose(hFind); + return; + } + + nNumMP3s = 1; + + strcpy(_pMP3List->aFilename, fd.cFileName); + + _pMP3List->nTrackLength = total_ms; + + _pMP3List->pNext = NULL; + + pList = _pMP3List; + + if ( bShortcut ) + { + _pMP3List->pLinkPath = new char[MAX_PATH*2]; + strcpy(_pMP3List->pLinkPath, filepath); + } + else + { + _pMP3List->pLinkPath = NULL; + } + bInitFirstEntry = FALSE; + } + else + { + strcat(filepath, " - NOT A VALID MP3"); + + OutputDebugString(filepath); + + bInitFirstEntry = TRUE; + } + + while ( TRUE ) + { + if ( !FindNextFile(hFind, &fd) ) + break; + + if ( bInitFirstEntry ) + { + strcpy(filepath, _mp3DirectoryPath); + strcat(filepath, fd.cFileName); + + int32 filepathlen = strlen(filepath); + + if ( filepathlen > 0 ) + { + if ( filepathlen > 4 ) + { + if ( !strcmp(&filepath[filepathlen - 4], ".lnk") ) + { + if ( _ResolveLink(filepath, filepath) ) + { + OutputDebugString("Resolving Link"); + OutputDebugString(filepath); + } + + bShortcut = TRUE; + } + else + { + bShortcut = FALSE; + + if ( filepathlen > MAX_PATH ) + { + continue; + } + } + } + + mp3Stream[0] = AIL_open_stream(DIG, filepath, 0); + if ( mp3Stream[0] ) + { + AIL_stream_ms_position(mp3Stream[0], &total_ms, NULL); + + AIL_close_stream(mp3Stream[0]); + mp3Stream[0] = NULL; + + OutputDebugString(fd.cFileName); + + _pMP3List = new tMP3Entry; + + if ( _pMP3List == NULL) + break; + + nNumMP3s = 1; + + strcpy(_pMP3List->aFilename, fd.cFileName); + + _pMP3List->nTrackLength = total_ms; + _pMP3List->pNext = NULL; + + if ( bShortcut ) + { + _pMP3List->pLinkPath = new char [MAX_PATH*2]; + strcpy(_pMP3List->pLinkPath, filepath); + } + else + { + _pMP3List->pLinkPath = NULL; + } + + pList = _pMP3List; + + bInitFirstEntry = FALSE; + } + else + { + strcat(filepath, " - NOT A VALID MP3"); + OutputDebugString(filepath); + } + } + } + else + { + strcpy(filepath, _mp3DirectoryPath); + strcat(filepath, fd.cFileName); + + int32 filepathlen = strlen(filepath); + + if ( filepathlen > 0 ) + { + if ( filepathlen > 4 ) + { + if ( !strcmp(&filepath[filepathlen - 4], ".lnk") ) + { + if ( _ResolveLink(filepath, filepath) ) + { + OutputDebugString("Resolving Link"); + OutputDebugString(filepath); + } + + bShortcut = TRUE; + } + else + { + bShortcut = FALSE; + } + } + + mp3Stream[0] = AIL_open_stream(DIG, filepath, 0); + if ( mp3Stream[0] ) + { + AIL_stream_ms_position(mp3Stream[0], &total_ms, NULL); + + AIL_close_stream(mp3Stream[0]); + mp3Stream[0] = NULL; + + pList->pNext = new tMP3Entry; + + tMP3Entry *e = pList->pNext; + + if ( e == NULL ) + break; + + pList = pList->pNext; + + strcpy(e->aFilename, fd.cFileName); + e->nTrackLength = total_ms; + e->pNext = NULL; + + if ( bShortcut ) + { + e->pLinkPath = new char [MAX_PATH*2]; + strcpy(e->pLinkPath, filepath); + } + else + { + e->pLinkPath = NULL; + } + + nNumMP3s++; + + OutputDebugString(fd.cFileName); + } + else + { + strcat(filepath, " - NOT A VALID MP3"); + OutputDebugString(filepath); + } + } + } + } + + FindClose(hFind); +} + +static void +_DeleteMP3Entries(void) +{ + tMP3Entry *e = _pMP3List; + + while ( e != NULL ) + { + tMP3Entry *next = e->pNext; + + if ( next == NULL ) + next = NULL; + + if ( e->pLinkPath != NULL ) + { +#ifndef FIX_BUGS + delete e->pLinkPath; // BUG: should be delete [] +#else + delete[] e->pLinkPath; +#endif + e->pLinkPath = NULL; + } + + delete e; + + if ( next ) + e = next; + else + e = NULL; + + nNumMP3s--; + } + + + if ( nNumMP3s != 0 ) + { + OutputDebugString("Not all MP3 entries were deleted"); + nNumMP3s = 0; + } + + _pMP3List = NULL; +} + +static tMP3Entry * +_GetMP3EntryByIndex(uint32 idx) +{ + uint32 n = ( idx < nNumMP3s ) ? idx : 0; + + if ( _pMP3List != NULL ) + { + tMP3Entry *e = _pMP3List; + + for ( uint32 i = 0; i < n; i++ ) + e = e->pNext; + + return e; + + } + + return NULL; +} + +static inline bool8 +_GetMP3PosFromStreamPos(uint32 *pPosition, tMP3Entry **pEntry) +{ + _CurMP3Index = 0; + + for ( *pEntry = _pMP3List; *pEntry != NULL; *pEntry = (*pEntry)->pNext ) + { + if ( *pPosition >= (*pEntry)->nTrackStreamPos + && *pPosition < (*pEntry)->nTrackLength + (*pEntry)->nTrackStreamPos ) + { + *pPosition -= (*pEntry)->nTrackStreamPos; + _CurMP3Pos = *pPosition; + + return TRUE; + } + + _CurMP3Index++; + } + + *pPosition = 0; + *pEntry = _pMP3List; + _CurMP3Pos = 0; + _CurMP3Index = 0; + + return FALSE; +} + +bool8 +cSampleManager::IsMP3RadioChannelAvailable(void) +{ + return nNumMP3s != 0; +} + +void +cSampleManager::ReleaseDigitalHandle(void) +{ + if ( DIG ) + { +#ifdef EXTERNAL_3D_SOUND + prevprovider = curprovider; + release_existing(); + curprovider = -1; +#endif + AIL_digital_handle_release(DIG); + } +} + +void +cSampleManager::ReacquireDigitalHandle(void) +{ + if ( DIG ) + { + AIL_digital_handle_reacquire(DIG); +#ifdef EXTERNAL_3D_SOUND + if ( prevprovider != -1 ) + set_new_provider(prevprovider); +#endif + } +} + +bool8 +cSampleManager::Initialise(void) +{ + TRACE("start"); + + if ( _bSampmanInitialised ) + return TRUE; + + { + for ( int32 i = 0; i < TOTAL_AUDIO_SAMPLES; i++ ) + { + m_aSamples[i].nOffset = 0; + m_aSamples[i].nSize = 0; + m_aSamples[i].nFrequency = 22050; + m_aSamples[i].nLoopStart = 0; + m_aSamples[i].nLoopEnd = -1; + } + + m_nEffectsVolume = MAX_VOLUME; + m_nMusicVolume = MAX_VOLUME; + m_nEffectsFadeVolume = MAX_VOLUME; + m_nMusicFadeVolume = MAX_VOLUME; + + m_nMonoMode = 0; + } + +#ifdef EXTERNAL_3D_SOUND + // miles + TRACE("MILES"); + { + curprovider = -1; + prevprovider = -1; + + _usingMilesFast2D = FALSE; + usingEAX=0; + usingEAX3=0; + + _fEffectsLevel = 0.0f; + + _maxSamples = 0; + + opened_provider = NULL; + DIG = NULL; + + for ( int32 i = 0; i < MAXCHANNELS; i++ ) + opened_samples[i] = NULL; + } +#endif + + // banks + TRACE("banks"); + { + fpSampleDescHandle = NULL; + fpSampleDataHandle = NULL; + + _nSampleDataEndOffset = 0; + + for ( int32 i = 0; i < MAX_SFX_BANKS; i++ ) + { + gBankLoaded[i] = LOADING_STATUS_NOT_LOADED; + nSampleBankDiscStartOffset[i] = 0; + nSampleBankSize[i] = 0; + nSampleBankMemoryStartAddress[i] = 0; + } + } + + // pedsfx + TRACE("pedsfx"); + { + for ( int32 i = 0; i < MAX_PEDSFX; i++ ) + { + nPedSlotSfx[i] = NO_SAMPLE; + nPedSlotSfxAddr[i] = 0; + } + + nCurrentPedSlot = 0; + } + + // channel volume + TRACE("vol"); + { + for ( int32 i = 0; i < MAXCHANNELS+MAX2DCHANNELS; i++ ) + nChannelVolume[i] = 0; + } + + TRACE("mss"); + { + AIL_set_redist_directory( "mss" ); + + AIL_startup(); + + AIL_set_preference(DIG_MIXER_CHANNELS, MAX_DIGITAL_MIXER_CHANNELS); + + DIG = AIL_open_digital_driver(DIGITALRATE, DIGITALBITS, DIGITALCHANNELS, 0); + + } + +#ifdef AUDIO_CACHE + TRACE("cache"); + FILE *cacheFile = fcaseopen("audio\\sound.cache", "rb"); + bool8 CreateCache = FALSE; + if (cacheFile) { + fread(nStreamLength, sizeof(uint32), TOTAL_STREAMED_SOUNDS, cacheFile); + fclose(cacheFile); + }else + CreateCache = TRUE; +#endif + + char filepath[MAX_PATH]; + bool8 bFileNotFound; + S32 tatalms; + + TRACE("cdrom"); + { + m_bInitialised = FALSE; + + + while (TRUE) + { + + // Find path of WAVs (originally in HDD) + int32 drive = 'C'; + +#ifndef NO_CDCHECK + do + { + char latter[2]; + + latter[0] = drive; + latter[1] = '\0'; + + strcpy(m_szCDRomRootPath, latter); + strcat(m_szCDRomRootPath, ":\\"); + + if ( GetDriveType(m_szCDRomRootPath) == DRIVE_CDROM ) + { + FILE *f; +#ifdef PS2_AUDIO_PATHS + strcpy(filepath, m_szCDRomRootPath); + strcat(filepath, PS2StreamedNameTable[0]); + f = fopen(filepath, "rb"); + + if ( !f ) +#endif + { + strcpy(filepath, m_szCDRomRootPath); + strcat(filepath, StreamedNameTable[0]); + + f = fopen(filepath, "rb"); + } + if ( f ) + { + fclose(f); + strcpy(m_MiscomPath, m_szCDRomRootPath); + break; + } + } + + } while ( ++drive <= 'Z' ); +#else + m_MiscomPath[0] = '\0'; +#endif + + if ( DIG == NULL ) + { + OutputDebugString(AIL_last_error()); + Terminate(); + return FALSE; + } + +#ifdef EXTERNAL_3D_SOUND + add_providers(); +#endif + + m_szCDRomRootPath[0] = '\0'; + + strcpy(m_WavFilesPath, m_szCDRomRootPath); + +#ifdef AUDIO_CACHE + if ( CreateCache ) +#endif + for ( int32 i = STREAMED_SOUND_MISSION_MOBR1; i < TOTAL_STREAMED_SOUNDS; i++ ) + { +#ifdef PS2_AUDIO_PATHS + strcpy(filepath, m_szCDRomRootPath); + strcat(filepath, PS2StreamedNameTable[i]); + + mp3Stream[0] = AIL_open_stream(DIG, filepath, 0); + + if ( !mp3Stream[0] ) +#endif + { + strcpy(filepath, m_szCDRomRootPath); + strcat(filepath, StreamedNameTable[i]); + + mp3Stream[0] = AIL_open_stream(DIG, filepath, 0); + } + + if ( mp3Stream[0] ) + { + AIL_stream_ms_position(mp3Stream[0], &tatalms, NULL); + + AIL_close_stream(mp3Stream[0]); + mp3Stream[0] = NULL; + + nStreamLength[i] = tatalms; + } + else + { + m_bInitialised = FALSE; + Terminate(); + return FALSE; + } + } + + // Find path of MP3s (originally in CD-Rom) + // if NO_CDCHECK is NOT defined but AUDIO_CACHE is defined, we still need to find MP3s' path, but will exit after the first file +#ifndef NO_CDCHECK + int32 drive = 'C'; + do + { + latter[0] = drive; + latter[1] = '\0'; + + strcpy(m_szCDRomRootPath, latter); + strcat(m_szCDRomRootPath, ":"); + strcat(m_MP3FilesPath, m_szCDRomRootPath); +#else + m_MP3FilesPath[0] = '\0'; + { +#endif + + for (int32 i = 0; i < STREAMED_SOUND_MISSION_MOBR1; i++) + { +#ifdef PS2_AUDIO_PATHS + strcpy(filepath, m_MP3FilesPath); + strcat(filepath, PS2StreamedNameTable[i]); + + mp3Stream[0] = AIL_open_stream(DIG, filepath, 0); + + if ( !mp3Stream[0] ) +#endif + { + strcpy(filepath, m_MP3FilesPath); + strcat(filepath, StreamedNameTable[i]); + + mp3Stream[0] = AIL_open_stream(DIG, filepath, 0); + } + + if (mp3Stream[0]) + { + AIL_stream_ms_position(mp3Stream[0], &tatalms, NULL); + + AIL_close_stream(mp3Stream[0]); + mp3Stream[0] = NULL; + + bFileNotFound = FALSE; +#ifdef AUDIO_CACHE + if (!CreateCache) + break; + else +#endif + nStreamLength[i] = tatalms; + + } + else + { + bFileNotFound = TRUE; + break; + } + } + +#ifndef NO_CDCHECK + if (!bFileNotFound) // otherwise try next drive + break; + + } + while (++drive <= 'Z'); +#else + } +#endif + + if ( !bFileNotFound ) { + +#ifdef AUDIO_CACHE + if ( CreateCache ) +#endif + for ( int32 i = STREAMED_SOUND_MISSION_COMPLETED4; i < STREAMED_SOUND_MISSION_PAGER; i++ ) + { +#ifdef PS2_AUDIO_PATHS + strcpy(filepath, m_MiscomPath); + strcat(filepath, PS2StreamedNameTable[i]); + + mp3Stream[0] = AIL_open_stream(DIG, filepath, 0); + + if ( !mp3Stream[0] ) +#endif + { + strcpy(filepath, m_MiscomPath); + strcat(filepath, StreamedNameTable[i]); + + mp3Stream[0] = AIL_open_stream(DIG, filepath, 0); + } + + if ( mp3Stream[0] ) + { + AIL_stream_ms_position(mp3Stream[0], &tatalms, NULL); + + AIL_close_stream(mp3Stream[0]); + mp3Stream[0] = NULL; + + nStreamLength[i] = tatalms; + bFileNotFound = FALSE; + } + else + { + bFileNotFound = TRUE; + break; + } + } + } + + m_bInitialised = !bFileNotFound; + + if ( !m_bInitialised ) + { +#if !defined(GTA3_STEAM_PATCH) && !defined(NO_CDCHECK) + FrontEndMenuManager.WaitForUserCD(); + if ( FrontEndMenuManager.m_bQuitGameNoCD ) + { + Terminate(); + return FALSE; + } + continue; +#else + m_bInitialised = TRUE; +#endif + } + + break; + } + } + +#ifdef AUDIO_CACHE + if (CreateCache) { + cacheFile = fcaseopen("audio\\sound.cache", "wb"); + fwrite(nStreamLength, sizeof(uint32), TOTAL_STREAMED_SOUNDS, cacheFile); + fclose(cacheFile); + } +#endif + + if ( !InitialiseSampleBanks() ) + { + Terminate(); + return FALSE; + } + + nSampleBankMemoryStartAddress[SFX_BANK_0] = (int32)AIL_mem_alloc_lock(nSampleBankSize[SFX_BANK_0]); + if ( !nSampleBankMemoryStartAddress[SFX_BANK_0] ) + { + Terminate(); + return FALSE; + } + + nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] = (int32)AIL_mem_alloc_lock(PED_BLOCKSIZE*MAX_PEDSFX); + +#ifdef FIX_BUGS + // Find biggest player comment + uint32 nMaxPedSize = 0; + for (uint32 i = PLAYER_COMMENTS_START; i <= PLAYER_COMMENTS_END; i++) + nMaxPedSize = Max(nMaxPedSize, m_aSamples[i].nSize); + + gPlayerTalkData = AIL_mem_alloc_lock(nMaxPedSize); + if ( !gPlayerTalkData ) + { + Terminate(); + return FALSE; + } +#endif + + LoadSampleBank(SFX_BANK_0); + + TRACE("stream"); + { + for ( int32 i = 0; i < MAX_STREAMS; i++ ) + { + mp3Stream [i] = NULL; + nStreamPan [i] = 63; + nStreamVolume[i] = 100; + } + } + + for ( int32 i = 0; i < MAX2DCHANNELS; i++ ) + { + opened_2dsamples[i] = AIL_allocate_sample_handle(DIG); + if ( opened_2dsamples[i] ) + { + AIL_init_sample(opened_2dsamples[i]); + AIL_set_sample_type(opened_2dsamples[i], DIG_F_MONO_16, DIG_PCM_SIGN); + } + } + + TRACE("providerset"); + { + _bSampmanInitialised = TRUE; + +#ifdef EXTERNAL_3D_SOUND + U32 n = 0; + + while ( n < m_nNumberOfProviders ) + { + if ( !strcmp(strupr(providers[n].name), "DIRECTSOUND3D SOFTWARE EMULATION") ) + { + set_new_provider(n); + break; + } + n++; + } + + if ( n == m_nNumberOfProviders ) + { + Terminate(); + return FALSE; + } +#endif + } + + // mp3 + TRACE("mp3"); + { + nNumMP3s = 0; + + _pMP3List = NULL; + + _FindMP3s(); + + if ( nNumMP3s != 0 ) + { + nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER] = 0; + + for ( tMP3Entry *e = _pMP3List; e != NULL; e = e->pNext ) + { + e->nTrackStreamPos = nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER]; + nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER] += e->nTrackLength; + } + + time_t t = time(NULL); + tm *localtm; + bool8 bUseRandomTable; + + if ( t == -1 ) + bUseRandomTable = TRUE; + else + { + bUseRandomTable = FALSE; + localtm = localtime(&t); + } + + int32 randval; + if ( bUseRandomTable ) + randval = AudioManager.m_anRandomTable[1]; + else + randval = localtm->tm_sec * localtm->tm_min; + + _CurMP3Index = randval % nNumMP3s; + + tMP3Entry *randmp3 = _pMP3List; + for ( int32 i = randval % nNumMP3s; i > 0; --i) + randmp3 = randmp3->pNext; + + if ( bUseRandomTable ) + _CurMP3Pos = AudioManager.m_anRandomTable[0] % randmp3->nTrackLength; + else + { + if ( localtm->tm_sec > 0 ) + { + int32 s = localtm->tm_sec; + _CurMP3Pos = s*s*s*s*s*s*s*s % randmp3->nTrackLength; + } + else + _CurMP3Pos = AudioManager.m_anRandomTable[0] % randmp3->nTrackLength; + } + } + else + _CurMP3Pos = 0; + + _bIsMp3Active = FALSE; + } + + TRACE("end"); + + return TRUE; +} + +void +cSampleManager::Terminate(void) +{ + for ( int32 i = 0; i < MAX_STREAMS; i++ ) + { + if ( mp3Stream[i] ) + { + AIL_pause_stream(mp3Stream[i], 1); + AIL_close_stream(mp3Stream[i]); + mp3Stream[i] = NULL; + } + } + + for ( int32 i = 0; i < MAX2DCHANNELS; i++ ) + { + if ( opened_2dsamples[i] ) + { + AIL_release_sample_handle(opened_2dsamples[i]); + opened_2dsamples[i] = NULL; + } + } + +#ifdef EXTERNAL_3D_SOUND + release_existing(); +#endif + + _DeleteMP3Entries(); + + if ( nSampleBankMemoryStartAddress[SFX_BANK_0] != 0 ) + { + AIL_mem_free_lock((void *)nSampleBankMemoryStartAddress[SFX_BANK_0]); + nSampleBankMemoryStartAddress[SFX_BANK_0] = 0; + } + + if ( nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] != 0 ) + { + AIL_mem_free_lock((void *)nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS]); + nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] = 0; + } + +#ifdef FIX_BUGS + if ( gPlayerTalkData != 0) + { + AIL_mem_free_lock(gPlayerTalkData); + gPlayerTalkData = 0; + } +#endif + + if ( DIG ) + { + AIL_close_digital_driver(DIG); + DIG = NULL; + } + + AIL_shutdown(); + + _bSampmanInitialised = FALSE; +} + +bool8 +cSampleManager::CheckForAnAudioFileOnCD(void) +{ +#if !defined(NO_CDCHECK) // TODO: check steam, probably GTAVC_STEAM_PATCH needs to be added + char filepath[MAX_PATH]; + FILE *f; + + strcpy(filepath, m_MiscomPath); + strcat(filepath, StreamedNameTable[STREAMED_SOUND_MISSION_COMPLETED4]); + + FILE *f = fopen(filepath, "rb"); + + if ( f ) + { + fclose(f); + DMAudio.SetMusicMasterVolume(FrontEndMenuManager.m_PrefsMusicVolume); + DMAudio.SetEffectsMasterVolume(FrontEndMenuManager.m_PrefsSfxVolume); + DMAudio.Service(); + + return TRUE; + } + + DMAudio.SetMusicMasterVolume(0); + DMAudio.SetEffectsMasterVolume(0); + DMAudio.Service(); + + return FALSE; + +#else + return TRUE; +#endif // #if !defined(NO_CDCHECK) +} + +char +cSampleManager::GetCDAudioDriveLetter(void) +{ + if ( strlen(m_MiscomPath) != 0 ) + return m_MiscomPath[0]; + else + return '\0'; +} + +void +cSampleManager::UpdateEffectsVolume(void) //[Y], cSampleManager::UpdateSoundBuffers ? +{ + if ( _bSampmanInitialised ) + { + for ( int32 i = 0; i < MAXCHANNELS+MAX2DCHANNELS; i++ ) + { +#ifdef EXTERNAL_3D_SOUND + if ( i < MAXCHANNELS ) + { + if ( opened_samples[i] && GetChannelUsedFlag(i) ) + { + if ( nChannelVolume[i] ) + { + AIL_set_3D_sample_volume(opened_samples[i], + m_nEffectsFadeVolume * nChannelVolume[i] * m_nEffectsVolume >> 14); + } + } + } + else +#endif + { + if ( opened_2dsamples[i - MAXCHANNELS] ) + { + if ( GetChannelUsedFlag(i - MAXCHANNELS) ) + { + if ( nChannelVolume[i - MAXCHANNELS] ) + { + AIL_set_sample_volume(opened_2dsamples[i - MAXCHANNELS], + m_nEffectsFadeVolume * nChannelVolume[i - MAXCHANNELS] * m_nEffectsVolume >> 14); + } + } + } + } + } + } +} + +void +cSampleManager::SetEffectsMasterVolume(uint8 nVolume) +{ + m_nEffectsVolume = nVolume; + UpdateEffectsVolume(); +} + +void +cSampleManager::SetMusicMasterVolume(uint8 nVolume) +{ + m_nMusicVolume = nVolume; +} + +void +cSampleManager::SetMP3BoostVolume(uint8 nVolume) +{ + m_nMP3BoostVolume = nVolume; +} + +void +cSampleManager::SetEffectsFadeVolume(uint8 nVolume) +{ + m_nEffectsFadeVolume = nVolume; + UpdateEffectsVolume(); +} + +void +cSampleManager::SetMusicFadeVolume(uint8 nVolume) +{ + m_nMusicFadeVolume = nVolume; +} + +void +cSampleManager::SetMonoMode(bool8 nMode) +{ + m_nMonoMode = nMode; +} + +bool8 +cSampleManager::LoadSampleBank(uint8 nBank) +{ + if ( CTimer::GetIsCodePaused() ) + return FALSE; + + if ( MusicManager.IsInitialised() + && MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE + && nBank != SFX_BANK_0 ) + { + return FALSE; + } + + if ( fseek(fpSampleDataHandle, nSampleBankDiscStartOffset[nBank], SEEK_SET) != 0 ) + return FALSE; + + if ( fread((void *)nSampleBankMemoryStartAddress[nBank], 1, nSampleBankSize[nBank],fpSampleDataHandle) != nSampleBankSize[nBank] ) + return FALSE; + + gBankLoaded[nBank] = LOADING_STATUS_LOADED; + + return TRUE; +} + +void +cSampleManager::UnloadSampleBank(uint8 nBank) +{ + gBankLoaded[nBank] = LOADING_STATUS_NOT_LOADED; +} + +int8 +cSampleManager::IsSampleBankLoaded(uint8 nBank) +{ + return gBankLoaded[nBank]; +} + +#ifdef FIX_BUGS +uint8 +cSampleManager::IsMissionAudioLoaded(uint8 nSlot, uint32 nSample) +{ + ASSERT(nSlot == MISSION_AUDIO_PLAYER_COMMENT); // only MISSION_AUDIO_PLAYER_COMMENT is supported on PC + + return nSample == gPlayerTalkSfx ? LOADING_STATUS_LOADED : LOADING_STATUS_NOT_LOADED; +} + +bool8 +cSampleManager::LoadMissionAudio(uint8 nSlot, uint32 nSample) +{ + ASSERT(nSlot == MISSION_AUDIO_PLAYER_COMMENT); // only MISSION_AUDIO_PLAYER_COMMENT is supported on PC + ASSERT(nSample < TOTAL_AUDIO_SAMPLES); + + if (fseek(fpSampleDataHandle, m_aSamples[nSample].nOffset, SEEK_SET) != 0) + return FALSE; + + if (fread(gPlayerTalkData, 1, m_aSamples[nSample].nSize, fpSampleDataHandle) != m_aSamples[nSample].nSize) + return FALSE; + + gPlayerTalkSfx = nSample; + + return TRUE; +} +#endif + +uint8 +cSampleManager::IsPedCommentLoaded(uint32 nComment) +{ + int8 slot; + + for ( int32 i = 0; i < _TODOCONST(3); i++ ) + { + slot = nCurrentPedSlot - i - 1; +#ifdef FIX_BUGS + if (slot < 0) + slot += ARRAY_SIZE(nPedSlotSfx); +#endif + if ( nComment == nPedSlotSfx[slot] ) + return LOADING_STATUS_LOADED; + } + + return LOADING_STATUS_NOT_LOADED; +} + +int32 +cSampleManager::_GetPedCommentSlot(uint32 nComment) +{ + int8 slot; + + for ( int32 i = 0; i < _TODOCONST(3); i++ ) + { + slot = nCurrentPedSlot - i - 1; +#ifdef FIX_BUGS + if (slot < 0) + slot += ARRAY_SIZE(nPedSlotSfx); +#endif + if ( nComment == nPedSlotSfx[slot] ) + return slot; + } + + return -1; +} + +bool8 +cSampleManager::LoadPedComment(uint32 nComment) +{ + if ( CTimer::GetIsCodePaused() ) + return FALSE; + + // no talking peds during cutsenes or the game end + if ( MusicManager.IsInitialised() ) + { + switch ( MusicManager.GetMusicMode() ) + { + case MUSICMODE_CUTSCENE: + { + return FALSE; + + break; + } + } + } + + if ( fseek(fpSampleDataHandle, m_aSamples[nComment].nOffset, SEEK_SET) != 0 ) + return FALSE; + + if ( fread((void *)(nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] + PED_BLOCKSIZE*nCurrentPedSlot), 1, m_aSamples[nComment].nSize, fpSampleDataHandle) != m_aSamples[nComment].nSize ) + return FALSE; + + nPedSlotSfxAddr[nCurrentPedSlot] = nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] + PED_BLOCKSIZE*nCurrentPedSlot; + nPedSlotSfx [nCurrentPedSlot] = nComment; + + if ( ++nCurrentPedSlot >= MAX_PEDSFX ) + nCurrentPedSlot = 0; + + return TRUE; +} + +int32 +cSampleManager::GetBankContainingSound(uint32 offset) +{ + if ( offset >= BankStartOffset[SFX_BANK_PED_COMMENTS] ) + return SFX_BANK_PED_COMMENTS; + + if ( offset >= BankStartOffset[SFX_BANK_0] ) + return SFX_BANK_0; + + return INVALID_SFX_BANK; +} + +uint32 +cSampleManager::GetSampleBaseFrequency(uint32 nSample) +{ + return m_aSamples[nSample].nFrequency; +} + +uint32 +cSampleManager::GetSampleLoopStartOffset(uint32 nSample) +{ + return m_aSamples[nSample].nLoopStart; +} + +int32 +cSampleManager::GetSampleLoopEndOffset(uint32 nSample) +{ + return m_aSamples[nSample].nLoopEnd; +} + +uint32 +cSampleManager::GetSampleLength(uint32 nSample) +{ + return m_aSamples[nSample].nSize >> 1; +} + +bool8 +cSampleManager::UpdateReverb(void) +{ +#ifdef EXTERNAL_3D_SOUND + if ( !usingEAX ) + return FALSE; + + if ( AudioManager.m_FrameCounter & 15 ) + return FALSE; + + float fRatio = 0.0f; + +#ifdef AUDIO_REFLECTIONS +#define MIN_DIST 0.5f +#define CALCULATE_RATIO(value, maxDist, maxRatio) (value > MIN_DIST && value < maxDist ? value / maxDist * maxRatio : 0) + + fRatio += CALCULATE_RATIO(AudioManager.m_afReflectionsDistances[REFLECTION_CEIL_NORTH], 10.0f, 1/2.f); + fRatio += CALCULATE_RATIO(AudioManager.m_afReflectionsDistances[REFLECTION_CEIL_SOUTH], 10.0f, 1/2.f); + fRatio += CALCULATE_RATIO(AudioManager.m_afReflectionsDistances[REFLECTION_CEIL_WEST], 10.0f, 1/2.f); + fRatio += CALCULATE_RATIO(AudioManager.m_afReflectionsDistances[REFLECTION_CEIL_EAST], 10.0f, 1/2.f); + + fRatio += CALCULATE_RATIO((AudioManager.m_afReflectionsDistances[REFLECTION_NORTH] + AudioManager.m_afReflectionsDistances[REFLECTION_SOUTH]) / 2.f, 4.0f, 1/3.f); + fRatio += CALCULATE_RATIO((AudioManager.m_afReflectionsDistances[REFLECTION_WEST] + AudioManager.m_afReflectionsDistances[REFLECTION_EAST]) / 2.f, 4.0f, 1/3.f); + +#undef CALCULATE_RATIO +#undef MIN_DIST +#endif + + fRatio = Clamp(fRatio, 0.0f, 0.6f); + + if ( fRatio == _fPrevEaxRatioDestination ) + return FALSE; + + if ( usingEAX3 ) + { + fRatio = Min(fRatio * 1.67f, 1.0f); + if ( EAX3ListenerInterpolate(&StartEAX3, &FinishEAX3, fRatio, &EAX3Params, false) ) + { + AIL_set_3D_provider_preference(opened_provider, "EAX all parameters", &EAX3Params); + _fEffectsLevel = fRatio * 0.75f; + } + } + else + { + if ( _usingMilesFast2D ) + _fEffectsLevel = fRatio * 0.8f; + else + _fEffectsLevel = fRatio * 0.22f; + } + _fEffectsLevel = Min(_fEffectsLevel, 1.0f); + + _fPrevEaxRatioDestination = fRatio; + + return TRUE; +#endif + return FALSE; +} + +void +cSampleManager::SetChannelReverbFlag(uint32 nChannel, bool8 nReverbFlag) +{ +#ifdef EXTERNAL_3D_SOUND + bool8 b2d = FALSE; + + switch ( nChannel ) + { + case CHANNEL_POLICE_RADIO: + { + b2d = TRUE; + break; + } + } + + if ( usingEAX ) + { + if ( nReverbFlag != FALSE ) + { + if ( !b2d ) + AIL_set_3D_sample_effects_level(opened_samples[nChannel], _fEffectsLevel); + } + else + { + if ( !b2d ) + AIL_set_3D_sample_effects_level(opened_samples[nChannel], 0.0f); + } + } +#endif +} + +bool8 +cSampleManager::InitialiseChannel(uint32 nChannel, uint32 nSfx, uint8 nBank) +{ +#ifdef EXTERNAL_3D_SOUND + bool8 b2d = FALSE; + + switch ( nChannel ) + { + case CHANNEL_POLICE_RADIO: + { + b2d = TRUE; + break; + } + } +#endif + + int32 addr; + + if ( nSfx < SAMPLEBANK_MAX ) + { + if ( !IsSampleBankLoaded(nBank) ) + return FALSE; + + addr = nSampleBankMemoryStartAddress[nBank] + m_aSamples[nSfx].nOffset - m_aSamples[BankStartOffset[nBank]].nOffset; + } +#ifdef FIX_BUGS + else if ( nSfx >= PLAYER_COMMENTS_START && nSfx <= PLAYER_COMMENTS_END ) + { + if ( !IsMissionAudioLoaded(MISSION_AUDIO_PLAYER_COMMENT, nSfx) ) + return FALSE; + + addr = (uintptr)gPlayerTalkData; + } +#endif + else + { + int32 i; + for ( i = 0; i < _TODOCONST(3); i++ ) + { + int32 slot = nCurrentPedSlot - i - 1; +#ifdef FIX_BUGS + if (slot < 0) + slot += ARRAY_SIZE(nPedSlotSfx); +#endif + if ( nSfx == nPedSlotSfx[slot] ) + { + addr = nPedSlotSfxAddr[slot]; + break; + } + } + + if (i == _TODOCONST(3)) + return FALSE; + } + +#ifdef EXTERNAL_3D_SOUND + if ( b2d ) + { +#endif + if ( opened_2dsamples[nChannel - MAXCHANNELS] ) + { + AIL_set_sample_address(opened_2dsamples[nChannel - MAXCHANNELS], (void *)addr, m_aSamples[nSfx].nSize); + return TRUE; + } + else + return FALSE; +#ifdef EXTERNAL_3D_SOUND + } + else + { + AILSOUNDINFO info; + + info.format = WAVE_FORMAT_PCM; + info.data_ptr = (void *)addr; + info.channels = 1; + info.data_len = m_aSamples[nSfx].nSize; + info.rate = m_aSamples[nSfx].nFrequency; + info.bits = 16; + + if ( AIL_set_3D_sample_info(opened_samples[nChannel], &info) == 0 ) + { + OutputDebugString(AIL_last_error()); + return FALSE; + } + + return TRUE; + } +#endif +} + +#ifdef EXTERNAL_3D_SOUND +void +cSampleManager::SetChannelEmittingVolume(uint32 nChannel, uint32 nVolume) +{ + uint32 vol = nVolume; + if ( vol > MAX_VOLUME ) vol = MAX_VOLUME; + + nChannelVolume[nChannel] = vol; + + // increase the volume for JB.MP3 and S4_BDBD.MP3 + if (MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE ) { + if (MusicManager.GetCurrentTrack() == STREAMED_SOUND_CUTSCENE_FINALE) + nChannelVolume[nChannel] = 0; + else + nChannelVolume[nChannel] >>= 2; + } + + if ( opened_samples[nChannel] ) + AIL_set_3D_sample_volume(opened_samples[nChannel], m_nEffectsFadeVolume*nChannelVolume[nChannel]*m_nEffectsVolume >> 14); + +} + +void +cSampleManager::SetChannel3DPosition(uint32 nChannel, float fX, float fY, float fZ) +{ + if ( opened_samples[nChannel] ) + AIL_set_3D_position(opened_samples[nChannel], -fX, fY, fZ); +} + +void +cSampleManager::SetChannel3DDistances(uint32 nChannel, float fMax, float fMin) +{ + if ( opened_samples[nChannel] ) + AIL_set_3D_sample_distances(opened_samples[nChannel], fMax, fMin); +} +#endif + +void +cSampleManager::SetChannelVolume(uint32 nChannel, uint32 nVolume) +{ + uint32 vol = nVolume; + if ( vol > MAX_VOLUME ) vol = MAX_VOLUME; + +#ifdef EXTERNAL_3D_SOUND + switch ( nChannel ) + { + case CHANNEL_POLICE_RADIO: + { +#endif + nChannelVolume[nChannel] = vol; + + // increase the volume for JB.MP3 and S4_BDBD.MP3 + if ( MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE + && MusicManager.GetCurrentTrack() != STREAMED_SOUND_CUTSCENE_FINALE ) + { + nChannelVolume[nChannel] >>= 2; + } + + if ( opened_2dsamples[nChannel - MAXCHANNELS] ) + { + AIL_set_sample_volume(opened_2dsamples[nChannel - MAXCHANNELS], + m_nEffectsFadeVolume*vol*m_nEffectsVolume >> 14); + } + +#ifdef EXTERNAL_3D_SOUND + break; + } + } +#endif +} + +void +cSampleManager::SetChannelPan(uint32 nChannel, uint32 nPan) +{ +#ifdef EXTERNAL_3D_SOUND + switch ( nChannel ) + { + case CHANNEL_POLICE_RADIO: + { +#endif +#if !defined(FIX_BUGS) && defined(EXTERNAL_3D_SOUND) + if ( opened_samples[nChannel - MAXCHANNELS] ) // BUG +#else + if ( opened_2dsamples[nChannel - MAXCHANNELS] ) +#endif + AIL_set_sample_pan(opened_2dsamples[nChannel - MAXCHANNELS], nPan); + +#ifdef EXTERNAL_3D_SOUND + break; + } + } +#endif +} + +void +cSampleManager::SetChannelFrequency(uint32 nChannel, uint32 nFreq) +{ +#ifdef EXTERNAL_3D_SOUND + bool8 b2d = FALSE; + + switch ( nChannel ) + { + case CHANNEL_POLICE_RADIO: + { + b2d = TRUE; + break; + } + } + + if ( b2d ) + { +#endif + if ( opened_2dsamples[nChannel - MAXCHANNELS] ) + AIL_set_sample_playback_rate(opened_2dsamples[nChannel - MAXCHANNELS], nFreq); +#ifdef EXTERNAL_3D_SOUND + } + else + { + if ( opened_samples[nChannel] ) + AIL_set_3D_sample_playback_rate(opened_samples[nChannel], nFreq); + } +#endif +} + +void +cSampleManager::SetChannelLoopPoints(uint32 nChannel, uint32 nLoopStart, int32 nLoopEnd) +{ +#ifdef EXTERNAL_3D_SOUND + bool8 b2d = FALSE; + + switch ( nChannel ) + { + case CHANNEL_POLICE_RADIO: + { + b2d = TRUE; + break; + } + } + + if ( b2d ) + { +#endif + if ( opened_2dsamples[nChannel - MAXCHANNELS] ) + AIL_set_sample_loop_block(opened_2dsamples[nChannel - MAXCHANNELS], nLoopStart, nLoopEnd); +#ifdef EXTERNAL_3D_SOUND + } + else + { + if ( opened_samples[nChannel] ) + AIL_set_3D_sample_loop_block(opened_samples[nChannel], nLoopStart, nLoopEnd); + } +#endif +} + +void +cSampleManager::SetChannelLoopCount(uint32 nChannel, uint32 nLoopCount) +{ +#ifdef EXTERNAL_3D_SOUND + bool8 b2d = FALSE; + + switch ( nChannel ) + { + case CHANNEL_POLICE_RADIO: + { + b2d = TRUE; + break; + } + } + + if ( b2d ) + { +#endif + if ( opened_2dsamples[nChannel - MAXCHANNELS] ) + AIL_set_sample_loop_count(opened_2dsamples[nChannel - MAXCHANNELS], nLoopCount); +#ifdef EXTERNAL_3D_SOUND + } + else + { + if ( opened_samples[nChannel] ) + AIL_set_3D_sample_loop_count(opened_samples[nChannel], nLoopCount); + } +#endif +} + +bool8 +cSampleManager::GetChannelUsedFlag(uint32 nChannel) +{ +#ifdef EXTERNAL_3D_SOUND + bool8 b2d = FALSE; + + switch ( nChannel ) + { + case CHANNEL_POLICE_RADIO: + { + b2d = TRUE; + break; + } + } + + if ( b2d ) + { +#endif + if ( opened_2dsamples[nChannel - MAXCHANNELS] ) + return AIL_sample_status(opened_2dsamples[nChannel - MAXCHANNELS]) == SMP_PLAYING; + else + return FALSE; +#ifdef EXTERNAL_3D_SOUND + } + else + { + if ( opened_samples[nChannel] ) + return AIL_3D_sample_status(opened_samples[nChannel]) == SMP_PLAYING; + else + return FALSE; + } +#endif + +} + +void +cSampleManager::StartChannel(uint32 nChannel) +{ +#ifdef EXTERNAL_3D_SOUND + bool8 b2d = FALSE; + + switch ( nChannel ) + { + case CHANNEL_POLICE_RADIO: + { + b2d = TRUE; + break; + } + } + + if ( b2d ) + { +#endif + if ( opened_2dsamples[nChannel - MAXCHANNELS] ) + AIL_start_sample(opened_2dsamples[nChannel - MAXCHANNELS]); +#ifdef EXTERNAL_3D_SOUND + } + else + { + if ( opened_samples[nChannel] ) + AIL_start_3D_sample(opened_samples[nChannel]); + } +#endif +} + +void +cSampleManager::StopChannel(uint32 nChannel) +{ +#ifdef EXTERNAL_3D_SOUND + bool8 b2d = FALSE; + + switch ( nChannel ) + { + case CHANNEL_POLICE_RADIO: + { + b2d = TRUE; + break; + } + } + + if ( b2d ) + { +#endif + if ( opened_2dsamples[nChannel - MAXCHANNELS] ) + AIL_end_sample(opened_2dsamples[nChannel - MAXCHANNELS]); +#ifdef EXTERNAL_3D_SOUND + } + else + { + if ( opened_samples[nChannel] ) + { + if ( AIL_3D_sample_status(opened_samples[nChannel]) == SMP_PLAYING ) + AIL_end_3D_sample(opened_samples[nChannel]); + } + } +#endif +} + +void +cSampleManager::PreloadStreamedFile(uint32 nFile, uint8 nStream) +{ + if ( m_bInitialised ) + { + if ( nFile < TOTAL_STREAMED_SOUNDS ) + { + if ( mp3Stream[nStream] ) + { + AIL_pause_stream(mp3Stream[nStream], 1); + AIL_close_stream(mp3Stream[nStream]); + } + + char filepath[MAX_PATH]; +#ifdef PS2_AUDIO_PATHS + strcpy(filepath, nFile < STREAMED_SOUND_MISSION_COMPLETED4 ? m_MP3FilesPath : (nFile < STREAMED_SOUND_MISSION_MOBR1 ? m_MiscomPath : m_WavFilesPath)); + strcat(filepath, PS2StreamedNameTable[nFile]); + + mp3Stream[nStream] = AIL_open_stream(DIG, filepath, 0); + + if ( !mp3Stream[nStream] ) +#endif + { + strcpy(filepath, nFile < STREAMED_SOUND_MISSION_COMPLETED4 ? m_MP3FilesPath : (nFile < STREAMED_SOUND_MISSION_MOBR1 ? m_MiscomPath : m_WavFilesPath)); + strcat(filepath, StreamedNameTable[nFile]); + + mp3Stream[nStream] = AIL_open_stream(DIG, filepath, 0); + } + + if ( mp3Stream[nStream] ) + { + AIL_set_stream_loop_count(mp3Stream[nStream], 1); + AIL_service_stream(mp3Stream[nStream], 1); + } + else + OutputDebugString(AIL_last_error()); + } + } +} + +void +cSampleManager::PauseStream(bool8 nPauseFlag, uint8 nStream) +{ + if ( m_bInitialised ) + { + if ( mp3Stream[nStream] ) + AIL_pause_stream(mp3Stream[nStream], nPauseFlag != FALSE); + } +} + +void +cSampleManager::StartPreloadedStreamedFile(uint8 nStream) +{ + if ( m_bInitialised ) + { + if ( mp3Stream[nStream] ) + AIL_start_stream(mp3Stream[nStream]); + } +} + +bool8 +cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream) +{ + uint32 i = 0; + uint32 position = nPos; + char filename[MAX_PATH]; + + if ( !m_bInitialised || nFile >= TOTAL_STREAMED_SOUNDS ) + return FALSE; + + if ( mp3Stream[nStream] ) + { + AIL_pause_stream(mp3Stream[nStream], 1); + AIL_close_stream(mp3Stream[nStream]); + } + if ( nFile == STREAMED_SOUND_RADIO_MP3_PLAYER ) + { + do + { + // Just switched to MP3 player + if ( !_bIsMp3Active && i == 0 ) + { + if ( nPos > nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER] ) + position = 0; + tMP3Entry *e = _pMP3List; + + // Try to continue from previous song, if already started + if(!_GetMP3PosFromStreamPos(&position, &e) && !e) { + nFile = 0; +#ifdef PS2_AUDIO_PATHS + strcpy(filename, m_MiscomPath); + strcat(filename, PS2StreamedNameTable[nFile]); + + mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0); + + if ( !mp3Stream[nStream] ) +#endif + { + strcpy(filename, m_MiscomPath); + strcat(filename, StreamedNameTable[nFile]); + mp3Stream[nStream] = + AIL_open_stream(DIG, filename, 0); + } + if(mp3Stream[nStream]) { + AIL_set_stream_loop_count(mp3Stream[nStream], nStreamLoopedFlag[nStream] ? 0 : 1); + nStreamLoopedFlag[nStream] = TRUE; + AIL_set_stream_ms_position(mp3Stream[nStream], position); + AIL_pause_stream(mp3Stream[nStream], 0); + return TRUE; + } + return FALSE; + + } else { + if ( e->pLinkPath != NULL ) + mp3Stream[nStream] = AIL_open_stream(DIG, e->pLinkPath, 0); + else { + strcpy(filename, _mp3DirectoryPath); + strcat(filename, e->aFilename); + + mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0); + } + + if ( mp3Stream[nStream] ) { + AIL_set_stream_loop_count(mp3Stream[nStream], 1); + AIL_set_stream_ms_position(mp3Stream[nStream], position); + AIL_pause_stream(mp3Stream[nStream], 0); + + _bIsMp3Active = TRUE; + + return TRUE; + } + // fall through, start playing from another song + } + } else { + if(++_CurMP3Index >= nNumMP3s) _CurMP3Index = 0; + + _CurMP3Pos = 0; + + tMP3Entry *mp3 = _GetMP3EntryByIndex(_CurMP3Index); + if ( !mp3 ) + { + mp3 = _pMP3List; + if ( !_pMP3List ) + { + nFile = 0; + _bIsMp3Active = FALSE; +#ifdef PS2_AUDIO_PATHS + strcpy(filename, m_MiscomPath); + strcat(filename, PS2StreamedNameTable[nFile]); + + mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0); + + if ( !mp3Stream[nStream] ) +#endif + { + strcpy(filename, m_MiscomPath); + strcat(filename, StreamedNameTable[nFile]); + mp3Stream[nStream] = + AIL_open_stream(DIG, filename, 0); + } + if(mp3Stream[nStream]) { + AIL_set_stream_loop_count( + mp3Stream[nStream], nStreamLoopedFlag[nStream] ? 0 : 1); + nStreamLoopedFlag[nStream] = TRUE; + AIL_set_stream_ms_position( + mp3Stream[nStream], position); + AIL_pause_stream(mp3Stream[nStream], + 0); + return TRUE; + } + return FALSE; + } + } + if(mp3->pLinkPath != NULL) + mp3Stream[nStream] = AIL_open_stream(DIG, mp3->pLinkPath, 0); + else { + strcpy(filename, _mp3DirectoryPath); + strcat(filename, mp3->aFilename); + + mp3Stream[nStream] = + AIL_open_stream(DIG, filename, 0); + } + + if(mp3Stream[nStream]) { + AIL_set_stream_loop_count(mp3Stream[nStream], 1); + AIL_set_stream_ms_position(mp3Stream[nStream], 0); + AIL_pause_stream(mp3Stream[nStream], 0); +#ifdef FIX_BUGS + _bIsMp3Active = TRUE; +#endif + return TRUE; + } + + } + _bIsMp3Active = FALSE; + } + while ( ++i < nNumMP3s ); + position = 0; + nFile = 0; + } +#ifdef PS2_AUDIO_PATHS + strcpy(filename, m_MiscomPath); + strcat(filename, PS2StreamedNameTable[nFile]); + + mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0); + + if ( !mp3Stream[nStream] ) +#endif + { + strcpy(filename, m_MiscomPath); + strcat(filename, StreamedNameTable[nFile]); + mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0); + } + + if ( mp3Stream[nStream] ) + { + AIL_set_stream_loop_count(mp3Stream[nStream], nStreamLoopedFlag[nStream] ? 0 : 1); + nStreamLoopedFlag[nStream] = TRUE; + AIL_set_stream_ms_position(mp3Stream[nStream], position); + AIL_pause_stream(mp3Stream[nStream], 0); + return TRUE; + } + return FALSE; +} + +void +cSampleManager::StopStreamedFile(uint8 nStream) +{ + if ( m_bInitialised ) + { + if ( mp3Stream[nStream] ) + { + AIL_pause_stream(mp3Stream[nStream], 1); + + AIL_close_stream(mp3Stream[nStream]); + mp3Stream[nStream] = NULL; + + if ( nStream == 0 ) + _bIsMp3Active = FALSE; + } + } +} + +int32 +cSampleManager::GetStreamedFilePosition(uint8 nStream) +{ + S32 currentms; + + if ( m_bInitialised ) + { + if ( mp3Stream[nStream] ) + { + if ( _bIsMp3Active ) + { + tMP3Entry *mp3 = _GetMP3EntryByIndex(_CurMP3Index); + + if ( mp3 != NULL ) + { + AIL_stream_ms_position(mp3Stream[nStream], NULL, ¤tms); + return currentms + mp3->nTrackStreamPos; + } + else + return 0; + } + else + { + AIL_stream_ms_position(mp3Stream[nStream], NULL, ¤tms); + return currentms; + } + } + } + + return 0; +} + +void +cSampleManager::SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, bool8 nEffectFlag, uint8 nStream) +{ + uint8 vol = nVolume; + float boostMult = 0.0f; + + if ( m_bInitialised ) + { + if ( vol > MAX_VOLUME ) vol = MAX_VOLUME; + + if ( MusicManager.GetRadioInCar() == USERTRACK && !MusicManager.CheckForMusicInterruptions() ) + boostMult = m_nMP3BoostVolume / 64.f; + + nStreamVolume[nStream] = vol; + nStreamPan[nStream] = nPan; + + if ( mp3Stream[nStream] ) + { + if ( nEffectFlag ) + { + if ( nStream == 1 || nStream == 2 ) + AIL_set_stream_volume(mp3Stream[nStream], 128*vol*m_nEffectsVolume >> 14); + else + AIL_set_stream_volume(mp3Stream[nStream], m_nEffectsFadeVolume*vol*m_nEffectsVolume >> 14); + } + else + AIL_set_stream_volume(mp3Stream[nStream], (m_nMusicFadeVolume*vol*(uint32)(m_nMusicVolume * boostMult + m_nMusicVolume)) >> 14); + + AIL_set_stream_pan(mp3Stream[nStream], nPan); + } + } +} + +int32 +cSampleManager::GetStreamedFileLength(uint8 nStream) +{ + if ( m_bInitialised ) + return nStreamLength[nStream]; + + return 0; +} + +bool8 +cSampleManager::IsStreamPlaying(uint8 nStream) +{ + if ( m_bInitialised ) + { + if ( mp3Stream[nStream] ) + { + if ( AIL_stream_status(mp3Stream[nStream]) == SMP_PLAYING ) + return TRUE; + else + return FALSE; + } + } + + return FALSE; +} + +bool8 +cSampleManager::InitialiseSampleBanks(void) +{ + int32 nBank = SFX_BANK_0; + + fpSampleDescHandle = fopen(SampleBankDescFilename, "rb"); + if ( fpSampleDescHandle == NULL ) + return FALSE; + + fpSampleDataHandle = fopen(SampleBankDataFilename, "rb"); + if ( fpSampleDataHandle == NULL ) + { + fclose(fpSampleDescHandle); + fpSampleDescHandle = NULL; + + return FALSE; + } + + fseek(fpSampleDataHandle, 0, SEEK_END); + _nSampleDataEndOffset = ftell(fpSampleDataHandle); + rewind(fpSampleDataHandle); + + fread(m_aSamples, sizeof(tSample), TOTAL_AUDIO_SAMPLES, fpSampleDescHandle); + + fclose(fpSampleDescHandle); + fpSampleDescHandle = NULL; + + for ( uint32 i = 0; i < TOTAL_AUDIO_SAMPLES; i++ ) + { +#ifdef FIX_BUGS + if (nBank >= MAX_SFX_BANKS) break; +#endif + if ( BankStartOffset[nBank] == BankStartOffset[SFX_BANK_0] + i ) + { + nSampleBankDiscStartOffset[nBank] = m_aSamples[i].nOffset; + nBank++; + } + } + + nSampleBankSize[SFX_BANK_0] = nSampleBankDiscStartOffset[SFX_BANK_PED_COMMENTS] - nSampleBankDiscStartOffset[SFX_BANK_0]; + nSampleBankSize[SFX_BANK_PED_COMMENTS] = _nSampleDataEndOffset - nSampleBankDiscStartOffset[SFX_BANK_PED_COMMENTS]; + + return TRUE; +} + + +void +cSampleManager::SetStreamedFileLoopFlag(bool8 nLoopFlag, uint8 nChannel) +{ + if (m_bInitialised) + nStreamLoopedFlag[nChannel] = nLoopFlag; +} + +#endif diff --git a/src/miami/audio/sampman_null.cpp b/src/miami/audio/sampman_null.cpp new file mode 100644 index 00000000..473191ee --- /dev/null +++ b/src/miami/audio/sampman_null.cpp @@ -0,0 +1,403 @@ +#include "common.h" +#if !defined(AUDIO_OAL) && !defined(AUDIO_MSS) +#include "sampman.h" +#include "AudioManager.h" + +cSampleManager SampleManager; +bool8 _bSampmanInitialised = FALSE; + +uint32 BankStartOffset[MAX_SFX_BANKS]; +uint32 nNumMP3s; + +cSampleManager::cSampleManager(void) +{ + ; +} + +cSampleManager::~cSampleManager(void) +{ + +} + +#ifdef EXTERNAL_3D_SOUND +void cSampleManager::SetSpeakerConfig(int32 nConfig) +{ + +} + +uint32 cSampleManager::GetMaximumSupportedChannels(void) +{ + return MAXCHANNELS; +} + +uint32 cSampleManager::GetNum3DProvidersAvailable() +{ + return 1; +} + +void cSampleManager::SetNum3DProvidersAvailable(uint32 num) +{ + +} + +char *cSampleManager::Get3DProviderName(uint8 id) +{ + static char name[64] = "NULL"; + return name; +} + +void cSampleManager::Set3DProviderName(uint8 id, char *name) +{ + +} + +int8 cSampleManager::GetCurrent3DProviderIndex(void) +{ + return 0; +} + +int8 cSampleManager::SetCurrent3DProvider(uint8 nProvider) +{ + return 0; +} +#endif + +bool8 +cSampleManager::IsMP3RadioChannelAvailable(void) +{ + return nNumMP3s != 0; +} + + +void cSampleManager::ReleaseDigitalHandle(void) +{ +} + +void cSampleManager::ReacquireDigitalHandle(void) +{ +} + +bool8 +cSampleManager::Initialise(void) +{ + return TRUE; +} + +void +cSampleManager::Terminate(void) +{ + +} + +bool8 cSampleManager::CheckForAnAudioFileOnCD(void) +{ + return TRUE; +} + +char cSampleManager::GetCDAudioDriveLetter(void) +{ + return '\0'; +} + +void +cSampleManager::UpdateEffectsVolume(void) +{ + +} + +void +cSampleManager::SetEffectsMasterVolume(uint8 nVolume) +{ +} + +void +cSampleManager::SetMusicMasterVolume(uint8 nVolume) +{ +} + +void +cSampleManager::SetMP3BoostVolume(uint8 nVolume) +{ +} + +void +cSampleManager::SetEffectsFadeVolume(uint8 nVolume) +{ +} + +void +cSampleManager::SetMusicFadeVolume(uint8 nVolume) +{ +} + +void +cSampleManager::SetMonoMode(bool8 nMode) +{ +} + +bool8 +cSampleManager::LoadSampleBank(uint8 nBank) +{ + ASSERT( nBank < MAX_SFX_BANKS ); + return FALSE; +} + +void +cSampleManager::UnloadSampleBank(uint8 nBank) +{ + ASSERT( nBank < MAX_SFX_BANKS ); +} + +int8 +cSampleManager::IsSampleBankLoaded(uint8 nBank) +{ + ASSERT( nBank < MAX_SFX_BANKS ); + + return LOADING_STATUS_NOT_LOADED; +} + +uint8 +cSampleManager::IsMissionAudioLoaded(uint8 nSlot, uint32 nSample) +{ + ASSERT(nSlot < MISSION_AUDIO_COUNT); + + return LOADING_STATUS_NOT_LOADED; +} + +bool8 +cSampleManager::LoadMissionAudio(uint8 nSlot, uint32 nSample) +{ + ASSERT(nSlot < MISSION_AUDIO_COUNT); + + return FALSE; +} + +uint8 +cSampleManager::IsPedCommentLoaded(uint32 nComment) +{ + ASSERT( nComment < TOTAL_AUDIO_SAMPLES ); + + return LOADING_STATUS_NOT_LOADED; +} + + +int32 +cSampleManager::_GetPedCommentSlot(uint32 nComment) +{ + return -1; +} + +bool8 +cSampleManager::LoadPedComment(uint32 nComment) +{ + ASSERT( nComment < TOTAL_AUDIO_SAMPLES ); + return FALSE; +} + +int32 +cSampleManager::GetBankContainingSound(uint32 offset) +{ + return INVALID_SFX_BANK; +} + +uint32 +cSampleManager::GetSampleBaseFrequency(uint32 nSample) +{ + ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); + return 0; +} + +uint32 +cSampleManager::GetSampleLoopStartOffset(uint32 nSample) +{ + ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); + return 0; +} + +int32 +cSampleManager::GetSampleLoopEndOffset(uint32 nSample) +{ + ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); + return 0; +} + +uint32 +cSampleManager::GetSampleLength(uint32 nSample) +{ + ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); + return 0; +} + +bool8 cSampleManager::UpdateReverb(void) +{ + return FALSE; +} + +void +cSampleManager::SetChannelReverbFlag(uint32 nChannel, bool8 nReverbFlag) +{ + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); +} + +bool8 +cSampleManager::InitialiseChannel(uint32 nChannel, uint32 nSfx, uint8 nBank) +{ + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); + return FALSE; +} + +#ifdef EXTERNAL_3D_SOUND +void +cSampleManager::SetChannelEmittingVolume(uint32 nChannel, uint32 nVolume) +{ + ASSERT( nChannel < MAXCHANNELS ); + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); +} + +void +cSampleManager::SetChannel3DPosition(uint32 nChannel, float fX, float fY, float fZ) +{ + ASSERT( nChannel < MAXCHANNELS ); + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); +} + +void +cSampleManager::SetChannel3DDistances(uint32 nChannel, float fMax, float fMin) +{ + ASSERT( nChannel < MAXCHANNELS ); + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); +} +#endif + +void +cSampleManager::SetChannelVolume(uint32 nChannel, uint32 nVolume) +{ + ASSERT( nChannel >= MAXCHANNELS ); + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); +} + +void +cSampleManager::SetChannelPan(uint32 nChannel, uint32 nPan) +{ + ASSERT( nChannel >= MAXCHANNELS ); + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); +} + +void +cSampleManager::SetChannelFrequency(uint32 nChannel, uint32 nFreq) +{ + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); +} + +void +cSampleManager::SetChannelLoopPoints(uint32 nChannel, uint32 nLoopStart, int32 nLoopEnd) +{ + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); +} + +void +cSampleManager::SetChannelLoopCount(uint32 nChannel, uint32 nLoopCount) +{ + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); +} + +bool8 +cSampleManager::GetChannelUsedFlag(uint32 nChannel) +{ + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); + + return FALSE; +} + +void +cSampleManager::StartChannel(uint32 nChannel) +{ + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); +} + +void +cSampleManager::StopChannel(uint32 nChannel) +{ + ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); +} + +void +cSampleManager::PreloadStreamedFile(uint32 nFile, uint8 nStream, uint32_t) +{ + ASSERT( nStream < MAX_STREAMS ); +} + +void +cSampleManager::PauseStream(bool8 nPauseFlag, uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); +} + +void +cSampleManager::StartPreloadedStreamedFile(uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); +} + +bool8 +cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); + + return FALSE; +} + +void +cSampleManager::StopStreamedFile(uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); +} + +int32 +cSampleManager::GetStreamedFilePosition(uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); + + return 0; +} + +void +cSampleManager::SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, bool8 nEffectFlag, uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); +} + +int32 +cSampleManager::GetStreamedFileLength(uint8 nStream) +{ + ASSERT( nStream < TOTAL_STREAMED_SOUNDS ); + + return 1; +} + +bool8 +cSampleManager::IsStreamPlaying(uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); + + return FALSE; +} + +bool8 +cSampleManager::InitialiseSampleBanks(void) +{ + + return TRUE; +} + +void +cSampleManager::SetStreamedFileLoopFlag(bool8 nLoopFlag, uint8 nChannel) +{ +} + +int8 cSampleManager::AutoDetect3DProviders() +{ + return -1; +} + +#endif diff --git a/src/miami/audio/sampman_oal.cpp b/src/miami/audio/sampman_oal.cpp new file mode 100644 index 00000000..9cf30832 --- /dev/null +++ b/src/miami/audio/sampman_oal.cpp @@ -0,0 +1,2082 @@ +//#define JUICY_OAL + +#ifdef AUDIO_OAL +#include + +#include "eax.h" +#include "eax-util.h" + +#ifdef _WIN32 +#include +#include +#include +#include +#include +#include + +// for user MP3s +#include +#include +#include +#else +#define _getcwd getcwd +#endif + +#if defined _MSC_VER && !defined CMAKE_NO_AUTOLINK +#pragma comment( lib, "OpenAL32.lib" ) +#endif + +#include "common.h" +#include "crossplatform.h" + +#include "sampman.h" + +#include "oal/oal_utils.h" +#include "oal/aldlist.h" +#include "oal/channel.h" + +#include +#ifdef MULTITHREADED_AUDIO +#include +#include +#include +#endif +#include "oal/stream.h" + +#include "AudioManager.h" +#include "MusicManager.h" +#include "Frontend.h" +#include "Timer.h" +#ifdef AUDIO_OAL_USE_OPUS +#include +#endif + +//TODO: fix eax3 reverb + +cSampleManager SampleManager; +bool8 _bSampmanInitialised = FALSE; + +uint32 BankStartOffset[MAX_SFX_BANKS]; + +int prevprovider=-1; +int curprovider=-1; +int usingEAX=0; +int usingEAX3=0; +//int speaker_type=0; +ALCdevice *ALDevice = NULL; +ALCcontext *ALContext = NULL; +unsigned int _maxSamples; +float _fPrevEaxRatioDestination; +bool _effectsSupported = false; +bool _usingEFX; +float _fEffectsLevel; +ALuint ALEffect = AL_EFFECT_NULL; +ALuint ALEffectSlot = AL_EFFECTSLOT_NULL; +struct +{ + const char *id; + char name[256]; + int sources; + bool bSupportsFx; +}providers[MAXPROVIDERS]; + +int defaultProvider; + + +char SampleBankDescFilename[] = "audio/sfx.SDT"; +char SampleBankDataFilename[] = "audio/sfx.RAW"; + +FILE *fpSampleDescHandle; +#ifdef OPUS_SFX +OggOpusFile *fpSampleDataHandle; +#else +FILE *fpSampleDataHandle; +#endif +int8 gBankLoaded [MAX_SFX_BANKS]; +int32 nSampleBankDiscStartOffset [MAX_SFX_BANKS]; +int32 nSampleBankSize [MAX_SFX_BANKS]; +uintptr nSampleBankMemoryStartAddress[MAX_SFX_BANKS]; +int32 _nSampleDataEndOffset; + +int32 nPedSlotSfx [MAX_PEDSFX]; +int32 nPedSlotSfxAddr[MAX_PEDSFX]; +uint8 nCurrentPedSlot; + +#ifdef FIX_BUGS +uint32 gPlayerTalkSfx = UINT32_MAX; +void *gPlayerTalkData = 0; +#endif + +CChannel aChannel[NUM_CHANNELS]; +uint8 nChannelVolume[NUM_CHANNELS]; + +uint32 nStreamLength[TOTAL_STREAMED_SOUNDS]; +ALuint ALStreamSources[MAX_STREAMS][2]; +ALuint ALStreamBuffers[MAX_STREAMS][NUM_STREAMBUFFERS]; + +struct tMP3Entry +{ + char aFilename[MAX_PATH]; + + uint32 nTrackLength; + uint32 nTrackStreamPos; + + tMP3Entry* pNext; + char* pLinkPath; +}; + +uint32 nNumMP3s; +tMP3Entry* _pMP3List; +char _mp3DirectoryPath[MAX_PATH]; +CStream *aStream[MAX_STREAMS]; +uint8 nStreamPan [MAX_STREAMS]; +uint8 nStreamVolume[MAX_STREAMS]; +bool8 nStreamLoopedFlag[MAX_STREAMS]; +uint32 _CurMP3Index; +int32 _CurMP3Pos; +bool8 _bIsMp3Active; +/////////////////////////////////////////////////////////////// +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +EAXLISTENERPROPERTIES StartEAX3 = + {26, 1.7f, 0.8f, -1000, -1000, -100, 4.42f, 0.14f, 1.00f, 429, 0.014f, 0.00f,0.00f,0.00f, 1023, 0.021f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 2727.1f, 250.0f, 0.00f, 0x3f }; + +EAXLISTENERPROPERTIES FinishEAX3 = + {26, 100.0f, 1.0f, 0, -1000, -2200, 20.0f, 1.39f, 1.00f, 1000, 0.069f, 0.00f,0.00f,0.00f, 400, 0.100f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 3.982f, 0.000f, -18.0f, 3530.8f, 417.9f, 6.70f, 0x3f }; + +EAXLISTENERPROPERTIES EAX3Params; + + +bool IsFXSupported() +{ + return _effectsSupported; // usingEAX || usingEAX3 || _usingEFX; +} + +void EAX_SetAll(const EAXLISTENERPROPERTIES *allparameters) +{ + if ( usingEAX || usingEAX3 ) + EAX3_Set(ALEffect, allparameters); + else + EFX_Set(ALEffect, allparameters); +} + +static void +add_providers() +{ + SampleManager.SetNum3DProvidersAvailable(0); + + static ALDeviceList DeviceList; + ALDeviceList *pDeviceList = &DeviceList; + + if ((pDeviceList) && (pDeviceList->GetNumDevices())) + { + const int devNumber = Min(pDeviceList->GetNumDevices(), MAXPROVIDERS); + int n = 0; + + //for (int i = 0; i < devNumber; i++) + int i = pDeviceList->GetDefaultDevice(); + { + if ( n < MAXPROVIDERS ) + { + providers[n].id = pDeviceList->GetDeviceName(i); + strcpy(providers[n].name, "OPENAL SOFT"); + providers[n].sources = pDeviceList->GetMaxNumSources(i); + SampleManager.Set3DProviderName(n, providers[n].name); + n++; + } + + if ( alGetEnumValue("AL_EFFECT_EAXREVERB") != 0 + || pDeviceList->IsExtensionSupported(i, ADEXT_EAX2) + || pDeviceList->IsExtensionSupported(i, ADEXT_EAX3) + || pDeviceList->IsExtensionSupported(i, ADEXT_EAX4) + || pDeviceList->IsExtensionSupported(i, ADEXT_EAX5) ) + { + providers[n - 1].bSupportsFx = true; + if ( n < MAXPROVIDERS ) + { + providers[n].id = pDeviceList->GetDeviceName(i); + strcpy(providers[n].name, "OPENAL SOFT EAX"); + providers[n].sources = pDeviceList->GetMaxNumSources(i); + providers[n].bSupportsFx = true; + SampleManager.Set3DProviderName(n, providers[n].name); + n++; + } + + if ( n < MAXPROVIDERS ) + { + providers[n].id = pDeviceList->GetDeviceName(i); + strcpy(providers[n].name, "OPENAL SOFT EAX3"); + providers[n].sources = pDeviceList->GetMaxNumSources(i); + providers[n].bSupportsFx = true; + SampleManager.Set3DProviderName(n, providers[n].name); + n++; + } + } + } + SampleManager.SetNum3DProvidersAvailable(n); + + for(int j=n;jGetDefaultDevice(); + //if ( defaultProvider > MAXPROVIDERS ) + defaultProvider = 0; + } +} + +static void +release_existing() +{ + if ( IsFXSupported() ) + { + if ( alIsEffect(ALEffect) ) + { + alEffecti(ALEffect, AL_EFFECT_TYPE, AL_EFFECT_NULL); + } + + if (alIsAuxiliaryEffectSlot(ALEffectSlot)) + { + alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL); + } + } + + DEV("release_existing()\n"); +} + +static bool8 +set_new_provider(int index) +{ + if ( curprovider == index ) + return TRUE; + + curprovider = index; + + release_existing(); + + if ( curprovider != -1 ) + { + DEV("set_new_provider()\n"); + + usingEAX = 0; + usingEAX3 = 0; + _usingEFX = false; + + if ( !strcmp(&providers[index].name[strlen(providers[index].name) - strlen(" EAX3")], " EAX3") + && alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) ) + { + + usingEAX = 1; + usingEAX3 = 1; + alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, ALEffect); + EAX_SetAll(&FinishEAX3); + + DEV("EAX3\n"); + } + else if ( alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) ) + { + + if ( !strcmp(&providers[index].name[strlen(providers[index].name) - strlen(" EAX")], " EAX")) + { + usingEAX = 1; + DEV("EAX1\n"); + } + else + { + _usingEFX = true; + DEV("EFX\n"); + } + alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, ALEffect); + EAX_SetAll(&EAX30_ORIGINAL_PRESETS[EAX_ENVIRONMENT_CAVE]); + } + + //SampleManager.SetSpeakerConfig(speaker_type); + + if ( IsFXSupported() ) + { + for ( int32 i = 0; i < MAXCHANNELS; i++ ) + aChannel[i].SetReverbMix(ALEffectSlot, 0.0f); + } + + return TRUE; + } + + return FALSE; +} + +static bool8 +IsThisTrackAt16KHz(uint32 track) +{ + return track == STREAMED_SOUND_RADIO_KCHAT || track == STREAMED_SOUND_RADIO_VCPR || track == STREAMED_SOUND_RADIO_POLICE; +} + +cSampleManager::cSampleManager(void) +{ + ; +} + +cSampleManager::~cSampleManager(void) +{ + +} + +void cSampleManager::SetSpeakerConfig(int32 nConfig) +{ + +} + +uint32 cSampleManager::GetMaximumSupportedChannels(void) +{ + if ( _maxSamples > MAXCHANNELS ) + return MAXCHANNELS; + + return _maxSamples; +} + +uint32 cSampleManager::GetNum3DProvidersAvailable() +{ + return m_nNumberOfProviders; +} + +void cSampleManager::SetNum3DProvidersAvailable(uint32 num) +{ + m_nNumberOfProviders = num; +} + +char *cSampleManager::Get3DProviderName(uint8 id) +{ + return m_aAudioProviders[id]; +} + +void cSampleManager::Set3DProviderName(uint8 id, char *name) +{ + m_aAudioProviders[id] = name; +} + +int8 cSampleManager::GetCurrent3DProviderIndex(void) +{ + return curprovider; +} + +int8 cSampleManager::SetCurrent3DProvider(uint8 nProvider) +{ + int savedprovider = curprovider; + + nProvider = Clamp(nProvider, 0, m_nNumberOfProviders - 1); + + if ( set_new_provider(nProvider) ) + return curprovider; + else if ( savedprovider != -1 && savedprovider < m_nNumberOfProviders && set_new_provider(savedprovider) ) + return curprovider; + else + return curprovider; +} + +int8 +cSampleManager::AutoDetect3DProviders() +{ + if (!AudioManager.IsAudioInitialised()) + return -1; + + if (defaultProvider >= 0 && defaultProvider < m_nNumberOfProviders) { + if (set_new_provider(defaultProvider)) + return defaultProvider; + } + + for (uint32 i = 0; i < GetNum3DProvidersAvailable(); i++) + { + char* providername = Get3DProviderName(i); + + if (!strcasecmp(providername, "OPENAL SOFT")) { + SetCurrent3DProvider(i); + if (GetCurrent3DProviderIndex() == i) + return i; + } + } + + return -1; +} + +static bool8 +_ResolveLink(char const *path, char *out) +{ +#ifdef _WIN32 + size_t len = strlen(path); + if (len < 4 || strcmp(&path[len - 4], ".lnk") != 0) + return FALSE; + + IShellLink* psl; + WIN32_FIND_DATA fd; + char filepath[MAX_PATH]; + + CoInitialize(NULL); + + if (SUCCEEDED( CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl ) )) + { + IPersistFile *ppf; + + if (SUCCEEDED(psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf))) + { + WCHAR wpath[MAX_PATH]; + + MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, MAX_PATH); + + if (SUCCEEDED(ppf->Load(wpath, STGM_READ))) + { + /* Resolve the link */ + if (SUCCEEDED(psl->Resolve(NULL, SLR_ANY_MATCH|SLR_NO_UI|SLR_NOSEARCH))) + { + strcpy(filepath, path); + + if (SUCCEEDED(psl->GetPath(filepath, MAX_PATH, &fd, SLGP_UNCPRIORITY))) + { + OutputDebugString(fd.cFileName); + + strcpy(out, filepath); + // FIX: Release the objects. Taken from SA. +#ifdef FIX_BUGS + ppf->Release(); + psl->Release(); +#endif + return TRUE; + } + } + } + + ppf->Release(); + } + psl->Release(); + } + + return FALSE; +#else + struct stat sb; + + if (lstat(path, &sb) == -1) { + perror("lstat: "); + return FALSE; + } + + if (S_ISLNK(sb.st_mode)) { + char* linkname = (char*)alloca(sb.st_size + 1); + if (linkname == NULL) { + fprintf(stderr, "insufficient memory\n"); + return FALSE; + } + + if (readlink(path, linkname, sb.st_size + 1) < 0) { + perror("readlink: "); + return FALSE; + } + linkname[sb.st_size] = '\0'; + strcpy(out, linkname); + return TRUE; + } else { + return FALSE; + } +#endif +} + +static void +_FindMP3s(void) +{ + tMP3Entry *pList; + bool8 bShortcut; + bool8 bInitFirstEntry; + HANDLE hFind; + char path[MAX_PATH]; + int total_ms; + WIN32_FIND_DATA fd; + char filepath[MAX_PATH + sizeof(fd.cFileName)]; + + if (getcwd(_mp3DirectoryPath, MAX_PATH) == NULL) { + perror("getcwd: "); + return; + } + + if (strlen(_mp3DirectoryPath) + 1 > MAX_PATH - 10) { + // This is not gonna end well + printf("MP3 folder path is too long, no place left for file names. MP3 finding aborted.\n"); + return; + } + + OutputDebugString("Finding MP3s..."); + strcpy(path, _mp3DirectoryPath); + strcat(path, "\\MP3\\"); + +#if !defined(_WIN32) + char *actualPath = casepath(path); + if (actualPath) { + strcpy(path, actualPath); + free(actualPath); + } +#endif + + strcpy(_mp3DirectoryPath, path); + OutputDebugString(_mp3DirectoryPath); + + strcat(path, "*"); + + hFind = FindFirstFile(path, &fd); + + if ( hFind == INVALID_HANDLE_VALUE ) + { + return; + } + + bShortcut = FALSE; + bInitFirstEntry = TRUE; + + do + { + strcpy(filepath, _mp3DirectoryPath); + strcat(filepath, fd.cFileName); + + if (!strcmp(fd.cFileName, ".") || !strcmp(fd.cFileName, "..")) + continue; + + size_t filepathlen = strlen(filepath); + + if ( bInitFirstEntry ) + { + if (filepathlen > 0) + { + if (_ResolveLink(filepath, filepath)) + { + OutputDebugString("Resolving Link"); + OutputDebugString(filepath); + bShortcut = TRUE; + } + else + { + bShortcut = FALSE; + if (filepathlen > MAX_PATH) { + continue; + } + } + if (aStream[0] && aStream[0]->Open(filepath)) + { + total_ms = aStream[0]->GetLengthMS(); + aStream[0]->Close(); + + OutputDebugString(fd.cFileName); + + _pMP3List = new tMP3Entry; + + if (_pMP3List == NULL) + break; + + nNumMP3s = 1; + + strcpy(_pMP3List->aFilename, fd.cFileName); + + _pMP3List->nTrackLength = total_ms; + _pMP3List->pNext = NULL; + + if (bShortcut) + { + _pMP3List->pLinkPath = new char[MAX_PATH + sizeof(fd.cFileName)]; + strcpy(_pMP3List->pLinkPath, filepath); + } + else + { + _pMP3List->pLinkPath = NULL; + } + + pList = _pMP3List; + + bInitFirstEntry = FALSE; + } + else + { + strcat(filepath, " - NOT A VALID MP3"); + OutputDebugString(filepath); + } + } + else + break; + } + else + { + if ( filepathlen > 0 ) + { + if ( _ResolveLink(filepath, filepath) ) + { + OutputDebugString("Resolving Link"); + OutputDebugString(filepath); + bShortcut = TRUE; + } + else + bShortcut = FALSE; + + if (aStream[0] && aStream[0]->Open(filepath)) + { + total_ms = aStream[0]->GetLengthMS(); + aStream[0]->Close(); + + OutputDebugString(fd.cFileName); + + pList->pNext = new tMP3Entry; + + tMP3Entry *e = pList->pNext; + + if ( e == NULL ) + break; + + pList = pList->pNext; + + strcpy(e->aFilename, fd.cFileName); + e->nTrackLength = total_ms; + e->pNext = NULL; + + if ( bShortcut ) + { + e->pLinkPath = new char [MAX_PATH + sizeof(fd.cFileName)]; + strcpy(e->pLinkPath, filepath); + } + else + { + e->pLinkPath = NULL; + } + + nNumMP3s++; + + OutputDebugString(fd.cFileName); + } + else + { + strcat(filepath, " - NOT A VALID MP3"); + OutputDebugString(filepath); + } + } + } + } while (FindNextFile(hFind, &fd)); + + FindClose(hFind); +} + +static void +_DeleteMP3Entries(void) +{ + tMP3Entry *e = _pMP3List; + + while ( e != NULL ) + { + tMP3Entry *next = e->pNext; + + if ( next == NULL ) + next = NULL; + + if ( e->pLinkPath != NULL ) + { +#ifndef FIX_BUGS + delete e->pLinkPath; // BUG: should be delete [] +#else + delete[] e->pLinkPath; +#endif + e->pLinkPath = NULL; + } + + delete e; + + if ( next ) + e = next; + else + e = NULL; + + nNumMP3s--; + } + + + if ( nNumMP3s != 0 ) + { + OutputDebugString("Not all MP3 entries were deleted"); + nNumMP3s = 0; + } + + _pMP3List = NULL; +} + +static tMP3Entry * +_GetMP3EntryByIndex(uint32 idx) +{ + uint32 n = ( idx < nNumMP3s ) ? idx : 0; + + if ( _pMP3List != NULL ) + { + tMP3Entry *e = _pMP3List; + + for ( uint32 i = 0; i < n; i++ ) + e = e->pNext; + + return e; + + } + + return NULL; +} + +static inline bool8 +_GetMP3PosFromStreamPos(uint32 *pPosition, tMP3Entry **pEntry) +{ + _CurMP3Index = 0; + + for ( *pEntry = _pMP3List; *pEntry != NULL; *pEntry = (*pEntry)->pNext ) + { + if ( *pPosition >= (*pEntry)->nTrackStreamPos + && *pPosition < (*pEntry)->nTrackLength + (*pEntry)->nTrackStreamPos ) + { + *pPosition -= (*pEntry)->nTrackStreamPos; + _CurMP3Pos = *pPosition; + + return TRUE; + } + + _CurMP3Index++; + } + + *pPosition = 0; + *pEntry = _pMP3List; + _CurMP3Pos = 0; + _CurMP3Index = 0; + + return FALSE; +} + +bool8 +cSampleManager::IsMP3RadioChannelAvailable(void) +{ + return nNumMP3s != 0; +} + + +void cSampleManager::ReleaseDigitalHandle(void) +{ + // TODO? alcSuspendContext +} + +void cSampleManager::ReacquireDigitalHandle(void) +{ + // TODO? alcProcessContext +} + +bool8 +cSampleManager::Initialise(void) +{ + if ( _bSampmanInitialised ) + return TRUE; + + EFXInit(); + + for(int i = 0; i < MAX_STREAMS; i++) + aStream[i] = new CStream(ALStreamSources[i], ALStreamBuffers[i]); + + CStream::Initialise(); + + { + for ( int32 i = 0; i < TOTAL_AUDIO_SAMPLES; i++ ) + { + m_aSamples[i].nOffset = 0; + m_aSamples[i].nSize = 0; + m_aSamples[i].nFrequency = 22050; + m_aSamples[i].nLoopStart = 0; + m_aSamples[i].nLoopEnd = -1; + } + + m_nEffectsVolume = MAX_VOLUME; + m_nMusicVolume = MAX_VOLUME; + m_nEffectsFadeVolume = MAX_VOLUME; + m_nMusicFadeVolume = MAX_VOLUME; + + m_nMonoMode = 0; + } + + { + curprovider = -1; + prevprovider = -1; + + _usingEFX = false; + usingEAX =0; + usingEAX3=0; + + _fEffectsLevel = 0.0f; + + _maxSamples = 0; + + ALDevice = NULL; + ALContext = NULL; + } + + { + fpSampleDescHandle = NULL; + fpSampleDataHandle = NULL; + + for ( int32 i = 0; i < MAX_SFX_BANKS; i++ ) + { + gBankLoaded[i] = LOADING_STATUS_NOT_LOADED; + nSampleBankDiscStartOffset[i] = 0; + nSampleBankSize[i] = 0; + nSampleBankMemoryStartAddress[i] = 0; + } + } + + { + for ( int32 i = 0; i < MAX_PEDSFX; i++ ) + { + nPedSlotSfx[i] = NO_SAMPLE; + nPedSlotSfxAddr[i] = 0; + } + + nCurrentPedSlot = 0; + } + + { + for ( int32 i = 0; i < NUM_CHANNELS; i++ ) + nChannelVolume[i] = 0; + } + + add_providers(); + + { + int index = 0; + _maxSamples = Min(MAXCHANNELS, providers[index].sources); + + ALCint attr[] = {ALC_FREQUENCY,MAX_FREQ, + ALC_MONO_SOURCES, MAX_DIGITAL_MIXER_CHANNELS - MAX2DCHANNELS, + ALC_STEREO_SOURCES, MAX2DCHANNELS, + 0, + }; + + ALDevice = alcOpenDevice(providers[index].id); + ASSERT(ALDevice != NULL); + + ALContext = alcCreateContext(ALDevice, attr); + ASSERT(ALContext != NULL); + + alcMakeContextCurrent(ALContext); + + const char* ext=(const char*)alGetString(AL_EXTENSIONS); + ASSERT(strstr(ext,"AL_SOFT_loop_points")!=NULL); + if ( strstr(ext,"AL_SOFT_loop_points")==NULL ) + { + Terminate(); + return FALSE; + } + + alListenerf (AL_GAIN, 1.0f); + alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f); + alListener3f(AL_VELOCITY, 0.0f, 0.0f, 0.0f); + ALfloat orientation[6] = { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f }; + alListenerfv(AL_ORIENTATION, orientation); + + alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); + + if ( alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) ) + { + _effectsSupported = providers[index].bSupportsFx; + alGenAuxiliaryEffectSlots(1, &ALEffectSlot); + alGenEffects(1, &ALEffect); + } + + alGenSources(MAX_STREAMS*2, ALStreamSources[0]); + for ( int32 i = 0; i < MAX_STREAMS; i++ ) + { + alGenBuffers(NUM_STREAMBUFFERS, ALStreamBuffers[i]); + alSourcei(ALStreamSources[i][0], AL_SOURCE_RELATIVE, AL_TRUE); + alSource3f(ALStreamSources[i][0], AL_POSITION, 0.0f, 0.0f, 0.0f); + alSourcef(ALStreamSources[i][0], AL_GAIN, 1.0f); + alSourcei(ALStreamSources[i][1], AL_SOURCE_RELATIVE, AL_TRUE); + alSource3f(ALStreamSources[i][1], AL_POSITION, 0.0f, 0.0f, 0.0f); + alSourcef(ALStreamSources[i][1], AL_GAIN, 1.0f); + } + + CChannel::InitChannels(); + + for ( int32 i = 0; i < MAXCHANNELS; i++ ) + aChannel[i].Init(i); + for ( int32 i = 0; i < MAX2DCHANNELS; i++ ) + aChannel[MAXCHANNELS+i].Init(MAXCHANNELS+i, true); + + if ( IsFXSupported() ) + { + /**/ + alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, ALEffect); + /**/ + + for ( int32 i = 0; i < MAXCHANNELS; i++ ) + aChannel[i].SetReverbMix(ALEffectSlot, 0.0f); + } + } + + { + for ( int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++ ) + nStreamLength[i] = 0; + } + +#ifdef AUDIO_CACHE + FILE *cacheFile = fcaseopen("audio\\sound.cache", "rb"); + if (cacheFile) { + debug("Loadind audio cache (If game crashes around here, then your cache is corrupted, remove audio/sound.cache)\n"); + fread(nStreamLength, sizeof(uint32), TOTAL_STREAMED_SOUNDS, cacheFile); + fclose(cacheFile); + } else + { + debug("Cannot load audio cache\n"); +#endif + + for ( int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++ ) + { + if ( aStream[0] && ( +#ifdef PS2_AUDIO_PATHS + aStream[0]->Open(PS2StreamedNameTable[i], IsThisTrackAt16KHz(i) ? 16000 : 32000) || +#endif + aStream[0]->Open(StreamedNameTable[i], IsThisTrackAt16KHz(i) ? 16000 : 32000)) ) + { + uint32 tatalms = aStream[0]->GetLengthMS(); + aStream[0]->Close(); + + nStreamLength[i] = tatalms; + } else + USERERROR("Can't open '%s'\n", StreamedNameTable[i]); + } +#ifdef AUDIO_CACHE + cacheFile = fcaseopen("audio\\sound.cache", "wb"); + if(cacheFile) { + debug("Saving audio cache\n"); + fwrite(nStreamLength, sizeof(uint32), TOTAL_STREAMED_SOUNDS, cacheFile); + fclose(cacheFile); + } else { + debug("Cannot save audio cache\n"); + } + } +#endif + + { + if ( !InitialiseSampleBanks() ) + { + Terminate(); + return FALSE; + } + + nSampleBankMemoryStartAddress[SFX_BANK_0] = (uintptr)malloc(nSampleBankSize[SFX_BANK_0]); + ASSERT(nSampleBankMemoryStartAddress[SFX_BANK_0] != 0); + + if ( nSampleBankMemoryStartAddress[SFX_BANK_0] == 0 ) + { + Terminate(); + return FALSE; + } + + nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] = (uintptr)malloc(PED_BLOCKSIZE*MAX_PEDSFX); + ASSERT(nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] != 0); + +#ifdef FIX_BUGS + // Find biggest player comment + uint32 nMaxPedSize = 0; + for (uint32 i = PLAYER_COMMENTS_START; i <= PLAYER_COMMENTS_END; i++) + nMaxPedSize = Max(nMaxPedSize, m_aSamples[i].nSize); + + gPlayerTalkData = malloc(nMaxPedSize); + ASSERT(gPlayerTalkData != 0); +#endif + + LoadSampleBank(SFX_BANK_0); + } + + { + for ( int32 i = 0; i < MAX_STREAMS; i++ ) + { + aStream[i]->Close(); + + nStreamVolume[i] = 100; + nStreamPan[i] = 63; + } + } + + { + _bSampmanInitialised = TRUE; + + if ( defaultProvider >= 0 && defaultProvider < m_nNumberOfProviders ) + { + set_new_provider(defaultProvider); + } + else + { + Terminate(); + return FALSE; + } + } + + { + nNumMP3s = 0; + + _pMP3List = NULL; + + _FindMP3s(); + + if ( nNumMP3s != 0 ) + { + nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER] = 0; + + for ( tMP3Entry *e = _pMP3List; e != NULL; e = e->pNext ) + { + e->nTrackStreamPos = nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER]; + nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER] += e->nTrackLength; + } + + time_t t = time(NULL); + tm *localtm; + bool8 bUseRandomTable; + + if ( t == -1 ) + bUseRandomTable = TRUE; + else + { + bUseRandomTable = FALSE; + localtm = localtime(&t); + } + + int32 randval; + if ( bUseRandomTable ) + randval = AudioManager.m_anRandomTable[1]; + else + randval = localtm->tm_sec * localtm->tm_min; + + _CurMP3Index = randval % nNumMP3s; + + tMP3Entry *randmp3 = _pMP3List; + for ( int32 i = randval % nNumMP3s; i > 0; --i) + randmp3 = randmp3->pNext; + + if ( bUseRandomTable ) + _CurMP3Pos = AudioManager.m_anRandomTable[0] % randmp3->nTrackLength; + else + { + if ( localtm->tm_sec > 0 ) + { + int32 s = localtm->tm_sec; + _CurMP3Pos = s*s*s*s*s*s*s*s % randmp3->nTrackLength; + } + else + _CurMP3Pos = AudioManager.m_anRandomTable[0] % randmp3->nTrackLength; + } + } + else + _CurMP3Pos = 0; + + _bIsMp3Active = FALSE; + } + + return TRUE; +} + +void +cSampleManager::Terminate(void) +{ + for (int32 i = 0; i < MAX_STREAMS; i++) + aStream[i]->Close(); + + for ( int32 i = 0; i < NUM_CHANNELS; i++ ) + aChannel[i].Term(); + + if ( IsFXSupported() ) + { + if ( alIsEffect(ALEffect) ) + { + alEffecti(ALEffect, AL_EFFECT_TYPE, AL_EFFECT_NULL); + alDeleteEffects(1, &ALEffect); + ALEffect = AL_EFFECT_NULL; + } + + if (alIsAuxiliaryEffectSlot(ALEffectSlot)) + { + alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL); + + alDeleteAuxiliaryEffectSlots(1, &ALEffectSlot); + ALEffectSlot = AL_EFFECTSLOT_NULL; + } + } + + for ( int32 i = 0; i < MAX_STREAMS; i++ ) + { + alDeleteBuffers(NUM_STREAMBUFFERS, ALStreamBuffers[i]); + } + alDeleteSources(MAX_STREAMS*2, ALStreamSources[0]); + + CChannel::DestroyChannels(); + + if ( ALContext ) + { + alcMakeContextCurrent(NULL); + alcSuspendContext(ALContext); + alcDestroyContext(ALContext); + } + if ( ALDevice ) + alcCloseDevice(ALDevice); + + ALDevice = NULL; + ALContext = NULL; + + _fPrevEaxRatioDestination = 0.0f; + _usingEFX = false; + _fEffectsLevel = 0.0f; + + _DeleteMP3Entries(); + + CStream::Terminate(); + + for(int32 i = 0; i < MAX_STREAMS; i++) + delete aStream[i]; + + if ( nSampleBankMemoryStartAddress[SFX_BANK_0] != 0 ) + { + free((void *)nSampleBankMemoryStartAddress[SFX_BANK_0]); + nSampleBankMemoryStartAddress[SFX_BANK_0] = 0; + } + + if ( nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] != 0 ) + { + free((void *)nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS]); + nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] = 0; + } + +#ifdef FIX_BUGS + if ( gPlayerTalkData != 0 ) + { + free(gPlayerTalkData); + gPlayerTalkData = 0; + } +#endif + + _bSampmanInitialised = FALSE; +} + +bool8 cSampleManager::CheckForAnAudioFileOnCD(void) +{ + return TRUE; +} + +char cSampleManager::GetCDAudioDriveLetter(void) +{ + return '\0'; +} + +void +cSampleManager::UpdateEffectsVolume(void) +{ + if ( _bSampmanInitialised ) + { + for ( int32 i = 0; i < NUM_CHANNELS; i++ ) + { + if ( GetChannelUsedFlag(i) ) + { + if ( nChannelVolume[i] != 0 ) + aChannel[i].SetVolume(m_nEffectsFadeVolume*nChannelVolume[i]*m_nEffectsVolume >> 14); + } + } + } +} + +void +cSampleManager::SetEffectsMasterVolume(uint8 nVolume) +{ + m_nEffectsVolume = nVolume; + UpdateEffectsVolume(); +} + +void +cSampleManager::SetMusicMasterVolume(uint8 nVolume) +{ + m_nMusicVolume = nVolume; +} + +void +cSampleManager::SetMP3BoostVolume(uint8 nVolume) +{ + m_nMP3BoostVolume = nVolume; +} + +void +cSampleManager::SetEffectsFadeVolume(uint8 nVolume) +{ + m_nEffectsFadeVolume = nVolume; + UpdateEffectsVolume(); +} + +void +cSampleManager::SetMusicFadeVolume(uint8 nVolume) +{ + m_nMusicFadeVolume = nVolume; +} + +void +cSampleManager::SetMonoMode(bool8 nMode) +{ + m_nMonoMode = nMode; +} + +bool8 +cSampleManager::LoadSampleBank(uint8 nBank) +{ + ASSERT( nBank < MAX_SFX_BANKS); + + if ( CTimer::GetIsCodePaused() ) + return FALSE; + + if ( MusicManager.IsInitialised() + && MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE + && nBank != SFX_BANK_0 ) + { + return FALSE; + } + +#ifdef OPUS_SFX + int samplesRead = 0; + int samplesSize = nSampleBankSize[nBank] / 2; + op_pcm_seek(fpSampleDataHandle, 0); + while (samplesSize > 0) { + int size = op_read(fpSampleDataHandle, (opus_int16 *)(nSampleBankMemoryStartAddress[nBank] + samplesRead), samplesSize, NULL); + if (size <= 0) { + // huh? + //assert(0); + break; + } + samplesRead += size*2; + samplesSize -= size; + } +#else + if ( fseek(fpSampleDataHandle, nSampleBankDiscStartOffset[nBank], SEEK_SET) != 0 ) + return FALSE; + + if ( fread((void *)nSampleBankMemoryStartAddress[nBank], 1, nSampleBankSize[nBank], fpSampleDataHandle) != nSampleBankSize[nBank] ) + return FALSE; +#endif + gBankLoaded[nBank] = LOADING_STATUS_LOADED; + + return TRUE; +} + +void +cSampleManager::UnloadSampleBank(uint8 nBank) +{ + ASSERT( nBank < MAX_SFX_BANKS); + + gBankLoaded[nBank] = LOADING_STATUS_NOT_LOADED; +} + +int8 +cSampleManager::IsSampleBankLoaded(uint8 nBank) +{ + ASSERT( nBank < MAX_SFX_BANKS); + + return gBankLoaded[nBank]; +} + +#ifdef FIX_BUGS +uint8 +cSampleManager::IsMissionAudioLoaded(uint8 nSlot, uint32 nSample) +{ + ASSERT(nSlot == MISSION_AUDIO_PLAYER_COMMENT); // only MISSION_AUDIO_PLAYER_COMMENT is supported on PC + + return nSample == gPlayerTalkSfx ? LOADING_STATUS_LOADED : LOADING_STATUS_NOT_LOADED; +} + +bool8 +cSampleManager::LoadMissionAudio(uint8 nSlot, uint32 nSample) +{ + ASSERT(nSlot == MISSION_AUDIO_PLAYER_COMMENT); // only MISSION_AUDIO_PLAYER_COMMENT is supported on PC + ASSERT(nSample < TOTAL_AUDIO_SAMPLES); + + if (fseek(fpSampleDataHandle, m_aSamples[nSample].nOffset, SEEK_SET) != 0) + return FALSE; + + if (fread(gPlayerTalkData, 1, m_aSamples[nSample].nSize, fpSampleDataHandle) != m_aSamples[nSample].nSize) + return FALSE; + + gPlayerTalkSfx = nSample; + + return TRUE; +} +#endif + +uint8 +cSampleManager::IsPedCommentLoaded(uint32 nComment) +{ + ASSERT( nComment < TOTAL_AUDIO_SAMPLES ); + + for ( int32 i = 0; i < _TODOCONST(3); i++ ) + { +#ifdef FIX_BUGS + int8 slot = (int8)nCurrentPedSlot - i - 1; + if (slot < 0) + slot += ARRAY_SIZE(nPedSlotSfx); +#else + uint8 slot = nCurrentPedSlot - i - 1; +#endif + if ( nComment == nPedSlotSfx[slot] ) + return LOADING_STATUS_LOADED; + } + + return LOADING_STATUS_NOT_LOADED; +} + + +int32 +cSampleManager::_GetPedCommentSlot(uint32 nComment) +{ + for (int32 i = 0; i < _TODOCONST(3); i++) + { +#ifdef FIX_BUGS + int8 slot = (int8)nCurrentPedSlot - i - 1; + if (slot < 0) + slot += ARRAY_SIZE(nPedSlotSfx); +#else + uint8 slot = nCurrentPedSlot - i - 1; +#endif + if (nComment == nPedSlotSfx[slot]) + return slot; + } + + return -1; +} + +bool8 +cSampleManager::LoadPedComment(uint32 nComment) +{ + ASSERT( nComment < TOTAL_AUDIO_SAMPLES ); + + if ( CTimer::GetIsCodePaused() ) + return FALSE; + + // no talking peds during cutsenes or the game end + if ( MusicManager.IsInitialised() ) + { + switch ( MusicManager.GetMusicMode() ) + { + case MUSICMODE_CUTSCENE: + { + return FALSE; + + break; + } + } + } + +#ifdef OPUS_SFX + int samplesRead = 0; + int samplesSize = m_aSamples[nComment].nSize / 2; + op_pcm_seek(fpSampleDataHandle, m_aSamples[nComment].nOffset / 2); + while (samplesSize > 0) { + int size = op_read(fpSampleDataHandle, (opus_int16 *)(nSampleBankMemoryStartAddress[SAMPLEBANK_PED] + PED_BLOCKSIZE * nCurrentPedSlot + samplesRead), + samplesSize, NULL); + if (size <= 0) { + return FALSE; + } + samplesRead += size * 2; + samplesSize -= size; + } +#else + if ( fseek(fpSampleDataHandle, m_aSamples[nComment].nOffset, SEEK_SET) != 0 ) + return FALSE; + + if ( fread((void *)(nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] + PED_BLOCKSIZE*nCurrentPedSlot), 1, m_aSamples[nComment].nSize, fpSampleDataHandle) != m_aSamples[nComment].nSize ) + return FALSE; + +#endif + nPedSlotSfx[nCurrentPedSlot] = nComment; + + if ( ++nCurrentPedSlot >= MAX_PEDSFX ) + nCurrentPedSlot = 0; + + return TRUE; +} + +int32 +cSampleManager::GetBankContainingSound(uint32 offset) +{ + if ( offset >= BankStartOffset[SFX_BANK_PED_COMMENTS] ) + return SFX_BANK_PED_COMMENTS; + + if ( offset >= BankStartOffset[SFX_BANK_0] ) + return SFX_BANK_0; + + return INVALID_SFX_BANK; +} + +uint32 +cSampleManager::GetSampleBaseFrequency(uint32 nSample) +{ + ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); + return m_aSamples[nSample].nFrequency; +} + +uint32 +cSampleManager::GetSampleLoopStartOffset(uint32 nSample) +{ + ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); + return m_aSamples[nSample].nLoopStart; +} + +int32 +cSampleManager::GetSampleLoopEndOffset(uint32 nSample) +{ + ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); + return m_aSamples[nSample].nLoopEnd; +} + +uint32 +cSampleManager::GetSampleLength(uint32 nSample) +{ + ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); + return m_aSamples[nSample].nSize / sizeof(uint16); +} + +bool8 cSampleManager::UpdateReverb(void) +{ + if ( !usingEAX && !_usingEFX ) + return FALSE; + + if ( AudioManager.m_FrameCounter & 15 ) + return FALSE; + + float fRatio = 0.0f; + +#ifdef AUDIO_REFLECTIONS +#define MIN_DIST 0.5f +#define CALCULATE_RATIO(value, maxDist, maxRatio) (value > MIN_DIST && value < maxDist ? value / maxDist * maxRatio : 0) + + fRatio += CALCULATE_RATIO(AudioManager.m_afReflectionsDistances[REFLECTION_CEIL_NORTH], 10.0f, 1/2.f); + fRatio += CALCULATE_RATIO(AudioManager.m_afReflectionsDistances[REFLECTION_CEIL_SOUTH], 10.0f, 1/2.f); + fRatio += CALCULATE_RATIO(AudioManager.m_afReflectionsDistances[REFLECTION_CEIL_WEST], 10.0f, 1/2.f); + fRatio += CALCULATE_RATIO(AudioManager.m_afReflectionsDistances[REFLECTION_CEIL_EAST], 10.0f, 1/2.f); + + fRatio += CALCULATE_RATIO((AudioManager.m_afReflectionsDistances[REFLECTION_NORTH] + AudioManager.m_afReflectionsDistances[REFLECTION_SOUTH]) / 2.f, 4.0f, 1/3.f); + fRatio += CALCULATE_RATIO((AudioManager.m_afReflectionsDistances[REFLECTION_WEST] + AudioManager.m_afReflectionsDistances[REFLECTION_EAST]) / 2.f, 4.0f, 1/3.f); + +#undef CALCULATE_RATIO +#undef MIN_DIST +#endif + + fRatio = Clamp(fRatio, 0.0f, 0.6f); + + if ( fRatio == _fPrevEaxRatioDestination ) + return FALSE; + +#ifdef JUICY_OAL + if ( usingEAX3 || _usingEFX ) +#else + if ( usingEAX3 ) +#endif + { + fRatio = Min(fRatio * 1.67f, 1.0f); + if ( EAX3ListenerInterpolate(&StartEAX3, &FinishEAX3, fRatio, &EAX3Params, false) ) + { + EAX_SetAll(&EAX3Params); + + /* + if ( IsFXSupported() ) + { + alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, ALEffect); + + for ( int32 i = 0; i < MAXCHANNELS; i++ ) + aChannel[i].UpdateReverb(ALEffectSlot); + } + */ + + _fEffectsLevel = fRatio * 0.75f; + } + } + else + { + if ( _usingEFX ) + _fEffectsLevel = fRatio * 0.8f; + else + _fEffectsLevel = fRatio * 0.22f; + } + _fEffectsLevel = Min(_fEffectsLevel, 1.0f); + + _fPrevEaxRatioDestination = fRatio; + + return TRUE; +} + +void +cSampleManager::SetChannelReverbFlag(uint32 nChannel, bool8 nReverbFlag) +{ + ASSERT( nChannel < NUM_CHANNELS ); + + if ( usingEAX || _usingEFX ) + { + if ( IsFXSupported() ) + { + alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, ALEffect); + + if ( nReverbFlag != FALSE ) + aChannel[nChannel].SetReverbMix(ALEffectSlot, _fEffectsLevel); + else + aChannel[nChannel].SetReverbMix(ALEffectSlot, 0.0f); + } + } +} + +bool8 +cSampleManager::InitialiseChannel(uint32 nChannel, uint32 nSfx, uint8 nBank) +{ + ASSERT( nChannel < NUM_CHANNELS ); + + uintptr addr; + + if ( nSfx < SAMPLEBANK_MAX ) + { + if ( !IsSampleBankLoaded(nBank) ) + return FALSE; + + addr = nSampleBankMemoryStartAddress[nBank] + m_aSamples[nSfx].nOffset - m_aSamples[BankStartOffset[nBank]].nOffset; + } +#ifdef FIX_BUGS + else if ( nSfx >= PLAYER_COMMENTS_START && nSfx <= PLAYER_COMMENTS_END ) + { + if ( !IsMissionAudioLoaded(MISSION_AUDIO_PLAYER_COMMENT, nSfx) ) + return FALSE; + + addr = (uintptr)gPlayerTalkData; + } +#endif + else + { + int32 i; + for ( i = 0; i < _TODOCONST(3); i++ ) + { + int32 slot = nCurrentPedSlot - i - 1; +#ifdef FIX_BUGS + if (slot < 0) + slot += ARRAY_SIZE(nPedSlotSfx); +#endif + if ( nSfx == nPedSlotSfx[slot] ) + { + addr = (nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] + PED_BLOCKSIZE * slot); + break; + } + } + + if (i == _TODOCONST(3)) + return FALSE; + } + + if ( GetChannelUsedFlag(nChannel) ) + { + TRACE("Stopping channel %d - really!!!", nChannel); + StopChannel(nChannel); + } + + aChannel[nChannel].Reset(); + if ( aChannel[nChannel].HasSource() ) + { + aChannel[nChannel].SetSampleData ((void*)addr, m_aSamples[nSfx].nSize, m_aSamples[nSfx].nFrequency); + aChannel[nChannel].SetLoopPoints (0, -1); + aChannel[nChannel].SetPitch (1.0f); + return TRUE; + } + + return FALSE; +} + +void +cSampleManager::SetChannelEmittingVolume(uint32 nChannel, uint32 nVolume) +{ + ASSERT( nChannel < MAXCHANNELS ); + + uint32 vol = nVolume; + if ( vol > MAX_VOLUME ) vol = MAX_VOLUME; + + nChannelVolume[nChannel] = vol; + + if (MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE ) { + if (MusicManager.GetCurrentTrack() == STREAMED_SOUND_CUTSCENE_FINALE) + nChannelVolume[nChannel] = 0; + else + nChannelVolume[nChannel] >>= 2; + } + + // no idea, does this one looks like a bug or it's SetChannelVolume ? + aChannel[nChannel].SetVolume(m_nEffectsFadeVolume*nChannelVolume[nChannel]*m_nEffectsVolume >> 14); +} + +void +cSampleManager::SetChannel3DPosition(uint32 nChannel, float fX, float fY, float fZ) +{ + ASSERT( nChannel < MAXCHANNELS ); + + aChannel[nChannel].SetPosition(-fX, fY, fZ); +} + +void +cSampleManager::SetChannel3DDistances(uint32 nChannel, float fMax, float fMin) +{ + ASSERT( nChannel < MAXCHANNELS ); + aChannel[nChannel].SetDistances(fMax, fMin); +} + +void +cSampleManager::SetChannelVolume(uint32 nChannel, uint32 nVolume) +{ + ASSERT( nChannel >= MAXCHANNELS ); + ASSERT( nChannel < NUM_CHANNELS ); + + if ( nChannel == CHANNEL_POLICE_RADIO ) + { + uint32 vol = nVolume; + if ( vol > MAX_VOLUME ) vol = MAX_VOLUME; + + nChannelVolume[nChannel] = vol; + + // increase the volume for JB.MP3 and S4_BDBD.MP3 + if (MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE ) { + if (MusicManager.GetCurrentTrack() == STREAMED_SOUND_CUTSCENE_FINALE) + nChannelVolume[nChannel] = 0; + else + nChannelVolume[nChannel] >>= 2; + } + + aChannel[nChannel].SetVolume(m_nEffectsFadeVolume*vol*m_nEffectsVolume >> 14); + } +} + +void +cSampleManager::SetChannelPan(uint32 nChannel, uint32 nPan) +{ + ASSERT( nChannel >= MAXCHANNELS ); + ASSERT( nChannel < NUM_CHANNELS ); + + if ( nChannel == CHANNEL_POLICE_RADIO ) + { + aChannel[nChannel].SetPan(nPan); + } +} + +void +cSampleManager::SetChannelFrequency(uint32 nChannel, uint32 nFreq) +{ + ASSERT( nChannel < NUM_CHANNELS ); + + aChannel[nChannel].SetCurrentFreq(nFreq); +} + +void +cSampleManager::SetChannelLoopPoints(uint32 nChannel, uint32 nLoopStart, int32 nLoopEnd) +{ + ASSERT( nChannel < NUM_CHANNELS ); + + aChannel[nChannel].SetLoopPoints(nLoopStart / (DIGITALBITS / 8), nLoopEnd / (DIGITALBITS / 8)); +} + +void +cSampleManager::SetChannelLoopCount(uint32 nChannel, uint32 nLoopCount) +{ + ASSERT( nChannel < NUM_CHANNELS ); + + aChannel[nChannel].SetLoopCount(nLoopCount); +} + +bool8 +cSampleManager::GetChannelUsedFlag(uint32 nChannel) +{ + ASSERT( nChannel < NUM_CHANNELS ); + + return aChannel[nChannel].IsUsed(); +} + +void +cSampleManager::StartChannel(uint32 nChannel) +{ + ASSERT( nChannel < NUM_CHANNELS ); + + aChannel[nChannel].Start(); +} + +void +cSampleManager::StopChannel(uint32 nChannel) +{ + ASSERT( nChannel < NUM_CHANNELS ); + + aChannel[nChannel].Stop(); +} + +void +cSampleManager::PreloadStreamedFile(uint32 nFile, uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); + + if ( nFile < TOTAL_STREAMED_SOUNDS ) + { + CStream *stream = aStream[nStream]; + + stream->Close(); +#ifdef PS2_AUDIO_PATHS + if(!stream->Open(PS2StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000)) +#endif + stream->Open(StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000); + if ( !stream->Setup() ) + { + stream->Close(); + } + } +} + +void +cSampleManager::PauseStream(bool8 nPauseFlag, uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); + + CStream *stream = aStream[nStream]; + + if ( stream->IsOpened() ) + { + stream->SetPause(nPauseFlag != FALSE); + } +} + +void +cSampleManager::StartPreloadedStreamedFile(uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); + + CStream *stream = aStream[nStream]; + + if ( stream->IsOpened() ) + { + stream->Start(); + } +} + +bool8 +cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream) +{ + uint32 i = 0; + uint32 position = nPos; + char filename[MAX_PATH]; + + if ( nFile >= TOTAL_STREAMED_SOUNDS ) + return FALSE; + + aStream[nStream]->Close(); + + if ( nFile == STREAMED_SOUND_RADIO_MP3_PLAYER ) + { + do + { + // Just switched to MP3 player + if ( !_bIsMp3Active && i == 0 ) + { + if ( nPos > nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER] ) + position = 0; + tMP3Entry *e = _pMP3List; + + // Try to continue from previous song, if already started + if(!_GetMP3PosFromStreamPos(&position, &e) && !e) { + nFile = 0; + + CStream *stream = aStream[nStream]; +#ifdef PS2_AUDIO_PATHS + if(!stream->Open(PS2StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000)) +#endif + stream->Open(StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000); + if ( stream->Setup() ) { + stream->SetLoopCount(nStreamLoopedFlag[nStream] ? 0 : 1); + nStreamLoopedFlag[nStream] = TRUE; + if (position != 0) + stream->SetPosMS(position); + + stream->Start(); + + return TRUE; + } else { + stream->Close(); + } + return FALSE; + + } else { + + if (e->pLinkPath != NULL) + aStream[nStream]->Open(e->pLinkPath, IsThisTrackAt16KHz(nFile) ? 16000 : 32000); + else { + strcpy(filename, _mp3DirectoryPath); + strcat(filename, e->aFilename); + + aStream[nStream]->Open(filename); + } + + if (aStream[nStream]->Setup()) { + if (position != 0) + aStream[nStream]->SetPosMS(position); + + aStream[nStream]->Start(); + + _bIsMp3Active = TRUE; + return TRUE; + } else { + aStream[nStream]->Close(); + } + // fall through, start playing from another song + } + } else { + if(++_CurMP3Index >= nNumMP3s) _CurMP3Index = 0; + + _CurMP3Pos = 0; + + tMP3Entry *mp3 = _GetMP3EntryByIndex(_CurMP3Index); + if ( !mp3 ) + { + mp3 = _pMP3List; + if ( !_pMP3List ) + { + nFile = 0; + _bIsMp3Active = FALSE; + + CStream *stream = aStream[nStream]; +#ifdef PS2_AUDIO_PATHS + if(!stream->Open(PS2StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000)) +#endif + stream->Open(StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000); + + if (stream->Setup()) { + stream->SetLoopCount(nStreamLoopedFlag[nStream] ? 0 : 1); + nStreamLoopedFlag[nStream] = TRUE; + if (position != 0) + stream->SetPosMS(position); + + stream->Start(); + + return TRUE; + } else { + stream->Close(); + } + return FALSE; + } + } + if (mp3->pLinkPath != NULL) + aStream[nStream]->Open(mp3->pLinkPath, IsThisTrackAt16KHz(nFile) ? 16000 : 32000); + else { + strcpy(filename, _mp3DirectoryPath); + strcat(filename, mp3->aFilename); + + aStream[nStream]->Open(filename, IsThisTrackAt16KHz(nFile) ? 16000 : 32000); + } + + if (aStream[nStream]->Setup()) { + aStream[nStream]->Start(); +#ifdef FIX_BUGS + _bIsMp3Active = TRUE; +#endif + return TRUE; + } else { + aStream[nStream]->Close(); + } + + } + _bIsMp3Active = FALSE; + } + while ( ++i < nNumMP3s ); + position = 0; + nFile = 0; + } + strcpy(filename, StreamedNameTable[nFile]); + + CStream *stream = aStream[nStream]; + +#ifdef PS2_AUDIO_PATHS + if(!stream->Open(PS2StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000)) +#endif + stream->Open(StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000); + + if ( stream->Setup() ) { + stream->SetLoopCount(nStreamLoopedFlag[nStream] ? 0 : 1); + nStreamLoopedFlag[nStream] = TRUE; + if (position != 0) + stream->SetPosMS(position); + + stream->Start(); + + return TRUE; + } else { + stream->Close(); + } + return FALSE; +} + +void +cSampleManager::StopStreamedFile(uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); + + CStream *stream = aStream[nStream]; + + stream->Close(); + + if ( nStream == 0 ) + _bIsMp3Active = FALSE; +} + +int32 +cSampleManager::GetStreamedFilePosition(uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); + + CStream *stream = aStream[nStream]; + + if ( stream->IsOpened() ) + { + if ( _bIsMp3Active ) + { + tMP3Entry *mp3 = _GetMP3EntryByIndex(_CurMP3Index); + + if ( mp3 != NULL ) + { + return stream->GetPosMS() + mp3->nTrackStreamPos; + } + else + return 0; + } + else + { + return stream->GetPosMS(); + } + } + + return 0; +} + +void +cSampleManager::SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, bool8 nEffectFlag, uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); + + float boostMult = 0.0f; + + if ( nVolume > MAX_VOLUME ) + nVolume = MAX_VOLUME; + + if ( nPan > MAX_VOLUME ) + nPan = MAX_VOLUME; + + if ( MusicManager.GetRadioInCar() == USERTRACK && !MusicManager.CheckForMusicInterruptions() ) + boostMult = m_nMP3BoostVolume / 64.f; + + nStreamVolume[nStream] = nVolume; + nStreamPan [nStream] = nPan; + + CStream *stream = aStream[nStream]; + + if ( stream->IsOpened() ) + { + if ( nEffectFlag ) { + if ( nStream == 1 || nStream == 2 ) + stream->SetVolume(128*nVolume*m_nEffectsVolume >> 14); + else + stream->SetVolume(m_nEffectsFadeVolume*nVolume*m_nEffectsVolume >> 14); + } + else + stream->SetVolume((m_nMusicFadeVolume*nVolume*(uint32)(m_nMusicVolume * boostMult + m_nMusicVolume)) >> 14); + + stream->SetPan(nPan); + } +} + +int32 +cSampleManager::GetStreamedFileLength(uint8 nStream) +{ + ASSERT( nStream < TOTAL_STREAMED_SOUNDS ); + + return nStreamLength[nStream]; +} + +bool8 +cSampleManager::IsStreamPlaying(uint8 nStream) +{ + ASSERT( nStream < MAX_STREAMS ); + + CStream *stream = aStream[nStream]; + + if ( stream->IsOpened() ) + { + if ( stream->IsPlaying() ) + return TRUE; + } + + return FALSE; +} + +void +cSampleManager::Service(void) +{ + for ( int32 i = 0; i < MAX_STREAMS; i++ ) + { + CStream *stream = aStream[i]; + + if ( stream->IsOpened() ) + stream->Update(); + } + int refCount = CChannel::channelsThatNeedService; + for ( int32 i = 0; refCount && i < NUM_CHANNELS; i++ ) + { + if ( aChannel[i].Update() ) + refCount--; + } +} + +bool8 +cSampleManager::InitialiseSampleBanks(void) +{ + int32 nBank = SFX_BANK_0; + + fpSampleDescHandle = fcaseopen(SampleBankDescFilename, "rb"); + if ( fpSampleDescHandle == NULL ) + return FALSE; +#ifndef OPUS_SFX + fpSampleDataHandle = fcaseopen(SampleBankDataFilename, "rb"); + if ( fpSampleDataHandle == NULL ) + { + fclose(fpSampleDescHandle); + fpSampleDescHandle = NULL; + + return FALSE; + } + + fseek(fpSampleDataHandle, 0, SEEK_END); + int32 _nSampleDataEndOffset = ftell(fpSampleDataHandle); + rewind(fpSampleDataHandle); +#else + int e; + fpSampleDataHandle = op_open_file(SampleBankDataFilename, &e); +#endif + fread(m_aSamples, sizeof(tSample), TOTAL_AUDIO_SAMPLES, fpSampleDescHandle); +#ifdef OPUS_SFX + int32 _nSampleDataEndOffset = m_aSamples[TOTAL_AUDIO_SAMPLES - 1].nOffset + m_aSamples[TOTAL_AUDIO_SAMPLES - 1].nSize; +#endif + fclose(fpSampleDescHandle); + fpSampleDescHandle = NULL; + + for ( uint32 i = 0; i < TOTAL_AUDIO_SAMPLES; i++ ) + { +#ifdef FIX_BUGS + if (nBank >= MAX_SFX_BANKS) break; +#endif + if ( BankStartOffset[nBank] == BankStartOffset[SFX_BANK_0] + i ) + { + nSampleBankDiscStartOffset[nBank] = m_aSamples[i].nOffset; + nBank++; + } + } + + nSampleBankSize[SFX_BANK_0] = nSampleBankDiscStartOffset[SFX_BANK_PED_COMMENTS] - nSampleBankDiscStartOffset[SFX_BANK_0]; + nSampleBankSize[SFX_BANK_PED_COMMENTS] = _nSampleDataEndOffset - nSampleBankDiscStartOffset[SFX_BANK_PED_COMMENTS]; + + return TRUE; +} + +void +cSampleManager::SetStreamedFileLoopFlag(bool8 nLoopFlag, uint8 nChannel) +{ + nStreamLoopedFlag[nChannel] = nLoopFlag; +} + +#endif diff --git a/src/miami/audio/soundlist.h b/src/miami/audio/soundlist.h new file mode 100644 index 00000000..c6dbb634 --- /dev/null +++ b/src/miami/audio/soundlist.h @@ -0,0 +1,282 @@ +#pragma once + +enum eSound +{ + SOUND_CAR_DOOR_CLOSE_BONNET = 0, + SOUND_CAR_DOOR_CLOSE_BUMPER, + SOUND_CAR_DOOR_CLOSE_FRONT_LEFT, + SOUND_CAR_DOOR_CLOSE_FRONT_RIGHT, + SOUND_CAR_DOOR_CLOSE_BACK_LEFT, + SOUND_CAR_DOOR_CLOSE_BACK_RIGHT, + SOUND_CAR_DOOR_OPEN_BONNET, + SOUND_CAR_DOOR_OPEN_BUMPER, + SOUND_CAR_DOOR_OPEN_FRONT_LEFT, + SOUND_CAR_DOOR_OPEN_FRONT_RIGHT, + SOUND_CAR_DOOR_OPEN_BACK_LEFT, + SOUND_CAR_DOOR_OPEN_BACK_RIGHT, + SOUND_CAR_WINDSHIELD_CRACK, + SOUND_CAR_JUMP, + SOUND_CAR_JUMP_2, + SOUND_CAR_TYRE_POP, + SOUND_16, + SOUND_17, + SOUND_CAR_ENGINE_START, + SOUND_CAR_LIGHT_BREAK, + SOUND_CAR_HYDRAULIC_1, + SOUND_CAR_HYDRAULIC_2, + SOUND_CAR_HYDRAULIC_3, + SOUND_CAR_JERK, + SOUND_CAR_SPLASH, + SOUND_BOAT_SLOWDOWN, + SOUND_TRAIN_DOOR_CLOSE, + SOUND_TRAIN_DOOR_OPEN, + SOUND_CAR_TANK_TURRET_ROTATE, + SOUND_CAR_BOMB_TICK, + SOUND_PLANE_ON_GROUND, + SOUND_HELI_BLADE, + SOUND_32, + SOUND_STEP_START, + SOUND_STEP_END, + SOUND_FALL_LAND, + SOUND_FALL_COLLAPSE, + SOUND_FIGHT_37, + SOUND_FIGHT_38, + SOUND_FIGHT_39, + SOUND_FIGHT_40, + SOUND_FIGHT_41, + SOUND_FIGHT_42, + SOUND_FIGHT_43, + SOUND_FIGHT_44, + SOUND_FIGHT_45, + SOUND_FIGHT_46, + SOUND_FIGHT_47, + SOUND_FIGHT_48, + SOUND_49, + SOUND_WEAPON_BAT_ATTACK, + SOUND_WEAPON_KNIFE_ATTACK, + SOUND_WEAPON_CHAINSAW_IDLE, + SOUND_WEAPON_CHAINSAW_ATTACK, + SOUND_WEAPON_CHAINSAW_MADECONTACT, + SOUND_WEAPON_SHOT_FIRED, + SOUND_WEAPON_RELOAD, + SOUND_WEAPON_AK47_BULLET_ECHO, + SOUND_WEAPON_FLAMETHROWER_FIRE, + SOUND_WEAPON_SNIPER_SHOT_NO_ZOOM, + SOUND_WEAPON_ROCKET_SHOT_NO_ZOOM, + SOUND_WEAPON_HIT_PED, + SOUND_WEAPON_HIT_VEHICLE, + SOUND_GARAGE_NO_MONEY, + SOUND_GARAGE_BAD_VEHICLE, + SOUND_GARAGE_OPENING, + SOUND_GARAGE_BOMB_ALREADY_SET, + SOUND_GARAGE_BOMB1_SET, + SOUND_GARAGE_BOMB2_SET, + SOUND_GARAGE_BOMB3_SET, + SOUND_70, + SOUND_71, + SOUND_GARAGE_VEHICLE_DECLINED, + SOUND_GARAGE_VEHICLE_ACCEPTED, + SOUND_GARAGE_DOOR_CLOSED, + SOUND_GARAGE_DOOR_OPENED, + SOUND_CRANE_PICKUP, + SOUND_PICKUP_WEAPON_BOUGHT, + SOUND_PICKUP_WEAPON, + SOUND_PICKUP_HEALTH, + SOUND_80, + SOUND_81, + SOUND_PICKUP_ADRENALINE, + SOUND_PICKUP_ARMOUR, + SOUND_PICKUP_BONUS, + SOUND_PICKUP_MONEY, + SOUND_PICKUP_HIDDEN_PACKAGE, + SOUND_PICKUP_PACMAN_PILL, + SOUND_PICKUP_PACMAN_PACKAGE, + SOUND_PICKUP_FLOAT_PACKAGE, + SOUND_BOMB_TIMED_ACTIVATED, + SOUND_91, + SOUND_BOMB_ONIGNITION_ACTIVATED, + SOUND_BOMB_TICK, + SOUND_RAMPAGE_START, + SOUND_RAMPAGE_ONGOING, + SOUND_RAMPAGE_PASSED, + SOUND_RAMPAGE_FAILED, + SOUND_RAMPAGE_KILL, + SOUND_RAMPAGE_CAR_BLOWN, + SOUND_EVIDENCE_PICKUP, + SOUND_UNLOAD_GOLD, + SOUND_PAGER, + SOUND_PED_DEATH, + SOUND_PED_DAMAGE, + SOUND_PED_HIT, + SOUND_PED_LAND, + SOUND_PED_BULLET_HIT, + SOUND_PED_BURNING, + SOUND_PED_PLAYER_REACTTOCOP, + SOUND_PED_ARREST_COP, + SOUND_PED_MIAMIVICE_EXITING_CAR, + SOUND_PED_COP_HELIPILOTPHRASE, + SOUND_PED_PULLOUTWEAPON, + SOUND_PED_HELI_PLAYER_FOUND, + SOUND_PED_VCPA_PLAYER_FOUND, + SOUND_PED_ON_FIRE, + SOUND_PED_AIMING, + SOUND_PED_HANDS_UP, + SOUND_PED_HANDS_COWER, + SOUND_PED_FLEE_SPRINT, + SOUND_PED_CAR_JACKING, + SOUND_PED_MUGGING, + SOUND_PED_CAR_JACKED, + SOUND_PED_ROBBED, + SOUND_PED_ACCIDENTREACTION1, + SOUND_PED_INNOCENT, + SOUND_PED_PLAYER_AFTERSEX, + SOUND_PED_PLAYER_BEFORESEX, + SOUND_PED_COP_TARGETING, // also used for medics + SOUND_PED_COP_MANYCOPSAROUND, // also used for medics + SOUND_PED_GUNAIMEDAT2, + SOUND_PED_COP_ALONE, // also used for medics + SOUND_PED_GUNAIMEDAT3, + SOUND_PED_COP_ASK_FOR_ID, + SOUND_PED_COP_LITTLECOPSAROUND, // also used for medics + SOUND_PED_PLAYER_FARFROMCOPS, // also used for medics + SOUND_PED_TAXI_WAIT, + SOUND_PED_ATTACK, + SOUND_PED_DEFEND, + SOUND_PED_HEALING, + SOUND_PED_LEAVE_VEHICLE, + SOUND_PED_EVADE, + SOUND_PED_FLEE_RUN, + SOUND_PED_CRASH_VEHICLE, + SOUND_PED_CRASH_CAR, + SOUND_PED_ANNOYED_DRIVER, + SOUND_PED_147, + SOUND_PED_SOLICIT, + SOUND_PED_JEER, + SOUND_PED_150, + SOUND_PED_EXTINGUISHING_FIRE, + SOUND_PED_WAIT_DOUBLEBACK, + SOUND_PED_CHAT_SEXY_FEMALE, + SOUND_PED_CHAT_SEXY_MALE, + SOUND_PED_CHAT_EVENT, + SOUND_PED_PED_COLLISION, + SOUND_PED_CHAT, + SOUND_PED_TAXI_CALL, + SOUND_RACE_START_3, + SOUND_RACE_START_2, + SOUND_RACE_START_1, + SOUND_RACE_START_GO, + SOUND_SPLASH, + SOUND_WATER_FALL, + SOUND_SPLATTER, + SOUND_CAR_PED_COLLISION, + SOUND_CLOCK_TICK, + SOUND_PART_MISSION_COMPLETE, + SOUND_FRONTEND_MENU_STARTING, // same sound as SOUND_HUD + + // TODO(Miami): What are 170-175?? + + SOUND_FRONTEND_RADIO_TURN_OFF = 176, // those 2 are same sound + SOUND_FRONTEND_RADIO_TURN_ON, + SOUND_FRONTEND_HURRICANE, // yes, frontend + SOUND_HUD, + SOUND_180, + SOUND_181, + SOUND_182, + SOUND_LIGHTNING, + SOUND_BULLETTRACE_1, + SOUND_BULLETTRACE_2, + SOUND_186, // makes same sound as 40 + SOUND_187, // makes same sound as 46 + SOUND_MELEE_ATTACK_START, + SOUND_SKATING, + SOUND_WEAPON_MINIGUN_ATTACK, + SOUND_WEAPON_MINIGUN_2, + SOUND_WEAPON_MINIGUN_3, + SOUND_AMMUNATION_IMRAN_ARM_BOMB, + SOUND_RADIO_CHANGE, + SOUND_FRONTEND_HIGHLIGHT_OPTION, + SOUND_FRONTEND_ENTER_OR_ADJUST, + SOUND_FRONTEND_BACK, + SOUND_FRONTEND_FAIL, + SOUND_FRONTEND_AUDIO_TEST, + SOUND_INJURED_PED_MALE_OUCH, + SOUND_INJURED_PED_FEMALE, + SOUND_SHIRT_WIND_FLAP, + SOUND_SET_203, + SOUND_TOTAL_SOUNDS = 204, + SOUND_NO_SOUND = 205, +}; + + +enum eScriptSounds { + SCRIPT_SOUND_BANK_ALARM_LOOP = 0, + SCRIPT_SOUND_PART_MISSION_COMPLETE, + SCRIPT_SOUND_POLICE_CELL_DOOR_SLIDING_LOOP, + SCRIPT_SOUND_POLICE_CELL_DOOR_CLUNK, + SCRIPT_SOUND_GARAGE_DOOR_SLIDING_LOOP, + SCRIPT_SOUND_GARAGE_DOOR_CLUNK, + SCRIPT_SOUND_SNORING_LOOP, + SCRIPT_SOUND_RACE_START_3, + SCRIPT_SOUND_RACE_START_2, + SCRIPT_SOUND_RACE_START_1, + SCRIPT_SOUND_RACE_START_GO, + SCRIPT_SOUND_SHOOTING_RANGE_TARGET_MOVING_LOOP, + SCRIPT_SOUND_SHOOTING_RANGE_TARGET_HIT, + SCRIPT_SOUND_AMMUNATION_BUY_WEAPON, + SCRIPT_SOUND_AMMUNATION_BUY_WEAPON_DENIED, + SCRIPT_SOUND_WMYCW_TICKET_SPEECH, + SCRIPT_SOUND_IMRAN_ARM_BOMB, + SCRIPT_SOUND_ANDY_SNIPER_SHOT, + SCRIPT_SOUND_WILLIE_CARD_SWIPE, + SCRIPT_SOUND_MALE_AMBULANCE_OUCH, + SCRIPT_SOUND_FEMALE_AMBULANCE_OUCH, + SCRIPT_SOUND_BUILDING_BAR_1, + SCRIPT_SOUND_BUILDING_BAR_2, + SCRIPT_SOUND_BUILDING_BAR_3, + SCRIPT_SOUND_BUILDING_BAR_4, + SCRIPT_SOUND_BUILDING_BIKER_BAR, + SCRIPT_SOUND_BUILDING_CHURCH, + SCRIPT_SOUND_BUILDING_CLUB, + SCRIPT_SOUND_BUILDING_CUBA_1, + SCRIPT_SOUND_BUILDING_CUBA_2, + SCRIPT_SOUND_BUILDING_VOODOO, + SCRIPT_SOUND_BUILDING_MUSIC_SHOP, + SCRIPT_SOUND_BUILDING_STRIPCLUB_1, + SCRIPT_SOUND_BUILDING_STRIPCLUB_2, + SCRIPT_SOUND_BUILDING_SUPERSWEEP, + SCRIPT_SOUND_SEAPLANE_LOW_FUEL, + SCRIPT_SOUND_NEW_BUILDING_BAR_1, + SCRIPT_SOUND_NEW_BUILDING_BAR_2, + SCRIPT_SOUND_NEW_BUILDING_BAR_3, + SCRIPT_SOUND_NEW_BUILDING_BAR_4, + SCRIPT_SOUND_NEW_BUILDING_MALIBU_1, + SCRIPT_SOUND_NEW_BUILDING_MALIBU_2, + SCRIPT_SOUND_NEW_BUILDING_MALIBU_3, + SCRIPT_SOUND_NEW_BUILDING_STRIP_1, + SCRIPT_SOUND_NEW_BUILDING_STRIP_2, + SCRIPT_SOUND_NEW_BUILDING_STRIP_3, + SCRIPT_SOUND_NEW_BUILDING_CHURCH, + SCRIPT_SOUND_NEW_BUILDING_FAN_1, + SCRIPT_SOUND_NEW_BUILDING_FAN_2, + SCRIPT_SOUND_NEW_BUILDING_INSECT_1, + SCRIPT_SOUND_NEW_BUILDING_INSECT_2, + SCRIPT_SOUND_NEW_WATERFALL, + SCRIPT_SOUND_BULLET_HIT_GROUND_1, + SCRIPT_SOUND_BULLET_HIT_GROUND_2, + SCRIPT_SOUND_BULLET_HIT_GROUND_3, + SCRIPT_SOUND_BULLET_HIT_WATER, // no sound + SCRIPT_SOUND_PAYPHONE_RINGING, + SCRIPT_SOUND_GLASS_BREAK_L, + SCRIPT_SOUND_GLASS_BREAK_S, + SCRIPT_SOUND_GLASS_CRACK, + SCRIPT_SOUND_GLASS_LIGHT_BREAK, + SCRIPT_SOUND_BOX_DESTROYED_1, + SCRIPT_SOUND_BOX_DESTROYED_2, + SCRIPT_SOUND_METAL_COLLISION, + SCRIPT_SOUND_TIRE_COLLISION, + SCRIPT_SOUND_HIT_BALL, + SCRIPT_SOUND_GUNSHELL_DROP, + SCRIPT_SOUND_GUNSHELL_DROP_SOFT, + SCRIPT_SOUND_TOTAL, + SCRIPT_SOUND_INVALID, +}; diff --git a/src/miami/buildings/Building.cpp b/src/miami/buildings/Building.cpp new file mode 100644 index 00000000..92c787e5 --- /dev/null +++ b/src/miami/buildings/Building.cpp @@ -0,0 +1,44 @@ +#include "common.h" + +#include "Building.h" +#include "Streaming.h" +#include "Pools.h" + +void *CBuilding::operator new(size_t sz) throw() { return CPools::GetBuildingPool()->New(); } +void CBuilding::operator delete(void *p, size_t sz) throw() { CPools::GetBuildingPool()->Delete((CBuilding*)p); } + +void +CBuilding::ReplaceWithNewModel(int32 id) +{ + DeleteRwObject(); + + if (CModelInfo::GetModelInfo(m_modelIndex)->GetNumRefs() == 0) + CStreaming::RemoveModel(m_modelIndex); + m_modelIndex = id; + + if(bIsBIGBuilding) + if(m_level == LEVEL_GENERIC || m_level == CGame::currLevel) + CStreaming::RequestModel(id, STREAMFLAGS_DONT_REMOVE); +} + +bool +IsBuildingPointerValid(CBuilding* pBuilding) +{ + if (!pBuilding) + return false; + if (pBuilding->GetIsATreadable()) { + int index = CPools::GetTreadablePool()->GetJustIndex_NoFreeAssert((CTreadable*)pBuilding); +#ifdef FIX_BUGS + return index >= 0 && index < CPools::GetTreadablePool()->GetSize(); +#else + return index >= 0 && index <= CPools::GetTreadablePool()->GetSize(); +#endif + } else { + int index = CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert(pBuilding); +#ifdef FIX_BUGS + return index >= 0 && index < CPools::GetBuildingPool()->GetSize(); +#else + return index >= 0 && index <= CPools::GetBuildingPool()->GetSize(); +#endif + } +} diff --git a/src/miami/buildings/Building.h b/src/miami/buildings/Building.h new file mode 100644 index 00000000..f8ddfa46 --- /dev/null +++ b/src/miami/buildings/Building.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Entity.h" + +class CBuilding : public CEntity +{ +public: + CBuilding(void) { + m_type = ENTITY_TYPE_BUILDING; + bUsesCollision = true; + } + static void *operator new(size_t) throw(); + static void operator delete(void*, size_t) throw(); + + void ReplaceWithNewModel(int32 id); + + virtual bool GetIsATreadable(void) { return false; } +}; + +bool IsBuildingPointerValid(CBuilding*); diff --git a/src/miami/buildings/Solid.h b/src/miami/buildings/Solid.h new file mode 100644 index 00000000..4ca800c2 --- /dev/null +++ b/src/miami/buildings/Solid.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Entity.h" + +class CSolid : public CEntity +{ +public: + CSolid(void) { + m_type = ENTITY_TYPE_BUILDING; + bUsesCollision = true; + } +}; \ No newline at end of file diff --git a/src/miami/buildings/Treadable.cpp b/src/miami/buildings/Treadable.cpp new file mode 100644 index 00000000..d84603a6 --- /dev/null +++ b/src/miami/buildings/Treadable.cpp @@ -0,0 +1,8 @@ +#include "common.h" + +#include "rpworld.h" +#include "Treadable.h" +#include "Pools.h" + +void *CTreadable::operator new(size_t sz) throw() { return CPools::GetTreadablePool()->New(); } +void CTreadable::operator delete(void *p, size_t sz) throw() { CPools::GetTreadablePool()->Delete((CTreadable*)p); } diff --git a/src/miami/buildings/Treadable.h b/src/miami/buildings/Treadable.h new file mode 100644 index 00000000..6a183c63 --- /dev/null +++ b/src/miami/buildings/Treadable.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Building.h" + +class CTreadable : public CBuilding +{ +public: + static void *operator new(size_t) throw(); + static void operator delete(void*, size_t) throw(); + + bool GetIsATreadable(void) { return true; } +}; diff --git a/src/miami/collision/ColBox.cpp b/src/miami/collision/ColBox.cpp new file mode 100644 index 00000000..53cba88b --- /dev/null +++ b/src/miami/collision/ColBox.cpp @@ -0,0 +1,21 @@ +#include "common.h" +#include "ColBox.h" + +void +CColBox::Set(const CVector &min, const CVector &max, uint8 surf, uint8 piece) +{ + this->min = min; + this->max = max; + this->surface = surf; + this->piece = piece; +} + +CColBox& +CColBox::operator=(const CColBox& other) +{ + min = other.min; + max = other.max; + surface = other.surface; + piece = other.piece; + return *this; +} \ No newline at end of file diff --git a/src/miami/collision/ColBox.h b/src/miami/collision/ColBox.h new file mode 100644 index 00000000..0df55925 --- /dev/null +++ b/src/miami/collision/ColBox.h @@ -0,0 +1,22 @@ +#pragma once + +#include "SurfaceTable.h" + +struct CBox +{ + CVector min; + CVector max; + CVector GetSize(void) { return max - min; } + void Set(const CVector &min, const CVector &max) { this->min = min; this->max = max; } +}; + +struct CColBox : public CBox +{ + uint8 surface; + uint8 piece; + + void Set(const CVector &min, const CVector &max, uint8 surf, uint8 piece); + using CBox::Set; + + CColBox& operator=(const CColBox &other); +}; \ No newline at end of file diff --git a/src/miami/collision/ColLine.cpp b/src/miami/collision/ColLine.cpp new file mode 100644 index 00000000..c6247449 --- /dev/null +++ b/src/miami/collision/ColLine.cpp @@ -0,0 +1,9 @@ +#include "common.h" +#include "ColLine.h" + +void +CColLine::Set(const CVector &p0, const CVector &p1) +{ + this->p0 = p0; + this->p1 = p1; +} \ No newline at end of file diff --git a/src/miami/collision/ColLine.h b/src/miami/collision/ColLine.h new file mode 100644 index 00000000..21587a06 --- /dev/null +++ b/src/miami/collision/ColLine.h @@ -0,0 +1,14 @@ +#pragma once + +struct CColLine +{ + // NB: this has to be compatible with two CVuVectors + CVector p0; + int pad0; + CVector p1; + int pad1; + + CColLine(void) { }; + CColLine(const CVector &p0, const CVector &p1) { this->p0 = p0; this->p1 = p1; }; + void Set(const CVector &p0, const CVector &p1); +}; \ No newline at end of file diff --git a/src/miami/collision/ColModel.cpp b/src/miami/collision/ColModel.cpp new file mode 100644 index 00000000..2224a804 --- /dev/null +++ b/src/miami/collision/ColModel.cpp @@ -0,0 +1,206 @@ +#include "common.h" +#include "ColModel.h" +#include "Collision.h" +#include "Game.h" +#include "MemoryHeap.h" +#include "Pools.h" + +CColModel::CColModel(void) +{ + numSpheres = 0; + spheres = nil; + numLines = 0; + lines = nil; + numBoxes = 0; + boxes = nil; + numTriangles = 0; + vertices = nil; + triangles = nil; + trianglePlanes = nil; + level = LEVEL_GENERIC; // generic col slot + ownsCollisionVolumes = true; +} + +CColModel::~CColModel(void) +{ + RemoveCollisionVolumes(); +} + +void* +CColModel::operator new(size_t) throw() +{ + CColModel* node = CPools::GetColModelPool()->New(); + assert(node); + return node; +} + +void +CColModel::operator delete(void *p, size_t) throw() +{ + CPools::GetColModelPool()->Delete((CColModel*)p); +} + +void +CColModel::RemoveCollisionVolumes(void) +{ + if(ownsCollisionVolumes){ + RwFree(spheres); + RwFree(lines); + RwFree(boxes); + RwFree(vertices); + RwFree(triangles); + CCollision::RemoveTrianglePlanes(this); + } + numSpheres = 0; + numLines = 0; + numBoxes = 0; + numTriangles = 0; + spheres = nil; + lines = nil; + boxes = nil; + vertices = nil; + triangles = nil; +} + +void +CColModel::CalculateTrianglePlanes(void) +{ + PUSH_MEMID(MEMID_COLLISION); + + // HACK: allocate space for one more element to stuff the link pointer into + trianglePlanes = (CColTrianglePlane*)RwMalloc(sizeof(CColTrianglePlane) * (numTriangles+1)); + REGISTER_MEMPTR(&trianglePlanes); + for(int i = 0; i < numTriangles; i++) + trianglePlanes[i].Set(vertices, triangles[i]); + + POP_MEMID(); +} + +void +CColModel::RemoveTrianglePlanes(void) +{ + RwFree(trianglePlanes); + trianglePlanes = nil; +} + +void +CColModel::SetLinkPtr(CLink *lptr) +{ + assert(trianglePlanes); + *(CLink**)ALIGNPTR(&trianglePlanes[numTriangles]) = lptr; +} + +CLink* +CColModel::GetLinkPtr(void) +{ + assert(trianglePlanes); + return *(CLink**)ALIGNPTR(&trianglePlanes[numTriangles]); +} + +void +CColModel::GetTrianglePoint(CVector &v, int i) const +{ + v = vertices[i].Get(); +} + +CColModel& +CColModel::operator=(const CColModel &other) +{ + int i; + int numVerts; + + boundingSphere = other.boundingSphere; + boundingBox = other.boundingBox; + + // copy spheres + if(other.numSpheres){ + if(numSpheres != other.numSpheres){ + numSpheres = other.numSpheres; + if(spheres) + RwFree(spheres); + spheres = (CColSphere*)RwMalloc(numSpheres*sizeof(CColSphere)); + } + for(i = 0; i < numSpheres; i++) + spheres[i] = other.spheres[i]; + }else{ + numSpheres = 0; + if(spheres) + RwFree(spheres); + spheres = nil; + } + + // copy lines + if(other.numLines){ + if(numLines != other.numLines){ + numLines = other.numLines; + if(lines) + RwFree(lines); + lines = (CColLine*)RwMalloc(numLines*sizeof(CColLine)); + } + for(i = 0; i < numLines; i++) + lines[i] = other.lines[i]; + }else{ + numLines = 0; + if(lines) + RwFree(lines); + lines = nil; + } + + // copy boxes + if(other.numBoxes){ + if(numBoxes != other.numBoxes){ + numBoxes = other.numBoxes; + if(boxes) + RwFree(boxes); + boxes = (CColBox*)RwMalloc(numBoxes*sizeof(CColBox)); + } + for(i = 0; i < numBoxes; i++) + boxes[i] = other.boxes[i]; + }else{ + numBoxes = 0; + if(boxes) + RwFree(boxes); + boxes = nil; + } + + // copy mesh + if(other.numTriangles){ + // copy vertices + numVerts = 0; + for(i = 0; i < other.numTriangles; i++){ + if(other.triangles[i].a > numVerts) + numVerts = other.triangles[i].a; + if(other.triangles[i].b > numVerts) + numVerts = other.triangles[i].b; + if(other.triangles[i].c > numVerts) + numVerts = other.triangles[i].c; + } + numVerts++; + if(vertices) + RwFree(vertices); + if(numVerts){ + vertices = (CompressedVector*)RwMalloc(numVerts*sizeof(CompressedVector)); + for(i = 0; i < numVerts; i++) + vertices[i] = other.vertices[i]; + } + + // copy triangles + if(numTriangles != other.numTriangles){ + numTriangles = other.numTriangles; + if(triangles) + RwFree(triangles); + triangles = (CColTriangle*)RwMalloc(numTriangles*sizeof(CColTriangle)); + } + for(i = 0; i < numTriangles; i++) + triangles[i] = other.triangles[i]; + }else{ + numTriangles = 0; + if(triangles) + RwFree(triangles); + triangles = nil; + if(vertices) + RwFree(vertices); + vertices = nil; + } + return *this; +} diff --git a/src/miami/collision/ColModel.h b/src/miami/collision/ColModel.h new file mode 100644 index 00000000..64f05f76 --- /dev/null +++ b/src/miami/collision/ColModel.h @@ -0,0 +1,39 @@ +#pragma once + +#include "templates.h" +#include "ColBox.h" +#include "ColSphere.h" +#include "ColLine.h" +#include "ColPoint.h" +#include "ColTriangle.h" + +struct CColModel +{ + CSphere boundingSphere; + CBox boundingBox; + int16 numSpheres; + int16 numBoxes; + int16 numTriangles; + int8 numLines; + uint8 level; // colstore slot but probably still named level + bool ownsCollisionVolumes; + CColSphere *spheres; + CColLine *lines; + CColBox *boxes; + CompressedVector *vertices; + CColTriangle *triangles; + CColTrianglePlane *trianglePlanes; + + CColModel(void); + ~CColModel(void); + void RemoveCollisionVolumes(void); + void CalculateTrianglePlanes(void); + void RemoveTrianglePlanes(void); + CLink *GetLinkPtr(void); + void SetLinkPtr(CLink*); + void GetTrianglePoint(CVector &v, int i) const; + + void *operator new(size_t) throw(); + void operator delete(void *p, size_t) throw(); + CColModel& operator=(const CColModel& other); +}; \ No newline at end of file diff --git a/src/miami/collision/ColPoint.cpp b/src/miami/collision/ColPoint.cpp new file mode 100644 index 00000000..fbf9e8c3 --- /dev/null +++ b/src/miami/collision/ColPoint.cpp @@ -0,0 +1,16 @@ +#include "common.h" +#include "ColPoint.h" + +CColPoint& +CColPoint::operator=(const CColPoint &other) +{ + point = other.point; + normal = other.normal; + surfaceA = other.surfaceA; + pieceA = other.pieceA; + surfaceB = other.surfaceB; + pieceB = other.pieceB; + + // no depth? + return *this; +} diff --git a/src/miami/collision/ColPoint.h b/src/miami/collision/ColPoint.h new file mode 100644 index 00000000..a15b2345 --- /dev/null +++ b/src/miami/collision/ColPoint.h @@ -0,0 +1,34 @@ +#pragma once + +struct CColPoint +{ + CVector point; + int pad1; + // the surface normal on the surface of point + CVector normal; + int pad2; + uint8 surfaceA; + uint8 pieceA; + uint8 surfaceB; + uint8 pieceB; + float depth; + + const CVector &GetNormal() { return normal; } + float GetDepth() { return depth; } + void Set(float depth, uint8 surfA, uint8 pieceA, uint8 surfB, uint8 pieceB) { + this->depth = depth; + this->surfaceA = surfA; + this->pieceA = pieceA; + this->surfaceB = surfB; + this->pieceB = pieceB; + } + void Set(uint8 surfA, uint8 pieceA, uint8 surfB, uint8 pieceB) { + this->surfaceA = surfA; + this->pieceA = pieceA; + this->surfaceB = surfB; + this->pieceB = pieceB; + } + + CColPoint &operator=(const CColPoint &other); +}; + diff --git a/src/miami/collision/ColSphere.cpp b/src/miami/collision/ColSphere.cpp new file mode 100644 index 00000000..65f02860 --- /dev/null +++ b/src/miami/collision/ColSphere.cpp @@ -0,0 +1,27 @@ +#include "common.h" +#include "ColSphere.h" +#include "General.h" + +void +CColSphere::Set(float radius, const CVector ¢er, uint8 surf, uint8 piece) +{ + this->radius = radius; + this->center = center; + this->surface = surf; + this->piece = piece; +} + +bool +CColSphere::IntersectRay(CVector const& from, CVector const& dir, CVector &entry, CVector &exit) +{ + CVector distToCenter = from - center; + float distToTouchSqr = distToCenter.MagnitudeSqr() - sq(radius); + float root1, root2; + + if (!CGeneral::SolveQuadratic(1.0f, DotProduct(distToCenter, dir) * 2.f, distToTouchSqr, root1, root2)) + return false; + + entry = from + dir * root1; + exit = from + dir * root2; + return true; +} \ No newline at end of file diff --git a/src/miami/collision/ColSphere.h b/src/miami/collision/ColSphere.h new file mode 100644 index 00000000..f86b282a --- /dev/null +++ b/src/miami/collision/ColSphere.h @@ -0,0 +1,21 @@ +#pragma once + +#include "SurfaceTable.h" + +struct CSphere +{ + // NB: this has to be compatible with a CVuVector + CVector center; + float radius; + void Set(float radius, const CVector ¢er) { this->center = center; this->radius = radius; } +}; + +struct CColSphere : public CSphere +{ + uint8 surface; + uint8 piece; + + void Set(float radius, const CVector ¢er, uint8 surf, uint8 piece); + bool IntersectRay(CVector const &from, CVector const &dir, CVector &entry, CVector &exit); + using CSphere::Set; +}; \ No newline at end of file diff --git a/src/miami/collision/ColStore.cpp b/src/miami/collision/ColStore.cpp new file mode 100644 index 00000000..da13f01e --- /dev/null +++ b/src/miami/collision/ColStore.cpp @@ -0,0 +1,255 @@ +#include "common.h" + +#include "templates.h" +#include "General.h" +#include "ModelInfo.h" +#include "Streaming.h" +#include "FileLoader.h" +#include "Script.h" +#include "Timer.h" +#include "Camera.h" +#include "Frontend.h" +#include "Physical.h" +#include "ColStore.h" +#include "VarConsole.h" +#include "Pools.h" + +CPool *CColStore::ms_pColPool; +#ifndef MASTER +bool bDispColInMem; +#endif + +void +CColStore::Initialise(void) +{ + if(ms_pColPool == nil) + ms_pColPool = new CPool(COLSTORESIZE, "CollisionFiles"); + AddColSlot("generic"); // slot 0. not streamed +#ifndef MASTER + VarConsole.Add("Display collision in memory", &bDispColInMem, true); +#endif +} + +void +CColStore::Shutdown(void) +{ + int i; + for(i = 0; i < COLSTORESIZE; i++) + RemoveColSlot(i); + if(ms_pColPool) + delete ms_pColPool; + ms_pColPool = nil; +} + +int +CColStore::AddColSlot(const char *name) +{ + ColDef *def = ms_pColPool->New(); + assert(def); + def->isLoaded = false; + def->unused = 0; + def->bounds.left = 1000000.0f; + def->bounds.top = 1000000.0f; + def->bounds.right = -1000000.0f; + def->bounds.bottom = -1000000.0f; + def->minIndex = INT16_MAX; + def->maxIndex = INT16_MIN; + strcpy(def->name, name); + return ms_pColPool->GetJustIndex(def); +} + +void +CColStore::RemoveColSlot(int32 slot) +{ + if(GetSlot(slot)){ + if(GetSlot(slot)->isLoaded) + RemoveCol(slot); + ms_pColPool->Delete(GetSlot(slot)); + } +} + +int +CColStore::FindColSlot(const char *name) +{ + ColDef *def; + int size = ms_pColPool->GetSize(); + for(int i = 0; i < size; i++){ + def = GetSlot(i); + if(def && !CGeneral::faststricmp(def->name, name)) + return i; + } + return -1; +} + +char* +CColStore::GetColName(int32 slot) +{ + return GetSlot(slot)->name; +} + +CRect& +CColStore::GetBoundingBox(int32 slot) +{ + return GetSlot(slot)->bounds; +} + +void +CColStore::IncludeModelIndex(int32 slot, int32 modelIndex) +{ + ColDef *def = GetSlot(slot); + if(modelIndex < def->minIndex) + def->minIndex = modelIndex; + if(modelIndex > def->maxIndex) + def->maxIndex = modelIndex; +} + +bool +CColStore::LoadCol(int32 slot, uint8 *buffer, int32 bufsize) +{ + bool success; + ColDef *def = GetSlot(slot); + if(def->minIndex > def->maxIndex) + success = CFileLoader::LoadCollisionFileFirstTime(buffer, bufsize, slot); + else + success = CFileLoader::LoadCollisionFile(buffer, bufsize, slot); + if(success) + def->isLoaded = true; + else + debug("Failed to load Collision\n"); + return success; +} + +void +CColStore::RemoveCol(int32 slot) +{ + int id; + GetSlot(slot)->isLoaded = false; + for(id = 0; id < MODELINFOSIZE; id++){ + CBaseModelInfo *mi = CModelInfo::GetModelInfo(id); + if(mi){ + CColModel *col = mi->GetColModel(); + if(col && col->level == slot) + col->RemoveCollisionVolumes(); + } + } +} + +void +CColStore::LoadAllCollision(void) +{ + int i; + for(i = 1; i < COLSTORESIZE; i++) + if(GetSlot(i)) + CStreaming::RequestCol(i, 0); + + CStreaming::LoadAllRequestedModels(false); +} + +void +CColStore::RemoveAllCollision(void) +{ + int i; + for(i = 1; i < COLSTORESIZE; i++) + if(GetSlot(i)) + if(CStreaming::CanRemoveCol(i)) + CStreaming::RemoveCol(i); +} + +static bool bLoadAtSecondPosition; +static CVector2D secondPosition; + +void +CColStore::AddCollisionNeededAtPosn(const CVector2D &pos) +{ + bLoadAtSecondPosition = true; + secondPosition = pos; +} + +void +CColStore::LoadCollision(const CVector2D &pos) +{ + int i; + + if(CStreaming::ms_disableStreaming) + return; + + for(i = 1; i < COLSTORESIZE; i++){ + if(GetSlot(i) == nil) + continue; + + bool wantThisOne = false; + + if(GetBoundingBox(i).IsPointInside(pos) || + bLoadAtSecondPosition && GetBoundingBox(i).IsPointInside(secondPosition, -119.0f) || + strcmp(GetColName(i), "yacht") == 0){ + wantThisOne = true; + }else{ + for (int j = 0; j < MAX_CLEANUP; j++) { + CPhysical* pEntity = nil; + cleanup_entity_struct* pCleanup = &CTheScripts::MissionCleanUp.m_sEntities[j]; + if (pCleanup->type == CLEANUP_CAR) { + pEntity = CPools::GetVehiclePool()->GetAt(pCleanup->id); + if (!pEntity || pEntity->GetStatus() == STATUS_WRECKED) + continue; + } + else if (pCleanup->type == CLEANUP_CHAR) { + pEntity = CPools::GetPedPool()->GetAt(pCleanup->id); + if (!pEntity || ((CPed*)pEntity)->DyingOrDead()) + continue; + } + if (pEntity && !pEntity->bDontLoadCollision && !pEntity->bIsFrozen) { + if (GetBoundingBox(i).IsPointInside(pEntity->GetPosition(), -80.0f)) + wantThisOne = true; + } + } + } + + if(wantThisOne) + CStreaming::RequestCol(i, STREAMFLAGS_PRIORITY); + else + CStreaming::RemoveCol(i); + } + bLoadAtSecondPosition = false; +} + +void +CColStore::RequestCollision(const CVector2D &pos) +{ + int i; + + for(i = 1; i < COLSTORESIZE; i++) + if(GetSlot(i) && GetBoundingBox(i).IsPointInside(pos, -115.0f)) + CStreaming::RequestCol(i, STREAMFLAGS_PRIORITY); +} + +void +CColStore::EnsureCollisionIsInMemory(const CVector2D &pos) +{ + int i; + + if(CStreaming::ms_disableStreaming) + return; + + for(i = 1; i < COLSTORESIZE; i++) + if(GetSlot(i) && GetBoundingBox(i).IsPointInside(pos, -110.0f) && + !CStreaming::HasColLoaded(i)){ + CStreaming::RequestCol(i, 0); + if(TheCamera.GetScreenFadeStatus() == FADE_0) + FrontEndMenuManager.MessageScreen("LOADCOL", false); + CTimer::Suspend(); + CStreaming::LoadAllRequestedModels(false); + CTimer::Resume(); + } +} + +bool +CColStore::HasCollisionLoaded(const CVector2D &pos) +{ + int i; + + for(i = 1; i < COLSTORESIZE; i++) + if(GetSlot(i) && GetBoundingBox(i).IsPointInside(pos, -115.0f) && + !GetSlot(i)->isLoaded) + return false; + return true; +} diff --git a/src/miami/collision/ColStore.h b/src/miami/collision/ColStore.h new file mode 100644 index 00000000..8e2a3a70 --- /dev/null +++ b/src/miami/collision/ColStore.h @@ -0,0 +1,43 @@ +#pragma once + +#include "templates.h" + +struct ColDef { // made up name + int32 unused; + bool isLoaded; + CRect bounds; + char name[20]; + int16 minIndex; + int16 maxIndex; +}; + +class CColStore +{ + static CPool *ms_pColPool; + +public: + static void Initialise(void); + static void Shutdown(void); + static int AddColSlot(const char *name); + static void RemoveColSlot(int32 slot); + static int FindColSlot(const char *name); + static char *GetColName(int32 slot); + static CRect &GetBoundingBox(int32 slot); + static void IncludeModelIndex(int32 slot, int32 modelIndex); + static bool LoadCol(int32 storeID, uint8 *buffer, int32 bufsize); + static void RemoveCol(int32 slot); + static void AddCollisionNeededAtPosn(const CVector2D &pos); + static void LoadAllCollision(void); + static void RemoveAllCollision(void); + static void LoadCollision(const CVector2D &pos); + static void RequestCollision(const CVector2D &pos); + static void EnsureCollisionIsInMemory(const CVector2D &pos); + static bool HasCollisionLoaded(const CVector2D &pos); + + static ColDef *GetSlot(int slot) { + assert(slot >= 0); + assert(ms_pColPool); + assert(slot < ms_pColPool->GetSize()); + return ms_pColPool->GetSlot(slot); + } +}; diff --git a/src/miami/collision/ColTriangle.cpp b/src/miami/collision/ColTriangle.cpp new file mode 100644 index 00000000..843fb93f --- /dev/null +++ b/src/miami/collision/ColTriangle.cpp @@ -0,0 +1,32 @@ +#include "common.h" +#include "ColTriangle.h" + +#ifdef VU_COLLISION +void +CColTrianglePlane::Set(const CVector &va, const CVector &vb, const CVector &vc) +{ + CVector norm = CrossProduct(vc-va, vb-va); + norm.Normalise(); + float d = DotProduct(norm, va); + normal.x = norm.x*4096.0f; + normal.y = norm.y*4096.0f; + normal.z = norm.z*4096.0f; + dist = d*128.0f; +} +#else +void +CColTrianglePlane::Set(const CVector &va, const CVector &vb, const CVector &vc) +{ + normal = CrossProduct(vc-va, vb-va); + normal.Normalise(); + dist = DotProduct(normal, va); + CVector an(Abs(normal.x), Abs(normal.y), Abs(normal.z)); + // find out largest component and its direction + if(an.x > an.y && an.x > an.z) + dir = normal.x < 0.0f ? DIR_X_NEG : DIR_X_POS; + else if(an.y > an.z) + dir = normal.y < 0.0f ? DIR_Y_NEG : DIR_Y_POS; + else + dir = normal.z < 0.0f ? DIR_Z_NEG : DIR_Z_POS; +} +#endif \ No newline at end of file diff --git a/src/miami/collision/ColTriangle.h b/src/miami/collision/ColTriangle.h new file mode 100644 index 00000000..5af0cf76 --- /dev/null +++ b/src/miami/collision/ColTriangle.h @@ -0,0 +1,77 @@ +#pragma once + +#include "CompressedVector.h" + +enum Direction { + DIR_X_POS, + DIR_X_NEG, + DIR_Y_POS, + DIR_Y_NEG, + DIR_Z_POS, + DIR_Z_NEG, +}; + +struct CColTriangle +{ + uint16 a; + uint16 b; + uint16 c; + uint8 surface; + + void Set(uint16 a, uint16 b, uint16 c, uint8 surf) + { + this->a = a; + this->b = b; + this->c = c; + this->surface = surf; + } +}; + +struct CColTrianglePlane +{ +#ifdef VU_COLLISION + CompressedVector normal; + int16 dist; + + void Set(const CVector &va, const CVector &vb, const CVector &vc); + void Set(const CompressedVector *v, CColTriangle &tri) { Set(v[tri.a].Get(), v[tri.b].Get(), v[tri.c].Get()); } + void GetNormal(CVector &n) const { n.x = normal.x/4096.0f; n.y = normal.y/4096.0f; n.z = normal.z/4096.0f; } + float CalcPoint(const CVector &v) const { CVector n; GetNormal(n); return DotProduct(n, v) - dist/128.0f; }; +#ifdef GTA_PS2 + void Unpack(uint128 &qword) const { + __asm__ volatile ( + "lh $8, 0(%1)\n" + "lh $9, 2(%1)\n" + "lh $10, 4(%1)\n" + "lh $11, 6(%1)\n" + "pextlw $10, $8\n" + "pextlw $11, $9\n" + "pextlw $2, $11, $10\n" + "sq $2, %0\n" + : "=m" (qword) + : "r" (this) + : "$8", "$9", "$10", "$11", "$2" + ); + } +#else + void Unpack(int32 *qword) const { + qword[0] = normal.x; + qword[1] = normal.y; + qword[2] = normal.z; + qword[3] = dist; + } +#endif +#else + CVector normal; + float dist; + uint8 dir; + + void Set(const CVector &va, const CVector &vb, const CVector &vc); + void Set(const CompressedVector *v, CColTriangle &tri) { Set(v[tri.a].Get(), v[tri.b].Get(), v[tri.c].Get()); } + void GetNormal(CVector &n) const { n = normal; } + float GetNormalX() const { return normal.x; } + float GetNormalY() const { return normal.y; } + float GetNormalZ() const { return normal.z; } + float CalcPoint(const CVector &v) const { return DotProduct(normal, v) - dist; }; +#endif +}; \ No newline at end of file diff --git a/src/miami/collision/Collision.cpp b/src/miami/collision/Collision.cpp new file mode 100644 index 00000000..f39f3f35 --- /dev/null +++ b/src/miami/collision/Collision.cpp @@ -0,0 +1,2590 @@ +#include "common.h" + +#include "VuVector.h" +#include "main.h" +#include "Lists.h" +#include "Game.h" +#include "Zones.h" +#include "General.h" +#include "ZoneCull.h" +#include "World.h" +#include "Entity.h" +#include "Train.h" +#include "Streaming.h" +#include "Pad.h" +#include "DMAudio.h" +#include "Population.h" +#include "FileLoader.h" +#include "Replay.h" +#include "CutsceneMgr.h" +#include "RenderBuffer.h" +#include "SurfaceTable.h" +#include "Lines.h" +#include "Collision.h" +#include "Camera.h" +#include "ColStore.h" + +#ifdef VU_COLLISION +#include "VuCollision.h" + +inline int +GetVUresult(void) +{ +#ifdef GTA_PS2 + int ret; + __asm__ volatile ( + "cfc2.i %0,vi01\n" // .i important! wait for VU0 to finish + : "=r" (ret) + ); + return ret; +#else + return vi01; +#endif +} + +inline int +GetVUresult(CVuVector &point, CVuVector &normal, float &dist) +{ +#ifdef GTA_PS2 + int ret; + __asm__ volatile ( + "cfc2.i %0,vi01\n" // .i important! wait for VU0 to finish + "sqc2 vf01,(%1)\n" + "sqc2 vf02,(%2)\n" + "qmfc2 $12,vf03\n" + "sw $12,(%3)\n" + : "=r" (ret) + : "r" (&point), "r" (&normal), "r" (&dist) + : "$12" + ); + return ret; +#else + point = vf01; + normal = vf02; + dist = vf03.x; + return vi01; +#endif +} + +#endif + +eLevelName CCollision::ms_collisionInMemory; +CLinkList CCollision::ms_colModelCache; + +void +CCollision::Init(void) +{ + ms_colModelCache.Init(NUMCOLCACHELINKS); + ms_collisionInMemory = LEVEL_GENERIC; + CColStore::Initialise(); +} + +void +CCollision::Shutdown(void) +{ + ms_colModelCache.Shutdown(); + CColStore::Shutdown(); +} + +void +CCollision::Update(void) +{ +} + +// unused +eLevelName +GetCollisionInSectorList(CPtrList &list) +{ + CPtrNode *node; + CEntity *e; + int level; + + for(node = list.first; node; node = node->next){ + e = (CEntity*)node->item; + level = CModelInfo::GetColModel(e->GetModelIndex())->level; + if(level != LEVEL_GENERIC) + return (eLevelName)level; + } + return LEVEL_GENERIC; +} + +// unused +// Get a level this sector is in based on collision models +eLevelName +GetCollisionInSector(CSector §) +{ + int level; + + level = GetCollisionInSectorList(sect.m_lists[ENTITYLIST_BUILDINGS]); + if(level == LEVEL_GENERIC) + level = GetCollisionInSectorList(sect.m_lists[ENTITYLIST_BUILDINGS_OVERLAP]); + if(level == LEVEL_GENERIC) + level = GetCollisionInSectorList(sect.m_lists[ENTITYLIST_OBJECTS]); + if(level == LEVEL_GENERIC) + level = GetCollisionInSectorList(sect.m_lists[ENTITYLIST_OBJECTS_OVERLAP]); + if(level == LEVEL_GENERIC) + level = GetCollisionInSectorList(sect.m_lists[ENTITYLIST_DUMMIES]); + if(level == LEVEL_GENERIC) + level = GetCollisionInSectorList(sect.m_lists[ENTITYLIST_DUMMIES_OVERLAP]); + return (eLevelName)level; +} + +void +CCollision::LoadCollisionWhenINeedIt(bool forceChange) +{ +} + +void +CCollision::SortOutCollisionAfterLoad(void) +{ + CColStore::LoadCollision(TheCamera.GetPosition()); + CStreaming::LoadAllRequestedModels(false); +} + +void +CCollision::LoadCollisionScreen(eLevelName level) +{ + static Const char *levelNames[] = { + "", + "IND_ZON", + "COM_ZON", + }; + + // Why twice? + LoadingIslandScreen(levelNames[level]); + LoadingIslandScreen(levelNames[level]); +} + +// +// Test +// + + +bool +CCollision::TestSphereSphere(const CSphere &s1, const CSphere &s2) +{ + float d = s1.radius + s2.radius; + return (s1.center - s2.center).MagnitudeSqr() < d*d; +} + +bool +CCollision::TestSphereBox(const CSphere &sph, const CBox &box) +{ + if(sph.center.x + sph.radius < box.min.x) return false; + if(sph.center.x - sph.radius > box.max.x) return false; + if(sph.center.y + sph.radius < box.min.y) return false; + if(sph.center.y - sph.radius > box.max.y) return false; + if(sph.center.z + sph.radius < box.min.z) return false; + if(sph.center.z - sph.radius > box.max.z) return false; + return true; +} + +bool +CCollision::TestLineBox(const CColLine &line, const CBox &box) +{ + float t, x, y, z; + // If either line point is in the box, we have a collision + if(line.p0.x > box.min.x && line.p0.x < box.max.x && + line.p0.y > box.min.y && line.p0.y < box.max.y && + line.p0.z > box.min.z && line.p0.z < box.max.z) + return true; + if(line.p1.x > box.min.x && line.p1.x < box.max.x && + line.p1.y > box.min.y && line.p1.y < box.max.y && + line.p1.z > box.min.z && line.p1.z < box.max.z) + return true; + + // check if points are on opposite sides of min x plane + if((box.min.x - line.p1.x) * (box.min.x - line.p0.x) < 0.0f){ + // parameter along line where we intersect + t = (box.min.x - line.p0.x) / (line.p1.x - line.p0.x); + // y of intersection + y = line.p0.y + (line.p1.y - line.p0.y)*t; + if(y > box.min.y && y < box.max.y){ + // z of intersection + z = line.p0.z + (line.p1.z - line.p0.z)*t; + if(z > box.min.z && z < box.max.z) + return true; + } + } + + // same test with max x plane + if((line.p1.x - box.max.x) * (line.p0.x - box.max.x) < 0.0f){ + t = (line.p0.x - box.max.x) / (line.p0.x - line.p1.x); + y = line.p0.y + (line.p1.y - line.p0.y)*t; + if(y > box.min.y && y < box.max.y){ + z = line.p0.z + (line.p1.z - line.p0.z)*t; + if(z > box.min.z && z < box.max.z) + return true; + } + } + + // min y plne + if((box.min.y - line.p0.y) * (box.min.y - line.p1.y) < 0.0f){ + t = (box.min.y - line.p0.y) / (line.p1.y - line.p0.y); + x = line.p0.x + (line.p1.x - line.p0.x)*t; + if(x > box.min.x && x < box.max.x){ + z = line.p0.z + (line.p1.z - line.p0.z)*t; + if(z > box.min.z && z < box.max.z) + return true; + } + } + + // max y plane + if((line.p0.y - box.max.y) * (line.p1.y - box.max.y) < 0.0f){ + t = (line.p0.y - box.max.y) / (line.p0.y - line.p1.y); + x = line.p0.x + (line.p1.x - line.p0.x)*t; + if(x > box.min.x && x < box.max.x){ + z = line.p0.z + (line.p1.z - line.p0.z)*t; + if(z > box.min.z && z < box.max.z) + return true; + } + } + + // min z plne + if((box.min.z - line.p0.z) * (box.min.z - line.p1.z) < 0.0f){ + t = (box.min.z - line.p0.z) / (line.p1.z - line.p0.z); + x = line.p0.x + (line.p1.x - line.p0.x)*t; + if(x > box.min.x && x < box.max.x){ + y = line.p0.y + (line.p1.y - line.p0.y)*t; + if(y > box.min.y && y < box.max.y) + return true; + } + } + + // max z plane + if((line.p0.z - box.max.z) * (line.p1.z - box.max.z) < 0.0f){ + t = (line.p0.z - box.max.z) / (line.p0.z - line.p1.z); + x = line.p0.x + (line.p1.x - line.p0.x)*t; + if(x > box.min.x && x < box.max.x){ + y = line.p0.y + (line.p1.y - line.p0.y)*t; + if(y > box.min.y && y < box.max.y) + return true; + } + } + return false; +} + +bool +CCollision::TestVerticalLineBox(const CColLine &line, const CBox &box) +{ + if(line.p0.x <= box.min.x) return false; + if(line.p0.y <= box.min.y) return false; + if(line.p0.x >= box.max.x) return false; + if(line.p0.y >= box.max.y) return false; + if(line.p0.z < line.p1.z){ + if(line.p0.z > box.max.z) return false; + if(line.p1.z < box.min.z) return false; + }else{ + if(line.p1.z > box.max.z) return false; + if(line.p0.z < box.min.z) return false; + } + return true; +} + +bool +CCollision::TestLineTriangle(const CColLine &line, const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane) +{ +#ifdef VU_COLLISION + // not used in favour of optimized loops + VuTriangle vutri; + verts[tri.a].Unpack(vutri.v0); + verts[tri.b].Unpack(vutri.v1); + verts[tri.c].Unpack(vutri.v2); + plane.Unpack(vutri.plane); + + LineToTriangleCollisionCompressed(*(CVuVector*)&line.p0, *(CVuVector*)&line.p1, vutri); + + if(GetVUresult()) + return true; + return false; +#else + float t; + CVector normal; + plane.GetNormal(normal); + + // if points are on the same side, no collision + if(plane.CalcPoint(line.p0) * plane.CalcPoint(line.p1) > 0.0f) + return false; + + float p0dist = DotProduct(line.p1 - line.p0, normal); + +#ifdef FIX_BUGS + // line lines in the plane, assume no collision + if (p0dist == 0.0f) + return false; +#endif + + // intersection parameter on line + t = -plane.CalcPoint(line.p0) / p0dist; + // find point of intersection + CVector p = line.p0 + (line.p1-line.p0)*t; + + const CVector &va = verts[tri.a].Get(); + const CVector &vb = verts[tri.b].Get(); + const CVector &vc = verts[tri.c].Get(); + CVector2D vec1, vec2, vec3, vect; + + // We do the test in 2D. With the plane direction we + // can figure out how to project the vectors. + // normal = (c-a) x (b-a) + switch(plane.dir){ + case DIR_X_POS: + vec1.x = va.y; vec1.y = va.z; + vec2.x = vc.y; vec2.y = vc.z; + vec3.x = vb.y; vec3.y = vb.z; + vect.x = p.y; vect.y = p.z; + break; + case DIR_X_NEG: + vec1.x = va.y; vec1.y = va.z; + vec2.x = vb.y; vec2.y = vb.z; + vec3.x = vc.y; vec3.y = vc.z; + vect.x = p.y; vect.y = p.z; + break; + case DIR_Y_POS: + vec1.x = va.z; vec1.y = va.x; + vec2.x = vc.z; vec2.y = vc.x; + vec3.x = vb.z; vec3.y = vb.x; + vect.x = p.z; vect.y = p.x; + break; + case DIR_Y_NEG: + vec1.x = va.z; vec1.y = va.x; + vec2.x = vb.z; vec2.y = vb.x; + vec3.x = vc.z; vec3.y = vc.x; + vect.x = p.z; vect.y = p.x; + break; + case DIR_Z_POS: + vec1.x = va.x; vec1.y = va.y; + vec2.x = vc.x; vec2.y = vc.y; + vec3.x = vb.x; vec3.y = vb.y; + vect.x = p.x; vect.y = p.y; + break; + case DIR_Z_NEG: + vec1.x = va.x; vec1.y = va.y; + vec2.x = vb.x; vec2.y = vb.y; + vec3.x = vc.x; vec3.y = vc.y; + vect.x = p.x; vect.y = p.y; + break; + default: + assert(0); + } + // This is our triangle: + // 3-------2 + // \ P / + // \ / + // \ / + // 1 + // We can use the "2d cross product" to check on which side + // a vector is of another. Test is true if point is inside of all edges. + if(CrossProduct2D(vec2-vec1, vect-vec1) < 0.0f) return false; + if(CrossProduct2D(vec3-vec1, vect-vec1) > 0.0f) return false; + if(CrossProduct2D(vec3-vec2, vect-vec2) < 0.0f) return false; + return true; +#endif +} + +// Test if line segment intersects with sphere. +// If the first point is inside the sphere this test does not register a collision! +// The code is reversed from the original code and rather ugly, see Process for a clear version. +// TODO: actually rewrite this mess +bool +CCollision::TestLineSphere(const CColLine &line, const CColSphere &sph) +{ + CVector v01 = line.p1 - line.p0; // vector from p0 to p1 + CVector v0c = sph.center - line.p0; // vector from p0 to center + float linesq = v01.MagnitudeSqr(); + // I leave in the strange -2 factors even though they serve no real purpose + float projline = -2.0f * DotProduct(v01, v0c); // project v0c onto line + // Square of tangent from p0 multiplied by line length so we can compare with projline. + // The length of the tangent would be this: Sqrt((c-p0)^2 - r^2). + // Negative if p0 is inside the sphere! This breaks the test! + float tansq = 4.0f * linesq * + (sph.center.MagnitudeSqr() - 2.0f*DotProduct(sph.center, line.p0) + line.p0.MagnitudeSqr() - sph.radius*sph.radius); + float diffsq = projline*projline - tansq; + // if diffsq < 0 that means the line is a passant, so no intersection + if(diffsq < 0.0f) + return false; + // projline (negative in GTA for some reason) is the point on the line + // in the middle of the two intersection points (startin from p0). + // Sqrt(diffsq) somehow works out to be the distance from that + // midpoint to the intersection points. + // So subtract that and get rid of the awkward scaling: + float f = (-projline - Sqrt(diffsq)) / (2.0f*linesq); + // f should now be in range [0, 1] for [p0, p1] + return f >= 0.0f && f <= 1.0f; +} + +bool +CCollision::TestSphereTriangle(const CColSphere &sphere, + const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane) +{ +#ifdef VU_COLLISION + // not used in favour of optimized loops + VuTriangle vutri; + verts[tri.a].Unpack(vutri.v0); + verts[tri.b].Unpack(vutri.v1); + verts[tri.c].Unpack(vutri.v2); + plane.Unpack(vutri.plane); + + SphereToTriangleCollisionCompressed(*(CVuVector*)&sphere, vutri); + + if(GetVUresult()) + return true; + return false; +#else + // If sphere and plane don't intersect, no collision + float planedist = plane.CalcPoint(sphere.center); + if(Abs(planedist) > sphere.radius) + return false; + + const CVector &va = verts[tri.a].Get(); + const CVector &vb = verts[tri.b].Get(); + const CVector &vc = verts[tri.c].Get(); + + // calculate two orthogonal basis vectors for the triangle + CVector vec2 = vb - va; + float len = vec2.Magnitude(); + vec2 = vec2 * (1.0f/len); + CVector normal; + plane.GetNormal(normal); + CVector vec1 = CrossProduct(vec2, normal); + + // We know A has local coordinate [0,0] and B has [0,len]. + // Now calculate coordinates on triangle for these two vectors: + CVector vac = vc - va; + CVector vas = sphere.center - va; + CVector2D b(0.0f, len); + CVector2D c(DotProduct(vec1, vac), DotProduct(vec2, vac)); + CVector2D s(DotProduct(vec1, vas), DotProduct(vec2, vas)); + + // The three triangle lines partition the space into 6 sectors, + // find out in which the center lies. + int insideAB = CrossProduct2D(s, b) >= 0.0f; + int insideAC = CrossProduct2D(c, s) >= 0.0f; + int insideBC = CrossProduct2D(s-b, c-b) >= 0.0f; + + int testcase = insideAB + insideAC + insideBC; + float dist = 0.0f; + switch(testcase){ + case 0: + return false; // shouldn't happen + case 1: + // closest to a vertex + if(insideAB) dist = (sphere.center - vc).Magnitude(); + else if(insideAC) dist = (sphere.center - vb).Magnitude(); + else if(insideBC) dist = (sphere.center - va).Magnitude(); + else assert(0); + break; + case 2: + // closest to an edge + // looks like original game as DistToLine manually inlined + if(!insideAB) dist = DistToLine(&va, &vb, &sphere.center); + else if(!insideAC) dist = DistToLine(&va, &vc, &sphere.center); + else if(!insideBC) dist = DistToLine(&vb, &vc, &sphere.center); + else assert(0); + break; + case 3: + // center is in triangle + dist = Abs(planedist); + break; + default: + assert(0); + } + + return dist < sphere.radius; +#endif +} + +bool +CCollision::TestLineOfSight(const CColLine &line, const CMatrix &matrix, CColModel &model, bool ignoreSeeThrough, bool ignoreShootThrough) +{ +#ifdef VU_COLLISION + CMatrix matTransform; + int i; + + // transform line to model space + Invert(matrix, matTransform); + CVuVector newline[2]; + TransformPoints(newline, 2, matTransform, &line.p0, sizeof(CColLine)/2); + + // If we don't intersect with the bounding box, no chance on the rest + if(!TestLineBox(*(CColLine*)newline, model.boundingBox)) + return false; + + for(i = 0; i < model.numSpheres; i++){ + if(ignoreSeeThrough && IsSeeThrough(model.spheres[i].surface)) continue; + if(ignoreShootThrough && IsShootThrough(model.spheres[i].surface)) continue; + if(TestLineSphere(*(CColLine*)newline, model.spheres[i])) + return true; + } + + for(i = 0; i < model.numBoxes; i++){ + if(ignoreSeeThrough && IsSeeThrough(model.boxes[i].surface)) continue; + if(ignoreShootThrough && IsShootThrough(model.boxes[i].surface)) continue; + if(TestLineBox(*(CColLine*)newline, model.boxes[i])) + return true; + } + + CalculateTrianglePlanes(&model); + int lastTest = -1; + VuTriangle vutri; + for(i = 0; i < model.numTriangles; i++){ + if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue; + if(ignoreShootThrough && IsShootThrough(model.triangles[i].surface)) continue; + + CColTriangle *tri = &model.triangles[i]; + model.vertices[tri->a].Unpack(vutri.v0); + model.vertices[tri->b].Unpack(vutri.v1); + model.vertices[tri->c].Unpack(vutri.v2); + model.trianglePlanes[i].Unpack(vutri.plane); + + LineToTriangleCollisionCompressed(newline[0], newline[1], vutri); + lastTest = i; + break; + } +#ifdef FIX_BUGS + // no need to check first again + i++; +#endif + for(; i < model.numTriangles; i++){ + if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue; + if(ignoreShootThrough && IsShootThrough(model.triangles[i].surface)) continue; + + CColTriangle *tri = &model.triangles[i]; + model.vertices[tri->a].Unpack(vutri.v0); + model.vertices[tri->b].Unpack(vutri.v1); + model.vertices[tri->c].Unpack(vutri.v2); + model.trianglePlanes[i].Unpack(vutri.plane); + + if(GetVUresult()) + return true; + + LineToTriangleCollisionCompressed(newline[0], newline[1], vutri); + lastTest = i; + + } + if(lastTest != -1 && GetVUresult()) + return true; + + return false; +#else + static CMatrix matTransform; + int i; + + // transform line to model space + Invert(matrix, matTransform); + CColLine newline(matTransform * line.p0, matTransform * line.p1); + + // If we don't intersect with the bounding box, no chance on the rest + if(!TestLineBox(newline, model.boundingBox)) + return false; + + for(i = 0; i < model.numSpheres; i++){ + if(ignoreSeeThrough && IsSeeThrough(model.spheres[i].surface)) continue; + if(ignoreShootThrough && IsShootThrough(model.spheres[i].surface)) continue; + if(TestLineSphere(newline, model.spheres[i])) + return true; + } + + for(i = 0; i < model.numBoxes; i++){ + if(ignoreSeeThrough && IsSeeThrough(model.boxes[i].surface)) continue; + if(ignoreShootThrough && IsShootThrough(model.boxes[i].surface)) continue; + if(TestLineBox(newline, model.boxes[i])) + return true; + } + + CalculateTrianglePlanes(&model); + for(i = 0; i < model.numTriangles; i++){ + if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue; + if(ignoreShootThrough && IsShootThrough(model.triangles[i].surface)) continue; + if(TestLineTriangle(newline, model.vertices, model.triangles[i], model.trianglePlanes[i])) + return true; + } + + return false; +#endif +} + +// TODO: TestPillWithSpheresInColModel, but only called from overloaded CWeapon::FireMelee which isn't used + +// +// Process +// + +// For Spheres mindist is the squared distance to its center +// For Lines mindist is between [0,1] + +bool +CCollision::ProcessSphereSphere(const CColSphere &s1, const CColSphere &s2, CColPoint &point, float &mindistsq) +{ + CVector dist = s1.center - s2.center; + float d = dist.Magnitude() - s2.radius; // distance from s1's center to s2 + float depth = s1.radius - d; // sphere overlap + if(d < 0.0f) d = 0.0f; // clamp to zero, i.e. if s1's center is inside s2 + // no collision if sphere is not close enough + if(d*d < mindistsq && d < s1.radius){ + dist.Normalise(); + point.point = s1.center - dist*d; + point.normal = dist; +#ifndef VU_COLLISION + point.surfaceA = s1.surface; + point.pieceA = s1.piece; + point.surfaceB = s2.surface; + point.pieceB = s2.piece; +#endif + point.depth = depth; + mindistsq = d*d; // collision radius + return true; + } + return false; +} + +bool +CCollision::ProcessSphereBox(const CColSphere &sph, const CColBox &box, CColPoint &point, float &mindistsq) +{ + CVector p; + CVector dist; + + // GTA's code is too complicated, uses a huge 3x3x3 if statement + // we can simplify the structure a lot + + // first make sure we have a collision at all + if(sph.center.x + sph.radius < box.min.x) return false; + if(sph.center.x - sph.radius > box.max.x) return false; + if(sph.center.y + sph.radius < box.min.y) return false; + if(sph.center.y - sph.radius > box.max.y) return false; + if(sph.center.z + sph.radius < box.min.z) return false; + if(sph.center.z - sph.radius > box.max.z) return false; + + // Now find out where the sphere center lies in relation to all the sides + int xpos = sph.center.x < box.min.x ? 1 : + sph.center.x > box.max.x ? 2 : + 0; + int ypos = sph.center.y < box.min.y ? 1 : + sph.center.y > box.max.y ? 2 : + 0; + int zpos = sph.center.z < box.min.z ? 1 : + sph.center.z > box.max.z ? 2 : + 0; + + if(xpos == 0 && ypos == 0 && zpos == 0){ + // sphere is inside the box + p = (box.min + box.max)*0.5f; + + dist = sph.center - p; + float lensq = dist.MagnitudeSqr(); + if(lensq < mindistsq){ + point.normal = dist * (1.0f/Sqrt(lensq)); + point.point = sph.center - point.normal; +#ifndef VU_COLLISION + point.surfaceA = sph.surface; + point.pieceA = sph.piece; + point.surfaceB = box.surface; + point.pieceB = box.piece; +#endif + + // find absolute distance to the closer side in each dimension + float dx = dist.x > 0.0f ? + box.max.x - sph.center.x : + sph.center.x - box.min.x; + float dy = dist.y > 0.0f ? + box.max.y - sph.center.y : + sph.center.y - box.min.y; + float dz = dist.z > 0.0f ? + box.max.z - sph.center.z : + sph.center.z - box.min.z; + // collision depth is maximum of that: + if(dx > dy && dx > dz) + point.depth = dx; + else if(dy > dz) + point.depth = dy; + else + point.depth = dz; + return true; + } + }else{ + // sphere is outside. + // closest point on box: + p.x = xpos == 1 ? box.min.x : + xpos == 2 ? box.max.x : + sph.center.x; + p.y = ypos == 1 ? box.min.y : + ypos == 2 ? box.max.y : + sph.center.y; + p.z = zpos == 1 ? box.min.z : + zpos == 2 ? box.max.z : + sph.center.z; + + dist = sph.center - p; + float lensq = dist.MagnitudeSqr(); + if(lensq < mindistsq){ + float len = Sqrt(lensq); + point.point = p; + point.normal = dist * (1.0f/len); +#ifndef VU_COLLISION + point.surfaceA = sph.surface; + point.pieceA = sph.piece; + point.surfaceB = box.surface; + point.pieceB = box.piece; +#endif + point.depth = sph.radius - len; + mindistsq = lensq; + return true; + } + } + return false; +} + +bool +CCollision::ProcessLineBox(const CColLine &line, const CColBox &box, CColPoint &point, float &mindist) +{ + float mint, t, x, y, z; + CVector normal; + CVector p; + + mint = 1.0f; + // check if points are on opposite sides of min x plane + if((box.min.x - line.p1.x) * (box.min.x - line.p0.x) < 0.0f){ + // parameter along line where we intersect + t = (box.min.x - line.p0.x) / (line.p1.x - line.p0.x); + // y of intersection + y = line.p0.y + (line.p1.y - line.p0.y)*t; + if(y > box.min.y && y < box.max.y){ + // z of intersection + z = line.p0.z + (line.p1.z - line.p0.z)*t; + if(z > box.min.z && z < box.max.z) + if(t < mint){ + mint = t; + p = CVector(box.min.x, y, z); + normal = CVector(-1.0f, 0.0f, 0.0f); + } + } + } + + // max x plane + if((line.p1.x - box.max.x) * (line.p0.x - box.max.x) < 0.0f){ + t = (line.p0.x - box.max.x) / (line.p0.x - line.p1.x); + y = line.p0.y + (line.p1.y - line.p0.y)*t; + if(y > box.min.y && y < box.max.y){ + z = line.p0.z + (line.p1.z - line.p0.z)*t; + if(z > box.min.z && z < box.max.z) + if(t < mint){ + mint = t; + p = CVector(box.max.x, y, z); + normal = CVector(1.0f, 0.0f, 0.0f); + } + } + } + + // min y plne + if((box.min.y - line.p0.y) * (box.min.y - line.p1.y) < 0.0f){ + t = (box.min.y - line.p0.y) / (line.p1.y - line.p0.y); + x = line.p0.x + (line.p1.x - line.p0.x)*t; + if(x > box.min.x && x < box.max.x){ + z = line.p0.z + (line.p1.z - line.p0.z)*t; + if(z > box.min.z && z < box.max.z) + if(t < mint){ + mint = t; + p = CVector(x, box.min.y, z); + normal = CVector(0.0f, -1.0f, 0.0f); + } + } + } + + // max y plane + if((line.p0.y - box.max.y) * (line.p1.y - box.max.y) < 0.0f){ + t = (line.p0.y - box.max.y) / (line.p0.y - line.p1.y); + x = line.p0.x + (line.p1.x - line.p0.x)*t; + if(x > box.min.x && x < box.max.x){ + z = line.p0.z + (line.p1.z - line.p0.z)*t; + if(z > box.min.z && z < box.max.z) + if(t < mint){ + mint = t; + p = CVector(x, box.max.y, z); + normal = CVector(0.0f, 1.0f, 0.0f); + } + } + } + + // min z plne + if((box.min.z - line.p0.z) * (box.min.z - line.p1.z) < 0.0f){ + t = (box.min.z - line.p0.z) / (line.p1.z - line.p0.z); + x = line.p0.x + (line.p1.x - line.p0.x)*t; + if(x > box.min.x && x < box.max.x){ + y = line.p0.y + (line.p1.y - line.p0.y)*t; + if(y > box.min.y && y < box.max.y) + if(t < mint){ + mint = t; + p = CVector(x, y, box.min.z); + normal = CVector(0.0f, 0.0f, -1.0f); + } + } + } + + // max z plane + if((line.p0.z - box.max.z) * (line.p1.z - box.max.z) < 0.0f){ + t = (line.p0.z - box.max.z) / (line.p0.z - line.p1.z); + x = line.p0.x + (line.p1.x - line.p0.x)*t; + if(x > box.min.x && x < box.max.x){ + y = line.p0.y + (line.p1.y - line.p0.y)*t; + if(y > box.min.y && y < box.max.y) + if(t < mint){ + mint = t; + p = CVector(x, y, box.max.z); + normal = CVector(0.0f, 0.0f, 1.0f); + } + } + } + + if(mint >= mindist) + return false; + + point.point = p; + point.normal = normal; +#ifndef VU_COLLISION + point.surfaceA = 0; + point.pieceA = 0; + point.surfaceB = box.surface; + point.pieceB = box.piece; +#endif + mindist = mint; + + return true; +} + +// If line.p0 lies inside sphere, no collision is registered. +bool +CCollision::ProcessLineSphere(const CColLine &line, const CColSphere &sphere, CColPoint &point, float &mindist) +{ + CVector v01 = line.p1 - line.p0; + CVector v0c = sphere.center - line.p0; + float linesq = v01.MagnitudeSqr(); + // project v0c onto v01, scaled by |v01| this is the midpoint of the two intersections + float projline = DotProduct(v01, v0c); + // tangent of p0 to sphere, scaled by linesq just like projline^2 + float tansq = (v0c.MagnitudeSqr() - sphere.radius*sphere.radius) * linesq; + // this works out to be the square of the distance between the midpoint and the intersections + float diffsq = projline*projline - tansq; + // no intersection + if(diffsq < 0.0f) + return false; + // point of first intersection, in range [0,1] between p0 and p1 + float t = (projline - Sqrt(diffsq)) / linesq; + // if not on line or beyond mindist, no intersection + if(t < 0.0f || t > 1.0f || t >= mindist) + return false; + point.point = line.p0 + v01*t; + point.normal = point.point - sphere.center; + point.normal.Normalise(); +#ifndef VU_COLLISION + point.surfaceA = 0; + point.pieceA = 0; + point.surfaceB = sphere.surface; + point.pieceB = sphere.piece; +#endif + mindist = t; + return true; +} + +// unused +bool +CCollision::ProcessVerticalLineTriangle(const CColLine &line, + const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane, + CColPoint &point, float &mindist, CStoredCollPoly *poly) +{ +#ifdef VU_COLLISION + // not used in favour of optimized loops + bool res = ProcessLineTriangle(line, verts, tri, plane, point, mindist); + if(res && poly){ + poly->verts[0] = verts[tri.a].Get(); + poly->verts[1] = verts[tri.b].Get(); + poly->verts[2] = verts[tri.c].Get(); + poly->valid = true; + } + return res; +#else + float t; + CVector normal; + + const CVector &p0 = line.p0; + const CVector &va = verts[tri.a].Get(); + const CVector &vb = verts[tri.b].Get(); + const CVector &vc = verts[tri.c].Get(); + + // early out bound rect test + if(p0.x < va.x && p0.x < vb.x && p0.x < vc.x) return false; + if(p0.x > va.x && p0.x > vb.x && p0.x > vc.x) return false; + if(p0.y < va.y && p0.y < vb.y && p0.y < vc.y) return false; + if(p0.y > va.y && p0.y > vb.y && p0.y > vc.y) return false; + + plane.GetNormal(normal); + // if points are on the same side, no collision + if(plane.CalcPoint(p0) * plane.CalcPoint(line.p1) > 0.0f) + return false; + + // intersection parameter on line + float h = (line.p1 - p0).z; + t = -plane.CalcPoint(p0) / (h * normal.z); + // early out if we're beyond the mindist + if(t >= mindist) + return false; + CVector p(p0.x, p0.y, p0.z + h*t); + + CVector2D vec1, vec2, vec3, vect; + switch(plane.dir){ + case DIR_X_POS: + vec1.x = va.y; vec1.y = va.z; + vec2.x = vc.y; vec2.y = vc.z; + vec3.x = vb.y; vec3.y = vb.z; + vect.x = p.y; vect.y = p.z; + break; + case DIR_X_NEG: + vec1.x = va.y; vec1.y = va.z; + vec2.x = vb.y; vec2.y = vb.z; + vec3.x = vc.y; vec3.y = vc.z; + vect.x = p.y; vect.y = p.z; + break; + case DIR_Y_POS: + vec1.x = va.z; vec1.y = va.x; + vec2.x = vc.z; vec2.y = vc.x; + vec3.x = vb.z; vec3.y = vb.x; + vect.x = p.z; vect.y = p.x; + break; + case DIR_Y_NEG: + vec1.x = va.z; vec1.y = va.x; + vec2.x = vb.z; vec2.y = vb.x; + vec3.x = vc.z; vec3.y = vc.x; + vect.x = p.z; vect.y = p.x; + break; + case DIR_Z_POS: + vec1.x = va.x; vec1.y = va.y; + vec2.x = vc.x; vec2.y = vc.y; + vec3.x = vb.x; vec3.y = vb.y; + vect.x = p.x; vect.y = p.y; + break; + case DIR_Z_NEG: + vec1.x = va.x; vec1.y = va.y; + vec2.x = vb.x; vec2.y = vb.y; + vec3.x = vc.x; vec3.y = vc.y; + vect.x = p.x; vect.y = p.y; + break; + default: + assert(0); + } + if(CrossProduct2D(vec2-vec1, vect-vec1) < 0.0f) return false; + if(CrossProduct2D(vec3-vec1, vect-vec1) > 0.0f) return false; + if(CrossProduct2D(vec3-vec2, vect-vec2) < 0.0f) return false; + if(t >= mindist) return false; + point.point = p; + point.normal = normal; + point.surfaceA = 0; + point.pieceA = 0; + point.surfaceB = tri.surface; + point.pieceB = 0; + if(poly){ + poly->verts[0] = va; + poly->verts[1] = vb; + poly->verts[2] = vc; + poly->valid = true; + } + mindist = t; + return true; +#endif +} + +bool +CCollision::IsStoredPolyStillValidVerticalLine(const CVector &pos, float z, CColPoint &point, CStoredCollPoly *poly) +{ +#ifdef VU_COLLISION + if(!poly->valid) + return false; + + CVuVector p0 = pos; + CVuVector p1 = pos; + p1.z = z; + + CVector v01 = poly->verts[1] - poly->verts[0]; + CVector v02 = poly->verts[2] - poly->verts[0]; + CVuVector plane = CrossProduct(v02, v01); + plane.Normalise(); + plane.w = DotProduct(plane, poly->verts[0]); + + LineToTriangleCollision(p0, p1, poly->verts[0], poly->verts[1], poly->verts[2], plane); + + CVuVector pnt; + float dist; + if(!GetVUresult(pnt, plane, dist)) +#ifdef FIX_BUGS + // perhaps not needed but be safe + return poly->valid = false; +#else + return false; +#endif + point.point = pnt; + return true; +#else + float t; + + if(!poly->valid) + return false; + + // maybe inlined? + CColTrianglePlane plane; + plane.Set(poly->verts[0], poly->verts[1], poly->verts[2]); + + const CVector &va = poly->verts[0]; + const CVector &vb = poly->verts[1]; + const CVector &vc = poly->verts[2]; + CVector p0 = pos; + CVector p1(pos.x, pos.y, z); + + // The rest is pretty much CCollision::ProcessLineTriangle + + // if points are on the same side, no collision + if(plane.CalcPoint(p0) * plane.CalcPoint(p1) > 0.0f) + return poly->valid = false; + + // intersection parameter on line + CVector normal; + plane.GetNormal(normal); + t = -plane.CalcPoint(p0) / DotProduct(p1 - p0, normal); + // find point of intersection + CVector p = p0 + (p1-p0)*t; + + CVector2D vec1, vec2, vec3, vect; + switch(plane.dir){ + case DIR_X_POS: + vec1.x = va.y; vec1.y = va.z; + vec2.x = vc.y; vec2.y = vc.z; + vec3.x = vb.y; vec3.y = vb.z; + vect.x = p.y; vect.y = p.z; + break; + case DIR_X_NEG: + vec1.x = va.y; vec1.y = va.z; + vec2.x = vb.y; vec2.y = vb.z; + vec3.x = vc.y; vec3.y = vc.z; + vect.x = p.y; vect.y = p.z; + break; + case DIR_Y_POS: + vec1.x = va.z; vec1.y = va.x; + vec2.x = vc.z; vec2.y = vc.x; + vec3.x = vb.z; vec3.y = vb.x; + vect.x = p.z; vect.y = p.x; + break; + case DIR_Y_NEG: + vec1.x = va.z; vec1.y = va.x; + vec2.x = vb.z; vec2.y = vb.x; + vec3.x = vc.z; vec3.y = vc.x; + vect.x = p.z; vect.y = p.x; + break; + case DIR_Z_POS: + vec1.x = va.x; vec1.y = va.y; + vec2.x = vc.x; vec2.y = vc.y; + vec3.x = vb.x; vec3.y = vb.y; + vect.x = p.x; vect.y = p.y; + break; + case DIR_Z_NEG: + vec1.x = va.x; vec1.y = va.y; + vec2.x = vb.x; vec2.y = vb.y; + vec3.x = vc.x; vec3.y = vc.y; + vect.x = p.x; vect.y = p.y; + break; + default: + assert(0); + } + if(CrossProduct2D(vec2-vec1, vect-vec1) < 0.0f) return poly->valid = false; + if(CrossProduct2D(vec3-vec1, vect-vec1) > 0.0f) return poly->valid = false; + if(CrossProduct2D(vec3-vec2, vect-vec2) < 0.0f) return poly->valid = false; + point.point = p; + return poly->valid = true; +#endif +} + +bool +CCollision::ProcessLineTriangle(const CColLine &line, + const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane, + CColPoint &point, float &mindist, CStoredCollPoly *poly) +{ +#ifdef VU_COLLISION + // not used in favour of optimized loops + VuTriangle vutri; + verts[tri.a].Unpack(vutri.v0); + verts[tri.b].Unpack(vutri.v1); + verts[tri.c].Unpack(vutri.v2); + plane.Unpack(vutri.plane); + + LineToTriangleCollisionCompressed(*(CVuVector*)&line.p0, *(CVuVector*)&line.p1, vutri); + + CVuVector pnt, normal; + float dist; + if(GetVUresult(pnt, normal, dist)){ + if(dist < mindist){ + point.point = pnt; + point.normal = normal; + mindist = dist; + return true; + } + } + return false; +#else + float t; + CVector normal; + plane.GetNormal(normal); + + // if points are on the same side, no collision + if(plane.CalcPoint(line.p0) * plane.CalcPoint(line.p1) > 0.0f) + return false; + + float p0dist = DotProduct(line.p1 - line.p0, normal); + +#ifdef FIX_BUGS + // line lines in the plane, assume no collision + if (p0dist == 0.0f) + return false; +#endif + + // intersection parameter on line + t = -plane.CalcPoint(line.p0) / p0dist; + + // early out if we're beyond the mindist + if(t >= mindist) + return false; + // find point of intersection + CVector p = line.p0 + (line.p1-line.p0)*t; + + const CVector &va = verts[tri.a].Get(); + const CVector &vb = verts[tri.b].Get(); + const CVector &vc = verts[tri.c].Get(); + CVector2D vec1, vec2, vec3, vect; + + switch(plane.dir){ + case DIR_X_POS: + vec1.x = va.y; vec1.y = va.z; + vec2.x = vc.y; vec2.y = vc.z; + vec3.x = vb.y; vec3.y = vb.z; + vect.x = p.y; vect.y = p.z; + break; + case DIR_X_NEG: + vec1.x = va.y; vec1.y = va.z; + vec2.x = vb.y; vec2.y = vb.z; + vec3.x = vc.y; vec3.y = vc.z; + vect.x = p.y; vect.y = p.z; + break; + case DIR_Y_POS: + vec1.x = va.z; vec1.y = va.x; + vec2.x = vc.z; vec2.y = vc.x; + vec3.x = vb.z; vec3.y = vb.x; + vect.x = p.z; vect.y = p.x; + break; + case DIR_Y_NEG: + vec1.x = va.z; vec1.y = va.x; + vec2.x = vb.z; vec2.y = vb.x; + vec3.x = vc.z; vec3.y = vc.x; + vect.x = p.z; vect.y = p.x; + break; + case DIR_Z_POS: + vec1.x = va.x; vec1.y = va.y; + vec2.x = vc.x; vec2.y = vc.y; + vec3.x = vb.x; vec3.y = vb.y; + vect.x = p.x; vect.y = p.y; + break; + case DIR_Z_NEG: + vec1.x = va.x; vec1.y = va.y; + vec2.x = vb.x; vec2.y = vb.y; + vec3.x = vc.x; vec3.y = vc.y; + vect.x = p.x; vect.y = p.y; + break; + default: + assert(0); + } + if(CrossProduct2D(vec2-vec1, vect-vec1) < 0.0f) return false; + if(CrossProduct2D(vec3-vec1, vect-vec1) > 0.0f) return false; + if(CrossProduct2D(vec3-vec2, vect-vec2) < 0.0f) return false; + if(t >= mindist) return false; + point.point = p; + point.normal = normal; + point.surfaceA = 0; + point.pieceA = 0; + point.surfaceB = tri.surface; + point.pieceB = 0; + if(poly){ + poly->verts[0] = va; + poly->verts[1] = vb; + poly->verts[2] = vc; + poly->valid = true; + } + mindist = t; + return true; +#endif +} + +bool +CCollision::ProcessSphereTriangle(const CColSphere &sphere, + const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane, + CColPoint &point, float &mindistsq) +{ +#ifdef VU_COLLISION + // not used in favour of optimized loops + VuTriangle vutri; + verts[tri.a].Unpack(vutri.v0); + verts[tri.b].Unpack(vutri.v1); + verts[tri.c].Unpack(vutri.v2); + plane.Unpack(vutri.plane); + + SphereToTriangleCollisionCompressed(*(CVuVector*)&sphere, vutri); + + CVuVector pnt, normal; + float dist; + if(GetVUresult(pnt, normal, dist) && dist*dist < mindistsq){ + float depth = sphere.radius - dist; + if(depth > point.depth){ + point.point = pnt; + point.normal = normal; + point.depth = depth; + mindistsq = dist*dist; + return true; + } + } + return false; +#else + // If sphere and plane don't intersect, no collision + float planedist = plane.CalcPoint(sphere.center); + float distsq = planedist*planedist; + if(Abs(planedist) > sphere.radius || distsq > mindistsq) + return false; + + const CVector &va = verts[tri.a].Get(); + const CVector &vb = verts[tri.b].Get(); + const CVector &vc = verts[tri.c].Get(); + + // calculate two orthogonal basis vectors for the triangle + CVector normal; + plane.GetNormal(normal); + CVector vec2 = vb - va; + float len = vec2.Magnitude(); + vec2 = vec2 * (1.0f/len); + CVector vec1 = CrossProduct(vec2, normal); + + // We know A has local coordinate [0,0] and B has [0,len]. + // Now calculate coordinates on triangle for these two vectors: + CVector vac = vc - va; + CVector vas = sphere.center - va; + CVector2D b(0.0f, len); + CVector2D c(DotProduct(vec1, vac), DotProduct(vec2, vac)); + CVector2D s(DotProduct(vec1, vas), DotProduct(vec2, vas)); + + // The three triangle lines partition the space into 6 sectors, + // find out in which the center lies. + int insideAB = CrossProduct2D(s, b) >= 0.0f; + int insideAC = CrossProduct2D(c, s) >= 0.0f; + int insideBC = CrossProduct2D(s-b, c-b) >= 0.0f; + + int testcase = insideAB + insideAC + insideBC; + float dist = 0.0f; + CVector p; + switch(testcase){ + case 0: + return false; // shouldn't happen + case 1: + // closest to a vertex + if(insideAB) p = vc; + else if(insideAC) p = vb; + else if(insideBC) p = va; + else assert(0); + dist = (sphere.center - p).Magnitude(); + break; + case 2: + // closest to an edge + // looks like original game as DistToLine manually inlined + if(!insideAB) dist = DistToLine(&va, &vb, &sphere.center, p); + else if(!insideAC) dist = DistToLine(&va, &vc, &sphere.center, p); + else if(!insideBC) dist = DistToLine(&vb, &vc, &sphere.center, p); + else assert(0); + break; + case 3: + // center is in triangle + dist = Abs(planedist); + p = sphere.center - normal*planedist; + break; + default: + assert(0); + } + + if(dist >= sphere.radius || dist*dist >= mindistsq) + return false; + + point.point = p; + point.normal = sphere.center - p; + point.normal.Normalise(); +#ifndef VU_COLLISION + point.surfaceA = sphere.surface; + point.pieceA = sphere.piece; + point.surfaceB = tri.surface; + point.pieceB = 0; +#endif + point.depth = sphere.radius - dist; + mindistsq = dist*dist; + return true; +#endif +} + +bool +CCollision::ProcessLineOfSight(const CColLine &line, + const CMatrix &matrix, CColModel &model, + CColPoint &point, float &mindist, bool ignoreSeeThrough, bool ignoreShootThrough) +{ +#ifdef VU_COLLISION + CMatrix matTransform; + int i; + + // transform line to model space + Invert(matrix, matTransform); + CVuVector newline[2]; + TransformPoints(newline, 2, matTransform, &line.p0, sizeof(CColLine)/2); + + if(mindist < 1.0f) + newline[1] = newline[0] + (newline[1] - newline[0])*mindist; + + // If we don't intersect with the bounding box, no chance on the rest + if(!TestLineBox(*(CColLine*)newline, model.boundingBox)) + return false; + + float coldist = 1.0f; + for(i = 0; i < model.numSpheres; i++){ + if(ignoreSeeThrough && IsSeeThrough(model.spheres[i].surface)) continue; + if(ignoreShootThrough && IsShootThrough(model.spheres[i].surface)) continue; + if(ProcessLineSphere(*(CColLine*)newline, model.spheres[i], point, coldist)) + point.Set(0, 0, model.spheres[i].surface, model.spheres[i].piece); + } + + for(i = 0; i < model.numBoxes; i++){ + if(ignoreSeeThrough && IsSeeThrough(model.boxes[i].surface)) continue; + if(ProcessLineBox(*(CColLine*)newline, model.boxes[i], point, coldist)) + point.Set(0, 0, model.boxes[i].surface, model.boxes[i].piece); + } + + CalculateTrianglePlanes(&model); + VuTriangle vutri; + CColTriangle *lasttri = nil; + for(i = 0; i < model.numTriangles; i++){ + if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue; + if(ignoreShootThrough && IsShootThrough(model.triangles[i].surface)) continue; + + CColTriangle *tri = &model.triangles[i]; + model.vertices[tri->a].Unpack(vutri.v0); + model.vertices[tri->b].Unpack(vutri.v1); + model.vertices[tri->c].Unpack(vutri.v2); + model.trianglePlanes[i].Unpack(vutri.plane); + + LineToTriangleCollisionCompressed(newline[0], newline[1], vutri); + lasttri = tri; + break; + } +#ifdef FIX_BUGS + // no need to check first again + i++; +#endif + CVuVector pnt, normal; + float dist; + for(; i < model.numTriangles; i++){ + if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue; + if(ignoreShootThrough && IsShootThrough(model.triangles[i].surface)) continue; + + CColTriangle *tri = &model.triangles[i]; + model.vertices[tri->a].Unpack(vutri.v0); + model.vertices[tri->b].Unpack(vutri.v1); + model.vertices[tri->c].Unpack(vutri.v2); + model.trianglePlanes[i].Unpack(vutri.plane); + + if(GetVUresult(pnt, normal, dist)) + if(dist < coldist){ + point.point = pnt; + point.normal = normal; + point.Set(0, 0, lasttri->surface, 0); + coldist = dist; + } + + LineToTriangleCollisionCompressed(newline[0], newline[1], vutri); + lasttri = tri; + } + if(lasttri && GetVUresult(pnt, normal, dist)) + if(dist < coldist){ + point.point = pnt; + point.normal = normal; + point.Set(0, 0, lasttri->surface, 0); + coldist = dist; + } + + + if(coldist < 1.0f){ + point.point = matrix * point.point; + point.normal = Multiply3x3(matrix, point.normal); + mindist *= coldist; + return true; + } + return false; +#else + static CMatrix matTransform; + int i; + + // transform line to model space + Invert(matrix, matTransform); + CColLine newline(matTransform * line.p0, matTransform * line.p1); + + // If we don't intersect with the bounding box, no chance on the rest + if(!TestLineBox(newline, model.boundingBox)) + return false; + + float coldist = mindist; + for(i = 0; i < model.numSpheres; i++){ + if(ignoreSeeThrough && IsSeeThrough(model.spheres[i].surface)) continue; + if(ignoreShootThrough && IsShootThrough(model.spheres[i].surface)) continue; + ProcessLineSphere(newline, model.spheres[i], point, coldist); + } + + for(i = 0; i < model.numBoxes; i++){ + if(ignoreSeeThrough && IsSeeThrough(model.boxes[i].surface)) continue; + if(ignoreShootThrough && IsShootThrough(model.boxes[i].surface)) continue; + ProcessLineBox(newline, model.boxes[i], point, coldist); + } + + CalculateTrianglePlanes(&model); + for(i = 0; i < model.numTriangles; i++){ + if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue; + if(ignoreShootThrough && IsShootThrough(model.triangles[i].surface)) continue; + ProcessLineTriangle(newline, model.vertices, model.triangles[i], model.trianglePlanes[i], point, coldist); + } + + if(coldist < mindist){ + point.point = matrix * point.point; + point.normal = Multiply3x3(matrix, point.normal); + mindist = coldist; + return true; + } + return false; +#endif +} + +bool +CCollision::ProcessVerticalLine(const CColLine &line, + const CMatrix &matrix, CColModel &model, + CColPoint &point, float &mindist, bool ignoreSeeThrough, bool ignoreShootThrough, CStoredCollPoly *poly) +{ +#ifdef VU_COLLISION + static CStoredCollPoly TempStoredPoly; + CMatrix matTransform; + int i; + + // transform line to model space + Invert(matrix, matTransform); + CVuVector newline[2]; + TransformPoints(newline, 2, matTransform, &line.p0, sizeof(CColLine)/2); + + if(mindist < 1.0f) + newline[1] = newline[0] + (newline[1] - newline[0])*mindist; + + if(!TestLineBox(*(CColLine*)newline, model.boundingBox)) + return false; + + float coldist = 1.0f; + for(i = 0; i < model.numSpheres; i++){ + if(ignoreSeeThrough && IsSeeThroughVertical(model.spheres[i].surface)) continue; + if(ProcessLineSphere(*(CColLine*)newline, model.spheres[i], point, coldist)) + point.Set(0, 0, model.spheres[i].surface, model.spheres[i].piece); + } + + for(i = 0; i < model.numBoxes; i++){ + if(ignoreSeeThrough && IsSeeThroughVertical(model.boxes[i].surface)) continue; + if(ProcessLineBox(*(CColLine*)newline, model.boxes[i], point, coldist)) + point.Set(0, 0, model.boxes[i].surface, model.boxes[i].piece); + } + + CalculateTrianglePlanes(&model); + TempStoredPoly.valid = false; + if(model.numTriangles){ + bool registeredCol; + CColTriangle *lasttri = nil; + VuTriangle vutri; + for(i = 0; i < model.numTriangles; i++){ + if(ignoreSeeThrough && IsSeeThroughVertical(model.triangles[i].surface)) continue; + + CColTriangle *tri = &model.triangles[i]; + model.vertices[tri->a].Unpack(vutri.v0); + model.vertices[tri->b].Unpack(vutri.v1); + model.vertices[tri->c].Unpack(vutri.v2); + model.trianglePlanes[i].Unpack(vutri.plane); + + LineToTriangleCollisionCompressed(newline[0], newline[1], vutri); + lasttri = tri; + break; + } +#ifdef FIX_BUGS + // no need to check first again + i++; +#endif + CVuVector pnt, normal; + float dist; + for(; i < model.numTriangles; i++){ + if(ignoreSeeThrough && IsSeeThroughVertical(model.triangles[i].surface)) continue; + + CColTriangle *tri = &model.triangles[i]; + model.vertices[tri->a].Unpack(vutri.v0); + model.vertices[tri->b].Unpack(vutri.v1); + model.vertices[tri->c].Unpack(vutri.v2); + model.trianglePlanes[i].Unpack(vutri.plane); + + if(GetVUresult(pnt, normal, dist)){ + if(dist < coldist){ + point.point = pnt; + point.normal = normal; + point.Set(0, 0, lasttri->surface, 0); + coldist = dist; + registeredCol = true; + }else + registeredCol = false; + }else + registeredCol = false; + + if(registeredCol){ + TempStoredPoly.verts[0] = model.vertices[lasttri->a].Get(); + TempStoredPoly.verts[1] = model.vertices[lasttri->b].Get(); + TempStoredPoly.verts[2] = model.vertices[lasttri->c].Get(); + TempStoredPoly.valid = true; + } + + LineToTriangleCollisionCompressed(newline[0], newline[1], vutri); + lasttri = tri; + } + if(lasttri && GetVUresult(pnt, normal, dist)){ + if(dist < coldist){ + point.point = pnt; + point.normal = normal; + point.Set(0, 0, lasttri->surface, 0); + coldist = dist; + registeredCol = true; + }else + registeredCol = false; + }else + registeredCol = false; + + if(registeredCol){ + TempStoredPoly.verts[0] = model.vertices[lasttri->a].Get(); + TempStoredPoly.verts[1] = model.vertices[lasttri->b].Get(); + TempStoredPoly.verts[2] = model.vertices[lasttri->c].Get(); + TempStoredPoly.valid = true; + } + } + + if(coldist < 1.0f){ + point.point = matrix * point.point; + point.normal = Multiply3x3(matrix, point.normal); + if(TempStoredPoly.valid && poly){ + *poly = TempStoredPoly; + poly->verts[0] = matrix * CVector(poly->verts[0]); + poly->verts[1] = matrix * CVector(poly->verts[1]); + poly->verts[2] = matrix * CVector(poly->verts[2]); + } + mindist *= coldist; + return true; + } + return false; +#else + static CStoredCollPoly TempStoredPoly; + int i; + + // transform line to model space + // Why does the game seem to do this differently than above? + CColLine newline(MultiplyInverse(matrix, line.p0), MultiplyInverse(matrix, line.p1)); + + if(!TestLineBox(newline, model.boundingBox)) + return false; + + // BUG? is IsSeeThroughVertical really the right thing? also not checking shoot through + float coldist = mindist; + for(i = 0; i < model.numSpheres; i++){ + if(ignoreSeeThrough && IsSeeThroughVertical(model.spheres[i].surface)) continue; + ProcessLineSphere(newline, model.spheres[i], point, coldist); + } + + for(i = 0; i < model.numBoxes; i++){ + if(ignoreSeeThrough && IsSeeThroughVertical(model.boxes[i].surface)) continue; + ProcessLineBox(newline, model.boxes[i], point, coldist); + } + + CalculateTrianglePlanes(&model); + TempStoredPoly.valid = false; + for(i = 0; i < model.numTriangles; i++){ + if(ignoreSeeThrough && IsSeeThroughVertical(model.triangles[i].surface)) continue; + ProcessLineTriangle(newline, model.vertices, model.triangles[i], model.trianglePlanes[i], point, coldist, &TempStoredPoly); + } + + if(coldist < mindist){ + point.point = matrix * point.point; + point.normal = Multiply3x3(matrix, point.normal); + if(TempStoredPoly.valid && poly){ + *poly = TempStoredPoly; + poly->verts[0] = matrix * poly->verts[0]; + poly->verts[1] = matrix * poly->verts[1]; + poly->verts[2] = matrix * poly->verts[2]; + } + mindist = coldist; + return true; + } + return false; +#endif +} + +enum { + MAXNUMSPHERES = 128, + MAXNUMBOXES = 32, + MAXNUMLINES = 16, + MAXNUMTRIS = 600 +}; + +#ifdef VU_COLLISION +#ifdef GTA_PS2 +#define SPR(off) ((uint8*)(0x70000000 + (off))) +#else +static uint8 fakeSPR[16*1024]; +#define SPR(off) ((uint8*)(fakeSPR + (off))) +#endif +#endif + +// This checks model A's spheres and lines against model B's spheres, boxes and triangles. +// Returns the number of A's spheres that collide. +// Returned ColPoints are in world space. +// NB: only vehicles can have col models with lines, exactly 4, one for each wheel +int32 +CCollision::ProcessColModels(const CMatrix &matrixA, CColModel &modelA, + const CMatrix &matrixB, CColModel &modelB, + CColPoint *spherepoints, CColPoint *linepoints, float *linedists) +{ +#ifdef VU_COLLISION + CVuVector *aSpheresA = (CVuVector*)SPR(0x0000); + CVuVector *aSpheresB = (CVuVector*)SPR(0x0800); + CVuVector *aLinesA = (CVuVector*)SPR(0x1000); + int32 *aSphereIndicesA = (int32*)SPR(0x1200); + int32 *aSphereIndicesB = (int32*)SPR(0x1400); + int32 *aBoxIndicesB = (int32*)SPR(0x1600); + int32 *aTriangleIndicesB = (int32*)SPR(0x1680); + bool *aCollided = (bool*)SPR(0x1FE0); + CMatrix &matAB = *(CMatrix*)SPR(0x1FF0); + CMatrix &matBA = *(CMatrix*)SPR(0x2040); + int i, j, k; + + // From model A space to model B space + Invert(matrixB, matAB); + matAB *= matrixA; + + CVuVector bsphereAB; // bounding sphere of A in B space + TransformPoint(bsphereAB, matAB, modelA.boundingSphere.center); // inlined + bsphereAB.w = modelA.boundingSphere.radius; + if(!TestSphereBox(*(CColSphere*)&bsphereAB, modelB.boundingBox)) + return 0; + + // transform modelA's spheres and lines to B space + TransformPoints(aSpheresA, modelA.numSpheres, matAB, &modelA.spheres->center, sizeof(CColSphere)); + for(i = 0; i < modelA.numSpheres; i++) + aSpheresA[i].w = modelA.spheres[i].radius; + TransformPoints(aLinesA, modelA.numLines*2, matAB, &modelA.lines->p0, sizeof(CColLine)/2); + + // Test them against model B's bounding volumes + int numSpheresA = 0; + for(i = 0; i < modelA.numSpheres; i++) + if(TestSphereBox(*(CColSphere*)&aSpheresA[i], modelB.boundingBox)) + aSphereIndicesA[numSpheresA++] = i; + // No collision + if(numSpheresA == 0 && modelA.numLines == 0) + return 0; + + + // B to A space + Invert(matrixA, matBA); + matBA *= matrixB; + + // transform modelB's spheres to A space + TransformPoints(aSpheresB, modelB.numSpheres, matBA, &modelB.spheres->center, sizeof(CColSphere)); + for(i = 0; i < modelB.numSpheres; i++) + aSpheresB[i].w = modelB.spheres[i].radius; + + // Check model B against A's bounding volumes + int numSpheresB = 0; + int numBoxesB = 0; + int numTrianglesB = 0; + for(i = 0; i < modelB.numSpheres; i++) + if(TestSphereBox(*(CColSphere*)&aSpheresB[i], modelA.boundingBox)) + aSphereIndicesB[numSpheresB++] = i; + for(i = 0; i < modelB.numBoxes; i++) + if(TestSphereBox(*(CColSphere*)&bsphereAB, modelB.boxes[i])) + aBoxIndicesB[numBoxesB++] = i; + CalculateTrianglePlanes(&modelB); + if(modelB.numTriangles){ + VuTriangle vutri; + // process the first triangle + CColTriangle *tri = &modelB.triangles[0]; + modelB.vertices[tri->a].Unpack(vutri.v0); + modelB.vertices[tri->b].Unpack(vutri.v1); + modelB.vertices[tri->c].Unpack(vutri.v2); + modelB.trianglePlanes[0].Unpack(vutri.plane); + + SphereToTriangleCollisionCompressed(bsphereAB, vutri); + + for(i = 1; i < modelB.numTriangles; i++){ + // set up the next triangle while VU0 is running + tri = &modelB.triangles[i]; + modelB.vertices[tri->a].Unpack(vutri.v0); + modelB.vertices[tri->b].Unpack(vutri.v1); + modelB.vertices[tri->c].Unpack(vutri.v2); + modelB.trianglePlanes[i].Unpack(vutri.plane); + + // check previous result + if(GetVUresult()) + aTriangleIndicesB[numTrianglesB++] = i-1; + + // kick off this one + SphereToTriangleCollisionCompressed(bsphereAB, vutri); + } + + // check last result + if(GetVUresult()) + aTriangleIndicesB[numTrianglesB++] = i-1; + } + // No collision + if(numSpheresB == 0 && numBoxesB == 0 && numTrianglesB == 0) + return 0; + + // We now have the collision volumes in A and B that are worth processing. + + // Process A's spheres against B's collision volumes + int numCollisions = 0; + spherepoints[numCollisions].depth = -1.0f; + for(i = 0; i < numSpheresA; i++){ + float coldist = 1.0e24f; + bool hasCollided = false; + CColSphere *sphA = &modelA.spheres[aSphereIndicesA[i]]; + CVuVector *vusphA = &aSpheresA[aSphereIndicesA[i]]; + + for(j = 0; j < numSpheresB; j++) + // This actually looks like something was inlined here + if(ProcessSphereSphere(*(CColSphere*)vusphA, modelB.spheres[aSphereIndicesB[j]], + spherepoints[numCollisions], coldist)){ + spherepoints[numCollisions].Set( + sphA->surface, sphA->piece, + modelB.spheres[aSphereIndicesB[j]].surface, modelB.spheres[aSphereIndicesB[j]].piece); + hasCollided = true; + } + for(j = 0; j < numBoxesB; j++) + if(ProcessSphereBox(*(CColSphere*)vusphA, modelB.boxes[aBoxIndicesB[j]], + spherepoints[numCollisions], coldist)){ + spherepoints[numCollisions].Set( + sphA->surface, sphA->piece, + modelB.boxes[aBoxIndicesB[j]].surface, modelB.boxes[aBoxIndicesB[j]].piece); + hasCollided = true; + } + if(numTrianglesB){ + CVuVector point, normal; + float depth; + bool registeredCol; + CColTriangle *lasttri; + + VuTriangle vutri; + // process the first triangle + k = aTriangleIndicesB[0]; + CColTriangle *tri = &modelB.triangles[k]; + modelB.vertices[tri->a].Unpack(vutri.v0); + modelB.vertices[tri->b].Unpack(vutri.v1); + modelB.vertices[tri->c].Unpack(vutri.v2); + modelB.trianglePlanes[k].Unpack(vutri.plane); + + SphereToTriangleCollisionCompressed(*vusphA, vutri); + lasttri = tri; + + for(j = 1; j < numTrianglesB; j++){ + k = aTriangleIndicesB[j]; + // set up the next triangle while VU0 is running + tri = &modelB.triangles[k]; + modelB.vertices[tri->a].Unpack(vutri.v0); + modelB.vertices[tri->b].Unpack(vutri.v1); + modelB.vertices[tri->c].Unpack(vutri.v2); + modelB.trianglePlanes[k].Unpack(vutri.plane); + + // check previous result + // TODO: this looks inlined but spherepoints[numCollisions] does not... + if(GetVUresult(point, normal, depth)){ + depth = sphA->radius - depth; + if(depth > spherepoints[numCollisions].depth){ + spherepoints[numCollisions].point = point; + spherepoints[numCollisions].normal = normal; + spherepoints[numCollisions].Set(depth, + sphA->surface, sphA->piece, lasttri->surface, 0); + registeredCol = true; + }else + registeredCol = false; + }else + registeredCol = false; + + if(registeredCol) + hasCollided = true; + + // kick off this one + SphereToTriangleCollisionCompressed(*vusphA, vutri); + lasttri = tri; + } + + // check last result + // TODO: this looks inlined but spherepoints[numCollisions] does not... + if(GetVUresult(point, normal, depth)){ + depth = sphA->radius - depth; + if(depth > spherepoints[numCollisions].depth){ + spherepoints[numCollisions].point = point; + spherepoints[numCollisions].normal = normal; + spherepoints[numCollisions].Set(depth, + sphA->surface, sphA->piece, lasttri->surface, 0); + registeredCol = true; + }else + registeredCol = false; + }else + registeredCol = false; + + if(registeredCol) + hasCollided = true; + } + + if(hasCollided){ + numCollisions++; + if(numCollisions == MAX_COLLISION_POINTS) + break; + spherepoints[numCollisions].depth = -1.0f; + } + } + for(i = 0; i < numCollisions; i++){ + // TODO: both VU0 macros + spherepoints[i].point = matrixB * spherepoints[i].point; + spherepoints[i].normal = Multiply3x3(matrixB, spherepoints[i].normal); + } + + // And the same thing for the lines in A + for(i = 0; i < modelA.numLines; i++){ + aCollided[i] = false; + CVuVector *lineA = &aLinesA[i*2]; + + for(j = 0; j < numSpheresB; j++) + if(ProcessLineSphere(*(CColLine*)lineA, modelB.spheres[aSphereIndicesB[j]], + linepoints[i], linedists[i])){ + linepoints[i].Set(0, 0, +#ifdef FIX_BUGS + modelB.spheres[aSphereIndicesB[j]].surface, modelB.spheres[aSphereIndicesB[j]].piece); +#else + modelB.spheres[j].surface, modelB.spheres[j].piece); +#endif + aCollided[i] = true; + } + for(j = 0; j < numBoxesB; j++) + if(ProcessLineBox(*(CColLine*)lineA, modelB.boxes[aBoxIndicesB[j]], + linepoints[i], linedists[i])){ + linepoints[i].Set(0, 0, + modelB.boxes[aBoxIndicesB[j]].surface, modelB.boxes[aBoxIndicesB[j]].piece); + aCollided[i] = true; + } + if(numTrianglesB){ + CVuVector point, normal; + float dist; + bool registeredCol; + CColTriangle *lasttri; + + VuTriangle vutri; + // process the first triangle + k = aTriangleIndicesB[0]; + CColTriangle *tri = &modelB.triangles[k]; + modelB.vertices[tri->a].Unpack(vutri.v0); + modelB.vertices[tri->b].Unpack(vutri.v1); + modelB.vertices[tri->c].Unpack(vutri.v2); + modelB.trianglePlanes[k].Unpack(vutri.plane); + + LineToTriangleCollisionCompressed(lineA[0], lineA[1], vutri); + lasttri = tri; + + for(j = 1; j < numTrianglesB; j++){ + k = aTriangleIndicesB[j]; + // set up the next triangle while VU0 is running + CColTriangle *tri = &modelB.triangles[k]; + modelB.vertices[tri->a].Unpack(vutri.v0); + modelB.vertices[tri->b].Unpack(vutri.v1); + modelB.vertices[tri->c].Unpack(vutri.v2); + modelB.trianglePlanes[k].Unpack(vutri.plane); + + // check previous result + // TODO: this again somewhat looks inlined + if(GetVUresult(point, normal, dist)){ + if(dist < linedists[i]){ + linepoints[i].point = point; + linepoints[i].normal = normal; + linedists[i] = dist; + linepoints[i].Set(0, 0, lasttri->surface, 0); + registeredCol = true; + }else + registeredCol = false; + }else + registeredCol = false; + + if(registeredCol) + aCollided[i] = true; + + // kick of this one + LineToTriangleCollisionCompressed(lineA[0], lineA[1], vutri); + lasttri = tri; + } + + // check last result + if(GetVUresult(point, normal, dist)){ + if(dist < linedists[i]){ + linepoints[i].point = point; + linepoints[i].normal = normal; + linedists[i] = dist; + linepoints[i].Set(0, 0, lasttri->surface, 0); + registeredCol = true; + }else + registeredCol = false; + }else + registeredCol = false; + + if(registeredCol) + aCollided[i] = true; + } + + if(aCollided[i]){ + // TODO: both VU0 macros + linepoints[i].point = matrixB * linepoints[i].point; + linepoints[i].normal = Multiply3x3(matrixB, linepoints[i].normal); + } + } + + return numCollisions; // sphere collisions +#else + static int aSphereIndicesA[MAXNUMSPHERES]; + static int aLineIndicesA[MAXNUMLINES]; + static int aSphereIndicesB[MAXNUMSPHERES]; + static int aBoxIndicesB[MAXNUMBOXES]; + static int aTriangleIndicesB[MAXNUMTRIS]; + static bool aCollided[MAXNUMLINES]; + static CColSphere aSpheresA[MAXNUMSPHERES]; + static CColLine aLinesA[MAXNUMLINES]; + static CMatrix matAB, matBA; + CColSphere s; + int i, j; + + assert(modelA.numSpheres <= MAXNUMSPHERES); + assert(modelA.numLines <= MAXNUMLINES); + + // From model A space to model B space + matAB = Invert(matrixB, matAB); + matAB *= matrixA; + + CColSphere bsphereAB; // bounding sphere of A in B space + bsphereAB.radius = modelA.boundingSphere.radius; + bsphereAB.center = matAB * modelA.boundingSphere.center; + if(!TestSphereBox(bsphereAB, modelB.boundingBox)) + return 0; + // B to A space + matBA = Invert(matrixA, matBA); + matBA *= matrixB; + + // transform modelA's spheres and lines to B space + for(i = 0; i < modelA.numSpheres; i++){ + CColSphere &s = modelA.spheres[i]; + aSpheresA[i].Set(s.radius, matAB * s.center, s.surface, s.piece); + } + for(i = 0; i < modelA.numLines; i++) + aLinesA[i].Set(matAB * modelA.lines[i].p0, matAB * modelA.lines[i].p1); + + // Test them against model B's bounding volumes + int numSpheresA = 0; + int numLinesA = 0; + for(i = 0; i < modelA.numSpheres; i++) + if(TestSphereBox(aSpheresA[i], modelB.boundingBox)) + aSphereIndicesA[numSpheresA++] = i; + // no actual check??? + for(i = 0; i < modelA.numLines; i++) + aLineIndicesA[numLinesA++] = i; + // No collision + if(numSpheresA == 0 && numLinesA == 0) + return 0; + + // Check model B against A's bounding volumes + int numSpheresB = 0; + int numBoxesB = 0; + int numTrianglesB = 0; + for(i = 0; i < modelB.numSpheres; i++){ + s.radius = modelB.spheres[i].radius; + s.center = matBA * modelB.spheres[i].center; + if(TestSphereBox(s, modelA.boundingBox)) + aSphereIndicesB[numSpheresB++] = i; + } + for(i = 0; i < modelB.numBoxes; i++) + if(TestSphereBox(bsphereAB, modelB.boxes[i])) + aBoxIndicesB[numBoxesB++] = i; + CalculateTrianglePlanes(&modelB); + for(i = 0; i < modelB.numTriangles; i++) + if(TestSphereTriangle(bsphereAB, modelB.vertices, modelB.triangles[i], modelB.trianglePlanes[i])) + aTriangleIndicesB[numTrianglesB++] = i; + assert(numSpheresB <= MAXNUMSPHERES); + assert(numBoxesB <= MAXNUMBOXES); + assert(numTrianglesB <= MAXNUMTRIS); + // No collision + if(numSpheresB == 0 && numBoxesB == 0 && numTrianglesB == 0) + return 0; + + // We now have the collision volumes in A and B that are worth processing. + + // Process A's spheres against B's collision volumes + int numCollisions = 0; + for(i = 0; i < numSpheresA; i++){ + float coldist = 1.0e24f; + bool hasCollided = false; + + for(j = 0; j < numSpheresB; j++) + hasCollided |= ProcessSphereSphere( + aSpheresA[aSphereIndicesA[i]], + modelB.spheres[aSphereIndicesB[j]], + spherepoints[numCollisions], coldist); + for(j = 0; j < numBoxesB; j++) + hasCollided |= ProcessSphereBox( + aSpheresA[aSphereIndicesA[i]], + modelB.boxes[aBoxIndicesB[j]], + spherepoints[numCollisions], coldist); + for(j = 0; j < numTrianglesB; j++) + hasCollided |= ProcessSphereTriangle( + aSpheresA[aSphereIndicesA[i]], + modelB.vertices, + modelB.triangles[aTriangleIndicesB[j]], + modelB.trianglePlanes[aTriangleIndicesB[j]], + spherepoints[numCollisions], coldist); + + if(hasCollided) + numCollisions++; + } + for(i = 0; i < numCollisions; i++){ + spherepoints[i].point = matrixB * spherepoints[i].point; + spherepoints[i].normal = Multiply3x3(matrixB, spherepoints[i].normal); + } + + // And the same thing for the lines in A + for(i = 0; i < numLinesA; i++){ + aCollided[i] = false; + + for(j = 0; j < numSpheresB; j++) + aCollided[i] |= ProcessLineSphere( + aLinesA[aLineIndicesA[i]], + modelB.spheres[aSphereIndicesB[j]], + linepoints[aLineIndicesA[i]], + linedists[aLineIndicesA[i]]); + for(j = 0; j < numBoxesB; j++) + aCollided[i] |= ProcessLineBox( + aLinesA[aLineIndicesA[i]], + modelB.boxes[aBoxIndicesB[j]], + linepoints[aLineIndicesA[i]], + linedists[aLineIndicesA[i]]); + for(j = 0; j < numTrianglesB; j++) + aCollided[i] |= ProcessLineTriangle( + aLinesA[aLineIndicesA[i]], + modelB.vertices, + modelB.triangles[aTriangleIndicesB[j]], + modelB.trianglePlanes[aTriangleIndicesB[j]], + linepoints[aLineIndicesA[i]], + linedists[aLineIndicesA[i]]); + } + for(i = 0; i < numLinesA; i++) + if(aCollided[i]){ + j = aLineIndicesA[i]; + linepoints[j].point = matrixB * linepoints[j].point; + linepoints[j].normal = Multiply3x3(matrixB, linepoints[j].normal); + } + + return numCollisions; // sphere collisions +#endif +} + + +// +// Misc +// + +float +CCollision::DistToLine(const CVector *l0, const CVector *l1, const CVector *point) +{ + float lensq = (*l1 - *l0).MagnitudeSqr(); + float dot = DotProduct(*point - *l0, *l1 - *l0); + // Between 0 and len we're above the line. + // if not, calculate distance to endpoint + if(dot <= 0.0f) return (*point - *l0).Magnitude(); + if(dot >= lensq) return (*point - *l1).Magnitude(); + // distance to line + float distSqr = (*point - *l0).MagnitudeSqr() - dot * dot / lensq; + if(distSqr <= 0.f) return 0.f; + return Sqrt(distSqr); +} + +// same as above but also return the point on the line +float +CCollision::DistToLine(const CVector *l0, const CVector *l1, const CVector *point, CVector &closest) +{ + float lensq = (*l1 - *l0).MagnitudeSqr(); + float dot = DotProduct(*point - *l0, *l1 - *l0); + // find out which point we're closest to + if(dot <= 0.0f) + closest = *l0; + else if(dot >= lensq) + closest = *l1; + else + closest = *l0 + (*l1 - *l0)*(dot/lensq); + // this is the distance + return (*point - closest).Magnitude(); +} + +void +CCollision::CalculateTrianglePlanes(CColModel *model) +{ + assert(model); + if(model->numTriangles == 0) + return; + + CLink *lptr; + if(model->trianglePlanes){ + // re-insert at front so it's not removed again soon + lptr = model->GetLinkPtr(); + lptr->Remove(); + ms_colModelCache.head.Insert(lptr); + }else{ + lptr = ms_colModelCache.Insert(model); + if(lptr == nil){ + // make room if we have to, remove last in list + lptr = ms_colModelCache.tail.prev; + assert(lptr); + assert(lptr->item); + lptr->item->RemoveTrianglePlanes(); + ms_colModelCache.Remove(lptr); + // now this cannot fail + lptr = ms_colModelCache.Insert(model); + assert(lptr); + } + model->CalculateTrianglePlanes(); + model->SetLinkPtr(lptr); + } +} + +void +CCollision::RemoveTrianglePlanes(CColModel *model) +{ + if(model->trianglePlanes){ + ms_colModelCache.Remove(model->GetLinkPtr()); + model->RemoveTrianglePlanes(); + } +} + +void +CCollision::DrawColModel(const CMatrix &mat, const CColModel &colModel) +{ + int i; + CVector min, max; + CVector verts[8]; + CVector c; + float r; + + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + + min = colModel.boundingBox.min; + max = colModel.boundingBox.max; + + verts[0] = mat * CVector(min.x, min.y, min.z); + verts[1] = mat * CVector(min.x, min.y, max.z); + verts[2] = mat * CVector(min.x, max.y, min.z); + verts[3] = mat * CVector(min.x, max.y, max.z); + verts[4] = mat * CVector(max.x, min.y, min.z); + verts[5] = mat * CVector(max.x, min.y, max.z); + verts[6] = mat * CVector(max.x, max.y, min.z); + verts[7] = mat * CVector(max.x, max.y, max.z); + + CLines::RenderLineWithClipping( + verts[0].x, verts[0].y, verts[0].z, + verts[1].x, verts[1].y, verts[1].z, + 0xFF0000FF, 0xFF0000FF); + CLines::RenderLineWithClipping( + verts[1].x, verts[1].y, verts[1].z, + verts[3].x, verts[3].y, verts[3].z, + 0xFF0000FF, 0xFF0000FF); + CLines::RenderLineWithClipping( + verts[3].x, verts[3].y, verts[3].z, + verts[2].x, verts[2].y, verts[2].z, + 0xFF0000FF, 0xFF0000FF); + CLines::RenderLineWithClipping( + verts[2].x, verts[2].y, verts[2].z, + verts[0].x, verts[0].y, verts[0].z, + 0xFF0000FF, 0xFF0000FF); + + CLines::RenderLineWithClipping( + verts[4].x, verts[4].y, verts[4].z, + verts[5].x, verts[5].y, verts[5].z, + 0xFF0000FF, 0xFF0000FF); + CLines::RenderLineWithClipping( + verts[5].x, verts[5].y, verts[5].z, + verts[7].x, verts[7].y, verts[7].z, + 0xFF0000FF, 0xFF0000FF); + CLines::RenderLineWithClipping( + verts[7].x, verts[7].y, verts[7].z, + verts[6].x, verts[6].y, verts[6].z, + 0xFF0000FF, 0xFF0000FF); + CLines::RenderLineWithClipping( + verts[6].x, verts[6].y, verts[6].z, + verts[4].x, verts[4].y, verts[4].z, + 0xFF0000FF, 0xFF0000FF); + + CLines::RenderLineWithClipping( + verts[0].x, verts[0].y, verts[0].z, + verts[4].x, verts[4].y, verts[4].z, + 0xFF0000FF, 0xFF0000FF); + CLines::RenderLineWithClipping( + verts[1].x, verts[1].y, verts[1].z, + verts[5].x, verts[5].y, verts[5].z, + 0xFF0000FF, 0xFF0000FF); + CLines::RenderLineWithClipping( + verts[2].x, verts[2].y, verts[2].z, + verts[6].x, verts[6].y, verts[6].z, + 0xFF0000FF, 0xFF0000FF); + CLines::RenderLineWithClipping( + verts[3].x, verts[3].y, verts[3].z, + verts[7].x, verts[7].y, verts[7].z, + 0xFF0000FF, 0xFF0000FF); + + for(i = 0; i < colModel.numSpheres; i++){ + c = mat * colModel.spheres[i].center; + r = colModel.spheres[i].radius; + + CLines::RenderLineWithClipping( + c.x, c.y, c.z-r, + c.x-r, c.y-r, c.z, + 0xFF00FFFF, 0xFF00FFFF); + CLines::RenderLineWithClipping( + c.x, c.y, c.z-r, + c.x-r, c.y+r, c.z, + 0xFF00FFFF, 0xFF00FFFF); + CLines::RenderLineWithClipping( + c.x, c.y, c.z-r, + c.x+r, c.y-r, c.z, + 0xFF00FFFF, 0xFF00FFFF); + CLines::RenderLineWithClipping( + c.x, c.y, c.z-r, + c.x+r, c.y+r, c.z, + 0xFF00FFFF, 0xFF00FFFF); + CLines::RenderLineWithClipping( + c.x-r, c.y-r, c.z, + c.x, c.y, c.z+r, + 0xFF00FFFF, 0xFF00FFFF); + CLines::RenderLineWithClipping( + c.x-r, c.y+r, c.z, + c.x, c.y, c.z+r, + 0xFF00FFFF, 0xFF00FFFF); + CLines::RenderLineWithClipping( + c.x+r, c.y-r, c.z, + c.x, c.y, c.z+r, + 0xFF00FFFF, 0xFF00FFFF); + CLines::RenderLineWithClipping( + c.x+r, c.y+r, c.z, + c.x, c.y, c.z+r, + 0xFF00FFFF, 0xFF00FFFF); + } + + for(i = 0; i < colModel.numLines; i++){ + verts[0] = colModel.lines[i].p0; + verts[1] = colModel.lines[i].p1; + + verts[0] = mat * verts[0]; + verts[1] = mat * verts[1]; + + CLines::RenderLineWithClipping( + verts[0].x, verts[0].y, verts[0].z, + verts[1].x, verts[1].y, verts[1].z, + 0x00FFFFFF, 0x00FFFFFF); + } + + for(i = 0; i < colModel.numBoxes; i++){ + min = colModel.boxes[i].min; + max = colModel.boxes[i].max; + + verts[0] = mat * CVector(min.x, min.y, min.z); + verts[1] = mat * CVector(min.x, min.y, max.z); + verts[2] = mat * CVector(min.x, max.y, min.z); + verts[3] = mat * CVector(min.x, max.y, max.z); + verts[4] = mat * CVector(max.x, min.y, min.z); + verts[5] = mat * CVector(max.x, min.y, max.z); + verts[6] = mat * CVector(max.x, max.y, min.z); + verts[7] = mat * CVector(max.x, max.y, max.z); + + CLines::RenderLineWithClipping( + verts[0].x, verts[0].y, verts[0].z, + verts[1].x, verts[1].y, verts[1].z, + 0xFFFFFFFF, 0xFFFFFFFF); + CLines::RenderLineWithClipping( + verts[1].x, verts[1].y, verts[1].z, + verts[3].x, verts[3].y, verts[3].z, + 0xFFFFFFFF, 0xFFFFFFFF); + CLines::RenderLineWithClipping( + verts[3].x, verts[3].y, verts[3].z, + verts[2].x, verts[2].y, verts[2].z, + 0xFFFFFFFF, 0xFFFFFFFF); + CLines::RenderLineWithClipping( + verts[2].x, verts[2].y, verts[2].z, + verts[0].x, verts[0].y, verts[0].z, + 0xFFFFFFFF, 0xFFFFFFFF); + + CLines::RenderLineWithClipping( + verts[4].x, verts[4].y, verts[4].z, + verts[5].x, verts[5].y, verts[5].z, + 0xFFFFFFFF, 0xFFFFFFFF); + CLines::RenderLineWithClipping( + verts[5].x, verts[5].y, verts[5].z, + verts[7].x, verts[7].y, verts[7].z, + 0xFFFFFFFF, 0xFFFFFFFF); + CLines::RenderLineWithClipping( + verts[7].x, verts[7].y, verts[7].z, + verts[6].x, verts[6].y, verts[6].z, + 0xFFFFFFFF, 0xFFFFFFFF); + CLines::RenderLineWithClipping( + verts[6].x, verts[6].y, verts[6].z, + verts[4].x, verts[4].y, verts[4].z, + 0xFFFFFFFF, 0xFFFFFFFF); + + CLines::RenderLineWithClipping( + verts[0].x, verts[0].y, verts[0].z, + verts[4].x, verts[4].y, verts[4].z, + 0xFFFFFFFF, 0xFFFFFFFF); + CLines::RenderLineWithClipping( + verts[1].x, verts[1].y, verts[1].z, + verts[5].x, verts[5].y, verts[5].z, + 0xFFFFFFFF, 0xFFFFFFFF); + CLines::RenderLineWithClipping( + verts[2].x, verts[2].y, verts[2].z, + verts[6].x, verts[6].y, verts[6].z, + 0xFFFFFFFF, 0xFFFFFFFF); + CLines::RenderLineWithClipping( + verts[3].x, verts[3].y, verts[3].z, + verts[7].x, verts[7].y, verts[7].z, + 0xFFFFFFFF, 0xFFFFFFFF); + } + + for(i = 0; i < colModel.numTriangles; i++){ + colModel.GetTrianglePoint(verts[0], colModel.triangles[i].a); + colModel.GetTrianglePoint(verts[1], colModel.triangles[i].b); + colModel.GetTrianglePoint(verts[2], colModel.triangles[i].c); + verts[0] = mat * verts[0]; + verts[1] = mat * verts[1]; + verts[2] = mat * verts[2]; + CLines::RenderLineWithClipping( + verts[0].x, verts[0].y, verts[0].z, + verts[1].x, verts[1].y, verts[1].z, + 0x00FF00FF, 0x00FF00FF); + CLines::RenderLineWithClipping( + verts[0].x, verts[0].y, verts[0].z, + verts[2].x, verts[2].y, verts[2].z, + 0x00FF00FF, 0x00FF00FF); + CLines::RenderLineWithClipping( + verts[1].x, verts[1].y, verts[1].z, + verts[2].x, verts[2].y, verts[2].z, + 0x00FF00FF, 0x00FF00FF); + } + + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); +} + +static void +GetSurfaceColor(uint8 surf, uint8 &r, uint8 &g, uint8 &b) +{ + // game doesn't do this + r = 255; + g = 128; + b = 0; + + switch(CSurfaceTable::GetAdhesionGroup(surf)){ + case ADHESIVE_RUBBER: + r = 255; + g = 0; + b = 0; + break; + case ADHESIVE_HARD: + r = 255; + g = 255; + b = 128; + break; + case ADHESIVE_ROAD: + r = 128; + g = 128; + b = 128; + break; + case ADHESIVE_LOOSE: + r = 0; + g = 255; + b = 0; + break; + case ADHESIVE_SAND: + r = 255; + g = 128; + b = 128; + break; + case ADHESIVE_WET: + r = 0; + g = 0; + b = 255; + break; + } + + if(surf == SURFACE_SAND || surf == SURFACE_SAND_BEACH){ + r = 255; + g = 255; + b = 0; + } + + float f = (surf & 0xF)/32.0f + 0.5f; + r *= f; + g *= f; + b *= f; + + if(surf == SURFACE_TRANSPARENT_CLOTH || surf == SURFACE_METAL_CHAIN_FENCE || + surf == SURFACE_TRANSPARENT_STONE || surf == SURFACE_SCAFFOLD_POLE) + if(CTimer::GetFrameCounter() & 1){ + r = 0; + g = 0; + b = 0; + } +} + +void +CCollision::DrawColModel_Coloured(const CMatrix &mat, const CColModel &colModel, int32 id) +{ + int i; + int s; + CVector verts[8]; + CVector min, max; + uint8 r, g, b; + RwImVertexIndex *iptr; + RwIm3DVertex *vptr; + + RenderBuffer::ClearRenderBuffer(); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + + for(i = 0; i < colModel.numTriangles; i++){ + colModel.GetTrianglePoint(verts[0], colModel.triangles[i].a); + colModel.GetTrianglePoint(verts[1], colModel.triangles[i].b); + colModel.GetTrianglePoint(verts[2], colModel.triangles[i].c); + verts[0] = mat * verts[0]; + verts[1] = mat * verts[1]; + verts[2] = mat * verts[2]; + + s = colModel.triangles[i].surface; + GetSurfaceColor(s, r, g, b); + + if(s > SURFACE_METAL_GATE){ + r = CGeneral::GetRandomNumber(); + g = CGeneral::GetRandomNumber(); + b = CGeneral::GetRandomNumber(); + printf("Illegal surfacetype:%d on MI:%d\n", s, id); + } + + RenderBuffer::StartStoring(6, 3, &iptr, &vptr); + RwIm3DVertexSetRGBA(&vptr[0], r, g, b, 255); + RwIm3DVertexSetRGBA(&vptr[1], r, g, b, 255); + RwIm3DVertexSetRGBA(&vptr[2], r, g, b, 255); + RwIm3DVertexSetU(&vptr[0], 0.0f); + RwIm3DVertexSetV(&vptr[0], 0.0f); + RwIm3DVertexSetU(&vptr[1], 0.0f); + RwIm3DVertexSetV(&vptr[1], 1.0f); + RwIm3DVertexSetU(&vptr[2], 1.0f); + RwIm3DVertexSetV(&vptr[2], 1.0f); + RwIm3DVertexSetPos(&vptr[0], verts[0].x, verts[0].y, verts[0].z); + RwIm3DVertexSetPos(&vptr[1], verts[1].x, verts[1].y, verts[1].z); + RwIm3DVertexSetPos(&vptr[2], verts[2].x, verts[2].y, verts[2].z); + iptr[0] = 0; iptr[1] = 1; iptr[2] = 2; + iptr[3] = 0; iptr[4] = 2; iptr[5] = 1; + RenderBuffer::StopStoring(); + } + + for(i = 0; i < colModel.numBoxes; i++){ + min = colModel.boxes[i].min; + max = colModel.boxes[i].max; + + verts[0] = mat * CVector(min.x, min.y, min.z); + verts[1] = mat * CVector(min.x, min.y, max.z); + verts[2] = mat * CVector(min.x, max.y, min.z); + verts[3] = mat * CVector(min.x, max.y, max.z); + verts[4] = mat * CVector(max.x, min.y, min.z); + verts[5] = mat * CVector(max.x, min.y, max.z); + verts[6] = mat * CVector(max.x, max.y, min.z); + verts[7] = mat * CVector(max.x, max.y, max.z); + + s = colModel.boxes[i].surface; + GetSurfaceColor(s, r, g, b); + + RenderBuffer::StartStoring(36, 8, &iptr, &vptr); + RwIm3DVertexSetRGBA(&vptr[0], r, g, b, 255); + RwIm3DVertexSetRGBA(&vptr[1], r, g, b, 255); + RwIm3DVertexSetRGBA(&vptr[2], r, g, b, 255); + RwIm3DVertexSetRGBA(&vptr[3], r, g, b, 255); + RwIm3DVertexSetRGBA(&vptr[4], r, g, b, 255); + RwIm3DVertexSetRGBA(&vptr[5], r, g, b, 255); + RwIm3DVertexSetRGBA(&vptr[6], r, g, b, 255); + RwIm3DVertexSetRGBA(&vptr[7], r, g, b, 255); + RwIm3DVertexSetU(&vptr[0], 0.0f); + RwIm3DVertexSetV(&vptr[0], 0.0f); + RwIm3DVertexSetU(&vptr[1], 0.0f); + RwIm3DVertexSetV(&vptr[1], 1.0f); + RwIm3DVertexSetU(&vptr[2], 1.0f); + RwIm3DVertexSetV(&vptr[2], 1.0f); + RwIm3DVertexSetU(&vptr[3], 0.0f); + RwIm3DVertexSetV(&vptr[3], 0.0f); + RwIm3DVertexSetU(&vptr[4], 0.0f); + RwIm3DVertexSetV(&vptr[4], 1.0f); + RwIm3DVertexSetU(&vptr[5], 1.0f); + RwIm3DVertexSetV(&vptr[5], 1.0f); + RwIm3DVertexSetU(&vptr[6], 0.0f); + RwIm3DVertexSetV(&vptr[6], 1.0f); + RwIm3DVertexSetU(&vptr[7], 1.0f); + RwIm3DVertexSetV(&vptr[7], 1.0f); + RwIm3DVertexSetPos(&vptr[0], verts[0].x, verts[0].y, verts[0].z); + RwIm3DVertexSetPos(&vptr[1], verts[1].x, verts[1].y, verts[1].z); + RwIm3DVertexSetPos(&vptr[2], verts[2].x, verts[2].y, verts[2].z); + RwIm3DVertexSetPos(&vptr[3], verts[3].x, verts[3].y, verts[3].z); + RwIm3DVertexSetPos(&vptr[4], verts[4].x, verts[4].y, verts[4].z); + RwIm3DVertexSetPos(&vptr[5], verts[5].x, verts[5].y, verts[5].z); + RwIm3DVertexSetPos(&vptr[6], verts[6].x, verts[6].y, verts[6].z); + RwIm3DVertexSetPos(&vptr[7], verts[7].x, verts[7].y, verts[7].z); + iptr[0] = 0; iptr[1] = 1; iptr[2] = 2; + iptr[3] = 1; iptr[4] = 3; iptr[5] = 2; + iptr[6] = 1; iptr[7] = 5; iptr[8] = 7; + iptr[9] = 1; iptr[10] = 7; iptr[11] = 3; + iptr[12] = 2; iptr[13] = 3; iptr[14] = 7; + iptr[15] = 2; iptr[16] = 7; iptr[17] = 6; + iptr[18] = 0; iptr[19] = 5; iptr[20] = 1; + iptr[21] = 0; iptr[22] = 4; iptr[23] = 5; + iptr[24] = 0; iptr[25] = 2; iptr[26] = 4; + iptr[27] = 2; iptr[28] = 6; iptr[29] = 4; + iptr[30] = 4; iptr[31] = 6; iptr[32] = 7; + iptr[33] = 4; iptr[34] = 7; iptr[35] = 5; + RenderBuffer::StopStoring(); + } + + RenderBuffer::RenderStuffInBuffer(); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); +} diff --git a/src/miami/collision/Collision.h b/src/miami/collision/Collision.h new file mode 100644 index 00000000..57f5f86e --- /dev/null +++ b/src/miami/collision/Collision.h @@ -0,0 +1,68 @@ +#pragma once + +#include "ColModel.h" +#include "Game.h" // for eLevelName +#ifdef VU_COLLISION +#include "VuVector.h" +#endif + +struct CStoredCollPoly +{ +#ifdef VU_COLLISION + CVuVector verts[3]; +#else + CVector verts[3]; +#endif + bool valid; +}; + +// If you spawn many tanks at once, you will see that collisions of two entity exceeds 32. +#if defined(FIX_BUGS) && !defined(SQUEEZE_PERFORMANCE) +#define MAX_COLLISION_POINTS 64 +#else +#define MAX_COLLISION_POINTS 32 +#endif + +class CCollision +{ +public: + static eLevelName ms_collisionInMemory; + static CLinkList ms_colModelCache; + + static void Init(void); + static void Shutdown(void); + static void Update(void); + static void LoadCollisionWhenINeedIt(bool changeLevel); + static void SortOutCollisionAfterLoad(void); + static void LoadCollisionScreen(eLevelName level); + static void DrawColModel(const CMatrix &mat, const CColModel &colModel); + static void DrawColModel_Coloured(const CMatrix &mat, const CColModel &colModel, int32 id); + + static void CalculateTrianglePlanes(CColModel *model); + static void RemoveTrianglePlanes(CColModel *model); + + // all these return true if there's a collision + static bool TestSphereSphere(const CSphere &s1, const CSphere &s2); + static bool TestSphereBox(const CSphere &sph, const CBox &box); + static bool TestLineBox(const CColLine &line, const CBox &box); + static bool TestVerticalLineBox(const CColLine &line, const CBox &box); + static bool TestLineTriangle(const CColLine &line, const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane); + static bool TestLineSphere(const CColLine &line, const CColSphere &sph); + static bool TestSphereTriangle(const CColSphere &sphere, const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane); + static bool TestLineOfSight(const CColLine &line, const CMatrix &matrix, CColModel &model, bool ignoreSeeThrough, bool ignoreShootThrough); + + static bool ProcessSphereSphere(const CColSphere &s1, const CColSphere &s2, CColPoint &point, float &mindistsq); + static bool ProcessSphereBox(const CColSphere &sph, const CColBox &box, CColPoint &point, float &mindistsq); + static bool ProcessLineBox(const CColLine &line, const CColBox &box, CColPoint &point, float &mindist); + static bool ProcessVerticalLineTriangle(const CColLine &line, const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane, CColPoint &point, float &mindist, CStoredCollPoly *poly); + static bool ProcessLineTriangle(const CColLine &line , const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane, CColPoint &point, float &mindist, CStoredCollPoly *poly = nil); + static bool ProcessLineSphere(const CColLine &line, const CColSphere &sphere, CColPoint &point, float &mindist); + static bool ProcessSphereTriangle(const CColSphere &sph, const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane, CColPoint &point, float &mindistsq); + static bool ProcessLineOfSight(const CColLine &line, const CMatrix &matrix, CColModel &model, CColPoint &point, float &mindist, bool ignoreSeeThrough, bool ignoreShootThrough); + static bool ProcessVerticalLine(const CColLine &line, const CMatrix &matrix, CColModel &model, CColPoint &point, float &mindist, bool ignoreSeeThrough, bool ignoreShootThrough, CStoredCollPoly *poly); + static int32 ProcessColModels(const CMatrix &matrixA, CColModel &modelA, const CMatrix &matrixB, CColModel &modelB, CColPoint *spherepoints, CColPoint *linepoints, float *linedists); + static bool IsStoredPolyStillValidVerticalLine(const CVector &pos, float z, CColPoint &point, CStoredCollPoly *poly); + + static float DistToLine(const CVector *l0, const CVector *l1, const CVector *point); + static float DistToLine(const CVector *l0, const CVector *l1, const CVector *point, CVector &closest); +}; diff --git a/src/miami/collision/CompressedVector.h b/src/miami/collision/CompressedVector.h new file mode 100644 index 00000000..0c2faf91 --- /dev/null +++ b/src/miami/collision/CompressedVector.h @@ -0,0 +1,36 @@ +#pragma once + +struct CompressedVector +{ +#ifdef COMPRESSED_COL_VECTORS + int16 x, y, z; + CVector Get(void) const { return CVector(x, y, z)/128.0f; }; + void SetFixed(int16 x, int16 y, int16 z) { this->x = x; this->y = y; this->z = z; }; +#ifdef GTA_PS2 + void Unpack(uint128 &qword) const { + __asm__ volatile ( + "lh $8, 0(%1)\n" + "lh $9, 2(%1)\n" + "lh $10, 4(%1)\n" + "pextlw $10, $8\n" + "pextlw $2, $9, $10\n" + "sq $2, %0\n" + : "=m" (qword) + : "r" (this) + : "$8", "$9", "$10", "$2" + ); + } +#else + void Unpack(int32 *qword) const { + qword[0] = x; + qword[1] = y; + qword[2] = z; + qword[3] = 0; // junk + } +#endif +#else + float x, y, z; + CVector Get(void) const { return CVector(x, y, z); }; + void Set(float x, float y, float z) { this->x = x; this->y = y; this->z = z; }; +#endif +}; \ No newline at end of file diff --git a/src/miami/collision/TempColModels.cpp b/src/miami/collision/TempColModels.cpp new file mode 100644 index 00000000..0c0d4376 --- /dev/null +++ b/src/miami/collision/TempColModels.cpp @@ -0,0 +1,306 @@ +#include "common.h" + +#include "TempColModels.h" +#include "Game.h" + +CColModel CTempColModels::ms_colModelPed1; +CColModel CTempColModels::ms_colModelPed2; +CColModel CTempColModels::ms_colModelBBox; +CColModel CTempColModels::ms_colModelBumper1; +CColModel CTempColModels::ms_colModelWheel1; +CColModel CTempColModels::ms_colModelPanel1; +CColModel CTempColModels::ms_colModelBodyPart2; +CColModel CTempColModels::ms_colModelBodyPart1; +CColModel CTempColModels::ms_colModelCutObj[5]; +CColModel CTempColModels::ms_colModelPedGroundHit; +CColModel CTempColModels::ms_colModelBoot1; +CColModel CTempColModels::ms_colModelDoor1; +CColModel CTempColModels::ms_colModelBonnet1; +CColModel CTempColModels::ms_colModelWeapon; + + +CColSphere s_aPedSpheres[3]; +CColSphere s_aPed2Spheres[3]; +CColSphere s_aPedGSpheres[4]; +#ifdef FIX_BUGS +CColSphere s_aDoorSpheres[3]; +#else +CColSphere s_aDoorSpheres[4]; +#endif +CColSphere s_aBumperSpheres[4]; +CColSphere s_aPanelSpheres[4]; +CColSphere s_aBonnetSpheres[4]; +CColSphere s_aBootSpheres[4]; +CColSphere s_aWheelSpheres[2]; +CColSphere s_aBodyPartSpheres1[2]; +CColSphere s_aBodyPartSpheres2[2]; + +void +CTempColModels::Initialise(void) +{ +#define SET_COLMODEL_SPHERES(colmodel, sphrs)\ + colmodel.numSpheres = ARRAY_SIZE(sphrs);\ + colmodel.spheres = sphrs;\ + colmodel.level = LEVEL_GENERIC;\ + colmodel.ownsCollisionVolumes = false; + + int i; + + ms_colModelBBox.boundingSphere.Set(2.0f, CVector(0.0f, 0.0f, 0.0f)); + ms_colModelBBox.boundingBox.Set(CVector(-2.0f, -2.0f, -2.0f), CVector(2.0f, 2.0f, 2.0f)); + ms_colModelBBox.level = LEVEL_GENERIC; + + for (i = 0; i < ARRAY_SIZE(ms_colModelCutObj); i++) { + ms_colModelCutObj[i].boundingSphere.Set(2.0f, CVector(0.0f, 0.0f, 0.0f)); + ms_colModelCutObj[i].boundingBox.Set(CVector(-2.0f, -2.0f, -2.0f), CVector(2.0f, 2.0f, 2.0f)); + ms_colModelCutObj[i].level = LEVEL_GENERIC; + } + + // Ped Spheres + + for (i = 0; i < ARRAY_SIZE(s_aPedSpheres); i++) + s_aPedSpheres[i].radius = 0.35f; + + s_aPedSpheres[0].center = CVector(0.0f, 0.0f, -0.25f); + s_aPedSpheres[1].center = CVector(0.0f, 0.0f, 0.15f); + s_aPedSpheres[2].center = CVector(0.0f, 0.0f, 0.55f); + +#ifdef FIX_BUGS + for (i = 0; i < ARRAY_SIZE(s_aPedSpheres); i++) { +#else + for (i = 0; i < ARRAY_SIZE(s_aPedGSpheres); i++) { +#endif + s_aPedSpheres[i].surface = SURFACE_PED; + s_aPedSpheres[i].piece = 0; + } + + ms_colModelPed1.boundingSphere.Set(1.25f, CVector(0.0f, 0.0f, 0.0f)); + ms_colModelPed1.boundingBox.Set(CVector(-0.35f, -0.35f, -1.0f), CVector(0.35f, 0.35f, 0.9f)); + SET_COLMODEL_SPHERES(ms_colModelPed1, s_aPedSpheres); + + // Ped 2 Spheres + + s_aPed2Spheres[0].radius = 0.3f; + s_aPed2Spheres[1].radius = 0.4f; + s_aPed2Spheres[2].radius = 0.3f; + + s_aPed2Spheres[0].center = CVector(0.0f, 0.35f, -0.9f); + s_aPed2Spheres[1].center = CVector(0.0f, 0.0f, -0.9f); + s_aPed2Spheres[2].center = CVector(0.0f, -0.35f, -0.9f); + + for (i = 0; i < ARRAY_SIZE(s_aPed2Spheres); i++) { + s_aPed2Spheres[i].surface = SURFACE_PED; + s_aPed2Spheres[i].piece = 0; + } + + ms_colModelPed2.boundingSphere.Set(2.0f, CVector(0.0f, 0.0f, 0.0f)); + ms_colModelPed2.boundingBox.Set(CVector(-0.7f, -0.7f, -1.2f), CVector(0.7f, 0.7f, 0.0f)); + + SET_COLMODEL_SPHERES(ms_colModelPed2, s_aPed2Spheres); + + // Ped ground collision + + s_aPedGSpheres[0].radius = 0.35f; + s_aPedGSpheres[1].radius = 0.35f; + s_aPedGSpheres[2].radius = 0.35f; + s_aPedGSpheres[3].radius = 0.3f; + + s_aPedGSpheres[0].center = CVector(0.0f, -0.4f, -0.9f); + s_aPedGSpheres[1].center = CVector(0.0f, -0.1f, -0.9f); + s_aPedGSpheres[2].center = CVector(0.0f, 0.25f, -0.9f); + s_aPedGSpheres[3].center = CVector(0.0f, 0.65f, -0.9f); + + s_aPedGSpheres[0].surface = SURFACE_PED; + s_aPedGSpheres[1].surface = SURFACE_PED; + s_aPedGSpheres[2].surface = SURFACE_PED; + s_aPedGSpheres[3].surface = SURFACE_PED; + s_aPedGSpheres[0].piece = 4; + s_aPedGSpheres[1].piece = 1; + s_aPedGSpheres[2].piece = 0; + s_aPedGSpheres[3].piece = 6; + + ms_colModelPedGroundHit.boundingSphere.Set(2.0f, CVector(0.0f, 0.0f, 0.0f)); + ms_colModelPedGroundHit.boundingBox.Set(CVector(-0.4f, -1.0f, -1.25f), CVector(0.4f, 1.2f, -0.5f)); + + SET_COLMODEL_SPHERES(ms_colModelPedGroundHit, s_aPedGSpheres); + + // Door Spheres + + s_aDoorSpheres[0].radius = 0.15f; + s_aDoorSpheres[1].radius = 0.15f; + s_aDoorSpheres[2].radius = 0.25f; + + s_aDoorSpheres[0].center = CVector(0.0f, -0.25f, -0.35f); + s_aDoorSpheres[1].center = CVector(0.0f, -0.95f, -0.35f); + s_aDoorSpheres[2].center = CVector(0.0f, -0.6f, 0.25f); + +#ifdef FIX_BUGS + for (i = 0; i < ARRAY_SIZE(s_aDoorSpheres); i++) { +#else + for (i = 0; i < ARRAY_SIZE(s_aPed2Spheres); i++) { +#endif + s_aDoorSpheres[i].surface = SURFACE_CAR_PANEL; + s_aDoorSpheres[i].piece = 0; + } + + ms_colModelDoor1.boundingSphere.Set(1.5f, CVector(0.0f, -0.6f, 0.0f)); + ms_colModelDoor1.boundingBox.Set(CVector(-0.3f, 0.0f, -0.6f), CVector(0.3f, -1.2f, 0.6f)); + + SET_COLMODEL_SPHERES(ms_colModelDoor1, s_aDoorSpheres); + + // Bumper Spheres + + for (i = 0; i < ARRAY_SIZE(s_aBumperSpheres); i++) + s_aBumperSpheres[i].radius = 0.15f; + + s_aBumperSpheres[0].center = CVector(0.85f, -0.05f, 0.0f); + s_aBumperSpheres[1].center = CVector(0.4f, 0.05f, 0.0f); + s_aBumperSpheres[2].center = CVector(-0.4f, 0.05f, 0.0f); + s_aBumperSpheres[3].center = CVector(-0.85f, -0.05f, 0.0f); + + for (i = 0; i < ARRAY_SIZE(s_aBumperSpheres); i++) { + s_aBumperSpheres[i].surface = SURFACE_CAR_PANEL; + s_aBumperSpheres[i].piece = 0; + } + + ms_colModelBumper1.boundingSphere.Set(2.2f, CVector(0.0f, -0.6f, 0.0f)); + ms_colModelBumper1.boundingBox.Set(CVector(-1.2f, -0.3f, -0.2f), CVector(1.2f, 0.3f, 0.2f)); + + SET_COLMODEL_SPHERES(ms_colModelBumper1, s_aBumperSpheres); + + // Panel Spheres + + for (i = 0; i < ARRAY_SIZE(s_aPanelSpheres); i++) + s_aPanelSpheres[i].radius = 0.15f; + + s_aPanelSpheres[0].center = CVector(0.15f, 0.45f, 0.0f); + s_aPanelSpheres[1].center = CVector(0.15f, -0.45f, 0.0f); + s_aPanelSpheres[2].center = CVector(-0.15f, -0.45f, 0.0f); + s_aPanelSpheres[3].center = CVector(-0.15f, 0.45f, 0.0f); + + for (i = 0; i < ARRAY_SIZE(s_aPanelSpheres); i++) { + s_aPanelSpheres[i].surface = SURFACE_CAR_PANEL; + s_aPanelSpheres[i].piece = 0; + } + + ms_colModelPanel1.boundingSphere.Set(1.4f, CVector(0.0f, 0.0f, 0.0f)); + ms_colModelPanel1.boundingBox.Set(CVector(-0.3f, -0.6f, -0.15f), CVector(0.3f, 0.6f, 0.15f)); + + SET_COLMODEL_SPHERES(ms_colModelPanel1, s_aPanelSpheres); + + // Bonnet Spheres + + for (i = 0; i < ARRAY_SIZE(s_aBonnetSpheres); i++) + s_aBonnetSpheres[i].radius = 0.2f; + + s_aBonnetSpheres[0].center = CVector(-0.4f, 0.1f, 0.0f); + s_aBonnetSpheres[1].center = CVector(-0.4f, 0.9f, 0.0f); + s_aBonnetSpheres[2].center = CVector(0.4f, 0.1f, 0.0f); + s_aBonnetSpheres[3].center = CVector(0.4f, 0.9f, 0.0f); + + for (i = 0; i < ARRAY_SIZE(s_aBonnetSpheres); i++) { + s_aBonnetSpheres[i].surface = SURFACE_CAR_PANEL; + s_aBonnetSpheres[i].piece = 0; + } + + ms_colModelBonnet1.boundingSphere.Set(1.7f, CVector(0.0f, 0.5f, 0.0f)); + ms_colModelBonnet1.boundingBox.Set(CVector(-0.7f, -0.2f, -0.3f), CVector(0.7f, 1.2f, 0.3f)); + + SET_COLMODEL_SPHERES(ms_colModelBonnet1, s_aBonnetSpheres); + + // Boot Spheres + + for (i = 0; i < ARRAY_SIZE(s_aBootSpheres); i++) + s_aBootSpheres[i].radius = 0.2f; + + s_aBootSpheres[0].center = CVector(-0.4f, -0.1f, 0.0f); + s_aBootSpheres[1].center = CVector(-0.4f, -0.6f, 0.0f); + s_aBootSpheres[2].center = CVector(0.4f, -0.1f, 0.0f); + s_aBootSpheres[3].center = CVector(0.4f, -0.6f, 0.0f); + + for (i = 0; i < ARRAY_SIZE(s_aBootSpheres); i++) { + s_aBootSpheres[i].surface = SURFACE_CAR_PANEL; + s_aBootSpheres[i].piece = 0; + } + + ms_colModelBoot1.boundingSphere.Set(1.4f, CVector(0.0f, -0.4f, 0.0f)); + ms_colModelBoot1.boundingBox.Set(CVector(-0.7f, -0.9f, -0.3f), CVector(0.7f, 0.2f, 0.3f)); + + SET_COLMODEL_SPHERES(ms_colModelBoot1, s_aBootSpheres); + + // Wheel Spheres + + s_aWheelSpheres[0].radius = 0.35f; + s_aWheelSpheres[1].radius = 0.35f; + + s_aWheelSpheres[0].center = CVector(-0.3f, 0.0f, 0.0f); + s_aWheelSpheres[1].center = CVector(0.3f, 0.0f, 0.0f); + +#ifdef FIX_BUGS + for (i = 0; i < ARRAY_SIZE(s_aWheelSpheres); i++) { +#else + for (i = 0; i < ARRAY_SIZE(s_aBootSpheres); i++) { +#endif + s_aWheelSpheres[i].surface = SURFACE_WHEELBASE; + s_aWheelSpheres[i].piece = 0; + } + + ms_colModelWheel1.boundingSphere.Set(1.4f, CVector(0.0f, 0.0f, 0.0f)); + ms_colModelWheel1.boundingBox.Set(CVector(-0.7f, -0.4f, -0.4f), CVector(0.7f, 0.4f, 0.4f)); + + SET_COLMODEL_SPHERES(ms_colModelWheel1, s_aWheelSpheres); + + // Body Part Spheres 1 + + s_aBodyPartSpheres1[0].radius = 0.2f; + s_aBodyPartSpheres1[1].radius = 0.2f; + + s_aBodyPartSpheres1[0].center = CVector(0.0f, 0.0f, 0.0f); + s_aBodyPartSpheres1[1].center = CVector(0.8f, 0.0f, 0.0f); + +#ifdef FIX_BUGS + for (i = 0; i < ARRAY_SIZE(s_aBodyPartSpheres1); i++) { +#else + for (i = 0; i < ARRAY_SIZE(s_aBootSpheres); i++) { +#endif + s_aBodyPartSpheres1[i].surface = SURFACE_PED; + s_aBodyPartSpheres1[i].piece = 0; + } + + ms_colModelBodyPart1.boundingSphere.Set(0.7f, CVector(0.4f, 0.0f, 0.0f)); + ms_colModelBodyPart1.boundingBox.Set(CVector(-0.3f, -0.3f, -0.3f), CVector(1.1f, 0.3f, 0.3f)); + + SET_COLMODEL_SPHERES(ms_colModelBodyPart1, s_aBodyPartSpheres1); + + // Body Part Spheres 2 + + s_aBodyPartSpheres2[0].radius = 0.15f; + s_aBodyPartSpheres2[1].radius = 0.15f; + + s_aBodyPartSpheres2[0].center = CVector(0.0f, 0.0f, 0.0f); + s_aBodyPartSpheres2[1].center = CVector(0.5f, 0.0f, 0.0f); + +#ifdef FIX_BUGS + for (i = 0; i < ARRAY_SIZE(s_aBodyPartSpheres2); i++) { +#else + for (i = 0; i < ARRAY_SIZE(s_aBootSpheres); i++) { +#endif + s_aBodyPartSpheres2[i].surface = SURFACE_PED; + s_aBodyPartSpheres2[i].piece = 0; + } + + ms_colModelBodyPart2.boundingSphere.Set(0.5f, CVector(0.25f, 0.0f, 0.0f)); + ms_colModelBodyPart2.boundingBox.Set(CVector(-0.2f, -0.2f, -0.2f), CVector(0.7f, 0.2f, 0.2f)); + + SET_COLMODEL_SPHERES(ms_colModelBodyPart2, s_aBodyPartSpheres2); + + ms_colModelWeapon.boundingSphere.radius = 0.25f; + ms_colModelWeapon.boundingBox.min.x = -0.25f; + ms_colModelWeapon.boundingBox.min.y = -0.25f; + ms_colModelWeapon.boundingBox.min.z = -0.25f; + ms_colModelWeapon.boundingBox.max.x = 0.25f; + ms_colModelWeapon.boundingBox.max.y = 0.25f; + ms_colModelWeapon.boundingBox.max.z = 0.25f; + +#undef SET_COLMODEL_SPHERES +} diff --git a/src/miami/collision/TempColModels.h b/src/miami/collision/TempColModels.h new file mode 100644 index 00000000..1a888723 --- /dev/null +++ b/src/miami/collision/TempColModels.h @@ -0,0 +1,24 @@ +#pragma once + +#include "ColModel.h" + +class CTempColModels +{ +public: + static CColModel ms_colModelPed1; + static CColModel ms_colModelPed2; + static CColModel ms_colModelBBox; + static CColModel ms_colModelBumper1; + static CColModel ms_colModelWheel1; + static CColModel ms_colModelPanel1; + static CColModel ms_colModelBodyPart2; + static CColModel ms_colModelBodyPart1; + static CColModel ms_colModelCutObj[5]; + static CColModel ms_colModelPedGroundHit; + static CColModel ms_colModelBoot1; + static CColModel ms_colModelDoor1; + static CColModel ms_colModelBonnet1; + static CColModel ms_colModelWeapon; + + static void Initialise(void); +}; diff --git a/src/miami/collision/VuCollision.cpp b/src/miami/collision/VuCollision.cpp new file mode 100644 index 00000000..8828d2e1 --- /dev/null +++ b/src/miami/collision/VuCollision.cpp @@ -0,0 +1,282 @@ +#include "common.h" +#ifdef VU_COLLISION +#include "VuVector.h" +#include "VuCollision.h" + +#ifndef GTA_PS2 +int16 vi01; +CVuVector vf01; +CVuVector vf02; +CVuVector vf03; + +CVuVector +DistanceBetweenSphereAndLine(const CVuVector ¢er, const CVuVector &p0, const CVuVector &line) +{ + // center VF12 + // p0 VF14 + // line VF15 + CVuVector ret; // VF16 + CVuVector p1 = p0+line; + CVuVector dist0 = center - p0; // VF20 + CVuVector dist1 = center - p1; // VF25 + float lenSq = line.MagnitudeSqr(); // VF21 + float distSq0 = dist0.MagnitudeSqr(); // VF22 + float distSq1 = dist1.MagnitudeSqr(); + float dot = DotProduct(dist0, line); // VF23 + if(dot < 0.0f){ + // not above line, closest to p0 + ret = p0; + ret.w = distSq0; + return ret; + } + float t = dot/lenSq; // param of nearest point on infinite line + if(t > 1.0f){ + // not above line, closest to p1 + ret = p1; + ret.w = distSq1; + return ret; + } + // closest to line + ret = p0 + line*t; + ret.w = (ret - center).MagnitudeSqr(); + return ret; +} +inline int SignFlags(const CVector &v) +{ + int f = 0; + if(v.x < 0.0f) f |= 1; + if(v.y < 0.0f) f |= 2; + if(v.z < 0.0f) f |= 4; + return f; +} +#endif + +extern "C" void +LineToTriangleCollision(const CVuVector &p0, const CVuVector &p1, + const CVuVector &v0, const CVuVector &v1, const CVuVector &v2, + const CVuVector &plane) +{ +#ifdef GTA_PS2 + __asm__ volatile ( + ".set noreorder\n" + "lqc2 vf12, 0x0(%0)\n" + "lqc2 vf13, 0x0(%1)\n" + "lqc2 vf14, 0x0(%2)\n" + "lqc2 vf15, 0x0(%3)\n" + "lqc2 vf16, 0x0(%4)\n" + "lqc2 vf17, 0x0(%5)\n" + "vcallms Vu0LineToTriangleCollisionStart\n" + ".set reorder\n" + : + : "r" (&p0), "r" (&p1), "r" (&v0), "r" (&v1), "r" (&v2), "r" (&plane) + ); +#else + float dot0 = DotProduct(plane, p0); + float dot1 = DotProduct(plane, p1); + float dist0 = plane.w - dot0; + float dist1 = plane.w - dot1; + + // if points are on the same side, no collision + if(dist0 * dist1 > 0.0f){ + vi01 = 0; + return; + } + + CVuVector diff = p1 - p0; + float t = dist0/(dot1 - dot0); + CVuVector p = p0 + diff*t; + p.w = 0.0f; + vf01 = p; + vf03.x = t; + + // Check if point is inside + CVector cross1 = CrossProduct(p-v0, v1-v0); + CVector cross2 = CrossProduct(p-v1, v2-v1); + CVector cross3 = CrossProduct(p-v2, v0-v2); + // Only check relevant directions + int flagmask = 0; + if(Abs(plane.x) > 0.5f) flagmask |= 1; + if(Abs(plane.y) > 0.5f) flagmask |= 2; + if(Abs(plane.z) > 0.5f) flagmask |= 4; + int flags1 = SignFlags(cross1) & flagmask; + int flags2 = SignFlags(cross2) & flagmask; + int flags3 = SignFlags(cross3) & flagmask; + // inside if on the same side of all edges + if(flags1 != flags2 || flags1 != flags3){ + vi01 = 0; + return; + } + vi01 = 1; + vf02 = plane; + return; +#endif +} + +extern "C" void +LineToTriangleCollisionCompressed(const CVuVector &p0, const CVuVector &p1, VuTriangle &tri) +{ +#ifdef GTA_PS2 + __asm__ volatile ( + ".set noreorder\n" + "lqc2 vf12, 0x0(%0)\n" + "lqc2 vf13, 0x0(%1)\n" + "lqc2 vf14, 0x0(%2)\n" + "lqc2 vf15, 0x10(%2)\n" + "lqc2 vf16, 0x20(%2)\n" + "lqc2 vf17, 0x30(%2)\n" + "vcallms Vu0LineToTriangleCollisionCompressedStart\n" + ".set reorder\n" + : + : "r" (&p0), "r" (&p1), "r" (&tri) + ); +#else + CVuVector v0, v1, v2, plane; + v0.x = tri.v0[0]/128.0f; + v0.y = tri.v0[1]/128.0f; + v0.z = tri.v0[2]/128.0f; + v0.w = tri.v0[3]/128.0f; + v1.x = tri.v1[0]/128.0f; + v1.y = tri.v1[1]/128.0f; + v1.z = tri.v1[2]/128.0f; + v1.w = tri.v1[3]/128.0f; + v2.x = tri.v2[0]/128.0f; + v2.y = tri.v2[1]/128.0f; + v2.z = tri.v2[2]/128.0f; + v2.w = tri.v2[3]/128.0f; + plane.x = tri.plane[0]/4096.0f; + plane.y = tri.plane[1]/4096.0f; + plane.z = tri.plane[2]/4096.0f; + plane.w = tri.plane[3]/128.0f; + LineToTriangleCollision(p0, p1, v0, v1, v2, plane); +#endif +} + +extern "C" void +SphereToTriangleCollision(const CVuVector &sph, + const CVuVector &v0, const CVuVector &v1, const CVuVector &v2, + const CVuVector &plane) +{ +#ifdef GTA_PS2 + __asm__ volatile ( + ".set noreorder\n" + "lqc2 vf12, 0x0(%0)\n" + "lqc2 vf14, 0x0(%1)\n" + "lqc2 vf15, 0x0(%2)\n" + "lqc2 vf16, 0x0(%3)\n" + "lqc2 vf17, 0x0(%4)\n" + "vcallms Vu0SphereToTriangleCollisionStart\n" + ".set reorder\n" + : + : "r" (&sph), "r" (&v0), "r" (&v1), "r" (&v2), "r" (&plane) + ); +#else + float planedist = DotProduct(plane, sph) - plane.w; // VF02 + if(Abs(planedist) > sph.w){ + vi01 = 0; + return; + } + // point on plane + CVuVector p = sph - planedist*plane; + p.w = 0.0f; + vf01 = p; + planedist = Abs(planedist); + // edges + CVuVector v01 = v1 - v0; + CVuVector v12 = v2 - v1; + CVuVector v20 = v0 - v2; + // VU code calculates normal again for some weird reason... + // Check sides of point + CVector cross1 = CrossProduct(p-v0, v01); + CVector cross2 = CrossProduct(p-v1, v12); + CVector cross3 = CrossProduct(p-v2, v20); + // Only check relevant directions + int flagmask = 0; + if(Abs(plane.x) > 0.1f) flagmask |= 1; + if(Abs(plane.y) > 0.1f) flagmask |= 2; + if(Abs(plane.z) > 0.1f) flagmask |= 4; + int nflags = SignFlags(plane) & flagmask; + int flags1 = SignFlags(cross1) & flagmask; + int flags2 = SignFlags(cross2) & flagmask; + int flags3 = SignFlags(cross3) & flagmask; + int testcase = 0; + CVuVector closest(0.0f, 0.0f, 0.0f); // VF04 + if(flags1 == nflags){ + closest += v2; + testcase++; + } + if(flags2 == nflags){ + closest += v0; + testcase++; + } + if(flags3 == nflags){ + closest += v1; + testcase++; + } + if(testcase == 3){ + // inside triangle - dist to plane already checked + vf02 = plane; + vf02.w = vf03.x = planedist; + vi01 = 1; + }else if(testcase == 1){ + // outside two sides - closest to point opposide inside edge + vf01 = closest; + vf02 = sph - closest; + float distSq = vf02.MagnitudeSqr(); + vi01 = sph.w*sph.w > distSq; + vf03.x = Sqrt(distSq); + vf02 *= 1.0f/vf03.x; + }else{ + // inside two sides - closest to third edge + if(flags1 != nflags) + closest = DistanceBetweenSphereAndLine(sph, v0, v01); + else if(flags2 != nflags) + closest = DistanceBetweenSphereAndLine(sph, v1, v12); + else + closest = DistanceBetweenSphereAndLine(sph, v2, v20); + vi01 = sph.w*sph.w > closest.w; + vf01 = closest; + vf02 = sph - closest; + vf03.x = Sqrt(closest.w); + vf02 *= 1.0f/vf03.x; + } +#endif +} + +extern "C" void +SphereToTriangleCollisionCompressed(const CVuVector &sph, VuTriangle &tri) +{ +#ifdef GTA_PS2 + __asm__ volatile ( + ".set noreorder\n" + "lqc2 vf12, 0x0(%0)\n" + "lqc2 vf14, 0x0(%1)\n" + "lqc2 vf15, 0x10(%1)\n" + "lqc2 vf16, 0x20(%1)\n" + "lqc2 vf17, 0x30(%1)\n" + "vcallms Vu0SphereToTriangleCollisionCompressedStart\n" + ".set reorder\n" + : + : "r" (&sph), "r" (&tri) + ); +#else + CVuVector v0, v1, v2, plane; + v0.x = tri.v0[0]/128.0f; + v0.y = tri.v0[1]/128.0f; + v0.z = tri.v0[2]/128.0f; + v0.w = tri.v0[3]/128.0f; + v1.x = tri.v1[0]/128.0f; + v1.y = tri.v1[1]/128.0f; + v1.z = tri.v1[2]/128.0f; + v1.w = tri.v1[3]/128.0f; + v2.x = tri.v2[0]/128.0f; + v2.y = tri.v2[1]/128.0f; + v2.z = tri.v2[2]/128.0f; + v2.w = tri.v2[3]/128.0f; + plane.x = tri.plane[0]/4096.0f; + plane.y = tri.plane[1]/4096.0f; + plane.z = tri.plane[2]/4096.0f; + plane.w = tri.plane[3]/128.0f; + SphereToTriangleCollision(sph, v0, v1, v2, plane); +#endif +} +#endif \ No newline at end of file diff --git a/src/miami/collision/VuCollision.h b/src/miami/collision/VuCollision.h new file mode 100644 index 00000000..29ca4cbf --- /dev/null +++ b/src/miami/collision/VuCollision.h @@ -0,0 +1,32 @@ +#pragma once + + +struct VuTriangle +{ + // Compressed int16 but unpacked +#ifdef GTA_PS2 + uint128 v0; + uint128 v1; + uint128 v2; + uint128 plane; +#else + int32 v0[4]; + int32 v1[4]; + int32 v2[4]; + int32 plane[4]; +#endif +}; + +#ifndef GTA_PS2 +extern int16 vi01; +extern CVuVector vf01; +extern CVuVector vf02; +extern CVuVector vf03; +#endif + +extern "C" { +void LineToTriangleCollision(const CVuVector &p0, const CVuVector &p1, const CVuVector &v0, const CVuVector &v1, const CVuVector &v2, const CVuVector &plane); +void LineToTriangleCollisionCompressed(const CVuVector &p0, const CVuVector &p1, VuTriangle &tri); +void SphereToTriangleCollision(const CVuVector &sph, const CVuVector &v0, const CVuVector &v1, const CVuVector &v2, const CVuVector &plane); +void SphereToTriangleCollisionCompressed(const CVuVector &sph, VuTriangle &tri); +} diff --git a/src/miami/collision/vu0Collision.dsm b/src/miami/collision/vu0Collision.dsm new file mode 100644 index 00000000..657c8b81 --- /dev/null +++ b/src/miami/collision/vu0Collision.dsm @@ -0,0 +1,21 @@ +.align 4 +.global Vu0CollisionDmaTag +Vu0CollisionDmaTag: +DMAcnt * +MPG 0, * +.vu +.include "vu0Collision_1.s" +.EndMPG +.EndDmaData +DMAend + +.global Vu0Collision2DmaTag +Vu0Collision2DmaTag: +DMAcnt * +MPG 0, * +.vu +.include "vu0Collision_2.s" +.EndMPG +.EndDmaData +DMAend +.end diff --git a/src/miami/collision/vu0Collision_1.s b/src/miami/collision/vu0Collision_1.s new file mode 100644 index 00000000..055c8640 --- /dev/null +++ b/src/miami/collision/vu0Collision_1.s @@ -0,0 +1,610 @@ +QuitAndFail: + NOP[E] IADDIU VI01, VI00, 0 + NOP NOP + + +QuitAndSucceed: + NOP[E] IADDIU VI01, VI00, 1 + NOP NOP + + +; 20 -- unused +; VF12, VF13 xyz: sphere centers +; VF14, VF15 x: sphere radii +; out: +; VI01: set when collision +; VF01: supposed to be intersection point? +; VF02: normal (pointing towards s1, not normalized) +.globl Vu0SphereToSphereCollision +Vu0SphereToSphereCollision: + SUB.xyz VF02, VF13, VF12 NOP ; dist of centers + ADD.x VF04, VF14, VF15 NOP ; s = sum of radii + MUL.xyzw VF03, VF02, VF02 NOP ; + MUL.x VF04, VF04, VF04 DIV Q, VF14x, VF04x ; square s + NOP NOP ; + NOP NOP ; + MULAx.w ACC, VF00, VF03 NOP ; + MADDAy.w ACC, VF00, VF03 NOP ; + MADDz.w VF03, VF00, VF03 NOP ; d = DistSq of centers + NOP NOP ; + MULAw.xyz ACC, VF12, VF00 NOP ; + MADDq.xyz VF01, VF02, Q NOP ; intersection, but wrong + CLIPw.xyz VF04, VF03 NOP ; compare s and d + SUB.xyz VF02, VF00, VF02 NOP ; compute normal + NOP NOP ; + NOP NOP ; + NOP FCAND VI01, 0x3 ; 0x2 cannot be set here + NOP[E] NOP ; + NOP NOP ; + + +; B8 -- unused +; VF12: +; VF13: radius +; VF14: +; VF15: box dimensions (?) +.globl Vu0SphereToAABBCollision +Vu0SphereToAABBCollision: + SUB.xyz VF03, VF12, VF14 LOI 0.5 + MULi.xyz VF15, VF15, I NOP + MUL.x VF13, VF13, VF13 NOP + SUB.xyz VF04, VF03, VF15 NOP + ADD.xyz VF05, VF03, VF15 MR32.xyzw VF16, VF15 + CLIPw.xyz VF03, VF16 MR32.xyzw VF17, VF16 + MUL.xyz VF04, VF04, VF04 NOP + MUL.xyz VF05, VF05, VF05 NOP + CLIPw.xyz VF03, VF17 MR32.xyzw VF16, VF17 + NOP FCAND VI01, 0x1 + MINI.xyz VF04, VF04, VF05 MFIR.x VF09, VI01 + NOP NOP + CLIPw.xyz VF03, VF16 FCAND VI01, 0x4 + NOP MFIR.y VF09, VI01 + NOP NOP + MULAx.w ACC, VF00, VF00 NOP + ADD.xyz VF01, VF00, VF03 FCAND VI01, 0x10 + NOP MFIR.z VF09, VI01 + NOP LOI 2 + NOP FCAND VI01, 0x30 + SUBAw.xyz ACC, VF00, VF00 IADD VI04, VI00, VI01 + ITOF0.xyz VF09, VF09 FCAND VI01, 0x300 + NOP IADD VI03, VI00, VI01 + NOP FCAND VI01, 0x3000 + NOP IADD VI02, VI00, VI01 + MADDi.xyzw VF09, VF09, I NOP + NOP IBEQ VI04, VI00, IgnoreZValue + NOP NOP + MADDAz.w ACC, VF00, VF04 NOP + MUL.z VF01, VF09, VF15 NOP +IgnoreZValue: + NOP IBEQ VI03, VI00, IgnoreYValue + NOP NOP + MADDAy.w ACC, VF00, VF04 NOP + MUL.y VF01, VF09, VF15 NOP +IgnoreYValue: + NOP IBEQ VI02, VI00, IgnoreXValue + NOP NOP + MADDAx.w ACC, VF00, VF04 NOP + MUL.x VF01, VF09, VF15 NOP +IgnoreXValue: + MADDx.w VF06, VF00, VF00 NOP + SUB.xyz VF02, VF03, VF01 NOP + ADD.xyz VF01, VF01, VF14 NOP + MULx.w VF01, VF00, VF00 NOP + CLIPw.xyz VF13, VF06 NOP + NOP NOP + NOP NOP + NOP NOP + NOP FCAND VI01, 0x1 +QuitMicrocode: + NOP[E] NOP + NOP NOP + + +; 240 +.globl Vu0LineToSphereCollision +Vu0LineToSphereCollision: + SUB.xyzw VF01, VF13, VF12 NOP + SUB.xyzw VF02, VF14, VF12 NOP + MUL.xyz VF03, VF01, VF02 NOP + MUL.xyz VF04, VF01, VF01 NOP + MUL.x VF15, VF15, VF15 NOP + MUL.xyz VF02, VF02, VF02 NOP + MULAx.w ACC, VF00, VF03 NOP + MADDAy.w ACC, VF00, VF03 NOP + MADDz.w VF03, VF00, VF03 NOP + MULAx.w ACC, VF00, VF04 NOP + MADDAy.w ACC, VF00, VF04 NOP + MADDz.w VF01, VF00, VF04 NOP + MULAx.w ACC, VF00, VF02 NOP + MADDAy.w ACC, VF00, VF02 NOP + MADDz.w VF02, VF00, VF02 NOP + MULA.w ACC, VF03, VF03 NOP + MADDAx.w ACC, VF01, VF15 NOP + MSUB.w VF05, VF01, VF02 NOP + NOP NOP + NOP NOP + NOP IADDIU VI02, VI00, 0x10 + NOP FMAND VI01, VI02 + NOP IBNE VI01, VI00, QuitAndFail + NOP NOP + CLIPw.xyz VF15, VF02 SQRT Q, VF05w + NOP NOP + NOP NOP + NOP NOP + NOP FCAND VI01, 0x1 + NOP IBNE VI00, VI01, LineStartInsideSphere + NOP NOP + SUBq.w VF05, VF03, Q NOP + SUB.w VF05, VF05, VF01 DIV Q, VF05w, VF01w + NOP FMAND VI01, VI02 + NOP IBNE VI01, VI00, QuitAndFail + NOP NOP + NOP FMAND VI01, VI02 + NOP IBEQ VI01, VI00, QuitAndFail + NOP NOP + ADDA.xyz ACC, VF12, VF00 NOP + MADDq.xyz VF01, VF01, Q NOP + MULx.w VF01, VF00, VF00 NOP + SUB.xyz VF02, VF01, VF14 NOP + NOP[E] NOP + NOP NOP +LineStartInsideSphere: + NOP MOVE.xyzw VF01, VF12 + NOP[E] IADDIU VI01, VI00, 0x1 + NOP NOP + + +; 3C0 +.globl Vu0LineToAABBCollision +Vu0LineToAABBCollision: + SUB.xyzw VF08, VF13, VF12 LOI 0.5 + MULi.xyz VF15, VF15, I IADDIU VI08, VI00, 0x0 + SUB.xyzw VF12, VF12, VF14 NOP + SUB.xyzw VF13, VF13, VF14 NOP + NOP DIV Q, VF00w, VF08x + NOP MR32.xyzw VF03, VF15 + SUB.xyz VF06, VF15, VF12 NOP + ADD.xyz VF07, VF15, VF12 NOP + NOP NOP + CLIPw.xyz VF12, VF03 MR32.xyzw VF04, VF03 + NOP NOP + ADDq.x VF09, VF00, Q DIV Q, VF00w, VF08y + NOP NOP + CLIPw.xyz VF12, VF04 MR32.xyzw VF05, VF04 + SUB.xyz VF07, VF00, VF07 IADDIU VI06, VI00, 0xCC + NOP IADDIU VI07, VI00, 0x30 + NOP NOP + CLIPw.xyz VF12, VF05 FCGET VI02 + NOP IAND VI02, VI02, VI06 + ADDq.y VF09, VF00, Q DIV Q, VF00w, VF08z + SUB.xyz VF10, VF00, VF10 NOP + CLIPw.xyz VF13, VF03 FCGET VI03 + CLIPw.xyz VF13, VF04 IAND VI03, VI03, VI07 + CLIPw.xyz VF13, VF05 FCAND VI01, 0x3330 + NOP IBEQ VI01, VI00, StartPointInsideAABB + NOP NOP + ADDq.z VF09, VF00, Q FCGET VI04 + NOP FCGET VI05 + NOP IAND VI04, VI04, VI06 + NOP IAND VI05, VI05, VI07 + MULx.xyz VF17, VF08, VF09 NOP + MULy.xyz VF18, VF08, VF09 IADDIU VI07, VI00, 0x80 + MULz.xyz VF19, VF08, VF09 IAND VI06, VI02, VI07 + MUL.w VF10, VF00, VF00 IAND VI07, VI04, VI07 + NOP NOP + NOP IBEQ VI06, VI07, CheckMaxXSide + NOP NOP + MULAx.xyz ACC, VF17, VF07 NOP + MADDw.xyz VF16, VF12, VF00 NOP + MUL.x VF10, VF07, VF09 NOP + CLIPw.xyz VF16, VF04 NOP + CLIPw.xyz VF16, VF05 NOP + NOP NOP + NOP NOP + NOP NOP + NOP FCAND VI01, 0x330 + NOP IBNE VI01, VI00, CheckMaxXSide + NOP NOP + MULx.w VF10, VF00, VF10 IADDIU VI08, VI00, 0x1 + ADD.yz VF02, VF00, VF00 MOVE.xyzw VF01, VF16 + SUBw.x VF02, VF00, VF00 NOP +CheckMaxXSide: + MULAx.xyz ACC, VF17, VF06 IADDIU VI07, VI00, 0x40 + MADDw.xyz VF16, VF12, VF00 IAND VI06, VI02, VI07 + MUL.x VF10, VF06, VF09 IAND VI07, VI04, VI07 + NOP NOP + NOP IBEQ VI06, VI07, CheckMinYSide + NOP NOP + CLIPw.xyz VF16, VF04 NOP + CLIPw.xyz VF16, VF05 NOP + CLIPw.xyz VF10, VF10 NOP + NOP NOP + NOP NOP + NOP NOP + NOP FCAND VI01, 0xCC03 + NOP IBNE VI01, VI00, CheckMinYSide + NOP NOP + MULx.w VF10, VF00, VF10 IADDIU VI08, VI00, 0x1 + ADD.yz VF02, VF00, VF00 MOVE.xyzw VF01, VF16 + ADDw.x VF02, VF00, VF00 NOP +CheckMinYSide: + MULAy.xyz ACC, VF18, VF07 IADDIU VI07, VI00, 0x8 + MADDw.xyz VF16, VF12, VF00 IAND VI06, VI02, VI07 + MUL.y VF10, VF07, VF09 IAND VI07, VI04, VI07 + NOP NOP + NOP IBEQ VI06, VI07, CheckMaxYSide + NOP NOP + CLIPw.xyz VF16, VF03 NOP + CLIPw.xyz VF16, VF05 NOP + CLIPw.xyz VF10, VF10 NOP + NOP NOP + NOP NOP + NOP NOP + NOP FCAND VI01, 0x3C0C + NOP IBNE VI01, VI00, CheckMaxYSide + NOP NOP + MULy.w VF10, VF00, VF10 IADDIU VI08, VI00, 0x1 + ADD.xz VF02, VF00, VF00 MOVE.xyzw VF01, VF16 + SUBw.y VF02, VF00, VF00 NOP +CheckMaxYSide: + MULAy.xyz ACC, VF18, VF06 IADDIU VI07, VI00, 0x4 + MADDw.xyz VF16, VF12, VF00 IAND VI06, VI02, VI07 + MUL.y VF10, VF06, VF09 IAND VI07, VI04, VI07 + NOP NOP + NOP IBEQ VI06, VI07, CheckMinZSide + NOP NOP + CLIPw.xyz VF16, VF03 NOP + CLIPw.xyz VF16, VF05 NOP + CLIPw.xyz VF10, VF10 NOP + NOP NOP + NOP NOP + NOP NOP + NOP FCAND VI01, 0x3C0C + NOP IBNE VI01, VI00, CheckMinZSide + NOP NOP + MULy.w VF10, VF00, VF10 IADDIU VI08, VI00, 0x1 + ADD.xz VF02, VF00, VF00 MOVE.xyzw VF01, VF16 + ADDw.y VF02, VF00, VF00 NOP +CheckMinZSide: + MULAz.xyz ACC, VF19, VF07 IADDIU VI07, VI00, 0x20 + MADDw.xyz VF16, VF12, VF00 IAND VI06, VI03, VI07 + MUL.z VF10, VF07, VF09 IAND VI07, VI05, VI07 + NOP NOP + NOP IBEQ VI06, VI07, CheckMaxZSide + NOP NOP + CLIPw.xyz VF16, VF03 NOP + CLIPw.xyz VF16, VF04 NOP + CLIPw.xyz VF10, VF10 NOP + NOP NOP + NOP NOP + NOP NOP + NOP FCAND VI01, 0x3330 + NOP IBNE VI01, VI00, CheckMaxZSide + NOP NOP + MULz.w VF10, VF00, VF10 IADDIU VI08, VI00, 0x1 + ADD.xy VF02, VF00, VF00 MOVE.xyzw VF01, VF16 + SUBw.z VF02, VF00, VF00 NOP +CheckMaxZSide: + MULAz.xyz ACC, VF19, VF06 IADDIU VI07, VI00, 0x10 + MADDw.xyz VF16, VF12, VF00 IAND VI06, VI03, VI07 + MUL.z VF10, VF06, VF09 IAND VI07, VI05, VI07 + NOP NOP + NOP IBEQ VI06, VI07, DoneAllChecks + NOP NOP + CLIPw.xyz VF16, VF03 NOP + CLIPw.xyz VF16, VF04 NOP + CLIPw.xyz VF10, VF10 NOP + NOP NOP + NOP NOP + NOP NOP + NOP FCAND VI01, 0x3330 + NOP IBNE VI01, VI00, DoneAllChecks + NOP NOP + MULz.w VF10, VF00, VF10 IADDIU VI08, VI00, 0x1 + ADD.xy VF02, VF00, VF00 MOVE.xyzw VF01, VF16 + ADDw.z VF02, VF00, VF00 NOP +DoneAllChecks: + ADD.xyz VF01, VF01, VF14 IADD VI01, VI00, VI08 + NOP[E] NOP + NOP NOP +StartPointInsideAABB: + ADD.xyz VF01, VF12, VF14 WAITQ + NOP IADDIU VI01, VI00, 0x1 + NOP[E] NOP + NOP NOP + + +; 860 +.globl Vu0LineToTriangleCollisionCompressedStart +Vu0LineToTriangleCollisionCompressedStart: + ITOF0.xyzw VF17, VF17 LOI 0.000244140625 ; 1.0/4096.0 + ITOF0.xyzw VF14, VF14 NOP + ITOF0.xyzw VF15, VF15 NOP + ITOF0.xyzw VF16, VF16 NOP + MULi.xyz VF17, VF17, I LOI 0.0078125 ; 1.0/128.0 + MULi.w VF17, VF17, I NOP + MULi.xyzw VF14, VF14, I NOP + MULi.xyzw VF15, VF15, I NOP + MULi.xyzw VF16, VF16, I NOP +; fall through + +; 8A8 +; VF12: point0 +; VF13: point1 +; VF14-16: verts +; VF17: plane +; out: +; VF01: intersection point +; VF02: triangle normal +; VF03 x: intersection parameter +.globl Vu0LineToTriangleCollisionStart +Vu0LineToTriangleCollisionStart: + MUL.xyz VF10, VF17, VF12 LOI 0.5 + MUL.xyz VF11, VF17, VF13 NOP + SUB.xyz VF02, VF13, VF12 NOP ; line dist + ADD.xyz VF17, VF17, VF00 NOP + MULi.w VF03, VF00, I NOP + MULAx.w ACC, VF00, VF10 NOP + MADDAy.w ACC, VF00, VF10 IADDIU VI06, VI00, 0xE0 + MADDz.w VF10, VF00, VF10 FMAND VI05, VI06 ; -- normal sign flags, unused + MULAx.w ACC, VF00, VF11 NOP + MADDAy.w ACC, VF00, VF11 NOP + MADDz.w VF11, VF00, VF11 NOP + SUB.w VF09, VF17, VF10 NOP ; plane-pos 0 + CLIPw.xyz VF17, VF03 NOP ; compare normal against 0.5 to figure out which in which dimension to compare + NOP IADDIU VI02, VI00, 0x10 ; Sw flag + SUBA.w ACC, VF17, VF11 NOP ; plane-pos 1 + SUB.w VF08, VF11, VF10 FMAND VI01, VI02 + NOP NOP + NOP NOP + NOP FMAND VI02, VI02 + NOP IBEQ VI01, VI02, QuitAndFail ; if on same side, no collision + NOP NOP + NOP DIV Q, VF09w, VF08w ; parameter of intersection + NOP FCAND VI01, 0x3 ; check x direction + NOP IADDIU VI02, VI01, 0x7F + NOP IADDIU VI06, VI00, 0x80 + NOP IAND VI02, VI02, VI06 ; Sx flag + NOP FCAND VI01, 0xC ; check y direction + NOP IADDIU VI03, VI01, 0x3F + MULAw.xyz ACC, VF12, VF00 IADDIU VI06, VI00, 0x40 + MADDq.xyz VF01, VF02, Q IAND VI03, VI03, VI06 ; point of intersection -- Sy flag + MULx.w VF01, VF00, VF00 FCAND VI01, 0x30 ; -- check z direction + ADDq.x VF03, VF00, Q IADDIU VI04, VI01, 0x1F ; output parameter + SUB.xyz VF05, VF15, VF14 IADDIU VI06, VI00, 0x20 ; edge vectors + SUB.xyz VF08, VF01, VF14 IAND VI04, VI04, VI06 ; edge vectors -- Sz flag + SUB.xyz VF06, VF16, VF15 IADD VI06, VI02, VI03 ; edge vectors + SUB.xyz VF09, VF01, VF15 IADD VI06, VI06, VI04 ; edge vectors -- combine flags + SUB.xyz VF07, VF14, VF16 NOP ; edge vectors + SUB.xyz VF10, VF01, VF16 NOP ; edge vectors + OPMULA.xyz ACC, VF08, VF05 NOP + OPMSUB.xyz VF18, VF05, VF08 NOP ; cross1 + OPMULA.xyz ACC, VF09, VF06 NOP + OPMSUB.xyz VF19, VF06, VF09 NOP ; cross2 + OPMULA.xyz ACC, VF10, VF07 NOP + OPMSUB.xyz VF20, VF07, VF10 FMAND VI02, VI06 ; cross3 + NOP NOP + NOP FMAND VI03, VI06 + NOP NOP + NOP FMAND VI04, VI06 + NOP NOP + NOP IBNE VI03, VI02, QuitAndFail ; point has to lie on the same side of all edges (i.e. inside) + NOP NOP + NOP IBNE VI04, VI02, QuitAndFail + NOP NOP + MULw.xyz VF02, VF17, VF00 IADDIU VI01, VI00, 0x1 ; success + NOP[E] NOP + NOP NOP + + +; A68 +; VF12: center +; VF14: line origin +; VF15: line vector to other point +; out: VF16 xyz: nearest point on line; w: distance to that point +DistanceBetweenSphereAndLine: + SUB.xyz VF20, VF12, VF14 NOP + MUL.xyz VF21, VF15, VF15 NOP + ADDA.xyz ACC, VF14, VF15 NOP + MSUBw.xyz VF25, VF12, VF00 NOP ; VF25 = VF12 - (VF14+VF15) + MUL.xyz VF22, VF20, VF20 NOP + MUL.xyz VF23, VF20, VF15 NOP + MULAx.w ACC, VF00, VF21 NOP + MADDAy.w ACC, VF00, VF21 NOP + MADDz.w VF21, VF00, VF21 NOP ; MagSq VF15 (line length) + MULAx.w ACC, VF00, VF23 NOP + MADDAy.w ACC, VF00, VF23 NOP + MADDz.w VF23, VF00, VF23 NOP ; dot(VF12-VF14, VF15) + MULAx.w ACC, VF00, VF22 NOP + MADDAy.w ACC, VF00, VF22 NOP + MADDz.w VF22, VF00, VF22 IADDIU VI08, VI00, 0x10 ; MagSq VF12-VF14 -- Sw bit + MUL.xyz VF25, VF25, VF25 FMAND VI08, VI08 + NOP DIV Q, VF23w, VF21w + NOP IBNE VI00, VI08, NegativeRatio + NOP NOP + ADDA.xyz ACC, VF00, VF14 NOP + MADDq.xyz VF16, VF15, Q WAITQ ; nearest point on infinte line + ADDq.x VF24, VF00, Q NOP ; ratio + NOP NOP + NOP NOP + SUB.xyz VF26, VF16, VF12 NOP + CLIPw.xyz VF24, VF00 NOP ; compare ratio to 1.0 + NOP NOP + NOP NOP + MUL.xyz VF26, VF26, VF26 NOP + NOP FCAND VI01, 0x1 + NOP IBNE VI00, VI01, RatioGreaterThanOne + NOP NOP + MULAx.w ACC, VF00, VF26 NOP + MADDAy.w ACC, VF00, VF26 NOP + MADDz.w VF16, VF00, VF26 NOP ; distance + NOP JR VI15 + NOP NOP +NegativeRatio: + ADD.xyz VF16, VF00, VF14 NOP ; return line origin + MUL.w VF16, VF00, VF22 NOP ; and DistSq to it + NOP JR VI15 + NOP NOP +RatioGreaterThanOne: + MULAx.w ACC, VF00, VF25 NOP + MADDAy.w ACC, VF00, VF25 NOP + MADDz.w VF16, VF00, VF25 NOP + ADD.xyz VF16, VF14, VF15 NOP ; return toerh line point + NOP JR VI15 + NOP NOP + + +; BE0 +.globl Vu0SphereToTriangleCollisionCompressedStart +Vu0SphereToTriangleCollisionCompressedStart: + ITOF0.xyzw VF17, VF17 LOI 0.000244140625 ; 1.0/4096.0 + ITOF0.xyzw VF14, VF14 NOP + ITOF0.xyzw VF15, VF15 NOP + ITOF0.xyzw VF16, VF16 NOP + MULi.xyz VF17, VF17, I LOI 0.0078125 ; 1.0/128.0 + MULi.w VF17, VF17, I NOP + MULi.xyzw VF14, VF14, I NOP + MULi.xyzw VF15, VF15, I NOP + MULi.xyzw VF16, VF16, I NOP +; fall through + +; C28 +; VF12: sphere +; VF14-16: verts +; VF17: plane +; out: +; VF01: intersection point +; VF02: triangle normal +; VF03 x: intersection parameter +.globl Vu0SphereToTriangleCollisionStart +Vu0SphereToTriangleCollisionStart: + MUL.xyz VF02, VF12, VF17 LOI 0.1 + ADD.xyz VF17, VF17, VF00 NOP + ADDw.x VF13, VF00, VF12 NOP + NOP NOP + MULAx.w ACC, VF00, VF02 IADDIU VI06, VI00, 0xE0 + MADDAy.w ACC, VF00, VF02 FMAND VI05, VI06 ; normal sign flags + MADDAz.w ACC, VF00, VF02 NOP + MSUB.w VF02, VF00, VF17 NOP ; center plane pos + MULi.w VF03, VF00, I MOVE.xyzw VF04, VF03 + NOP NOP + NOP NOP + CLIPw.xyz VF13, VF02 NOP ; compare dist and radius + CLIPw.xyz VF17, VF03 NOP + MULAw.xyz ACC, VF12, VF00 IADDIU VI07, VI00, 0x0 ; -- clear test case + MSUBw.xyz VF01, VF17, VF02 NOP + MULx.w VF01, VF00, VF00 FCAND VI01, 0x3 ; projected center on plane + ABS.w VF02, VF02 IBEQ VI00, VI01, QuitAndFail ; no intersection + NOP NOP + NOP FCAND VI01, 0x3 ; -- check x direction + SUB.xyz VF02, VF12, VF01 IADDIU VI02, VI01, 0x7F + NOP IADDIU VI06, VI00, 0x80 + SUB.xyz VF05, VF15, VF14 IAND VI02, VI02, VI06 + SUB.xyz VF08, VF01, VF14 FCAND VI01, 0xC ; -- check y direction + SUB.xyz VF06, VF16, VF15 IADDIU VI03, VI01, 0x3F + SUB.xyz VF09, VF01, VF15 IADDIU VI06, VI00, 0x40 + SUB.xyz VF07, VF14, VF16 IAND VI03, VI03, VI06 + SUB.xyz VF10, VF01, VF16 FCAND VI01, 0x30 ; -- check z direction + MUL.xyz VF03, VF02, VF02 IADDIU VI04, VI01, 0x1F + OPMULA.xyz ACC, VF08, VF05 IADDIU VI06, VI00, 0x20 + OPMSUB.xyz VF18, VF05, VF08 IAND VI04, VI04, VI06 + OPMULA.xyz ACC, VF09, VF06 NOP + OPMSUB.xyz VF19, VF06, VF09 IADD VI06, VI02, VI03 + OPMULA.xyz ACC, VF10, VF07 IADD VI06, VI06, VI04 ; -- combine flags + OPMSUB.xyz VF20, VF07, VF10 FMAND VI02, VI06 ; -- cross 1 flags + MULAx.w ACC, VF00, VF03 IAND VI05, VI05, VI06 + MADDAy.w ACC, VF00, VF03 FMAND VI03, VI06 ; -- cross 2 flags + MADDz.w VF03, VF00, VF03 IADDIU VI08, VI00, 0x3 + NOP FMAND VI04, VI06 ; -- cross 3 flags + NOP NOP + NOP IBNE VI02, VI05, CheckSide2 + NOP RSQRT Q, VF00w, VF03w + ADD.xyz VF04, VF00, VF16 IADDIU VI07, VI07, 0x1 ; inside side 1 +CheckSide2: + NOP IBNE VI03, VI05, CheckSide3 + NOP NOP + ADD.xyz VF04, VF00, VF14 IADDIU VI07, VI07, 0x1 ; inside side 2 +CheckSide3: + NOP IBNE VI04, VI05, FinishCheckingSides + NOP NOP + ADD.xyz VF04, VF00, VF15 IADDIU VI07, VI07, 0x1 ; inside side 3 + NOP NOP + NOP IBEQ VI07, VI08, TotallyInsideTriangle + NOP NOP +FinishCheckingSides: + MUL.x VF13, VF13, VF13 IADDIU VI08, VI00, 0x2 + MULq.xyz VF02, VF02, Q WAITQ + NOP IBNE VI07, VI08, IntersectionOutsideTwoSides + NOP NOP + NOP IBEQ VI02, VI05, CheckDistanceSide2 + NOP NOP + NOP MOVE.xyzw VF15, VF05 + NOP BAL VI15, DistanceBetweenSphereAndLine + NOP NOP + NOP B ProcessLineResult + NOP NOP +CheckDistanceSide2: + NOP IBEQ VI03, VI05, CheckDistanceSide3 + NOP NOP + NOP MOVE.xyzw VF14, VF15 + NOP MOVE.xyzw VF15, VF06 + NOP BAL VI15, DistanceBetweenSphereAndLine + NOP NOP + NOP B ProcessLineResult + NOP NOP +CheckDistanceSide3: + NOP MOVE.xyzw VF14, VF16 + NOP MOVE.xyzw VF15, VF07 + NOP BAL VI15, DistanceBetweenSphereAndLine + NOP NOP + NOP B ProcessLineResult + NOP NOP +IntersectionOutsideTwoSides: + SUB.xyz VF05, VF04, VF12 NOP + ADD.xyz VF01, VF00, VF04 NOP ; col point + SUB.xyz VF02, VF12, VF04 NOP + NOP NOP + MUL.xyz VF05, VF05, VF05 NOP + NOP NOP + NOP NOP + NOP NOP + MULAx.w ACC, VF00, VF05 NOP + MADDAy.w ACC, VF00, VF05 NOP + MADDz.w VF05, VF00, VF05 NOP ; distSq to vertex + NOP NOP + NOP NOP + NOP NOP + CLIPw.xyz VF13, VF05 SQRT Q, VF05w ; compare radiusSq and distSq + NOP NOP + NOP NOP + NOP NOP + NOP FCAND VI01, 0x1 + ADDq.x VF03, VF00, Q WAITQ ; dist to vertex + NOP IBEQ VI00, VI01, QuitAndFail ; too far + NOP NOP + NOP NOP + NOP DIV Q, VF00w, VF03x + MULq.xyz VF02, VF02, Q WAITQ ; col normal + NOP[E] NOP + NOP NOP +TotallyInsideTriangle: + ADDw.x VF03, VF00, VF02 WAITQ + MULq.xyz VF02, VF02, Q NOP + NOP[E] IADDIU VI01, VI00, 0x1 + NOP NOP +ProcessLineResult: + CLIPw.xyz VF13, VF16 SQRT Q, VF16w + ADD.xyz VF01, VF00, VF16 NOP + SUB.xyz VF02, VF12, VF16 NOP + NOP NOP + NOP FCAND VI01, 0x1 + ADDq.x VF03, VF00, Q WAITQ + NOP IBEQ VI00, VI01, QuitAndFail + NOP NOP + NOP NOP + NOP DIV Q, VF00w, VF03x + MULq.xyz VF02, VF02, Q WAITQ + NOP[E] NOP + NOP NOP + +EndOfMicrocode: diff --git a/src/miami/collision/vu0Collision_2.s b/src/miami/collision/vu0Collision_2.s new file mode 100644 index 00000000..716c29ac --- /dev/null +++ b/src/miami/collision/vu0Collision_2.s @@ -0,0 +1,191 @@ +QuitAndFail2: + NOP[E] IADDIU VI01, VI00, 0x0 + NOP NOP + + +QuitAndSucceed2: + NOP[E] IADDIU VI01, VI00, 0x1 + NOP NOP + + +; 20 +GetBBVertices: + MULw.xy VF02, VF01, VF00 NOP + MUL.z VF02, VF01, VF11 NOP + MULw.xz VF03, VF01, VF00 NOP + MUL.y VF03, VF01, VF11 NOP + MULw.x VF04, VF01, VF00 NOP + MUL.yz VF04, VF01, VF11 NOP + NOP JR VI15 + NOP NOP + + +; 60 +Vu0OBBToOBBCollision: + SUBw.xyz VF11, VF00, VF00 LOI 0.5 + MULi.xyz VF12, VF12, I NOP + MULi.xyz VF13, VF13, I NOP + NOP NOP + NOP NOP + NOP MOVE.xyz VF01, VF12 + NOP BAL VI15, GetBBVertices + NOP NOP + MULAx.xyz ACC, VF14, VF01 NOP + MADDAy.xyz ACC, VF15, VF01 NOP + MADDz.xyz VF01, VF16, VF01 NOP + MULAx.xyz ACC, VF14, VF02 NOP + MADDAy.xyz ACC, VF15, VF02 NOP + MADDz.xyz VF02, VF16, VF02 NOP + MULAx.xyz ACC, VF14, VF03 NOP + MADDAy.xyz ACC, VF15, VF03 NOP + MADDz.xyz VF03, VF16, VF03 NOP + MULAx.xyz ACC, VF14, VF04 NOP + MADDAy.xyz ACC, VF15, VF04 NOP + MADDz.xyz VF04, VF16, VF04 NOP + ABS.xyz VF05, VF01 NOP + ABS.xyz VF06, VF02 NOP + ABS.xyz VF07, VF03 NOP + ABS.xyz VF08, VF04 NOP + NOP NOP + MAX.xyz VF05, VF05, VF06 NOP + NOP NOP + MAX.xyz VF07, VF07, VF08 NOP + NOP NOP + NOP NOP + NOP NOP + MAX.xyz VF05, VF05, VF07 NOP + NOP NOP + NOP NOP + NOP NOP + ADD.xyz VF09, VF05, VF13 NOP + NOP NOP + NOP NOP + NOP NOP + MULx.w VF05, VF00, VF09 NOP + MULy.w VF06, VF00, VF09 NOP + MULz.w VF07, VF00, VF09 NOP + CLIPw.xyz VF17, VF05 NOP + CLIPw.xyz VF17, VF06 NOP + CLIPw.xyz VF17, VF07 MOVE.xyz VF01, VF13 + NOP NOP + NOP NOP + NOP NOP + NOP FCAND VI01, 0x3330 + NOP IBNE VI01, VI00, QuitAndFail2 + NOP NOP + NOP BAL VI15, GetBBVertices + NOP NOP + MULAx.xyz ACC, VF18, VF01 NOP + MADDAy.xyz ACC, VF19, VF01 NOP + MADDz.xyz VF01, VF20, VF01 NOP + MULAx.xyz ACC, VF18, VF02 NOP + MADDAy.xyz ACC, VF19, VF02 NOP + MADDz.xyz VF02, VF20, VF02 NOP + MULAx.xyz ACC, VF18, VF03 NOP + MADDAy.xyz ACC, VF19, VF03 NOP + MADDz.xyz VF03, VF20, VF03 NOP + MULAx.xyz ACC, VF18, VF04 NOP + MADDAy.xyz ACC, VF19, VF04 NOP + MADDz.xyz VF04, VF20, VF04 NOP + ABS.xyz VF05, VF01 NOP + ABS.xyz VF06, VF02 NOP + ABS.xyz VF07, VF03 NOP + ABS.xyz VF08, VF04 NOP + NOP NOP + MAX.xyz VF05, VF05, VF06 NOP + NOP NOP + MAX.xyz VF07, VF07, VF08 NOP + NOP NOP + NOP NOP + NOP NOP + MAX.xyz VF05, VF05, VF07 NOP + NOP NOP + NOP NOP + NOP NOP + ADD.xyz VF09, VF05, VF12 NOP + NOP NOP + NOP NOP + NOP NOP + MULx.w VF05, VF00, VF09 NOP + MULy.w VF06, VF00, VF09 NOP + MULz.w VF07, VF00, VF09 NOP + CLIPw.xyz VF21, VF05 NOP + CLIPw.xyz VF21, VF06 NOP + CLIPw.xyz VF21, VF07 NOP + NOP NOP + NOP NOP + NOP NOP + NOP FCAND VI01, 0x3330 + NOP IBNE VI01, VI00, QuitAndFail2 + NOP NOP + SUB.xyz VF06, VF02, VF01 NOP + SUB.xyz VF07, VF03, VF01 NOP + ADD.xyz VF08, VF04, VF01 NOP + ADD.x VF09, VF00, VF12 NOP + ADD.yz VF09, VF00, VF00 NOP + ADD.y VF10, VF00, VF12 NOP + ADD.xz VF10, VF00, VF00 NOP + ADD.z VF11, VF00, VF12 IADDI VI04, VI00, 0x0 + ADD.xy VF11, VF00, VF00 IADD VI02, VI00, VI00 + OPMULA.xyz ACC, VF06, VF09 NOP + OPMSUB.xyz VF01, VF09, VF06 NOP + OPMULA.xyz ACC, VF06, VF10 NOP + OPMSUB.xyz VF02, VF10, VF06 NOP + OPMULA.xyz ACC, VF06, VF11 NOP + OPMSUB.xyz VF03, VF11, VF06 SQI.xyzw VF01, (VI02++) + OPMULA.xyz ACC, VF07, VF09 NOP + OPMSUB.xyz VF01, VF09, VF07 SQI.xyzw VF02, (VI02++) + OPMULA.xyz ACC, VF07, VF10 NOP + OPMSUB.xyz VF02, VF10, VF07 SQI.xyzw VF03, (VI02++) + OPMULA.xyz ACC, VF07, VF11 NOP + OPMSUB.xyz VF03, VF11, VF07 SQI.xyzw VF01, (VI02++) + OPMULA.xyz ACC, VF08, VF09 NOP + OPMSUB.xyz VF01, VF09, VF08 SQI.xyzw VF02, (VI02++) + OPMULA.xyz ACC, VF08, VF10 NOP + OPMSUB.xyz VF02, VF10, VF08 SQI.xyzw VF03, (VI02++) + OPMULA.xyz ACC, VF08, VF11 LOI 0.5 + OPMSUB.xyz VF01, VF11, VF08 SQI.xyzw VF01, (VI02++) + MULi.xyz VF06, VF06, I NOP + MULi.xyz VF07, VF07, I SQI.xyzw VF02, (VI02++) + MULi.xyz VF08, VF08, I NOP + MUL.xyz VF02, VF21, VF01 NOP + MUL.xyz VF03, VF12, VF01 NOP + MUL.xyz VF09, VF06, VF01 NOP + MUL.xyz VF10, VF07, VF01 NOP + MUL.xyz VF11, VF08, VF01 NOP + ABS.xyz VF03, VF03 NOP + ADDy.x VF05, VF09, VF09 NOP + ADDx.y VF05, VF10, VF10 NOP + ADDx.z VF05, VF11, VF11 NOP + NOP NOP +EdgePairLoop: + ADDz.x VF05, VF05, VF09 NOP + ADDz.y VF05, VF05, VF10 NOP + ADDy.z VF05, VF05, VF11 NOP + MULAx.w ACC, VF00, VF02 IADD VI03, VI02, VI00 + MADDAy.w ACC, VF00, VF02 LQD.xyzw VF01, (--VI02) + MADDz.w VF02, VF00, VF02 NOP + ABS.xyz VF05, VF05 NOP + MULAx.w ACC, VF00, VF03 NOP + MADDAy.w ACC, VF00, VF03 NOP + MADDAz.w ACC, VF00, VF03 NOP + MADDAx.w ACC, VF00, VF05 NOP + MADDAy.w ACC, VF00, VF05 NOP + MADDz.w VF03, VF00, VF05 NOP + ADDw.x VF04, VF00, VF02 NOP + MUL.xyz VF02, VF21, VF01 NOP + MUL.xyz VF03, VF12, VF01 NOP + MUL.xyz VF09, VF06, VF01 NOP + CLIPw.xyz VF04, VF03 NOP + MUL.xyz VF10, VF07, VF01 NOP + MUL.xyz VF11, VF08, VF01 NOP + ABS.xyz VF03, VF03 NOP + ADDy.x VF05, VF09, VF09 FCAND VI01, 0x3 + ADDx.y VF05, VF10, VF10 IBNE VI01, VI00, QuitAndFail2 + ADDx.z VF05, VF11, VF11 NOP + NOP IBNE VI03, VI00, EdgePairLoop + NOP NOP + NOP[E] IADDIU VI01, VI00, 0x1 + NOP NOP + +EndOfMicrocode2: diff --git a/src/miami/control/AutoPilot.cpp b/src/miami/control/AutoPilot.cpp new file mode 100644 index 00000000..d7c17a68 --- /dev/null +++ b/src/miami/control/AutoPilot.cpp @@ -0,0 +1,136 @@ +#include "common.h" + +#include "AutoPilot.h" + +#include "CarCtrl.h" +#include "Curves.h" +#include "PathFind.h" +#include "SaveBuf.h" + +void CAutoPilot::ModifySpeed(float speed) +{ + m_fMaxTrafficSpeed = Max(0.01f, speed); + float positionBetweenNodes = (float)(CTimer::GetTimeInMilliseconds() - m_nTimeEnteredCurve) / m_nTimeToSpendOnCurrentCurve; + CCarPathLink* pCurrentLink = &ThePaths.m_carPathLinks[m_nCurrentPathNodeInfo]; + CCarPathLink* pNextLink = &ThePaths.m_carPathLinks[m_nNextPathNodeInfo]; + float currentPathLinkForwardX = m_nCurrentDirection * ThePaths.m_carPathLinks[m_nCurrentPathNodeInfo].GetDirX(); + float currentPathLinkForwardY = m_nCurrentDirection * ThePaths.m_carPathLinks[m_nCurrentPathNodeInfo].GetDirY(); + float nextPathLinkForwardX = m_nNextDirection * ThePaths.m_carPathLinks[m_nNextPathNodeInfo].GetDirX(); + float nextPathLinkForwardY = m_nNextDirection * ThePaths.m_carPathLinks[m_nNextPathNodeInfo].GetDirY(); + CVector positionOnCurrentLinkIncludingLane( + pCurrentLink->GetX() + ((m_nCurrentLane + 0.5f) * LANE_WIDTH) * currentPathLinkForwardY, + pCurrentLink->GetY() - ((m_nCurrentLane + 0.5f) * LANE_WIDTH) * currentPathLinkForwardX, + 0.0f); + CVector positionOnNextLinkIncludingLane( + pNextLink->GetX() + ((m_nNextLane + 0.5f) * LANE_WIDTH) * nextPathLinkForwardY, + pNextLink->GetY() - ((m_nNextLane + 0.5f) * LANE_WIDTH) * nextPathLinkForwardX, + 0.0f); + m_nTimeToSpendOnCurrentCurve = CCurves::CalcSpeedScaleFactor( + &positionOnCurrentLinkIncludingLane, + &positionOnNextLinkIncludingLane, + currentPathLinkForwardX, currentPathLinkForwardY, + nextPathLinkForwardX, nextPathLinkForwardY + ) * (1000.0f / m_fMaxTrafficSpeed); +#ifdef FIX_BUGS + /* Casting timer to float is very unwanted, and in this case even causes crashes. */ + m_nTimeEnteredCurve = CTimer::GetTimeInMilliseconds() - + (uint32)(positionBetweenNodes * m_nTimeToSpendOnCurrentCurve); +#else + m_nTimeEnteredCurve = CTimer::GetTimeInMilliseconds() - positionBetweenNodes * m_nTimeToSpendOnCurrentCurve; +#endif +} + +void CAutoPilot::RemoveOnePathNode() +{ + --m_nPathFindNodesCount; + for (int i = 0; i < m_nPathFindNodesCount; i++) + m_aPathFindNodesInfo[i] = m_aPathFindNodesInfo[i + 1]; +} + +#ifdef COMPATIBLE_SAVES +void CAutoPilot::Save(uint8*& buf) +{ + WriteSaveBuf(buf, m_nCurrentRouteNode); + WriteSaveBuf(buf, m_nNextRouteNode); + WriteSaveBuf(buf, m_nPrevRouteNode); + WriteSaveBuf(buf, m_nTimeEnteredCurve); + WriteSaveBuf(buf, m_nTimeToSpendOnCurrentCurve); + WriteSaveBuf(buf, m_nCurrentPathNodeInfo); + WriteSaveBuf(buf, m_nNextPathNodeInfo); + WriteSaveBuf(buf, m_nPreviousPathNodeInfo); + WriteSaveBuf(buf, m_nAntiReverseTimer); + WriteSaveBuf(buf, m_nTimeToStartMission); + WriteSaveBuf(buf, m_nPreviousDirection); + WriteSaveBuf(buf, m_nCurrentDirection); + WriteSaveBuf(buf, m_nNextDirection); + WriteSaveBuf(buf, m_nCurrentLane); + WriteSaveBuf(buf, m_nNextLane); + WriteSaveBuf(buf, m_nDrivingStyle); + WriteSaveBuf(buf, m_nCarMission); + WriteSaveBuf(buf, m_nTempAction); + WriteSaveBuf(buf, m_nTimeTempAction); + WriteSaveBuf(buf, m_fMaxTrafficSpeed); + WriteSaveBuf(buf, m_nCruiseSpeed); + WriteSaveBuf(buf, m_nCruiseSpeedMultiplierType); + ZeroSaveBuf(buf, 2); + WriteSaveBuf(buf, m_fCruiseSpeedMultiplier); + uint8 flags = 0; + if (m_bSlowedDownBecauseOfCars) flags |= BIT(0); + if (m_bSlowedDownBecauseOfPeds) flags |= BIT(1); + if (m_bStayInCurrentLevel) flags |= BIT(2); + if (m_bStayInFastLane) flags |= BIT(3); + if (m_bIgnorePathfinding) flags |= BIT(4); + WriteSaveBuf(buf, flags); + WriteSaveBuf(buf, m_nSwitchDistance); + ZeroSaveBuf(buf, 2); + WriteSaveBuf(buf, m_vecDestinationCoors.x); + WriteSaveBuf(buf, m_vecDestinationCoors.y); + WriteSaveBuf(buf, m_vecDestinationCoors.z); + ZeroSaveBuf(buf, 32); + WriteSaveBuf(buf, m_nPathFindNodesCount); + ZeroSaveBuf(buf, 6); +} + +void CAutoPilot::Load(uint8*& buf) +{ + ReadSaveBuf(&m_nCurrentRouteNode, buf); + ReadSaveBuf(&m_nNextRouteNode, buf); + ReadSaveBuf(&m_nPrevRouteNode, buf); + ReadSaveBuf(&m_nTimeEnteredCurve, buf); + ReadSaveBuf(&m_nTimeToSpendOnCurrentCurve, buf); + ReadSaveBuf(&m_nCurrentPathNodeInfo, buf); + ReadSaveBuf(&m_nNextPathNodeInfo, buf); + ReadSaveBuf(&m_nPreviousPathNodeInfo, buf); + ReadSaveBuf(&m_nAntiReverseTimer, buf); + ReadSaveBuf(&m_nTimeToStartMission, buf); + ReadSaveBuf(&m_nPreviousDirection, buf); + ReadSaveBuf(&m_nCurrentDirection, buf); + ReadSaveBuf(&m_nNextDirection, buf); + ReadSaveBuf(&m_nCurrentLane, buf); + ReadSaveBuf(&m_nNextLane, buf); + ReadSaveBuf(&m_nDrivingStyle, buf); + ReadSaveBuf(&m_nCarMission, buf); + ReadSaveBuf(&m_nTempAction, buf); + ReadSaveBuf(&m_nTimeTempAction, buf); + ReadSaveBuf(&m_fMaxTrafficSpeed, buf); + ReadSaveBuf(&m_nCruiseSpeed, buf); + ReadSaveBuf(&m_nCruiseSpeedMultiplierType, buf); + SkipSaveBuf(buf, 2); + ReadSaveBuf(&m_fCruiseSpeedMultiplier, buf); + uint8 flags; + ReadSaveBuf(&flags, buf); + m_bSlowedDownBecauseOfCars = !!(flags & BIT(0)); + m_bSlowedDownBecauseOfPeds = !!(flags & BIT(1)); + m_bStayInCurrentLevel = !!(flags & BIT(2)); + m_bStayInFastLane = !!(flags & BIT(3)); + m_bIgnorePathfinding = !!(flags & BIT(4)); + ReadSaveBuf(&m_nSwitchDistance, buf); + SkipSaveBuf(buf, 2); + ReadSaveBuf(&m_vecDestinationCoors.x, buf); + ReadSaveBuf(&m_vecDestinationCoors.y, buf); + ReadSaveBuf(&m_vecDestinationCoors.z, buf); + SkipSaveBuf(buf, 32); + ReadSaveBuf(&m_nPathFindNodesCount, buf); + SkipSaveBuf(buf, 6); +} +#endif \ No newline at end of file diff --git a/src/miami/control/AutoPilot.h b/src/miami/control/AutoPilot.h new file mode 100644 index 00000000..ec3bb8d8 --- /dev/null +++ b/src/miami/control/AutoPilot.h @@ -0,0 +1,137 @@ +#pragma once +#include "Timer.h" + +class CVehicle; +struct CPathNode; + +enum eCarMission +{ + MISSION_NONE, + MISSION_CRUISE, + MISSION_RAMPLAYER_FARAWAY, + MISSION_RAMPLAYER_CLOSE, + MISSION_BLOCKPLAYER_FARAWAY, + MISSION_BLOCKPLAYER_CLOSE, + MISSION_BLOCKPLAYER_HANDBRAKESTOP, + MISSION_WAITFORDELETION, + MISSION_GOTOCOORDS, + MISSION_GOTOCOORDS_STRAIGHT, + MISSION_EMERGENCYVEHICLE_STOP, + MISSION_STOP_FOREVER, + MISSION_GOTOCOORDS_ACCURATE, + MISSION_GOTO_COORDS_STRAIGHT_ACCURATE, + MISSION_GOTOCOORDS_ASTHECROWSWIMS, + MISSION_RAMCAR_FARAWAY, + MISSION_RAMCAR_CLOSE, + MISSION_BLOCKCAR_FARAWAY, + MISSION_BLOCKCAR_CLOSE, + MISSION_BLOCKCAR_HANDBRAKESTOP, + MISSION_HELI_FLYTOCOORS, + MISSION_ATTACKPLAYER, + MISSION_PLANE_FLYTOCOORS, + MISSION_HELI_LAND, + MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_1, + MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_2, + MISSION_BLOCKPLAYER_FORWARDANDBACK +}; + +enum eCarTempAction +{ + TEMPACT_NONE, + TEMPACT_WAIT, + TEMPACT_REVERSE, + TEMPACT_HANDBRAKETURNLEFT, + TEMPACT_HANDBRAKETURNRIGHT, + TEMPACT_HANDBRAKESTRAIGHT, + TEMPACT_TURNLEFT, + TEMPACT_TURNRIGHT, + TEMPACT_GOFORWARD, + TEMPACT_SWERVELEFT, + TEMPACT_SWERVERIGHT +}; + +enum eCarDrivingStyle +{ + DRIVINGSTYLE_STOP_FOR_CARS, + DRIVINGSTYLE_SLOW_DOWN_FOR_CARS, + DRIVINGSTYLE_AVOID_CARS, + DRIVINGSTYLE_PLOUGH_THROUGH, + DRIVINGSTYLE_STOP_FOR_CARS_IGNORE_LIGHTS +}; + +class CAutoPilot { +public: + int32 m_nCurrentRouteNode; + int32 m_nNextRouteNode; + int32 m_nPrevRouteNode; + int32 m_nTimeEnteredCurve; + int32 m_nTimeToSpendOnCurrentCurve; + uint32 m_nCurrentPathNodeInfo; + uint32 m_nNextPathNodeInfo; + uint32 m_nPreviousPathNodeInfo; + uint32 m_nAntiReverseTimer; + uint32 m_nTimeToStartMission; + int8 m_nPreviousDirection; + int8 m_nCurrentDirection; + int8 m_nNextDirection; + int8 m_nCurrentLane; + int8 m_nNextLane; + uint8 m_nDrivingStyle; + uint8 m_nCarMission; + uint8 m_nTempAction; + uint32 m_nTimeTempAction; + float m_fMaxTrafficSpeed; + uint8 m_nCruiseSpeed; + uint8 m_nCruiseSpeedMultiplierType; + float m_fCruiseSpeedMultiplier; + uint8 m_bSlowedDownBecauseOfCars : 1; + uint8 m_bSlowedDownBecauseOfPeds : 1; + uint8 m_bStayInCurrentLevel : 1; + uint8 m_bStayInFastLane : 1; + uint8 m_bIgnorePathfinding : 1; + uint8 m_nSwitchDistance; + CVector m_vecDestinationCoors; + CPathNode *m_aPathFindNodesInfo[NUM_PATH_NODES_IN_AUTOPILOT]; + int16 m_nPathFindNodesCount; + CVehicle *m_pTargetCar; + + CAutoPilot(void) { + m_nPrevRouteNode = 0; + m_nNextRouteNode = m_nPrevRouteNode; + m_nCurrentRouteNode = m_nNextRouteNode; + m_nTimeEnteredCurve = 0; + m_nTimeToSpendOnCurrentCurve = 1000; + m_nPreviousPathNodeInfo = 0; + m_nNextPathNodeInfo = m_nPreviousPathNodeInfo; + m_nCurrentPathNodeInfo = m_nNextPathNodeInfo; + m_nNextDirection = 1; + m_nCurrentDirection = m_nNextDirection; + m_nCurrentLane = m_nNextLane = 0; + m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; + m_nCarMission = MISSION_NONE; + m_nTempAction = TEMPACT_NONE; + m_nCruiseSpeed = 10; + m_fMaxTrafficSpeed = 10.0f; + m_bSlowedDownBecauseOfPeds = false; + m_bSlowedDownBecauseOfCars = false; + m_nPathFindNodesCount = 0; + m_pTargetCar = 0; + m_nTimeToStartMission = CTimer::GetTimeInMilliseconds(); + m_nAntiReverseTimer = m_nTimeToStartMission; + m_bStayInFastLane = false; + m_nCruiseSpeedMultiplierType = 0; + m_fCruiseSpeedMultiplier = 1.0f; + } + + void ModifySpeed(float); + void RemoveOnePathNode(); +#ifdef COMPATIBLE_SAVES + void Save(uint8*& buf); + void Load(uint8*& buf); +#endif + + float GetCruiseSpeed(void) { return m_nCruiseSpeed * m_fCruiseSpeedMultiplier; } + +}; + +VALIDATE_SIZE(CAutoPilot, 0x70); diff --git a/src/miami/control/Bridge.cpp b/src/miami/control/Bridge.cpp new file mode 100644 index 00000000..1e63cf30 --- /dev/null +++ b/src/miami/control/Bridge.cpp @@ -0,0 +1,163 @@ +#include "common.h" + +#include "Bridge.h" +#include "Pools.h" +#include "ModelIndices.h" +#include "PathFind.h" +#include "Stats.h" + +CEntity *CBridge::pLiftRoad; +CEntity *CBridge::pLiftPart; +CEntity *CBridge::pWeight; + +int CBridge::State; +int CBridge::OldState; + +float CBridge::DefaultZLiftPart; +float CBridge::DefaultZLiftRoad; +float CBridge::DefaultZLiftWeight; + +float CBridge::OldLift; + +uint32 CBridge::TimeOfBridgeBecomingOperational; + +void CBridge::Init() +{ +#ifdef GTA_BRIDGE + FindBridgeEntities(); + OldLift = -1.0f; + if (pLiftPart && pWeight) + { + DefaultZLiftPart = pLiftPart->GetPosition().z; + DefaultZLiftWeight = pWeight->GetPosition().z; + + if (pLiftRoad) + DefaultZLiftRoad = pLiftRoad->GetPosition().z; + + ThePaths.SetLinksBridgeLights(-330.0, -230.0, -700.0, -588.0, true); + } +#endif +} + +void CBridge::Update() +{ +#ifdef GTA_BRIDGE + if (!pLiftPart || !pWeight) + return; + + OldState = State; + + float liftHeight; + + // Set bridge height and state + if (CStats::CommercialPassed) + { + if (TimeOfBridgeBecomingOperational == 0) + TimeOfBridgeBecomingOperational = CTimer::GetTimeInMilliseconds(); + + // Time remaining for bridge to become operational + // uint16, so after about a minute it overflows to 0 and the cycle repeats + uint16 timeElapsed = CTimer::GetTimeInMilliseconds() - TimeOfBridgeBecomingOperational; + + // Calculate lift part height and bridge state + if (timeElapsed < 10000) + { + State = STATE_LIFT_PART_MOVING_DOWN; + liftHeight = 25.0f - timeElapsed / 10000.0f * 25.0f; + } + else if (timeElapsed < 40000) + { + liftHeight = 0.0f; + State = STATE_LIFT_PART_IS_DOWN; + } + else if (timeElapsed < 50000) + { + liftHeight = 0.0f; + State = STATE_LIFT_PART_ABOUT_TO_MOVE_UP; + } + else if (timeElapsed < 60000) + { + State = STATE_LIFT_PART_MOVING_UP; + liftHeight = (timeElapsed - 50000) / 10000.0f * 25.0f; + } + else + { + liftHeight = 25.0f; + State = STATE_LIFT_PART_IS_UP; + } + } + else + { + liftHeight = 25.0f; + TimeOfBridgeBecomingOperational = 0; + State = STATE_BRIDGE_LOCKED; + } + + // Move bridge part + if (liftHeight != OldLift) + { + pLiftPart->GetMatrix().GetPosition().z = DefaultZLiftPart + liftHeight; + pLiftPart->GetMatrix().UpdateRW(); + pLiftPart->UpdateRwFrame(); + if (pLiftRoad) + { + pLiftRoad->GetMatrix().GetPosition().z = DefaultZLiftRoad + liftHeight; + pLiftRoad->GetMatrix().UpdateRW(); + pLiftRoad->UpdateRwFrame(); + } + pWeight->GetMatrix().GetPosition().z = DefaultZLiftWeight - liftHeight; + pWeight->GetMatrix().UpdateRW(); + pWeight->UpdateRwFrame(); + + OldLift = liftHeight; + } + + if (State == STATE_LIFT_PART_ABOUT_TO_MOVE_UP && OldState == STATE_LIFT_PART_IS_DOWN) + ThePaths.SetLinksBridgeLights(-330.0, -230.0, -700.0, -588.0, true); + else if (State == STATE_LIFT_PART_IS_DOWN && OldState == STATE_LIFT_PART_MOVING_DOWN) + ThePaths.SetLinksBridgeLights(-330.0, -230.0, -700.0, -588.0, false); +#endif +} + +bool CBridge::ShouldLightsBeFlashing() +{ +#ifdef GTA_BRIDGE + return State != STATE_LIFT_PART_IS_DOWN; +#else + return false; +#endif +} + +void CBridge::FindBridgeEntities() +{ +#ifdef GTA_BRIDGE + pWeight = nil; + pLiftRoad = nil; + pLiftPart = nil; + + for (int i = CPools::GetBuildingPool()->GetSize()-1; i >= 0; i--) { + CBuilding* entry = CPools::GetBuildingPool()->GetSlot(i); + if (entry) + { + if (entry->GetModelIndex() == MI_BRIDGELIFT) + pLiftPart = entry; + else if (entry->GetModelIndex() == MI_BRIDGEROADSEGMENT) + pLiftRoad = entry; + else if (entry->GetModelIndex() == MI_BRIDGEWEIGHT) + pWeight = entry; + } + } +#endif +} + +bool CBridge::ThisIsABridgeObjectMovingUp(int index) +{ +#ifdef GTA_BRIDGE + if (index != MI_BRIDGEROADSEGMENT && index != MI_BRIDGELIFT) + return false; + + return State == STATE_LIFT_PART_ABOUT_TO_MOVE_UP || State == STATE_LIFT_PART_MOVING_UP; +#else + return false; +#endif +} diff --git a/src/miami/control/Bridge.h b/src/miami/control/Bridge.h new file mode 100644 index 00000000..c5702629 --- /dev/null +++ b/src/miami/control/Bridge.h @@ -0,0 +1,28 @@ +#pragma once + +class CEntity; + +enum bridgeStates { + STATE_BRIDGE_LOCKED, + STATE_LIFT_PART_IS_UP, + STATE_LIFT_PART_MOVING_DOWN, + STATE_LIFT_PART_IS_DOWN, + STATE_LIFT_PART_ABOUT_TO_MOVE_UP, + STATE_LIFT_PART_MOVING_UP +}; + +class CBridge +{ +public: + static CEntity *pLiftRoad, *pLiftPart, *pWeight; + static int State, OldState; + static float DefaultZLiftPart, DefaultZLiftRoad, DefaultZLiftWeight; + static float OldLift; + static uint32 TimeOfBridgeBecomingOperational; + + static void Init(); + static void Update(); + static bool ShouldLightsBeFlashing(); + static void FindBridgeEntities(); + static bool ThisIsABridgeObjectMovingUp(int); +}; diff --git a/src/miami/control/CarAI.cpp b/src/miami/control/CarAI.cpp new file mode 100644 index 00000000..c8230d4d --- /dev/null +++ b/src/miami/control/CarAI.cpp @@ -0,0 +1,802 @@ +#include "common.h" + +#include "CarAI.h" + +#include "Accident.h" +#include "AutoPilot.h" +#include "CarCtrl.h" +#include "General.h" +#include "HandlingMgr.h" +#include "ModelIndices.h" +#include "PlayerPed.h" +#include "Wanted.h" +#include "DMAudio.h" +#include "Fire.h" +#include "Pools.h" +#include "Population.h" +#include "Timer.h" +#include "TrafficLights.h" +#include "Vehicle.h" +#include "World.h" +#include "ZoneCull.h" + +#define DISTANCE_TO_SWITCH_DISTANCE_GOTO 20.0f + +float CCarAI::FindSwitchDistanceClose(CVehicle* pVehicle) +{ + return pVehicle->AutoPilot.m_nSwitchDistance; +} + +float CCarAI::FindSwitchDistanceFarNormalVehicle(CVehicle* pVehicle) +{ + return FindSwitchDistanceClose(pVehicle) + 5.0f; +} + +float CCarAI::FindSwitchDistanceFar(CVehicle* pVehicle) +{ + if (pVehicle->bIsLawEnforcer) + return 50.0f; + return FindSwitchDistanceFarNormalVehicle(pVehicle); +} + +void CCarAI::BackToCruisingIfNoWantedLevel(CVehicle* pVehicle) +{ + if (FindPlayerPed()->m_pWanted->m_bIgnoredByEveryone || pVehicle->bIsLawEnforcer && + (FindPlayerPed()->m_pWanted->GetWantedLevel() == 0 || FindPlayerPed()->m_pWanted->m_bIgnoredByCops || CCullZones::NoPolice())) { + CCarCtrl::JoinCarWithRoadSystem(pVehicle); + pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; + pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; + pVehicle->m_bSirenOrAlarm = false; + if (CCullZones::NoPolice()) + pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; + } +} + +void CCarAI::UpdateCarAI(CVehicle* pVehicle) +{ + if (pVehicle->bIsLawEnforcer){ + if (pVehicle->AutoPilot.m_nCarMission == MISSION_BLOCKCAR_FARAWAY || + pVehicle->AutoPilot.m_nCarMission == MISSION_RAMPLAYER_FARAWAY || + pVehicle->AutoPilot.m_nCarMission == MISSION_BLOCKPLAYER_CLOSE || + pVehicle->AutoPilot.m_nCarMission == MISSION_RAMPLAYER_CLOSE) + pVehicle->AutoPilot.m_nCruiseSpeed = FindPoliceCarSpeedForWantedLevel(pVehicle); + } + switch (pVehicle->GetStatus()){ + case STATUS_PLAYER: + case STATUS_PLAYER_PLAYBACKFROMBUFFER: + case STATUS_TRAIN_MOVING: + case STATUS_TRAIN_NOT_MOVING: + case STATUS_HELI: + case STATUS_PLANE: + case STATUS_PLAYER_REMOTE: + case STATUS_PLAYER_DISABLED: + break; + case STATUS_SIMPLE: + case STATUS_PHYSICS: + switch (pVehicle->AutoPilot.m_nCarMission) { + case MISSION_RAMPLAYER_FARAWAY: + if (FindSwitchDistanceClose(pVehicle) > (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() || + pVehicle->AutoPilot.m_bIgnorePathfinding) { + pVehicle->AutoPilot.m_nCarMission = MISSION_RAMPLAYER_CLOSE; + if (pVehicle->UsesSiren()) + pVehicle->m_bSirenOrAlarm = true; + } + BackToCruisingIfNoWantedLevel(pVehicle); + break; + case MISSION_RAMPLAYER_CLOSE: + if (FindSwitchDistanceFar(pVehicle) >= (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() || + pVehicle->AutoPilot.m_bIgnorePathfinding) { + if (FindPlayerVehicle()) { + if (pVehicle->GetHasCollidedWith(FindPlayerVehicle())) { + if (pVehicle->AutoPilot.m_nTempAction != TEMPACT_TURNLEFT && pVehicle->AutoPilot.m_nTempAction != TEMPACT_TURNRIGHT) { + if (FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f) { + pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE; + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 800; + } + else { + pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE; + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 50; + } + } + } + } + if (FindPlayerVehicle() && FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f) +#ifdef FIX_BUGS + pVehicle->m_nTimeBlocked += CTimer::GetTimeStepInMilliseconds(); +#else + pVehicle->m_nTimeBlocked += 1000.0f / 60.0f * CTimer::GetTimeStep(); +#endif + else + pVehicle->m_nTimeBlocked = 0; + if (!FindPlayerVehicle() || FindPlayerVehicle()->IsUpsideDown() || + FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f && pVehicle->m_nTimeBlocked > TIME_COPS_WAIT_TO_EXIT_AFTER_STOPPING) { + if (pVehicle->bIsLawEnforcer && + (pVehicle->GetModelIndex() != MI_RHINO || pVehicle->m_randomSeed > 10000) && + (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() < 10.0f) { + TellOccupantsToLeaveCar(pVehicle); + pVehicle->AutoPilot.m_nCruiseSpeed = 0; + pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; + if (FindPlayerPed()->m_pWanted->GetWantedLevel() <= 1) + pVehicle->m_bSirenOrAlarm = false; + } + } + } + else if (!CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, FindPlayerCoors(), true)){ + pVehicle->AutoPilot.m_nCarMission = MISSION_RAMPLAYER_FARAWAY; + pVehicle->m_bSirenOrAlarm = false; + pVehicle->m_nCarHornTimer = 0; + } + if (pVehicle->bIsLawEnforcer) + MellowOutChaseSpeed(pVehicle); + BackToCruisingIfNoWantedLevel(pVehicle); + break; + case MISSION_BLOCKPLAYER_FARAWAY: + if (FindSwitchDistanceClose(pVehicle) > (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() || + pVehicle->AutoPilot.m_bIgnorePathfinding) { + pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKPLAYER_CLOSE; + if (pVehicle->UsesSiren()) + pVehicle->m_bSirenOrAlarm = true; + } + BackToCruisingIfNoWantedLevel(pVehicle); + break; + case MISSION_BLOCKPLAYER_CLOSE: + if (FindSwitchDistanceFar(pVehicle) >= (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() || + pVehicle->AutoPilot.m_bIgnorePathfinding) { + if (FindPlayerVehicle() && FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.04f) +#ifdef FIX_BUGS + pVehicle->m_nTimeBlocked += CTimer::GetTimeStepInMilliseconds(); +#else + pVehicle->m_nTimeBlocked += 1000.0f / 60.0f * CTimer::GetTimeStep(); +#endif + else + pVehicle->m_nTimeBlocked = 0; + if (!FindPlayerVehicle() || FindPlayerVehicle()->IsUpsideDown() || + FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.04f && pVehicle->m_nTimeBlocked > TIME_COPS_WAIT_TO_EXIT_AFTER_STOPPING) { + if (pVehicle->bIsLawEnforcer && + (pVehicle->GetModelIndex() != MI_RHINO || pVehicle->m_randomSeed > 10000) && + (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() < 10.0f) { + TellOccupantsToLeaveCar(pVehicle); + pVehicle->AutoPilot.m_nCruiseSpeed = 0; + pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; + if (FindPlayerPed()->m_pWanted->GetWantedLevel() <= 1) + pVehicle->m_bSirenOrAlarm = false; + } + } + }else if (!CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, FindPlayerCoors(), true)) { + pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKPLAYER_FARAWAY; + pVehicle->m_bSirenOrAlarm = false; + pVehicle->m_nCarHornTimer = 0; + } + if (pVehicle->bIsLawEnforcer) + MellowOutChaseSpeed(pVehicle); + BackToCruisingIfNoWantedLevel(pVehicle); + break; + case MISSION_GOTOCOORDS: + if ((pVehicle->AutoPilot.m_vecDestinationCoors - pVehicle->GetPosition()).Magnitude2D() < FindSwitchDistanceClose(pVehicle) || + pVehicle->AutoPilot.m_bIgnorePathfinding) + pVehicle->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS_STRAIGHT; + break; + case MISSION_GOTOCOORDS_STRAIGHT: + { + float distance = (pVehicle->AutoPilot.m_vecDestinationCoors - pVehicle->GetPosition()).Magnitude2D(); + if ((pVehicle->bIsAmbulanceOnDuty || pVehicle->bIsFireTruckOnDuty) && distance < 20.0f) + pVehicle->AutoPilot.m_nCarMission = MISSION_EMERGENCYVEHICLE_STOP; + if (distance < 3.0f){ + pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + if (pVehicle->bParking) { + TellOccupantsToLeaveCar(pVehicle); + pVehicle->bParking = false; + } + } + else if (distance > FindSwitchDistanceFarNormalVehicle(pVehicle) && !pVehicle->AutoPilot.m_bIgnorePathfinding && (CTimer::GetFrameCounter() & 7) == 0){ + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + pVehicle->AutoPilot.m_nCarMission = (CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, pVehicle->AutoPilot.m_vecDestinationCoors, true)) ? + MISSION_GOTOCOORDS_STRAIGHT : MISSION_GOTOCOORDS; + } + break; + } + case MISSION_EMERGENCYVEHICLE_STOP: + if (pVehicle->GetMoveSpeed().Magnitude2D() < 0.01f){ + if (pVehicle->bIsAmbulanceOnDuty){ + float distance = 30.0f; + if (gAccidentManager.FindNearestAccident(pVehicle->AutoPilot.m_vecDestinationCoors, &distance)){ + TellOccupantsToLeaveCar(pVehicle); + pVehicle->AutoPilot.m_nCarMission = MISSION_STOP_FOREVER; + }else{ + CCarCtrl::JoinCarWithRoadSystem(pVehicle); + pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; + pVehicle->m_bSirenOrAlarm = false; + pVehicle->AutoPilot.m_nCruiseSpeed = 17; + if (pVehicle->bIsAmbulanceOnDuty){ + pVehicle->bIsAmbulanceOnDuty = false; + --CCarCtrl::NumAmbulancesOnDuty; + } + } + } + if (pVehicle->bIsFireTruckOnDuty) { + float distance = 30.0f; + if (gFireManager.FindNearestFire(pVehicle->AutoPilot.m_vecDestinationCoors, &distance)) { + TellOccupantsToLeaveCar(pVehicle); + pVehicle->AutoPilot.m_nCarMission = MISSION_STOP_FOREVER; + } + else { + CCarCtrl::JoinCarWithRoadSystem(pVehicle); + pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; + pVehicle->m_bSirenOrAlarm = false; + pVehicle->AutoPilot.m_nCruiseSpeed = 17; + if (pVehicle->bIsFireTruckOnDuty) { + pVehicle->bIsFireTruckOnDuty = false; + --CCarCtrl::NumFiretrucksOnDuty; + } + } + } + } + break; + case MISSION_GOTOCOORDS_ACCURATE: + if ((pVehicle->AutoPilot.m_vecDestinationCoors - pVehicle->GetPosition()).Magnitude2D() < FindSwitchDistanceClose(pVehicle) || + pVehicle->AutoPilot.m_bIgnorePathfinding) + pVehicle->AutoPilot.m_nCarMission = MISSION_GOTO_COORDS_STRAIGHT_ACCURATE; + break; + case MISSION_GOTO_COORDS_STRAIGHT_ACCURATE: + { + float distance = (pVehicle->AutoPilot.m_vecDestinationCoors - pVehicle->GetPosition()).Magnitude2D(); + if (distance < 1.0f) { + pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + if (pVehicle->bParking) { + TellOccupantsToLeaveCar(pVehicle); + pVehicle->bParking = false; + } + } + else if (distance > FindSwitchDistanceFarNormalVehicle(pVehicle) && !pVehicle->AutoPilot.m_bIgnorePathfinding && (CTimer::GetFrameCounter() & 7) == 0) { + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + pVehicle->AutoPilot.m_nCarMission = (CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, pVehicle->AutoPilot.m_vecDestinationCoors, true)) ? + MISSION_GOTO_COORDS_STRAIGHT_ACCURATE : MISSION_GOTOCOORDS_ACCURATE; + } + break; + } + case MISSION_RAMCAR_FARAWAY: + if (pVehicle->AutoPilot.m_pTargetCar){ + if ((pVehicle->GetPosition() - pVehicle->AutoPilot.m_pTargetCar->GetPosition()).Magnitude2D() < FindSwitchDistanceClose(pVehicle) || + pVehicle->AutoPilot.m_bIgnorePathfinding) + pVehicle->AutoPilot.m_nCarMission = MISSION_RAMCAR_CLOSE; + }else{ + pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; + } + break; + case MISSION_RAMCAR_CLOSE: + if (pVehicle->AutoPilot.m_pTargetCar){ +#ifdef FIX_BUGS // btw fixed in SA + if (FindPlayerVehicle() == pVehicle->AutoPilot.m_pTargetCar) +#endif + BackToCruisingIfNoWantedLevel(pVehicle); + if ((pVehicle->AutoPilot.m_pTargetCar->GetPosition() - pVehicle->GetPosition()).Magnitude2D() <= FindSwitchDistanceFar(pVehicle) || + pVehicle->AutoPilot.m_bIgnorePathfinding){ + if (pVehicle->GetHasCollidedWith(pVehicle->AutoPilot.m_pTargetCar)){ + if (pVehicle->GetMoveSpeed().Magnitude() < 0.04f){ + pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE; + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 800; + } + } + }else{ + pVehicle->AutoPilot.m_nCarMission = MISSION_RAMCAR_FARAWAY; + CCarCtrl::JoinCarWithRoadSystem(pVehicle); + } + }else{ + pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; + } + break; + case MISSION_BLOCKCAR_FARAWAY: + if (pVehicle->AutoPilot.m_pTargetCar){ + if ((pVehicle->AutoPilot.m_pTargetCar->GetPosition() - pVehicle->GetPosition()).Magnitude2D() < FindSwitchDistanceClose(pVehicle) || + pVehicle->AutoPilot.m_bIgnorePathfinding){ + pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKCAR_CLOSE; + if (pVehicle->UsesSiren()) + pVehicle->m_bSirenOrAlarm = true; + } + }else{ + pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; + } + break; + case MISSION_BLOCKCAR_CLOSE: + if (pVehicle->AutoPilot.m_pTargetCar){ + if ((pVehicle->AutoPilot.m_pTargetCar->GetPosition() - pVehicle->GetPosition()).Magnitude2D() > FindSwitchDistanceFar(pVehicle) && + !pVehicle->AutoPilot.m_bIgnorePathfinding){ + pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKCAR_FARAWAY; + pVehicle->m_bSirenOrAlarm = false; + pVehicle->m_nCarHornTimer = 0; + CCarCtrl::JoinCarWithRoadSystem(pVehicle); + } + }else{ + pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; + } + break; + case MISSION_ATTACKPLAYER: + if (pVehicle->bIsLawEnforcer) + MellowOutChaseSpeedBoat(pVehicle); + BackToCruisingIfNoWantedLevel(pVehicle); + break; + case MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_1: + if (((CVector2D)(pVehicle->AutoPilot.m_vecDestinationCoors) - pVehicle->GetPosition()).Magnitude() < 1.5f) + pVehicle->AutoPilot.m_nCarMission = MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_2; + BackToCruisingIfNoWantedLevel(pVehicle); + break; + case MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_2: + { + float distance = ((CVector2D)FindPlayerCoors() - pVehicle->GetPosition()).Magnitude(); + if (distance < 13.0f) { + TellOccupantsToLeaveCar(pVehicle); + pVehicle->AutoPilot.m_nCruiseSpeed = 0; + pVehicle->AutoPilot.m_nCarMission = MISSION_STOP_FOREVER; + } + if (distance > 70.0f || FindPlayerPed()->m_pWanted->m_bIgnoredByEveryone || + (FindPlayerPed()->m_pWanted->GetWantedLevel() == 0 || FindPlayerPed()->m_pWanted->m_bIgnoredByCops || CCullZones::NoPolice())) { + TellOccupantsToLeaveCar(pVehicle); + pVehicle->AutoPilot.m_nCruiseSpeed = 0; + pVehicle->AutoPilot.m_nCarMission = MISSION_STOP_FOREVER; + } + break; + } + case MISSION_BLOCKPLAYER_FORWARDANDBACK: + { + CVector2D diff = (CVector2D)FindPlayerCoors() - pVehicle->GetPosition(); + float distance = Max(0.001f, diff.Magnitude()); + if (!FindPlayerVehicle() || DotProduct2D(CVector2D(diff.x / distance, diff.y / distance), FindPlayerSpeed()) > 0.05f) + pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKPLAYER_CLOSE; + BackToCruisingIfNoWantedLevel(pVehicle); + break; + } + default: + if (pVehicle->bIsLawEnforcer && FindPlayerPed()->m_pWanted->GetWantedLevel() > 0 && !CCullZones::NoPolice()){ + if (ABS(FindPlayerCoors().x - pVehicle->GetPosition().x) > 10.0f || + ABS(FindPlayerCoors().y - pVehicle->GetPosition().y) > 10.0f){ + pVehicle->AutoPilot.m_nCruiseSpeed = FindPoliceCarSpeedForWantedLevel(pVehicle); + pVehicle->SetStatus(STATUS_PHYSICS); + pVehicle->AutoPilot.m_nCarMission = + pVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_BOAT ? FindPoliceBoatMissionForWantedLevel() : FindPoliceCarMissionForWantedLevel(); + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + }else if (pVehicle->AutoPilot.m_nCarMission == MISSION_CRUISE){ + pVehicle->SetStatus(STATUS_PHYSICS); + TellOccupantsToLeaveCar(pVehicle); + pVehicle->AutoPilot.m_nCruiseSpeed = 0; + pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; + if (FindPlayerPed()->m_pWanted->GetWantedLevel() <= 1) + pVehicle->m_bSirenOrAlarm = false; + } + } + break; + } + break; + case STATUS_ABANDONED: + case STATUS_WRECKED: + pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; + pVehicle->AutoPilot.m_nCruiseSpeed = 0; + break; + } + if (pVehicle->bIsLawEnforcer && FindPlayerPed()->m_pWanted->GetWantedLevel() >= 1 && CCullZones::PoliceAbandonCars()) { + TellOccupantsToLeaveCar(pVehicle); + pVehicle->AutoPilot.m_nCruiseSpeed = 0; + pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; + } + float flatSpeed = pVehicle->GetMoveSpeed().MagnitudeSqr2D(); + if (flatSpeed > SQR(0.018f)){ + pVehicle->AutoPilot.m_nTimeToStartMission = CTimer::GetTimeInMilliseconds(); + pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); + } + if (pVehicle->GetStatus() == STATUS_PHYSICS && pVehicle->AutoPilot.m_nTempAction == TEMPACT_NONE){ + if (pVehicle->AutoPilot.m_nCarMission != MISSION_NONE){ + if (pVehicle->AutoPilot.m_nCarMission != MISSION_STOP_FOREVER && + pVehicle->AutoPilot.m_nCarMission != MISSION_BLOCKPLAYER_HANDBRAKESTOP && + pVehicle->AutoPilot.m_nCruiseSpeed != 0 && + (pVehicle->VehicleCreatedBy != RANDOM_VEHICLE || pVehicle->AutoPilot.m_nCarMission != MISSION_CRUISE)){ + if (pVehicle->AutoPilot.m_nDrivingStyle != DRIVINGSTYLE_STOP_FOR_CARS + && pVehicle->AutoPilot.m_nDrivingStyle != DRIVINGSTYLE_STOP_FOR_CARS_IGNORE_LIGHTS || + pVehicle->VehicleCreatedBy == MISSION_VEHICLE + ) { + if (CTimer::GetTimeInMilliseconds() - pVehicle->m_nLastTimeCollided > 500) + pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); + if (flatSpeed < SQR(0.018f) && CTimer::GetTimeInMilliseconds() - pVehicle->AutoPilot.m_nAntiReverseTimer > 2000){ + pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE; + if (pVehicle->AutoPilot.m_nCarMission != MISSION_NONE && + pVehicle->AutoPilot.m_nCarMission != MISSION_CRUISE || pVehicle->VehicleCreatedBy == MISSION_VEHICLE) + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 1500; + else + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 750; + pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); + if (pVehicle->VehicleCreatedBy == RANDOM_VEHICLE) + pVehicle->AutoPilot.m_nDrivingStyle = Max(DRIVINGSTYLE_AVOID_CARS, pVehicle->AutoPilot.m_nDrivingStyle); + pVehicle->PlayCarHorn(); + } + } + } + } + } + if ((pVehicle->m_randomSeed & 7) == 0){ + if (CTimer::GetTimeInMilliseconds() - pVehicle->AutoPilot.m_nTimeToStartMission > 30000 && + CTimer::GetPreviousTimeInMilliseconds() - pVehicle->AutoPilot.m_nTimeToStartMission <= 30000 && + pVehicle->AutoPilot.m_nCarMission == MISSION_CRUISE && + !CTrafficLights::ShouldCarStopForBridge(pVehicle)){ + pVehicle->SetStatus(STATUS_PHYSICS); + CCarCtrl::SwitchVehicleToRealPhysics(pVehicle); + pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE; + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 400; + } + } + if (pVehicle->bIsLawEnforcer) { + if (pVehicle->AutoPilot.m_nCarMission == MISSION_RAMPLAYER_FARAWAY || + pVehicle->AutoPilot.m_nCarMission == MISSION_RAMPLAYER_CLOSE) { + if (FindPlayerVehicle() && FindPlayerVehicle()->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE) + pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKPLAYER_FARAWAY; + } + } + if (pVehicle->GetUp().z < -0.7f){ + pVehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT; + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 1000; + } + if (pVehicle->AutoPilot.m_nTempAction == TEMPACT_NONE){ + switch (pVehicle->AutoPilot.m_nCarMission){ + case MISSION_RAMPLAYER_FARAWAY: + case MISSION_RAMPLAYER_CLOSE: + case MISSION_BLOCKPLAYER_FARAWAY: + case MISSION_BLOCKPLAYER_CLOSE: + if (FindPlayerVehicle() && FindPlayerSpeed().Magnitude() > pVehicle->GetMoveSpeed().Magnitude()){ + if (FindPlayerSpeed().Magnitude() > 0.1f){ + if (DotProduct2D(FindPlayerVehicle()->GetForward(), pVehicle->GetForward()) > 0.0f){ + CVector2D dist = pVehicle->GetPosition() - FindPlayerCoors(); + CVector2D speed = FindPlayerSpeed(); + if (0.5f * dist.Magnitude() * speed.Magnitude() < DotProduct2D(dist, speed)){ + if ((FindPlayerCoors() - pVehicle->GetPosition()).Magnitude() > 12.0f){ + pVehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT; + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 500; + } + } + } + } + } + break; + default: break; + } + } + if (pVehicle->pDriver && pVehicle->pDriver->m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS){ + if ((pVehicle->GetPosition() - FindPlayerCoors()).Magnitude() < 15.0f){ + if (!FindPlayerVehicle() || pVehicle->GetHasCollidedWith(FindPlayerVehicle())){ + pVehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT; + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 3000; + } + } + } + if (pVehicle->m_bSirenOrAlarm){ + if ((uint8)(pVehicle->m_randomSeed ^ CGeneral::GetRandomNumber()) == 0xAD) + pVehicle->m_nCarHornTimer = 45; + } + float target = 1.0f; + if (pVehicle->AutoPilot.m_nCarMission == MISSION_CRUISE) + target = CCarCtrl::FindSpeedMultiplierWithSpeedFromNodes(pVehicle->AutoPilot.m_nCruiseSpeedMultiplierType); + float change = CTimer::GetTimeStep() * 0.01f; + if (Abs(pVehicle->AutoPilot.m_fCruiseSpeedMultiplier - target) < change) + pVehicle->AutoPilot.m_fCruiseSpeedMultiplier = target; + else if (pVehicle->AutoPilot.m_fCruiseSpeedMultiplier > target) + pVehicle->AutoPilot.m_fCruiseSpeedMultiplier -= change; + else + pVehicle->AutoPilot.m_fCruiseSpeedMultiplier += change; + + if (pVehicle->bIsLawEnforcer && FindPlayerPed()->m_pWanted->GetWantedLevel() > 0) { + if (!FindPlayerVehicle() || + FindPlayerVehicle()->GetVehicleAppearance() == VEHICLE_APPEARANCE_CAR || + FindPlayerVehicle()->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE) { + if (pVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_BOAT) { + pVehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT; + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 1000; + } + } + else if (FindPlayerVehicle()->GetVehicleAppearance() == VEHICLE_APPEARANCE_BOAT) { + if (pVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_CAR || + pVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE) { + pVehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT; + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 1000; + } + } + } +} + +void CCarAI::CarHasReasonToStop(CVehicle* pVehicle) +{ + pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); +} + +float CCarAI::GetCarToGoToCoors(CVehicle* pVehicle, CVector* pTarget) +{ + if (pVehicle->AutoPilot.m_nCarMission != MISSION_GOTOCOORDS && pVehicle->AutoPilot.m_nCarMission != MISSION_GOTOCOORDS_STRAIGHT){ + pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + pVehicle->AutoPilot.m_nCruiseSpeed = 20; + pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); + pVehicle->SetStatus(STATUS_PHYSICS); + pVehicle->AutoPilot.m_nCarMission = (CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, *pTarget, false)) ? + MISSION_GOTOCOORDS_STRAIGHT : MISSION_GOTOCOORDS; + }else if (Abs(pTarget->x - pVehicle->AutoPilot.m_vecDestinationCoors.x) > 2.0f || + Abs(pTarget->y - pVehicle->AutoPilot.m_vecDestinationCoors.y) > 2.0f){ + pVehicle->AutoPilot.m_vecDestinationCoors = *pTarget; + } + return (pVehicle->GetPosition() - *pTarget).Magnitude2D(); +} + +float CCarAI::GetCarToParkAtCoors(CVehicle* pVehicle, CVector* pTarget) +{ + GetCarToGoToCoors(pVehicle, pTarget); + pVehicle->bParking = true; + pVehicle->AutoPilot.m_nCruiseSpeed = 10; + return (pVehicle->GetPosition() - *pTarget).Magnitude2D(); +} + +void CCarAI::AddPoliceCarOccupants(CVehicle* pVehicle) +{ + if (pVehicle->bOccupantsHaveBeenGenerated) + return; + pVehicle->bOccupantsHaveBeenGenerated = true; + switch (pVehicle->GetModelIndex()){ + case MI_FBIRANCH: + case MI_ENFORCER: + pVehicle->SetUpDriver(); + for (int i = 0; i < 3; i++) + pVehicle->SetupPassenger(i); + return; + case MI_POLICE: + case MI_RHINO: + case MI_BARRACKS: + pVehicle->SetUpDriver(); + if (FindPlayerPed()->m_pWanted->GetWantedLevel() > 1) + pVehicle->SetupPassenger(0); + return; + case MI_PREDATOR: + pVehicle->SetUpDriver(); + return; + case MI_VICECHEE: + { + pVehicle->SetUpDriver()->bMiamiViceCop = true; + pVehicle->SetupPassenger(0)->bMiamiViceCop = true; + CPopulation::NumMiamiViceCops += 2; + CCarCtrl::MiamiViceCycle = (CCarCtrl::MiamiViceCycle + 1) % 4; + CCarCtrl::LastTimeMiamiViceGenerated = CTimer::GetTimeInMilliseconds(); + return; + } + default: + return; + } +} + +void CCarAI::AddAmbulanceOccupants(CVehicle* pVehicle) +{ + pVehicle->SetUpDriver(); + pVehicle->SetupPassenger(1); +} + +void CCarAI::AddFiretruckOccupants(CVehicle* pVehicle) +{ + pVehicle->SetUpDriver(); + pVehicle->SetupPassenger(0); +} + +void CCarAI::TellOccupantsToLeaveCar(CVehicle* pVehicle) +{ + if (pVehicle->pDriver){ + pVehicle->pDriver->SetObjective(OBJECTIVE_LEAVE_CAR, pVehicle); + switch (pVehicle->GetModelIndex()) { + case MI_FIRETRUCK: + case MI_FBICAR: + case MI_ENFORCER: + case MI_BARRACKS: + case MI_RHINO: + case MI_POLICE: + break; + case MI_AMBULAN: + pVehicle->pDriver->Say(SOUND_PED_LEAVE_VEHICLE); + break; + } + } + int timer = 100; + for (int i = 0; i < pVehicle->m_nNumMaxPassengers; i++){ + if (pVehicle->pPassengers[i]) { + pVehicle->pPassengers[i]->m_leaveCarTimer = timer; + pVehicle->pPassengers[i]->SetObjective(OBJECTIVE_LEAVE_CAR, pVehicle); + timer += CGeneral::GetRandomNumberInRange(200, 400); + } + } +} + +void CCarAI::TellOccupantsToFleeCar(CVehicle* pVehicle) +{ + if (pVehicle->pDriver && !pVehicle->pDriver->IsPlayer()) { + pVehicle->pDriver->SetObjective(OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE); + switch (pVehicle->GetModelIndex()) { + case MI_FIRETRUCK: + case MI_FBIRANCH: + case MI_ENFORCER: + case MI_BARRACKS: + case MI_RHINO: + case MI_POLICE: + break; + case MI_AMBULAN: + pVehicle->pDriver->Say(SOUND_PED_LEAVE_VEHICLE); + break; + } + } + int timer = 100; + for (int i = 0; i < pVehicle->m_nNumMaxPassengers; i++) { + if (pVehicle->pPassengers[i]) { + pVehicle->pPassengers[i]->m_leaveCarTimer = timer; + pVehicle->pPassengers[i]->SetObjective(OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE); + timer += CGeneral::GetRandomNumberInRange(200, 400); + } + } +} + +void CCarAI::TellCarToRamOtherCar(CVehicle* pVehicle, CVehicle* pTarget) +{ + pVehicle->AutoPilot.m_pTargetCar = pTarget; + pTarget->RegisterReference((CEntity**)&pVehicle->AutoPilot.m_pTargetCar); + pVehicle->AutoPilot.m_nCarMission = MISSION_RAMCAR_FARAWAY; + pVehicle->bEngineOn = true; + pVehicle->AutoPilot.m_nCruiseSpeed = Max(6, pVehicle->AutoPilot.m_nCruiseSpeed); +} + +void CCarAI::TellCarToBlockOtherCar(CVehicle* pVehicle, CVehicle* pTarget) +{ + pVehicle->AutoPilot.m_pTargetCar = pTarget; + pTarget->RegisterReference((CEntity**)&pVehicle->AutoPilot.m_pTargetCar); + pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKCAR_FARAWAY; + pVehicle->bEngineOn = true; + pVehicle->AutoPilot.m_nCruiseSpeed = Max(6, pVehicle->AutoPilot.m_nCruiseSpeed); +} + +uint8 CCarAI::FindPoliceCarMissionForWantedLevel() +{ + switch (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->GetWantedLevel()){ + case 0: + case 1: return MISSION_BLOCKPLAYER_FARAWAY; + case 2: return (CGeneral::GetRandomNumber() & 3) >= 3 ? MISSION_RAMPLAYER_FARAWAY : MISSION_BLOCKPLAYER_FARAWAY; + case 3: return (CGeneral::GetRandomNumber() & 3) >= 2 ? MISSION_RAMPLAYER_FARAWAY : MISSION_BLOCKPLAYER_FARAWAY; + case 4: + case 5: + case 6: return (CGeneral::GetRandomNumber() & 3) >= 1 ? MISSION_RAMPLAYER_FARAWAY : MISSION_BLOCKPLAYER_FARAWAY; + default: return MISSION_BLOCKPLAYER_FARAWAY; + } +} + +uint8 CCarAI::FindPoliceBoatMissionForWantedLevel() +{ + switch (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->GetWantedLevel()) { + case 0: + case 1: return MISSION_BLOCKPLAYER_FARAWAY; + case 2: + case 3: + case 4: + case 5: + case 6: return MISSION_ATTACKPLAYER; + default: return MISSION_BLOCKPLAYER_FARAWAY; + } +} + +int32 CCarAI::FindPoliceCarSpeedForWantedLevel(CVehicle* pVehicle) +{ + switch (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->GetWantedLevel()) { + case 0: return CGeneral::GetRandomNumberInRange(12, 16); + case 1: return 25; + case 2: return 34; + case 3: return GAME_SPEED_TO_CARAI_SPEED * pVehicle->pHandling->Transmission.fMaxVelocity * 0.9f; + case 4: return GAME_SPEED_TO_CARAI_SPEED * pVehicle->pHandling->Transmission.fMaxVelocity * 1.2f; + case 5: return GAME_SPEED_TO_CARAI_SPEED * pVehicle->pHandling->Transmission.fMaxVelocity * 1.25f; + case 6: return GAME_SPEED_TO_CARAI_SPEED * pVehicle->pHandling->Transmission.fMaxVelocity * 1.3f; + default: return 0; + } +} + +void CCarAI::MellowOutChaseSpeed(CVehicle* pVehicle) +{ + if (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->GetWantedLevel() == 1){ + float distanceToPlayer = (pVehicle->GetPosition() - FindPlayerCoors()).Magnitude(); + if (FindPlayerVehicle()){ + if (distanceToPlayer < 10.0f) + pVehicle->AutoPilot.m_nCruiseSpeed = 15; + else if (distanceToPlayer < 20.0f) + pVehicle->AutoPilot.m_nCruiseSpeed = 22; + else + pVehicle->AutoPilot.m_nCruiseSpeed = 25; + }else{ + if (distanceToPlayer < 20.0f) + pVehicle->AutoPilot.m_nCruiseSpeed = 5; + else if (distanceToPlayer < 40.0f) + pVehicle->AutoPilot.m_nCruiseSpeed = 13; + else + pVehicle->AutoPilot.m_nCruiseSpeed = 25; + } + }else if (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->GetWantedLevel() == 2){ + float distanceToPlayer = (pVehicle->GetPosition() - FindPlayerCoors()).Magnitude(); + if (FindPlayerVehicle()) { + if (distanceToPlayer < 10.0f) + pVehicle->AutoPilot.m_nCruiseSpeed = 27; + else if (distanceToPlayer < 20.0f) + pVehicle->AutoPilot.m_nCruiseSpeed = 30; + else + pVehicle->AutoPilot.m_nCruiseSpeed = 34; + } + else { + if (distanceToPlayer < 20.0f) + pVehicle->AutoPilot.m_nCruiseSpeed = 5; + else if (distanceToPlayer < 40.0f) + pVehicle->AutoPilot.m_nCruiseSpeed = 18; + else + pVehicle->AutoPilot.m_nCruiseSpeed = 34; + } + } + if (!FindPlayerVehicle() && FindPlayerPed()->GetMoveSpeed().Magnitude() < 0.07f) { + if ((FindPlayerCoors() - pVehicle->GetPosition()).Magnitude() < 30.0f) + pVehicle->AutoPilot.m_nCruiseSpeed = Min(10, pVehicle->AutoPilot.m_nCruiseSpeed); + } +} + +void CCarAI::MellowOutChaseSpeedBoat(CVehicle* pVehicle) +{ + switch (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->GetWantedLevel()) { + case 0: pVehicle->AutoPilot.m_nCruiseSpeed = 8; break; + case 1: pVehicle->AutoPilot.m_nCruiseSpeed = 10; break; + case 2: pVehicle->AutoPilot.m_nCruiseSpeed = 15; break; + case 3: pVehicle->AutoPilot.m_nCruiseSpeed = 20; break; + case 4: pVehicle->AutoPilot.m_nCruiseSpeed = 25; break; + case 5: pVehicle->AutoPilot.m_nCruiseSpeed = 30; break; + case 6: pVehicle->AutoPilot.m_nCruiseSpeed = 40; break; + } +} + +void CCarAI::MakeWayForCarWithSiren(CVehicle *pVehicle) +{ + float flatSpeed = pVehicle->GetMoveSpeed().Magnitude2D(); + if (flatSpeed < 0.1f) + return; + CVector2D forward = pVehicle->GetMoveSpeed() / flatSpeed; + float projection = flatSpeed * 45 + 20; + int i = CPools::GetVehiclePool()->GetSize(); + while (--i >= 0) { + CVehicle* vehicle = CPools::GetVehiclePool()->GetSlot(i); + if (!vehicle) + continue; + if (!vehicle->IsCar() && !vehicle->IsBike()) + continue; + if (vehicle->GetStatus() != STATUS_SIMPLE && vehicle->GetStatus() != STATUS_PHYSICS) + continue; + if (vehicle->VehicleCreatedBy != RANDOM_VEHICLE) + continue; + if (vehicle->bIsLawEnforcer || vehicle->bIsAmbulanceOnDuty || vehicle->bIsFireTruckOnDuty) + continue; + if (vehicle == pVehicle) + continue; + if (vehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_AVOID_CARS) + return; + if (Abs(pVehicle->GetPosition().z - vehicle->GetPosition().z) >= 5.0f) + continue; + CVector2D distance = vehicle->GetPosition() - pVehicle->GetPosition(); + if (distance.Magnitude() >= projection) + continue; + if (vehicle->GetMoveSpeed().Magnitude2D() <= 0.05f) + continue; + float correlation = DotProduct2D(forward, distance) / distance.Magnitude(); + if (correlation <= 0.0f) + continue; + if (correlation > 0.8f && DotProduct2D(forward, vehicle->GetForward()) > 0.7f){ + if (vehicle->AutoPilot.m_nTempAction != TEMPACT_SWERVELEFT && vehicle->AutoPilot.m_nTempAction != TEMPACT_SWERVERIGHT){ + vehicle->AutoPilot.m_nTempAction = (distance.x * forward.y - distance.y * forward.x > 0.0f) ? + TEMPACT_SWERVELEFT : TEMPACT_SWERVERIGHT; + vehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 2000; + } + vehicle->SetStatus(STATUS_PHYSICS); + }else{ + if (DotProduct2D(vehicle->GetMoveSpeed(), distance) < 0.0f && vehicle->AutoPilot.m_nTempAction != TEMPACT_WAIT){ + vehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT; + vehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 2000; + } + } + } +} diff --git a/src/miami/control/CarAI.h b/src/miami/control/CarAI.h new file mode 100644 index 00000000..dcd76d78 --- /dev/null +++ b/src/miami/control/CarAI.h @@ -0,0 +1,31 @@ +#pragma once + +#include "AutoPilot.h" + +class CVehicle; + +class CCarAI +{ +public: + static float FindSwitchDistanceClose(CVehicle*); + static float FindSwitchDistanceFarNormalVehicle(CVehicle*); + static float FindSwitchDistanceFar(CVehicle*); + static void BackToCruisingIfNoWantedLevel(CVehicle*); + static void UpdateCarAI(CVehicle*); + static void CarHasReasonToStop(CVehicle*); + static float GetCarToGoToCoors(CVehicle*, CVector*); + static float GetCarToParkAtCoors(CVehicle*, CVector*); + static void AddPoliceCarOccupants(CVehicle*); + static void AddAmbulanceOccupants(CVehicle*); + static void AddFiretruckOccupants(CVehicle*); + static void TellOccupantsToLeaveCar(CVehicle*); + static void TellOccupantsToFleeCar(CVehicle*); + static void TellCarToRamOtherCar(CVehicle*, CVehicle*); + static void TellCarToBlockOtherCar(CVehicle*, CVehicle*); + static uint8 FindPoliceCarMissionForWantedLevel(); + static uint8 FindPoliceBoatMissionForWantedLevel(); + static int32 FindPoliceCarSpeedForWantedLevel(CVehicle*); + static void MellowOutChaseSpeed(CVehicle*); + static void MellowOutChaseSpeedBoat(CVehicle*); + static void MakeWayForCarWithSiren(CVehicle *veh); +}; diff --git a/src/miami/control/CarCtrl.cpp b/src/miami/control/CarCtrl.cpp new file mode 100644 index 00000000..5aaf4d23 --- /dev/null +++ b/src/miami/control/CarCtrl.cpp @@ -0,0 +1,3370 @@ +#include "common.h" + +#include "CarCtrl.h" + +#include "Accident.h" +#include "Automobile.h" +#include "Bike.h" +#include "Camera.h" +#include "CarAI.h" +#include "CarGen.h" +#include "Cranes.h" +#include "Curves.h" +#include "CutsceneMgr.h" +#include "Gangs.h" +#include "Game.h" +#include "Garages.h" +#include "General.h" +#include "IniFile.h" +#include "ModelIndices.h" +#include "PathFind.h" +#include "Ped.h" +#include "PlayerInfo.h" +#include "PlayerPed.h" +#include "Population.h" +#include "Wanted.h" +#include "Pools.h" +#include "Renderer.h" +#include "RoadBlocks.h" +#include "Timer.h" +#include "TrafficLights.h" +#include "Streaming.h" +#include "VisibilityPlugins.h" +#include "Vehicle.h" +#include "Fire.h" +#include "WaterLevel.h" +#include "World.h" +#include "Zones.h" +#include "Pickups.h" + +#define DISTANCE_TO_SPAWN_ROADBLOCK_PEDS (51.0f) +#define DISTANCE_TO_SCAN_FOR_DANGER (14.0f) +#define DISTANCE_TO_SCAN_FOR_PED_DANGER (11.0f) +#define SAFE_DISTANCE_TO_PED (3.0f) +#define INFINITE_Z (1000000000.0f) + +#define VEHICLE_HEIGHT_DIFF_TO_CONSIDER_WEAVING (4.0f) +#define PED_HEIGHT_DIFF_TO_CONSIDER_WEAVING (4.0f) +#define OBJECT_HEIGHT_DIFF_TO_CONSIDER_WEAVING (8.0f) +#define WIDTH_COEF_TO_WEAVE_SAFELY (1.2f) +#define OBJECT_WIDTH_TO_WEAVE (0.3f) +#define PED_WIDTH_TO_WEAVE (0.8f) + +#define PATH_DIRECTION_NONE (0) +#define PATH_DIRECTION_STRAIGHT (1) +#define PATH_DIRECTION_RIGHT (2) +#define PATH_DIRECTION_LEFT (4) + +#define ATTEMPTS_TO_FIND_NEXT_NODE (15) + +#define DISTANCE_TO_SWITCH_FROM_BLOCK_TO_STOP (5.0f) +#define DISTANCE_TO_SWITCH_FROM_STOP_TO_BLOCK (10.0f) +#define MAX_SPEED_TO_ACCOUNT_IN_INTERCEPTING (0.13f) +#define DISTANCE_TO_NEXT_NODE_TO_CONSIDER_SLOWING_DOWN (40.0f) +#define MAX_ANGLE_TO_STEER_AT_HIGH_SPEED (0.2f) +#define MIN_SPEED_TO_START_LIMITING_STEER (0.45f) +#define DISTANCE_TO_NEXT_NODE_TO_SELECT_NEW (5.0f) +#define DISTANCE_TO_FACING_NEXT_NODE_TO_SELECT_NEW (8.0f) +#define DEFAULT_MAX_STEER_ANGLE (0.5f) +#define MIN_LOWERING_SPEED_COEFFICIENT (0.4f) +#define MAX_ANGLE_FOR_SPEED_LIMITING (1.2f) +#define MIN_ANGLE_FOR_SPEED_LIMITING (0.4f) +#define MIN_ANGLE_FOR_SPEED_LIMITING_BETWEEN_NODES (0.1f) +#define MIN_ANGLE_TO_APPLY_HANDBRAKE (0.7f) +#define MIN_SPEED_TO_APPLY_HANDBRAKE (0.3f) + +#define PROBABILITY_OF_DEAD_PED_ACCIDENT (0.005f) +#define DISTANCE_BETWEEN_CAR_AND_DEAD_PED (6.0f) +#define PROBABILITY_OF_PASSENGER_IN_VEHICLE (0.125f) + +#define ONSCREEN_DESPAWN_RANGE (120.0f) +#define MINIMAL_DISTANCE_TO_SPAWN_ONSCREEN (100.0f) +#define REQUEST_ONSCREEN_DISTANCE ((ONSCREEN_DESPAWN_RANGE + MINIMAL_DISTANCE_TO_SPAWN_ONSCREEN) / 2) +#define OFFSCREEN_DESPAWN_RANGE (40.0f) +#define EXTENDED_RANGE_DESPAWN_MULTIPLIER (1.5f) + +bool CCarCtrl::bMadDriversCheat; +int32 CCarCtrl::NumLawEnforcerCars; +int32 CCarCtrl::NumAmbulancesOnDuty; +int32 CCarCtrl::NumFiretrucksOnDuty; +bool CCarCtrl::bCarsGeneratedAroundCamera; +float CCarCtrl::CarDensityMultiplier = 1.0f; +int32 CCarCtrl::NumMissionCars; +int32 CCarCtrl::NumRandomCars; +int32 CCarCtrl::NumParkedCars; +int32 CCarCtrl::NumPermanentCars; +int8 CCarCtrl::CountDownToCarsAtStart; +int32 CCarCtrl::MaxNumberOfCarsInUse = DEFAULT_MAX_NUMBER_OF_CARS; +uint32 CCarCtrl::LastTimeLawEnforcerCreated; +uint32 CCarCtrl::LastTimeFireTruckCreated; +uint32 CCarCtrl::LastTimeAmbulanceCreated; +int32 CCarCtrl::MiamiViceCycle; +uint32 CCarCtrl::LastTimeMiamiViceGenerated; +int32 CCarCtrl::TotalNumOfCarsOfRating[TOTAL_CUSTOM_CLASSES]; +int32 CCarCtrl::CarArrays[TOTAL_CUSTOM_CLASSES][MAX_CAR_MODELS_IN_ARRAY]; +int32 CCarCtrl::NumRequestsOfCarRating[TOTAL_CUSTOM_CLASSES]; +int32 CCarCtrl::NumOfLoadedCarsOfRating[TOTAL_CUSTOM_CLASSES]; +int32 CCarCtrl::CarFreqArrays[TOTAL_CUSTOM_CLASSES][MAX_CAR_MODELS_IN_ARRAY]; +int32 CCarCtrl::LoadedCarsArray[TOTAL_CUSTOM_CLASSES][MAX_CAR_MODELS_IN_ARRAY]; +CVehicle* apCarsToKeep[MAX_CARS_TO_KEEP]; +uint32 aCarsToKeepTime[MAX_CARS_TO_KEEP]; + +void +CCarCtrl::GenerateRandomCars() +{ + if (CCutsceneMgr::IsRunning()) { + CountDownToCarsAtStart = 2; + return; + } + if (NumRandomCars < 30){ + if (CountDownToCarsAtStart == 0) + GenerateOneRandomCar(); + else if (--CountDownToCarsAtStart == 0) { + for (int i = 0; i < 100; i++) + GenerateOneRandomCar(); + CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter = 20; + } + } + /* Approximately once per 4 seconds. */ + if ((CTimer::GetTimeInMilliseconds() & 0xFFFFF000) != (CTimer::GetPreviousTimeInMilliseconds() & 0xFFFFF000)) + GenerateEmergencyServicesCar(); +} + +void +CCarCtrl::GenerateOneRandomCar() +{ + static int32 unk = 0; + bool bTopDownCamera = false; + CPlayerInfo* pPlayer = &CWorld::Players[CWorld::PlayerInFocus]; + CVector vecTargetPos = FindPlayerCentreOfWorld(CWorld::PlayerInFocus); + CVector2D vecPlayerSpeed = FindPlayerSpeed(); + CZoneInfo zone; + CTheZones::GetZoneInfoForTimeOfDay(&vecTargetPos, &zone); + pPlayer->m_nTrafficMultiplier = pPlayer->m_fRoadDensity * zone.carDensity; + if (NumRandomCars >= pPlayer->m_nTrafficMultiplier * CarDensityMultiplier * CIniFile::CarNumberMultiplier) + return; + if (NumFiretrucksOnDuty + NumAmbulancesOnDuty + NumParkedCars + NumMissionCars + NumLawEnforcerCars + NumRandomCars >= MaxNumberOfCarsInUse) + return; + CWanted* pWanted = pPlayer->m_pPed->m_pWanted; + int carClass; + int carModel; + if (pWanted->GetWantedLevel() > 1 && NumLawEnforcerCars < pWanted->m_MaximumLawEnforcerVehicles && + pWanted->m_CurrentCops < pWanted->m_MaxCops && !CGame::IsInInterior() && ( + pWanted->GetWantedLevel() > 3 || + pWanted->GetWantedLevel() > 2 && CTimer::GetTimeInMilliseconds() > LastTimeLawEnforcerCreated + 5000 || + pWanted->GetWantedLevel() > 1 && CTimer::GetTimeInMilliseconds() > LastTimeLawEnforcerCreated + 8000)) { + /* Last pWanted->GetWantedLevel() > 1 is unnecessary but I added it for better readability. */ + /* Wouldn't be surprised it was there originally but was optimized out. */ + carClass = COPS; + carModel = ChoosePoliceCarModel(); + }else{ + carModel = ChooseModel(&zone, &carClass); + if (carModel == -1 || (carClass == COPS && pWanted->GetWantedLevel() >= 1)) + /* All cop spawns with wanted level are handled by condition above. */ + /* In particular it means that cop cars never spawn if player has wanted level of 1. */ + return; + } + float frontX, frontY; + float preferredDistance, angleLimit; + bool invertAngleLimitTest; + CVector spawnPosition; + int32 curNodeId, nextNodeId; + float positionBetweenNodes; + bool testForCollision; + CVehicle* pPlayerVehicle = FindPlayerVehicle(); + CVector2D vecPlayerVehicleSpeed; + float fPlayerVehicleSpeed; + if (pPlayerVehicle) { + vecPlayerVehicleSpeed = FindPlayerVehicle()->GetMoveSpeed(); + fPlayerVehicleSpeed = vecPlayerVehicleSpeed.Magnitude(); + } + if (TheCamera.GetForward().z < -0.9f){ + /* Player uses topdown camera. */ + /* Spawn essentially anywhere. */ + frontX = frontY = 0.707f; /* 45 degrees */ + angleLimit = -1.0f; + bTopDownCamera = true; + invertAngleLimitTest = true; + preferredDistance = OFFSCREEN_DESPAWN_RANGE + 15.0f; + /* BUG: testForCollision not initialized in original game. */ + testForCollision = false; + }else if (!pPlayerVehicle){ + /* Player is not in vehicle. */ + testForCollision = true; + frontX = TheCamera.CamFrontXNorm; + frontY = TheCamera.CamFrontYNorm; + switch (CTimer::GetFrameCounter() & 1) { + case 0: + /* Spawn a vehicle relatively far away from player. */ + /* Forward to his current direction (camera direction). */ + angleLimit = 0.707f; /* 45 degrees */ + invertAngleLimitTest = true; + preferredDistance = REQUEST_ONSCREEN_DISTANCE * TheCamera.GenerationDistMultiplier; + break; + case 1: + /* Spawn a vehicle close to player to his side. */ + /* Kinda not within camera angle. */ + angleLimit = 0.707f; /* 45 degrees */ + invertAngleLimitTest = false; + preferredDistance = OFFSCREEN_DESPAWN_RANGE; + break; + } + }else if (fPlayerVehicleSpeed > 0.4f){ /* 72 km/h */ + /* Player is moving fast in vehicle */ + /* Prefer spawning vehicles very far away from him. */ + frontX = vecPlayerVehicleSpeed.x / fPlayerVehicleSpeed; + frontY = vecPlayerVehicleSpeed.y / fPlayerVehicleSpeed; + testForCollision = false; + switch (CTimer::GetFrameCounter() & 3) { + case 0: + case 1: + /* Spawn a vehicle in a very narrow gap in front of a player */ + angleLimit = 0.85f; /* approx 30 degrees */ + invertAngleLimitTest = true; + preferredDistance = REQUEST_ONSCREEN_DISTANCE * TheCamera.GenerationDistMultiplier; + break; + case 2: + /* Spawn a vehicle relatively far away from player. */ + /* Forward to his current direction (camera direction). */ + angleLimit = 0.707f; /* 45 degrees */ + invertAngleLimitTest = true; + preferredDistance = REQUEST_ONSCREEN_DISTANCE * TheCamera.GenerationDistMultiplier; + break; + case 3: + /* Spawn a vehicle close to player to his side. */ + /* Kinda not within camera angle. */ + angleLimit = 0.707f; /* 45 degrees */ + invertAngleLimitTest = false; + preferredDistance = OFFSCREEN_DESPAWN_RANGE; + break; + } + }else if (fPlayerVehicleSpeed > 0.1f){ /* 18 km/h */ + /* Player is moving moderately fast in vehicle */ + /* Spawn more vehicles to player's side. */ + frontX = vecPlayerVehicleSpeed.x / fPlayerVehicleSpeed; + frontY = vecPlayerVehicleSpeed.y / fPlayerVehicleSpeed; + testForCollision = false; + switch (CTimer::GetFrameCounter() & 3) { + case 0: + /* Spawn a vehicle in a very narrow gap in front of a player */ + angleLimit = 0.85f; /* approx 30 degrees */ + invertAngleLimitTest = true; + preferredDistance = REQUEST_ONSCREEN_DISTANCE * TheCamera.GenerationDistMultiplier; + break; + case 1: + /* Spawn a vehicle relatively far away from player. */ + /* Forward to his current direction (camera direction). */ + angleLimit = 0.707f; /* 45 degrees */ + invertAngleLimitTest = true; + preferredDistance = REQUEST_ONSCREEN_DISTANCE * TheCamera.GenerationDistMultiplier; + break; + case 2: + case 3: + /* Spawn a vehicle close to player to his side. */ + /* Kinda not within camera angle. */ + angleLimit = 0.707f; /* 45 degrees */ + invertAngleLimitTest = false; + preferredDistance = OFFSCREEN_DESPAWN_RANGE; + break; + } + }else{ + /* Player is in vehicle but moving very slow. */ + /* Then use camera direction instead of vehicle direction. */ + testForCollision = true; + frontX = TheCamera.CamFrontXNorm; + frontY = TheCamera.CamFrontYNorm; + switch (CTimer::GetFrameCounter() & 1) { + case 0: + /* Spawn a vehicle relatively far away from player. */ + /* Forward to his current direction (camera direction). */ + angleLimit = 0.707f; /* 45 degrees */ + invertAngleLimitTest = true; + preferredDistance = REQUEST_ONSCREEN_DISTANCE * TheCamera.GenerationDistMultiplier; + break; + case 1: + /* Spawn a vehicle close to player to his side. */ + /* Kinda not within camera angle. */ + angleLimit = 0.707f; /* 45 degrees */ + invertAngleLimitTest = false; + preferredDistance = OFFSCREEN_DESPAWN_RANGE; + break; + } + } + if (!ThePaths.GenerateCarCreationCoors(vecTargetPos.x, vecTargetPos.y, frontX, frontY, + preferredDistance, angleLimit, invertAngleLimitTest, &spawnPosition, &curNodeId, &nextNodeId, + &positionBetweenNodes, carClass == COPS && pWanted->GetWantedLevel() >= 1)) + return; + CPathNode* pCurNode = &ThePaths.m_pathNodes[curNodeId]; + CPathNode* pNextNode = &ThePaths.m_pathNodes[nextNodeId]; + bool bBoatGenerated = false; + if ((CGeneral::GetRandomNumber() & 0xF) > Min(pCurNode->spawnRate, pNextNode->spawnRate)) + return; + if (pCurNode->bWaterPath) { + bBoatGenerated = true; + if (carClass == COPS) { + carModel = MI_PREDATOR; + carClass = COPS_BOAT; + if (!CStreaming::HasModelLoaded(MI_PREDATOR)) { + CStreaming::RequestModel(MI_PREDATOR, STREAMFLAGS_DEPENDENCY); + return; + } + } + else { + int i; + carModel = -1; + for (i = 10; i > 0 && (carModel == -1 || !CStreaming::HasModelLoaded(carModel)); i--) { + carModel = ChooseBoatModel(ChooseBoatRating(&zone)); + } + if (i == 0) + return; + } + if (pCurNode->bOnlySmallBoats || pNextNode->bOnlySmallBoats) { + if (BoatWithTallMast(carModel)) + return; + } + } + int16 colliding; + CWorld::FindObjectsKindaColliding(spawnPosition, bBoatGenerated ? 40.0f : 10.0f, true, &colliding, 2, nil, false, true, true, false, false); + if (colliding) + /* If something is already present in spawn position, do not create vehicle*/ + return; + if (!bBoatGenerated && !ThePaths.TestCoorsCloseness(vecTargetPos, false, spawnPosition)) + /* Testing if spawn position can reach target position via valid path. */ + return; + int16 idInNode = 0; + + while (idInNode < pCurNode->numLinks && + ThePaths.ConnectedNode(idInNode + pCurNode->firstLink) != nextNodeId) + idInNode++; + int16 connectionId = ThePaths.m_carPathConnections[idInNode + pCurNode->firstLink]; + CCarPathLink* pPathLink = &ThePaths.m_carPathLinks[connectionId]; + int16 lanesOnCurrentRoad = pPathLink->pathNodeIndex == nextNodeId ? pPathLink->numLeftLanes : pPathLink->numRightLanes; + CVehicleModelInfo* pModelInfo = (CVehicleModelInfo*)CModelInfo::GetModelInfo(carModel); + if (lanesOnCurrentRoad == 0) + /* Not spawning vehicle if road is one way and intended direction is opposide to that way. */ + return; + CVehicle* pVehicle; + if (CModelInfo::IsBoatModel(carModel)) + pVehicle = new CBoat(carModel, RANDOM_VEHICLE); + else if (CModelInfo::IsBikeModel(carModel)) + pVehicle = new CBike(carModel, RANDOM_VEHICLE); + else + pVehicle = new CAutomobile(carModel, RANDOM_VEHICLE); + pVehicle->AutoPilot.m_nPrevRouteNode = 0; + pVehicle->AutoPilot.m_nCurrentRouteNode = curNodeId; + pVehicle->AutoPilot.m_nNextRouteNode = nextNodeId; + switch (carClass) { + case COPS: + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + if (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->GetWantedLevel() != 0){ + pVehicle->AutoPilot.m_nCruiseSpeed = CCarAI::FindPoliceCarSpeedForWantedLevel(pVehicle); + pVehicle->AutoPilot.m_fMaxTrafficSpeed = pVehicle->AutoPilot.m_nCruiseSpeed / 2; + pVehicle->AutoPilot.m_nCarMission = CCarAI::FindPoliceCarMissionForWantedLevel(); + pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + }else{ + pVehicle->AutoPilot.m_nCruiseSpeed = CGeneral::GetRandomNumberInRange(12, 16); + pVehicle->AutoPilot.m_fMaxTrafficSpeed = pVehicle->AutoPilot.m_nCruiseSpeed; + pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; + pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; + } + if (carModel == MI_FBIRANCH){ + pVehicle->m_currentColour1 = 0; + pVehicle->m_currentColour2 = 0; + } + pVehicle->bCreatedAsPoliceVehicle = true; + break; + case COPS_BOAT: + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + pVehicle->AutoPilot.m_nCruiseSpeed = CGeneral::GetRandomNumberInRange(4, 16); + pVehicle->AutoPilot.m_fMaxTrafficSpeed = pVehicle->AutoPilot.m_nCruiseSpeed; + pVehicle->AutoPilot.m_nCarMission = CCarAI::FindPoliceBoatMissionForWantedLevel(); + pVehicle->bCreatedAsPoliceVehicle = true; + break; + default: + pVehicle->AutoPilot.m_nCruiseSpeed = CGeneral::GetRandomNumberInRange(9, 14); + if (carClass == EXEC) + pVehicle->AutoPilot.m_nCruiseSpeed = CGeneral::GetRandomNumberInRange(12, 18); + else if (carClass == POOR) + pVehicle->AutoPilot.m_nCruiseSpeed = CGeneral::GetRandomNumberInRange(7, 10); + if (pVehicle->GetColModel()->boundingBox.max.y - pVehicle->GetColModel()->boundingBox.min.y > 10.0f || carClass == BIG) { + pVehicle->AutoPilot.m_nCruiseSpeed *= 3; + pVehicle->AutoPilot.m_nCruiseSpeed /= 4; + } + pVehicle->AutoPilot.m_fMaxTrafficSpeed = pVehicle->AutoPilot.m_nCruiseSpeed; + pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; + break; + } + if (pVehicle && pVehicle->GetModelIndex() == MI_MRWHOOP) + pVehicle->m_bSirenOrAlarm = true; + pVehicle->AutoPilot.m_nNextPathNodeInfo = connectionId; + pVehicle->AutoPilot.m_nNextLane = pVehicle->AutoPilot.m_nCurrentLane = CGeneral::GetRandomNumber() % lanesOnCurrentRoad; + CBox* boundingBox = &CModelInfo::GetColModel(pVehicle->GetModelIndex())->boundingBox; + float carLength = 1.0f + (boundingBox->max.y - boundingBox->min.y) / 2; + float distanceBetweenNodes = (pCurNode->GetPosition() - pNextNode->GetPosition()).Magnitude2D(); + /* If car is so long that it doesn't fit between two car nodes, place it directly in the middle. */ + /* Otherwise put it at least in a way that full vehicle length fits between two nodes. */ + if (distanceBetweenNodes / 2 < carLength) + positionBetweenNodes = 0.5f; + else + positionBetweenNodes = Min(1.0f - carLength / distanceBetweenNodes, Max(carLength / distanceBetweenNodes, positionBetweenNodes)); + pVehicle->AutoPilot.m_nNextDirection = (curNodeId >= nextNodeId) ? 1 : -1; + if (pCurNode->numLinks == 1){ + /* Do not create vehicle if there is nowhere to go. */ + delete pVehicle; + return; + } + int16 nextConnection = pVehicle->AutoPilot.m_nNextPathNodeInfo; + int16 newLink; + while (nextConnection == pVehicle->AutoPilot.m_nNextPathNodeInfo){ + newLink = CGeneral::GetRandomNumber() % pCurNode->numLinks; + nextConnection = ThePaths.m_carPathConnections[newLink + pCurNode->firstLink]; + } + pVehicle->AutoPilot.m_nCurrentPathNodeInfo = nextConnection; + pVehicle->AutoPilot.m_nCurrentDirection = (ThePaths.ConnectedNode(newLink + pCurNode->firstLink) >= curNodeId) ? 1 : -1; + CVector2D vecBetweenNodes = pNextNode->GetPosition() - pCurNode->GetPosition(); + float forwardX, forwardY; + float distBetweenNodes = vecBetweenNodes.Magnitude(); + if (distanceBetweenNodes == 0.0f){ + forwardX = 1.0f; + forwardY = 0.0f; + }else{ + forwardX = vecBetweenNodes.x / distBetweenNodes; + forwardY = vecBetweenNodes.y / distBetweenNodes; + } + /* I think the following might be some form of SetRotateZOnly. */ + /* Setting up direction between two car nodes. */ + pVehicle->GetForward() = CVector(forwardX, forwardY, 0.0f); + pVehicle->GetRight() = CVector(forwardY, -forwardX, 0.0f); + pVehicle->GetUp() = CVector(0.0f, 0.0f, 1.0f); + + float currentPathLinkForwardX = pVehicle->AutoPilot.m_nCurrentDirection * ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nCurrentPathNodeInfo].GetDirX(); + float currentPathLinkForwardY = pVehicle->AutoPilot.m_nCurrentDirection * ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nCurrentPathNodeInfo].GetDirY(); + float nextPathLinkForwardX = pVehicle->AutoPilot.m_nNextDirection * ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo].GetDirX(); + float nextPathLinkForwardY = pVehicle->AutoPilot.m_nNextDirection * ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo].GetDirY(); + +#ifdef FIX_BUGS + CCarPathLink* pCurrentLink; + CCarPathLink* pNextLink; + CVector positionOnCurrentLinkIncludingLane; + CVector positionOnNextLinkIncludingLane; + float directionCurrentLinkX; + float directionCurrentLinkY; + float directionNextLinkX; + float directionNextLinkY; + if (positionBetweenNodes < 0.5f) { + pCurrentLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nCurrentPathNodeInfo]; + pNextLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo]; + positionOnCurrentLinkIncludingLane = CVector( + pCurrentLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardY, + pCurrentLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardX, + 0.0f); + positionOnNextLinkIncludingLane = CVector( + pNextLink->GetX() + ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardY, + pNextLink->GetY() - ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardX, + 0.0f); + directionCurrentLinkX = pCurrentLink->GetDirX() * pVehicle->AutoPilot.m_nCurrentDirection; + directionCurrentLinkY = pCurrentLink->GetDirY() * pVehicle->AutoPilot.m_nCurrentDirection; + directionNextLinkX = pNextLink->GetDirX() * pVehicle->AutoPilot.m_nNextDirection; + directionNextLinkY = pNextLink->GetDirY() * pVehicle->AutoPilot.m_nNextDirection; + /* We want to make a path between two links that may not have the same forward directions a curve. */ + pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve = CCurves::CalcSpeedScaleFactor( + &positionOnCurrentLinkIncludingLane, + &positionOnNextLinkIncludingLane, + directionCurrentLinkX, directionCurrentLinkY, + directionNextLinkX, directionNextLinkY + ) * (1000.0f / pVehicle->AutoPilot.m_fMaxTrafficSpeed); + pVehicle->AutoPilot.m_nTimeEnteredCurve = CTimer::GetTimeInMilliseconds() - + (uint32)((0.5f + positionBetweenNodes) * pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve); + } + else { + PickNextNodeRandomly(pVehicle); + pVehicle->AutoPilot.m_nTimeEnteredCurve = CTimer::GetTimeInMilliseconds() - + (uint32)((positionBetweenNodes - 0.5f) * pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve); + + pCurrentLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nCurrentPathNodeInfo]; + pNextLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo]; + positionOnCurrentLinkIncludingLane = CVector( + pCurrentLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardY, + pCurrentLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardX, + 0.0f); + positionOnNextLinkIncludingLane = CVector( + pNextLink->GetX() + ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardY, + pNextLink->GetY() - ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardX, + 0.0f); + directionCurrentLinkX = pCurrentLink->GetDirX() * pVehicle->AutoPilot.m_nCurrentDirection; + directionCurrentLinkY = pCurrentLink->GetDirY() * pVehicle->AutoPilot.m_nCurrentDirection; + directionNextLinkX = pNextLink->GetDirX() * pVehicle->AutoPilot.m_nNextDirection; + directionNextLinkY = pNextLink->GetDirY() * pVehicle->AutoPilot.m_nNextDirection; + } +#else + + CCarPathLink* pCurrentLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nCurrentPathNodeInfo]; + CCarPathLink* pNextLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo]; + CVector positionOnCurrentLinkIncludingLane( + pCurrentLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardY, + pCurrentLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardX, + 0.0f); + CVector positionOnNextLinkIncludingLane( + pNextLink->GetX() + ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardY, + pNextLink->GetY() - ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardX, + 0.0f); + float directionCurrentLinkX = pCurrentLink->GetDirX() * pVehicle->AutoPilot.m_nCurrentDirection; + float directionCurrentLinkY = pCurrentLink->GetDirY() * pVehicle->AutoPilot.m_nCurrentDirection; + float directionNextLinkX = pNextLink->GetDirX() * pVehicle->AutoPilot.m_nNextDirection; + float directionNextLinkY = pNextLink->GetDirY() * pVehicle->AutoPilot.m_nNextDirection; + /* We want to make a path between two links that may not have the same forward directions a curve. */ + pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve = CCurves::CalcSpeedScaleFactor( + &positionOnCurrentLinkIncludingLane, + &positionOnNextLinkIncludingLane, + directionCurrentLinkX, directionCurrentLinkY, + directionNextLinkX, directionNextLinkY + ) * (1000.0f / pVehicle->AutoPilot.m_fMaxTrafficSpeed); + pVehicle->AutoPilot.m_nTimeEnteredCurve = CTimer::GetTimeInMilliseconds() - + (0.5f + positionBetweenNodes) * pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve; +#endif + + CVector directionCurrentLink(directionCurrentLinkX, directionCurrentLinkY, 0.0f); + CVector directionNextLink(directionNextLinkX, directionNextLinkY, 0.0f); + CVector positionIncludingCurve; + CVector directionIncludingCurve; + CCurves::CalcCurvePoint( + &positionOnCurrentLinkIncludingLane, + &positionOnNextLinkIncludingLane, + &directionCurrentLink, + &directionNextLink, + GetPositionAlongCurrentCurve(pVehicle), + pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve, + &positionIncludingCurve, + &directionIncludingCurve + ); + CVector vectorBetweenNodes = pCurNode->GetPosition() - pNextNode->GetPosition(); + CVector finalPosition = positionIncludingCurve + vectorBetweenNodes * 2.0f / vectorBetweenNodes.Magnitude(); + finalPosition.z = positionBetweenNodes * pNextNode->GetZ() + + (1.0f - positionBetweenNodes) * pCurNode->GetZ(); + float groundZ = INFINITE_Z; + CColPoint colPoint; + CEntity* pEntity; + if (bBoatGenerated) { + if (!CWaterLevel::GetWaterLevel(finalPosition, &groundZ, true)) { + delete pVehicle; + return; + } + } + else { + if (CWorld::ProcessVerticalLine(finalPosition, 1000.0f, colPoint, pEntity, true, false, false, false, true, false, nil)) + groundZ = colPoint.point.z; + if (CWorld::ProcessVerticalLine(finalPosition, -1000.0f, colPoint, pEntity, true, false, false, false, true, false, nil)) { + if (ABS(colPoint.point.z - finalPosition.z) < ABS(groundZ - finalPosition.z)) + groundZ = colPoint.point.z; + } + } + if (groundZ == INFINITE_Z || ABS(groundZ - finalPosition.z) > 7.0f) { + /* Failed to find ground or too far from expected position. */ + delete pVehicle; + return; + } + if (CModelInfo::IsBoatModel(carModel)) { + finalPosition.z = groundZ; + pVehicle->bExtendedRange = true; + } + else + finalPosition.z = groundZ + pVehicle->GetHeightAboveRoad(); + pVehicle->SetPosition(finalPosition); + pVehicle->SetMoveSpeed(directionIncludingCurve / GAME_SPEED_TO_CARAI_SPEED); + CVector2D speedDifferenceWithTarget = (CVector2D)pVehicle->GetMoveSpeed() - vecPlayerSpeed; + CVector2D distanceToTarget = positionIncludingCurve - vecTargetPos; + switch (carClass) { + case COPS: + pVehicle->SetStatus((pVehicle->AutoPilot.m_nCarMission == MISSION_CRUISE) ? STATUS_SIMPLE : STATUS_PHYSICS); + pVehicle->ChangeLawEnforcerState(1); + break; + case COPS_BOAT: + pVehicle->ChangeLawEnforcerState(1); + pVehicle->SetStatus(STATUS_PHYSICS); + break; + default: + bBoatGenerated ? pVehicle->SetStatus(STATUS_PHYSICS) : pVehicle->SetStatus(STATUS_SIMPLE); + break; + } + CVisibilityPlugins::SetClumpAlpha(pVehicle->GetClump(), 0); + if (!pVehicle->GetIsOnScreen()){ + if ((vecTargetPos - pVehicle->GetPosition()).Magnitude2D() > OFFSCREEN_DESPAWN_RANGE * (pVehicle->bExtendedRange ? EXTENDED_RANGE_DESPAWN_MULTIPLIER : 1.0f)) { + /* Too far away cars that are not visible aren't needed. */ + delete pVehicle; + return; + } + }else{ + if ((vecTargetPos - pVehicle->GetPosition()).Magnitude2D() > TheCamera.GenerationDistMultiplier * (pVehicle->bExtendedRange ? EXTENDED_RANGE_DESPAWN_MULTIPLIER : 1.0f) * ONSCREEN_DESPAWN_RANGE || + (vecTargetPos - pVehicle->GetPosition()).Magnitude2D() < TheCamera.GenerationDistMultiplier * MINIMAL_DISTANCE_TO_SPAWN_ONSCREEN) { + delete pVehicle; + return; + } + if ((TheCamera.GetPosition() - pVehicle->GetPosition()).Magnitude2D() < 82.5f * TheCamera.GenerationDistMultiplier || bTopDownCamera) { + delete pVehicle; + return; + } + if (pVehicle->GetModelIndex() == MI_MARQUIS) { // so marquis can only spawn if player doesn't see it? + delete pVehicle; + return; + } + } + CVehicleModelInfo* pVehicleModel = pVehicle->GetModelInfo(); + float radiusToTest = pVehicleModel->GetColModel()->boundingSphere.radius; + if (testForCollision){ + CWorld::FindObjectsKindaColliding(pVehicle->GetPosition(), radiusToTest + 20.0f, true, &colliding, 2, nil, false, true, false, false, false); + if (colliding){ + delete pVehicle; + return; + } + } + CWorld::FindObjectsKindaColliding(pVehicle->GetPosition(), radiusToTest, true, &colliding, 2, nil, false, true, false, false, false); + if (colliding){ + delete pVehicle; + return; + } + if (speedDifferenceWithTarget.x * distanceToTarget.x + + speedDifferenceWithTarget.y * distanceToTarget.y >= 0.0f){ + delete pVehicle; + return; + } + pVehicleModel->AvoidSameVehicleColour(&pVehicle->m_currentColour1, &pVehicle->m_currentColour2); + CWorld::Add(pVehicle); + if (carClass == COPS || carClass == COPS_BOAT) + CCarAI::AddPoliceCarOccupants(pVehicle); + else { + pVehicle->SetUpDriver(); + int32 passengers = 0; + for (int i = 0; i < pVehicle->m_nNumMaxPassengers; i++) + passengers += (CGeneral::GetRandomNumberInRange(0.0f, 1.0f) < PROBABILITY_OF_PASSENGER_IN_VEHICLE) ? 1 : 0; + if (CModelInfo::IsCarModel(carModel) && (CModelInfo::GetModelInfo(carModel)->GetAnimFileIndex() == CAnimManager::GetAnimationBlockIndex("van") && passengers >= 1)) + passengers = 1; + for (int i = 0; i < passengers; i++) { + CPed* pPassenger = pVehicle->SetupPassenger(i); + if (pPassenger) { + ++CPopulation::ms_nTotalCarPassengerPeds; + pPassenger->bCarPassenger = true; + } + } + } + int nMadDrivers; + switch (pVehicle->GetVehicleAppearance()) { + case VEHICLE_APPEARANCE_BIKE: + nMadDrivers = 30; + break; + case VEHICLE_APPEARANCE_BOAT: + nMadDrivers = 40; + break; + default: + nMadDrivers = 6; + break; + } + if ((CGeneral::GetRandomNumber() & 0x7F) < nMadDrivers || bMadDriversCheat) { + pVehicle->SetStatus(STATUS_PHYSICS); + pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + pVehicle->AutoPilot.m_nCruiseSpeed += 10; + } + if (carClass == COPS) + LastTimeLawEnforcerCreated = CTimer::GetTimeInMilliseconds(); + if (pVehicle->GetModelIndex() == MI_CADDY) { + pVehicle->SetStatus(STATUS_PHYSICS); + pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + } + if (carClass == COPS && pVehicle->GetModelIndex() == MI_VICECHEE) { + CVehicleModelInfo* pVehicleModel = (CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_VICECHEE); + switch (MiamiViceCycle) { + case 0: + pVehicleModel->SetVehicleColour(53, 77); + break; + case 1: + pVehicleModel->SetVehicleColour(15, 77); + break; + case 2: + pVehicleModel->SetVehicleColour(41, 77); + break; + case 3: + pVehicleModel->SetVehicleColour(61, 77); + break; + default: + break; + } + } + if (CGeneral::GetRandomNumberInRange(0.0f, 1.0f) >= (1 - PROBABILITY_OF_DEAD_PED_ACCIDENT)) { + if (CModelInfo::IsCarModel(pVehicle->GetModelIndex()) && !pVehicle->bIsLawEnforcer) { + if (CPopulation::AddDeadPedInFrontOfCar(pVehicle->GetPosition() + pVehicle->GetForward() * DISTANCE_BETWEEN_CAR_AND_DEAD_PED, pVehicle)) { + pVehicle->AutoPilot.m_nCruiseSpeed = 0; + pVehicle->SetMoveSpeed(0.0f, 0.0f, 0.0f); + for (int i = 0; i < pVehicle->m_nNumPassengers; i++) { + if (pVehicle->pPassengers[i]) { + pVehicle->pPassengers[i]->SetObjective(OBJECTIVE_LEAVE_CAR, pVehicle); + pVehicle->pPassengers[i]->m_nLastPedState = PED_WANDER_PATH; + pVehicle->pPassengers[i]->m_vehicleInAccident = pVehicle; + pVehicle->pPassengers[i]->bDeadPedInFrontOfCar = true; + pVehicle->RegisterReference((CEntity**)&pVehicle->pPassengers[i]->m_vehicleInAccident); + } + } + if (pVehicle->pDriver) { + pVehicle->pDriver->SetObjective(OBJECTIVE_LEAVE_CAR, pVehicle); + pVehicle->pDriver->m_nLastPedState = PED_WANDER_PATH; + pVehicle->pDriver->m_vehicleInAccident = pVehicle; + pVehicle->pDriver->bDeadPedInFrontOfCar = true; + pVehicle->RegisterReference((CEntity**)&pVehicle->pDriver->m_vehicleInAccident); + } + } + } + } +} + +bool +CCarCtrl::BoatWithTallMast(int32 mi) +{ + return mi == MI_RIO || mi == MI_TROPIC || mi == MI_MARQUIS; +} + +int32 +CCarCtrl::ChooseBoatModel(int32 rating) +{ + ++NumRequestsOfCarRating[rating]; + return ChooseCarModel(rating); +} + +int32 +CCarCtrl::ChooseBoatRating(CZoneInfo* pZoneInfo) +{ + int rnd = CGeneral::GetRandomNumberInRange(0, 1000); + for (int i = 0; i < NUM_BOAT_CLASSES - 1; i++) { + if (rnd < pZoneInfo->boatThreshold[i]) + return FIRST_BOAT_RATING + i; + } + return FIRST_BOAT_RATING + NUM_BOAT_CLASSES - 1; +} + +int32 +CCarCtrl::ChooseCarRating(CZoneInfo* pZoneInfo) +{ + int rnd = CGeneral::GetRandomNumberInRange(0, 1000); + for (int i = 0; i < NUM_CAR_CLASSES - 1; i++) { + if (rnd < pZoneInfo->carThreshold[i]) + return i; + } + return FIRST_CAR_RATING + NUM_CAR_CLASSES - 1; +} + +int32 +CCarCtrl::ChooseModel(CZoneInfo* pZone, int* pClass) { + int32 model = -1; + int32 i; + for (i = 10; i > 0 && (model == -1 || !CStreaming::HasModelLoaded(model)); i--) { + int rnd = CGeneral::GetRandomNumberInRange(0, 1000); + + if (rnd < pZone->copThreshold) { + *pClass = COPS; + model = ChoosePoliceCarModel(); + continue; + } + + int32 j; + for (j = 0; j < NUM_GANG_CAR_CLASSES; j++) { + if (rnd < pZone->gangThreshold[j]) { + *pClass = j + FIRST_GANG_CAR_RATING; + model = ChooseGangCarModel(j); + break; + } + } + + if (j != NUM_GANG_CAR_CLASSES) + continue; + + *pClass = ChooseCarRating(pZone); + model = ChooseCarModel(*pClass); + } + if (i == 0) + return -1; + return model; +} + +int32 +CCarCtrl::ChooseCarModel(int32 vehclass) +{ + int32 model = -1; + ++NumRequestsOfCarRating[vehclass]; + if (NumOfLoadedCarsOfRating[vehclass] == 0) + return -1; + int32 rnd = CGeneral::GetRandomNumberInRange(0, CarFreqArrays[vehclass][NumOfLoadedCarsOfRating[vehclass] - 1]); + int32 index = 0; + while (rnd > CarFreqArrays[vehclass][index]) + index++; + assert(LoadedCarsArray[vehclass][index]); + return LoadedCarsArray[vehclass][index]; +} + +void +CCarCtrl::AddToLoadedVehicleArray(int32 mi, int32 rating, int32 freq) +{ + LoadedCarsArray[rating][NumOfLoadedCarsOfRating[rating]] = mi; + assert(mi >= 130); + CarFreqArrays[rating][NumOfLoadedCarsOfRating[rating]] = freq; + if (NumOfLoadedCarsOfRating[rating]) + CarFreqArrays[rating][NumOfLoadedCarsOfRating[rating]] += CarFreqArrays[rating][NumOfLoadedCarsOfRating[rating] - 1]; + NumOfLoadedCarsOfRating[rating]++; +} + +void +CCarCtrl::RemoveFromLoadedVehicleArray(int32 mi, int32 rating) +{ + int index = 0; + while (LoadedCarsArray[rating][index] != -1) { + if (LoadedCarsArray[rating][index] == mi) + break; + index++; + } + assert(LoadedCarsArray[rating][index] == mi); + int32 freq = CarFreqArrays[rating][index]; + if (index > 0) + freq -= CarFreqArrays[rating][index - 1]; + while (LoadedCarsArray[rating][index + 1] != -1) { + LoadedCarsArray[rating][index] = LoadedCarsArray[rating][index + 1]; + CarFreqArrays[rating][index] = CarFreqArrays[rating][index + 1] - freq; + index++; + } + --NumOfLoadedCarsOfRating[rating]; +} + +int32 +CCarCtrl::ChooseCarModelToLoad(int32 rating) +{ + return CarArrays[rating][CGeneral::GetRandomNumberInRange(0, TotalNumOfCarsOfRating[rating])]; +} + +int32 +CCarCtrl::ChoosePoliceCarModel(void) +{ + if (FindPlayerPed()->m_pWanted->AreMiamiViceRequired() && +#ifdef FIX_BUGS + (CTimer::GetTimeInMilliseconds() > LastTimeMiamiViceGenerated + 120000 || LastTimeMiamiViceGenerated == 0) && +#else + CTimer::GetTimeInMilliseconds() > LastTimeMiamiViceGenerated + 120000 && +#endif + CStreaming::HasModelLoaded(MI_VICECHEE)) { + switch (MiamiViceCycle) { + case 0: + if (CStreaming::HasModelLoaded(MI_VICE1) && CStreaming::HasModelLoaded(MI_VICE2)) + return MI_VICECHEE; + break; + case 1: + if (CStreaming::HasModelLoaded(MI_VICE3) && CStreaming::HasModelLoaded(MI_VICE4)) + return MI_VICECHEE; + break; + case 2: + if (CStreaming::HasModelLoaded(MI_VICE5) && CStreaming::HasModelLoaded(MI_VICE6)) + return MI_VICECHEE; + break; + case 3: + if (CStreaming::HasModelLoaded(MI_VICE7) && CStreaming::HasModelLoaded(MI_VICE8)) + return MI_VICECHEE; + break; + default: + break; + } + } + if (FindPlayerPed()->m_pWanted->AreSwatRequired() && + CStreaming::HasModelLoaded(MI_ENFORCER) && + CStreaming::HasModelLoaded(MI_POLICE)) + return ((CGeneral::GetRandomNumber() & 0xF) == 0) ? MI_ENFORCER : MI_POLICE; + if (FindPlayerPed()->m_pWanted->AreFbiRequired() && + CStreaming::HasModelLoaded(MI_FBIRANCH) && + CStreaming::HasModelLoaded(MI_FBI)) + return MI_FBIRANCH; + if (FindPlayerPed()->m_pWanted->AreArmyRequired() && + CStreaming::HasModelLoaded(MI_RHINO) && + CStreaming::HasModelLoaded(MI_BARRACKS) && + CStreaming::HasModelLoaded(MI_ARMY)) + return CGeneral::GetRandomTrueFalse() ? MI_BARRACKS : MI_RHINO; + return MI_POLICE; +} + +int32 +CCarCtrl::ChooseGangCarModel(int32 gang) +{ + if (CGangs::HaveGangModelsLoaded(gang)) + return CGangs::GetGangVehicleModel(gang); + return -1; +} + +void +CCarCtrl::AddToCarArray(int32 id, int32 vehclass) +{ + assert(TotalNumOfCarsOfRating[vehclass] < MAX_CAR_MODELS_IN_ARRAY); + CarArrays[vehclass][TotalNumOfCarsOfRating[vehclass]++] = id; +} + +void +CCarCtrl::RemoveDistantCars() +{ + for (int i = CPools::GetVehiclePool()->GetSize()-1; i >= 0; i--) { + CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); + if (!pVehicle) + continue; + PossiblyRemoveVehicle(pVehicle); + if (pVehicle->bCreateRoadBlockPeds){ + if ((pVehicle->GetPosition() - FindPlayerCentreOfWorld(CWorld::PlayerInFocus)).Magnitude2D() < DISTANCE_TO_SPAWN_ROADBLOCK_PEDS) { + CRoadBlocks::GenerateRoadBlockCopsForCar(pVehicle, pVehicle->m_nRoadblockType); + pVehicle->bCreateRoadBlockPeds = false; + } + } + } +} + +void +CCarCtrl::RemoveCarsIfThePoolGetsFull(void) +{ + if ((CTimer::GetFrameCounter() & 7) != 3) + return; + if (CPools::GetVehiclePool()->GetNoOfFreeSpaces() >= 8) + return; + int i = CPools::GetVehiclePool()->GetSize(); + float md = 10000000.f; + CVehicle* pClosestVehicle = nil; + while (i--) { + CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); + if (!pVehicle) + continue; + if (IsThisVehicleInteresting(pVehicle) || pVehicle->bIsLocked) + continue; + if (!pVehicle->CanBeDeleted() || CCranes::IsThisCarBeingTargettedByAnyCrane(pVehicle)) + continue; + float distance = (TheCamera.GetPosition() - pVehicle->GetPosition()).Magnitude(); + if (distance < md) { + md = distance; + pClosestVehicle = pVehicle; + } + } + if (pClosestVehicle) { + CWorld::Remove(pClosestVehicle); + delete pClosestVehicle; + } +} + +void +CCarCtrl::PossiblyRemoveVehicle(CVehicle* pVehicle) +{ +#ifdef FIX_BUGS + if (pVehicle->bIsLocked) + return; +#endif + CVector vecPlayerPos = FindPlayerCentreOfWorld(CWorld::PlayerInFocus); + /* BUG: this variable is initialized only in if-block below but can be used outside of it. */ + if (!IsThisVehicleInteresting(pVehicle) && !pVehicle->bIsLocked && + pVehicle->CanBeDeleted() && !CCranes::IsThisCarBeingTargettedByAnyCrane(pVehicle)){ + if (pVehicle->bFadeOut && CVisibilityPlugins::GetClumpAlpha(pVehicle->GetClump()) == 0){ + CWorld::Remove(pVehicle); + delete pVehicle; + return; + } + float distanceToPlayer = (pVehicle->GetPosition() - vecPlayerPos).Magnitude2D(); + float threshold = OFFSCREEN_DESPAWN_RANGE; +#ifndef EXTENDED_OFFSCREEN_DESPAWN_RANGE + if (pVehicle->GetIsOnScreen() || + TheCamera.Cams[TheCamera.ActiveCam].LookingLeft || + TheCamera.Cams[TheCamera.ActiveCam].LookingRight || + TheCamera.Cams[TheCamera.ActiveCam].LookingBehind || + TheCamera.GetLookDirection() == 0 || + pVehicle->VehicleCreatedBy == PARKED_VEHICLE || + pVehicle->GetModelIndex() == MI_AMBULAN || + pVehicle->GetModelIndex() == MI_FIRETRUCK || + pVehicle->bIsLawEnforcer || + pVehicle->bIsCarParkVehicle || + CTimer::GetTimeInMilliseconds() < pVehicle->m_nSetPieceExtendedRangeTime + ) +#endif + { + threshold = ONSCREEN_DESPAWN_RANGE * TheCamera.GenerationDistMultiplier; + } +#ifndef EXTENDED_OFFSCREEN_DESPAWN_RANGE + if (TheCamera.GetForward().z < -0.9f) + threshold = 70.0f; +#endif + if (pVehicle->bExtendedRange) + threshold *= EXTENDED_RANGE_DESPAWN_MULTIPLIER; + if (distanceToPlayer > threshold && !CGarages::IsPointWithinHideOutGarage(pVehicle->GetPosition())){ + if (pVehicle->GetIsOnScreen()){ + pVehicle->bFadeOut = true; + }else{ + CWorld::Remove(pVehicle); + delete pVehicle; + } + return; + } + } + if ((pVehicle->GetStatus() == STATUS_SIMPLE || pVehicle->GetStatus() == STATUS_PHYSICS && + (pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_STOP_FOR_CARS || pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_STOP_FOR_CARS_IGNORE_LIGHTS)) && + CTimer::GetTimeInMilliseconds() - pVehicle->AutoPilot.m_nTimeToStartMission > 5000 && + !pVehicle->GetIsOnScreen() && + (pVehicle->GetPosition() - vecPlayerPos).Magnitude2D() > 22.0f && + !IsThisVehicleInteresting(pVehicle) && + !pVehicle->bIsLocked && + pVehicle->CanBeDeleted() && + !CTrafficLights::ShouldCarStopForLight(pVehicle, true) && + !CTrafficLights::ShouldCarStopForBridge(pVehicle) && + !CGarages::IsPointWithinHideOutGarage(pVehicle->GetPosition())){ + CWorld::Remove(pVehicle); + delete pVehicle; + return; + } + if (pVehicle->GetStatus() == STATUS_WRECKED) { + if (pVehicle->m_nTimeOfDeath != 0) { + if (CTimer::GetTimeInMilliseconds() > pVehicle->m_nTimeOfDeath + 60000 && + CTimer::GetTimeInMilliseconds() > pVehicle->m_nSetPieceExtendedRangeTime && + !(pVehicle->GetIsOnScreen())) { + if ((pVehicle->GetPosition() - vecPlayerPos).MagnitudeSqr() > SQR(6.5f)) { + if (!CGarages::IsPointWithinHideOutGarage(pVehicle->GetPosition())) { + CWorld::Remove(pVehicle); + delete pVehicle; + } + } + } + } + } +} + +int32 +CCarCtrl::CountCarsOfType(int32 mi) +{ + int32 total = 0; + for (int i = CPools::GetVehiclePool()->GetSize()-1; i >= 0; i--) { + CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); + if (!pVehicle) + continue; + if (pVehicle->GetModelIndex() == mi) + total++; + } + return total; +} + +static CVector GetRandomOffsetForVehicle(CVehicle* pVehicle, bool bNext) +{ + CVector offset; + int32 seed = ((bNext ? pVehicle->AutoPilot.m_nNextPathNodeInfo : pVehicle->AutoPilot.m_nCurrentPathNodeInfo) + pVehicle->m_randomSeed) & 7; + offset.x = (seed - 3) * 0.009f; + offset.y = ((seed >> 3) - 3) * 0.009f; + offset.z = 0.0f; + return offset; +} + +void +CCarCtrl::UpdateCarOnRails(CVehicle* pVehicle) +{ + if (pVehicle->AutoPilot.m_nTempAction == TEMPACT_WAIT){ + pVehicle->SetMoveSpeed(0.0f, 0.0f, 0.0f); + pVehicle->AutoPilot.ModifySpeed(0.0f); + if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTempAction){ + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); + pVehicle->AutoPilot.m_nTimeToStartMission = CTimer::GetTimeInMilliseconds(); + } + return; + } + SlowCarOnRailsDownForTrafficAndLights(pVehicle); + if (pVehicle->AutoPilot.m_nTimeEnteredCurve + pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve <= CTimer::GetTimeInMilliseconds()) + PickNextNodeAccordingStrategy(pVehicle); + if (pVehicle->GetStatus() == STATUS_PHYSICS) + return; + CCarPathLink* pCurrentLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nCurrentPathNodeInfo]; + CCarPathLink* pNextLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo]; + float currentPathLinkForwardX = pCurrentLink->GetDirX() * pVehicle->AutoPilot.m_nCurrentDirection; + float currentPathLinkForwardY = pCurrentLink->GetDirY() * pVehicle->AutoPilot.m_nCurrentDirection; + float nextPathLinkForwardX = pNextLink->GetDirX() * pVehicle->AutoPilot.m_nNextDirection; + float nextPathLinkForwardY = pNextLink->GetDirY() * pVehicle->AutoPilot.m_nNextDirection; + CVector positionOnCurrentLinkIncludingLane( + pCurrentLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardY, + pCurrentLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardX, + 0.0f); + CVector positionOnNextLinkIncludingLane( + pNextLink->GetX() + ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardY, + pNextLink->GetY() - ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardX, + 0.0f); + CVector directionCurrentLink = GetRandomOffsetForVehicle(pVehicle, false); + directionCurrentLink += CVector(currentPathLinkForwardX, currentPathLinkForwardY, 0.0f); + directionCurrentLink.Normalise(); + CVector directionNextLink = GetRandomOffsetForVehicle(pVehicle, true); + directionNextLink += CVector(nextPathLinkForwardX, nextPathLinkForwardY, 0.0f); + directionNextLink.Normalise(); + CVector positionIncludingCurve; + CVector directionIncludingCurve; + CCurves::CalcCurvePoint( + &positionOnCurrentLinkIncludingLane, + &positionOnNextLinkIncludingLane, + &directionCurrentLink, + &directionNextLink, + GetPositionAlongCurrentCurve(pVehicle), + pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve, + &positionIncludingCurve, + &directionIncludingCurve + ); + positionIncludingCurve.z = 15.0f; + DragCarToPoint(pVehicle, &positionIncludingCurve); + pVehicle->SetMoveSpeed(directionIncludingCurve / GAME_SPEED_TO_CARAI_SPEED); +} + +float +CCarCtrl::FindMaximumSpeedForThisCarInTraffic(CVehicle* pVehicle) +{ + if (pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_AVOID_CARS || + pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_PLOUGH_THROUGH) + return pVehicle->AutoPilot.GetCruiseSpeed(); + float left = pVehicle->GetPosition().x - DISTANCE_TO_SCAN_FOR_DANGER; + float right = pVehicle->GetPosition().x + DISTANCE_TO_SCAN_FOR_DANGER; + float top = pVehicle->GetPosition().y - DISTANCE_TO_SCAN_FOR_DANGER; + float bottom = pVehicle->GetPosition().y + DISTANCE_TO_SCAN_FOR_DANGER; + int xstart = Max(0, CWorld::GetSectorIndexX(left)); + int xend = Min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(right)); + int ystart = Max(0, CWorld::GetSectorIndexY(top)); + int yend = Min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(bottom)); + assert(xstart <= xend); + assert(ystart <= yend); + + float maxSpeed = pVehicle->AutoPilot.GetCruiseSpeed(); + + CWorld::AdvanceCurrentScanCode(); + + for (int y = ystart; y <= yend; y++){ + for (int x = xstart; x <= xend; x++){ + CSector* s = CWorld::GetSector(x, y); + SlowCarDownForCarsSectorList(s->m_lists[ENTITYLIST_VEHICLES], pVehicle, left, top, right, bottom, &maxSpeed, pVehicle->AutoPilot.GetCruiseSpeed()); + SlowCarDownForCarsSectorList(s->m_lists[ENTITYLIST_VEHICLES_OVERLAP], pVehicle, left, top, right, bottom, &maxSpeed, pVehicle->AutoPilot.GetCruiseSpeed()); + SlowCarDownForPedsSectorList(s->m_lists[ENTITYLIST_PEDS], pVehicle, left, top, right, bottom, &maxSpeed, pVehicle->AutoPilot.GetCruiseSpeed()); + SlowCarDownForPedsSectorList(s->m_lists[ENTITYLIST_PEDS_OVERLAP], pVehicle, left, top, right, bottom, &maxSpeed, pVehicle->AutoPilot.GetCruiseSpeed()); + } + } + pVehicle->bWarnedPeds = true; + if (pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_STOP_FOR_CARS || pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_STOP_FOR_CARS_IGNORE_LIGHTS) + return maxSpeed; + return (maxSpeed + pVehicle->AutoPilot.GetCruiseSpeed()) / 2; +} + +void +CCarCtrl::ScanForPedDanger(CVehicle* pVehicle) +{ + bool storedSlowDownFlag = pVehicle->AutoPilot.m_bSlowedDownBecauseOfPeds; + float left = pVehicle->GetPosition().x - DISTANCE_TO_SCAN_FOR_PED_DANGER; + float right = pVehicle->GetPosition().x + DISTANCE_TO_SCAN_FOR_PED_DANGER; + float top = pVehicle->GetPosition().y - DISTANCE_TO_SCAN_FOR_PED_DANGER; + float bottom = pVehicle->GetPosition().y + DISTANCE_TO_SCAN_FOR_PED_DANGER; + int xstart = Max(0, CWorld::GetSectorIndexX(left)); + int xend = Min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(right)); + int ystart = Max(0, CWorld::GetSectorIndexY(top)); + int yend = Min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(bottom)); + assert(xstart <= xend); + assert(ystart <= yend); + + float maxSpeed = pVehicle->AutoPilot.m_nCruiseSpeed; + + CWorld::AdvanceCurrentScanCode(); + + for (int y = ystart; y <= yend; y++) { + for (int x = xstart; x <= xend; x++) { + CSector* s = CWorld::GetSector(x, y); + SlowCarDownForPedsSectorList(s->m_lists[ENTITYLIST_PEDS], pVehicle, left, top, right, bottom, &maxSpeed, pVehicle->AutoPilot.m_nCruiseSpeed); + SlowCarDownForPedsSectorList(s->m_lists[ENTITYLIST_PEDS_OVERLAP], pVehicle, left, top, right, bottom, &maxSpeed, pVehicle->AutoPilot.m_nCruiseSpeed); + } + } + pVehicle->bWarnedPeds = true; + pVehicle->AutoPilot.m_bSlowedDownBecauseOfPeds = storedSlowDownFlag; +} + +void +CCarCtrl::SlowCarOnRailsDownForTrafficAndLights(CVehicle* pVehicle) +{ + float maxSpeed; + if (CTrafficLights::ShouldCarStopForLight(pVehicle, false) || CTrafficLights::ShouldCarStopForBridge(pVehicle)){ + CCarAI::CarHasReasonToStop(pVehicle); + maxSpeed = 0.0f; + }else{ + maxSpeed = FindMaximumSpeedForThisCarInTraffic(pVehicle); + } + float curSpeed = pVehicle->AutoPilot.m_fMaxTrafficSpeed; + if (maxSpeed >= curSpeed){ + if (maxSpeed > curSpeed) + pVehicle->AutoPilot.ModifySpeed(Min(maxSpeed, curSpeed + 0.05f * CTimer::GetTimeStep())); + }else if (curSpeed != 0.0f) { + if (curSpeed < 0.1f) + pVehicle->AutoPilot.ModifySpeed(0.0f); + else + pVehicle->AutoPilot.ModifySpeed(Max(maxSpeed, curSpeed - 0.7f * CTimer::GetTimeStep())); + } +} + +void CCarCtrl::SlowCarDownForPedsSectorList(CPtrList& lst, CVehicle* pVehicle, float x_inf, float y_inf, float x_sup, float y_sup, float* pSpeed, float curSpeed) +{ + float frontOffset = pVehicle->GetModelInfo()->GetColModel()->boundingBox.max.y; + float frontSafe = frontOffset + SAFE_DISTANCE_TO_PED; + for (CPtrNode* pNode = lst.first; pNode != nil; pNode = pNode->next){ + CPed* pPed = (CPed*)pNode->item; + if (pPed->m_scanCode == CWorld::GetCurrentScanCode()) + continue; + if (!pPed->bUsesCollision) + continue; + pPed->m_scanCode = CWorld::GetCurrentScanCode(); + CVector vecPedPos = pPed->GetPosition(); + if (vecPedPos.x < x_inf || vecPedPos.x > x_sup) + continue; + if (vecPedPos.y < y_inf || vecPedPos.y > y_sup) + continue; + if (ABS(vecPedPos.z - pVehicle->GetPosition().z) >= 4.0f) + continue; + CVector vecToPed = vecPedPos - pVehicle->GetPosition(); + float dotDirection = DotProduct(pVehicle->GetForward(), vecToPed); + float dotVelocity = DotProduct(pVehicle->GetForward(), pVehicle->GetMoveSpeed()); + if (dotDirection <= frontOffset) /* If already run him over, don't care */ + continue; + float distanceUntilHit = dotDirection - frontOffset; + float movementTowardsPedPerSecond = GAME_SPEED_TO_METERS_PER_SECOND * dotVelocity; + if (4 * movementTowardsPedPerSecond <= distanceUntilHit) + /* If car isn't projected to hit a ped in 4 seconds, don't care */ + continue; + float sidewaysDistance = ABS(DotProduct(pVehicle->GetRight(), vecToPed)); + float sideLength = pVehicle->GetModelInfo()->GetColModel()->boundingBox.max.x; + if (pVehicle->m_vehType == VEHICLE_TYPE_BIKE) + sideLength *= 1.6f; + if (sideLength + 0.5f < sidewaysDistance) + /* If car is far enough taking side into account, don't care */ + continue; + if (pPed->IsPed()){ /* ...how can it not be? */ + if (pPed->GetPedState() != PED_STEP_AWAY && pPed->GetPedState() != PED_DIVE_AWAY){ + if (distanceUntilHit < movementTowardsPedPerSecond){ + /* Very close. Time to evade. */ + if (pVehicle->GetModelIndex() == MI_RCBANDIT){ + if (dotVelocity * GAME_SPEED_TO_METERS_PER_SECOND / 2 > distanceUntilHit) + pPed->SetEvasiveStep(pVehicle, 0); + }else if (dotVelocity > 0.3f) { + if (sideLength + 0.1f < sidewaysDistance) + pPed->SetEvasiveStep(pVehicle, 0); + else + pPed->SetEvasiveDive(pVehicle, 0); + }else if (dotVelocity > 0.1f) { + if (sideLength - 0.5f < sidewaysDistance) + pPed->SetEvasiveStep(pVehicle, 0); + else + pPed->SetEvasiveDive(pVehicle, 0); + } + }else{ + /* Relatively safe but annoying. */ + if (pVehicle->GetStatus() == STATUS_PLAYER && + pPed->GetPedState() != PED_FLEE_ENTITY && + pPed->CharCreatedBy == RANDOM_CHAR){ + float angleCarToPed = CGeneral::GetRadianAngleBetweenPoints( + pVehicle->GetPosition().x, pVehicle->GetPosition().y, + pPed->GetPosition().x, pPed->GetPosition().y + ); + angleCarToPed = CGeneral::LimitRadianAngle(angleCarToPed); + pPed->m_headingRate = CGeneral::LimitRadianAngle(pPed->m_headingRate); + float visibilityAngle = ABS(angleCarToPed - pPed->m_headingRate); + if (visibilityAngle > PI) + visibilityAngle = TWOPI - visibilityAngle; + if (visibilityAngle < HALFPI || pVehicle->m_nCarHornTimer){ + /* if ped sees the danger or if car horn is on */ + pPed->SetFlee(pVehicle, 2000); + pPed->bUsePedNodeSeek = false; + pPed->SetMoveState(PEDMOVE_RUN); + } + }else{ + CPlayerPed* pPlayerPed = (CPlayerPed*)pPed; + if (pPlayerPed->IsPlayer() && dotDirection < frontSafe && + pPlayerPed->IsPedInControl() && + pPlayerPed->m_fMoveSpeed < 1.0f && !pPlayerPed->bIsLooking && + CTimer::GetTimeInMilliseconds() > pPlayerPed->m_lookTimer) { + pPlayerPed->AnnoyPlayerPed(false); + pPlayerPed->SetLookFlag(pVehicle, true); + pPlayerPed->SetLookTimer(1500); + if (pPlayerPed->GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED || + pPlayerPed->GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT || + pPlayerPed->GetWeapon()->m_eWeaponType == WEAPONTYPE_COLT45 || + pPlayerPed->GetWeapon()->m_eWeaponType == WEAPONTYPE_UZI) { + pPlayerPed->bShakeFist = true; + } + } + } + } + } + } + /* Ped stuff done. Now vehicle stuff. */ + if (distanceUntilHit < 10.0f){ + if (pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_STOP_FOR_CARS || + pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_SLOW_DOWN_FOR_CARS){ + *pSpeed = Min(*pSpeed, ABS(distanceUntilHit - 1.0f) / 10.0f * curSpeed); + pVehicle->AutoPilot.m_bSlowedDownBecauseOfPeds = true; + if (distanceUntilHit < 2.0f){ + pVehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT; + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 3000; + } + } + } + } +} + +void CCarCtrl::SlowCarDownForCarsSectorList(CPtrList& lst, CVehicle* pVehicle, float x_inf, float y_inf, float x_sup, float y_sup, float* pSpeed, float curSpeed) +{ + for (CPtrNode* pNode = lst.first; pNode != nil; pNode = pNode->next){ + CVehicle* pTestVehicle = (CVehicle*)pNode->item; + if (pVehicle == pTestVehicle) + continue; + if (pTestVehicle->m_scanCode == CWorld::GetCurrentScanCode()) + continue; + if (!pTestVehicle->bUsesCollision) + continue; + pTestVehicle->m_scanCode = CWorld::GetCurrentScanCode(); + CVector boundCenter = pTestVehicle->GetBoundCentre(); + if (boundCenter.x < x_inf || boundCenter.x > x_sup) + continue; + if (boundCenter.y < y_inf || boundCenter.y > y_sup) + continue; + if (Abs(boundCenter.z - pVehicle->GetPosition().z) < 5.0f) + SlowCarDownForOtherCar(pTestVehicle, pVehicle, pSpeed, curSpeed); + } +} + +void CCarCtrl::SlowCarDownForOtherCar(CEntity* pOtherEntity, CVehicle* pVehicle, float* pSpeed, float curSpeed) +{ + CVector forwardA = pVehicle->GetForward(); + ((CVector2D)forwardA).Normalise(); + if (DotProduct2D(pOtherEntity->GetPosition() - pVehicle->GetPosition(), forwardA) < 0.0f) + return; + CVector forwardB = pOtherEntity->GetForward(); + ((CVector2D)forwardB).Normalise(); + forwardA.z = forwardB.z = 0.0f; + CVehicle* pOtherVehicle = (CVehicle*)pOtherEntity; + /* why is the argument CEntity if it's always CVehicle anyway and is casted? */ + float speedOtherX = GAME_SPEED_TO_CARAI_SPEED * pOtherVehicle->GetMoveSpeed().x; + float speedOtherY = GAME_SPEED_TO_CARAI_SPEED * pOtherVehicle->GetMoveSpeed().y; + float projectionX = speedOtherX - forwardA.x * curSpeed; + float projectionY = speedOtherY - forwardA.y * curSpeed; + float proximityA = TestCollisionBetween2MovingRects(pOtherVehicle, pVehicle, projectionX, projectionY, &forwardA, &forwardB, 0); + float proximityB = TestCollisionBetween2MovingRects(pVehicle, pOtherVehicle, -projectionX, -projectionY, &forwardB, &forwardA, 1); + float minProximity = Min(proximityA, proximityB); + if (minProximity >= 0.0f && minProximity < 1.5f){ + minProximity = Max(0.0f, (minProximity - 0.2f) / 1.3f); + pVehicle->AutoPilot.m_bSlowedDownBecauseOfCars = true; + *pSpeed = Min(*pSpeed, minProximity * curSpeed); + } + if (minProximity >= 0.0f && minProximity < 0.5f && pOtherEntity->IsVehicle() && + CTimer::GetTimeInMilliseconds() - pVehicle->AutoPilot.m_nTimeToStartMission > 15000 && + CTimer::GetTimeInMilliseconds() - pOtherVehicle->AutoPilot.m_nTimeToStartMission > 15000){ + /* If cars are standing for 15 seconds, annoy one of them and make avoid cars. */ + if (pOtherEntity != FindPlayerVehicle() && + DotProduct2D(pVehicle->GetForward(), pOtherVehicle->GetForward()) < -0.5f && + pVehicle < pOtherVehicle){ /* that comparasion though... */ + *pSpeed = Max(curSpeed / 5, *pSpeed); + if (pVehicle->GetStatus() == STATUS_SIMPLE){ + pVehicle->SetStatus(STATUS_PHYSICS); + SwitchVehicleToRealPhysics(pVehicle); + } + pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 1000; + } + } +} + +float CCarCtrl::TestCollisionBetween2MovingRects(CVehicle* pVehicleA, CVehicle* pVehicleB, float projectionX, float projectionY, CVector* pForwardA, CVector* pForwardB, uint8 id) +{ + CVector2D vecBToA = pVehicleA->GetPosition() - pVehicleB->GetPosition(); + float lenB = pVehicleB->GetModelInfo()->GetColModel()->boundingBox.max.y; + float widthB = pVehicleB->GetModelInfo()->GetColModel()->boundingBox.max.x; + float backLenB = -pVehicleB->GetModelInfo()->GetColModel()->boundingBox.min.y; + float lenA = pVehicleA->GetModelInfo()->GetColModel()->boundingBox.max.y; + float widthA = pVehicleA->GetModelInfo()->GetColModel()->boundingBox.max.x; + float backLenA = -pVehicleA->GetModelInfo()->GetColModel()->boundingBox.min.y; + float proximity = 1.0f; + float fullWidthB = 2.0f * widthB; + float fullLenB = lenB + backLenB; + for (int i = 0; i < 4; i++){ + float testedOffsetX; + float testedOffsetY; + switch (i) { + case 0: /* Front right corner */ + testedOffsetX = vecBToA.x + widthA * pForwardB->y + lenA * pForwardB->x; + testedOffsetY = vecBToA.y + lenA * pForwardB->y - widthA * pForwardB->x; + break; + case 1: /* Front left corner */ + testedOffsetX = vecBToA.x + -widthA * pForwardB->x + lenA * pForwardB->x; + testedOffsetY = vecBToA.y + lenA * pForwardB->y + widthA * pForwardB->x; + break; + case 2: /* Rear right corner */ + testedOffsetX = vecBToA.x + widthA * pForwardB->y - backLenA * pForwardB->x; + testedOffsetY = vecBToA.y - backLenA * pForwardB->y - widthA * pForwardB->x; + break; + case 3: /* Rear left corner */ + testedOffsetX = vecBToA.x - widthA * pForwardB->y - backLenA * pForwardB->x; + testedOffsetY = vecBToA.y - backLenA * pForwardB->y + widthA * pForwardB->x; + break; + default: + break; + } + /* Testing width collision */ + float baseWidthProximity = 0.0f; + float fullWidthProximity = 1.0f; + float widthDistance = testedOffsetX * pForwardA->y - testedOffsetY * pForwardA->x; + float widthProjection = projectionX * pForwardA->y - projectionY * pForwardA->x; + if (widthDistance > widthB){ + if (widthProjection < 0.0f){ + float proximityWidth = -(widthDistance - widthB) / widthProjection; + if (proximityWidth < 1.0f){ + baseWidthProximity = proximityWidth; + fullWidthProximity = Min(1.0f, proximityWidth - fullWidthB / widthProjection); + }else{ + baseWidthProximity = 1.0f; + } + }else{ + baseWidthProximity = 1.0f; + fullWidthProximity = 1.0f; + } + }else if (widthDistance < -widthB){ + if (widthProjection > 0.0f) { + float proximityWidth = -(widthDistance + widthB) / widthProjection; + if (proximityWidth < 1.0f) { + baseWidthProximity = proximityWidth; + fullWidthProximity = Min(1.0f, proximityWidth + fullWidthB / widthProjection); + } + else { + baseWidthProximity = 1.0f; + } + } + else { + baseWidthProximity = 1.0f; + fullWidthProximity = 1.0f; + } + }else if (widthProjection > 0.0f){ + fullWidthProximity = (widthB - widthDistance) / widthProjection; + }else if (widthProjection < 0.0f){ + fullWidthProximity = -(widthB + widthDistance) / widthProjection; + } + /* Testing length collision */ + float baseLengthProximity = 0.0f; + float fullLengthProximity = 1.0f; + float lenDistance = testedOffsetX * pForwardA->x + testedOffsetY * pForwardA->y; + float lenProjection = projectionX * pForwardA->x + projectionY * pForwardA->y; + if (lenDistance > lenB) { + if (lenProjection < 0.0f) { + float proximityLength = -(lenDistance - lenB) / lenProjection; + if (proximityLength < 1.0f) { + baseLengthProximity = proximityLength; + fullLengthProximity = Min(1.0f, proximityLength - fullLenB / lenProjection); + } + else { + baseLengthProximity = 1.0f; + } + } + else { + baseLengthProximity = 1.0f; + fullLengthProximity = 1.0f; + } + } + else if (lenDistance < -backLenB) { + if (lenProjection > 0.0f) { + float proximityLength = -(lenDistance + backLenB) / lenProjection; + if (proximityLength < 1.0f) { + baseLengthProximity = proximityLength; + fullLengthProximity = Min(1.0f, proximityLength + fullLenB / lenProjection); + } + else { + baseLengthProximity = 1.0f; + } + } + else { + baseLengthProximity = 1.0f; + fullLengthProximity = 1.0f; + } + } + else if (lenProjection > 0.0f) { + fullLengthProximity = (lenB - lenDistance) / lenProjection; + } + else if (lenProjection < 0.0f) { + fullLengthProximity = -(backLenB + lenDistance) / lenProjection; + } + float baseProximity = Max(baseWidthProximity, baseLengthProximity); + if (baseProximity < fullWidthProximity && baseProximity < fullLengthProximity) + proximity = Min(proximity, baseProximity); + } + return proximity; +} + +float CCarCtrl::FindAngleToWeaveThroughTraffic(CVehicle* pVehicle, CPhysical* pTarget, float angleToTarget, float angleForward) +{ + float distanceToTest = Min(2.0f, pVehicle->GetMoveSpeed().Magnitude2D() / 0.4f + 1.0f) * 12.0f; + float left = pVehicle->GetPosition().x - distanceToTest; + float right = pVehicle->GetPosition().x + distanceToTest; + float top = pVehicle->GetPosition().y - distanceToTest; + float bottom = pVehicle->GetPosition().y + distanceToTest; + int xstart = Max(0, CWorld::GetSectorIndexX(left)); + int xend = Min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(right)); + int ystart = Max(0, CWorld::GetSectorIndexY(top)); + int yend = Min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(bottom)); + assert(xstart <= xend); + assert(ystart <= yend); + + float angleToWeaveLeft = angleToTarget; + float angleToWeaveRight = angleToTarget; + + CWorld::AdvanceCurrentScanCode(); + + float angleToWeaveLeftLastIteration = -9999.9f; + float angleToWeaveRightLastIteration = -9999.9f; + + while (angleToWeaveLeft != angleToWeaveLeftLastIteration || + angleToWeaveRight != angleToWeaveRightLastIteration){ + angleToWeaveLeftLastIteration = angleToWeaveLeft; + angleToWeaveRightLastIteration = angleToWeaveRight; + for (int y = ystart; y <= yend; y++) { + for (int x = xstart; x <= xend; x++) { + CSector* s = CWorld::GetSector(x, y); + WeaveThroughCarsSectorList(s->m_lists[ENTITYLIST_VEHICLES], pVehicle, pTarget, + left, top, right, bottom, &angleToWeaveLeft, &angleToWeaveRight); + WeaveThroughCarsSectorList(s->m_lists[ENTITYLIST_VEHICLES_OVERLAP], pVehicle, pTarget, + left, top, right, bottom, &angleToWeaveLeft, &angleToWeaveRight); + WeaveThroughPedsSectorList(s->m_lists[ENTITYLIST_PEDS], pVehicle, pTarget, + left, top, right, bottom, &angleToWeaveLeft, &angleToWeaveRight); + WeaveThroughPedsSectorList(s->m_lists[ENTITYLIST_PEDS_OVERLAP], pVehicle, pTarget, + left, top, right, bottom, &angleToWeaveLeft, &angleToWeaveRight); + WeaveThroughObjectsSectorList(s->m_lists[ENTITYLIST_OBJECTS], pVehicle, + left, top, right, bottom, &angleToWeaveLeft, &angleToWeaveRight); + WeaveThroughObjectsSectorList(s->m_lists[ENTITYLIST_OBJECTS_OVERLAP], pVehicle, + left, top, right, bottom, &angleToWeaveLeft, &angleToWeaveRight); + } + } + } + float angleDiffFromActualToTarget = LimitRadianAngle(angleForward - angleToTarget); + float angleToBisectActualToTarget = LimitRadianAngle(angleToTarget + angleDiffFromActualToTarget / 2); + float angleDiffLeft = LimitRadianAngle(angleToWeaveLeft - angleToBisectActualToTarget); + angleDiffLeft = ABS(angleDiffLeft); + float angleDiffRight = LimitRadianAngle(angleToWeaveRight - angleToBisectActualToTarget); + angleDiffRight = ABS(angleDiffRight); + if (angleDiffLeft > HALFPI && angleDiffRight > HALFPI) + return angleToBisectActualToTarget; + if (ABS(angleDiffLeft - angleDiffRight) < 0.08f) + return angleToWeaveRight; + return angleDiffLeft < angleDiffRight ? angleToWeaveLeft : angleToWeaveRight; +} + +void CCarCtrl::WeaveThroughCarsSectorList(CPtrList& lst, CVehicle* pVehicle, CPhysical* pTarget, float x_inf, float y_inf, float x_sup, float y_sup, float* pAngleToWeaveLeft, float* pAngleToWeaveRight) +{ + for (CPtrNode* pNode = lst.first; pNode != nil; pNode = pNode->next) { + CVehicle* pTestVehicle = (CVehicle*)pNode->item; + if (pTestVehicle->m_scanCode == CWorld::GetCurrentScanCode()) + continue; + if (!pTestVehicle->bUsesCollision) + continue; + if (pTestVehicle == pTarget) + continue; + pTestVehicle->m_scanCode = CWorld::GetCurrentScanCode(); + if (pTestVehicle->GetBoundCentre().x < x_inf || pTestVehicle->GetBoundCentre().x > x_sup) + continue; + if (pTestVehicle->GetBoundCentre().y < y_inf || pTestVehicle->GetBoundCentre().y > y_sup) + continue; + if (Abs(pTestVehicle->GetPosition().z - pVehicle->GetPosition().z) >= VEHICLE_HEIGHT_DIFF_TO_CONSIDER_WEAVING) + continue; + if (pTestVehicle != pVehicle && (!pVehicle->bPartOfConvoy || !pTestVehicle->bPartOfConvoy)) + WeaveForOtherCar(pTestVehicle, pVehicle, pAngleToWeaveLeft, pAngleToWeaveRight); + } +} + +void CCarCtrl::WeaveForOtherCar(CEntity* pOtherEntity, CVehicle* pVehicle, float* pAngleToWeaveLeft, float* pAngleToWeaveRight) +{ + CVehicle* pOtherCar = (CVehicle*)pOtherEntity; + if (pVehicle->AutoPilot.m_nCarMission == MISSION_RAMPLAYER_CLOSE && pOtherEntity == FindPlayerVehicle()) + return; + if (pVehicle->AutoPilot.m_nCarMission == MISSION_RAMCAR_CLOSE && pOtherEntity == pVehicle->AutoPilot.m_pTargetCar) + return; + CVector2D vecDiff = pOtherCar->GetPosition() - pVehicle->GetPosition(); + float angleBetweenVehicles = CGeneral::GetATanOfXY(vecDiff.x, vecDiff.y); + float distance = vecDiff.Magnitude(); + if (distance < 1.0f) + return; + if (DotProduct2D(pVehicle->GetMoveSpeed() - pOtherCar->GetMoveSpeed(), vecDiff) * 110.0f - + pOtherCar->GetColModel()->boundingSphere.radius - + pVehicle->GetColModel()->boundingSphere.radius < distance) + return; + CVector2D forward = pVehicle->GetForward(); + forward.Normalise(); + float forwardAngle = CGeneral::GetATanOfXY(forward.x, forward.y); + float angleDiff = angleBetweenVehicles - forwardAngle; + float lenProjection = ABS(pOtherCar->GetColModel()->boundingBox.max.y * Sin(angleDiff)); + float widthProjection = ABS(pOtherCar->GetColModel()->boundingBox.max.x * Cos(angleDiff)); + float lengthToEvade = (2 * (lenProjection + widthProjection) + WIDTH_COEF_TO_WEAVE_SAFELY * 2 * pVehicle->GetColModel()->boundingBox.max.x) / distance; + float diffToLeftAngle = LimitRadianAngle(angleBetweenVehicles - *pAngleToWeaveLeft); + diffToLeftAngle = ABS(diffToLeftAngle); + float angleToWeave = lengthToEvade / 2; + if (diffToLeftAngle < angleToWeave){ + *pAngleToWeaveLeft = angleBetweenVehicles - angleToWeave; + while (*pAngleToWeaveLeft < -PI) + *pAngleToWeaveLeft += TWOPI; + } + float diffToRightAngle = LimitRadianAngle(angleBetweenVehicles - *pAngleToWeaveRight); + diffToRightAngle = ABS(diffToRightAngle); + if (diffToRightAngle < angleToWeave){ + *pAngleToWeaveRight = angleBetweenVehicles + angleToWeave; + while (*pAngleToWeaveRight > PI) + *pAngleToWeaveRight -= TWOPI; + } +} + +void CCarCtrl::WeaveThroughPedsSectorList(CPtrList& lst, CVehicle* pVehicle, CPhysical* pTarget, float x_inf, float y_inf, float x_sup, float y_sup, float* pAngleToWeaveLeft, float* pAngleToWeaveRight) +{ + for (CPtrNode* pNode = lst.first; pNode != nil; pNode = pNode->next) { + CPed* pPed = (CPed*)pNode->item; + if (pPed->m_scanCode == CWorld::GetCurrentScanCode()) + continue; + if (!pPed->bUsesCollision) + continue; + if (pPed == pTarget) + continue; + pPed->m_scanCode = CWorld::GetCurrentScanCode(); + if (pPed->GetPosition().x < x_inf || pPed->GetPosition().x > x_sup) + continue; + if (pPed->GetPosition().y < y_inf || pPed->GetPosition().y > y_sup) + continue; + if (Abs(pPed->GetPosition().z - pVehicle->GetPosition().z) >= PED_HEIGHT_DIFF_TO_CONSIDER_WEAVING) + continue; + if (pPed->m_pCurSurface != pVehicle && pPed->m_attachedTo != pVehicle) + WeaveForPed(pPed, pVehicle, pAngleToWeaveLeft, pAngleToWeaveRight); + } + +} +void CCarCtrl::WeaveForPed(CEntity* pOtherEntity, CVehicle* pVehicle, float* pAngleToWeaveLeft, float* pAngleToWeaveRight) +{ + if (pVehicle->AutoPilot.m_nCarMission == MISSION_RAMPLAYER_CLOSE && pOtherEntity == FindPlayerPed()) + return; + CPed* pPed = (CPed*)pOtherEntity; + CVector2D vecDiff = pPed->GetPosition() - pVehicle->GetPosition(); + float angleBetweenVehicleAndPed = CGeneral::GetATanOfXY(vecDiff.x, vecDiff.y); + float distance = vecDiff.Magnitude(); + float lengthToEvade = (WIDTH_COEF_TO_WEAVE_SAFELY * 2 * pVehicle->GetColModel()->boundingBox.max.x + PED_WIDTH_TO_WEAVE) / distance; + float diffToLeftAngle = LimitRadianAngle(angleBetweenVehicleAndPed - *pAngleToWeaveLeft); + diffToLeftAngle = ABS(diffToLeftAngle); + float angleToWeave = lengthToEvade / 2; + if (diffToLeftAngle < angleToWeave) { + *pAngleToWeaveLeft = angleBetweenVehicleAndPed - angleToWeave; + while (*pAngleToWeaveLeft < -PI) + *pAngleToWeaveLeft += TWOPI; + } + float diffToRightAngle = LimitRadianAngle(angleBetweenVehicleAndPed - *pAngleToWeaveRight); + diffToRightAngle = ABS(diffToRightAngle); + if (diffToRightAngle < angleToWeave) { + *pAngleToWeaveRight = angleBetweenVehicleAndPed + angleToWeave; + while (*pAngleToWeaveRight > PI) + *pAngleToWeaveRight -= TWOPI; + } +} + +void CCarCtrl::WeaveThroughObjectsSectorList(CPtrList& lst, CVehicle* pVehicle, float x_inf, float y_inf, float x_sup, float y_sup, float* pAngleToWeaveLeft, float* pAngleToWeaveRight) +{ + for (CPtrNode* pNode = lst.first; pNode != nil; pNode = pNode->next) { + CObject* pObject = (CObject*)pNode->item; + if (pObject->m_scanCode == CWorld::GetCurrentScanCode()) + continue; + if (!pObject->bUsesCollision) + continue; + pObject->m_scanCode = CWorld::GetCurrentScanCode(); + if (pObject->GetPosition().x < x_inf || pObject->GetPosition().x > x_sup) + continue; + if (pObject->GetPosition().y < y_inf || pObject->GetPosition().y > y_sup) + continue; + if (Abs(pObject->GetPosition().z - pVehicle->GetPosition().z) >= OBJECT_HEIGHT_DIFF_TO_CONSIDER_WEAVING) + continue; + if (pObject->GetUp().z > 0.9f) + WeaveForObject(pObject, pVehicle, pAngleToWeaveLeft, pAngleToWeaveRight); + } +} + +void CCarCtrl::WeaveForObject(CEntity* pOtherEntity, CVehicle* pVehicle, float* pAngleToWeaveLeft, float* pAngleToWeaveRight) +{ + float rightCoef; + float forwardCoef; + if (pOtherEntity->GetModelIndex() == MI_TRAFFICLIGHTS){ + rightCoef = 2.957f; + forwardCoef = 0.147f; + }else if (pOtherEntity->GetModelIndex() == MI_SINGLESTREETLIGHTS1){ + rightCoef = 0.744f; + forwardCoef = 0.0f; + }else if (pOtherEntity->GetModelIndex() == MI_SINGLESTREETLIGHTS2){ + rightCoef = 0.043f; + forwardCoef = 0.0f; + }else if (pOtherEntity->GetModelIndex() == MI_SINGLESTREETLIGHTS3){ + rightCoef = 1.143f; + forwardCoef = 0.145f; + }else if (pOtherEntity->GetModelIndex() == MI_DOUBLESTREETLIGHTS){ + rightCoef = 0.0f; + forwardCoef = -0.048f; + }else if (IsTreeModel(pOtherEntity->GetModelIndex())){ + rightCoef = 0.0f; + forwardCoef = 0.0f; + }else if (pOtherEntity->GetModelIndex() == MI_STREETLAMP1 || pOtherEntity->GetModelIndex() == MI_STREETLAMP2){ + rightCoef = 0.0f; + forwardCoef = 0.0f; + }else + return; + CObject* pObject = (CObject*)pOtherEntity; + CVector2D vecDiff = pObject->GetPosition() + + rightCoef * pObject->GetRight() + + forwardCoef * pObject->GetForward() - + pVehicle->GetPosition(); + float angleBetweenVehicleAndObject = CGeneral::GetATanOfXY(vecDiff.x, vecDiff.y); + float distance = vecDiff.Magnitude(); + float lengthToEvade = (WIDTH_COEF_TO_WEAVE_SAFELY * 2 * pVehicle->GetColModel()->boundingBox.max.x + OBJECT_WIDTH_TO_WEAVE) / distance; + float diffToLeftAngle = LimitRadianAngle(angleBetweenVehicleAndObject - *pAngleToWeaveLeft); + diffToLeftAngle = ABS(diffToLeftAngle); + float angleToWeave = lengthToEvade / 2; + if (diffToLeftAngle < angleToWeave) { + *pAngleToWeaveLeft = angleBetweenVehicleAndObject - angleToWeave; + while (*pAngleToWeaveLeft < -PI) + *pAngleToWeaveLeft += TWOPI; + } + float diffToRightAngle = LimitRadianAngle(angleBetweenVehicleAndObject - *pAngleToWeaveRight); + diffToRightAngle = ABS(diffToRightAngle); + if (diffToRightAngle < angleToWeave) { + *pAngleToWeaveRight = angleBetweenVehicleAndObject + angleToWeave; + while (*pAngleToWeaveRight > PI) + *pAngleToWeaveRight -= TWOPI; + } +} + +bool CCarCtrl::PickNextNodeAccordingStrategy(CVehicle* pVehicle) +{ + pVehicle->AutoPilot.m_nCruiseSpeedMultiplierType = ThePaths.m_pathNodes[pVehicle->AutoPilot.m_nNextRouteNode].speedLimit; + switch (pVehicle->AutoPilot.m_nCarMission){ + case MISSION_RAMPLAYER_FARAWAY: + case MISSION_BLOCKPLAYER_FARAWAY: + PickNextNodeToChaseCar(pVehicle, + FindPlayerCoors().x, + FindPlayerCoors().y, +#ifdef FIX_PATHFIND_BUG + FindPlayerCoors().z, +#endif + FindPlayerVehicle()); + return false; + case MISSION_GOTOCOORDS: + case MISSION_GOTOCOORDS_ACCURATE: + return PickNextNodeToFollowPath(pVehicle); + case MISSION_RAMCAR_FARAWAY: + case MISSION_BLOCKCAR_FARAWAY: + PickNextNodeToChaseCar(pVehicle, + pVehicle->AutoPilot.m_pTargetCar->GetPosition().x, + pVehicle->AutoPilot.m_pTargetCar->GetPosition().y, +#ifdef FIX_PATHFIND_BUG + pVehicle->AutoPilot.m_pTargetCar->GetPosition().z, +#endif + pVehicle->AutoPilot.m_pTargetCar); + return false; + default: + PickNextNodeRandomly(pVehicle); + if (ThePaths.GetNode(pVehicle->AutoPilot.m_nNextRouteNode)->bOnlySmallBoats && BoatWithTallMast(pVehicle->GetModelIndex())) + pVehicle->AutoPilot.m_nCruiseSpeed = 0; + return false; + } +} + +void CCarCtrl::PickNextNodeRandomly(CVehicle* pVehicle) +{ + if (pVehicle->m_nRouteSeed) + CGeneral::SetRandomSeed(pVehicle->m_nRouteSeed); + int32 prevNode = pVehicle->AutoPilot.m_nCurrentRouteNode; + int32 curNode = pVehicle->AutoPilot.m_nNextRouteNode; + uint8 totalLinks = ThePaths.m_pathNodes[curNode].numLinks; + CCarPathLink* pCurLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo]; + uint8 lanesOnCurrentPath; + bool isOnOneWayRoad; + if (pCurLink->pathNodeIndex == curNode) { + lanesOnCurrentPath = pCurLink->numLeftLanes; + isOnOneWayRoad = pCurLink->numRightLanes == 0; + } + else { + lanesOnCurrentPath = pCurLink->numRightLanes; + isOnOneWayRoad = pCurLink->numLeftLanes == 0; + } + uint8 allowedDirections = PATH_DIRECTION_NONE; + uint8 nextLane = pVehicle->AutoPilot.m_nNextLane; + if (nextLane == 0) + /* We are always allowed to turn left from leftmost lane */ + allowedDirections |= PATH_DIRECTION_LEFT; + if (nextLane == lanesOnCurrentPath - 1) + /* We are always allowed to turn right from rightmost lane */ + allowedDirections |= PATH_DIRECTION_RIGHT; + if (lanesOnCurrentPath < 3 || allowedDirections == PATH_DIRECTION_NONE) + /* We are always allowed to go straight on one/two-laned road */ + /* or if we are in one of middle lanes of the road */ + allowedDirections |= PATH_DIRECTION_STRAIGHT; + int attempt; + pVehicle->AutoPilot.m_nPrevRouteNode = pVehicle->AutoPilot.m_nCurrentRouteNode; + pVehicle->AutoPilot.m_nCurrentRouteNode = pVehicle->AutoPilot.m_nNextRouteNode; + CPathNode* pPrevPathNode = &ThePaths.m_pathNodes[prevNode]; + CPathNode* pCurPathNode = &ThePaths.m_pathNodes[curNode]; + int16 nextLink; + CCarPathLink* pNextLink; + CPathNode* pNextPathNode; + bool goingAgainstOneWayRoad; + bool nextNodeIsOneWayRoad; + uint8 direction; + for(attempt = 0; attempt < ATTEMPTS_TO_FIND_NEXT_NODE; attempt++){ + if (attempt != 0){ + if (pVehicle->AutoPilot.m_nNextRouteNode != prevNode){ + if (direction & allowedDirections){ + pNextPathNode = &ThePaths.m_pathNodes[pVehicle->AutoPilot.m_nNextRouteNode]; + if ((!pNextPathNode->bDeadEnd || pPrevPathNode->bDeadEnd) && + (!pNextPathNode->bDisabled || pPrevPathNode->bDisabled) && + (!pNextPathNode->bBetweenLevels || pPrevPathNode->bBetweenLevels || !pVehicle->AutoPilot.m_bStayInCurrentLevel) && + !goingAgainstOneWayRoad && (!isOnOneWayRoad || !nextNodeIsOneWayRoad)) + break; + } + } + } + nextLink = CGeneral::GetRandomNumber() % totalLinks; + pVehicle->AutoPilot.m_nNextRouteNode = ThePaths.ConnectedNode(nextLink + pCurPathNode->firstLink); + direction = FindPathDirection(prevNode, curNode, pVehicle->AutoPilot.m_nNextRouteNode); + pNextLink = &ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[nextLink + pCurPathNode->firstLink]]; + goingAgainstOneWayRoad = pNextLink->pathNodeIndex == curNode ? pNextLink->numRightLanes == 0 : pNextLink->numLeftLanes == 0; + nextNodeIsOneWayRoad = pNextLink->pathNodeIndex == curNode ? pNextLink->numLeftLanes == 0 : pNextLink->numRightLanes == 0; + } + if (attempt >= ATTEMPTS_TO_FIND_NEXT_NODE) { + /* If we failed 15 times, then remove dead end, one way road and current lane limitations */ + for (attempt = 0; attempt < ATTEMPTS_TO_FIND_NEXT_NODE; attempt++) { + if (attempt != 0) { + if (pVehicle->AutoPilot.m_nNextRouteNode != prevNode) { + pNextPathNode = &ThePaths.m_pathNodes[pVehicle->AutoPilot.m_nNextRouteNode]; + if ((!pNextPathNode->bDisabled || pPrevPathNode->bDisabled) && + (!pNextPathNode->bBetweenLevels || pPrevPathNode->bBetweenLevels || !pVehicle->AutoPilot.m_bStayInCurrentLevel) && + !goingAgainstOneWayRoad) + break; + } + } + nextLink = CGeneral::GetRandomNumber() % totalLinks; + pVehicle->AutoPilot.m_nNextRouteNode = ThePaths.ConnectedNode(nextLink + pCurPathNode->firstLink); + pNextLink = &ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[nextLink + pCurPathNode->firstLink]]; + goingAgainstOneWayRoad = pNextLink->pathNodeIndex == curNode ? pNextLink->numRightLanes == 0 : pNextLink->numLeftLanes == 0; + } + } + if (attempt >= ATTEMPTS_TO_FIND_NEXT_NODE) { + /* If we failed again, remove no U-turn limitation and remove randomness */ + for (nextLink = 0; nextLink < totalLinks; nextLink++) { + pVehicle->AutoPilot.m_nNextRouteNode = ThePaths.ConnectedNode(nextLink + pCurPathNode->firstLink); + pNextLink = &ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[nextLink + pCurPathNode->firstLink]]; + goingAgainstOneWayRoad = pNextLink->pathNodeIndex == curNode ? pNextLink->numRightLanes == 0 : pNextLink->numLeftLanes == 0; + if (!goingAgainstOneWayRoad) { + pNextPathNode = &ThePaths.m_pathNodes[pVehicle->AutoPilot.m_nNextRouteNode]; + if ((!pNextPathNode->bDisabled || pPrevPathNode->bDisabled) && + (!pNextPathNode->bBetweenLevels || pPrevPathNode->bBetweenLevels || !pVehicle->AutoPilot.m_bStayInCurrentLevel)) + /* Nice way to exit loop but this will fail because this is used for indexing! */ + nextLink = 1000; + } + } + if (nextLink < 999) + /* If everything else failed, turn vehicle around */ + pVehicle->AutoPilot.m_nNextRouteNode = prevNode; + } + pNextPathNode = &ThePaths.m_pathNodes[pVehicle->AutoPilot.m_nNextRouteNode]; + pNextLink = &ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[nextLink + pCurPathNode->firstLink]]; + if (prevNode == pVehicle->AutoPilot.m_nNextRouteNode){ + /* We can no longer shift vehicle without physics if we have to turn it around. */ + pVehicle->SetStatus(STATUS_PHYSICS); + SwitchVehicleToRealPhysics(pVehicle); + } + pVehicle->AutoPilot.m_nTimeEnteredCurve += pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve; + pVehicle->AutoPilot.m_nPreviousPathNodeInfo = pVehicle->AutoPilot.m_nCurrentPathNodeInfo; + pVehicle->AutoPilot.m_nCurrentPathNodeInfo = pVehicle->AutoPilot.m_nNextPathNodeInfo; + pVehicle->AutoPilot.m_nPreviousDirection = pVehicle->AutoPilot.m_nCurrentDirection; + pVehicle->AutoPilot.m_nCurrentDirection = pVehicle->AutoPilot.m_nNextDirection; + pVehicle->AutoPilot.m_nCurrentLane = pVehicle->AutoPilot.m_nNextLane; + pVehicle->AutoPilot.m_nNextPathNodeInfo = ThePaths.m_carPathConnections[nextLink + pCurPathNode->firstLink]; + int8 lanesOnNextNode; + if (curNode >= pVehicle->AutoPilot.m_nNextRouteNode){ + pVehicle->AutoPilot.m_nNextDirection = 1; + lanesOnNextNode = pNextLink->numLeftLanes; + }else{ + pVehicle->AutoPilot.m_nNextDirection = -1; + lanesOnNextNode = pNextLink->numRightLanes; + } + float currentPathLinkForwardX = pVehicle->AutoPilot.m_nCurrentDirection * pCurLink->GetDirX(); + float nextPathLinkForwardX = pVehicle->AutoPilot.m_nNextDirection * pNextLink->GetDirX(); +#ifdef FIX_BUGS + float currentPathLinkForwardY = pVehicle->AutoPilot.m_nCurrentDirection * pCurLink->GetDirY(); + float nextPathLinkForwardY = pVehicle->AutoPilot.m_nNextDirection * pNextLink->GetDirY(); +#endif + if (lanesOnNextNode >= 0){ + if ((CGeneral::GetRandomNumber() & 0x600) == 0){ + /* 25% chance vehicle will try to switch lane */ + CVector2D dist = pNextPathNode->GetPosition() - pCurPathNode->GetPosition(); + if (dist.MagnitudeSqr() >= SQR(14.0f)){ + if (CGeneral::GetRandomTrueFalse()) + pVehicle->AutoPilot.m_nNextLane += 1; + else + pVehicle->AutoPilot.m_nNextLane -= 1; + } + } + pVehicle->AutoPilot.m_nNextLane = Min(lanesOnNextNode - 1, pVehicle->AutoPilot.m_nNextLane); + pVehicle->AutoPilot.m_nNextLane = Max(0, pVehicle->AutoPilot.m_nNextLane); + }else{ + pVehicle->AutoPilot.m_nNextLane = pVehicle->AutoPilot.m_nCurrentLane; + } + if (pVehicle->AutoPilot.m_bStayInFastLane) + pVehicle->AutoPilot.m_nNextLane = 0; + CVector positionOnCurrentLinkIncludingLane( + pCurLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurLink->OneWayLaneOffset()) * LANE_WIDTH) +#ifdef FIX_BUGS + * currentPathLinkForwardY +#endif + ,pCurLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardX, + 0.0f); + CVector positionOnNextLinkIncludingLane( + pNextLink->GetX() + ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) +#ifdef FIX_BUGS + * nextPathLinkForwardY +#endif + ,pNextLink->GetY() - ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardX, + 0.0f); + float directionCurrentLinkX = pCurLink->GetDirX() * pVehicle->AutoPilot.m_nCurrentDirection; + float directionCurrentLinkY = pCurLink->GetDirY() * pVehicle->AutoPilot.m_nCurrentDirection; + float directionNextLinkX = pNextLink->GetDirX() * pVehicle->AutoPilot.m_nNextDirection; + float directionNextLinkY = pNextLink->GetDirY() * pVehicle->AutoPilot.m_nNextDirection; + /* We want to make a path between two links that may not have the same forward directions a curve. */ + pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve = CCurves::CalcSpeedScaleFactor( + &positionOnCurrentLinkIncludingLane, + &positionOnNextLinkIncludingLane, + directionCurrentLinkX, directionCurrentLinkY, + directionNextLinkX, directionNextLinkY + ) * (1000.0f / pVehicle->AutoPilot.m_fMaxTrafficSpeed); + if (pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve < 10) + /* Oh hey there Obbe */ + printf("fout\n"); + pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve = Max(10, pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve); +} + +uint8 CCarCtrl::FindPathDirection(int32 prevNode, int32 curNode, int32 nextNode) +{ + CVector2D prevToCur = ThePaths.m_pathNodes[curNode].GetPosition() - ThePaths.m_pathNodes[prevNode].GetPosition(); + CVector2D curToNext = ThePaths.m_pathNodes[nextNode].GetPosition() - ThePaths.m_pathNodes[curNode].GetPosition(); + float distPrevToCur = prevToCur.Magnitude(); + if (distPrevToCur == 0.0f) + return PATH_DIRECTION_NONE; + /* We are trying to determine angle between prevToCur and curToNext. */ + /* To find it, we consider a to be an angle between y axis and prevToCur */ + /* and b to be an angle between x axis and curToNext */ + /* Then the angle we are looking for is (pi/2 + a + b). */ + float sin_a = prevToCur.x / distPrevToCur; + float cos_a = prevToCur.y / distPrevToCur; + float distCurToNext = curToNext.Magnitude(); + if (distCurToNext == 0.0f) + return PATH_DIRECTION_NONE; + float sin_b = curToNext.y / distCurToNext; + float cos_b = curToNext.x / distCurToNext; + /* sin(a) * sin(b) - cos(a) * cos(b) = -cos(a+b) = sin(pi/2+a+b) */ + float sin_direction = sin_a * sin_b - cos_a * cos_b; + if (sin_direction > 0.77f) /* Roughly between -50 and -130 degrees */ + return PATH_DIRECTION_LEFT; + if (sin_direction < -0.77f) /* Roughly between 50 and 130 degrees */ + return PATH_DIRECTION_RIGHT; + return PATH_DIRECTION_STRAIGHT; +} + +#ifdef FIX_PATHFIND_BUG +void CCarCtrl::PickNextNodeToChaseCar(CVehicle* pVehicle, float targetX, float targetY, float targetZ, CVehicle* pTarget) +#else +void CCarCtrl::PickNextNodeToChaseCar(CVehicle* pVehicle, float targetX, float targetY, CVehicle* pTarget) +#endif +{ + if (pVehicle->m_nRouteSeed) + CGeneral::SetRandomSeed(pVehicle->m_nRouteSeed); + int prevNode = pVehicle->AutoPilot.m_nCurrentRouteNode; + int curNode = pVehicle->AutoPilot.m_nNextRouteNode; + CPathNode* pPrevNode = &ThePaths.m_pathNodes[prevNode]; + CPathNode* pCurNode = &ThePaths.m_pathNodes[curNode]; + CPathNode* pTargetNode[2]; + int16 numNodes; + float distanceToTargetNode; + ThePaths.DoPathSearch(0, pCurNode->GetPosition(), curNode, +#ifdef FIX_PATHFIND_BUG + CVector(targetX, targetY, targetZ), +#else + CVector(targetX, targetY, 0.0f), +#endif + pTargetNode, &numNodes, 2, pVehicle, &distanceToTargetNode, 999999.9f, -1); + + int newNextNode; + int nextLink; + if (numNodes != 1 && numNodes != 2 || pTargetNode[0] == pCurNode){ + if (numNodes != 2 || pTargetNode[1] == pCurNode) { + float currentAngle = CGeneral::GetATanOfXY(targetX - pVehicle->GetPosition().x, targetY - pVehicle->GetPosition().y); + nextLink = 0; + float lowestAngleChange = 10.0f; + int numLinks = pCurNode->numLinks; + newNextNode = 0; + for (int i = 0; i < numLinks; i++) { + int conNode = ThePaths.ConnectedNode(i + pCurNode->firstLink); + if (conNode == prevNode && i > 1) + continue; + CPathNode* pTestNode = &ThePaths.m_pathNodes[conNode]; + float angle = CGeneral::GetATanOfXY(pTestNode->GetX() - pCurNode->GetX(), pTestNode->GetY() - pCurNode->GetY()); + angle = LimitRadianAngle(angle - currentAngle); + angle = ABS(angle); + if (angle < lowestAngleChange) { + lowestAngleChange = angle; + newNextNode = conNode; + nextLink = i; + } + } + } + else { + nextLink = 0; + newNextNode = pTargetNode[1] - ThePaths.m_pathNodes; + for (int i = pCurNode->firstLink; ThePaths.ConnectedNode(i) != newNextNode; i++, nextLink++) + ; + } + } + else { + nextLink = 0; + newNextNode = pTargetNode[0] - ThePaths.m_pathNodes; + for (int i = pCurNode->firstLink; ThePaths.ConnectedNode(i) != newNextNode; i++, nextLink++) + ; + } + CPathNode* pNextPathNode = &ThePaths.m_pathNodes[pVehicle->AutoPilot.m_nNextRouteNode]; + CCarPathLink* pNextLink = &ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[nextLink + pCurNode->firstLink]]; + CCarPathLink* pCurLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo]; + pVehicle->AutoPilot.m_nPrevRouteNode = pVehicle->AutoPilot.m_nCurrentRouteNode; + pVehicle->AutoPilot.m_nCurrentRouteNode = pVehicle->AutoPilot.m_nNextRouteNode; + pVehicle->AutoPilot.m_nNextRouteNode = newNextNode; + pVehicle->AutoPilot.m_nTimeEnteredCurve += pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve; + pVehicle->AutoPilot.m_nPreviousPathNodeInfo = pVehicle->AutoPilot.m_nCurrentPathNodeInfo; + pVehicle->AutoPilot.m_nCurrentPathNodeInfo = pVehicle->AutoPilot.m_nNextPathNodeInfo; + pVehicle->AutoPilot.m_nPreviousDirection = pVehicle->AutoPilot.m_nCurrentDirection; + pVehicle->AutoPilot.m_nCurrentDirection = pVehicle->AutoPilot.m_nNextDirection; + pVehicle->AutoPilot.m_nCurrentLane = pVehicle->AutoPilot.m_nNextLane; + pVehicle->AutoPilot.m_nNextPathNodeInfo = ThePaths.m_carPathConnections[nextLink + pCurNode->firstLink]; + int8 lanesOnNextNode; + if (curNode >= pVehicle->AutoPilot.m_nNextRouteNode) { + pVehicle->AutoPilot.m_nNextDirection = 1; + lanesOnNextNode = pNextLink->numRightLanes; + } + else { + pVehicle->AutoPilot.m_nNextDirection = -1; + lanesOnNextNode = pNextLink->numLeftLanes; + } + float currentPathLinkForwardX = pVehicle->AutoPilot.m_nCurrentDirection * pCurLink->GetDirX(); + float currentPathLinkForwardY = pVehicle->AutoPilot.m_nCurrentDirection * pCurLink->GetDirY(); + float nextPathLinkForwardX = pVehicle->AutoPilot.m_nNextDirection * pNextLink->GetDirX(); + float nextPathLinkForwardY = pVehicle->AutoPilot.m_nNextDirection * pNextLink->GetDirY(); + if (lanesOnNextNode >= 0) { + CVector2D dist = pNextPathNode->GetPosition() - pCurNode->GetPosition(); + if (dist.MagnitudeSqr() >= SQR(7.0f)){ + /* 25% chance vehicle will try to switch lane */ + /* No lane switching if following car from far away */ + /* ...although it's always one of those. */ + if ((CGeneral::GetRandomNumber() & 0x600) == 0 && + pVehicle->AutoPilot.m_nCarMission != MISSION_RAMPLAYER_FARAWAY && + pVehicle->AutoPilot.m_nCarMission != MISSION_BLOCKPLAYER_FARAWAY && + pVehicle->AutoPilot.m_nCarMission != MISSION_RAMCAR_FARAWAY && + pVehicle->AutoPilot.m_nCarMission != MISSION_BLOCKCAR_FARAWAY){ + if (CGeneral::GetRandomTrueFalse()) + pVehicle->AutoPilot.m_nNextLane += 1; + else + pVehicle->AutoPilot.m_nNextLane -= 1; + } + } + pVehicle->AutoPilot.m_nNextLane = Min(lanesOnNextNode - 1, pVehicle->AutoPilot.m_nNextLane); + pVehicle->AutoPilot.m_nNextLane = Max(0, pVehicle->AutoPilot.m_nNextLane); + } + else { + pVehicle->AutoPilot.m_nNextLane = pVehicle->AutoPilot.m_nCurrentLane; + } + if (pVehicle->AutoPilot.m_bStayInFastLane) + pVehicle->AutoPilot.m_nNextLane = 0; + CVector positionOnCurrentLinkIncludingLane( + pCurLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardY, + pCurLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardX, + 0.0f); + CVector positionOnNextLinkIncludingLane( + pNextLink->GetX() + ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardY, + pNextLink->GetY() - ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardX, + 0.0f); + float directionCurrentLinkX = pCurLink->GetDirX() * pVehicle->AutoPilot.m_nCurrentDirection; + float directionCurrentLinkY = pCurLink->GetDirY() * pVehicle->AutoPilot.m_nCurrentDirection; + float directionNextLinkX = pNextLink->GetDirX() * pVehicle->AutoPilot.m_nNextDirection; + float directionNextLinkY = pNextLink->GetDirY() * pVehicle->AutoPilot.m_nNextDirection; + /* We want to make a path between two links that may not have the same forward directions a curve. */ + pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve = CCurves::CalcSpeedScaleFactor( + &positionOnCurrentLinkIncludingLane, + &positionOnNextLinkIncludingLane, + directionCurrentLinkX, directionCurrentLinkY, + directionNextLinkX, directionNextLinkY + ) * (1000.0f / pVehicle->AutoPilot.m_fMaxTrafficSpeed); + pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve = Max(10, pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve); +} + +bool CCarCtrl::PickNextNodeToFollowPath(CVehicle* pVehicle) +{ + if (pVehicle->m_nRouteSeed) + CGeneral::SetRandomSeed(pVehicle->m_nRouteSeed); + int curNode = pVehicle->AutoPilot.m_nNextRouteNode; + CPathNode* pCurNode = &ThePaths.m_pathNodes[curNode]; + if (pVehicle->AutoPilot.m_nPathFindNodesCount == 0){ + ThePaths.DoPathSearch(0, pVehicle->GetPosition(), curNode, + pVehicle->AutoPilot.m_vecDestinationCoors, pVehicle->AutoPilot.m_aPathFindNodesInfo, + &pVehicle->AutoPilot.m_nPathFindNodesCount, NUM_PATH_NODES_IN_AUTOPILOT, + pVehicle, nil, 999999.9f, -1); + if (pVehicle->AutoPilot.m_nPathFindNodesCount < 2) + return true; + pVehicle->AutoPilot.RemoveOnePathNode(); + } + CPathNode* pNextPathNode = &ThePaths.m_pathNodes[pVehicle->AutoPilot.m_nNextRouteNode]; + CCarPathLink* pCurLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo]; + pVehicle->AutoPilot.m_nPrevRouteNode = pVehicle->AutoPilot.m_nCurrentRouteNode; + pVehicle->AutoPilot.m_nCurrentRouteNode = pVehicle->AutoPilot.m_nNextRouteNode; + pVehicle->AutoPilot.m_nNextRouteNode = pVehicle->AutoPilot.m_aPathFindNodesInfo[0] - ThePaths.m_pathNodes; + pVehicle->AutoPilot.RemoveOnePathNode(); + pVehicle->AutoPilot.m_nTimeEnteredCurve += pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve; + pVehicle->AutoPilot.m_nPreviousPathNodeInfo = pVehicle->AutoPilot.m_nCurrentPathNodeInfo; + pVehicle->AutoPilot.m_nCurrentPathNodeInfo = pVehicle->AutoPilot.m_nNextPathNodeInfo; + pVehicle->AutoPilot.m_nPreviousDirection = pVehicle->AutoPilot.m_nCurrentDirection; + pVehicle->AutoPilot.m_nCurrentDirection = pVehicle->AutoPilot.m_nNextDirection; + pVehicle->AutoPilot.m_nCurrentLane = pVehicle->AutoPilot.m_nNextLane; + int nextLink = 0; + for (int i = pCurNode->firstLink; ThePaths.ConnectedNode(i) != pVehicle->AutoPilot.m_nNextRouteNode; i++, nextLink++) + ; + CCarPathLink* pNextLink = &ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[nextLink + pCurNode->firstLink]]; + pVehicle->AutoPilot.m_nNextPathNodeInfo = ThePaths.m_carPathConnections[nextLink + pCurNode->firstLink]; + int8 lanesOnNextNode; + if (curNode >= pVehicle->AutoPilot.m_nNextRouteNode) { + pVehicle->AutoPilot.m_nNextDirection = 1; + lanesOnNextNode = pNextLink->numLeftLanes; + } + else { + pVehicle->AutoPilot.m_nNextDirection = -1; + lanesOnNextNode = pNextLink->numRightLanes; + } + float currentPathLinkForwardX = pVehicle->AutoPilot.m_nCurrentDirection * pCurLink->GetDirX(); + float currentPathLinkForwardY = pVehicle->AutoPilot.m_nCurrentDirection * pCurLink->GetDirY(); + float nextPathLinkForwardX = pVehicle->AutoPilot.m_nNextDirection * pNextLink->GetDirX(); + float nextPathLinkForwardY = pVehicle->AutoPilot.m_nNextDirection * pNextLink->GetDirY(); + if (lanesOnNextNode >= 0) { + CVector2D dist = pNextPathNode->GetPosition() - pCurNode->GetPosition(); + if (dist.MagnitudeSqr() >= SQR(7.0f) && (CGeneral::GetRandomNumber() & 0x600) == 0) { + if (CGeneral::GetRandomTrueFalse()) + pVehicle->AutoPilot.m_nNextLane += 1; + else + pVehicle->AutoPilot.m_nNextLane -= 1; + } + pVehicle->AutoPilot.m_nNextLane = Min(lanesOnNextNode - 1, pVehicle->AutoPilot.m_nNextLane); + pVehicle->AutoPilot.m_nNextLane = Max(0, pVehicle->AutoPilot.m_nNextLane); + } + else { + pVehicle->AutoPilot.m_nNextLane = pVehicle->AutoPilot.m_nCurrentLane; + } + if (pVehicle->AutoPilot.m_bStayInFastLane) + pVehicle->AutoPilot.m_nNextLane = 0; + CVector positionOnCurrentLinkIncludingLane( + pCurLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardY, + pCurLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardX, + 0.0f); + CVector positionOnNextLinkIncludingLane( + pNextLink->GetX() + ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardY, + pNextLink->GetY() - ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardX, + 0.0f); + float directionCurrentLinkX = pCurLink->GetDirX() * pVehicle->AutoPilot.m_nCurrentDirection; + float directionCurrentLinkY = pCurLink->GetDirY() * pVehicle->AutoPilot.m_nCurrentDirection; + float directionNextLinkX = pNextLink->GetDirX() * pVehicle->AutoPilot.m_nNextDirection; + float directionNextLinkY = pNextLink->GetDirY() * pVehicle->AutoPilot.m_nNextDirection; + /* We want to make a path between two links that may not have the same forward directions a curve. */ + pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve = CCurves::CalcSpeedScaleFactor( + &positionOnCurrentLinkIncludingLane, + &positionOnNextLinkIncludingLane, + directionCurrentLinkX, directionCurrentLinkY, + directionNextLinkX, directionNextLinkY + ) * (1000.0f / pVehicle->AutoPilot.m_fMaxTrafficSpeed); + pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve = Max(10, pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve); + return false; +} + +void CCarCtrl::Init(void) +{ + NumRandomCars = 0; + NumLawEnforcerCars = 0; + NumMissionCars = 0; + NumParkedCars = 0; + NumPermanentCars = 0; + NumAmbulancesOnDuty = 0; + NumFiretrucksOnDuty = 0; + LastTimeFireTruckCreated = 0; + LastTimeAmbulanceCreated = 0; +#ifdef FIX_BUGS + LastTimeLawEnforcerCreated = 0; + LastTimeMiamiViceGenerated = 0; +#endif + bCarsGeneratedAroundCamera = false; + CountDownToCarsAtStart = 2; + CarDensityMultiplier = 1.0f; + for (int i = 0; i < MAX_CARS_TO_KEEP; i++) + apCarsToKeep[i] = nil; + for (int i = 0; i < TOTAL_CUSTOM_CLASSES; i++){ + for (int j = 0; j < MAX_CAR_MODELS_IN_ARRAY; j++) { + LoadedCarsArray[i][j] = -1; + } + NumOfLoadedCarsOfRating[i] = 0; + NumRequestsOfCarRating[i] = 0; + TotalNumOfCarsOfRating[i] = 0; + } +} + +void CCarCtrl::ReInit(void) +{ + NumRandomCars = 0; + NumLawEnforcerCars = 0; + NumMissionCars = 0; + NumParkedCars = 0; + NumPermanentCars = 0; + NumAmbulancesOnDuty = 0; + NumFiretrucksOnDuty = 0; +#ifdef FIX_BUGS + LastTimeFireTruckCreated = 0; + LastTimeAmbulanceCreated = 0; + LastTimeLawEnforcerCreated = 0; + LastTimeMiamiViceGenerated = 0; +#endif + CountDownToCarsAtStart = 2; + CarDensityMultiplier = 1.0f; + for (int i = 0; i < MAX_CARS_TO_KEEP; i++) + apCarsToKeep[i] = nil; + for (int i = 0; i < TOTAL_CUSTOM_CLASSES; i++) + NumRequestsOfCarRating[i] = 0; +} + +void CCarCtrl::DragCarToPoint(CVehicle* pVehicle, CVector* pPoint) +{ + CVector2D posBehind = (CVector2D)pVehicle->GetPosition() - 3 * pVehicle->GetForward() / 2; + CVector2D posTarget = *pPoint; + CVector2D direction = posBehind - posTarget; + CVector2D midPos = posTarget + direction * 3 / direction.Magnitude(); + float actualAheadZ; + float actualBehindZ; + CColPoint point; + CEntity* pRoadObject; + if (CCollision::IsStoredPolyStillValidVerticalLine(CVector(posTarget.x, posTarget.y, pVehicle->GetPosition().z - 3.0f), + pVehicle->GetPosition().z - 3.0f, point, &pVehicle->m_aCollPolys[0])){ + actualAheadZ = point.point.z; + }else if (CWorld::ProcessVerticalLine(CVector(posTarget.x, posTarget.y, pVehicle->GetPosition().z + 1.5f), + pVehicle->GetPosition().z - 2.0f, point, + pRoadObject, true, false, false, false, false, false, &pVehicle->m_aCollPolys[0])){ + actualAheadZ = point.point.z; + pVehicle->m_pCurGroundEntity = pRoadObject; + if (ThisRoadObjectCouldMove(pRoadObject->GetModelIndex())) + pVehicle->m_aCollPolys[0].valid = false; + }else if (CWorld::ProcessVerticalLine(CVector(posTarget.x, posTarget.y, pVehicle->GetPosition().z + 3.0f), + pVehicle->GetPosition().z - 3.0f, point, + pRoadObject, true, false, false, false, false, false, &pVehicle->m_aCollPolys[0])) { + actualAheadZ = point.point.z; + pVehicle->m_pCurGroundEntity = pRoadObject; + if (ThisRoadObjectCouldMove(pRoadObject->GetModelIndex())) + pVehicle->m_aCollPolys[0].valid = false; + }else{ + actualAheadZ = pVehicle->m_fMapObjectHeightAhead; + } + pVehicle->m_fMapObjectHeightAhead = actualAheadZ; + if (CCollision::IsStoredPolyStillValidVerticalLine(CVector(midPos.x, midPos.y, pVehicle->GetPosition().z - 3.0f), + pVehicle->GetPosition().z - 3.0f, point, &pVehicle->m_aCollPolys[1])){ + actualBehindZ = point.point.z; + }else if (CWorld::ProcessVerticalLine(CVector(midPos.x, midPos.y, pVehicle->GetPosition().z + 1.5f), + pVehicle->GetPosition().z - 2.0f, point, + pRoadObject, true, false, false, false, false, false, &pVehicle->m_aCollPolys[1])){ + actualBehindZ = point.point.z; + pVehicle->m_pCurGroundEntity = pRoadObject; + if (ThisRoadObjectCouldMove(pRoadObject->GetModelIndex())) + pVehicle->m_aCollPolys[1].valid = false; + }else if (CWorld::ProcessVerticalLine(CVector(midPos.x, midPos.y, pVehicle->GetPosition().z + 3.0f), + pVehicle->GetPosition().z - 3.0f, point, + pRoadObject, true, false, false, false, false, false, &pVehicle->m_aCollPolys[1])){ + actualBehindZ = point.point.z; + pVehicle->m_pCurGroundEntity = pRoadObject; + if (ThisRoadObjectCouldMove(pRoadObject->GetModelIndex())) + pVehicle->m_aCollPolys[1].valid = false; + }else{ + actualBehindZ = pVehicle->m_fMapObjectHeightBehind; + } + pVehicle->m_fMapObjectHeightBehind = actualBehindZ; + float angleZ = Atan2((actualAheadZ - actualBehindZ) / 3, 1.0f); + float cosZ = Cos(angleZ); + float sinZ = Sin(angleZ); + pVehicle->GetRight() = CVector(posTarget.y - midPos.y, -(posTarget.x - midPos.x), 0.0f) / 3; + pVehicle->GetForward() = CVector(-cosZ * pVehicle->GetRight().y, cosZ * pVehicle->GetRight().x, sinZ); + pVehicle->GetUp() = CrossProduct(pVehicle->GetRight(), pVehicle->GetForward()); + pVehicle->SetPosition((CVector(midPos.x, midPos.y, actualBehindZ) + CVector(posTarget.x, posTarget.y, actualAheadZ)) / 2); + pVehicle->GetMatrix().GetPosition().z += pVehicle->GetHeightAboveRoad(); +} + +float CCarCtrl::FindSpeedMultiplier(float angleChange, float minAngle, float maxAngle, float coef) +{ + float angle = Abs(LimitRadianAngle(angleChange)); + float n = angle - minAngle; + n = Max(0.0f, n); + float d = maxAngle - minAngle; + float mult = 1.0f - n / d * (1.0f - coef); + if (n > d) + return coef; + return mult; +} + +void CCarCtrl::SteerAICarWithPhysics(CVehicle* pVehicle) +{ + float swerve; + float accel; + float brake; + bool handbrake; + switch (pVehicle->AutoPilot.m_nTempAction){ + case TEMPACT_WAIT: + swerve = 0.0f; + accel = 0.0f; + brake = 0.2f; + handbrake = false; + if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction){ + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds(); + } + break; + case TEMPACT_REVERSE: + SteerAICarWithPhysics_OnlyMission(pVehicle, &swerve, &accel, &brake, &handbrake); + handbrake = false; + swerve = -swerve; + if (DotProduct(pVehicle->GetMoveSpeed(), pVehicle->GetForward()) > 0.04f){ + accel = 0.0f; + brake = 0.5f; + }else{ + accel = -0.5f; + brake = 0.0f; + } + if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction) + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + break; + case TEMPACT_HANDBRAKETURNLEFT: + swerve = 1.0f; + accel = 0.0f; + brake = 0.0f; + handbrake = true; + if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction) + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + break; + case TEMPACT_HANDBRAKETURNRIGHT: + swerve = -1.0f; + accel = 0.0f; + brake = 0.0f; + handbrake = true; + if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction) + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + break; + case TEMPACT_HANDBRAKESTRAIGHT: + swerve = 0.0f; + accel = 0.0f; + brake = 0.0f; + handbrake = true; + if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction) + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + break; + case TEMPACT_TURNLEFT: + swerve = 1.0f; + accel = 1.0f; + brake = 0.0f; + handbrake = false; + if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction) + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + break; + case TEMPACT_TURNRIGHT: + swerve = -1.0f; + accel = 1.0f; + brake = 0.0f; + handbrake = false; + if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction) + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + break; + case TEMPACT_GOFORWARD: + swerve = 0.0f; + accel = 0.5f; + brake = 0.0f; + handbrake = false; + if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction) + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + break; + case TEMPACT_SWERVELEFT: + case TEMPACT_SWERVERIGHT: + swerve = (pVehicle->AutoPilot.m_nTempAction == TEMPACT_SWERVERIGHT) ? 0.15f : -0.15f; + accel = 0.0f; + brake = 0.001f; + handbrake = false; + if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction - 1000) + swerve = -swerve; + if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction) + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + break; + default: + SteerAICarWithPhysics_OnlyMission(pVehicle, &swerve, &accel, &brake, &handbrake); + break; + } + pVehicle->m_fSteerAngle = swerve; + pVehicle->bIsHandbrakeOn = handbrake; + pVehicle->m_fGasPedal = accel; + pVehicle->m_fBrakePedal = brake; +} + +void CCarCtrl::SteerAICarWithPhysics_OnlyMission(CVehicle* pVehicle, float* pSwerve, float* pAccel, float* pBrake, bool* pHandbrake) +{ + switch (pVehicle->AutoPilot.m_nCarMission) { + case MISSION_NONE: + *pSwerve = 0.0f; + *pAccel = 0.0f; + *pBrake = 0.5f; + *pHandbrake = true; + return; + case MISSION_CRUISE: + case MISSION_RAMPLAYER_FARAWAY: + case MISSION_BLOCKPLAYER_FARAWAY: + case MISSION_GOTOCOORDS: + case MISSION_GOTOCOORDS_ACCURATE: + case MISSION_RAMCAR_FARAWAY: + case MISSION_BLOCKCAR_FARAWAY: + if (pVehicle->AutoPilot.m_bIgnorePathfinding) { + *pSwerve = 0.0f; + *pAccel = 1.0f; + *pBrake = 0.0f; + *pHandbrake = false; + }else + SteerAICarWithPhysicsFollowPath(pVehicle, pSwerve, pAccel, pBrake, pHandbrake); + return; + case MISSION_RAMPLAYER_CLOSE: + { + CVector2D targetPos = FindPlayerCoors(); + if (FindPlayerVehicle()){ + if (pVehicle->m_randomSeed & 1 && DotProduct(FindPlayerVehicle()->GetForward(), pVehicle->GetForward()) > 0.5f){ + float targetWidth = FindPlayerVehicle()->GetColModel()->boundingBox.max.x; + float ownWidth = pVehicle->GetColModel()->boundingBox.max.x; + if (pVehicle->m_randomSeed & 2){ + targetPos += (targetWidth + ownWidth - 0.2f) * FindPlayerVehicle()->GetRight(); + }else{ + targetPos -= (targetWidth + ownWidth - 0.2f) * FindPlayerVehicle()->GetRight(); + } + float targetSpeed = FindPlayerVehicle()->GetMoveSpeed().Magnitude(); + float distanceToTarget = ((CVector2D)pVehicle->GetPosition() - targetPos).Magnitude(); + if (12.0f * targetSpeed + 2.0f > distanceToTarget && pVehicle->AutoPilot.m_nTempAction == TEMPACT_NONE){ + pVehicle->AutoPilot.m_nTempAction = (pVehicle->m_randomSeed & 2) ? TEMPACT_TURNLEFT : TEMPACT_TURNRIGHT; + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 250; + } + }else{ + targetPos += FindPlayerVehicle()->GetRight() / 160 * ((pVehicle->m_randomSeed & 0xFF) - 128); + } + } + SteerAICarWithPhysicsHeadingForTarget(pVehicle, FindPlayerVehicle(), targetPos.x, targetPos.y, pSwerve, pAccel, pBrake, pHandbrake); + return; + } + case MISSION_BLOCKPLAYER_CLOSE: + SteerAICarWithPhysicsTryingToBlockTarget(pVehicle, FindPlayerCoors().x, FindPlayerCoors().y, + FindPlayerSpeed().x, FindPlayerSpeed().y, pSwerve, pAccel, pBrake, pHandbrake); + return; + case MISSION_BLOCKPLAYER_HANDBRAKESTOP: + SteerAICarWithPhysicsTryingToBlockTarget_Stop(pVehicle, FindPlayerCoors().x, FindPlayerCoors().y, + FindPlayerSpeed().x, FindPlayerSpeed().y, pSwerve, pAccel, pBrake, pHandbrake); + return; + case MISSION_WAITFORDELETION: + case MISSION_HELI_LAND: + return; + case MISSION_GOTOCOORDS_STRAIGHT: + case MISSION_GOTO_COORDS_STRAIGHT_ACCURATE: + SteerAICarWithPhysicsHeadingForTarget(pVehicle, nil, + pVehicle->AutoPilot.m_vecDestinationCoors.x, pVehicle->AutoPilot.m_vecDestinationCoors.y, + pSwerve, pAccel, pBrake, pHandbrake); + return; + case MISSION_EMERGENCYVEHICLE_STOP: + case MISSION_STOP_FOREVER: + *pSwerve = 0.0f; + *pAccel = 0.0f; + *pHandbrake = true; + *pBrake = 0.5f; + return; + case MISSION_GOTOCOORDS_ASTHECROWSWIMS: + SteerAIBoatWithPhysicsHeadingForTarget(pVehicle, + pVehicle->AutoPilot.m_vecDestinationCoors.x, pVehicle->AutoPilot.m_vecDestinationCoors.y, + pSwerve, pAccel, pBrake); + *pHandbrake = false; + return; + case MISSION_RAMCAR_CLOSE: + SteerAICarWithPhysicsHeadingForTarget(pVehicle, pVehicle->AutoPilot.m_pTargetCar, + pVehicle->AutoPilot.m_pTargetCar->GetPosition().x, pVehicle->AutoPilot.m_pTargetCar->GetPosition().y, + pSwerve, pAccel, pBrake, pHandbrake); + return; + case MISSION_BLOCKCAR_CLOSE: + SteerAICarWithPhysicsTryingToBlockTarget(pVehicle, + pVehicle->AutoPilot.m_pTargetCar->GetPosition().x, + pVehicle->AutoPilot.m_pTargetCar->GetPosition().y, + pVehicle->AutoPilot.m_pTargetCar->GetMoveSpeed().x, + pVehicle->AutoPilot.m_pTargetCar->GetMoveSpeed().y, + pSwerve, pAccel, pBrake, pHandbrake); + return; + case MISSION_BLOCKCAR_HANDBRAKESTOP: + SteerAICarWithPhysicsTryingToBlockTarget_Stop(pVehicle, + pVehicle->AutoPilot.m_pTargetCar->GetPosition().x, + pVehicle->AutoPilot.m_pTargetCar->GetPosition().y, + pVehicle->AutoPilot.m_pTargetCar->GetMoveSpeed().x, + pVehicle->AutoPilot.m_pTargetCar->GetMoveSpeed().y, + pSwerve, pAccel, pBrake, pHandbrake); + return; + case MISSION_HELI_FLYTOCOORS: + SteerAIHeliTowardsTargetCoors((CAutomobile*)pVehicle); + return; + case MISSION_ATTACKPLAYER: + SteerAIBoatWithPhysicsAttackingPlayer(pVehicle, pSwerve, pAccel, pBrake, pHandbrake); + return; + case MISSION_PLANE_FLYTOCOORS: + SteerAIPlaneTowardsTargetCoors((CAutomobile*)pVehicle); + return; + case MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_1: + SteerAICarWithPhysicsHeadingForTarget(pVehicle, nil, + pVehicle->AutoPilot.m_vecDestinationCoors.x, pVehicle->AutoPilot.m_vecDestinationCoors.y, + pSwerve, pAccel, pBrake, pHandbrake); + return; + case MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_2: + SteerAICarWithPhysicsHeadingForTarget(pVehicle, nil, FindPlayerCoors().x, FindPlayerCoors().y, + pSwerve, pAccel, pBrake, pHandbrake); + return; + case MISSION_BLOCKPLAYER_FORWARDANDBACK: + SteerAICarBlockingPlayerForwardAndBack(pVehicle, pSwerve, pAccel, pBrake, pHandbrake); + return; + default: + assert(0); + return; + } +} + +void CCarCtrl::SteerAICarBlockingPlayerForwardAndBack(CVehicle* pVehicle, float* pSwerve, float* pAccel, float* pBrake, bool* pHandbrake) +{ + *pSwerve = 0.0f; + *pHandbrake = false; + CVector player = FindPlayerSpeed() + 0.1f * FindPlayerEntity()->GetForward(); + player.z = 0.0f; + CVector right(pVehicle->GetRight().x, pVehicle->GetRight().y, 0.0f); + right.Normalise(); + CVector forward(pVehicle->GetForward().x, pVehicle->GetForward().y, 0.0f); + forward.Normalise(); + float dpPlayerAndRight = DotProduct(player, right); + if (dpPlayerAndRight == 0.0f) + dpPlayerAndRight = 0.01f; + float dpDiffAndRight = -DotProduct((FindPlayerCoors() - pVehicle->GetPosition()), right) / dpPlayerAndRight; + if (dpDiffAndRight < 0.0f) { + *pAccel = 0.0f; + *pBrake = 0.0f; + return; + } + float dpSpeedAndForward = DotProduct(pVehicle->GetMoveSpeed(), forward); + float dpPlayerAndForward = DotProduct(player, forward); + float dpDiffAndForward = DotProduct((FindPlayerCoors() - pVehicle->GetPosition()), forward); + float multiplier = dpPlayerAndForward * dpDiffAndRight + dpDiffAndForward - dpSpeedAndForward * dpDiffAndRight; + if (multiplier > 0) { + *pAccel = Min(1.0f, 0.1f * multiplier); + *pBrake = 0.0f; + } + else if (dpSpeedAndForward > 0) { + *pAccel = 0.0f; + *pBrake = Min(1.0f, -0.1f * multiplier); + if (*pBrake > 0.95f) + *pHandbrake = true; + } + else { + *pAccel = Max(-1.0f, 0.1f * multiplier); + *pBrake = 0.0f; + } +} + +void CCarCtrl::SteerAIBoatWithPhysicsHeadingForTarget(CVehicle* pVehicle, float targetX, float targetY, float* pSwerve, float* pAccel, float* pBrake) +{ + CVector2D forward = pVehicle->GetForward(); + forward.Normalise(); + float angleToTarget = CGeneral::GetATanOfXY(targetX - pVehicle->GetPosition().x, targetY - pVehicle->GetPosition().y); + float angleForward = CGeneral::GetATanOfXY(forward.x, forward.y); + float steerAngle = LimitRadianAngle(angleToTarget - angleForward); + steerAngle = Clamp(steerAngle, -DEFAULT_MAX_STEER_ANGLE, DEFAULT_MAX_STEER_ANGLE); +#ifdef FIX_BUGS + float speedTarget = pVehicle->AutoPilot.GetCruiseSpeed(); +#else + float speedTarget = pVehicle->AutoPilot.m_nCruiseSpeed; +#endif + float currentSpeed = pVehicle->GetMoveSpeed().Magnitude() * GAME_SPEED_TO_CARAI_SPEED; + float speedDiff = speedTarget - currentSpeed; + if (speedDiff <= 0.0f) { + speedDiff < -5.0f ? *pAccel = -0.2f : *pAccel = -0.1f; + steerAngle *= -1; + } + else if (speedDiff / currentSpeed > 0.25f) { + *pAccel = 1.0f; + } + else { + *pAccel = 1.0f - (0.25f - speedDiff / currentSpeed) * 4.0f; + } + *pBrake = 0.0f; + *pSwerve = steerAngle; +} + +void CCarCtrl::SteerAIBoatWithPhysicsAttackingPlayer(CVehicle* pVehicle, float* pSwerve, float* pAccel, float* pBrake, bool* pHandbrake) +{ + float distanceToPlayer = (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude(); + float projection = Min(distanceToPlayer / 20.0f, 2.0f); + CVector2D forward = pVehicle->GetForward(); + forward.Normalise(); + CVector2D vecToProjection = FindPlayerCoors() + FindPlayerSpeed() * projection * GAME_SPEED_TO_CARAI_SPEED; + float angleToTarget = CGeneral::GetATanOfXY(vecToProjection.x - pVehicle->GetPosition().x, vecToProjection.y - pVehicle->GetPosition().y); + float angleForward = CGeneral::GetATanOfXY(forward.x, forward.y); + float steerAngle = LimitRadianAngle(angleToTarget - angleForward); +#ifdef FIX_BUGS + float speedTarget = pVehicle->AutoPilot.GetCruiseSpeed(); +#else + float speedTarget = pVehicle->AutoPilot.m_nCruiseSpeed; +#endif + float currentSpeed = pVehicle->GetMoveSpeed().Magnitude() * GAME_SPEED_TO_CARAI_SPEED; + float speedDiff = speedTarget - currentSpeed; + if (speedDiff <= 0.0f) { + speedDiff < -5.0f ? *pAccel = -0.2f : *pAccel = -0.1f; + } + else if (speedDiff / currentSpeed > 0.25f) { + *pAccel = 1.0f; + } + else { + *pAccel = 1.0f - (0.25f - speedDiff / currentSpeed) * 4.0f; + } + *pBrake = 0.0f; + *pSwerve = steerAngle; + *pHandbrake = false; + if (pVehicle->GetModelIndex() == MI_PREDATOR && distanceToPlayer < 40.0f && steerAngle < 0.15f) + pVehicle->FireFixedMachineGuns(); +} + +float CCarCtrl::FindMaxSteerAngle(CVehicle* pVehicle) +{ + return pVehicle->GetModelIndex() == MI_ENFORCER ? 0.7f : DEFAULT_MAX_STEER_ANGLE; +} + +void CCarCtrl::SteerAIHeliTowardsTargetCoors(CAutomobile* pHeli) +{ + if (pHeli->m_aWheelSpeed[1] < 0.22f) + pHeli->m_aWheelSpeed[1] += 0.001f; + if (pHeli->m_aWheelSpeed[1] < 0.15f) + return; + CVector2D vecToTarget = pHeli->AutoPilot.m_vecDestinationCoors - pHeli->GetPosition(); + float distanceToTarget = vecToTarget.Magnitude(); +#ifdef FIX_BUGS + float speed = pHeli->AutoPilot.GetCruiseSpeed() * 0.01f; +#else + float speed = pHeli->AutoPilot.m_nCruiseSpeed * 0.01f; +#endif + if (distanceToTarget <= 100.0f) + { + if (distanceToTarget > 75.0f) + speed *= 0.7f; + else if (distanceToTarget > 10.0f) + speed *= 0.4f; + else + speed *= 0.2f; + } + vecToTarget.Normalise(); + CVector2D vecAdvanceThisFrame(vecToTarget * speed); + float resistance = Pow(0.997f, CTimer::GetTimeStep()); + pHeli->m_vecMoveSpeed.x *= resistance; + pHeli->m_vecMoveSpeed.y *= resistance; + CVector2D vecSpeedDirection = vecAdvanceThisFrame - pHeli->m_vecMoveSpeed; + float vecSpeedChangeLength = vecSpeedDirection.Magnitude(); + vecSpeedDirection.Normalise(); + float changeMultiplier = 0.002f * CTimer::GetTimeStep(); + if (distanceToTarget < 5.0f) + changeMultiplier /= 5.0f; + if (vecSpeedChangeLength < changeMultiplier) + pHeli->SetMoveSpeed(vecAdvanceThisFrame.x, vecAdvanceThisFrame.y, pHeli->GetMoveSpeed().z); + else + pHeli->AddToMoveSpeed(vecSpeedDirection * changeMultiplier); + pHeli->GetMatrix().Translate(CTimer::GetTimeStep() * pHeli->GetMoveSpeed().x, CTimer::GetTimeStep() * pHeli->GetMoveSpeed().y, 0.0f); + float ZTarget = pHeli->AutoPilot.m_vecDestinationCoors.z; + if (CTimer::GetTimeInMilliseconds() & 0x800) // switch every ~2 seconds + ZTarget += 2.0f; + float ZSpeedTarget = (ZTarget - pHeli->GetPosition().z) * 0.01f; + float ZSpeedChangeTarget = ZSpeedTarget - pHeli->GetMoveSpeed().z; + float ZSpeedChangeMax = 0.001f * CTimer::GetTimeStep(); + if (!pHeli->bHeliDestroyed) { + if (Abs(ZSpeedChangeTarget) < ZSpeedChangeMax) + pHeli->SetMoveSpeed(pHeli->GetMoveSpeed().x, pHeli->GetMoveSpeed().y, ZSpeedTarget); + else if (ZSpeedChangeTarget < 0.0f) + pHeli->AddToMoveSpeed(0.0f, 0.0f, -ZSpeedChangeMax); + else + pHeli->AddToMoveSpeed(0.0f, 0.0f, 1.5f * ZSpeedChangeMax); + } + pHeli->GetMatrix().Translate(0.0f, 0.0f, CTimer::GetTimeStep() * pHeli->GetMoveSpeed().z); + pHeli->m_vecTurnSpeed.z *= Pow(0.99f, CTimer::GetTimeStep()); + float ZTurnSpeedTarget; + if (distanceToTarget < 8.0f && pHeli->m_fHeliOrientation < 0.0f) + ZTurnSpeedTarget = 0.0f; + else { + float fAngleTarget = CGeneral::GetATanOfXY(vecToTarget.x, vecToTarget.y) + PI; + if (pHeli->m_fHeliOrientation >= 0.0f) + fAngleTarget = pHeli->m_fHeliOrientation; + fAngleTarget -= pHeli->m_fOrientation; + while (fAngleTarget < -PI) + fAngleTarget += TWOPI; + while (fAngleTarget > PI) + fAngleTarget -= TWOPI; + if (Abs(fAngleTarget) <= 0.4f) + ZTurnSpeedTarget = 0.0f; + else if (fAngleTarget < 0.0f) + ZTurnSpeedTarget = -0.03f; + else + ZTurnSpeedTarget = 0.03f; + } + float ZTurnSpeedChangeTarget = ZTurnSpeedTarget - pHeli->GetTurnSpeed().z; + float ZTurnSpeedLimit = 0.0002f * CTimer::GetTimeStep(); + if (Abs(ZTurnSpeedChangeTarget) < ZTurnSpeedLimit) + pHeli->m_vecTurnSpeed.z = ZTurnSpeedTarget; + else if (ZTurnSpeedChangeTarget < 0.0f) + pHeli->m_vecTurnSpeed.z -= ZTurnSpeedLimit; + else + pHeli->m_vecTurnSpeed.z += ZTurnSpeedLimit; + pHeli->m_fOrientation += pHeli->GetTurnSpeed().z * CTimer::GetTimeStep(); + CVector up; + if (pHeli->bHeliMinimumTilt) + up = CVector(0.5f * pHeli->GetMoveSpeed().x, 0.5f * pHeli->GetMoveSpeed().y, 1.0f); + else + up = CVector(3.0f * pHeli->GetMoveSpeed().x, 3.0f * pHeli->GetMoveSpeed().y, 1.0f); + up.Normalise(); + CVector forward(Cos(pHeli->m_fOrientation), Sin(pHeli->m_fOrientation), 0.0f); + CVector right = CrossProduct(up, forward); + forward = CrossProduct(up, right); + pHeli->GetMatrix().GetRight() = right; + pHeli->GetMatrix().GetForward() = forward; + pHeli->GetMatrix().GetUp() = up; +} + +void CCarCtrl::SteerAIPlaneTowardsTargetCoors(CAutomobile* pPlane) +{ + CVector2D vecToTarget = pPlane->AutoPilot.m_vecDestinationCoors - pPlane->GetPosition(); + float fForwardZ = (pPlane->AutoPilot.m_vecDestinationCoors.z - pPlane->GetPosition().z) / vecToTarget.Magnitude(); + fForwardZ = Clamp(fForwardZ, -0.3f, 0.3f); + float angle = CGeneral::GetATanOfXY(vecToTarget.x, vecToTarget.y); + while (angle > TWOPI) + angle -= TWOPI; + float difference = LimitRadianAngle(angle - pPlane->m_fOrientation); + float steer = difference > 0.0f ? 0.04f : -0.04f; + if (Abs(difference) < 0.2f) + steer *= 5.0f * Abs(difference); + pPlane->m_fPlaneSteer *= Pow(0.96f, CTimer::GetTimeStep()); + float steerChange = steer - pPlane->m_fPlaneSteer; + float maxChange = 0.003f * CTimer::GetTimeStep(); + if (Abs(steerChange) < maxChange) + pPlane->m_fPlaneSteer = steer; + else if (steerChange < 0.0f) + pPlane->m_fPlaneSteer -= maxChange; + else + pPlane->m_fPlaneSteer += maxChange; + pPlane->m_fOrientation += pPlane->m_fPlaneSteer * CTimer::GetTimeStep(); + CVector up(0.0f, 0.0f, 1.0f); + up.Normalise(); + CVector forward(Cos(pPlane->m_fOrientation), Sin(pPlane->m_fOrientation), fForwardZ); + forward.Normalise(); + CVector right = CrossProduct(up, forward); + right.z -= 5.0f * pPlane->m_fPlaneSteer; + right.Normalise(); + up = CrossProduct(forward, right); + up.Normalise(); + right = CrossProduct(forward, up); + pPlane->GetMatrix().GetRight() = right; + pPlane->GetMatrix().GetForward() = forward; + pPlane->GetMatrix().GetUp() = up; + float newSplit = 1.0f - Pow(0.95f, CTimer::GetTimeStep()); + float oldSplit = 1.0f - newSplit; +#ifdef FIX_BUGS + pPlane->m_vecMoveSpeed = pPlane->m_vecMoveSpeed * oldSplit + pPlane->AutoPilot.GetCruiseSpeed() * 0.01f * forward * newSplit; +#else + pPlane->m_vecMoveSpeed = pPlane->m_vecMoveSpeed * oldSplit + pPlane->AutoPilot.m_nCruiseSpeed * 0.01f * forward * newSplit; +#endif + pPlane->m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); +} + +void CCarCtrl::SteerAICarWithPhysicsFollowPath(CVehicle* pVehicle, float* pSwerve, float* pAccel, float* pBrake, bool* pHandbrake) +{ + CVector2D forward = pVehicle->GetForward(); + forward.Normalise(); + CCarPathLink* pCurrentLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nCurrentPathNodeInfo]; + CCarPathLink* pNextLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo]; + CVector2D currentPathLinkForward(pCurrentLink->GetDirX() * pVehicle->AutoPilot.m_nCurrentDirection, + pCurrentLink->GetDirY() * pVehicle->AutoPilot.m_nCurrentDirection); + float nextPathLinkForwardX = pNextLink->GetDirX() * pVehicle->AutoPilot.m_nNextDirection; + float nextPathLinkForwardY = pNextLink->GetDirY() * pVehicle->AutoPilot.m_nNextDirection; + CVector2D positionOnCurrentLinkIncludingLane( + pCurrentLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForward.y, + pCurrentLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForward.x); + CVector2D positionOnNextLinkIncludingLane( + pNextLink->GetX() + ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardY, + pNextLink->GetY() - ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardX); + CVector2D distanceToNextNode = (CVector2D)pVehicle->GetPosition() - positionOnCurrentLinkIncludingLane; + float scalarDistanceToNextNode = distanceToNextNode.Magnitude(); + CVector2D distanceBetweenNodes = positionOnNextLinkIncludingLane - positionOnCurrentLinkIncludingLane; + float dp = DotProduct2D(distanceBetweenNodes, distanceToNextNode); + if (scalarDistanceToNextNode < DISTANCE_TO_NEXT_NODE_TO_SELECT_NEW || + dp > 0.0f && scalarDistanceToNextNode < DISTANCE_TO_FACING_NEXT_NODE_TO_SELECT_NEW || + dp / (scalarDistanceToNextNode * distanceBetweenNodes.Magnitude()) > 0.7f || + pVehicle->AutoPilot.m_nNextPathNodeInfo == pVehicle->AutoPilot.m_nCurrentPathNodeInfo){ + if (PickNextNodeAccordingStrategy(pVehicle)) { + switch (pVehicle->AutoPilot.m_nCarMission){ + case MISSION_GOTOCOORDS: + pVehicle->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS_STRAIGHT; + SteerAICarWithPhysicsHeadingForTarget(pVehicle, nil, pVehicle->AutoPilot.m_vecDestinationCoors.x, + pVehicle->AutoPilot.m_vecDestinationCoors.y, pSwerve, pAccel, pBrake, pHandbrake); + return; + case MISSION_GOTOCOORDS_ACCURATE: + pVehicle->AutoPilot.m_nCarMission = MISSION_GOTO_COORDS_STRAIGHT_ACCURATE; + SteerAICarWithPhysicsHeadingForTarget(pVehicle, nil, pVehicle->AutoPilot.m_vecDestinationCoors.x, + pVehicle->AutoPilot.m_vecDestinationCoors.y, pSwerve, pAccel, pBrake, pHandbrake); + return; + default: break; + } + } + pCurrentLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nCurrentPathNodeInfo]; + scalarDistanceToNextNode = CVector2D( + pCurrentLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForward.y - pVehicle->GetPosition().x, + pCurrentLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForward.x - pVehicle->GetPosition().y).Magnitude(); + pNextLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo]; + currentPathLinkForward.x = pCurrentLink->GetDirX() * pVehicle->AutoPilot.m_nCurrentDirection; + currentPathLinkForward.y = pCurrentLink->GetDirY() * pVehicle->AutoPilot.m_nCurrentDirection; + nextPathLinkForwardX = pNextLink->GetDirX() * pVehicle->AutoPilot.m_nNextDirection; + nextPathLinkForwardY = pNextLink->GetDirY() * pVehicle->AutoPilot.m_nNextDirection; + } + positionOnCurrentLinkIncludingLane.x = pCurrentLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForward.y; + positionOnCurrentLinkIncludingLane.y = pCurrentLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForward.x; + CVector2D projectedPosition = positionOnCurrentLinkIncludingLane - currentPathLinkForward * scalarDistanceToNextNode * 0.4f; + if (scalarDistanceToNextNode > DISTANCE_TO_NEXT_NODE_TO_CONSIDER_SLOWING_DOWN){ + projectedPosition.x = positionOnCurrentLinkIncludingLane.x; + projectedPosition.y = positionOnCurrentLinkIncludingLane.y; + } + CVector2D distanceToProjectedPosition = projectedPosition - pVehicle->GetPosition(); + float angleCurrentLink = CGeneral::GetATanOfXY(distanceToProjectedPosition.x, distanceToProjectedPosition.y); + float angleForward = CGeneral::GetATanOfXY(forward.x, forward.y); + if (pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_AVOID_CARS) + angleCurrentLink = FindAngleToWeaveThroughTraffic(pVehicle, nil, angleCurrentLink, angleForward); + float steerAngle = LimitRadianAngle(angleCurrentLink - angleForward); + float maxAngle = FindMaxSteerAngle(pVehicle); + steerAngle = Min(maxAngle, Max(-maxAngle, steerAngle)); + if (pVehicle->GetMoveSpeed().Magnitude() > MIN_SPEED_TO_START_LIMITING_STEER) + steerAngle = Min(MAX_ANGLE_TO_STEER_AT_HIGH_SPEED, Max(-MAX_ANGLE_TO_STEER_AT_HIGH_SPEED, steerAngle)); + float currentForwardSpeed = DotProduct(pVehicle->GetMoveSpeed(), pVehicle->GetForward()) * GAME_SPEED_TO_CARAI_SPEED; + float speedStyleMultiplier; + switch (pVehicle->AutoPilot.m_nDrivingStyle) { + case DRIVINGSTYLE_STOP_FOR_CARS: + case DRIVINGSTYLE_SLOW_DOWN_FOR_CARS: + case DRIVINGSTYLE_STOP_FOR_CARS_IGNORE_LIGHTS: + speedStyleMultiplier = FindMaximumSpeedForThisCarInTraffic(pVehicle); +#ifdef FIX_BUGS + if (pVehicle->AutoPilot.GetCruiseSpeed() != 0) + speedStyleMultiplier /= pVehicle->AutoPilot.GetCruiseSpeed(); +#else + speedStyleMultiplier /= pVehicle->AutoPilot.m_nCruiseSpeed; +#endif + break; + default: + speedStyleMultiplier = 1.0f; + break; + } + switch (pVehicle->AutoPilot.m_nDrivingStyle) { + case DRIVINGSTYLE_STOP_FOR_CARS: + case DRIVINGSTYLE_SLOW_DOWN_FOR_CARS: + if (CTrafficLights::ShouldCarStopForLight(pVehicle, false)){ + CCarAI::CarHasReasonToStop(pVehicle); + speedStyleMultiplier = 0.0f; + } + break; + default: + break; + } + if (CTrafficLights::ShouldCarStopForBridge(pVehicle)){ + CCarAI::CarHasReasonToStop(pVehicle); + speedStyleMultiplier = 0.0f; + } + CVector2D trajectory(pCurrentLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForward.y, + pCurrentLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForward.x); + trajectory -= pVehicle->GetPosition(); + float speedAngleMultiplier = FindSpeedMultiplier( + CGeneral::GetATanOfXY(trajectory.x, trajectory.y) - angleForward, + MIN_ANGLE_FOR_SPEED_LIMITING, MAX_ANGLE_FOR_SPEED_LIMITING, MIN_LOWERING_SPEED_COEFFICIENT); + float tmpWideMultiplier = FindSpeedMultiplier( + CGeneral::GetATanOfXY(currentPathLinkForward.x, currentPathLinkForward.y) - + CGeneral::GetATanOfXY(nextPathLinkForwardX, nextPathLinkForwardY), + MIN_ANGLE_FOR_SPEED_LIMITING_BETWEEN_NODES, MAX_ANGLE_FOR_SPEED_LIMITING, MIN_LOWERING_SPEED_COEFFICIENT); + float speedNodesMultiplier; + if (scalarDistanceToNextNode > DISTANCE_TO_NEXT_NODE_TO_CONSIDER_SLOWING_DOWN || pVehicle->AutoPilot.m_nCruiseSpeed < 12) + speedNodesMultiplier = 1.0f; + else + speedNodesMultiplier = 1.0f - + (1.0f - scalarDistanceToNextNode / DISTANCE_TO_NEXT_NODE_TO_CONSIDER_SLOWING_DOWN) * + (1.0f - tmpWideMultiplier); + float speedMultiplier = Min(speedStyleMultiplier, Min(speedAngleMultiplier, speedNodesMultiplier)); + float speed = pVehicle->AutoPilot.m_nCruiseSpeed * speedMultiplier; + float speedDifference = speed - currentForwardSpeed; + if (speed < 0.05f && speedDifference < 0.03f){ + *pBrake = 1.0f; + *pAccel = 0.0f; + }else if (speedDifference <= 0.0f){ + *pBrake = Min(0.5f, -speedDifference * 0.05f); + *pAccel = 0.0f; + }else if (currentForwardSpeed < 2.0f){ + *pBrake = 0.0f; + *pAccel = Min(1.0f, speedDifference * 0.25f); + }else{ + *pBrake = 0.0f; + *pAccel = Min(1.0f, speedDifference * 0.125f); + } + *pSwerve = steerAngle; + *pHandbrake = false; +} + +void CCarCtrl::SteerAICarWithPhysicsHeadingForTarget(CVehicle* pVehicle, CPhysical* pTarget, float targetX, float targetY, float* pSwerve, float* pAccel, float* pBrake, bool* pHandbrake) +{ + *pHandbrake = false; + CVector2D forward = pVehicle->GetForward(); + forward.Normalise(); + float angleToTarget = CGeneral::GetATanOfXY(targetX - pVehicle->GetPosition().x, targetY - pVehicle->GetPosition().y); + float angleForward = CGeneral::GetATanOfXY(forward.x, forward.y); + if (pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_AVOID_CARS) + angleToTarget = FindAngleToWeaveThroughTraffic(pVehicle, pTarget, angleToTarget, angleForward); + float steerAngle = LimitRadianAngle(angleToTarget - angleForward); + if (pVehicle->GetMoveSpeed().Magnitude() > MIN_SPEED_TO_APPLY_HANDBRAKE) + if (ABS(steerAngle) > MIN_ANGLE_TO_APPLY_HANDBRAKE) + *pHandbrake = true; + float maxAngle = FindMaxSteerAngle(pVehicle); + steerAngle = Min(maxAngle, Max(-maxAngle, steerAngle)); + float speedMultiplier = FindSpeedMultiplier(CGeneral::GetATanOfXY(targetX - pVehicle->GetPosition().x, targetY - pVehicle->GetPosition().y) - angleForward, + MIN_ANGLE_FOR_SPEED_LIMITING, MAX_ANGLE_FOR_SPEED_LIMITING, MIN_LOWERING_SPEED_COEFFICIENT); + float speedTarget = pVehicle->AutoPilot.m_nCruiseSpeed * speedMultiplier; + float currentSpeed = pVehicle->GetMoveSpeed().Magnitude() * GAME_SPEED_TO_CARAI_SPEED; + float speedDiff = speedTarget - currentSpeed; + if (speedDiff <= 0.0f){ + *pAccel = 0.0f; + *pBrake = Min(0.5f, -speedDiff / 20.0f); + }else if (currentSpeed < 25.0f){ + *pAccel = Min(1.0f, speedDiff * 0.1f); + *pBrake = 0.0f; + }else{ + *pAccel = 1.0f; + *pBrake = 0.0f; + } + *pSwerve = steerAngle; +} + +void CCarCtrl::SteerAICarWithPhysicsTryingToBlockTarget(CVehicle* pVehicle, float targetX, float targetY, float targetSpeedX, float targetSpeedY, float* pSwerve, float* pAccel, float* pBrake, bool* pHandbrake) +{ + CVector2D targetPos(targetX, targetY); + CVector2D offset(targetSpeedX, targetSpeedY); + float trajectoryLen = offset.Magnitude(); + if (trajectoryLen > MAX_SPEED_TO_ACCOUNT_IN_INTERCEPTING) + offset *= MAX_SPEED_TO_ACCOUNT_IN_INTERCEPTING / trajectoryLen; + targetPos += offset * GAME_SPEED_TO_CARAI_SPEED; + pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + SteerAICarWithPhysicsHeadingForTarget(pVehicle, nil, targetPos.x, targetPos.y, pSwerve, pAccel, pBrake, pHandbrake); + if ((targetPos - pVehicle->GetPosition()).MagnitudeSqr() < SQR(DISTANCE_TO_SWITCH_FROM_BLOCK_TO_STOP)) + pVehicle->AutoPilot.m_nCarMission = (pVehicle->AutoPilot.m_nCarMission == MISSION_BLOCKCAR_CLOSE) ? + MISSION_BLOCKCAR_HANDBRAKESTOP : MISSION_BLOCKPLAYER_HANDBRAKESTOP; +} + +void CCarCtrl::SteerAICarWithPhysicsTryingToBlockTarget_Stop(CVehicle* pVehicle, float targetX, float targetY, float targetSpeedX, float targetSpeedY, float* pSwerve, float* pAccel, float* pBrake, bool* pHandbrake) +{ + *pSwerve = 0.0f; + *pAccel = 0.0f; + *pBrake = 1.0f; + *pHandbrake = true; + float distanceToTargetSqr = (CVector2D(targetX, targetY) - pVehicle->GetPosition()).MagnitudeSqr(); + if (distanceToTargetSqr > SQR(DISTANCE_TO_SWITCH_FROM_STOP_TO_BLOCK)){ + pVehicle->AutoPilot.m_nCarMission = (pVehicle->AutoPilot.m_nCarMission == MISSION_BLOCKCAR_HANDBRAKESTOP) ? + MISSION_BLOCKCAR_CLOSE : MISSION_BLOCKPLAYER_CLOSE; + return; + } + if (pVehicle->AutoPilot.m_nCarMission == MISSION_BLOCKCAR_HANDBRAKESTOP){ + if (((CVector2D)pVehicle->GetMoveSpeed()).MagnitudeSqr() < SQR(0.01f) && + CVector2D(targetSpeedX, targetSpeedY).MagnitudeSqr() < SQR(0.02f) && + pVehicle->bIsLawEnforcer){ + CCarAI::TellOccupantsToLeaveCar(pVehicle); + pVehicle->AutoPilot.m_nCruiseSpeed = 0; + pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; + } + }else{ + if (FindPlayerVehicle() && FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f) +#ifdef FIX_BUGS + pVehicle->m_nTimeBlocked += CTimer::GetTimeStepInMilliseconds(); +#else + pVehicle->m_nTimeBlocked += 1000.0f / 60.0f * CTimer::GetTimeStep(); // very doubtful constant +#endif + else + pVehicle->m_nTimeBlocked = 0; + if (FindPlayerVehicle() == nil || FindPlayerVehicle()->IsUpsideDown() || + FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f && + pVehicle->m_nTimeBlocked > TIME_COPS_WAIT_TO_EXIT_AFTER_STOPPING){ + if (pVehicle->bIsLawEnforcer && distanceToTargetSqr < SQR(DISTANCE_TO_SWITCH_FROM_STOP_TO_BLOCK)){ + CCarAI::TellOccupantsToLeaveCar(pVehicle); + pVehicle->AutoPilot.m_nCruiseSpeed = 0; + pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; + } + } + } +} + +void +CCarCtrl::RegisterVehicleOfInterest(CVehicle* pVehicle) +{ + for (int i = 0; i < MAX_CARS_TO_KEEP; i++) { + if (apCarsToKeep[i] == pVehicle) { + aCarsToKeepTime[i] = CTimer::GetTimeInMilliseconds(); + return; + } + } + for (int i = 0; i < MAX_CARS_TO_KEEP; i++) { + if (!apCarsToKeep[i]) { + apCarsToKeep[i] = pVehicle; + aCarsToKeepTime[i] = CTimer::GetTimeInMilliseconds(); + return; + } + } + uint32 oldestCarWeKeepTime = UINT32_MAX; + int oldestCarWeKeepIndex = 0; + for (int i = 0; i < MAX_CARS_TO_KEEP; i++) { + if (apCarsToKeep[i] && aCarsToKeepTime[i] < oldestCarWeKeepTime) { + oldestCarWeKeepTime = aCarsToKeepTime[i]; + oldestCarWeKeepIndex = i; + } + } + apCarsToKeep[oldestCarWeKeepIndex] = pVehicle; + aCarsToKeepTime[oldestCarWeKeepIndex] = CTimer::GetTimeInMilliseconds(); +} + +bool +CCarCtrl::IsThisVehicleInteresting(CVehicle* pVehicle) +{ + for (int i = 0; i < MAX_CARS_TO_KEEP; i++) { + if (apCarsToKeep[i] == pVehicle) + return true; + } + return false; +} + +void CCarCtrl::RemoveFromInterestingVehicleList(CVehicle* pVehicle) +{ + for (int i = 0; i < MAX_CARS_TO_KEEP; i++) { + if (apCarsToKeep[i] == pVehicle) + apCarsToKeep[i] = nil; + } +} + +void CCarCtrl::ClearInterestingVehicleList() +{ + for (int i = 0; i < MAX_CARS_TO_KEEP; i++) { + apCarsToKeep[i] = nil; + } +} + +void CCarCtrl::SwitchVehicleToRealPhysics(CVehicle* pVehicle) +{ + pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds(); +} + +void CCarCtrl::JoinCarWithRoadSystem(CVehicle* pVehicle) +{ + pVehicle->AutoPilot.m_nPrevRouteNode = pVehicle->AutoPilot.m_nCurrentRouteNode = pVehicle->AutoPilot.m_nNextRouteNode = 0; + pVehicle->AutoPilot.m_nCurrentPathNodeInfo = pVehicle->AutoPilot.m_nPreviousPathNodeInfo = pVehicle->AutoPilot.m_nNextPathNodeInfo = 0; + int nodeId = ThePaths.FindNodeClosestToCoorsFavourDirection(pVehicle->GetPosition(), 0, pVehicle->GetForward().x, pVehicle->GetForward().y); + CPathNode* pNode = &ThePaths.m_pathNodes[nodeId]; + int prevNodeId = -1; + float minDistance = 999999.9f; + for (int i = 0; i < pNode->numLinks; i++){ + int candidateId = ThePaths.ConnectedNode(i + pNode->firstLink); + CPathNode* pCandidateNode = &ThePaths.m_pathNodes[candidateId]; + float distance = (pCandidateNode->GetPosition() - pNode->GetPosition()).Magnitude2D(); + if (distance < minDistance){ + minDistance = distance; + prevNodeId = candidateId; + } + } + if (prevNodeId < 0) + return; + CVector2D forward = pVehicle->GetForward(); + CPathNode* pPrevNode = &ThePaths.m_pathNodes[prevNodeId]; + if (forward.x == 0.0f && forward.y == 0.0f) + forward.x = 1.0f; + if (DotProduct2D(pNode->GetPosition() - pPrevNode->GetPosition(), forward) < 0.0f){ + int tmp; + tmp = prevNodeId; + prevNodeId = nodeId; + nodeId = tmp; + } + pVehicle->AutoPilot.m_nPrevRouteNode = 0; + pVehicle->AutoPilot.m_nCurrentRouteNode = prevNodeId; + pVehicle->AutoPilot.m_nNextRouteNode = nodeId; + pVehicle->AutoPilot.m_nPathFindNodesCount = 0; + FindLinksToGoWithTheseNodes(pVehicle); + pVehicle->AutoPilot.m_nNextLane = pVehicle->AutoPilot.m_nCurrentLane = 0; +} + +bool CCarCtrl::JoinCarWithRoadSystemGotoCoors(CVehicle* pVehicle, CVector vecTarget, bool isProperNow) +{ + pVehicle->AutoPilot.m_vecDestinationCoors = vecTarget; + ThePaths.DoPathSearch(0, pVehicle->GetPosition(), -1, vecTarget, pVehicle->AutoPilot.m_aPathFindNodesInfo, + &pVehicle->AutoPilot.m_nPathFindNodesCount, NUM_PATH_NODES_IN_AUTOPILOT, pVehicle, nil, 999999.9f, -1); + ThePaths.RemoveBadStartNode(pVehicle->GetPosition(), + pVehicle->AutoPilot.m_aPathFindNodesInfo, &pVehicle->AutoPilot.m_nPathFindNodesCount); + if (pVehicle->AutoPilot.m_nPathFindNodesCount < 2){ + pVehicle->AutoPilot.m_nPrevRouteNode = pVehicle->AutoPilot.m_nCurrentRouteNode = pVehicle->AutoPilot.m_nNextRouteNode = 0; + pVehicle->AutoPilot.m_nPathFindNodesCount = 0; + return true; + } + pVehicle->AutoPilot.m_nPrevRouteNode = 0; + pVehicle->AutoPilot.m_nCurrentRouteNode = pVehicle->AutoPilot.m_aPathFindNodesInfo[0] - ThePaths.m_pathNodes; + pVehicle->AutoPilot.RemoveOnePathNode(); + pVehicle->AutoPilot.m_nNextRouteNode = pVehicle->AutoPilot.m_aPathFindNodesInfo[0] - ThePaths.m_pathNodes; + pVehicle->AutoPilot.RemoveOnePathNode(); + FindLinksToGoWithTheseNodes(pVehicle); + pVehicle->AutoPilot.m_nNextLane = pVehicle->AutoPilot.m_nCurrentLane = 0; + return false; +} + +void CCarCtrl::FindLinksToGoWithTheseNodes(CVehicle* pVehicle) +{ + if (pVehicle->m_nRouteSeed) + CGeneral::SetRandomSeed(pVehicle->m_nRouteSeed); + int nextLink; + CPathNode* pCurNode = &ThePaths.m_pathNodes[pVehicle->AutoPilot.m_nCurrentRouteNode]; + for (nextLink = 0; nextLink < 12; nextLink++) + if (ThePaths.ConnectedNode(nextLink + pCurNode->firstLink) == pVehicle->AutoPilot.m_nNextRouteNode) + break; + pVehicle->AutoPilot.m_nNextPathNodeInfo = ThePaths.m_carPathConnections[nextLink + pCurNode->firstLink]; + pVehicle->AutoPilot.m_nNextDirection = (pVehicle->AutoPilot.m_nCurrentRouteNode >= pVehicle->AutoPilot.m_nNextRouteNode) ? 1 : -1; + int curLink; + int curConnection; + if (pCurNode->numLinks == 1) { + curLink = 0; + curConnection = ThePaths.m_carPathConnections[pCurNode->firstLink]; + }else{ + int closestLink = -1; + float md = 999999.9f; + + for (curLink = 0; curLink < pCurNode->numLinks; curLink++) { + int node = ThePaths.ConnectedNode(curLink + pCurNode->firstLink); + CPathNode* pNode = &ThePaths.m_pathNodes[node]; + if (node == pVehicle->AutoPilot.m_nNextRouteNode) + continue; + CVector vCurPos = pCurNode->GetPosition(); + CVector vNextPos = pNode->GetPosition(); + float dist = CCollision::DistToLine(&vCurPos, &vNextPos, &pVehicle->GetPosition()); + if (dist < md) { + md = dist; + closestLink = curLink; + } + } + curConnection = ThePaths.m_carPathConnections[closestLink + pCurNode->firstLink]; + } + pVehicle->AutoPilot.m_nCurrentPathNodeInfo = curConnection; + pVehicle->AutoPilot.m_nCurrentDirection = (ThePaths.ConnectedNode(curLink + pCurNode->firstLink) >= pVehicle->AutoPilot.m_nCurrentRouteNode) ? 1 : -1; +} + +void CCarCtrl::GenerateEmergencyServicesCar(void) +{ + if (FindPlayerPed()->m_pWanted->GetWantedLevel() > 3) + return; + if (CGame::IsInInterior()) + return; + if (NumFiretrucksOnDuty + NumAmbulancesOnDuty + NumParkedCars + NumMissionCars + + NumLawEnforcerCars + NumRandomCars > MaxNumberOfCarsInUse) + return; + if (NumAmbulancesOnDuty == 0){ + if (gAccidentManager.CountActiveAccidents() < 2){ + if (CStreaming::HasModelLoaded(MI_AMBULAN)) + CStreaming::SetModelIsDeletable(MI_MEDIC); + }else{ + float distance = 30.0f; + CAccident* pNearestAccident = gAccidentManager.FindNearestAccident(FindPlayerCoors(), &distance); + if (pNearestAccident){ + if (CountCarsOfType(MI_AMBULAN) < 2 && CTimer::GetTimeInMilliseconds() > LastTimeAmbulanceCreated + 30000){ + CStreaming::RequestModel(MI_AMBULAN, STREAMFLAGS_DEPENDENCY); + CStreaming::RequestModel(MI_MEDIC, STREAMFLAGS_DONT_REMOVE); + if (CStreaming::HasModelLoaded(MI_AMBULAN) && CStreaming::HasModelLoaded(MI_MEDIC)){ + if (GenerateOneEmergencyServicesCar(MI_AMBULAN, pNearestAccident->m_pVictim->GetPosition())){ + LastTimeAmbulanceCreated = CTimer::GetTimeInMilliseconds(); + } + } + } + } + } + } + if (NumFiretrucksOnDuty == 0){ + if (gFireManager.GetTotalActiveFires() < 3){ + if (CStreaming::HasModelLoaded(MI_FIRETRUCK)) + CStreaming::SetModelIsDeletable(MI_FIREMAN); + }else{ + float distance = 30.0f; + CFire* pNearestFire = gFireManager.FindNearestFire(FindPlayerCoors(), &distance); + if (pNearestFire) { + if (CountCarsOfType(MI_FIRETRUCK) < 2 && CTimer::GetTimeInMilliseconds() > LastTimeFireTruckCreated + 35000){ + CStreaming::RequestModel(MI_FIRETRUCK, STREAMFLAGS_DEPENDENCY); + CStreaming::RequestModel(MI_FIREMAN, STREAMFLAGS_DONT_REMOVE); + if (CStreaming::HasModelLoaded(MI_FIRETRUCK) && CStreaming::HasModelLoaded(MI_FIREMAN)){ + if (GenerateOneEmergencyServicesCar(MI_FIRETRUCK, pNearestFire->m_vecPos)){ + LastTimeFireTruckCreated = CTimer::GetTimeInMilliseconds(); +#ifdef SECUROM + if ((myrand() & 7) == 5){ + // if pirated game + CPickups::Init(); + } +#endif + } + } + } + } + } + } +} + +bool CCarCtrl::GenerateOneEmergencyServicesCar(uint32 mi, CVector vecPos) +{ + CVector pPlayerPos = FindPlayerCentreOfWorld(CWorld::PlayerInFocus); + bool created = false; + int attempts = 0; + CVector spawnPos; + int32 curNode, nextNode; + float posBetweenNodes; + while (!created && attempts < 5){ + if (ThePaths.GenerateCarCreationCoors(pPlayerPos.x, pPlayerPos.y, 0.707f, 0.707f, + REQUEST_ONSCREEN_DISTANCE, -1.0f, true, &spawnPos, &curNode, &nextNode, &posBetweenNodes, false)){ + int16 colliding[2]; + if (!ThePaths.GetNode(curNode)->bWaterPath) { + CWorld::FindObjectsKindaColliding(spawnPos, 10.0f, true, colliding, 2, nil, false, true, true, false, false); + if (colliding[0] == 0) + created = true; + } + } + attempts += 1; + } + if (attempts >= 5) + return false; + CAutomobile* pVehicle = new CAutomobile(mi, RANDOM_VEHICLE); + pVehicle->AutoPilot.m_vecDestinationCoors = vecPos; + pVehicle->SetPosition(spawnPos); + pVehicle->AutoPilot.m_nCarMission = (JoinCarWithRoadSystemGotoCoors(pVehicle, vecPos, false)) ? MISSION_GOTOCOORDS_STRAIGHT : MISSION_GOTOCOORDS; + pVehicle->AutoPilot.m_fMaxTrafficSpeed = pVehicle->AutoPilot.m_nCruiseSpeed = 25; + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + CVector2D direction = vecPos - spawnPos; + direction.Normalise(); + pVehicle->GetForward() = CVector(direction.x, direction.y, 0.0f); + pVehicle->GetRight() = CVector(direction.y, -direction.x, 0.0f); + pVehicle->GetUp() = CVector(0.0f, 0.0f, 1.0f); + spawnPos.z = posBetweenNodes * ThePaths.m_pathNodes[curNode].GetZ() + (1.0f - posBetweenNodes) * ThePaths.m_pathNodes[nextNode].GetZ(); + float groundZ = INFINITE_Z; + CColPoint colPoint; + CEntity* pEntity; + if (CWorld::ProcessVerticalLine(spawnPos, 1000.0f, colPoint, pEntity, true, false, false, false, true, false, nil)) + groundZ = colPoint.point.z; + if (CWorld::ProcessVerticalLine(spawnPos, -1000.0f, colPoint, pEntity, true, false, false, false, true, false, nil)) { + if (ABS(colPoint.point.z - spawnPos.z) < ABS(groundZ - spawnPos.z)) + groundZ = colPoint.point.z; + } + if (groundZ == INFINITE_Z) { + delete pVehicle; + return false; + } + spawnPos.z = groundZ + pVehicle->GetDistanceFromCentreOfMassToBaseOfModel(); + pVehicle->SetPosition(spawnPos); + pVehicle->SetMoveSpeed(CVector(0.0f, 0.0f, 0.0f)); + pVehicle->SetStatus(STATUS_PHYSICS); + switch (mi){ + case MI_FIRETRUCK: + pVehicle->bIsFireTruckOnDuty = true; + ++NumFiretrucksOnDuty; + CCarAI::AddFiretruckOccupants(pVehicle); + break; + case MI_AMBULAN: + pVehicle->bIsAmbulanceOnDuty = true; + ++NumAmbulancesOnDuty; + CCarAI::AddAmbulanceOccupants(pVehicle); + break; + } + pVehicle->m_bSirenOrAlarm = true; + CWorld::Add(pVehicle); + printf("CREATED EMERGENCY VEHICLE\n"); + return true; +} + +void CCarCtrl::UpdateCarCount(CVehicle* pVehicle, bool remove) +{ + if (remove){ + switch (pVehicle->VehicleCreatedBy){ + case RANDOM_VEHICLE: + if (pVehicle->bIsLawEnforcer) { + if (--NumLawEnforcerCars < 0) + NumLawEnforcerCars = 0; + } + if (--NumRandomCars < 0) + NumRandomCars = 0; + return; + case MISSION_VEHICLE: + if (--NumMissionCars < 0) + NumMissionCars = 0; + return; + case PARKED_VEHICLE: + if (--NumParkedCars < 0) + NumParkedCars = 0; + return; + case PERMANENT_VEHICLE: + if (--NumPermanentCars < 0) + NumPermanentCars = 0; + return; + } + } + else{ + switch (pVehicle->VehicleCreatedBy){ + case RANDOM_VEHICLE: + if (pVehicle->bIsLawEnforcer) + ++NumLawEnforcerCars; + ++NumRandomCars; + return; + case MISSION_VEHICLE: + ++NumMissionCars; + return; + case PARKED_VEHICLE: + ++NumParkedCars; + return; + case PERMANENT_VEHICLE: + ++NumPermanentCars; + return; + } + } +} + +bool CCarCtrl::ThisRoadObjectCouldMove(int16 mi) +{ +#ifdef GTA_BRIDGE + return mi == MI_BRIDGELIFT || mi == MI_BRIDGEROADSEGMENT; +#else + return false; +#endif +} + +bool CCarCtrl::MapCouldMoveInThisArea(float x, float y) +{ +#ifdef GTA_BRIDGE // actually they forgot that in VC... + // bridge moves up and down + return x > -342.0f && x < -219.0f && + y > -677.0f && y < -580.0f; +#else + return false; +#endif +} + +float CCarCtrl::FindSpeedMultiplierWithSpeedFromNodes(int8 type) +{ + switch (type) + { + case 1: return 1.5f; + case 2: return 2.0f; + } + return 1.0f; +} diff --git a/src/miami/control/CarCtrl.h b/src/miami/control/CarCtrl.h new file mode 100644 index 00000000..5efbe275 --- /dev/null +++ b/src/miami/control/CarCtrl.h @@ -0,0 +1,174 @@ +#pragma once +#include "PathFind.h" +#include "Boat.h" +#include "Vehicle.h" + +#define GAME_SPEED_TO_METERS_PER_SECOND 50.0f +#define METERS_PER_SECOND_TO_GAME_SPEED (1.0f / GAME_SPEED_TO_METERS_PER_SECOND) +#define GAME_SPEED_TO_CARAI_SPEED 60.0f +#define TIME_COPS_WAIT_TO_EXIT_AFTER_STOPPING 2500 + +class CZoneInfo; +class CAutomobile; + +enum{ + MAX_CARS_TO_KEEP = 2, + MAX_CAR_MODELS_IN_ARRAY = 25, +}; + +#ifdef FIX_BUGS +#define FIX_PATHFIND_BUG +#endif + +class CCarCtrl +{ +public: + enum eCarClass { + NORMAL = 0, + POOR, + RICH, + EXEC, + WORKER, + BIG, + TAXI, + MOPED, + MOTORBIKE, + + LEISUREBOAT, + WORKERBOAT, + + COPS, + CUBAN, + HAITIAN, + STREET, + DIAZ, + BIKER, + SECURITY, + PLAYER, + GOLFERS, + GANG9, + COPS_BOAT, + FIRST_CAR_RATING = NORMAL, + FIRST_BOAT_RATING = LEISUREBOAT, + FIRST_GANG_CAR_RATING = CUBAN, + NUM_CAR_CLASSES = MOTORBIKE - FIRST_CAR_RATING + 1, + NUM_BOAT_CLASSES = WORKERBOAT - FIRST_BOAT_RATING + 1, + NUM_GANG_CAR_CLASSES = GANG9 - FIRST_GANG_CAR_RATING + 1, + TOTAL_CUSTOM_CLASSES = NUM_CAR_CLASSES + NUM_BOAT_CLASSES + }; + + static void SwitchVehicleToRealPhysics(CVehicle*); + static void AddToCarArray(int32 id, int32 vehclass); + static void UpdateCarCount(CVehicle*, bool); + static int32 ChooseCarModel(int32 vehclass); + static bool JoinCarWithRoadSystemGotoCoors(CVehicle*, CVector, bool); + static void JoinCarWithRoadSystem(CVehicle*); + static void UpdateCarOnRails(CVehicle*); + static bool MapCouldMoveInThisArea(float x, float y); + static void ScanForPedDanger(CVehicle *veh); + static void RemoveFromInterestingVehicleList(CVehicle*); + static void GenerateRandomCars(void); + static void GenerateOneRandomCar(void); + static void GenerateEmergencyServicesCar(void); + static int32 ChooseModel(CZoneInfo*, int*); + static int32 ChoosePoliceCarModel(void); + static int32 ChooseGangCarModel(int32 gang); + static void RemoveDistantCars(void); + static void PossiblyRemoveVehicle(CVehicle*); + static bool IsThisVehicleInteresting(CVehicle*); + static void RegisterVehicleOfInterest(CVehicle*); + static int32 CountCarsOfType(int32 mi); + static void SlowCarOnRailsDownForTrafficAndLights(CVehicle*); + static bool PickNextNodeAccordingStrategy(CVehicle*); + static void DragCarToPoint(CVehicle*, CVector*); + static float FindMaximumSpeedForThisCarInTraffic(CVehicle*); + static void SlowCarDownForCarsSectorList(CPtrList&, CVehicle*, float, float, float, float, float*, float); + static void SlowCarDownForPedsSectorList(CPtrList&, CVehicle*, float, float, float, float, float*, float); + static void SlowCarDownForOtherCar(CEntity*, CVehicle*, float*, float); + static float TestCollisionBetween2MovingRects(CVehicle*, CVehicle*, float, float, CVector*, CVector*, uint8); + static float FindAngleToWeaveThroughTraffic(CVehicle*, CPhysical*, float, float); + static void WeaveThroughCarsSectorList(CPtrList&, CVehicle*, CPhysical*, float, float, float, float, float*, float*); + static void WeaveForOtherCar(CEntity*, CVehicle*, float*, float*); + static void WeaveThroughPedsSectorList(CPtrList&, CVehicle*, CPhysical*, float, float, float, float, float*, float*); + static void WeaveForPed(CEntity*, CVehicle*, float*, float*); + static void WeaveThroughObjectsSectorList(CPtrList&, CVehicle*, float, float, float, float, float*, float*); + static void WeaveForObject(CEntity*, CVehicle*, float*, float*); +#ifdef FIX_PATHFIND_BUG + static void PickNextNodeToChaseCar(CVehicle*, float, float, float, CVehicle*); +#else + static void PickNextNodeToChaseCar(CVehicle*, float, float, CVehicle*); +#endif + static bool PickNextNodeToFollowPath(CVehicle*); + static void PickNextNodeRandomly(CVehicle*); + static uint8 FindPathDirection(int32, int32, int32); + static void Init(void); + static void ReInit(void); + static float FindSpeedMultiplier(float, float, float, float); + static void SteerAICarWithPhysics(CVehicle*); + static void SteerAICarWithPhysics_OnlyMission(CVehicle*, float*, float*, float*, bool*); + static float FindMaxSteerAngle(CVehicle*); + static void SteerAICarWithPhysicsFollowPath(CVehicle*, float*, float*, float*, bool*); + static void SteerAICarWithPhysicsHeadingForTarget(CVehicle*, CPhysical*, float, float, float*, float*, float*, bool*); + static void SteerAICarWithPhysicsTryingToBlockTarget(CVehicle*, float, float, float, float, float*, float*, float*, bool*); + static void SteerAICarWithPhysicsTryingToBlockTarget_Stop(CVehicle*, float, float, float, float, float*, float*, float*, bool*); + static bool ThisRoadObjectCouldMove(int16); + static void ClearInterestingVehicleList(); + static void FindLinksToGoWithTheseNodes(CVehicle*); + static bool GenerateOneEmergencyServicesCar(uint32, CVector); + static float FindSpeedMultiplierWithSpeedFromNodes(int8); + static int32 ChooseBoatModel(int32); + static int32 ChooseBoatRating(CZoneInfo* pZoneInfo); + static int32 ChooseCarRating(CZoneInfo* pZoneInfo); + static void AddToLoadedVehicleArray(int32 mi, int32 rating, int32 freq); + static void RemoveFromLoadedVehicleArray(int32 mi, int32 rating); + static int32 ChooseCarModelToLoad(int32 rating); + static bool BoatWithTallMast(int32 mi); + static void RemoveCarsIfThePoolGetsFull(void); + static void SteerAIBoatWithPhysicsHeadingForTarget(CVehicle*, float, float, float*, float*, float*); + static void SteerAIHeliTowardsTargetCoors(CAutomobile*); + static void SteerAIPlaneTowardsTargetCoors(CAutomobile*); + static void SteerAIBoatWithPhysicsAttackingPlayer(CVehicle*, float*, float*, float*, bool*); + static void SteerAICarBlockingPlayerForwardAndBack(CVehicle*, float*, float*, float*, bool*); + + static float GetPositionAlongCurrentCurve(CVehicle* pVehicle) + { + uint32 timeInCurve = CTimer::GetTimeInMilliseconds() - pVehicle->AutoPilot.m_nTimeEnteredCurve; + return (float)timeInCurve / pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve; + } + + static float LimitRadianAngle(float angle) + { + while (angle < -PI) + angle += TWOPI; + while (angle > PI) + angle -= TWOPI; + return angle; + } + + static bool bMadDriversCheat; + static int32 NumLawEnforcerCars; + static int32 NumAmbulancesOnDuty; + static int32 NumFiretrucksOnDuty; + static int32 NumRandomCars; + static int32 NumMissionCars; + static int32 NumParkedCars; + static int32 NumPermanentCars; + static bool bCarsGeneratedAroundCamera; + static float CarDensityMultiplier; + static int8 CountDownToCarsAtStart; + static int32 MaxNumberOfCarsInUse; + static uint32 LastTimeLawEnforcerCreated; + static uint32 LastTimeFireTruckCreated; + static uint32 LastTimeAmbulanceCreated; + static int32 TotalNumOfCarsOfRating[TOTAL_CUSTOM_CLASSES]; + static int32 CarArrays[TOTAL_CUSTOM_CLASSES][MAX_CAR_MODELS_IN_ARRAY]; + + static int32 MiamiViceCycle; + static uint32 LastTimeMiamiViceGenerated; + static int32 NumRequestsOfCarRating[TOTAL_CUSTOM_CLASSES]; + static int32 NumOfLoadedCarsOfRating[TOTAL_CUSTOM_CLASSES]; + static int32 CarFreqArrays[TOTAL_CUSTOM_CLASSES][MAX_CAR_MODELS_IN_ARRAY]; + static int32 LoadedCarsArray[TOTAL_CUSTOM_CLASSES][MAX_CAR_MODELS_IN_ARRAY]; +}; + +extern CVehicle* apCarsToKeep[MAX_CARS_TO_KEEP]; \ No newline at end of file diff --git a/src/miami/control/Curves.cpp b/src/miami/control/Curves.cpp new file mode 100644 index 00000000..5b590440 --- /dev/null +++ b/src/miami/control/Curves.cpp @@ -0,0 +1,55 @@ +#include "common.h" + +#include "Curves.h" + +float CCurves::CalcSpeedScaleFactor(CVector* pPoint1, CVector* pPoint2, float dir1X, float dir1Y, float dir2X, float dir2Y) +{ + CVector2D dir1(dir1X, dir1Y); + CVector2D dir2(dir2X, dir2Y); + float distance = (*pPoint1 - *pPoint2).Magnitude2D(); + float dp = DotProduct2D(dir1, dir2); + if (dp > 0.9f) + return distance + Abs((pPoint1->x * dir1Y - pPoint1->y * dir1X) - (pPoint2->x * dir1Y - pPoint2->y * dir1X)); + else + return ((1.0f - dp) * 0.25f + 1.0f) * distance; +} + +void CCurves::CalcCurvePoint(CVector* pPos1, CVector* pPos2, CVector* pDir1, CVector* pDir2, float between, int32 timeOnCurve, CVector* pOutPos, CVector* pOutDir) +{ + float actualFactor = CalcSpeedScaleFactor(pPos1, pPos2, pDir1->x, pDir1->y, pDir2->x, pDir2->y); + CVector2D dir1 = *pDir1 * actualFactor; + CVector2D dir2 = *pDir2 * actualFactor; + float t1 = Abs(DotProduct2D(*pPos2 - *pPos1, *pDir1)); + float t2 = Abs(DotProduct2D(*pPos1 - *pPos2, *pDir2)); + float curveCoef; + if (t1 > t2) { + float coef = (t1 - t2) / (t1 + t2); +#ifdef FIX_BUGS + if (between <= coef) +#else + if (between < coef) +#endif + curveCoef = 0.0f; + else + curveCoef = 0.5f - 0.5f * Cos(3.1415f * (between - coef) * (t1 + t2) / (2 * t2)); + } + else { + float coef = 2 * t1 / (t1 + t2); +#ifdef FIX_BUGS + if (coef <= between) +#else + if (coef < between) +#endif + curveCoef = 1.0f; + else + curveCoef = 0.5f - 0.5f * Cos(3.1415f * between * (t1 + t2) / (2 * t1)); + } + *pOutPos = CVector( + (pPos1->x + between * dir1.x) * (1.0f - curveCoef) + (pPos2->x - (1 - between) * dir2.x) * curveCoef, + (pPos1->y + between * dir1.y) * (1.0f - curveCoef) + (pPos2->y - (1 - between) * dir2.y) * curveCoef, + 0.0f); + *pOutDir = CVector( + (dir1.x * (1.0f - curveCoef) + dir2.x * curveCoef) / (timeOnCurve * 0.001f), + (dir1.y * (1.0f - curveCoef) + dir2.y * curveCoef) / (timeOnCurve * 0.001f), + 0.0f); +} \ No newline at end of file diff --git a/src/miami/control/Curves.h b/src/miami/control/Curves.h new file mode 100644 index 00000000..5d4e05a7 --- /dev/null +++ b/src/miami/control/Curves.h @@ -0,0 +1,9 @@ +#pragma once +class CVector; + +class CCurves +{ +public: + static float CalcSpeedScaleFactor(CVector*, CVector*, float, float, float, float); + static void CalcCurvePoint(CVector*, CVector*, CVector*, CVector*, float, int32, CVector*, CVector*); +}; diff --git a/src/miami/control/Darkel.cpp b/src/miami/control/Darkel.cpp new file mode 100644 index 00000000..a6aca57e --- /dev/null +++ b/src/miami/control/Darkel.cpp @@ -0,0 +1,439 @@ +#include "common.h" + +#include "main.h" +#include "Darkel.h" +#include "PlayerPed.h" +#include "Wanted.h" +#include "Timer.h" +#include "DMAudio.h" +#include "Population.h" +#include "Replay.h" +#include "Weapon.h" +#include "World.h" +#include "Stats.h" +#include "Font.h" +#include "Text.h" +#include "Vehicle.h" +#include "GameLogic.h" + +#define FRENZY_ANY_PED -1 +#define FRENZY_ANY_CAR -2 + +int32 CDarkel::TimeLimit; +int32 CDarkel::PreviousTime; +int32 CDarkel::TimeOfFrenzyStart; +int32 CDarkel::WeaponType; +int32 CDarkel::AmmoInterruptedWeapon; +int32 CDarkel::KillsNeeded; +int32 CDarkel::InterruptedWeaponType; +int32 CDarkel::InterruptedWeaponSelected; + +/* + * TODO: Collect timer/kill counter RGBA colors on top like in Hud/Frontend. + * bStandardSoundAndMessages is a completely beta thing, + * makes game handle sounds & messages instead of SCM (just like in GTA2) + * but it's never been used in the game. Has unused sliding text when frenzy completed etc. + */ +bool CDarkel::bStandardSoundAndMessages; +bool CDarkel::bNeedHeadShot; +bool CDarkel::bProperKillFrenzy; +uint16 CDarkel::Status; +uint16 CDarkel::RegisteredKills[NUM_DEFAULT_MODELS]; +int32 CDarkel::ModelToKill; +int32 CDarkel::ModelToKill2; +int32 CDarkel::ModelToKill3; +int32 CDarkel::ModelToKill4; +wchar *CDarkel::pStartMessage; + +uint8 +CDarkel::CalcFade(uint32 time, uint32 start, uint32 end) +{ + if (time >= start && time <= end) { + if (time >= start + 500) { + if (time <= end - 500) + return 255; + else + return 255 * (end - time) / 500; + } else + return 255 * (time - start) / 500; + } else + return 0; +} + +void +CDarkel::DrawMessages() +{ + if (CReplay::IsPlayingBack()) + return; + + switch (Status) { + case KILLFRENZY_ONGOING: + { + CFont::SetJustifyOff(); + CFont::SetBackgroundOff(); +#ifdef FIX_BUGS + CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH - 30)); +#else + CFont::SetCentreSize(SCREEN_WIDTH - 30); +#endif + CFont::SetCentreOn(); + CFont::SetPropOn(); + uint32 timePassedSinceStart = CTimer::GetTimeInMilliseconds() - TimeOfFrenzyStart; + if (bStandardSoundAndMessages) { + if (timePassedSinceStart >= 3000 && timePassedSinceStart < 11000) { +#ifdef FIX_BUGS + CFont::SetScale(SCREEN_SCALE_X(1.3f), SCREEN_SCALE_Y(1.3f)); +#else + CFont::SetScale(1.3f, 1.3f); +#endif + CFont::SetJustifyOff(); + CFont::SetColor(CRGBA(255, 255, 128, CalcFade(timePassedSinceStart, 3000, 11000))); + CFont::SetFontStyle(FONT_STANDARD); + if (pStartMessage) { + CFont::PrintString(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, pStartMessage); + } + } + } else { + if (timePassedSinceStart < 8000) { +#ifdef FIX_BUGS + CFont::SetScale(SCREEN_SCALE_X(1.3f), SCREEN_SCALE_Y(1.3f)); +#else + CFont::SetScale(1.3f, 1.3f); +#endif + CFont::SetJustifyOff(); + CFont::SetColor(CRGBA(255, 255, 128, CalcFade(timePassedSinceStart, 0, 8000))); + CFont::SetFontStyle(FONT_STANDARD); + if (pStartMessage) { + CFont::PrintString(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, pStartMessage); + } + } + } +#ifdef FIX_BUGS + CFont::SetScale(SCREEN_SCALE_X(0.75f), SCREEN_SCALE_Y(1.5f)); +#else + CFont::SetScale(0.75f, 1.5f); +#endif + CFont::SetCentreOff(); + CFont::SetRightJustifyOn(); + CFont::SetFontStyle(FONT_HEADING); + if (TimeLimit >= 0) { + uint32 timeLeft = TimeLimit - (CTimer::GetTimeInMilliseconds() - TimeOfFrenzyStart); + sprintf(gString, "%d:%02d", timeLeft / 60000, timeLeft % 60000 / 1000); + AsciiToUnicode(gString, gUString); + if (timeLeft > 4000 || CTimer::GetFrameCounter() & 1) { + CFont::SetColor(CRGBA(0, 0, 0, 255)); + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(35.0f), SCREEN_SCALE_Y(109.0f), gUString); + CFont::SetColor(CRGBA(0, 207, 133, 255)); + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(34.0f), SCREEN_SCALE_Y(108.0f), gUString); + } + } + sprintf(gString, "%d", (KillsNeeded >= 0 ? KillsNeeded : 0)); + AsciiToUnicode(gString, gUString); + CFont::SetColor(CRGBA(0, 0, 0, 255)); + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(35.0f), SCREEN_SCALE_Y(144.0f), gUString); + CFont::SetColor(CRGBA(156, 91, 40, 255)); + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(34.0f), SCREEN_SCALE_Y(143.0f), gUString); + break; + } + case KILLFRENZY_PASSED: + { + if (bStandardSoundAndMessages) { + uint32 timePassedSinceStart = CTimer::GetTimeInMilliseconds() - TimeOfFrenzyStart; + if (CTimer::GetTimeInMilliseconds() - TimeOfFrenzyStart < 5000) { + CFont::SetBackgroundOff(); +#ifdef FIX_BUGS + CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH - 20)); +#else + CFont::SetCentreSize(SCREEN_WIDTH - 20); +#endif + CFont::SetCentreOn(); +#ifdef FIX_BUGS + CFont::SetScale(SCREEN_SCALE_X(1.5f), SCREEN_SCALE_Y(1.5f)); +#else + CFont::SetScale(1.5f, 1.5f); +#endif + CFont::SetJustifyOff(); + CFont::SetColor(CRGBA(128, 255, 128, CalcFade(timePassedSinceStart, 0, 5000))); + CFont::SetFontStyle(FONT_STANDARD); +#ifdef FIX_BUGS + int y = SCREEN_HEIGHT / 2 + SCREEN_SCALE_Y(25.0f - timePassedSinceStart * 0.01f); +#else + int y = (SCREEN_HEIGHT / 2 + 25) - (timePassedSinceStart * 0.01f); +#endif + CFont::PrintString(SCREEN_WIDTH / 2, y, TheText.Get("KF_3")); + } + } + break; + } + default: + break; + } +} + +void +CDarkel::Init() +{ + Status = KILLFRENZY_NONE; +} + +uint16 +CDarkel::QueryModelsKilledByPlayer(int32 modelId) +{ + return RegisteredKills[modelId]; +} + + +bool +CDarkel::FrenzyOnGoing() +{ + return Status == KILLFRENZY_ONGOING; +} + + +uint16 +CDarkel::ReadStatus() +{ + return Status; +} + +void +CDarkel::RegisterCarBlownUpByPlayer(CVehicle *vehicle) +{ +#ifdef FIX_BUGS + if (CReplay::IsPlayingBack()) + return; +#endif + if (FrenzyOnGoing()) { + int32 model = vehicle->GetModelIndex(); + if (ModelToKill == FRENZY_ANY_CAR || ModelToKill == model || ModelToKill2 == model || ModelToKill3 == model || ModelToKill4 == model) { + KillsNeeded--; + DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_CAR_BLOWN, 0); + } + } + RegisteredKills[vehicle->GetModelIndex()]++; + switch (vehicle->GetVehicleAppearance()) { + case VEHICLE_APPEARANCE_CAR: + case VEHICLE_APPEARANCE_BIKE: + CStats::CarsExploded++;; + break; + case VEHICLE_APPEARANCE_HELI: + case VEHICLE_APPEARANCE_PLANE: + CStats::HelisDestroyed++; + break; + case VEHICLE_APPEARANCE_BOAT: + CStats::BoatsExploded++; + break; + } + +} + +void +CDarkel::RegisterKillByPlayer(CPed *victim, eWeaponType weapon, bool headshot) +{ +#ifdef FIX_BUGS + if (CReplay::IsPlayingBack()) + return; +#endif + if (FrenzyOnGoing() && (weapon == WeaponType + || weapon == WEAPONTYPE_EXPLOSION + || weapon == WEAPONTYPE_UZI_DRIVEBY && WeaponType == WEAPONTYPE_UZI + || weapon == WEAPONTYPE_RAMMEDBYCAR && WeaponType == WEAPONTYPE_RUNOVERBYCAR + || weapon == WEAPONTYPE_RUNOVERBYCAR && WeaponType == WEAPONTYPE_RAMMEDBYCAR + || weapon == WEAPONTYPE_FLAMETHROWER && WeaponType == WEAPONTYPE_MOLOTOV)) { + int32 model = victim->GetModelIndex(); + if (ModelToKill == FRENZY_ANY_PED || ModelToKill == model || ModelToKill2 == model || ModelToKill3 == model || ModelToKill4 == model) { + if (!bNeedHeadShot || headshot) { + KillsNeeded--; + DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_KILL, 0); + } + } + } + CStats::PeopleKilledByPlayer++; + RegisteredKills[victim->GetModelIndex()]++; + CStats::PedsKilledOfThisType[victim->bChrisCriminal ? PEDTYPE_CRIMINAL : victim->m_nPedType]++; + if (headshot) + CStats::HeadsPopped++; + CStats::KillsSinceLastCheckpoint++; +} + +void +CDarkel::RegisterKillNotByPlayer(CPed* victim, eWeaponType weapontype) +{ +#ifdef FIX_BUGS + if (CReplay::IsPlayingBack()) + return; +#endif + CStats::PeopleKilledByOthers++; +} + +void +CDarkel::ResetModelsKilledByPlayer() +{ + for (int i = 0; i < NUM_DEFAULT_MODELS; i++) + RegisteredKills[i] = 0; +} + +void +CDarkel::ResetOnPlayerDeath() +{ + if (Status != KILLFRENZY_ONGOING) + return; + + CPopulation::m_AllRandomPedsThisType = -1; + Status = KILLFRENZY_FAILED; + TimeOfFrenzyStart = CTimer::GetTimeInMilliseconds(); + + DealWithWeaponChangeAtEndOfFrenzy(); +} + +void +CDarkel::StartFrenzy(eWeaponType weaponType, int32 time, uint16 kill, int32 modelId0, wchar *text, int32 modelId2, int32 modelId3, int32 modelId4, bool standardSound, bool needHeadShot) +{ + CGameLogic::ClearShortCut(); + CGameLogic::RemoveShortCutDropOffPointForMission(); + eWeaponType fixedWeapon; + if (weaponType == WEAPONTYPE_UZI_DRIVEBY) + fixedWeapon = WEAPONTYPE_UZI; + else + fixedWeapon = weaponType; + + WeaponType = weaponType; + Status = KILLFRENZY_ONGOING; + KillsNeeded = kill; + ModelToKill = modelId0; + ModelToKill2 = modelId2; + ModelToKill3 = modelId3; + ModelToKill4 = modelId4; + pStartMessage = text; + + if (text == TheText.Get("PAGE_00")) { + CDarkel::bProperKillFrenzy = true; + CDarkel::pStartMessage = nil; + } else + bProperKillFrenzy = false; + + bStandardSoundAndMessages = standardSound; + bNeedHeadShot = needHeadShot; + TimeOfFrenzyStart = CTimer::GetTimeInMilliseconds(); + TimeLimit = time; + PreviousTime = time / 1000; + + CPlayerPed *player = FindPlayerPed(); + if (fixedWeapon < WEAPONTYPE_TOTALWEAPONS) { + InterruptedWeaponSelected = player->GetWeapon()->m_eWeaponType; +#if (defined FIX_BUGS || !defined GTA_PS2) + player->RemoveWeaponAnims(InterruptedWeaponSelected, -1000.0f); +#endif + InterruptedWeaponType = player->GetWeapon(player->GetWeaponSlot(fixedWeapon)).m_eWeaponType; + AmmoInterruptedWeapon = player->GetWeapon(player->GetWeaponSlot(fixedWeapon)).m_nAmmoTotal; + if (InterruptedWeaponType) + CModelInfo::GetModelInfo(CWeaponInfo::GetWeaponInfo((eWeaponType)InterruptedWeaponType)->m_nModelId)->AddRef(); +#if (!defined FIX_BUGS && defined GTA_PS2) + player->RemoveWeaponAnims(InterruptedWeaponSelected, -1000.0f); +#endif + player->GiveWeapon(fixedWeapon, 30000); + player->SetCurrentWeapon(fixedWeapon); + player->MakeChangesForNewWeapon(player->m_nSelectedWepSlot); + + if (FindPlayerVehicle()) { + player->SetCurrentWeapon(FindPlayerPed()->m_nSelectedWepSlot); + player->SetAmmo(fixedWeapon, Min(player->GetWeapon()->m_nAmmoTotal, CWeaponInfo::GetWeaponInfo(player->GetWeapon()->m_eWeaponType)->m_nAmountofAmmunition)); + player->ClearWeaponTarget(); + } + } + if (CDarkel::bStandardSoundAndMessages) + DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_START, 0); +} + +void +CDarkel::Update() +{ +#ifdef FIX_BUGS + if (CReplay::IsPlayingBack()) + return; +#endif + + if (Status != KILLFRENZY_ONGOING) + return; + + int32 FrameTime = TimeLimit - (CTimer::GetTimeInMilliseconds() - TimeOfFrenzyStart); + if (FrameTime > 0 || TimeLimit < 0) { + + DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_ONGOING, FrameTime); + + int32 PrevTime = FrameTime / 1000; + + if (PrevTime != PreviousTime) { + if (PreviousTime < 12) + DMAudio.PlayFrontEndSound(SOUND_CLOCK_TICK, PrevTime); + PreviousTime = PrevTime; + } + + } else { + CPopulation::m_AllRandomPedsThisType = -1; + Status = KILLFRENZY_FAILED; + TimeOfFrenzyStart = CTimer::GetTimeInMilliseconds(); + DealWithWeaponChangeAtEndOfFrenzy(); + + if (bStandardSoundAndMessages) + DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_FAILED, 0); + } + + if (KillsNeeded <= 0) { + CPopulation::m_AllRandomPedsThisType = -1; + Status = KILLFRENZY_PASSED; + + if (bProperKillFrenzy) + CStats::AnotherKillFrenzyPassed(); + + TimeOfFrenzyStart = CTimer::GetTimeInMilliseconds(); + + FindPlayerPed()->m_pWanted->SetWantedLevel(0); + + DealWithWeaponChangeAtEndOfFrenzy(); + + if (bStandardSoundAndMessages) + DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_PASSED, 0); + } +} + +void +CDarkel::DealWithWeaponChangeAtEndOfFrenzy() +{ + eWeaponType fixedWeapon; + if (WeaponType == WEAPONTYPE_UZI_DRIVEBY) + fixedWeapon = WEAPONTYPE_UZI; + else + fixedWeapon = (eWeaponType)WeaponType; + + if (fixedWeapon < WEAPONTYPE_TOTALWEAPONS && InterruptedWeaponType) + CModelInfo::GetModelInfo(CWeaponInfo::GetWeaponInfo((eWeaponType)InterruptedWeaponType)->m_nModelId)->RemoveRef(); + + if (fixedWeapon < WEAPONTYPE_TOTALWEAPONS) { + int slot = CWeaponInfo::GetWeaponInfo(fixedWeapon)->m_nWeaponSlot; + FindPlayerPed()->RemoveWeaponModel(FindPlayerPed()->GetWeapon(slot).GetInfo()->m_nModelId); + FindPlayerPed()->GetWeapon(slot).m_eWeaponType = WEAPONTYPE_UNARMED; + FindPlayerPed()->GetWeapon(slot).m_nAmmoTotal = 0; + FindPlayerPed()->GetWeapon(slot).m_nAmmoInClip = 0; + FindPlayerPed()->GetWeapon(slot).m_eWeaponState = WEAPONSTATE_READY; + FindPlayerPed()->RemoveWeaponAnims(fixedWeapon, -1000.0f); + CModelInfo::GetModelInfo(CWeaponInfo::GetWeaponInfo(fixedWeapon)->m_nModelId)->RemoveRef(); + } + + CPlayerPed* player = FindPlayerPed(); + if (fixedWeapon < WEAPONTYPE_TOTALWEAPONS) { + player->m_nSelectedWepSlot = CWeaponInfo::GetWeaponInfo((eWeaponType)InterruptedWeaponSelected)->m_nWeaponSlot; + player->GiveWeapon((eWeaponType)InterruptedWeaponType, AmmoInterruptedWeapon, true); + } + + if (FindPlayerVehicle()) { + player->RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(player->GetWeapon()->m_eWeaponType)->m_nModelId); + if (FindPlayerPed()->GetWeapon(WEAPONSLOT_SUBMACHINEGUN).m_eWeaponType) + FindPlayerPed()->m_nSelectedWepSlot = WEAPONSLOT_SUBMACHINEGUN; + else + FindPlayerPed()->m_nSelectedWepSlot = WEAPONSLOT_UNARMED; + player->SetCurrentWeapon(FindPlayerPed()->m_nSelectedWepSlot); + player->MakeChangesForNewWeapon(player->m_currentWeapon); + player->RemoveDrivebyAnims(); + } +} diff --git a/src/miami/control/Darkel.h b/src/miami/control/Darkel.h new file mode 100644 index 00000000..91955479 --- /dev/null +++ b/src/miami/control/Darkel.h @@ -0,0 +1,55 @@ +#pragma once + +#include "ModelIndices.h" +#include "WeaponType.h" + +class CVehicle; +class CPed; + +enum +{ + KILLFRENZY_NONE, + KILLFRENZY_ONGOING, + KILLFRENZY_PASSED, + KILLFRENZY_FAILED, +}; + +class CDarkel +{ +private: + static int32 TimeLimit; + static int32 PreviousTime; + static int32 TimeOfFrenzyStart; + static int32 WeaponType; + static int32 AmmoInterruptedWeapon; + static int32 KillsNeeded; + static int32 InterruptedWeaponType; + static int32 InterruptedWeaponSelected; + static bool bStandardSoundAndMessages; + static bool bNeedHeadShot; + static bool bProperKillFrenzy; + static uint16 Status; + static uint16 RegisteredKills[NUM_DEFAULT_MODELS]; + static int32 ModelToKill; + static int32 ModelToKill2; + static int32 ModelToKill3; + static int32 ModelToKill4; + static wchar *pStartMessage; + +public: + static uint8 CalcFade(uint32 time, uint32 min, uint32 max); + static void DrawMessages(void); + static bool FrenzyOnGoing(); + static void Init(); + static uint16 QueryModelsKilledByPlayer(int32 modelId); + static uint16 ReadStatus(); + static void RegisterCarBlownUpByPlayer(CVehicle *vehicle); + static void RegisterKillByPlayer(CPed *victim, eWeaponType weapontype, bool headshot = false); + static void RegisterKillNotByPlayer(CPed* victim, eWeaponType weapontype); + static void ResetModelsKilledByPlayer(); + static void ResetOnPlayerDeath(); + static void StartFrenzy(eWeaponType weaponType, int32 time, uint16 kill, int32 modelId0, wchar *text, int32 modelId2, int32 modelId3, int32 modelId4, bool standardSound, bool needHeadShot); + static void Update(); + static void DealWithWeaponChangeAtEndOfFrenzy(); + +}; diff --git a/src/miami/control/GameLogic.cpp b/src/miami/control/GameLogic.cpp new file mode 100644 index 00000000..63c685d1 --- /dev/null +++ b/src/miami/control/GameLogic.cpp @@ -0,0 +1,632 @@ +#include "common.h" + +#include "GameLogic.h" +#include "Clock.h" +#include "Stats.h" +#include "Pickups.h" +#include "Timer.h" +#include "Streaming.h" +#include "CutsceneMgr.h" +#include "World.h" +#include "PlayerPed.h" +#include "Wanted.h" +#include "Camera.h" +#include "Messages.h" +#include "CarCtrl.h" +#include "Restart.h" +#include "Pad.h" +#include "References.h" +#include "Fire.h" +#include "Script.h" +#include "Garages.h" +#include "Population.h" +#include "General.h" +#include "DMAudio.h" +#include "Radar.h" +#include "Pools.h" +#include "Hud.h" +#include "Particle.h" +#include "ColStore.h" +#include "Automobile.h" +#include "MBlur.h" +#include "screendroplets.h" +#include "SaveBuf.h" + +uint8 CGameLogic::ActivePlayers; +uint8 CGameLogic::ShortCutState; +CAutomobile* CGameLogic::pShortCutTaxi; +uint32 CGameLogic::NumAfterDeathStartPoints; +CVector CGameLogic::ShortCutStart; +float CGameLogic::ShortCutStartOrientation; +CVector CGameLogic::ShortCutDestination; +float CGameLogic::ShortCutDestinationOrientation; +uint32 CGameLogic::ShortCutTimer; +CVector CGameLogic::AfterDeathStartPoints[NUM_SHORTCUT_START_POINTS]; +float CGameLogic::AfterDeathStartPointOrientation[NUM_SHORTCUT_START_POINTS]; +CVector CGameLogic::ShortCutDropOffForMission; +float CGameLogic::ShortCutDropOffOrientationForMission; +bool CGameLogic::MissionDropOffReadyToBeUsed; + +#define SHORTCUT_TAXI_COST (9) +#define TOTAL_BUSTED_AUDIO (28) + +void +CGameLogic::InitAtStartOfGame() +{ + ActivePlayers = 1; + ShortCutState = SHORTCUT_NONE; + pShortCutTaxi = nil; + NumAfterDeathStartPoints = 0; +} + +void +CGameLogic::PassTime(uint32 time) +{ + int32 minutes, hours, days; + + minutes = time + CClock::GetMinutes(); + hours = CClock::GetHours(); + + for (; minutes >= 60; minutes -= 60) + hours++; + + if (hours > 23) { + days = CStats::DaysPassed; + for (; hours >= 24; hours -= 24) + days++; + CStats::DaysPassed = days; + } + + CClock::SetGameClock(hours, minutes); + CPickups::PassTime(time * 1000); +} + +void +CGameLogic::SortOutStreamingAndMemory(const CVector &pos) +{ + CTimer::Stop(); + CStreaming::FlushRequestList(); + CStreaming::DeleteRwObjectsAfterDeath(pos); + CStreaming::RemoveUnusedModelsInLoadedList(); + CGame::DrasticTidyUpMemory(true); + CWorld::Players[CWorld::PlayerInFocus].m_pPed->Undress("player"); + CStreaming::LoadSceneCollision(pos); + CStreaming::LoadScene(pos); + CWorld::Players[CWorld::PlayerInFocus].m_pPed->Dress(); + CTimer::Update(); +} + +void +CGameLogic::Update() +{ + CVector vecRestartPos; + float fRestartFloat; + +#ifdef MISSION_REPLAY + // what a place to check! + if (gbTryingPorn4Again) { + CRunningScript* pScript = CTheScripts::pActiveScripts; + if (pScript && !CGeneral::faststricmp(pScript->m_abScriptName, "porno4")) + gbTryingPorn4Again = false; + } +#endif + + if (CCutsceneMgr::IsCutsceneProcessing()) return; + + UpdateShortCut(); + CPlayerInfo &pPlayerInfo = CWorld::Players[CWorld::PlayerInFocus]; + + switch (pPlayerInfo.m_WBState) { + case WBSTATE_PLAYING: + if (pPlayerInfo.m_pPed->m_nPedState == PED_DEAD) { + pPlayerInfo.m_pPed->ClearAdrenaline(); + pPlayerInfo.KillPlayer(); + } + if (pPlayerInfo.m_pPed->m_nPedState == PED_ARRESTED) { + pPlayerInfo.m_pPed->ClearAdrenaline(); + pPlayerInfo.ArrestPlayer(); + } + break; + case WBSTATE_WASTED: +#ifdef MISSION_REPLAY + if ((CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > AddExtraDeathDelay() + 0x800) && (CTimer::GetPreviousTimeInMilliseconds() - pPlayerInfo.m_nWBTime <= AddExtraDeathDelay() + 0x800)) { +#else + if ((CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > 0x800) && (CTimer::GetPreviousTimeInMilliseconds() - pPlayerInfo.m_nWBTime <= 0x800)) { +#endif + TheCamera.SetFadeColour(200, 200, 200); + TheCamera.Fade(2.0f, FADE_OUT); + } + +#ifdef MISSION_REPLAY + if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime >= AddExtraDeathDelay() + 0x1000) { +#else + if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime >= 0x1000) { +#endif + pPlayerInfo.m_WBState = WBSTATE_PLAYING; + if (pPlayerInfo.m_bGetOutOfHospitalFree) { + pPlayerInfo.m_bGetOutOfHospitalFree = false; + } else { + pPlayerInfo.m_nMoney = Max(0, pPlayerInfo.m_nMoney - 100); + pPlayerInfo.m_pPed->ClearWeapons(); + } + + if (pPlayerInfo.m_pPed->bInVehicle) { + CVehicle *pVehicle = pPlayerInfo.m_pPed->m_pMyVehicle; + if (pVehicle != nil) { + if (pVehicle->pDriver == pPlayerInfo.m_pPed) { + pVehicle->pDriver = nil; + if (pVehicle->GetStatus() != STATUS_WRECKED) + pVehicle->SetStatus(STATUS_ABANDONED); + } else + pVehicle->RemovePassenger(pPlayerInfo.m_pPed); + } + } + CEventList::Initialise(); +#ifdef SCREEN_DROPLETS + ScreenDroplets::Initialise(); +#endif + CMessages::ClearMessages(); + CCarCtrl::ClearInterestingVehicleList(); + CWorld::ClearExcitingStuffFromArea(pPlayerInfo.GetPos(), 4000.0f, true); + CRestart::FindClosestHospitalRestartPoint(pPlayerInfo.GetPos(), &vecRestartPos, &fRestartFloat); + CRestart::OverrideHospitalLevel = LEVEL_GENERIC; + CRestart::OverridePoliceStationLevel = LEVEL_GENERIC; + PassTime(720); + RestorePlayerStuffDuringResurrection(pPlayerInfo.m_pPed, vecRestartPos, fRestartFloat); + AfterDeathArrestSetUpShortCutTaxi(); + SortOutStreamingAndMemory(pPlayerInfo.GetPos()); + TheCamera.m_fCamShakeForce = 0.0f; + TheCamera.SetMotionBlur(0, 0, 0, 0, MOTION_BLUR_NONE); + CPad::GetPad(0)->StopShaking(0); + CReferences::RemoveReferencesToPlayer(); + CPopulation::m_CountDownToPedsAtStart = 10; + CCarCtrl::CountDownToCarsAtStart = 10; + CPad::GetPad(CWorld::PlayerInFocus)->DisablePlayerControls = PLAYERCONTROL_ENABLED; + if (CRestart::bFadeInAfterNextDeath) { + TheCamera.SetFadeColour(200, 200, 200); + TheCamera.Fade(4.0f, FADE_IN); + } else + CRestart::bFadeInAfterNextDeath = true; + } + break; + case WBSTATE_BUSTED: +#ifdef MISSION_REPLAY + if ((CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > AddExtraDeathDelay() + 0x800) && (CTimer::GetPreviousTimeInMilliseconds() - pPlayerInfo.m_nWBTime <= AddExtraDeathDelay() + 0x800)) { +#else + if ((CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > 0x800) && (CTimer::GetPreviousTimeInMilliseconds() - pPlayerInfo.m_nWBTime <= 0x800)) { +#endif + TheCamera.SetFadeColour(0, 0, 0); + TheCamera.Fade(2.0f, FADE_OUT); + } + + + if (!CTheScripts::IsPlayerOnAMission() && pPlayerInfo.m_nBustedAudioStatus == BUSTEDAUDIO_NONE) { + if (CGeneral::GetRandomNumberInRange(0, 4) == 0) + pPlayerInfo.m_nBustedAudioStatus = BUSTEDAUDIO_DONE; + else { + pPlayerInfo.m_nBustedAudioStatus = BUSTEDAUDIO_LOADING; + char name[12]; + sprintf(name, pPlayerInfo.m_nCurrentBustedAudio >= 10 ? "bust_%d" : "bust_0%d", pPlayerInfo.m_nCurrentBustedAudio); + DMAudio.ClearMissionAudio(0); + DMAudio.PreloadMissionAudio(0, name); + pPlayerInfo.m_nCurrentBustedAudio = pPlayerInfo.m_nCurrentBustedAudio % TOTAL_BUSTED_AUDIO + 1; + } + } + if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > 4000 && + pPlayerInfo.m_nBustedAudioStatus == BUSTEDAUDIO_LOADING && + DMAudio.GetMissionAudioLoadingStatus(0) == 1) { + DMAudio.PlayLoadedMissionAudio(0); + pPlayerInfo.m_nBustedAudioStatus = BUSTEDAUDIO_DONE; + } + +#ifdef MISSION_REPLAY + if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime >= AddExtraDeathDelay() + 0x1000) { +#else + if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime >= 0x1000) { +#endif +#ifdef FIX_BUGS + pPlayerInfo.m_nBustedAudioStatus = BUSTEDAUDIO_NONE; +#endif + pPlayerInfo.m_WBState = WBSTATE_PLAYING; + int takeMoney; + + switch (pPlayerInfo.m_pPed->m_pWanted->GetWantedLevel()) { + case 0: + case 1: + takeMoney = 100; + break; + case 2: + takeMoney = 200; + break; + case 3: + takeMoney = 400; + break; + case 4: + takeMoney = 600; + break; + case 5: + takeMoney = 900; + break; + case 6: + takeMoney = 1500; + break; + } + if (pPlayerInfo.m_bGetOutOfJailFree) { + pPlayerInfo.m_bGetOutOfJailFree = false; + } else { + pPlayerInfo.m_nMoney = Max(0, pPlayerInfo.m_nMoney - takeMoney); + pPlayerInfo.m_pPed->ClearWeapons(); + } + + if (pPlayerInfo.m_pPed->bInVehicle) { + CVehicle *pVehicle = pPlayerInfo.m_pPed->m_pMyVehicle; + if (pVehicle != nil) { + if (pVehicle->pDriver == pPlayerInfo.m_pPed) { + pVehicle->pDriver = nil; + if (pVehicle->GetStatus() != STATUS_WRECKED) + pVehicle->SetStatus(STATUS_ABANDONED); + } + else + pVehicle->RemovePassenger(pPlayerInfo.m_pPed); + } + } + CEventList::Initialise(); +#ifdef SCREEN_DROPLETS + ScreenDroplets::Initialise(); +#endif + CMessages::ClearMessages(); + CCarCtrl::ClearInterestingVehicleList(); + CWorld::ClearExcitingStuffFromArea(pPlayerInfo.GetPos(), 4000.0f, true); + CRestart::FindClosestPoliceRestartPoint(pPlayerInfo.GetPos(), &vecRestartPos, &fRestartFloat); + CRestart::OverrideHospitalLevel = LEVEL_GENERIC; + CRestart::OverridePoliceStationLevel = LEVEL_GENERIC; + PassTime(720); + RestorePlayerStuffDuringResurrection(pPlayerInfo.m_pPed, vecRestartPos, fRestartFloat); + AfterDeathArrestSetUpShortCutTaxi(); + pPlayerInfo.m_pPed->ClearWeapons(); + SortOutStreamingAndMemory(pPlayerInfo.GetPos()); + TheCamera.m_fCamShakeForce = 0.0f; + TheCamera.SetMotionBlur(0, 0, 0, 0, MOTION_BLUR_NONE); + CPad::GetPad(0)->StopShaking(0); + CReferences::RemoveReferencesToPlayer(); + CPopulation::m_CountDownToPedsAtStart = 10; + CCarCtrl::CountDownToCarsAtStart = 10; + CPad::GetPad(CWorld::PlayerInFocus)->DisablePlayerControls = PLAYERCONTROL_ENABLED; + if (CRestart::bFadeInAfterNextArrest) { + TheCamera.SetFadeColour(0, 0, 0); + TheCamera.Fade(4.0f, FADE_IN); + } else + CRestart::bFadeInAfterNextArrest = true; + } + break; + case WBSTATE_FAILED_CRITICAL_MISSION: +#ifdef MISSION_REPLAY + if ((CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > AddExtraDeathDelay() + 0x800) && (CTimer::GetPreviousTimeInMilliseconds() - pPlayerInfo.m_nWBTime <= AddExtraDeathDelay() + 0x800)) { +#else + if ((CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > 0x800) && (CTimer::GetPreviousTimeInMilliseconds() - pPlayerInfo.m_nWBTime <= 0x800)) { +#endif + TheCamera.SetFadeColour(0, 0, 0); + TheCamera.Fade(2.0f, FADE_OUT); + } +#ifdef MISSION_REPLAY + if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime >= AddExtraDeathDelay() + 0x1000) { +#else + if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime >= 0x1000) { +#endif + pPlayerInfo.m_WBState = WBSTATE_PLAYING; + if (pPlayerInfo.m_pPed->bInVehicle) { + CVehicle *pVehicle = pPlayerInfo.m_pPed->m_pMyVehicle; + if (pVehicle != nil) { + if (pVehicle->pDriver == pPlayerInfo.m_pPed) { + pVehicle->pDriver = nil; + if (pVehicle->GetStatus() != STATUS_WRECKED) + pVehicle->SetStatus(STATUS_ABANDONED); + } else + pVehicle->RemovePassenger(pPlayerInfo.m_pPed); + } + } + CEventList::Initialise(); +#ifdef SCREEN_DROPLETS + ScreenDroplets::Initialise(); +#endif + CMessages::ClearMessages(); + CCarCtrl::ClearInterestingVehicleList(); + CWorld::ClearExcitingStuffFromArea(pPlayerInfo.GetPos(), 4000.0f, true); + CRestart::FindClosestPoliceRestartPoint(pPlayerInfo.GetPos(), &vecRestartPos, &fRestartFloat); + CRestart::OverridePoliceStationLevel = LEVEL_GENERIC; + CRestart::OverrideHospitalLevel = LEVEL_GENERIC; + RestorePlayerStuffDuringResurrection(pPlayerInfo.m_pPed, vecRestartPos, fRestartFloat); + SortOutStreamingAndMemory(pPlayerInfo.GetPos()); + TheCamera.m_fCamShakeForce = 0.0f; + TheCamera.SetMotionBlur(0, 0, 0, 0, MOTION_BLUR_NONE); + CPad::GetPad(0)->StopShaking(0); + CReferences::RemoveReferencesToPlayer(); + CPopulation::m_CountDownToPedsAtStart = 10; + CCarCtrl::CountDownToCarsAtStart = 10; + CPad::GetPad(CWorld::PlayerInFocus)->DisablePlayerControls = PLAYERCONTROL_ENABLED; + TheCamera.SetFadeColour(0, 0, 0); + TheCamera.Fade(4.0f, FADE_IN); + } + break; + case 4: + return; + } +} + +void +CGameLogic::RestorePlayerStuffDuringResurrection(CPlayerPed *pPlayerPed, CVector pos, float angle) +{ + ClearShortCut(); + CPlayerInfo* pPlayerInfo = pPlayerPed->GetPlayerInfoForThisPlayerPed(); + pPlayerPed->m_fHealth = pPlayerInfo->m_nMaxHealth; + pPlayerPed->m_fArmour = 0.0f; + pPlayerPed->bIsVisible = true; + pPlayerPed->m_bloodyFootprintCountOrDeathTime = 0; + pPlayerPed->bDoBloodyFootprints = false; + pPlayerPed->m_nDrunkenness = 0; + pPlayerPed->m_nFadeDrunkenness = 0; + CMBlur::ClearDrunkBlur(); + pPlayerPed->m_nDrunkCountdown = 0; + pPlayerPed->ClearAdrenaline(); + pPlayerPed->m_fCurrentStamina = pPlayerPed->m_fMaxStamina; + if (pPlayerPed->m_pFire) + pPlayerPed->m_pFire->Extinguish(); + pPlayerPed->bInVehicle = false; + pPlayerPed->m_pMyVehicle = nil; + pPlayerPed->m_pVehicleAnim = nil; + pPlayerPed->m_pWanted->Reset(); + pPlayerPed->bCancelEnteringCar = false; + pPlayerPed->RestartNonPartialAnims(); + pPlayerInfo->MakePlayerSafe(false); + pPlayerPed->bRemoveFromWorld = false; + pPlayerPed->ClearWeaponTarget(); + pPlayerPed->SetInitialState(); + CCarCtrl::ClearInterestingVehicleList(); + pPlayerPed->Teleport(pos + CVector(0.0f, 0.0f, 1.0f)); + pPlayerPed->SetMoveSpeed(0.0f, 0.0f, 0.0f); + pPlayerPed->m_fRotationCur = DEGTORAD(angle); + pPlayerPed->m_fRotationDest = pPlayerPed->m_fRotationCur; + pPlayerPed->SetHeading(pPlayerPed->m_fRotationCur); + CTheScripts::ClearSpaceForMissionEntity(pos, pPlayerPed); + CWorld::ClearExcitingStuffFromArea(pos, 4000.0f, true); + pPlayerPed->RestoreHeadingRate(); + CGame::currArea = AREA_MAIN_MAP; + CStreaming::RemoveBuildingsNotInArea(AREA_MAIN_MAP); + TheCamera.SetCameraDirectlyInFrontForFollowPed_CamOnAString(); + TheCamera.Restore(); + CReferences::RemoveReferencesToPlayer(); + CGarages::PlayerArrestedOrDied(); + CStats::CheckPointReachedUnsuccessfully(); + CWorld::Remove(pPlayerPed); + CWorld::Add(pPlayerPed); + CHud::ResetWastedText(); + CStreaming::StreamZoneModels(pos); + clearWaterDrop = true; +} + +void +CGameLogic::ClearShortCut() +{ + if (pShortCutTaxi) { + if (pShortCutTaxi->VehicleCreatedBy == MISSION_VEHICLE) { + pShortCutTaxi->VehicleCreatedBy = RANDOM_VEHICLE; + --CCarCtrl::NumMissionCars; + ++CCarCtrl::NumRandomCars; + } + CRadar::ClearBlipForEntity(BLIP_CAR, CPools::GetVehiclePool()->GetIndex(pShortCutTaxi)); + pShortCutTaxi = nil; + } + CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_SHORTCUT_TAXI); +} + +void +CGameLogic::SetUpShortCut(CVector vStartPos, float fStartAngle, CVector vEndPos, float fEndAngle) +{ + ClearShortCut(); + ShortCutState = SHORTCUT_INIT; + ShortCutStart = vStartPos; + ShortCutStartOrientation = fStartAngle; + ShortCutDestination = vEndPos; + ShortCutDestinationOrientation = fEndAngle; + CStreaming::RequestModel(MI_KAUFMAN, 0); +} + +void +CGameLogic::AbandonShortCutIfTaxiHasBeenMessedWith() +{ + if (!pShortCutTaxi) + return; + if (pShortCutTaxi->pDriver == nil || + pShortCutTaxi->pDriver->DyingOrDead() || + pShortCutTaxi->pDriver->GetPedState() == PED_DRAG_FROM_CAR || + pShortCutTaxi->pDriver->GetPedState() == PED_ON_FIRE || + pShortCutTaxi->pDriver->m_objective == OBJECTIVE_LEAVE_CAR_AND_DIE || + pShortCutTaxi->m_fHealth < 250.0f || + pShortCutTaxi->bRenderScorched) + ClearShortCut(); +} + +void +CGameLogic::AbandonShortCutIfPlayerMilesAway() +{ + if (!pShortCutTaxi) + return; + if ((FindPlayerCoors() - pShortCutTaxi->GetPosition()).Magnitude() > 120.0f) + ClearShortCut(); +} + +void +CGameLogic::UpdateShortCut() +{ + switch (ShortCutState) { + case SHORTCUT_INIT: + if (!CStreaming::HasModelLoaded(MI_KAUFMAN)) { + CStreaming::RequestModel(MI_KAUFMAN, 0); + return; + } + pShortCutTaxi = new CAutomobile(MI_KAUFMAN, RANDOM_VEHICLE); + if (!pShortCutTaxi) + return; + pShortCutTaxi->SetPosition(ShortCutStart); + pShortCutTaxi->SetHeading(DEGTORAD(ShortCutStartOrientation)); + pShortCutTaxi->PlaceOnRoadProperly(); + pShortCutTaxi->SetStatus(STATUS_PHYSICS); + pShortCutTaxi->AutoPilot.m_nCarMission = MISSION_STOP_FOREVER; + pShortCutTaxi->AutoPilot.m_nCruiseSpeed = 0; + pShortCutTaxi->SetUpDriver(); + pShortCutTaxi->VehicleCreatedBy = MISSION_VEHICLE; + ++CCarCtrl::NumMissionCars; + --CCarCtrl::NumRandomCars; + CTheScripts::ClearSpaceForMissionEntity(ShortCutStart, pShortCutTaxi); + CWorld::Add(pShortCutTaxi); + CRadar::SetEntityBlip(BLIP_CAR, CPools::GetVehiclePool()->GetIndex(pShortCutTaxi), 0, BLIP_DISPLAY_MARKER_ONLY); + ShortCutState = SHORTCUT_IDLE; + break; + case SHORTCUT_IDLE: + if (FindPlayerPed()->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER && FindPlayerPed()->m_carInObjective == pShortCutTaxi) { + CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_SHORTCUT_TAXI); + FindPlayerPed()->SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, pShortCutTaxi); + ShortCutState = SHORTCUT_GETTING_IN; + } + AbandonShortCutIfTaxiHasBeenMessedWith(); + AbandonShortCutIfPlayerMilesAway(); + break; + case SHORTCUT_GETTING_IN: + if (pShortCutTaxi->pPassengers[0] == FindPlayerPed() || + pShortCutTaxi->pPassengers[1] == FindPlayerPed() || + pShortCutTaxi->pPassengers[2] == FindPlayerPed()) { + pShortCutTaxi->AutoPilot.m_nTempAction = TEMPACT_GOFORWARD; + pShortCutTaxi->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 2500; + TheCamera.SetFadeColour(0, 0, 0); + TheCamera.Fade(2.5f, FADE_OUT); + ShortCutState = SHORTCUT_TRANSITION; + ShortCutTimer = CTimer::GetTimeInMilliseconds() + 3000; + CMessages::AddBigMessage(TheText.Get("TAXI"), 4500, 1); + } + AbandonShortCutIfTaxiHasBeenMessedWith(); + break; + case SHORTCUT_TRANSITION: + if (CTimer::GetTimeInMilliseconds() > ShortCutTimer) { + CTimer::Suspend(); + CColStore::RequestCollision(ShortCutDestination); + CStreaming::LoadSceneCollision(ShortCutDestination); + CStreaming::LoadScene(ShortCutDestination); + CTheScripts::ClearSpaceForMissionEntity(ShortCutDestination, pShortCutTaxi); + pShortCutTaxi->Teleport(ShortCutDestination); + pShortCutTaxi->SetHeading(DEGTORAD(ShortCutDestinationOrientation)); + pShortCutTaxi->PlaceOnRoadProperly(); + pShortCutTaxi->SetMoveSpeed(pShortCutTaxi->GetForward() * 0.4f); + ShortCutTimer = CTimer::GetTimeInMilliseconds() + 1500; + TheCamera.SetFadeColour(0, 0, 0); + TheCamera.Fade(1.0f, FADE_IN); + ShortCutState = SHORTCUT_ARRIVING; + CTimer::Resume(); + } + break; + case SHORTCUT_ARRIVING: + if (CTimer::GetTimeInMilliseconds() > ShortCutTimer) { + CWorld::Players[CWorld::PlayerInFocus].m_nMoney = Max(0, CWorld::Players[CWorld::PlayerInFocus].m_nMoney - SHORTCUT_TAXI_COST); + FindPlayerPed()->SetObjective(OBJECTIVE_LEAVE_CAR, pShortCutTaxi); + FindPlayerPed()->m_carInObjective = pShortCutTaxi; + ShortCutState = SHORTCUT_GETTING_OUT; + } + AbandonShortCutIfTaxiHasBeenMessedWith(); + break; + case SHORTCUT_GETTING_OUT: + if (pShortCutTaxi->pPassengers[0] != FindPlayerPed() && + pShortCutTaxi->pPassengers[1] != FindPlayerPed() && + pShortCutTaxi->pPassengers[2] != FindPlayerPed()) { + CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_SHORTCUT_TAXI); + pShortCutTaxi->AutoPilot.m_nCarMission = MISSION_CRUISE; + pShortCutTaxi->AutoPilot.m_nCruiseSpeed = 18; + CCarCtrl::JoinCarWithRoadSystem(pShortCutTaxi); + pShortCutTaxi->VehicleCreatedBy = RANDOM_VEHICLE; + ++CCarCtrl::NumRandomCars; + --CCarCtrl::NumMissionCars; + CRadar::ClearBlipForEntity(BLIP_CAR, CPools::GetVehiclePool()->GetIndex(pShortCutTaxi)); + ShortCutState = SHORTCUT_NONE; + pShortCutTaxi = nil; + } + AbandonShortCutIfTaxiHasBeenMessedWith(); + break; + } +} + +void +CGameLogic::AddShortCutPointAfterDeath(CVector point, float angle) +{ + if (NumAfterDeathStartPoints >= NUM_SHORTCUT_START_POINTS) + return; + AfterDeathStartPoints[NumAfterDeathStartPoints] = point; + AfterDeathStartPointOrientation[NumAfterDeathStartPoints] = angle; + NumAfterDeathStartPoints++; +} + +void +CGameLogic::AddShortCutDropOffPointForMission(CVector point, float angle) +{ + ShortCutDropOffForMission = point; + ShortCutDropOffOrientationForMission = angle; + MissionDropOffReadyToBeUsed = true; +} + +void +CGameLogic::RemoveShortCutDropOffPointForMission() +{ + MissionDropOffReadyToBeUsed = false; +} + +void +CGameLogic::AfterDeathArrestSetUpShortCutTaxi() +{ + if (!MissionDropOffReadyToBeUsed) + return; + int nClosestPoint = -1; + float fDistanceToPoint = 999999.9f; + for (int i = 0; i < NUM_SHORTCUT_START_POINTS; i++) { + float dist = (AfterDeathStartPoints[i] - FindPlayerCoors()).Magnitude(); + if (dist < fDistanceToPoint) { + fDistanceToPoint = dist; + nClosestPoint = i; + } + } + if (fDistanceToPoint < 100.0f) + SetUpShortCut(AfterDeathStartPoints[nClosestPoint], + AfterDeathStartPointOrientation[nClosestPoint], + ShortCutDropOffForMission, + ShortCutDropOffOrientationForMission); + MissionDropOffReadyToBeUsed = false; +} + +void +CGameLogic::Save(uint8* buf, uint32* size) +{ +INITSAVEBUF + WriteSaveBuf(buf, NumAfterDeathStartPoints); + *size += sizeof(NumAfterDeathStartPoints); + for (int i = 0; i < NUM_SHORTCUT_START_POINTS; i++) { + WriteSaveBuf(buf, AfterDeathStartPoints[i].x); + *size += sizeof(AfterDeathStartPoints[i].x); + WriteSaveBuf(buf, AfterDeathStartPoints[i].y); + *size += sizeof(AfterDeathStartPoints[i].y); + WriteSaveBuf(buf, AfterDeathStartPoints[i].z); + *size += sizeof(AfterDeathStartPoints[i].z); + WriteSaveBuf(buf, AfterDeathStartPointOrientation[i]); + *size += sizeof(AfterDeathStartPointOrientation[i]); + } +VALIDATESAVEBUF(*size) +} + +void +CGameLogic::Load(uint8* buf, uint32 size) +{ +INITSAVEBUF + ReadSaveBuf(&NumAfterDeathStartPoints, buf); + for (int i = 0; i < NUM_SHORTCUT_START_POINTS; i++) { + ReadSaveBuf(&AfterDeathStartPoints[i].x, buf); + ReadSaveBuf(&AfterDeathStartPoints[i].y, buf); + ReadSaveBuf(&AfterDeathStartPoints[i].z, buf); + ReadSaveBuf(&AfterDeathStartPointOrientation[i], buf); + } +VALIDATESAVEBUF(size) +} diff --git a/src/miami/control/GameLogic.h b/src/miami/control/GameLogic.h new file mode 100644 index 00000000..9b774cc7 --- /dev/null +++ b/src/miami/control/GameLogic.h @@ -0,0 +1,51 @@ +#pragma once + +class CAutomobile; + +class CGameLogic +{ +public: + enum { + SHORTCUT_NONE = 0, + SHORTCUT_INIT, + SHORTCUT_IDLE, + SHORTCUT_GETTING_IN, + SHORTCUT_TRANSITION, + SHORTCUT_ARRIVING, + SHORTCUT_GETTING_OUT + }; + + static void InitAtStartOfGame(); + static void PassTime(uint32 time); + static void SortOutStreamingAndMemory(const CVector &pos); + static void Update(); + static void RestorePlayerStuffDuringResurrection(class CPlayerPed *pPlayerPed, CVector pos, float angle); + + static void ClearShortCut(); + static void SetUpShortCut(CVector, float, CVector, float); + static void AbandonShortCutIfTaxiHasBeenMessedWith(); + static void AbandonShortCutIfPlayerMilesAway(); + static void UpdateShortCut(); + static void AddShortCutPointAfterDeath(CVector, float); + static void AddShortCutDropOffPointForMission(CVector, float); + static void RemoveShortCutDropOffPointForMission(); + static void AfterDeathArrestSetUpShortCutTaxi(); + + static void Save(uint8*, uint32*); + static void Load(uint8*, uint32); + + static uint8 ActivePlayers; + static uint8 ShortCutState; + static CAutomobile* pShortCutTaxi; + static uint32 NumAfterDeathStartPoints; + static CVector ShortCutStart; + static float ShortCutStartOrientation; + static CVector ShortCutDestination; + static float ShortCutDestinationOrientation; + static uint32 ShortCutTimer; + static CVector AfterDeathStartPoints[NUM_SHORTCUT_START_POINTS]; + static float AfterDeathStartPointOrientation[NUM_SHORTCUT_START_POINTS]; + static CVector ShortCutDropOffForMission; + static float ShortCutDropOffOrientationForMission; + static bool MissionDropOffReadyToBeUsed; +}; \ No newline at end of file diff --git a/src/miami/control/Garages.cpp b/src/miami/control/Garages.cpp new file mode 100644 index 00000000..b24c9122 --- /dev/null +++ b/src/miami/control/Garages.cpp @@ -0,0 +1,2495 @@ +#include "common.h" + +#include "Garages.h" +#include "main.h" + +#include "Bike.h" +#include "Boat.h" +#include "DMAudio.h" +#include "General.h" +#include "Font.h" +#include "HandlingMgr.h" +#include "Hud.h" +#include "Messages.h" +#include "ModelIndices.h" +#include "Pad.h" +#include "Particle.h" +#include "PlayerPed.h" +#include "Replay.h" +#include "Stats.h" +#include "Streaming.h" +#include "Text.h" +#include "Timer.h" +#include "Vehicle.h" +#include "Wanted.h" +#include "World.h" +#include "VarConsole.h" +#include "SaveBuf.h" + +#define ROTATED_DOOR_OPEN_SPEED (0.015f) +#define ROTATED_DOOR_CLOSE_SPEED (0.02f) +#define DEFAULT_DOOR_OPEN_SPEED (0.035f) +#define DEFAULT_DOOR_CLOSE_SPEED (0.04f) +#define CRUSHER_CRANE_SPEED (0.005f) + +// Prices +#define BOMB_PRICE (500) +#define RESPRAY_PRICE (100) + +// Distances +#define DISTANCE_TO_CALL_OFF_CHASE (10.0f) +#define DISTANCE_FOR_MRWHOOP_HACK (0.5f) +#define DISTANCE_TO_ACTIVATE_GARAGE (8.0f) +#define DISTANCE_TO_ACTIVATE_KEEPCAR_GARAGE (17.0f) +#define DISTANCE_TO_CLOSE_MISSION_GARAGE (30.0f) +#define DISTANCE_TO_CLOSE_COLLECTSPECIFICCARS_GARAGE (25.0f) +#define DISTANCE_TO_CLOSE_COLLECTCARS_GARAGE (40.0f) +#define DISTANCE_TO_CLOSE_HIDEOUT_GARAGE_ON_FOOT (3.2f) +#define DISTANCE_TO_CLOSE_HIDEOUT_GARAGE_IN_CAR (15.0f) +#define DISTANCE_TO_FORCE_CLOSE_HIDEOUT_GARAGE (70.0f) +#define DISTANCE_TO_OPEN_HIDEOUT_GARAGE_ON_FOOT (2.8f) +#define DISTANCE_TO_OPEN_HIDEOUT_GARAGE_IN_CAR (10.0f) +#define DISTANCE_TO_SHOW_HIDEOUT_MESSAGE (5.0f) + +#define DISTANCE_TO_CONSIDER_DOOR_FOR_GARAGE (20.0f) + +// Time +#define TIME_TO_RESPRAY (2000) +#define TIME_TO_SETUP_BOMB (2000) +#define TIME_TO_CRUSH_CAR (3000) +#define TIME_TO_PROCESS_KEEPCAR_GARAGE (2000) + +// Respray stuff +#define FREE_RESPRAY_HEALTH_THRESHOLD (970.0f) +#define NUM_PARTICLES_IN_RESPRAY (200) +#define RESPRAY_CENTERING_COEFFICIENT (0.4f) + +// Bomb stuff +#define KGS_OF_EXPLOSIVES_IN_BOMB (10) + +// Collect specific cars stuff +#define REWARD_FOR_FIRST_POLICE_CAR (5000) +#define REWARD_FOR_FIRST_BANK_VAN (5000) +#define MAX_POLICE_CARS_TO_COLLECT (10) +#define MAX_BANK_VANS_TO_COLLECT (10) + +// Collect cars stuff +#define MAX_SPEED_TO_SHOW_COLLECTED_MESSAGE (0.03f) +#define IMPORT_REWARD (500) +#define IMPORT_ALLCARS_REWARD (20500) + +// Crusher stuff +#define CRUSHER_VEHICLE_TEST_SPAN (8) +#define CRUSHER_MIN_REWARD (25) +#define CRUSHER_MAX_REWARD (125) +#define CRUSHER_REWARD_COEFFICIENT (1.0f/500000) + +// Hideout stuff +#define HIDEOUT_DOOR_SPEED_COEFFICIENT (1.7f) +#define TIME_BETWEEN_HIDEOUT_MESSAGES (18000) + +// Camera stuff +#define MARGIN_FOR_CAMERA_COLLECTCARS (0.5f) +#define MARGIN_FOR_CAMERA_DEFAULT (0.5f) + +const int32 gaCarsToCollectInCraigsGarages[TOTAL_COLLECTCARS_GARAGES][TOTAL_COLLECTCARS_CARS] = +{ + { MI_LANDSTAL, MI_IDAHO, MI_ESPERANT, MI_STALLION, MI_RANCHER, MI_BLISTAC }, + { MI_SABRE, MI_VIRGO, MI_SENTINEL, MI_STRETCH, MI_WASHING, MI_ADMIRAL }, + { MI_CHEETAH, MI_INFERNUS, MI_BANSHEE, MI_PHEONIX, MI_COMET, MI_STINGER }, + { MI_VOODOO, MI_CUBAN, MI_CADDY, MI_BAGGAGE, MI_MRWHOOP, MI_PIZZABOY } +}; + +const int32 gaCarsToCollectIn60Seconds[] = { MI_CHEETAH, MI_TAXI, MI_ESPERANT, MI_SENTINEL, MI_IDAHO }; + +int32 CGarages::BankVansCollected; +bool CGarages::BombsAreFree; +bool CGarages::RespraysAreFree; +int32 CGarages::CarsCollected; +int32 CGarages::CarTypesCollected[TOTAL_COLLECTCARS_GARAGES]; +int32 CGarages::CrushedCarId; +uint32 CGarages::LastTimeHelpMessage; +int32 CGarages::MessageNumberInString; +char CGarages::MessageIDString[MESSAGE_LENGTH]; +int32 CGarages::MessageNumberInString2; +uint32 CGarages::MessageStartTime; +uint32 CGarages::MessageEndTime; +uint32 CGarages::NumGarages; +bool CGarages::PlayerInGarage; +int32 CGarages::PoliceCarsCollected; +CStoredCar CGarages::aCarsInSafeHouses[TOTAL_HIDEOUT_GARAGES][NUM_GARAGE_STORED_CARS]; +int32 hGarages = AEHANDLE_NONE; +CGarage CGarages::aGarages[NUM_GARAGES]; +bool CGarages::bCamShouldBeOutisde; + +#ifndef MASTER +bool bPrintNearestObject; +#endif + +void CGarages::Init(void) +{ +#ifndef MASTER + VarConsole.Add("Print nearest object", &bPrintNearestObject, true); +#endif + CrushedCarId = -1; + NumGarages = 0; + MessageEndTime = 0; + MessageStartTime = 0; + PlayerInGarage = false; + BombsAreFree = false; +#ifdef FIX_BUGS + RespraysAreFree = false; +#endif + CarsCollected = 0; + BankVansCollected = 0; + PoliceCarsCollected = 0; + for (int i = 0; i < TOTAL_COLLECTCARS_GARAGES; i++) + CarTypesCollected[i] = 0; + LastTimeHelpMessage = 0; + for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++) { + for (int j = 0; j < TOTAL_HIDEOUT_GARAGES; j++) + aCarsInSafeHouses[j][i].Init(); + } + hGarages = DMAudio.CreateEntity(AUDIOTYPE_GARAGE, (void*)1); + if (hGarages >= 0) + DMAudio.SetEntityStatus(hGarages, TRUE); +} + +void CGarages::Shutdown(void) +{ + NumGarages = 0; + if (hGarages < 0) + return; + DMAudio.DestroyEntity(hGarages); + hGarages = AEHANDLE_NONE; +} + +void CGarages::Update(void) +{ + static uint32 GarageToBeTidied = 0; + if (CReplay::IsPlayingBack()) + return; +#ifdef SECUROM + extern uint8 gameProcessPirateCheck; + if (gameProcessPirateCheck == 2) return; +#endif + bCamShouldBeOutisde = false; + TheCamera.pToGarageWeAreIn = nil; + TheCamera.pToGarageWeAreInForHackAvoidFirstPerson = nil; +#ifdef FIX_BUGS + for (uint32 i = 0; i < NumGarages; i++) { +#else + for (int i = 0; i < NUM_GARAGES; i++) { +#endif + if (aGarages[i].IsUsed()) + aGarages[i].Update(); + } + if ((CTimer::GetFrameCounter() & 0xF) != 0xC) + return; +#ifdef FIX_BUGS + if (++GarageToBeTidied >= NumGarages) +#else + if (++GarageToBeTidied >= NUM_GARAGES) +#endif + GarageToBeTidied = 0; + if (!aGarages[GarageToBeTidied].IsUsed()) + return; + if (!aGarages[GarageToBeTidied].IsFar()) + aGarages[GarageToBeTidied].TidyUpGarageClose(); + else + aGarages[GarageToBeTidied].TidyUpGarage(); +} + +int16 CGarages::AddOne(float X1, float Y1, float Z1, float X2, float Y2, float X3, float Y3, float Z2, uint8 type, int32 targetId) +{ + if (NumGarages >= NUM_GARAGES) { + assert(0); + return NumGarages++; + } + CGarage* pGarage = &aGarages[NumGarages]; + pGarage->m_fInfX = Min(Min(Min(X1, X2), X3), X2 + X3 - X1); + pGarage->m_fSupX = Max(Max(X1, X2), X3); + pGarage->m_fInfY = Min(Min(Min(Y1, Y2), Y3), Y2 + Y3 - Y1); + pGarage->m_fSupY = Max(Max(Y1, Y2), Y3); + pGarage->m_vecCorner1 = CVector(X1, Y1, Z1); + pGarage->m_fInfZ = Z1; + pGarage->m_vDir1 = CVector2D(X2 - X1, Y2 - Y1); + pGarage->m_vDir2 = CVector2D(X3 - X1, Y3 - Y1); + pGarage->m_fSupZ = Z2; + pGarage->m_nMaxStoredCars = NUM_GARAGE_STORED_CARS; + pGarage->m_fDir1Len = pGarage->m_vDir1.Magnitude(); + pGarage->m_fDir2Len = pGarage->m_vDir2.Magnitude(); + pGarage->m_vDir1 /= pGarage->m_fDir1Len; + pGarage->m_vDir2 /= pGarage->m_fDir2Len; + pGarage->m_pDoor1 = nil; + pGarage->m_pDoor2 = nil; + pGarage->m_fDoor1Z = Z1; + pGarage->m_fDoor2Z = Z1; + pGarage->m_eGarageType = type; + pGarage->m_bRecreateDoorOnNextRefresh = false; + pGarage->m_bRotatedDoor = false; + pGarage->m_bCameraFollowsPlayer = false; + pGarage->RefreshDoorPointers(true); + if (pGarage->m_pDoor1) { + pGarage->m_fDoor1Z = pGarage->m_pDoor1->GetPosition().z; + pGarage->m_fDoor1X = pGarage->m_pDoor1->GetPosition().x; + pGarage->m_fDoor1Y = pGarage->m_pDoor1->GetPosition().y; + } + if (pGarage->m_pDoor2) { + pGarage->m_fDoor2Z = pGarage->m_pDoor2->GetPosition().z; + pGarage->m_fDoor2X = pGarage->m_pDoor2->GetPosition().x; + pGarage->m_fDoor2Y = pGarage->m_pDoor2->GetPosition().y; + } + pGarage->m_fDoorHeight = pGarage->m_pDoor1 ? FindDoorHeightForMI(pGarage->m_pDoor1->GetModelIndex()) : 4.0f; + pGarage->m_fDoorPos = 0.0f; + pGarage->m_eGarageState = GS_FULLYCLOSED; + pGarage->m_nTimeToStartAction = 0; + pGarage->field_2 = false; + pGarage->m_nTargetModelIndex = targetId; + pGarage->m_bCollectedCarsState = 0; + pGarage->m_bDeactivated = false; + pGarage->m_bResprayHappened = false; + switch (type) { + case GARAGE_MISSION: + case GARAGE_COLLECTORSITEMS: + case GARAGE_COLLECTSPECIFICCARS: + case GARAGE_COLLECTCARS_1: + case GARAGE_COLLECTCARS_2: + case GARAGE_COLLECTCARS_3: + case GARAGE_FORCARTOCOMEOUTOF: + case GARAGE_60SECONDS: + case GARAGE_MISSION_KEEPCAR: + case GARAGE_FOR_SCRIPT_TO_OPEN: + case GARAGE_HIDEOUT_ONE: + case GARAGE_HIDEOUT_TWO: + case GARAGE_HIDEOUT_THREE: + case GARAGE_FOR_SCRIPT_TO_OPEN_AND_CLOSE: + case GARAGE_KEEPS_OPENING_FOR_SPECIFIC_CAR: + case GARAGE_MISSION_KEEPCAR_REMAINCLOSED: + case GARAGE_COLLECTCARS_4: + case GARAGE_FOR_SCRIPT_TO_OPEN_FOR_CAR: + case GARAGE_HIDEOUT_FOUR: + case GARAGE_HIDEOUT_FIVE: + case GARAGE_HIDEOUT_SIX: + case GARAGE_HIDEOUT_SEVEN: + case GARAGE_HIDEOUT_EIGHT: + case GARAGE_HIDEOUT_NINE: + case GARAGE_HIDEOUT_TEN: + case GARAGE_HIDEOUT_ELEVEN: + case GARAGE_HIDEOUT_TWELVE: + pGarage->m_eGarageState = GS_FULLYCLOSED; + pGarage->m_fDoorPos = 0.0f; + break; + case GARAGE_BOMBSHOP1: + case GARAGE_BOMBSHOP2: + case GARAGE_BOMBSHOP3: + case GARAGE_RESPRAY: + pGarage->m_eGarageState = GS_OPENED; + pGarage->m_fDoorPos = pGarage->m_fDoorHeight; + break; + case GARAGE_CRUSHER: + pGarage->m_eGarageState = GS_OPENED; + pGarage->m_fDoorPos = HALFPI; + break; + default: + assert(false); + } + if (type == GARAGE_CRUSHER) + pGarage->UpdateCrusherAngle(); + else + pGarage->UpdateDoorsHeight(); + return NumGarages++; +} + +void CGarages::ChangeGarageType(int16 garage, uint8 type, int32 mi) +{ + CGarage* pGarage = &aGarages[garage]; + pGarage->m_eGarageType = type; + pGarage->m_nTargetModelIndex = mi; + pGarage->m_eGarageState = GS_FULLYCLOSED; +} + +void CGarage::Update() +{ + if (m_eGarageType != GARAGE_CRUSHER) { + switch (m_eGarageState) { + case GS_FULLYCLOSED: + case GS_OPENED: + case GS_CLOSING: + case GS_OPENING: + case GS_OPENEDCONTAINSCAR: + case GS_CLOSEDCONTAINSCAR: + if (FindPlayerPed() && !m_bCameraFollowsPlayer) { + CVehicle* pVehicle = FindPlayerVehicle(); + if (IsEntityEntirelyInside3D(FindPlayerPed(), 0.25f)) { + TheCamera.pToGarageWeAreIn = this; + CGarages::bCamShouldBeOutisde = true; + } + if (pVehicle) { + if (!IsEntityEntirelyOutside(pVehicle, 0.0f)) + TheCamera.pToGarageWeAreInForHackAvoidFirstPerson = this; + if (pVehicle->GetModelIndex() == MI_MRWHOOP) { + if (pVehicle->IsWithinArea( + m_fInfX - DISTANCE_FOR_MRWHOOP_HACK, + m_fInfY - DISTANCE_FOR_MRWHOOP_HACK, + m_fSupX + DISTANCE_FOR_MRWHOOP_HACK, + m_fSupY + DISTANCE_FOR_MRWHOOP_HACK)) { + TheCamera.pToGarageWeAreIn = this; + CGarages::bCamShouldBeOutisde = true; + } + } + } + } + break; + default: + break; + } + } + if (m_bDeactivated && m_eGarageState == GS_FULLYCLOSED) + return; + if (m_bRotatedDoor) { +#ifdef GTA_PS2 + if (m_eGarageState == GS_OPENING) { + if (m_pDoor1) { + if (FindPlayerPed()->m_pCurrentPhysSurface == m_pDoor1) + m_pDoor1->bUsesCollision = false; + } + if (m_pDoor2) { + if (FindPlayerPed()->m_pCurrentPhysSurface == m_pDoor2) + m_pDoor2->bUsesCollision = false; + } + } + else if (m_eGarageState == GS_OPENED) { + if (m_pDoor1) + m_pDoor1->bUsesCollision = true; + if (m_pDoor2) + m_pDoor2->bUsesCollision = true; + } +#else + if (m_eGarageState == GS_OPENING || m_eGarageState == GS_OPENED) { + if (m_pDoor1) { + if (FindPlayerPed()->m_pCurrentPhysSurface == m_pDoor1 || FindPlayerPed()->GetPedState() == PED_JUMP || FindPlayerPed()->GetPedState() == PED_FALL || !FindPlayerPed()->bIsStanding) + m_pDoor1->bUsesCollision = false; + } + if (m_pDoor2) { + if (FindPlayerPed()->m_pCurrentPhysSurface == m_pDoor2 || FindPlayerPed()->GetPedState() == PED_JUMP || FindPlayerPed()->GetPedState() == PED_FALL || !FindPlayerPed()->bIsStanding) + m_pDoor2->bUsesCollision = false; + } + } + else { + if (m_pDoor1) + m_pDoor1->bUsesCollision = true; + if (m_pDoor2) + m_pDoor2->bUsesCollision = true; + } +#endif + } + switch (m_eGarageType) { + case GARAGE_RESPRAY: + switch (m_eGarageState) { + case GS_OPENED: + if (IsStaticPlayerCarEntirelyInside()) { + if (CGarages::IsCarSprayable(FindPlayerVehicle())) { + if (CWorld::Players[CWorld::PlayerInFocus].m_nMoney >= RESPRAY_PRICE || CGarages::RespraysAreFree) { + m_eGarageState = GS_CLOSING; + CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_GARAGE); + FindPlayerPed()->m_pWanted->m_bIgnoredByCops = true; + } + else { + CGarages::TriggerMessage("GA_3", -1, 4000, -1); // No more freebies. $100 to respray! + m_eGarageState = GS_OPENEDCONTAINSCAR; + DMAudio.PlayFrontEndSound(SOUND_GARAGE_NO_MONEY, 1); + } + } + else { + CGarages::TriggerMessage("GA_1", -1, 4000, -1); // Whoa! I don't touch nothing THAT hot! + m_eGarageState = GS_OPENEDCONTAINSCAR; + DMAudio.PlayFrontEndSound(SOUND_GARAGE_BAD_VEHICLE, 1); + } + } + if (FindPlayerVehicle()) { + if (CalcDistToGarageRectangleSquared(FindPlayerVehicle()->GetPosition().x, FindPlayerVehicle()->GetPosition().y) < SQR(DISTANCE_TO_ACTIVATE_GARAGE)) + CWorld::CallOffChaseForArea( + m_fInfX - DISTANCE_TO_CALL_OFF_CHASE, + m_fInfY - DISTANCE_TO_CALL_OFF_CHASE, + m_fSupX + DISTANCE_TO_CALL_OFF_CHASE, + m_fSupY + DISTANCE_TO_CALL_OFF_CHASE); + } + break; + case GS_CLOSING: + if (FindPlayerVehicle()) + ThrowCarsNearDoorOutOfGarage(FindPlayerVehicle()); + m_fDoorPos = Max(0.0f, m_fDoorPos - (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == 0.0f) { + m_eGarageState = GS_FULLYCLOSED; + m_nTimeToStartAction = CTimer::GetTimeInMilliseconds() + TIME_TO_RESPRAY; + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); + CStats::CheckPointReachedSuccessfully(); + } + UpdateDoorsHeight(); +#ifdef FIX_BUGS + if (FindPlayerVehicle() && FindPlayerVehicle()->IsCar()) +#else + if (FindPlayerVehicle()) +#endif + ((CAutomobile*)(FindPlayerVehicle()))->m_fFireBlowUpTimer = 0.0f; + CWorld::CallOffChaseForArea( + m_fInfX - DISTANCE_TO_CALL_OFF_CHASE, + m_fInfY - DISTANCE_TO_CALL_OFF_CHASE, + m_fSupX + DISTANCE_TO_CALL_OFF_CHASE, + m_fSupY + DISTANCE_TO_CALL_OFF_CHASE); + break; + case GS_FULLYCLOSED: + if (CTimer::GetTimeInMilliseconds() > m_nTimeToStartAction) { + m_eGarageState = GS_OPENING; + DMAudio.PlayFrontEndSound(SOUND_GARAGE_OPENING, 1); + bool bTakeMoney = false; + if (FindPlayerPed()->m_pWanted->GetWantedLevel() != 0) { + bTakeMoney = true; + FindPlayerPed()->m_pWanted->Suspend(); + } + CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_GARAGE); + FindPlayerPed()->m_pWanted->m_bIgnoredByCops = false; +#ifdef FIX_BUGS + bool bChangedColour = false; +#else + bool bChangedColour; +#endif + if (FindPlayerVehicle() && (FindPlayerVehicle()->IsCar() || FindPlayerVehicle()->IsBike())) { + if (FindPlayerVehicle()->m_fHealth < FREE_RESPRAY_HEALTH_THRESHOLD) + bTakeMoney = true; + FindPlayerVehicle()->m_fHealth = 1000.0f; + if (FindPlayerVehicle()->IsCar()) { + ((CAutomobile*)(FindPlayerVehicle()))->m_fFireBlowUpTimer = 0.0f; + ((CAutomobile*)(FindPlayerVehicle()))->Fix(); + } + else { + ((CBike*)(FindPlayerVehicle()))->m_fFireBlowUpTimer = 0.0f; + ((CBike*)(FindPlayerVehicle()))->Fix(); + } + FindPlayerVehicle()->m_nDoorLock = CARLOCK_UNLOCKED; + ++CStats::Sprayings; + if (FindPlayerVehicle()->GetUp().z < 0.0f) { + FindPlayerVehicle()->GetUp() = -FindPlayerVehicle()->GetUp(); + FindPlayerVehicle()->GetRight() = -FindPlayerVehicle()->GetRight(); + } + bChangedColour = false; +#ifdef FIX_BUGS + if (!FindPlayerVehicle()->IsCar() || !((CAutomobile*)(FindPlayerVehicle()))->bFixedColour) { +#else + if (!((CAutomobile*)(FindPlayerVehicle()))->bFixedColour) { +#endif + uint8 colour1, colour2; + uint16 attempt; + FindPlayerVehicle()->GetModelInfo()->ChooseVehicleColour(colour1, colour2); + for (attempt = 0; attempt < 10; attempt++) { + if (colour1 != FindPlayerVehicle()->m_currentColour1 || colour2 != FindPlayerVehicle()->m_currentColour2) + break; + FindPlayerVehicle()->GetModelInfo()->ChooseVehicleColour(colour1, colour2); + } + bChangedColour = (attempt < 10); + FindPlayerVehicle()->m_currentColour1 = colour1; + FindPlayerVehicle()->m_currentColour2 = colour2; + if (bChangedColour) { + for (int i = 0; i < NUM_PARTICLES_IN_RESPRAY; i++) { + CVector pos; + pos.x = CGeneral::GetRandomNumberInRange(m_fInfX + 0.5f, m_fSupX - 0.5f); + pos.y = CGeneral::GetRandomNumberInRange(m_fInfY + 0.5f, m_fSupY - 0.5f); + pos.z = CGeneral::GetRandomNumberInRange(m_fDoor1Z - 3.0f, m_fDoor1Z + 1.0f); + CParticle::AddParticle(PARTICLE_GARAGEPAINT_SPRAY, pos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, CVehicleModelInfo::ms_vehicleColourTable[colour1]); + } + } + } + CenterCarInGarage(FindPlayerVehicle()); + } + if (bTakeMoney) { + if (!CGarages::RespraysAreFree) { + CWorld::Players[CWorld::PlayerInFocus].m_nMoney = Max(0, CWorld::Players[CWorld::PlayerInFocus].m_nMoney - RESPRAY_PRICE); + CStats::AutoPaintingBudget += RESPRAY_PRICE; + } + CGarages::TriggerMessage("GA_2", -1, 4000, -1); // New engine and paint job. The cops won't recognize you! + } + else if (bChangedColour) { + if (CGeneral::GetRandomTrueFalse()) + CGarages::TriggerMessage("GA_15", -1, 4000, -1); // Hope you like the new color. + else + CGarages::TriggerMessage("GA_16", -1, 4000, -1); // Respray is complementary. + } + m_bResprayHappened = true; + } + CWorld::CallOffChaseForArea( + m_fInfX - DISTANCE_TO_CALL_OFF_CHASE, + m_fInfY - DISTANCE_TO_CALL_OFF_CHASE, + m_fSupX + DISTANCE_TO_CALL_OFF_CHASE, + m_fSupY + DISTANCE_TO_CALL_OFF_CHASE); + break; + case GS_OPENING: + m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == m_fDoorHeight) { + m_eGarageState = GS_OPENEDCONTAINSCAR; + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); + } + UpdateDoorsHeight(); + break; + case GS_OPENEDCONTAINSCAR: + if (IsPlayerOutsideGarage()) + m_eGarageState = GS_OPENED; + break; + //case GS_CLOSEDCONTAINSCAR: + //case GS_AFTERDROPOFF: + default: + break; + } + break; + case GARAGE_BOMBSHOP1: + case GARAGE_BOMBSHOP2: + case GARAGE_BOMBSHOP3: + switch (m_eGarageState) { + case GS_OPENED: + if (IsStaticPlayerCarEntirelyInside()) { + if (!FindPlayerVehicle() || FindPlayerVehicle()->m_bombType) { + CGarages::TriggerMessage("GA_5", -1, 4000, -1); //"Your car is already fitted with a bomb" + m_eGarageState = GS_OPENEDCONTAINSCAR; + DMAudio.PlayFrontEndSound(SOUND_GARAGE_BOMB_ALREADY_SET, 1); + break; + } + if (!CGarages::BombsAreFree && CWorld::Players[CWorld::PlayerInFocus].m_nMoney < BOMB_PRICE) { + CGarages::TriggerMessage("GA_4", -1, 4000, -1); // "Car bombs are $1000 each" - weird that the price is hardcoded in message + m_eGarageState = GS_OPENEDCONTAINSCAR; + DMAudio.PlayFrontEndSound(SOUND_GARAGE_NO_MONEY, 1); + break; + } + m_eGarageState = GS_CLOSING; + CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_GARAGE); + FindPlayerPed()->m_pWanted->m_bIgnoredByCops = true; + } + break; + case GS_CLOSING: + if (FindPlayerVehicle()) + ThrowCarsNearDoorOutOfGarage(FindPlayerVehicle()); + m_fDoorPos = Max(0.0f, m_fDoorPos - (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == 0.0f) { + m_eGarageState = GS_FULLYCLOSED; + m_nTimeToStartAction = CTimer::GetTimeInMilliseconds() + TIME_TO_SETUP_BOMB; + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); + } + UpdateDoorsHeight(); + if (m_eGarageType == GARAGE_BOMBSHOP3) + CStreaming::RequestModel(MI_BOMB, STREAMFLAGS_DONT_REMOVE); + break; + case GS_FULLYCLOSED: + if (CTimer::GetTimeInMilliseconds() > m_nTimeToStartAction) { + if (m_eGarageType != GARAGE_BOMBSHOP3 || CStreaming::HasModelLoaded(MI_BOMB)) { + switch (m_eGarageType) { + case GARAGE_BOMBSHOP1: DMAudio.PlayFrontEndSound(SOUND_GARAGE_BOMB1_SET, 1); break; + case GARAGE_BOMBSHOP2: DMAudio.PlayFrontEndSound(SOUND_GARAGE_BOMB2_SET, 1); break; + case GARAGE_BOMBSHOP3: DMAudio.PlayFrontEndSound(SOUND_GARAGE_BOMB3_SET, 1); break; + } + m_eGarageState = GS_OPENING; + if (!CGarages::BombsAreFree) + CWorld::Players[CWorld::PlayerInFocus].m_nMoney = Max(0, CWorld::Players[CWorld::PlayerInFocus].m_nMoney - BOMB_PRICE); + if (FindPlayerVehicle() && (FindPlayerVehicle()->IsCar() || FindPlayerVehicle()->IsBike())) { +#if (!defined GTA_PS2 || defined FIX_BUGS) + FindPlayerVehicle()->m_bombType = CGarages::GetBombTypeForGarageType(m_eGarageType); + FindPlayerVehicle()->m_pBombRigger = FindPlayerPed(); +#else // PS2 version contained a bug: CBike was casted to CAutomobile, but due to coincidence it didn't corrupt memory + ((CAutomobile*)(FindPlayerVehicle()))->m_bombType = CGarages::GetBombTypeForGarageType(m_eGarageType); + ((CAutomobile*)(FindPlayerVehicle()))->m_pBombRigger = FindPlayerPed(); +#endif + if (m_eGarageType == GARAGE_BOMBSHOP3) + CGarages::GivePlayerDetonator(); + CStats::KgsOfExplosivesUsed += KGS_OF_EXPLOSIVES_IN_BOMB; + } +#ifdef DETECT_PAD_INPUT_SWITCH + int16 Mode = CPad::IsAffectedByController ? CPad::GetPad(0)->Mode : 0; +#else + int16 Mode = CPad::GetPad(0)->Mode; +#endif + switch (m_eGarageType) { + case GARAGE_BOMBSHOP1: + switch (Mode) { + case 0: + case 1: + case 2: + CHud::SetHelpMessage(TheText.Get("GA_6"), false); // Arm with ~h~~k~~PED_FIREWEAPON~ button~w~. Bomb will go off when engine is started. + break; + case 3: + CHud::SetHelpMessage(TheText.Get("GA_6B"), false); // Arm with ~h~~k~~PED_FIREWEAPON~ button~w~. Bomb will go off when engine is started. + break; + } + break; + case GARAGE_BOMBSHOP2: + switch (Mode) { + case 0: + case 1: + case 2: + CHud::SetHelpMessage(TheText.Get("GA_7"), false); // Park it, prime it by pressing the ~h~~k~~PED_FIREWEAPON~ button~w~ and LEG IT! + break; + case 3: + CHud::SetHelpMessage(TheText.Get("GA_7B"), false); // Park it, prime it by pressing the ~h~~k~~PED_FIREWEAPON~ button~w~ and LEG IT! + break; + } + break; + case GARAGE_BOMBSHOP3: + CHud::SetHelpMessage(TheText.Get("GA_8"), false); // Use the detonator to activate the bomb. + break; + } + CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_GARAGE); + FindPlayerPed()->m_pWanted->m_bIgnoredByCops = false; + } + else { + CStreaming::RequestModel(MI_BOMB, STREAMFLAGS_DONT_REMOVE); + } + } + break; + case GS_OPENING: + m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == m_fDoorHeight) { + m_eGarageState = GS_OPENEDCONTAINSCAR; + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); + } + UpdateDoorsHeight(); + break; + case GS_OPENEDCONTAINSCAR: + if (IsPlayerOutsideGarage()) + m_eGarageState = GS_OPENED; + break; + //case GS_CLOSEDCONTAINSCAR: + //case GS_AFTERDROPOFF: + default: + break; + } + break; + case GARAGE_MISSION: + switch (m_eGarageState) { + case GS_OPENED: + if (((CVector2D)FindPlayerCoors() - CVector2D(GetGarageCenterX(), GetGarageCenterY())).MagnitudeSqr() > SQR(DISTANCE_TO_CLOSE_MISSION_GARAGE)) { + if ((CTimer::GetFrameCounter() & 0x1F) == 0 +#ifndef GTA_PS2 + && (!m_pTarget || IsEntityTouching3D(m_pTarget)) +#endif + ) { + m_eGarageState = GS_CLOSING; + m_bClosingWithoutTargetCar = true; + } + } + else if (!FindPlayerVehicle() && m_pTarget && IsEntityEntirelyInside3D(m_pTarget, 0.0f) && + IsEntityEntirelyOutside(FindPlayerVehicle() ? (CEntity*)FindPlayerVehicle() : (CEntity*)FindPlayerPed(), 2.0f)) { + CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_GARAGE); + FindPlayerPed()->m_pWanted->m_bIgnoredByCops = true; + m_eGarageState = GS_CLOSING; + m_bClosingWithoutTargetCar = false; + } + break; + case GS_CLOSING: + if (m_pTarget) + ThrowCarsNearDoorOutOfGarage(m_pTarget); + m_fDoorPos = Max(0.0f, m_fDoorPos - (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == 0.0f) { + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); + if (m_bClosingWithoutTargetCar) + m_eGarageState = GS_FULLYCLOSED; + else { + if (m_pTarget) { + m_eGarageState = GS_CLOSEDCONTAINSCAR; + DestroyVehicleAndDriverAndPassengers(m_pTarget); + m_pTarget = nil; + } + else { + m_eGarageState = GS_FULLYCLOSED; + } + CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_GARAGE); + FindPlayerPed()->m_pWanted->m_bIgnoredByCops = false; + } + } + UpdateDoorsHeight(); + break; + case GS_FULLYCLOSED: + if (FindPlayerVehicle() == m_pTarget && m_pTarget) { + if (CalcDistToGarageRectangleSquared( + FindPlayerVehicle()->GetPosition().x, + FindPlayerVehicle()->GetPosition().y) < SQR(DISTANCE_TO_ACTIVATE_GARAGE)) + m_eGarageState = GS_OPENING; + } + break; + case GS_OPENING: + m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == m_fDoorHeight) { + m_eGarageState = GS_OPENED; + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); + } + UpdateDoorsHeight(); + break; + //case GS_OPENEDCONTAINSCAR: + //case GS_CLOSEDCONTAINSCAR: + //case GS_AFTERDROPOFF: + default: + break; + } + break; + case GARAGE_COLLECTSPECIFICCARS: + case GARAGE_COLLECTCARS_1: + case GARAGE_COLLECTCARS_2: + case GARAGE_COLLECTCARS_3: + case GARAGE_COLLECTCARS_4: + switch (m_eGarageState) { + case GS_OPENED: + if (FindPlayerVehicle() && DoesCraigNeedThisCar(FindPlayerVehicle()->GetModelIndex())) { + m_pTarget = FindPlayerVehicle(); + m_pTarget->RegisterReference((CEntity**)&m_pTarget); + } + if (Abs(FindPlayerCoors().x - GetGarageCenterX()) > DISTANCE_TO_CLOSE_COLLECTCARS_GARAGE || + Abs(FindPlayerCoors().y - GetGarageCenterY()) > DISTANCE_TO_CLOSE_COLLECTCARS_GARAGE) { + m_eGarageState = GS_CLOSING; + m_pTarget = nil; + break; + } + if (m_pTarget && !FindPlayerVehicle() && IsEntityEntirelyInside3D(m_pTarget, 0.0f) && + !IsAnyOtherCarTouchingGarage(m_pTarget) && IsEntityEntirelyOutside(FindPlayerPed(), 2.0f)) { +#ifdef FIX_BUGS + if (!m_pTarget->IsCar() || + ((CAutomobile*)(m_pTarget))->Damage.GetEngineStatus() <= ENGINE_STATUS_ON_FIRE && + ((CAutomobile*)(m_pTarget))->m_fFireBlowUpTimer == 0.0f) { +#else + if (((CAutomobile*)(m_pTarget))->Damage.GetEngineStatus() <= ENGINE_STATUS_ON_FIRE && + ((CAutomobile*)(m_pTarget))->m_fFireBlowUpTimer == 0.0f) { +#endif + if (m_pTarget->GetStatus() != STATUS_WRECKED) { + CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_GARAGE); + FindPlayerPed()->m_pWanted->m_bIgnoredByCops = true; + m_eGarageState = GS_CLOSING; + TheCamera.SetCameraDirectlyBehindForFollowPed_CamOnAString(); + } + } + } + break; + case GS_CLOSING: + if (m_pTarget) + ThrowCarsNearDoorOutOfGarage(m_pTarget); + m_fDoorPos = Max(0.0f, m_fDoorPos - (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == 0.0f) { + m_eGarageState = GS_FULLYCLOSED; + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); + if (m_pTarget) { + MarkThisCarAsCollectedForCraig(m_pTarget->GetModelIndex()); + DestroyVehicleAndDriverAndPassengers(m_pTarget); + m_pTarget = nil; + CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_GARAGE); + FindPlayerPed()->m_pWanted->m_bIgnoredByCops = false; + } + } + UpdateDoorsHeight(); + break; + case GS_FULLYCLOSED: + if (FindPlayerVehicle() && + CalcSmallestDistToGarageDoorSquared( + FindPlayerVehicle()->GetPosition().x, + FindPlayerVehicle()->GetPosition().y + ) < SQR(DISTANCE_TO_ACTIVATE_GARAGE)) { + if (DoesCraigNeedThisCar(FindPlayerVehicle()->GetModelIndex())) { + if (FindPlayerVehicle()->VehicleCreatedBy == MISSION_VEHICLE) + CGarages::TriggerMessage("GA_1A", -1, 5000, -1); // Come back when you're not so busy... + else + m_eGarageState = GS_OPENING; + } + else { + if (HasCraigCollectedThisCar(FindPlayerVehicle()->GetModelIndex())) + CGarages::TriggerMessage("GA_20", -1, 5000, -1); // We got more of these than we can shift. Sorry man, no deal. + else if (FindPlayerSpeed().Magnitude() < MAX_SPEED_TO_SHOW_COLLECTED_MESSAGE) + CGarages::TriggerMessage("GA_19", -1, 5000, -1); // We're not interested in that model. + } + } + m_pTarget = nil; + break; + case GS_OPENING: + if (FindPlayerVehicle() && DoesCraigNeedThisCar(FindPlayerVehicle()->GetModelIndex())) { + m_pTarget = FindPlayerVehicle(); + m_pTarget->RegisterReference((CEntity**)&m_pTarget); + } + m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == m_fDoorHeight) { + m_eGarageState = GS_OPENED; + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); + } + UpdateDoorsHeight(); + break; + //case GS_OPENEDCONTAINSCAR: + //case GS_CLOSEDCONTAINSCAR: + //case GS_AFTERDROPOFF: + default: + break; + } + break; + case GARAGE_FORCARTOCOMEOUTOF: + switch (m_eGarageState) { + case GS_OPENED: + if (IsGarageEmpty()) + m_eGarageState = GS_CLOSING; + break; + case GS_CLOSING: + m_fDoorPos = Max(0.0f, m_fDoorPos - (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == 0.0f) { + m_eGarageState = GS_FULLYCLOSED; + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); + } + if (!IsGarageEmpty()) + m_eGarageState = GS_OPENING; + break; + case GS_FULLYCLOSED: + break; + case GS_OPENING: + m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == m_fDoorHeight) { + m_eGarageState = GS_OPENED; + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); + } + UpdateDoorsHeight(); + break; + //case GS_OPENEDCONTAINSCAR: + //case GS_CLOSEDCONTAINSCAR: + //case GS_AFTERDROPOFF: + default: + break; + } + break; + case GARAGE_CRUSHER: + break; + case GARAGE_MISSION_KEEPCAR: + case GARAGE_MISSION_KEEPCAR_REMAINCLOSED: + switch (m_eGarageState) { + case GS_OPENED: + if (((CVector2D)FindPlayerCoors() - CVector2D(GetGarageCenterX(), GetGarageCenterY())).MagnitudeSqr() > SQR(DISTANCE_TO_CLOSE_MISSION_GARAGE) && + !IsAnyOtherCarTouchingGarage(nil)) { + m_eGarageState = GS_CLOSING; + m_bClosingWithoutTargetCar = true; + } + else if (m_pTarget && m_pTarget == FindPlayerVehicle() && IsStaticPlayerCarEntirelyInside() && !IsAnyCarBlockingDoor()) { + CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_GARAGE); + FindPlayerPed()->m_pWanted->m_bIgnoredByCops = true; + m_eGarageState = GS_CLOSING; + m_bClosingWithoutTargetCar = false; + } + break; + case GS_CLOSING: + if (m_pTarget) + ThrowCarsNearDoorOutOfGarage(m_pTarget); + m_fDoorPos = Max(0.0f, m_fDoorPos - (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == 0.0f) { + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); + if (m_bClosingWithoutTargetCar) + m_eGarageState = GS_FULLYCLOSED; + else { + if (m_pTarget) { + m_eGarageState = GS_CLOSEDCONTAINSCAR; + m_nTimeToStartAction = CTimer::GetTimeInMilliseconds() + TIME_TO_PROCESS_KEEPCAR_GARAGE; + m_pTarget = nil; + } + else + m_eGarageState = GS_FULLYCLOSED; + CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_GARAGE); + FindPlayerPed()->m_pWanted->m_bIgnoredByCops = false; + } + } + UpdateDoorsHeight(); + break; + case GS_FULLYCLOSED: + if (FindPlayerVehicle() == m_pTarget && m_pTarget && + CalcDistToGarageRectangleSquared( + FindPlayerVehicle()->GetPosition().x, + FindPlayerVehicle()->GetPosition().y + ) < SQR(DISTANCE_TO_ACTIVATE_KEEPCAR_GARAGE)) + m_eGarageState = GS_OPENING; + break; + case GS_OPENING: + m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == m_fDoorHeight) { + m_eGarageState = GS_OPENED; + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); + } + UpdateDoorsHeight(); + break; + case GS_CLOSEDCONTAINSCAR: + if (m_eGarageType == GARAGE_MISSION_KEEPCAR && CTimer::GetTimeInMilliseconds() > m_nTimeToStartAction) + m_eGarageState = GS_OPENING; + break; + //case GS_OPENEDCONTAINSCAR: + //case GS_AFTERDROPOFF: + default: + break; + } + break; + case GARAGE_FOR_SCRIPT_TO_OPEN: + switch (m_eGarageState) { + case GS_OPENING: + m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == m_fDoorHeight) { + m_eGarageState = GS_OPENED; + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); + } + UpdateDoorsHeight(); + break; + //case GS_OPENED: + //case GS_CLOSING: + //case GS_FULLYCLOSED: + //case GS_OPENEDCONTAINSCAR: + //case GS_CLOSEDCONTAINSCAR: + //case GS_AFTERDROPOFF: + default: + break; + } + break; + case GARAGE_FOR_SCRIPT_TO_OPEN_AND_CLOSE: + switch (m_eGarageState) { + case GS_CLOSING: + m_fDoorPos = Max(0.0f, m_fDoorPos - (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == 0.0f) { + m_eGarageState = GS_FULLYCLOSED; + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); + } + UpdateDoorsHeight(); + break; + case GS_OPENING: + m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == m_fDoorHeight) { + m_eGarageState = GS_OPENED; + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); + } + UpdateDoorsHeight(); + break; + //case GS_OPENED: + //case GS_FULLYCLOSED: + //case GS_OPENEDCONTAINSCAR: + //case GS_CLOSEDCONTAINSCAR: + //case GS_AFTERDROPOFF: + default: + break; + } + break; + case GARAGE_HIDEOUT_ONE: + case GARAGE_HIDEOUT_TWO: + case GARAGE_HIDEOUT_THREE: + case GARAGE_HIDEOUT_FOUR: + case GARAGE_HIDEOUT_FIVE: + case GARAGE_HIDEOUT_SIX: + case GARAGE_HIDEOUT_SEVEN: + case GARAGE_HIDEOUT_EIGHT: + case GARAGE_HIDEOUT_NINE: + case GARAGE_HIDEOUT_TEN: + case GARAGE_HIDEOUT_ELEVEN: + case GARAGE_HIDEOUT_TWELVE: + switch (m_eGarageState) { + case GS_OPENED: + { + float distance = CalcDistToGarageRectangleSquared(FindPlayerCoors().x, FindPlayerCoors().y); + // Close car doors either if player is far, or if he is in vehicle and garage is full, + // or if player is very very far so that we can remove whatever is blocking garage door without him noticing + if ((distance > SQR(DISTANCE_TO_CLOSE_HIDEOUT_GARAGE_IN_CAR) || + !FindPlayerVehicle() && distance > SQR(DISTANCE_TO_CLOSE_HIDEOUT_GARAGE_ON_FOOT)) && + !IsAnyCarBlockingDoor()) + m_eGarageState = GS_CLOSING; + else if (FindPlayerVehicle() && + CountCarsWithCenterPointWithinGarage(FindPlayerVehicle()) >= + FindMaxNumStoredCarsForGarage()) { + m_eGarageState = GS_CLOSING; + } + else if (distance > SQR(DISTANCE_TO_FORCE_CLOSE_HIDEOUT_GARAGE)) { + m_eGarageState = GS_CLOSING; + RemoveCarsBlockingDoorNotInside(); + } + break; + } + case GS_CLOSING: + m_fDoorPos = Max(0.0f, m_fDoorPos - HIDEOUT_DOOR_SPEED_COEFFICIENT * (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); + if (!IsPlayerOutsideGarage()) + m_eGarageState = GS_OPENING; + else if (m_fDoorPos == 0.0f) { + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); + m_eGarageState = GS_FULLYCLOSED; + StoreAndRemoveCarsForThisHideout(CGarages::aCarsInSafeHouses[CGarages::FindSafeHouseIndexForGarageType(m_eGarageType)], NUM_GARAGE_STORED_CARS); + } + UpdateDoorsHeight(); + break; + case GS_FULLYCLOSED: + { + float distance = CalcDistToGarageRectangleSquared(FindPlayerCoors().x, FindPlayerCoors().y); + if (distance < SQR(DISTANCE_TO_OPEN_HIDEOUT_GARAGE_ON_FOOT) || + distance < SQR(DISTANCE_TO_OPEN_HIDEOUT_GARAGE_IN_CAR) && FindPlayerVehicle()) { + if (FindPlayerVehicle() && CGarages::CountCarsInHideoutGarage(m_eGarageType) >= FindMaxNumStoredCarsForGarage()) { + if (m_pDoor1) { + if (((CVector2D)FindPlayerVehicle()->GetPosition() - (CVector2D)m_pDoor1->GetPosition()).MagnitudeSqr() < SQR(DISTANCE_TO_SHOW_HIDEOUT_MESSAGE) && + CTimer::GetTimeInMilliseconds() - CGarages::LastTimeHelpMessage > TIME_BETWEEN_HIDEOUT_MESSAGES) { + if (FindPlayerVehicle()->GetVehicleAppearance() != VEHICLE_APPEARANCE_HELI && FindPlayerVehicle()->GetVehicleAppearance() != VEHICLE_APPEARANCE_PLANE) { + CHud::SetHelpMessage(TheText.Get("GA_21"), false); // You cannot store any more cars in this garage. + CGarages::LastTimeHelpMessage = CTimer::GetTimeInMilliseconds(); + } + } + } + } + else if (RestoreCarsForThisHideout(CGarages::aCarsInSafeHouses[CGarages::FindSafeHouseIndexForGarageType(m_eGarageType)])) + m_eGarageState = GS_OPENING; + } + break; + } + case GS_OPENING: + m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + HIDEOUT_DOOR_SPEED_COEFFICIENT * (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == m_fDoorHeight) { + m_eGarageState = GS_OPENED; + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); + } + UpdateDoorsHeight(); + break; + //case GS_OPENEDCONTAINSCAR: + //case GS_CLOSEDCONTAINSCAR: + //case GS_AFTERDROPOFF: + default: + break; + } + break; + case GARAGE_KEEPS_OPENING_FOR_SPECIFIC_CAR: + switch (m_eGarageState) { + case GS_OPENED: + if (((CVector2D)FindPlayerCoors() - CVector2D(GetGarageCenterX(), GetGarageCenterY())).MagnitudeSqr() > SQR(DISTANCE_TO_CLOSE_MISSION_GARAGE)) { + if (m_pTarget && IsEntityEntirelyOutside(m_pTarget, 0.0f) && !IsAnyOtherCarTouchingGarage(nil)) { + m_eGarageState = GS_CLOSING; + m_bClosingWithoutTargetCar = true; + } + } + break; + case GS_CLOSING: + if (m_pTarget) + ThrowCarsNearDoorOutOfGarage(m_pTarget); + m_fDoorPos = Max(0.0f, m_fDoorPos - (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == 0.0f) { + m_eGarageState = GS_FULLYCLOSED; + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); + } + UpdateDoorsHeight(); + break; + case GS_FULLYCLOSED: + if (FindPlayerVehicle() == m_pTarget && m_pTarget && + CalcDistToGarageRectangleSquared( + FindPlayerVehicle()->GetPosition().x, + FindPlayerVehicle()->GetPosition().y + ) < SQR(DISTANCE_TO_ACTIVATE_GARAGE)) + m_eGarageState = GS_OPENING; + break; + case GS_OPENING: + m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == m_fDoorHeight) { + m_eGarageState = GS_OPENED; + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); + } + UpdateDoorsHeight(); + break; + //case GS_OPENEDCONTAINSCAR: + //case GS_CLOSEDCONTAINSCAR: + //case GS_AFTERDROPOFF: + default: + break; + } + break; + //case GARAGE_COLLECTORSITEMS: + //case GARAGE_60SECONDS: + case GARAGE_FOR_SCRIPT_TO_OPEN_FOR_CAR: + switch (m_eGarageState) { + case GS_OPENED: + if (m_pTarget && IsEntityEntirelyInside3D(m_pTarget, 0.0f) && !IsAnyCarBlockingDoor() && IsPlayerOutsideGarage()) { + CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_GARAGE); + m_eGarageState = GS_CLOSING; + m_bClosingWithoutTargetCar = false; + } + break; + case GS_CLOSING: + m_fDoorPos = Max(0.0f, m_fDoorPos - (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == 0.0f) { + m_eGarageState = GS_FULLYCLOSED; + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); + CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_GARAGE); + } + UpdateDoorsHeight(); + break; + case GS_FULLYCLOSED: + break; + case GS_OPENING: + m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); + if (m_fDoorPos == m_fDoorHeight) { + m_eGarageState = GS_OPENED; + DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); + } + UpdateDoorsHeight(); + break; + //case GS_OPENEDCONTAINSCAR: + //case GS_CLOSEDCONTAINSCAR: + //case GS_AFTERDROPOFF: + default: + break; + } + default: + break; + } +} + +bool CGarage::IsStaticPlayerCarEntirelyInside() +{ + if (!FindPlayerVehicle()) + return false; + if (!FindPlayerVehicle()->IsCar() && !FindPlayerVehicle()->IsBike()) + return false; + if (FindPlayerPed()->GetPedState() != PED_DRIVING) + return false; + if (FindPlayerPed()->m_objective == OBJECTIVE_LEAVE_CAR) + return false; + CVehicle* pVehicle = FindPlayerVehicle(); + if (pVehicle->GetPosition().x < m_fInfX || pVehicle->GetPosition().x > m_fSupX || + pVehicle->GetPosition().y < m_fInfY || pVehicle->GetPosition().y > m_fSupY) + return false; + if (Abs(pVehicle->GetSpeed().x) > 0.01f || + Abs(pVehicle->GetSpeed().y) > 0.01f || + Abs(pVehicle->GetSpeed().z) > 0.01f) + return false; + if (pVehicle->GetSpeed().MagnitudeSqr() > SQR(0.01f)) + return false; + return IsEntityEntirelyInside3D(pVehicle, 0.0f); +} + +bool CGarage::IsPointInsideGarage(CVector pos) +{ + // is it IsPointInsideGarage(pos, 0.0f)? + if (pos.z < m_fInfZ) + return false; + if (pos.z > m_fSupZ) + return false; + CVector2D vecToTarget((CVector2D)pos - m_vecCorner1); + float dp = DotProduct2D(m_vDir1, vecToTarget); + if (dp < 0.0f) + return false; + if (m_fDir1Len < dp) + return false; + dp = DotProduct2D(m_vDir2, vecToTarget); + if (dp < 0.0f) + return false; + if (m_fDir2Len < dp) + return false; + return true; +} + +bool CGarage::IsPointInsideGarage(CVector pos, float m_fMargin) +{ + if (pos.z < m_fInfZ - m_fMargin) + return false; + if (pos.z > m_fSupZ + m_fMargin) + return false; + CVector2D vecToTarget((CVector2D)pos - m_vecCorner1); + float dp = DotProduct2D(m_vDir1, vecToTarget); + if (dp < -m_fMargin) + return false; + if (m_fDir1Len + m_fMargin < dp) + return false; + dp = DotProduct2D(m_vDir2, vecToTarget); + if (dp < -m_fMargin) + return false; + if (m_fDir2Len + m_fMargin < dp) + return false; + return true; +} + +bool CGarage::IsEntityEntirelyInside3D(CEntity* pEntity, float fMargin) +{ + if (pEntity->GetPosition().x < m_fInfX - fMargin || pEntity->GetPosition().x > m_fSupX + fMargin || + pEntity->GetPosition().y < m_fInfY - fMargin || pEntity->GetPosition().y > m_fSupY + fMargin || + pEntity->GetPosition().z < m_fInfZ - fMargin || pEntity->GetPosition().z > m_fSupZ + fMargin) + return false; + CColModel* pColModel = pEntity->GetColModel(); + for (int i = 0; i < pColModel->numSpheres; i++) { + CVector pos = pEntity->GetMatrix() * pColModel->spheres[i].center; + float radius = pColModel->spheres[i].radius; + if (!IsPointInsideGarage(pos, fMargin - radius)) + return false; + } + return true; +} + +bool CGarage::IsEntityEntirelyOutside(CEntity * pEntity, float fMargin) +{ + if (pEntity->GetPosition().x > m_fInfX - fMargin && pEntity->GetPosition().x < m_fSupX + fMargin && + pEntity->GetPosition().y > m_fInfY - fMargin && pEntity->GetPosition().y < m_fSupY + fMargin) + return false; + CColModel* pColModel = pEntity->GetColModel(); + for (int i = 0; i < pColModel->numSpheres; i++) { + CVector pos = pEntity->GetMatrix() * pColModel->spheres[i].center; + float radius = pColModel->spheres[i].radius; + if (IsPointInsideGarage(pos, fMargin + radius)) + return false; + } + return true; +} + +bool CGarage::IsGarageEmpty() +{ + int16 num; + CEntity* pEntities[16]; + CWorld::FindObjectsIntersectingCube(CVector(m_fInfX, m_fInfY, m_fInfZ), CVector(m_fSupX, m_fSupY, m_fSupZ), &num, 16, pEntities, false, true, true, false, false); + if (num <= 0) + return true; + for (int i = 0; i < 16; i++) { + if (IsEntityTouching3D(pEntities[i])) + return false; + } + return true; +} + +bool CGarage::IsPlayerOutsideGarage() +{ + if (FindPlayerVehicle()) + return IsEntityEntirelyOutside(FindPlayerVehicle(), 0.0f); + return IsEntityEntirelyOutside(FindPlayerPed(), 0.0f); +} + +bool CGarage::IsEntityTouching3D(CEntity* pEntity) +{ + float radius = pEntity->GetBoundRadius(); + if (m_fInfX - radius > pEntity->GetPosition().x || m_fSupX + radius < pEntity->GetPosition().x || + m_fInfY - radius > pEntity->GetPosition().y || m_fSupY + radius < pEntity->GetPosition().y || + m_fInfZ - radius > pEntity->GetPosition().z || m_fSupZ + radius < pEntity->GetPosition().z) + return false; + CColModel* pColModel = pEntity->GetColModel(); + for (int i = 0; i < pColModel->numSpheres; i++) { + CVector pos = pEntity->GetMatrix() * pColModel->spheres[i].center; + radius = pColModel->spheres[i].radius; + if (IsPointInsideGarage(pos, radius)) + return true; + } + return false; +} + +bool CGarage::EntityHasASphereWayOutsideGarage(CEntity * pEntity, float fMargin) +{ + CColModel* pColModel = pEntity->GetColModel(); + for (int i = 0; i < pColModel->numSpheres; i++) { + CVector pos = pEntity->GetMatrix() * pColModel->spheres[i].center; + float radius = pColModel->spheres[i].radius; + if (!IsPointInsideGarage(pos, fMargin + radius)) + return true; + } + return false; +} + +bool CGarage::IsAnyOtherCarTouchingGarage(CVehicle * pException) +{ + uint32 i = CPools::GetVehiclePool()->GetSize(); + while (i--) { + CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); + if (!pVehicle || pVehicle == pException || pVehicle->GetStatus() == STATUS_WRECKED) + continue; + if (!IsEntityTouching3D(pVehicle)) + continue; + CColModel* pColModel = pVehicle->GetColModel(); + for (int i = 0; i < pColModel->numSpheres; i++) { + CVector pos = pVehicle->GetMatrix() * pColModel->spheres[i].center; + float radius = pColModel->spheres[i].radius; + if (IsPointInsideGarage(pos, radius)) + return true; + } + } + return false; +} + +void CGarage::ThrowCarsNearDoorOutOfGarage(CVehicle* pException) +{ + uint32 i = CPools::GetVehiclePool()->GetSize(); + while (i--) { + CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); + if (!pVehicle || pVehicle == pException) + continue; + if (!IsEntityTouching3D(pVehicle)) + continue; + CColModel* pColModel = pVehicle->GetColModel(); + for (int i = 0; i < pColModel->numSpheres; i++) { + CVector pos = pVehicle->GetMatrix() * pColModel->spheres[i].center; + float radius = pColModel->spheres[i].radius; + if (!IsPointInsideGarage(pos, 0.0f)) { + CVector vecDirectionAway(pVehicle->GetPosition().x - GetGarageCenterX(), pVehicle->GetPosition().y - GetGarageCenterY(), 0.0f); + vecDirectionAway.Normalise(); + pVehicle->AddToMoveSpeed(vecDirectionAway * CTimer::GetTimeStepInSeconds()); + } + } + } +} + +bool CGarage::IsAnyOtherPedTouchingGarage(CPed * pException) +{ + uint32 i = CPools::GetPedPool()->GetSize(); + while (i--) { + CPed* pPed = CPools::GetPedPool()->GetSlot(i); + if (!pPed || pPed == pException) + continue; + if (!IsEntityTouching3D(pPed)) + continue; + CColModel* pColModel = pException->GetColModel(); + for (int i = 0; i < pColModel->numSpheres; i++) { + CVector pos = pPed->GetMatrix() * pColModel->spheres[i].center; + float radius = pColModel->spheres[i].radius; + if (IsPointInsideGarage(pos, radius)) + return true; + } + } + return false; +} + +bool CGarage::IsAnyCarBlockingDoor() +{ + uint32 i = CPools::GetVehiclePool()->GetSize(); + while (i--) { + CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); + if (!pVehicle) + continue; + if (!IsEntityTouching3D(pVehicle)) + continue; + CColModel* pColModel = pVehicle->GetColModel(); + for (int i = 0; i < pColModel->numSpheres; i++) { + CVector pos = pVehicle->GetMatrix() * pColModel->spheres[i].center; + float radius = pColModel->spheres[i].radius; + if (!IsPointInsideGarage(pos, radius)) + return true; + } + } + return false; +} + +int32 CGarage::CountCarsWithCenterPointWithinGarage(CEntity * pException) +{ + int32 total = 0; + uint32 i = CPools::GetVehiclePool()->GetSize(); + while (i--) { + CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); + if (!pVehicle || pVehicle == pException) + continue; + if (IsPointInsideGarage(pVehicle->GetPosition())) + total++; + } + return total; +} + +void CGarage::RemoveCarsBlockingDoorNotInside() +{ + uint32 i = CPools::GetVehiclePool()->GetSize(); + while (i--) { + CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); + if (!pVehicle) + continue; + if (!IsEntityTouching3D(pVehicle)) + continue; + if (!IsPointInsideGarage(pVehicle->GetPosition())) { + if (!pVehicle->bIsLocked && pVehicle->CanBeDeleted()) { + CWorld::Remove(pVehicle); + delete pVehicle; +#ifndef FIX_BUGS + return; +#endif + } + } + } +} + +void CGarages::PrintMessages() +{ + if (CTimer::GetTimeInMilliseconds() > MessageStartTime && CTimer::GetTimeInMilliseconds() < MessageEndTime) { + CFont::DrawFonts(); + CFont::SetScale(SCREEN_SCALE_X(1.2f), SCREEN_SCALE_Y(1.5f)); + CFont::SetPropOn(); + CFont::SetJustifyOff(); + CFont::SetBackgroundOff(); +#ifdef FIX_BUGS + CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH - 50)); +#else + CFont::SetCentreSize(SCREEN_WIDTH - 50); +#endif + CFont::SetCentreOn(); + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); + CFont::SetColor(CRGBA(27, 89, 130, 255)); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + + float y_offset = SCREEN_SCALE_Y(140.0f); + + if (MessageNumberInString2 >= 0) { + CMessages::InsertNumberInString(TheText.Get(MessageIDString), MessageNumberInString, MessageNumberInString2, -1, -1, -1, -1, gUString); + CFont::PrintString(SCREEN_WIDTH / 2, y_offset - SCREEN_SCALE_Y(30.0f), gUString); + } + else if (MessageNumberInString >= 0) { + CMessages::InsertNumberInString(TheText.Get(MessageIDString), MessageNumberInString, -1, -1, -1, -1, -1, gUString); + CFont::PrintString(SCREEN_WIDTH / 2, y_offset - SCREEN_SCALE_Y(30.0f), gUString); + } + else { + CFont::PrintString(SCREEN_WIDTH / 2, y_offset, TheText.Get(MessageIDString)); + } + } +} + +bool CGarages::IsCarSprayable(CVehicle * pVehicle) +{ + switch (pVehicle->GetModelIndex()) { + case MI_FIRETRUCK: + case MI_AMBULAN: + case MI_POLICE: + case MI_ENFORCER: + case MI_BUS: + case MI_RHINO: + case MI_BARRACKS: + case MI_DODO: + case MI_COACH: +#ifndef GTA_PS2 + case MI_FBIRANCH: +#endif + return false; + default: + break; + } + return true; +} + +void CGarage::UpdateDoorsHeight() +{ + RefreshDoorPointers(false); + if (m_pDoor1) { + m_pDoor1->GetMatrix().GetPosition().z = m_fDoorPos + m_fDoor1Z; + if (m_bRotatedDoor) { + CVector pos; + pos.x = m_fDoor1X + m_fDoorPos * m_pDoor1->GetForward().y * 5.0f / 6.0f; + pos.y = m_fDoor1Y - m_fDoorPos * m_pDoor1->GetForward().x * 5.0f / 6.0f; + pos.z = m_pDoor1->GetPosition().z; + m_pDoor1->SetPosition(pos); + BuildRotatedDoorMatrix(m_pDoor1, m_fDoorPos / m_fDoorHeight); + } + m_pDoor1->GetMatrix().UpdateRW(); + m_pDoor1->UpdateRwFrame(); + } + if (m_pDoor2) { + m_pDoor2->GetMatrix().GetPosition().z = m_fDoorPos + m_fDoor2Z; + if (m_bRotatedDoor) { + CVector pos; + pos.x = m_fDoor2X + m_fDoorPos * m_pDoor2->GetForward().y * 5.0f / 6.0f; + pos.y = m_fDoor2Y - m_fDoorPos * m_pDoor2->GetForward().x * 5.0f / 6.0f; + pos.z = m_pDoor2->GetPosition().z; + m_pDoor2->SetPosition(pos); + BuildRotatedDoorMatrix(m_pDoor2, m_fDoorPos / m_fDoorHeight); + } + m_pDoor2->GetMatrix().UpdateRW(); + m_pDoor2->UpdateRwFrame(); + } +} + +void CGarage::BuildRotatedDoorMatrix(CEntity * pDoor, float fPosition) +{ + float fAngle = -fPosition * HALFPI; + CVector up(-Sin(fAngle) * pDoor->GetForward().y, Sin(fAngle) * pDoor->GetForward().x, Cos(fAngle)); + pDoor->GetRight() = CrossProduct(up, pDoor->GetForward()); + pDoor->GetUp() = up; +} + +void CGarage::UpdateCrusherAngle() +{ + RefreshDoorPointers(false); + m_pDoor2->GetMatrix().SetRotateXOnly(TWOPI - m_fDoorPos); + m_pDoor2->GetMatrix().UpdateRW(); + m_pDoor2->UpdateRwFrame(); +} + +void CGarage::UpdateCrusherShake(float X, float Y) +{ + RefreshDoorPointers(false); + m_pDoor1->GetMatrix().GetPosition().x += X; + m_pDoor1->GetMatrix().GetPosition().y += Y; + m_pDoor1->GetMatrix().UpdateRW(); + m_pDoor1->UpdateRwFrame(); + m_pDoor1->GetMatrix().GetPosition().x -= X; + m_pDoor1->GetMatrix().GetPosition().y -= Y; + m_pDoor2->GetMatrix().GetPosition().x += X; + m_pDoor2->GetMatrix().GetPosition().y += Y; + m_pDoor2->GetMatrix().UpdateRW(); + m_pDoor2->UpdateRwFrame(); + m_pDoor2->GetMatrix().GetPosition().x -= X; + m_pDoor2->GetMatrix().GetPosition().y -= Y; +} + +void CGarage::RefreshDoorPointers(bool bCreate) +{ + bool bNeedToFindDoorEntities = bCreate || m_bRecreateDoorOnNextRefresh; + m_bRecreateDoorOnNextRefresh = false; + if (m_pDoor1) { + if (m_bDoor1IsDummy) { + if (CPools::GetDummyPool()->GetIsFree(CPools::GetDummyPool()->GetJustIndex_NoFreeAssert((CDummy*)m_pDoor1))) + bNeedToFindDoorEntities = true; + else { + if (m_bDoor1PoolIndex != (CPools::GetDummyPool()->GetIndex((CDummy*)m_pDoor1) & 0x7F)) + bNeedToFindDoorEntities = true; + if (!CGarages::IsModelIndexADoor(m_pDoor1->GetModelIndex())) + bNeedToFindDoorEntities = true; + } + } + else { + if (CPools::GetObjectPool()->GetIsFree(CPools::GetObjectPool()->GetJustIndex_NoFreeAssert((CObject*)m_pDoor1))) + bNeedToFindDoorEntities = true; + else { + if (m_bDoor1PoolIndex != (CPools::GetObjectPool()->GetIndex((CObject*)m_pDoor1) & 0x7F)) + bNeedToFindDoorEntities = true; + if (!CGarages::IsModelIndexADoor(m_pDoor1->GetModelIndex())) + bNeedToFindDoorEntities = true; + } + } + } + if (m_pDoor2) { + if (m_bDoor2IsDummy) { + if (CPools::GetDummyPool()->GetIsFree(CPools::GetDummyPool()->GetJustIndex_NoFreeAssert((CDummy*)m_pDoor2))) + bNeedToFindDoorEntities = true; + else { + if (m_bDoor2PoolIndex != (CPools::GetDummyPool()->GetIndex((CDummy*)m_pDoor2) & 0x7F)) + bNeedToFindDoorEntities = true; + if (!CGarages::IsModelIndexADoor(m_pDoor2->GetModelIndex())) + bNeedToFindDoorEntities = true; + } + } + else { + if (CPools::GetObjectPool()->GetIsFree(CPools::GetObjectPool()->GetJustIndex_NoFreeAssert((CObject*)m_pDoor2))) + bNeedToFindDoorEntities = true; + else { + if (m_bDoor2PoolIndex != (CPools::GetObjectPool()->GetIndex((CObject*)m_pDoor2) & 0x7F)) + bNeedToFindDoorEntities = true; + if (!CGarages::IsModelIndexADoor(m_pDoor2->GetModelIndex())) + bNeedToFindDoorEntities = true; + } + } + } + if (bNeedToFindDoorEntities) + FindDoorsEntities(); +} + +void CGarages::TriggerMessage(const char* text, int16 num1, uint16 time, int16 num2) +{ + if (strcmp(text, MessageIDString) == 0 && + CTimer::GetTimeInMilliseconds() >= MessageStartTime && + CTimer::GetTimeInMilliseconds() <= MessageEndTime) { + if (CTimer::GetTimeInMilliseconds() - MessageStartTime <= 500) + return; + MessageStartTime = CTimer::GetTimeInMilliseconds() - 500; + MessageEndTime = CTimer::GetTimeInMilliseconds() - 500 + time; + } + else { + strcpy(MessageIDString, text); + MessageStartTime = CTimer::GetTimeInMilliseconds(); + MessageEndTime = CTimer::GetTimeInMilliseconds() + time; + } + MessageNumberInString = num1; + MessageNumberInString2 = num2; +} + +void CGarages::SetTargetCarForMissonGarage(int16 garage, CVehicle* pVehicle) +{ + assert(garage >= 0 && garage < NUM_GARAGES); + if (pVehicle) { + aGarages[garage].m_pTarget = pVehicle; + aGarages[garage].m_pTarget->RegisterReference((CEntity**)&aGarages[garage].m_pTarget); + if (aGarages[garage].m_eGarageState == GS_CLOSEDCONTAINSCAR) + aGarages[garage].m_eGarageState = GS_FULLYCLOSED; + } + else + aGarages[garage].m_pTarget = nil; +} + +bool CGarages::HasCarBeenDroppedOffYet(int16 garage) +{ + return aGarages[garage].m_eGarageState == GS_CLOSEDCONTAINSCAR; +} + +void CGarages::DeActivateGarage(int16 garage) +{ + aGarages[garage].m_bDeactivated = true; +} + +void CGarages::ActivateGarage(int16 garage) +{ + aGarages[garage].m_bDeactivated = false; + if (aGarages[garage].m_eGarageType == GARAGE_FORCARTOCOMEOUTOF && aGarages[garage].m_eGarageState == GS_FULLYCLOSED) + aGarages[garage].m_eGarageState = GS_OPENING; +} + +int32 CGarages::QueryCarsCollected(int16 garage) +{ + return 0; +} + +bool CGarages::HasImportExportGarageCollectedThisCar(int16 garage, int8 car) +{ + return CarTypesCollected[GetCarsCollectedIndexForGarageType(aGarages[garage].m_eGarageType)] & (BIT(car)); +} + +bool CGarages::IsGarageOpen(int16 garage) +{ + return aGarages[garage].IsOpen(); +} + +bool CGarages::IsGarageClosed(int16 garage) +{ + return aGarages[garage].IsClosed(); +} + +bool CGarages::HasThisCarBeenCollected(int16 garage, uint8 id) +{ + return aGarages[garage].m_bCollectedCarsState & BIT(id); +} + +bool CGarage::DoesCraigNeedThisCar(int32 mi) +{ + int ct = CGarages::GetCarsCollectedIndexForGarageType(m_eGarageType); + for (int i = 0; i < TOTAL_COLLECTCARS_CARS; i++) { + if (mi == gaCarsToCollectInCraigsGarages[ct][i] || (gaCarsToCollectInCraigsGarages[ct][i] == MI_CHEETAH && mi == MI_VICECHEE)) + return (CGarages::CarTypesCollected[ct] & BIT(i)) == 0; + } + return false; +} + +bool CGarage::HasCraigCollectedThisCar(int32 mi) +{ + int ct = CGarages::GetCarsCollectedIndexForGarageType(m_eGarageType); + for (int i = 0; i < TOTAL_COLLECTCARS_CARS; i++) { + if (mi == gaCarsToCollectInCraigsGarages[ct][i] || (gaCarsToCollectInCraigsGarages[ct][i] == MI_CHEETAH && mi == MI_VICECHEE)) + return CGarages::CarTypesCollected[ct] & BIT(i); + } + return false; +} + +bool CGarage::MarkThisCarAsCollectedForCraig(int32 mi) +{ + int ct = CGarages::GetCarsCollectedIndexForGarageType(m_eGarageType); + int index; + for (index = 0; index < TOTAL_COLLECTCARS_CARS; index++) { + if (mi == gaCarsToCollectInCraigsGarages[ct][index] || (gaCarsToCollectInCraigsGarages[ct][index] == MI_CHEETAH && mi == MI_VICECHEE)) + break; + } + if (index >= TOTAL_COLLECTCARS_CARS) + return false; + CGarages::CarTypesCollected[ct] |= BIT(index); + CWorld::Players[CWorld::PlayerInFocus].m_nMoney += IMPORT_REWARD; + for (int i = 0; i < TOTAL_COLLECTCARS_CARS; i++) { + if ((CGarages::CarTypesCollected[ct] & BIT(i)) == 0) { + CGarages::TriggerMessage("GA_13", -1, 5000, -1); // Delivered like a pro. Complete the list and there'll be a bonus for you. + return false; + } + } + CWorld::Players[CWorld::PlayerInFocus].m_nMoney += IMPORT_ALLCARS_REWARD; + CGarages::TriggerMessage("GA_14", -1, 5000, -1); // All the cars. NICE! Here's a little something. + return true; +} + +void CGarage::OpenThisGarage() +{ + if (m_eGarageState == GS_FULLYCLOSED || m_eGarageState == GS_CLOSING || m_eGarageState == GS_CLOSEDCONTAINSCAR) + m_eGarageState = GS_OPENING; +} + +void CGarage::CloseThisGarage() +{ + if (m_eGarageState == GS_OPENED || m_eGarageState == GS_OPENING) + m_eGarageState = GS_CLOSING; +} + +float CGarage::CalcDistToGarageRectangleSquared(float X, float Y) +{ + float distX, distY; + if (X < m_fInfX) + distX = m_fInfX - X; + else if (X > m_fSupX) + distX = X - m_fSupX; + else + distX = 0.0f; + if (Y < m_fInfY) + distY = m_fInfY - Y; + else if (Y > m_fSupY) + distY = Y - m_fSupY; + else + distY = 0.0f; + return SQR(distX) + SQR(distY); +} + +float CGarage::CalcSmallestDistToGarageDoorSquared(float X, float Y) +{ + float dist1 = 10000000.0f; + float dist2 = 10000000.0f; + if (m_pDoor1) + dist1 = SQR(m_fDoor1X - X) + SQR(m_fDoor1Y - Y); + if (m_pDoor2) + dist2 = SQR(m_fDoor2X - X) + SQR(m_fDoor2Y - Y); + return Min(dist1, dist2); +} + +void CGarage::FindDoorsEntities() +{ + m_pDoor1 = nil; + m_pDoor2 = nil; + int xstart = Max(0, CWorld::GetSectorIndexX(GetGarageCenterX() - 100.0f)); + int xend = Min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(GetGarageCenterX() + 100.0f)); + int ystart = Max(0, CWorld::GetSectorIndexY(GetGarageCenterY() - 100.0f)); + int yend = Min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(GetGarageCenterY() + 100.0f)); + assert(xstart <= xend); + assert(ystart <= yend); + + CWorld::AdvanceCurrentScanCode(); + + for (int y = ystart; y <= yend; y++) { + for (int x = xstart; x <= xend; x++) { + CSector* s = CWorld::GetSector(x, y); + FindDoorsEntitiesSectorList(s->m_lists[ENTITYLIST_OBJECTS], false); + FindDoorsEntitiesSectorList(s->m_lists[ENTITYLIST_OBJECTS_OVERLAP], false); + FindDoorsEntitiesSectorList(s->m_lists[ENTITYLIST_DUMMIES], true); + FindDoorsEntitiesSectorList(s->m_lists[ENTITYLIST_DUMMIES_OVERLAP], true); + } + } + if (m_pDoor1 && m_pDoor2) { + CVector2D vecDoor1ToGarage(m_pDoor1->GetPosition().x - GetGarageCenterX(), m_pDoor1->GetPosition().y - GetGarageCenterY()); + CVector2D vecDoor2ToGarage(m_pDoor2->GetPosition().x - GetGarageCenterX(), m_pDoor2->GetPosition().y - GetGarageCenterY()); + if (DotProduct2D(vecDoor1ToGarage, vecDoor2ToGarage) > 0.0f) { + if (vecDoor1ToGarage.MagnitudeSqr() >= vecDoor2ToGarage.MagnitudeSqr()) { + m_pDoor1 = m_pDoor2; + m_bDoor1IsDummy = m_bDoor2IsDummy; + } + m_pDoor2 = nil; + m_bDoor2IsDummy = false; + } + } + if (m_pDoor1) + m_pDoor1->bUsesCollision = true; + if (m_pDoor2) + m_pDoor2->bUsesCollision = true; +} + +void CGarage::FindDoorsEntitiesSectorList(CPtrList& list, bool dummy) +{ + CPtrNode* node; + for (node = list.first; node; node = node->next) { + CEntity* pEntity = (CEntity*)node->item; + if (pEntity->m_scanCode == CWorld::GetCurrentScanCode()) + continue; + pEntity->m_scanCode = CWorld::GetCurrentScanCode(); + if (!pEntity || !CGarages::IsModelIndexADoor(pEntity->GetModelIndex())) + continue; + if (!IsPointInsideGarage(pEntity->GetPosition(), 2.0f)) + continue; + if (!m_pDoor1) { + m_pDoor1 = pEntity; + m_bDoor1IsDummy = dummy; + if (dummy) + m_bDoor1PoolIndex = (CPools::GetDummyPool()->GetIndex((CDummy*)pEntity)) & 0x7F; + else + m_bDoor1PoolIndex = (CPools::GetObjectPool()->GetIndex((CObject*)pEntity)) & 0x7F; + continue; + } + else { + m_pDoor2 = pEntity; + m_bDoor2IsDummy = dummy; + if (dummy) + m_bDoor2PoolIndex = (CPools::GetDummyPool()->GetIndex((CDummy*)pEntity)) & 0x7F; + else + m_bDoor2PoolIndex = (CPools::GetObjectPool()->GetIndex((CObject*)pEntity)) & 0x7F; + } + } +} + +bool CGarages::HasResprayHappened(int16 garage) +{ + bool result = aGarages[garage].m_bResprayHappened; + aGarages[garage].m_bResprayHappened = false; + return result; +} + +void CGarages::SetGarageDoorToRotate(int16 garage) +{ + if (aGarages[garage].m_bRotatedDoor) + return; + aGarages[garage].m_bRotatedDoor = true; + aGarages[garage].m_fDoorHeight /= 2.0f; + aGarages[garage].m_fDoorHeight -= 0.1f; + aGarages[garage].m_fDoorPos = Min(aGarages[garage].m_fDoorHeight, aGarages[garage].m_fDoorPos); + aGarages[garage].UpdateDoorsHeight(); +} + +void CGarages::SetLeaveCameraForThisGarage(int16 garage) +{ + aGarages[garage].m_bCameraFollowsPlayer = true; +} + +bool CGarages::IsThisCarWithinGarageArea(int16 garage, CEntity * pCar) +{ + return aGarages[garage].IsEntityEntirelyInside3D(pCar, 0.0f); +} + +bool CGarages::HasCarBeenCrushed(int32 handle) +{ + return CrushedCarId == handle; +} + +void CStoredCar::StoreCar(CVehicle* pVehicle) +{ + m_nModelIndex = pVehicle->GetModelIndex(); + m_vecPos = pVehicle->GetPosition(); + m_vecAngle = pVehicle->GetForward(); + m_nPrimaryColor = pVehicle->m_currentColour1; + m_nSecondaryColor = pVehicle->m_currentColour2; + m_nRadioStation = pVehicle->m_nRadioStation; + m_nVariationA = pVehicle->m_aExtras[0]; + m_nVariationB = pVehicle->m_aExtras[1]; + m_nFlags = 0; + if (pVehicle->bBulletProof) m_nFlags |= FLAG_BULLETPROOF; + if (pVehicle->bFireProof) m_nFlags |= FLAG_FIREPROOF; + if (pVehicle->bExplosionProof) m_nFlags |= FLAG_EXPLOSIONPROOF; + if (pVehicle->bCollisionProof) m_nFlags |= FLAG_COLLISIONPROOF; + if (pVehicle->bMeleeProof) m_nFlags |= FLAG_MELEEPROOF; + if (pVehicle->IsCar() || pVehicle->IsBike()) + m_nCarBombType = ((CAutomobile*)pVehicle)->m_bombType; // NB: cast to CAutomobile is original behaviour +} + +CVehicle* CStoredCar::RestoreCar() +{ + CStreaming::RequestModel(m_nModelIndex, STREAMFLAGS_DEPENDENCY); + if (!CStreaming::HasModelLoaded(m_nModelIndex)) + return nil; +#ifdef FIX_BUGS + CVehicleModelInfo* pModelInfo = (CVehicleModelInfo*)CModelInfo::GetModelInfo(m_nModelIndex); + assert(pModelInfo); + if (pModelInfo->m_numComps != 0) +#endif + { + CVehicleModelInfo::SetComponentsToUse(m_nVariationA, m_nVariationB); + } + CVehicle* pVehicle; + if (CModelInfo::IsBoatModel(m_nModelIndex)) + pVehicle = new CBoat(m_nModelIndex, RANDOM_VEHICLE); + else if (CModelInfo::IsBikeModel(m_nModelIndex)) + { + CBike* pBike = new CBike(m_nModelIndex, RANDOM_VEHICLE); + pBike->bIsStanding = true; + pVehicle = pBike; + } + else + pVehicle = new CAutomobile(m_nModelIndex, RANDOM_VEHICLE); + pVehicle->SetPosition(m_vecPos); + pVehicle->SetStatus(STATUS_ABANDONED); + pVehicle->GetForward() = m_vecAngle; + pVehicle->GetRight() = CVector(m_vecAngle.y, -m_vecAngle.x, 0.0f); + pVehicle->GetUp() = CVector(0.0f, 0.0f, 1.0f); + pVehicle->pDriver = nil; + pVehicle->m_currentColour1 = m_nPrimaryColor; + pVehicle->m_currentColour2 = m_nSecondaryColor; + pVehicle->m_nRadioStation = m_nRadioStation; + pVehicle->bFreebies = false; + if (pVehicle->IsCar()) + { + ((CAutomobile*)pVehicle)->m_bombType = m_nCarBombType; +#ifdef FIX_BUGS + if (m_nCarBombType != CARBOMB_NONE) + ((CAutomobile*)pVehicle)->m_pBombRigger = FindPlayerPed(); +#endif + } + pVehicle->bHasBeenOwnedByPlayer = true; + pVehicle->m_nDoorLock = CARLOCK_UNLOCKED; + if (m_nFlags & FLAG_BULLETPROOF) pVehicle->bBulletProof = true; + if (m_nFlags & FLAG_FIREPROOF) pVehicle->bFireProof = true; + if (m_nFlags & FLAG_EXPLOSIONPROOF) pVehicle->bExplosionProof = true; + if (m_nFlags & FLAG_COLLISIONPROOF) pVehicle->bCollisionProof = true; + if (m_nFlags & FLAG_MELEEPROOF) pVehicle->bMeleeProof = true; + return pVehicle; +} + +void CGarage::StoreAndRemoveCarsForThisHideout(CStoredCar* aCars, int32 nMax) +{ + for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++) + aCars[i].Clear(); + int i = CPools::GetVehiclePool()->GetSize(); + int index = 0; + while (i--) { + CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); + if (!pVehicle) + continue; + if (IsPointInsideGarage(pVehicle->GetPosition())) { + if (pVehicle->VehicleCreatedBy != MISSION_VEHICLE) { + if (index < Max(NUM_GARAGE_STORED_CARS, nMax) && !EntityHasASphereWayOutsideGarage(pVehicle, 1.0f)) + aCars[index++].StoreCar(pVehicle); + CWorld::Players[CWorld::PlayerInFocus].CancelPlayerEnteringCars(pVehicle); + CWorld::Remove(pVehicle); + delete pVehicle; + } + } + } + // why? + for (i = index; i < NUM_GARAGE_STORED_CARS; i++) + aCars[i].Clear(); +} + +bool CGarage::RestoreCarsForThisHideout(CStoredCar* aCars) +{ + for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++) { + if (aCars[i].HasCar()) { + CVehicle* pVehicle = aCars[i].RestoreCar(); + if (pVehicle) { + CWorld::Add(pVehicle); + aCars[i].Clear(); + } + } + } + for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++) { + if (aCars[i].HasCar()) + return false; + } + return true; +} + +bool CGarages::IsPointInAGarageCameraZone(CVector point) +{ +#ifdef FIX_BUGS + for (uint32 i = 0; i < NumGarages; i++) { +#else + for (int i = 0; i < NUM_GARAGES; i++) { +#endif + switch (aGarages[i].m_eGarageType) { + case GARAGE_NONE: + break; + case GARAGE_COLLECTCARS_1: + case GARAGE_COLLECTCARS_2: + case GARAGE_COLLECTCARS_3: + case GARAGE_COLLECTCARS_4: + if (aGarages[i].IsPointInsideGarage(point, MARGIN_FOR_CAMERA_COLLECTCARS)) + return true; + break; + default: + if (aGarages[i].IsPointInsideGarage(point, MARGIN_FOR_CAMERA_DEFAULT)) + return true; + break; + } + } + return false; +} + +bool CGarages::CameraShouldBeOutside() +{ + return bCamShouldBeOutisde; +} + +void CGarages::GivePlayerDetonator() +{ + CPlayerPed* pPed = FindPlayerPed(); + int slot = CWeaponInfo::GetWeaponInfo(WEAPONTYPE_DETONATOR)->m_nWeaponSlot; + pPed->GiveWeapon(WEAPONTYPE_DETONATOR, 1); + pPed->GetWeapon(pPed->GetWeaponSlot(WEAPONTYPE_DETONATOR)).m_eWeaponState = WEAPONSTATE_READY; + pPed->m_nSelectedWepSlot = slot; + if (pPed->m_storedWeapon != WEAPONTYPE_UNIDENTIFIED) + pPed->m_storedWeapon = WEAPONTYPE_DETONATOR; +} + +float CGarages::FindDoorHeightForMI(int32 mi) +{ + return CModelInfo::GetColModel(mi)->boundingBox.max.z - CModelInfo::GetColModel(mi)->boundingBox.min.z - 0.1f; +} + +void CGarage::TidyUpGarage() +{ + uint32 i = CPools::GetVehiclePool()->GetSize(); +#ifdef FIX_BUGS + while (i--) { +#else + while (--i) { +#endif + CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); + if (pVehicle && (pVehicle->IsCar() || pVehicle->IsBike())) { + if (IsPointInsideGarage(pVehicle->GetPosition())) { + if (pVehicle->GetStatus() == STATUS_WRECKED || pVehicle->GetUp().z < 0.5f) { + CWorld::Remove(pVehicle); + delete pVehicle; + } + } + } + } +} + +void CGarage::TidyUpGarageClose() +{ + uint32 i = CPools::GetVehiclePool()->GetSize(); +#ifdef FIX_BUGS + while (i--) { +#else + while (--i) { +#endif + CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); + if (!pVehicle) + continue; + if ((!pVehicle->IsCar() && !pVehicle->IsBike()) || pVehicle->GetStatus() != STATUS_WRECKED || !IsEntityTouching3D(pVehicle)) + continue; + bool bRemove = false; + if (m_eGarageState != GS_FULLYCLOSED) { + CColModel* pColModel = pVehicle->GetColModel(); + for (int i = 0; i < pColModel->numSpheres; i++) { + CVector pos = pVehicle->GetMatrix() * pColModel->spheres[i].center; + float radius = pColModel->spheres[i].radius; + if (!IsPointInsideGarage(pos, radius)) + bRemove = true; + } + } + else + bRemove = true; + if (bRemove) { + // no MISSION_VEHICLE check??? + CWorld::Remove(pVehicle); + delete pVehicle; + } + } +} + +void CGarages::PlayerArrestedOrDied() +{ + static int GarageToBeTidied = 0; // lol +#ifdef FIX_BUGS + for (uint32 i = 0; i < NumGarages; i++) { +#else + for (int i = 0; i < NUM_GARAGES; i++) { +#endif + if (aGarages[i].m_eGarageType != GARAGE_NONE) + aGarages[i].PlayerArrestedOrDied(); + } + MessageEndTime = 0; + MessageStartTime = 0; +} + +void CGarage::PlayerArrestedOrDied() +{ + switch (m_eGarageType) { + case GARAGE_MISSION: + case GARAGE_COLLECTORSITEMS: + case GARAGE_COLLECTSPECIFICCARS: + case GARAGE_COLLECTCARS_1: + case GARAGE_COLLECTCARS_2: + case GARAGE_COLLECTCARS_3: + case GARAGE_FORCARTOCOMEOUTOF: + case GARAGE_60SECONDS: + case GARAGE_MISSION_KEEPCAR: + case GARAGE_FOR_SCRIPT_TO_OPEN: + case GARAGE_HIDEOUT_ONE: + case GARAGE_HIDEOUT_TWO: + case GARAGE_HIDEOUT_THREE: + case GARAGE_FOR_SCRIPT_TO_OPEN_AND_CLOSE: + case GARAGE_KEEPS_OPENING_FOR_SPECIFIC_CAR: + case GARAGE_MISSION_KEEPCAR_REMAINCLOSED: + case GARAGE_COLLECTCARS_4: + case GARAGE_FOR_SCRIPT_TO_OPEN_FOR_CAR: + case GARAGE_HIDEOUT_FOUR: + case GARAGE_HIDEOUT_FIVE: + case GARAGE_HIDEOUT_SIX: + case GARAGE_HIDEOUT_SEVEN: + case GARAGE_HIDEOUT_EIGHT: + case GARAGE_HIDEOUT_NINE: + case GARAGE_HIDEOUT_TEN: + case GARAGE_HIDEOUT_ELEVEN: + case GARAGE_HIDEOUT_TWELVE: + switch (m_eGarageState) { + case GS_OPENED: + case GS_CLOSING: + case GS_OPENING: + m_eGarageState = GS_CLOSING; + break; + default: + break; + } + break; + case GARAGE_BOMBSHOP1: + case GARAGE_BOMBSHOP2: + case GARAGE_BOMBSHOP3: + case GARAGE_RESPRAY: + case GARAGE_CRUSHER: + switch (m_eGarageState) { + case GS_FULLYCLOSED: + case GS_CLOSING: + case GS_OPENING: + m_eGarageState = GS_OPENING; + break; + default: + break; + } + break; + default: + break; + } +} + +void CGarage::CenterCarInGarage(CVehicle* pVehicle) +{ + if (IsAnyOtherCarTouchingGarage(FindPlayerVehicle())) + return; + if (IsAnyOtherPedTouchingGarage(FindPlayerPed())) + return; + CVector pos = pVehicle->GetPosition(); + float garageX = GetGarageCenterX(); + float garageY = GetGarageCenterY(); + float offsetX = garageX - pos.x; + float offsetY = garageY - pos.y; + float offsetZ = pos.z - pos.z; + float distance = CVector(offsetX, offsetY, offsetZ).Magnitude(); + if (distance < RESPRAY_CENTERING_COEFFICIENT) { + pVehicle->GetMatrix().GetPosition().x = GetGarageCenterX(); + pVehicle->GetMatrix().GetPosition().y = GetGarageCenterY(); + } + else { + pVehicle->GetMatrix().GetPosition().x += offsetX * RESPRAY_CENTERING_COEFFICIENT / distance; + pVehicle->GetMatrix().GetPosition().y += offsetY * RESPRAY_CENTERING_COEFFICIENT / distance; + } + if (!IsEntityEntirelyInside3D(pVehicle, 0.3f)) + pVehicle->SetPosition(pos); +} + +void CGarages::CloseHideOutGaragesBeforeSave() +{ +#ifdef FIX_BUGS + for (uint32 i = 0; i < NumGarages; i++) { +#else + for (int i = 0; i < NUM_GARAGES; i++) { +#endif + if (!IsThisGarageTypeSafehouse(aGarages[i].m_eGarageType)) + continue; + if (aGarages[i].m_eGarageState != GS_FULLYCLOSED) { + aGarages[i].m_eGarageState = GS_FULLYCLOSED; + aGarages[i].StoreAndRemoveCarsForThisHideout(aCarsInSafeHouses[FindSafeHouseIndexForGarageType(aGarages[i].m_eGarageType)], NUM_GARAGE_STORED_CARS); + aGarages[i].RemoveCarsBlockingDoorNotInside(); + aGarages[i].m_fDoorPos = 0.0f; + aGarages[i].UpdateDoorsHeight(); + } + } +} + +int32 CGarages::CountCarsInHideoutGarage(uint8 type) +{ + int32 total = 0; + for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++) { + total += aCarsInSafeHouses[FindSafeHouseIndexForGarageType(type)][i].HasCar(); + } + return total; +} + +bool CGarages::IsPointWithinHideOutGarage(Const CVector& point) +{ +#ifdef FIX_BUGS + for (uint32 i = 0; i < NumGarages; i++) { +#else + for (int i = 0; i < NUM_GARAGES; i++) { +#endif + switch (aGarages[i].m_eGarageType) { + case GARAGE_HIDEOUT_ONE: + case GARAGE_HIDEOUT_TWO: + case GARAGE_HIDEOUT_THREE: + case GARAGE_HIDEOUT_FOUR: + case GARAGE_HIDEOUT_FIVE: + case GARAGE_HIDEOUT_SIX: + case GARAGE_HIDEOUT_SEVEN: + case GARAGE_HIDEOUT_EIGHT: + case GARAGE_HIDEOUT_NINE: + case GARAGE_HIDEOUT_TEN: + case GARAGE_HIDEOUT_ELEVEN: + case GARAGE_HIDEOUT_TWELVE: + if (aGarages[i].IsPointInsideGarage(point)) + return true; + default: break; + } + } + return false; +} + +bool CGarages::IsPointWithinAnyGarage(Const CVector& point) +{ +#ifdef FIX_BUGS + for (uint32 i = 0; i < NumGarages; i++) { +#else + for (int i = 0; i < NUM_GARAGES; i++) { +#endif + switch (aGarages[i].m_eGarageType) { + case GARAGE_NONE: + continue; + default: + if (aGarages[i].IsPointInsideGarage(point)) + return true; + } + } + return false; +} + +void CGarages::SetAllDoorsBackToOriginalHeight() +{ +#ifdef FIX_BUGS + for (uint32 i = 0; i < NumGarages; i++) { +#else + for (int i = 0; i < NUM_GARAGES; i++) { +#endif + switch (aGarages[i].m_eGarageType) { + case GARAGE_NONE: + continue; + default: + aGarages[i].RefreshDoorPointers(true); + if (aGarages[i].m_pDoor1) { + aGarages[i].m_pDoor1->GetMatrix().GetPosition().x = aGarages[i].m_fDoor1X; + aGarages[i].m_pDoor1->GetMatrix().GetPosition().y = aGarages[i].m_fDoor1Y; + aGarages[i].m_pDoor1->GetMatrix().GetPosition().z = aGarages[i].m_fDoor1Z; + if (aGarages[i].m_pDoor1->IsObject()) + ((CObject*)aGarages[i].m_pDoor1)->m_objectMatrix.GetPosition().z = aGarages[i].m_fDoor1Z; + if (aGarages[i].m_bRotatedDoor) + aGarages[i].BuildRotatedDoorMatrix(aGarages[i].m_pDoor1, 0.0f); + aGarages[i].m_pDoor1->GetMatrix().UpdateRW(); + aGarages[i].m_pDoor1->UpdateRwFrame(); + } + if (aGarages[i].m_pDoor2) { + aGarages[i].m_pDoor2->GetMatrix().GetPosition().x = aGarages[i].m_fDoor2X; + aGarages[i].m_pDoor2->GetMatrix().GetPosition().y = aGarages[i].m_fDoor2Y; + aGarages[i].m_pDoor2->GetMatrix().GetPosition().z = aGarages[i].m_fDoor2Z; + if (aGarages[i].m_pDoor2->IsObject()) + ((CObject*)aGarages[i].m_pDoor2)->m_objectMatrix.GetPosition().z = aGarages[i].m_fDoor2Z; + if (aGarages[i].m_bRotatedDoor) + aGarages[i].BuildRotatedDoorMatrix(aGarages[i].m_pDoor2, 0.0f); + aGarages[i].m_pDoor2->GetMatrix().UpdateRW(); + aGarages[i].m_pDoor2->UpdateRwFrame(); + } + } + } +} + +void CGarages::Save(uint8 * buf, uint32 * size) +{ +//INITSAVEBUF + *size = 7876; // for some reason it's not actual size again + //*size = (6 * sizeof(uint32) + TOTAL_COLLECTCARS_GARAGES * sizeof(*CarTypesCollected) + sizeof(uint32) + TOTAL_HIDEOUT_GARAGES * NUM_GARAGE_STORED_CARS * sizeof(CStoredCar) + NUM_GARAGES * sizeof(CGarage)); +#if !defined THIS_IS_STUPID && defined COMPATIBLE_SAVES + memset(buf + 7340, 0, *size - 7340); // garbage data is written otherwise +#endif + CloseHideOutGaragesBeforeSave(); + WriteSaveBuf(buf, NumGarages); + WriteSaveBuf(buf, (uint32)BombsAreFree); + WriteSaveBuf(buf, (uint32)RespraysAreFree); + WriteSaveBuf(buf, CarsCollected); + WriteSaveBuf(buf, BankVansCollected); + WriteSaveBuf(buf, PoliceCarsCollected); + for (int i = 0; i < TOTAL_COLLECTCARS_GARAGES; i++) + WriteSaveBuf(buf, CarTypesCollected[i]); + WriteSaveBuf(buf, LastTimeHelpMessage); + for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++) { + for (int j = 0; j < TOTAL_HIDEOUT_GARAGES; j++) { + WriteSaveBuf(buf, aCarsInSafeHouses[j][i]); + } + } + for (int i = 0; i < NUM_GARAGES; i++) { +#ifdef COMPATIBLE_SAVES + WriteSaveBuf(buf, aGarages[i].m_eGarageType); + WriteSaveBuf(buf, aGarages[i].m_eGarageState); + WriteSaveBuf(buf, aGarages[i].m_nMaxStoredCars); + WriteSaveBuf(buf, aGarages[i].field_2); + WriteSaveBuf(buf, aGarages[i].m_bClosingWithoutTargetCar); + WriteSaveBuf(buf, aGarages[i].m_bDeactivated); + WriteSaveBuf(buf, aGarages[i].m_bResprayHappened); + ZeroSaveBuf(buf, 1); + WriteSaveBuf(buf, aGarages[i].m_nTargetModelIndex); + ZeroSaveBuf(buf, 4 + 4); + WriteSaveBuf(buf, aGarages[i].m_bDoor1PoolIndex); + WriteSaveBuf(buf, aGarages[i].m_bDoor2PoolIndex); + WriteSaveBuf(buf, aGarages[i].m_bDoor1IsDummy); + WriteSaveBuf(buf, aGarages[i].m_bDoor2IsDummy); + WriteSaveBuf(buf, aGarages[i].m_bRecreateDoorOnNextRefresh); + WriteSaveBuf(buf, aGarages[i].m_bRotatedDoor); + WriteSaveBuf(buf, aGarages[i].m_bCameraFollowsPlayer); + ZeroSaveBuf(buf, 1); + WriteSaveBuf(buf, aGarages[i].m_vecCorner1); + WriteSaveBuf(buf, aGarages[i].m_fInfZ); + WriteSaveBuf(buf, aGarages[i].m_vDir1); + WriteSaveBuf(buf, aGarages[i].m_vDir2); + WriteSaveBuf(buf, aGarages[i].m_fSupZ); + WriteSaveBuf(buf, aGarages[i].m_fDir1Len); + WriteSaveBuf(buf, aGarages[i].m_fDir2Len); + WriteSaveBuf(buf, aGarages[i].m_fInfX); + WriteSaveBuf(buf, aGarages[i].m_fSupX); + WriteSaveBuf(buf, aGarages[i].m_fInfY); + WriteSaveBuf(buf, aGarages[i].m_fSupY); + WriteSaveBuf(buf, aGarages[i].m_fDoorPos); + WriteSaveBuf(buf, aGarages[i].m_fDoorHeight); + WriteSaveBuf(buf, aGarages[i].m_fDoor1X); + WriteSaveBuf(buf, aGarages[i].m_fDoor1Y); + WriteSaveBuf(buf, aGarages[i].m_fDoor2X); + WriteSaveBuf(buf, aGarages[i].m_fDoor2Y); + WriteSaveBuf(buf, aGarages[i].m_fDoor1Z); + WriteSaveBuf(buf, aGarages[i].m_fDoor2Z); + WriteSaveBuf(buf, aGarages[i].m_nTimeToStartAction); + WriteSaveBuf(buf, aGarages[i].m_bCollectedCarsState); + ZeroSaveBuf(buf, 3 + 4); + ZeroSaveBuf(buf, sizeof(aGarages[i].m_sStoredCar)); +#else + WriteSaveBuf(buf, aGarages[i]); +#endif + } +//VALIDATESAVEBUF(*size); +} + +const CStoredCar &CStoredCar::operator=(const CStoredCar & other) +{ + m_nModelIndex = other.m_nModelIndex; + m_vecPos = other.m_vecPos; + m_vecAngle = other.m_vecAngle; + m_nFlags = other.m_nFlags; + m_nPrimaryColor = other.m_nPrimaryColor; + m_nSecondaryColor = other.m_nSecondaryColor; + m_nRadioStation = other.m_nRadioStation; + m_nVariationA = other.m_nVariationA; + m_nVariationB = other.m_nVariationB; + m_nCarBombType = other.m_nCarBombType; + return *this; +} + +void CGarages::Load(uint8* buf, uint32 size) +{ +//INITSAVEBUF + assert(size == 7876); + //assert(size == (6 * sizeof(uint32) + TOTAL_COLLECTCARS_GARAGES * sizeof(*CarTypesCollected) + sizeof(uint32) + TOTAL_HIDEOUT_GARAGES * NUM_GARAGE_STORED_CARS * sizeof(CStoredCar) + NUM_GARAGES * sizeof(CGarage))); + CloseHideOutGaragesBeforeSave(); + ReadSaveBuf(&NumGarages, buf); + int32 tempInt; + ReadSaveBuf(&tempInt, buf); + BombsAreFree = tempInt ? true : false; + ReadSaveBuf(&tempInt, buf); + RespraysAreFree = tempInt ? true : false; + ReadSaveBuf(&CarsCollected, buf); + ReadSaveBuf(&BankVansCollected, buf); + ReadSaveBuf(&PoliceCarsCollected, buf); + for (int i = 0; i < TOTAL_COLLECTCARS_GARAGES; i++) + ReadSaveBuf(&CarTypesCollected[i], buf); + ReadSaveBuf(&LastTimeHelpMessage, buf); + for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++) { + for (int j = 0; j < TOTAL_HIDEOUT_GARAGES; j++) { + ReadSaveBuf(&aCarsInSafeHouses[j][i], buf); + } + } + for (int i = 0; i < NUM_GARAGES; i++) { +#ifdef COMPATIBLE_SAVES + ReadSaveBuf(&aGarages[i].m_eGarageType, buf); + ReadSaveBuf(&aGarages[i].m_eGarageState, buf); + ReadSaveBuf(&aGarages[i].m_nMaxStoredCars, buf); + ReadSaveBuf(&aGarages[i].field_2, buf); + ReadSaveBuf(&aGarages[i].m_bClosingWithoutTargetCar, buf); + ReadSaveBuf(&aGarages[i].m_bDeactivated, buf); + ReadSaveBuf(&aGarages[i].m_bResprayHappened, buf); + SkipSaveBuf(buf, 1); + ReadSaveBuf(&aGarages[i].m_nTargetModelIndex, buf); + SkipSaveBuf(buf, 4 + 4); + ReadSaveBuf(&aGarages[i].m_bDoor1PoolIndex, buf); + ReadSaveBuf(&aGarages[i].m_bDoor2PoolIndex, buf); + ReadSaveBuf(&aGarages[i].m_bDoor1IsDummy, buf); + ReadSaveBuf(&aGarages[i].m_bDoor2IsDummy, buf); + ReadSaveBuf(&aGarages[i].m_bRecreateDoorOnNextRefresh, buf); + ReadSaveBuf(&aGarages[i].m_bRotatedDoor, buf); + ReadSaveBuf(&aGarages[i].m_bCameraFollowsPlayer, buf); + SkipSaveBuf(buf, 1); + ReadSaveBuf(&aGarages[i].m_vecCorner1, buf); + ReadSaveBuf(&aGarages[i].m_fInfZ, buf); + ReadSaveBuf(&aGarages[i].m_vDir1, buf); + ReadSaveBuf(&aGarages[i].m_vDir2, buf); + ReadSaveBuf(&aGarages[i].m_fSupZ, buf); + ReadSaveBuf(&aGarages[i].m_fDir1Len, buf); + ReadSaveBuf(&aGarages[i].m_fDir2Len, buf); + ReadSaveBuf(&aGarages[i].m_fInfX, buf); + ReadSaveBuf(&aGarages[i].m_fSupX, buf); + ReadSaveBuf(&aGarages[i].m_fInfY, buf); + ReadSaveBuf(&aGarages[i].m_fSupY, buf); + ReadSaveBuf(&aGarages[i].m_fDoorPos, buf); + ReadSaveBuf(&aGarages[i].m_fDoorHeight, buf); + ReadSaveBuf(&aGarages[i].m_fDoor1X, buf); + ReadSaveBuf(&aGarages[i].m_fDoor1Y, buf); + ReadSaveBuf(&aGarages[i].m_fDoor2X, buf); + ReadSaveBuf(&aGarages[i].m_fDoor2Y, buf); + ReadSaveBuf(&aGarages[i].m_fDoor1Z, buf); + ReadSaveBuf(&aGarages[i].m_fDoor2Z, buf); + ReadSaveBuf(&aGarages[i].m_nTimeToStartAction, buf); + ReadSaveBuf(&aGarages[i].m_bCollectedCarsState, buf); + SkipSaveBuf(buf, 3 + 4); + SkipSaveBuf(buf, sizeof(aGarages[i].m_sStoredCar)); +#else + ReadSaveBuf(&aGarages[i], buf); +#endif + aGarages[i].m_pDoor1 = nil; + aGarages[i].m_pDoor2 = nil; + aGarages[i].m_pTarget = nil; + aGarages[i].m_bRecreateDoorOnNextRefresh = true; + aGarages[i].RefreshDoorPointers(true); + if (aGarages[i].m_eGarageType == GARAGE_CRUSHER) + aGarages[i].UpdateCrusherAngle(); + else + aGarages[i].UpdateDoorsHeight(); + } +//VALIDATESAVEBUF(size); + + MessageEndTime = 0; + bCamShouldBeOutisde = false; + MessageStartTime = 0; +} + +bool +CGarages::IsModelIndexADoor(uint32 id) +{ + return id == MI_GARAGEDOOR2 || + id == MI_GARAGEDOOR3 || + id == MI_GARAGEDOOR4 || + id == MI_GARAGEDOOR5 || + id == MI_GARAGEDOOR6 || + id == MI_GARAGEDOOR7 || + id == MI_GARAGEDOOR9 || + id == MI_GARAGEDOOR10 || + id == MI_GARAGEDOOR11 || + id == MI_GARAGEDOOR12 || + id == MI_GARAGEDOOR13 || + id == MI_GARAGEDOOR14 || + id == MI_GARAGEDOOR15 || + id == MI_GARAGEDOOR16 || + id == MI_GARAGEDOOR18 || + id == MI_GARAGEDOOR19 || + id == MI_GARAGEDOOR20 || + id == MI_GARAGEDOOR21 || + id == MI_GARAGEDOOR22 || + id == MI_GARAGEDOOR23 || + id == MI_GARAGEDOOR24 || + id == MI_GARAGEDOOR25 || + id == MI_GARAGEDOOR26; +} + +void CGarages::StopCarFromBlowingUp(CAutomobile* pCar) +{ + pCar->m_fFireBlowUpTimer = 0.0f; + pCar->m_fHealth = Max(pCar->m_fHealth, 300.0f); + pCar->Damage.SetEngineStatus(Max(pCar->Damage.GetEngineStatus(), 275)); +} + +bool CGarage::Does60SecondsNeedThisCarAtAll(int mi) +{ + for (int i = 0; i < ARRAY_SIZE(gaCarsToCollectIn60Seconds); i++) { + if (gaCarsToCollectIn60Seconds[i] == mi) + return true; + } + return false; +} + +bool CGarage::Does60SecondsNeedThisCar(int mi) +{ + for (int i = 0; i < ARRAY_SIZE(gaCarsToCollectIn60Seconds); i++) { + if (gaCarsToCollectIn60Seconds[i] == mi) + return m_bCollectedCarsState & BIT(i); + } + return false; +} + +void CGarage::MarkThisCarAsCollectedFor60Seconds(int mi) +{ + for (int i = 0; i < ARRAY_SIZE(gaCarsToCollectIn60Seconds); i++) { + if (gaCarsToCollectIn60Seconds[i] == mi) + m_bCollectedCarsState |= BIT(i); + } +} + +bool CGarage::IsPlayerEntirelyInsideGarage() +{ + return IsEntityEntirelyInside3D(FindPlayerVehicle() ? (CEntity*)FindPlayerVehicle() : (CEntity*)FindPlayerPed(), 0.0f); +} diff --git a/src/miami/control/Garages.h b/src/miami/control/Garages.h new file mode 100644 index 00000000..358d404d --- /dev/null +++ b/src/miami/control/Garages.h @@ -0,0 +1,303 @@ +#pragma once +#include "audio_enums.h" +#include "Camera.h" +#include "config.h" +#include "Lists.h" + +class CVehicle; + +enum eGarageState +{ + GS_FULLYCLOSED, + GS_OPENED, + GS_CLOSING, + GS_OPENING, + GS_OPENEDCONTAINSCAR, + GS_CLOSEDCONTAINSCAR, + GS_AFTERDROPOFF, +}; + +enum eGarageType +{ + GARAGE_NONE, + GARAGE_MISSION, + GARAGE_BOMBSHOP1, + GARAGE_BOMBSHOP2, + GARAGE_BOMBSHOP3, + GARAGE_RESPRAY, + GARAGE_COLLECTORSITEMS, + GARAGE_COLLECTSPECIFICCARS, + GARAGE_COLLECTCARS_1, + GARAGE_COLLECTCARS_2, + GARAGE_COLLECTCARS_3, + GARAGE_FORCARTOCOMEOUTOF, + GARAGE_60SECONDS, + GARAGE_CRUSHER, + GARAGE_MISSION_KEEPCAR, + GARAGE_FOR_SCRIPT_TO_OPEN, + GARAGE_HIDEOUT_ONE, + GARAGE_HIDEOUT_TWO, + GARAGE_HIDEOUT_THREE, + GARAGE_FOR_SCRIPT_TO_OPEN_AND_CLOSE, + GARAGE_KEEPS_OPENING_FOR_SPECIFIC_CAR, + GARAGE_MISSION_KEEPCAR_REMAINCLOSED, + GARAGE_COLLECTCARS_4, + GARAGE_FOR_SCRIPT_TO_OPEN_FOR_CAR, + GARAGE_HIDEOUT_FOUR, + GARAGE_HIDEOUT_FIVE, + GARAGE_HIDEOUT_SIX, + GARAGE_HIDEOUT_SEVEN, + GARAGE_HIDEOUT_EIGHT, + GARAGE_HIDEOUT_NINE, + GARAGE_HIDEOUT_TEN, + GARAGE_HIDEOUT_ELEVEN, + GARAGE_HIDEOUT_TWELVE +}; + +enum +{ + TOTAL_COLLECTCARS_GARAGES = 4, + TOTAL_HIDEOUT_GARAGES = 12, + TOTAL_COLLECTCARS_CARS = 6 +}; + +class CStoredCar +{ + enum { + FLAG_BULLETPROOF = 0x1, + FLAG_FIREPROOF = 0x2, + FLAG_EXPLOSIONPROOF = 0x4, + FLAG_COLLISIONPROOF = 0x8, + FLAG_MELEEPROOF = 0x10, + }; + int32 m_nModelIndex; + CVector m_vecPos; + CVector m_vecAngle; + int32 m_nFlags; + int8 m_nPrimaryColor; + int8 m_nSecondaryColor; + int8 m_nRadioStation; + int8 m_nVariationA; + int8 m_nVariationB; + int8 m_nCarBombType; +public: + void Init() { m_nModelIndex = 0; } + void Clear() { m_nModelIndex = 0; } + bool HasCar() { return m_nModelIndex != 0; } + const CStoredCar &operator=(const CStoredCar& other); + void StoreCar(CVehicle*); + CVehicle* RestoreCar(); +}; + +VALIDATE_SIZE(CStoredCar, 0x28); + +#define SWITCH_GARAGE_DISTANCE_CLOSE 40.0f + +class CGarage +{ +public: + uint8 m_eGarageType; + uint8 m_eGarageState; + uint8 m_nMaxStoredCars; + bool field_2; // unused + bool m_bClosingWithoutTargetCar; + bool m_bDeactivated; + bool m_bResprayHappened; + int32 m_nTargetModelIndex; + CEntity *m_pDoor1; + CEntity *m_pDoor2; + uint8 m_bDoor1PoolIndex; + uint8 m_bDoor2PoolIndex; + bool m_bDoor1IsDummy; + bool m_bDoor2IsDummy; + bool m_bRecreateDoorOnNextRefresh; + bool m_bRotatedDoor; + bool m_bCameraFollowsPlayer; + CVector2D m_vecCorner1; + float m_fInfZ; + CVector2D m_vDir1; + CVector2D m_vDir2; + float m_fSupZ; + float m_fDir1Len; + float m_fDir2Len; + float m_fInfX; + float m_fSupX; + float m_fInfY; + float m_fSupY; + float m_fDoorPos; + float m_fDoorHeight; + float m_fDoor1X; + float m_fDoor1Y; + float m_fDoor2X; + float m_fDoor2Y; + float m_fDoor1Z; + float m_fDoor2Z; + uint32 m_nTimeToStartAction; + uint8 m_bCollectedCarsState; + CVehicle *m_pTarget; + CStoredCar m_sStoredCar; // not needed + + void OpenThisGarage(); + void CloseThisGarage(); + bool IsOpen() { return m_eGarageState == GS_OPENED || m_eGarageState == GS_OPENEDCONTAINSCAR; } + bool IsClosed() { return m_eGarageState == GS_FULLYCLOSED; } + bool IsUsed() { return m_eGarageType != GARAGE_NONE; } + void Update(); + float GetGarageCenterX() { return (m_fInfX + m_fSupX) / 2; } + float GetGarageCenterY() { return (m_fInfY + m_fSupY) / 2; } + bool IsFar() + { +#ifdef FIX_BUGS + return Abs(TheCamera.GetPosition().x - GetGarageCenterX()) > SWITCH_GARAGE_DISTANCE_CLOSE || + Abs(TheCamera.GetPosition().y - GetGarageCenterY()) > SWITCH_GARAGE_DISTANCE_CLOSE; +#else + return Abs(TheCamera.GetPosition().x - m_fInfX) > SWITCH_GARAGE_DISTANCE_CLOSE || + Abs(TheCamera.GetPosition().y - m_fInfY) > SWITCH_GARAGE_DISTANCE_CLOSE; +#endif + } + void TidyUpGarageClose(); + void TidyUpGarage(); + void RefreshDoorPointers(bool); + void UpdateCrusherAngle(); + void UpdateDoorsHeight(); + bool IsEntityEntirelyInside3D(CEntity*, float); + bool IsEntityEntirelyOutside(CEntity*, float); + float CalcDistToGarageRectangleSquared(float, float); + float CalcSmallestDistToGarageDoorSquared(float, float); + bool IsAnyOtherCarTouchingGarage(CVehicle* pException); + bool IsStaticPlayerCarEntirelyInside(); + bool IsPlayerOutsideGarage(); + bool IsAnyCarBlockingDoor(); + void CenterCarInGarage(CVehicle*); + bool DoesCraigNeedThisCar(int32); + bool MarkThisCarAsCollectedForCraig(int32); + bool HasCraigCollectedThisCar(int32); + bool IsGarageEmpty(); + void UpdateCrusherShake(float, float); + int32 CountCarsWithCenterPointWithinGarage(CEntity* pException); + void RemoveCarsBlockingDoorNotInside(); + void StoreAndRemoveCarsForThisHideout(CStoredCar*, int32); + bool RestoreCarsForThisHideout(CStoredCar*); + bool IsEntityTouching3D(CEntity*); + bool EntityHasASphereWayOutsideGarage(CEntity*, float); + bool IsAnyOtherPedTouchingGarage(CPed* pException); + void BuildRotatedDoorMatrix(CEntity*, float); + void FindDoorsEntities(); + void FindDoorsEntitiesSectorList(CPtrList&, bool); + void PlayerArrestedOrDied(); + bool Does60SecondsNeedThisCarAtAll(int mi); + bool Does60SecondsNeedThisCar(int mi); + void MarkThisCarAsCollectedFor60Seconds(int mi); + bool IsPlayerEntirelyInsideGarage(); + + bool IsPointInsideGarage(CVector); + bool IsPointInsideGarage(CVector, float); + void ThrowCarsNearDoorOutOfGarage(CVehicle*); + + int32 FindMaxNumStoredCarsForGarage() { return Min(NUM_GARAGE_STORED_CARS, m_nMaxStoredCars); } + +}; + +class CGarages +{ + enum { + MESSAGE_LENGTH = 8, + }; +public: + static int32 BankVansCollected; + static bool BombsAreFree; + static bool RespraysAreFree; + static int32 CarsCollected; + static int32 CarTypesCollected[TOTAL_COLLECTCARS_GARAGES]; + static int32 CrushedCarId; + static uint32 LastTimeHelpMessage; + static int32 MessageNumberInString; + static char MessageIDString[MESSAGE_LENGTH]; + static int32 MessageNumberInString2; + static uint32 MessageStartTime; + static uint32 MessageEndTime; + static uint32 NumGarages; + static bool PlayerInGarage; + static int32 PoliceCarsCollected; + static CGarage aGarages[NUM_GARAGES]; + static CStoredCar aCarsInSafeHouses[TOTAL_HIDEOUT_GARAGES][NUM_GARAGE_STORED_CARS]; + static bool bCamShouldBeOutisde; + + static void Init(void); +#ifndef PS2 + static void Shutdown(void); +#endif + static void Update(void); + + static int16 AddOne(float X1, float Y1, float Z1, float X2, float Y2, float X3, float Y3, float Z2, uint8 type, int32 targetId); + static void ChangeGarageType(int16, uint8, int32); + static void PrintMessages(void); + static void TriggerMessage(const char* text, int16, uint16 time, int16); + static void SetTargetCarForMissonGarage(int16, CVehicle*); + static bool HasCarBeenDroppedOffYet(int16); + static void DeActivateGarage(int16); + static void ActivateGarage(int16); + static int32 QueryCarsCollected(int16); + static bool HasImportExportGarageCollectedThisCar(int16, int8); + static bool IsGarageOpen(int16); + static bool IsGarageClosed(int16); + static bool HasThisCarBeenCollected(int16, uint8); + static void OpenGarage(int16 garage) { aGarages[garage].OpenThisGarage(); } + static void CloseGarage(int16 garage) { aGarages[garage].CloseThisGarage(); } + static bool HasResprayHappened(int16); + static void SetGarageDoorToRotate(int16); + static void SetLeaveCameraForThisGarage(int16); + static bool IsThisCarWithinGarageArea(int16, CEntity*); + static bool HasCarBeenCrushed(int32); + static bool IsPointInAGarageCameraZone(CVector); + static bool CameraShouldBeOutside(void); + static void GivePlayerDetonator(void); + static void PlayerArrestedOrDied(void); + static bool IsPointWithinHideOutGarage(Const CVector&); + static bool IsPointWithinAnyGarage(Const CVector&); + static void SetAllDoorsBackToOriginalHeight(void); + static void Save(uint8* buf, uint32* size); + static void Load(uint8* buf, uint32 size); + static bool IsModelIndexADoor(uint32 id); + static void SetFreeBombs(bool bValue) { BombsAreFree = bValue; } + static void SetFreeResprays(bool bValue) { RespraysAreFree = bValue; } + static void StopCarFromBlowingUp(CAutomobile*); + static void SetMaxNumStoredCarsForGarage(int16 garage, uint8 num) { aGarages[garage].m_nMaxStoredCars = num; } + + static bool IsCarSprayable(CVehicle*); + static float FindDoorHeightForMI(int32); + static void CloseHideOutGaragesBeforeSave(void); + static int32 CountCarsInHideoutGarage(uint8); + static int32 GetBombTypeForGarageType(uint8 type) { return type - GARAGE_BOMBSHOP1 + 1; } + static int32 GetCarsCollectedIndexForGarageType(uint8 type) + { + switch (type) { + case GARAGE_COLLECTCARS_1: return 0; + case GARAGE_COLLECTCARS_2: return 1; + case GARAGE_COLLECTCARS_3: return 2; + case GARAGE_COLLECTCARS_4: return 3; + default: assert(0); + } + return 0; + } + static int32 FindSafeHouseIndexForGarageType(uint8 type) + { + switch (type) { + case GARAGE_HIDEOUT_ONE: return 0; + case GARAGE_HIDEOUT_TWO: return 1; + case GARAGE_HIDEOUT_THREE: return 2; + case GARAGE_HIDEOUT_FOUR: return 3; + case GARAGE_HIDEOUT_FIVE: return 4; + case GARAGE_HIDEOUT_SIX: return 5; + case GARAGE_HIDEOUT_SEVEN: return 6; + case GARAGE_HIDEOUT_EIGHT: return 7; + case GARAGE_HIDEOUT_NINE: return 8; + case GARAGE_HIDEOUT_TEN: return 9; + case GARAGE_HIDEOUT_ELEVEN: return 10; + case GARAGE_HIDEOUT_TWELVE: return 11; + } + return -1; + } + static bool IsThisGarageTypeSafehouse(uint8 type) { return FindSafeHouseIndexForGarageType(type) >= 0; } + +}; diff --git a/src/miami/control/NameGrid.cpp b/src/miami/control/NameGrid.cpp new file mode 100644 index 00000000..204e8b9c --- /dev/null +++ b/src/miami/control/NameGrid.cpp @@ -0,0 +1,87 @@ +#include "common.h" +#include "NameGrid.h" + +// TODO: reverse mobile code + +CPlayerName::CPlayerName() +{ + // TODO +} + +void +CPlayerName::DisplayName(int) +{ + // TODO +} + +CRow::CRow() +{ + // TODO +} + +void +CRow::SetLetter(int, wchar *) +{ + // TODO +} + +CGrid::CGrid() +{ + // TODO +} + +void +CGrid::ProcessAnyLeftJustDown() +{ + unk_int2--; +} + +void +CGrid::ProcessAnyRightJustDown() +{ + unk_int2++; +} + +void +CGrid::ProcessAnyUpJustDown() +{ + unk_int1--; +} + +void +CGrid::ProcessAnyDownJustDown() +{ + unk_int1++; +} + +void +CGrid::AllDoneMakePlayerName() +{ + // TODO +} + +void +CGrid::ProcessDPadCrossJustDown() +{ + // TODO +} + +void +CGrid::DisplayGrid() +{ + // TODO +} + +void +CGrid::ProcessControllerInput() +{ + // TODO +} + +void +CGrid::Process() +{ + ProcessControllerInput(); + DisplayGrid(); + playerName.DisplayName(2 * playerName.unk_4c); +} \ No newline at end of file diff --git a/src/miami/control/NameGrid.h b/src/miami/control/NameGrid.h new file mode 100644 index 00000000..d52cec73 --- /dev/null +++ b/src/miami/control/NameGrid.h @@ -0,0 +1,53 @@ +#pragma once + +// TODO: reverse mobile code + +class CPlayerName +{ + friend class CGrid; + + float x; + float y; + wchar unk_8[34]; + int unk_4c; +public: + CPlayerName(); + void DisplayName(int); +}; + +class CRow +{ + friend class CGrid; + + int unk_0; + int unk_4; + wchar unk_8[20]; + int unk_30; +public: + CRow(); + void SetLetter(int, wchar *); +}; + +class CGrid +{ + CRow rows[5]; + int unk_int1; + int unk_int2; + int unk_int3; + float unk_float1; + float unk_float2; + CPlayerName playerName; + char unk2[4]; + char unk3[4]; +public: + CGrid(); + void ProcessAnyLeftJustDown(); + void ProcessAnyRightJustDown(); + void ProcessAnyUpJustDown(); + void ProcessAnyDownJustDown(); + void AllDoneMakePlayerName(); + void ProcessDPadCrossJustDown(); + void DisplayGrid(); + void ProcessControllerInput(); + void Process(); +}; \ No newline at end of file diff --git a/src/miami/control/OnscreenTimer.cpp b/src/miami/control/OnscreenTimer.cpp new file mode 100644 index 00000000..5045c1e0 --- /dev/null +++ b/src/miami/control/OnscreenTimer.cpp @@ -0,0 +1,165 @@ +#include "common.h" + + +#include "DMAudio.h" +#include "Hud.h" +#include "Replay.h" +#include "Timer.h" +#include "Script.h" +#include "OnscreenTimer.h" +#include "Camera.h" + +void +COnscreenTimer::Init() +{ + m_bDisabled = false; + for(uint32 i = 0; i < NUMONSCREENCOUNTERS; i++) { + m_sCounters[i].m_nCounterOffset = 0; + + for(uint32 j = 0; j < ARRAY_SIZE(m_sCounters[0].m_aCounterText); j++) + m_sCounters[i].m_aCounterText[j] = '\0'; + + m_sCounters[i].m_nType = COUNTER_DISPLAY_NUMBER; + m_sCounters[i].m_bCounterProcessed = false; + } + for(uint32 i = 0; i < NUMONSCREENCLOCKS; i++) { + m_sClocks[i].m_nClockOffset = 0; + + for(uint32 j = 0; j < ARRAY_SIZE(m_sClocks[0].m_aClockText); j++) + m_sClocks[i].m_aClockText[j] = '\0'; + + m_sClocks[i].m_bClockProcessed = false; + m_sClocks[i].m_bClockGoingDown = true; + } +} + +void +COnscreenTimer::Process() +{ + if(!CReplay::IsPlayingBack() && !m_bDisabled) + for(uint32 i = 0; i < NUMONSCREENCLOCKS; i++) + m_sClocks[i].Process(); +} + +void +COnscreenTimer::ProcessForDisplay() +{ + if(CHud::m_Wants_To_Draw_Hud) { + m_bProcessed = false; + for(uint32 i = 0; i < NUMONSCREENCLOCKS; i++) { + m_sClocks[i].m_bClockProcessed = false; + if (m_sClocks[i].m_nClockOffset != 0) { + m_sClocks[i].ProcessForDisplayClock(); + m_sClocks[i].m_bClockProcessed = true; + m_bProcessed = true; + } + } + for(uint32 i = 0; i < NUMONSCREENCOUNTERS; i++) { + m_sCounters[i].m_bCounterProcessed = false; + if (m_sCounters[i].m_nCounterOffset != 0) { + m_sCounters[i].ProcessForDisplayCounter(); + m_sCounters[i].m_bCounterProcessed = true; + m_bProcessed = true; + } + } + } +} + +void +COnscreenTimer::ClearCounter(uint32 offset) +{ + for(uint32 i = 0; i < NUMONSCREENCOUNTERS; i++) { + if(offset == m_sCounters[i].m_nCounterOffset) { + m_sCounters[i].m_nCounterOffset = 0; + m_sCounters[i].m_aCounterText[0] = '\0'; + m_sCounters[i].m_nType = COUNTER_DISPLAY_NUMBER; + m_sCounters[i].m_bCounterProcessed = false; + } + } +} + +void +COnscreenTimer::ClearClock(uint32 offset) +{ + for(uint32 i = 0; i < NUMONSCREENCLOCKS; i++) + if(offset == m_sClocks[i].m_nClockOffset) { + m_sClocks[i].m_nClockOffset = 0; + m_sClocks[i].m_aClockText[0] = '\0'; + m_sClocks[i].m_bClockProcessed = false; + m_sClocks[i].m_bClockGoingDown = true; + } +} + +void +COnscreenTimer::AddCounter(uint32 offset, uint16 type, char* text, uint16 pos) +{ + if (m_sCounters[pos].m_aCounterText[0] != '\0') + return; + + m_sCounters[pos].m_nCounterOffset = offset; + if(text) + strncpy(m_sCounters[pos].m_aCounterText, text, ARRAY_SIZE(m_sCounters[0].m_aCounterText)); + else + m_sCounters[pos].m_aCounterText[0] = '\0'; + + m_sCounters[pos].m_nType = type; +} + +void +COnscreenTimer::AddClock(uint32 offset, char* text, bool bGoingDown) +{ + for(uint32 i = 0; i < NUMONSCREENCLOCKS; i++) { + if(m_sClocks[i].m_nClockOffset == 0) { + m_sClocks[i].m_nClockOffset = offset; + m_sClocks[i].m_bClockGoingDown = bGoingDown; + if(text) + strncpy(m_sClocks[i].m_aClockText, text, ARRAY_SIZE(m_sClocks[0].m_aClockText)); + else + m_sClocks[i].m_aClockText[0] = '\0'; + break; + } + } +} + +void +COnscreenTimerEntry::Process() +{ + if(m_nClockOffset == 0) + return; + + int32* timerPtr = CTheScripts::GetPointerToScriptVariable(m_nClockOffset); + int32 oldTime = *timerPtr; + if (m_bClockGoingDown) { + int32 newTime = oldTime - int32(CTimer::GetTimeStepInMilliseconds()); + *timerPtr = newTime; + if (newTime < 0) { + *timerPtr = 0; + m_bClockProcessed = 0; + m_nClockOffset = 0; + m_aClockText[0] = 0; + } + else { + int32 oldTimeSeconds = oldTime / 1000; + if (oldTimeSeconds < 12 && newTime / 1000 != oldTimeSeconds && !TheCamera.m_WideScreenOn) { + DMAudio.PlayFrontEndSound(SOUND_CLOCK_TICK, newTime / 1000); + } + } + } + else + *timerPtr = oldTime + int32(CTimer::GetTimeStepInMilliseconds()); +} + +void +COnscreenTimerEntry::ProcessForDisplayClock() +{ + uint32 time = *CTheScripts::GetPointerToScriptVariable(m_nClockOffset); + sprintf(m_aClockBuffer, "%02d:%02d", time / 1000 / 60 % 100, + time / 1000 % 60); +} + +void +COnscreenCounterEntry::ProcessForDisplayCounter() +{ + uint32 counter = *CTheScripts::GetPointerToScriptVariable(m_nCounterOffset); + sprintf(m_aCounterBuffer, "%d", counter); +} diff --git a/src/miami/control/OnscreenTimer.h b/src/miami/control/OnscreenTimer.h new file mode 100644 index 00000000..8c049d7d --- /dev/null +++ b/src/miami/control/OnscreenTimer.h @@ -0,0 +1,57 @@ +#pragma once + +enum +{ + COUNTER_DISPLAY_NUMBER, + COUNTER_DISPLAY_BAR, +}; + +class COnscreenTimerEntry +{ +public: + uint32 m_nClockOffset; + char m_aClockText[10]; + char m_aClockBuffer[40]; + bool m_bClockProcessed; + bool m_bClockGoingDown; + + void Process(); + void ProcessForDisplayClock(); +}; + +VALIDATE_SIZE(COnscreenTimerEntry, 0x3C); + +class COnscreenCounterEntry +{ +public: + uint32 m_nCounterOffset; + char m_aCounterText[10]; + uint16 m_nType; + char m_aCounterBuffer[40]; + bool m_bCounterProcessed; + + void ProcessForDisplayCounter(); +}; + +VALIDATE_SIZE(COnscreenCounterEntry, 0x3C); + +class COnscreenTimer +{ +public: + COnscreenTimerEntry m_sClocks[NUMONSCREENCLOCKS]; + COnscreenCounterEntry m_sCounters[NUMONSCREENCOUNTERS]; + bool m_bProcessed; + bool m_bDisabled; + + void Init(); + void Process(); + void ProcessForDisplay(); + + void ClearCounter(uint32 offset); + void ClearClock(uint32 offset); + + void AddCounter(uint32 offset, uint16 type, char* text, uint16 pos); + void AddClock(uint32 offset, char* text, bool bGoingDown); +}; + +VALIDATE_SIZE(COnscreenTimer, 0xF4); diff --git a/src/miami/control/PathFind.cpp b/src/miami/control/PathFind.cpp new file mode 100644 index 00000000..03ff61b6 --- /dev/null +++ b/src/miami/control/PathFind.cpp @@ -0,0 +1,1989 @@ +#include "common.h" + +#include "General.h" +#include "FileMgr.h" // only needed for empty function +#include "Camera.h" +#include "Vehicle.h" +#include "World.h" +#include "Lines.h" // for debug +#include "PathFind.h" + +bool gbShowPedPaths; +bool gbShowCarPaths; +bool gbShowCarPathsLinks; + +CPathFind ThePaths; + +#define MAX_DIST INT16_MAX-1 +#define MIN_PED_ROUTE_DISTANCE 23.8f + + +#define NUMTEMPNODES 5000 +#define NUMDETACHED_CARS 1024 +#define NUMDETACHED_PEDS 1214 +#define NUMTEMPEXTERNALNODES 4600 + +CPathInfoForObject *InfoForTileCars; +CPathInfoForObject *InfoForTilePeds; + +CPathInfoForObject *DetachedInfoForTileCars; +CPathInfoForObject *DetachedInfoForTilePeds; +CTempNodeExternal *TempExternalNodes; +int32 NumTempExternalNodes; +int32 NumDetachedPedNodeGroups; +int32 NumDetachedCarNodeGroups; + +bool +CPedPath::CalcPedRoute(int8 pathType, CVector position, CVector destination, CVector *pointPoses, int16 *pointsFound, int16 maxPoints) +{ + *pointsFound = 0; + CVector vecDistance = destination - position; + if (Abs(vecDistance.x) > MIN_PED_ROUTE_DISTANCE || Abs(vecDistance.y) > MIN_PED_ROUTE_DISTANCE || Abs(vecDistance.z) > MIN_PED_ROUTE_DISTANCE) + return false; + CVector vecPos = (position + destination) * 0.5f; + CVector vecSectorStartPos (vecPos.x - 14.0f, vecPos.y - 14.0f, vecPos.z); + CVector2D vecSectorEndPos (vecPos.x + 28.0f, vecPos.x + 28.0f); + const int16 nodeStartX = (position.x - vecSectorStartPos.x) / 0.7f; + const int16 nodeStartY = (position.y - vecSectorStartPos.y) / 0.7f; + const int16 nodeEndX = (destination.x - vecSectorStartPos.x) / 0.7f; + const int16 nodeEndY = (destination.y - vecSectorStartPos.y) / 0.7f; + if (nodeStartX == nodeEndX && nodeStartY == nodeEndY) + return false; + CPedPathNode pathNodes[40][40]; + CPedPathNode pathNodesList[416]; + for (int32 x = 0; x < 40; x++) { + for (int32 y = 0; y < 40; y++) { + pathNodes[x][y].bBlockade = false; + pathNodes[x][y].id = INT16_MAX; + pathNodes[x][y].nodeIdX = x; + pathNodes[x][y].nodeIdY = y; + } + } + CWorld::AdvanceCurrentScanCode(); + if (pathType != ROUTE_NO_BLOCKADE) { + const int32 nStartX = Max(CWorld::GetSectorIndexX(vecSectorStartPos.x), 0); + const int32 nStartY = Max(CWorld::GetSectorIndexY(vecSectorStartPos.y), 0); + const int32 nEndX = Min(CWorld::GetSectorIndexX(vecSectorEndPos.x), NUMSECTORS_X - 1); + const int32 nEndY = Min(CWorld::GetSectorIndexY(vecSectorEndPos.y), NUMSECTORS_Y - 1); + for (int32 y = nStartY; y <= nEndY; y++) { + for (int32 x = nStartX; x <= nEndX; x++) { + CSector *pSector = CWorld::GetSector(x, y); + AddBlockadeSectorList(pSector->m_lists[ENTITYLIST_VEHICLES], pathNodes, &vecSectorStartPos); + AddBlockadeSectorList(pSector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], pathNodes, &vecSectorStartPos); + AddBlockadeSectorList(pSector->m_lists[ENTITYLIST_OBJECTS], pathNodes, &vecSectorStartPos); + AddBlockadeSectorList(pSector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], pathNodes, &vecSectorStartPos); + } + } + } + for (int32 i = 0; i < 416; i++) { + pathNodesList[i].prev = nil; + pathNodesList[i].next = nil; + } + CPedPathNode *pStartPathNode = &pathNodes[nodeStartX][nodeStartY]; + CPedPathNode *pEndPathNode = &pathNodes[nodeEndX][nodeEndY]; + pEndPathNode->bBlockade = false; + pEndPathNode->id = 0; + pEndPathNode->prev = nil; + pEndPathNode->next = pathNodesList; + pathNodesList[0].prev = pEndPathNode; + int32 pathNodeIndex = 0; + CPedPathNode *pPreviousNode = nil; + for (; pathNodeIndex < 414; pathNodeIndex++) + { + pPreviousNode = pathNodesList[pathNodeIndex].prev; + while (pPreviousNode && pPreviousNode != pStartPathNode) { + const uint8 nodeIdX = pPreviousNode->nodeIdX; + const uint8 nodeIdY = pPreviousNode->nodeIdY; + if (nodeIdX > 0) { + AddNodeToPathList(&pathNodes[nodeIdX - 1][nodeIdY], pathNodeIndex + 5, pathNodesList); + if (nodeIdY > 0) + AddNodeToPathList(&pathNodes[nodeIdX - 1][nodeIdY - 1], pathNodeIndex + 7, pathNodesList); + if (nodeIdY < 39) + AddNodeToPathList(&pathNodes[nodeIdX - 1][nodeIdY + 1], pathNodeIndex + 7, pathNodesList); + } + if (nodeIdX < 39) { + AddNodeToPathList(&pathNodes[nodeIdX + 1][nodeIdY], pathNodeIndex + 5, pathNodesList); + if (nodeIdY > 0) + AddNodeToPathList(&pathNodes[nodeIdX + 1][nodeIdY - 1], pathNodeIndex + 7, pathNodesList); + if (nodeIdY < 39) + AddNodeToPathList(&pathNodes[nodeIdX + 1][nodeIdY + 1], pathNodeIndex + 7, pathNodesList); + } + if (nodeIdY > 0) + AddNodeToPathList(&pathNodes[nodeIdX][nodeIdY - 1], pathNodeIndex + 5, pathNodesList); + if (nodeIdY < 39) + AddNodeToPathList(&pathNodes[nodeIdX][nodeIdY + 1], pathNodeIndex + 5, pathNodesList); + pPreviousNode = pPreviousNode->prev; + if (!pPreviousNode) + break; + } + + if (pPreviousNode && pPreviousNode == pStartPathNode) + break; + } + if (pathNodeIndex == 414) + return false; + CPedPathNode *pPathNode = pStartPathNode; + for (*pointsFound = 0; pPathNode != pEndPathNode && *pointsFound < maxPoints; ++ *pointsFound) { + const uint8 nodeIdX = pPathNode->nodeIdX; + const uint8 nodeIdY = pPathNode->nodeIdY; + if (nodeIdX > 0 && pathNodes[nodeIdX - 1][nodeIdY].id + 5 == pPathNode->id) + pPathNode = &pathNodes[nodeIdX - 1][nodeIdY]; + else if (nodeIdX > 39 && pathNodes[nodeIdX + 1][nodeIdY].id + 5 == pPathNode->id) + pPathNode = &pathNodes[nodeIdX + 1][nodeIdY]; + else if (nodeIdY > 0 && pathNodes[nodeIdX][nodeIdY - 1].id + 5 == pPathNode->id) + pPathNode = &pathNodes[nodeIdX][nodeIdY - 1]; + else if (nodeIdY > 39 && pathNodes[nodeIdX][nodeIdY + 1].id + 5 == pPathNode->id) + pPathNode = &pathNodes[nodeIdX][nodeIdY + 1]; + else if (nodeIdX > 0 && nodeIdY > 0 && pathNodes[nodeIdX - 1][nodeIdY - 1].id + 7 == pPathNode->id) + pPathNode = &pathNodes[nodeIdX - 1][nodeIdY - 1]; + else if (nodeIdX > 0 && nodeIdY < 39 && pathNodes[nodeIdX - 1][nodeIdY + 1].id + 7 == pPathNode->id) + pPathNode = &pathNodes[nodeIdX - 1][nodeIdY + 1]; + else if (nodeIdX < 39 && nodeIdY > 0 && pathNodes[nodeIdX + 1][nodeIdY - 1].id + 7 == pPathNode->id) + pPathNode = &pathNodes[nodeIdX + 1][nodeIdY - 1]; + else if (nodeIdX < 39 && nodeIdY < 39 && pathNodes[nodeIdX + 1][nodeIdY + 1].id + 7 == pPathNode->id) + pPathNode = &pathNodes[nodeIdX + 1][nodeIdY + 1]; + pointPoses[*pointsFound] = vecSectorStartPos; + pointPoses[*pointsFound].x += pPathNode->nodeIdX * 0.7f; + pointPoses[*pointsFound].y += pPathNode->nodeIdY * 0.7f; + } + return true; +} + + +void +CPedPath::AddNodeToPathList(CPedPathNode *pNodeToAdd, int16 id, CPedPathNode *pNodeList) +{ + if (!pNodeToAdd->bBlockade && id < pNodeToAdd->id) { + if (pNodeToAdd->id != INT16_MAX) + RemoveNodeFromList(pNodeToAdd); + AddNodeToList(pNodeToAdd, id, pNodeList); + } +} + +void +CPedPath::RemoveNodeFromList(CPedPathNode *pNode) +{ + pNode->next->prev = pNode->prev; + if (pNode->prev) + pNode->prev->next = pNode->next; +} + +void +CPedPath::AddNodeToList(CPedPathNode *pNode, int16 index, CPedPathNode *pList) +{ + pNode->prev = pList[index].prev; + pNode->next = &pList[index]; + if (pList[index].prev) + pList[index].prev->next = pNode; + pList[index].prev = pNode; + pNode->id = index; +} + +void +CPedPath::AddBlockadeSectorList(CPtrList& list, CPedPathNode(*pathNodes)[40], CVector *pPosition) +{ + CPtrNode* listNode = list.first; + while (listNode) { + CEntity* pEntity = (CEntity*)listNode->item; + if (pEntity->m_scanCode != CWorld::GetCurrentScanCode() && pEntity->bUsesCollision) { + pEntity->m_scanCode = CWorld::GetCurrentScanCode(); + AddBlockade(pEntity, pathNodes, pPosition); + } + listNode = listNode->next; + } +} + +void +CPedPath::AddBlockade(CEntity *pEntity, CPedPathNode(*pathNodes)[40], CVector *pPosition) +{ + const CBox& boundingBox = pEntity->GetColModel()->boundingBox; + const float fBoundMaxY = boundingBox.max.y + 0.3f; + const float fBoundMinY = boundingBox.min.y - 0.3f; + const float fBoundMaxX = boundingBox.max.x + 0.3f; + const float fDistanceX = pPosition->x - pEntity->GetMatrix().GetPosition().x; + const float fDistanceY = pPosition->y - pEntity->GetMatrix().GetPosition().y; + const float fBoundRadius = pEntity->GetBoundRadius(); + CVector vecBoundCentre; + pEntity->GetBoundCentre(vecBoundCentre); + if (vecBoundCentre.x + fBoundRadius >= pPosition->x && + vecBoundCentre.y + fBoundRadius >= pPosition->y && + vecBoundCentre.x - fBoundRadius <= pPosition->x + 28.0f && + vecBoundCentre.y - fBoundRadius <= pPosition->y + 28.0f) { + for (int16 x = 0; x < 40; x++) { + const float pointX = x * 0.7f + fDistanceX; + for (int16 y = 0; y < 40; y++) { + if (!pathNodes[x][y].bBlockade) { + const float pointY = y * 0.7f + fDistanceY; + CVector2D point(pointX, pointY); + if (fBoundMaxX > Abs(DotProduct2D(point, pEntity->GetMatrix().GetRight()))) { + float fDotProduct = DotProduct2D(point, pEntity->GetMatrix().GetForward()); + if (fBoundMaxY > fDotProduct && fBoundMinY < fDotProduct) + pathNodes[x][y].bBlockade = true; + } + } + } + } + } +} + +// Make sure all externals link TO an internal +void +CPathInfoForObject::SwapConnectionsToBeRightWayRound(void) +{ + int e, i; + CPathInfoForObject *tile = this; + + for(e = 0; e < 12; e++) + if(tile[e].type == NodeTypeExtern && tile[e].next < 0) + for(i = 0; i < 12; i++) + if(tile[i].type == NodeTypeIntern && tile[i].next == e){ + tile[e].next = i; + tile[i].next = -1; + bool tmp = !!tile[e].crossing; + tile[e].crossing = tile[i].crossing; + tile[i].crossing = tmp; + } +} + +void +CPathFind::Init(void) +{ + int i; + + m_numPathNodes = 0; + m_numMapObjects = 0; + m_numConnections = 0; + m_numCarPathLinks = 0; + unk = 0; + NumTempExternalNodes = 0; + + for(i = 0; i < NUM_PATHNODES; i++) + m_pathNodes[i].distance = MAX_DIST; +} + +void +CPathFind::AllocatePathFindInfoMem(int16 numPathGroups) +{ + delete[] InfoForTileCars; + InfoForTileCars = nil; + delete[] InfoForTilePeds; + InfoForTilePeds = nil; + + // NB: MIAMI doesn't use numPathGroups here but hardcodes PATHNODESIZE + InfoForTileCars = new CPathInfoForObject[12*PATHNODESIZE]; + memset(InfoForTileCars, 0, 12*PATHNODESIZE*sizeof(CPathInfoForObject)); + InfoForTilePeds = new CPathInfoForObject[12*PATHNODESIZE]; + memset(InfoForTilePeds, 0, 12*PATHNODESIZE*sizeof(CPathInfoForObject)); + + delete[] DetachedInfoForTileCars; + DetachedInfoForTileCars = nil; + delete[] DetachedInfoForTilePeds; + DetachedInfoForTilePeds = nil; + DetachedInfoForTileCars = new CPathInfoForObject[12*NUMDETACHED_CARS]; + memset(DetachedInfoForTileCars, 0, 12*NUMDETACHED_CARS*sizeof(CPathInfoForObject)); + DetachedInfoForTilePeds = new CPathInfoForObject[12*NUMDETACHED_PEDS]; + memset(DetachedInfoForTilePeds, 0, 12*NUMDETACHED_PEDS*sizeof(CPathInfoForObject)); + + delete[] TempExternalNodes; + TempExternalNodes = nil; + TempExternalNodes = new CTempNodeExternal[NUMTEMPEXTERNALNODES]; + memset(TempExternalNodes, 0, NUMTEMPEXTERNALNODES*sizeof(CTempNodeExternal)); + NumTempExternalNodes = 0; + NumDetachedPedNodeGroups = 0; + NumDetachedCarNodeGroups = 0; +} + +void +CPathFind::RegisterMapObject(CTreadable *mapObject) +{ + m_mapObjects[m_numMapObjects++] = mapObject; +} + +void +CPathFind::StoreNodeInfoPed(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, float width, bool crossing, uint8 spawnRate) +{ + int i; + + i = id*12 + node; + InfoForTilePeds[i].type = type; + InfoForTilePeds[i].next = next; + InfoForTilePeds[i].x = x/16.0f; + InfoForTilePeds[i].y = y/16.0f; + InfoForTilePeds[i].z = z/16.0f; + InfoForTilePeds[i].width = 8.0f*Min(width, 15.0f); + InfoForTilePeds[i].numLeftLanes = 0; + InfoForTilePeds[i].numRightLanes = 0; + InfoForTilePeds[i].crossing = crossing; + InfoForTilePeds[i].speedLimit = 0; + InfoForTilePeds[i].roadBlock = false; + InfoForTilePeds[i].disabled = false; + InfoForTilePeds[i].waterPath = false; + InfoForTilePeds[i].onlySmallBoats = false; + InfoForTilePeds[i].betweenLevels = false; + InfoForTilePeds[i].spawnRate = Min(spawnRate, 15); + + if(node == 11) + InfoForTilePeds[id*12].SwapConnectionsToBeRightWayRound(); +} + +void +CPathFind::StoreNodeInfoCar(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, float width, int8 numLeft, int8 numRight, + bool disabled, bool betweenLevels, uint8 speedLimit, bool roadBlock, bool waterPath, uint8 spawnRate) +{ + int i; + + i = id*12 + node; + InfoForTileCars[i].type = type; + InfoForTileCars[i].next = next; + InfoForTileCars[i].x = x/16.0f; + InfoForTileCars[i].y = y/16.0f; + InfoForTileCars[i].z = z/16.0f; + InfoForTileCars[i].width = 8.0f*Min(width, 15.0f); + InfoForTileCars[i].numLeftLanes = numLeft; + InfoForTileCars[i].numRightLanes = numRight; + InfoForTileCars[i].crossing = false; + InfoForTileCars[i].speedLimit = 0; + InfoForTileCars[i].roadBlock = false; + InfoForTileCars[i].disabled = false; + InfoForTileCars[i].waterPath = false; + InfoForTileCars[i].onlySmallBoats = false; + InfoForTileCars[i].betweenLevels = false; + InfoForTileCars[i].spawnRate = Min(spawnRate, 15); + + if(node == 11) + InfoForTileCars[id*12].SwapConnectionsToBeRightWayRound(); +} + +void +CPathFind::StoreDetachedNodeInfoPed(int32 node, int8 type, int32 next, float x, float y, float z, float width, bool crossing, + bool disabled, bool betweenLevels, uint8 spawnRate) +{ + int i; + + if(NumDetachedPedNodeGroups >= NUMDETACHED_PEDS) + return; + + i = NumDetachedPedNodeGroups*12 + node; + DetachedInfoForTilePeds[i].type = type; + DetachedInfoForTilePeds[i].next = next; + DetachedInfoForTilePeds[i].x = x/16.0f; + DetachedInfoForTilePeds[i].y = y/16.0f; + DetachedInfoForTilePeds[i].z = z/16.0f; + DetachedInfoForTilePeds[i].width = 8.0f*Min(width, 31.0f); + DetachedInfoForTilePeds[i].numLeftLanes = 0; + DetachedInfoForTilePeds[i].numRightLanes = 0; + DetachedInfoForTilePeds[i].crossing = crossing; + DetachedInfoForTilePeds[i].speedLimit = 0; + DetachedInfoForTilePeds[i].roadBlock = false; + DetachedInfoForTilePeds[i].disabled = disabled; + DetachedInfoForTilePeds[i].waterPath = false; + DetachedInfoForTilePeds[i].onlySmallBoats = false; + DetachedInfoForTilePeds[i].betweenLevels = betweenLevels; + DetachedInfoForTilePeds[i].spawnRate = Min(spawnRate, 15); + + if(node == 11){ + DetachedInfoForTilePeds[NumDetachedPedNodeGroups*12].SwapConnectionsToBeRightWayRound(); + NumDetachedPedNodeGroups++; + } +} + +void +CPathFind::StoreDetachedNodeInfoCar(int32 node, int8 type, int32 next, float x, float y, float z, float width, int8 numLeft, int8 numRight, + bool disabled, bool betweenLevels, uint8 speedLimit, bool roadBlock, bool waterPath, uint8 spawnRate, bool onlySmallBoats) +{ + int i; + + if(NumDetachedCarNodeGroups >= NUMDETACHED_CARS) + return; + + i = NumDetachedCarNodeGroups*12 + node; + DetachedInfoForTileCars[i].type = type; + DetachedInfoForTileCars[i].next = next; + DetachedInfoForTileCars[i].x = x/16.0f; + DetachedInfoForTileCars[i].y = y/16.0f; + DetachedInfoForTileCars[i].z = z/16.0f; + DetachedInfoForTileCars[i].width = 8.0f*Min(width, 15.0f); + DetachedInfoForTileCars[i].numLeftLanes = numLeft; + DetachedInfoForTileCars[i].numRightLanes = numRight; + DetachedInfoForTileCars[i].crossing = false; + DetachedInfoForTileCars[i].speedLimit = speedLimit; + DetachedInfoForTileCars[i].roadBlock = roadBlock; + DetachedInfoForTileCars[i].disabled = disabled; + DetachedInfoForTileCars[i].waterPath = waterPath; + DetachedInfoForTileCars[i].onlySmallBoats = onlySmallBoats; + DetachedInfoForTileCars[i].betweenLevels = betweenLevels; + DetachedInfoForTileCars[i].spawnRate = Min(spawnRate, 15); + + if(node == 11){ + DetachedInfoForTileCars[NumDetachedCarNodeGroups*12].SwapConnectionsToBeRightWayRound(); + NumDetachedCarNodeGroups++; + } +} + +void +CPathFind::CalcNodeCoors(float x, float y, float z, int32 id, CVector *out) +{ + CVector pos; + pos.x = x; + pos.y = y; + pos.z = z; + *out = m_mapObjects[id]->GetMatrix() * pos; +} + +bool +CPathFind::LoadPathFindData(void) +{ + CFileMgr::SetDir(""); + return false; +} + +void +CPathFind::PreparePathData(void) +{ + int i, j; + int numExtern, numIntern; + CTempNode *tempNodes; + + printf("PreparePathData\n"); + if(!CPathFind::LoadPathFindData() && // empty + InfoForTileCars && InfoForTilePeds && + DetachedInfoForTileCars && DetachedInfoForTilePeds && TempExternalNodes){ + tempNodes = new CTempNode[NUMTEMPNODES]; + + m_numConnections = 0; + + for(i = 0; i < PATHNODESIZE; i++){ + numExtern = 0; + numIntern = 0; + for(j = 0; j < 12; j++){ + if(InfoForTileCars[i*12 + j].type == NodeTypeExtern) + numExtern++; + if(InfoForTileCars[i*12 + j].type == NodeTypeIntern) + numIntern++; + } + if(numIntern > 1 && numExtern != 2) + printf("ILLEGAL BLOCK. MORE THAN 1 INTERNALS AND NOT 2 EXTERNALS (Modelindex:%d)\n", i); + } + + int numExternDetached, numInternDetached; + for(i = 0; i < NUMDETACHED_CARS; i++){ + numExternDetached = 0; + numInternDetached = 0; + for(j = 0; j < 12; j++){ + if(DetachedInfoForTileCars[i*12 + j].type == NodeTypeExtern) + numExternDetached++; + if(DetachedInfoForTilePeds[i*12 + j].type == NodeTypeIntern) + numInternDetached++; + } + // no diagnostic here + } + + for(i = 0; i < PATHNODESIZE; i++) + for(j = 0; j < 12; j++) + if(InfoForTileCars[i*12 + j].type == NodeTypeExtern){ + // MIAMI has MI:%d here but no argument for it + if(InfoForTileCars[i*12 + j].numLeftLanes < 0) + printf("ILLEGAL BLOCK. NEGATIVE NUMBER OF LANES (Obj:%d)\n", i); + if(InfoForTileCars[i*12 + j].numRightLanes < 0) + printf("ILLEGAL BLOCK. NEGATIVE NUMBER OF LANES (Obj:%d)\n", i); + if(InfoForTileCars[i*12 + j].numLeftLanes + InfoForTileCars[i*12 + j].numRightLanes <= 0) + printf("ILLEGAL BLOCK. NO LANES IN NODE (Obj:%d)\n", i); + } + for(i = 0; i < NUMDETACHED_CARS; i++) + for(j = 0; j < 12; j++) + if(DetachedInfoForTileCars[i*12 + j].type == NodeTypeExtern){ + // MI:%d here but no argument for it + if(DetachedInfoForTileCars[i*12 + j].numLeftLanes < 0) + printf("ILLEGAL BLOCK. NEGATIVE NUMBER OF LANES (Obj:%d)\n", i); + if(DetachedInfoForTileCars[i*12 + j].numRightLanes < 0) + printf("ILLEGAL BLOCK. NEGATIVE NUMBER OF LANES (Obj:%d)\n", i); + if(DetachedInfoForTileCars[i*12 + j].numLeftLanes + DetachedInfoForTileCars[i*12 + j].numRightLanes <= 0) + printf("ILLEGAL BLOCK. NO LANES IN NODE (Obj:%d)\n", i); + } + + m_numPathNodes = 0; + PreparePathDataForType(PATH_CAR, tempNodes, InfoForTileCars, 1.0f, DetachedInfoForTileCars, NumDetachedCarNodeGroups); + m_numCarPathNodes = m_numPathNodes; + PreparePathDataForType(PATH_PED, tempNodes, InfoForTilePeds, 1.0f, DetachedInfoForTilePeds, NumDetachedPedNodeGroups); + m_numPedPathNodes = m_numPathNodes - m_numCarPathNodes; + + delete[] tempNodes; + + CountFloodFillGroups(PATH_CAR); + CountFloodFillGroups(PATH_PED); + + delete[] InfoForTileCars; + InfoForTileCars = nil; + delete[] InfoForTilePeds; + InfoForTilePeds = nil; + + delete[] DetachedInfoForTileCars; + DetachedInfoForTileCars = nil; + delete[] DetachedInfoForTilePeds; + DetachedInfoForTilePeds = nil; + delete[] TempExternalNodes; + TempExternalNodes = nil; + } + printf("Done with PreparePathData\n"); +} + +/* String together connected nodes in a list by a flood fill algorithm */ +void +CPathFind::CountFloodFillGroups(uint8 type) +{ + int start, end; + int i, l; + uint16 n; + CPathNode *node, *prev; + + switch(type){ + case PATH_CAR: + start = 0; + end = m_numCarPathNodes; + break; + case PATH_PED: + start = m_numCarPathNodes; + end = start + m_numPedPathNodes; + break; + } + + for(i = start; i < end; i++) + m_pathNodes[i].group = 0; + + n = 0; + for(;;){ + n++; + if(n > 1500){ + for(i = start; m_pathNodes[i].group && i < end; i++); + printf("NumNodes:%d Accounted for:%d\n", end - start, i - start); + } + + // Look for unvisited node + for(i = start; m_pathNodes[i].group && i < end; i++); + if(i == end) + break; + + node = &m_pathNodes[i]; + node->SetNext(nil); + node->group = n; + + if(node->numLinks == 0){ + if(type == PATH_CAR) + printf("Single car node: %f %f %f\n", + node->GetX(), node->GetY(), node->GetZ()); + else + printf("Single ped node: %f %f %f\n", + node->GetX(), node->GetY(), node->GetZ()); + } + + while(node){ + prev = node; + node = node->GetNext(); + for(i = 0; i < prev->numLinks; i++){ + l = ConnectedNode(prev->firstLink + i); + if(m_pathNodes[l].group == 0){ + m_pathNodes[l].group = n; + if(m_pathNodes[l].group == 0) + m_pathNodes[l].group = INT8_MIN; + m_pathNodes[l].SetNext(node); + node = &m_pathNodes[l]; + } + } + } + } + + m_numGroups[type] = n-1; + printf("GraphType:%d. FloodFill groups:%d\n", type, n); +} + +int32 TempListLength; + +void +CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoForObject *objectpathinfo, + float maxdist, CPathInfoForObject *detachednodes, int32 numDetached) +{ + static CVector CoorsXFormed; + int i, j, k; + int l1, l2; + int start; + float posx, posy; + float dx, dy, mag; + float nearestDist; + int nearestId; + int oldNumPathNodes, oldNumLinks; + float dist; + int iseg, jseg; + int done, cont; + int tileStart; + + oldNumPathNodes = m_numPathNodes; + oldNumLinks = m_numConnections; + +#define OBJECTINDEX(n) (mapObjIndices[(n)]) + int16 *mapObjIndices = new int16[NUM_PATHNODES]; + NumTempExternalNodes = 0; + + // Calculate internal nodes, store them and connect them to defining object + for(i = 0; i < m_numMapObjects; i++){ + tileStart = m_numPathNodes; + start = 12 * m_mapObjects[i]->GetModelIndex(); + for(j = 0; j < 12; j++){ + if(objectpathinfo[start + j].type == NodeTypeIntern){ + CalcNodeCoors( + objectpathinfo[start + j].x, + objectpathinfo[start + j].y, + objectpathinfo[start + j].z, + i, + &CoorsXFormed); + m_pathNodes[m_numPathNodes].SetPosition(CoorsXFormed); + OBJECTINDEX(m_numPathNodes) = i; + m_pathNodes[m_numPathNodes].width = objectpathinfo[start + j].width; + m_pathNodes[m_numPathNodes].speedLimit = objectpathinfo[start + j].speedLimit; + m_pathNodes[m_numPathNodes].spawnRate = objectpathinfo[start + j].spawnRate; + m_pathNodes[m_numPathNodes].bUseInRoadBlock = objectpathinfo[start + j].roadBlock; + m_pathNodes[m_numPathNodes].bDisabled = objectpathinfo[start + j].disabled; + m_pathNodes[m_numPathNodes].bWaterPath = objectpathinfo[start + j].waterPath; + m_pathNodes[m_numPathNodes].bOnlySmallBoats = objectpathinfo[start + j].onlySmallBoats; + m_pathNodes[m_numPathNodes].bBetweenLevels = objectpathinfo[start + j].betweenLevels; + m_numPathNodes++; + } + else if(objectpathinfo[start + j].type == NodeTypeExtern){ + CalcNodeCoors( + objectpathinfo[start + j].x, + objectpathinfo[start + j].y, + objectpathinfo[start + j].z, + i, + &CoorsXFormed); + TempExternalNodes[NumTempExternalNodes].pos = CoorsXFormed; + assert(objectpathinfo[start + j].next >= 0); + TempExternalNodes[NumTempExternalNodes].next = tileStart + objectpathinfo[start + j].next; + TempExternalNodes[NumTempExternalNodes].numLeftLanes = objectpathinfo[start + j].numLeftLanes; + TempExternalNodes[NumTempExternalNodes].numRightLanes = objectpathinfo[start + j].numRightLanes; + TempExternalNodes[NumTempExternalNodes].width = objectpathinfo[start + j].width; + TempExternalNodes[NumTempExternalNodes].isCross = !!objectpathinfo[start + j].crossing; + NumTempExternalNodes++; + } + } + } + + // Same thing for detached nodes + for(i = 0; i < numDetached; i++){ + tileStart = m_numPathNodes; + start = 12*i; + for(j = 0; j < 12; j++){ + if(detachednodes[start + j].type == NodeTypeIntern){ + CVector pos; + pos.x = detachednodes[start + j].x; + pos.y = detachednodes[start + j].y; + pos.z = detachednodes[start + j].z; + m_pathNodes[m_numPathNodes].SetPosition(pos); + mapObjIndices[m_numPathNodes] = -(i+1); + m_pathNodes[m_numPathNodes].width = detachednodes[start + j].width; + m_pathNodes[m_numPathNodes].speedLimit = detachednodes[start + j].speedLimit; + m_pathNodes[m_numPathNodes].spawnRate = detachednodes[start + j].spawnRate; + m_pathNodes[m_numPathNodes].bUseInRoadBlock = detachednodes[start + j].roadBlock; + m_pathNodes[m_numPathNodes].bDisabled = detachednodes[start + j].disabled; + m_pathNodes[m_numPathNodes].bWaterPath = detachednodes[start + j].waterPath; + m_pathNodes[m_numPathNodes].bOnlySmallBoats = detachednodes[start + j].onlySmallBoats; + m_pathNodes[m_numPathNodes].bBetweenLevels = detachednodes[start + j].betweenLevels; + m_numPathNodes++; + }else if(detachednodes[start + j].type == NodeTypeExtern){ + TempExternalNodes[NumTempExternalNodes].pos.x = detachednodes[start + j].x; + TempExternalNodes[NumTempExternalNodes].pos.y = detachednodes[start + j].y; + TempExternalNodes[NumTempExternalNodes].pos.z = detachednodes[start + j].z; + assert(detachednodes[start + j].next >= 0); + TempExternalNodes[NumTempExternalNodes].next = tileStart + detachednodes[start + j].next; + TempExternalNodes[NumTempExternalNodes].numLeftLanes = detachednodes[start + j].numLeftLanes; + TempExternalNodes[NumTempExternalNodes].numRightLanes = detachednodes[start + j].numRightLanes; + TempExternalNodes[NumTempExternalNodes].width = detachednodes[start + j].width; + TempExternalNodes[NumTempExternalNodes].isCross = !!detachednodes[start + j].crossing; + NumTempExternalNodes++; + } + } + } + + // Insert external nodes into TempList + TempListLength = 0; + for(i = 0; i < NumTempExternalNodes; i++){ + // find closest unconnected node + nearestId = -1; + nearestDist = maxdist; + for(k = 0; k < TempListLength; k++){ + if(tempnodes[k].linkState != 1) + continue; + dx = tempnodes[k].pos.x - TempExternalNodes[i].pos.x; + if(Abs(dx) < nearestDist){ + dy = tempnodes[k].pos.y - TempExternalNodes[i].pos.y; + if(Abs(dy) < nearestDist){ + nearestDist = Max(Abs(dx), Abs(dy)); + nearestId = k; + } + } + } + + if(nearestId < 0){ + // None found, add this one to temp list + tempnodes[TempListLength].pos = TempExternalNodes[i].pos; + // link to connecting internal node + tempnodes[TempListLength].link1 = TempExternalNodes[i].next; + if(type == PATH_CAR){ + tempnodes[TempListLength].numLeftLanes = TempExternalNodes[i].numLeftLanes; + tempnodes[TempListLength].numRightLanes = TempExternalNodes[i].numRightLanes; + } + tempnodes[TempListLength].width = TempExternalNodes[i].width; + tempnodes[TempListLength].isCross = TempExternalNodes[i].isCross; + tempnodes[TempListLength++].linkState = 1; + }else{ + // Found nearest, connect it to our neighbour + tempnodes[nearestId].link2 = TempExternalNodes[i].next; + tempnodes[nearestId].linkState = 2; + + // collapse this node with nearest we found + dx = m_pathNodes[tempnodes[nearestId].link1].GetX() - m_pathNodes[tempnodes[nearestId].link2].GetX(); + dy = m_pathNodes[tempnodes[nearestId].link1].GetY() - m_pathNodes[tempnodes[nearestId].link2].GetY(); + tempnodes[nearestId].pos = (tempnodes[nearestId].pos + TempExternalNodes[i].pos)*0.5f; + mag = Sqrt(dx*dx + dy*dy); + tempnodes[nearestId].dirX = dx/mag * 100; + tempnodes[nearestId].dirY = dy/mag * 100; + tempnodes[nearestId].width = Max(tempnodes[nearestId].width, TempExternalNodes[i].width); + if(TempExternalNodes[i].isCross) + tempnodes[nearestId].isCross = true; // TODO: is this guaranteed to be false otherwise? + // do something when number of lanes doesn't agree + if(type == PATH_CAR) + if(tempnodes[nearestId].numLeftLanes != 0 && tempnodes[nearestId].numRightLanes != 0 && + (TempExternalNodes[i].numLeftLanes == 0 || TempExternalNodes[i].numRightLanes == 0)){ + // why switch left and right here? + tempnodes[nearestId].numLeftLanes = TempExternalNodes[i].numRightLanes; + tempnodes[nearestId].numRightLanes = TempExternalNodes[i].numLeftLanes; + } + } + } + + // Loop through previously added internal nodes and link them + for(i = oldNumPathNodes; i < m_numPathNodes; i++){ + // Init link + m_pathNodes[i].numLinks = 0; + m_pathNodes[i].firstLink = m_numConnections; + + // See if node connects to external nodes + for(j = 0; j < TempListLength; j++){ + if(tempnodes[j].linkState != 2) + continue; + + // Add link to other side of the external + // NB this clears the flags in MIAMI + if(tempnodes[j].link1 == i) + m_connections[m_numConnections] = tempnodes[j].link2; + else if(tempnodes[j].link2 == i) + m_connections[m_numConnections] = tempnodes[j].link1; + else + continue; + + dist = (m_pathNodes[i].GetPosition() - m_pathNodes[ConnectedNode(m_numConnections)].GetPosition()).Magnitude(); + m_distances[m_numConnections] = Min(dist, 255); + if(tempnodes[j].isCross) + m_connections[j] |= 0x8000; // crosses road flag + + if(type == PATH_CAR){ + // IMPROVE: use a goto here + // Find existing car path link + for(k = 0; k < m_numCarPathLinks; k++){ + if(m_carPathLinks[k].dirX == tempnodes[j].dirX && + m_carPathLinks[k].dirY == tempnodes[j].dirY && + m_carPathLinks[k].x == (int)(tempnodes[j].pos.x*8.0f) && + m_carPathLinks[k].y == (int)(tempnodes[j].pos.y*8.0f)){ + m_carPathConnections[m_numConnections] = k; + k = m_numCarPathLinks; + } + } + // k is m_numCarPathLinks+1 if we found one + if(k == m_numCarPathLinks){ + m_carPathLinks[m_numCarPathLinks].dirX = tempnodes[j].dirX; + m_carPathLinks[m_numCarPathLinks].dirY = tempnodes[j].dirY; + m_carPathLinks[m_numCarPathLinks].x = tempnodes[j].pos.x*8.0f; + m_carPathLinks[m_numCarPathLinks].y = tempnodes[j].pos.y*8.0f; + m_carPathLinks[m_numCarPathLinks].trafficLightDirection = false; + m_carPathLinks[m_numCarPathLinks].width = tempnodes[j].width; + m_carPathLinks[m_numCarPathLinks].pathNodeIndex = i; + m_carPathLinks[m_numCarPathLinks].numLeftLanes = tempnodes[j].numLeftLanes; + m_carPathLinks[m_numCarPathLinks].numRightLanes = tempnodes[j].numRightLanes; + m_carPathLinks[m_numCarPathLinks].trafficLightType = 0; + assert(m_numCarPathLinks <= NUM_CARPATHLINKS); + m_carPathConnections[m_numConnections] = m_numCarPathLinks++; + } + } + + m_pathNodes[i].numLinks++; + m_numConnections++; + } + + CPathInfoForObject *tile; + if(mapObjIndices[i] < 0){ + if(type == PATH_CAR) + tile = &DetachedInfoForTileCars[12 * (-1 - mapObjIndices[i])]; + else + tile = &DetachedInfoForTilePeds[12 * (-1 - mapObjIndices[i])]; + }else{ + if(type == PATH_CAR) + tile = &InfoForTileCars[12 * m_mapObjects[mapObjIndices[i]]->GetModelIndex()]; + else + tile = &InfoForTilePeds[12 * m_mapObjects[mapObjIndices[i]]->GetModelIndex()]; + } + + // Find i inside path segment + iseg = 0; + for(j = Max(oldNumPathNodes, i-12); j < i; j++) + if(OBJECTINDEX(j) == OBJECTINDEX(i)) + iseg++; + + // Add links to other internal nodes + for(j = Max(oldNumPathNodes, i-12); j < Min(m_numPathNodes, i+12); j++){ + if(OBJECTINDEX(i) != OBJECTINDEX(j) || i == j) + continue; + // N.B.: in every path segment, the externals have to be at the end + jseg = j-i + iseg; + + if(tile[iseg].next == jseg || + tile[jseg].next == iseg){ + // Found a link between i and jConnectionSetCrossesRoad + // NB this clears the flags in MIAMI + m_connections[m_numConnections] = j; + dist = (m_pathNodes[i].GetPosition() - m_pathNodes[j].GetPosition()).Magnitude(); + m_distances[m_numConnections] = Min(dist, 255); + + if(type == PATH_CAR){ + posx = (m_pathNodes[i].GetX() + m_pathNodes[j].GetX())*0.5f; + posy = (m_pathNodes[i].GetY() + m_pathNodes[j].GetY())*0.5f; + dx = m_pathNodes[j].GetX() - m_pathNodes[i].GetX(); + dy = m_pathNodes[j].GetY() - m_pathNodes[i].GetY(); + mag = Sqrt(dx*dx + dy*dy); + dx /= mag; + dy /= mag; + uint8 width = Max(m_pathNodes[i].width, m_pathNodes[j].width); + if(i < j){ + dx = -dx; + dy = -dy; + } + // IMPROVE: use a goto here + // Find existing car path link + for(k = 0; k < m_numCarPathLinks; k++){ + if(m_carPathLinks[k].dirX == (int)(dx*100.0f) && + m_carPathLinks[k].dirY == (int)(dy*100.0f) && + m_carPathLinks[k].x == (int)(posx*8.0f) && + m_carPathLinks[k].y == (int)(posy*8.0f)){ + m_carPathConnections[m_numConnections] = k; + k = m_numCarPathLinks; + } + } + // k is m_numCarPathLinks+1 if we found one + if(k == m_numCarPathLinks){ + m_carPathLinks[m_numCarPathLinks].dirX = dx*100.0f; + m_carPathLinks[m_numCarPathLinks].dirY = dy*100.0f; + m_carPathLinks[m_numCarPathLinks].x = posx*8.0f; + m_carPathLinks[m_numCarPathLinks].y = posy*8.0f; + m_carPathLinks[m_numCarPathLinks].trafficLightDirection = false; + m_carPathLinks[m_numCarPathLinks].width = width; + m_carPathLinks[m_numCarPathLinks].pathNodeIndex = i; + m_carPathLinks[m_numCarPathLinks].numLeftLanes = -1; + m_carPathLinks[m_numCarPathLinks].numRightLanes = -1; + m_carPathLinks[m_numCarPathLinks].trafficLightType = 0; + assert(m_numCarPathLinks <= NUM_CARPATHLINKS); + m_carPathConnections[m_numConnections] = m_numCarPathLinks++; + } + }else{ + // Crosses road + if(tile[iseg].next == jseg && tile[iseg].crossing || + tile[jseg].next == iseg && tile[jseg].crossing) + m_connections[m_numConnections] |= 0x8000; // crosses road flag + } + + m_pathNodes[i].numLinks++; + m_numConnections++; + } + } + } + + if(type == PATH_CAR){ + done = 0; + // Set number of lanes for all nodes somehow + // very strange code + for(k = 0; !done && k < 12; k++){ + done = 1; + for(i = 0; i < m_numPathNodes; i++){ + if(m_pathNodes[i].numLinks != 2) + continue; + l1 = m_carPathConnections[m_pathNodes[i].firstLink]; + l2 = m_carPathConnections[m_pathNodes[i].firstLink+1]; + + int8 l1Left = m_carPathLinks[l1].numLeftLanes; + int8 l1Right = m_carPathLinks[l1].numRightLanes; + int8 l2Left = m_carPathLinks[l2].numLeftLanes; + int8 l2Right = m_carPathLinks[l2].numRightLanes; + int8 *l1Leftp, *l1Rightp; + int8 *l2Leftp, *l2Rightp; + if(m_carPathLinks[l1].pathNodeIndex == i){ + l1Leftp = &l1Left; + l1Rightp = &l1Right; + }else{ + l1Leftp = &l1Right; + l1Rightp = &l1Left; + } + if(m_carPathLinks[l2].pathNodeIndex == i){ + l2Leftp = &l2Left; + l2Rightp = &l2Right; + }else{ + l2Leftp = &l2Right; + l2Rightp = &l2Left; + } + if(*l1Leftp == -1 && *l2Rightp != -1){ + *l1Leftp = *l2Rightp; + done = 0; + } + if(*l1Rightp == -1 && *l2Leftp != -1){ + *l1Rightp = *l2Leftp; + done = 0; + } + if(*l2Leftp == -1 && *l1Rightp != -1){ + *l2Leftp = *l1Rightp; + done = 0; + } + if(*l2Rightp == -1 && *l1Leftp != -1){ + *l2Rightp = *l1Leftp; + done = 0; + } + if(*l1Leftp == -1 && *l2Rightp == -1) + done = 0; + if(*l2Leftp == -1 && *l1Rightp == -1) + done = 0; + m_carPathLinks[l1].numLeftLanes = l1Left; + m_carPathLinks[l1].numRightLanes = l1Right; + m_carPathLinks[l2].numLeftLanes = l2Left; + m_carPathLinks[l2].numRightLanes = l2Right; + } + } + + // Fall back to default values for number of lanes + for(i = 0; i < m_numPathNodes; i++) + for(j = 0; j < m_pathNodes[i].numLinks; j++){ + k = m_carPathConnections[m_pathNodes[i].firstLink + j]; + if(m_carPathLinks[k].numLeftLanes == -1) + m_carPathLinks[k].numLeftLanes = 0; + if(m_carPathLinks[k].numRightLanes == -1) + m_carPathLinks[k].numRightLanes = 0; + } + } + + // Set flags for car nodes + if(type == PATH_CAR){ + do{ + cont = 0; + for(i = 0; i < m_numPathNodes; i++){ + // See if node is a dead end, if so, we're not done yet + if(!m_pathNodes[i].bDeadEnd){ + k = 0; + for(j = 0; j < m_pathNodes[i].numLinks; j++) + if(!m_pathNodes[ConnectedNode(m_pathNodes[i].firstLink + j)].bDeadEnd) + k++; + if(k < 2){ + m_pathNodes[i].bDeadEnd = true; + cont = 1; + } + } + } + }while(cont); + } + + // Remove isolated ped nodes + if(type == PATH_PED) + for(i = oldNumPathNodes; i < m_numPathNodes; i++){ + if(m_pathNodes[i].numLinks != 0) + continue; + + // Remove node + for(j = i; j < m_numPathNodes-1; j++) + m_pathNodes[j] = m_pathNodes[j+1]; + + // Fix links + for(j = oldNumLinks; j < m_numConnections; j++){ + int node = ConnectedNode(j); + if(node >= i) + m_connections[j] = node-1; + } + + i--; + m_numPathNodes--; + } + + delete[] mapObjIndices; +} + +float +CPathFind::CalcRoadDensity(float x, float y) +{ + int i, j; + float density = 0.0f; + + for(i = 0; i < m_numCarPathNodes; i++){ + if(Abs(m_pathNodes[i].GetX() - x) < 80.0f && + Abs(m_pathNodes[i].GetY() - y) < 80.0f && + m_pathNodes[i].numLinks > 0){ + for(j = 0; j < m_pathNodes[i].numLinks; j++){ + int next = ConnectedNode(m_pathNodes[i].firstLink + j); + float dist = (m_pathNodes[i].GetPosition() - m_pathNodes[next].GetPosition()).Magnitude2D(); + next = m_carPathConnections[m_pathNodes[i].firstLink + j]; + density += m_carPathLinks[next].numLeftLanes * dist; + density += m_carPathLinks[next].numRightLanes * dist; + } + } + } + return density/2500.0f; +} + +bool +CPathFind::TestForPedTrafficLight(CPathNode *n1, CPathNode *n2) +{ + int i; + for(i = 0; i < n1->numLinks; i++) + if(&m_pathNodes[ConnectedNode(n1->firstLink + i)] == n2) + return ConnectionHasTrafficLight(n1->firstLink + i); + return false; +} + +bool +CPathFind::TestCrossesRoad(CPathNode *n1, CPathNode *n2) +{ + int i; + for(i = 0; i < n1->numLinks; i++) + if(&m_pathNodes[ConnectedNode(n1->firstLink + i)] == n2) + return ConnectionCrossesRoad(n1->firstLink + i); + return false; +} + +void +CPathFind::AddNodeToList(CPathNode *node, int32 listId) +{ + int i = listId & 0x1FF; + node->SetNext(m_searchNodes[i].GetNext()); + node->SetPrev(&m_searchNodes[i]); + if(m_searchNodes[i].GetNext()) + m_searchNodes[i].GetNext()->SetPrev(node); + m_searchNodes[i].SetNext(node); + node->distance = listId; +} + +void +CPathFind::RemoveNodeFromList(CPathNode *node) +{ + node->GetPrev()->SetNext(node->GetNext()); + if(node->GetNext()) + node->GetNext()->SetPrev(node->GetPrev()); +} + +void +CPathFind::RemoveBadStartNode(CVector pos, CPathNode **nodes, int16 *n) +{ + int i; + if(*n < 2) + return; + if(DotProduct2D(nodes[1]->GetPosition() - pos, nodes[0]->GetPosition() - pos) < 0.0f){ + (*n)--; + for(i = 0; i < *n; i++) + nodes[i] = nodes[i+1]; + } +} + +#ifdef GTA_BRIDGE +void +CPathFind::SetLinksBridgeLights(float x1, float x2, float y1, float y2, bool enable) +{ + int i; + for(i = 0; i < m_numCarPathLinks; i++){ + CVector2D pos = m_carPathLinks[i].GetPosition(); + if(x1 < pos.x && pos.x < x2 && + y1 < pos.y && pos.y < y2) + m_carPathLinks[i].bBridgeLights = enable; + } +} +#endif + +void +CPathFind::SwitchOffNodeAndNeighbours(int32 nodeId, bool disable) +{ + int i, next; + + m_pathNodes[nodeId].bDisabled = disable; + if(m_pathNodes[nodeId].numLinks < 3) + for(i = 0; i < m_pathNodes[nodeId].numLinks; i++){ + next = ConnectedNode(m_pathNodes[nodeId].firstLink + i); + if(m_pathNodes[next].bDisabled != disable && + m_pathNodes[next].numLinks < 3) + SwitchOffNodeAndNeighbours(next, disable); + } +} + +void +CPathFind::SwitchRoadsOffInArea(float x1, float x2, float y1, float y2, float z1, float z2, bool disable) +{ + int i; + + for(i = 0; i < m_numCarPathNodes; i++){ + CVector pos = m_pathNodes[i].GetPosition(); + if(x1 <= pos.x && pos.x <= x2 && + y1 <= pos.y && pos.y <= y2 && + z1 <= pos.z && pos.z <= z2 && + disable != m_pathNodes[i].bDisabled) + SwitchOffNodeAndNeighbours(i, disable); + } +} + +void +CPathFind::SwitchPedRoadsOffInArea(float x1, float x2, float y1, float y2, float z1, float z2, bool disable) +{ + int i; + + for(i = m_numCarPathNodes; i < m_numPathNodes; i++){ + CVector pos = m_pathNodes[i].GetPosition(); + if(x1 <= pos.x && pos.x <= x2 && + y1 <= pos.y && pos.y <= y2 && + z1 <= pos.z && pos.z <= z2 && + disable != m_pathNodes[i].bDisabled) + SwitchOffNodeAndNeighbours(i, disable); + } +} + +void +CPathFind::SwitchRoadsInAngledArea(float x1, float y1, float z1, float x2, float y2, float z2, float length, uint8 type, uint8 mode) +{ + int i; + int firstNode, lastNode; + + // this is NOT PATH_CAR + if(type != 0){ + firstNode = 0; + lastNode = m_numCarPathNodes; + }else{ + firstNode = m_numCarPathNodes; + lastNode = m_numPathNodes; + } + + if(z1 > z2){ + float tmp = z2; + z2 = z1; + z1 = tmp; + } + + // angle of vector from p2 to p1 + float angle = CGeneral::GetRadianAngleBetweenPoints(x1, y1, x2, y2) + HALFPI; + while(angle < 0.0f) angle += TWOPI; + while(angle > TWOPI) angle -= TWOPI; + // vector from p1 to p2 + CVector2D v12(x2 - x1, y2 - y1); + float len12 = v12.Magnitude(); + v12 /= len12; + + // vector from p2 to new point p3 + CVector2D v23(Sin(angle)*length, -(Cos(angle)*length)); + v23 /= v23.Magnitude(); // obivously just 'length' but whatever + + bool disable = mode == SWITCH_OFF; + for(i = firstNode; i < lastNode; i++){ + CVector pos = m_pathNodes[i].GetPosition(); + if(pos.z < z1 || pos.z > z2) + continue; + CVector2D d(pos.x - x1, pos.y - y1); + float dot = DotProduct2D(d, v12); + if(dot < 0.0f || dot > len12) + continue; + dot = DotProduct2D(d, v23); + if(dot < 0.0f || dot > length) + continue; + if(m_pathNodes[i].bDisabled != disable) + SwitchOffNodeAndNeighbours(i, disable); + } +} + +void +CPathFind::MarkRoadsBetweenLevelsNodeAndNeighbours(int32 nodeId) +{ + int i, next; + + m_pathNodes[nodeId].bBetweenLevels = true; + if(m_pathNodes[nodeId].numLinks < 3) + for(i = 0; i < m_pathNodes[nodeId].numLinks; i++){ + next = ConnectedNode(m_pathNodes[nodeId].firstLink + i); + if(!m_pathNodes[next].bBetweenLevels && + m_pathNodes[next].numLinks < 3) + MarkRoadsBetweenLevelsNodeAndNeighbours(next); + } +} + +void +CPathFind::MarkRoadsBetweenLevelsInArea(float x1, float x2, float y1, float y2, float z1, float z2) +{ + int i; + + for(i = 0; i < m_numPathNodes; i++){ + CVector pos = m_pathNodes[i].GetPosition(); + if(x1 < pos.x && pos.x < x2 && + y1 < pos.y && pos.y < y2 && + z1 < pos.z && pos.z < z2) + MarkRoadsBetweenLevelsNodeAndNeighbours(i); + } +} + +void +CPathFind::PedMarkRoadsBetweenLevelsInArea(float x1, float x2, float y1, float y2, float z1, float z2) +{ + int i; + + for(i = m_numCarPathNodes; i < m_numPathNodes; i++){ + CVector pos = m_pathNodes[i].GetPosition(); + if(x1 < pos.x && pos.x < x2 && + y1 < pos.y && pos.y < y2 && + z1 < pos.z && pos.z < z2) + MarkRoadsBetweenLevelsNodeAndNeighbours(i); + } +} + +int32 +CPathFind::FindNodeClosestToCoors(CVector coors, uint8 type, float distLimit, bool ignoreDisabled, bool ignoreBetweenLevels, bool ignoreSelected, bool bWaterPath) +{ + int i; + int firstNode, lastNode; + float dist; + float closestDist = 10000.0f; + int closestNode = 0; + + switch(type){ + case PATH_CAR: + firstNode = 0; + lastNode = m_numCarPathNodes; + break; + case PATH_PED: + firstNode = m_numCarPathNodes; + lastNode = m_numPathNodes; + break; + } + + for(i = firstNode; i < lastNode; i++){ + if(ignoreDisabled && m_pathNodes[i].bDisabled) continue; + if(ignoreBetweenLevels && m_pathNodes[i].bBetweenLevels) continue; + if(ignoreSelected && m_pathNodes[i].bSelected) continue; + if(bWaterPath != m_pathNodes[i].bWaterPath) continue; + dist = Abs(m_pathNodes[i].GetX() - coors.x) + + Abs(m_pathNodes[i].GetY() - coors.y) + + 3.0f*Abs(m_pathNodes[i].GetZ() - coors.z); + if(dist < closestDist){ + closestDist = dist; + closestNode = i; + } + } + return closestDist < distLimit ? closestNode : -1; +} + +int32 +CPathFind::FindNodeClosestToCoorsFavourDirection(CVector coors, uint8 type, float dirX, float dirY) +{ + int i; + int firstNode, lastNode; + float dist, dX, dY; + NormalizeXY(dirX, dirY); + float closestDist = 10000.0f; + int closestNode = 0; + + switch(type){ + case PATH_CAR: + firstNode = 0; + lastNode = m_numCarPathNodes; + break; + case PATH_PED: + firstNode = m_numCarPathNodes; + lastNode = m_numPathNodes; + break; + } + + for(i = firstNode; i < lastNode; i++){ + dX = m_pathNodes[i].GetX() - coors.x; + dY = m_pathNodes[i].GetY() - coors.y; + dist = Abs(dX) + Abs(dY) + + 3.0f*Abs(m_pathNodes[i].GetZ() - coors.z); + if(dist < closestDist){ + NormalizeXY(dX, dY); + dist -= (dX*dirX + dY*dirY - 1.0f)*20.0f; + if(dist < closestDist){ + closestDist = dist; + closestNode = i; + } + } + } + return closestNode; +} + +void +CPathFind::FindNodePairClosestToCoors(CVector coors, uint8 type, int* node1, int* node2, float* angle, float minDist, float maxDist, bool ignoreDisabled, bool ignoreBetweenLevels, bool bWaterPath) +{ + int i, j; + int firstNode, lastNode, connectedNode; + float dist; + float closestDist = 10000.0f; + int closestNode = 0, closestConnectedNode = 0; + + switch (type) { + case PATH_CAR: + firstNode = 0; + lastNode = m_numCarPathNodes; + break; + case PATH_PED: + firstNode = m_numCarPathNodes; + lastNode = m_numPathNodes; + break; + } + + for (i = firstNode; i < lastNode; i++) { + if (ignoreDisabled && m_pathNodes[i].bDisabled) continue; + if (ignoreBetweenLevels && m_pathNodes[i].bBetweenLevels) continue; + if (bWaterPath != m_pathNodes[i].bWaterPath) continue; + dist = Abs(m_pathNodes[i].GetX() - coors.x) + + Abs(m_pathNodes[i].GetY() - coors.y) + + 3.0f * Abs(m_pathNodes[i].GetZ() - coors.z); + if (dist < closestDist) { + for (j = 0; j < m_pathNodes[i].numLinks; j++) { + connectedNode = ConnectedNode(m_pathNodes[i].firstLink + j); + if (ignoreDisabled && m_pathNodes[connectedNode].bDisabled) continue; + if (ignoreBetweenLevels && m_pathNodes[connectedNode].bBetweenLevels) continue; + if (bWaterPath != m_pathNodes[connectedNode].bWaterPath) continue; + if ((m_pathNodes[connectedNode].GetPosition() - m_pathNodes[i].GetPosition()).Magnitude() > minDist) { + closestDist = dist; + closestNode = i; + closestConnectedNode = connectedNode; + } + } + } + } + if (closestDist < maxDist) { + *node1 = closestNode; + *node2 = closestConnectedNode; + CVector dir(m_pathNodes[*node2].GetX() - m_pathNodes[*node1].GetX(), m_pathNodes[*node2].GetY() - m_pathNodes[*node1].GetY(), 0.0f); + dir.Normalise(); + *angle = RADTODEG(Atan2(-dir.x, dir.y)); + } + else { + *node1 = -1; + *node2 = -1; + *angle = 0.0f; + } +} + +int32 +CPathFind::FindNthNodeClosestToCoors(CVector coors, uint8 type, float distLimit, bool ignoreDisabled, bool ignoreBetweenLevels, int N, bool bWaterPath) +{ + int i; + int firstNode, lastNode; + switch (type) { + case PATH_CAR: + firstNode = 0; + lastNode = m_numCarPathNodes; + break; + case PATH_PED: + firstNode = m_numCarPathNodes; + lastNode = m_numPathNodes; + break; + } + for (i = firstNode; i < lastNode; i++) + m_pathNodes[i].bSelected = false; + + for (; N > 0; N--) { + i = FindNodeClosestToCoors(coors, type, distLimit, ignoreDisabled, ignoreBetweenLevels, true, bWaterPath); + if (i < 0) + return -1; + m_pathNodes[i].bSelected = true; + } + return FindNodeClosestToCoors(coors, type, distLimit, ignoreDisabled, ignoreBetweenLevels, true, bWaterPath); +} + +CVector +CPathFind::FindNodeCoorsForScript(int32 id) +{ + // the point is to return valid position in case there is a divider in the middle of the road + if (!m_pathNodes[id].HasDivider() || m_pathNodes[id].numLinks == 0) + return m_pathNodes[id].GetPosition(); + CVector2D dir(m_pathNodes[ConnectedNode(m_pathNodes[id].firstLink)].GetX() - m_pathNodes[id].GetX(), + m_pathNodes[ConnectedNode(m_pathNodes[id].firstLink)].GetY() - m_pathNodes[id].GetY()); + dir.Normalise(); + if (dir.x < 0) + dir = -dir; + return m_pathNodes[id].GetPosition() + CVector(-dir.y, dir.x, 0.0f) * (LANE_WIDTH / 2 + m_pathNodes[id].GetDividerWidth()); +} + +float +CPathFind::FindNodeOrientationForCarPlacement(int32 nodeId) +{ + if(m_pathNodes[nodeId].numLinks == 0) + return 0.0f; + CVector dir = m_pathNodes[ConnectedNode(m_pathNodes[nodeId].firstLink)].GetPosition() - m_pathNodes[nodeId].GetPosition(); + dir.z = 0.0f; + dir.Normalise(); + return RADTODEG(dir.Heading()); +} + +float +CPathFind::FindNodeOrientationForCarPlacementFacingDestination(int32 nodeId, float x, float y, bool towards) +{ + int i; + + CVector targetDir(x - m_pathNodes[nodeId].GetX(), y - m_pathNodes[nodeId].GetY(), 0.0f); + targetDir.Normalise(); + CVector dir; + + if(m_pathNodes[nodeId].numLinks == 0) + return 0.0f; + + int bestNode = ConnectedNode(m_pathNodes[nodeId].firstLink); +#ifdef FIX_BUGS + float bestDot = towards ? -2.0f : 2.0f; +#else + int bestDot = towards ? -2 : 2; // why int? +#endif + + for(i = 0; i < m_pathNodes[nodeId].numLinks; i++){ + dir = m_pathNodes[ConnectedNode(m_pathNodes[nodeId].firstLink + i)].GetPosition() - m_pathNodes[nodeId].GetPosition(); + dir.z = 0.0f; + dir.Normalise(); + float angle = DotProduct2D(dir, targetDir); + if(towards){ + if(angle > bestDot){ + bestDot = angle; + bestNode = ConnectedNode(m_pathNodes[nodeId].firstLink + i); + } + }else{ + if(angle < bestDot){ + bestDot = angle; + bestNode = ConnectedNode(m_pathNodes[nodeId].firstLink + i); + } + } + } + + dir = m_pathNodes[bestNode].GetPosition() - m_pathNodes[nodeId].GetPosition(); + dir.z = 0.0f; + dir.Normalise(); + return RADTODEG(dir.Heading()); +} + +bool +CPathFind::GenerateCarCreationCoors(float x, float y, float dirX, float dirY, float spawnDist, float angleLimit, bool forward, CVector *pPosition, int32 *pNode1, int32 *pNode2, float *pPositionBetweenNodes, bool ignoreDisabled) +{ + int i, j; + int node1, node2; + float dist1, dist2, d1, d2; + + if(m_numCarPathNodes == 0) + return false; + + for(i = 0; i < 500; i++){ + node1 = (CGeneral::GetRandomNumber()>>3) % m_numCarPathNodes; + if(m_pathNodes[node1].bDisabled && !ignoreDisabled) + continue; + dist1 = Distance2D(m_pathNodes[node1].GetPosition(), x, y); + if(dist1 < Max(spawnDist + 70.0f, spawnDist * 1.7f)){ + d1 = m_pathNodes[node1].bWaterPath ? (dist1 - spawnDist * 1.5f) : (dist1 - spawnDist); + for(j = 0; j < m_pathNodes[node1].numLinks; j++){ + node2 = ConnectedNode(m_pathNodes[node1].firstLink + j); + if(m_pathNodes[node2].bDisabled && !ignoreDisabled) + continue; + dist2 = Distance2D(m_pathNodes[node2].GetPosition(), x, y); + d2 = m_pathNodes[node2].bWaterPath ? (dist2 - spawnDist * 1.5f) : (dist2 - spawnDist); + if(d1*d2 < 0.0f){ + // nodes are on different sides of spawn distance + float f2 = Abs(d1)/(Abs(d1) + Abs(d2)); + float f1 = 1.0f - f2; + *pPositionBetweenNodes = f2; + CVector pos = m_pathNodes[node1].GetPosition()*f1 + m_pathNodes[node2].GetPosition()*f2; + CVector2D dist2d(pos.x - x, pos.y - y); + dist2d.Normalise(); // done manually in the game + float dot = DotProduct2D(dist2d, CVector2D(dirX, dirY)); + if(forward){ + if(dot > angleLimit){ + *pNode1 = node1; + *pNode2 = node2; + *pPosition = pos; + return true; + } + }else{ + if(dot <= angleLimit){ + *pNode1 = node1; + *pNode2 = node2; + *pPosition = pos; + return true; + } + } + } + } + } + } + return false; +} + +bool +CPathFind::GeneratePedCreationCoors(float x, float y, float minDist, float maxDist, float minDistOffScreen, float maxDistOffScreen, CVector *pPosition, int32 *pNode1, int32 *pNode2, float *pPositionBetweenNodes, CMatrix *camMatrix) +{ + int i; + int node1, node2; + float node1_dist, node2_dist; + static int32 node_cnt; + + if(m_numPedPathNodes == 0) + return false; + + for(i = 0; i < 230; i++){ + if (node_cnt++ >= m_numPedPathNodes) + node_cnt = 0; + node1 = node_cnt + m_numCarPathNodes; + node1_dist = Distance2D(m_pathNodes[node1].GetPosition(), x, y); + if(node1_dist < maxDist+30.0f){ + if(m_pathNodes[node1].numLinks != 0) + break; + } + } + if (i >= 230) + return false; + + for(i = 0; i < m_pathNodes[node1].numLinks; i++){ + int link = m_pathNodes[node1].firstLink + i; + if(ConnectionCrossesRoad(link)) + continue; + node2 = ConnectedNode(link); + if(m_pathNodes[node1].bDisabled || m_pathNodes[node2].bDisabled) + continue; + node2_dist = Distance2D(m_pathNodes[node2].GetPosition(), x, y); + if ((node1_dist < maxDist || node2_dist < maxDist) && (node1_dist > minDistOffScreen || node2_dist > minDistOffScreen)) + break; + } + if(i >= m_pathNodes[node1].numLinks) + return false; + + for(i = 0; i < 5; i++){ + float f2 = (CGeneral::GetRandomNumber()&0xFF)/256.0f; + float f1 = 1.0f - f2; + *pPositionBetweenNodes = f2; + CVector pos = m_pathNodes[node1].GetPosition()*f1 + m_pathNodes[node2].GetPosition()*f2; + if(Distance2D(pos, x, y) < maxDist+20.0f){ + pos.x += ((CGeneral::GetRandomNumber()&0xFF)-128)*0.01f; + pos.y += ((CGeneral::GetRandomNumber()&0xFF)-128)*0.01f; + float dist = Distance2D(pos, x, y); + + bool visible; + if(camMatrix) + visible = TheCamera.IsSphereVisible(pos, 2.0f, camMatrix); + else + visible = TheCamera.IsSphereVisible(pos, 2.0f); + if(!visible){ + minDist = minDistOffScreen; + maxDist = maxDistOffScreen; + } + if(visible && (minDist < dist && dist < maxDist) || + !visible && (minDistOffScreen < dist && dist < maxDistOffScreen)){ + *pNode1 = node1; + *pNode2 = node2; + *pPosition = pos; + + bool found; + float groundZ = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z+2.0f, &found); + if(!found) + return false; + if(Abs(groundZ - pos.z) > 3.0f) + return false; + pPosition->z = groundZ; + return true; + } + } + } + return false; +} + +void +CPathFind::FindNextNodeWandering(uint8 type, CVector coors, CPathNode **lastNode, CPathNode **nextNode, uint8 curDir, uint8 *nextDir) +{ + int i; + CPathNode *node; + + if(lastNode == nil || (node = *lastNode) == nil || (coors - (*lastNode)->GetPosition()).MagnitudeSqr() > 7.0f){ + int32 nodeIdx = FindNodeClosestToCoors(coors, type, 999999.88f); + node = &m_pathNodes[nodeIdx]; + } + + CVector2D vCurDir(Sin(curDir*PI/4.0f), Cos(curDir * PI / 4.0f)); + *nextNode = 0; + float bestDot = -999999.0f; + for(i = 0; i < node->numLinks; i++){ + int next = ConnectedNode(node->firstLink+i); + if(!node->bDisabled && m_pathNodes[next].bDisabled) + continue; + CVector pedCoors = coors; + pedCoors.z += 1.0f; + CVector nodeCoors = m_pathNodes[next].GetPosition(); + nodeCoors.z += 1.0f; + if(!CWorld::GetIsLineOfSightClear(pedCoors, nodeCoors, true, false, false, false, false, false)) + continue; + CVector2D nodeDir = m_pathNodes[next].GetPosition() - node->GetPosition(); + nodeDir.Normalise(); + float dot = DotProduct2D(nodeDir, vCurDir); + if(dot >= bestDot){ + *nextNode = &m_pathNodes[next]; + bestDot = dot; + + // direction is 0, 2, 4, 6 for north, east, south, west + // this could be done simpler... + if(nodeDir.x < 0.0f){ + if(2.0f*Abs(nodeDir.y) < -nodeDir.x) + *nextDir = 6; // west + else if(-2.0f*nodeDir.x < nodeDir.y) + *nextDir = 0; // north + else if(2.0f*nodeDir.x > nodeDir.y) + *nextDir = 4; // south + else if(nodeDir.y > 0.0f) + *nextDir = 7; // north west + else + *nextDir = 5; // south west` + }else{ + if(2.0f*Abs(nodeDir.y) < nodeDir.x) + *nextDir = 2; // east + else if(2.0f*nodeDir.x < nodeDir.y) + *nextDir = 0; // north + else if(-2.0f*nodeDir.x > nodeDir.y) + *nextDir = 4; // south + else if(nodeDir.y > 0.0f) + *nextDir = 1; // north east + else + *nextDir = 3; // south east` + } + } + } + if(*nextNode == nil){ + *nextDir = 0; + *nextNode = node; + } +} + +static CPathNode *apNodesToBeCleared[6525]; + +void +CPathFind::DoPathSearch(uint8 type, CVector start, int32 startNodeId, CVector target, CPathNode **nodes, int16 *pNumNodes, int16 maxNumNodes, CVehicle *vehicle, float *pDist, float distLimit, int32 targetNodeId) +{ + int i, j; + + // Find target + if(targetNodeId < 0) + targetNodeId = FindNodeClosestToCoors(target, type, distLimit); + if(targetNodeId < 0) { + *pNumNodes = 0; + if(pDist) *pDist = 100000.0f; + return; + } + + // Find start + if(startNodeId < 0) + startNodeId = FindNodeClosestToCoors(start, type, 999999.88f); + if(startNodeId < 0) { + *pNumNodes = 0; + if(pDist) *pDist = 100000.0f; + return; + } + if(startNodeId == targetNodeId){ + *pNumNodes = 0; + if(pDist) *pDist = 0.0f; + return; + } + if(m_pathNodes[startNodeId].group != m_pathNodes[targetNodeId].group) { + *pNumNodes = 0; + if(pDist) *pDist = 100000.0f; + return; + } + + for(i = 0; i < ARRAY_SIZE(m_searchNodes); i++) + m_searchNodes[i].SetNext(nil); + AddNodeToList(&m_pathNodes[targetNodeId], 0); + int numNodesToBeCleared = 0; + apNodesToBeCleared[numNodesToBeCleared++] = &m_pathNodes[targetNodeId]; + + // Dijkstra's algorithm + // Find distances + int numPathsFound = 0; + for(i = 0; numPathsFound == 0; i = (i+1) & 0x1FF){ + CPathNode *node; + for(node = m_searchNodes[i].GetNext(); node; node = node->GetNext()){ + if(node == &m_pathNodes[startNodeId]) + numPathsFound = 1; + + for(j = 0; j < node->numLinks; j++){ + int next = ConnectedNode(node->firstLink + j); + int dist = node->distance + m_distances[node->firstLink + j]; + if(dist < m_pathNodes[next].distance){ + if(m_pathNodes[next].distance != MAX_DIST) + RemoveNodeFromList(&m_pathNodes[next]); + if(m_pathNodes[next].distance == MAX_DIST) + apNodesToBeCleared[numNodesToBeCleared++] = &m_pathNodes[next]; + AddNodeToList(&m_pathNodes[next], dist); + } + } + + RemoveNodeFromList(node); + } + } + + // Find out whence to start tracing back + CPathNode *curNode; + curNode = &m_pathNodes[startNodeId]; + *pNumNodes = 0; + if(pDist) + *pDist = m_pathNodes[startNodeId].distance; + + nodes[(*pNumNodes)++] = curNode; + // Trace back to target and update list of nodes + while(*pNumNodes < maxNumNodes && curNode != &m_pathNodes[targetNodeId]) + for(i = 0; i < curNode->numLinks; i++){ + int next = ConnectedNode(curNode->firstLink + i); + if(curNode->distance - m_distances[curNode->firstLink + i] == m_pathNodes[next].distance){ + curNode = &m_pathNodes[next]; + nodes[(*pNumNodes)++] = curNode; + i = 29030; // could have used a break... + } + } + + for(i = 0; i < numNodesToBeCleared; i++) + apNodesToBeCleared[i]->distance = MAX_DIST; +} + +static CPathNode *pNodeList[32]; +static int16 DummyResult; +static int16 DummyResult2; + +bool +CPathFind::TestCoorsCloseness(CVector target, uint8 type, CVector start) +{ + float dist; + + if(type == PATH_CAR) + DoPathSearch(type, start, -1, target, pNodeList, &DummyResult, 32, nil, &dist, 999999.88f, -1); + else + DoPathSearch(type, start, -1, target, nil, &DummyResult2, 0, nil, &dist, 50.0f, -1); +#ifdef FIX_BUGS + // dist has GenerationDistMultiplier as a factor, so our reference dist should have it too + if(type == PATH_CAR) + return dist < 150.0f*TheCamera.GenerationDistMultiplier; + else + return dist < 100.0f*TheCamera.GenerationDistMultiplier; +#else + if(type == PATH_CAR) + return dist < 150.0f; + else + return dist < 100.0f; +#endif +} + +void +CPathFind::Save(uint8 *buf, uint32 *size) +{ + int i; + int n = m_numPathNodes/8 + 1; + + *size = 2*n; + + for(i = 0; i < m_numPathNodes; i++) + if(m_pathNodes[i].bDisabled) + buf[i/8] |= 1 << i%8; + else + buf[i/8] &= ~(1 << i%8); + + for(i = 0; i < m_numPathNodes; i++) + if(m_pathNodes[i].bBetweenLevels) + buf[i/8 + n] |= 1 << i%8; + else + buf[i/8 + n] &= ~(1 << i%8); +} + +void +CPathFind::Load(uint8 *buf, uint32 size) +{ + int i; + int n = m_numPathNodes/8 + 1; + + for(i = 0; i < m_numPathNodes; i++) + if(buf[i/8] & (1 << i%8)) + m_pathNodes[i].bDisabled = true; + else + m_pathNodes[i].bDisabled = false; + + for(i = 0; i < m_numPathNodes; i++) + if(buf[i/8 + n] & (1 << i%8)) + m_pathNodes[i].bBetweenLevels = true; + else + m_pathNodes[i].bBetweenLevels = false; + +#ifdef SECUROM + // if pirated game + for(i = 0; i < m_numPathNodes; i++) + m_pathNodes[i].bDisabled = true; +#endif +} + +void +CPathFind::DisplayPathData(void) +{ + // Not the function from mobile but my own! + + int i, j, k; + // Draw 50 units around camera + CVector pos = TheCamera.GetPosition(); + const float maxDist = 50.0f; + + // Render car path nodes + if(gbShowCarPaths) + for(i = 0; i < m_numCarPathNodes; i++){ + if((m_pathNodes[i].GetPosition() - pos).MagnitudeSqr() > SQR(maxDist)) + continue; + + CVector n1 = m_pathNodes[i].GetPosition(); + n1.z += 0.3f; + + // Draw node itself + CLines::RenderLineWithClipping(n1.x, n1.y, n1.z, + n1.x, n1.y, n1.z + 1.0f, + 0xFFFFFFFF, 0xFFFFFFFF); + + for(j = 0; j < m_pathNodes[i].numLinks; j++){ + k = ConnectedNode(m_pathNodes[i].firstLink + j); + CVector n2 = m_pathNodes[k].GetPosition(); + n2.z += 0.3f; + // Draw links to neighbours + CLines::RenderLineWithClipping(n1.x, n1.y, n1.z, + n2.x, n2.y, n2.z, + 0xFFFFFFFF, 0xFFFFFFFF); + } + } + + // Render car path nodes + if(gbShowCarPathsLinks) + for(i = 0; i < m_numCarPathLinks; i++){ + CVector2D n1_2d = m_carPathLinks[i].GetPosition(); + if((n1_2d - pos).MagnitudeSqr() > SQR(maxDist)) + continue; + + int ni = m_carPathLinks[i].pathNodeIndex; + CVector pn1 = m_pathNodes[ni].GetPosition(); + pn1.z += 0.3f; + CVector n1(n1_2d.x, n1_2d.y, pn1.z); + n1.z += 0.3f; + + // Draw car node itself + CLines::RenderLineWithClipping(n1.x, n1.y, n1.z, + n1.x, n1.y, n1.z + 1.0f, + 0xFFFFFFFF, 0xFFFFFFFF); + CLines::RenderLineWithClipping(n1.x, n1.y, n1.z + 0.5f, + n1.x+m_carPathLinks[i].GetDirX(), n1.y+m_carPathLinks[i].GetDirY(), n1.z + 0.5f, + 0xFFFFFFFF, 0xFFFFFFFF); + + // Draw connection to car path node + CLines::RenderLineWithClipping(n1.x, n1.y, n1.z, + pn1.x, pn1.y, pn1.z, + 0xFF0000FF, 0xFFFFFFFF); + + // traffic light type + uint32 col = 0xFF; + if((m_carPathLinks[i].trafficLightType&0x7F) == 1) + col += 0xFF000000; + if((m_carPathLinks[i].trafficLightType&0x7F) == 2) + col += 0x00FF0000; + if(m_carPathLinks[i].trafficLightType & 0x80) + col += 0x0000FF00; + CLines::RenderLineWithClipping(n1.x+0.2f, n1.y, n1.z, + n1.x+0.2f, n1.y, n1.z + 1.0f, + col, col); + + for(j = 0; j < m_pathNodes[ni].numLinks; j++){ + k = m_carPathConnections[m_pathNodes[ni].firstLink + j]; + CVector2D n2_2d = m_carPathLinks[k].GetPosition(); + int nk = m_carPathLinks[k].pathNodeIndex; + CVector pn2 = m_pathNodes[nk].GetPosition(); + pn2.z += 0.3f; + CVector n2(n2_2d.x, n2_2d.y, pn2.z); + n2.z += 0.3f; + + // Draw links to neighbours + CLines::RenderLineWithClipping(n1.x, n1.y, n1.z, + n2.x, n2.y, n2.z, + 0xFF00FFFF, 0xFF00FFFF); + } + } + + // Render ped path nodes + if(gbShowPedPaths) + for(i = m_numCarPathNodes; i < m_numPathNodes; i++){ + if((m_pathNodes[i].GetPosition() - pos).MagnitudeSqr() > SQR(maxDist)) + continue; + + CVector n1 = m_pathNodes[i].GetPosition(); + n1.z += 0.3f; + + // Draw node itself + CLines::RenderLineWithClipping(n1.x, n1.y, n1.z, + n1.x, n1.y, n1.z + 1.0f, + 0xFFFFFFFF, 0xFFFFFFFF); + + for(j = 0; j < m_pathNodes[i].numLinks; j++){ + k = ConnectedNode(m_pathNodes[i].firstLink + j); + CVector n2 = m_pathNodes[k].GetPosition(); + n2.z += 0.3f; + // Draw links to neighbours + CLines::RenderLineWithClipping(n1.x, n1.y, n1.z, + n2.x, n2.y, n2.z, + 0xFFFFFFFF, 0xFFFFFFFF); + + // Draw connection flags + CVector mid = (n1+n2)/2.0f; + uint32 col = 0xFF; + if(ConnectionCrossesRoad(m_pathNodes[i].firstLink + j)) + col += 0x00FF0000; + if(ConnectionHasTrafficLight(m_pathNodes[i].firstLink + j)) + col += 0xFF000000; + CLines::RenderLineWithClipping(mid.x, mid.y, mid.z, + mid.x, mid.y, mid.z + 1.0f, + col, col); + } + } +} + +CVector +CPathFind::TakeWidthIntoAccountForWandering(CPathNode* nextNode, uint16 random) +{ + CVector pos = nextNode->GetPosition(); + float newX = (nextNode->GetPedNodeWidth() * ((random % 16) - 7)) + pos.x; + float newY = (nextNode->GetPedNodeWidth() * (((random / 16) % 16) - 7)) + pos.y; + return CVector(newX, newY, pos.z); +} + +void +CPathFind::TakeWidthIntoAccountForCoors(CPathNode* node1, CPathNode* node2, uint16 random, float* x, float* y) +{ + *x += (Min(node1->width, node2->width) * WIDTH_TO_PED_NODE_WIDTH * ((random % 16) - 7)); + *y += (Min(node1->width, node2->width) * WIDTH_TO_PED_NODE_WIDTH * (((random / 16) % 16) - 7)); +} + +CPathNode* +CPathFind::GetNode(int16 index) +{ + if(index < 0) + return nil; + if(index < ARRAY_SIZE(ThePaths.m_searchNodes)) + return &ThePaths.m_searchNodes[index]; + return &ThePaths.m_pathNodes[index - ARRAY_SIZE(ThePaths.m_searchNodes)]; +} +int16 +CPathFind::GetIndex(CPathNode *node) +{ + if(node == nil) + return -1; + if(node >= &ThePaths.m_searchNodes[0] && node < &ThePaths.m_searchNodes[ARRAY_SIZE(ThePaths.m_searchNodes)]) + return node - ThePaths.m_searchNodes; + else + return (node - ThePaths.m_pathNodes) + ARRAY_SIZE(ThePaths.m_searchNodes); +} diff --git a/src/miami/control/PathFind.h b/src/miami/control/PathFind.h new file mode 100644 index 00000000..99759590 --- /dev/null +++ b/src/miami/control/PathFind.h @@ -0,0 +1,304 @@ +#pragma once + +#include "Treadable.h" + +class CVehicle; +class CPtrList; + +#define LANE_WIDTH 5.0f +#define WIDTH_TO_PED_NODE_WIDTH (31.f/(500.f * 8.f)) + +enum +{ + NodeTypeExtern = 1, + NodeTypeIntern = 2, +}; + +enum +{ + PATH_CAR = 0, + PATH_PED = 1, +}; + +enum +{ + SWITCH_OFF = 0, + SWITCH_ON = 1, +}; + +enum +{ + ROUTE_ADD_BLOCKADE = 0, + ROUTE_NO_BLOCKADE = 1 +}; + +struct CPedPathNode +{ + bool bBlockade; + uint8 nodeIdX; + uint8 nodeIdY; + int16 id; + CPedPathNode* prev; + CPedPathNode* next; +}; + +VALIDATE_SIZE(CPedPathNode, 0x10); + +class CPedPath { +public: + static bool CalcPedRoute(int8 pathType, CVector position, CVector destination, CVector *pointPoses, int16 *pointsFound, int16 maxPoints); + static void AddNodeToPathList(CPedPathNode *pNodeToAdd, int16 id, CPedPathNode *pNodeList); + static void RemoveNodeFromList(CPedPathNode *pNode); + static void AddNodeToList(CPedPathNode *pNode, int16 index, CPedPathNode *pList); + static void AddBlockade(CEntity *pEntity, CPedPathNode(*pathNodes)[40], CVector *pPosition); + static void AddBlockadeSectorList(CPtrList& list, CPedPathNode(*pathNodes)[40], CVector *pPosition); + static void AddBuildingBlockade(CEntity*, CPedPathNode(*)[40], CVector*); + static void AddBuildingBlockadeSectorList(CPtrList&, CPedPathNode(*)[40], CVector*); +}; + +struct CPathNode +{ + int16 prevIndex; + int16 nextIndex; + int16 x; + int16 y; + int16 z; + int16 distance; // in path search + int16 firstLink; + uint8 width; + int8 group; + + uint8 numLinks : 4; + uint8 bDeadEnd : 1; + uint8 bDisabled : 1; + uint8 bBetweenLevels : 1; + uint8 bUseInRoadBlock : 1; + + uint8 bWaterPath : 1; + uint8 bOnlySmallBoats : 1; + uint8 bSelected : 1; + uint8 speedLimit : 2; + //uint8 flagB20 : 1; + //uint8 flagB40 : 1; + //uint8 flagB80 : 1; + + uint8 spawnRate : 4; + uint8 flagsC : 4; + + CVector GetPosition(void) { return CVector(x/8.0f, y/8.0f, z/8.0f); } + void SetPosition(const CVector &p) { x = p.x*8.0f; y = p.y*8.0f; z = p.z*8.0f; } + float GetX(void) { return x/8.0f; } + float GetY(void) { return y/8.0f; } + float GetZ(void) { return z/8.0f; } + bool HasDivider(void) { return width != 0; } + float GetDividerWidth(void) { return width/(2*8.0f); } + float GetPedNodeWidth(void) { return width*WIDTH_TO_PED_NODE_WIDTH; } + CPathNode *GetPrev(void); + CPathNode *GetNext(void); + void SetPrev(CPathNode *node); + void SetNext(CPathNode *node); +}; + +union CConnectionFlags +{ + uint8 flags; + struct { + uint8 bCrossesRoad : 1; + uint8 bTrafficLight : 1; + }; +}; + +struct CCarPathLink +{ + int16 x; + int16 y; + int16 pathNodeIndex; + int8 dirX; + int8 dirY; + int8 numLeftLanes : 3; + int8 numRightLanes : 3; + uint8 trafficLightDirection : 1; + uint8 trafficLightType : 2; + uint8 bBridgeLights : 1; // at least in LCS... + uint8 width; + + CVector2D GetPosition(void) { return CVector2D(x/8.0f, y/8.0f); } + CVector2D GetDirection(void) { return CVector2D(dirX/100.0f, dirY/100.0f); } + float GetX(void) { return x/8.0f; } + float GetY(void) { return y/8.0f; } + float GetDirX(void) { return dirX/100.0f; } + float GetDirY(void) { return dirY/100.0f; } + float GetLaneOffset(void) { return width/(2*8.0f*LANE_WIDTH); } + + float OneWayLaneOffset() + { + if (numLeftLanes == 0) + return 0.5f - 0.5f * numRightLanes; + if (numRightLanes == 0) + return 0.5f - 0.5f * numLeftLanes; + return 0.5f + GetLaneOffset(); + } +}; + +// This is what we're reading from the files, only temporary +struct CPathInfoForObject +{ + float x; + float y; + float z; + int8 type; + int8 next; + int8 numLeftLanes; + int8 numRightLanes; + int8 speedLimit; + uint8 width; + + uint8 crossing : 1; + uint8 onlySmallBoats : 1; + uint8 roadBlock : 1; + uint8 disabled : 1; + uint8 waterPath : 1; + uint8 betweenLevels : 1; + + uint8 spawnRate : 4; + + void CheckIntegrity(void); + void SwapConnectionsToBeRightWayRound(void); +}; +extern CPathInfoForObject *InfoForTileCars; +extern CPathInfoForObject *InfoForTilePeds; + +struct CTempNode +{ + CVector pos; + int8 dirX; // *100 + int8 dirY; + int16 link1; + int16 link2; + int8 numLeftLanes; + int8 numRightLanes; + uint8 width; + bool isCross; + int8 linkState; +}; + +struct CTempNodeExternal // made up name +{ + CVector pos; + int16 next; + int8 numLeftLanes; + int8 numRightLanes; + uint8 width; + bool isCross; +}; + +// from mobile +template +class CRoute +{ + T m_node[8]; +}; + + +class CPathFind +{ +public: + CPathNode m_pathNodes[NUM_PATHNODES]; + CCarPathLink m_carPathLinks[NUM_CARPATHLINKS]; + CTreadable *m_mapObjects[NUM_MAPOBJECTS]; + uint16 m_connections[NUM_PATHCONNECTIONS]; // and flags + uint8 m_distances[NUM_PATHCONNECTIONS]; + int16 m_carPathConnections[NUM_PATHCONNECTIONS]; + + int32 m_numPathNodes; + int32 m_numCarPathNodes; + int32 m_numPedPathNodes; + int16 m_numMapObjects; + int16 m_numConnections; + int32 m_numCarPathLinks; + int32 unk; + uint8 m_numGroups[2]; + CPathNode m_searchNodes[512]; + + void Init(void); + void AllocatePathFindInfoMem(int16 numPathGroups); + void RegisterMapObject(CTreadable *mapObject); + void StoreNodeInfoPed(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, float width, bool crossing, uint8 spawnRate); + void StoreNodeInfoCar(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, float width, int8 numLeft, int8 numRight, + bool disabled, bool betweenLevels, uint8 speedLimit, bool roadBlock, bool waterPath, uint8 spawnRate); + void StoreDetachedNodeInfoPed(int32 node, int8 type, int32 next, float x, float y, float z, float width, bool crossing, + bool disabled, bool betweenLevels, uint8 spawnRate); + void StoreDetachedNodeInfoCar(int32 node, int8 type, int32 next, float x, float y, float z, float width, int8 numLeft, int8 numRight, + bool disabled, bool betweenLevels, uint8 speedLimit, bool roadBlock, bool waterPath, uint8 spawnRate, bool unk); + void CalcNodeCoors(float x, float y, float z, int32 id, CVector *out); + bool LoadPathFindData(void); + void PreparePathData(void); + void CountFloodFillGroups(uint8 type); + void PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoForObject *objectpathinfo, + float maxdist, CPathInfoForObject *detachednodes, int32 numDetached); + + bool IsPathObject(int id) { return id < PATHNODESIZE && (InfoForTileCars[id*12].type != 0 || InfoForTilePeds[id*12].type != 0); } + + float CalcRoadDensity(float x, float y); + bool TestForPedTrafficLight(CPathNode *n1, CPathNode *n2); + bool TestCrossesRoad(CPathNode *n1, CPathNode *n2); + void AddNodeToList(CPathNode *node, int32 listId); + void RemoveNodeFromList(CPathNode *node); + void RemoveBadStartNode(CVector pos, CPathNode **nodes, int16 *n); + void SetLinksBridgeLights(float, float, float, float, bool); + void SwitchOffNodeAndNeighbours(int32 nodeId, bool disable); + void SwitchRoadsOffInArea(float x1, float x2, float y1, float y2, float z1, float z2, bool disable); + void SwitchPedRoadsOffInArea(float x1, float x2, float y1, float y2, float z1, float z2, bool disable); + void SwitchRoadsInAngledArea(float x1, float y1, float z1, float x2, float y2, float z2, float length, uint8 type, uint8 enable); + void MarkRoadsBetweenLevelsNodeAndNeighbours(int32 nodeId); + void MarkRoadsBetweenLevelsInArea(float x1, float x2, float y1, float y2, float z1, float z2); + void PedMarkRoadsBetweenLevelsInArea(float x1, float x2, float y1, float y2, float z1, float z2); + int32 FindNodeClosestToCoors(CVector coors, uint8 type, float distLimit, bool ignoreDisabled = false, bool ignoreBetweenLevels = false, bool ignoreSelected = false, bool bWaterPath = false); + int32 FindNodeClosestToCoorsFavourDirection(CVector coors, uint8 type, float dirX, float dirY); + void FindNodePairClosestToCoors(CVector coors, uint8 type, int* node1, int* node2, float* angle, float minDist, float maxDist, bool ignoreDisabled = false, bool ignoreBetweenLevels = false, bool bWaterPath = false); + int32 FindNthNodeClosestToCoors(CVector coors, uint8 type, float distLimit, bool ignoreDisabled, bool ignoreBetweenLevels, int N, bool bWaterPath = false); + CVector FindNodeCoorsForScript(int32 id); + float FindNodeOrientationForCarPlacement(int32 nodeId); + float FindNodeOrientationForCarPlacementFacingDestination(int32 nodeId, float x, float y, bool towards); + bool GenerateCarCreationCoors(float x, float y, float dirX, float dirY, float spawnDist, float angleLimit, bool forward, CVector *pPosition, int32 *pNode1, int32 *pNode2, float *pPositionBetweenNodes, bool ignoreDisabled = false); + bool GeneratePedCreationCoors(float x, float y, float minDist, float maxDist, float minDistOffScreen, float maxDistOffScreen, CVector *pPosition, int32 *pNode1, int32 *pNode2, float *pPositionBetweenNodes, CMatrix *camMatrix); + void FindNextNodeWandering(uint8, CVector, CPathNode**, CPathNode**, uint8, uint8*); + void DoPathSearch(uint8 type, CVector start, int32 startNodeId, CVector target, CPathNode **nodes, int16 *numNodes, int16 maxNumNodes, CVehicle *vehicle, float *dist, float distLimit, int32 forcedTargetNode); + bool TestCoorsCloseness(CVector target, uint8 type, CVector start); + void Save(uint8 *buf, uint32 *size); + void Load(uint8 *buf, uint32 size); + + static CVector TakeWidthIntoAccountForWandering(CPathNode*, uint16); + static void TakeWidthIntoAccountForCoors(CPathNode*, CPathNode*, uint16, float*, float*); + + CPathNode *GetNode(int16 index); + int16 GetIndex(CPathNode *node); + + uint16 ConnectedNode(int id) { return m_connections[id] & 0x3FFF; } + bool ConnectionCrossesRoad(int id) { return !!(m_connections[id] & 0x8000); } + bool ConnectionHasTrafficLight(int id) { return !!(m_connections[id] & 0x4000); } + void ConnectionSetTrafficLight(int id) { m_connections[id] |= 0x4000; } + + void DisplayPathData(void); + + // Following methods are present on mobile but are unused. TODO: implement them + void SavePathFindData(void); + void ComputeRoute(uint8, const CVector&, const CVector&, CRoute&); + void RecordNodesClosestToCoors(CVector, uint8, int, CPathNode**, float, bool, bool, bool); + void RecordNodesInCircle(const CVector&, float, uint8, int, CPathNode**, bool, bool, bool, bool); + void ArrangeOneNodeList(CPathInfoForObject*, int16); + void ArrangeNodes(int16); + void RegisterMarker(CVector*); + void Shutdown(void); +}; + +extern CPathFind ThePaths; + +inline CPathNode *CPathNode::GetPrev(void) { return ThePaths.GetNode(prevIndex); } +inline CPathNode *CPathNode::GetNext(void) { return ThePaths.GetNode(nextIndex); } +inline void CPathNode::SetPrev(CPathNode *node) { prevIndex = ThePaths.GetIndex(node); } +inline void CPathNode::SetNext(CPathNode *node) { nextIndex = ThePaths.GetIndex(node); } + +extern bool gbShowPedPaths; +extern bool gbShowCarPaths; +extern bool gbShowCarPathsLinks; diff --git a/src/miami/control/Phones.cpp b/src/miami/control/Phones.cpp new file mode 100644 index 00000000..41f9d766 --- /dev/null +++ b/src/miami/control/Phones.cpp @@ -0,0 +1,404 @@ +#include "common.h" + +#include "Phones.h" +#include "Pools.h" +#include "ModelIndices.h" +#include "Ped.h" +#include "Pad.h" +#include "Messages.h" +#include "Camera.h" +#include "World.h" +#include "General.h" +#include "AudioScriptObject.h" +#include "RpAnimBlend.h" +#include "AnimBlendAssociation.h" +#include "soundlist.h" +#include "SaveBuf.h" +#ifdef FIX_BUGS +#include "Replay.h" +#endif + +#ifdef COMPATIBLE_SAVES +#define PHONEINFO_SAVE_SIZE 0xA30 +#else +#define PHONEINFO_SAVE_SIZE sizeof(CPhoneInfo) +#endif + +CPhoneInfo gPhoneInfo; + +bool CPhoneInfo::bDisplayingPhoneMessage; // is phone picked up +uint32 CPhoneInfo::PhoneEnableControlsTimer; +CPhone *CPhoneInfo::pPhoneDisplayingMessages; +bool CPhoneInfo::bPickingUpPhone; +CPed *CPhoneInfo::pCallBackPed; // ped who picking up the phone (reset after pickup cb) + +/* + Entering phonebooth cutscene, showing messages and triggering these things + by checking coordinates happens in here - blue mission marker is cosmetic. + + Repeated message means after the script set the messages for a particular phone, + player can pick the phone again with the same messages appearing, + after 60 seconds of last phone pick-up. +*/ + +void +CPhoneInfo::Update(void) +{ +#ifdef FIX_BUGS + if (CReplay::IsPlayingBack()) + return; +#endif + CPlayerPed *player = FindPlayerPed(); + CPlayerInfo *playerInfo = &CWorld::Players[CWorld::PlayerInFocus]; + if (bDisplayingPhoneMessage && CTimer::GetTimeInMilliseconds() > PhoneEnableControlsTimer) { + playerInfo->MakePlayerSafe(false); + TheCamera.SetWideScreenOff(); + pPhoneDisplayingMessages = nil; + bDisplayingPhoneMessage = false; + CAnimBlendAssociation *talkAssoc = RpAnimBlendClumpGetAssociation(player->GetClump(), ANIM_STD_PHONE_TALK); + if (talkAssoc && talkAssoc->blendAmount > 0.5f) { + CAnimBlendAssociation *endAssoc = CAnimManager::BlendAnimation(player->GetClump(), ASSOCGRP_STD, ANIM_STD_PHONE_OUT, 8.0f); + endAssoc->flags &= ~ASSOC_DELETEFADEDOUT; + endAssoc->SetFinishCallback(PhonePutDownCB, player); + } else { + CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_PHONE); + if (player->m_nPedState == PED_MAKE_CALL) + player->SetPedState(PED_IDLE); + } + } + bool notInCar; + CVector playerPos; + if (FindPlayerVehicle()) { + notInCar = false; + playerPos = FindPlayerVehicle()->GetPosition(); + } else { + notInCar = true; + playerPos = player->GetPosition(); + } + bool phoneRings = false; + bool scratchTheCabinet; + for(int phoneId = 0; phoneId < m_nScriptPhonesMax; phoneId++) { + if (m_aPhones[phoneId].m_visibleToCam) { + switch (m_aPhones[phoneId].m_nState) { + case PHONE_STATE_ONETIME_MESSAGE_SET: + case PHONE_STATE_REPEATED_MESSAGE_SET: + case PHONE_STATE_REPEATED_MESSAGE_SHOWN_ONCE: + if (bPickingUpPhone) { + scratchTheCabinet = false; + phoneRings = false; + } else { + scratchTheCabinet = (CTimer::GetTimeInMilliseconds() / 1880) % 2 == 1; + phoneRings = (CTimer::GetPreviousTimeInMilliseconds() / 1880) % 2 == 1; + } + if (scratchTheCabinet) { + m_aPhones[phoneId].m_pEntity->GetUp().z = (CGeneral::GetRandomNumber() % 1024) / 16000.0f + 1.0f; + if (!phoneRings) + PlayOneShotScriptObject(SCRIPT_SOUND_PAYPHONE_RINGING, m_aPhones[phoneId].m_pEntity->GetPosition()); + } else { + m_aPhones[phoneId].m_pEntity->GetUp().z = 1.0f; + } + m_aPhones[phoneId].m_pEntity->GetMatrix().UpdateRW(); + m_aPhones[phoneId].m_pEntity->UpdateRwFrame(); + if (notInCar && !bPickingUpPhone && player->IsPedInControl()) { + CVector2D distToPhone = playerPos - m_aPhones[phoneId].m_vecPos; + if (Abs(distToPhone.x) < 1.0f && Abs(distToPhone.y) < 1.0f) { + if (DotProduct2D(distToPhone, m_aPhones[phoneId].m_pEntity->GetForward()) / distToPhone.Magnitude() < -0.85f) { + CVector2D distToPhoneObj = playerPos - m_aPhones[phoneId].m_pEntity->GetPosition(); + float angleToFace = CGeneral::GetATanOfXY(distToPhoneObj.x, distToPhoneObj.y) + HALFPI; + if (angleToFace > TWOPI) + angleToFace = angleToFace - TWOPI; + player->m_fRotationCur = angleToFace; + player->m_fRotationDest = angleToFace; + player->SetHeading(angleToFace); + player->SetPedState(PED_MAKE_CALL); + CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_PHONE); + TheCamera.SetWideScreenOn(); + playerInfo->MakePlayerSafe(true); + CAnimBlendAssociation *phonePickAssoc = CAnimManager::BlendAnimation(player->GetClump(), ASSOCGRP_STD, ANIM_STD_PHONE_IN, 4.0f); + phonePickAssoc->SetFinishCallback(PhonePickUpCB, &m_aPhones[phoneId]); + bPickingUpPhone = true; + pCallBackPed = player; + } + } + } + break; + case PHONE_STATE_REPEATED_MESSAGE_STARTED: + if (CTimer::GetTimeInMilliseconds() - m_aPhones[phoneId].m_repeatedMessagePickupStart > 60000) + m_aPhones[phoneId].m_nState = PHONE_STATE_REPEATED_MESSAGE_SHOWN_ONCE; + break; + case PHONE_STATE_9: + scratchTheCabinet = (CTimer::GetTimeInMilliseconds() / 1880) % 2 == 1; + phoneRings = (CTimer::GetPreviousTimeInMilliseconds() / 1880) % 2 == 1; + if (scratchTheCabinet) { + m_aPhones[phoneId].m_pEntity->GetUp().z = (CGeneral::GetRandomNumber() % 1024) / 16000.0f + 1.0f; + if (!phoneRings) + PlayOneShotScriptObject(SCRIPT_SOUND_PAYPHONE_RINGING, m_aPhones[phoneId].m_pEntity->GetPosition()); + } else { + m_aPhones[phoneId].m_pEntity->GetUp().z = 1.0f; + } + m_aPhones[phoneId].m_pEntity->GetMatrix().UpdateRW(); + m_aPhones[phoneId].m_pEntity->UpdateRwFrame(); + break; + default: + break; + } + if (CVector2D(TheCamera.GetPosition() - m_aPhones[phoneId].m_vecPos).MagnitudeSqr() > sq(100.0f)) + m_aPhones[phoneId].m_visibleToCam = false; + } else if (!((CTimer::GetFrameCounter() + m_aPhones[phoneId].m_pEntity->m_randomSeed) % 16)) { + if (CVector2D(TheCamera.GetPosition() - m_aPhones[phoneId].m_vecPos).MagnitudeSqr() < sq(60.0f)) + m_aPhones[phoneId].m_visibleToCam = true; + } + } +} + +int +CPhoneInfo::FindNearestFreePhone(CVector *pos) +{ + int nearestPhoneId = -1; + float nearestPhoneDist = 60.0f; + + for (int phoneId = 0; phoneId < m_nMax; phoneId++) { + + if (gPhoneInfo.m_aPhones[phoneId].m_nState == PHONE_STATE_FREE) { + float phoneDist = (m_aPhones[phoneId].m_vecPos - *pos).Magnitude2D(); + + if (phoneDist < nearestPhoneDist) { + nearestPhoneDist = phoneDist; + nearestPhoneId = phoneId; + } + } + } + return nearestPhoneId; +} + +bool +CPhoneInfo::PhoneAtThisPosition(CVector pos) +{ + for (int phoneId = 0; phoneId < m_nMax; phoneId++) { + if (pos.x == m_aPhones[phoneId].m_vecPos.x && pos.y == m_aPhones[phoneId].m_vecPos.y) + return true; + } + return false; +} + +bool +CPhoneInfo::HasMessageBeenDisplayed(int phoneId) +{ + if (bDisplayingPhoneMessage) + return false; + + int state = m_aPhones[phoneId].m_nState; + + return state == PHONE_STATE_REPEATED_MESSAGE_SHOWN_ONCE || + state == PHONE_STATE_ONETIME_MESSAGE_STARTED || + state == PHONE_STATE_REPEATED_MESSAGE_STARTED; +} + +bool +CPhoneInfo::IsMessageBeingDisplayed(int phoneId) +{ + return pPhoneDisplayingMessages == &m_aPhones[phoneId]; +} + +void +CPhoneInfo::Load(uint8 *buf, uint32 size) +{ +INITSAVEBUF + ReadSaveBuf(&m_nMax, buf); + ReadSaveBuf(&m_nScriptPhonesMax, buf); + for (int i = 0; i < NUMPHONES; i++) { +#ifdef COMPATIBLE_SAVES + ReadSaveBuf(&m_aPhones[i].m_vecPos, buf); + SkipSaveBuf(buf, 6 * 4); + ReadSaveBuf(&m_aPhones[i].m_repeatedMessagePickupStart, buf); + int32 tmp; + ReadSaveBuf(&tmp, buf); + // It's saved as building pool index in save file, convert it to true entity + m_aPhones[i].m_pEntity = tmp != 0 ? CPools::GetBuildingPool()->GetSlot(tmp - 1) : nil; + ReadSaveBuf(&m_aPhones[i].m_nState, buf); + ReadSaveBuf(&m_aPhones[i].m_visibleToCam, buf); + SkipSaveBuf(buf, 3); +#else + ReadSaveBuf(&m_aPhones[i], buf); + // It's saved as building pool index in save file, convert it to true entity + if (m_aPhones[i].m_pEntity) { + m_aPhones[i].m_pEntity = CPools::GetBuildingPool()->GetSlot((uintptr)m_aPhones[i].m_pEntity - 1); + } +#endif + } +VALIDATESAVEBUF(size) +} + +void +CPhoneInfo::SetPhoneMessage_JustOnce(int phoneId, wchar *msg1, wchar *msg2, wchar *msg3, wchar *msg4, wchar *msg5, wchar *msg6) +{ + // If there is at least one message, it should be msg1. + if (msg1) { + m_aPhones[phoneId].m_apMessages[0] = msg1; + m_aPhones[phoneId].m_apMessages[1] = msg2; + m_aPhones[phoneId].m_apMessages[2] = msg3; + m_aPhones[phoneId].m_apMessages[3] = msg4; + m_aPhones[phoneId].m_apMessages[4] = msg5; + m_aPhones[phoneId].m_apMessages[5] = msg6; + m_aPhones[phoneId].m_nState = PHONE_STATE_ONETIME_MESSAGE_SET; + } else { + m_aPhones[phoneId].m_nState = PHONE_STATE_MESSAGE_REMOVED; + } +} + +void +CPhoneInfo::SetPhoneMessage_Repeatedly(int phoneId, wchar *msg1, wchar *msg2, wchar *msg3, wchar *msg4, wchar *msg5, wchar *msg6) +{ + // If there is at least one message, it should be msg1. + if (msg1) { + m_aPhones[phoneId].m_apMessages[0] = msg1; + m_aPhones[phoneId].m_apMessages[1] = msg2; + m_aPhones[phoneId].m_apMessages[2] = msg3; + m_aPhones[phoneId].m_apMessages[3] = msg4; + m_aPhones[phoneId].m_apMessages[4] = msg5; + m_aPhones[phoneId].m_apMessages[5] = msg6; + m_aPhones[phoneId].m_nState = PHONE_STATE_REPEATED_MESSAGE_SET; + } else { + m_aPhones[phoneId].m_nState = PHONE_STATE_MESSAGE_REMOVED; + } +} + +int +CPhoneInfo::GrabPhone(float xPos, float yPos) +{ + // "Grab" doesn't mean picking up the phone, it means allocating some particular phone to + // whoever called the 024A opcode first with the position parameters closest to phone. + // Same phone won't be available on next run of this function. + + int nearestPhoneId = -1; + CVector pos(xPos, yPos, 0.0f); + float nearestPhoneDist = 100.0f; + + for (int phoneId = m_nScriptPhonesMax; phoneId < m_nMax; phoneId++) { + float phoneDistance = (m_aPhones[phoneId].m_vecPos - pos).Magnitude2D(); + if (phoneDistance < nearestPhoneDist) { + nearestPhoneDist = phoneDistance; + nearestPhoneId = phoneId; + } + } + m_aPhones[nearestPhoneId].m_nState = PHONE_STATE_MESSAGE_REMOVED; + + CPhone oldFirstPhone = m_aPhones[m_nScriptPhonesMax]; + m_aPhones[m_nScriptPhonesMax] = m_aPhones[nearestPhoneId]; + m_aPhones[nearestPhoneId] = oldFirstPhone; + m_nScriptPhonesMax++; + return m_nScriptPhonesMax - 1; +} + +void +CPhoneInfo::Initialise(void) +{ + CBuildingPool *pool = CPools::GetBuildingPool(); + pCallBackPed = nil; + bDisplayingPhoneMessage = false; + bPickingUpPhone = false; + pPhoneDisplayingMessages = nil; + m_nMax = 0; + m_nScriptPhonesMax = 0; + for (int i = pool->GetSize() - 1; i >= 0; i--) { + CBuilding *building = pool->GetSlot(i); + if (building) { + if (building->GetModelIndex() == MI_PHONEBOOTH1) { + assert(m_nMax < ARRAY_SIZE(m_aPhones) && "NUMPHONES should be increased"); + CPhone *maxPhone = &m_aPhones[m_nMax]; + maxPhone->m_nState = PHONE_STATE_FREE; + maxPhone->m_vecPos = building->GetPosition(); + maxPhone->m_pEntity = building; + m_nMax++; + } + } + } +} + +void +CPhoneInfo::Save(uint8 *buf, uint32 *size) +{ + *size = PHONEINFO_SAVE_SIZE; +INITSAVEBUF + WriteSaveBuf(buf, m_nMax); + WriteSaveBuf(buf, m_nScriptPhonesMax); + for(int phoneId = 0; phoneId < NUMPHONES; phoneId++) { +#ifdef COMPATIBLE_SAVES + WriteSaveBuf(buf, m_aPhones[phoneId].m_vecPos); + ZeroSaveBuf(buf, 6 * 4); + WriteSaveBuf(buf, m_aPhones[phoneId].m_repeatedMessagePickupStart); + // Convert entity pointer to building pool index while saving + int32 tmp = m_aPhones[phoneId].m_pEntity ? CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert((CBuilding*)m_aPhones[phoneId].m_pEntity) + 1 : 0; + WriteSaveBuf(buf, tmp); + WriteSaveBuf(buf, m_aPhones[phoneId].m_nState); + WriteSaveBuf(buf, m_aPhones[phoneId].m_visibleToCam); + ZeroSaveBuf(buf, 3); +#else + CPhone* phone = WriteSaveBuf(buf, m_aPhones[phoneId]); + + // Convert entity pointer to building pool index while saving + if (phone->m_pEntity) { + phone->m_pEntity = (CEntity*) (CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert((CBuilding*)phone->m_pEntity) + 1); + } +#endif + } +VALIDATESAVEBUF(*size) +} + +void +CPhoneInfo::Shutdown(void) +{ + m_nMax = 0; + m_nScriptPhonesMax = 0; +} + +void +PhonePutDownCB(CAnimBlendAssociation *assoc, void *arg) +{ + assoc->flags |= ASSOC_DELETEFADEDOUT; + assoc->blendDelta = -1000.0f; + CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_PHONE); + CPed *ped = (CPed*)arg; + + if (assoc->blendAmount > 0.5f) + ped->bUpdateAnimHeading = true; + + if (ped->m_nPedState == PED_MAKE_CALL) + ped->SetPedState(PED_IDLE); +} + +void +PhonePickUpCB(CAnimBlendAssociation *assoc, void *arg) +{ + CPhone *phone = (CPhone*)arg; + int messagesDisplayTime = 0; + + for(int i=0; i < 6; i++) { + wchar *msg = phone->m_apMessages[i]; + if (msg) { + CMessages::AddMessage(msg, 3000, 0); + messagesDisplayTime += 3000; + } + } + + CPhoneInfo::bPickingUpPhone = false; + CPhoneInfo::bDisplayingPhoneMessage = true; + CPhoneInfo::pPhoneDisplayingMessages = phone; + CPhoneInfo::PhoneEnableControlsTimer = CTimer::GetTimeInMilliseconds() + messagesDisplayTime; + + if (phone->m_nState == PHONE_STATE_ONETIME_MESSAGE_SET) { + phone->m_nState = PHONE_STATE_ONETIME_MESSAGE_STARTED; + } else { + phone->m_nState = PHONE_STATE_REPEATED_MESSAGE_STARTED; + phone->m_repeatedMessagePickupStart = CTimer::GetTimeInMilliseconds(); + } + + CPed *ped = CPhoneInfo::pCallBackPed; + ped->m_nMoveState = PEDMOVE_STILL; + CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE, 8.0f); + + if (assoc->blendAmount > 0.5f && ped) + CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_PHONE_TALK, 8.0f); + + CPhoneInfo::pCallBackPed = nil; +} diff --git a/src/miami/control/Phones.h b/src/miami/control/Phones.h new file mode 100644 index 00000000..81b40dc2 --- /dev/null +++ b/src/miami/control/Phones.h @@ -0,0 +1,69 @@ +#pragma once + +#include "Physical.h" + +class CPed; +class CAnimBlendAssociation; + +enum PhoneState { + PHONE_STATE_FREE, + PHONE_STATE_REPORTING_CRIME, // CCivilianPed::ProcessControl sets it but unused + PHONE_STATE_2, + PHONE_STATE_MESSAGE_REMOVED, + PHONE_STATE_ONETIME_MESSAGE_SET, + PHONE_STATE_REPEATED_MESSAGE_SET, + PHONE_STATE_REPEATED_MESSAGE_SHOWN_ONCE, + PHONE_STATE_ONETIME_MESSAGE_STARTED, + PHONE_STATE_REPEATED_MESSAGE_STARTED, + PHONE_STATE_9 // just rings, picking being handled via script. most of the time game uses this +}; + +class CPhone +{ +public: + CVector m_vecPos; + wchar *m_apMessages[6]; + uint32 m_repeatedMessagePickupStart; + CEntity *m_pEntity; // stored as building pool index in save files + PhoneState m_nState; + bool m_visibleToCam; + + CPhone() { } + ~CPhone() { } +}; + +VALIDATE_SIZE(CPhone, 0x34); + +class CPhoneInfo { +public: + static bool bDisplayingPhoneMessage; + static uint32 PhoneEnableControlsTimer; + static CPhone *pPhoneDisplayingMessages; + static bool bPickingUpPhone; + static CPed *pCallBackPed; + + int32 m_nMax; + int32 m_nScriptPhonesMax; + CPhone m_aPhones[NUMPHONES]; + + CPhoneInfo() { } + ~CPhoneInfo() { } + + int FindNearestFreePhone(CVector*); + bool PhoneAtThisPosition(CVector); + bool HasMessageBeenDisplayed(int); + bool IsMessageBeingDisplayed(int); + void Load(uint8 *buf, uint32 size); + void Save(uint8 *buf, uint32 *size); + void SetPhoneMessage_JustOnce(int phoneId, wchar *msg1, wchar *msg2, wchar *msg3, wchar *msg4, wchar *msg5, wchar *msg6); + void SetPhoneMessage_Repeatedly(int phoneId, wchar *msg1, wchar *msg2, wchar *msg3, wchar *msg4, wchar *msg5, wchar *msg6); + int GrabPhone(float, float); + void Initialise(void); + void Shutdown(void); + void Update(void); +}; + +extern CPhoneInfo gPhoneInfo; + +void PhonePutDownCB(CAnimBlendAssociation *assoc, void *arg); +void PhonePickUpCB(CAnimBlendAssociation *assoc, void *arg); \ No newline at end of file diff --git a/src/miami/control/Pickups.cpp b/src/miami/control/Pickups.cpp new file mode 100644 index 00000000..86627713 --- /dev/null +++ b/src/miami/control/Pickups.cpp @@ -0,0 +1,1742 @@ +#include "common.h" + +#include "main.h" + +#include "Camera.h" +#include "Coronas.h" +#include "Darkel.h" +#include "Entity.h" +#include "Explosion.h" +#include "Font.h" +#include "Garages.h" +#include "General.h" +#include "ModelIndices.h" +#include "Object.h" +#include "Pad.h" +#include "Pickups.h" +#include "PlayerPed.h" +#include "Wanted.h" +#include "DMAudio.h" +#include "Fire.h" +#include "PointLights.h" +#include "Pools.h" +#ifdef FIX_BUGS +#include "Replay.h" +#endif +#include "SaveBuf.h" +#include "Script.h" +#include "Shadows.h" +#include "SpecialFX.h" +#include "Sprite.h" +#include "Timer.h" +#include "WaterLevel.h" +#include "World.h" +#include "Hud.h" +#include "Messages.h" +#include "Streaming.h" +#include "SaveBuf.h" + +#ifdef COMPATIBLE_SAVES +#define PICKUPS_SAVE_SIZE 0x4440 +#else +#define PICKUPS_SAVE_SIZE sizeof(aPickUps) +#endif + +CPickup CPickups::aPickUps[NUMPICKUPS]; +int16 CPickups::NumMessages; +int32 CPickups::aPickUpsCollected[NUMCOLLECTEDPICKUPS]; +int16 CPickups::CollectedPickUpIndex; + +int32 CPickups::PlayerOnWeaponPickup; +int32 CollectPickupBuffer; + +// unused +bool CPickups::bPickUpcamActivated; +CVehicle *CPickups::pPlayerVehicle; +CVector CPickups::StaticCamCoors; +uint32 CPickups::StaticCamStartTime; + +tPickupMessage CPickups::aMessages[NUMPICKUPMESSAGES]; + +uint16 AmmoForWeapon[WEAPONTYPE_TOTALWEAPONS + 1] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 8, 8, 8, 8, 68, 24, + 32, 28, 20, 200, 120, 120, 120, 120, 120, 40, 28, 8, 300, 200, 1000, 1, 400, 36, 0 }; + +uint16 AmmoForWeapon_OnStreet[WEAPONTYPE_TOTALWEAPONS + 1] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4, 4, 4, 34, 12, + 16, 14, 10, 100, 60, 60, 60, 60, 60, 20, 14, 4, 150, 100, 500, 1, 400, 36, 0 }; + +uint16 CostOfWeapon[WEAPONTYPE_TOTALWEAPONS + 3] = { 0, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 1000, 1000, + 1000, 500, 8000, 250, 400, 1200, 1250, 1250, 800, 800, 650, 1200, 5000, 400, + 10000, 10000, 8000, 8000, 8000, 10000, 1000, 11000, 500, 20, 10, 0 }; + +struct +{ + uint8 r,g,b; + float unk; +} aPickupColors[] = { + { 128, 128, 128, 1.0f }, + { 128, 128, 128, 1.0f }, + { 97, 194, 247, 1.0f }, + { 97, 194, 247, 1.0f }, + { 97, 194, 247, 1.0f }, + { 97, 194, 247, 1.0f }, + { 97, 194, 247, 1.0f }, + { 97, 194, 247, 1.0f }, + { 97, 194, 247, 1.0f }, + { 97, 194, 247, 1.0f }, + { 97, 194, 247, 1.0f }, + { 97, 194, 247, 1.0f }, + { 27, 89, 130, 1.0f }, + { 27, 89, 130, 1.0f }, + { 27, 89, 130, 1.0f }, + { 27, 89, 130, 1.0f }, + { 27, 89, 130, 1.0f }, + { 149, 194, 24, 1.0f }, + { 149, 194, 24, 1.0f }, + { 45, 155, 90, 1.0f }, + { 45, 155, 90, 1.0f }, + { 45, 155, 90, 1.0f }, + { 255, 227, 79, 1.0f }, + { 255, 227, 79, 1.0f }, + { 255, 227, 79, 1.0f }, + { 255, 227, 79, 1.0f }, + { 254, 137, 0, 1.0f }, + { 254, 137, 0, 1.0f }, + { 249, 131, 215, 1.0f }, + { 249, 131, 215, 1.0f }, + { 164, 40, 178, 1.0f }, + { 164, 40, 178, 1.0f }, + { 164, 40, 178, 1.0f }, + { 164, 40, 178, 1.0f }, + { 69, 69, 69, 1.0f }, + { 69, 69, 69, 1.0f }, + { 69, 69, 69, 1.0f }, + { 255, 100, 100, 1.0f }, + { 128, 255, 128, 1.0f }, + { 100, 100, 255, 1.0f }, + { 255, 255, 100, 1.0f }, + { 255, 100, 100, 1.0f }, + { 100, 255, 100, 1.0f }, + { 255, 255, 255, 1.0f } +}; + + +void +ModifyStringLabelForControlSetting(char *str) +{ + int len = (int)strlen(str); + if (len <= 2) + return; + + if (str[len - 2] != '_') + return; + + switch (CPad::GetPad(0)->Mode) { + case 0: + case 1: + str[len - 1] = 'L'; + break; + case 2: + str[len - 1] = 'T'; + break; + case 3: + str[len - 1] = 'C'; + break; + default: + return; + } +} + +void +CPickup::ExtractAmmoFromPickup(CPlayerPed *player) +{ + eWeaponType weaponType = CPickups::WeaponForModel(m_pObject->GetModelIndex()); + + if (m_eType == PICKUP_IN_SHOP || !CWeaponInfo::IsWeaponSlotAmmoMergeable(CWeaponInfo::GetWeaponInfo(weaponType)->m_nWeaponSlot)) + return; + + uint32 ammo = m_nQuantity; + if (ammo == 0) { + if (!m_bWasAmmoCollected) + ammo = AmmoForWeapon_OnStreet[weaponType]; + else + goto removeAmmo; + } + player->GrantAmmo(weaponType, ammo); + DMAudio.PlayOneShot(player->m_audioEntityId, SOUND_WEAPON_RELOAD, weaponType); // BUG? weapon type as volume, wtf? +removeAmmo: + m_nQuantity = 0; + m_bWasAmmoCollected = true; +} + +void +CPickup::Remove() +{ + GetRidOfObjects(); + m_bRemoved = true; + m_eType = PICKUP_NONE; +} + +CObject * +CPickup::GiveUsAPickUpObject(CObject **ppObject, CObject **ppExtraObject, int32 handle, int32 extraHandle) +{ + CObject *&object = *ppObject; + CObject *&extraObject = *ppExtraObject; + + object = extraObject = nil; + + int32 modelId = -1; + if (CModelInfo::GetModelInfo(m_eModelIndex)->GetModelType() == MITYPE_WEAPON) { + CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(((CWeaponModelInfo*)CModelInfo::GetModelInfo(m_eModelIndex))->GetWeaponInfo()); + modelId = weaponInfo->m_nModelId; + if (modelId == m_eModelIndex) + modelId = weaponInfo->m_nModel2Id; + } + + if (handle >= 0) { + CPools::MakeSureSlotInObjectPoolIsEmpty(handle); + if (extraHandle >= 0) + CPools::MakeSureSlotInObjectPoolIsEmpty(extraHandle); + if (object == nil) + object = new(handle) CObject(m_eModelIndex, false); + + if (extraHandle >= 0 && modelId != -1 && extraObject == nil) + extraObject = new(extraHandle) CObject(modelId, false); + } else { + object = new CObject(m_eModelIndex, false); + if (modelId != -1) + extraObject = new CObject(modelId, false); + } + + if (object == nil) return nil; + object->ObjectCreatedBy = MISSION_OBJECT; + object->SetPosition(m_vecPos); + object->SetOrientation(0.0f, 0.0f, -HALFPI); + object->GetMatrix().UpdateRW(); + object->UpdateRwFrame(); + + object->bAffectedByGravity = false; + object->bExplosionProof = true; + object->bUsesCollision = false; + object->bIsPickup = true; + object->bAmmoCollected = m_bWasAmmoCollected; + object->bHasPreRenderEffects = true; + + if (extraObject) { + extraObject->ObjectCreatedBy = MISSION_OBJECT; + extraObject->SetPosition(m_vecPos); + extraObject->SetOrientation(0.0f, 0.0f, -HALFPI); + extraObject->GetMatrix().UpdateRW(); + extraObject->UpdateRwFrame(); + + extraObject->bAffectedByGravity = false; + extraObject->bExplosionProof = true; + extraObject->bUsesCollision = false; + extraObject->bIsPickup = true; + extraObject->bAmmoCollected = true; + extraObject->bHasPreRenderEffects = true; + extraObject->m_nBonusValue = 0; + extraObject->bPickupObjWithMessage = false; + extraObject->bOutOfStock = false; + } + + object->m_nBonusValue = (m_eModelIndex == MI_PICKUP_BONUS || m_eModelIndex == MI_PICKUP_CLOTHES) ? m_nQuantity : 0; + + switch (m_eType) + { + case PICKUP_IN_SHOP: + object->bPickupObjWithMessage = true; + object->bOutOfStock = false; + if (m_eModelIndex == MI_PICKUP_HEALTH || m_eModelIndex == MI_PICKUP_ADRENALINE) + object->m_nCostValue = 0; + else + object->m_nCostValue = CostOfWeapon[CPickups::WeaponForModel(m_eModelIndex)]; + break; + case PICKUP_ON_STREET: + case PICKUP_ONCE: + case PICKUP_ONCE_TIMEOUT: + case PICKUP_COLLECTABLE1: + case PICKUP_MONEY: + case PICKUP_MINE_INACTIVE: + case PICKUP_MINE_ARMED: + case PICKUP_NAUTICAL_MINE_INACTIVE: + case PICKUP_NAUTICAL_MINE_ARMED: + case PICKUP_FLOATINGPACKAGE: + case PICKUP_ON_STREET_SLOW: + object->bPickupObjWithMessage = false; + object->bOutOfStock = false; + break; + case PICKUP_IN_SHOP_OUT_OF_STOCK: + object->bPickupObjWithMessage = false; + object->bOutOfStock = true; + object->bRenderScorched = true; + break; + case PICKUP_FLOATINGPACKAGE_FLOATING: + default: + break; + } + return object; +} + +bool +CPickup::CanBePickedUp(CPlayerPed *player, int playerId) +{ + assert(m_pObject != nil); + bool cannotBePickedUp = + (m_pObject->GetModelIndex() == MI_PICKUP_BODYARMOUR && player->m_fArmour > CWorld::Players[playerId].m_nMaxArmour - 0.2f) + || (m_pObject->GetModelIndex() == MI_PICKUP_HEALTH && player->m_fHealth > CWorld::Players[playerId].m_nMaxHealth - 0.2f) + || (m_pObject->GetModelIndex() == MI_PICKUP_BRIBE && player->m_pWanted->GetWantedLevel() == 0) + || (m_pObject->GetModelIndex() == MI_PICKUP_KILLFRENZY && (CTheScripts::IsPlayerOnAMission() || CDarkel::FrenzyOnGoing() || !CGame::nastyGame)) + || (m_eType == PICKUP_ASSET_REVENUE && m_fRevenue < 10.0f); + return !cannotBePickedUp; +} + +bool +CPickup::Update(CPlayerPed *player, CVehicle *vehicle, int playerId) +{ + bool result = false; + float waterLevel; + + if (m_pObject) { + m_pObject->GetMatrix().GetPosition() = m_vecPos; + if (m_pExtraObject) + m_pExtraObject->GetMatrix().GetPosition() = m_vecPos; + } + if (m_eType == PICKUP_ASSET_REVENUE) { + uint32 timePassed = CTimer::GetTimeInMilliseconds() - m_nTimer; + m_nTimer = CTimer::GetTimeInMilliseconds(); + + if (Distance(FindPlayerCoors(), m_vecPos) > 10.0f) + m_fRevenue += float(timePassed * m_nMoneySpeed) / SQR(1200.0f); + + m_fRevenue = Min(m_fRevenue, m_nQuantity); + + m_pObject->m_nCostValue = m_fRevenue < 10 ? 0 : m_fRevenue; + } + + if (m_bRemoved) { + if (CTimer::GetTimeInMilliseconds() > m_nTimer) { + // respawn pickup if we're far enough + float dist = (FindPlayerCoors().x - m_vecPos.x) * (FindPlayerCoors().x - m_vecPos.x) + (FindPlayerCoors().y - m_vecPos.y) * (FindPlayerCoors().y - m_vecPos.y); + if (dist > 100.0f || m_eType == PICKUP_IN_SHOP && dist > 2.4f) { + m_pObject = GiveUsAPickUpObject(&m_pObject, &m_pExtraObject, -1, -1); + if (m_pObject) { + CWorld::Add(m_pObject); + m_bRemoved = false; + } + } + } + return false; + } + + if (!m_pObject) { + GiveUsAPickUpObject(&m_pObject, &m_pExtraObject, -1, -1); + if (m_pObject) + CWorld::Add(m_pObject); + if (m_pExtraObject) + CWorld::Add(m_pExtraObject); + } + + if (!m_pObject) return false; + + if (!IsMine()) { + // let's check if we touched the pickup + bool isPickupTouched = false; + if (m_pObject->GetModelIndex() == MI_PICKUP_BRIBE) { + if (vehicle != nil) { + if (vehicle->IsSphereTouchingVehicle(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z, 2.0f)) + isPickupTouched = true; + } + else { + if (Abs(player->GetPosition().z - m_pObject->GetPosition().z) < 2.0f) { + if ((player->GetPosition().x - m_pObject->GetPosition().x) * (player->GetPosition().x - m_pObject->GetPosition().x) + + (player->GetPosition().y - m_pObject->GetPosition().y) * (player->GetPosition().y - m_pObject->GetPosition().y) < 1.8f) + isPickupTouched = true; + } + } + } else if (m_pObject->GetModelIndex() == MI_PICKUP_CAMERA) { + if (vehicle != nil && vehicle->IsSphereTouchingVehicle(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z, 2.0f)) { + isPickupTouched = true; + } + } else if (vehicle == nil) { + if (Abs(player->GetPosition().z - m_pObject->GetPosition().z) < 2.0f) { + if ((player->GetPosition().x - m_pObject->GetPosition().x) * (player->GetPosition().x - m_pObject->GetPosition().x) + + (player->GetPosition().y - m_pObject->GetPosition().y) * (player->GetPosition().y - m_pObject->GetPosition().y) < 1.8f) + isPickupTouched = true; + } + } + + if (isPickupTouched) { + eWeaponType weaponType = CPickups::WeaponForModel(m_pObject->GetModelIndex()); + if (weaponType < WEAPONTYPE_TOTALWEAPONS && CDarkel::FrenzyOnGoing()) { + isPickupTouched = false; + m_bWasControlMessageShown = false; + } else if (weaponType < WEAPONTYPE_TOTALWEAPONS && weaponType != WEAPONTYPE_UNARMED) { + uint32 slot = CWeaponInfo::GetWeaponInfo(weaponType)->m_nWeaponSlot; + eWeaponType plrWeaponSlot = FindPlayerPed()->GetWeapon(slot).m_eWeaponType; + if (plrWeaponSlot != weaponType) { + if (CStreaming::ms_aInfoForModel[m_pObject->GetModelIndex()].m_loadState == STREAMSTATE_LOADED) { + if (plrWeaponSlot == WEAPONTYPE_UNARMED || (FindPlayerPed()->GetWeapon(slot).m_nAmmoTotal == 0 && !CWeaponInfo::IsWeaponSlotAmmoMergeable(slot))) { + if (CTimer::GetTimeInMilliseconds() - FindPlayerPed()->m_nPadDownPressedInMilliseconds < 1500) { + CPickups::PlayerOnWeaponPickup = 6; + isPickupTouched = false; + } + } else { + CPickups::PlayerOnWeaponPickup = 6; + if (CWeaponInfo::IsWeaponSlotAmmoMergeable(slot)) { + if (m_eType == PICKUP_ONCE_TIMEOUT || m_eType == PICKUP_ONCE || m_eType == PICKUP_ON_STREET) { + ExtractAmmoFromPickup(player); + FindPlayerPed()->GetWeapon(slot).Reload(); + } + } + if (!m_bWasControlMessageShown) { + switch (CPad::GetPad(0)->Mode) + { + case 0: + case 1: + CHud::SetHelpMessage(TheText.Get("PU_CF1"), false); + break; + case 2: + CHud::SetHelpMessage(TheText.Get("PU_CF3"), false); + break; + case 3: + CHud::SetHelpMessage(TheText.Get("PU_CF4"), false); + break; + default: + break; + } + m_bWasControlMessageShown = true; + } + if (CollectPickupBuffer == 0) + isPickupTouched = false; + if (CTimer::GetTimeInMilliseconds() - FindPlayerPed()->m_nPadDownPressedInMilliseconds < 1500) + isPickupTouched = false; + } + } else + isPickupTouched = false; + } + } + } else + m_bWasControlMessageShown = false; + + // if we didn't then we've got nothing to do + if (isPickupTouched && CanBePickedUp(player, playerId)) { + if (m_pObject->GetModelIndex() != MI_PICKUP_PROPERTY && m_pObject->GetModelIndex() != MI_PICKUP_PROPERTY_FORSALE) + CPad::GetPad(0)->StartShake(120, 100); + + eWeaponType weaponType = CPickups::WeaponForModel(m_pObject->GetModelIndex()); + switch (m_eType) + { + case PICKUP_IN_SHOP: + if (CWorld::Players[playerId].m_nMoney < CostOfWeapon[weaponType]) + CGarages::TriggerMessage("PU_MONY", -1, 6000, -1); + else { + CWorld::Players[playerId].m_nMoney -= CostOfWeapon[weaponType]; + if (!CPickups::GivePlayerGoodiesWithPickUpMI(m_pObject->GetModelIndex(), playerId)) { + if (!player->DoesPlayerWantNewWeapon(weaponType, false)) + break; + player->GiveWeapon(weaponType, AmmoForWeapon[weaponType]); + player->m_nSelectedWepSlot = player->GetWeaponSlot(weaponType); + DMAudio.PlayFrontEndSound(SOUND_PICKUP_WEAPON_BOUGHT, m_pObject->GetModelIndex() - MI_GRENADE); + } + result = true; + Remove(); + } + break; + case PICKUP_ON_STREET: + case PICKUP_ON_STREET_SLOW: + if (!CPickups::GivePlayerGoodiesWithPickUpMI(m_pObject->GetModelIndex(), playerId)) { + if (!player->DoesPlayerWantNewWeapon(weaponType, false)) + break; + if (weaponType != WEAPONTYPE_UNARMED) { + player->GiveWeapon(weaponType, m_nQuantity != 0 ? m_nQuantity : (m_bWasAmmoCollected ? 0 : AmmoForWeapon_OnStreet[weaponType]), true); + + if (player->m_nSelectedWepSlot == player->GetWeaponSlot(WEAPONTYPE_UNARMED)) + player->m_nSelectedWepSlot = player->GetWeaponSlot(weaponType); + + DMAudio.PlayFrontEndSound(SOUND_PICKUP_WEAPON, m_pObject->GetModelIndex() - MI_GRENADE); + } else if (m_pObject->GetModelIndex() == MI_PICKUP_CAMERA && vehicle != nil) { + DMAudio.PlayFrontEndSound(SOUND_PICKUP_BONUS, 0); + CPickups::bPickUpcamActivated = true; + CPickups::pPlayerVehicle = FindPlayerVehicle(); + CPickups::StaticCamCoors = m_pObject->GetPosition(); + CPickups::StaticCamStartTime = CTimer::GetTimeInMilliseconds(); + } + } + if (m_eType == PICKUP_ON_STREET) + m_nTimer = CTimer::GetTimeInMilliseconds() + 30000; + else if (m_eType == PICKUP_ON_STREET_SLOW) { + if (MI_PICKUP_BRIBE == m_pObject->GetModelIndex()) + m_nTimer = CTimer::GetTimeInMilliseconds() + 300000; + else + m_nTimer = CTimer::GetTimeInMilliseconds() + 720000; + } + + result = true; + GetRidOfObjects(); + m_bRemoved = true; + break; + case PICKUP_ONCE: + case PICKUP_ONCE_TIMEOUT: + case PICKUP_ONCE_TIMEOUT_SLOW: + if (!CPickups::GivePlayerGoodiesWithPickUpMI(m_pObject->GetModelIndex(), playerId)) { + if (!player->DoesPlayerWantNewWeapon(weaponType, false)) { + ExtractAmmoFromPickup(player); + break; + } + + if (weaponType != WEAPONTYPE_UNARMED) { + player->GiveWeapon(weaponType, m_nQuantity != 0 ? m_nQuantity : (m_bWasAmmoCollected ? 0 : AmmoForWeapon[weaponType]), true); + if (player->m_nSelectedWepSlot == player->GetWeaponSlot(WEAPONTYPE_UNARMED)) + player->m_nSelectedWepSlot = player->GetWeaponSlot(weaponType); + } + if (MI_PICKUP_SAVEGAME != m_pObject->GetModelIndex()) + DMAudio.PlayFrontEndSound(SOUND_PICKUP_WEAPON, m_pObject->GetModelIndex() - MI_GRENADE); + } + result = true; + Remove(); + break; + case PICKUP_COLLECTABLE1: + CWorld::Players[playerId].m_nCollectedPackages++; + CWorld::Players[playerId].m_nMoney += 100; + + if (CWorld::Players[playerId].m_nCollectedPackages == CWorld::Players[playerId].m_nTotalPackages) { + printf("All collectables have been picked up\n"); + CGarages::TriggerMessage("CO_ALL", -1, 5000, -1); + CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 100000; + } else + CGarages::TriggerMessage("CO_ONE", CWorld::Players[CWorld::PlayerInFocus].m_nCollectedPackages, 5000, CWorld::Players[CWorld::PlayerInFocus].m_nTotalPackages); + + result = true; + Remove(); + DMAudio.PlayFrontEndSound(SOUND_PICKUP_HIDDEN_PACKAGE, 0); + break; + case PICKUP_MONEY: + CWorld::Players[playerId].m_nMoney += m_nQuantity; + sprintf(gString, "$%d", m_nQuantity); +#ifdef MONEY_MESSAGES + CMoneyMessages::RegisterOne(m_vecPos + CVector(0.0f, 0.0f, 1.0f), gString, 0, 255, 0, 0.5f, 0.5f); +#endif + result = true; + Remove(); + DMAudio.PlayFrontEndSound(SOUND_PICKUP_MONEY, 0); + player->Say(SOUND_PED_MUGGING); + break; + case PICKUP_ASSET_REVENUE: + CWorld::Players[CWorld::PlayerInFocus].m_nMoney += m_fRevenue; + m_fRevenue = 0.0f; + DMAudio.PlayFrontEndSound(SOUND_PICKUP_MONEY, 0); + break; + case PICKUP_PROPERTY_LOCKED: + if (!m_bWasControlMessageShown) { + m_bWasControlMessageShown = true; + CHud::SetHelpMessage(TheText.Get(m_sTextKey), false); + } + break; + case PICKUP_PROPERTY_FORSALE: + ModifyStringLabelForControlSetting(m_sTextKey); + CMessages::InsertNumberInString(TheText.Get(m_sTextKey), m_nQuantity, + 0, 0, 0, 0, 0, gUString); + if (!CHud::IsHelpMessageBeingDisplayed()) + CHud::SetHelpMessage(gUString, false); + if (CollectPickupBuffer == 0) + break; + if (CTheScripts::IsPlayerOnAMission()) + CHud::SetHelpMessage(TheText.Get("PROP_2"), true); + else { + if (CWorld::Players[CWorld::PlayerInFocus].m_nMoney >= m_nQuantity) { + CWorld::Players[CWorld::PlayerInFocus].m_nMoney -= m_nQuantity; + CHud::SetHelpMessage(nil, true); + result = true; + Remove(); + break; + } + CHud::SetHelpMessage(TheText.Get("PROP_1"), true); + } + break; + default: + break; + } + } + } else { + switch (m_eType) + { + case PICKUP_MINE_INACTIVE: + if (vehicle != nil && !vehicle->IsSphereTouchingVehicle(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z, 2.0f)) { + m_eType = PICKUP_MINE_ARMED; + m_nTimer = CTimer::GetTimeInMilliseconds() + 10000; + } + break; + case PICKUP_NAUTICAL_MINE_INACTIVE: + { + if (CWaterLevel::GetWaterLevel(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z + 5.0f, &waterLevel, false)) + m_pObject->GetMatrix().GetPosition().z = waterLevel + 0.6f; + + m_pObject->GetMatrix().UpdateRW(); + m_pObject->UpdateRwFrame(); + + bool touched = false; + for (int32 i = CPools::GetVehiclePool()->GetSize()-1; i >= 0; i--) { + CVehicle *vehicle = CPools::GetVehiclePool()->GetSlot(i); + if (vehicle != nil && vehicle->IsSphereTouchingVehicle(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z, 1.5f)) { + touched = true; +#ifdef FIX_BUGS + break; // added break here +#endif + } + } + + if (!touched) { + m_eType = PICKUP_NAUTICAL_MINE_ARMED; + m_nTimer = CTimer::GetTimeInMilliseconds() + 10000; + } + break; + } + case PICKUP_NAUTICAL_MINE_ARMED: + if (CWaterLevel::GetWaterLevel(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z + 5.0f, &waterLevel, false)) + m_pObject->GetMatrix().GetPosition().z = waterLevel + 0.6f; + + m_pObject->GetMatrix().UpdateRW(); + m_pObject->UpdateRwFrame(); + // no break here + case PICKUP_MINE_ARMED: + { + bool explode = false; + if (CTimer::GetTimeInMilliseconds() > m_nTimer) + explode = true; +#ifdef FIX_BUGS + else// added else here since vehicle lookup is useless +#endif + { + for (int32 i = CPools::GetVehiclePool()->GetSize()-1; i >= 0; i--) { + CVehicle *vehicle = CPools::GetVehiclePool()->GetSlot(i); + if (vehicle != nil && vehicle->IsSphereTouchingVehicle(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z, 1.5f)) { + explode = true; +#ifdef FIX_BUGS + break; // added break here +#endif + } + } + } + if (explode) { + CExplosion::AddExplosion(nil, nil, EXPLOSION_MINE, m_pObject->GetPosition(), 0); + Remove(); + } + break; + } + case PICKUP_FLOATINGPACKAGE: + m_pObject->m_vecMoveSpeed.z -= 0.01f * CTimer::GetTimeStep(); + m_pObject->GetMatrix().GetPosition() += m_pObject->GetMoveSpeed() * CTimer::GetTimeStep(); + + m_pObject->GetMatrix().UpdateRW(); + m_pObject->UpdateRwFrame(); + if (CWaterLevel::GetWaterLevel(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z + 5.0f, &waterLevel, false) && waterLevel >= m_pObject->GetPosition().z) + m_eType = PICKUP_FLOATINGPACKAGE_FLOATING; + break; + case PICKUP_FLOATINGPACKAGE_FLOATING: + if (CWaterLevel::GetWaterLevel(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z + 5.0f, &waterLevel, false)) + m_pObject->GetMatrix().GetPosition().z = waterLevel; + + m_pObject->GetMatrix().UpdateRW(); + m_pObject->UpdateRwFrame(); + if (vehicle != nil && vehicle->IsSphereTouchingVehicle(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z, 2.0f)) { + Remove(); + result = true; + DMAudio.PlayFrontEndSound(SOUND_PICKUP_FLOAT_PACKAGE, 0); + } + break; + default: break; + } + } + + if (!m_bRemoved && (m_eType == PICKUP_ONCE_TIMEOUT || m_eType == PICKUP_ONCE_TIMEOUT_SLOW || m_eType == PICKUP_MONEY) && CTimer::GetTimeInMilliseconds() > m_nTimer) + Remove(); + + return result; +} + +void +CPickup::ProcessGunShot(CVector *vec1, CVector *vec2) +{ + CColLine line(*vec1, *vec2); + if (m_pObject) { + CColSphere sphere; + sphere.radius = 4.0f; + sphere.center = m_pObject->GetPosition(); + if (CCollision::TestLineSphere(line, sphere)) { + CExplosion::AddExplosion(nil, nil, EXPLOSION_MINE, m_pObject->GetPosition(), 0); + CWorld::Remove(m_pObject); + delete m_pObject; + m_pObject = nil; + m_bRemoved = true; + m_eType = PICKUP_NONE; + } + } +} + +void +CPickup::GetRidOfObjects() +{ + if (m_pObject) { + CWorld::Remove(m_pObject); + delete m_pObject; + m_pObject = nil; + } + if (m_pExtraObject) { + CWorld::Remove(m_pExtraObject); + delete m_pExtraObject; + m_pExtraObject = nil; + } +} + +void +CPickups::Init(void) +{ + NumMessages = 0; + for (int i = 0; i < NUMPICKUPS; i++) { + aPickUps[i].m_eType = PICKUP_NONE; + aPickUps[i].m_nIndex = 1; + aPickUps[i].m_pObject = nil; + aPickUps[i].m_pExtraObject = nil; + } + + for (int i = 0; i < NUMCOLLECTEDPICKUPS; i++) + aPickUpsCollected[i] = 0; + + CollectedPickUpIndex = 0; +} + +bool +CPickups::TestForPickupsInBubble(CVector pos, float range) +{ + for (int i = 0; i < NUMPICKUPS; i++) { + if ((aPickUps[i].m_vecPos - pos).Magnitude() < range) + return true; + } + return false; +} + +bool +CPickups::TryToMerge_WeaponType(CVector pos, eWeaponType weapon, uint8 type, uint32 quantity, bool unused) { + for (int i = 0; i < NUMPICKUPS; i++) { + if (aPickUps[i].m_eType == type && aPickUps[i].m_eModelIndex == ModelForWeapon(weapon)) + if ((aPickUps[i].m_vecPos - pos).Magnitude() < 7.5f) { + aPickUps[i].m_nQuantity += quantity; + return true; + } + } + return false; +} + +bool +CPickups::IsPickUpPickedUp(int32 pickupId) +{ + for (int i = 0; i < NUMCOLLECTEDPICKUPS; i++) { + if (pickupId == aPickUpsCollected[i]) { + aPickUpsCollected[i] = 0; + return true; + } + } + return false; +} + +void +CPickups::PassTime(uint32 time) +{ + for (int i = 0; i < NUMPICKUPS; i++) { + if (aPickUps[i].m_eType != PICKUP_NONE && aPickUps[i].m_eType != PICKUP_ASSET_REVENUE) { + if (aPickUps[i].m_nTimer <= time) + aPickUps[i].m_nTimer = 0; + else + aPickUps[i].m_nTimer -= time; + } + } +} + +int32 +CPickups::GetActualPickupIndex(int32 index) +{ + if (index == -1) return -1; + + // doesn't look nice + if ((uint16)((index & 0xFFFF0000) >> 16) != aPickUps[(uint16)index].m_nIndex) return -1; + return (uint16)index; +} + +bool +CPickups::GivePlayerGoodiesWithPickUpMI(int16 modelIndex, int playerIndex) +{ + CPlayerPed *player; + + if (playerIndex <= 0) player = CWorld::Players[CWorld::PlayerInFocus].m_pPed; + else player = CWorld::Players[playerIndex].m_pPed; + + if (modelIndex == MI_PICKUP_ADRENALINE) { + player->m_bAdrenalineActive = true; + player->m_nAdrenalineTime = CTimer::GetTimeInMilliseconds() + 20000; + player->m_fCurrentStamina = player->m_fMaxStamina; + DMAudio.PlayFrontEndSound(SOUND_PICKUP_ADRENALINE, 0); + return true; + } else if (modelIndex == MI_PICKUP_BODYARMOUR) { + player->m_fArmour = CWorld::Players[playerIndex].m_nMaxArmour; + DMAudio.PlayFrontEndSound(SOUND_PICKUP_ARMOUR, 0); + return true; + } else if (modelIndex == MI_PICKUP_INFO) { + DMAudio.PlayFrontEndSound(SOUND_PICKUP_BONUS, 0); + return true; + } else if (modelIndex == MI_PICKUP_HEALTH) { + player->m_fHealth = CWorld::Players[playerIndex].m_nMaxHealth; + DMAudio.PlayFrontEndSound(SOUND_PICKUP_HEALTH, 0); + return true; + } else if (modelIndex == MI_PICKUP_BONUS) { + DMAudio.PlayFrontEndSound(SOUND_PICKUP_BONUS, 0); + return true; + } else if (modelIndex == MI_PICKUP_BRIBE) { + int32 level = Max(FindPlayerPed()->m_pWanted->GetWantedLevel() - 1, 0); + player->SetWantedLevel(level); + DMAudio.PlayFrontEndSound(SOUND_PICKUP_BONUS, 0); + return true; + } else if (modelIndex == MI_PICKUP_KILLFRENZY) { + DMAudio.PlayFrontEndSound(SOUND_PICKUP_BONUS, 0); + return true; + } + return false; +} + +void +CPickups::RemovePickUp(int32 pickupIndex) +{ + int32 index = GetActualPickupIndex(pickupIndex); + if (index == -1) return; + + if (aPickUps[index].m_pObject) { + CWorld::Remove(aPickUps[index].m_pObject); + delete aPickUps[index].m_pObject; + aPickUps[index].m_pObject = nil; + } + if (aPickUps[index].m_pExtraObject) { + CWorld::Remove(aPickUps[index].m_pExtraObject); + delete aPickUps[index].m_pExtraObject; + aPickUps[index].m_pExtraObject = nil; + } + aPickUps[index].m_eType = PICKUP_NONE; + aPickUps[index].m_bRemoved = true; +} + +int32 +CPickups::GenerateNewOne(CVector pos, uint32 modelIndex, uint8 type, uint32 quantity, uint32 rate, bool highPriority, char* pText) +{ + bool bFreeFound = false; + int32 slot = 0; + + if (type == PICKUP_FLOATINGPACKAGE || type == PICKUP_NAUTICAL_MINE_INACTIVE || highPriority) { + for (slot = NUMPICKUPS-1; slot >= 0; slot--) { + if (aPickUps[slot].m_eType == PICKUP_NONE) { + bFreeFound = true; + break; + } + } + } + if (!bFreeFound) { + for (slot = 0; slot < NUMGENERALPICKUPS; slot++) { + if (aPickUps[slot].m_eType == PICKUP_NONE) { + bFreeFound = true; + break; + } + } + } + + if (!bFreeFound) { + for (slot = 0; slot < NUMGENERALPICKUPS; slot++) { + if (aPickUps[slot].m_eType == PICKUP_MONEY) break; + } + + if (slot >= NUMGENERALPICKUPS) { + for (slot = 0; slot < NUMGENERALPICKUPS; slot++) { + if (aPickUps[slot].m_eType == PICKUP_ONCE_TIMEOUT || aPickUps[slot].m_eType == PICKUP_ONCE_TIMEOUT_SLOW) break; + } + + if (slot >= NUMGENERALPICKUPS) return -1; + aPickUps[slot].GetRidOfObjects(); + } + } + + if (slot >= NUMPICKUPS) return -1; + + aPickUps[slot].m_eType = type; + aPickUps[slot].m_bRemoved = false; + aPickUps[slot].m_nQuantity = quantity; + aPickUps[slot].m_nMoneySpeed = rate; + aPickUps[slot].m_fRevenue = 0.0f; + aPickUps[slot].m_nTimer = CTimer::GetTimeInMilliseconds(); + aPickUps[slot].m_bWasAmmoCollected = highPriority; + aPickUps[slot].m_bWasControlMessageShown = false; + if (type == PICKUP_ONCE_TIMEOUT) + aPickUps[slot].m_nTimer = CTimer::GetTimeInMilliseconds() + 20000; + else if (type == PICKUP_ONCE_TIMEOUT_SLOW) + aPickUps[slot].m_nTimer = CTimer::GetTimeInMilliseconds() + 120000; + else if (type == PICKUP_MONEY) + aPickUps[slot].m_nTimer = CTimer::GetTimeInMilliseconds() + 30000; + else if (type == PICKUP_MINE_INACTIVE || type == PICKUP_MINE_ARMED) { + aPickUps[slot].m_eType = PICKUP_MINE_INACTIVE; + aPickUps[slot].m_nTimer = CTimer::GetTimeInMilliseconds() + 1500; + } else if (type == PICKUP_NAUTICAL_MINE_INACTIVE || type == PICKUP_NAUTICAL_MINE_ARMED) { + aPickUps[slot].m_eType = PICKUP_NAUTICAL_MINE_INACTIVE; + aPickUps[slot].m_nTimer = CTimer::GetTimeInMilliseconds() + 1500; + } + aPickUps[slot].m_eModelIndex = modelIndex; + if (pText) + strncpy(aPickUps[slot].m_sTextKey, pText, 8); + else + aPickUps[slot].m_sTextKey[0] = '\0'; + + aPickUps[slot].m_vecPos = pos; + aPickUps[slot].m_pObject = aPickUps[slot].GiveUsAPickUpObject(&aPickUps[slot].m_pObject, &aPickUps[slot].m_pExtraObject, -1, -1); + if (aPickUps[slot].m_pObject) + CWorld::Add(aPickUps[slot].m_pObject); + if (aPickUps[slot].m_pExtraObject) + CWorld::Add(aPickUps[slot].m_pExtraObject); + return GetNewUniquePickupIndex(slot); +} + +int32 +CPickups::GenerateNewOne_WeaponType(CVector pos, eWeaponType weaponType, uint8 type, uint32 quantity) +{ + return GenerateNewOne(pos, ModelForWeapon(weaponType), type, quantity); +} + +int32 +CPickups::GetNewUniquePickupIndex(int32 slot) +{ + if (aPickUps[slot].m_nIndex >= 0xFFFE) + aPickUps[slot].m_nIndex = 1; + else + aPickUps[slot].m_nIndex++; + return slot | (aPickUps[slot].m_nIndex << 16); +} + +int32 +CPickups::ModelForWeapon(eWeaponType weaponType) +{ + return CWeaponInfo::GetWeaponInfo(weaponType)->m_nModelId; +} + +eWeaponType +CPickups::WeaponForModel(int32 model) +{ + if (model == MI_PICKUP_BODYARMOUR) return WEAPONTYPE_ARMOUR; + if (model == MI_PICKUP_HEALTH) return WEAPONTYPE_HEALTH; + if (model == MI_PICKUP_ADRENALINE) return WEAPONTYPE_ARMOUR; + if (model == -1) return WEAPONTYPE_UNARMED; + return ((CWeaponModelInfo*)CModelInfo::GetModelInfo(model))->GetWeaponInfo(); +} + +void +CPickups::AddToCollectedPickupsArray(int32 index) +{ + aPickUpsCollected[CollectedPickUpIndex++] = index | (aPickUps[index].m_nIndex << 16); + if (CollectedPickUpIndex >= NUMCOLLECTEDPICKUPS) + CollectedPickUpIndex = 0; +} + +void +CPickups::Update() +{ +#ifdef FIX_BUGS // RIP speedrunning (solution from SA) + if (CReplay::IsPlayingBack()) + return; +#endif +#ifdef CAMERA_PICKUP + if ( bPickUpcamActivated ) // taken from PS2 + { + float dist = Distance2D(StaticCamCoors, FindPlayerCoors()); + float mult; + if ( dist < 10.0f ) + mult = 1.0f - (dist / 10.0f ); + else + mult = 0.0f; + + CVector pos = StaticCamCoors; + pos.z += (pPlayerVehicle->GetColModel()->boundingBox.GetSize().z + 2.0f) * mult; + + if ( (CTimer::GetTimeInMilliseconds() - StaticCamStartTime) > 750 ) + { + TheCamera.SetCamPositionForFixedMode(pos, CVector(0.0f, 0.0f, 0.0f)); + TheCamera.TakeControl(FindPlayerVehicle(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_SCRIPT); + } + + if ( FindPlayerVehicle() != pPlayerVehicle || Distance(StaticCamCoors, FindPlayerCoors()) > 40.0f + || ((CTimer::GetTimeInMilliseconds() - StaticCamStartTime) > 60000) ) + { + TheCamera.RestoreWithJumpCut(); + bPickUpcamActivated = false; + } + } +#endif + if (CPad::GetPad(0)->CollectPickupJustDown()) + CollectPickupBuffer = 6; + else + CollectPickupBuffer = Max(0, CollectPickupBuffer - 1); + + if (PlayerOnWeaponPickup) + PlayerOnWeaponPickup = Max(0, PlayerOnWeaponPickup - 1); + +#define PICKUPS_FRAME_SPAN (6) +#ifdef FIX_BUGS + for (uint32 i = NUMGENERALPICKUPS * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN) / PICKUPS_FRAME_SPAN; i < NUMGENERALPICKUPS * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN + 1) / PICKUPS_FRAME_SPAN; i++) { +#else // BUG: this code can only reach 318 out of 320 pickups + for (uint32 i = NUMGENERALPICKUPS / PICKUPS_FRAME_SPAN * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN); i < NUMGENERALPICKUPS / PICKUPS_FRAME_SPAN * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN + 1); i++) { +#endif + if (aPickUps[i].m_eType != PICKUP_NONE && aPickUps[i].Update(FindPlayerPed(), FindPlayerVehicle(), CWorld::PlayerInFocus)) + AddToCollectedPickupsArray(i); + } +#undef PICKUPS_FRAME_SPAN + for (uint32 i = NUMGENERALPICKUPS; i < NUMPICKUPS; i++) { + if (aPickUps[i].m_eType != PICKUP_NONE && aPickUps[i].Update(FindPlayerPed(), FindPlayerVehicle(), CWorld::PlayerInFocus)) + AddToCollectedPickupsArray(i); + } +} + +CPickup* +CPickups::FindPickUpForThisObject(CEntity *object) +{ + for (uint32 i = 0; i < NUMPICKUPS; i++) { + if (aPickUps[i].m_eType != PICKUP_NONE && (aPickUps[i].m_pObject == object || aPickUps[i].m_pExtraObject == object)) { + return &aPickUps[i]; + } + } + return &aPickUps[0]; +} + +void +CPickups::DoPickUpEffects(CEntity *entity) +{ + CPickup *pickup = FindPickUpForThisObject(entity); + + if (entity->GetModelIndex() == MI_PICKUP_KILLFRENZY) + entity->bDoNotRender = CTheScripts::IsPlayerOnAMission() || CDarkel::FrenzyOnGoing() || !CGame::nastyGame; + + if (!entity->bDoNotRender) { + float modifiedSin = 0.3f * (Sin((float)((CTimer::GetTimeInMilliseconds() + (uintptr)entity) & 0x7FF) * DEGTORAD(360.0f / 0x800)) + 1.0f); + +#ifdef FIX_BUGS + int16 colorId = 0; +#else + int16 colorId; +#endif + bool doInnerGlow = false; + bool doOuterGlow = true; + + if (entity->GetModelIndex() == MI_PICKUP_ADRENALINE || entity->GetModelIndex() == MI_PICKUP_CAMERA) { + colorId = WEAPONTYPE_TOTALWEAPONS; + doInnerGlow = true; + doOuterGlow = false; + } else if (entity->GetModelIndex() == MI_PICKUP_BODYARMOUR) { + colorId = WEAPONTYPE_ARMOUR; + } else if (entity->GetModelIndex() == MI_PICKUP_BRIBE) { + doInnerGlow = true; + doOuterGlow = false; + } else if (entity->GetModelIndex() == MI_PICKUP_INFO || entity->GetModelIndex() == MI_PICKUP_KILLFRENZY) { + doInnerGlow = true; + doOuterGlow = false; + } else if (entity->GetModelIndex() == MI_PICKUP_HEALTH || entity->GetModelIndex() == MI_PICKUP_BONUS) { + colorId = WEAPONTYPE_HEALTH; + doInnerGlow = true; + doOuterGlow = false; + } else if (entity->GetModelIndex() == MI_PICKUP_PROPERTY) { + doInnerGlow = true; + doOuterGlow = false; + } else if (entity->GetModelIndex() == MI_PICKUP_PROPERTY_FORSALE) { + doInnerGlow = true; + doOuterGlow = false; + } else if (entity->GetModelIndex() == MI_PICKUP_REVENUE) { + doInnerGlow = true; + doOuterGlow = false; + } else if (entity->GetModelIndex() == MI_PICKUP_SAVEGAME) { + doInnerGlow = true; + doOuterGlow = false; + } else if (entity->GetModelIndex() == MI_PICKUP_CLOTHES) { + colorId = WEAPONTYPE_TOTALWEAPONS; + doOuterGlow = false; + doInnerGlow = true; + } else + colorId = WeaponForModel(entity->GetModelIndex()); + + const CVector& pos = pickup->m_vecPos; + if (doOuterGlow) { + bool corona1 = false; + bool corona2 = false; + int timerVal = (CTimer::GetTimeInMilliseconds() >> 9) & 7; + + if (timerVal < 3) + corona1 = false; + else if (timerVal == 3) + corona1 = (CGeneral::GetRandomNumber() & 3) != 0; + else + corona1 = true; + + timerVal = (timerVal - 1) & 7; + if (timerVal < 3) + corona2 = false; + else if (timerVal == 3) + corona2 = (CGeneral::GetRandomNumber() & 3) != 0; + else + corona2 = true; + + if (((CObject*)entity)->bAmmoCollected) { + corona2 = false; + corona1 = false; + } + + if (corona1) { + CCoronas::RegisterCorona((uintptr)entity, + aPickupColors[colorId].r * 0.45f, aPickupColors[colorId].g * 0.45f, aPickupColors[colorId].b * 0.45f, + 255, pos, 0.76f, 65.0f, + CCoronas::TYPE_RING, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, + 0.0f, false, -0.4f); + CShadows::StoreStaticShadow((uintptr)entity, + SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &pos, 2.0f, 0.0f, 0.0f, -2.0f, 0, + aPickupColors[colorId].r * 0.3f, aPickupColors[colorId].g * 0.3f, aPickupColors[colorId].b * 0.3f, + 4.0f, 1.0f, 40.0f, false, 0.0f); + float radius = (CGeneral::GetRandomNumber() & 0xF) * 0.1f + 3.0f; + CPointLights::AddLight(CPointLights::LIGHT_POINT, pos, CVector(0.0f, 0.0f, 0.0f), radius, aPickupColors[colorId].r / 256.0f, aPickupColors[colorId].g / 256.0f, aPickupColors[colorId].b / 256.0f, CPointLights::FOG_NONE, true); + } else + CCoronas::RegisterCorona((uintptr)entity, 0, 0, 0, 255, pos, 0.57f, 65.0f, CCoronas::TYPE_RING, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + + if (corona2) { + CCoronas::RegisterCorona( + (uintptr)entity + 1, + aPickupColors[colorId].r * 0.55f, aPickupColors[colorId].g * 0.55f, aPickupColors[colorId].b * 0.55f, + 255, + pos, + 0.6f, + 65.0f, + CCoronas::TYPE_RING, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, + 0.0f, false, -0.4f); + if (!corona1) + CShadows::StoreStaticShadow((uintptr)entity, SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &pos, 2.0f, 0.0f, 0.0f, -2.0f, 0, + aPickupColors[colorId].r * 0.25f, aPickupColors[colorId].g * 0.25f, aPickupColors[colorId].b * 0.25f, + 4.0f, 1.0f, 40.0f, false, 0.0f); + } else + CCoronas::RegisterCorona((uintptr)entity + 1, 0, 0, 0, 255, pos, 0.45f, 65.0f, CCoronas::TYPE_RING, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + } + + CObject *object = (CObject*)entity; + if (object->bPickupObjWithMessage || object->bOutOfStock || object->m_nBonusValue || object->m_nCostValue) { + + float dist = Distance2D(pos, TheCamera.GetPosition()); + const float MAXDIST = 14.0f; + + if (dist < MAXDIST && NumMessages < NUMPICKUPMESSAGES) { + RwV3d vecOut; + float fDistX, fDistY; + if (CSprite::CalcScreenCoors(entity->GetPosition() + CVector(0.0f, 0.0f, 0.7f), &vecOut, &fDistX, &fDistY, true)) { + aMessages[NumMessages].m_pos.x = vecOut.x; + aMessages[NumMessages].m_pos.y = vecOut.y; + aMessages[NumMessages].m_dist.x = fDistX; + aMessages[NumMessages].m_dist.y = fDistY; + aMessages[NumMessages].m_weaponType = WeaponForModel(entity->GetModelIndex()); + aMessages[NumMessages].m_color.red = aPickupColors[colorId].r; + aMessages[NumMessages].m_color.green = aPickupColors[colorId].g; + aMessages[NumMessages].m_color.blue = aPickupColors[colorId].b; + aMessages[NumMessages].m_color.alpha = (1.0f - dist / MAXDIST) * 128.0f; + aMessages[NumMessages].m_bOutOfStock = object->bOutOfStock; + aMessages[NumMessages].m_quantity = object->m_nBonusValue; + aMessages[NumMessages].money = object->m_nCostValue; + NumMessages++; + } + } + } + + uint32 model = entity->GetModelIndex(); + CColModel *colModel = entity->GetColModel(); + CVector colLength = colModel->boundingBox.max - colModel->boundingBox.min; + float maxDimension = Max(colLength.x, Max(colLength.y, colLength.z)); + + float scale = (Max(1.f, 1.2f / maxDimension) - 1.0f) * 0.6f + 1.0f; + if (model == MI_MINIGUN || model == MI_MINIGUN2) + scale = 1.2f; + + float angle = (float)(CTimer::GetTimeInMilliseconds() & 0x7FF) * DEGTORAD(360.0f / 0x800); + float c = Cos(angle) * scale; + float s = Sin(angle) * scale; + + // we know from SA they were setting each field manually like this + entity->GetMatrix().rx = c; + entity->GetMatrix().ry = s; + entity->GetMatrix().rz = 0.0f; + entity->GetMatrix().fx = -s; + entity->GetMatrix().fy = c; + entity->GetMatrix().fz = 0.0f; + entity->GetMatrix().ux = 0.0f; + entity->GetMatrix().uy = 0.0f; + entity->GetMatrix().uz = scale; + + if (entity->GetModelIndex() == MI_MINIGUN2) { + CMatrix matrix1; + CMatrix matrix2; // unused + entity->SetPosition(pickup->m_vecPos); + matrix1.SetRotateX(0.0f); + matrix1.Rotate(DEGTORAD(4.477f), DEGTORAD(-29.731f), DEGTORAD(-1.064f)); + matrix1.Translate(CVector(0.829f, -0.001f, 0.226f)); + entity->GetMatrix() *= matrix1; + } + + if (doOuterGlow) { + CVector scale(0.0f, 0.0f, 0.0f); + if (colLength.x == maxDimension) + scale.x = colLength.x; + else if (colLength.y == maxDimension) + scale.y = colLength.y; + else + scale.z = colLength.z; + + for (int i = 0; i < 4; i++) { + CVector pos = entity->GetMatrix() * (scale * ((float)i / 3.0f)); + CCoronas::RegisterCorona( + (uintptr)entity + 8 + i, + aPickupColors[colorId].r * 0.15f, + aPickupColors[colorId].g * 0.15f, + aPickupColors[colorId].b * 0.15f, + 255, + pos, + 1.0f, + 65.0f, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, + CCoronas::REFLECTION_OFF, + CCoronas::LOSCHECK_OFF, + CCoronas::STREAK_OFF, + 0.0f, + false, + -0.5f); + } + } + + if (doInnerGlow) + CCoronas::RegisterCorona( +#ifdef FIX_BUGS + (uintptr)entity + 8 + 4, +#else + (uintptr)entity + 9, +#endif + 126, 69, 121, 255, entity->GetPosition(), 1.2f, 50.0f, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + } +} + +void +CPickups::DoMineEffects(CEntity *entity) +{ + const CVector &pos = entity->GetPosition(); + float dist = Distance(pos, TheCamera.GetPosition()); + const float MAXDIST = 20.0f; + + if (dist < MAXDIST) { + float s = Sin((float)((CTimer::GetTimeInMilliseconds() + (uintptr)entity) & 0x1FF) * DEGTORAD(360.0f / 0x200)); + + int32 red = (MAXDIST - dist) * (0.5f * s + 0.5f) / MAXDIST * 64.0f; + CShadows::StoreStaticShadow((uintptr)entity, SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &pos, 2.0f, 0.0f, 0.0f, -2.0f, 0, red, 0, 0, 4.0f, 1.0f, 40.0f, + false, 0.0f); + CCoronas::RegisterCorona((uintptr)entity, red, 0, 0, 255, pos, 0.6f, 60.0f, CCoronas::TYPE_RING, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + } + + entity->GetMatrix().SetRotateZOnly((float)(CTimer::GetTimeInMilliseconds() & 0x3FF) * DEGTORAD(360.0f / 0x400)); +} + +void +CPickups::DoMoneyEffects(CEntity *entity) +{ + const CVector &pos = entity->GetPosition(); + float dist = Distance(pos, TheCamera.GetPosition()); + const float MAXDIST = 20.0f; + + if (dist < MAXDIST) { + float s = Sin((float)((CTimer::GetTimeInMilliseconds() + (uintptr)entity) & 0x3FF) * DEGTORAD(360.0f / 0x400)); + + int32 green = (MAXDIST - dist) * (0.2f * s + 0.3f) / MAXDIST * 64.0f; + CShadows::StoreStaticShadow((uintptr)entity, SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &pos, 2.0f, 0.0f, 0.0f, -2.0f, 0, 0, green, 0, 4.0f, 1.0f, + 40.0f, false, 0.0f); + CCoronas::RegisterCorona((uintptr)entity, 0, green, 0, 255, pos, 0.4f, 40.0f, CCoronas::TYPE_RING, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + } + + entity->GetMatrix().SetRotateZOnly((float)(CTimer::GetTimeInMilliseconds() & 0x7FF) * DEGTORAD(360.0f / 0x800)); +} + +void +CPickups::DoCollectableEffects(CEntity *entity) +{ + const CVector &pos = entity->GetPosition(); + float dist = Distance(pos, TheCamera.GetPosition()); + const float MAXDIST = 14.0f; + + if (dist < MAXDIST) { + float s = Sin((float)((CTimer::GetTimeInMilliseconds() + (uintptr)entity) & 0x7FF) * DEGTORAD(360.0f / 0x800)); + + int32 color = (MAXDIST - dist) * (0.5f * s + 0.5f) / MAXDIST * 255.0f; + CShadows::StoreStaticShadow((uintptr)entity, SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &pos, 2.0f, 0.0f, 0.0f, -2.0f, 0, color, color, color, 4.0f, + 1.0f, 40.0f, false, 0.0f); + CCoronas::RegisterCorona((uintptr)entity, color, color, color, 255, pos, 0.6f, 40.0f, CCoronas::TYPE_HEX, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + } + + entity->GetMatrix().SetRotateZOnly((float)(CTimer::GetTimeInMilliseconds() & 0xFFF) * DEGTORAD(360.0f / 0x1000)); +} + +void +CPickups::RenderPickUpText() +{ + wchar *strToPrint; + for (int32 i = 0; i < NumMessages; i++) { + + if (aMessages[i].money != 0) { + sprintf(gString, "$%d", aMessages[i].money); + AsciiToUnicode(gString, gUString); + strToPrint = gUString; + } else { + switch (aMessages[i].m_quantity) // could use some enum maybe + { + case 0: + if (aMessages[i].m_weaponType == WEAPONTYPE_HEALTH || aMessages[i].m_weaponType == WEAPONTYPE_ARMOUR) { + strToPrint = nil; + } else { + if (aMessages[i].m_bOutOfStock) + strToPrint = TheText.Get("STOCK"); + else { + sprintf(gString, "$%d", CostOfWeapon[aMessages[i].m_weaponType]); + AsciiToUnicode(gString, gUString); + strToPrint = gUString; + } + } + break; + case 1: + strToPrint = TheText.Get("OUTFT1"); + break; + case 2: + strToPrint = TheText.Get("OUTFT2"); + break; + case 3: + strToPrint = TheText.Get("OUTFT3"); + break; + case 4: + strToPrint = TheText.Get("OUTFT4"); + break; + case 5: + strToPrint = TheText.Get("OUTFT5"); + break; + case 6: + strToPrint = TheText.Get("OUTFT6"); + break; + case 7: + strToPrint = TheText.Get("OUTFT7"); + break; + case 8: + strToPrint = TheText.Get("OUTFT8"); + break; + case 9: + strToPrint = TheText.Get("OUTFT9"); + break; + case 10: + strToPrint = TheText.Get("OUTFT10"); + break; + case 11: + strToPrint = TheText.Get("OUTFT11"); + break; + case 12: + strToPrint = TheText.Get("OUTFT12"); + break; + case 13: + strToPrint = TheText.Get("OUTFT13"); + break; + default: + break; + } + } + if (strToPrint == nil) + continue; + CFont::SetPropOn(); + CFont::SetBackgroundOff(); + +#ifdef FIX_BUGS + const float MAX_SCALE = SCREEN_WIDTH / DEFAULT_SCREEN_WIDTH; +#else + float MAX_SCALE = RsGlobal.width / DEFAULT_SCREEN_WIDTH; +#endif + + float fScaleY = aMessages[i].m_dist.y / 30.0f; + if (fScaleY > MAX_SCALE) fScaleY = MAX_SCALE; + + float fScaleX = aMessages[i].m_dist.x / 30.0f; + if (fScaleX > MAX_SCALE) fScaleX = MAX_SCALE; + + CFont::SetScale(fScaleX, fScaleY); // this shouldn't be scaled + CFont::SetCentreOn(); + CFont::SetCentreSize(SCREEN_WIDTH); + CFont::SetJustifyOff(); + + CFont::SetColor(CRGBA(aMessages[i].m_color.red, aMessages[i].m_color.green, aMessages[i].m_color.blue, aMessages[i].m_color.alpha)); + CFont::SetBackGroundOnlyTextOff(); + CFont::SetFontStyle(FONT_STANDARD); + CFont::PrintString(aMessages[i].m_pos.x, aMessages[i].m_pos.y, strToPrint); + } + NumMessages = 0; +} + +void +CPickups::CreateSomeMoney(CVector pos, int money) +{ + bool found; + + int pickupCount = Min(money / 20 + 1, 7); + int moneyPerPickup = money / pickupCount; + + for (int i = 0; i < pickupCount; i++) { + // (CGeneral::GetRandomNumber() % 256) * PI / 128 gives a float up to something TWOPI-ish. + pos.x += 1.5f * Sin((CGeneral::GetRandomNumber() % 256) * PI / 128); + pos.y += 1.5f * Cos((CGeneral::GetRandomNumber() % 256) * PI / 128); + pos.z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &found) + 0.5f; + if (found) { + CPickups::GenerateNewOne(CVector(pos.x, pos.y, pos.z), MI_MONEY, PICKUP_MONEY, moneyPerPickup + (CGeneral::GetRandomNumber() & 3)); + } + } +} + +void +CPickups::RemoveAllPickupsOfACertainWeaponGroupWithNoAmmo(eWeaponType weaponType) +{ + uint32 weaponSlot = CWeaponInfo::GetWeaponInfo(weaponType)->m_nWeaponSlot; + if (CWeaponInfo::IsWeaponSlotAmmoMergeable(weaponSlot)) { + for (int slot = 0; slot < NUMPICKUPS; slot++) { + if (aPickUps[slot].m_eType == PICKUP_ONCE || aPickUps[slot].m_eType == PICKUP_ONCE_TIMEOUT || aPickUps[slot].m_eType == PICKUP_ONCE_TIMEOUT_SLOW) { + if (aPickUps[slot].m_pObject) { + if (CWeaponInfo::GetWeaponInfo(WeaponForModel(aPickUps[slot].m_pObject->GetModelIndex()))->m_nWeaponSlot == weaponSlot && + aPickUps[slot].m_nQuantity == 0) { + CWorld::Remove(aPickUps[slot].m_pObject); + delete aPickUps[slot].m_pObject; + aPickUps[slot].m_bRemoved = true; + aPickUps[slot].m_pObject = nil; + aPickUps[slot].m_eType = PICKUP_NONE; + } + } + } + } + } +} + +void +CPickups::DetonateMinesHitByGunShot(CVector *vec1, CVector *vec2) +{ + for (int i = 0; i < NUMGENERALPICKUPS; i++) { + if (aPickUps[i].m_eType == PICKUP_NAUTICAL_MINE_ARMED) + aPickUps[i].ProcessGunShot(vec1, vec2); + } +} + +void +CPickups::RemoveUnnecessaryPickups(const CVector& center, float radius) +{ + for (int i = 0; i < NUMPICKUPS; i++) { + if (aPickUps[i].m_eType == PICKUP_ONCE_TIMEOUT || aPickUps[i].m_eType == PICKUP_MONEY) { + if (Distance(center, aPickUps[i].m_vecPos) < radius) { + aPickUps[i].GetRidOfObjects(); + aPickUps[i].m_bRemoved = true; + aPickUps[i].m_eType = PICKUP_NONE; + } + } + } +} + +void +CPickups::Load(uint8 *buf, uint32 size) +{ +INITSAVEBUF + + for (int32 i = 0; i < NUMPICKUPS; i++) { +#ifdef COMPATIBLE_SAVES + ReadSaveBuf(&aPickUps[i].m_vecPos, buf); + ReadSaveBuf(&aPickUps[i].m_fRevenue, buf); + int32 tmp_pObject; + ReadSaveBuf(&tmp_pObject, buf); + int32 tmp_pExtraObject; + ReadSaveBuf(&tmp_pExtraObject, buf); + ReadSaveBuf(&aPickUps[i].m_nQuantity, buf); + ReadSaveBuf(&aPickUps[i].m_nTimer, buf); + ReadSaveBuf(&aPickUps[i].m_nMoneySpeed, buf); + ReadSaveBuf(&aPickUps[i].m_eModelIndex, buf); + ReadSaveBuf(&aPickUps[i].m_nIndex, buf); + memcpy(aPickUps[i].m_sTextKey, buf, sizeof(aPickUps[i].m_sTextKey)); + SkipSaveBuf(buf, sizeof(aPickUps[i].m_sTextKey)); + ReadSaveBuf(&aPickUps[i].m_eType, buf); + ReadSaveBuf(&aPickUps[i].m_bRemoved, buf); + uint8 flags; + ReadSaveBuf(&flags, buf); + aPickUps[i].m_bWasAmmoCollected = !!(flags & BIT(0)); + aPickUps[i].m_bWasControlMessageShown = !!(flags & BIT(1)); + SkipSaveBuf(buf, 3); + + aPickUps[i].m_pObject = aPickUps[i].m_eType != PICKUP_NONE && tmp_pObject != 0 ? CPools::GetObjectPool()->GetSlot(tmp_pObject - 1) : nil; + aPickUps[i].m_pExtraObject = aPickUps[i].m_eType != PICKUP_NONE && tmp_pExtraObject != 0 ? CPools::GetObjectPool()->GetSlot(tmp_pExtraObject - 1) : nil; +#else + ReadSaveBuf(&aPickUps[i], buf); + + if (aPickUps[i].m_eType != PICKUP_NONE) { + if (aPickUps[i].m_pObject != nil) + aPickUps[i].m_pObject = CPools::GetObjectPool()->GetSlot((uintptr)aPickUps[i].m_pObject - 1); + if (aPickUps[i].m_pExtraObject != nil) + aPickUps[i].m_pExtraObject = CPools::GetObjectPool()->GetSlot((uintptr)aPickUps[i].m_pExtraObject - 1); + } +#endif + } + + ReadSaveBuf(&CollectedPickUpIndex, buf); + SkipSaveBuf(buf, 2); + NumMessages = 0; + + for (uint16 i = 0; i < NUMCOLLECTEDPICKUPS; i++) + ReadSaveBuf(&aPickUpsCollected[i], buf); + +VALIDATESAVEBUF(size) +} + +void +CPickups::Save(uint8 *buf, uint32 *size) +{ + *size = PICKUPS_SAVE_SIZE; + *size += sizeof(uint16) + sizeof(uint16) + sizeof(aPickUpsCollected); + +INITSAVEBUF + + for (int32 i = 0; i < NUMPICKUPS; i++) { +#ifdef COMPATIBLE_SAVES + WriteSaveBuf(buf, aPickUps[i].m_vecPos); + WriteSaveBuf(buf, aPickUps[i].m_fRevenue); + int32 tmp = aPickUps[i].m_eType != PICKUP_NONE && aPickUps[i].m_pObject != nil ? CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(aPickUps[i].m_pObject) + 1 : 0; + WriteSaveBuf(buf, tmp); + tmp = aPickUps[i].m_eType != PICKUP_NONE && aPickUps[i].m_pExtraObject != nil ? CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(aPickUps[i].m_pExtraObject) + 1 : 0; + WriteSaveBuf(buf, tmp); + WriteSaveBuf(buf, aPickUps[i].m_nQuantity); + WriteSaveBuf(buf, aPickUps[i].m_nTimer); + WriteSaveBuf(buf, aPickUps[i].m_nMoneySpeed); + WriteSaveBuf(buf, aPickUps[i].m_eModelIndex); + WriteSaveBuf(buf, aPickUps[i].m_nIndex); + memcpy(buf, aPickUps[i].m_sTextKey, sizeof(aPickUps[i].m_sTextKey)); + SkipSaveBuf(buf, sizeof(aPickUps[i].m_sTextKey)); + WriteSaveBuf(buf, aPickUps[i].m_eType); + WriteSaveBuf(buf, aPickUps[i].m_bRemoved); + uint8 flags = 0; + if (aPickUps[i].m_bWasAmmoCollected) flags |= BIT(0); + if (aPickUps[i].m_bWasControlMessageShown) flags |= BIT(1); + WriteSaveBuf(buf, flags); + ZeroSaveBuf(buf, 3); +#else + CPickup *buf_pickup = WriteSaveBuf(buf, aPickUps[i]); + if (buf_pickup->m_eType != PICKUP_NONE) { + if (buf_pickup->m_pObject != nil) + buf_pickup->m_pObject = (CObject*)(CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(buf_pickup->m_pObject) + 1); + if (buf_pickup->m_pExtraObject != nil) + buf_pickup->m_pExtraObject = (CObject*)(CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(buf_pickup->m_pExtraObject) + 1); + } +#endif + } + + WriteSaveBuf(buf, CollectedPickUpIndex); + WriteSaveBuf(buf, (uint16)0); // possibly was NumMessages + + for (uint16 i = 0; i < NUMCOLLECTEDPICKUPS; i++) + WriteSaveBuf(buf, aPickUpsCollected[i]); + +VALIDATESAVEBUF(*size) +} + +void +CPacManPickup::Update() +{ +} + +int32 CollectGameState; +int16 ThingsToCollect; + +CPacManPickup CPacManPickups::aPMPickUps[NUMPACMANPICKUPS]; +CVector CPacManPickups::LastPickUpCoors; +int CPacManPickups::PillsEatenInRace; +bool CPacManPickups::bPMActive; + +void +CPacManPickups::Init() +{ +} + +void +CPacManPickups::Update() +{ +} + +void +CPacManPickups::GeneratePMPickUps(CVector pos, float scrambleMult, int16 count, uint8 type) +{ +} + +// diablo porn mission pickups +static const CVector aRacePoints1[] = { + CVector(913.62219f, -155.13692f, 4.9699469f), + CVector(913.92401f, -124.12943f, 4.9692569f), + CVector(913.27899f, -93.524231f, 7.4325991f), + CVector(912.60852f, -63.15905f, 7.4533591f), + CVector(934.22144f, -42.049122f, 7.4511471f), + CVector(0.0f, 0.0f, 0.0f), +}; + +void +CPacManPickups::GeneratePMPickUpsForRace(int32 race) +{ +} + +void +CPacManPickups::GenerateOnePMPickUp(CVector pos) +{ +} + +void +CPacManPickups::Render() +{ +} + +void +CPacManPickups::ClearPMPickUps() +{ +} + +void +CPacManPickups::StartPacManRace(int32 race) +{ +} + +void +CPacManPickups::StartPacManRecord() +{ +} + +uint32 +CPacManPickups::QueryPowerPillsEatenInRace() +{ + return 0; +} + +void +CPacManPickups::ResetPowerPillsEatenInRace() +{ +} + +void +CPacManPickups::CleanUpPacManStuff() +{ +} + +void +CPacManPickups::StartPacManScramble(CVector pos, float scrambleMult, int16 count) +{ +} + +uint32 +CPacManPickups::QueryPowerPillsCarriedByPlayer() +{ + return 0; +} + +void +CPacManPickups::ResetPowerPillsCarriedByPlayer() +{ +} + +void +CPed::CreateDeadPedMoney(void) +{ + if (!CGame::nastyGame) + return; + + int mi = GetModelIndex(); + + if ((mi >= MI_COP && mi <= MI_FIREMAN) || (CharCreatedBy == MISSION_CHAR && !bMoneyHasBeenGivenByScript) || bInVehicle) + return; + + int money = m_nPedMoney; + if (money < 10) + return; + + CVector pickupPos = GetPosition(); + CPickups::CreateSomeMoney(pickupPos, money); + m_nPedMoney = 0; +} + +void +CPed::CreateDeadPedWeaponPickups(void) +{ + CVector pickupPos; + + if (bInVehicle) + return; + + for(int i = 0; i < TOTAL_WEAPON_SLOTS; i++) { + + eWeaponType weapon = GetWeapon(i).m_eWeaponType; + int weaponAmmo = GetWeapon(i).m_nAmmoTotal; + if (weapon == WEAPONTYPE_UNARMED || weapon == WEAPONTYPE_DETONATOR || (weaponAmmo == 0 && !GetWeapon(i).IsTypeMelee())) + continue; + + int quantity = Min(weaponAmmo, AmmoForWeapon_OnStreet[weapon] / 2); + CreateDeadPedPickupCoors(&pickupPos.x, &pickupPos.y, &pickupPos.z); + pickupPos.z += 0.3f; + if (!CPickups::TryToMerge_WeaponType(pickupPos, weapon, PICKUP_ONCE_TIMEOUT, quantity, false)) { + CPickups::GenerateNewOne_WeaponType(pickupPos, weapon, PICKUP_ONCE_TIMEOUT, Min(weaponAmmo, quantity)); + } + } + ClearWeapons(); +} + +void +CPed::CreateDeadPedPickupCoors(float *x, float *y, float *z) +{ + bool found = false; + CVector pickupPos; + +#define NUMBER_OF_ATTEMPTS 32 + for (int i = 0; i < NUMBER_OF_ATTEMPTS; i++) { + + pickupPos = GetPosition(); + pickupPos.x = 1.5f * Sin((CGeneral::GetRandomNumber() % 256)/256.0f * TWOPI) + GetPosition().x; + pickupPos.y = 1.5f * Cos((CGeneral::GetRandomNumber() % 256)/256.0f * TWOPI) + GetPosition().y; + pickupPos.z = CWorld::FindGroundZFor3DCoord(pickupPos.x, pickupPos.y, pickupPos.z, &found) + 0.5f; + + if (!found) + continue; + + CVector pedPos = GetPosition(); + pedPos.z += 0.3f; + + CVector pedToPickup = pickupPos - pedPos; + float distance = pedToPickup.Magnitude(); + + // outer edge of pickup + distance = (distance + 0.4f) / distance; + CVector pickupPos2 = pedPos; + pickupPos2 += distance * pedToPickup; + + if ((pickupPos - FindPlayerCoors()).Magnitude2D() > 2.0f || i > NUMBER_OF_ATTEMPTS / 2) { + + if (i > NUMBER_OF_ATTEMPTS / 2 || !CPickups::TestForPickupsInBubble(pickupPos, 1.3f)) { + + if (CWorld::GetIsLineOfSightClear(pickupPos2, pedPos, + true, i < NUMBER_OF_ATTEMPTS / 2, false, i < NUMBER_OF_ATTEMPTS / 2, false, false, false)) { + + if (i > NUMBER_OF_ATTEMPTS / 2 || !CWorld::TestSphereAgainstWorld(pickupPos, 1.2f, nil, false, true, false, false, false, false)) { + *x = pickupPos.x; + *y = pickupPos.y; + *z = pickupPos.z; + return; + } + } + } + } + } + *x = GetPosition().x; + *y = GetPosition().y; + *z = GetPosition().z + 0.4f; +#undef NUMBER_OF_ATTEMPTS +} \ No newline at end of file diff --git a/src/miami/control/Pickups.h b/src/miami/control/Pickups.h new file mode 100644 index 00000000..0de7f827 --- /dev/null +++ b/src/miami/control/Pickups.h @@ -0,0 +1,170 @@ +#pragma once +#include "Weapon.h" + +enum ePickupType +{ + PICKUP_NONE = 0, + PICKUP_IN_SHOP, + PICKUP_ON_STREET, + PICKUP_ONCE, + PICKUP_ONCE_TIMEOUT, + PICKUP_ONCE_TIMEOUT_SLOW, + PICKUP_COLLECTABLE1, + PICKUP_IN_SHOP_OUT_OF_STOCK, + PICKUP_MONEY, + PICKUP_MINE_INACTIVE, + PICKUP_MINE_ARMED, + PICKUP_NAUTICAL_MINE_INACTIVE, + PICKUP_NAUTICAL_MINE_ARMED, + PICKUP_FLOATINGPACKAGE, + PICKUP_FLOATINGPACKAGE_FLOATING, + PICKUP_ON_STREET_SLOW, + PICKUP_ASSET_REVENUE, + PICKUP_PROPERTY_LOCKED, + PICKUP_PROPERTY_FORSALE, + PICKUP_NUMOFTYPES +}; + +class CEntity; +class CObject; +class CVehicle; +class CPlayerPed; + +class CPickup +{ +public: + CVector m_vecPos; + float m_fRevenue; + CObject *m_pObject; + CObject *m_pExtraObject; + uint32 m_nQuantity; + uint32 m_nTimer; + uint16 m_nMoneySpeed; + int16 m_eModelIndex; + uint16 m_nIndex; + char m_sTextKey[8]; + uint8 m_eType; + bool m_bRemoved; + uint8 m_bWasAmmoCollected:1; + uint8 m_bWasControlMessageShown:1; + + CObject *GiveUsAPickUpObject(CObject **object, CObject **extraObject, int32 handle, int32 extraHandle); + bool Update(CPlayerPed *player, CVehicle *vehicle, int playerId); + void GetRidOfObjects(); + void ExtractAmmoFromPickup(CPlayerPed *player); + void ProcessGunShot(CVector *vec1, CVector *vec2); +private: + inline bool IsMine() { return m_eType >= PICKUP_MINE_INACTIVE && m_eType <= PICKUP_FLOATINGPACKAGE_FLOATING; } + inline bool CanBePickedUp(CPlayerPed *player, int playerId); + inline void Remove(); +}; + +VALIDATE_SIZE(CPickup, 0x1C); + +struct tPickupMessage +{ + CVector2D m_pos; + eWeaponType m_weaponType; + CVector2D m_dist; + CRGBA m_color; + uint8 m_bOutOfStock; + uint8 m_quantity; + uint16 money; +}; + +class CPickups +{ + static int32 aPickUpsCollected[NUMCOLLECTEDPICKUPS]; + static int16 CollectedPickUpIndex; + static int16 NumMessages; + static tPickupMessage aMessages[NUMPICKUPMESSAGES]; +public: + static int32 PlayerOnWeaponPickup; + + static void Init(); + static void Update(); + static void RenderPickUpText(); + static void DoCollectableEffects(CEntity *ent); + static void DoMoneyEffects(CEntity *ent); + static void DoMineEffects(CEntity *ent); + static void DoPickUpEffects(CEntity *ent); + static int32 GenerateNewOne(CVector pos, uint32 modelIndex, uint8 type, uint32 quantity, uint32 rate = 0, bool highPriority = false, char* pText = nil); + static int32 GenerateNewOne_WeaponType(CVector pos, eWeaponType weaponType, uint8 type, uint32 quantity); + static void RemovePickUp(int32 pickupIndex); + static void AddToCollectedPickupsArray(int32 index); + static bool IsPickUpPickedUp(int32 pickupId); + static int32 ModelForWeapon(eWeaponType weaponType); + static enum eWeaponType WeaponForModel(int32 model); + static int32 GetActualPickupIndex(int32 index); + static int32 GetNewUniquePickupIndex(int32 slot); + static void PassTime(uint32 time); + static bool GivePlayerGoodiesWithPickUpMI(int16 modelIndex, int playerIndex); + static bool TestForPickupsInBubble(CVector pos, float range); + static bool TryToMerge_WeaponType(CVector pos, eWeaponType weapon, uint8 type, uint32 quantity, bool unused); + static void CreateSomeMoney(CVector, int); + static void DetonateMinesHitByGunShot(CVector *vec1, CVector *vec2); + static void RemoveUnnecessaryPickups(const CVector& center, float radius); + static void Load(uint8 *buf, uint32 size); + static void Save(uint8 *buf, uint32 *size); + + static CPickup aPickUps[NUMPICKUPS]; + + // unused + static bool bPickUpcamActivated; + static CVehicle *pPlayerVehicle; + static CVector StaticCamCoors; + static uint32 StaticCamStartTime; + + static void RemoveAllPickupsOfACertainWeaponGroupWithNoAmmo(eWeaponType); + static CPickup *FindPickUpForThisObject(CEntity*); +}; + +extern uint16 AmmoForWeapon[WEAPONTYPE_TOTALWEAPONS + 1]; +extern uint16 AmmoForWeapon_OnStreet[WEAPONTYPE_TOTALWEAPONS + 1]; +extern uint16 CostOfWeapon[WEAPONTYPE_TOTALWEAPONS + 3]; + +extern int32 CollectPickupBuffer; + +enum ePacmanPickupType +{ + PACMAN_NONE, + PACMAN_SCRAMBLE, + PACMAN_RACE, +}; + +class CPacManPickup +{ +public: + CVector m_vecPosn; + CObject *m_pObject; + uint8 m_eType; + + void Update(); +}; + +class CPacManPickups +{ + friend class CPacManPickup; + + static CPacManPickup aPMPickUps[NUMPACMANPICKUPS]; + static CVector LastPickUpCoors; + static int PillsEatenInRace; + static bool bPMActive; +public: + static void Init(void); + static void Update(void); + static void GeneratePMPickUps(CVector, float, int16, uint8); + static void GeneratePMPickUpsForRace(int32); + static void GenerateOnePMPickUp(CVector); + static void Render(void); + static void StartPacManRace(int32); + static void StartPacManRecord(void); + static uint32 QueryPowerPillsEatenInRace(void); + static void ResetPowerPillsEatenInRace(void); + static void ClearPMPickUps(void); + static void CleanUpPacManStuff(void); + static void StartPacManScramble(CVector, float, int16); + static uint32 QueryPowerPillsCarriedByPlayer(void); + static void ResetPowerPillsCarriedByPlayer(void); + +}; diff --git a/src/miami/control/PowerPoints.cpp b/src/miami/control/PowerPoints.cpp new file mode 100644 index 00000000..9a74e8d9 --- /dev/null +++ b/src/miami/control/PowerPoints.cpp @@ -0,0 +1,22 @@ +#include "common.h" +#include "PowerPoints.h" + +// Some cut beta feature + +void CPowerPoint::Update() +{} + +void CPowerPoints::Init() +{} + +void CPowerPoints::Update() +{} + +void CPowerPoints::GenerateNewOne(float, float, float, float, float, float, uint8) +{} + +void CPowerPoints::Save(uint8**, uint32*) +{} + +void CPowerPoints::Load(uint8*, uint32) +{} \ No newline at end of file diff --git a/src/miami/control/PowerPoints.h b/src/miami/control/PowerPoints.h new file mode 100644 index 00000000..ee3750cd --- /dev/null +++ b/src/miami/control/PowerPoints.h @@ -0,0 +1,26 @@ +#pragma once + +enum +{ + POWERPOINT_NONE = 0, + POWERPOINT_HEALTH, + POWERPOINT_HIDEOUT_INDUSTRIAL, + POWERPOINT_HIDEOUT_COMMERCIAL, + POWERPOINT_HIDEOUT_SUBURBAN +}; + +class CPowerPoint +{ +public: + void Update(); +}; + +class CPowerPoints +{ +public: + static void Init(); + static void Update(); + static void GenerateNewOne(float, float, float, float, float, float, uint8); + static void Save(uint8**, uint32*); + static void Load(uint8*, uint32); +}; \ No newline at end of file diff --git a/src/miami/control/Record.cpp b/src/miami/control/Record.cpp new file mode 100644 index 00000000..5e6c7cdb --- /dev/null +++ b/src/miami/control/Record.cpp @@ -0,0 +1,107 @@ +#include "common.h" + +#include "Record.h" + +#include "FileMgr.h" +#include "Pad.h" +#include "Pools.h" +#include "Streaming.h" +#include "Timer.h" +#include "VehicleModelInfo.h" +#include "World.h" + +uint16 CRecordDataForGame::RecordingState; + +void CRecordDataForGame::Init(void) +{ + RecordingState = STATE_NONE; +} + +void CRecordDataForGame::SaveOrRetrieveDataForThisFrame(void) +{ +} + +uint8* CRecordDataForGame::PackCurrentPadValues(uint8* buf, CControllerState* os, CControllerState* ns) +{ + return nil; +} + +uint8* CRecordDataForGame::UnPackCurrentPadValues(uint8* buf, uint8 total, CControllerState* state) +{ + return nil; +} + +uint16 CRecordDataForGame::CalcGameChecksum(void) +{ + return 0; +} + +uint8 CRecordDataForChase::Status; + +void CRecordDataForChase::Init(void) +{ + Status = STATE_NONE; +} + +void CRecordDataForChase::SaveOrRetrieveDataForThisFrame(void) +{ +} + +void CRecordDataForChase::SaveOrRetrieveCarPositions(void) +{ +} + +void CRecordDataForChase::StoreInfoForCar(CAutomobile* pCar, CCarStateEachFrame* pState) +{ +} + +void CRecordDataForChase::RestoreInfoForMatrix(CMatrix& matrix, CCarStateEachFrame* pState) +{ +} + +void CRecordDataForChase::RestoreInfoForCar(CAutomobile* pCar, CCarStateEachFrame* pState, bool stop) +{ +} + +void CRecordDataForChase::ProcessControlCars(void) +{ +} + +bool CRecordDataForChase::ShouldThisPadBeLeftAlone(uint8 pad) +{ + return false; +} + +void CRecordDataForChase::GiveUsACar(int32 mi, CVector pos, float angle, CAutomobile** ppCar, uint8 colour1, uint8 colour2) +{ +} + +void RemoveUnusedCollision(void) +{ +} + +void CRecordDataForChase::StartChaseScene(float startTime) +{ +} + +void CRecordDataForChase::CleanUpChaseScene(void) +{ +} + +void CRecordDataForChase::SetUpCarsForChaseScene(void) +{ +} + +void CRecordDataForChase::CleanUpCarsForChaseScene(void) +{ +} + +void CRecordDataForChase::RemoveCarFromChase(int32 i) +{ +} + +CVehicle* CRecordDataForChase::TurnChaseCarIntoScriptCar(int32 i) +{ + return nil; +} + diff --git a/src/miami/control/Record.h b/src/miami/control/Record.h new file mode 100644 index 00000000..6a94c408 --- /dev/null +++ b/src/miami/control/Record.h @@ -0,0 +1,104 @@ +#pragma once + +class CAutomobile; +class CVehicle; +class CControllerState; + +class CCarStateEachFrame +{ +public: + int16 velX; + int16 velY; + int16 velZ; + int8 rightX; + int8 rightY; + int8 rightZ; + int8 forwardX; + int8 forwardY; + int8 forwardZ; + int8 wheel; + int8 gas; + int8 brake; + bool handbrake; + CVector pos; +}; + +extern char gString[256]; + +class CRecordDataForChase +{ + enum { + NUM_CHASE_CARS = 20 + }; + enum { + STATE_NONE = 0, + STATE_RECORD = 1, + STATE_PLAYBACK_INIT = 2, + STATE_PLAYBACK = 3, + STATE_PLAYBACK_BEFORE_RECORDING = 4 + }; + static uint8 Status; + static int PositionChanges; + static uint8 CurrentCar; + static CAutomobile*pChaseCars[NUM_CHASE_CARS]; + static float AnimTime; + static uint32 AnimStartTime; + static CCarStateEachFrame* pBaseMemForCar[NUM_CHASE_CARS]; + static float TimeMultiplier; + static int FId2; +public: + + static bool IsRecording(void) { return Status == STATE_RECORD; } + + static void Init(void); + static void SaveOrRetrieveDataForThisFrame(void); + static void SaveOrRetrieveCarPositions(void); + static void StoreInfoForCar(CAutomobile*, CCarStateEachFrame*); + static void RestoreInfoForMatrix(CMatrix&, CCarStateEachFrame*); + static void RestoreInfoForCar(CAutomobile*, CCarStateEachFrame*, bool); + static void ProcessControlCars(void); + static bool ShouldThisPadBeLeftAlone(uint8 pad); + static void GiveUsACar(int32, CVector, float, CAutomobile**, uint8, uint8); + static void StartChaseScene(float); + static void CleanUpChaseScene(void); + static void SetUpCarsForChaseScene(void); + static void CleanUpCarsForChaseScene(void); + static void RemoveCarFromChase(int32); + static CVehicle* TurnChaseCarIntoScriptCar(int32); + +}; + +struct tGameBuffer +{ + float m_fTimeStep; + uint32 m_nTimeInMilliseconds; + uint8 m_nSizeOfPads[2]; + uint16 m_nChecksum; + uint8 m_ControllerBuffer[116]; +}; + +class CRecordDataForGame +{ + enum { + STATE_NONE = 0, + STATE_RECORD = 1, + STATE_PLAYBACK = 2, + }; + static uint16 RecordingState; + static uint8* pDataBuffer; + static uint8* pDataBufferPointer; + static int FId; + static tGameBuffer pDataBufferForFrame; + +public: + static bool IsRecording() { return RecordingState == STATE_RECORD; } + static bool IsPlayingBack() { return RecordingState == STATE_PLAYBACK; } + + static void SaveOrRetrieveDataForThisFrame(void); + static void Init(void); + +private: + static uint16 CalcGameChecksum(void); + static uint8* PackCurrentPadValues(uint8*, CControllerState*, CControllerState*); + static uint8* UnPackCurrentPadValues(uint8*, uint8, CControllerState*); +}; diff --git a/src/miami/control/Remote.cpp b/src/miami/control/Remote.cpp new file mode 100644 index 00000000..047b19f3 --- /dev/null +++ b/src/miami/control/Remote.cpp @@ -0,0 +1,58 @@ +#include "common.h" + +#include "Automobile.h" +#include "CarCtrl.h" +#include "Camera.h" +#include "Remote.h" +#include "Timer.h" +#include "World.h" +#include "PlayerInfo.h" +#include "Vehicle.h" + +void +CRemote::GivePlayerRemoteControlledCar(float x, float y, float z, float rot, uint16 model) +{ + CAutomobile *car = new CAutomobile(model, MISSION_VEHICLE); + bool found; + + z = car->GetDistanceFromCentreOfMassToBaseOfModel() + CWorld::FindGroundZFor3DCoord(x, y, z + 2.0f, &found); + + car->GetMatrix().SetRotateZOnly(rot); + car->SetPosition(x, y, z); + car->SetStatus(STATUS_PLAYER_REMOTE); + car->bIsLocked = true; + + CCarCtrl::JoinCarWithRoadSystem(car); + car->AutoPilot.m_nCarMission = MISSION_NONE; + car->AutoPilot.m_nTempAction = TEMPACT_NONE; + car->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; + car->AutoPilot.m_nCruiseSpeed = car->AutoPilot.m_fMaxTrafficSpeed = 9.0f; + car->AutoPilot.m_nNextLane = car->AutoPilot.m_nCurrentLane = 0; + car->bEngineOn = true; + CWorld::Add(car); + if (FindPlayerVehicle() != nil) + FindPlayerVehicle()->SetStatus(STATUS_PLAYER_DISABLED); + + CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle = car; + CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle->RegisterReference((CEntity**)&CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle); + if (car->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE || car->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI) { + TheCamera.TakeControl(car, CCam::MODE_CAM_ON_A_STRING, INTERPOLATION, CAMCONTROL_SCRIPT); + TheCamera.SetZoomValueCamStringScript(0); + } else + TheCamera.TakeControl(car, CCam::MODE_BEHINDCAR, INTERPOLATION, CAMCONTROL_SCRIPT); +} + +void +CRemote::TakeRemoteControlledCarFromPlayer(bool blowUp) +{ + if (CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle->VehicleCreatedBy == MISSION_VEHICLE) { + CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle->VehicleCreatedBy = RANDOM_VEHICLE; + CCarCtrl::NumMissionCars--; + CCarCtrl::NumRandomCars++; + } + CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle->bIsLocked = false; + CWorld::Players[CWorld::PlayerInFocus].m_nTimeLostRemoteCar = CTimer::GetTimeInMilliseconds(); + CWorld::Players[CWorld::PlayerInFocus].m_bInRemoteMode = true; + CWorld::Players[CWorld::PlayerInFocus].field_D5 = blowUp; + CWorld::Players[CWorld::PlayerInFocus].field_D6 = true; +} diff --git a/src/miami/control/Remote.h b/src/miami/control/Remote.h new file mode 100644 index 00000000..72cabb7c --- /dev/null +++ b/src/miami/control/Remote.h @@ -0,0 +1,8 @@ +#pragma once + +class CRemote +{ +public: + static void GivePlayerRemoteControlledCar(float, float, float, float, uint16); + static void TakeRemoteControlledCarFromPlayer(bool blowUp = true); +}; diff --git a/src/miami/control/Replay.cpp b/src/miami/control/Replay.cpp new file mode 100644 index 00000000..71b28f7a --- /dev/null +++ b/src/miami/control/Replay.cpp @@ -0,0 +1,1911 @@ +#include "common.h" +#ifdef GTA_REPLAY +#include "AnimBlendAssocGroup.h" +#include "AnimBlendAssociation.h" +#include "Bike.h" +#include "Boat.h" +#include "SpecialFX.h" +#include "CarCtrl.h" +#include "CivilianPed.h" +#include "CopPed.h" +#include "Wanted.h" +#include "Clock.h" +#include "DMAudio.h" +#include "Draw.h" +#include "Explosion.h" +#include "FileMgr.h" +#include "Fire.h" +#include "Frontend.h" +#include "Garages.h" +#include "Heli.h" +#include "main.h" +#include "Matrix.h" +#include "ModelIndices.h" +#include "ModelInfo.h" +#include "Object.h" +#include "Pad.h" +#include "Particle.h" +#include "PedAttractor.h" +#include "Phones.h" +#include "Pickups.h" +#include "Plane.h" +#include "Pools.h" +#include "Population.h" +#include "Projectile.h" +#include "ProjectileInfo.h" +#include "Replay.h" +#include "References.h" +#include "Pools.h" +#include "RpAnimBlend.h" +#include "RwHelper.h" +#include "CutsceneMgr.h" +#include "Skidmarks.h" +#include "Stinger.h" +#include "Streaming.h" +#include "Timer.h" +#include "Train.h" +#include "Weather.h" +#include "Zones.h" +#include "Font.h" +#include "Text.h" +#include "Camera.h" +#include "Radar.h" +#include "Fluff.h" +#include "WaterCreatures.h" + +uint8 CReplay::Mode; +CAddressInReplayBuffer CReplay::Record; +CAddressInReplayBuffer CReplay::Playback; +uint8 *CReplay::pBuf0; +CAutomobile *CReplay::pBuf1; +uint8 *CReplay::pBuf2; +CPlayerPed *CReplay::pBuf3; +uint8 *CReplay::pBuf4; +CCutsceneObject *CReplay::pBuf5; +uint8 *CReplay::pBuf6; +CPtrNode *CReplay::pBuf7; +uint8 *CReplay::pBuf8; +CEntryInfoNode *CReplay::pBuf9; +uint8 *CReplay::pBuf10; +CDummyPed *CReplay::pBuf11; +uint8 *CReplay::pRadarBlips; +uint8 *CReplay::pStoredCam; +uint8 *CReplay::pWorld1; +CReference *CReplay::pEmptyReferences; +CStoredDetailedAnimationState *CReplay::pPedAnims; +uint8 *CReplay::pPickups; +uint8 *CReplay::pReferences; +uint8 CReplay::BufferStatus[NUM_REPLAYBUFFERS]; +uint8 CReplay::Buffers[NUM_REPLAYBUFFERS][REPLAYBUFFERSIZE]; +bool CReplay::bPlayingBackFromFile; +bool CReplay::bReplayEnabled = true; +uint32 CReplay::SlowMotion; +uint32 CReplay::FramesActiveLookAroundCam; +bool CReplay::bDoLoadSceneWhenDone; +CPtrNode* CReplay::WorldPtrList; +CPtrNode* CReplay::BigBuildingPtrList; +CWanted CReplay::PlayerWanted; +CPlayerInfo CReplay::PlayerInfo; +uint32 CReplay::Time1; +uint32 CReplay::Time2; +uint32 CReplay::Time3; +uint32 CReplay::Time4; +uint32 CReplay::Frame; +uint8 CReplay::ClockHours; +uint8 CReplay::ClockMinutes; +uint16 CReplay::OldWeatherType; +uint16 CReplay::NewWeatherType; +float CReplay::WeatherInterpolationValue; +float CReplay::TimeStepNonClipped; +float CReplay::TimeStep; +float CReplay::TimeScale; +float CReplay::CameraFixedX; +float CReplay::CameraFixedY; +float CReplay::CameraFixedZ; +int32 CReplay::OldRadioStation; +int8 CReplay::CameraMode; +bool CReplay::bAllowLookAroundCam; +float CReplay::LoadSceneX; +float CReplay::LoadSceneY; +float CReplay::LoadSceneZ; +float CReplay::CameraFocusX; +float CReplay::CameraFocusY; +float CReplay::CameraFocusZ; +bool CReplay::bPlayerInRCBuggy; +float CReplay::fDistanceLookAroundCam; +float CReplay::fBetaAngleLookAroundCam; +float CReplay::fAlphaAngleLookAroundCam; +int CReplay::ms_nNumCivMale_Stored; +int CReplay::ms_nNumCivFemale_Stored; +int CReplay::ms_nNumCop_Stored; +int CReplay::ms_nNumEmergency_Stored; +int CReplay::ms_nNumGang1_Stored; +int CReplay::ms_nNumGang2_Stored; +int CReplay::ms_nNumGang3_Stored; +int CReplay::ms_nNumGang4_Stored; +int CReplay::ms_nNumGang5_Stored; +int CReplay::ms_nNumGang6_Stored; +int CReplay::ms_nNumGang7_Stored; +int CReplay::ms_nNumGang8_Stored; +int CReplay::ms_nNumGang9_Stored; +int CReplay::ms_nNumDummy_Stored; +int CReplay::ms_nTotalCarPassengerPeds_Stored; +int CReplay::ms_nTotalCivPeds_Stored; +int CReplay::ms_nTotalGangPeds_Stored; +int CReplay::ms_nTotalPeds_Stored; +int CReplay::ms_nTotalMissionPeds_Stored; +uint8* CReplay::pGarages; +CFire* CReplay::FireArray; +uint32 CReplay::NumOfFires; +uint8* CReplay::paProjectileInfo; +uint8* CReplay::paProjectiles; +uint8 CReplay::CurrArea; +#ifdef FIX_BUGS +int CReplay::nHandleOfPlayerPed[NUMPLAYERS]; +#endif + +static void(*CBArray[])(CAnimBlendAssociation*, void*) = +{ + nil, &CPed::PedGetupCB, &CPed::PedStaggerCB, &CPed::PedEvadeCB, &CPed::FinishDieAnimCB, + &CPed::FinishedWaitCB, &CPed::FinishLaunchCB, &CPed::FinishHitHeadCB, &CPed::PedAnimGetInCB, &CPed::PedAnimDoorOpenCB, + &CPed::PedAnimPullPedOutCB, &CPed::PedAnimDoorCloseCB, &CPed::PedSetInCarCB, &CPed::PedSetOutCarCB, &CPed::PedAnimAlignCB, + &CPed::PedSetDraggedOutCarCB, &CPed::PedAnimStepOutCarCB, &CPed::PedSetInTrainCB, +#ifdef GTA_TRAIN + &CPed::PedSetOutTrainCB, +#endif + &CPed::FinishedAttackCB, + &CPed::FinishFightMoveCB, &PhonePutDownCB, &PhonePickUpCB, &CPed::PedAnimDoorCloseRollingCB, &CPed::FinishJumpCB, + &CPed::PedLandCB, &CPed::RestoreHeadingRateCB, &CPed::PedSetQuickDraggedOutCarPositionCB, &CPed::PedSetDraggedOutCarPositionCB, + &CPed::PedSetPreviousStateCB, &CPed::FinishedReloadCB, &CPed::PedSetGetInCarPositionCB, + &CPed::PedAnimShuffleCB, &CPed::DeleteSunbatheIdleAnimCB, &StartTalkingOnMobileCB, &FinishTalkingOnMobileCB +}; + +static uint8 FindCBFunctionID(void(*f)(CAnimBlendAssociation*, void*)) +{ + for (int i = 0; i < sizeof(CBArray) / sizeof(*CBArray); i++){ + if (CBArray[i] == f) + return i; + } + + return 0; +} + +static void(*FindCBFunction(uint8 id))(CAnimBlendAssociation*, void*) +{ + return CBArray[id]; +} + +static void ApplyPanelDamageToCar(uint32 panels, CAutomobile* vehicle, bool flying) +{ + if(vehicle->Damage.GetPanelStatus(VEHPANEL_FRONT_LEFT) != CDamageManager::GetPanelStatus(panels, VEHPANEL_FRONT_LEFT)){ + vehicle->Damage.SetPanelStatus(VEHPANEL_FRONT_LEFT, CDamageManager::GetPanelStatus(panels, VEHPANEL_FRONT_LEFT)); + vehicle->SetPanelDamage(CAR_WING_LF, VEHPANEL_FRONT_LEFT, flying); + } + if(vehicle->Damage.GetPanelStatus(VEHPANEL_FRONT_RIGHT) != CDamageManager::GetPanelStatus(panels, VEHPANEL_FRONT_RIGHT)){ + vehicle->Damage.SetPanelStatus(VEHPANEL_FRONT_RIGHT, CDamageManager::GetPanelStatus(panels, VEHPANEL_FRONT_RIGHT)); + vehicle->SetPanelDamage(CAR_WING_RF, VEHPANEL_FRONT_RIGHT, flying); + } + if(vehicle->Damage.GetPanelStatus(VEHPANEL_REAR_LEFT) != CDamageManager::GetPanelStatus(panels, VEHPANEL_REAR_LEFT)){ + vehicle->Damage.SetPanelStatus(VEHPANEL_REAR_LEFT, CDamageManager::GetPanelStatus(panels, VEHPANEL_REAR_LEFT)); + vehicle->SetPanelDamage(CAR_WING_LR, VEHPANEL_REAR_LEFT, flying); + } + if(vehicle->Damage.GetPanelStatus(VEHPANEL_REAR_RIGHT) != CDamageManager::GetPanelStatus(panels, VEHPANEL_REAR_RIGHT)){ + vehicle->Damage.SetPanelStatus(VEHPANEL_REAR_RIGHT, CDamageManager::GetPanelStatus(panels, VEHPANEL_REAR_RIGHT)); + vehicle->SetPanelDamage(CAR_WING_RR, VEHPANEL_REAR_RIGHT, flying); + } + if(vehicle->Damage.GetPanelStatus(VEHPANEL_WINDSCREEN) != CDamageManager::GetPanelStatus(panels, VEHPANEL_WINDSCREEN)){ + vehicle->Damage.SetPanelStatus(VEHPANEL_WINDSCREEN, CDamageManager::GetPanelStatus(panels, VEHPANEL_WINDSCREEN)); + vehicle->SetPanelDamage(CAR_WINDSCREEN, VEHPANEL_WINDSCREEN, flying); + } + if(vehicle->Damage.GetPanelStatus(VEHBUMPER_FRONT) != CDamageManager::GetPanelStatus(panels, VEHBUMPER_FRONT)){ + vehicle->Damage.SetPanelStatus(VEHBUMPER_FRONT, CDamageManager::GetPanelStatus(panels, VEHBUMPER_FRONT)); + vehicle->SetPanelDamage(CAR_BUMP_FRONT, VEHBUMPER_FRONT, flying); + } + if(vehicle->Damage.GetPanelStatus(VEHBUMPER_REAR) != CDamageManager::GetPanelStatus(panels, VEHBUMPER_REAR)){ + vehicle->Damage.SetPanelStatus(VEHBUMPER_REAR, CDamageManager::GetPanelStatus(panels, VEHBUMPER_REAR)); + vehicle->SetPanelDamage(CAR_BUMP_REAR, VEHBUMPER_REAR, flying); + } +} + +void PrintElementsInPtrList(void) +{ + for (CPtrNode* node = CWorld::GetBigBuildingList(LEVEL_GENERIC).first; node; node = node->next) { + /* Most likely debug print was present here */ + } +} + +void CReplay::Init(void) +{ + pBuf0 = nil; + pBuf1 = nil; + pBuf2 = nil; + pBuf3 = nil; + pBuf4 = nil; + pBuf5 = nil; + pBuf6 = nil; + pBuf7 = nil; + pBuf8 = nil; + pBuf9 = nil; + pBuf10 = nil; + pBuf11 = nil; + pRadarBlips = nil; + pStoredCam = nil; + pWorld1 = nil; + pEmptyReferences = nil; + pPedAnims = nil; + pPickups = nil; + pReferences = nil; + Mode = MODE_RECORD; + Playback.m_nOffset = 0; + Playback.m_pBase = nil; + Playback.m_bSlot = 0; + Record.m_nOffset = 0; + Record.m_pBase = nil; + Record.m_bSlot = 0; + for (int i = 0; i < NUM_REPLAYBUFFERS; i++) + BufferStatus[i] = REPLAYBUFFER_UNUSED; + Record.m_bSlot = 0; + Record.m_pBase = Buffers[0]; + BufferStatus[0] = REPLAYBUFFER_RECORD; + Buffers[0][Record.m_nOffset] = REPLAYPACKET_END; + bPlayingBackFromFile = false; + bReplayEnabled = true; + SlowMotion = 1; + FramesActiveLookAroundCam = 0; + bDoLoadSceneWhenDone = false; + MarkEverythingAsNew(); +} + +void CReplay::DisableReplays(void) +{ + bReplayEnabled = false; +} + +void CReplay::EnableReplays(void) +{ + bReplayEnabled = true; +} + +void PlayReplayFromHD(void); +void CReplay::Update(void) +{ + if (CCutsceneMgr::IsCutsceneProcessing() || CPad::GetPad(0)->ArePlayerControlsDisabled() || CScriptPaths::IsOneActive() || FrontEndMenuManager.GetIsMenuActive()) { + Init(); + return; + } + switch (Mode){ + case MODE_RECORD: + RecordThisFrame(); + break; + case MODE_PLAYBACK: + PlaybackThisFrame(); + break; + } + if (CDraw::FadeValue || !bReplayEnabled) + return; + if (Mode == MODE_PLAYBACK){ + if (CPad::GetPad(0)->GetFJustDown(0)) + FinishPlayback(); + } + else if (Mode == MODE_RECORD){ + if (CPad::GetPad(0)->GetFJustDown(0)) + TriggerPlayback(REPLAYCAMMODE_ASSTORED, 0.0f, 0.0f, 0.0f, false); + if (CPad::GetPad(0)->GetFJustDown(1)) + SaveReplayToHD(); + if (CPad::GetPad(0)->GetFJustDown(2)) + PlayReplayFromHD(); +#ifdef USE_BETA_REPLAY_MODE + if (CPad::GetPad(0)->GetFJustDown(3)) + TriggerPlaybackLastCoupleOfSeconds(5000, REPLAYCAMMODE_TOPDOWN, 0.0f, 0.0f, 0.0f, 4); +#endif + } +} + +void CReplay::RecordThisFrame(void) +{ + uint32 memory_required = sizeof(tGeneralPacket) + sizeof(tClockPacket) + sizeof(tWeatherPacket) + sizeof(tTimerPacket) + sizeof(tMiscPacket); + CVehiclePool* vehiclesT = CPools::GetVehiclePool(); + for (int i = 0; i < vehiclesT->GetSize(); i++) { + CVehicle* v = vehiclesT->GetSlot(i); + if (v && v->m_rwObject && v->GetModelIndex() != MI_AIRTRAIN && v->GetModelIndex() != MI_TRAIN) { + if (v->IsBike()) + memory_required += sizeof(tBikeUpdatePacket); + else + memory_required += sizeof(tVehicleUpdatePacket); + } + } + CPedPool* pedsT = CPools::GetPedPool(); + for (int i = 0; i < pedsT->GetSize(); i++) { + CPed* p = pedsT->GetSlot(i); + if (!p || !p->m_rwObject) + continue; + if (!p->bHasAlreadyBeenRecorded) { + memory_required += sizeof(tPedHeaderPacket); + } + memory_required += sizeof(tPedUpdatePacket); + } + for (uint8 i = 0; i < NUMBULLETTRACES; i++) { + if (!CBulletTraces::aTraces[i].m_bInUse) + continue; + memory_required += sizeof(tBulletTracePacket); + } + memory_required += sizeof(tEndOfFramePacket) + 1; // 1 for Record.m_pBase[Record.m_nOffset] = REPLAYPACKET_END; + if (Record.m_nOffset + memory_required > REPLAYBUFFERSIZE - 16) + GoToNextBlock(); + tGeneralPacket* general = (tGeneralPacket*)&Record.m_pBase[Record.m_nOffset]; + general->type = REPLAYPACKET_GENERAL; + general->camera_pos.CopyOnlyMatrix(TheCamera.GetMatrix()); + general->player_pos = FindPlayerCoors(); + general->in_rcvehicle = CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle ? true : false; + Record.m_nOffset += sizeof(*general); + tClockPacket* clock = (tClockPacket*)&Record.m_pBase[Record.m_nOffset]; + clock->type = REPLAYPACKET_CLOCK; + clock->hours = CClock::GetHours(); + clock->minutes = CClock::GetMinutes(); + Record.m_nOffset += sizeof(*clock); + tWeatherPacket* weather = (tWeatherPacket*)&Record.m_pBase[Record.m_nOffset]; + weather->type = REPLAYPACKET_WEATHER; + weather->old_weather = CWeather::OldWeatherType; + weather->new_weather = CWeather::NewWeatherType; + weather->interpolation = CWeather::InterpolationValue; + Record.m_nOffset += sizeof(*weather); + tTimerPacket* timer = (tTimerPacket*)&Record.m_pBase[Record.m_nOffset]; + timer->type = REPLAYPACKET_TIMER; + timer->timer = CTimer::GetTimeInMilliseconds(); + Record.m_nOffset += sizeof(*timer); + CVehiclePool* vehicles = CPools::GetVehiclePool(); + for (int i = 0; i < vehicles->GetSize(); i++){ + CVehicle* v = vehicles->GetSlot(i); + if (v && v->m_rwObject && v->GetModelIndex() != MI_AIRTRAIN && v->GetModelIndex() != MI_TRAIN) { + if (v->IsBike()) + StoreBikeUpdate(v, i); + else + StoreCarUpdate(v, i); + } + } + CPedPool* peds = CPools::GetPedPool(); + for (int i = 0; i < peds->GetSize(); i++) { + CPed* p = peds->GetSlot(i); + if (!p || !p->m_rwObject) + continue; + if (!p->bHasAlreadyBeenRecorded){ + tPedHeaderPacket* ph = (tPedHeaderPacket*)&Record.m_pBase[Record.m_nOffset]; + ph->type = REPLAYPACKET_PED_HEADER; + ph->index = i; + ph->mi = p->GetModelIndex(); + ph->pedtype = p->m_nPedType; + Record.m_nOffset += sizeof(*ph); + p->bHasAlreadyBeenRecorded = true; + } + StorePedUpdate(p, i); + } + for (uint8 i = 0; i < NUMBULLETTRACES; i++){ + if (!CBulletTraces::aTraces[i].m_bInUse) + continue; + tBulletTracePacket* bt = (tBulletTracePacket*)&Record.m_pBase[Record.m_nOffset]; + bt->type = REPLAYPACKET_BULLET_TRACES; + bt->index = i; + bt->inf = CBulletTraces::aTraces[i].m_vecStartPos; + bt->sup = CBulletTraces::aTraces[i].m_vecEndPos; + Record.m_nOffset += sizeof(*bt); + } + tMiscPacket* misc = (tMiscPacket*)&Record.m_pBase[Record.m_nOffset]; + misc->type = REPLAYPACKET_MISC; + misc->cam_shake_start = TheCamera.m_uiCamShakeStart; + misc->cam_shake_strength = TheCamera.m_fCamShakeForce; + misc->cur_area = CGame::currArea; + misc->video_cam = CSpecialFX::bVideoCam; + misc->lift_cam = CSpecialFX::bLiftCam; + Record.m_nOffset += sizeof(*misc); + tEndOfFramePacket* eof = (tEndOfFramePacket*)&Record.m_pBase[Record.m_nOffset]; + eof->type = REPLAYPACKET_ENDOFFRAME; + Record.m_nOffset += sizeof(*eof); + Record.m_pBase[Record.m_nOffset] = REPLAYPACKET_END; +} + +void CReplay::GoToNextBlock(void) +{ + Record.m_pBase[Record.m_nOffset] = REPLAYPACKET_END; + BufferStatus[Record.m_bSlot] = REPLAYBUFFER_PLAYBACK; + Record.m_bSlot = (Record.m_bSlot + 1) % NUM_REPLAYBUFFERS; + BufferStatus[Record.m_bSlot] = REPLAYBUFFER_RECORD; + Record.m_pBase = Buffers[Record.m_bSlot]; + Record.m_nOffset = 0; + *Record.m_pBase = REPLAYPACKET_END; + MarkEverythingAsNew(); +} + +void CReplay::RecordParticle(tParticleType type, const CVector& vecPos, const CVector& vecDir, float fSize, const RwRGBA& color) +{ + if (Record.m_nOffset > REPLAYBUFFERSIZE - 16 - sizeof(tParticlePacket)) + GoToNextBlock(); + tParticlePacket* pp = (tParticlePacket*)&Record.m_pBase[Record.m_nOffset]; + pp->type = REPLAYPACKET_PARTICLE; + pp->particle_type = type; + pp->pos_x = 4.0f * vecPos.x; + pp->pos_y = 4.0f * vecPos.y; + pp->pos_z = 4.0f * vecPos.z; + pp->dir_x = 120.0f * Clamp(vecDir.x, -1.0f, 1.0f); + pp->dir_y = 120.0f * Clamp(vecDir.y, -1.0f, 1.0f); + pp->dir_z = 120.0f * Clamp(vecDir.z, -1.0f, 1.0f); + pp->size = fSize; + pp->r = color.red; + pp->g = color.green; + pp->b = color.blue; + pp->a = color.alpha; + Record.m_nOffset += sizeof(tParticlePacket); + Record.m_pBase[Record.m_nOffset] = REPLAYPACKET_END; +} + +void CReplay::StorePedUpdate(CPed *ped, int id) +{ + tPedUpdatePacket* pp = (tPedUpdatePacket*)&Record.m_pBase[Record.m_nOffset]; + pp->type = REPLAYPACKET_PED_UPDATE; + pp->index = id; + pp->heading = 128.0f / PI * ped->m_fRotationCur; + pp->matrix.CompressFromFullMatrix(ped->GetMatrix()); + pp->assoc_group_id = ped->m_animGroup; + pp->is_visible = ped->bIsVisible; + /* Would be more sane to use GetJustIndex(ped->m_pMyVehicle) in following assignment */ + if (ped->InVehicle()) + pp->vehicle_index = (CPools::GetVehiclePool()->GetIndex(ped->m_pMyVehicle) >> 8) + 1; + else + pp->vehicle_index = 0; + pp->weapon_model = ped->m_wepModelID; + StorePedAnimation(ped, &pp->anim_state); + Record.m_nOffset += sizeof(tPedUpdatePacket); +} + +void CReplay::StorePedAnimation(CPed *ped, CStoredAnimationState *state) +{ + CAnimBlendAssociation* second; + float blend_amount; + CAnimBlendAssociation* main = RpAnimBlendClumpGetMainAssociation((RpClump*)ped->m_rwObject, &second, &blend_amount); + if (main){ + state->animId = main->animId; + state->time = 255.0f / 4.0f * Clamp(main->currentTime, 0.0f, 4.0f); + state->speed = 255.0f / 3.0f * Clamp(main->speed, 0.0f, 3.0f); + state->groupId = main->groupId; + }else{ + state->animId = 3; + state->time = 0; + state->speed = 85; + state->groupId = 0; + } + if (second) { + state->secAnimId = second->animId; + state->secTime = 255.0f / 4.0f * Clamp(second->currentTime, 0.0f, 4.0f); + state->secSpeed = 255.0f / 3.0f * Clamp(second->speed, 0.0f, 3.0f); + state->blendAmount = 255.0f / 2.0f * Clamp(blend_amount, 0.0f, 2.0f); + state->secGroupId = second->groupId; + }else{ + state->secAnimId = 0; + state->secTime = 0; + state->secSpeed = 0; + state->blendAmount = 0; + state->secGroupId = 0; + } + CAnimBlendAssociation* partial = RpAnimBlendClumpGetMainPartialAssociation((RpClump*)ped->m_rwObject); + if (partial) { + state->partAnimId = partial->animId; + state->partAnimTime = 255.0f / 4.0f * Clamp(partial->currentTime, 0.0f, 4.0f); + state->partAnimSpeed = 255.0f / 3.0f * Clamp(partial->speed, 0.0f, 3.0f); + state->partBlendAmount = 255.0f / 2.0f * Clamp(partial->blendAmount, 0.0f, 2.0f); + state->partGroupId = partial->groupId; + }else{ + state->partAnimId = 0; + state->partAnimTime = 0; + state->partAnimSpeed = 0; + state->partBlendAmount = 0; + state->partGroupId = 0; + } +} + +void CReplay::StoreDetailedPedAnimation(CPed *ped, CStoredDetailedAnimationState *state) +{ + for (int i = 0; i < NUM_MAIN_ANIMS_IN_REPLAY; i++){ + CAnimBlendAssociation* assoc = RpAnimBlendClumpGetMainAssociation_N((RpClump*)ped->m_rwObject, i); + if (assoc){ + state->aAnimId[i] = assoc->animId; + state->aCurTime[i] = 255.0f / 4.0f * Clamp(assoc->currentTime, 0.0f, 4.0f); + state->aSpeed[i] = 255.0f / 3.0f * Clamp(assoc->speed, 0.0f, 3.0f); + state->aBlendAmount[i] = 255.0f / 2.0f * Clamp(assoc->blendAmount, 0.0f, 2.0f); + state->aBlendDelta[i] = 127.0f / 32.0f * Clamp(assoc->blendDelta, -16.0f, 16.0f); + state->aFlags[i] = assoc->flags; + state->aGroupId[i] = assoc->groupId; + if (assoc->callbackType == CAnimBlendAssociation::CB_FINISH || assoc->callbackType == CAnimBlendAssociation::CB_DELETE) { + state->aFunctionCallbackID[i] = FindCBFunctionID(assoc->callback); + if (assoc->callbackType == CAnimBlendAssociation::CB_FINISH) + state->aFunctionCallbackID[i] |= 0x80; + }else{ + state->aFunctionCallbackID[i] = 0; + } + }else{ + state->aAnimId[i] = ANIM_STD_NUM; + state->aCurTime[i] = 0; + state->aSpeed[i] = 85; + state->aFunctionCallbackID[i] = 0; + state->aFlags[i] = 0; + state->aGroupId[i] = 0; + } + } + for (int i = 0; i < NUM_PARTIAL_ANIMS_IN_REPLAY; i++) { + CAnimBlendAssociation* assoc = RpAnimBlendClumpGetMainPartialAssociation_N((RpClump*)ped->m_rwObject, i); + if (assoc) { + state->aAnimId2[i] = assoc->animId; + state->aCurTime2[i] = 255.0f / 4.0f * Clamp(assoc->currentTime, 0.0f, 4.0f); + state->aSpeed2[i] = 255.0f / 3.0f * Clamp(assoc->speed, 0.0f, 3.0f); + state->aBlendAmount2[i] = 255.0f / 2.0f * Clamp(assoc->blendAmount, 0.0f, 2.0f); + state->aBlendDelta2[i] = 127.0f / 16.0f * Clamp(assoc->blendDelta, -16.0f, 16.0f); + state->aFlags2[i] = assoc->flags; + state->aGroupId2[i] = assoc->groupId; + if (assoc->callbackType == CAnimBlendAssociation::CB_FINISH || assoc->callbackType == CAnimBlendAssociation::CB_DELETE) { + state->aFunctionCallbackID2[i] = FindCBFunctionID(assoc->callback); + if (assoc->callbackType == CAnimBlendAssociation::CB_FINISH) + state->aFunctionCallbackID2[i] |= 0x80; + }else{ + state->aFunctionCallbackID2[i] = 0; + } + } + else { + state->aAnimId2[i] = ANIM_STD_NUM; + state->aCurTime2[i] = 0; + state->aSpeed2[i] = 85; + state->aFunctionCallbackID2[i] = 0; + state->aFlags2[i] = 0; + state->aGroupId2[i] = 0; + } + } +} + +void CReplay::ProcessPedUpdate(CPed *ped, float interpolation, CAddressInReplayBuffer *buffer) +{ + tPedUpdatePacket *pp = (tPedUpdatePacket*)&buffer->m_pBase[buffer->m_nOffset]; + if (!ped){ + printf("Replay:Ped wasn't there\n"); + buffer->m_nOffset += sizeof(tPedUpdatePacket); + return; + } + ped->m_fRotationCur = pp->heading * PI / 128.0f; + ped->m_fRotationDest = pp->heading * PI / 128.0f; + CMatrix ped_matrix; + pp->matrix.DecompressIntoFullMatrix(ped_matrix); + ped->GetMatrix() = ped->GetMatrix() * CMatrix(1.0f - interpolation); + ped->GetMatrix().GetPosition() *= (1.0f - interpolation); + ped->GetMatrix() += CMatrix(interpolation) * ped_matrix; + if (pp->vehicle_index) { + ped->m_pMyVehicle = CPools::GetVehiclePool()->GetSlot(pp->vehicle_index - 1); + ped->bInVehicle = true; + } + else { + ped->m_pMyVehicle = nil; + ped->bInVehicle = false; + } + if (pp->assoc_group_id != ped->m_animGroup) { + ped->m_animGroup = (AssocGroupId)pp->assoc_group_id; + if (ped == FindPlayerPed()) + ((CPlayerPed*)ped)->ReApplyMoveAnims(); + } + ped->bIsVisible = pp->is_visible; + if (FramesActiveLookAroundCam && ped->m_nPedType == PEDTYPE_PLAYER1) + ped->bIsVisible = true; + RetrievePedAnimation(ped, &pp->anim_state); + ped->RemoveWeaponModel(-1); + if (pp->weapon_model != (uint16)-1) { + if (CStreaming::HasModelLoaded(pp->weapon_model)) + ped->AddWeaponModel(pp->weapon_model); + else + CStreaming::RequestModel(pp->weapon_model, 0); + } + CWorld::Remove(ped); + CWorld::Add(ped); + buffer->m_nOffset += sizeof(tPedUpdatePacket); +} + +bool HasAnimGroupLoaded(uint8 group) +{ + CAnimBlendAssocGroup* pGroup = &CAnimManager::GetAnimAssocGroups()[group]; + return pGroup->animBlock && pGroup->animBlock->isLoaded; +} + +void CReplay::RetrievePedAnimation(CPed *ped, CStoredAnimationState *state) +{ + CAnimBlendAssociation* anim1; + if (state->animId <= ANIM_STD_IDLE) + anim1 = CAnimManager::BlendAnimation( + (RpClump*)ped->m_rwObject, ped->m_animGroup, (AnimationId)state->animId, 100.0f); + else if (HasAnimGroupLoaded(state->groupId)) + anim1 = CAnimManager::BlendAnimation((RpClump*)ped->m_rwObject, (AssocGroupId)state->groupId, (AnimationId)state->animId, 100.0f); + else + anim1 = CAnimManager::BlendAnimation((RpClump*)ped->m_rwObject, ASSOCGRP_STD, ANIM_STD_WALK, 100.0f); + + anim1->SetCurrentTime(state->time * 4.0f / 255.0f); + anim1->speed = state->speed * 3.0f / 255.0f; + anim1->SetBlend(1.0f, 1.0f); + anim1->callbackType = CAnimBlendAssociation::CB_NONE; + if (state->blendAmount && state->secAnimId){ + float time = state->secTime * 4.0f / 255.0f; + float speed = state->secSpeed * 3.0f / 255.0f; + float blend = state->blendAmount * 2.0f / 255.0f; + CAnimBlendAssociation* anim2 = CAnimManager::BlendAnimation( + (RpClump*)ped->m_rwObject, + (state->secAnimId > ANIM_STD_IDLE) ? (AssocGroupId)state->secGroupId : ped->m_animGroup, + (AnimationId)state->secAnimId, 100.0f); + anim2->SetCurrentTime(time); + anim2->speed = speed; + anim2->SetBlend(blend, 1.0f); + anim2->callbackType = CAnimBlendAssociation::CB_NONE; + } + RpAnimBlendClumpRemoveAssociations((RpClump*)ped->m_rwObject, 0x10); + if (state->partAnimId){ + float time = state->partAnimTime * 4.0f / 255.0f; + float speed = state->partAnimSpeed * 3.0f / 255.0f; + float blend = state->partBlendAmount * 2.0f / 255.0f; + if (blend > 0.0f && state->partAnimId != ANIM_STD_IDLE && HasAnimGroupLoaded(state->partGroupId)){ + CAnimBlendAssociation* anim3 = CAnimManager::BlendAnimation( + (RpClump*)ped->m_rwObject, (AssocGroupId)state->partGroupId, (AnimationId)state->partAnimId, 1000.0f); + anim3->SetCurrentTime(time); + anim3->speed = speed; + anim3->SetBlend(blend, 0.0f); + } + } +} + +void CReplay::RetrieveDetailedPedAnimation(CPed *ped, CStoredDetailedAnimationState *state) +{ + CAnimBlendAssociation* assoc; + for (int i = 0; ((assoc = RpAnimBlendClumpGetMainAssociation_N(ped->GetClump(), i))); i++) + assoc->SetBlend(0.0f, -1.0f); + for (int i = 0; ((assoc = RpAnimBlendClumpGetMainPartialAssociation_N(ped->GetClump(), i))); i++) + assoc->SetBlend(0.0f, -1.0f); + for (int i = 0; i < NUM_MAIN_ANIMS_IN_REPLAY; i++) { + if (state->aAnimId[i] == ANIM_STD_NUM) + continue; + CAnimBlendAssociation* anim = CAnimManager::AddAnimation(ped->GetClump(), + state->aAnimId[i] > ANIM_STD_IDLE ? (AssocGroupId)state->aGroupId[i] : ped->m_animGroup, + (AnimationId)state->aAnimId[i]); + anim->SetCurrentTime(state->aCurTime[i] * 4.0f / 255.0f); + anim->speed = state->aSpeed[i] * 3.0f / 255.0f; + anim->SetBlend(state->aBlendAmount[i] * 2.0f / 255.0f, state->aBlendDelta[i] * 16.0f / 127.0f); + anim->flags = state->aFlags[i]; + uint8 callback = state->aFunctionCallbackID[i]; + if (!callback) + anim->callbackType = CAnimBlendAssociation::CB_NONE; + else if (callback & 0x80) + anim->SetFinishCallback(FindCBFunction(callback & 0x7F), ped); + else + anim->SetDeleteCallback(FindCBFunction(callback & 0x7F), ped); + } + for (int i = 0; i < NUM_PARTIAL_ANIMS_IN_REPLAY; i++) { + if (state->aAnimId2[i] == ANIM_STD_NUM) + continue; + CAnimBlendAssociation* anim = CAnimManager::AddAnimation(ped->GetClump(), + state->aAnimId2[i] > ANIM_STD_IDLE ? (AssocGroupId)state->aGroupId2[i] : ped->m_animGroup, + (AnimationId)state->aAnimId2[i]); + anim->SetCurrentTime(state->aCurTime2[i] * 4.0f / 255.0f); + anim->speed = state->aSpeed2[i] * 3.0f / 255.0f; + anim->SetBlend(state->aBlendAmount2[i] * 2.0f / 255.0f, state->aBlendDelta2[i] * 16.0f / 127.0f); + anim->flags = state->aFlags2[i]; + uint8 callback = state->aFunctionCallbackID2[i]; + if (!callback) + anim->callbackType = CAnimBlendAssociation::CB_NONE; + else if (callback & 0x80) + anim->SetFinishCallback(FindCBFunction(callback & 0x7F), ped); + else + anim->SetDeleteCallback(FindCBFunction(callback & 0x7F), ped); + } +} + +void CReplay::PlaybackThisFrame(void) +{ + static int FrameSloMo = 0; + CAddressInReplayBuffer buf = Playback; + if (PlayBackThisFrameInterpolation(&buf, 1.0f, nil)){ + DMAudio.SetEffectsFadeVol(127); + DMAudio.SetMusicFadeVol(127); + return; + } + if (FrameSloMo){ + CAddressInReplayBuffer buf_sm = buf; + if (PlayBackThisFrameInterpolation(&buf_sm, FrameSloMo * 1.0f / SlowMotion, nil)){ + DMAudio.SetEffectsFadeVol(127); + DMAudio.SetMusicFadeVol(127); + return; + } + } + FrameSloMo = (FrameSloMo + 1) % SlowMotion; + if (FrameSloMo == 0) + Playback = buf; + ProcessLookAroundCam(); + DMAudio.SetEffectsFadeVol(0); + DMAudio.SetMusicFadeVol(0); +} + +// next two functions are only found in mobile version +// most likely they were optimized out for being unused +void CReplay::TriggerPlaybackLastCoupleOfSeconds(uint32 start, uint8 cam_mode, float cam_x, float cam_y, float cam_z, uint32 slomo) +{ + if (Mode != MODE_RECORD) + return; + TriggerPlayback(cam_mode, cam_x, cam_y, cam_z, true); + SlowMotion = slomo; + bAllowLookAroundCam = false; + if (!FastForwardToTime(CTimer::GetTimeInMilliseconds() - start)) + Mode = MODE_RECORD; +} + +bool CReplay::FastForwardToTime(uint32 start) +{ + uint32 timer = 0; + while (start > timer) + if (PlayBackThisFrameInterpolation(&Playback, 1.0f, &timer)) + return false; + return true; +} + +void CReplay::StoreCarUpdate(CVehicle *vehicle, int id) +{ + tVehicleUpdatePacket* vp = (tVehicleUpdatePacket*)&Record.m_pBase[Record.m_nOffset]; + vp->type = REPLAYPACKET_VEHICLE; + vp->index = id; + vp->matrix.CompressFromFullMatrix(vehicle->GetMatrix()); + vp->health = vehicle->m_fHealth / 4.0f; /* Not anticipated that health can be > 1000. */ + vp->acceleration = vehicle->m_fGasPedal * 100.0f; + vp->panels = vehicle->IsCar() ? ((CAutomobile*)vehicle)->Damage.m_panelStatus : 0; + vp->velocityX = 8000.0f * Max(-4.0f, Min(4.0f, vehicle->GetMoveSpeed().x)); /* 8000!? */ + vp->velocityY = 8000.0f * Max(-4.0f, Min(4.0f, vehicle->GetMoveSpeed().y)); + vp->velocityZ = 8000.0f * Max(-4.0f, Min(4.0f, vehicle->GetMoveSpeed().z)); + vp->mi = vehicle->GetModelIndex(); + vp->primary_color = vehicle->m_currentColour1; + vp->secondary_color = vehicle->m_currentColour2; + if (vehicle->GetModelIndex() == MI_RHINO) + vp->car_gun = 128.0f / PI * ((CAutomobile*)vehicle)->m_fCarGunLR; + else + vp->wheel_state = 50.0f * vehicle->m_fSteerAngle; + if (vehicle->IsCar()){ + CAutomobile* car = (CAutomobile*)vehicle; + for (int i = 0; i < 4; i++){ + vp->wheel_susp_dist[i] = 50.0f * car->m_aSuspensionSpringRatio[i]; + vp->wheel_rotation[i] = 128.0f / PI * car->m_aWheelRotation[i]; + } + vp->door_angles[0] = 127.0f / PI * car->Doors[2].m_fAngle; + vp->door_angles[1] = 127.0f / PI * car->Doors[3].m_fAngle; + vp->door_status = 0; + for (int i = 0; i < 6; i++){ + if (car->Damage.GetDoorStatus(i) == DOOR_STATUS_MISSING) + vp->door_status |= BIT(i); + } + } + if (vehicle->GetModelIndex() == MI_SKIMMER) + vp->skimmer_speed = 50.0f * ((CBoat*)vehicle)->m_fMovingSpeed; + vp->render_scorched = vehicle->bRenderScorched; + vp->vehicle_type = vehicle->m_vehType; + Record.m_nOffset += sizeof(tVehicleUpdatePacket); +} + +void CReplay::StoreBikeUpdate(CVehicle* vehicle, int id) +{ + CBike* bike = (CBike*)vehicle; + tBikeUpdatePacket* vp = (tBikeUpdatePacket*)&Record.m_pBase[Record.m_nOffset]; + vp->type = REPLAYPACKET_BIKE; + vp->index = id; + vp->matrix.CompressFromFullMatrix(vehicle->GetMatrix()); + vp->health = vehicle->m_fHealth / 4.0f; /* Not anticipated that health can be > 1000. */ + vp->acceleration = vehicle->m_fGasPedal * 100.0f; +#ifdef FIX_BUGS // originally it's undefined behaviour - different fields are copied on PC and mobile + for (int i = 0; i < 2; i++) + vp->wheel_rotation[i] = 128.0f / PI * bike->m_aWheelRotation[i]; + for (int i = 0; i < 2; i++) + vp->wheel_rotation[i + 2] = 128.0f / PI * bike->m_aWheelSpeed[i]; + for (int i = 0; i < 4; i++) + vp->wheel_susp_dist[i] = 50.0f * bike->m_aSuspensionSpringRatio[i]; +#else + for (int i = 0; i < 4; i++) { + vp->wheel_susp_dist[i] = 50.0f * bike->m_aSuspensionSpringRatio[i]; + vp->wheel_rotation[i] = 128.0f / PI * bike->m_aWheelRotation[i]; + } +#endif + vp->velocityX = 8000.0f * Max(-4.0f, Min(4.0f, vehicle->GetMoveSpeed().x)); /* 8000!? */ + vp->velocityY = 8000.0f * Max(-4.0f, Min(4.0f, vehicle->GetMoveSpeed().y)); + vp->velocityZ = 8000.0f * Max(-4.0f, Min(4.0f, vehicle->GetMoveSpeed().z)); + vp->mi = vehicle->GetModelIndex(); + vp->primary_color = vehicle->m_currentColour1; + vp->secondary_color = vehicle->m_currentColour2; + vp->wheel_state = 50.0f * vehicle->m_fSteerAngle; + vp->lean_angle = 50.0f * bike->m_fLeanLRAngle; + vp->wheel_angle = 50.0f * bike->m_fWheelAngle; + Record.m_nOffset += sizeof(tBikeUpdatePacket); +} + +void CReplay::ProcessCarUpdate(CVehicle *vehicle, float interpolation, CAddressInReplayBuffer *buffer) +{ + tVehicleUpdatePacket* vp = (tVehicleUpdatePacket*)&buffer->m_pBase[buffer->m_nOffset]; + if (!vehicle){ + printf("Replay:Car wasn't there"); + return; + } + CMatrix vehicle_matrix; + vp->matrix.DecompressIntoFullMatrix(vehicle_matrix); + vehicle->GetMatrix() = vehicle->GetMatrix() * CMatrix(1.0f - interpolation); + vehicle->GetMatrix().GetPosition() *= (1.0f - interpolation); + vehicle->GetMatrix() += CMatrix(interpolation) * vehicle_matrix; + vehicle->m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + vehicle->m_fHealth = 4 * vp->health; + vehicle->m_fGasPedal = vp->acceleration / 100.0f; + if (vehicle->IsCar()) + ApplyPanelDamageToCar(vp->panels, (CAutomobile*)vehicle, true); + vehicle->m_vecMoveSpeed = CVector(vp->velocityX / 8000.0f, vp->velocityY / 8000.0f, vp->velocityZ / 8000.0f); + if (vehicle->GetModelIndex() == MI_RHINO) { + ((CAutomobile*)vehicle)->m_fCarGunLR = vp->car_gun * PI / 128.0f; + vehicle->m_fSteerAngle = 0.0f; + }else{ + vehicle->m_fSteerAngle = vp->wheel_state / 50.0f; + } + if (vehicle->IsCar()) { + CAutomobile* car = (CAutomobile*)vehicle; + for (int i = 0; i < 4; i++) { + car->m_aSuspensionSpringRatio[i] = vp->wheel_susp_dist[i] / 50.0f; + car->m_aWheelRotation[i] = vp->wheel_rotation[i] * PI / 128.0f; + } + car->Doors[DOOR_FRONT_LEFT].m_fAngle = car->Doors[DOOR_FRONT_LEFT].m_fPrevAngle = vp->door_angles[0] * PI / 127.0f; + car->Doors[DOOR_FRONT_RIGHT].m_fAngle = car->Doors[DOOR_FRONT_RIGHT].m_fPrevAngle = vp->door_angles[1] * PI / 127.0f; + if (vp->door_angles[0]) + car->Damage.SetDoorStatus(DOOR_FRONT_LEFT, DOOR_STATUS_SWINGING); + if (vp->door_angles[1]) + car->Damage.SetDoorStatus(DOOR_FRONT_RIGHT, DOOR_STATUS_SWINGING); + if (vp->door_status & 1 && car->Damage.GetDoorStatus(DOOR_BONNET) != DOOR_STATUS_MISSING) { + car->Damage.SetDoorStatus(DOOR_BONNET, DOOR_STATUS_MISSING); + car->SetDoorDamage(CAR_BONNET, DOOR_BONNET, true); + } + if (vp->door_status & 2 && car->Damage.GetDoorStatus(DOOR_BOOT) != DOOR_STATUS_MISSING) { + car->Damage.SetDoorStatus(DOOR_BOOT, DOOR_STATUS_MISSING); + car->SetDoorDamage(CAR_BOOT, DOOR_BOOT, true); + } + if (vp->door_status & 4 && car->Damage.GetDoorStatus(DOOR_FRONT_LEFT) != DOOR_STATUS_MISSING) { + car->Damage.SetDoorStatus(DOOR_FRONT_LEFT, DOOR_STATUS_MISSING); + car->SetDoorDamage(CAR_DOOR_LF, DOOR_FRONT_LEFT, true); + } + if (vp->door_status & 8 && car->Damage.GetDoorStatus(DOOR_FRONT_RIGHT) != DOOR_STATUS_MISSING) { + car->Damage.SetDoorStatus(DOOR_FRONT_RIGHT, DOOR_STATUS_MISSING); + car->SetDoorDamage(CAR_DOOR_RF, DOOR_FRONT_RIGHT, true); + } + if (vp->door_status & 0x10 && car->Damage.GetDoorStatus(DOOR_REAR_LEFT) != DOOR_STATUS_MISSING) { + car->Damage.SetDoorStatus(DOOR_REAR_LEFT, DOOR_STATUS_MISSING); + car->SetDoorDamage(CAR_DOOR_LR, DOOR_REAR_LEFT, true); + } + if (vp->door_status & 0x20 && car->Damage.GetDoorStatus(DOOR_REAR_RIGHT) != DOOR_STATUS_MISSING) { + car->Damage.SetDoorStatus(DOOR_REAR_RIGHT, DOOR_STATUS_MISSING); + car->SetDoorDamage(CAR_DOOR_RR, DOOR_REAR_RIGHT, true); + } + } + vehicle->bEngineOn = true; + if (vehicle->IsCar()) + ((CAutomobile*)vehicle)->m_nDriveWheelsOnGround = 4; + CWorld::Remove(vehicle); + CWorld::Add(vehicle); + if (vehicle->IsBoat()) + ((CBoat*)vehicle)->m_bIsAnchored = false; + vehicle->bRenderScorched = vp->render_scorched; + if (vehicle->GetModelIndex() == MI_SKIMMER) + ((CBoat*)vehicle)->m_fMovingSpeed = vp->skimmer_speed / 50.0f; +} + +void CReplay::ProcessBikeUpdate(CVehicle* vehicle, float interpolation, CAddressInReplayBuffer* buffer) +{ + CBike* bike = (CBike*)vehicle; + tBikeUpdatePacket* vp = (tBikeUpdatePacket*)&buffer->m_pBase[buffer->m_nOffset]; + if (!vehicle) { + printf("Replay:Car wasn't there"); + return; + } + CMatrix vehicle_matrix; + vp->matrix.DecompressIntoFullMatrix(vehicle_matrix); + vehicle->GetMatrix() = vehicle->GetMatrix() * CMatrix(1.0f - interpolation); + vehicle->GetMatrix().GetPosition() *= (1.0f - interpolation); + vehicle->GetMatrix() += CMatrix(interpolation) * vehicle_matrix; + vehicle->m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + vehicle->m_fHealth = 4 * vp->health; + vehicle->m_fGasPedal = vp->acceleration / 100.0f; + vehicle->m_vecMoveSpeed = CVector(vp->velocityX / 8000.0f, vp->velocityY / 8000.0f, vp->velocityZ / 8000.0f); + vehicle->m_fSteerAngle = vp->wheel_state / 50.0f; + vehicle->bEngineOn = true; +#ifdef FIX_BUGS + for (int i = 0; i < 2; i++) + bike->m_aWheelRotation[i] = vp->wheel_rotation[i] / (128.0f / PI); + for (int i = 0; i < 2; i++) + bike->m_aWheelSpeed[i] = vp->wheel_rotation[i + 2] / (128.0f / PI); + for (int i = 0; i < 4; i++) + bike->m_aSuspensionSpringRatio[i] = vp->wheel_susp_dist[i] / 50.0f; +#else + for (int i = 0; i < 4; i++) { + bike->m_aSuspensionSpringRatio[i] = vp->wheel_susp_dist[i] / 50.0f; + bike->m_aWheelRotation[i] = vp->wheel_rotation[i] / (128.0f / PI); + } +#endif + bike->m_fLeanLRAngle = vp->lean_angle / 50.0f; + bike->m_fWheelAngle = vp->wheel_angle / 50.0f; + bike->bLeanMatrixClean = false; + bike->CalculateLeanMatrix(); + CWorld::Remove(vehicle); + CWorld::Add(vehicle); +} + +bool CReplay::PlayBackThisFrameInterpolation(CAddressInReplayBuffer *buffer, float interpolation, uint32 *pTimer) +{ + CBulletTraces::Init(); + float split = 1.0f - interpolation; + int ped_min_index = 0; /* Optimization due to peds and vehicles placed in buffer sequentially. */ + int vehicle_min_index = 0; /* So next ped can't have pool index less than current. */ + for(;;){ + uint8* ptr = buffer->m_pBase; + uint32 offset = buffer->m_nOffset; + uint8 type = ptr[offset]; + if (type == REPLAYPACKET_ENDOFFRAME) + break; + switch (type) { + case REPLAYPACKET_END: + { + int slot = buffer->m_bSlot; + if (BufferStatus[slot] == REPLAYBUFFER_RECORD) { + FinishPlayback(); + return true; + } + buffer->m_bSlot = (slot + 1) % NUM_REPLAYBUFFERS; + buffer->m_nOffset = 0; + buffer->m_pBase = Buffers[buffer->m_bSlot]; + ped_min_index = 0; + vehicle_min_index = 0; + break; + } + case REPLAYPACKET_VEHICLE: + { + tVehicleUpdatePacket* vp = (tVehicleUpdatePacket*)&ptr[offset]; + for (int i = vehicle_min_index; i < vp->index; i++) { + CVehicle* v = CPools::GetVehiclePool()->GetSlot(i); + if (!v) + continue; + /* Removing vehicles not present in this frame. */ + CWorld::Remove(v); + delete v; + } + vehicle_min_index = vp->index + 1; + CVehicle* v = CPools::GetVehiclePool()->GetSlot(vp->index); + CVehicle* new_v; + if (!v) { + int mi = vp->mi; + if (CStreaming::ms_aInfoForModel[mi].m_loadState != 1) { + CStreaming::RequestModel(mi, 0); + } + else { + switch (vp->vehicle_type) { + case VEHICLE_TYPE_CAR: + new_v = new(vp->index << 8) CAutomobile(mi, 2); + break; + case VEHICLE_TYPE_BOAT: + new_v = new(vp->index << 8) CBoat(mi, 2); + break; + case VEHICLE_TYPE_TRAIN: + new_v = new(vp->index << 8) CTrain(mi, 2); + break; + case VEHICLE_TYPE_HELI: + new_v = new(vp->index << 8) CHeli(mi, 2); + break; + case VEHICLE_TYPE_PLANE: + new_v = new(vp->index << 8) CPlane(mi, 2); + break; + case VEHICLE_TYPE_BIKE: // not possible + new_v = new(vp->index << 8) CBike(mi, 2); + break; + } + new_v->SetStatus(STATUS_PLAYER_PLAYBACKFROMBUFFER); + vp->matrix.DecompressIntoFullMatrix(new_v->GetMatrix()); + new_v->m_currentColour1 = vp->primary_color; + new_v->m_currentColour2 = vp->secondary_color; + CWorld::Add(new_v); + } + } + ProcessCarUpdate(CPools::GetVehiclePool()->GetSlot(vp->index), interpolation, buffer); + buffer->m_nOffset += sizeof(tVehicleUpdatePacket); + break; + } + case REPLAYPACKET_BIKE: + { + tBikeUpdatePacket* vp = (tBikeUpdatePacket*)&ptr[offset]; + for (int i = vehicle_min_index; i < vp->index; i++) { + CVehicle* v = CPools::GetVehiclePool()->GetSlot(i); + if (!v) + continue; + /* Removing vehicles not present in this frame. */ + CWorld::Remove(v); + delete v; + } + vehicle_min_index = vp->index + 1; + CVehicle* v = CPools::GetVehiclePool()->GetSlot(vp->index); + CVehicle* new_v; + if (!v) { + int mi = vp->mi; + if (CStreaming::ms_aInfoForModel[mi].m_loadState != 1) { + CStreaming::RequestModel(mi, 0); + } + else { + new_v = new(vp->index << 8) CBike(mi, 2); + new_v->SetStatus(STATUS_PLAYER_PLAYBACKFROMBUFFER); + vp->matrix.DecompressIntoFullMatrix(new_v->GetMatrix()); + new_v->m_currentColour1 = vp->primary_color; + new_v->m_currentColour2 = vp->secondary_color; + CWorld::Add(new_v); + } + } + ProcessBikeUpdate(CPools::GetVehiclePool()->GetSlot(vp->index), interpolation, buffer); + buffer->m_nOffset += sizeof(tBikeUpdatePacket); + break; + } + case REPLAYPACKET_PED_HEADER: + { + tPedHeaderPacket* ph = (tPedHeaderPacket*)&ptr[offset]; + if (!CPools::GetPedPool()->GetSlot(ph->index)) { + if (!CStreaming::HasModelLoaded(ph->mi) || (ph->mi >= MI_SPECIAL01 && ph->mi < MI_LAST_PED)) { + CStreaming::RequestModel(ph->mi, 0); + } + else { + CPed* new_p; + if (ph->pedtype != PEDTYPE_PLAYER1) + new_p = new(ph->index << 8) CCivilianPed((ePedType)ph->pedtype, ph->mi); + else + new_p = new(ph->index << 8) CPlayerPed(); + new_p->SetStatus(STATUS_PLAYER_PLAYBACKFROMBUFFER); + new_p->GetMatrix().SetUnity(); + CWorld::Add(new_p); + } + } + buffer->m_nOffset += sizeof(tPedHeaderPacket); + break; + } + case REPLAYPACKET_PED_UPDATE: + { + tPedUpdatePacket* pu = (tPedUpdatePacket*)&ptr[offset]; + for (int i = ped_min_index; i < pu->index; i++) { + CPed* p = CPools::GetPedPool()->GetSlot(i); + if (!p) + continue; + /* Removing peds not present in this frame. */ + CWorld::Remove(p); + delete p; + } + ped_min_index = pu->index + 1; + ProcessPedUpdate(CPools::GetPedPool()->GetSlot(pu->index), interpolation, buffer); + break; + } + case REPLAYPACKET_GENERAL: + { + tGeneralPacket* pg = (tGeneralPacket*)&ptr[offset]; + TheCamera.GetMatrix() = TheCamera.GetMatrix() * CMatrix(split); + TheCamera.GetMatrix().GetPosition() *= split; + TheCamera.GetMatrix() += CMatrix(interpolation) * pg->camera_pos; + RwMatrix* pm = RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera)); + pm->pos = TheCamera.GetPosition(); + pm->at = TheCamera.GetForward(); + pm->up = TheCamera.GetUp(); + pm->right = TheCamera.GetRight(); + CameraFocusX = split * CameraFocusX + interpolation * pg->player_pos.x; + CameraFocusY = split * CameraFocusY + interpolation * pg->player_pos.y; + CameraFocusZ = split * CameraFocusZ + interpolation * pg->player_pos.z; + bPlayerInRCBuggy = pg->in_rcvehicle; + buffer->m_nOffset += sizeof(tGeneralPacket); + break; + } + case REPLAYPACKET_CLOCK: + { + tClockPacket* pc = (tClockPacket*)&ptr[offset]; + CClock::SetGameClock(pc->hours, pc->minutes); + buffer->m_nOffset += sizeof(tClockPacket); + break; + } + case REPLAYPACKET_WEATHER: + { + tWeatherPacket* pw = (tWeatherPacket*)&ptr[offset]; + CWeather::OldWeatherType = pw->old_weather; + CWeather::NewWeatherType = pw->new_weather; + CWeather::InterpolationValue = pw->interpolation; + buffer->m_nOffset += sizeof(tWeatherPacket); + break; + } + case REPLAYPACKET_ENDOFFRAME: + { + /* Not supposed to be here. */ + assert(false); + buffer->m_nOffset++; + break; + } + case REPLAYPACKET_TIMER: + { + tTimerPacket* pt = (tTimerPacket*)&ptr[offset]; + if (pTimer) + *pTimer = pt->timer; + CTimer::SetTimeInMilliseconds(pt->timer); + buffer->m_nOffset += sizeof(tTimerPacket); + break; + } + case REPLAYPACKET_BULLET_TRACES: + { + tBulletTracePacket* pb = (tBulletTracePacket*)&ptr[offset]; + CBulletTraces::aTraces[pb->index].m_bInUse = true; + CBulletTraces::aTraces[pb->index].m_vecStartPos = pb->inf; + CBulletTraces::aTraces[pb->index].m_vecEndPos = pb->sup; + buffer->m_nOffset += sizeof(tBulletTracePacket); + break; + } + case REPLAYPACKET_PARTICLE: + { + tParticlePacket* pp = (tParticlePacket*)&ptr[offset]; + CVector pos(pp->pos_x / 4.0f, pp->pos_y / 4.0f, pp->pos_z / 4.0f); + CVector dir(pp->dir_x / 120.0f, pp->dir_y / 120.0f, pp->dir_z / 120.0f); + RwRGBA color; + color.red = pp->r; + color.green = pp->g; + color.blue = pp->b; + color.alpha = pp->a; + CParticle::AddParticle((tParticleType)pp->particle_type, pos, dir, nil, pp->size, color); + buffer->m_nOffset += sizeof(tParticlePacket); + break; + } + case REPLAYPACKET_MISC: + { + tMiscPacket* pm = (tMiscPacket*)&ptr[offset]; + TheCamera.m_uiCamShakeStart = pm->cam_shake_start; + TheCamera.m_fCamShakeForce = pm->cam_shake_strength; + CSpecialFX::bVideoCam = pm->video_cam; + CSpecialFX::bLiftCam = pm->lift_cam; + CGame::currArea = pm->cur_area; + buffer->m_nOffset += sizeof(tMiscPacket); + break; + } + default: + break; + } + } + buffer->m_nOffset += 4; + for (int i = vehicle_min_index; i < CPools::GetVehiclePool()->GetSize(); i++) { + CVehicle* v = CPools::GetVehiclePool()->GetSlot(i); + if (!v) + continue; + /* Removing vehicles not present in this frame. */ + CWorld::Remove(v); + delete v; + } + for (int i = ped_min_index; i < CPools::GetPedPool()->GetSize(); i++) { + CPed* p = CPools::GetPedPool()->GetSlot(i); + if (!p) + continue; + /* Removing peds not present in this frame. */ + CWorld::Remove(p); + delete p; + } + ProcessReplayCamera(); + return false; +} + +void CReplay::FinishPlayback(void) +{ + if (Mode != MODE_PLAYBACK) + return; + EmptyAllPools(); + RestoreStuffFromMem(); + Mode = MODE_RECORD; + if (bDoLoadSceneWhenDone){ + CVector v_ls(LoadSceneX, LoadSceneY, LoadSceneZ); + CGame::currLevel = CTheZones::GetLevelFromPosition(&v_ls); + CCollision::SortOutCollisionAfterLoad(); + CStreaming::LoadScene(v_ls); + } + bDoLoadSceneWhenDone = false; + if (bPlayingBackFromFile){ + Init(); + MarkEverythingAsNew(); + } + DMAudio.SetEffectsFadeVol(127); + DMAudio.SetMusicFadeVol(127); +} + +void CReplay::EmptyReplayBuffer(void) +{ + if (Mode == MODE_PLAYBACK) + return; + Record.m_nOffset = 0; + for (int i = 0; i < NUM_REPLAYBUFFERS; i++){ + BufferStatus[i] = REPLAYBUFFER_UNUSED; + } + Record.m_bSlot = 0; + Record.m_pBase = Buffers[0]; + BufferStatus[0] = REPLAYBUFFER_RECORD; + Record.m_pBase[Record.m_nOffset] = 0; + MarkEverythingAsNew(); +} + +void CReplay::ProcessReplayCamera(void) +{ + switch (CameraMode) { + case REPLAYCAMMODE_TOPDOWN: + { + TheCamera.SetPosition(CameraFocusX, CameraFocusY, CameraFocusZ + 15.0f); + TheCamera.GetForward() = CVector(0.0f, 0.0f, -1.0f); + TheCamera.GetUp() = CVector(0.0f, 1.0f, 0.0f); + TheCamera.GetRight() = CVector(1.0f, 0.0f, 0.0f); + RwMatrix* pm = RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera)); + pm->pos = TheCamera.GetPosition(); + pm->at = TheCamera.GetForward(); + pm->up = TheCamera.GetUp(); + pm->right = TheCamera.GetRight(); + break; + } + case REPLAYCAMMODE_FIXED: + { + TheCamera.GetMatrix().GetPosition() = CVector(CameraFixedX, CameraFixedY, CameraFixedZ); + CVector forward(CameraFocusX - CameraFixedX, CameraFocusY - CameraFixedY, CameraFocusZ - CameraFixedZ); + forward.Normalise(); + CVector right = CrossProduct(CVector(0.0f, 0.0f, 1.0f), forward); + right.Normalise(); + CVector up = CrossProduct(forward, right); + up.Normalise(); + TheCamera.GetMatrix().GetForward() = forward; + TheCamera.GetMatrix().GetUp() = up; + TheCamera.GetMatrix().GetRight() = right; + RwMatrix* pm = RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera)); + pm->pos = TheCamera.GetMatrix().GetPosition(); + pm->at = TheCamera.GetMatrix().GetForward(); + pm->up = TheCamera.GetMatrix().GetUp(); + pm->right = TheCamera.GetMatrix().GetRight(); + break; + } + default: + break; + } + TheCamera.m_vecGameCamPos = TheCamera.GetMatrix().GetPosition(); + TheCamera.CalculateDerivedValues(); + RwMatrixUpdate(RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera))); + RwFrameUpdateObjects(RwCameraGetFrame(TheCamera.m_pRwCamera)); +} + +extern CWeaponEffects gCrossHair; + +void CReplay::TriggerPlayback(uint8 cam_mode, float cam_x, float cam_y, float cam_z, bool load_scene) +{ + if (Mode != MODE_RECORD) + return; + CameraFixedX = cam_x; + CameraFixedY = cam_y; + CameraFixedZ = cam_z; + Mode = MODE_PLAYBACK; + FramesActiveLookAroundCam = 0; + CameraMode = cam_mode; + bAllowLookAroundCam = true; + bPlayingBackFromFile = false; + OldRadioStation = DMAudio.GetRadioInCar(); + DMAudio.ChangeMusicMode(MUSICMODE_FRONTEND); + DMAudio.SetEffectsFadeVol(0); + DMAudio.SetMusicFadeVol(0); + CEscalators::Shutdown(); + CWaterCreatures::RemoveAll(); + int current; + for (current = 0; current < NUM_REPLAYBUFFERS; current++) + if (BufferStatus[current] == REPLAYBUFFER_RECORD) + break; + int first; + for (first = (current + 1) % NUM_REPLAYBUFFERS; ; first = (first + 1) % NUM_REPLAYBUFFERS) + if (BufferStatus[first] == REPLAYBUFFER_RECORD || BufferStatus[first] == REPLAYBUFFER_PLAYBACK) + break; + Playback.m_bSlot = first; + Playback.m_nOffset = 0; + Playback.m_pBase = Buffers[first]; + CObject::DeleteAllTempObjectsInArea(CVector(0.0f, 0.0f, 0.0f), 1000000.0f); + StoreStuffInMem(); + EmptyPedsAndVehiclePools(); + SlowMotion = 1; + CSkidmarks::Clear(); + StreamAllNecessaryCarsAndPeds(); + if (load_scene) + bDoLoadSceneWhenDone = false; + else{ + bDoLoadSceneWhenDone = true; + LoadSceneX = TheCamera.GetPosition().x; + LoadSceneY = TheCamera.GetPosition().y; + LoadSceneZ = TheCamera.GetPosition().z; + CVector ff_coord; + FindFirstFocusCoordinate(&ff_coord); + CGame::currLevel = CTheZones::GetLevelFromPosition(&ff_coord); + CCollision::SortOutCollisionAfterLoad(); + CStreaming::LoadScene(ff_coord); + } + if (cam_mode == REPLAYCAMMODE_ASSTORED) + TheCamera.CarZoomIndicator = CAM_ZOOM_CINEMATIC; + gCrossHair.m_bActive = false; + CExplosion::ClearAllExplosions(); + CPlaneBanners::Init(); +#ifndef FIX_BUGS // this doesn't do anything useful and accesses destroyed player ped + TheCamera.Restore(); +#endif + CDraw::SetFOV(70.0f); +} + +void CReplay::StoreStuffInMem(void) +{ +#ifdef FIX_BUGS + for (int i = 0; i < NUMPLAYERS; i++) + nHandleOfPlayerPed[i] = CPools::GetPedPool()->GetIndex(CWorld::Players[i].m_pPed); +#endif + int i = CPools::GetPedPool()->GetSize(); + while (--i >= 0) { + CPed* ped = CPools::GetPedPool()->GetSlot(i); + if (!ped) + continue; + if (ped->m_attractor) + GetPedAttractorManager()->DeRegisterPed(ped, ped->m_attractor); + } + CPools::GetVehiclePool()->Store(pBuf0, pBuf1); + CPools::GetPedPool()->Store(pBuf2, pBuf3); + CPools::GetObjectPool()->Store(pBuf4, pBuf5); + CPools::GetPtrNodePool()->Store(pBuf6, pBuf7); + CPools::GetEntryInfoNodePool()->Store(pBuf8, pBuf9); + CPools::GetDummyPool()->Store(pBuf10, pBuf11); + pWorld1 = new uint8[sizeof(CSector) * NUMSECTORS_X * NUMSECTORS_Y]; + memcpy(pWorld1, CWorld::GetSector(0, 0), NUMSECTORS_X * NUMSECTORS_Y * sizeof(CSector)); + WorldPtrList = CWorld::GetMovingEntityList().first; // why + BigBuildingPtrList = CWorld::GetBigBuildingList(LEVEL_GENERIC).first; + pPickups = new uint8[sizeof(CPickup) * NUMPICKUPS]; + memcpy(pPickups, CPickups::aPickUps, NUMPICKUPS * sizeof(CPickup)); + pReferences = new uint8[(sizeof(CReference) * NUMREFERENCES)]; + memcpy(pReferences, CReferences::aRefs, NUMREFERENCES * sizeof(CReference)); + pEmptyReferences = CReferences::pEmptyList; + pStoredCam = new uint8[sizeof(CCamera)]; + memcpy(pStoredCam, &TheCamera, sizeof(CCamera)); + pRadarBlips = new uint8[sizeof(sRadarTrace) * NUMRADARBLIPS]; + memcpy(pRadarBlips, CRadar::ms_RadarTrace, NUMRADARBLIPS * sizeof(sRadarTrace)); + PlayerWanted = *FindPlayerPed()->m_pWanted; + PlayerInfo = CWorld::Players[0]; + Time1 = CTimer::GetTimeInMilliseconds(); + Time2 = CTimer::GetTimeInMillisecondsNonClipped(); + Time3 = CTimer::GetPreviousTimeInMilliseconds(); + Time4 = CTimer::GetTimeInMillisecondsPauseMode(); + Frame = CTimer::GetFrameCounter(); + ClockHours = CClock::GetHours(); + ClockMinutes = CClock::GetMinutes(); + OldWeatherType = CWeather::OldWeatherType; + NewWeatherType = CWeather::NewWeatherType; + WeatherInterpolationValue = CWeather::InterpolationValue; + CurrArea = CGame::currArea; + TimeStepNonClipped = CTimer::GetTimeStepNonClipped(); + TimeStep = CTimer::GetTimeStep(); + TimeScale = CTimer::GetTimeScale(); + ms_nNumCivMale_Stored = CPopulation::ms_nNumCivMale; + ms_nNumCivFemale_Stored = CPopulation::ms_nNumCivFemale; + ms_nNumCop_Stored = CPopulation::ms_nNumCop; + ms_nNumEmergency_Stored = CPopulation::ms_nNumEmergency; + ms_nNumGang1_Stored = CPopulation::ms_nNumGang1; + ms_nNumGang2_Stored = CPopulation::ms_nNumGang2; + ms_nNumGang3_Stored = CPopulation::ms_nNumGang3; + ms_nNumGang4_Stored = CPopulation::ms_nNumGang4; + ms_nNumGang5_Stored = CPopulation::ms_nNumGang5; + ms_nNumGang6_Stored = CPopulation::ms_nNumGang6; + ms_nNumGang7_Stored = CPopulation::ms_nNumGang7; + ms_nNumGang8_Stored = CPopulation::ms_nNumGang8; + ms_nNumGang9_Stored = CPopulation::ms_nNumGang9; + ms_nNumDummy_Stored = CPopulation::ms_nNumDummy; + ms_nTotalCivPeds_Stored = CPopulation::ms_nTotalCivPeds; + ms_nTotalGangPeds_Stored = CPopulation::ms_nTotalGangPeds; + ms_nTotalPeds_Stored = CPopulation::ms_nTotalPeds; + ms_nTotalMissionPeds_Stored = CPopulation::ms_nTotalMissionPeds; + int size = CPools::GetPedPool()->GetSize(); + pPedAnims = new CStoredDetailedAnimationState[size]; + for (int i = 0; i < size; i++) { + CPed* ped = CPools::GetPedPool()->GetSlot(i); + if (ped) + StoreDetailedPedAnimation(ped, &pPedAnims[i]); + } + pGarages = new uint8[sizeof(CGarages::aGarages)]; + memcpy(pGarages, CGarages::aGarages, sizeof(CGarages::aGarages)); + FireArray = new CFire[NUM_FIRES]; + memcpy(FireArray, gFireManager.m_aFires, sizeof(gFireManager.m_aFires)); + NumOfFires = gFireManager.m_nTotalFires; + paProjectileInfo = new uint8[sizeof(gaProjectileInfo)]; + memcpy(paProjectileInfo, gaProjectileInfo, sizeof(gaProjectileInfo)); + paProjectiles = new uint8[sizeof(CProjectileInfo::ms_apProjectile)]; + memcpy(paProjectiles, CProjectileInfo::ms_apProjectile, sizeof(CProjectileInfo::ms_apProjectile)); + CScriptPaths::Save_ForReplay(); +} + +void CReplay::RestoreStuffFromMem(void) +{ + CPools::GetVehiclePool()->CopyBack(pBuf0, pBuf1); + CPools::GetPedPool()->CopyBack(pBuf2, pBuf3); + CPools::GetObjectPool()->CopyBack(pBuf4, pBuf5); + CPools::GetPtrNodePool()->CopyBack(pBuf6, pBuf7); + CPools::GetEntryInfoNodePool()->CopyBack(pBuf8, pBuf9); + CPools::GetDummyPool()->CopyBack(pBuf10, pBuf11); + memcpy(CWorld::GetSector(0, 0), pWorld1, sizeof(CSector) * NUMSECTORS_X * NUMSECTORS_Y); + delete[] pWorld1; + pWorld1 = nil; + CWorld::GetMovingEntityList().first = WorldPtrList; + CWorld::GetBigBuildingList(LEVEL_GENERIC).first = BigBuildingPtrList; + memcpy(CPickups::aPickUps, pPickups, sizeof(CPickup) * NUMPICKUPS); + delete[] pPickups; + pPickups = nil; + memcpy(CReferences::aRefs, pReferences, sizeof(CReference) * NUMREFERENCES); + delete[] pReferences; + pReferences = nil; + CReferences::pEmptyList = pEmptyReferences; + pEmptyReferences = nil; + memcpy(&TheCamera, pStoredCam, sizeof(CCamera)); + delete[] pStoredCam; + pStoredCam = nil; + memcpy(CRadar::ms_RadarTrace, pRadarBlips, sizeof(sRadarTrace) * NUMRADARBLIPS); + delete[] pRadarBlips; + pRadarBlips = nil; +#ifdef FIX_BUGS + for (int i = 0; i < NUMPLAYERS; i++) { + CPlayerPed* pPlayerPed = (CPlayerPed*)CPools::GetPedPool()->GetAt(nHandleOfPlayerPed[i]); + assert(pPlayerPed); + CWorld::Players[i].m_pPed = pPlayerPed; + pPlayerPed->RegisterReference((CEntity**)&CWorld::Players[i].m_pPed); + } +#endif + FindPlayerPed()->m_pWanted = new CWanted(PlayerWanted); + CWorld::Players[0] = PlayerInfo; + int i = CPools::GetPedPool()->GetSize(); + while (--i >= 0) { + CPed* ped = CPools::GetPedPool()->GetSlot(i); + if (!ped) + continue; + int mi = ped->GetModelIndex(); + CStreaming::RequestModel(mi, 0); + CStreaming::LoadAllRequestedModels(false); + ped->m_rwObject = nil; + ped->m_modelIndex = -1; + ped->SetModelIndex(mi); + ped->m_pVehicleAnim = nil; + ped->m_audioEntityId = DMAudio.CreateEntity(AUDIOTYPE_PHYSICAL, ped); + DMAudio.SetEntityStatus(ped->m_audioEntityId, TRUE); + CPopulation::UpdatePedCount((ePedType)ped->m_nPedType, false); + for (int j = 0; j < TOTAL_WEAPON_SLOTS; j++) { + int mi1 = CWeaponInfo::GetWeaponInfo(ped->m_weapons[j].m_eWeaponType)->m_nModelId; + if (mi1 != -1) + CStreaming::RequestModel(mi1, STREAMFLAGS_DEPENDENCY); + int mi2 = CWeaponInfo::GetWeaponInfo(ped->m_weapons[j].m_eWeaponType)->m_nModel2Id; + if (mi2 != -1) + CStreaming::RequestModel(mi2, STREAMFLAGS_DEPENDENCY); + CStreaming::LoadAllRequestedModels(false); + ped->m_weapons[j].Initialise(ped->m_weapons[j].m_eWeaponType, ped->m_weapons[j].m_nAmmoTotal); + } + if (ped->m_wepModelID >= 0) { + ped->m_pWeaponModel = nil; + if (ped->IsPlayer()) + ((CPlayerPed*)ped)->m_pMinigunTopAtomic = nil; + ped->AddWeaponModel(ped->m_wepModelID); + } + if (ped->m_nPedType == PEDTYPE_COP) + ((CCopPed*)ped)->m_pStinger = new CStinger; + } + i = CPools::GetVehiclePool()->GetSize(); + while (--i >= 0) { + CVehicle* vehicle = CPools::GetVehiclePool()->GetSlot(i); + if (!vehicle) + continue; + int mi = vehicle->GetModelIndex(); + CStreaming::RequestModel(mi, 0); + CStreaming::LoadAllRequestedModels(false); + vehicle->m_rwObject = nil; + vehicle->m_modelIndex = -1; + vehicle->SetModelIndex(mi); + if (vehicle->IsCar()) { + CAutomobile* car = (CAutomobile*)vehicle; + if (mi == MI_DODO) { + RpAtomicSetFlags((RpAtomic*)GetFirstObject(car->m_aCarNodes[CAR_WHEEL_LF]), 0); + CMatrix tmp1; + tmp1.Attach(RwFrameGetMatrix(car->m_aCarNodes[CAR_WHEEL_RF]), false); + CMatrix tmp2(RwFrameGetMatrix(car->m_aCarNodes[CAR_WHEEL_LF]), false); + tmp1.GetPosition() += CVector(tmp2.GetPosition().x + 0.1f, 0.0f, tmp2.GetPosition().z); + tmp1.UpdateRW(); + } + else if (mi == MI_HUNTER) { + RpAtomicSetFlags((RpAtomic*)GetFirstObject(car->m_aCarNodes[CAR_WHEEL_LB]), 0); + RpAtomicSetFlags((RpAtomic*)GetFirstObject(car->m_aCarNodes[CAR_WHEEL_RB]), 0); + } + else if (vehicle->IsRealHeli()) { + RpAtomicSetFlags((RpAtomic*)GetFirstObject(car->m_aCarNodes[CAR_WHEEL_LF]), 0); + RpAtomicSetFlags((RpAtomic*)GetFirstObject(car->m_aCarNodes[CAR_WHEEL_RF]), 0); + RpAtomicSetFlags((RpAtomic*)GetFirstObject(car->m_aCarNodes[CAR_WHEEL_LB]), 0); + RpAtomicSetFlags((RpAtomic*)GetFirstObject(car->m_aCarNodes[CAR_WHEEL_RB]), 0); + } + } + if (vehicle->IsCar()){ + CAutomobile* car = (CAutomobile*)vehicle; + int32 panels = car->Damage.m_panelStatus; + car->Damage.m_panelStatus = 0; + ApplyPanelDamageToCar(panels, car, true); + car->SetDoorDamage(CAR_BONNET, DOOR_BONNET, true); + car->SetDoorDamage(CAR_BOOT, DOOR_BOOT, true); + car->SetDoorDamage(CAR_DOOR_LF, DOOR_FRONT_LEFT, true); + car->SetDoorDamage(CAR_DOOR_RF, DOOR_FRONT_RIGHT, true); + car->SetDoorDamage(CAR_DOOR_LR, DOOR_REAR_LEFT, true); + car->SetDoorDamage(CAR_DOOR_RR, DOOR_REAR_RIGHT, true); + } + vehicle->m_audioEntityId = DMAudio.CreateEntity(AUDIOTYPE_PHYSICAL, vehicle); + DMAudio.SetEntityStatus(vehicle->m_audioEntityId, TRUE); + CCarCtrl::UpdateCarCount(vehicle, false); + if ((mi == MI_AIRTRAIN || mi == MI_DEADDODO) && vehicle->m_rwObject){ + CVehicleModelInfo* info = (CVehicleModelInfo*)CModelInfo::GetModelInfo(mi); + if (RwObjectGetType(vehicle->m_rwObject) == rpATOMIC){ + vehicle->GetMatrix().Detach(); + if (vehicle->m_rwObject){ + if (RwObjectGetType(vehicle->m_rwObject) == rpATOMIC){ + RwFrame* frame = RpAtomicGetFrame((RpAtomic*)vehicle->m_rwObject); + RpAtomicDestroy((RpAtomic*)vehicle->m_rwObject); + RwFrameDestroy(frame); + } + vehicle->m_rwObject = nil; + } + }else{ + vehicle->DeleteRwObject(); + int model_id = info->m_wheelId; + if (model_id != -1){ + if ((vehicle->m_rwObject = CModelInfo::GetModelInfo(model_id)->CreateInstance())){ + vehicle->GetMatrix().AttachRW(RwFrameGetMatrix(RpClumpGetFrame((RpClump*)vehicle->m_rwObject)), false); + } + } + } + } + } + PrintElementsInPtrList(); + i = CPools::GetObjectPool()->GetSize(); + while (--i >= 0) { + CObject* object = CPools::GetObjectPool()->GetSlot(i); + if (!object) + continue; + int mi = object->GetModelIndex(); + object->m_rwObject = nil; + object->m_modelIndex = -1; + object->SetModelIndexNoCreate(mi); + object->GetMatrix().m_attachment = nil; + } + i = CPools::GetDummyPool()->GetSize(); + while (--i >= 0) { + CDummy* dummy = CPools::GetDummyPool()->GetSlot(i); + if (!dummy) + continue; + int mi = dummy->GetModelIndex(); + dummy->m_rwObject = nil; + dummy->m_modelIndex = -1; + dummy->SetModelIndexNoCreate(mi); + dummy->GetMatrix().m_attachment = nil; + } + ++ClockMinutes; + CTimer::SetTimeInMilliseconds(Time1); + CTimer::SetTimeInMillisecondsNonClipped(Time2); + CTimer::SetPreviousTimeInMilliseconds(Time3); + CTimer::SetTimeInMillisecondsPauseMode(Time4); + CTimer::SetTimeScale(TimeScale); + CTimer::SetFrameCounter(Frame); + CTimer::SetTimeStep(TimeStep); + CTimer::SetTimeStepNonClipped(TimeStepNonClipped); + CClock::SetGameClock(ClockHours, ClockMinutes); + CWeather::OldWeatherType = OldWeatherType; + CWeather::NewWeatherType = NewWeatherType; + CWeather::InterpolationValue = WeatherInterpolationValue; + CGame::currArea = CurrArea; + CPopulation::ms_nNumCivMale = ms_nNumCivMale_Stored; + CPopulation::ms_nNumCivFemale = ms_nNumCivFemale_Stored; + CPopulation::ms_nNumCop = ms_nNumCop_Stored; + CPopulation::ms_nNumEmergency = ms_nNumEmergency_Stored; + CPopulation::ms_nNumGang1 = ms_nNumGang1_Stored; + CPopulation::ms_nNumGang2 = ms_nNumGang2_Stored; + CPopulation::ms_nNumGang3 = ms_nNumGang3_Stored; + CPopulation::ms_nNumGang4 = ms_nNumGang4_Stored; + CPopulation::ms_nNumGang5 = ms_nNumGang5_Stored; + CPopulation::ms_nNumGang6 = ms_nNumGang6_Stored; + CPopulation::ms_nNumGang7 = ms_nNumGang7_Stored; + CPopulation::ms_nNumGang8 = ms_nNumGang8_Stored; + CPopulation::ms_nNumGang9 = ms_nNumGang9_Stored; + CPopulation::ms_nNumDummy = ms_nNumDummy_Stored; + CPopulation::ms_nTotalCivPeds = ms_nTotalCivPeds_Stored; + CPopulation::ms_nTotalGangPeds = ms_nTotalGangPeds_Stored; + CPopulation::ms_nTotalPeds = ms_nTotalPeds_Stored; + CPopulation::ms_nTotalMissionPeds = ms_nTotalMissionPeds_Stored; + for (int i = 0; i < CPools::GetPedPool()->GetSize(); i++) { + CPed* ped = CPools::GetPedPool()->GetSlot(i); + if (!ped) + continue; + RetrieveDetailedPedAnimation(ped, &pPedAnims[i]); + } + delete[] pPedAnims; + pPedAnims = nil; + memcpy(CGarages::aGarages, pGarages, sizeof(CGarages::aGarages)); + delete[] pGarages; + pGarages = nil; + memcpy(gFireManager.m_aFires, FireArray, sizeof(gFireManager.m_aFires)); + delete[] FireArray; + FireArray = nil; + gFireManager.m_nTotalFires = NumOfFires; + memcpy(gaProjectileInfo, paProjectileInfo, sizeof(gaProjectileInfo)); + delete[] paProjectileInfo; + paProjectileInfo = nil; + memcpy(CProjectileInfo::ms_apProjectile, paProjectiles, sizeof(CProjectileInfo::ms_apProjectile)); + delete[] paProjectiles; + paProjectiles = nil; + CScriptPaths::Load_ForReplay(); + CExplosion::ClearAllExplosions(); + DMAudio.ChangeMusicMode(MUSICMODE_FRONTEND); + DMAudio.SetRadioInCar(OldRadioStation); + DMAudio.ChangeMusicMode(MUSICMODE_GAME); +} + +void CReplay::EmptyPedsAndVehiclePools(void) +{ + int i = CPools::GetVehiclePool()->GetSize(); + while (--i >= 0) { + CVehicle* v = CPools::GetVehiclePool()->GetSlot(i); + if (!v) + continue; + CWorld::Remove(v); + delete v; + } + i = CPools::GetPedPool()->GetSize(); + while (--i >= 0) { + CPed* p = CPools::GetPedPool()->GetSlot(i); + if (!p) + continue; + CWorld::Remove(p); + delete p; + } +} + +void CReplay::EmptyAllPools(void) +{ + EmptyPedsAndVehiclePools(); + int i = CPools::GetObjectPool()->GetSize(); + while (--i >= 0) { + CObject* o = CPools::GetObjectPool()->GetSlot(i); + if (!o) + continue; + CWorld::Remove(o); + delete o; + } + i = CPools::GetDummyPool()->GetSize(); + while (--i >= 0) { + CDummy* d = CPools::GetDummyPool()->GetSlot(i); + if (!d) + continue; + CWorld::Remove(d); + delete d; + } +} + +void CReplay::MarkEverythingAsNew(void) +{ + int i = CPools::GetVehiclePool()->GetSize(); + while (--i >= 0) { + CVehicle* v = CPools::GetVehiclePool()->GetSlot(i); + if (!v) + continue; + v->bHasAlreadyBeenRecorded = false; + } + i = CPools::GetPedPool()->GetSize(); + while (--i >= 0) { + CPed* p = CPools::GetPedPool()->GetSlot(i); + if (!p) + continue; + p->bHasAlreadyBeenRecorded = false; + } +} + +void CReplay::SaveReplayToHD(void) +{ + CFileMgr::SetDirMyDocuments(); + int fw = CFileMgr::OpenFileForWriting("replay.rep"); +#ifdef FIX_REPLAY_BUGS + if (fw == 0) { +#else + if (fw < 0){ // BUG? +#endif + printf("Couldn't open replay.rep for writing"); + CFileMgr::SetDir(""); + return; + } + CFileMgr::Write(fw, "gta3_7f", sizeof("gta3_7f")); + int current; + for (current = 0; current < NUM_REPLAYBUFFERS; current++) + if (BufferStatus[current] == REPLAYBUFFER_RECORD) + break; + int first; + for (first = (current + 1) % NUM_REPLAYBUFFERS; ; first = (first + 1) % NUM_REPLAYBUFFERS) + if (BufferStatus[first] == REPLAYBUFFER_RECORD || BufferStatus[first] == REPLAYBUFFER_PLAYBACK) + break; + for(int i = first; ; i = (i + 1) % NUM_REPLAYBUFFERS){ + CFileMgr::Write(fw, (char*)Buffers[i], sizeof(Buffers[i])); + if (BufferStatus[i] == REPLAYBUFFER_RECORD) + break; + } + CFileMgr::CloseFile(fw); + CFileMgr::SetDir(""); +} + +void CReplay::PlayReplayFromHD(void) +{ + CFileMgr::SetDirMyDocuments(); + int fr = CFileMgr::OpenFile("replay.rep", "rb"); + if (fr == 0) { + printf("Couldn't open replay.rep for reading"); +#ifdef FIX_REPLAY_BUGS + CFileMgr::SetDir(""); +#endif + return; + } + CFileMgr::Read(fr, gString, 8); + if (strncmp(gString, "gta3_7f", sizeof("gta3_7f"))){ + CFileMgr::CloseFile(fr); + printf("Wrong file type for replay"); + CFileMgr::SetDir(""); + return; + } + int slot; + for (slot = 0; CFileMgr::Read(fr, (char*)Buffers[slot], sizeof(Buffers[slot])); slot++) + BufferStatus[slot] = REPLAYBUFFER_PLAYBACK; + BufferStatus[slot - 1] = REPLAYBUFFER_RECORD; + while (slot < NUM_REPLAYBUFFERS) + BufferStatus[slot++] = REPLAYBUFFER_UNUSED; + CFileMgr::CloseFile(fr); + CFileMgr::SetDir(""); + TriggerPlayback(REPLAYCAMMODE_ASSTORED, 0.0f, 0.0f, 0.0f, false); + bPlayingBackFromFile = true; + bAllowLookAroundCam = true; + StreamAllNecessaryCarsAndPeds(); +} + +void CReplay::StreamAllNecessaryCarsAndPeds(void) +{ + for (int slot = 0; slot < NUM_REPLAYBUFFERS; slot++) { + if (BufferStatus[slot] == REPLAYBUFFER_UNUSED) + continue; + for (size_t offset = 0; Buffers[slot][offset] != REPLAYPACKET_END; offset += FindSizeOfPacket(Buffers[slot][offset])) { + switch (Buffers[slot][offset]) { + case REPLAYPACKET_VEHICLE: + CStreaming::RequestModel(((tVehicleUpdatePacket*)&Buffers[slot][offset])->mi, 0); + break; + case REPLAYPACKET_BIKE: + CStreaming::RequestModel(((tBikeUpdatePacket*)&Buffers[slot][offset])->mi, 0); + break; + case REPLAYPACKET_PED_HEADER: + CStreaming::RequestModel(((tPedHeaderPacket*)&Buffers[slot][offset])->mi, 0); + break; + default: + break; + } + } + } + CStreaming::LoadAllRequestedModels(false); +} + +void CReplay::FindFirstFocusCoordinate(CVector *coord) +{ + *coord = CVector(0.0f, 0.0f, 0.0f); + for (int slot = 0; slot < NUM_REPLAYBUFFERS; slot++) { + if (BufferStatus[slot] == REPLAYBUFFER_UNUSED) + continue; + for (size_t offset = 0; Buffers[slot][offset] != REPLAYPACKET_END; offset += FindSizeOfPacket(Buffers[slot][offset])) { + if (Buffers[slot][offset] == REPLAYPACKET_GENERAL) { + *coord = ((tGeneralPacket*)&Buffers[slot][offset])->player_pos; + return; + } + } + } +} + +bool CReplay::ShouldStandardCameraBeProcessed(void) +{ + if (Mode != MODE_PLAYBACK) + return true; + if (FramesActiveLookAroundCam || bPlayerInRCBuggy) + return false; + return FindPlayerVehicle() != nil; +} + +void CReplay::ProcessLookAroundCam(void) +{ + if (!bAllowLookAroundCam) + return; + float x_moved = CPad::NewMouseControllerState.x / 200.0f; + float y_moved = CPad::NewMouseControllerState.y / 200.0f; + if (x_moved > 0.01f || y_moved > 0.01f) { + if (FramesActiveLookAroundCam == 0) + fDistanceLookAroundCam = 9.0f; + FramesActiveLookAroundCam = 60; + } + if (bPlayerInRCBuggy) + FramesActiveLookAroundCam = 0; + if (!FramesActiveLookAroundCam) + return; + --FramesActiveLookAroundCam; + fBetaAngleLookAroundCam += x_moved; + if (CPad::NewMouseControllerState.LMB && CPad::NewMouseControllerState.RMB) + fDistanceLookAroundCam = Max(3.0f, Min(15.0f, fDistanceLookAroundCam + 2.0f * y_moved)); + else + fAlphaAngleLookAroundCam = Max(0.1f, Min(1.5f, fAlphaAngleLookAroundCam + y_moved)); + CVector camera_pt( + fDistanceLookAroundCam * Sin(fBetaAngleLookAroundCam) * Cos(fAlphaAngleLookAroundCam), + fDistanceLookAroundCam * Cos(fBetaAngleLookAroundCam) * Cos(fAlphaAngleLookAroundCam), + fDistanceLookAroundCam * Sin(fAlphaAngleLookAroundCam) + ); + CVector focus = CVector(CameraFocusX, CameraFocusY, CameraFocusZ); + camera_pt += focus; + CColPoint cp; + CEntity* pe = nil; + if (CWorld::ProcessLineOfSight(focus, camera_pt, cp, pe, true, false, false, false, false, true, true)){ + camera_pt = cp.point; + CVector direction = focus - cp.point; + direction.Normalise(); + camera_pt += direction / 4.0f; + } + CVector forward = focus - camera_pt; + forward.Normalise(); + CVector right = CrossProduct(CVector(0.0f, 0.0f, 1.0f), forward); + right.Normalise(); + CVector up = CrossProduct(forward, right); + up.Normalise(); + TheCamera.GetForward() = forward; + TheCamera.GetUp() = up; + TheCamera.GetRight() = right; + TheCamera.SetPosition(camera_pt); + RwMatrix* pm = RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera)); + pm->pos = TheCamera.GetPosition(); + pm->at = TheCamera.GetForward(); + pm->up = TheCamera.GetUp(); + pm->right = TheCamera.GetRight(); + TheCamera.CalculateDerivedValues(); + RwMatrixUpdate(RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera))); + RwFrameUpdateObjects(RwCameraGetFrame(TheCamera.m_pRwCamera)); +} + +size_t CReplay::FindSizeOfPacket(uint8 type) +{ + switch (type) { + case REPLAYPACKET_END: return 4; + case REPLAYPACKET_VEHICLE: return sizeof(tVehicleUpdatePacket); + case REPLAYPACKET_BIKE: return sizeof(tBikeUpdatePacket); + case REPLAYPACKET_PED_HEADER: return sizeof(tPedHeaderPacket); + case REPLAYPACKET_PED_UPDATE: return sizeof(tPedUpdatePacket); + case REPLAYPACKET_GENERAL: return sizeof(tGeneralPacket); + case REPLAYPACKET_CLOCK: return sizeof(tClockPacket); + case REPLAYPACKET_WEATHER: return sizeof(tWeatherPacket); + case REPLAYPACKET_ENDOFFRAME: return 4; + case REPLAYPACKET_TIMER: return sizeof(tTimerPacket); + case REPLAYPACKET_BULLET_TRACES:return sizeof(tBulletTracePacket); + case REPLAYPACKET_PARTICLE: return sizeof(tParticlePacket); + case REPLAYPACKET_MISC: return sizeof(tMiscPacket); + default: assert(false); break; + } + return 0; +} + +void CReplay::Display() +{ + static int TimeCount = 0; + if (Mode == MODE_RECORD) + return; + TimeCount = (TimeCount + 1) % UINT16_MAX; + if ((TimeCount & 0x20) == 0) + return; + + CFont::SetScale(SCREEN_SCALE_X(1.5f), SCREEN_SCALE_Y(1.5f)); + CFont::SetJustifyOff(); + CFont::SetBackgroundOff(); +#ifdef FIX_BUGS + CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH-20)); +#else + CFont::SetCentreSize(SCREEN_WIDTH-20); +#endif + CFont::SetCentreOff(); + CFont::SetPropOn(); + CFont::SetColor(CRGBA(255, 255, 200, 200)); + CFont::SetFontStyle(FONT_STANDARD); + if (Mode == MODE_PLAYBACK) + CFont::PrintString(SCREEN_WIDTH/15, SCREEN_HEIGHT/10, TheText.Get("REPLAY")); +} +#endif diff --git a/src/miami/control/Replay.h b/src/miami/control/Replay.h new file mode 100644 index 00000000..5dd8b651 --- /dev/null +++ b/src/miami/control/Replay.h @@ -0,0 +1,417 @@ +#pragma once + +#include "Pools.h" +#include "World.h" +#include "WeaponEffects.h" +#include "ParticleType.h" + +#ifdef FIX_BUGS +#ifndef DONT_FIX_REPLAY_BUGS +#define FIX_REPLAY_BUGS +#endif +#endif + +class CVehicle; +struct CReference; + +struct CAddressInReplayBuffer +{ + uint32 m_nOffset; + uint8 *m_pBase; + uint8 m_bSlot; +}; + +struct CStoredAnimationState +{ + uint8 animId; + uint8 time; + uint8 speed; + uint8 groupId; + uint8 secAnimId; + uint8 secTime; + uint8 secSpeed; + uint8 secGroupId; + uint8 blendAmount; + uint8 partAnimId; + uint8 partAnimTime; + uint8 partAnimSpeed; + uint8 partBlendAmount; + uint8 partGroupId; +}; + +enum { + NUM_MAIN_ANIMS_IN_REPLAY = 3, + NUM_PARTIAL_ANIMS_IN_REPLAY = 6 +}; + +struct CStoredDetailedAnimationState +{ + uint8 aAnimId[NUM_MAIN_ANIMS_IN_REPLAY]; + uint8 aCurTime[NUM_MAIN_ANIMS_IN_REPLAY]; + uint8 aSpeed[NUM_MAIN_ANIMS_IN_REPLAY]; + uint8 aBlendAmount[NUM_MAIN_ANIMS_IN_REPLAY]; + int8 aBlendDelta[NUM_MAIN_ANIMS_IN_REPLAY]; + uint8 aFunctionCallbackID[NUM_MAIN_ANIMS_IN_REPLAY]; + uint16 aFlags[NUM_MAIN_ANIMS_IN_REPLAY]; + uint8 aGroupId[NUM_MAIN_ANIMS_IN_REPLAY]; + uint8 aAnimId2[NUM_PARTIAL_ANIMS_IN_REPLAY]; + uint8 aCurTime2[NUM_PARTIAL_ANIMS_IN_REPLAY]; + uint8 aSpeed2[NUM_PARTIAL_ANIMS_IN_REPLAY]; + uint8 aBlendAmount2[NUM_PARTIAL_ANIMS_IN_REPLAY]; + int8 aBlendDelta2[NUM_PARTIAL_ANIMS_IN_REPLAY]; + uint8 aFunctionCallbackID2[NUM_PARTIAL_ANIMS_IN_REPLAY]; + uint16 aFlags2[NUM_PARTIAL_ANIMS_IN_REPLAY]; + uint8 aGroupId2[NUM_PARTIAL_ANIMS_IN_REPLAY]; +}; + +#ifdef GTA_REPLAY +#define REPLAY_STUB +#else +#define REPLAY_STUB {} +#endif + +class CReplay +{ + enum { + MODE_RECORD = 0, + MODE_PLAYBACK = 1 + }; + + enum { + REPLAYCAMMODE_ASSTORED = 0, + REPLAYCAMMODE_TOPDOWN, + REPLAYCAMMODE_FIXED + }; + + enum { + REPLAYPACKET_END = 0, + REPLAYPACKET_VEHICLE, + REPLAYPACKET_BIKE, + REPLAYPACKET_PED_HEADER, + REPLAYPACKET_PED_UPDATE, + REPLAYPACKET_GENERAL, + REPLAYPACKET_CLOCK, + REPLAYPACKET_WEATHER, + REPLAYPACKET_ENDOFFRAME, + REPLAYPACKET_TIMER, + REPLAYPACKET_BULLET_TRACES, + REPLAYPACKET_PARTICLE, + REPLAYPACKET_MISC + }; + + enum { + REPLAYBUFFER_UNUSED = 0, + REPLAYBUFFER_PLAYBACK = 1, + REPLAYBUFFER_RECORD = 2 + }; + + enum { + NUM_REPLAYBUFFERS = 8, + REPLAYBUFFERSIZE = 100000 + }; + + + struct tGeneralPacket + { + uint8 type; + bool in_rcvehicle; + CMatrix camera_pos; + CVector player_pos; + }; + + VALIDATE_SIZE(tGeneralPacket, 88); + + struct tClockPacket + { + uint8 type; + uint8 hours; + uint8 minutes; + private: + uint8 __align; + }; + VALIDATE_SIZE(tClockPacket, 4); + + struct tWeatherPacket + { + uint8 type; + uint8 old_weather; + uint8 new_weather; + float interpolation; + }; + VALIDATE_SIZE(tWeatherPacket, 8); + + struct tTimerPacket + { + uint8 type; + uint32 timer; + }; + VALIDATE_SIZE(tTimerPacket, 8); + + struct tPedHeaderPacket + { + uint8 type; + uint8 index; + uint16 mi; + uint8 pedtype; + private: + uint8 __align[3]; + }; + VALIDATE_SIZE(tPedHeaderPacket, 8); + + struct tBulletTracePacket + { + uint8 type; + uint8 frames; + uint8 lifetime; + uint8 index; + CVector inf; + CVector sup; + }; + VALIDATE_SIZE(tBulletTracePacket, 28); + + struct tEndOfFramePacket + { + uint8 type; + private: + uint8 __align[3]; + }; + VALIDATE_SIZE(tEndOfFramePacket, 4); + + struct tPedUpdatePacket + { + uint8 type; + uint8 index; + int8 heading; + int8 vehicle_index; + CStoredAnimationState anim_state; + CCompressedMatrixNotAligned matrix; + uint16 weapon_model; + int8 assoc_group_id; + bool is_visible; + }; + VALIDATE_SIZE(tPedUpdatePacket, 40); + + struct tVehicleUpdatePacket + { + uint8 type; + uint8 index; + uint8 health; + uint8 acceleration; + CCompressedMatrixNotAligned matrix; + int8 door_angles[2]; + uint16 mi; + uint32 panels; + int8 velocityX; + int8 velocityY; + int8 velocityZ; + union { + int8 car_gun; + int8 wheel_state; + }; + uint8 wheel_susp_dist[4]; + uint8 wheel_rotation[4]; + uint8 door_status; + uint8 primary_color; + uint8 secondary_color; + bool render_scorched; + int8 skimmer_speed; + int8 vehicle_type; + + }; + VALIDATE_SIZE(tVehicleUpdatePacket, 52); + + struct tBikeUpdatePacket + { + uint8 type; + uint8 index; + uint8 health; + uint8 acceleration; + CCompressedMatrixNotAligned matrix; + int8 door_angles[2]; + uint16 mi; + int8 velocityX; + int8 velocityY; + int8 velocityZ; + int8 wheel_state; + uint8 wheel_susp_dist[4]; + uint8 wheel_rotation[4]; + uint8 primary_color; + uint8 secondary_color; + int8 lean_angle; + int8 wheel_angle; + + }; + VALIDATE_SIZE(tBikeUpdatePacket, 44); + + struct tParticlePacket + { + uint8 type; + uint8 particle_type; + int8 dir_x; + int8 dir_y; + int8 dir_z; + uint8 r; + uint8 g; + uint8 b; + uint8 a; + int16 pos_x; + int16 pos_y; + int16 pos_z; + float size; + }; + VALIDATE_SIZE(tParticlePacket, 20); + + struct tMiscPacket + { + uint8 type; + uint32 cam_shake_start; + float cam_shake_strength; + uint8 cur_area; + uint8 video_cam : 1; + uint8 lift_cam : 1; + }; + + VALIDATE_SIZE(tMiscPacket, 16); + +private: + static uint8 Mode; + static CAddressInReplayBuffer Record; + static CAddressInReplayBuffer Playback; + static uint8* pBuf0; + static CAutomobile* pBuf1; + static uint8* pBuf2; + static CPlayerPed* pBuf3; + static uint8* pBuf4; + static CCutsceneObject* pBuf5; + static uint8* pBuf6; + static CPtrNode* pBuf7; + static uint8* pBuf8; + static CEntryInfoNode* pBuf9; + static uint8* pBuf10; + static CDummyPed* pBuf11; + static uint8* pRadarBlips; + static uint8* pStoredCam; + static uint8* pWorld1; + static CReference* pEmptyReferences; + static CStoredDetailedAnimationState* pPedAnims; + static uint8* pPickups; + static uint8* pReferences; + static uint8 BufferStatus[NUM_REPLAYBUFFERS]; + static uint8 Buffers[NUM_REPLAYBUFFERS][REPLAYBUFFERSIZE]; + static bool bPlayingBackFromFile; + static bool bReplayEnabled; + static uint32 SlowMotion; + static uint32 FramesActiveLookAroundCam; + static bool bDoLoadSceneWhenDone; + static CPtrNode* WorldPtrList; + static CPtrNode* BigBuildingPtrList; + static CWanted PlayerWanted; + static CPlayerInfo PlayerInfo; + static uint32 Time1; + static uint32 Time2; + static uint32 Time3; + static uint32 Time4; + static uint32 Frame; + static uint8 ClockHours; + static uint8 ClockMinutes; + static uint16 OldWeatherType; + static uint16 NewWeatherType; + static float WeatherInterpolationValue; + static float TimeStepNonClipped; + static float TimeStep; + static float TimeScale; + static float CameraFixedX; + static float CameraFixedY; + static float CameraFixedZ; + static int32 OldRadioStation; + static int8 CameraMode; + static bool bAllowLookAroundCam; + static float LoadSceneX; + static float LoadSceneY; + static float LoadSceneZ; + static float CameraFocusX; + static float CameraFocusY; + static float CameraFocusZ; + static bool bPlayerInRCBuggy; + static float fDistanceLookAroundCam; + static float fAlphaAngleLookAroundCam; + static float fBetaAngleLookAroundCam; + static int ms_nNumCivMale_Stored; + static int ms_nNumCivFemale_Stored; + static int ms_nNumCop_Stored; + static int ms_nNumEmergency_Stored; + static int ms_nNumGang1_Stored; + static int ms_nNumGang2_Stored; + static int ms_nNumGang3_Stored; + static int ms_nNumGang4_Stored; + static int ms_nNumGang5_Stored; + static int ms_nNumGang6_Stored; + static int ms_nNumGang7_Stored; + static int ms_nNumGang8_Stored; + static int ms_nNumGang9_Stored; + static int ms_nNumDummy_Stored; + static int ms_nTotalCarPassengerPeds_Stored; + static int ms_nTotalCivPeds_Stored; + static int ms_nTotalGangPeds_Stored; + static int ms_nTotalPeds_Stored; + static int ms_nTotalMissionPeds_Stored; + static uint8* pGarages; + static CFire* FireArray; + static uint32 NumOfFires; + static uint8* paProjectileInfo; + static uint8* paProjectiles; + static uint8 CurrArea; +#ifdef FIX_BUGS + static int nHandleOfPlayerPed[NUMPLAYERS]; +#endif + +public: + static void Init(void) REPLAY_STUB; + static void DisableReplays(void) REPLAY_STUB; + static void EnableReplays(void) REPLAY_STUB; + static void Update(void) REPLAY_STUB; + static void FinishPlayback(void) REPLAY_STUB; + static void EmptyReplayBuffer(void) REPLAY_STUB; + static void Display(void) REPLAY_STUB; + static void TriggerPlayback(uint8 cam_mode, float cam_x, float cam_y, float cam_z, bool load_scene) REPLAY_STUB; + static void StreamAllNecessaryCarsAndPeds(void) REPLAY_STUB; + static void RecordParticle(tParticleType type, CVector const& vecPos, CVector const& vecDir, float fSize, RwRGBA const& color) REPLAY_STUB; + +#ifndef GTA_REPLAY + static bool ShouldStandardCameraBeProcessed(void) { return true; } + static bool IsPlayingBack() { return false; } + static bool IsPlayingBackFromFile() { return false; } +#else + static bool ShouldStandardCameraBeProcessed(void); + static bool IsPlayingBack() { return Mode == MODE_PLAYBACK; } + static bool IsPlayingBackFromFile() { return bPlayingBackFromFile; } +private: + static void RecordThisFrame(void); + static void StorePedUpdate(CPed *ped, int id); + static void StorePedAnimation(CPed *ped, CStoredAnimationState *state); + static void StoreDetailedPedAnimation(CPed *ped, CStoredDetailedAnimationState *state); + static void ProcessPedUpdate(CPed *ped, float interpolation, CAddressInReplayBuffer *buffer); + static void RetrievePedAnimation(CPed *ped, CStoredAnimationState *state); + static void RetrieveDetailedPedAnimation(CPed *ped, CStoredDetailedAnimationState *state); + static void PlaybackThisFrame(void); + static void TriggerPlaybackLastCoupleOfSeconds(uint32, uint8, float, float, float, uint32); + static bool FastForwardToTime(uint32); + static void StoreCarUpdate(CVehicle *vehicle, int id); + static void StoreBikeUpdate(CVehicle* vehicle, int id); + static void ProcessCarUpdate(CVehicle *vehicle, float interpolation, CAddressInReplayBuffer *buffer); + static void ProcessBikeUpdate(CVehicle* vehicle, float interpolation, CAddressInReplayBuffer* buffer); + static bool PlayBackThisFrameInterpolation(CAddressInReplayBuffer *buffer, float interpolation, uint32 *pTimer); + static void ProcessReplayCamera(void); + static void StoreStuffInMem(void); + static void RestoreStuffFromMem(void); + static void EmptyPedsAndVehiclePools(void); + static void EmptyAllPools(void); + static void MarkEverythingAsNew(void); + static void SaveReplayToHD(void); + static void PlayReplayFromHD(void); // out of class in III PC and later because of SecuROM + static void FindFirstFocusCoordinate(CVector *coord); + static void ProcessLookAroundCam(void); + static size_t FindSizeOfPacket(uint8); + static void GoToNextBlock(void); +#endif +}; diff --git a/src/miami/control/Restart.cpp b/src/miami/control/Restart.cpp new file mode 100644 index 00000000..af38537d --- /dev/null +++ b/src/miami/control/Restart.cpp @@ -0,0 +1,250 @@ +#include "common.h" + +#include "Restart.h" +#include "SaveBuf.h" +#include "Zones.h" +#include "PathFind.h" +#include "SaveBuf.h" + +uint8 CRestart::OverrideHospitalLevel; +uint8 CRestart::OverridePoliceStationLevel; +bool CRestart::bFadeInAfterNextArrest; +bool CRestart::bFadeInAfterNextDeath; + +bool CRestart::bOverrideRestart; +CVector CRestart::OverridePosition; +float CRestart::OverrideHeading; + +CVector CRestart::HospitalRestartPoints[NUM_RESTART_POINTS]; +float CRestart::HospitalRestartHeadings[NUM_RESTART_POINTS]; +uint16 CRestart::NumberOfHospitalRestarts; + +CVector CRestart::PoliceRestartPoints[NUM_RESTART_POINTS]; +float CRestart::PoliceRestartHeadings[NUM_RESTART_POINTS]; +uint16 CRestart::NumberOfPoliceRestarts; + +void +CRestart::Initialise() +{ + OverridePoliceStationLevel = LEVEL_GENERIC; + OverrideHospitalLevel = LEVEL_GENERIC; + bFadeInAfterNextArrest = true; + bFadeInAfterNextDeath = true; + OverrideHeading = 0.0f; + OverridePosition = CVector(0.0f, 0.0f, 0.0f); + bOverrideRestart = false; + NumberOfPoliceRestarts = 0; + NumberOfHospitalRestarts = 0; + + for (int i = 0; i < NUM_RESTART_POINTS; i++) { + HospitalRestartPoints[i] = CVector(0.0f, 0.0f, 0.0f); + HospitalRestartHeadings[i] = 0.0f; + PoliceRestartPoints[i] = CVector(0.0f, 0.0f, 0.0f); + PoliceRestartHeadings[i] = 0.0f; + } +} + +void +CRestart::AddHospitalRestartPoint(const CVector &pos, float heading) +{ + HospitalRestartPoints[NumberOfHospitalRestarts] = pos; + HospitalRestartHeadings[NumberOfHospitalRestarts++] = heading; +} + +void +CRestart::AddPoliceRestartPoint(const CVector &pos, float heading) +{ + PoliceRestartPoints[NumberOfPoliceRestarts] = pos; + PoliceRestartHeadings[NumberOfPoliceRestarts++] = heading; +} + +void +CRestart::OverrideNextRestart(const CVector &pos, float heading) +{ + bOverrideRestart = true; + OverridePosition = pos; + OverrideHeading = heading; +} + +void +CRestart::CancelOverrideRestart() +{ + bOverrideRestart = false; +} + +void +CRestart::FindClosestHospitalRestartPoint(const CVector &pos, CVector *outPos, float *outHeading) +{ + if (bOverrideRestart) { + *outPos = OverridePosition; + *outHeading = OverrideHeading; + CancelOverrideRestart(); + return; + } + + eLevelName curlevel = CTheZones::GetLevelFromPosition(&pos); + float fMinDist = SQR(4000.0f); + int closestPoint = NUM_RESTART_POINTS; + + // find closest point on this level + for (int i = 0; i < NumberOfHospitalRestarts; i++) { + if (CTheZones::GetLevelFromPosition(&HospitalRestartPoints[i]) == (OverrideHospitalLevel != LEVEL_GENERIC ? OverrideHospitalLevel : curlevel)) { + float dist = (pos - HospitalRestartPoints[i]).MagnitudeSqr(); + if (fMinDist >= dist) { + fMinDist = dist; + closestPoint = i; + } + } + } + + // if we didn't find anything, find closest point on any level + if (closestPoint == NUM_RESTART_POINTS) { + for (int i = 0; i < NumberOfHospitalRestarts; i++) { + float dist = (pos - HospitalRestartPoints[i]).MagnitudeSqr(); + if (fMinDist >= dist) { + fMinDist = dist; + closestPoint = i; + } + } + } + + // if we still didn't find anything, find closest path node + if (closestPoint == NUM_RESTART_POINTS) { + *outPos = ThePaths.m_pathNodes[ThePaths.FindNodeClosestToCoors(pos, PATH_PED, 999999.9f)].GetPosition(); + *outHeading = 0.0f; + printf("Couldn't find a hospital restart zone near the player %f %f %f->%f %f %f\n", pos.x, pos.y, pos.z, outPos->x, outPos->y, outPos->z); + } else { + *outPos = HospitalRestartPoints[closestPoint]; + *outHeading = HospitalRestartHeadings[closestPoint]; + } +} + +void +CRestart::FindClosestPoliceRestartPoint(const CVector &pos, CVector *outPos, float *outHeading) +{ + if (bOverrideRestart) { + *outPos = OverridePosition; + *outHeading = OverrideHeading; + CancelOverrideRestart(); + return; + } + + eLevelName curlevel = CTheZones::GetLevelFromPosition(&pos); + float fMinDist = SQR(4000.0f); + int closestPoint = NUM_RESTART_POINTS; + + // find closest point on this level + for (int i = 0; i < NumberOfPoliceRestarts; i++) { + if (CTheZones::GetLevelFromPosition(&PoliceRestartPoints[i]) == (OverridePoliceStationLevel != LEVEL_GENERIC ? OverridePoliceStationLevel : curlevel)) { + float dist = (pos - PoliceRestartPoints[i]).MagnitudeSqr(); + if (fMinDist >= dist) { + fMinDist = dist; + closestPoint = i; + } + } + } + + // if we didn't find anything, find closest point on any level + if (closestPoint == NUM_RESTART_POINTS) { + for (int i = 0; i < NumberOfPoliceRestarts; i++) { + float dist = (pos - PoliceRestartPoints[i]).MagnitudeSqr(); + if (fMinDist >= dist) { + fMinDist = dist; + closestPoint = i; + } + } + } + + // if we still didn't find anything, find closest path node + if (closestPoint == NUM_RESTART_POINTS) { + printf("Couldn't find a police restart zone near the player\n"); + *outPos = ThePaths.m_pathNodes[ThePaths.FindNodeClosestToCoors(pos, PATH_PED, 999999.9f)].GetPosition(); + *outHeading = 0.0f; + } else { + *outPos = PoliceRestartPoints[closestPoint]; + *outHeading = PoliceRestartHeadings[closestPoint]; + } +} + +void +CRestart::LoadAllRestartPoints(uint8 *buf, uint32 size) +{ + Initialise(); + +INITSAVEBUF + CheckSaveHeader(buf, 'R','S','T','\0', size - SAVE_HEADER_SIZE); + + for (int i = 0; i < NUM_RESTART_POINTS; i++) { + ReadSaveBuf(&HospitalRestartPoints[i], buf); + ReadSaveBuf(&HospitalRestartHeadings[i], buf); + } + + for (int i = 0; i < NUM_RESTART_POINTS; i++) { + ReadSaveBuf(&PoliceRestartPoints[i], buf); + ReadSaveBuf(&PoliceRestartHeadings[i], buf); + } + + ReadSaveBuf(&NumberOfHospitalRestarts, buf); + ReadSaveBuf(&NumberOfPoliceRestarts, buf); + ReadSaveBuf(&bOverrideRestart, buf); + + // skip something unused + SkipSaveBuf(buf, 3); + + ReadSaveBuf(&OverridePosition, buf); + ReadSaveBuf(&OverrideHeading, buf); + ReadSaveBuf(&bFadeInAfterNextDeath, buf); + ReadSaveBuf(&bFadeInAfterNextArrest, buf); + ReadSaveBuf(&OverrideHospitalLevel, buf); + ReadSaveBuf(&OverridePoliceStationLevel, buf); +VALIDATESAVEBUF(size); +} + +void +CRestart::SaveAllRestartPoints(uint8 *buf, uint32 *size) +{ + *size = SAVE_HEADER_SIZE + + sizeof(HospitalRestartPoints) + + sizeof(HospitalRestartHeadings) + + sizeof(PoliceRestartPoints) + + sizeof(PoliceRestartHeadings) + + sizeof(NumberOfHospitalRestarts) + + sizeof(NumberOfPoliceRestarts) + + sizeof(bOverrideRestart) + + sizeof(uint8) + + sizeof(uint16) + + sizeof(OverridePosition) + + sizeof(OverrideHeading) + + sizeof(bFadeInAfterNextDeath) + + sizeof(bFadeInAfterNextArrest) + + sizeof(OverrideHospitalLevel) + + sizeof(OverridePoliceStationLevel); // == 292 + +INITSAVEBUF + WriteSaveHeader(buf, 'R','S','T','\0', *size - SAVE_HEADER_SIZE); + + for (int i = 0; i < NUM_RESTART_POINTS; i++) { + WriteSaveBuf(buf, HospitalRestartPoints[i]); + WriteSaveBuf(buf, HospitalRestartHeadings[i]); + } + + for (int i = 0; i < NUM_RESTART_POINTS; i++) { + WriteSaveBuf(buf, PoliceRestartPoints[i]); + WriteSaveBuf(buf, PoliceRestartHeadings[i]); + } + + WriteSaveBuf(buf, NumberOfHospitalRestarts); + WriteSaveBuf(buf, NumberOfPoliceRestarts); + WriteSaveBuf(buf, bOverrideRestart); + + WriteSaveBuf(buf, (uint8)0); + WriteSaveBuf(buf, (uint16)0); + + WriteSaveBuf(buf, OverridePosition); + WriteSaveBuf(buf, OverrideHeading); + WriteSaveBuf(buf, bFadeInAfterNextDeath); + WriteSaveBuf(buf, bFadeInAfterNextArrest); + WriteSaveBuf(buf, OverrideHospitalLevel); + WriteSaveBuf(buf, OverridePoliceStationLevel); +VALIDATESAVEBUF(*size); +} diff --git a/src/miami/control/Restart.h b/src/miami/control/Restart.h new file mode 100644 index 00000000..5d84c723 --- /dev/null +++ b/src/miami/control/Restart.h @@ -0,0 +1,36 @@ +#pragma once + +#define NUM_RESTART_POINTS 8 + +class CRestart +{ +public: + static void AddPoliceRestartPoint(const CVector&, float); + static void AddHospitalRestartPoint(const CVector&, float); + static void OverrideNextRestart(const CVector&, float); + + static void FindClosestHospitalRestartPoint(const CVector &, CVector *, float *); + static void FindClosestPoliceRestartPoint(const CVector &, CVector *, float *); + static void Initialise(); + static void CancelOverrideRestart(); + + static void LoadAllRestartPoints(uint8 *buf, uint32 size); + static void SaveAllRestartPoints(uint8 *buf, uint32 *size); + + static uint8 OverrideHospitalLevel; + static uint8 OverridePoliceStationLevel; + static bool bFadeInAfterNextArrest; + static bool bFadeInAfterNextDeath; + + static bool bOverrideRestart; + static CVector OverridePosition; + static float OverrideHeading; + + static CVector HospitalRestartPoints[NUM_RESTART_POINTS]; + static float HospitalRestartHeadings[NUM_RESTART_POINTS]; + static uint16 NumberOfHospitalRestarts; + + static CVector PoliceRestartPoints[NUM_RESTART_POINTS]; + static float PoliceRestartHeadings[NUM_RESTART_POINTS]; + static uint16 NumberOfPoliceRestarts; +}; diff --git a/src/miami/control/RoadBlocks.cpp b/src/miami/control/RoadBlocks.cpp new file mode 100644 index 00000000..e30313bf --- /dev/null +++ b/src/miami/control/RoadBlocks.cpp @@ -0,0 +1,298 @@ +#include "common.h" + +#include "RoadBlocks.h" +#include "PathFind.h" +#include "ModelIndices.h" +#include "Streaming.h" +#include "World.h" +#include "PedPlacement.h" +#include "Automobile.h" +#include "CopPed.h" +#include "VisibilityPlugins.h" +#include "PlayerPed.h" +#include "Wanted.h" +#include "Camera.h" +#include "CarCtrl.h" +#include "General.h" +#include "Object.h" + +#define ROADBLOCKDIST (90.0f) +#define ROADBLOCK_OBJECT_WIDTH (4.0f) + +int16 CRoadBlocks::NumRoadBlocks; +int16 CRoadBlocks::RoadBlockNodes[NUMROADBLOCKS]; +bool CRoadBlocks::InOrOut[NUMROADBLOCKS]; +CScriptRoadblock CRoadBlocks::aScriptRoadBlocks[NUM_SCRIPT_ROADBLOCKS]; + +#ifdef SECUROM +uint8 roadBlocksPirateCheck = 0; +#endif + +void +CRoadBlocks::Init(void) +{ + int i; + NumRoadBlocks = 0; + for(i = 0; i < ThePaths.m_numCarPathNodes; i++){ + if(ThePaths.m_pathNodes[i].bUseInRoadBlock && ThePaths.m_pathNodes[i].numLinks == 2){ + if (NumRoadBlocks < NUMROADBLOCKS) { + InOrOut[NumRoadBlocks] = true; + RoadBlockNodes[NumRoadBlocks] = i; + NumRoadBlocks++; + } else { +#ifndef MASTER + printf("Not enough room for the potential roadblocks\n"); +#endif + // FIX: Don't iterate loop after NUMROADBLOCKS + return; + } + } + } + + ClearScriptRoadBlocks(); +} + +void +CRoadBlocks::GenerateRoadBlockCopsForCar(CVehicle* pVehicle, int32 roadBlockType) +{ + static const CVector vecRoadBlockOffets[6] = { CVector(-1.5, 1.8f, 0.0f), CVector(-1.5f, -1.8f, 0.0f), CVector(1.5f, 1.8f, 0.0f), + CVector(1.5f, -1.8f, 0.0f), CVector(-1.5f, 0.0f, 0.0f), CVector(1.5, 0.0, 0.0) }; + CEntity* pEntityToAttack = (CEntity*)FindPlayerVehicle(); + if (!pEntityToAttack) + pEntityToAttack = (CEntity*)FindPlayerPed(); + CColModel* pPoliceColModel = CModelInfo::GetColModel(MI_POLICE); + float fRadius = pVehicle->GetBoundRadius() / pPoliceColModel->boundingSphere.radius; + for (int32 i = 0; i < 2; i++) { + const int32 roadBlockIndex = i + 2 * roadBlockType; + CVector posForZ = pVehicle->GetMatrix() * (fRadius * vecRoadBlockOffets[roadBlockIndex]); + int32 modelInfoId = MI_COP; + eCopType copType = COP_STREET; + switch (pVehicle->GetModelIndex()) + { + case MI_FBIRANCH: + modelInfoId = MI_FBI; + copType = COP_FBI; + break; + case MI_ENFORCER: + modelInfoId = MI_SWAT; + copType = COP_SWAT; + break; + case MI_BARRACKS: + modelInfoId = MI_ARMY; + copType = COP_ARMY; + break; + } + if (!CStreaming::HasModelLoaded(modelInfoId)) + copType = COP_STREET; + CCopPed* pCopPed = new CCopPed(copType); + if (copType == COP_STREET) + pCopPed->SetCurrentWeapon(WEAPONTYPE_COLT45); + CPedPlacement::FindZCoorForPed(&posForZ); + pCopPed->SetPosition(posForZ); + pCopPed->SetOrientation(0.0f, 0.0f, -HALFPI); + pCopPed->m_bIsDisabledCop = true; + pCopPed->SetIdle(); + pCopPed->bKindaStayInSamePlace = true; + pCopPed->bNotAllowedToDuck = false; + pCopPed->m_nExtendedRangeTimer = CTimer::GetTimeInMilliseconds() + 10000; + pCopPed->m_nRoadblockVeh = pVehicle; + pCopPed->m_nRoadblockVeh->RegisterReference((CEntity**)&pCopPed->m_nRoadblockVeh); + pCopPed->bCrouchWhenShooting = roadBlockType == 2 ? false : true; + if (pEntityToAttack) { + pCopPed->SetWeaponLockOnTarget(pEntityToAttack); + pCopPed->SetAttack(pEntityToAttack); + } + pCopPed->m_pMyVehicle = pVehicle; + pVehicle->RegisterReference((CEntity**)&pCopPed->m_pMyVehicle); + pCopPed->bCullExtraFarAway = true; + CVisibilityPlugins::SetClumpAlpha(pCopPed->GetClump(), 0); + CWorld::Add(pCopPed); + } +} + +void +CRoadBlocks::GenerateRoadBlocks(void) +{ + CMatrix tmp1, tmp2; + static int16 unk; +#ifdef SQUEEZE_PERFORMANCE + if (FindPlayerPed()->m_pWanted->m_RoadblockDensity == 0) + return; +#endif + uint32 frame = CTimer::GetFrameCounter() & 0xF; + int16 nRoadblockNode = (int16)(NUMROADBLOCKS * frame) / 16; + const int16 maxRoadBlocks = (int16)(NUMROADBLOCKS * (frame + 1)) / 16; + for (; nRoadblockNode < Min(NumRoadBlocks, maxRoadBlocks); nRoadblockNode++) { + int16 node = RoadBlockNodes[nRoadblockNode]; + CVector2D vecDistance = FindPlayerCoors() - ThePaths.m_pathNodes[node].GetPosition(); + if (vecDistance.x > -ROADBLOCKDIST && vecDistance.x < ROADBLOCKDIST && + vecDistance.y > -ROADBLOCKDIST && vecDistance.y < ROADBLOCKDIST && + vecDistance.Magnitude() < ROADBLOCKDIST) { + if (!InOrOut[nRoadblockNode]) { + InOrOut[nRoadblockNode] = true; + if (FindPlayerVehicle() && (CGeneral::GetRandomNumber() & 0x7F) < FindPlayerPed()->m_pWanted->m_RoadblockDensity) { + CCarPathLink* pLink1 = &ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[ThePaths.m_pathNodes[node].firstLink]]; + CCarPathLink* pLink2 = &ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[ThePaths.m_pathNodes[node].firstLink + 1]]; + int lanes = Min(pLink1->numRightLanes + pLink1->numLeftLanes, pLink2->numLeftLanes + pLink2->numRightLanes); + float length = LANE_WIDTH * (lanes + 1); + CVector forward(pLink2->GetY() - pLink1->GetY(), -(pLink2->GetX() - pLink1->GetX()), 0.0f); + forward.Normalise(); + if (ThePaths.m_pathNodes[node].HasDivider()) { + CreateRoadBlockBetween2Points( + ThePaths.m_pathNodes[node].GetPosition() + (length * 0.5f + ThePaths.m_pathNodes[node].GetDividerWidth()) * forward, + ThePaths.m_pathNodes[node].GetPosition() + ThePaths.m_pathNodes[node].GetDividerWidth() * forward); + CreateRoadBlockBetween2Points( + ThePaths.m_pathNodes[node].GetPosition() - ThePaths.m_pathNodes[node].GetDividerWidth() * forward, + ThePaths.m_pathNodes[node].GetPosition() - (length * 0.5f + ThePaths.m_pathNodes[node].GetDividerWidth()) * forward); + } + else { + CreateRoadBlockBetween2Points( + ThePaths.m_pathNodes[node].GetPosition() + (length * 0.5f) * forward, + ThePaths.m_pathNodes[node].GetPosition() - (length * 0.5f) * forward); + } + } + } + } + else { + InOrOut[nRoadblockNode] = false; + } + } + int i = CTimer::GetFrameCounter() & 0xF; + if (!aScriptRoadBlocks[i].m_bInUse) + return; + if ((aScriptRoadBlocks[i].GetPosition() - FindPlayerCoors()).Magnitude() < 100.0f) { + CreateRoadBlockBetween2Points(aScriptRoadBlocks[i].m_vInf, aScriptRoadBlocks[i].m_vSup); + aScriptRoadBlocks[i].m_bInUse = false; + } +} + +void +CRoadBlocks::ClearScriptRoadBlocks(void) +{ + for (int i = 0; i < NUM_SCRIPT_ROADBLOCKS; i++) + aScriptRoadBlocks[i].m_bInUse = false; +} + +void +CRoadBlocks::RegisterScriptRoadBlock(CVector vInf, CVector vSup) +{ + int32 i; + for (i = 0; i < NUM_SCRIPT_ROADBLOCKS; i++) { + if (!aScriptRoadBlocks[i].m_bInUse) + break; + } + if (i == NUM_SCRIPT_ROADBLOCKS) + return; + aScriptRoadBlocks[i].m_bInUse = true; + aScriptRoadBlocks[i].m_vInf = vInf; + aScriptRoadBlocks[i].m_vSup = vSup; +} + +void +CRoadBlocks::CreateRoadBlockBetween2Points(CVector point1, CVector point2) +{ +#ifdef SECUROM + if (roadBlocksPirateCheck == 0) + // if not pirated game + // roadBlocksPirateCheck = 1; + // else + roadBlocksPirateCheck = 2; +#endif + CMatrix tmp; + CVector forward = (point2 - point1); + float distBetween = forward.Magnitude(); + CVector pos = (point1 + point2) / 2; + CVector right(forward.y, -forward.x, 0.0f); + forward.Normalise(); + right.Normalise(); + if (DotProduct(FindPlayerCoors() - pos, right) < 0.0f) { + right *= -1.0f; + } + int32 vehicleId = MI_POLICE; + if (FindPlayerPed()->m_pWanted->AreArmyRequired()) + vehicleId = MI_BARRACKS; + else if (FindPlayerPed()->m_pWanted->AreFbiRequired()) + vehicleId = MI_FBICAR; + else if (FindPlayerPed()->m_pWanted->AreSwatRequired()) + vehicleId = MI_ENFORCER; + if (!CStreaming::HasModelLoaded(vehicleId)) + vehicleId = MI_POLICE; + CColModel *pVehicleColModel = CModelInfo::GetColModel(vehicleId); + float fModelRadius = 2.0f * pVehicleColModel->boundingSphere.radius + 0.25f; + int16 numRoadblockVehicles = Min(6, (int16)(distBetween / fModelRadius)); + for (int16 i = 0; i < numRoadblockVehicles; i++) { + float offset = fModelRadius * (i - numRoadblockVehicles / 2); + tmp.SetTranslate(0.0f, 0.0f, 0.0f); + tmp.GetRight() = CVector(forward.y, -forward.x, 0.0f); + tmp.GetForward() = forward; + tmp.GetUp() = CVector(0.0f, 0.0f, 1.0f); + tmp.RotateZ(((CGeneral::GetRandomNumber() & 0xFF) - 128.0f) * 0.003f); + if (CGeneral::GetRandomNumber() & 1) + tmp.RotateZ(((CGeneral::GetRandomNumber() & 0xFF) - 128.0f) * 0.003f + 3.1416f); + tmp.SetTranslateOnly(offset * forward + pos); + tmp.GetPosition().z += 0.6f; + float fModelRadius = CModelInfo::GetColModel(vehicleId)->boundingSphere.radius - 0.25f; + int16 colliding = 0; + CWorld::FindObjectsKindaColliding(tmp.GetPosition(), fModelRadius, 0, &colliding, 2, nil, false, true, true, false, false); + if (!colliding) { + CAutomobile* pVehicle = new CAutomobile(vehicleId, RANDOM_VEHICLE); + pVehicle->SetStatus(STATUS_ABANDONED); + // pVehicle->GetHeightAboveRoad(); // called but return value is ignored? + tmp.GetPosition().z += fModelRadius - 0.6f; + pVehicle->SetMatrix(tmp); + pVehicle->PlaceOnRoadProperly(); + pVehicle->SetIsStatic(false); + pVehicle->GetMatrix().UpdateRW(); + pVehicle->m_nDoorLock = CARLOCK_UNLOCKED; + CCarCtrl::JoinCarWithRoadSystem(pVehicle); + pVehicle->bIsLocked = false; + pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; + pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; + pVehicle->AutoPilot.m_nNextLane = pVehicle->AutoPilot.m_nCurrentLane = 0; + pVehicle->AutoPilot.m_nCruiseSpeed = pVehicle->AutoPilot.m_fMaxTrafficSpeed = 0; + pVehicle->bExtendedRange = true; + if (pVehicle->UsesSiren() && CGeneral::GetRandomNumber() & 1) + pVehicle->m_bSirenOrAlarm = true; + if (pVehicle->GetUp().z > 0.94f) { + CVisibilityPlugins::SetClumpAlpha(pVehicle->GetClump(), 0); + CWorld::Add(pVehicle); + pVehicle->bCreateRoadBlockPeds = true; + pVehicle->m_nRoadblockType = DotProduct(pVehicle->GetRight(), pVehicle->GetPosition() - FindPlayerCoors()) >= 0.0f; + pVehicle->m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 7000; + } + else { + delete pVehicle; + } + } + } + int numBarriers = distBetween / ROADBLOCK_OBJECT_WIDTH; + CStreaming::RequestModel(MI_ROADWORKBARRIER1, STREAMFLAGS_DONT_REMOVE); + if (!CStreaming::HasModelLoaded(MI_ROADWORKBARRIER1)) + return; + for (int i = 0; i < numBarriers; i++) { + float offset = ROADBLOCK_OBJECT_WIDTH * (i - numBarriers / 2); + tmp.SetTranslate(0.0f, 0.0f, 0.0f); + tmp.GetRight() = CVector(forward.y, -forward.x, 0.0f); + tmp.GetForward() = forward; + tmp.GetUp() = CVector(0.0f, 0.0f, 1.0f); + tmp.RotateZ(((CGeneral::GetRandomNumber() & 0xFF) - 128.0f) * 0.003f); + tmp.SetTranslateOnly(5.0f * right + offset * forward + pos); + tmp.GetPosition().x += (CGeneral::GetRandomNumber() & 0xF) * 0.1f; + tmp.GetPosition().y += (CGeneral::GetRandomNumber() & 0xF) * 0.1f; + bool found; + tmp.GetPosition().z = CWorld::FindGroundZFor3DCoord(tmp.GetPosition().x, tmp.GetPosition().y, tmp.GetPosition().z + 2.0f, &found); + if (!found) + continue; + int16 colliding = 0; + CBaseModelInfo* pMI = CModelInfo::GetModelInfo(MI_ROADWORKBARRIER1); + tmp.GetPosition().z -= pMI->GetColModel()->boundingBox.min.z; + CWorld::FindObjectsKindaColliding(tmp.GetPosition(), pMI->GetColModel()->boundingSphere.radius, 0, &colliding, 2, nil, false, true, true, false, false); + if (colliding == 0) { + CObject* pObject = new CObject(MI_ROADWORKBARRIER1, true); + pObject->GetMatrix() = tmp; + pObject->ObjectCreatedBy = TEMP_OBJECT; + pObject->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 600000; + CWorld::Add(pObject); + } + } +} diff --git a/src/miami/control/RoadBlocks.h b/src/miami/control/RoadBlocks.h new file mode 100644 index 00000000..ef614950 --- /dev/null +++ b/src/miami/control/RoadBlocks.h @@ -0,0 +1,30 @@ +#pragma once +#include "common.h" + +class CVehicle; + +class CScriptRoadblock +{ +public: + CVector m_vInf; + CVector m_vSup; + bool m_bInUse; + CVector GetPosition() { return (m_vInf + m_vSup) / 2; } +}; + +class CRoadBlocks +{ +public: + static int16 NumRoadBlocks; + static int16 RoadBlockNodes[NUMROADBLOCKS]; + static bool InOrOut[NUMROADBLOCKS]; + static CScriptRoadblock aScriptRoadBlocks[NUM_SCRIPT_ROADBLOCKS]; + + static void Init(void); + static void GenerateRoadBlockCopsForCar(CVehicle* pVehicle, int32 roadBlockType); + static void GenerateRoadBlocks(void); + + static void CreateRoadBlockBetween2Points(CVector, CVector); + static void RegisterScriptRoadBlock(CVector, CVector); + static void ClearScriptRoadBlocks(); +}; diff --git a/src/miami/control/SceneEdit.cpp b/src/miami/control/SceneEdit.cpp new file mode 100644 index 00000000..74d81327 --- /dev/null +++ b/src/miami/control/SceneEdit.cpp @@ -0,0 +1,1127 @@ +#include "common.h" + +#include "SceneEdit.h" +#ifdef GTA_SCENE_EDIT +#include "Automobile.h" +#include "Camera.h" +#include "CarCtrl.h" +#include "CivilianPed.h" +#include "FileMgr.h" +#include "Font.h" +#include "ModelIndices.h" +#include "ModelInfo.h" +#include "Pad.h" +#include "Ped.h" +#include "Population.h" +#include "Text.h" +#include "Timecycle.h" +#include "Streaming.h" +#include "Vehicle.h" +#include "WeaponInfo.h" +#include "World.h" + +bool CSceneEdit::m_bEditOn; +int32 CSceneEdit::m_bCameraFollowActor; +bool CSceneEdit::m_bRecording; +CVector CSceneEdit::m_vecCurrentPosition; +CVector CSceneEdit::m_vecCamHeading; +CVector CSceneEdit::m_vecGotoPosition; +int32 CSceneEdit::m_nVehicle; +int32 CSceneEdit::m_nVehicle2; +int32 CSceneEdit::m_nActor; +int32 CSceneEdit::m_nActor2; +int32 CSceneEdit::m_nVehiclemodelId; +int32 CSceneEdit::m_nPedmodelId; +int16 CSceneEdit::m_nCurrentMovieCommand; +int16 CSceneEdit::m_nNumActors; +int16 CSceneEdit::m_nNumMovieCommands; +int16 CSceneEdit::m_nCurrentCommand; +int16 CSceneEdit::m_nCurrentVehicle; +int16 CSceneEdit::m_nCurrentActor; +int16 CSceneEdit::m_nWeaponType; +bool CSceneEdit::m_bCommandActive; +bool CSceneEdit::m_bActorSelected; +bool CSceneEdit::m_bActor2Selected; +bool CSceneEdit::m_bVehicleSelected; +int16 CSceneEdit::m_nNumVehicles; +CPed* CSceneEdit::pActors[NUM_ACTORS_IN_MOVIE]; +CVehicle* CSceneEdit::pVehicles[NUM_VEHICLES_IN_MOVIE]; +bool CSceneEdit::m_bDrawGotoArrow; +CMovieCommand CSceneEdit::Movie[NUM_COMMANDS_IN_MOVIE]; + +#define SHADOW_OFFSET (2.0f) +#define ACTION_MESSAGE_X_RIGHT (60.0f) +#define ACTION_MESSAGE_Y (8.0f) +#define SELECTED_MESSAGE_X_RIGHT (60.0f) +#define SELECTED_MESSAGE_Y (248.0f) +#define COMMAND_NAME_X_RIGHT (60.0f) +#define COMMAND_NAME_Y (38.0f) +#define COMMAND_NAME_HEIGHT (16.0f) + +#define NUM_COMMANDS_TO_DRAW (9) + +static const char* pCommandStrings[] = { + "do-nothing", "New Actor", "Move Actor", "Select Actor", "Delete Actor", + "New Vehicle", "Move Vehicle", "Select Vehicle", "Delete Vehicle", "Give Weapon", + "Goto", "Goto (wait)", "Get In Car", "Get Out Car", "Kill", + "Flee", "Wait", "Position Camera", "Set Camera Target", "Select Camera Mode", + "Save Movie", "Load Movie", "Play Movie", "END" +}; + +#ifdef CHECK_STRUCT_SIZES +static_assert(ARRAY_SIZE(pCommandStrings) == CSceneEdit::MOVIE_TOTAL_COMMANDS, "Scene edit: not all commands have names"); +#endif + +static int32 NextValidModelId(int32 mi, int32 step) +{ + int32 result = -1; + int32 i = mi; + while (result == -1) { + i += step; + if (i < 0 || i > MODELINFOSIZE) { + step = -step; + continue; + } + CBaseModelInfo* pInfo = CModelInfo::GetModelInfo(i); + CVehicleModelInfo* pVehicleInfo = (CVehicleModelInfo*)pInfo; + if (!pInfo) + continue; + if (pInfo->GetModelType() == MITYPE_PED +#ifdef FIX_BUGS + && !(i >= MI_SPECIAL01 && i <= MI_SPECIAL21) +#endif + || pInfo->GetModelType() == MITYPE_VEHICLE && +#ifdef FIX_BUGS + (pVehicleInfo->m_vehicleType == VEHICLE_TYPE_CAR || pVehicleInfo->m_vehicleType == VEHICLE_TYPE_BOAT)) +#else // && and || priority failure it seems, also crashes on special models + pVehicleInfo->m_vehicleType == VEHICLE_TYPE_CAR || pVehicleInfo->m_vehicleType == VEHICLE_TYPE_BOAT) +#endif + result = i; + } + return result; +} + +void CSceneEdit::LoadMovie(void) +{ + ReInitialise(); + CFileMgr::SetDir("DATA"); + int fid = CFileMgr::OpenFile("movie.dat", "r"); +#ifdef FIX_BUGS + if (fid >= 0) +#endif + { + CFileMgr::Read(fid, (char*)&Movie, sizeof(Movie)); + CFileMgr::Read(fid, (char*)&m_nNumMovieCommands, sizeof(m_nNumMovieCommands)); + CFileMgr::CloseFile(fid); + } + CFileMgr::SetDir(""); + m_bCommandActive = false; +} + +void CSceneEdit::SaveMovie(void) +{ + CFileMgr::SetDir("DATA"); + int fid = CFileMgr::OpenFileForWriting("movie.dat"); + if (fid >= 0) { + CFileMgr::Write(fid, (char*)&Movie, sizeof(Movie)); + CFileMgr::Write(fid, (char*)&m_nNumMovieCommands, sizeof(m_nNumMovieCommands)); + CFileMgr::CloseFile(fid); + } + CFileMgr::SetDir(""); + m_bCommandActive = false; +} + +void CSceneEdit::Initialise(void) +{ + m_nActor = -1; + m_nActor2 = -1; + m_nVehicle = -1; + m_nVehicle2 = -1; + m_nCurrentCommand = MOVIE_NEW_ACTOR; + m_nVehiclemodelId = MI_INFERNUS; + m_nPedmodelId = MI_MALE01; + m_nNumVehicles = 0; + m_nNumActors = 0; + m_nNumMovieCommands = 0; + m_bCommandActive = false; + m_bRecording = true; + m_bEditOn = false; + for (int i = 0; i < NUM_ACTORS_IN_MOVIE; i++) + pActors[i] = nil; + for (int i = 0; i < NUM_VEHICLES_IN_MOVIE; i++) + pVehicles[i] = nil; + m_vecCamHeading = TheCamera.Cams[TheCamera.ActiveCam].Front; + m_vecGotoPosition = CVector(0.0f, 0.0f, 0.0f); + m_bCameraFollowActor = false; + TheCamera.Cams[TheCamera.ActiveCam].ResetStatics = true; + m_bDrawGotoArrow = false; +} + +void CSceneEdit::InitPlayback(void) +{ + m_nVehiclemodelId = MI_INFERNUS; + m_nPedmodelId = MI_MALE01; + m_bCommandActive = false; + m_nNumActors = 0; + m_nNumVehicles = 0; + m_nActor = -1; + m_nActor2 = -1; + m_nVehicle = -1; + m_nVehicle2 = -1; + TheCamera.Cams[TheCamera.ActiveCam].ResetStatics = true; + m_vecCamHeading = TheCamera.Cams[TheCamera.ActiveCam].Front; + for (int i = 0; i < NUM_ACTORS_IN_MOVIE; i++) { + if (pActors[i]) { + CPopulation::RemovePed(pActors[i]); + pActors[i] = nil; + } + } + m_nCurrentActor = 0; + for (int i = 0; i < NUM_VEHICLES_IN_MOVIE; i++) { + if (pVehicles[i]) { + CWorld::Remove(pVehicles[i]); + delete pVehicles[i]; + pVehicles[i] = nil; + } + } + m_nCurrentVehicle = 0; + m_vecGotoPosition = CVector(0.0f, 0.0f, 0.0f); + m_nCurrentMovieCommand = MOVIE_DO_NOTHING; + m_bDrawGotoArrow = false; +} + +void CSceneEdit::ReInitialise(void) +{ + m_nVehiclemodelId = MI_INFERNUS; + m_nPedmodelId = MI_MALE01; + m_nCurrentCommand = MOVIE_NEW_ACTOR; + m_bEditOn = true; + m_bRecording = true; + m_bCommandActive = false; +#ifdef FIX_BUGS + m_bCameraFollowActor = false; + TheCamera.Cams[TheCamera.ActiveCam].ResetStatics = true; // not enough... +#endif + m_nActor = -1; + m_nActor2 = -1; + m_nVehicle = -1; + m_nVehicle2 = -1; + m_nNumMovieCommands = 0; + m_nCurrentMovieCommand = MOVIE_DO_NOTHING; + m_nNumActors = 0; + m_nNumVehicles = 0; + for (int i = 0; i < NUM_ACTORS_IN_MOVIE; i++) { + if (pActors[i]) { + CPopulation::RemovePed(pActors[i]); + pActors[i] = nil; + } + } + for (int i = 0; i < NUM_VEHICLES_IN_MOVIE; i++) { + if (pVehicles[i]) { + CWorld::Remove(pVehicles[i]); + delete pVehicles[i]; + pVehicles[i] = nil; + } + } + for (int i = 0; i < NUM_COMMANDS_IN_MOVIE; i++) { + Movie[i].m_nCommandId = MOVIE_DO_NOTHING; + Movie[i].m_vecPosition = CVector(0.0f, 0.0f, 0.0f); + Movie[i].m_vecCamera = CVector(0.0f, 0.0f, 0.0f); + Movie[i].m_nActorId = -1; + Movie[i].m_nActor2Id = -1; + Movie[i].m_nVehicleId = -1; + Movie[i].m_nModelIndex = 0; + } + m_vecGotoPosition = CVector(0.0f, 0.0f, 0.0f); + m_bDrawGotoArrow = false; +} + +void CSceneEdit::Update(void) +{ + if (!m_bEditOn) + return; + if (m_bRecording) + ProcessCommand(); + else { + if (m_bCameraFollowActor && m_nActor != -1) { + if (pActors[m_nActor]->bInVehicle) + TheCamera.TakeControl(pActors[m_nActor]->m_pMyVehicle, CCam::MODE_BEHINDCAR, JUMP_CUT, CAMCONTROL_SCRIPT); + else + TheCamera.TakeControl(pActors[m_nActor], CCam::MODE_FOLLOWPED, JUMP_CUT, CAMCONTROL_SCRIPT); + } + PlayBack(); + } +} + +void CSceneEdit::Draw(void) +{ + char str[200]; + wchar wstr[200]; + if (TheCamera.m_WideScreenOn) + return; +#ifndef FIX_BUGS + CFont::SetPropOff(); +#endif + CFont::SetBackgroundOff(); + CFont::SetScale(SCREEN_SCALE_X(0.8f), SCREEN_SCALE_Y(1.35f)); + CFont::SetCentreOn(); + CFont::SetRightJustifyOn(); + CFont::SetRightJustifyWrap(0.0f); + CFont::SetBackGroundOnlyTextOff(); +#ifdef FIX_BUGS + CFont::SetFontStyle(FONT_STANDARD); + CFont::SetPropOn(); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + CFont::SetDropShadowPosition(1); +#else + CFont::SetFontStyle(FONT_HEADING); + CFont::SetPropOff(); +#endif + sprintf(str, "Action"); + AsciiToUnicode(str, wstr); + CFont::SetColor(CRGBA(0, 0, 0, 255)); +#ifdef FIX_BUGS + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(ACTION_MESSAGE_X_RIGHT - SHADOW_OFFSET), SCREEN_SCALE_Y(ACTION_MESSAGE_Y + SHADOW_OFFSET), wstr); +#else + CFont::PrintString(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH-ACTION_MESSAGE_X_RIGHT) + SHADOW_OFFSET, SCREEN_SCALE_FROM_BOTTOM(DEFAULT_SCREEN_HEIGHT-ACTION_MESSAGE_Y) + SHADOW_OFFSET, wstr); +#endif + CFont::SetColor(CRGBA(193, 164, 120, 255)); +#ifdef FIX_BUGS + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(ACTION_MESSAGE_X_RIGHT), SCREEN_SCALE_Y(ACTION_MESSAGE_Y), wstr); +#else + CFont::PrintString(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH-ACTION_MESSAGE_X_RIGHT), SCREEN_SCALE_FROM_BOTTOM(DEFAULT_SCREEN_HEIGHT-ACTION_MESSAGE_Y), wstr); +#endif + sprintf(str, "Selected"); + AsciiToUnicode(str, wstr); + CFont::SetColor(CRGBA(0, 0, 0, 255)); +#ifdef FIX_BUGS + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(SELECTED_MESSAGE_X_RIGHT - SHADOW_OFFSET), SCREEN_SCALE_Y(SELECTED_MESSAGE_Y + SHADOW_OFFSET), wstr); +#else + CFont::PrintString(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH-SELECTED_MESSAGE_X_RIGHT) + SHADOW_OFFSET, SCREEN_SCALE_FROM_BOTTOM(DEFAULT_SCREEN_HEIGHT-SELECTED_MESSAGE_Y) + SHADOW_OFFSET, wstr); +#endif + CFont::SetColor(CRGBA(193, 164, 120, 255)); +#ifdef FIX_BUGS + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(SELECTED_MESSAGE_X_RIGHT), SCREEN_SCALE_Y(SELECTED_MESSAGE_Y), wstr); +#else + CFont::PrintString(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH-SELECTED_MESSAGE_X_RIGHT), SCREEN_SCALE_FROM_BOTTOM(DEFAULT_SCREEN_HEIGHT-SELECTED_MESSAGE_Y), wstr); +#endif + CFont::SetCentreOff(); +#ifdef FIX_BUGS + CFont::SetScale(SCREEN_SCALE_X(0.7f), SCREEN_SCALE_Y(0.7f)); +#else + CFont::SetScale(0.7f, 0.7f); +#endif +#ifdef FIX_BUGS + CFont::SetFontStyle(FONT_STANDARD); +#else + CFont::SetFontStyle(FONT_HEADING); +#endif + CFont::SetColor(CRGBA(0, 0, 0, 255)); + for (int i = 0; i < NUM_COMMANDS_TO_DRAW; i++) { + int16 nCommandDrawn = m_nCurrentCommand + i - NUM_COMMANDS_TO_DRAW / 2; + if (nCommandDrawn >= MOVIE_TOTAL_COMMANDS) + nCommandDrawn -= (MOVIE_TOTAL_COMMANDS - 1); + if (nCommandDrawn <= MOVIE_DO_NOTHING) + nCommandDrawn += (MOVIE_TOTAL_COMMANDS - 1); + sprintf(str, "%s", pCommandStrings[nCommandDrawn]); + AsciiToUnicode(str, wstr); + CFont::SetColor(CRGBA(0, 0, 0, 255)); +#ifdef FIX_BUGS + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(COMMAND_NAME_X_RIGHT - SHADOW_OFFSET), SCREEN_SCALE_Y(COMMAND_NAME_Y + SHADOW_OFFSET + i * COMMAND_NAME_HEIGHT), wstr); +#else + CFont::PrintString(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH-COMMAND_NAME_X_RIGHT) + SHADOW_OFFSET, SCREEN_SCALE_FROM_BOTTOM(DEFAULT_SCREEN_HEIGHT-COMMAND_NAME_Y) + SHADOW_OFFSET + i * COMMAND_NAME_HEIGHT, wstr); +#endif + if (nCommandDrawn == m_nCurrentCommand) + CFont::SetColor(CRGBA(156, 91, 40, 255)); + else + CFont::SetColor(CRGBA(193, 164, 120, 255)); +#ifdef FIX_BUGS + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(COMMAND_NAME_X_RIGHT), SCREEN_SCALE_Y(COMMAND_NAME_Y + i * COMMAND_NAME_HEIGHT), wstr); +#else + CFont::PrintString(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH-COMMAND_NAME_X_RIGHT), SCREEN_SCALE_FROM_BOTTOM(DEFAULT_SCREEN_HEIGHT-COMMAND_NAME_Y) + i * COMMAND_NAME_HEIGHT, wstr); +#endif + } +} + +void CSceneEdit::ProcessCommand(void) +{ + if (!m_bCommandActive) { + ClearForNewCommand(); + if (CPad::GetPad(1)->GetDPadUpJustDown()) { + if (--m_nCurrentCommand == MOVIE_DO_NOTHING) + m_nCurrentCommand = MOVIE_END; + } + if (CPad::GetPad(1)->GetDPadDownJustDown()) { + if (++m_nCurrentCommand == MOVIE_TOTAL_COMMANDS) + m_nCurrentCommand = MOVIE_NEW_ACTOR; + } + if (CPad::GetPad(1)->GetTriangleJustDown()) { + if (m_nCurrentCommand != MOVIE_DO_NOTHING) + m_bCommandActive = true; + } + return; + } + switch (m_nCurrentCommand) { + case MOVIE_DO_NOTHING: + m_bCommandActive = false; + break; + case MOVIE_NEW_ACTOR: + if (m_nActor == -1) { + if (m_nNumActors == NUM_ACTORS_IN_MOVIE) + break; + if (!CStreaming::HasModelLoaded(m_nPedmodelId)) { + CStreaming::RequestModel(m_nPedmodelId, 0); +#ifdef FIX_BUGS + CStreaming::LoadAllRequestedModels(false); // otherwise gets stuck :( +#endif + break; + } + CPed* pPed = new CCivilianPed(PEDTYPE_SPECIAL, m_nPedmodelId); + pPed->CharCreatedBy = MISSION_CHAR; + pPed->SetPosition(m_vecCurrentPosition); + pPed->SetOrientation(0.0f, 0.0f, 0.0f); + CWorld::Add(pPed); + pPed->bUsesCollision = false; + pPed->bAffectedByGravity = false; + for (int i = 0; i < NUM_ACTORS_IN_MOVIE; i++) { + if (pActors[i] == nil) { + m_nActor = i; + pActors[i] = pPed; + break; + } + } + } + else { + pActors[m_nActor]->SetPosition(m_vecCurrentPosition); + pActors[m_nActor]->SetOrientation(0.0f, 0.0f, 0.0f); + int32 mi = m_nPedmodelId; + if (CPad::GetPad(1)->GetLeftShoulder1JustDown()) + mi = NextValidModelId(m_nPedmodelId, -1); + else if (CPad::GetPad(1)->GetRightShoulder1JustDown()) + mi = NextValidModelId(m_nPedmodelId, 1); + if (mi == m_nPedmodelId) { + if (CPad::GetPad(1)->GetTriangleJustDown()) { + pActors[m_nActor]->bUsesCollision = true; + pActors[m_nActor]->bAffectedByGravity = true; + ++m_nNumActors; + Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_NEW_ACTOR; + Movie[m_nNumMovieCommands].m_vecPosition = m_vecCurrentPosition; + Movie[m_nNumMovieCommands].m_nModelIndex = m_nPedmodelId; + Movie[m_nNumMovieCommands++].m_nActorId = m_nActor; + m_nActor = -1; + m_bCommandActive = false; + } + if (CPad::GetPad(1)->GetCircleJustDown()) { + CWorld::Remove(pActors[m_nActor]); + delete pActors[m_nActor]; + pActors[m_nActor] = nil; + m_nActor = -1; + m_bCommandActive = false; + } + } + else { + m_nPedmodelId = mi; + if (pActors[m_nActor]) { + CWorld::Remove(pActors[m_nActor]); + delete pActors[m_nActor]; + } + pActors[m_nActor] = nil; + m_nActor = -1; + } + } + break; + case MOVIE_MOVE_ACTOR: + SelectActor(); + if (m_bCommandActive) + break; + pActors[m_nActor]->SetPosition(m_vecCurrentPosition); + if (CPad::GetPad(1)->GetTriangleJustDown()) { + m_bCommandActive = false; +#ifndef FIX_BUGS // why? it crashes, also makes no sense + pActors[m_nActor] = nil; +#endif + SelectActor(); + } + break; + case MOVIE_SELECT_ACTOR: + SelectActor(); + break; + case MOVIE_DELETE_ACTOR: + SelectActor(); + if (m_bActorSelected) { + CPopulation::RemovePed(pActors[m_nActor]); + m_nCurrentActor = 0; + --m_nNumActors; +#ifdef FIX_BUGS + pActors[m_nActor] = nil; + m_nActor = -1; +#else + m_nActor = -1; + pActors[m_nActor] = nil; +#endif + SelectActor(); + m_bCommandActive = false; + } + else if (CPad::GetPad(1)->GetCircleJustDown()) { + m_nActor = -1; + m_bCommandActive = false; + } + break; + case MOVIE_NEW_VEHICLE: + if (m_nVehicle == -1) { + if (m_nNumVehicles == NUM_VEHICLES_IN_MOVIE) + break; + if (!CStreaming::HasModelLoaded(m_nVehiclemodelId)) { + CStreaming::RequestModel(m_nVehiclemodelId, 0); +#ifdef FIX_BUGS + CStreaming::LoadAllRequestedModels(false); // otherwise gets stuck :( +#endif + break; + } + CVehicle* pVehicle = new CAutomobile(m_nVehiclemodelId, MISSION_VEHICLE); + pVehicle->SetStatus(STATUS_PHYSICS); + pVehicle->SetPosition(m_vecCurrentPosition); + pVehicle->SetOrientation(0.0f, 0.0f, 0.0f); + CWorld::Add(pVehicle); + pVehicle->bUsesCollision = false; + pVehicle->bAffectedByGravity = false; + for (int i = 0; i < NUM_VEHICLES_IN_MOVIE; i++) { + if (pVehicles[i] == nil) { + m_nVehicle = i; + pVehicles[i] = pVehicle; + break; + } + } + } + else { + pVehicles[m_nVehicle]->SetPosition(m_vecCurrentPosition); + pVehicles[m_nVehicle]->SetOrientation(0.0f, 0.0f, 0.0f); + int32 mi = m_nVehiclemodelId; + if (CPad::GetPad(1)->GetLeftShoulder1JustDown()) + mi = NextValidModelId(m_nVehiclemodelId, -1); + else if (CPad::GetPad(1)->GetRightShoulder1JustDown()) + mi = NextValidModelId(m_nVehiclemodelId, 1); + if (mi == m_nVehiclemodelId) { + if (CPad::GetPad(1)->GetTriangleJustDown()) { + pVehicles[m_nVehicle]->bUsesCollision = true; + pVehicles[m_nVehicle]->bAffectedByGravity = true; + ++m_nNumVehicles; + Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_NEW_VEHICLE; + Movie[m_nNumMovieCommands].m_vecPosition = m_vecCurrentPosition; + Movie[m_nNumMovieCommands].m_nModelIndex = m_nVehiclemodelId; + Movie[m_nNumMovieCommands++].m_nVehicleId = m_nVehicle; + m_nVehicle = -1; + m_bCommandActive = false; + } + if (CPad::GetPad(1)->GetCircleJustDown()) { + CWorld::Remove(pVehicles[m_nVehicle]); + delete pVehicles[m_nVehicle]; + pVehicles[m_nVehicle] = nil; + m_nVehicle = -1; + m_bCommandActive = false; + } + } + else { + m_nVehiclemodelId = mi; + if (pVehicles[m_nVehicle]) { + CWorld::Remove(pVehicles[m_nVehicle]); + delete pVehicles[m_nVehicle]; + } + pVehicles[m_nVehicle] = nil; + m_nVehicle = -1; + } + } + break; + case MOVIE_MOVE_VEHICLE: + SelectVehicle(); + if (m_bCommandActive) + break; + pVehicles[m_nVehicle]->SetPosition(m_vecCurrentPosition); + if (CPad::GetPad(1)->GetTriangleJustDown()) { + m_bCommandActive = false; +#ifndef FIX_BUGS // again, why? works wrong + pVehicles[m_nVehicle] = nil; +#endif + m_nVehicle = -1; + } + break; + case MOVIE_SELECT_VEHICLE: + SelectVehicle(); + break; + case MOVIE_DELETE_VEHICLE: + SelectVehicle(); + if (m_bVehicleSelected) { + CWorld::Remove(pVehicles[m_nVehicle]); + delete pVehicles[m_nVehicle]; + m_nCurrentVehicle = 0; + --m_nNumVehicles; + pVehicles[m_nVehicle] = nil; + m_nVehicle = -1; + SelectVehicle(); + m_bCommandActive = false; + } + else if (CPad::GetPad(1)->GetCircleJustDown()) { + pVehicles[m_nVehicle] = nil; + m_nVehicle = -1; + m_bCommandActive = false; + } + break; + case MOVIE_GIVE_WEAPON: + if (m_bActorSelected) { + if (SelectWeapon()) { + m_bCommandActive = false; + Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_GIVE_WEAPON; + Movie[m_nNumMovieCommands].m_nActorId = m_nActor; + Movie[m_nNumMovieCommands++].m_nModelIndex = m_nWeaponType; + } + } + else { + SelectActor(); + m_bCommandActive = true; + } + break; + case MOVIE_GOTO: + case MOVIE_GOTO_WAIT: + if (!m_bActorSelected) { + m_bDrawGotoArrow = true; + SelectActor(); + if (m_nActor == -1) + m_bCommandActive = true; + } + else { + m_vecGotoPosition = m_vecCurrentPosition; + if (CPad::GetPad(1)->GetTriangleJustDown()) { + if (pActors[m_nActor]->bInVehicle) { + if (CCarCtrl::JoinCarWithRoadSystemGotoCoors(pActors[m_nActor]->m_pMyVehicle, m_vecGotoPosition, false)) + pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS_STRAIGHT; + else + pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS; + pActors[m_nActor]->m_pMyVehicle->SetStatus(STATUS_PHYSICS); + pActors[m_nActor]->m_pMyVehicle->bEngineOn = true; + pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCruiseSpeed = Max(16, pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCruiseSpeed); + pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); + TheCamera.TakeControl(pActors[m_nActor]->m_pMyVehicle, CCam::MODE_BEHINDCAR, JUMP_CUT, CAMCONTROL_SCRIPT); + } + else { + pActors[m_nActor]->SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, m_vecGotoPosition); + TheCamera.TakeControl(pActors[m_nActor], CCam::MODE_FOLLOWPED, JUMP_CUT, CAMCONTROL_SCRIPT); + } + m_bDrawGotoArrow = false; + Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_GOTO; + Movie[m_nNumMovieCommands].m_nActorId = m_nActor; + Movie[m_nNumMovieCommands++].m_vecPosition = m_vecGotoPosition; + } + if (!m_bDrawGotoArrow) { + if (pActors[m_nActor]->bInVehicle && pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCarMission == MISSION_NONE || + !pActors[m_nActor]->bInVehicle && pActors[m_nActor]->m_objective == OBJECTIVE_NONE) { + if (pActors[m_nActor]) // if there is something that requires this check the least, it's this one + m_vecCamHeading = TheCamera.Cams[TheCamera.ActiveCam].CamTargetEntity->GetPosition() - TheCamera.Cams[TheCamera.ActiveCam].Source; + m_bCommandActive = false; + TheCamera.Cams[TheCamera.ActiveCam].Mode = CCam::MODE_FIGHT_CAM_RUNABOUT; + m_vecCurrentPosition = pActors[m_nActor]->GetPosition(); + pActors[m_nActor]->SetObjective(OBJECTIVE_NONE); + if (pActors[m_nActor]->bInVehicle) + pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_NONE; + } + } + if (CPad::GetPad(1)->GetCircleJustDown()) { + pActors[m_nActor] = nil; + m_nActor = -1; + m_bCommandActive = false; + } + } + break; + case MOVIE_GET_IN_CAR: + if (m_bActorSelected) + SelectVehicle(); + else { + SelectActor(); + if (m_nActor != -1) + m_bCommandActive = true; + } + if (m_bVehicleSelected) { + pActors[m_nActor]->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, pVehicles[m_nVehicle]); + Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_GET_IN_CAR; + Movie[m_nNumMovieCommands].m_nActorId = m_nActor; + Movie[m_nNumMovieCommands++].m_nVehicleId = m_nVehicle; + m_nVehicle = -1; + m_bCommandActive = false; + } + if (CPad::GetPad(1)->GetCircleJustDown()) { + pVehicles[m_nVehicle] = nil; + m_nVehicle = -1; + pActors[m_nActor] = nil; + m_nActor = -1; + m_bCommandActive = false; + } + break; + case MOVIE_GET_OUT_CAR: + SelectActor(); + if (m_bActorSelected) { + if (pActors[m_nActor]->bInVehicle) { + pActors[m_nActor]->SetObjective(OBJECTIVE_LEAVE_CAR); + Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_GET_OUT_CAR; + Movie[m_nNumMovieCommands++].m_nActorId = m_nActor; + } + m_nActor = -1; + m_bCommandActive = false; + } + if (CPad::GetPad(1)->GetCircleJustDown()) { + pVehicles[m_nVehicle] = nil; + m_nVehicle = -1; + pActors[m_nActor] = nil; + m_nActor = -1; + m_bCommandActive = false; + } + break; + case MOVIE_KILL: + if (!m_bActorSelected) { + SelectActor(); + m_bCommandActive = true; + } + else if (!m_bActor2Selected) { + SelectActor2(); + if (m_bActorSelected && m_bActor2Selected && m_nActor != -1 && m_nActor2 != -1 && m_nActor != m_nActor2) { + pActors[m_nActor]->SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, pActors[m_nActor2]); + Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_KILL; + Movie[m_nNumMovieCommands].m_nActorId = m_nActor; + Movie[m_nNumMovieCommands++].m_nActor2Id = m_nActor2; + m_bCommandActive = false; + } + } + if (CPad::GetPad(1)->GetCircleJustDown()) { + pActors[m_nActor] = nil; + m_nActor = -1; + pActors[m_nActor2] = nil; + m_nActor2 = -1; + m_bCommandActive = false; + } + break; + case MOVIE_FLEE: + if (!m_bActorSelected) { + SelectActor(); + m_bCommandActive = true; + } + else if (!m_bActor2Selected) { + SelectActor2(); + if (m_bActorSelected && m_bActor2Selected && m_nActor != -1 && m_nActor2 != -1 && m_nActor != m_nActor2) { + pActors[m_nActor]->SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS, pActors[m_nActor2]); + Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_FLEE; + Movie[m_nNumMovieCommands].m_nActorId = m_nActor; + Movie[m_nNumMovieCommands++].m_nActor2Id = m_nActor2; + m_bCommandActive = false; + } + } + if (CPad::GetPad(1)->GetCircleJustDown()) { + pActors[m_nActor] = nil; + m_nActor = -1; + pActors[m_nActor2] = nil; + m_nActor2 = -1; + m_bCommandActive = false; + } + break; + case MOVIE_WAIT: + SelectActor(); + if (m_bActorSelected) { + pActors[m_nActor]->SetObjective(OBJECTIVE_WAIT_ON_FOOT); + Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_WAIT; + Movie[m_nNumMovieCommands++].m_nActorId = m_nActor; + } + if (CPad::GetPad(1)->GetCircleJustDown()) { + pActors[m_nActor] = nil; + m_nActor = -1; + m_bCommandActive = false; + } + break; + case MOVIE_POSITION_CAMERA: + if (CPad::GetPad(1)->GetTriangleJustDown()) { + Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_POSITION_CAMERA; + Movie[m_nNumMovieCommands].m_vecPosition = TheCamera.Cams[TheCamera.ActiveCam].Source; + Movie[m_nNumMovieCommands++].m_vecCamera = m_vecCamHeading; + m_bCommandActive = false; + } + if (CPad::GetPad(1)->GetCircleJustDown()) { + m_bCommandActive = false; + } + break; + case MOVIE_SET_CAMERA_TARGET: + if (!m_bActorSelected) { + SelectActor(); + m_bCommandActive = true; + } + else { + TheCamera.Cams[TheCamera.ActiveCam].CamTargetEntity = pActors[m_nActor]; + if (CPad::GetPad(1)->GetTriangleJustDown()) { + Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_SET_CAMERA_TARGET; + Movie[m_nNumMovieCommands++].m_nActorId = m_nActor; + m_bCommandActive = false; + } + } + break; + case MOVIE_SELECT_CAMERA_MODE: + m_bCommandActive = false; + break; + case MOVIE_SAVE_MOVIE: + SaveMovie(); + break; + case MOVIE_LOAD_MOVIE: + LoadMovie(); + break; + case MOVIE_PLAY_MOVIE: + InitPlayback(); + LoadMovie(); + m_bRecording = false; + break; + case MOVIE_END: + m_bRecording = false; + break; + default: + assert(0); + } +} + +void CSceneEdit::PlayBack(void) +{ + m_nCurrentCommand = Movie[m_nCurrentMovieCommand].m_nCommandId; + if (m_nCurrentMovieCommand >= m_nNumMovieCommands) { + if (CPad::GetPad(1)->GetTriangleJustDown()) { + m_nCurrentCommand = MOVIE_DO_NOTHING; + m_bRecording = true; + ReInitialise(); + } + return; + } + switch (m_nCurrentCommand) { + case MOVIE_DO_NOTHING: + case MOVIE_MOVE_ACTOR: + case MOVIE_SELECT_ACTOR: + case MOVIE_DELETE_ACTOR: + case MOVIE_MOVE_VEHICLE: + case MOVIE_SELECT_VEHICLE: + case MOVIE_DELETE_VEHICLE: + break; + case MOVIE_NEW_ACTOR: + { + m_nPedmodelId = Movie[m_nCurrentMovieCommand].m_nModelIndex; + m_vecCurrentPosition = Movie[m_nCurrentMovieCommand].m_vecPosition; + if (!CStreaming::HasModelLoaded(m_nPedmodelId)) { + CStreaming::RequestModel(m_nPedmodelId, 0); +#ifdef FIX_BUGS + CStreaming::LoadAllRequestedModels(false); // otherwise gets stuck :( +#endif + break; + } + CPed* pPed = new CCivilianPed(PEDTYPE_SPECIAL, m_nPedmodelId); + pPed->CharCreatedBy = MISSION_CHAR; + CWorld::Add(pPed); + pPed->SetPosition(m_vecCurrentPosition); + pPed->SetOrientation(0.0f, 0.0f, 0.0f); + for (int i = 0; i < NUM_ACTORS_IN_MOVIE; i++) { + if (pActors[i] == nil) { + m_nActor = i; + pActors[i] = pPed; + break; + } + } + m_nNumActors++; + m_nCurrentMovieCommand++; + break; + } + case MOVIE_NEW_VEHICLE: + { + m_nVehiclemodelId = Movie[m_nCurrentMovieCommand].m_nModelIndex; + m_vecCurrentPosition = Movie[m_nCurrentMovieCommand].m_vecPosition; + if (!CStreaming::HasModelLoaded(m_nVehiclemodelId)) { + CStreaming::RequestModel(m_nVehiclemodelId, 0); +#ifdef FIX_BUGS + CStreaming::LoadAllRequestedModels(false); // otherwise gets stuck :( +#endif + break; + } + CVehicle* pVehicle = new CAutomobile(m_nVehiclemodelId, MISSION_VEHICLE); + pVehicle->SetStatus(STATUS_PHYSICS); + pVehicle->SetPosition(m_vecCurrentPosition); + pVehicle->SetOrientation(0.0f, 0.0f, 0.0f); + CWorld::Add(pVehicle); + for (int i = 0; i < NUM_VEHICLES_IN_MOVIE; i++) { + if (pVehicles[i] == nil) { + m_nVehicle = i; + pVehicles[i] = pVehicle; + break; + } + } + m_nNumVehicles++; + m_nCurrentMovieCommand++; + break; + } + case MOVIE_GIVE_WEAPON: + m_nActor = Movie[m_nCurrentMovieCommand].m_nActorId; + m_nWeaponType = Movie[m_nCurrentMovieCommand].m_nModelIndex; + pActors[m_nActor]->GiveWeapon((eWeaponType)m_nWeaponType, 1000); + pActors[m_nActor]->AddWeaponModel(CWeaponInfo::GetWeaponInfo(pActors[m_nActor]->GetWeapon()->m_eWeaponType)->m_nModelId); + pActors[m_nActor]->SetCurrentWeapon(m_nWeaponType); + m_nCurrentMovieCommand++; + break; + case MOVIE_GOTO: + case MOVIE_GOTO_WAIT: + m_nActor = Movie[m_nCurrentMovieCommand].m_nActorId; + m_vecGotoPosition = Movie[m_nCurrentMovieCommand].m_vecPosition; + if (pActors[m_nActor]->bInVehicle) { + if (pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCarMission != MISSION_GOTOCOORDS && + pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCarMission != MISSION_GOTOCOORDS_STRAIGHT) { + if ((pActors[m_nActor]->m_pMyVehicle->GetPosition() - m_vecGotoPosition).Magnitude() < 5.0f) { + if (CCarCtrl::JoinCarWithRoadSystemGotoCoors(pActors[m_nActor]->m_pMyVehicle, m_vecGotoPosition, false)) + pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS_STRAIGHT; + else + pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS; + pActors[m_nActor]->m_pMyVehicle->SetStatus(STATUS_PHYSICS); + pActors[m_nActor]->m_pMyVehicle->bEngineOn = true; + pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCruiseSpeed = Max(16, pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCruiseSpeed); + pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); + if (m_nCurrentCommand != MOVIE_GOTO_WAIT) + ++m_nCurrentMovieCommand; + } + else + ++m_nCurrentMovieCommand; + } + } + else { + if (pActors[m_nActor]->m_objective != OBJECTIVE_GOTO_AREA_ON_FOOT) { + pActors[m_nActor]->SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, m_vecGotoPosition); + ++m_nCurrentMovieCommand; + } + } + break; + case MOVIE_GET_IN_CAR: + m_nActor = Movie[m_nCurrentMovieCommand].m_nActorId; + if (!pActors[m_nActor]->bInVehicle){ + m_nVehicle = Movie[m_nCurrentMovieCommand].m_nVehicleId; + pActors[m_nActor]->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, pVehicles[m_nVehicle]); + } + else + ++m_nCurrentMovieCommand; + break; + case MOVIE_GET_OUT_CAR: + m_nActor = Movie[m_nCurrentMovieCommand].m_nActorId; + if (pActors[m_nActor]->bInVehicle) + pActors[m_nActor]->SetObjective(OBJECTIVE_LEAVE_CAR); + else + ++m_nCurrentMovieCommand; + break; + case MOVIE_KILL: + m_nActor = Movie[m_nCurrentMovieCommand].m_nActorId; + m_nActor2 = Movie[m_nCurrentMovieCommand].m_nActor2Id; + pActors[m_nActor]->SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, pActors[m_nActor2]); + if (pActors[m_nActor2]->GetPedState() == PED_DEAD) + ++m_nCurrentMovieCommand; + break; + case MOVIE_FLEE: + m_nActor = Movie[m_nCurrentMovieCommand].m_nActorId; + m_nActor2 = Movie[m_nCurrentMovieCommand].m_nActor2Id; + pActors[m_nActor]->SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS, pActors[m_nActor2]); + ++m_nCurrentMovieCommand; + break; + case MOVIE_WAIT: + m_nActor = Movie[m_nCurrentMovieCommand].m_nActorId; + pActors[m_nActor]->SetObjective(OBJECTIVE_WAIT_ON_FOOT); + ++m_nCurrentMovieCommand; + break; + case MOVIE_POSITION_CAMERA: + TheCamera.Cams[TheCamera.ActiveCam].Source = Movie[m_nCurrentMovieCommand].m_vecPosition; + m_vecCamHeading = Movie[m_nCurrentMovieCommand].m_vecCamera; + TheCamera.Cams[TheCamera.ActiveCam].Front = m_vecCamHeading; + ++m_nCurrentMovieCommand; + break; + case MOVIE_SET_CAMERA_TARGET: + m_bCameraFollowActor = true; + TheCamera.Cams[TheCamera.ActiveCam].CamTargetEntity = pActors[Movie[m_nNumMovieCommands].m_nActorId]; + TheCamera.pTargetEntity = pActors[Movie[m_nNumMovieCommands].m_nActorId]; + TheCamera.m_bLookingAtPlayer = false; + ++m_nCurrentMovieCommand; + break; + case MOVIE_SELECT_CAMERA_MODE: + m_bCommandActive = false; // this is wrong + break; + } +} + +void CSceneEdit::ClearForNewCommand(void) +{ + m_nActor = -1; + m_nActor2 = -1; + m_nVehicle = -1; + m_bActorSelected = false; + m_bActor2Selected = false; + m_bVehicleSelected = false; + m_bDrawGotoArrow = false; +} +void CSceneEdit::SelectActor(void) +{ + m_bActorSelected = false; + if (m_nActor != -1) { + if (CPad::GetPad(1)->GetLeftShoulder1JustDown()) { + CPed* pPed; + do { + if (--m_nActor < 0) + m_nActor = NUM_ACTORS_IN_MOVIE - 1; + pPed = pActors[m_nActor]; + } while (pPed == nil); + TheCamera.Cams[TheCamera.ActiveCam].Source = pPed->GetPosition() - m_vecCamHeading; + } + else if (CPad::GetPad(1)->GetRightShoulder1JustDown()) { + CPed* pPed; + do { + if (++m_nActor == NUM_ACTORS_IN_MOVIE) + m_nActor = 0; + pPed = pActors[m_nActor]; + } while (pPed == nil); + TheCamera.Cams[TheCamera.ActiveCam].Source = pPed->GetPosition() - m_vecCamHeading; + } + m_vecCurrentPosition = pActors[m_nActor]->GetPosition(); + if (CPad::GetPad(1)->GetTriangleJustDown()) { + m_bActorSelected = true; + m_bCommandActive = false; + } + else if (CPad::GetPad(1)->GetCircleJustDown()) { + m_nActor = -1; + } + } + else if (m_nNumActors != 0) { + for (int i = 0; i < NUM_ACTORS_IN_MOVIE; i++) { + if (pActors[i] != nil) { + m_nActor = i; + break; + } + } + TheCamera.Cams[TheCamera.ActiveCam].Source = pActors[m_nActor]->GetPosition() - m_vecCamHeading; + if (m_nNumActors == 1) { + m_bActorSelected = true; + m_bCommandActive = false; + } + } + else { + m_bCommandActive = false; + } +} + +void CSceneEdit::SelectActor2(void) +{ + m_bActor2Selected = false; + if (m_nNumActors <= 1) { + m_bCommandActive = false; + return; + } + if (m_nActor2 != -1) { + if (CPad::GetPad(1)->GetLeftShoulder1JustDown()) { + CPed* pPed; + do { + if (--m_nActor2 < 0) + m_nActor2 = NUM_ACTORS_IN_MOVIE - 1; + pPed = pActors[m_nActor2]; + } while (pPed == nil || pPed == pActors[m_nActor]); + TheCamera.Cams[TheCamera.ActiveCam].Source = pPed->GetPosition() - m_vecCamHeading; + } + else if (CPad::GetPad(1)->GetRightShoulder1JustDown()) { + CPed* pPed; + do { + if (++m_nActor2 == NUM_ACTORS_IN_MOVIE) + m_nActor2 = 0; + pPed = pActors[m_nActor2]; + } while (pPed == nil || pPed == pActors[m_nActor]); + TheCamera.Cams[TheCamera.ActiveCam].Source = pPed->GetPosition() - m_vecCamHeading; + } + m_vecCurrentPosition = pActors[m_nActor2]->GetPosition(); + if (CPad::GetPad(1)->GetTriangleJustDown()) { + m_bActor2Selected = true; + m_bCommandActive = false; + } + else if (CPad::GetPad(1)->GetCircleJustDown()) { + m_nActor2 = -1; + } + } + else { + for (int i = 0; i < NUM_ACTORS_IN_MOVIE; i++) { + if (pActors[i] != nil && pActors[m_nActor] != pActors[i] ) { + m_nActor2 = i; + break; + } + } + TheCamera.Cams[TheCamera.ActiveCam].Source = pActors[m_nActor2]->GetPosition() - m_vecCamHeading; + } +} + +void CSceneEdit::SelectVehicle(void) +{ + m_bVehicleSelected = false; + if (m_nVehicle != -1) { + if (CPad::GetPad(1)->GetLeftShoulder1JustDown()) { + CVehicle* pVehicle; + do { + if (--m_nVehicle < 0) + m_nVehicle = NUM_VEHICLES_IN_MOVIE - 1; + pVehicle = pVehicles[m_nVehicle]; + } while (pVehicle == nil); + } + else if (CPad::GetPad(1)->GetRightShoulder1JustDown()) { + CVehicle* pVehicle; + do { + if (++m_nVehicle == NUM_VEHICLES_IN_MOVIE) + m_nVehicle = 0; + pVehicle = pVehicles[m_nVehicle]; + } while (pVehicle == nil); + } + m_vecCurrentPosition = pVehicles[m_nVehicle]->GetPosition(); + TheCamera.Cams[TheCamera.ActiveCam].Source = pVehicles[m_nVehicle]->GetPosition() - m_vecCamHeading; + if (CPad::GetPad(1)->GetTriangleJustDown()) { + m_bVehicleSelected = true; + m_bCommandActive = false; + } + else if (CPad::GetPad(1)->GetCircleJustDown()) { + m_nVehicle = -1; + } + } + else if (m_nNumVehicles != 0) { + for (int i = 0; i < NUM_ACTORS_IN_MOVIE; i++) { + if (pVehicles[i] != nil) { + m_nVehicle = i; + break; + } + } + } +} + +bool CSceneEdit::SelectWeapon(void) +{ + if (m_nWeaponType == WEAPONTYPE_UNARMED) { + m_nWeaponType = WEAPONTYPE_COLT45; + return false; + } + if (CPad::GetPad(1)->GetLeftShoulder1JustDown()) { + if (++m_nWeaponType >= WEAPONTYPE_DETONATOR) + m_nWeaponType = WEAPONTYPE_BRASSKNUCKLE; + pActors[m_nActor]->ClearWeapons(); + pActors[m_nActor]->GiveWeapon((eWeaponType)m_nWeaponType, 1000); + pActors[m_nActor]->AddWeaponModel(CWeaponInfo::GetWeaponInfo(pActors[m_nActor]->GetWeapon()->m_eWeaponType)->m_nModelId); + pActors[m_nActor]->SetCurrentWeapon(m_nWeaponType); + } + else if (CPad::GetPad(1)->GetRightShoulder1JustDown()){ + if (--m_nWeaponType <= WEAPONTYPE_UNARMED) + m_nWeaponType = WEAPONTYPE_MINIGUN; + pActors[m_nActor]->ClearWeapons(); + pActors[m_nActor]->GiveWeapon((eWeaponType)m_nWeaponType, 1000); + pActors[m_nActor]->AddWeaponModel(CWeaponInfo::GetWeaponInfo(pActors[m_nActor]->GetWeapon()->m_eWeaponType)->m_nModelId); + pActors[m_nActor]->SetCurrentWeapon(m_nWeaponType); + } + if (CPad::GetPad(1)->GetTriangleJustDown()) { + m_bCommandActive = false; + return true; + } + if (CPad::GetPad(1)->GetCircleJustDown()) { + pActors[m_nActor]->ClearWeapons(); + m_nWeaponType = WEAPONTYPE_UNARMED; + m_bCommandActive = false; + return false; + } + return false; +} +#endif diff --git a/src/miami/control/SceneEdit.h b/src/miami/control/SceneEdit.h new file mode 100644 index 00000000..7c8fb98a --- /dev/null +++ b/src/miami/control/SceneEdit.h @@ -0,0 +1,96 @@ +#pragma once +#ifdef GTA_SCENE_EDIT +class CPed; +class CVehicle; + +struct CMovieCommand +{ + int32 m_nCommandId; + CVector m_vecPosition; + CVector m_vecCamera; + int16 m_nActorId; + int16 m_nActor2Id; + int16 m_nVehicleId; + int16 m_nModelIndex; +}; + +class CSceneEdit +{ +public: + enum { + MOVIE_DO_NOTHING = 0, + MOVIE_NEW_ACTOR, + MOVIE_MOVE_ACTOR, + MOVIE_SELECT_ACTOR, + MOVIE_DELETE_ACTOR, + MOVIE_NEW_VEHICLE, + MOVIE_MOVE_VEHICLE, + MOVIE_SELECT_VEHICLE, + MOVIE_DELETE_VEHICLE, + MOVIE_GIVE_WEAPON, + MOVIE_GOTO, + MOVIE_GOTO_WAIT, + MOVIE_GET_IN_CAR, + MOVIE_GET_OUT_CAR, + MOVIE_KILL, + MOVIE_FLEE, + MOVIE_WAIT, + MOVIE_POSITION_CAMERA, + MOVIE_SET_CAMERA_TARGET, + MOVIE_SELECT_CAMERA_MODE, + MOVIE_SAVE_MOVIE, + MOVIE_LOAD_MOVIE, + MOVIE_PLAY_MOVIE, + MOVIE_END, + MOVIE_TOTAL_COMMANDS + }; + enum { + NUM_ACTORS_IN_MOVIE = 5, + NUM_VEHICLES_IN_MOVIE = 5, + NUM_COMMANDS_IN_MOVIE = 20 + }; + static int32 m_bCameraFollowActor; + static CVector m_vecCurrentPosition; + static CVector m_vecCamHeading; + static CVector m_vecGotoPosition; + static int32 m_nVehicle; + static int32 m_nVehicle2; + static int32 m_nActor; + static int32 m_nActor2; + static int32 m_nVehiclemodelId; + static int32 m_nPedmodelId; + static int16 m_nCurrentMovieCommand; + static int16 m_nCurrentCommand; + static int16 m_nCurrentVehicle; + static int16 m_nCurrentActor; + static bool m_bEditOn; + static bool m_bRecording; + static bool m_bCommandActive; + static bool m_bActorSelected; + static bool m_bActor2Selected; + static bool m_bVehicleSelected; + static int16 m_nNumActors; + static int16 m_nNumVehicles; + static int16 m_nNumMovieCommands; + static int16 m_nWeaponType; + static CPed* pActors[NUM_ACTORS_IN_MOVIE]; + static CVehicle* pVehicles[NUM_VEHICLES_IN_MOVIE]; + static bool m_bDrawGotoArrow; + static CMovieCommand Movie[NUM_COMMANDS_IN_MOVIE]; + + static void LoadMovie(void); + static void SaveMovie(void); + static void Initialise(void); + static void InitPlayback(void); + static void ReInitialise(void); + static void Update(void); + static void Draw(void); + static void ProcessCommand(void); + static void PlayBack(void); + static void ClearForNewCommand(void); + static void SelectActor(void); + static void SelectActor2(void); + static void SelectVehicle(void); + static bool SelectWeapon(void); +}; +#endif diff --git a/src/miami/control/Script.cpp b/src/miami/control/Script.cpp new file mode 100644 index 00000000..a9edbf30 --- /dev/null +++ b/src/miami/control/Script.cpp @@ -0,0 +1,3265 @@ +#include "common.h" + +#include "Script.h" +#include "ScriptCommands.h" + +#include "AnimBlendAssociation.h" +#include "AudioManager.h" +#include "Boat.h" +#include "Camera.h" +#include "CarCtrl.h" +#include "CivilianPed.h" +#include "Clock.h" +#include "CopPed.h" +#include "Debug.h" +#include "DMAudio.h" +#include "EmergencyPed.h" +#include "FileMgr.h" +#include "Frontend.h" +#include "General.h" +#ifdef MISSION_REPLAY +#include "GenericGameStorage.h" +#endif +#include "HandlingMgr.h" +#include "Heli.h" +#include "Hud.h" +#include "Lines.h" +#include "Messages.h" +#include "Pad.h" +#include "Pickups.h" +#include "Pools.h" +#include "Population.h" +#include "Remote.h" +#include "Replay.h" +#include "Stats.h" +#include "Streaming.h" +#include "User.h" +#include "Wanted.h" +#include "Weather.h" +#include "Zones.h" +#include "main.h" +#include "Ropes.h" +#include "ColStore.h" +#include "Fluff.h" +#include "GameLogic.h" +#include "MBlur.h" +#include "PedRoutes.h" +#include "RoadBlocks.h" +#include "SpecialFX.h" +#include "Timecycle.h" +#include "TxdStore.h" +#include "Bike.h" +#ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT +#include +#endif + +uint8 CTheScripts::ScriptSpace[SIZE_SCRIPT_SPACE]; +CRunningScript CTheScripts::ScriptsArray[MAX_NUM_SCRIPTS]; +intro_text_line CTheScripts::IntroTextLines[MAX_NUM_INTRO_TEXT_LINES]; +intro_script_rectangle CTheScripts::IntroRectangles[MAX_NUM_INTRO_RECTANGLES]; +CSprite2d CTheScripts::ScriptSprites[MAX_NUM_SCRIPT_SRPITES]; +script_sphere_struct CTheScripts::ScriptSphereArray[MAX_NUM_SCRIPT_SPHERES]; +tUsedObject CTheScripts::UsedObjectArray[MAX_NUM_USED_OBJECTS]; +int32 CTheScripts::MultiScriptArray[MAX_NUM_MISSION_SCRIPTS]; +tBuildingSwap CTheScripts::BuildingSwapArray[MAX_NUM_BUILDING_SWAPS]; +CEntity* CTheScripts::InvisibilitySettingArray[MAX_NUM_INVISIBILITY_SETTINGS]; +CStoredLine CTheScripts::aStoredLines[MAX_NUM_STORED_LINES]; +bool CTheScripts::DbgFlag; +uint32 CTheScripts::OnAMissionFlag; +int32 CTheScripts::StoreVehicleIndex; +bool CTheScripts::StoreVehicleWasRandom; +CRunningScript *CTheScripts::pIdleScripts; +CRunningScript *CTheScripts::pActiveScripts; +int32 CTheScripts::NextFreeCollectiveIndex; +int32 CTheScripts::LastRandomPedId; +uint16 CTheScripts::NumberOfUsedObjects; +bool CTheScripts::bAlreadyRunningAMissionScript; +bool CTheScripts::bUsingAMultiScriptFile; +uint16 CTheScripts::NumberOfMissionScripts; +uint32 CTheScripts::LargestMissionScriptSize; +uint32 CTheScripts::MainScriptSize; +uint8 CTheScripts::FailCurrentMission; +uint16 CTheScripts::NumScriptDebugLines; +uint16 CTheScripts::NumberOfIntroRectanglesThisFrame; +uint16 CTheScripts::NumberOfIntroTextLinesThisFrame; +uint8 CTheScripts::UseTextCommands; +CMissionCleanup CTheScripts::MissionCleanUp; +CUpsideDownCarCheck CTheScripts::UpsideDownCars; +CStuckCarCheck CTheScripts::StuckCars; +uint16 CTheScripts::CommandsExecuted; +uint16 CTheScripts::ScriptsUpdated; +int32 ScriptParams[32]; +uint8 CTheScripts::RiotIntensity; +uint32 CTheScripts::LastMissionPassedTime; +uint16 CTheScripts::NumberOfExclusiveMissionScripts; +bool CTheScripts::bPlayerHasMetDebbieHarry; +bool CTheScripts::bPlayerIsInTheStatium; +#if (defined GTA_PC && !defined GTAVC_JP_PATCH || defined GTA_XBOX || defined SUPPORT_XBOX_SCRIPT || defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) +int16 CTheScripts::CardStack[CARDS_IN_DECK * MAX_DECKS]; +int16 CTheScripts::CardStackPosition; +#endif + +#ifdef MISSION_REPLAY + +static const char* nonMissionScripts[] = { + "copcar", + "ambulan", + "taxi", + "firetru", + "rampage", + "t4x4_1", + "t4x4_2", + "t4x4_3", + "rc1", + "rc2", + "rc3", + "rc4", + "hj", + "usj", + "mayhem", + "range", + "race", + "pizza", + "rcheli", + "rcplne1", + "rcrace1", + "cokerun", + "buypro1", + "carbuy1", + "buypro2", + "icecut", + "icecre1", + "buypro3", + "buypro4", + "buypro5", + "buypro6", + "buypro7", + "buypro8", + "buypro9", + "buypro10", + "buypro11", + "ovalrng", + "mm", + "kickst", + "heli1sc", + "heli2sc", + "heli3sc", + "heli4sc", + "carpark_1", + "bmx_1", + "bmx_2" +}; + +static const char* MissionScripts[] = { + "LAWYER1", + "LAWYER2", + "LAWYER3", + "LAWYER4", + "GENERL1", + "COL2", + "GENERL3", + "COL_4", + "COL_5", + "baron1", + "baron2", + "baron3", + "baron4", + "kent1", + "baron5", + "serg1", + "serg2", + "serg3", + "bankjo1", + "bankjo2", + "bankjo3", + "bankjo4", + "phil1", + "phil2", + "porno1", + "porno2", + "porno3", + "porno4", + "protec1", + "protec2", + "protec3", + "count1", + "count2", + "CAP_1", + "FIN_1", + "bike1", + "bike2", + "bike3", + "rockb1", + "rockb2", + "rockb3", + "cuban1", + "cuban2", + "cuban3", + "cuban4", + "hait1", + "hait2", + "hait3", + "assin1", + "assin2", + "assin3", + "assin4", + "assin5", + "taxwar1", + "taxwar2", + "taxwar3" +}; + +int AllowMissionReplay; +uint32 NextMissionDelay; +uint32 MissionStartTime; +uint32 WaitForMissionActivate; +uint32 WaitForSave; +float oldTargetX; +float oldTargetY; +int missionRetryScriptIndex; +bool doingMissionRetry; +bool gbTryingPorn4Again; +int IsInAmmunation; +int MissionSkipLevel; + +#ifdef USE_MISSION_REPLAY_OVERRIDE_FOR_NON_MOBILE_SCRIPT +bool UsingMobileScript; +bool AlreadySavedGame; +#endif + +#endif + +const uint32 CRunningScript::nSaveStructSize = +#ifdef COMPATIBLE_SAVES + 136; +#else + sizeof(CRunningScript); +#endif + +CMissionCleanup::CMissionCleanup() +{ + Init(); +} + +void CMissionCleanup::Init() +{ + m_nCount = 0; + for (int i = 0; i < MAX_CLEANUP; i++){ + m_sEntities[i].type = CLEANUP_UNUSED; + m_sEntities[i].id = 0; + } +} + +cleanup_entity_struct* CMissionCleanup::FindFree() +{ + for (int i = 0; i < MAX_CLEANUP; i++){ + if (m_sEntities[i].type == CLEANUP_UNUSED) + return &m_sEntities[i]; + } + script_assert(0); + return nil; +} + +void CMissionCleanup::AddEntityToList(int32 id, uint8 type) +{ + cleanup_entity_struct* pNew = FindFree(); + if (!pNew) + return; + pNew->id = id; + pNew->type = type; + m_nCount++; +} + +void CMissionCleanup::RemoveEntityFromList(int32 id, uint8 type) +{ + for (int i = 0; i < MAX_CLEANUP; i++){ + if (m_sEntities[i].type == type && m_sEntities[i].id == id){ + switch (m_sEntities[i].type) { + case CLEANUP_CAR: + { + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(m_sEntities[i].id); + if (pVehicle) { + if (pVehicle->bIsStaticWaitingForCollision) { + pVehicle->bIsStaticWaitingForCollision = false; + if (!pVehicle->GetIsStatic()) + pVehicle->AddToMovingList(); + } + } + break; + } + case CLEANUP_CHAR: + { + CPed* pPed = CPools::GetPedPool()->GetAt(m_sEntities[i].id); + if (pPed) { + if (pPed->bIsStaticWaitingForCollision) { + pPed->bIsStaticWaitingForCollision = false; + if (!pPed->GetIsStatic()) + pPed->AddToMovingList(); + } + } + break; + } + case CLEANUP_OBJECT: + { + CObject* pObject = CPools::GetObjectPool()->GetAt(m_sEntities[i].id); + if (pObject) { + if (pObject->bIsStaticWaitingForCollision) { + pObject->bIsStaticWaitingForCollision = false; + if (!pObject->GetIsStatic()) + pObject->AddToMovingList(); + } + } + break; + } + default: + break; + } + m_sEntities[i].id = 0; + m_sEntities[i].type = CLEANUP_UNUSED; + m_nCount--; + } + } +} + +void CMissionCleanup::CheckIfCollisionHasLoadedForMissionObjects() +{ + for (int i = 0; i < MAX_CLEANUP; i++) { + switch (m_sEntities[i].type) { + case CLEANUP_CAR: + { + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(m_sEntities[i].id); + if (pVehicle) { + if (pVehicle->bIsStaticWaitingForCollision) { + if (CColStore::HasCollisionLoaded(pVehicle->GetPosition())) { + pVehicle->bIsStaticWaitingForCollision = false; + if (!pVehicle->GetIsStatic()) + pVehicle->AddToMovingList(); + } + } + } + break; + } + case CLEANUP_CHAR: + { + CPed* pPed = CPools::GetPedPool()->GetAt(m_sEntities[i].id); + if (pPed) { + if (pPed->bIsStaticWaitingForCollision) { + if (CColStore::HasCollisionLoaded(pPed->GetPosition())) { + pPed->bIsStaticWaitingForCollision = false; + if (!pPed->GetIsStatic()) + pPed->AddToMovingList(); + } + } + } + break; + } + case CLEANUP_OBJECT: + { + CObject* pObject = CPools::GetObjectPool()->GetAt(m_sEntities[i].id); + if (pObject) { + if (pObject->bIsStaticWaitingForCollision) { + if (CColStore::HasCollisionLoaded(pObject->GetPosition())) { + pObject->bIsStaticWaitingForCollision = false; + if (!pObject->GetIsStatic()) + pObject->AddToMovingList(); + } + } + } + break; + } + default: + break; + } + } +} + +void CMissionCleanup::Process() +{ + CPopulation::m_AllRandomPedsThisType = -1; + CPopulation::PedDensityMultiplier = 1.0f; + CCarCtrl::CarDensityMultiplier = 1.0f; + CPed::nThreatReactionRangeMultiplier = 1; + CPed::nEnterCarRangeMultiplier = 1; + FindPlayerPed()->m_pWanted->m_fCrimeSensitivity = 1.0f; + CRoadBlocks::ClearScriptRoadBlocks(); + CRouteNode::Initialise(); + if (!CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle) + TheCamera.Restore(); + TheCamera.SetWideScreenOff(); + CSpecialFX::bLiftCam = false; + CSpecialFX::bVideoCam = false; + CTimeCycle::StopExtraColour(0); + for (int i = 0; i < MISSION_AUDIO_SLOTS; i++) + DMAudio.ClearMissionAudio(i); + CWeather::ReleaseWeather(); + for (int i = 0; i < NUM_OF_SPECIAL_CHARS; i++) + CStreaming::SetMissionDoesntRequireSpecialChar(i); + for (int i = 0; i < NUM_OF_CUTSCENE_OBJECTS; i++) + CStreaming::SetMissionDoesntRequireModel(MI_CUTOBJ01 + i); + CStreaming::ms_disableStreaming = false; + CHud::m_ItemToFlash = -1; + CHud::SetHelpMessage(nil, false); + CUserDisplay::OnscnTimer.m_bDisabled = false; + CTheScripts::RemoveScriptTextureDictionary(); + CWorld::Players[0].m_pPed->m_pWanted->m_bIgnoredByCops = false; + CWorld::Players[0].m_pPed->m_pWanted->m_bIgnoredByEveryone = false; + CWorld::Players[0].MakePlayerSafe(false); + CWorld::Players[0].m_pPed->m_nFadeDrunkenness = 1; + CWorld::Players[0].m_pPed->m_nDrunkCountdown = 0; + CPad::GetPad(0)->SetDrunkInputDelay(0); + CWorld::Players[0].m_bDriveByAllowed = true; + DMAudio.ShutUpPlayerTalking(FALSE); + CVehicle::bDisableRemoteDetonation = false; + CVehicle::bDisableRemoteDetonationOnContact = false; + CGameLogic::ClearShortCut(); + CTheScripts::RiotIntensity = 0; + CTheScripts::StoreVehicleIndex = -1; + CTheScripts::StoreVehicleWasRandom = true; + CTheScripts::UpsideDownCars.Init(); + CTheScripts::StuckCars.Init(); + for (int i = 0; i < MAX_CLEANUP; i++){ + if (m_sEntities[i].type == CLEANUP_UNUSED) + continue; + switch (m_sEntities[i].type) { + case CLEANUP_CAR: + { + CVehicle* v = CPools::GetVehiclePool()->GetAt(m_sEntities[i].id); + if (v) + CTheScripts::CleanUpThisVehicle(v); + break; + } + case CLEANUP_CHAR: + { + CPed* p = CPools::GetPedPool()->GetAt(m_sEntities[i].id); + if (p) + CTheScripts::CleanUpThisPed(p); + break; + } + case CLEANUP_OBJECT: + { + CObject* o = CPools::GetObjectPool()->GetAt(m_sEntities[i].id); + if (o) + CTheScripts::CleanUpThisObject(o); + break; + } + default: + break; + } + RemoveEntityFromList(m_sEntities[i].id, m_sEntities[i].type); + } +#ifdef SECUROM + if ((myrand() & 3) == 2){ + // if pirated game + CWeather::ForceHurricaneWeather(); + } +#endif +} + +/* NB: CUpsideDownCarCheck is not used by actual script at all + * It has a weird usage: AreAnyCarsUpsideDown would fail any mission + * just like death or arrest. */ + +void CUpsideDownCarCheck::Init() +{ + for (int i = 0; i < MAX_UPSIDEDOWN_CAR_CHECKS; i++){ + m_sCars[i].m_nVehicleIndex = -1; + m_sCars[i].m_nUpsideDownTimer = 0; + } +} + +bool CUpsideDownCarCheck::IsCarUpsideDown(int32 id) +{ + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(id); + return IsCarUpsideDown(pVehicle); +} + +bool CUpsideDownCarCheck::IsCarUpsideDown(CVehicle* pVehicle) +{ + assert(pVehicle); + return pVehicle->GetUp().z <= UPSIDEDOWN_UP_THRESHOLD && + pVehicle->GetMoveSpeed().Magnitude() < UPSIDEDOWN_MOVE_SPEED_THRESHOLD && + pVehicle->GetTurnSpeed().Magnitude() < UPSIDEDOWN_TURN_SPEED_THRESHOLD; +} + +void CUpsideDownCarCheck::UpdateTimers() +{ + uint32 timeStep = CTimer::GetTimeStepInMilliseconds(); + for (int i = 0; i < MAX_UPSIDEDOWN_CAR_CHECKS; i++){ + CVehicle* v = CPools::GetVehiclePool()->GetAt(m_sCars[i].m_nVehicleIndex); + if (v){ + if (IsCarUpsideDown(m_sCars[i].m_nVehicleIndex)) + m_sCars[i].m_nUpsideDownTimer += timeStep; + else + m_sCars[i].m_nUpsideDownTimer = 0; + }else{ + m_sCars[i].m_nVehicleIndex = -1; + m_sCars[i].m_nUpsideDownTimer = 0; + } + } +} + +bool CUpsideDownCarCheck::AreAnyCarsUpsideDown() +{ + for (int i = 0; i < MAX_UPSIDEDOWN_CAR_CHECKS; i++){ + if (m_sCars[i].m_nVehicleIndex >= 0 && m_sCars[i].m_nUpsideDownTimer > UPSIDEDOWN_TIMER_THRESHOLD) + return true; + } + return false; +} + +void CUpsideDownCarCheck::AddCarToCheck(int32 id) +{ + uint16 index = 0; + while (index < MAX_UPSIDEDOWN_CAR_CHECKS && m_sCars[index].m_nVehicleIndex >= 0) + index++; +#ifdef FIX_BUGS + if (index >= MAX_UPSIDEDOWN_CAR_CHECKS) + return; +#endif + m_sCars[index].m_nVehicleIndex = id; + m_sCars[index].m_nUpsideDownTimer = 0; +} + +void CUpsideDownCarCheck::RemoveCarFromCheck(int32 id) +{ + for (int i = 0; i < MAX_UPSIDEDOWN_CAR_CHECKS; i++){ + if (m_sCars[i].m_nVehicleIndex == id){ + m_sCars[i].m_nVehicleIndex = -1; + m_sCars[i].m_nUpsideDownTimer = 0; + } + } +} + +bool CUpsideDownCarCheck::HasCarBeenUpsideDownForAWhile(int32 id) +{ + for (int i = 0; i < MAX_UPSIDEDOWN_CAR_CHECKS; i++){ + if (m_sCars[i].m_nVehicleIndex == id) + return m_sCars[i].m_nUpsideDownTimer > UPSIDEDOWN_TIMER_THRESHOLD; + } + return false; +} + +void stuck_car_data::Reset() +{ + m_nVehicleIndex = -1; + m_vecPos = CVector(-5000.0f, -5000.0f, -5000.0f); + m_nLastCheck = -1; + m_fRadius = 0.0f; + m_nStuckTime = 0; + m_bStuck = false; +} + +void CStuckCarCheck::Init() +{ + for (int i = 0; i < MAX_STUCK_CAR_CHECKS; i++) { + m_sCars[i].Reset(); + } +} + +void CStuckCarCheck::Process() +{ + uint32 timer = CTimer::GetTimeInMilliseconds(); + for (int i = 0; i < MAX_STUCK_CAR_CHECKS; i++){ + if (m_sCars[i].m_nVehicleIndex < 0) + continue; + if (timer <= m_sCars[i].m_nStuckTime + m_sCars[i].m_nLastCheck) + continue; + CVehicle* pv = CPools::GetVehiclePool()->GetAt(m_sCars[i].m_nVehicleIndex); + if (!pv){ + m_sCars[i].Reset(); + continue; + } + float distance = (pv->GetPosition() - m_sCars[i].m_vecPos).Magnitude(); + m_sCars[i].m_bStuck = distance < m_sCars[i].m_fRadius; + m_sCars[i].m_vecPos = pv->GetPosition(); + m_sCars[i].m_nLastCheck = timer; + } +} + +void CStuckCarCheck::AddCarToCheck(int32 id, float radius, uint32 time) +{ + CVehicle* pv = CPools::GetVehiclePool()->GetAt(id); + if (!pv) + return; + int index = 0; + while (index < MAX_STUCK_CAR_CHECKS && m_sCars[index].m_nVehicleIndex >= 0) + index++; +#ifdef FIX_BUGS + if (index >= MAX_STUCK_CAR_CHECKS) + return; +#endif + m_sCars[index].m_nVehicleIndex = id; + m_sCars[index].m_vecPos = pv->GetPosition(); + m_sCars[index].m_nLastCheck = CTimer::GetTimeInMilliseconds(); + m_sCars[index].m_fRadius = radius; + m_sCars[index].m_nStuckTime = time; + m_sCars[index].m_bStuck = false; +} + +void CStuckCarCheck::RemoveCarFromCheck(int32 id) +{ + for (int i = 0; i < MAX_STUCK_CAR_CHECKS; i++){ + if (m_sCars[i].m_nVehicleIndex == id){ + m_sCars[i].Reset(); + } + } +} + +bool CStuckCarCheck::HasCarBeenStuckForAWhile(int32 id) +{ + for (int i = 0; i < MAX_STUCK_CAR_CHECKS; i++){ + if (m_sCars[i].m_nVehicleIndex == id) + return m_sCars[i].m_bStuck; + } + return false; +} + +void CRunningScript::CollectParameters(uint32* pIp, int16 total) +{ + for (int16 i = 0; i < total; i++){ + uint16 varIndex; + switch (CTheScripts::Read1ByteFromScript(pIp)) + { + case ARGUMENT_INT32: + case ARGUMENT_FLOAT: + ScriptParams[i] = CTheScripts::Read4BytesFromScript(pIp); + break; + case ARGUMENT_GLOBALVAR: + varIndex = CTheScripts::Read2BytesFromScript(pIp); + script_assert(varIndex >= 8 && varIndex < CTheScripts::GetSizeOfVariableSpace()); + ScriptParams[i] = *((int32*)&CTheScripts::ScriptSpace[varIndex]); + break; + case ARGUMENT_LOCALVAR: + varIndex = CTheScripts::Read2BytesFromScript(pIp); + script_assert(varIndex >= 0 && varIndex < ARRAY_SIZE(m_anLocalVariables)); + ScriptParams[i] = m_anLocalVariables[varIndex]; + break; + case ARGUMENT_INT8: + ScriptParams[i] = CTheScripts::Read1ByteFromScript(pIp); + break; + case ARGUMENT_INT16: + ScriptParams[i] = CTheScripts::Read2BytesFromScript(pIp); + break; + default: + script_assert(0); + break; + } + } +} + +int32 CRunningScript::CollectNextParameterWithoutIncreasingPC(uint32 ip) +{ + uint32* pIp = &ip; + switch (CTheScripts::Read1ByteFromScript(pIp)) + { + case ARGUMENT_INT32: + return CTheScripts::Read4BytesFromScript(pIp); + case ARGUMENT_GLOBALVAR: + return *((int32*)&CTheScripts::ScriptSpace[(uint16)CTheScripts::Read2BytesFromScript(pIp)]); + case ARGUMENT_LOCALVAR: + return m_anLocalVariables[CTheScripts::Read2BytesFromScript(pIp)]; + case ARGUMENT_INT8: + return CTheScripts::Read1ByteFromScript(pIp); + case ARGUMENT_INT16: + return CTheScripts::Read2BytesFromScript(pIp); + case ARGUMENT_FLOAT: + return CTheScripts::Read4BytesFromScript(pIp); + default: + script_assert(0); + } + return -1; +} + +void CRunningScript::StoreParameters(uint32* pIp, int16 number) +{ + for (int16 i = 0; i < number; i++){ + switch (CTheScripts::Read1ByteFromScript(pIp)) { + case ARGUMENT_GLOBALVAR: + *(int32*)&CTheScripts::ScriptSpace[(uint16)CTheScripts::Read2BytesFromScript(pIp)] = ScriptParams[i]; + break; + case ARGUMENT_LOCALVAR: + m_anLocalVariables[CTheScripts::Read2BytesFromScript(pIp)] = ScriptParams[i]; + break; + default: + script_assert(0); + } + } +} + +int32 *CRunningScript::GetPointerToScriptVariable(uint32* pIp, int16 type) +{ + switch (CTheScripts::Read1ByteFromScript(pIp)) + { + case ARGUMENT_GLOBALVAR: + script_assert(type == VAR_GLOBAL); + return (int32*)&CTheScripts::ScriptSpace[(uint16)CTheScripts::Read2BytesFromScript(pIp)]; + case ARGUMENT_LOCALVAR: + script_assert(type == VAR_LOCAL); + return &m_anLocalVariables[CTheScripts::Read2BytesFromScript(pIp)]; + default: + script_assert(0); + } + return nil; +} + +void CRunningScript::Init() +{ + strcpy(m_abScriptName, "noname"); + next = prev = nil; + SetIP(0); + for (int i = 0; i < MAX_STACK_DEPTH; i++) + m_anStack[i] = 0; + m_nStackPointer = 0; + m_nWakeTime = 0; + m_bIsActive = false; + m_bCondResult = false; + m_bIsMissionScript = false; + m_bSkipWakeTime = false; + for (int i = 0; i < NUM_LOCAL_VARS + NUM_TIMERS; i++) + m_anLocalVariables[i] = 0; + m_nAndOrState = 0; + m_bNotFlag = false; + m_bDeatharrestEnabled = true; + m_bDeatharrestExecuted = false; + m_bMissionFlag = false; +} + +#ifdef USE_DEBUG_SCRIPT_LOADER +int CTheScripts::ScriptToLoad = 0; + +int CTheScripts::OpenScript() +{ + CFileMgr::ChangeDir("\\"); + switch (ScriptToLoad) { + case 0: return CFileMgr::OpenFile("data\\main.scm", "rb"); + case 1: return CFileMgr::OpenFile("data\\freeroam_miami.scm", "rb"); + case 2: return CFileMgr::OpenFile("data\\main_d.scm", "rb"); + } + return CFileMgr::OpenFile("data\\main.scm", "rb"); +} +#endif + +void CTheScripts::Init() +{ + for (int i = 0; i < SIZE_SCRIPT_SPACE; i++) + ScriptSpace[i] = 0; + pActiveScripts = pIdleScripts = nil; + for (int i = 0; i < MAX_NUM_SCRIPTS; i++){ + ScriptsArray[i].Init(); + ScriptsArray[i].AddScriptToList(&pIdleScripts); + } + MissionCleanUp.Init(); + UpsideDownCars.Init(); + StuckCars.Init(); +#ifdef USE_DEBUG_SCRIPT_LOADER + // glfwGetKey doesn't work because of CGame::Initialise is blocking + CPad::UpdatePads(); + if(CPad::GetPad(0)->GetChar('G')) ScriptToLoad = 0; + if(CPad::GetPad(0)->GetChar('R')) ScriptToLoad = 1; + if(CPad::GetPad(0)->GetChar('D')) ScriptToLoad = 2; + + int mainf = OpenScript(); +#else + CFileMgr::SetDir("data"); + int mainf = CFileMgr::OpenFile("main.scm", "rb"); +#endif + CFileMgr::Read(mainf, (char*)ScriptSpace, SIZE_MAIN_SCRIPT); + CFileMgr::CloseFile(mainf); + CFileMgr::SetDir(""); + StoreVehicleIndex = -1; + StoreVehicleWasRandom = true; + OnAMissionFlag = 0; + LastMissionPassedTime = (uint32)-1; + LastRandomPedId = -1; + for (int i = 0; i < MAX_NUM_USED_OBJECTS; i++){ + memset(&UsedObjectArray[i].name, 0, sizeof(UsedObjectArray[i].name)); + UsedObjectArray[i].index = 0; + } + NumberOfUsedObjects = 0; + ReadObjectNamesFromScript(); + UpdateObjectIndices(); + bAlreadyRunningAMissionScript = false; + bUsingAMultiScriptFile = true; + for (int i = 0; i < MAX_NUM_MISSION_SCRIPTS; i++) + MultiScriptArray[i] = 0; + NumberOfExclusiveMissionScripts = 0; + NumberOfMissionScripts = 0; + LargestMissionScriptSize = 0; + MainScriptSize = 0; + ReadMultiScriptFileOffsetsFromScript(); + FailCurrentMission = 0; + DbgFlag = false; + NumScriptDebugLines = 0; + RiotIntensity = 0; + bPlayerHasMetDebbieHarry = false; + bPlayerIsInTheStatium = false; + for (int i = 0; i < MAX_NUM_SCRIPT_SPHERES; i++){ + ScriptSphereArray[i].m_bInUse = false; + ScriptSphereArray[i].m_Index = 1; + ScriptSphereArray[i].m_Id = 0; + ScriptSphereArray[i].m_vecCenter = CVector(0.0f, 0.0f, 0.0f); + ScriptSphereArray[i].m_fRadius = 0.0f; + } + for (int i = 0; i < MAX_NUM_INTRO_TEXT_LINES; i++){ + IntroTextLines[i].Reset(); + } + NumberOfIntroTextLinesThisFrame = 0; + UseTextCommands = 0; + for (int i = 0; i < MAX_NUM_INTRO_RECTANGLES; i++){ + IntroRectangles[i].m_bIsUsed = false; + IntroRectangles[i].m_bBeforeFade = false; + IntroRectangles[i].m_nTextureId = -1; + IntroRectangles[i].m_sRect = CRect(0.0f, 0.0f, 0.0f, 0.0f); + IntroRectangles[i].m_sColor = CRGBA(255, 255, 255, 255); + } + NumberOfIntroRectanglesThisFrame = 0; + RemoveScriptTextureDictionary(); + for (int i = 0; i < MAX_NUM_BUILDING_SWAPS; i++){ + BuildingSwapArray[i].m_pBuilding = nil; + BuildingSwapArray[i].m_nNewModel = -1; + BuildingSwapArray[i].m_nOldModel = -1; + } + for (int i = 0; i < MAX_NUM_INVISIBILITY_SETTINGS; i++) + InvisibilitySettingArray[i] = nil; + +#ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT + LogAfterScriptInitializing(); +#endif +#ifdef USE_MISSION_REPLAY_OVERRIDE_FOR_NON_MOBILE_SCRIPT + UsingMobileScript = false; + AlreadySavedGame = false; +#endif +} + +void CTheScripts::RemoveScriptTextureDictionary() +{ + for (int i = 0; i < ARRAY_SIZE(CTheScripts::ScriptSprites); i++) + CTheScripts::ScriptSprites[i].Delete(); + int slot = CTxdStore::FindTxdSlot("script"); + if (slot != -1) + CTxdStore::RemoveTxd(slot); +} + +void CRunningScript::RemoveScriptFromList(CRunningScript** ppScript) +{ + if (prev) + prev->next = next; + else + *ppScript = next; + if (next) + next->prev = prev; +} + +void CRunningScript::AddScriptToList(CRunningScript** ppScript) +{ + next = *ppScript; + prev = nil; + if (*ppScript) + (*ppScript)->prev = this; + *ppScript = this; +} + +CRunningScript* CTheScripts::StartNewScript(uint32 ip) +{ + CRunningScript* pNew = pIdleScripts; + script_assert(pNew); + pNew->RemoveScriptFromList(&pIdleScripts); + pNew->Init(); + pNew->SetIP(ip); + pNew->AddScriptToList(&pActiveScripts); + pNew->m_bIsActive = true; + return pNew; +} + +void CTheScripts::Process() +{ + if (CReplay::IsPlayingBack()) + return; + CommandsExecuted = 0; + ScriptsUpdated = 0; + float timeStep = CTimer::GetTimeStepInMilliseconds(); + UpsideDownCars.UpdateTimers(); + StuckCars.Process(); + MissionCleanUp.CheckIfCollisionHasLoadedForMissionObjects(); + DrawScriptSpheres(); + if (FailCurrentMission) + --FailCurrentMission; + if (UseTextCommands){ + for (int i = 0; i < MAX_NUM_INTRO_TEXT_LINES; i++) + IntroTextLines[i].Reset(); + NumberOfIntroTextLinesThisFrame = 0; + for (int i = 0; i < MAX_NUM_INTRO_RECTANGLES; i++){ + IntroRectangles[i].m_bIsUsed = false; + IntroRectangles[i].m_bBeforeFade = false; + } + NumberOfIntroRectanglesThisFrame = 0; + if (UseTextCommands == 1) + UseTextCommands = 0; + } + +#ifdef MISSION_REPLAY + static uint32 TimeToWaitTill; + static bool AlreadyResetHealth; + switch (AllowMissionReplay) { + case MISSION_RETRY_STAGE_START_PROCESSING: + AllowMissionReplay = MISSION_RETRY_STAGE_WAIT_FOR_DELAY; + TimeToWaitTill = CTimer::GetTimeInMilliseconds() + (AddExtraDeathDelay() > 1000 ? 4000 : 2500); + break; + case MISSION_RETRY_STAGE_WAIT_FOR_DELAY: + if (TimeToWaitTill < CTimer::GetTimeInMilliseconds()) + AllowMissionReplay = MISSION_RETRY_STAGE_WAIT_FOR_MENU; + break; + case MISSION_RETRY_STAGE_WAIT_FOR_MENU: + AllowMissionReplay = MISSION_RETRY_STAGE_WAIT_FOR_USER; + RetryMission(MISSION_RETRY_TYPE_SUGGEST_TO_PLAYER); + break; + case MISSION_RETRY_STAGE_START_RESTARTING: + AllowMissionReplay = MISSION_RETRY_STAGE_WAIT_FOR_TIMER_AFTER_RESTART; + AlreadyResetHealth = false; + TimeToWaitTill = CTimer::GetTimeInMilliseconds() + 500; + break; + case MISSION_RETRY_STAGE_WAIT_FOR_TIMER_AFTER_RESTART: + if (!AlreadyResetHealth) { + AlreadyResetHealth = true; + CPlayerPed* pPlayerPed = FindPlayerPed(); + if (pPlayerPed) { + CPlayerInfo* pPlayerInfo = pPlayerPed->GetPlayerInfoForThisPlayerPed(); + if (pPlayerInfo) + pPlayerPed->m_fHealth = pPlayerInfo->m_nMaxHealth; + } + } + if (TimeToWaitTill < CTimer::GetTimeInMilliseconds()) { + AllowMissionReplay = MISSION_RETRY_STAGE_NORMAL; + return; + } + break; + } + if (WaitForMissionActivate) { + if (WaitForMissionActivate > CTimer::GetTimeInMilliseconds()) + return; + WaitForMissionActivate = 0; + WaitForSave = CTimer::GetTimeInMilliseconds() + 3000; + } + if (WaitForSave && WaitForSave > CTimer::GetTimeInMilliseconds()) + WaitForSave = 0; +#endif + +#ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT + LogBeforeScriptProcessing(); +#endif + + CRunningScript* script = pActiveScripts; + while (script != nil){ + CRunningScript* next = script->GetNext(); + ++ScriptsUpdated; + script->UpdateTimers(timeStep); + script->Process(); + script = next; + if (script && !script->m_bIsActive) + script = nil; + } + DbgFlag = false; + +#ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT + LogAfterScriptProcessing(); +#endif +} + +CRunningScript* CTheScripts::StartTestScript() +{ + return StartNewScript(0); +} + +bool CTheScripts::IsPlayerOnAMission() +{ + return OnAMissionFlag && *(int32*)&ScriptSpace[OnAMissionFlag] == 1; +} + +void CRunningScript::Process() +{ +#ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT + LogOnStartProcessing(); +#endif + if (m_bIsMissionScript) + DoDeatharrestCheck(); + if (m_bMissionFlag && CTheScripts::FailCurrentMission == 1 && m_nStackPointer == 1) + SetIP(m_anStack[--m_nStackPointer]); + if (CTimer::GetTimeInMilliseconds() >= m_nWakeTime){ + while (!ProcessOneCommand()) + ; + return; + } + if (!m_bSkipWakeTime) + return; + if (!CPad::GetPad(0)->GetCrossJustDown()) + return; + m_nWakeTime = 0; + for (int i = 0; i < NUMBIGMESSAGES; i++){ + if (CMessages::BIGMessages[i].m_Stack[0].m_pText != nil) + CMessages::BIGMessages[i].m_Stack[0].m_nStartTime = 0; + } + if (CMessages::BriefMessages[0].m_pText != nil) + CMessages::BriefMessages[0].m_nStartTime = 0; +} + +int8 CRunningScript::ProcessOneCommand() +{ + int8 retval = -1; + ++CTheScripts::CommandsExecuted; + int32 command = (uint16)CTheScripts::Read2BytesFromScript(&m_nIp); + m_bNotFlag = (command & 0x8000); + command &= 0x7FFF; +#ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT + LogBeforeProcessingCommand(command); +#endif + if (command < 100) + retval = ProcessCommands0To99(command); + else if (command < 200) + retval = ProcessCommands100To199(command); + else if (command < 300) + retval = ProcessCommands200To299(command); + else if (command < 400) + retval = ProcessCommands300To399(command); + else if (command < 500) + retval = ProcessCommands400To499(command); + else if (command < 600) + retval = ProcessCommands500To599(command); + else if (command < 700) + retval = ProcessCommands600To699(command); + else if (command < 800) + retval = ProcessCommands700To799(command); + else if (command < 900) + retval = ProcessCommands800To899(command); + else if (command < 1000) + retval = ProcessCommands900To999(command); + else if (command < 1100) + retval = ProcessCommands1000To1099(command); + else if (command < 1200) + retval = ProcessCommands1100To1199(command); + else if (command < 1300) + retval = ProcessCommands1200To1299(command); + else if (command < 1400) + retval = ProcessCommands1300To1399(command); + else if (command < 1500) + retval = ProcessCommands1400To1499(command); +#ifdef USE_MISSION_REPLAY_OVERRIDE_FOR_NON_MOBILE_SCRIPT + if (!AlreadySavedGame) // we need to ignore first "fake" command which actually just saves the game +#endif + { +#ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT + LogAfterProcessingCommand(command); +#elif defined USE_BASIC_SCRIPT_DEBUG_OUTPUT + if (m_bMissionFlag) { + char tmp[128]; + sprintf(tmp, "Comm %d Cmp %d", command, m_bCondResult); + CDebug::DebugAddText(tmp); + } +#endif + } + return retval; +} + +int8 CRunningScript::ProcessCommands0To99(int32 command) +{ + float *fScriptVar1; + int32 *nScriptVar1; + switch (command) { + case COMMAND_NOP: + return 0; + case COMMAND_WAIT: + CollectParameters(&m_nIp, 1); + m_nWakeTime = CTimer::GetTimeInMilliseconds() + ScriptParams[0]; + m_bSkipWakeTime = false; + return 1; + case COMMAND_GOTO: + CollectParameters(&m_nIp, 1); + SetIP(ScriptParams[0] >= 0 ? ScriptParams[0] : SIZE_MAIN_SCRIPT - ScriptParams[0]); + /* Known issue: GOTO to 0. It might have been "better" to use > instead of >= */ + /* simply because it never makes sense to jump to start of the script */ + /* but jumping to start of a custom mission is an issue for simple mission-like scripts */ + /* However, it's not an issue for actual mission scripts, because they follow a structure */ + /* and never start with a loop. */ + return 0; + case COMMAND_SHAKE_CAM: + CollectParameters(&m_nIp, 1); + CamShakeNoPos(&TheCamera, ScriptParams[0] / 1000.0f); + return 0; + case COMMAND_SET_VAR_INT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + *ptr = ScriptParams[0]; + return 0; + } + case COMMAND_SET_VAR_FLOAT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + *(float*)ptr = *(float*)&ScriptParams[0]; + return 0; + } + case COMMAND_SET_LVAR_INT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + *ptr = ScriptParams[0]; + return 0; + } + case COMMAND_SET_LVAR_FLOAT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + *(float*)ptr = *(float*)&ScriptParams[0]; + return 0; + } + case COMMAND_ADD_VAL_TO_INT_VAR: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + *ptr += ScriptParams[0]; + return 0; + } + case COMMAND_ADD_VAL_TO_FLOAT_VAR: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + *(float*)ptr += *(float*)&ScriptParams[0]; + return 0; + } + case COMMAND_ADD_VAL_TO_INT_LVAR: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + *ptr += ScriptParams[0]; + return 0; + } + case COMMAND_ADD_VAL_TO_FLOAT_LVAR: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + *(float*)ptr += *(float*)&ScriptParams[0]; + return 0; + } + case COMMAND_SUB_VAL_FROM_INT_VAR: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + *ptr -= ScriptParams[0]; + return 0; + } + case COMMAND_SUB_VAL_FROM_FLOAT_VAR: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + *(float*)ptr -= *(float*)&ScriptParams[0]; + return 0; + } + case COMMAND_SUB_VAL_FROM_INT_LVAR: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + *ptr -= ScriptParams[0]; + return 0; + } + case COMMAND_SUB_VAL_FROM_FLOAT_LVAR: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + *(float*)ptr -= *(float*)&ScriptParams[0]; + return 0; + } + case COMMAND_MULT_INT_VAR_BY_VAL: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + *ptr *= ScriptParams[0]; + return 0; + } + case COMMAND_MULT_FLOAT_VAR_BY_VAL: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + *(float*)ptr *= *(float*)&ScriptParams[0]; + return 0; + } + case COMMAND_MULT_INT_LVAR_BY_VAL: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + *ptr *= ScriptParams[0]; + return 0; + } + case COMMAND_MULT_FLOAT_LVAR_BY_VAL: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + *(float*)ptr *= *(float*)&ScriptParams[0]; + return 0; + } + case COMMAND_DIV_INT_VAR_BY_VAL: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + *ptr /= ScriptParams[0]; + return 0; + } + case COMMAND_DIV_FLOAT_VAR_BY_VAL: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + *(float*)ptr /= *(float*)&ScriptParams[0]; + return 0; + } + case COMMAND_DIV_INT_LVAR_BY_VAL: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + *ptr /= ScriptParams[0]; + return 0; + } + case COMMAND_DIV_FLOAT_LVAR_BY_VAL: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + *(float*)ptr /= *(float*)&ScriptParams[0]; + return 0; + } + case COMMAND_IS_INT_VAR_GREATER_THAN_NUMBER: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(*ptr > ScriptParams[0]); + return 0; + } + case COMMAND_IS_INT_LVAR_GREATER_THAN_NUMBER: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(*ptr > ScriptParams[0]); + return 0; + } + case COMMAND_IS_NUMBER_GREATER_THAN_INT_VAR: + { + CollectParameters(&m_nIp, 1); + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + UpdateCompareFlag(ScriptParams[0] > *ptr); + return 0; + } + case COMMAND_IS_NUMBER_GREATER_THAN_INT_LVAR: + { + CollectParameters(&m_nIp, 1); + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + UpdateCompareFlag(ScriptParams[0] > *ptr); + return 0; + } + case COMMAND_IS_INT_VAR_GREATER_THAN_INT_VAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + UpdateCompareFlag(*ptr1 > *ptr2); + return 0; + } + case COMMAND_IS_INT_LVAR_GREATER_THAN_INT_VAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + UpdateCompareFlag(*ptr1 > *ptr2); + return 0; + } + case COMMAND_IS_INT_VAR_GREATER_THAN_INT_LVAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + UpdateCompareFlag(*ptr1 > *ptr2); + return 0; + } + case COMMAND_IS_INT_LVAR_GREATER_THAN_INT_LVAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + UpdateCompareFlag(*ptr1 > *ptr2); + return 0; + } + case COMMAND_IS_FLOAT_VAR_GREATER_THAN_NUMBER: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(*(float*)ptr > *(float*)&ScriptParams[0]); + return 0; + } + case COMMAND_IS_FLOAT_LVAR_GREATER_THAN_NUMBER: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(*(float*)ptr > *(float*)&ScriptParams[0]); + return 0; + } + case COMMAND_IS_NUMBER_GREATER_THAN_FLOAT_VAR: + { + CollectParameters(&m_nIp, 1); + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + UpdateCompareFlag(*(float*)&ScriptParams[0] > *(float*)ptr); + return 0; + } + case COMMAND_IS_NUMBER_GREATER_THAN_FLOAT_LVAR: + { + CollectParameters(&m_nIp, 1); + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + UpdateCompareFlag(*(float*)&ScriptParams[0] > *(float*)ptr); + return 0; + } + case COMMAND_IS_FLOAT_VAR_GREATER_THAN_FLOAT_VAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + UpdateCompareFlag(*(float*)ptr1 > *(float*)ptr2); + return 0; + } + case COMMAND_IS_FLOAT_LVAR_GREATER_THAN_FLOAT_VAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + UpdateCompareFlag(*(float*)ptr1 > *(float*)ptr2); + return 0; + } + case COMMAND_IS_FLOAT_VAR_GREATER_THAN_FLOAT_LVAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + UpdateCompareFlag(*(float*)ptr1 > *(float*)ptr2); + return 0; + } + case COMMAND_IS_FLOAT_LVAR_GREATER_THAN_FLOAT_LVAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + UpdateCompareFlag(*(float*)ptr1 > *(float*)ptr2); + return 0; + } + case COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_NUMBER: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(*ptr >= ScriptParams[0]); + return 0; + } + case COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_NUMBER: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(*ptr >= ScriptParams[0]); + return 0; + } + case COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_INT_VAR: + { + CollectParameters(&m_nIp, 1); + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + UpdateCompareFlag(ScriptParams[0] >= *ptr); + return 0; + } + case COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_INT_LVAR: + { + CollectParameters(&m_nIp, 1); + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + UpdateCompareFlag(ScriptParams[0] >= *ptr); + return 0; + } + case COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_INT_VAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + UpdateCompareFlag(*ptr1 >= *ptr2); + return 0; + } + case COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_INT_VAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + UpdateCompareFlag(*ptr1 >= *ptr2); + return 0; + } + case COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_INT_LVAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + UpdateCompareFlag(*ptr1 >= *ptr2); + return 0; + } + case COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_INT_LVAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + UpdateCompareFlag(*ptr1 >= *ptr2); + return 0; + } + case COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_NUMBER: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(*(float*)ptr >= *(float*)&ScriptParams[0]); + return 0; + } + case COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_NUMBER: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(*(float*)ptr >= *(float*)&ScriptParams[0]); + return 0; + } + case COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_FLOAT_VAR: + { + CollectParameters(&m_nIp, 1); + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + UpdateCompareFlag(*(float*)&ScriptParams[0] >= *(float*)ptr); + return 0; + } + case COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_FLOAT_LVAR: + { + CollectParameters(&m_nIp, 1); + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + UpdateCompareFlag(*(float*)&ScriptParams[0] >= *(float*)ptr); + return 0; + } + case COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_FLOAT_VAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + UpdateCompareFlag(*(float*)ptr1 >= *(float*)ptr2); + return 0; + } + case COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_FLOAT_VAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + UpdateCompareFlag(*(float*)ptr1 >= *(float*)ptr2); + return 0; + } + case COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_FLOAT_LVAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + UpdateCompareFlag(*(float*)ptr1 >= *(float*)ptr2); + return 0; + } + case COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_FLOAT_LVAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + UpdateCompareFlag(*(float*)ptr1 >= *(float*)ptr2); + return 0; + } + case COMMAND_IS_INT_VAR_EQUAL_TO_NUMBER: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(*ptr == ScriptParams[0]); + return 0; + } + case COMMAND_IS_INT_LVAR_EQUAL_TO_NUMBER: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(*ptr == ScriptParams[0]); + return 0; + } + case COMMAND_IS_INT_VAR_EQUAL_TO_INT_VAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + UpdateCompareFlag(*ptr1 == *ptr2); + return 0; + } + case COMMAND_IS_INT_VAR_EQUAL_TO_INT_LVAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + UpdateCompareFlag(*ptr1 == *ptr2); + return 0; + } + case COMMAND_IS_INT_LVAR_EQUAL_TO_INT_LVAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + UpdateCompareFlag(*ptr1 == *ptr2); + return 0; + } + //case COMMAND_IS_INT_VAR_NOT_EQUAL_TO_NUMBER: + //case COMMAND_IS_INT_LVAR_NOT_EQUAL_TO_NUMBER: + //case COMMAND_IS_INT_VAR_NOT_EQUAL_TO_INT_VAR: + //case COMMAND_IS_INT_LVAR_NOT_EQUAL_TO_INT_LVAR: + //case COMMAND_IS_INT_VAR_NOT_EQUAL_TO_INT_LVAR: + case COMMAND_IS_FLOAT_VAR_EQUAL_TO_NUMBER: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(*(float*)ptr == *(float*)&ScriptParams[0]); + return 0; + } + case COMMAND_IS_FLOAT_LVAR_EQUAL_TO_NUMBER: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(*(float*)ptr == *(float*)&ScriptParams[0]); + return 0; + } + case COMMAND_IS_FLOAT_VAR_EQUAL_TO_FLOAT_VAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + UpdateCompareFlag(*(float*)ptr1 == *(float*)ptr2); + return 0; + } + case COMMAND_IS_FLOAT_VAR_EQUAL_TO_FLOAT_LVAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + UpdateCompareFlag(*(float*)ptr1 == *(float*)ptr2); + return 0; + } + case COMMAND_IS_FLOAT_LVAR_EQUAL_TO_FLOAT_LVAR: + { + int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + UpdateCompareFlag(*(float*)ptr1 == *(float*)ptr2); + return 0; + } + //case COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_NUMBER: + //case COMMAND_IS_FLOAT_LVAR_NOT_EQUAL_TO_NUMBER: + //case COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_FLOAT_VAR: + //case COMMAND_IS_FLOAT_LVAR_NOT_EQUAL_TO_FLOAT_LVAR: + //case COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_FLOAT_LVAR: + /* + case COMMAND_GOTO_IF_TRUE: + CollectParameters(&m_nIp, 1); + if (m_bCondResult) + SetIP(ScriptParams[0] >= 0 ? ScriptParams[0] : SIZE_MAIN_SCRIPT - ScriptParams[0]); + return 0; + */ + case COMMAND_GOTO_IF_FALSE: + CollectParameters(&m_nIp, 1); + if (!m_bCondResult) + SetIP(ScriptParams[0] >= 0 ? ScriptParams[0] : SIZE_MAIN_SCRIPT - ScriptParams[0]); + /* Check COMMAND_GOTO note. */ + return 0; + case COMMAND_TERMINATE_THIS_SCRIPT: + if (m_bMissionFlag) + CTheScripts::bAlreadyRunningAMissionScript = false; + RemoveScriptFromList(&CTheScripts::pActiveScripts); + AddScriptToList(&CTheScripts::pIdleScripts); + m_bIsActive = false; +#ifdef MISSION_REPLAY + if (m_bMissionFlag) { + CPlayerInfo* pPlayerInfo = &CWorld::Players[CWorld::PlayerInFocus]; +#if 0 // makeing autosave is pointless and is a bit buggy + if (pPlayerInfo->m_pPed->GetPedState() != PED_DEAD && pPlayerInfo->m_WBState == WBSTATE_PLAYING && !m_bDeatharrestExecuted) + SaveGameForPause(SAVE_TYPE_QUICKSAVE); +#endif + oldTargetX = oldTargetY = 0.0f; + if (AllowMissionReplay == MISSION_RETRY_STAGE_WAIT_FOR_SCRIPT_TO_TERMINATE) + AllowMissionReplay = MISSION_RETRY_STAGE_START_PROCESSING; + // I am fairly sure they forgot to set return value here + } +#endif + return 1; + case COMMAND_START_NEW_SCRIPT: + { + CollectParameters(&m_nIp, 1); + script_assert(ScriptParams[0] >= 0); + CRunningScript* pNew = CTheScripts::StartNewScript(ScriptParams[0]); + pNew->m_bIsActive = true; + int8 type = CTheScripts::Read1ByteFromScript(&m_nIp); + float tmp; + for (int i = 0; type != ARGUMENT_END; type = CTheScripts::Read1ByteFromScript(&m_nIp), i++) { + switch (type) { + case ARGUMENT_INT32: + pNew->m_anLocalVariables[i] = CTheScripts::Read4BytesFromScript(&m_nIp); + break; + case ARGUMENT_GLOBALVAR: + pNew->m_anLocalVariables[i] = *(int32*)&CTheScripts::ScriptSpace[(uint16)CTheScripts::Read2BytesFromScript(&m_nIp)]; + break; + case ARGUMENT_LOCALVAR: + pNew->m_anLocalVariables[i] = m_anLocalVariables[CTheScripts::Read2BytesFromScript(&m_nIp)]; + break; + case ARGUMENT_INT8: + pNew->m_anLocalVariables[i] = CTheScripts::Read1ByteFromScript(&m_nIp); + break; + case ARGUMENT_INT16: + pNew->m_anLocalVariables[i] = CTheScripts::Read2BytesFromScript(&m_nIp); + break; + case ARGUMENT_FLOAT: + tmp = CTheScripts::ReadFloatFromScript(&m_nIp); + pNew->m_anLocalVariables[i] = *(int32*)&tmp; + break; + default: + break; + } + } + return 0; + } + case COMMAND_GOSUB: + CollectParameters(&m_nIp, 1); + script_assert(m_nStackPointer < MAX_STACK_DEPTH); + m_anStack[m_nStackPointer++] = m_nIp; + SetIP(ScriptParams[0] >= 0 ? ScriptParams[0] : SIZE_MAIN_SCRIPT - ScriptParams[0]); + return 0; + case COMMAND_RETURN: + script_assert(m_nStackPointer > 0); /* No more SSU */ + SetIP(m_anStack[--m_nStackPointer]); + return 0; + case COMMAND_LINE: + CollectParameters(&m_nIp, 6); + /* Something must have been here */ + return 0; + case COMMAND_CREATE_PLAYER: + { + CollectParameters(&m_nIp, 4); + int32 index = ScriptParams[0]; + script_assert(index < NUMPLAYERS); + printf("&&&&&&&&&&&&&Creating player: %d\n", index); + if (!CStreaming::HasModelLoaded(MI_PLAYER)) { + CStreaming::RequestSpecialModel(MI_PLAYER, "player", STREAMFLAGS_DONT_REMOVE | STREAMFLAGS_DEPENDENCY); + CStreaming::LoadAllRequestedModels(false); + } + CPlayerPed::SetupPlayerPed(index); + CWorld::Players[index].m_pPed->CharCreatedBy = MISSION_CHAR; + CPlayerPed::DeactivatePlayerPed(index); + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + pos.z += CWorld::Players[index].m_pPed->GetDistanceFromCentreOfMassToBaseOfModel(); + CWorld::Players[index].m_pPed->SetPosition(pos); + CTheScripts::ClearSpaceForMissionEntity(pos, CWorld::Players[index].m_pPed); + CPlayerPed::ReactivatePlayerPed(index); + ScriptParams[0] = index; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_GET_PLAYER_COORDINATES: + { + CVector pos; + CollectParameters(&m_nIp, 1); + if (CWorld::Players[ScriptParams[0]].m_pPed->bInVehicle) + pos = CWorld::Players[ScriptParams[0]].m_pPed->m_pMyVehicle->GetPosition(); + else + pos = CWorld::Players[ScriptParams[0]].m_pPed->GetPosition(); + *(CVector*)&ScriptParams[0] = pos; + StoreParameters(&m_nIp, 3); + return 0; + } + case COMMAND_SET_PLAYER_COORDINATES: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[1]; + int index = ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CPlayerPed* ped = CWorld::Players[index].m_pPed; + if (ped->bInVehicle) { + pos.z += ped->m_pMyVehicle->GetDistanceFromCentreOfMassToBaseOfModel(); + ped->m_pMyVehicle->Teleport(pos); // removed dumb stuff that was present here + CTheScripts::ClearSpaceForMissionEntity(pos, ped->m_pMyVehicle); + return 0; + } + pos.z += ped->GetDistanceFromCentreOfMassToBaseOfModel(); + CVector vOldPos = ped->GetPosition(); + ped->Teleport(pos); + CTheScripts::ClearSpaceForMissionEntity(pos, ped); + if (ped) { // great time to check + for (int i = 0; i < ped->m_numNearPeds; i++) { + CPed* pTestedPed = ped->m_nearPeds[i]; + if (!pTestedPed || !IsPedPointerValid(pTestedPed)) + continue; + if (pTestedPed->m_pedInObjective == ped && pTestedPed->m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION) { + CVector vFollowerPos = pTestedPed->GetFormationPosition(); + CTheScripts::ClearSpaceForMissionEntity(vFollowerPos, ped); + bool bFound = false; + vFollowerPos.z = CWorld::FindGroundZFor3DCoord(vFollowerPos.x, vFollowerPos.y, vFollowerPos.z + 1.0f, &bFound) + 1.0f; + if (bFound) { + if (CWorld::GetIsLineOfSightClear(vFollowerPos, ped->GetPosition(), true, false, false, true, false, false)) { + pTestedPed->Teleport(vFollowerPos); + } + } + } + else if (pTestedPed->m_leader == ped) { + CVector vFollowerPos; + if (pTestedPed->m_pedFormation) + vFollowerPos = pTestedPed->GetFormationPosition(); + else + vFollowerPos = ped->GetPosition() + pTestedPed->GetPosition() - vOldPos; + CTheScripts::ClearSpaceForMissionEntity(vFollowerPos, ped); + bool bFound = false; + vFollowerPos.z = CWorld::FindGroundZFor3DCoord(vFollowerPos.x, vFollowerPos.y, vFollowerPos.z + 1.0f, &bFound) + 1.0f; + if (bFound) { + if (CWorld::GetIsLineOfSightClear(vFollowerPos, ped->GetPosition(), true, false, false, true, false, false)) { + pTestedPed->Teleport(vFollowerPos); + } + } + } + } + } + return 0; + } + case COMMAND_IS_PLAYER_IN_AREA_2D: + { + CollectParameters(&m_nIp, 6); + CPlayerPed* ped = CWorld::Players[ScriptParams[0]].m_pPed; + float x1 = *(float*)&ScriptParams[1]; + float y1 = *(float*)&ScriptParams[2]; + float x2 = *(float*)&ScriptParams[3]; + float y2 = *(float*)&ScriptParams[4]; + if (!ped->bInVehicle) + UpdateCompareFlag(ped->IsWithinArea(x1, y1, x2, y2)); + else + UpdateCompareFlag(ped->m_pMyVehicle->IsWithinArea(x1, y1, x2, y2)); + if (ScriptParams[5]) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) + CTheScripts::DrawDebugSquare(x1, y1, x2, y2); + return 0; + } + case COMMAND_IS_PLAYER_IN_AREA_3D: + { + CollectParameters(&m_nIp, 8); + CPlayerPed* ped = CWorld::Players[ScriptParams[0]].m_pPed; + float x1 = *(float*)&ScriptParams[1]; + float y1 = *(float*)&ScriptParams[2]; + float z1 = *(float*)&ScriptParams[3]; + float x2 = *(float*)&ScriptParams[4]; + float y2 = *(float*)&ScriptParams[5]; + float z2 = *(float*)&ScriptParams[6]; + if (ped->bInVehicle) + UpdateCompareFlag(ped->m_pMyVehicle->IsWithinArea(x1, y1, z1, x2, y2, z2)); + else + UpdateCompareFlag(ped->IsWithinArea(x1, y1, z1, x2, y2, z2)); + if (ScriptParams[7]) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, (z1 + z2) / 2); + if (CTheScripts::DbgFlag) + CTheScripts::DrawDebugCube(x1, y1, z1, x2, y2, z2); + return 0; + } + case COMMAND_ADD_INT_VAR_TO_INT_VAR: + nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *nScriptVar1 += *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + case COMMAND_ADD_INT_LVAR_TO_INT_VAR: + nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *nScriptVar1 += *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + case COMMAND_ADD_INT_VAR_TO_INT_LVAR: + nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *nScriptVar1 += *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + case COMMAND_ADD_INT_LVAR_TO_INT_LVAR: + nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *nScriptVar1 += *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + case COMMAND_ADD_FLOAT_VAR_TO_FLOAT_VAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *fScriptVar1 += *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + case COMMAND_ADD_FLOAT_LVAR_TO_FLOAT_VAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *fScriptVar1 += *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + case COMMAND_ADD_FLOAT_VAR_TO_FLOAT_LVAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *fScriptVar1 += *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + case COMMAND_ADD_FLOAT_LVAR_TO_FLOAT_LVAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *fScriptVar1 += *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + case COMMAND_SUB_INT_VAR_FROM_INT_VAR: + nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *nScriptVar1 -= *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + case COMMAND_SUB_INT_LVAR_FROM_INT_LVAR: + nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *nScriptVar1 -= *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + case COMMAND_SUB_FLOAT_VAR_FROM_FLOAT_VAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *fScriptVar1 -= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + case COMMAND_SUB_FLOAT_LVAR_FROM_FLOAT_LVAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *fScriptVar1 -= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + default: + script_assert(0); + break; + } + return -1; +} + +int8 CRunningScript::ProcessCommands100To199(int32 command) +{ + float *fScriptVar1; + int32 *nScriptVar1; + switch (command) { + case COMMAND_SUB_INT_LVAR_FROM_INT_VAR: + nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *nScriptVar1 -= *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + case COMMAND_SUB_INT_VAR_FROM_INT_LVAR: + nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *nScriptVar1 -= *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + case COMMAND_SUB_FLOAT_LVAR_FROM_FLOAT_VAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *fScriptVar1 -= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + case COMMAND_SUB_FLOAT_VAR_FROM_FLOAT_LVAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *fScriptVar1 -= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + case COMMAND_MULT_INT_VAR_BY_INT_VAR: + nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *nScriptVar1 *= *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + case COMMAND_MULT_INT_VAR_BY_INT_LVAR: + nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *nScriptVar1 *= *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + case COMMAND_MULT_INT_LVAR_BY_INT_VAR: + nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *nScriptVar1 *= *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + case COMMAND_MULT_INT_LVAR_BY_INT_LVAR: + nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *nScriptVar1 *= *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + case COMMAND_MULT_FLOAT_VAR_BY_FLOAT_VAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *fScriptVar1 *= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + case COMMAND_MULT_FLOAT_VAR_BY_FLOAT_LVAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *fScriptVar1 *= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + case COMMAND_MULT_FLOAT_LVAR_BY_FLOAT_VAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *fScriptVar1 *= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + case COMMAND_MULT_FLOAT_LVAR_BY_FLOAT_LVAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *fScriptVar1 *= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + case COMMAND_DIV_INT_VAR_BY_INT_VAR: + nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *nScriptVar1 /= *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + case COMMAND_DIV_INT_VAR_BY_INT_LVAR: + nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *nScriptVar1 /= *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + case COMMAND_DIV_INT_LVAR_BY_INT_VAR: + nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *nScriptVar1 /= *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + case COMMAND_DIV_INT_LVAR_BY_INT_LVAR: + nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *nScriptVar1 /= *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + case COMMAND_DIV_FLOAT_VAR_BY_FLOAT_VAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *fScriptVar1 /= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + case COMMAND_DIV_FLOAT_VAR_BY_FLOAT_LVAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *fScriptVar1 /= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + case COMMAND_DIV_FLOAT_LVAR_BY_FLOAT_VAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *fScriptVar1 /= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + case COMMAND_DIV_FLOAT_LVAR_BY_FLOAT_LVAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *fScriptVar1 /= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + case COMMAND_ADD_TIMED_VAL_TO_FLOAT_VAR: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + *(float*)ptr += CTimer::GetTimeStep() * *(float*)&ScriptParams[0]; + return 0; + } + case COMMAND_ADD_TIMED_VAL_TO_FLOAT_LVAR: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + *(float*)ptr += CTimer::GetTimeStep() * *(float*)&ScriptParams[0]; + return 0; + } + case COMMAND_ADD_TIMED_FLOAT_VAR_TO_FLOAT_VAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *fScriptVar1 += CTimer::GetTimeStep() * *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; +#ifdef FIX_BUGS + case COMMAND_ADD_TIMED_FLOAT_VAR_TO_FLOAT_LVAR: +#else + case COMMAND_ADD_TIMED_FLOAT_LVAR_TO_FLOAT_VAR: +#endif + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *fScriptVar1 += CTimer::GetTimeStep() * *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; +#ifdef FIX_BUGS + case COMMAND_ADD_TIMED_FLOAT_LVAR_TO_FLOAT_VAR: +#else + case COMMAND_ADD_TIMED_FLOAT_VAR_TO_FLOAT_LVAR: +#endif + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *fScriptVar1 += CTimer::GetTimeStep() * *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + case COMMAND_ADD_TIMED_FLOAT_LVAR_TO_FLOAT_LVAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *fScriptVar1 += CTimer::GetTimeStep() * *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + case COMMAND_SUB_TIMED_VAL_FROM_FLOAT_VAR: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + *(float*)ptr -= CTimer::GetTimeStep() * *(float*)&ScriptParams[0]; + return 0; + } + case COMMAND_SUB_TIMED_VAL_FROM_FLOAT_LVAR: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + *(float*)ptr -= CTimer::GetTimeStep() * *(float*)&ScriptParams[0]; + return 0; + } + case COMMAND_SUB_TIMED_FLOAT_VAR_FROM_FLOAT_VAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *fScriptVar1 -= CTimer::GetTimeStep() * *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; +#ifdef FIX_BUGS // in SA it was fixed by reversing their order in enum + case COMMAND_SUB_TIMED_FLOAT_VAR_FROM_FLOAT_LVAR: +#else + case COMMAND_SUB_TIMED_FLOAT_LVAR_FROM_FLOAT_VAR: +#endif + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *fScriptVar1 -= CTimer::GetTimeStep() * *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; +#ifdef FIX_BUGS + case COMMAND_SUB_TIMED_FLOAT_LVAR_FROM_FLOAT_VAR: +#else + case COMMAND_SUB_TIMED_FLOAT_VAR_FROM_FLOAT_LVAR: +#endif + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *fScriptVar1 -= CTimer::GetTimeStep() * *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + case COMMAND_SUB_TIMED_FLOAT_LVAR_FROM_FLOAT_LVAR: + fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *fScriptVar1 -= CTimer::GetTimeStep() * *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + case COMMAND_SET_VAR_INT_TO_VAR_INT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *ptr = *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + } + case COMMAND_SET_VAR_INT_TO_LVAR_INT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *ptr = *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + } + case COMMAND_SET_LVAR_INT_TO_VAR_INT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *ptr = *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + } + case COMMAND_SET_LVAR_INT_TO_LVAR_INT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *ptr = *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + } + case COMMAND_SET_VAR_FLOAT_TO_VAR_FLOAT: + { + float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *ptr = *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + } + case COMMAND_SET_VAR_FLOAT_TO_LVAR_FLOAT: + { + float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *ptr = *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + } + case COMMAND_SET_LVAR_FLOAT_TO_VAR_FLOAT: + { + float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *ptr = *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + } + case COMMAND_SET_LVAR_FLOAT_TO_LVAR_FLOAT: + { + float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *ptr = *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + } + case COMMAND_CSET_VAR_INT_TO_VAR_FLOAT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *ptr = *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + } + case COMMAND_CSET_VAR_INT_TO_LVAR_FLOAT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *ptr = *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + } + case COMMAND_CSET_LVAR_INT_TO_VAR_FLOAT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *ptr = *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + } + case COMMAND_CSET_LVAR_INT_TO_LVAR_FLOAT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *ptr = *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + } + case COMMAND_CSET_VAR_FLOAT_TO_VAR_INT: + { + float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *ptr = *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + } + case COMMAND_CSET_VAR_FLOAT_TO_LVAR_INT: + { + float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *ptr = *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + } + case COMMAND_CSET_LVAR_FLOAT_TO_VAR_INT: + { + float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *ptr = *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + return 0; + } + case COMMAND_CSET_LVAR_FLOAT_TO_LVAR_INT: + { + float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *ptr = *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + return 0; + } + case COMMAND_ABS_VAR_INT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *ptr = ABS(*ptr); + return 0; + } + case COMMAND_ABS_LVAR_INT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *ptr = ABS(*ptr); + return 0; + } + case COMMAND_ABS_VAR_FLOAT: + { + float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + *ptr = ABS(*ptr); + return 0; + } + case COMMAND_ABS_LVAR_FLOAT: + { + float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + *ptr = ABS(*ptr); + return 0; + } + case COMMAND_GENERATE_RANDOM_FLOAT: + { + float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CGeneral::GetRandomNumber(); + CGeneral::GetRandomNumber(); + CGeneral::GetRandomNumber(); /* To make it EXTRA random! */ +#ifdef FIX_BUGS + *ptr = CGeneral::GetRandomNumberInRange(0.0f, 1.0f); +#else + *ptr = CGeneral::GetRandomNumber() / 65536.0f; + /* Between 0 and 0.5 on PC (oh well...), never used in original script. */ +#endif + + return 0; + } + case COMMAND_GENERATE_RANDOM_INT: + *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL) = CGeneral::GetRandomNumber(); + return 0; + case COMMAND_CREATE_CHAR: + { + CollectParameters(&m_nIp, 5); + switch (ScriptParams[1]) { + case MI_COP: + if (ScriptParams[0] == PEDTYPE_COP) + ScriptParams[1] = COP_STREET; + break; + case MI_SWAT: + if (ScriptParams[0] == PEDTYPE_COP) + ScriptParams[1] = COP_SWAT; + break; + case MI_FBI: + if (ScriptParams[0] == PEDTYPE_COP) + ScriptParams[1] = COP_FBI; + break; + case MI_ARMY: + if (ScriptParams[0] == PEDTYPE_COP) + ScriptParams[1] = COP_ARMY; + break; + case MI_MEDIC: + if (ScriptParams[0] == PEDTYPE_EMERGENCY) + ScriptParams[1] = PEDTYPE_EMERGENCY; + break; + case MI_FIREMAN: + if (ScriptParams[0] == PEDTYPE_FIREMAN) + ScriptParams[1] = PEDTYPE_FIREMAN; + break; + default: + break; + } + CPed* ped; + if (ScriptParams[0] == PEDTYPE_COP) + ped = new CCopPed((eCopType)ScriptParams[1]); + else if (ScriptParams[0] == PEDTYPE_EMERGENCY || ScriptParams[0] == PEDTYPE_FIREMAN) + ped = new CEmergencyPed(ScriptParams[1]); + else + ped = new CCivilianPed((ePedType)ScriptParams[0], ScriptParams[1]); + ped->CharCreatedBy = MISSION_CHAR; + ped->bRespondsToThreats = false; + ped->bAllowMedicsToReviveMe = false; + ped->bIsPlayerFriend = false; + CVector pos = *(CVector*)&ScriptParams[2]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + pos.z += 1.0f; + ped->SetPosition(pos); + ped->SetOrientation(0.0f, 0.0f, 0.0f); + CTheScripts::ClearSpaceForMissionEntity(pos, ped); + if (m_bIsMissionScript) + ped->bIsStaticWaitingForCollision = true; + CWorld::Add(ped); + ped->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pos); + CPopulation::ms_nTotalMissionPeds++; + ScriptParams[0] = CPools::GetPedPool()->GetIndex(ped); + StoreParameters(&m_nIp, 1); + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.AddEntityToList(ScriptParams[0], CLEANUP_CHAR); + return 0; + } + case COMMAND_DELETE_CHAR: + { + CollectParameters(&m_nIp, 1); + CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); + CTheScripts::RemoveThisPed(ped); + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_CHAR); + return 0; + } + case COMMAND_CHAR_WANDER_DIR: + { + CollectParameters(&m_nIp, 2); + CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(ped); + ped->ClearAll(); + int8 path = ScriptParams[1]; + if (ScriptParams[1] < 0 || ScriptParams[1] > 7) + // Max number GetRandomNumberInRange returns is max-1 +#ifdef FIX_BUGS + path = CGeneral::GetRandomNumberInRange(0, 8); +#else + path = CGeneral::GetRandomNumberInRange(0, 7); +#endif + + ped->SetWanderPath(path); + return 0; + } + //case COMMAND_CHAR_WANDER_RANGE: + case COMMAND_CHAR_FOLLOW_PATH: + { + CollectParameters(&m_nIp, 6); + CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(ped); + if (ped->GetPedState() == PED_ATTACK || ped->GetPedState() == PED_FIGHT || !ped->IsPedInControl()) + return 0; + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + float radius = *(float*)&ScriptParams[4]; + eMoveState state; + switch (ScriptParams[5]) { + case 0: state = PEDMOVE_WALK; break; + case 1: state = PEDMOVE_RUN; break; + default: assert(0); + } + ped->ClearAll(); + ped->m_pathNodeTimer = 0; + ped->SetFollowPath(pos, radius, state, nil, nil, 999999); + return 0; + } + case COMMAND_CHAR_SET_IDLE: + { + CollectParameters(&m_nIp, 1); + CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(ped); + ped->bScriptObjectiveCompleted = false; + ped->SetObjective(OBJECTIVE_WAIT_ON_FOOT); + return 0; + } + case COMMAND_GET_CHAR_COORDINATES: + { + CollectParameters(&m_nIp, 1); + CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(ped); + CVehicle* vehicle; + CVector pos; + /* Seems a bit clumsy but I'll leave original flow */ + if (ped->bInVehicle) + vehicle = ped->m_pMyVehicle; + else + vehicle = nil; + if (vehicle) + pos = vehicle->GetPosition(); + else + pos = ped->GetPosition(); + *(CVector*)&ScriptParams[0] = pos; + StoreParameters(&m_nIp, 3); + return 0; + } + case COMMAND_SET_CHAR_COORDINATES: + { + CollectParameters(&m_nIp, 4); + CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(ped); + CVehicle* vehicle; + if (ped->bInVehicle) + vehicle = ped->m_pMyVehicle; + else + vehicle = nil; + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + // removed dumb stuff again + if (!vehicle) { + pos.z += ped->GetDistanceFromCentreOfMassToBaseOfModel(); + ped->Teleport(pos); + CTheScripts::ClearSpaceForMissionEntity(pos, ped); + for (int i = 0; i < ped->m_numNearPeds; i++) { + CPed* pNearPed = ped->m_nearPeds[i]; + if (pNearPed->m_leader == ped) { + pNearPed->Teleport(pos); + pNearPed->PositionAnyPedOutOfCollision(); + } + } + } + else { + pos.z += vehicle->GetDistanceFromCentreOfMassToBaseOfModel(); + vehicle->Teleport(pos); + CTheScripts::ClearSpaceForMissionEntity(pos, vehicle); + } + return 0; + } + /* + case COMMAND_IS_CHAR_STILL_ALIVE: + { + CollectParameters(&m_nIp, 1); + CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); + UpdateCompareFlag(ped && ped->GetPedState() != PED_DEAD && ped->GetPedState() != PED_DIE); + return 0; + } + */ + case COMMAND_IS_CHAR_IN_AREA_2D: + { + CollectParameters(&m_nIp, 6); + CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(ped); + CVehicle* vehicle; + if (ped->bInVehicle) + vehicle = ped->m_pMyVehicle; + else + vehicle = nil; + float x1 = *(float*)&ScriptParams[1]; + float y1 = *(float*)&ScriptParams[2]; + float x2 = *(float*)&ScriptParams[3]; + float y2 = *(float*)&ScriptParams[4]; + if (vehicle) + UpdateCompareFlag(ped->m_pMyVehicle->IsWithinArea(x1, y1, x2, y2)); + else + UpdateCompareFlag(ped->IsWithinArea(x1, y1, x2, y2)); + if (ScriptParams[5]) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) + CTheScripts::DrawDebugSquare(x1, y1, x2, y2); + return 0; + } + case COMMAND_IS_CHAR_IN_AREA_3D: + { + CollectParameters(&m_nIp, 8); + CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(ped); + CVehicle* vehicle; + if (ped->bInVehicle) + vehicle = ped->m_pMyVehicle; + else + vehicle = nil; + float x1 = *(float*)&ScriptParams[1]; + float y1 = *(float*)&ScriptParams[2]; + float z1 = *(float*)&ScriptParams[3]; + float x2 = *(float*)&ScriptParams[4]; + float y2 = *(float*)&ScriptParams[5]; + float z2 = *(float*)&ScriptParams[6]; + if (vehicle) + UpdateCompareFlag(ped->m_pMyVehicle->IsWithinArea(x1, y1, z1, x2, y2, z2)); + else + UpdateCompareFlag(ped->IsWithinArea(x1, y1, z1, x2, y2, z2)); + if (ScriptParams[7]) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, (z1 + z2) / 2); + if (CTheScripts::DbgFlag) + CTheScripts::DrawDebugCube(x1, y1, z1, x2, y2, z2); + return 0; + } + case COMMAND_CREATE_CAR: + { + CollectParameters(&m_nIp, 4); + int32 handle; + if (CModelInfo::IsBoatModel(ScriptParams[0])) { + CBoat* boat = new CBoat(ScriptParams[0], MISSION_VEHICLE); + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + pos.z += boat->GetDistanceFromCentreOfMassToBaseOfModel(); + boat->SetPosition(pos); + CTheScripts::ClearSpaceForMissionEntity(pos, boat); + boat->SetStatus(STATUS_ABANDONED); + boat->bIsLocked = true; + boat->AutoPilot.m_nCarMission = MISSION_NONE; + boat->AutoPilot.m_nTempAction = TEMPACT_NONE; + boat->AutoPilot.m_nCruiseSpeed = boat->AutoPilot.m_fMaxTrafficSpeed = 20.0f; + if (m_bIsMissionScript) + boat->bIsStaticWaitingForCollision = true; + CWorld::Add(boat); + handle = CPools::GetVehiclePool()->GetIndex(boat); + } + else { + CVehicle* car; + + if (!CModelInfo::IsBikeModel(ScriptParams[0])) + car = new CAutomobile(ScriptParams[0], MISSION_VEHICLE); + else { + car = new CBike(ScriptParams[0], MISSION_VEHICLE); + ((CBike*)(car))->bIsStanding = true; + } + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + pos.z += car->GetDistanceFromCentreOfMassToBaseOfModel(); + car->SetPosition(pos); + CTheScripts::ClearSpaceForMissionEntity(pos, car); + car->SetStatus(STATUS_ABANDONED); + car->bIsLocked = true; + CCarCtrl::JoinCarWithRoadSystem(car); + car->AutoPilot.m_nCarMission = MISSION_NONE; + car->AutoPilot.m_nTempAction = TEMPACT_NONE; + car->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; + car->AutoPilot.m_nCruiseSpeed = car->AutoPilot.m_fMaxTrafficSpeed = 9.0f; + car->AutoPilot.m_nCurrentLane = car->AutoPilot.m_nNextLane = 0; + car->bEngineOn = false; + car->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pos); + car->bHasBeenOwnedByPlayer = true; + if (m_bIsMissionScript) + car->bIsStaticWaitingForCollision = true; + CWorld::Add(car); + handle = CPools::GetVehiclePool()->GetIndex(car); + } + ScriptParams[0] = handle; + StoreParameters(&m_nIp, 1); + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.AddEntityToList(handle, CLEANUP_CAR); + return 0; + } + case COMMAND_DELETE_CAR: + { + CollectParameters(&m_nIp, 1); + CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + if (car) { + CWorld::Remove(car); + CWorld::RemoveReferencesToDeletedObject(car); + delete car; + } + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_CAR); + return 0; + } + case COMMAND_CAR_GOTO_COORDINATES: + { + CollectParameters(&m_nIp, 4); + CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(car); + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + pos.z += car->GetDistanceFromCentreOfMassToBaseOfModel(); + if (CCarCtrl::JoinCarWithRoadSystemGotoCoors(car, pos, false)) + car->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS_STRAIGHT; + else + car->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS; + car->SetStatus(STATUS_PHYSICS); + car->bEngineOn = true; + car->AutoPilot.m_nCruiseSpeed = Max(1, car->AutoPilot.m_nCruiseSpeed); + car->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); + return 0; + } + case COMMAND_CAR_WANDER_RANDOMLY: + { + CollectParameters(&m_nIp, 1); + CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(car); + CCarCtrl::JoinCarWithRoadSystem(car); + car->AutoPilot.m_nCarMission = MISSION_CRUISE; + car->bEngineOn = true; + car->AutoPilot.m_nCruiseSpeed = Max(1, car->AutoPilot.m_nCruiseSpeed); + car->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); + return 0; + } + case COMMAND_CAR_SET_IDLE: + { + CollectParameters(&m_nIp, 1); + CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(car); + car->AutoPilot.m_nCarMission = MISSION_NONE; + return 0; + } + case COMMAND_GET_CAR_COORDINATES: + { + CollectParameters(&m_nIp, 1); + CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(car); + *(CVector*)&ScriptParams[0] = car->GetPosition(); + StoreParameters(&m_nIp, 3); + return 0; + } + case COMMAND_SET_CAR_COORDINATES: + { + CollectParameters(&m_nIp, 4); + CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(car); + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + pos.z += car->GetDistanceFromCentreOfMassToBaseOfModel(); + car->SetIsStatic(false); + /* Again weird usage of virtual functions. */ + if (car->IsBoat()) { + car->Teleport(pos); + CTheScripts::ClearSpaceForMissionEntity(pos, car); + } + else { + car->Teleport(pos); + CTheScripts::ClearSpaceForMissionEntity(pos, car); + /* May the following be inlined CCarCtrl function? */ + switch (car->AutoPilot.m_nCarMission) { + case MISSION_CRUISE: + CCarCtrl::JoinCarWithRoadSystem(car); + break; + case MISSION_RAMPLAYER_FARAWAY: + case MISSION_RAMPLAYER_CLOSE: + case MISSION_BLOCKPLAYER_FARAWAY: + case MISSION_BLOCKPLAYER_CLOSE: + case MISSION_BLOCKPLAYER_HANDBRAKESTOP: + CCarCtrl::JoinCarWithRoadSystemGotoCoors(car, FindPlayerCoors(), false); + break; + case MISSION_GOTOCOORDS: + case MISSION_GOTOCOORDS_STRAIGHT: + CCarCtrl::JoinCarWithRoadSystemGotoCoors(car, car->AutoPilot.m_vecDestinationCoors, false); + break; + case MISSION_GOTOCOORDS_ACCURATE: + case MISSION_GOTO_COORDS_STRAIGHT_ACCURATE: + CCarCtrl::JoinCarWithRoadSystemGotoCoors(car, car->AutoPilot.m_vecDestinationCoors, false); + break; + case MISSION_RAMCAR_FARAWAY: + case MISSION_RAMCAR_CLOSE: + case MISSION_BLOCKCAR_FARAWAY: + case MISSION_BLOCKCAR_CLOSE: + case MISSION_BLOCKCAR_HANDBRAKESTOP: + CCarCtrl::JoinCarWithRoadSystemGotoCoors(car, car->AutoPilot.m_pTargetCar->GetPosition(), false); + break; + default: + break; + } + } + return 0; + } + /* + case COMMAND_IS_CAR_STILL_ALIVE: + { + CollectParameters(&m_nIp, 1); + CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + UpdateCompareFlag(car && car->GetStatus() != STATUS_WRECKED && (car->IsBoat() || !car->bIsInWater)); + return 0; + } + */ + case COMMAND_SET_CAR_CRUISE_SPEED: + { + CollectParameters(&m_nIp, 2); + CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(car); + car->AutoPilot.m_nCruiseSpeed = Min(*(float*)&ScriptParams[1], 60.0f * car->pHandling->Transmission.fMaxCruiseVelocity); + return 0; + } + case COMMAND_SET_CAR_DRIVING_STYLE: + { + CollectParameters(&m_nIp, 2); + CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(car); + car->AutoPilot.m_nDrivingStyle = (uint8)ScriptParams[1]; + return 0; + } + case COMMAND_SET_CAR_MISSION: + { + CollectParameters(&m_nIp, 2); + CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(car); + car->AutoPilot.m_nCarMission = (uint8)ScriptParams[1]; + car->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); + car->bEngineOn = true; + return 0; + } + case COMMAND_IS_CAR_IN_AREA_2D: + { + CollectParameters(&m_nIp, 6); + CVehicle* vehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(vehicle); + float x1 = *(float*)&ScriptParams[1]; + float y1 = *(float*)&ScriptParams[2]; + float x2 = *(float*)&ScriptParams[3]; + float y2 = *(float*)&ScriptParams[4]; + UpdateCompareFlag(vehicle->IsWithinArea(x1, y1, x2, y2)); + if (ScriptParams[5]) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) + CTheScripts::DrawDebugSquare(x1, y1, x2, y2); + return 0; + } + case COMMAND_IS_CAR_IN_AREA_3D: + { + CollectParameters(&m_nIp, 8); + CVehicle* vehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(vehicle); + float x1 = *(float*)&ScriptParams[1]; + float y1 = *(float*)&ScriptParams[2]; + float z1 = *(float*)&ScriptParams[3]; + float x2 = *(float*)&ScriptParams[4]; + float y2 = *(float*)&ScriptParams[5]; + float z2 = *(float*)&ScriptParams[6]; + UpdateCompareFlag(vehicle->IsWithinArea(x1, y1, z1, x2, y2, z2)); + if (ScriptParams[7]) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, (z1 + z2) / 2); + if (CTheScripts::DbgFlag) + CTheScripts::DrawDebugCube(x1, y1, z1, x2, y2, z2); + return 0; + } + case COMMAND_SPECIAL_0: + case COMMAND_SPECIAL_1: + case COMMAND_SPECIAL_2: + case COMMAND_SPECIAL_3: + case COMMAND_SPECIAL_4: + case COMMAND_SPECIAL_5: + case COMMAND_SPECIAL_6: + case COMMAND_SPECIAL_7: + script_assert(0); + return 0; + case COMMAND_PRINT_BIG: + { + wchar* key = CTheScripts::GetTextByKeyFromScript(&m_nIp); +#ifdef MISSION_REPLAY + if (strcmp((char*)&CTheScripts::ScriptSpace[m_nIp - KEY_LENGTH_IN_SCRIPT], "M_FAIL") == 0) { + if (AllowMissionReplay == MISSION_RETRY_STAGE_WAIT_FOR_TIMER_AFTER_RESTART) + AllowMissionReplay = MISSION_RETRY_STAGE_NORMAL; + if (CanAllowMissionReplay()) + AllowMissionReplay = MISSION_RETRY_STAGE_WAIT_FOR_SCRIPT_TO_TERMINATE; + } +#endif + CollectParameters(&m_nIp, 2); + CMessages::AddBigMessage(key, ScriptParams[0], ScriptParams[1] - 1); + return 0; + } + case COMMAND_PRINT: + { + wchar* key = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 2); + CMessages::AddMessage(key, ScriptParams[0], ScriptParams[1]); + return 0; + } + case COMMAND_PRINT_NOW: + { + wchar* key = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 2); + CMessages::AddMessageJumpQ(key, ScriptParams[0], ScriptParams[1]); + return 0; + } + /* + case COMMAND_PRINT_SOON: + { + wchar* key = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 2); + CMessages::AddMessageSoon(key, ScriptParams[0], ScriptParams[1]); + return 0; + } + */ + case COMMAND_CLEAR_PRINTS: + CMessages::ClearMessages(); + return 0; + case COMMAND_GET_TIME_OF_DAY: + ScriptParams[0] = CClock::GetHours(); + ScriptParams[1] = CClock::GetMinutes(); + StoreParameters(&m_nIp, 2); + return 0; + case COMMAND_SET_TIME_OF_DAY: + CollectParameters(&m_nIp, 2); + CClock::SetGameClock(ScriptParams[0], ScriptParams[1]); + return 0; + case COMMAND_GET_MINUTES_TO_TIME_OF_DAY: + CollectParameters(&m_nIp, 2); + ScriptParams[0] = CClock::GetGameClockMinutesUntil(ScriptParams[0], ScriptParams[1]); + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_IS_POINT_ON_SCREEN: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= -100) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + UpdateCompareFlag(TheCamera.IsSphereVisible(pos, *(float*)&ScriptParams[3])); + return 0; + } + case COMMAND_DEBUG_ON: + CTheScripts::DbgFlag = true; + return 0; + case COMMAND_DEBUG_OFF: + CTheScripts::DbgFlag = false; + return 0; + /* + case COMMAND_RETURN_TRUE: + UpdateCompareFlag(true); + return 0; + case COMMAND_RETURN_FALSE: + UpdateCompareFlag(false); + return 0; + */ + //case COMMAND_VAR_INT: + default: + script_assert(0); + break; + } + return -1; +} + +int8 CRunningScript::ProcessCommands200To299(int32 command) +{ + switch (command) { + /* Special commands. + case COMMAND_VAR_FLOAT: + case COMMAND_LVAR_INT: + case COMMAND_LVAR_FLOAT: + case COMMAND_LBRACKET: + case COMMAND_RBRACKET: + case COMMAND_REPEAT: + case COMMAND_ENDREPEAT: + case COMMAND_IF: + case COMMAND_IFNOT: + case COMMAND_ELSE: + case COMMAND_ENDIF: + case COMMAND_WHILE: + case COMMAND_WHILENOT: + case COMMAND_ENDWHILE: + */ + case COMMAND_ANDOR: + CollectParameters(&m_nIp, 1); + m_nAndOrState = ScriptParams[0]; + if (m_nAndOrState == ANDOR_NONE){ + m_bCondResult = false; // pointless + }else if (m_nAndOrState >= ANDS_1 && m_nAndOrState <= ANDS_8){ + m_bCondResult = true; + m_nAndOrState++; + }else if (m_nAndOrState >= ORS_1 && m_nAndOrState <= ORS_8){ + m_bCondResult = false; + m_nAndOrState++; + }else{ + script_assert(0 && "COMMAND_ANDOR: invalid ANDOR state"); + } + return 0; + case COMMAND_LAUNCH_MISSION: + { + CollectParameters(&m_nIp, 1); + CRunningScript* pNew = CTheScripts::StartNewScript(ScriptParams[0]); + pNew->m_bIsMissionScript = true; + return 0; + } + case COMMAND_MISSION_HAS_FINISHED: + { + if (!m_bIsMissionScript) + return 0; + CTheScripts::MissionCleanUp.Process(); + return 0; + } + case COMMAND_STORE_CAR_CHAR_IS_IN: + { + CollectParameters(&m_nIp, 1); + CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(ped); + CVehicle* pCurrent = nil; + if (ped->bInVehicle) { + pCurrent = ped->m_pMyVehicle; + } + script_assert(pCurrent); // GetIndex(0) doesn't look good + int handle = CPools::GetVehiclePool()->GetIndex(pCurrent); + if (handle != CTheScripts::StoreVehicleIndex && m_bIsMissionScript){ + CVehicle* pOld = CPools::GetVehiclePool()->GetAt(CTheScripts::StoreVehicleIndex); + if (pOld){ + CCarCtrl::RemoveFromInterestingVehicleList(pOld); + if (pOld->VehicleCreatedBy == MISSION_VEHICLE && CTheScripts::StoreVehicleWasRandom){ + pOld->VehicleCreatedBy = RANDOM_VEHICLE; + pOld->bIsLocked = false; + CCarCtrl::NumRandomCars++; + CCarCtrl::NumMissionCars--; + CTheScripts::MissionCleanUp.RemoveEntityFromList(CTheScripts::StoreVehicleIndex, CLEANUP_CAR); + } + } + + CTheScripts::StoreVehicleIndex = handle; + switch (pCurrent->VehicleCreatedBy){ + case RANDOM_VEHICLE: + pCurrent->VehicleCreatedBy = MISSION_VEHICLE; + CCarCtrl::NumMissionCars++; + CCarCtrl::NumRandomCars--; + CTheScripts::StoreVehicleWasRandom = true; + CTheScripts::MissionCleanUp.AddEntityToList(CTheScripts::StoreVehicleIndex, CLEANUP_CAR); + break; + case PARKED_VEHICLE: + pCurrent->VehicleCreatedBy = MISSION_VEHICLE; + CCarCtrl::NumMissionCars++; + CCarCtrl::NumParkedCars--; + CTheScripts::StoreVehicleWasRandom = true; + CTheScripts::MissionCleanUp.AddEntityToList(CTheScripts::StoreVehicleIndex, CLEANUP_CAR); + break; + case MISSION_VEHICLE: + case PERMANENT_VEHICLE: + CTheScripts::StoreVehicleWasRandom = false; + break; + default: + break; + } + } + ScriptParams[0] = CTheScripts::StoreVehicleIndex; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_STORE_CAR_PLAYER_IS_IN: + { + CollectParameters(&m_nIp, 1); + CPed* ped = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(ped); + if (!ped->bInVehicle) + return 0; // No value written to output variable + CVehicle* pCurrent = ped->m_pMyVehicle; + script_assert(pCurrent); // Here pCurrent shouldn't be NULL anyway + int handle = CPools::GetVehiclePool()->GetIndex(pCurrent); + if (handle != CTheScripts::StoreVehicleIndex && m_bIsMissionScript) { + CVehicle* pOld = CPools::GetVehiclePool()->GetAt(CTheScripts::StoreVehicleIndex); + if (pOld){ + CCarCtrl::RemoveFromInterestingVehicleList(pOld); + if (pOld->VehicleCreatedBy == MISSION_VEHICLE && CTheScripts::StoreVehicleWasRandom){ + pOld->VehicleCreatedBy = RANDOM_VEHICLE; + pOld->bIsLocked = false; + CCarCtrl::NumRandomCars++; + CCarCtrl::NumMissionCars--; + CTheScripts::MissionCleanUp.RemoveEntityFromList(CTheScripts::StoreVehicleIndex, CLEANUP_CAR); + } + } + + CTheScripts::StoreVehicleIndex = handle; + switch (pCurrent->VehicleCreatedBy) { + case RANDOM_VEHICLE: + pCurrent->VehicleCreatedBy = MISSION_VEHICLE; + CCarCtrl::NumMissionCars++; + CCarCtrl::NumRandomCars--; + CTheScripts::StoreVehicleWasRandom = true; + CTheScripts::MissionCleanUp.AddEntityToList(CTheScripts::StoreVehicleIndex, CLEANUP_CAR); + break; + case PARKED_VEHICLE: + pCurrent->VehicleCreatedBy = MISSION_VEHICLE; + CCarCtrl::NumMissionCars++; + CCarCtrl::NumParkedCars--; + CTheScripts::StoreVehicleWasRandom = true; + CTheScripts::MissionCleanUp.AddEntityToList(CTheScripts::StoreVehicleIndex, CLEANUP_CAR); + break; + case MISSION_VEHICLE: + case PERMANENT_VEHICLE: + CTheScripts::StoreVehicleWasRandom = false; + break; + default: + break; + } + } + ScriptParams[0] = CTheScripts::StoreVehicleIndex; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_IS_CHAR_IN_CAR: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + CVehicle* pCheckedVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + CVehicle* pActualVehicle = pPed->bInVehicle ? pPed->m_pMyVehicle : nil; + UpdateCompareFlag(pActualVehicle && pActualVehicle == pCheckedVehicle); + return 0; + } + case COMMAND_IS_PLAYER_IN_CAR: + { + CollectParameters(&m_nIp, 2); + CVehicle* pCheckedVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle == pCheckedVehicle); + return 0; + } + case COMMAND_IS_CHAR_IN_MODEL: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + CVehicle* pActualVehicle = pPed->bInVehicle ? pPed->m_pMyVehicle : nil; + UpdateCompareFlag(pActualVehicle && pActualVehicle->GetModelIndex() == ScriptParams[1]); + return 0; + } + case COMMAND_IS_PLAYER_IN_MODEL: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle->GetModelIndex() == ScriptParams[1]); + return 0; + } + case COMMAND_IS_CHAR_IN_ANY_CAR: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle); + return 0; + } + case COMMAND_IS_PLAYER_IN_ANY_CAR: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle); + return 0; + } + case COMMAND_IS_BUTTON_PRESSED: + { + CollectParameters(&m_nIp, 2); + UpdateCompareFlag(GetPadState(ScriptParams[0], ScriptParams[1]) != 0); + return 0; + } + /* + case COMMAND_GET_PAD_STATE: + { + CollectParameters(&m_nIp, 1); + ScriptParams[0] = GetPadState(ScriptParams[0], ScriptParams[1]); + StoreParameters(&m_nIp, 1); + return 0; + } + */ + case COMMAND_LOCATE_PLAYER_ANY_MEANS_2D: + case COMMAND_LOCATE_PLAYER_ON_FOOT_2D: + case COMMAND_LOCATE_PLAYER_IN_CAR_2D: + case COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_2D: + case COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_2D: + case COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_2D: + LocatePlayerCommand(command, &m_nIp); + return 0; + case COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_2D: + case COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_2D: + case COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_2D: + LocatePlayerCharCommand(command, &m_nIp); + return 0; + case COMMAND_LOCATE_CHAR_ANY_MEANS_2D: + case COMMAND_LOCATE_CHAR_ON_FOOT_2D: + case COMMAND_LOCATE_CHAR_IN_CAR_2D: + case COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_2D: + case COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_2D: + case COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_2D: + LocateCharCommand(command, &m_nIp); + return 0; + case COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_2D: + case COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_2D: + case COMMAND_LOCATE_CHAR_IN_CAR_CHAR_2D: + LocateCharCharCommand(command, &m_nIp); + return 0; + case COMMAND_LOCATE_PLAYER_ANY_MEANS_3D: + case COMMAND_LOCATE_PLAYER_ON_FOOT_3D: + case COMMAND_LOCATE_PLAYER_IN_CAR_3D: + case COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_3D: + case COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_3D: + case COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_3D: + LocatePlayerCommand(command, &m_nIp); + return 0; + case COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_3D: + case COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_3D: + case COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_3D: + LocatePlayerCharCommand(command, &m_nIp); + return 0; + case COMMAND_LOCATE_CHAR_ANY_MEANS_3D: + case COMMAND_LOCATE_CHAR_ON_FOOT_3D: + case COMMAND_LOCATE_CHAR_IN_CAR_3D: + case COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_3D: + case COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_3D: + case COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_3D: + LocateCharCommand(command, &m_nIp); + return 0; + case COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_3D: + case COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_3D: + case COMMAND_LOCATE_CHAR_IN_CAR_CHAR_3D: + LocateCharCharCommand(command, &m_nIp); + return 0; + case COMMAND_CREATE_OBJECT: + { + CollectParameters(&m_nIp, 4); + int mi = ScriptParams[0] >= 0 ? ScriptParams[0] : CTheScripts::UsedObjectArray[-ScriptParams[0]].index; + CObject* pObj = new CObject(mi, false); + pObj->ObjectCreatedBy = MISSION_OBJECT; + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + pos.z += pObj->GetDistanceFromCentreOfMassToBaseOfModel(); + pObj->SetPosition(pos); + pObj->SetOrientation(0.0f, 0.0f, 0.0f); + pObj->GetMatrix().UpdateRW(); + pObj->UpdateRwFrame(); + CBaseModelInfo* pModelInfo = CModelInfo::GetModelInfo(mi); + if (pModelInfo->IsBuilding() && ((CSimpleModelInfo*)pModelInfo)->m_isBigBuilding) + pObj->SetupBigBuilding(); + CTheScripts::ClearSpaceForMissionEntity(pos, pObj); + CWorld::Add(pObj); + ScriptParams[0] = CPools::GetObjectPool()->GetIndex(pObj); + StoreParameters(&m_nIp, 1); + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.AddEntityToList(ScriptParams[0], CLEANUP_OBJECT); + return 0; + } + case COMMAND_DELETE_OBJECT: + { + CollectParameters(&m_nIp, 1); + CObject* pObj = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + if (pObj){ + CWorld::Remove(pObj); + CWorld::RemoveReferencesToDeletedObject(pObj); + delete pObj; + } + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_OBJECT); + return 0; + } + case COMMAND_ADD_SCORE: + CollectParameters(&m_nIp, 2); + CWorld::Players[ScriptParams[0]].m_nMoney += ScriptParams[1]; + if (CWorld::Players[ScriptParams[0]].m_nMoney < 0) + CWorld::Players[ScriptParams[0]].m_nMoney = 0; + return 0; + case COMMAND_IS_SCORE_GREATER: + CollectParameters(&m_nIp, 2); + UpdateCompareFlag(CWorld::Players[ScriptParams[0]].m_nMoney > ScriptParams[1]); + return 0; + case COMMAND_STORE_SCORE: + CollectParameters(&m_nIp, 1); + ScriptParams[0] = CWorld::Players[ScriptParams[0]].m_nMoney; + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_GIVE_REMOTE_CONTROLLED_CAR_TO_PLAYER: + { + CollectParameters(&m_nIp, 5); + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CRemote::GivePlayerRemoteControlledCar(pos.x, pos.y, pos.z, DEGTORAD(*(float*)&ScriptParams[4]), MI_RCBANDIT); + return 0; + } + case COMMAND_ALTER_WANTED_LEVEL: + CollectParameters(&m_nIp, 2); + CWorld::Players[ScriptParams[0]].m_pPed->SetWantedLevel(ScriptParams[1]); + return 0; + case COMMAND_ALTER_WANTED_LEVEL_NO_DROP: + CollectParameters(&m_nIp, 2); + CWorld::Players[ScriptParams[0]].m_pPed->SetWantedLevelNoDrop(ScriptParams[1]); + return 0; + case COMMAND_IS_WANTED_LEVEL_GREATER: + CollectParameters(&m_nIp, 2); + UpdateCompareFlag(CWorld::Players[ScriptParams[0]].m_pPed->m_pWanted->GetWantedLevel() > ScriptParams[1]); + return 0; + case COMMAND_CLEAR_WANTED_LEVEL: + CollectParameters(&m_nIp, 1); + CWorld::Players[ScriptParams[0]].m_pPed->SetWantedLevel(0); + return 0; + case COMMAND_SET_DEATHARREST_STATE: + CollectParameters(&m_nIp, 1); + m_bDeatharrestEnabled = (ScriptParams[0] == 1); + return 0; + case COMMAND_HAS_DEATHARREST_BEEN_EXECUTED: + UpdateCompareFlag(m_bDeatharrestExecuted); + return 0; + /* + case COMMAND_ADD_AMMO_TO_PLAYER: + { + CollectParameters(&m_nIp, 3); + CWorld::Players[ScriptParams[0]].m_pPed->GrantAmmo((eWeaponType)ScriptParams[1], ScriptParams[2]); + return 0; + } + */ + case COMMAND_ADD_AMMO_TO_CHAR: + { + CollectParameters(&m_nIp, 3); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->GrantAmmo((eWeaponType)ScriptParams[1], ScriptParams[2]); + return 0; + } + //case COMMAND_ADD_AMMO_TO_CAR: + //case COMMAND_IS_PLAYER_STILL_ALIVE: + case COMMAND_IS_PLAYER_DEAD: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CWorld::Players[ScriptParams[0]].m_WBState == WBSTATE_WASTED); + return 0; + case COMMAND_IS_CHAR_DEAD: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + UpdateCompareFlag(!pPed || pPed->DyingOrDead()); + return 0; + } + case COMMAND_IS_CAR_DEAD: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + UpdateCompareFlag(!pVehicle || pVehicle->GetStatus() == STATUS_WRECKED || pVehicle->bIsDrowning); + return 0; + } + case COMMAND_SET_CHAR_THREAT_SEARCH: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->m_fearFlags |= ScriptParams[1]; + return 0; + } + //case COMMAND_SET_CHAR_THREAT_REACTION: + case COMMAND_SET_CHAR_OBJ_NO_OBJ: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bScriptObjectiveCompleted = false; + pPed->ClearObjective(); + return 0; + } + //case COMMAND_ORDER_DRIVER_OUT_OF_CAR: + //case COMMAND_ORDER_CHAR_TO_DRIVE_CAR: + //case COMMAND_ADD_PATROL_POINT: + //case COMMAND_IS_PLAYER_IN_GANGZONE: + case COMMAND_IS_PLAYER_IN_ZONE: + { + CollectParameters(&m_nIp, 1); + CPlayerInfo* pPlayer = &CWorld::Players[ScriptParams[0]]; + char label[12]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, label); + int zoneToCheck = CTheZones::FindZoneByLabelAndReturnIndex(label, ZONE_DEFAULT); + if (zoneToCheck != -1) + m_nIp += KEY_LENGTH_IN_SCRIPT; /* why only if zone != -1? */ + CVector pos = pPlayer->GetPos(); + CZone* pZone = CTheZones::GetNavigationZone(zoneToCheck); + UpdateCompareFlag(CTheZones::PointLiesWithinZone(&pos, pZone)); + return 0; + } + case COMMAND_IS_PLAYER_PRESSING_HORN: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CWorld::Players[ScriptParams[0]].m_pPed->GetPedState() == PED_DRIVING && + CPad::GetPad(ScriptParams[0])->GetHorn()); + return 0; + case COMMAND_HAS_CHAR_SPOTTED_PLAYER: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(pPed->OurPedCanSeeThisOne(CWorld::Players[ScriptParams[1]].m_pPed)); + return 0; + } +#ifdef SUPPORT_GINPUT_SCRIPT + case COMMAND_HAS_PAD_IN_HANDS: + UpdateCompareFlag(CPad::GetPad(0)->IsAffectedByController); + return 0; +#else + //case COMMAND_ORDER_CHAR_TO_BACKDOOR: +#endif + //case COMMAND_ADD_CHAR_TO_GANG: + case COMMAND_IS_CHAR_OBJECTIVE_PASSED: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(pPed->bScriptObjectiveCompleted); + return 0; + } + /* Not implemented. + case COMMAND_SET_CHAR_DRIVE_AGGRESSION: + case COMMAND_SET_CHAR_MAX_DRIVESPEED: + */ + case COMMAND_CREATE_CHAR_INSIDE_CAR: + { + CollectParameters(&m_nIp, 3); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + switch (ScriptParams[2]) { + case MI_COP: + if (ScriptParams[1] == PEDTYPE_COP) + ScriptParams[2] = COP_STREET; + break; + case MI_SWAT: + if (ScriptParams[1] == PEDTYPE_COP) + ScriptParams[2] = COP_SWAT; + break; + case MI_FBI: + if (ScriptParams[1] == PEDTYPE_COP) + ScriptParams[2] = COP_FBI; + break; + case MI_ARMY: + if (ScriptParams[1] == PEDTYPE_COP) + ScriptParams[2] = COP_ARMY; + break; + case MI_MEDIC: + if (ScriptParams[1] == PEDTYPE_EMERGENCY) + ScriptParams[2] = PEDTYPE_EMERGENCY; + break; + case MI_FIREMAN: + if (ScriptParams[1] == PEDTYPE_FIREMAN) + ScriptParams[2] = PEDTYPE_FIREMAN; + break; + default: + break; + } + CPed* pPed; + if (ScriptParams[1] == PEDTYPE_COP) + pPed = new CCopPed((eCopType)ScriptParams[2]); + else if (ScriptParams[1] == PEDTYPE_EMERGENCY || ScriptParams[1] == PEDTYPE_FIREMAN) + pPed = new CEmergencyPed(ScriptParams[2]); + else + pPed = new CCivilianPed((ePedType)ScriptParams[1], ScriptParams[2]); + pPed->CharCreatedBy = MISSION_CHAR; + pPed->bRespondsToThreats = false; + pPed->bAllowMedicsToReviveMe = false; + pPed->bIsPlayerFriend = false; + if (pVehicle->bIsBus) + pPed->bRenderPedInCar = false; + pPed->SetPosition(pVehicle->GetPosition()); + pPed->SetOrientation(0.0f, 0.0f, 0.0f); + pPed->SetPedState(PED_DRIVING); + CPopulation::ms_nTotalMissionPeds++; + script_assert(!pVehicle->pDriver); + pVehicle->pDriver = pPed; + pVehicle->pDriver->RegisterReference((CEntity**)&pVehicle->pDriver); + pPed->m_pMyVehicle = pVehicle; + pPed->m_pMyVehicle->RegisterReference((CEntity**)&pPed->m_pMyVehicle); + pPed->bInVehicle = true; + pVehicle->SetStatus(STATUS_PHYSICS); + if (!pVehicle->IsBoat()) + pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; + pVehicle->bEngineOn = true; + pPed->bUsesCollision = false; + pPed->AddInCarAnims(pVehicle, true); + pPed->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pPed->GetPosition()); + CWorld::Add(pPed); + ScriptParams[0] = CPools::GetPedPool()->GetIndex(pPed); + StoreParameters(&m_nIp, 1); + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.AddEntityToList(ScriptParams[0], CLEANUP_CHAR); + return 0; + } + case COMMAND_WARP_PLAYER_FROM_CAR_TO_COORD: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[1]; + CPlayerInfo* pPlayer = &CWorld::Players[ScriptParams[0]]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + if (pPlayer->m_pPed->bInVehicle){ + script_assert(pPlayer->m_pPed->m_pMyVehicle); + if (pPlayer->m_pPed->m_pMyVehicle->bIsBus) + pPlayer->m_pPed->bRenderPedInCar = true; + if (pPlayer->m_pPed->m_pMyVehicle->pDriver == pPlayer->m_pPed){ + pPlayer->m_pPed->m_pMyVehicle->RemoveDriver(); + pPlayer->m_pPed->m_pMyVehicle->SetStatus(STATUS_ABANDONED); + pPlayer->m_pPed->m_pMyVehicle->bEngineOn = false; + pPlayer->m_pPed->m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; + }else{ + pPlayer->m_pPed->m_pMyVehicle->RemovePassenger(pPlayer->m_pPed); + } + } + pPlayer->m_pPed->RemoveInCarAnims(); + pPlayer->m_pPed->bInVehicle = false; + pPlayer->m_pPed->m_pMyVehicle = nil; + pPlayer->m_pPed->SetPedState(PED_IDLE); + pPlayer->m_pPed->bUsesCollision = true; + pPlayer->m_pPed->SetMoveSpeed(0.0f, 0.0f, 0.0f); + pPlayer->m_pPed->ReplaceWeaponWhenExitingVehicle(); + if (pPlayer->m_pPed->m_pVehicleAnim) + pPlayer->m_pPed->m_pVehicleAnim->blendDelta = -1000.0f; + pPlayer->m_pPed->m_pVehicleAnim = nil; + pPlayer->m_pPed->SetMoveState(PEDMOVE_NONE); + CAnimManager::BlendAnimation(pPlayer->m_pPed->GetClump(), pPlayer->m_pPed->m_animGroup, ANIM_STD_IDLE, 1000.0f); + pPlayer->m_pPed->RestartNonPartialAnims(); + AudioManager.PlayerJustLeftCar(); + pos.z += pPlayer->m_pPed->GetDistanceFromCentreOfMassToBaseOfModel(); + pPlayer->m_pPed->Teleport(pos); + CTheScripts::ClearSpaceForMissionEntity(pos, pPlayer->m_pPed); + return 0; + } + //case COMMAND_MAKE_CHAR_DO_NOTHING: + default: + script_assert(0); + break; + } + return -1; +} + +#ifdef MISSION_REPLAY + +bool CRunningScript::CanAllowMissionReplay() +{ + if (AllowMissionReplay != MISSION_RETRY_STAGE_NORMAL) + return false; + for (int i = 0; i < ARRAY_SIZE(MissionScripts); i++) { + if (!CGeneral::faststricmp(m_abScriptName, MissionScripts[i])) + return true; + } + return false; +} + +uint32 AddExtraDeathDelay() +{ + return 1000; +} + +void RetryMission(int type, int unk) +{ + if (type == MISSION_RETRY_TYPE_SUGGEST_TO_PLAYER) { + doingMissionRetry = true; + FrontEndMenuManager.m_nCurrScreen = MENUPAGE_MISSION_RETRY; + FrontEndMenuManager.m_bAttemptingMissionRetry = true; + FrontEndMenuManager.RequestFrontEndStartUp(); + } + else if (type == MISSION_RETRY_TYPE_BEGIN_RESTARTING) { + doingMissionRetry = false; + AllowMissionReplay = MISSION_RETRY_STAGE_START_RESTARTING; + CTheScripts::MissionCleanUp.Process(); + } +} + +#endif diff --git a/src/miami/control/Script.h b/src/miami/control/Script.h new file mode 100644 index 00000000..7f9a7717 --- /dev/null +++ b/src/miami/control/Script.h @@ -0,0 +1,639 @@ +#pragma once +#include "Font.h" +#include "PedType.h" +#include "Text.h" +#include "Sprite2d.h" + +class CEntity; +class CBuilding; +class CPhysical; +class CVehicle; +class CPed; +class CObject; +class CPlayerInfo; + +class CRunningScript; + +extern int32 ScriptParams[32]; + +void FlushLog(); +#define script_assert(_Expression) FlushLog(); assert(_Expression); + +#define PICKUP_PLACEMENT_OFFSET (0.5f) +#define PED_FIND_Z_OFFSET (5.0f) +#define COP_PED_FIND_Z_OFFSET (10.0f) + +#define UPSIDEDOWN_UP_THRESHOLD (-0.97f) +#define UPSIDEDOWN_MOVE_SPEED_THRESHOLD (0.01f) +#define UPSIDEDOWN_TURN_SPEED_THRESHOLD (0.02f) +#define UPSIDEDOWN_TIMER_THRESHOLD (1000) + +#define SPHERE_MARKER_R (252) +#define SPHERE_MARKER_G (138) +#define SPHERE_MARKER_B (242) +#define SPHERE_MARKER_A (228) +#define SPHERE_MARKER_PULSE_PERIOD 2048 +#define SPHERE_MARKER_PULSE_FRACTION 0.1f + +#ifdef USE_PRECISE_MEASUREMENT_CONVERTION +#define MILES_IN_METER (0.000621371192f) +#define METERS_IN_FOOT (0.3048f) +#define FEET_IN_METER (3.28084f) +#else +#define MILES_IN_METER (1 / 1670.f) +#define METERS_IN_FOOT (0.3f) +#define FEET_IN_METER (3.33f) +#endif + +#define KEY_LENGTH_IN_SCRIPT (8) + +//#define GTA_SCRIPT_COLLECTIVE + +struct intro_script_rectangle +{ + bool m_bIsUsed; + bool m_bBeforeFade; + int16 m_nTextureId; + CRect m_sRect; + CRGBA m_sColor; + + intro_script_rectangle() { } + ~intro_script_rectangle() { } +}; + +VALIDATE_SIZE(intro_script_rectangle, 0x18); + +enum { + SCRIPT_TEXT_MAX_LENGTH = 100 +}; + +struct intro_text_line +{ + float m_fScaleX; + float m_fScaleY; + CRGBA m_sColor; + bool m_bJustify; + bool m_bCentered; + bool m_bBackground; + bool m_bBackgroundOnly; + float m_fWrapX; + float m_fCenterSize; + CRGBA m_sBackgroundColor; + bool m_bTextProportional; + bool m_bTextBeforeFade; + bool m_bRightJustify; + int32 m_nFont; + float m_fAtX; + float m_fAtY; + wchar m_Text[SCRIPT_TEXT_MAX_LENGTH]; + + intro_text_line() { } + ~intro_text_line() { } + + void Reset() + { + m_fScaleX = 0.48f; + m_fScaleY = 1.12f; + m_sColor = CRGBA(225, 225, 225, 255); + m_bJustify = false; + m_bRightJustify = false; + m_bCentered = false; + m_bBackground = false; + m_bBackgroundOnly = false; + m_fWrapX = 182.0f; + m_fCenterSize = DEFAULT_SCREEN_WIDTH; + m_sBackgroundColor = CRGBA(128, 128, 128, 128); + m_bTextProportional = true; + m_bTextBeforeFade = false; + m_nFont = FONT_STANDARD; + m_fAtX = 0.0f; + m_fAtY = 0.0f; + memset(&m_Text, 0, sizeof(m_Text)); + } +}; + +VALIDATE_SIZE(intro_text_line, 0x414); + +struct script_sphere_struct +{ + bool m_bInUse; + uint16 m_Index; + uint32 m_Id; + CVector m_vecCenter; + float m_fRadius; + + script_sphere_struct() { } +}; + +struct CStoredLine +{ + CVector vecInf; + CVector vecSup; + uint32 color1; + uint32 color2; +}; + +enum { + CLEANUP_UNUSED = 0, + CLEANUP_CAR, + CLEANUP_CHAR, + CLEANUP_OBJECT +}; + +struct cleanup_entity_struct +{ + uint8 type; + int32 id; +}; + +enum { + MAX_CLEANUP = 50, + MAX_UPSIDEDOWN_CAR_CHECKS = 6, + MAX_STUCK_CAR_CHECKS = 16 +}; + +class CMissionCleanup +{ +public: + cleanup_entity_struct m_sEntities[MAX_CLEANUP]; + uint8 m_nCount; + + CMissionCleanup(); + + void Init(); + cleanup_entity_struct* FindFree(); + void AddEntityToList(int32, uint8); + void RemoveEntityFromList(int32, uint8); + void Process(); + void CheckIfCollisionHasLoadedForMissionObjects(); +}; + +struct upsidedown_car_data +{ + int32 m_nVehicleIndex; + uint32 m_nUpsideDownTimer; +}; + +class CUpsideDownCarCheck +{ + upsidedown_car_data m_sCars[MAX_UPSIDEDOWN_CAR_CHECKS]; + +public: + void Init(); + bool IsCarUpsideDown(int32); + bool IsCarUpsideDown(CVehicle*); + void UpdateTimers(); + bool AreAnyCarsUpsideDown(); + void AddCarToCheck(int32); + void RemoveCarFromCheck(int32); + bool HasCarBeenUpsideDownForAWhile(int32); +}; + +struct stuck_car_data +{ + int32 m_nVehicleIndex; + CVector m_vecPos; + int32 m_nLastCheck; + float m_fRadius; + uint32 m_nStuckTime; + bool m_bStuck; + + stuck_car_data() { } + void Reset(); +}; + +class CStuckCarCheck +{ + stuck_car_data m_sCars[MAX_STUCK_CAR_CHECKS]; + +public: + void Init(); + void Process(); + void AddCarToCheck(int32, float, uint32); + void RemoveCarFromCheck(int32); + bool HasCarBeenStuckForAWhile(int32); +}; + +enum { + ARGUMENT_END = 0, + ARGUMENT_INT32, + ARGUMENT_GLOBALVAR, + ARGUMENT_LOCALVAR, + ARGUMENT_INT8, + ARGUMENT_INT16, + ARGUMENT_FLOAT +}; + +struct tCollectiveData +{ + int32 colIndex; + int32 pedIndex; +}; + +enum { + USED_OBJECT_NAME_LENGTH = 24 +}; + +struct tUsedObject +{ + char name[USED_OBJECT_NAME_LENGTH]; + int32 index; +}; + +struct tBuildingSwap +{ + CBuilding* m_pBuilding; + int32 m_nNewModel; + int32 m_nOldModel; +}; + + +enum { + MAX_STACK_DEPTH = 6, + NUM_LOCAL_VARS = 16, + NUM_TIMERS = 2 +}; + +class CRunningScript +{ + enum { + ANDOR_NONE = 0, + ANDS_1 = 1, + ANDS_2, + ANDS_3, + ANDS_4, + ANDS_5, + ANDS_6, + ANDS_7, + ANDS_8, + ORS_1 = 21, + ORS_2, + ORS_3, + ORS_4, + ORS_5, + ORS_6, + ORS_7, + ORS_8 + }; + +public: + CRunningScript* next; + CRunningScript* prev; + char m_abScriptName[8]; + uint32 m_nIp; + uint32 m_anStack[MAX_STACK_DEPTH]; + uint16 m_nStackPointer; + int32 m_anLocalVariables[NUM_LOCAL_VARS + NUM_TIMERS]; + bool m_bIsActive; + bool m_bCondResult; + bool m_bIsMissionScript; + bool m_bSkipWakeTime; + uint32 m_nWakeTime; + uint16 m_nAndOrState; + bool m_bNotFlag; + bool m_bDeatharrestEnabled; + bool m_bDeatharrestExecuted; + bool m_bMissionFlag; + +public: + void SetIP(uint32 ip) { m_nIp = ip; } + CRunningScript* GetNext() const { return next; } + + void Save(uint8*& buf); + void Load(uint8*& buf); + + void UpdateTimers(float timeStep) { + m_anLocalVariables[NUM_LOCAL_VARS] += timeStep; + m_anLocalVariables[NUM_LOCAL_VARS + 1] += timeStep; + } + + void Init(); + void Process(); + + void RemoveScriptFromList(CRunningScript**); + void AddScriptToList(CRunningScript**); + + static const uint32 nSaveStructSize; + + void CollectParameters(uint32*, int16); + int32 CollectNextParameterWithoutIncreasingPC(uint32); + int32* GetPointerToScriptVariable(uint32*, int16); + void StoreParameters(uint32*, int16); + + int8 ProcessOneCommand(); + void DoDeatharrestCheck(); + void UpdateCompareFlag(bool); + int16 GetPadState(uint16, uint16); + + int8 ProcessCommands0To99(int32); + int8 ProcessCommands100To199(int32); + int8 ProcessCommands200To299(int32); + int8 ProcessCommands300To399(int32); + int8 ProcessCommands400To499(int32); + int8 ProcessCommands500To599(int32); + int8 ProcessCommands600To699(int32); + int8 ProcessCommands700To799(int32); + int8 ProcessCommands800To899(int32); + int8 ProcessCommands900To999(int32); + int8 ProcessCommands1000To1099(int32); + int8 ProcessCommands1100To1199(int32); + int8 ProcessCommands1200To1299(int32); + int8 ProcessCommands1300To1399(int32); + int8 ProcessCommands1400To1499(int32); + + void LocatePlayerCommand(int32, uint32*); + void LocatePlayerCharCommand(int32, uint32*); + void LocatePlayerCarCommand(int32, uint32*); + void LocateCharCommand(int32, uint32*); + void LocateCharCharCommand(int32, uint32*); + void LocateCharCarCommand(int32, uint32*); + void LocateCharObjectCommand(int32, uint32*); + void LocateCarCommand(int32, uint32*); + void LocateSniperBulletCommand(int32, uint32*); + void PlayerInAreaCheckCommand(int32, uint32*); + void PlayerInAngledAreaCheckCommand(int32, uint32*); + void CharInAreaCheckCommand(int32, uint32*); + void CarInAreaCheckCommand(int32, uint32*); + void LocateObjectCommand(int32, uint32*); + void ObjectInAreaCheckCommand(int32, uint32*); + +#ifdef GTA_SCRIPT_COLLECTIVE + void LocateCollectiveCommand(int32, uint32*); + void LocateCollectiveCharCommand(int32, uint32*); + void LocateCollectiveCarCommand(int32, uint32*); + void LocateCollectivePlayerCommand(int32, uint32*); + void CollectiveInAreaCheckCommand(int32, uint32*); +#endif + +#ifdef MISSION_REPLAY + bool CanAllowMissionReplay(); +#endif + +#ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT + int CollectParameterForDebug(char* buf, bool& var); + void GetStoredParameterForDebug(char* buf); + void LogOnStartProcessing(); + void LogBeforeProcessingCommand(int32 command); + void LogAfterProcessingCommand(int32 command); + + static char commandInfo[]; + static uint32 storedIp; + +#endif + + float LimitAngleOnCircle(float angle) { return angle < 0.0f ? angle + 360.0f : angle; } + + bool ThisIsAValidRandomCop(uint32 mi, int cop, int swat, int fbi, int army, int miami); + bool ThisIsAValidRandomPed(uint32 pedtype, int civ, int gang, int criminal); + + bool CheckDamagedWeaponType(int32 actual, int32 type); + +}; + + +enum { + VAR_LOCAL = 1, + VAR_GLOBAL = 2, +}; + +enum { +#ifdef PS2 + SIZE_MAIN_SCRIPT = 205512, +#else + SIZE_MAIN_SCRIPT = 225512, +#endif + SIZE_MISSION_SCRIPT = 35000, + SIZE_SCRIPT_SPACE = SIZE_MAIN_SCRIPT + SIZE_MISSION_SCRIPT +}; + +enum { + MAX_NUM_SCRIPTS = 128, + MAX_NUM_INTRO_TEXT_LINES = 48, + MAX_NUM_INTRO_RECTANGLES = 16, + MAX_NUM_SCRIPT_SRPITES = 16, + MAX_NUM_SCRIPT_SPHERES = 16, + MAX_NUM_USED_OBJECTS = 220, + MAX_NUM_MISSION_SCRIPTS = 120, + MAX_NUM_BUILDING_SWAPS = 25, + MAX_NUM_INVISIBILITY_SETTINGS = 20, + MAX_NUM_STORED_LINES = 1024 +}; + +class CTheScripts +{ +public: + static uint8 ScriptSpace[SIZE_SCRIPT_SPACE]; + static CRunningScript ScriptsArray[MAX_NUM_SCRIPTS]; + static intro_text_line IntroTextLines[MAX_NUM_INTRO_TEXT_LINES]; + static intro_script_rectangle IntroRectangles[MAX_NUM_INTRO_RECTANGLES]; + static CSprite2d ScriptSprites[MAX_NUM_SCRIPT_SRPITES]; + static script_sphere_struct ScriptSphereArray[MAX_NUM_SCRIPT_SPHERES]; + static tUsedObject UsedObjectArray[MAX_NUM_USED_OBJECTS]; + static int32 MultiScriptArray[MAX_NUM_MISSION_SCRIPTS]; + static tBuildingSwap BuildingSwapArray[MAX_NUM_BUILDING_SWAPS]; + static CEntity* InvisibilitySettingArray[MAX_NUM_INVISIBILITY_SETTINGS]; + static CStoredLine aStoredLines[MAX_NUM_STORED_LINES]; + static bool DbgFlag; + static uint32 OnAMissionFlag; + static CMissionCleanup MissionCleanUp; + static CStuckCarCheck StuckCars; + static CUpsideDownCarCheck UpsideDownCars; + static int32 StoreVehicleIndex; + static bool StoreVehicleWasRandom; + static CRunningScript *pIdleScripts; + static CRunningScript *pActiveScripts; + static int32 NextFreeCollectiveIndex; + static int32 LastRandomPedId; + static uint16 NumberOfUsedObjects; + static bool bAlreadyRunningAMissionScript; + static bool bUsingAMultiScriptFile; + static uint16 NumberOfMissionScripts; + static uint32 LargestMissionScriptSize; + static uint32 MainScriptSize; + static uint8 FailCurrentMission; + static uint16 NumScriptDebugLines; + static uint16 NumberOfIntroRectanglesThisFrame; + static uint16 NumberOfIntroTextLinesThisFrame; + static uint8 UseTextCommands; + static uint16 CommandsExecuted; + static uint16 ScriptsUpdated; + static uint32 LastMissionPassedTime; + static uint16 NumberOfExclusiveMissionScripts; +#if (defined GTA_PC && !defined GTAVC_JP_PATCH || defined GTA_XBOX || defined SUPPORT_XBOX_SCRIPT || defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) +#define CARDS_IN_SUIT (13) +#define NUM_SUITS (4) +#define MAX_DECKS (6) +#define CARDS_IN_DECK (CARDS_IN_SUIT * NUM_SUITS) +#define CARDS_IN_STACK (CARDS_IN_DECK * MAX_DECKS) + static int16 CardStack[CARDS_IN_STACK]; + static int16 CardStackPosition; +#endif + static bool bPlayerIsInTheStatium; + static uint8 RiotIntensity; + static bool bPlayerHasMetDebbieHarry; + + static void Init(); + static void Process(); + + static CRunningScript* StartTestScript(); + static bool IsPlayerOnAMission(); + static void ClearSpaceForMissionEntity(const CVector&, CEntity*); + + static void UndoBuildingSwaps(); + static void UndoEntityInvisibilitySettings(); + + static void ScriptDebugLine3D(float x1, float y1, float z1, float x2, float y2, float z2, uint32 col, uint32 col2); + static void RenderTheScriptDebugLines(); + + static void SaveAllScripts(uint8*, uint32*); + static void LoadAllScripts(uint8*, uint32); + + static bool IsDebugOn() { return DbgFlag; }; + static void InvertDebugFlag() { DbgFlag = !DbgFlag; } + + static int32* GetPointerToScriptVariable(int32 offset) { assert(offset >= 8 && offset < CTheScripts::GetSizeOfVariableSpace()); return (int32*)&ScriptSpace[offset]; } + + static int32 Read4BytesFromScript(uint32* pIp) { + int32 retval = ScriptSpace[*pIp + 3] << 24 | ScriptSpace[*pIp + 2] << 16 | ScriptSpace[*pIp + 1] << 8 | ScriptSpace[*pIp]; + *pIp += 4; + return retval; + } + static int16 Read2BytesFromScript(uint32* pIp) { + int16 retval = ScriptSpace[*pIp + 1] << 8 | ScriptSpace[*pIp]; + *pIp += 2; + return retval; + } + static int8 Read1ByteFromScript(uint32* pIp) { + int8 retval = ScriptSpace[*pIp]; + *pIp += 1; + return retval; + } + static float ReadFloatFromScript(uint32* pIp) { + return Read2BytesFromScript(pIp) / 16.0f; + } + static void ReadTextLabelFromScript(uint32* pIp, char* buf) { + strncpy(buf, (const char*)&CTheScripts::ScriptSpace[*pIp], KEY_LENGTH_IN_SCRIPT); + } + static wchar* GetTextByKeyFromScript(uint32* pIp) { + wchar* text = TheText.Get((const char*)&CTheScripts::ScriptSpace[*pIp]); + *pIp += KEY_LENGTH_IN_SCRIPT; + return text; + } + static int32 GetSizeOfVariableSpace() + { + uint32 tmp = 3; + return Read4BytesFromScript(&tmp); + } + + static CRunningScript* StartNewScript(uint32); + + static void CleanUpThisVehicle(CVehicle*); + static void CleanUpThisPed(CPed*); + static void CleanUpThisObject(CObject*); + + static bool IsPedStopped(CPed*); + static bool IsPlayerStopped(CPlayerInfo*); + static bool IsVehicleStopped(CVehicle*); + + static void PrintListSizes(); + static void ReadObjectNamesFromScript(); + static void UpdateObjectIndices(); + static void ReadMultiScriptFileOffsetsFromScript(); + static void DrawScriptSpheres(); + static void HighlightImportantArea(uint32, float, float, float, float, float); + static void HighlightImportantAngledArea(uint32, float, float, float, float, float, float, float, float, float); + static void DrawDebugSquare(float, float, float, float); + static void DrawDebugAngledSquare(float, float, float, float, float, float, float, float); + static void DrawDebugCube(float, float, float, float, float, float); + static void DrawDebugAngledCube(float, float, float, float, float, float, float, float, float, float); + + static void AddToInvisibilitySwapArray(CEntity*, bool); + static void AddToBuildingSwapArray(CBuilding*, int32, int32); + + static int32 GetActualScriptSphereIndex(int32 index); + static int32 AddScriptSphere(int32 id, CVector pos, float radius); + static int32 GetNewUniqueScriptSphereIndex(int32 index); + static void RemoveScriptSphere(int32 index); + static void RemoveScriptTextureDictionary(); +public: + static void RemoveThisPed(CPed* pPed); + + static uint32& GetLastMissionPassedTime() { return LastMissionPassedTime; } +#ifdef MISSION_SWITCHER + static void SwitchToMission(int32 mission); +#endif + +#ifdef GTA_SCRIPT_COLLECTIVE + static void AdvanceCollectiveIndex() + { + if (NextFreeCollectiveIndex == INT32_MAX) + NextFreeCollectiveIndex = 0; + else + NextFreeCollectiveIndex++; + } + + static int AddPedsInVehicleToCollective(int); + static int AddPedsInAreaToCollective(float, float, float, float); + static int FindFreeSlotInCollectiveArray(); + static void SetObjectiveForAllPedsInCollective(int, eObjective, int16, int16); + static void SetObjectiveForAllPedsInCollective(int, eObjective, CVector, float); + static void SetObjectiveForAllPedsInCollective(int, eObjective, CVector); + static void SetObjectiveForAllPedsInCollective(int, eObjective, void*); + static void SetObjectiveForAllPedsInCollective(int, eObjective); +#endif + +#ifdef USE_MISSION_REPLAY_OVERRIDE_FOR_NON_MOBILE_SCRIPT + static bool MissionSupportsMissionReplay(int index) + { + return index >= 3 && index <= 35 || index >= 51 && index <= 65 || index >= 67 && index <= 74 || index >= 83 && index <= 87; + } +#endif + +#ifdef USE_DEBUG_SCRIPT_LOADER + static int ScriptToLoad; + static int OpenScript(); +#endif + +#ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT + static void LogAfterScriptInitializing(); + static void LogBeforeScriptProcessing(); + static void LogAfterScriptProcessing(); +#endif +}; + +#ifdef MISSION_REPLAY +extern int AllowMissionReplay; +extern uint32 WaitForMissionActivate; +extern uint32 WaitForSave; +extern uint32 MissionStartTime; +extern int missionRetryScriptIndex; +extern bool doingMissionRetry; +extern bool gbTryingPorn4Again; +extern int IsInAmmunation; +extern int MissionSkipLevel; + +#ifdef USE_MISSION_REPLAY_OVERRIDE_FOR_NON_MOBILE_SCRIPT +extern bool UsingMobileScript; +extern bool AlreadySavedGame; +#endif + +uint32 AddExtraDeathDelay(); +void RetryMission(int, int unk = 0); + +enum { + MISSION_RETRY_TYPE_SUGGEST_TO_PLAYER = 0, + MISSION_RETRY_TYPE_1, + MISSION_RETRY_TYPE_BEGIN_RESTARTING +}; + +enum { + MISSION_RETRY_STAGE_NORMAL = 0, + MISSION_RETRY_STAGE_WAIT_FOR_SCRIPT_TO_TERMINATE, + MISSION_RETRY_STAGE_START_PROCESSING, + MISSION_RETRY_STAGE_WAIT_FOR_DELAY, + MISSION_RETRY_STAGE_WAIT_FOR_MENU, + MISSION_RETRY_STAGE_WAIT_FOR_USER, + MISSION_RETRY_STAGE_START_RESTARTING, + MISSION_RETRY_STAGE_WAIT_FOR_TIMER_AFTER_RESTART, +}; +#endif diff --git a/src/miami/control/Script2.cpp b/src/miami/control/Script2.cpp new file mode 100644 index 00000000..4e7a1c3e --- /dev/null +++ b/src/miami/control/Script2.cpp @@ -0,0 +1,1557 @@ +#include "common.h" + +#include "Script.h" +#include "ScriptCommands.h" + +#include "Camera.h" +#include "CarCtrl.h" +#include "CarGen.h" +#include "CivilianPed.h" +#include "CopPed.h" +#include "Cranes.h" +#include "DMAudio.h" +#include "EmergencyPed.h" +#include "Garages.h" +#include "General.h" +#include "Messages.h" +#include "Pad.h" +#include "PedRoutes.h" +#include "Pools.h" +#include "Population.h" +#include "Radar.h" +#include "Restart.h" +#include "Shadows.h" +#include "User.h" +#include "Wanted.h" +#include "WaterLevel.h" +#include "Weather.h" +#include "World.h" +#include "Zones.h" + +int8 CRunningScript::ProcessCommands300To399(int32 command) +{ + switch (command) { + //case COMMAND_SET_CHAR_INVINCIBLE: + //case COMMAND_SET_PLAYER_INVINCIBLE: + //case COMMAND_SET_CHAR_GRAPHIC_TYPE: + //case COMMAND_SET_PLAYER_GRAPHIC_TYPE: + /* + case COMMAND_HAS_PLAYER_BEEN_ARRESTED: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CWorld::Players[ScriptParams[0]].m_WBState == WBSTATE_BUSTED); + return 0; + */ + //case COMMAND_STOP_CHAR_DRIVING: + //case COMMAND_KILL_CHAR: + //case COMMAND_SET_FAVOURITE_CAR_MODEL_FOR_CHAR: + //case COMMAND_SET_CHAR_OCCUPATION: + /* + case COMMAND_CHANGE_CAR_LOCK: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->m_nDoorLock = (eCarLock)ScriptParams[1]; + return 0; + } + case COMMAND_SHAKE_CAM_WITH_POINT: + CollectParameters(&m_nIp, 4); + TheCamera.CamShake(ScriptParams[0] / 1000.0f, + *(float*)&ScriptParams[1], + *(float*)&ScriptParams[2], + *(float*)&ScriptParams[3]); + return 0; + */ + case COMMAND_IS_CAR_MODEL: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + UpdateCompareFlag(pVehicle->GetModelIndex() == ScriptParams[1]); + return 0; + } + //case COMMAND_IS_CAR_REMAP: + //case COMMAND_HAS_CAR_JUST_SUNK: + //case COMMAND_SET_CAR_NO_COLLIDE: + /* + case COMMAND_IS_CAR_DEAD_IN_AREA_2D: + { + CollectParameters(&m_nIp, 6); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + float x1 = *(float*)&ScriptParams[1]; + float y1 = *(float*)&ScriptParams[2]; + float x2 = *(float*)&ScriptParams[3]; + float y2 = *(float*)&ScriptParams[4]; + UpdateCompareFlag(pVehicle->GetStatus() == STATUS_WRECKED && + pVehicle->IsWithinArea(x1, y1, x2, y2)); + if (ScriptParams[5]) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) + CTheScripts::DrawDebugSquare(x1, y1, x2, y2); + return 0; + } + case COMMAND_IS_CAR_DEAD_IN_AREA_3D: + { + CollectParameters(&m_nIp, 8); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + float x1 = *(float*)&ScriptParams[1]; + float y1 = *(float*)&ScriptParams[2]; + float z1 = *(float*)&ScriptParams[3]; + float x2 = *(float*)&ScriptParams[4]; + float y2 = *(float*)&ScriptParams[5]; + float z2 = *(float*)&ScriptParams[6]; + UpdateCompareFlag(pVehicle->GetStatus() == STATUS_WRECKED && + pVehicle->IsWithinArea(x1, y1, z1, x2, y2, z2)); + if (ScriptParams[7]) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, (z1 + z2) / 2); + if (CTheScripts::DbgFlag) + CTheScripts::DrawDebugCube(x1, y1, z1, x2, y2, z2); + return 0; + } + */ + //case COMMAND_IS_TRAILER_ATTACHED: + //case COMMAND_IS_CAR_ON_TRAILER: + //case COMMAND_HAS_CAR_GOT_WEAPON: + //case COMMAND_PARK: + //case COMMAND_HAS_PARK_FINISHED: + //case COMMAND_KILL_ALL_PASSENGERS: + //case COMMAND_SET_CAR_BULLETPROOF: + //case COMMAND_SET_CAR_FLAMEPROOF: + //case COMMAND_SET_CAR_ROCKETPROOF: + //case COMMAND_IS_CARBOMB_ACTIVE: + //case COMMAND_GIVE_CAR_ALARM: + //case COMMAND_PUT_CAR_ON_TRAILER: + /* + case COMMAND_IS_CAR_CRUSHED: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CGarages::HasCarBeenCrushed(ScriptParams[0])); + return 0; + */ + //case COMMAND_CREATE_GANG_CAR: + case COMMAND_CREATE_CAR_GENERATOR: + { + CollectParameters(&m_nIp, 12); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z > MAP_Z_LOW_LIMIT) + pos.z += 0.015f; + ScriptParams[0] = CTheCarGenerators::CreateCarGenerator( + pos.x, pos.y, pos.z, *(float*)&ScriptParams[3], + ScriptParams[4], ScriptParams[5], ScriptParams[6], ScriptParams[7], + ScriptParams[8], ScriptParams[9], ScriptParams[10], ScriptParams[11]); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_SWITCH_CAR_GENERATOR: + { + CollectParameters(&m_nIp, 2); + CCarGenerator* pCarGen = &CTheCarGenerators::CarGeneratorArray[ScriptParams[0]]; + if (ScriptParams[1] == 0){ + pCarGen->SwitchOff(); + }else if (ScriptParams[1] <= 100){ + pCarGen->SwitchOn(); + pCarGen->SetUsesRemaining(ScriptParams[1]); + }else{ + pCarGen->SwitchOn(); + } + return 0; + } + /* + case COMMAND_ADD_PAGER_MESSAGE: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 3); + CUserDisplay::Pager.AddMessage(text, ScriptParams[0], ScriptParams[1], ScriptParams[2]); + return 0; + } + */ + case COMMAND_DISPLAY_ONSCREEN_TIMER: + { + script_assert(CTheScripts::ScriptSpace[m_nIp] == ARGUMENT_GLOBALVAR); + m_nIp++; + uint16 offset = CTheScripts::Read2BytesFromScript(&m_nIp); + CollectParameters(&m_nIp, 1); + CUserDisplay::OnscnTimer.AddClock(offset, nil, ScriptParams[0] != 0); + return 0; + } + case COMMAND_CLEAR_ONSCREEN_TIMER: + { + script_assert(CTheScripts::ScriptSpace[m_nIp] == ARGUMENT_GLOBALVAR); + m_nIp++; + CUserDisplay::OnscnTimer.ClearClock((uint16)CTheScripts::Read2BytesFromScript(&m_nIp)); + return 0; + } + case COMMAND_DISPLAY_ONSCREEN_COUNTER: + { + script_assert(CTheScripts::ScriptSpace[m_nIp] == ARGUMENT_GLOBALVAR); + m_nIp++; + int16 counter = CTheScripts::Read2BytesFromScript(&m_nIp); + CollectParameters(&m_nIp, 1); + CUserDisplay::OnscnTimer.AddCounter(counter, ScriptParams[0], nil, 0); + return 0; + } + case COMMAND_CLEAR_ONSCREEN_COUNTER: + { + script_assert(CTheScripts::ScriptSpace[m_nIp] == ARGUMENT_GLOBALVAR); + m_nIp++; + CUserDisplay::OnscnTimer.ClearCounter((uint16)CTheScripts::Read2BytesFromScript(&m_nIp)); + return 0; + } + case COMMAND_SET_ZONE_CAR_INFO: + { + char label[12]; + int16 gangDensities[NUM_GANGS] = { 0 }; + int i; + + CTheScripts::ReadTextLabelFromScript(&m_nIp, label); + m_nIp += KEY_LENGTH_IN_SCRIPT; + CollectParameters(&m_nIp, 12); + for (i = 0; i < NUM_GANGS; i++) + gangDensities[i] = ScriptParams[i + 2]; + int zone = CTheZones::FindZoneByLabelAndReturnIndex(label, ZONE_INFO); + for (int i = 0; i < NUM_GANGS; i++) { + if (gangDensities[i] != 0 && CGangs::GetGangInfo(i)->m_nVehicleMI == -1) + debug("SET_ZONE_CAR_INFO - Gang %d car ratio should be 0 in %s zone\n", i + 1, label); + } + if (zone < 0) { + debug("Couldn't find zone - %s\n", label); + return 0; + } + while (zone >= 0) { + CTheZones::SetZoneCarInfo(zone, ScriptParams[0], ScriptParams[1], ScriptParams[11], gangDensities); + zone = CTheZones::FindNextZoneByLabelAndReturnIndex(label, ZONE_INFO); + } + return 0; + } + //case COMMAND_IS_CHAR_IN_GANG_ZONE: + case COMMAND_IS_CHAR_IN_ZONE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + char label[12]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, label); + int zone = CTheZones::FindZoneByLabelAndReturnIndex(label, ZONE_DEFAULT); + if (zone != -1) + m_nIp += KEY_LENGTH_IN_SCRIPT; + CVector pos = pPed->bInVehicle ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); + UpdateCompareFlag(CTheZones::PointLiesWithinZone(&pos, CTheZones::GetNavigationZone(zone))); + return 0; + } + //case COMMAND_SET_CAR_DENSITY: + //case COMMAND_SET_PED_DENSITY: + case COMMAND_POINT_CAMERA_AT_PLAYER: + { + CollectParameters(&m_nIp, 3); + // ScriptParams[0] is unused. + TheCamera.TakeControl(nil, ScriptParams[1], ScriptParams[2], CAMCONTROL_SCRIPT); + return 0; + } + case COMMAND_POINT_CAMERA_AT_CAR: + { + CollectParameters(&m_nIp, 3); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + if (pVehicle) + TheCamera.TakeControl(pVehicle, ScriptParams[1], ScriptParams[2], CAMCONTROL_SCRIPT); + return 0; + } + case COMMAND_POINT_CAMERA_AT_CHAR: + { + CollectParameters(&m_nIp, 3); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + if (pPed) + TheCamera.TakeControl(pPed, ScriptParams[1], ScriptParams[2], CAMCONTROL_SCRIPT); + return 0; + } + case COMMAND_RESTORE_CAMERA: + TheCamera.Restore(); + return 0; + /* + case COMMAND_SHAKE_PAD: + CPad::GetPad(ScriptParams[0])->StartShake(ScriptParams[1], ScriptParams[2]); + return 0; + */ + case COMMAND_SET_ZONE_PED_INFO: + { + char label[12]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, label); + m_nIp += KEY_LENGTH_IN_SCRIPT; + CollectParameters(&m_nIp, 12); + int16 zone = CTheZones::FindZoneByLabelAndReturnIndex(label, ZONE_INFO); + if (zone < 0) { + debug("Couldn't find zone - %s\n", label); + return 0; + } + while (zone >= 0) { + CTheZones::SetZonePedInfo(zone, ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], + ScriptParams[4], ScriptParams[5], ScriptParams[6], ScriptParams[7], ScriptParams[8], ScriptParams[9], ScriptParams[10], ScriptParams[11]); + zone = CTheZones::FindNextZoneByLabelAndReturnIndex(label, ZONE_INFO); + } + return 0; + } + case COMMAND_SET_TIME_SCALE: + CollectParameters(&m_nIp, 1); + CTimer::SetTimeScale(*(float*)&ScriptParams[0]); + return 0; + /* + case COMMAND_IS_CAR_IN_AIR: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle && pVehicle->IsCar()); + CAutomobile* pCar = (CAutomobile*)pVehicle; + UpdateCompareFlag(pCar->GetAllWheelsOffGround()); + return 0; + } + */ + case COMMAND_SET_FIXED_CAMERA_POSITION: + { + CollectParameters(&m_nIp, 6); + TheCamera.SetCamPositionForFixedMode( + CVector(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1], *(float*)&ScriptParams[2]), + CVector(*(float*)&ScriptParams[3], *(float*)&ScriptParams[4], *(float*)&ScriptParams[5])); + return 0; + } + case COMMAND_POINT_CAMERA_AT_POINT: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + TheCamera.TakeControlNoEntity(pos, ScriptParams[3], CAMCONTROL_SCRIPT); + return 0; + } + case COMMAND_ADD_BLIP_FOR_CAR_OLD: + { + CollectParameters(&m_nIp, 3); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + ScriptParams[0] = CRadar::SetEntityBlip(BLIP_CAR, ScriptParams[0], ScriptParams[1], (eBlipDisplay)ScriptParams[2]); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_ADD_BLIP_FOR_CHAR_OLD: + { + CollectParameters(&m_nIp, 3); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + ScriptParams[0] = CRadar::SetEntityBlip(BLIP_CHAR, ScriptParams[0], ScriptParams[1], (eBlipDisplay)ScriptParams[2]); + StoreParameters(&m_nIp, 1); + return 0; + } + /* + case COMMAND_ADD_BLIP_FOR_OBJECT_OLD: + { + CollectParameters(&m_nIp, 3); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + ScriptParams[0] = CRadar::SetEntityBlip(BLIP_OBJECT, ScriptParams[0], ScriptParams[1], (eBlipDisplay)ScriptParams[2]); + StoreParameters(&m_nIp, 1); + return 0; + } + */ + case COMMAND_REMOVE_BLIP: + CollectParameters(&m_nIp, 1); + CRadar::ClearBlip(ScriptParams[0]); + return 0; + case COMMAND_CHANGE_BLIP_COLOUR: + CollectParameters(&m_nIp, 2); + CRadar::ChangeBlipColour(ScriptParams[0], ScriptParams[1]); + return 0; + case COMMAND_DIM_BLIP: + CollectParameters(&m_nIp, 2); + CRadar::ChangeBlipBrightness(ScriptParams[0], ScriptParams[1]); + return 0; + case COMMAND_ADD_BLIP_FOR_COORD_OLD: + { + CollectParameters(&m_nIp, 5); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + ScriptParams[0] = CRadar::SetCoordBlip(BLIP_COORD, pos, ScriptParams[3], (eBlipDisplay)ScriptParams[4]); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_CHANGE_BLIP_SCALE: + CollectParameters(&m_nIp, 2); + CRadar::ChangeBlipScale(ScriptParams[0], ScriptParams[1]); + return 0; + case COMMAND_SET_FADING_COLOUR: + CollectParameters(&m_nIp, 3); + TheCamera.SetFadeColour(ScriptParams[0], ScriptParams[1], ScriptParams[2]); + return 0; + case COMMAND_DO_FADE: + CollectParameters(&m_nIp, 2); + TheCamera.Fade(ScriptParams[0] / 1000.0f, ScriptParams[1]); + return 0; + case COMMAND_GET_FADING_STATUS: + UpdateCompareFlag(TheCamera.GetFading()); + return 0; + case COMMAND_ADD_HOSPITAL_RESTART: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[0]; + float angle = *(float*)&ScriptParams[3]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CRestart::AddHospitalRestartPoint(pos, angle); + return 0; + } + case COMMAND_ADD_POLICE_RESTART: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[0]; + float angle = *(float*)&ScriptParams[3]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CRestart::AddPoliceRestartPoint(pos, angle); + return 0; + } + case COMMAND_OVERRIDE_NEXT_RESTART: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[0]; + float angle = *(float*)&ScriptParams[3]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CRestart::OverrideNextRestart(pos, angle); + return 0; + } + /* + case COMMAND_DRAW_SHADOW: + { + CollectParameters(&m_nIp, 10); + CVector pos = *(CVector*)&ScriptParams[1]; + float angle = *(float*)&ScriptParams[4]; + float length = *(float*)&ScriptParams[5]; + float x, y; + if (angle != 0.0f){ + y = Cos(angle) * length; + x = Sin(angle) * length; + }else{ + y = length; + x = 0.0f; + } + float frontX = -x; + float frontY = y; + float sideX = y; + float sideY = x; + CShadows::StoreShadowToBeRendered(ScriptParams[0], &pos, frontX, frontY, sideX, sideY, + ScriptParams[6], ScriptParams[7], ScriptParams[8], ScriptParams[9]); + return 0; + } + */ + case COMMAND_GET_PLAYER_HEADING: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + float angle = pPed->bInVehicle ? pPed->m_pMyVehicle->GetForward().Heading() : pPed->GetForward().Heading(); + angle = RADTODEG(angle); + if (angle < 0.0f) + angle += 360.0f; + if (angle > 360.0f) + angle -= 360.0f; + *(float*)&ScriptParams[0] = angle; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_SET_PLAYER_HEADING: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + if (pPed->bInVehicle) + return 0; + pPed->m_fRotationDest = pPed->m_fRotationCur = DEGTORAD(*(float*)&ScriptParams[1]); + pPed->SetHeading(DEGTORAD(*(float*)&ScriptParams[1])); + return 0; + } + case COMMAND_GET_CHAR_HEADING: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + float angle = pPed->bInVehicle ? pPed->m_pMyVehicle->GetForward().Heading() : pPed->GetForward().Heading(); + angle = RADTODEG(angle); + if (angle < 0.0f) + angle += 360.0f; + if (angle > 360.0f) + angle -= 360.0f; + *(float*)&ScriptParams[0] = angle; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_SET_CHAR_HEADING: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + if (pPed->bInVehicle) + return 0; + pPed->m_fRotationDest = pPed->m_fRotationCur = DEGTORAD(*(float*)&ScriptParams[1]); + pPed->SetHeading(DEGTORAD(*(float*)&ScriptParams[1])); + return 0; + } + case COMMAND_GET_CAR_HEADING: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + float angle = pVehicle->GetForward().Heading(); + angle = RADTODEG(angle); + if (angle < 0.0f) + angle += 360.0f; + if (angle > 360.0f) + angle -= 360.0f; + *(float*)&ScriptParams[0] = angle; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_SET_CAR_HEADING: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->SetHeading(DEGTORAD(*(float*)&ScriptParams[1])); + return 0; + } + case COMMAND_GET_OBJECT_HEADING: + { + CollectParameters(&m_nIp, 1); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + float angle = pObject->GetForward().Heading(); + angle = RADTODEG(angle); + if (angle < 0.0f) + angle += 360.0f; + if (angle > 360.0f) + angle -= 360.0f; + *(float*)&ScriptParams[0] = angle; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_SET_OBJECT_HEADING: + { + CollectParameters(&m_nIp, 2); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + CWorld::Remove(pObject); + pObject->SetHeading(DEGTORAD(*(float*)&ScriptParams[1])); + pObject->GetMatrix().UpdateRW(); + pObject->UpdateRwFrame(); + CWorld::Add(pObject); + return 0; + } + /* + case COMMAND_IS_PLAYER_TOUCHING_OBJECT: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[1]); + script_assert(pObject); + CPhysical* pEntityToTest = pPed->bInVehicle ? (CPhysical*)pPed->m_pMyVehicle : pPed; + UpdateCompareFlag(pEntityToTest->GetHasCollidedWith(pObject)); + return 0; + } + case COMMAND_IS_CHAR_TOUCHING_OBJECT: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[1]); + script_assert(pObject); + CPhysical* pEntityToTest = pPed->bInVehicle ? (CPhysical*)pPed->m_pMyVehicle : pPed; + UpdateCompareFlag(pEntityToTest->GetHasCollidedWith(pObject)); + return 0; + } + */ + case COMMAND_SET_PLAYER_AMMO: + { + CollectParameters(&m_nIp, 3); + CWorld::Players[0].m_pPed->SetAmmo((eWeaponType)ScriptParams[1], ScriptParams[2]); + return 0; + } + /* + case COMMAND_SET_CHAR_AMMO: + { + CollectParameters(&m_nIp, 3); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + pPed->SetAmmo((eWeaponType)ScriptParams[1], ScriptParams[2]); + return 0; + } + */ + //case COMMAND_SET_CAR_AMMO: + //case COMMAND_LOAD_CAMERA_SPLINE: + //case COMMAND_MOVE_CAMERA_ALONG_SPLINE: + //case COMMAND_GET_CAMERA_POSITION_ALONG_SPLINE: + case COMMAND_DECLARE_MISSION_FLAG: + CTheScripts::OnAMissionFlag = (uint16)CTheScripts::Read2BytesFromScript(&++m_nIp); + return 0; + case COMMAND_DECLARE_MISSION_FLAG_FOR_CONTACT: + return 0; + //case COMMAND_DECLARE_BASE_BRIEF_ID_FOR_CONTACT: + case COMMAND_IS_PLAYER_HEALTH_GREATER: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + UpdateCompareFlag(pPed->m_fHealth > ScriptParams[1]); + return 0; + } + case COMMAND_IS_CHAR_HEALTH_GREATER: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(pPed->m_fHealth > ScriptParams[1]); + return 0; + } + case COMMAND_IS_CAR_HEALTH_GREATER: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + UpdateCompareFlag(pVehicle->m_fHealth > ScriptParams[1]); + return 0; + } + case COMMAND_ADD_BLIP_FOR_CAR: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + int handle = CRadar::SetEntityBlip(BLIP_CAR, ScriptParams[0], 0, BLIP_DISPLAY_BOTH); + CRadar::ChangeBlipScale(handle, 3); + ScriptParams[0] = handle; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_ADD_BLIP_FOR_CHAR: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + int handle = CRadar::SetEntityBlip(BLIP_CHAR, ScriptParams[0], 1, BLIP_DISPLAY_BOTH); + CRadar::ChangeBlipScale(handle, 3); + ScriptParams[0] = handle; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_ADD_BLIP_FOR_OBJECT: + { + CollectParameters(&m_nIp, 1); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + int handle = CRadar::SetEntityBlip(BLIP_OBJECT, ScriptParams[0], 6, BLIP_DISPLAY_BOTH); + CRadar::ChangeBlipScale(handle, 3); + ScriptParams[0] = handle; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_ADD_BLIP_FOR_CONTACT_POINT: + { + CollectParameters(&m_nIp, 3); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + int handle = CRadar::SetCoordBlip(BLIP_CONTACT_POINT, pos, 2, BLIP_DISPLAY_BOTH); + CRadar::ChangeBlipScale(handle, 3); + ScriptParams[0] = handle; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_ADD_BLIP_FOR_COORD: + { + CollectParameters(&m_nIp, 3); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + int handle = CRadar::SetCoordBlip(BLIP_COORD, pos, 5, BLIP_DISPLAY_BOTH); + CRadar::ChangeBlipScale(handle, 3); + ScriptParams[0] = handle; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_CHANGE_BLIP_DISPLAY: + CollectParameters(&m_nIp, 2); + CRadar::ChangeBlipDisplay(ScriptParams[0], (eBlipDisplay)ScriptParams[1]); + return 0; + case COMMAND_ADD_ONE_OFF_SOUND: + { + CollectParameters(&m_nIp, 4); + switch (ScriptParams[3]) { + case SCRIPT_SOUND_PART_MISSION_COMPLETE: + DMAudio.PlayFrontEndSound(SOUND_PART_MISSION_COMPLETE, 0); + return 0; + case SCRIPT_SOUND_RACE_START_3: + DMAudio.PlayFrontEndSound(SOUND_RACE_START_3, 0); + return 0; + case SCRIPT_SOUND_RACE_START_2: + DMAudio.PlayFrontEndSound(SOUND_RACE_START_2, 0); + return 0; + case SCRIPT_SOUND_RACE_START_1: + DMAudio.PlayFrontEndSound(SOUND_RACE_START_1, 0); + return 0; + case SCRIPT_SOUND_RACE_START_GO: + DMAudio.PlayFrontEndSound(SOUND_RACE_START_GO, 0); + return 0; + case SCRIPT_SOUND_AMMUNATION_BUY_WEAPON: + DMAudio.PlayFrontEndSound(SOUND_PICKUP_WEAPON_BOUGHT, 0); + return 0; + case SCRIPT_SOUND_AMMUNATION_BUY_WEAPON_DENIED: + DMAudio.PlayFrontEndSound(SOUND_GARAGE_NO_MONEY, 0); + return 0; + case SCRIPT_SOUND_IMRAN_ARM_BOMB: + DMAudio.PlayFrontEndSound(SOUND_AMMUNATION_IMRAN_ARM_BOMB, 0); + return 0; + default: + break; + } + if (!DMAudio.IsAudioInitialised()) + return 0; + cAudioScriptObject* obj = new cAudioScriptObject(); + obj->Posn = *(CVector*)&ScriptParams[0]; + obj->AudioId = ScriptParams[3]; + obj->AudioEntity = AEHANDLE_NONE; + DMAudio.CreateOneShotScriptObject(obj); + return 0; + } + case COMMAND_ADD_CONTINUOUS_SOUND: + { + CollectParameters(&m_nIp, 4); + if (DMAudio.IsAudioInitialised()) { + cAudioScriptObject* obj = new cAudioScriptObject(); + obj->Posn = *(CVector*)&ScriptParams[0]; + obj->AudioId = ScriptParams[3]; + obj->AudioEntity = DMAudio.CreateLoopingScriptObject(obj); + ScriptParams[0] = CPools::GetAudioScriptObjectPool()->GetIndex(obj); + } + else + ScriptParams[0] = -1; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_REMOVE_SOUND: + { + CollectParameters(&m_nIp, 1); + cAudioScriptObject* obj = CPools::GetAudioScriptObjectPool()->GetAt(ScriptParams[0]); + if (!obj){ + debug("REMOVE_SOUND - Sound doesn't exist\n"); + return 0; + } + DMAudio.DestroyLoopingScriptObject(obj->AudioEntity); + delete obj; + return 0; + } + case COMMAND_IS_CAR_STUCK_ON_ROOF: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + UpdateCompareFlag(CTheScripts::UpsideDownCars.HasCarBeenUpsideDownForAWhile(ScriptParams[0])); + return 0; + } + default: + script_assert(0); + } + return -1; +} + +int8 CRunningScript::ProcessCommands400To499(int32 command) +{ + switch (command) { + case COMMAND_ADD_UPSIDEDOWN_CAR_CHECK: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + CTheScripts::UpsideDownCars.AddCarToCheck(ScriptParams[0]); + return 0; + } + case COMMAND_REMOVE_UPSIDEDOWN_CAR_CHECK: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + CTheScripts::UpsideDownCars.RemoveCarFromCheck(ScriptParams[0]); + return 0; + } + case COMMAND_SET_CHAR_OBJ_WAIT_ON_FOOT: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_WAIT_ON_FOOT); + return 0; + } + case COMMAND_SET_CHAR_OBJ_FLEE_ON_FOOT_TILL_SAFE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE); + return 0; + } + case COMMAND_SET_CHAR_OBJ_GUARD_SPOT: + { + CollectParameters(&m_nIp, 4); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_GUARD_SPOT, pos); + return 0; + } + /* + case COMMAND_SET_CHAR_OBJ_GUARD_AREA: + { + CollectParameters(&m_nIp, 5); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + float infX = *(float*)&ScriptParams[1]; + float infY = *(float*)&ScriptParams[2]; + float supX = *(float*)&ScriptParams[3]; + float supY = *(float*)&ScriptParams[4]; + if (infX > supX){ + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[1]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[2]; + } + CVector pos; + pos.x = (infX + supX) / 2; + pos.y = (infY + supY) / 2; + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + float radius = Max(pos.x - infX, pos.y - infY); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_GUARD_SPOT, pos, radius); + return 0; + } + case COMMAND_SET_CHAR_OBJ_WAIT_IN_CAR: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_WAIT_IN_CAR); + return 0; + } + */ + case COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_2D: + case COMMAND_IS_PLAYER_IN_AREA_IN_CAR_2D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_2D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_2D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_2D: + case COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_3D: + case COMMAND_IS_PLAYER_IN_AREA_IN_CAR_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_3D: + PlayerInAreaCheckCommand(command, &m_nIp); + return 0; + case COMMAND_IS_CHAR_IN_AREA_ON_FOOT_2D: + case COMMAND_IS_CHAR_IN_AREA_IN_CAR_2D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_2D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_2D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_2D: + case COMMAND_IS_CHAR_IN_AREA_ON_FOOT_3D: + case COMMAND_IS_CHAR_IN_AREA_IN_CAR_3D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_3D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_3D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_3D: + CharInAreaCheckCommand(command, &m_nIp); + return 0; + case COMMAND_IS_CAR_STOPPED_IN_AREA_2D: + case COMMAND_IS_CAR_STOPPED_IN_AREA_3D: + CarInAreaCheckCommand(command, &m_nIp); + return 0; + case COMMAND_LOCATE_CAR_2D: + case COMMAND_LOCATE_STOPPED_CAR_2D: + case COMMAND_LOCATE_CAR_3D: + case COMMAND_LOCATE_STOPPED_CAR_3D: + LocateCarCommand(command, &m_nIp); + return 0; + case COMMAND_GIVE_WEAPON_TO_PLAYER: + { + CollectParameters(&m_nIp, 3); + CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + pPed->m_nSelectedWepSlot = pPed->GiveWeapon((eWeaponType)ScriptParams[1], ScriptParams[2]); + return 0; + } + case COMMAND_GIVE_WEAPON_TO_CHAR: + { + CollectParameters(&m_nIp, 3); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->SetCurrentWeapon(pPed->GiveWeapon((eWeaponType)ScriptParams[1], ScriptParams[2])); + if (pPed->bInVehicle && pPed->m_pMyVehicle) + pPed->RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(pPed->m_weapons[pPed->m_currentWeapon].m_eWeaponType)->m_nModelId); + return 0; + } + //case COMMAND_GIVE_WEAPON_TO_CAR: + case COMMAND_SET_PLAYER_CONTROL: + { + CollectParameters(&m_nIp, 2); + CPlayerInfo* pPlayer = &CWorld::Players[ScriptParams[0]]; + if (ScriptParams[1]){ + pPlayer->MakePlayerSafe(false); + if (strcmp(m_abScriptName, "serg1") == 0) // Four Iron + pPlayer->m_pPed->ClearFollowPath(); + }else{ + pPlayer->MakePlayerSafe(true); + } + return 0; + } + case COMMAND_FORCE_WEATHER: + CollectParameters(&m_nIp, 1); + CWeather::ForceWeather(ScriptParams[0]); + return 0; + case COMMAND_FORCE_WEATHER_NOW: + CollectParameters(&m_nIp, 1); + CWeather::ForceWeatherNow(ScriptParams[0]); + return 0; + case COMMAND_RELEASE_WEATHER: + CWeather::ReleaseWeather(); + return 0; + case COMMAND_SET_CURRENT_PLAYER_WEAPON: + { + CollectParameters(&m_nIp, 2); + CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + for (int i = 0; i < TOTAL_WEAPON_SLOTS; i++){ + if (pPed->m_weapons[i].m_eWeaponType == ScriptParams[1]) + pPed->m_nSelectedWepSlot = i; + } + return 0; + } + case COMMAND_SET_CURRENT_CHAR_WEAPON: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + for (int i = 0; i < TOTAL_WEAPON_SLOTS; i++) { + if (pPed->m_weapons[i].m_eWeaponType == ScriptParams[1]) + pPed->SetCurrentWeapon(i); + } + return 0; + } + //case COMMAND_SET_CURRENT_CAR_WEAPON: + case COMMAND_GET_OBJECT_COORDINATES: + { + CollectParameters(&m_nIp, 1); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + *(CVector*)&ScriptParams[0] = pObject->GetPosition(); + StoreParameters(&m_nIp, 3); + return 0; + } + case COMMAND_SET_OBJECT_COORDINATES: + { + CollectParameters(&m_nIp, 4); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + pObject->Teleport(pos); + CTheScripts::ClearSpaceForMissionEntity(pos, pObject); + return 0; + } + case COMMAND_GET_GAME_TIMER: + ScriptParams[0] = CTimer::GetTimeInMilliseconds(); + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_TURN_CHAR_TO_FACE_COORD: + { + CollectParameters(&m_nIp, 4); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CVehicle* pVehicle; + CVector pos; + if (pPed->bInVehicle) + pVehicle = pPed->m_pMyVehicle; + else + pVehicle = nil; + if (pVehicle) + pos = pVehicle->GetPosition(); + else + pos = pPed->GetPosition(); + float heading = CGeneral::GetATanOfXY(pos.x - *(float*)&ScriptParams[1], pos.y - *(float*)&ScriptParams[2]); + heading += HALFPI; + if (heading > TWOPI) + heading -= TWOPI; + if (!pVehicle){ + pPed->m_fRotationCur = heading; + pPed->m_fRotationDest = heading; + pPed->SetHeading(heading); + } + return 0; + } + case COMMAND_TURN_PLAYER_TO_FACE_COORD: + { + CollectParameters(&m_nIp, 4); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + CVehicle* pVehicle; + CVector pos; + if (pPed->bInVehicle) + pVehicle = pPed->m_pMyVehicle; + else + pVehicle = nil; + if (pVehicle) + pos = pVehicle->GetPosition(); + else + pos = pPed->GetPosition(); + float heading = CGeneral::GetATanOfXY(pos.x - *(float*)&ScriptParams[1], pos.y - *(float*)&ScriptParams[2]); + heading += HALFPI; + if (heading > TWOPI) + heading -= TWOPI; + if (!pVehicle) { + pPed->m_fRotationCur = heading; + pPed->m_fRotationDest = heading; + pPed->SetHeading(heading); + } + return 0; + } + case COMMAND_STORE_WANTED_LEVEL: + { + CollectParameters(&m_nIp, 1); + CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + ScriptParams[0] = pPed->m_pWanted->GetWantedLevel(); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_IS_CAR_STOPPED: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + UpdateCompareFlag(CTheScripts::IsVehicleStopped(pVehicle)); + return 0; + } + case COMMAND_MARK_CHAR_AS_NO_LONGER_NEEDED: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + CTheScripts::CleanUpThisPed(pPed); + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_CHAR); + return 0; + } + case COMMAND_MARK_CAR_AS_NO_LONGER_NEEDED: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + CTheScripts::CleanUpThisVehicle(pVehicle); + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_CAR); + return 0; + } + case COMMAND_MARK_OBJECT_AS_NO_LONGER_NEEDED: + { + CollectParameters(&m_nIp, 1); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + CTheScripts::CleanUpThisObject(pObject); + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_OBJECT); + return 0; + } + case COMMAND_DONT_REMOVE_CHAR: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_CHAR); + return 0; + } + case COMMAND_DONT_REMOVE_CAR: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_CAR); + return 0; + } + case COMMAND_DONT_REMOVE_OBJECT: + { + CollectParameters(&m_nIp, 1); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_OBJECT); + return 0; + } + case COMMAND_CREATE_CHAR_AS_PASSENGER: + { + CollectParameters(&m_nIp, 4); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + switch (ScriptParams[2]) { + case MI_COP: + if (ScriptParams[1] == PEDTYPE_COP) + ScriptParams[2] = COP_STREET; + break; + case MI_SWAT: + if (ScriptParams[1] == PEDTYPE_COP) + ScriptParams[2] = COP_SWAT; + break; + case MI_FBI: + if (ScriptParams[1] == PEDTYPE_COP) + ScriptParams[2] = COP_FBI; + break; + case MI_ARMY: + if (ScriptParams[1] == PEDTYPE_COP) + ScriptParams[2] = COP_ARMY; + break; + case MI_MEDIC: + if (ScriptParams[1] == PEDTYPE_EMERGENCY) + ScriptParams[2] = PEDTYPE_EMERGENCY; + break; + case MI_FIREMAN: + if (ScriptParams[1] == PEDTYPE_FIREMAN) + ScriptParams[2] = PEDTYPE_FIREMAN; + break; + default: + break; + } + CPed* pPed; + if (ScriptParams[1] == PEDTYPE_COP) + pPed = new CCopPed((eCopType)ScriptParams[2]); + else if (ScriptParams[1] == PEDTYPE_EMERGENCY || ScriptParams[1] == PEDTYPE_FIREMAN) + pPed = new CEmergencyPed(ScriptParams[2]); + else + pPed = new CCivilianPed((ePedType)ScriptParams[1], ScriptParams[2]); + pPed->CharCreatedBy = MISSION_CHAR; + pPed->bRespondsToThreats = false; + pPed->bAllowMedicsToReviveMe = false; + pPed->bIsPlayerFriend = false; + if (pVehicle->bIsBus) + pPed->bRenderPedInCar = false; + pPed->SetPosition(pVehicle->GetPosition()); + pPed->SetOrientation(0.0f, 0.0f, 0.0f); + CPopulation::ms_nTotalMissionPeds++; + CWorld::Add(pPed); + if (ScriptParams[3] >= 0) + pVehicle->AddPassenger(pPed, ScriptParams[3]); + else + pVehicle->AddPassenger(pPed); + pPed->m_pMyVehicle = pVehicle; + pPed->m_pMyVehicle->RegisterReference((CEntity**)&pPed->m_pMyVehicle); + pPed->bInVehicle = true; + pPed->SetPedState(PED_DRIVING); + pPed->bUsesCollision = false; + pPed->AddInCarAnims(pVehicle, false); + pPed->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pPed->GetPosition()); + ScriptParams[0] = CPools::GetPedPool()->GetIndex(pPed); + StoreParameters(&m_nIp, 1); + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.AddEntityToList(ScriptParams[0], CLEANUP_CHAR); + return 0; + } + case COMMAND_SET_CHAR_OBJ_KILL_CHAR_ON_FOOT: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, pTarget); + return 0; + } + case COMMAND_SET_CHAR_OBJ_KILL_PLAYER_ON_FOOT: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CPed* pTarget = CWorld::Players[ScriptParams[1]].m_pPed; + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, pTarget); + return 0; + } + case COMMAND_SET_CHAR_OBJ_KILL_CHAR_ANY_MEANS: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_KILL_CHAR_ANY_MEANS, pTarget); + return 0; + } + case COMMAND_SET_CHAR_OBJ_KILL_PLAYER_ANY_MEANS: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CPed* pTarget = CWorld::Players[ScriptParams[1]].m_pPed; + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_KILL_CHAR_ANY_MEANS, pTarget); + return 0; + } + /* + case COMMAND_SET_CHAR_OBJ_FLEE_CHAR_ON_FOOT_TILL_SAFE: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, pTarget); + return 0; + } + */ + case COMMAND_SET_CHAR_OBJ_FLEE_PLAYER_ON_FOOT_TILL_SAFE: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CPed* pTarget = CWorld::Players[ScriptParams[1]].m_pPed; + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, pTarget); + return 0; + } + case COMMAND_SET_CHAR_OBJ_FLEE_CHAR_ON_FOOT_ALWAYS: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS, pTarget); + return 0; + } + case COMMAND_SET_CHAR_OBJ_FLEE_PLAYER_ON_FOOT_ALWAYS: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CPed* pTarget = CWorld::Players[ScriptParams[1]].m_pPed; + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS, pTarget); + return 0; + } + case COMMAND_SET_CHAR_OBJ_GOTO_CHAR_ON_FOOT: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_GOTO_CHAR_ON_FOOT, pTarget); + return 0; + } + case COMMAND_SET_CHAR_OBJ_GOTO_PLAYER_ON_FOOT: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CPed* pTarget = CWorld::Players[ScriptParams[1]].m_pPed; + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_GOTO_CHAR_ON_FOOT, pTarget); + return 0; + } + case COMMAND_SET_CHAR_OBJ_LEAVE_CAR: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_LEAVE_CAR, pVehicle); + return 0; + } + case COMMAND_SET_CHAR_OBJ_ENTER_CAR_AS_PASSENGER: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, pVehicle); + return 0; + } + case COMMAND_SET_CHAR_OBJ_ENTER_CAR_AS_DRIVER: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, pVehicle); + return 0; + } + //case COMMAND_SET_CHAR_OBJ_FOLLOW_CAR_IN_CAR: + //case COMMAND_SET_CHAR_OBJ_FIRE_AT_OBJECT_FROM_VEHICLE: + case COMMAND_SET_CHAR_OBJ_DESTROY_OBJECT: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[1]); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_DESTROY_OBJECT, pObject); + return 0; + } + case COMMAND_SET_CHAR_OBJ_DESTROY_CAR: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_DESTROY_CAR, pVehicle); + return 0; + } + /* + case COMMAND_SET_CHAR_OBJ_GOTO_AREA_ON_FOOT: + { + CollectParameters(&m_nIp, 5); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + float infX = *(float*)&ScriptParams[1]; + float infY = *(float*)&ScriptParams[2]; + float supX = *(float*)&ScriptParams[3]; + float supY = *(float*)&ScriptParams[4]; + if (infX > supX) { + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[1]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[2]; + } + CVector pos; + pos.x = (infX + supX) / 2; + pos.y = (infY + supY) / 2; + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + float radius = Max(pos.x - infX, pos.y - infY); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, pos, radius); + return 0; + } + */ + //case COMMAND_SET_CHAR_OBJ_GOTO_AREA_IN_CAR: + //case COMMAND_SET_CHAR_OBJ_FOLLOW_CAR_ON_FOOT_WITH_OFFSET: + //case COMMAND_SET_CHAR_OBJ_GUARD_ATTACK: + case COMMAND_SET_CHAR_AS_LEADER: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); + pPed->SetObjective(OBJECTIVE_SET_LEADER, pTarget); + return 0; + } + case COMMAND_SET_PLAYER_AS_LEADER: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CPed* pTarget = CWorld::Players[ScriptParams[1]].m_pPed; + pPed->SetObjective(OBJECTIVE_SET_LEADER, pTarget); + return 0; + } + case COMMAND_LEAVE_GROUP: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->ClearLeader(); + return 0; + } + case COMMAND_SET_CHAR_OBJ_FOLLOW_ROUTE: + { + CollectParameters(&m_nIp, 3); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_FOLLOW_ROUTE, ScriptParams[1], ScriptParams[2]); + return 0; + } + case COMMAND_ADD_ROUTE_POINT: + { + CollectParameters(&m_nIp, 4); + CRouteNode::AddRoutePoint(ScriptParams[0], *(CVector*)&ScriptParams[1]); + return 0; + } + case COMMAND_PRINT_WITH_NUMBER_BIG: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 3); + CMessages::AddBigMessageWithNumber(text, ScriptParams[1], ScriptParams[2] - 1, ScriptParams[0], -1, -1, -1, -1, -1); + return 0; + } + case COMMAND_PRINT_WITH_NUMBER: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 3); + CMessages::AddMessageWithNumber(text, ScriptParams[1], ScriptParams[2], ScriptParams[0], -1, -1, -1, -1, -1); + return 0; + } + case COMMAND_PRINT_WITH_NUMBER_NOW: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 3); + CMessages::AddMessageJumpQWithNumber(text, ScriptParams[1], ScriptParams[2], ScriptParams[0], -1, -1, -1, -1, -1); + return 0; + } + //case COMMAND_PRINT_WITH_NUMBER_SOON: + case COMMAND_SWITCH_ROADS_ON: + { + CollectParameters(&m_nIp, 6); + float infX = *(float*)&ScriptParams[0]; + float infY = *(float*)&ScriptParams[1]; + float infZ = *(float*)&ScriptParams[2]; + float supX = *(float*)&ScriptParams[3]; + float supY = *(float*)&ScriptParams[4]; + float supZ = *(float*)&ScriptParams[5]; + if (infX > supX){ + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[0]; + } + if (infY > supY){ + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[1]; + } + if (infZ > supZ){ + infZ = *(float*)&ScriptParams[5]; + supZ = *(float*)&ScriptParams[2]; + } + ThePaths.SwitchRoadsOffInArea(infX, supX, infY, supY, infZ, supZ, false); + return 0; + } + case COMMAND_SWITCH_ROADS_OFF: + { + CollectParameters(&m_nIp, 6); + float infX = *(float*)&ScriptParams[0]; + float infY = *(float*)&ScriptParams[1]; + float infZ = *(float*)&ScriptParams[2]; + float supX = *(float*)&ScriptParams[3]; + float supY = *(float*)&ScriptParams[4]; + float supZ = *(float*)&ScriptParams[5]; + if (infX > supX) { + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[0]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[1]; + } + if (infZ > supZ) { + infZ = *(float*)&ScriptParams[5]; + supZ = *(float*)&ScriptParams[2]; + } + ThePaths.SwitchRoadsOffInArea(infX, supX, infY, supY, infZ, supZ, true); + return 0; + } + case COMMAND_GET_NUMBER_OF_PASSENGERS: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + ScriptParams[0] = pVehicle->m_nNumPassengers; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_GET_MAXIMUM_NUMBER_OF_PASSENGERS: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + ScriptParams[0] = pVehicle->m_nNumMaxPassengers; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_SET_CAR_DENSITY_MULTIPLIER: + { + CollectParameters(&m_nIp, 1); + CCarCtrl::CarDensityMultiplier = *(float*)&ScriptParams[0]; + return 0; + } + case COMMAND_SET_CAR_HEAVY: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + if (ScriptParams[1] != 0) { + pVehicle->bIsHeavy = true; + pVehicle->m_fMass = 3.0f * pVehicle->pHandling->fMass; + pVehicle->m_fTurnMass = 5.0f * pVehicle->pHandling->fTurnMass; + } + else { + pVehicle->bIsHeavy = false; + pVehicle->m_fMass = pVehicle->pHandling->fMass; + pVehicle->m_fTurnMass = pVehicle->pHandling->fTurnMass; + } + return 0; + } + case COMMAND_CLEAR_CHAR_THREAT_SEARCH: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->m_fearFlags = 0; + return 0; + } + /* + case COMMAND_ACTIVATE_CRANE: + { + CollectParameters(&m_nIp, 10); + float infX = *(float*)&ScriptParams[2]; + float infY = *(float*)&ScriptParams[3]; + float supX = *(float*)&ScriptParams[4]; + float supY = *(float*)&ScriptParams[5]; + if (infX > supX) { + infX = *(float*)&ScriptParams[4]; + supX = *(float*)&ScriptParams[2]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[5]; + supY = *(float*)&ScriptParams[3]; + } + CCranes::ActivateCrane(infX, supX, infY, supY, + *(float*)&ScriptParams[6], *(float*)&ScriptParams[7], *(float*)&ScriptParams[8], + DEGTORAD(*(float*)&ScriptParams[9]), false, false, + *(float*)&ScriptParams[0], *(float*)&ScriptParams[1]); + return 0; + } + case COMMAND_DEACTIVATE_CRANE: + { + CollectParameters(&m_nIp, 2); + CCranes::DeActivateCrane(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1]); + return 0; + } + */ + case COMMAND_SET_MAX_WANTED_LEVEL: + { + CollectParameters(&m_nIp, 1); + CWanted::SetMaximumWantedLevel(ScriptParams[0]); + return 0; + } + //case COMMAND_SAVE_VAR_INT: + //case COMMAND_SAVE_VAR_FLOAT: + case COMMAND_IS_CAR_IN_AIR_PROPER: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + UpdateCompareFlag(pVehicle->m_nCollisionRecords == 0); + return 0; + } + default: + script_assert(0); + } + return -1; +} diff --git a/src/miami/control/Script3.cpp b/src/miami/control/Script3.cpp new file mode 100644 index 00000000..f831645e --- /dev/null +++ b/src/miami/control/Script3.cpp @@ -0,0 +1,2417 @@ +#include "common.h" + +#include "Script.h" +#include "ScriptCommands.h" + +#include "Boat.h" +#include "CarCtrl.h" +#include "Clock.h" +#include "Coronas.h" +#include "Cranes.h" +#include "CutsceneMgr.h" +#include "Darkel.h" +#include "Explosion.h" +#include "Fire.h" +#include "General.h" +#include "Garages.h" +#include "Heli.h" +#include "Messages.h" +#include "Pad.h" +#include "ParticleObject.h" +#include "Phones.h" +#include "Pickups.h" +#include "PointLights.h" +#include "Population.h" +#include "Pools.h" +#include "ProjectileInfo.h" +#include "Radar.h" +#include "Restart.h" +#include "Stats.h" +#include "Streaming.h" +#include "User.h" +#include "WaterLevel.h" +#include "Weather.h" +#include "Zones.h" +#include "GameLogic.h" +#include "Bike.h" +#include "Wanted.h" + +int8 CRunningScript::ProcessCommands500To599(int32 command) +{ + switch (command) { + case COMMAND_IS_CAR_UPSIDEDOWN: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + UpdateCompareFlag(pVehicle->GetUp().z <= -0.97f); + return 0; + } + case COMMAND_GET_PLAYER_CHAR: + { + CollectParameters(&m_nIp, 1); + CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + ScriptParams[0] = CPools::GetPedPool()->GetIndex(pPed); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_CANCEL_OVERRIDE_RESTART: + CRestart::CancelOverrideRestart(); + return 0; + case COMMAND_SET_POLICE_IGNORE_PLAYER: + { + CollectParameters(&m_nIp, 2); + CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + if (ScriptParams[1]) { + pPed->m_pWanted->m_bIgnoredByCops = true; + CWorld::StopAllLawEnforcersInTheirTracks(); + } + else { + pPed->m_pWanted->m_bIgnoredByCops = false; + } + return 0; + } + /* + case COMMAND_ADD_PAGER_MESSAGE_WITH_NUMBER: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 4); + CUserDisplay::Pager.AddMessageWithNumber(text, ScriptParams[0], -1, -1, -1, -1, -1, + ScriptParams[1], ScriptParams[2], ScriptParams[3]); + return 0; + } + */ + case COMMAND_START_KILL_FRENZY: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 8); + CDarkel::StartFrenzy((eWeaponType)ScriptParams[0], ScriptParams[1], ScriptParams[2], + ScriptParams[3], text, ScriptParams[4], ScriptParams[5], + ScriptParams[6], ScriptParams[7] != 0, false); + return 0; + } + case COMMAND_READ_KILL_FRENZY_STATUS: + { + ScriptParams[0] = CDarkel::ReadStatus(); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_SQRT: + { + CollectParameters(&m_nIp, 1); + *(float*)&ScriptParams[0] = Sqrt(*(float*)&ScriptParams[0]); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_2D: + case COMMAND_LOCATE_PLAYER_ON_FOOT_CAR_2D: + case COMMAND_LOCATE_PLAYER_IN_CAR_CAR_2D: + case COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_3D: + case COMMAND_LOCATE_PLAYER_ON_FOOT_CAR_3D: + case COMMAND_LOCATE_PLAYER_IN_CAR_CAR_3D: + LocatePlayerCarCommand(command, &m_nIp); + return 0; + case COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_2D: + case COMMAND_LOCATE_CHAR_ON_FOOT_CAR_2D: + case COMMAND_LOCATE_CHAR_IN_CAR_CAR_2D: + case COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_3D: + case COMMAND_LOCATE_CHAR_ON_FOOT_CAR_3D: + case COMMAND_LOCATE_CHAR_IN_CAR_CAR_3D: + LocateCharCarCommand(command, &m_nIp); + return 0; + case COMMAND_GENERATE_RANDOM_FLOAT_IN_RANGE: + CollectParameters(&m_nIp, 2); + *(float*)&ScriptParams[0] = CGeneral::GetRandomNumberInRange(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1]); + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_GENERATE_RANDOM_INT_IN_RANGE: + CollectParameters(&m_nIp, 2); + ScriptParams[0] = CGeneral::GetRandomNumberInRange(ScriptParams[0], ScriptParams[1]); + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_LOCK_CAR_DOORS: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->m_nDoorLock = (eCarLock)ScriptParams[1]; + return 0; + } + case COMMAND_EXPLODE_CAR: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->BlowUpCar(nil); + return 0; + } + case COMMAND_ADD_EXPLOSION: + CollectParameters(&m_nIp, 4); +#ifdef SIMPLER_MISSIONS + if (!CGeneral::faststricmp(m_abScriptName, "hait2")) + CExplosion::AddExplosion(nil, nil, (eExplosionType)ScriptParams[3], *(CVector*)&ScriptParams[0], 0, true, 11.25f); + else +#endif + CExplosion::AddExplosion(nil, nil, (eExplosionType)ScriptParams[3], *(CVector*)&ScriptParams[0], 0, true); + return 0; + + case COMMAND_IS_CAR_UPRIGHT: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + UpdateCompareFlag(pVehicle->GetUp().z >= 0.0f); + return 0; + } + case COMMAND_TURN_CHAR_TO_FACE_CHAR: + { + CollectParameters(&m_nIp, 2); + CPed* pSourcePed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + CPed* pTargetPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); + CVehicle* pVehicle = pSourcePed->bInVehicle ? pSourcePed->m_pMyVehicle : nil; + CVector2D sourcePos = pSourcePed->bInVehicle ? pVehicle->GetPosition() : pSourcePed->GetPosition(); + CVector2D targetPos = pTargetPed->bInVehicle ? pTargetPed->m_pMyVehicle->GetPosition() : pTargetPed->GetPosition(); + float angle = CGeneral::GetATanOfXY(sourcePos.x - targetPos.x, sourcePos.y - targetPos.y) + HALFPI; + if (angle > TWOPI) + angle -= TWOPI; + if (!pVehicle) { + pSourcePed->m_fRotationCur = angle; + pSourcePed->m_fRotationDest = angle; + pSourcePed->SetHeading(angle); + } + return 0; + } + case COMMAND_TURN_CHAR_TO_FACE_PLAYER: + { + CollectParameters(&m_nIp, 2); + CPed* pSourcePed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + CPed* pTargetPed = CWorld::Players[ScriptParams[1]].m_pPed; + CVehicle* pVehicle = pSourcePed->bInVehicle ? pSourcePed->m_pMyVehicle : nil; + CVector2D sourcePos = pSourcePed->bInVehicle ? pVehicle->GetPosition() : pSourcePed->GetPosition(); + CVector2D targetPos = pTargetPed->bInVehicle ? pTargetPed->m_pMyVehicle->GetPosition() : pTargetPed->GetPosition(); + float angle = CGeneral::GetATanOfXY(sourcePos.x - targetPos.x, sourcePos.y - targetPos.y) + HALFPI; + if (angle > TWOPI) + angle -= TWOPI; + if (!pVehicle) { + pSourcePed->m_fRotationCur = angle; + pSourcePed->m_fRotationDest = angle; + pSourcePed->SetHeading(angle); + } + return 0; + } + case COMMAND_TURN_PLAYER_TO_FACE_CHAR: + { + CollectParameters(&m_nIp, 2); + CPed* pSourcePed = CWorld::Players[ScriptParams[0]].m_pPed; + CPed* pTargetPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); + CVehicle* pVehicle = pSourcePed->bInVehicle ? pSourcePed->m_pMyVehicle : nil; + CVector2D sourcePos = pSourcePed->bInVehicle ? pVehicle->GetPosition() : pSourcePed->GetPosition(); + CVector2D targetPos = pTargetPed->bInVehicle ? pTargetPed->m_pMyVehicle->GetPosition() : pTargetPed->GetPosition(); + float angle = CGeneral::GetATanOfXY(sourcePos.x - targetPos.x, sourcePos.y - targetPos.y) + HALFPI; + if (angle > TWOPI) + angle -= TWOPI; + if (!pVehicle) { + pSourcePed->m_fRotationCur = angle; + pSourcePed->m_fRotationDest = angle; + pSourcePed->SetHeading(angle); + } + return 0; + } + case COMMAND_SET_CHAR_OBJ_GOTO_COORD_ON_FOOT: + { + CollectParameters(&m_nIp, 3); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CVector target; + target.x = *(float*)&ScriptParams[1]; + target.y = *(float*)&ScriptParams[2]; + target.z = CWorld::FindGroundZForCoord(target.x, target.y); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, target); + return 0; + } + //case COMMAND_SET_CHAR_OBJ_GOTO_COORD_IN_CAR: + case COMMAND_CREATE_PICKUP: + { + CollectParameters(&m_nIp, 5); + int16 model = ScriptParams[0]; + if (model < 0) + model = CTheScripts::UsedObjectArray[-model].index; + CVector pos = *(CVector*)&ScriptParams[2]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; + CPickups::GetActualPickupIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + ScriptParams[0] = CPickups::GenerateNewOne(pos, model, ScriptParams[1], 0); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_HAS_PICKUP_BEEN_COLLECTED: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CPickups::IsPickUpPickedUp(ScriptParams[0]) != 0); + return 0; + case COMMAND_REMOVE_PICKUP: + CollectParameters(&m_nIp, 1); + CPickups::RemovePickUp(ScriptParams[0]); + return 0; + case COMMAND_SET_TAXI_LIGHTS: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + script_assert(pVehicle->m_vehType == VEHICLE_TYPE_CAR); + ((CAutomobile*)pVehicle)->SetTaxiLight(ScriptParams[1] != 0); + return 0; + } + case COMMAND_PRINT_BIG_Q: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 2); + CMessages::AddBigMessageQ(text, ScriptParams[0], ScriptParams[1] - 1); + return 0; + } + /* + case COMMAND_PRINT_WITH_NUMBER_BIG_Q: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 3); + CMessages::AddBigMessageWithNumberQ(text, ScriptParams[1], ScriptParams[2] - 1, + ScriptParams[0], -1, -1, -1, -1, -1); + return 0; + } + */ + case COMMAND_SET_GARAGE: + { + CollectParameters(&m_nIp, 9); + float infX = *(float*)&ScriptParams[0]; + float infY = *(float*)&ScriptParams[1]; + float infZ = *(float*)&ScriptParams[2]; + float X2 = *(float*)&ScriptParams[3]; + float Y2 = *(float*)&ScriptParams[4]; + float supX = *(float*)&ScriptParams[5]; + float supY = *(float*)&ScriptParams[6]; + float supZ = *(float*)&ScriptParams[7]; + ScriptParams[0] = CGarages::AddOne(infX, infY, infZ, X2, Y2, supX, supY, supZ, ScriptParams[8], 0); + StoreParameters(&m_nIp, 1); + return 0; + } + /* + case COMMAND_SET_GARAGE_WITH_CAR_MODEL: + { + CollectParameters(&m_nIp, 10); + float infX = *(float*)&ScriptParams[0]; + float infY = *(float*)&ScriptParams[1]; + float infZ = *(float*)&ScriptParams[2]; + float X2 = *(float*)&ScriptParams[3]; + float Y2 = *(float*)&ScriptParams[4]; + float supX = *(float*)&ScriptParams[5]; + float supY = *(float*)&ScriptParams[6]; + float supZ = *(float*)&ScriptParams[7]; + ScriptParams[0] = CGarages::AddOne(infX, infY, infZ, X2, Y2, supX, supY, supZ, ScriptParams[8], ScriptParams[9]); + StoreParameters(&m_nIp, 1); + return 0; + } + */ + case COMMAND_SET_TARGET_CAR_FOR_MISSION_GARAGE: + { + CollectParameters(&m_nIp, 2); + CVehicle* pTarget; + if (ScriptParams[1] >= 0) { + pTarget = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + script_assert(pTarget); + } + else { + pTarget = nil; + } + CGarages::SetTargetCarForMissonGarage(ScriptParams[0], pTarget); + return 0; + } + case COMMAND_IS_CAR_IN_MISSION_GARAGE: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CGarages::HasCarBeenDroppedOffYet(ScriptParams[0])); + return 0; +/* + case COMMAND_SET_FREE_BOMBS: + CollectParameters(&m_nIp, 1); + CGarages::SetFreeBombs(ScriptParams[0] != 0); + return 0; + case COMMAND_SET_POWERPOINT: + { + CollectParameters(&m_nIp, 7); + float f1 = *(float*)&ScriptParams[0]; + float f2 = *(float*)&ScriptParams[1]; + float f3 = *(float*)&ScriptParams[2]; + float f4 = *(float*)&ScriptParams[3]; + float f5 = *(float*)&ScriptParams[4]; + float f6 = *(float*)&ScriptParams[5]; + float temp; + + if (f1 > f4) { + temp = f1; + f1 = f4; + f4 = temp; + } + + if (f2 > f5) { + temp = f2; + f2 = f5; + f5 = temp; + } + + if (f3 > f6) { + temp = f3; + f3 = f6; + f6 = temp; + } + + CPowerPoints::GenerateNewOne(f1, f2, f3, f4, f5, f6, *(uint8*)&ScriptParams[6]); + + return 0; + } + case COMMAND_SET_ALL_TAXI_LIGHTS: + CollectParameters(&m_nIp, 1); + CAutomobile::SetAllTaxiLights(ScriptParams[0] != 0); + return 0; + case COMMAND_IS_CAR_ARMED_WITH_ANY_BOMB: + { + CollectParameters(&m_nIp, 1); + CAutomobile* pCar = (CAutomobile*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pCar); + script_assert(pCar->m_vehType == VEHICLE_TYPE_CAR); + UpdateCompareFlag(pCar->m_bombType != 0); //TODO: enum + return 0; + } + */ + case COMMAND_APPLY_BRAKES_TO_PLAYERS_CAR: + CollectParameters(&m_nIp, 2); + CPad::GetPad(ScriptParams[0])->bApplyBrakes = (ScriptParams[1] != 0); + return 0; + case COMMAND_SET_PLAYER_HEALTH: + { + CollectParameters(&m_nIp, 2); + CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + pPed->m_fHealth = Min(ScriptParams[1], CWorld::Players[ScriptParams[0]].m_nMaxHealth); + return 0; + } + case COMMAND_SET_CHAR_HEALTH: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + if (ScriptParams[1]) { + pPed->m_fHealth = ScriptParams[1]; + } + else if (pPed->bInVehicle) { + pPed->SetDead(); + if (!pPed->IsPlayer()) + pPed->FlagToDestroyWhenNextProcessed(); + } + else { + pPed->SetDie(); + } + return 0; + } + case COMMAND_SET_CAR_HEALTH: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->m_fHealth = ScriptParams[1]; + return 0; + } + case COMMAND_GET_PLAYER_HEALTH: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + ScriptParams[0] = pPed->m_fHealth; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_GET_CHAR_HEALTH: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + ScriptParams[0] = pPed->m_fHealth; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_GET_CAR_HEALTH: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + ScriptParams[0] = pVehicle->m_fHealth; + StoreParameters(&m_nIp, 1); + return 0; + } + /* + case COMMAND_IS_CAR_ARMED_WITH_BOMB: + { + CollectParameters(&m_nIp, 2); + CAutomobile* pCar = (CAutomobile*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pCar); + script_assert(pCar->m_vehType == VEHICLE_TYPE_CAR); + UpdateCompareFlag(pCar->m_bombType == ScriptParams[1]); + return 0; + } + */ + case COMMAND_CHANGE_CAR_COLOUR: + { + CollectParameters(&m_nIp, 3); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + if (ScriptParams[1] >= 256 || ScriptParams[2] >= 256) + debug("CHANGE_CAR_COLOUR - Colours must be less than %d", 256); + pVehicle->m_currentColour1 = ScriptParams[1]; + pVehicle->m_currentColour2 = ScriptParams[2]; + return 0; + } + case COMMAND_SWITCH_PED_ROADS_ON: + { + CollectParameters(&m_nIp, 6); + float infX = *(float*)&ScriptParams[0]; + float infY = *(float*)&ScriptParams[1]; + float infZ = *(float*)&ScriptParams[2]; + float supX = *(float*)&ScriptParams[3]; + float supY = *(float*)&ScriptParams[4]; + float supZ = *(float*)&ScriptParams[5]; + if (infX > supX) { + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[0]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[1]; + } + if (infZ > supZ) { + infZ = *(float*)&ScriptParams[5]; + supZ = *(float*)&ScriptParams[2]; + } + ThePaths.SwitchPedRoadsOffInArea(infX, supX, infY, supY, infZ, supZ, false); + return 0; + } + case COMMAND_SWITCH_PED_ROADS_OFF: + { + CollectParameters(&m_nIp, 6); + float infX = *(float*)&ScriptParams[0]; + float infY = *(float*)&ScriptParams[1]; + float infZ = *(float*)&ScriptParams[2]; + float supX = *(float*)&ScriptParams[3]; + float supY = *(float*)&ScriptParams[4]; + float supZ = *(float*)&ScriptParams[5]; + if (infX > supX) { + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[0]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[1]; + } + if (infZ > supZ) { + infZ = *(float*)&ScriptParams[5]; + supZ = *(float*)&ScriptParams[2]; + } + ThePaths.SwitchPedRoadsOffInArea(infX, supX, infY, supY, infZ, supZ, true); + return 0; + } + case COMMAND_CHAR_LOOK_AT_CHAR_ALWAYS: + { + CollectParameters(&m_nIp, 2); + CPed* pSourcePed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pSourcePed); + CPed* pTargetPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); + script_assert(pTargetPed); + pSourcePed->SetLookFlag(pTargetPed, true); + pSourcePed->SetLookTimer(60000); + return 0; + } + case COMMAND_CHAR_LOOK_AT_PLAYER_ALWAYS: + { + CollectParameters(&m_nIp, 2); + CPed* pSourcePed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pSourcePed); + CPed* pTargetPed = CWorld::Players[ScriptParams[1]].m_pPed; + script_assert(pTargetPed); + pSourcePed->SetLookFlag(pTargetPed, true); + pSourcePed->SetLookTimer(60000); + return 0; + } + case COMMAND_PLAYER_LOOK_AT_CHAR_ALWAYS: + { + CollectParameters(&m_nIp, 2); + CPed* pSourcePed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pSourcePed); + CPed* pTargetPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); + script_assert(pTargetPed); + pSourcePed->SetLookFlag(pTargetPed, true); + pSourcePed->SetLookTimer(60000); + return 0; + } + case COMMAND_STOP_CHAR_LOOKING: + { + CollectParameters(&m_nIp, 1); + CPed* pSourcePed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pSourcePed); + pSourcePed->ClearLookFlag(); + pSourcePed->bKeepTryingToLook = false; + if (pSourcePed->GetPedState() == PED_LOOK_HEADING || pSourcePed->GetPedState() == PED_LOOK_ENTITY) + pSourcePed->RestorePreviousState(); + return 0; + } + case COMMAND_STOP_PLAYER_LOOKING: + { + CollectParameters(&m_nIp, 1); + CPed* pSourcePed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pSourcePed); + pSourcePed->ClearLookFlag(); + pSourcePed->bKeepTryingToLook = false; + if (pSourcePed->GetPedState() == PED_LOOK_HEADING || pSourcePed->GetPedState() == PED_LOOK_ENTITY) + pSourcePed->RestorePreviousState(); + return 0; + } + /* + case COMMAND_SWITCH_HELICOPTER: + CollectParameters(&m_nIp, 1); + CHeli::ActivateHeli(ScriptParams[0] != 0); + return 0; + */ + //case COMMAND_SET_GANG_ATTITUDE: + //case COMMAND_SET_GANG_GANG_ATTITUDE: + //case COMMAND_SET_GANG_PLAYER_ATTITUDE: + case COMMAND_SET_GANG_PED_MODELS: + CollectParameters(&m_nIp, 3); + CGangs::SetGangPedModels(ScriptParams[0], ScriptParams[1], ScriptParams[2]); + return 0; + case COMMAND_SET_GANG_CAR_MODEL: + CollectParameters(&m_nIp, 2); + CGangs::SetGangVehicleModel(ScriptParams[0], ScriptParams[1]); + return 0; + case COMMAND_SET_GANG_WEAPONS: + CollectParameters(&m_nIp, 3); + CGangs::SetGangWeapons(ScriptParams[0], (eWeaponType)ScriptParams[1], (eWeaponType)ScriptParams[2]); + return 0; + /* + case COMMAND_SET_CHAR_OBJ_RUN_TO_AREA: + { + CollectParameters(&m_nIp, 5); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + float infX = *(float*)&ScriptParams[1]; + float infY = *(float*)&ScriptParams[2]; + float supX = *(float*)&ScriptParams[3]; + float supY = *(float*)&ScriptParams[4]; + if (infX > supX) { + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[1]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[2]; + } + CVector pos; + pos.x = (infX + supX) / 2; + pos.y = (infY + supY) / 2; + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + float radius = Max(pos.x - infX, pos.y - infY); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_RUN_TO_AREA, pos, radius); + return 0; + } + */ + case COMMAND_SET_CHAR_OBJ_RUN_TO_COORD: + { + CollectParameters(&m_nIp, 3); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CVector pos; + pos.x = *(float*)&ScriptParams[1]; + pos.y = *(float*)&ScriptParams[2]; + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_RUN_TO_AREA, pos); + return 0; + } + /* + case COMMAND_IS_PLAYER_TOUCHING_OBJECT_ON_FOOT: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[1]); + bool isTouching = false; + if (pPed->bInVehicle) + isTouching = false; + else if (pPed->GetHasCollidedWith(pObject)) + isTouching = true; + UpdateCompareFlag(isTouching); + return 0; + } + case COMMAND_IS_CHAR_TOUCHING_OBJECT_ON_FOOT: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[1]); + bool isTouching = false; + if (pPed->InVehicle()) + isTouching = false; + else if (pPed->GetHasCollidedWith(pObject)) + isTouching = true; + UpdateCompareFlag(isTouching); + return 0; + } + */ + case COMMAND_LOAD_SPECIAL_CHARACTER: + { + CollectParameters(&m_nIp, 1); + char name[16]; + strncpy(name, (char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); + for (int i = 0; i < KEY_LENGTH_IN_SCRIPT; i++) + name[i] = tolower(name[i]); + CStreaming::RequestSpecialChar(ScriptParams[0] - 1, name, STREAMFLAGS_DEPENDENCY | STREAMFLAGS_SCRIPTOWNED); + m_nIp += KEY_LENGTH_IN_SCRIPT; + return 0; + } + case COMMAND_HAS_SPECIAL_CHARACTER_LOADED: + { + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CStreaming::HasSpecialCharLoaded(ScriptParams[0] - 1)); + return 0; + } + /* + case COMMAND_FLASH_CAR: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->bHasBlip = (ScriptParams[1] != 0); + return 0; + } + case COMMAND_FLASH_CHAR: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bHasBlip = (ScriptParams[1] != 0); + return 0; + } + case COMMAND_FLASH_OBJECT: + { + CollectParameters(&m_nIp, 2); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + pObject->bHasBlip = (ScriptParams[1] != 0); + return 0; + } + */ + case COMMAND_IS_PLAYER_IN_REMOTE_MODE: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CWorld::Players[ScriptParams[0]].IsPlayerInRemoteMode()); + return 0; + /* + case COMMAND_ARM_CAR_WITH_BOMB: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + script_assert(pVehicle->m_vehType == VEHICLE_TYPE_CAR); + ((CAutomobile*)pVehicle)->m_bombType = ScriptParams[1]; + ((CAutomobile*)pVehicle)->m_pBombRigger = FindPlayerPed(); + return 0; + } + */ + case COMMAND_SET_CHAR_PERSONALITY: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->SetPedStats((ePedStats)ScriptParams[1]); + return 0; + } + case COMMAND_SET_CUTSCENE_OFFSET: + CollectParameters(&m_nIp, 3); + CCutsceneMgr::SetCutsceneOffset(*(CVector*)&ScriptParams[0]); + return 0; + case COMMAND_SET_ANIM_GROUP_FOR_CHAR: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->m_animGroup = (AssocGroupId)ScriptParams[1]; + return 0; + } + /* + case COMMAND_SET_ANIM_GROUP_FOR_PLAYER: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + pPed->m_animGroup = (AssocGroupId)ScriptParams[1]; + return 0; + } + */ + case COMMAND_REQUEST_MODEL: + { + CollectParameters(&m_nIp, 1); + int model = ScriptParams[0]; + if (model < 0) + model = CTheScripts::UsedObjectArray[-model].index; + CStreaming::RequestModel(model, STREAMFLAGS_DEPENDENCY | STREAMFLAGS_NOFADE | STREAMFLAGS_SCRIPTOWNED); + return 0; + } + case COMMAND_HAS_MODEL_LOADED: + { + CollectParameters(&m_nIp, 1); + int model = ScriptParams[0]; + if (model < 0) + model = CTheScripts::UsedObjectArray[-model].index; + UpdateCompareFlag(CStreaming::HasModelLoaded(model)); + return 0; + } + case COMMAND_MARK_MODEL_AS_NO_LONGER_NEEDED: + { + CollectParameters(&m_nIp, 1); + int model = ScriptParams[0]; + if (model < 0) + model = CTheScripts::UsedObjectArray[-model].index; + CStreaming::SetMissionDoesntRequireModel(model); + return 0; + } + case COMMAND_GRAB_PHONE: + { + CollectParameters(&m_nIp, 2); + ScriptParams[0] = gPhoneInfo.GrabPhone(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1]); + StoreParameters(&m_nIp, 1); + return 0; + } + /* + case COMMAND_SET_REPEATED_PHONE_MESSAGE: + { + CollectParameters(&m_nIp, 1); + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + gPhoneInfo.SetPhoneMessage_Repeatedly(ScriptParams[0], text, nil, nil, nil, nil, nil); + return 0; + } + case COMMAND_SET_PHONE_MESSAGE: + { + CollectParameters(&m_nIp, 1); + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + gPhoneInfo.SetPhoneMessage_JustOnce(ScriptParams[0], text, nil, nil, nil, nil, nil); + return 0; + } + case COMMAND_HAS_PHONE_DISPLAYED_MESSAGE: + { + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(gPhoneInfo.HasMessageBeenDisplayed(ScriptParams[0])); + return 0; + } + */ + case COMMAND_TURN_PHONE_OFF: + { + CollectParameters(&m_nIp, 1); + gPhoneInfo.SetPhoneMessage_JustOnce(ScriptParams[0], nil, nil, nil, nil, nil, nil); + return 0; + } + case COMMAND_DRAW_CORONA: + { + CollectParameters(&m_nIp, 9); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CCoronas::RegisterCorona((uintptr)this + m_nIp, ScriptParams[6], ScriptParams[7], ScriptParams[8], + 255, pos, *(float*)&ScriptParams[3], 450.0f, ScriptParams[4], ScriptParams[5], 1, 0, 0, 0.0f); + return 0; + } + case COMMAND_DRAW_LIGHT: + { + CollectParameters(&m_nIp, 6); + CVector pos = *(CVector*)&ScriptParams[0]; + CVector unused(0.0f, 0.0f, 0.0f); + CPointLights::AddLight(0, *(CVector*)&ScriptParams[0], CVector(0.0f, 0.0f, 0.0f), 12.0f, + ScriptParams[3] / 255.0f, ScriptParams[4] / 255.0f, ScriptParams[5] / 255.0f, 0, true); + return 0; + } + //case COMMAND_STORE_WEATHER: + //case COMMAND_RESTORE_WEATHER: + case COMMAND_STORE_CLOCK: + CClock::StoreClock(); + return 0; + case COMMAND_RESTORE_CLOCK: + CClock::RestoreClock(); + return 0; + /* + case COMMAND_RESTART_CRITICAL_MISSION: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CRestart::OverrideNextRestart(pos, *(float*)&ScriptParams[3]); + if (CWorld::Players[CWorld::PlayerInFocus].m_WBState != WBSTATE_PLAYING) + printf("RESTART_CRITICAL_MISSION - Player state is not PLAYING\n"); + CWorld::Players[CWorld::PlayerInFocus].PlayerFailedCriticalMission(); + return 0; + } + */ + case COMMAND_IS_PLAYER_PLAYING: + { + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CWorld::Players[ScriptParams[0]].m_WBState == WBSTATE_PLAYING); + return 0; + } +#ifdef GTA_SCRIPT_COLLECTIVE + case COMMAND_SET_COLL_OBJ_NO_OBJ: + CollectParameters(&m_nIp, 1); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_NONE); + return 0; +#endif + default: + script_assert(0); + } + return -1; +} + +int8 CRunningScript::ProcessCommands600To699(int32 command) +{ + switch (command){ +#ifdef GTA_SCRIPT_COLLECTIVE + case COMMAND_SET_COLL_OBJ_WAIT_ON_FOOT: + CollectParameters(&m_nIp, 1); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_WAIT_ON_FOOT); + return 0; + case COMMAND_SET_COLL_OBJ_FLEE_ON_FOOT_TILL_SAFE: + CollectParameters(&m_nIp, 1); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE); + return 0; + case COMMAND_SET_COLL_OBJ_GUARD_SPOT: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_GUARD_AREA, pos); + return 0; + } + case COMMAND_SET_COLL_OBJ_GUARD_AREA: + { + CollectParameters(&m_nIp, 5); + float infX = *(float*)&ScriptParams[1]; + float supX = *(float*)&ScriptParams[3]; + if (infX > supX) { + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[1]; + } + float infY = *(float*)&ScriptParams[2]; + float supY = *(float*)&ScriptParams[4]; + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[2]; + } + CVector pos; + pos.x = (infX + supX) / 2; + pos.y = (infY + supY) / 2; + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + float radius = Max(pos.x - infX, pos.y - infY); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_GUARD_AREA, pos, radius); + return 0; + } + case COMMAND_SET_COLL_OBJ_WAIT_IN_CAR: + CollectParameters(&m_nIp, 1); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_WAIT_IN_CAR); + return 0; + case COMMAND_SET_COLL_OBJ_KILL_CHAR_ON_FOOT: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_KILL_CHAR_ON_FOOT, pPed); + return 0; + } + case COMMAND_SET_COLL_OBJ_KILL_PLAYER_ON_FOOT: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[1]].m_pPed; + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_KILL_CHAR_ON_FOOT, pPed); + return 0; + } + case COMMAND_SET_COLL_OBJ_KILL_CHAR_ANY_MEANS: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_KILL_CHAR_ANY_MEANS, pPed); + return 0; + } + case COMMAND_SET_COLL_OBJ_KILL_PLAYER_ANY_MEANS: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[1]].m_pPed; + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_KILL_CHAR_ANY_MEANS, pPed); + return 0; + } + case COMMAND_SET_COLL_OBJ_FLEE_CHAR_ON_FOOT_TILL_SAFE: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, pPed); + return 0; + } + case COMMAND_SET_COLL_OBJ_FLEE_PLAYER_ON_FOOT_TILL_SAFE: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[1]].m_pPed; + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, pPed); + return 0; + } + case COMMAND_SET_COLL_OBJ_FLEE_CHAR_ON_FOOT_ALWAYS: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS, pPed); + return 0; + } + case COMMAND_SET_COLL_OBJ_FLEE_PLAYER_ON_FOOT_ALWAYS: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[1]].m_pPed; + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS, pPed); + return 0; + } + case COMMAND_SET_COLL_OBJ_GOTO_CHAR_ON_FOOT: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_GOTO_CHAR_ON_FOOT, pPed); + return 0; + } + case COMMAND_SET_COLL_OBJ_GOTO_PLAYER_ON_FOOT: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[1]].m_pPed; + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_GOTO_CHAR_ON_FOOT, pPed); + return 0; + } + case COMMAND_SET_COLL_OBJ_LEAVE_CAR: + CollectParameters(&m_nIp, 1); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_LEAVE_CAR); + return 0; + case COMMAND_SET_COLL_OBJ_ENTER_CAR_AS_PASSENGER: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_ENTER_CAR_AS_PASSENGER, pVehicle); + return 0; + } + case COMMAND_SET_COLL_OBJ_ENTER_CAR_AS_DRIVER: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_ENTER_CAR_AS_DRIVER, pVehicle); + return 0; + } + /* + case COMMAND_SET_COLL_OBJ_FOLLOW_CAR_IN_CAR: + case COMMAND_SET_COLL_OBJ_FIRE_AT_OBJECT_FROM_VEHICLE: + case COMMAND_SET_COLL_OBJ_DESTROY_OBJECT: + */ + case COMMAND_SET_COLL_OBJ_DESTROY_CAR: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_DESTROY_CAR, pVehicle); + return 0; + } + case COMMAND_SET_COLL_OBJ_GOTO_AREA_ON_FOOT: + { + CollectParameters(&m_nIp, 5); + float infX = *(float*)&ScriptParams[1]; + float supX = *(float*)&ScriptParams[3]; + if (infX > supX) { + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[1]; + } + float infY = *(float*)&ScriptParams[2]; + float supY = *(float*)&ScriptParams[4]; + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[2]; + } + CVector pos; + pos.x = (infX + supX) / 2; + pos.y = (infY + supY) / 2; + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + float radius = Max(pos.x - infX, pos.y - infY); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_GOTO_AREA_ON_FOOT, pos, radius); + return 0; + } + /* + case COMMAND_SET_COLL_OBJ_GOTO_AREA_IN_CAR: + case COMMAND_SET_COLL_OBJ_FOLLOW_CAR_ON_FOOT_WITH_OFFSET: + case COMMAND_SET_COLL_OBJ_GUARD_ATTACK: + */ + case COMMAND_SET_COLL_OBJ_FOLLOW_ROUTE: + CollectParameters(&m_nIp, 3); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_FOLLOW_ROUTE, ScriptParams[1], ScriptParams[2]); + return 0; + case COMMAND_SET_COLL_OBJ_GOTO_COORD_ON_FOOT: + { + CollectParameters(&m_nIp, 3); + CVector pos(*(float*)&ScriptParams[1], *(float*)&ScriptParams[2], CWorld::FindGroundZForCoord(*(float*)&ScriptParams[1], *(float*)&ScriptParams[2])); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_GOTO_AREA_ON_FOOT, pos); + return 0; + } + //case COMMAND_SET_COLL_OBJ_GOTO_COORD_IN_CAR: + case COMMAND_SET_COLL_OBJ_RUN_TO_AREA: + { + CollectParameters(&m_nIp, 5); + float infX = *(float*)&ScriptParams[1]; + float supX = *(float*)&ScriptParams[3]; + if (infX > supX) { + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[1]; + } + float infY = *(float*)&ScriptParams[2]; + float supY = *(float*)&ScriptParams[4]; + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[2]; + } + CVector pos; + pos.x = (infX + supX) / 2; + pos.y = (infY + supY) / 2; + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + float radius = Max(pos.x - infX, pos.y - infY); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_RUN_TO_AREA, pos, radius); + return 0; + } + case COMMAND_SET_COLL_OBJ_RUN_TO_COORD: + { + CollectParameters(&m_nIp, 3); + CVector pos(*(float*)&ScriptParams[1], *(float*)&ScriptParams[2], CWorld::FindGroundZForCoord(*(float*)&ScriptParams[1], *(float*)&ScriptParams[2])); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_RUN_TO_AREA, pos); + return 0; + } + case COMMAND_ADD_PEDS_IN_AREA_TO_COLL: + { + CollectParameters(&m_nIp, 3); + float X = *(float*)&ScriptParams[0]; + float Y = *(float*)&ScriptParams[1]; + float Z = CWorld::FindGroundZForCoord(X, Y); + float radius = *(float*)&ScriptParams[2]; + ScriptParams[0] = CTheScripts::AddPedsInAreaToCollective(X, Y, Z, radius); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_ADD_PEDS_IN_VEHICLE_TO_COLL: + CollectParameters(&m_nIp, 1); + ScriptParams[0] = CTheScripts::AddPedsInVehicleToCollective(ScriptParams[0]); + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_CLEAR_COLL: + CollectParameters(&m_nIp, 1); + for (int i = 0; i < MAX_NUM_COLLECTIVES; i++) { + if (CTheScripts::CollectiveArray[i].colIndex == ScriptParams[0]) { + CTheScripts::CollectiveArray[i].colIndex = -1; + CTheScripts::CollectiveArray[i].pedIndex = 0; + } + } + return 0; + case COMMAND_IS_COLL_IN_CARS: + { + CollectParameters(&m_nIp, 1); + bool result = true; + for (int i = 0; i < MAX_NUM_COLLECTIVES; i++) { + CPed* pPed = CPools::GetPedPool()->GetAt(CTheScripts::CollectiveArray[i].pedIndex); + if (!pPed) { + CTheScripts::CollectiveArray[i].colIndex = -1; + CTheScripts::CollectiveArray[i].pedIndex = 0; + } + else { + result = false; + break; + } + } + UpdateCompareFlag(result); + return 0; + } + case COMMAND_LOCATE_COLL_ANY_MEANS_2D: + case COMMAND_LOCATE_COLL_ON_FOOT_2D: + case COMMAND_LOCATE_COLL_IN_CAR_2D: + case COMMAND_LOCATE_STOPPED_COLL_ANY_MEANS_2D: + case COMMAND_LOCATE_STOPPED_COLL_ON_FOOT_2D: + case COMMAND_LOCATE_STOPPED_COLL_IN_CAR_2D: + LocateCollectiveCommand(command, &m_nIp); + return 0; + case COMMAND_LOCATE_COLL_ANY_MEANS_CHAR_2D: + case COMMAND_LOCATE_COLL_ON_FOOT_CHAR_2D: + case COMMAND_LOCATE_COLL_IN_CAR_CHAR_2D: + LocateCollectiveCharCommand(command, &m_nIp); + return 0; + case COMMAND_LOCATE_COLL_ANY_MEANS_CAR_2D: + case COMMAND_LOCATE_COLL_ON_FOOT_CAR_2D: + case COMMAND_LOCATE_COLL_IN_CAR_CAR_2D: + LocateCollectiveCarCommand(command, &m_nIp); + return 0; + case COMMAND_LOCATE_COLL_ANY_MEANS_PLAYER_2D: + case COMMAND_LOCATE_COLL_ON_FOOT_PLAYER_2D: + case COMMAND_LOCATE_COLL_IN_CAR_PLAYER_2D: + LocateCollectivePlayerCommand(command, &m_nIp); + return 0; + case COMMAND_IS_COLL_IN_AREA_2D: + case COMMAND_IS_COLL_IN_AREA_ON_FOOT_2D: + case COMMAND_IS_COLL_IN_AREA_IN_CAR_2D: + case COMMAND_IS_COLL_STOPPED_IN_AREA_2D: + case COMMAND_IS_COLL_STOPPED_IN_AREA_ON_FOOT_2D: + case COMMAND_IS_COLL_STOPPED_IN_AREA_IN_CAR_2D: + CollectiveInAreaCheckCommand(command, &m_nIp); + return 0; + case COMMAND_GET_NUMBER_OF_PEDS_IN_COLL: + { + CollectParameters(&m_nIp, 1); + int total = 0; + for (int i = 0; i < MAX_NUM_COLLECTIVES; i++) { + CPed* pPed = CPools::GetPedPool()->GetAt(CTheScripts::CollectiveArray[i].pedIndex); + if (!pPed) { + CTheScripts::CollectiveArray[i].colIndex = -1; + CTheScripts::CollectiveArray[i].pedIndex = 0; + } + else { + total++; + } + } + ScriptParams[0] = total; + StoreParameters(&m_nIp, 1); + return 0; + } +#endif + case COMMAND_SET_CHAR_HEED_THREATS: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bRespondsToThreats = (ScriptParams[1] != 0); + return 0; + } + case COMMAND_SET_PLAYER_HEED_THREATS: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + pPed->bRespondsToThreats = (ScriptParams[1] != 0); + return 0; + } + case COMMAND_GET_CONTROLLER_MODE: +#if defined(GTA_PC) && !defined(DETECT_PAD_INPUT_SWITCH) + ScriptParams[0] = 0; +#else + ScriptParams[0] = CPad::IsAffectedByController ? CPad::GetPad(0)->Mode : 0; +#endif + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_SET_CAN_RESPRAY_CAR: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + //assert(pVehicle->m_vehType == VEHICLE_TYPE_CAR); + // they DO call this for bikes, we don't really want to destroy the structure... +#ifdef FIX_BUGS + if (pVehicle->m_vehType == VEHICLE_TYPE_CAR) +#endif + ((CAutomobile*)pVehicle)->bFixedColour = (ScriptParams[1] == 0); + + return 0; + } + /* + case COMMAND_IS_TAXI: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + UpdateCompareFlag(pVehicle->IsTaxi()); + return 0; + } + */ + case COMMAND_UNLOAD_SPECIAL_CHARACTER: + CollectParameters(&m_nIp, 1); + CStreaming::SetMissionDoesntRequireSpecialChar(ScriptParams[0] - 1); + return 0; + case COMMAND_RESET_NUM_OF_MODELS_KILLED_BY_PLAYER: + CDarkel::ResetModelsKilledByPlayer(); + return 0; + case COMMAND_GET_NUM_OF_MODELS_KILLED_BY_PLAYER: + CollectParameters(&m_nIp, 1); + ScriptParams[0] = CDarkel::QueryModelsKilledByPlayer(ScriptParams[0]); + StoreParameters(&m_nIp, 1); + return 0; + /* + case COMMAND_ACTIVATE_GARAGE: + CollectParameters(&m_nIp, 1); + CGarages::ActivateGarage(ScriptParams[0]); + return 0; + case COMMAND_SWITCH_TAXI_TIMER: + { + CollectParameters(&m_nIp, 1); + if (ScriptParams[0] != 0){ + CWorld::Players[CWorld::PlayerInFocus].m_nUnusedTaxiTimer = CTimer::GetTimeInMilliseconds(); + CWorld::Players[CWorld::PlayerInFocus].m_bUnusedTaxiThing = true; + }else{ + CWorld::Players[CWorld::PlayerInFocus].m_bUnusedTaxiThing = false; + } + return 0; + } + */ + case COMMAND_CREATE_OBJECT_NO_OFFSET: + { + CollectParameters(&m_nIp, 4); + int mi = ScriptParams[0] >= 0 ? ScriptParams[0] : CTheScripts::UsedObjectArray[-ScriptParams[0]].index; + CObject* pObj = new CObject(mi, false); +; pObj->ObjectCreatedBy = MISSION_OBJECT; + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + pObj->SetPosition(pos); + pObj->SetOrientation(0.0f, 0.0f, 0.0f); + pObj->GetMatrix().UpdateRW(); + pObj->UpdateRwFrame(); + CBaseModelInfo* pModelInfo = CModelInfo::GetModelInfo(mi); + if (pModelInfo->IsBuilding() && ((CSimpleModelInfo*)pModelInfo)->m_isBigBuilding) + pObj->SetupBigBuilding(); + CTheScripts::ClearSpaceForMissionEntity(pos, pObj); + CWorld::Add(pObj); + ScriptParams[0] = CPools::GetObjectPool()->GetIndex(pObj); + StoreParameters(&m_nIp, 1); + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.AddEntityToList(ScriptParams[0], CLEANUP_OBJECT); + return 0; + } + /* + case COMMAND_IS_BOAT: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + UpdateCompareFlag(pVehicle->m_vehType == VEHICLE_TYPE_BOAT); + return 0; + } + case COMMAND_SET_CHAR_OBJ_GOTO_AREA_ANY_MEANS: + { + CollectParameters(&m_nIp, 5); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + float infX = *(float*)&ScriptParams[1]; + float infY = *(float*)&ScriptParams[2]; + float supX = *(float*)&ScriptParams[3]; + float supY = *(float*)&ScriptParams[4]; + if (infX > supX) { + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[1]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[2]; + } + CVector pos; + pos.x = (infX + supX) / 2; + pos.y = (infY + supY) / 2; + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + float radius = Max(pos.x - infX, pos.y - infY); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_GOTO_AREA_ANY_MEANS, pos, radius); + return 0; + } + */ +#ifdef GTA_SCRIPT_COLLECTIVE + case COMMAND_SET_COLL_OBJ_GOTO_AREA_ANY_MEANS: + { + CollectParameters(&m_nIp, 5); + float infX = *(float*)&ScriptParams[1]; + float supX = *(float*)&ScriptParams[3]; + if (infX > supX) { + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[1]; + } + float infY = *(float*)&ScriptParams[2]; + float supY = *(float*)&ScriptParams[4]; + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[2]; + } + CVector pos; + pos.x = (infX + supX) / 2; + pos.y = (infY + supY) / 2; + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + float radius = Max(pos.x - infX, pos.y - infY); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_GOTO_AREA_ANY_MEANS, pos, radius); + return 0; + } +#endif + case COMMAND_IS_PLAYER_STOPPED: + { + CollectParameters(&m_nIp, 1); + CPlayerInfo* pPlayer = &CWorld::Players[ScriptParams[0]]; + UpdateCompareFlag(CTheScripts::IsPlayerStopped(pPlayer)); + return 0; + + } + /* + case COMMAND_IS_CHAR_STOPPED: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + UpdateCompareFlag(CTheScripts::IsPedStopped(pPed)); + return 0; + } + case COMMAND_MESSAGE_WAIT: + CollectParameters(&m_nIp, 2); + m_nWakeTime = CTimer::GetTimeInMilliseconds() + ScriptParams[0]; + if (ScriptParams[1] != 0) + m_bSkipWakeTime = true; + return 1; + case COMMAND_ADD_PARTICLE_EFFECT: + { + CollectParameters(&m_nIp, 5); + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CParticleObject::AddObject(ScriptParams[0], pos, ScriptParams[4] != 0); + return 0; + } + */ + case COMMAND_SWITCH_WIDESCREEN: + CollectParameters(&m_nIp, 1); + if (ScriptParams[0] != 0) + TheCamera.SetWideScreenOn(); + else + TheCamera.SetWideScreenOff(); + return 0; + /* + case COMMAND_ADD_SPRITE_BLIP_FOR_CAR: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + int id = CRadar::SetEntityBlip(BLIP_CAR, ScriptParams[0], 0, BLIP_DISPLAY_BOTH); + CRadar::SetBlipSprite(id, ScriptParams[1]); + ScriptParams[0] = id; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_ADD_SPRITE_BLIP_FOR_CHAR: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + int id = CRadar::SetEntityBlip(BLIP_CHAR, ScriptParams[0], 1, BLIP_DISPLAY_BOTH); + CRadar::SetBlipSprite(id, ScriptParams[1]); + ScriptParams[0] = id; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_ADD_SPRITE_BLIP_FOR_OBJECT: + { + CollectParameters(&m_nIp, 2); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + int id = CRadar::SetEntityBlip(BLIP_OBJECT, ScriptParams[0], 6, BLIP_DISPLAY_BOTH); + CRadar::SetBlipSprite(id, ScriptParams[1]); + ScriptParams[0] = id; + StoreParameters(&m_nIp, 1); + return 0; + } + */ + case COMMAND_ADD_SPRITE_BLIP_FOR_CONTACT_POINT: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + int id = CRadar::SetCoordBlip(BLIP_CONTACT_POINT, pos, 2, BLIP_DISPLAY_BOTH); + CRadar::SetBlipSprite(id, ScriptParams[3]); + ScriptParams[0] = id; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_ADD_SPRITE_BLIP_FOR_COORD: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + int id = CRadar::SetCoordBlip(BLIP_COORD, pos, 5, BLIP_DISPLAY_BOTH); + CRadar::SetBlipSprite(id, ScriptParams[3]); + ScriptParams[0] = id; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_SET_CHAR_ONLY_DAMAGED_BY_PLAYER: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bOnlyDamagedByPlayer = (ScriptParams[1] != 0); + return 0; + } + case COMMAND_SET_CAR_ONLY_DAMAGED_BY_PLAYER: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->bOnlyDamagedByPlayer = (ScriptParams[1] != 0); + return 0; + } + case COMMAND_SET_CHAR_PROOFS: + { + CollectParameters(&m_nIp, 6); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bBulletProof = (ScriptParams[1] != 0); + pPed->bFireProof = (ScriptParams[2] != 0); + pPed->bExplosionProof = (ScriptParams[3] != 0); + pPed->bCollisionProof = (ScriptParams[4] != 0); + pPed->bMeleeProof = (ScriptParams[5] != 0); + return 0; + } + case COMMAND_SET_CAR_PROOFS: + { + CollectParameters(&m_nIp, 6); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->bBulletProof = (ScriptParams[1] != 0); + pVehicle->bFireProof = (ScriptParams[2] != 0); + pVehicle->bExplosionProof = (ScriptParams[3] != 0); + pVehicle->bCollisionProof = (ScriptParams[4] != 0); + pVehicle->bMeleeProof = (ScriptParams[5] != 0); + return 0; + } + case COMMAND_IS_PLAYER_IN_ANGLED_AREA_2D: + case COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_2D: + case COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_2D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_2D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_2D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_2D: + case COMMAND_IS_PLAYER_IN_ANGLED_AREA_3D: + case COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_3D: + case COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_3D: + PlayerInAngledAreaCheckCommand(command, &m_nIp); + return 0; + /* + case COMMAND_DEACTIVATE_GARAGE: + CollectParameters(&m_nIp, 1); + CGarages::DeActivateGarage(ScriptParams[0]); + return 0; + case COMMAND_GET_NUMBER_OF_CARS_COLLECTED_BY_GARAGE: + CollectParameters(&m_nIp, 1); + ScriptParams[0] = CGarages::QueryCarsCollected(ScriptParams[0]); + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_HAS_CAR_BEEN_TAKEN_TO_GARAGE: + CollectParameters(&m_nIp, 2); + UpdateCompareFlag(CGarages::HasThisCarBeenCollected(ScriptParams[0], ScriptParams[1] - 1)); + return 0; + */ + default: + script_assert(0); + } + return -1; +} + +int8 CRunningScript::ProcessCommands700To799(int32 command) +{ + switch (command){ + /* + case COMMAND_SET_SWAT_REQUIRED: + CollectParameters(&m_nIp, 1); + FindPlayerPed()->m_pWanted->m_bSwatRequired = (ScriptParams[0] != 0); + return 0; + case COMMAND_SET_FBI_REQUIRED: + CollectParameters(&m_nIp, 1); + FindPlayerPed()->m_pWanted->m_bFbiRequired = (ScriptParams[0] != 0); + return 0; + case COMMAND_SET_ARMY_REQUIRED: + CollectParameters(&m_nIp, 1); + FindPlayerPed()->m_pWanted->m_bArmyRequired = (ScriptParams[0] != 0); + return 0; + */ + case COMMAND_IS_CAR_IN_WATER: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + UpdateCompareFlag(pVehicle && pVehicle->bIsInWater); + return 0; + } + case COMMAND_GET_CLOSEST_CHAR_NODE: + { + CollectParameters(&m_nIp, 3); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CPathNode* pNode = &ThePaths.m_pathNodes[ThePaths.FindNodeClosestToCoors(pos, 1, 999999.9f, true)]; + *(CVector*)&ScriptParams[0] = pNode->GetPosition(); + StoreParameters(&m_nIp, 3); + return 0; + } + case COMMAND_GET_CLOSEST_CAR_NODE: + { + CollectParameters(&m_nIp, 3); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + *(CVector*)&ScriptParams[0] = ThePaths.FindNodeCoorsForScript(ThePaths.FindNodeClosestToCoors(pos, 0, 999999.9f, true, true)); + StoreParameters(&m_nIp, 3); + return 0; + } + case COMMAND_CAR_GOTO_COORDINATES_ACCURATE: + { + CollectParameters(&m_nIp, 4); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + pos.z += pVehicle->GetDistanceFromCentreOfMassToBaseOfModel(); + if (CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, pos, false)) + pVehicle->AutoPilot.m_nCarMission = MISSION_GOTO_COORDS_STRAIGHT_ACCURATE; + else + pVehicle->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS_ACCURATE; + pVehicle->SetStatus(STATUS_PHYSICS); + pVehicle->bEngineOn = true; + pVehicle->AutoPilot.m_nCruiseSpeed = Max(1, pVehicle->AutoPilot.m_nCruiseSpeed); + pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); + return 0; + } + /* + case COMMAND_START_PACMAN_RACE: + CollectParameters(&m_nIp, 1); + CPacManPickups::StartPacManRace(ScriptParams[0]); + return 0; + case COMMAND_START_PACMAN_RECORD: + CPacManPickups::StartPacManRecord(); + return 0; + case COMMAND_GET_NUMBER_OF_POWER_PILLS_EATEN: + ScriptParams[0] = CPacManPickups::QueryPowerPillsEatenInRace(); + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_CLEAR_PACMAN: + CPacManPickups::CleanUpPacManStuff(); + return 0; + case COMMAND_START_PACMAN_SCRAMBLE: + { + CollectParameters(&m_nIp, 5); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CPacManPickups::StartPacManScramble(pos, *(float*)&ScriptParams[3], ScriptParams[4]); + return 0; + } + case COMMAND_GET_NUMBER_OF_POWER_PILLS_CARRIED: + ScriptParams[0] = CPacManPickups::QueryPowerPillsCarriedByPlayer(); + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_CLEAR_NUMBER_OF_POWER_PILLS_CARRIED: + CPacManPickups::ResetPowerPillsCarriedByPlayer(); + return 0; + */ + case COMMAND_IS_CAR_ON_SCREEN: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + UpdateCompareFlag(TheCamera.IsSphereVisible(pVehicle->GetBoundCentre(), pVehicle->GetBoundRadius())); + return 0; + } + case COMMAND_IS_CHAR_ON_SCREEN: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(TheCamera.IsSphereVisible(pPed->GetBoundCentre(), pPed->GetBoundRadius())); + return 0; + } + case COMMAND_IS_OBJECT_ON_SCREEN: + { + CollectParameters(&m_nIp, 1); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + UpdateCompareFlag(TheCamera.IsSphereVisible(pObject->GetBoundCentre(), pObject->GetBoundRadius())); + return 0; + } + /* + case COMMAND_GOSUB_FILE: + { + CollectParameters(&m_nIp, 2); + script_assert(m_nStackPointer < MAX_STACK_DEPTH); + m_anStack[m_nStackPointer++] = m_nIp; + SetIP(ScriptParams[0]); + // ScriptParams[1] == filename + return 0; + } + */ + case COMMAND_GET_GROUND_Z_FOR_3D_COORD: + { + CollectParameters(&m_nIp, 3); + CVector pos = *(CVector*)&ScriptParams[0]; + bool success; + *(float*)&ScriptParams[0] = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &success); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_START_SCRIPT_FIRE: + { + CollectParameters(&m_nIp, 3); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + ScriptParams[0] = gFireManager.StartScriptFire(pos, nil, 0.8f, 1); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_IS_SCRIPT_FIRE_EXTINGUISHED: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(gFireManager.IsScriptFireExtinguish(ScriptParams[0])); + return 0; + case COMMAND_REMOVE_SCRIPT_FIRE: + CollectParameters(&m_nIp, 1); + gFireManager.RemoveScriptFire(ScriptParams[0]); + return 0; + /* + case COMMAND_SET_COMEDY_CONTROLS: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->bComedyControls = (ScriptParams[1] != 0); + return 0; + } + */ + case COMMAND_BOAT_GOTO_COORDS: + { + CollectParameters(&m_nIp, 4); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + script_assert(pVehicle->m_vehType == VEHICLE_TYPE_BOAT); + CBoat* pBoat = (CBoat*)pVehicle; + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= MAP_Z_LOW_LIMIT) + CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &pos.z, false); + pBoat->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS_ASTHECROWSWIMS; + pBoat->AutoPilot.m_vecDestinationCoors = pos; + pBoat->SetStatus(STATUS_PHYSICS); + pBoat->bEngineOn = true; + pBoat->AutoPilot.m_nCruiseSpeed = Max(1, pBoat->AutoPilot.m_nCruiseSpeed); + pBoat->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); + return 0; + } + case COMMAND_BOAT_STOP: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + script_assert(pVehicle->m_vehType == VEHICLE_TYPE_BOAT); + CBoat* pBoat = (CBoat*)pVehicle; + pBoat->AutoPilot.m_nCarMission = MISSION_NONE; + pBoat->SetStatus(STATUS_PHYSICS); + pBoat->bEngineOn = false; + pBoat->AutoPilot.m_nCruiseSpeed = 0; + return 0; + } + case COMMAND_IS_PLAYER_SHOOTING_IN_AREA: + { + CollectParameters(&m_nIp, 6); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + float x1 = *(float*)&ScriptParams[1]; + float y1 = *(float*)&ScriptParams[2]; + float x2 = *(float*)&ScriptParams[3]; + float y2 = *(float*)&ScriptParams[4]; + UpdateCompareFlag(pPed->bIsShooting && pPed->IsWithinArea(x1, y1, x2, y2)); + if (ScriptParams[5]) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) + CTheScripts::DrawDebugSquare(x1, y1, x2, y2); + return 0; + } + case COMMAND_IS_CHAR_SHOOTING_IN_AREA: + { + CollectParameters(&m_nIp, 6); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + float x1 = *(float*)&ScriptParams[1]; + float y1 = *(float*)&ScriptParams[2]; + float x2 = *(float*)&ScriptParams[3]; + float y2 = *(float*)&ScriptParams[4]; + UpdateCompareFlag(pPed->bIsShooting && pPed->IsWithinArea(x1, y1, x2, y2)); + if (ScriptParams[5]) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) + CTheScripts::DrawDebugSquare(x1, y1, x2, y2); + return 0; + } + case COMMAND_IS_CURRENT_PLAYER_WEAPON: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + UpdateCompareFlag(ScriptParams[1] == pPed->GetWeapon()->m_eWeaponType); + return 0; + } + case COMMAND_IS_CURRENT_CHAR_WEAPON: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(ScriptParams[1] == pPed->GetWeapon()->m_eWeaponType); + return 0; + } + /* + case COMMAND_CLEAR_NUMBER_OF_POWER_PILLS_EATEN: + CPacManPickups::ResetPowerPillsEatenInRace(); + return 0; + case COMMAND_ADD_POWER_PILL: + { + CollectParameters(&m_nIp, 3); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CPacManPickups::GenerateOnePMPickUp(pos); + return 0; + } + */ + case COMMAND_SET_BOAT_CRUISE_SPEED: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + script_assert(pVehicle->m_vehType == VEHICLE_TYPE_BOAT); + CBoat* pBoat = (CBoat*)pVehicle; + pBoat->AutoPilot.m_nCruiseSpeed = *(float*)&ScriptParams[1]; + return 0; + } + /* + case COMMAND_GET_RANDOM_CHAR_IN_AREA: + { + CollectParameters(&m_nIp, 4); + int ped_handle = -1; + CVector pos = FindPlayerCoors(); + float x1 = *(float*)&ScriptParams[0]; + float y1 = *(float*)&ScriptParams[1]; + float x2 = *(float*)&ScriptParams[2]; + float y2 = *(float*)&ScriptParams[3]; + int i = CPools::GetPedPool()->GetSize(); + while (--i && ped_handle == -1){ + CPed* pPed = CPools::GetPedPool()->GetSlot(i); + if (!pPed) + continue; + if (CTheScripts::LastRandomPedId == CPools::GetPedPool()->GetIndex(pPed)) + continue; + if (pPed->CharCreatedBy != RANDOM_CHAR) + continue; + if (!pPed->IsPedInControl()) + continue; + if (pPed->bRemoveFromWorld) + continue; + if (pPed->bFadeOut) + continue; +// if (pPed->GetModelIndex() == MI_SCUM_WOM || pPed->GetModelIndex() == MI_SCUM_MAN) +// continue; + if (!ThisIsAValidRandomPed(pPed->m_nPedType)) + continue; + if (pPed->bIsLeader || pPed->m_leader) + continue; + if (!pPed->IsWithinArea(x1, y1, x2, y2)) + continue; + if (pos.z - PED_FIND_Z_OFFSET > pPed->GetPosition().z) + continue; + if (pos.z + PED_FIND_Z_OFFSET < pPed->GetPosition().z) + continue; + ped_handle = CPools::GetPedPool()->GetIndex(pPed); + CTheScripts::LastRandomPedId = ped_handle; + pPed->CharCreatedBy = MISSION_CHAR; + pPed->bRespondsToThreats = false; + ++CPopulation::ms_nTotalMissionPeds; + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.AddEntityToList(ped_handle, CLEANUP_CHAR); + } + ScriptParams[0] = ped_handle; + StoreParameters(&m_nIp, 1); + return 0; + } + */ + case COMMAND_GET_RANDOM_CHAR_IN_ZONE: + { + char zone[KEY_LENGTH_IN_SCRIPT]; + strncpy(zone, (const char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); + int nZone = CTheZones::FindZoneByLabelAndReturnIndex(zone, ZONE_DEFAULT); + if (nZone != -1) + m_nIp += KEY_LENGTH_IN_SCRIPT; + CZone* pZone = CTheZones::GetNavigationZone(nZone); + CollectParameters(&m_nIp, 3); + int ped_handle = -1; + CVector pos = FindPlayerCoors(); + int i = CPools::GetPedPool()->GetSize(); + while (--i && ped_handle == -1) { + CPed* pPed = CPools::GetPedPool()->GetSlot(i); + if (!pPed) + continue; + if (CTheScripts::LastRandomPedId == CPools::GetPedPool()->GetIndex(pPed)) + continue; + if (pPed->CharCreatedBy != RANDOM_CHAR) + continue; + if (!pPed->IsPedInControl()) + continue; + if (pPed->bRemoveFromWorld) + continue; + if (pPed->bFadeOut) + continue; + if (pPed->m_nWaitState != WAITSTATE_FALSE) + continue; + if (!ThisIsAValidRandomPed(pPed->m_nPedType, ScriptParams[0], ScriptParams[1], ScriptParams[2])) + continue; + if (pPed->bIsLeader || pPed->m_leader) + continue; + if (!CTheZones::PointLiesWithinZone(&pPed->GetPosition(), pZone)) + continue; + if (pos.z - PED_FIND_Z_OFFSET > pPed->GetPosition().z) + continue; + if (pos.z + PED_FIND_Z_OFFSET < pPed->GetPosition().z) + continue; + bool found; + CWorld::FindRoofZFor3DCoord(pos.x, pos.y, pos.z, &found); + if (found) + continue; + ped_handle = CPools::GetPedPool()->GetIndex(pPed); + CTheScripts::LastRandomPedId = ped_handle; + pPed->CharCreatedBy = MISSION_CHAR; + pPed->bRespondsToThreats = false; + ++CPopulation::ms_nTotalMissionPeds; + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.AddEntityToList(ped_handle, CLEANUP_CHAR); + } + ScriptParams[0] = ped_handle; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_IS_PLAYER_IN_TAXI: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle->IsTaxi()); + return 0; + } + case COMMAND_IS_PLAYER_SHOOTING: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + UpdateCompareFlag(pPed->bIsShooting); + return 0; + } + case COMMAND_IS_CHAR_SHOOTING: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(pPed->bIsShooting); + return 0; + } + case COMMAND_CREATE_MONEY_PICKUP: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; + CPickups::GetActualPickupIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + ScriptParams[0] = CPickups::GenerateNewOne(pos, MI_MONEY, PICKUP_MONEY, ScriptParams[3]); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_SET_CHAR_ACCURACY: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->m_wepAccuracy = ScriptParams[1]; + return 0; + } + case COMMAND_GET_CAR_SPEED: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + *(float*)&ScriptParams[0] = pVehicle->GetSpeed().Magnitude() * GAME_SPEED_TO_METERS_PER_SECOND; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_LOAD_CUTSCENE: + { + char name[KEY_LENGTH_IN_SCRIPT]; + strncpy(name, (const char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); + m_nIp += KEY_LENGTH_IN_SCRIPT; + CCutsceneMgr::LoadCutsceneData(name); + return 0; + } + case COMMAND_CREATE_CUTSCENE_OBJECT: + { + CollectParameters(&m_nIp, 1); + CCutsceneObject* pCutObj = CCutsceneMgr::CreateCutsceneObject(ScriptParams[0]); + ScriptParams[0] = CPools::GetObjectPool()->GetIndex(pCutObj); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_SET_CUTSCENE_ANIM: + { + CollectParameters(&m_nIp, 1); + char name[KEY_LENGTH_IN_SCRIPT]; + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + strncpy(name, (const char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); + m_nIp += KEY_LENGTH_IN_SCRIPT; + CCutsceneMgr::SetCutsceneAnim(name, pObject); + return 0; + } + case COMMAND_START_CUTSCENE: + CCutsceneMgr::ms_cutsceneLoadStatus = 1; + return 0; + case COMMAND_GET_CUTSCENE_TIME: + ScriptParams[0] = CCutsceneMgr::GetCutsceneTimeInMilleseconds(); + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_HAS_CUTSCENE_FINISHED: + UpdateCompareFlag(CCutsceneMgr::HasCutsceneFinished()); + return 0; + case COMMAND_CLEAR_CUTSCENE: + CCutsceneMgr::DeleteCutsceneData(); + return 0; + case COMMAND_RESTORE_CAMERA_JUMPCUT: + TheCamera.RestoreWithJumpCut(); + return 0; + case COMMAND_CREATE_COLLECTABLE1: + { + CollectParameters(&m_nIp, 3); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; + CPickups::GenerateNewOne(pos, MI_COLLECTABLE1, PICKUP_COLLECTABLE1, 0); + return 0; + } + case COMMAND_SET_COLLECTABLE1_TOTAL: + CollectParameters(&m_nIp, 1); + CWorld::Players[CWorld::PlayerInFocus].m_nTotalPackages = ScriptParams[0]; + return 0; + /* + case COMMAND_IS_PROJECTILE_IN_AREA: + { + CollectParameters(&m_nIp, 6); + float infX = *(float*)&ScriptParams[0]; + float infY = *(float*)&ScriptParams[1]; + float infZ = *(float*)&ScriptParams[2]; + float supX = *(float*)&ScriptParams[3]; + float supY = *(float*)&ScriptParams[4]; + float supZ = *(float*)&ScriptParams[5]; + if (infX > supX) { + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[0]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[1]; + } + if (infZ > supZ) { + infZ = *(float*)&ScriptParams[5]; + supZ = *(float*)&ScriptParams[2]; + } + UpdateCompareFlag(CProjectileInfo::IsProjectileInRange(infX, supX, infY, supY, infZ, supZ, false)); + if (CTheScripts::DbgFlag) + CTheScripts::DrawDebugCube(infX, infY, infZ, supX, supY, supZ); + return 0; + } + case COMMAND_DESTROY_PROJECTILES_IN_AREA: + { + CollectParameters(&m_nIp, 6); + float infX = *(float*)&ScriptParams[0]; + float infY = *(float*)&ScriptParams[1]; + float infZ = *(float*)&ScriptParams[2]; + float supX = *(float*)&ScriptParams[3]; + float supY = *(float*)&ScriptParams[4]; + float supZ = *(float*)&ScriptParams[5]; + if (infX > supX) { + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[0]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[1]; + } + if (infZ > supZ) { + infZ = *(float*)&ScriptParams[5]; + supZ = *(float*)&ScriptParams[2]; + } + UpdateCompareFlag(CProjectileInfo::IsProjectileInRange(infX, supX, infY, supY, infZ, supZ, true)); + if (CTheScripts::DbgFlag) + CTheScripts::DrawDebugCube(infX, infY, infZ, supX, supY, supZ); + return 0; + } + case COMMAND_DROP_MINE: + { + CollectParameters(&m_nIp, 3); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; + CPickups::GenerateNewOne(pos, MI_CARMINE, PICKUP_MINE_INACTIVE, 0); + return 0; + } + case COMMAND_DROP_NAUTICAL_MINE: + { + CollectParameters(&m_nIp, 3); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; + CPickups::GenerateNewOne(pos, MI_NAUTICALMINE, PICKUP_MINE_INACTIVE, 0); + return 0; + } + */ + case COMMAND_IS_CHAR_MODEL: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(ScriptParams[1] == pPed->GetModelIndex()); + return 0; + } + case COMMAND_LOAD_SPECIAL_MODEL: + { + CollectParameters(&m_nIp, 1); + char name[KEY_LENGTH_IN_SCRIPT]; + strncpy(name, (const char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); + for (int i = 0; i < KEY_LENGTH_IN_SCRIPT; i++) + name[i] = tolower(name[i]); + CStreaming::RequestSpecialModel(ScriptParams[0], name, STREAMFLAGS_DEPENDENCY | STREAMFLAGS_SCRIPTOWNED); + m_nIp += KEY_LENGTH_IN_SCRIPT; + return 0; + } + //case COMMAND_CREATE_CUTSCENE_HEAD: + //case COMMAND_SET_CUTSCENE_HEAD_ANIM: + case COMMAND_SIN: + CollectParameters(&m_nIp, 1); + *(float*)&ScriptParams[0] = Sin(DEGTORAD(*(float*)&ScriptParams[0])); + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_COS: + CollectParameters(&m_nIp, 1); + *(float*)&ScriptParams[0] = Cos(DEGTORAD(*(float*)&ScriptParams[0])); + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_GET_CAR_FORWARD_X: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + float forwardX = pVehicle->GetForward().x / pVehicle->GetForward().Magnitude2D(); + *(float*)&ScriptParams[0] = forwardX; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_GET_CAR_FORWARD_Y: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + float forwardY = pVehicle->GetForward().y / pVehicle->GetForward().Magnitude2D(); + *(float*)&ScriptParams[0] = forwardY; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_CHANGE_GARAGE_TYPE: + CollectParameters(&m_nIp, 2); + CGarages::ChangeGarageType(ScriptParams[0], ScriptParams[1], 0); + return 0; + /* + case COMMAND_ACTIVATE_CRUSHER_CRANE: + { + CollectParameters(&m_nIp, 10); + float infX = *(float*)&ScriptParams[2]; + float infY = *(float*)&ScriptParams[3]; + float supX = *(float*)&ScriptParams[4]; + float supY = *(float*)&ScriptParams[5]; + if (infX > supX) { + infX = *(float*)&ScriptParams[4]; + supX = *(float*)&ScriptParams[2]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[5]; + supY = *(float*)&ScriptParams[3]; + } + CCranes::ActivateCrane(infX, supX, infY, supY, + *(float*)&ScriptParams[6], *(float*)&ScriptParams[7], *(float*)&ScriptParams[8], + DEGTORAD(*(float*)&ScriptParams[9]), true, false, + *(float*)&ScriptParams[0], *(float*)&ScriptParams[1]); + return 0; + } + case COMMAND_PRINT_WITH_2_NUMBERS: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 4); + CMessages::AddMessageWithNumber(text, ScriptParams[2], ScriptParams[3], ScriptParams[0], ScriptParams[1], -1, -1, -1, -1); + return 0; + } + */ + case COMMAND_PRINT_WITH_2_NUMBERS_NOW: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 4); + CMessages::AddMessageJumpQWithNumber(text, ScriptParams[2], ScriptParams[3], ScriptParams[0], ScriptParams[1], -1, -1, -1, -1); + return 0; + } + /* + case COMMAND_PRINT_WITH_2_NUMBERS_SOON: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 4); + CMessages::AddMessageSoonWithNumber(text, ScriptParams[2], ScriptParams[3], ScriptParams[0], ScriptParams[1], -1, -1, -1, -1); + return 0; + } + */ + case COMMAND_PRINT_WITH_3_NUMBERS: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 5); + CMessages::AddMessageWithNumber(text, ScriptParams[3], ScriptParams[4], ScriptParams[0], ScriptParams[1], ScriptParams[2], -1, -1, -1); + return 0; + } + /* + case COMMAND_PRINT_WITH_3_NUMBERS_NOW: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 5); + CMessages::AddMessageJumpQWithNumber(text, ScriptParams[3], ScriptParams[4], ScriptParams[0], ScriptParams[1], ScriptParams[2], -1, -1, -1); + return 0; + } + case COMMAND_PRINT_WITH_3_NUMBERS_SOON: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 5); + CMessages::AddMessageSoonWithNumber(text, ScriptParams[3], ScriptParams[4], ScriptParams[0], ScriptParams[1], ScriptParams[2], -1, -1, -1); + return 0; + } + */ + case COMMAND_PRINT_WITH_4_NUMBERS: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 6); + CMessages::AddMessageWithNumber(text, ScriptParams[4], ScriptParams[5], ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], -1, -1); + return 0; + } + /* + case COMMAND_PRINT_WITH_4_NUMBERS_NOW: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 6); + CMessages::AddMessageJumpQWithNumber(text, ScriptParams[4], ScriptParams[5], ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], -1, -1); + return 0; + } + case COMMAND_PRINT_WITH_4_NUMBERS_SOON: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 6); + CMessages::AddMessageSoonWithNumber(text, ScriptParams[4], ScriptParams[5], ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], -1, -1); + return 0; + } + case COMMAND_PRINT_WITH_5_NUMBERS: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 7); + CMessages::AddMessageWithNumber(text, ScriptParams[5], ScriptParams[6], ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], ScriptParams[4], -1); + return 0; + } + case COMMAND_PRINT_WITH_5_NUMBERS_NOW: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 7); + CMessages::AddMessageJumpQWithNumber(text, ScriptParams[5], ScriptParams[6], ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], ScriptParams[4], -1); + return 0; + } + case COMMAND_PRINT_WITH_5_NUMBERS_SOON: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 7); + CMessages::AddMessageSoonWithNumber(text, ScriptParams[5], ScriptParams[6], ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], ScriptParams[4], -1); + return 0; + } + */ + case COMMAND_PRINT_WITH_6_NUMBERS: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 8); + CMessages::AddMessageWithNumber(text, ScriptParams[6], ScriptParams[7], ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], ScriptParams[4], ScriptParams[5]); + return 0; + } + /* + case COMMAND_PRINT_WITH_6_NUMBERS_NOW: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 8); + CMessages::AddMessageJumpQWithNumber(text, ScriptParams[6], ScriptParams[7], ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], ScriptParams[4], ScriptParams[5]); + return 0; + } + case COMMAND_PRINT_WITH_6_NUMBERS_SOON: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 8); + CMessages::AddMessageSoonWithNumber(text, ScriptParams[6], ScriptParams[7], ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], ScriptParams[4], ScriptParams[5]); + return 0; + } + case COMMAND_SET_CHAR_OBJ_FOLLOW_CHAR_IN_FORMATION: + { + CollectParameters(&m_nIp, 3); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CPed* pTargetPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_FOLLOW_CHAR_IN_FORMATION, pTargetPed); + pPed->SetFormation((eFormation)ScriptParams[2]); + return 0; + } + */ + case COMMAND_PLAYER_MADE_PROGRESS: + CollectParameters(&m_nIp, 1); + CStats::ProgressMade += ScriptParams[0]; + return 0; + case COMMAND_SET_PROGRESS_TOTAL: + CollectParameters(&m_nIp, 1); + CStats::TotalProgressInGame = ScriptParams[0]; + if (CGame::germanGame) + CStats::TotalProgressInGame -= 2; + return 0; + case COMMAND_REGISTER_JUMP_DISTANCE: + CollectParameters(&m_nIp, 1); + CStats::MaximumJumpDistance = Max(CStats::MaximumJumpDistance, *(float*)&ScriptParams[0]); + return 0; + case COMMAND_REGISTER_JUMP_HEIGHT: + CollectParameters(&m_nIp, 1); + CStats::MaximumJumpHeight = Max(CStats::MaximumJumpHeight, *(float*)&ScriptParams[0]); + return 0; + case COMMAND_REGISTER_JUMP_FLIPS: + CollectParameters(&m_nIp, 1); + CStats::MaximumJumpFlips = Max(CStats::MaximumJumpFlips, ScriptParams[0]); + return 0; + case COMMAND_REGISTER_JUMP_SPINS: + CollectParameters(&m_nIp, 1); + CStats::MaximumJumpSpins = Max(CStats::MaximumJumpSpins, ScriptParams[0]); + return 0; + case COMMAND_REGISTER_JUMP_STUNT: + CollectParameters(&m_nIp, 1); + CStats::BestStuntJump = Max(CStats::BestStuntJump, ScriptParams[0]); + return 0; + case COMMAND_REGISTER_UNIQUE_JUMP_FOUND: + ++CStats::NumberOfUniqueJumpsFound; + return 0; + case COMMAND_SET_UNIQUE_JUMPS_TOTAL: + CollectParameters(&m_nIp, 1); + CStats::TotalNumberOfUniqueJumps = ScriptParams[0]; + return 0; + case COMMAND_REGISTER_PASSENGER_DROPPED_OFF_TAXI: + ++CStats::PassengersDroppedOffWithTaxi; + return 0; + case COMMAND_REGISTER_MONEY_MADE_TAXI: + CollectParameters(&m_nIp, 1); + CStats::MoneyMadeWithTaxi += ScriptParams[0]; + return 0; + case COMMAND_REGISTER_MISSION_GIVEN: + ++CStats::MissionsGiven; + return 0; + case COMMAND_REGISTER_MISSION_PASSED: + { + char name[KEY_LENGTH_IN_SCRIPT]; + strncpy(name, (const char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); + m_nIp += KEY_LENGTH_IN_SCRIPT; + strncpy(CStats::LastMissionPassedName, name, KEY_LENGTH_IN_SCRIPT); + ++CStats::MissionsPassed; + CStats::CheckPointReachedSuccessfully(); + CTheScripts::LastMissionPassedTime = CTimer::GetTimeInMilliseconds(); + CGameLogic::RemoveShortCutDropOffPointForMission(); + return 0; + } + case COMMAND_SET_CHAR_RUNNING: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bIsRunning = (ScriptParams[1] != 0); + return 0; + } + case COMMAND_REMOVE_ALL_SCRIPT_FIRES: + gFireManager.RemoveAllScriptFires(); + return 0; + /* + case COMMAND_IS_FIRST_CAR_COLOUR: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + UpdateCompareFlag(pVehicle->m_currentColour1 == ScriptParams[1]); + return 0; + } + case COMMAND_IS_SECOND_CAR_COLOUR: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + UpdateCompareFlag(pVehicle->m_currentColour2 == ScriptParams[1]); + return 0; + } + */ + case COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_WEAPON: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + bool result = false; + if (!pPed) + printf("HAS_CHAR_BEEN_DAMAGED_BY_WEAPON - Character doesn't exist\n"); + else { + if (ScriptParams[1] == WEAPONTYPE_ANYMELEE || ScriptParams[1] == WEAPONTYPE_ANYWEAPON) + result = CheckDamagedWeaponType(pPed->m_lastWepDam, ScriptParams[1]); + else + result = ScriptParams[1] == pPed->m_lastWepDam; + } + UpdateCompareFlag(result); + return 0; + } + case COMMAND_HAS_CAR_BEEN_DAMAGED_BY_WEAPON: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + bool result = false; + if (!pVehicle) + printf("HAS_CAR_BEEN_DAMAGED_BY_WEAPON - Vehicle doesn't exist\n"); + else { + if (ScriptParams[1] == WEAPONTYPE_ANYMELEE || ScriptParams[1] == WEAPONTYPE_ANYWEAPON) + result = CheckDamagedWeaponType(pVehicle->m_nLastWeaponDamage, ScriptParams[1]); + else + result = ScriptParams[1] == pVehicle->m_nLastWeaponDamage; + } + UpdateCompareFlag(result); + return 0; + } + case COMMAND_IS_CHAR_IN_CHARS_GROUP: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + CPed* pLeader = CPools::GetPedPool()->GetAt(ScriptParams[1]); + script_assert(pPed); + script_assert(pLeader); + UpdateCompareFlag(pPed->m_leader == pLeader && !pPed->bWaitForLeaderToComeCloser); + return 0; + } + default: + script_assert(0); + } + return -1; +} diff --git a/src/miami/control/Script4.cpp b/src/miami/control/Script4.cpp new file mode 100644 index 00000000..16a4ddae --- /dev/null +++ b/src/miami/control/Script4.cpp @@ -0,0 +1,2348 @@ +#include "common.h" + +#include "Script.h" +#include "ScriptCommands.h" + +#include "AnimBlendAssociation.h" +#include "BulletInfo.h" +#include "CarAI.h" +#include "CarCtrl.h" +#include "CivilianPed.h" +#include "Cranes.h" +#include "DMAudio.h" +#include "Darkel.h" +#include "Explosion.h" +#include "Fire.h" +#include "Frontend.h" +#include "Garages.h" +#include "General.h" +#include "Heli.h" +#include "Hud.h" +#include "Messages.h" +#include "ParticleObject.h" +#include "PedRoutes.h" +#include "Phones.h" +#include "Pickups.h" +#include "Plane.h" +#include "Pools.h" +#include "Population.h" +#include "Radar.h" +#include "Record.h" +#include "RpAnimBlend.h" +#include "Rubbish.h" +#include "SpecialFX.h" +#include "Stats.h" +#include "Streaming.h" +#include "TxdStore.h" +#include "User.h" +#include "WaterLevel.h" +#include "World.h" +#include "Zones.h" +#include "Bike.h" +#include "Wanted.h" + +#ifdef FIX_BUGS +static bool IsSlideObjectUsedWrongByScript(const CVector& posTarget, const CVector& slideBy) +{ + if (posTarget == CVector(-559.476f, 784.807f, 23.279f) && slideBy == CVector(0.0f, 10.0f, 0.0f)) + return true; // G-Spotlight bottom elevator, east side + if (posTarget == CVector(-559.476f, 779.64f, 23.279f) && slideBy == CVector(0.0f, 10.0f, 0.0f)) + return true; // G-Spotlight bottom elevator, west side + if (posTarget == CVector(-553.563f, 790.595f, 97.917f) && slideBy == CVector(0.0f, 10.0f, 0.0f)) + return true; // G-Spotlight top elevator, east side + if (posTarget == CVector(-553.563f, 785.427f, 97.917f) && slideBy == CVector(0.0f, 10.0f, 0.0f)) + return true; // G-Spotlight top elevator, west side + if (posTarget == CVector(-866.689f, -572.095f, 15.573f) && slideBy == CVector(0.0f, 0.0f, 4.5f)) + return true; // Cherry Popper garage door + return false; +} +#endif + +int8 CRunningScript::ProcessCommands800To899(int32 command) +{ + CMatrix tmp_matrix; + switch (command) { + case COMMAND_IS_CHAR_IN_PLAYERS_GROUP: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + CPed* pLeader = CWorld::Players[ScriptParams[1]].m_pPed; + script_assert(pPed); + script_assert(pLeader); + UpdateCompareFlag(pPed->m_leader == pLeader && !pPed->bWaitForLeaderToComeCloser); + return 0; + } + case COMMAND_EXPLODE_CHAR_HEAD: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->InflictDamage(nil, WEAPONTYPE_SNIPERRIFLE, 1000.0f, PEDPIECE_HEAD, 0); + return 0; + } + case COMMAND_EXPLODE_PLAYER_HEAD: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + pPed->InflictDamage(nil, WEAPONTYPE_SNIPERRIFLE, 1000.0f, PEDPIECE_HEAD, 0); + return 0; + } + case COMMAND_ANCHOR_BOAT: + { + CollectParameters(&m_nIp, 2); + CBoat* pBoat = (CBoat*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pBoat && pBoat->m_vehType == VEHICLE_TYPE_BOAT); + pBoat->m_bIsAnchored = (ScriptParams[1] != 0); + return 0; + } + case COMMAND_SET_ZONE_GROUP: + { + char zone[KEY_LENGTH_IN_SCRIPT]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, zone); + m_nIp += KEY_LENGTH_IN_SCRIPT; + CollectParameters(&m_nIp, 2); + int zone_id = CTheZones::FindZoneByLabelAndReturnIndex(zone, ZONE_INFO); + if (zone_id < 0) { + printf("Couldn't find zone - %s\n", zone); + return 0; + } + while (zone_id >= 0) { + CTheZones::SetPedGroup(zone_id, ScriptParams[0], ScriptParams[1]); + zone_id = CTheZones::FindNextZoneByLabelAndReturnIndex(zone, ZONE_INFO); + } + return 0; + } + case COMMAND_START_CAR_FIRE: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + ScriptParams[0] = gFireManager.StartScriptFire(pVehicle->GetPosition(), pVehicle, 0.8f, 1); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_START_CHAR_FIRE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + ScriptParams[0] = gFireManager.StartScriptFire(pPed->GetPosition(), pPed, 0.8f, 1); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_GET_RANDOM_CAR_OF_TYPE_IN_AREA: + { + CollectParameters(&m_nIp, 5); + int handle = -1; + uint32 i = CPools::GetVehiclePool()->GetSize(); + float infX = *(float*)&ScriptParams[0]; + float infY = *(float*)&ScriptParams[1]; + float supX = *(float*)&ScriptParams[2]; + float supY = *(float*)&ScriptParams[3]; + while (i--) { + CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); + if (!pVehicle) + continue; + if (pVehicle->GetVehicleAppearance() != VEHICLE_APPEARANCE_CAR && pVehicle->GetVehicleAppearance() != VEHICLE_APPEARANCE_BIKE) + continue; + if (!pVehicle->bUsesCollision) + continue; + if (ScriptParams[4] != pVehicle->GetModelIndex() && ScriptParams[4] >= 0) + continue; + if (pVehicle->VehicleCreatedBy != RANDOM_VEHICLE) + continue; + if (!pVehicle->IsWithinArea(infX, infY, supX, supY)) + continue; + handle = CPools::GetVehiclePool()->GetIndex(pVehicle); + pVehicle->VehicleCreatedBy = MISSION_VEHICLE; + ++CCarCtrl::NumMissionCars; + --CCarCtrl::NumRandomCars; + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.AddEntityToList(handle, CLEANUP_CAR); + } + ScriptParams[0] = handle; + StoreParameters(&m_nIp, 1); + return 0; + } + /* + case COMMAND_GET_RANDOM_CAR_OF_TYPE_IN_ZONE: + { + char zone[KEY_LENGTH_IN_SCRIPT]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, zone); + int zone_id = CTheZones::FindZoneByLabelAndReturnIndex(zone, ZONE_DEFAULT); + if (zone_id != -1) + m_nIp += KEY_LENGTH_IN_SCRIPT; + CZone* pZone = CTheZones::GetNavigationZone(zone_id); + CollectParameters(&m_nIp, 1); + int handle = -1; + uint32 i = CPools::GetVehiclePool()->GetSize(); + while (i--) { + CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); + if (!pVehicle) + continue; + if (ScriptParams[0] != pVehicle->GetModelIndex() && ScriptParams[0] >= 0) + continue; + if (pVehicle->VehicleCreatedBy != RANDOM_VEHICLE) + continue; + if (!CTheZones::PointLiesWithinZone(&pVehicle->GetPosition(), pZone)) + continue; + handle = CPools::GetVehiclePool()->GetIndex(pVehicle); + pVehicle->VehicleCreatedBy = MISSION_VEHICLE; + ++CCarCtrl::NumMissionCars; + --CCarCtrl::NumRandomCars; + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.AddEntityToList(handle, CLEANUP_CAR); + } + ScriptParams[0] = handle; + StoreParameters(&m_nIp, 1); + return 0; + } + */ + case COMMAND_HAS_RESPRAY_HAPPENED: + { + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CGarages::HasResprayHappened(ScriptParams[0])); + return 0; + } + case COMMAND_SET_CAMERA_ZOOM: + { + CollectParameters(&m_nIp, 1); + if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_FOLLOWPED) + TheCamera.SetZoomValueFollowPedScript(ScriptParams[0]); + else if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_CAM_ON_A_STRING) + TheCamera.SetZoomValueCamStringScript(ScriptParams[0]); + return 0; + } + case COMMAND_CREATE_PICKUP_WITH_AMMO: + { + CollectParameters(&m_nIp, 6); + int16 model = ScriptParams[0]; + if (model < 0) + model = CTheScripts::UsedObjectArray[-model].index; + CVector pos = *(CVector*)&ScriptParams[3]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; + CPickups::GetActualPickupIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + ScriptParams[0] = CPickups::GenerateNewOne(pos, model, ScriptParams[1], ScriptParams[2]); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_SET_CAR_RAM_CAR: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + CVehicle* pTarget = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + script_assert(pTarget); + CCarAI::TellCarToRamOtherCar(pVehicle, pTarget); + return 0; + } + /* + case COMMAND_SET_CAR_BLOCK_CAR: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + CVehicle* pTarget = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + script_assert(pTarget); + CCarAI::TellCarToBlockOtherCar(pVehicle, pTarget); + return 0; + } + case COMMAND_SET_CHAR_OBJ_CATCH_TRAIN: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_CATCH_TRAIN); + return 0; + } + */ +#ifdef GTA_SCRIPT_COLLECTIVE + case COMMAND_SET_COLL_OBJ_CATCH_TRAIN: + CollectParameters(&m_nIp, 1); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_CATCH_TRAIN); + return 0; +#endif + case COMMAND_SET_PLAYER_NEVER_GETS_TIRED: + { + CollectParameters(&m_nIp, 2); + CPlayerInfo* pPlayer = &CWorld::Players[ScriptParams[0]]; + pPlayer->m_bInfiniteSprint = (ScriptParams[1] != 0); + return 0; + } + case COMMAND_SET_PLAYER_FAST_RELOAD: + { + CollectParameters(&m_nIp, 2); + CPlayerInfo* pPlayer = &CWorld::Players[ScriptParams[0]]; + pPlayer->m_bFastReload = (ScriptParams[1] != 0); + return 0; + } + case COMMAND_SET_CHAR_BLEEDING: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bPedIsBleeding = (ScriptParams[1] != 0); + return 0; + } + /* + case COMMAND_SET_CAR_FUNNY_SUSPENSION: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + // no action + return 0; + } + case COMMAND_SET_CAR_BIG_WHEELS: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + script_assert(pVehicle->m_vehType == VEHICLE_TYPE_CAR); + CAutomobile* pCar = (CAutomobile*)pVehicle; + pCar->bBigWheels = (ScriptParams[1] != 0); + return 0; + } + */ + case COMMAND_SET_FREE_RESPRAYS: + CollectParameters(&m_nIp, 1); + CGarages::SetFreeResprays(ScriptParams[0] != 0); + return 0; + case COMMAND_SET_PLAYER_VISIBLE: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + pPed->bIsVisible = (ScriptParams[1] != 0); + return 0; + } + case COMMAND_SET_CHAR_VISIBLE: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bIsVisible = (ScriptParams[1] != 0); + return 0; + } + /* + case COMMAND_SET_CAR_VISIBLE: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->bIsVisible = (ScriptParams[1] != 0); + return 0; + } + */ + case COMMAND_IS_AREA_OCCUPIED: + { + CollectParameters(&m_nIp, 11); + float infX = *(float*)&ScriptParams[0]; + float infY = *(float*)&ScriptParams[1]; + float infZ = *(float*)&ScriptParams[2]; + float supX = *(float*)&ScriptParams[3]; + float supY = *(float*)&ScriptParams[4]; + float supZ = *(float*)&ScriptParams[5]; + if (infX > supX) { + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[0]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[1]; + } + if (infZ > supZ) { + infZ = *(float*)&ScriptParams[5]; + supZ = *(float*)&ScriptParams[2]; + } + int16 total; + CWorld::FindObjectsIntersectingCube(CVector(infX, infY, infZ), CVector(supX, supY, supZ), &total, 2, nil, + !!ScriptParams[6], !!ScriptParams[7], !!ScriptParams[8], !!ScriptParams[9], !!ScriptParams[10]); + UpdateCompareFlag(total > 0); + return 0; + } + /* + case COMMAND_START_DRUG_RUN: + CPlane::CreateIncomingCesna(); + return 0; + case COMMAND_HAS_DRUG_RUN_BEEN_COMPLETED: + UpdateCompareFlag(CPlane::HasCesnaLanded()); + return 0; + case COMMAND_HAS_DRUG_PLANE_BEEN_SHOT_DOWN: + UpdateCompareFlag(CPlane::HasCesnaBeenDestroyed()); + return 0; + case COMMAND_SAVE_PLAYER_FROM_FIRES: + CollectParameters(&m_nIp, 1); + gFireManager.ExtinguishPoint(CWorld::Players[ScriptParams[0]].GetPos(), 3.0f); + return 0; + */ + case COMMAND_DISPLAY_TEXT: + { + CollectParameters(&m_nIp, 2); + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fAtX = *(float*)&ScriptParams[0]; + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fAtY = *(float*)&ScriptParams[1]; + uint16 len = CMessages::GetWideStringLength(text); + for (uint16 i = 0; i < len; i++) + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_Text[i] = text[i]; + for (uint16 i = len; i < SCRIPT_TEXT_MAX_LENGTH; i++) + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_Text[i] = 0; + ++CTheScripts::NumberOfIntroTextLinesThisFrame; + return 0; + } + case COMMAND_SET_TEXT_SCALE: + { + CollectParameters(&m_nIp, 2); + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fScaleX = *(float*)&ScriptParams[0]; + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fScaleY = *(float*)&ScriptParams[1]; + return 0; + } + case COMMAND_SET_TEXT_COLOUR: + { + CollectParameters(&m_nIp, 4); + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_sColor = + CRGBA(ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3]); + return 0; + } + case COMMAND_SET_TEXT_JUSTIFY: + { + CollectParameters(&m_nIp, 1); + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_bJustify = (ScriptParams[0] != 0); + return 0; + } + case COMMAND_SET_TEXT_CENTRE: + { + CollectParameters(&m_nIp, 1); + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_bCentered = (ScriptParams[0] != 0); + return 0; + } + case COMMAND_SET_TEXT_WRAPX: + { + CollectParameters(&m_nIp, 1); + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fWrapX = *(float*)&ScriptParams[0]; + return 0; + } + /* + case COMMAND_SET_TEXT_CENTRE_SIZE: + { + CollectParameters(&m_nIp, 1); + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fCenterSize = *(float*)&ScriptParams[0]; + return 0; + } + */ + case COMMAND_SET_TEXT_BACKGROUND: + { + CollectParameters(&m_nIp, 1); + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_bBackground = (ScriptParams[0] != 0); + return 0; + } + /* + case COMMAND_SET_TEXT_BACKGROUND_COLOUR: + { + CollectParameters(&m_nIp, 4); + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_sBackgroundColor = + CRGBA(ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3]); + return 0; + } + case COMMAND_SET_TEXT_BACKGROUND_ONLY_TEXT: + { + CollectParameters(&m_nIp, 1); + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_bBackgroundOnly = (ScriptParams[0] != 0); + return 0; + } + */ + case COMMAND_SET_TEXT_PROPORTIONAL: + { + CollectParameters(&m_nIp, 1); + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_bTextProportional = (ScriptParams[0] != 0); + return 0; + } + /* + case COMMAND_SET_TEXT_FONT: + { + CollectParameters(&m_nIp, 1); + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_nFont = ScriptParams[0]; + return 0; + } + case COMMAND_INDUSTRIAL_PASSED: + CStats::IndustrialPassed = true; + DMAudio.PlayRadioAnnouncement(STREAMED_SOUND_ANNOUNCE_COMMERCIAL_OPEN); + return 0; + case COMMAND_COMMERCIAL_PASSED: + CStats::CommercialPassed = true; + DMAudio.PlayRadioAnnouncement(STREAMED_SOUND_ANNOUNCE_SUBURBAN_OPEN); + return 0; + case COMMAND_SUBURBAN_PASSED: + CStats::SuburbanPassed = true; + return 0; + */ + case COMMAND_ROTATE_OBJECT: + { + CollectParameters(&m_nIp, 4); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + float heading = LimitAngleOnCircle( + RADTODEG(Atan2(-pObject->GetForward().x, pObject->GetForward().y))); + float headingTarget = *(float*)&ScriptParams[1]; +#ifdef FIX_BUGS + float rotateBy = *(float*)&ScriptParams[2] * CTimer::GetTimeStepFix(); +#else + float rotateBy = *(float*)&ScriptParams[2]; +#endif + if (headingTarget == heading) { // using direct comparasion here is fine + UpdateCompareFlag(true); + return 0; + } + float angleClockwise = LimitAngleOnCircle(headingTarget - heading); + float angleCounterclockwise = LimitAngleOnCircle(heading - headingTarget); + float newHeading; + if (angleClockwise < angleCounterclockwise) + newHeading = rotateBy < angleClockwise ? heading + rotateBy : headingTarget; + else + newHeading = rotateBy < angleCounterclockwise ? heading - rotateBy : headingTarget; + bool obstacleInPath = false; + if (ScriptParams[3]) { + CVector pos = pObject->GetPosition(); + tmp_matrix.SetRotateZ(DEGTORAD(newHeading)); + tmp_matrix.GetPosition() += pos; + CColModel* pColModel = pObject->GetColModel(); + CVector cp1 = tmp_matrix * pColModel->boundingBox.min; + CVector cp2 = tmp_matrix * CVector(pColModel->boundingBox.max.x, pColModel->boundingBox.min.y, pColModel->boundingBox.min.z); + CVector cp3 = tmp_matrix * CVector(pColModel->boundingBox.min.x, pColModel->boundingBox.max.y, pColModel->boundingBox.min.z); + CVector cp4 = tmp_matrix * CVector(pColModel->boundingBox.min.x, pColModel->boundingBox.min.y, pColModel->boundingBox.max.z); + int16 collisions; + CWorld::FindObjectsIntersectingAngledCollisionBox(pColModel->boundingBox, tmp_matrix, pos, + Min(cp1.x, Min(cp2.x, Min(cp3.x, cp4.x))), + Min(cp1.y, Min(cp2.y, Min(cp3.y, cp4.y))), + Max(cp1.x, Max(cp2.x, Max(cp3.x, cp4.x))), + Max(cp1.y, Max(cp2.y, Max(cp3.y, cp4.y))), + &collisions, 2, nil, false, true, true, false, false); + if (collisions > 0) + obstacleInPath = true; + } + if (obstacleInPath) { + UpdateCompareFlag(true); + return 0; + } + pObject->SetHeading(DEGTORAD(newHeading)); + pObject->GetMatrix().UpdateRW(); + pObject->UpdateRwFrame(); + UpdateCompareFlag(newHeading == headingTarget); // using direct comparasion here is fine + return 0; + } + case COMMAND_SLIDE_OBJECT: + { + CollectParameters(&m_nIp, 8); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + CVector pos = pObject->GetPosition(); + CVector posTarget = *(CVector*)&ScriptParams[1]; + CVector slideBy = *(CVector*)&ScriptParams[4]; +#ifdef FIX_BUGS + // the check is a hack for original script, where some objects are moved + // via SLIDE_OBJECT instead of SET_OBJECT_POSITION + // assuming the slide will take exactly one frame, which is true + // only without accounting time step (which is a bug) + if (!IsSlideObjectUsedWrongByScript(posTarget, slideBy)) + slideBy *= CTimer::GetTimeStepFix(); +#endif + if (posTarget == pos) { // using direct comparasion here is fine + UpdateCompareFlag(true); + return 0; + } + CVector posDiff = pos - posTarget; + CVector newPosition; + if (posDiff.x < 0) + newPosition.x = -posDiff.x < slideBy.x ? posTarget.x : pos.x + slideBy.x; + else + newPosition.x = posDiff.x < slideBy.x ? posTarget.x : pos.x - slideBy.x; + if (posDiff.y < 0) + newPosition.y = -posDiff.y < slideBy.y ? posTarget.y : pos.y + slideBy.y; + else + newPosition.y = posDiff.y < slideBy.y ? posTarget.y : pos.y - slideBy.y; + if (posDiff.z < 0) + newPosition.z = -posDiff.z < slideBy.z ? posTarget.z : pos.z + slideBy.z; + else + newPosition.z = posDiff.z < slideBy.z ? posTarget.z : pos.z - slideBy.z; + bool obstacleInPath = false; + if (ScriptParams[7]) { + tmp_matrix = pObject->GetMatrix(); + tmp_matrix.GetPosition() = newPosition; + CColModel* pColModel = pObject->GetColModel(); + CVector cp1 = tmp_matrix * pColModel->boundingBox.min; + CVector cp2 = tmp_matrix * CVector(pColModel->boundingBox.max.x, pColModel->boundingBox.min.y, pColModel->boundingBox.min.z); + CVector cp3 = tmp_matrix * CVector(pColModel->boundingBox.min.x, pColModel->boundingBox.max.y, pColModel->boundingBox.min.z); + CVector cp4 = tmp_matrix * CVector(pColModel->boundingBox.min.x, pColModel->boundingBox.min.y, pColModel->boundingBox.max.z); + int16 collisions; + CWorld::FindObjectsIntersectingAngledCollisionBox(pColModel->boundingBox, tmp_matrix, newPosition, + Min(cp1.x, Min(cp2.x, Min(cp3.x, cp4.x))), + Min(cp1.y, Min(cp2.y, Min(cp3.y, cp4.y))), + Max(cp1.x, Max(cp2.x, Max(cp3.x, cp4.x))), + Max(cp1.y, Max(cp2.y, Max(cp3.y, cp4.y))), + &collisions, 2, nil, false, true, true, false, false); + if (collisions > 0) + obstacleInPath = true; + } + if (obstacleInPath) { + UpdateCompareFlag(true); + return 0; + } + pObject->Teleport(newPosition); + UpdateCompareFlag(newPosition == posTarget); // using direct comparasion here is fine + return 0; + } + case COMMAND_REMOVE_CHAR_ELEGANTLY: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + if (pPed && pPed->CharCreatedBy == MISSION_CHAR){ + CWorld::RemoveReferencesToDeletedObject(pPed); + if (pPed->bInVehicle && pPed->m_pMyVehicle) + CTheScripts::RemoveThisPed(pPed); + else{ + pPed->CharCreatedBy = RANDOM_CHAR; + pPed->bRespondsToThreats = true; + pPed->bScriptObjectiveCompleted = false; + pPed->ClearLeader(); + --CPopulation::ms_nTotalMissionPeds; + pPed->bFadeOut = true; + CWorld::RemoveReferencesToDeletedObject(pPed); + } + } + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_CHAR); + return 0; + } + case COMMAND_SET_CHAR_STAY_IN_SAME_PLACE: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bKindaStayInSamePlace = (ScriptParams[1] != 0); + return 0; + } + /* + case COMMAND_IS_NASTY_GAME: + UpdateCompareFlag(CGame::nastyGame); + return 0; + */ + case COMMAND_UNDRESS_CHAR: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + char name[KEY_LENGTH_IN_SCRIPT]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, name); + for (int i = 0; i < KEY_LENGTH_IN_SCRIPT; i++) + name[i] = tolower(name[i]); + m_nIp += KEY_LENGTH_IN_SCRIPT; + pPed->Undress(name); + return 0; + } + case COMMAND_DRESS_CHAR: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->Dress(); + return 0; + } + /* + case COMMAND_START_CHASE_SCENE: + CollectParameters(&m_nIp, 1); + CTimer::Suspend(); + CStreaming::DeleteAllRwObjects(); + CRecordDataForChase::StartChaseScene(*(float*)&ScriptParams[0]); + CTimer::Resume(); + return 0; + case COMMAND_STOP_CHASE_SCENE: + CRecordDataForChase::CleanUpChaseScene(); + return 0; + case COMMAND_IS_EXPLOSION_IN_AREA: + { + CollectParameters(&m_nIp, 7); + float infX = *(float*)&ScriptParams[1]; + float infY = *(float*)&ScriptParams[2]; + float infZ = *(float*)&ScriptParams[3]; + float supX = *(float*)&ScriptParams[4]; + float supY = *(float*)&ScriptParams[5]; + float supZ = *(float*)&ScriptParams[6]; + if (infX > supX) { + infX = *(float*)&ScriptParams[4]; + supX = *(float*)&ScriptParams[1]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[5]; + supY = *(float*)&ScriptParams[2]; + } + if (infZ > supZ) { + infZ = *(float*)&ScriptParams[6]; + supZ = *(float*)&ScriptParams[3]; + } + UpdateCompareFlag(CExplosion::TestForExplosionInArea((eExplosionType)ScriptParams[0], + infX, supX, infY, supY, infZ, supZ)); + return 0; + } + case COMMAND_IS_EXPLOSION_IN_ZONE: + { + CollectParameters(&m_nIp, 1); + char zone[KEY_LENGTH_IN_SCRIPT]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, zone); + int zone_id = CTheZones::FindZoneByLabelAndReturnIndex(zone, ZONE_DEFAULT); + if (zone_id != -1) + m_nIp += KEY_LENGTH_IN_SCRIPT; + CZone* pZone = CTheZones::GetNavigationZone(zone_id); + UpdateCompareFlag(CExplosion::TestForExplosionInArea((eExplosionType)ScriptParams[0], + pZone->minx, pZone->maxx, pZone->miny, pZone->maxy, pZone->minz, pZone->maxz)); + return 0; + } + case COMMAND_START_DRUG_DROP_OFF: + CPlane::CreateDropOffCesna(); + return 0; + case COMMAND_HAS_DROP_OFF_PLANE_BEEN_SHOT_DOWN: + UpdateCompareFlag(CPlane::HasDropOffCesnaBeenShotDown()); + return 0; + case COMMAND_FIND_DROP_OFF_PLANE_COORDINATES: + { + CVector pos = CPlane::FindDropOffCesnaCoordinates(); + *(CVector*)&ScriptParams[0] = pos; + StoreParameters(&m_nIp, 3); + return 0; + } + case COMMAND_CREATE_FLOATING_PACKAGE: + { + CollectParameters(&m_nIp, 3); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; + ScriptParams[0] = CPickups::GenerateNewOne(pos, MI_FLOATPACKAGE1, PICKUP_FLOATINGPACKAGE, 0); + StoreParameters(&m_nIp, 1); + return 0; + } + */ + case COMMAND_PLACE_OBJECT_RELATIVE_TO_CAR: + { + CollectParameters(&m_nIp, 5); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + script_assert(pVehicle); + CVector offset = *(CVector*)&ScriptParams[2]; + CPhysical::PlacePhysicalRelativeToOtherPhysical(pVehicle, pObject, offset); + return 0; + } + case COMMAND_MAKE_OBJECT_TARGETTABLE: + { + CollectParameters(&m_nIp, 1); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + CPlayerPed* pPlayerPed = CWorld::Players[CWorld::PlayerInFocus].m_pPed; + script_assert(pPlayerPed); + pPlayerPed->MakeObjectTargettable(ScriptParams[0]); + return 0; + } + case COMMAND_ADD_ARMOUR_TO_PLAYER: + { + CollectParameters(&m_nIp, 2); + CPlayerPed* pPlayerPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPlayerPed); + pPlayerPed->m_fArmour = Clamp(pPlayerPed->m_fArmour + ScriptParams[1], 0.0f, CWorld::Players[ScriptParams[0]].m_nMaxArmour); + return 0; + } + case COMMAND_ADD_ARMOUR_TO_CHAR: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->m_fArmour = Clamp(pPed->m_fArmour + ScriptParams[1], 0.0f, 100.0f); + return 0; + } + case COMMAND_OPEN_GARAGE: + { + CollectParameters(&m_nIp, 1); + CGarages::OpenGarage(ScriptParams[0]); + return 0; + } + case COMMAND_CLOSE_GARAGE: + { + CollectParameters(&m_nIp, 1); + CGarages::CloseGarage(ScriptParams[0]); + return 0; + } + case COMMAND_WARP_CHAR_FROM_CAR_TO_COORD: + { + CollectParameters(&m_nIp, 4); + CPed *pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + if (pPed->bInVehicle){ + if (pPed->m_pMyVehicle->bIsBus) + pPed->bRenderPedInCar = true; + if (pPed->m_pMyVehicle->pDriver == pPed){ + pPed->m_pMyVehicle->RemoveDriver(); + pPed->m_pMyVehicle->SetStatus(STATUS_ABANDONED); + pPed->m_pMyVehicle->bEngineOn = false; + pPed->m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; + pPed->m_pMyVehicle->SetMoveSpeed(0.0f, 0.0f, -0.00001f); + pPed->m_pMyVehicle->SetTurnSpeed(0.0f, 0.0f, 0.0f); + }else{ + pPed->m_pMyVehicle->RemovePassenger(pPed); + } + if (pPed->m_vehDoor) { + if (pPed->GetPedState() == PED_EXIT_CAR || pPed->GetPedState() == PED_DRAG_FROM_CAR) { + uint8 flags = 0; + if (pPed->m_pMyVehicle->IsBike()) { + if (pPed->m_vehDoor == CAR_DOOR_LF || + pPed->m_vehDoor == CAR_DOOR_RF || + pPed->m_vehDoor == CAR_WINDSCREEN) + flags = CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_RF; + else if (pPed->m_vehDoor == CAR_DOOR_LR || + pPed->m_vehDoor == CAR_DOOR_RR) + flags = CAR_DOOR_FLAG_LR | CAR_DOOR_FLAG_RR; + } + else { + switch (pPed->m_vehDoor) { + case CAR_DOOR_LF: + flags = pPed->m_pMyVehicle->m_nNumMaxPassengers != 0 ? CAR_DOOR_FLAG_LF : CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_LR; + break; + case CAR_DOOR_LR: + flags = pPed->m_pMyVehicle->m_nNumMaxPassengers != 0 ? CAR_DOOR_FLAG_LR : CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_LR; + break; + case CAR_DOOR_RF: + flags = CAR_DOOR_FLAG_RF; + break; + case CAR_DOOR_RR: + flags = CAR_DOOR_FLAG_RR; + break; + } + } + pPed->m_pMyVehicle->m_nGettingOutFlags &= ~flags; + pPed->m_pMyVehicle->ProcessOpenDoor(pPed->m_vehDoor, ANIM_STD_NUM, 0.0f); + } + } + } + pPed->RemoveInCarAnims(); + pPed->bInVehicle = false; + pPed->m_pMyVehicle = nil; + pPed->SetPedState(PED_IDLE); + pPed->m_nLastPedState = PED_NONE; + pPed->bUsesCollision = true; + pPed->SetMoveSpeed(0.0f, 0.0f, 0.0f); + pPed->ReplaceWeaponWhenExitingVehicle(); + if (pPed->m_pVehicleAnim) + pPed->m_pVehicleAnim->blendDelta = -1000.0f; + pPed->m_pVehicleAnim = nil; + pPed->RestartNonPartialAnims(); + pPed->SetMoveState(PEDMOVE_NONE); + CAnimManager::BlendAnimation(pPed->GetClump(), pPed->m_animGroup, ANIM_STD_IDLE, 1000.0f); + pos.z += pPed->GetDistanceFromCentreOfMassToBaseOfModel(); + pPed->Teleport(pos); + CTheScripts::ClearSpaceForMissionEntity(pos, pPed); + return 0; + } + case COMMAND_SET_VISIBILITY_OF_CLOSEST_OBJECT_OF_TYPE: + { + CollectParameters(&m_nIp, 6); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + float range = *(float*)&ScriptParams[3]; + int mi = ScriptParams[4] < 0 ? CTheScripts::UsedObjectArray[-ScriptParams[4]].index : ScriptParams[4]; + int16 total; + CEntity* apEntities[16]; + CWorld::FindObjectsOfTypeInRange(mi, pos, range, true, &total, 16, apEntities, true, false, false, true, true); + if (total == 0) + CWorld::FindObjectsOfTypeInRangeSectorList(mi, CWorld::GetBigBuildingList(LEVEL_GENERIC), pos, range, true, &total, 16, apEntities); + if (total == 0) + CWorld::FindObjectsOfTypeInRangeSectorList(mi, CWorld::GetBigBuildingList(CTheZones::GetLevelFromPosition(&pos)), pos, range, true, &total, 16, apEntities); + CEntity* pClosestEntity = nil; + float min_dist = 2.0f * range; + for (int i = 0; i < total; i++) { + float dist = (apEntities[i]->GetPosition() - pos).Magnitude(); + if (dist < min_dist) { + min_dist = dist; + pClosestEntity = apEntities[i]; + } + } + if (pClosestEntity) { + pClosestEntity->bIsVisible = (ScriptParams[5] != 0); + CTheScripts::AddToInvisibilitySwapArray(pClosestEntity, ScriptParams[5] != 0); + } + return 0; + } + /* + case COMMAND_HAS_CHAR_SPOTTED_CHAR: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); + script_assert(pTarget); + UpdateCompareFlag(pPed->OurPedCanSeeThisOne(pTarget)); + return 0; + } + */ + case COMMAND_SET_CHAR_OBJ_HAIL_TAXI: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_HAIL_TAXI); + return 0; + } + case COMMAND_HAS_OBJECT_BEEN_DAMAGED: + { + CollectParameters(&m_nIp, 1); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + UpdateCompareFlag(pObject->bRenderDamaged || !pObject->bIsVisible); + return 0; + } + /* + case COMMAND_START_KILL_FRENZY_HEADSHOT: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 8); + CDarkel::StartFrenzy((eWeaponType)ScriptParams[0], ScriptParams[1], ScriptParams[2], + ScriptParams[3], text, ScriptParams[4], ScriptParams[5], + ScriptParams[6], ScriptParams[7] != 0, true); + return 0; + } + case COMMAND_ACTIVATE_MILITARY_CRANE: + { + CollectParameters(&m_nIp, 10); + float infX = *(float*)&ScriptParams[2]; + float infY = *(float*)&ScriptParams[3]; + float supX = *(float*)&ScriptParams[4]; + float supY = *(float*)&ScriptParams[5]; + if (infX > supX) { + infX = *(float*)&ScriptParams[4]; + supX = *(float*)&ScriptParams[2]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[5]; + supY = *(float*)&ScriptParams[3]; + } + CCranes::ActivateCrane(infX, supX, infY, supY, + *(float*)&ScriptParams[6], *(float*)&ScriptParams[7], *(float*)&ScriptParams[8], + DEGTORAD(*(float*)&ScriptParams[9]), false, true, + *(float*)&ScriptParams[0], *(float*)&ScriptParams[1]); + return 0; + } + */ + case COMMAND_WARP_PLAYER_INTO_CAR: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + script_assert(pVehicle); + pPed->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, pVehicle); + pPed->WarpPedIntoCar(pVehicle); + return 0; + } + case COMMAND_WARP_CHAR_INTO_CAR: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + script_assert(pVehicle); + pPed->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, pVehicle); + pPed->WarpPedIntoCar(pVehicle); + return 0; + } + //case COMMAND_SWITCH_CAR_RADIO: + //case COMMAND_SET_AUDIO_STREAM: + case COMMAND_PRINT_WITH_2_NUMBERS_BIG: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 4); + CMessages::AddBigMessageWithNumber(text, ScriptParams[2], ScriptParams[3] - 1, ScriptParams[0], ScriptParams[1], -1, -1, -1, -1); + return 0; + } + /* + case COMMAND_PRINT_WITH_3_NUMBERS_BIG: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 5); + CMessages::AddBigMessageWithNumber(text, ScriptParams[3], ScriptParams[4] - 1, ScriptParams[0], ScriptParams[1], ScriptParams[2], -1, -1, -1); + return 0; + } + case COMMAND_PRINT_WITH_4_NUMBERS_BIG: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 6); + CMessages::AddBigMessageWithNumber(text, ScriptParams[4], ScriptParams[5] - 1, ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], -1, -1); + return 0; + } + case COMMAND_PRINT_WITH_5_NUMBERS_BIG: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 7); + CMessages::AddBigMessageWithNumber(text, ScriptParams[5], ScriptParams[6] - 1, ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], ScriptParams[4], -1); + return 0; + } + case COMMAND_PRINT_WITH_6_NUMBERS_BIG: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 8); + CMessages::AddBigMessageWithNumber(text, ScriptParams[6], ScriptParams[7] - 1, ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], ScriptParams[4], ScriptParams[5]); + return 0; + } + */ + case COMMAND_SET_CHAR_WAIT_STATE: + { + CollectParameters(&m_nIp, 3); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->SetWaitState((eWaitState)ScriptParams[1], ScriptParams[2] >= 0 ? &ScriptParams[2] : nil); + return 0; + } + case COMMAND_SET_CAMERA_BEHIND_PLAYER: + TheCamera.SetCameraDirectlyBehindForFollowPed_CamOnAString(); + return 0; + /* + case COMMAND_SET_MOTION_BLUR: + CollectParameters(&m_nIp, 1); + TheCamera.SetMotionBlur(0, 0, 0, 0, ScriptParams[0]); + return 0; + case COMMAND_PRINT_STRING_IN_STRING: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* string = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 2); + CMessages::AddMessageWithString(text, ScriptParams[0], ScriptParams[1], string); + return 0; + } + */ + case COMMAND_CREATE_RANDOM_CHAR: + { + CollectParameters(&m_nIp, 3); + CZoneInfo zoneinfo; + CTheZones::GetZoneInfoForTimeOfDay(&CWorld::Players[CWorld::PlayerInFocus].GetPos(), &zoneinfo); + int mi; + ePedType pedtype = PEDTYPE_COP; + int attempt = 0; + while (pedtype != PEDTYPE_CIVMALE && pedtype != PEDTYPE_CIVFEMALE && attempt < 5) { + mi = CPopulation::ChooseCivilianOccupation(zoneinfo.pedGroup); + if (CModelInfo::GetModelInfo(mi)->GetRwObject()) + pedtype = ((CPedModelInfo*)(CModelInfo::GetModelInfo(mi)))->m_pedType; + attempt++; + } + if (!CModelInfo::GetModelInfo(mi)->GetRwObject()) { + mi = MI_MALE01; + pedtype = ((CPedModelInfo*)(CModelInfo::GetModelInfo(mi)))->m_pedType; + } + CPed* ped = new CCivilianPed(pedtype, mi); + ped->CharCreatedBy = MISSION_CHAR; + ped->bRespondsToThreats = false; + ped->bAllowMedicsToReviveMe = false; + ped->bIsPlayerFriend = false; + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + pos.z += 1.0f; + ped->SetPosition(pos); + ped->SetOrientation(0.0f, 0.0f, 0.0f); + CTheScripts::ClearSpaceForMissionEntity(pos, ped); + if (m_bIsMissionScript) + ped->bIsStaticWaitingForCollision = true; + CWorld::Add(ped); + ped->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pos); + CPopulation::ms_nTotalMissionPeds++; + ScriptParams[0] = CPools::GetPedPool()->GetIndex(ped); + StoreParameters(&m_nIp, 1); + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.AddEntityToList(ScriptParams[0], CLEANUP_CHAR); + return 0; + } + case COMMAND_SET_CHAR_OBJ_STEAL_ANY_CAR: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_STEAL_ANY_CAR); + return 0; + } + /* + case COMMAND_SET_2_REPEATED_PHONE_MESSAGES: + { + CollectParameters(&m_nIp, 1); + wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + gPhoneInfo.SetPhoneMessage_Repeatedly(ScriptParams[0], text1, text2, nil, nil, nil, nil); + return 0; + } + case COMMAND_SET_2_PHONE_MESSAGES: + { + CollectParameters(&m_nIp, 1); + wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + gPhoneInfo.SetPhoneMessage_JustOnce(ScriptParams[0], text1, text2, nil, nil, nil, nil); + return 0; + } + case COMMAND_SET_3_REPEATED_PHONE_MESSAGES: + { + CollectParameters(&m_nIp, 1); + wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text3 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + gPhoneInfo.SetPhoneMessage_Repeatedly(ScriptParams[0], text1, text2, text3, nil, nil, nil); + return 0; + } + case COMMAND_SET_3_PHONE_MESSAGES: + { + CollectParameters(&m_nIp, 1); + wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text3 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + gPhoneInfo.SetPhoneMessage_JustOnce(ScriptParams[0], text1, text2, text3, nil, nil, nil); + return 0; + } + case COMMAND_SET_4_REPEATED_PHONE_MESSAGES: + { + CollectParameters(&m_nIp, 1); + wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text3 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text4 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + gPhoneInfo.SetPhoneMessage_Repeatedly(ScriptParams[0], text1, text2, text3, text4, nil, nil); + return 0; + } + case COMMAND_SET_4_PHONE_MESSAGES: + { + CollectParameters(&m_nIp, 1); + wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text3 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text4 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + gPhoneInfo.SetPhoneMessage_JustOnce(ScriptParams[0], text1, text2, text3, text4, nil, nil); + return 0; + } + */ + case COMMAND_IS_SNIPER_BULLET_IN_AREA: + { + CollectParameters(&m_nIp, 6); + float infX = *(float*)&ScriptParams[0]; + float infY = *(float*)&ScriptParams[1]; + float infZ = *(float*)&ScriptParams[2]; + float supX = *(float*)&ScriptParams[3]; + float supY = *(float*)&ScriptParams[4]; + float supZ = *(float*)&ScriptParams[5]; + if (infX > supX) { + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[0]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[1]; + } + if (infZ > supZ) { + infZ = *(float*)&ScriptParams[5]; + supZ = *(float*)&ScriptParams[2]; + } + UpdateCompareFlag(CBulletInfo::TestForSniperBullet(infX, supX, infY, supY, infZ, supZ)); + return 0; + } + /* + case COMMAND_GIVE_PLAYER_DETONATOR: + CGarages::GivePlayerDetonator(); + return 0; + */ +#ifdef GTA_SCRIPT_COLLECTIVE + case COMMAND_SET_COLL_OBJ_STEAL_ANY_CAR: + CollectParameters(&m_nIp, 1); + CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_STEAL_ANY_CAR); + return 0; +#endif + case COMMAND_SET_OBJECT_VELOCITY: + { + CollectParameters(&m_nIp, 4); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + pObject->SetMoveSpeed(*(CVector*)&ScriptParams[1] * METERS_PER_SECOND_TO_GAME_SPEED); + return 0; + } + case COMMAND_SET_OBJECT_COLLISION: + { + CollectParameters(&m_nIp, 2); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + pObject->bUsesCollision = (ScriptParams[1] != 0); + return 0; + } + case COMMAND_IS_ICECREAM_JINGLE_ON: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + // Adding this check to correspond to command name. + // All original game scripts always assume that the vehicle is actually Mr. Whoopee, + // but maybe there are mods that use it as "is alarm activated"? + script_assert(pVehicle->GetModelIndex() == MI_MRWHOOP); + UpdateCompareFlag(pVehicle->m_bSirenOrAlarm); + return 0; + } + default: + script_assert(0); + } + return -1; +} + +int8 CRunningScript::ProcessCommands900To999(int32 command) +{ + char str[52]; + char onscreen_str[KEY_LENGTH_IN_SCRIPT]; + switch (command) { + case COMMAND_PRINT_STRING_IN_STRING_NOW: + { + wchar* source = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* pstr = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CollectParameters(&m_nIp, 2); + CMessages::AddMessageJumpQWithString(source, ScriptParams[0], ScriptParams[1], pstr); + return 0; + } + //case COMMAND_PRINT_STRING_IN_STRING_SOON: + /* + case COMMAND_SET_5_REPEATED_PHONE_MESSAGES: + { + CollectParameters(&m_nIp, 1); + wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text3 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text4 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text5 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + gPhoneInfo.SetPhoneMessage_Repeatedly(ScriptParams[0], text1, text2, text3, text4, text5, nil); + return 0; + } + case COMMAND_SET_5_PHONE_MESSAGES: + { + CollectParameters(&m_nIp, 1); + wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text3 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text4 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text5 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + gPhoneInfo.SetPhoneMessage_JustOnce(ScriptParams[0], text1, text2, text3, text4, text5, nil); + return 0; + } + case COMMAND_SET_6_REPEATED_PHONE_MESSAGES: + { + CollectParameters(&m_nIp, 1); + wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text3 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text4 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text5 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text6 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + gPhoneInfo.SetPhoneMessage_Repeatedly(ScriptParams[0], text1, text2, text3, text4, text5, text6); + return 0; + } + case COMMAND_SET_6_PHONE_MESSAGES: + { + CollectParameters(&m_nIp, 1); + wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text3 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text4 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text5 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + wchar* text6 = CTheScripts::GetTextByKeyFromScript(&m_nIp); + gPhoneInfo.SetPhoneMessage_JustOnce(ScriptParams[0], text1, text2, text3, text4, text5, text6); + return 0; + } + */ + case COMMAND_IS_POINT_OBSCURED_BY_A_MISSION_ENTITY: + { + CollectParameters(&m_nIp, 6); + float infX = *(float*)&ScriptParams[0] - *(float*)&ScriptParams[3]; + float supX = *(float*)&ScriptParams[0] + *(float*)&ScriptParams[3]; + float infY = *(float*)&ScriptParams[1] - *(float*)&ScriptParams[4]; + float supY = *(float*)&ScriptParams[1] + *(float*)&ScriptParams[4]; + float infZ = *(float*)&ScriptParams[2] - *(float*)&ScriptParams[5]; + float supZ = *(float*)&ScriptParams[2] + *(float*)&ScriptParams[5]; + if (infX > supX) { + float tmp = infX; + infX = supX; + supX = tmp; + } + if (infY > supY) { + float tmp = infY; + infY = supY; + supY = tmp; + } + if (infZ > supZ) { + float tmp = infZ; + infZ = supZ; + supZ = tmp; + } + int16 total; + CWorld::FindMissionEntitiesIntersectingCube(CVector(infX, infY, infZ), CVector(supX, supY, supZ), &total, 2, nil, true, true, true); + UpdateCompareFlag(total > 0); + return 0; + } + case COMMAND_LOAD_ALL_MODELS_NOW: +#ifdef FIX_BUGS + CTimer::Suspend(); +#else + CTimer::Stop(); +#endif + CStreaming::LoadAllRequestedModels(false); +#ifdef FIX_BUGS + CTimer::Resume(); +#else + CTimer::Update(); +#endif + return 0; + case COMMAND_ADD_TO_OBJECT_VELOCITY: + { + CollectParameters(&m_nIp, 4); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + pObject->AddToMoveSpeed(*(CVector*)&ScriptParams[1] * METERS_PER_SECOND_TO_GAME_SPEED); + return 0; + } + case COMMAND_DRAW_SPRITE: + { + CollectParameters(&m_nIp, 9); + CTheScripts::IntroRectangles[CTheScripts::NumberOfIntroRectanglesThisFrame].m_bIsUsed = true; + CTheScripts::IntroRectangles[CTheScripts::NumberOfIntroRectanglesThisFrame].m_nTextureId = ScriptParams[0] - 1; + CTheScripts::IntroRectangles[CTheScripts::NumberOfIntroRectanglesThisFrame].m_sRect = CRect( + *(float*)&ScriptParams[1], *(float*)&ScriptParams[2], *(float*)&ScriptParams[1] + *(float*)&ScriptParams[3], *(float*)&ScriptParams[2] + *(float*)&ScriptParams[4]); + CTheScripts::IntroRectangles[CTheScripts::NumberOfIntroRectanglesThisFrame].m_sColor = CRGBA(ScriptParams[5], ScriptParams[6], ScriptParams[7], ScriptParams[8]); + CTheScripts::NumberOfIntroRectanglesThisFrame++; + return 0; + } + case COMMAND_DRAW_RECT: + { + CollectParameters(&m_nIp, 8); + CTheScripts::IntroRectangles[CTheScripts::NumberOfIntroRectanglesThisFrame].m_bIsUsed = true; + CTheScripts::IntroRectangles[CTheScripts::NumberOfIntroRectanglesThisFrame].m_nTextureId = -1; + CTheScripts::IntroRectangles[CTheScripts::NumberOfIntroRectanglesThisFrame].m_sRect = CRect( + *(float*)&ScriptParams[0], *(float*)&ScriptParams[1], *(float*)&ScriptParams[0] + *(float*)&ScriptParams[2], *(float*)&ScriptParams[1] + *(float*)&ScriptParams[3]); + CTheScripts::IntroRectangles[CTheScripts::NumberOfIntroRectanglesThisFrame].m_sColor = CRGBA(ScriptParams[4], ScriptParams[5], ScriptParams[6], ScriptParams[7]); + CTheScripts::NumberOfIntroRectanglesThisFrame++; + return 0; + } + case COMMAND_LOAD_SPRITE: + { + CollectParameters(&m_nIp, 1); + strncpy(str, (char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); + for (int i = 0; i < KEY_LENGTH_IN_SCRIPT; i++) + str[i] = tolower(str[i]); + m_nIp += KEY_LENGTH_IN_SCRIPT; + int slot = CTxdStore::FindTxdSlot("script"); + CTxdStore::PushCurrentTxd(); + CTxdStore::SetCurrentTxd(slot); + CTheScripts::ScriptSprites[ScriptParams[0] - 1].SetTexture(str); + CTxdStore::PopCurrentTxd(); + return 0; + } + case COMMAND_LOAD_TEXTURE_DICTIONARY: + { + strcpy(str, "models\\"); + strncpy(str + sizeof("models\\"), (char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); + strcat(str, ".txd"); + m_nIp += KEY_LENGTH_IN_SCRIPT; + int slot = CTxdStore::FindTxdSlot("script"); + if (slot == -1) + slot = CTxdStore::AddTxdSlot("script"); + CTxdStore::LoadTxd(slot, str); + CTxdStore::AddRef(slot); + return 0; + } + case COMMAND_REMOVE_TEXTURE_DICTIONARY: + { + CTheScripts::RemoveScriptTextureDictionary(); + return 0; + } + case COMMAND_SET_OBJECT_DYNAMIC: + { + CollectParameters(&m_nIp, 2); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + if (ScriptParams[1]) { + if (pObject->bIsStatic) { + pObject->SetIsStatic(false); + pObject->AddToMovingList(); + } + } + else { + if (!pObject->bIsStatic) { + pObject->SetIsStatic(true); + pObject->RemoveFromMovingList(); + } + } + return 0; + } + /* + case COMMAND_SET_CHAR_ANIM_SPEED: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CAnimBlendAssociation* pAssoc = RpAnimBlendClumpGetFirstAssociation(pPed->GetClump()); + if (pAssoc) + pAssoc->speed = *(float*)&ScriptParams[1]; + return 0; + } + */ + case COMMAND_PLAY_MISSION_PASSED_TUNE: + { + CollectParameters(&m_nIp, 1); + DMAudio.ChangeMusicMode(MUSICMODE_FRONTEND); + DMAudio.PlayFrontEndTrack(ScriptParams[0] + STREAMED_SOUND_MISSION_COMPLETED - 1, FALSE); + return 0; + } + case COMMAND_CLEAR_AREA: + { + CollectParameters(&m_nIp, 5); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CWorld::ClearExcitingStuffFromArea(pos, *(float*)&ScriptParams[3], ScriptParams[4]); + return 0; + } + case COMMAND_FREEZE_ONSCREEN_TIMER: + CollectParameters(&m_nIp, 1); + CUserDisplay::OnscnTimer.m_bDisabled = ScriptParams[0] != 0; + return 0; + case COMMAND_SWITCH_CAR_SIREN: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->m_bSirenOrAlarm = ScriptParams[1] != 0; + return 0; + } + /* + case COMMAND_SWITCH_PED_ROADS_ON_ANGLED: + { + CollectParameters(&m_nIp, 7); + ThePaths.SwitchRoadsInAngledArea(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1], *(float*)&ScriptParams[2], + *(float*)&ScriptParams[3], *(float*)&ScriptParams[4], *(float*)&ScriptParams[5], *(float*)&ScriptParams[6], 0, 1); + return 0; + } + case COMMAND_SWITCH_PED_ROADS_OFF_ANGLED: + CollectParameters(&m_nIp, 7); + ThePaths.SwitchRoadsInAngledArea(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1], *(float*)&ScriptParams[2], + *(float*)&ScriptParams[3], *(float*)&ScriptParams[4], *(float*)&ScriptParams[5], *(float*)&ScriptParams[6], 0, 0); + return 0; + case COMMAND_SWITCH_ROADS_ON_ANGLED: + CollectParameters(&m_nIp, 7); + ThePaths.SwitchRoadsInAngledArea(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1], *(float*)&ScriptParams[2], + *(float*)&ScriptParams[3], *(float*)&ScriptParams[4], *(float*)&ScriptParams[5], *(float*)&ScriptParams[6], 1, 1); + return 0; + case COMMAND_SWITCH_ROADS_OFF_ANGLED: + CollectParameters(&m_nIp, 7); + ThePaths.SwitchRoadsInAngledArea(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1], *(float*)&ScriptParams[2], + *(float*)&ScriptParams[3], *(float*)&ScriptParams[4], *(float*)&ScriptParams[5], *(float*)&ScriptParams[6], 1, 0); + return 0; + */ + case COMMAND_SET_CAR_WATERTIGHT: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + if (pVehicle->IsBike()) { + CBike* pBike = (CBike*)pVehicle; + pBike->bWaterTight = ScriptParams[1] != 0; + } + else if (pVehicle->IsCar()) { + CAutomobile* pCar = (CAutomobile*)pVehicle; + pCar->bWaterTight = ScriptParams[1] != 0; + } + return 0; + } + case COMMAND_ADD_MOVING_PARTICLE_EFFECT: + { + CollectParameters(&m_nIp, 12); + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + float size = Max(0.0f, *(float*)&ScriptParams[7]); + eParticleObjectType type = (eParticleObjectType)ScriptParams[0]; + RwRGBA color; + if (type == POBJECT_SMOKE_TRAIL){ + color.alpha = -1; + color.red = ScriptParams[8]; + color.green = ScriptParams[9]; + color.blue = ScriptParams[10]; + }else{ + color.alpha = color.red = color.blue = color.green = 0; + } + CVector target = *(CVector*)&ScriptParams[4]; + CParticleObject::AddObject(type, pos, target, size, ScriptParams[11], color, 1); + return 0; + } + case COMMAND_SET_CHAR_CANT_BE_DRAGGED_OUT: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bDontDragMeOutCar = ScriptParams[1] != 0; + return 0; + } + case COMMAND_TURN_CAR_TO_FACE_COORD: + { + CollectParameters(&m_nIp, 3); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + const CVector& pos = pVehicle->GetPosition(); + float heading = CGeneral::GetATanOfXY(pos.x - *(float*)&ScriptParams[1], pos.y - *(float*)&ScriptParams[2]) + HALFPI; + if (heading > TWOPI) + heading -= TWOPI; + pVehicle->SetHeading(heading); + return 0; + } + /* + case COMMAND_IS_CRANE_LIFTING_CAR: + { + CollectParameters(&m_nIp, 3); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[2]); + UpdateCompareFlag(CCranes::IsThisCarPickedUp(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1], pVehicle)); + return 0; + } + */ + case COMMAND_DRAW_SPHERE: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + C3dMarkers::PlaceMarkerSet((uintptr)this + m_nIp, MARKERTYPE_CYLINDER, pos, *(float*)&ScriptParams[3], + SPHERE_MARKER_R, SPHERE_MARKER_G, SPHERE_MARKER_B, SPHERE_MARKER_A, + SPHERE_MARKER_PULSE_PERIOD, SPHERE_MARKER_PULSE_FRACTION, 0); + return 0; + } + case COMMAND_SET_CAR_STATUS: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->SetStatus(ScriptParams[1]); + return 0; + } + case COMMAND_IS_CHAR_MALE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(pPed->m_nPedType != PEDTYPE_CIVFEMALE && pPed->m_nPedType != PEDTYPE_PROSTITUTE); + return 0; + } + case COMMAND_SCRIPT_NAME: + { + strncpy(str, (char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); + for (int i = 0; i < KEY_LENGTH_IN_SCRIPT; i++) + str[i] = tolower(str[i]); + m_nIp += KEY_LENGTH_IN_SCRIPT; + strncpy(m_abScriptName, str, KEY_LENGTH_IN_SCRIPT); + return 0; + } + /* + case COMMAND_CHANGE_GARAGE_TYPE_WITH_CAR_MODEL: + { + CollectParameters(&m_nIp, 3); + CGarages::ChangeGarageType(ScriptParams[0], ScriptParams[1], ScriptParams[2]); + return 0; + } + case COMMAND_FIND_DRUG_PLANE_COORDINATES: + *(CVector*)&ScriptParams[0] = CPlane::FindDrugPlaneCoordinates(); + StoreParameters(&m_nIp, 3); + return 0; + */ + case COMMAND_SAVE_INT_TO_DEBUG_FILE: + // TODO: implement something here + CollectParameters(&m_nIp, 1); + return 0; + case COMMAND_SAVE_FLOAT_TO_DEBUG_FILE: + CollectParameters(&m_nIp, 1); + return 0; + case COMMAND_SAVE_NEWLINE_TO_DEBUG_FILE: + return 0; + case COMMAND_POLICE_RADIO_MESSAGE: + CollectParameters(&m_nIp, 3); + DMAudio.PlaySuspectLastSeen(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1], *(float*)&ScriptParams[2]); + return 0; + case COMMAND_SET_CAR_STRONG: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->bTakeLessDamage = ScriptParams[1] != 0; + return 0; + } + case COMMAND_REMOVE_ROUTE: + CollectParameters(&m_nIp, 1); + CRouteNode::RemoveRoute(ScriptParams[0]); + return 0; + case COMMAND_SWITCH_RUBBISH: + CollectParameters(&m_nIp, 1); + CRubbish::SetVisibility(ScriptParams[0] != 0); + return 0; + case COMMAND_REMOVE_PARTICLE_EFFECTS_IN_AREA: + { + CollectParameters(&m_nIp, 6); + float x1 = *(float*)&ScriptParams[0]; + float y1 = *(float*)&ScriptParams[1]; + float z1 = *(float*)&ScriptParams[2]; + float x2 = *(float*)&ScriptParams[3]; + float y2 = *(float*)&ScriptParams[4]; + float z2 = *(float*)&ScriptParams[5]; + CParticleObject* tmp = CParticleObject::pCloseListHead; + while (tmp) { + CParticleObject* next = tmp->m_pNext; + if (tmp->IsWithinArea(x1, y1, z1, x2, y2, z2)) + tmp->RemoveObject(); + tmp = next; + } + tmp = CParticleObject::pFarListHead; + while (tmp) { + CParticleObject* next = tmp->m_pNext; + if (tmp->IsWithinArea(x1, y1, z1, x2, y2, z2)) + tmp->RemoveObject(); + tmp = next; + } + return 0; + } + case COMMAND_SWITCH_STREAMING: + CollectParameters(&m_nIp, 1); + CStreaming::ms_disableStreaming = ScriptParams[0] == 0; + return 0; + case COMMAND_IS_GARAGE_OPEN: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CGarages::IsGarageOpen(ScriptParams[0])); + return 0; + case COMMAND_IS_GARAGE_CLOSED: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CGarages::IsGarageClosed(ScriptParams[0])); + return 0; + /* + case COMMAND_START_CATALINA_HELI: + CHeli::StartCatalinaFlyBy(); + return 0; + case COMMAND_CATALINA_HELI_TAKE_OFF: + CHeli::CatalinaTakeOff(); + return 0; + case COMMAND_REMOVE_CATALINA_HELI: + CHeli::RemoveCatalinaHeli(); + return 0; + case COMMAND_HAS_CATALINA_HELI_BEEN_SHOT_DOWN: + UpdateCompareFlag(CHeli::HasCatalinaBeenShotDown()); + return 0; + */ + case COMMAND_SWAP_NEAREST_BUILDING_MODEL: + { + CollectParameters(&m_nIp, 6); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + float radius = *(float*)&ScriptParams[3]; + int mi1 = ScriptParams[4] >= 0 ? ScriptParams[4] : CTheScripts::UsedObjectArray[-ScriptParams[4]].index; + int mi2 = ScriptParams[5] >= 0 ? ScriptParams[5] : CTheScripts::UsedObjectArray[-ScriptParams[5]].index; + int16 total; + CEntity* apEntities[16]; + CWorld::FindObjectsOfTypeInRange(mi1, pos, radius, true, &total, 16, apEntities, true, false, false, false, false); + if (total == 0) + CWorld::FindObjectsOfTypeInRangeSectorList(mi1, CWorld::GetBigBuildingList(LEVEL_GENERIC), pos, radius, true, &total, 16, apEntities); + if (total == 0) + CWorld::FindObjectsOfTypeInRangeSectorList(mi1, CWorld::GetBigBuildingList(CTheZones::GetLevelFromPosition(&pos)), pos, radius, true, &total, 16, apEntities); + CEntity* pClosestEntity = nil; + float min_dist = 2.0f * radius; + for (int i = 0; i < total; i++) { + float dist = (apEntities[i]->GetPosition() - pos).Magnitude(); + if (dist < min_dist) { + min_dist = dist; + pClosestEntity = apEntities[i]; + } + } + if (!pClosestEntity) { + printf("Failed to find building\n"); + return 0; + } + CBuilding* pReplacedBuilding = ((CBuilding*)pClosestEntity); + pReplacedBuilding->ReplaceWithNewModel(mi2); + CTheScripts::AddToBuildingSwapArray(pReplacedBuilding, mi1, mi2); + return 0; + } + case COMMAND_SWITCH_WORLD_PROCESSING: + CollectParameters(&m_nIp, 1); + CWorld::bProcessCutsceneOnly = ScriptParams[0] == 0; + return 0; + case COMMAND_REMOVE_ALL_PLAYER_WEAPONS: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + pPed->ClearWeapons(); + return 0; + } + /* + case COMMAND_GRAB_CATALINA_HELI: + { + CHeli* pHeli = CHeli::FindPointerToCatalinasHeli(); + ScriptParams[0] = pHeli ? CPools::GetVehiclePool()->GetIndex(pHeli) : -1; + StoreParameters(&m_nIp, 1); + return 0; + } + */ + case COMMAND_CLEAR_AREA_OF_CARS: + { + CollectParameters(&m_nIp, 6); + float infX = *(float*)&ScriptParams[0]; + float infY = *(float*)&ScriptParams[1]; + float infZ = *(float*)&ScriptParams[2]; + float supX = *(float*)&ScriptParams[3]; + float supY = *(float*)&ScriptParams[4]; + float supZ = *(float*)&ScriptParams[5]; + if (infX > supX) { + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[0]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[1]; + } + if (infZ > supZ) { + infZ = *(float*)&ScriptParams[5]; + supZ = *(float*)&ScriptParams[2]; + } + CWorld::ClearCarsFromArea(infX, infY, infZ, supX, supY, supZ); + return 0; + } + case COMMAND_SET_ROTATING_GARAGE_DOOR: + CollectParameters(&m_nIp, 1); + CGarages::SetGarageDoorToRotate(ScriptParams[0]); + return 0; + case COMMAND_ADD_SPHERE: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + float radius = *(float*)&ScriptParams[3]; + CTheScripts::GetActualScriptSphereIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + ScriptParams[0] = CTheScripts::AddScriptSphere((uintptr)this + m_nIp, pos, radius); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_REMOVE_SPHERE: + CollectParameters(&m_nIp, 1); + CTheScripts::RemoveScriptSphere(ScriptParams[0]); + return 0; + /* + case COMMAND_CATALINA_HELI_FLY_AWAY: + CHeli::MakeCatalinaHeliFlyAway(); + return 0; + */ + case COMMAND_SET_EVERYONE_IGNORE_PLAYER: + { + CollectParameters(&m_nIp, 2); + CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + if (ScriptParams[1]) { + pPed->m_pWanted->m_bIgnoredByEveryone = true; + CWorld::StopAllLawEnforcersInTheirTracks(); + } + else { + pPed->m_pWanted->m_bIgnoredByEveryone = false; + } + return 0; + } + case COMMAND_STORE_CAR_CHAR_IS_IN_NO_SAVE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CVehicle* pVehicle = pPed->bInVehicle ? pPed->m_pMyVehicle : nil; + ScriptParams[0] = CPools::GetVehiclePool()->GetIndex(pVehicle); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_STORE_CAR_PLAYER_IS_IN_NO_SAVE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + CVehicle* pVehicle = pPed->bInVehicle ? pPed->m_pMyVehicle : nil; + ScriptParams[0] = CPools::GetVehiclePool()->GetIndex(pVehicle); + StoreParameters(&m_nIp, 1); + return 0; + } + /* + case COMMAND_IS_PHONE_DISPLAYING_MESSAGE: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(gPhoneInfo.IsMessageBeingDisplayed(ScriptParams[0])); + return 0; + */ + case COMMAND_DISPLAY_ONSCREEN_TIMER_WITH_STRING: + { + script_assert(CTheScripts::ScriptSpace[m_nIp++] == ARGUMENT_GLOBALVAR); + uint16 var = CTheScripts::Read2BytesFromScript(&m_nIp); + CollectParameters(&m_nIp, 1); + wchar* text = TheText.Get((char*)&CTheScripts::ScriptSpace[m_nIp]); // ??? + strncpy(onscreen_str, (char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); + m_nIp += KEY_LENGTH_IN_SCRIPT; + CUserDisplay::OnscnTimer.AddClock(var, onscreen_str, ScriptParams[0] != 0); + return 0; + } + case COMMAND_DISPLAY_ONSCREEN_COUNTER_WITH_STRING: + { + script_assert(CTheScripts::ScriptSpace[m_nIp++] == ARGUMENT_GLOBALVAR); + uint16 var = CTheScripts::Read2BytesFromScript(&m_nIp); + CollectParameters(&m_nIp, 1); + wchar* text = TheText.Get((char*)&CTheScripts::ScriptSpace[m_nIp]); // ??? + strncpy(onscreen_str, (char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); + m_nIp += KEY_LENGTH_IN_SCRIPT; + CUserDisplay::OnscnTimer.AddCounter(var, ScriptParams[0], onscreen_str, 0); + return 0; + } + case COMMAND_CREATE_RANDOM_CAR_FOR_CAR_PARK: + { + CollectParameters(&m_nIp, 4); + if (CCarCtrl::NumRandomCars >= 30) + return 0; + int attempts; + int model = -1; + int index = CGeneral::GetRandomNumberInRange(0, MAXVEHICLESLOADED); + for (attempts = 0; attempts < MAXVEHICLESLOADED; attempts++) { + if (model != -1) + break; + model = CStreaming::ms_vehiclesLoaded[index]; + if (model == -1) + continue; + if (CModelInfo::IsCarModel(model) || CModelInfo::IsBikeModel(model)) { + switch (model) { + case MI_LANDSTAL: + case MI_LINERUN: + case MI_RIO: + case MI_FIRETRUCK: + case MI_TRASH: + case MI_STRETCH: + case MI_VOODOO: + case MI_MULE: + case MI_AMBULAN: + case MI_FBICAR: + case MI_MRWHOOP: + case MI_BFINJECT: + case MI_HUNTER: + case MI_POLICE: + case MI_ENFORCER: + case MI_SECURICA: + case MI_PREDATOR: + case MI_BUS: + case MI_RHINO: + case MI_BARRACKS: + case MI_CUBAN: + case MI_CHOPPER: + case MI_ANGEL: + case MI_COACH: + case MI_RCBANDIT: + case MI_ROMERO: + case MI_PACKER: + case MI_SENTXS: + case MI_SQUALO: + case MI_SEASPAR: + case MI_PIZZABOY: + case MI_GANGBUR: + case MI_AIRTRAIN: + case MI_DEADDODO: + case MI_SPEEDER: + case MI_REEFER: + case MI_TROPIC: + case MI_FLATBED: + case MI_YANKEE: + case MI_CADDY: + case MI_ZEBRA: + case MI_TOPFUN: + case MI_SKIMMER: + case MI_RCBARON: + case MI_RCRAIDER: + case MI_SPARROW: + case MI_PATRIOT: + case MI_LOVEFIST: + case MI_COASTG: + case MI_DINGHY: + case MI_HERMES: + case MI_SABRETUR: + case MI_PHEONIX: + case MI_WALTON: + case MI_COMET: + case MI_DELUXO: + case MI_BURRITO: + case MI_SPAND: + case MI_MARQUIS: + case MI_BAGGAGE: + case MI_KAUFMAN: + case MI_MAVERICK: + case MI_VCNMAV: + case MI_RANCHER: + case MI_FBIRANCH: + case MI_JETMAX: + case MI_HOTRING: + case MI_SANDKING: + case MI_BLISTAC: + case MI_POLMAV: + case MI_BOXVILLE: + case MI_BENSON: + case MI_MESA: + case MI_RCGOBLIN: + case MI_HOTRINA: + case MI_HOTRINB: + case MI_BLOODRA: + case MI_BLOODRB: + case MI_VICECHEE: + model = -1; + break; + case MI_IDAHO: + case MI_STINGER: + case MI_PEREN: + case MI_SENTINEL: + case MI_MANANA: + case MI_INFERNUS: + case MI_PONY: + case MI_CHEETAH: + case MI_MOONBEAM: + case MI_ESPERANT: + case MI_TAXI: + case MI_WASHING: + case MI_BOBCAT: + case MI_BANSHEE: + case MI_CABBIE: + case MI_STALLION: + case MI_RUMPO: + case MI_ADMIRAL: + case MI_PCJ600: + case MI_FAGGIO: + case MI_FREEWAY: + case MI_GLENDALE: + case MI_OCEANIC: + case MI_SANCHEZ: + case MI_SABRE: + case MI_REGINA: + case MI_VIRGO: + case MI_GREENWOO: + break; + default: + printf("CREATE_RANDOM_CAR_FOR_CAR_PARK - Unknown car model %d\n", CStreaming::ms_vehiclesLoaded[index]); + model = -1; + break; + } + } + else + model = -1; + if (++index >= 50) + index = 0; + } + if (model == -1) + return 0; + CVehicle* car; + if (CModelInfo::IsBikeModel(model)) { + car = new CBike(model, RANDOM_VEHICLE); + ((CBike*)(car))->bIsStanding = true; + } + else + car = new CAutomobile(model, RANDOM_VEHICLE); + CVector pos = *(CVector*)&ScriptParams[0]; + pos.z += car->GetDistanceFromCentreOfMassToBaseOfModel(); + car->SetPosition(pos); + car->SetHeading(DEGTORAD(*(float*)&ScriptParams[3])); + CTheScripts::ClearSpaceForMissionEntity(pos, car); + car->SetStatus(STATUS_ABANDONED); + car->bIsLocked = false; + car->bIsCarParkVehicle = true; + CCarCtrl::JoinCarWithRoadSystem(car); + car->AutoPilot.m_nCarMission = MISSION_NONE; + car->AutoPilot.m_nTempAction = TEMPACT_NONE; + car->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; + car->AutoPilot.m_nCruiseSpeed = car->AutoPilot.m_fMaxTrafficSpeed = 9.0f; + car->AutoPilot.m_nCurrentLane = car->AutoPilot.m_nNextLane = 0; + car->bEngineOn = false; + car->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pos); + CWorld::Add(car); + return 0; + } + /* + case COMMAND_IS_COLLISION_IN_MEMORY: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CCollision::ms_collisionInMemory == ScriptParams[0]); + return 0; + */ + case COMMAND_SET_WANTED_MULTIPLIER: + CollectParameters(&m_nIp, 1); + FindPlayerPed()->m_pWanted->m_fCrimeSensitivity = *(float*)&ScriptParams[0]; + return 0; + case COMMAND_SET_CAMERA_IN_FRONT_OF_PLAYER: + TheCamera.SetCameraDirectlyInFrontForFollowPed_CamOnAString(); + return 0; + /* + case COMMAND_IS_CAR_VISIBLY_DAMAGED: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + UpdateCompareFlag(pVehicle->bIsDamaged); + return 0; + } + */ + case COMMAND_DOES_OBJECT_EXIST: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CPools::GetObjectPool()->GetAt(ScriptParams[0])); + return 0; + case COMMAND_LOAD_SCENE: + { + CollectParameters(&m_nIp, 3); + CVector pos = *(CVector*)&ScriptParams[0]; +#ifdef FIX_BUGS + CTimer::Suspend(); +#else + CTimer::Stop(); +#endif + CStreaming::LoadScene(pos); +#ifdef FIX_BUGS + CTimer::Suspend(); +#else + CTimer::Update(); +#endif + return 0; + } + case COMMAND_ADD_STUCK_CAR_CHECK: + { + CollectParameters(&m_nIp, 3); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + CTheScripts::StuckCars.AddCarToCheck(ScriptParams[0], *(float*)&ScriptParams[1], ScriptParams[2]); + return 0; + } + case COMMAND_REMOVE_STUCK_CAR_CHECK: + { + CollectParameters(&m_nIp, 1); + CTheScripts::StuckCars.RemoveCarFromCheck(ScriptParams[0]); + return 0; + } + case COMMAND_IS_CAR_STUCK: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CTheScripts::StuckCars.HasCarBeenStuckForAWhile(ScriptParams[0])); + return 0; + case COMMAND_LOAD_MISSION_AUDIO: + { + CollectParameters(&m_nIp, 1); + strncpy(str, (char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); + for (int i = 0; i < KEY_LENGTH_IN_SCRIPT; i++) + str[i] = tolower(str[i]); + m_nIp += KEY_LENGTH_IN_SCRIPT; + DMAudio.PreloadMissionAudio(ScriptParams[0] - 1, str); + return 0; + } + case COMMAND_HAS_MISSION_AUDIO_LOADED: + { + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(DMAudio.GetMissionAudioLoadingStatus(ScriptParams[0] - 1) == 1); + return 0; + } + case COMMAND_PLAY_MISSION_AUDIO: + CollectParameters(&m_nIp, 1); + DMAudio.PlayLoadedMissionAudio(ScriptParams[0] - 1); + return 0; + case COMMAND_HAS_MISSION_AUDIO_FINISHED: + { + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(DMAudio.IsMissionAudioSampleFinished(ScriptParams[0] - 1)); + return 0; + } + case COMMAND_GET_CLOSEST_CAR_NODE_WITH_HEADING: + { + CollectParameters(&m_nIp, 3); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + int node = ThePaths.FindNodeClosestToCoors(pos, 0, 999999.9f, true, true); + *(CVector*)&ScriptParams[0] = ThePaths.FindNodeCoorsForScript(node); + *(float*)&ScriptParams[3] = ThePaths.FindNodeOrientationForCarPlacement(node); + StoreParameters(&m_nIp, 4); + return 0; + } + case COMMAND_HAS_IMPORT_GARAGE_SLOT_BEEN_FILLED: + { + CollectParameters(&m_nIp, 2); + UpdateCompareFlag(CGarages::HasImportExportGarageCollectedThisCar(ScriptParams[0], ScriptParams[1] - 1)); + return 0; + } + case COMMAND_CLEAR_THIS_PRINT: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CMessages::ClearThisPrint(text); + return 0; + } + case COMMAND_CLEAR_THIS_BIG_PRINT: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CMessages::ClearThisBigPrint(text); + return 0; + } + case COMMAND_SET_MISSION_AUDIO_POSITION: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[1]; + DMAudio.SetMissionAudioLocation(ScriptParams[0] - 1, pos.x, pos.y, pos.z); + return 0; + } + case COMMAND_ACTIVATE_SAVE_MENU: + { + CStats::SafeHouseVisits++; + FrontEndMenuManager.m_bActivateSaveMenu = true; + FindPlayerPed()->SetMoveSpeed(0.0f, 0.0f, 0.0f); + FindPlayerPed()->SetTurnSpeed(0.0f, 0.0f, 0.0f); + return 0; + } + case COMMAND_HAS_SAVE_GAME_FINISHED: + UpdateCompareFlag(!FrontEndMenuManager.m_bMenuActive && !FrontEndMenuManager.m_bActivateSaveMenu); + return 0; + case COMMAND_NO_SPECIAL_CAMERA_FOR_THIS_GARAGE: + CollectParameters(&m_nIp, 1); + CGarages::SetLeaveCameraForThisGarage(ScriptParams[0]); + return 0; + /* + case COMMAND_ADD_BLIP_FOR_PICKUP_OLD: + { + CollectParameters(&m_nIp, 3); + CObject* pObject = CPickups::aPickUps[CPickups::GetActualPickupIndex(ScriptParams[0])].m_pObject; + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + ScriptParams[0] = CRadar::SetEntityBlip(BLIP_OBJECT, CPools::GetObjectPool()->GetIndex(pObject), ScriptParams[1], (eBlipDisplay)ScriptParams[2]); + StoreParameters(&m_nIp, 1); + return 0; + } + */ + case COMMAND_ADD_BLIP_FOR_PICKUP: + { + CollectParameters(&m_nIp, 1); + CObject* pObject = CPickups::aPickUps[CPickups::GetActualPickupIndex(ScriptParams[0])].m_pObject; + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + int handle = CRadar::SetEntityBlip(BLIP_OBJECT, CPools::GetObjectPool()->GetIndex(pObject), 6, BLIP_DISPLAY_BOTH); + CRadar::ChangeBlipScale(handle, 3); + ScriptParams[0] = handle; + StoreParameters(&m_nIp, 1); + return 0; + } + /* + case COMMAND_ADD_SPRITE_BLIP_FOR_PICKUP: + { + CollectParameters(&m_nIp, 2); + CObject* pObject = CPickups::aPickUps[CPickups::GetActualPickupIndex(ScriptParams[0])].m_pObject; + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + int handle = CRadar::SetEntityBlip(BLIP_OBJECT, CPools::GetObjectPool()->GetIndex(pObject), 6, BLIP_DISPLAY_BOTH); + CRadar::SetBlipSprite(handle, ScriptParams[1]); + ScriptParams[0] = handle; + StoreParameters(&m_nIp, 1); + return 0; + } + */ + case COMMAND_SET_PED_DENSITY_MULTIPLIER: + CollectParameters(&m_nIp, 1); + CPopulation::PedDensityMultiplier = *(float*)&ScriptParams[0]; + return 0; + case COMMAND_FORCE_RANDOM_PED_TYPE: + CollectParameters(&m_nIp, 1); + CPopulation::m_AllRandomPedsThisType = ScriptParams[0]; + return 0; + /* + case COMMAND_SET_TEXT_DRAW_BEFORE_FADE: + CollectParameters(&m_nIp, 1); + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_bTextBeforeFade = ScriptParams[0] != 0; + return 0; + */ + case COMMAND_GET_COLLECTABLE1S_COLLECTED: + ScriptParams[0] = CWorld::Players[CWorld::PlayerInFocus].m_nCollectedPackages; + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_SET_CHAR_OBJ_LEAVE_ANY_CAR: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_LEAVE_CAR, pPed->m_pMyVehicle); + return 0; + } + case COMMAND_SET_SPRITES_DRAW_BEFORE_FADE: + CollectParameters(&m_nIp, 1); + CTheScripts::IntroRectangles[CTheScripts::NumberOfIntroRectanglesThisFrame].m_bBeforeFade = ScriptParams[0] != 0; + return 0; + case COMMAND_SET_TEXT_RIGHT_JUSTIFY: + CollectParameters(&m_nIp, 1); + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_bRightJustify = ScriptParams[0] != 0; + return 0; + case COMMAND_PRINT_HELP: + { + if (CCamera::m_bUseMouse3rdPerson && ( + strcmp((char*)&CTheScripts::ScriptSpace[m_nIp], "HELP15") == 0 || + strcmp((char*)&CTheScripts::ScriptSpace[m_nIp], "GUN_2A") == 0 || + strcmp((char*)&CTheScripts::ScriptSpace[m_nIp], "GUN_2C") == 0 || + strcmp((char*)&CTheScripts::ScriptSpace[m_nIp], "GUN_2D") == 0)) { + m_nIp += KEY_LENGTH_IN_SCRIPT; + return 0; + } + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CHud::SetHelpMessage(text, false); + return 0; + } + case COMMAND_CLEAR_HELP: + CHud::SetHelpMessage(nil, false); + return 0; + case COMMAND_FLASH_HUD_OBJECT: + CollectParameters(&m_nIp, 1); + CHud::m_ItemToFlash = ScriptParams[0]; + return 0; + default: + script_assert(0); + } + return -1; +} + +int32 CTheScripts::GetNewUniqueScriptSphereIndex(int32 index) +{ + if (ScriptSphereArray[index].m_Index >= UINT16_MAX - 1) + ScriptSphereArray[index].m_Index = 1; + else + ScriptSphereArray[index].m_Index++; + return (uint16)index | ScriptSphereArray[index].m_Index << 16; +} + +int32 CTheScripts::GetActualScriptSphereIndex(int32 index) +{ + if (index == -1) + return -1; + uint16 check = (uint32)index >> 16; + uint16 array_idx = index & (0xFFFF); + script_assert(array_idx < ARRAY_SIZE(ScriptSphereArray)); + if (check != ScriptSphereArray[array_idx].m_Index) + return -1; + return array_idx; +} + +void CTheScripts::DrawScriptSpheres() +{ + for (int i = 0; i < MAX_NUM_SCRIPT_SPHERES; i++) { + if (ScriptSphereArray[i].m_bInUse) + C3dMarkers::PlaceMarkerSet(ScriptSphereArray[i].m_Id, MARKERTYPE_CYLINDER, ScriptSphereArray[i].m_vecCenter, ScriptSphereArray[i].m_fRadius, + SPHERE_MARKER_R, SPHERE_MARKER_G, SPHERE_MARKER_B, SPHERE_MARKER_A, SPHERE_MARKER_PULSE_PERIOD, SPHERE_MARKER_PULSE_FRACTION, 0); + } +} + +int32 CTheScripts::AddScriptSphere(int32 id, CVector pos, float radius) +{ + int16 i = 0; + for (i = 0; i < MAX_NUM_SCRIPT_SPHERES; i++) { + if (!ScriptSphereArray[i].m_bInUse) + break; + } +#ifdef FIX_BUGS + if (i == MAX_NUM_SCRIPT_SPHERES) + return -1; +#endif + ScriptSphereArray[i].m_bInUse = true; + ScriptSphereArray[i].m_Id = id; + ScriptSphereArray[i].m_vecCenter = pos; + ScriptSphereArray[i].m_fRadius = radius; + return GetNewUniqueScriptSphereIndex(i); +} + +void CTheScripts::RemoveScriptSphere(int32 index) +{ + index = GetActualScriptSphereIndex(index); + if (index == -1) + return; + ScriptSphereArray[index].m_bInUse = false; + ScriptSphereArray[index].m_Id = 0; +} + +void CTheScripts::AddToBuildingSwapArray(CBuilding* pBuilding, int32 old_model, int32 new_model) +{ + int i = 0; + bool found = false; + while (i < MAX_NUM_BUILDING_SWAPS && !found) { + if (BuildingSwapArray[i].m_pBuilding == pBuilding) + found = true; + else + i++; + } + if (found) { + if (BuildingSwapArray[i].m_nOldModel == new_model) { + BuildingSwapArray[i].m_pBuilding = nil; + BuildingSwapArray[i].m_nOldModel = BuildingSwapArray[i].m_nNewModel = -1; + } + else { + BuildingSwapArray[i].m_nNewModel = new_model; + } + } + else { + i = 0; + while (i < MAX_NUM_BUILDING_SWAPS && !found) { + if (BuildingSwapArray[i].m_pBuilding == nil) + found = true; + else + i++; + } + if (found) { + BuildingSwapArray[i].m_pBuilding = pBuilding; + BuildingSwapArray[i].m_nNewModel = new_model; + BuildingSwapArray[i].m_nOldModel = old_model; + } + } +} + +void CTheScripts::AddToInvisibilitySwapArray(CEntity* pEntity, bool remove) +{ + int i = 0; + bool found = false; + while (i < MAX_NUM_INVISIBILITY_SETTINGS && !found) { + if (InvisibilitySettingArray[i] == pEntity) + found = true; + else + i++; + } + if (found) { + if (remove) + InvisibilitySettingArray[i] = nil; + } + else if (!remove) { + i = 0; + while (i < MAX_NUM_INVISIBILITY_SETTINGS && !found) { + if (InvisibilitySettingArray[i] == nil) + found = true; + else + i++; + } + if (found) + InvisibilitySettingArray[i] = pEntity; + } +} + +void CTheScripts::UndoBuildingSwaps() +{ + for (int i = 0; i < MAX_NUM_BUILDING_SWAPS; i++) { + if (BuildingSwapArray[i].m_pBuilding) { + BuildingSwapArray[i].m_pBuilding->ReplaceWithNewModel(BuildingSwapArray[i].m_nOldModel); + BuildingSwapArray[i].m_pBuilding = nil; + BuildingSwapArray[i].m_nOldModel = BuildingSwapArray[i].m_nNewModel = -1; + } + } +} + +void CTheScripts::UndoEntityInvisibilitySettings() +{ + for (int i = 0; i < MAX_NUM_INVISIBILITY_SETTINGS; i++) { + if (InvisibilitySettingArray[i]) { + InvisibilitySettingArray[i]->bIsVisible = true; + InvisibilitySettingArray[i] = nil; + } + } +} diff --git a/src/miami/control/Script5.cpp b/src/miami/control/Script5.cpp new file mode 100644 index 00000000..e9f0967e --- /dev/null +++ b/src/miami/control/Script5.cpp @@ -0,0 +1,2810 @@ +#include "common.h" + +#include "Script.h" +#include "ScriptCommands.h" + +#include "CarCtrl.h" +#include "BulletInfo.h" +#include "General.h" +#include "Lines.h" +#include "Messages.h" +#include "Pad.h" +#include "Pools.h" +#include "Population.h" +#include "RpAnimBlend.h" +#include "SaveBuf.h" +#include "Shadows.h" +#include "SpecialFX.h" +#include "World.h" +#include "main.h" +#include "SaveBuf.h" + +void CRunningScript::UpdateCompareFlag(bool flag) +{ + if (m_bNotFlag) + flag = !flag; + if (m_nAndOrState == ANDOR_NONE) { + m_bCondResult = flag; + return; + } + if (m_nAndOrState >= ANDS_1 && m_nAndOrState <= ANDS_8) { + m_bCondResult &= flag; + if (m_nAndOrState == ANDS_1) { + m_nAndOrState = ANDOR_NONE; + return; + } + } + else if (m_nAndOrState >= ORS_1 && m_nAndOrState <= ORS_8) { + m_bCondResult |= flag; + if (m_nAndOrState == ORS_1) { + m_nAndOrState = ANDOR_NONE; + return; + } + } + else { + return; + } + m_nAndOrState--; +} + +void CRunningScript::LocatePlayerCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug, decided = false; + float X, Y, Z, dX, dY, dZ; + switch (command) { + case COMMAND_LOCATE_PLAYER_ANY_MEANS_3D: + case COMMAND_LOCATE_PLAYER_ON_FOOT_3D: + case COMMAND_LOCATE_PLAYER_IN_CAR_3D: + case COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_3D: + case COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_3D: + case COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_3D: + b3D = true; + break; + default: + b3D = false; + break; + } + CollectParameters(pIp, b3D ? 8 : 6); + CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; + switch (command) { + case COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_2D: + case COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_3D: + case COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_2D: + case COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_3D: + case COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_2D: + case COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_3D: + if (!CTheScripts::IsPlayerStopped(pPlayerInfo)) { + result = false; + decided = true; + } + break; + default: + break; + } + X = *(float*)&ScriptParams[1]; + Y = *(float*)&ScriptParams[2]; + if (b3D) { + Z = *(float*)&ScriptParams[3]; + dX = *(float*)&ScriptParams[4]; + dY = *(float*)&ScriptParams[5]; + dZ = *(float*)&ScriptParams[6]; + debug = ScriptParams[7]; + } else { + dX = *(float*)&ScriptParams[3]; + dY = *(float*)&ScriptParams[4]; + debug = ScriptParams[5]; + } + if (!decided) { + CVector pos = pPlayerInfo->GetPos(); + result = false; + bool in_area; + if (b3D) { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y && + Z - dZ <= pos.z && + Z + dZ >= pos.z; + } else { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y; + } + if (in_area) { + switch (command) { + case COMMAND_LOCATE_PLAYER_ANY_MEANS_2D: + case COMMAND_LOCATE_PLAYER_ANY_MEANS_3D: + case COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_2D: + case COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_3D: + result = true; + break; + case COMMAND_LOCATE_PLAYER_ON_FOOT_2D: + case COMMAND_LOCATE_PLAYER_ON_FOOT_3D: + case COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_2D: + case COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_3D: + result = !pPlayerInfo->m_pPed->bInVehicle; + break; + case COMMAND_LOCATE_PLAYER_IN_CAR_2D: + case COMMAND_LOCATE_PLAYER_IN_CAR_3D: + case COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_2D: + case COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_3D: + result = pPlayerInfo->m_pPed->bInVehicle; + break; + default: + script_assert(false); + break; + } + } + } + UpdateCompareFlag(result); + if (debug) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); + else + CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); + } +} + +void CRunningScript::LocatePlayerCharCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug; + float X, Y, Z, dX, dY, dZ; + switch (command) { + case COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_3D: + case COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_3D: + case COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_3D: + b3D = true; + break; + default: + b3D = false; + break; + } + CollectParameters(pIp, b3D ? 6 : 5); + CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; + CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); + script_assert(pTarget); + CVector pos = pPlayerInfo->GetPos(); + if (pTarget->bInVehicle) { + X = pTarget->m_pMyVehicle->GetPosition().x; + Y = pTarget->m_pMyVehicle->GetPosition().y; + Z = pTarget->m_pMyVehicle->GetPosition().z; + } else { + X = pTarget->GetPosition().x; + Y = pTarget->GetPosition().y; + Z = pTarget->GetPosition().z; + } + dX = *(float*)&ScriptParams[2]; + dY = *(float*)&ScriptParams[3]; + if (b3D) { + dZ = *(float*)&ScriptParams[4]; + debug = ScriptParams[5]; + } + else { + debug = ScriptParams[4]; + } + result = false; + bool in_area; + if (b3D) { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y && + Z - dZ <= pos.z && + Z + dZ >= pos.z; + } + else { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y; + } + if (in_area) { + switch (command) { + case COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_2D: + case COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_3D: + result = true; + break; + case COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_2D: + case COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_3D: + result = !pPlayerInfo->m_pPed->bInVehicle; + break; + case COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_2D: + case COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_3D: + result = pPlayerInfo->m_pPed->bInVehicle; + break; + default: + script_assert(false); + break; + } + } + UpdateCompareFlag(result); + if (debug) +#ifdef FIX_BUGS + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); +#else + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dX, b3D ? Z : MAP_Z_LOW_LIMIT); +#endif + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); + else + CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); + } +} + +void CRunningScript::LocatePlayerCarCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug; + float X, Y, Z, dX, dY, dZ; + switch (command) { + case COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_3D: + case COMMAND_LOCATE_PLAYER_ON_FOOT_CAR_3D: + case COMMAND_LOCATE_PLAYER_IN_CAR_CAR_3D: + b3D = true; + break; + default: + b3D = false; + break; + } + CollectParameters(pIp, b3D ? 6 : 5); + CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; + CVehicle* pTarget = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + script_assert(pTarget); + CVector pos = pPlayerInfo->GetPos(); + X = pTarget->GetPosition().x; + Y = pTarget->GetPosition().y; + Z = pTarget->GetPosition().z; + dX = *(float*)&ScriptParams[2]; + dY = *(float*)&ScriptParams[3]; + if (b3D) { + dZ = *(float*)&ScriptParams[4]; + debug = ScriptParams[5]; + } + else { + debug = ScriptParams[4]; + } + result = false; + bool in_area; + if (b3D) { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y && + Z - dZ <= pos.z && + Z + dZ >= pos.z; + } + else { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y; + } + if (in_area) { + switch (command) { + case COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_2D: + case COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_3D: + result = true; + break; + case COMMAND_LOCATE_PLAYER_ON_FOOT_CAR_2D: + case COMMAND_LOCATE_PLAYER_ON_FOOT_CAR_3D: + result = !pPlayerInfo->m_pPed->bInVehicle; + break; + case COMMAND_LOCATE_PLAYER_IN_CAR_CAR_2D: + case COMMAND_LOCATE_PLAYER_IN_CAR_CAR_3D: + result = pPlayerInfo->m_pPed->bInVehicle; + break; + default: + script_assert(false); + break; + } + } + UpdateCompareFlag(result); + if (debug) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); + else + CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); + } +} + +void CRunningScript::LocateCharCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug, decided = false; + float X, Y, Z, dX, dY, dZ; + switch (command) { + case COMMAND_LOCATE_CHAR_ANY_MEANS_3D: + case COMMAND_LOCATE_CHAR_ON_FOOT_3D: + case COMMAND_LOCATE_CHAR_IN_CAR_3D: + case COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_3D: + case COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_3D: + case COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_3D: + b3D = true; + break; + default: + b3D = false; + break; + } + CollectParameters(pIp, b3D ? 8 : 6); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CVector pos = pPed->InVehicle() ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); + switch (command) { + case COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_2D: + case COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_3D: + case COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_2D: + case COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_3D: + case COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_2D: + case COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_3D: + if (!CTheScripts::IsPedStopped(pPed)) { + result = false; + decided = true; + } + break; + default: + break; + } + X = *(float*)&ScriptParams[1]; + Y = *(float*)&ScriptParams[2]; + if (b3D) { + Z = *(float*)&ScriptParams[3]; + dX = *(float*)&ScriptParams[4]; + dY = *(float*)&ScriptParams[5]; + dZ = *(float*)&ScriptParams[6]; + debug = ScriptParams[7]; + } + else { + dX = *(float*)&ScriptParams[3]; + dY = *(float*)&ScriptParams[4]; + debug = ScriptParams[5]; + } + if (!decided) { + result = false; + bool in_area; + if (b3D) { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y && + Z - dZ <= pos.z && + Z + dZ >= pos.z; + } + else { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y; + } + if (in_area) { + switch (command) { + case COMMAND_LOCATE_CHAR_ANY_MEANS_2D: + case COMMAND_LOCATE_CHAR_ANY_MEANS_3D: + case COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_2D: + case COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_3D: + result = true; + break; + case COMMAND_LOCATE_CHAR_ON_FOOT_2D: + case COMMAND_LOCATE_CHAR_ON_FOOT_3D: + case COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_2D: + case COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_3D: + result = !pPed->bInVehicle; + break; + case COMMAND_LOCATE_CHAR_IN_CAR_2D: + case COMMAND_LOCATE_CHAR_IN_CAR_3D: + case COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_2D: + case COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_3D: + result = pPed->bInVehicle; + break; + default: + script_assert(false); + break; + } + } + } + UpdateCompareFlag(result); + if (debug) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); + else + CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); + } +} + +void CRunningScript::LocateCharCharCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug; + float X, Y, Z, dX, dY, dZ; + switch (command) { + case COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_3D: + case COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_3D: + case COMMAND_LOCATE_CHAR_IN_CAR_CHAR_3D: + b3D = true; + break; + default: + b3D = false; + break; + } + CollectParameters(pIp, b3D ? 6 : 5); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); + script_assert(pTarget); + CVector pos = pPed->bInVehicle ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); + if (pTarget->bInVehicle) { + X = pTarget->m_pMyVehicle->GetPosition().x; + Y = pTarget->m_pMyVehicle->GetPosition().y; + Z = pTarget->m_pMyVehicle->GetPosition().z; + } + else { + X = pTarget->GetPosition().x; + Y = pTarget->GetPosition().y; + Z = pTarget->GetPosition().z; + } + dX = *(float*)&ScriptParams[2]; + dY = *(float*)&ScriptParams[3]; + if (b3D) { + dZ = *(float*)&ScriptParams[4]; + debug = ScriptParams[5]; + } + else { + debug = ScriptParams[4]; + } + result = false; + bool in_area; + if (b3D) { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y && + Z - dZ <= pos.z && + Z + dZ >= pos.z; + } + else { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y; + } + if (in_area) { + switch (command) { + case COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_2D: + case COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_3D: + result = true; + break; + case COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_2D: + case COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_3D: + result = !pPed->bInVehicle; + break; + case COMMAND_LOCATE_CHAR_IN_CAR_CHAR_2D: + case COMMAND_LOCATE_CHAR_IN_CAR_CHAR_3D: + result = pPed->bInVehicle; + break; + default: + script_assert(false); + break; + } + } + UpdateCompareFlag(result); + if (debug) +#ifdef FIX_BUGS + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); +#else + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dX, b3D ? Z : MAP_Z_LOW_LIMIT); +#endif + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); + else + CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); + } +} + +void CRunningScript::LocateCharCarCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug; + float X, Y, Z, dX, dY, dZ; + switch (command) { + case COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_3D: + case COMMAND_LOCATE_CHAR_ON_FOOT_CAR_3D: + case COMMAND_LOCATE_CHAR_IN_CAR_CAR_3D: + b3D = true; + break; + default: + b3D = false; + break; + } + CollectParameters(pIp, b3D ? 6 : 5); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CVehicle* pTarget = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + script_assert(pTarget); + CVector pos = pPed->bInVehicle ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); + X = pTarget->GetPosition().x; + Y = pTarget->GetPosition().y; + Z = pTarget->GetPosition().z; + dX = *(float*)&ScriptParams[2]; + dY = *(float*)&ScriptParams[3]; + if (b3D) { + dZ = *(float*)&ScriptParams[4]; + debug = ScriptParams[5]; + } + else { + debug = ScriptParams[4]; + } + result = false; + bool in_area; + if (b3D) { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y && + Z - dZ <= pos.z && + Z + dZ >= pos.z; + } + else { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y; + } + if (in_area) { + switch (command) { + case COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_2D: + case COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_3D: + result = true; + break; + case COMMAND_LOCATE_CHAR_ON_FOOT_CAR_2D: + case COMMAND_LOCATE_CHAR_ON_FOOT_CAR_3D: + result = !pPed->bInVehicle; + break; + case COMMAND_LOCATE_CHAR_IN_CAR_CAR_2D: + case COMMAND_LOCATE_CHAR_IN_CAR_CAR_3D: + result = pPed->bInVehicle; + break; + default: + script_assert(false); + break; + } + } + UpdateCompareFlag(result); + if (debug) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); + else + CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); + } +} + +void CRunningScript::LocateCharObjectCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug; + float X, Y, Z, dX, dY, dZ; + switch (command) { + case COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_3D: + case COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_3D: + case COMMAND_LOCATE_CHAR_IN_CAR_OBJECT_3D: + b3D = true; + break; + default: + b3D = false; + break; + } + CollectParameters(pIp, b3D ? 6 : 5); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CObject* pTarget = CPools::GetObjectPool()->GetAt(ScriptParams[1]); + script_assert(pTarget); + CVector pos = pPed->bInVehicle ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); + X = pTarget->GetPosition().x; + Y = pTarget->GetPosition().y; + Z = pTarget->GetPosition().z; + dX = *(float*)&ScriptParams[2]; + dY = *(float*)&ScriptParams[3]; + if (b3D) { + dZ = *(float*)&ScriptParams[4]; + debug = ScriptParams[5]; + } + else { + debug = ScriptParams[4]; + } + result = false; + bool in_area; + if (b3D) { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y && + Z - dZ <= pos.z && + Z + dZ >= pos.z; + } + else { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y; + } + if (in_area) { + switch (command) { + case COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_2D: + case COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_3D: + result = true; + break; + case COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_2D: + case COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_3D: + result = !pPed->bInVehicle; + break; + case COMMAND_LOCATE_CHAR_IN_CAR_OBJECT_2D: + case COMMAND_LOCATE_CHAR_IN_CAR_OBJECT_3D: + result = pPed->bInVehicle; + break; + default: + script_assert(false); + break; + } + } + UpdateCompareFlag(result); + if (debug) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); + else + CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); + } +} + +void CRunningScript::LocateCarCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug, decided = false; + float X, Y, Z, dX, dY, dZ; + switch (command) { + case COMMAND_LOCATE_CAR_3D: + case COMMAND_LOCATE_STOPPED_CAR_3D: + b3D = true; + break; + default: + b3D = false; + break; + } + CollectParameters(pIp, b3D ? 8 : 6); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + CVector pos = pVehicle->GetPosition(); + switch (command) { + case COMMAND_LOCATE_STOPPED_CAR_2D: + case COMMAND_LOCATE_STOPPED_CAR_3D: + if (!CTheScripts::IsVehicleStopped(pVehicle)) { + result = false; + decided = true; + } + break; + default: + break; + } + X = *(float*)&ScriptParams[1]; + Y = *(float*)&ScriptParams[2]; + if (b3D) { + Z = *(float*)&ScriptParams[3]; + dX = *(float*)&ScriptParams[4]; + dY = *(float*)&ScriptParams[5]; + dZ = *(float*)&ScriptParams[6]; + debug = ScriptParams[7]; + } + else { + dX = *(float*)&ScriptParams[3]; + dY = *(float*)&ScriptParams[4]; + debug = ScriptParams[5]; + } + if (!decided) { + result = false; + bool in_area; + if (b3D) { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y && + Z - dZ <= pos.z && + Z + dZ >= pos.z; + } + else { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y; + } + result = in_area; + } + UpdateCompareFlag(result); + if (debug) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); + else + CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); + } +} + +void CRunningScript::LocateObjectCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug; + float X, Y, Z, dX, dY, dZ; + switch (command) { + case COMMAND_LOCATE_OBJECT_3D: + b3D = true; + break; + default: + b3D = false; + break; + } + CollectParameters(pIp, b3D ? 8 : 6); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + CVector pos = pObject->GetPosition(); + X = *(float*)&ScriptParams[1]; + Y = *(float*)&ScriptParams[2]; + if (b3D) { + Z = *(float*)&ScriptParams[3]; + dX = *(float*)&ScriptParams[4]; + dY = *(float*)&ScriptParams[5]; + dZ = *(float*)&ScriptParams[6]; + debug = ScriptParams[7]; + } + else { + dX = *(float*)&ScriptParams[3]; + dY = *(float*)&ScriptParams[4]; + debug = ScriptParams[5]; + } + result = false; + bool in_area; + if (b3D) { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y && + Z - dZ <= pos.z && + Z + dZ >= pos.z; + } + else { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y; + } + result = in_area; + UpdateCompareFlag(result); + if (debug) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); + else + CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); + } +} + +void CRunningScript::LocateSniperBulletCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug; + float X, Y, Z, dX, dY, dZ; + switch (command) { + case COMMAND_LOCATE_SNIPER_BULLET_3D: + b3D = true; + break; + default: + b3D = false; + break; + } + CollectParameters(pIp, b3D ? 7 : 5); + X = *(float*)&ScriptParams[0]; + Y = *(float*)&ScriptParams[1]; + if (b3D) { + Z = *(float*)&ScriptParams[2]; + dX = *(float*)&ScriptParams[3]; + dY = *(float*)&ScriptParams[4]; + dZ = *(float*)&ScriptParams[5]; + debug = ScriptParams[6]; + } + else { + dX = *(float*)&ScriptParams[2]; + dY = *(float*)&ScriptParams[3]; + debug = ScriptParams[4]; + } + result = CBulletInfo::TestForSniperBullet(X - dX, X + dX, Y - dY, Y + dY, b3D ? Z - dZ : -1000.0f, b3D ? Z + dZ : 1000.0f); + UpdateCompareFlag(result); + if (debug) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); + else + CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); + } +} + +void CRunningScript::PlayerInAreaCheckCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug, decided = false; + float infX, infY, infZ, supX, supY, supZ; + switch (command) { + case COMMAND_IS_PLAYER_IN_AREA_3D: + case COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_3D: + case COMMAND_IS_PLAYER_IN_AREA_IN_CAR_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_3D: + b3D = true; + break; + default: + b3D = false; + break; + } + CollectParameters(pIp, b3D ? 8 : 6); + CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; + switch (command) { + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_2D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_2D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_2D: + if (!CTheScripts::IsPlayerStopped(pPlayerInfo)) { + result = false; + decided = true; + } + break; + default: + break; + } + infX = *(float*)&ScriptParams[1]; + infY = *(float*)&ScriptParams[2]; + if (b3D) { + infZ = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[5]; + supZ = *(float*)&ScriptParams[6]; + if (infZ > supZ) { + infZ = *(float*)&ScriptParams[6]; + supZ = *(float*)&ScriptParams[3]; + } + debug = ScriptParams[7]; + } + else { + supX = *(float*)&ScriptParams[3]; + supY = *(float*)&ScriptParams[4]; + debug = ScriptParams[5]; + } + if (infX > supX) { + float tmp = infX; + infX = supX; + supX = tmp; + } + if (infY > supY) { + float tmp = infY; + infY = supY; + supY = tmp; + } + if (!decided) { + CVector pos = pPlayerInfo->GetPos(); + result = false; + bool in_area; + if (b3D) { + in_area = infX <= pos.x && + supX >= pos.x && + infY <= pos.y && + supY >= pos.y && + infZ <= pos.z && + supZ >= pos.z; + } + else { + in_area = infX <= pos.x && + supX >= pos.x && + infY <= pos.y && + supY >= pos.y; + } + if (in_area) { + switch (command) { + case COMMAND_IS_PLAYER_IN_AREA_2D: + case COMMAND_IS_PLAYER_IN_AREA_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_2D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_3D: + result = true; + break; + case COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_2D: + case COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_2D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_3D: + result = !pPlayerInfo->m_pPed->bInVehicle; + break; + case COMMAND_IS_PLAYER_IN_AREA_IN_CAR_2D: + case COMMAND_IS_PLAYER_IN_AREA_IN_CAR_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_2D: + case COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_3D: + result = pPlayerInfo->m_pPed->bInVehicle; + break; + default: + script_assert(false); + break; + } + } + } + UpdateCompareFlag(result); + if (debug) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, infX, infY, supX, supY, b3D ? (infZ + supZ) / 2 : MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugCube(infX, infY, infZ, supX, supY, supZ); + else + CTheScripts::DrawDebugSquare(infX, infY, supX, supY); + } +} + +void CRunningScript::PlayerInAngledAreaCheckCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug, decided = false; + float infX, infY, infZ, supX, supY, supZ, side2length; + switch (command) { + case COMMAND_IS_PLAYER_IN_ANGLED_AREA_3D: + case COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_3D: + case COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_3D: + b3D = true; + break; + default: + b3D = false; + break; + } + CollectParameters(pIp, b3D ? 9 : 7); + CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; + switch (command) { + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_2D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_2D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_2D: + if (!CTheScripts::IsPlayerStopped(pPlayerInfo)) { + result = false; + decided = true; + } + break; + default: + break; + } + infX = *(float*)&ScriptParams[1]; + infY = *(float*)&ScriptParams[2]; + if (b3D) { + infZ = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[5]; + supZ = *(float*)&ScriptParams[6]; + if (infZ > supZ) { + infZ = *(float*)&ScriptParams[6]; + supZ = *(float*)&ScriptParams[3]; + } + side2length = *(float*)&ScriptParams[7]; + debug = ScriptParams[8]; + } + else { + supX = *(float*)&ScriptParams[3]; + supY = *(float*)&ScriptParams[4]; + side2length = *(float*)&ScriptParams[5]; + debug = ScriptParams[6]; + } + float initAngle = CGeneral::GetRadianAngleBetweenPoints(infX, infY, supX, supY) + HALFPI; + while (initAngle < 0.0f) + initAngle += TWOPI; + while (initAngle > TWOPI) + initAngle -= TWOPI; + // it looks like the idea is to use a rectangle using the diagonal of the rectangle as + // the side of new rectangle, with "length" being the length of second side + float rotatedSupX = supX + side2length * Sin(initAngle); + float rotatedSupY = supY - side2length * Cos(initAngle); + float rotatedInfX = infX + side2length * Sin(initAngle); + float rotatedInfY = infY - side2length * Cos(initAngle); + float side1X = supX - infX; + float side1Y = supY - infY; + float side1Length = CVector2D(side1X, side1Y).Magnitude(); + float side2X = rotatedInfX - infX; + float side2Y = rotatedInfY - infY; + float side2Length = CVector2D(side2X, side2Y).Magnitude(); // == side2length? + if (!decided) { + CVector pos = pPlayerInfo->GetPos(); + result = false; + float X = pos.x - infX; + float Y = pos.y - infY; + float positionAlongSide1 = X * side1X / side1Length + Y * side1Y / side1Length; + bool in_area = false; + if (positionAlongSide1 >= 0.0f && positionAlongSide1 <= side1Length) { + float positionAlongSide2 = X * side2X / side2Length + Y * side2Y / side2Length; + if (positionAlongSide2 >= 0.0f && positionAlongSide2 <= side2Length) { + in_area = !b3D || pos.z >= infZ && pos.z <= supZ; + } + } + + if (in_area) { + switch (command) { + case COMMAND_IS_PLAYER_IN_ANGLED_AREA_2D: + case COMMAND_IS_PLAYER_IN_ANGLED_AREA_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_2D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_3D: + result = true; + break; + case COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_2D: + case COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_2D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_3D: + result = !pPlayerInfo->m_pPed->bInVehicle; + break; + case COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_2D: + case COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_3D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_2D: + case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_3D: + result = pPlayerInfo->m_pPed->bInVehicle; + break; + default: + script_assert(false); + break; + } + } + } + UpdateCompareFlag(result); + if (debug) + CTheScripts::HighlightImportantAngledArea((uintptr)this + m_nIp, infX, infY, supX, supY, + rotatedSupX, rotatedSupY, rotatedInfX, rotatedInfY, b3D ? (infZ + supZ) / 2 : MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugAngledCube(infX, infY, infZ, supX, supY, supZ, + rotatedSupX, rotatedSupY, rotatedInfX, rotatedInfY); + else + CTheScripts::DrawDebugAngledSquare(infX, infY, supX, supY, + rotatedSupX, rotatedSupY, rotatedInfX, rotatedInfY); + } +} + +void CRunningScript::CharInAreaCheckCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug, decided = false; + float infX, infY, infZ, supX, supY, supZ; + switch (command) { + case COMMAND_IS_CHAR_IN_AREA_3D: + case COMMAND_IS_CHAR_IN_AREA_ON_FOOT_3D: + case COMMAND_IS_CHAR_IN_AREA_IN_CAR_3D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_3D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_3D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_3D: + b3D = true; + break; + default: + b3D = false; + break; + } + CollectParameters(pIp, b3D ? 8 : 6); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CVector pos = pPed->InVehicle() ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); + switch (command) { + case COMMAND_IS_CHAR_STOPPED_IN_AREA_3D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_3D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_3D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_2D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_2D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_2D: + if (!CTheScripts::IsPedStopped(pPed)) { + result = false; + decided = true; + } + break; + default: + break; + } + infX = *(float*)&ScriptParams[1]; + infY = *(float*)&ScriptParams[2]; + if (b3D) { + infZ = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[5]; + supZ = *(float*)&ScriptParams[6]; + if (infZ > supZ) { + infZ = *(float*)&ScriptParams[6]; + supZ = *(float*)&ScriptParams[3]; + } + debug = ScriptParams[7]; + } + else { + supX = *(float*)&ScriptParams[3]; + supY = *(float*)&ScriptParams[4]; + debug = ScriptParams[5]; + } + if (infX > supX) { + float tmp = infX; + infX = supX; + supX = tmp; + } + if (infY > supY) { + float tmp = infY; + infY = supY; + supY = tmp; + } + if (!decided) { + result = false; + bool in_area; + if (b3D) { + in_area = infX <= pos.x && + supX >= pos.x && + infY <= pos.y && + supY >= pos.y && + infZ <= pos.z && + supZ >= pos.z; + } + else { + in_area = infX <= pos.x && + supX >= pos.x && + infY <= pos.y && + supY >= pos.y; + } + if (in_area) { + switch (command) { + case COMMAND_IS_CHAR_IN_AREA_2D: + case COMMAND_IS_CHAR_IN_AREA_3D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_2D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_3D: + result = true; + break; + case COMMAND_IS_CHAR_IN_AREA_ON_FOOT_2D: + case COMMAND_IS_CHAR_IN_AREA_ON_FOOT_3D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_2D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_3D: + result = !pPed->bInVehicle; + break; + case COMMAND_IS_CHAR_IN_AREA_IN_CAR_2D: + case COMMAND_IS_CHAR_IN_AREA_IN_CAR_3D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_2D: + case COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_3D: + result = pPed->bInVehicle; + break; + default: + script_assert(false); + break; + } + } + } + UpdateCompareFlag(result); + if (debug) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, infX, infY, supX, supY, b3D ? (infZ + supZ) / 2 : MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugCube(infX, infY, infZ, supX, supY, supZ); + else + CTheScripts::DrawDebugSquare(infX, infY, supX, supY); + } +} + +void CRunningScript::CarInAreaCheckCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug, decided = false; + float infX, infY, infZ, supX, supY, supZ; + switch (command) { + case COMMAND_IS_CAR_IN_AREA_3D: + case COMMAND_IS_CAR_STOPPED_IN_AREA_3D: + b3D = true; + break; + default: + b3D = false; + break; + } + CollectParameters(pIp, b3D ? 8 : 6); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + CVector pos = pVehicle->GetPosition(); + switch (command) { + case COMMAND_IS_CAR_STOPPED_IN_AREA_3D: + case COMMAND_IS_CAR_STOPPED_IN_AREA_2D: + if (!CTheScripts::IsVehicleStopped(pVehicle)) { + result = false; + decided = true; + } + break; + default: + break; + } + infX = *(float*)&ScriptParams[1]; + infY = *(float*)&ScriptParams[2]; + if (b3D) { + infZ = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[5]; + supZ = *(float*)&ScriptParams[6]; + if (infZ > supZ) { + infZ = *(float*)&ScriptParams[6]; + supZ = *(float*)&ScriptParams[3]; + } + debug = ScriptParams[7]; + } + else { + supX = *(float*)&ScriptParams[3]; + supY = *(float*)&ScriptParams[4]; + debug = ScriptParams[5]; + } + if (infX > supX) { + float tmp = infX; + infX = supX; + supX = tmp; + } + if (infY > supY) { + float tmp = infY; + infY = supY; + supY = tmp; + } + if (!decided) { + result = false; + bool in_area; + if (b3D) { + in_area = infX <= pos.x && + supX >= pos.x && + infY <= pos.y && + supY >= pos.y && + infZ <= pos.z && + supZ >= pos.z; + } + else { + in_area = infX <= pos.x && + supX >= pos.x && + infY <= pos.y && + supY >= pos.y; + } + if (in_area) { + switch (command) { + case COMMAND_IS_CAR_IN_AREA_2D: + case COMMAND_IS_CAR_IN_AREA_3D: + case COMMAND_IS_CAR_STOPPED_IN_AREA_2D: + case COMMAND_IS_CAR_STOPPED_IN_AREA_3D: + result = true; + break; + default: + script_assert(false); + break; + } + } + } + UpdateCompareFlag(result); + if (debug) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, infX, infY, supX, supY, b3D ? (infZ + supZ) / 2 : MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugCube(infX, infY, infZ, supX, supY, supZ); + else + CTheScripts::DrawDebugSquare(infX, infY, supX, supY); + } +} + +void CRunningScript::ObjectInAreaCheckCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug; + float infX, infY, infZ, supX, supY, supZ; + switch (command) { + case COMMAND_IS_OBJECT_IN_AREA_3D: + b3D = true; + break; + default: + b3D = false; + break; + } + CollectParameters(pIp, b3D ? 8 : 6); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + CVector pos = pObject->GetPosition(); + infX = *(float*)&ScriptParams[1]; + infY = *(float*)&ScriptParams[2]; + if (b3D) { + infZ = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[5]; + supZ = *(float*)&ScriptParams[6]; + if (infZ > supZ) { + infZ = *(float*)&ScriptParams[6]; + supZ = *(float*)&ScriptParams[3]; + } + debug = ScriptParams[7]; + } + else { + supX = *(float*)&ScriptParams[3]; + supY = *(float*)&ScriptParams[4]; + debug = ScriptParams[5]; + } + if (infX > supX) { + float tmp = infX; + infX = supX; + supX = tmp; + } + if (infY > supY) { + float tmp = infY; + infY = supY; + supY = tmp; + } + result = false; + bool in_area; + if (b3D) { + in_area = infX <= pos.x && + supX >= pos.x && + infY <= pos.y && + supY >= pos.y && + infZ <= pos.z && + supZ >= pos.z; + } + else { + in_area = infX <= pos.x && + supX >= pos.x && + infY <= pos.y && + supY >= pos.y; + } + if (in_area) { + switch (command) { + case COMMAND_IS_OBJECT_IN_AREA_2D: + case COMMAND_IS_OBJECT_IN_AREA_3D: + result = true; + break; + default: + script_assert(false); + break; + } + } + UpdateCompareFlag(result); + if (debug) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, infX, infY, supX, supY, b3D ? (infZ + supZ) / 2 : MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugCube(infX, infY, infZ, supX, supY, supZ); + else + CTheScripts::DrawDebugSquare(infX, infY, supX, supY); + } +} + +void CRunningScript::DoDeatharrestCheck() +{ + if (!m_bDeatharrestEnabled) + return; + if (!CTheScripts::IsPlayerOnAMission()) + return; + CPlayerInfo* pPlayer = &CWorld::Players[CWorld::PlayerInFocus]; + if (!pPlayer->IsRestartingAfterDeath() && !pPlayer->IsRestartingAfterArrest()) + return; +#ifdef MISSION_REPLAY + if (AllowMissionReplay != MISSION_RETRY_STAGE_WAIT_FOR_TIMER_AFTER_RESTART && AllowMissionReplay != MISSION_RETRY_STAGE_NORMAL) + return; + if (AllowMissionReplay == MISSION_RETRY_STAGE_WAIT_FOR_TIMER_AFTER_RESTART) + AllowMissionReplay = MISSION_RETRY_STAGE_NORMAL; + if (CanAllowMissionReplay()) + AllowMissionReplay = MISSION_RETRY_STAGE_WAIT_FOR_SCRIPT_TO_TERMINATE; +#endif + script_assert(m_nStackPointer > 0); + while (m_nStackPointer > 1) + --m_nStackPointer; + m_nIp = m_anStack[--m_nStackPointer]; + CMessages::ClearSmallMessagesOnly(); + *(int32*)&CTheScripts::ScriptSpace[CTheScripts::OnAMissionFlag] = 0; + m_bDeatharrestExecuted = true; + m_nWakeTime = 0; +} + +int16 CRunningScript::GetPadState(uint16 pad, uint16 button) +{ + CPad* pPad = CPad::GetPad(pad); + switch (button) { + case 0: return pPad->NewState.LeftStickX; + case 1: return pPad->NewState.LeftStickY; + case 2: return pPad->NewState.RightStickX; + case 3: return pPad->NewState.RightStickY; + case 4: return pPad->NewState.LeftShoulder1; + case 5: return pPad->NewState.LeftShoulder2; + case 6: return pPad->NewState.RightShoulder1; + case 7: return pPad->NewState.RightShoulder2; + case 8: return pPad->NewState.DPadUp; + case 9: return pPad->NewState.DPadDown; + case 10: return pPad->NewState.DPadLeft; + case 11: return pPad->NewState.DPadRight; + case 12: return pPad->NewState.Start; + case 13: return pPad->NewState.Select; + case 14: return pPad->NewState.Square; + case 15: return pPad->NewState.Triangle; + case 16: return pPad->NewState.Cross; + case 17: return pPad->NewState.Circle; + case 18: return pPad->NewState.LeftShock; + case 19: return pPad->NewState.RightShock; + default: break; + } + return 0; +} + +#ifdef GTA_SCRIPT_COLLECTIVE +void CRunningScript::LocateCollectiveCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug, decided = false; + float X, Y, Z, dX, dY, dZ; + switch (command) { + case COMMAND_LOCATE_COLL_ANY_MEANS_2D: + case COMMAND_LOCATE_COLL_ON_FOOT_2D: + case COMMAND_LOCATE_COLL_IN_CAR_2D: + case COMMAND_LOCATE_STOPPED_COLL_ANY_MEANS_2D: + case COMMAND_LOCATE_STOPPED_COLL_ON_FOOT_2D: + case COMMAND_LOCATE_STOPPED_COLL_IN_CAR_2D: + b3D = false; + break; + default: + b3D = true; + break; + } + CollectParameters(pIp, b3D ? 8 : 6); + X = *(float*)&ScriptParams[1]; + Y = *(float*)&ScriptParams[2]; + if (b3D) { + Z = *(float*)&ScriptParams[3]; + dX = *(float*)&ScriptParams[4]; + dY = *(float*)&ScriptParams[5]; + dZ = *(float*)&ScriptParams[6]; + debug = ScriptParams[7]; + } + else { + dX = *(float*)&ScriptParams[3]; + dY = *(float*)&ScriptParams[4]; + debug = ScriptParams[5]; + } + result = true; + for (int i = 0; i < MAX_NUM_COLLECTIVES && result; i++) { + if (ScriptParams[0] != CTheScripts::CollectiveArray[i].colIndex) + continue; + CPed* pPed = CPools::GetPedPool()->GetAt(CTheScripts::CollectiveArray[i].pedIndex); + if (!pPed) { + CTheScripts::CollectiveArray[i].colIndex = -1; + CTheScripts::CollectiveArray[i].pedIndex = 0; + continue; + } + CVector pos = pPed->bInVehicle ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); + switch (command) { + case COMMAND_LOCATE_STOPPED_COLL_ANY_MEANS_2D: + case COMMAND_LOCATE_STOPPED_COLL_ON_FOOT_2D: + case COMMAND_LOCATE_STOPPED_COLL_IN_CAR_2D: + if (!CTheScripts::IsPedStopped(pPed)) { + result = false; + decided = true; + } + break; + default: + break; + } + if (!decided) { + bool in_area; + if (b3D) { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y && + Z - dZ <= pos.z && + Z + dZ >= pos.z; + } + else { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y; + } + result = false; + if (in_area) { + switch (command) { + case COMMAND_LOCATE_COLL_ANY_MEANS_2D: + case COMMAND_LOCATE_STOPPED_COLL_ANY_MEANS_2D: + result = true; + break; + case COMMAND_LOCATE_COLL_ON_FOOT_2D: + case COMMAND_LOCATE_STOPPED_COLL_ON_FOOT_2D: + result = !pPed->bInVehicle; + break; + case COMMAND_LOCATE_COLL_IN_CAR_2D: + case COMMAND_LOCATE_STOPPED_COLL_IN_CAR_2D: + result = pPed->bInVehicle; + break; + default: + script_assert(false); + break; + } + } + } + } + UpdateCompareFlag(result); + if (debug) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); + else + CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); + } +} + +void CRunningScript::LocateCollectiveCharCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug; + float X, Y, Z, dX, dY, dZ; + switch (command) { + case COMMAND_LOCATE_COLL_ANY_MEANS_CHAR_2D: + case COMMAND_LOCATE_COLL_ON_FOOT_CHAR_2D: + case COMMAND_LOCATE_COLL_IN_CAR_CHAR_2D: + b3D = false; + break; + default: + b3D = true; + break; + } + CollectParameters(pIp, b3D ? 6 : 5); + CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); + script_assert(pTarget); + if (pTarget->bInVehicle) { + X = pTarget->m_pMyVehicle->GetPosition().x; + Y = pTarget->m_pMyVehicle->GetPosition().y; + Z = pTarget->m_pMyVehicle->GetPosition().z; + } + else { + X = pTarget->GetPosition().x; + Y = pTarget->GetPosition().y; + Z = pTarget->GetPosition().z; + } + dX = *(float*)&ScriptParams[2]; + dY = *(float*)&ScriptParams[3]; + if (b3D) { + dZ = *(float*)&ScriptParams[4]; + debug = ScriptParams[5]; + } + else { + debug = ScriptParams[4]; + } + result = true; + for (int i = 0; i < MAX_NUM_COLLECTIVES && result; i++) { + if (ScriptParams[0] != CTheScripts::CollectiveArray[i].colIndex) + continue; + CPed* pPed = CPools::GetPedPool()->GetAt(CTheScripts::CollectiveArray[i].pedIndex); + if (!pPed) { + CTheScripts::CollectiveArray[i].colIndex = -1; + CTheScripts::CollectiveArray[i].pedIndex = 0; + continue; + } + CVector pos = pPed->bInVehicle ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); + bool in_area; + if (b3D) { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y && + Z - dZ <= pos.z && + Z + dZ >= pos.z; + } + else { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y; + } + result = false; + if (in_area) { + switch (command) { + case COMMAND_LOCATE_COLL_ANY_MEANS_CHAR_2D: + result = true; + break; + case COMMAND_LOCATE_COLL_ON_FOOT_CHAR_2D: + result = !pPed->bInVehicle; + break; + case COMMAND_LOCATE_COLL_IN_CAR_CHAR_2D: + result = pPed->bInVehicle; + break; + default: + script_assert(false); + break; + } + } + } + UpdateCompareFlag(result); + if (debug) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); + else + CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); + } +} + +void CRunningScript::LocateCollectiveCarCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug; + float X, Y, Z, dX, dY, dZ; + switch (command) { + case COMMAND_LOCATE_COLL_ANY_MEANS_CAR_2D: + case COMMAND_LOCATE_COLL_ON_FOOT_CAR_2D: + case COMMAND_LOCATE_COLL_IN_CAR_CAR_2D: + b3D = false; + break; + default: + b3D = true; + break; + } + CollectParameters(pIp, b3D ? 6 : 5); + CVehicle* pTarget = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + script_assert(pTarget); + X = pTarget->GetPosition().x; + Y = pTarget->GetPosition().y; + Z = pTarget->GetPosition().z; + dX = *(float*)&ScriptParams[2]; + dY = *(float*)&ScriptParams[3]; + if (b3D) { + dZ = *(float*)&ScriptParams[4]; + debug = ScriptParams[5]; + } + else { + debug = ScriptParams[4]; + } + result = true; + for (int i = 0; i < MAX_NUM_COLLECTIVES && result; i++) { + if (ScriptParams[0] != CTheScripts::CollectiveArray[i].colIndex) + continue; + CPed* pPed = CPools::GetPedPool()->GetAt(CTheScripts::CollectiveArray[i].pedIndex); + if (!pPed) { + CTheScripts::CollectiveArray[i].colIndex = -1; + CTheScripts::CollectiveArray[i].pedIndex = 0; + continue; + } + CVector pos = pPed->bInVehicle ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); + bool in_area; + if (b3D) { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y && + Z - dZ <= pos.z && + Z + dZ >= pos.z; + } + else { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y; + } + result = false; + if (in_area) { + switch (command) { + case COMMAND_LOCATE_COLL_ANY_MEANS_CAR_2D: + result = true; + break; + case COMMAND_LOCATE_COLL_ON_FOOT_CAR_2D: + result = !pPed->bInVehicle; + break; + case COMMAND_LOCATE_COLL_IN_CAR_CAR_2D: + result = pPed->bInVehicle; + break; + default: + script_assert(false); + break; + } + } + } + UpdateCompareFlag(result); + if (debug) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); + else + CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); + } +} + +void CRunningScript::LocateCollectivePlayerCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug; + float X, Y, Z, dX, dY, dZ; + switch (command) { + case COMMAND_LOCATE_COLL_ANY_MEANS_PLAYER_2D: + case COMMAND_LOCATE_COLL_ON_FOOT_PLAYER_2D: + case COMMAND_LOCATE_COLL_IN_CAR_PLAYER_2D: + b3D = false; + break; + default: + b3D = true; + break; + } + CollectParameters(pIp, b3D ? 6 : 5); + CVector pos = CWorld::Players[ScriptParams[1]].GetPos(); + X = pos.x; + Y = pos.y; + Z = pos.z; + dX = *(float*)&ScriptParams[2]; + dY = *(float*)&ScriptParams[3]; + if (b3D) { + dZ = *(float*)&ScriptParams[4]; + debug = ScriptParams[5]; + } + else { + debug = ScriptParams[4]; + } + result = true; + for (int i = 0; i < MAX_NUM_COLLECTIVES && result; i++) { + if (ScriptParams[0] != CTheScripts::CollectiveArray[i].colIndex) + continue; + CPed* pPed = CPools::GetPedPool()->GetAt(CTheScripts::CollectiveArray[i].pedIndex); + if (!pPed) { + CTheScripts::CollectiveArray[i].colIndex = -1; + CTheScripts::CollectiveArray[i].pedIndex = 0; + continue; + } + CVector pos = pPed->bInVehicle ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); + bool in_area; + if (b3D) { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y && + Z - dZ <= pos.z && + Z + dZ >= pos.z; + } + else { + in_area = X - dX <= pos.x && + X + dX >= pos.x && + Y - dY <= pos.y && + Y + dY >= pos.y; + } + result = false; + if (in_area) { + switch (command) { + case COMMAND_LOCATE_COLL_ANY_MEANS_PLAYER_2D: + result = true; + break; + case COMMAND_LOCATE_COLL_ON_FOOT_PLAYER_2D: + result = !pPed->bInVehicle; + break; + case COMMAND_LOCATE_COLL_IN_CAR_PLAYER_2D: + result = pPed->bInVehicle; + break; + default: + script_assert(false); + break; + } + } + } + UpdateCompareFlag(result); + if (debug) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); + else + CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); + } +} + +void CRunningScript::CollectiveInAreaCheckCommand(int32 command, uint32* pIp) +{ + bool b3D, result, debug, decided = false; + float infX, infY, infZ, supX, supY, supZ; + switch (command) { + case COMMAND_IS_COLL_IN_AREA_2D: + case COMMAND_IS_COLL_IN_AREA_ON_FOOT_2D: + case COMMAND_IS_COLL_IN_AREA_IN_CAR_2D: + case COMMAND_IS_COLL_STOPPED_IN_AREA_2D: + case COMMAND_IS_COLL_STOPPED_IN_AREA_ON_FOOT_2D: + case COMMAND_IS_COLL_STOPPED_IN_AREA_IN_CAR_2D: + b3D = false; + break; + default: + b3D = true; + break; + } + CollectParameters(pIp, b3D ? 8 : 6); + infX = *(float*)&ScriptParams[1]; + infY = *(float*)&ScriptParams[2]; + if (b3D) { + infZ = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[5]; + supZ = *(float*)&ScriptParams[6]; + if (infZ > supZ) { + infZ = *(float*)&ScriptParams[6]; + supZ = *(float*)&ScriptParams[3]; + } + debug = ScriptParams[7]; + } + else { + supX = *(float*)&ScriptParams[3]; + supY = *(float*)&ScriptParams[4]; + debug = ScriptParams[5]; + } + if (infX > supX) { + float tmp = infX; + infX = supX; + supX = tmp; + } + if (infY > supY) { + float tmp = infY; + infY = supY; + supY = tmp; + } + result = true; + for (int i = 0; i < MAX_NUM_COLLECTIVES && result; i++) { + if (ScriptParams[0] != CTheScripts::CollectiveArray[i].colIndex) + continue; + CPed* pPed = CPools::GetPedPool()->GetAt(CTheScripts::CollectiveArray[i].pedIndex); + if (!pPed) { + CTheScripts::CollectiveArray[i].colIndex = -1; + CTheScripts::CollectiveArray[i].pedIndex = 0; + continue; + } + CVector pos = pPed->bInVehicle ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); + switch (command) { + case COMMAND_IS_COLL_STOPPED_IN_AREA_2D: + case COMMAND_IS_COLL_STOPPED_IN_AREA_ON_FOOT_2D: + case COMMAND_IS_COLL_STOPPED_IN_AREA_IN_CAR_2D: + if (!CTheScripts::IsPedStopped(pPed)) { + result = false; + decided = true; + } + break; + default: + break; + } + if (!decided) { + bool in_area; + if (b3D) { + in_area = infX <= pos.x && + supX >= pos.x && + infY <= pos.y && + supY >= pos.y && + infZ <= pos.z && + supZ >= pos.z; + } + else { + in_area = infX <= pos.x && + supX >= pos.x && + infY <= pos.y && + supY >= pos.y; + } + result = false; + if (in_area) { + switch (command) { + case COMMAND_IS_COLL_IN_AREA_2D: + case COMMAND_IS_COLL_STOPPED_IN_AREA_2D: + result = true; + break; + case COMMAND_IS_COLL_IN_AREA_ON_FOOT_2D: + case COMMAND_IS_COLL_STOPPED_IN_AREA_ON_FOOT_2D: + result = !pPed->bInVehicle; + break; + case COMMAND_IS_COLL_IN_AREA_IN_CAR_2D: + case COMMAND_IS_COLL_STOPPED_IN_AREA_IN_CAR_2D: + result = pPed->bInVehicle; + break; + default: + script_assert(false); + break; + } + } + } + } + UpdateCompareFlag(result); + if (debug) + CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, infX, infY, supX, supY, b3D ? (infZ + supZ) / 2 : MAP_Z_LOW_LIMIT); + if (CTheScripts::DbgFlag) { + if (b3D) + CTheScripts::DrawDebugCube(infX, infY, infZ, supX, supY, supZ); + else + CTheScripts::DrawDebugSquare(infX, infY, supX, supY); + } +} +#endif + +bool CRunningScript::CheckDamagedWeaponType(int32 actual, int32 type) +{ + if (actual == -1) + return false; + + if (type == WEAPONTYPE_ANYMELEE) { + if (actual <= WEAPONTYPE_CHAINSAW) + return true; + if (actual >= WEAPONTYPE_GRENADE && actual <= WEAPONTYPE_UNIDENTIFIED) + return false; + return false; + } + + if (type != WEAPONTYPE_ANYWEAPON) + return false; + + switch (actual) { + case WEAPONTYPE_UNARMED: + case WEAPONTYPE_BRASSKNUCKLE: + case WEAPONTYPE_SCREWDRIVER: + case WEAPONTYPE_GOLFCLUB: + case WEAPONTYPE_NIGHTSTICK: + case WEAPONTYPE_KNIFE: + case WEAPONTYPE_BASEBALLBAT: + case WEAPONTYPE_HAMMER: + case WEAPONTYPE_CLEAVER: + case WEAPONTYPE_MACHETE: + case WEAPONTYPE_KATANA: + case WEAPONTYPE_CHAINSAW: + case WEAPONTYPE_GRENADE: + case WEAPONTYPE_DETONATOR_GRENADE: + case WEAPONTYPE_TEARGAS: + case WEAPONTYPE_MOLOTOV: + case WEAPONTYPE_ROCKET: + case WEAPONTYPE_COLT45: + case WEAPONTYPE_PYTHON: + case WEAPONTYPE_SHOTGUN: + case WEAPONTYPE_SPAS12_SHOTGUN: + case WEAPONTYPE_STUBBY_SHOTGUN: + case WEAPONTYPE_TEC9: + case WEAPONTYPE_UZI: + case WEAPONTYPE_SILENCED_INGRAM: + case WEAPONTYPE_MP5: + case WEAPONTYPE_M4: + case WEAPONTYPE_RUGER: + case WEAPONTYPE_SNIPERRIFLE: + case WEAPONTYPE_LASERSCOPE: + case WEAPONTYPE_ROCKETLAUNCHER: + case WEAPONTYPE_FLAMETHROWER: + case WEAPONTYPE_M60: + case WEAPONTYPE_MINIGUN: + case WEAPONTYPE_DETONATOR: + case WEAPONTYPE_HELICANNON: + case WEAPONTYPE_CAMERA: + case WEAPONTYPE_EXPLOSION: + case WEAPONTYPE_UZI_DRIVEBY: + return true; + case WEAPONTYPE_HEALTH: + case WEAPONTYPE_ARMOUR: + case WEAPONTYPE_RAMMEDBYCAR: + case WEAPONTYPE_RUNOVERBYCAR: + case WEAPONTYPE_DROWNING: + case WEAPONTYPE_FALL: + case WEAPONTYPE_UNIDENTIFIED: + return false; + } + + return false; +} + +void CTheScripts::PrintListSizes() +{ + int active = 0; + int idle = 0; + + for (CRunningScript* pScript = pActiveScripts; pScript; pScript = pScript->GetNext()) + active++; + for (CRunningScript* pScript = pIdleScripts; pScript; pScript = pScript->GetNext()) + idle++; + + debug("active: %d, idle: %d", active, idle); +} + +uint32 DbgLineColour = 0x0000FFFF; // r = 0, g = 0, b = 255, a = 255 + +void CTheScripts::DrawDebugSquare(float infX, float infY, float supX, float supY) +{ + CColPoint tmpCP; + CEntity* tmpEP; + CVector p1, p2, p3, p4; + p1 = CVector(infX, infY, -1000.0f); + CWorld::ProcessVerticalLine(p1, 1000.0f, tmpCP, tmpEP, true, false, false, false, true, false, nil); + p1.z = 2.0f + tmpCP.point.z; + p2 = CVector(supX, supY, -1000.0f); + CWorld::ProcessVerticalLine(p2, 1000.0f, tmpCP, tmpEP, true, false, false, false, true, false, nil); + p2.z = 2.0f + tmpCP.point.z; + p3 = CVector(infX, supY, -1000.0f); + CWorld::ProcessVerticalLine(p3, 1000.0f, tmpCP, tmpEP, true, false, false, false, true, false, nil); + p3.z = 2.0f + tmpCP.point.z; + p4 = CVector(supX, infY, -1000.0f); + CWorld::ProcessVerticalLine(p4, 1000.0f, tmpCP, tmpEP, true, false, false, false, true, false, nil); + p4.z = 2.0f + tmpCP.point.z; + CTheScripts::ScriptDebugLine3D(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(p2.x, p2.y, p2.z, p3.x, p3.y, p3.z, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(p3.x, p3.y, p3.z, p4.x, p4.y, p4.z, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(p4.x, p4.y, p4.z, p1.x, p1.y, p1.z, DbgLineColour, DbgLineColour); +} + +void CTheScripts::DrawDebugAngledSquare(float infX, float infY, float supX, float supY, float rotSupX, float rotSupY, float rotInfX, float rotInfY) +{ + CColPoint tmpCP; + CEntity* tmpEP; + CVector p1, p2, p3, p4; + p1 = CVector(infX, infY, -1000.0f); + CWorld::ProcessVerticalLine(p1, 1000.0f, tmpCP, tmpEP, true, false, false, false, true, false, nil); + p1.z = 2.0f + tmpCP.point.z; + p2 = CVector(supX, supY, -1000.0f); + CWorld::ProcessVerticalLine(p2, 1000.0f, tmpCP, tmpEP, true, false, false, false, true, false, nil); + p2.z = 2.0f + tmpCP.point.z; + p3 = CVector(rotSupX, rotSupY, -1000.0f); + CWorld::ProcessVerticalLine(p3, 1000.0f, tmpCP, tmpEP, true, false, false, false, true, false, nil); + p3.z = 2.0f + tmpCP.point.z; + p4 = CVector(rotInfX, rotInfY, -1000.0f); + CWorld::ProcessVerticalLine(p4, 1000.0f, tmpCP, tmpEP, true, false, false, false, true, false, nil); + p4.z = 2.0f + tmpCP.point.z; + CTheScripts::ScriptDebugLine3D(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(p2.x, p2.y, p2.z, p3.x, p3.y, p3.z, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(p3.x, p3.y, p3.z, p4.x, p4.y, p4.z, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(p4.x, p4.y, p4.z, p1.x, p1.y, p1.z, DbgLineColour, DbgLineColour); +} + +void CTheScripts::DrawDebugCube(float infX, float infY, float infZ, float supX, float supY, float supZ) +{ + CTheScripts::ScriptDebugLine3D(infX, infY, infZ, supX, infY, infZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(supX, infY, infZ, supX, supY, infZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(supX, supY, infZ, infX, supY, infZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(infX, supY, infZ, infX, infY, infZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(infX, infY, supZ, supX, infY, supZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(supX, infY, supZ, supX, supY, supZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(supX, supY, supZ, infX, supY, supZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(infX, supY, supZ, infX, infY, supZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(infX, infY, supZ, infX, infY, infZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(supX, infY, supZ, supX, infY, infZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(supX, supY, supZ, supX, supY, infZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(infX, supY, supZ, infX, supY, infZ, DbgLineColour, DbgLineColour); +} + +void CTheScripts::DrawDebugAngledCube(float infX, float infY, float infZ, float supX, float supY, float supZ, float rotSupX, float rotSupY, float rotInfX, float rotInfY) +{ + CTheScripts::ScriptDebugLine3D(infX, infY, infZ, supX, infY, infZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(supX, infY, infZ, rotSupX, rotSupY, infZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(rotSupX, rotSupY, infZ, rotInfX, rotInfY, infZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(rotInfX, rotInfY, infZ, infX, infY, infZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(infX, infY, supZ, supX, infY, supZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(supX, infY, supZ, rotSupX, rotSupY, supZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(rotSupX, rotSupY, rotInfX, rotInfY, supY, supZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(rotInfX, rotInfY, supZ, infX, infY, supZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(infX, infY, supZ, infX, infY, infZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(supX, infY, supZ, supX, infY, infZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(rotSupX, rotSupY, supZ, rotSupX, rotSupY, infZ, DbgLineColour, DbgLineColour); + CTheScripts::ScriptDebugLine3D(rotInfX, rotInfY, supZ, rotInfX, rotInfY, infZ, DbgLineColour, DbgLineColour); +} + +void CTheScripts::ScriptDebugLine3D(float x1, float y1, float z1, float x2, float y2, float z2, uint32 col, uint32 col2) +{ + if (NumScriptDebugLines >= MAX_NUM_STORED_LINES) + return; + aStoredLines[NumScriptDebugLines].vecInf = CVector(x1, y1, z1); + aStoredLines[NumScriptDebugLines].vecSup = CVector(x2, y2, z2); + aStoredLines[NumScriptDebugLines].color1 = col; + aStoredLines[NumScriptDebugLines++].color2 = col2; +} + +void CTheScripts::RenderTheScriptDebugLines() +{ + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)1); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)1); + for (int i = 0; i < NumScriptDebugLines; i++) { + CLines::RenderLineWithClipping( + aStoredLines[i].vecInf.x, + aStoredLines[i].vecInf.y, + aStoredLines[i].vecInf.z, + aStoredLines[i].vecSup.x, + aStoredLines[i].vecSup.y, + aStoredLines[i].vecSup.z, + aStoredLines[i].color1, + aStoredLines[i].color2); + } + NumScriptDebugLines = 0; + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)0); +} + +#define SCRIPT_DATA_SIZE sizeof(CTheScripts::OnAMissionFlag) +\ + 4 * sizeof(uint32) * MAX_NUM_BUILDING_SWAPS + 2 * sizeof(uint32) * MAX_NUM_INVISIBILITY_SETTINGS + 5 * sizeof(uint32) + +void CTheScripts::SaveAllScripts(uint8* buf, uint32* size) +{ +INITSAVEBUF + uint32 varSpace = GetSizeOfVariableSpace(); + uint32 runningScripts = 0; + for (CRunningScript* pScript = pActiveScripts; pScript; pScript = pScript->GetNext()) + runningScripts++; + *size = CRunningScript::nSaveStructSize * runningScripts + varSpace + SCRIPT_DATA_SIZE + SAVE_HEADER_SIZE + 3 * sizeof(uint32); + WriteSaveHeader(buf, 'S', 'C', 'R', '\0', *size - SAVE_HEADER_SIZE); + WriteSaveBuf(buf, varSpace); + for (uint32 i = 0; i < varSpace; i++) + WriteSaveBuf(buf, ScriptSpace[i]); +#ifdef CHECK_STRUCT_SIZES + static_assert(SCRIPT_DATA_SIZE == 968, "CTheScripts::SaveAllScripts"); +#endif + uint32 script_data_size = SCRIPT_DATA_SIZE; + WriteSaveBuf(buf, script_data_size); + WriteSaveBuf(buf, OnAMissionFlag); + WriteSaveBuf(buf, LastMissionPassedTime); + for (uint32 i = 0; i < MAX_NUM_BUILDING_SWAPS; i++) { + CBuilding* pBuilding = BuildingSwapArray[i].m_pBuilding; + uint32 type, handle; + if (!pBuilding) { + type = 0; + handle = 0; + } else if (pBuilding->GetIsATreadable()) { + type = 1; + handle = CPools::GetTreadablePool()->GetJustIndex_NoFreeAssert((CTreadable*)pBuilding) + 1; + } else { + type = 2; + handle = CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert(pBuilding) + 1; + } + WriteSaveBuf(buf, type); + WriteSaveBuf(buf, handle); + WriteSaveBuf(buf, BuildingSwapArray[i].m_nNewModel); + WriteSaveBuf(buf, BuildingSwapArray[i].m_nOldModel); + } + for (uint32 i = 0; i < MAX_NUM_INVISIBILITY_SETTINGS; i++) { + CEntity* pEntity = InvisibilitySettingArray[i]; + uint32 type, handle; + if (!pEntity) { + type = 0; + handle = 0; + } else { + switch (pEntity->GetType()) { + case ENTITY_TYPE_BUILDING: + if (((CBuilding*)pEntity)->GetIsATreadable()) { + type = 1; + handle = CPools::GetTreadablePool()->GetJustIndex_NoFreeAssert((CTreadable*)pEntity) + 1; + } else { + type = 2; + handle = CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert((CBuilding*)pEntity) + 1; + } + break; + case ENTITY_TYPE_OBJECT: + type = 3; + handle = CPools::GetObjectPool()->GetJustIndex_NoFreeAssert((CObject*)pEntity) + 1; + break; + case ENTITY_TYPE_DUMMY: + type = 4; + handle = CPools::GetDummyPool()->GetJustIndex_NoFreeAssert((CDummy*)pEntity) + 1; + default: break; + } + } + WriteSaveBuf(buf, type); + WriteSaveBuf(buf, handle); + } + WriteSaveBuf(buf, bUsingAMultiScriptFile); + WriteSaveBuf(buf, bPlayerHasMetDebbieHarry); + WriteSaveBuf(buf, (uint16)0); + WriteSaveBuf(buf, MainScriptSize); + WriteSaveBuf(buf, LargestMissionScriptSize); + WriteSaveBuf(buf, NumberOfMissionScripts); + WriteSaveBuf(buf, NumberOfExclusiveMissionScripts); + WriteSaveBuf(buf, runningScripts); + for (CRunningScript* pScript = pActiveScripts; pScript; pScript = pScript->GetNext()) + pScript->Save(buf); +VALIDATESAVEBUF(*size) +} + +void CTheScripts::LoadAllScripts(uint8* buf, uint32 size) +{ + Init(); +INITSAVEBUF + CheckSaveHeader(buf, 'S', 'C', 'R', '\0', size - SAVE_HEADER_SIZE); + uint32 varSpace, type, handle; + uint32 tmp; + + ReadSaveBuf(&varSpace, buf); + for (uint32 i = 0; i < varSpace; i++) + ReadSaveBuf(&ScriptSpace[i], buf); + ReadSaveBuf(&tmp, buf); + script_assert(tmp == SCRIPT_DATA_SIZE); + ReadSaveBuf(&OnAMissionFlag, buf); + ReadSaveBuf(&LastMissionPassedTime, buf); + for (uint32 i = 0; i < MAX_NUM_BUILDING_SWAPS; i++) { + ReadSaveBuf(&type, buf); + ReadSaveBuf(&handle, buf); + switch (type) { + case 0: + BuildingSwapArray[i].m_pBuilding = nil; + break; + case 1: + BuildingSwapArray[i].m_pBuilding = CPools::GetTreadablePool()->GetSlot(handle - 1); + break; + case 2: + BuildingSwapArray[i].m_pBuilding = CPools::GetBuildingPool()->GetSlot(handle - 1); + break; + default: + script_assert(false); + } + ReadSaveBuf(&BuildingSwapArray[i].m_nNewModel, buf); + ReadSaveBuf(&BuildingSwapArray[i].m_nOldModel, buf); + if (BuildingSwapArray[i].m_pBuilding) + BuildingSwapArray[i].m_pBuilding->ReplaceWithNewModel(BuildingSwapArray[i].m_nNewModel); + } + for (uint32 i = 0; i < MAX_NUM_INVISIBILITY_SETTINGS; i++) { + ReadSaveBuf(&type, buf); + ReadSaveBuf(&handle, buf); + switch (type) { + case 0: + InvisibilitySettingArray[i] = nil; + break; + case 1: + InvisibilitySettingArray[i] = CPools::GetTreadablePool()->GetSlot(handle - 1); + break; + case 2: + InvisibilitySettingArray[i] = CPools::GetBuildingPool()->GetSlot(handle - 1); + break; + case 3: + InvisibilitySettingArray[i] = CPools::GetObjectPool()->GetSlot(handle - 1); + break; + case 4: + InvisibilitySettingArray[i] = CPools::GetDummyPool()->GetSlot(handle - 1); + break; + default: + script_assert(false); + } + if (InvisibilitySettingArray[i]) + InvisibilitySettingArray[i]->bIsVisible = false; + } + bool tmpBool; + ReadSaveBuf(&tmpBool, buf); + script_assert(tmpBool == bUsingAMultiScriptFile); + ReadSaveBuf(&bPlayerHasMetDebbieHarry, buf); + SkipSaveBuf(buf, 2); + ReadSaveBuf(&tmp, buf); + script_assert(tmp == MainScriptSize); + ReadSaveBuf(&tmp, buf); + script_assert(tmp == LargestMissionScriptSize); + uint16 tmp16; + ReadSaveBuf(&tmp16, buf); + script_assert(tmp16 == NumberOfMissionScripts); + ReadSaveBuf(&tmp16, buf); + script_assert(tmp16 == NumberOfExclusiveMissionScripts); + uint32 runningScripts; + ReadSaveBuf(&runningScripts, buf); + for (uint32 i = 0; i < runningScripts; i++) + StartNewScript(0)->Load(buf); +VALIDATESAVEBUF(size) +} + +#undef SCRIPT_DATA_SIZE + +void CRunningScript::Save(uint8*& buf) +{ +#ifdef COMPATIBLE_SAVES + ZeroSaveBuf(buf, 8); + for (int i = 0; i < 8; i++) + WriteSaveBuf(buf, m_abScriptName[i]); + WriteSaveBuf(buf, m_nIp); +#ifdef CHECK_STRUCT_SIZES + static_assert(MAX_STACK_DEPTH == 6, "Compatibility loss: MAX_STACK_DEPTH != 6"); +#endif + for (int i = 0; i < MAX_STACK_DEPTH; i++) + WriteSaveBuf(buf, m_anStack[i]); + WriteSaveBuf(buf, m_nStackPointer); + ZeroSaveBuf(buf, 2); +#ifdef CHECK_STRUCT_SIZES + static_assert(NUM_LOCAL_VARS + NUM_TIMERS == 18, "Compatibility loss: NUM_LOCAL_VARS + NUM_TIMERS != 18"); +#endif + for (int i = 0; i < NUM_LOCAL_VARS + NUM_TIMERS; i++) + WriteSaveBuf(buf, m_anLocalVariables[i]); + WriteSaveBuf(buf, m_bIsActive); + WriteSaveBuf(buf, m_bCondResult); + WriteSaveBuf(buf, m_bIsMissionScript); + WriteSaveBuf(buf, m_bSkipWakeTime); + WriteSaveBuf(buf, m_nWakeTime); + WriteSaveBuf(buf, m_nAndOrState); + WriteSaveBuf(buf, m_bNotFlag); + WriteSaveBuf(buf, m_bDeatharrestEnabled); + WriteSaveBuf(buf, m_bDeatharrestExecuted); + WriteSaveBuf(buf, m_bMissionFlag); + ZeroSaveBuf(buf, 2); +#else + WriteSaveBuf(buf, *this); +#endif +} + +void CRunningScript::Load(uint8*& buf) +{ +#ifdef COMPATIBLE_SAVES + SkipSaveBuf(buf, 8); + for (int i = 0; i < 8; i++) + ReadSaveBuf(&m_abScriptName[i], buf); + ReadSaveBuf(&m_nIp, buf); +#ifdef CHECK_STRUCT_SIZES + static_assert(MAX_STACK_DEPTH == 6, "Compatibility loss: MAX_STACK_DEPTH != 6"); +#endif + for (int i = 0; i < MAX_STACK_DEPTH; i++) + ReadSaveBuf(&m_anStack[i], buf); + ReadSaveBuf(&m_nStackPointer, buf); + SkipSaveBuf(buf, 2); +#ifdef CHECK_STRUCT_SIZES + static_assert(NUM_LOCAL_VARS + NUM_TIMERS == 18, "Compatibility loss: NUM_LOCAL_VARS + NUM_TIMERS != 18"); +#endif + for (int i = 0; i < NUM_LOCAL_VARS + NUM_TIMERS; i++) + ReadSaveBuf(&m_anLocalVariables[i], buf); + ReadSaveBuf(&m_bIsActive, buf); + ReadSaveBuf(&m_bCondResult, buf); + ReadSaveBuf(&m_bIsMissionScript, buf); + ReadSaveBuf(&m_bSkipWakeTime, buf); + ReadSaveBuf(&m_nWakeTime, buf); + ReadSaveBuf(&m_nAndOrState, buf); + ReadSaveBuf(&m_bNotFlag, buf); + ReadSaveBuf(&m_bDeatharrestEnabled, buf); + ReadSaveBuf(&m_bDeatharrestExecuted, buf); + ReadSaveBuf(&m_bMissionFlag, buf); + SkipSaveBuf(buf, 2); +#else + CRunningScript* n = next; + CRunningScript* p = prev; + ReadSaveBuf(this, buf); + next = n; + prev = p; +#endif +} + +void CTheScripts::ClearSpaceForMissionEntity(const CVector& pos, CEntity* pEntity) +{ + static CColPoint aTempColPoints[MAX_COLLISION_POINTS]; + int16 entities = 0; + CEntity* aEntities[16]; + CWorld::FindObjectsKindaColliding(pos, pEntity->GetBoundRadius(), false, &entities, 16, aEntities, false, true, true, false, false); + if (entities <= 0) + return; + for (uint16 i = 0; i < entities; i++) { + if (aEntities[i] != pEntity && aEntities[i]->IsPed() && ((CPed*)aEntities[i])->bInVehicle) + aEntities[i] = nil; + } + for (uint16 i = 0; i < entities; i++) { + if (aEntities[i] == pEntity || !aEntities[i]) + continue; + CEntity* pFound = aEntities[i]; + int cols; + if (pEntity->GetColModel()->numLines <= 0) + cols = CCollision::ProcessColModels(pEntity->GetMatrix(), *pEntity->GetColModel(), + pFound->GetMatrix(), *pFound->GetColModel(), aTempColPoints, nil, nil); + else { + float lines[4]; + lines[0] = lines[1] = lines[2] = lines[3] = 1.0f; + CColPoint tmp[4]; + cols = CCollision::ProcessColModels(pEntity->GetMatrix(), *pEntity->GetColModel(), + pFound->GetMatrix(), *pFound->GetColModel(), aTempColPoints,tmp, lines); + } + if (cols <= 0) + continue; + switch (pFound->GetType()) { + case ENTITY_TYPE_VEHICLE: + { + printf("Will try to delete a vehicle where a mission entity should be\n"); + CVehicle* pVehicle = (CVehicle*)pFound; + if (pVehicle->bIsLocked || !pVehicle->CanBeDeleted()) + break; + if (pVehicle->pDriver) { + CPopulation::RemovePed(pVehicle->pDriver); + pVehicle->pDriver = nil; + } + for (int i = 0; i < pVehicle->m_nNumMaxPassengers; i++) { + if (pVehicle->pPassengers[i]) { + CPopulation::RemovePed(pVehicle->pPassengers[i]); + pVehicle->pPassengers[i] = 0; + pVehicle->m_nNumPassengers--; + } + } + CCarCtrl::RemoveFromInterestingVehicleList(pVehicle); + CWorld::Remove(pVehicle); + delete pVehicle; + break; + } + case ENTITY_TYPE_PED: + { + CPed* pPed = (CPed*)pFound; + if (pPed->IsPlayer() || !pPed->CanBeDeleted()) + break; + CPopulation::RemovePed(pPed); + printf("Deleted a ped where a mission entity should be\n"); + break; + } + default: break; + } + } +} + +void CTheScripts::HighlightImportantArea(uint32 id, float x1, float y1, float x2, float y2, float z) +{ + float infX, infY, supX, supY; + if (x1 < x2) { + infX = x1; + supX = x2; + } else { + infX = x2; + supX = x1; + } + if (y1 < y2) { + infY = y1; + supY = y2; + } + else { + infY = y2; + supY = y1; + } + CVector center; + center.x = (infX + supX) / 2; + center.y = (infY + supY) / 2; + center.z = (z <= MAP_Z_LOW_LIMIT) ? CWorld::FindGroundZForCoord(center.x, center.y) : z; + CShadows::RenderIndicatorShadow(id, 2, gpGoalTex, ¢er, supX - center.x, 0.0f, 0.0f, center.y - supY, 0); +} + +void CTheScripts::HighlightImportantAngledArea(uint32 id, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float z) +{ + float infX, infY, supX, supY, X, Y; + X = (x1 + x2) / 2; + Y = (y1 + y2) / 2; + supX = infX = X; + supY = infY = Y; + X = (x2 + x3) / 2; + Y = (y2 + y3) / 2; + infX = Min(infX, X); + supX = Max(supX, X); + infY = Min(infY, Y); + supY = Max(supY, Y); + X = (x3 + x4) / 2; + Y = (y3 + y4) / 2; + infX = Min(infX, X); + supX = Max(supX, X); + infY = Min(infY, Y); + supY = Max(supY, Y); + X = (x4 + x1) / 2; + Y = (y4 + y1) / 2; + infX = Min(infX, X); + supX = Max(supX, X); + infY = Min(infY, Y); + supY = Max(supY, Y); + CVector center; + center.x = (infX + supX) / 2; + center.y = (infY + supY) / 2; + center.z = (z <= MAP_Z_LOW_LIMIT) ? CWorld::FindGroundZForCoord(center.x, center.y) : z; + CShadows::RenderIndicatorShadow(id, 2, gpGoalTex, ¢er, supX - center.x, 0.0f, 0.0f, center.y - supY, 0); +} + +#ifdef GTA_SCRIPT_COLLECTIVE +int CTheScripts::AddPedsInVehicleToCollective(int index) +{ + int colIndex = NextFreeCollectiveIndex; + AdvanceCollectiveIndex(); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(index); + script_assert(pVehicle); + CPed* pDriver = pVehicle->pDriver; + if (pDriver && !pDriver->IsPlayer() && pDriver->CharCreatedBy != MISSION_CHAR && pDriver->m_nPedType != PEDTYPE_COP) { + int index = FindFreeSlotInCollectiveArray(); + if (index > -1) { + CollectiveArray[index].colIndex = colIndex; + CollectiveArray[index].pedIndex = CPools::GetPedPool()->GetIndex(pDriver); + } + } + for (int i = 0; i < pVehicle->m_nNumMaxPassengers; i++) { + CPed* pPassenger = pVehicle->pPassengers[i]; + if (pPassenger && !pPassenger->IsPlayer() && pPassenger->CharCreatedBy != MISSION_CHAR && pPassenger->m_nPedType != PEDTYPE_COP) { + int index = FindFreeSlotInCollectiveArray(); + if (index > -1) { + CollectiveArray[index].colIndex = colIndex; + CollectiveArray[index].pedIndex = CPools::GetPedPool()->GetIndex(pPassenger); + } + } + } + return colIndex; +} + +int CTheScripts::AddPedsInAreaToCollective(float x, float y, float z, float radius) +{ + int16 numFound; + CEntity* pEntities[64]; + int colIndex = NextFreeCollectiveIndex; + AdvanceCollectiveIndex(); + CWorld::FindObjectsInRange(CVector(x, y, z), radius, true, &numFound, 64, pEntities, false, true, true, false, false); + for (int16 i = 0; i < numFound; i++) { + if (pEntities[i]->GetType() == ENTITY_TYPE_PED) { + CPed* pPed = (CPed*)pEntities[i]; + if (pPed && !pPed->IsPlayer() && pPed->CharCreatedBy != MISSION_CHAR && pPed->m_nPedType != PEDTYPE_COP) { + int index = FindFreeSlotInCollectiveArray(); + if (index > -1) { + CollectiveArray[index].colIndex = colIndex; + CollectiveArray[index].pedIndex = CPools::GetPedPool()->GetIndex(pPed); + } + } + } + else if (pEntities[i]->GetType() == ENTITY_TYPE_VEHICLE) { + CVehicle* pVehicle = (CVehicle*)pEntities[i]; + CPed* pDriver = pVehicle->pDriver; + if (pDriver && !pDriver->IsPlayer() && pDriver->CharCreatedBy != MISSION_CHAR && pDriver->m_nPedType != PEDTYPE_COP) { + int index = FindFreeSlotInCollectiveArray(); + if (index > -1) { + CollectiveArray[index].colIndex = colIndex; + CollectiveArray[index].pedIndex = CPools::GetPedPool()->GetIndex(pDriver); + } + } + for (int i = 0; i < pVehicle->m_nNumMaxPassengers; i++) { + CPed* pPassenger = pVehicle->pPassengers[i]; + if (pPassenger && !pPassenger->IsPlayer() && pPassenger->CharCreatedBy != MISSION_CHAR && pPassenger->m_nPedType != PEDTYPE_COP) { + int index = FindFreeSlotInCollectiveArray(); + if (index > -1) { + CollectiveArray[index].colIndex = colIndex; + CollectiveArray[index].pedIndex = CPools::GetPedPool()->GetIndex(pPassenger); + } + } + } + } + } + return colIndex; +} + +int CTheScripts::FindFreeSlotInCollectiveArray() +{ + for (int i = 0; i < MAX_NUM_COLLECTIVES; i++) { + if (CollectiveArray[i].colIndex == -1) + return i; + } + return -1; +} + +void CTheScripts::SetObjectiveForAllPedsInCollective(int colIndex, eObjective objective, int16 p1, int16 p2) +{ + for (int i = 0; i < MAX_NUM_COLLECTIVES; i++) { + if (CollectiveArray[i].colIndex == colIndex) { + CPed* pPed = CPools::GetPedPool()->GetAt(CollectiveArray[i].pedIndex); + if (pPed == nil) { + CollectiveArray[i].colIndex = -1; + CollectiveArray[i].pedIndex = 0; + } + else { + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(objective, p1, p2); + } + } + } +} + +void CTheScripts::SetObjectiveForAllPedsInCollective(int colIndex, eObjective objective, CVector p1, float p2) +{ + for (int i = 0; i < MAX_NUM_COLLECTIVES; i++) { + if (CollectiveArray[i].colIndex == colIndex) { + CPed* pPed = CPools::GetPedPool()->GetAt(CollectiveArray[i].pedIndex); + if (pPed == nil) { + CollectiveArray[i].colIndex = -1; + CollectiveArray[i].pedIndex = 0; + } + else { + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(objective, p1, p2); + } + } + } +} + +void CTheScripts::SetObjectiveForAllPedsInCollective(int colIndex, eObjective objective, CVector p1) +{ + for (int i = 0; i < MAX_NUM_COLLECTIVES; i++) { + if (CollectiveArray[i].colIndex == colIndex) { + CPed* pPed = CPools::GetPedPool()->GetAt(CollectiveArray[i].pedIndex); + if (pPed == nil) { + CollectiveArray[i].colIndex = -1; + CollectiveArray[i].pedIndex = 0; + } + else { + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(objective, p1); + } + } + } +} + +void CTheScripts::SetObjectiveForAllPedsInCollective(int colIndex, eObjective objective, void* p1) +{ + for (int i = 0; i < MAX_NUM_COLLECTIVES; i++) { + if (CollectiveArray[i].colIndex == colIndex) { + CPed* pPed = CPools::GetPedPool()->GetAt(CollectiveArray[i].pedIndex); + if (pPed == nil) { + CollectiveArray[i].colIndex = -1; + CollectiveArray[i].pedIndex = 0; + } + else { + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(objective, p1); + } + } + } +} + +void CTheScripts::SetObjectiveForAllPedsInCollective(int colIndex, eObjective objective) +{ + for (int i = 0; i < MAX_NUM_COLLECTIVES; i++) { + if (CollectiveArray[i].colIndex == colIndex) { + CPed* pPed = CPools::GetPedPool()->GetAt(CollectiveArray[i].pedIndex); + if (pPed == nil) { + CollectiveArray[i].colIndex = -1; + CollectiveArray[i].pedIndex = 0; + } + else { + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(objective); + } + } + } +} +#endif //GTA_SCRIPT_COLLECTIVE + +bool CTheScripts::IsPedStopped(CPed* pPed) +{ + if (pPed->InVehicle()) + return IsVehicleStopped(pPed->m_pMyVehicle); + return (pPed->m_nMoveState == PEDMOVE_NONE || pPed->m_nMoveState == PEDMOVE_STILL) && + !pPed->bIsInTheAir && !pPed->bIsLanding && pPed->bIsStanding && pPed->m_vecAnimMoveDelta.x == 0.0f && pPed->m_vecAnimMoveDelta.y == 0.0f; +} + +bool CTheScripts::IsPlayerStopped(CPlayerInfo* pPlayer) +{ + CPed* pPed = pPlayer->m_pPed; + if (pPed->InVehicle()) + return IsVehicleStopped(pPed->m_pMyVehicle); + if (RpAnimBlendClumpGetAssociation(pPed->GetClump(), ANIM_STD_RUNSTOP1) || + RpAnimBlendClumpGetAssociation(pPed->GetClump(), ANIM_STD_RUNSTOP2) || + RpAnimBlendClumpGetAssociation(pPed->GetClump(), ANIM_STD_JUMP_LAUNCH) || + RpAnimBlendClumpGetAssociation(pPed->GetClump(), ANIM_STD_JUMP_GLIDE)) + return false; + return (pPed->m_nMoveState == PEDMOVE_NONE || pPed->m_nMoveState == PEDMOVE_STILL) && + !pPed->bIsInTheAir && !pPed->bIsLanding && pPed->bIsStanding && pPed->m_vecAnimMoveDelta.x == 0.0f && pPed->m_vecAnimMoveDelta.y == 0.0f; +} + +bool CTheScripts::IsVehicleStopped(CVehicle* pVehicle) +{ + return 0.01f * CTimer::GetTimeStep() >= pVehicle->m_fDistanceTravelled; +} + +void CTheScripts::RemoveThisPed(CPed* pPed) +{ + if (pPed) { + bool bWasMissionPed = pPed->CharCreatedBy == MISSION_CHAR; + if (pPed->InVehicle() && pPed->m_pMyVehicle) { + if (pPed->m_pMyVehicle->pDriver == pPed) { + pPed->m_pMyVehicle->RemoveDriver(); + pPed->m_pMyVehicle->SetStatus(STATUS_ABANDONED); + if (pPed->m_pMyVehicle->m_nDoorLock == CARLOCK_LOCKED_INITIALLY) + pPed->m_pMyVehicle->m_nDoorLock = CARLOCK_UNLOCKED; + if (pPed->m_nPedType == PEDTYPE_COP && pPed->m_pMyVehicle->IsLawEnforcementVehicle()) + pPed->m_pMyVehicle->ChangeLawEnforcerState(0); + } + else { + pPed->m_pMyVehicle->RemovePassenger(pPed); + } + } + CWorld::RemoveReferencesToDeletedObject(pPed); + delete pPed; + if (bWasMissionPed) + --CPopulation::ms_nTotalMissionPeds; + } +} + +void CTheScripts::CleanUpThisPed(CPed* pPed) +{ + if (!pPed) + return; + if (pPed->CharCreatedBy != MISSION_CHAR) + return; + pPed->CharCreatedBy = RANDOM_CHAR; + if (pPed->m_nPedType == PEDTYPE_PROSTITUTE) + pPed->m_objectiveTimer = CTimer::GetTimeInMilliseconds() + 30000; + if (pPed->InVehicle()) { + if (pPed->m_pMyVehicle->pDriver == pPed) { + if (pPed->m_pMyVehicle->m_vehType == VEHICLE_TYPE_CAR) { + CCarCtrl::JoinCarWithRoadSystem(pPed->m_pMyVehicle); + pPed->m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; + } + } + else { + if (pPed->m_pMyVehicle->m_vehType == VEHICLE_TYPE_CAR) { + pPed->SetObjective(OBJECTIVE_LEAVE_CAR, pPed->m_pMyVehicle); + pPed->bWanderPathAfterExitingCar = true; + } + } + } + bool flees = false; + PedState state; + eMoveState ms; + if (pPed->m_nPedState == PED_FLEE_ENTITY || pPed->m_nPedState == PED_FLEE_POS) { + ms = pPed->m_nMoveState; + state = pPed->m_nPedState; + flees = true; + } + pPed->ClearObjective(); + pPed->bRespondsToThreats = true; + pPed->bScriptObjectiveCompleted = false; + pPed->bKindaStayInSamePlace = false; + pPed->ClearLeader(); + if (pPed->IsPedInControl()) + pPed->SetWanderPath(CGeneral::GetRandomNumber() & 7); + if (flees) { + pPed->SetPedState(state); + pPed->SetMoveState(ms); + } + --CPopulation::ms_nTotalMissionPeds; +} + +void CTheScripts::CleanUpThisVehicle(CVehicle* pVehicle) +{ + if (!pVehicle) + return; + if (pVehicle->VehicleCreatedBy != MISSION_VEHICLE) + return; + pVehicle->bIsLocked = false; + CCarCtrl::RemoveFromInterestingVehicleList(pVehicle); + pVehicle->VehicleCreatedBy = RANDOM_VEHICLE; + ++CCarCtrl::NumRandomCars; + --CCarCtrl::NumMissionCars; +} + +void CTheScripts::CleanUpThisObject(CObject* pObject) +{ + if (!pObject) + return; + if (pObject->ObjectCreatedBy != MISSION_OBJECT) + return; + pObject->ObjectCreatedBy = TEMP_OBJECT; + pObject->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 20000000; + pObject->m_nRefModelIndex = -1; + pObject->bUseVehicleColours = false; + ++CObject::nNoTempObjects; +} + +void CTheScripts::ReadObjectNamesFromScript() +{ + int32 varSpace = GetSizeOfVariableSpace(); + uint32 ip = varSpace + 8; + NumberOfUsedObjects = Read2BytesFromScript(&ip); + ip += 2; + for (uint16 i = 0; i < NumberOfUsedObjects; i++) { + for (int j = 0; j < USED_OBJECT_NAME_LENGTH; j++) + UsedObjectArray[i].name[j] = ScriptSpace[ip++]; + UsedObjectArray[i].index = 0; + } +} + +void CTheScripts::UpdateObjectIndices() +{ + char name[USED_OBJECT_NAME_LENGTH]; + char error[112]; + for (int i = 1; i < NumberOfUsedObjects; i++) { + bool found = false; + for (int j = 0; j < MODELINFOSIZE && !found; j++) { + CBaseModelInfo* pModel = CModelInfo::GetModelInfo(j); + if (!pModel) + continue; + strcpy(name, pModel->GetModelName()); +#ifdef FIX_BUGS + for (int k = 0; k < USED_OBJECT_NAME_LENGTH && name[k]; k++) +#else + for (int k = 0; k < USED_OBJECT_NAME_LENGTH; k++) +#endif + name[k] = toupper(name[k]); + if (strcmp(name, UsedObjectArray[i].name) == 0) { + found = true; + UsedObjectArray[i].index = j; + } + } + if (!found) { + sprintf(error, "CTheScripts::UpdateObjectIndices - Couldn't find %s", UsedObjectArray[i].name); + debug("%s\n", error); + } + } +} + +void CTheScripts::ReadMultiScriptFileOffsetsFromScript() +{ + int32 varSpace = GetSizeOfVariableSpace(); + uint32 ip = varSpace + 3; + int32 objectSize = Read4BytesFromScript(&ip); + ip = objectSize + 8; + MainScriptSize = Read4BytesFromScript(&ip); + LargestMissionScriptSize = Read4BytesFromScript(&ip); + NumberOfMissionScripts = Read2BytesFromScript(&ip); + NumberOfExclusiveMissionScripts = Read2BytesFromScript(&ip); + for (int i = 0; i < NumberOfMissionScripts; i++) { + MultiScriptArray[i] = Read4BytesFromScript(&ip); + } +} diff --git a/src/miami/control/Script6.cpp b/src/miami/control/Script6.cpp new file mode 100644 index 00000000..8af32f57 --- /dev/null +++ b/src/miami/control/Script6.cpp @@ -0,0 +1,1814 @@ +#include "common.h" + +#include "Script.h" +#include "ScriptCommands.h" + +#include "Bike.h" +#include "CarCtrl.h" +#include "Cranes.h" +#include "Credits.h" +#include "CutsceneMgr.h" +#include "DMAudio.h" +#include "FileMgr.h" +#include "Fire.h" +#include "Frontend.h" +#include "Garages.h" +#include "General.h" +#ifdef MISSION_REPLAY +#include "GenericGameStorage.h" +#endif +#include "Messages.h" +#include "Pad.h" +#include "Particle.h" +#include "Phones.h" +#include "Population.h" +#include "Pools.h" +#include "Record.h" +#include "Remote.h" +#include "Restart.h" +#include "SpecialFX.h" +#include "Stats.h" +#include "Streaming.h" +#include "Weather.h" +#include "Zones.h" +#include "main.h" +#include "GameLogic.h" +#include "Sprite.h" +#include "CarAI.h" +#include "Pickups.h" +#include "Fluff.h" + +#ifdef USE_DEBUG_SCRIPT_LOADER +extern const char* scriptfile; +#endif + +bool CRunningScript::ThisIsAValidRandomCop(uint32 mi, int cop, int swat, int fbi, int army, int miami) +{ + switch (mi) + { + case MI_COP: if (cop) return true; break; + case MI_SWAT: if (swat) return true; break; + case MI_FBI: if (fbi) return true; break; + case MI_ARMY: if (army) return true; break; + default: if (mi >= MI_VICE1 && mi <= MI_VICE8 && miami) return true; break; + } + return false; +} + +bool CRunningScript::ThisIsAValidRandomPed(uint32 pedtype, int civ, int gang, int criminal) +{ + switch (pedtype) { + case PEDTYPE_CIVMALE: + case PEDTYPE_CIVFEMALE: + return civ; + case PEDTYPE_GANG1: + case PEDTYPE_GANG2: + case PEDTYPE_GANG3: + case PEDTYPE_GANG4: + case PEDTYPE_GANG5: + case PEDTYPE_GANG6: + case PEDTYPE_GANG7: + case PEDTYPE_GANG8: + case PEDTYPE_GANG9: + return gang; + case PEDTYPE_CRIMINAL: + case PEDTYPE_PROSTITUTE: + return criminal; + default: + return false; + } +} + +int8 CRunningScript::ProcessCommands1000To1099(int32 command) +{ + switch (command) { + //case COMMAND_FLASH_RADAR_BLIP: + /* + case COMMAND_IS_CHAR_IN_CONTROL: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + UpdateCompareFlag(pPed->IsPedInControl()); + return 0; + } + */ + case COMMAND_SET_GENERATE_CARS_AROUND_CAMERA: + CollectParameters(&m_nIp, 1); + CCarCtrl::bCarsGeneratedAroundCamera = (ScriptParams[0] != 0); + return 0; + case COMMAND_CLEAR_SMALL_PRINTS: + CMessages::ClearSmallMessagesOnly(); + return 0; + /* + case COMMAND_HAS_MILITARY_CRANE_COLLECTED_ALL_CARS: + UpdateCompareFlag(CCranes::HaveAllCarsBeenCollectedByMilitaryCrane()); + return 0; + */ + case COMMAND_SET_UPSIDEDOWN_CAR_NOT_DAMAGED: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + script_assert(pVehicle->m_vehType == VEHICLE_TYPE_CAR); + CAutomobile* pCar = (CAutomobile*)pVehicle; + pCar->bNotDamagedUpsideDown = (ScriptParams[1] != 0); + return 0; + } + case COMMAND_CAN_PLAYER_START_MISSION: + { + CollectParameters(&m_nIp, 1); + CPlayerPed* pPlayerPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPlayerPed); + UpdateCompareFlag(pPlayerPed->IsPedInControl() || pPlayerPed->m_nPedState == PED_DRIVING); + return 0; + } + case COMMAND_MAKE_PLAYER_SAFE_FOR_CUTSCENE: + { + CollectParameters(&m_nIp, 1); + CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; + CPad::GetPad(ScriptParams[0])->SetDisablePlayerControls(PLAYERCONTROL_CUTSCENE); + pPlayerInfo->MakePlayerSafe(true); + CCutsceneMgr::StartCutsceneProcessing(); + return 0; + } + case COMMAND_USE_TEXT_COMMANDS: + CollectParameters(&m_nIp, 1); + CTheScripts::UseTextCommands = (ScriptParams[0] != 0) ? 2 : 1; + return 0; + case COMMAND_SET_THREAT_FOR_PED_TYPE: + CollectParameters(&m_nIp, 2); + CPedType::AddThreat(ScriptParams[0], ScriptParams[1]); + return 0; + case COMMAND_CLEAR_THREAT_FOR_PED_TYPE: + CollectParameters(&m_nIp, 2); + CPedType::RemoveThreat(ScriptParams[0], ScriptParams[1]); + return 0; + case COMMAND_GET_CAR_COLOURS: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + ScriptParams[0] = pVehicle->m_currentColour1; + ScriptParams[1] = pVehicle->m_currentColour2; + StoreParameters(&m_nIp, 2); + return 0; + } + case COMMAND_SET_ALL_CARS_CAN_BE_DAMAGED: + CollectParameters(&m_nIp, 1); + CWorld::SetAllCarsCanBeDamaged(ScriptParams[0] != 0); + if (!ScriptParams[0]) + CWorld::ExtinguishAllCarFiresInArea(FindPlayerCoors(), 4000.0f); + return 0; + case COMMAND_SET_CAR_CAN_BE_DAMAGED: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + pVehicle->bCanBeDamaged = ScriptParams[1] != 0; + if (!ScriptParams[1]) + pVehicle->ExtinguishCarFire(); + return 0; + } + //case COMMAND_MAKE_PLAYER_UNSAFE: + /* + case COMMAND_LOAD_COLLISION: + { + CollectParameters(&m_nIp, 1); + CTimer::Stop(); + CGame::currLevel = (eLevelName)ScriptParams[0]; + ISLAND_LOADING_IS(LOW) + { + CStreaming::RemoveUnusedBigBuildings(CGame::currLevel); + CStreaming::RemoveUnusedBuildings(CGame::currLevel); + } + CCollision::SortOutCollisionAfterLoad(); + ISLAND_LOADING_ISNT(HIGH) + { + CStreaming::RequestIslands(CGame::currLevel); + CStreaming::LoadAllRequestedModels(true); + } + CTimer::Update(); + return 0; + } + case COMMAND_GET_BODY_CAST_HEALTH: + // ScriptParams[0] = CObject::nBodyCastHealth; + // StoreParameters(&m_nIp, 1); + return 0; + */ + case COMMAND_SET_CHARS_CHATTING: + { + CollectParameters(&m_nIp, 3); + CPed* pPed1 = CPools::GetPedPool()->GetAt(ScriptParams[0]); + CPed* pPed2 = CPools::GetPedPool()->GetAt(ScriptParams[1]); + script_assert(pPed1 && pPed2); + pPed1->SetChat(pPed2, ScriptParams[2]); + pPed2->SetChat(pPed1, ScriptParams[2]); + return 0; + } + //case COMMAND_MAKE_PLAYER_SAFE: + /* + case COMMAND_SET_CAR_STAYS_IN_CURRENT_LEVEL: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + if (ScriptParams[1]) + pVehicle->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pVehicle->GetPosition()); + else + pVehicle->m_nZoneLevel = LEVEL_GENERIC; + return 0; + } + case COMMAND_SET_CHAR_STAYS_IN_CURRENT_LEVEL: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + if (ScriptParams[1]) + pPed->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pPed->GetPosition()); + else + pPed->m_nZoneLevel = LEVEL_GENERIC; + return 0; + } + */ + case COMMAND_SET_DRUNK_INPUT_DELAY: + { + CollectParameters(&m_nIp, 2); + assert(ScriptParams[1] < CPad::DRUNK_STEERING_BUFFER_SIZE); + CPad::GetPad(ScriptParams[0])->SetDrunkInputDelay(ScriptParams[1]); + return 0; + } + case COMMAND_SET_CHAR_MONEY: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->m_nPedMoney = ScriptParams[1]; + pPed->bMoneyHasBeenGivenByScript = true; + return 0; + } + //case COMMAND_INCREASE_CHAR_MONEY: + case COMMAND_GET_OFFSET_FROM_OBJECT_IN_WORLD_COORDS: + { + CollectParameters(&m_nIp, 4); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + CVector result = Multiply3x3(pObject->GetMatrix(), *(CVector*)&ScriptParams[1]) + pObject->GetPosition(); + *(CVector*)&ScriptParams[0] = result; + StoreParameters(&m_nIp, 3); + return 0; + } + case COMMAND_REGISTER_LIFE_SAVED: + CStats::AnotherLifeSavedWithAmbulance(); + return 0; + case COMMAND_REGISTER_CRIMINAL_CAUGHT: + CStats::AnotherCriminalCaught(); + return 0; + case COMMAND_REGISTER_AMBULANCE_LEVEL: + CollectParameters(&m_nIp, 1); + CStats::RegisterLevelAmbulanceMission(ScriptParams[0]); + return 0; + case COMMAND_REGISTER_FIRE_EXTINGUISHED: + CStats::AnotherFireExtinguished(); + return 0; + case COMMAND_TURN_PHONE_ON: + CollectParameters(&m_nIp, 1); + gPhoneInfo.m_aPhones[ScriptParams[0]].m_nState = PHONE_STATE_9; + return 0; + /* + case COMMAND_REGISTER_LONGEST_DODO_FLIGHT: + CollectParameters(&m_nIp, 1); + CStats::RegisterLongestFlightInDodo(ScriptParams[0]); + return 0; + */ + case COMMAND_GET_OFFSET_FROM_CAR_IN_WORLD_COORDS: + { + CollectParameters(&m_nIp, 4); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + CVector result = Multiply3x3(pVehicle->GetMatrix(), *(CVector*)&ScriptParams[1]) + pVehicle->GetPosition(); + *(CVector*)&ScriptParams[0] = result; + StoreParameters(&m_nIp, 3); + return 0; + } + case COMMAND_SET_TOTAL_NUMBER_OF_KILL_FRENZIES: + CollectParameters(&m_nIp, 1); + CStats::SetTotalNumberKillFrenzies(ScriptParams[0]); + return 0; + case COMMAND_BLOW_UP_RC_BUGGY: + CWorld::Players[CWorld::PlayerInFocus].BlowUpRCBuggy(true); + return 0; + /* + case COMMAND_REMOVE_CAR_FROM_CHASE: + CollectParameters(&m_nIp, 1); + CRecordDataForChase::RemoveCarFromChase(ScriptParams[0]); + return 0; + */ + case COMMAND_IS_FRENCH_GAME: + UpdateCompareFlag(CGame::frenchGame); + return 0; + case COMMAND_IS_GERMAN_GAME: + UpdateCompareFlag(CGame::germanGame); + return 0; + case COMMAND_CLEAR_MISSION_AUDIO: + CollectParameters(&m_nIp, 1); + DMAudio.ClearMissionAudio(ScriptParams[0] - 1); + return 0; + /* + case COMMAND_SET_FADE_IN_AFTER_NEXT_ARREST: + CollectParameters(&m_nIp, 1); + CRestart::bFadeInAfterNextArrest = !!ScriptParams[0]; + return 0; + case COMMAND_SET_FADE_IN_AFTER_NEXT_DEATH: + CollectParameters(&m_nIp, 1); + CRestart::bFadeInAfterNextDeath = !!ScriptParams[0]; + return 0; + case COMMAND_SET_GANG_PED_MODEL_PREFERENCE: + CollectParameters(&m_nIp, 2); + CGangs::SetGangPedModelOverride(ScriptParams[0], ScriptParams[1]); + return 0; + */ + case COMMAND_SET_CHAR_USE_PEDNODE_SEEK: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + if (ScriptParams[1]) + pPed->m_pNextPathNode = nil; + pPed->bUsePedNodeSeek = !!ScriptParams[1]; + return 0; + } + /* + case COMMAND_SWITCH_VEHICLE_WEAPONS: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->bGunSwitchedOff = !ScriptParams[1]; + return 0; + } + case COMMAND_SET_GET_OUT_OF_JAIL_FREE: + CollectParameters(&m_nIp, 2); + CWorld::Players[ScriptParams[0]].m_bGetOutOfJailFree = !!ScriptParams[1]; + return 0; + */ + case COMMAND_SET_FREE_HEALTH_CARE: + CollectParameters(&m_nIp, 2); + CWorld::Players[ScriptParams[0]].m_bGetOutOfHospitalFree = !!ScriptParams[1]; + return 0; + /* + case COMMAND_IS_CAR_DOOR_CLOSED: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + UpdateCompareFlag(!pVehicle->IsDoorMissing((eDoors)ScriptParams[1]) && pVehicle->IsDoorClosed((eDoors)ScriptParams[1])); + return 0; + } + */ + case COMMAND_LOAD_AND_LAUNCH_MISSION: + return 0; + case COMMAND_LOAD_AND_LAUNCH_MISSION_INTERNAL: + { +#ifdef USE_MISSION_REPLAY_OVERRIDE_FOR_NON_MOBILE_SCRIPT + uint32 oldIp = m_nIp; +#endif + CollectParameters(&m_nIp, 1); + + if (CTheScripts::NumberOfExclusiveMissionScripts > 0 && ScriptParams[0] <= UINT16_MAX - 2) + return 0; +#ifdef MISSION_REPLAY + missionRetryScriptIndex = ScriptParams[0]; +#ifdef USE_MISSION_REPLAY_OVERRIDE_FOR_NON_MOBILE_SCRIPT + if (!UsingMobileScript && CTheScripts::MissionSupportsMissionReplay(missionRetryScriptIndex)){ + if (!AlreadySavedGame) { + m_nIp = oldIp - 2; + SaveGameForPause(SAVE_TYPE_QUICKSAVE_FOR_SCRIPT); + AlreadySavedGame = true; + return 0; + } + else { + AlreadySavedGame = false; + } + } +#endif +#endif + CTimer::Suspend(); + int offset = CTheScripts::MultiScriptArray[ScriptParams[0]]; +#ifdef USE_DEBUG_SCRIPT_LOADER + int handle = CTheScripts::OpenScript(); +#else + CFileMgr::ChangeDir("\\"); + int handle = CFileMgr::OpenFile("data\\main.scm", "rb"); +#endif + CFileMgr::Seek(handle, offset, 0); + CFileMgr::Read(handle, (const char*)&CTheScripts::ScriptSpace[SIZE_MAIN_SCRIPT], SIZE_MISSION_SCRIPT); + CFileMgr::CloseFile(handle); + CRunningScript* pMissionScript = CTheScripts::StartNewScript(SIZE_MAIN_SCRIPT); + CTimer::Resume(); + pMissionScript->m_bIsMissionScript = true; + pMissionScript->m_bMissionFlag = true; + CTheScripts::bAlreadyRunningAMissionScript = true; + CGameLogic::ClearShortCut(); + return 0; + } + case COMMAND_SET_OBJECT_DRAW_LAST: + { + CollectParameters(&m_nIp, 2); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + pObject->bDrawLast = !!ScriptParams[1]; + return 0; + } + case COMMAND_GET_AMMO_IN_PLAYER_WEAPON: + { + CollectParameters(&m_nIp, 2); + CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + ScriptParams[0] = 0; + for (int i = 0; i < TOTAL_WEAPON_SLOTS; i++) { + if (pPed->GetWeapon(i).m_eWeaponType == (eWeaponType)ScriptParams[1]) + ScriptParams[0] = pPed->GetWeapon(i).m_nAmmoTotal; + } + StoreParameters(&m_nIp, 1); + return 0; + } + /* + case COMMAND_GET_AMMO_IN_CHAR_WEAPON: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CWeapon* pWeaponSlot = &pPed->m_weapons[ScriptParams[1]]; + if (pWeaponSlot->m_eWeaponType == (eWeaponType)ScriptParams[1]) + ScriptParams[0] = pWeaponSlot->m_nAmmoTotal; + else + ScriptParams[0] = 0; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_REGISTER_KILL_FRENZY_PASSED: + CStats::AnotherKillFrenzyPassed(); + return 0; + case COMMAND_SET_CHAR_SAY: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + switch (ScriptParams[1]) { + case SCRIPT_SOUND_CHUNKY_RUN_SHOUT: + pPed->Say(SOUND_PED_FLEE_RUN); + break; + case SCRIPT_SOUND_SECURITY_GUARD_AWAY_SHOUT: + pPed->Say(SOUND_PED_FLEE_RUN); + break; + case SCRIPT_SOUND_SWAT_PED_SHOUT: + pPed->Say(SOUND_PED_PURSUIT_SWAT); + break; + case SCRIPT_SOUND_AMMUNATION_CHAT_1: + pPed->Say(SOUND_AMMUNATION_WELCOME_1); + break; + case SCRIPT_SOUND_AMMUNATION_CHAT_2: + pPed->Say(SOUND_AMMUNATION_WELCOME_2); + break; + case SCRIPT_SOUND_AMMUNATION_CHAT_3: + pPed->Say(SOUND_AMMUNATION_WELCOME_3); + break; + default: + break; + } + return 0; + } + */ + case COMMAND_SET_NEAR_CLIP: + CollectParameters(&m_nIp, 1); + TheCamera.SetNearClipScript(*(float*)&ScriptParams[0]); + return 0; + case COMMAND_SET_RADIO_CHANNEL: + CollectParameters(&m_nIp, 2); + DMAudio.SetRadioChannel(ScriptParams[0], ScriptParams[1]); + return 0; + /* + case COMMAND_OVERRIDE_HOSPITAL_LEVEL: + CollectParameters(&m_nIp, 1); + CRestart::OverrideHospitalLevel = ScriptParams[0]; + return 0; + case COMMAND_OVERRIDE_POLICE_STATION_LEVEL: + CollectParameters(&m_nIp, 1); + CRestart::OverridePoliceStationLevel = ScriptParams[0]; + return 0; + case COMMAND_FORCE_RAIN: + CollectParameters(&m_nIp, 1); + CWeather::bScriptsForceRain = !!ScriptParams[0]; + return 0; + case COMMAND_DOES_GARAGE_CONTAIN_CAR: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + script_assert(pVehicle); + UpdateCompareFlag(CGarages::IsThisCarWithinGarageArea(ScriptParams[0], pVehicle)); + return 0; + } + */ + case COMMAND_SET_CAR_TRACTION: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + float fTraction = *(float*)&ScriptParams[1]; + script_assert(pVehicle->m_vehType == VEHICLE_TYPE_CAR || pVehicle->m_vehType == VEHICLE_TYPE_BIKE); + if (pVehicle->m_vehType == VEHICLE_TYPE_CAR) + ((CAutomobile*)pVehicle)->m_fTraction = fTraction; + else + ((CBike*)pVehicle)->m_fTraction = fTraction; + return 0; + } + case COMMAND_ARE_MEASUREMENTS_IN_METRES: +#ifdef USE_MEASUREMENTS_IN_METERS + UpdateCompareFlag(true); +#else + UpdateCompareFlag(false); +#endif + return 0; + case COMMAND_CONVERT_METRES_TO_FEET: + { + CollectParameters(&m_nIp, 1); + float fMeterValue = *(float*)&ScriptParams[0]; + float fFeetValue = fMeterValue / METERS_IN_FOOT; + *(float*)&ScriptParams[0] = fFeetValue; + StoreParameters(&m_nIp, 1); + return 0; + } + /* + case COMMAND_MARK_ROADS_BETWEEN_LEVELS: + { + CollectParameters(&m_nIp, 6); + float infX = *(float*)&ScriptParams[0]; + float infY = *(float*)&ScriptParams[1]; + float infZ = *(float*)&ScriptParams[2]; + float supX = *(float*)&ScriptParams[3]; + float supY = *(float*)&ScriptParams[4]; + float supZ = *(float*)&ScriptParams[5]; + if (infX > supX) { + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[0]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[1]; + } + if (infZ > supZ) { + infZ = *(float*)&ScriptParams[5]; + supZ = *(float*)&ScriptParams[2]; + } + ThePaths.MarkRoadsBetweenLevelsInArea(infX, supX, infY, supY, infZ, supZ); + return 0; + } + case COMMAND_MARK_PED_ROADS_BETWEEN_LEVELS: + { + CollectParameters(&m_nIp, 6); + float infX = *(float*)&ScriptParams[0]; + float infY = *(float*)&ScriptParams[1]; + float infZ = *(float*)&ScriptParams[2]; + float supX = *(float*)&ScriptParams[3]; + float supY = *(float*)&ScriptParams[4]; + float supZ = *(float*)&ScriptParams[5]; + if (infX > supX) { + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[0]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[1]; + } + if (infZ > supZ) { + infZ = *(float*)&ScriptParams[5]; + supZ = *(float*)&ScriptParams[2]; + } + ThePaths.PedMarkRoadsBetweenLevelsInArea(infX, supX, infY, supY, infZ, supZ); + return 0; + } + */ + case COMMAND_SET_CAR_AVOID_LEVEL_TRANSITIONS: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->AutoPilot.m_bStayInCurrentLevel = !!ScriptParams[1]; + return 0; + } + /* + case COMMAND_SET_CHAR_AVOID_LEVEL_TRANSITIONS: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); + script_assert(pPed); + // not implemented + return 0; + } + case COMMAND_IS_THREAT_FOR_PED_TYPE: + CollectParameters(&m_nIp, 2); + UpdateCompareFlag(CPedType::IsThreat(ScriptParams[0], ScriptParams[1])); + return 0; + */ + case COMMAND_CLEAR_AREA_OF_CHARS: + { + CollectParameters(&m_nIp, 6); + float infX = *(float*)&ScriptParams[0]; + float infY = *(float*)&ScriptParams[1]; + float infZ = *(float*)&ScriptParams[2]; + float supX = *(float*)&ScriptParams[3]; + float supY = *(float*)&ScriptParams[4]; + float supZ = *(float*)&ScriptParams[5]; + if (infX > supX) { + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[0]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[1]; + } + if (infZ > supZ) { + infZ = *(float*)&ScriptParams[5]; + supZ = *(float*)&ScriptParams[2]; + } + CWorld::ClearPedsFromArea(infX, infY, infZ, supX, supY, supZ); + return 0; + } + case COMMAND_SET_TOTAL_NUMBER_OF_MISSIONS: + CollectParameters(&m_nIp, 1); + CStats::SetTotalNumberMissions(CGame::germanGame ? ScriptParams[0] - 2 : ScriptParams[0]); + return 0; + case COMMAND_CONVERT_METRES_TO_FEET_INT: + CollectParameters(&m_nIp, 1); + ScriptParams[0] *= FEET_IN_METER; + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_REGISTER_FASTEST_TIME: + CollectParameters(&m_nIp, 2); + CStats::RegisterFastestTime(ScriptParams[0], ScriptParams[1]); + return 0; + case COMMAND_REGISTER_HIGHEST_SCORE: + CollectParameters(&m_nIp, 2); + CStats::RegisterHighestScore(ScriptParams[0], ScriptParams[1]); + return 0; + //case COMMAND_WARP_CHAR_INTO_CAR_AS_PASSENGER: + case COMMAND_IS_CAR_PASSENGER_SEAT_FREE: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + UpdateCompareFlag(ScriptParams[1] < pVehicle->m_nNumMaxPassengers && pVehicle->pPassengers[ScriptParams[1]] == nil); + return 0; + } + /* + case COMMAND_GET_CHAR_IN_CAR_PASSENGER_SEAT: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + script_assert(ScriptParams[1] >= 0 && ScriptParams[1] < ARRAY_SIZE(pVehicle->pPassengers)); + CPed* pPassenger = pVehicle->pPassengers[ScriptParams[1]]; + ScriptParams[0] = CPools::GetPedPool()->GetIndex(pPassenger); + StoreParameters(&m_nIp, 1); + return 0; + } + */ + case COMMAND_SET_CHAR_IS_CHRIS_CRIMINAL: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bChrisCriminal = !!ScriptParams[1]; + return 0; + } + case COMMAND_START_CREDITS: + CCredits::Start(); + return 0; + case COMMAND_STOP_CREDITS: + CCredits::Stop(); + return 0; + case COMMAND_ARE_CREDITS_FINISHED: + UpdateCompareFlag(CCredits::AreCreditsDone()); + return 0; + case COMMAND_CREATE_SINGLE_PARTICLE: + CollectParameters(&m_nIp, 8); + CParticle::AddParticle((tParticleType)ScriptParams[0], *(CVector*)&ScriptParams[1], + *(CVector*)&ScriptParams[4], nil, *(float*)&ScriptParams[7], 0, 0, 0, 0); + return 0; + /* + case COMMAND_SET_CHAR_IGNORE_LEVEL_TRANSITIONS: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + if (ScriptParams[1]) + pPed->m_nZoneLevel = LEVEL_IGNORE; + else + pPed->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pPed->GetPosition()); + return 0; + } + case COMMAND_GET_CHASE_CAR: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CRecordDataForChase::TurnChaseCarIntoScriptCar(ScriptParams[0]); + ScriptParams[0] = CPools::GetVehiclePool()->GetIndex(pVehicle); + StoreParameters(&m_nIp, 1); + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.AddEntityToList(ScriptParams[0], CLEANUP_CAR); + return 0; + } + case COMMAND_START_BOAT_FOAM_ANIMATION: + CSpecialParticleStuff::StartBoatFoamAnimation(); + return 0; + case COMMAND_UPDATE_BOAT_FOAM_ANIMATION: + { + CollectParameters(&m_nIp, 1); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + CSpecialParticleStuff::UpdateBoatFoamAnimation(&pObject->GetMatrix()); + return 0; + } + */ + case COMMAND_SET_MUSIC_DOES_FADE: + CollectParameters(&m_nIp, 1); + TheCamera.m_bIgnoreFadingStuffForMusic = (ScriptParams[0] == 0); + return 0; + /* + case COMMAND_SET_INTRO_IS_PLAYING: + CollectParameters(&m_nIp, 1); + if (ScriptParams[0]) { + CGame::playingIntro = true; + CStreaming::RemoveCurrentZonesModels(); + } else { + CGame::playingIntro = false; + DMAudio.ChangeMusicMode(MUSICMODE_GAME); + int mi; + CModelInfo::GetModelInfo("bridgefukb", &mi); + CStreaming::RequestModel(mi, STREAMFLAGS_DEPENDENCY); + CStreaming::LoadAllRequestedModels(false); + } + return 0; + */ + case COMMAND_SET_PLAYER_HOOKER: + { + CollectParameters(&m_nIp, 2); + CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; + if (ScriptParams[1] < 0) { + pPlayerInfo->m_pHooker = nil; + pPlayerInfo->m_nNextSexFrequencyUpdateTime = 0; + pPlayerInfo->m_nNextSexMoneyUpdateTime = 0; + } else { + CPed* pHooker = CPools::GetPedPool()->GetAt(ScriptParams[1]); + script_assert(pHooker); + pPlayerInfo->m_pHooker = (CCivilianPed*)pHooker; + pPlayerInfo->m_nSexFrequency = 1000; + pPlayerInfo->m_nNextSexFrequencyUpdateTime = CTimer::GetTimeInMilliseconds() + 1000; + pPlayerInfo->m_nNextSexMoneyUpdateTime = CTimer::GetTimeInMilliseconds() + 3000; + } + return 0; + } + case COMMAND_PLAY_END_OF_GAME_TUNE: + DMAudio.PlayPreloadedCutSceneMusic(); + return 0; + case COMMAND_STOP_END_OF_GAME_TUNE: + DMAudio.StopCutSceneMusic(); + DMAudio.ChangeMusicMode(MUSICMODE_GAME); + return 0; + case COMMAND_GET_CAR_MODEL: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + ScriptParams[0] = pVehicle->GetModelIndex(); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_IS_PLAYER_SITTING_IN_CAR: + { + CollectParameters(&m_nIp, 2); + CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + UpdateCompareFlag(pPed->GetPedState() == PED_DRIVING && pPed->m_objective != OBJECTIVE_LEAVE_CAR && pPed->m_pMyVehicle == pVehicle); + return 0; + } + case COMMAND_IS_PLAYER_SITTING_IN_ANY_CAR: + { + CollectParameters(&m_nIp, 1); + CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + UpdateCompareFlag(pPed->GetPedState() == PED_DRIVING && pPed->m_objective != OBJECTIVE_LEAVE_CAR); + return 0; + } + /* + case COMMAND_SET_SCRIPT_FIRE_AUDIO: + CollectParameters(&m_nIp, 2); + gFireManager.SetScriptFireAudio(ScriptParams[0], !!ScriptParams[1]); + return 0; + */ + case COMMAND_ARE_ANY_CAR_CHEATS_ACTIVATED: + UpdateCompareFlag(CVehicle::bAllDodosCheat || CVehicle::bCheat3 || CVehicle::bHoverCheat || CVehicle::bCheat8 || CVehicle::bCheat9); + return 0; + case COMMAND_SET_CHAR_SUFFERS_CRITICAL_HITS: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bNoCriticalHits = (ScriptParams[1] == 0); + return 0; + } + /* + case COMMAND_IS_PLAYER_LIFTING_A_PHONE: + { + CollectParameters(&m_nIp, 1); + CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + UpdateCompareFlag(pPed->GetPedState() == PED_MAKE_CALL); + return 0; + } + */ + case COMMAND_IS_CHAR_SITTING_IN_CAR: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + script_assert(pVehicle); + UpdateCompareFlag(pPed->GetPedState() == PED_DRIVING && pPed->m_objective != OBJECTIVE_LEAVE_CAR && pPed->m_pMyVehicle == pVehicle); + return 0; + } + case COMMAND_IS_CHAR_SITTING_IN_ANY_CAR: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(pPed->GetPedState() == PED_DRIVING && pPed->m_objective != OBJECTIVE_LEAVE_CAR); + return 0; + } + case COMMAND_IS_PLAYER_ON_FOOT: + { + CollectParameters(&m_nIp, 1); + CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + UpdateCompareFlag(!pPed->bInVehicle && pPed->m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER && + pPed->m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER); + return 0; + } + case COMMAND_IS_CHAR_ON_FOOT: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(!pPed->bInVehicle && pPed->m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER && + pPed->m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER); + return 0; + } + default: + script_assert(0); + } + return -1; +} + +int8 CRunningScript::ProcessCommands1100To1199(int32 command) +{ + char tmp[48]; + switch (command) { + /* + case COMMAND_LOAD_COLLISION_WITH_SCREEN: + CollectParameters(&m_nIp, 1); + CTimer::Stop(); + CGame::currLevel = (eLevelName)ScriptParams[0]; + if (CGame::currLevel != CCollision::ms_collisionInMemory) { + ISLAND_LOADING_IS(LOW) + { + DMAudio.SetEffectsFadeVol(0); + CPad::StopPadsShaking(); + CCollision::LoadCollisionScreen(CGame::currLevel); + DMAudio.Service(); + } + CPopulation::DealWithZoneChange(CCollision::ms_collisionInMemory, CGame::currLevel, false); + + ISLAND_LOADING_IS(LOW) + { + CStreaming::RemoveUnusedBigBuildings(CGame::currLevel); + CStreaming::RemoveUnusedBuildings(CGame::currLevel); + } + CCollision::SortOutCollisionAfterLoad(); + + ISLAND_LOADING_ISNT(HIGH) + CStreaming::RequestIslands(CGame::currLevel); + + ISLAND_LOADING_IS(LOW) + CStreaming::RequestBigBuildings(CGame::currLevel); + + ISLAND_LOADING_ISNT(HIGH) + CStreaming::LoadAllRequestedModels(true); + + ISLAND_LOADING_IS(LOW) + DMAudio.SetEffectsFadeVol(127); + } + CTimer::Update(); + return 0; + */ + case COMMAND_LOAD_SPLASH_SCREEN: + CTheScripts::ReadTextLabelFromScript(&m_nIp, tmp); + for (int i = 0; i < KEY_LENGTH_IN_SCRIPT; i++) + tmp[i] = tolower(tmp[i]); + m_nIp += 8; + LoadSplash(tmp); + return 0; + /* + case COMMAND_SET_CAR_IGNORE_LEVEL_TRANSITIONS: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + if (ScriptParams[1]) + pVehicle->m_nZoneLevel = LEVEL_IGNORE; + else + pVehicle->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pVehicle->GetPosition()); + return 0; + } + case COMMAND_MAKE_CRAIGS_CAR_A_BIT_STRONGER: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + script_assert(pVehicle->m_vehType == VEHICLE_TYPE_CAR); + CAutomobile* pCar = (CAutomobile*)pVehicle; + pCar->bMoreResistantToDamage = ScriptParams[1]; + return 0; + } + */ + case COMMAND_SET_JAMES_CAR_ON_PATH_TO_PLAYER: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, FindPlayerCoors(), false); + return 0; + } + case COMMAND_LOAD_END_OF_GAME_TUNE: + DMAudio.ChangeMusicMode(MUSICMODE_CUTSCENE); + printf("Start preload end of game audio\n"); + DMAudio.PreloadCutSceneMusic(STREAMED_SOUND_CUTSCENE_FINALE); + printf("End preload end of game audio\n"); + return 0; + /* + case COMMAND_ENABLE_PLAYER_CONTROL_CAMERA: + CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_CAMERA); + return 0; + */ + case COMMAND_SET_OBJECT_ROTATION: + { + CollectParameters(&m_nIp, 4); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + CWorld::Remove(pObject); + pObject->SetOrientation( + DEGTORAD(*(float*)&ScriptParams[1]), + DEGTORAD(*(float*)&ScriptParams[2]), + DEGTORAD(*(float*)&ScriptParams[3])); + pObject->GetMatrix().UpdateRW(); + pObject->UpdateRwFrame(); + CWorld::Add(pObject); + return 0; + } + case COMMAND_GET_DEBUG_CAMERA_COORDINATES: + *(CVector*)&ScriptParams[0] = TheCamera.Cams[2].Source; + StoreParameters(&m_nIp, 3); + return 0; + /* + case COMMAND_GET_DEBUG_CAMERA_FRONT_VECTOR: + *(CVector*)&ScriptParams[0] = TheCamera.Cams[2].Front; + StoreParameters(&m_nIp, 3); + return 0; + case COMMAND_IS_PLAYER_TARGETTING_ANY_CHAR: + { + CollectParameters(&m_nIp, 1); + CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + CEntity* pTarget = pPed->m_pPointGunAt; + UpdateCompareFlag(pTarget && pTarget->IsPed()); + return 0; + } + */ + case COMMAND_IS_PLAYER_TARGETTING_CHAR: + { + CollectParameters(&m_nIp, 2); + CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + CPed* pTestedPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); + script_assert(pTestedPed); + CEntity* pTarget = pPed->m_pPointGunAt; + bool bTargetting = pTarget && pTarget->IsPed() && pTarget == pTestedPed; + // PC shit + static int nCounter = 0; + nCounter = Max(0, nCounter - 1); + if (!pPed->GetWeapon()->IsTypeMelee() && !bTargetting) { + if ((pTestedPed->GetPosition() - TheCamera.GetPosition()).Magnitude() < 10.0f) { + CVector vTestedPos(pTestedPed->GetPosition().x, pTestedPed->GetPosition().y, pTestedPed->GetPosition().z + 0.4); + CVector vScreenPos; + float w, h; + if (CSprite::CalcScreenCoors(vTestedPos, &vScreenPos, &w, &h, false)) { + CVector2D vCrosshairPosition(CCamera::m_f3rdPersonCHairMultX * RsGlobal.maximumWidth, CCamera::m_f3rdPersonCHairMultY * RsGlobal.maximumHeight); + float fScreenDistance = ((CVector2D)vScreenPos - vCrosshairPosition).Magnitude(); + if (SCREEN_STRETCH_X(0.45f) > fScreenDistance / w) { + CColPoint point; + CEntity* entity; + if (!CWorld::ProcessLineOfSight(TheCamera.GetPosition() + 2.0f * TheCamera.GetForward(), + vTestedPos, point, entity, true, true, true, true, true, false) || + entity == pTestedPed) { + nCounter += 2; + if (nCounter > 20) { + bTargetting = true; + nCounter = 20; + } + } + } + } + } + } + UpdateCompareFlag(bTargetting); + return 0; + } + /* + case COMMAND_IS_PLAYER_TARGETTING_OBJECT: + { + CollectParameters(&m_nIp, 2); + CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + CObject* pTestedObject = CPools::GetObjectPool()->GetAt(ScriptParams[1]); + script_assert(pTestedObject); + CEntity* pTarget = pPed->m_pPointGunAt; + UpdateCompareFlag(pTarget && pTarget->IsObject() && pTarget == pTestedObject); + return 0; + } + */ + case COMMAND_TERMINATE_ALL_SCRIPTS_WITH_THIS_NAME: + { + CTheScripts::ReadTextLabelFromScript(&m_nIp, tmp); + for (int i = 0; i < KEY_LENGTH_IN_SCRIPT; i++) + tmp[i] = tolower(tmp[i]); + m_nIp += 8; + CRunningScript* pScript = CTheScripts::pActiveScripts; + while (pScript) { + CRunningScript* pNext = pScript->next; + if (strcmp(pScript->m_abScriptName, tmp) == 0) { + pScript->RemoveScriptFromList(&CTheScripts::pActiveScripts); + pScript->AddScriptToList(&CTheScripts::pIdleScripts); + } + pScript = pNext; + } + return 0; + } + case COMMAND_DISPLAY_TEXT_WITH_NUMBER: + { + CollectParameters(&m_nIp, 2); + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fAtX = *(float*)&ScriptParams[0]; + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fAtY = *(float*)&ScriptParams[1]; + CollectParameters(&m_nIp, 1); + CMessages::InsertNumberInString(text, ScriptParams[0], -1, -1, -1, -1, -1, + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame++].m_Text); + return 0; + } + case COMMAND_DISPLAY_TEXT_WITH_2_NUMBERS: + { + CollectParameters(&m_nIp, 2); + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fAtX = *(float*)&ScriptParams[0]; + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fAtY = *(float*)&ScriptParams[1]; + CollectParameters(&m_nIp, 2); + CMessages::InsertNumberInString(text, ScriptParams[0], ScriptParams[1], -1, -1, -1, -1, + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame++].m_Text); + return 0; + } + case COMMAND_FAIL_CURRENT_MISSION: + CTheScripts::FailCurrentMission = 2; +#ifdef MISSION_REPLAY + MissionSkipLevel = 0; +#endif + return 0; + case COMMAND_GET_CLOSEST_OBJECT_OF_TYPE: + { + return 0; +/* + CollectParameters(&m_nIp, 5); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + float range = *(float*)&ScriptParams[3]; + int mi = ScriptParams[4] < 0 ? CTheScripts::UsedObjectArray[-ScriptParams[4]].index : ScriptParams[4]; + int16 total; + CEntity* apEntities[16]; + CWorld::FindObjectsOfTypeInRange(mi, pos, range, true, &total, 16, apEntities, false, false, false, true, true); + CEntity* pClosestEntity = nil; + float min_dist = 2.0f * range; + for (int i = 0; i < total; i++) { + float dist = (apEntities[i]->GetPosition() - pos).Magnitude(); + if (dist < min_dist) { + min_dist = dist; + pClosestEntity = apEntities[i]; + } + } + if (pClosestEntity && pClosestEntity->IsDummy()) { + CPopulation::ConvertToRealObject((CDummyObject*)pClosestEntity); + CWorld::FindObjectsOfTypeInRange(mi, pos, range, true, &total, 16, apEntities, false, false, false, true, true); + pClosestEntity = nil; + float min_dist = 2.0f * range; + for (int i = 0; i < total; i++) { + float dist = (apEntities[i]->GetPosition() - pos).Magnitude(); + if (dist < min_dist) { + min_dist = dist; + pClosestEntity = apEntities[i]; + } + } + if (pClosestEntity->IsDummy()) + pClosestEntity = nil; + } + if (pClosestEntity) { + script_assert(pClosestEntity->IsObject()); + CObject* pObject = (CObject*)pClosestEntity; + pObject->ObjectCreatedBy = MISSION_OBJECT; + ScriptParams[0] = CPools::GetObjectPool()->GetIndex(pObject); + } else { + ScriptParams[0] = -1; + } + StoreParameters(&m_nIp, 1); + return 0; +*/ + } + /* + case COMMAND_PLACE_OBJECT_RELATIVE_TO_OBJECT: + { + CollectParameters(&m_nIp, 5); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + CObject* pTarget = CPools::GetObjectPool()->GetAt(ScriptParams[1]); + script_assert(pTarget); + CVector offset = *(CVector*)&ScriptParams[2]; + CPhysical::PlacePhysicalRelativeToOtherPhysical(pTarget, pObject, offset); + return 0; + } + */ + case COMMAND_SET_ALL_OCCUPANTS_OF_CAR_LEAVE_CAR: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + CCarAI::TellOccupantsToLeaveCar(pVehicle); + return 0; + } + case COMMAND_SET_INTERPOLATION_PARAMETERS: + CollectParameters(&m_nIp, 2); + TheCamera.SetParametersForScriptInterpolation(*(float*)&ScriptParams[0], 100.0f - *(float*)&ScriptParams[0], ScriptParams[1]); + return 0; + /* + case COMMAND_GET_CLOSEST_CAR_NODE_WITH_HEADING_TOWARDS_POINT: + { + CollectParameters(&m_nIp, 5); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + float destX = *(float*)&ScriptParams[3]; + float destY = *(float*)&ScriptParams[4]; + int32 nid = ThePaths.FindNodeClosestToCoors(pos, 0, 999999.9f, true, true); + CPathNode* pNode = &ThePaths.m_pathNodes[nid]; + *(CVector*)&ScriptParams[0] = pNode->GetPosition(); + *(float*)&ScriptParams[3] = ThePaths.FindNodeOrientationForCarPlacementFacingDestination(nid, destX, destY, true); + StoreParameters(&m_nIp, 4); + return 0; + } + case COMMAND_GET_CLOSEST_CAR_NODE_WITH_HEADING_AWAY_POINT: + { + CollectParameters(&m_nIp, 5); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + float destX = *(float*)&ScriptParams[3]; + float destY = *(float*)&ScriptParams[4]; + int32 nid = ThePaths.FindNodeClosestToCoors(pos, 0, 999999.9f, true, true); + CPathNode* pNode = &ThePaths.m_pathNodes[nid]; + *(CVector*)&ScriptParams[0] = pNode->GetPosition(); + *(float*)&ScriptParams[3] = ThePaths.FindNodeOrientationForCarPlacementFacingDestination(nid, destX, destY, false); + StoreParameters(&m_nIp, 4); + return 0; + } + */ + case COMMAND_GET_DEBUG_CAMERA_POINT_AT: + *(CVector*)&ScriptParams[0] = TheCamera.Cams[2].Source + TheCamera.Cams[2].Front; + StoreParameters(&m_nIp, 3); + return 0; + case COMMAND_ATTACH_CHAR_TO_CAR: + { + CollectParameters(&m_nIp, 8); + CPed *pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + CVehicle *pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + pPed->AttachPedToEntity(pVehicle, *(CVector*)&ScriptParams[2], ScriptParams[5], DEGTORAD(*(float*)&ScriptParams[6]), (eWeaponType)ScriptParams[7]); + return 0; + } + case COMMAND_DETACH_CHAR_FROM_CAR: + { + CollectParameters(&m_nIp, 1); + CPed *pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + if (pPed && pPed->m_attachedTo) + pPed->DettachPedFromEntity(); + return 0; + } + case COMMAND_SET_CAR_CHANGE_LANE: // for some reason changed in SA + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->AutoPilot.m_bStayInFastLane = !ScriptParams[1]; + return 0; + } + case COMMAND_CLEAR_CHAR_LAST_WEAPON_DAMAGE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + if (pPed) + pPed->m_lastWepDam = -1; + else + debug("CLEAR_CHAR_LAST_WEAPON_DAMAGE - Character doesn't exist\n"); + return 0; + } + case COMMAND_CLEAR_CAR_LAST_WEAPON_DAMAGE: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + if (pVehicle) + pVehicle->m_nLastWeaponDamage = -1; + else + debug("CLEAR_CAR_LAST_WEAPON_DAMAGE - Vehicle doesn't exist\n"); + return 0; + } + case COMMAND_GET_RANDOM_COP_IN_AREA: + { + CollectParameters(&m_nIp, 9); + int ped_handle = -1; + CVector pos = FindPlayerCoors(); + float x1 = *(float*)&ScriptParams[0]; + float y1 = *(float*)&ScriptParams[1]; + float x2 = *(float*)&ScriptParams[2]; + float y2 = *(float*)&ScriptParams[3]; + int i = CPools::GetPedPool()->GetSize(); + while (--i && ped_handle == -1) { + CPed* pPed = CPools::GetPedPool()->GetSlot(i); + if (!pPed) + continue; + if (CTheScripts::LastRandomPedId == CPools::GetPedPool()->GetIndex(pPed)) + continue; + if (pPed->m_nPedType != PEDTYPE_COP) + continue; + if (!ThisIsAValidRandomCop(pPed->GetModelIndex(), ScriptParams[4], ScriptParams[5], ScriptParams[6], ScriptParams[7], ScriptParams[8])) + continue; + if (pPed->CharCreatedBy != RANDOM_CHAR) + continue; + if (!pPed->IsPedInControl() && pPed->GetPedState() != PED_DRIVING && pPed->GetPedState() != PED_ABSEIL) + continue; + if (pPed->bRemoveFromWorld) + continue; + if (pPed->bFadeOut) + continue; + if (pPed->bIsLeader || pPed->m_leader) + continue; + if (!pPed->IsWithinArea(x1, y1, x2, y2)) + continue; + if (pos.z - COP_PED_FIND_Z_OFFSET > pPed->GetPosition().z) + continue; + if (pos.z + COP_PED_FIND_Z_OFFSET < pPed->GetPosition().z) + continue; + ped_handle = CPools::GetPedPool()->GetIndex(pPed); + CTheScripts::LastRandomPedId = ped_handle; + pPed->CharCreatedBy = MISSION_CHAR; + pPed->bRespondsToThreats = false; + ++CPopulation::ms_nTotalMissionPeds; + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.AddEntityToList(ped_handle, CLEANUP_CHAR); + } + ScriptParams[0] = ped_handle; + StoreParameters(&m_nIp, 1); + return 0; + } + /* + case COMMAND_GET_RANDOM_COP_IN_ZONE: + { + char zone[KEY_LENGTH_IN_SCRIPT]; + strncpy(zone, (const char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); + int nZone = CTheZones::FindZoneByLabelAndReturnIndex(zone, ZONE_DEFAULT); + if (nZone != -1) + m_nIp += KEY_LENGTH_IN_SCRIPT; + CZone* pZone = CTheZones::GetNavigationZone(nZone); + int ped_handle = -1; + CVector pos = FindPlayerCoors(); + int i = CPools::GetPedPool()->GetSize(); + while (--i && ped_handle == -1) { + CPed* pPed = CPools::GetPedPool()->GetSlot(i); + if (!pPed) + continue; + if (CTheScripts::LastRandomPedId == CPools::GetPedPool()->GetIndex(pPed)) + continue; + if (pPed->m_nPedType != PEDTYPE_COP) + continue; + if (pPed->CharCreatedBy != RANDOM_CHAR) + continue; + if (!pPed->IsPedInControl() && pPed->GetPedState() != PED_DRIVING) + continue; + if (pPed->bRemoveFromWorld) + continue; + if (pPed->bFadeOut) + continue; + if (pPed->bIsLeader || pPed->m_leader) + continue; + if (!CTheZones::PointLiesWithinZone(&pPed->GetPosition(), pZone)) + continue; + if (pos.z - COP_PED_FIND_Z_OFFSET > pPed->GetPosition().z) + continue; + if (pos.z + COP_PED_FIND_Z_OFFSET < pPed->GetPosition().z) + continue; + ped_handle = CPools::GetPedPool()->GetIndex(pPed); + CTheScripts::LastRandomPedId = ped_handle; + pPed->CharCreatedBy = MISSION_CHAR; + pPed->bRespondsToThreats = false; + ++CPopulation::ms_nTotalMissionPeds; + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.AddEntityToList(ped_handle, CLEANUP_CHAR); + } + ScriptParams[0] = ped_handle; + StoreParameters(&m_nIp, 1); + return 0; + } + */ + case COMMAND_SET_CHAR_OBJ_FLEE_CAR: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + script_assert(pVehicle); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_FLEE_CAR, pVehicle); + return 0; + } + case COMMAND_GET_DRIVER_OF_CAR: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + CPed* pDriver = pVehicle->pDriver; + if (pDriver) + ScriptParams[0] = CPools::GetPedPool()->GetIndex(pDriver); + else + ScriptParams[0] = -1; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_GET_NUMBER_OF_FOLLOWERS: + { + CollectParameters(&m_nIp, 1); + CPed* pLeader = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pLeader); + int total = 0; + int i = CPools::GetPedPool()->GetSize(); + while (--i) { + CPed* pPed = CPools::GetPedPool()->GetSlot(i); + if (!pPed) + continue; + if (pPed->m_leader == pLeader) + total++; + } + ScriptParams[0] = total; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_GIVE_REMOTE_CONTROLLED_MODEL_TO_PLAYER: + { + CollectParameters(&m_nIp, 6); + CVector pos = *(CVector*)&ScriptParams[1]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CRemote::GivePlayerRemoteControlledCar(pos.x, pos.y, pos.z, DEGTORAD(*(float*)&ScriptParams[4]), ScriptParams[5]); + return 0; + } + case COMMAND_GET_CURRENT_PLAYER_WEAPON: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + ScriptParams[0] = pPed->GetWeapon()->m_eWeaponType; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_GET_CURRENT_CHAR_WEAPON: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + ScriptParams[0] = pPed->GetWeapon()->m_eWeaponType; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_2D: + case COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_2D: + case COMMAND_LOCATE_CHAR_IN_CAR_OBJECT_2D: + case COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_3D: + case COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_3D: + case COMMAND_LOCATE_CHAR_IN_CAR_OBJECT_3D: + LocateCharObjectCommand(command, &m_nIp); + return 0; + case COMMAND_SET_CAR_TEMP_ACTION: + { + CollectParameters(&m_nIp, 3); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->AutoPilot.m_nTempAction = (uint8)ScriptParams[1]; + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + ScriptParams[2]; + return 0; + } + /* + case COMMAND_SET_CAR_HANDBRAKE_TURN_RIGHT: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->AutoPilot.m_nTempAction = TEMPACT_HANDBRAKETURNRIGHT; + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + ScriptParams[1]; + return 0; + } + case COMMAND_SET_CAR_HANDBRAKE_STOP: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->AutoPilot.m_nTempAction = TEMPACT_HANDBRAKESTRAIGHT; + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + ScriptParams[1]; + return 0; + } + */ + case COMMAND_IS_CHAR_ON_ANY_BIKE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(pPed->bInVehicle&& pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE); + return 0; + } + /* + case COMMAND_LOCATE_SNIPER_BULLET_2D: + case COMMAND_LOCATE_SNIPER_BULLET_3D: + LocateSniperBulletCommand(command, &m_nIp); + return 0; + */ + case COMMAND_GET_NUMBER_OF_SEATS_IN_MODEL: + CollectParameters(&m_nIp, 1); + ScriptParams[0] = CVehicleModelInfo::GetMaximumNumberOfPassengersFromNumberOfDoors(ScriptParams[0]) + 1; + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_IS_PLAYER_ON_ANY_BIKE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE); + return 0; + } + /* + case COMMAND_IS_CHAR_LYING_DOWN: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(pPed->bFallenDown); + return 0; + } + */ + case COMMAND_CAN_CHAR_SEE_DEAD_CHAR: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + int pedtype = ScriptParams[1]; + bool can = false; + for (int i = 0; i < pPed->m_numNearPeds; i++) { + CPed* pTestPed = pPed->m_nearPeds[i]; + if (pTestPed->m_fHealth <= 0.0f && pTestPed->m_nPedType == pedtype && pPed->OurPedCanSeeThisOne(pTestPed)) + can = true; + } + UpdateCompareFlag(can); + return 0; + } + case COMMAND_SET_ENTER_CAR_RANGE_MULTIPLIER: + CollectParameters(&m_nIp, 1); + CPed::nEnterCarRangeMultiplier = *(float*)&ScriptParams[0]; + return 0; + case COMMAND_SET_THREAT_REACTION_RANGE_MULTIPLIER: + CollectParameters(&m_nIp, 1); + CPed::nThreatReactionRangeMultiplier = *(float*)&ScriptParams[0]; + return 0; + case COMMAND_SET_CHAR_CEASE_ATTACK_TIMER: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->m_ceaseAttackTimer = ScriptParams[1]; + return 0; + } + case COMMAND_GET_REMOTE_CONTROLLED_CAR: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CWorld::Players[ScriptParams[0]].m_pRemoteVehicle; + if (pVehicle) + ScriptParams[0] = CPools::GetVehiclePool()->GetIndex(pVehicle); + else + ScriptParams[0] = -1; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_IS_PC_VERSION: + UpdateCompareFlag(true); + return 0; + //case COMMAND_REPLAY: + //case COMMAND_IS_REPLAY_PLAYING: + case COMMAND_IS_MODEL_AVAILABLE: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CModelInfo::GetModelInfo(ScriptParams[0]) != nil); + return 0; + case COMMAND_SHUT_CHAR_UP: + CollectParameters(&m_nIp, 2); + DMAudio.SetPedTalkingStatus(CPools::GetPedPool()->GetAt(ScriptParams[0]), ScriptParams[1] == 0); + return 0; + case COMMAND_SET_ENABLE_RC_DETONATE: + CollectParameters(&m_nIp, 1); + CVehicle::bDisableRemoteDetonation = !ScriptParams[0]; + return 0; + case COMMAND_SET_CAR_RANDOM_ROUTE_SEED: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->m_nRouteSeed = ScriptParams[1]; + return 0; + } + case COMMAND_IS_ANY_PICKUP_AT_COORDS: + { + CollectParameters(&m_nIp, 3); + CVector pos = *(CVector*)&ScriptParams[0]; + CRunningScript::UpdateCompareFlag(CPickups::TestForPickupsInBubble(pos, 0.5f)); + return 0; + } + case COMMAND_GET_FIRST_PICKUP_COORDS: + case COMMAND_GET_NEXT_PICKUP_COORDS: + case COMMAND_REMOVE_ALL_CHAR_WEAPONS: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->ClearWeapons(); + return 0; + } + case COMMAND_HAS_PLAYER_GOT_WEAPON: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + bool bFound = false; + for (int i = 0; i < TOTAL_WEAPON_SLOTS; i++) { + if (pPed->GetWeapon(i).m_eWeaponType == ScriptParams[1]) { + bFound = true; + break; + } + } + UpdateCompareFlag(bFound); + return 0; + } + //case COMMAND_HAS_CHAR_GOT_WEAPON: + //case COMMAND_IS_PLAYER_FACING_CHAR: + case COMMAND_SET_TANK_DETONATE_CARS: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle && pVehicle->m_vehType == VEHICLE_TYPE_CAR); + ((CAutomobile*)pVehicle)->bTankDetonateCars = ScriptParams[1]; + return 0; + } + case COMMAND_GET_POSITION_OF_ANALOGUE_STICKS: + { + CollectParameters(&m_nIp, 1); + CPad* pPad = CPad::GetPad(ScriptParams[0]); + ScriptParams[0] = pPad->NewState.LeftStickX; + ScriptParams[1] = pPad->NewState.LeftStickY; + ScriptParams[2] = pPad->NewState.RightStickX; + ScriptParams[3] = pPad->NewState.RightStickY; + StoreParameters(&m_nIp, 4); + return 0; + } + case COMMAND_IS_CAR_ON_FIRE: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + bool bOnFire = false; + if (pVehicle->m_pCarFire) + bOnFire = true; + if (pVehicle->m_vehType == VEHICLE_TYPE_CAR && ((CAutomobile*)pVehicle)->Damage.GetEngineStatus() >= ENGINE_STATUS_ON_FIRE) + bOnFire = true; + if (pVehicle->m_fHealth < 250.0f) + bOnFire = true; + UpdateCompareFlag(bOnFire); + return 0; + } + case COMMAND_IS_CAR_TYRE_BURST: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + bool bIsBurst = false; + CBike* pBike = (CBike*)pVehicle; + if (pVehicle->IsBike()) { + if (ScriptParams[1] == 4) { + for (int i = 0; i < 2; i++) { + if (pBike->m_wheelStatus[i] == WHEEL_STATUS_BURST) + bIsBurst = true; + } + } + else { + if (ScriptParams[1] == 2) + ScriptParams[1] = 0; + if (ScriptParams[1] == 3) + ScriptParams[1] = 1; + bIsBurst = pBike->m_wheelStatus[ScriptParams[1]] == WHEEL_STATUS_BURST; + } + } + else { + CAutomobile* pCar = (CAutomobile*)pVehicle; + if (ScriptParams[1] == 4) { + for (int i = 0; i < 4; i++) { + if (pCar->Damage.GetWheelStatus(i) == WHEEL_STATUS_BURST) + bIsBurst = true; + } + } + else + bIsBurst = pCar->Damage.GetWheelStatus(ScriptParams[1] == WHEEL_STATUS_BURST); + } + UpdateCompareFlag(bIsBurst); + return 0; + } + //case COMMAND_SET_CAR_DRIVE_STRAIGHT_AHEAD: + //case COMMAND_SET_CAR_WAIT: + //case COMMAND_IS_PLAYER_STANDING_ON_A_VEHICLE: + //case COMMAND_IS_PLAYER_FOOT_DOWN: + //case COMMAND_IS_CHAR_FOOT_DOWN: + case COMMAND_INITIALISE_OBJECT_PATH: { + CollectParameters(&m_nIp, 2); + int32 counter = 0; + while (counter < 3 && CScriptPaths::aArray[counter].m_state != SCRIPT_PATH_DISABLED) { + counter++; + } + CScriptPaths::aArray[counter].InitialiseOne(ScriptParams[0], *(float*)&ScriptParams[1]); + ScriptParams[0] = counter; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_START_OBJECT_ON_PATH: + { + CollectParameters(&m_nIp, 2); + CObject *pObj = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + assert(pObj); + CScriptPaths::aArray[ScriptParams[1]].SetObjectToControl(pObj); + return 0; + } + case COMMAND_SET_OBJECT_PATH_SPEED: + { + CollectParameters(&m_nIp, 2); + CScriptPaths::aArray[ScriptParams[0]].m_fSpeed = *(float*)&ScriptParams[1]; + return 0; + } + case COMMAND_SET_OBJECT_PATH_POSITION: + { + CollectParameters(&m_nIp, 2); + CScriptPaths::aArray[ScriptParams[0]].m_fPosition = *(float*)&ScriptParams[1]; + return 0; + } + //case COMMAND_GET_OBJECT_DISTANCE_ALONG_PATH: + case COMMAND_CLEAR_OBJECT_PATH: + { + CollectParameters(&m_nIp, 1); + CScriptPaths::aArray[ScriptParams[0]].Clear(); + return 0; + } + case COMMAND_HELI_GOTO_COORDS: + { + CollectParameters(&m_nIp, 5); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle && pVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI); + ((CAutomobile*)pVehicle)->TellHeliToGoToCoors(*(float*)&ScriptParams[1], *(float*)&ScriptParams[2], *(float*)&ScriptParams[3], ScriptParams[4]); + return 0; + } + case COMMAND_IS_INT_VAR_EQUAL_TO_CONSTANT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(*ptr == ScriptParams[0]); + return 0; + } + case COMMAND_IS_INT_LVAR_EQUAL_TO_CONSTANT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(*ptr == ScriptParams[0]); + return 0; + } + case COMMAND_GET_DEAD_CHAR_PICKUP_COORDS: + { + CollectParameters(&m_nIp, 1); + CPed *pTarget = CPools::GetPedPool()->GetAt(ScriptParams[0]); + CVector pos; + pTarget->CreateDeadPedPickupCoors(&pos.x, &pos.y, &pos.z); + *(CVector*)&ScriptParams[0] = pos; + StoreParameters(&m_nIp, 3); + return 0; + } + case COMMAND_CREATE_PROTECTION_PICKUP: + { + CollectParameters(&m_nIp, 5); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; + CPickups::GetActualPickupIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + ScriptParams[0] = CPickups::GenerateNewOne(pos, MI_PICKUP_REVENUE, PICKUP_ASSET_REVENUE, ScriptParams[3], ScriptParams[4]); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_IS_CHAR_IN_ANY_BOAT: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_BOAT); + return 0; + } + case COMMAND_IS_PLAYER_IN_ANY_BOAT: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_BOAT); + return 0; + } + case COMMAND_IS_CHAR_IN_ANY_HELI: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI); + return 0; + } + case COMMAND_IS_PLAYER_IN_ANY_HELI: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI); + return 0; + } + case COMMAND_IS_CHAR_IN_ANY_PLANE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE); + return 0; + } + case COMMAND_IS_PLAYER_IN_ANY_PLANE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI); + return 0; + } + case COMMAND_IS_CHAR_IN_WATER: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + UpdateCompareFlag(pPed && pPed->bIsInWater); + return 0; + } + case COMMAND_SET_VAR_INT_TO_CONSTANT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + *ptr = ScriptParams[0]; + return 0; + } + case COMMAND_SET_LVAR_INT_TO_CONSTANT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + *ptr = ScriptParams[0]; + return 0; + } + default: + script_assert(0); + } + return -1; +} diff --git a/src/miami/control/Script7.cpp b/src/miami/control/Script7.cpp new file mode 100644 index 00000000..71099cc4 --- /dev/null +++ b/src/miami/control/Script7.cpp @@ -0,0 +1,1404 @@ +#include "common.h" + +#include "Script.h" +#include "ScriptCommands.h" + +#include "CarCtrl.h" +#include "ColStore.h" +#include "Coronas.h" +#include "CutsceneMgr.h" +#include "DMAudio.h" +#include "Explosion.h" +#include "GameLogic.h" +#include "General.h" +#include "Glass.h" +#include "Fluff.h" +#include "Hud.h" +#include "MBlur.h" +#include "Pad.h" +#include "Pickups.h" +#include "Pools.h" +#include "Population.h" +#include "Radar.h" +#include "RoadBlocks.h" +#include "Ropes.h" +#include "SetPieces.h" +#include "SpecialFX.h" +#include "Stats.h" +#include "Streaming.h" +#include "Timecycle.h" +#include "User.h" +#include "World.h" +#include "Zones.h" + +int8 CRunningScript::ProcessCommands1200To1299(int32 command) +{ + switch (command) { + case COMMAND_IS_INT_VAR_GREATER_THAN_CONSTANT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(*ptr > ScriptParams[0]); + return 0; + } + case COMMAND_IS_INT_LVAR_GREATER_THAN_CONSTANT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(*ptr > ScriptParams[0]); + return 0; + } + case COMMAND_IS_CONSTANT_GREATER_THAN_INT_VAR: + { + CollectParameters(&m_nIp, 1); + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + UpdateCompareFlag(ScriptParams[0] > *ptr); + return 0; + } + case COMMAND_IS_CONSTANT_GREATER_THAN_INT_LVAR: + { + CollectParameters(&m_nIp, 1); + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + UpdateCompareFlag(ScriptParams[0] > *ptr); + return 0; + } + case COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_CONSTANT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(*ptr >= ScriptParams[0]); + return 0; + } + case COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_CONSTANT: + { + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(*ptr >= ScriptParams[0]); + return 0; + } + case COMMAND_IS_CONSTANT_GREATER_OR_EQUAL_TO_INT_VAR: + { + CollectParameters(&m_nIp, 1); + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); + UpdateCompareFlag(ScriptParams[0] >= *ptr); + return 0; + } + case COMMAND_IS_CONSTANT_GREATER_OR_EQUAL_TO_INT_LVAR: + { + CollectParameters(&m_nIp, 1); + int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); + UpdateCompareFlag(ScriptParams[0] >= *ptr); + return 0; + } + case COMMAND_GET_CHAR_WEAPON_IN_SLOT: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + ScriptParams[0] = pPed->GetWeapon(ScriptParams[1] - 1).m_eWeaponType; + ScriptParams[1] = pPed->GetWeapon(ScriptParams[1] - 1).m_nAmmoTotal; + ScriptParams[2] = CPickups::ModelForWeapon((eWeaponType)ScriptParams[0]); + StoreParameters(&m_nIp, 3); + return 0; + } + case COMMAND_GET_CLOSEST_STRAIGHT_ROAD: + { + CollectParameters(&m_nIp, 5); + int node1, node2; + float angle; + ThePaths.FindNodePairClosestToCoors(*(CVector*)&ScriptParams[0], PATH_CAR, &node1, &node2, &angle, + *(float*)&ScriptParams[3], *(float*)&ScriptParams[4], true, true); + if (node1 == -1) { + for (int i = 0; i < 7; i++) + ScriptParams[i] = 0; + } + else { + *(CVector*)&ScriptParams[0] = ThePaths.FindNodeCoorsForScript(node1); + *(CVector*)&ScriptParams[3] = ThePaths.FindNodeCoorsForScript(node2); + *(float*)&ScriptParams[6] = angle; + } + StoreParameters(&m_nIp, 7); + return 0; + } + case COMMAND_SET_CAR_FORWARD_SPEED: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + float speed = *(float*)&ScriptParams[1] / GAME_SPEED_TO_CARAI_SPEED; + pVehicle->SetMoveSpeed(pVehicle->GetForward() * speed); + if (pVehicle->IsRealHeli() && pVehicle->IsCar()) + ((CAutomobile*)pVehicle)->m_aWheelSpeed[1] = 0.22f; + return 0; + } + case COMMAND_SET_AREA_VISIBLE: + CollectParameters(&m_nIp, 1); + CGame::currArea = ScriptParams[0]; + CStreaming::RemoveBuildingsNotInArea(ScriptParams[0]); + return 0; + case COMMAND_SET_CUTSCENE_ANIM_TO_LOOP: + { + char key[KEY_LENGTH_IN_SCRIPT]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, key); + m_nIp += KEY_LENGTH_IN_SCRIPT; + CCutsceneMgr::SetCutsceneAnimToLoop(key); + return 0; + } + case COMMAND_MARK_CAR_AS_CONVOY_CAR: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->bPartOfConvoy = ScriptParams[1]; + return 0; + } + case COMMAND_RESET_HAVOC_CAUSED_BY_PLAYER: + { + CollectParameters(&m_nIp, 1); + CWorld::Players[ScriptParams[0]].m_nHavocLevel = 0; + return 0; + } + case COMMAND_GET_HAVOC_CAUSED_BY_PLAYER: + { + CollectParameters(&m_nIp, 1); + ScriptParams[0] = CWorld::Players[ScriptParams[0]].m_nHavocLevel; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_CREATE_SCRIPT_ROADBLOCK: + { + CollectParameters(&m_nIp, 6); + CRoadBlocks::RegisterScriptRoadBlock(*(CVector*)&ScriptParams[0], *(CVector*)&ScriptParams[3]); + return 0; + } + case COMMAND_CLEAR_ALL_SCRIPT_ROADBLOCKS: + { + CRoadBlocks::ClearScriptRoadBlocks(); + return 0; + } + case COMMAND_SET_CHAR_OBJ_WALK_TO_CHAR: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CPed* pTargetPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); + script_assert(pTargetPed); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING, pTargetPed); + return 0; + } + //case COMMAND_IS_PICKUP_IN_ZONE: + case COMMAND_GET_OFFSET_FROM_CHAR_IN_WORLD_COORDS: + { + CollectParameters(&m_nIp, 4); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CVector result = Multiply3x3(pPed->GetMatrix(), *(CVector*)&ScriptParams[1]) + pPed->GetPosition(); + *(CVector*)&ScriptParams[0] = result; + StoreParameters(&m_nIp, 3); + return 0; + } + case COMMAND_HAS_CHAR_BEEN_PHOTOGRAPHED: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + bool result = false; + if (pPed->bHasBeenPhotographed) { + result = true; + pPed->bHasBeenPhotographed = false; + } + UpdateCompareFlag(result); + return 0; + } + case COMMAND_SET_CHAR_OBJ_AIM_GUN_AT_CHAR: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CPed* pTargetPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); + script_assert(pTargetPed); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_AIM_GUN_AT, pTargetPed); + return 0; + } + case COMMAND_SWITCH_SECURITY_CAMERA: + { + CollectParameters(&m_nIp, 1); + CSpecialFX::bVideoCam = ScriptParams[0] != 0; + return 0; + } + //case COMMAND_IS_CHAR_IN_FLYING_VEHICLE: + case COMMAND_IS_PLAYER_IN_FLYING_VEHICLE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + UpdateCompareFlag(pPed->bInVehicle && (pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI || pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE)); + return 0; + } + //case COMMAND_HAS_SONY_CD_BEEN_READ: + //case COMMAND_GET_NUMBER_OF_SONY_CDS_READ: + //case COMMAND_ADD_SHORT_RANGE_BLIP_FOR_COORD_OLD: + //case COMMAND_ADD_SHORT_RANGE_BLIP_FOR_COORD: + case COMMAND_ADD_SHORT_RANGE_SPRITE_BLIP_FOR_COORD: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + int id = CRadar::SetShortRangeCoordBlip(BLIP_COORD, pos, 5, BLIP_DISPLAY_BOTH); + CRadar::SetBlipSprite(id, ScriptParams[3]); + ScriptParams[0] = id; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_ADD_MONEY_SPENT_ON_CLOTHES: + CollectParameters(&m_nIp, 1); + CStats::MoneySpentOnFashion(ScriptParams[0]); + return 0; + + case COMMAND_SET_HELI_ORIENTATION: + { + CollectParameters(&m_nIp, 2); + CAutomobile* pHeli = (CAutomobile*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pHeli && pHeli->IsCar() && pHeli->IsRealHeli()); + float fAngle = DEGTORAD(*(float*)&ScriptParams[1] - 90.0f); + while (fAngle < 0.0f) + fAngle += TWOPI; + while (fAngle > TWOPI) + fAngle -= TWOPI; + pHeli->SetHeliOrientation(fAngle); + return 0; + } + case COMMAND_CLEAR_HELI_ORIENTATION: + { + CollectParameters(&m_nIp, 1); + CAutomobile* pHeli = (CAutomobile*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pHeli && pHeli->IsCar() && pHeli->IsRealHeli()); + pHeli->ClearHeliOrientation(); + return 0; + } + case COMMAND_PLANE_GOTO_COORDS: + { + CollectParameters(&m_nIp, 5); + CAutomobile* pPlane = (CAutomobile*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pPlane && pPlane->IsCar() && pPlane->IsRealPlane()); + pPlane->TellPlaneToGoToCoors(*(float*)&ScriptParams[1], *(float*)&ScriptParams[2], *(float*)&ScriptParams[3], ScriptParams[4]); + return 0; + } + case COMMAND_GET_NTH_CLOSEST_CAR_NODE: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + *(CVector*)&ScriptParams[0] = ThePaths.FindNodeCoorsForScript(ThePaths.FindNthNodeClosestToCoors(pos, 0, 999999.9f, true, true, ScriptParams[3] - 1)); + StoreParameters(&m_nIp, 3); + return 0; + } + //case COMMAND_GET_NTH_CLOSEST_CHAR_NODE: + case COMMAND_DRAW_WEAPONSHOP_CORONA: + { + CollectParameters(&m_nIp, 9); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CCoronas::RegisterCorona((uintptr)this + m_nIp, ScriptParams[6], ScriptParams[7], ScriptParams[8], 255, pos, *(float*)&ScriptParams[3], + 150.0f, ScriptParams[4], ScriptParams[5], 1, 0, 0, 0.0f, false, 0.2f); + return 0; + } + case COMMAND_SET_ENABLE_RC_DETONATE_ON_CONTACT: + { + CollectParameters(&m_nIp, 1); + CVehicle::bDisableRemoteDetonationOnContact = (ScriptParams[0] == 0); + return 0; + } + case COMMAND_FREEZE_CHAR_POSITION: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bIsFrozen = ScriptParams[1]; + return 0; + } + case COMMAND_SET_CHAR_DROWNS_IN_WATER: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bDrownsInWater = ScriptParams[1]; + return 0; + } + case COMMAND_SET_OBJECT_RECORDS_COLLISIONS: + { + CollectParameters(&m_nIp, 2); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + pObject->bUseCollisionRecords = ScriptParams[1]; + return 0; + } + case COMMAND_HAS_OBJECT_COLLIDED_WITH_ANYTHING: + { + CollectParameters(&m_nIp, 1); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + UpdateCompareFlag(pObject->m_nCollisionRecords != 0); + return 0; + } + case COMMAND_REMOVE_RC_BUGGY: + { + CWorld::Players[CWorld::PlayerInFocus].BlowUpRCBuggy(false); + return 0; + } + //case COMMAND_HAS_PHOTOGRAPH_BEEN_TAKEN: + case COMMAND_GET_CHAR_ARMOUR: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + ScriptParams[0] = pPed->m_fArmour; + StoreParameters(&m_nIp, 1); + return 0; + } + //case COMMAND_SET_CHAR_ARMOUR: + case COMMAND_SET_HELI_STABILISER: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->bHeliMinimumTilt = ScriptParams[1]; + return 0; + } + case COMMAND_SET_CAR_STRAIGHT_LINE_DISTANCE: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->AutoPilot.m_nSwitchDistance = ScriptParams[1]; + return 0; + } + case COMMAND_POP_CAR_BOOT: + { + CollectParameters(&m_nIp, 1); + CAutomobile* pCar = (CAutomobile*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pCar&& pCar->IsCar()); + pCar->PopBoot(); + return 0; + } + case COMMAND_SHUT_PLAYER_UP: + { + CollectParameters(&m_nIp, 2); + DMAudio.ShutUpPlayerTalking(!!ScriptParams[1]); + return 0; + } + case COMMAND_SET_PLAYER_MOOD: + { + CollectParameters(&m_nIp, 3); + DMAudio.SetPlayersMood(ScriptParams[1], ScriptParams[2]); + return 0; + } + case COMMAND_REQUEST_COLLISION: + { + CollectParameters(&m_nIp, 2); + CVector2D pos; + pos.x = *(float*)&ScriptParams[0]; + pos.y = *(float*)&ScriptParams[1]; + CColStore::RequestCollision(pos); + return 0; + } + case COMMAND_LOCATE_OBJECT_2D: + case COMMAND_LOCATE_OBJECT_3D: + LocateObjectCommand(command, &m_nIp); + return 0; + case COMMAND_IS_OBJECT_IN_WATER: + { + CollectParameters(&m_nIp, 1); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + UpdateCompareFlag(pObject->bIsInWater); + return 0; + } + //case COMMAND_SET_CHAR_OBJ_STEAL_ANY_CAR_EVEN_MISSION_CAR: + case COMMAND_IS_OBJECT_IN_AREA_2D: + case COMMAND_IS_OBJECT_IN_AREA_3D: + ObjectInAreaCheckCommand(command, &m_nIp); + return 0; + case COMMAND_SET_CHAR_CROUCH: + { + CollectParameters(&m_nIp, 3); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + if (ScriptParams[1]) { + pPed->bCrouchWhenShooting = true; + pPed->SetDuck(ScriptParams[2], true); + } + else { + pPed->ClearDuck(true); + pPed->bCrouchWhenShooting = false; + } + return 0; + } + case COMMAND_SET_ZONE_CIVILIAN_CAR_INFO: + { + char label[12]; + int16 carDensities[CCarCtrl::NUM_CAR_CLASSES] = { 0 }; + int16 boatDensities[CCarCtrl::NUM_BOAT_CLASSES] = { 0 }; + int i; + + CTheScripts::ReadTextLabelFromScript(&m_nIp, label); + m_nIp += KEY_LENGTH_IN_SCRIPT; + CollectParameters(&m_nIp, 12); + for (i = 0; i < CCarCtrl::NUM_CAR_CLASSES; i++) + carDensities[i] = ScriptParams[i + 1]; + for (i = 0; i < CCarCtrl::NUM_BOAT_CLASSES; i++) + boatDensities[i] = ScriptParams[i + 1 + CCarCtrl::NUM_CAR_CLASSES]; + int zone = CTheZones::FindZoneByLabelAndReturnIndex(label, ZONE_INFO); + if (zone < 0) { + debug("Couldn't find zone - %s\n", label); + return 0; + } + while (zone >= 0) { + CTheZones::SetZoneCivilianCarInfo(zone, ScriptParams[0], carDensities, boatDensities); + zone = CTheZones::FindNextZoneByLabelAndReturnIndex(label, ZONE_INFO); + } + return 0; + } + case COMMAND_REQUEST_ANIMATION: + { + char key[KEY_LENGTH_IN_SCRIPT]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, key); + m_nIp += KEY_LENGTH_IN_SCRIPT; + CStreaming::RequestAnim(CAnimManager::GetAnimationBlockIndex(key), STREAMFLAGS_SCRIPTOWNED); + return 0; + } + case COMMAND_HAS_ANIMATION_LOADED: + { + char key[KEY_LENGTH_IN_SCRIPT]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, key); + m_nIp += KEY_LENGTH_IN_SCRIPT; + UpdateCompareFlag(CAnimManager::GetAnimationBlock(key)->isLoaded); + return 0; + } + case COMMAND_REMOVE_ANIMATION: + { + char key[KEY_LENGTH_IN_SCRIPT]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, key); + m_nIp += KEY_LENGTH_IN_SCRIPT; + CStreaming::RemoveAnim(CAnimManager::GetAnimationBlockIndex(key)); + return 0; + } + case COMMAND_IS_CHAR_WAITING_FOR_WORLD_COLLISION: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(pPed->bIsStaticWaitingForCollision); + return 0; + } + case COMMAND_IS_CAR_WAITING_FOR_WORLD_COLLISION: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + UpdateCompareFlag(pVehicle->bIsStaticWaitingForCollision); + return 0; + } + case COMMAND_IS_OBJECT_WAITING_FOR_WORLD_COLLISION: + { + CollectParameters(&m_nIp, 1); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + UpdateCompareFlag(pObject->bIsStaticWaitingForCollision); + return 0; + } + case COMMAND_SET_CHAR_SHUFFLE_INTO_DRIVERS_SEAT: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + pPed->PedShuffle(); + return 0; + } + case COMMAND_ATTACH_CHAR_TO_OBJECT: + { + CollectParameters(&m_nIp, 8); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[1]); + pPed->AttachPedToEntity(pObject, *(CVector*)&ScriptParams[2], ScriptParams[5], DEGTORAD(ScriptParams[6]), (eWeaponType)ScriptParams[7]); + return 0; + } + case COMMAND_SET_CHAR_AS_PLAYER_FRIEND: + { + CollectParameters(&m_nIp, 3); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bIsPlayerFriend = ScriptParams[2]; + return 0; + } + //case COMMAND_DISPLAY_NTH_ONSCREEN_COUNTER: + case COMMAND_DISPLAY_NTH_ONSCREEN_COUNTER_WITH_STRING: + { + char onscreen_str[12]; + script_assert(CTheScripts::ScriptSpace[m_nIp++] == ARGUMENT_GLOBALVAR); + uint16 var = CTheScripts::Read2BytesFromScript(&m_nIp); + CollectParameters(&m_nIp, 2); + wchar* text = TheText.Get((char*)&CTheScripts::ScriptSpace[m_nIp]); // ??? + strncpy(onscreen_str, (char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); + m_nIp += KEY_LENGTH_IN_SCRIPT; + CUserDisplay::OnscnTimer.AddCounter(var, ScriptParams[0], onscreen_str, ScriptParams[1] - 1); + return 0; + } + case COMMAND_ADD_SET_PIECE: + { + CollectParameters(&m_nIp, 13); + CSetPieces::AddOne(ScriptParams[0], + *(CVector2D*)&ScriptParams[1], *(CVector2D*)&ScriptParams[3], + *(CVector2D*)&ScriptParams[5], *(CVector2D*)&ScriptParams[7], + *(CVector2D*)&ScriptParams[9], *(CVector2D*)&ScriptParams[11]); + return 0; + } + case COMMAND_SET_EXTRA_COLOURS: + { + CollectParameters(&m_nIp, 2); + CTimeCycle::StartExtraColour(ScriptParams[0]-1, ScriptParams[1] != 0); + return 0; + } + case COMMAND_CLEAR_EXTRA_COLOURS: + { + CollectParameters(&m_nIp, 1); + CTimeCycle::StopExtraColour(ScriptParams[0]); + return 0; + } + //case COMMAND_CLOSE_CAR_BOOT: + case COMMAND_GET_WHEELIE_STATS: + { + CollectParameters(&m_nIp, 1); + CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; + ScriptParams[0] = pPlayerInfo->m_nLastTimeCarSpentOnTwoWheels; + *(float*)&ScriptParams[1] = pPlayerInfo->m_nLastDistanceCarTravelledOnTwoWheels; + ScriptParams[2] = pPlayerInfo->m_nLastTimeSpentOnWheelie; + *(float*)&ScriptParams[3] = pPlayerInfo->m_nLastDistanceTravelledOnWheelie; + ScriptParams[4] = pPlayerInfo->m_nLastTimeSpentOnStoppie; + *(float*)&ScriptParams[5] = pPlayerInfo->m_nLastDistanceTravelledOnStoppie; + StoreParameters(&m_nIp, 6); + pPlayerInfo->m_nLastTimeCarSpentOnTwoWheels = 0; + pPlayerInfo->m_nLastDistanceCarTravelledOnTwoWheels = 0.0f; + pPlayerInfo->m_nLastTimeSpentOnWheelie = 0; + pPlayerInfo->m_nLastDistanceTravelledOnWheelie = 0.0f; + pPlayerInfo->m_nLastTimeSpentOnStoppie = 0; + pPlayerInfo->m_nLastDistanceTravelledOnStoppie = 0.0f; + return 0; + } + //case COMMAND_DISARM_CHAR: + case COMMAND_BURST_CAR_TYRE: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + if (pVehicle->IsBike()) { + if (ScriptParams[1] == 2) + ScriptParams[1] = 0; + else if (ScriptParams[1] == 3) + ScriptParams[1] = 1; + pVehicle->BurstTyre(ScriptParams[1], true); + } + else { + pVehicle->BurstTyre(ScriptParams[1], true); + } + return 0; + } + case COMMAND_IS_CHAR_OBJ_NO_OBJ: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(pPed->m_prevObjective == OBJECTIVE_NONE && pPed->m_objective == OBJECTIVE_NONE); + return 0; + } + case COMMAND_IS_PLAYER_WEARING: + { + CollectParameters(&m_nIp, 1); + char key[12]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, key); + m_nIp += KEY_LENGTH_IN_SCRIPT; + for (int i = 0; i < KEY_LENGTH_IN_SCRIPT; i++) + key[i] = tolower(key[i]); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + UpdateCompareFlag(strcmp(key, CModelInfo::GetModelInfo(pPed->GetModelIndex())->GetModelName()) == 0); + return 0; + } + case COMMAND_SET_PLAYER_CAN_DO_DRIVE_BY: + { + CollectParameters(&m_nIp, 2); + CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; + pPlayerInfo->m_bDriveByAllowed = ScriptParams[1]; + return 0; + } + case COMMAND_SET_CHAR_OBJ_SPRINT_TO_COORD: + { + CollectParameters(&m_nIp, 3); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CVector pos; + pos.x = *(float*)&ScriptParams[1]; + pos.y = *(float*)&ScriptParams[2]; + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + pPed->bScriptObjectiveCompleted = false; + pPed->SetObjective(OBJECTIVE_SPRINT_TO_AREA, pos); + return 0; + } + case COMMAND_CREATE_SWAT_ROPE: + { + CollectParameters(&m_nIp, 3); + CRopes::CreateRopeWithSwatComingDown(*(CVector*)&ScriptParams[0]); + return 0; + } + //case COMMAND_SET_FIRST_PERSON_CONTROL_CAMERA: + //case COMMAND_GET_NEAREST_TYRE_TO_POINT: + case COMMAND_SET_CAR_MODEL_COMPONENTS: + { + CollectParameters(&m_nIp, 3); + CVehicleModelInfo::SetComponentsToUse(ScriptParams[1], ScriptParams[2]); + return 0; + } + case COMMAND_SWITCH_LIFT_CAMERA: + { + CollectParameters(&m_nIp, 1); + CSpecialFX::bLiftCam = ScriptParams[0] != 0; + return 0; + } + case COMMAND_CLOSE_ALL_CAR_DOORS: + { + CollectParameters(&m_nIp, 1); + CAutomobile* pCar = (CAutomobile*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pCar&& pCar->IsCar()); + pCar->CloseAllDoors(); + return 0; + } + case COMMAND_GET_DISTANCE_BETWEEN_COORDS_2D: + { + CollectParameters(&m_nIp, 4); + *(float*)&ScriptParams[0] = (*(CVector2D*)&ScriptParams[0] - *(CVector2D*)&ScriptParams[2]).Magnitude(); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_GET_DISTANCE_BETWEEN_COORDS_3D: + { + CollectParameters(&m_nIp, 6); + *(float*)&ScriptParams[0] = (*(CVector*)&ScriptParams[0] - *(CVector*)&ScriptParams[3]).Magnitude(); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_POP_CAR_BOOT_USING_PHYSICS: + { + CollectParameters(&m_nIp, 1); + CAutomobile* pCar = (CAutomobile*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pCar && pCar->IsCar()); + pCar->PopBootUsingPhysics(); + return 0; + } + //case COMMAND_SET_FIRST_PERSON_WEAPON_CAMERA: + case COMMAND_IS_CHAR_LEAVING_VEHICLE_TO_DIE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(pPed->m_objective == OBJECTIVE_LEAVE_CAR_AND_DIE); + return 0; + } + case COMMAND_SORT_OUT_OBJECT_COLLISION_WITH_CAR: + { + CollectParameters(&m_nIp, 2); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + pObject->m_pCollidingEntity = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + return 0; + } + //case COMMAND_GET_MAX_WANTED_LEVEL: + case COMMAND_IS_CHAR_WANDER_PATH_CLEAR: + { + CollectParameters(&m_nIp, 5); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(CWorld::IsWanderPathClear(pPed->GetPosition(), *(CVector*)&ScriptParams[0], *(float*)&ScriptParams[3], 4)); + return 0; + } + //case COMMAND_PRINT_HELP_WITH_NUMBER: + case COMMAND_PRINT_HELP_FOREVER: + { + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CHud::SetHelpMessage(text, false, true); + return 0; + } + //case COMMAND_PRINT_HELP_FOREVER_WITH_NUMBER: + default: + script_assert(0); + } + return -1; +} + +int8 CRunningScript::ProcessCommands1300To1399(int32 command) +{ + switch (command) { + case COMMAND_SET_CHAR_CAN_BE_DAMAGED_BY_MEMBERS_OF_GANG: + { + CollectParameters(&m_nIp, 3); + CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pTarget); + uint8 flag = 1 << (uint8)ScriptParams[1]; + if (ScriptParams[2]) + pTarget->m_gangFlags |= flag; + else + pTarget->m_gangFlags &= ~flag; + + return 0; + } + case COMMAND_LOAD_AND_LAUNCH_MISSION_EXCLUSIVE: + return 0; + //case COMMAND_IS_MISSION_AUDIO_PLAYING: + case COMMAND_CREATE_LOCKED_PROPERTY_PICKUP: + { + CollectParameters(&m_nIp, 3); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; + char key[KEY_LENGTH_IN_SCRIPT]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, key); + m_nIp += KEY_LENGTH_IN_SCRIPT; + // TheText.Get(key); + CPickups::GetActualPickupIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + ScriptParams[0] = CPickups::GenerateNewOne(pos, MI_PICKUP_PROPERTY, PICKUP_PROPERTY_LOCKED, 0, 0, false, key); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_CREATE_FORSALE_PROPERTY_PICKUP: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; + char key[KEY_LENGTH_IN_SCRIPT]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, key); + m_nIp += KEY_LENGTH_IN_SCRIPT; + // TheText.Get(key); + CPickups::GetActualPickupIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + ScriptParams[0] = CPickups::GenerateNewOne(pos, MI_PICKUP_PROPERTY_FORSALE, PICKUP_PROPERTY_FORSALE, ScriptParams[3], 0, false, key); + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_FREEZE_CAR_POSITION: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->bIsFrozen = ScriptParams[1]; + pVehicle->bInfiniteMass = ScriptParams[1]; + return 0; + } + case COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_CHAR: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + CPed* pTestedPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); + bool result = false; + if (pPed) { + if (pPed->m_lastDamEntity) { + if (pPed->m_lastDamEntity == pTestedPed) + result = true; + if (pTestedPed->bInVehicle && pPed->m_lastDamEntity == pTestedPed->m_pMyVehicle) + result = true; + } + }else + debug("HAS_CHAR_BEEN_DAMAGED_BY_CHAR - First character doesn't exist\n"); + UpdateCompareFlag(result); + return 0; + } + //case COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_CAR: + //case COMMAND_HAS_CAR_BEEN_DAMAGED_BY_CHAR: + //case COMMAND_HAS_CAR_BEEN_DAMAGED_BY_CAR: + //case COMMAND_GET_RADIO_CHANNEL: + //case COMMAND_DISPLAY_TEXT_WITH_3_NUMBERS: + //case COMMAND_IS_CAR_DROWNING_IN_WATER: + case COMMAND_IS_CHAR_DROWNING_IN_WATER: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + UpdateCompareFlag(pPed && pPed->bIsDrowning); + return 0; + } + case COMMAND_DISABLE_CUTSCENE_SHADOWS: + { + CCutsceneMgr::DisableCutsceneShadows(); + return 0; + } + case COMMAND_HAS_GLASS_BEEN_SHATTERED_NEARBY: + { + CollectParameters(&m_nIp, 3); + + bool shattered = false; + if ( CGlass::HasGlassBeenShatteredAtCoors(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1], *(float*)&ScriptParams[2]) ) + shattered = true; + + UpdateCompareFlag(shattered); + return 0; + } + case COMMAND_ATTACH_CUTSCENE_OBJECT_TO_BONE: + { + CollectParameters(&m_nIp, 3); + CCutsceneMgr::AttachObjectToBone(CPools::GetObjectPool()->GetAt(ScriptParams[0]), CPools::GetObjectPool()->GetAt(ScriptParams[1]), ScriptParams[2]); + return 0; + } + case COMMAND_ATTACH_CUTSCENE_OBJECT_TO_COMPONENT: + { + CollectParameters(&m_nIp, 2); + CObject *obj1 = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + CObject *obj2 = CPools::GetObjectPool()->GetAt(ScriptParams[1]); + + char key[KEY_LENGTH_IN_SCRIPT]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, key); + m_nIp += KEY_LENGTH_IN_SCRIPT; + + CCutsceneMgr::AttachObjectToFrame(obj1, obj2, key); + return 0; + } + case COMMAND_SET_CHAR_STAY_IN_CAR_WHEN_JACKED: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bStayInCarOnJack = ScriptParams[1]; + return 0; + } + //case COMMAND_IS_MISSION_AUDIO_LOADING: + case COMMAND_ADD_MONEY_SPENT_ON_WEAPONS: + CollectParameters(&m_nIp, 1); + CStats::MoneySpentOnWeapons(ScriptParams[0]); + return 0; + case COMMAND_ADD_MONEY_SPENT_ON_PROPERTY: + CollectParameters(&m_nIp, 1); + CStats::MoneySpentOnProperty(ScriptParams[0]); + return 0; + //case COMMAND_ADD_MONEY_SPENT_ON_AUTO_PAINTING: + case COMMAND_SET_CHAR_ANSWERING_MOBILE: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + if (ScriptParams[1]) + pPed->SetAnswerMobile(); + else + pPed->ClearAnswerMobile(); + return 0; + } + case COMMAND_SET_PLAYER_DRUNKENNESS: + { + CollectParameters(&m_nIp, 2); + CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; + pPlayerInfo->m_pPed->m_nDrunkenness = ScriptParams[1]; + pPlayerInfo->m_pPed->m_nFadeDrunkenness = 0; + if (pPlayerInfo->m_pPed->m_nDrunkenness == 0) + CMBlur::ClearDrunkBlur(); + return 0; + } + //case COMMAND_GET_PLAYER_DRUNKENNESS: + //case COMMAND_SET_PLAYER_DRUG_LEVEL: + //case COMMAND_GET_PLAYER_DRUG_LEVEL: + //case COMMAND_ADD_LOAN_SHARK_VISITS: + case COMMAND_ADD_STORES_KNOCKED_OFF: + CollectParameters(&m_nIp, 1); + CStats::NumOfStoresKnockedOff(ScriptParams[0]); + return 0; + //case COMMAND_ADD_MOVIE_STUNTS: + case COMMAND_ADD_NUMBER_OF_ASSASSINATIONS: + CollectParameters(&m_nIp, 1); + CStats::NumOfAssassinations(ScriptParams[0]); + return 0; + case COMMAND_ADD_PIZZAS_DELIVERED: + CollectParameters(&m_nIp, 1); + CStats::NumOfPizzasDelivered(ScriptParams[0]); + return 0; + //case COMMAND_ADD_GARBAGE_PICKUPS: + case COMMAND_ADD_ICE_CREAMS_SOLD: + CollectParameters(&m_nIp, 1); + CStats::NumOfIceCreamSold(ScriptParams[0]); + return 0; + //case COMMAND_SET_TOP_SHOOTING_RANGE_SCORE: + //case COMMAND_ADD_SHOOTING_RANGE_RANK: + //case COMMAND_ADD_MONEY_SPENT_ON_GAMBLING: + //case COMMAND_ADD_MONEY_WON_ON_GAMBLING: + //case COMMAND_SET_LARGEST_GAMBLING_WIN: + case COMMAND_SET_CHAR_IN_PLAYERS_GROUP_CAN_FIGHT: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bDontFight = !ScriptParams[1]; + return 0; + } + case COMMAND_CLEAR_CHAR_WAIT_STATE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->ClearWaitState(); + return 0; + } + case COMMAND_GET_RANDOM_CAR_OF_TYPE_IN_AREA_NO_SAVE: + { + CollectParameters(&m_nIp, 5); + int handle = -1; + uint32 i = CPools::GetVehiclePool()->GetSize(); + float infX = *(float*)&ScriptParams[0]; + float infY = *(float*)&ScriptParams[1]; + float supX = *(float*)&ScriptParams[2]; + float supY = *(float*)&ScriptParams[3]; + while (i--) { + CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); + if (!pVehicle) + continue; + if (pVehicle->GetVehicleAppearance() != VEHICLE_APPEARANCE_CAR && pVehicle->GetVehicleAppearance() != VEHICLE_APPEARANCE_BIKE) + continue; + if (ScriptParams[4] != pVehicle->GetModelIndex() && ScriptParams[4] >= 0) + continue; + if (pVehicle->VehicleCreatedBy != RANDOM_VEHICLE) + continue; + if (!pVehicle->IsWithinArea(infX, infY, supX, supY)) + continue; + handle = CPools::GetVehiclePool()->GetIndex(pVehicle); + } + ScriptParams[0] = handle; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_SET_CAN_BURST_CAR_TYRES: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + pVehicle->bTyresDontBurst = !ScriptParams[1]; + return 0; + } + case COMMAND_SET_PLAYER_AUTO_AIM: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + pPed->bDoomAim = ScriptParams[1]; + return 0; + } + case COMMAND_FIRE_HUNTER_GUN: + { + CollectParameters(&m_nIp, 1); + CVehicle *pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + if (CTimer::GetTimeInMilliseconds() > pVehicle->m_nGunFiringTime + 150) { + CWeapon gun(WEAPONTYPE_HELICANNON, 5000); + CVector worldGunPos = (pVehicle->GetMatrix() * vecHunterGunPos) + (CTimer::GetTimeStep() * pVehicle->m_vecMoveSpeed); + gun.FireInstantHit(pVehicle, &worldGunPos); + gun.AddGunshell(pVehicle, worldGunPos, CVector2D(0.f, 0.1f), 0.025f); + DMAudio.PlayOneShot(pVehicle->m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.f); + pVehicle->m_nGunFiringTime = CTimer::GetTimeInMilliseconds(); + } + return 0; + } + case COMMAND_SET_PROPERTY_AS_OWNED: + CollectParameters(&m_nIp, 1); + CStats::AddPropertyAsOwned(ScriptParams[0]); + return 0; + case COMMAND_ADD_BLOOD_RING_KILLS: + CollectParameters(&m_nIp, 1); + CStats::AddNumBloodRingKills(ScriptParams[0]); + return 0; + case COMMAND_SET_LONGEST_TIME_IN_BLOOD_RING: + CollectParameters(&m_nIp, 1); + CStats::LongestTimeInBloodRing(ScriptParams[0]); + return 0; + case COMMAND_REMOVE_EVERYTHING_FOR_HUGE_CUTSCENE: + { + CCutsceneMgr::RemoveEverythingFromTheWorldForTheBiggestFuckoffCutsceneEver(); + return 0; + } + case COMMAND_IS_PLAYER_TOUCHING_VEHICLE: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + CPhysical* pTestedEntity = pPed; + if (pPed->bInVehicle && pPed->m_pMyVehicle) + pTestedEntity = pPed->m_pMyVehicle; + UpdateCompareFlag(pTestedEntity->GetHasCollidedWith(pVehicle)); + return 0; + } + //case COMMAND_IS_CHAR_TOUCHING_VEHICLE: + case COMMAND_CHECK_FOR_PED_MODEL_AROUND_PLAYER: + { + CollectParameters(&m_nIp, 6); + CVector d1 = CWorld::Players[ScriptParams[0]].GetPos() - *(CVector*)&ScriptParams[1]; + CVector d2 = CWorld::Players[ScriptParams[0]].GetPos() + *(CVector*)&ScriptParams[1]; + int i = CPools::GetPedPool()->GetSize(); + bool result = false; + while (i--) { + CPed* pPed = CPools::GetPedPool()->GetSlot(i); + if (!pPed) + continue; + if (ScriptParams[4] != pPed->GetModelIndex() && ScriptParams[5] != pPed->GetModelIndex()) + continue; + if (pPed->IsWithinArea(d1.x, d1.y, d1.z, d2.x, d2.y, d2.z)) + result = true; + } + UpdateCompareFlag(result); + return 0; + } + case COMMAND_CLEAR_CHAR_FOLLOW_PATH: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + if (pPed->GetPedState() == PED_FOLLOW_PATH) { + pPed->RestorePreviousState(); + pPed->ClearFollowPath(); + } + return 0; + } + case COMMAND_SET_CHAR_CAN_BE_SHOT_IN_VEHICLE: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bCanBeShotInVehicle = ScriptParams[1]; + return 0; + } + case COMMAND_ATTACH_CUTSCENE_OBJECT_TO_VEHICLE: + { + CollectParameters(&m_nIp, 2); + CCutsceneMgr::AttachObjectToParent(CPools::GetObjectPool()->GetAt(ScriptParams[0]), CPools::GetVehiclePool()->GetAt(ScriptParams[1])); + return 0; + } + case COMMAND_LOAD_MISSION_TEXT: + { + char key[8]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, key); + m_nIp += KEY_LENGTH_IN_SCRIPT; + TheText.LoadMissionText(key); + return 0; + } + case COMMAND_SET_TONIGHTS_EVENT: + { + CollectParameters(&m_nIp, 1); + CScrollBar::TonightsEvent = ScriptParams[0]; + return 0; + } + case COMMAND_CLEAR_CHAR_LAST_DAMAGE_ENTITY: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + if (pPed) + pPed->m_lastDamEntity = nil; + else + debug("CLEAR_CHAR_LAST_DAMAGE_ENTITY - Character doesn't exist\n"); + return 0; + } + //case COMMAND_CLEAR_CAR_LAST_DAMAGE_ENTITY: + case COMMAND_FREEZE_OBJECT_POSITION: + { + CollectParameters(&m_nIp, 2); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + pObject->bIsFrozen = ScriptParams[1]; + pObject->bInfiniteMass = ScriptParams[1]; + return 0; + } + case COMMAND_SET_PLAYER_HAS_MET_DEBBIE_HARRY: + { + CollectParameters(&m_nIp, 1); + CTheScripts::bPlayerHasMetDebbieHarry = ScriptParams[0]; + return 0; + } + case COMMAND_SET_RIOT_INTENSITY: + { + CollectParameters(&m_nIp, 1); + CTheScripts::RiotIntensity = ScriptParams[0]; + return 0; + } + //case COMMAND_IS_CAR_IN_ANGLED_AREA_2D: + //case COMMAND_IS_CAR_IN_ANGLED_AREA_3D: + //case COMMAND_REMOVE_WEAPON_FROM_CHAR: + case COMMAND_SET_UP_TAXI_SHORTCUT: + { + CollectParameters(&m_nIp, 8); + CGameLogic::SetUpShortCut( + *(CVector*)&ScriptParams[0], *(float*)&ScriptParams[3], + *(CVector*)&ScriptParams[4], *(float*)&ScriptParams[7]); + return 0; + } + case COMMAND_CLEAR_TAXI_SHORTCUT: + CGameLogic::ClearShortCut(); + return 0; + //case COMMAND_SET_CHAR_OBJ_GOTO_CAR_ON_FOOT: + //case COMMAND_GET_CLOSEST_WATER_NODE: + case COMMAND_ADD_PORN_LEAFLET_TO_RUBBISH: + CollectParameters(&m_nIp, 1); + CStats::PamphletMissionPassed = ScriptParams[0]; + return 0; + case COMMAND_CREATE_CLOTHES_PICKUP: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; + CPickups::GetActualPickupIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + ScriptParams[0] = CPickups::GenerateNewOne(pos, MI_PICKUP_CLOTHES, PICKUP_ON_STREET, ScriptParams[3]); + StoreParameters(&m_nIp, 1); + return 0; + } + //case COMMAND_CHANGE_BLIP_THRESHOLD: + case COMMAND_MAKE_PLAYER_FIRE_PROOF: + { + CollectParameters(&m_nIp, 2); + CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; + pPlayerInfo->m_bFireproof = ScriptParams[1]; + return 0; + } + case COMMAND_INCREASE_PLAYER_MAX_HEALTH: + { + CollectParameters(&m_nIp, 2); + CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; + pPlayerInfo->m_nMaxHealth += ScriptParams[1]; + pPlayerInfo->m_pPed->m_fHealth = pPlayerInfo->m_nMaxHealth; + return 0; + } + case COMMAND_INCREASE_PLAYER_MAX_ARMOUR: + { + CollectParameters(&m_nIp, 2); + CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; + pPlayerInfo->m_nMaxArmour += ScriptParams[1]; + pPlayerInfo->m_pPed->m_fArmour = pPlayerInfo->m_nMaxArmour; + return 0; + } + case COMMAND_CREATE_RANDOM_CHAR_AS_DRIVER: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + CPed* pPed = CPopulation::AddPedInCar(pVehicle, true); + pPed->CharCreatedBy = MISSION_CHAR; + pPed->bRespondsToThreats = false; + pPed->bAllowMedicsToReviveMe = false; + pPed->bIsPlayerFriend = false; + if (pVehicle->bIsBus) + pPed->bRenderPedInCar = false; + pPed->SetPosition(pVehicle->GetPosition()); + pPed->SetOrientation(0.0f, 0.0f, 0.0f); + pPed->SetPedState(PED_DRIVING); + pPed->m_pMyVehicle = pVehicle; + pPed->m_pMyVehicle->RegisterReference((CEntity**)&pPed->m_pMyVehicle); + pVehicle->pDriver = pPed; + pVehicle->pDriver->RegisterReference((CEntity**)&pVehicle->pDriver); + pPed->bInVehicle = true; + pVehicle->SetStatus(STATUS_PHYSICS); + if (pVehicle->m_vehType == VEHICLE_TYPE_BOAT) + pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; + pVehicle->bEngineOn = true; + pVehicle->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pVehicle->GetPosition()); + CPopulation::ms_nTotalMissionPeds++; + ScriptParams[0] = CPools::GetPedPool()->GetIndex(pPed); + StoreParameters(&m_nIp, 1); + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.AddEntityToList(ScriptParams[0], CLEANUP_CHAR); + return 0; + } + case COMMAND_CREATE_RANDOM_CHAR_AS_PASSENGER: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + CPed* pPed = CPopulation::AddPedInCar(pVehicle, false); + pPed->CharCreatedBy = MISSION_CHAR; + pPed->bRespondsToThreats = false; + pPed->bAllowMedicsToReviveMe = false; + pPed->bIsPlayerFriend = false; + if (pVehicle->bIsBus) + pPed->bRenderPedInCar = false; + pPed->SetPosition(pVehicle->GetPosition()); + pPed->SetOrientation(0.0f, 0.0f, 0.0f); + CPopulation::ms_nTotalMissionPeds++; + pVehicle->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pVehicle->GetPosition()); + if (ScriptParams[1] >= 0) + pVehicle->AddPassenger(pPed, ScriptParams[1]); + else + pVehicle->AddPassenger(pPed); + + pPed->m_pMyVehicle = pVehicle; + pPed->m_pMyVehicle->RegisterReference((CEntity**)&pPed->m_pMyVehicle); + pPed->bInVehicle = true; + pPed->SetPedState(PED_DRIVING); + ScriptParams[0] = CPools::GetPedPool()->GetIndex(pPed); + StoreParameters(&m_nIp, 1); + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.AddEntityToList(ScriptParams[0], CLEANUP_CHAR); + return 0; + } + case COMMAND_SET_CHAR_IGNORE_THREATS_BEHIND_OBJECTS: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bIgnoreThreatsBehindObjects = ScriptParams[1]; + return 0; + } + case COMMAND_ENSURE_PLAYER_HAS_DRIVE_BY_WEAPON: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + if (pPed->bInVehicle) { + if (pPed->GetWeapon(WEAPONSLOT_SUBMACHINEGUN).m_eWeaponType) { + if (pPed->GetWeapon(WEAPONSLOT_SUBMACHINEGUN).m_nAmmoTotal < ScriptParams[1]) + pPed->SetAmmo(pPed->GetWeapon(WEAPONSLOT_SUBMACHINEGUN).m_eWeaponType, ScriptParams[1]); + } + else { + pPed->GiveWeapon(WEAPONTYPE_UZI, ScriptParams[1], true); + if (pPed->m_storedWeapon == WEAPONTYPE_UNIDENTIFIED) + pPed->m_storedWeapon = pPed->GetWeapon()->m_eWeaponType; + pPed->SetCurrentWeapon(WEAPONTYPE_UZI); + } + } + return 0; + } + case COMMAND_MAKE_HELI_COME_CRASHING_DOWN: + { + CollectParameters(&m_nIp, 1); + CAutomobile* pHeli = (CAutomobile*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pHeli && pHeli->IsCar() && pHeli->IsRealHeli()); + pHeli->bHeliDestroyed = true; + return 0; + } + case COMMAND_ADD_EXPLOSION_NO_SOUND: + { + CollectParameters(&m_nIp, 4); + CExplosion::AddExplosion(nil, nil, (eExplosionType)ScriptParams[3], *(CVector*)&ScriptParams[0], 0, false); + return 0; + } + case COMMAND_SET_OBJECT_AREA_VISIBLE: + { + CollectParameters(&m_nIp, 2); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + script_assert(pObject); + pObject->m_area = ScriptParams[1]; + return 0; + } + //case COMMAND_WAS_VEHICLE_EVER_POLICE: + case COMMAND_SET_CHAR_NEVER_TARGETTED: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bNeverEverTargetThisPed = ScriptParams[1]; + return 0; + } + case COMMAND_LOAD_UNCOMPRESSED_ANIM: + { + char key[KEY_LENGTH_IN_SCRIPT]; + CTheScripts::ReadTextLabelFromScript(&m_nIp, key); + m_nIp += KEY_LENGTH_IN_SCRIPT; + CCutsceneMgr::LoadAnimationUncompressed(key); + return 0; + } + case COMMAND_WAS_CUTSCENE_SKIPPED: + { + UpdateCompareFlag(CCutsceneMgr::WasCutsceneSkipped()); + return 0; + } + case COMMAND_SET_CHAR_CROUCH_WHEN_THREATENED: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bCrouchWhenScared = true; + return 0; + } + case COMMAND_IS_CHAR_IN_ANY_POLICE_VEHICLE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle && + pPed->m_pMyVehicle->IsLawEnforcementVehicle() && + pPed->m_pMyVehicle->GetModelIndex() != MI_PREDATOR); + return 0; + } + case COMMAND_DOES_CHAR_EXIST: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CPools::GetPedPool()->GetAt(ScriptParams[0]) != 0); + return 0; + //case COMMAND_DOES_VEHICLE_EXIST: + //case COMMAND_ADD_SHORT_RANGE_BLIP_FOR_CONTACT_POINT: + case COMMAND_ADD_SHORT_RANGE_SPRITE_BLIP_FOR_CONTACT_POINT: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + int id = CRadar::SetShortRangeCoordBlip(BLIP_COORD, pos, 2, BLIP_DISPLAY_BOTH); + CRadar::SetBlipSprite(id, ScriptParams[3]); + ScriptParams[0] = id; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_IS_CHAR_STUCK: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(pPed->m_nWaitState == WAITSTATE_STUCK); + return 0; + } + case COMMAND_SET_ALL_TAXIS_HAVE_NITRO: + { + CollectParameters(&m_nIp, 1); + CVehicle::bAllTaxisHaveNitro = ScriptParams[0] != 0; + return 0; + } + case COMMAND_SET_CHAR_STOP_SHOOT_DONT_SEEK_ENTITY: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + if (ScriptParams[1]) { + pPed->bKindaStayInSamePlace = true; + pPed->bStopAndShoot = true; + } + else { + pPed->bKindaStayInSamePlace = false; + pPed->bStopAndShoot = false; + } + pPed->m_nLastPedState = PED_NONE; + return 0; + } + case COMMAND_FREEZE_CAR_POSITION_AND_DONT_LOAD_COLLISION: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + if (ScriptParams[1]) { + pVehicle->bIsFrozen = true; + pVehicle->bInfiniteMass = true; + if (m_bIsMissionScript) { + CWorld::Remove(pVehicle); + pVehicle->bIsStaticWaitingForCollision = true; + CWorld::Add(pVehicle); + } + } + else { + pVehicle->bIsFrozen = false; + pVehicle->bInfiniteMass = false; + } + return 0; + } + //case COMMAND_FREEZE_CHAR_POSITION_AND_DONT_LOAD_COLLISION: + //case COMMAND_FREEZE_OBJECT_POSITION_AND_DONT_LOAD_COLLISION: + //case COMMAND_SET_FADE_AND_JUMPCUT_AFTER_RC_EXPLOSION: + default: + script_assert(0); + } + return -1; +} diff --git a/src/miami/control/Script8.cpp b/src/miami/control/Script8.cpp new file mode 100644 index 00000000..98f69737 --- /dev/null +++ b/src/miami/control/Script8.cpp @@ -0,0 +1,618 @@ +#include "common.h" + +#include "Script.h" +#include "ScriptCommands.h" + +#include "DMAudio.h" +#if ((defined GTAVC_JP_PATCH || defined SUPPORT_JAPANESE_SCRIPT) && defined MORE_LANGUAGES) +#include "Frontend.h" +#endif +#include "GameLogic.h" +#include "Garages.h" +#ifdef MISSION_REPLAY +#include "GenericGameStorage.h" +#endif +#if (defined GTA_PC && !defined GTAVC_JP_PATCH || defined GTA_XBOX || defined SUPPORT_XBOX_SCRIPT || defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) +#include "General.h" +#include "maths.h" +#endif +#include "Hud.h" +#include "Pad.h" +#include "PedAttractor.h" +#include "Population.h" +#include "Pools.h" +#include "RpAnimBlend.h" +#include "Stats.h" +#include "VisibilityPlugins.h" +#include "Wanted.h" +#include "WaterLevel.h" +#include "World.h" +#include "Zones.h" + +int8 CRunningScript::ProcessCommands1400To1499(int32 command) +{ + switch (command) { + case COMMAND_REGISTER_VIGILANTE_LEVEL: + CollectParameters(&m_nIp, 1); + CStats::RegisterLevelVigilanteMission(ScriptParams[0]); + return 0; + case COMMAND_CLEAR_ALL_CHAR_ANIMS: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + if (!pPed->bInVehicle) { + pPed->m_pVehicleAnim = nil; + pPed->RestartNonPartialAnims(); + RpAnimBlendClumpRemoveAllAssociations(pPed->GetClump()); + pPed->SetPedState(PED_IDLE); + pPed->SetMoveState(PEDMOVE_STILL); + pPed->m_nLastPedState = PED_NONE; + pPed->ClearAimFlag(); + pPed->ClearLookFlag(); + pPed->bIsPointingGunAt = false; + if (pPed->IsPlayer()) + ((CPlayerPed*)pPed)->m_fMoveSpeed = 0.0f; + else + pPed->m_nStoredMoveState = PEDMOVE_STILL; + CAnimManager::AddAnimation(pPed->GetClump(), pPed->m_animGroup, ANIM_STD_IDLE); + pPed->bIsPedDieAnimPlaying = false; + } + return 0; + } + case COMMAND_SET_MAXIMUM_NUMBER_OF_CARS_IN_GARAGE: + CollectParameters(&m_nIp, 2); + CGarages::SetMaxNumStoredCarsForGarage(ScriptParams[0], ScriptParams[1]); + return 0; + case COMMAND_WANTED_STARS_ARE_FLASHING: + { + CWanted* pWanted = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted; + UpdateCompareFlag(pWanted->m_nMinWantedLevel - pWanted->GetWantedLevel() > 0); + return 0; + } + case COMMAND_SET_ALLOW_HURRICANES: + CollectParameters(&m_nIp, 1); + CStats::NoMoreHurricanes = ScriptParams[0]; + return 0; + case COMMAND_PLAY_ANNOUNCEMENT: + { + CollectParameters(&m_nIp, 1); + DMAudio.PlayRadioAnnouncement(ScriptParams[0] + STREAMED_SOUND_ANNOUNCE_BRIDGE_CLOSED); + return 0; + } + case COMMAND_SET_PLAYER_IS_IN_STADIUM: + { + CollectParameters(&m_nIp, 1); + CTheScripts::bPlayerIsInTheStatium = ScriptParams[0]; + return 0; + } + case COMMAND_GET_BUS_FARES_COLLECTED_BY_PLAYER: + { + CollectParameters(&m_nIp, 1); + CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; + ScriptParams[0] = pPlayerInfo->m_pPed->m_nLastBusFareCollected; + pPlayerInfo->m_pPed->m_nLastBusFareCollected = 0; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_SET_CHAR_OBJ_BUY_ICE_CREAM: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); + script_assert(pVehicle); + ScriptParams[0] = 0; + if (pPed->m_objective == OBJECTIVE_NONE && !pPed->bHasAlreadyUsedAttractor) { + C2dEffect* pEffect = (C2dEffect*)GetPedAttractorManager()->GetEffectForIceCreamVan(pVehicle, pPed->GetPosition()); // has to be casted, because inner methods are const + if (pEffect) { + CVector pos; + CPedAttractorManager::ComputeEffectPos(pEffect, pVehicle->GetMatrix(), pos); + if ((pPed->GetPosition() - pos).MagnitudeSqr() < SQR(20.0f)) { + if (GetPedAttractorManager()->HasEmptySlot(pEffect) && GetPedAttractorManager()->IsApproachable(pEffect, pVehicle->GetMatrix(), 0, pPed)) { + if (GetPedAttractorManager()->RegisterPedWithAttractor(pPed, pEffect, pVehicle->GetMatrix())) + ScriptParams[0] = 1; + } + } + } + } + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_DISPLAY_RADAR: + CollectParameters(&m_nIp, 1); + CHud::m_HideRadar = ScriptParams[0] == 0; + return 0; + case COMMAND_REGISTER_BEST_POSITION: + CollectParameters(&m_nIp, 2); + CStats::RegisterBestPosition(ScriptParams[0], ScriptParams[1]); + return 0; + case COMMAND_IS_PLAYER_IN_INFO_ZONE: + { + CollectParameters(&m_nIp, 1); + CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; + char key[KEY_LENGTH_IN_SCRIPT]; + memset(key, 0, KEY_LENGTH_IN_SCRIPT); + CTheScripts::ReadTextLabelFromScript(&m_nIp, key); + m_nIp += KEY_LENGTH_IN_SCRIPT; + CVector pos = pPlayerInfo->GetPos(); + CZone* infoZone = CTheZones::FindInformationZoneForPosition(&pos); + UpdateCompareFlag(strncmp(key, infoZone->name, 8) == 0); // original code doesn't seem to be using strncmp in here and compare 2 ints instead + return 0; + } + case COMMAND_CLEAR_CHAR_ICE_CREAM_PURCHASE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + if (pPed->m_attractor) + GetPedAttractorManager()->DeRegisterPed(pPed, pPed->m_attractor); + return 0; + } + case COMMAND_IS_IN_CAR_FIRE_BUTTON_PRESSED: + UpdateCompareFlag(CPad::GetPad(0)->GetCarGunFired()); + return 0; + case COMMAND_HAS_CHAR_ATTEMPTED_ATTRACTOR: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(pPed->bHasAlreadyUsedAttractor); + return 0; + } + case COMMAND_SET_LOAD_COLLISION_FOR_CAR_FLAG: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + if (ScriptParams[1]) { + pVehicle->bDontLoadCollision = false; + if (m_bMissionFlag) { + CWorld::Remove(pVehicle); + pVehicle->bIsStaticWaitingForCollision = true; + CWorld::Add(pVehicle); + } + } + else { + pVehicle->bDontLoadCollision = true; + if (pVehicle->bIsStaticWaitingForCollision) { + pVehicle->bIsStaticWaitingForCollision = false; + if (!pVehicle->GetIsStatic()) + pVehicle->AddToMovingList(); + } + } + return 0; + } + case COMMAND_SET_LOAD_COLLISION_FOR_CHAR_FLAG: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + if (ScriptParams[1]) { + pPed->bDontLoadCollision = false; + if (m_bMissionFlag) { + CWorld::Remove(pPed); + pPed->bIsStaticWaitingForCollision = true; + CWorld::Add(pPed); + } + } + else { + pPed->bDontLoadCollision = true; + if (pPed->bIsStaticWaitingForCollision) { + pPed->bIsStaticWaitingForCollision = false; + if (!pPed->GetIsStatic()) + pPed->AddToMovingList(); + } + } + return 0; + } + //case COMMAND_SET_LOAD_COLLISION_FOR_OBJECT_FLAG: + case COMMAND_ADD_BIG_GUN_FLASH: + { + CollectParameters(&m_nIp, 6); + CWeapon::AddGunFlashBigGuns(*(CVector*)&ScriptParams[0], *(CVector*)&ScriptParams[3]); + return 0; + } + case COMMAND_HAS_CHAR_BOUGHT_ICE_CREAM: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(pPed->bBoughtIceCream); + return 0; + } + case COMMAND_GET_PROGRESS_PERCENTAGE: + *(float*)&ScriptParams[0] = CStats::GetPercentageProgress(); + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_SET_SHORTCUT_PICKUP_POINT: + { + CollectParameters(&m_nIp, 4); + CGameLogic::AddShortCutPointAfterDeath(*(CVector*)&ScriptParams[0], *(float*)&ScriptParams[3]); + return 0; + } + case COMMAND_SET_SHORTCUT_DROPOFF_POINT_FOR_MISSION: + { + CollectParameters(&m_nIp, 4); + CGameLogic::AddShortCutDropOffPointForMission(*(CVector*)&ScriptParams[0], *(float*)&ScriptParams[3]); + return 0; + } + case COMMAND_GET_RANDOM_ICE_CREAM_CUSTOMER_IN_AREA: + { + CollectParameters(&m_nIp, 7); + int ped_handle = -1; + CVector pos = FindPlayerCoors(); + float x1 = *(float*)&ScriptParams[0]; + float y1 = *(float*)&ScriptParams[1]; + float x2 = *(float*)&ScriptParams[2]; + float y2 = *(float*)&ScriptParams[3]; + int i = CPools::GetPedPool()->GetSize(); + while (--i && ped_handle == -1) { + CPed* pPed = CPools::GetPedPool()->GetSlot(i); + if (!pPed) + continue; + if (CTheScripts::LastRandomPedId == CPools::GetPedPool()->GetIndex(pPed)) + continue; + if (pPed->CharCreatedBy != RANDOM_CHAR) + continue; + if (!pPed->IsPedInControl()) + continue; + if (pPed->bRemoveFromWorld) + continue; + if (pPed->bFadeOut) + continue; + if (pPed->m_nWaitState != WAITSTATE_FALSE) + continue; + if (pPed->bHasAlreadyUsedAttractor) + continue; + if (pPed->m_attractor) + continue; + if (!ThisIsAValidRandomPed(pPed->m_nPedType, ScriptParams[4], ScriptParams[5], ScriptParams[6])) + continue; + if (pPed->bIsLeader || pPed->m_leader) + continue; + if (!pPed->IsWithinArea(x1, y1, x2, y2)) + continue; + if (pos.z - PED_FIND_Z_OFFSET > pPed->GetPosition().z) + continue; + if (pos.z + PED_FIND_Z_OFFSET < pPed->GetPosition().z) + continue; + ped_handle = CPools::GetPedPool()->GetIndex(pPed); + CTheScripts::LastRandomPedId = ped_handle; + pPed->CharCreatedBy = MISSION_CHAR; + pPed->bRespondsToThreats = false; + ++CPopulation::ms_nTotalMissionPeds; + if (m_bIsMissionScript) + CTheScripts::MissionCleanUp.AddEntityToList(ped_handle, CLEANUP_CHAR); + } + ScriptParams[0] = ped_handle; + StoreParameters(&m_nIp, 1); + return 0; + } + //case COMMAND_GET_RANDOM_ICE_CREAM_CUSTOMER_IN_ZONE: + case COMMAND_UNLOCK_ALL_CAR_DOORS_IN_AREA: + { + CollectParameters(&m_nIp, 4); + uint32 i = CPools::GetVehiclePool()->GetSize(); + float infX = *(float*)&ScriptParams[0]; + float infY = *(float*)&ScriptParams[1]; + float supX = *(float*)&ScriptParams[2]; + float supY = *(float*)&ScriptParams[3]; + while (i--) { + CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); + if (!pVehicle) + continue; + if (pVehicle->IsWithinArea(infX, infY, supX, supY)) + pVehicle->m_nDoorLock = CARLOCK_UNLOCKED; + } + return 0; + } + case COMMAND_SET_GANG_ATTACK_PLAYER_WITH_COPS: + CollectParameters(&m_nIp, 2); + CGangs::SetWillAttackPlayerWithCops((ePedType)((int)PEDTYPE_GANG1 + ScriptParams[0]), !!ScriptParams[1]); + return 0; + case COMMAND_SET_CHAR_FRIGHTENED_IN_JACKED_CAR: + { + CollectParameters(&m_nIp, 2); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + pPed->bHeldHostageInCar = ScriptParams[1]; + return 0; + } + case COMMAND_SET_VEHICLE_TO_FADE_IN: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + script_assert(pVehicle); + CVisibilityPlugins::SetClumpAlpha(pVehicle->GetClump(), ScriptParams[1]); + return 0; + } + case COMMAND_REGISTER_ODDJOB_MISSION_PASSED: + ++CStats::MissionsPassed; + CStats::CheckPointReachedSuccessfully(); + CTheScripts::LastMissionPassedTime = CTimer::GetTimeInMilliseconds(); + CGameLogic::RemoveShortCutDropOffPointForMission(); + return 0; + case COMMAND_IS_PLAYER_IN_SHORTCUT_TAXI: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + script_assert(pPed); + UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle && pPed->m_pMyVehicle == CGameLogic::pShortCutTaxi); + return 0; + } + case COMMAND_IS_CHAR_DUCKING: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + script_assert(pPed); + UpdateCompareFlag(RpAnimBlendClumpGetAssociation(pPed->GetClump(), ANIM_STD_DUCK_DOWN) != nil); + return 0; + } + case COMMAND_CREATE_DUST_EFFECT_FOR_CUTSCENE_HELI: + { + CollectParameters(&m_nIp, 3); + CObject* pHeli = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + bool found = false; + float waterLevel = -1000.0f; + CVector pos = pHeli->GetPosition(); + float radius = *(float*)&ScriptParams[1]; + float ground = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &found); + if (!CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &waterLevel, false)) + waterLevel = 0.0f; + if (waterLevel > ground) + ground = waterLevel; + if (ScriptParams[2] > 8) + ScriptParams[2] = 8; + CVehicle::HeliDustGenerate(pHeli, (pos.z - ground - 1.0f - radius) * 0.3 + radius, ground, ScriptParams[2]); + return 0; + } + case COMMAND_REGISTER_FIRE_LEVEL: + CollectParameters(&m_nIp, 1); + CStats::RegisterLevelFireMission(ScriptParams[0]); + return 0; + case COMMAND_IS_AUSTRALIAN_GAME: + UpdateCompareFlag(false); // should we make some check? + return 0; + case COMMAND_DISARM_CAR_BOMB: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + if (pVehicle->m_bombType != CARBOMB_NONE) { + pVehicle->m_bombType = CARBOMB_NONE; + pVehicle->m_pBombRigger = nil; + } + return 0; + } +#if (defined GTAVC_JP_PATCH || defined SUPPORT_JAPANESE_SCRIPT) + case COMMAND_IS_JAPANESE_GAME: +#ifdef MORE_LANGUAGES + UpdateCompareFlag(FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_JAPANESE); +#elif (defined GTAVC_JP_PATCH) + UpdateCompareFlag(true); +#else + UpdateCompareFlag(false); +#endif + return 0; +#elif (!defined GTA_PS2) + case COMMAND_SET_ONSCREEN_COUNTER_FLASH_WHEN_FIRST_DISPLAYED: + { + script_assert(CTheScripts::ScriptSpace[m_nIp++] == ARGUMENT_GLOBALVAR); + uint16 var = CTheScripts::Read2BytesFromScript(&m_nIp); + CollectParameters(&m_nIp, 1); + //CUserDisplay::OnscnTimer.SetCounterFlashWhenFirstDisplayed(var, ScriptParams[0]); + return 0; + } +#endif +#if (defined GTA_PC && !defined GTAVC_JP_PATCH || defined GTA_XBOX || defined SUPPORT_XBOX_SCRIPT || defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) + case COMMAND_SHUFFLE_CARD_DECKS: + { + CollectParameters(&m_nIp, 1); + script_assert(ScriptParams[0] >= 0 && ScriptParams[0] <= 6); + for (int i = 0; i < CARDS_IN_STACK; i++) + CTheScripts::CardStack[i] = 0; + int16 seq[CARDS_IN_STACK]; + for (int i = 0; i < MAX_DECKS * CARDS_IN_DECK; i++) + seq[i] = i; + int cards_left = CARDS_IN_DECK * ScriptParams[0]; + for (int k = 1; k < CARDS_IN_DECK + 1; k++) { + for (int deck = 0; deck < ScriptParams[0]; deck++) { + int index = CGeneral::GetRandomNumberInRange(0, cards_left); + CTheScripts::CardStack[seq[index]] = k; + for (int l = index; l < cards_left; l++) { + if (l + 1 < CARDS_IN_STACK) + seq[l] = seq[l + 1]; + else + seq[l] = 0; + } + --cards_left; + } + } + CTheScripts::CardStackPosition = 0; + return 0; + } + case COMMAND_FETCH_NEXT_CARD: + { + if (CTheScripts::CardStack[CTheScripts::CardStackPosition] == 0) + CTheScripts::CardStackPosition = 0; + ScriptParams[0] = CTheScripts::CardStack[CTheScripts::CardStackPosition++]; + if (CTheScripts::CardStackPosition == CARDS_IN_DECK * MAX_DECKS) + CTheScripts::CardStackPosition = 0; + StoreParameters(&m_nIp, 1); + return 0; + } + case COMMAND_GET_OBJECT_VELOCITY: + { + CollectParameters(&m_nIp, 1); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + *(CVector*)&ScriptParams[0] = GAME_SPEED_TO_METERS_PER_SECOND * pObject->GetMoveSpeed(); + StoreParameters(&m_nIp, 3); + return 0; + } + case COMMAND_IS_DEBUG_CAMERA_ON: + UpdateCompareFlag(TheCamera.WorldViewerBeingUsed); + return 0; + case COMMAND_ADD_TO_OBJECT_ROTATION_VELOCITY: + { + CollectParameters(&m_nIp, 4); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + CVector newSpeed = pObject->GetTurnSpeed() + *(CVector*)&ScriptParams[1] / GAME_SPEED_TO_METERS_PER_SECOND; + if (pObject->bIsStatic) { + pObject->SetIsStatic(false); + pObject->AddToMovingList(); + } + pObject->SetTurnSpeed(newSpeed.x, newSpeed.y, newSpeed.z); + return 0; + } + case COMMAND_SET_OBJECT_ROTATION_VELOCITY: + { + CollectParameters(&m_nIp, 4); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + CVector newSpeed = *(CVector*)&ScriptParams[1] / GAME_SPEED_TO_METERS_PER_SECOND; + if (pObject->bIsStatic) { + pObject->SetIsStatic(false); + pObject->AddToMovingList(); + } + pObject->SetTurnSpeed(newSpeed.x, newSpeed.y, newSpeed.z); + return 0; + } + case COMMAND_IS_OBJECT_STATIC: + { + CollectParameters(&m_nIp, 1); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + UpdateCompareFlag(pObject->GetIsStatic()); + return 0; + } + case COMMAND_GET_ANGLE_BETWEEN_2D_VECTORS: + { + CollectParameters(&m_nIp, 4); + CVector2D v1 = *(CVector2D*)&ScriptParams[0]; + CVector2D v2 = *(CVector2D*)&ScriptParams[2]; + float c = DotProduct2D(v1, v2) / (v1.Magnitude() * v2.Magnitude()); +#ifdef FIX_BUGS // command is a SA leftover where it was fixed to this + *(float*)&ScriptParams[0] = RADTODEG(Acos(c)); +#else + *(float*)&ScriptParams[0] = Acos(c); +#endif + return 0; + } + case COMMAND_DO_2D_RECTANGLES_COLLIDE: + { + CollectParameters(&m_nIp, 8); + float infX1 = *(float*)&ScriptParams[0] - *(float*)&ScriptParams[2] * 0.5; // NB: not float + float supX1 = *(float*)&ScriptParams[0] + *(float*)&ScriptParams[2] * 0.5; + float infX2 = *(float*)&ScriptParams[4] - *(float*)&ScriptParams[6] * 0.5; + float supX2 = *(float*)&ScriptParams[4] + *(float*)&ScriptParams[6] * 0.5; + float infY1 = *(float*)&ScriptParams[1] - *(float*)&ScriptParams[3] * 0.5; + float supY1 = *(float*)&ScriptParams[1] + *(float*)&ScriptParams[3] * 0.5; + float infY2 = *(float*)&ScriptParams[5] - *(float*)&ScriptParams[7] * 0.5; + float supY2 = *(float*)&ScriptParams[5] + *(float*)&ScriptParams[7] * 0.5; + bool collide = true; + if (infY2 > supY1) + collide = false; + if (infY1 > supY2) + collide = false; + if (infX2 > supX1) + collide = false; + if (infX1 > supX2) + collide = false; + UpdateCompareFlag(collide); + return 0; + } + case COMMAND_GET_OBJECT_ROTATION_VELOCITY: + { + CollectParameters(&m_nIp, 1); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + *(CVector*)&ScriptParams[0] = pObject->GetTurnSpeed() * GAME_SPEED_TO_METERS_PER_SECOND; + StoreParameters(&m_nIp, 3); + return 0; + } + case COMMAND_ADD_VELOCITY_RELATIVE_TO_OBJECT_VELOCITY: + { + CollectParameters(&m_nIp, 4); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + CVector vecAddition = *(CVector*)&ScriptParams[1] * CTimer::GetTimeStep() / GAME_SPEED_TO_METERS_PER_SECOND; + if (!pObject->bIsStatic) { + CVector vecCurrSpeed = pObject->GetSpeed(); + vecCurrSpeed.Normalise(); + if (vecCurrSpeed.z != 1.0) { // NB: not float! + CVector vx = CrossProduct(vecCurrSpeed, CVector(0.0f, 0.0f, 1.0f)); + vx.Normalise(); + CVector vz = CrossProduct(vx, vecCurrSpeed); + vz.Normalise(); + CVector vecNewSpeed = pObject->GetSpeed() + vecAddition.x * vx + vecAddition.y * vecCurrSpeed + vecAddition.z * vecCurrSpeed; + if (pObject->bIsStatic) { + pObject->SetIsStatic(false); + pObject->AddToMovingList(); + } + pObject->SetMoveSpeed(vecNewSpeed); + } + } + return 0; + } + case COMMAND_GET_OBJECT_SPEED: + { + CollectParameters(&m_nIp, 1); + CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); + *(float*)&ScriptParams[0] = pObject->GetMoveSpeed().Magnitude() * GAME_SPEED_TO_METERS_PER_SECOND; + StoreParameters(&m_nIp, 1); + return 0; + } +#endif +#if (defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) + case COMMAND_IS_MISSION_SKIP: +#ifdef MISSION_REPLAY + ScriptParams[0] = MissionSkipLevel; +#else + ScriptParams[0] = 0; +#endif + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_SET_IN_AMMUNATION: + CollectParameters(&m_nIp, 1); +#ifdef MISSION_REPLAY + IsInAmmunation = ScriptParams[0]; +#endif + return 0; + case COMMAND_DO_SAVE_GAME: + CollectParameters(&m_nIp, 1); +#ifdef USE_MISSION_REPLAY_OVERRIDE_FOR_NON_MOBILE_SCRIPT + UsingMobileScript = true; +#endif +#ifdef MISSION_REPLAY + SaveGameForPause(ScriptParams[0]); +#endif + return 0; + case COMMAND_IS_RETRY: +#ifdef MISSION_REPLAY + if (strcmp(m_abScriptName, "porno4") != 0) + ScriptParams[0] = AllowMissionReplay; +#ifdef FIX_BUGS + else + ScriptParams[0] = gbTryingPorn4Again; +#else + else if (gbTryingPorn4Again) + ScriptParams[0] = 1; +#endif +#else + ScriptParams[0] = 0; +#endif + StoreParameters(&m_nIp, 1); + return 0; + case COMMAND_DUMMY: + return 0; +#endif +#if (defined GTA_XBOX || defined SUPPORT_XBOX_SCRIPT || defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) + // it is unknown what these commands do but they don't take parameters + case COMMAND_MARK_CUTSCENE_START: + return 0; + case COMMAND_MARK_CUTSCENE_END: + return 0; + case COMMAND_CUTSCENE_SCROLL: + return 0; +#endif + default: + script_assert(0); + } + return -1; +} diff --git a/src/miami/control/ScriptCommands.h b/src/miami/control/ScriptCommands.h new file mode 100644 index 00000000..9cb39e23 --- /dev/null +++ b/src/miami/control/ScriptCommands.h @@ -0,0 +1,1508 @@ +#pragma once + +enum { + COMMAND_NOP = 0, + COMMAND_WAIT, + COMMAND_GOTO, + COMMAND_SHAKE_CAM, + COMMAND_SET_VAR_INT, + COMMAND_SET_VAR_FLOAT, + COMMAND_SET_LVAR_INT, + COMMAND_SET_LVAR_FLOAT, + COMMAND_ADD_VAL_TO_INT_VAR, + COMMAND_ADD_VAL_TO_FLOAT_VAR, + COMMAND_ADD_VAL_TO_INT_LVAR, + COMMAND_ADD_VAL_TO_FLOAT_LVAR, + COMMAND_SUB_VAL_FROM_INT_VAR, + COMMAND_SUB_VAL_FROM_FLOAT_VAR, + COMMAND_SUB_VAL_FROM_INT_LVAR, + COMMAND_SUB_VAL_FROM_FLOAT_LVAR, + COMMAND_MULT_INT_VAR_BY_VAL, + COMMAND_MULT_FLOAT_VAR_BY_VAL, + COMMAND_MULT_INT_LVAR_BY_VAL, + COMMAND_MULT_FLOAT_LVAR_BY_VAL, + COMMAND_DIV_INT_VAR_BY_VAL, + COMMAND_DIV_FLOAT_VAR_BY_VAL, + COMMAND_DIV_INT_LVAR_BY_VAL, + COMMAND_DIV_FLOAT_LVAR_BY_VAL, + COMMAND_IS_INT_VAR_GREATER_THAN_NUMBER, + COMMAND_IS_INT_LVAR_GREATER_THAN_NUMBER, + COMMAND_IS_NUMBER_GREATER_THAN_INT_VAR, + COMMAND_IS_NUMBER_GREATER_THAN_INT_LVAR, + COMMAND_IS_INT_VAR_GREATER_THAN_INT_VAR, + COMMAND_IS_INT_LVAR_GREATER_THAN_INT_LVAR, + COMMAND_IS_INT_VAR_GREATER_THAN_INT_LVAR, + COMMAND_IS_INT_LVAR_GREATER_THAN_INT_VAR, + COMMAND_IS_FLOAT_VAR_GREATER_THAN_NUMBER, + COMMAND_IS_FLOAT_LVAR_GREATER_THAN_NUMBER, + COMMAND_IS_NUMBER_GREATER_THAN_FLOAT_VAR, + COMMAND_IS_NUMBER_GREATER_THAN_FLOAT_LVAR, + COMMAND_IS_FLOAT_VAR_GREATER_THAN_FLOAT_VAR, + COMMAND_IS_FLOAT_LVAR_GREATER_THAN_FLOAT_LVAR, + COMMAND_IS_FLOAT_VAR_GREATER_THAN_FLOAT_LVAR, + COMMAND_IS_FLOAT_LVAR_GREATER_THAN_FLOAT_VAR, + COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_NUMBER, + COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_NUMBER, + COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_INT_VAR, + COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_INT_LVAR, + COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_INT_VAR, + COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_INT_LVAR, + COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_INT_LVAR, + COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_INT_VAR, + COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_NUMBER, + COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_NUMBER, + COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_FLOAT_VAR, + COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_FLOAT_LVAR, + COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_FLOAT_VAR, + COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_FLOAT_LVAR, + COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_FLOAT_LVAR, + COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_FLOAT_VAR, + COMMAND_IS_INT_VAR_EQUAL_TO_NUMBER, + COMMAND_IS_INT_LVAR_EQUAL_TO_NUMBER, + COMMAND_IS_INT_VAR_EQUAL_TO_INT_VAR, + COMMAND_IS_INT_LVAR_EQUAL_TO_INT_LVAR, + COMMAND_IS_INT_VAR_EQUAL_TO_INT_LVAR, + COMMAND_IS_INT_VAR_NOT_EQUAL_TO_NUMBER, + COMMAND_IS_INT_LVAR_NOT_EQUAL_TO_NUMBER, + COMMAND_IS_INT_VAR_NOT_EQUAL_TO_INT_VAR, + COMMAND_IS_INT_LVAR_NOT_EQUAL_TO_INT_LVAR, + COMMAND_IS_INT_VAR_NOT_EQUAL_TO_INT_LVAR, + COMMAND_IS_FLOAT_VAR_EQUAL_TO_NUMBER, + COMMAND_IS_FLOAT_LVAR_EQUAL_TO_NUMBER, + COMMAND_IS_FLOAT_VAR_EQUAL_TO_FLOAT_VAR, + COMMAND_IS_FLOAT_LVAR_EQUAL_TO_FLOAT_LVAR, + COMMAND_IS_FLOAT_VAR_EQUAL_TO_FLOAT_LVAR, + COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_NUMBER, + COMMAND_IS_FLOAT_LVAR_NOT_EQUAL_TO_NUMBER, + COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_FLOAT_VAR, + COMMAND_IS_FLOAT_LVAR_NOT_EQUAL_TO_FLOAT_LVAR, + COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_FLOAT_LVAR, + COMMAND_GOTO_IF_TRUE, + COMMAND_GOTO_IF_FALSE, + COMMAND_TERMINATE_THIS_SCRIPT, + COMMAND_START_NEW_SCRIPT, + COMMAND_GOSUB, + COMMAND_RETURN, + COMMAND_LINE, + COMMAND_CREATE_PLAYER, + COMMAND_GET_PLAYER_COORDINATES, + COMMAND_SET_PLAYER_COORDINATES, + COMMAND_IS_PLAYER_IN_AREA_2D, + COMMAND_IS_PLAYER_IN_AREA_3D, + COMMAND_ADD_INT_VAR_TO_INT_VAR, + COMMAND_ADD_FLOAT_VAR_TO_FLOAT_VAR, + COMMAND_ADD_INT_LVAR_TO_INT_LVAR, + COMMAND_ADD_FLOAT_LVAR_TO_FLOAT_LVAR, + COMMAND_ADD_INT_VAR_TO_INT_LVAR, + COMMAND_ADD_FLOAT_VAR_TO_FLOAT_LVAR, + COMMAND_ADD_INT_LVAR_TO_INT_VAR, + COMMAND_ADD_FLOAT_LVAR_TO_FLOAT_VAR, + COMMAND_SUB_INT_VAR_FROM_INT_VAR, + COMMAND_SUB_FLOAT_VAR_FROM_FLOAT_VAR, + COMMAND_SUB_INT_LVAR_FROM_INT_LVAR, + COMMAND_SUB_FLOAT_LVAR_FROM_FLOAT_LVAR, + COMMAND_SUB_INT_VAR_FROM_INT_LVAR, + COMMAND_SUB_FLOAT_VAR_FROM_FLOAT_LVAR, + COMMAND_SUB_INT_LVAR_FROM_INT_VAR, + COMMAND_SUB_FLOAT_LVAR_FROM_FLOAT_VAR, + COMMAND_MULT_INT_VAR_BY_INT_VAR, + COMMAND_MULT_FLOAT_VAR_BY_FLOAT_VAR, + COMMAND_MULT_INT_LVAR_BY_INT_LVAR, + COMMAND_MULT_FLOAT_LVAR_BY_FLOAT_LVAR, + COMMAND_MULT_INT_VAR_BY_INT_LVAR, + COMMAND_MULT_FLOAT_VAR_BY_FLOAT_LVAR, + COMMAND_MULT_INT_LVAR_BY_INT_VAR, + COMMAND_MULT_FLOAT_LVAR_BY_FLOAT_VAR, + COMMAND_DIV_INT_VAR_BY_INT_VAR, + COMMAND_DIV_FLOAT_VAR_BY_FLOAT_VAR, + COMMAND_DIV_INT_LVAR_BY_INT_LVAR, + COMMAND_DIV_FLOAT_LVAR_BY_FLOAT_LVAR, + COMMAND_DIV_INT_VAR_BY_INT_LVAR, + COMMAND_DIV_FLOAT_VAR_BY_FLOAT_LVAR, + COMMAND_DIV_INT_LVAR_BY_INT_VAR, + COMMAND_DIV_FLOAT_LVAR_BY_FLOAT_VAR, + COMMAND_ADD_TIMED_VAL_TO_FLOAT_VAR, + COMMAND_ADD_TIMED_VAL_TO_FLOAT_LVAR, + COMMAND_ADD_TIMED_FLOAT_VAR_TO_FLOAT_VAR, + COMMAND_ADD_TIMED_FLOAT_LVAR_TO_FLOAT_LVAR, + COMMAND_ADD_TIMED_FLOAT_LVAR_TO_FLOAT_VAR, + COMMAND_ADD_TIMED_FLOAT_VAR_TO_FLOAT_LVAR, + COMMAND_SUB_TIMED_VAL_FROM_FLOAT_VAR, + COMMAND_SUB_TIMED_VAL_FROM_FLOAT_LVAR, + COMMAND_SUB_TIMED_FLOAT_VAR_FROM_FLOAT_VAR, + COMMAND_SUB_TIMED_FLOAT_LVAR_FROM_FLOAT_LVAR, + COMMAND_SUB_TIMED_FLOAT_LVAR_FROM_FLOAT_VAR, + COMMAND_SUB_TIMED_FLOAT_VAR_FROM_FLOAT_LVAR, + COMMAND_SET_VAR_INT_TO_VAR_INT, + COMMAND_SET_LVAR_INT_TO_LVAR_INT, + COMMAND_SET_VAR_FLOAT_TO_VAR_FLOAT, + COMMAND_SET_LVAR_FLOAT_TO_LVAR_FLOAT, + COMMAND_SET_VAR_FLOAT_TO_LVAR_FLOAT, + COMMAND_SET_LVAR_FLOAT_TO_VAR_FLOAT, + COMMAND_SET_VAR_INT_TO_LVAR_INT, + COMMAND_SET_LVAR_INT_TO_VAR_INT, + COMMAND_CSET_VAR_INT_TO_VAR_FLOAT, + COMMAND_CSET_VAR_FLOAT_TO_VAR_INT, + COMMAND_CSET_LVAR_INT_TO_VAR_FLOAT, + COMMAND_CSET_LVAR_FLOAT_TO_VAR_INT, + COMMAND_CSET_VAR_INT_TO_LVAR_FLOAT, + COMMAND_CSET_VAR_FLOAT_TO_LVAR_INT, + COMMAND_CSET_LVAR_INT_TO_LVAR_FLOAT, + COMMAND_CSET_LVAR_FLOAT_TO_LVAR_INT, + COMMAND_ABS_VAR_INT, + COMMAND_ABS_LVAR_INT, + COMMAND_ABS_VAR_FLOAT, + COMMAND_ABS_LVAR_FLOAT, + COMMAND_GENERATE_RANDOM_FLOAT, + COMMAND_GENERATE_RANDOM_INT, + COMMAND_CREATE_CHAR, + COMMAND_DELETE_CHAR, + COMMAND_CHAR_WANDER_DIR, + COMMAND_CHAR_WANDER_RANGE, + COMMAND_CHAR_FOLLOW_PATH, + COMMAND_CHAR_SET_IDLE, + COMMAND_GET_CHAR_COORDINATES, + COMMAND_SET_CHAR_COORDINATES, + COMMAND_IS_CHAR_STILL_ALIVE, + COMMAND_IS_CHAR_IN_AREA_2D, + COMMAND_IS_CHAR_IN_AREA_3D, + COMMAND_CREATE_CAR, + COMMAND_DELETE_CAR, + COMMAND_CAR_GOTO_COORDINATES, + COMMAND_CAR_WANDER_RANDOMLY, + COMMAND_CAR_SET_IDLE, + COMMAND_GET_CAR_COORDINATES, + COMMAND_SET_CAR_COORDINATES, + COMMAND_IS_CAR_STILL_ALIVE, + COMMAND_SET_CAR_CRUISE_SPEED, + COMMAND_SET_CAR_DRIVING_STYLE, + COMMAND_SET_CAR_MISSION, + COMMAND_IS_CAR_IN_AREA_2D, + COMMAND_IS_CAR_IN_AREA_3D, + COMMAND_SPECIAL_0, + COMMAND_SPECIAL_1, + COMMAND_SPECIAL_2, + COMMAND_SPECIAL_3, + COMMAND_SPECIAL_4, + COMMAND_SPECIAL_5, + COMMAND_SPECIAL_6, + COMMAND_SPECIAL_7, + COMMAND_PRINT_BIG, + COMMAND_PRINT, + COMMAND_PRINT_NOW, + COMMAND_PRINT_SOON, + COMMAND_CLEAR_PRINTS, + COMMAND_GET_TIME_OF_DAY, + COMMAND_SET_TIME_OF_DAY, + COMMAND_GET_MINUTES_TO_TIME_OF_DAY, + COMMAND_IS_POINT_ON_SCREEN, + COMMAND_DEBUG_ON, + COMMAND_DEBUG_OFF, + COMMAND_RETURN_TRUE, + COMMAND_RETURN_FALSE, + COMMAND_VAR_INT, + COMMAND_VAR_FLOAT, + COMMAND_LVAR_INT, + COMMAND_LVAR_FLOAT, + COMMAND_LBRACKET, + COMMAND_RBRACKET, + COMMAND_REPEAT, + COMMAND_ENDREPEAT, + COMMAND_IF, + COMMAND_IFNOT, + COMMAND_ELSE, + COMMAND_ENDIF, + COMMAND_WHILE, + COMMAND_WHILENOT, + COMMAND_ENDWHILE, + COMMAND_ANDOR, + COMMAND_LAUNCH_MISSION, + COMMAND_MISSION_HAS_FINISHED, + COMMAND_STORE_CAR_CHAR_IS_IN, + COMMAND_STORE_CAR_PLAYER_IS_IN, + COMMAND_IS_CHAR_IN_CAR, + COMMAND_IS_PLAYER_IN_CAR, + COMMAND_IS_CHAR_IN_MODEL, + COMMAND_IS_PLAYER_IN_MODEL, + COMMAND_IS_CHAR_IN_ANY_CAR, + COMMAND_IS_PLAYER_IN_ANY_CAR, + COMMAND_IS_BUTTON_PRESSED, + COMMAND_GET_PAD_STATE, + COMMAND_LOCATE_PLAYER_ANY_MEANS_2D, + COMMAND_LOCATE_PLAYER_ON_FOOT_2D, + COMMAND_LOCATE_PLAYER_IN_CAR_2D, + COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_2D, + COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_2D, + COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_2D, + COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_2D, + COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_2D, + COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_2D, + COMMAND_LOCATE_CHAR_ANY_MEANS_2D, + COMMAND_LOCATE_CHAR_ON_FOOT_2D, + COMMAND_LOCATE_CHAR_IN_CAR_2D, + COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_2D, + COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_2D, + COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_2D, + COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_2D, + COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_2D, + COMMAND_LOCATE_CHAR_IN_CAR_CHAR_2D, + COMMAND_LOCATE_PLAYER_ANY_MEANS_3D, + COMMAND_LOCATE_PLAYER_ON_FOOT_3D, + COMMAND_LOCATE_PLAYER_IN_CAR_3D, + COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_3D, + COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_3D, + COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_3D, + COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_3D, + COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_3D, + COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_3D, + COMMAND_LOCATE_CHAR_ANY_MEANS_3D, + COMMAND_LOCATE_CHAR_ON_FOOT_3D, + COMMAND_LOCATE_CHAR_IN_CAR_3D, + COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_3D, + COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_3D, + COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_3D, + COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_3D, + COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_3D, + COMMAND_LOCATE_CHAR_IN_CAR_CHAR_3D, + COMMAND_CREATE_OBJECT, + COMMAND_DELETE_OBJECT, + COMMAND_ADD_SCORE, + COMMAND_IS_SCORE_GREATER, + COMMAND_STORE_SCORE, + COMMAND_GIVE_REMOTE_CONTROLLED_CAR_TO_PLAYER, + COMMAND_ALTER_WANTED_LEVEL, + COMMAND_ALTER_WANTED_LEVEL_NO_DROP, + COMMAND_IS_WANTED_LEVEL_GREATER, + COMMAND_CLEAR_WANTED_LEVEL, + COMMAND_SET_DEATHARREST_STATE, + COMMAND_HAS_DEATHARREST_BEEN_EXECUTED, + COMMAND_ADD_AMMO_TO_PLAYER, + COMMAND_ADD_AMMO_TO_CHAR, + COMMAND_ADD_AMMO_TO_CAR, + COMMAND_IS_PLAYER_STILL_ALIVE, + COMMAND_IS_PLAYER_DEAD, + COMMAND_IS_CHAR_DEAD, + COMMAND_IS_CAR_DEAD, + COMMAND_SET_CHAR_THREAT_SEARCH, + COMMAND_SET_CHAR_THREAT_REACTION, + COMMAND_SET_CHAR_OBJ_NO_OBJ, + COMMAND_ORDER_DRIVER_OUT_OF_CAR, + COMMAND_ORDER_CHAR_TO_DRIVE_CAR, + COMMAND_ADD_PATROL_POINT, + COMMAND_IS_PLAYER_IN_GANGZONE, + COMMAND_IS_PLAYER_IN_ZONE, + COMMAND_IS_PLAYER_PRESSING_HORN, + COMMAND_HAS_CHAR_SPOTTED_PLAYER, +#ifdef SUPPORT_GINPUT_SCRIPT + COMMAND_HAS_PAD_IN_HANDS, +#else + COMMAND_ORDER_CHAR_TO_BACKDOOR, +#endif + COMMAND_ADD_CHAR_TO_GANG, + COMMAND_IS_CHAR_OBJECTIVE_PASSED, + COMMAND_SET_CHAR_DRIVE_AGGRESSION, + COMMAND_SET_CHAR_MAX_DRIVESPEED, + COMMAND_CREATE_CHAR_INSIDE_CAR, + COMMAND_WARP_PLAYER_FROM_CAR_TO_COORD, + COMMAND_MAKE_CHAR_DO_NOTHING, + COMMAND_SET_CHAR_INVINCIBLE, + COMMAND_SET_PLAYER_INVINCIBLE, + COMMAND_SET_CHAR_GRAPHIC_TYPE, + COMMAND_SET_PLAYER_GRAPHIC_TYPE, + COMMAND_HAS_PLAYER_BEEN_ARRESTED, + COMMAND_STOP_CHAR_DRIVING, + COMMAND_KILL_CHAR, + COMMAND_SET_FAVOURITE_CAR_MODEL_FOR_CHAR, + COMMAND_SET_CHAR_OCCUPATION, + COMMAND_CHANGE_CAR_LOCK, + COMMAND_SHAKE_CAM_WITH_POINT, + COMMAND_IS_CAR_MODEL, + COMMAND_IS_CAR_REMAP, + COMMAND_HAS_CAR_JUST_SUNK, + COMMAND_SET_CAR_NO_COLLIDE, + COMMAND_IS_CAR_DEAD_IN_AREA_2D, + COMMAND_IS_CAR_DEAD_IN_AREA_3D, + COMMAND_IS_TRAILER_ATTACHED, + COMMAND_IS_CAR_ON_TRAILER, + COMMAND_HAS_CAR_GOT_WEAPON, + COMMAND_PARK, + COMMAND_HAS_PARK_FINISHED, + COMMAND_KILL_ALL_PASSENGERS, + COMMAND_SET_CAR_BULLETPROOF, + COMMAND_SET_CAR_FLAMEPROOF, + COMMAND_SET_CAR_ROCKETPROOF, + COMMAND_IS_CARBOMB_ACTIVE, + COMMAND_GIVE_CAR_ALARM, + COMMAND_PUT_CAR_ON_TRAILER, + COMMAND_IS_CAR_CRUSHED, + COMMAND_CREATE_GANG_CAR, + COMMAND_CREATE_CAR_GENERATOR, + COMMAND_SWITCH_CAR_GENERATOR, + COMMAND_ADD_PAGER_MESSAGE, + COMMAND_DISPLAY_ONSCREEN_TIMER, + COMMAND_CLEAR_ONSCREEN_TIMER, + COMMAND_DISPLAY_ONSCREEN_COUNTER, + COMMAND_CLEAR_ONSCREEN_COUNTER, + COMMAND_SET_ZONE_CAR_INFO, + COMMAND_IS_CHAR_IN_GANG_ZONE, + COMMAND_IS_CHAR_IN_ZONE, + COMMAND_SET_CAR_DENSITY, + COMMAND_SET_PED_DENSITY, + COMMAND_POINT_CAMERA_AT_PLAYER, + COMMAND_POINT_CAMERA_AT_CAR, + COMMAND_POINT_CAMERA_AT_CHAR, + COMMAND_RESTORE_CAMERA, + COMMAND_SHAKE_PAD, + COMMAND_SET_ZONE_PED_INFO, + COMMAND_SET_TIME_SCALE, + COMMAND_IS_CAR_IN_AIR, + COMMAND_SET_FIXED_CAMERA_POSITION, + COMMAND_POINT_CAMERA_AT_POINT, + COMMAND_ADD_BLIP_FOR_CAR_OLD, + COMMAND_ADD_BLIP_FOR_CHAR_OLD, + COMMAND_ADD_BLIP_FOR_OBJECT_OLD, + COMMAND_REMOVE_BLIP, + COMMAND_CHANGE_BLIP_COLOUR, + COMMAND_DIM_BLIP, + COMMAND_ADD_BLIP_FOR_COORD_OLD, + COMMAND_CHANGE_BLIP_SCALE, + COMMAND_SET_FADING_COLOUR, + COMMAND_DO_FADE, + COMMAND_GET_FADING_STATUS, + COMMAND_ADD_HOSPITAL_RESTART, + COMMAND_ADD_POLICE_RESTART, + COMMAND_OVERRIDE_NEXT_RESTART, + COMMAND_DRAW_SHADOW, + COMMAND_GET_PLAYER_HEADING, + COMMAND_SET_PLAYER_HEADING, + COMMAND_GET_CHAR_HEADING, + COMMAND_SET_CHAR_HEADING, + COMMAND_GET_CAR_HEADING, + COMMAND_SET_CAR_HEADING, + COMMAND_GET_OBJECT_HEADING, + COMMAND_SET_OBJECT_HEADING, + COMMAND_IS_PLAYER_TOUCHING_OBJECT, + COMMAND_IS_CHAR_TOUCHING_OBJECT, + COMMAND_SET_PLAYER_AMMO, + COMMAND_SET_CHAR_AMMO, + COMMAND_SET_CAR_AMMO, + COMMAND_LOAD_CAMERA_SPLINE, + COMMAND_MOVE_CAMERA_ALONG_SPLINE, + COMMAND_GET_CAMERA_POSITION_ALONG_SPLINE, + COMMAND_DECLARE_MISSION_FLAG, + COMMAND_DECLARE_MISSION_FLAG_FOR_CONTACT, + COMMAND_DECLARE_BASE_BRIEF_ID_FOR_CONTACT, + COMMAND_IS_PLAYER_HEALTH_GREATER, + COMMAND_IS_CHAR_HEALTH_GREATER, + COMMAND_IS_CAR_HEALTH_GREATER, + COMMAND_ADD_BLIP_FOR_CAR, + COMMAND_ADD_BLIP_FOR_CHAR, + COMMAND_ADD_BLIP_FOR_OBJECT, + COMMAND_ADD_BLIP_FOR_CONTACT_POINT, + COMMAND_ADD_BLIP_FOR_COORD, + COMMAND_CHANGE_BLIP_DISPLAY, + COMMAND_ADD_ONE_OFF_SOUND, + COMMAND_ADD_CONTINUOUS_SOUND, + COMMAND_REMOVE_SOUND, + COMMAND_IS_CAR_STUCK_ON_ROOF, + COMMAND_ADD_UPSIDEDOWN_CAR_CHECK, + COMMAND_REMOVE_UPSIDEDOWN_CAR_CHECK, + COMMAND_SET_CHAR_OBJ_WAIT_ON_FOOT, + COMMAND_SET_CHAR_OBJ_FLEE_ON_FOOT_TILL_SAFE, + COMMAND_SET_CHAR_OBJ_GUARD_SPOT, + COMMAND_SET_CHAR_OBJ_GUARD_AREA, + COMMAND_SET_CHAR_OBJ_WAIT_IN_CAR, + COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_2D, + COMMAND_IS_PLAYER_IN_AREA_IN_CAR_2D, + COMMAND_IS_PLAYER_STOPPED_IN_AREA_2D, + COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_2D, + COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_2D, + COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_3D, + COMMAND_IS_PLAYER_IN_AREA_IN_CAR_3D, + COMMAND_IS_PLAYER_STOPPED_IN_AREA_3D, + COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_3D, + COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_3D, + COMMAND_IS_CHAR_IN_AREA_ON_FOOT_2D, + COMMAND_IS_CHAR_IN_AREA_IN_CAR_2D, + COMMAND_IS_CHAR_STOPPED_IN_AREA_2D, + COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_2D, + COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_2D, + COMMAND_IS_CHAR_IN_AREA_ON_FOOT_3D, + COMMAND_IS_CHAR_IN_AREA_IN_CAR_3D, + COMMAND_IS_CHAR_STOPPED_IN_AREA_3D, + COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_3D, + COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_3D, + COMMAND_IS_CAR_STOPPED_IN_AREA_2D, + COMMAND_IS_CAR_STOPPED_IN_AREA_3D, + COMMAND_LOCATE_CAR_2D, + COMMAND_LOCATE_STOPPED_CAR_2D, + COMMAND_LOCATE_CAR_3D, + COMMAND_LOCATE_STOPPED_CAR_3D, + COMMAND_GIVE_WEAPON_TO_PLAYER, + COMMAND_GIVE_WEAPON_TO_CHAR, + COMMAND_GIVE_WEAPON_TO_CAR, + COMMAND_SET_PLAYER_CONTROL, + COMMAND_FORCE_WEATHER, + COMMAND_FORCE_WEATHER_NOW, + COMMAND_RELEASE_WEATHER, + COMMAND_SET_CURRENT_PLAYER_WEAPON, + COMMAND_SET_CURRENT_CHAR_WEAPON, + COMMAND_SET_CURRENT_CAR_WEAPON, + COMMAND_GET_OBJECT_COORDINATES, + COMMAND_SET_OBJECT_COORDINATES, + COMMAND_GET_GAME_TIMER, + COMMAND_TURN_CHAR_TO_FACE_COORD, + COMMAND_TURN_PLAYER_TO_FACE_COORD, + COMMAND_STORE_WANTED_LEVEL, + COMMAND_IS_CAR_STOPPED, + COMMAND_MARK_CHAR_AS_NO_LONGER_NEEDED, + COMMAND_MARK_CAR_AS_NO_LONGER_NEEDED, + COMMAND_MARK_OBJECT_AS_NO_LONGER_NEEDED, + COMMAND_DONT_REMOVE_CHAR, + COMMAND_DONT_REMOVE_CAR, + COMMAND_DONT_REMOVE_OBJECT, + COMMAND_CREATE_CHAR_AS_PASSENGER, + COMMAND_SET_CHAR_OBJ_KILL_CHAR_ON_FOOT, + COMMAND_SET_CHAR_OBJ_KILL_PLAYER_ON_FOOT, + COMMAND_SET_CHAR_OBJ_KILL_CHAR_ANY_MEANS, + COMMAND_SET_CHAR_OBJ_KILL_PLAYER_ANY_MEANS, + COMMAND_SET_CHAR_OBJ_FLEE_CHAR_ON_FOOT_TILL_SAFE, + COMMAND_SET_CHAR_OBJ_FLEE_PLAYER_ON_FOOT_TILL_SAFE, + COMMAND_SET_CHAR_OBJ_FLEE_CHAR_ON_FOOT_ALWAYS, + COMMAND_SET_CHAR_OBJ_FLEE_PLAYER_ON_FOOT_ALWAYS, + COMMAND_SET_CHAR_OBJ_GOTO_CHAR_ON_FOOT, + COMMAND_SET_CHAR_OBJ_GOTO_PLAYER_ON_FOOT, + COMMAND_SET_CHAR_OBJ_LEAVE_CAR, + COMMAND_SET_CHAR_OBJ_ENTER_CAR_AS_PASSENGER, + COMMAND_SET_CHAR_OBJ_ENTER_CAR_AS_DRIVER, + COMMAND_SET_CHAR_OBJ_FOLLOW_CAR_IN_CAR, + COMMAND_SET_CHAR_OBJ_FIRE_AT_OBJECT_FROM_VEHICLE, + COMMAND_SET_CHAR_OBJ_DESTROY_OBJECT, + COMMAND_SET_CHAR_OBJ_DESTROY_CAR, + COMMAND_SET_CHAR_OBJ_GOTO_AREA_ON_FOOT, + COMMAND_SET_CHAR_OBJ_GOTO_AREA_IN_CAR, + COMMAND_SET_CHAR_OBJ_FOLLOW_CAR_ON_FOOT_WITH_OFFSET, + COMMAND_SET_CHAR_OBJ_GUARD_ATTACK, + COMMAND_SET_CHAR_AS_LEADER, + COMMAND_SET_PLAYER_AS_LEADER, + COMMAND_LEAVE_GROUP, + COMMAND_SET_CHAR_OBJ_FOLLOW_ROUTE, + COMMAND_ADD_ROUTE_POINT, + COMMAND_PRINT_WITH_NUMBER_BIG, + COMMAND_PRINT_WITH_NUMBER, + COMMAND_PRINT_WITH_NUMBER_NOW, + COMMAND_PRINT_WITH_NUMBER_SOON, + COMMAND_SWITCH_ROADS_ON, + COMMAND_SWITCH_ROADS_OFF, + COMMAND_GET_NUMBER_OF_PASSENGERS, + COMMAND_GET_MAXIMUM_NUMBER_OF_PASSENGERS, + COMMAND_SET_CAR_DENSITY_MULTIPLIER, + COMMAND_SET_CAR_HEAVY, + COMMAND_CLEAR_CHAR_THREAT_SEARCH, + COMMAND_ACTIVATE_CRANE, + COMMAND_DEACTIVATE_CRANE, + COMMAND_SET_MAX_WANTED_LEVEL, + COMMAND_SAVE_VAR_INT, + COMMAND_SAVE_VAR_FLOAT, + COMMAND_IS_CAR_IN_AIR_PROPER, + COMMAND_IS_CAR_UPSIDEDOWN, + COMMAND_GET_PLAYER_CHAR, + COMMAND_CANCEL_OVERRIDE_RESTART, + COMMAND_SET_POLICE_IGNORE_PLAYER, + COMMAND_ADD_PAGER_MESSAGE_WITH_NUMBER, + COMMAND_START_KILL_FRENZY, + COMMAND_READ_KILL_FRENZY_STATUS, + COMMAND_SQRT, + COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_2D, + COMMAND_LOCATE_PLAYER_ON_FOOT_CAR_2D, + COMMAND_LOCATE_PLAYER_IN_CAR_CAR_2D, + COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_3D, + COMMAND_LOCATE_PLAYER_ON_FOOT_CAR_3D, + COMMAND_LOCATE_PLAYER_IN_CAR_CAR_3D, + COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_2D, + COMMAND_LOCATE_CHAR_ON_FOOT_CAR_2D, + COMMAND_LOCATE_CHAR_IN_CAR_CAR_2D, + COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_3D, + COMMAND_LOCATE_CHAR_ON_FOOT_CAR_3D, + COMMAND_LOCATE_CHAR_IN_CAR_CAR_3D, + COMMAND_GENERATE_RANDOM_FLOAT_IN_RANGE, + COMMAND_GENERATE_RANDOM_INT_IN_RANGE, + COMMAND_LOCK_CAR_DOORS, + COMMAND_EXPLODE_CAR, + COMMAND_ADD_EXPLOSION, + COMMAND_IS_CAR_UPRIGHT, + COMMAND_TURN_CHAR_TO_FACE_CHAR, + COMMAND_TURN_CHAR_TO_FACE_PLAYER, + COMMAND_TURN_PLAYER_TO_FACE_CHAR, + COMMAND_SET_CHAR_OBJ_GOTO_COORD_ON_FOOT, + COMMAND_SET_CHAR_OBJ_GOTO_COORD_IN_CAR, + COMMAND_CREATE_PICKUP, + COMMAND_HAS_PICKUP_BEEN_COLLECTED, + COMMAND_REMOVE_PICKUP, + COMMAND_SET_TAXI_LIGHTS, + COMMAND_PRINT_BIG_Q, + COMMAND_PRINT_WITH_NUMBER_BIG_Q, + COMMAND_SET_GARAGE, + COMMAND_SET_GARAGE_WITH_CAR_MODEL, + COMMAND_SET_TARGET_CAR_FOR_MISSION_GARAGE, + COMMAND_IS_CAR_IN_MISSION_GARAGE, + COMMAND_SET_FREE_BOMBS, + COMMAND_SET_POWERPOINT, + COMMAND_SET_ALL_TAXI_LIGHTS, + COMMAND_IS_CAR_ARMED_WITH_ANY_BOMB, + COMMAND_APPLY_BRAKES_TO_PLAYERS_CAR, + COMMAND_SET_PLAYER_HEALTH, + COMMAND_SET_CHAR_HEALTH, + COMMAND_SET_CAR_HEALTH, + COMMAND_GET_PLAYER_HEALTH, + COMMAND_GET_CHAR_HEALTH, + COMMAND_GET_CAR_HEALTH, + COMMAND_IS_CAR_ARMED_WITH_BOMB, + COMMAND_CHANGE_CAR_COLOUR, + COMMAND_SWITCH_PED_ROADS_ON, + COMMAND_SWITCH_PED_ROADS_OFF, + COMMAND_CHAR_LOOK_AT_CHAR_ALWAYS, + COMMAND_CHAR_LOOK_AT_PLAYER_ALWAYS, + COMMAND_PLAYER_LOOK_AT_CHAR_ALWAYS, + COMMAND_STOP_CHAR_LOOKING, + COMMAND_STOP_PLAYER_LOOKING, + COMMAND_SWITCH_HELICOPTER, + COMMAND_SET_GANG_ATTITUDE, + COMMAND_SET_GANG_GANG_ATTITUDE, + COMMAND_SET_GANG_PLAYER_ATTITUDE, + COMMAND_SET_GANG_PED_MODELS, + COMMAND_SET_GANG_CAR_MODEL, + COMMAND_SET_GANG_WEAPONS, + COMMAND_SET_CHAR_OBJ_RUN_TO_AREA, + COMMAND_SET_CHAR_OBJ_RUN_TO_COORD, + COMMAND_IS_PLAYER_TOUCHING_OBJECT_ON_FOOT, + COMMAND_IS_CHAR_TOUCHING_OBJECT_ON_FOOT, + COMMAND_LOAD_SPECIAL_CHARACTER, + COMMAND_HAS_SPECIAL_CHARACTER_LOADED, + COMMAND_FLASH_CAR, + COMMAND_FLASH_CHAR, + COMMAND_FLASH_OBJECT, + COMMAND_IS_PLAYER_IN_REMOTE_MODE, + COMMAND_ARM_CAR_WITH_BOMB, + COMMAND_SET_CHAR_PERSONALITY, + COMMAND_SET_CUTSCENE_OFFSET, + COMMAND_SET_ANIM_GROUP_FOR_CHAR, + COMMAND_SET_ANIM_GROUP_FOR_PLAYER, + COMMAND_REQUEST_MODEL, + COMMAND_HAS_MODEL_LOADED, + COMMAND_MARK_MODEL_AS_NO_LONGER_NEEDED, + COMMAND_GRAB_PHONE, + COMMAND_SET_REPEATED_PHONE_MESSAGE, + COMMAND_SET_PHONE_MESSAGE, + COMMAND_HAS_PHONE_DISPLAYED_MESSAGE, + COMMAND_TURN_PHONE_OFF, + COMMAND_DRAW_CORONA, + COMMAND_DRAW_LIGHT, + COMMAND_STORE_WEATHER, + COMMAND_RESTORE_WEATHER, + COMMAND_STORE_CLOCK, + COMMAND_RESTORE_CLOCK, + COMMAND_RESTART_CRITICAL_MISSION, + COMMAND_IS_PLAYER_PLAYING, + COMMAND_SET_COLL_OBJ_NO_OBJ, + COMMAND_SET_COLL_OBJ_WAIT_ON_FOOT, + COMMAND_SET_COLL_OBJ_FLEE_ON_FOOT_TILL_SAFE, + COMMAND_SET_COLL_OBJ_GUARD_SPOT, + COMMAND_SET_COLL_OBJ_GUARD_AREA, + COMMAND_SET_COLL_OBJ_WAIT_IN_CAR, + COMMAND_SET_COLL_OBJ_KILL_CHAR_ON_FOOT, + COMMAND_SET_COLL_OBJ_KILL_PLAYER_ON_FOOT, + COMMAND_SET_COLL_OBJ_KILL_CHAR_ANY_MEANS, + COMMAND_SET_COLL_OBJ_KILL_PLAYER_ANY_MEANS, + COMMAND_SET_COLL_OBJ_FLEE_CHAR_ON_FOOT_TILL_SAFE, + COMMAND_SET_COLL_OBJ_FLEE_PLAYER_ON_FOOT_TILL_SAFE, + COMMAND_SET_COLL_OBJ_FLEE_CHAR_ON_FOOT_ALWAYS, + COMMAND_SET_COLL_OBJ_FLEE_PLAYER_ON_FOOT_ALWAYS, + COMMAND_SET_COLL_OBJ_GOTO_CHAR_ON_FOOT, + COMMAND_SET_COLL_OBJ_GOTO_PLAYER_ON_FOOT, + COMMAND_SET_COLL_OBJ_LEAVE_CAR, + COMMAND_SET_COLL_OBJ_ENTER_CAR_AS_PASSENGER, + COMMAND_SET_COLL_OBJ_ENTER_CAR_AS_DRIVER, + COMMAND_SET_COLL_OBJ_FOLLOW_CAR_IN_CAR, + COMMAND_SET_COLL_OBJ_FIRE_AT_OBJECT_FROM_VEHICLE, + COMMAND_SET_COLL_OBJ_DESTROY_OBJECT, + COMMAND_SET_COLL_OBJ_DESTROY_CAR, + COMMAND_SET_COLL_OBJ_GOTO_AREA_ON_FOOT, + COMMAND_SET_COLL_OBJ_GOTO_AREA_IN_CAR, + COMMAND_SET_COLL_OBJ_FOLLOW_CAR_ON_FOOT_WITH_OFFSET, + COMMAND_SET_COLL_OBJ_GUARD_ATTACK, + COMMAND_SET_COLL_OBJ_FOLLOW_ROUTE, + COMMAND_SET_COLL_OBJ_GOTO_COORD_ON_FOOT, + COMMAND_SET_COLL_OBJ_GOTO_COORD_IN_CAR, + COMMAND_SET_COLL_OBJ_RUN_TO_AREA, + COMMAND_SET_COLL_OBJ_RUN_TO_COORD, + COMMAND_ADD_PEDS_IN_AREA_TO_COLL, + COMMAND_ADD_PEDS_IN_VEHICLE_TO_COLL, + COMMAND_CLEAR_COLL, + COMMAND_IS_COLL_IN_CARS, + COMMAND_LOCATE_COLL_ANY_MEANS_2D, + COMMAND_LOCATE_COLL_ON_FOOT_2D, + COMMAND_LOCATE_COLL_IN_CAR_2D, + COMMAND_LOCATE_STOPPED_COLL_ANY_MEANS_2D, + COMMAND_LOCATE_STOPPED_COLL_ON_FOOT_2D, + COMMAND_LOCATE_STOPPED_COLL_IN_CAR_2D, + COMMAND_LOCATE_COLL_ANY_MEANS_CHAR_2D, + COMMAND_LOCATE_COLL_ON_FOOT_CHAR_2D, + COMMAND_LOCATE_COLL_IN_CAR_CHAR_2D, + COMMAND_LOCATE_COLL_ANY_MEANS_CAR_2D, + COMMAND_LOCATE_COLL_ON_FOOT_CAR_2D, + COMMAND_LOCATE_COLL_IN_CAR_CAR_2D, + COMMAND_LOCATE_COLL_ANY_MEANS_PLAYER_2D, + COMMAND_LOCATE_COLL_ON_FOOT_PLAYER_2D, + COMMAND_LOCATE_COLL_IN_CAR_PLAYER_2D, + COMMAND_IS_COLL_IN_AREA_2D, + COMMAND_IS_COLL_IN_AREA_ON_FOOT_2D, + COMMAND_IS_COLL_IN_AREA_IN_CAR_2D, + COMMAND_IS_COLL_STOPPED_IN_AREA_2D, + COMMAND_IS_COLL_STOPPED_IN_AREA_ON_FOOT_2D, + COMMAND_IS_COLL_STOPPED_IN_AREA_IN_CAR_2D, + COMMAND_GET_NUMBER_OF_PEDS_IN_COLL, + COMMAND_SET_CHAR_HEED_THREATS, + COMMAND_SET_PLAYER_HEED_THREATS, + COMMAND_GET_CONTROLLER_MODE, + COMMAND_SET_CAN_RESPRAY_CAR, + COMMAND_IS_TAXI, + COMMAND_UNLOAD_SPECIAL_CHARACTER, + COMMAND_RESET_NUM_OF_MODELS_KILLED_BY_PLAYER, + COMMAND_GET_NUM_OF_MODELS_KILLED_BY_PLAYER, + COMMAND_ACTIVATE_GARAGE, + COMMAND_SWITCH_TAXI_TIMER, + COMMAND_CREATE_OBJECT_NO_OFFSET, + COMMAND_IS_BOAT, + COMMAND_SET_CHAR_OBJ_GOTO_AREA_ANY_MEANS, + COMMAND_SET_COLL_OBJ_GOTO_AREA_ANY_MEANS, + COMMAND_IS_PLAYER_STOPPED, + COMMAND_IS_CHAR_STOPPED, + COMMAND_MESSAGE_WAIT, + COMMAND_ADD_PARTICLE_EFFECT, + COMMAND_SWITCH_WIDESCREEN, + COMMAND_ADD_SPRITE_BLIP_FOR_CAR, + COMMAND_ADD_SPRITE_BLIP_FOR_CHAR, + COMMAND_ADD_SPRITE_BLIP_FOR_OBJECT, + COMMAND_ADD_SPRITE_BLIP_FOR_CONTACT_POINT, + COMMAND_ADD_SPRITE_BLIP_FOR_COORD, + COMMAND_SET_CHAR_ONLY_DAMAGED_BY_PLAYER, + COMMAND_SET_CAR_ONLY_DAMAGED_BY_PLAYER, + COMMAND_SET_CHAR_PROOFS, + COMMAND_SET_CAR_PROOFS, + COMMAND_IS_PLAYER_IN_ANGLED_AREA_2D, + COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_2D, + COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_2D, + COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_2D, + COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_2D, + COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_2D, + COMMAND_IS_PLAYER_IN_ANGLED_AREA_3D, + COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_3D, + COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_3D, + COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_3D, + COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_3D, + COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_3D, + COMMAND_DEACTIVATE_GARAGE, + COMMAND_GET_NUMBER_OF_CARS_COLLECTED_BY_GARAGE, + COMMAND_HAS_CAR_BEEN_TAKEN_TO_GARAGE, + COMMAND_SET_SWAT_REQUIRED, + COMMAND_SET_FBI_REQUIRED, + COMMAND_SET_ARMY_REQUIRED, + COMMAND_IS_CAR_IN_WATER, + COMMAND_GET_CLOSEST_CHAR_NODE, + COMMAND_GET_CLOSEST_CAR_NODE, + COMMAND_CAR_GOTO_COORDINATES_ACCURATE, + COMMAND_START_PACMAN_RACE, + COMMAND_START_PACMAN_RECORD, + COMMAND_GET_NUMBER_OF_POWER_PILLS_EATEN, + COMMAND_CLEAR_PACMAN, + COMMAND_START_PACMAN_SCRAMBLE, + COMMAND_GET_NUMBER_OF_POWER_PILLS_CARRIED, + COMMAND_CLEAR_NUMBER_OF_POWER_PILLS_CARRIED, + COMMAND_IS_CAR_ON_SCREEN, + COMMAND_IS_CHAR_ON_SCREEN, + COMMAND_IS_OBJECT_ON_SCREEN, + COMMAND_GOSUB_FILE, + COMMAND_GET_GROUND_Z_FOR_3D_COORD, + COMMAND_START_SCRIPT_FIRE, + COMMAND_IS_SCRIPT_FIRE_EXTINGUISHED, + COMMAND_REMOVE_SCRIPT_FIRE, + COMMAND_SET_COMEDY_CONTROLS, + COMMAND_BOAT_GOTO_COORDS, + COMMAND_BOAT_STOP, + COMMAND_IS_PLAYER_SHOOTING_IN_AREA, + COMMAND_IS_CHAR_SHOOTING_IN_AREA, + COMMAND_IS_CURRENT_PLAYER_WEAPON, + COMMAND_IS_CURRENT_CHAR_WEAPON, + COMMAND_CLEAR_NUMBER_OF_POWER_PILLS_EATEN, + COMMAND_ADD_POWER_PILL, + COMMAND_SET_BOAT_CRUISE_SPEED, + COMMAND_GET_RANDOM_CHAR_IN_AREA, + COMMAND_GET_RANDOM_CHAR_IN_ZONE, + COMMAND_IS_PLAYER_IN_TAXI, + COMMAND_IS_PLAYER_SHOOTING, + COMMAND_IS_CHAR_SHOOTING, + COMMAND_CREATE_MONEY_PICKUP, + COMMAND_SET_CHAR_ACCURACY, + COMMAND_GET_CAR_SPEED, + COMMAND_LOAD_CUTSCENE, + COMMAND_CREATE_CUTSCENE_OBJECT, + COMMAND_SET_CUTSCENE_ANIM, + COMMAND_START_CUTSCENE, + COMMAND_GET_CUTSCENE_TIME, + COMMAND_HAS_CUTSCENE_FINISHED, + COMMAND_CLEAR_CUTSCENE, + COMMAND_RESTORE_CAMERA_JUMPCUT, + COMMAND_CREATE_COLLECTABLE1, + COMMAND_SET_COLLECTABLE1_TOTAL, + COMMAND_IS_PROJECTILE_IN_AREA, + COMMAND_DESTROY_PROJECTILES_IN_AREA, + COMMAND_DROP_MINE, + COMMAND_DROP_NAUTICAL_MINE, + COMMAND_IS_CHAR_MODEL, + COMMAND_LOAD_SPECIAL_MODEL, + COMMAND_CREATE_CUTSCENE_HEAD, + COMMAND_SET_CUTSCENE_HEAD_ANIM, + COMMAND_SIN, + COMMAND_COS, + COMMAND_GET_CAR_FORWARD_X, + COMMAND_GET_CAR_FORWARD_Y, + COMMAND_CHANGE_GARAGE_TYPE, + COMMAND_ACTIVATE_CRUSHER_CRANE, + COMMAND_PRINT_WITH_2_NUMBERS, + COMMAND_PRINT_WITH_2_NUMBERS_NOW, + COMMAND_PRINT_WITH_2_NUMBERS_SOON, + COMMAND_PRINT_WITH_3_NUMBERS, + COMMAND_PRINT_WITH_3_NUMBERS_NOW, + COMMAND_PRINT_WITH_3_NUMBERS_SOON, + COMMAND_PRINT_WITH_4_NUMBERS, + COMMAND_PRINT_WITH_4_NUMBERS_NOW, + COMMAND_PRINT_WITH_4_NUMBERS_SOON, + COMMAND_PRINT_WITH_5_NUMBERS, + COMMAND_PRINT_WITH_5_NUMBERS_NOW, + COMMAND_PRINT_WITH_5_NUMBERS_SOON, + COMMAND_PRINT_WITH_6_NUMBERS, + COMMAND_PRINT_WITH_6_NUMBERS_NOW, + COMMAND_PRINT_WITH_6_NUMBERS_SOON, + COMMAND_SET_CHAR_OBJ_FOLLOW_CHAR_IN_FORMATION, + COMMAND_PLAYER_MADE_PROGRESS, + COMMAND_SET_PROGRESS_TOTAL, + COMMAND_REGISTER_JUMP_DISTANCE, + COMMAND_REGISTER_JUMP_HEIGHT, + COMMAND_REGISTER_JUMP_FLIPS, + COMMAND_REGISTER_JUMP_SPINS, + COMMAND_REGISTER_JUMP_STUNT, + COMMAND_REGISTER_UNIQUE_JUMP_FOUND, + COMMAND_SET_UNIQUE_JUMPS_TOTAL, + COMMAND_REGISTER_PASSENGER_DROPPED_OFF_TAXI, + COMMAND_REGISTER_MONEY_MADE_TAXI, + COMMAND_REGISTER_MISSION_GIVEN, + COMMAND_REGISTER_MISSION_PASSED, + COMMAND_SET_CHAR_RUNNING, + COMMAND_REMOVE_ALL_SCRIPT_FIRES, + COMMAND_IS_FIRST_CAR_COLOUR, + COMMAND_IS_SECOND_CAR_COLOUR, + COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_WEAPON, + COMMAND_HAS_CAR_BEEN_DAMAGED_BY_WEAPON, + COMMAND_IS_CHAR_IN_CHARS_GROUP, + COMMAND_IS_CHAR_IN_PLAYERS_GROUP, + COMMAND_EXPLODE_CHAR_HEAD, + COMMAND_EXPLODE_PLAYER_HEAD, + COMMAND_ANCHOR_BOAT, + COMMAND_SET_ZONE_GROUP, + COMMAND_START_CAR_FIRE, + COMMAND_START_CHAR_FIRE, + COMMAND_GET_RANDOM_CAR_OF_TYPE_IN_AREA, + COMMAND_GET_RANDOM_CAR_OF_TYPE_IN_ZONE, + COMMAND_HAS_RESPRAY_HAPPENED, + COMMAND_SET_CAMERA_ZOOM, + COMMAND_CREATE_PICKUP_WITH_AMMO, + COMMAND_SET_CAR_RAM_CAR, + COMMAND_SET_CAR_BLOCK_CAR, + COMMAND_SET_CHAR_OBJ_CATCH_TRAIN, + COMMAND_SET_COLL_OBJ_CATCH_TRAIN, + COMMAND_SET_PLAYER_NEVER_GETS_TIRED, + COMMAND_SET_PLAYER_FAST_RELOAD, + COMMAND_SET_CHAR_BLEEDING, + COMMAND_SET_CAR_FUNNY_SUSPENSION, + COMMAND_SET_CAR_BIG_WHEELS, + COMMAND_SET_FREE_RESPRAYS, + COMMAND_SET_PLAYER_VISIBLE, + COMMAND_SET_CHAR_VISIBLE, + COMMAND_SET_CAR_VISIBLE, + COMMAND_IS_AREA_OCCUPIED, + COMMAND_START_DRUG_RUN, + COMMAND_HAS_DRUG_RUN_BEEN_COMPLETED, + COMMAND_HAS_DRUG_PLANE_BEEN_SHOT_DOWN, + COMMAND_SAVE_PLAYER_FROM_FIRES, + COMMAND_DISPLAY_TEXT, + COMMAND_SET_TEXT_SCALE, + COMMAND_SET_TEXT_COLOUR, + COMMAND_SET_TEXT_JUSTIFY, + COMMAND_SET_TEXT_CENTRE, + COMMAND_SET_TEXT_WRAPX, + COMMAND_SET_TEXT_CENTRE_SIZE, + COMMAND_SET_TEXT_BACKGROUND, + COMMAND_SET_TEXT_BACKGROUND_COLOUR, + COMMAND_SET_TEXT_BACKGROUND_ONLY_TEXT, + COMMAND_SET_TEXT_PROPORTIONAL, + COMMAND_SET_TEXT_FONT, + COMMAND_INDUSTRIAL_PASSED, + COMMAND_COMMERCIAL_PASSED, + COMMAND_SUBURBAN_PASSED, + COMMAND_ROTATE_OBJECT, + COMMAND_SLIDE_OBJECT, + COMMAND_REMOVE_CHAR_ELEGANTLY, + COMMAND_SET_CHAR_STAY_IN_SAME_PLACE, + COMMAND_IS_NASTY_GAME, + COMMAND_UNDRESS_CHAR, + COMMAND_DRESS_CHAR, + COMMAND_START_CHASE_SCENE, + COMMAND_STOP_CHASE_SCENE, + COMMAND_IS_EXPLOSION_IN_AREA, + COMMAND_IS_EXPLOSION_IN_ZONE, + COMMAND_START_DRUG_DROP_OFF, + COMMAND_HAS_DROP_OFF_PLANE_BEEN_SHOT_DOWN, + COMMAND_FIND_DROP_OFF_PLANE_COORDINATES, + COMMAND_CREATE_FLOATING_PACKAGE, + COMMAND_PLACE_OBJECT_RELATIVE_TO_CAR, + COMMAND_MAKE_OBJECT_TARGETTABLE, + COMMAND_ADD_ARMOUR_TO_PLAYER, + COMMAND_ADD_ARMOUR_TO_CHAR, + COMMAND_OPEN_GARAGE, + COMMAND_CLOSE_GARAGE, + COMMAND_WARP_CHAR_FROM_CAR_TO_COORD, + COMMAND_SET_VISIBILITY_OF_CLOSEST_OBJECT_OF_TYPE, + COMMAND_HAS_CHAR_SPOTTED_CHAR, + COMMAND_SET_CHAR_OBJ_HAIL_TAXI, + COMMAND_HAS_OBJECT_BEEN_DAMAGED, + COMMAND_START_KILL_FRENZY_HEADSHOT, + COMMAND_ACTIVATE_MILITARY_CRANE, + COMMAND_WARP_PLAYER_INTO_CAR, + COMMAND_WARP_CHAR_INTO_CAR, + COMMAND_SWITCH_CAR_RADIO, + COMMAND_SET_AUDIO_STREAM, + COMMAND_PRINT_WITH_2_NUMBERS_BIG, + COMMAND_PRINT_WITH_3_NUMBERS_BIG, + COMMAND_PRINT_WITH_4_NUMBERS_BIG, + COMMAND_PRINT_WITH_5_NUMBERS_BIG, + COMMAND_PRINT_WITH_6_NUMBERS_BIG, + COMMAND_SET_CHAR_WAIT_STATE, + COMMAND_SET_CAMERA_BEHIND_PLAYER, + COMMAND_SET_MOTION_BLUR, + COMMAND_PRINT_STRING_IN_STRING, + COMMAND_CREATE_RANDOM_CHAR, + COMMAND_SET_CHAR_OBJ_STEAL_ANY_CAR, + COMMAND_SET_2_REPEATED_PHONE_MESSAGES, + COMMAND_SET_2_PHONE_MESSAGES, + COMMAND_SET_3_REPEATED_PHONE_MESSAGES, + COMMAND_SET_3_PHONE_MESSAGES, + COMMAND_SET_4_REPEATED_PHONE_MESSAGES, + COMMAND_SET_4_PHONE_MESSAGES, + COMMAND_IS_SNIPER_BULLET_IN_AREA, + COMMAND_GIVE_PLAYER_DETONATOR, + COMMAND_SET_COLL_OBJ_STEAL_ANY_CAR, + COMMAND_SET_OBJECT_VELOCITY, + COMMAND_SET_OBJECT_COLLISION, + COMMAND_IS_ICECREAM_JINGLE_ON, + COMMAND_PRINT_STRING_IN_STRING_NOW, + COMMAND_PRINT_STRING_IN_STRING_SOON, + COMMAND_SET_5_REPEATED_PHONE_MESSAGES, + COMMAND_SET_5_PHONE_MESSAGES, + COMMAND_SET_6_REPEATED_PHONE_MESSAGES, + COMMAND_SET_6_PHONE_MESSAGES, + COMMAND_IS_POINT_OBSCURED_BY_A_MISSION_ENTITY, + COMMAND_LOAD_ALL_MODELS_NOW, + COMMAND_ADD_TO_OBJECT_VELOCITY, + COMMAND_DRAW_SPRITE, + COMMAND_DRAW_RECT, + COMMAND_LOAD_SPRITE, + COMMAND_LOAD_TEXTURE_DICTIONARY, + COMMAND_REMOVE_TEXTURE_DICTIONARY, + COMMAND_SET_OBJECT_DYNAMIC, + COMMAND_SET_CHAR_ANIM_SPEED, + COMMAND_PLAY_MISSION_PASSED_TUNE, + COMMAND_CLEAR_AREA, + COMMAND_FREEZE_ONSCREEN_TIMER, + COMMAND_SWITCH_CAR_SIREN, + COMMAND_SWITCH_PED_ROADS_ON_ANGLED, + COMMAND_SWITCH_PED_ROADS_OFF_ANGLED, + COMMAND_SWITCH_ROADS_ON_ANGLED, + COMMAND_SWITCH_ROADS_OFF_ANGLED, + COMMAND_SET_CAR_WATERTIGHT, + COMMAND_ADD_MOVING_PARTICLE_EFFECT, + COMMAND_SET_CHAR_CANT_BE_DRAGGED_OUT, + COMMAND_TURN_CAR_TO_FACE_COORD, + COMMAND_IS_CRANE_LIFTING_CAR, + COMMAND_DRAW_SPHERE, + COMMAND_SET_CAR_STATUS, + COMMAND_IS_CHAR_MALE, + COMMAND_SCRIPT_NAME, + COMMAND_CHANGE_GARAGE_TYPE_WITH_CAR_MODEL, + COMMAND_FIND_DRUG_PLANE_COORDINATES, + COMMAND_SAVE_INT_TO_DEBUG_FILE, + COMMAND_SAVE_FLOAT_TO_DEBUG_FILE, + COMMAND_SAVE_NEWLINE_TO_DEBUG_FILE, + COMMAND_POLICE_RADIO_MESSAGE, + COMMAND_SET_CAR_STRONG, + COMMAND_REMOVE_ROUTE, + COMMAND_SWITCH_RUBBISH, + COMMAND_REMOVE_PARTICLE_EFFECTS_IN_AREA, + COMMAND_SWITCH_STREAMING, + COMMAND_IS_GARAGE_OPEN, + COMMAND_IS_GARAGE_CLOSED, + COMMAND_START_CATALINA_HELI, + COMMAND_CATALINA_HELI_TAKE_OFF, + COMMAND_REMOVE_CATALINA_HELI, + COMMAND_HAS_CATALINA_HELI_BEEN_SHOT_DOWN, + COMMAND_SWAP_NEAREST_BUILDING_MODEL, + COMMAND_SWITCH_WORLD_PROCESSING, + COMMAND_REMOVE_ALL_PLAYER_WEAPONS, + COMMAND_GRAB_CATALINA_HELI, + COMMAND_CLEAR_AREA_OF_CARS, + COMMAND_SET_ROTATING_GARAGE_DOOR, + COMMAND_ADD_SPHERE, + COMMAND_REMOVE_SPHERE, + COMMAND_CATALINA_HELI_FLY_AWAY, + COMMAND_SET_EVERYONE_IGNORE_PLAYER, + COMMAND_STORE_CAR_CHAR_IS_IN_NO_SAVE, + COMMAND_STORE_CAR_PLAYER_IS_IN_NO_SAVE, + COMMAND_IS_PHONE_DISPLAYING_MESSAGE, + COMMAND_DISPLAY_ONSCREEN_TIMER_WITH_STRING, + COMMAND_DISPLAY_ONSCREEN_COUNTER_WITH_STRING, + COMMAND_CREATE_RANDOM_CAR_FOR_CAR_PARK, + COMMAND_IS_COLLISION_IN_MEMORY, + COMMAND_SET_WANTED_MULTIPLIER, + COMMAND_SET_CAMERA_IN_FRONT_OF_PLAYER, + COMMAND_IS_CAR_VISIBLY_DAMAGED, + COMMAND_DOES_OBJECT_EXIST, + COMMAND_LOAD_SCENE, + COMMAND_ADD_STUCK_CAR_CHECK, + COMMAND_REMOVE_STUCK_CAR_CHECK, + COMMAND_IS_CAR_STUCK, + COMMAND_LOAD_MISSION_AUDIO, + COMMAND_HAS_MISSION_AUDIO_LOADED, + COMMAND_PLAY_MISSION_AUDIO, + COMMAND_HAS_MISSION_AUDIO_FINISHED, + COMMAND_GET_CLOSEST_CAR_NODE_WITH_HEADING, + COMMAND_HAS_IMPORT_GARAGE_SLOT_BEEN_FILLED, + COMMAND_CLEAR_THIS_PRINT, + COMMAND_CLEAR_THIS_BIG_PRINT, + COMMAND_SET_MISSION_AUDIO_POSITION, + COMMAND_ACTIVATE_SAVE_MENU, + COMMAND_HAS_SAVE_GAME_FINISHED, + COMMAND_NO_SPECIAL_CAMERA_FOR_THIS_GARAGE, + COMMAND_ADD_BLIP_FOR_PICKUP_OLD, + COMMAND_ADD_BLIP_FOR_PICKUP, + COMMAND_ADD_SPRITE_BLIP_FOR_PICKUP, + COMMAND_SET_PED_DENSITY_MULTIPLIER, + COMMAND_FORCE_RANDOM_PED_TYPE, + COMMAND_SET_TEXT_DRAW_BEFORE_FADE, + COMMAND_GET_COLLECTABLE1S_COLLECTED, + COMMAND_SET_CHAR_OBJ_LEAVE_ANY_CAR, + COMMAND_SET_SPRITES_DRAW_BEFORE_FADE, + COMMAND_SET_TEXT_RIGHT_JUSTIFY, + COMMAND_PRINT_HELP, + COMMAND_CLEAR_HELP, + COMMAND_FLASH_HUD_OBJECT, + COMMAND_FLASH_RADAR_BLIP, + COMMAND_IS_CHAR_IN_CONTROL, + COMMAND_SET_GENERATE_CARS_AROUND_CAMERA, + COMMAND_CLEAR_SMALL_PRINTS, + COMMAND_HAS_MILITARY_CRANE_COLLECTED_ALL_CARS, + COMMAND_SET_UPSIDEDOWN_CAR_NOT_DAMAGED, + COMMAND_CAN_PLAYER_START_MISSION, + COMMAND_MAKE_PLAYER_SAFE_FOR_CUTSCENE, + COMMAND_USE_TEXT_COMMANDS, + COMMAND_SET_THREAT_FOR_PED_TYPE, + COMMAND_CLEAR_THREAT_FOR_PED_TYPE, + COMMAND_GET_CAR_COLOURS, + COMMAND_SET_ALL_CARS_CAN_BE_DAMAGED, + COMMAND_SET_CAR_CAN_BE_DAMAGED, + COMMAND_MAKE_PLAYER_UNSAFE, + COMMAND_LOAD_COLLISION, + COMMAND_GET_BODY_CAST_HEALTH, + COMMAND_SET_CHARS_CHATTING, + COMMAND_MAKE_PLAYER_SAFE, + COMMAND_SET_CAR_STAYS_IN_CURRENT_LEVEL, + COMMAND_SET_CHAR_STAYS_IN_CURRENT_LEVEL, + COMMAND_SET_DRUNK_INPUT_DELAY, + COMMAND_SET_CHAR_MONEY, + COMMAND_INCREASE_CHAR_MONEY, + COMMAND_GET_OFFSET_FROM_OBJECT_IN_WORLD_COORDS, + COMMAND_REGISTER_LIFE_SAVED, + COMMAND_REGISTER_CRIMINAL_CAUGHT, + COMMAND_REGISTER_AMBULANCE_LEVEL, + COMMAND_REGISTER_FIRE_EXTINGUISHED, + COMMAND_TURN_PHONE_ON, + COMMAND_REGISTER_LONGEST_DODO_FLIGHT, + COMMAND_GET_OFFSET_FROM_CAR_IN_WORLD_COORDS, + COMMAND_SET_TOTAL_NUMBER_OF_KILL_FRENZIES, + COMMAND_BLOW_UP_RC_BUGGY, + COMMAND_REMOVE_CAR_FROM_CHASE, + COMMAND_IS_FRENCH_GAME, + COMMAND_IS_GERMAN_GAME, + COMMAND_CLEAR_MISSION_AUDIO, + COMMAND_SET_FADE_IN_AFTER_NEXT_ARREST, + COMMAND_SET_FADE_IN_AFTER_NEXT_DEATH, + COMMAND_SET_GANG_PED_MODEL_PREFERENCE, + COMMAND_SET_CHAR_USE_PEDNODE_SEEK, + COMMAND_SWITCH_VEHICLE_WEAPONS, + COMMAND_SET_GET_OUT_OF_JAIL_FREE, + COMMAND_SET_FREE_HEALTH_CARE, + COMMAND_IS_CAR_DOOR_CLOSED, + COMMAND_LOAD_AND_LAUNCH_MISSION, + COMMAND_LOAD_AND_LAUNCH_MISSION_INTERNAL, + COMMAND_SET_OBJECT_DRAW_LAST, + COMMAND_GET_AMMO_IN_PLAYER_WEAPON, + COMMAND_GET_AMMO_IN_CHAR_WEAPON, + COMMAND_REGISTER_KILL_FRENZY_PASSED, + COMMAND_SET_CHAR_SAY, + COMMAND_SET_NEAR_CLIP, + COMMAND_SET_RADIO_CHANNEL, + COMMAND_OVERRIDE_HOSPITAL_LEVEL, + COMMAND_OVERRIDE_POLICE_STATION_LEVEL, + COMMAND_FORCE_RAIN, + COMMAND_DOES_GARAGE_CONTAIN_CAR, + COMMAND_SET_CAR_TRACTION, + COMMAND_ARE_MEASUREMENTS_IN_METRES, + COMMAND_CONVERT_METRES_TO_FEET, + COMMAND_MARK_ROADS_BETWEEN_LEVELS, + COMMAND_MARK_PED_ROADS_BETWEEN_LEVELS, + COMMAND_SET_CAR_AVOID_LEVEL_TRANSITIONS, + COMMAND_SET_CHAR_AVOID_LEVEL_TRANSITIONS, + COMMAND_IS_THREAT_FOR_PED_TYPE, + COMMAND_CLEAR_AREA_OF_CHARS, + COMMAND_SET_TOTAL_NUMBER_OF_MISSIONS, + COMMAND_CONVERT_METRES_TO_FEET_INT, + COMMAND_REGISTER_FASTEST_TIME, + COMMAND_REGISTER_HIGHEST_SCORE, + COMMAND_WARP_CHAR_INTO_CAR_AS_PASSENGER, + COMMAND_IS_CAR_PASSENGER_SEAT_FREE, + COMMAND_GET_CHAR_IN_CAR_PASSENGER_SEAT, + COMMAND_SET_CHAR_IS_CHRIS_CRIMINAL, + COMMAND_START_CREDITS, + COMMAND_STOP_CREDITS, + COMMAND_ARE_CREDITS_FINISHED, + COMMAND_CREATE_SINGLE_PARTICLE, + COMMAND_SET_CHAR_IGNORE_LEVEL_TRANSITIONS, + COMMAND_GET_CHASE_CAR, + COMMAND_START_BOAT_FOAM_ANIMATION, + COMMAND_UPDATE_BOAT_FOAM_ANIMATION, + COMMAND_SET_MUSIC_DOES_FADE, + COMMAND_SET_INTRO_IS_PLAYING, + COMMAND_SET_PLAYER_HOOKER, + COMMAND_PLAY_END_OF_GAME_TUNE, + COMMAND_STOP_END_OF_GAME_TUNE, + COMMAND_GET_CAR_MODEL, + COMMAND_IS_PLAYER_SITTING_IN_CAR, + COMMAND_IS_PLAYER_SITTING_IN_ANY_CAR, + COMMAND_SET_SCRIPT_FIRE_AUDIO, + COMMAND_ARE_ANY_CAR_CHEATS_ACTIVATED, + COMMAND_SET_CHAR_SUFFERS_CRITICAL_HITS, + COMMAND_IS_PLAYER_LIFTING_A_PHONE, + COMMAND_IS_CHAR_SITTING_IN_CAR, + COMMAND_IS_CHAR_SITTING_IN_ANY_CAR, + COMMAND_IS_PLAYER_ON_FOOT, + COMMAND_IS_CHAR_ON_FOOT, + COMMAND_LOAD_COLLISION_WITH_SCREEN, + COMMAND_LOAD_SPLASH_SCREEN, + COMMAND_SET_CAR_IGNORE_LEVEL_TRANSITIONS, + COMMAND_MAKE_CRAIGS_CAR_A_BIT_STRONGER, + COMMAND_SET_JAMES_CAR_ON_PATH_TO_PLAYER, + COMMAND_LOAD_END_OF_GAME_TUNE, + COMMAND_ENABLE_PLAYER_CONTROL_CAMERA, + COMMAND_SET_OBJECT_ROTATION, + COMMAND_GET_DEBUG_CAMERA_COORDINATES, + COMMAND_GET_DEBUG_CAMERA_FRONT_VECTOR, + COMMAND_IS_PLAYER_TARGETTING_ANY_CHAR, + COMMAND_IS_PLAYER_TARGETTING_CHAR, + COMMAND_IS_PLAYER_TARGETTING_OBJECT, + COMMAND_TERMINATE_ALL_SCRIPTS_WITH_THIS_NAME, + COMMAND_DISPLAY_TEXT_WITH_NUMBER, + COMMAND_DISPLAY_TEXT_WITH_2_NUMBERS, + COMMAND_FAIL_CURRENT_MISSION, + COMMAND_GET_CLOSEST_OBJECT_OF_TYPE, + COMMAND_PLACE_OBJECT_RELATIVE_TO_OBJECT, + COMMAND_SET_ALL_OCCUPANTS_OF_CAR_LEAVE_CAR, + COMMAND_SET_INTERPOLATION_PARAMETERS, + COMMAND_GET_CLOSEST_CAR_NODE_WITH_HEADING_TOWARDS_POINT, + COMMAND_GET_CLOSEST_CAR_NODE_WITH_HEADING_AWAY_POINT, + COMMAND_GET_DEBUG_CAMERA_POINT_AT, + COMMAND_ATTACH_CHAR_TO_CAR, + COMMAND_DETACH_CHAR_FROM_CAR, + COMMAND_SET_CAR_CHANGE_LANE, + COMMAND_CLEAR_CHAR_LAST_WEAPON_DAMAGE, + COMMAND_CLEAR_CAR_LAST_WEAPON_DAMAGE, + COMMAND_GET_RANDOM_COP_IN_AREA, + COMMAND_GET_RANDOM_COP_IN_ZONE, + COMMAND_SET_CHAR_OBJ_FLEE_CAR, + COMMAND_GET_DRIVER_OF_CAR, + COMMAND_GET_NUMBER_OF_FOLLOWERS, + COMMAND_GIVE_REMOTE_CONTROLLED_MODEL_TO_PLAYER, + COMMAND_GET_CURRENT_PLAYER_WEAPON, + COMMAND_GET_CURRENT_CHAR_WEAPON, + COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_2D, + COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_2D, + COMMAND_LOCATE_CHAR_IN_CAR_OBJECT_2D, + COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_3D, + COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_3D, + COMMAND_LOCATE_CHAR_IN_CAR_OBJECT_3D, + COMMAND_SET_CAR_TEMP_ACTION, + COMMAND_SET_CAR_HANDBRAKE_TURN_RIGHT, + COMMAND_SET_CAR_HANDBRAKE_STOP, + COMMAND_IS_CHAR_ON_ANY_BIKE, + COMMAND_LOCATE_SNIPER_BULLET_2D, + COMMAND_LOCATE_SNIPER_BULLET_3D, + COMMAND_GET_NUMBER_OF_SEATS_IN_MODEL, + COMMAND_IS_PLAYER_ON_ANY_BIKE, + COMMAND_IS_CHAR_LYING_DOWN, + COMMAND_CAN_CHAR_SEE_DEAD_CHAR, + COMMAND_SET_ENTER_CAR_RANGE_MULTIPLIER, + COMMAND_SET_THREAT_REACTION_RANGE_MULTIPLIER, + COMMAND_SET_CHAR_CEASE_ATTACK_TIMER, + COMMAND_GET_REMOTE_CONTROLLED_CAR, + COMMAND_IS_PC_VERSION, + COMMAND_REPLAY, + COMMAND_IS_REPLAY_PLAYING, + COMMAND_IS_MODEL_AVAILABLE, + COMMAND_SHUT_CHAR_UP, + COMMAND_SET_ENABLE_RC_DETONATE, + COMMAND_SET_CAR_RANDOM_ROUTE_SEED, + COMMAND_IS_ANY_PICKUP_AT_COORDS, + COMMAND_GET_FIRST_PICKUP_COORDS, + COMMAND_GET_NEXT_PICKUP_COORDS, + COMMAND_REMOVE_ALL_CHAR_WEAPONS, + COMMAND_HAS_PLAYER_GOT_WEAPON, + COMMAND_HAS_CHAR_GOT_WEAPON, + COMMAND_IS_PLAYER_FACING_CHAR, + COMMAND_SET_TANK_DETONATE_CARS, + COMMAND_GET_POSITION_OF_ANALOGUE_STICKS, + COMMAND_IS_CAR_ON_FIRE, + COMMAND_IS_CAR_TYRE_BURST, + COMMAND_SET_CAR_DRIVE_STRAIGHT_AHEAD, + COMMAND_SET_CAR_WAIT, + COMMAND_IS_PLAYER_STANDING_ON_A_VEHICLE, + COMMAND_IS_PLAYER_FOOT_DOWN, + COMMAND_IS_CHAR_FOOT_DOWN, + COMMAND_INITIALISE_OBJECT_PATH, + COMMAND_START_OBJECT_ON_PATH, + COMMAND_SET_OBJECT_PATH_SPEED, + COMMAND_SET_OBJECT_PATH_POSITION, + COMMAND_GET_OBJECT_DISTANCE_ALONG_PATH, + COMMAND_CLEAR_OBJECT_PATH, + COMMAND_HELI_GOTO_COORDS, + COMMAND_IS_INT_VAR_EQUAL_TO_CONSTANT, + COMMAND_IS_INT_LVAR_EQUAL_TO_CONSTANT, + COMMAND_GET_DEAD_CHAR_PICKUP_COORDS, + COMMAND_CREATE_PROTECTION_PICKUP, + COMMAND_IS_CHAR_IN_ANY_BOAT, + COMMAND_IS_PLAYER_IN_ANY_BOAT, + COMMAND_IS_CHAR_IN_ANY_HELI, + COMMAND_IS_PLAYER_IN_ANY_HELI, + COMMAND_IS_CHAR_IN_ANY_PLANE, + COMMAND_IS_PLAYER_IN_ANY_PLANE, + COMMAND_IS_CHAR_IN_WATER, + COMMAND_SET_VAR_INT_TO_CONSTANT, + COMMAND_SET_LVAR_INT_TO_CONSTANT, + COMMAND_IS_INT_VAR_GREATER_THAN_CONSTANT, + COMMAND_IS_INT_LVAR_GREATER_THAN_CONSTANT, + COMMAND_IS_CONSTANT_GREATER_THAN_INT_VAR, + COMMAND_IS_CONSTANT_GREATER_THAN_INT_LVAR, + COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_CONSTANT, + COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_CONSTANT, + COMMAND_IS_CONSTANT_GREATER_OR_EQUAL_TO_INT_VAR, + COMMAND_IS_CONSTANT_GREATER_OR_EQUAL_TO_INT_LVAR, + COMMAND_GET_CHAR_WEAPON_IN_SLOT, + COMMAND_GET_CLOSEST_STRAIGHT_ROAD, + COMMAND_SET_CAR_FORWARD_SPEED, + COMMAND_SET_AREA_VISIBLE, + COMMAND_SET_CUTSCENE_ANIM_TO_LOOP, + COMMAND_MARK_CAR_AS_CONVOY_CAR, + COMMAND_RESET_HAVOC_CAUSED_BY_PLAYER, + COMMAND_GET_HAVOC_CAUSED_BY_PLAYER, + COMMAND_CREATE_SCRIPT_ROADBLOCK, + COMMAND_CLEAR_ALL_SCRIPT_ROADBLOCKS, + COMMAND_SET_CHAR_OBJ_WALK_TO_CHAR, + COMMAND_IS_PICKUP_IN_ZONE, + COMMAND_GET_OFFSET_FROM_CHAR_IN_WORLD_COORDS, + COMMAND_HAS_CHAR_BEEN_PHOTOGRAPHED, + COMMAND_SET_CHAR_OBJ_AIM_GUN_AT_CHAR, + COMMAND_SWITCH_SECURITY_CAMERA, + COMMAND_IS_CHAR_IN_FLYING_VEHICLE, + COMMAND_IS_PLAYER_IN_FLYING_VEHICLE, + COMMAND_HAS_SONY_CD_BEEN_READ, + COMMAND_GET_NUMBER_OF_SONY_CDS_READ, + COMMAND_ADD_SHORT_RANGE_BLIP_FOR_COORD_OLD, + COMMAND_ADD_SHORT_RANGE_BLIP_FOR_COORD, + COMMAND_ADD_SHORT_RANGE_SPRITE_BLIP_FOR_COORD, + COMMAND_ADD_MONEY_SPENT_ON_CLOTHES, + COMMAND_SET_HELI_ORIENTATION, + COMMAND_CLEAR_HELI_ORIENTATION, + COMMAND_PLANE_GOTO_COORDS, + COMMAND_GET_NTH_CLOSEST_CAR_NODE, + COMMAND_GET_NTH_CLOSEST_CHAR_NODE, + COMMAND_DRAW_WEAPONSHOP_CORONA, + COMMAND_SET_ENABLE_RC_DETONATE_ON_CONTACT, + COMMAND_FREEZE_CHAR_POSITION, + COMMAND_SET_CHAR_DROWNS_IN_WATER, + COMMAND_SET_OBJECT_RECORDS_COLLISIONS, + COMMAND_HAS_OBJECT_COLLIDED_WITH_ANYTHING, + COMMAND_REMOVE_RC_BUGGY, + COMMAND_HAS_PHOTOGRAPH_BEEN_TAKEN, + COMMAND_GET_CHAR_ARMOUR, + COMMAND_SET_CHAR_ARMOUR, + COMMAND_SET_HELI_STABILISER, + COMMAND_SET_CAR_STRAIGHT_LINE_DISTANCE, + COMMAND_POP_CAR_BOOT, + COMMAND_SHUT_PLAYER_UP, + COMMAND_SET_PLAYER_MOOD, + COMMAND_REQUEST_COLLISION, + COMMAND_LOCATE_OBJECT_2D, + COMMAND_LOCATE_OBJECT_3D, + COMMAND_IS_OBJECT_IN_WATER, + COMMAND_SET_CHAR_OBJ_STEAL_ANY_CAR_EVEN_MISSION_CAR, + COMMAND_IS_OBJECT_IN_AREA_2D, + COMMAND_IS_OBJECT_IN_AREA_3D, + COMMAND_SET_CHAR_CROUCH, + COMMAND_SET_ZONE_CIVILIAN_CAR_INFO, + COMMAND_REQUEST_ANIMATION, + COMMAND_HAS_ANIMATION_LOADED, + COMMAND_REMOVE_ANIMATION, + COMMAND_IS_CHAR_WAITING_FOR_WORLD_COLLISION, + COMMAND_IS_CAR_WAITING_FOR_WORLD_COLLISION, + COMMAND_IS_OBJECT_WAITING_FOR_WORLD_COLLISION, + COMMAND_SET_CHAR_SHUFFLE_INTO_DRIVERS_SEAT, + COMMAND_ATTACH_CHAR_TO_OBJECT, + COMMAND_SET_CHAR_AS_PLAYER_FRIEND, + COMMAND_DISPLAY_NTH_ONSCREEN_COUNTER, + COMMAND_DISPLAY_NTH_ONSCREEN_COUNTER_WITH_STRING, + COMMAND_ADD_SET_PIECE, + COMMAND_SET_EXTRA_COLOURS, + COMMAND_CLEAR_EXTRA_COLOURS, + COMMAND_CLOSE_CAR_BOOT, + COMMAND_GET_WHEELIE_STATS, + COMMAND_DISARM_CHAR, + COMMAND_BURST_CAR_TYRE, + COMMAND_IS_CHAR_OBJ_NO_OBJ, + COMMAND_IS_PLAYER_WEARING, + COMMAND_SET_PLAYER_CAN_DO_DRIVE_BY, + COMMAND_SET_CHAR_OBJ_SPRINT_TO_COORD, + COMMAND_CREATE_SWAT_ROPE, + COMMAND_SET_FIRST_PERSON_CONTROL_CAMERA, + COMMAND_GET_NEAREST_TYRE_TO_POINT, + COMMAND_SET_CAR_MODEL_COMPONENTS, + COMMAND_SWITCH_LIFT_CAMERA, + COMMAND_CLOSE_ALL_CAR_DOORS, + COMMAND_GET_DISTANCE_BETWEEN_COORDS_2D, + COMMAND_GET_DISTANCE_BETWEEN_COORDS_3D, + COMMAND_POP_CAR_BOOT_USING_PHYSICS, + COMMAND_SET_FIRST_PERSON_WEAPON_CAMERA, + COMMAND_IS_CHAR_LEAVING_VEHICLE_TO_DIE, + COMMAND_SORT_OUT_OBJECT_COLLISION_WITH_CAR, + COMMAND_GET_MAX_WANTED_LEVEL, + COMMAND_IS_CHAR_WANDER_PATH_CLEAR, + COMMAND_PRINT_HELP_WITH_NUMBER, + COMMAND_PRINT_HELP_FOREVER, + COMMAND_PRINT_HELP_FOREVER_WITH_NUMBER, + COMMAND_SET_CHAR_CAN_BE_DAMAGED_BY_MEMBERS_OF_GANG, + COMMAND_LOAD_AND_LAUNCH_MISSION_EXCLUSIVE, + COMMAND_IS_MISSION_AUDIO_PLAYING, + COMMAND_CREATE_LOCKED_PROPERTY_PICKUP, + COMMAND_CREATE_FORSALE_PROPERTY_PICKUP, + COMMAND_FREEZE_CAR_POSITION, + COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_CHAR, + COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_CAR, + COMMAND_HAS_CAR_BEEN_DAMAGED_BY_CHAR, + COMMAND_HAS_CAR_BEEN_DAMAGED_BY_CAR, + COMMAND_GET_RADIO_CHANNEL, + COMMAND_DISPLAY_TEXT_WITH_3_NUMBERS, + COMMAND_IS_CAR_DROWNING_IN_WATER, + COMMAND_IS_CHAR_DROWNING_IN_WATER, + COMMAND_DISABLE_CUTSCENE_SHADOWS, + COMMAND_HAS_GLASS_BEEN_SHATTERED_NEARBY, + COMMAND_ATTACH_CUTSCENE_OBJECT_TO_BONE, + COMMAND_ATTACH_CUTSCENE_OBJECT_TO_COMPONENT, + COMMAND_SET_CHAR_STAY_IN_CAR_WHEN_JACKED, + COMMAND_IS_MISSION_AUDIO_LOADING, + COMMAND_ADD_MONEY_SPENT_ON_WEAPONS, + COMMAND_ADD_MONEY_SPENT_ON_PROPERTY, + COMMAND_ADD_MONEY_SPENT_ON_AUTO_PAINTING, + COMMAND_SET_CHAR_ANSWERING_MOBILE, + COMMAND_SET_PLAYER_DRUNKENNESS, + COMMAND_GET_PLAYER_DRUNKENNESS, + COMMAND_SET_PLAYER_DRUG_LEVEL, + COMMAND_GET_PLAYER_DRUG_LEVEL, + COMMAND_ADD_LOAN_SHARK_VISITS, + COMMAND_ADD_STORES_KNOCKED_OFF, + COMMAND_ADD_MOVIE_STUNTS, + COMMAND_ADD_NUMBER_OF_ASSASSINATIONS, + COMMAND_ADD_PIZZAS_DELIVERED, + COMMAND_ADD_GARBAGE_PICKUPS, + COMMAND_ADD_ICE_CREAMS_SOLD, + COMMAND_SET_TOP_SHOOTING_RANGE_SCORE, + COMMAND_ADD_SHOOTING_RANGE_RANK, + COMMAND_ADD_MONEY_SPENT_ON_GAMBLING, + COMMAND_ADD_MONEY_WON_ON_GAMBLING, + COMMAND_SET_LARGEST_GAMBLING_WIN, + COMMAND_SET_CHAR_IN_PLAYERS_GROUP_CAN_FIGHT, + COMMAND_CLEAR_CHAR_WAIT_STATE, + COMMAND_GET_RANDOM_CAR_OF_TYPE_IN_AREA_NO_SAVE, + COMMAND_SET_CAN_BURST_CAR_TYRES, + COMMAND_SET_PLAYER_AUTO_AIM, + COMMAND_FIRE_HUNTER_GUN, + COMMAND_SET_PROPERTY_AS_OWNED, + COMMAND_ADD_BLOOD_RING_KILLS, + COMMAND_SET_LONGEST_TIME_IN_BLOOD_RING, + COMMAND_REMOVE_EVERYTHING_FOR_HUGE_CUTSCENE, + COMMAND_IS_PLAYER_TOUCHING_VEHICLE, + COMMAND_IS_CHAR_TOUCHING_VEHICLE, + COMMAND_CHECK_FOR_PED_MODEL_AROUND_PLAYER, + COMMAND_CLEAR_CHAR_FOLLOW_PATH, + COMMAND_SET_CHAR_CAN_BE_SHOT_IN_VEHICLE, + COMMAND_ATTACH_CUTSCENE_OBJECT_TO_VEHICLE, + COMMAND_LOAD_MISSION_TEXT, + COMMAND_SET_TONIGHTS_EVENT, + COMMAND_CLEAR_CHAR_LAST_DAMAGE_ENTITY, + COMMAND_CLEAR_CAR_LAST_DAMAGE_ENTITY, + COMMAND_FREEZE_OBJECT_POSITION, + COMMAND_SET_PLAYER_HAS_MET_DEBBIE_HARRY, + COMMAND_SET_RIOT_INTENSITY, + COMMAND_IS_CAR_IN_ANGLED_AREA_2D, + COMMAND_IS_CAR_IN_ANGLED_AREA_3D, + COMMAND_REMOVE_WEAPON_FROM_CHAR, + COMMAND_SET_UP_TAXI_SHORTCUT, + COMMAND_CLEAR_TAXI_SHORTCUT, + COMMAND_SET_CHAR_OBJ_GOTO_CAR_ON_FOOT, + COMMAND_GET_CLOSEST_WATER_NODE, + COMMAND_ADD_PORN_LEAFLET_TO_RUBBISH, + COMMAND_CREATE_CLOTHES_PICKUP, + COMMAND_CHANGE_BLIP_THRESHOLD, + COMMAND_MAKE_PLAYER_FIRE_PROOF, + COMMAND_INCREASE_PLAYER_MAX_HEALTH, + COMMAND_INCREASE_PLAYER_MAX_ARMOUR, + COMMAND_CREATE_RANDOM_CHAR_AS_DRIVER, + COMMAND_CREATE_RANDOM_CHAR_AS_PASSENGER, + COMMAND_SET_CHAR_IGNORE_THREATS_BEHIND_OBJECTS, + COMMAND_ENSURE_PLAYER_HAS_DRIVE_BY_WEAPON, + COMMAND_MAKE_HELI_COME_CRASHING_DOWN, + COMMAND_ADD_EXPLOSION_NO_SOUND, + COMMAND_SET_OBJECT_AREA_VISIBLE, + COMMAND_WAS_VEHICLE_EVER_POLICE, + COMMAND_SET_CHAR_NEVER_TARGETTED, + COMMAND_LOAD_UNCOMPRESSED_ANIM, + COMMAND_WAS_CUTSCENE_SKIPPED, + COMMAND_SET_CHAR_CROUCH_WHEN_THREATENED, + COMMAND_IS_CHAR_IN_ANY_POLICE_VEHICLE, + COMMAND_DOES_CHAR_EXIST, + COMMAND_DOES_VEHICLE_EXIST, + COMMAND_ADD_SHORT_RANGE_BLIP_FOR_CONTACT_POINT, + COMMAND_ADD_SHORT_RANGE_SPRITE_BLIP_FOR_CONTACT_POINT, + COMMAND_IS_CHAR_STUCK, + COMMAND_SET_ALL_TAXIS_HAVE_NITRO, + COMMAND_SET_CHAR_STOP_SHOOT_DONT_SEEK_ENTITY, + COMMAND_FREEZE_CAR_POSITION_AND_DONT_LOAD_COLLISION, + COMMAND_FREEZE_CHAR_POSITION_AND_DONT_LOAD_COLLISION, + COMMAND_FREEZE_OBJECT_POSITION_AND_DONT_LOAD_COLLISION, + COMMAND_SET_FADE_AND_JUMPCUT_AFTER_RC_EXPLOSION, + COMMAND_REGISTER_VIGILANTE_LEVEL, + COMMAND_CLEAR_ALL_CHAR_ANIMS, + COMMAND_SET_MAXIMUM_NUMBER_OF_CARS_IN_GARAGE, + COMMAND_WANTED_STARS_ARE_FLASHING, + COMMAND_SET_ALLOW_HURRICANES, + COMMAND_PLAY_ANNOUNCEMENT, + COMMAND_SET_PLAYER_IS_IN_STADIUM, + COMMAND_GET_BUS_FARES_COLLECTED_BY_PLAYER, + COMMAND_SET_CHAR_OBJ_BUY_ICE_CREAM, + COMMAND_DISPLAY_RADAR, + COMMAND_REGISTER_BEST_POSITION, + COMMAND_IS_PLAYER_IN_INFO_ZONE, + COMMAND_CLEAR_CHAR_ICE_CREAM_PURCHASE, + COMMAND_IS_IN_CAR_FIRE_BUTTON_PRESSED, + COMMAND_HAS_CHAR_ATTEMPTED_ATTRACTOR, + COMMAND_SET_LOAD_COLLISION_FOR_CAR_FLAG, + COMMAND_SET_LOAD_COLLISION_FOR_CHAR_FLAG, + COMMAND_SET_LOAD_COLLISION_FOR_OBJECT_FLAG, + COMMAND_ADD_BIG_GUN_FLASH, + COMMAND_HAS_CHAR_BOUGHT_ICE_CREAM, + COMMAND_GET_PROGRESS_PERCENTAGE, + COMMAND_SET_SHORTCUT_PICKUP_POINT, + COMMAND_SET_SHORTCUT_DROPOFF_POINT_FOR_MISSION, + COMMAND_GET_RANDOM_ICE_CREAM_CUSTOMER_IN_AREA, + COMMAND_GET_RANDOM_ICE_CREAM_CUSTOMER_IN_ZONE, + COMMAND_UNLOCK_ALL_CAR_DOORS_IN_AREA, + COMMAND_SET_GANG_ATTACK_PLAYER_WITH_COPS, + COMMAND_SET_CHAR_FRIGHTENED_IN_JACKED_CAR, + COMMAND_SET_VEHICLE_TO_FADE_IN, + COMMAND_REGISTER_ODDJOB_MISSION_PASSED, + COMMAND_IS_PLAYER_IN_SHORTCUT_TAXI, + COMMAND_IS_CHAR_DUCKING, + COMMAND_CREATE_DUST_EFFECT_FOR_CUTSCENE_HELI, + COMMAND_REGISTER_FIRE_LEVEL, + COMMAND_IS_AUSTRALIAN_GAME, + COMMAND_DISARM_CAR_BOMB, +#if (defined GTAVC_JP_PATCH || defined SUPPORT_JAPANESE_SCRIPT) + COMMAND_IS_JAPANESE_GAME, +#elif (!defined GTA_PS2) + COMMAND_SET_ONSCREEN_COUNTER_FLASH_WHEN_FIRST_DISPLAYED, +#endif +#if (defined GTA_PC && !defined GTAVC_JP_PATCH || defined GTA_XBOX || defined SUPPORT_XBOX_SCRIPT || defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) + COMMAND_SHUFFLE_CARD_DECKS, + COMMAND_FETCH_NEXT_CARD, + COMMAND_GET_OBJECT_VELOCITY, + COMMAND_IS_DEBUG_CAMERA_ON, + COMMAND_ADD_TO_OBJECT_ROTATION_VELOCITY, + COMMAND_SET_OBJECT_ROTATION_VELOCITY, + COMMAND_IS_OBJECT_STATIC, + COMMAND_GET_ANGLE_BETWEEN_2D_VECTORS, + COMMAND_DO_2D_RECTANGLES_COLLIDE, + COMMAND_GET_OBJECT_ROTATION_VELOCITY, + COMMAND_ADD_VELOCITY_RELATIVE_TO_OBJECT_VELOCITY, + COMMAND_GET_OBJECT_SPEED, +#endif +#if (defined GTA_XBOX || defined SUPPORT_XBOX_SCRIPT) + COMMAND_MARK_CUTSCENE_START, + COMMAND_MARK_CUTSCENE_END, + COMMAND_CUTSCENE_SCROLL, +#elif (defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) + COMMAND_IS_MISSION_SKIP, + COMMAND_SET_IN_AMMUNATION, + COMMAND_DO_SAVE_GAME, + COMMAND_IS_RETRY, + COMMAND_DUMMY, + COMMAND_MARK_CUTSCENE_START, + COMMAND_MARK_CUTSCENE_END, + COMMAND_CUTSCENE_SCROLL, +#endif +#ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT + LAST_SCRIPT_COMMAND +#endif +}; + +#ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT + +enum eScriptArgument +{ + ARGTYPE_NONE = 0, + ARGTYPE_INT, + ARGTYPE_FLOAT, + ARGTYPE_STRING, + ARGTYPE_LABEL, + ARGTYPE_BOOL, + ARGTYPE_PED_HANDLE, + ARGTYPE_VEHICLE_HANDLE, + ARGTYPE_OBJECT_HANDLE, + ARGTYPE_ANDOR +}; + +struct tScriptCommandData +{ + int id; + const char name[64]; + eScriptArgument input[18]; + eScriptArgument output[18]; + bool cond; + int position; + const char name_override[8]; +}; +#endif \ No newline at end of file diff --git a/src/miami/control/ScriptDebug.cpp b/src/miami/control/ScriptDebug.cpp new file mode 100644 index 00000000..1ca5f588 --- /dev/null +++ b/src/miami/control/ScriptDebug.cpp @@ -0,0 +1,1765 @@ +#include "common.h" + +#include "Script.h" +#include "ScriptCommands.h" + +#include "Debug.h" +#include "FileMgr.h" +#include "GameLogic.h" +#ifdef MISSION_REPLAY +#include "GenericGameStorage.h" +#endif +#include "Messages.h" +#include "Timer.h" +#include "Stats.h" +#ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT +#include +#endif + +#ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT + +char CRunningScript::commandInfo[1024]; +uint32 CRunningScript::storedIp; + +#define REGISTER_COMMAND(command, in, out, cond, ovrd, visual) { command, #command, in, out, cond, ovrd, visual } +#define INPUT_ARGUMENTS(...) { __VA_ARGS__ ARGTYPE_NONE } +#define OUTPUT_ARGUMENTS(...) { __VA_ARGS__ ARGTYPE_NONE } +const tScriptCommandData commands[] = { + REGISTER_COMMAND(COMMAND_NOP, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_WAIT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GOTO, INPUT_ARGUMENTS(ARGTYPE_LABEL,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SHAKE_CAM, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_VAR_INT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " ="), + REGISTER_COMMAND(COMMAND_SET_VAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " ="), + REGISTER_COMMAND(COMMAND_SET_LVAR_INT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " ="), + REGISTER_COMMAND(COMMAND_SET_LVAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " ="), + REGISTER_COMMAND(COMMAND_ADD_VAL_TO_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " +="), + REGISTER_COMMAND(COMMAND_ADD_VAL_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +="), + REGISTER_COMMAND(COMMAND_ADD_VAL_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " +="), + REGISTER_COMMAND(COMMAND_ADD_VAL_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +="), + REGISTER_COMMAND(COMMAND_SUB_VAL_FROM_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " -="), + REGISTER_COMMAND(COMMAND_SUB_VAL_FROM_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -="), + REGISTER_COMMAND(COMMAND_SUB_VAL_FROM_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " -="), + REGISTER_COMMAND(COMMAND_SUB_VAL_FROM_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -="), + REGISTER_COMMAND(COMMAND_MULT_INT_VAR_BY_VAL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " *="), + REGISTER_COMMAND(COMMAND_MULT_FLOAT_VAR_BY_VAL, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " *="), + REGISTER_COMMAND(COMMAND_MULT_INT_LVAR_BY_VAL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " *="), + REGISTER_COMMAND(COMMAND_MULT_FLOAT_LVAR_BY_VAL, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " *="), + REGISTER_COMMAND(COMMAND_DIV_INT_VAR_BY_VAL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " /="), + REGISTER_COMMAND(COMMAND_DIV_FLOAT_VAR_BY_VAL, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " /="), + REGISTER_COMMAND(COMMAND_DIV_INT_LVAR_BY_VAL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " /="), + REGISTER_COMMAND(COMMAND_DIV_FLOAT_LVAR_BY_VAL, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " /="), + REGISTER_COMMAND(COMMAND_IS_INT_VAR_GREATER_THAN_NUMBER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_INT_LVAR_GREATER_THAN_NUMBER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_NUMBER_GREATER_THAN_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_NUMBER_GREATER_THAN_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_INT_VAR_GREATER_THAN_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_INT_LVAR_GREATER_THAN_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_INT_VAR_GREATER_THAN_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_INT_LVAR_GREATER_THAN_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_GREATER_THAN_NUMBER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_GREATER_THAN_NUMBER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_NUMBER_GREATER_THAN_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_NUMBER_GREATER_THAN_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_GREATER_THAN_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_GREATER_THAN_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_GREATER_THAN_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_GREATER_THAN_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_IS_INT_VAR_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " =="), + REGISTER_COMMAND(COMMAND_IS_INT_LVAR_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " =="), + REGISTER_COMMAND(COMMAND_IS_INT_VAR_EQUAL_TO_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " =="), + REGISTER_COMMAND(COMMAND_IS_INT_LVAR_EQUAL_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " =="), + REGISTER_COMMAND(COMMAND_IS_INT_VAR_EQUAL_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " =="), + REGISTER_COMMAND(COMMAND_IS_INT_VAR_NOT_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " !="), + REGISTER_COMMAND(COMMAND_IS_INT_LVAR_NOT_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " !="), + REGISTER_COMMAND(COMMAND_IS_INT_VAR_NOT_EQUAL_TO_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " !="), + REGISTER_COMMAND(COMMAND_IS_INT_LVAR_NOT_EQUAL_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " !="), + REGISTER_COMMAND(COMMAND_IS_INT_VAR_NOT_EQUAL_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " !="), + REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " =="), + REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " =="), + REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_EQUAL_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " =="), + REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_EQUAL_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " =="), + REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_EQUAL_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " =="), + REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " !="), + REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_NOT_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " !="), + REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " !="), + REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_NOT_EQUAL_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " !="), + REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " !="), + REGISTER_COMMAND(COMMAND_GOTO_IF_TRUE, INPUT_ARGUMENTS(ARGTYPE_LABEL,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GOTO_IF_FALSE, INPUT_ARGUMENTS(ARGTYPE_LABEL,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_TERMINATE_THIS_SCRIPT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_START_NEW_SCRIPT, INPUT_ARGUMENTS(ARGTYPE_LABEL,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GOSUB, INPUT_ARGUMENTS(ARGTYPE_LABEL,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_RETURN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_LINE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_PLAYER_COORDINATES, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_COORDINATES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_BOOL,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_BOOL,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_INT_VAR_TO_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " +="), + REGISTER_COMMAND(COMMAND_ADD_FLOAT_VAR_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +="), + REGISTER_COMMAND(COMMAND_ADD_INT_LVAR_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " +="), + REGISTER_COMMAND(COMMAND_ADD_FLOAT_LVAR_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +="), + REGISTER_COMMAND(COMMAND_ADD_INT_VAR_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " +="), + REGISTER_COMMAND(COMMAND_ADD_FLOAT_VAR_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +="), + REGISTER_COMMAND(COMMAND_ADD_INT_LVAR_TO_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " +="), + REGISTER_COMMAND(COMMAND_ADD_FLOAT_LVAR_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +="), + REGISTER_COMMAND(COMMAND_SUB_INT_VAR_FROM_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " -="), + REGISTER_COMMAND(COMMAND_SUB_FLOAT_VAR_FROM_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -="), + REGISTER_COMMAND(COMMAND_SUB_INT_LVAR_FROM_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " -="), + REGISTER_COMMAND(COMMAND_SUB_FLOAT_LVAR_FROM_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -="), + REGISTER_COMMAND(COMMAND_SUB_INT_VAR_FROM_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " -="), + REGISTER_COMMAND(COMMAND_SUB_FLOAT_VAR_FROM_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -="), + REGISTER_COMMAND(COMMAND_SUB_INT_LVAR_FROM_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " -="), + REGISTER_COMMAND(COMMAND_SUB_FLOAT_LVAR_FROM_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -="), + REGISTER_COMMAND(COMMAND_MULT_INT_VAR_BY_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " *="), + REGISTER_COMMAND(COMMAND_MULT_FLOAT_VAR_BY_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " *="), + REGISTER_COMMAND(COMMAND_MULT_INT_LVAR_BY_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " *="), + REGISTER_COMMAND(COMMAND_MULT_FLOAT_LVAR_BY_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " *="), + REGISTER_COMMAND(COMMAND_MULT_INT_VAR_BY_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " *="), + REGISTER_COMMAND(COMMAND_MULT_FLOAT_VAR_BY_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " *="), + REGISTER_COMMAND(COMMAND_MULT_INT_LVAR_BY_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " *="), + REGISTER_COMMAND(COMMAND_MULT_FLOAT_LVAR_BY_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " *="), + REGISTER_COMMAND(COMMAND_DIV_INT_VAR_BY_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " /="), + REGISTER_COMMAND(COMMAND_DIV_FLOAT_VAR_BY_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " /="), + REGISTER_COMMAND(COMMAND_DIV_INT_LVAR_BY_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " /="), + REGISTER_COMMAND(COMMAND_DIV_FLOAT_LVAR_BY_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " /="), + REGISTER_COMMAND(COMMAND_DIV_INT_VAR_BY_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " /="), + REGISTER_COMMAND(COMMAND_DIV_FLOAT_VAR_BY_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " /="), + REGISTER_COMMAND(COMMAND_DIV_INT_LVAR_BY_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " /="), + REGISTER_COMMAND(COMMAND_DIV_FLOAT_LVAR_BY_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " /="), + REGISTER_COMMAND(COMMAND_ADD_TIMED_VAL_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +=@"), + REGISTER_COMMAND(COMMAND_ADD_TIMED_VAL_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +=@"), + REGISTER_COMMAND(COMMAND_ADD_TIMED_FLOAT_VAR_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +=@"), + REGISTER_COMMAND(COMMAND_ADD_TIMED_FLOAT_LVAR_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +=@"), + REGISTER_COMMAND(COMMAND_ADD_TIMED_FLOAT_LVAR_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +=@"), + REGISTER_COMMAND(COMMAND_ADD_TIMED_FLOAT_VAR_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +=@"), + REGISTER_COMMAND(COMMAND_SUB_TIMED_VAL_FROM_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -=@"), + REGISTER_COMMAND(COMMAND_SUB_TIMED_VAL_FROM_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -=@"), + REGISTER_COMMAND(COMMAND_SUB_TIMED_FLOAT_VAR_FROM_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -=@"), + REGISTER_COMMAND(COMMAND_SUB_TIMED_FLOAT_LVAR_FROM_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -=@"), + REGISTER_COMMAND(COMMAND_SUB_TIMED_FLOAT_LVAR_FROM_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -=@"), + REGISTER_COMMAND(COMMAND_SUB_TIMED_FLOAT_VAR_FROM_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -=@"), + REGISTER_COMMAND(COMMAND_SET_VAR_INT_TO_VAR_INT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " ="), + REGISTER_COMMAND(COMMAND_SET_LVAR_INT_TO_LVAR_INT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " ="), + REGISTER_COMMAND(COMMAND_SET_VAR_FLOAT_TO_VAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " ="), + REGISTER_COMMAND(COMMAND_SET_LVAR_FLOAT_TO_LVAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " ="), + REGISTER_COMMAND(COMMAND_SET_VAR_FLOAT_TO_LVAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " ="), + REGISTER_COMMAND(COMMAND_SET_LVAR_FLOAT_TO_VAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " ="), + REGISTER_COMMAND(COMMAND_SET_VAR_INT_TO_LVAR_INT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " ="), + REGISTER_COMMAND(COMMAND_SET_LVAR_INT_TO_VAR_INT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " ="), + REGISTER_COMMAND(COMMAND_CSET_VAR_INT_TO_VAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " =#"), + REGISTER_COMMAND(COMMAND_CSET_VAR_FLOAT_TO_VAR_INT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " =#"), + REGISTER_COMMAND(COMMAND_CSET_LVAR_INT_TO_VAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " =#"), + REGISTER_COMMAND(COMMAND_CSET_LVAR_FLOAT_TO_VAR_INT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " =#"), + REGISTER_COMMAND(COMMAND_CSET_VAR_INT_TO_LVAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " =#"), + REGISTER_COMMAND(COMMAND_CSET_VAR_FLOAT_TO_LVAR_INT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " =#"), + REGISTER_COMMAND(COMMAND_CSET_LVAR_INT_TO_LVAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " =#"), + REGISTER_COMMAND(COMMAND_CSET_LVAR_FLOAT_TO_LVAR_INT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " =#"), + REGISTER_COMMAND(COMMAND_ABS_VAR_INT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " ABS"), + REGISTER_COMMAND(COMMAND_ABS_LVAR_INT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " ABS"), + REGISTER_COMMAND(COMMAND_ABS_VAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " ABS"), + REGISTER_COMMAND(COMMAND_ABS_LVAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " ABS"), + REGISTER_COMMAND(COMMAND_GENERATE_RANDOM_FLOAT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GENERATE_RANDOM_INT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_PED_HANDLE,), false, -1, ""), + REGISTER_COMMAND(COMMAND_DELETE_CHAR, INPUT_ARGUMENTS(ARGTYPE_PED_HANDLE,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CHAR_WANDER_DIR, INPUT_ARGUMENTS(ARGTYPE_PED_HANDLE, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CHAR_WANDER_RANGE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CHAR_FOLLOW_PATH, INPUT_ARGUMENTS(ARGTYPE_PED_HANDLE, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CHAR_SET_IDLE, INPUT_ARGUMENTS(ARGTYPE_PED_HANDLE,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CHAR_COORDINATES, INPUT_ARGUMENTS(ARGTYPE_PED_HANDLE,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_COORDINATES, INPUT_ARGUMENTS(ARGTYPE_PED_HANDLE, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_STILL_ALIVE, INPUT_ARGUMENTS(ARGTYPE_PED_HANDLE,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_PED_HANDLE, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_BOOL,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_PED_HANDLE, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_BOOL,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE,), false, -1, ""), + REGISTER_COMMAND(COMMAND_DELETE_CAR, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CAR_GOTO_COORDINATES, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CAR_WANDER_RANDOMLY, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CAR_SET_IDLE, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CAR_COORDINATES, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_COORDINATES, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_STILL_ALIVE, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_CRUISE_SPEED, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_DRIVING_STYLE, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_MISSION, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_BOOL,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_IN_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_BOOL,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SPECIAL_0, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SPECIAL_1, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SPECIAL_2, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SPECIAL_3, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SPECIAL_4, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SPECIAL_5, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SPECIAL_6, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SPECIAL_7, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_BIG, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_NOW, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_SOON, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_PRINTS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_TIME_OF_DAY, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TIME_OF_DAY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_MINUTES_TO_TIME_OF_DAY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_POINT_ON_SCREEN, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_DEBUG_ON, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DEBUG_OFF, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_RETURN_TRUE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_RETURN_FALSE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_VAR_INT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_VAR_FLOAT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_LVAR_INT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_LVAR_FLOAT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_LBRACKET, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_RBRACKET, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REPEAT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ENDREPEAT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IF, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IFNOT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ELSE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ENDIF, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_WHILE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_WHILENOT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ENDWHILE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ANDOR, INPUT_ARGUMENTS(ARGTYPE_ANDOR,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_LAUNCH_MISSION, INPUT_ARGUMENTS(ARGTYPE_LABEL,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_MISSION_HAS_FINISHED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_STORE_CAR_CHAR_IS_IN, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_STORE_CAR_PLAYER_IS_IN, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_ANY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_BUTTON_PRESSED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GET_PAD_STATE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ANY_MEANS_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ANY_MEANS_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_IN_CAR_CHAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ANY_MEANS_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ANY_MEANS_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_IN_CAR_CHAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_DELETE_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_SCORE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_SCORE_GREATER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_STORE_SCORE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GIVE_REMOTE_CONTROLLED_CAR_TO_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ALTER_WANTED_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ALTER_WANTED_LEVEL_NO_DROP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_WANTED_LEVEL_GREATER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_WANTED_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_DEATHARREST_STATE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_DEATHARREST_BEEN_EXECUTED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_AMMO_TO_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_AMMO_TO_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_AMMO_TO_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_STILL_ALIVE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_DEAD, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_DEAD, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_DEAD, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_THREAT_SEARCH, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_THREAT_REACTION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_NO_OBJ, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ORDER_DRIVER_OUT_OF_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ORDER_CHAR_TO_DRIVE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_PATROL_POINT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_GANGZONE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ZONE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_PRESSING_HORN, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_CHAR_SPOTTED_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), +#ifdef SUPPORT_GINPUT_SCRIPT + REGISTER_COMMAND(COMMAND_HAS_PAD_IN_HANDS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), +#else + REGISTER_COMMAND(COMMAND_ORDER_CHAR_TO_BACKDOOR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), +#endif + REGISTER_COMMAND(COMMAND_ADD_CHAR_TO_GANG, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_OBJECTIVE_PASSED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_DRIVE_AGGRESSION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_MAX_DRIVESPEED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_CHAR_INSIDE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_WARP_PLAYER_FROM_CAR_TO_COORD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_MAKE_CHAR_DO_NOTHING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_INVINCIBLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_INVINCIBLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_GRAPHIC_TYPE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_GRAPHIC_TYPE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_PLAYER_BEEN_ARRESTED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_STOP_CHAR_DRIVING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_KILL_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_FAVOURITE_CAR_MODEL_FOR_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OCCUPATION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CHANGE_CAR_LOCK, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SHAKE_CAM_WITH_POINT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_REMAP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_CAR_JUST_SUNK, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_NO_COLLIDE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_DEAD_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_DEAD_IN_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_TRAILER_ATTACHED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_ON_TRAILER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_CAR_GOT_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_PARK, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_PARK_FINISHED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_KILL_ALL_PASSENGERS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_BULLETPROOF, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_FLAMEPROOF, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_ROCKETPROOF, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CARBOMB_ACTIVE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GIVE_CAR_ALARM, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PUT_CAR_ON_TRAILER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_CRUSHED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_GANG_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_CAR_GENERATOR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_CAR_GENERATOR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_PAGER_MESSAGE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DISPLAY_ONSCREEN_TIMER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_ONSCREEN_TIMER, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DISPLAY_ONSCREEN_COUNTER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_ONSCREEN_COUNTER, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_ZONE_CAR_INFO, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_GANG_ZONE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_ZONE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_DENSITY, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PED_DENSITY, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_POINT_CAMERA_AT_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_POINT_CAMERA_AT_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_POINT_CAMERA_AT_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_RESTORE_CAMERA, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SHAKE_PAD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_ZONE_PED_INFO, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TIME_SCALE, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_IN_AIR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_FIXED_CAMERA_POSITION, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_POINT_CAMERA_AT_POINT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_CAR_OLD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_CHAR_OLD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_OBJECT_OLD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_BLIP, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CHANGE_BLIP_COLOUR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DIM_BLIP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_COORD_OLD, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_CHANGE_BLIP_SCALE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_FADING_COLOUR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DO_FADE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_FADING_STATUS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_HOSPITAL_RESTART, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_POLICE_RESTART, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_OVERRIDE_NEXT_RESTART, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DRAW_SHADOW, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_PLAYER_HEADING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_HEADING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CHAR_HEADING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_HEADING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CAR_HEADING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_HEADING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_OBJECT_HEADING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_OBJECT_HEADING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_TOUCHING_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_TOUCHING_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_AMMO, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_AMMO, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_AMMO, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_LOAD_CAMERA_SPLINE, INPUT_ARGUMENTS(ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_MOVE_CAMERA_ALONG_SPLINE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CAMERA_POSITION_ALONG_SPLINE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_DECLARE_MISSION_FLAG, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DECLARE_MISSION_FLAG_FOR_CONTACT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DECLARE_BASE_BRIEF_ID_FOR_CONTACT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_HEALTH_GREATER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_HEALTH_GREATER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_HEALTH_GREATER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_CONTACT_POINT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_COORD, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_CHANGE_BLIP_DISPLAY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_ONE_OFF_SOUND, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_CONTINUOUS_SOUND, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_SOUND, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_STUCK_ON_ROOF, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_UPSIDEDOWN_CAR_CHECK, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_UPSIDEDOWN_CAR_CHECK, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_WAIT_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FLEE_ON_FOOT_TILL_SAFE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GUARD_SPOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GUARD_AREA, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_WAIT_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_AREA_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_AREA_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_AREA_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_AREA_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_STOPPED_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_AREA_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_AREA_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_STOPPED_IN_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_STOPPED_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_STOPPED_IN_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GIVE_WEAPON_TO_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GIVE_WEAPON_TO_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GIVE_WEAPON_TO_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_CONTROL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_FORCE_WEATHER, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_FORCE_WEATHER_NOW, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_RELEASE_WEATHER, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CURRENT_PLAYER_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CURRENT_CHAR_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CURRENT_CAR_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_OBJECT_COORDINATES, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_OBJECT_COORDINATES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_GAME_TIMER, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_TURN_CHAR_TO_FACE_COORD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_TURN_PLAYER_TO_FACE_COORD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_STORE_WANTED_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_STOPPED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_MARK_CHAR_AS_NO_LONGER_NEEDED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_MARK_CAR_AS_NO_LONGER_NEEDED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_MARK_OBJECT_AS_NO_LONGER_NEEDED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DONT_REMOVE_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DONT_REMOVE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DONT_REMOVE_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_CHAR_AS_PASSENGER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_KILL_CHAR_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_KILL_PLAYER_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_KILL_CHAR_ANY_MEANS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_KILL_PLAYER_ANY_MEANS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FLEE_CHAR_ON_FOOT_TILL_SAFE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FLEE_PLAYER_ON_FOOT_TILL_SAFE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FLEE_CHAR_ON_FOOT_ALWAYS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FLEE_PLAYER_ON_FOOT_ALWAYS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GOTO_CHAR_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GOTO_PLAYER_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_LEAVE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_ENTER_CAR_AS_PASSENGER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_ENTER_CAR_AS_DRIVER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FOLLOW_CAR_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FIRE_AT_OBJECT_FROM_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_DESTROY_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_DESTROY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GOTO_AREA_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GOTO_AREA_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FOLLOW_CAR_ON_FOOT_WITH_OFFSET, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GUARD_ATTACK, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_AS_LEADER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_AS_LEADER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_LEAVE_GROUP, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FOLLOW_ROUTE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_ROUTE_POINT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_NUMBER_BIG, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_NUMBER, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_NUMBER_NOW, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_NUMBER_SOON, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_ROADS_ON, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_ROADS_OFF, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_NUMBER_OF_PASSENGERS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_MAXIMUM_NUMBER_OF_PASSENGERS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_DENSITY_MULTIPLIER, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_HEAVY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_CHAR_THREAT_SEARCH, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ACTIVATE_CRANE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DEACTIVATE_CRANE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_MAX_WANTED_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SAVE_VAR_INT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SAVE_VAR_FLOAT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_IN_AIR_PROPER, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_UPSIDEDOWN, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GET_PLAYER_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_CANCEL_OVERRIDE_RESTART, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_POLICE_IGNORE_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_PAGER_MESSAGE_WITH_NUMBER, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_START_KILL_FRENZY, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_READ_KILL_FRENZY_STATUS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SQRT, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ON_FOOT_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_IN_CAR_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ON_FOOT_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_IN_CAR_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ON_FOOT_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_IN_CAR_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ON_FOOT_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_IN_CAR_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GENERATE_RANDOM_FLOAT_IN_RANGE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GENERATE_RANDOM_INT_IN_RANGE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_LOCK_CAR_DOORS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_EXPLODE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_EXPLOSION, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_UPRIGHT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_TURN_CHAR_TO_FACE_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_TURN_CHAR_TO_FACE_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_TURN_PLAYER_TO_FACE_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GOTO_COORD_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GOTO_COORD_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_PICKUP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_PICKUP_BEEN_COLLECTED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_PICKUP, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TAXI_LIGHTS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_BIG_Q, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_NUMBER_BIG_Q, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_GARAGE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_GARAGE_WITH_CAR_MODEL, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TARGET_CAR_FOR_MISSION_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_IN_MISSION_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_FREE_BOMBS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_POWERPOINT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_ALL_TAXI_LIGHTS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_ARMED_WITH_ANY_BOMB, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_APPLY_BRAKES_TO_PLAYERS_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_HEALTH, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_HEALTH, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_HEALTH, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_PLAYER_HEALTH, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CHAR_HEALTH, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CAR_HEALTH, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_ARMED_WITH_BOMB, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_CHANGE_CAR_COLOUR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_PED_ROADS_ON, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_PED_ROADS_OFF, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CHAR_LOOK_AT_CHAR_ALWAYS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CHAR_LOOK_AT_PLAYER_ALWAYS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PLAYER_LOOK_AT_CHAR_ALWAYS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_STOP_CHAR_LOOKING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_STOP_PLAYER_LOOKING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_HELICOPTER, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_GANG_ATTITUDE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_GANG_GANG_ATTITUDE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_GANG_PLAYER_ATTITUDE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_GANG_PED_MODELS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_GANG_CAR_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_GANG_WEAPONS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_RUN_TO_AREA, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_RUN_TO_COORD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_TOUCHING_OBJECT_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_TOUCHING_OBJECT_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOAD_SPECIAL_CHARACTER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_SPECIAL_CHARACTER_LOADED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_FLASH_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_FLASH_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_FLASH_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_REMOTE_MODE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_ARM_CAR_WITH_BOMB, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_PERSONALITY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CUTSCENE_OFFSET, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_ANIM_GROUP_FOR_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_ANIM_GROUP_FOR_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REQUEST_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_MODEL_LOADED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_MARK_MODEL_AS_NO_LONGER_NEEDED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GRAB_PHONE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_REPEATED_PHONE_MESSAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PHONE_MESSAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_PHONE_DISPLAYED_MESSAGE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_TURN_PHONE_OFF, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DRAW_CORONA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DRAW_LIGHT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_STORE_WEATHER, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_RESTORE_WEATHER, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_STORE_CLOCK, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_RESTORE_CLOCK, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_RESTART_CRITICAL_MISSION, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_PLAYING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_NO_OBJ, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_WAIT_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_FLEE_ON_FOOT_TILL_SAFE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GUARD_SPOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GUARD_AREA, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_WAIT_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_KILL_CHAR_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_KILL_PLAYER_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_KILL_CHAR_ANY_MEANS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_KILL_PLAYER_ANY_MEANS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_FLEE_CHAR_ON_FOOT_TILL_SAFE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_FLEE_PLAYER_ON_FOOT_TILL_SAFE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_FLEE_CHAR_ON_FOOT_ALWAYS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_FLEE_PLAYER_ON_FOOT_ALWAYS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GOTO_CHAR_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GOTO_PLAYER_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_LEAVE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_ENTER_CAR_AS_PASSENGER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_ENTER_CAR_AS_DRIVER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_FOLLOW_CAR_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_FIRE_AT_OBJECT_FROM_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_DESTROY_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_DESTROY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GOTO_AREA_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GOTO_AREA_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_FOLLOW_CAR_ON_FOOT_WITH_OFFSET, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GUARD_ATTACK, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_FOLLOW_ROUTE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GOTO_COORD_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GOTO_COORD_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_RUN_TO_AREA, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_RUN_TO_COORD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_PEDS_IN_AREA_TO_COLL, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_PEDS_IN_VEHICLE_TO_COLL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_COLL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_COLL_IN_CARS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_COLL_ANY_MEANS_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_COLL_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_COLL_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_COLL_ANY_MEANS_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_COLL_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_COLL_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_COLL_ANY_MEANS_CHAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_COLL_ON_FOOT_CHAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_COLL_IN_CAR_CHAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_COLL_ANY_MEANS_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_COLL_ON_FOOT_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_COLL_IN_CAR_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_COLL_ANY_MEANS_PLAYER_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_COLL_ON_FOOT_PLAYER_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_COLL_IN_CAR_PLAYER_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_COLL_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_COLL_IN_AREA_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_COLL_IN_AREA_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_COLL_STOPPED_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_COLL_STOPPED_IN_AREA_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_COLL_STOPPED_IN_AREA_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GET_NUMBER_OF_PEDS_IN_COLL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_HEED_THREATS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_HEED_THREATS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CONTROLLER_MODE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAN_RESPRAY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_TAXI, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_UNLOAD_SPECIAL_CHARACTER, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_RESET_NUM_OF_MODELS_KILLED_BY_PLAYER, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_NUM_OF_MODELS_KILLED_BY_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ACTIVATE_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_TAXI_TIMER, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_OBJECT_NO_OFFSET, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_BOAT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GOTO_AREA_ANY_MEANS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GOTO_AREA_ANY_MEANS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_STOPPED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_MESSAGE_WAIT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_PARTICLE_EFFECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_WIDESCREEN, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_SPRITE_BLIP_FOR_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_SPRITE_BLIP_FOR_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_SPRITE_BLIP_FOR_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_SPRITE_BLIP_FOR_CONTACT_POINT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_SPRITE_BLIP_FOR_COORD, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_ONLY_DAMAGED_BY_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_ONLY_DAMAGED_BY_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_PROOFS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_PROOFS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANGLED_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANGLED_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_DEACTIVATE_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_NUMBER_OF_CARS_COLLECTED_BY_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_CAR_BEEN_TAKEN_TO_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_SWAT_REQUIRED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_FBI_REQUIRED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_ARMY_REQUIRED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_IN_WATER, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CLOSEST_CHAR_NODE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CLOSEST_CAR_NODE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_CAR_GOTO_COORDINATES_ACCURATE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_START_PACMAN_RACE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_START_PACMAN_RECORD, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_NUMBER_OF_POWER_PILLS_EATEN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_PACMAN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_START_PACMAN_SCRAMBLE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_NUMBER_OF_POWER_PILLS_CARRIED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_NUMBER_OF_POWER_PILLS_CARRIED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_ON_SCREEN, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_ON_SCREEN, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_OBJECT_ON_SCREEN, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GOSUB_FILE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_GROUND_Z_FOR_3D_COORD, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_START_SCRIPT_FIRE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_SCRIPT_FIRE_EXTINGUISHED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_SCRIPT_FIRE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COMEDY_CONTROLS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_BOAT_GOTO_COORDS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_BOAT_STOP, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_SHOOTING_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_SHOOTING_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CURRENT_PLAYER_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CURRENT_CHAR_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_NUMBER_OF_POWER_PILLS_EATEN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_POWER_PILL, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_BOAT_CRUISE_SPEED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_RANDOM_CHAR_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_RANDOM_CHAR_IN_ZONE, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_TAXI, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_SHOOTING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_SHOOTING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_MONEY_PICKUP, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_ACCURACY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CAR_SPEED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_LOAD_CUTSCENE, INPUT_ARGUMENTS(ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_CUTSCENE_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CUTSCENE_ANIM, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_START_CUTSCENE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CUTSCENE_TIME, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_CUTSCENE_FINISHED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_CUTSCENE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_RESTORE_CAMERA_JUMPCUT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_COLLECTABLE1, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLLECTABLE1_TOTAL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PROJECTILE_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_DESTROY_PROJECTILES_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DROP_MINE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DROP_NAUTICAL_MINE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOAD_SPECIAL_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_CUTSCENE_HEAD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CUTSCENE_HEAD_ANIM, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SIN, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_COS, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CAR_FORWARD_X, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CAR_FORWARD_Y, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_CHANGE_GARAGE_TYPE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ACTIVATE_CRUSHER_CRANE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_2_NUMBERS, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_2_NUMBERS_NOW, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_2_NUMBERS_SOON, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_3_NUMBERS, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_3_NUMBERS_NOW, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_3_NUMBERS_SOON, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_4_NUMBERS, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_4_NUMBERS_NOW, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_4_NUMBERS_SOON, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_5_NUMBERS, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_5_NUMBERS_NOW, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_5_NUMBERS_SOON, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_6_NUMBERS, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_6_NUMBERS_NOW, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_6_NUMBERS_SOON, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FOLLOW_CHAR_IN_FORMATION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PLAYER_MADE_PROGRESS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PROGRESS_TOTAL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_JUMP_DISTANCE, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_JUMP_HEIGHT, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_JUMP_FLIPS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_JUMP_SPINS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_JUMP_STUNT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_UNIQUE_JUMP_FOUND, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_UNIQUE_JUMPS_TOTAL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_PASSENGER_DROPPED_OFF_TAXI, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_MONEY_MADE_TAXI, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_MISSION_GIVEN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_MISSION_PASSED, INPUT_ARGUMENTS(ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_RUNNING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_ALL_SCRIPT_FIRES, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_FIRST_CAR_COLOUR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_SECOND_CAR_COLOUR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_CAR_BEEN_DAMAGED_BY_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_CHARS_GROUP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_PLAYERS_GROUP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_EXPLODE_CHAR_HEAD, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_EXPLODE_PLAYER_HEAD, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ANCHOR_BOAT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_ZONE_GROUP, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_START_CAR_FIRE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_START_CHAR_FIRE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_RANDOM_CAR_OF_TYPE_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_RANDOM_CAR_OF_TYPE_IN_ZONE, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_RESPRAY_HAPPENED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAMERA_ZOOM, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_PICKUP_WITH_AMMO, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_RAM_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_BLOCK_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_CATCH_TRAIN, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_CATCH_TRAIN, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_NEVER_GETS_TIRED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_FAST_RELOAD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_BLEEDING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_FUNNY_SUSPENSION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_BIG_WHEELS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_FREE_RESPRAYS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_VISIBLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_VISIBLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_VISIBLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_AREA_OCCUPIED, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_START_DRUG_RUN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_DRUG_RUN_BEEN_COMPLETED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_DRUG_PLANE_BEEN_SHOT_DOWN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SAVE_PLAYER_FROM_FIRES, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DISPLAY_TEXT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TEXT_SCALE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TEXT_COLOUR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TEXT_JUSTIFY, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TEXT_CENTRE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TEXT_WRAPX, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TEXT_CENTRE_SIZE, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TEXT_BACKGROUND, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TEXT_BACKGROUND_COLOUR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TEXT_BACKGROUND_ONLY_TEXT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TEXT_PROPORTIONAL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TEXT_FONT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_INDUSTRIAL_PASSED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_COMMERCIAL_PASSED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SUBURBAN_PASSED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ROTATE_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SLIDE_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_CHAR_ELEGANTLY, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_STAY_IN_SAME_PLACE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_NASTY_GAME, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_UNDRESS_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DRESS_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_START_CHASE_SCENE, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_STOP_CHASE_SCENE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_EXPLOSION_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_EXPLOSION_IN_ZONE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_START_DRUG_DROP_OFF, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_DROP_OFF_PLANE_BEEN_SHOT_DOWN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_FIND_DROP_OFF_PLANE_COORDINATES, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_FLOATING_PACKAGE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_PLACE_OBJECT_RELATIVE_TO_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_MAKE_OBJECT_TARGETTABLE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_ARMOUR_TO_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_ARMOUR_TO_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_OPEN_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLOSE_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_WARP_CHAR_FROM_CAR_TO_COORD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_VISIBILITY_OF_CLOSEST_OBJECT_OF_TYPE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_CHAR_SPOTTED_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_HAIL_TAXI, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_OBJECT_BEEN_DAMAGED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_START_KILL_FRENZY_HEADSHOT, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ACTIVATE_MILITARY_CRANE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_WARP_PLAYER_INTO_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_WARP_CHAR_INTO_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_CAR_RADIO, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_AUDIO_STREAM, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_2_NUMBERS_BIG, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_3_NUMBERS_BIG, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_4_NUMBERS_BIG, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_5_NUMBERS_BIG, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_WITH_6_NUMBERS_BIG, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_WAIT_STATE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAMERA_BEHIND_PLAYER, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_MOTION_BLUR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_STRING_IN_STRING, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_RANDOM_CHAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_STEAL_ANY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_2_REPEATED_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_2_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_3_REPEATED_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_3_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_4_REPEATED_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_4_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_SNIPER_BULLET_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GIVE_PLAYER_DETONATOR, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_STEAL_ANY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_OBJECT_VELOCITY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_OBJECT_COLLISION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_ICECREAM_JINGLE_ON, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_STRING_IN_STRING_NOW, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_STRING_IN_STRING_SOON, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_5_REPEATED_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_5_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_6_REPEATED_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_6_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_POINT_OBSCURED_BY_A_MISSION_ENTITY, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOAD_ALL_MODELS_NOW, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_TO_OBJECT_VELOCITY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DRAW_SPRITE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DRAW_RECT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_LOAD_SPRITE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_LOAD_TEXTURE_DICTIONARY, INPUT_ARGUMENTS(ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_TEXTURE_DICTIONARY, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_OBJECT_DYNAMIC, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_ANIM_SPEED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PLAY_MISSION_PASSED_TUNE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_FREEZE_ONSCREEN_TIMER, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_CAR_SIREN, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_PED_ROADS_ON_ANGLED, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_PED_ROADS_OFF_ANGLED, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_ROADS_ON_ANGLED, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_ROADS_OFF_ANGLED, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_WATERTIGHT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_MOVING_PARTICLE_EFFECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_CANT_BE_DRAGGED_OUT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_TURN_CAR_TO_FACE_COORD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CRANE_LIFTING_CAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_DRAW_SPHERE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_STATUS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_MALE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SCRIPT_NAME, INPUT_ARGUMENTS(ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CHANGE_GARAGE_TYPE_WITH_CAR_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_FIND_DRUG_PLANE_COORDINATES, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SAVE_INT_TO_DEBUG_FILE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SAVE_FLOAT_TO_DEBUG_FILE, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SAVE_NEWLINE_TO_DEBUG_FILE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_POLICE_RADIO_MESSAGE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_STRONG, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_ROUTE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_RUBBISH, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_PARTICLE_EFFECTS_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_STREAMING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_GARAGE_OPEN, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_GARAGE_CLOSED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_START_CATALINA_HELI, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CATALINA_HELI_TAKE_OFF, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_CATALINA_HELI, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_CATALINA_HELI_BEEN_SHOT_DOWN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SWAP_NEAREST_BUILDING_MODEL, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_WORLD_PROCESSING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_ALL_PLAYER_WEAPONS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GRAB_CATALINA_HELI, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_AREA_OF_CARS, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_ROTATING_GARAGE_DOOR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_SPHERE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_SPHERE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CATALINA_HELI_FLY_AWAY, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_EVERYONE_IGNORE_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_STORE_CAR_CHAR_IS_IN_NO_SAVE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_STORE_CAR_PLAYER_IS_IN_NO_SAVE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PHONE_DISPLAYING_MESSAGE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_DISPLAY_ONSCREEN_TIMER_WITH_STRING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DISPLAY_ONSCREEN_COUNTER_WITH_STRING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_RANDOM_CAR_FOR_CAR_PARK, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_COLLISION_IN_MEMORY, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_WANTED_MULTIPLIER, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAMERA_IN_FRONT_OF_PLAYER, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_VISIBLY_DAMAGED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_DOES_OBJECT_EXIST, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOAD_SCENE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_STUCK_CAR_CHECK, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_STUCK_CAR_CHECK, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_STUCK, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOAD_MISSION_AUDIO, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_MISSION_AUDIO_LOADED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_PLAY_MISSION_AUDIO, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_MISSION_AUDIO_FINISHED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CLOSEST_CAR_NODE_WITH_HEADING, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_IMPORT_GARAGE_SLOT_BEEN_FILLED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_THIS_PRINT, INPUT_ARGUMENTS(ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_THIS_BIG_PRINT, INPUT_ARGUMENTS(ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_MISSION_AUDIO_POSITION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ACTIVATE_SAVE_MENU, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_SAVE_GAME_FINISHED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_NO_SPECIAL_CAMERA_FOR_THIS_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_PICKUP_OLD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_PICKUP, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_SPRITE_BLIP_FOR_PICKUP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PED_DENSITY_MULTIPLIER, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_FORCE_RANDOM_PED_TYPE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TEXT_DRAW_BEFORE_FADE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_COLLECTABLE1S_COLLECTED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_LEAVE_ANY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_SPRITES_DRAW_BEFORE_FADE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TEXT_RIGHT_JUSTIFY, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_HELP, INPUT_ARGUMENTS(ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_HELP, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_FLASH_HUD_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_FLASH_RADAR_BLIP, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_CONTROL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_GENERATE_CARS_AROUND_CAMERA, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_SMALL_PRINTS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_MILITARY_CRANE_COLLECTED_ALL_CARS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_UPSIDEDOWN_CAR_NOT_DAMAGED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CAN_PLAYER_START_MISSION, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_MAKE_PLAYER_SAFE_FOR_CUTSCENE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_USE_TEXT_COMMANDS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_THREAT_FOR_PED_TYPE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_THREAT_FOR_PED_TYPE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CAR_COLOURS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_ALL_CARS_CAN_BE_DAMAGED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_CAN_BE_DAMAGED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_MAKE_PLAYER_UNSAFE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_LOAD_COLLISION, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_BODY_CAST_HEALTH, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHARS_CHATTING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_MAKE_PLAYER_SAFE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_STAYS_IN_CURRENT_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_STAYS_IN_CURRENT_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_DRUNK_INPUT_DELAY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_MONEY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_INCREASE_CHAR_MONEY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_OFFSET_FROM_OBJECT_IN_WORLD_COORDS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_LIFE_SAVED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_CRIMINAL_CAUGHT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_AMBULANCE_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_FIRE_EXTINGUISHED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_TURN_PHONE_ON, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_LONGEST_DODO_FLIGHT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_OFFSET_FROM_CAR_IN_WORLD_COORDS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TOTAL_NUMBER_OF_KILL_FRENZIES, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_BLOW_UP_RC_BUGGY, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_CAR_FROM_CHASE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_FRENCH_GAME, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_GERMAN_GAME, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_MISSION_AUDIO, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_FADE_IN_AFTER_NEXT_ARREST, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_FADE_IN_AFTER_NEXT_DEATH, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_GANG_PED_MODEL_PREFERENCE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_USE_PEDNODE_SEEK, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_VEHICLE_WEAPONS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_GET_OUT_OF_JAIL_FREE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_FREE_HEALTH_CARE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_DOOR_CLOSED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOAD_AND_LAUNCH_MISSION, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_LOAD_AND_LAUNCH_MISSION_INTERNAL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_OBJECT_DRAW_LAST, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_AMMO_IN_PLAYER_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_AMMO_IN_CHAR_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_KILL_FRENZY_PASSED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_SAY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_NEAR_CLIP, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_RADIO_CHANNEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_OVERRIDE_HOSPITAL_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_OVERRIDE_POLICE_STATION_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_FORCE_RAIN, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DOES_GARAGE_CONTAIN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_TRACTION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ARE_MEASUREMENTS_IN_METRES, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_CONVERT_METRES_TO_FEET, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_MARK_ROADS_BETWEEN_LEVELS, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_MARK_PED_ROADS_BETWEEN_LEVELS, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_AVOID_LEVEL_TRANSITIONS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_AVOID_LEVEL_TRANSITIONS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_THREAT_FOR_PED_TYPE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_AREA_OF_CHARS, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TOTAL_NUMBER_OF_MISSIONS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CONVERT_METRES_TO_FEET_INT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_FASTEST_TIME, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_HIGHEST_SCORE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_WARP_CHAR_INTO_CAR_AS_PASSENGER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_PASSENGER_SEAT_FREE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CHAR_IN_CAR_PASSENGER_SEAT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_IS_CHRIS_CRIMINAL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_START_CREDITS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_STOP_CREDITS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ARE_CREDITS_FINISHED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_SINGLE_PARTICLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_IGNORE_LEVEL_TRANSITIONS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CHASE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_START_BOAT_FOAM_ANIMATION, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_UPDATE_BOAT_FOAM_ANIMATION, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_MUSIC_DOES_FADE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_INTRO_IS_PLAYING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_HOOKER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PLAY_END_OF_GAME_TUNE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_STOP_END_OF_GAME_TUNE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CAR_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_SITTING_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_SITTING_IN_ANY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_SCRIPT_FIRE_AUDIO, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ARE_ANY_CAR_CHEATS_ACTIVATED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_SUFFERS_CRITICAL_HITS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_LIFTING_A_PHONE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_SITTING_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_SITTING_IN_ANY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOAD_COLLISION_WITH_SCREEN, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_LOAD_SPLASH_SCREEN, INPUT_ARGUMENTS(ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_IGNORE_LEVEL_TRANSITIONS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_MAKE_CRAIGS_CAR_A_BIT_STRONGER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_JAMES_CAR_ON_PATH_TO_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_LOAD_END_OF_GAME_TUNE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ENABLE_PLAYER_CONTROL_CAMERA, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_OBJECT_ROTATION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_DEBUG_CAMERA_COORDINATES, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_DEBUG_CAMERA_FRONT_VECTOR, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_TARGETTING_ANY_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_TARGETTING_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_TARGETTING_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_TERMINATE_ALL_SCRIPTS_WITH_THIS_NAME, INPUT_ARGUMENTS(ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DISPLAY_TEXT_WITH_NUMBER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_STRING, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DISPLAY_TEXT_WITH_2_NUMBERS, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_FAIL_CURRENT_MISSION, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CLOSEST_OBJECT_OF_TYPE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_PLACE_OBJECT_RELATIVE_TO_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_ALL_OCCUPANTS_OF_CAR_LEAVE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_INTERPOLATION_PARAMETERS, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CLOSEST_CAR_NODE_WITH_HEADING_TOWARDS_POINT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CLOSEST_CAR_NODE_WITH_HEADING_AWAY_POINT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_DEBUG_CAMERA_POINT_AT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ATTACH_CHAR_TO_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DETACH_CHAR_FROM_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_CHANGE_LANE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_CHAR_LAST_WEAPON_DAMAGE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_CAR_LAST_WEAPON_DAMAGE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_RANDOM_COP_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_RANDOM_COP_IN_ZONE, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FLEE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_DRIVER_OF_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_NUMBER_OF_FOLLOWERS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GIVE_REMOTE_CONTROLLED_MODEL_TO_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CURRENT_PLAYER_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CURRENT_CHAR_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_IN_CAR_OBJECT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_CHAR_IN_CAR_OBJECT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_TEMP_ACTION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_HANDBRAKE_TURN_RIGHT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_HANDBRAKE_STOP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_ON_ANY_BIKE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_SNIPER_BULLET_2D, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_SNIPER_BULLET_3D, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GET_NUMBER_OF_SEATS_IN_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_ON_ANY_BIKE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_LYING_DOWN, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_CAN_CHAR_SEE_DEAD_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_ENTER_CAR_RANGE_MULTIPLIER, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_THREAT_REACTION_RANGE_MULTIPLIER, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_CEASE_ATTACK_TIMER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_REMOTE_CONTROLLED_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PC_VERSION, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_REPLAY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_REPLAY_PLAYING, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_MODEL_AVAILABLE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SHUT_CHAR_UP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_ENABLE_RC_DETONATE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_RANDOM_ROUTE_SEED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_ANY_PICKUP_AT_COORDS, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GET_FIRST_PICKUP_COORDS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_NEXT_PICKUP_COORDS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_ALL_CHAR_WEAPONS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_PLAYER_GOT_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_CHAR_GOT_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_FACING_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TANK_DETONATE_CARS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_POSITION_OF_ANALOGUE_STICKS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_ON_FIRE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_TYRE_BURST, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_DRIVE_STRAIGHT_AHEAD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_WAIT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_STANDING_ON_A_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_FOOT_DOWN, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_FOOT_DOWN, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_INITIALISE_OBJECT_PATH, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_START_OBJECT_ON_PATH, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_OBJECT_PATH_SPEED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_OBJECT_PATH_POSITION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_OBJECT_DISTANCE_ALONG_PATH, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_OBJECT_PATH, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HELI_GOTO_COORDS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_INT_VAR_EQUAL_TO_CONSTANT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_INT_LVAR_EQUAL_TO_CONSTANT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GET_DEAD_CHAR_PICKUP_COORDS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_PROTECTION_PICKUP, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_ANY_BOAT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANY_BOAT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_ANY_HELI, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANY_HELI, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_ANY_PLANE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANY_PLANE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_WATER, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_VAR_INT_TO_CONSTANT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " ="), + REGISTER_COMMAND(COMMAND_SET_LVAR_INT_TO_CONSTANT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " ="), + REGISTER_COMMAND(COMMAND_IS_INT_VAR_GREATER_THAN_CONSTANT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_INT_LVAR_GREATER_THAN_CONSTANT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_CONSTANT_GREATER_THAN_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_CONSTANT_GREATER_THAN_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), + REGISTER_COMMAND(COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_CONSTANT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_CONSTANT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_IS_CONSTANT_GREATER_OR_EQUAL_TO_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_IS_CONSTANT_GREATER_OR_EQUAL_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), + REGISTER_COMMAND(COMMAND_GET_CHAR_WEAPON_IN_SLOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CLOSEST_STRAIGHT_ROAD, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_FORWARD_SPEED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_AREA_VISIBLE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CUTSCENE_ANIM_TO_LOOP, INPUT_ARGUMENTS(ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_MARK_CAR_AS_CONVOY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_RESET_HAVOC_CAUSED_BY_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_HAVOC_CAUSED_BY_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_SCRIPT_ROADBLOCK, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_ALL_SCRIPT_ROADBLOCKS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_WALK_TO_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PICKUP_IN_ZONE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GET_OFFSET_FROM_CHAR_IN_WORLD_COORDS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_CHAR_BEEN_PHOTOGRAPHED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_AIM_GUN_AT_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_SECURITY_CAMERA, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_FLYING_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_FLYING_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_SONY_CD_BEEN_READ, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GET_NUMBER_OF_SONY_CDS_READ, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_SHORT_RANGE_BLIP_FOR_COORD_OLD, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_SHORT_RANGE_BLIP_FOR_COORD, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_SHORT_RANGE_SPRITE_BLIP_FOR_COORD, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_MONEY_SPENT_ON_CLOTHES, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_HELI_ORIENTATION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_HELI_ORIENTATION, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PLANE_GOTO_COORDS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_NTH_CLOSEST_CAR_NODE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_NTH_CLOSEST_CHAR_NODE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_DRAW_WEAPONSHOP_CORONA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_ENABLE_RC_DETONATE_ON_CONTACT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_FREEZE_CHAR_POSITION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_DROWNS_IN_WATER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_OBJECT_RECORDS_COLLISIONS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_OBJECT_COLLIDED_WITH_ANYTHING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_RC_BUGGY, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_PHOTOGRAPH_BEEN_TAKEN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CHAR_ARMOUR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_ARMOUR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_HELI_STABILISER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_STRAIGHT_LINE_DISTANCE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_POP_CAR_BOOT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SHUT_PLAYER_UP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_MOOD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REQUEST_COLLISION, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_OBJECT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_LOCATE_OBJECT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_OBJECT_IN_WATER, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_STEAL_ANY_CAR_EVEN_MISSION_CAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_OBJECT_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_OBJECT_IN_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_CROUCH, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_ZONE_CIVILIAN_CAR_INFO, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REQUEST_ANIMATION, INPUT_ARGUMENTS(ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_ANIMATION_LOADED, INPUT_ARGUMENTS(ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_ANIMATION, INPUT_ARGUMENTS(ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_WAITING_FOR_WORLD_COLLISION, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_WAITING_FOR_WORLD_COLLISION, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_OBJECT_WAITING_FOR_WORLD_COLLISION, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_SHUFFLE_INTO_DRIVERS_SEAT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ATTACH_CHAR_TO_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_AS_PLAYER_FRIEND, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DISPLAY_NTH_ONSCREEN_COUNTER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DISPLAY_NTH_ONSCREEN_COUNTER_WITH_STRING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_SET_PIECE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_EXTRA_COLOURS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_EXTRA_COLOURS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLOSE_CAR_BOOT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_WHEELIE_STATS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_DISARM_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_BURST_CAR_TYRE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_OBJ_NO_OBJ, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_WEARING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_CAN_DO_DRIVE_BY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_SPRINT_TO_COORD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_SWAT_ROPE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_FIRST_PERSON_CONTROL_CAMERA, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_NEAREST_TYRE_TO_POINT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAR_MODEL_COMPONENTS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SWITCH_LIFT_CAMERA, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLOSE_ALL_CAR_DOORS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_DISTANCE_BETWEEN_COORDS_2D, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_DISTANCE_BETWEEN_COORDS_3D, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_POP_CAR_BOOT_USING_PHYSICS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_FIRST_PERSON_WEAPON_CAMERA, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_LEAVING_VEHICLE_TO_DIE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SORT_OUT_OBJECT_COLLISION_WITH_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_MAX_WANTED_LEVEL, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_WANDER_PATH_CLEAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_HELP_WITH_NUMBER, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_HELP_FOREVER, INPUT_ARGUMENTS(ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PRINT_HELP_FOREVER_WITH_NUMBER, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_CAN_BE_DAMAGED_BY_MEMBERS_OF_GANG, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_LOAD_AND_LAUNCH_MISSION_EXCLUSIVE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_MISSION_AUDIO_PLAYING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_LOCKED_PROPERTY_PICKUP, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_FORSALE_PROPERTY_PICKUP, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_FREEZE_CAR_POSITION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_CAR_BEEN_DAMAGED_BY_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_CAR_BEEN_DAMAGED_BY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GET_RADIO_CHANNEL, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_DISPLAY_TEXT_WITH_3_NUMBERS, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_DROWNING_IN_WATER, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_DROWNING_IN_WATER, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_DISABLE_CUTSCENE_SHADOWS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_GLASS_BEEN_SHATTERED_NEARBY, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_ATTACH_CUTSCENE_OBJECT_TO_BONE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ATTACH_CUTSCENE_OBJECT_TO_COMPONENT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_STAY_IN_CAR_WHEN_JACKED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_MISSION_AUDIO_LOADING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_MONEY_SPENT_ON_WEAPONS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_MONEY_SPENT_ON_PROPERTY, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_MONEY_SPENT_ON_AUTO_PAINTING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_ANSWERING_MOBILE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_DRUNKENNESS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_PLAYER_DRUNKENNESS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_DRUG_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_PLAYER_DRUG_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_LOAN_SHARK_VISITS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_STORES_KNOCKED_OFF, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_MOVIE_STUNTS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_NUMBER_OF_ASSASSINATIONS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_PIZZAS_DELIVERED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_GARBAGE_PICKUPS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_ICE_CREAMS_SOLD, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TOP_SHOOTING_RANGE_SCORE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_SHOOTING_RANGE_RANK, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_MONEY_SPENT_ON_GAMBLING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_MONEY_WON_ON_GAMBLING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_LARGEST_GAMBLING_WIN, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_IN_PLAYERS_GROUP_CAN_FIGHT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_CHAR_WAIT_STATE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_RANDOM_CAR_OF_TYPE_IN_AREA_NO_SAVE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CAN_BURST_CAR_TYRES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_AUTO_AIM, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_FIRE_HUNTER_GUN, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PROPERTY_AS_OWNED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_BLOOD_RING_KILLS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_LONGEST_TIME_IN_BLOOD_RING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_EVERYTHING_FOR_HUGE_CUTSCENE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_TOUCHING_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_TOUCHING_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_CHECK_FOR_PED_MODEL_AROUND_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_CHAR_FOLLOW_PATH, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_CAN_BE_SHOT_IN_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ATTACH_CUTSCENE_OBJECT_TO_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_LOAD_MISSION_TEXT, INPUT_ARGUMENTS(ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_TONIGHTS_EVENT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_CHAR_LAST_DAMAGE_ENTITY, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_CAR_LAST_DAMAGE_ENTITY, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_FREEZE_OBJECT_POSITION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_HAS_MET_DEBBIE_HARRY, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_RIOT_INTENSITY, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_IN_ANGLED_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CAR_IN_ANGLED_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_REMOVE_WEAPON_FROM_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_UP_TAXI_SHORTCUT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_TAXI_SHORTCUT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GOTO_CAR_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_CLOSEST_WATER_NODE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_PORN_LEAFLET_TO_RUBBISH, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_CLOTHES_PICKUP, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_CHANGE_BLIP_THRESHOLD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_MAKE_PLAYER_FIRE_PROOF, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_INCREASE_PLAYER_MAX_HEALTH, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_INCREASE_PLAYER_MAX_ARMOUR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_RANDOM_CHAR_AS_DRIVER, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_RANDOM_CHAR_AS_PASSENGER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_IGNORE_THREATS_BEHIND_OBJECTS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ENSURE_PLAYER_HAS_DRIVE_BY_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_MAKE_HELI_COME_CRASHING_DOWN, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_EXPLOSION_NO_SOUND, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_OBJECT_AREA_VISIBLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_WAS_VEHICLE_EVER_POLICE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_NEVER_TARGETTED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_LOAD_UNCOMPRESSED_ANIM, INPUT_ARGUMENTS(ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_WAS_CUTSCENE_SKIPPED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_CROUCH_WHEN_THREATENED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_IN_ANY_POLICE_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_DOES_CHAR_EXIST, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_DOES_VEHICLE_EXIST, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_SHORT_RANGE_BLIP_FOR_CONTACT_POINT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_SHORT_RANGE_SPRITE_BLIP_FOR_CONTACT_POINT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_STUCK, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_ALL_TAXIS_HAVE_NITRO, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_STOP_SHOOT_DONT_SEEK_ENTITY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_FREEZE_CAR_POSITION_AND_DONT_LOAD_COLLISION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_FREEZE_CHAR_POSITION_AND_DONT_LOAD_COLLISION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_FREEZE_OBJECT_POSITION_AND_DONT_LOAD_COLLISION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_FADE_AND_JUMPCUT_AFTER_RC_EXPLOSION, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_VIGILANTE_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_ALL_CHAR_ANIMS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_MAXIMUM_NUMBER_OF_CARS_IN_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_WANTED_STARS_ARE_FLASHING, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_ALLOW_HURRICANES, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_PLAY_ANNOUNCEMENT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_PLAYER_IS_IN_STADIUM, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_BUS_FARES_COLLECTED_BY_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_BUY_ICE_CREAM, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_DISPLAY_RADAR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_BEST_POSITION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_INFO_ZONE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_CLEAR_CHAR_ICE_CREAM_PURCHASE, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_IN_CAR_FIRE_BUTTON_PRESSED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_CHAR_ATTEMPTED_ATTRACTOR, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_SET_LOAD_COLLISION_FOR_CAR_FLAG, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_LOAD_COLLISION_FOR_CHAR_FLAG, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_LOAD_COLLISION_FOR_OBJECT_FLAG, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_BIG_GUN_FLASH, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_HAS_CHAR_BOUGHT_ICE_CREAM, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GET_PROGRESS_PERCENTAGE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_SHORTCUT_PICKUP_POINT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_SHORTCUT_DROPOFF_POINT_FOR_MISSION, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_RANDOM_ICE_CREAM_CUSTOMER_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_RANDOM_ICE_CREAM_CUSTOMER_IN_ZONE, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_UNLOCK_ALL_CAR_DOORS_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_GANG_ATTACK_PLAYER_WITH_COPS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_CHAR_FRIGHTENED_IN_JACKED_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_VEHICLE_TO_FADE_IN, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_ODDJOB_MISSION_PASSED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_SHORTCUT_TAXI, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_IS_CHAR_DUCKING, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_CREATE_DUST_EFFECT_FOR_CUTSCENE_HELI, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_REGISTER_FIRE_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_AUSTRALIAN_GAME, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_DISARM_CAR_BOMB, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), +#if (defined GTAVC_JP_PATCH || defined SUPPORT_JAPANESE_SCRIPT) + REGISTER_COMMAND(COMMAND_IS_JAPANESE_GAME, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), +#elif (!defined GTA_PS2) + REGISTER_COMMAND(COMMAND_SET_ONSCREEN_COUNTER_FLASH_WHEN_FIRST_DISPLAYED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), +#endif +#if (defined GTA_PC && !defined GTAVC_JP_PATCH || defined GTA_XBOX || defined SUPPORT_XBOX_SCRIPT || defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) + REGISTER_COMMAND(COMMAND_SHUFFLE_CARD_DECKS, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_FETCH_NEXT_CARD, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_OBJECT_VELOCITY, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_DEBUG_CAMERA_ON, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_TO_OBJECT_ROTATION_VELOCITY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_OBJECT_ROTATION_VELOCITY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_OBJECT_STATIC, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GET_ANGLE_BETWEEN_2D_VECTORS, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_DO_2D_RECTANGLES_COLLIDE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, -1, ""), + REGISTER_COMMAND(COMMAND_GET_OBJECT_ROTATION_VELOCITY, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_ADD_VELOCITY_RELATIVE_TO_OBJECT_VELOCITY, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_GET_OBJECT_SPEED, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), +#endif +#if (defined GTA_XBOX || defined SUPPORT_XBOX_SCRIPT) + REGISTER_COMMAND(COMMAND_MARK_CUTSCENE_START, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_MARK_CUTSCENE_END, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CUTSCENE_SCROLL, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), +#elif (defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) + REGISTER_COMMAND(COMMAND_IS_MISSION_SKIP, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_IN_AMMUNATION, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_DO_SAVE_GAME, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_IS_RETRY, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), + REGISTER_COMMAND(COMMAND_DUMMY, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_MARK_CUTSCENE_START, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_MARK_CUTSCENE_END, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_CUTSCENE_SCROLL, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), +#endif +}; +#undef REGISTER_COMMAND +#undef INPUT_ARGUMENTS +#undef OUTPUT_ARGUMENTS + +static_assert(ARRAY_SIZE(commands) == LAST_SCRIPT_COMMAND, "commands array not filled"); + +#if SCRIPT_LOG_FILE_LEVEL == 1 || SCRIPT_LOG_FILE_LEVEL == 2 +static FILE* dbg_log; +#endif + +static void PrintToLog(const char* format, ...) +{ + va_list va; + va_start(va, format); + char tmp[1024]; +#ifdef _WIN32 + vsprintf_s(tmp, 1024, format, va); +#else + vsprintf(tmp, format, va); +#endif + va_end(va); + +#if SCRIPT_LOG_FILE_LEVEL == 1 || SCRIPT_LOG_FILE_LEVEL == 2 + if (dbg_log) + fwrite(tmp, 1, strlen(tmp), dbg_log); +#endif +} + +#endif + +void FlushLog() +{ +#ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT +#if SCRIPT_LOG_FILE_LEVEL == 1 || SCRIPT_LOG_FILE_LEVEL == 2 + if (dbg_log) + fflush(dbg_log); +#endif +#endif +} + +#ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT +int CRunningScript::CollectParameterForDebug(char* buf, bool& var) +{ + uint16 varIndex; + char tmpstr[24]; + var = false; + switch (CTheScripts::Read1ByteFromScript(&m_nIp)) + { + case ARGUMENT_INT32: + case ARGUMENT_FLOAT: + return CTheScripts::Read4BytesFromScript(&m_nIp); + case ARGUMENT_GLOBALVAR: + varIndex = CTheScripts::Read2BytesFromScript(&m_nIp); + script_assert(varIndex >= 8 && varIndex < CTheScripts::GetSizeOfVariableSpace()); + var = true; + sprintf(tmpstr, " $%d", varIndex / 4); + strcat(buf, tmpstr); + return *((int32*)&CTheScripts::ScriptSpace[varIndex]); + case ARGUMENT_LOCALVAR: + varIndex = CTheScripts::Read2BytesFromScript(&m_nIp); + script_assert(varIndex >= 0 && varIndex < ARRAY_SIZE(m_anLocalVariables)); + var = true; + sprintf(tmpstr, " %d@", varIndex); + strcat(buf, tmpstr); + return m_anLocalVariables[varIndex]; + case ARGUMENT_INT8: + return CTheScripts::Read1ByteFromScript(&m_nIp); + case ARGUMENT_INT16: + return CTheScripts::Read2BytesFromScript(&m_nIp); + default: + PrintToLog("%s - script assertion failed in CollectParameterForDebug", buf); + script_assert(0); + break; + } + return 0; +} + +void CRunningScript::GetStoredParameterForDebug(char* buf) +{ + uint16 varIndex; + char tmpstr[24]; + switch (CTheScripts::Read1ByteFromScript(&m_nIp)) { + case ARGUMENT_GLOBALVAR: + varIndex = CTheScripts::Read2BytesFromScript(&m_nIp); + sprintf(tmpstr, " $%d", varIndex / 4); + strcat(buf, tmpstr); + break; + case ARGUMENT_LOCALVAR: + varIndex = CTheScripts::Read2BytesFromScript(&m_nIp); + sprintf(tmpstr, " %d@", varIndex); + strcat(buf, tmpstr); + break; + default: + PrintToLog("%s - script_assertion failed in GetStoredParameterForDebug", buf); + script_assert(0); + } +} + +void CTheScripts::LogAfterScriptInitializing() +{ +#if SCRIPT_LOG_FILE_LEVEL == 2 + CFileMgr::SetDirMyDocuments(); + if (dbg_log) + fclose(dbg_log); + dbg_log = fopen("SCRDBG.LOG", "w"); + static const char* init_msg = "Starting debug script log\n\n"; + PrintToLog(init_msg); + CFileMgr::SetDir(""); +#endif +} + +void CTheScripts::LogBeforeScriptProcessing() +{ + +#if SCRIPT_LOG_FILE_LEVEL == 1 + CFileMgr::SetDirMyDocuments(); + dbg_log = fopen("SCRDBG.LOG", "w"); + static const char* init_msg = "Starting debug script log\n\n"; + PrintToLog(init_msg); + CFileMgr::SetDir(""); +#endif + PrintToLog("------------------------\n"); + PrintToLog("CTheScripts::Process started, CTimer::GetTimeInMilliseconds == %u\n", CTimer::GetTimeInMilliseconds()); +} + +void CTheScripts::LogAfterScriptProcessing() +{ + PrintToLog("Script processing done, ScriptsUpdated: %d, CommandsExecuted: %d\n", ScriptsUpdated, CommandsExecuted); +#if SCRIPT_LOG_FILE_LEVEL == 1 + fclose(dbg_log); + dbg_log = nil; +#endif +} + +void CRunningScript::LogOnStartProcessing() +{ + PrintToLog("\n\nProcessing script %s (id %d)\n\n", m_abScriptName, this - CTheScripts::ScriptsArray); +} + +void CRunningScript::LogBeforeProcessingCommand(int32 command) +{ + storedIp = m_nIp; + if (command < ARRAY_SIZE(commands)) { + script_assert(commands[command].id == command); + m_nIp -= 2; + sprintf(commandInfo, m_nIp >= SIZE_MAIN_SCRIPT ? "M<%5d> " : "<%6d> ", m_nIp >= SIZE_MAIN_SCRIPT ? m_nIp - SIZE_MAIN_SCRIPT : m_nIp); + m_nIp += 2; + if (m_bNotFlag) + strcat(commandInfo, "NOT "); + if (commands[command].position == -1) + strcat(commandInfo, commands[command].name + sizeof("COMMAND_") - 1); + for (int i = 0; commands[command].input[i] != ARGTYPE_NONE; i++) { + char tmp[16]; + bool var = false; + int value; + switch (commands[command].input[i]) { + case ARGTYPE_INT: + case ARGTYPE_PED_HANDLE: + case ARGTYPE_VEHICLE_HANDLE: + case ARGTYPE_OBJECT_HANDLE: value = CollectParameterForDebug(commandInfo, var); sprintf(tmp, var ? " (%d)" : " %d", value); break; + case ARGTYPE_FLOAT: value = CollectParameterForDebug(commandInfo, var); sprintf(tmp, var ? " (%.3f)" : " %.3f", *(float*)&value); break; + case ARGTYPE_STRING: sprintf(tmp, " '%s'", (const char*)&CTheScripts::ScriptSpace[m_nIp]); m_nIp += KEY_LENGTH_IN_SCRIPT; break; + case ARGTYPE_LABEL: value = CollectParameterForDebug(commandInfo, var); sprintf(tmp, var ? " (%s(%d))" : " %s(%d)", value >= 0 ? "G" : "L", abs(value)); break; + case ARGTYPE_BOOL: value = CollectParameterForDebug(commandInfo, var); sprintf(tmp, var ? " (%s)" : " %s", value ? "TRUE" : "FALSE"); break; + case ARGTYPE_ANDOR: value = CollectParameterForDebug(commandInfo, var); sprintf(tmp, " %d %ss", (value + 1) % 10, value / 10 == 0 ? "AND" : "OR"); break; + default: script_assert(0); + } + strcat(commandInfo, tmp); + if (commands[command].position == i) + strcat(commandInfo, commands[command].name_override); + } + uint32 t = m_nIp; + m_nIp = storedIp; + storedIp = t; + } +} + +void CRunningScript::LogAfterProcessingCommand(int32 command) +{ + if (command < ARRAY_SIZE(commands)) { + if (commands[command].cond || commands[command].output[0] != ARGTYPE_NONE) { + strcat(commandInfo, " ->"); + if (commands[command].cond) + strcat(commandInfo, m_bCondResult ? " TRUE" : " FALSE"); + uint32 t = m_nIp; + m_nIp = storedIp; + storedIp = t; + for (int i = 0; commands[command].output[i] != ARGTYPE_NONE; i++) { + char tmp[16]; + switch (commands[command].output[i]) { + case ARGTYPE_INT: + case ARGTYPE_PED_HANDLE: + case ARGTYPE_VEHICLE_HANDLE: + case ARGTYPE_OBJECT_HANDLE: GetStoredParameterForDebug(commandInfo); sprintf(tmp, " (%d)", ScriptParams[i]); strcat(commandInfo, tmp); break; + case ARGTYPE_FLOAT: GetStoredParameterForDebug(commandInfo); sprintf(tmp, " (%8.3f)", *(float*)&ScriptParams[i]); strcat(commandInfo, tmp); break; + default: script_assert(0 && "Script only returns INTs and FLOATs"); + } + } + m_nIp = storedIp; + } + PrintToLog("%s\n", commandInfo); + if (m_bMissionFlag) { + for (int i = 0; commandInfo[i]; i++) { + if (commandInfo[i] == '_') + commandInfo[i] = ' '; + } + CDebug::DebugAddText(commandInfo); + } + } +} + +#endif + +#ifdef MISSION_SWITCHER +void +CTheScripts::SwitchToMission(int32 mission) +{ + for (CRunningScript* pScript = CTheScripts::pActiveScripts; pScript != nil; pScript = pScript->GetNext()) { + if (!pScript->m_bIsMissionScript || !pScript->m_bDeatharrestEnabled) { + continue; + } + while (pScript->m_nStackPointer > 0) + --pScript->m_nStackPointer; + + pScript->m_nIp = pScript->m_anStack[pScript->m_nStackPointer]; + *(int32*)&CTheScripts::ScriptSpace[CTheScripts::OnAMissionFlag] = 0; + pScript->m_nWakeTime = 0; + pScript->m_bDeatharrestExecuted = true; + + while (!pScript->ProcessOneCommand()); + + CMessages::ClearMessages(); + } + + if (CTheScripts::NumberOfExclusiveMissionScripts > 0 && mission <= UINT16_MAX - 2) + return; + +#ifdef MISSION_REPLAY + missionRetryScriptIndex = mission; +#ifdef USE_MISSION_REPLAY_OVERRIDE_FOR_NON_MOBILE_SCRIPT + if (CTheScripts::MissionSupportsMissionReplay(missionRetryScriptIndex)) { + SaveGameForPause(SAVE_TYPE_QUICKSAVE_FOR_SCRIPT); + } +#endif +#endif + CTimer::Suspend(); + int offset = CTheScripts::MultiScriptArray[mission]; +#ifdef USE_DEBUG_SCRIPT_LOADER + int handle = OpenScript(); +#else + CFileMgr::ChangeDir("\\"); + int handle = CFileMgr::OpenFile("data\\main.scm", "rb"); +#endif + CFileMgr::Seek(handle, offset, 0); + CFileMgr::Read(handle, (const char*)&CTheScripts::ScriptSpace[SIZE_MAIN_SCRIPT], SIZE_MISSION_SCRIPT); + CFileMgr::CloseFile(handle); + CRunningScript* pMissionScript = CTheScripts::StartNewScript(SIZE_MAIN_SCRIPT); + CTimer::Resume(); + pMissionScript->m_bIsMissionScript = true; + pMissionScript->m_bMissionFlag = true; + CTheScripts::bAlreadyRunningAMissionScript = true; + CGameLogic::ClearShortCut(); +} +#endif diff --git a/src/miami/control/SetPieces.cpp b/src/miami/control/SetPieces.cpp new file mode 100644 index 00000000..5edcd335 --- /dev/null +++ b/src/miami/control/SetPieces.cpp @@ -0,0 +1,325 @@ +#include "common.h" + +#include "SetPieces.h" +#include "Automobile.h" +#include "CarAI.h" +#include "CopPed.h" +#include "GenericGameStorage.h" +#include "PlayerPed.h" +#include "Timer.h" +#include "Vehicle.h" +#include "Wanted.h" +#include "World.h" +#include "VarConsole.h" +#include "SaveBuf.h" + +#define TIME_BETWEEN_SETPIECE_SPAWNS 20000 + +bool CSetPieces::bDebug; +uint32 CSetPieces::NumSetPieces; +CSetPiece CSetPieces::aSetPieces[NUM_SETPIECES]; + +void CSetPieces::Init(void) +{ + bDebug = false; + NumSetPieces = 0; +#ifndef MASTER + VarConsole.Add("Show set pieces", &bDebug, true); +#endif +} + +void CSetPieces::AddOne(uint8 type, CVector2D vTriggerInf, CVector2D vTriggerSup, CVector2D vSpawn1, CVector2D vTarget1, CVector2D vSpawn2, CVector2D vTarget2) +{ + if (NumSetPieces >= NUM_SETPIECES) + return; + aSetPieces[NumSetPieces].m_nType = type; + aSetPieces[NumSetPieces].m_vTriggerInf.x = Min(vTriggerInf.x, vTriggerSup.x); + aSetPieces[NumSetPieces].m_vTriggerInf.y = Min(vTriggerInf.y, vTriggerSup.y); + aSetPieces[NumSetPieces].m_vTriggerSup.x = Max(vTriggerInf.x, vTriggerSup.x); + aSetPieces[NumSetPieces].m_vTriggerSup.y = Max(vTriggerInf.y, vTriggerSup.y); + aSetPieces[NumSetPieces].m_vSpawn1 = vSpawn1; + aSetPieces[NumSetPieces].m_vSpawn2 = vSpawn2; + aSetPieces[NumSetPieces].m_vTarget1 = vTarget1; + aSetPieces[NumSetPieces].m_vTarget2 = vTarget2; + ++NumSetPieces; +} + +void CSetPieces::Update(void) +{ + int nFirst = NumSetPieces * (CTimer::GetFrameCounter() % 8) / 8; + int nLast = NumSetPieces * (CTimer::GetFrameCounter() % 8 + 1) / 8; + for (int i = nFirst; i < nLast; i++) + aSetPieces[i].Update(); +#ifndef MASTER + // TODO: debug code from mobile +#endif // !MASTER +} + +void CSetPieces::Save(uint8* buf, uint32* size) +{ +INITSAVEBUF + WriteSaveBuf(buf, NumSetPieces); + for (int i = 0; i < NUM_SETPIECES; i++) + WriteSaveBuf(buf, aSetPieces[i]); + *size = sizeof(NumSetPieces) + NUM_SETPIECES * sizeof(CSetPiece); +VALIDATESAVEBUF(*size) +} + +void CSetPieces::Load(uint8* buf, uint32 size) +{ +INITSAVEBUF + ReadSaveBuf(&NumSetPieces, buf); + for (int i = 0; i < NUM_SETPIECES; i++) + ReadSaveBuf(&aSetPieces[i], buf); +VALIDATESAVEBUF(size) +} + +void CSetPiece::Update(void) +{ + if (m_nLastTimeCreated != 0 && CTimer::GetTimeInMilliseconds() <= m_nLastTimeCreated + TIME_BETWEEN_SETPIECE_SPAWNS) + return; + CVector pos = FindPlayerCoors(); + if (pos.x < m_vTriggerInf.x || pos.x > m_vTriggerSup.x || + pos.y < m_vTriggerInf.y || pos.y > m_vTriggerSup.y) + return; + switch (m_nType) { + case SETPIECE_TWOCOPCARSINALLEY: + { + if (FindPlayerPed()->m_pWanted->GetWantedLevel() < 1 || FindPlayerVehicle()) + return; + CVehicle* pVehicle1 = TryToGenerateCopCar(m_vSpawn1, m_vTarget1); + if (!pVehicle1) + return; + CVehicle* pVehicle2 = TryToGenerateCopCar(m_vSpawn2, m_vTarget2); + if (!pVehicle2) { + CWorld::Remove(pVehicle1); + delete pVehicle1; + return; + } + pVehicle1->SetStatus(STATUS_PHYSICS); + pVehicle1->AutoPilot.m_fMaxTrafficSpeed = pVehicle1->AutoPilot.m_nCruiseSpeed = 4; + pVehicle1->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_SLOW_DOWN_FOR_CARS; + pVehicle1->AutoPilot.m_nCarMission = MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_1; + pVehicle1->AutoPilot.m_vecDestinationCoors.x = m_vTarget1.x; + pVehicle1->AutoPilot.m_vecDestinationCoors.y = m_vTarget1.y; + pVehicle1->AutoPilot.m_vecDestinationCoors.z = 0.0f; + pVehicle1->m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 25000; + CCarAI::AddPoliceCarOccupants(pVehicle1); + pVehicle2->SetStatus(STATUS_PHYSICS); + pVehicle2->AutoPilot.m_fMaxTrafficSpeed = pVehicle2->AutoPilot.m_nCruiseSpeed = 4; + pVehicle2->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_SLOW_DOWN_FOR_CARS; + pVehicle2->AutoPilot.m_nCarMission = MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_1; + pVehicle2->AutoPilot.m_vecDestinationCoors.x = m_vTarget2.x; + pVehicle2->AutoPilot.m_vecDestinationCoors.y = m_vTarget2.y; + pVehicle2->AutoPilot.m_vecDestinationCoors.z = 0.0f; + pVehicle2->m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 25000; + CCarAI::AddPoliceCarOccupants(pVehicle2); + m_nLastTimeCreated = CTimer::GetTimeInMilliseconds(); + break; + } + case SETPIECE_CARBLOCKINGPLAYERFROMSIDE: + { + if (FindPlayerPed()->m_pWanted->GetWantedLevel() < 2) + return; + if (!FindPlayerVehicle()) + return; + if (DotProduct2D(FindPlayerSpeed(), (CVector2D)FindPlayerCoors() - m_vSpawn1) >= 0.0f) + return; + CVehicle* pVehicle1 = TryToGenerateCopCar(m_vSpawn1, m_vTarget1); + if (!pVehicle1) + return; + pVehicle1->SetStatus(STATUS_PHYSICS); + pVehicle1->AutoPilot.m_fMaxTrafficSpeed = pVehicle1->AutoPilot.m_nCruiseSpeed = 16; + pVehicle1->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_PLOUGH_THROUGH; + pVehicle1->AutoPilot.m_nCarMission = MISSION_BLOCKPLAYER_FORWARDANDBACK; + pVehicle1->AutoPilot.m_nTempAction = TEMPACT_GOFORWARD; + pVehicle1->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 100; + pVehicle1->m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 10000; + pVehicle1->SetMoveSpeed(2.0f * pVehicle1->GetForward() / 3.0f); + CCarAI::AddPoliceCarOccupants(pVehicle1); + m_nLastTimeCreated = CTimer::GetTimeInMilliseconds(); + return; + } + case SETPIECE_CARRAMMINGPLAYERFROMSIDE: + { + if (FindPlayerPed()->m_pWanted->GetWantedLevel() < 2) + return; + if (!FindPlayerVehicle()) + return; + if (DotProduct2D(FindPlayerSpeed(), (CVector2D)FindPlayerCoors() - m_vSpawn1) >= 0.0f) + return; + CVehicle* pVehicle1 = TryToGenerateCopCar(m_vSpawn1, m_vTarget1); + if (!pVehicle1) + return; + pVehicle1->SetStatus(STATUS_PHYSICS); + pVehicle1->AutoPilot.m_fMaxTrafficSpeed = pVehicle1->AutoPilot.m_nCruiseSpeed = 16; + pVehicle1->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + pVehicle1->AutoPilot.m_nCarMission = MISSION_RAMCAR_CLOSE; + pVehicle1->AutoPilot.m_nTempAction = TEMPACT_GOFORWARD; + pVehicle1->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 100; + pVehicle1->m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 10000; + pVehicle1->SetMoveSpeed(2.0f * pVehicle1->GetForward() / 3.0f); + CCarAI::AddPoliceCarOccupants(pVehicle1); + m_nLastTimeCreated = CTimer::GetTimeInMilliseconds(); + return; + } + case SETPIECE_CREATECOPPERONFOOT: + { + if (FindPlayerPed()->m_pWanted->GetWantedLevel() < 1 || FindPlayerVehicle()) + return; + CCopPed* pCop = TryToGenerateCopPed(m_vSpawn1); + if (!pCop) + return; + float z = CWorld::FindGroundZForCoord(m_vTarget1.x, m_vTarget1.y); + pCop->bScriptObjectiveCompleted = false; + pCop->SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, CVector(m_vTarget1.x, m_vTarget1.y, z)); + pCop->m_nExtendedRangeTimer = CTimer::GetTimeInMilliseconds() + 10000; + m_nLastTimeCreated = CTimer::GetTimeInMilliseconds(); + return; + } + case SETPIECE_CREATETWOCOPPERSONFOOT: + { + if (FindPlayerPed()->m_pWanted->GetWantedLevel() < 1 || FindPlayerVehicle()) + return; + CCopPed* pCop = TryToGenerateCopPed(m_vSpawn1); + if (!pCop) + return; + float z = CWorld::FindGroundZForCoord(m_vTarget1.x, m_vTarget1.y); + pCop->bScriptObjectiveCompleted = false; + pCop->SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, CVector(m_vTarget1.x, m_vTarget1.y, z)); + pCop->m_nExtendedRangeTimer = CTimer::GetTimeInMilliseconds() + 10000; + CCopPed* pCop2 = TryToGenerateCopPed(m_vSpawn2); + if (!pCop2) { + CWorld::Remove(pCop); + delete pCop; + return; + } + z = CWorld::FindGroundZForCoord(m_vTarget2.x, m_vTarget2.y); + pCop2->bScriptObjectiveCompleted = false; + pCop2->SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, CVector(m_vTarget2.x, m_vTarget2.y, z)); + pCop2->m_nExtendedRangeTimer = CTimer::GetTimeInMilliseconds() + 10000; + m_nLastTimeCreated = CTimer::GetTimeInMilliseconds(); + return; + } + case SETPIECE_TWOCARSBLOCKINGPLAYERFROMSIDE: + { + if (FindPlayerPed()->m_pWanted->GetWantedLevel() < 2) + return; + if (!FindPlayerVehicle()) + return; + if (DotProduct2D(FindPlayerSpeed(), (CVector2D)FindPlayerCoors() - m_vSpawn1) >= 0.0f) + return; + CVehicle* pVehicle1 = TryToGenerateCopCar(m_vSpawn1, m_vTarget1); + if (!pVehicle1) + return; + pVehicle1->SetStatus(STATUS_PHYSICS); + pVehicle1->AutoPilot.m_fMaxTrafficSpeed = pVehicle1->AutoPilot.m_nCruiseSpeed = 16; + pVehicle1->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_PLOUGH_THROUGH; + pVehicle1->AutoPilot.m_nCarMission = MISSION_BLOCKPLAYER_FORWARDANDBACK; + pVehicle1->AutoPilot.m_nTempAction = TEMPACT_GOFORWARD; + pVehicle1->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 100; + pVehicle1->m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 10000; + pVehicle1->SetMoveSpeed(2.0f * pVehicle1->GetForward() / 3.0f); + CCarAI::AddPoliceCarOccupants(pVehicle1); + CVehicle* pVehicle2 = TryToGenerateCopCar(m_vSpawn2, m_vTarget2); + if (!pVehicle2) { + CWorld::Remove(pVehicle1); + delete pVehicle1; + return; + } + pVehicle2->SetStatus(STATUS_PHYSICS); + pVehicle2->AutoPilot.m_fMaxTrafficSpeed = pVehicle1->AutoPilot.m_nCruiseSpeed = 16; + pVehicle2->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_PLOUGH_THROUGH; + pVehicle2->AutoPilot.m_nCarMission = MISSION_BLOCKPLAYER_FORWARDANDBACK; + pVehicle2->AutoPilot.m_nTempAction = TEMPACT_GOFORWARD; + pVehicle2->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 100; + pVehicle2->m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 10000; + pVehicle2->SetMoveSpeed(2.0f * pVehicle2->GetForward() / 3.0f); + CCarAI::AddPoliceCarOccupants(pVehicle2); + m_nLastTimeCreated = CTimer::GetTimeInMilliseconds(); + return; + } + case SETPIECE_TWOCARSRAMMINGPLAYERFROMSIDE: + { + if (FindPlayerPed()->m_pWanted->GetWantedLevel() < 2) + return; + if (!FindPlayerVehicle()) + return; + if (DotProduct2D(FindPlayerSpeed(), (CVector2D)FindPlayerCoors() - m_vSpawn1) >= 0.0f) + return; + CVehicle* pVehicle1 = TryToGenerateCopCar(m_vSpawn1, m_vTarget1); + if (!pVehicle1) + return; + pVehicle1->SetStatus(STATUS_PHYSICS); + pVehicle1->AutoPilot.m_fMaxTrafficSpeed = pVehicle1->AutoPilot.m_nCruiseSpeed = 16; + pVehicle1->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + pVehicle1->AutoPilot.m_nCarMission = MISSION_RAMCAR_CLOSE; + pVehicle1->AutoPilot.m_nTempAction = TEMPACT_GOFORWARD; + pVehicle1->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 100; + pVehicle1->m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 10000; + pVehicle1->SetMoveSpeed(2.0f * pVehicle1->GetForward() / 3.0f); + CCarAI::AddPoliceCarOccupants(pVehicle1); + CVehicle* pVehicle2 = TryToGenerateCopCar(m_vSpawn2, m_vTarget2); + if (!pVehicle2) { + CWorld::Remove(pVehicle1); + delete pVehicle1; + return; + } + pVehicle2->SetStatus(STATUS_PHYSICS); + pVehicle2->AutoPilot.m_fMaxTrafficSpeed = pVehicle2->AutoPilot.m_nCruiseSpeed = 16; + pVehicle2->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + pVehicle2->AutoPilot.m_nCarMission = MISSION_RAMCAR_CLOSE; + pVehicle2->AutoPilot.m_nTempAction = TEMPACT_GOFORWARD; + pVehicle2->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 100; + pVehicle2->m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 10000; + pVehicle2->SetMoveSpeed(2.0f * pVehicle2->GetForward() / 3.0f); + CCarAI::AddPoliceCarOccupants(pVehicle2); + m_nLastTimeCreated = CTimer::GetTimeInMilliseconds(); + return; + } + } +} + +CVehicle* CSetPiece::TryToGenerateCopCar(CVector2D vSpawn, CVector2D vTarget) +{ + CVehicle* pVehicle = new CAutomobile(MI_POLICE, RANDOM_VEHICLE); + CVector pos(vSpawn.x, vSpawn.y, 1000.0f); + CColPoint point; + CEntity* pEntity; + if (CWorld::ProcessVerticalLine(pos, -1000.0f, point, pEntity, true, false, false, false, true, false, nil)) + pos.z = point.point.z + pVehicle->GetHeightAboveRoad(); + CVector vDirection(vTarget.x - vSpawn.x, vTarget.y - vSpawn.y, 0.0f); + vDirection.Normalise(); + pVehicle->GetForward() = CVector(vDirection.x, vDirection.y, 0.0f); + pVehicle->GetRight() = CVector(vDirection.y, -vDirection.x, 0.0f); + pVehicle->GetUp() = CVector(0.0f, 0.0f, 1.0f); + pVehicle->SetPosition(pos); + int16 total; + CWorld::FindObjectsKindaColliding(pos, pVehicle->GetColModel()->spheres->radius, false, &total, 16, nil, false, true, true, false, false); + if (total != 0) { + delete pVehicle; + return nil; + } + pVehicle->ChangeLawEnforcerState(true); + CWorld::Add(pVehicle); + return pVehicle; +} + +CCopPed* CSetPiece::TryToGenerateCopPed(CVector2D vSpawn) +{ + CCopPed* pCop = new CCopPed(COP_STREET); + CVector pos(vSpawn.x, vSpawn.y, 1000.0f); + CColPoint point; + CEntity* pEntity; + if (CWorld::ProcessVerticalLine(pos, -1000.0f, point, pEntity, true, false, false, false, true, false, nil)) + pos.z = point.point.z + 0.9f; + pCop->SetPosition(pos); + int16 total; + CWorld::FindObjectsKindaColliding(pos, pCop->GetColModel()->spheres->radius, false, &total, 16, nil, false, true, true, false, false); + if (total != 0) { + delete pCop; + return nil; + } + CWorld::Add(pCop); + return pCop; +} \ No newline at end of file diff --git a/src/miami/control/SetPieces.h b/src/miami/control/SetPieces.h new file mode 100644 index 00000000..5c228d4c --- /dev/null +++ b/src/miami/control/SetPieces.h @@ -0,0 +1,48 @@ +#pragma once + +#include "config.h" + +class CVehicle; +class CCopPed; + +enum eSetPieceType +{ + SETPIECE_NONE = 0, + SETPIECE_TWOCOPCARSINALLEY, + SETPIECE_CARBLOCKINGPLAYERFROMSIDE, + SETPIECE_CARRAMMINGPLAYERFROMSIDE, + SETPIECE_CREATECOPPERONFOOT, + SETPIECE_CREATETWOCOPPERSONFOOT, + SETPIECE_TWOCARSBLOCKINGPLAYERFROMSIDE, + SETPIECE_TWOCARSRAMMINGPLAYERFROMSIDE +}; + +class CSetPiece +{ +public: + uint8 m_nType; + uint32 m_nLastTimeCreated; + CVector2D m_vTriggerInf; + CVector2D m_vTriggerSup; + CVector2D m_vSpawn1; + CVector2D m_vSpawn2; + CVector2D m_vTarget1; + CVector2D m_vTarget2; + + CVehicle* TryToGenerateCopCar(CVector2D, CVector2D); + CCopPed* TryToGenerateCopPed(CVector2D); + void Update(void); +}; + +class CSetPieces +{ + static bool bDebug; + static uint32 NumSetPieces; + static CSetPiece aSetPieces[NUM_SETPIECES]; +public: + static void Init(void); + static void AddOne(uint8 type, CVector2D, CVector2D, CVector2D, CVector2D, CVector2D, CVector2D); + static void Save(uint8*, uint32*); + static void Load(uint8*, uint32); + static void Update(void); +}; diff --git a/src/miami/control/TrafficLights.cpp b/src/miami/control/TrafficLights.cpp new file mode 100644 index 00000000..e484d3be --- /dev/null +++ b/src/miami/control/TrafficLights.cpp @@ -0,0 +1,536 @@ +#include "common.h" + +#include "Camera.h" +#include "Clock.h" +#include "Coronas.h" +#include "General.h" +#include "PathFind.h" +#include "PointLights.h" +#include "Shadows.h" +#include "SpecialFX.h" +#include "Timecycle.h" +#include "Timer.h" +#include "TrafficLights.h" +#include "Vehicle.h" +#include "Weather.h" +#include "World.h" + +bool CTrafficLights::bGreenLightsCheat; + +void +CTrafficLights::DisplayActualLight(CEntity *ent) +{ + if(ent->GetUp().z < 0.96f || ent->bRenderDamaged) + return; + + int phase; + if(FindTrafficLightType(ent) == 1) + phase = LightForCars1_Visual(); + else + phase = LightForCars2_Visual(); + + int i, m = ent->GetModelIndex(); + if (MI_TRAFFICLIGHTS == m) { + CBaseModelInfo* mi = CModelInfo::GetModelInfo(ent->GetModelIndex()); + float x = mi->Get2dEffect(0)->pos.x; + float yMin = mi->Get2dEffect(0)->pos.y; + float yMax = mi->Get2dEffect(0)->pos.y; + float zMin = mi->Get2dEffect(0)->pos.z; + float zMax = mi->Get2dEffect(0)->pos.z; + for (i = 1; i < 6; i++) { + assert(mi->Get2dEffect(i)); + yMin = Min(yMin, mi->Get2dEffect(i)->pos.y); + yMax = Max(yMax, mi->Get2dEffect(i)->pos.y); + zMin = Min(zMin, mi->Get2dEffect(i)->pos.z); + zMax = Max(zMax, mi->Get2dEffect(i)->pos.z); + } + + CVector pos1, pos2; + uint8 r, g; + int id; + switch (phase) { + case CAR_LIGHTS_GREEN: + r = 0; + g = 255; + pos1 = ent->GetMatrix() * CVector(x, yMax, zMin); + pos2 = ent->GetMatrix() * CVector(x, yMin, zMin); + id = 0; + break; + case CAR_LIGHTS_YELLOW: + r = 255; + g = 128; + pos1 = ent->GetMatrix() * CVector(x, yMax, (zMin + zMax) / 2.0f); + pos2 = ent->GetMatrix() * CVector(x, yMin, (zMin + zMax) / 2.0f); + id = 1; + break; + case CAR_LIGHTS_RED: + r = 255; + g = 0; + pos1 = ent->GetMatrix() * CVector(x, yMax, zMax); + pos2 = ent->GetMatrix() * CVector(x, yMin, zMax); + id = 2; + break; + default: + r = 0; + g = 0; + pos1 = ent->GetMatrix() * CVector(x, yMax, (zMin + zMax) / 2.0f); + pos2 = ent->GetMatrix() * CVector(x, yMin, (zMin + zMax) / 2.0f); + id = -1; + break; + } + + if (CWeather::TrafficLightBrightness > 0.5f) + CPointLights::AddLight(CPointLights::LIGHT_POINT, + pos1, CVector(0.0f, 0.0f, 0.0f), 8.0f, + r / 255.0f, g / 255.0f, 0 / 255.0f, CPointLights::FOG_NORMAL, true); + + if (CWeather::TrafficLightBrightness > 0.05f) + CShadows::StoreStaticShadow((uintptr)ent, + SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &pos1, + 8.0f, 0.0f, 0.0f, -8.0f, 128, + r * CTimeCycle::GetLightOnGroundBrightness() * CWeather::TrafficLightBrightness / 8.0f, + g * CTimeCycle::GetLightOnGroundBrightness() * CWeather::TrafficLightBrightness / 8.0f, + 0 * CTimeCycle::GetLightOnGroundBrightness() * CWeather::TrafficLightBrightness / 8.0f, + 12.0f, 1.0f, 40.0f, false, 0.0f); + + if (DotProduct(TheCamera.GetForward(), ent->GetForward()) < 0.0f) + CCoronas::RegisterCorona((uintptr)ent + id, + r * CTimeCycle::GetSpriteBrightness() * 0.7f, + g * CTimeCycle::GetSpriteBrightness() * 0.7f, + 0 * CTimeCycle::GetSpriteBrightness() * 0.7f, + 255, + pos1, 1.75f * CTimeCycle::GetSpriteSize(), 50.0f, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + else + CCoronas::RegisterCorona((uintptr)ent + id + 3, + r * CTimeCycle::GetSpriteBrightness() * 0.7f, + g * CTimeCycle::GetSpriteBrightness() * 0.7f, + 0 * CTimeCycle::GetSpriteBrightness() * 0.7f, + 255, + pos2, 1.75f * CTimeCycle::GetSpriteSize(), 50.0f, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + + CBrightLights::RegisterOne(pos1, ent->GetUp(), ent->GetRight(), CVector(0.0f, 0.0f, 0.0f), id + BRIGHTLIGHT_TRAFFIC_GREEN); + CBrightLights::RegisterOne(pos2, ent->GetUp(), -ent->GetRight(), CVector(0.0f, 0.0f, 0.0f), id + BRIGHTLIGHT_TRAFFIC_GREEN); + } + else if (MI_TRAFFICLIGHTS_VERTICAL == m) { + CBaseModelInfo* mi = CModelInfo::GetModelInfo(ent->GetModelIndex()); + float x = mi->Get2dEffect(0)->pos.x; + float yMin = mi->Get2dEffect(0)->pos.y; + float yMax = mi->Get2dEffect(0)->pos.y; + float zMin = mi->Get2dEffect(0)->pos.z; + float zMax = mi->Get2dEffect(0)->pos.z; + for (i = 1; i < 6; i++) { + assert(mi->Get2dEffect(i)); + yMin = Min(yMin, mi->Get2dEffect(i)->pos.y); + yMax = Max(yMax, mi->Get2dEffect(i)->pos.y); + zMin = Min(zMin, mi->Get2dEffect(i)->pos.z); + zMax = Max(zMax, mi->Get2dEffect(i)->pos.z); + } + + CVector pos1; + uint8 r, g; + int id; + switch (phase) { + case CAR_LIGHTS_GREEN: + r = 0; + g = 255; + pos1 = ent->GetMatrix() * mi->Get2dEffect(2)->pos; + id = 0; + break; + case CAR_LIGHTS_YELLOW: + r = 255; + g = 128; + pos1 = ent->GetMatrix() * mi->Get2dEffect(1)->pos; + id = 1; + break; + case CAR_LIGHTS_RED: + r = 255; + g = 0; + pos1 = ent->GetMatrix() * mi->Get2dEffect(0)->pos; + id = 2; + break; + default: + r = 0; + g = 0; + pos1 = ent->GetMatrix() * mi->Get2dEffect(1)->pos; + id = -1; + break; + } + + CBrightLights::RegisterOne(pos1, ent->GetUp(), ent->GetRight(), CVector(0.0f, 0.0f, 0.0f), id + BRIGHTLIGHT_TRAFFIC_GREEN); + + if (CWeather::TrafficLightBrightness > 0.5f) + CPointLights::AddLight(CPointLights::LIGHT_POINT, + pos1, CVector(0.0f, 0.0f, 0.0f), 8.0f, + r / 255.0f, g / 255.0f, 0 / 255.0f, CPointLights::FOG_NORMAL, true); + + if (CWeather::TrafficLightBrightness > 0.05f) + CShadows::StoreStaticShadow((uintptr)ent, + SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &pos1, + 8.0f, 0.0f, 0.0f, -8.0f, 128, + r * CTimeCycle::GetLightOnGroundBrightness() * CWeather::TrafficLightBrightness / 8.0f, + g * CTimeCycle::GetLightOnGroundBrightness() * CWeather::TrafficLightBrightness / 8.0f, + 0 * CTimeCycle::GetLightOnGroundBrightness() * CWeather::TrafficLightBrightness / 8.0f, + 12.0f, 1.0f, 40.0f, false, 0.0f); + + if (DotProduct(TheCamera.GetForward(), ent->GetForward()) < 0.0f) + CCoronas::RegisterCorona((uintptr)ent + id, + r * CTimeCycle::GetSpriteBrightness() * 0.7f, + g * CTimeCycle::GetSpriteBrightness() * 0.7f, + 0 * CTimeCycle::GetSpriteBrightness() * 0.7f, + 255, + pos1, 1.75f * CTimeCycle::GetSpriteSize(), 50.0f, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + } + else if (MI_TRAFFICLIGHTS_MIAMI == m || MI_TRAFFICLIGHTS_TWOVERTICAL == m) { + CBaseModelInfo* mi = CModelInfo::GetModelInfo(ent->GetModelIndex()); + CVector pos1, pos2; + uint8 r, g; + int id; + if (MI_TRAFFICLIGHTS_MIAMI == m) { + switch (phase) { + case CAR_LIGHTS_GREEN: + r = 0; + g = 255; + pos1 = ent->GetMatrix() * mi->Get2dEffect(4)->pos; + pos2 = ent->GetMatrix() * mi->Get2dEffect(5)->pos; + id = 0; + break; + case CAR_LIGHTS_YELLOW: + r = 255; + g = 128; + pos1 = ent->GetMatrix() * mi->Get2dEffect(2)->pos; + pos2 = ent->GetMatrix() * mi->Get2dEffect(3)->pos; + id = 1; + break; + case CAR_LIGHTS_RED: + r = 255; + g = 0; + pos1 = ent->GetMatrix() * mi->Get2dEffect(0)->pos; + pos2 = ent->GetMatrix() * mi->Get2dEffect(1)->pos; + id = 2; + break; + default: + r = 0; + g = 0; + pos1 = ent->GetMatrix() * mi->Get2dEffect(2)->pos; + pos2 = ent->GetMatrix() * mi->Get2dEffect(3)->pos; + id = -1; + break; + } + } + else { + switch (phase) { + case CAR_LIGHTS_GREEN: + r = 0; + g = 255; + pos1 = ent->GetMatrix() * mi->Get2dEffect(2)->pos; + pos2 = ent->GetMatrix() * mi->Get2dEffect(5)->pos; + id = 0; + break; + case CAR_LIGHTS_YELLOW: + r = 255; + g = 128; + pos1 = ent->GetMatrix() * mi->Get2dEffect(1)->pos; + pos2 = ent->GetMatrix() * mi->Get2dEffect(4)->pos; + id = 1; + break; + case CAR_LIGHTS_RED: + r = 255; + g = 0; + pos1 = ent->GetMatrix() * mi->Get2dEffect(0)->pos; + pos2 = ent->GetMatrix() * mi->Get2dEffect(3)->pos; + id = 2; + break; + default: + r = 0; + g = 0; + pos1 = ent->GetMatrix() * mi->Get2dEffect(1)->pos; + pos2 = ent->GetMatrix() * mi->Get2dEffect(4)->pos; + id = -1; + break; + } + } + + CVector pos = (pos1 + pos2) / 2; + if (id >= 0) { + CBrightLights::RegisterOne(pos1, ent->GetUp(), ent->GetRight(), CVector(0.0f, 0.0f, 0.0f), id + BRIGHTLIGHT_TRAFFIC_GREEN); + CBrightLights::RegisterOne(pos2, ent->GetUp(), ent->GetRight(), CVector(0.0f, 0.0f, 0.0f), id + BRIGHTLIGHT_TRAFFIC_GREEN); + } + + if (CWeather::TrafficLightBrightness > 0.5f) + CPointLights::AddLight(CPointLights::LIGHT_POINT, + pos, CVector(0.0f, 0.0f, 0.0f), 8.0f, + r / 255.0f, g / 255.0f, 0 / 255.0f, CPointLights::FOG_NORMAL, true); + + if (CWeather::TrafficLightBrightness > 0.05f) + CShadows::StoreStaticShadow((uintptr)ent, + SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &pos, + 8.0f, 0.0f, 0.0f, -8.0f, 128, + r * CTimeCycle::GetLightOnGroundBrightness() * CWeather::TrafficLightBrightness / 8.0f, + g * CTimeCycle::GetLightOnGroundBrightness() * CWeather::TrafficLightBrightness / 8.0f, + 0 * CTimeCycle::GetLightOnGroundBrightness() * CWeather::TrafficLightBrightness / 8.0f, + 12.0f, 1.0f, 40.0f, false, 0.0f); + + if (id >= 0) { + if (DotProduct(TheCamera.GetForward(), ent->GetForward()) > 0.0f) + CCoronas::RegisterCorona((uintptr)ent + id, + r * CTimeCycle::GetSpriteBrightness() * 0.7f, + g * CTimeCycle::GetSpriteBrightness() * 0.7f, + 0 * CTimeCycle::GetSpriteBrightness() * 0.7f, + 255, + pos1, 1.75f * CTimeCycle::GetSpriteSize(), 50.0f, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + else + CCoronas::RegisterCorona((uintptr)ent + id, + r * CTimeCycle::GetSpriteBrightness() * 0.7f, + g * CTimeCycle::GetSpriteBrightness() * 0.7f, + 0 * CTimeCycle::GetSpriteBrightness() * 0.7f, + 255, + pos2, 1.75f * CTimeCycle::GetSpriteSize(), 50.0f, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + } + } +} + +bool DoesLineSegmentIntersect(float l1x1, float l1y1, float l1x2, float l1y2, float l2x1, float l2y1, float l2x2, float l2y2) +{ + return ((l2y2 - l1y1) * (l1x2 - l1x1) + (l1x1 - l2x2) * (l1y2 - l1y1)) * + ((l2y1 - l1y1) * (l1x2 - l1x1) + (l1x1 - l2x1) * (l1y2 - l1y1)) <= 0.0f && + ((l1y2 - l2y1) * (l2x2 - l2x1) + (l2y2 - l2y1) * (l2x1 - l1x2)) * + ((l1y1 - l2y1) * (l2x2 - l2x1) + (l2y2 - l2y1) * (l2x1 - l1x1)) <= 0.0f; +} + +void +CTrafficLights::ScanForLightsOnMap(void) +{ + int x, y; + int i, j, k, l; + CPtrNode *node; + + for(x = 0; x < NUMSECTORS_X; x++) + for(y = 0; y < NUMSECTORS_Y; y++){ + CPtrList &list = CWorld::GetSector(x, y)->m_lists[ENTITYLIST_DUMMIES]; + for(node = list.first; node; node = node->next){ + CEntity *light = (CEntity*)node->item; + if (!IsTrafficLight(light->GetModelIndex())) + continue; + + CVector pos1 = light->GetMatrix() * CVector(17.0f, 0.0f, 0.0f); + CVector pos2 = light->GetMatrix() * CVector(-15.0f, 0.0f, 0.0f); + + // Check cars + for(i = 0; i < ThePaths.m_numCarPathNodes; i++){ + if ((ThePaths.m_pathNodes[i].GetPosition() - pos1).MagnitudeSqr() >= SQR(100.0f)) + continue; + for (j = 0; j < ThePaths.m_pathNodes[i].numLinks; j++){ + int con = ThePaths.ConnectedNode(ThePaths.m_pathNodes[i].firstLink + j); + if (i < con) { + CVector i_pos = ThePaths.m_pathNodes[i].GetPosition(); + CVector con_pos = ThePaths.m_pathNodes[con].GetPosition(); + if (Abs(pos1.z - (i_pos.z + con_pos.z) / 2) < 10.0f && + DoesLineSegmentIntersect(pos1.x, pos1.y, pos2.x, pos2.y, i_pos.x, i_pos.y, con_pos.x, con_pos.y)) { + //debug("Setting up light: nodes %f %f %f - %f %f %f, light %f %f %f - %f %f %f\n", i_pos.x, i_pos.y, i_pos.z, con_pos.x, con_pos.y, con_pos.z, pos1.x, pos1.y, pos1.z, pos2.x, pos2.y, pos2.z); + int link = ThePaths.m_carPathConnections[ThePaths.m_pathNodes[i].firstLink + j]; + ThePaths.m_carPathLinks[link].trafficLightType = FindTrafficLightType(light); + if (ThePaths.m_pathNodes[i].numLinks > ThePaths.m_pathNodes[con].numLinks) + con = i; + if (ThePaths.m_carPathLinks[link].pathNodeIndex != con) + ThePaths.m_carPathLinks[link].trafficLightDirection = true; + } + } + } + } + + // Check peds + for(i = ThePaths.m_numCarPathNodes; i < ThePaths.m_numPathNodes; i++){ + float dist1, dist2; + dist1 = Abs(ThePaths.m_pathNodes[i].GetX() - light->GetPosition().x) + + Abs(ThePaths.m_pathNodes[i].GetY() - light->GetPosition().y); + if(dist1 < 50.0f){ + for(l = 0; l < ThePaths.m_pathNodes[i].numLinks; l++){ + j = ThePaths.m_pathNodes[i].firstLink + l; + if(ThePaths.ConnectionCrossesRoad(j)){ + k = ThePaths.ConnectedNode(j); + dist2 = Abs(ThePaths.m_pathNodes[k].GetX() - light->GetPosition().x) + + Abs(ThePaths.m_pathNodes[k].GetY() - light->GetPosition().y); + if(dist1 < 15.0f || dist2 < 15.0f) + ThePaths.ConnectionSetTrafficLight(j); + } + } + } + } + } + } +} + +bool +CTrafficLights::ShouldCarStopForLight(CVehicle *vehicle, bool alwaysStop) +{ + int node, type; + bool direction; + + node = vehicle->AutoPilot.m_nNextPathNodeInfo; + type = ThePaths.m_carPathLinks[node].trafficLightType; + direction = ThePaths.m_carPathLinks[node].trafficLightDirection; + + if(type){ + if((direction || ThePaths.m_carPathLinks[node].pathNodeIndex == vehicle->AutoPilot.m_nNextRouteNode) && + (!direction || ThePaths.m_carPathLinks[node].pathNodeIndex != vehicle->AutoPilot.m_nNextRouteNode)) + if(alwaysStop || + type == 1 && LightForCars1() != CAR_LIGHTS_GREEN || + type == 2 && LightForCars2() != CAR_LIGHTS_GREEN){ + float dist = DotProduct2D(CVector2D(vehicle->GetPosition()) - ThePaths.m_carPathLinks[node].GetPosition(), + ThePaths.m_carPathLinks[node].GetDirection()); + if(vehicle->AutoPilot.m_nNextDirection == -1){ + if(dist > 0.0f && dist < 8.0f) + return true; + }else{ + if(dist < 0.0f && dist > -8.0f) + return true; + } + } + } + + node = vehicle->AutoPilot.m_nCurrentPathNodeInfo; + type = ThePaths.m_carPathLinks[node].trafficLightType; + direction = ThePaths.m_carPathLinks[node].trafficLightDirection; + if(type){ + if((direction || ThePaths.m_carPathLinks[node].pathNodeIndex == vehicle->AutoPilot.m_nCurrentRouteNode) && + (!direction || ThePaths.m_carPathLinks[node].pathNodeIndex != vehicle->AutoPilot.m_nCurrentRouteNode)) + if(alwaysStop || + type == 1 && LightForCars1() != CAR_LIGHTS_GREEN || + type == 2 && LightForCars2() != CAR_LIGHTS_GREEN){ + float dist = DotProduct2D(CVector2D(vehicle->GetPosition()) - ThePaths.m_carPathLinks[node].GetPosition(), + ThePaths.m_carPathLinks[node].GetDirection()); + if(vehicle->AutoPilot.m_nCurrentDirection == -1){ + if(dist > 0.0f && dist < 8.0f) + return true; + }else{ + if(dist < 0.0f && dist > -8.0f) + return true; + } + } + } + + if(vehicle->GetStatus() == STATUS_PHYSICS){ + node = vehicle->AutoPilot.m_nPreviousPathNodeInfo; + type = ThePaths.m_carPathLinks[node].trafficLightType; + direction = ThePaths.m_carPathLinks[node].trafficLightDirection; + if(type){ + if((direction || ThePaths.m_carPathLinks[node].pathNodeIndex == vehicle->AutoPilot.m_nPrevRouteNode) && + (!direction || ThePaths.m_carPathLinks[node].pathNodeIndex != vehicle->AutoPilot.m_nPrevRouteNode)) + if(alwaysStop || + type == 1 && LightForCars1() != CAR_LIGHTS_GREEN || + type == 2 && LightForCars2() != CAR_LIGHTS_GREEN){ + float dist = DotProduct2D(CVector2D(vehicle->GetPosition()) - ThePaths.m_carPathLinks[node].GetPosition(), + ThePaths.m_carPathLinks[node].GetDirection()); + if(vehicle->AutoPilot.m_nPreviousDirection == -1){ + if(dist > 0.0f && dist < 6.0f) + return true; + }else{ + if(dist < 0.0f && dist > -6.0f) + return true; + } + } + } + } + + return false; +} + +bool +CTrafficLights::ShouldCarStopForBridge(CVehicle *vehicle) +{ +#ifdef GTA_BRIDGE + return ThePaths.m_carPathLinks[vehicle->AutoPilot.m_nNextPathNodeInfo].bBridgeLights && + !ThePaths.m_carPathLinks[vehicle->AutoPilot.m_nCurrentPathNodeInfo].bBridgeLights; +#else + return false; +#endif +} + +int +CTrafficLights::FindTrafficLightType(CEntity *light) +{ + float orientation = RADTODEG(CGeneral::GetATanOfXY(light->GetForward().x, light->GetForward().y)); + if((orientation > 60.0f && orientation < 60.0f + 90.0f) || + (orientation > 240.0f && orientation < 240.0f + 90.0f)) + return 1; + return 2; +} + +uint8 +CTrafficLights::LightForPeds(void) +{ + uint32 period = CTimer::GetTimeInMilliseconds() % 16384; + + if(period < 12000) + return PED_LIGHTS_DONT_WALK; + else if(period < 16384 - 1000) + return PED_LIGHTS_WALK; + else + return PED_LIGHTS_WALK_BLINK; +} + +uint8 +CTrafficLights::LightForCars1(void) +{ + if (CWeather::Wind > 1.1f) + return CAR_LIGHTS_GREEN; + + if (bGreenLightsCheat) + return CAR_LIGHTS_GREEN; + + uint32 period = CTimer::GetTimeInMilliseconds() % 16384; + + if(period < 5000) + return CAR_LIGHTS_GREEN; + else if(period < 5000 + 1000) + return CAR_LIGHTS_YELLOW; + else + return CAR_LIGHTS_RED; +} + +uint8 +CTrafficLights::LightForCars2(void) +{ + if (CWeather::Wind > 1.1f) + return CAR_LIGHTS_GREEN; + + if (bGreenLightsCheat) + return CAR_LIGHTS_GREEN; + + uint32 period = CTimer::GetTimeInMilliseconds() % 16384; + + if(period < 6000) + return CAR_LIGHTS_RED; + else if(period < 12000 - 1000) + return CAR_LIGHTS_GREEN; + else if(period < 12000) + return CAR_LIGHTS_YELLOW; + else + return CAR_LIGHTS_RED; +} + +uint8 +CTrafficLights::LightForCars1_Visual(void) +{ + if (CWeather::Wind <= 1.1f) + return LightForCars1(); + return (CTimer::GetTimeInMilliseconds() & 0x400 ? CAR_LIGHTS_NONE : CAR_LIGHTS_YELLOW); +} + +uint8 +CTrafficLights::LightForCars2_Visual(void) +{ + if (CWeather::Wind <= 1.1f) + return LightForCars2(); + return (CTimer::GetTimeInMilliseconds() & 0x400 ? CAR_LIGHTS_NONE : CAR_LIGHTS_YELLOW); +} diff --git a/src/miami/control/TrafficLights.h b/src/miami/control/TrafficLights.h new file mode 100644 index 00000000..8dba45e1 --- /dev/null +++ b/src/miami/control/TrafficLights.h @@ -0,0 +1,32 @@ +#pragma once + +class CEntity; +class CVehicle; + +enum { + PED_LIGHTS_WALK, + PED_LIGHTS_WALK_BLINK, + PED_LIGHTS_DONT_WALK, + + CAR_LIGHTS_GREEN = 0, + CAR_LIGHTS_YELLOW, + CAR_LIGHTS_RED, + CAR_LIGHTS_NONE +}; + +class CTrafficLights +{ +public: + static bool bGreenLightsCheat; + + static void DisplayActualLight(CEntity *ent); + static void ScanForLightsOnMap(void); + static int FindTrafficLightType(CEntity *light); + static uint8 LightForPeds(void); + static uint8 LightForCars1(void); + static uint8 LightForCars2(void); + static uint8 LightForCars1_Visual(void); + static uint8 LightForCars2_Visual(void); + static bool ShouldCarStopForLight(CVehicle*, bool); + static bool ShouldCarStopForBridge(CVehicle*); +}; diff --git a/src/miami/core/Accident.cpp b/src/miami/core/Accident.cpp new file mode 100644 index 00000000..c8611323 --- /dev/null +++ b/src/miami/core/Accident.cpp @@ -0,0 +1,128 @@ +#include "common.h" + +#include "Accident.h" + +#include "Ped.h" +#include "Pools.h" +#include "World.h" + +CAccidentManager gAccidentManager; + +CAccident* +CAccidentManager::GetNextFreeAccident() +{ + for (int i = 0; i < NUM_ACCIDENTS; i++) { + if (m_aAccidents[i].m_pVictim == nil) + return &m_aAccidents[i]; + } + + return nil; +} + +void +CAccidentManager::ReportAccident(CPed *ped) +{ + if (!ped->IsPlayer() && ped->CharCreatedBy != MISSION_CHAR && !ped->bRenderScorched && !ped->bBodyPartJustCameOff && ped->bAllowMedicsToReviveMe && !ped->bIsInWater) { + for (int i = 0; i < NUM_ACCIDENTS; i++) { + if (m_aAccidents[i].m_pVictim != nil && m_aAccidents[i].m_pVictim == ped) + return; + } + + if (ped->m_pCurrentPhysSurface == nil) { + CVector point = ped->GetPosition(); + point.z -= 2.0f; + + CColPoint colPoint; + CEntity *pEntity; + + if (!CWorld::ProcessVerticalLine(point, -100.0f, colPoint, pEntity, true, false, false, false, false, false, nil)) { + CAccident *accident = GetNextFreeAccident(); + if (accident != nil) { + accident->m_pVictim = ped; + ped->RegisterReference((CEntity**)&accident->m_pVictim); + accident->m_nMedicsPerformingCPR = 0; + accident->m_nMedicsAttending = 0; + ped->m_lastAccident = accident; + WorkToDoForMedics(); + } + } + } + } +} + +void +CAccidentManager::Update() +{ +#ifdef SQUEEZE_PERFORMANCE + // Handled after injury registered. + return; +#endif + int32 e; + if (CEventList::GetEvent(EVENT_INJURED_PED, &e)) { + CPed *ped = CPools::GetPed(gaEvent[e].entityRef); + if (ped) { + ReportAccident(ped); + CEventList::ClearEvent(e); + } + } +} + +CAccident* +CAccidentManager::FindNearestAccident(CVector vecPos, float *pDistance) +{ + for (int i = 0; i < MAX_MEDICS_TO_ATTEND_ACCIDENT; i++){ + int accidentId = -1; + float minDistance = 999999; + for (int j = 0; j < NUM_ACCIDENTS; j++){ + CPed* pVictim = m_aAccidents[j].m_pVictim; + if (!pVictim) + continue; + if (pVictim->CharCreatedBy == MISSION_CHAR) + continue; + if (pVictim->m_fHealth != 0.0f) + continue; + if (m_aAccidents[j].m_nMedicsPerformingCPR != i) + continue; + float distance = (pVictim->GetPosition() - vecPos).Magnitude2D(); + if (distance / 2 > pVictim->GetPosition().z - vecPos.z && distance < minDistance){ + minDistance = distance; + accidentId = j; + } + } + *pDistance = minDistance; + if (accidentId != -1) + return &m_aAccidents[accidentId]; + } + return nil; +} + +uint16 +CAccidentManager::CountActiveAccidents() +{ + uint16 accidents = 0; + for (int i = 0; i < NUM_ACCIDENTS; i++) { + if (m_aAccidents[i].m_pVictim) + accidents++; + } + return accidents; +} + +bool +CAccidentManager::WorkToDoForMedics() +{ + for (int i = 0; i < NUM_ACCIDENTS; i++) { + if (m_aAccidents[i].m_pVictim != nil && m_aAccidents[i].m_nMedicsAttending < MAX_MEDICS_TO_ATTEND_ACCIDENT) + return true; + } + return false; +} + +bool +CAccidentManager::UnattendedAccidents() +{ + for (int i = 0; i < NUM_ACCIDENTS; i++) { + if (m_aAccidents[i].m_pVictim != nil && m_aAccidents[i].m_nMedicsAttending == 0) + return true; + } + return false; +} diff --git a/src/miami/core/Accident.h b/src/miami/core/Accident.h new file mode 100644 index 00000000..568e1149 --- /dev/null +++ b/src/miami/core/Accident.h @@ -0,0 +1,31 @@ +#pragma once +#include "config.h" + +class CPed; + +class CAccident +{ +public: + CPed *m_pVictim; + uint32 m_nMedicsAttending; + uint32 m_nMedicsPerformingCPR; + CAccident() : m_pVictim(nil), m_nMedicsAttending(0), m_nMedicsPerformingCPR(0) {} +}; + +class CAccidentManager +{ + CAccident m_aAccidents[NUM_ACCIDENTS]; + enum { + MAX_MEDICS_TO_ATTEND_ACCIDENT = 2 + }; +public: + CAccident *GetNextFreeAccident(); + void ReportAccident(CPed *ped); + void Update(); + CAccident *FindNearestAccident(CVector vecPos, float *pDistance); + uint16 CountActiveAccidents(); + bool UnattendedAccidents(); + bool WorkToDoForMedics(); +}; + +extern CAccidentManager gAccidentManager; \ No newline at end of file diff --git a/src/miami/core/AnimViewer.cpp b/src/miami/core/AnimViewer.cpp new file mode 100644 index 00000000..562b9c15 --- /dev/null +++ b/src/miami/core/AnimViewer.cpp @@ -0,0 +1,437 @@ +#include "common.h" + +#include "Font.h" +#include "Pad.h" +#include "Text.h" +#include "main.h" +#include "Timer.h" +#include "DMAudio.h" +#include "FileMgr.h" +#include "Streaming.h" +#include "TxdStore.h" +#include "General.h" +#include "Camera.h" +#include "Vehicle.h" +#include "Bike.h" +#include "PlayerSkin.h" +#include "PlayerInfo.h" +#include "World.h" +#include "Renderer.h" +#include "AnimManager.h" +#include "AnimBlendAssocGroup.h" +#include "AnimViewer.h" +#include "PlayerPed.h" +#include "Pools.h" +#include "References.h" +#include "PathFind.h" +#include "HandlingMgr.h" +#include "TempColModels.h" +#include "Particle.h" +#include "CdStream.h" +#include "Messages.h" +#include "CarCtrl.h" +#include "FileLoader.h" +#include "ModelIndices.h" +#include "Clock.h" +#include "Timecycle.h" +#include "RpAnimBlend.h" +#include "AnimBlendAssociation.h" +#include "Shadows.h" +#include "Radar.h" +#include "Hud.h" +#include "debugmenu.h" + +int CAnimViewer::animTxdSlot = 0; +CEntity *CAnimViewer::pTarget = nil; + +void +CAnimViewer::Render(void) { + if (pTarget) { + if (pTarget) { +#ifdef FIX_BUGS + if(pTarget->IsPed()) + ((CPed*)pTarget)->UpdateRpHAnim(); +#endif + pTarget->Render(); + CRenderer::RenderOneNonRoad(pTarget); + } + } +} + +void +CAnimViewer::Initialise(void) { + + // we need messages, messages needs hud, hud needs those + int hudSlot = CTxdStore::AddTxdSlot("hud"); + CTxdStore::LoadTxd(hudSlot, "MODELS/HUD.TXD"); + CHud::m_Wants_To_Draw_Hud = false; + + animTxdSlot = CTxdStore::AddTxdSlot("generic"); + CTxdStore::Create(animTxdSlot); + int particleSlot = CTxdStore::AddTxdSlot("particle"); + CTxdStore::LoadTxd(particleSlot, "MODELS/PARTICLE.TXD"); + CTxdStore::SetCurrentTxd(animTxdSlot); + CPools::Initialise(); + CReferences::Init(); + TheCamera.Init(); + TheCamera.SetRwCamera(Scene.camera); + TheCamera.Cams[TheCamera.ActiveCam].Distance = 5.0f; + ThePaths.Init(); + ThePaths.AllocatePathFindInfoMem(4500); + CCollision::Init(); + CWorld::Initialise(); + mod_HandlingManager.Initialise(); + CTempColModels::Initialise(); + CAnimManager::Initialise(); + CModelInfo::Initialise(); + CParticle::Initialise(); + CCarCtrl::Init(); + CPedStats::Initialise(); + CMessages::Init(); + CdStreamAddImage("MODELS\\GTA3.IMG"); + CFileLoader::LoadLevel("DATA\\DEFAULT.DAT"); + CFileLoader::LoadLevel("DATA\\ANIMVIEWER.DAT"); + CStreaming::Init(); + for(int i = 0; i < MODELINFOSIZE; i++) + if(CModelInfo::GetModelInfo(i)) + CModelInfo::GetModelInfo(i)->ConvertAnimFileIndex(); + CStreaming::LoadInitialPeds(); + CStreaming::RequestSpecialModel(MI_PLAYER, "player", STREAMFLAGS_DONT_REMOVE); + CStreaming::LoadAllRequestedModels(false); + CRenderer::Init(); + CVehicleModelInfo::LoadVehicleColours(); +#ifdef FIX_BUGS + CVehicleModelInfo::LoadEnvironmentMaps(); +#endif + CAnimManager::LoadAnimFiles(); + CWorld::PlayerInFocus = 0; + CWeapon::InitialiseWeapons(); + CPed::Initialise(); + CTimer::Initialise(); + CClock::Initialise(60000); + CTimeCycle::Initialise(); + CCarCtrl::Init(); + CPlayerPed *player = new CPlayerPed(); + player->SetPosition(1000.0f, 1000.0f, 1000.0f); + CWorld::Players[0].m_pPed = player; + CDraw::SetFOV(120.0f); + CDraw::ms_fLODDistance = 500.0f; + + int fd = CFileMgr::OpenFile("DATA\\SPECIAL.TXT", "r"); + char animGroup[32], modelName[32]; + if (fd) { + for (int lineId = 0; lineId < NUM_OF_SPECIAL_CHARS; lineId++) { + if (!CFileMgr::ReadLine(fd, gString, 255)) + break; + + sscanf(gString, "%s %s", modelName, animGroup); + int groupId; + for (groupId = 0; groupId < NUM_ANIM_ASSOC_GROUPS; groupId++) { + if (!strcmp(animGroup, CAnimManager::GetAnimGroupName((AssocGroupId)groupId))) + break; + } + + if (groupId != NUM_ANIM_ASSOC_GROUPS) + ((CPedModelInfo*)CModelInfo::GetModelInfo(MI_SPECIAL01 + lineId))->m_animGroup = groupId; + + CStreaming::RequestSpecialChar(lineId, modelName, STREAMFLAGS_DONT_REMOVE); + } + CFileMgr::CloseFile(fd); + } else { + // TODO? maybe request some special models here so the thing doesn't crash + } + + // From LCS. idk if needed + int vanBlock = CAnimManager::GetAnimationBlockIndex("van"); + int bikesBlock = CAnimManager::GetAnimationBlockIndex("bikes"); + int bikevBlock = CAnimManager::GetAnimationBlockIndex("bikev"); + int bikehBlock = CAnimManager::GetAnimationBlockIndex("bikeh"); + int bikedBlock = CAnimManager::GetAnimationBlockIndex("biked"); + CStreaming::FlushRequestList(); + CStreaming::RequestAnim(vanBlock, STREAMFLAGS_DEPENDENCY); + CStreaming::RequestAnim(bikesBlock, STREAMFLAGS_DEPENDENCY); + CStreaming::RequestAnim(bikevBlock, STREAMFLAGS_DEPENDENCY); + CStreaming::RequestAnim(bikehBlock, STREAMFLAGS_DEPENDENCY); + CStreaming::RequestAnim(bikedBlock, STREAMFLAGS_DEPENDENCY); + CStreaming::LoadAllRequestedModels(false); + CAnimManager::AddAnimBlockRef(vanBlock); + CAnimManager::AddAnimBlockRef(bikesBlock); + CAnimManager::AddAnimBlockRef(bikevBlock); + CAnimManager::AddAnimBlockRef(bikehBlock); + CAnimManager::AddAnimBlockRef(bikedBlock); +} + +int +LastPedModelId(int modelId) +{ + CBaseModelInfo *model; + for(;;){ + assert(modelId < MODELINFOSIZE); + model = CModelInfo::GetModelInfo(modelId); + if (model && model->GetModelType() == MITYPE_PED) + break; + modelId--; + } + return modelId; +} + +int +FirstCarModelId(int modelId) +{ + CBaseModelInfo *model; + for(;;){ + assert(modelId < MODELINFOSIZE); + model = CModelInfo::GetModelInfo(modelId); + if (model && model->GetModelType() == MITYPE_VEHICLE) + break; + modelId++; + } + return modelId; +} + + +int +NextModelId(int modelId, int wantedChange) +{ + // Max. 2 trials wasn't here, it's me that added it. + + int tryCount = 2; + int ogModelId = modelId; + + while(tryCount != 0) { + modelId += wantedChange; + if (modelId < 0 || modelId >= MODELINFOSIZE) { + tryCount--; + wantedChange = -wantedChange; + } else if (modelId != 5 && modelId != 6 && modelId != 405) { + CBaseModelInfo *model = CModelInfo::GetModelInfo(modelId); + if (model) + { + //int type = model->m_type; + return modelId; + } + } + } + return ogModelId; +} + +void +PlayAnimation(RpClump *clump, AssocGroupId animGroup, AnimationId anim) +{ + CAnimBlendAssociation *currentAssoc = RpAnimBlendClumpGetAssociation(clump, anim); + + if (currentAssoc && currentAssoc->IsPartial()) + delete currentAssoc; + + RpAnimBlendClumpSetBlendDeltas(clump, ASSOC_PARTIAL, -8.0f); + + CAnimBlendAssociation *animAssoc = CAnimManager::BlendAnimation(clump, animGroup, anim, 8.0f); + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + animAssoc->SetCurrentTime(0.0f); + animAssoc->SetRun(); +} + +void +CAnimViewer::Update(void) +{ + static int modelId = 0; + static int animId = 0; + static bool reloadIFP = false; + + AssocGroupId animGroup = ASSOCGRP_STD; + int nextModelId = modelId; + CBaseModelInfo *modelInfo = CModelInfo::GetModelInfo(modelId); + + if (modelInfo->GetModelType() == MITYPE_PED) { + int animGroup = ((CPedModelInfo*)modelInfo)->m_animGroup; + + if (animId > ANIM_STD_IDLE) + animGroup = ASSOCGRP_STD; + + if (reloadIFP) { + if (pTarget) { + CWorld::Remove(pTarget); + if (pTarget) + delete pTarget; + } + pTarget = nil; + + // These calls were inside of LoadIFP function. + CAnimManager::Shutdown(); + CAnimManager::Initialise(); + CAnimManager::LoadAnimFiles(); + + reloadIFP = false; + } + } else { + animGroup = ASSOCGRP_STD; + } + CPad::UpdatePads(); + CPad* pad = CPad::GetPad(0); +#ifdef DEBUGMENU + DebugMenuProcess(); +#endif + + CStreaming::UpdateForAnimViewer(); + CStreaming::RequestModel(modelId, 0); + if (CStreaming::HasModelLoaded(modelId)) { + + if (!pTarget) { + + if (modelInfo->GetModelType() == MITYPE_VEHICLE) { + + CVehicleModelInfo* veh = (CVehicleModelInfo*)modelInfo; + if (veh->m_vehicleType == VEHICLE_TYPE_CAR) { + pTarget = new CAutomobile(modelId, RANDOM_VEHICLE); + } else if (veh->m_vehicleType == VEHICLE_TYPE_BOAT) { + pTarget = new CBoat(modelId, RANDOM_VEHICLE); + } else if (veh->m_vehicleType == VEHICLE_TYPE_BIKE) { + pTarget = new CBike(modelId, RANDOM_VEHICLE); + } else { + pTarget = new CObject(modelId, true); + if (!modelInfo->GetColModel()) { + modelInfo->SetColModel(&CTempColModels::ms_colModelWheel1); + } + } + pTarget->SetStatus(STATUS_ABANDONED); + } else if (modelInfo->GetModelType() == MITYPE_PED) { + pTarget = new CPed(PEDTYPE_CIVMALE); + pTarget->SetModelIndex(modelId); + } else { + pTarget = new CObject(modelId, true); + if (!modelInfo->GetColModel()) + { + modelInfo->SetColModel(&CTempColModels::ms_colModelWheel1); + } + pTarget->SetStatus(STATUS_ABANDONED); + } + pTarget->SetPosition(0.0f, 0.0f, 0.0f); + CWorld::Add(pTarget); + TheCamera.TakeControl(pTarget, CCam::MODE_MODELVIEW, JUMP_CUT, CAMCONTROL_SCRIPT); + } + if (pTarget->IsVehicle() || pTarget->IsPed() || pTarget->IsObject()) { + ((CPhysical*)pTarget)->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + } +#ifdef FIX_BUGS + // so we don't end up in the water + pTarget->GetMatrix().GetPosition().z = 10.0f; +#else + pTarget->GetMatrix().GetPosition().z = 0.0f; +#endif + + if (modelInfo->GetModelType() == MITYPE_PED) { + ((CPed*)pTarget)->bKindaStayInSamePlace = true; + + // Triangle in mobile + if (pad->GetSquareJustDown()) { + reloadIFP = true; + AsciiToUnicode("IFP reloaded", gUString); + CMessages::AddMessage(gUString, 1000, 0); + + } else if (pad->GetCrossJustDown()) { + PlayAnimation(pTarget->GetClump(), animGroup, (AnimationId)animId); + AsciiToUnicode("Animation restarted", gUString); + CMessages::AddMessage(gUString, 1000, 0); + + } else if (pad->GetCircleJustDown()) { + PlayAnimation(pTarget->GetClump(), animGroup, ANIM_STD_IDLE); + AsciiToUnicode("Idle animation playing", gUString); + CMessages::AddMessage(gUString, 1000, 0); + + } else if (pad->GetDPadUpJustDown()) { + animId--; + if (animId < 0) { + animId = ANIM_STD_NUM - 1; + } + PlayAnimation(pTarget->GetClump(), animGroup, (AnimationId)animId); + + sprintf(gString, "Current anim: %d", animId); + AsciiToUnicode(gString, gUString); + CMessages::AddMessage(gUString, 1000, 0); + + } else if (pad->GetDPadDownJustDown()) { + animId = (animId == (ANIM_STD_NUM - 1) ? 0 : animId + 1); + PlayAnimation(pTarget->GetClump(), animGroup, (AnimationId)animId); + + sprintf(gString, "Current anim: %d", animId); + AsciiToUnicode(gString, gUString); + CMessages::AddMessage(gUString, 1000, 0); + + } else if (pad->GetStartJustDown()) { + + } else if (pad->GetLeftShoulder1JustDown()) { + nextModelId = FirstCarModelId(modelId); + AsciiToUnicode("Switched to vehicles", gUString); + CMessages::AddMessage(gUString, 1000, 0); + // Originally it was GetPad(1)->LeftShoulder2 + } else if (pad->NewState.Triangle) { + ((CPedModelInfo *)CModelInfo::GetModelInfo(pTarget->GetModelIndex()))->AnimatePedColModelSkinned(pTarget->GetClump()); + AsciiToUnicode("Ped Col model will be animated as long as you hold the button", gUString); + CMessages::AddMessage(gUString, 100, 0); + } + + // From LCS + if (CAnimManager::GetAnimAssocGroups()[animGroup].numAssociations <= animId) + animId = 0; + + } else if (modelInfo->GetModelType() == MITYPE_VEHICLE) { + + if (pad->GetLeftShoulder1JustDown()) { + nextModelId = LastPedModelId(modelId); + AsciiToUnicode("Switched to peds", gUString); + CMessages::AddMessage(gUString, 1000, 0); + // Start in mobile + } else if (pad->GetSquareJustDown()) { + CVehicleModelInfo::LoadVehicleColours(); + AsciiToUnicode("Carcols.dat reloaded", gUString); + CMessages::AddMessage(gUString, 1000, 0); + } + } + } + + if (pad->GetDPadLeftJustDown()) { + nextModelId = NextModelId(modelId, -1); + + sprintf(gString, "Current model ID: %d", nextModelId); + AsciiToUnicode(gString, gUString); + CMessages::AddMessage(gUString, 1000, 0); + + } else if (pad->GetDPadRightJustDown()) { + nextModelId = NextModelId(modelId, 1); + + sprintf(gString, "Current model ID: %d", nextModelId); + AsciiToUnicode(gString, gUString); + CMessages::AddMessage(gUString, 1000, 0); + } + // There were extra codes here to let us change model id by 50, but xbox CPad struct is different, so I couldn't port. + + if (nextModelId != modelId) { + modelId = nextModelId; + if (pTarget) { + CWorld::Remove(pTarget); + if (pTarget) + delete pTarget; + } + pTarget = nil; + return; + } + + CTimeCycle::Update(); + CWorld::Process(); + if (pTarget) + TheCamera.Process(); +} + +void +CAnimViewer::Shutdown(void) +{ + if (CWorld::Players[0].m_pPed) + delete CWorld::Players[0].m_pPed; + + CWorld::ShutDown(); + CModelInfo::ShutDown(); + CAnimManager::Shutdown(); + CTimer::Shutdown(); + CStreaming::Shutdown(); + CTxdStore::RemoveTxdSlot(animTxdSlot); +} diff --git a/src/miami/core/AnimViewer.h b/src/miami/core/AnimViewer.h new file mode 100644 index 00000000..13dbb8fb --- /dev/null +++ b/src/miami/core/AnimViewer.h @@ -0,0 +1,12 @@ +#pragma once + +class CAnimViewer { +public: + static int animTxdSlot; + static CEntity *pTarget; + + static void Initialise(); + static void Render(); + static void Shutdown(); + static void Update(); +}; \ No newline at end of file diff --git a/src/miami/core/Cam.cpp b/src/miami/core/Cam.cpp new file mode 100644 index 00000000..cd6e38f4 --- /dev/null +++ b/src/miami/core/Cam.cpp @@ -0,0 +1,5422 @@ +#include "common.h" + +#include "main.h" +#include "Draw.h" +#include "World.h" +#include "Vehicle.h" +#include "Automobile.h" +#include "Boat.h" +#include "Bones.h" +#include "Ped.h" +#include "PlayerPed.h" +#include "CopPed.h" +#include "RpAnimBlend.h" +#include "ControllerConfig.h" +#include "Pad.h" +#include "Frontend.h" +#include "General.h" +#include "Timecycle.h" +#include "Renderer.h" +#include "Shadows.h" +#include "Hud.h" +#include "ZoneCull.h" +#include "SurfaceTable.h" +#include "WaterLevel.h" +#include "MBlur.h" +#include "SceneEdit.h" +#include "Debug.h" +#include "Camera.h" +#include "DMAudio.h" +#include "Bike.h" +#include "Pickups.h" + +bool PrintDebugCode = false; +int16 DebugCamMode; + +extern float fRangePlayerRadius; +extern float fCloseNearClipLimit; + +#ifdef FREE_CAM +bool CCamera::bFreeCam = false; +int nPreviousMode = -1; +#endif + +void +CCam::Init(void) +{ + Mode = MODE_FOLLOWPED; + Front = CVector(0.0f, 0.0f, -1.0f); + Up = CVector(0.0f, 0.0f, 1.0f); + Rotating = false; + m_iDoCollisionChecksOnFrameNum = 1; + m_iDoCollisionCheckEveryNumOfFrames = 9; + m_iFrameNumWereAt = 0; + m_bCollisionChecksOn = false; + m_fRealGroundDist = 0.0f; + BetaSpeed = 0.0f; + AlphaSpeed = 0.0f; + DistanceSpeed = 0.0f; + f_max_role_angle = DEGTORAD(5.0f); + Distance = 30.0f; + DistanceSpeed = 0.0f; + m_pLastCarEntered = nil; + m_pLastPedLookedAt = nil; + ResetStatics = true; + Beta = 0.0f; + m_fTilt = 0.0f; + m_fTiltSpeed = 0.0f; + m_bFixingBeta = false; + CA_MIN_DISTANCE = 0.0f; + CA_MAX_DISTANCE = 0.0f; + LookingBehind = false; + LookingLeft = false; + LookingRight = false; + m_fPlayerInFrontSyphonAngleOffSet = DEGTORAD(20.0f); + m_fSyphonModeTargetZOffSet = 0.5f; + m_fRadiusForDead = 1.5f; + DirectionWasLooking = LOOKING_FORWARD; + LookBehindCamWasInFront = false; + f_Roll = 0.0f; + f_rollSpeed = 0.0f; + m_fCloseInPedHeightOffset = 0.0f; + m_fCloseInPedHeightOffsetSpeed = 0.0f; + m_fCloseInCarHeightOffset = 0.0f; + m_fCloseInCarHeightOffsetSpeed = 0.0f; + m_fPedBetweenCameraHeightOffset = 0.0f; + m_fTargetBeta = 0.0f; + m_fBufferedTargetBeta = 0.0f; + m_fBufferedTargetOrientation = 0.0f; + m_fBufferedTargetOrientationSpeed = 0.0f; + m_fDimensionOfHighestNearCar = 0.0f; +} + +float PLAYERPED_LEVEL_SMOOTHING_CONST_INV = 0.6f; +float PLAYERPED_TREND_SMOOTHING_CONST_INV = 0.8f; + +void +CCam::Process(void) +{ + CVector CameraTarget; + float TargetSpeedVar = 0.0f; + float TargetOrientation = 0.0f; + + static CVector SmoothedPos(0.0f, 0.0f, 10000.0f); + static CVector SmoothedSpeed(0.0f, 0.0f, 0.0f); + + if(CamTargetEntity == nil) + CamTargetEntity = TheCamera.pTargetEntity; + + m_iFrameNumWereAt++; + if(m_iFrameNumWereAt > m_iDoCollisionCheckEveryNumOfFrames) + m_iFrameNumWereAt = 1; + m_bCollisionChecksOn = m_iFrameNumWereAt == m_iDoCollisionChecksOnFrameNum; + + if(m_bCamLookingAtVector){ + CameraTarget = m_cvecCamFixedModeVector; + }else if(CamTargetEntity->IsVehicle()){ + CameraTarget = CamTargetEntity->GetPosition(); + + if(CamTargetEntity->GetForward().x == 0.0f && CamTargetEntity->GetForward().y == 0.0f) + TargetOrientation = 0.0f; + else + TargetOrientation = CGeneral::GetATanOfXY(CamTargetEntity->GetForward().x, CamTargetEntity->GetForward().y); + + CVector Fwd(0.0f, 0.0f, 0.0f); + Fwd.x = CamTargetEntity->GetForward().x; + Fwd.y = CamTargetEntity->GetForward().y; + Fwd.Normalise(); + float FwdLength = Fwd.Magnitude2D(); + if(FwdLength != 0.0f){ + Fwd.x /= FwdLength; + Fwd.y /= FwdLength; + } + + float FwdSpeedX = ((CVehicle*)CamTargetEntity)->GetMoveSpeed().x * Fwd.x; + float FwdSpeedY = ((CVehicle*)CamTargetEntity)->GetMoveSpeed().y * Fwd.y; + if(FwdSpeedX + FwdSpeedY > 0.0f) + TargetSpeedVar = Min(Sqrt(SQR(FwdSpeedX) + SQR(FwdSpeedY))/0.9f, 1.0f); + else + TargetSpeedVar = -Min(Sqrt(SQR(FwdSpeedX) + SQR(FwdSpeedY))/1.8f, 0.5f); + SpeedVar = 0.895f*SpeedVar + 0.105*TargetSpeedVar; + }else{ + if(CamTargetEntity == FindPlayerPed()){ + // Some fancy smoothing of player position and speed + float LevelSmoothing = 1.0f - Pow(PLAYERPED_LEVEL_SMOOTHING_CONST_INV, CTimer::GetTimeStep()); + float TrendSmoothing = 1.0f - Pow(PLAYERPED_TREND_SMOOTHING_CONST_INV, CTimer::GetTimeStep()); + + CVector NewSmoothedPos, NewSmoothedSpeed; + if((SmoothedPos - CamTargetEntity->GetPosition()).MagnitudeSqr() > SQR(3.0f) || + CTimer::GetTimeStep() < 0.2f || Using3rdPersonMouseCam()){ + // Reset values + NewSmoothedPos = CamTargetEntity->GetPosition(); + NewSmoothedSpeed = CVector(0.0f, 0.0f, 0.0f); + }else{ + NewSmoothedPos = LevelSmoothing*CamTargetEntity->GetPosition() + (1.0f-LevelSmoothing)*(SmoothedPos + SmoothedSpeed*CTimer::GetTimeStep()); + NewSmoothedSpeed = TrendSmoothing*(NewSmoothedPos-SmoothedPos)/CTimer::GetTimeStep() + (1.0f-TrendSmoothing)*SmoothedSpeed; + } + + CameraTarget = NewSmoothedPos; + SmoothedPos = NewSmoothedPos; + SmoothedSpeed = NewSmoothedSpeed; + }else + CameraTarget = CamTargetEntity->GetPosition(); + + if(CamTargetEntity->GetForward().x == 0.0f && CamTargetEntity->GetForward().y == 0.0f) + TargetOrientation = 0.0f; + else + TargetOrientation = CGeneral::GetATanOfXY(CamTargetEntity->GetForward().x, CamTargetEntity->GetForward().y); + TargetSpeedVar = 0.0f; + SpeedVar = 0.0f; + } + + switch(Mode){ + case MODE_TOPDOWN: + case MODE_GTACLASSIC: + // Process_TopDown(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; + case MODE_BEHINDCAR: + Process_BehindCar(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; + case MODE_FOLLOWPED: +#ifdef PC_PLAYER_CONTROLS + if(CCamera::m_bUseMouse3rdPerson) + Process_FollowPedWithMouse(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + else +#endif +#ifdef FREE_CAM + if(CCamera::bFreeCam) + Process_FollowPed_Rotation(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + else +#endif + Process_FollowPed(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; +// case MODE_AIMING: + case MODE_DEBUG: + Process_Debug(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; + case MODE_SNIPER: + case MODE_CAMERA: + Process_Sniper(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; + case MODE_ROCKETLAUNCHER: + Process_Rocket(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; + case MODE_MODELVIEW: + Process_ModelView(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; +// case MODE_BILL: + case MODE_SYPHON: + Process_Syphon(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; + case MODE_CIRCLE: +// Process_Circle(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; +// case MODE_CHEESYZOOM: + case MODE_WHEELCAM: + Process_WheelCam(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; + case MODE_FIXED: + Process_Fixed(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; + case MODE_1STPERSON: + Process_1stPerson(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; + case MODE_FLYBY: + Process_FlyBy(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; + case MODE_CAM_ON_A_STRING: +#ifdef FREE_CAM + if(CCamera::bFreeCam && !CVehicle::bCheat5) + Process_FollowCar_SA(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + else +#endif + Process_Cam_On_A_String(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; +// case MODE_REACTION: +// case MODE_FOLLOW_PED_WITH_BIND: +// case MODE_CHRIS: + case MODE_BEHINDBOAT: +#ifdef FREE_CAM + if (CCamera::bFreeCam) + Process_FollowCar_SA(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + else +#endif + Process_BehindBoat(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; + case MODE_PLAYER_FALLEN_WATER: + Process_Player_Fallen_Water(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; +// case MODE_CAM_ON_TRAIN_ROOF: +// case MODE_CAM_RUNNING_SIDE_TRAIN: +// case MODE_BLOOD_ON_THE_TRACKS: +// case MODE_IM_THE_PASSENGER_WOOWOO: + case MODE_SYPHON_CRIM_IN_FRONT: + Process_Syphon_Crim_In_Front(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; + case MODE_PED_DEAD_BABY: + ProcessPedsDeadBaby(); + break; +// case MODE_PILLOWS_PAPS: +// case MODE_LOOK_AT_CARS: + case MODE_ARRESTCAM_ONE: + ProcessArrestCamOne(); + break; + case MODE_ARRESTCAM_TWO: + ProcessArrestCamTwo(); + break; + case MODE_M16_1STPERSON: + case MODE_HELICANNON_1STPERSON: + Process_M16_1stPerson(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; + case MODE_SPECIAL_FIXED_FOR_SYPHON: + Process_SpecialFixedForSyphon(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; + case MODE_FIGHT_CAM: + Process_Fight_Cam(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; + case MODE_LIGHTHOUSE: + Process_LightHouse(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; + case MODE_TOP_DOWN_PED: + // Process_TopDownPed(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; + case MODE_SNIPER_RUNABOUT: + case MODE_ROCKETLAUNCHER_RUNABOUT: + case MODE_1STPERSON_RUNABOUT: + case MODE_M16_1STPERSON_RUNABOUT: + case MODE_FIGHT_CAM_RUNABOUT: + Process_1rstPersonPedOnPC(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; +#ifdef GTA_SCENE_EDIT + case MODE_EDITOR: + Process_Editor(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); + break; +#endif + default: + Source = CVector(0.0f, 0.0f, 0.0f); + Front = CVector(0.0f, 1.0f, 0.0f); + Up = CVector(0.0f, 0.0f, 1.0f); + } + +#ifdef FREE_CAM + nPreviousMode = Mode; +#endif + CVector TargetToCam = Source - m_cvecTargetCoorsForFudgeInter; + float DistOnGround = TargetToCam.Magnitude2D(); + m_fTrueBeta = CGeneral::GetATanOfXY(TargetToCam.x, TargetToCam.y); + m_fTrueAlpha = CGeneral::GetATanOfXY(DistOnGround, TargetToCam.z); + if(TheCamera.m_uiTransitionState == 0) + KeepTrackOfTheSpeed(Source, m_cvecTargetCoorsForFudgeInter, Up, m_fTrueAlpha, m_fTrueBeta, FOV); + + // Look Behind, Left, Right + LookingBehind = false; + LookingLeft = false; + LookingRight = false; + SourceBeforeLookBehind = Source; + if(&TheCamera.Cams[TheCamera.ActiveCam] == this){ + if((Mode == MODE_CAM_ON_A_STRING || Mode == MODE_1STPERSON || Mode == MODE_BEHINDBOAT || Mode == MODE_BEHINDCAR) && + CamTargetEntity->IsVehicle()){ + bool bDisableLR = CamTargetEntity && + (((CVehicle*)CamTargetEntity)->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI || CamTargetEntity->GetModelIndex() == MI_RCBARON); + if(CPad::GetPad(0)->GetLookBehindForCar()){ + LookBehind(); + if(DirectionWasLooking != LOOKING_BEHIND) + TheCamera.m_bJust_Switched = true; + DirectionWasLooking = LOOKING_BEHIND; + }else if(bDisableLR){ + if(DirectionWasLooking != LOOKING_FORWARD) + TheCamera.m_bJust_Switched = true; + DirectionWasLooking = LOOKING_FORWARD; + }else if(CPad::GetPad(0)->GetLookLeft()){ + LookLeft(); + if(DirectionWasLooking != LOOKING_LEFT) + TheCamera.m_bJust_Switched = true; + DirectionWasLooking = LOOKING_LEFT; + }else if(CPad::GetPad(0)->GetLookRight()){ + LookRight(); + if(DirectionWasLooking != LOOKING_RIGHT) + TheCamera.m_bJust_Switched = true; + DirectionWasLooking = LOOKING_RIGHT; + }else{ + if(DirectionWasLooking != LOOKING_FORWARD) + TheCamera.m_bJust_Switched = true; + DirectionWasLooking = LOOKING_FORWARD; + } + } + if(Mode == MODE_FOLLOWPED && CamTargetEntity->IsPed()){ + if(CPad::GetPad(0)->GetLookBehindForPed()){ + LookBehind(); + if(DirectionWasLooking != LOOKING_BEHIND) + TheCamera.m_bJust_Switched = true; + DirectionWasLooking = LOOKING_BEHIND; + }else + DirectionWasLooking = LOOKING_FORWARD; + } + } + + if(Mode == MODE_SNIPER || Mode == MODE_ROCKETLAUNCHER || Mode == MODE_M16_1STPERSON || + Mode == MODE_1STPERSON || Mode == MODE_HELICANNON_1STPERSON || Mode == MODE_CAMERA || GetWeaponFirstPersonOn()) + ClipIfPedInFrontOfPlayer(); +} + +// MaxSpeed is a limit of how fast the value is allowed to change. 1.0 = to Target in up to 1ms +// Acceleration is how fast the speed will change to MaxSpeed. 1.0 = to MaxSpeed in 1ms +void +WellBufferMe(float Target, float *CurrentValue, float *CurrentSpeed, float MaxSpeed, float Acceleration, bool IsAngle) +{ + float Delta = Target - *CurrentValue; + + if(IsAngle){ + while(Delta >= PI) Delta -= 2*PI; + while(Delta < -PI) Delta += 2*PI; + } + + float TargetSpeed = Delta * MaxSpeed; + // Add or subtract absolute depending on sign, genius! +// if(TargetSpeed - *CurrentSpeed > 0.0f) +// *CurrentSpeed += Acceleration * Abs(TargetSpeed - *CurrentSpeed) * CTimer::GetTimeStep(); +// else +// *CurrentSpeed -= Acceleration * Abs(TargetSpeed - *CurrentSpeed) * CTimer::GetTimeStep(); + // this is simpler: + *CurrentSpeed += Acceleration * (TargetSpeed - *CurrentSpeed) * CTimer::GetTimeStep(); + + // Clamp speed if we overshot + if(TargetSpeed < 0.0f && *CurrentSpeed < TargetSpeed) + *CurrentSpeed = TargetSpeed; + else if(TargetSpeed > 0.0f && *CurrentSpeed > TargetSpeed) + *CurrentSpeed = TargetSpeed; + + *CurrentValue += *CurrentSpeed * Min(10.0f, CTimer::GetTimeStep()); +} + +void +MakeAngleLessThan180(float &Angle) +{ + while(Angle >= PI) Angle -= 2*PI; + while(Angle < -PI) Angle += 2*PI; +} + +void +CCam::ProcessSpecialHeightRoutines(void) +{ + int i; + bool StandingOnBoat = false; + static bool PreviouslyFailedRoadHeightCheck = false; + CVector CamToTarget, CamToPed; + float DistOnGround, BetaAngle; + CPed *Player; + float PedZDist; + CColPoint colPoint; + + CamToTarget = TheCamera.pTargetEntity->GetPosition() - TheCamera.GetGameCamPosition(); + DistOnGround = CamToTarget.Magnitude2D(); + BetaAngle = CGeneral::GetATanOfXY(CamToTarget.x, CamToTarget.y); + m_bTheHeightFixerVehicleIsATrain = false; + // CGeneral::GetATanOfXY(TheCamera.GetForward().x, TheCamera.GetForward().y); + Player = CWorld::Players[CWorld::PlayerInFocus].m_pPed; + + if(DistOnGround > 10.0f) + DistOnGround = 10.0f; + + if(CamTargetEntity && CamTargetEntity->IsPed()){ + if(FindPlayerPed()->m_pCurSurface && FindPlayerPed()->m_pCurSurface->IsVehicle() && + ((CVehicle*)FindPlayerPed()->m_pCurSurface)->IsBoat()) + StandingOnBoat = true; + + float FoundPedZ = -100.0f; + + // Move up the camera if there is a ped close to it + if(Mode == MODE_FOLLOWPED || Mode == MODE_FIGHT_CAM || Mode == MODE_PILLOWS_PAPS){ + // Find highest ped close to camera + for(i = 0; i < Player->m_numNearPeds; i++){ + CPed *nearPed = Player->m_nearPeds[i]; + if(nearPed && nearPed->GetPedState() != PED_DEAD){ + CamToPed = nearPed->GetPosition() - TheCamera.GetGameCamPosition(); + if(Abs(CamToPed.z) < 1.0f){ + float DistSq = CamToPed.MagnitudeSqr(); + if(DistSq < SQR(2.1f)){ + if(nearPed->GetPosition().z > FoundPedZ) + FoundPedZ = nearPed->GetPosition().z; + }else{ + float Dist = Sqrt(DistSq); + CamToPed /= Dist; + // strange calculation + CVector PlayerCamSpeed = DotProduct(Front, Player->m_vecMoveSpeed)*Front; + float SpeedDiff = DotProduct(PlayerCamSpeed - nearPed->m_vecMoveSpeed, CamToPed); + if(SpeedDiff > 0.01f && + (m_fPedBetweenCameraHeightOffset > 0.0f && (Dist-2.1f)/SpeedDiff < 75.0f || + m_fPedBetweenCameraHeightOffset <= 0.0f && (Dist-2.1f)/SpeedDiff < 75.0f * 0.1f)) + if(nearPed->GetPosition().z > FoundPedZ) + FoundPedZ = nearPed->GetPosition().z; + } + } + } + } + + if(FoundPedZ > -99.0f){ + float Offset = 0.0f; + PedZDist = 0.0f; + if(FoundPedZ > Player->GetPosition().z) + PedZDist = FoundPedZ - Player->GetPosition().z; + + if(Mode == MODE_FOLLOWPED){ + if(TheCamera.PedZoomIndicator == CAM_ZOOM_1 && + ((CPed*)CamTargetEntity)->GetPedState() != PED_ENTER_CAR && + ((CPed*)CamTargetEntity)->GetPedState() != PED_CARJACK) + Offset = 0.45f + PedZDist; + // BUG: overrides this ^ case + if(TheCamera.PedZoomIndicator == CAM_ZOOM_2 || TheCamera.PedZoomIndicator == CAM_ZOOM_1) + Offset = 0.35f + PedZDist; + if(TheCamera.PedZoomIndicator == CAM_ZOOM_3) + Offset = 0.25f + PedZDist; + m_fPedBetweenCameraHeightOffset = Offset + 1.3f; + }else if(Mode == MODE_FIGHT_CAM) + m_fPedBetweenCameraHeightOffset = PedZDist + 1.3f + 0.5f; + else if(Mode == MODE_PILLOWS_PAPS) + m_fPedBetweenCameraHeightOffset = PedZDist + 1.3f + 0.45f; + }else{ + m_fPedBetweenCameraHeightOffset = 0.0f; + } + } + + + // Move camera up for vehicles in the way + if(m_bCollisionChecksOn && (Mode == MODE_FOLLOWPED || Mode == MODE_FIGHT_CAM)){ + bool FoundCar = false; + CEntity *vehicle = nil; + float TestDist = DistOnGround + 1.25f; + float HighestCar = 0.0f; + if(m_fDimensionOfHighestNearCar > 0.0f) + TestDist += 0.3f; + CVector TestBase = CamTargetEntity->GetPosition(); + CVector TestPoint; + TestBase.z -= 0.15f; + + TestPoint = TestBase - TestDist * CVector(Cos(BetaAngle), Sin(BetaAngle), 0.0f); + if(CWorld::ProcessLineOfSight(CamTargetEntity->GetPosition(), TestPoint, colPoint, vehicle, false, true, false, false, false, false) && + vehicle->IsVehicle()){ + float height = vehicle->GetColModel()->boundingBox.GetSize().z; + FoundCar = true; + HighestCar = height; + if(((CVehicle*)vehicle)->IsTrain()) + m_bTheHeightFixerVehicleIsATrain = true; + } + + TestPoint = TestBase - TestDist * CVector(Cos(BetaAngle+DEGTORAD(28.0f)), Sin(BetaAngle+DEGTORAD(28.0f)), 0.0f); + if(CWorld::ProcessLineOfSight(CamTargetEntity->GetPosition(), TestPoint, colPoint, vehicle, false, true, false, false, false, false) && + vehicle->IsVehicle()){ + float height = vehicle->GetColModel()->boundingBox.GetSize().z; + if(FoundCar){ + HighestCar = Max(HighestCar, height); + }else{ + FoundCar = true; + HighestCar = height; + } + if(((CVehicle*)vehicle)->IsTrain()) + m_bTheHeightFixerVehicleIsATrain = true; + } + + TestPoint = TestBase - TestDist * CVector(Cos(BetaAngle-DEGTORAD(28.0f)), Sin(BetaAngle-DEGTORAD(28.0f)), 0.0f); + if(CWorld::ProcessLineOfSight(CamTargetEntity->GetPosition(), TestPoint, colPoint, vehicle, false, true, false, false, false, false) && + vehicle->IsVehicle()){ + float height = vehicle->GetColModel()->boundingBox.GetSize().z; + if(FoundCar){ + HighestCar = Max(HighestCar, height); + }else{ + FoundCar = true; + HighestCar = height; + } + if(((CVehicle*)vehicle)->IsTrain()) + m_bTheHeightFixerVehicleIsATrain = true; + } + + if(FoundCar){ + m_fDimensionOfHighestNearCar = HighestCar + 0.1f; + if(Mode == MODE_FIGHT_CAM) + m_fDimensionOfHighestNearCar += 0.75f; + }else + m_fDimensionOfHighestNearCar = 0.0f; + } + } + + if(StandingOnBoat){ + m_fDimensionOfHighestNearCar = 1.0f; + m_fPedBetweenCameraHeightOffset = 0.0f; + } +} + +void +CCam::GetVectorsReadyForRW(void) +{ + CVector right; + Up = CVector(0.0f, 0.0f, 1.0f); + Front.Normalise(); + if(Front.x == 0.0f && Front.y == 0.0f){ + Front.x = 0.0001f; + Front.y = 0.0001f; + } + right = CrossProduct(Front, Up); + right.Normalise(); + Up = CrossProduct(right, Front); +} + +bool +CCam::GetBoatLook_L_R_HeightOffset(float &Offset) +{ + if(CamTargetEntity == nil) + return false; + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(CamTargetEntity->GetModelIndex()); + tBoatHandlingData *handling = mod_HandlingManager.GetBoatPointer(mi->m_handlingId); + if(handling){ + Offset = handling->fLook_L_R_BehindCamHeight; + return true; + } + return false; // can't happen, we always get a boat pointer back +} + +void +CCam::LookBehind(void) +{ + float Dist, DeltaBeta, TargetOrientation, Angle; + CVector TargetCoors, TargetFwd, TestCoors; + + TargetCoors = CamTargetEntity->GetPosition(); + Front = CamTargetEntity->GetPosition() - Source; + + if((Mode == MODE_CAM_ON_A_STRING || Mode == MODE_BEHINDBOAT || Mode == MODE_BEHINDCAR) && CamTargetEntity->IsVehicle()){ + LookingBehind = true; + Dist = Mode == MODE_CAM_ON_A_STRING ? CA_MAX_DISTANCE : 15.5f; + TargetFwd = CamTargetEntity->GetForward(); + TargetFwd.Normalise(); + TargetOrientation = CGeneral::GetATanOfXY(TargetFwd.x, TargetFwd.y); + DeltaBeta = TargetOrientation - Beta; + while(DeltaBeta >= PI) DeltaBeta -= 2*PI; + while(DeltaBeta < -PI) DeltaBeta += 2*PI; + if(DirectionWasLooking != LOOKING_BEHIND) + LookBehindCamWasInFront = DeltaBeta <= -HALFPI || DeltaBeta >= HALFPI; + if(LookBehindCamWasInFront) + TargetOrientation += PI; + Source.x = Dist*Cos(TargetOrientation) + TargetCoors.x; + Source.y = Dist*Sin(TargetOrientation) + TargetCoors.y; + CVector OrigSource = Source; + TheCamera.AvoidTheGeometry(OrigSource, TargetCoors, Source, FOV); + Front = CamTargetEntity->GetPosition() - Source; + GetVectorsReadyForRW(); + } + if(Mode == MODE_1STPERSON && CamTargetEntity->IsVehicle()){ + LookingBehind = true; + RwCameraSetNearClipPlane(Scene.camera, 0.25f); + Front = CamTargetEntity->GetForward(); + Front.Normalise(); + if(((CVehicle*)CamTargetEntity)->IsBoat()) + Source.z -= 0.5f; + if(((CVehicle*)CamTargetEntity)->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE){ + float FrontDist = 1.1f; + if(((CVehicle*)CamTargetEntity)->pDriver){ + CVector ExtraFwd(0.0f, 0.0f, 0.0f); + ((CVehicle*)CamTargetEntity)->pDriver->m_pedIK.GetComponentPosition(ExtraFwd, PED_HEAD); + ExtraFwd += ((CVehicle*)CamTargetEntity)->m_vecMoveSpeed*CTimer::GetTimeStep() - CamTargetEntity->GetPosition(); + FrontDist += 0.2f + Max(DotProduct(ExtraFwd, CamTargetEntity->GetForward()), 0.0f); + } + Source += FrontDist*Front; + Front = -Front; + }else if(((CVehicle*)CamTargetEntity)->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI){ + Front = -1.0f*CamTargetEntity->GetUp(); + Up = CamTargetEntity->GetForward(); + Source += 0.25f*Front; + }else{ + Source += 0.25f*Front; + Front = -Front; + } + } + if(CamTargetEntity->IsPed()){ + Angle = CGeneral::GetATanOfXY(Source.x - TargetCoors.x, Source.y - TargetCoors.y) + PI; + Source.x = 4.5f*Cos(Angle) + TargetCoors.x; + Source.y = 4.5f*Sin(Angle) + TargetCoors.y; + Source.z = 1.15f + TargetCoors.z; + CVector OrigSource = Source; + TheCamera.AvoidTheGeometry(OrigSource, TargetCoors, Source, FOV); + Front = TargetCoors - Source; + GetVectorsReadyForRW(); + } +} + +float BOAT_1STPERSON_L_OFFSETX = 0.7f; +float BOAT_1STPERSON_R_OFFSETX = 0.3f; +float BOAT_1STPERSON_LR_OFFSETZ = 0.2f; + +void +CCam::LookLeft(void) +{ + float Dist, TargetOrientation; + CVector TargetCoors, TargetFwd; + + if((Mode == MODE_CAM_ON_A_STRING || Mode == MODE_BEHINDBOAT || Mode == MODE_BEHINDCAR) && CamTargetEntity->IsVehicle()){ + LookingLeft = true; + TargetCoors = CamTargetEntity->GetPosition(); + Front = CamTargetEntity->GetPosition() - Source; + if(Mode == MODE_CAM_ON_A_STRING) + Dist = CA_MAX_DISTANCE; + else if(Mode == MODE_BEHINDBOAT){ + Dist = 9.0f; + float Offset = 0.0f; + if(GetBoatLook_L_R_HeightOffset(Offset) && !CCullZones::Cam1stPersonForPlayer()) + Source.z = TargetCoors.z + Offset; + }else + Dist = 9.0f; + TargetFwd = CamTargetEntity->GetForward(); + TargetFwd.Normalise(); + TargetOrientation = CGeneral::GetATanOfXY(TargetFwd.x, TargetFwd.y); + Source.x = Dist*Cos(TargetOrientation - HALFPI) + TargetCoors.x; + Source.y = Dist*Sin(TargetOrientation - HALFPI) + TargetCoors.y; + + CColModel *colModel = CamTargetEntity->GetColModel(); + CVector OrigSource = Source; + TheCamera.AvoidTheGeometry(OrigSource, TargetCoors, Source, FOV); + + CVector TopRight = CamTargetEntity->GetPosition() + + CamTargetEntity->GetRight()*colModel->boundingBox.max.x + + CamTargetEntity->GetUp()*colModel->boundingBox.max.z; + float Height = Min(Max(m_cvecTargetCoorsForFudgeInter.z, TopRight.z)+0.1f, OrigSource.z); + Source.z = Max(Height, Source.z); + + Front = CamTargetEntity->GetPosition() - Source; + Front.z += 1.1f; + if(Mode == MODE_BEHINDBOAT) + Front.z += 1.2f; + GetVectorsReadyForRW(); + } + if(Mode == MODE_1STPERSON && CamTargetEntity->IsVehicle()){ + LookingLeft = true; + RwCameraSetNearClipPlane(Scene.camera, 0.25f); + if(((CVehicle*)CamTargetEntity)->IsBoat()){ + if(((CVehicle*)CamTargetEntity)->pDriver){ + CVector neck(0.0f, 0.0f, 0.0f); + CPed *driver = ((CVehicle*)CamTargetEntity)->pDriver; + driver->SetPedPositionInCar(); + driver->GetMatrix().UpdateRW(); + driver->UpdateRwFrame(); + driver->UpdateRpHAnim(); + driver->m_pedIK.GetComponentPosition(neck, PED_NECK); + Source = neck + + BOAT_1STPERSON_L_OFFSETX*CamTargetEntity->GetRight() + + BOAT_1STPERSON_LR_OFFSETZ*CamTargetEntity->GetUp(); + }else + Source.z -= 0.5f; + } + + Up = CamTargetEntity->GetUp(); + Up.Normalise(); + Front = CamTargetEntity->GetForward(); + Front.Normalise(); + Front = -CrossProduct(Front, Up); + Front.Normalise(); + if(((CVehicle*)CamTargetEntity)->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE) + Source -= 1.45f*Front; + } +} + +void +CCam::LookRight(void) +{ + float Dist, TargetOrientation; + CVector TargetCoors, TargetFwd; + CColPoint colPoint; + + if((Mode == MODE_CAM_ON_A_STRING || Mode == MODE_BEHINDBOAT || Mode == MODE_BEHINDCAR) && CamTargetEntity->IsVehicle()){ + LookingRight = true; + TargetCoors = CamTargetEntity->GetPosition(); + Front = CamTargetEntity->GetPosition() - Source; + if(Mode == MODE_CAM_ON_A_STRING) + Dist = CA_MAX_DISTANCE; + else if(Mode == MODE_BEHINDBOAT){ + Dist = 9.0f; + float Offset = 0.0f; + if(GetBoatLook_L_R_HeightOffset(Offset) && !CCullZones::Cam1stPersonForPlayer()) + Source.z = TargetCoors.z + Offset; + }else + Dist = 9.0f; + TargetFwd = CamTargetEntity->GetForward(); + TargetFwd.Normalise(); + TargetOrientation = CGeneral::GetATanOfXY(TargetFwd.x, TargetFwd.y); + Source.x = Dist*Cos(TargetOrientation + HALFPI) + TargetCoors.x; + Source.y = Dist*Sin(TargetOrientation + HALFPI) + TargetCoors.y; + + CColModel *colModel = CamTargetEntity->GetColModel(); + CVector OrigSource = Source; + TheCamera.AvoidTheGeometry(OrigSource, TargetCoors, Source, FOV); + + CVector TopLeft = CamTargetEntity->GetPosition() + + CamTargetEntity->GetRight()*colModel->boundingBox.min.x + + CamTargetEntity->GetUp()*colModel->boundingBox.max.z; + float Height = Min(Max(m_cvecTargetCoorsForFudgeInter.z, TopLeft.z)+0.1f, OrigSource.z); + Source.z = Max(Height, Source.z); + + Front = CamTargetEntity->GetPosition() - Source; + Front.z += 1.1f; + if(Mode == MODE_BEHINDBOAT) + Front.z += 1.2f; + GetVectorsReadyForRW(); + } + if(Mode == MODE_1STPERSON && CamTargetEntity->IsVehicle()){ + LookingRight = true; + RwCameraSetNearClipPlane(Scene.camera, 0.25f); + if(((CVehicle*)CamTargetEntity)->IsBoat()){ + if(((CVehicle*)CamTargetEntity)->pDriver){ + CVector neck(0.0f, 0.0f, 0.0f); + CPed *driver = ((CVehicle*)CamTargetEntity)->pDriver; + driver->SetPedPositionInCar(); + driver->GetMatrix().UpdateRW(); + driver->UpdateRwFrame(); + driver->UpdateRpHAnim(); + driver->m_pedIK.GetComponentPosition(neck, PED_NECK); + Source = neck + + BOAT_1STPERSON_R_OFFSETX*CamTargetEntity->GetRight() + + BOAT_1STPERSON_LR_OFFSETZ*CamTargetEntity->GetUp(); + }else + Source.z -= 0.5f; + } + + Up = CamTargetEntity->GetUp(); + Up.Normalise(); + Front = CamTargetEntity->GetForward(); + Front.Normalise(); + Front = CrossProduct(Front, Up); + Front.Normalise(); + if(((CVehicle*)CamTargetEntity)->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE) + Source -= 1.45f*Front; + } +} + +void +CCam::ClipIfPedInFrontOfPlayer(void) +{ + float FwdAngle, PedAngle, DeltaAngle, fDist, Near; + CVector vDist; + CPed *Player; + bool found = false; + int ped = 0; + + // unused: TheCamera.pTargetEntity->GetPosition() - TheCamera.GetGameCamPosition(); + + FwdAngle = CGeneral::GetATanOfXY(TheCamera.GetForward().x, TheCamera.GetForward().y); + Player = CWorld::Players[CWorld::PlayerInFocus].m_pPed; + while(ped < Player->m_numNearPeds && !found) + if(Player->m_nearPeds[ped] && Player->m_nearPeds[ped]->GetPedState() != PED_DEAD) + found = true; + else + ped++; + if(found){ + vDist = Player->m_nearPeds[ped]->GetPosition() - TheCamera.GetGameCamPosition(); + PedAngle = CGeneral::GetATanOfXY(vDist.x, vDist.y); + DeltaAngle = FwdAngle - PedAngle; + while(DeltaAngle >= PI) DeltaAngle -= 2*PI; + while(DeltaAngle < -PI) DeltaAngle += 2*PI; + if(Abs(DeltaAngle) < HALFPI){ + fDist = vDist.Magnitude2D(); + if(fDist < 1.25f){ + Near = DEFAULT_NEAR - (1.25f - fDist); + if(Near < 0.05f) + Near = 0.05f; + RwCameraSetNearClipPlane(Scene.camera, Near); + } + } + } +} + +void +CCam::KeepTrackOfTheSpeed(const CVector &source, const CVector &target, const CVector &up, const float &alpha, const float &beta, const float &fov) +{ + static CVector PreviousSource = source; + static CVector PreviousTarget = target; + static CVector PreviousUp = up; + static float PreviousBeta = beta; + static float PreviousAlpha = alpha; + static float PreviousFov = fov; + + if(TheCamera.m_bJust_Switched){ + PreviousSource = source; + PreviousTarget = target; + PreviousUp = up; + } + + m_cvecSourceSpeedOverOneFrame = source - PreviousSource; + m_cvecTargetSpeedOverOneFrame = target - PreviousTarget; + m_cvecUpOverOneFrame = up - PreviousUp; + m_fFovSpeedOverOneFrame = fov - PreviousFov; + m_fBetaSpeedOverOneFrame = beta - PreviousBeta; + MakeAngleLessThan180(m_fBetaSpeedOverOneFrame); + m_fAlphaSpeedOverOneFrame = alpha - PreviousAlpha; + MakeAngleLessThan180(m_fAlphaSpeedOverOneFrame); + + PreviousSource = source; + PreviousTarget = target; + PreviousUp = up; + PreviousBeta = beta; + PreviousAlpha = alpha; + PreviousFov = fov; +} + +bool +CCam::Using3rdPersonMouseCam(void) +{ + return CCamera::m_bUseMouse3rdPerson && Mode == MODE_FOLLOWPED; +} + +bool +CCam::GetWeaponFirstPersonOn(void) +{ + return CamTargetEntity && CamTargetEntity->IsPed() && ((CPed*)CamTargetEntity)->GetWeapon()->m_bAddRotOffset; +} + +bool +CCam::IsTargetInWater(const CVector &CamCoors) +{ + if(CamTargetEntity){ + float WaterZ = -6000.0f; + CWaterLevel::GetWaterLevel(CamTargetEntity->GetPosition(), &WaterZ, false); + if(CamTargetEntity->IsPed()){ + if(((CPed*)CamTargetEntity)->bIsDrowning || + ((CPed*)CamTargetEntity)->bIsInWater && CamTargetEntity->GetPosition().z < WaterZ) + return true; + }else{ + assert(CamTargetEntity->IsVehicle()); + if(((CVehicle*)CamTargetEntity)->bIsDrowning || + ((CVehicle*)CamTargetEntity)->bIsInWater && CamTargetEntity->GetPosition().z < WaterZ) + return true; + } + } + m_vecLastAboveWaterCamPosition = Source; + return false; +} + +void +CCam::PrintMode(void) +{ + // Doesn't do anything + char buf[256]; + + if(PrintDebugCode){ + sprintf(buf, " "); + sprintf(buf, " "); + sprintf(buf, " "); + + static Const char *modes[] = { "None", + "Top Down", "GTA Classic", "Behind Car", "Follow Ped", + "Aiming", "Debug", "Sniper", "Rocket", "Model Viewer", "Bill", + "Syphon", "Circle", "Cheesy Zoom", "Wheel", "Fixed", + "1st Person", "Fly by", "on a String", "Reaction", + "Follow Ped with Bind", "Chris", "Behind Boat", + "Player fallen in Water", "Train Roof", "Train Side", + "Blood on the tracks", "Passenger", "Syphon Crim in Front", + "Dead Baby", "Pillow Paps", "Look at Cars", "Arrest One", + "Arrest Two", "M16", "Special fixed for Syphon", "Fight", + "Top Down Ped", "Lighthouse", + "Sniper run about", "Rocket run about", + "1st Person run about", "M16 run about", "Fight run about", + "Editor", "Helicannon", "Camera" + }; + sprintf(buf, "Cam: %s", modes[TheCamera.Cams[TheCamera.ActiveCam].Mode]); + CDebug::PrintAt(buf, 2, 5); + } + + if(DebugCamMode != MODE_NONE){ + switch(Mode){ + case MODE_FOLLOWPED: + sprintf(buf, "Debug:- Cam Choice1. No Locking, used as game default"); + break; + case MODE_REACTION: + sprintf(buf, "Debug:- Cam Choice2. Reaction Cam On A String "); + sprintf(buf, " Uses Locking Button LeftShoulder 1. "); // lie + break; + case MODE_FOLLOW_PED_WITH_BIND: + sprintf(buf, "Debug:- Cam Choice3. Game ReactionCam with Locking "); + sprintf(buf, " Uses Locking Button LeftShoulder 1. "); + break; + case MODE_CHRIS: + sprintf(buf, "Debug:- Cam Choice4. Chris's idea. "); + sprintf(buf, " Uses Locking Button LeftShoulder 1. "); + sprintf(buf, " Also control the camera using the right analogue stick."); + break; + } + } +} + +// This code is really bad. wtf R*? +CVector +CCam::DoAverageOnVector(const CVector &vec) +{ + int i; + CVector Average = CVector(0.0f, 0.0f, 0.0f); + + if(ResetStatics){ + m_iRunningVectorArrayPos = 0; + m_iRunningVectorCounter = 1; + } + + // TODO: make this work with NUMBER_OF_VECTORS_FOR_AVERAGE != 2 + if(m_iRunningVectorCounter == 3){ + m_arrPreviousVectors[0] = m_arrPreviousVectors[1]; + m_arrPreviousVectors[1] = vec; + }else + m_arrPreviousVectors[m_iRunningVectorArrayPos] = vec; + + for(i = 0; i <= m_iRunningVectorArrayPos; i++) + Average += m_arrPreviousVectors[i]; + Average /= i; + + m_iRunningVectorArrayPos++; + m_iRunningVectorCounter++; + if(m_iRunningVectorArrayPos >= NUMBER_OF_VECTORS_FOR_AVERAGE) + m_iRunningVectorArrayPos = NUMBER_OF_VECTORS_FOR_AVERAGE-1; + if(m_iRunningVectorCounter > NUMBER_OF_VECTORS_FOR_AVERAGE+1) + m_iRunningVectorCounter = NUMBER_OF_VECTORS_FOR_AVERAGE+1; + + return Average; +} + +float DefaultAcceleration = 0.045f; +float DefaultMaxStep = 0.15f; +float fDefaultSpeedStep = 0.025f; +float fDefaultSpeedMultiplier = 0.09f; +float fDefaultSpeedLimit = 0.15f; +float fDefaultSpeedStep4Avoid = 0.02f; +float fDefaultSpeedMultiplier4Avoid = 0.05f; +float fDefaultSpeedLimit4Avoid = 0.25f; +float fAvoidGeomThreshhold = 1.5f; +float fMiniGunBetaOffset = 0.3f; + +void +CCam::Process_FollowPed(const CVector &CameraTarget, float TargetOrientation, float, float) +{ + if(!CamTargetEntity->IsPed()) + return; + + CVector TargetCoors, Dist, IdealSource; + float Length = 0.0f; + static bool PickedASide; + static float FixedTargetOrientation = 0.0f; + float AngleToGoTo = 0.0f; + bool StandingInTrain = false; + float ZoomGroundTarget = 0.0f; + float ZoomZTarget = 0.0f; + static int TimeIndicatedWantedToGoDown = 0; + static bool StartedCountingForGoDown = false; + static float ZoomGround = 0.0f; + static float ZoomGroundSpeed = 0.0f; + static float ZoomZ = 0.0f; + static float ZoomZSpeed = 0.0f; + float DeltaBeta; + + m_bFixingBeta = false; + bBelowMinDist = false; + bBehindPlayerDesired = false; + + FOV = DefaultFOV; + + if(ResetStatics){ + Rotating = false; + m_bCollisionChecksOn = true; + FixedTargetOrientation = 0.0f; + PickedASide = false; + StartedCountingForGoDown = false; + AngleToGoTo = 0.0f; + ZoomGround = 0.0f; + ZoomGroundSpeed = 0.0f; + ZoomZ = 0.0f; + ZoomZSpeed = 0.0f; + Distance = 500.0f; + } + + + TargetCoors = CameraTarget; + + // Take speed of thing we're standing on into account + CVector GroundMovement(0.0f, 0.0f, 0.0f); + CPhysical *ground = (CPhysical*)((CPed*)CamTargetEntity)->m_pCurSurface; + if(ground && (ground->IsVehicle() || ground->IsObject())) + GroundMovement += ground->GetSpeed(CamTargetEntity->GetPosition() - ground->GetPosition()) * CTimer::GetTimeStep(); + + Source += GroundMovement; + IdealSource = Source; + TargetCoors.z += m_fSyphonModeTargetZOffSet; + + TargetCoors.z = DoAverageOnVector(TargetCoors).z; + + Dist.x = IdealSource.x - TargetCoors.x; + Dist.y = IdealSource.y - TargetCoors.y; + Length = Dist.Magnitude2D(); + + // Cam on a string. With a fixed distance. Zoom in/out is done later. + if(Length != 0.0f){ + IdealSource = TargetCoors + CVector(Dist.x, Dist.y, 0.0f)/Length * m_fMinRealGroundDist; + IdealSource.z += GroundMovement.z; + }else + IdealSource = TargetCoors + CVector(1.0f, 1.0f, 0.0f); + + if(TheCamera.m_bUseTransitionBeta && ResetStatics){ + CVector VecDistance; + IdealSource.x = TargetCoors.x + m_fMinRealGroundDist*Cos(m_fTransitionBeta); + IdealSource.y = TargetCoors.y + m_fMinRealGroundDist*Sin(m_fTransitionBeta); + Beta = CGeneral::GetATanOfXY(IdealSource.x - TargetCoors.x, IdealSource.y - TargetCoors.y); + }else + Beta = CGeneral::GetATanOfXY(Source.x - TargetCoors.x, Source.y - TargetCoors.y); + + if(TheCamera.m_bCamDirectlyBehind){ + m_bCollisionChecksOn = true; + Beta = TargetOrientation + PI; + } + + if(FindPlayerVehicle()) + if(FindPlayerVehicle()->m_vehType == VEHICLE_TYPE_TRAIN) + StandingInTrain = true; + + if(TheCamera.m_bCamDirectlyInFront){ + m_bCollisionChecksOn = true; + Beta = TargetOrientation; + } + + while(Beta >= PI) Beta -= 2.0f * PI; + while(Beta < -PI) Beta += 2.0f * PI; + + if(TheCamera.PedZoomIndicator == CAM_ZOOM_1 && + ((CPed*)CamTargetEntity)->GetPedState() != PED_ENTER_CAR && + ((CPed*)CamTargetEntity)->GetPedState() != PED_CARJACK){ + ZoomGroundTarget = m_fTargetZoomGroundOne; + ZoomZTarget = m_fTargetZoomOneZExtra; + }else if(TheCamera.PedZoomIndicator == CAM_ZOOM_2 || TheCamera.PedZoomIndicator == CAM_ZOOM_1){ + ZoomGroundTarget = m_fTargetZoomGroundTwo; + ZoomZTarget = m_fTargetZoomTwoZExtra; + }else if(TheCamera.PedZoomIndicator == CAM_ZOOM_3){ + ZoomGroundTarget = m_fTargetZoomGroundThree; + ZoomZTarget = m_fTargetZoomThreeZExtra; + } + if(m_fCloseInPedHeightOffset > 0.00001f){ + ZoomGroundTarget = m_fTargetCloseInDist; + ZoomZTarget = m_fTargetZoomZCloseIn; + } + if(ResetStatics){ + ZoomGround = ZoomGroundTarget; + ZoomZ = ZoomZTarget; + } + + float SpeedStep = fDefaultSpeedStep; + float SpeedMultiplier = fDefaultSpeedMultiplier; + float SpeedLimit = fDefaultSpeedLimit; + bool Shooting = false; + CPed *ped = (CPed*)CamTargetEntity; + if(ped->GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) + if(CPad::GetPad(0)->GetWeapon()) + Shooting = true; + if(ped->GetWeapon()->m_eWeaponType == WEAPONTYPE_DETONATOR || + ped->GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT) + Shooting = false; + + + // Figure out if and where we want to rotate + + if(CPad::GetPad(0)->ForceCameraBehindPlayer() && !CPickups::PlayerOnWeaponPickup || Shooting){ + + // Center cam behind player + + if(PickedASide){ + if(AngleToGoTo == 0.0f){ + FixedTargetOrientation = TargetOrientation + PI; + if(Shooting && ped->GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN) + FixedTargetOrientation -= fMiniGunBetaOffset; + } + Rotating = true; + }else{ + FixedTargetOrientation = TargetOrientation + PI; + Rotating = true; + PickedASide = true; + if(Shooting && ped->GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN) + FixedTargetOrientation -= fMiniGunBetaOffset; + } + }else if(Abs(TheCamera.m_fAvoidTheGeometryProbsTimer) > fAvoidGeomThreshhold && !Rotating ){ + + if(TheCamera.m_fAvoidTheGeometryProbsTimer < 0.0f) + FixedTargetOrientation = TargetOrientation; + else + FixedTargetOrientation = TargetOrientation + PI; + float dist = (Source - TargetCoors).Magnitude(); + float mult = dist > 0.1f ? 1.0f/dist : 10.0f; + SpeedStep = mult * fDefaultSpeedStep4Avoid; + SpeedMultiplier = mult * fDefaultSpeedMultiplier4Avoid; + SpeedLimit = mult * fDefaultSpeedLimit4Avoid; + } + + int MoveState = ((CPed*)CamTargetEntity)->m_nMoveState; + if(MoveState != PEDMOVE_NONE && MoveState != PEDMOVE_STILL && + !(CPad::GetPad(0)->ForceCameraBehindPlayer() && !CPickups::PlayerOnWeaponPickup) && !Shooting){ + Rotating = false; + if(TheCamera.m_fAvoidTheGeometryProbsTimer <= fAvoidGeomThreshhold) + BetaSpeed = 0.0f; + } + + // Now do the Beta rotation + + float RotDistance = m_fMinRealGroundDist; + + if(Rotating || TheCamera.m_fAvoidTheGeometryProbsTimer > fAvoidGeomThreshhold){ + m_bFixingBeta = true; + + while(FixedTargetOrientation >= PI) FixedTargetOrientation -= 2*PI; + while(FixedTargetOrientation < -PI) FixedTargetOrientation += 2*PI; + + while(Beta >= PI) Beta -= 2*PI; + while(Beta < -PI) Beta += 2*PI; + + + // This is inlined WellBufferMe - unfortunately modified so we can't just call it + { + DeltaBeta = FixedTargetOrientation - Beta; + while(DeltaBeta >= PI) DeltaBeta -= 2*PI; + while(DeltaBeta < -PI) DeltaBeta += 2*PI; + + // this is the added bit + if(!Rotating){ + if(TheCamera.m_nAvoidTheGeometryProbsDirn == -1 && DeltaBeta > 0.0f || + TheCamera.m_nAvoidTheGeometryProbsDirn == 1 && DeltaBeta < 0.0f) + DeltaBeta *= -1.0f; + } + + float ReqSpeed = DeltaBeta * SpeedMultiplier; + // this is also added + ReqSpeed = Clamp(ReqSpeed, -SpeedLimit, SpeedLimit); + + // Add or subtract absolute depending on sign, genius! + if(ReqSpeed - BetaSpeed > 0.0f) + BetaSpeed += SpeedStep * Abs(ReqSpeed - BetaSpeed) * CTimer::GetTimeStep(); + else + BetaSpeed -= SpeedStep * Abs(ReqSpeed - BetaSpeed) * CTimer::GetTimeStep(); + // this would be simpler: + // BetaSpeed += SpeedStep * (ReqSpeed - BetaSpeed) * CTimer::ms_fTimeStep; + + if(ReqSpeed < 0.0f && BetaSpeed < ReqSpeed) + BetaSpeed = ReqSpeed; + else if(ReqSpeed > 0.0f && BetaSpeed > ReqSpeed) + BetaSpeed = ReqSpeed; + + Beta += BetaSpeed * Min(10.0f, CTimer::GetTimeStep()); + } + + if(ResetStatics){ + Beta = FixedTargetOrientation; + BetaSpeed = 0.0f; + } + + Source.x = TargetCoors.x + RotDistance * Cos(Beta); + Source.y = TargetCoors.y + RotDistance * Sin(Beta); + + // Check if we can stop rotating + DeltaBeta = FixedTargetOrientation - Beta; + while(DeltaBeta >= PI) DeltaBeta -= 2*PI; + while(DeltaBeta < -PI) DeltaBeta += 2*PI; + if(Abs(DeltaBeta) < DEGTORAD(1.0f) && !bBehindPlayerDesired){ + // Stop rotation + PickedASide = false; + Rotating = false; + BetaSpeed = 0.0f; + } + } + + + if(TheCamera.m_bCamDirectlyBehind || TheCamera.m_bCamDirectlyInFront || + StandingInTrain || Rotating || + TheCamera.m_bUseTransitionBeta && ResetStatics || + Abs(TheCamera.m_fAvoidTheGeometryProbsTimer) > fAvoidGeomThreshhold){ + if(TheCamera.m_bUseTransitionBeta){ + Beta = m_fTransitionBeta; + Source.x = TargetCoors.x + RotDistance * Cos(m_fTransitionBeta); + Source.y = TargetCoors.y + RotDistance * Sin(m_fTransitionBeta); + } + if(TheCamera.m_bCamDirectlyBehind){ + Beta = TargetOrientation + PI; + Source.x = TargetCoors.x + RotDistance * Cos(Beta); + Source.y = TargetCoors.y + RotDistance * Sin(Beta); + } + if(TheCamera.m_bCamDirectlyInFront){ + Beta = TargetOrientation; + Source.x = TargetCoors.x + RotDistance * Cos(Beta); + Source.y = TargetCoors.y + RotDistance * Sin(Beta); + } + if(StandingInTrain){ + Beta = TargetOrientation + PI; + Source.x = TargetCoors.x + RotDistance * Cos(Beta); + Source.y = TargetCoors.y + RotDistance * Sin(Beta); + m_fDimensionOfHighestNearCar = 0.0f; + m_fCamBufferedHeight = 0.0f; + m_fCamBufferedHeightSpeed = 0.0f; + } + if(StandingInTrain){ + Beta = TargetOrientation + PI; + Source.x = TargetCoors.x + RotDistance * Cos(Beta); + Source.y = TargetCoors.y + RotDistance * Sin(Beta); + m_fDimensionOfHighestNearCar = 0.0f; + m_fCamBufferedHeight = 0.0f; + m_fCamBufferedHeightSpeed = 0.0f; + } + + // Beta and Source already set in the rotation code + }else{ + Source = IdealSource; + BetaSpeed = 0.0f; + } + Source.z = IdealSource.z; + + // Zoom out camera + Front = TargetCoors - Source; + Front.Normalise(); + WellBufferMe(ZoomGroundTarget, &ZoomGround, &ZoomGroundSpeed, 0.2f, 0.07f, false); + WellBufferMe(ZoomZTarget, &ZoomZ, &ZoomZSpeed, 0.2f, 0.07f, false); + Source.x -= Front.x*ZoomGround; + Source.y -= Front.y*ZoomGround; + Source.z += ZoomZ; + + + // Process height offset to avoid peds and cars + + float TargetZOffSet = Max(m_fDimensionOfHighestNearCar, m_fPedBetweenCameraHeightOffset); + float TargetHeight = CameraTarget.z + TargetZOffSet - Source.z; + + if(TargetHeight > m_fCamBufferedHeight){ + // Have to go up + if(TargetZOffSet == m_fPedBetweenCameraHeightOffset && TargetZOffSet > m_fCamBufferedHeight) + WellBufferMe(TargetHeight, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.2f, 0.04f, false); + else + WellBufferMe(TargetHeight, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.2f, 0.025f, false); + StartedCountingForGoDown = false; + }else{ + // Have to go down + if(StartedCountingForGoDown){ + if(CTimer::GetTimeInMilliseconds() != TimeIndicatedWantedToGoDown){ + if(TargetHeight > 0.0f) + WellBufferMe(TargetHeight, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.2f, 0.01f, false); + else + WellBufferMe(0.0f, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.2f, 0.01f, false); + } + }else{ + StartedCountingForGoDown = true; + TimeIndicatedWantedToGoDown = CTimer::GetTimeInMilliseconds(); + } + } + + Source.z += m_fCamBufferedHeight; + TargetCoors.z += Min(1.0f, m_fCamBufferedHeight/2.0f); + m_cvecTargetCoorsForFudgeInter = TargetCoors; + + CVector OrigSource = Source; + TheCamera.AvoidTheGeometry(OrigSource, TargetCoors, Source, FOV); + float TargetDist = (TargetCoors - Source).Magnitude(); + if(TargetDist < Distance) + Distance = TargetDist; + else{ + float f = Pow(0.97f, CTimer::GetTimeStep()); + Distance = (1.0f - f)*TargetDist + f*Distance; + if(TargetDist > 0.05f) + Source = TargetCoors + (Source-TargetCoors)*Distance/TargetDist; + float clip = Distance-fRangePlayerRadius; + if(clip < RwCameraGetNearClipPlane(Scene.camera)) + RwCameraSetNearClipPlane(Scene.camera, Max(clip, fCloseNearClipLimit)); + } + + Front = TargetCoors - Source; + m_fRealGroundDist = Front.Magnitude2D(); + m_fMinDistAwayFromCamWhenInterPolating = m_fRealGroundDist; + Front.Normalise(); + GetVectorsReadyForRW(); + TheCamera.m_bCamDirectlyBehind = false; + TheCamera.m_bCamDirectlyInFront = false; + + ResetStatics = false; +} + +float fBaseDist = 1.7f; +float fAngleDist = 2.0f; +float fFalloff = 3.0f; +float fStickSens = 0.01f; +float fTweakFOV = 1.1f; +float fTranslateCamUp = 0.8f; +int16 nFadeControlThreshhold = 45; +float fDefaultAlphaOrient = -0.22f; +float fMouseAvoidGeomReturnRate = 0.92f; + +void +CCam::Process_FollowPedWithMouse(const CVector &CameraTarget, float TargetOrientation, float, float) +{ + FOV = DefaultFOV; + + if(!CamTargetEntity->IsPed()) + return; + + CVector TargetCoors; + float CamDist; + CColPoint colPoint; + CEntity *entity; + + if(ResetStatics){ + Rotating = false; + m_bCollisionChecksOn = true; + CPad::GetPad(0)->ClearMouseHistory(); + ResetStatics = false; + } + + bool OnTrain = FindPlayerVehicle() && FindPlayerVehicle()->IsTrain(); + + TargetCoors = CameraTarget; + TargetCoors.z += fTranslateCamUp; + + float AlphaOffset, BetaOffset; + if(CPad::GetPad(0)->IsPlayerControlsDisabledBy(PLAYERCONTROL_PLAYERINFO)){ + CVector ToCam = Source - TargetCoors; + ToCam.Normalise(); + if(ToCam.z < -0.9f) + BetaOffset = TargetOrientation + PI; + else + BetaOffset = Atan2(ToCam.y, ToCam.x); + BetaOffset -= Beta; + AlphaOffset = 0.0f; + }else{ + // Look around + bool UseMouse = false; + float MouseX = CPad::GetPad(0)->GetMouseX(); + float MouseY = CPad::GetPad(0)->GetMouseY(); + float LookLeftRight, LookUpDown; + if((MouseX != 0.0f || MouseY != 0.0f) && !CPad::GetPad(0)->ArePlayerControlsDisabled()){ + UseMouse = true; + LookLeftRight = -2.5f*MouseX; + LookUpDown = 4.0f*MouseY; + }else{ + LookLeftRight = -CPad::GetPad(0)->LookAroundLeftRight(); + LookUpDown = CPad::GetPad(0)->LookAroundUpDown(); + } + if(UseMouse){ + BetaOffset = LookLeftRight * TheCamera.m_fMouseAccelHorzntl * FOV/80.0f; + AlphaOffset = LookUpDown * TheCamera.m_fMouseAccelVertical * FOV/80.0f; + }else{ + BetaOffset = LookLeftRight * fStickSens * (1.0f/14.0f) * FOV/80.0f * CTimer::GetTimeStep(); + AlphaOffset = LookUpDown * fStickSens * (0.6f/14.0f) * FOV/80.0f * CTimer::GetTimeStep(); + } + } + + if(TheCamera.GetFading() && TheCamera.GetFadingDirection() == FADE_IN && nFadeControlThreshhold < CDraw::FadeValue || + CDraw::FadeValue > 200 || + CPad::GetPad(0)->IsPlayerControlsDisabledBy(PLAYERCONTROL_PLAYERINFO)){ + if(Alpha < fDefaultAlphaOrient-0.05f) + AlphaOffset = 0.05f; + else if(Alpha < fDefaultAlphaOrient) + AlphaOffset = fDefaultAlphaOrient - Alpha; + else if(Alpha > fDefaultAlphaOrient+0.05f) + AlphaOffset = -0.05f; + else if(Alpha > fDefaultAlphaOrient) + AlphaOffset = fDefaultAlphaOrient - Alpha; + else + AlphaOffset = 0.0f; + } + + Alpha += AlphaOffset; + Beta += BetaOffset; + while(Beta >= PI) Beta -= 2*PI; + while(Beta < -PI) Beta += 2*PI; + if(Alpha > DEGTORAD(45.0f)) Alpha = DEGTORAD(45.0f); + else if(Alpha < -DEGTORAD(89.5f)) Alpha = -DEGTORAD(89.5f); + + // SA code +#ifdef FREE_CAM + if((CCamera::bFreeCam && Alpha > 0.0f) || (!CCamera::bFreeCam && Alpha > fBaseDist)) +#else + if(Alpha > fBaseDist) // comparing an angle against a distance? +#endif + CamDist = fBaseDist + Cos(Min(Alpha*fFalloff, HALFPI))*fAngleDist; + else + CamDist = fBaseDist + Cos(Alpha)*fAngleDist; + + if(TheCamera.m_bUseTransitionBeta) + Beta = m_fTransitionBeta; + + if(TheCamera.m_bCamDirectlyBehind) + Beta = TheCamera.m_PedOrientForBehindOrInFront + PI; + if(TheCamera.m_bCamDirectlyInFront) + Beta = TheCamera.m_PedOrientForBehindOrInFront; + if(OnTrain) + Beta = TargetOrientation; + + Front.x = Cos(Alpha) * -Cos(Beta); + Front.y = Cos(Alpha) * -Sin(Beta); + Front.z = Sin(Alpha); + Source = TargetCoors - Front*CamDist; + m_cvecTargetCoorsForFudgeInter = TargetCoors; + + // Clip Source and fix near clip + CWorld::pIgnoreEntity = CamTargetEntity; + entity = nil; + if(CWorld::ProcessLineOfSight(TargetCoors, Source, colPoint, entity, true, true, true, true, false, false, true)){ + float PedColDist = (TargetCoors - colPoint.point).Magnitude(); + float ColCamDist = CamDist - PedColDist; + if(entity->IsPed() && ColCamDist > DEFAULT_NEAR + 0.1f){ + // Ped in the way but not clipping through + if(CWorld::ProcessLineOfSight(colPoint.point, Source, colPoint, entity, true, true, true, true, false, false, true)){ + PedColDist = (TargetCoors - colPoint.point).Magnitude(); + Source = colPoint.point; + if(PedColDist < DEFAULT_NEAR + 0.3f) + RwCameraSetNearClipPlane(Scene.camera, Max(PedColDist-0.3f, 0.05f)); + }else{ + RwCameraSetNearClipPlane(Scene.camera, Min(ColCamDist-0.35f, DEFAULT_NEAR)); + } + }else{ + Source = colPoint.point; + if(PedColDist < DEFAULT_NEAR + 0.3f) + RwCameraSetNearClipPlane(Scene.camera, Max(PedColDist-0.3f, 0.05f)); + } + } + CWorld::pIgnoreEntity = nil; + + float ViewPlaneHeight = Tan(DEGTORAD(FOV) / 2.0f); + float ViewPlaneWidth = ViewPlaneHeight * CDraw::CalculateAspectRatio() * fTweakFOV; + float Near = RwCameraGetNearClipPlane(Scene.camera); + float radius = ViewPlaneWidth*Near; + entity = CWorld::TestSphereAgainstWorld(Source + Front*Near, radius, nil, true, true, false, true, false, false); + int i = 0; + while(entity){ + CVector CamToCol = gaTempSphereColPoints[0].point - Source; + float frontDist = DotProduct(CamToCol, Front); + float dist = (CamToCol - Front*frontDist).Magnitude() / ViewPlaneWidth; + + // Try to decrease near clip + dist = Max(Min(Near, dist), 0.1f); + if(dist < Near) + RwCameraSetNearClipPlane(Scene.camera, dist); + + // Move forward a bit + if(dist == 0.1f) + Source += (TargetCoors - Source)*0.3f; + + Near = RwCameraGetNearClipPlane(Scene.camera); +#ifndef FIX_BUGS + // this is wrong...DEGTORAD missing + radius = Tan(FOV / 2.0f) * CDraw::CalculateAspectRatio() * fTweakFOV * Near; +#else + radius = ViewPlaneWidth*Near; +#endif + // Keep testing + entity = CWorld::TestSphereAgainstWorld(Source + Front*Near, radius, nil, true, true, false, true, false, false); + + i++; + if(i > 5) + entity = nil; + } + + float TargetDist = (TargetCoors - Source).Magnitude(); + if(TargetDist < Distance) + Distance = TargetDist; + else{ + float f = Pow(fMouseAvoidGeomReturnRate, CTimer::GetTimeStep()); + Distance = (1.0f - f)*TargetDist + f*Distance; + if(TargetDist > 0.05f) + Source = TargetCoors + (Source-TargetCoors)*Distance/TargetDist; + float clip = Distance-fRangePlayerRadius; + if(clip < RwCameraGetNearClipPlane(Scene.camera)) + RwCameraSetNearClipPlane(Scene.camera, Max(clip, fCloseNearClipLimit)); + } + + TheCamera.m_bCamDirectlyInFront = false; + TheCamera.m_bCamDirectlyBehind = false; + + GetVectorsReadyForRW(); + + if(((CPed*)CamTargetEntity)->CanStrafeOrMouseControl() && CDraw::FadeValue < 250 && + (TheCamera.GetFadingDirection() != FADE_OUT || CDraw::FadeValue <= 100) && + !CPad::GetPad(0)->IsPlayerControlsDisabledBy(PLAYERCONTROL_PLAYERINFO)){ + float Heading = Front.Heading(); + ((CPed*)TheCamera.pTargetEntity)->m_fRotationCur = Heading; + ((CPed*)TheCamera.pTargetEntity)->m_fRotationDest = Heading; + TheCamera.pTargetEntity->SetHeading(Heading); + TheCamera.pTargetEntity->GetMatrix().UpdateRW(); + } +} + +float fBillsBetaOffset; // made up name, actually in CCam + +void +CCam::Process_BehindCar(const CVector &CameraTarget, float TargetOrientation, float, float) +{ + FOV = DefaultFOV; + + if(!CamTargetEntity->IsVehicle()) + return; + + CVector TargetCoors = CameraTarget; + TargetCoors.z -= 0.2f; + CA_MAX_DISTANCE = 9.95f; + CA_MIN_DISTANCE = 8.5f; + + CVector Dist = Source - TargetCoors; + float Length = Dist.Magnitude2D(); + m_fDistanceBeforeChanges = Length; + if(Length < 0.002f) + Length = 0.002f; + Beta = CGeneral::GetATanOfXY(TargetCoors.x - Source.x, TargetCoors.y - Source.y); +#ifdef TOGGLEABLE_BETA_FEATURES + // This is completely made up but Bill's cam manipulates an angle before calling this + // and otherwise calculating Beta doesn't make much sense. + Beta += fBillsBetaOffset; + fBillsBetaOffset = 0.0f; + Dist.x = -Length*Cos(Beta); + Dist.y = -Length*Sin(Beta); + Source = TargetCoors + Dist; +#endif + if(Length > CA_MAX_DISTANCE){ + Source.x = TargetCoors.x + Dist.x/Length * CA_MAX_DISTANCE; + Source.y = TargetCoors.y + Dist.y/Length * CA_MAX_DISTANCE; + }else if(Length < CA_MIN_DISTANCE){ + Source.x = TargetCoors.x + Dist.x/Length * CA_MIN_DISTANCE; + Source.y = TargetCoors.y + Dist.y/Length * CA_MIN_DISTANCE; + } + TargetCoors.z += 0.8f; + + Alpha = DEGTORAD(25.0f); + Source.z = TargetCoors.z + CA_MAX_DISTANCE*Sin(Alpha); + + RotCamIfInFrontCar(TargetCoors, TargetOrientation); + m_cvecTargetCoorsForFudgeInter = TargetCoors; + CVector OrigSource = Source; + TheCamera.AvoidTheGeometry(OrigSource, m_cvecTargetCoorsForFudgeInter, Source, FOV); + + Front = TargetCoors - Source; + ResetStatics = false; + GetVectorsReadyForRW(); +} + +float ZmOneAlphaOffset[] = { -0.01f, 0.1f, 0.125f, -0.1f, -0.06f }; +float ZmTwoAlphaOffset[] = { 0.045f, 0.12f, 0.045f, 0.045f, -0.035f }; +float ZmThreeAlphaOffset[] = { 0.005f, 0.005f, 0.15f, 0.005f, 0.12f }; +float INIT_RC_HELI_HORI_EXTRA = 6.0f; +float INIT_RC_PLANE_HORI_EXTRA = 9.5f; +float INIT_RC_HELI_ALPHA_EXTRA = 0.2f; +float INIT_RC_PLANE_ALPHA_EXTRA = 0.295f; + +void +CCam::WorkOutCamHeight(const CVector &TargetCoors, float TargetOrientation, float TargetHeight) +{ + if(!CamTargetEntity->IsVehicle()) + return; + + static float AlphaOffset = 0.0; + static float AlphaOffsetSpeed = 0.0; + static float AlphaDec = 0.0f; + + bool isHeli = false; + bool isBike = false; + int appearance = ((CVehicle*)CamTargetEntity)->GetVehicleAppearance(); + if(appearance == VEHICLE_APPEARANCE_BIKE) + isBike = true; + if(appearance == VEHICLE_APPEARANCE_HELI) + isHeli = true; + int index = 0; + TheCamera.GetArrPosForVehicleType(appearance, index); + + float ExtraOffset = 0.0f; + int id = CamTargetEntity->GetModelIndex(); + if(id == MI_RCRAIDER || id == MI_RCGOBLIN) + ExtraOffset = INIT_RC_HELI_ALPHA_EXTRA; + else if(id == MI_RCBARON) + ExtraOffset = INIT_RC_PLANE_ALPHA_EXTRA; + + if(ResetStatics){ + AlphaOffset = 0.0f; + AlphaOffsetSpeed = 0.0f; + AlphaDec = 0.0f; + + if(TheCamera.CarZoomIndicator == CAM_ZOOM_1) + AlphaOffset = ZmOneAlphaOffset[index] + ExtraOffset; + else if(TheCamera.CarZoomIndicator == CAM_ZOOM_2) + AlphaOffset = ZmTwoAlphaOffset[index] + ExtraOffset; + else if(TheCamera.CarZoomIndicator == CAM_ZOOM_3) + AlphaOffset = ZmThreeAlphaOffset[index] + ExtraOffset; + } + + if(TheCamera.CarZoomIndicator == CAM_ZOOM_1) + WellBufferMe(ZmOneAlphaOffset[index] + ExtraOffset, &AlphaOffset, &AlphaOffsetSpeed, 0.17f, 0.08f, false); + else if(TheCamera.CarZoomIndicator == CAM_ZOOM_2) + WellBufferMe(ZmTwoAlphaOffset[index] + ExtraOffset, &AlphaOffset, &AlphaOffsetSpeed, 0.17f, 0.08f, false); + else if(TheCamera.CarZoomIndicator == CAM_ZOOM_3) + WellBufferMe(ZmThreeAlphaOffset[index] + ExtraOffset, &AlphaOffset, &AlphaOffsetSpeed, 0.17f, 0.08f, false); + + float Length = (Source - TargetCoors).Magnitude2D(); + + CVector Forward = CamTargetEntity->GetForward(); + float CarAlpha = CGeneral::GetATanOfXY(Forward.Magnitude2D(), Forward.z); + // this shouldn't be necessary.... + while(CarAlpha >= PI) CarAlpha -= 2*PI; + while(CarAlpha < -PI) CarAlpha += 2*PI; + + while(Beta >= PI) Beta -= 2*PI; + while(Beta < -PI) Beta += 2*PI; + + float DeltaBeta = Beta - TargetOrientation; + while(DeltaBeta >= PI) DeltaBeta -= 2*PI; + while(DeltaBeta < -PI) DeltaBeta += 2*PI; + + float BehindCarNess = Cos(DeltaBeta); // 1 if behind car, 0 if side, -1 if in front + CarAlpha = -CarAlpha * BehindCarNess; + + float fwdSpeed = DotProduct(((CPhysical*)CamTargetEntity)->m_vecMoveSpeed, CamTargetEntity->GetForward())*180.0f; + if(CamTargetEntity->GetModelIndex() == MI_FIRETRUCK && CPad::GetPad(0)->GetCarGunFired()){ + CarAlpha = DEGTORAD(10.0f); + }else if(isHeli){ + CarAlpha = 0.0f; + float heliFwdZ = CamTargetEntity->GetForward().z; + float heliFwdXY = CamTargetEntity->GetForward().Magnitude2D(); + float alphaAmount = Min(Abs(fwdSpeed/90.0f), 1.0f); + if(heliFwdXY != 0.0f || heliFwdZ != 0.0f) + CarAlpha = CGeneral::GetATanOfXY(heliFwdXY, Abs(heliFwdZ)) * alphaAmount; + + CColPoint point; + CEntity *entity = nil; + CVector Test = Source; + Test.z = TargetCoors.z + 0.2f + Length*Sin(CarAlpha+AlphaOffset) + m_fCloseInCarHeightOffset; + if(CWorld::ProcessVerticalLine(Test, CamTargetEntity->GetPosition().z, point, entity, true, false, false, false, false, false, nil)){ + float sin = (point.point.z - TargetCoors.z - 0.2f - m_fCloseInCarHeightOffset)/Length; + CarAlpha = Asin(Clamp(sin, -1.0f, 1.0f)) - AlphaOffset; + if(CarAlpha < 0.0f) + AlphaOffset += CarAlpha; + } + } + + CarAlpha = CGeneral::LimitRadianAngle(CarAlpha); + if(CarAlpha < 0.0f) CarAlpha = 0.0f; + if(CarAlpha > DEGTORAD(89.0f)) CarAlpha = DEGTORAD(89.0f); + + if(ResetStatics) + Alpha = CarAlpha; + + float TargetAlpha = Alpha; + float DeltaAlpha = CarAlpha - TargetAlpha; + while(DeltaAlpha >= PI) DeltaAlpha -= 2*PI; + while(DeltaAlpha < -PI) DeltaAlpha += 2*PI; + if(Abs(DeltaAlpha) > 0.0f && !TheCamera.m_bVehicleSuspenHigh) + TargetAlpha = CarAlpha; + + if(isBike) + WellBufferMe(TargetAlpha, &Alpha, &AlphaSpeed, 0.09f, 0.04f, true); + else if(isHeli) + WellBufferMe(TargetAlpha, &Alpha, &AlphaSpeed, 0.09f, 0.04f, true); + else + WellBufferMe(TargetAlpha, &Alpha, &AlphaSpeed, 0.15f, 0.07f, true); + + Source.z = TargetCoors.z + Sin(Alpha + AlphaOffset)*Length + m_fCloseInCarHeightOffset; + AlphaOffset -= AlphaDec; +} + +// Rotate cam behind the car when the car is moving forward +bool +CCam::RotCamIfInFrontCar(CVector &TargetCoors, float TargetOrientation) +{ + float BetaMaxSpeed = 0.15f; + float BetaAcceleration = 0.007f; + bool MovingForward = false; + float MaxDiffBeta = DEGTORAD(160.0f); + CPhysical *phys = (CPhysical*)CamTargetEntity; + + float ForwardSpeed = DotProduct(phys->GetForward(), phys->GetSpeed(CVector(0.0f, 0.0f, 0.0f))); + if(ForwardSpeed > 0.02f) + MovingForward = true; + + if(phys->IsVehicle() && (phys->GetModelIndex() == MI_SPARROW || phys->GetModelIndex() == MI_HUNTER)){ + MaxDiffBeta = DEGTORAD(160.0f); + BetaMaxSpeed = 0.1f; + BetaAcceleration = 0.003f; + CVector speed = phys->GetSpeed(CVector(0.0f, 0.0f, 0.0f)); + speed.z = 0.0f; + if(50.0f*speed.Magnitude() > 3.13f) + TargetOrientation = CGeneral::GetATanOfXY(speed.x, speed.y); + } + + float Dist = (Source - TargetCoors).Magnitude2D(); + + float DeltaBeta = TargetOrientation - Beta; + while(DeltaBeta >= PI) DeltaBeta -= 2*PI; + while(DeltaBeta < -PI) DeltaBeta += 2*PI; + + if(Abs(DeltaBeta) > PI-MaxDiffBeta && MovingForward && TheCamera.m_uiTransitionState == 0) + m_bFixingBeta = true; + + CPad *pad = CPad::GetPad(0); + if(!(pad->GetLookBehindForCar() || pad->GetLookBehindForPed() || pad->GetLookLeft() || pad->GetLookRight())) + if(DirectionWasLooking != LOOKING_FORWARD) + TheCamera.m_bCamDirectlyBehind = true; + + if(!m_bFixingBeta && !TheCamera.m_bUseTransitionBeta && !TheCamera.m_bCamDirectlyBehind && !TheCamera.m_bCamDirectlyInFront) + return false; + + bool SetBeta = false; + if(TheCamera.m_bCamDirectlyBehind || TheCamera.m_bCamDirectlyInFront || TheCamera.m_bUseTransitionBeta) + if(&TheCamera.Cams[TheCamera.ActiveCam] == this) + SetBeta = true; + + if(m_bFixingBeta || SetBeta){ + WellBufferMe(TargetOrientation, &Beta, &BetaSpeed, BetaMaxSpeed, BetaAcceleration, true); + + if(TheCamera.m_bCamDirectlyBehind && &TheCamera.Cams[TheCamera.ActiveCam] == this) + Beta = TargetOrientation; + if(TheCamera.m_bCamDirectlyInFront && &TheCamera.Cams[TheCamera.ActiveCam] == this) + Beta = TargetOrientation + PI; + if(TheCamera.m_bUseTransitionBeta && &TheCamera.Cams[TheCamera.ActiveCam] == this) + Beta = m_fTransitionBeta; + + Source.x = TargetCoors.x - Cos(Beta)*Dist; + Source.y = TargetCoors.y - Sin(Beta)*Dist; + + // Check if we're done + DeltaBeta = TargetOrientation - Beta; + while(DeltaBeta >= PI) DeltaBeta -= 2*PI; + while(DeltaBeta < -PI) DeltaBeta += 2*PI; + if(Abs(DeltaBeta) < DEGTORAD(2.0f)) + m_bFixingBeta = false; + } + TheCamera.m_bCamDirectlyBehind = false; + TheCamera.m_bCamDirectlyInFront = false; + return true; +} + +float FIRETRUCK_TRACKING_MULT = 0.1f; +float fTestShiftHeliCamTarget = 0.6f; +float TiltTopSpeed[] = { 0.035f, 0.035f, 0.001f, 0.005f, 0.035f }; +float TiltSpeedStep[] = { 0.016f, 0.016f, 0.0002f, 0.0014f, 0.016f }; +float TiltOverShoot[] = { 1.05f, 1.05f, 0.0f, 0.0f, 1.0f }; + +void +CCam::Process_Cam_On_A_String(const CVector &CameraTarget, float TargetOrientation, float, float) +{ + if(!CamTargetEntity->IsVehicle()) + return; + + // unused + // ((CVehicle*)CamTargetEntity)->GetVehicleAppearance(); + + FOV = DefaultFOV; + + if(ResetStatics){ + AlphaSpeed = 0.0f; + m_fTilt = 0.0f; + m_fTiltSpeed = 0.0; + } + + CBaseModelInfo *mi = CModelInfo::GetModelInfo(CamTargetEntity->GetModelIndex()); + CVector Dimensions = mi->GetColModel()->boundingBox.max - mi->GetColModel()->boundingBox.min; + CVector TargetCoors = CameraTarget; + float BaseDist = Dimensions.Magnitude(); + + if(((CVehicle*)CamTargetEntity)->IsBike()) + BaseDist *= 1.45f; + if(((CVehicle*)CamTargetEntity)->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI && + CamTargetEntity->GetStatus() != STATUS_PLAYER_REMOTE) + TargetCoors += fTestShiftHeliCamTarget * CamTargetEntity->GetUp() * Dimensions.z; + else + TargetCoors.z += 0.8f*Dimensions.z; + + Beta = CGeneral::GetATanOfXY(TargetCoors.x - Source.x, TargetCoors.y - Source.y); + Alpha = CGeneral::LimitRadianAngle(Alpha); + Beta = CGeneral::LimitRadianAngle(Beta); + + if(CamTargetEntity->GetModelIndex() == MI_FIRETRUCK && CPad::GetPad(0)->GetCarGunFired() && + ((CVehicle*)CamTargetEntity)->m_vecMoveSpeed.Magnitude2D() < 0.01f){ + float TargetBeta = CamTargetEntity->GetForward().Heading() - ((CAutomobile*)CamTargetEntity)->m_fCarGunLR + HALFPI; + TargetBeta = CGeneral::LimitRadianAngle(TargetBeta); + float DeltaBeta = TargetBeta - Beta; + if(DeltaBeta > PI) DeltaBeta -= TWOPI; + else if(DeltaBeta < -PI) DeltaBeta += TWOPI; + float dist = (TargetCoors - Source).Magnitude(); + dist = FIRETRUCK_TRACKING_MULT*dist*Clamp(DeltaBeta, -0.8f, 0.8f); + Source += dist*CrossProduct(Front, CVector(0.0f, 0.0f, 1.0f)); + } + + m_fDistanceBeforeChanges = (Source - TargetCoors).Magnitude2D(); + + Cam_On_A_String_Unobscured(TargetCoors, BaseDist); + WorkOutCamHeight(TargetCoors, TargetOrientation, Dimensions.z); + RotCamIfInFrontCar(TargetCoors, TargetOrientation); + FixCamWhenObscuredByVehicle(TargetCoors); + + m_cvecTargetCoorsForFudgeInter = TargetCoors; + CVector OrigSource = Source; + if(CWorld::GetIsLineOfSightClear(CamTargetEntity->GetPosition(), m_cvecTargetCoorsForFudgeInter, true, false, false, true, false, false, true)) + TheCamera.AvoidTheGeometry(OrigSource, m_cvecTargetCoorsForFudgeInter, Source, FOV); + else + TheCamera.AvoidTheGeometry(OrigSource, CamTargetEntity->GetPosition(), Source, FOV); + + Front = TargetCoors - Source; + Front.Normalise(); + + int appearance = ((CVehicle*)CamTargetEntity)->GetVehicleAppearance(); + int index = 0; + TheCamera.GetArrPosForVehicleType(appearance, index); + + if(appearance == VEHICLE_APPEARANCE_HELI){ + float TargetTilt = DotProduct(Front, ((CVehicle*)CamTargetEntity)->GetSpeed(CVector(0.0f, 0.0f, 0.0f))); + CVector UpTarget = CamTargetEntity->GetUp(); + UpTarget.Normalise(); + int dir = TargetTilt < 0.0f ? -1 : 1; + if(m_fTilt != 0.0f) + TargetTilt += TiltOverShoot[index]*TargetTilt/m_fTilt * dir; + WellBufferMe(TargetTilt, &m_fTilt, &m_fTiltSpeed, TiltTopSpeed[index], TiltSpeedStep[index], false); + + Up = CVector(0.0f, 0.0f, 1.0f) - (CVector(0.0f, 0.0f, 1.0f) - UpTarget)*m_fTilt; + Up.Normalise(); + Front.Normalise(); + CVector Left = CrossProduct(Up, Front); + Up = CrossProduct(Front, Left); + Up.Normalise(); + }else{ + float TargetRoll; + if(CPad::GetPad(0)->GetDPadLeft() || CPad::GetPad(0)->GetDPadRight()){ + float fwdSpeed = 180.0f*DotProduct(((CVehicle*)CamTargetEntity)->m_vecMoveSpeed, CamTargetEntity->GetForward()); + if(fwdSpeed > 210.0f) fwdSpeed = 210.0f; + if(CPad::GetPad(0)->GetDPadLeft()) + TargetRoll = DEGTORAD(10.0f)*TiltOverShoot[index] + f_max_role_angle; + else + TargetRoll = -(DEGTORAD(10.0f)*TiltOverShoot[index] + f_max_role_angle); + CVector FwdTarget = CamTargetEntity->GetForward(); + FwdTarget.Normalise(); + float AngleDiff = DotProduct(FwdTarget, Front); + AngleDiff = Acos(Min(Abs(AngleDiff), 1.0f)); + TargetRoll *= fwdSpeed/210.0f * Sin(AngleDiff); + }else{ + float fwdSpeed = 180.0f*DotProduct(((CVehicle*)CamTargetEntity)->m_vecMoveSpeed, CamTargetEntity->GetForward()); + if(fwdSpeed > 210.0f) fwdSpeed = 210.0f; + TargetRoll = CPad::GetPad(0)->GetLeftStickX()/128.0f * fwdSpeed/210.0f; + CVector FwdTarget = CamTargetEntity->GetForward(); + FwdTarget.Normalise(); + float AngleDiff = DotProduct(FwdTarget, Front); + AngleDiff = Acos(Min(Abs(AngleDiff), 1.0f)); + TargetRoll *= (DEGTORAD(10.0f)*TiltOverShoot[index] + f_max_role_angle) * Sin(AngleDiff); + } + + WellBufferMe(TargetRoll, &f_Roll, &f_rollSpeed, 0.15f, 0.07f, false); + Up = CVector(Cos(f_Roll + HALFPI), 0.0f, Sin(f_Roll + HALFPI)); + Up.Normalise(); + Front.Normalise(); + CVector Left = CrossProduct(Up, Front); + Left.Normalise(); + Up = CrossProduct(Front, Left); + Up.Normalise(); + } + + ResetStatics = false; +} + +// Basic Cam on a string algorithm +void +CCam::Cam_On_A_String_Unobscured(const CVector &TargetCoors, float BaseDist) +{ + int id = CamTargetEntity->GetModelIndex(); + float ExtraDist = 0.0f; + if(id == MI_RCRAIDER || id == MI_RCGOBLIN) + ExtraDist = INIT_RC_HELI_HORI_EXTRA; + else if(id == MI_RCBARON) + ExtraDist = INIT_RC_PLANE_HORI_EXTRA; + + CA_MAX_DISTANCE = BaseDist + 0.1f + TheCamera.CarZoomValueSmooth + ExtraDist; + CA_MIN_DISTANCE = Min(BaseDist*0.6f, 3.5f); + if(CA_MIN_DISTANCE > CA_MAX_DISTANCE) + CA_MIN_DISTANCE = CA_MAX_DISTANCE - 0.05f; + + CVector Dist = Source - TargetCoors; + + if(ResetStatics) + Source = TargetCoors + Dist*(CA_MAX_DISTANCE + 1.0f); + + Dist = Source - TargetCoors; + + float Length = Dist.Magnitude2D(); + if(Length < 0.001f){ + // This probably shouldn't happen. reset view + CVector Forward = CamTargetEntity->GetForward(); + Forward.z = 0.0f; + Forward.Normalise(); + Source = TargetCoors - Forward*CA_MAX_DISTANCE; + Dist = Source - TargetCoors; + Length = Dist.Magnitude2D(); + } + + if(Length > CA_MAX_DISTANCE){ + Source.x = TargetCoors.x + Dist.x/Length * CA_MAX_DISTANCE; + Source.y = TargetCoors.y + Dist.y/Length * CA_MAX_DISTANCE; + }else if(Length < CA_MIN_DISTANCE){ + Source.x = TargetCoors.x + Dist.x/Length * CA_MIN_DISTANCE; + Source.y = TargetCoors.y + Dist.y/Length * CA_MIN_DISTANCE; + } +} + +void +CCam::FixCamWhenObscuredByVehicle(const CVector &TargetCoors) +{ + // BUG? is this never reset + static float HeightFixerCarsObscuring = 0.0f; + static float HeightFixerCarsObscuringSpeed = 0.0f; + CColPoint colPoint; + CEntity *entity = nil; + + float HeightTarget = 0.0f; + if(CWorld::ProcessLineOfSight(TargetCoors, Source, colPoint, entity, false, true, false, false, false, false, false)){ + CBaseModelInfo *mi = CModelInfo::GetModelInfo(entity->GetModelIndex()); + HeightTarget = mi->GetColModel()->boundingBox.max.z + 1.0f + TargetCoors.z - Source.z; + if(HeightTarget < 0.0f) + HeightTarget = 0.0f; + } + WellBufferMe(HeightTarget, &HeightFixerCarsObscuring, &HeightFixerCarsObscuringSpeed, 0.2f, 0.025f, false); + Source.z += HeightFixerCarsObscuring; +} + +void +CCam::Process_TopDown(const CVector &CameraTarget, float TargetOrientation, float SpeedVar, float TargetSpeedVar) +{ + FOV = DefaultFOV; + + if(!CamTargetEntity->IsVehicle()) + return; + + float Dist; + float HeightTarget = 0.0f; + static float AdjustHeightTargetMoveBuffer = 0.0f; + static float AdjustHeightTargetMoveSpeed = 0.0f; + static float NearClipDistance = 1.5f; + const float FarClipDistance = 200.0f; + CVector TargetFront, Target; + CVector TestSource, TestTarget; + CColPoint colPoint; + CEntity *entity; + + TargetFront = CameraTarget; + TargetFront.x += 18.0f*CamTargetEntity->GetForward().x*SpeedVar; + TargetFront.y += 18.0f*CamTargetEntity->GetForward().y*SpeedVar; + + if(ResetStatics){ + AdjustHeightTargetMoveBuffer = 0.0f; + AdjustHeightTargetMoveSpeed = 0.0f; + } + + float f = Pow(0.8f, 4.0f); + Target = f*CameraTarget + (1.0f-f)*TargetFront; + if(Mode == MODE_GTACLASSIC) + SpeedVar = TargetSpeedVar; + Source = Target + CVector(0.0f, 0.0f, (40.0f*SpeedVar + 30.0f)*0.8f); + // What is this? looks horrible + if(Mode == MODE_GTACLASSIC) + Source.x += (uint8)(100.0f*CameraTarget.x)/500.0f; + + TestSource = Source; + TestTarget = TestSource; + TestTarget.z = Target.z; + if(CWorld::ProcessLineOfSight(TestTarget, TestSource, colPoint, entity, true, false, false, false, false, false, false)){ + if(Source.z < colPoint.point.z+3.0f) + HeightTarget = colPoint.point.z+3.0f - Source.z; + }else{ + TestSource = Source; + TestTarget = TestSource; + TestTarget.z += 10.0f; + if(CWorld::ProcessLineOfSight(TestTarget, TestSource, colPoint, entity, true, false, false, false, false, false, false)) + if(Source.z < colPoint.point.z+3.0f) + HeightTarget = colPoint.point.z+3.0f - Source.z; + } + WellBufferMe(HeightTarget, &AdjustHeightTargetMoveBuffer, &AdjustHeightTargetMoveSpeed, 0.2f, 0.02f, false); + Source.z += AdjustHeightTargetMoveBuffer; + + if(RwCameraGetFarClipPlane(Scene.camera) > FarClipDistance) + RwCameraSetFarClipPlane(Scene.camera, FarClipDistance); + RwCameraSetNearClipPlane(Scene.camera, NearClipDistance); + + Front = CVector(-0.01f, -0.01f, -1.0f); // look down + Front.Normalise(); + Dist = (Source - CameraTarget).Magnitude(); + m_cvecTargetCoorsForFudgeInter = Dist*Front + Source; + Up = CVector(0.0f, 1.0f, 0.0f); + + ResetStatics = false; +} + +void +CCam::AvoidWallsTopDownPed(const CVector &TargetCoors, const CVector &Offset, float *Adjuster, float *AdjusterSpeed, float yDistLimit) +{ + float Target = 0.0f; + float MaxSpeed = 0.13f; + float Acceleration = 0.015f; + float SpeedMult; + float dy; + CVector TestPoint2; + CVector TestPoint1; + CColPoint colPoint; + CEntity *entity; + + TestPoint2 = TargetCoors + Offset; + TestPoint1 = TargetCoors; + TestPoint1.z = TestPoint2.z; + if(CWorld::ProcessLineOfSight(TestPoint1, TestPoint2, colPoint, entity, true, false, false, false, false, false, false)){ + // What is this even? + dy = TestPoint1.y - colPoint.point.y; + if(dy > yDistLimit) + dy = yDistLimit; + SpeedMult = yDistLimit - Abs(dy/yDistLimit); + + Target = 2.5f; + MaxSpeed += SpeedMult*0.3f; + Acceleration += SpeedMult*0.03f; + } + WellBufferMe(Target, Adjuster, AdjusterSpeed, MaxSpeed, Acceleration, false); +} + +void +CCam::Process_TopDownPed(const CVector &CameraTarget, float TargetOrientation, float, float) +{ + if(!CamTargetEntity->IsPed()) + return; + + float Dist; + float HeightTarget; + static int NumPedPosCountsSoFar = 0; + static float PedAverageSpeed = 0.0f; + static float AdjustHeightTargetMoveBuffer = 0.0f; + static float AdjustHeightTargetMoveSpeed = 0.0f; + static float PedSpeedSoFar = 0.0f; + static float FarClipDistance = 200.0f; + static float NearClipDistance = 1.5f; + static float TargetAdjusterForSouth = 0.0f; + static float TargetAdjusterSpeedForSouth = 0.0f; + static float TargetAdjusterForNorth = 0.0f; + static float TargetAdjusterSpeedForNorth = 0.0f; + static float TargetAdjusterForEast = 0.0f; + static float TargetAdjusterSpeedForEast = 0.0f; + static float TargetAdjusterForWest = 0.0f; + static float TargetAdjusterSpeedForWest = 0.0f; + static CVector PreviousPlayerMoveSpeedVec; + CVector TargetCoors, PlayerMoveSpeed; + CVector TestSource, TestTarget; + CColPoint colPoint; + CEntity *entity; + + FOV = DefaultFOV; + TargetCoors = CameraTarget; + PlayerMoveSpeed = ((CPed*)CamTargetEntity)->GetMoveSpeed(); + + if(ResetStatics){ + PreviousPlayerMoveSpeedVec = PlayerMoveSpeed; + AdjustHeightTargetMoveBuffer = 0.0f; + AdjustHeightTargetMoveSpeed = 0.0f; + NumPedPosCountsSoFar = 0; + PedSpeedSoFar = 0.0f; + PedAverageSpeed = 0.0f; + TargetAdjusterForWest = 0.0f; + TargetAdjusterSpeedForWest = 0.0f; + TargetAdjusterForEast = 0.0f; + TargetAdjusterSpeedForEast = 0.0f; + TargetAdjusterForNorth = 0.0f; + TargetAdjusterSpeedForNorth = 0.0f; + TargetAdjusterForSouth = 0.0f; + TargetAdjusterSpeedForSouth = 0.0f; + } + + if(RwCameraGetFarClipPlane(Scene.camera) > FarClipDistance) + RwCameraSetFarClipPlane(Scene.camera, FarClipDistance); + RwCameraSetNearClipPlane(Scene.camera, NearClipDistance); + + // Average ped speed + NumPedPosCountsSoFar++; + PedSpeedSoFar += PlayerMoveSpeed.Magnitude(); + if(NumPedPosCountsSoFar == 5){ + PedAverageSpeed = 0.4f*PedAverageSpeed + 0.6*(PedSpeedSoFar/5.0f); + NumPedPosCountsSoFar = 0; + PedSpeedSoFar = 0.0f; + } + PreviousPlayerMoveSpeedVec = PlayerMoveSpeed; + + // Zoom out depending on speed + if(PedAverageSpeed > 0.01f && PedAverageSpeed <= 0.04f) + HeightTarget = 2.5f; + else if(PedAverageSpeed > 0.04f && PedAverageSpeed <= 0.145f) + HeightTarget = 4.5f; + else if(PedAverageSpeed > 0.145f) + HeightTarget = 7.0f; + else + HeightTarget = 0.0f; + + // Zoom out if locked on target is far away + if(FindPlayerPed()->m_pPointGunAt){ + Dist = (FindPlayerPed()->m_pPointGunAt->GetPosition() - CameraTarget).Magnitude2D(); + if(Dist > 6.0f) + HeightTarget = Max(HeightTarget, Dist/22.0f*37.0f); + } + + Source = TargetCoors + CVector(0.0f, -1.0f, 9.0f); + + // Collision checks + entity = nil; + TestSource = TargetCoors + CVector(0.0f, -1.0f, 9.0f); + TestTarget = TestSource; + TestTarget.z = TargetCoors.z; + if(CWorld::ProcessLineOfSight(TestTarget, TestSource, colPoint, entity, true, false, false, false, false, false, false)){ + if(TargetCoors.z+9.0f+HeightTarget < colPoint.point.z+3.0f) + HeightTarget = colPoint.point.z+3.0f - (TargetCoors.z+9.0f); + }else{ + TestSource = TargetCoors + CVector(0.0f, -1.0f, 9.0f); + TestTarget = TestSource; + TestSource.z += HeightTarget; + TestTarget.z = TestSource.z + 10.0f; + if(CWorld::ProcessLineOfSight(TestTarget, TestSource, colPoint, entity, true, false, false, false, false, false, false)){ + if(TargetCoors.z+9.0f+HeightTarget < colPoint.point.z+3.0f) + HeightTarget = colPoint.point.z+3.0f - (TargetCoors.z+9.0f); + } + } + + WellBufferMe(HeightTarget, &AdjustHeightTargetMoveBuffer, &AdjustHeightTargetMoveSpeed, 0.3f, 0.03f, false); + Source.z += AdjustHeightTargetMoveBuffer; + + // Wall checks + AvoidWallsTopDownPed(TargetCoors, CVector(0.0f, -3.0f, 3.0f), &TargetAdjusterForSouth, &TargetAdjusterSpeedForSouth, 1.0f); + Source.y += TargetAdjusterForSouth; + AvoidWallsTopDownPed(TargetCoors, CVector(0.0f, 3.0f, 3.0f), &TargetAdjusterForNorth, &TargetAdjusterSpeedForNorth, 1.0f); + Source.y -= TargetAdjusterForNorth; + // BUG: east and west flipped + AvoidWallsTopDownPed(TargetCoors, CVector(3.0f, 0.0f, 3.0f), &TargetAdjusterForWest, &TargetAdjusterSpeedForWest, 1.0f); + Source.x -= TargetAdjusterForWest; + AvoidWallsTopDownPed(TargetCoors, CVector(-3.0f, 0.0f, 3.0f), &TargetAdjusterForEast, &TargetAdjusterSpeedForEast, 1.0f); + Source.x += TargetAdjusterForEast; + + TargetCoors.y = Source.y + 1.0f; + TargetCoors.y += TargetAdjusterForSouth; + TargetCoors.x += TargetAdjusterForEast; + TargetCoors.x -= TargetAdjusterForWest; + + Front = TargetCoors - Source; + Front.Normalise(); +#ifdef FIX_BUGS + if(Front.x == 0.0f && Front.y == 0.0f) + Front.y = 0.0001f; +#else + // someone used = instead of == in the above check by accident + Front.x = 0.0f; +#endif + m_cvecTargetCoorsForFudgeInter = TargetCoors; + Up = CrossProduct(Front, CVector(-1.0f, 0.0f, 0.0f)); + Up.Normalise(); + + ResetStatics = false; +} + +void +CCam::Process_Rocket(const CVector &CameraTarget, float, float, float) +{ + if(!CamTargetEntity->IsPed()) + return; + + float BackOffset = 0.19f; + static bool FailedTestTwelveFramesAgo = false; + RwV3d HeadPos; + CVector TargetCoors; + + FOV = DefaultFOV; + TargetCoors = CameraTarget; + + if(ResetStatics){ + Beta = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; + Alpha = 0.0f; + m_fInitialPlayerOrientation = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; + FailedTestTwelveFramesAgo = false; + // static DPadVertical unused + // static DPadHorizontal unused + m_bCollisionChecksOn = true; + ResetStatics = false; + } + + if(((CPed*)CamTargetEntity)->bIsDucking) + BackOffset = 0.8f; + CamTargetEntity->GetMatrix().UpdateRW(); + CamTargetEntity->UpdateRwFrame(); + CamTargetEntity->UpdateRpHAnim(); + ((CPed*)CamTargetEntity)->m_pedIK.GetComponentPosition(HeadPos, PED_HEAD); + Source = HeadPos; + Source.z += 0.1f; + Source.x -= BackOffset*Cos(m_fInitialPlayerOrientation); + Source.y -= BackOffset*Sin(m_fInitialPlayerOrientation); + + // Look around + bool UseMouse = false; + float MouseX = CPad::GetPad(0)->GetMouseX(); + float MouseY = CPad::GetPad(0)->GetMouseY(); + float LookLeftRight, LookUpDown; + if(MouseX != 0.0f || MouseY != 0.0f){ + UseMouse = true; + LookLeftRight = -3.0f*MouseX; + LookUpDown = 4.0f*MouseY; + }else{ + LookLeftRight = -CPad::GetPad(0)->SniperModeLookLeftRight(); + LookUpDown = CPad::GetPad(0)->SniperModeLookUpDown(); + } + if(UseMouse){ + Beta += TheCamera.m_fMouseAccelHorzntl * LookLeftRight * FOV/80.0f; + Alpha += TheCamera.m_fMouseAccelVertical * LookUpDown * FOV/80.0f; + }else{ + float xdir = LookLeftRight < 0.0f ? -1.0f : 1.0f; + float ydir = LookUpDown < 0.0f ? -1.0f : 1.0f; + Beta += SQR(LookLeftRight/100.0f)*xdir*0.8f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); + Alpha += SQR(LookUpDown/150.0f)*ydir*1.0f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); + } + while(Beta >= PI) Beta -= 2*PI; + while(Beta < -PI) Beta += 2*PI; + if(Alpha > DEGTORAD(60.0f)) Alpha = DEGTORAD(60.0f); + else if(Alpha < -DEGTORAD(89.5f)) Alpha = -DEGTORAD(89.5f); + + TargetCoors.x = 3.0f * Cos(Alpha) * Cos(Beta) + Source.x; + TargetCoors.y = 3.0f * Cos(Alpha) * Sin(Beta) + Source.y; + TargetCoors.z = 3.0f * Sin(Alpha) + Source.z; + Front = TargetCoors - Source; + Front.Normalise(); + Source += Front*0.4f; + + if(m_bCollisionChecksOn){ + if(!CWorld::GetIsLineOfSightClear(TargetCoors, Source, true, true, false, true, false, true, true)){ + RwCameraSetNearClipPlane(Scene.camera, 0.4f); + FailedTestTwelveFramesAgo = true; + }else{ + CVector TestPoint; + TestPoint.x = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Cos(Beta + DEGTORAD(35.0f)) + Source.x; + TestPoint.y = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Sin(Beta + DEGTORAD(35.0f)) + Source.y; + TestPoint.z = 3.0f * Sin(Alpha - DEGTORAD(20.0f)) + Source.z; + if(!CWorld::GetIsLineOfSightClear(TestPoint, Source, true, true, false, true, false, true, true)){ + RwCameraSetNearClipPlane(Scene.camera, 0.4f); + FailedTestTwelveFramesAgo = true; + }else{ + TestPoint.x = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Cos(Beta - DEGTORAD(35.0f)) + Source.x; + TestPoint.y = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Sin(Beta - DEGTORAD(35.0f)) + Source.y; + TestPoint.z = 3.0f * Sin(Alpha - DEGTORAD(20.0f)) + Source.z; + if(!CWorld::GetIsLineOfSightClear(TestPoint, Source, true, true, false, true, false, true, true)){ + RwCameraSetNearClipPlane(Scene.camera, 0.4f); + FailedTestTwelveFramesAgo = true; + }else + FailedTestTwelveFramesAgo = false; + } + } + } + + if(FailedTestTwelveFramesAgo) + RwCameraSetNearClipPlane(Scene.camera, 0.4f); + Source -= Front*0.4f; + + GetVectorsReadyForRW(); + float Rotation = CGeneral::GetATanOfXY(Front.x, Front.y) - HALFPI; + ((CPed*)TheCamera.pTargetEntity)->m_fRotationCur = Rotation; + ((CPed*)TheCamera.pTargetEntity)->m_fRotationDest = Rotation; +} + +float fDuckingBackOffset = 0.5f; +float fDuckingRightOffset = 0.18f; + +void +CCam::Process_M16_1stPerson(const CVector &CameraTarget, float, float, float) +{ + if(!CamTargetEntity->IsPed()) + return; + + float BackOffset = 0.3f; + static bool FailedTestTwelveFramesAgo = false; + RwV3d HeadPos; + CVector TargetCoors; + + bool isAttached = ((CPed*)CamTargetEntity)->IsPlayer() && ((CPed*)CamTargetEntity)->m_attachedTo; + + FOV = DefaultFOV; + TargetCoors = CameraTarget; + + if(ResetStatics){ + if(isAttached) + Beta = 0.0f; + else + Beta = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; + Alpha = 0.0f; + m_fInitialPlayerOrientation = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; + FailedTestTwelveFramesAgo = false; + // static DPadVertical unused + // static DPadHorizontal unused + m_bCollisionChecksOn = true; + ResetStatics = false; + } + + // Look around + bool UseMouse = false; + float MouseX = CPad::GetPad(0)->GetMouseX(); + float MouseY = CPad::GetPad(0)->GetMouseY(); + float LookLeftRight, LookUpDown; + if(MouseX != 0.0f || MouseY != 0.0f){ + UseMouse = true; + LookLeftRight = -3.0f*MouseX; + LookUpDown = 4.0f*MouseY; + }else{ + LookLeftRight = -CPad::GetPad(0)->SniperModeLookLeftRight(); + LookUpDown = CPad::GetPad(0)->SniperModeLookUpDown(); + } + if(UseMouse){ + Beta += TheCamera.m_fMouseAccelHorzntl * LookLeftRight * FOV/80.0f; + Alpha += TheCamera.m_fMouseAccelVertical * LookUpDown * FOV/80.0f; + }else if(Mode == MODE_HELICANNON_1STPERSON){ + LookLeftRight /= 128.0f; + LookUpDown /= 128.0f; + Beta += LookLeftRight*Abs(LookLeftRight)*0.56f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); + Alpha += LookUpDown*Abs(LookUpDown)*0.48f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); + }else{ + float xdir = LookLeftRight < 0.0f ? -1.0f : 1.0f; + float ydir = LookUpDown < 0.0f ? -1.0f : 1.0f; + Beta += SQR(LookLeftRight/100.0f)*xdir*0.8f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); + Alpha += SQR(LookUpDown/150.0f)*ydir*1.0f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); + } + if (!isAttached) { + while(Beta >= TWOPI) Beta -= TWOPI; + while(Beta < 0) Beta += TWOPI; + } + if(Alpha > DEGTORAD(60.0f)) Alpha = DEGTORAD(60.0f); + else if(Alpha < -DEGTORAD(89.5f)) Alpha = -DEGTORAD(89.5f); + + if(((CPed*)CamTargetEntity)->bIsDucking) + BackOffset = 0.8f; + if(isAttached){ + CMatrix mat, rot; + CPed *TargetPed = (CPed*)CamTargetEntity; + TargetPed->PositionAttachedPed(); + CamTargetEntity->GetMatrix().UpdateRW(); + CamTargetEntity->UpdateRwFrame(); + CamTargetEntity->UpdateRpHAnim(); + + HeadPos.x = 0.0f; + HeadPos.y = 0.0f; + HeadPos.z = 0.0f; + TargetPed->m_pedIK.GetComponentPosition(HeadPos, PED_HEAD); + Source = HeadPos; + Source += 0.1f*CamTargetEntity->GetUp(); + Source -= BackOffset*CamTargetEntity->GetForward(); + + if(TargetPed->m_attachRotStep < PI){ + if(Beta > TargetPed->m_attachRotStep){ + Beta = TargetPed->m_attachRotStep; + CAutomobile *heli = (CAutomobile*)TargetPed->m_attachedTo; + if(heli->IsVehicle() && heli->IsCar() && heli->IsRealHeli() && heli->m_fHeliOrientation > 0.0f){ + float heliOrient = heli->m_fHeliOrientation + CTimer::GetTimeStep()*0.01f; + if(heliOrient < 0.0f) heliOrient += TWOPI; + else if(heliOrient > TWOPI) heliOrient -= TWOPI; + heli->SetHeliOrientation(heliOrient); + } + }else if(Beta < -TargetPed->m_attachRotStep){ + Beta = -TargetPed->m_attachRotStep; + CAutomobile *heli = (CAutomobile*)TargetPed->m_attachedTo; + if(heli->IsVehicle() && heli->IsCar() && heli->IsRealHeli() && heli->m_fHeliOrientation > 0.0f){ + float heliOrient = heli->m_fHeliOrientation - CTimer::GetTimeStep()*0.01f; + if(heliOrient < 0.0f) heliOrient += TWOPI; + else if(heliOrient > TWOPI) heliOrient -= TWOPI; + heli->SetHeliOrientation(heliOrient); + } + } + }else{ + while(Beta < -PI) Beta += TWOPI; + while(Beta >= PI) Beta -= TWOPI; + } + + mat = TargetPed->m_attachedTo->GetMatrix(); + rot.SetRotateX(Alpha); + switch(TargetPed->m_attachType){ + case 0: rot.RotateZ(Beta); break; + case 1: rot.RotateZ(Beta + HALFPI); break; + case 2: rot.RotateZ(Beta + PI); break; + case 3: rot.RotateZ(Beta - HALFPI); break; + } + mat = mat * rot; + Front = mat.GetForward(); + Up = mat.GetUp(); + TargetCoors = Source + 3.0f*Front; + RwCameraSetNearClipPlane(Scene.camera, 0.4f); + + float Rotation = CGeneral::GetATanOfXY(Front.x, Front.y) - HALFPI; + ((CPed*)TheCamera.pTargetEntity)->m_fRotationCur = Rotation; + ((CPed*)TheCamera.pTargetEntity)->m_fRotationDest = Rotation; + }else{ + CamTargetEntity->GetMatrix().UpdateRW(); + CamTargetEntity->UpdateRwFrame(); + CamTargetEntity->UpdateRpHAnim(); + HeadPos.x = 0.0f; + HeadPos.y = 0.0f; + HeadPos.z = 0.0f; + ((CPed*)CamTargetEntity)->m_pedIK.GetComponentPosition(HeadPos, PED_HEAD); + Source = HeadPos; + Source.z += 0.1f; + if(((CPed*)CamTargetEntity)->bIsDucking){ + Source.x -= fDuckingBackOffset*CamTargetEntity->GetForward().x; + Source.y -= fDuckingBackOffset*CamTargetEntity->GetForward().y; + Source.x -= fDuckingRightOffset*CamTargetEntity->GetRight().x; + Source.y -= fDuckingRightOffset*CamTargetEntity->GetRight().y; + }else{ + Source.x -= BackOffset*CamTargetEntity->GetForward().x; + Source.y -= BackOffset*CamTargetEntity->GetForward().y; + } + + TargetCoors.x = 3.0f * Cos(Alpha) * Cos(Beta) + Source.x; + TargetCoors.y = 3.0f * Cos(Alpha) * Sin(Beta) + Source.y; + TargetCoors.z = 3.0f * Sin(Alpha) + Source.z; + Front = TargetCoors - Source; + Front.Normalise(); + Source += Front*0.4f; + + if(m_bCollisionChecksOn){ + if(!CWorld::GetIsLineOfSightClear(TargetCoors, Source, true, true, false, true, false, true, true)){ + RwCameraSetNearClipPlane(Scene.camera, 0.4f); + FailedTestTwelveFramesAgo = true; + }else{ + CVector TestPoint; + TestPoint.x = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Cos(Beta + DEGTORAD(35.0f)) + Source.x; + TestPoint.y = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Sin(Beta + DEGTORAD(35.0f)) + Source.y; + TestPoint.z = 3.0f * Sin(Alpha - DEGTORAD(20.0f)) + Source.z; + if(!CWorld::GetIsLineOfSightClear(TestPoint, Source, true, true, false, true, false, true, true)){ + RwCameraSetNearClipPlane(Scene.camera, 0.4f); + FailedTestTwelveFramesAgo = true; + }else{ + TestPoint.x = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Cos(Beta - DEGTORAD(35.0f)) + Source.x; + TestPoint.y = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Sin(Beta - DEGTORAD(35.0f)) + Source.y; + TestPoint.z = 3.0f * Sin(Alpha - DEGTORAD(20.0f)) + Source.z; + if(!CWorld::GetIsLineOfSightClear(TestPoint, Source, true, true, false, true, false, true, true)){ + RwCameraSetNearClipPlane(Scene.camera, 0.4f); + FailedTestTwelveFramesAgo = true; + }else + FailedTestTwelveFramesAgo = false; + } + } + } + + if(FailedTestTwelveFramesAgo) + RwCameraSetNearClipPlane(Scene.camera, 0.4f); + Source -= Front*0.4f; + + GetVectorsReadyForRW(); + float Rotation = CGeneral::GetATanOfXY(Front.x, Front.y) - HALFPI; + ((CPed*)TheCamera.pTargetEntity)->m_fRotationCur = Rotation; + ((CPed*)TheCamera.pTargetEntity)->m_fRotationDest = Rotation; + } +} + +float fBike1stPersonOffsetZ = 0.15f; + +void +CCam::Process_1stPerson(const CVector &CameraTarget, float TargetOrientation, float SpeedVar, float TargetSpeedVar) +{ + float BackOffset = 0.3f; + static float DontLookThroughWorldFixer = 0.0f; + CVector TargetCoors; + + FOV = DefaultFOV; + TargetCoors = CameraTarget; + if(CamTargetEntity->m_rwObject == nil) + return; + + if(ResetStatics){ + Beta = TargetOrientation; + Alpha = 0.0f; + m_fInitialPlayerOrientation = TargetOrientation; + if(CamTargetEntity->IsPed()){ + Beta = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; + Alpha = 0.0f; + m_fInitialPlayerOrientation = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; + } + TheCamera.m_fAvoidTheGeometryProbsTimer = 0.0f; + DontLookThroughWorldFixer = 0.0f; + } + + if(CamTargetEntity->IsPed()){ + static bool FailedTestTwelveFramesAgo = false; + RwV3d HeadPos; + + TargetCoors = CameraTarget; + + if(ResetStatics){ + Beta = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; + Alpha = 0.0f; + m_fInitialPlayerOrientation = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; + FailedTestTwelveFramesAgo = false; + // static DPadVertical unused + // static DPadHorizontal unused + m_bCollisionChecksOn = true; + ResetStatics = false; + } + + CamTargetEntity->GetMatrix().UpdateRW(); + CamTargetEntity->UpdateRwFrame(); + CamTargetEntity->UpdateRpHAnim(); + + ((CPed*)CamTargetEntity)->m_pedIK.GetComponentPosition(HeadPos, PED_HEAD); + Source = HeadPos; + Source.z += 0.1f; + if(((CPed*)CamTargetEntity)->bIsDucking){ + Source.x -= fDuckingBackOffset*CamTargetEntity->GetForward().x; + Source.y -= fDuckingBackOffset*CamTargetEntity->GetForward().y; + Source.x -= fDuckingRightOffset*CamTargetEntity->GetRight().x; + Source.y -= fDuckingRightOffset*CamTargetEntity->GetRight().y; + }else{ + Source.x -= BackOffset*CamTargetEntity->GetForward().x; + Source.y -= BackOffset*CamTargetEntity->GetForward().y; + } + + float LookLeftRight, LookUpDown; + LookLeftRight = -CPad::GetPad(0)->LookAroundLeftRight(); + LookUpDown = CPad::GetPad(0)->LookAroundUpDown(); + float xdir = LookLeftRight < 0.0f ? -1.0f : 1.0f; + float ydir = LookUpDown < 0.0f ? -1.0f : 1.0f; + Beta += SQR(LookLeftRight/100.0f)*xdir*0.8f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); + Alpha += SQR(LookUpDown/150.0f)*ydir*1.0f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); + while(Beta >= PI) Beta -= 2*PI; + while(Beta < -PI) Beta += 2*PI; + if(Alpha > DEGTORAD(60.0f)) Alpha = DEGTORAD(60.0f); + else if(Alpha < -DEGTORAD(89.5f)) Alpha = -DEGTORAD(89.5f); + + TargetCoors.x = 3.0f * Cos(Alpha) * Cos(Beta) + Source.x; + TargetCoors.y = 3.0f * Cos(Alpha) * Sin(Beta) + Source.y; + TargetCoors.z = 3.0f * Sin(Alpha) + Source.z; + Front = TargetCoors - Source; + Front.Normalise(); + Source += Front*0.4f; + + if(m_bCollisionChecksOn){ + if(!CWorld::GetIsLineOfSightClear(TargetCoors, Source, true, true, false, true, false, true, true)){ + RwCameraSetNearClipPlane(Scene.camera, 0.4f); + FailedTestTwelveFramesAgo = true; + }else{ + CVector TestPoint; + TestPoint.x = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Cos(Beta + DEGTORAD(35.0f)) + Source.x; + TestPoint.y = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Sin(Beta + DEGTORAD(35.0f)) + Source.y; + TestPoint.z = 3.0f * Sin(Alpha - DEGTORAD(20.0f)) + Source.z; + if(!CWorld::GetIsLineOfSightClear(TestPoint, Source, true, true, false, true, false, true, true)){ + RwCameraSetNearClipPlane(Scene.camera, 0.4f); + FailedTestTwelveFramesAgo = true; + }else{ + TestPoint.x = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Cos(Beta - DEGTORAD(35.0f)) + Source.x; + TestPoint.y = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Sin(Beta - DEGTORAD(35.0f)) + Source.y; + TestPoint.z = 3.0f * Sin(Alpha - DEGTORAD(20.0f)) + Source.z; + if(!CWorld::GetIsLineOfSightClear(TestPoint, Source, true, true, false, true, false, true, true)){ + RwCameraSetNearClipPlane(Scene.camera, 0.4f); + FailedTestTwelveFramesAgo = true; + }else + FailedTestTwelveFramesAgo = false; + } + } + } + + if(FailedTestTwelveFramesAgo) + RwCameraSetNearClipPlane(Scene.camera, 0.4f); + Source -= Front*0.4f; + + GetVectorsReadyForRW(); + float Rotation = CGeneral::GetATanOfXY(Front.x, Front.y) - HALFPI; + ((CPed*)TheCamera.pTargetEntity)->m_fRotationCur = Rotation; + ((CPed*)TheCamera.pTargetEntity)->m_fRotationDest = Rotation; + }else{ + assert(CamTargetEntity->IsVehicle()); + + if(((CVehicle*)CamTargetEntity)->IsBike() && + (((CBike*)CamTargetEntity)->bWheelieCam || TheCamera.m_fAvoidTheGeometryProbsTimer > 0.0f)){ + if(CPad::GetPad(0)->GetLeftShoulder2() || CPad::GetPad(0)->GetRightShoulder2()){ + TheCamera.m_fAvoidTheGeometryProbsTimer = 0.0f; + ((CBike*)CamTargetEntity)->bWheelieCam = false; + }else if(Process_WheelCam(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar)){ + if(((CBike*)CamTargetEntity)->bWheelieCam) + TheCamera.m_fAvoidTheGeometryProbsTimer = 50.0f; + else{ + TheCamera.m_fAvoidTheGeometryProbsTimer -= CTimer::GetTimeStep(); + ((CBike*)CamTargetEntity)->bWheelieCam = true; + } + return; + }else{ + TheCamera.m_fAvoidTheGeometryProbsTimer = 0.0f; + ((CBike*)CamTargetEntity)->bWheelieCam = false; + } + } + + CMatrix *matrix = &CamTargetEntity->GetMatrix(); + if(((CVehicle*)CamTargetEntity)->IsBike()){ + ((CBike*)CamTargetEntity)->CalculateLeanMatrix(); + matrix = &((CBike*)CamTargetEntity)->m_leanMatrix; + } + + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(CamTargetEntity->GetModelIndex()); + CVector CamPos = mi->GetFrontSeatPosn(); + CamPos.x = 0.0f; + CamPos.y += 0.08f; + CamPos.z += 0.62f; + FOV = 60.0f; + Source = Multiply3x3(*matrix, CamPos); + Source += CamTargetEntity->GetPosition(); + if(((CVehicle*)CamTargetEntity)->IsBoat()) + Source.z += 0.5f; + else if(((CVehicle*)CamTargetEntity)->IsBike() && ((CVehicle*)CamTargetEntity)->pDriver){ + CVector Neck(0.0f, 0.0f, 0.0f); + ((CVehicle*)CamTargetEntity)->pDriver->m_pedIK.GetComponentPosition(Neck, PED_NECK); + Neck += ((CVehicle*)CamTargetEntity)->m_vecMoveSpeed * CTimer::GetTimeStep(); + Source.z = Neck.z + fBike1stPersonOffsetZ; + } + + if(((CVehicle*)CamTargetEntity)->IsUpsideDown()){ + if(DontLookThroughWorldFixer < 0.5f) + DontLookThroughWorldFixer += 0.03f; + else + DontLookThroughWorldFixer = 0.5f; + }else{ + if(DontLookThroughWorldFixer < 0.0f) +#ifdef FIX_BUGS + DontLookThroughWorldFixer += 0.03f; +#else + DontLookThroughWorldFixer -= 0.03f; +#endif + else + DontLookThroughWorldFixer = 0.0f; + } + Source.z += DontLookThroughWorldFixer; + Front = matrix->GetForward(); + Front.Normalise(); + Up = matrix->GetUp(); + Up.Normalise(); + CVector Right = CrossProduct(Front, Up); + Right.Normalise(); + Up = CrossProduct(Right, Front); + Up.Normalise(); + } + + ResetStatics = false; +} + +static CVector vecHeadCamOffset(0.06f, 0.05f, 0.0f); + +void +CCam::Process_1rstPersonPedOnPC(const CVector&, float TargetOrientation, float, float) +{ + // static int DontLookThroughWorldFixer = 0; // unused + static CVector InitialHeadPos; + + if(Mode != MODE_SNIPER_RUNABOUT) + FOV = DefaultFOV; + TheCamera.m_1rstPersonRunCloseToAWall = false; + if(CamTargetEntity->m_rwObject == nil) + return; + + if(CamTargetEntity->IsPed()){ + // static bool FailedTestTwelveFramesAgo = false; // unused + CVector HeadPos = vecHeadCamOffset; + CVector TargetCoors; + + ((CPed*)CamTargetEntity)->TransformToNode(HeadPos, PED_HEAD); + RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(CamTargetEntity->GetClump()); + int32 idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_HEAD)); + RwMatrix *mats = RpHAnimHierarchyGetMatrixArray(hier); + RwV3dTransformPoints(&HeadPos, &HeadPos, 1, &mats[idx]); + RwV3d scl = { 0.0f, 0.0f, 0.0f }; + RwMatrixScale(&mats[idx], &scl, rwCOMBINEPRECONCAT); + + if(ResetStatics){ + Beta = TargetOrientation; + Alpha = 0.0f; + m_fInitialPlayerOrientation = TargetOrientation; + if(CamTargetEntity->IsPed()){ // useless check + Beta = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; + Alpha = 0.0f; + m_fInitialPlayerOrientation = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; + // FailedTestTwelveFramesAgo = false; + m_bCollisionChecksOn = true; + } + // DontLookThroughWorldFixer = false; + m_vecBufferedPlayerBodyOffset = HeadPos; + InitialHeadPos = HeadPos; + } + + m_vecBufferedPlayerBodyOffset.y = HeadPos.y; + + if(TheCamera.m_bHeadBob){ + m_vecBufferedPlayerBodyOffset.x = + TheCamera.m_fGaitSwayBuffer * m_vecBufferedPlayerBodyOffset.x + + (1.0f-TheCamera.m_fGaitSwayBuffer) * HeadPos.x; + m_vecBufferedPlayerBodyOffset.z = + TheCamera.m_fGaitSwayBuffer * m_vecBufferedPlayerBodyOffset.z + + (1.0f-TheCamera.m_fGaitSwayBuffer) * HeadPos.z; + HeadPos = (CamTargetEntity->GetMatrix() * m_vecBufferedPlayerBodyOffset); + }else{ + float HeadDelta = (HeadPos - InitialHeadPos).Magnitude2D(); + CVector Fwd = CamTargetEntity->GetForward(); + Fwd.z = 0.0f; + Fwd.Normalise(); + HeadPos = HeadDelta*1.23f*Fwd + CamTargetEntity->GetPosition(); + HeadPos.z += 0.59f; + } + Source = HeadPos; + + // unused: + // ((CPed*)CamTargetEntity)->m_pedIK.GetComponentPosition(MidPos, PED_MID); + // Source - MidPos; + + // Look around + bool UseMouse = false; + float MouseX = CPad::GetPad(0)->GetMouseX(); + float MouseY = CPad::GetPad(0)->GetMouseY(); + float LookLeftRight, LookUpDown; + if(MouseX != 0.0f || MouseY != 0.0f){ + UseMouse = true; + LookLeftRight = -3.0f*MouseX; + LookUpDown = 4.0f*MouseY; + }else{ + LookLeftRight = -CPad::GetPad(0)->LookAroundLeftRight(); + LookUpDown = CPad::GetPad(0)->LookAroundUpDown(); + } + if(UseMouse){ + Beta += TheCamera.m_fMouseAccelHorzntl * LookLeftRight * FOV/80.0f; + Alpha += TheCamera.m_fMouseAccelVertical * LookUpDown * FOV/80.0f; + }else{ + float xdir = LookLeftRight < 0.0f ? -1.0f : 1.0f; + float ydir = LookUpDown < 0.0f ? -1.0f : 1.0f; + Beta += SQR(LookLeftRight/100.0f)*xdir*0.8f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); + Alpha += SQR(LookUpDown/150.0f)*ydir*1.0f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); + } + while(Beta >= PI) Beta -= 2*PI; + while(Beta < -PI) Beta += 2*PI; + if(Alpha > DEGTORAD(60.0f)) Alpha = DEGTORAD(60.0f); + else if(Alpha < -DEGTORAD(89.5f)) Alpha = -DEGTORAD(89.5f); + + if(((CPed*)CamTargetEntity)->IsPlayer() && ((CPed*)CamTargetEntity)->m_attachedTo){ + CPed *pedTarget = ((CPed*)CamTargetEntity); + float NewBeta; + switch(pedTarget->m_attachType){ + case 0: + NewBeta = pedTarget->GetForward().Heading() + HALFPI; + break; + case 1: + NewBeta = pedTarget->GetForward().Heading() + PI; + break; + case 2: + NewBeta = pedTarget->GetForward().Heading() - HALFPI; + break; + case 3: + NewBeta = pedTarget->GetForward().Heading(); + break; + } + + float BetaOffset = Beta - NewBeta; + if(BetaOffset > PI) BetaOffset -= TWOPI; + else if(BetaOffset < PI) BetaOffset += TWOPI; + + BetaOffset = Clamp(BetaOffset, -pedTarget->m_attachRotStep, pedTarget->m_attachRotStep); + Beta = NewBeta + BetaOffset; + } + + TargetCoors.x = 3.0f * Cos(Alpha) * Cos(Beta) + Source.x; + TargetCoors.y = 3.0f * Cos(Alpha) * Sin(Beta) + Source.y; + TargetCoors.z = 3.0f * Sin(Alpha) + Source.z; + Front = TargetCoors - Source; + Front.Normalise(); + Source += Front*0.4f; + + TheCamera.m_AlphaForPlayerAnim1rstPerson = Alpha; + + GetVectorsReadyForRW(); + + float Heading = Front.Heading(); + ((CPed*)TheCamera.pTargetEntity)->m_fRotationCur = Heading; + ((CPed*)TheCamera.pTargetEntity)->m_fRotationDest = Heading; + TheCamera.pTargetEntity->SetHeading(Heading); + TheCamera.pTargetEntity->GetMatrix().UpdateRW(); + + if(Mode == MODE_SNIPER_RUNABOUT){ + // no mouse wheel FOV buffering here like in normal sniper mode + if(CPad::GetPad(0)->SniperZoomIn() || CPad::GetPad(0)->SniperZoomOut()){ + if(CPad::GetPad(0)->SniperZoomOut()) + FOV *= (255.0f*CTimer::GetTimeStep() + 10000.0f) / 10000.0f; + else + FOV /= (255.0f*CTimer::GetTimeStep() + 10000.0f) / 10000.0f; + } + + TheCamera.SetMotionBlur(180, 255, 180, 120, MOTION_BLUR_SNIPER); + + if(FOV > DefaultFOV) + FOV = DefaultFOV; + if(FOV < 15.0f) + FOV = 15.0f; + } + } + + ResetStatics = false; + RwCameraSetNearClipPlane(Scene.camera, 0.05f); +} + +float fCameraNearClipMult = 0.15f; + +void +CCam::Process_Sniper(const CVector &CameraTarget, float TargetOrientation, float, float) +{ + if(!CamTargetEntity->IsPed()) + return; + + float BackOffset = 0.19f; + static bool FailedTestTwelveFramesAgo = false; + RwV3d HeadPos; + CVector TargetCoors; + TargetCoors = CameraTarget; + + static float TargetFOV = 0.0f; + + if(ResetStatics){ + Beta = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; + Alpha = 0.0f; + m_fInitialPlayerOrientation = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; + FailedTestTwelveFramesAgo = false; + // static DPadVertical unused + // static DPadHorizontal unused + m_bCollisionChecksOn = true; + FOVSpeed = 0.0f; + TargetFOV = FOV; + ResetStatics = false; + } + + if(((CPed*)CamTargetEntity)->bIsDucking) + BackOffset = 0.8f; + CamTargetEntity->GetMatrix().UpdateRW(); + CamTargetEntity->UpdateRwFrame(); + CamTargetEntity->UpdateRpHAnim(); + ((CPed*)CamTargetEntity)->m_pedIK.GetComponentPosition(HeadPos, PED_HEAD); + Source = HeadPos; + Source.z += 0.1f; + if(((CPed*)CamTargetEntity)->bIsDucking){ + Source.x -= fDuckingBackOffset*CamTargetEntity->GetForward().x; + Source.y -= fDuckingBackOffset*CamTargetEntity->GetForward().y; + Source.x -= fDuckingRightOffset*CamTargetEntity->GetRight().x; + Source.y -= fDuckingRightOffset*CamTargetEntity->GetRight().y; + }else{ + Source.x -= BackOffset*CamTargetEntity->GetForward().x; + Source.y -= BackOffset*CamTargetEntity->GetForward().y; + } + + // Look around + bool UseMouse = false; + float MouseX = CPad::GetPad(0)->GetMouseX(); + float MouseY = CPad::GetPad(0)->GetMouseY(); + float LookLeftRight, LookUpDown; + if(MouseX != 0.0f || MouseY != 0.0f){ + UseMouse = true; + LookLeftRight = -3.0f*MouseX; + LookUpDown = 4.0f*MouseY; + }else{ + LookLeftRight = -CPad::GetPad(0)->SniperModeLookLeftRight(); + LookUpDown = CPad::GetPad(0)->SniperModeLookUpDown(); + } + if(UseMouse){ + Beta += TheCamera.m_fMouseAccelHorzntl * LookLeftRight * FOV/80.0f; + Alpha += TheCamera.m_fMouseAccelVertical * LookUpDown * FOV/80.0f; + }else{ + float xdir = LookLeftRight < 0.0f ? -1.0f : 1.0f; + float ydir = LookUpDown < 0.0f ? -1.0f : 1.0f; + Beta += SQR(LookLeftRight/100.0f)*xdir*0.8f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); + Alpha += SQR(LookUpDown/150.0f)*ydir*1.0f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); + } + while(Beta >= PI) Beta -= 2*PI; + while(Beta < -PI) Beta += 2*PI; + if(Alpha > DEGTORAD(60.0f)) Alpha = DEGTORAD(60.0f); + else if(Alpha < -DEGTORAD(89.5f)) Alpha = -DEGTORAD(89.5f); + + TargetCoors.x = 3.0f * Cos(Alpha) * Cos(Beta) + Source.x; + TargetCoors.y = 3.0f * Cos(Alpha) * Sin(Beta) + Source.y; + TargetCoors.z = 3.0f * Sin(Alpha) + Source.z; + + UseMouse = false; + int ZoomInButton = ControlsManager.GetMouseButtonAssociatedWithAction(PED_SNIPER_ZOOM_IN); + int ZoomOutButton = ControlsManager.GetMouseButtonAssociatedWithAction(PED_SNIPER_ZOOM_OUT); + if(ZoomInButton == rsMOUSEWHEELUPBUTTON || ZoomInButton == rsMOUSEWHEELDOWNBUTTON || ZoomOutButton == rsMOUSEWHEELUPBUTTON || ZoomOutButton == rsMOUSEWHEELDOWNBUTTON){ + if(CPad::GetPad(0)->GetMouseWheelUp() || CPad::GetPad(0)->GetMouseWheelDown()){ + if(CPad::GetPad(0)->SniperZoomIn()){ + TargetFOV = FOV - 10.0f; + UseMouse = true; + } + if(CPad::GetPad(0)->SniperZoomOut()){ + TargetFOV = FOV + 10.0f; + UseMouse = true; + } + } + } + if((CPad::GetPad(0)->SniperZoomIn() || CPad::GetPad(0)->SniperZoomOut()) && !UseMouse){ + if(CPad::GetPad(0)->SniperZoomOut()){ + FOV *= (255.0f*CTimer::GetTimeStep() + 10000.0f) / 10000.0f; + TargetFOV = FOV; + FOVSpeed = 0.0f; + }else{ + FOV /= (255.0f*CTimer::GetTimeStep() + 10000.0f) / 10000.0f; + TargetFOV = FOV; + FOVSpeed = 0.0f; + } + }else{ + if(Abs(TargetFOV - FOV) > 0.5f) + WellBufferMe(TargetFOV, &FOV, &FOVSpeed, 0.5f, 0.25f, false); + else + FOVSpeed = 0.0f; + } + + TheCamera.SetMotionBlur(180, 255, 180, 120, MOTION_BLUR_SNIPER); + + if(FOV > DefaultFOV) + FOV = DefaultFOV; + if(Mode == MODE_CAMERA){ + if(FOV < 3.0f) + FOV = 3.0f; + }else{ + if(FOV < 15.0f) + FOV = 15.0f; + } + + Front = TargetCoors - Source; + Front.Normalise(); + Source += Front*0.4f; + + if(m_bCollisionChecksOn){ + if(!CWorld::GetIsLineOfSightClear(TargetCoors, Source, true, true, false, true, false, true, true)){ + RwCameraSetNearClipPlane(Scene.camera, 0.4f); + FailedTestTwelveFramesAgo = true; + }else{ + CVector TestPoint; + TestPoint.x = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Cos(Beta + DEGTORAD(35.0f)) + Source.x; + TestPoint.y = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Sin(Beta + DEGTORAD(35.0f)) + Source.y; + TestPoint.z = 3.0f * Sin(Alpha - DEGTORAD(20.0f)) + Source.z; + if(!CWorld::GetIsLineOfSightClear(TestPoint, Source, true, true, false, true, false, true, true)){ + RwCameraSetNearClipPlane(Scene.camera, 0.4f); + FailedTestTwelveFramesAgo = true; + }else{ + TestPoint.x = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Cos(Beta - DEGTORAD(35.0f)) + Source.x; + TestPoint.y = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Sin(Beta - DEGTORAD(35.0f)) + Source.y; + TestPoint.z = 3.0f * Sin(Alpha - DEGTORAD(20.0f)) + Source.z; + if(!CWorld::GetIsLineOfSightClear(TestPoint, Source, true, true, false, true, false, true, true)){ + RwCameraSetNearClipPlane(Scene.camera, 0.4f); + FailedTestTwelveFramesAgo = true; + }else + FailedTestTwelveFramesAgo = false; + } + } + } + + if(FailedTestTwelveFramesAgo) + RwCameraSetNearClipPlane(Scene.camera, 0.4f); + else if(Mode == MODE_CAMERA) + RwCameraSetNearClipPlane(Scene.camera, ((15.0f - Min(FOV, 15.0f))*fCameraNearClipMult + 1.0f)*DEFAULT_NEAR); + Source -= Front*0.4f; + + GetVectorsReadyForRW(); + float Rotation = CGeneral::GetATanOfXY(Front.x, Front.y) - HALFPI; + ((CPed*)TheCamera.pTargetEntity)->m_fRotationCur = Rotation; + ((CPed*)TheCamera.pTargetEntity)->m_fRotationDest = Rotation; +} + +float INIT_SYPHON_GROUND_DIST = 2.419f; +float INIT_SYPHON_ALPHA_OFFSET = -DEGTORAD(3.0f); +float INIT_SYPHON_DEGREE_OFFSET = -DEGTORAD(30.0f); +float FrontOffsetSyphon = -DEGTORAD(25.5f); // unused +float INIT_SYPHON_Z_OFFSET = -0.5f; + +void +CCam::Process_Syphon(const CVector &CameraTarget, float, float, float) +{ + FOV = DefaultFOV; + + if(!CamTargetEntity->IsPed()) + return; + + static bool CameraObscured = false; + // unused FailedClippingTestPrevously + static float BetaOffset = INIT_SYPHON_DEGREE_OFFSET; + // unused AngleToGoTo + // unused AngleToGoToSpeed + // unused DistBetweenPedAndPlayerPreviouslyOn + static float HeightDown = INIT_SYPHON_Z_OFFSET; + static float AlphaOffset = INIT_SYPHON_ALPHA_OFFSET; + static bool NegateBetaOffset = true; + CVector TargetCoors; + float fAimingDist; + float TargetAlpha; + + bool StandingOnMovingThing = false; + TargetCoors = CameraTarget; + AlphaOffset = INIT_SYPHON_ALPHA_OFFSET; + float GroundDist = INIT_SYPHON_GROUND_DIST; + + while(Beta >= PI) Beta -= 2*PI; + while(Beta < -PI) Beta += 2*PI; + + float NewBeta = CGeneral::GetATanOfXY(TheCamera.m_cvecAimingTargetCoors.x - TargetCoors.x, TheCamera.m_cvecAimingTargetCoors.y - TargetCoors.y) + PI; + if(ResetStatics){ + BetaOffset = INIT_SYPHON_DEGREE_OFFSET; + Beta = CGeneral::GetATanOfXY(Source.x - TargetCoors.x, Source.y - TargetCoors.y); + // some unuseds + ResetStatics = false; + } + if(NegateBetaOffset) + BetaOffset = -INIT_SYPHON_DEGREE_OFFSET; + Beta = NewBeta + BetaOffset; + Source = TargetCoors; + Source.x += GroundDist*Cos(Beta); + Source.y += GroundDist*Sin(Beta); + CPhysical *ground = (CPhysical*)((CPed*)CamTargetEntity)->m_pCurSurface; + if(ground && (ground->IsVehicle() || ground->IsObject())) + StandingOnMovingThing = true; + TargetCoors.z += m_fSyphonModeTargetZOffSet; + + bool PlayerTooClose = false; + fAimingDist = (TheCamera.m_cvecAimingTargetCoors - TargetCoors).Magnitude2D(); + if(fAimingDist < 6.5f){ + fAimingDist = 6.5f; + PlayerTooClose = true; + } + TargetAlpha = CGeneral::GetATanOfXY(fAimingDist, TheCamera.m_cvecAimingTargetCoors.z - TargetCoors.z); + if(ResetStatics) // BUG: can never happen + Alpha = -TargetAlpha; + while(TargetAlpha >= PI) TargetAlpha -= 2*PI; + while(TargetAlpha < -PI) TargetAlpha += 2*PI; + while(Alpha >= PI) Alpha -= 2*PI; + while(Alpha < -PI) Alpha += 2*PI; + + // inlined + if(StandingOnMovingThing) + WellBufferMe(-TargetAlpha, &Alpha, &AlphaSpeed, 0.07f/2.0f, 0.015f/2.0f, true); + else + WellBufferMe(-TargetAlpha, &Alpha, &AlphaSpeed, 0.07f, 0.015f, true); + + Source.z += GroundDist*Sin(Alpha+AlphaOffset) + GroundDist*0.2f; + if(Source.z < TargetCoors.z + HeightDown) + Source.z = TargetCoors.z + HeightDown; + + if(!PlayerTooClose){ + CColPoint point; + CEntity *entity = nil; + CWorld::pIgnoreEntity = CamTargetEntity; + if(CWorld::ProcessLineOfSight(TheCamera.m_cvecAimingTargetCoors, Source, point, entity, true, false, false, true, false, false, true)){ + CVector TestFront = TheCamera.m_cvecAimingTargetCoors - Source; + TestFront.Normalise(); + CVector CamToPlayer = CameraTarget - Source; + CVector CamToCol = point.point - Source; + if(DotProduct(TestFront, CamToCol) > DotProduct(TestFront, CamToPlayer)){ + // collision is beyond player + float ColDist = (TheCamera.m_cvecAimingTargetCoors - point.point).Magnitude(); + CVector PlayerToTarget = TheCamera.m_cvecAimingTargetCoors - CameraTarget; + float PlayerToTargetDist = PlayerToTarget.Magnitude(); + PlayerToTarget.Normalise(); + CVector Center = TheCamera.m_cvecAimingTargetCoors - ColDist*PlayerToTarget; + float Radius = (point.point - Center).Magnitude(); + if(CWorld::TestSphereAgainstWorld(Center, Radius, nil, true, false, false, true, false, true)){ + CVector LineToCol = gaTempSphereColPoints[0].point - Center; + LineToCol -= DotProduct(LineToCol, PlayerToTarget)*PlayerToTarget; + // unused + CVector LineToPrevCol = point.point - Center; + LineToPrevCol -= DotProduct(LineToPrevCol, PlayerToTarget)*PlayerToTarget; + float LineDist = LineToCol.Magnitude(); + float NewBetaOffset = 0.0f; + if(LineDist > 0.0f && ColDist > 0.1f){ + // scale offset at center to offset at player + float DistOffset = LineDist/ColDist * PlayerToTargetDist; + // turn into an angle + NewBetaOffset = 0.9f*Asin(Min(DistOffset/GroundDist, 1.0f)); + } + if(NewBetaOffset < BetaOffset){ + float Ratio = NewBetaOffset / BetaOffset; + BetaOffset = NewBetaOffset; + Beta = NewBeta + NewBetaOffset; + GroundDist *= Max(Ratio, 0.5f); + Source.x = TargetCoors.x + GroundDist*Cos(Beta); + Source.y = TargetCoors.y + GroundDist*Sin(Beta); + Source.z += (1.0f-Ratio)*0.5f; + } + } + } + } + CWorld::pIgnoreEntity = nil; + } + + Front = TheCamera.m_cvecAimingTargetCoors - Source; + float TargetDistGround = Front.Magnitude2D(); + Front.Normalise(); + m_cvecTargetCoorsForFudgeInter = Source + TargetDistGround*Front; + m_cvecTargetCoorsForFudgeInter.z = TargetCoors.z; + + CVector OrigSource = Source; + TheCamera.AvoidTheGeometry(OrigSource, CameraTarget + CVector(0.0f, 0.0f, 0.75f), Source, FOV); + Source.z = OrigSource.z; + + GetVectorsReadyForRW(); +} + +void +CCam::Process_Syphon_Crim_In_Front(const CVector &CameraTarget, float, float, float) +{ + FOV = DefaultFOV; + + if(!CamTargetEntity->IsPed()) + return; + + CVector TargetCoors = CameraTarget; + CVector vDist; + float fDist, TargetDist; + float zOffset; + float AimingAngle; + + TargetDist = TheCamera.m_fPedZoomValueSmooth * 0.5f + 4.0f; + vDist = Source - TargetCoors; + fDist = vDist.Magnitude2D(); + zOffset = TargetDist - 2.65f; + if(zOffset < 0.0f) + zOffset = 0.0f; + if(zOffset == 0.0f) + Source = TargetCoors + CVector(1.0f, 1.0f, zOffset); + else + Source = TargetCoors + CVector(vDist.x/fDist*TargetDist, vDist.y/fDist*TargetDist, zOffset); + + AimingAngle = CGeneral::GetATanOfXY(TheCamera.m_cvecAimingTargetCoors.x - TargetCoors.x, TheCamera.m_cvecAimingTargetCoors.y - TargetCoors.y); + while(AimingAngle >= PI) AimingAngle -= 2*PI; + while(AimingAngle < -PI) AimingAngle += 2*PI; + + if(ResetStatics){ + if(AimingAngle > 0.0f) + m_fPlayerInFrontSyphonAngleOffSet = -m_fPlayerInFrontSyphonAngleOffSet; + ResetStatics = false; + } + + if(TheCamera.PlayerWeaponMode.Mode == MODE_SYPHON) + Beta = AimingAngle + m_fPlayerInFrontSyphonAngleOffSet; + + Source.x = TargetCoors.x; + Source.y = TargetCoors.y; + Source.x += Cos(Beta) * TargetDist; + Source.y += Sin(Beta) * TargetDist; + + TargetCoors = CameraTarget; + TargetCoors.z += m_fSyphonModeTargetZOffSet; + m_cvecTargetCoorsForFudgeInter = TargetCoors; + + CVector OrigSource = Source; + TheCamera.AvoidTheGeometry(OrigSource, TargetCoors, Source, FOV); + + Front = TargetCoors - Source; + GetVectorsReadyForRW(); +} + +float MAX_HEIGHT_UP = 15.0f; +float WATER_Z_ADDITION = 2.75f; +float WATER_Z_ADDITION_MIN = 1.5f; +float SMALLBOAT_CLOSE_ALPHA_MINUS = 0.2f; +float afBoatBetaDiffMult[3] = { 0.15f, 0.07f, 0.01f }; +float afBoatBetaSpeedDiffMult[3] = { 0.02f, 0.015f, 0.005f }; + +void +CCam::Process_BehindBoat(const CVector &CameraTarget, float TargetOrientation, float, float) +{ + if(!CamTargetEntity->IsVehicle()){ + ResetStatics = false; + return; + } + + CVector TargetCoors = CameraTarget; + float DeltaBeta = 0.0f; + static float TargetWhenChecksWereOn = 0.0f; + static float CenterObscuredWhenChecksWereOn = 0.0f; + static float WaterZAddition = 2.75f; + float WaterLevel = 0.0f; + float MaxHeightUp = MAX_HEIGHT_UP; + static float WaterLevelBuffered = 0.0f; + static float WaterLevelSpeed = 0.0f; + float BetaDiffMult = 0.0f; + float BetaSpeedDiffMult = 0.0f; + + Beta = CGeneral::GetATanOfXY(TargetCoors.x - Source.x, TargetCoors.y - Source.y); + FOV = DefaultFOV; + float TargetAlpha = 0.0f; + + if(ResetStatics){ + CenterObscuredWhenChecksWereOn = 0.0f; + TargetWhenChecksWereOn = 0.0f; + }else if(DirectionWasLooking != LOOKING_FORWARD) + Beta = TargetOrientation; + + if(!CWaterLevel::GetWaterLevelNoWaves(TargetCoors.x, TargetCoors.y, TargetCoors.z, &WaterLevel)) + WaterLevel = TargetCoors.z - 0.5f; + if(ResetStatics){ + WaterLevelBuffered = WaterLevel; + WaterLevelSpeed = 0.0f; + } + WellBufferMe(WaterLevel, &WaterLevelBuffered, &WaterLevelSpeed, 0.2f, 0.07f, false); + + static float FixerForGoingBelowGround = 0.4f; + if(-FixerForGoingBelowGround < TargetCoors.z-WaterLevelBuffered+WATER_Z_ADDITION) + WaterLevelBuffered += TargetCoors.z-WaterLevelBuffered+WATER_Z_ADDITION - FixerForGoingBelowGround; + + CVector BoatDimensions = CamTargetEntity->GetColModel()->boundingBox.GetSize(); + float BoatSize = BoatDimensions.Magnitude2D(); + int index = 0; + TheCamera.GetArrPosForVehicleType(((CVehicle*)CamTargetEntity)->GetVehicleAppearance(), index); + if(TheCamera.CarZoomIndicator == CAM_ZOOM_1){ + TargetAlpha = ZmOneAlphaOffset[index]; + BetaDiffMult = afBoatBetaDiffMult[0]; + BetaSpeedDiffMult = afBoatBetaSpeedDiffMult[0]; + }else if(TheCamera.CarZoomIndicator == CAM_ZOOM_2){ + TargetAlpha = ZmTwoAlphaOffset[index]; + BetaDiffMult = afBoatBetaDiffMult[1]; + BetaSpeedDiffMult = afBoatBetaSpeedDiffMult[1]; + }else if(TheCamera.CarZoomIndicator == CAM_ZOOM_3){ + TargetAlpha = ZmThreeAlphaOffset[index]; + BetaDiffMult = afBoatBetaDiffMult[2]; + BetaSpeedDiffMult = afBoatBetaSpeedDiffMult[2]; + } + if(TheCamera.CarZoomIndicator == CAM_ZOOM_1 && BoatSize < 10.0f){ + TargetAlpha -= SMALLBOAT_CLOSE_ALPHA_MINUS; + BoatSize = 10.0f; + }else if(CCullZones::Cam1stPersonForPlayer()){ + float Water = 0.0f; + // useless call + //CWaterLevel::GetWaterLevelNoWaves(TargetCoors.x, TargetCoors.y, TargetCoors.z, &Water); + Water = (WaterLevel + WATER_Z_ADDITION_MIN - WaterLevelBuffered - WATER_Z_ADDITION)/(BoatDimensions.z/2.0f + MaxHeightUp); + TargetAlpha = Asin(Clamp(Water, -1.0f, 1.0f)); + } + + if(ResetStatics){ + Alpha = TargetAlpha; + AlphaSpeed = 0.0f; + } + WellBufferMe(TargetAlpha, &Alpha, &AlphaSpeed, 0.15f, 0.07f, true); + + if(ResetStatics){ + Beta = TargetOrientation; + DeltaBeta = 0.0f; + } + // inlined + WellBufferMe(TargetOrientation, &Beta, &BetaSpeed, BetaDiffMult * ((CVehicle*)CamTargetEntity)->m_vecMoveSpeed.Magnitude(), BetaSpeedDiffMult, true); + + Source = (TheCamera.CarZoomValueSmooth+BoatSize) * CVector(-Cos(Beta), -Sin(Beta), 0.0f) + TargetCoors; + Source.z = WaterLevelBuffered + WATER_Z_ADDITION + (BoatDimensions.z/2.0f + MaxHeightUp) * Sin(Alpha); + + m_cvecTargetCoorsForFudgeInter = TargetCoors; + CVector OrigSource = Source; + TheCamera.AvoidTheGeometry(OrigSource, TargetCoors, Source, FOV); + Front = TargetCoors - Source; + Front.Normalise(); + + + float TargetRoll; + if(CPad::GetPad(0)->GetDPadLeft() || CPad::GetPad(0)->GetDPadRight()){ +#ifdef FIX_BUGS + float fwdSpeed = 180.0f*DotProduct(((CVehicle*)CamTargetEntity)->m_vecMoveSpeed, CamTargetEntity->GetForward()); + if(fwdSpeed > 210.0f) fwdSpeed = 210.0f; +#endif + if(CPad::GetPad(0)->GetDPadLeft()) + TargetRoll = DEGTORAD(10.0f)*TiltOverShoot[index] + f_max_role_angle; + else + TargetRoll = -(DEGTORAD(10.0f)*TiltOverShoot[index] + f_max_role_angle); + CVector FwdTarget = CamTargetEntity->GetForward(); + FwdTarget.Normalise(); + float AngleDiff = DotProduct(FwdTarget, Front); + AngleDiff = Acos(Min(Abs(AngleDiff), 1.0f)); +#ifdef FIX_BUGS + TargetRoll *= fwdSpeed/210.0f * Sin(AngleDiff); +#else + TargetRoll *= Sin(AngleDiff); +#endif + }else{ + float fwdSpeed = 180.0f*DotProduct(((CVehicle*)CamTargetEntity)->m_vecMoveSpeed, CamTargetEntity->GetForward()); + if(fwdSpeed > 210.0f) fwdSpeed = 210.0f; + TargetRoll = CPad::GetPad(0)->GetLeftStickX()/128.0f * fwdSpeed/210.0f; + CVector FwdTarget = CamTargetEntity->GetForward(); + FwdTarget.Normalise(); + float AngleDiff = DotProduct(FwdTarget, Front); + AngleDiff = Acos(Min(Abs(AngleDiff), 1.0f)); + TargetRoll *= (DEGTORAD(10.0f)*TiltOverShoot[index] + f_max_role_angle) * Sin(AngleDiff); + } + + WellBufferMe(TargetRoll, &f_Roll, &f_rollSpeed, 0.15f, 0.07f, false); + Up = CVector(Cos(f_Roll + HALFPI), 0.0f, Sin(f_Roll + HALFPI)); + Up.Normalise(); + Front.Normalise(); + CVector Left = CrossProduct(Up, Front); + Left.Normalise(); + Up = CrossProduct(Front, Left); + Up.Normalise(); + + ResetStatics = false; +} + +float FIGHT_HORIZ_DIST = 3.0f; +float FIGHT_VERT_DIST = 1.0f; +float FIGHT_BETA_ANGLE = 125.0f; + +void +CCam::Process_Fight_Cam(const CVector &CameraTarget, float TargetOrientation, float, float) +{ + if(!CamTargetEntity->IsPed()) + return; + + FOV = DefaultFOV; + float HorizDist = FIGHT_HORIZ_DIST; + float VertDist = FIGHT_VERT_DIST; + float BetaLeft, BetaRight, DeltaBetaLeft, DeltaBetaRight; + static bool PreviouslyFailedBuildingChecks = false; + float TargetCamHeight; + CVector TargetCoors; + + m_fMinDistAwayFromCamWhenInterPolating = FIGHT_HORIZ_DIST; + Front = Source - CameraTarget; + if(ResetStatics) + Beta = CGeneral::GetATanOfXY(Front.x, Front.y); + while(TargetOrientation >= PI) TargetOrientation -= 2*PI; + while(TargetOrientation < -PI) TargetOrientation += 2*PI; + while(Beta >= PI) Beta -= 2*PI; + while(Beta < -PI) Beta += 2*PI; + + // Figure out Beta + BetaLeft = TargetOrientation - DEGTORAD(FIGHT_BETA_ANGLE); + BetaRight = TargetOrientation + DEGTORAD(FIGHT_BETA_ANGLE); + DeltaBetaLeft = Beta - BetaLeft; + DeltaBetaRight = Beta - BetaRight; + while(DeltaBetaLeft >= PI) DeltaBetaLeft -= 2*PI; + while(DeltaBetaLeft < -PI) DeltaBetaLeft += 2*PI; + while(DeltaBetaRight >= PI) DeltaBetaRight -= 2*PI; + while(DeltaBetaRight < -PI) DeltaBetaRight += 2*PI; + + if(ResetStatics){ + if(Abs(DeltaBetaLeft) < Abs(DeltaBetaRight)) + m_fTargetBeta = DeltaBetaLeft; + else + m_fTargetBeta = DeltaBetaRight; + m_fBufferedTargetOrientation = TargetOrientation; + m_fBufferedTargetOrientationSpeed = 0.0f; + m_bCollisionChecksOn = true; + BetaSpeed = 0.0f; + }else if(CPad::GetPad(0)->WeaponJustDown()){ + if(Abs(DeltaBetaLeft) < Abs(DeltaBetaRight)) + m_fTargetBeta = DeltaBetaLeft; + else + m_fTargetBeta = DeltaBetaRight; + } + + WellBufferMe(m_fTargetBeta, &Beta, &BetaSpeed, 0.015f, 0.007f, true); + + Source = CameraTarget + HorizDist*CVector(Cos(Beta), Sin(Beta), 0.0f); + Source.z += VertDist; + + WellBufferMe(TargetOrientation, &m_fBufferedTargetOrientation, &m_fBufferedTargetOrientationSpeed, 0.07f, 0.004f, true); + TargetCoors = CameraTarget + 0.1f*CVector(Cos(m_fBufferedTargetOrientation), Sin(m_fBufferedTargetOrientation), 0.0f); + + TargetCamHeight = CameraTarget.z - Source.z + Max(m_fPedBetweenCameraHeightOffset, m_fDimensionOfHighestNearCar) + VertDist; + if(TargetCamHeight > m_fCamBufferedHeight) + WellBufferMe(TargetCamHeight, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.15f, 0.04f, false); + else + WellBufferMe(0.0f, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.08f, 0.0175f, false); + Source.z += m_fCamBufferedHeight; + + m_cvecTargetCoorsForFudgeInter = TargetCoors; + CVector OrigSource = Source; + TheCamera.AvoidTheGeometry(OrigSource, TargetCoors, Source, FOV); + Front = TargetCoors - Source; + Front.Normalise(); + GetVectorsReadyForRW(); + + ResetStatics = false; +} + +/* +// Spline format is this, but game doesn't seem to use any kind of struct: +struct Spline +{ + float numFrames; + struct { + float time; + float f[3]; // CVector for Vector spline + } frames[1]; // numFrames +}; +*/ + +// These two functions are pretty ugly + +#define MS(t) (uint32)((t)*1000.0f) + +void +FindSplinePathPositionFloat(float *out, float *spline, uint32 time, uint32 &marker) +{ + // marker is at time + uint32 numFrames = spline[0]; + uint32 timeDelta = MS(spline[marker] - spline[marker-4]); + uint32 endTime = MS(spline[4*(numFrames-1) + 1]); + if(time < endTime){ + bool canAdvance = true; + if((marker-1)/4 > numFrames){ + canAdvance = false; + marker = 4*(numFrames-1) + 1; + } + // skipping over small time deltas apparently? + while(timeDelta <= 75 && canAdvance){ + marker += 4; + if((marker-1)/4 > numFrames){ + canAdvance = false; + marker = 4*(numFrames-1) + 1; + } + timeDelta = (spline[marker] - spline[marker-4]) * 1000.0f; + } + } + float a = ((float)time - (float)MS(spline[marker-4])) / (float)MS(spline[marker] - spline[marker-4]); + a = Clamp(a, 0.0f, 1.0f); + float b = 1.0f - a; + *out = b*b*b * spline[marker-3] + + 3.0f*a*b*b * spline[marker-1] + + 3.0f*a*a*b * spline[marker+2] + + a*a*a * spline[marker+1]; +} + +void +FindSplinePathPositionVector(CVector *out, float *spline, uint32 time, uint32 &marker) +{ + // marker is at time + uint32 numFrames = spline[0]; + uint32 timeDelta = MS(spline[marker] - spline[marker-10]); + uint32 endTime = MS(spline[10*(numFrames-1) + 1]); + if(time < endTime){ + bool canAdvance = true; + if((marker-1)/10 > numFrames){ + canAdvance = false; + marker = 10*(numFrames-1) + 1; + } + // skipping over small time deltas apparently? + while(timeDelta <= 75 && canAdvance){ + marker += 10; + if((marker-1)/10 > numFrames){ + canAdvance = false; + marker = 10*(numFrames-1) + 1; + } + timeDelta = (spline[marker] - spline[marker-10]) * 1000.0f; + } + } + + if((marker-1)/10 > numFrames){ + printf("Arraymarker %i \n", marker); + printf("Path zero %i \n", numFrames); + } + + float a = ((float)time - (float)MS(spline[marker-10])) / (float)MS(spline[marker] - spline[marker-10]); + a = Clamp(a, 0.0f, 1.0f); + float b = 1.0f - a; + out->x = + b*b*b * spline[marker-9] + + 3.0f*a*b*b * spline[marker-3] + + 3.0f*a*a*b * spline[marker+4] + + a*a*a * spline[marker+1]; + out->y = + b*b*b * spline[marker-8] + + 3.0f*a*b*b * spline[marker-2] + + 3.0f*a*a*b * spline[marker+5] + + a*a*a * spline[marker+2]; + out->z = + b*b*b * spline[marker-7] + + 3.0f*a*b*b * spline[marker-1] + + 3.0f*a*a*b * spline[marker+6] + + a*a*a * spline[marker+3]; + *out += TheCamera.m_vecCutSceneOffset; +} + +void +CCam::Process_FlyBy(const CVector&, float, float, float) +{ + float UpAngle = 0.0f; + static float FirstFOVValue = 0.0f; + static float PsuedoFOV; + static uint32 ArrayMarkerFOV; + static uint32 ArrayMarkerUp; + static uint32 ArrayMarkerSource; + static uint32 ArrayMarkerFront; + + if(TheCamera.m_bcutsceneFinished) + return; +#ifdef FIX_BUGS + // this would crash, not nice when cycling debug mode + if(TheCamera.m_arrPathArray[0].m_arr_PathData == nil) + return; +#endif + + Up = CVector(0.0f, 0.0f, 1.0f); + if(TheCamera.m_bStartingSpline) + m_fTimeElapsedFloat += CTimer::GetTimeStepNonClippedInMilliseconds(); + else{ + m_fTimeElapsedFloat = 0.0f; + m_uiFinishTime = MS(TheCamera.m_arrPathArray[2].m_arr_PathData[10*((int)TheCamera.m_arrPathArray[2].m_arr_PathData[0]-1) + 1]); + TheCamera.m_bStartingSpline = true; + FirstFOVValue = TheCamera.m_arrPathArray[0].m_arr_PathData[2]; + PsuedoFOV = TheCamera.m_arrPathArray[0].m_arr_PathData[2]; + ArrayMarkerFOV = 5; + ArrayMarkerUp = 5; + ArrayMarkerSource = 11; + ArrayMarkerFront = 11; + } + + float fTime = m_fTimeElapsedFloat; + uint32 uiFinishTime = m_uiFinishTime; + uint32 uiTime = fTime; + if(uiTime < uiFinishTime){ + TheCamera.m_fPositionAlongSpline = (float) uiTime / uiFinishTime; + + while(uiTime >= (TheCamera.m_arrPathArray[2].m_arr_PathData[ArrayMarkerSource] - TheCamera.m_arrPathArray[2].m_arr_PathData[1])*1000.0f) + ArrayMarkerSource += 10; + FindSplinePathPositionVector(&Source, TheCamera.m_arrPathArray[2].m_arr_PathData, uiTime, ArrayMarkerSource); + + while(uiTime >= (TheCamera.m_arrPathArray[3].m_arr_PathData[ArrayMarkerFront] - TheCamera.m_arrPathArray[3].m_arr_PathData[1])*1000.0f) + ArrayMarkerFront += 10; + FindSplinePathPositionVector(&Front, TheCamera.m_arrPathArray[3].m_arr_PathData, uiTime, ArrayMarkerFront); + + while(uiTime >= (TheCamera.m_arrPathArray[1].m_arr_PathData[ArrayMarkerUp] - TheCamera.m_arrPathArray[1].m_arr_PathData[1])*1000.0f) + ArrayMarkerUp += 4; + FindSplinePathPositionFloat(&UpAngle, TheCamera.m_arrPathArray[1].m_arr_PathData, uiTime, ArrayMarkerUp); + UpAngle = DEGTORAD(UpAngle) + HALFPI; + Up.x = Cos(UpAngle); + Up.z = Sin(UpAngle); + + while(uiTime >= (TheCamera.m_arrPathArray[0].m_arr_PathData[ArrayMarkerFOV] - TheCamera.m_arrPathArray[0].m_arr_PathData[1])*1000.0f) + ArrayMarkerFOV += 4; + FindSplinePathPositionFloat(&PsuedoFOV, TheCamera.m_arrPathArray[0].m_arr_PathData, uiTime, ArrayMarkerFOV); + + m_cvecTargetCoorsForFudgeInter = Front; + Front = Front - Source; + Front.Normalise(); + CVector Left = CrossProduct(Up, Front); + Up = CrossProduct(Front, Left); + Up.Normalise(); + }else if(uiTime >= uiFinishTime){ + // end + ArrayMarkerSource = (TheCamera.m_arrPathArray[2].m_arr_PathData[0] - 1)*10 + 1; + ArrayMarkerFront = (TheCamera.m_arrPathArray[3].m_arr_PathData[0] - 1)*10 + 1; + ArrayMarkerUp = (TheCamera.m_arrPathArray[1].m_arr_PathData[0] - 1)*4 + 1; + ArrayMarkerFOV = (TheCamera.m_arrPathArray[0].m_arr_PathData[0] - 1)*4 + 1; + + FindSplinePathPositionVector(&Source, TheCamera.m_arrPathArray[2].m_arr_PathData, uiTime, ArrayMarkerSource); + FindSplinePathPositionVector(&Front, TheCamera.m_arrPathArray[3].m_arr_PathData, uiTime, ArrayMarkerFront); + FindSplinePathPositionFloat(&UpAngle, TheCamera.m_arrPathArray[1].m_arr_PathData, uiTime, ArrayMarkerUp); + UpAngle = DEGTORAD(UpAngle) + HALFPI; + Up.x = Cos(UpAngle); + Up.z = Sin(UpAngle); + FindSplinePathPositionFloat(&PsuedoFOV, TheCamera.m_arrPathArray[0].m_arr_PathData, uiTime, ArrayMarkerFOV); + + TheCamera.m_fPositionAlongSpline = 1.0f; + ArrayMarkerFOV = 0; + ArrayMarkerUp = 0; + ArrayMarkerSource = 0; + ArrayMarkerFront = 0; + + m_cvecTargetCoorsForFudgeInter = Front; + Front = Front - Source; + Front.Normalise(); + CVector Left = CrossProduct(Up, Front); + Up = CrossProduct(Front, Left); + Up.Normalise(); + } + FOV = PsuedoFOV; +} + +CVector vecWheelCamBoatOffset(-0.5f, -0.8f, 0.3f); +CVector vecWheelCamBoatOffsetAlt(0.2f, -0.2f, -0.3f); +float fWheelCamCarXOffset = 0.33f; +float fWheelCamBikeXOffset = 0.2f; + +bool +CCam::Process_WheelCam(const CVector&, float, float, float) +{ + FOV = DefaultFOV; + + CVector WheelPos; + if(CamTargetEntity->IsPed()){ + // what? ped with wheels or what? + Source = Multiply3x3(CamTargetEntity->GetMatrix(), CVector(-0.3f, -0.5f, 0.1f)); + Source += CamTargetEntity->GetPosition(); + Front = CVector(1.0f, 0.0f, 0.0f); + }else{ + WheelPos = CamTargetEntity->GetColModel()->boundingBox.min; + WheelPos.x -= 0.33f; + WheelPos.y = -2.3f; + WheelPos.z = 0.3f; + Source = CamTargetEntity->GetMatrix() * WheelPos; + Front = CamTargetEntity->GetForward(); + } + + CVector NewUp, Right; + if(CamTargetEntity->IsVehicle() && + (((CVehicle*)CamTargetEntity)->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI || + ((CVehicle*)CamTargetEntity)->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE)){ + WheelPos.x = -1.55f; + Right = CamTargetEntity->GetRight(); + NewUp = CamTargetEntity->GetUp(); + Source = CamTargetEntity->GetMatrix() * WheelPos; + }else if(CamTargetEntity->IsVehicle() && ((CVehicle*)CamTargetEntity)->IsBoat()){ + NewUp = CVector(0.0f, 0.0f, 1.0f); + Right = CrossProduct(Front, NewUp); + Right.Normalise(); + NewUp = CrossProduct(Right, Front); + NewUp.Normalise(); + + CVector BoatCamPos(0.0f, 0.0f, 0.0f); + if(((CVehicle*)CamTargetEntity)->pDriver){ + ((CVehicle*)CamTargetEntity)->pDriver->m_pedIK.GetComponentPosition(BoatCamPos, PED_HEAD); + BoatCamPos += ((CVehicle*)CamTargetEntity)->m_vecMoveSpeed * CTimer::GetTimeStep(); + BoatCamPos += vecWheelCamBoatOffset.x * Right; + BoatCamPos += vecWheelCamBoatOffset.y * CamTargetEntity->GetForward(); + BoatCamPos.z += vecWheelCamBoatOffset.z; + if(CamTargetEntity->GetModelIndex() == MI_PREDATOR){ + BoatCamPos += vecWheelCamBoatOffsetAlt.x * Right; + BoatCamPos += vecWheelCamBoatOffsetAlt.y * CamTargetEntity->GetForward(); + BoatCamPos.z += vecWheelCamBoatOffsetAlt.z; + } + Source = BoatCamPos; + }else + Source.z += 2.0f*vecWheelCamBoatOffset.z; + }else if(CamTargetEntity->IsVehicle() && ((CVehicle*)CamTargetEntity)->IsBike()){ + NewUp = CVector(0.0f, 0.0f, 1.0f); + Right = CrossProduct(Front, NewUp); + Right.Normalise(); + NewUp = CrossProduct(Right, Front); + NewUp.Normalise(); + + WheelPos.z += fWheelCamCarXOffset - fWheelCamBikeXOffset; + Source = CamTargetEntity->GetPosition(); + Source += WheelPos.x * CamTargetEntity->GetRight(); + Source += WheelPos.y * Front; + Source += WheelPos.z * Up; + }else{ + NewUp = CVector(0.0f, 0.0f, 1.0f); + Right = CrossProduct(Front, NewUp); + Right.Normalise(); + NewUp = CrossProduct(Right, Front); + NewUp.Normalise(); + } + + float Roll = Cos((CTimer::GetTimeInMilliseconds()&0x1FFFF)/(float)0x1FFFF * TWOPI); + Up = Cos(Roll*0.4f)*NewUp + Sin(Roll*0.4f)*Right; + + CEntity *entity = nil; + CColPoint point; + CWorld::pIgnoreEntity = CamTargetEntity; + bool blocked = CWorld::ProcessLineOfSight(Source, CamTargetEntity->GetPosition(), point, entity, true, false, false, true, false, false, true); + CWorld::pIgnoreEntity = nil; + return !blocked; +} + +int BOAT_UNDERWATER_CAM_BLUR = 20; +float BOAT_UNDERWATER_CAM_COLORMAG_LIMIT = 10.0f; + +//--MIAIM: done +void +CCam::Process_Fixed(const CVector &CameraTarget, float, float, float) +{ + if(DirectionWasLooking != LOOKING_FORWARD) + DirectionWasLooking = LOOKING_FORWARD; + + Source = m_cvecCamFixedModeSource; + Front = CameraTarget - Source; + Front.Normalise(); + m_cvecTargetCoorsForFudgeInter = CameraTarget; + GetVectorsReadyForRW(); + + Up = CVector(0.0f, 0.0f, 1.0f) + m_cvecCamFixedModeUpOffSet; + Up.Normalise(); + CVector Right = CrossProduct(Front, Up); + Right.Normalise(); + Up = CrossProduct(Right, Front); + + FOV = DefaultFOV; + if(TheCamera.m_bUseSpecialFovTrain) + FOV = TheCamera.m_fFovForTrain; + + float WaterZ = 0.0f; + if(CWaterLevel::GetWaterLevel(Source, &WaterZ, true) && Source.z < WaterZ){ + float WaterLum = Sqrt(SQR(CTimeCycle::GetWaterRed()) + SQR(CTimeCycle::GetWaterGreen()) + SQR(CTimeCycle::GetWaterBlue())); + if(WaterLum > BOAT_UNDERWATER_CAM_COLORMAG_LIMIT){ + float f = BOAT_UNDERWATER_CAM_COLORMAG_LIMIT/WaterLum; + TheCamera.SetMotionBlur(CTimeCycle::GetWaterRed()*f, + CTimeCycle::GetWaterGreen()*f, + CTimeCycle::GetWaterBlue()*f, BOAT_UNDERWATER_CAM_BLUR, MOTION_BLUR_LIGHT_SCENE); + }else{ + TheCamera.SetMotionBlur(CTimeCycle::GetWaterRed(), + CTimeCycle::GetWaterGreen(), + CTimeCycle::GetWaterBlue(), BOAT_UNDERWATER_CAM_BLUR, MOTION_BLUR_LIGHT_SCENE); + } + } + +#ifdef PC_PLAYER_CONTROLS + if(FrontEndMenuManager.m_ControlMethod == CONTROL_STANDARD && Using3rdPersonMouseCam()){ + CPed *player = FindPlayerPed(); + if(player && player->CanStrafeOrMouseControl()){ + float Heading = Front.Heading(); + ((CPed*)TheCamera.pTargetEntity)->m_fRotationCur = Heading; + ((CPed*)TheCamera.pTargetEntity)->m_fRotationDest = Heading; + TheCamera.pTargetEntity->SetHeading(Heading); + TheCamera.pTargetEntity->GetMatrix().UpdateRW(); + } + } +#endif +} + +void +CCam::Process_LightHouse(const CVector &CameraTarget, float, float, float) +{ + static float Timer; + + Source = CameraTarget; + Source.x = 474.3f; + Source.y = -1717.6f; + + int CamMode; + if(CameraTarget.z > 57.0f && (CameraTarget-Source).Magnitude2D() > 3.2f){ + // Outside at top + if(Timer > 0.0f){ + Timer -= CTimer::GetTimeStep(); + CamMode = 1; + }else{ + Timer = -24.0f; + CamMode = 2; + } + }else if(CameraTarget.z > 57.0f){ + // Inside at top + if(Timer < 0.0f){ + Timer += CTimer::GetTimeStep(); + CamMode = 2; + }else{ + Timer = 24.0f; + CamMode = 1; + } + }else{ + Timer = 0.0f; + CamMode = 0; + } + + if(CamMode == 2){ + Source.z = 57.5f; + Front = Source - CameraTarget; + Front.Normalise(); + Source.x = CameraTarget.x - 5.0f*Front.x; + Source.y = CameraTarget.y - 5.0f*Front.y; + }else if(CamMode == 1){ + Front = CameraTarget - Source; + Front.Normalise(); + Source.x = CameraTarget.x - 2.0f*Front.x; + Source.y = CameraTarget.y - 2.0f*Front.y; + }else{ + Source.z += 4.0f; + Front = CameraTarget - Source; + Front.Normalise(); + Source -= 4.0f*Front; + Source.z = Min(Source.z, 55.0f); + Front = CameraTarget - Source; + } + + m_cvecTargetCoorsForFudgeInter = CameraTarget; + GetVectorsReadyForRW(); + + Up = CVector(0.0f, 0.0f, 1.0f) + m_cvecCamFixedModeUpOffSet; + Up.Normalise(); + CVector Right = CrossProduct(Front, Up); + Right.Normalise(); + Up = CrossProduct(Right, Front); + + FOV = DefaultFOV; + if(TheCamera.m_bUseSpecialFovTrain) // uh, sure... + FOV = TheCamera.m_fFovForTrain; +} + +void +CCam::Process_Player_Fallen_Water(const CVector &CameraTarget, float TargetOrientation, float, float) +{ + CColPoint colPoint; + CEntity *entity = nil; + + FOV = DefaultFOV; + Source = m_vecLastAboveWaterCamPosition; + Source.z += 4.0f; + + m_cvecTargetCoorsForFudgeInter = CameraTarget; + Front = CameraTarget - Source; + Front.Normalise(); + if(CWorld::ProcessLineOfSight(CameraTarget, Source, colPoint, entity, true, false, false, true, false, true, true)) + Source = colPoint.point; + GetVectorsReadyForRW(); + Front = CameraTarget - Source; + Front.Normalise(); +} + +void +CCam::Process_SpecialFixedForSyphon(const CVector &CameraTarget, float, float, float) +{ + Source = m_cvecCamFixedModeSource; + m_cvecTargetCoorsForFudgeInter = CameraTarget; + m_cvecTargetCoorsForFudgeInter.z += m_fSyphonModeTargetZOffSet; + Front = CameraTarget - Source; + CVector OrigSource = Source; + TheCamera.AvoidTheGeometry(OrigSource, m_cvecTargetCoorsForFudgeInter, Source, FOV); + Front.z += m_fSyphonModeTargetZOffSet; + + GetVectorsReadyForRW(); + + Up += m_cvecCamFixedModeUpOffSet; + Up.Normalise(); + CVector Left = CrossProduct(Up, Front); + Left.Normalise(); + Front = CrossProduct(Left, Up); + Front.Normalise(); + FOV = DefaultFOV; +} + +#ifdef IMPROVED_CAMERA + +#define KEYJUSTDOWN(k) ControlsManager.GetIsKeyboardKeyJustDown((RsKeyCodes)k) +#define KEYDOWN(k) ControlsManager.GetIsKeyboardKeyDown((RsKeyCodes)k) +#define CTRLJUSTDOWN(key) \ + ((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYJUSTDOWN((RsKeyCodes)key) || \ + (KEYJUSTDOWN(rsLCTRL) || KEYJUSTDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key)) +#define CTRLDOWN(key) ((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key)) + + +void +CCam::Process_Debug(const CVector&, float, float, float) +{ + static float Speed = 0.0f; + static float PanSpeedX = 0.0f; + static float PanSpeedY = 0.0f; + CVector TargetCoors; + + RwCameraSetNearClipPlane(Scene.camera, DEFAULT_NEAR); + FOV = DefaultFOV; + Alpha += DEGTORAD(CPad::GetPad(1)->GetLeftStickY()) / 50.0f; + Beta += DEGTORAD(CPad::GetPad(1)->GetLeftStickX()*1.5f) / 19.0f; + if(CPad::GetPad(0)->GetLeftMouse()){ + Alpha += DEGTORAD(CPad::GetPad(0)->GetMouseY()/2.0f); + Beta += DEGTORAD(CPad::GetPad(0)->GetMouseX()/2.0f); + } + + TargetCoors.x = Source.x + Cos(Alpha) * Sin(Beta) * 7.0f; + TargetCoors.y = Source.y + Cos(Alpha) * Cos(Beta) * 7.0f; + TargetCoors.z = Source.z + Sin(Alpha) * 3.0f; + + if(Alpha > DEGTORAD(89.5f)) Alpha = DEGTORAD(89.5f); + else if(Alpha < DEGTORAD(-89.5f)) Alpha = DEGTORAD(-89.5f); + + if(CPad::GetPad(1)->GetSquare() || KEYDOWN('W')) + Speed += 0.1f; + else if(CPad::GetPad(1)->GetCross() || KEYDOWN('S')) + Speed -= 0.1f; + else + Speed = 0.0f; + if(Speed > 70.0f) Speed = 70.0f; + if(Speed < -70.0f) Speed = -70.0f; + + + if(KEYDOWN(rsRIGHT) || KEYDOWN('D')) + PanSpeedX += 0.1f; + else if(KEYDOWN(rsLEFT) || KEYDOWN('A')) + PanSpeedX -= 0.1f; + else + PanSpeedX = 0.0f; + if(PanSpeedX > 70.0f) PanSpeedX = 70.0f; + if(PanSpeedX < -70.0f) PanSpeedX = -70.0f; + + + if(KEYDOWN(rsUP)) + PanSpeedY += 0.1f; + else if(KEYDOWN(rsDOWN)) + PanSpeedY -= 0.1f; + else + PanSpeedY = 0.0f; + if(PanSpeedY > 70.0f) PanSpeedY = 70.0f; + if(PanSpeedY < -70.0f) PanSpeedY = -70.0f; + + + Front = TargetCoors - Source; + Front.Normalise(); + Source = Source + Front*Speed; + + Up = CVector{ 0.0f, 0.0f, 1.0f }; + CVector Right = CrossProduct(Front, Up); + Up = CrossProduct(Right, Front); + Source = Source + Up*PanSpeedY + Right*PanSpeedX; + + if(Source.z < -450.0f) + Source.z = -450.0f; + + if(CPad::GetPad(1)->GetRightShoulder2JustDown() || KEYJUSTDOWN(rsENTER)){ + if(FindPlayerVehicle()) + FindPlayerVehicle()->Teleport(Source); + else + CWorld::Players[CWorld::PlayerInFocus].m_pPed->SetPosition(Source); + } + + // stay inside sectors + while(CWorld::GetSectorX(Source.x) > NUMSECTORS_X-5.0f) + Source.x -= 1.0f; + while(CWorld::GetSectorX(Source.x) < 5.0f) + Source.x += 1.0f; + while(CWorld::GetSectorY(Source.y) > NUMSECTORS_X-5.0f) + Source.y -= 1.0f; + while(CWorld::GetSectorY(Source.y) < 5.0f) + Source.y += 1.0f; + GetVectorsReadyForRW(); + +#ifdef FIX_BUGS + CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_CAMERA); +#else + CPad::GetPad(0)->DisablePlayerControls = PLAYERCONTROL_CAMERA; +#endif + + if(CPad::GetPad(1)->GetLeftShockJustDown() && gbBigWhiteDebugLightSwitchedOn) + CShadows::StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &Source, + 12.0f, 0.0f, 0.0f, -12.0f, + 128, 128, 128, 128, 1000.0f, false, 1.0f, nil, false); + + if(CHud::m_Wants_To_Draw_Hud){ + char str[256]; + sprintf(str, "CamX: %f CamY: %f CamZ: %f", Source.x, Source.y, Source.z); + sprintf(str, "Frontx: %f, Fronty: %f, Frontz: %f ", Front.x, Front.y, Front.z); + sprintf(str, "Look@: %f, Look@: %f, Look@: %f ", Front.x + Source.x, Front.y + Source.y, Front.z + Source.z); + } +} +#else +void +CCam::Process_Debug(const CVector&, float, float, float) +{ + static float Speed = 0.0f; + CVector TargetCoors; + + RwCameraSetNearClipPlane(Scene.camera, DEFAULT_NEAR); + FOV = DefaultFOV; + Alpha += DEGTORAD(CPad::GetPad(1)->GetLeftStickY()) / 50.0f; + Beta += DEGTORAD(CPad::GetPad(1)->GetLeftStickX()*1.5f) / 19.0f; + + TargetCoors.x = Source.x + Cos(Alpha) * Sin(Beta) * 7.0f; + TargetCoors.y = Source.y + Cos(Alpha) * Cos(Beta) * 7.0f; + TargetCoors.z = Source.z + Sin(Alpha) * 3.0f; + + if(Alpha > DEGTORAD(89.5f)) Alpha = DEGTORAD(89.5f); + else if(Alpha < DEGTORAD(-89.5f)) Alpha = DEGTORAD(-89.5f); + + if(CPad::GetPad(1)->GetSquare() || CPad::GetPad(1)->GetLeftMouse()) + Speed += 0.1f; + else if(CPad::GetPad(1)->GetCross() || CPad::GetPad(1)->GetRightMouse()) + Speed -= 0.1f; + else + Speed = 0.0f; + if(Speed > 70.0f) Speed = 70.0f; + if(Speed < -70.0f) Speed = -70.0f; + + Front = TargetCoors - Source; + Front.Normalise(); + Source = Source + Front*Speed; + + if(Source.z < -450.0f) + Source.z = -450.0f; + + if(CPad::GetPad(1)->GetRightShoulder2JustDown()){ + if(FindPlayerVehicle()) + FindPlayerVehicle()->Teleport(Source); + else + CWorld::Players[CWorld::PlayerInFocus].m_pPed->SetPosition(Source); + } + + // stay inside sectors + while(CWorld::GetSectorX(Source.x) > NUMSECTORS_X-5.0f) + Source.x -= 1.0f; + while(CWorld::GetSectorX(Source.x) < 5.0f) + Source.x += 1.0f; + while(CWorld::GetSectorY(Source.y) > NUMSECTORS_X-5.0f) + Source.y -= 1.0f; + while(CWorld::GetSectorY(Source.y) < 5.0f) + Source.y += 1.0f; + GetVectorsReadyForRW(); + + if(CPad::GetPad(1)->GetLeftShockJustDown() && gbBigWhiteDebugLightSwitchedOn) + CShadows::StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &Source, + 12.0f, 0.0f, 0.0f, -12.0f, + 128, 128, 128, 128, 1000.0f, false, 1.0f, nil, 1.0f); + + if(CHud::m_Wants_To_Draw_Hud){ + char str[256]; + sprintf(str, "CamX: %f CamY: %f CamZ: %f", Source.x, Source.y, Source.z); + sprintf(str, "Frontx: %f, Fronty: %f, Frontz: %f ", Front.x, Front.y, Front.z); + sprintf(str, "Look@: %f, Look@: %f, Look@: %f ", Front.x + Source.x, Front.y + Source.y, Front.z + Source.z); + } +} +#endif + +#ifdef GTA_SCENE_EDIT +void +CCam::Process_Editor(const CVector&, float, float, float) +{ + static float Speed = 0.0f; + CVector TargetCoors; + + if(ResetStatics){ + Source = CVector(796.0f, -937.0, 40.0f); + CamTargetEntity = nil; + } + ResetStatics = false; + + RwCameraSetNearClipPlane(Scene.camera, DEFAULT_NEAR); + FOV = DefaultFOV; + Alpha += DEGTORAD(CPad::GetPad(1)->GetLeftStickY()) / 50.0f; + Beta += DEGTORAD(CPad::GetPad(1)->GetLeftStickX()*1.5f) / 19.0f; + + if(CamTargetEntity && CSceneEdit::m_bCameraFollowActor){ + TargetCoors = CamTargetEntity->GetPosition(); + }else if(CSceneEdit::m_bRecording){ + TargetCoors.x = Source.x + Cos(Alpha) * Sin(Beta) * 7.0f; + TargetCoors.y = Source.y + Cos(Alpha) * Cos(Beta) * 7.0f; + TargetCoors.z = Source.z + Sin(Alpha) * 7.0f; + }else + TargetCoors = CSceneEdit::m_vecCamHeading + Source; + CSceneEdit::m_vecCurrentPosition = TargetCoors; + CSceneEdit::m_vecCamHeading = TargetCoors - Source; + + if(Alpha > DEGTORAD(89.5f)) Alpha = DEGTORAD(89.5f); + else if(Alpha < DEGTORAD(-89.5f)) Alpha = DEGTORAD(-89.5f); + + if(CPad::GetPad(1)->GetSquare() || CPad::GetPad(1)->GetLeftMouse()) + Speed += 0.1f; + else if(CPad::GetPad(1)->GetCross() || CPad::GetPad(1)->GetRightMouse()) + Speed -= 0.1f; + else + Speed = 0.0f; + if(Speed > 70.0f) Speed = 70.0f; + if(Speed < -70.0f) Speed = -70.0f; + + Front = TargetCoors - Source; + Front.Normalise(); + Source = Source + Front*Speed; + + if(Source.z < -450.0f) + Source.z = -450.0f; + + if(CPad::GetPad(1)->GetRightShoulder2JustDown()){ + if(FindPlayerVehicle()) + FindPlayerVehicle()->Teleport(Source); + else + CWorld::Players[CWorld::PlayerInFocus].m_pPed->SetPosition(Source); + + } + + // stay inside sectors + while(CWorld::GetSectorX(Source.x) > NUMSECTORS_X-5.0f) + Source.x -= 1.0f; + while(CWorld::GetSectorX(Source.x) < 5.0f) + Source.x += 1.0f; + while(CWorld::GetSectorY(Source.y) > NUMSECTORS_X-5.0f) + Source.y -= 1.0f; + while(CWorld::GetSectorY(Source.y) < 5.0f) + Source.y += 1.0f; + GetVectorsReadyForRW(); + + if(CPad::GetPad(1)->GetLeftShockJustDown() && gbBigWhiteDebugLightSwitchedOn) + CShadows::StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &Source, + 12.0f, 0.0f, 0.0f, -12.0f, + 128, 128, 128, 128, 1000.0f, false, 1.0f, nil, false); + + if(CHud::m_Wants_To_Draw_Hud){ + char str[256]; + sprintf(str, "CamX: %f CamY: %f CamZ: %f", Source.x, Source.y, Source.z); + sprintf(str, "Frontx: %f, Fronty: %f, Frontz: %f ", Front.x, Front.y, Front.z); + sprintf(str, "Look@: %f, Look@: %f, Look@: %f ", Front.x + Source.x, Front.y + Source.y, Front.z + Source.z); + } +} +#endif + +void +CCam::Process_ModelView(const CVector &CameraTarget, float, float, float) +{ + CVector TargetCoors = CameraTarget; + float Angle = Atan2(Front.x, Front.y); + FOV = DefaultFOV; + + Angle += CPad::GetPad(0)->GetLeftStickX()/1280.0f; + if(Distance < 10.0f) + Distance += CPad::GetPad(0)->GetLeftStickY()/1000.0f; + else + Distance += CPad::GetPad(0)->GetLeftStickY() * ((Distance - 10.0f)/20.0f + 1.0f) / 1000.0f; +#ifdef IMPROVED_CAMERA + if(CPad::GetPad(0)->GetLeftMouse()){ + Distance += DEGTORAD(CPad::GetPad(0)->GetMouseY()/2.0f); + Angle += DEGTORAD(CPad::GetPad(0)->GetMouseX()/2.0f); + } +#endif + if(Distance < 1.5f) + Distance = 1.5f; + + Front.x = Cos(0.3f) * Sin(Angle); + Front.y = Cos(0.3f) * Cos(Angle); + Front.z = -Sin(0.3f); + Source = CameraTarget - Distance*Front; + + GetVectorsReadyForRW(); +} + +float DEADCAM_HEIGHT_START = 2.0f; +float DEADCAM_HEIGHT_RATE = 0.04f; +float DEADCAM_WAFT_AMPLITUDE = 2.0f; +float DEADCAM_WAFT_RATE = 600.0f; +float DEADCAM_WAFT_TILT_AMP = -0.35f; + +void +CCam::ProcessPedsDeadBaby(void) +{ + CVector TargetCoors; + CVector CamPos; + + if(TheCamera.pTargetEntity->IsPed()) + ((CPed*)TheCamera.pTargetEntity)->m_pedIK.GetComponentPosition(TargetCoors, PED_MID); + else if(TheCamera.pTargetEntity->IsVehicle()){ + TargetCoors = TheCamera.pTargetEntity->GetPosition(); + TargetCoors.z += TheCamera.pTargetEntity->GetColModel()->boundingBox.max.z; + }else + return; + + if(ResetStatics){ + TheCamera.m_uiTimeLastChange = CTimer::GetTimeInMilliseconds(); + CamPos = TargetCoors; + CamPos.z += DEADCAM_HEIGHT_START; + float WaterZ = 0.0f; + if(CWaterLevel::GetWaterLevelNoWaves(TargetCoors.x, TargetCoors.y, TargetCoors.z, &WaterZ)){ + if(WaterZ + 1.5f > CamPos.z) + CamPos.z = WaterZ + 1.5f; + } + CVector Right = CrossProduct(TheCamera.pTargetEntity->GetForward(), CVector(0.0f, 0.0f, 1.0f)); + Right.z = 0.0f; + Right.Normalise(); + Front = TargetCoors - CamPos; + Front.Normalise(); + Up = CrossProduct(Right, Front); + Up.Normalise(); + ResetStatics = false; + }else{ + CamPos = Source; + if(CWorld::TestSphereAgainstWorld(CamPos+CVector(0.0f, 0.0f, 0.2f), 0.3f, TheCamera.pTargetEntity, true, true, false, true, false, true) == nil) + CamPos.z += DEADCAM_HEIGHT_RATE*CTimer::GetTimeStep(); + CVector Right = CrossProduct(TheCamera.pTargetEntity->GetForward(), CVector(0.0f, 0.0f, 1.0f)); + Right.z = 0.0f; + Right.Normalise(); + + float Time = CTimer::GetTimeInMilliseconds() - TheCamera.m_uiTimeLastChange; + CVector WaftOffset = DEADCAM_WAFT_AMPLITUDE * Min(1000.0f,Time)/1000.0f * Sin(Time/DEADCAM_WAFT_RATE) * Right; + CVector WaftPos = TargetCoors + WaftOffset; + WaftPos.z = CamPos.z; + CVector WaftFront = WaftPos - CamPos; + WaftFront.Normalise(); + if(CWorld::TestSphereAgainstWorld(CamPos+0.2f*WaftFront, 0.3f, TheCamera.pTargetEntity, true, true, false, true, false, true) == nil) + CamPos = WaftPos; + + Front = CVector(0.0f, 0.0f, -1.0f); + Front += Cos(Time/DEADCAM_WAFT_RATE) * DEADCAM_WAFT_TILT_AMP * Min(2000.0f,Time)/2000.0f * Right; + + Front.Normalise(); + Up = CrossProduct(Right, Front); + Up.Normalise(); + } + + Source = CamPos; + CVector OrigSource = Source; + TheCamera.AvoidTheGeometry(OrigSource, TargetCoors, Source, FOV); + TheCamera.m_bMoveCamToAvoidGeom = false; +} + +float ARRESTDIST_BEHIND_COP = 5.0f; +float ARRESTDIST_RIGHTOF_COP = 3.0f; +float ARRESTDIST_ABOVE_COP = 1.4f; // unused +float ARRESTDIST_MINFROM_PLAYER = 8.0f; +float ARRESTCAM_LAMP_BEST_DIST = 17.0f; +float ARRESTCAM_ROTATION_SPEED = 0.1f; +float ARRESTCAM_ROTATION_UP = 0.05f; +float ARRESTCAM_S_ROTATION_UP = 0.1f; +float ARRESTDIST_ALONG_GROUND = 5.0f; +float ARRESTDIST_SIDE_GROUND = 10.0f; +float ARRESTDIST_ABOVE_GROUND = 0.7f; +float ARRESTCAM_LAMPPOST_ROTATEDIST = 10.0f; +float ARRESTCAM_LAMPPOST_TRANSLATE = 0.1f; + +bool +CCam::GetLookAlongGroundPos(CEntity *Target, CPed *Cop, CVector &TargetCoors, CVector &SourceOut) +{ + if(Target == nil || Cop == nil) + return false; + CVector CopToTarget = TargetCoors - Cop->GetPosition(); + CopToTarget.z = 0.0f; + CopToTarget.Normalise(); + SourceOut = TargetCoors + ARRESTDIST_ALONG_GROUND*CopToTarget; + CVector Side = CrossProduct(CopToTarget, CVector(0.0f, 0.0f, 1.0f)); + SourceOut += ARRESTDIST_SIDE_GROUND*Side; + SourceOut.z += 5.0f; + bool found = false; + float ground = CWorld::FindGroundZFor3DCoord(SourceOut.x, SourceOut.y, SourceOut.z, &found); + if(found) + SourceOut.z = ground + ARRESTDIST_ABOVE_GROUND; + return true; +} + +bool +CCam::GetLookFromLampPostPos(CEntity *Target, CPed *Cop, CVector &TargetCoors, CVector &SourceOut) +{ + int i; + int16 NumObjects; + CEntity *Objects[16]; + CEntity *NearestLampPost = nil; + CWorld::FindObjectsInRange(TargetCoors, 30.0f, true, &NumObjects, 15, Objects, false, false, false, true, true); + float NearestDist = 10000.0f; + for(i = 0; i < NumObjects; i++){ + if(Objects[i]->GetIsStatic() && Objects[i]->GetUp().z > 0.9f && IsLampPost(Objects[i]->GetModelIndex())){ + float Dist = (Objects[i]->GetPosition() - TargetCoors).Magnitude2D(); + if(Abs(ARRESTCAM_LAMP_BEST_DIST - Dist) < NearestDist){ + CVector TestStart = Objects[i]->GetColModel()->boundingBox.max; + TestStart = Objects[i]->GetMatrix() * TestStart; + CVector TestEnd = TestStart - TargetCoors; + TestEnd.Normalise(); + TestEnd += TargetCoors; + if(CWorld::GetIsLineOfSightClear(TestStart, TestEnd, true, false, false, false, false, true, true)){ + NearestDist = Abs(ARRESTCAM_LAMP_BEST_DIST - Dist); + NearestLampPost = Objects[i]; + SourceOut = TestStart; + } + } + } + } + return NearestLampPost != nil; +} + +bool +CCam::GetLookOverShoulderPos(CEntity *Target, CPed *Cop, CVector &TargetCoors, CVector &SourceOut) +{ + if(Target == nil || Cop == nil) + return false; + CVector CopCoors = Cop->GetPosition(); + CVector CopToTarget = TargetCoors - CopCoors; + CVector Side = CrossProduct(CopToTarget, CVector(0.0f, 0.0f, 1.0f)); + Side.Normalise(); + CopCoors += ARRESTDIST_RIGHTOF_COP * Side; + CopToTarget.Normalise(); + if(CopToTarget.z < -0.7071f){ + CopToTarget.z = -0.7071f; + float GroundDist = CopToTarget.Magnitude2D(); + if(GroundDist > 0.0f){ + CopToTarget.x *= 0.7071f/GroundDist; + CopToTarget.y *= 0.7071f/GroundDist; + } + CopToTarget.Normalise(); + }else{ + if(CopToTarget.z > 0.0f){ + CopToTarget.z = 0.0f; + CopToTarget.Normalise(); + } + } + CopCoors -= ARRESTDIST_BEHIND_COP * CopToTarget; + CopToTarget = TargetCoors - CopCoors; + float Dist = CopToTarget.Magnitude(); + if(Dist < ARRESTDIST_MINFROM_PLAYER && Dist > 0.0f) + CopToTarget *= ARRESTDIST_MINFROM_PLAYER/Dist; + SourceOut = TargetCoors - CopToTarget; + return true; +} + +enum { + ARRESTCAM_OVERSHOULDER = 1, + ARRESTCAM_ALONGGROUND, + ARRESTCAM_ALONGGROUND_RIGHT, + ARRESTCAM_ALONGGROUND_RIGHT_UP, + ARRESTCAM_ALONGGROUND_LEFT, + ARRESTCAM_ALONGGROUND_LEFT_UP, + ARRESTCAM_LAMPPOST, +}; + +int nUsingWhichCamera; +CPed *pStoredCopPed; + +bool +CCam::ProcessArrestCamOne(void) +{ + CVector TargetPos; + CVector CamSource; + CPed *cop = nil; + FOV = 45.0f; + bool foundPos = false; + int ArrestModes[5] = { -1, -1, -1, -1, -1 }; + + if(ResetStatics){ + CPed *targetPed = (CPed*)TheCamera.pTargetEntity; + nUsingWhichCamera = 0; + if(TheCamera.pTargetEntity->IsPed()){ + ((CPed*)TheCamera.pTargetEntity)->m_pedIK.GetComponentPosition(TargetPos, PED_MID); + if(FindPlayerPed() && FindPlayerPed()->m_pArrestingCop) + cop = FindPlayerPed()->m_pArrestingCop; + if(cop && CGeneral::GetRandomNumberInRange(0.0f, 1.0f) > 0.5f){ + ArrestModes[0] = ARRESTCAM_OVERSHOULDER; + ArrestModes[1] = ARRESTCAM_ALONGGROUND; + ArrestModes[2] = ARRESTCAM_OVERSHOULDER; + ArrestModes[3] = ARRESTCAM_LAMPPOST; + }else{ + ArrestModes[0] = ARRESTCAM_ALONGGROUND; + ArrestModes[1] = ARRESTCAM_OVERSHOULDER; + ArrestModes[2] = ARRESTCAM_LAMPPOST; + } + }else if(TheCamera.pTargetEntity->IsVehicle()){ + CVehicle *targetVehicle = (CVehicle*)TheCamera.pTargetEntity; + if(targetVehicle->pDriver && targetVehicle->pDriver->IsPlayer()){ + targetPed = targetVehicle->pDriver; + targetPed->m_pedIK.GetComponentPosition(TargetPos, PED_MID); + }else{ + targetPed = nil; + TargetPos = targetVehicle->GetPosition(); + } + + if(FindPlayerPed() && FindPlayerPed()->m_pArrestingCop) + cop = FindPlayerPed()->m_pArrestingCop; + if(cop && CGeneral::GetRandomNumberInRange(0.0f, 1.0f) > 0.65f){ + ArrestModes[0] = ARRESTCAM_OVERSHOULDER; + ArrestModes[1] = ARRESTCAM_LAMPPOST; + ArrestModes[2] = ARRESTCAM_ALONGGROUND; + ArrestModes[3] = ARRESTCAM_OVERSHOULDER; + }else{ + ArrestModes[0] = ARRESTCAM_LAMPPOST; + ArrestModes[1] = ARRESTCAM_ALONGGROUND; + ArrestModes[2] = ARRESTCAM_OVERSHOULDER; + } + }else + return false; + + for(int i = 0; nUsingWhichCamera == 0 && i < ARRAY_SIZE(ArrestModes) && ArrestModes[i] > 0; i++){ + switch(ArrestModes[i]){ + case ARRESTCAM_OVERSHOULDER: + if(cop){ + foundPos = GetLookOverShoulderPos(TheCamera.pTargetEntity, cop, TargetPos, CamSource); + pStoredCopPed = cop; + cop = nil; + }else if(targetPed){ + for(int j = 0; j < targetPed->m_numNearPeds; j++){ + CPed *nearPed = targetPed->m_nearPeds[j]; + if(nearPed->GetPedState() == PED_ARREST_PLAYER) + foundPos = GetLookOverShoulderPos(TheCamera.pTargetEntity, nearPed, TargetPos, CamSource); + if(foundPos){ + pStoredCopPed = nearPed; + break; + } + } + } + break; + case ARRESTCAM_ALONGGROUND: + if(cop){ + foundPos = GetLookAlongGroundPos(TheCamera.pTargetEntity, cop, TargetPos, CamSource); + pStoredCopPed = cop; + cop = nil; + }else if(targetPed){ + for(int j = 0; j < targetPed->m_numNearPeds; j++){ + CPed *nearPed = targetPed->m_nearPeds[j]; + if(nearPed->GetPedState() == PED_ARREST_PLAYER) + foundPos = GetLookAlongGroundPos(TheCamera.pTargetEntity, nearPed, TargetPos, CamSource); + if(foundPos){ + pStoredCopPed = nearPed; + break; + } + } + } + break; + case ARRESTCAM_LAMPPOST: + foundPos = GetLookFromLampPostPos(TheCamera.pTargetEntity, cop, TargetPos, CamSource); + break; + } + + if(foundPos){ + if(pStoredCopPed) + pStoredCopPed->RegisterReference((CEntity**)&pStoredCopPed); + nUsingWhichCamera = ArrestModes[i]; + if(ArrestModes[i] == ARRESTCAM_ALONGGROUND){ + float rnd = CGeneral::GetRandomNumberInRange(0.0f, 5.0f); + if(rnd < 1.0f) nUsingWhichCamera = ARRESTCAM_ALONGGROUND; + else if(rnd < 2.0f) nUsingWhichCamera = ARRESTCAM_ALONGGROUND_RIGHT; + else if(rnd < 3.0f) nUsingWhichCamera = ARRESTCAM_ALONGGROUND_RIGHT_UP; + else if(rnd < 4.0f) nUsingWhichCamera = ARRESTCAM_ALONGGROUND_LEFT; + else nUsingWhichCamera = ARRESTCAM_ALONGGROUND_LEFT_UP; + } + }else + pStoredCopPed = nil; + } + + Source = CamSource; + CVector OrigSource = Source; + TheCamera.AvoidTheGeometry(OrigSource, TargetPos, Source, FOV); + Front = TargetPos - Source; + Front.Normalise(); + Up = CVector(0.0f, 0.0f, 1.0f); + CVector Right = CrossProduct(Front, Up); + Right.Normalise(); + Up = CrossProduct(Right, Front); + if(nUsingWhichCamera != 0) + ResetStatics = false; + return true; + } + + if(TheCamera.pTargetEntity->IsPed()){ + ((CPed*)TheCamera.pTargetEntity)->m_pedIK.GetComponentPosition(TargetPos, PED_MID); + }else if(TheCamera.pTargetEntity->IsVehicle()){ + CPed *driver = ((CVehicle*)TheCamera.pTargetEntity)->pDriver; + if(driver && driver->IsPlayer()) + driver->m_pedIK.GetComponentPosition(TargetPos, PED_MID); + else + TargetPos = TheCamera.pTargetEntity->GetPosition(); + }else + return false; + + if(nUsingWhichCamera == ARRESTCAM_OVERSHOULDER && pStoredCopPed){ + foundPos = GetLookOverShoulderPos(TheCamera.pTargetEntity, pStoredCopPed, TargetPos, CamSource); + float newZ = Source.z + ARRESTCAM_S_ROTATION_UP*CTimer::GetTimeStep(); + if(CamSource.z > newZ) + CamSource.z = newZ; + }else if(nUsingWhichCamera >= ARRESTCAM_ALONGGROUND_RIGHT && nUsingWhichCamera <= ARRESTCAM_ALONGGROUND_LEFT_UP){ + CamSource = Source; + Front = TargetPos - CamSource; + Front.Normalise(); + Up = CVector(0.0f, 0.0f, 1.0f); + CVector Right = CrossProduct(Front, Up); + if(nUsingWhichCamera == ARRESTCAM_ALONGGROUND_LEFT || nUsingWhichCamera == ARRESTCAM_ALONGGROUND_LEFT_UP) + Right *= -1.0f; + if(CWorld::TestSphereAgainstWorld(CamSource + 0.5f*Right, 0.4f, TheCamera.pTargetEntity, true, true, false, true, false, true) == nil){ + foundPos = true; + CamSource += Right*ARRESTCAM_ROTATION_SPEED*CTimer::GetTimeStep(); + if(nUsingWhichCamera == ARRESTCAM_ALONGGROUND_RIGHT_UP || nUsingWhichCamera == ARRESTCAM_ALONGGROUND_LEFT_UP){ + CamSource.z += ARRESTCAM_ROTATION_UP*CTimer::GetTimeStep(); + }else{ + bool found = false; + float ground = CWorld::FindGroundZFor3DCoord(CamSource.x, CamSource.y, CamSource.z, &found); + if(found) + CamSource.z = ground + ARRESTDIST_ABOVE_GROUND; + } + } + }else if(nUsingWhichCamera == ARRESTCAM_LAMPPOST){ + CamSource = Source; + Front = TargetPos - CamSource; + Front.z = 0.0f; + Front.Normalise(); + Up = CVector(0.0f, 0.0f, 1.0f); + CVector Right = CrossProduct(Front, Up); + Right.Normalise(); + Front = TargetPos - CamSource + Right*ARRESTCAM_LAMPPOST_ROTATEDIST; + Front.z = 0.0f; + Front.Normalise(); + if(CWorld::TestSphereAgainstWorld(CamSource + 0.5f*Front, 0.4f, TheCamera.pTargetEntity, true, true, false, true, false, true) == nil){ + foundPos = true; + CamSource += Front*ARRESTCAM_LAMPPOST_TRANSLATE*CTimer::GetTimeStep(); + } + } + + if(foundPos){ + Source = CamSource; + CVector OrigSource = Source; + TheCamera.AvoidTheGeometry(OrigSource, TargetPos, Source, FOV); + Front = TargetPos - Source; + Front.Normalise(); + Up = CVector(0.0f, 0.0f, 1.0f); + CVector Right = CrossProduct(Front, Up); + Right.Normalise(); + Up = CrossProduct(Right, Front); + }else{ + CVector OrigSource = Source; + TheCamera.AvoidTheGeometry(OrigSource, TargetPos, Source, FOV); + } + + return true; +} + +bool +CCam::ProcessArrestCamTwo(void) +{ + CPed *player = CWorld::Players[CWorld::PlayerInFocus].m_pPed; + if(!ResetStatics) + return true; + ResetStatics = false; + + CVector TargetCoors, ToCamera; + float BetaOffset; + float SourceX, SourceY; + if(&TheCamera.Cams[TheCamera.ActiveCam] == this){ + SourceX = TheCamera.Cams[(TheCamera.ActiveCam + 1) % 2].Source.x; + SourceY = TheCamera.Cams[(TheCamera.ActiveCam + 1) % 2].Source.y; + }else{ + SourceX = TheCamera.Cams[TheCamera.ActiveCam].Source.x; + SourceY = TheCamera.Cams[TheCamera.ActiveCam].Source.y; + } + + for(int i = 0; i <= 1; i++){ + int Dir = i == 0 ? 1 : -1; + + FOV = 60.0f; + TargetCoors = player->GetPosition(); + Beta = CGeneral::GetATanOfXY(TargetCoors.x-SourceX, TargetCoors.y-SourceY); + BetaOffset = DEGTORAD(Dir*80); + Source = TargetCoors + 11.5f*CVector(Cos(Beta+BetaOffset), Sin(Beta+BetaOffset), 0.0f); + + ToCamera = Source - TargetCoors; + ToCamera.Normalise(); + TargetCoors.x += 0.4f*ToCamera.x; + TargetCoors.y += 0.4f*ToCamera.y; + if(CWorld::GetIsLineOfSightClear(Source, TargetCoors, true, true, false, true, false, true, true)){ + Source.z += 5.5f; + TargetCoors += CVector(-0.8f*ToCamera.x, -0.8f*ToCamera.y, 2.2f); + m_cvecTargetCoorsForFudgeInter = TargetCoors; + Front = TargetCoors - Source; + ResetStatics = false; + GetVectorsReadyForRW(); + return true; + } + } + return false; +} + + +#ifdef FREE_CAM +void +CCam::Process_FollowPed_Rotation(const CVector &CameraTarget, float TargetOrientation, float, float) +{ + FOV = DefaultFOV; + + const float MinDist = 2.0f; + const float MaxDist = 2.0f + TheCamera.m_fPedZoomValueSmooth; + const float BaseOffset = 0.75f; // base height of camera above target + + CVector TargetCoors = CameraTarget; + + TargetCoors.z += m_fSyphonModeTargetZOffSet; + TargetCoors = DoAverageOnVector(TargetCoors); + TargetCoors.z += BaseOffset; // add offset so alpha evens out to 0 +// TargetCoors.z += m_fRoadOffSet; + + CVector Dist = Source - TargetCoors; + CVector ToCam; + + bool Shooting = false; + if(((CPed*)CamTargetEntity)->GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) + if(CPad::GetPad(0)->GetWeapon()) + Shooting = true; + if(((CPed*)CamTargetEntity)->GetWeapon()->m_eWeaponType == WEAPONTYPE_DETONATOR || + ((CPed*)CamTargetEntity)->GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT) + Shooting = false; + + + if(ResetStatics){ + // Coming out of top down here probably + // so keep Beta, reset alpha and calculate vectors + Beta = CGeneral::GetATanOfXY(Dist.x, Dist.y); + Alpha = 0.0f; + + Dist = MaxDist*CVector(Cos(Alpha) * Cos(Beta), Cos(Alpha) * Sin(Beta), Sin(Alpha)); + Source = TargetCoors + Dist; + + ResetStatics = false; + } + + // Drag the camera along at the look-down offset + float CamDist = Dist.Magnitude(); + if(CamDist == 0.0f) + Dist = CVector(1.0f, 1.0f, 0.0f); + else if(CamDist < MinDist) + Dist *= MinDist/CamDist; + else if(CamDist > MaxDist) + Dist *= MaxDist/CamDist; + CamDist = Dist.Magnitude(); + + // Beta = 0 is looking east, HALFPI is north, &c. + // Alpha positive is looking up + float GroundDist = Dist.Magnitude2D(); + Beta = CGeneral::GetATanOfXY(-Dist.x, -Dist.y); + Alpha = CGeneral::GetATanOfXY(GroundDist, -Dist.z); + while(Beta >= PI) Beta -= 2.0f*PI; + while(Beta < -PI) Beta += 2.0f*PI; + while(Alpha >= PI) Alpha -= 2.0f*PI; + while(Alpha < -PI) Alpha += 2.0f*PI; + + // Look around + bool UseMouse = false; + float MouseX = CPad::GetPad(0)->GetMouseX(); + float MouseY = CPad::GetPad(0)->GetMouseY(); + float LookLeftRight, LookUpDown; +/* + if((MouseX != 0.0f || MouseY != 0.0f) && !CPad::GetPad(0)->ArePlayerControlsDisabled()){ + UseMouse = true; + LookLeftRight = -2.5f*MouseX; + LookUpDown = 4.0f*MouseY; + }else +*/ + { + LookLeftRight = -CPad::GetPad(0)->LookAroundLeftRight(); + LookUpDown = CPad::GetPad(0)->LookAroundUpDown(); + } + float AlphaOffset, BetaOffset; + if(UseMouse){ + BetaOffset = LookLeftRight * TheCamera.m_fMouseAccelHorzntl * FOV/80.0f; + AlphaOffset = LookUpDown * TheCamera.m_fMouseAccelVertical * FOV/80.0f; + }else{ + BetaOffset = LookLeftRight * fStickSens * (1.0f/20.0f) * FOV/80.0f * CTimer::GetTimeStep(); + AlphaOffset = LookUpDown * fStickSens * (0.6f/20.0f) * FOV/80.0f * CTimer::GetTimeStep(); + } + + // Stop centering once stick has been touched + if(BetaOffset) + Rotating = false; + + Beta += BetaOffset; + Alpha += AlphaOffset; + while(Beta >= PI) Beta -= 2.0f*PI; + while(Beta < -PI) Beta += 2.0f*PI; + if(Alpha > DEGTORAD(45.0f)) Alpha = DEGTORAD(45.0f); + else if(Alpha < -DEGTORAD(89.5f)) Alpha = -DEGTORAD(89.5f); + + + float BetaDiff = TargetOrientation+PI - Beta; + while(BetaDiff >= PI) BetaDiff -= 2.0f*PI; + while(BetaDiff < -PI) BetaDiff += 2.0f*PI; + float TargetAlpha = Alpha; + // 12deg to account for our little height offset. we're not working on the true alpha here + const float AlphaLimitUp = DEGTORAD(15.0f) + DEGTORAD(12.0f); + const float AlphaLimitDown = -DEGTORAD(15.0f) + DEGTORAD(12.0f); + if(Abs(BetaDiff) < DEGTORAD(25.0f) && ((CPed*)CamTargetEntity)->GetMoveSpeed().Magnitude2D() > 0.01f){ + // Limit alpha when player is walking towards camera + if(TargetAlpha > AlphaLimitUp) TargetAlpha = AlphaLimitUp; + if(TargetAlpha < AlphaLimitDown) TargetAlpha = AlphaLimitDown; + } + + WellBufferMe(TargetAlpha, &Alpha, &AlphaSpeed, 0.2f, 0.1f, true); + + if(CPad::GetPad(0)->ForceCameraBehindPlayer() || Shooting){ + m_fTargetBeta = TargetOrientation; + Rotating = true; + } + + if(Rotating){ + WellBufferMe(m_fTargetBeta, &Beta, &BetaSpeed, 0.1f, 0.06f, true); + float DeltaBeta = m_fTargetBeta - Beta; + while(DeltaBeta >= PI) DeltaBeta -= 2*PI; + while(DeltaBeta < -PI) DeltaBeta += 2*PI; + if(Abs(DeltaBeta) < 0.06f) + Rotating = false; + } + + if(TheCamera.m_bUseTransitionBeta) + Beta = CGeneral::GetATanOfXY(-Cos(m_fTransitionBeta), -Sin(m_fTransitionBeta)); + + if(TheCamera.m_bUseTransitionBeta) + Beta = CGeneral::GetATanOfXY(-Cos(m_fTransitionBeta), -Sin(m_fTransitionBeta)); + + Front = CVector(Cos(Alpha) * Cos(Beta), Cos(Alpha) * Sin(Beta), Sin(Alpha)); + Source = TargetCoors - Front*CamDist; + TargetCoors.z -= BaseOffset; // now get back to the real target coors again + + m_cvecTargetCoorsForFudgeInter = TargetCoors; + + + Front = TargetCoors - Source; + Front.Normalise(); + + + + /* + * Handle collisions - taken from FollowPedWithMouse + */ + + CEntity *entity; + CColPoint colPoint; + // Clip Source and fix near clip + CWorld::pIgnoreEntity = CamTargetEntity; + entity = nil; + if(CWorld::ProcessLineOfSight(TargetCoors, Source, colPoint, entity, true, true, true, true, false, false, true)){ + float PedColDist = (TargetCoors - colPoint.point).Magnitude(); + float ColCamDist = CamDist - PedColDist; + if(entity->IsPed() && ColCamDist > DEFAULT_NEAR + 0.1f){ + // Ped in the way but not clipping through + if(CWorld::ProcessLineOfSight(colPoint.point, Source, colPoint, entity, true, true, true, true, false, false, true)){ + PedColDist = (TargetCoors - colPoint.point).Magnitude(); + Source = colPoint.point; + if(PedColDist < DEFAULT_NEAR + 0.3f) + RwCameraSetNearClipPlane(Scene.camera, Max(PedColDist-0.3f, 0.05f)); + }else{ + RwCameraSetNearClipPlane(Scene.camera, Min(ColCamDist-0.35f, DEFAULT_NEAR)); + } + }else{ + Source = colPoint.point; + if(PedColDist < DEFAULT_NEAR + 0.3f) + RwCameraSetNearClipPlane(Scene.camera, Max(PedColDist-0.3f, 0.05f)); + } + } + CWorld::pIgnoreEntity = nil; + + float ViewPlaneHeight = Tan(DEGTORAD(FOV) / 2.0f); + float ViewPlaneWidth = ViewPlaneHeight * CDraw::CalculateAspectRatio() * fTweakFOV; + float Near = RwCameraGetNearClipPlane(Scene.camera); + float radius = ViewPlaneWidth*Near; + entity = CWorld::TestSphereAgainstWorld(Source + Front*Near, radius, nil, true, true, false, true, false, false); + int i = 0; + while(entity){ + CVector CamToCol = gaTempSphereColPoints[0].point - Source; + float frontDist = DotProduct(CamToCol, Front); + float dist = (CamToCol - Front*frontDist).Magnitude() / ViewPlaneWidth; + + // Try to decrease near clip + dist = Max(Min(Near, dist), 0.1f); + if(dist < Near) + RwCameraSetNearClipPlane(Scene.camera, dist); + + // Move forward a bit + if(dist == 0.1f) + Source += (TargetCoors - Source)*0.3f; + + // Keep testing + Near = RwCameraGetNearClipPlane(Scene.camera); + radius = ViewPlaneWidth*Near; + entity = CWorld::TestSphereAgainstWorld(Source + Front*Near, radius, nil, true, true, false, true, false, false); + + i++; + if(i > 5) + entity = nil; + } + + GetVectorsReadyForRW(); +} + +// LCS cam hehe +void +CCam::Process_FollowCar_SA(const CVector& CameraTarget, float TargetOrientation, float, float) +{ + // Missing things on III CCam + static CVector m_aTargetHistoryPosOne; + static CVector m_aTargetHistoryPosTwo; + static CVector m_aTargetHistoryPosThree; + static int m_nCurrentHistoryPoints = 0; + static float lastBeta = -9999.0f; + static float lastAlpha = -9999.0f; + static float stepsLeftToChangeBetaByMouse; + static float dontCollideWithCars; + static bool alphaCorrected; + static float heightIncreaseMult; + + if (!CamTargetEntity->IsVehicle()) + return; + + CVehicle* car = (CVehicle*)CamTargetEntity; + CVector TargetCoors = CameraTarget; + uint8 camSetArrPos = 0; + + // We may need those later + bool isPlane = car->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE; + bool isHeli = car->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI; + bool isBike = car->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE; + bool isCar = car->IsCar() && !isPlane && !isHeli && !isBike; + + CPad* pad = CPad::GetPad(0); + + // Next direction is non-existent in III + uint8 nextDirectionIsForward = !(pad->GetLookBehindForCar() || pad->GetLookBehindForPed() || pad->GetLookLeft() || pad->GetLookRight()) && + DirectionWasLooking == LOOKING_FORWARD; + + if (car->GetModelIndex() == MI_FIRETRUCK) { + camSetArrPos = 7; + } else if (car->GetModelIndex() == MI_RCBANDIT || car->GetModelIndex() == MI_RCBARON) { + camSetArrPos = 5; + } else if (car->GetModelIndex() == MI_RCGOBLIN || car->GetModelIndex() == MI_RCRAIDER) { + camSetArrPos = 6; + } else if (car->IsBoat()) { + camSetArrPos = 4; + } else if (isBike) { + camSetArrPos = 1; + } else if (isPlane) { + camSetArrPos = 3; + } else if (isHeli) { + camSetArrPos = 2; + } + + // LCS one but index 1(firetruck) moved to last + float CARCAM_SET[][15] = { + {1.3f, 1.0f, 0.4f, 10.0f, 15.0f, 0.5f, 1.0f, 1.0f, 0.85f, 0.2f, 0.075f, 0.05f, 0.8f, DEGTORAD(45.0f), DEGTORAD(89.0f)}, // cars + {1.1f, 1.0f, 0.1f, 10.0f, 11.0f, 0.5f, 1.0f, 1.0f, 0.85f, 0.2f, 0.075f, 0.05f, 0.75f, DEGTORAD(45.0f), DEGTORAD(89.0f)}, // bike + {1.1f, 1.0f, 0.2f, 10.0f, 15.0f, 0.05f, 0.05f, 0.0f, 0.9f, 0.05f, 0.01f, 0.05f, 1.0f, DEGTORAD(10.0f), DEGTORAD(70.0f)}, // heli (SA values) + {1.1f, 3.5f, 0.2f, 10.0f, 25.0f, 0.5f, 1.0f, 1.0f, 0.75f, 0.1f, 0.005f, 0.2f, 1.0f, DEGTORAD(89.0f), DEGTORAD(89.0f)}, // plane (SA values) + {0.9f, 1.0f, 0.1f, 10.0f, 15.0f, 0.5f, 1.0f, 0.0f, 0.9f, 0.05f, 0.005f, 0.05f, 1.0f, -0.2f, DEGTORAD(70.0f)}, // boat + {1.1f, 1.0f, 0.2f, 10.0f, 5.0f, 0.5f, 1.0f, 1.0f, 0.75f, 0.1f, 0.005f, 0.2f, 1.0f, DEGTORAD(45.0f), DEGTORAD(89.0f)}, // rc cars + {1.1f, 1.0f, 0.2f, 10.0f, 5.0f, 0.5f, 1.0f, 1.0f, 0.75f, 0.1f, 0.005f, 0.2f, 1.0f, DEGTORAD(20.0f), DEGTORAD(70.0f)}, // rc heli/planes + {1.3f, 1.0f, 0.4f, 10.0f, 15.0f, 0.5f, 1.0f, 1.0f, 0.85f, 0.2f, 0.075f, 0.05f, 0.8f, -0.18f, DEGTORAD(40.0f)}, // firetruck... + }; + + // RC Heli/planes use same alpha values with heli/planes (LCS firetruck will fallback to 0) + uint8 alphaArrPos = (camSetArrPos > 4 ? (isPlane ? 3 : (isHeli ? 2 : 0)) : camSetArrPos); + float zoomModeAlphaOffset = 0.0f; + static float ZmOneAlphaOffsetLCS[] = { 0.12f, 0.08f, 0.15f, 0.08f, 0.08f }; + static float ZmTwoAlphaOffsetLCS[] = { 0.1f, 0.08f, 0.3f, 0.08f, 0.08f }; + static float ZmThreeAlphaOffsetLCS[] = { 0.065f, 0.05f, 0.15f, 0.06f, 0.08f }; + + if (isHeli && car->GetStatus() == STATUS_PLAYER_REMOTE) + zoomModeAlphaOffset = ZmTwoAlphaOffsetLCS[alphaArrPos]; + else { + switch ((int)TheCamera.CarZoomIndicator) { + // near + case CAM_ZOOM_1: + zoomModeAlphaOffset = ZmOneAlphaOffsetLCS[alphaArrPos]; + break; + // mid + case CAM_ZOOM_2: + zoomModeAlphaOffset = ZmTwoAlphaOffsetLCS[alphaArrPos]; + break; + // far + case CAM_ZOOM_3: + zoomModeAlphaOffset = ZmThreeAlphaOffsetLCS[alphaArrPos]; + break; + default: + break; + } + } + + CColModel* carCol = (CColModel*)car->GetColModel(); + float colMaxZ = carCol->boundingBox.max.z; // As opposed to LCS and SA, VC does this: carCol->boundingBox.max.z - carCol->boundingBox.min.z; + float approxCarLength = 2.0f * Abs(carCol->boundingBox.min.y); // SA taxi min.y = -2.95, max.z = 0.883502f + + float newDistance = TheCamera.CarZoomValueSmooth + CARCAM_SET[camSetArrPos][1] + approxCarLength; + + // Taken from VC CCam::Cam_On_A_String_Unobscured. If we don't this, we will end up seeing the world from the inside of RC Goblin/Raider. + // I couldn't find where SA does that. It's possible that they've increased the size of these veh.'s collision bounding box. + + if (car->m_modelIndex == MI_RCRAIDER || car->m_modelIndex == MI_RCGOBLIN) + newDistance += INIT_RC_HELI_HORI_EXTRA; + else if (car->m_modelIndex == MI_RCBARON) + newDistance += INIT_RC_PLANE_HORI_EXTRA; + + float minDistForThisCar = approxCarLength * CARCAM_SET[camSetArrPos][3]; + + if (!isHeli || car->GetStatus() == STATUS_PLAYER_REMOTE) { + float radiusToStayOutside = colMaxZ * CARCAM_SET[camSetArrPos][0] - CARCAM_SET[camSetArrPos][2]; + if (radiusToStayOutside > 0.0f) { + TargetCoors.z += radiusToStayOutside; + newDistance += radiusToStayOutside; + zoomModeAlphaOffset += 0.3f / newDistance * radiusToStayOutside; + } + } else { + // 0.6f = fTestShiftHeliCamTarget + TargetCoors += 0.6f * car->GetUp() * colMaxZ; + } + + if (car->m_modelIndex == MI_RCGOBLIN) + zoomModeAlphaOffset += 0.178997f; + + float minDistForVehType = CARCAM_SET[camSetArrPos][4]; + + if (TheCamera.CarZoomIndicator == CAM_ZOOM_1 && (camSetArrPos < 2 || camSetArrPos == 7)) { + minDistForVehType = minDistForVehType * 0.65f; + } + + float nextDistance = Max(newDistance, minDistForVehType); + + CA_MAX_DISTANCE = newDistance; + CA_MIN_DISTANCE = 3.5f; + + if (ResetStatics) { + FOV = DefaultFOV; + } else { + if (isCar || isBike) { + // 0.4f: CAR_FOV_START_SPEED + if (DotProduct(car->GetForward(), car->m_vecMoveSpeed) > 0.4f) + FOV += (DotProduct(car->GetForward(), car->m_vecMoveSpeed) - 0.4f) * CTimer::GetTimeStep(); + } + + if (FOV > DefaultFOV) + // 0.98f: CAR_FOV_FADE_MULT + FOV = Pow(0.98f, CTimer::GetTimeStep()) * (FOV - DefaultFOV) + DefaultFOV; + + FOV = Clamp(FOV, DefaultFOV, DefaultFOV + 30.0f); + } + + // WORKAROUND: I still don't know how looking behind works (m_bCamDirectlyInFront is unused in III, they seem to use m_bUseTransitionBeta) + if (pad->GetLookBehindForCar()) + if (DirectionWasLooking == LOOKING_FORWARD || !LookingBehind) + TheCamera.m_bCamDirectlyInFront = true; + + // Taken from RotCamIfInFrontCar, because we don't call it anymore + if (!(pad->GetLookBehindForCar() || pad->GetLookBehindForPed() || pad->GetLookLeft() || pad->GetLookRight())) + if (DirectionWasLooking != LOOKING_FORWARD) + TheCamera.m_bCamDirectlyBehind = true; + + // Called when we just entered the car, just started to look behind or returned back from looking left, right or behind + if (ResetStatics || TheCamera.m_bCamDirectlyBehind || TheCamera.m_bCamDirectlyInFront) { + ResetStatics = false; + Rotating = false; + m_bCollisionChecksOn = true; + + if (!TheCamera.m_bJustCameOutOfGarage) { + Alpha = 0.0f; + Beta = car->GetForward().Heading() - HALFPI; + if (TheCamera.m_bCamDirectlyInFront) { + Beta += PI; + } + } + + BetaSpeed = 0.0; + AlphaSpeed = 0.0; + Distance = 1000.0; + + Front.x = -(Cos(Beta) * Cos(Alpha)); + Front.y = -(Sin(Beta) * Cos(Alpha)); + Front.z = Sin(Alpha); + + m_aTargetHistoryPosOne = TargetCoors - nextDistance * Front; + + m_aTargetHistoryPosTwo = TargetCoors - newDistance * Front; + + m_nCurrentHistoryPoints = 0; + if (!TheCamera.m_bJustCameOutOfGarage) + Alpha = -zoomModeAlphaOffset; + } + + Front = TargetCoors - m_aTargetHistoryPosOne; + Front.Normalise(); + + // Code that makes cam rotate around the car + float camRightHeading = Front.Heading() - HALFPI; + if (camRightHeading < -PI) + camRightHeading = camRightHeading + TWOPI; + + float velocityRightHeading; + if (car->m_vecMoveSpeed.Magnitude2D() <= 0.02f) + velocityRightHeading = camRightHeading; + else + velocityRightHeading = car->m_vecMoveSpeed.Heading() - HALFPI; + + if (velocityRightHeading < camRightHeading - PI) + velocityRightHeading = velocityRightHeading + TWOPI; + else if (velocityRightHeading > camRightHeading + PI) + velocityRightHeading = velocityRightHeading - TWOPI; + + float betaChangeMult1 = CTimer::GetTimeStep() * CARCAM_SET[camSetArrPos][10]; + float betaChangeLimit = CTimer::GetTimeStep() * CARCAM_SET[camSetArrPos][11]; + + float betaChangeMult2 = (car->m_vecMoveSpeed - DotProduct(car->m_vecMoveSpeed, Front) * Front).Magnitude(); + + float betaChange = Min(1.0f, betaChangeMult1 * betaChangeMult2) * (velocityRightHeading - camRightHeading); + if (betaChange <= betaChangeLimit) { + if (betaChange < -betaChangeLimit) + betaChange = -betaChangeLimit; + } else { + betaChange = betaChangeLimit; + } + float targetBeta = camRightHeading + betaChange; + + if (targetBeta < Beta - HALFPI) + targetBeta += TWOPI; + else if (targetBeta > Beta + PI) + targetBeta -= TWOPI; + + float carPosChange = (TargetCoors - m_aTargetHistoryPosTwo).Magnitude(); + if (carPosChange < newDistance && newDistance > minDistForThisCar) { + newDistance = Max(minDistForThisCar, carPosChange); + } + float maxAlphaAllowed = CARCAM_SET[camSetArrPos][13]; + + // Originally this is to prevent camera enter into car while we're stopping, but what about moving??? + // This is also original LCS and SA bug, or some attempt to fix lag. We'll never know + + // if (car->m_vecMoveSpeed.MagnitudeSqr() < sq(0.2f)) + if (car->GetModelIndex() != MI_FIRETRUCK) + if (!isBike || ((CBike*)car)->m_nWheelsOnGround > 3) + if (!isHeli && (!isPlane || ((CAutomobile*)car)->m_nWheelsOnGround)) { + + CVector left = CrossProduct(car->GetForward(), CVector(0.0f, 0.0f, 1.0f)); + left.Normalise(); + CVector up = CrossProduct(left, car->GetForward()); + up.Normalise(); + float lookingUp = DotProduct(up, Front); + if (lookingUp > 0.0f) { + float v88 = Asin(Abs(Sin(Beta - (car->GetForward().Heading() - HALFPI)))); + float v200; + if (v88 <= Atan2(carCol->boundingBox.max.x, -carCol->boundingBox.min.y)) { + v200 = (1.5f - carCol->boundingBox.min.y) / Cos(v88); + } else { + float a6g = 1.2f + carCol->boundingBox.max.x; + v200 = a6g / Cos(Max(0.0f, HALFPI - v88)); + } + maxAlphaAllowed = Cos(Beta - (car->GetForward().Heading() - HALFPI)) * Atan2(car->GetForward().z, car->GetForward().Magnitude2D()) + + Atan2(TargetCoors.z - car->GetPosition().z + car->GetHeightAboveRoad(), v200 * 1.2f); + + if (isCar && ((CAutomobile*)car)->m_nWheelsOnGround > 1 && Abs(DotProduct(car->m_vecTurnSpeed, car->GetForward())) < 0.05f) { + maxAlphaAllowed += Cos(Beta - (car->GetForward().Heading() - HALFPI) + HALFPI) * Atan2(car->GetRight().z, car->GetRight().Magnitude2D()); + } + } + } + + float targetAlpha = Asin(Clamp(Front.z, -1.0f, 1.0f)) - zoomModeAlphaOffset; + if (targetAlpha <= maxAlphaAllowed) { + if (targetAlpha < -CARCAM_SET[camSetArrPos][14]) + targetAlpha = -CARCAM_SET[camSetArrPos][14]; + } else { + targetAlpha = maxAlphaAllowed; + } + float maxAlphaBlendAmount = CTimer::GetTimeStep() * CARCAM_SET[camSetArrPos][6]; + float targetAlphaBlendAmount = (1.0f - Pow(CARCAM_SET[camSetArrPos][5], CTimer::GetTimeStep())) * (targetAlpha - Alpha); + if (targetAlphaBlendAmount <= maxAlphaBlendAmount) { + if (targetAlphaBlendAmount < -maxAlphaBlendAmount) + targetAlphaBlendAmount = -maxAlphaBlendAmount; + } else { + targetAlphaBlendAmount = maxAlphaBlendAmount; + } + + // Using GetCarGun(LR/UD) will give us same unprocessed RightStick value as SA + float stickX = -(pad->GetCarGunLeftRight()); + float stickY = -pad->GetCarGunUpDown(); + + // In SA this checks for m_bUseMouse3rdPerson so num2 / num8 do not move camera + // when Keyboard & Mouse controls are used. To make it work better with III/VC, check for actual pad state instead + if (!CPad::IsAffectedByController && !isCar) + stickY = 0.0f; + else if (CPad::bInvertLook4Pad) + stickY = -stickY; + + float xMovement = Abs(stickX) * (FOV / 80.0f * 5.f / 70.f) * stickX * 0.007f * 0.007f; + float yMovement = Abs(stickY) * (FOV / 80.0f * 3.f / 70.f) * stickY * 0.007f * 0.007f; + + bool correctAlpha = true; + // if (SA checks if we aren't in work car, why?) { + if (!isCar || car->GetModelIndex() != MI_VOODOO) { + correctAlpha = false; + } + else { + xMovement = 0.0f; + yMovement = 0.0f; + } + // } else + // yMovement = 0.0; + + if (!nextDirectionIsForward) { + yMovement = 0.0f; + xMovement = 0.0f; + } + + if (camSetArrPos == 0 || camSetArrPos == 7) { + // This is not working on cars as SA + // Because III/VC doesn't have any buttons tied to LeftStick if you're not in Classic Configuration, using Dodo or using GInput/Pad, so :shrug: + if (Abs(pad->GetSteeringUpDown()) > 120.0f) { + if (car->pDriver && car->pDriver->m_objective != OBJECTIVE_LEAVE_CAR) { + yMovement += Abs(pad->GetSteeringUpDown()) * (FOV / 80.0f * 3.f / 70.f) * pad->GetSteeringUpDown() * 0.007f * 0.007f * 0.5; + } + } + } + + if (yMovement > 0.0) + yMovement = yMovement * 0.5; + + bool mouseChangesBeta = false; + + // FIX: Disable mouse movement in drive-by, it's buggy. Original SA bug. + if (/*bFreeMouseCam &&*/ CCamera::m_bUseMouse3rdPerson && !pad->ArePlayerControlsDisabled() && nextDirectionIsForward) { + float mouseY = pad->GetMouseY() * 2.0f; + float mouseX = pad->GetMouseX() * -2.0f; + + // If you want an ability to toggle free cam while steering with mouse, you can add an OR after DisableMouseSteering. + // There was a pad->NewState.m_bVehicleMouseLook in SA, which doesn't exists in III. + + if ((mouseX != 0.0 || mouseY != 0.0) && (CVehicle::m_bDisableMouseSteering)) { + yMovement = mouseY * FOV / 80.0f * TheCamera.m_fMouseAccelHorzntl; // Same as SA, horizontal sensitivity. + BetaSpeed = 0.0; + AlphaSpeed = 0.0; + xMovement = mouseX * FOV / 80.0f * TheCamera.m_fMouseAccelHorzntl; + targetAlpha = Alpha; + stepsLeftToChangeBetaByMouse = 1.0f * 50.0f; + mouseChangesBeta = true; + } else if (stepsLeftToChangeBetaByMouse > 0.0f) { + // Finish rotation by decreasing speed when we stopped moving mouse + BetaSpeed = 0.0; + AlphaSpeed = 0.0; + yMovement = 0.0; + xMovement = 0.0; + targetAlpha = Alpha; + stepsLeftToChangeBetaByMouse = Max(0.0f, stepsLeftToChangeBetaByMouse - CTimer::GetTimeStep()); + mouseChangesBeta = true; + } + } + + if (correctAlpha) { + if (nPreviousMode != MODE_CAM_ON_A_STRING) + alphaCorrected = false; + + if (!alphaCorrected && Abs(zoomModeAlphaOffset + Alpha) > 0.05f) { + yMovement = (-zoomModeAlphaOffset - Alpha) * 0.05f; + } else + alphaCorrected = true; + } + float alphaSpeedFromStickY = yMovement * CARCAM_SET[camSetArrPos][12]; + float betaSpeedFromStickX = xMovement * CARCAM_SET[camSetArrPos][12]; + + float newAngleSpeedMaxBlendAmount = CARCAM_SET[camSetArrPos][9]; + float angleChangeStep = Pow(CARCAM_SET[camSetArrPos][8], CTimer::GetTimeStep()); + float targetBetaWithStickBlendAmount = betaSpeedFromStickX + (targetBeta - Beta) / Max(CTimer::GetTimeStep(), 1.0f); + + if (targetBetaWithStickBlendAmount < -newAngleSpeedMaxBlendAmount) + targetBetaWithStickBlendAmount = -newAngleSpeedMaxBlendAmount; + else if (targetBetaWithStickBlendAmount > newAngleSpeedMaxBlendAmount) + targetBetaWithStickBlendAmount = newAngleSpeedMaxBlendAmount; + + float angleChangeStepLeft = 1.0f - angleChangeStep; + BetaSpeed = targetBetaWithStickBlendAmount * angleChangeStepLeft + angleChangeStep * BetaSpeed; + if (Abs(BetaSpeed) < 0.0001f) + BetaSpeed = 0.0f; + + float betaChangePerFrame; + if (mouseChangesBeta) + betaChangePerFrame = betaSpeedFromStickX; + else + betaChangePerFrame = CTimer::GetTimeStep() * BetaSpeed; + Beta = betaChangePerFrame + Beta; + + if (TheCamera.m_bJustCameOutOfGarage) { + float invHeading = Atan2(Front.y, Front.x); + if (invHeading < 0.0f) + invHeading += TWOPI; + + Beta = invHeading + PI; + } + + Beta = CGeneral::LimitRadianAngle(Beta); + if (Beta < 0.0f) + Beta += TWOPI; + + if ((camSetArrPos <= 1 || camSetArrPos == 7) && targetAlpha < Alpha && carPosChange >= newDistance) { + if (isCar && ((CAutomobile*)car)->m_nWheelsOnGround > 1 || + isBike && ((CBike*)car)->m_nWheelsOnGround > 1) + alphaSpeedFromStickY += (targetAlpha - Alpha) * 0.075f; + } + + AlphaSpeed = angleChangeStepLeft * alphaSpeedFromStickY + angleChangeStep * AlphaSpeed; + float maxAlphaSpeed = newAngleSpeedMaxBlendAmount; + if (alphaSpeedFromStickY > 0.0f) + maxAlphaSpeed = maxAlphaSpeed * 0.5; + + if (AlphaSpeed <= maxAlphaSpeed) { + float minAlphaSpeed = -maxAlphaSpeed; + if (AlphaSpeed < minAlphaSpeed) + AlphaSpeed = minAlphaSpeed; + } else { + AlphaSpeed = maxAlphaSpeed; + } + + if (Abs(AlphaSpeed) < 0.0001f) + AlphaSpeed = 0.0f; + + float alphaWithSpeedAccounted; + if (mouseChangesBeta) { + alphaWithSpeedAccounted = alphaSpeedFromStickY + targetAlpha; + Alpha += alphaSpeedFromStickY; + } else { + alphaWithSpeedAccounted = CTimer::GetTimeStep() * AlphaSpeed + targetAlpha; + Alpha += targetAlphaBlendAmount; + } + + if (Alpha <= maxAlphaAllowed) { + float minAlphaAllowed = -CARCAM_SET[camSetArrPos][14]; + if (minAlphaAllowed > Alpha) { + Alpha = minAlphaAllowed; + AlphaSpeed = 0.0f; + } + } else { + Alpha = maxAlphaAllowed; + AlphaSpeed = 0.0f; + } + + // Prevent unsignificant angle changes + if (Abs(lastAlpha - Alpha) < 0.0001f) + Alpha = lastAlpha; + + lastAlpha = Alpha; + + if (Abs(lastBeta - Beta) < 0.0001f) + Beta = lastBeta; + + lastBeta = Beta; + + Front.x = -(Cos(Beta) * Cos(Alpha)); + Front.y = -(Sin(Beta) * Cos(Alpha)); + Front.z = Sin(Alpha); + GetVectorsReadyForRW(); + TheCamera.m_bCamDirectlyBehind = false; + TheCamera.m_bCamDirectlyInFront = false; + + Source = TargetCoors - newDistance * Front; + + m_cvecTargetCoorsForFudgeInter = TargetCoors; + m_aTargetHistoryPosThree = m_aTargetHistoryPosOne; + float nextAlpha = alphaWithSpeedAccounted + zoomModeAlphaOffset; + float nextFrontX = -(Cos(Beta) * Cos(nextAlpha)); + float nextFrontY = -(Sin(Beta) * Cos(nextAlpha)); + float nextFrontZ = Sin(nextAlpha); + + m_aTargetHistoryPosOne.x = TargetCoors.x - nextFrontX * nextDistance; + m_aTargetHistoryPosOne.y = TargetCoors.y - nextFrontY * nextDistance; + m_aTargetHistoryPosOne.z = TargetCoors.z - nextFrontZ * nextDistance; + + m_aTargetHistoryPosTwo.x = TargetCoors.x - nextFrontX * newDistance; + m_aTargetHistoryPosTwo.y = TargetCoors.y - nextFrontY * newDistance; + m_aTargetHistoryPosTwo.z = TargetCoors.z - nextFrontZ * newDistance; + + // SA calls SetColVarsVehicle in here + if (nextDirectionIsForward) { + + // LCS uses exactly the same collision code as FollowPedWithMouse, so we will do so. + + // This is only in LCS! + float timestepFactor = Pow(0.99f, CTimer::GetTimeStep()); + dontCollideWithCars = (timestepFactor * dontCollideWithCars) + ((1.0f - timestepFactor) * car->m_vecMoveSpeed.Magnitude()); + + // Our addition +#define IS_TRAFFIC_LIGHT(ent) (ent->IsObject() && IsLightObject(ent->GetModelIndex())) + + // Clip Source and fix near clip + CColPoint colPoint; + CEntity* entity; + CWorld::pIgnoreEntity = CamTargetEntity; + if(CWorld::ProcessLineOfSight(TargetCoors, Source, colPoint, entity, true, dontCollideWithCars < 0.1f, false, true, false, true, true) && !IS_TRAFFIC_LIGHT(entity)){ + float PedColDist = (TargetCoors - colPoint.point).Magnitude(); + float ColCamDist = newDistance - PedColDist; + if(entity->IsPed() && ColCamDist > DEFAULT_NEAR + 0.1f){ + // Ped in the way but not clipping through + if(CWorld::ProcessLineOfSight(colPoint.point, Source, colPoint, entity, true, dontCollideWithCars < 0.1f, false, true, false, true, true) || IS_TRAFFIC_LIGHT(entity)){ + PedColDist = (TargetCoors - colPoint.point).Magnitude(); + Source = colPoint.point; + if(PedColDist < DEFAULT_NEAR + 0.3f) + RwCameraSetNearClipPlane(Scene.camera, Max(PedColDist-0.3f, 0.05f)); + }else{ + RwCameraSetNearClipPlane(Scene.camera, Min(ColCamDist-0.35f, DEFAULT_NEAR)); + } + }else{ + Source = colPoint.point; + if(PedColDist < DEFAULT_NEAR + 0.3f) + RwCameraSetNearClipPlane(Scene.camera, Max(PedColDist-0.3f, 0.05f)); + } + } + + CWorld::pIgnoreEntity = nil; + + // If we're seeing blue hell due to camera intersects some surface, fix it. + // SA and LCS have this unrolled. + + float ViewPlaneHeight = Tan(DEGTORAD(FOV) / 2.0f); + float ViewPlaneWidth = ViewPlaneHeight * CDraw::CalculateAspectRatio() * fTweakFOV; + float Near = RwCameraGetNearClipPlane(Scene.camera); + float radius = ViewPlaneWidth*Near; + entity = CWorld::TestSphereAgainstWorld(Source + Front*Near, radius, nil, true, true, false, true, false, true); + int i = 0; + while(entity){ + + if (IS_TRAFFIC_LIGHT(entity)) + break; + + CVector CamToCol = gaTempSphereColPoints[0].point - Source; + float frontDist = DotProduct(CamToCol, Front); + float dist = (CamToCol - Front*frontDist).Magnitude() / ViewPlaneWidth; + + // Try to decrease near clip + dist = Max(Min(Near, dist), 0.1f); + if(dist < Near) + RwCameraSetNearClipPlane(Scene.camera, dist); + + // Move forward a bit + if(dist == 0.1f) + Source += (TargetCoors - Source)*0.3f; + + // Keep testing + Near = RwCameraGetNearClipPlane(Scene.camera); + radius = ViewPlaneWidth*Near; + entity = CWorld::TestSphereAgainstWorld(Source + Front*Near, radius, nil, true, true, false, true, false, true); + + i++; + if(i > 5) + entity = nil; + } +#undef IS_TRAFFIC_LIGHT + } + TheCamera.m_bCamDirectlyBehind = false; + TheCamera.m_bCamDirectlyInFront = false; + + // ------- LCS specific part starts + + if (camSetArrPos == 5 && Source.z < 1.0f) // RC Bandit and Baron + Source.z = 1.0f; + + // CCam::FixSourceAboveWaterLevel + if (CameraTarget.z >= -2.0f) { + float level = -6000.0; + + if (CWaterLevel::GetWaterLevelNoWaves(Source.x, Source.y, Source.z, &level)) { + if (Source.z < level) + Source.z = level; + } + } + Front = TargetCoors - Source; + + // -------- LCS specific part ends + + GetVectorsReadyForRW(); + // SA + // gTargetCoordsForLookingBehind = TargetCoors; + + // SA code from CAutomobile::TankControl/FireTruckControl. + if (car->GetModelIndex() == MI_RHINO || car->GetModelIndex() == MI_FIRETRUCK) { + + float &carGunLR = ((CAutomobile*)car)->m_fCarGunLR; + CVector hi = Multiply3x3(Front, car->GetMatrix()); + + // III/VC's firetruck turret angle is reversed + float angleToFace = (car->GetModelIndex() == MI_FIRETRUCK ? -hi.Heading() : hi.Heading()); + + if (angleToFace <= carGunLR + PI) { + if (angleToFace < carGunLR - PI) + angleToFace = angleToFace + TWOPI; + } else { + angleToFace = angleToFace - TWOPI; + } + + float neededTurn = angleToFace - carGunLR; + float turnPerFrame = CTimer::GetTimeStep() * (car->GetModelIndex() == MI_FIRETRUCK ? 0.05f : 0.015f); + if (neededTurn <= turnPerFrame) { + if (neededTurn < -turnPerFrame) + angleToFace = carGunLR - turnPerFrame; + } else { + angleToFace = turnPerFrame + carGunLR; + } + + if (car->GetModelIndex() == MI_RHINO && carGunLR != angleToFace) { + DMAudio.PlayOneShot(car->m_audioEntityId, SOUND_CAR_TANK_TURRET_ROTATE, Abs(angleToFace - carGunLR)); + } + carGunLR = angleToFace; + + if (carGunLR < -PI) { + carGunLR += TWOPI; + } else if (carGunLR > PI) { + carGunLR -= TWOPI; + } + + // Because firetruk turret also has Y movement + if (car->GetModelIndex() == MI_FIRETRUCK) { + float &carGunUD = ((CAutomobile*)car)->m_fCarGunUD; + + float alphaToFace = Atan2(hi.z, hi.Magnitude2D()) + DEGTORAD(15.0f); + float neededAlphaTurn = alphaToFace - carGunUD; + float alphaTurnPerFrame = CTimer::GetTimeStepInSeconds(); + + if (neededAlphaTurn > alphaTurnPerFrame) { + neededTurn = alphaTurnPerFrame; + carGunUD = neededTurn + carGunUD; + } else { + if (neededAlphaTurn >= -alphaTurnPerFrame) { + carGunUD = alphaToFace; + } else { + carGunUD = carGunUD - alphaTurnPerFrame; + } + } + + float turretMinY = -DEGTORAD(20.0f); + float turretMaxY = DEGTORAD(20.0f); + if (turretMinY <= carGunUD) { + if (carGunUD > turretMaxY) + carGunUD = turretMaxY; + } else { + carGunUD = turretMinY; + } + } + } +} +#endif diff --git a/src/miami/core/Camera.cpp b/src/miami/core/Camera.cpp new file mode 100644 index 00000000..5ce205fe --- /dev/null +++ b/src/miami/core/Camera.cpp @@ -0,0 +1,4143 @@ +#include "common.h" + +#include "main.h" +#include "Draw.h" +#include "World.h" +#include "Vehicle.h" +#include "Train.h" +#include "Automobile.h" +#include "Ped.h" +#include "PlayerPed.h" +#include "Wanted.h" +#include "Pad.h" +#include "ControllerConfig.h" +#include "General.h" +#include "ZoneCull.h" +#include "SurfaceTable.h" +#include "Particle.h" +#include "WaterLevel.h" +#include "World.h" +#include "Garages.h" +#include "Replay.h" +#include "CutsceneMgr.h" +#include "Renderer.h" +#include "Timecycle.h" +#include "MBlur.h" +#include "Text.h" +#include "Hud.h" +#include "DMAudio.h" +#include "FileMgr.h" +#include "Frontend.h" +#include "SceneEdit.h" +#include "Pools.h" +#include "Debug.h" +#include "GenericGameStorage.h" +#include "Camera.h" + +enum +{ + // car + OBBE_WHEEL, + OBBE_1, + OBBE_2, + OBBE_3, + OBBE_1STPERSON, // unused + OBBE_5, + OBBE_ONSTRING, + OBBE_COPCAR, + OBBE_COPCAR_WHEEL, + // ped + OBBE_9, + OBBE_10, + OBBE_11, + OBBE_12, + OBBE_13, + // heli + OBBE_14, + OBBE_15, + OBBE_16, + OBBE_17, + OBBE_18, + OBBE_19, + OBBE_ONSTRING_HELI, + + OBBE_INVALID +}; + +// abbreviate a few things +#define PLAYER (CWorld::Players[CWorld::PlayerInFocus].m_pPed) +// NB: removed explicit TheCamera from all functions + +CCamera TheCamera; +#ifdef PC_PLAYER_CONTROLS +bool CCamera::m_bUseMouse3rdPerson = true; +#else +bool CCamera::m_bUseMouse3rdPerson = false; +#endif +bool bDidWeProcessAnyCinemaCam; +static bool bSwitchedToObbeCam; +float CCamera::m_fMouseAccelHorzntl; +float CCamera::m_fMouseAccelVertical; +float CCamera::m_f3rdPersonCHairMultX; +float CCamera::m_f3rdPersonCHairMultY; + +#ifdef IMPROVED_CAMERA +#define KEYJUSTDOWN(k) ControlsManager.GetIsKeyboardKeyJustDown((RsKeyCodes)k) +#define KEYDOWN(k) ControlsManager.GetIsKeyboardKeyDown((RsKeyCodes)k) +#define CTRLJUSTDOWN(key) \ + ((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYJUSTDOWN((RsKeyCodes)key) || \ + (KEYJUSTDOWN(rsLCTRL) || KEYJUSTDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key)) +#define CTRLDOWN(key) ((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key)) +#endif + +const float ZOOM_ONE_DISTANCE[] = { -0.6f, 0.05f, -3.2f, 0.05f, -2.41f }; +const float ZOOM_TWO_DISTANCE[] = { 1.9f, 1.4f, 0.65f, 1.9f, 6.49f }; +const float ZOOM_THREE_DISTANCE[] = { 15.9f, 15.9f, 15.9f, 15.9f, 25.25f }; + +#ifdef FREE_CAM +const float LCS_ZOOM_ONE_DISTANCE[] = { -1.0f, -0.2f, -3.2f, 0.05f, -2.41f }; +const float LCS_ZOOM_TWO_DISTANCE[] = { 2.0f, 2.2f, 1.65f, 2.9f, 6.49f }; +const float LCS_ZOOM_THREE_DISTANCE[] = { 6.0f, 6.0f, 15.9f, 15.9f, 15.0f }; +#endif + +CCamera::CCamera(void) +{ + Init(); +} + +void +CCamera::Init(void) +{ + memset(this, 0, sizeof(CCamera)); // this is fine, no vtable + m_pRwCamera = nil; + m_bPlayerWasOnBike = false; + m_1rstPersonRunCloseToAWall = false; + m_fPositionAlongSpline = 0.0f; + m_bCameraJustRestored = false; + Cams[0].Init(); + Cams[1].Init(); + Cams[2].Init(); + Cams[0].Mode = CCam::MODE_FOLLOWPED; + Cams[1].Mode = CCam::MODE_FOLLOWPED; + m_bEnable1rstPersonCamCntrlsScript = false; + m_bAllow1rstPersonWeaponsCamera = false; + m_bVehicleSuspenHigh = false; + Cams[0].m_fMinRealGroundDist = 1.85f; + // TODO: what weird value is this? + Cams[0].m_fTargetCloseInDist = 2.0837801f - Cams[0].m_fMinRealGroundDist; + Cams[0].m_fTargetZoomGroundOne = 0.25f; + Cams[0].m_fTargetZoomGroundTwo = 1.5f; + Cams[0].m_fTargetZoomGroundThree = 4.0f; + Cams[0].m_fTargetZoomOneZExtra = -0.14f; + Cams[0].m_fTargetZoomTwoZExtra = 0.16f; + Cams[0].m_fTargetZoomThreeZExtra = 0.25f; + // TODO: another weird value + Cams[0].m_fTargetZoomZCloseIn = 0.90040702f; + m_bMoveCamToAvoidGeom = false; + ClearPlayerWeaponMode(); + m_bInATunnelAndABigVehicle = false; + m_iModeObbeCamIsInForCar = OBBE_INVALID; + Cams[0].CamTargetEntity = nil; + Cams[1].CamTargetEntity = nil; + Cams[2].CamTargetEntity = nil; + Cams[0].m_fCamBufferedHeight = 0.0f; + Cams[0].m_fCamBufferedHeightSpeed = 0.0f; + Cams[1].m_fCamBufferedHeight = 0.0f; + Cams[1].m_fCamBufferedHeightSpeed = 0.0f; + Cams[0].m_bCamLookingAtVector = false; + Cams[1].m_bCamLookingAtVector = false; + Cams[2].m_bCamLookingAtVector = false; + Cams[0].m_fPlayerVelocity = 0.0f; + Cams[1].m_fPlayerVelocity = 0.0f; + Cams[2].m_fPlayerVelocity = 0.0f; + m_bHeadBob = false; + m_fFractionInterToStopMoving = 0.25f; + m_fFractionInterToStopCatchUp = 0.75f; + m_fGaitSwayBuffer = 0.85f; + m_bScriptParametersSetForInterPol = false; + m_uiCamShakeStart = 0; + m_fCamShakeForce = 0.0f; + m_iModeObbeCamIsInForCar = OBBE_INVALID; + m_bIgnoreFadingStuffForMusic = false; + m_bWaitForInterpolToFinish = false; + pToGarageWeAreIn = nil; + pToGarageWeAreInForHackAvoidFirstPerson = nil; + m_bPlayerIsInGarage = false; + m_bJustCameOutOfGarage = false; + m_fNearClipScript = DEFAULT_NEAR; + m_bUseNearClipScript = false; + m_vecDoingSpecialInterPolation = false; + m_bAboveGroundTrainNodesLoaded = false; + m_bBelowGroundTrainNodesLoaded = false; + m_WideScreenOn = false; + m_fFOV_Wide_Screen = 0.0f; + m_bRestoreByJumpCut = false; + CarZoomIndicator = CAM_ZOOM_2; + PedZoomIndicator = CAM_ZOOM_2; + CarZoomValueSmooth = 0.0f; + m_fPedZoomValueSmooth = 0.0f; + pTargetEntity = nil; + if(FindPlayerVehicle()) + pTargetEntity = FindPlayerVehicle(); + else + pTargetEntity = CWorld::Players[CWorld::PlayerInFocus].m_pPed; + m_bInitialNodeFound = false; + m_ScreenReductionPercentage = 0.0f; + m_ScreenReductionSpeed = 0.0f; + m_WideScreenOn = false; + m_bWantsToSwitchWidescreenOff = false; + WorldViewerBeingUsed = false; + PlayerExhaustion = 1.0f; + DebugCamMode = CCam::MODE_NONE; + m_PedOrientForBehindOrInFront = 0.0f; + if(!FrontEndMenuManager.m_bWantToRestart){ + m_bFading = false; + CDraw::FadeValue = 0; + m_fFLOATingFade = 0.0f; + m_bMusicFading = false; + m_fTimeToFadeMusic = 0.0f; + m_fFLOATingFadeMusic = 0.0f; + m_fMouseAccelVertical = 0.003f; + m_fMouseAccelHorzntl = 0.0025f; + } + if(FrontEndMenuManager.m_bWantToRestart) + m_fTimeToFadeMusic = 0.0f; + m_bStartingSpline = false; + m_iTypeOfSwitch = INTERPOLATION; + m_bUseScriptZoomValuePed = false; + m_bUseScriptZoomValueCar = false; + m_fPedZoomValueScript = 0.0f; + m_fCarZoomValueScript = 0.0f; + m_bUseSpecialFovTrain = false; + m_fFovForTrain = 70.0f; // or DefaultFOV from Cam.cpp + m_iModeToGoTo = CCam::MODE_FOLLOWPED; + m_bJust_Switched = false; + m_bUseTransitionBeta = false; + GetMatrix().SetScale(1.0f); + m_bTargetJustBeenOnTrain = false; + m_bInitialNoNodeStaticsSet = false; + m_uiLongestTimeInMill = 5000; + m_uiTimeLastChange = 0; + m_uiTimeWeEnteredIdle = 0; + m_bIdleOn = false; + m_uiTimeWeLeftIdle_StillNoInput = 0; + m_uiTimeWeEnteredIdle = 0; + LODDistMultiplier = 1.0f; + m_bCamDirectlyBehind = false; + m_bCamDirectlyInFront = false; + m_motionBlur = 0; + m_bGarageFixedCamPositionSet = false; + SetMotionBlur(255, 255, 255, 0, 0); + m_bCullZoneChecksOn = false; + m_bFailedCullZoneTestPreviously = false; + m_iCheckCullZoneThisNumFrames = 6; + m_iZoneCullFrameNumWereAt = 0; + m_CameraAverageSpeed = 0.0f; + m_CameraSpeedSoFar = 0.0f; + m_PreviousCameraPosition = CVector(0.0f, 0.0f, 0.0f); + m_iWorkOutSpeedThisNumFrames = 4; + m_iNumFramesSoFar = 0; + m_bJustInitalised = true; + m_uiTransitionState = 0; + m_uiTimeTransitionStart = 0; + m_bLookingAtPlayer = true; + m_f3rdPersonCHairMultX = 0.53f; + m_f3rdPersonCHairMultY = 0.4f; + m_fAvoidTheGeometryProbsTimer = 0.0f; + m_nAvoidTheGeometryProbsDirn = 0; +} + +void +CCamera::Process(void) +{ + // static bool InterpolatorNotInitialised = true; // unused + static float PlayerMinDist = 1.3f; + static bool WasPreviouslyInterSyhonFollowPed = false; // only written + float FOV = 0.0f; + float oldBeta, newBeta; + float deltaBeta = 0.0f; + bool lookLRBVehicle = false; + CVector CamFront, CamUp, CamRight, CamSource, Target; + + m_bJust_Switched = false; + m_RealPreviousCameraPosition = GetPosition(); + + // Update target entity + if(m_bLookingAtPlayer || m_bTargetJustBeenOnTrain || WhoIsInControlOfTheCamera == CAMCONTROL_OBBE) + UpdateTargetEntity(); + if(pTargetEntity == nil) + pTargetEntity = FindPlayerPed(); + if(Cams[ActiveCam].CamTargetEntity == nil) + Cams[ActiveCam].CamTargetEntity = pTargetEntity; + if(Cams[(ActiveCam+1)%2].CamTargetEntity == nil) + Cams[(ActiveCam+1)%2].CamTargetEntity = pTargetEntity; + + CamControl(); + if(m_bFading) + ProcessFade(); + if(m_bMusicFading) + ProcessMusicFade(); + if(m_WideScreenOn) + ProcessWideScreenOn(); + +#ifndef MASTER +#ifdef IMPROVED_CAMERA + if(CPad::GetPad(1)->GetCircleJustDown() || CTRLJUSTDOWN('B')){ +#else + if(CPad::GetPad(1)->GetCircleJustDown()){ +#endif + WorldViewerBeingUsed = !WorldViewerBeingUsed; + if(WorldViewerBeingUsed) + InitialiseCameraForDebugMode(); + else + CPad::m_bMapPadOneToPadTwo = false; + } +#endif + + RwCameraSetNearClipPlane(Scene.camera, DEFAULT_NEAR); + + if(Cams[ActiveCam].Front.x == 0.0f && Cams[ActiveCam].Front.y == 0.0f) + oldBeta = 0.0f; + else + oldBeta = CGeneral::GetATanOfXY(Cams[ActiveCam].Front.x, Cams[ActiveCam].Front.y); + + Cams[ActiveCam].Process(); + Cams[ActiveCam].ProcessSpecialHeightRoutines(); + + if(Cams[ActiveCam].Front.x == 0.0f && Cams[ActiveCam].Front.y == 0.0f) + newBeta = 0.0f; + else + newBeta = CGeneral::GetATanOfXY(Cams[ActiveCam].Front.x, Cams[ActiveCam].Front.y); + + + // Stop transition when it's done + if(m_uiTransitionState != 0){ + if(CTimer::GetTimeInMilliseconds() > m_uiTransitionDuration+m_uiTimeTransitionStart){ + m_uiTransitionState = 0; + m_vecDoingSpecialInterPolation = false; + m_bWaitForInterpolToFinish = false; + } + } + + if(m_bUseNearClipScript) + RwCameraSetNearClipPlane(Scene.camera, m_fNearClipScript); + + deltaBeta = newBeta - oldBeta; + while(deltaBeta >= PI) deltaBeta -= 2*PI; + while(deltaBeta < -PI) deltaBeta += 2*PI; + if(Abs(deltaBeta) > 0.3f) + m_bJust_Switched = true; + +#ifndef MASTER + // Debug stuff + if(!gbModelViewer) + Cams[ActiveCam].PrintMode(); // actually missing in VC + if(WorldViewerBeingUsed) + Cams[2].Process(); +#endif + + if(Cams[ActiveCam].DirectionWasLooking != LOOKING_FORWARD && pTargetEntity->IsVehicle()) + lookLRBVehicle = true; + + if(m_uiTransitionState != 0 && !lookLRBVehicle){ + // Process transition + + uint32 currentTime = CTimer::GetTimeInMilliseconds() - m_uiTimeTransitionStart; + if(currentTime >= m_uiTransitionDuration) + currentTime = m_uiTransitionDuration; + float fractionInter = (float) currentTime / m_uiTransitionDuration; + float fractionInterTarget = (float) currentTime / m_uiTransitionDurationTargetCoors; + fractionInterTarget = Clamp(fractionInterTarget, 0.0f, 1.0f); + + // Interpolate target separately + if(fractionInterTarget <= m_fFractionInterToStopMovingTarget){ + float inter; + if(m_fFractionInterToStopMovingTarget == 0.0f) + inter = 0.0f; + else + inter = (m_fFractionInterToStopMovingTarget - fractionInterTarget)/m_fFractionInterToStopMovingTarget; + inter = 0.5f - 0.5*Cos(inter*PI); // smooth it + + m_vecTargetWhenInterPol = m_cvecStartingTargetForInterPol + inter*m_cvecTargetSpeedAtStartInter; + Target = m_vecTargetWhenInterPol; + }else if(fractionInterTarget > m_fFractionInterToStopMovingTarget){ + float inter; + if(m_fFractionInterToStopCatchUpTarget == 0.0f) + inter = 0.0f; + else + inter = (fractionInterTarget - m_fFractionInterToStopMovingTarget)/m_fFractionInterToStopCatchUpTarget; + inter = 0.5f - 0.5*Cos(inter*PI); // smooth it + + if(m_fFractionInterToStopMovingTarget == 0.0f) + m_vecTargetWhenInterPol = m_cvecStartingTargetForInterPol; + Target = m_vecTargetWhenInterPol + inter*(Cams[ActiveCam].m_cvecTargetCoorsForFudgeInter - m_vecTargetWhenInterPol); + } + + if(fractionInter <= m_fFractionInterToStopMoving){ + float inter; + if(m_fFractionInterToStopMoving == 0.0f) + inter = 0.0f; + else + inter = (m_fFractionInterToStopMoving - fractionInter)/m_fFractionInterToStopMoving; + inter = 0.5f - 0.5*Cos(inter*PI); // smooth it + + m_vecSourceWhenInterPol = m_cvecStartingSourceForInterPol + inter*m_cvecSourceSpeedAtStartInter; + + if(m_bLookingAtPlayer){ + CVector ToCam = m_vecSourceWhenInterPol - Target; + if(ToCam.Magnitude2D() < PlayerMinDist){ + float beta = CGeneral::GetATanOfXY(ToCam.x, ToCam.y); + CamSource.x = Target.x + PlayerMinDist*Cos(beta); + CamSource.y = Target.y + PlayerMinDist*Sin(beta); + } + } + + m_vecUpWhenInterPol = m_cvecStartingUpForInterPol + inter*m_cvecUpSpeedAtStartInter; + m_fFOVWhenInterPol = m_fStartingFOVForInterPol + inter*m_fFOVSpeedAtStartInter; + + CamSource = m_vecSourceWhenInterPol; + CamFront = Target - CamSource; + StoreValuesDuringInterPol(CamSource, m_vecTargetWhenInterPol, m_vecUpWhenInterPol, m_fFOVWhenInterPol); + CamFront.Normalise(); + if(m_bLookingAtPlayer) + CamUp = CVector(0.0f, 0.0f, 1.0f); + else + CamUp = m_vecUpWhenInterPol; + CamUp.Normalise(); + + if(Cams[ActiveCam].Mode == CCam::MODE_TOPDOWN || Cams[ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED){ + CamFront.Normalise(); + CamRight = CVector(-1.0f, 0.0f, 0.0f); + CamUp = CrossProduct(CamFront, CamRight); + CamUp.Normalise(); + }else{ + CamFront.Normalise(); + CamUp.Normalise(); + CamRight = CrossProduct(CamFront, CamUp); + CamRight.Normalise(); + CamUp = CrossProduct(CamRight, CamFront); + CamUp.Normalise(); + } + FOV = m_fFOVWhenInterPol; + }else if(fractionInter > m_fFractionInterToStopMoving && fractionInter <= 1.0f){ + float inter; + if(m_fFractionInterToStopCatchUp == 0.0f) + inter = 0.0f; + else + inter = (fractionInter - m_fFractionInterToStopMoving)/m_fFractionInterToStopCatchUp; + inter = 0.5f - 0.5*Cos(inter*PI); // smooth it + + CamSource = m_vecSourceWhenInterPol + inter*(Cams[ActiveCam].Source - m_vecSourceWhenInterPol); + + if(m_bLookingAtPlayer){ + CVector ToCam = m_vecSourceWhenInterPol - Target; + if(ToCam.Magnitude2D() < PlayerMinDist){ + float beta = CGeneral::GetATanOfXY(ToCam.x, ToCam.y); + CamSource.x = Target.x + PlayerMinDist*Cos(beta); + CamSource.y = Target.y + PlayerMinDist*Sin(beta); + } + } + + FOV = m_fFOVWhenInterPol + inter*(Cams[ActiveCam].FOV - m_fFOVWhenInterPol); + CamUp = m_vecUpWhenInterPol + inter*(Cams[ActiveCam].Up - m_vecUpWhenInterPol); + deltaBeta = Cams[ActiveCam].m_fTrueBeta - m_fBetaWhenInterPol; + MakeAngleLessThan180(deltaBeta); + + CamFront = Target - CamSource; + StoreValuesDuringInterPol(CamSource, Target, CamUp, FOV); + CamFront.Normalise(); + if(m_bLookingAtPlayer) + CamUp = CVector(0.0f, 0.0f, 1.0f); + + if(Cams[ActiveCam].Mode == CCam::MODE_TOPDOWN || Cams[ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED){ + CamFront.Normalise(); + CamRight = CVector(-1.0f, 0.0f, 0.0f); + CamUp = CrossProduct(CamFront, CamRight); + CamUp.Normalise(); + }else{ + CamFront.Normalise(); + CamUp.Normalise(); + CamRight = CrossProduct(CamFront, CamUp); + CamRight.Normalise(); + CamUp = CrossProduct(CamRight, CamFront); + CamUp.Normalise(); + } +#ifndef FIX_BUGS + // BUG: FOV was already interpolated but m_fFOVWhenInterPol was not + FOV = m_fFOVWhenInterPol; +#endif + } + + CVector Dist = CamSource - Target; + float DistOnGround = Dist.Magnitude2D(); + float Alpha = CGeneral::GetATanOfXY(DistOnGround, Dist.z); + float Beta = CGeneral::GetATanOfXY(Dist.x, Dist.y); + Cams[ActiveCam].KeepTrackOfTheSpeed(CamSource, Target, CamUp, Alpha, Beta, FOV); + }else{ + // No transition, take Cam values directly +#ifndef MASTER + if(WorldViewerBeingUsed){ + CamSource = Cams[2].Source; + CamFront = Cams[2].Front; + CamUp = Cams[2].Up; + FOV = Cams[2].FOV; + }else +#endif + { + CamSource = Cams[ActiveCam].Source; + CamUp = Cams[ActiveCam].Up; + if(m_bMoveCamToAvoidGeom){ + CamSource += m_vecClearGeometryVec; + CamFront = Cams[ActiveCam].m_cvecTargetCoorsForFudgeInter - CamSource; + CamFront.Normalise(); + CVector Right = CrossProduct(CamFront, CamUp); + Right.Normalise(); + CamUp = CrossProduct(Right, CamFront); + CamUp.Normalise(); + }else{ + CamFront = Cams[ActiveCam].Front; + CamUp = Cams[ActiveCam].Up; + } + FOV = Cams[ActiveCam].FOV; + } + WasPreviouslyInterSyhonFollowPed = false; // unused + } + + if(m_uiTransitionState != 0) + if(!m_bLookingAtVector && m_bLookingAtPlayer && !CCullZones::CamStairsForPlayer() && !m_bPlayerIsInGarage){ + CEntity *entity = nil; + CColPoint colPoint; + if(CWorld::ProcessLineOfSight(pTargetEntity->GetPosition(), CamSource, colPoint, entity, true, false, false, true, false, true, true)){ + CamSource = colPoint.point; + RwCameraSetNearClipPlane(Scene.camera, 0.05f); + } + } + + if(CMBlur::Drunkness > 0.0f){ + static float DrunkAngle; + + int tableIndex = (int)(DEGTORAD(DrunkAngle)/TWOPI * CParticle::SIN_COS_TABLE_SIZE) & CParticle::SIN_COS_TABLE_SIZE-1; + DrunkAngle += 5.0f; +#ifndef FIX_BUGS + // This just messes up interpolation, probably not what they intended + // and multiplying the interpolated FOV is also a bit extreme + // so let's not do any of this nonsense + Cams[ActiveCam].FOV *= (1.0f + CMBlur::Drunkness); +#endif + + CamSource.x += -0.02f*CMBlur::Drunkness * CParticle::m_CosTable[tableIndex]; + CamSource.y += -0.02f*CMBlur::Drunkness * CParticle::m_SinTable[tableIndex]; + + CamUp.Normalise(); + CamUp.x += 0.05f*CMBlur::Drunkness * CParticle::m_CosTable[tableIndex]; + CamUp.y += 0.05f*CMBlur::Drunkness * CParticle::m_SinTable[tableIndex]; + CamUp.Normalise(); + + CamFront.Normalise(); + CamFront.x += -0.1f*CMBlur::Drunkness * CParticle::m_CosTable[tableIndex]; + CamFront.y += -0.1f*CMBlur::Drunkness * CParticle::m_SinTable[tableIndex]; + CamFront.Normalise(); + + CamRight = CrossProduct(CamFront, CamUp); + CamRight.Normalise(); + CamUp = CrossProduct(CamRight, CamFront); + CamUp.Normalise(); + } + + GetMatrix().GetRight() = CrossProduct(CamUp, CamFront); // actually Left + GetMatrix().GetForward() = CamFront; + GetMatrix().GetUp() = CamUp; + GetMatrix().GetPosition() = CamSource; + + // Process Shake + float shakeStrength = m_fCamShakeForce - 0.28f*(CTimer::GetTimeInMilliseconds()-m_uiCamShakeStart)/1000.0f; + shakeStrength = Clamp(shakeStrength, 0.0f, 2.0f); + int shakeRand = CGeneral::GetRandomNumber(); + float shakeOffset = shakeStrength*0.1f; + GetMatrix().GetPosition().x += shakeOffset * ((shakeRand & 0xF) - 7); + GetMatrix().GetPosition().y += shakeOffset * (((shakeRand & 0xF0) >> 4) - 7); + GetMatrix().GetPosition().z += shakeOffset * (((shakeRand & 0xF00) >> 8) - 7); + + if(shakeOffset > 0.0f && m_BlurType != MOTION_BLUR_SNIPER) + SetMotionBlurAlpha(Min((int)(shakeStrength*255.0f) + 25, 150)); + + static bool bExtra1stPrsBlur = false; + if(Cams[ActiveCam].Mode == CCam::MODE_1STPERSON && FindPlayerVehicle() && FindPlayerVehicle()->GetUp().z < 0.2f){ + SetMotionBlur(230, 230, 230, 215, MOTION_BLUR_LIGHT_SCENE); + bExtra1stPrsBlur = true; + }else if(bExtra1stPrsBlur){ + SetMotionBlur(CTimeCycle::GetBlurRed(), CTimeCycle::GetBlurGreen(), CTimeCycle::GetBlurBlue(), m_motionBlur, MOTION_BLUR_LIGHT_SCENE); + bExtra1stPrsBlur = false; + } + + CalculateDerivedValues(); + CDraw::SetFOV(FOV); + + // Set RW camera +#ifndef MASTER + if(WorldViewerBeingUsed){ + RwFrame *frame = RwCameraGetFrame(m_pRwCamera); + CVector Source = Cams[2].Source; + CVector Front = Cams[2].Front; + CVector Up = Cams[2].Up; + + GetMatrix().GetRight() = CrossProduct(Up, Front); + GetMatrix().GetForward() = Front; + GetMatrix().GetUp() = Up; + GetMatrix().GetPosition() = Source; + + CDraw::SetFOV(Cams[2].FOV); + m_vecGameCamPos = Cams[ActiveCam].Source; + + *RwMatrixGetPos(RwFrameGetMatrix(frame)) = GetPosition(); + *RwMatrixGetAt(RwFrameGetMatrix(frame)) = GetForward(); + *RwMatrixGetUp(RwFrameGetMatrix(frame)) = GetUp(); + *RwMatrixGetRight(RwFrameGetMatrix(frame)) = GetRight(); + RwMatrixUpdate(RwFrameGetMatrix(frame)); + RwFrameUpdateObjects(frame); + }else +#endif + { + RwFrame *frame = RwCameraGetFrame(m_pRwCamera); + m_vecGameCamPos = GetPosition(); + *RwMatrixGetPos(RwFrameGetMatrix(frame)) = GetPosition(); + *RwMatrixGetAt(RwFrameGetMatrix(frame)) = GetForward(); + *RwMatrixGetUp(RwFrameGetMatrix(frame)) = GetUp(); + *RwMatrixGetRight(RwFrameGetMatrix(frame)) = GetRight(); + RwMatrixUpdate(RwFrameGetMatrix(frame)); + RwFrameUpdateObjects(frame); + RwFrameOrthoNormalize(frame); + } + + UpdateSoundDistances(); + + if((CTimer::GetFrameCounter()&0xF) == 3) + DistanceToWater = CWaterLevel::CalcDistanceToWater(GetPosition().x, GetPosition().y); + + // LOD dist + if(!CCutsceneMgr::IsRunning() || CCutsceneMgr::UseLodMultiplier()){ + LODDistMultiplier = 70.0f/CDraw::GetFOV(); + + if(GetPosition().z > 55.0f && FindPlayerVehicle() && FindPlayerVehicle()->pHandling->Flags & (HANDLING_IS_HELI|HANDLING_IS_PLANE) || + FindPlayerPed()->m_attachedTo){ + LODDistMultiplier *= 1.0f + Max((GetPosition().z - 55.0f)/60.0f, 0.0f); + float NewNear = DEFAULT_NEAR * (1.0f + Max((GetPosition().z - 55.0f)/60.0f, 0.0f)); + if(RwCameraGetNearClipPlane(Scene.camera) >= DEFAULT_NEAR) + RwCameraSetNearClipPlane(Scene.camera, NewNear); + } + if(LODDistMultiplier > 2.2f) LODDistMultiplier = 2.2f; + }else + LODDistMultiplier = 1.0f; + GenerationDistMultiplier = LODDistMultiplier; + LODDistMultiplier *= CRenderer::ms_lodDistScale; + + CDraw::SetNearClipZ(RwCameraGetNearClipPlane(m_pRwCamera)); + CDraw::SetFarClipZ(RwCameraGetFarClipPlane(m_pRwCamera)); + + // Keep track of speed + if(m_bJustInitalised || m_bJust_Switched){ + m_PreviousCameraPosition = GetPosition(); + m_bJustInitalised = false; + } + m_CameraSpeedSoFar += (GetPosition() - m_PreviousCameraPosition).Magnitude(); + m_iNumFramesSoFar++; + if(m_iNumFramesSoFar == m_iWorkOutSpeedThisNumFrames){ + m_CameraAverageSpeed = m_CameraSpeedSoFar / m_iWorkOutSpeedThisNumFrames; + m_CameraSpeedSoFar = 0.0f; + m_iNumFramesSoFar = 0; + } + m_PreviousCameraPosition = GetPosition(); + + if(Cams[ActiveCam].DirectionWasLooking != LOOKING_FORWARD && Cams[ActiveCam].Mode != CCam::MODE_TOP_DOWN_PED){ + Cams[ActiveCam].Source = Cams[ActiveCam].SourceBeforeLookBehind; + Orientation += PI; + } + + if(m_uiTransitionState != 0){ + int OtherCam = (ActiveCam+1)%2; + if(Cams[OtherCam].CamTargetEntity && + pTargetEntity && pTargetEntity->IsPed() && + !Cams[OtherCam].CamTargetEntity->IsVehicle() && + Cams[ActiveCam].Mode != CCam::MODE_TOP_DOWN_PED && Cams[ActiveCam].DirectionWasLooking != LOOKING_FORWARD){ + Cams[OtherCam].Source = Cams[ActiveCam%2].SourceBeforeLookBehind; + Orientation += PI; + } + } + + m_bCameraJustRestored = false; + m_bMoveCamToAvoidGeom = false; +} + +void +CCamera::CamControl(void) +{ + static bool PlaceForFixedWhenSniperFound = false; + static int16 ReqMode; + bool switchByJumpCut = false; + bool stairs = false; + bool boatTarget = false; + int PrevMode = Cams[ActiveCam].Mode; + CVector targetPos; + CVector garageCenter, garageDoorPos1, garageDoorPos2; + CVector garageCenterToDoor, garageCamPos; + int whichDoor; + + m_bObbeCinematicPedCamOn = false; + m_bObbeCinematicCarCamOn = false; + m_bUseTransitionBeta = false; + m_bUseSpecialFovTrain = false; + m_bJustCameOutOfGarage = false; + m_bTargetJustCameOffTrain = false; + m_bInATunnelAndABigVehicle = false; + m_bJustJumpedOutOf1stPersonBecauseOfTarget = false; + bSwitchedToObbeCam = false; + + if(Cams[ActiveCam].CamTargetEntity == nil && pTargetEntity == nil) + pTargetEntity = PLAYER; + + m_iZoneCullFrameNumWereAt++; + if(m_iZoneCullFrameNumWereAt > m_iCheckCullZoneThisNumFrames) + m_iZoneCullFrameNumWereAt = 1; + m_bCullZoneChecksOn = m_iZoneCullFrameNumWereAt == m_iCheckCullZoneThisNumFrames; + if(m_bCullZoneChecksOn) + m_bFailedCullZoneTestPreviously = CCullZones::CamCloseInForPlayer(); + + if(m_bLookingAtPlayer){ + CPad::GetPad(0)->DisablePlayerControls &= ~PLAYERCONTROL_CAMERA; + FindPlayerPed()->bIsVisible = true; + } + + if(!CTimer::GetIsPaused() && !m_bIdleOn){ + float CloseInCarHeightTarget = 0.0f; + float CloseInPedHeightTarget = 0.0f; + + if(m_bTargetJustBeenOnTrain){ + // Getting off train + if(!pTargetEntity->IsVehicle() || !((CVehicle*)pTargetEntity)->IsTrain()){ + Restore(); + m_bTargetJustCameOffTrain = true; + m_bTargetJustBeenOnTrain = false; + SetWideScreenOff(); + } + } + + // Vehicle target + if(pTargetEntity->IsVehicle()){ +#ifdef GTA_TRAIN + if(((CVehicle*)pTargetEntity)->IsTrain()){ + if(!m_bTargetJustBeenOnTrain){ + m_bInitialNodeFound = false; + m_bInitialNoNodeStaticsSet = false; + } + Process_Train_Camera_Control(); + }else +#endif + { + if(((CVehicle*)pTargetEntity)->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) + boatTarget = true; + + // Change user selected mode + if(CPad::GetPad(0)->CycleCameraModeUpJustDown() && !CReplay::IsPlayingBack() && + (m_bLookingAtPlayer || WhoIsInControlOfTheCamera == CAMCONTROL_OBBE) && + !m_WideScreenOn){ + CarZoomIndicator--; + // disable topdown here + if(CarZoomIndicator == CAM_ZOOM_TOPDOWN) + CarZoomIndicator--; + } + if(CPad::GetPad(0)->CycleCameraModeDownJustDown() && !CReplay::IsPlayingBack() && + (m_bLookingAtPlayer || WhoIsInControlOfTheCamera == CAMCONTROL_OBBE) && + !m_WideScreenOn){ + CarZoomIndicator++; + if(CarZoomIndicator == CAM_ZOOM_TOPDOWN) + CarZoomIndicator++; + } + if(!m_bFailedCullZoneTestPreviously){ + if(CarZoomIndicator < CAM_ZOOM_1STPRS) CarZoomIndicator = CAM_ZOOM_CINEMATIC; + else if(CarZoomIndicator > CAM_ZOOM_CINEMATIC) CarZoomIndicator = CAM_ZOOM_1STPRS; + } + + if(m_bFailedCullZoneTestPreviously) + if(CarZoomIndicator != CAM_ZOOM_1STPRS && CarZoomIndicator != CAM_ZOOM_TOPDOWN) + ReqMode = CCam::MODE_CAM_ON_A_STRING; + + int vehType = ((CVehicle*)pTargetEntity)->m_vehType; + if(((CVehicle*)pTargetEntity)->IsBoat() && pTargetEntity->GetModelIndex() == MI_SKIMMER) + vehType = VEHICLE_TYPE_CAR; + + switch(vehType){ + case VEHICLE_TYPE_CAR: + case VEHICLE_TYPE_BIKE:{ + CAttributeZone *stairsZone = nil; + if(vehType == VEHICLE_TYPE_BIKE && CCullZones::CamStairsForPlayer()){ + stairsZone = CCullZones::FindZoneWithStairsAttributeForPlayer(); + if(stairsZone) + stairs = true; + } + if(CGarages::IsPointInAGarageCameraZone(pTargetEntity->GetPosition()) || stairs){ + if(!m_bGarageFixedCamPositionSet && m_bLookingAtPlayer || + WhoIsInControlOfTheCamera == CAMCONTROL_OBBE){ + if(pToGarageWeAreIn || stairsZone){ + float ground; + bool foundGround; + + if(pToGarageWeAreIn){ + // This is all very strange.... + // targetPos = pTargetEntity->GetPosition(); // unused + if(pToGarageWeAreIn->m_pDoor1){ + whichDoor = 1; + garageDoorPos1.x = pToGarageWeAreIn->m_fDoor1X; + garageDoorPos1.y = pToGarageWeAreIn->m_fDoor1Y; + garageDoorPos1.z = 0.0f; + // targetPos.z = 0.0f; // unused + // (targetPos - doorPos1).Magnitude(); // unused + }else if(pToGarageWeAreIn->m_pDoor2){ + whichDoor = 2; +#ifdef FIX_BUGS + garageDoorPos2.x = pToGarageWeAreIn->m_fDoor2X; + garageDoorPos2.y = pToGarageWeAreIn->m_fDoor2Y; + garageDoorPos2.z = 0.0f; +#endif + }else{ + whichDoor = 1; + garageDoorPos1.x = pTargetEntity->GetPosition().x; + garageDoorPos1.y = pTargetEntity->GetPosition().y; +#ifdef FIX_BUGS + garageDoorPos1.z = 0.0f; +#else + garageDoorPos2.z = 0.0f; +#endif + } + }else{ + assert(stairsZone); + whichDoor = 1; + garageDoorPos1 = Cams[ActiveCam].Source; + garageCenter = CVector((stairsZone->minx+stairsZone->maxx)/2.0f, (stairsZone->miny+stairsZone->maxy)/2.0f, 0.0f); + if((garageCenter-garageDoorPos1).Magnitude() > 15.0f){ + bool bClearViewOutside = true; + CVector dirOutside = pTargetEntity->GetPosition() - garageCenter; + dirOutside.z = 0.0f; + dirOutside.Normalise(); + float zoneDim = stairsZone->maxx - stairsZone->minx; + if(zoneDim < stairsZone->maxy - stairsZone->miny) + zoneDim = stairsZone->maxy - stairsZone->miny; + zoneDim *= 2.0f; + CVector posOutside = pTargetEntity->GetPosition() + zoneDim*dirOutside; + if(!CWorld::GetIsLineOfSightClear(pTargetEntity->GetPosition(), posOutside, true, false, false, false, false, false, true)){ + posOutside = pTargetEntity->GetPosition() - zoneDim*dirOutside; + if(!CWorld::GetIsLineOfSightClear(pTargetEntity->GetPosition(), posOutside, true, false, false, false, false, false, true)) + bClearViewOutside = false; + } + if(bClearViewOutside) + garageDoorPos1 = posOutside; + } + } + + if(pToGarageWeAreIn){ + garageCenter.x = pToGarageWeAreIn->GetGarageCenterX(); + garageCenter.y = pToGarageWeAreIn->GetGarageCenterY(); + garageCenter.z = 0.0f; + }else{ + garageDoorPos1.z = 0.0f; + if(stairsZone == nil) // how can this be true? + garageCenter = CVector(pTargetEntity->GetPosition().x, pTargetEntity->GetPosition().y, 0.0f); + } + + if(whichDoor == 1) + garageCenterToDoor = garageDoorPos1 - garageCenter; + else + garageCenterToDoor = garageDoorPos2 - garageCenter; + targetPos = pTargetEntity->GetPosition(); + ground = CWorld::FindGroundZFor3DCoord(targetPos.x, targetPos.y, targetPos.z, &foundGround); + if(!foundGround) + ground = targetPos.z - 0.2f; + garageCenterToDoor.z = 0.0f; + garageCenterToDoor.Normalise(); + if(whichDoor == 1){ + if(pToGarageWeAreIn == nil && stairsZone){ + float zoneDim = stairsZone->maxx - stairsZone->minx; + if(zoneDim < stairsZone->maxy - stairsZone->miny) + zoneDim = stairsZone->maxy - stairsZone->miny; + garageCamPos = garageCenter + (0.7f*zoneDim + 3.75f)*garageCenterToDoor; + }else + garageCamPos = garageDoorPos1 + 13.0f*garageCenterToDoor; + }else + garageCamPos = garageDoorPos2 + 13.0f*garageCenterToDoor; + garageCamPos.z = ground + 3.1f; + SetCamPositionForFixedMode(garageCamPos, CVector(0.0f, 0.0f, 0.0f)); + m_bGarageFixedCamPositionSet = true; + } + } + + if(CGarages::CameraShouldBeOutside() && m_bGarageFixedCamPositionSet && + (m_bLookingAtPlayer || WhoIsInControlOfTheCamera == CAMCONTROL_OBBE)){ + if(pToGarageWeAreIn){ + ReqMode = CCam::MODE_FIXED; + m_bPlayerIsInGarage = true; + } + }else{ + if(m_bPlayerIsInGarage){ + m_bJustCameOutOfGarage = true; + m_bPlayerIsInGarage = false; + } + ReqMode = CCam::MODE_CAM_ON_A_STRING; + } + }else{ + if(m_bPlayerIsInGarage){ + m_bJustCameOutOfGarage = true; + m_bPlayerIsInGarage = false; + } + m_bGarageFixedCamPositionSet = false; + ReqMode = CCam::MODE_CAM_ON_A_STRING; + } + break; + } + case VEHICLE_TYPE_BOAT: + ReqMode = CCam::MODE_BEHINDBOAT; + break; + default: break; + } + + int vehApp = ((CVehicle*)pTargetEntity)->GetVehicleAppearance(); + int vehArrPos = 0; + GetArrPosForVehicleType(vehApp, vehArrPos); + + // Car zoom value + if (CarZoomIndicator == CAM_ZOOM_1STPRS && !m_bPlayerIsInGarage) { + CarZoomValue = 0.0f; + ReqMode = CCam::MODE_1STPERSON; + } +#ifdef FREE_CAM + else if (bFreeCam) { + if (CarZoomIndicator == CAM_ZOOM_1) + CarZoomValue = LCS_ZOOM_ONE_DISTANCE[vehArrPos]; + else if (CarZoomIndicator == CAM_ZOOM_2) + CarZoomValue = LCS_ZOOM_TWO_DISTANCE[vehArrPos]; + else if (CarZoomIndicator == CAM_ZOOM_3) + CarZoomValue = LCS_ZOOM_THREE_DISTANCE[vehArrPos]; + } +#endif + else if (CarZoomIndicator == CAM_ZOOM_1) + CarZoomValue = ZOOM_ONE_DISTANCE[vehArrPos]; + else if(CarZoomIndicator == CAM_ZOOM_2) + CarZoomValue = ZOOM_TWO_DISTANCE[vehArrPos]; + else if(CarZoomIndicator == CAM_ZOOM_3) + CarZoomValue = ZOOM_THREE_DISTANCE[vehArrPos]; + + if(CarZoomIndicator == CAM_ZOOM_TOPDOWN && !m_bPlayerIsInGarage){ + CarZoomValue = 1.0f; + ReqMode = CCam::MODE_TOPDOWN; + } + + // Check if we have to go into first person + if(vehType == VEHICLE_TYPE_CAR && !m_bPlayerIsInGarage){ + if(CCullZones::Cam1stPersonForPlayer() && + pTargetEntity->GetColModel()->boundingBox.GetSize().z >= 3.026f && + pToGarageWeAreInForHackAvoidFirstPerson == nil){ + ReqMode = CCam::MODE_1STPERSON; + m_bInATunnelAndABigVehicle = true; + } + } + if(ReqMode == CCam::MODE_TOPDOWN && + (CCullZones::Cam1stPersonForPlayer() || CCullZones::CamNoRain() || CCullZones::PlayerNoRain())) + ReqMode = CCam::MODE_1STPERSON; + + // Smooth zoom value - ugly code + if(m_bUseScriptZoomValueCar){ + if(CarZoomValueSmooth < m_fCarZoomValueScript){ + CarZoomValueSmooth += 0.12f * CTimer::GetTimeStep(); + CarZoomValueSmooth = Min(CarZoomValueSmooth, m_fCarZoomValueScript); + }else{ + CarZoomValueSmooth -= 0.12f * CTimer::GetTimeStep(); + CarZoomValueSmooth = Max(CarZoomValueSmooth, m_fCarZoomValueScript); + } + }else if(m_bFailedCullZoneTestPreviously){ + CloseInCarHeightTarget = 0.65f; + if(CarZoomValueSmooth < -0.65f){ + CarZoomValueSmooth += 0.12f * CTimer::GetTimeStep(); + CarZoomValueSmooth = Min(CarZoomValueSmooth, -0.65f); + }else{ + CarZoomValueSmooth -= 0.12f * CTimer::GetTimeStep(); + CarZoomValueSmooth = Max(CarZoomValueSmooth, -0.65f); + } + }else{ + if(CarZoomValueSmooth < CarZoomValue){ + CarZoomValueSmooth += 0.12f * CTimer::GetTimeStep(); + CarZoomValueSmooth = Min(CarZoomValueSmooth, CarZoomValue); + }else{ + CarZoomValueSmooth -= 0.12f * CTimer::GetTimeStep(); + CarZoomValueSmooth = Max(CarZoomValueSmooth, CarZoomValue); + } + } + + WellBufferMe(CloseInCarHeightTarget, &Cams[ActiveCam].m_fCloseInCarHeightOffset, &Cams[ActiveCam].m_fCloseInCarHeightOffsetSpeed, 0.1f, 0.25f, false); + + // Fallen into water + if(Cams[ActiveCam].IsTargetInWater(Cams[ActiveCam].Source) && !boatTarget && + !Cams[ActiveCam].CamTargetEntity->IsPed() && + pTargetEntity->GetModelIndex() != MI_SKIMMER && pTargetEntity->GetModelIndex() != MI_SEASPAR) + ReqMode = CCam::MODE_PLAYER_FALLEN_WATER; + } + } + + // Ped target + else if(pTargetEntity->IsPed()){ + // Change user selected mode + if(CPad::GetPad(0)->CycleCameraModeUpJustDown() && !CReplay::IsPlayingBack() && + (m_bLookingAtPlayer || WhoIsInControlOfTheCamera == CAMCONTROL_OBBE) && + !m_WideScreenOn && !m_bFailedCullZoneTestPreviously && !m_bFirstPersonBeingUsed){ + if(FrontEndMenuManager.m_ControlMethod == CONTROL_STANDARD){ + if(PedZoomIndicator == CAM_ZOOM_3) + PedZoomIndicator = CAM_ZOOM_1; + else + PedZoomIndicator = CAM_ZOOM_3; + }else + PedZoomIndicator--; + } + if(CPad::GetPad(0)->CycleCameraModeDownJustDown() && !CReplay::IsPlayingBack() && + (m_bLookingAtPlayer || WhoIsInControlOfTheCamera == CAMCONTROL_OBBE) && + !m_WideScreenOn && !m_bFailedCullZoneTestPreviously && !m_bFirstPersonBeingUsed){ + if(FrontEndMenuManager.m_ControlMethod == CONTROL_STANDARD){ + if(PedZoomIndicator == CAM_ZOOM_3) + PedZoomIndicator = CAM_ZOOM_1; + else + PedZoomIndicator = CAM_ZOOM_3; + }else + PedZoomIndicator++; + } + // disabled top down and obbe's cam here + if(PedZoomIndicator < CAM_ZOOM_1) PedZoomIndicator = CAM_ZOOM_3; + else if(PedZoomIndicator > CAM_ZOOM_3) PedZoomIndicator = CAM_ZOOM_1; + + ReqMode = CCam::MODE_FOLLOWPED; + + // Check 1st person mode + if((m_bLookingAtPlayer || m_bEnable1rstPersonCamCntrlsScript) && pTargetEntity->IsPed() && + (!m_WideScreenOn || m_bEnable1rstPersonCamCntrlsScript) && !Cams[0].Using3rdPersonMouseCam() +#ifdef FREE_CAM + && (!CCamera::bFreeCam || m_bEnable1rstPersonCamCntrlsScript) +#endif + ){ + // See if we want to enter first person mode + if(CPad::GetPad(0)->LookAroundLeftRight() || CPad::GetPad(0)->LookAroundUpDown()){ + m_uiFirstPersonCamLastInputTime = CTimer::GetTimeInMilliseconds(); + m_bFirstPersonBeingUsed = true; + } + if(m_bFirstPersonBeingUsed){ + // Or if we want to go back to 3rd person + if(CPad::GetPad(0)->GetPedWalkLeftRight() || CPad::GetPad(0)->GetPedWalkUpDown() || + CPad::GetPad(0)->GetSquare() || CPad::GetPad(0)->GetTriangle() || + CPad::GetPad(0)->GetCross() || CPad::GetPad(0)->GetCircle() || + CTimer::GetTimeInMilliseconds() - m_uiFirstPersonCamLastInputTime > 2850.0f){ + m_bFirstPersonBeingUsed = false; + }else if(CPad::GetPad(0)->TargetJustDown()){ + m_bFirstPersonBeingUsed = false; + m_bJustJumpedOutOf1stPersonBecauseOfTarget = true; + } + } + }else + m_bFirstPersonBeingUsed = false; + + if(!FindPlayerPed()->IsPedInControl() || FindPlayerPed()->m_fMoveSpeed > 0.0f) + m_bFirstPersonBeingUsed = false; + if(m_bFirstPersonBeingUsed){ + ReqMode = CCam::MODE_1STPERSON; + CPad::GetPad(0)->DisablePlayerControls |= PLAYERCONTROL_CAMERA; + } + + // Zoom value + if(PedZoomIndicator == CAM_ZOOM_1) + m_fPedZoomValue = 0.25f; + else if(PedZoomIndicator == CAM_ZOOM_2) + m_fPedZoomValue = 1.5f; + else if(PedZoomIndicator == CAM_ZOOM_3) + m_fPedZoomValue = 2.9f; + + // Smooth zoom value - ugly code + if(m_bUseScriptZoomValuePed){ + if(m_fPedZoomValueSmooth < m_fPedZoomValueScript){ + m_fPedZoomValueSmooth += 0.12f * CTimer::GetTimeStep(); + m_fPedZoomValueSmooth = Min(m_fPedZoomValueSmooth, m_fPedZoomValueScript); + }else{ + m_fPedZoomValueSmooth -= 0.12f * CTimer::GetTimeStep(); + m_fPedZoomValueSmooth = Max(m_fPedZoomValueSmooth, m_fPedZoomValueScript); + } + }else if(m_bFailedCullZoneTestPreviously){ + static float PedZoomedInVal = 0.5f; + CloseInPedHeightTarget = 0.7f; + if(m_fPedZoomValueSmooth < PedZoomedInVal){ + m_fPedZoomValueSmooth += 0.12f * CTimer::GetTimeStep(); + m_fPedZoomValueSmooth = Min(m_fPedZoomValueSmooth, PedZoomedInVal); + }else{ + m_fPedZoomValueSmooth -= 0.12f * CTimer::GetTimeStep(); + m_fPedZoomValueSmooth = Max(m_fPedZoomValueSmooth, PedZoomedInVal); + } + }else{ + if(m_fPedZoomValueSmooth < m_fPedZoomValue){ + m_fPedZoomValueSmooth += 0.12f * CTimer::GetTimeStep(); + m_fPedZoomValueSmooth = Min(m_fPedZoomValueSmooth, m_fPedZoomValue); + }else{ + m_fPedZoomValueSmooth -= 0.12f * CTimer::GetTimeStep(); + m_fPedZoomValueSmooth = Max(m_fPedZoomValueSmooth, m_fPedZoomValue); + } + if(PedZoomIndicator == CAM_ZOOM_3 && m_fPedZoomValue == 0.0f) + m_fPedZoomValueSmooth = m_fPedZoomValue; + } + + WellBufferMe(CloseInPedHeightTarget, &Cams[ActiveCam].m_fCloseInPedHeightOffset, &Cams[ActiveCam].m_fCloseInPedHeightOffsetSpeed, 0.1f, 0.025f, false); + + // Check if entering fight cam + if(!m_bFirstPersonBeingUsed){ + if(FindPlayerPed()->GetPedState() == PED_FIGHT && !m_bUseMouse3rdPerson) + ReqMode = CCam::MODE_FIGHT_CAM; + if(((CPed*)pTargetEntity)->GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT && + FindPlayerPed()->GetPedState() == PED_ATTACK && !m_bUseMouse3rdPerson) + ReqMode = CCam::MODE_FIGHT_CAM; + } + + // Garage cam + CAttributeZone *stairsZone = nil; + if(CCullZones::CamStairsForPlayer()){ + stairsZone = CCullZones::FindZoneWithStairsAttributeForPlayer(); + if(stairsZone) + stairs = true; + } + if(CGarages::IsPointInAGarageCameraZone(pTargetEntity->GetPosition()) && !m_bUseMouse3rdPerson || stairs){ + if(!m_bGarageFixedCamPositionSet && m_bLookingAtPlayer){ + if(pToGarageWeAreIn || stairs){ + float ground; + bool foundGround; + + if(pToGarageWeAreIn){ + // targetPos = pTargetEntity->GetPosition(); // unused + if(pToGarageWeAreIn->m_pDoor1){ + whichDoor = 1; + garageDoorPos1.x = pToGarageWeAreIn->m_fDoor1X; + garageDoorPos1.y = pToGarageWeAreIn->m_fDoor1Y; + garageDoorPos1.z = 0.0f; + // targetPos.z = 0.0f; // unused + // (targetPos - doorPos1).Magnitude(); // unused + }else if(pToGarageWeAreIn->m_pDoor2){ + whichDoor = 2; +#ifdef FIX_BUGS + garageDoorPos2.x = pToGarageWeAreIn->m_fDoor2X; + garageDoorPos2.y = pToGarageWeAreIn->m_fDoor2Y; + garageDoorPos2.z = 0.0f; +#endif + }else{ + whichDoor = 1; + garageDoorPos1.x = pTargetEntity->GetPosition().x; + garageDoorPos1.y = pTargetEntity->GetPosition().y; +#ifdef FIX_BUGS + garageDoorPos1.z = 0.0f; +#else + garageDoorPos2.z = 0.0f; +#endif + } + }else{ + whichDoor = 1; + garageDoorPos1 = Cams[ActiveCam].Source; + + if(stairsZone){ // always true + garageCenter = CVector((stairsZone->minx+stairsZone->maxx)/2, (stairsZone->miny+stairsZone->maxy)/2, 0.0f); + if(pTargetEntity->GetPosition().x > 376.0f && pTargetEntity->GetPosition().x < 383.0f && + pTargetEntity->GetPosition().y > -496.0f && pTargetEntity->GetPosition().y < -489.0f && + pTargetEntity->GetPosition().z > 11.6f && pTargetEntity->GetPosition().z < 13.6f){ + garageDoorPos1 = CVector(382.6f, -489.6f, 13.1f); + }else{ + bool bClearViewOutside = true; + CVector dirOutside = pTargetEntity->GetPosition() - garageCenter; + dirOutside.z = 0.0f; + dirOutside.Normalise(); + float zoneDim = stairsZone->maxx - stairsZone->minx; + if(zoneDim < stairsZone->maxy - stairsZone->miny) + zoneDim = stairsZone->maxy - stairsZone->miny; + zoneDim *= 2.0f; + CVector posOutside = pTargetEntity->GetPosition() + zoneDim*dirOutside; + if(!CWorld::GetIsLineOfSightClear(pTargetEntity->GetPosition(), posOutside, true, false, false, false, false, false, true)){ + posOutside = pTargetEntity->GetPosition() - zoneDim*dirOutside; + if(!CWorld::GetIsLineOfSightClear(pTargetEntity->GetPosition(), posOutside, true, false, false, false, false, false, true)) + bClearViewOutside = false; + } + if(bClearViewOutside) + garageDoorPos1 = posOutside; + } + } + } + + if(pToGarageWeAreIn){ + garageCenter.x = pToGarageWeAreIn->GetGarageCenterX(); + garageCenter.y = pToGarageWeAreIn->GetGarageCenterY(); + garageCenter.z = 0.0f; + }else{ + garageDoorPos1.z = 0.0f; + if(!stairs) // how can this be true? + garageCenter = CVector(pTargetEntity->GetPosition().x, pTargetEntity->GetPosition().y, 0.0f); + } + if(whichDoor == 1) + garageCenterToDoor = garageDoorPos1 - garageCenter; + else + garageCenterToDoor = garageDoorPos2 - garageCenter; + targetPos = pTargetEntity->GetPosition(); + ground = CWorld::FindGroundZFor3DCoord(targetPos.x, targetPos.y, targetPos.z, &foundGround); + if(!foundGround) + ground = targetPos.z - 0.2f; + garageCenterToDoor.z = 0.0f; + garageCenterToDoor.Normalise(); + if(whichDoor == 1){ + if(pToGarageWeAreIn == nil && stairs){ + if(stairsZone){ + float zoneDim = stairsZone->maxx - stairsZone->minx; + if(zoneDim < stairsZone->maxy - stairsZone->miny) + zoneDim = stairsZone->maxy - stairsZone->miny; + garageCamPos = garageCenter + (0.7f*zoneDim + 3.75f)*garageCenterToDoor; + }else // how can this be true? + garageCamPos = garageDoorPos1 + 3.75f*garageCenterToDoor; + }else + garageCamPos = garageDoorPos1 + 13.0f*garageCenterToDoor; + }else{ + garageCamPos = garageDoorPos2 + 13.0f*garageCenterToDoor; + } + if(PedZoomIndicator == CAM_ZOOM_TOPDOWN && !stairs){ + garageCamPos = garageCenter; + garageCamPos.z += FindPlayerPed()->GetPosition().z + 2.1f; + if(pToGarageWeAreIn && garageCamPos.z > pToGarageWeAreIn->m_fSupX) // What? + garageCamPos.z = pToGarageWeAreIn->m_fSupX; + }else + garageCamPos.z = ground + 3.1f; + SetCamPositionForFixedMode(garageCamPos, CVector(0.0f, 0.0f, 0.0f)); + m_bGarageFixedCamPositionSet = true; + } + } + + if((CGarages::CameraShouldBeOutside() || stairs) && m_bLookingAtPlayer && m_bGarageFixedCamPositionSet){ + if(pToGarageWeAreIn || stairs){ + ReqMode = CCam::MODE_FIXED; + m_bPlayerIsInGarage = true; + } + }else{ + if(m_bPlayerIsInGarage){ + m_bJustCameOutOfGarage = true; + m_bPlayerIsInGarage = false; + } + ReqMode = CCam::MODE_FOLLOWPED; + } + }else{ + if(m_bPlayerIsInGarage){ + m_bJustCameOutOfGarage = true; + m_bPlayerIsInGarage = false; + } + m_bGarageFixedCamPositionSet = false; + } + + // Lighthouse + if(!m_bFirstPersonBeingUsed && (pTargetEntity->GetPosition() - CVector(474.3f, -1717.6f, 0.0f)).Magnitude2D() < 6.0f) + if((pTargetEntity->GetPosition() - CVector(474.3f, -1717.6f, 0.0f)).Magnitude2D() < 3.8f || + pTargetEntity->GetPosition().z > 50.0f) + if(!Cams[ActiveCam].Using3rdPersonMouseCam()) + ReqMode = CCam::MODE_LIGHTHOUSE; + + // Fallen into water + if(Cams[ActiveCam].IsTargetInWater(Cams[ActiveCam].Source) && + Cams[ActiveCam].CamTargetEntity->IsPed()) + ReqMode = CCam::MODE_PLAYER_FALLEN_WATER; + + // Set top down + if(PedZoomIndicator == CAM_ZOOM_TOPDOWN && + !CCullZones::Cam1stPersonForPlayer() && + !CCullZones::CamNoRain() && + !CCullZones::PlayerNoRain() && + !m_bFirstPersonBeingUsed && + !m_bPlayerIsInGarage) + ReqMode = CCam::MODE_TOP_DOWN_PED; + + // Weapon mode + if(!CPad::GetPad(0)->GetTarget() && PlayerWeaponMode.Mode != CCam::MODE_HELICANNON_1STPERSON) + ClearPlayerWeaponMode(); + if(m_PlayerMode.Mode != CCam::MODE_NONE) + ReqMode = m_PlayerMode.Mode; + if(PlayerWeaponMode.Mode != CCam::MODE_NONE && !stairs){ + if(PlayerWeaponMode.Mode == CCam::MODE_SNIPER || + PlayerWeaponMode.Mode == CCam::MODE_ROCKETLAUNCHER || + // game also checks MODE_MODELVIEW here but that does make any sense... + PlayerWeaponMode.Mode == CCam::MODE_M16_1STPERSON || + PlayerWeaponMode.Mode == CCam::MODE_HELICANNON_1STPERSON || + PlayerWeaponMode.Mode == CCam::MODE_CAMERA || + Cams[ActiveCam].GetWeaponFirstPersonOn()){ + // First person weapon mode + if(PLAYER->GetPedState() == PED_SEEK_CAR){ + if(ReqMode == CCam::MODE_TOP_DOWN_PED || Cams[ActiveCam].GetWeaponFirstPersonOn()) + ReqMode = PlayerWeaponMode.Mode; + else + ReqMode = CCam::MODE_FOLLOWPED; + }else + ReqMode = PlayerWeaponMode.Mode; + }else if(ReqMode != CCam::MODE_TOP_DOWN_PED && PedZoomIndicator != CAM_ZOOM_3){ + // Syphon mode + float playerTargetDist; + float deadPedDist = 4.0f; + static float alivePedDist = 2.0f; // original name lost + float pedDist; // actually only used on dead target + bool targetDead = false; + float camAngle, targetAngle; + CVector playerToTarget = m_cvecAimingTargetCoors - pTargetEntity->GetPosition(); + CVector playerToCam = Cams[ActiveCam].Source - pTargetEntity->GetPosition(); + + if(PedZoomIndicator == CAM_ZOOM_1) + deadPedDist = 2.25f; + if(FindPlayerPed()->m_pPointGunAt){ + // BUG: this need not be a ped! + if(((CPed*)FindPlayerPed()->m_pPointGunAt)->DyingOrDead()){ + targetDead = true; + pedDist = deadPedDist; + }else + pedDist = alivePedDist; + playerTargetDist = playerToTarget.Magnitude2D(); + camAngle = CGeneral::GetATanOfXY(playerToCam.x, playerToCam.y); + targetAngle = CGeneral::GetATanOfXY(playerToTarget.x, playerToTarget.y); + ReqMode = PlayerWeaponMode.Mode; + + // Check whether to start aiming in crim-in-front mode + if(Cams[ActiveCam].Mode != CCam::MODE_SYPHON){ + float angleDiff = camAngle - targetAngle; + while(angleDiff >= PI) angleDiff -= 2*PI; + while(angleDiff < -PI) angleDiff += 2*PI; + if(Abs(angleDiff) < HALFPI && playerTargetDist < 3.5f && playerToTarget.z > -1.0f) + ReqMode = CCam::MODE_SYPHON_CRIM_IN_FRONT; + } + + // Check whether to go to special fixed mode + float fixedModeDist = 0.0f; + if((ReqMode == CCam::MODE_SYPHON_CRIM_IN_FRONT || ReqMode == CCam::MODE_SYPHON) && + (m_uiTransitionState == 0 || Cams[ActiveCam].Mode == CCam::MODE_SPECIAL_FIXED_FOR_SYPHON) && + playerTargetDist < pedDist && targetDead){ + if(ReqMode == CCam::MODE_SYPHON_CRIM_IN_FRONT) + fixedModeDist = 5.0f; + else + fixedModeDist = 5.6f; + ReqMode = CCam::MODE_SPECIAL_FIXED_FOR_SYPHON; + } + if(ReqMode == CCam::MODE_SPECIAL_FIXED_FOR_SYPHON){ + if(!PlaceForFixedWhenSniperFound){ + // Find position + CEntity *entity; + CColPoint colPoint; + CVector fixedPos = pTargetEntity->GetPosition(); + fixedPos.x += fixedModeDist*Cos(camAngle); + fixedPos.y += fixedModeDist*Sin(camAngle); + fixedPos.z += 1.15f; + if(CWorld::ProcessLineOfSight(pTargetEntity->GetPosition(), fixedPos, colPoint, entity, true, false, false, true, false, true, true)) + SetCamPositionForFixedMode(colPoint.point, CVector(0.0f, 0.0f, 0.0f)); + else + SetCamPositionForFixedMode(fixedPos, CVector(0.0f, 0.0f, 0.0f)); + PlaceForFixedWhenSniperFound = true; + } + }else + PlaceForFixedWhenSniperFound = false; + } + } + } + } + } + + if(DebugCamMode) + ReqMode = DebugCamMode; + + + // Process arrested player + static int ThePickedArrestMode; + static int LastPedState; + bool startArrestCam = false; + static bool beingArrested = false; + bool stopArrestCam = false; + + if(PLAYER->GetPedState() == PED_ARRESTED) + beingArrested = true; + else if(beingArrested){ + stopArrestCam = true; + beingArrested = false; + } + if(LastPedState != PED_ARRESTED && PLAYER->GetPedState() == PED_ARRESTED){ + if(CarZoomIndicator != CAM_ZOOM_1STPRS || !pTargetEntity->IsVehicle()) + startArrestCam = true; + }else + startArrestCam = false; + LastPedState = PLAYER->GetPedState(); + + if(startArrestCam){ + ThePickedArrestMode = CCam::MODE_ARRESTCAM_ONE; + ReqMode = CCam::MODE_ARRESTCAM_ONE; + Cams[ActiveCam].ResetStatics = true; + }else if(PLAYER->GetPedState() == PED_ARRESTED) + ReqMode = ThePickedArrestMode; + + // Process dead player + if(PLAYER->GetPedState() == PED_DEAD){ + if(Cams[ActiveCam].Mode == CCam::MODE_PED_DEAD_BABY) + ReqMode = CCam::MODE_PED_DEAD_BABY; + else{ + bool useArrestCam = false; + if(pTargetEntity->IsPed()){ + for(int i = 0; i < ((CPed*)pTargetEntity)->m_numNearPeds; i++){ + CPed *ped = ((CPed*)pTargetEntity)->m_nearPeds[i]; + if(ped && ped->GetPedState() == PED_ARREST_PLAYER) + if((ped->GetPosition() - pTargetEntity->GetPosition()).Magnitude() < 4.0f){ + ReqMode = CCam::MODE_ARRESTCAM_ONE; + Cams[ActiveCam].ResetStatics = true; + useArrestCam = true; + break; + } + } + } + if(!useArrestCam){ + ReqMode = CCam::MODE_PED_DEAD_BABY; + Cams[ActiveCam].ResetStatics = true; + } + } + } + + // Restore with a jump cut + if(m_bRestoreByJumpCut){ + if(ReqMode != CCam::MODE_FOLLOWPED && + ReqMode != CCam::MODE_BEHINDCAR && + ReqMode != CCam::MODE_CAM_ON_A_STRING && + ReqMode != CCam::MODE_M16_1STPERSON && + ReqMode != CCam::MODE_SYPHON && + ReqMode != CCam::MODE_SYPHON_CRIM_IN_FRONT && + ReqMode != CCam::MODE_SPECIAL_FIXED_FOR_SYPHON && + ReqMode != CCam::MODE_SNIPER && + ReqMode != CCam::MODE_ROCKETLAUNCHER && + ReqMode != CCam::MODE_CAMERA && + !m_bUseMouse3rdPerson) + SetCameraDirectlyBehindForFollowPed_CamOnAString(); + + ReqMode = m_iModeToGoTo; + Cams[ActiveCam].Mode = ReqMode; + m_bJust_Switched = true; + Cams[ActiveCam].ResetStatics = true; + Cams[ActiveCam].m_cvecCamFixedModeVector = m_vecFixedModeVector; + Cams[ActiveCam].CamTargetEntity = pTargetEntity; + Cams[ActiveCam].m_cvecCamFixedModeSource = m_vecFixedModeSource; + Cams[ActiveCam].m_cvecCamFixedModeUpOffSet = m_vecFixedModeUpOffSet; + Cams[ActiveCam].m_bCamLookingAtVector = false; + Cams[ActiveCam].m_vecLastAboveWaterCamPosition = Cams[(ActiveCam+1)%2].m_vecLastAboveWaterCamPosition; + m_bRestoreByJumpCut = false; + Cams[ActiveCam].ResetStatics = true; + pTargetEntity->RegisterReference(&pTargetEntity); + Cams[ActiveCam].CamTargetEntity->RegisterReference(&Cams[ActiveCam].CamTargetEntity); + CarZoomValueSmooth = CarZoomValue; + m_fPedZoomValueSmooth = m_fPedZoomValue; + m_uiTransitionState = 0; + m_vecDoingSpecialInterPolation = false; + } + + if(gbModelViewer) + ReqMode = CCam::MODE_MODELVIEW; + + // Turn on Obbe's cam + bool canUseObbeCam = true; + if(pTargetEntity){ + if(pTargetEntity->IsVehicle()){ + if(CarZoomIndicator == CAM_ZOOM_CINEMATIC) + m_bObbeCinematicCarCamOn = true; + }else{ + if(PedZoomIndicator == CAM_ZOOM_CINEMATIC) + m_bObbeCinematicPedCamOn = true; + } + } + if(m_bTargetJustBeenOnTrain || + ReqMode == CCam::MODE_SYPHON || ReqMode == CCam::MODE_SYPHON_CRIM_IN_FRONT || ReqMode == CCam::MODE_SPECIAL_FIXED_FOR_SYPHON || + ReqMode == CCam::MODE_PED_DEAD_BABY || ReqMode == CCam::MODE_ARRESTCAM_ONE || ReqMode == CCam::MODE_ARRESTCAM_TWO || + ReqMode == CCam::MODE_FIGHT_CAM || ReqMode == CCam::MODE_PLAYER_FALLEN_WATER || + ReqMode == CCam::MODE_SNIPER || ReqMode == CCam::MODE_ROCKETLAUNCHER || ReqMode == CCam::MODE_M16_1STPERSON || + ReqMode == CCam::MODE_SNIPER_RUNABOUT || ReqMode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || + ReqMode == CCam::MODE_1STPERSON_RUNABOUT || ReqMode == CCam::MODE_M16_1STPERSON_RUNABOUT || + ReqMode == CCam::MODE_FIGHT_CAM_RUNABOUT || ReqMode == CCam::MODE_HELICANNON_1STPERSON || ReqMode == CCam::MODE_CAMERA || + WhoIsInControlOfTheCamera == CAMCONTROL_SCRIPT || + m_bJustCameOutOfGarage || m_bPlayerIsInGarage) + canUseObbeCam = false; + + if(m_bObbeCinematicPedCamOn && canUseObbeCam) + ProcessObbeCinemaCameraPed(); + else if(m_bObbeCinematicCarCamOn && canUseObbeCam){ + if(pTargetEntity->IsVehicle() && ((CVehicle*)pTargetEntity)->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI || + ((CVehicle*)pTargetEntity)->IsBoat()) + ProcessObbeCinemaCameraHeli(); + else + ProcessObbeCinemaCameraCar(); + }else{ + if(m_bPlayerIsInGarage && m_bObbeCinematicCarCamOn) + switchByJumpCut = true; + canUseObbeCam = false; + DontProcessObbeCinemaCamera(); + } + + // Start the transition or do a jump cut + if(m_bLookingAtPlayer){ + // Going into top down modes normally needs a jump cut (but see below) + if(ReqMode == CCam::MODE_TOPDOWN || ReqMode == CCam::MODE_1STPERSON || ReqMode == CCam::MODE_TOP_DOWN_PED){ + switchByJumpCut = true; + } + // Going from top down to vehicle + else if(ReqMode == CCam::MODE_CAM_ON_A_STRING || ReqMode == CCam::MODE_BEHINDBOAT){ + if(Cams[ActiveCam].Mode == CCam::MODE_TOPDOWN || + Cams[ActiveCam].Mode == CCam::MODE_1STPERSON || + Cams[ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED) + switchByJumpCut = true; + }else if(ReqMode == CCam::MODE_FIXED){ + if(Cams[ActiveCam].Mode == CCam::MODE_TOPDOWN) + switchByJumpCut = true; + } + + // Going into Syphon mode + if(ReqMode == CCam::MODE_SYPHON || ReqMode == CCam::MODE_SYPHON_CRIM_IN_FRONT) + switchByJumpCut = true; + + // Top down modes can interpolate between each other + if(ReqMode == CCam::MODE_TOPDOWN){ + if(Cams[ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED || Cams[ActiveCam].Mode == CCam::MODE_PED_DEAD_BABY) + switchByJumpCut = false; + }else if(ReqMode == CCam::MODE_TOP_DOWN_PED){ + if(Cams[ActiveCam].Mode == CCam::MODE_TOPDOWN || Cams[ActiveCam].Mode == CCam::MODE_PED_DEAD_BABY) + switchByJumpCut = false; + } + + if(ReqMode == CCam::MODE_1STPERSON || ReqMode == CCam::MODE_M16_1STPERSON || + ReqMode == CCam::MODE_SNIPER || ReqMode == CCam::MODE_ROCKETLAUNCHER || + ReqMode == CCam::MODE_SNIPER_RUNABOUT || ReqMode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || + ReqMode == CCam::MODE_1STPERSON_RUNABOUT || ReqMode == CCam::MODE_M16_1STPERSON_RUNABOUT || + ReqMode == CCam::MODE_FIGHT_CAM_RUNABOUT || + ReqMode == CCam::MODE_HELICANNON_1STPERSON || ReqMode == CCam::MODE_CAMERA || + ReqMode == CCam::MODE_ARRESTCAM_ONE || ReqMode == CCam::MODE_ARRESTCAM_TWO){ + // Going into any 1st person mode is a jump cut + if(pTargetEntity->IsPed()) + switchByJumpCut = true; + }else if(ReqMode == CCam::MODE_FIXED && m_bPlayerIsInGarage){ + // Going from 1st peron mode into garage + if(Cams[ActiveCam].Mode == CCam::MODE_SNIPER || + Cams[ActiveCam].Mode == CCam::MODE_HELICANNON_1STPERSON || + Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER || + Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON || + Cams[ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED || + stairs || + Cams[ActiveCam].Mode == CCam::MODE_1STPERSON || + Cams[ActiveCam].Mode == CCam::MODE_SNIPER_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_FIGHT_CAM_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_1STPERSON_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_CAMERA){ + if(pTargetEntity && pTargetEntity->IsVehicle()) + switchByJumpCut = true; + } + }else if(ReqMode == CCam::MODE_FOLLOWPED){ + bool syphonJumpCut = false; + if(Cams[ActiveCam].Mode == CCam::MODE_SYPHON || Cams[ActiveCam].Mode == CCam::MODE_SYPHON_CRIM_IN_FRONT) + if(!((CPed*)pTargetEntity)->CanWeRunAndFireWithWeapon()) + syphonJumpCut = true; + if(Cams[ActiveCam].Mode == CCam::MODE_1STPERSON || + Cams[ActiveCam].Mode == CCam::MODE_SNIPER || + Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER || + Cams[ActiveCam].Mode == CCam::MODE_ARRESTCAM_ONE || + Cams[ActiveCam].Mode == CCam::MODE_ARRESTCAM_TWO || + Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON || + Cams[ActiveCam].Mode == CCam::MODE_PED_DEAD_BABY || + Cams[ActiveCam].Mode == CCam::MODE_PILLOWS_PAPS || + Cams[ActiveCam].Mode == CCam::MODE_SNIPER_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_1STPERSON_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_FIGHT_CAM_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_HELICANNON_1STPERSON || + Cams[ActiveCam].Mode == CCam::MODE_CAMERA || + Cams[ActiveCam].Mode == CCam::MODE_TOPDOWN || + Cams[ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED || + syphonJumpCut || stopArrestCam){ + if(!m_bJustCameOutOfGarage){ + if(Cams[ActiveCam].Mode == CCam::MODE_1STPERSON || + Cams[ActiveCam].Mode == CCam::MODE_SNIPER || + Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER || + Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON || + Cams[ActiveCam].Mode == CCam::MODE_SNIPER_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_1STPERSON_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_FIGHT_CAM_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_HELICANNON_1STPERSON || + Cams[ActiveCam].Mode == CCam::MODE_CAMERA){ + float angle = CGeneral::GetATanOfXY(Cams[ActiveCam].Front.x, Cams[ActiveCam].Front.y) - HALFPI; + ((CPed*)pTargetEntity)->m_fRotationCur = angle; + ((CPed*)pTargetEntity)->m_fRotationDest = angle; + } + m_bUseTransitionBeta = true; + switchByJumpCut = true; + if(Cams[ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED){ + CVector front = Cams[ActiveCam].Source - FindPlayerPed()->GetPosition(); + front.z = 0.0f; + front.Normalise(); +#ifdef FIX_BUGS + // this is almost as bad as the bugged code + if(front.x == 0.001f && front.y == 0.001f) + front.y = 1.0f; +#else + // someone used = instead of == in the above check by accident + front.x = 0.001f; + front.y = 1.0f; +#endif + Cams[ActiveCam].m_fTransitionBeta = CGeneral::GetATanOfXY(front.x, front.y); + }else + Cams[ActiveCam].m_fTransitionBeta = CGeneral::GetATanOfXY(Cams[ActiveCam].Front.x, Cams[ActiveCam].Front.y) + PI; + } + } + }else if(ReqMode == CCam::MODE_FIGHT_CAM){ + if(Cams[ActiveCam].Mode == CCam::MODE_1STPERSON) + switchByJumpCut = true; + }else if(ReqMode == CCam::MODE_LIGHTHOUSE || + ReqMode == CCam::MODE_ARRESTCAM_ONE || ReqMode == CCam::MODE_ARRESTCAM_TWO || + ReqMode == CCam::MODE_PED_DEAD_BABY) + switchByJumpCut = true; + else if(Cams[ActiveCam].Mode == CCam::MODE_PED_DEAD_BABY && ReqMode != CCam::MODE_PED_DEAD_BABY) + switchByJumpCut = true; + + if(ReqMode != Cams[ActiveCam].Mode && Cams[ActiveCam].CamTargetEntity == nil) + switchByJumpCut = true; + if(m_bPlayerIsInGarage && pToGarageWeAreIn){ + if(pToGarageWeAreIn->m_eGarageType == GARAGE_BOMBSHOP1 || + pToGarageWeAreIn->m_eGarageType == GARAGE_BOMBSHOP2 || + pToGarageWeAreIn->m_eGarageType == GARAGE_BOMBSHOP3){ + if(pTargetEntity->IsVehicle() && pTargetEntity->GetModelIndex() == MI_MRWHOOP && + ReqMode != Cams[ActiveCam].Mode) + switchByJumpCut = true; + } + } +#ifdef GTA_SCENE_EDIT + if(CSceneEdit::m_bEditOn) + ReqMode = CCam::MODE_EDITOR; +#endif + + if((m_uiTransitionState == 0 || switchByJumpCut) && ReqMode != Cams[ActiveCam].Mode){ + if(switchByJumpCut){ + if(!m_bPlayerIsInGarage || m_bJustCameOutOfGarage){ + if(ReqMode != CCam::MODE_FOLLOWPED && + ReqMode != CCam::MODE_M16_1STPERSON && + ReqMode != CCam::MODE_SNIPER && + ReqMode != CCam::MODE_ROCKETLAUNCHER && + !m_bUseMouse3rdPerson) + SetCameraDirectlyBehindForFollowPed_CamOnAString(); + } + Cams[ActiveCam].Mode = ReqMode; + m_bJust_Switched = true; + Cams[ActiveCam].m_cvecCamFixedModeVector = m_vecFixedModeVector; + Cams[ActiveCam].CamTargetEntity = pTargetEntity; + Cams[ActiveCam].m_cvecCamFixedModeSource = m_vecFixedModeSource; + Cams[ActiveCam].m_cvecCamFixedModeUpOffSet = m_vecFixedModeUpOffSet; + Cams[ActiveCam].m_bCamLookingAtVector = m_bLookingAtVector; + Cams[ActiveCam].m_vecLastAboveWaterCamPosition = Cams[(ActiveCam+1)%2].m_vecLastAboveWaterCamPosition; + CarZoomValueSmooth = CarZoomValue; + m_fPedZoomValueSmooth = m_fPedZoomValue; + m_uiTransitionState = 0; + m_vecDoingSpecialInterPolation = false; + m_bStartInterScript = false; + Cams[ActiveCam].ResetStatics = true; + + pTargetEntity->RegisterReference(&pTargetEntity); + Cams[ActiveCam].CamTargetEntity->RegisterReference(&Cams[ActiveCam].CamTargetEntity); + }else if(!m_bWaitForInterpolToFinish){ + StartTransition(ReqMode); + pTargetEntity->RegisterReference(&pTargetEntity); + Cams[ActiveCam].CamTargetEntity->RegisterReference(&Cams[ActiveCam].CamTargetEntity); + } + }else if(m_uiTransitionState != 0 && ReqMode != Cams[ActiveCam].Mode){ + bool startTransition = true; + + if(ReqMode == CCam::MODE_FIGHT_CAM || Cams[ActiveCam].Mode == CCam::MODE_FIGHT_CAM) + startTransition = false; + if(ReqMode == CCam::MODE_FOLLOWPED && Cams[ActiveCam].Mode == CCam::MODE_FIGHT_CAM) + startTransition = false; + + if(!m_bWaitForInterpolToFinish && m_bLookingAtPlayer && m_uiTransitionState != 0){ + CVector playerDist; + playerDist.x = FindPlayerPed()->GetPosition().x - GetPosition().x; + playerDist.y = FindPlayerPed()->GetPosition().y - GetPosition().y; + playerDist.z = FindPlayerPed()->GetPosition().z - GetPosition().z; + // if player is too far away, keep interpolating and don't transition + if(pTargetEntity && pTargetEntity->IsPed()){ + if(playerDist.Magnitude() > 17.5f && + (ReqMode == CCam::MODE_SYPHON || ReqMode == CCam::MODE_SYPHON_CRIM_IN_FRONT)) + m_bWaitForInterpolToFinish = true; + } + } + if(m_bWaitForInterpolToFinish) + startTransition = false; + + if(startTransition){ + StartTransitionWhenNotFinishedInter(ReqMode); + pTargetEntity->RegisterReference(&pTargetEntity); + Cams[ActiveCam].CamTargetEntity->RegisterReference(&Cams[ActiveCam].CamTargetEntity); + } + }else if(ReqMode == CCam::MODE_FIXED && pTargetEntity != Cams[ActiveCam].CamTargetEntity && m_bPlayerIsInGarage){ + if(m_uiTransitionState != 0) + StartTransitionWhenNotFinishedInter(ReqMode); + else + StartTransition(ReqMode); + pTargetEntity->RegisterReference(&pTargetEntity); + Cams[ActiveCam].CamTargetEntity->RegisterReference(&Cams[ActiveCam].CamTargetEntity); + } + }else{ + // not following player + bool useWeaponMode = false; + bool jumpCutTo1stPrs = false; + if(m_bEnable1rstPersonCamCntrlsScript || m_bAllow1rstPersonWeaponsCamera){ + if(ReqMode == CCam::MODE_1STPERSON){ + if(Cams[ActiveCam].Mode != ReqMode) + jumpCutTo1stPrs = true; + }else if((PlayerWeaponMode.Mode == CCam::MODE_SNIPER || PlayerWeaponMode.Mode == CCam::MODE_1STPERSON || PlayerWeaponMode.Mode == CCam::MODE_ROCKETLAUNCHER) && + CPad::GetPad(0)->GetTarget() && m_bAllow1rstPersonWeaponsCamera){ + useWeaponMode = true; + jumpCutTo1stPrs = true; + }else if(Cams[ActiveCam].Mode != m_iModeToGoTo){ + m_bStartInterScript = true; + m_iTypeOfSwitch = JUMP_CUT; + CPad::GetPad(0)->DisablePlayerControls &= ~PLAYERCONTROL_CAMERA; + } + } + + if(m_uiTransitionState == 0 && m_bStartInterScript && m_iTypeOfSwitch == INTERPOLATION){ + ReqMode = m_iModeToGoTo; + StartTransition(ReqMode); + pTargetEntity->RegisterReference(&pTargetEntity); + Cams[ActiveCam].CamTargetEntity->RegisterReference(&Cams[ActiveCam].CamTargetEntity); + }else if(m_uiTransitionState != 0 && m_bStartInterScript && m_iTypeOfSwitch == INTERPOLATION){ + ReqMode = m_iModeToGoTo; + StartTransitionWhenNotFinishedInter(ReqMode); + pTargetEntity->RegisterReference(&pTargetEntity); + Cams[ActiveCam].CamTargetEntity->RegisterReference(&Cams[ActiveCam].CamTargetEntity); + }else if(m_bStartInterScript && m_iTypeOfSwitch == JUMP_CUT || jumpCutTo1stPrs){ + m_uiTransitionState = 0; + m_vecDoingSpecialInterPolation = false; + if(m_bEnable1rstPersonCamCntrlsScript && ReqMode == CCam::MODE_1STPERSON) + Cams[ActiveCam].Mode = ReqMode; + else if(useWeaponMode) + Cams[ActiveCam].Mode = PlayerWeaponMode.Mode; + else + Cams[ActiveCam].Mode = m_iModeToGoTo; + m_bJust_Switched = true; + Cams[ActiveCam].ResetStatics = true; + Cams[ActiveCam].m_cvecCamFixedModeVector = m_vecFixedModeVector; + Cams[ActiveCam].CamTargetEntity = pTargetEntity; + Cams[ActiveCam].m_cvecCamFixedModeSource = m_vecFixedModeSource; + Cams[ActiveCam].m_cvecCamFixedModeUpOffSet = m_vecFixedModeUpOffSet; + Cams[ActiveCam].m_bCamLookingAtVector = m_bLookingAtVector; + Cams[ActiveCam].m_vecLastAboveWaterCamPosition = Cams[(ActiveCam+1)%2].m_vecLastAboveWaterCamPosition; + m_bJust_Switched = true; + pTargetEntity->RegisterReference(&pTargetEntity); + Cams[ActiveCam].CamTargetEntity->RegisterReference(&Cams[ActiveCam].CamTargetEntity); + CarZoomValueSmooth = CarZoomValue; + m_fPedZoomValueSmooth = m_fPedZoomValue; + } + } + + m_bStartInterScript = false; + + if(Cams[ActiveCam].CamTargetEntity == nil) + Cams[ActiveCam].CamTargetEntity = pTargetEntity; + + // Ped visibility + if((Cams[ActiveCam].Mode == CCam::MODE_1STPERSON || + Cams[ActiveCam].Mode == CCam::MODE_SNIPER || + Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON || + Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER || + Cams[ActiveCam].Mode == CCam::MODE_HELICANNON_1STPERSON || + Cams[ActiveCam].Mode == CCam::MODE_CAMERA) && pTargetEntity->IsPed() || + Cams[ActiveCam].Mode == CCam::MODE_FLYBY) + FindPlayerPed()->bIsVisible = false; + else + FindPlayerPed()->bIsVisible = true; + + bool switchedFromObbe = false; + if(!canUseObbeCam && WhoIsInControlOfTheCamera == CAMCONTROL_OBBE){ + RestoreWithJumpCut(); + switchedFromObbe = true; + SetCameraDirectlyBehindForFollowPed_CamOnAString(); + } + + if(PrevMode != Cams[ActiveCam].Mode || switchedFromObbe || + Cams[ActiveCam].Mode == CCam::MODE_FOLLOWPED || Cams[ActiveCam].Mode == CCam::MODE_CAM_ON_A_STRING) + if(CPad::GetPad(0)->CycleCameraModeJustDown() && + !CReplay::IsPlayingBack() && + (m_bLookingAtPlayer || WhoIsInControlOfTheCamera == CAMCONTROL_OBBE) && + !m_WideScreenOn && + (WhoIsInControlOfTheCamera != CAMCONTROL_OBBE || bSwitchedToObbeCam)) + DMAudio.PlayFrontEndSound(SOUND_HUD, 0); +} + +// What a mess! +void +CCamera::UpdateTargetEntity(void) +{ + bool enteringCar = false; + bool obbeCam = false; + + m_bPlayerWasOnBike = false; + if(pTargetEntity && pTargetEntity->IsVehicle() && ((CVehicle*)pTargetEntity)->IsBike()) + m_bPlayerWasOnBike = true; + + if(WhoIsInControlOfTheCamera == CAMCONTROL_OBBE){ + obbeCam = true; + if(m_iModeObbeCamIsInForCar == OBBE_COPCAR_WHEEL || m_iModeObbeCamIsInForCar == OBBE_COPCAR){ + if(FindPlayerPed()->GetPedState() != PED_ARRESTED) + obbeCam = false; + if(FindPlayerVehicle() == nil) + pTargetEntity = FindPlayerPed(); + } + } + + if((m_bLookingAtPlayer || obbeCam) && m_uiTransitionState == 0 || + pTargetEntity == nil || + m_bTargetJustBeenOnTrain){ + if(FindPlayerVehicle()) + pTargetEntity = FindPlayerVehicle(); + else{ + pTargetEntity = FindPlayerPed(); + // this keeps the camera on the player while entering cars + if(PLAYER->GetPedState() == PED_ENTER_CAR || + PLAYER->GetPedState() == PED_CARJACK || + PLAYER->GetPedState() == PED_OPEN_DOOR) + enteringCar = true; + + if(!enteringCar) + if(Cams[ActiveCam].CamTargetEntity != pTargetEntity) + Cams[ActiveCam].CamTargetEntity = pTargetEntity; + } + + bool cantOpen = true; + if(PLAYER){ + if(PLAYER->m_pMyVehicle){ + if(FindPlayerPed()->m_pMyVehicle->CanPedOpenLocks(PLAYER)) + cantOpen = false; + }else if(FindPlayerPed()->m_carInObjective && + (FindPlayerPed()->GetPedState() == PED_ENTER_CAR || + FindPlayerPed()->GetPedState() == PED_CARJACK || + FindPlayerPed()->GetPedState() == PED_OPEN_DOOR)){ + if(FindPlayerPed()->m_carInObjective->CanPedOpenLocks(FindPlayerPed())) + cantOpen = false; + } + } + + if(PLAYER->GetPedState() == PED_ENTER_CAR && !cantOpen){ + if(!enteringCar && CarZoomIndicator != CAM_ZOOM_1STPRS){ + pTargetEntity = PLAYER->m_pMyVehicle; + if(PLAYER->m_pMyVehicle == nil) + pTargetEntity = PLAYER; + } + } + + if((PLAYER->GetPedState() == PED_CARJACK || PLAYER->GetPedState() == PED_OPEN_DOOR) && !cantOpen){ + if(!enteringCar && CarZoomIndicator != CAM_ZOOM_1STPRS) + pTargetEntity = PLAYER->m_pMyVehicle; + if(PLAYER->m_pMyVehicle == nil) + pTargetEntity = PLAYER; + } + + if(PLAYER->GetPedState() == PED_EXIT_CAR) + pTargetEntity = FindPlayerPed(); + if(PLAYER->GetPedState() == PED_DRAG_FROM_CAR) + pTargetEntity = FindPlayerPed(); + if(pTargetEntity->IsVehicle() && CarZoomIndicator == CAM_ZOOM_1STPRS && FindPlayerPed()->GetPedState() == PED_ARRESTED) + pTargetEntity = FindPlayerPed(); + } +} + +const float SOUND_DIST = 20.0f; + +void +CCamera::UpdateSoundDistances(void) +{ + CVector center, end; + CEntity *entity; + CColPoint colPoint; + float f; + int n; + + if((Cams[ActiveCam].Mode == CCam::MODE_1STPERSON || + Cams[ActiveCam].Mode == CCam::MODE_SNIPER || + Cams[ActiveCam].Mode == CCam::MODE_SNIPER_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_1STPERSON_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_FIGHT_CAM_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_HELICANNON_1STPERSON || + Cams[ActiveCam].Mode == CCam::MODE_CAMERA || + Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON || + Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER) && + pTargetEntity->IsPed()) + center = GetPosition() + 0.5f*GetForward(); + else + center = GetPosition() + 5.0f*GetForward(); + + // check up + n = CTimer::GetFrameCounter() % 12; + if(n == 0){ + SoundDistUpAsReadOld = SoundDistUpAsRead; + if(CWorld::ProcessVerticalLine(center, center.z+SOUND_DIST, colPoint, entity, true, false, false, false, true, false, nil)) + SoundDistUpAsRead = colPoint.point.z - center.z; + else + SoundDistUpAsRead = SOUND_DIST; + } + f = (n + 1) / 6.0f; + SoundDistUp = (1.0f-f)*SoundDistUpAsReadOld + f*SoundDistUpAsRead; +} + +void +CCamera::InitialiseCameraForDebugMode(void) +{ + if(FindPlayerVehicle()) + Cams[2].Source = FindPlayerVehicle()->GetPosition(); + else if(FindPlayerPed()) + Cams[2].Source = FindPlayerPed()->GetPosition(); + Cams[2].Alpha = 0.0f; + Cams[2].Beta = 0.0f; + Cams[2].Mode = CCam::MODE_DEBUG; +} + +void +CCamera::CamShake(float strength, float x, float y, float z) +{ + CVector Dist = Cams[ActiveCam].Source - CVector(x, y, z); + // a bit complicated... + float dist2d = Sqrt(SQR(Dist.x) + SQR(Dist.y)); + float dist3d = Sqrt(SQR(dist2d) + SQR(Dist.z)); + if(dist3d > 100.0f) dist3d = 100.0f; + if(dist3d < 0.0f) dist3d = 0.0f; + float mult = 1.0f - dist3d/100.0f; + + float curForce = mult*(m_fCamShakeForce - (CTimer::GetTimeInMilliseconds() - m_uiCamShakeStart)/1000.0f); + strength = mult*strength; + if(Clamp(curForce, 0.0f, 2.0f) < strength){ + m_fCamShakeForce = strength; + m_uiCamShakeStart = CTimer::GetTimeInMilliseconds(); + } +} + +// This seems to be CCamera::CamShake(float) on PS2 +void +CamShakeNoPos(CCamera *cam, float strength) +{ + float curForce = cam->m_fCamShakeForce - (CTimer::GetTimeInMilliseconds() - cam->m_uiCamShakeStart)/1000.0f; + if(Clamp(curForce, 0.0f, 2.0f) < strength){ + cam->m_fCamShakeForce = strength; + cam->m_uiCamShakeStart = CTimer::GetTimeInMilliseconds(); + } +} + +bool bAvoidTest1 = false; +bool bAvoidTest2 = false; // unused +bool bAvoidTest3 = false; // unused +float fRangePlayerRadius = 0.5f; +float fCloseNearClipLimit = 0.15f; +float fAvoidTweakFOV = 1.15f; +float fAvoidProbTimerDamp = 0.9f; + +void +CCamera::AvoidTheGeometry(const CVector &Source, const CVector &TargetPos, CVector &NewSource, float FOV) +{ + float Beta = 0.0f; + float Alpha = 0.0f; + + CVector vDist = TargetPos - Source; + m_vecClearGeometryVec = CVector(0.0f, 0.0f, 0.0f); + float fDist = vDist.Magnitude(); + float fDistOnGround = vDist.Magnitude2D(); + if(vDist.x == 0.0f && vDist.y == 0.0f) + Beta = CGeneral::GetATanOfXY(GetForward().x, GetForward().y); + else + Beta = CGeneral::GetATanOfXY(vDist.x, vDist.y); + if(fDistOnGround != 0.0f || vDist.z != 0.0f) + Alpha = CGeneral::GetATanOfXY(fDistOnGround, vDist.z); + CVector Front(Cos(Alpha)*Cos(Beta), Cos(Alpha)*Sin(Beta), Sin(Alpha)); + NewSource = TargetPos - Front*fDist; + Front.Normalise(); + + // Clip camera source + CColPoint point; + CEntity *entity = nil; + CWorld::pIgnoreEntity = pTargetEntity; + if(CWorld::ProcessLineOfSight(TargetPos, NewSource, point, entity, true, false, false, true, false, false, true)){ + CVector ClipPoint1 = point.point; + NewSource = point.point; + if(!bAvoidTest1){ + if(CWorld::ProcessLineOfSight(NewSource, TargetPos, point, entity, false, true, true, true, false, false, true)){ + if((NewSource - point.point).Magnitude() < RwCameraGetNearClipPlane(Scene.camera)) + NewSource = point.point; + else if((NewSource - ClipPoint1).Magnitude() < RwCameraGetNearClipPlane(Scene.camera)) + NewSource = ClipPoint1; + } + } + } + CWorld::pIgnoreEntity = nil; + + + vDist = TargetPos - NewSource; + fDist = vDist.Magnitude(); + if(FindPlayerPed()) + if(fDist - fRangePlayerRadius < RwCameraGetNearClipPlane(Scene.camera)) + RwCameraSetNearClipPlane(Scene.camera, Max(fDist - fRangePlayerRadius, fCloseNearClipLimit)); + + + static float fClearGeomAmount; + static float fClearGeomAmountSpeed; + float Near = RwCameraGetNearClipPlane(Scene.camera); + float ViewPlaneHeight = Tan(DEGTORAD(FOV) / 2.0f); + float ViewPlaneWidth = ViewPlaneHeight * CDraw::CalculateAspectRatio() * fAvoidTweakFOV; + CVector Center = NewSource + Front*Near; + float fClearGeomTarget = 0.0f; + if(CWorld::TestSphereAgainstWorld(Center, ViewPlaneWidth, nil, true, false, false, true, false, true)){ + CVector CamToCol = gaTempSphereColPoints[0].point - NewSource; + float FrontDist = DotProduct(CamToCol, Front); + CVector CenterToCol = gaTempSphereColPoints[0].point - Center; + if(FrontDist < DEFAULT_NEAR && FrontDist > fCloseNearClipLimit){ + if(FrontDist < RwCameraGetNearClipPlane(Scene.camera)) + RwCameraSetNearClipPlane(Scene.camera, FrontDist); + }else if(FrontDist < fCloseNearClipLimit) + RwCameraSetNearClipPlane(Scene.camera, fCloseNearClipLimit); + + float ColDepth = ViewPlaneWidth - CenterToCol.Magnitude(); // amount of radius in collision + CenterToCol.Normalise(); + CVector Normal = gaTempSphereColPoints[0].normal; + Normal.Normalise(); + if(-DotProduct(CenterToCol, Normal) < 0.0f) + Normal = -Normal; // always push away from col surface + float DistToMove = DotProduct(-ColDepth*CenterToCol, Normal); + m_vecClearGeometryVec = DistToMove*Normal; // move source so this point is out of collision + + if(pTargetEntity && pTargetEntity->IsPed() && RwCameraGetNearClipPlane(Scene.camera) < 2.0f*fCloseNearClipLimit){ + float TargetNormalDir = DotProduct(Normal, pTargetEntity->GetForward()); + if(TargetNormalDir < 0.0f){ + // target looking towards collision + if(m_fAvoidTheGeometryProbsTimer < 0.0f) + m_fAvoidTheGeometryProbsTimer = 0.0f; + m_fAvoidTheGeometryProbsTimer += CTimer::GetTimeStep(); + }else if(TargetNormalDir > 0.5f){ + // target looking away from collision + if(m_fAvoidTheGeometryProbsTimer > 0.0f) + m_fAvoidTheGeometryProbsTimer = 0.0f; + m_fAvoidTheGeometryProbsTimer -= CTimer::GetTimeStep(); + } + + if(m_nAvoidTheGeometryProbsDirn == 0){ + if(CrossProduct(pTargetEntity->GetPosition() - NewSource, Normal).z > 0.0f) + m_nAvoidTheGeometryProbsDirn = -1; + else + m_nAvoidTheGeometryProbsDirn = 1; + } + } + + fClearGeomTarget = 1.0f; + } + + m_fAvoidTheGeometryProbsTimer *= Pow(fAvoidProbTimerDamp, CTimer::GetTimeStep()); + WellBufferMe(fClearGeomTarget, &fClearGeomAmount, &fClearGeomAmountSpeed, 0.2f, 0.05f, false); + m_vecClearGeometryVec *= fClearGeomAmount; + m_bMoveCamToAvoidGeom = true; +} + +void +CCamera::GetArrPosForVehicleType(int apperance, int &index) +{ + switch(apperance){ + case VEHICLE_APPEARANCE_CAR: index = 0; break; + case VEHICLE_APPEARANCE_BIKE: index = 1; break; + case VEHICLE_APPEARANCE_HELI: index = 2; break; + case VEHICLE_APPEARANCE_PLANE: index = 3; break; + case VEHICLE_APPEARANCE_BOAT: index = 4; break; + } +} + +void +CCamera::GetScreenRect(CRect &rect) +{ + rect.left = 0.0f; + rect.right = SCREEN_WIDTH; + if(m_WideScreenOn +#ifdef CUTSCENE_BORDERS_SWITCH + && CMenuManager::m_PrefsCutsceneBorders +#endif + ){ + float borderSize = (SCREEN_HEIGHT / 2) * (m_ScreenReductionPercentage / 100.f); + rect.top = borderSize - SCREEN_SCALE_Y(22.f); + rect.bottom = SCREEN_HEIGHT - borderSize - SCREEN_SCALE_Y(14.f); + }else{ + rect.top = 0.0f; + rect.bottom = SCREEN_HEIGHT; + } +} + + +void +CCamera::TakeControl(CEntity *target, int16 mode, int16 typeOfSwitch, int32 controller) +{ + bool doSwitch = true; + if(controller == CAMCONTROL_OBBE && WhoIsInControlOfTheCamera == CAMCONTROL_SCRIPT) + doSwitch = false; + if(doSwitch){ + WhoIsInControlOfTheCamera = controller; + if(target){ + if(mode == CCam::MODE_NONE){ + // Why are we checking the old entity? + if(pTargetEntity->IsPed()) + mode = CCam::MODE_FOLLOWPED; + else if(pTargetEntity->IsVehicle()) + mode = CCam::MODE_CAM_ON_A_STRING; + } + }else if(FindPlayerVehicle()) + target = FindPlayerVehicle(); + else + target = PLAYER; + + m_bLookingAtVector = false; + pTargetEntity = target; + m_iModeToGoTo = mode; + m_iTypeOfSwitch = typeOfSwitch; + m_bLookingAtPlayer = false; + m_bStartInterScript = true; + } +} + +void +CCamera::TakeControlNoEntity(const CVector &position, int16 typeOfSwitch, int32 controller) +{ + bool doSwitch = true; + if(controller == CAMCONTROL_OBBE && WhoIsInControlOfTheCamera == CAMCONTROL_SCRIPT) + doSwitch = false; + if(doSwitch){ + WhoIsInControlOfTheCamera = controller; + m_bLookingAtVector = true; + m_bLookingAtPlayer = false; + m_iModeToGoTo = CCam::MODE_FIXED; + m_vecFixedModeVector = position; + m_iTypeOfSwitch = typeOfSwitch; + m_bStartInterScript = true; + } +} + +void +CCamera::TakeControlWithSpline(int16 typeOfSwitch) +{ + m_iModeToGoTo = CCam::MODE_FLYBY; + m_bLookingAtPlayer = false; + m_bLookingAtVector = false; + m_bcutsceneFinished = false; + m_iTypeOfSwitch = typeOfSwitch; + m_bStartInterScript = true; +}; + +void +CCamera::Restore(void) +{ + m_bLookingAtPlayer = true; + m_bLookingAtVector = false; + m_iTypeOfSwitch = INTERPOLATION; + m_bUseNearClipScript = false; + m_iModeObbeCamIsInForCar = OBBE_INVALID; + m_fPositionAlongSpline = 0.0; + m_bStartingSpline = false; + m_bScriptParametersSetForInterPol = false; + WhoIsInControlOfTheCamera = CAMCONTROL_GAME; + + if(FindPlayerVehicle()){ + m_iModeToGoTo = CCam::MODE_CAM_ON_A_STRING; + pTargetEntity = FindPlayerVehicle(); + }else{ + m_iModeToGoTo = CCam::MODE_FOLLOWPED; + pTargetEntity = PLAYER; + } + + if(PLAYER->GetPedState() == PED_ENTER_CAR || + PLAYER->GetPedState() == PED_CARJACK || + PLAYER->GetPedState() == PED_OPEN_DOOR){ + m_iModeToGoTo = CCam::MODE_CAM_ON_A_STRING; + pTargetEntity = PLAYER->m_pSeekTarget; + } + if(PLAYER->GetPedState() == PED_EXIT_CAR){ + m_iModeToGoTo = CCam::MODE_FOLLOWPED; + pTargetEntity = PLAYER; + } + + m_bEnable1rstPersonCamCntrlsScript = false; + m_bAllow1rstPersonWeaponsCamera = false; + m_bUseScriptZoomValuePed = false; + m_bUseScriptZoomValueCar = false; + m_bStartInterScript = true; + m_bCameraJustRestored = true; + m_fAvoidTheGeometryProbsTimer = 0.0f; +} + +void +CCamera::RestoreWithJumpCut(void) +{ + m_bRestoreByJumpCut = true; + m_bLookingAtPlayer = true; + m_bLookingAtVector = false; + m_iTypeOfSwitch = JUMP_CUT; + m_bUseNearClipScript = false; + m_iModeObbeCamIsInForCar = OBBE_INVALID; + m_fPositionAlongSpline = 0.0; + m_bStartingSpline = false; + m_bScriptParametersSetForInterPol = false; + WhoIsInControlOfTheCamera = CAMCONTROL_GAME; + m_bCameraJustRestored = true; + m_bEnable1rstPersonCamCntrlsScript = false; + m_bAllow1rstPersonWeaponsCamera = false; + + if(FindPlayerVehicle()){ + m_iModeToGoTo = CCam::MODE_CAM_ON_A_STRING; + pTargetEntity = FindPlayerVehicle(); + }else{ + m_iModeToGoTo = CCam::MODE_FOLLOWPED; + pTargetEntity = PLAYER; + } + + if(PLAYER->GetPedState() == PED_ENTER_CAR || + PLAYER->GetPedState() == PED_CARJACK || + PLAYER->GetPedState() == PED_OPEN_DOOR){ + m_iModeToGoTo = CCam::MODE_CAM_ON_A_STRING; + pTargetEntity = PLAYER->m_pSeekTarget; + } + if(PLAYER->GetPedState() == PED_EXIT_CAR){ + m_iModeToGoTo = CCam::MODE_FOLLOWPED; + pTargetEntity = PLAYER; + } + + m_bUseScriptZoomValuePed = false; + m_bUseScriptZoomValueCar = false; +} + +void +CCamera::SetCamPositionForFixedMode(const CVector &Source, const CVector &UpOffSet) +{ + m_vecFixedModeSource = Source; + m_vecFixedModeUpOffSet = UpOffSet; + m_bGarageFixedCamPositionSet = false; +} + + +void +CCamera::StartTransition(int16 newMode) +{ + bool switchFromFixedSyphon = false; + bool switchSyphonMode = false; + bool switchPedMode = false; + bool switchPedToCar = false; + bool switchFromFight = false; + bool switchBikeToPed = false; + bool switchFromFixed = false; + bool switch1stPersonToVehicle = false; + float betaOffset, targetBeta, camBeta, deltaBeta; + int door; + bool vehicleVertical; + + m_bItsOkToLookJustAtThePlayer = false; + m_fFractionInterToStopMoving = 0.25f; + m_fFractionInterToStopCatchUp = 0.75f; + + if(Cams[ActiveCam].Mode == CCam::MODE_SYPHON_CRIM_IN_FRONT || + Cams[ActiveCam].Mode == CCam::MODE_FOLLOWPED || + Cams[ActiveCam].Mode == CCam::MODE_SYPHON || + Cams[ActiveCam].Mode == CCam::MODE_SPECIAL_FIXED_FOR_SYPHON){ + if(newMode == CCam::MODE_SYPHON_CRIM_IN_FRONT || + newMode == CCam::MODE_FOLLOWPED || + newMode == CCam::MODE_SYPHON || + newMode == CCam::MODE_SPECIAL_FIXED_FOR_SYPHON) + switchPedMode = true; + if(newMode == CCam::MODE_CAM_ON_A_STRING) + switchPedToCar = true; + } + + if(Cams[ActiveCam].Mode == CCam::MODE_SPECIAL_FIXED_FOR_SYPHON) + switchFromFixedSyphon = true; + if(Cams[ActiveCam].Mode == CCam::MODE_CAM_ON_A_STRING && newMode == CCam::MODE_FOLLOWPED && m_bPlayerWasOnBike) + switchBikeToPed = true; + if(Cams[ActiveCam].Mode == CCam::MODE_SYPHON_CRIM_IN_FRONT && newMode == CCam::MODE_SYPHON) + switchSyphonMode = true; + if(Cams[ActiveCam].Mode == CCam::MODE_FIGHT_CAM && newMode == CCam::MODE_FOLLOWPED) + switchFromFight = true; + if(Cams[ActiveCam].Mode == CCam::MODE_FIXED) + switchFromFixed = true; + + m_bUseTransitionBeta = false; + + if((Cams[ActiveCam].Mode == CCam::MODE_SNIPER || + Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER || + Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON || + Cams[ActiveCam].Mode == CCam::MODE_SNIPER_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_FIGHT_CAM_RUNABOUT || + Cams[ActiveCam].Mode == CCam::MODE_HELICANNON_1STPERSON || + Cams[ActiveCam].Mode == CCam::MODE_CAMERA || + Cams[ActiveCam].Mode == CCam::MODE_1STPERSON_RUNABOUT) && + pTargetEntity->IsPed()){ + float angle = CGeneral::GetATanOfXY(Cams[ActiveCam].Front.x, Cams[ActiveCam].Front.y) - HALFPI; + ((CPed*)pTargetEntity)->m_fRotationCur = angle; + ((CPed*)pTargetEntity)->m_fRotationDest = angle; + } + + Cams[ActiveCam].m_cvecCamFixedModeVector = m_vecFixedModeVector; + Cams[ActiveCam].CamTargetEntity = pTargetEntity; + Cams[ActiveCam].m_cvecCamFixedModeSource = m_vecFixedModeSource; + Cams[ActiveCam].m_cvecCamFixedModeUpOffSet = m_vecFixedModeUpOffSet; + Cams[ActiveCam].m_bCamLookingAtVector = m_bLookingAtVector; + + if(newMode == CCam::MODE_SNIPER || + newMode == CCam::MODE_ROCKETLAUNCHER || + newMode == CCam::MODE_M16_1STPERSON || + newMode == CCam::MODE_SNIPER_RUNABOUT || + newMode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || + newMode == CCam::MODE_1STPERSON_RUNABOUT || + newMode == CCam::MODE_M16_1STPERSON_RUNABOUT || + newMode == CCam::MODE_FIGHT_CAM_RUNABOUT || + newMode == CCam::MODE_HELICANNON_1STPERSON || + newMode == CCam::MODE_CAMERA) + Cams[ActiveCam].Alpha = 0.0f; + + switch(Cams[ActiveCam].Mode) + case CCam::MODE_SNIPER_RUNABOUT: + case CCam::MODE_ROCKETLAUNCHER_RUNABOUT: + case CCam::MODE_1STPERSON_RUNABOUT: + case CCam::MODE_M16_1STPERSON_RUNABOUT: + case CCam::MODE_FIGHT_CAM_RUNABOUT: + case CCam::MODE_CAMERA: + if(newMode == CCam::MODE_CAM_ON_A_STRING || newMode == CCam::MODE_BEHINDBOAT) + switch1stPersonToVehicle = true; + + switch(newMode){ + case CCam::MODE_BEHINDCAR: + Cams[ActiveCam].BetaSpeed = 0.0f; + break; + + case CCam::MODE_BEHINDBOAT: + Cams[ActiveCam].BetaSpeed = 0.0f; + break; + + case CCam::MODE_FOLLOWPED: + // Getting out of vehicle normally + betaOffset = DEGTORAD(55.0f); + if(m_bJustCameOutOfGarage){ + m_bUseTransitionBeta = true; + if(Cams[ActiveCam].Front.x != 0.0f || Cams[ActiveCam].Front.y != 0.0f) + Cams[ActiveCam].m_fTransitionBeta = CGeneral::GetATanOfXY(Cams[ActiveCam].Front.x, Cams[ActiveCam].Front.y) + PI; + else + Cams[ActiveCam].m_fTransitionBeta = 0.0f; + } + if(m_bTargetJustCameOffTrain) + m_bCamDirectlyInFront = true; + if(Cams[ActiveCam].Mode != CCam::MODE_CAM_ON_A_STRING) + break; + m_bUseTransitionBeta = true; + vehicleVertical = false; + if(((CPed*)pTargetEntity)->m_carInObjective && + ((CPed*)pTargetEntity)->m_carInObjective->GetForward().x == 0.0f && + ((CPed*)pTargetEntity)->m_carInObjective->GetForward().y == 0.0f) + vehicleVertical = true; + if(vehicleVertical){ + Cams[ActiveCam].m_fTransitionBeta = 0.0f; + break; + } + camBeta = CGeneral::GetATanOfXY(Cams[ActiveCam].Front.x, Cams[ActiveCam].Front.y); + if(((CPed*)pTargetEntity)->m_carInObjective) + targetBeta = CGeneral::GetATanOfXY(((CPed*)pTargetEntity)->m_carInObjective->GetForward().x, ((CPed*)pTargetEntity)->m_carInObjective->GetForward().y); + else + targetBeta = camBeta; + deltaBeta = targetBeta - camBeta; + while(deltaBeta >= PI) deltaBeta -= 2*PI; + while(deltaBeta < -PI) deltaBeta += 2*PI; + deltaBeta = Abs(deltaBeta); + + door = FindPlayerPed()->m_vehDoor; + if(deltaBeta > HALFPI){ + if(((CPed*)pTargetEntity)->m_carInObjective){ + if(((CPed*)pTargetEntity)->m_carInObjective->IsUpsideDown()){ + if(door == CAR_DOOR_LF || door == CAR_DOOR_LR) + betaOffset = -DEGTORAD(95.0f); + }else{ + if(door == CAR_DOOR_RF || door == CAR_DOOR_RR) + betaOffset = -DEGTORAD(95.0f); + } + } + Cams[ActiveCam].m_fTransitionBeta = targetBeta + betaOffset; + }else{ + if(((CPed*)pTargetEntity)->m_carInObjective){ + if(((CPed*)pTargetEntity)->m_carInObjective->IsUpsideDown()){ + if(door == CAR_DOOR_RF || door == CAR_DOOR_RR) + betaOffset = -DEGTORAD(55.0f); + else if(door == CAR_DOOR_LF || door == CAR_DOOR_LR) + betaOffset = DEGTORAD(95.0f); + }else{ + if(door == CAR_DOOR_LF || door == CAR_DOOR_LR) + betaOffset = -DEGTORAD(55.0f); + else if(door == CAR_DOOR_RF || door == CAR_DOOR_RR) + betaOffset = DEGTORAD(95.0f); + } + } + Cams[ActiveCam].m_fTransitionBeta = targetBeta + betaOffset + PI; + } + break; + + case CCam::MODE_SNIPER: + case CCam::MODE_ROCKETLAUNCHER: + case CCam::MODE_M16_1STPERSON: + case CCam::MODE_SNIPER_RUNABOUT: + case CCam::MODE_ROCKETLAUNCHER_RUNABOUT: + case CCam::MODE_1STPERSON_RUNABOUT: + case CCam::MODE_M16_1STPERSON_RUNABOUT: + case CCam::MODE_FIGHT_CAM_RUNABOUT: + case CCam::MODE_HELICANNON_1STPERSON: + case CCam::MODE_CAMERA: + if(FindPlayerVehicle()) + Cams[ActiveCam].Beta = Atan2(FindPlayerVehicle()->GetForward().x, FindPlayerVehicle()->GetForward().y); + else + Cams[ActiveCam].Beta = Atan2(PLAYER->GetForward().x, PLAYER->GetForward().y); + break; + + case CCam::MODE_SYPHON: + Cams[ActiveCam].Alpha = 0.0f; + Cams[ActiveCam].AlphaSpeed = 0.0f; + break; + + case CCam::MODE_CAM_ON_A_STRING: + // Get into vehicle + betaOffset = DEGTORAD(57.0f); + if(!m_bLookingAtPlayer || m_bJustCameOutOfGarage) + break; + m_bUseTransitionBeta = true; + Cams[ActiveCam].m_fTransitionBeta = CGeneral::GetATanOfXY(Cams[ActiveCam].Front.x, Cams[ActiveCam].Front.y); + break; + + case CCam::MODE_PED_DEAD_BABY: + Cams[ActiveCam].Alpha = DEGTORAD(15.0f); + break; + + case CCam::MODE_FIGHT_CAM: + Cams[ActiveCam].Beta = 0.0f; + Cams[ActiveCam].BetaSpeed = 0.0f; + Cams[ActiveCam].Alpha = 0.0f; + Cams[ActiveCam].AlphaSpeed = 0.0f; + break; + } + + Cams[ActiveCam].Init(); + Cams[ActiveCam].Mode = newMode; + + m_uiTransitionDuration = 1350; + if(switchSyphonMode) + m_uiTransitionDuration = 1800; + else if(switchFromFight) + m_uiTransitionDuration = 750; + else if(switchPedToCar){ + m_fFractionInterToStopMoving = 0.1f; + m_fFractionInterToStopCatchUp = 0.9f; + m_uiTransitionDuration = 750; + }else if(switchFromFixedSyphon){ + m_fFractionInterToStopMoving = 0.0f; + m_fFractionInterToStopCatchUp = 1.0f; + m_uiTransitionDuration = 600; + }else if(switchFromFixed){ + m_fFractionInterToStopMoving = 0.05f; + m_fFractionInterToStopCatchUp = 0.95f; + }else if(switchBikeToPed){ + m_uiTransitionDuration = 800; + }else if(switch1stPersonToVehicle){ + m_fFractionInterToStopMoving = 0.0f; + m_fFractionInterToStopCatchUp = 1.0f; + m_uiTransitionDuration = 1; + }else if(switchPedMode){ + m_fFractionInterToStopMoving = 0.5f; + m_fFractionInterToStopCatchUp = 0.5f; + m_uiTransitionDuration = 350; + }else + m_uiTransitionDuration = 1350; // already set above + m_uiTransitionState = 1; + m_uiTimeTransitionStart = CTimer::GetTimeInMilliseconds(); + m_uiTransitionJUSTStarted = 1; + if(m_vecDoingSpecialInterPolation){ + m_cvecStartingSourceForInterPol = SourceDuringInter; + m_cvecStartingTargetForInterPol = TargetDuringInter; + m_cvecStartingUpForInterPol = UpDuringInter; + m_fStartingAlphaForInterPol = m_fAlphaDuringInterPol; + m_fStartingBetaForInterPol = m_fBetaDuringInterPol; + }else{ + m_cvecStartingSourceForInterPol = Cams[ActiveCam].Source; + m_cvecStartingTargetForInterPol = Cams[ActiveCam].m_cvecTargetCoorsForFudgeInter; + m_cvecStartingUpForInterPol = Cams[ActiveCam].Up; + m_fStartingAlphaForInterPol = Cams[ActiveCam].m_fTrueAlpha; + m_fStartingBetaForInterPol = Cams[ActiveCam].m_fTrueBeta; + } + Cams[ActiveCam].m_bCamLookingAtVector = m_bLookingAtVector; + Cams[ActiveCam].m_cvecCamFixedModeVector = m_vecFixedModeVector; + Cams[ActiveCam].m_cvecCamFixedModeSource = m_vecFixedModeSource; + Cams[ActiveCam].m_cvecCamFixedModeUpOffSet = m_vecFixedModeUpOffSet; + Cams[ActiveCam].Mode = newMode; // already done above + Cams[ActiveCam].CamTargetEntity = pTargetEntity; + m_uiTransitionState = 1; // these three already done above + m_uiTimeTransitionStart = CTimer::GetTimeInMilliseconds(); + m_uiTransitionJUSTStarted = 1; + m_fStartingFOVForInterPol = Cams[ActiveCam].FOV; + m_cvecSourceSpeedAtStartInter = Cams[ActiveCam].m_cvecSourceSpeedOverOneFrame; + m_cvecTargetSpeedAtStartInter = Cams[ActiveCam].m_cvecTargetSpeedOverOneFrame; + m_cvecUpSpeedAtStartInter = Cams[ActiveCam].m_cvecUpOverOneFrame; + m_fAlphaSpeedAtStartInter = Cams[ActiveCam].m_fAlphaSpeedOverOneFrame; + m_fBetaSpeedAtStartInter = Cams[ActiveCam].m_fBetaSpeedOverOneFrame; + m_fFOVSpeedAtStartInter = Cams[ActiveCam].m_fFovSpeedOverOneFrame; + Cams[ActiveCam].ResetStatics = true; + if(m_bLookingAtPlayer){ + if(switchPedMode) + m_uiTransitionDurationTargetCoors = 350; + else + m_uiTransitionDurationTargetCoors = 600; + m_fFractionInterToStopMovingTarget = 0.0f; + m_fFractionInterToStopCatchUpTarget = 1.0f; + }else{ + if(m_bScriptParametersSetForInterPol){ + m_fFractionInterToStopMoving = m_fScriptPercentageInterToStopMoving; + m_fFractionInterToStopCatchUp = m_fScriptPercentageInterToCatchUp; + m_uiTransitionDuration = m_fScriptTimeForInterPolation; + } + m_uiTransitionDurationTargetCoors = m_uiTransitionDuration; + m_fFractionInterToStopMovingTarget = m_fFractionInterToStopMoving; + m_fFractionInterToStopCatchUpTarget = m_fFractionInterToStopCatchUp; + } +} + +void +CCamera::StartTransitionWhenNotFinishedInter(int16 mode) +{ + m_vecDoingSpecialInterPolation = true; + StartTransition(mode); +} + +void +CCamera::StoreValuesDuringInterPol(CVector &source, CVector &target, CVector &up, float &FOV) +{ + SourceDuringInter = source; + TargetDuringInter = target; + UpDuringInter = up; + FOVDuringInter = FOV; + CVector Dist = source - TargetDuringInter; + float DistOnGround = Dist.Magnitude2D(); + m_fBetaDuringInterPol = CGeneral::GetATanOfXY(Dist.x, Dist.y); + m_fAlphaDuringInterPol = CGeneral::GetATanOfXY(DistOnGround, Dist.z); +} + + + +void +CCamera::SetWideScreenOn(void) +{ + m_WideScreenOn = true; +} + +void +CCamera::SetWideScreenOff(void) +{ + m_bWantsToSwitchWidescreenOff = m_WideScreenOn; +} + +void +CCamera::ProcessWideScreenOn(void) +{ + if(m_bWantsToSwitchWidescreenOff){ + m_bWantsToSwitchWidescreenOff = false; + m_WideScreenOn = false; + m_ScreenReductionPercentage = 0.0f; + m_fFOV_Wide_Screen = 0.0f; + m_fWideScreenReductionAmount = 0.0f; + }else{ + m_fFOV_Wide_Screen = 0.3f*Cams[ActiveCam].FOV; + m_fWideScreenReductionAmount = 1.0f; + m_ScreenReductionPercentage = 30.0f; + } +} + +void +CCamera::DrawBordersForWideScreen(void) +{ + float bottom, top; + if (m_WideScreenOn) { + float borderSize = (SCREEN_HEIGHT / 2) * (m_ScreenReductionPercentage / 100.f); + top = borderSize - SCREEN_SCALE_Y(22.f); + bottom = SCREEN_HEIGHT - borderSize - SCREEN_SCALE_Y(14.f); + } else { + top = 0.f; + bottom = SCREEN_HEIGHT; + } + + if(m_BlurType == MOTION_BLUR_NONE || m_BlurType == MOTION_BLUR_LIGHT_SCENE) + SetMotionBlurAlpha(80); + + // top border + CSprite2d::DrawRect(CRect(0.0f, 0.0f, SCREEN_WIDTH, top), CRGBA(0, 0, 0, 255)); + + // bottom border + CSprite2d::DrawRect(CRect(0.0f, bottom, SCREEN_WIDTH, SCREEN_HEIGHT), CRGBA(0, 0, 0, 255)); +} + + + +bool +CCamera::IsItTimeForNewcam(int32 obbeMode, int32 time) +{ + CVehicle *veh; + uint32 t = time; // no annoying compiler warnings + CVector fwd; + + if(obbeMode < 0) + return true; + switch(obbeMode){ + case OBBE_WHEEL: + veh = FindPlayerVehicle(); + if(veh){ + if(veh->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) + return true; + if(veh->GetModelIndex() == MI_RHINO) + return true; + if(!CWorld::GetIsLineOfSightClear(pTargetEntity->GetPosition(), Cams[ActiveCam].Source, true, false, false, false, false, false, false)) + return true; + } + if(CTimer::GetTimeInMilliseconds() > t+5000) + return true; + SetNearClipScript(0.6f); + return false; + case OBBE_1: + if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) + return true; + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) + return true; + + fwd = FindPlayerCoors() - m_vecFixedModeSource; + fwd.z = 0.0f; + + // too far and driving away from cam + if(fwd.Magnitude() > 40.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) + return true; + // too close + if(fwd.Magnitude() < 1.6f) + return true; + return false; + case OBBE_2: + if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) + return true; + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) + return true; + + fwd = FindPlayerCoors() - m_vecFixedModeSource; + fwd.z = 0.0f; + + if(fwd.Magnitude() < 2.0f) + // very close, fix near clip + SetNearClipScript(Max(fwd.Magnitude()*0.5f, 0.05f)); + // too far and driving away from cam + if(fwd.Magnitude() > 29.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) + return true; + // too close + if(fwd.Magnitude() < 2.0f) + return true; + return false; + case OBBE_3: + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) + return true; + + fwd = FindPlayerCoors() - m_vecFixedModeSource; + fwd.z = 0.0f; + + // too far and driving away from cam + if(fwd.Magnitude() > 48.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) + return true; + return false; + case OBBE_1STPERSON: + return CTimer::GetTimeInMilliseconds() > t+3000; + case OBBE_5: + if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) + return true; + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) + return true; + + fwd = FindPlayerCoors() - m_vecFixedModeSource; + fwd.z = 0.0f; + + // too far and driving away from cam + if(fwd.Magnitude() > 38.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) + return true; + return false; + case OBBE_ONSTRING: + return CTimer::GetTimeInMilliseconds() > t+3000; + case OBBE_COPCAR: + return CTimer::GetTimeInMilliseconds() > t+2000 && !FindPlayerVehicle()->GetIsOnScreen(); + case OBBE_COPCAR_WHEEL: + if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat()) + return true; + if(!CWorld::GetIsLineOfSightClear(pTargetEntity->GetPosition(), Cams[ActiveCam].Source, true, false, false, false, false, false, false)) + return true; + if(CTimer::GetTimeInMilliseconds() > t+1000) + return true; + SetNearClipScript(0.6f); + return false; + + // Ped modes + case OBBE_9: + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) + return true; + + fwd = FindPlayerCoors() - m_vecFixedModeSource; + fwd.z = 0.0f; + + // too far and driving away from cam + if(fwd.Magnitude() > 20.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) + return true; + return false; + case OBBE_10: + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) + return true; + + fwd = FindPlayerCoors() - m_vecFixedModeSource; + fwd.z = 0.0f; + + // too far and driving away from cam + if(fwd.Magnitude() > 8.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) + return true; + return false; + case OBBE_11: + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) + return true; + + fwd = FindPlayerCoors() - m_vecFixedModeSource; + fwd.z = 0.0f; + + // too far and driving away from cam + if(fwd.Magnitude() > 25.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) + return true; + return false; + case OBBE_12: + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) + return true; + + fwd = FindPlayerCoors() - m_vecFixedModeSource; + fwd.z = 0.0f; + + // too far and driving away from cam + if(fwd.Magnitude() > 8.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) + return true; + return false; + case OBBE_13: + return CTimer::GetTimeInMilliseconds() > t+5000; + + // Heli modes + case OBBE_14: + if(FindPlayerVehicle()) + if(!CWorld::GetIsLineOfSightClear(pTargetEntity->GetPosition(), Cams[ActiveCam].Source, true, false, false, false, false, false, false)) + return true; + return CTimer::GetTimeInMilliseconds() > t+8000; + case OBBE_15: + if(FindPlayerVehicle()){ + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) + return true; + fwd = FindPlayerCoors() - m_vecFixedModeSource; + fwd.z = 0.0f; + + // too far and driving away from cam + if(fwd.Magnitude() > 44.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) + return true; + // too close + if(fwd.Magnitude() < 2.0f) + return true; + } + return false; + case OBBE_16: + if(FindPlayerVehicle()){ + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) + return true; + fwd = FindPlayerCoors() - m_vecFixedModeSource; + fwd.z = 0.0f; + + // too far and driving away from cam + if(fwd.Magnitude() > 50.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) + return true; + // too close + if(fwd.Magnitude() < 3.0f) + return true; + } + return false; + case OBBE_17: + if(FindPlayerVehicle()){ + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) + return true; + fwd = FindPlayerCoors() - m_vecFixedModeSource; + fwd.z = 0.0f; + + // too far + if(fwd.Magnitude() > 50.0f) + return true; + // too close + if(fwd.Magnitude() < 2.0f) + return true; + } + return false; + case OBBE_18: + if(FindPlayerVehicle()){ + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) + return true; + fwd = FindPlayerCoors() - m_vecFixedModeSource; + + // too far + if(fwd.Magnitude() > 57.0f) + return true; + // too close + if(fwd.Magnitude() < 1.0f) + return true; + } + return false; + case OBBE_19: + if(FindPlayerVehicle()){ + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) + return true; + fwd = FindPlayerCoors() - m_vecFixedModeSource; + fwd.z = 0.0f; + + // too far + if(fwd.Magnitude() > 36.0f) + return true; + // too close + if(fwd.Magnitude() < 2.0f) + return true; + } + return false; + case OBBE_ONSTRING_HELI: + return CTimer::GetTimeInMilliseconds() > t+5000; + + default: + return false; + } +} + +bool +CCamera::TryToStartNewCamMode(int32 obbeMode) +{ + CVehicle *veh; + CVector target, camPos, playerSpeed, fwd, fwd2; + float angle; + float ground; + bool foundGround; + int i; + + if(obbeMode < 0) + return true; + switch(obbeMode){ + case OBBE_WHEEL: + veh = FindPlayerVehicle(); + if(veh == nil || (veh->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) || veh->GetModelIndex() == MI_RHINO) + return false; + target = Multiply3x3(FindPlayerVehicle()->GetMatrix(), CVector(-1.4f, -2.3f, 0.3f)); + target += FindPlayerVehicle()->GetPosition(); + if(!CWorld::GetIsLineOfSightClear(veh->GetPosition(), target, true, false, false, false, false, false, false)) + return false; + TakeControl(veh, CCam::MODE_WHEELCAM, JUMP_CUT, CAMCONTROL_OBBE); + return true; + case OBBE_1: + camPos = FindPlayerCoors(); + playerSpeed = FindPlayerSpeed(); + playerSpeed.z = 0.0f; + playerSpeed.Normalise(); + camPos += 20.0f*playerSpeed; + camPos += 3.0f*CVector(playerSpeed.y, -playerSpeed.x, 0.0f); + if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) + return false; + + ground = CWorld::FindGroundZFor3DCoord(camPos.x, camPos.y, camPos.z+5.0f, &foundGround); + if(foundGround) + camPos.z = ground + 1.5f; + else{ + ground = CWorld::FindRoofZFor3DCoord(camPos.x, camPos.y, camPos.z-5.0f, &foundGround); + if(foundGround) + camPos.z = ground + 1.5f; + } + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) + return false; + + fwd = FindPlayerCoors() - camPos; + fwd.z = 0.0f; + // too far and driving away from cam + if(fwd.Magnitude() > 40.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) + return false; + // too close + if(fwd.Magnitude() < 2.5f) + return true; + + SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); + TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); + return true; + case OBBE_2: + if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) + return false; + camPos = FindPlayerCoors(); + playerSpeed = FindPlayerSpeed(); + playerSpeed.z = 0.0f; + playerSpeed.Normalise(); + camPos += 16.0f*playerSpeed; + camPos += 2.5f*CVector(playerSpeed.y, -playerSpeed.x, 0.0f); + + ground = CWorld::FindGroundZFor3DCoord(camPos.x, camPos.y, camPos.z+5.0f, &foundGround); + if(foundGround) + camPos.z = ground + 0.5f; + else{ + ground = CWorld::FindRoofZFor3DCoord(camPos.x, camPos.y, camPos.z-5.0f, &foundGround); + if(foundGround) + camPos.z = ground + 0.5f; + } + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) + return false; + + fwd = FindPlayerCoors() - camPos; + fwd.z = 0.0f; + // too far and driving away from cam + if(fwd.Magnitude() > 29.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) + return false; + // too close + if(fwd.Magnitude() < 2.0f) + return true; + + SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); + TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); + return true; + case OBBE_3: + camPos = FindPlayerCoors(); + playerSpeed = FindPlayerSpeed(); + playerSpeed.z = 0.0f; + playerSpeed.Normalise(); + camPos += 30.0f*playerSpeed; + camPos += 8.0f*CVector(playerSpeed.y, -playerSpeed.x, 0.0f); + + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) + return false; + + SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); + TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); + return true; + case OBBE_1STPERSON: + TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); + return true; + case OBBE_5: + camPos = FindPlayerCoors(); + playerSpeed = FindPlayerSpeed(); + playerSpeed.z = 0.0f; + playerSpeed.Normalise(); + camPos += 30.0f*playerSpeed; + camPos += 6.0f*CVector(playerSpeed.y, -playerSpeed.x, 0.0f); + + ground = CWorld::FindGroundZFor3DCoord(camPos.x, camPos.y, camPos.z+5.0f, &foundGround); + if(foundGround) + camPos.z = ground + 3.5f; + else{ + ground = CWorld::FindRoofZFor3DCoord(camPos.x, camPos.y, camPos.z-5.0f, &foundGround); + if(foundGround) + camPos.z = ground + 3.5f; + } + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) + return false; + + SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); + TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); + return true; + case OBBE_ONSTRING: + TakeControl(FindPlayerEntity(), CCam::MODE_CAM_ON_A_STRING, JUMP_CUT, CAMCONTROL_OBBE); + return true; + case OBBE_COPCAR: +#ifdef FIX_BUGS + if (CReplay::IsPlayingBack()) + return false; +#endif + if(FindPlayerPed()->m_pWanted->GetWantedLevel() < 1) + return false; + if(FindPlayerVehicle() == nil) + return false; + if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) + return false; + i = CPools::GetVehiclePool()->GetSize(); + while(--i >= 0){ + veh = CPools::GetVehiclePool()->GetSlot(i); + if(veh && veh->IsCar() && veh != FindPlayerVehicle() && veh->bIsLawEnforcer){ + float dx = veh->GetPosition().x - FindPlayerCoors().x; + float dy = veh->GetPosition().y - FindPlayerCoors().y; + float dist = (veh->GetPosition() - FindPlayerCoors()).Magnitude(); + if(dist < 30.0f){ + if(dx*FindPlayerVehicle()->GetForward().x + dy*FindPlayerVehicle()->GetForward().y < 0.0f && + veh->GetForward().x*FindPlayerVehicle()->GetForward().x + veh->GetForward().y*FindPlayerVehicle()->GetForward().y > 0.8f){ + TakeControl(veh, CCam::MODE_CAM_ON_A_STRING, JUMP_CUT, CAMCONTROL_OBBE); + return true; + } + } + } + } + return false; + case OBBE_COPCAR_WHEEL: +#ifdef FIX_BUGS + if (CReplay::IsPlayingBack()) + return false; +#endif + if(FindPlayerPed()->m_pWanted->GetWantedLevel() < 1) + return false; + if(FindPlayerVehicle() == nil) + return false; + if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) + return false; + i = CPools::GetVehiclePool()->GetSize(); + while(--i >= 0){ + veh = CPools::GetVehiclePool()->GetSlot(i); + if(veh && veh->IsCar() && veh != FindPlayerVehicle() && veh->bIsLawEnforcer){ + float dx = veh->GetPosition().x - FindPlayerCoors().x; + float dy = veh->GetPosition().y - FindPlayerCoors().y; + float dist = (veh->GetPosition() - FindPlayerCoors()).Magnitude(); + if(dist < 30.0f){ + if(dx*FindPlayerVehicle()->GetForward().x + dy*FindPlayerVehicle()->GetForward().y < 0.0f && + veh->GetForward().x*FindPlayerVehicle()->GetForward().x + veh->GetForward().y*FindPlayerVehicle()->GetForward().y > 0.8f){ + target = Multiply3x3(veh->GetMatrix(), CVector(-1.4f, -2.3f, 0.3f)); + target += veh->GetPosition(); + if(!CWorld::GetIsLineOfSightClear(veh->GetPosition(), target, true, false, false, false, false, false, false)) + return false; + TakeControl(veh, CCam::MODE_WHEELCAM, JUMP_CUT, CAMCONTROL_OBBE); + return true; + } + } + } + } + return false; + + case OBBE_9: + camPos = FindPlayerCoors(); + playerSpeed = FindPlayerSpeed(); + playerSpeed.z = 0.0f; + playerSpeed.Normalise(); + camPos += 15.0f*playerSpeed; + camPos += CVector(2.0f, 1.0f, 0.0f); + + ground = CWorld::FindGroundZFor3DCoord(camPos.x, camPos.y, camPos.z+5.0f, &foundGround); + if(foundGround) + camPos.z = ground + 0.5f; + else{ + ground = CWorld::FindRoofZFor3DCoord(camPos.x, camPos.y, camPos.z-5.0f, &foundGround); + if(foundGround) + camPos.z = ground + 0.5f; + } + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) + return false; + + SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); + TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); + return true; + case OBBE_10: + camPos = FindPlayerCoors(); + playerSpeed = FindPlayerSpeed(); + playerSpeed.z = 0.0f; + playerSpeed.Normalise(); + camPos += 5.0f*playerSpeed; + camPos += CVector(2.0f, 1.0f, 0.5f); + + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) + return false; + + SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); + TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); + return true; + case OBBE_11: + camPos = FindPlayerCoors(); + playerSpeed = FindPlayerSpeed(); + playerSpeed.z = 0.0f; + playerSpeed.Normalise(); + camPos += 20.0f*playerSpeed; + camPos += CVector(2.0f, 1.0f, 20.0f); + + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) + return false; + + SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); + TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); + return true; + case OBBE_12: + camPos = FindPlayerCoors(); + playerSpeed = FindPlayerSpeed(); + playerSpeed.z = 0.0f; + playerSpeed.Normalise(); + camPos += 5.0f*playerSpeed; + camPos += CVector(2.0f, 1.0f, 10.5f); + + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) + return false; + + SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); + TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); + return true; + case OBBE_13: +#ifdef FIX_BUGS + TakeControl(FindPlayerEntity(), CCam::MODE_TOP_DOWN_PED, JUMP_CUT, CAMCONTROL_OBBE); +#else + TakeControl(FindPlayerEntity(), CCam::MODE_TOPDOWN, JUMP_CUT, CAMCONTROL_OBBE); +#endif + return true; + + // Heli modes + case OBBE_14: + veh = FindPlayerVehicle(); + if(veh == nil) + return false; + target = Multiply3x3(FindPlayerVehicle()->GetMatrix(), CVector(-1.4f, -2.3f, 0.3f)); + target += FindPlayerVehicle()->GetPosition(); + if(!veh->IsBoat() && !CWorld::GetIsLineOfSightClear(veh->GetPosition(), target, true, false, false, false, false, false, false)) + return false; + TakeControl(veh, CCam::MODE_WHEELCAM, JUMP_CUT, CAMCONTROL_OBBE); + return true; + case OBBE_15: + if(FindPlayerVehicle() == nil) + return false; + camPos = FindPlayerCoors(); + playerSpeed = FindPlayerSpeed(); + playerSpeed.z = 0.0f; + playerSpeed.Normalise(); + camPos += 34.0f*playerSpeed; + camPos.z = FindPlayerCoors().z + 0.5f; + if(FindPlayerVehicle()->IsBoat()) + camPos.z += 1.0f; + + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) + return false; + + fwd = FindPlayerCoors() - camPos; + fwd2 = FindPlayerCoors() - camPos; + fwd2.z = 0.0f; + // too far and driving away from cam + if(fwd.Magnitude() > 44.0f && DotProduct(FindPlayerSpeed(), fwd2) > 0.0f) + return false; + // too close + if(fwd.Magnitude() < 3.0f) + return true; + + SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); + TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); + return true; + case OBBE_16: + if(FindPlayerVehicle() == nil) + return false; + camPos = FindPlayerCoors(); + playerSpeed = FindPlayerSpeed(); + playerSpeed.z = 0.0f; + playerSpeed.Normalise(); + angle = CGeneral::GetATanOfXY(playerSpeed.x, playerSpeed.y) + DEGTORAD(60.0f); + playerSpeed += CVector(Cos(angle), Sin(angle), 0.0f); + playerSpeed.Normalise(); + camPos += 30.0f*playerSpeed; + camPos.z = FindPlayerCoors().z - 5.5f; + + foundGround = false; + ground = CWorld::FindRoofZFor3DCoord(camPos.x, camPos.y, camPos.z+5.0f, &foundGround); + if(foundGround) + camPos.z = ground + 0.5f; + else if(CWaterLevel::GetWaterLevelNoWaves(camPos.x, camPos.y, camPos.z, &ground)){ + float waterOffset = 1.0f; + if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat()) + waterOffset = -2.0f; + if(camPos.z < ground + waterOffset) + camPos.z = ground + waterOffset; + } + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) + return false; + + fwd = FindPlayerCoors() - camPos; + // too far + if(fwd.Magnitude() > 50.0f) + return false; + // too close + if(fwd.Magnitude() < 3.0f) + return true; + + SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); + TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); + return true; + case OBBE_17: + if(FindPlayerVehicle() == nil) + return false; + camPos = FindPlayerCoors(); + playerSpeed = FindPlayerSpeed(); + playerSpeed.z = 0.0f; + playerSpeed.Normalise(); + angle = CGeneral::GetATanOfXY(playerSpeed.x, playerSpeed.y) + DEGTORAD(190.0f); + playerSpeed += CVector(Cos(angle), Sin(angle), 0.0f); + playerSpeed.Normalise(); + camPos += 25.0f*playerSpeed; + camPos.z = FindPlayerCoors().z - 1.0f; + + foundGround = false; + ground = CWorld::FindRoofZFor3DCoord(camPos.x, camPos.y, camPos.z+5.0f, &foundGround); + if(foundGround) + camPos.z = ground + 0.5f; + else if(CWaterLevel::GetWaterLevelNoWaves(camPos.x, camPos.y, camPos.z, &ground)){ + float waterOffset = 1.0f; + if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat()) + waterOffset = -2.0f; + if(camPos.z < ground + waterOffset) + camPos.z = ground + waterOffset; + } + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) + return false; + + fwd = FindPlayerCoors() - camPos; + fwd2 = FindPlayerCoors() - camPos; + fwd2.z = 0.0f; + // too far and driving away from cam + if(fwd.Magnitude() > 50.0f && DotProduct(FindPlayerSpeed(), fwd2) > 0.0f) + return false; + // too close + if(fwd.Magnitude() < 2.0f) + return true; + + SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); + TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); + return true; + case OBBE_18: + camPos = FindPlayerCoors(); + if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat()) + camPos.z += 23.0f; + else + camPos.z -= 23.0f; + playerSpeed = FindPlayerSpeed(); + angle = CGeneral::GetATanOfXY(playerSpeed.x, playerSpeed.y) + DEGTORAD(145.0f); + playerSpeed += CVector(Cos(angle), Sin(angle), 0.0f); + playerSpeed.Normalise(); + camPos += 15.0f*playerSpeed; + + foundGround = false; + ground = CWorld::FindGroundZFor3DCoord(camPos.x, camPos.y, camPos.z+5.0f, &foundGround); +#ifdef FIX_BUGS + if(foundGround) +#else + if(ground == true) +#endif + { + if(camPos.z < ground) + camPos.z = ground + 0.5f; + }else if(CWaterLevel::GetWaterLevelNoWaves(camPos.x, camPos.y, camPos.z, &ground)){ + float waterOffset = 1.0f; + if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat()) + waterOffset = -2.0f; + if(camPos.z < ground + waterOffset) + camPos.z = ground + waterOffset; + } + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) + return false; + + fwd = FindPlayerCoors() - camPos; + // too far + if(fwd.Magnitude() > 57.0f) + return false; + // too close + if(fwd.Magnitude() < 1.0f) + return true; + + SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); + TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); + return true; + case OBBE_19: + camPos = FindPlayerCoors(); + if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat()) + camPos.z += 4.0f; + else + camPos.z -= 1.0f; + playerSpeed = FindPlayerSpeed(); + angle = CGeneral::GetATanOfXY(playerSpeed.x, playerSpeed.y) + DEGTORAD(28.0f); + playerSpeed += CVector(Cos(angle), Sin(angle), 0.0f); + playerSpeed.Normalise(); + camPos += 12.5f*playerSpeed; + + foundGround = false; + ground = CWorld::FindGroundZFor3DCoord(camPos.x, camPos.y, camPos.z+5.0f, &foundGround); +#ifdef FIX_BUGS + if(foundGround) +#else + if(ground == true) +#endif + { + if(camPos.z < ground) + camPos.z = ground + 0.5f; + }else if(CWaterLevel::GetWaterLevelNoWaves(camPos.x, camPos.y, camPos.z, &ground)){ + float waterOffset = 1.0f; + if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat()) + waterOffset = -2.0f; + if(camPos.z < ground + waterOffset) + camPos.z = ground + waterOffset; + } + if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) + return false; + + fwd = FindPlayerCoors() - camPos; + // too far + if(fwd.Magnitude() > 36.0f) + return false; + // too close + if(fwd.Magnitude() < 2.0f) + return true; + + SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); + TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); + return true; + case OBBE_ONSTRING_HELI: + TakeControl(FindPlayerEntity(), CCam::MODE_CAM_ON_A_STRING, JUMP_CUT, CAMCONTROL_OBBE); + return true; + default: + return false; + } +} + +int32 SequenceOfCarCams[16] = { + OBBE_WHEEL, OBBE_COPCAR, OBBE_3, OBBE_1, OBBE_3, OBBE_COPCAR_WHEEL, + OBBE_2, OBBE_3, OBBE_COPCAR_WHEEL, OBBE_COPCAR, OBBE_2, OBBE_3, + OBBE_5, OBBE_3, + OBBE_ONSTRING // actually unused... +}; + +void +CCamera::ProcessObbeCinemaCameraCar(void) +{ + static int OldMode = -1; + static int32 TimeForNext = 0; + int i = 0; + + if(!bDidWeProcessAnyCinemaCam){ + OldMode = -1; + bSwitchedToObbeCam = true; + } + + if(!bDidWeProcessAnyCinemaCam || IsItTimeForNewcam(SequenceOfCarCams[OldMode], TimeForNext)){ + // This is very strange code... + for(OldMode = (OldMode+1) % 14; + !TryToStartNewCamMode(SequenceOfCarCams[OldMode]) && i <= 14; + OldMode = (OldMode+1) % 14) + i++; + TimeForNext = CTimer::GetTimeInMilliseconds(); + if(i >= 14){ + OldMode = 14; + TryToStartNewCamMode(SequenceOfCarCams[14]); + } + } + + m_iModeObbeCamIsInForCar = OldMode; + bDidWeProcessAnyCinemaCam = true; +} + +int32 SequenceOfHeliCams[6] = { OBBE_14, OBBE_15, OBBE_16, OBBE_17, OBBE_18, OBBE_19 }; + +void +CCamera::ProcessObbeCinemaCameraHeli(void) +{ + static int OldMode = -1; + static int32 TimeForNext = 0; + int i = 0; + + if(!bDidWeProcessAnyCinemaCam){ + OldMode = -1; + bSwitchedToObbeCam = true; + } + + if(!bDidWeProcessAnyCinemaCam || IsItTimeForNewcam(SequenceOfHeliCams[OldMode], TimeForNext)){ + // This is very strange code... + for(OldMode = (OldMode+1) % 6; + !TryToStartNewCamMode(SequenceOfCarCams[OldMode]) && i <= 6; + OldMode = (OldMode+1) % 6) + i++; + if(i >= 6){ + OldMode = 6; + if(Cams[ActiveCam].Mode != CCam::MODE_CAM_ON_A_STRING){ + TryToStartNewCamMode(OBBE_ONSTRING_HELI); + TimeForNext = CTimer::GetTimeInMilliseconds(); + } + }else + TimeForNext = CTimer::GetTimeInMilliseconds(); + } + + m_iModeObbeCamIsInForCar = OldMode; + bDidWeProcessAnyCinemaCam = true; +} + +int32 SequenceOfPedCams[5] = { OBBE_9, OBBE_10, OBBE_11, OBBE_12, OBBE_13 }; + +void +CCamera::ProcessObbeCinemaCameraPed(void) +{ + // static bool bObbePedProcessed = false; // unused + static int PedOldMode = -1; + static int32 PedTimeForNext = 0; + + if(!bDidWeProcessAnyCinemaCam) + PedOldMode = -1; + + if(!bDidWeProcessAnyCinemaCam || IsItTimeForNewcam(SequenceOfPedCams[PedOldMode], PedTimeForNext)){ + for(PedOldMode = (PedOldMode+1) % 5; + !TryToStartNewCamMode(SequenceOfPedCams[PedOldMode]); + PedOldMode = (PedOldMode+1) % 5); + PedTimeForNext = CTimer::GetTimeInMilliseconds(); + } + bDidWeProcessAnyCinemaCam = true; +} + +void +CCamera::DontProcessObbeCinemaCamera(void) +{ + bDidWeProcessAnyCinemaCam = false; +} + +#ifdef GTA_TRAIN +void +CCamera::LoadTrainCamNodes(char const *name) +{ + CFileMgr::SetDir("data"); + + char token[16] = { 0 }; + char filename[16] = { 0 }; + uint8 *buf; + ssize_t bufpos = 0; + int field = 0; + int tokpos = 0; + char c; + int i; + ssize_t len; + + strcpy(filename, name); + len = (int)strlen(filename); + filename[len] = '.'; + filename[len+1] = 'd'; + filename[len+2] = 'a'; + filename[len+3] = 't'; + + m_uiNumberOfTrainCamNodes = 0; + + buf = new uint8[20000]; + len = CFileMgr::LoadFile(filename, buf, 20000, "r"); + + for(i = 0; i < MAX_NUM_OF_NODES; i++){ + m_arrTrainCamNode[i].m_cvecPointToLookAt = CVector(0.0f, 0.0f, 0.0f); + m_arrTrainCamNode[i].m_cvecMinPointInRange = CVector(0.0f, 0.0f, 0.0f); + m_arrTrainCamNode[i].m_cvecMaxPointInRange = CVector(0.0f, 0.0f, 0.0f); + m_arrTrainCamNode[i].m_fDesiredFOV = 0.0f; + m_arrTrainCamNode[i].m_fNearClip = 0.0f; + } + + while(bufpos <= len){ + c = buf[bufpos]; + switch(c){ + case '-': + case '.': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': +// case '10': case '11': case '12': case '13': // ahem... + token[tokpos++] = c; + bufpos++; + break; + + case ',': + case ';': // game has the code for this duplicated but we handle both under the same case + switch((field+14)%14){ + case 0: + m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecCamPosition.x = atof(token); + break; + case 1: + m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecCamPosition.y = atof(token); + break; + case 2: + m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecCamPosition.z = atof(token); + break; + case 3: + m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecPointToLookAt.x = atof(token); + break; + case 4: + m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecPointToLookAt.y = atof(token); + break; + case 5: + m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecPointToLookAt.z = atof(token); + break; + case 6: + m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecMinPointInRange.x = atof(token); + break; + case 7: + m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecMinPointInRange.y = atof(token); + break; + case 8: + m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecMinPointInRange.z = atof(token); + break; + case 9: + m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecMaxPointInRange.x = atof(token); + break; + case 10: + m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecMaxPointInRange.y = atof(token); + break; + case 11: + m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecMaxPointInRange.z = atof(token); + break; + case 12: + m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_fDesiredFOV = atof(token); + break; + case 13: + m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_fNearClip = atof(token); + m_uiNumberOfTrainCamNodes++; + break; + } + field++; + bufpos++; + memset(token, 0, sizeof(token)); + tokpos = 0; + break; + + default: + bufpos++; + break; + } + } + + delete[] buf; + CFileMgr::SetDir(""); +} + +void +CCamera::Process_Train_Camera_Control(void) +{ + bool found = false; + CTrain *target = (CTrain*)pTargetEntity; + m_bUseSpecialFovTrain = true; + static bool OKtoGoBackToNodeCam = true; // only ever set to true + uint32 i; + + if(target->m_nTrackId == TRACK_ELTRAIN && !m_bAboveGroundTrainNodesLoaded){ + m_bAboveGroundTrainNodesLoaded = true; + m_bBelowGroundTrainNodesLoaded = false; + LoadTrainCamNodes("Train"); + m_uiTimeLastChange = CTimer::GetTimeInMilliseconds(); + OKtoGoBackToNodeCam = true; + m_iCurrentTrainCamNode = 0; + } + if(target->m_nTrackId == TRACK_SUBWAY && !m_bBelowGroundTrainNodesLoaded){ + m_bBelowGroundTrainNodesLoaded = true; + m_bAboveGroundTrainNodesLoaded = false; + LoadTrainCamNodes("Train2"); + m_uiTimeLastChange = CTimer::GetTimeInMilliseconds(); + OKtoGoBackToNodeCam = true; + m_iCurrentTrainCamNode = 0; + } + + m_bTargetJustBeenOnTrain = true; + uint32 node = m_iCurrentTrainCamNode; + for(i = 0; i < m_uiNumberOfTrainCamNodes && !found; i++){ + if(target->IsWithinArea(m_arrTrainCamNode[node].m_cvecMinPointInRange.x, + m_arrTrainCamNode[node].m_cvecMinPointInRange.y, + m_arrTrainCamNode[node].m_cvecMinPointInRange.z, + m_arrTrainCamNode[node].m_cvecMaxPointInRange.x, + m_arrTrainCamNode[node].m_cvecMaxPointInRange.y, + m_arrTrainCamNode[node].m_cvecMaxPointInRange.z)){ + m_iCurrentTrainCamNode = node; + found = true; + } + node++; + if(node >= m_uiNumberOfTrainCamNodes) + node = 0; + } +#ifdef FIX_BUGS + // Not really a bug but be nice and respect the debug mode + if(DebugCamMode){ + TakeControl(target, DebugCamMode, JUMP_CUT, CAMCONTROL_SCRIPT); + return; + } +#endif + + if(found){ + SetWideScreenOn(); + if(DotProduct(((CTrain*)pTargetEntity)->GetMoveSpeed(), pTargetEntity->GetForward()) < 0.001f){ + TakeControl(FindPlayerPed(), CCam::MODE_FOLLOWPED, JUMP_CUT, CAMCONTROL_SCRIPT); + if(target->Doors[0].IsFullyOpen()) + SetWideScreenOff(); + }else{ + SetCamPositionForFixedMode(m_arrTrainCamNode[m_iCurrentTrainCamNode].m_cvecCamPosition, CVector(0.0f, 0.0f, 0.0f)); + if(m_arrTrainCamNode[m_iCurrentTrainCamNode].m_cvecPointToLookAt.x == 999.0f && + m_arrTrainCamNode[m_iCurrentTrainCamNode].m_cvecPointToLookAt.y == 999.0f && + m_arrTrainCamNode[m_iCurrentTrainCamNode].m_cvecPointToLookAt.z == 999.0f) + TakeControl(target, CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_SCRIPT); + else + TakeControlNoEntity(m_arrTrainCamNode[m_iCurrentTrainCamNode].m_cvecPointToLookAt, JUMP_CUT, CAMCONTROL_SCRIPT); + RwCameraSetNearClipPlane(Scene.camera, m_arrTrainCamNode[m_iCurrentTrainCamNode].m_fNearClip); + } + }else{ + if(DotProduct(((CTrain*)pTargetEntity)->GetMoveSpeed(), pTargetEntity->GetForward()) < 0.001f){ + TakeControl(FindPlayerPed(), CCam::MODE_FOLLOWPED, JUMP_CUT, CAMCONTROL_SCRIPT); + if(target->Doors[0].IsFullyOpen()) + SetWideScreenOff(); + } + } +} +#endif + + +void +CCamera::LoadPathSplines(int file) +{ + bool reading = true; + char c, token[32] = { 0 }; + int i, j, n; + + n = 0; + + DeleteCutSceneCamDataMemory(); + for(i = 0; i < MAX_NUM_OF_SPLINETYPES; i++) + m_arrPathArray[i].m_arr_PathData = new float[CCamPathSplines::MAXPATHLENGTH]; + +// Why is this gone? +// for(i = 0; i < MAX_NUM_OF_SPLINETYPES; i++) +// for(j = 0; j < CCamPathSplines::MAXPATHLENGTH; j++) +// m_arrPathArray[i].m_arr_PathData[j] = 0.0f; + + m_bStartingSpline = false; + + i = 0; + j = 0; + while(reading){ + CFileMgr::Read(file, &c, 1); + switch(c){ + case '\0': + reading = false; + break; + + case '+': case '-': case '.': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case 'e': case 'E': + token[n++] = c; + break; + + case ',': +#ifdef FIX_BUGS + if(i < MAX_NUM_OF_SPLINETYPES && j < CCamPathSplines::MAXPATHLENGTH) +#endif + m_arrPathArray[i].m_arr_PathData[j] = atof(token); + j++; + memset(token, 0, 32); + n = 0; + break; + + case ';': +#ifdef FIX_BUGS + if(i < MAX_NUM_OF_SPLINETYPES && j < CCamPathSplines::MAXPATHLENGTH) +#endif + m_arrPathArray[i].m_arr_PathData[j] = atof(token); + i++; + j = 0; + if (i == MAX_NUM_OF_SPLINETYPES) + reading = false; + memset(token, 0, 32); + n = 0; + } + } +} + +void +CCamera::DeleteCutSceneCamDataMemory(void) +{ + int i; + for(i = 0; i < MAX_NUM_OF_SPLINETYPES; i++) + if(m_arrPathArray[i].m_arr_PathData){ + delete[] m_arrPathArray[i].m_arr_PathData; + m_arrPathArray[i].m_arr_PathData = nil; + } +} + +void +CCamera::FinishCutscene(void) +{ + SetPercentAlongCutScene(100.0f); + m_fPositionAlongSpline = 1.0f; + m_bcutsceneFinished = true; +} + +uint32 +CCamera::GetCutSceneFinishTime(void) +{ + int cam = ActiveCam; + if (Cams[cam].Mode == CCam::MODE_FLYBY) + return Cams[cam].m_uiFinishTime; + cam = (cam + 1) % 2; + if (Cams[cam].Mode == CCam::MODE_FLYBY) + return Cams[cam].m_uiFinishTime; + + return 0; +} + +void +CCamera::SetCamCutSceneOffSet(const CVector &pos) +{ + m_vecCutSceneOffset = pos; +}; + +void +CCamera::SetPercentAlongCutScene(float percent) +{ + if(Cams[ActiveCam].Mode == CCam::MODE_FLYBY) + Cams[ActiveCam].m_fTimeElapsedFloat = percent/100.0f * Cams[ActiveCam].m_uiFinishTime; + else if(Cams[(ActiveCam+1)%2].Mode == CCam::MODE_FLYBY) + Cams[(ActiveCam+1)%2].m_fTimeElapsedFloat = percent/100.0f * Cams[(ActiveCam+1)%2].m_uiFinishTime; +} + +void +CCamera::SetParametersForScriptInterpolation(float stopMoving, float catchUp, int32 time) +{ + m_fScriptPercentageInterToStopMoving = stopMoving * 0.01f; + m_fScriptPercentageInterToCatchUp = catchUp * 0.01f; + m_fScriptTimeForInterPolation = time; + m_bScriptParametersSetForInterPol = true; +} + +void +CCamera::SetZoomValueFollowPedScript(int16 dist) +{ + switch (dist) { + case 0: m_fPedZoomValueScript = 0.25f; break; + case 1: m_fPedZoomValueScript = 1.5f; break; + case 2: m_fPedZoomValueScript = 2.9f; break; + default: break; + } + + m_bUseScriptZoomValuePed = true; +} + +void +CCamera::SetZoomValueCamStringScript(int16 dist) +{ + if (Cams[ActiveCam].CamTargetEntity->IsVehicle()) { + int vehApp = ((CVehicle*)Cams[ActiveCam].CamTargetEntity)->GetVehicleAppearance(); + int vehArrPos = 0; + GetArrPosForVehicleType(vehApp, vehArrPos); + +#ifdef FREE_CAM + if (bFreeCam) { + switch (dist) { + case 0: m_fCarZoomValueScript = LCS_ZOOM_ONE_DISTANCE[vehArrPos]; break; + case 1: m_fCarZoomValueScript = LCS_ZOOM_TWO_DISTANCE[vehArrPos]; break; + case 2: m_fCarZoomValueScript = LCS_ZOOM_THREE_DISTANCE[vehArrPos]; break; + default: break; + } + } + else +#endif + { + switch (dist) { + case 0: m_fCarZoomValueScript = ZOOM_ONE_DISTANCE[vehArrPos]; break; + case 1: m_fCarZoomValueScript = ZOOM_TWO_DISTANCE[vehArrPos]; break; + case 2: m_fCarZoomValueScript = ZOOM_THREE_DISTANCE[vehArrPos]; break; + default: break; + } + } + + m_bUseScriptZoomValueCar = true; + } else { + switch (dist) { + case 0: m_fPedZoomValueScript = 0.25f; break; + case 1: m_fPedZoomValueScript = 1.5f; break; + case 2: m_fPedZoomValueScript = 2.9f; break; + default: break; + } + + m_bUseScriptZoomValuePed = true; + } +} + +void +CCamera::SetNearClipScript(float clip) +{ + m_fNearClipScript = clip; + m_bUseNearClipScript = true; +} + + + +void +CCamera::ProcessFade(void) +{ + if(m_bFading){ + if(m_iFadingDirection == FADE_IN){ + if(m_fTimeToFadeOut != 0.0f) + m_fFLOATingFade -= CTimer::GetTimeStepInSeconds() * 255.0f / m_fTimeToFadeOut; + else + m_fFLOATingFade = 0.0f; + if (m_fFLOATingFade <= 0.0f) { + m_bFading = false; + m_fFLOATingFade = 0.0f; + } + }else if(m_iFadingDirection == FADE_OUT){ + if(m_fTimeToFadeOut != 0.0f) + m_fFLOATingFade += CTimer::GetTimeStepInSeconds() * 255.0f / m_fTimeToFadeOut; + else + m_fFLOATingFade = 255.0f; + if (m_fFLOATingFade >= 255.0f) { + m_bFading = false; + m_fFLOATingFade = 255.0f; + } + } + CDraw::FadeValue = m_fFLOATingFade; + } +} + +void +CCamera::ProcessMusicFade(void) +{ + if(m_bMusicFading){ + if(m_iMusicFadingDirection == FADE_IN){ + if(m_fTimeToFadeMusic == 0.0f) + m_fFLOATingFadeMusic = 0.0f; + else + m_fFLOATingFadeMusic -= 255.0f*CTimer::GetTimeStepInSeconds()/m_fTimeToFadeMusic; + if(m_fFLOATingFadeMusic <= 0.0f){ + m_bMusicFading = false; + m_fFLOATingFadeMusic = 0.0f; + } + }else if(m_iMusicFadingDirection == FADE_OUT){ + if(m_fTimeToFadeMusic == 0.0f) + m_fFLOATingFadeMusic = 255.0f; + else + m_fFLOATingFadeMusic += 255.0f*CTimer::GetTimeStepInSeconds()/m_fTimeToFadeMusic; + if(m_fFLOATingFadeMusic >= 255.0f){ + m_bMusicFading = false; + m_fFLOATingFadeMusic = 255.0f; + } + } + DMAudio.SetEffectsFadeVol(127 - m_fFLOATingFadeMusic/255.0f * 127); + DMAudio.SetMusicFadeVol(127 - m_fFLOATingFadeMusic/255.0f * 127); + } +} + +void +CCamera::Fade(float timeout, int16 direction) +{ + m_bFading = true; + m_iFadingDirection = direction; + m_fTimeToFadeOut = timeout; + m_uiFadeTimeStarted = CTimer::GetTimeInMilliseconds(); + if(!m_bIgnoreFadingStuffForMusic){ + m_bMusicFading = true; + m_iMusicFadingDirection = direction; + m_fTimeToFadeMusic = timeout; + m_uiFadeTimeStartedMusic = CTimer::GetTimeInMilliseconds(); + } +} + +void +CCamera::SetFadeColour(uint8 r, uint8 g, uint8 b) +{ + m_FadeTargetIsSplashScreen = r == 2 && g == 2 && b == 2; + CDraw::FadeRed = r; + CDraw::FadeGreen = g; + CDraw::FadeBlue = b; +} + +bool +CCamera::GetFading(void) +{ + return m_bFading; +} + +int +CCamera::GetFadingDirection(void) +{ + if(m_bFading) + return m_iFadingDirection == FADE_IN ? FADE_IN : FADE_OUT; + else + return FADE_NONE; +} + +int +CCamera::GetScreenFadeStatus(void) +{ + if(m_fFLOATingFade == 0.0f) + return FADE_0; + if(m_fFLOATingFade == 255.0f) + return FADE_2; + return FADE_1; +} + + + +void +CCamera::RenderMotionBlur(void) +{ + if(m_BlurType == 0) + return; + + CMBlur::MotionBlurRender(m_pRwCamera, + m_BlurRed, m_BlurGreen, m_BlurBlue, + m_motionBlur, m_BlurType, m_imotionBlurAddAlpha); +} + +void +CCamera::SetMotionBlur(int r, int g, int b, int a, int type) +{ + m_BlurRed = r; + m_BlurGreen = g; + m_BlurBlue = b; + m_motionBlur = a; + m_BlurType = type; +} + +void +CCamera::SetMotionBlurAlpha(int a) +{ + m_imotionBlurAddAlpha = a; +} + + + +int +CCamera::GetLookDirection(void) +{ + if(Cams[ActiveCam].Mode == CCam::MODE_CAM_ON_A_STRING || + Cams[ActiveCam].Mode == CCam::MODE_1STPERSON || + Cams[ActiveCam].Mode == CCam::MODE_BEHINDBOAT || + Cams[ActiveCam].Mode == CCam::MODE_FOLLOWPED) + return Cams[ActiveCam].DirectionWasLooking; + return LOOKING_FORWARD; +} + +bool +CCamera::GetLookingForwardFirstPerson(void) +{ + return Cams[ActiveCam].Mode == CCam::MODE_1STPERSON && + Cams[ActiveCam].DirectionWasLooking == LOOKING_FORWARD; +} + +bool +CCamera::GetLookingLRBFirstPerson(void) +{ + return Cams[ActiveCam].Mode == CCam::MODE_1STPERSON && Cams[ActiveCam].DirectionWasLooking != LOOKING_FORWARD; +} + +void +CCamera::SetCameraDirectlyBehindForFollowPed_CamOnAString(void) +{ + m_bCamDirectlyBehind = true; + CPlayerPed *player = FindPlayerPed(); + if (player) + m_PedOrientForBehindOrInFront = CGeneral::GetATanOfXY(player->GetForward().x, player->GetForward().y); +} + +void +CCamera::SetCameraDirectlyInFrontForFollowPed_CamOnAString(void) +{ + m_bCamDirectlyInFront = true; + CPlayerPed *player = FindPlayerPed(); + if (player) + m_PedOrientForBehindOrInFront = CGeneral::GetATanOfXY(player->GetForward().x, player->GetForward().y); +} + +void +CCamera::SetNewPlayerWeaponMode(int16 mode, int16 minZoom, int16 maxZoom) +{ + SetMotionBlur(CTimeCycle::GetBlurRed(), CTimeCycle::GetBlurGreen(), CTimeCycle::GetBlurBlue(), m_motionBlur, MOTION_BLUR_LIGHT_SCENE); + PlayerWeaponMode.Mode = mode; + PlayerWeaponMode.MaxZoom = maxZoom; + PlayerWeaponMode.MinZoom = minZoom; + PlayerWeaponMode.Duration = 0.0f; +} + +void +CCamera::ClearPlayerWeaponMode(void) +{ + SetMotionBlur(CTimeCycle::GetBlurRed(), CTimeCycle::GetBlurGreen(), CTimeCycle::GetBlurBlue(), m_motionBlur, MOTION_BLUR_LIGHT_SCENE); + PlayerWeaponMode.Mode = 0; + PlayerWeaponMode.MaxZoom = 1; + PlayerWeaponMode.MinZoom = -1; + PlayerWeaponMode.Duration = 0.0f; +} + +void +CCamera::UpdateAimingCoors(CVector const &coors) +{ + m_cvecAimingTargetCoors = coors; +} + +bool +CCamera::Find3rdPersonCamTargetVector(float dist, CVector pos, CVector &source, CVector &target) +{ + if(CPad::GetPad(0)->GetLookBehindForPed()){ + source = pos; + target = dist*Cams[ActiveCam].CamTargetEntity->GetForward() + source; + return false; + }else{ + float angleX = DEGTORAD((m_f3rdPersonCHairMultX-0.5f) * 1.8f * 0.5f * Cams[ActiveCam].FOV * CDraw::GetAspectRatio()); + float angleY = DEGTORAD((0.5f-m_f3rdPersonCHairMultY) * 1.8f * 0.5f * Cams[ActiveCam].FOV); + source = Cams[ActiveCam].Source; + target = Cams[ActiveCam].Front; + target += Cams[ActiveCam].Up * Tan(angleY); + target += CrossProduct(Cams[ActiveCam].Front, Cams[ActiveCam].Up) * Tan(angleX); + target.Normalise(); + source += DotProduct(pos - source, target)*target; + target = dist*target + source; + return true; + } +} + +float +CCamera::Find3rdPersonQuickAimPitch(void) +{ + float clampedFrontZ = Clamp(Cams[ActiveCam].Front.z, -1.0f, 1.0f); + + float rot = Asin(clampedFrontZ); + + return -(DEGTORAD(((0.5f - m_f3rdPersonCHairMultY) * 1.8f * 0.5f * Cams[ActiveCam].FOV)) + rot); +} + +bool +CCamera::Using1stPersonWeaponMode(void) +{ + switch(PlayerWeaponMode.Mode) + case CCam::MODE_SNIPER: + case CCam::MODE_M16_1STPERSON: + case CCam::MODE_ROCKETLAUNCHER: + case CCam::MODE_HELICANNON_1STPERSON: + case CCam::MODE_CAMERA: + return true; + return false; +} + + +void +CCamera::SetRwCamera(RwCamera *cam) +{ + m_pRwCamera = cam; + m_viewMatrix.Attach(RwCameraGetViewMatrix(m_pRwCamera), false); + CMBlur::MotionBlurOpen(m_pRwCamera); +} + +void +CCamera::CalculateDerivedValues(void) +{ + m_cameraMatrix = Invert(GetMatrix()); + + float hfov = DEGTORAD(CDraw::GetScaledFOV()/2.0f); + float c = Cos(hfov); + float s = Sin(hfov); + + // right plane + m_vecFrustumNormals[0] = CVector(c, -s, 0.0f); + // left plane + m_vecFrustumNormals[1] = CVector(-c, -s, 0.0f); + + CDraw::CalculateAspectRatio(); + c /= SCREEN_ASPECT_RATIO; + s /= SCREEN_ASPECT_RATIO; + // bottom plane + m_vecFrustumNormals[2] = CVector(0.0f, -s, -c); + // top plane + m_vecFrustumNormals[3] = CVector(0.0f, -s, c); + + if(GetForward().x == 0.0f && GetForward().y == 0.0f) + GetForward().x = 0.0001f; + else + Orientation = Atan2(GetForward().x, GetForward().y); + + CamFrontXNorm = GetForward().x; + CamFrontYNorm = GetForward().y; + float l = Sqrt(SQR(CamFrontXNorm) + SQR(CamFrontYNorm)); + if(l == 0.0f) + CamFrontXNorm = 1.0f; + else{ + CamFrontXNorm /= l; + CamFrontYNorm /= l; + } +} + +bool +CCamera::IsPointVisible(const CVector ¢er, const CMatrix *mat) +{ +#ifdef GTA_PS2 + CVuVector c; + TransformPoint(c, *mat, center); +#else + CVector c = center; + #ifdef FIX_BUGS + c = *mat * center; + #else + RwV3dTransformPoints(&c, &c, 1, (RwMatrix*)mat); + #endif +#endif + if(c.y < CDraw::GetNearClipZ()) return false; + if(c.y > CDraw::GetFarClipZ()) return false; + if(c.x*m_vecFrustumNormals[0].x + c.y*m_vecFrustumNormals[0].y > 0.0f) return false; + if(c.x*m_vecFrustumNormals[1].x + c.y*m_vecFrustumNormals[1].y > 0.0f) return false; + if(c.y*m_vecFrustumNormals[2].y + c.z*m_vecFrustumNormals[2].z > 0.0f) return false; + if(c.y*m_vecFrustumNormals[3].y + c.z*m_vecFrustumNormals[3].z > 0.0f) return false; + return true; +} + +bool +CCamera::IsSphereVisible(const CVector ¢er, float radius, const CMatrix *mat) +{ +#ifdef GTA_PS2 + CVuVector c; + TransformPoint(c, *mat, center); +#else + CVector c = center; + #ifdef FIX_BUGS + c = *mat * center; + #else + RwV3dTransformPoints(&c, &c, 1, (RwMatrix*)mat); + #endif +#endif + if(c.y + radius < CDraw::GetNearClipZ()) return false; + if(c.y - radius > CDraw::GetFarClipZ()) return false; + if(c.x*m_vecFrustumNormals[0].x + c.y*m_vecFrustumNormals[0].y > radius) return false; + if(c.x*m_vecFrustumNormals[1].x + c.y*m_vecFrustumNormals[1].y > radius) return false; + if(c.y*m_vecFrustumNormals[2].y + c.z*m_vecFrustumNormals[2].z > radius) return false; + if(c.y*m_vecFrustumNormals[3].y + c.z*m_vecFrustumNormals[3].z > radius) return false; + return true; +} + +bool +CCamera::IsSphereVisible(const CVector ¢er, float radius) +{ + return IsSphereVisible(center, radius, &GetCameraMatrix()); +} + +bool +CCamera::IsBoxVisible(CVUVECTOR *box, const CMatrix *mat) +{ + int i; + int frustumTests[6] = { 0 }; +#ifdef GTA_PS2 + TransformPoints(box, 8, *mat, box); +#else + #ifdef FIX_BUGS + for (i = 0; i < 8; i++) + box[i] = *mat * box[i]; + #else + RwV3dTransformPoints(box, box, 8, (RwMatrix*)mat); + #endif +#endif + + for(i = 0; i < 8; i++){ + if(box[i].y < CDraw::GetNearClipZ()) frustumTests[0]++; + if(box[i].y > CDraw::GetFarClipZ()) frustumTests[1]++; + if(box[i].x*m_vecFrustumNormals[0].x + box[i].y*m_vecFrustumNormals[0].y > 0.0f) frustumTests[2]++; + if(box[i].x*m_vecFrustumNormals[1].x + box[i].y*m_vecFrustumNormals[1].y > 0.0f) frustumTests[3]++; +// Why not test z? +// if(box[i].y*m_vecFrustumNormals[2].y + box[i].z*m_vecFrustumNormals[2].z > 0.0f) frustumTests[4]++; +// if(box[i].y*m_vecFrustumNormals[3].y + box[i].z*m_vecFrustumNormals[3].z > 0.0f) frustumTests[5]++; + } + for(i = 0; i < 6; i++) + if(frustumTests[i] == 8) + return false; // Box is completely outside of one plane + return true; +} + + + +CCamPathSplines::CCamPathSplines(void) +{ + m_arr_PathData = nil; +} diff --git a/src/miami/core/Camera.h b/src/miami/core/Camera.h new file mode 100644 index 00000000..39ecb760 --- /dev/null +++ b/src/miami/core/Camera.h @@ -0,0 +1,645 @@ +#pragma once +#include "Placeable.h" + +class CEntity; +class CPed; +class CAutomobile; +class CGarage; + +extern int16 DebugCamMode; + +enum +{ + NUMBER_OF_VECTORS_FOR_AVERAGE = 2, + MAX_NUM_OF_SPLINETYPES = 4, + MAX_NUM_OF_NODES = 800 // for trains +}; + +#define DEFAULT_NEAR (0.9f) +enum +{ + CAM_ZOOM_1STPRS, + CAM_ZOOM_1, + CAM_ZOOM_2, + CAM_ZOOM_3, + CAM_ZOOM_TOPDOWN, + CAM_ZOOM_CINEMATIC, +}; + +const float DefaultFOV = 70.0f; // beta: 80.0f + +class CCam +{ +public: + enum + { + MODE_NONE = 0, + MODE_TOPDOWN, + MODE_GTACLASSIC, + MODE_BEHINDCAR, + MODE_FOLLOWPED, + MODE_AIMING, + MODE_DEBUG, + MODE_SNIPER, + MODE_ROCKETLAUNCHER, + MODE_MODELVIEW, + MODE_BILL, + MODE_SYPHON, + MODE_CIRCLE, + MODE_CHEESYZOOM, + MODE_WHEELCAM, + MODE_FIXED, + MODE_1STPERSON, + MODE_FLYBY, + MODE_CAM_ON_A_STRING, + MODE_REACTION, + MODE_FOLLOW_PED_WITH_BIND, + MODE_CHRIS, + MODE_BEHINDBOAT, + MODE_PLAYER_FALLEN_WATER, + MODE_CAM_ON_TRAIN_ROOF, + MODE_CAM_RUNNING_SIDE_TRAIN, + MODE_BLOOD_ON_THE_TRACKS, + MODE_IM_THE_PASSENGER_WOOWOO, + MODE_SYPHON_CRIM_IN_FRONT, + MODE_PED_DEAD_BABY, + MODE_PILLOWS_PAPS, + MODE_LOOK_AT_CARS, + MODE_ARRESTCAM_ONE, + MODE_ARRESTCAM_TWO, + MODE_M16_1STPERSON, + MODE_SPECIAL_FIXED_FOR_SYPHON, + MODE_FIGHT_CAM, + MODE_TOP_DOWN_PED, + MODE_LIGHTHOUSE, + MODE_SNIPER_RUNABOUT, + MODE_ROCKETLAUNCHER_RUNABOUT, + MODE_1STPERSON_RUNABOUT, + MODE_M16_1STPERSON_RUNABOUT, + MODE_FIGHT_CAM_RUNABOUT, + MODE_EDITOR, + MODE_HELICANNON_1STPERSON, + MODE_CAMERA, + }; + + bool bBelowMinDist; //used for follow ped mode + bool bBehindPlayerDesired; //used for follow ped mode + bool m_bCamLookingAtVector; + bool m_bCollisionChecksOn; + bool m_bFixingBeta; //used for camera on a string + bool m_bTheHeightFixerVehicleIsATrain; + bool LookBehindCamWasInFront; + bool LookingBehind; + bool LookingLeft; + bool LookingRight; + bool ResetStatics; //for interpolation type stuff to work + bool Rotating; + + int16 Mode; // CameraMode + uint32 m_uiFinishTime; + + int m_iDoCollisionChecksOnFrameNum; + int m_iDoCollisionCheckEveryNumOfFrames; + int m_iFrameNumWereAt; + int m_iRunningVectorArrayPos; + int m_iRunningVectorCounter; + int DirectionWasLooking; + + float f_max_role_angle; //=DEGTORAD(5.0f); + float f_Roll; //used for adding a slight roll to the camera in the + float f_rollSpeed; + float m_fSyphonModeTargetZOffSet; + float m_fAmountFractionObscured; + float m_fAlphaSpeedOverOneFrame; + float m_fBetaSpeedOverOneFrame; + float m_fBufferedTargetBeta; + float m_fBufferedTargetOrientation; + float m_fBufferedTargetOrientationSpeed; + float m_fCamBufferedHeight; + float m_fCamBufferedHeightSpeed; + float m_fCloseInPedHeightOffset; + float m_fCloseInPedHeightOffsetSpeed; + float m_fCloseInCarHeightOffset; + float m_fCloseInCarHeightOffsetSpeed; + float m_fDimensionOfHighestNearCar; + float m_fDistanceBeforeChanges; + float m_fFovSpeedOverOneFrame; + float m_fMinDistAwayFromCamWhenInterPolating; + float m_fPedBetweenCameraHeightOffset; + float m_fPlayerInFrontSyphonAngleOffSet; + float m_fRadiusForDead; + float m_fRealGroundDist; //used for follow ped mode + float m_fTargetBeta; + float m_fTimeElapsedFloat; + float m_fTilt; + float m_fTiltSpeed; + float m_fTransitionBeta; + float m_fTrueBeta; + float m_fTrueAlpha; + float m_fInitialPlayerOrientation; //used for first person + + float Alpha; + float AlphaSpeed; + float FOV; + float FOVSpeed; + float Beta; + float BetaSpeed; + float Distance; + float DistanceSpeed; + float CA_MIN_DISTANCE; + float CA_MAX_DISTANCE; + float SpeedVar; + + float m_fTargetZoomGroundOne; + float m_fTargetZoomGroundTwo; + float m_fTargetZoomGroundThree; + float m_fTargetZoomOneZExtra; + float m_fTargetZoomTwoZExtra; + float m_fTargetZoomThreeZExtra; + float m_fTargetZoomZCloseIn; + float m_fMinRealGroundDist; + float m_fTargetCloseInDist; + + CVector m_cvecSourceSpeedOverOneFrame; + CVector m_cvecTargetSpeedOverOneFrame; + CVector m_cvecUpOverOneFrame; + + CVector m_cvecTargetCoorsForFudgeInter; + CVector m_cvecCamFixedModeVector; + CVector m_cvecCamFixedModeSource; + CVector m_cvecCamFixedModeUpOffSet; + CVector m_vecLastAboveWaterCamPosition; //helper for when the player has gone under the water + CVector m_vecBufferedPlayerBodyOffset; + + // The three vectors that determine this camera for this frame + CVector Front; // Direction of looking in + CVector Source; // Coors in world space + CVector SourceBeforeLookBehind; + CVector Up; // Just that + CVector m_arrPreviousVectors[NUMBER_OF_VECTORS_FOR_AVERAGE]; // used to average stuff + CEntity *CamTargetEntity; + + float m_fCameraDistance; + float m_fIdealAlpha; + float m_fPlayerVelocity; + CAutomobile *m_pLastCarEntered; // So interpolation works + CPed *m_pLastPedLookedAt;// So interpolation works + bool m_bFirstPersonRunAboutActive; + + CCam(void) { Init(); } + void Init(void); + void Process(void); + void ProcessSpecialHeightRoutines(void); + void GetVectorsReadyForRW(void); + CVector DoAverageOnVector(const CVector &vec); + void WorkOutCamHeight(const CVector &TargetCoors, float TargetOrientation, float TargetHeight); + bool RotCamIfInFrontCar(CVector &TargetCoors, float TargetOrientation); + void Cam_On_A_String_Unobscured(const CVector &TargetCoors, float BaseDist); + void FixCamWhenObscuredByVehicle(const CVector &TargetCoors); + bool GetBoatLook_L_R_HeightOffset(float &Offset); + void LookBehind(void); + void LookLeft(void); + void LookRight(void); + void ClipIfPedInFrontOfPlayer(void); + void KeepTrackOfTheSpeed(const CVector &source, const CVector &target, const CVector &up, const float &alpha, const float &beta, const float &fov); + bool Using3rdPersonMouseCam(void); + bool GetWeaponFirstPersonOn(void); + bool IsTargetInWater(const CVector &CamCoors); + void AvoidWallsTopDownPed(const CVector &TargetCoors, const CVector &Offset, float *Adjuster, float *AdjusterSpeed, float yDistLimit); + void PrintMode(void); + + void Process_Debug(const CVector&, float, float, float); +#ifdef GTA_SCENE_EDIT + void Process_Editor(const CVector&, float, float, float); +#endif + void Process_ModelView(const CVector &CameraTarget, float, float, float); + void Process_FollowPed(const CVector &CameraTarget, float TargetOrientation, float, float); + void Process_FollowPedWithMouse(const CVector &CameraTarget, float TargetOrientation, float, float); + void Process_BehindCar(const CVector &CameraTarget, float TargetOrientation, float, float); + void Process_Cam_On_A_String(const CVector &CameraTarget, float TargetOrientation, float, float); + void Process_TopDown(const CVector &CameraTarget, float TargetOrientation, float SpeedVar, float TargetSpeedVar); + void Process_TopDownPed(const CVector &CameraTarget, float TargetOrientation, float, float); + void Process_Rocket(const CVector &CameraTarget, float, float, float); + void Process_M16_1stPerson(const CVector &CameraTarget, float, float, float); + void Process_1stPerson(const CVector &CameraTarget, float, float, float); + void Process_1rstPersonPedOnPC(const CVector &CameraTarget, float TargetOrientation, float, float); + void Process_Sniper(const CVector &CameraTarget, float, float, float); + void Process_Syphon(const CVector &CameraTarget, float, float, float); + void Process_Syphon_Crim_In_Front(const CVector &CameraTarget, float, float, float); + void Process_BehindBoat(const CVector &CameraTarget, float TargetOrientation, float, float); + void Process_Fight_Cam(const CVector &CameraTarget, float TargetOrientation, float, float); + void Process_FlyBy(const CVector&, float, float, float); + bool Process_WheelCam(const CVector&, float, float, float); + void Process_Fixed(const CVector &CameraTarget, float, float, float); + void Process_Player_Fallen_Water(const CVector &CameraTarget, float TargetOrientation, float, float); + void Process_SpecialFixedForSyphon(const CVector &CameraTarget, float, float, float); + void Process_LightHouse(const CVector &CameraTarget, float, float, float); + void ProcessPedsDeadBaby(void); + bool ProcessArrestCamOne(void); + bool ProcessArrestCamTwo(void); + bool GetLookAlongGroundPos(CEntity *Target, CPed *Cop, CVector &TargetCoors, CVector &SourceOut); + bool GetLookFromLampPostPos(CEntity *Target, CPed *Cop, CVector &TargetCoors, CVector &SourceOut); + bool GetLookOverShoulderPos(CEntity *Target, CPed *Cop, CVector &TargetCoors, CVector &SourceOut); + + // custom stuff + void Process_FollowPed_Rotation(const CVector &CameraTarget, float TargetOrientation, float, float); + void Process_FollowCar_SA(const CVector &CameraTarget, float TargetOrientation, float, float); +}; + +class CCamPathSplines +{ +public: + enum {MAXPATHLENGTH=800}; +// float m_arr_PathData[MAXPATHLENGTH]; + float *m_arr_PathData; + CCamPathSplines(void); +}; + +struct CTrainCamNode +{ + CVector m_cvecCamPosition; + CVector m_cvecPointToLookAt; + CVector m_cvecMinPointInRange; + CVector m_cvecMaxPointInRange; + float m_fDesiredFOV; + float m_fNearClip; +}; + +struct CQueuedMode +{ + int16 Mode; + float Duration; + int16 MinZoom; + int16 MaxZoom; +}; + +enum +{ + LOOKING_BEHIND, + LOOKING_LEFT, + LOOKING_RIGHT, + LOOKING_FORWARD, +}; + +enum +{ + // TODO: find better names + FADE_0, // faded in + FADE_1, // mid fade + FADE_2, // faded out + + // Direction + FADE_OUT = 0, + FADE_IN, + FADE_NONE +}; + +enum +{ + MOTION_BLUR_NONE = 0, + MOTION_BLUR_SNIPER, + MOTION_BLUR_LIGHT_SCENE, + MOTION_BLUR_SECURITY_CAM, + MOTION_BLUR_CUT_SCENE, + MOTION_BLUR_INTRO, + MOTION_BLUR_INTRO2, + MOTION_BLUR_SNIPER_ZOOM, + MOTION_BLUR_INTRO3, + MOTION_BLUR_INTRO4, +}; + +enum +{ + NONE = 0, + INTERPOLATION, + JUMP_CUT +}; + +enum +{ + CAMCONTROL_GAME, + CAMCONTROL_SCRIPT, + CAMCONTROL_OBBE +}; + +class CCamera : public CPlaceable +{ +public: + bool m_bAboveGroundTrainNodesLoaded; + bool m_bBelowGroundTrainNodesLoaded; + bool m_bCamDirectlyBehind; + bool m_bCamDirectlyInFront; + bool m_bCameraJustRestored; + bool m_bcutsceneFinished; + bool m_bCullZoneChecksOn; + bool m_bFirstPersonBeingUsed; + bool m_bJustJumpedOutOf1stPersonBecauseOfTarget; + bool m_bIdleOn; + bool m_bInATunnelAndABigVehicle; + bool m_bInitialNodeFound; + bool m_bInitialNoNodeStaticsSet; + bool m_bIgnoreFadingStuffForMusic; + bool m_bPlayerIsInGarage; + bool m_bPlayerWasOnBike; + bool m_bJustCameOutOfGarage; + bool m_bJustInitalised; + bool m_bJust_Switched; + bool m_bLookingAtPlayer; + bool m_bLookingAtVector; + bool m_bMoveCamToAvoidGeom; + bool m_bObbeCinematicPedCamOn; + bool m_bObbeCinematicCarCamOn; + bool m_bRestoreByJumpCut; + bool m_bUseNearClipScript; + bool m_bStartInterScript; + bool m_bStartingSpline; + bool m_bTargetJustBeenOnTrain; + bool m_bTargetJustCameOffTrain; + bool m_bUseSpecialFovTrain; + bool m_bUseTransitionBeta; + bool m_bUseScriptZoomValuePed; + bool m_bUseScriptZoomValueCar; + bool m_bWaitForInterpolToFinish; + bool m_bItsOkToLookJustAtThePlayer; + bool m_bWantsToSwitchWidescreenOff; + bool m_WideScreenOn; + bool m_1rstPersonRunCloseToAWall; + bool m_bHeadBob; + bool m_bVehicleSuspenHigh; + bool m_bEnable1rstPersonCamCntrlsScript; + bool m_bAllow1rstPersonWeaponsCamera; + bool m_bFailedCullZoneTestPreviously; + + bool m_FadeTargetIsSplashScreen; + + bool WorldViewerBeingUsed; + uint8 ActiveCam; + uint32 m_uiCamShakeStart; + uint32 m_uiFirstPersonCamLastInputTime; + + uint32 m_uiLongestTimeInMill; + uint32 m_uiNumberOfTrainCamNodes; + uint8 m_uiTransitionJUSTStarted; + uint8 m_uiTransitionState; // 0:one mode 1:transition + + uint32 m_uiTimeLastChange; + uint32 m_uiTimeWeLeftIdle_StillNoInput; + uint32 m_uiTimeWeEnteredIdle; + uint32 m_uiTimeTransitionStart; + uint32 m_uiTransitionDuration; + uint32 m_uiTransitionDurationTargetCoors; + int m_BlurBlue; + int m_BlurGreen; + int m_BlurRed; + int m_BlurType; + + int m_iWorkOutSpeedThisNumFrames; + int m_iNumFramesSoFar; + + + int m_iCurrentTrainCamNode; + int m_motionBlur; + int m_imotionBlurAddAlpha; + int m_iCheckCullZoneThisNumFrames; + int m_iZoneCullFrameNumWereAt; + int WhoIsInControlOfTheCamera; + + float CamFrontXNorm; + float CamFrontYNorm; +#ifdef FIX_BUGS + int32 CarZoomIndicator; +#else + float CarZoomIndicator; +#endif + float CarZoomValue; + float CarZoomValueSmooth; + + float DistanceToWater; + float FOVDuringInter; + float LODDistMultiplier; + float GenerationDistMultiplier; + float m_fAlphaSpeedAtStartInter; + float m_fAlphaWhenInterPol; + float m_fAlphaDuringInterPol; + float m_fBetaDuringInterPol; + float m_fBetaSpeedAtStartInter; + float m_fBetaWhenInterPol; + float m_fFOVWhenInterPol; + float m_fFOVSpeedAtStartInter; + float m_fStartingBetaForInterPol; + float m_fStartingAlphaForInterPol; + float m_PedOrientForBehindOrInFront; + float m_CameraAverageSpeed; + float m_CameraSpeedSoFar; + float m_fCamShakeForce; + float m_fCarZoomValueScript; + float m_fFovForTrain; + float m_fFOV_Wide_Screen; + float m_fNearClipScript; + float m_fOldBetaDiff; + float m_fPedZoomValue; + + float m_fPedZoomValueScript; + float m_fPedZoomValueSmooth; + float m_fPositionAlongSpline; + float m_ScreenReductionPercentage; + float m_ScreenReductionSpeed; + float m_AlphaForPlayerAnim1rstPerson; + float Orientation; +#ifdef FIX_BUGS + int32 PedZoomIndicator; +#else + float PedZoomIndicator; +#endif + float PlayerExhaustion; + float SoundDistUp; + float SoundDistUpAsRead; + float SoundDistUpAsReadOld; + float m_fAvoidTheGeometryProbsTimer; + int16 m_nAvoidTheGeometryProbsDirn; + float m_fWideScreenReductionAmount; + float m_fStartingFOVForInterPol; + + static float m_fMouseAccelHorzntl;// acceleration multiplier for 1st person controls + static float m_fMouseAccelVertical;// acceleration multiplier for 1st person controls + static float m_f3rdPersonCHairMultX; + static float m_f3rdPersonCHairMultY; + + + CCam Cams[3]; + CGarage *pToGarageWeAreIn; + CGarage *pToGarageWeAreInForHackAvoidFirstPerson; + CQueuedMode m_PlayerMode; + CQueuedMode PlayerWeaponMode; + CVector m_PreviousCameraPosition; + CVector m_RealPreviousCameraPosition; + CVector m_cvecAimingTargetCoors; + CVector m_vecFixedModeVector; + CVector m_vecFixedModeSource; + CVector m_vecFixedModeUpOffSet; + CVector m_vecCutSceneOffset; + + CVector m_cvecStartingSourceForInterPol; + CVector m_cvecStartingTargetForInterPol; + CVector m_cvecStartingUpForInterPol; + CVector m_cvecSourceSpeedAtStartInter; + CVector m_cvecTargetSpeedAtStartInter; + CVector m_cvecUpSpeedAtStartInter; + CVector m_vecSourceWhenInterPol; + CVector m_vecTargetWhenInterPol; + CVector m_vecUpWhenInterPol; + CVector m_vecClearGeometryVec; + CVector m_vecGameCamPos; + CVector SourceDuringInter; + CVector TargetDuringInter; + CVector UpDuringInter; + RwCamera *m_pRwCamera; + CEntity *pTargetEntity; + CCamPathSplines m_arrPathArray[MAX_NUM_OF_SPLINETYPES]; +#ifdef GTA_TRAIN + CTrainCamNode m_arrTrainCamNode[MAX_NUM_OF_NODES]; +#endif + CMatrix m_cameraMatrix; + bool m_bGarageFixedCamPositionSet; + bool m_vecDoingSpecialInterPolation; + bool m_bScriptParametersSetForInterPol; + bool m_bFading; + bool m_bMusicFading; + CMatrix m_viewMatrix; + CVector m_vecFrustumNormals[4]; + CVector m_vecOldSourceForInter; + CVector m_vecOldFrontForInter; + CVector m_vecOldUpForInter; + float m_vecOldFOVForInter; + float m_fFLOATingFade; + float m_fFLOATingFadeMusic; + float m_fTimeToFadeOut; + float m_fTimeToFadeMusic; + float m_fFractionInterToStopMoving; + float m_fFractionInterToStopCatchUp; + float m_fFractionInterToStopMovingTarget; + float m_fFractionInterToStopCatchUpTarget; + float m_fGaitSwayBuffer; + float m_fScriptPercentageInterToStopMoving; + float m_fScriptPercentageInterToCatchUp; + + uint32 m_fScriptTimeForInterPolation; + + + int16 m_iFadingDirection; + int m_iModeObbeCamIsInForCar; + int16 m_iModeToGoTo; + int16 m_iMusicFadingDirection; + int16 m_iTypeOfSwitch; + + uint32 m_uiFadeTimeStarted; + uint32 m_uiFadeTimeStartedMusic; + + static bool m_bUseMouse3rdPerson; +#ifdef FREE_CAM + static bool bFreeCam; +#endif + + // High level and misc + CCamera(void); + void Init(void); + void Process(void); + void CamControl(void); + void UpdateTargetEntity(void); + void UpdateSoundDistances(void); + void InitialiseCameraForDebugMode(void); + void CamShake(float strength, float x, float y, float z); + bool Get_Just_Switched_Status() { return m_bJust_Switched; } + void AvoidTheGeometry(const CVector &Source, const CVector &TargetPos, CVector &NewSource, float FOV); + void GetArrPosForVehicleType(int apperance, int &index); + void GetScreenRect(CRect &rect); + + // Who's in control + void TakeControl(CEntity *target, int16 mode, int16 typeOfSwitch, int32 controller); + void TakeControlNoEntity(const CVector &position, int16 typeOfSwitch, int32 controller); + void TakeControlWithSpline(int16 typeOfSwitch); + void Restore(void); + void RestoreWithJumpCut(void); + void SetCamPositionForFixedMode(const CVector &Source, const CVector &UppOffSet); + + // Transition + void StartTransition(int16 mode); + void StartTransitionWhenNotFinishedInter(int16 mode); + void StoreValuesDuringInterPol(CVector &source, CVector &target, CVector &up, float &FOV); + + // Widescreen borders + void SetWideScreenOn(void); + void SetWideScreenOff(void); + void ProcessWideScreenOn(void); + void DrawBordersForWideScreen(void); + + // Obbe's cam + bool IsItTimeForNewcam(int32 obbeMode, int32 time); + bool TryToStartNewCamMode(int32 obbeMode); + void DontProcessObbeCinemaCamera(void); + void ProcessObbeCinemaCameraCar(void); + void ProcessObbeCinemaCameraHeli(void); + void ProcessObbeCinemaCameraPed(void); + + // Train + void LoadTrainCamNodes(char const *name); + void Process_Train_Camera_Control(void); + + // Script + void LoadPathSplines(int file); + void DeleteCutSceneCamDataMemory(void); + void FinishCutscene(void); + float GetPositionAlongSpline(void) { return m_fPositionAlongSpline; } + uint32 GetCutSceneFinishTime(void); + void SetCamCutSceneOffSet(const CVector &pos); + void SetPercentAlongCutScene(float percent); + void SetParametersForScriptInterpolation(float stopMoving, float catchUp, int32 time); + void SetZoomValueFollowPedScript(int16 dist); + void SetZoomValueCamStringScript(int16 dist); + void SetNearClipScript(float); + + // Fading + void ProcessFade(void); + void ProcessMusicFade(void); + void Fade(float timeout, int16 direction); + void SetFadeColour(uint8 r, uint8 g, uint8 b); + bool GetFading(void); + int GetFadingDirection(void); + int GetScreenFadeStatus(void); + + // Motion blur + void RenderMotionBlur(void); + void SetMotionBlur(int r, int g, int b, int a, int type); + void SetMotionBlurAlpha(int a); + + // Player looking and aiming + int GetLookDirection(void); + bool GetLookingForwardFirstPerson(void); + bool GetLookingLRBFirstPerson(void); + void SetCameraDirectlyInFrontForFollowPed_CamOnAString(void); + void SetCameraDirectlyBehindForFollowPed_CamOnAString(void); + void SetNewPlayerWeaponMode(int16 mode, int16 minZoom, int16 maxZoom); + void ClearPlayerWeaponMode(void); + void UpdateAimingCoors(CVector const &coors); + bool Find3rdPersonCamTargetVector(float dist, CVector pos, CVector &source, CVector &target); + float Find3rdPersonQuickAimPitch(void); + bool Using1stPersonWeaponMode(void); + + // Physical camera + void SetRwCamera(RwCamera *cam); + const CMatrix& GetCameraMatrix(void) { return m_cameraMatrix; } + CVector &GetGameCamPosition(void) { return m_vecGameCamPos; } + void CalculateDerivedValues(void); + bool IsPointVisible(const CVector ¢er, const CMatrix *mat); + bool IsSphereVisible(const CVector ¢er, float radius, const CMatrix *mat); + bool IsSphereVisible(const CVector ¢er, float radius); + bool IsBoxVisible(CVUVECTOR *box, const CMatrix *mat); +}; + +VALIDATE_SIZE(CCamera, 0xE9D8); + +extern CCamera TheCamera; + +void CamShakeNoPos(CCamera*, float); +void MakeAngleLessThan180(float &Angle); +void WellBufferMe(float Target, float *CurrentValue, float *CurrentSpeed, float MaxSpeed, float Acceleration, bool IsAngle); diff --git a/src/miami/core/CdStream.cpp b/src/miami/core/CdStream.cpp new file mode 100644 index 00000000..977f16c2 --- /dev/null +++ b/src/miami/core/CdStream.cpp @@ -0,0 +1,538 @@ +#ifdef _WIN32 +#define WITHWINDOWS +#include "common.h" + +#include "CdStream.h" +#include "rwcore.h" +#include "RwHelper.h" +#include "MemoryMgr.h" + +struct CdReadInfo +{ + uint32 nSectorOffset; + uint32 nSectorsToRead; + void *pBuffer; + char field_C; + bool bLocked; + bool bReading; + int32 nStatus; + HANDLE pDoneSemaphore; // used for CdStreamSync + HANDLE hFile; + OVERLAPPED Overlapped; +}; + +VALIDATE_SIZE(CdReadInfo, 0x30); + +char gCdImageNames[MAX_CDIMAGES+1][64]; +int32 gNumImages; +int32 gNumChannels; + +HANDLE gImgFiles[MAX_CDIMAGES]; + +HANDLE _gCdStreamThread; +HANDLE gCdStreamSema; // released when we have new thing to read(so channel is set) +DWORD _gCdStreamThreadId; + +CdReadInfo *gpReadInfo; +Queue gChannelRequestQ; + +int32 lastPosnRead; + +BOOL _gbCdStreamOverlapped; +BOOL _gbCdStreamAsync; +DWORD _gdwCdStreamFlags; + +DWORD WINAPI CdStreamThread(LPVOID lpThreadParameter); + +void +CdStreamInitThread(void) +{ + SetLastError(0); + + if ( gNumChannels > 0 ) + { + for ( int32 i = 0; i < gNumChannels; i++ ) + { + gpReadInfo[i].pDoneSemaphore = CreateSemaphore(nil, 0, 2, nil); + + if ( gpReadInfo[i].pDoneSemaphore == nil ) + { + printf("%s: failed to create sync semaphore\n", "cdvd_stream"); + ASSERT(0); + return; + } + } + } + + gChannelRequestQ.items = (int32 *)LocalAlloc(LMEM_ZEROINIT, sizeof(int32) * (gNumChannels + 1)); + gChannelRequestQ.head = 0; + gChannelRequestQ.tail = 0; + gChannelRequestQ.size = gNumChannels + 1; + ASSERT(gChannelRequestQ.items != nil ); + +#ifdef FIX_BUGS + gCdStreamSema = CreateSemaphore(nil, 0, 5, nil); +#else + gCdStreamSema = CreateSemaphore(nil, 0, 5, "CdStream"); +#endif + + if ( gCdStreamSema == nil ) + { + printf("%s: failed to create stream semaphore\n", "cdvd_stream"); + ASSERT(0); + return; + } + + _gCdStreamThread = CreateThread(nil, 64*1024/*64KB*/, CdStreamThread, nil, CREATE_SUSPENDED, &_gCdStreamThreadId); + + if ( _gCdStreamThread == nil ) + { + printf("%s: failed to create streaming thread\n", "cdvd_stream"); + ASSERT(0); + return; + } + + SetThreadPriority(_gCdStreamThread, GetThreadPriority(GetCurrentThread()) - 1); + + ResumeThread(_gCdStreamThread); +} + +void +CdStreamInit(int32 numChannels) +{ + DWORD SectorsPerCluster; + DWORD BytesPerSector; + DWORD NumberOfFreeClusters; + DWORD TotalNumberOfClusters; + + GetDiskFreeSpace(nil, &SectorsPerCluster, &BytesPerSector, &NumberOfFreeClusters, &TotalNumberOfClusters); + + _gdwCdStreamFlags = 0; + +#ifndef FIX_BUGS // this just slows down streaming + if ( BytesPerSector <= CDSTREAM_SECTOR_SIZE ) + { + _gdwCdStreamFlags |= FILE_FLAG_NO_BUFFERING; + debug("Using no buffered loading for streaming\n"); + } +#endif + + _gbCdStreamOverlapped = TRUE; + + _gdwCdStreamFlags |= FILE_FLAG_OVERLAPPED; + + _gbCdStreamAsync = FALSE; + + void *pBuffer = (void *)RwMallocAlign(CDSTREAM_SECTOR_SIZE, BytesPerSector); + ASSERT( pBuffer != nil ); + + SetLastError(0); + + gNumImages = 0; + + gNumChannels = numChannels; + + gpReadInfo = (CdReadInfo *)LocalAlloc(LMEM_ZEROINIT, sizeof(CdReadInfo) * numChannels); + ASSERT( gpReadInfo != nil ); + + debug("%s: read info %p\n", "cdvd_stream", gpReadInfo); + + CdStreamAddImage("MODELS\\GTA3.IMG"); + + int32 nStatus = CdStreamRead(0, pBuffer, 0, 1); + + CdStreamRemoveImages(); + + if ( nStatus == STREAM_SUCCESS ) + { + _gbCdStreamAsync = TRUE; + + debug("Using async loading for streaming\n"); + } + else + { + _gdwCdStreamFlags &= ~FILE_FLAG_OVERLAPPED; + + _gbCdStreamOverlapped = FALSE; + + _gbCdStreamAsync = TRUE; + + debug("Using sync loading for streaming\n"); + } + + CdStreamInitThread(); + + ASSERT( pBuffer != nil ); + RwFreeAlign(pBuffer); +} + +uint32 +GetGTA3ImgSize(void) +{ + ASSERT( gImgFiles[0] != nil ); + return (uint32)GetFileSize(gImgFiles[0], nil); +} + +void +CdStreamShutdown(void) +{ + if ( _gbCdStreamAsync ) + { + LocalFree(gChannelRequestQ.items); + CloseHandle(gCdStreamSema); + CloseHandle(_gCdStreamThread); + + for ( int32 i = 0; i < gNumChannels; i++ ) + CloseHandle(gpReadInfo[i].pDoneSemaphore); + } + + LocalFree(gpReadInfo); +} + + + +int32 +CdStreamRead(int32 channel, void *buffer, uint32 offset, uint32 size) +{ + ASSERT( channel < gNumChannels ); + ASSERT( buffer != nil ); + + lastPosnRead = size + offset; + + ASSERT( _GET_INDEX(offset) < MAX_CDIMAGES ); + HANDLE hImage = gImgFiles[_GET_INDEX(offset)]; + ASSERT( hImage != nil ); + + + CdReadInfo *pChannel = &gpReadInfo[channel]; + ASSERT( pChannel != nil ); + + pChannel->hFile = hImage; + + SetLastError(0); + + if ( _gbCdStreamAsync ) + { + if ( pChannel->nSectorsToRead != 0 || pChannel->bReading ) + return STREAM_NONE; + + pChannel->nStatus = STREAM_NONE; + pChannel->nSectorOffset = _GET_OFFSET(offset); + pChannel->nSectorsToRead = size; + pChannel->pBuffer = buffer; + pChannel->bLocked = 0; + + AddToQueue(&gChannelRequestQ, channel); + + if ( !ReleaseSemaphore(gCdStreamSema, 1, nil) ) + printf("Signal Sema Error\n"); + + return STREAM_SUCCESS; + } + + if ( _gbCdStreamOverlapped ) + { + ASSERT( channel < gNumChannels ); + CdReadInfo *pChannel = &gpReadInfo[channel]; + ASSERT( pChannel != nil ); + + pChannel->Overlapped.Offset = _GET_OFFSET(offset) * CDSTREAM_SECTOR_SIZE; + + if ( !ReadFile(hImage, buffer, size * CDSTREAM_SECTOR_SIZE, NULL, &pChannel->Overlapped) + && GetLastError() != ERROR_IO_PENDING ) + return STREAM_NONE; + else + return STREAM_SUCCESS; + } + +#ifdef BIG_IMG + LARGE_INTEGER liDistanceToMove; + liDistanceToMove.QuadPart = _GET_OFFSET(offset); + liDistanceToMove.QuadPart *= CDSTREAM_SECTOR_SIZE; + SetFilePointerEx(hImage, liDistanceToMove, nil, FILE_BEGIN); +#else + SetFilePointer(hImage, _GET_OFFSET(offset) * CDSTREAM_SECTOR_SIZE, nil, FILE_BEGIN); +#endif + + DWORD NumberOfBytesRead; + + if ( !ReadFile(hImage, buffer, size * CDSTREAM_SECTOR_SIZE, &NumberOfBytesRead, nil) ) + return STREAM_NONE; + else + return STREAM_SUCCESS; +} + +int32 +CdStreamGetStatus(int32 channel) +{ + ASSERT( channel < gNumChannels ); + CdReadInfo *pChannel = &gpReadInfo[channel]; + ASSERT( pChannel != nil ); + + if ( _gbCdStreamAsync ) + { + if ( pChannel->bReading ) + return STREAM_READING; + + if ( pChannel->nSectorsToRead != 0 ) + return STREAM_WAITING; + + if ( pChannel->nStatus != STREAM_NONE ) + { + int32 status = pChannel->nStatus; + + pChannel->nStatus = STREAM_NONE; + + return status; + } + + return STREAM_NONE; + } + + if ( _gbCdStreamOverlapped ) + { + ASSERT( pChannel->hFile != nil ); + if ( WaitForSingleObjectEx(pChannel->hFile, 0, TRUE) == WAIT_OBJECT_0 ) + return STREAM_NONE; + else + return STREAM_READING; + } + + return STREAM_NONE; +} + +int32 +CdStreamGetLastPosn(void) +{ + return lastPosnRead; +} + +// wait for channel to finish reading +int32 +CdStreamSync(int32 channel) +{ + ASSERT( channel < gNumChannels ); + CdReadInfo *pChannel = &gpReadInfo[channel]; + ASSERT( pChannel != nil ); + + if ( _gbCdStreamAsync ) + { + if ( pChannel->nSectorsToRead != 0 ) + { + pChannel->bLocked = true; + + ASSERT( pChannel->pDoneSemaphore != nil ); + + // Deadlock fix 1 +#ifdef FIX_BUGS + // This is while loop on Posix streamer, for spurious wakeups + if (pChannel->bLocked && pChannel->nSectorsToRead != 0){ + WaitForSingleObject(pChannel->pDoneSemaphore, INFINITE); + } + pChannel->bLocked = false; +#else + WaitForSingleObject(pChannel->pDoneSemaphore, INFINITE); +#endif + } + + pChannel->bReading = false; + + return pChannel->nStatus; + } + + DWORD NumberOfBytesTransferred; + + if ( _gbCdStreamOverlapped && pChannel->hFile ) + { + ASSERT(pChannel->hFile != nil ); + // Beware: This is blocking call (because of last parameter) + if ( GetOverlappedResult(pChannel->hFile, &pChannel->Overlapped, &NumberOfBytesTransferred, TRUE) ) + return STREAM_NONE; + else + return STREAM_ERROR; + } + + return STREAM_NONE; +} + +void +AddToQueue(Queue *queue, int32 item) +{ + ASSERT( queue != nil ); + ASSERT( queue->items != nil ); + queue->items[queue->tail] = item; + + queue->tail = (queue->tail + 1) % queue->size; + + if ( queue->head == queue->tail ) + debug("Queue is full\n"); +} + +int32 +GetFirstInQueue(Queue *queue) +{ + ASSERT( queue != nil ); + if ( queue->head == queue->tail ) + return -1; + + ASSERT( queue->items != nil ); + return queue->items[queue->head]; +} + +void +RemoveFirstInQueue(Queue *queue) +{ + ASSERT( queue != nil ); + if ( queue->head == queue->tail ) + { + debug("Queue is empty\n"); + return; + } + + queue->head = (queue->head + 1) % queue->size; +} + +DWORD +WINAPI CdStreamThread(LPVOID lpThreadParameter) +{ + debug("Created cdstream thread\n"); + + while ( true ) + { + WaitForSingleObject(gCdStreamSema, INFINITE); + + int32 channel = GetFirstInQueue(&gChannelRequestQ); + ASSERT( channel < gNumChannels ); + + CdReadInfo *pChannel = &gpReadInfo[channel]; + ASSERT( pChannel != nil ); + + pChannel->bReading = true; + + if ( pChannel->nStatus == STREAM_NONE ) + { + if ( _gbCdStreamOverlapped ) + { + pChannel->Overlapped.Offset = pChannel->nSectorOffset * CDSTREAM_SECTOR_SIZE; + + ASSERT(pChannel->hFile != nil ); + ASSERT(pChannel->pBuffer != nil ); + + DWORD NumberOfBytesTransferred; + + if ( ReadFile(pChannel->hFile, + pChannel->pBuffer, + pChannel->nSectorsToRead * CDSTREAM_SECTOR_SIZE, + NULL, + &pChannel->Overlapped) ) + { + pChannel->nStatus = STREAM_NONE; + } + // Beware: This is blocking call (because of last parameter) + else if ( GetLastError() == ERROR_IO_PENDING + && GetOverlappedResult(pChannel->hFile, &pChannel->Overlapped, &NumberOfBytesTransferred, TRUE) ) + { + pChannel->nStatus = STREAM_NONE; + } + else + { + pChannel->nStatus = STREAM_ERROR; + } + } + else + { + ASSERT(pChannel->hFile != nil ); + ASSERT(pChannel->pBuffer != nil ); + + SetFilePointer(pChannel->hFile, pChannel->nSectorOffset * CDSTREAM_SECTOR_SIZE, nil, FILE_BEGIN); + + DWORD NumberOfBytesRead; + if ( ReadFile(pChannel->hFile, + pChannel->pBuffer, + pChannel->nSectorsToRead * CDSTREAM_SECTOR_SIZE, + &NumberOfBytesRead, + NULL) ) + { + pChannel->nStatus = STREAM_NONE; + } + } + } + + RemoveFirstInQueue(&gChannelRequestQ); + + pChannel->nSectorsToRead = 0; + + if ( pChannel->bLocked ) + { + ASSERT( pChannel->pDoneSemaphore != nil ); + // Deadlock fix 2 +#ifdef FIX_BUGS + pChannel->bLocked = 0; +#endif + ReleaseSemaphore(pChannel->pDoneSemaphore, 1, NULL); + } + + pChannel->bReading = false; + } +} + +bool +CdStreamAddImage(char const *path) +{ + ASSERT(path != nil); + ASSERT(gNumImages < MAX_CDIMAGES); + + SetLastError(0); + + gImgFiles[gNumImages] = CreateFile(path, + GENERIC_READ, + FILE_SHARE_READ, + nil, + OPEN_EXISTING, + _gdwCdStreamFlags | FILE_FLAG_RANDOM_ACCESS | FILE_ATTRIBUTE_READONLY, + nil); + + ASSERT( gImgFiles[gNumImages] != nil ); + if ( gImgFiles[gNumImages] == NULL ) + return false; + + strcpy(gCdImageNames[gNumImages], path); + + gNumImages++; + + return true; +} + +char * +CdStreamGetImageName(int32 cd) +{ + ASSERT(cd < MAX_CDIMAGES); + if ( gImgFiles[cd] != nil ) + return gCdImageNames[cd]; + + return nil; +} + +void +CdStreamRemoveImages(void) +{ + for ( int32 i = 0; i < gNumChannels; i++ ) + CdStreamSync(i); + + for ( int32 i = 0; i < gNumImages; i++ ) + { + SetLastError(0); + + CloseHandle(gImgFiles[i]); + gImgFiles[i] = nil; + } + + gNumImages = 0; +} + +int32 +CdStreamGetNumImages(void) +{ + return gNumImages; +} +#endif diff --git a/src/miami/core/CdStream.h b/src/miami/core/CdStream.h new file mode 100644 index 00000000..0a84d01a --- /dev/null +++ b/src/miami/core/CdStream.h @@ -0,0 +1,59 @@ +#pragma once +#include + +#define CDSTREAM_SECTOR_SIZE 2048 + +#define _GET_INDEX(a) (a >> 24) +#define _GET_OFFSET(a) (a & 0xFFFFFF) + +enum +{ + STREAM_NONE = uint8( 0), + STREAM_SUCCESS = uint8( 1), + STREAM_READING = uint8(-1), // 0xFF, + STREAM_ERROR = uint8(-2), // 0xFE, + STREAM_ERROR_NOCD = uint8(-3), // 0xFD, + STREAM_ERROR_WRONGCD = uint8(-4), // 0xFC, + STREAM_ERROR_OPENCD = uint8(-5), // 0xFB, + STREAM_WAITING = uint8(-6) // 0xFA, +}; + +struct Queue +{ + int32 *items; + int32 head; + int32 tail; + int32 size; +}; + +VALIDATE_SIZE(Queue, 0x10); + +void CdStreamInitThread(void); +void CdStreamInit(int32 numChannels); +uint32 GetGTA3ImgSize(void); +void CdStreamShutdown(void); +int32 CdStreamRead(int32 channel, void *buffer, uint32 offset, uint32 size); +int32 CdStreamGetStatus(int32 channel); +int32 CdStreamGetLastPosn(void); +int32 CdStreamSync(int32 channel); +void AddToQueue(Queue *queue, int32 item); +int32 GetFirstInQueue(Queue *queue); +void RemoveFirstInQueue(Queue *queue); +bool CdStreamAddImage(char const *path); +char *CdStreamGetImageName(int32 cd); +void CdStreamRemoveImages(void); +int32 CdStreamGetNumImages(void); + +struct AudioReadCmd { + void* dest; + int fd; + size_t size; + size_t seek; + std::function callback; +}; +void CdStreamQueueAudioRead(int fd, void* pBuffer, size_t bytes, size_t seek, std::function callback = nullptr); +void CdStreamDiscardAudioRead(int fd); + +#ifdef FLUSHABLE_STREAMING +extern bool flushStream[MAX_CDCHANNELS]; +#endif diff --git a/src/miami/core/CdStreamDC.cpp b/src/miami/core/CdStreamDC.cpp new file mode 100644 index 00000000..ab83f872 --- /dev/null +++ b/src/miami/core/CdStreamDC.cpp @@ -0,0 +1,738 @@ +#if defined RW_DC +#include "common.h" +#include "crossplatform.h" +#include +#include +#if defined(DC_SIM) +#include +#include +#endif +#include +#include +#include +// #include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +#include +#endif + +#include "CdStream.h" +#include "rwcore.h" +#include "MemoryMgr.h" + +#define CDDEBUG(f, ...) debug ("%s: " f "\n", "cdvd_stream", ## __VA_ARGS__) +#define CDTRACE(f, ...) printf("%s: " f "\n", "cdvd_stream", ## __VA_ARGS__) + +#ifdef FLUSHABLE_STREAMING +bool flushStream[MAX_CDCHANNELS]; +#endif + +#if defined(DC_SIM) +#ifdef USE_UNNAMED_SEM + +#define RE3_SEM_OPEN(name, ...) re3_sem_open() +sem_t* +re3_sem_open(void) +{ + sem_t* sem = (sem_t*)malloc(sizeof(sem_t)); + if (sem_init(sem, 0, 1) == -1) { + sem = SEM_FAILED; + } + + return sem; +} + +#define RE3_SEM_CLOSE(sem, format, ...) re3_sem_close(sem) +void +re3_sem_close(sem_t* sem) +{ + sem_destroy(sem); + free(sem); +} + +#else +#define RE3_SEM_OPEN re3_sem_open +sem_t* +re3_sem_open(const char* format, ...) +{ + char semName[21]; + va_list va; + va_start(va, format); + vsprintf(semName, format, va); + + return sem_open(semName, O_CREAT, 0644, 1); +} + +#define RE3_SEM_CLOSE re3_sem_close +void +re3_sem_close(sem_t* sem, const char* format, ...) +{ + sem_close(sem); + + char semName[21]; + va_list va; + va_start(va, format); + vsprintf(semName, format, va); + + sem_unlink(semName); +} + +#endif +#else +#define sem_t semaphore_t +#define SEM_FAILED ((semaphore_t*)-1) + + +int sem_post(sem_t *sem) { + return sem_signal(sem); +} + +#define RE3_SEM_OPEN re3_sem_open +sem_t* +re3_sem_open(const char* format, ...) +{ + sem_t* rv = (sem_t*)malloc(sizeof(sem_t)); + assert(sem_init(rv, 1) == 0); + return rv; +} + +#define RE3_SEM_CLOSE re3_sem_close +void +re3_sem_close(sem_t* sem, const char* format, ...) +{ + assert(sem_destroy(sem) == 0); +} +#endif + +struct CdReadInfo +{ + uint32 nSectorOffset; + uint32 nSectorsToRead; + void *pBuffer; + bool bLocked; + bool bReading; + int32 nStatus; +#ifdef ONE_THREAD_PER_CHANNEL + int8 nThreadStatus; // 0: created 1:priority set up 2:abort now + pthread_t pChannelThread; + sem_t *pStartSemaphore; +#endif + sem_t *pDoneSemaphore; // used for CdStreamSync + int32 hFile; +}; + +char gCdImageNames[MAX_CDIMAGES+1][64]; +int32 gNumImages; +int32 gNumChannels; + +int32 gImgFiles[MAX_CDIMAGES]; // -1: error 0:unused otherwise: fd +char *gImgNames[MAX_CDIMAGES]; + +#ifndef ONE_THREAD_PER_CHANNEL +pthread_t _gCdStreamThread; +sem_t *gCdStreamSema; // released when we have new thing to read(so channel is set) +int8 gCdStreamThreadStatus; // 0: created 1:priority set up 2:abort now +Queue gChannelRequestQ; +bool _gbCdStreamOverlapped; +#endif + +CdReadInfo *gpReadInfo; + +int32 lastPosnRead; + +int _gdwCdStreamFlags; + +void *CdStreamThread(void* channelId); + +void +CdStreamInitThread(void) +{ + int status; +#ifndef ONE_THREAD_PER_CHANNEL + gChannelRequestQ.items = (int32 *)calloc(gNumChannels + 1, sizeof(int32)); + gChannelRequestQ.head = 0; + gChannelRequestQ.tail = 0; + gChannelRequestQ.size = gNumChannels + 1; + ASSERT(gChannelRequestQ.items != nil ); + gCdStreamSema = RE3_SEM_OPEN("/semaphore_cd_stream"); + + + if (gCdStreamSema == SEM_FAILED) { + CDTRACE("failed to create stream semaphore"); + ASSERT(0); + return; + } +#endif + + if ( gNumChannels > 0 ) + { + for ( int32 i = 0; i < gNumChannels; i++ ) + { + gpReadInfo[i].pDoneSemaphore = RE3_SEM_OPEN("/semaphore_done%d", i); + + if (gpReadInfo[i].pDoneSemaphore == SEM_FAILED) + { + CDTRACE("failed to create sync semaphore"); + ASSERT(0); + return; + } + +#ifdef ONE_THREAD_PER_CHANNEL + gpReadInfo[i].pStartSemaphore = RE3_SEM_OPEN("/semaphore_start%d", i); + + if (gpReadInfo[i].pStartSemaphore == SEM_FAILED) + { + CDTRACE("failed to create start semaphore"); + ASSERT(0); + return; + } + gpReadInfo[i].nThreadStatus = 0; + int *channelI = (int*)malloc(sizeof(int)); + *channelI = i; + status = pthread_create(&gpReadInfo[i].pChannelThread, NULL, CdStreamThread, (void*)channelI); + + if (status == -1) + { + CDTRACE("failed to create sync thread"); + ASSERT(0); + return; + } +#endif + } + } + +#ifndef ONE_THREAD_PER_CHANNEL + debug("Using one streaming thread for all channels\n"); + gCdStreamThreadStatus = 0; + status = pthread_create(&_gCdStreamThread, NULL, CdStreamThread, nil); + + if (status == -1) + { + CDTRACE("failed to create sync thread"); + ASSERT(0); + return; + } +#else + debug("Using separate streaming threads for each channel\n"); +#endif +} + +void +CdStreamInit(int32 numChannels) +{ + // struct statvfs fsInfo; + + // if((statvfs("models/gta3.img", &fsInfo)) < 0) + // { + // CDTRACE("can't get filesystem info"); + // ASSERT(0); + // return; + // } + // RwUInt32 block_size = 2048; +#ifdef __linux__ + _gdwCdStreamFlags = O_RDONLY | O_NOATIME; +#else + _gdwCdStreamFlags = O_RDONLY; +#endif + // People say it's slower +/* + if ( fsInfo.f_bsize <= CDSTREAM_SECTOR_SIZE ) + { + _gdwCdStreamFlags |= O_DIRECT; + debug("Using no buffered loading for streaming\n"); + } +*/ + // void *pBuffer = (void *)RwMallocAlign(CDSTREAM_SECTOR_SIZE, (RwUInt32)block_size); + // ASSERT( pBuffer != nil ); + + gNumImages = 0; + + gNumChannels = numChannels; + ASSERT( gNumChannels != 0 ); + + gpReadInfo = (CdReadInfo *)calloc(numChannels, sizeof(CdReadInfo)); + ASSERT( gpReadInfo != nil ); + + CDDEBUG("read info %p", gpReadInfo); + + CdStreamInitThread(); + + // ASSERT( pBuffer != nil ); + // RwFreeAlign(pBuffer); +} + +uint32 +GetGTA3ImgSize(void) +{ + ASSERT( gImgFiles[0] > 0 ); + struct stat statbuf; + + char path[PATH_MAX]; + realpath(gImgNames[0], path); + if (stat(path, &statbuf) == -1) { + // Try case-insensitivity + char* real = casepath(gImgNames[0], false); + if (real) + { + realpath(real, path); + free(real); + if (stat(path, &statbuf) != -1) + goto ok; + } + + CDTRACE("can't get size of gta3.img"); + ASSERT(0); + return 0; + } + ok: + return (uint32)statbuf.st_size; +} + +void +CdStreamShutdown(void) +{ + // Destroying semaphores and free(gpReadInfo) will be done at threads +#ifndef ONE_THREAD_PER_CHANNEL + gCdStreamThreadStatus = 2; + sem_post(gCdStreamSema); + pthread_join(_gCdStreamThread, nil); +#else + for ( int32 i = 0; i < gNumChannels; i++ ) { + gpReadInfo[i].nThreadStatus = 2; + sem_post(gpReadInfo[i].pStartSemaphore); + pthread_join(gpReadInfo[i].pChannelThread, nil); + } +#endif +} + + +int32 +CdStreamRead(int32 channel, void *buffer, uint32 offset, uint32 size) +{ + ASSERT( channel < gNumChannels ); + ASSERT( buffer != nil ); + + lastPosnRead = size + offset; + + ASSERT( _GET_INDEX(offset) < MAX_CDIMAGES ); + int32 hImage = gImgFiles[_GET_INDEX(offset)]; + ASSERT( hImage > 0 ); + + CdReadInfo *pChannel = &gpReadInfo[channel]; + ASSERT( pChannel != nil ); + + if ( pChannel->nSectorsToRead != 0 || pChannel->bReading ) { + if (pChannel->hFile == hImage - 1 && pChannel->nSectorOffset == _GET_OFFSET(offset) && pChannel->nSectorsToRead >= size) + return STREAM_SUCCESS; +#ifdef FLUSHABLE_STREAMING + flushStream[channel] = 1; + CdStreamSync(channel); +#else + return STREAM_NONE; +#endif + } + + pChannel->hFile = hImage - 1; + pChannel->nStatus = STREAM_NONE; + pChannel->nSectorOffset = _GET_OFFSET(offset); + pChannel->nSectorsToRead = size; + pChannel->pBuffer = buffer; + pChannel->bLocked = 0; + +#ifndef ONE_THREAD_PER_CHANNEL + AddToQueue(&gChannelRequestQ, channel); + if ( sem_post(gCdStreamSema) != 0 ) + printf("Signal Sema Error\n"); +#else + if ( sem_post(pChannel->pStartSemaphore) != 0 ) + printf("Signal Sema Error\n"); +#endif + + return STREAM_SUCCESS; +} + +int32 +CdStreamGetStatus(int32 channel) +{ + ASSERT( channel < gNumChannels ); + CdReadInfo *pChannel = &gpReadInfo[channel]; + ASSERT( pChannel != nil ); + +#ifdef ONE_THREAD_PER_CHANNEL + if (pChannel->nThreadStatus == 2) + return STREAM_NONE; +#else + if (gCdStreamThreadStatus == 2) + return STREAM_NONE; +#endif + + if ( pChannel->bReading ) + return STREAM_READING; + + if ( pChannel->nSectorsToRead != 0 ) + return STREAM_WAITING; + + if ( pChannel->nStatus != STREAM_NONE ) + { + int32 status = pChannel->nStatus; + pChannel->nStatus = STREAM_NONE; + + return status; + } + + return STREAM_NONE; +} + +int32 +CdStreamGetLastPosn(void) +{ + return lastPosnRead; +} + +// wait for channel to finish reading +int32 +CdStreamSync(int32 channel) +{ + ASSERT( channel < gNumChannels ); + CdReadInfo *pChannel = &gpReadInfo[channel]; + ASSERT( pChannel != nil ); + +#ifdef FLUSHABLE_STREAMING + if (flushStream[channel]) { + pChannel->nSectorsToRead = 0; +#ifdef ONE_THREAD_PER_CHANNEL + // This cancels the operation in the middle of read(), if any, does nothing otherwise + pthread_kill(pChannel->pChannelThread, SIGUSR1); + if (pChannel->bReading) { + pChannel->bLocked = true; +#else + if (pChannel->bReading) { + pChannel->bLocked = true; + // This cancels the operation in the middle of read(), if any, does nothing otherwise + pthread_kill(_gCdStreamThread, SIGUSR1); +#endif + while (pChannel->bLocked) + sem_wait(pChannel->pDoneSemaphore); + } + pChannel->bReading = false; + flushStream[channel] = false; + return STREAM_NONE; + } +#endif + + if ( pChannel->nSectorsToRead != 0 ) + { + pChannel->bLocked = true; + while (pChannel->bLocked && pChannel->nSectorsToRead != 0){ + sem_wait(pChannel->pDoneSemaphore); + } + pChannel->bLocked = false; + } + + pChannel->bReading = false; + + return pChannel->nStatus; +} + +void +AddToQueue(Queue *queue, int32 item) +{ + ASSERT( queue != nil ); + ASSERT( queue->items != nil ); + queue->items[queue->tail] = item; + + queue->tail = (queue->tail + 1) % queue->size; + + if ( queue->head == queue->tail ) + debug("Queue is full\n"); +} + +int32 +GetFirstInQueue(Queue *queue) +{ + ASSERT( queue != nil ); + if ( queue->head == queue->tail ) + return -1; + + ASSERT( queue->items != nil ); + return queue->items[queue->head]; +} + +void +RemoveFirstInQueue(Queue *queue) +{ + ASSERT( queue != nil ); + if ( queue->head == queue->tail ) + { + debug("Queue is empty\n"); + return; + } + + queue->head = (queue->head + 1) % queue->size; +} + +std::vector pendingAudioReads; +#if !defined(DC_SH4) +std::mutex pendingAudioReadsMutex; +#endif +// Will replace a previous read request for the same file descriptor +void CdStreamQueueAudioRead(int fd, void* pBuffer, size_t bytes, size_t seek, std::function callback) { + AudioReadCmd cmd = { pBuffer, fd, bytes, seek}; + if (!callback) { + cmd.callback = [](AudioReadCmd* cmd){ + lseek(cmd->fd, cmd->seek, SEEK_SET); + read(cmd->fd, cmd->dest, cmd->size); + }; + } else { + cmd.callback = callback; + } + { + #if !defined(DC_SH4) + std::lock_guard lock(pendingAudioReadsMutex); + #else + auto mask = irq_disable(); + #endif + for (auto it = pendingAudioReads.rbegin(); it != pendingAudioReads.rend(); ++it) { + if (it->fd == -1 || it->fd == fd) { + *it = cmd; + goto out; + } + } + pendingAudioReads.push_back(cmd); + out: + #if !defined(DC_SH4) + ; + #else + irq_restore(mask); + #endif + } + sem_post(gCdStreamSema); +} + + +void CdStreamDiscardAudioRead(int fd) { + #if !defined(DC_SH4) + std::lock_guard lock(pendingAudioReadsMutex); + #else + auto mask = irq_disable(); + #endif + + for (auto &pending: pendingAudioReads) { + if (pending.fd == fd) { + pending.fd = -1; + } + } + #if defined(DC_SH4) + irq_restore(mask); + #endif +} + +AudioReadCmd CdStreamNextAudioRead() { + #if !defined(DC_SH4) + std::lock_guard lock(pendingAudioReadsMutex); + #else + auto mask = irq_disable(); + #endif + AudioReadCmd cmd = { nil, -1, 0, 0}; + for (auto &pending: pendingAudioReads) { + if (pending.fd != -1) { + cmd = pending; + pending.fd = -1; + break; + } + } + #if defined(DC_SH4) + irq_restore(mask); + #endif + return cmd; +} + +int read_loop(int fd, void* pBuffer, size_t bytes) { + size_t total_read = 0; + while (total_read < bytes) { + size_t to_read = bytes - total_read; + if (to_read > 64 * 1024) + to_read = 64 * 1024; + ssize_t read_bytes = read(fd, (char*)pBuffer + total_read, to_read); + if (read_bytes == -1) { + return -1; + } + total_read += read_bytes; + auto cmd = CdStreamNextAudioRead(); + while (cmd.fd != -1) { + cmd.callback(&cmd); + cmd = CdStreamNextAudioRead(); + } + } + return total_read; +} +void *CdStreamThread(void *param) +{ + debug("Created cdstream thread\n"); + +#ifndef ONE_THREAD_PER_CHANNEL + while (gCdStreamThreadStatus != 2) { + sem_wait(gCdStreamSema); + + auto cmd = CdStreamNextAudioRead(); + while (cmd.fd != -1) { + cmd.callback(&cmd); + cmd = CdStreamNextAudioRead(); + } + + int32 channel = GetFirstInQueue(&gChannelRequestQ); + + // spurious wakeup + if (channel == -1) + continue; +#else + int channel = *((int*)param); + while (gpReadInfo[channel].nThreadStatus != 2){ + sem_wait(gpReadInfo[channel].pStartSemaphore); +#endif + + CdReadInfo *pChannel = &gpReadInfo[channel]; + ASSERT( pChannel != nil ); + + // spurious wakeup or we sent interrupt signal for flushing + if(pChannel->nSectorsToRead == 0) + continue; + + pChannel->bReading = true; + + // Not standard POSIX :shrug: +#ifdef __linux__ +#ifdef ONE_THREAD_PER_CHANNEL + if (gpReadInfo[channel].nThreadStatus == 0){ + gpReadInfo[channel].nThreadStatus = 1; +#else + if (gCdStreamThreadStatus == 0){ + gCdStreamThreadStatus = 1; +#endif + pid_t tid = syscall(SYS_gettid); + int ret = setpriority(PRIO_PROCESS, tid, getpriority(PRIO_PROCESS, getpid()) + 1); + } +#endif + if ( pChannel->nStatus == STREAM_NONE ) + { + ASSERT(pChannel->hFile >= 0); + ASSERT(pChannel->pBuffer != nil ); + + lseek(pChannel->hFile, (size_t)pChannel->nSectorOffset * (size_t)CDSTREAM_SECTOR_SIZE, SEEK_SET); + if (read_loop(pChannel->hFile, pChannel->pBuffer, pChannel->nSectorsToRead * CDSTREAM_SECTOR_SIZE) == -1) { + // pChannel->nSectorsToRead == 0 at this point means we wanted to flush channel + // STREAM_WAITING is a little hack to make CStreaming not process this data + pChannel->nStatus = pChannel->nSectorsToRead == 0 ? STREAM_WAITING : STREAM_ERROR; + } else { + pChannel->nStatus = STREAM_NONE; + } + } + +#ifndef ONE_THREAD_PER_CHANNEL + RemoveFirstInQueue(&gChannelRequestQ); +#endif + + pChannel->nSectorsToRead = 0; + if ( pChannel->bLocked ) + { + pChannel->bLocked = 0; + sem_post(pChannel->pDoneSemaphore); + } + pChannel->bReading = false; + } + char semName[20]; +#ifndef ONE_THREAD_PER_CHANNEL + for ( int32 i = 0; i < gNumChannels; i++ ) + { + RE3_SEM_CLOSE(gpReadInfo[i].pDoneSemaphore, "/semaphore_done%d", i); + } + RE3_SEM_CLOSE(gCdStreamSema, "/semaphore_cd_stream"); + free(gChannelRequestQ.items); +#else + RE3_SEM_CLOSE(gpReadInfo[channel].pStartSemaphore, "/semaphore_start%d", channel); + + RE3_SEM_CLOSE(gpReadInfo[channel].pDoneSemaphore, "/semaphore_done%d", channel); +#endif + if (gpReadInfo) + free(gpReadInfo); + gpReadInfo = nil; + pthread_exit(nil); + return NULL; +} + +bool +CdStreamAddImage(char const *path) +{ + ASSERT(path != nil); + ASSERT(gNumImages < MAX_CDIMAGES); + + gImgFiles[gNumImages] = open(path, _gdwCdStreamFlags); + + // Fix case sensitivity and backslashes. + if (gImgFiles[gNumImages] == -1) { + char* real = casepath(path, false); + if (real) + { + gImgFiles[gNumImages] = open(real, _gdwCdStreamFlags); + free(real); + } + } + + if ( gImgFiles[gNumImages] == -1 ) { + assert(false); + return false; + } + + gImgNames[gNumImages] = strdup(path); + gImgFiles[gNumImages]++; // because -1: error 0: not used + + strcpy(gCdImageNames[gNumImages], path); + + gNumImages++; + + return true; +} + +char * +CdStreamGetImageName(int32 cd) +{ + ASSERT(cd < MAX_CDIMAGES); + if ( gImgFiles[cd] > 0) + return gCdImageNames[cd]; + + return nil; +} + +void +CdStreamRemoveImages(void) +{ + for ( int32 i = 0; i < gNumChannels; i++ ) { +#ifdef FLUSHABLE_STREAMING + flushStream[i] = 1; +#endif + CdStreamSync(i); + } + + for ( int32 i = 0; i < gNumImages; i++ ) + { + close(gImgFiles[i] - 1); + free(gImgNames[i]); + gImgFiles[i] = 0; + } + + gNumImages = 0; +} + +int32 +CdStreamGetNumImages(void) +{ + return gNumImages; +} +#endif diff --git a/src/miami/core/CdStreamPosix.cpp b/src/miami/core/CdStreamPosix.cpp new file mode 100644 index 00000000..bc9129eb --- /dev/null +++ b/src/miami/core/CdStreamPosix.cpp @@ -0,0 +1,604 @@ +#ifndef _WIN32 +#include "common.h" +#include "crossplatform.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +#include +#endif + +#include "CdStream.h" +#include "rwcore.h" +#include "MemoryMgr.h" + +#define CDDEBUG(f, ...) debug ("%s: " f "\n", "cdvd_stream", ## __VA_ARGS__) +#define CDTRACE(f, ...) printf("%s: " f "\n", "cdvd_stream", ## __VA_ARGS__) + +#ifdef FLUSHABLE_STREAMING +bool flushStream[MAX_CDCHANNELS]; +#endif + +#ifdef USE_UNNAMED_SEM + +#define RE3_SEM_OPEN(name, ...) re3_sem_open() +sem_t* +re3_sem_open(void) +{ + sem_t* sem = (sem_t*)malloc(sizeof(sem_t)); + if (sem_init(sem, 0, 1) == -1) { + sem = SEM_FAILED; + } + + return sem; +} + +#define RE3_SEM_CLOSE(sem, format, ...) re3_sem_close(sem) +void +re3_sem_close(sem_t* sem) +{ + sem_destroy(sem); + free(sem); +} + +#else + +#define RE3_SEM_OPEN re3_sem_open +sem_t* +re3_sem_open(const char* format, ...) +{ + char semName[21]; + va_list va; + va_start(va, format); + vsprintf(semName, format, va); + + return sem_open(semName, O_CREAT, 0644, 1); +} + +#define RE3_SEM_CLOSE re3_sem_close +void +re3_sem_close(sem_t* sem, const char* format, ...) +{ + sem_close(sem); + + char semName[21]; + va_list va; + va_start(va, format); + vsprintf(semName, format, va); + + sem_unlink(semName); +} + +#endif + +struct CdReadInfo +{ + uint32 nSectorOffset; + uint32 nSectorsToRead; + void *pBuffer; + bool bLocked; + bool bReading; + int32 nStatus; +#ifdef ONE_THREAD_PER_CHANNEL + int8 nThreadStatus; // 0: created 1:priority set up 2:abort now + pthread_t pChannelThread; + sem_t *pStartSemaphore; +#endif + sem_t *pDoneSemaphore; // used for CdStreamSync + int32 hFile; +}; + +char gCdImageNames[MAX_CDIMAGES+1][64]; +int32 gNumImages; +int32 gNumChannels; + +int32 gImgFiles[MAX_CDIMAGES]; // -1: error 0:unused otherwise: fd +char *gImgNames[MAX_CDIMAGES]; + +#ifndef ONE_THREAD_PER_CHANNEL +pthread_t _gCdStreamThread; +sem_t *gCdStreamSema; // released when we have new thing to read(so channel is set) +int8 gCdStreamThreadStatus; // 0: created 1:priority set up 2:abort now +Queue gChannelRequestQ; +bool _gbCdStreamOverlapped; +#endif + +CdReadInfo *gpReadInfo; + +int32 lastPosnRead; + +int _gdwCdStreamFlags; + +void *CdStreamThread(void* channelId); + +void +CdStreamInitThread(void) +{ + int status; +#ifndef ONE_THREAD_PER_CHANNEL + gChannelRequestQ.items = (int32 *)calloc(gNumChannels + 1, sizeof(int32)); + gChannelRequestQ.head = 0; + gChannelRequestQ.tail = 0; + gChannelRequestQ.size = gNumChannels + 1; + ASSERT(gChannelRequestQ.items != nil ); + gCdStreamSema = RE3_SEM_OPEN("/semaphore_cd_stream"); + + + if (gCdStreamSema == SEM_FAILED) { + CDTRACE("failed to create stream semaphore"); + ASSERT(0); + return; + } +#endif + + if ( gNumChannels > 0 ) + { + for ( int32 i = 0; i < gNumChannels; i++ ) + { + gpReadInfo[i].pDoneSemaphore = RE3_SEM_OPEN("/semaphore_done%d", i); + + if (gpReadInfo[i].pDoneSemaphore == SEM_FAILED) + { + CDTRACE("failed to create sync semaphore"); + ASSERT(0); + return; + } + +#ifdef ONE_THREAD_PER_CHANNEL + gpReadInfo[i].pStartSemaphore = RE3_SEM_OPEN("/semaphore_start%d", i); + + if (gpReadInfo[i].pStartSemaphore == SEM_FAILED) + { + CDTRACE("failed to create start semaphore"); + ASSERT(0); + return; + } + gpReadInfo[i].nThreadStatus = 0; + int *channelI = (int*)malloc(sizeof(int)); + *channelI = i; + status = pthread_create(&gpReadInfo[i].pChannelThread, NULL, CdStreamThread, (void*)channelI); + + if (status == -1) + { + CDTRACE("failed to create sync thread"); + ASSERT(0); + return; + } +#endif + } + } + +#ifndef ONE_THREAD_PER_CHANNEL + debug("Using one streaming thread for all channels\n"); + gCdStreamThreadStatus = 0; + status = pthread_create(&_gCdStreamThread, NULL, CdStreamThread, nil); + + if (status == -1) + { + CDTRACE("failed to create sync thread"); + ASSERT(0); + return; + } +#else + debug("Using separate streaming threads for each channel\n"); +#endif +} + +void +CdStreamInit(int32 numChannels) +{ + struct statvfs fsInfo; + + if((statvfs("models/gta3.img", &fsInfo)) < 0) + { + CDTRACE("can't get filesystem info"); + ASSERT(0); + return; + } +#ifdef __linux__ + _gdwCdStreamFlags = O_RDONLY | O_NOATIME; +#else + _gdwCdStreamFlags = O_RDONLY; +#endif + // People say it's slower +/* + if ( fsInfo.f_bsize <= CDSTREAM_SECTOR_SIZE ) + { + _gdwCdStreamFlags |= O_DIRECT; + debug("Using no buffered loading for streaming\n"); + } +*/ + void *pBuffer = (void *)RwMallocAlign(CDSTREAM_SECTOR_SIZE, (RwUInt32)fsInfo.f_bsize); + ASSERT( pBuffer != nil ); + + gNumImages = 0; + + gNumChannels = numChannels; + ASSERT( gNumChannels != 0 ); + + gpReadInfo = (CdReadInfo *)calloc(numChannels, sizeof(CdReadInfo)); + ASSERT( gpReadInfo != nil ); + + CDDEBUG("read info %p", gpReadInfo); + + CdStreamInitThread(); + + ASSERT( pBuffer != nil ); + RwFreeAlign(pBuffer); +} + +uint32 +GetGTA3ImgSize(void) +{ + ASSERT( gImgFiles[0] > 0 ); + struct stat statbuf; + + char path[PATH_MAX]; + realpath(gImgNames[0], path); + if (stat(path, &statbuf) == -1) { + // Try case-insensitivity + char* real = casepath(gImgNames[0], false); + if (real) + { + realpath(real, path); + free(real); + if (stat(path, &statbuf) != -1) + goto ok; + } + + CDTRACE("can't get size of gta3.img"); + ASSERT(0); + return 0; + } + ok: + return (uint32)statbuf.st_size; +} + +void +CdStreamShutdown(void) +{ + // Destroying semaphores and free(gpReadInfo) will be done at threads +#ifndef ONE_THREAD_PER_CHANNEL + gCdStreamThreadStatus = 2; + sem_post(gCdStreamSema); + pthread_join(_gCdStreamThread, nil); +#else + for ( int32 i = 0; i < gNumChannels; i++ ) { + gpReadInfo[i].nThreadStatus = 2; + sem_post(gpReadInfo[i].pStartSemaphore); + pthread_join(gpReadInfo[i].pChannelThread, nil); + } +#endif +} + + +int32 +CdStreamRead(int32 channel, void *buffer, uint32 offset, uint32 size) +{ + ASSERT( channel < gNumChannels ); + ASSERT( buffer != nil ); + + lastPosnRead = size + offset; + + ASSERT( _GET_INDEX(offset) < MAX_CDIMAGES ); + int32 hImage = gImgFiles[_GET_INDEX(offset)]; + ASSERT( hImage > 0 ); + + CdReadInfo *pChannel = &gpReadInfo[channel]; + ASSERT( pChannel != nil ); + + if ( pChannel->nSectorsToRead != 0 || pChannel->bReading ) { + if (pChannel->hFile == hImage - 1 && pChannel->nSectorOffset == _GET_OFFSET(offset) && pChannel->nSectorsToRead >= size) + return STREAM_SUCCESS; +#ifdef FLUSHABLE_STREAMING + flushStream[channel] = 1; + CdStreamSync(channel); +#else + return STREAM_NONE; +#endif + } + + pChannel->hFile = hImage - 1; + pChannel->nStatus = STREAM_NONE; + pChannel->nSectorOffset = _GET_OFFSET(offset); + pChannel->nSectorsToRead = size; + pChannel->pBuffer = buffer; + pChannel->bLocked = 0; + +#ifndef ONE_THREAD_PER_CHANNEL + AddToQueue(&gChannelRequestQ, channel); + if ( sem_post(gCdStreamSema) != 0 ) + printf("Signal Sema Error\n"); +#else + if ( sem_post(pChannel->pStartSemaphore) != 0 ) + printf("Signal Sema Error\n"); +#endif + + return STREAM_SUCCESS; +} + +int32 +CdStreamGetStatus(int32 channel) +{ + ASSERT( channel < gNumChannels ); + CdReadInfo *pChannel = &gpReadInfo[channel]; + ASSERT( pChannel != nil ); + +#ifdef ONE_THREAD_PER_CHANNEL + if (pChannel->nThreadStatus == 2) + return STREAM_NONE; +#else + if (gCdStreamThreadStatus == 2) + return STREAM_NONE; +#endif + + if ( pChannel->bReading ) + return STREAM_READING; + + if ( pChannel->nSectorsToRead != 0 ) + return STREAM_WAITING; + + if ( pChannel->nStatus != STREAM_NONE ) + { + int32 status = pChannel->nStatus; + pChannel->nStatus = STREAM_NONE; + + return status; + } + + return STREAM_NONE; +} + +int32 +CdStreamGetLastPosn(void) +{ + return lastPosnRead; +} + +// wait for channel to finish reading +int32 +CdStreamSync(int32 channel) +{ + ASSERT( channel < gNumChannels ); + CdReadInfo *pChannel = &gpReadInfo[channel]; + ASSERT( pChannel != nil ); + +#ifdef FLUSHABLE_STREAMING + if (flushStream[channel]) { + pChannel->nSectorsToRead = 0; +#ifdef ONE_THREAD_PER_CHANNEL + pthread_kill(pChannel->pChannelThread, SIGUSR1); + if (pChannel->bReading) { + pChannel->bLocked = true; +#else + if (pChannel->bReading) { + pChannel->bLocked = true; + pthread_kill(_gCdStreamThread, SIGUSR1); +#endif + while (pChannel->bLocked) + sem_wait(pChannel->pDoneSemaphore); + } + pChannel->bReading = false; + flushStream[channel] = false; + return STREAM_NONE; + } +#endif + + if ( pChannel->nSectorsToRead != 0 ) + { + pChannel->bLocked = true; + while (pChannel->bLocked && pChannel->nSectorsToRead != 0){ + sem_wait(pChannel->pDoneSemaphore); + } + pChannel->bLocked = false; + } + + pChannel->bReading = false; + + return pChannel->nStatus; +} + +void +AddToQueue(Queue *queue, int32 item) +{ + ASSERT( queue != nil ); + ASSERT( queue->items != nil ); + queue->items[queue->tail] = item; + + queue->tail = (queue->tail + 1) % queue->size; + + if ( queue->head == queue->tail ) + debug("Queue is full\n"); +} + +int32 +GetFirstInQueue(Queue *queue) +{ + ASSERT( queue != nil ); + if ( queue->head == queue->tail ) + return -1; + + ASSERT( queue->items != nil ); + return queue->items[queue->head]; +} + +void +RemoveFirstInQueue(Queue *queue) +{ + ASSERT( queue != nil ); + if ( queue->head == queue->tail ) + { + debug("Queue is empty\n"); + return; + } + + queue->head = (queue->head + 1) % queue->size; +} + +void *CdStreamThread(void *param) +{ + debug("Created cdstream thread\n"); + +#ifndef ONE_THREAD_PER_CHANNEL + while (gCdStreamThreadStatus != 2) { + sem_wait(gCdStreamSema); + + int32 channel = GetFirstInQueue(&gChannelRequestQ); + + // spurious wakeup + if (channel == -1) + continue; +#else + int channel = *((int*)param); + while (gpReadInfo[channel].nThreadStatus != 2){ + sem_wait(gpReadInfo[channel].pStartSemaphore); +#endif + + CdReadInfo *pChannel = &gpReadInfo[channel]; + ASSERT( pChannel != nil ); + + // spurious wakeup or we sent interrupt signal for flushing + if(pChannel->nSectorsToRead == 0) + continue; + + pChannel->bReading = true; + + // Not standard POSIX :shrug: +#ifdef __linux__ +#ifdef ONE_THREAD_PER_CHANNEL + if (gpReadInfo[channel].nThreadStatus == 0){ + gpReadInfo[channel].nThreadStatus = 1; +#else + if (gCdStreamThreadStatus == 0){ + gCdStreamThreadStatus = 1; +#endif + pid_t tid = syscall(SYS_gettid); + int ret = setpriority(PRIO_PROCESS, tid, getpriority(PRIO_PROCESS, getpid()) + 1); + } +#endif + if ( pChannel->nStatus == STREAM_NONE ) + { + ASSERT(pChannel->hFile >= 0); + ASSERT(pChannel->pBuffer != nil ); + + lseek(pChannel->hFile, (size_t)pChannel->nSectorOffset * (size_t)CDSTREAM_SECTOR_SIZE, SEEK_SET); + if (read(pChannel->hFile, pChannel->pBuffer, pChannel->nSectorsToRead * CDSTREAM_SECTOR_SIZE) == -1) { + // pChannel->nSectorsToRead == 0 at this point means we wanted to flush channel + // STREAM_WAITING is a little hack to make CStreaming not process this data + pChannel->nStatus = pChannel->nSectorsToRead == 0 ? STREAM_WAITING : STREAM_ERROR; + } else { + pChannel->nStatus = STREAM_NONE; + } + } + +#ifndef ONE_THREAD_PER_CHANNEL + RemoveFirstInQueue(&gChannelRequestQ); +#endif + + pChannel->nSectorsToRead = 0; + if ( pChannel->bLocked ) + { + pChannel->bLocked = 0; + sem_post(pChannel->pDoneSemaphore); + } + pChannel->bReading = false; + } + char semName[20]; +#ifndef ONE_THREAD_PER_CHANNEL + for ( int32 i = 0; i < gNumChannels; i++ ) + { + RE3_SEM_CLOSE(gpReadInfo[i].pDoneSemaphore, "/semaphore_done%d", i); + } + RE3_SEM_CLOSE(gCdStreamSema, "/semaphore_cd_stream"); + free(gChannelRequestQ.items); +#else + RE3_SEM_CLOSE(gpReadInfo[channel].pStartSemaphore, "/semaphore_start%d", channel); + + RE3_SEM_CLOSE(gpReadInfo[channel].pDoneSemaphore, "/semaphore_done%d", channel); +#endif + if (gpReadInfo) + free(gpReadInfo); + gpReadInfo = nil; + pthread_exit(nil); +} + +bool +CdStreamAddImage(char const *path) +{ + ASSERT(path != nil); + ASSERT(gNumImages < MAX_CDIMAGES); + + gImgFiles[gNumImages] = open(path, _gdwCdStreamFlags); + + // Fix case sensitivity and backslashes. + if (gImgFiles[gNumImages] == -1) { + char* real = casepath(path, false); + if (real) + { + gImgFiles[gNumImages] = open(real, _gdwCdStreamFlags); + free(real); + } + } + + if ( gImgFiles[gNumImages] == -1 ) { + assert(false); + return false; + } + + gImgNames[gNumImages] = strdup(path); + gImgFiles[gNumImages]++; // because -1: error 0: not used + + strcpy(gCdImageNames[gNumImages], path); + + gNumImages++; + + return true; +} + +char * +CdStreamGetImageName(int32 cd) +{ + ASSERT(cd < MAX_CDIMAGES); + if ( gImgFiles[cd] > 0) + return gCdImageNames[cd]; + + return nil; +} + +void +CdStreamRemoveImages(void) +{ + for ( int32 i = 0; i < gNumChannels; i++ ) { +#ifdef FLUSHABLE_STREAMING + flushStream[i] = 1; +#endif + CdStreamSync(i); + } + + for ( int32 i = 0; i < gNumImages; i++ ) + { + close(gImgFiles[i] - 1); + free(gImgNames[i]); + gImgFiles[i] = 0; + } + + gNumImages = 0; +} + +int32 +CdStreamGetNumImages(void) +{ + return gNumImages; +} +#endif diff --git a/src/miami/core/Clock.cpp b/src/miami/core/Clock.cpp new file mode 100644 index 00000000..a3298343 --- /dev/null +++ b/src/miami/core/Clock.cpp @@ -0,0 +1,136 @@ +#include "common.h" + +#include "Timer.h" +#include "Pad.h" +#include "Clock.h" +#include "Stats.h" +#include "VarConsole.h" + +_TODO("gbFastTime"); +bool gbFastTime; + +uint8 CClock::ms_nGameClockHours; +uint8 CClock::ms_nGameClockMinutes; +uint16 CClock::ms_nGameClockSeconds; +uint8 CClock::ms_Stored_nGameClockHours; +uint8 CClock::ms_Stored_nGameClockMinutes; +uint16 CClock::ms_Stored_nGameClockSeconds; +uint32 CClock::ms_nMillisecondsPerGameMinute; +uint32 CClock::ms_nLastClockTick; +bool CClock::ms_bClockHasBeenStored; + +#ifndef MASTER +bool gbFreezeTime; +#endif + +void +CClock::Initialise(uint32 scale) +{ + debug("Initialising CClock...\n"); + ms_nGameClockHours = 12; + ms_nGameClockMinutes = 0; + ms_nGameClockSeconds = 0; + ms_nMillisecondsPerGameMinute = scale; + ms_nLastClockTick = CTimer::GetTimeInMilliseconds(); + ms_bClockHasBeenStored = false; + debug("CClock ready\n"); +#ifndef MASTER + VarConsole.Add("Time (hour of day)", &ms_nGameClockHours, 1, 0, 23, true); + VarConsole.Add("Freeze time", &gbFreezeTime, true); +#endif +} + +void +CClock::Update(void) +{ + if(CPad::GetPad(1)->GetRightShoulder1()) + { + ms_nGameClockMinutes += 8; + ms_nLastClockTick = CTimer::GetTimeInMilliseconds(); + + if(ms_nGameClockMinutes >= 60) + { + ms_nGameClockHours++; + ms_nGameClockMinutes = 0; + if(ms_nGameClockHours >= 24) + ms_nGameClockHours = 0; + } + + } +#ifndef MASTER + else if (gbFreezeTime) + ms_nLastClockTick = CTimer::GetTimeInMilliseconds(); +#endif + else if(CTimer::GetTimeInMilliseconds() - ms_nLastClockTick > ms_nMillisecondsPerGameMinute || gbFastTime) + { + ms_nGameClockMinutes++; + ms_nLastClockTick += ms_nMillisecondsPerGameMinute; + + if ( gbFastTime ) + ms_nLastClockTick = CTimer::GetTimeInMilliseconds(); + + if(ms_nGameClockMinutes >= 60) + { + ms_nGameClockHours++; + ms_nGameClockMinutes = 0; + if(ms_nGameClockHours >= 24) + { + CStats::DaysPassed++; + ms_nGameClockHours = 0; + } + } + } + ms_nGameClockSeconds = 60 * (CTimer::GetTimeInMilliseconds() - ms_nLastClockTick) / ms_nMillisecondsPerGameMinute; +} + +void +CClock::SetGameClock(uint8 h, uint8 m) +{ + while (m >= 60) { + m -= 60; + h++; + } + ms_nGameClockMinutes = m; + while (h >= 24) + h -= 24; + ms_nGameClockHours = h; + ms_nGameClockSeconds = 0; + ms_nLastClockTick = CTimer::GetTimeInMilliseconds(); +} + +int32 +CClock::GetGameClockMinutesUntil(uint8 h, uint8 m) +{ + int32 now, then; + now = ms_nGameClockHours*60 + ms_nGameClockMinutes; + then = h*60 + m; + if(then < now) + then += 24*60; + return then-now; +} + +bool +CClock::GetIsTimeInRange(uint8 h1, uint8 h2) +{ + if(h1 > h2) + return ms_nGameClockHours >= h1 || ms_nGameClockHours < h2; + else + return ms_nGameClockHours >= h1 && ms_nGameClockHours < h2; +} + +void +CClock::StoreClock(void) +{ + ms_Stored_nGameClockHours = ms_nGameClockHours; + ms_Stored_nGameClockMinutes = ms_nGameClockMinutes; + ms_Stored_nGameClockSeconds = ms_nGameClockSeconds; + ms_bClockHasBeenStored = true; +} + +void +CClock::RestoreClock(void) +{ + ms_nGameClockHours = ms_Stored_nGameClockHours; + ms_nGameClockMinutes = ms_Stored_nGameClockMinutes; + ms_nGameClockSeconds = ms_Stored_nGameClockSeconds; +} diff --git a/src/miami/core/Clock.h b/src/miami/core/Clock.h new file mode 100644 index 00000000..a611cd50 --- /dev/null +++ b/src/miami/core/Clock.h @@ -0,0 +1,31 @@ +#pragma once + +class CClock +{ +public: + static uint8 ms_nGameClockHours; + static uint8 ms_nGameClockMinutes; + static uint16 ms_nGameClockSeconds; + static uint8 ms_Stored_nGameClockHours; + static uint8 ms_Stored_nGameClockMinutes; + static uint16 ms_Stored_nGameClockSeconds; + static uint32 ms_nMillisecondsPerGameMinute; + static uint32 ms_nLastClockTick; + static bool ms_bClockHasBeenStored; + + static void Initialise(uint32 scale); + static void Update(void); + static void SetGameClock(uint8 h, uint8 m); + static int32 GetGameClockMinutesUntil(uint8 h, uint8 m); + static bool GetIsTimeInRange(uint8 h1, uint8 h2); + static void StoreClock(void); + static void RestoreClock(void); + + static uint8 GetHours(void) { return ms_nGameClockHours; } + static uint8 GetMinutes(void) { return ms_nGameClockMinutes; } + static int16 GetSeconds(void) { return ms_nGameClockSeconds; } + + + static uint8 &GetHoursRef(void) { return ms_nGameClockHours; } + static uint8 &GetMinutesRef(void) { return ms_nGameClockMinutes; } +}; diff --git a/src/miami/core/ControllerConfig.cpp b/src/miami/core/ControllerConfig.cpp new file mode 100644 index 00000000..dbb0bd87 --- /dev/null +++ b/src/miami/core/ControllerConfig.cpp @@ -0,0 +1,2918 @@ +#define WITHDINPUT +#include "common.h" +#include "platform.h" +#include "crossplatform.h" +#include "ControllerConfig.h" +#include "Pad.h" +#include "FileMgr.h" +#include "Text.h" +#include "Font.h" +#include "Messages.h" +#include "Frontend.h" +#include "Ped.h" +#include "PlayerPed.h" +#include "Vehicle.h" +#include "World.h" +#include "ModelIndices.h" +#include "Camera.h" +#include "GenericGameStorage.h" + +CControllerConfigManager ControlsManager; + +CControllerConfigManager::CControllerConfigManager() +{ + m_bFirstCapture = false; + m_bMouseAssociated = false; + + MakeControllerActionsBlank(); + InitDefaultControlConfiguration(); + InitialiseControllerActionNameArray(); +} + +void CControllerConfigManager::MakeControllerActionsBlank() +{ +#ifdef LOAD_INI_SETTINGS + ms_padButtonsInited = 0; +#endif + for (int32 i = 0; i < MAX_CONTROLLERTYPES; i++) + { + for (int32 j = 0; j < MAX_CONTROLLERACTIONS; j++) + { + ClearSettingsAssociatedWithAction((e_ControllerAction)j, (eControllerType)i); + } + } +} + +#ifdef RW_GL3 +int MapIdToButtonId(int mapId) { + switch (mapId) { + case GLFW_GAMEPAD_BUTTON_A: // Cross + return 2; + case GLFW_GAMEPAD_BUTTON_B: // Circle + return 1; + case GLFW_GAMEPAD_BUTTON_X: // Square + return 3; + case GLFW_GAMEPAD_BUTTON_Y: // Triangle + return 4; + case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: + return 7; + case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: + return 8; + case GLFW_GAMEPAD_BUTTON_BACK: + return 9; + case GLFW_GAMEPAD_BUTTON_START: + return 12; + case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: + return 10; + case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: + return 11; + case GLFW_GAMEPAD_BUTTON_DPAD_UP: + return 13; + case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: + return 14; + case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: + return 15; + case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: + return 16; + // GLFW sends those as axes, so I added them here manually. + case 15: // Left trigger + return 5; + case 16: // Right trigger + return 6; + default: + return 0; + } +} +#endif + +int32 CControllerConfigManager::GetJoyButtonJustDown() +{ +#ifdef __DINPUT_INCLUDED__ +#ifdef FIX_BUGS + for (int32 i = 0; i < MAX_BUTTONS; i++) +#else + for (int32 i = 0; i < JOY_BUTTONS; i++) +#endif + { + if (m_NewState.rgbButtons[i] & 0x80 && !(m_OldState.rgbButtons[i] & 0x80)) + return i + 1; + } +#elif defined RW_GL3 + if (m_NewState.isGamepad) { + for (int32 i = 0; i < MAX_BUTTONS; i++) { + if (m_NewState.mappedButtons[i] && !(m_OldState.mappedButtons[i])) + return MapIdToButtonId(i); + } + } else { + for (int32 i = 0; i < Min(m_NewState.numButtons, MAX_BUTTONS); i++) { + if (m_NewState.buttons[i] && !(m_OldState.buttons[i])) + return i + 1; + } + } +#endif + return 0; +} + +void CControllerConfigManager::SaveSettings(int32 file) +{ + if (file) + { + for (int32 i = 0; i < MAX_CONTROLLERTYPES; i++) + { + for (int32 j = 0; j < MAX_CONTROLLERACTIONS; j++) + { + CFileMgr::Write(file, (char *)&ControlsManager.m_aSettings[j][i], sizeof(tControllerConfigBind)); + } + } + } +} + +void CControllerConfigManager::LoadSettings(int32 file) +{ + bool bValid = true; + int nVersion = 0; +#ifdef BIND_VEHICLE_FIREWEAPON + bool skipVehicleFireWeapon = false; +#endif + + if (file) + { + char buff[29]; + CFileMgr::Read(file, buff, sizeof(buff)); + + if (!strncmp(buff, TopLineEmptyFile, sizeof(TopLineEmptyFile)-1)) + bValid = false; + else { + CFileMgr::Seek(file, 0, 0); + CFileMgr::Read(file, (char*)&nVersion, sizeof(nVersion)); + } + } + + if (bValid && nVersion >= 3) + { + ControlsManager.MakeControllerActionsBlank(); +#ifdef BIND_VEHICLE_FIREWEAPON + skipVehicleFireWeapon = nVersion < 4; + // Set the default settings of VEHICLE_FIREWEAPON + if (skipVehicleFireWeapon) { + SetControllerKeyAssociatedWithAction(VEHICLE_FIREWEAPON, rsPADINS, KEYBOARD); + SetControllerKeyAssociatedWithAction(VEHICLE_FIREWEAPON, rsLCTRL, OPTIONAL_EXTRA); + if (m_bMouseAssociated) + SetMouseButtonAssociatedWithAction(VEHICLE_FIREWEAPON, 1); + } +#endif + + for (int32 i = 0; i < MAX_CONTROLLERTYPES; i++) + { + for (int32 j = 0; j < MAX_CONTROLLERACTIONS; j++) + { +#ifdef BIND_VEHICLE_FIREWEAPON + // Skip file read + if (skipVehicleFireWeapon && j == VEHICLE_FIREWEAPON) + continue; +#endif + CFileMgr::Read(file, (char *)&ControlsManager.m_aSettings[j][i], sizeof(tControllerConfigBind)); + } + } + } +} + +void CControllerConfigManager::InitDefaultControlConfiguration() +{ + SetControllerKeyAssociatedWithAction (VEHICLE_LOOKLEFT, rsPADEND, KEYBOARD); + SetControllerKeyAssociatedWithAction (VEHICLE_LOOKLEFT, 'Q', OPTIONAL_EXTRA); + + SetControllerKeyAssociatedWithAction (VEHICLE_LOOKRIGHT, rsPADDOWN, KEYBOARD); + SetControllerKeyAssociatedWithAction (VEHICLE_LOOKRIGHT, 'E', OPTIONAL_EXTRA); + + if ( _dwOperatingSystemVersion == OS_WIN98 ) + SetControllerKeyAssociatedWithAction(VEHICLE_HORN, rsSHIFT, OPTIONAL_EXTRA); // BUG: must be KEYBOARD ? + else + { + SetControllerKeyAssociatedWithAction(VEHICLE_HORN, rsLSHIFT, OPTIONAL_EXTRA); + SetControllerKeyAssociatedWithAction(VEHICLE_HORN, rsRSHIFT, KEYBOARD); + } + + SetControllerKeyAssociatedWithAction (VEHICLE_HANDBRAKE, rsRCTRL, KEYBOARD); + SetControllerKeyAssociatedWithAction (VEHICLE_HANDBRAKE, ' ', OPTIONAL_EXTRA); + + SetControllerKeyAssociatedWithAction (VEHICLE_ENTER_EXIT, rsENTER, KEYBOARD); + SetControllerKeyAssociatedWithAction (VEHICLE_ENTER_EXIT, 'F', OPTIONAL_EXTRA); + + SetControllerKeyAssociatedWithAction (VEHICLE_ACCELERATE, rsUP, KEYBOARD); + SetControllerKeyAssociatedWithAction (VEHICLE_ACCELERATE, 'W', OPTIONAL_EXTRA); + + SetControllerKeyAssociatedWithAction (VEHICLE_CHANGE_RADIO_STATION, rsINS, KEYBOARD); + SetControllerKeyAssociatedWithAction (VEHICLE_CHANGE_RADIO_STATION, 'R', OPTIONAL_EXTRA); + + SetControllerKeyAssociatedWithAction (VEHICLE_BRAKE, rsDOWN, KEYBOARD); + SetControllerKeyAssociatedWithAction (VEHICLE_BRAKE, 'S', OPTIONAL_EXTRA); + + SetControllerKeyAssociatedWithAction (TOGGLE_SUBMISSIONS, rsPLUS, KEYBOARD); + SetControllerKeyAssociatedWithAction (TOGGLE_SUBMISSIONS, rsCAPSLK, OPTIONAL_EXTRA); + + SetControllerKeyAssociatedWithAction (GO_LEFT, rsLEFT, KEYBOARD); + SetControllerKeyAssociatedWithAction (GO_LEFT, 'A', OPTIONAL_EXTRA); + + SetControllerKeyAssociatedWithAction (GO_RIGHT, rsRIGHT, KEYBOARD); + SetControllerKeyAssociatedWithAction (GO_RIGHT, 'D', OPTIONAL_EXTRA); + + SetControllerKeyAssociatedWithAction (GO_FORWARD, rsUP, KEYBOARD); + SetControllerKeyAssociatedWithAction (GO_FORWARD, 'W', OPTIONAL_EXTRA); + + SetControllerKeyAssociatedWithAction (GO_BACK, rsDOWN, KEYBOARD); + SetControllerKeyAssociatedWithAction (GO_BACK, 'S', OPTIONAL_EXTRA); + + SetControllerKeyAssociatedWithAction (NETWORK_TALK, 'T', KEYBOARD); + + SetControllerKeyAssociatedWithAction (PED_LOOKBEHIND, rsPADEND, KEYBOARD); + SetControllerKeyAssociatedWithAction (PED_LOOKBEHIND, rsCAPSLK, OPTIONAL_EXTRA); + + SetControllerKeyAssociatedWithAction (PED_DUCK, 'C', KEYBOARD); + + SetControllerKeyAssociatedWithAction (PED_FIREWEAPON, rsPADINS, KEYBOARD); + SetControllerKeyAssociatedWithAction (PED_FIREWEAPON, rsLCTRL, OPTIONAL_EXTRA); +#ifdef BIND_VEHICLE_FIREWEAPON + SetControllerKeyAssociatedWithAction (VEHICLE_FIREWEAPON, rsPADINS, KEYBOARD); + SetControllerKeyAssociatedWithAction (VEHICLE_FIREWEAPON, rsLCTRL, OPTIONAL_EXTRA); +#endif + SetControllerKeyAssociatedWithAction (PED_CYCLE_WEAPON_LEFT, rsPADDEL, KEYBOARD); + + SetControllerKeyAssociatedWithAction (PED_CYCLE_WEAPON_RIGHT, rsPADENTER, OPTIONAL_EXTRA); // BUG: must be KEYBOARD ? + + SetControllerKeyAssociatedWithAction (PED_LOCK_TARGET, rsDEL, KEYBOARD); + + SetControllerKeyAssociatedWithAction (PED_JUMPING, rsRCTRL, KEYBOARD); + SetControllerKeyAssociatedWithAction (PED_JUMPING, ' ', OPTIONAL_EXTRA); + + SetControllerKeyAssociatedWithAction (PED_ANSWER_PHONE, rsTAB, KEYBOARD); + + if ( _dwOperatingSystemVersion == OS_WIN98 ) + SetControllerKeyAssociatedWithAction(PED_SPRINT, rsSHIFT, OPTIONAL_EXTRA); // BUG: must be KEYBOARD ? + else + { + SetControllerKeyAssociatedWithAction(PED_SPRINT, rsLSHIFT, OPTIONAL_EXTRA); +#ifndef FIX_BUGS + SetControllerKeyAssociatedWithAction(PED_SPRINT, rsRSHIFT, OPTIONAL_EXTRA); // BUG: must be KEYBOARD +#else + SetControllerKeyAssociatedWithAction(PED_SPRINT, rsRSHIFT, KEYBOARD); +#endif + } + + SetControllerKeyAssociatedWithAction (PED_CYCLE_TARGET_LEFT, '[', KEYBOARD); + + SetControllerKeyAssociatedWithAction (PED_CYCLE_TARGET_RIGHT, ']', OPTIONAL_EXTRA); // BUG: must be KEYBOARD ? + + SetControllerKeyAssociatedWithAction (PED_CENTER_CAMERA_BEHIND_PLAYER, '#', KEYBOARD); + + SetControllerKeyAssociatedWithAction (PED_SNIPER_ZOOM_IN, rsPGUP, KEYBOARD); + SetControllerKeyAssociatedWithAction (PED_SNIPER_ZOOM_IN, 'Z', OPTIONAL_EXTRA); + + SetControllerKeyAssociatedWithAction (PED_SNIPER_ZOOM_OUT, rsPGDN, KEYBOARD); + SetControllerKeyAssociatedWithAction (PED_SNIPER_ZOOM_OUT, 'X', OPTIONAL_EXTRA); + + SetControllerKeyAssociatedWithAction (PED_1RST_PERSON_LOOK_LEFT, rsPADLEFT, KEYBOARD); + + SetControllerKeyAssociatedWithAction (PED_1RST_PERSON_LOOK_RIGHT, rsPADRIGHT, KEYBOARD); + + SetControllerKeyAssociatedWithAction (PED_1RST_PERSON_LOOK_UP, rsPADUP, KEYBOARD); + + SetControllerKeyAssociatedWithAction (PED_1RST_PERSON_LOOK_DOWN, rsPAD5, KEYBOARD); + + SetControllerKeyAssociatedWithAction (VEHICLE_TURRETLEFT, rsPADLEFT, KEYBOARD); + + SetControllerKeyAssociatedWithAction (VEHICLE_TURRETRIGHT, rsPAD5, KEYBOARD); + + SetControllerKeyAssociatedWithAction (VEHICLE_TURRETUP, rsPADPGUP, KEYBOARD); + + SetControllerKeyAssociatedWithAction (VEHICLE_TURRETDOWN, rsPADRIGHT, KEYBOARD); + + SetControllerKeyAssociatedWithAction (CAMERA_CHANGE_VIEW_ALL_SITUATIONS, rsHOME, KEYBOARD); + SetControllerKeyAssociatedWithAction (CAMERA_CHANGE_VIEW_ALL_SITUATIONS, 'V', OPTIONAL_EXTRA); + + for (int32 i = 0; i < MAX_SIMS; i++) + { + m_aSimCheckers[i][KEYBOARD] = false; + m_aSimCheckers[i][OPTIONAL_EXTRA] = false; + m_aSimCheckers[i][MOUSE] = false; + m_aSimCheckers[i][JOYSTICK] = false; + } +} + +void CControllerConfigManager::InitDefaultControlConfigMouse(CMouseControllerState const &availableButtons) +{ + if (availableButtons.LMB) + { + m_bMouseAssociated = true; + SetMouseButtonAssociatedWithAction(PED_FIREWEAPON, 1); +#ifdef BIND_VEHICLE_FIREWEAPON + SetMouseButtonAssociatedWithAction(VEHICLE_FIREWEAPON, 1); +#endif + } + + if (availableButtons.RMB) + { + SetMouseButtonAssociatedWithAction(PED_LOCK_TARGET, 3); + + SetMouseButtonAssociatedWithAction(VEHICLE_HANDBRAKE, 3); + } + + if (availableButtons.MMB) + { + SetMouseButtonAssociatedWithAction(VEHICLE_LOOKBEHIND, 2); + + SetMouseButtonAssociatedWithAction(PED_LOOKBEHIND, 2); + } + + if (availableButtons.WHEELUP || availableButtons.WHEELDN) + { + SetMouseButtonAssociatedWithAction(PED_CYCLE_WEAPON_LEFT, 4); + + SetMouseButtonAssociatedWithAction(PED_CYCLE_WEAPON_RIGHT, 5); + + SetMouseButtonAssociatedWithAction(VEHICLE_CHANGE_RADIO_STATION, 4); + + SetMouseButtonAssociatedWithAction(PED_SNIPER_ZOOM_IN, 4); + + SetMouseButtonAssociatedWithAction(PED_SNIPER_ZOOM_OUT, 5); + } +} + +#ifdef LOAD_INI_SETTINGS +uint32 CControllerConfigManager::ms_padButtonsInited = 0; +#endif + +void CControllerConfigManager::InitDefaultControlConfigJoyPad(uint32 buttons) +{ +#ifdef XINPUT + // No manual bindings for you, honey. + return; +#endif + + m_bFirstCapture = true; + + uint32 btn = buttons; + if (buttons > 16) + btn = 16; + +#ifdef LOAD_INI_SETTINGS + uint32 buttonMin = ms_padButtonsInited; + if (buttonMin >= btn) + return; + + ms_padButtonsInited = btn; + + #define IF_BTN_IN_RANGE(n) \ + case n: \ + if (n <= buttonMin) \ + return; +#else + #define IF_BTN_IN_RANGE(n) \ + case n: +#endif + + // Now we use SDL Game Controller DB +#if defined RW_D3D9 || defined RWLIBS + if ( AllValidWinJoys.m_aJoys[JOYSTICK1].m_nVendorID == 0x3427 + && AllValidWinJoys.m_aJoys[JOYSTICK1].m_nProductID == 0x1190) +#else + if (0) +#endif + { + //GIC USB Joystick, PS2 Gamepad ? + + switch (btn) + { + IF_BTN_IN_RANGE(16) + SetControllerKeyAssociatedWithAction(GO_LEFT, 16, JOYSTICK); + IF_BTN_IN_RANGE(15) + SetControllerKeyAssociatedWithAction(GO_BACK, 15, JOYSTICK); + IF_BTN_IN_RANGE(14) + SetControllerKeyAssociatedWithAction(GO_RIGHT, 14, JOYSTICK); + IF_BTN_IN_RANGE(13) + SetControllerKeyAssociatedWithAction(GO_FORWARD, 13, JOYSTICK); + IF_BTN_IN_RANGE(12) + IF_BTN_IN_RANGE(11) + SetControllerKeyAssociatedWithAction(PED_LOOKBEHIND, 11, JOYSTICK); + SetControllerKeyAssociatedWithAction(TOGGLE_SUBMISSIONS, 11, JOYSTICK); + IF_BTN_IN_RANGE(10) + SetControllerKeyAssociatedWithAction(VEHICLE_HORN, 10, JOYSTICK); + SetControllerKeyAssociatedWithAction(PED_DUCK, 10, JOYSTICK); + IF_BTN_IN_RANGE(9) + SetControllerKeyAssociatedWithAction(CAMERA_CHANGE_VIEW_ALL_SITUATIONS, 9, JOYSTICK); + IF_BTN_IN_RANGE(8) + SetControllerKeyAssociatedWithAction(VEHICLE_HANDBRAKE, 8, JOYSTICK); + SetControllerKeyAssociatedWithAction(PED_LOCK_TARGET, 8, JOYSTICK); + IF_BTN_IN_RANGE(7) + SetControllerKeyAssociatedWithAction(PED_ANSWER_PHONE, 7, JOYSTICK); + SetControllerKeyAssociatedWithAction(VEHICLE_CHANGE_RADIO_STATION, 7, JOYSTICK); + IF_BTN_IN_RANGE(6) + SetControllerKeyAssociatedWithAction(PED_CYCLE_WEAPON_RIGHT, 6, JOYSTICK); + SetControllerKeyAssociatedWithAction(VEHICLE_LOOKRIGHT, 6, JOYSTICK); + IF_BTN_IN_RANGE(5) + SetControllerKeyAssociatedWithAction(PED_CYCLE_WEAPON_LEFT, 5, JOYSTICK); + SetControllerKeyAssociatedWithAction(VEHICLE_LOOKLEFT, 5, JOYSTICK); + /*******************************************************************************************/ + IF_BTN_IN_RANGE(4) + SetControllerKeyAssociatedWithAction(VEHICLE_BRAKE, 4, JOYSTICK); + SetControllerKeyAssociatedWithAction(PED_JUMPING, 4, JOYSTICK); + SetControllerKeyAssociatedWithAction(PED_SNIPER_ZOOM_IN, 4, JOYSTICK); + IF_BTN_IN_RANGE(3) + SetControllerKeyAssociatedWithAction(VEHICLE_ACCELERATE, 3, JOYSTICK); + SetControllerKeyAssociatedWithAction(PED_SPRINT, 3, JOYSTICK); + SetControllerKeyAssociatedWithAction(PED_SNIPER_ZOOM_OUT, 3, JOYSTICK); + IF_BTN_IN_RANGE(2) + SetControllerKeyAssociatedWithAction(PED_FIREWEAPON, 2, JOYSTICK); +#ifdef BIND_VEHICLE_FIREWEAPON + SetControllerKeyAssociatedWithAction(VEHICLE_FIREWEAPON, 2, JOYSTICK); +#endif + IF_BTN_IN_RANGE(1) + SetControllerKeyAssociatedWithAction(VEHICLE_ENTER_EXIT, 1, JOYSTICK); + /*******************************************************************************************/ + } + } + else + { + switch (btn) + { + IF_BTN_IN_RANGE(16) + SetControllerKeyAssociatedWithAction(GO_LEFT, 16, JOYSTICK); + IF_BTN_IN_RANGE(15) + SetControllerKeyAssociatedWithAction(GO_BACK, 15, JOYSTICK); + IF_BTN_IN_RANGE(14) + SetControllerKeyAssociatedWithAction(GO_RIGHT, 14, JOYSTICK); + IF_BTN_IN_RANGE(13) + SetControllerKeyAssociatedWithAction(GO_FORWARD, 13, JOYSTICK); + IF_BTN_IN_RANGE(12) + IF_BTN_IN_RANGE(11) + SetControllerKeyAssociatedWithAction(PED_LOOKBEHIND, 11, JOYSTICK); + SetControllerKeyAssociatedWithAction(TOGGLE_SUBMISSIONS, 11, JOYSTICK); + IF_BTN_IN_RANGE(10) + SetControllerKeyAssociatedWithAction(VEHICLE_HORN, 10, JOYSTICK); + SetControllerKeyAssociatedWithAction(PED_DUCK, 10, JOYSTICK); + IF_BTN_IN_RANGE(9) + SetControllerKeyAssociatedWithAction(CAMERA_CHANGE_VIEW_ALL_SITUATIONS, 9, JOYSTICK); + IF_BTN_IN_RANGE(8) + SetControllerKeyAssociatedWithAction(VEHICLE_HANDBRAKE, 8, JOYSTICK); + SetControllerKeyAssociatedWithAction(PED_LOCK_TARGET, 8, JOYSTICK); + IF_BTN_IN_RANGE(7) + SetControllerKeyAssociatedWithAction(PED_ANSWER_PHONE, 7, JOYSTICK); + SetControllerKeyAssociatedWithAction(VEHICLE_CHANGE_RADIO_STATION, 7, JOYSTICK); + IF_BTN_IN_RANGE(6) + SetControllerKeyAssociatedWithAction(PED_CYCLE_WEAPON_RIGHT, 6, JOYSTICK); + SetControllerKeyAssociatedWithAction(VEHICLE_LOOKRIGHT, 6, JOYSTICK); + IF_BTN_IN_RANGE(5) + SetControllerKeyAssociatedWithAction(PED_CYCLE_WEAPON_LEFT, 5, JOYSTICK); + SetControllerKeyAssociatedWithAction(VEHICLE_LOOKLEFT, 5, JOYSTICK); + /*******************************************************************************************/ + IF_BTN_IN_RANGE(4) + SetControllerKeyAssociatedWithAction(VEHICLE_ENTER_EXIT, 4, JOYSTICK); + IF_BTN_IN_RANGE(3) + SetControllerKeyAssociatedWithAction(VEHICLE_BRAKE, 3, JOYSTICK); + SetControllerKeyAssociatedWithAction(PED_JUMPING, 3, JOYSTICK); + SetControllerKeyAssociatedWithAction(PED_SNIPER_ZOOM_IN, 3, JOYSTICK); + IF_BTN_IN_RANGE(2) + SetControllerKeyAssociatedWithAction(VEHICLE_ACCELERATE, 2, JOYSTICK); + SetControllerKeyAssociatedWithAction(PED_SPRINT, 2, JOYSTICK); + SetControllerKeyAssociatedWithAction(PED_SNIPER_ZOOM_OUT, 2, JOYSTICK); + IF_BTN_IN_RANGE(1) + SetControllerKeyAssociatedWithAction(PED_FIREWEAPON, 1, JOYSTICK); +#ifdef BIND_VEHICLE_FIREWEAPON + SetControllerKeyAssociatedWithAction(VEHICLE_FIREWEAPON, 1, JOYSTICK); +#endif + /*******************************************************************************************/ + } + } +} + +void CControllerConfigManager::InitialiseControllerActionNameArray() +{ + wchar buf[ACTIONNAME_LENGTH + 2]; + +#define SETACTIONNAME(name) AsciiToUnicode(#name, buf); CMessages::WideStringCopy(m_aActionNames[name], buf, ACTIONNAME_LENGTH); + + SETACTIONNAME(PED_LOOKBEHIND); + SETACTIONNAME(PED_CYCLE_WEAPON_LEFT); + SETACTIONNAME(PED_CYCLE_WEAPON_RIGHT); + SETACTIONNAME(PED_LOCK_TARGET); + SETACTIONNAME(PED_JUMPING); + SETACTIONNAME(PED_SPRINT); + SETACTIONNAME(PED_CYCLE_TARGET_LEFT); + SETACTIONNAME(PED_CYCLE_TARGET_RIGHT); + SETACTIONNAME(PED_LOCK_TARGET); // duplicate + SETACTIONNAME(PED_CENTER_CAMERA_BEHIND_PLAYER); + SETACTIONNAME(VEHICLE_LOOKBEHIND); + SETACTIONNAME(PED_DUCK); + SETACTIONNAME(PED_ANSWER_PHONE); + SETACTIONNAME(VEHICLE_LOOKLEFT); + SETACTIONNAME(VEHICLE_LOOKRIGHT); + SETACTIONNAME(VEHICLE_HORN); + SETACTIONNAME(VEHICLE_HANDBRAKE); + SETACTIONNAME(VEHICLE_ACCELERATE); + SETACTIONNAME(VEHICLE_BRAKE); + SETACTIONNAME(VEHICLE_CHANGE_RADIO_STATION); + SETACTIONNAME(TOGGLE_SUBMISSIONS); + SETACTIONNAME(PED_SNIPER_ZOOM_IN); + SETACTIONNAME(PED_SNIPER_ZOOM_OUT); + SETACTIONNAME(PED_1RST_PERSON_LOOK_LEFT); + SETACTIONNAME(PED_1RST_PERSON_LOOK_RIGHT); + SETACTIONNAME(PED_1RST_PERSON_LOOK_UP); + SETACTIONNAME(PED_1RST_PERSON_LOOK_DOWN); + SETACTIONNAME(SHOW_MOUSE_POINTER_TOGGLE); + SETACTIONNAME(CAMERA_CHANGE_VIEW_ALL_SITUATIONS); + SETACTIONNAME(PED_FIREWEAPON); +#ifdef BIND_VEHICLE_FIREWEAPON + SETACTIONNAME(VEHICLE_FIREWEAPON); +#endif + SETACTIONNAME(VEHICLE_ENTER_EXIT); + SETACTIONNAME(GO_LEFT); + SETACTIONNAME(GO_RIGHT); + SETACTIONNAME(GO_FORWARD); + SETACTIONNAME(GO_BACK); + SETACTIONNAME(VEHICLE_TURRETLEFT); + SETACTIONNAME(VEHICLE_TURRETRIGHT); + SETACTIONNAME(VEHICLE_TURRETUP); + SETACTIONNAME(VEHICLE_TURRETDOWN); + SETACTIONNAME(NETWORK_TALK); + SETACTIONNAME(TOGGLE_DPAD); + SETACTIONNAME(SWITCH_DEBUG_CAM_ON); + SETACTIONNAME(TAKE_SCREEN_SHOT); + +#undef SETACTIONNAME +} + +void CControllerConfigManager::UpdateJoyInConfigMenus_ButtonDown(int32 button, int32 padnumber) +{ + if (button != 0) + { + CPad *pad = CPad::GetPad(padnumber); + if (pad != NULL) + { + switch (button) + { + case 16: + pad->PCTempJoyState.DPadLeft = 255; + break; + case 15: + pad->PCTempJoyState.DPadDown = 255; + break; + case 14: + pad->PCTempJoyState.DPadRight = 255; + break; + case 13: + pad->PCTempJoyState.DPadUp = 255; + break; + case 12: +#ifndef REGISTER_START_BUTTON + if (padnumber == 1) +#endif + pad->PCTempJoyState.Start = 255; + break; + case 11: + pad->PCTempJoyState.RightShock = 255; + break; + case 10: + pad->PCTempJoyState.LeftShock = 255; + break; + case 9: + pad->PCTempJoyState.Select = 255; + break; + case 8: + pad->PCTempJoyState.RightShoulder1 = 255; + break; + case 7: + pad->PCTempJoyState.LeftShoulder1 = 255; + break; + case 6: + pad->PCTempJoyState.RightShoulder2 = 255; + break; + case 5: + pad->PCTempJoyState.LeftShoulder2 = 255; + break; + } + + // Now we use SDL Game Controller DB +#if defined RW_D3D9 || defined RWLIBS + if (AllValidWinJoys.m_aJoys[JOYSTICK1].m_nVendorID == 0x3427 + && AllValidWinJoys.m_aJoys[JOYSTICK1].m_nProductID == 0x1190) +#else + if (0) +#endif + { + //GIC USB Joystick, PS2 Gamepad ? + + switch (button) + { + case 4: + pad->PCTempJoyState.Square = 255; + break; + case 3: + pad->PCTempJoyState.Cross = 255; + break; + case 2: + pad->PCTempJoyState.Circle = 255; + break; + case 1: + pad->PCTempJoyState.Triangle = 255; + break; + } + } + else + { + switch (button) + { + case 4: + pad->PCTempJoyState.Triangle = 255; + break; + case 3: + pad->PCTempJoyState.Square = 255; + break; + case 2: + pad->PCTempJoyState.Cross = 255; + break; + case 1: + pad->PCTempJoyState.Circle = 255; + break; + } + } + } + } +} + +void CControllerConfigManager::AffectControllerStateOn_ButtonDown(int32 button, eControllerType type) +{ + bool process = true; + + if ((type == KEYBOARD || type == OPTIONAL_EXTRA) && button == rsNULL) + process = false; + if (type == JOYSTICK && button == 0) + process = false; + if (type == MOUSE && button == 0) + process = false; + + if (process) + { + CPad *pad = CPad::GetPad(PAD1); + + bool firstPerson = false; + bool playerDriving = false; + + if (FindPlayerVehicle() != NULL) + { + CPlayerPed *plr = FindPlayerPed(); + if (plr != NULL) + { + if (plr->m_nPedState == PED_DRIVING) + playerDriving = true; + } + } + + int16 mode = TheCamera.Cams[TheCamera.ActiveCam].Mode; + if ( mode == CCam::MODE_1STPERSON + || mode == CCam::MODE_SNIPER + || mode == CCam::MODE_ROCKETLAUNCHER + || mode == CCam::MODE_CAMERA + || mode == CCam::MODE_M16_1STPERSON) + { + firstPerson = true; + } + + CControllerState *state; + + switch (type) + { + case KEYBOARD: + case OPTIONAL_EXTRA: + state = &CPad::GetPad(PAD1)->PCTempKeyState; + break; + case JOYSTICK: + state = &CPad::GetPad(PAD1)->PCTempJoyState; + break; + case MOUSE: + state = &CPad::GetPad(PAD1)->PCTempMouseState; + break; + default: break; + } + + if (pad != NULL) + { + if (playerDriving) + { + AffectControllerStateOn_ButtonDown_Driving(button, type, *state); + AffectControllerStateOn_ButtonDown_VehicleAndThirdPersonOnly(button, type, *state); + } + else + { + AffectControllerStateOn_ButtonDown_FirstAndThirdPersonOnly(button, type, *state); + if (firstPerson) + AffectControllerStateOn_ButtonDown_FirstPersonOnly(button, type, *state); + else + { + AffectControllerStateOn_ButtonDown_ThirdPersonOnly(button, type, *state); + AffectControllerStateOn_ButtonDown_VehicleAndThirdPersonOnly(button, type, *state); + } + } + + AffectControllerStateOn_ButtonDown_AllStates(button, type, *state); + +#ifdef REGISTER_START_BUTTON + if (button == 12) + state->Start = 255; +#endif + } + } +} + +void CControllerConfigManager::AffectControllerStateOn_ButtonDown_Driving(int32 button, eControllerType type, CControllerState &state) +{ +#ifdef BIND_VEHICLE_FIREWEAPON + if (button == GetControllerKeyAssociatedWithAction(VEHICLE_FIREWEAPON, type)) + state.Circle = 255; +#endif + if (button == GetControllerKeyAssociatedWithAction(VEHICLE_LOOKBEHIND, type)) + { + state.LeftShoulder2 = 255; + state.RightShoulder2 = 255; + } + + if (button == GetControllerKeyAssociatedWithAction(VEHICLE_LOOKLEFT, type)) + state.LeftShoulder2 = 255; + if (button == GetControllerKeyAssociatedWithAction(VEHICLE_LOOKRIGHT, type)) + state.RightShoulder2 = 255; + if (button == GetControllerKeyAssociatedWithAction(VEHICLE_HORN, type)) + state.LeftShock = 255; + if (button == GetControllerKeyAssociatedWithAction(VEHICLE_HANDBRAKE, type)) + state.RightShoulder1 = 255; + if (button == GetControllerKeyAssociatedWithAction(VEHICLE_ACCELERATE, type)) + state.Cross = 255; + if (button == GetControllerKeyAssociatedWithAction(VEHICLE_CHANGE_RADIO_STATION, type)) + state.LeftShoulder1 = 255; + if (button == GetControllerKeyAssociatedWithAction(VEHICLE_BRAKE, type)) + state.Square = 255; + if (button == GetControllerKeyAssociatedWithAction(TOGGLE_SUBMISSIONS, type)) + state.RightShock = 255; + + if (button == GetControllerKeyAssociatedWithAction(VEHICLE_TURRETLEFT, type)) + { + if (state.RightStickX == 128 || m_aSimCheckers[SIM_X2][type]) + { + state.RightStickX = 0; + m_aSimCheckers[SIM_X2][type] = true; + } + else + { + state.RightStickX = -128; + } + } + + if (button == GetControllerKeyAssociatedWithAction(VEHICLE_TURRETRIGHT, type)) + { + if (state.RightStickX == -128 || m_aSimCheckers[SIM_X2][type]) + { + state.RightStickX = 0; + m_aSimCheckers[SIM_X2][type] = true; + } + else + state.RightStickX = 128; + } + + bool isDodo = false; + if (FindPlayerVehicle() && (FindPlayerVehicle()->IsVehicle() && ( + FindPlayerVehicle()->GetModelIndex() == MI_DODO +#ifdef FIX_BUGS + || (CVehicle::bAllDodosCheat && !FindPlayerVehicle()->IsRealHeli()) +#endif + ))) + { + isDodo = true; + } + + + if (button == GetControllerKeyAssociatedWithAction(VEHICLE_TURRETUP, type)) + { + if (isDodo == true) + { + if (state.LeftStickY == -128 || m_aSimCheckers[SIM_Y1][type]) // BUG: should be SIM_Y2. SIM_Y1 it's DPAD + { + state.LeftStickY = 0; + m_aSimCheckers[SIM_Y2][type] = true; + } + else + state.LeftStickY = 128; + } + + else if (state.RightStickY == -128 || m_aSimCheckers[SIM_Y2][type]) + { + state.RightStickY = 0; + m_aSimCheckers[SIM_Y2][type] = true; + } + else + { + state.RightStickY = 128; + } + } + + if (button == GetControllerKeyAssociatedWithAction(VEHICLE_TURRETDOWN, type)) + { + if (isDodo == true) + { + if (state.LeftStickY == 128 || m_aSimCheckers[SIM_Y1][type]) // BUG: should be SIM_Y2. SIM_Y1 it's DPAD + { + state.LeftStickY = 0; + m_aSimCheckers[SIM_Y2][type] = true; + } + else + state.LeftStickY = -128; + } + + else if (state.RightStickY == 128 || m_aSimCheckers[SIM_Y2][type]) + { + state.RightStickY = 0; + m_aSimCheckers[SIM_Y2][type] = true; + } + else + state.RightStickY = -128; + } +} + +void CControllerConfigManager::AffectControllerStateOn_ButtonDown_FirstPersonOnly(int32 button, eControllerType type, CControllerState &state) +{ + if (button == GetControllerKeyAssociatedWithAction(PED_SNIPER_ZOOM_IN, type)) + state.Square = 255; + if (button == GetControllerKeyAssociatedWithAction(PED_SNIPER_ZOOM_OUT, type)) + state.Cross = 255; + if (button == GetControllerKeyAssociatedWithAction(PED_DUCK, type)) + state.LeftShock = 255; +} + +void CControllerConfigManager::AffectControllerStateOn_ButtonDown_ThirdPersonOnly(int32 button, eControllerType type, CControllerState &state) +{ + if (button == GetControllerKeyAssociatedWithAction(PED_LOOKBEHIND, type)) + state.RightShock = 255; + if (button == GetControllerKeyAssociatedWithAction(PED_JUMPING, type)) + state.Square = 255; + if (button == GetControllerKeyAssociatedWithAction(PED_ANSWER_PHONE, type)) + state.LeftShoulder1 = 255; + if (button == GetControllerKeyAssociatedWithAction(PED_CYCLE_WEAPON_LEFT, type)) + state.LeftShoulder2 = 255; + if (button == GetControllerKeyAssociatedWithAction(PED_CYCLE_WEAPON_RIGHT, type)) + state.RightShoulder2 = 255; + if (button == GetControllerKeyAssociatedWithAction(PED_SPRINT, type)) + state.Cross = 255; + if (button == GetControllerKeyAssociatedWithAction(PED_DUCK, type)) + state.LeftShock = 255; + + if (FrontEndMenuManager.m_ControlMethod == CONTROL_CLASSIC) + { + if (button == GetControllerKeyAssociatedWithAction(PED_CYCLE_TARGET_LEFT, type)) + state.LeftShoulder2 = 255; + if (button == GetControllerKeyAssociatedWithAction(PED_CYCLE_TARGET_RIGHT, type)) + state.RightShoulder2 = 255; + if (button == GetControllerKeyAssociatedWithAction(PED_CENTER_CAMERA_BEHIND_PLAYER, type)) + state.LeftShoulder1 = 255; + } +} + +void CControllerConfigManager::AffectControllerStateOn_ButtonDown_FirstAndThirdPersonOnly(int32 button, eControllerType type, CControllerState &state) +{ + CPad *pad = CPad::GetPad(PAD1); + +#ifdef BIND_VEHICLE_FIREWEAPON + if (button == GetControllerKeyAssociatedWithAction(PED_FIREWEAPON, type)) + state.Circle = 255; +#endif + if (button == GetControllerKeyAssociatedWithAction(PED_LOCK_TARGET, type)) + state.RightShoulder1 = 255; + + if (button == GetControllerKeyAssociatedWithAction(GO_FORWARD, type)) + { + if (state.DPadDown || m_aSimCheckers[SIM_Y1][type]) + { + m_aSimCheckers[SIM_Y1][type] = true; + state.DPadDown = 0; + state.DPadUp = 0; + } + else + state.DPadUp = 255; + } + + if (button == GetControllerKeyAssociatedWithAction(GO_BACK, type)) + { + if (state.DPadUp || m_aSimCheckers[SIM_Y1][type]) + { + m_aSimCheckers[SIM_Y1][type] = true; + state.DPadDown = 0; + state.DPadUp = 0; + } + else + state.DPadDown = 255; + } + + if (button == GetControllerKeyAssociatedWithAction(PED_1RST_PERSON_LOOK_LEFT, type)) + { + if (state.RightStickX == 128 || m_aSimCheckers[SIM_X2][type]) + { + state.RightStickX = 0; + m_aSimCheckers[SIM_X2][type] = true; + } + else + { + state.RightStickX = -128; + } + } + + if (button == GetControllerKeyAssociatedWithAction(PED_1RST_PERSON_LOOK_RIGHT, type)) + { + if (state.RightStickX == -128 || m_aSimCheckers[SIM_X2][type]) + { + state.RightStickX = 0; + m_aSimCheckers[SIM_X2][type] = true; + } + else + state.RightStickX = 128; + } + + if (FrontEndMenuManager.m_ControlMethod == CONTROL_CLASSIC) + { + if (button == GetControllerKeyAssociatedWithAction(PED_1RST_PERSON_LOOK_UP, type)) + { + if (state.RightStickY == -128 || m_aSimCheckers[SIM_Y2][type]) + { + state.RightStickY = 0; + m_aSimCheckers[SIM_Y2][type] = true; + } + else + state.RightStickY = 128; + } + + if (button == GetControllerKeyAssociatedWithAction(PED_1RST_PERSON_LOOK_DOWN, type)) + { + if (state.RightStickY == 128 || m_aSimCheckers[SIM_Y2][type]) + { + state.RightStickY = 0; + m_aSimCheckers[SIM_Y2][type] = true; + } + else + state.RightStickY = -128; + } + } +} + +void CControllerConfigManager::AffectControllerStateOn_ButtonDown_AllStates(int32 button, eControllerType type, CControllerState &state) +{ + if (button == GetControllerKeyAssociatedWithAction(CAMERA_CHANGE_VIEW_ALL_SITUATIONS, type)) + state.Select = 255; + +#ifndef BIND_VEHICLE_FIREWEAPON + if (button == GetControllerKeyAssociatedWithAction(PED_FIREWEAPON, type)) + state.Circle = 255; +#endif + + if (button == GetControllerKeyAssociatedWithAction(GO_LEFT, type)) + { + if (state.DPadRight || m_aSimCheckers[SIM_X1][type]) + { + m_aSimCheckers[SIM_X1][type] = true; + state.DPadLeft = 0; + state.DPadRight = 0; + } + else + state.DPadLeft = 255; + } + + if (button == GetControllerKeyAssociatedWithAction(GO_RIGHT, type)) + { + if (state.DPadLeft || m_aSimCheckers[SIM_X1][type]) + { + m_aSimCheckers[SIM_X1][type] = true; + state.DPadLeft = 0; + state.DPadRight = 0; + } + else + state.DPadRight = 255; + } + + if (button == GetControllerKeyAssociatedWithAction(NETWORK_TALK, type)) + state.NetworkTalk = 255; +} + +void CControllerConfigManager::AffectControllerStateOn_ButtonDown_VehicleAndThirdPersonOnly(int32 button, eControllerType type, CControllerState &state) +{ + if (button == GetControllerKeyAssociatedWithAction(VEHICLE_ENTER_EXIT, type)) + state.Triangle = 255; +} + +void CControllerConfigManager::UpdateJoyInConfigMenus_ButtonUp(int32 button, int32 padnumber) +{ + if (button!=0) + { + CPad *pad = CPad::GetPad(padnumber); + + if (pad != NULL) + { + switch (button) + { + case 16: + pad->PCTempJoyState.DPadLeft = 0; + break; + case 15: + pad->PCTempJoyState.DPadDown = 0; + break; + case 14: + pad->PCTempJoyState.DPadRight = 0; + break; + case 13: + pad->PCTempJoyState.DPadUp = 0; + break; +#ifdef REGISTER_START_BUTTON + case 12: + pad->PCTempJoyState.Start = 0; + break; +#endif + case 11: + pad->PCTempJoyState.RightShock = 0; + break; + case 10: + pad->PCTempJoyState.LeftShock = 0; + break; + case 9: + pad->PCTempJoyState.Select = 0; + break; + case 8: + pad->PCTempJoyState.RightShoulder1 = 0; + break; + case 7: + pad->PCTempJoyState.LeftShoulder1 = 0; + break; + case 6: + pad->PCTempJoyState.RightShoulder2 = 0; + break; + case 5: + pad->PCTempJoyState.LeftShoulder2 = 0; + break; + } + + // Now we use SDL Game Controller DB +#if defined RW_D3D9 || defined RWLIBS + if (AllValidWinJoys.m_aJoys[JOYSTICK1].m_nVendorID == 0x3427 + && AllValidWinJoys.m_aJoys[JOYSTICK1].m_nProductID == 0x1190) +#else + if (0) +#endif + { + //GIC USB Joystick, PS2 Gamepad ? + + switch (button) + { + case 4: + pad->PCTempJoyState.Square = 0; + break; + case 3: + pad->PCTempJoyState.Cross = 0; + break; + case 2: + pad->PCTempJoyState.Circle = 0; + break; + case 1: + pad->PCTempJoyState.Triangle = 0; + break; + } + } + else + { + switch (button) + { + case 4: + pad->PCTempJoyState.Triangle = 0; + break; + case 3: + pad->PCTempJoyState.Square = 0; + break; + case 2: + pad->PCTempJoyState.Cross = 0; + break; + case 1: + pad->PCTempJoyState.Circle = 0; + break; + } + } + } + } +} + +void CControllerConfigManager::AffectControllerStateOn_ButtonUp(int32 button, eControllerType type) +{ + bool process = true; + + if ((type == KEYBOARD || type == OPTIONAL_EXTRA) && button == rsNULL) + process = false; + if (type == JOYSTICK && button == 0) + process = false; + if (type == MOUSE && button == 0) + process = false; + + CControllerState *state; + + switch (type) + { + case KEYBOARD: + case OPTIONAL_EXTRA: + state = &CPad::GetPad(PAD1)->PCTempKeyState; + break; + case MOUSE: + state = &CPad::GetPad(PAD1)->PCTempMouseState; + break; + case JOYSTICK: + state = &CPad::GetPad(PAD1)->PCTempJoyState; + break; + default: break; + } + + if (process) + { + CPad *pad = CPad::GetPad(PAD1); + + if (pad != NULL) + { + if (FrontEndMenuManager.GetIsMenuActive()) + AffectControllerStateOn_ButtonUp_All_Player_States(button, type, *state); + +#ifdef REGISTER_START_BUTTON + if (button == 12) + state->Start = 0; +#endif + } + } +} + +void CControllerConfigManager::AffectControllerStateOn_ButtonUp_All_Player_States(int32 button, eControllerType type, CControllerState &state) +{ + if (button == GetControllerKeyAssociatedWithAction(NETWORK_TALK, type)) + state.NetworkTalk = 0; +} + +void CControllerConfigManager::AffectPadFromKeyBoard() +{ + RsKeyCodes kc; + _InputTranslateShiftKeyUpDown(&kc); + + bool processdown = false; + if (!CPad::m_bMapPadOneToPadTwo && !FrontEndMenuManager.GetIsMenuActive()) + processdown = true; + + for (int32 i = 0; i < MAX_CONTROLLERACTIONS; i++) + { + int32 key = GetControllerKeyAssociatedWithAction((e_ControllerAction)i, KEYBOARD); + if (GetIsKeyboardKeyDown((RsKeyCodes)key) && processdown) + AffectControllerStateOn_ButtonDown(key, KEYBOARD); + + int32 extrakey = GetControllerKeyAssociatedWithAction((e_ControllerAction)i, OPTIONAL_EXTRA); + if (GetIsKeyboardKeyDown((RsKeyCodes)extrakey) && processdown) + AffectControllerStateOn_ButtonDown(extrakey, OPTIONAL_EXTRA); + + if (!GetIsKeyboardKeyDown((RsKeyCodes)key)) + AffectControllerStateOn_ButtonUp(key, KEYBOARD); + else if ( !GetIsKeyboardKeyDown((RsKeyCodes)extrakey)) + AffectControllerStateOn_ButtonUp(key, OPTIONAL_EXTRA); + } +} + +void CControllerConfigManager::AffectPadFromMouse() +{ + bool processdown = false; + if (!CPad::m_bMapPadOneToPadTwo && !FrontEndMenuManager.GetIsMenuActive()) + processdown = true; + + for (int32 i = 0; i < MAX_CONTROLLERACTIONS; i++) + { + int32 button = GetControllerKeyAssociatedWithAction((e_ControllerAction)i, MOUSE); + if (GetIsMouseButtonDown((RsKeyCodes)button) && processdown) + AffectControllerStateOn_ButtonDown(button, MOUSE); + if (GetIsMouseButtonUp((RsKeyCodes)button)) + AffectControllerStateOn_ButtonUp(button, MOUSE); + } +} + +void CControllerConfigManager::ClearSimButtonPressCheckers() +{ + for (int32 i = 0; i < MAX_SIMS; i++) + { + m_aSimCheckers[i][KEYBOARD] = false; + m_aSimCheckers[i][OPTIONAL_EXTRA] = false; + m_aSimCheckers[i][MOUSE] = false; + m_aSimCheckers[i][JOYSTICK] = false; + } +} + +bool CControllerConfigManager::GetIsKeyboardKeyDown(RsKeyCodes keycode) +{ + if (keycode < 255) + { + if (CPad::GetPad(PAD1)->GetChar(keycode)) + return true; + } + + for (int32 i = 0; i < 12; i++) + { + if (i + rsF1 == keycode) + { + if (CPad::GetPad(PAD1)->GetF(i)) + return true; + } + } + + switch (keycode) + { + case rsESC: + if (CPad::GetPad(PAD1)->GetEscape()) + return true; + break; + case rsINS: + if (CPad::GetPad(PAD1)->GetInsert()) + return true; + break; + case rsDEL: + if (CPad::GetPad(PAD1)->GetDelete()) + return true; + break; + case rsHOME: + if (CPad::GetPad(PAD1)->GetHome()) + return true; + break; + case rsEND: + if (CPad::GetPad(PAD1)->GetEnd()) + return true; + break; + case rsPGUP: + if (CPad::GetPad(PAD1)->GetPageUp()) + return true; + break; + case rsPGDN: + if (CPad::GetPad(PAD1)->GetPageDown()) + return true; + break; + case rsUP: + if (CPad::GetPad(PAD1)->GetUp()) + return true; + break; + case rsDOWN: + if (CPad::GetPad(PAD1)->GetDown()) + return true; + break; + case rsLEFT: + if (CPad::GetPad(PAD1)->GetLeft()) + return true; + break; + case rsRIGHT: + if (CPad::GetPad(PAD1)->GetRight()) + return true; + break; + case rsSCROLL: + if (CPad::GetPad(PAD1)->GetScrollLock()) + return true; + break; + case rsPAUSE: + if (CPad::GetPad(PAD1)->GetPause()) + return true; + break; + case rsNUMLOCK: + if (CPad::GetPad(PAD1)->GetNumLock()) + return true; + break; + case rsDIVIDE: + if (CPad::GetPad(PAD1)->GetDivide()) + return true; + break; + case rsTIMES: + if (CPad::GetPad(PAD1)->GetTimes()) + return true; + break; + case rsMINUS: + if (CPad::GetPad(PAD1)->GetMinus()) + return true; + break; + case rsPLUS: + if (CPad::GetPad(PAD1)->GetPlus()) + return true; + break; + case rsPADENTER: + if (CPad::GetPad(PAD1)->GetPadEnter()) + return true; + break; + case rsPADDEL: + if (CPad::GetPad(PAD1)->GetPadDel()) + return true; + break; + case rsPADEND: + if (CPad::GetPad(PAD1)->GetPad1()) + return true; + break; + case rsPADDOWN: + if (CPad::GetPad(PAD1)->GetPad2()) + return true; + break; + case rsPADPGDN: + if (CPad::GetPad(PAD1)->GetPad3()) + return true; + break; + case rsPADLEFT: + if (CPad::GetPad(PAD1)->GetPad4()) + return true; + break; + case rsPAD5: + if (CPad::GetPad(PAD1)->GetPad5()) + return true; + break; + case rsPADRIGHT: + if (CPad::GetPad(PAD1)->GetPad6()) + return true; + break; + case rsPADHOME: + if (CPad::GetPad(PAD1)->GetPad7()) + return true; + break; + case rsPADUP: + if (CPad::GetPad(PAD1)->GetPad8()) + return true; + break; + case rsPADPGUP: + if (CPad::GetPad(PAD1)->GetPad9()) + return true; + break; + case rsPADINS: + if (CPad::GetPad(PAD1)->GetPad0()) + return true; + break; + case rsBACKSP: + if (CPad::GetPad(PAD1)->GetBackspace()) + return true; + break; + case rsTAB: + if (CPad::GetPad(PAD1)->GetTab()) + return true; + break; + case rsCAPSLK: + if (CPad::GetPad(PAD1)->GetCapsLock()) + return true; + break; + case rsENTER: + if (CPad::GetPad(PAD1)->GetEnter()) + return true; + break; + case rsLSHIFT: + if (CPad::GetPad(PAD1)->GetLeftShift()) + return true; + break; + case rsSHIFT: + if (CPad::GetPad(PAD1)->GetShift()) + return true; + break; + case rsRSHIFT: + if (CPad::GetPad(PAD1)->GetRightShift()) + return true; + break; + case rsLCTRL: + if (CPad::GetPad(PAD1)->GetLeftCtrl()) + return true; + break; + case rsRCTRL: + if (CPad::GetPad(PAD1)->GetRightCtrl()) + return true; + break; + case rsLALT: + if (CPad::GetPad(PAD1)->GetLeftAlt()) + return true; + break; + case rsRALT: + if (CPad::GetPad(PAD1)->GetRightAlt()) + return true; + break; + case rsLWIN: + if (CPad::GetPad(PAD1)->GetLeftWin()) + return true; + break; + case rsRWIN: + if (CPad::GetPad(PAD1)->GetRightWin()) + return true; + break; + case rsAPPS: + if (CPad::GetPad(PAD1)->GetApps()) + return true; + break; + default: break; + } + + return false; +} + +bool CControllerConfigManager::GetIsKeyboardKeyJustDown(RsKeyCodes keycode) +{ + if (keycode < 255) + { + if (CPad::GetPad(PAD1)->GetCharJustDown(keycode)) + return true; + } + + for (int32 i = 0; i < 12; i++) + { + if (i + rsF1 == keycode) + { + if (CPad::GetPad(PAD1)->GetFJustDown(i)) + return true; + } + } + + switch (keycode) + { + case rsESC: + if (CPad::GetPad(PAD1)->GetEscapeJustDown()) + return true; + break; + case rsINS: + if (CPad::GetPad(PAD1)->GetInsertJustDown()) + return true; + break; + case rsDEL: + if (CPad::GetPad(PAD1)->GetDeleteJustDown()) + return true; + break; + case rsHOME: + if (CPad::GetPad(PAD1)->GetHomeJustDown()) + return true; + break; + case rsEND: + if (CPad::GetPad(PAD1)->GetEndJustDown()) + return true; + break; + case rsPGUP: + if (CPad::GetPad(PAD1)->GetPageUpJustDown()) + return true; + break; + case rsPGDN: + if (CPad::GetPad(PAD1)->GetPageDownJustDown()) + return true; + break; + case rsUP: + if (CPad::GetPad(PAD1)->GetUpJustDown()) + return true; + break; + case rsDOWN: + if (CPad::GetPad(PAD1)->GetDownJustDown()) + return true; + break; + case rsLEFT: + if (CPad::GetPad(PAD1)->GetLeftJustDown()) + return true; + break; + case rsRIGHT: + if (CPad::GetPad(PAD1)->GetRightJustDown()) + return true; + break; + case rsSCROLL: + if (CPad::GetPad(PAD1)->GetScrollLockJustDown()) + return true; + break; + case rsPAUSE: + if (CPad::GetPad(PAD1)->GetPauseJustDown()) + return true; + break; + case rsNUMLOCK: + if (CPad::GetPad(PAD1)->GetNumLockJustDown()) + return true; + break; + case rsDIVIDE: + if (CPad::GetPad(PAD1)->GetDivideJustDown()) + return true; + break; + case rsTIMES: + if (CPad::GetPad(PAD1)->GetTimesJustDown()) + return true; + break; + case rsMINUS: + if (CPad::GetPad(PAD1)->GetMinusJustDown()) + return true; + break; + case rsPLUS: + if (CPad::GetPad(PAD1)->GetPlusJustDown()) + return true; + break; + case rsPADENTER: + if (CPad::GetPad(PAD1)->GetPadEnterJustDown()) + return true; + break; + case rsPADDEL: + if (CPad::GetPad(PAD1)->GetPadDelJustDown()) + return true; + break; + case rsPADEND: + if (CPad::GetPad(PAD1)->GetPad1JustDown()) + return true; + break; + case rsPADDOWN: + if (CPad::GetPad(PAD1)->GetPad2JustDown()) + return true; + break; + case rsPADPGDN: + if (CPad::GetPad(PAD1)->GetPad3JustDown()) + return true; + break; + case rsPADLEFT: + if (CPad::GetPad(PAD1)->GetPad4JustDown()) + return true; + break; + case rsPAD5: + if (CPad::GetPad(PAD1)->GetPad5JustDown()) + return true; + break; + case rsPADRIGHT: + if (CPad::GetPad(PAD1)->GetPad6JustDown()) + return true; + break; + case rsPADHOME: + if (CPad::GetPad(PAD1)->GetPad7JustDown()) + return true; + break; + case rsPADUP: + if (CPad::GetPad(PAD1)->GetPad8JustDown()) + return true; + break; + case rsPADPGUP: + if (CPad::GetPad(PAD1)->GetPad9JustDown()) + return true; + break; + case rsPADINS: + if (CPad::GetPad(PAD1)->GetPad0JustDown()) + return true; + break; + case rsBACKSP: + if (CPad::GetPad(PAD1)->GetBackspaceJustDown()) + return true; + break; + case rsTAB: + if (CPad::GetPad(PAD1)->GetTabJustDown()) + return true; + break; + case rsCAPSLK: + if (CPad::GetPad(PAD1)->GetCapsLockJustDown()) + return true; + break; + case rsENTER: + if (CPad::GetPad(PAD1)->GetReturnJustDown()) + return true; + break; + case rsLSHIFT: + if (CPad::GetPad(PAD1)->GetLeftShiftJustDown()) + return true; + break; + case rsSHIFT: + if (CPad::GetPad(PAD1)->GetShiftJustDown()) + return true; + break; + case rsRSHIFT: + if (CPad::GetPad(PAD1)->GetRightShiftJustDown()) + return true; + break; + case rsLCTRL: + if (CPad::GetPad(PAD1)->GetLeftCtrlJustDown()) + return true; + break; + case rsRCTRL: + if (CPad::GetPad(PAD1)->GetRightCtrlJustDown()) + return true; + break; + case rsLALT: + if (CPad::GetPad(PAD1)->GetLeftAltJustDown()) + return true; + break; + case rsRALT: + if (CPad::GetPad(PAD1)->GetRightAltJustDown()) + return true; + break; + case rsLWIN: + if (CPad::GetPad(PAD1)->GetLeftWinJustDown()) + return true; + break; + case rsRWIN: + if (CPad::GetPad(PAD1)->GetRightWinJustDown()) + return true; + break; + case rsAPPS: + if (CPad::GetPad(PAD1)->GetAppsJustDown()) + return true; + break; + default: break; + } + + return false; +} + +bool CControllerConfigManager::GetIsMouseButtonDown(RsKeyCodes keycode) +{ + switch (keycode) + { + case rsMOUSELEFTBUTTON: + if (CPad::GetPad(PAD1)->GetLeftMouse()) + return true; + break; + case rsMOUSMIDDLEBUTTON: + if (CPad::GetPad(PAD1)->GetMiddleMouse()) + return true; + break; + case rsMOUSERIGHTBUTTON: + if (CPad::GetPad(PAD1)->GetRightMouse()) + return true; + break; + case rsMOUSEWHEELUPBUTTON: + if (CPad::GetPad(PAD1)->GetMouseWheelUp()) + return true; + break; + case rsMOUSEWHEELDOWNBUTTON: + if (CPad::GetPad(PAD1)->GetMouseWheelDown()) + return true; + break; + case rsMOUSEX1BUTTON: + if (CPad::GetPad(PAD1)->GetMouseX1()) + return true; + break; + case rsMOUSEX2BUTTON: + if (CPad::GetPad(PAD1)->GetMouseX2()) + return true; + break; + default: break; + } + + return false; +} + +bool CControllerConfigManager::GetIsMouseButtonUp(RsKeyCodes keycode) +{ + switch (keycode) + { + case rsMOUSELEFTBUTTON: + if (CPad::GetPad(PAD1)->GetLeftMouseUp()) + return true; + break; + case rsMOUSMIDDLEBUTTON: + if (CPad::GetPad(PAD1)->GetMiddleMouseUp()) + return true; + break; + case rsMOUSERIGHTBUTTON: + if (CPad::GetPad(PAD1)->GetRightMouseUp()) + return true; + break; + case rsMOUSEWHEELUPBUTTON: + if (CPad::GetPad(PAD1)->GetMouseWheelUpUp()) + return true; + break; + case rsMOUSEWHEELDOWNBUTTON: + if (CPad::GetPad(PAD1)->GetMouseWheelDownUp()) + return true; + break; + case rsMOUSEX1BUTTON: + if (CPad::GetPad(PAD1)->GetMouseX1Up()) + return true; + break; + case rsMOUSEX2BUTTON: + if (CPad::GetPad(PAD1)->GetMouseX2Up()) + return true; + break; + default: break; + } + + return false; +} + +#define CLEAR_ACTION_IF_NEEDED(action) \ +if (key == GetControllerKeyAssociatedWithAction(action, type))\ + ClearSettingsAssociatedWithAction(action, type); + +void CControllerConfigManager::DeleteMatchingCommonControls(e_ControllerAction action, int32 key, eControllerType type) +{ + if (!GetIsKeyBlank(key, type)) + { +#ifndef BIND_VEHICLE_FIREWEAPON + CLEAR_ACTION_IF_NEEDED(PED_FIREWEAPON); +#endif + CLEAR_ACTION_IF_NEEDED(GO_LEFT); + CLEAR_ACTION_IF_NEEDED(GO_RIGHT); + CLEAR_ACTION_IF_NEEDED(CAMERA_CHANGE_VIEW_ALL_SITUATIONS); + CLEAR_ACTION_IF_NEEDED(NETWORK_TALK); + CLEAR_ACTION_IF_NEEDED(SWITCH_DEBUG_CAM_ON); + CLEAR_ACTION_IF_NEEDED(TOGGLE_DPAD); + CLEAR_ACTION_IF_NEEDED(TAKE_SCREEN_SHOT); + CLEAR_ACTION_IF_NEEDED(SHOW_MOUSE_POINTER_TOGGLE); + } +} + +void CControllerConfigManager::DeleteMatching3rdPersonControls(e_ControllerAction action, int32 key, eControllerType type) +{ + if (!GetIsKeyBlank(key, type)) + { + CLEAR_ACTION_IF_NEEDED(PED_CYCLE_WEAPON_LEFT); + CLEAR_ACTION_IF_NEEDED(PED_CYCLE_WEAPON_RIGHT); + CLEAR_ACTION_IF_NEEDED(PED_JUMPING); + CLEAR_ACTION_IF_NEEDED(PED_SPRINT); + CLEAR_ACTION_IF_NEEDED(PED_LOOKBEHIND); + CLEAR_ACTION_IF_NEEDED(PED_DUCK); + CLEAR_ACTION_IF_NEEDED(PED_ANSWER_PHONE); + + if (FrontEndMenuManager.m_ControlMethod == CONTROL_CLASSIC) + { + CLEAR_ACTION_IF_NEEDED(PED_CYCLE_TARGET_LEFT); + CLEAR_ACTION_IF_NEEDED(PED_CYCLE_TARGET_RIGHT); + CLEAR_ACTION_IF_NEEDED(PED_CENTER_CAMERA_BEHIND_PLAYER); + } + } +} + +void CControllerConfigManager::DeleteMatching1rst3rdPersonControls(e_ControllerAction action, int32 key, eControllerType type) +{ + if (!GetIsKeyBlank(key, type)) + { +#ifdef BIND_VEHICLE_FIREWEAPON + CLEAR_ACTION_IF_NEEDED(PED_FIREWEAPON); +#endif + CLEAR_ACTION_IF_NEEDED(PED_LOCK_TARGET); + CLEAR_ACTION_IF_NEEDED(GO_FORWARD); + CLEAR_ACTION_IF_NEEDED(GO_BACK); + + if (FrontEndMenuManager.m_ControlMethod == CONTROL_CLASSIC) + { + CLEAR_ACTION_IF_NEEDED(PED_1RST_PERSON_LOOK_LEFT); + CLEAR_ACTION_IF_NEEDED(PED_1RST_PERSON_LOOK_RIGHT); + CLEAR_ACTION_IF_NEEDED(PED_1RST_PERSON_LOOK_DOWN); + CLEAR_ACTION_IF_NEEDED(PED_1RST_PERSON_LOOK_UP); + } + } +} + +void CControllerConfigManager::DeleteMatchingVehicleControls(e_ControllerAction action, int32 key, eControllerType type) +{ + if (!GetIsKeyBlank(key, type)) + { +#ifdef BIND_VEHICLE_FIREWEAPON + CLEAR_ACTION_IF_NEEDED(VEHICLE_FIREWEAPON); +#endif + CLEAR_ACTION_IF_NEEDED(VEHICLE_ACCELERATE); + CLEAR_ACTION_IF_NEEDED(VEHICLE_BRAKE); + CLEAR_ACTION_IF_NEEDED(VEHICLE_CHANGE_RADIO_STATION); + CLEAR_ACTION_IF_NEEDED(VEHICLE_HORN); + CLEAR_ACTION_IF_NEEDED(TOGGLE_SUBMISSIONS); + CLEAR_ACTION_IF_NEEDED(VEHICLE_HANDBRAKE); + CLEAR_ACTION_IF_NEEDED(VEHICLE_LOOKLEFT); + CLEAR_ACTION_IF_NEEDED(VEHICLE_LOOKRIGHT); + CLEAR_ACTION_IF_NEEDED(VEHICLE_LOOKBEHIND); + CLEAR_ACTION_IF_NEEDED(VEHICLE_TURRETLEFT); + CLEAR_ACTION_IF_NEEDED(VEHICLE_TURRETRIGHT); + CLEAR_ACTION_IF_NEEDED(VEHICLE_TURRETUP); + CLEAR_ACTION_IF_NEEDED(VEHICLE_TURRETDOWN); + } +} + +void CControllerConfigManager::DeleteMatchingVehicle_3rdPersonControls(e_ControllerAction action, int32 key, eControllerType type) +{ + if (!GetIsKeyBlank(key, type)) + { + CLEAR_ACTION_IF_NEEDED(VEHICLE_ENTER_EXIT); + } +} + +void CControllerConfigManager::DeleteMatching1rstPersonControls(e_ControllerAction action, int32 key, eControllerType type) +{ + if (!GetIsKeyBlank(key, type)) + { + CLEAR_ACTION_IF_NEEDED(PED_SNIPER_ZOOM_IN); + CLEAR_ACTION_IF_NEEDED(PED_SNIPER_ZOOM_OUT); + } +} + +#undef CLEAR_ACTION_IF_NEEDED + +#ifdef RADIO_SCROLL_TO_PREV_STATION +#define CHECK_ACTION(action) \ +if (key == GetControllerKeyAssociatedWithAction(action, type))\ + return true; + +bool CControllerConfigManager::IsAnyVehicleActionAssignedToMouseKey(int32 key) +{ + const eControllerType type = MOUSE; + if (!GetIsKeyBlank(key, type)) + { +#ifdef BIND_VEHICLE_FIREWEAPON + CHECK_ACTION(VEHICLE_FIREWEAPON); +#endif + CHECK_ACTION(VEHICLE_LOOKBEHIND); + CHECK_ACTION(VEHICLE_LOOKLEFT); + CHECK_ACTION(VEHICLE_LOOKRIGHT); + CHECK_ACTION(VEHICLE_HORN); + CHECK_ACTION(VEHICLE_HANDBRAKE); + CHECK_ACTION(VEHICLE_ACCELERATE); + CHECK_ACTION(VEHICLE_BRAKE); + CHECK_ACTION(VEHICLE_CHANGE_RADIO_STATION); + CHECK_ACTION(TOGGLE_SUBMISSIONS); + CHECK_ACTION(VEHICLE_TURRETLEFT); + CHECK_ACTION(VEHICLE_TURRETRIGHT); + CHECK_ACTION(VEHICLE_TURRETUP); + CHECK_ACTION(VEHICLE_TURRETDOWN); + CHECK_ACTION(VEHICLE_ENTER_EXIT); + CHECK_ACTION(CAMERA_CHANGE_VIEW_ALL_SITUATIONS); +#ifndef BIND_VEHICLE_FIREWEAPON + CHECK_ACTION(PED_FIREWEAPON); +#endif + CHECK_ACTION(GO_LEFT); + CHECK_ACTION(GO_RIGHT); + CHECK_ACTION(NETWORK_TALK); + CHECK_ACTION(SWITCH_DEBUG_CAM_ON); + CHECK_ACTION(TOGGLE_DPAD); + CHECK_ACTION(TAKE_SCREEN_SHOT); + CHECK_ACTION(SHOW_MOUSE_POINTER_TOGGLE); + } + return false; +} + +#undef CHECK_ACTION +#endif + +void CControllerConfigManager::DeleteMatchingActionInitiators(e_ControllerAction action, int32 key, eControllerType type) +{ + if (!GetIsKeyBlank(key, type)) + { + switch (GetActionType(action)) + { + case ACTIONTYPE_1RSTPERSON: + DeleteMatchingCommonControls (action, key, type); + DeleteMatching1rstPersonControls (action, key, type); + DeleteMatching1rst3rdPersonControls (action, key, type); + break; + case ACTIONTYPE_3RDPERSON: + DeleteMatchingCommonControls (action, key, type); + DeleteMatching1rst3rdPersonControls (action, key, type); + DeleteMatching3rdPersonControls (action, key, type); + DeleteMatchingVehicle_3rdPersonControls(action, key, type); + break; + case ACTIONTYPE_VEHICLE: + DeleteMatchingCommonControls (action, key, type); + DeleteMatchingVehicleControls (action, key, type); + DeleteMatchingVehicle_3rdPersonControls(action, key, type); + break; + case ACTIONTYPE_VEHICLE_3RDPERSON: + DeleteMatchingCommonControls (action, key, type); + DeleteMatching1rst3rdPersonControls (action, key, type); + DeleteMatching3rdPersonControls (action, key, type); + DeleteMatchingVehicleControls (action, key, type); + break; + case ACTIONTYPE_COMMON: + DeleteMatchingCommonControls (action, key, type); + DeleteMatching1rstPersonControls (action, key, type); + DeleteMatching1rst3rdPersonControls (action, key, type); + DeleteMatching3rdPersonControls (action, key, type); + DeleteMatchingVehicleControls (action, key, type); + DeleteMatchingVehicle_3rdPersonControls(action, key, type); + break; + case ACTIONTYPE_1RST3RDPERSON: + DeleteMatchingCommonControls (action, key, type); + DeleteMatching1rstPersonControls (action, key, type); + DeleteMatching1rst3rdPersonControls (action, key, type); + DeleteMatching3rdPersonControls (action, key, type); + DeleteMatchingVehicle_3rdPersonControls(action, key, type); + break; + default: break; + } + } +} + +bool CControllerConfigManager::GetIsKeyBlank(int32 key, eControllerType type) +{ + switch (type) + { + case KEYBOARD: + case OPTIONAL_EXTRA: + if (key != rsNULL) + return false; + break; + + case JOYSTICK: + if (key != 0) + return false; + break; + + case MOUSE: + if (key != 0) + return false; + break; + default: break; + } + + return true; +} + +e_ControllerActionType CControllerConfigManager::GetActionType(e_ControllerAction action) +{ + switch (action) + { +#ifndef BIND_VEHICLE_FIREWEAPON + case PED_FIREWEAPON: +#endif + case GO_LEFT: + case GO_RIGHT: + case CAMERA_CHANGE_VIEW_ALL_SITUATIONS: + case NETWORK_TALK: + case TOGGLE_DPAD: + case SWITCH_DEBUG_CAM_ON: + case TAKE_SCREEN_SHOT: + case SHOW_MOUSE_POINTER_TOGGLE: + return ACTIONTYPE_COMMON; + break; + + case PED_CYCLE_WEAPON_RIGHT: + case PED_CYCLE_WEAPON_LEFT: + case PED_JUMPING: + case PED_SPRINT: + case PED_LOOKBEHIND: + case PED_DUCK: + case PED_ANSWER_PHONE: + case PED_CYCLE_TARGET_LEFT: + case PED_CYCLE_TARGET_RIGHT: + case PED_CENTER_CAMERA_BEHIND_PLAYER: + return ACTIONTYPE_3RDPERSON; + break; + +#ifdef BIND_VEHICLE_FIREWEAPON + case VEHICLE_FIREWEAPON: +#endif + case VEHICLE_ACCELERATE: + case VEHICLE_BRAKE: + case VEHICLE_CHANGE_RADIO_STATION: + case VEHICLE_HORN: + case TOGGLE_SUBMISSIONS: + case VEHICLE_HANDBRAKE: + case VEHICLE_LOOKLEFT: + case VEHICLE_LOOKRIGHT: + case VEHICLE_LOOKBEHIND: + case VEHICLE_TURRETLEFT: + case VEHICLE_TURRETRIGHT: + case VEHICLE_TURRETUP: + case VEHICLE_TURRETDOWN: + return ACTIONTYPE_VEHICLE; + break; + + case VEHICLE_ENTER_EXIT: + return ACTIONTYPE_VEHICLE_3RDPERSON; + break; + +#ifdef BIND_VEHICLE_FIREWEAPON + case PED_FIREWEAPON: +#endif + case GO_FORWARD: + case GO_BACK: + case PED_1RST_PERSON_LOOK_LEFT: + case PED_1RST_PERSON_LOOK_RIGHT: + case PED_LOCK_TARGET: + case PED_1RST_PERSON_LOOK_UP: + case PED_1RST_PERSON_LOOK_DOWN: + return ACTIONTYPE_1RST3RDPERSON; + break; + + case PED_SNIPER_ZOOM_IN: + case PED_SNIPER_ZOOM_OUT: + return ACTIONTYPE_1RSTPERSON; + break; + default: break; + } + + return ACTIONTYPE_NONE; +} + +void CControllerConfigManager::ClearSettingsAssociatedWithAction(e_ControllerAction action, eControllerType type) +{ + switch (type) + { + case KEYBOARD: + m_aSettings[action][type].m_Key = rsNULL; + m_aSettings[action][type].m_ContSetOrder = SETORDER_NONE; + break; + case OPTIONAL_EXTRA: + m_aSettings[action][type].m_Key = rsNULL; + m_aSettings[action][type].m_ContSetOrder = SETORDER_NONE; + break; + case MOUSE: + m_aSettings[action][type].m_Key = 0; + m_aSettings[action][type].m_ContSetOrder = SETORDER_NONE; + break; + case JOYSTICK: + m_aSettings[action][type].m_Key = 0; + m_aSettings[action][type].m_ContSetOrder = SETORDER_NONE; + break; + default: break; + } + + ResetSettingOrder(action); +} + +wchar *CControllerConfigManager::GetControllerSettingTextWithOrderNumber(e_ControllerAction action, eContSetOrder setorder) +{ + for (int i = 0; i < MAX_CONTROLLERTYPES; i++) + { + if (m_aSettings[action][i].m_ContSetOrder == setorder) + { + switch (i) + { + case KEYBOARD: + case OPTIONAL_EXTRA: + return GetControllerSettingTextKeyBoard(action, (eControllerType)i); + case MOUSE: + return GetControllerSettingTextMouse (action); + case JOYSTICK: + return GetControllerSettingTextJoystick(action); + default: break; + } + } + } + + return NULL; +} + +wchar *CControllerConfigManager::GetControllerSettingTextKeyBoard(e_ControllerAction action, eControllerType type) +{ + static wchar ActionText[50]; + static wchar NewStringWithNumber[30]; + + for (int32 i = 0; i < ARRAY_SIZE(ActionText); i++) + ActionText[i] = '\0'; + + if (GetControllerKeyAssociatedWithAction(action, type) != rsNULL) + { + if ( GetControllerKeyAssociatedWithAction(action, type) >= 0 + && GetControllerKeyAssociatedWithAction(action, type) <= 255) + { + char c = GetControllerKeyAssociatedWithAction(action, type); + if (c == ' ') + return TheText.Get("FEC_SPC"); // "SPC" + else + { + ActionText[0] = CFont::character_code(c); + if (ActionText[0] == '\0') + ActionText[0] = CFont::character_code('#'); + ActionText[1] = '\0'; + return ActionText; + } + } + else + { + switch (GetControllerKeyAssociatedWithAction(action, type)) + { + case rsF1: + case rsF2: + case rsF3: + case rsF4: + case rsF5: + case rsF6: + case rsF7: + case rsF8: + case rsF9: + case rsF10: + case rsF11: + case rsF12: + { + CMessages::InsertNumberInString(TheText.Get("FEC_FNC"), // "F~1~" + GetControllerKeyAssociatedWithAction(action, type) - rsESC, + -1, -1, -1, -1, -1, + NewStringWithNumber); + return NewStringWithNumber; + break; + } + + case rsINS: + { + return TheText.Get("FEC_IRT"); // "INS" + break; + } + + case rsDEL: + { + return TheText.Get("FEC_DLL"); // "DEL" + break; + } + + case rsHOME: + { + return TheText.Get("FEC_HME"); // "HOME" + break; + } + + case rsEND: + { + return TheText.Get("FEC_END"); // "END" + break; + } + + case rsPGUP: + { + return TheText.Get("FEC_PGU"); // "PGUP" + break; + } + + case rsPGDN: + { + return TheText.Get("FEC_PGD"); // "PGDN" + break; + } + + case rsUP: + { + return TheText.Get("FEC_UPA"); // "UP" + break; + } + + case rsDOWN: + { + return TheText.Get("FEC_DWA"); // "DOWN" + break; + } + + case rsLEFT: + { + return TheText.Get("FEC_LFA"); // "LEFT" + break; + } + + case rsRIGHT: + { + return TheText.Get("FEC_RFA"); // "RIGHT" + break; + } + + case rsDIVIDE: + { + return TheText.Get("FEC_FWS"); // "NUM /" + break; + } + + case rsTIMES: + { + return TheText.Get("FEC_STR"); // "NUM STAR" + break; + } + + case rsPLUS: + { + return TheText.Get("FEC_PLS"); // "NUM +" + break; + } + + case rsMINUS: + { + return TheText.Get("FEC_MIN"); // "NUM -" + break; + } + + case rsPADDEL: + { + return TheText.Get("FEC_DOT"); // "NUM ." + break; + } + + case rsPADEND: + { + CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" + 1, -1, -1, -1, -1, -1, NewStringWithNumber); + return NewStringWithNumber; + break; + } + + case rsPADDOWN: + { + CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" + 2, -1, -1, -1, -1, -1, + NewStringWithNumber); + return NewStringWithNumber; + break; + } + + case rsPADPGDN: + { + CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" + 3, -1, -1, -1, -1, -1, + NewStringWithNumber); + return NewStringWithNumber; + break; + } + + case rsPADLEFT: + { + CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" + 4, -1, -1, -1, -1, -1, + NewStringWithNumber); + return NewStringWithNumber; + break; + } + + case rsPAD5: + { + CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" + 5, -1, -1, -1, -1, -1, + NewStringWithNumber); + return NewStringWithNumber; + break; + } + + case rsNUMLOCK: + { + return TheText.Get("FEC_NLK"); // "NUMLOCK" + break; + } + + case rsPADRIGHT: + { + CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" + 6, -1, -1, -1, -1, -1, + NewStringWithNumber); + return NewStringWithNumber; + break; + } + + case rsPADHOME: + { + CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" + 7, -1, -1, -1, -1, -1, + NewStringWithNumber); + return NewStringWithNumber; + break; + } + + case rsPADUP: + { + CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" + 8, -1, -1, -1, -1, -1, + NewStringWithNumber); + return NewStringWithNumber; + break; + } + + case rsPADPGUP: + { + CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" + 9, -1, -1, -1, -1, -1, + NewStringWithNumber); + return NewStringWithNumber; + break; + } + + case rsPADINS: + { + CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" + 0, -1, -1, -1, -1, -1, + NewStringWithNumber); + return NewStringWithNumber; + break; + } + + case rsPADENTER: + { + return TheText.Get("FEC_ETR"); // "ENT" + break; + } + + case rsSCROLL: + { + return TheText.Get("FEC_SLK"); // "SCROLL LOCK" + break; + } + + case rsPAUSE: + { + return TheText.Get("FEC_PSB"); // "BREAK" + break; + } + + case rsBACKSP: + { + return TheText.Get("FEC_BSP"); // "BSPACE" + break; + } + + case rsTAB: + { + return TheText.Get("FEC_TAB"); // "TAB" + break; + } + + case rsCAPSLK: + { + return TheText.Get("FEC_CLK"); // "CAPSLOCK" + break; + } + + case rsENTER: + { + return TheText.Get("FEC_RTN"); // "RET" + break; + } + + case rsLSHIFT: + { + return TheText.Get("FEC_LSF"); // "LSHIFT" + break; + } + + case rsRSHIFT: + { + return TheText.Get("FEC_RSF"); // "RSHIFT" + break; + } + + case rsLCTRL: + { + return TheText.Get("FEC_LCT"); // "LCTRL" + break; + } + + case rsRCTRL: + { + return TheText.Get("FEC_RCT"); // "RCTRL" + break; + } + + case rsLALT: + { + return TheText.Get("FEC_LAL"); // "LALT" + break; + } + + case rsRALT: + { + return TheText.Get("FEC_RAL"); // "RALT" + break; + } + + case rsLWIN: + { + return TheText.Get("FEC_LWD"); // "LWIN" + break; + } + + case rsRWIN: + { + return TheText.Get("FEC_RWD"); // "RWIN" + break; + } + + case rsAPPS: + { + return TheText.Get("FEC_WRC"); // "WINCLICK" + break; + } + + case rsSHIFT: + { + return TheText.Get("FEC_SFT"); // "SHIFT" + break; + } + default: break; + } + } + } + + return NULL; +} + +wchar *CControllerConfigManager::GetControllerSettingTextMouse(e_ControllerAction action) +{ + switch (m_aSettings[action][MOUSE].m_Key) + { + case 1: + return TheText.Get("FEC_MSL"); // LMB + break; + case 2: + return TheText.Get("FEC_MSM"); // MMB + break; + case 3: + return TheText.Get("FEC_MSR"); // RMB + break; + case 4: + return TheText.Get("FEC_MWF"); // WHEEL UP + break; + case 5: + return TheText.Get("FEC_MWB"); // WHEEL DN + break; + case 6: + return TheText.Get("FEC_MXO"); // MXB1 + break; + case 7: + return TheText.Get("FEC_MXT"); // MXB2 + break; + default: break; + } + + return NULL; +} + +wchar *CControllerConfigManager::GetControllerSettingTextJoystick(e_ControllerAction action) +{ + if (m_aSettings[action][JOYSTICK].m_Key == 0) + return NULL; + + static wchar NewStringWithNumber[30]; + + CMessages::InsertNumberInString(TheText.Get("FEC_JBO"), // JOY ~1~ + m_aSettings[action][JOYSTICK].m_Key, -1, -1, -1, -1, -1, + NewStringWithNumber); + + return NewStringWithNumber; +} + +int32 CControllerConfigManager::GetNumOfSettingsForAction(e_ControllerAction action) +{ + int32 num = 0; + + if (m_aSettings[action][KEYBOARD].m_Key != rsNULL) num++; + if (m_aSettings[action][OPTIONAL_EXTRA].m_Key != rsNULL) num++; + if (m_aSettings[action][MOUSE].m_Key != 0) num++; + if (m_aSettings[action][JOYSTICK].m_Key != 0) num++; + + return num; +} + +#ifdef BIND_VEHICLE_FIREWEAPON +#define VFB(b) b, +#else +#define VFB(b) +#endif + +#define CONTROLLER_BUTTONS(T, O, X, Q, L1, L2, L3, R1, R2, R3, SELECT, RSU, RSD, RSL, RSR) \ + {{ \ + O, /* PED_FIREWEAPON */ \ + R2, /* PED_CYCLE_WEAPON_RIGHT */ \ + L2, /* PED_CYCLE_WEAPON_LEFT */ \ + nil, /* GO_FORWARD */ \ + nil, /* GO_BACK */ \ + LEFT, /* GO_LEFT */ \ + RIGHT, /* GO_RIGHT */ \ + Q, /* PED_SNIPER_ZOOM_IN */ \ + X, /* PED_SNIPER_ZOOM_OUT */ \ + T, /* VEHICLE_ENTER_EXIT */ \ + SELECT, /* CAMERA_CHANGE_VIEW_ALL_SITUATIONS */ \ + Q, /* PED_JUMPING */ \ + X, /* PED_SPRINT */ \ + R3, /* PED_LOOKBEHIND */ \ + L3, /* PED_DUCK */ \ + L1, /* PED_ANSWER_PHONE */ \ + VFB(O) /* VEHICLE_FIREWEAPON */ \ + X, /* VEHICLE_ACCELERATE */ \ + Q, /* VEHICLE_BRAKE */ \ + L1, /* VEHICLE_CHANGE_RADIO_STATION */ \ + L3, /* VEHICLE_HORN */ \ + R3, /* TOGGLE_SUBMISSIONS */ \ + R1, /* VEHICLE_HANDBRAKE */ \ + nil, /* PED_1RST_PERSON_LOOK_LEFT */ \ + nil, /* PED_1RST_PERSON_LOOK_RIGHT */ \ + L2, /* VEHICLE_LOOKLEFT */ \ + R2, /* VEHICLE_LOOKRIGHT */ \ + nil, /* VEHICLE_LOOKBEHIND */ \ + RSL, /* VEHICLE_TURRETLEFT */ \ + RSR, /* VEHICLE_TURRETRIGHT */ \ + UP, /* VEHICLE_TURRETUP */ \ + DOWN, /* VEHICLE_TURRETDOWN */ \ + L2, /* PED_CYCLE_TARGET_LEFT */ \ + R2, /* PED_CYCLE_TARGET_RIGHT */ \ + L1, /* PED_CENTER_CAMERA_BEHIND_PLAYER */ \ + R1, /* PED_LOCK_TARGET */ \ + nil, /* NETWORK_TALK */ \ + nil, /* PED_1RST_PERSON_LOOK_UP */ \ + nil, /* PED_1RST_PERSON_LOOK_DOWN */ \ + nil, /* _CONTROLLERACTION_36 */ \ + nil, /* TOGGLE_DPAD */ \ + nil, /* SWITCH_DEBUG_CAM_ON */ \ + nil, /* TAKE_SCREEN_SHOT */ \ + nil, /* UNKNOWN_ACTION */ \ + nil, /* SHOW_MOUSE_POINTER_TOGGLE */ \ + }, \ + { \ + O, /* PED_FIREWEAPON */ \ + R2, /* PED_CYCLE_WEAPON_RIGHT */ \ + L2, /* PED_CYCLE_WEAPON_LEFT */ \ + nil, /* GO_FORWARD */ \ + nil, /* GO_BACK */ \ + LEFT, /* GO_LEFT */ \ + RIGHT, /* GO_RIGHT */ \ + Q, /* PED_SNIPER_ZOOM_IN */ \ + X, /* PED_SNIPER_ZOOM_OUT */ \ + T, /* VEHICLE_ENTER_EXIT */ \ + SELECT, /* CAMERA_CHANGE_VIEW_ALL_SITUATIONS */ \ + Q, /* PED_JUMPING */ \ + X, /* PED_SPRINT */ \ + R3, /* PED_LOOKBEHIND */ \ + L3, /* PED_DUCK */ \ + L1, /* PED_ANSWER_PHONE */ \ + VFB(O) /* VEHICLE_FIREWEAPON */ \ + X, /* VEHICLE_ACCELERATE */ \ + Q, /* VEHICLE_BRAKE */ \ + SELECT, /* VEHICLE_CHANGE_RADIO_STATION */ \ + L1, /* VEHICLE_HORN */ \ + R3, /* TOGGLE_SUBMISSIONS */ \ + R1, /* VEHICLE_HANDBRAKE */ \ + nil, /* PED_1RST_PERSON_LOOK_LEFT */ \ + nil, /* PED_1RST_PERSON_LOOK_RIGHT */ \ + L2, /* VEHICLE_LOOKLEFT */ \ + R2, /* VEHICLE_LOOKRIGHT */ \ + nil, /* VEHICLE_LOOKBEHIND */ \ + RSL, /* VEHICLE_TURRETLEFT */ \ + RSR, /* VEHICLE_TURRETRIGHT */ \ + UP, /* VEHICLE_TURRETUP */ \ + DOWN, /* VEHICLE_TURRETDOWN */ \ + L2, /* PED_CYCLE_TARGET_LEFT */ \ + R2, /* PED_CYCLE_TARGET_RIGHT */ \ + L1, /* PED_CENTER_CAMERA_BEHIND_PLAYER */ \ + R1, /* PED_LOCK_TARGET */ \ + nil, /* NETWORK_TALK */ \ + nil, /* PED_1RST_PERSON_LOOK_UP */ \ + nil, /* PED_1RST_PERSON_LOOK_DOWN */ \ + nil, /* _CONTROLLERACTION_36 */ \ + nil, /* TOGGLE_DPAD */ \ + nil, /* SWITCH_DEBUG_CAM_ON */ \ + nil, /* TAKE_SCREEN_SHOT */ \ + nil, /* UNKNOWN_ACTION */ \ + nil, /* SHOW_MOUSE_POINTER_TOGGLE */ \ + }, \ + { \ + X, /* PED_FIREWEAPON */ \ + R2, /* PED_CYCLE_WEAPON_RIGHT */ \ + L2, /* PED_CYCLE_WEAPON_LEFT */ \ + nil, /* GO_FORWARD */ \ + nil, /* GO_BACK */ \ + LEFT, /* GO_LEFT */ \ + RIGHT, /* GO_RIGHT */ \ + T, /* PED_SNIPER_ZOOM_IN */ \ + Q, /* PED_SNIPER_ZOOM_OUT */ \ + L1, /* VEHICLE_ENTER_EXIT */ \ + SELECT, /* CAMERA_CHANGE_VIEW_ALL_SITUATIONS */ \ + Q, /* PED_JUMPING */ \ + O, /* PED_SPRINT */ \ + R3, /* PED_LOOKBEHIND */ \ + L3, /* PED_DUCK */ \ + T, /* PED_ANSWER_PHONE */ \ + VFB(O) /* VEHICLE_FIREWEAPON */ \ + X, /* VEHICLE_ACCELERATE */ \ + Q, /* VEHICLE_BRAKE */ \ + L3, /* VEHICLE_CHANGE_RADIO_STATION */ \ + R1, /* VEHICLE_HORN */ \ + R3, /* TOGGLE_SUBMISSIONS */ \ + T, /* VEHICLE_HANDBRAKE */ \ + nil, /* PED_1RST_PERSON_LOOK_LEFT */ \ + nil, /* PED_1RST_PERSON_LOOK_RIGHT */ \ + L2, /* VEHICLE_LOOKLEFT */ \ + R2, /* VEHICLE_LOOKRIGHT */ \ + nil, /* VEHICLE_LOOKBEHIND */ \ + RSL, /* VEHICLE_TURRETLEFT */ \ + RSR, /* VEHICLE_TURRETRIGHT */ \ + UP, /* VEHICLE_TURRETUP */ \ + DOWN, /* VEHICLE_TURRETDOWN */ \ + L2, /* PED_CYCLE_TARGET_LEFT */ \ + R2, /* PED_CYCLE_TARGET_RIGHT */ \ + T, /* PED_CENTER_CAMERA_BEHIND_PLAYER */ \ + R1, /* PED_LOCK_TARGET */ \ + nil, /* NETWORK_TALK */ \ + nil, /* PED_1RST_PERSON_LOOK_UP */ \ + nil, /* PED_1RST_PERSON_LOOK_DOWN */ \ + nil, /* _CONTROLLERACTION_36 */ \ + nil, /* TOGGLE_DPAD */ \ + nil, /* SWITCH_DEBUG_CAM_ON */ \ + nil, /* TAKE_SCREEN_SHOT */ \ + nil, /* UNKNOWN_ACTION */ \ + nil, /* SHOW_MOUSE_POINTER_TOGGLE */ \ + }, \ + { \ + R1, /* PED_FIREWEAPON */ \ + R2, /* PED_CYCLE_WEAPON_RIGHT */ \ + L2, /* PED_CYCLE_WEAPON_LEFT */ \ + nil, /* GO_FORWARD */ \ + nil, /* GO_BACK */ \ + LEFT, /* GO_LEFT */ \ + RIGHT, /* GO_RIGHT */ \ + Q, /* PED_SNIPER_ZOOM_IN */ \ + X, /* PED_SNIPER_ZOOM_OUT */ \ + T, /* VEHICLE_ENTER_EXIT */ \ + SELECT, /* CAMERA_CHANGE_VIEW_ALL_SITUATIONS */ \ + Q, /* PED_JUMPING */ \ + X, /* PED_SPRINT */ \ + R3, /* PED_LOOKBEHIND */ \ + L3, /* PED_DUCK */ \ + O, /* PED_ANSWER_PHONE */ \ + VFB(R1) /* VEHICLE_FIREWEAPON */ \ + RSU, /* VEHICLE_ACCELERATE */ \ + RSD, /* VEHICLE_BRAKE */ \ + O, /* VEHICLE_CHANGE_RADIO_STATION */ \ + L3, /* VEHICLE_HORN */ \ + Q, /* TOGGLE_SUBMISSIONS */ \ + L1, /* VEHICLE_HANDBRAKE */ \ + nil, /* PED_1RST_PERSON_LOOK_LEFT */ \ + nil, /* PED_1RST_PERSON_LOOK_RIGHT */ \ + L2, /* VEHICLE_LOOKLEFT */ \ + R2, /* VEHICLE_LOOKRIGHT */ \ + nil, /* VEHICLE_LOOKBEHIND */ \ + RSL, /* VEHICLE_TURRETLEFT */ \ + RSR, /* VEHICLE_TURRETRIGHT */ \ + UP, /* VEHICLE_TURRETUP */ \ + DOWN, /* VEHICLE_TURRETDOWN */ \ + L2, /* PED_CYCLE_TARGET_LEFT */ \ + R2, /* PED_CYCLE_TARGET_RIGHT */ \ + O, /* PED_CENTER_CAMERA_BEHIND_PLAYER */ \ + L1, /* PED_LOCK_TARGET */ \ + nil, /* NETWORK_TALK */ \ + nil, /* PED_1RST_PERSON_LOOK_UP */ \ + nil, /* PED_1RST_PERSON_LOOK_DOWN */ \ + nil, /* _CONTROLLERACTION_36 */ \ + nil, /* TOGGLE_DPAD */ \ + nil, /* SWITCH_DEBUG_CAM_ON */ \ + nil, /* TAKE_SCREEN_SHOT */ \ + nil, /* UNKNOWN_ACTION */ \ + nil, /* SHOW_MOUSE_POINTER_TOGGLE */ \ + }} + +#ifdef BUTTON_ICONS +#define UP "~U~" +#define DOWN "~D~" +#define LEFT "~<~" +#define RIGHT "~>~" +#else +#define UP "UP" +#define DOWN "DOWN" +#define LEFT "LEFT" +#define RIGHT "RIGHT" +#endif + +const char *XboxButtons_noIcons[][MAX_CONTROLLERACTIONS] = CONTROLLER_BUTTONS("Y", "B", "A", "X", "LB", "LT", "LS", "RB", "RT", "RS", "BACK", "right stick up", "right stick down", "right stick left", "right stick right"); + +#ifdef BUTTON_ICONS +const char *XboxButtons[][MAX_CONTROLLERACTIONS] = CONTROLLER_BUTTONS("~T~", "~O~", "~X~", "~Q~", "~K~", "~M~", "~A~", "~J~", "~V~", "~C~", "BACK", "~H~", "~L~", "~(~", "~)~"); +#endif + + +#if 0 // set 1 for ps2 fonts +#define PS2_TRIANGLE "\"" +#define PS2_CIRCLE "|" +#define PS2_CROSS "/" +#define PS2_SQUARE "^" +#else +#define PS2_TRIANGLE "TRIANGLE" +#define PS2_CIRCLE "CIRCLE" +#define PS2_CROSS "CROSS" +#define PS2_SQUARE "SQUARE" +#endif + +const char *PlayStationButtons_noIcons[][MAX_CONTROLLERACTIONS] = + CONTROLLER_BUTTONS(PS2_TRIANGLE, PS2_CIRCLE, PS2_CROSS, PS2_SQUARE, "L1", "L2", "L3", "R1", "R2", "R3", "SELECT", "right stick up", "right stick down", "right stick left", "right stick right"); + +#ifdef BUTTON_ICONS +const char *PlayStationButtons[][MAX_CONTROLLERACTIONS] = + CONTROLLER_BUTTONS("~T~", "~O~", "~X~", "~Q~", "~K~", "~M~", "~A~", "~J~", "~V~", "~C~", "SELECT", "~H~", "~L~", "~(~", "~)~"); +#endif + +#undef PS2_TRIANGLE +#undef PS2_CIRCLE +#undef PS2_CROSS +#undef PS2_SQUARE + +const char *NintendoSwitchButtons_noIcons[][MAX_CONTROLLERACTIONS] = + CONTROLLER_BUTTONS("Y", "A", "B", "X", "L", "ZL", "LS", "R", "ZR", "RS", "BACK", "right stick up", "right stick down", "right stick left", "right stick right"); + +#ifdef BUTTON_ICONS +const char *NintendoSwitchButtons[][MAX_CONTROLLERACTIONS] = + CONTROLLER_BUTTONS("~T~", "~O~", "~X~", "~Q~", "~K~", "~M~", "~A~", "~J~", "~V~", "~C~", "BACK", "~H~", "~L~", "~(~", "~)~"); +#endif + +#undef UP +#undef DOWN +#undef LEFT +#undef RIGHT + +#undef CONTROLLER_BUTTONS +#undef VFB + +void CControllerConfigManager::GetWideStringOfCommandKeys(uint16 action, wchar *text, uint16 leight) +{ +#ifdef DETECT_PAD_INPUT_SWITCH + if (CPad::GetPad(0)->IsAffectedByController) { + wchar wstr[16]; + + const char* (*Buttons)[MAX_CONTROLLERACTIONS]; + +#ifdef BUTTON_ICONS + #ifdef GAMEPAD_MENU + switch (FrontEndMenuManager.m_PrefsControllerType) + { + case CMenuManager::CONTROLLER_DUALSHOCK2: + case CMenuManager::CONTROLLER_DUALSHOCK3: + case CMenuManager::CONTROLLER_DUALSHOCK4: + Buttons = CFont::ButtonsSlot != -1 ? PlayStationButtons : PlayStationButtons_noIcons; + break; + case CMenuManager::CONTROLLER_NINTENDO_SWITCH: + Buttons = CFont::ButtonsSlot != -1 ? NintendoSwitchButtons : NintendoSwitchButtons_noIcons; + break; + default: + #endif + Buttons = CFont::ButtonsSlot != -1 ? XboxButtons : XboxButtons_noIcons; + #ifdef GAMEPAD_MENU + break; + } + #endif +#else + switch (FrontEndMenuManager.m_PrefsControllerType) + { + case CMenuManager::CONTROLLER_DUALSHOCK2: + case CMenuManager::CONTROLLER_DUALSHOCK3: + case CMenuManager::CONTROLLER_DUALSHOCK4: + Buttons = PlayStationButtons_noIcons; + break; + case CMenuManager::CONTROLLER_NINTENDO_SWITCH: + Buttons = NintendoSwitchButtons_noIcons; + break; + default: + Buttons = XboxButtons_noIcons; + break; + } +#endif + + assert(Buttons[CPad::GetPad(0)->Mode][action] != nil); // we cannot use these + AsciiToUnicode(Buttons[CPad::GetPad(0)->Mode][action], wstr); + + CMessages::WideStringCopy(text, wstr, leight); + return; + } +#endif + + int32 nums = GetNumOfSettingsForAction((e_ControllerAction)action); + + int32 sets = 0; + + for (int32 i = SETORDER_1; i < MAX_SETORDERS; i++) + { + wchar *textorder = ControlsManager.GetControllerSettingTextWithOrderNumber((e_ControllerAction)action, (eContSetOrder)i); + if (textorder != NULL) + { + uint16 len = CMessages::GetWideStringLength(text); + CMessages::WideStringCopy(&text[len], textorder, leight - len); + + if (++sets < nums) + { + if (sets == nums - 1) + { + // if last - text += ' or '; + uint16 pos1 = CMessages::GetWideStringLength(text); + text[pos1] = ' '; + + CMessages::WideStringCopy(&text[pos1 + 1], + TheText.Get("FEC_ORR"), // "or" + leight - (pos1 + 1)); + + uint16 pos2 = CMessages::GetWideStringLength(text); + text[pos2 + 0] = ' '; + text[pos2 + 1] = '\0'; + } + else + { + // text += ', '; + uint16 pos1 = CMessages::GetWideStringLength(text); + text[pos1 + 0] = ','; + text[pos1 + 1] = ' '; + text[pos1 + 2] = '\0'; + + uint16 pos2 = CMessages::GetWideStringLength(text); + } + } + } + } +} + +int32 CControllerConfigManager::GetControllerKeyAssociatedWithAction(e_ControllerAction action, eControllerType type) +{ + return m_aSettings[action][type].m_Key; +} + +void CControllerConfigManager::UpdateJoyButtonState(int32 padnumber) +{ + for (int32 i = 0; i < MAX_BUTTONS; i++) + m_aButtonStates[i] = false; + +#ifdef __DINPUT_INCLUDED__ + for (int32 i = 0; i < MAX_BUTTONS; i++) + { + if (m_NewState.rgbButtons[i] & 0x80) + m_aButtonStates[i] = true; + else + m_aButtonStates[i] = false; + } +#elif defined RW_GL3 + if (m_NewState.isGamepad) { + for (int32 i = 0; i < MAX_BUTTONS; i++) { + if (i == GLFW_GAMEPAD_BUTTON_GUIDE) + continue; + + m_aButtonStates[MapIdToButtonId(i)-1] = m_NewState.mappedButtons[i]; + } + } else { + for (int32 i = 0; i < Min(m_NewState.numButtons, MAX_BUTTONS); i++) { + m_aButtonStates[i] = m_NewState.buttons[i]; + } + } +#endif +} + +bool CControllerConfigManager::GetIsActionAButtonCombo(e_ControllerAction action) +{ + switch (action) + { + case VEHICLE_LOOKBEHIND: + case PED_CYCLE_TARGET_LEFT: + case PED_CYCLE_TARGET_RIGHT: + return true; + break; + default: break; + } + + return false; +} + +wchar *CControllerConfigManager::GetButtonComboText(e_ControllerAction action) +{ + switch (action) + { + case PED_CYCLE_TARGET_LEFT: + return TheText.Get("FEC_PTL"); // Use LockTarget with Weapon Switch Left. + break; + + case PED_CYCLE_TARGET_RIGHT: + return TheText.Get("FEC_PTR"); // Use LockTarget with Weapon Switch Right. + break; + + case VEHICLE_LOOKBEHIND: + return TheText.Get("FEC_LBC"); // Use Look Left With Look Right. + break; + default: break; + } + + return NULL; +} + +void CControllerConfigManager::SetControllerKeyAssociatedWithAction(e_ControllerAction action, int32 key, eControllerType type) +{ + ResetSettingOrder(action); + int numOfSettings = GetNumOfSettingsForAction(action); + + m_aSettings[action][type].m_Key = key; + m_aSettings[action][type].m_ContSetOrder = numOfSettings + 1; +} + +int32 CControllerConfigManager::GetMouseButtonAssociatedWithAction(e_ControllerAction action) +{ + return m_aSettings[action][MOUSE].m_Key; +} + +void CControllerConfigManager::SetMouseButtonAssociatedWithAction(e_ControllerAction action, int32 button) +{ + int numOfSettings = GetNumOfSettingsForAction(action); + + m_aSettings[action][MOUSE].m_Key = button; + m_aSettings[action][MOUSE].m_ContSetOrder = numOfSettings + 1; +} + +void CControllerConfigManager::ResetSettingOrder(e_ControllerAction action) +{ + int32 conttype = KEYBOARD; + + for (int32 i = SETORDER_1; i < MAX_SETORDERS; i++) + { + bool isexist = false; + for (int32 j = 0; j < MAX_CONTROLLERTYPES; j++) + { + if (m_aSettings[action][j].m_ContSetOrder == i) + isexist = true; + } + + bool init = false; + + if (!isexist) + { + for (int32 k = 0; k < MAX_CONTROLLERTYPES; k++) + { + int32 setorder = m_aSettings[action][k].m_ContSetOrder; + if (setorder > i && setorder != 0) + { + if (init) + { + if (setorder < m_aSettings[action][conttype].m_ContSetOrder) + conttype = k; + } + else + { + init = true; + conttype = k; + } + } + } + + if (init) + m_aSettings[action][conttype].m_ContSetOrder = i; + } + } +} diff --git a/src/miami/core/ControllerConfig.h b/src/miami/core/ControllerConfig.h new file mode 100644 index 00000000..e150c1bd --- /dev/null +++ b/src/miami/core/ControllerConfig.h @@ -0,0 +1,229 @@ +#pragma once + +#if defined RW_D3D9 || defined RWLIBS +#define DIRECTINPUT_VERSION 0x0800 +#include +#endif + +// based on x-gtasa + +enum eControllerType +{ + KEYBOARD = 0, + OPTIONAL_EXTRA, + MOUSE, + JOYSTICK, + MAX_CONTROLLERTYPES, +}; + +enum e_ControllerAction +{ + PED_FIREWEAPON = 0, + PED_CYCLE_WEAPON_RIGHT, + PED_CYCLE_WEAPON_LEFT, + GO_FORWARD, + GO_BACK, + GO_LEFT, + GO_RIGHT, + PED_SNIPER_ZOOM_IN, + PED_SNIPER_ZOOM_OUT, + VEHICLE_ENTER_EXIT, + CAMERA_CHANGE_VIEW_ALL_SITUATIONS, + PED_JUMPING, + PED_SPRINT, + PED_LOOKBEHIND, + PED_DUCK, + PED_ANSWER_PHONE, +#ifdef BIND_VEHICLE_FIREWEAPON + VEHICLE_FIREWEAPON, +#endif + VEHICLE_ACCELERATE, + VEHICLE_BRAKE, + VEHICLE_CHANGE_RADIO_STATION, + VEHICLE_HORN, + TOGGLE_SUBMISSIONS, + VEHICLE_HANDBRAKE, + PED_1RST_PERSON_LOOK_LEFT, + PED_1RST_PERSON_LOOK_RIGHT, + VEHICLE_LOOKLEFT, + VEHICLE_LOOKRIGHT, + VEHICLE_LOOKBEHIND, + VEHICLE_TURRETLEFT, + VEHICLE_TURRETRIGHT, + VEHICLE_TURRETUP, + VEHICLE_TURRETDOWN, + PED_CYCLE_TARGET_LEFT, + PED_CYCLE_TARGET_RIGHT, + PED_CENTER_CAMERA_BEHIND_PLAYER, + PED_LOCK_TARGET, + NETWORK_TALK, + PED_1RST_PERSON_LOOK_UP, + PED_1RST_PERSON_LOOK_DOWN, + _CONTROLLERACTION_36, // Unused + TOGGLE_DPAD, + SWITCH_DEBUG_CAM_ON, + TAKE_SCREEN_SHOT, + SHOW_MOUSE_POINTER_TOGGLE, + UNKNOWN_ACTION, + MAX_CONTROLLERACTIONS, +}; + +enum e_ControllerActionType +{ + ACTIONTYPE_1RSTPERSON = 0, + ACTIONTYPE_3RDPERSON, + ACTIONTYPE_VEHICLE, + ACTIONTYPE_VEHICLE_3RDPERSON, + ACTIONTYPE_COMMON, + ACTIONTYPE_1RST3RDPERSON, + ACTIONTYPE_NONE, +}; + +enum eContSetOrder +{ + SETORDER_NONE = 0, + SETORDER_1, + SETORDER_2, + SETORDER_3, + SETORDER_4, + MAX_SETORDERS, +}; + +enum eSimCheckers +{ + SIM_X1 = 0, SIM_Y1, // DPad + SIM_X2, SIM_Y2, // LeftStick + + MAX_SIMS +}; + +class CMouseControllerState; +class CControllerState; + + +#define JOY_BUTTONS 16 +#define MAX_BUTTONS (JOY_BUTTONS+1) + +#define ACTIONNAME_LENGTH 40 + +#ifdef RW_GL3 +struct GlfwJoyState { + int8 id; + bool isGamepad; + uint8 numButtons; + uint8* buttons; + bool mappedButtons[17]; +}; +#endif + +class CControllerConfigManager +{ +public: + struct tControllerConfigBind + { + int32 m_Key; + int32 m_ContSetOrder; + + tControllerConfigBind() + { + m_Key = 0; + m_ContSetOrder = 0; + } + }; + + bool m_bFirstCapture; +#if defined RW_GL3 + GlfwJoyState m_OldState; + GlfwJoyState m_NewState; +#else + // DIJOYSTATE2 m_OldState; + // DIJOYSTATE2 m_NewState; +#endif + wchar m_aActionNames[MAX_CONTROLLERACTIONS][ACTIONNAME_LENGTH]; + bool m_aButtonStates[MAX_BUTTONS]; + tControllerConfigBind m_aSettings[MAX_CONTROLLERACTIONS][MAX_CONTROLLERTYPES]; + bool m_aSimCheckers[MAX_SIMS][MAX_CONTROLLERTYPES]; + bool m_bMouseAssociated; + +#ifdef LOAD_INI_SETTINGS + static uint32 ms_padButtonsInited; +#endif + + CControllerConfigManager(); + + void MakeControllerActionsBlank(); + + int32 GetJoyButtonJustDown(); + + void SaveSettings(int32 file); + void LoadSettings(int32 file); + + void InitDefaultControlConfiguration(); + void InitDefaultControlConfigMouse(CMouseControllerState const &availableButtons); + void InitDefaultControlConfigJoyPad(uint32 buttons); + void InitialiseControllerActionNameArray(); + + void UpdateJoyInConfigMenus_ButtonDown (int32 button, int32 padnumber); + void AffectControllerStateOn_ButtonDown (int32 button, eControllerType type); + void AffectControllerStateOn_ButtonDown_Driving (int32 button, eControllerType type, CControllerState &state); + void AffectControllerStateOn_ButtonDown_FirstPersonOnly (int32 button, eControllerType type, CControllerState &state); + void AffectControllerStateOn_ButtonDown_ThirdPersonOnly (int32 button, eControllerType type, CControllerState &state); + void AffectControllerStateOn_ButtonDown_FirstAndThirdPersonOnly (int32 button, eControllerType type, CControllerState &state); + void AffectControllerStateOn_ButtonDown_AllStates (int32 button, eControllerType type, CControllerState &state); + void AffectControllerStateOn_ButtonDown_VehicleAndThirdPersonOnly(int32 button, eControllerType type, CControllerState &state); + + void UpdateJoyInConfigMenus_ButtonUp(int32 button, int32 padnumber); + void AffectControllerStateOn_ButtonUp(int32 button, eControllerType type); + void AffectControllerStateOn_ButtonUp_All_Player_States(int32 button, eControllerType type, CControllerState &state); + + void AffectPadFromKeyBoard(); + void AffectPadFromMouse(); + + void ClearSimButtonPressCheckers(); + + bool GetIsKeyboardKeyDown (RsKeyCodes keycode); + bool GetIsKeyboardKeyJustDown(RsKeyCodes keycode); + bool GetIsMouseButtonDown (RsKeyCodes keycode); + bool GetIsMouseButtonUp (RsKeyCodes keycode); + + + void DeleteMatchingCommonControls (e_ControllerAction action, int32 key, eControllerType type); + void DeleteMatching3rdPersonControls (e_ControllerAction action, int32 key, eControllerType type); + void DeleteMatching1rst3rdPersonControls (e_ControllerAction action, int32 key, eControllerType type); + void DeleteMatchingVehicleControls (e_ControllerAction action, int32 key, eControllerType type); + void DeleteMatchingVehicle_3rdPersonControls(e_ControllerAction action, int32 key, eControllerType type); + void DeleteMatching1rstPersonControls (e_ControllerAction action, int32 key, eControllerType type); + void DeleteMatchingActionInitiators (e_ControllerAction action, int32 key, eControllerType type); + +#ifdef RADIO_SCROLL_TO_PREV_STATION + bool IsAnyVehicleActionAssignedToMouseKey(int32 key); +#endif + + bool GetIsKeyBlank(int32 key, eControllerType type); + e_ControllerActionType GetActionType(e_ControllerAction action); + + void ClearSettingsAssociatedWithAction (e_ControllerAction action, eControllerType type); + wchar *GetControllerSettingTextWithOrderNumber(e_ControllerAction action, eContSetOrder setorder); + wchar *GetControllerSettingTextKeyBoard (e_ControllerAction action, eControllerType type); + wchar *GetControllerSettingTextMouse (e_ControllerAction action); + wchar *GetControllerSettingTextJoystick (e_ControllerAction action); + + int32 GetNumOfSettingsForAction(e_ControllerAction action); + void GetWideStringOfCommandKeys(uint16 action, wchar *text, uint16 leight); + int32 GetControllerKeyAssociatedWithAction(e_ControllerAction action, eControllerType type); + + void UpdateJoyButtonState(int32 padnumber); + + bool GetIsActionAButtonCombo (e_ControllerAction action); + wchar *GetButtonComboText (e_ControllerAction action); + void SetControllerKeyAssociatedWithAction(e_ControllerAction action, int32 key, eControllerType type); + int32 GetMouseButtonAssociatedWithAction (e_ControllerAction action); + void SetMouseButtonAssociatedWithAction (e_ControllerAction action, int32 button); + void ResetSettingOrder (e_ControllerAction action); +}; + +#ifndef RW_GL3 +VALIDATE_SIZE(CControllerConfigManager, 0x143C); +#endif + +extern CControllerConfigManager ControlsManager; \ No newline at end of file diff --git a/src/miami/core/Crime.h b/src/miami/core/Crime.h new file mode 100644 index 00000000..4c7ea315 --- /dev/null +++ b/src/miami/core/Crime.h @@ -0,0 +1,39 @@ +#pragma once + +enum eCrimeType { + CRIME_NONE, + CRIME_POSSESSION_GUN, + CRIME_HIT_PED, + CRIME_HIT_COP, + CRIME_SHOOT_PED, + CRIME_SHOOT_COP, + CRIME_STEAL_CAR, + CRIME_RUN_REDLIGHT, + CRIME_RECKLESS_DRIVING, + CRIME_SPEEDING, + CRIME_RUNOVER_PED, + CRIME_RUNOVER_COP, + CRIME_SHOOT_HELI, + CRIME_PED_BURNED, + CRIME_COP_BURNED, + CRIME_VEHICLE_BURNED, + CRIME_DESTROYED_CESSNA, + CRIME_EXPLOSION, + CRIME_HIT_PED_NASTYWEAPON, + CRIME_HIT_COP_NASTYWEAPON, + NUM_CRIME_TYPES +}; + +class CCrimeBeingQd +{ +public: + eCrimeType m_nType; + int32 m_nId; + uint32 m_nTime; + CVector m_vecPosn; + bool m_bReported; + bool m_bPoliceDoesntCare; + + CCrimeBeingQd() { }; + ~CCrimeBeingQd() { }; +}; diff --git a/src/miami/core/Debug.cpp b/src/miami/core/Debug.cpp new file mode 100644 index 00000000..e319388c --- /dev/null +++ b/src/miami/core/Debug.cpp @@ -0,0 +1,170 @@ +#include "common.h" +#include "RwHelper.h" +#include "Debug.h" +#include "Lines.h" +#include "Font.h" +#include "main.h" +#include "Text.h" + +bool gbDebugStuffInRelease = false; + +#define DEBUG_X_POS (300) +#define DEBUG_Y_POS (41) +#define DEBUG_LINE_HEIGHT (22) + +int16 CDebug::ms_nCurrentTextLine; +char CDebug::ms_aTextBuffer[MAX_LINES][MAX_STR_LEN]; + +void +CDebug::DebugInitTextBuffer() +{ + ms_nCurrentTextLine = 0; +} + +void +CDebug::DebugAddText(const char *str) +{ + int32 i = 0; + if (*str != '\0') { + while (i < MAX_STR_LEN - 1) { + ms_aTextBuffer[ms_nCurrentTextLine][i++] = *(str++); + if (*str == '\0') + break; + } + } + + ms_aTextBuffer[ms_nCurrentTextLine++][i] = '\0'; + if (ms_nCurrentTextLine >= MAX_LINES) + ms_nCurrentTextLine = 0; +} + +void +CDebug::DebugDisplayTextBuffer() +{ +#ifndef MASTER + if (gbDebugStuffInRelease) + { + int32 i = 0; + int32 y = DEBUG_Y_POS; +#ifdef FIX_BUGS + CFont::SetPropOn(); + CFont::SetBackgroundOff(); + CFont::SetScale(1.0f, 1.0f); + CFont::SetCentreOff(); + CFont::SetRightJustifyOff(); + CFont::SetJustifyOn(); + CFont::SetRightJustifyWrap(0.0f); + CFont::SetBackGroundOnlyTextOff(); + CFont::SetFontStyle(FONT_STANDARD); +#else + // this is not even readable + CFont::SetPropOff(); + CFont::SetBackgroundOff(); + CFont::SetScale(1.0f, 1.0f); + CFont::SetCentreOff(); + CFont::SetRightJustifyOn(); + CFont::SetRightJustifyWrap(0.0f); + CFont::SetBackGroundOnlyTextOff(); + CFont::SetFontStyle(FONT_STANDARD); + CFont::SetPropOff(); +#endif + do { + char *line; + while (true) { + line = ms_aTextBuffer[(ms_nCurrentTextLine + i++) % MAX_LINES]; + if (*line != '\0') + break; + y += DEBUG_LINE_HEIGHT; + if (i == MAX_LINES) { + CFont::DrawFonts(); + return; + } + } + AsciiToUnicode(line, gUString); + CFont::SetColor(CRGBA(0, 0, 0, 255)); + CFont::PrintString(DEBUG_X_POS, y-1, gUString); + CFont::SetColor(CRGBA(255, 128, 128, 255)); + CFont::PrintString(DEBUG_X_POS+1, y, gUString); + y += DEBUG_LINE_HEIGHT; + } while (i != MAX_LINES); + CFont::DrawFonts(); + } +#endif +} + + +// custom + +CDebug::ScreenStr CDebug::ms_aScreenStrs[MAX_SCREEN_STRS]; +int CDebug::ms_nScreenStrs; + +void +CDebug::DisplayScreenStrings() +{ + int i; + + + CFont::SetPropOn(); + CFont::SetBackgroundOff(); + CFont::SetScale(1.0f, 1.0f); + CFont::SetCentreOff(); + CFont::SetRightJustifyOff(); + CFont::SetJustifyOff(); + CFont::SetRightJustifyWrap(0.0f); + CFont::SetWrapx(9999.0f); + CFont::SetBackGroundOnlyTextOff(); + CFont::SetFontStyle(FONT_STANDARD); + + for(i = 0; i < ms_nScreenStrs; i++){ +/* + AsciiToUnicode(ms_aScreenStrs[i].str, gUString); + CFont::SetColor(CRGBA(0, 0, 0, 255)); + CFont::PrintString(ms_aScreenStrs[i].x, ms_aScreenStrs[i].y, gUString); + CFont::SetColor(CRGBA(255, 255, 255, 255)); + CFont::PrintString(ms_aScreenStrs[i].x+1, ms_aScreenStrs[i].y+1, gUString); +*/ + ObrsPrintfString(ms_aScreenStrs[i].str, ms_aScreenStrs[i].x, ms_aScreenStrs[i].y); + } + CFont::DrawFonts(); + + ms_nScreenStrs = 0; +} + +void +CDebug::PrintAt(const char *str, int x, int y) +{ + if(ms_nScreenStrs >= MAX_SCREEN_STRS) + return; + strncpy(ms_aScreenStrs[ms_nScreenStrs].str, str, 256); + ms_aScreenStrs[ms_nScreenStrs].x = x;//*12; + ms_aScreenStrs[ms_nScreenStrs].y = y;//*22; + ms_nScreenStrs++; +} + +CDebug::Line CDebug::ms_aLines[MAX_DEBUG_LINES]; +int CDebug::ms_nLines; + +void +CDebug::AddLine(CVector p1, CVector p2, uint32 c1, uint32 c2) +{ + if(ms_nLines >= MAX_DEBUG_LINES) + return; + ms_aLines[ms_nLines].p1 = p1; + ms_aLines[ms_nLines].p2 = p2; + ms_aLines[ms_nLines].c1 = c1; + ms_aLines[ms_nLines].c2 = c2; + ms_nLines++; +} + +void +CDebug::DrawLines(void) +{ + int i; + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + for(i = 0; i < ms_nLines; i++){ + Line *l = &ms_aLines[i]; + CLines::RenderLineWithClipping(l->p1.x, l->p1.y, l->p1.z, l->p2.x, l->p2.y, l->p2.z, l->c1, l->c2); + } + ms_nLines = 0; +} diff --git a/src/miami/core/Debug.h b/src/miami/core/Debug.h new file mode 100644 index 00000000..4a25bf41 --- /dev/null +++ b/src/miami/core/Debug.h @@ -0,0 +1,45 @@ +#pragma once + +class CDebug +{ + enum + { + MAX_LINES = 15, + MAX_STR_LEN = 80, + + MAX_SCREEN_STRS = 100, + MAX_DEBUG_LINES = 100, + }; + + static int16 ms_nCurrentTextLine; + static char ms_aTextBuffer[MAX_LINES][MAX_STR_LEN]; + + // custom + struct ScreenStr { + int x, y; + char str[256]; + }; + static ScreenStr ms_aScreenStrs[MAX_SCREEN_STRS]; + static int ms_nScreenStrs; + + struct Line { + CVector p1, p2; + uint32 c1, c2; + }; + static Line ms_aLines[MAX_DEBUG_LINES]; + static int ms_nLines; + +public: + static void DebugInitTextBuffer(); + static void DebugDisplayTextBuffer(); + static void DebugAddText(const char *str); + + // custom + static void PrintAt(const char *str, int x, int y); + static void DisplayScreenStrings(); + + static void AddLine(CVector p1, CVector p2, uint32 c1, uint32 c2); + static void DrawLines(void); +}; + +extern bool gbDebugStuffInRelease; diff --git a/src/miami/core/Directory.cpp b/src/miami/core/Directory.cpp new file mode 100644 index 00000000..05344065 --- /dev/null +++ b/src/miami/core/Directory.cpp @@ -0,0 +1,74 @@ +#include "common.h" + +#include "General.h" +#include "FileMgr.h" +#include "Directory.h" + +CDirectory::CDirectory(int32 maxEntries) + : numEntries(0), maxEntries(maxEntries) +{ + entries = new DirectoryInfo[maxEntries]; +} + +CDirectory::~CDirectory(void) +{ + delete[] entries; +} + +void +CDirectory::ReadDirFile(const char *filename) +{ + int fd; + DirectoryInfo dirinfo; + + fd = CFileMgr::OpenFile(filename, "rb"); + while(CFileMgr::Read(fd, (char*)&dirinfo, sizeof(dirinfo))) + AddItem(dirinfo); + CFileMgr::CloseFile(fd); +} + +bool +CDirectory::WriteDirFile(const char *filename) +{ + int fd; + size_t n; + fd = CFileMgr::OpenFileForWriting(filename); + n = CFileMgr::Write(fd, (char*)entries, numEntries*sizeof(DirectoryInfo)); + CFileMgr::CloseFile(fd); + return n == numEntries*sizeof(DirectoryInfo); +} + +void +CDirectory::AddItem(const DirectoryInfo &dirinfo) +{ + assert(numEntries < maxEntries); +#ifdef FIX_BUGS + // don't add if already exists + uint32 offset, size; + if(FindItem(dirinfo.name, offset, size)) + return; +#endif + entries[numEntries++] = dirinfo; +} + +void +CDirectory::AddItem(const DirectoryInfo &dirinfo, int32 imgId) +{ + DirectoryInfo di = dirinfo; + di.offset |= imgId<<24; + AddItem(di); +} + +bool +CDirectory::FindItem(const char *name, uint32 &offset, uint32 &size) +{ + int i; + + for(i = 0; i < numEntries; i++) + if(!CGeneral::faststricmp(entries[i].name, name)){ + offset = entries[i].offset; + size = entries[i].size; + return true; + } + return false; +} diff --git a/src/miami/core/Directory.h b/src/miami/core/Directory.h new file mode 100644 index 00000000..0fef080f --- /dev/null +++ b/src/miami/core/Directory.h @@ -0,0 +1,23 @@ +#pragma once + +class CDirectory +{ +public: + struct DirectoryInfo { + uint32 offset; + uint32 size; + char name[24]; + }; + DirectoryInfo *entries; + int32 maxEntries; + int32 numEntries; + + CDirectory(int32 maxEntries); + ~CDirectory(void); + + void ReadDirFile(const char *filename); + bool WriteDirFile(const char *filename); + void AddItem(const DirectoryInfo &dirinfo); + void AddItem(const DirectoryInfo &dirinfo, int32 imgId); + bool FindItem(const char *name, uint32 &offset, uint32 &size); +}; diff --git a/src/miami/core/EventList.cpp b/src/miami/core/EventList.cpp new file mode 100644 index 00000000..b7f139b4 --- /dev/null +++ b/src/miami/core/EventList.cpp @@ -0,0 +1,251 @@ +#include "common.h" + +#include "Pools.h" +#include "ModelIndices.h" +#include "World.h" +#include "Wanted.h" +#include "EventList.h" +#include "Messages.h" +#include "Text.h" +#include "main.h" +#include "Accident.h" + +int32 CEventList::ms_nFirstFreeSlotIndex; +CEvent gaEvent[NUMEVENTS]; + +enum +{ + EVENT_STATE_0, + EVENT_STATE_CANDELETE, + EVENT_STATE_CLEAR, +}; + +void +CEventList::Initialise(void) +{ + int i; + + debug("Initialising CEventList..."); + for(i = 0; i < NUMEVENTS; i++){ + gaEvent[i].type = EVENT_NULL; + gaEvent[i].entityType = EVENT_ENTITY_NONE; + gaEvent[i].entityRef = 0; + gaEvent[i].posn.x = 0.0f; + gaEvent[i].posn.y = 0.0f; + gaEvent[i].posn.z = 0.0f; + gaEvent[i].timeout = 0; + gaEvent[i].state = EVENT_STATE_0; + } + ms_nFirstFreeSlotIndex = 0; +} + +void +CEventList::Update(void) +{ + int i; + + ms_nFirstFreeSlotIndex = 0; + for(i = 0; i < NUMEVENTS; i++){ + if(gaEvent[i].type == EVENT_NULL) + continue; + if(CTimer::GetTimeInMilliseconds() > gaEvent[i].timeout || gaEvent[i].state == EVENT_STATE_CANDELETE){ + gaEvent[i].type = EVENT_NULL; + gaEvent[i].state = EVENT_STATE_0; + } + if(gaEvent[i].state == EVENT_STATE_CLEAR) + gaEvent[i].state = EVENT_STATE_CANDELETE; + } +} + +void +CEventList::RegisterEvent(eEventType type, eEventEntity entityType, CEntity *ent, CPed *criminal, int32 timeout) +{ + int i; + int ref; + bool copsDontCare; + +#ifdef SQUEEZE_PERFORMANCE + if (type == EVENT_INJURED_PED) { + gAccidentManager.ReportAccident((CPed*)ent); + return; + } +#endif + + copsDontCare = false; + switch(entityType){ + case EVENT_ENTITY_PED: + ref = CPools::GetPedRef((CPed*)ent); + break; + case EVENT_ENTITY_VEHICLE: + ref = CPools::GetVehicleRef((CVehicle*)ent); + break; + case EVENT_ENTITY_OBJECT: + ref = CPools::GetObjectRef((CObject*)ent); + break; + default: + Error("Undefined entity type, RegisterEvent, EventList.cpp"); + ref = 0; + break; + } + + // only update time if event exists already + for(i = 0; i < NUMEVENTS; i++) + if(gaEvent[i].type == type && + gaEvent[i].entityType == entityType && + gaEvent[i].entityRef == ref){ + gaEvent[i].timeout = CTimer::GetTimeInMilliseconds() + timeout; + return; + } + + for(i = ms_nFirstFreeSlotIndex; i < NUMEVENTS; i++) + if(gaEvent[i].type == EVENT_NULL){ + ms_nFirstFreeSlotIndex = i; + break; + } + if(i < NUMEVENTS){ + gaEvent[i].type = type; + gaEvent[i].entityType = entityType; + gaEvent[i].timeout = CTimer::GetTimeInMilliseconds() + timeout; + gaEvent[i].entityRef = ref; + gaEvent[i].posn = ent->GetPosition(); + gaEvent[i].criminal = criminal; + if(gaEvent[i].criminal) + gaEvent[i].criminal->RegisterReference((CEntity**)&gaEvent[i].criminal); + if(type == EVENT_GUNSHOT) + gaEvent[i].state = EVENT_STATE_CLEAR; + else + gaEvent[i].state = EVENT_STATE_0; + } + + if(criminal == FindPlayerPed()) + ReportCrimeForEvent(type, (intptr)ent, copsDontCare); +} + +void +CEventList::RegisterEvent(eEventType type, CVector posn, int32 timeout) +{ + int i; + + // only update time if event exists already + for(i = 0; i < NUMEVENTS; i++) + if(gaEvent[i].type == type && + gaEvent[i].posn.x == posn.x && + gaEvent[i].posn.y == posn.y && + gaEvent[i].posn.z == posn.z && + gaEvent[i].entityType == EVENT_ENTITY_NONE){ + gaEvent[i].timeout = CTimer::GetTimeInMilliseconds() + timeout; + return; + } + + for(i = ms_nFirstFreeSlotIndex; i < NUMEVENTS; i++) + if(gaEvent[i].type == EVENT_NULL){ + ms_nFirstFreeSlotIndex = i; + break; + } + if(i < NUMEVENTS){ + gaEvent[i].type = type; + gaEvent[i].entityType = EVENT_ENTITY_NONE; + gaEvent[i].timeout = CTimer::GetTimeInMilliseconds() + timeout; + gaEvent[i].posn = posn; + gaEvent[i].entityRef = 0; + if(type == EVENT_GUNSHOT) + gaEvent[i].state = EVENT_STATE_CLEAR; + else + gaEvent[i].state = EVENT_STATE_0; + } +} + +bool +CEventList::GetEvent(eEventType type, int32 *event) +{ + int i; + for(i = 0; i < NUMEVENTS; i++) + if(gaEvent[i].type == type){ + *event = i; + return true; + } + return false; +} + +void +CEventList::ClearEvent(int32 event) +{ + if(gaEvent[event].state != EVENT_STATE_CANDELETE) + gaEvent[event].state = EVENT_STATE_CLEAR; +} + +bool +CEventList::FindClosestEvent(eEventType type, CVector posn, int32 *event) +{ + int i; + float dist; + bool found = false; + float mindist = 60.0f; + + for(i = 0; i < NUMEVENTS; i++){ + if(gaEvent[i].type != type) + continue; + dist = (posn - gaEvent[i].posn).Magnitude(); + if(dist < mindist){ + mindist = dist; + found = true; + *event = i; + } + } + return found; +} + +void +CEventList::ReportCrimeForEvent(eEventType type, intptr crimeId, bool copsDontCare) +{ + eCrimeType crime; + switch(type){ + case EVENT_ASSAULT: crime = CRIME_HIT_PED; break; + case EVENT_RUN_REDLIGHT: crime = CRIME_RUN_REDLIGHT; break; + case EVENT_ASSAULT_POLICE: crime = CRIME_HIT_COP; break; + case EVENT_GUNSHOT: crime = CRIME_POSSESSION_GUN; break; + case EVENT_STEAL_CAR: crime = CRIME_STEAL_CAR; break; + case EVENT_HIT_AND_RUN: crime = CRIME_RUNOVER_PED; break; + case EVENT_HIT_AND_RUN_COP: crime = CRIME_RUNOVER_COP; break; + case EVENT_SHOOT_PED: crime = CRIME_SHOOT_PED; break; + case EVENT_SHOOT_COP: crime = CRIME_SHOOT_COP; break; + case EVENT_EXPLOSION: crime = CRIME_EXPLOSION; break; + case EVENT_PED_SET_ON_FIRE: crime = CRIME_PED_BURNED; break; + case EVENT_COP_SET_ON_FIRE: crime = CRIME_COP_BURNED; break; + case EVENT_CAR_SET_ON_FIRE: crime = CRIME_VEHICLE_BURNED; break; + case EVENT_ASSAULT_NASTYWEAPON: crime = CRIME_HIT_PED_NASTYWEAPON; break; + case EVENT_ASSAULT_NASTYWEAPON_POLICE: crime = CRIME_HIT_COP_NASTYWEAPON; break; + default: crime = CRIME_NONE; break; + } + + if (crime == CRIME_HIT_PED && IsPedPointerValid((CPed*)crimeId) && FindPlayerPed()->m_pWanted->GetWantedLevel() == 0 && ((CPed*)crimeId)->bBeingChasedByPolice) { + if (!((CPed*)crimeId)->DyingOrDead()) { + CMessages::AddBigMessage(TheText.Get("GOODBOY"), 5000, 0); + CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 50; + } + return; + } + + if(crime == CRIME_NONE) + return; + +#ifdef FIX_BUGS + CVector playerPedCoors = FindPlayerCoors(); +#else + CVector playerPedCoors = FindPlayerPed()->GetPosition(); +#endif + CVector playerCoors = FindPlayerCoors(); + + if(CWanted::WorkOutPolicePresence(playerCoors, 14.0f) != 0 || + CGame::germanGame && (crime == CRIME_SHOOT_PED || crime == CRIME_SHOOT_COP || crime == CRIME_COP_BURNED || crime == CRIME_VEHICLE_BURNED)){ + FindPlayerPed()->m_pWanted->RegisterCrime_Immediately(crime, playerPedCoors, (uint32)crimeId, copsDontCare); + FindPlayerPed()->m_pWanted->SetWantedLevelNoDrop(1); + }else + FindPlayerPed()->m_pWanted->RegisterCrime(crime, playerPedCoors, (uint32)crimeId, copsDontCare); + + if(type == EVENT_ASSAULT_POLICE) + FindPlayerPed()->SetWantedLevelNoDrop(1); + if(type == EVENT_SHOOT_COP || type == EVENT_ASSAULT_NASTYWEAPON_POLICE) + FindPlayerPed()->SetWantedLevelNoDrop(2); + +} diff --git a/src/miami/core/EventList.h b/src/miami/core/EventList.h new file mode 100644 index 00000000..3e9d8fd4 --- /dev/null +++ b/src/miami/core/EventList.h @@ -0,0 +1,68 @@ +#pragma once + +class CEntity; +class CPed; + +enum eEventType +{ + EVENT_NULL, + EVENT_ASSAULT, + EVENT_RUN_REDLIGHT, + EVENT_ASSAULT_POLICE, + EVENT_GUNSHOT, + EVENT_INJURED_PED, + EVENT_DEAD_PED, + EVENT_FIRE, + EVENT_STEAL_CAR, + EVENT_HIT_AND_RUN, + EVENT_HIT_AND_RUN_COP, + EVENT_SHOOT_PED, + EVENT_SHOOT_COP, + EVENT_EXPLOSION, + EVENT_PED_SET_ON_FIRE, + EVENT_COP_SET_ON_FIRE, + EVENT_CAR_SET_ON_FIRE, + EVENT_ASSAULT_NASTYWEAPON, + EVENT_ASSAULT_NASTYWEAPON_POLICE, + EVENT_UNK, // Not on SA it seems + EVENT_ICECREAM, + EVENT_ATM, + EVENT_SHOPSTALL, + EVENT_SHOPWINDOW, + EVENT_LAST_EVENT +}; + +enum eEventEntity +{ + EVENT_ENTITY_NONE, + EVENT_ENTITY_PED, + EVENT_ENTITY_VEHICLE, + EVENT_ENTITY_OBJECT +}; + +struct CEvent +{ + eEventType type; + eEventEntity entityType; + int32 entityRef; + CPed *criminal; + CVector posn; + uint32 timeout; + int32 state; +}; + +class CEventList +{ + static int32 ms_nFirstFreeSlotIndex; +public: + static void Initialise(void); + static void Update(void); + static void RegisterEvent(eEventType type, eEventEntity entityType, CEntity *ent, CPed *criminal, int32 timeout); + static void RegisterEvent(eEventType type, CVector posn, int32 timeout); + static bool GetEvent(eEventType type, int32 *event); + static void ClearEvent(int32 event); + static bool FindClosestEvent(eEventType type, CVector posn, int32 *event); + static void ReportCrimeForEvent(eEventType type, intptr, bool); +}; + +extern CEvent gaEvent[NUMEVENTS]; \ No newline at end of file diff --git a/src/miami/core/FileLoader.cpp b/src/miami/core/FileLoader.cpp new file mode 100644 index 00000000..0a963a3b --- /dev/null +++ b/src/miami/core/FileLoader.cpp @@ -0,0 +1,1769 @@ +#include "common.h" +#include +#include "main.h" + +#include "General.h" +#include "Quaternion.h" +#include "ModelInfo.h" +#include "ModelIndices.h" +#include "TempColModels.h" +#include "VisibilityPlugins.h" +#include "FileMgr.h" +#include "HandlingMgr.h" +#include "CarCtrl.h" +#include "PedType.h" +#include "AnimManager.h" +#include "Game.h" +#include "RwHelper.h" +#include "NodeName.h" +#include "TxdStore.h" +#include "PathFind.h" +#include "ObjectData.h" +#include "DummyObject.h" +#include "World.h" +#include "Zones.h" +#include "ZoneCull.h" +#include "CdStream.h" +#include "FileLoader.h" +#include "MemoryHeap.h" +#include "Streaming.h" +#include "ColStore.h" +#include "Occlusion.h" + +char CFileLoader::ms_line[256]; + +const char* +GetFilename(const char *filename) +{ + char *s = strrchr((char*)filename, '\\'); + return s ? s+1 : filename; +} + +void +LoadingScreenLoadingFile(const char *filename) +{ + sprintf(gString, "Loading %s", GetFilename(filename)); + LoadingScreen("Loading the Game", gString, nil); +} + +void +CFileLoader::LoadLevel(const char *filename) +{ + int fd; + RwTexDictionary *savedTxd; + bool objectsLoaded; + char *line; + char txdname[64]; + + savedTxd = RwTexDictionaryGetCurrent(); + objectsLoaded = false; + if(savedTxd == nil){ + savedTxd = RwTexDictionaryCreate(); + RwTexDictionarySetCurrent(savedTxd); + } + fd = CFileMgr::OpenFile(filename, "r"); + assert(fd > 0); + + for(line = LoadLine(fd); line; line = LoadLine(fd)){ + if(*line == '#') + continue; + + if(strncmp(line, "EXIT", 4) == 0) + break; + + if(strncmp(line, "IMAGEPATH", 9) == 0){ + RwImageSetPath(line + 10); + }else if(strncmp(line, "TEXDICTION", 10) == 0){ + PUSH_MEMID(MEMID_TEXTURES); + strcpy(txdname, line+11); + LoadingScreenLoadingFile(txdname); + RwTexDictionary *txd = LoadTexDictionary(txdname); + AddTexDictionaries(savedTxd, txd); + RwTexDictionaryDestroy(txd); + POP_MEMID(); + }else if(strncmp(line, "COLFILE", 7) == 0){ + LoadingScreenLoadingFile(line+10); + LoadCollisionFile(line+10, 0); + }else if(strncmp(line, "MODELFILE", 9) == 0){ + LoadingScreenLoadingFile(line + 10); + LoadModelFile(line + 10); + }else if(strncmp(line, "HIERFILE", 8) == 0){ + LoadingScreenLoadingFile(line + 9); + LoadClumpFile(line + 9); + }else if(strncmp(line, "IDE", 3) == 0){ + LoadingScreenLoadingFile(line + 4); + LoadObjectTypes(line + 4); + }else if(strncmp(line, "IPL", 3) == 0){ + if(!objectsLoaded){ + LoadingScreenLoadingFile("Collision"); + PUSH_MEMID(MEMID_WORLD); + CObjectData::Initialise("DATA\\OBJECT.DAT"); + CStreaming::Init(); + POP_MEMID(); + PUSH_MEMID(MEMID_COLLISION); + CColStore::LoadAllCollision(); + POP_MEMID(); + for(int i = 0; i < MODELINFOSIZE; i++) + if(CModelInfo::GetModelInfo(i)) + CModelInfo::GetModelInfo(i)->ConvertAnimFileIndex(); + objectsLoaded = true; + } + PUSH_MEMID(MEMID_WORLD); + LoadingScreenLoadingFile(line + 4); + LoadScene(line + 4); + POP_MEMID(); + }else if(strncmp(line, "SPLASH", 6) == 0){ +#ifndef DISABLE_LOADING_SCREEN + LoadSplash(GetRandomSplashScreen()); +#endif +#ifndef GTA_PS2 + }else if(strncmp(line, "CDIMAGE", 7) == 0){ + CdStreamAddImage(line + 8); +#endif + } + } + + CFileMgr::CloseFile(fd); + RwTexDictionarySetCurrent(savedTxd); + + int i; + for(i = 1; i < COLSTORESIZE; i++) + if(CColStore::GetSlot(i)) + CColStore::GetBoundingBox(i).Grow(120.0f); + CWorld::RepositionCertainDynamicObjects(); + CColStore::RemoveAllCollision(); +} + +char* +CFileLoader::LoadLine(int fd) +{ + int i; + char *line; + + if(CFileMgr::ReadLine(fd, ms_line, 256) == false) + return nil; + for(i = 0; ms_line[i] != '\0'; i++) + if(ms_line[i] < ' ' || ms_line[i] == ',') + ms_line[i] = ' '; + for(line = ms_line; *line <= ' ' && *line != '\0'; line++); + return line; +} + +RwTexDictionary* +CFileLoader::LoadTexDictionary(const char *filename) +{ + RwTexDictionary *txd; + RwStream *stream; + + txd = nil; + stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, filename); + debug("Loading texture dictionary file %s\n", filename); + if(stream){ + if(RwStreamFindChunk(stream, rwID_TEXDICTIONARY, nil, nil)) + txd = RwTexDictionaryGtaStreamRead(stream); + RwStreamClose(stream, nil); + } + if(txd == nil) + txd = RwTexDictionaryCreate(); + return txd; +} + +struct ColHeader +{ + uint32 ident; + uint32 size; +}; + +void +CFileLoader::LoadCollisionFile(const char *filename, uint8 colSlot) +{ + int fd; + char modelname[24]; + CBaseModelInfo *mi; + ColHeader header; + + PUSH_MEMID(MEMID_COLLISION); + + debug("Loading collision file %s\n", filename); + fd = CFileMgr::OpenFile(filename, "rb"); + assert(fd > 0); + + while(CFileMgr::Read(fd, (char*)&header, sizeof(header))){ + assert(header.ident == 'CLOC'); + CFileMgr::Read(fd, (char*)work_buff, header.size); + memcpy(modelname, work_buff, 24); + + mi = CModelInfo::GetModelInfo(modelname, nil); + if(mi){ + if(mi->GetColModel() && mi->DoesOwnColModel()){ + LoadCollisionModel(work_buff+24, *mi->GetColModel(), modelname); + }else{ + CColModel *model = new CColModel; + model->level = colSlot; + LoadCollisionModel(work_buff+24, *model, modelname); + mi->SetColModel(model, true); + } + }else{ + debug("colmodel %s can't find a modelinfo\n", modelname); + } + } + + CFileMgr::CloseFile(fd); + + POP_MEMID(); +} + + +bool +CFileLoader::LoadCollisionFileFirstTime(uint8 *buffer, uint32 size, uint8 colSlot) +{ + uint32 modelsize; + char modelname[24]; + CBaseModelInfo *mi; + ColHeader *header; + int32 modelIndex; + + while(size > 8){ + header = (ColHeader*)buffer; + modelsize = header->size; + if(header->ident != 'CLOC') + return size-8 < CDSTREAM_SECTOR_SIZE; + memcpy(modelname, buffer+8, 24); + memcpy(work_buff, buffer+32, modelsize-24); + size -= 32 + (modelsize-24); + buffer += 32 + (modelsize-24); + if(modelsize > 15*1024) + debug("colmodel %s is huge, size %d\n", modelname, modelsize); + + mi = CModelInfo::GetModelInfo(modelname, &modelIndex); + if(mi){ + CColStore::IncludeModelIndex(colSlot, modelIndex); + CColModel *model = new CColModel; + model->level = colSlot; + LoadCollisionModel(work_buff, *model, modelname); + mi->SetColModel(model, true); + }else{ + debug("colmodel %s can't find a modelinfo\n", modelname); + } + } + return true; +} + +bool +CFileLoader::LoadCollisionFile(uint8 *buffer, uint32 size, uint8 colSlot) +{ + uint32 modelsize; + char modelname[24]; + CBaseModelInfo *mi; + ColHeader *header; + + while(size > 8){ + header = (ColHeader*)buffer; + modelsize = header->size; + if(header->ident != 'CLOC') + return size-8 < CDSTREAM_SECTOR_SIZE; + memcpy(modelname, buffer+8, 24); + memcpy(work_buff, buffer+32, modelsize-24); + size -= 32 + (modelsize-24); + buffer += 32 + (modelsize-24); + if(modelsize > 15*1024) + debug("colmodel %s is huge, size %d\n", modelname, modelsize); + + mi = CModelInfo::GetModelInfo(modelname, CColStore::GetSlot(colSlot)->minIndex, CColStore::GetSlot(colSlot)->maxIndex); + if(mi){ + if(mi->GetColModel()){ + LoadCollisionModel(work_buff, *mi->GetColModel(), modelname); + }else{ + CColModel *model = new CColModel; + model->level = colSlot; + LoadCollisionModel(work_buff, *model, modelname); + mi->SetColModel(model, true); + } + }else{ + debug("colmodel %s can't find a modelinfo\n", modelname); + } + } + return true; +} + +void +CFileLoader::LoadCollisionModel(uint8 *buf, CColModel &model, char *modelname) +{ + int i; + + model.boundingSphere.radius = *(float*)(buf); + model.boundingSphere.center.x = *(float*)(buf+4); + model.boundingSphere.center.y = *(float*)(buf+8); + model.boundingSphere.center.z = *(float*)(buf+12); + model.boundingBox.min.x = *(float*)(buf+16); + model.boundingBox.min.y = *(float*)(buf+20); + model.boundingBox.min.z = *(float*)(buf+24); + model.boundingBox.max.x = *(float*)(buf+28); + model.boundingBox.max.y = *(float*)(buf+32); + model.boundingBox.max.z = *(float*)(buf+36); + model.numSpheres = *(int16*)(buf+40); + buf += 44; + if(model.numSpheres > 0){ + model.spheres = (CColSphere*)RwMalloc(model.numSpheres*sizeof(CColSphere)); + REGISTER_MEMPTR(&model.spheres); + for(i = 0; i < model.numSpheres; i++){ + model.spheres[i].Set(*(float*)buf, *(CVector*)(buf+4), buf[16], buf[17]); + buf += 20; + } + }else + model.spheres = nil; + + model.numLines = *(int16*)buf; + buf += 4; + if(model.numLines > 0){ + //model.lines = (CColLine*)RwMalloc(model.numLines*sizeof(CColLine)); + REGISTER_MEMPTR(&model.lines); + for(i = 0; i < model.numLines; i++){ + //model.lines[i].Set(*(CVector*)buf, *(CVector*)(buf+12)); + buf += 24; + } + }else + model.lines = nil; + model.numLines = 0; + model.lines = nil; + + model.numBoxes = *(int16*)buf; + buf += 4; + if(model.numBoxes > 0){ + model.boxes = (CColBox*)RwMalloc(model.numBoxes*sizeof(CColBox)); + REGISTER_MEMPTR(&model.boxes); + for(i = 0; i < model.numBoxes; i++){ + model.boxes[i].Set(*(CVector*)buf, *(CVector*)(buf+12), buf[24], buf[25]); + buf += 28; + } + }else + model.boxes = nil; + + int32 numVertices = *(int16*)buf; + buf += 4; + if(numVertices > 0){ + model.vertices = (CompressedVector*)RwMalloc(numVertices*sizeof(CompressedVector)); + REGISTER_MEMPTR(&model.vertices); + for(i = 0; i < numVertices; i++){ + model.vertices[i].SetFixed(*(int16*)buf, *(int16*)(buf+2), *(int16*)(buf+4)); +#if 0 + if(Abs(*(float*)buf) >= 256.0f || + Abs(*(float*)(buf+4)) >= 256.0f || + Abs(*(float*)(buf+8)) >= 256.0f) + printf("%s:Collision volume too big\n", modelname); +#endif + buf += 6; + } + }else + model.vertices = nil; + + model.numTriangles = *(int16*)buf; + buf += 4; + if(model.numTriangles > 0){ + model.triangles = (CColTriangle*)RwMalloc(model.numTriangles*sizeof(CColTriangle)); + REGISTER_MEMPTR(&model.triangles); + for(i = 0; i < model.numTriangles; i++){ + model.triangles[i].Set(*(uint16*)buf, *(uint16*)(buf+2), *(uint16*)(buf+4), buf[6]); + buf += 8; + assert(model.triangles[i].a < numVertices); + assert(model.triangles[i].b < numVertices); + assert(model.triangles[i].c < numVertices); + } + }else + model.triangles = nil; +} + +static void +GetNameAndLOD(char *nodename, char *name, int *n) +{ + char *underscore = nil; + for(char *s = nodename; *s != '\0'; s++){ + if(s[0] == '_' && (s[1] == 'l' || s[1] == 'L') && isdigit(s[2])) + underscore = s; + } + if(underscore){ + strncpy(name, nodename, underscore - nodename); + name[underscore - nodename] = '\0'; + *n = atoi(underscore + 2); + }else{ + strncpy(name, nodename, 24); + *n = 0; + } +} + +RpAtomic* +CFileLoader::FindRelatedModelInfoCB(RpAtomic *atomic, void *data) +{ + CSimpleModelInfo *mi; + char *nodename, name[24]; + int n; + RpClump *clump = (RpClump*)data; + + nodename = GetFrameNodeName(RpAtomicGetFrame(atomic)); + GetNameAndLOD(nodename, name, &n); + mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(name, nil); + if(mi){ + assert(mi->IsSimple()); + CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); + mi->SetAtomic(n, atomic); + RpClumpRemoveAtomic(clump, atomic); + RpAtomicSetFrame(atomic, RwFrameCreate()); + CVisibilityPlugins::SetAtomicModelInfo(atomic, mi); + }else{ + debug("Can't find Atomic %s\n", name); + } + + return atomic; +} + +#ifdef LIBRW +void +InitClump(RpClump *clump) +{ + RpClumpForAllAtomics(clump, ConvertPlatformAtomic, nil); +} +#else +#define InitClump(clump) +#endif + +void +CFileLoader::LoadModelFile(const char *filename) +{ + RwStream *stream; + RpClump *clump; + + debug("Loading model file %s\n", filename); + stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, filename); + if(RwStreamFindChunk(stream, rwID_CLUMP, nil, nil)){ + clump = RpClumpStreamRead(stream); + if(clump){ + InitClump(clump); + RpClumpForAllAtomics(clump, FindRelatedModelInfoCB, clump); + RpClumpDestroy(clump); + } + } + RwStreamClose(stream, nil); +} + +void +CFileLoader::LoadClumpFile(const char *filename) +{ + RwStream *stream; + RpClump *clump; + char *nodename, name[24]; + int n; + CClumpModelInfo *mi; + + debug("Loading model file %s\n", filename); + stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, filename); + while(RwStreamFindChunk(stream, rwID_CLUMP, nil, nil)){ + clump = RpClumpStreamRead(stream); + if(clump){ + nodename = GetFrameNodeName(RpClumpGetFrame(clump)); + GetNameAndLOD(nodename, name, &n); + mi = (CClumpModelInfo*)CModelInfo::GetModelInfo(name, nil); + if(mi){ + InitClump(clump); + assert(mi->IsClump()); + mi->SetClump(clump); + }else + RpClumpDestroy(clump); + } + } + RwStreamClose(stream, nil); +} + +bool +CFileLoader::LoadClumpFile(RwStream *stream, uint32 id) +{ + RpClump *clump; + CClumpModelInfo *mi; + + if(!RwStreamFindChunk(stream, rwID_CLUMP, nil, nil)) + return false; + clump = RpClumpStreamRead(stream); + if(clump == nil) + return false; + InitClump(clump); + mi = (CClumpModelInfo*)CModelInfo::GetModelInfo(id); + mi->SetClump(clump); + return true; +} + +bool +CFileLoader::StartLoadClumpFile(RwStream *stream, uint32 id) +{ + if(RwStreamFindChunk(stream, rwID_CLUMP, nil, nil)){ + printf("Start loading %s\n", CModelInfo::GetModelInfo(id)->GetModelName()); + return RpClumpGtaStreamRead1(stream); + }else{ + printf("FAILED\n"); + return false; + } +} + +bool +CFileLoader::FinishLoadClumpFile(RwStream *stream, uint32 id) +{ + RpClump *clump; + CClumpModelInfo *mi; + + printf("Finish loading %s\n", CModelInfo::GetModelInfo(id)->GetModelName()); + clump = RpClumpGtaStreamRead2(stream); + + if(clump){ + InitClump(clump); + mi = (CClumpModelInfo*)CModelInfo::GetModelInfo(id); + mi->SetClump(clump); + return true; + }else{ + printf("FAILED\n"); + return false; + } +} + +CSimpleModelInfo *gpRelatedModelInfo; + +bool +CFileLoader::LoadAtomicFile(RwStream *stream, uint32 id) +{ + RpClump *clump; + + if(RwStreamFindChunk(stream, rwID_CLUMP, nil, nil)){ + clump = RpClumpStreamRead(stream); + if(clump == nil) + return false; + InitClump(clump); + gpRelatedModelInfo = (CSimpleModelInfo*)CModelInfo::GetModelInfo(id); + RpClumpForAllAtomics(clump, SetRelatedModelInfoCB, clump); + RpClumpDestroy(clump); + } + return true; +} + +RpAtomic* +CFileLoader::SetRelatedModelInfoCB(RpAtomic *atomic, void *data) +{ + char *nodename, name[24]; + int n; + RpClump *clump = (RpClump*)data; + + nodename = GetFrameNodeName(RpAtomicGetFrame(atomic)); + GetNameAndLOD(nodename, name, &n); + CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); + gpRelatedModelInfo->SetAtomic(n, atomic); + RpClumpRemoveAtomic(clump, atomic); + RpAtomicSetFrame(atomic, RwFrameCreate()); + CVisibilityPlugins::SetAtomicModelInfo(atomic, gpRelatedModelInfo); + return atomic; +} + +RpClump* +CFileLoader::LoadAtomicFile2Return(const char *filename) +{ + RwStream *stream; + RpClump *clump; + + clump = nil; + debug("Loading model file %s\n", filename); + stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, filename); + if(RwStreamFindChunk(stream, rwID_CLUMP, nil, nil)) + clump = RpClumpStreamRead(stream); + if(clump) + InitClump(clump); + RwStreamClose(stream, nil); + return clump; +} + +static RwTexture* +MoveTexturesCB(RwTexture *texture, void *pData) +{ + RwTexDictionaryAddTexture((RwTexDictionary*)pData, texture); + return texture; +} + +void +CFileLoader::AddTexDictionaries(RwTexDictionary *dst, RwTexDictionary *src) +{ + RwTexDictionaryForAllTextures(src, MoveTexturesCB, dst); +} + +#define isLine3(l, a, b, c) ((l[0] == a) && (l[1] == b) && (l[2] == c)) +#define isLine4(l, a, b, c, d) ((l[0] == a) && (l[1] == b) && (l[2] == c) && (l[3] == d)) + +void +CFileLoader::LoadObjectTypes(const char *filename) +{ + enum { + NONE, + OBJS, + MLO, // unused but enum still has it + TOBJ, + WEAP, + HIER, + CARS, + PEDS, + PATH, + TWODFX + }; + char *line; + int fd; + int section; + int pathIndex; + int id, pathType; + int minID, maxID; + + section = NONE; + minID = INT32_MAX; + maxID = -1; + pathIndex = -1; + debug("Loading object types from %s...\n", filename); + + fd = CFileMgr::OpenFile(filename, "rb"); + assert(fd > 0); + for(line = CFileLoader::LoadLine(fd); line; line = CFileLoader::LoadLine(fd)){ + if(*line == '\0' || *line == '#') + continue; + + if(section == NONE){ + if(isLine4(line, 'o','b','j','s')) section = OBJS; + else if(isLine4(line, 't','o','b','j')) section = TOBJ; + else if(isLine4(line, 'w','e','a','p')) section = WEAP; + else if(isLine4(line, 'h','i','e','r')) section = HIER; + else if(isLine4(line, 'c','a','r','s')) section = CARS; + else if(isLine4(line, 'p','e','d','s')) section = PEDS; + else if(isLine4(line, 'p','a','t','h')) section = PATH; + else if(isLine4(line, '2','d','f','x')) section = TWODFX; + }else if(isLine3(line, 'e','n','d')){ + section = NONE; + }else switch(section){ + case OBJS: + id = LoadObject(line); + if(id > maxID) maxID = id; + if(id < minID) minID = id; + break; + case TOBJ: + id = LoadTimeObject(line); + if(id > maxID) maxID = id; + if(id < minID) minID = id; + break; + case WEAP: + LoadWeaponObject(line); + break; + case HIER: + LoadClumpObject(line); + break; + case CARS: + LoadVehicleObject(line); + break; + case PEDS: + LoadPedObject(line); + break; + case PATH: + if(pathIndex == -1){ + id = LoadPathHeader(line, pathType); + pathIndex = 0; + }else{ + if(pathType == 0) + LoadPedPathNode(line, id, pathIndex); + else if (pathType == 1) + LoadCarPathNode(line, id, pathIndex, false); + else if (pathType == 2) + LoadCarPathNode(line, id, pathIndex, true); + pathIndex++; + if(pathIndex == 12) + pathIndex = -1; + } + break; + case TWODFX: + Load2dEffect(line); + break; + } + } + CFileMgr::CloseFile(fd); + + for(id = minID; id <= maxID; id++){ + CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(id); + if(mi && mi->IsBuilding()) + mi->SetupBigBuilding(minID, maxID); + } +} + +#ifdef RW_DC +// PH3NOM - I just searched the extracted img folder for the following DFF names: +//find . -type f -name "*tree*.dff" ! -name "*shad*" +//find . -type f -name "*palm*.dff" ! -name "*shad*" +//find . -type f -name "*fence*.dff" ! -name "*shad*" +//find . -type f -name "*bush*.dff" ! -name "*shad*" +//find . -type f -name "*rail*.dff" ! -name "*shad*" +//find . -type f -name "*veg*.dff" ! -name "*shad*" +char *AlphaTestNames[] = { + "xpolytrees2_dt", + "gf_treesfw3_01", + "ci_trees05", + "xpolytrees3_dt", + "LODrtrees5", + "LODrtrees2", + "nbt_mansiontrees04", + "malltreereflect2", + "veg_treeb1", + "LODrtrees4", + "Streetlamp2", + "nbt_mansiontrees02", + "LODmrgtrees4", + "gf_tree2_02", + "malltreereflect", + "nbt_hoteltrees02", + "veg_treea1", + "Streetlamp1", + "gf_treesfw5_01", + "nbt_mansiontrees05", + "malltrees01", + "xpolytrees4_dt", + "nbt_mansiontrees03", + "xpolytrees1_dt", + "hoteltrees01", + "hoteltrees03", + "gf_treesfw1_01", + "veg_tree3", + "ap_treeshot1_02", + "hoteltrees02", + "ci_trees03", + "veg_treea3", + "mc_treesfw3_01", + "ap_treesfw1_01", + "nbt_hoteltrees01", + "ci_trees04", + "gf_treesfw2_01", + "LODrtrees3", + "ci_trees1", + "mc_treesfw1_01", + "LODrtrees1", + "gf_treesfw4_01", + "nbt_mansiontrees01", + "hoteltrees06", + "mn_treesis_int", + "hoteltrees05", + "mc_treesfw2_01", + "gf_tree1_01", + "mn_treesisl05", + "doublestreetlght1", + "ci_trees02", + "bank_palms09", + "veg_palmkb3", + "lw_palm1", + "veg_palmkb5", + "bank_palms21", + "veg_palmkb1", + "veg_palmbig14", + "veg_palmkb14", + "veg_palm02", + "veg_palmkb2", + "veg_palmkb9", + "bank_palms28", + "veg_palmkb7", + "bank_palms23", + "bank_palms32", + "veg_palmkb8", + "bank_palms02", + "bank_palms33", + "veg_palmkbb11", + "veg_palmkb4", + "veg_palm01", + "veg_palm03", + "veg_palmkb13", + "veg_palmkb10", + "veg_palm04", + "gf_tennisfence", + "marinafence04", + "marinafence01", + "b_hse_pierfence", + "ap_wallfence5", + "dockfence", + "dtn_hospital_fence", + "mc_fence1", + "od_clubfence_dy", + "ap_wallfence1", + "const_woodfence30", + "bb_fence1", + "ap_seafences", + "marinafence03", + "nbthotel08fence02", + "dt_sheraton_fence", + "dt_scabby_fence04", + "nbt_barfence01", + "ap_wallfence2", + "od_clevelfence", + "mc_fence2", + "ci_fence2", + "dt_scabby_fence03", + "mansionfence", + "nbt_hotel07fence", + "marinafence02", + "fence01", + "compound_fence", + "ap_wallfence7", + "fencehaitism", + "od_clubfence_nt", + "marinafence05", + "ap_wallfence6", + "Mansion2_C_fence", + "dt_scabby_fence07", + "kickfence", + "pw_backfence1", + "mc_fence3", + "washfence1", + "LODst_woodfence30", + "washfence2", + "carparkfence0", + "ap_wallfence3", + "fencesmallb", + "ap_wallfence4", + "fencehaiti", + "stationfence", + "dt_scabby_fence01", + "pw_bayfence2_01", + "lha_carfence", + "tall_fence", + "ap_seaplanfence1", + "LODse_pierfence", + "marinaveg3", + "ml_vegbits05", + "svegrgedoor", + "ml_vegbits04", + "nrth3veg35", + "nrth3veg05", + "ht_veg01_nt", + "nbeachvegy1", + "odnvegbush1", + "washvegy4", + "ht_veg02_nt", + "wshotelveg1", + "dtn_veg3", + "nrth7veg09", + "washvegy238", + "veg_palwee02", + "nrth3veg50", + "washpshoutveg", + "nrth4veg08", + "washvegy3", + "washvegy241", + "washvegy2", + "washvegy239", + "odnvegbush2b", + "veg_gaz", + "nrth1veg21", + "ht_veg04_dy", + "ml_vegbits03", + "nbeachvegy2", + "nrth3veg16", + "ht_veg02_dy", + "nrth4veg05", + "ht_veg04_nt", + "veg_palwee01", + "ht_veg01_dy", + "vegeha1", + "veged", + "veg_ivy_balcny_kb3", + "nrth1veg42", + "marinaveg1", + "nrth3veg25", + "veg_fern_balcny_kb1", + "spad_veg1", + "washvegy2413", + "marinaveg2", + "washvegy240", + "vegetationb", + "nrth1veg37", + "ml_vegbits01", + "veged01", + "dtn_veg4", + "washvegy242", + "veg_palwee03", + "nrth3veg59", + "nrth4veg212", + "nrth4veg21", + "nrth7veg", + "washvegy237", + "nrth3veg08", + "nrth4veg09", + "veg_fern_balcny_kb2", + "ml_vegbits02", + "odnvegbush2", + "vegetationb03", + "veged02", + "kb_planterbox", + "plants05b", + "nbdecoshplants", + "ap_planters2_01", + "ml_planterbed", + "marinaplanter1", + "washskyplant1", + "dzplant", + "ci_planter2", + "ap_planters1_01", + "gf_planters4", + "washskyplant2", + "kb_planter+bush", + "marinaplanter2", + "ci_planter1", + "plants04", + "gf_planters2", + "kb_planterbush2", + "plants01", + "kb_planter+bush2", + "plants05", + "ci_busht_04", + "starsbush2", + "mansbushes2", + "odnvegbush1", + "odrv_bushes", + "ci_busht_02", + "mlmallbush", + "mansbushes", + "nbw_bush02", + "ci_busht_08", + "nbw_bush01", + "odnvegbush2b", + "ap_carbush2_01", + "new_bushtest42", + "new_bushsm", + "mallbushs", + "odrv_bushes01", + "kb_planter+bush", + "ci_busht_06", + "beach_bush08s", + "beach_bush06s", + "new_bushtest", + "doontoon66_bushes01", + "beach_bush04", + "starbitbush", + "beach_bush02", + "lhavnew_bush", + "kb_planterbush2", + "ci_busht_11", + "odnvegbush2", + "kb_planter+bush2", + "mallbushdense", + "dk_rail06", + "cl_railing", + "docks10rail", + "cl_railingb", + "dk_rail05", + "shpfrnts03rail01", + "dk_rail02", + "nbeachbit03rails", + "ci_mans1rail", + "dk_rail07", + "dk_rail01", + "ci_mans2rail1", + "ci_mans1rail1", + "dk_rail04", + "shpfrnts03rail02", + "dk_rail03", + "", +}; + +static bool +MatchModelName(char *name, char **list) +{ + int i; + char *s; + for(i = 0; *list[i] != '\0'; i++) + if(strncmp(name, "LOD", 3) == 0){ + if(!CGeneral::faststricmp(name+3, list[i]+3)) + return true; + }else{ + if(!CGeneral::faststricmp(name, list[i])) + return true; + } + return false; +} +#endif + +void +SetModelInfoFlags(CSimpleModelInfo *mi, uint32 flags) +{ + mi->m_wetRoadReflection = !!(flags & 1); + mi->m_noFade = !!(flags & 2); + mi->m_drawLast = !!(flags & (4|8)); + mi->m_additive = !!(flags & 8); + mi->m_isSubway = !!(flags & 0x10); + mi->m_ignoreLight = !!(flags & 0x20); + mi->m_noZwrite = !!(flags & 0x40); + mi->m_noShadows = !!(flags & 0x80); + mi->m_ignoreDrawDist = !!(flags & 0x100); + mi->m_isCodeGlass = !!(flags & 0x200); + mi->m_isArtistGlass = !!(flags & 0x400); + +#ifdef RW_DC + if(MatchModelName(mi->GetModelName(), AlphaTestNames)) mi->m_isAlphaTest = true; +#endif +} + +int +CFileLoader::LoadObject(const char *line) +{ + int id, numObjs; + char model[24], txd[24]; + float dist[3]; + uint32 flags; + int damaged; + CSimpleModelInfo *mi; + + if(sscanf(line, "%d %s %s %d", &id, model, txd, &numObjs) != 4) + return 0; // game returns return value + + switch(numObjs){ + case 1: + sscanf(line, "%d %s %s %d %f %d", + &id, model, txd, &numObjs, &dist[0], &flags); + damaged = 0; + break; + case 2: + sscanf(line, "%d %s %s %d %f %f %d", + &id, model, txd, &numObjs, &dist[0], &dist[1], &flags); + damaged = dist[0] < dist[1] ? // Are distances increasing? + 0 : // Yes, no damage model + 1; // No, 1 is damaged + break; + case 3: + sscanf(line, "%d %s %s %d %f %f %f %d", + &id, model, txd, &numObjs, &dist[0], &dist[1], &dist[2], &flags); + damaged = dist[0] < dist[1] ? // Are distances increasing? + (dist[1] < dist[2] ? 0 : 2) : // Yes, only 2 can still be a damage model + 1; // No, 1 and 2 are damaged + break; + } + + mi = CModelInfo::AddSimpleModel(id); + mi->SetModelName(model); + mi->SetNumAtomics(numObjs); + mi->SetLodDistances(dist); + SetModelInfoFlags(mi, flags); + mi->m_firstDamaged = damaged; + mi->SetTexDictionary(txd); + MatchModelString(model, id); + + return id; +} + +int +CFileLoader::LoadTimeObject(const char *line) +{ + int id, numObjs; + char model[24], txd[24]; + float dist[3]; + uint32 flags; + int timeOn, timeOff; + int damaged; + CTimeModelInfo *mi, *other; + + if(sscanf(line, "%d %s %s %d", &id, model, txd, &numObjs) != 4) + return 0; // game returns return value + + switch(numObjs){ + case 1: + sscanf(line, "%d %s %s %d %f %d %d %d", + &id, model, txd, &numObjs, &dist[0], &flags, &timeOn, &timeOff); + damaged = 0; + break; + case 2: + sscanf(line, "%d %s %s %d %f %f %d %d %d", + &id, model, txd, &numObjs, &dist[0], &dist[1], &flags, &timeOn, &timeOff); + damaged = dist[0] < dist[1] ? // Are distances increasing? + 0 : // Yes, no damage model + 1; // No, 1 is damaged + break; + case 3: + sscanf(line, "%d %s %s %d %f %f %f %d %d %d", + &id, model, txd, &numObjs, &dist[0], &dist[1], &dist[2], &flags, &timeOn, &timeOff); + damaged = dist[0] < dist[1] ? // Are distances increasing? + (dist[1] < dist[2] ? 0 : 2) : // Yes, only 2 can still be a damage model + 1; // No, 1 and 2 are damaged + break; + } + + mi = CModelInfo::AddTimeModel(id); + mi->SetModelName(model); + mi->SetNumAtomics(numObjs); + mi->SetLodDistances(dist); + SetModelInfoFlags(mi, flags); + mi->m_firstDamaged = damaged; + mi->SetTimes(timeOn, timeOff); + mi->SetTexDictionary(txd); + other = mi->FindOtherTimeModel(); + if(other) + other->SetOtherTimeModel(id); + MatchModelString(model, id); + + return id; +} + +int +CFileLoader::LoadWeaponObject(const char *line) +{ + int id, numObjs; + char model[24], txd[24], animFile[16]; + float dist; + CWeaponModelInfo *mi; + + sscanf(line, "%d %s %s %s %d %f", &id, model, txd, animFile, &numObjs, &dist); + + mi = CModelInfo::AddWeaponModel(id); + mi->SetModelName(model); + mi->SetNumAtomics(1); + mi->m_lodDistances[0] = dist; + mi->SetTexDictionary(txd); + mi->SetAnimFile(animFile); + mi->SetColModel(&CTempColModels::ms_colModelWeapon); + MatchModelString(model, id); + return id; +} + +void +CFileLoader::LoadClumpObject(const char *line) +{ + int id; + char model[24], txd[24]; + CClumpModelInfo *mi; + + if(sscanf(line, "%d %s %s", &id, model, txd) == 3){ + mi = CModelInfo::AddClumpModel(id); + mi->SetModelName(model); + mi->SetTexDictionary(txd); + mi->SetColModel(&CTempColModels::ms_colModelBBox); + } +} + +void +CFileLoader::LoadVehicleObject(const char *line) +{ + int id; + char model[24], txd[24]; + char type[8], handlingId[16], gamename[32], animFile[16], vehclass[12]; + uint32 frequency, comprules; + int32 level, misc; + float wheelScale; + CVehicleModelInfo *mi; + char *p; + + sscanf(line, "%d %s %s %s %s %s %s %s %d %d %x %d %f", + &id, model, txd, + type, handlingId, gamename, animFile, vehclass, + &frequency, &level, &comprules, &misc, &wheelScale); + + mi = CModelInfo::AddVehicleModel(id); + mi->SetModelName(model); + mi->SetTexDictionary(txd); + mi->SetAnimFile(animFile); + for(p = gamename; *p; p++) + if(*p == '_') *p = ' '; + strcpy(mi->m_gameName, gamename); + mi->m_level = level; + mi->m_compRules = comprules; + + if(strcmp(type, "car") == 0){ + mi->m_wheelId = misc; + mi->m_wheelScale = wheelScale; + mi->m_vehicleType = VEHICLE_TYPE_CAR; + }else if(strcmp(type, "boat") == 0){ + mi->m_vehicleType = VEHICLE_TYPE_BOAT; + }else if(strcmp(type, "train") == 0){ + mi->m_vehicleType = VEHICLE_TYPE_TRAIN; + }else if(strcmp(type, "heli") == 0){ + mi->m_vehicleType = VEHICLE_TYPE_HELI; + }else if(strcmp(type, "plane") == 0){ + mi->m_planeLodId = misc; + mi->m_wheelScale = 1.0f; + mi->m_vehicleType = VEHICLE_TYPE_PLANE; + }else if(strcmp(type, "bike") == 0){ + mi->m_bikeSteerAngle = misc; + mi->m_wheelScale = wheelScale; + mi->m_vehicleType = VEHICLE_TYPE_BIKE; + }else + assert(0); + + mi->m_handlingId = mod_HandlingManager.GetHandlingId(handlingId); + + if(strcmp(vehclass, "normal") == 0) + mi->m_vehicleClass = CCarCtrl::NORMAL; + else if(strcmp(vehclass, "poorfamily") == 0) + mi->m_vehicleClass = CCarCtrl::POOR; + else if(strcmp(vehclass, "richfamily") == 0) + mi->m_vehicleClass = CCarCtrl::RICH; + else if(strcmp(vehclass, "executive") == 0) + mi->m_vehicleClass = CCarCtrl::EXEC; + else if(strcmp(vehclass, "worker") == 0) + mi->m_vehicleClass = CCarCtrl::WORKER; + else if(strcmp(vehclass, "big") == 0) + mi->m_vehicleClass = CCarCtrl::BIG; + else if(strcmp(vehclass, "taxi") == 0) + mi->m_vehicleClass = CCarCtrl::TAXI; + else if(strcmp(vehclass, "moped") == 0) + mi->m_vehicleClass = CCarCtrl::MOPED; + else if(strcmp(vehclass, "motorbike") == 0) + mi->m_vehicleClass = CCarCtrl::MOTORBIKE; + else if(strcmp(vehclass, "leisureboat") == 0) + mi->m_vehicleClass = CCarCtrl::LEISUREBOAT; + else if(strcmp(vehclass, "workerboat") == 0) + mi->m_vehicleClass = CCarCtrl::WORKERBOAT; + else if(strcmp(vehclass, "ignore") == 0) { + mi->m_vehicleClass = -1; + return; + } + CCarCtrl::AddToCarArray(id, mi->m_vehicleClass); + mi->m_frequency = frequency; +} + +void +CFileLoader::LoadPedObject(const char *line) +{ + int id; + char model[24], txd[24]; + char pedType[24], pedStats[24], animGroup[24], animFile[16]; + int carsCanDrive; + CPedModelInfo *mi; + int animGroupId; + int radio1, radio2; + + sscanf(line, "%d %s %s %s %s %s %x %s %d %d", + &id, model, txd, + pedType, pedStats, animGroup, &carsCanDrive, + animFile, &radio1, &radio2); + + mi = CModelInfo::AddPedModel(id); + mi->SetModelName(model); + mi->SetTexDictionary(txd); + mi->SetAnimFile(animFile); + mi->SetColModel(&CTempColModels::ms_colModelPed1); + mi->m_pedType = CPedType::FindPedType(pedType); + mi->m_pedStatType = CPedStats::GetPedStatType(pedStats); + for(animGroupId = 0; animGroupId < NUM_ANIM_ASSOC_GROUPS; animGroupId++) + if(strcmp(animGroup, CAnimManager::GetAnimGroupName((AssocGroupId)animGroupId)) == 0) + break; + assert(animGroupId < NUM_ANIM_ASSOC_GROUPS); + mi->m_animGroup = animGroupId; + mi->m_carsCanDrive = carsCanDrive; + mi->radio1 = radio1; + mi->radio2 = radio2; +} + +int +CFileLoader::LoadPathHeader(const char *line, int &type) +{ + int id; + char modelname[32]; + + sscanf(line, "%d %d %s", &type, &id, modelname); + return id; +} + +void +CFileLoader::LoadPedPathNode(const char *line, int id, int node) +{ + int type, next, cross, numLeft, numRight, speed, flags; + float x, y, z, width, spawnRate; + + if(sscanf(line, "%d %d %d %f %f %f %f %d %d %d %d %f", + &type, &next, &cross, &x, &y, &z, &width, &numLeft, &numRight, + &speed, &flags, &spawnRate) != 12) + spawnRate = 1.0f; + + if(id == -1) + ThePaths.StoreDetachedNodeInfoPed(node, type, next, x, y, z, + width, !!cross, !!(flags&1), !!(flags&4), spawnRate*15.0f); + else + ThePaths.StoreNodeInfoPed(id, node, type, next, x, y, z, + width, !!cross, spawnRate*15.0f); +} + +void +CFileLoader::LoadCarPathNode(const char *line, int id, int node, bool waterPath) +{ + int type, next, cross, numLeft, numRight, speed, flags; + float x, y, z, width, spawnRate; + + if(sscanf(line, "%d %d %d %f %f %f %f %d %d %d %d %f", + &type, &next, &cross, &x, &y, &z, &width, &numLeft, &numRight, + &speed, &flags, &spawnRate) != 12) + spawnRate = 1.0f; + + if(id == -1) + ThePaths.StoreDetachedNodeInfoCar(node, type, next, x, y, z, width, numLeft, numRight, + !!(flags&1), !!(flags&4), speed, !!(flags&2), waterPath, spawnRate * 15, false); + else + ThePaths.StoreNodeInfoCar(id, node, type, next, x, y, z, 0, numLeft, numRight, + !!(flags&1), !!(flags&4), speed, !!(flags&2), waterPath, spawnRate * 15); +} + + +void +CFileLoader::Load2dEffect(const char *line) +{ + int id, r, g, b, a, type, ptype; + float x, y, z; + char corona[32], shadow[32]; + int shadowIntens, lightType, roadReflection, flare, flags, probability; + CBaseModelInfo *mi; + C2dEffect *effect; + char *p; + + sscanf(line, "%d %f %f %f %d %d %d %d %d", &id, &x, &y, &z, &r, &g, &b, &a, &type); + + CTxdStore::PushCurrentTxd(); + CTxdStore::SetCurrentTxd(CTxdStore::FindTxdSlot("particle")); + + mi = CModelInfo::GetModelInfo(id); + effect = CModelInfo::Get2dEffectStore().Alloc(); + mi->Add2dEffect(effect); + effect->pos = CVector(x, y, z); + effect->col = CRGBA(r, g, b, a); + effect->type = type; + + switch(effect->type){ + case EFFECT_LIGHT: + while(*line++ != '"'); + p = corona; + while(*line != '"') *p++ = *line++; + *p = '\0'; + line++; + + while(*line++ != '"'); + p = shadow; + while(*line != '"') *p++ = *line++; + *p = '\0'; + line++; + + sscanf(line, "%f %f %f %f %d %d %d %d %d", + &effect->light.dist, + &effect->light.range, + &effect->light.size, + &effect->light.shadowSize, + &shadowIntens, &lightType, &roadReflection, &flare, &flags); + effect->light.corona = RwTextureRead(corona, nil); + effect->light.shadow = RwTextureRead(shadow, nil); + effect->light.shadowIntensity = shadowIntens; + effect->light.lightType = lightType; + effect->light.roadReflection = roadReflection; + effect->light.flareType = flare; + + if(flags & LIGHTFLAG_FOG_ALWAYS) + flags &= ~LIGHTFLAG_FOG_NORMAL; + effect->light.flags = flags; + break; + + case EFFECT_PARTICLE: + sscanf(line, "%d %f %f %f %d %d %d %d %d %d %f %f %f %f", + &id, &x, &y, &z, &r, &g, &b, &a, &type, + &effect->particle.particleType, + &effect->particle.dir.x, + &effect->particle.dir.y, + &effect->particle.dir.z, + &effect->particle.scale); + break; + + case EFFECT_ATTRACTOR: + sscanf(line, "%d %f %f %f %d %d %d %d %d %d %f %f %f %d", + &id, &x, &y, &z, &r, &g, &b, &a, &type, + &flags, + &effect->attractor.dir.x, + &effect->attractor.dir.y, + &effect->attractor.dir.z, + &probability); + effect->attractor.type = flags; +#ifdef FIX_BUGS + effect->attractor.probability = Clamp(probability, 0, 255); +#else + effect->attractor.probability = probability; +#endif + break; + case EFFECT_PED_ATTRACTOR: + sscanf(line, "%d %f %f %f %d %d %d %d %d %d %f %f %f %f %f %f", + &id, &x, &y, &z, &r, &g, &b, &a, &type, + &ptype, + &effect->pedattr.queueDir.x, + &effect->pedattr.queueDir.y, + &effect->pedattr.queueDir.z, + &effect->pedattr.useDir.x, + &effect->pedattr.useDir.y, + &effect->pedattr.useDir.z); + effect->pedattr.type = ptype; + break; + } + + CTxdStore::PopCurrentTxd(); +} + +void +CFileLoader::LoadScene(const char *filename) +{ + enum { + NONE, + INST, + ZONE, + CULL, + OCCL, + PICK, + PATH, + }; + char *line; + int fd; + int section; + int pathType, pathIndex; + + section = NONE; + pathIndex = -1; + debug("Creating objects from %s...\n", filename); + + fd = CFileMgr::OpenFile(filename, "rb"); + assert(fd > 0); + for(line = CFileLoader::LoadLine(fd); line; line = CFileLoader::LoadLine(fd)){ + if(*line == '\0' || *line == '#') + continue; + + if(section == NONE){ + if(isLine4(line, 'i','n','s','t')) section = INST; + else if(isLine4(line, 'z','o','n','e')) section = ZONE; + else if(isLine4(line, 'c','u','l','l')) section = CULL; + else if(isLine4(line, 'p','i','c','k')) section = PICK; + else if(isLine4(line, 'p','a','t','h')) section = PATH; + else if(isLine4(line, 'o','c','c','l')) section = OCCL; + }else if(isLine3(line, 'e','n','d')){ + section = NONE; + }else switch(section){ + case INST: + LoadObjectInstance(line); + break; + case ZONE: + LoadZone(line); + break; + case CULL: + LoadCullZone(line); + break; + case OCCL: + LoadOcclusionVolume(line); + break; + case PICK: + // unused + LoadPickup(line); + break; + case PATH: + if(pathIndex == -1){ + LoadPathHeader(line, pathType); + pathIndex = 0; + }else{ + if(pathType == 0) + LoadPedPathNode(line, -1, pathIndex); + else if (pathType == 1) + LoadCarPathNode(line, -1, pathIndex, false); + else if (pathType == 2) + LoadCarPathNode(line, -1, pathIndex, true); + pathIndex++; + if(pathIndex == 12) + pathIndex = -1; + } + break; + } + } + CFileMgr::CloseFile(fd); + + debug("Finished loading IPL\n"); +} + +void +CFileLoader::LoadObjectInstance(const char *line) +{ + int id; + char name[24]; + RwV3d trans, scale, axis; + float angle; + CSimpleModelInfo *mi; + RwMatrix *xform; + CEntity *entity; + float area; + + if(sscanf(line, "%d %s %f %f %f %f %f %f %f %f %f %f %f", + &id, name, &area, + &trans.x, &trans.y, &trans.z, + &scale.x, &scale.y, &scale.z, + &axis.x, &axis.y, &axis.z, &angle) != 13){ + if(sscanf(line, "%d %s %f %f %f %f %f %f %f %f %f %f", + &id, name, + &trans.x, &trans.y, &trans.z, + &scale.x, &scale.y, &scale.z, + &axis.x, &axis.y, &axis.z, &angle) != 12) + return; + area = 0; + } + + mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(id); + if(mi == nil) + return; + assert(mi->IsSimple()); + + if(!CStreaming::IsObjectInCdImage(id)) + debug("Not in cdimage %s\n", mi->GetModelName()); + + angle = -RADTODEG(2.0f * Acos(angle)); + xform = RwMatrixCreate(); + RwMatrixRotate(xform, &axis, angle, rwCOMBINEREPLACE); + RwMatrixTranslate(xform, &trans, rwCOMBINEPOSTCONCAT); + + if(mi->GetObjectID() == -1){ + if(ThePaths.IsPathObject(id)){ + entity = new CTreadable; + ThePaths.RegisterMapObject((CTreadable*)entity); + }else + entity = new CBuilding; + entity->SetModelIndexNoCreate(id); + entity->GetMatrix() = CMatrix(xform); + entity->m_level = CTheZones::GetLevelFromPosition(&entity->GetPosition()); + entity->m_area = area; + if(mi->IsBuilding()){ + if(mi->m_isBigBuilding) + entity->SetupBigBuilding(); + if(mi->m_isSubway) + entity->bIsSubway = true; + } + if(mi->GetLargestLodDistance() < 2.0f) + entity->bIsVisible = false; + CWorld::Add(entity); + + CColModel *col = entity->GetColModel(); + if(col->numSpheres || col->numBoxes || col->numTriangles){ + if(col->level != 0) + CColStore::GetBoundingBox(col->level).ContainRect(entity->GetBoundRect()); + }else + entity->bUsesCollision = false; + + if(entity->GetPosition().z + col->boundingBox.min.z < 6.0f) + entity->bUnderwater = true; + }else{ + entity = new CDummyObject; + entity->SetModelIndexNoCreate(id); + entity->GetMatrix() = CMatrix(xform); + CWorld::Add(entity); + if(IsGlass(entity->GetModelIndex()) && !mi->m_isArtistGlass) + entity->bIsVisible = false; + entity->m_level = CTheZones::GetLevelFromPosition(&entity->GetPosition()); + entity->m_area = area; + } + + RwMatrixDestroy(xform); +} + +void +CFileLoader::LoadZone(const char *line) +{ + char name[24]; + int type, level; + float minx, miny, minz; + float maxx, maxy, maxz; + + if(sscanf(line, "%s %d %f %f %f %f %f %f %d", name, &type, &minx, &miny, &minz, &maxx, &maxy, &maxz, &level) == 9) + CTheZones::CreateZone(name, (eZoneType)type, minx, miny, minz, maxx, maxy, maxz, (eLevelName)level); +} + +void +CFileLoader::LoadCullZone(const char *line) +{ + CVector pos; + float minx, miny, minz; + float maxx, maxy, maxz; + int flags; + int wantedLevelDrop = 0; + + sscanf(line, "%f %f %f %f %f %f %f %f %f %d %d", + &pos.x, &pos.y, &pos.z, + &minx, &miny, &minz, + &maxx, &maxy, &maxz, + &flags, &wantedLevelDrop); + CCullZones::AddCullZone(pos, minx, maxx, miny, maxy, minz, maxz, flags, wantedLevelDrop); +} + +// unused +void +CFileLoader::LoadPickup(const char *line) +{ + int id; + float x, y, z; + + sscanf(line, "%d %f %f %f", &id, &x, &y, &z); +} + +void +CFileLoader::LoadOcclusionVolume(const char *line) +{ + float x, y, z; + float width, length, height; + float angle; + + sscanf(line, "%f %f %f %f %f %f %f", + &x, &y, &z, + &width, &length, &height, + &angle); + COcclusion::AddOne(x, y, z + height/2.0f, width, length, height, angle); +} + + +// unused +void +CFileLoader::ReloadPaths(const char *filename) +{ + enum { + NONE, + PATH, + }; + char *line; + int section = NONE; + int id, pathType, pathIndex = -1; + debug("Reloading paths from %s...\n", filename); + + int fd = CFileMgr::OpenFile(filename, "r"); + assert(fd > 0); + for (line = CFileLoader::LoadLine(fd); line; line = CFileLoader::LoadLine(fd)) { + if (*line == '\0' || *line == '#') + continue; + + if (section == NONE) { + if (isLine4(line, 'p','a','t','h')) { + section = PATH; + ThePaths.AllocatePathFindInfoMem(4500); + } + } else if (isLine3(line, 'e','n','d')) { + section = NONE; + } else { + switch (section) { + case PATH: + if (pathIndex == -1) { + id = LoadPathHeader(line, pathType); + pathIndex = 0; + } else { + if(pathType == 0) + LoadPedPathNode(line, id, pathIndex); + else if (pathType == 1) + LoadCarPathNode(line, id, pathIndex, false); + else if (pathType == 2) + LoadCarPathNode(line, id, pathIndex, true); + pathIndex++; + if (pathIndex == 12) + pathIndex = -1; + } + break; + default: + break; + } + } + } + CFileMgr::CloseFile(fd); +} + +void +CFileLoader::ReloadObjectTypes(const char *filename) +{ + enum { + NONE, + OBJS, + TOBJ, + TWODFX + }; + char *line; + int section = NONE; + CModelInfo::ReInit2dEffects(); + debug("Reloading object types from %s...\n", filename); + + CFileMgr::ChangeDir("\\DATA\\MAPS\\"); + int fd = CFileMgr::OpenFile(filename, "r"); + assert(fd > 0); + CFileMgr::ChangeDir("\\"); + for (line = CFileLoader::LoadLine(fd); line; line = CFileLoader::LoadLine(fd)) { + if (*line == '\0' || *line == '#') + continue; + + if (section == NONE) { + if (isLine4(line, 'o','b','j','s')) section = OBJS; + else if (isLine4(line, 't','o','b','j')) section = TOBJ; + else if (isLine4(line, '2','d','f','x')) section = TWODFX; + } else if (isLine3(line, 'e','n','d')) { + section = NONE; + } else { + switch (section) { + case OBJS: + case TOBJ: + ReloadObject(line); + break; + case TWODFX: + Load2dEffect(line); + break; + default: + break; + } + } + } + CFileMgr::CloseFile(fd); +} + +void +CFileLoader::ReloadObject(const char *line) +{ + int id, numObjs; + char model[24], txd[24]; + float dist[3]; + uint32 flags; + CSimpleModelInfo *mi; + + if(sscanf(line, "%d %s %s %d", &id, model, txd, &numObjs) != 4) + return; + + switch(numObjs){ + case 1: + sscanf(line, "%d %s %s %d %f %d", + &id, model, txd, &numObjs, &dist[0], &flags); + break; + case 2: + sscanf(line, "%d %s %s %d %f %f %d", + &id, model, txd, &numObjs, &dist[0], &dist[1], &flags); + break; + case 3: + sscanf(line, "%d %s %s %d %f %f %f %d", + &id, model, txd, &numObjs, &dist[0], &dist[1], &dist[2], &flags); + break; + } + + mi = (CSimpleModelInfo*) CModelInfo::GetModelInfo(id); + if ( +#ifdef FIX_BUGS + mi && +#endif + mi->GetModelType() == MITYPE_SIMPLE && !strcmp(mi->GetModelName(), model) && mi->m_numAtomics == numObjs) { + mi->SetLodDistances(dist); + SetModelInfoFlags(mi, flags); + } else { + printf("Can't reload %s\n", model); + } +} + +// unused mobile function - crashes +void +CFileLoader::ReLoadScene(const char *filename) +{ + char *line; + CFileMgr::ChangeDir("\\DATA\\"); + int fd = CFileMgr::OpenFile(filename, "r"); + assert(fd > 0); + CFileMgr::ChangeDir("\\"); + + for (line = CFileLoader::LoadLine(fd); line; line = CFileLoader::LoadLine(fd)) { + if (*line == '#') + continue; + + if (strncmp(line, "EXIT", 4) == 0) + break; + + if (strncmp(line, "IDE", 3) == 0) { + LoadObjectTypes(line + 4); + } + } + CFileMgr::CloseFile(fd); +} diff --git a/src/miami/core/FileLoader.h b/src/miami/core/FileLoader.h new file mode 100644 index 00000000..077e7bdd --- /dev/null +++ b/src/miami/core/FileLoader.h @@ -0,0 +1,48 @@ +#pragma once + +class CFileLoader +{ + static char ms_line[256]; +public: + static void LoadLevel(const char *filename); + static char *LoadLine(int fd); + static RwTexDictionary *LoadTexDictionary(const char *filename); + static void LoadCollisionFile(const char *filename, uint8 colSlot); + static bool LoadCollisionFileFirstTime(uint8 *buffer, uint32 size, uint8 colSlot); + static bool LoadCollisionFile(uint8 *buffer, uint32 size, uint8 colSlot); + static void LoadCollisionModel(uint8 *buf, struct CColModel &model, char *name); + static void LoadModelFile(const char *filename); + static RpAtomic *FindRelatedModelInfoCB(RpAtomic *atomic, void *data); + static void LoadClumpFile(const char *filename); + static bool LoadClumpFile(RwStream *stream, uint32 id); + static bool StartLoadClumpFile(RwStream *stream, uint32 id); + static bool FinishLoadClumpFile(RwStream *stream, uint32 id); + static bool LoadAtomicFile(RwStream *stream, uint32 id); + static RpAtomic *SetRelatedModelInfoCB(RpAtomic *atomic, void *data); + static RpClump *LoadAtomicFile2Return(const char *filename); + static void AddTexDictionaries(RwTexDictionary *dst, RwTexDictionary *src); + + static void LoadObjectTypes(const char *filename); + static int LoadObject(const char *line); + static int LoadTimeObject(const char *line); + static int LoadWeaponObject(const char *line); + static void LoadClumpObject(const char *line); + static void LoadVehicleObject(const char *line); + static void LoadPedObject(const char *line); + static int LoadPathHeader(const char *line, int &type); + static void LoadPedPathNode(const char *line, int id, int node); + static void LoadCarPathNode(const char *line, int id, int node, bool waterPath); + static void Load2dEffect(const char *line); + + static void LoadScene(const char *filename); + static void LoadObjectInstance(const char *line); + static void LoadZone(const char *line); + static void LoadCullZone(const char *line); + static void LoadPickup(const char *line); + static void LoadOcclusionVolume(const char *line); + + static void ReloadPaths(const char *filename); + static void ReloadObjectTypes(const char *filename); + static void ReloadObject(const char *line); + static void ReLoadScene(const char *filename); // unused mobile function +}; diff --git a/src/miami/core/FileMgr.cpp b/src/miami/core/FileMgr.cpp new file mode 100644 index 00000000..32aa4041 --- /dev/null +++ b/src/miami/core/FileMgr.cpp @@ -0,0 +1,313 @@ +#define _CRT_SECURE_NO_WARNINGS +#include +#ifdef _WIN32 +#include +#endif +#include "common.h" +#include "crossplatform.h" + +#include "FileMgr.h" + +const char *_psGetUserFilesFolder(); + +/* + * Windows FILE is BROKEN for GTA. + * + * We need to support mapping between LF and CRLF for text files + * but we do NOT want to end the file at the first sight of a SUB character. + * So here is a simple implementation of a FILE interface that works like GTA expects. + */ + +struct myFILE +{ + bool isText; + FILE *file; +}; + +#define NUMFILES 20 +static myFILE myfiles[NUMFILES]; + + +#if !defined(_WIN32) +#include +#include +#include +#define _getcwd getcwd + +// Case-insensitivity on linux (from https://github.com/OneSadCookie/fcaseopen) +void mychdir(char const *path) +{ + char* r = casepath(path, false); + if (r) { + chdir(r); + free(r); + } else { + errno = ENOENT; + } +} +#else +#define mychdir chdir +#endif + +/* Force file to open as binary but remember if it was text mode */ +static int +myfopen(const char *filename, const char *mode) +{ + int fd; + char realmode[10], *p; + + for(fd = 1; fd < NUMFILES; fd++) + if(myfiles[fd].file == nil) + goto found; + return 0; // no free fd +found: + myfiles[fd].isText = strchr(mode, 'b') == nil; + p = realmode; + while(*mode) + if(*mode != 't' && *mode != 'b') + *p++ = *mode++; + else + mode++; + *p++ = 'b'; + *p = '\0'; + + myfiles[fd].file = fcaseopen(filename, realmode); + if(myfiles[fd].file == nil) + return 0; + return fd; +} + +static int +myfclose(int fd) +{ + int ret; + assert(fd < NUMFILES); + if(myfiles[fd].file){ + ret = fclose(myfiles[fd].file); + myfiles[fd].file = nil; + return ret; + } + return EOF; +} + +static int +myfgetc(int fd) +{ + int c; + c = fgetc(myfiles[fd].file); + if(myfiles[fd].isText && c == 015){ + /* translate CRLF to LF */ + c = fgetc(myfiles[fd].file); + if(c == 012) + return c; + ungetc(c, myfiles[fd].file); + return 015; + } + return c; +} + +static int +myfputc(int c, int fd) +{ + /* translate LF to CRLF */ + if(myfiles[fd].isText && c == 012) + fputc(015, myfiles[fd].file); + return fputc(c, myfiles[fd].file); +} + +static char* +myfgets(char *buf, int len, int fd) +{ + int c; + char *p; + + p = buf; + len--; // NUL byte + while(len--){ + c = myfgetc(fd); + if(c == EOF){ + if(p == buf) + return nil; + break; + } + *p++ = c; + if(c == '\n') + break; + } + *p = '\0'; + return buf; +} + +static size_t +myfread(void *buf, size_t elt, size_t n, int fd) +{ + if(myfiles[fd].isText){ + unsigned char *p; + size_t i; + int c; + + n *= elt; + p = (unsigned char*)buf; + for(i = 0; i < n; i++){ + c = myfgetc(fd); + if(c == EOF) + break; + *p++ = (unsigned char)c; + } + return i / elt; + } + return fread(buf, elt, n, myfiles[fd].file); +} + +static size_t +myfwrite(void *buf, size_t elt, size_t n, int fd) +{ + if(myfiles[fd].isText){ + unsigned char *p; + size_t i; + int c; + + n *= elt; + p = (unsigned char*)buf; + for(i = 0; i < n; i++){ + c = *p++; + myfputc(c, fd); + if(feof(myfiles[fd].file)) // is this right? + break; + } + return i / elt; + } + return fwrite(buf, elt, n, myfiles[fd].file); +} + +static int +myfseek(int fd, long offset, int whence) +{ + return fseek(myfiles[fd].file, offset, whence); +} + +static int +myfeof(int fd) +{ + return feof(myfiles[fd].file); +// return ferror(myfiles[fd].file); +} + + +char CFileMgr::ms_rootDirName[128] = {'\0'}; +char CFileMgr::ms_dirName[128]; + +void +CFileMgr::Initialise(void) +{ + _getcwd(ms_rootDirName, 128); + strcat(ms_rootDirName, "\\"); +} + +void +CFileMgr::ChangeDir(const char *dir) +{ + if(*dir == '\\'){ + strcpy(ms_dirName, ms_rootDirName); + dir++; + } + if(*dir != '\0'){ + strcat(ms_dirName, dir); + // BUG in the game it seems, it's off by one + if(dir[strlen(dir)-1] != '\\') + strcat(ms_dirName, "\\"); + } + mychdir(ms_dirName); +} + +void +CFileMgr::SetDir(const char *dir) +{ + strcpy(ms_dirName, ms_rootDirName); + if(*dir != '\0'){ + strcat(ms_dirName, dir); + // BUG in the game it seems, it's off by one + if(dir[strlen(dir)-1] != '\\') + strcat(ms_dirName, "\\"); + } + mychdir(ms_dirName); +} + +void +CFileMgr::SetDirMyDocuments(void) +{ + SetDir(""); // better start at the root if user directory is relative + mychdir(_psGetUserFilesFolder()); +} + +ssize_t +CFileMgr::LoadFile(const char *file, uint8 *buf, int maxlen, const char *mode) +{ + int fd; + ssize_t n, len; + + fd = myfopen(file, mode); + if(fd == 0) + return -1; + len = 0; + do{ + n = myfread(buf + len, 1, 0x4000, fd); +#ifndef FIX_BUGS + if (n < 0) + return -1; +#endif + len += n; + assert(len < maxlen); + }while(n == 0x4000); + buf[len] = 0; + myfclose(fd); + return len; +} + +int +CFileMgr::OpenFile(const char *file, const char *mode) +{ + return myfopen(file, mode); +} + +int +CFileMgr::OpenFileForWriting(const char *file) +{ + return OpenFile(file, "wb"); +} + +size_t +CFileMgr::Read(int fd, const char *buf, ssize_t len) +{ + return myfread((void*)buf, 1, len, fd); +} + +size_t +CFileMgr::Write(int fd, const char *buf, ssize_t len) +{ + return myfwrite((void*)buf, 1, len, fd); +} + +bool +CFileMgr::Seek(int fd, int offset, int whence) +{ + return !!myfseek(fd, offset, whence); +} + +bool +CFileMgr::ReadLine(int fd, char *buf, int len) +{ + return myfgets(buf, len, fd) != nil; +} + +int +CFileMgr::CloseFile(int fd) +{ + return myfclose(fd); +} + +int +CFileMgr::GetErrorReadWrite(int fd) +{ + return myfeof(fd); +} diff --git a/src/miami/core/FileMgr.h b/src/miami/core/FileMgr.h new file mode 100644 index 00000000..f70451b7 --- /dev/null +++ b/src/miami/core/FileMgr.h @@ -0,0 +1,23 @@ +#pragma once + +class CFileMgr +{ + static char ms_rootDirName[128]; + static char ms_dirName[128]; +public: + static void Initialise(void); + static void ChangeDir(const char *dir); + static void SetDir(const char *dir); + static void SetDirMyDocuments(void); + static ssize_t LoadFile(const char *file, uint8 *buf, int maxlen, const char *mode); + static int OpenFile(const char *file, const char *mode); + static int OpenFile(const char *file) { return OpenFile(file, "rb"); } + static int OpenFileForWriting(const char *file); + static size_t Read(int fd, const char *buf, ssize_t len); + static size_t Write(int fd, const char *buf, ssize_t len); + static bool Seek(int fd, int offset, int whence); + static bool ReadLine(int fd, char *buf, int len); + static int CloseFile(int fd); + static int GetErrorReadWrite(int fd); + static char *GetRootDirName() { return ms_rootDirName; } +}; diff --git a/src/miami/core/Fire.cpp b/src/miami/core/Fire.cpp new file mode 100644 index 00000000..57315b15 --- /dev/null +++ b/src/miami/core/Fire.cpp @@ -0,0 +1,500 @@ +#include "common.h" + +#include "Vector.h" +#include "PlayerPed.h" +#include "Entity.h" +#include "PointLights.h" +#include "Particle.h" +#include "Timer.h" +#include "Vehicle.h" +#include "Shadows.h" +#include "Automobile.h" +#include "World.h" +#include "General.h" +#include "EventList.h" +#include "DamageManager.h" +#include "Ped.h" +#include "Fire.h" +#include "GameLogic.h" +#include "CarAI.h" + +CFireManager gFireManager; + +CFire::CFire() +{ + m_bIsOngoing = false; + m_bIsScriptFire = false; + m_bPropagationFlag = true; + m_bAudioSet = true; + m_vecPos = CVector(0.0f, 0.0f, 0.0f); + m_nExtinguishTime = 0; + m_nStartTime = 0; + m_pEntity = nil; + m_pSource = nil; + m_fStrength = 0.8f; + m_fWaterExtinguishCountdown = 1.0f; + m_bExtinguishedWithWater = false; +} + +CFire::~CFire() {} + +void +CFire::ProcessFire(void) +{ + float fDamagePlayer; + float fDamagePeds; + float fDamageVehicle; + int16 nRandNumber; + float fGreen; + float fRed; + CVector lightpos; + CVector firePos; + CPed *ped = (CPed *)m_pEntity; + CVehicle *veh = (CVehicle*)m_pEntity; + + m_fWaterExtinguishCountdown = Min(1.0f, 0.002f * CTimer::GetTimeStep() + m_fWaterExtinguishCountdown); + + if (m_pEntity) { + m_vecPos = m_pEntity->GetPosition(); + + if (((CPed *)m_pEntity)->IsPed()) { + if (ped->m_pFire != this) { + Extinguish(); + return; + } +#if defined GTAVC_JP_PATCH && !defined FIX_BUGS + if (m_pEntity == CGameLogic::pShortCutTaxi && CGameLogic::ShortCutState == CGameLogic::SHORTCUT_TRANSITION) { + Extinguish(); + return; + } +#endif + if (ped->m_nMoveState != PEDMOVE_RUN) + m_vecPos.z -= 1.0f; + if (ped->bInVehicle && ped->m_pMyVehicle) { + if (ped->m_pMyVehicle->IsCar()) + ped->m_pMyVehicle->m_fHealth = 75.0f; + } else if (m_pEntity == (CPed *)FindPlayerPed()) { + fDamagePlayer = 1.2f * CTimer::GetTimeStep(); + + ((CPlayerPed *)m_pEntity)->InflictDamage( + (CPlayerPed *)m_pSource, WEAPONTYPE_FLAMETHROWER, + fDamagePlayer, PEDPIECE_TORSO, 0); + } else { + fDamagePeds = 1.2f * CTimer::GetTimeStep(); + + if (((CPlayerPed *)m_pEntity)->InflictDamage( + (CPlayerPed *)m_pSource, WEAPONTYPE_FLAMETHROWER, + fDamagePeds, PEDPIECE_TORSO, 0)) { + m_pEntity->bRenderScorched = true; + } + } + } else if (m_pEntity->IsVehicle()) { + if (veh->m_pCarFire != this) { + Extinguish(); + return; + } +#ifdef FIX_BUGS + if (m_pEntity == CGameLogic::pShortCutTaxi && CGameLogic::ShortCutState == CGameLogic::SHORTCUT_TRANSITION) { + Extinguish(); + return; + } +#endif + if (!m_bIsScriptFire) { + fDamageVehicle = 1.2f * CTimer::GetTimeStep(); + veh->InflictDamage((CVehicle *)m_pSource, WEAPONTYPE_FLAMETHROWER, fDamageVehicle); + } + } + } + if (!FindPlayerVehicle() && +#ifdef FIX_BUGS + FindPlayerPed() && +#endif + !FindPlayerPed()->m_pFire && !(FindPlayerPed()->bFireProof) + && ((FindPlayerPed()->GetPosition() - m_vecPos).MagnitudeSqr() < 2.0f)) { + FindPlayerPed()->DoStuffToGoOnFire(); + gFireManager.StartFire(FindPlayerPed(), m_pSource, 0.8f, 1); + } + if (CTimer::GetTimeInMilliseconds() > m_nNextTimeToAddFlames) { + m_nNextTimeToAddFlames = CTimer::GetTimeInMilliseconds() + (m_fWaterExtinguishCountdown < 0.3f ? 400 : (m_fWaterExtinguishCountdown < 0.7f ? 200 : 80)); + firePos = m_vecPos; + + if (veh && veh->IsVehicle() && veh->IsCar()) { + CVehicleModelInfo *mi = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(veh->GetModelIndex())); + CVector ModelInfo = mi->m_positions[CAR_POS_HEADLIGHTS]; + ModelInfo = m_pEntity->GetMatrix() * ModelInfo; + + firePos.x = ModelInfo.x; + firePos.y = ModelInfo.y; + firePos.z = ModelInfo.z + 0.15f; + } + + CParticle::AddParticle(PARTICLE_CARFLAME, firePos, + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.0125f, 0.1f) * m_fStrength), + 0, m_fStrength, 0, 0, 0, 0); + + CGeneral::GetRandomNumber(); CGeneral::GetRandomNumber(); CGeneral::GetRandomNumber(); /* unsure why these three rands are called */ + + CParticle::AddParticle(PARTICLE_CARFLAME_SMOKE, firePos, + CVector(0.0f, 0.0f, 0.0f), 0, 0.0f, 0, 0, 0, 0); + } + if (CTimer::GetTimeInMilliseconds() < m_nExtinguishTime || m_bIsScriptFire) { + if (CTimer::GetTimeInMilliseconds() > m_nStartTime) + m_nStartTime = CTimer::GetTimeInMilliseconds() + 400; + + nRandNumber = CGeneral::GetRandomNumber() & 127; + lightpos.x = m_vecPos.x; + lightpos.y = m_vecPos.y; + lightpos.z = m_vecPos.z + 5.0f; + + if (!m_pEntity) { + CShadows::StoreStaticShadow((uintptr)this, SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &lightpos, 7.0f, 0.0f, 0.0f, -7.0f, 0, nRandNumber / 2, + nRandNumber / 2, 0, 10.0f, 1.0f, 40.0f, 0, 0.0f); + } + fGreen = nRandNumber / 128.f; + fRed = nRandNumber / 128.f; + + CPointLights::AddLight(CPointLights::LIGHT_POINT, m_vecPos, CVector(0.0f, 0.0f, 0.0f), 12.0f, fRed, fGreen, 0.0f, 0, 0); + } else { + Extinguish(); + } +} + +void +CFire::ReportThisFire(void) +{ + gFireManager.m_nTotalFires++; + CEventList::RegisterEvent(EVENT_FIRE, m_vecPos, 1000); +} + +void +CFire::Extinguish(void) +{ + if (m_bIsOngoing) { + if (!m_bIsScriptFire) + gFireManager.m_nTotalFires--; + + m_nExtinguishTime = 0; + m_bIsOngoing = false; + m_bExtinguishedWithWater = false; + + if (m_pEntity) { + if (m_pEntity->IsPed()) { + CPed *ped = (CPed*)m_pEntity; + if (ped->CanSetPedState()) { + if (ped->m_nPedState != PED_DRIVING && ped->m_nPedState != PED_FALL) { + if (ped->IsPlayer()) { + ped->SetIdle(); + } else { + ped->m_nLastPedState = PED_NONE; + ped->SetWanderPath(0); + ped->SetWaitState(WAITSTATE_FINISH_FLEE, 0); + } + } + } + ped->m_pFire = nil; + } else if (m_pEntity->IsVehicle()) { + ((CVehicle *)m_pEntity)->m_pCarFire = nil; + } + m_pEntity = nil; + } + } +} + +void +CFireManager::StartFire(CVector pos, float size, uint8 propagation) +{ + CFire *fire = GetNextFreeFire(); + + if (fire) { + fire->m_bIsOngoing = true; + fire->m_bIsScriptFire = false; + fire->m_bPropagationFlag = propagation; + fire->m_bAudioSet = true; + fire->m_vecPos = pos; + fire->m_nExtinguishTime = CTimer::GetTimeInMilliseconds() + 10000; + fire->m_nStartTime = CTimer::GetTimeInMilliseconds() + 400; + fire->m_pEntity = nil; + fire->m_pSource = nil; + fire->m_nNextTimeToAddFlames = 0; + fire->ReportThisFire(); + fire->m_fStrength = size; + fire->m_bExtinguishedWithWater = false; + } +} + +CFire * +CFireManager::StartFire(CEntity *entityOnFire, CEntity *fleeFrom, float strength, uint8 propagation) +{ + CPed *ped = (CPed *)entityOnFire; + CVehicle *veh = (CVehicle *)entityOnFire; + + if (entityOnFire->IsPed()) { + if (ped->m_pFire) + return nil; + if (!ped->IsPedInControl()) + return nil; + } else if (entityOnFire->IsVehicle()) { + if (veh->m_pCarFire) + return nil; + if (veh->IsCar() && ((CAutomobile *)veh)->Damage.GetEngineStatus() >= 225) + return nil; + } + CFire *fire = GetNextFreeFire(); + + if (fire) { + if (entityOnFire->IsPed()) { + ped->m_pFire = fire; + if (ped != FindPlayerPed()) { + if (fleeFrom) { + ped->SetFlee(fleeFrom, 10000); + } else { + CVector2D pos = entityOnFire->GetPosition(); + ped->SetFlee(pos, 10000); + ped->m_fleeFrom = nil; + } + ped->m_fleeTimer = CTimer::GetTimeInMilliseconds() + 10000; + ped->bDrawLast = false; + ped->SetMoveState(PEDMOVE_SPRINT); + ped->SetMoveAnim(); + ped->SetPedState(PED_ON_FIRE); + } + if (fleeFrom) { + if (ped->m_nPedType == PEDTYPE_COP) { + CEventList::RegisterEvent(EVENT_COP_SET_ON_FIRE, EVENT_ENTITY_PED, + entityOnFire, (CPed *)fleeFrom, 10000); + } else { + CEventList::RegisterEvent(EVENT_PED_SET_ON_FIRE, EVENT_ENTITY_PED, + entityOnFire, (CPed *)fleeFrom, 10000); + } + } + } else { + if (entityOnFire->IsVehicle()) { + veh->m_pCarFire = fire; + if (CModelInfo::IsBikeModel(veh->GetModelIndex()) || CModelInfo::IsCarModel(veh->GetModelIndex())) + CCarAI::TellOccupantsToFleeCar(veh); + + if (fleeFrom) { + CEventList::RegisterEvent(EVENT_CAR_SET_ON_FIRE, EVENT_ENTITY_VEHICLE, + entityOnFire, (CPed *)fleeFrom, 10000); + } + } + } + + fire->m_bIsOngoing = true; + fire->m_bExtinguishedWithWater = false; + fire->m_bIsScriptFire = false; + fire->m_vecPos = entityOnFire->GetPosition(); + + if (entityOnFire && entityOnFire->IsPed() && ped->IsPlayer()) { + fire->m_nExtinguishTime = CTimer::GetTimeInMilliseconds() + 3333; + } else if (entityOnFire->IsVehicle()) { + fire->m_nExtinguishTime = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(4000, 5000); + } else { + fire->m_nExtinguishTime = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(10000, 11000); + } + fire->m_nStartTime = CTimer::GetTimeInMilliseconds() + 400; + fire->m_pEntity = entityOnFire; + + entityOnFire->RegisterReference(&fire->m_pEntity); + fire->m_pSource = fleeFrom; + + if (fleeFrom) + fleeFrom->RegisterReference(&fire->m_pSource); + fire->ReportThisFire(); + fire->m_nNextTimeToAddFlames = 0; + fire->m_fStrength = strength; + fire->m_bPropagationFlag = propagation; + fire->m_bAudioSet = true; + } + return fire; +} + +void +CFireManager::Update(void) +{ + for (int i = 0; i < NUM_FIRES; i++) { + if (m_aFires[i].m_bIsOngoing) + m_aFires[i].ProcessFire(); + } +} + +CFire* CFireManager::FindNearestFire(CVector vecPos, float *pDistance) +{ + int fireId = -1; + float minDistance = 999999; + for (int j = 0; j < NUM_FIRES; j++) { + if (!m_aFires[j].m_bIsOngoing) + continue; + if (m_aFires[j].m_bIsScriptFire) + continue; + float distance = (m_aFires[j].m_vecPos - vecPos).Magnitude2D(); + if (distance < minDistance) { + minDistance = distance; + fireId = j; + } + } + *pDistance = minDistance; + if (fireId != -1) + return &m_aFires[fireId]; + + return nil; +} + +CFire * +CFireManager::FindFurthestFire_NeverMindFireMen(CVector coords, float minRange, float maxRange) +{ + int furthestFire = -1; + float lastFireDist = 0.0f; + float fireDist; + + for (int i = 0; i < NUM_FIRES; i++) { + if (m_aFires[i].m_bIsOngoing && !m_aFires[i].m_bIsScriptFire) { + fireDist = (m_aFires[i].m_vecPos - coords).Magnitude2D(); + if (fireDist > minRange && fireDist < maxRange && fireDist > lastFireDist) { + lastFireDist = fireDist; + furthestFire = i; + } + } + } + if (furthestFire == -1) + return nil; + else + return &m_aFires[furthestFire]; +} + +CFire * +CFireManager::GetNextFreeFire(void) +{ + for (int i = 0; i < NUM_FIRES; i++) { + if (!m_aFires[i].m_bIsOngoing && !m_aFires[i].m_bIsScriptFire) + return &m_aFires[i]; + } + return nil; +} + +uint32 +CFireManager::GetTotalActiveFires(void) const +{ + return m_nTotalFires; +} + +void +CFireManager::ExtinguishPoint(CVector point, float range) +{ + for (int i = 0; i < NUM_FIRES; i++) { + if (m_aFires[i].m_bIsOngoing) { + if ((point - m_aFires[i].m_vecPos).MagnitudeSqr() < sq(range)) + m_aFires[i].Extinguish(); + } + } +} + +bool +CFireManager::ExtinguishPointWithWater(CVector point, float range) +{ + int i; + for (i = 0; i < NUM_FIRES;) { + if (m_aFires[i].m_bIsOngoing && (point - m_aFires[i].m_vecPos).MagnitudeSqr() < sq(range)) { + break; + } + if (++i >= NUM_FIRES) + return false; + } + + CFire *fireToExtinguish = &m_aFires[i]; + fireToExtinguish->m_fWaterExtinguishCountdown -= 0.012f * CTimer::GetTimeStep(); + CVector steamPos = fireToExtinguish->m_vecPos + + CVector((CGeneral::GetRandomNumber() - 128) * 3.1f / 200.f, + (CGeneral::GetRandomNumber() - 128) * 3.1f / 200.f, + CGeneral::GetRandomNumber() / 200.f); + + CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, steamPos, CVector(0.f, 0.f, 0.2f), nil, 0.5f); + CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, steamPos, CVector(0.f, 0.f, 0.1f), nil, 0.8f); + fireToExtinguish->m_bExtinguishedWithWater = true; + if (fireToExtinguish->m_fWaterExtinguishCountdown < 0.0f ) + fireToExtinguish->Extinguish(); + + return true; +} + +int32 +CFireManager::StartScriptFire(const CVector &pos, CEntity *target, float strength, uint8 propagation) +{ + CFire *fire; + CPed *ped = (CPed *)target; + CVehicle *veh = (CVehicle *)target; + + if (target) { + if (target->IsPed()) { + if (ped->m_pFire) + ped->m_pFire->Extinguish(); + } else if (target->IsVehicle()) { + if (veh->m_pCarFire) + veh->m_pCarFire->Extinguish(); + if (veh->IsCar() && ((CAutomobile *)veh)->Damage.GetEngineStatus() >= 225) { + ((CAutomobile *)veh)->Damage.SetEngineStatus(215); + } + } + } + + fire = GetNextFreeFire(); + fire->m_bIsOngoing = true; + fire->m_bIsScriptFire = true; + fire->m_bPropagationFlag = propagation; + fire->m_bAudioSet = true; + fire->m_vecPos = pos; + fire->m_nStartTime = CTimer::GetTimeInMilliseconds() + 400; + fire->m_pEntity = target; + fire->m_bExtinguishedWithWater = false; + + if (target) + target->RegisterReference(&fire->m_pEntity); + fire->m_pSource = nil; + fire->m_nNextTimeToAddFlames = 0; + fire->m_fStrength = strength; + fire->m_fWaterExtinguishCountdown = 1.0f; + + if (target) { + if (target->IsPed()) { + ped->m_pFire = fire; + if (target != FindPlayerPed()) { + CVector2D pos = target->GetPosition(); + ped->SetFlee(pos, 10000); + ped->SetMoveAnim(); + ped->SetPedState(PED_ON_FIRE); + } + } else if (target->IsVehicle()) { + veh->m_pCarFire = fire; + } + } + return fire - m_aFires; +} + +bool +CFireManager::IsScriptFireExtinguish(int16 index) +{ + return !m_aFires[index].m_bIsOngoing; +} + +void +CFireManager::RemoveAllScriptFires(void) +{ + for (int i = 0; i < NUM_FIRES; i++) { + if (m_aFires[i].m_bIsScriptFire) { + RemoveScriptFire(i); + } + } +} + +void +CFireManager::RemoveScriptFire(int16 index) +{ + m_aFires[index].Extinguish(); + m_aFires[index].m_bIsScriptFire = false; +} + +void +CFireManager::SetScriptFireAudio(int16 index, bool state) +{ + m_aFires[index].m_bAudioSet = state; +} diff --git a/src/miami/core/Fire.h b/src/miami/core/Fire.h new file mode 100644 index 00000000..8126f830 --- /dev/null +++ b/src/miami/core/Fire.h @@ -0,0 +1,53 @@ +#pragma once + +class CEntity; + +class CFire +{ +public: + bool m_bIsOngoing; + bool m_bIsScriptFire; + bool m_bPropagationFlag; + bool m_bAudioSet; + CVector m_vecPos; + CEntity *m_pEntity; + CEntity *m_pSource; + uint32 m_nExtinguishTime; + uint32 m_nStartTime; + uint32 m_nNextTimeToAddFlames; + float m_fStrength; + float m_fWaterExtinguishCountdown; + bool m_bExtinguishedWithWater; + + CFire(); + ~CFire(); + void ProcessFire(void); + void ReportThisFire(void); + void Extinguish(void); +}; + +class CFireManager +{ + enum { + MAX_FIREMEN_ATTENDING = 2, + }; +public: + uint32 m_nTotalFires; + CFire m_aFires[NUM_FIRES]; + + void StartFire(CVector pos, float size, uint8 propagation); + CFire *StartFire(CEntity *entityOnFire, CEntity *fleeFrom, float strength, uint8 propagation); + void Update(void); + CFire *FindFurthestFire_NeverMindFireMen(CVector coords, float minRange, float maxRange); + CFire *FindNearestFire(CVector vecPos, float *pDistance); + CFire *GetNextFreeFire(void); + uint32 GetTotalActiveFires() const; + void ExtinguishPoint(CVector point, float range); + bool ExtinguishPointWithWater(CVector point, float range); + int32 StartScriptFire(const CVector &pos, CEntity *target, float strength, uint8 propagation); + bool IsScriptFireExtinguish(int16 index); + void RemoveAllScriptFires(void); + void RemoveScriptFire(int16 index); + void SetScriptFireAudio(int16 index, bool state); +}; +extern CFireManager gFireManager; diff --git a/src/miami/core/FrontEndControls.cpp b/src/miami/core/FrontEndControls.cpp new file mode 100644 index 00000000..18f6b3b2 --- /dev/null +++ b/src/miami/core/FrontEndControls.cpp @@ -0,0 +1,1887 @@ +#include "common.h" +#include "main.h" +#include "Timer.h" +#include "Sprite2d.h" +#include "Text.h" +#include "Font.h" +#include "FrontEndControls.h" + +#define X SCREEN_SCALE_X +#define Y(x) SCREEN_SCALE_Y(float(x)*(float(DEFAULT_SCREEN_HEIGHT)/float(SCREEN_HEIGHT_PAL))) + +void +CPlaceableShText::Draw(float x, float y) +{ + if(m_text == nil) + return; + + if(m_bRightJustify) + CFont::SetRightJustifyOn(); + if(m_bDropShadow){ + CFont::SetDropShadowPosition(m_shadowOffset.x); + CFont::SetDropColor(m_shadowColor); + } + CFont::SetColor(m_color); + CFont::PrintString(x+m_position.x, y+m_position.y, m_text); + if(m_bDropShadow) + CFont::SetDropShadowPosition(0); + if(m_bRightJustify) + CFont::SetRightJustifyOff(); +} + +void +CPlaceableShText::Draw(const CRGBA &color, float x, float y) +{ + if(m_text == nil) + return; + + if(m_bRightJustify) + CFont::SetRightJustifyOn(); + if(m_bDropShadow){ + CFont::SetDropShadowPosition(m_shadowOffset.x); + CFont::SetDropColor(m_shadowColor); + } + CFont::SetColor(color); + CFont::PrintString(x+m_position.x, y+m_position.y, m_text); + if(m_bDropShadow) + CFont::SetDropShadowPosition(0); + if(m_bRightJustify) + CFont::SetRightJustifyOff(); +} + +void +CPlaceableShTextTwoLines::Draw(float x, float y) +{ + if(m_line1.m_text == nil && m_line2.m_text == nil) + return; + + if(m_bRightJustify) + CFont::SetRightJustifyOn(); + if(m_bDropShadow){ + CFont::SetDropShadowPosition(m_shadowOffset.x); + CFont::SetDropColor(m_shadowColor); + } + + if(m_line1.m_text){ + CFont::SetColor(m_line1.m_color); + CFont::PrintString(x+m_line1.m_position.x, y+m_line1.m_position.y, m_line1.m_text); + } + if(m_line2.m_text){ + CFont::SetColor(m_line2.m_color); + CFont::PrintString(x+m_line2.m_position.x, y+m_line2.m_position.y, m_line2.m_text); + } + + if(m_bDropShadow) + CFont::SetDropShadowPosition(0); + if(m_bRightJustify) + CFont::SetRightJustifyOff(); +} + +void +CPlaceableShTextTwoLines::Draw(const CRGBA &color, float x, float y) +{ + if(m_line1.m_text == nil && m_line2.m_text == nil) + return; + + if(m_bRightJustify) + CFont::SetRightJustifyOn(); + if(m_bDropShadow){ + CFont::SetDropShadowPosition(m_shadowOffset.x); + CFont::SetDropColor(m_shadowColor); + } + + if(m_line1.m_text){ + CFont::SetColor(color); + CFont::PrintString(x+m_line1.m_position.x, y+m_line1.m_position.y, m_line1.m_text); + } + if(m_line2.m_text){ + CFont::SetColor(color); + CFont::PrintString(x+m_line2.m_position.x, y+m_line2.m_position.y, m_line2.m_text); + } + + if(m_bDropShadow) + CFont::SetDropShadowPosition(0); + if(m_bRightJustify) + CFont::SetRightJustifyOff(); +} + +void +CPlaceableShOption::Draw(const CRGBA &highlightColor, float x, float y, bool bHighlight) +{ + if(bHighlight) + CPlaceableShText::Draw(highlightColor, x, y); + else if(m_bSelected) + CPlaceableShText::Draw(m_selectedColor, x, y); + else + CPlaceableShText::Draw(x, y); +} + +void +CPlaceableShOptionTwoLines::Draw(const CRGBA &highlightColor, float x, float y, bool bHighlight) +{ + if(bHighlight) + CPlaceableShTextTwoLines::Draw(highlightColor, x, y); + else if(m_bSelected) + CPlaceableShTextTwoLines::Draw(m_selectedColor, x, y); + else + CPlaceableShTextTwoLines::Draw(x, y); +} + +void +CPlaceableSprite::Draw(float x, float y) +{ + Draw(m_color, x, y); +} + +void +CPlaceableSprite::Draw(const CRGBA &color, float x, float y) +{ + if(m_pSprite) + m_pSprite->Draw(CRect(m_position.x+x, m_position.y+y, + m_position.x+x + m_size.x, m_position.y+y + m_size.y), + color); +} + +void +CPlaceableShSprite::Draw(float x, float y) +{ + if(m_bDropShadow) + m_shadow.Draw(m_shadow.m_color, m_sprite.m_position.x+x, m_sprite.m_position.y+y); + m_sprite.Draw(x, y); +} + + +/* + * CMenuPictureAndText + */ + +void +CMenuPictureAndText::SetNewOldShadowWrapX(bool bWrapX, float newWrapX, float oldWrapX) +{ + m_bWrap = bWrapX; + m_wrapX = newWrapX; + m_oldWrapx = oldWrapX; +} + +void +CMenuPictureAndText::SetNewOldTextScale(bool bTextScale, const CVector2D &newScale, const CVector2D &oldScale) +{ + m_bSetTextScale = bTextScale; + m_textScale = newScale; + m_oldTextScale = oldScale; +} + +void +CMenuPictureAndText::SetTextsColor(CRGBA const &color) +{ + int i; + for(i = 0; i < m_numTexts; i++) + m_texts[i].m_color = color; +} + +void +CMenuPictureAndText::AddText(wchar *text, float positionX, float positionY, CRGBA const &color, bool bRightJustify) +{ + int i; + if(m_numTexts >= 20) + return; + i = m_numTexts++; + m_texts[i].m_text = text; + m_texts[i].m_position.x = positionX; + m_texts[i].m_position.y = positionY; + m_texts[i].m_color = color; + m_texts[i].m_bRightJustify = bRightJustify; +} + +void +CMenuPictureAndText::AddPicture(CSprite2d *sprite, CSprite2d *shadow, float positionX, float positionY, float width, float height, CRGBA const &color) +{ + int i; + if(m_numSprites >= 5) + return; + i = m_numSprites++; + m_sprites[i].m_sprite.m_pSprite = sprite; + m_sprites[i].m_shadow.m_pSprite = shadow; + m_sprites[i].m_sprite.m_position.x = positionX; + m_sprites[i].m_sprite.m_position.y = positionY; + m_sprites[i].m_sprite.m_size.x = width; + m_sprites[i].m_sprite.m_size.y = height; + m_sprites[i].m_shadow.m_size.x = width; + m_sprites[i].m_shadow.m_size.y = height; + m_sprites[i].m_sprite.m_color = color; +} + +void +CMenuPictureAndText::AddPicture(CSprite2d *sprite, float positionX, float positionY, float width, float height, CRGBA const &color) +{ + int i; + if(m_numSprites >= 5) + return; + i = m_numSprites++; + m_sprites[i].m_sprite.m_pSprite = sprite; + m_sprites[i].m_shadow.m_pSprite = nil; + m_sprites[i].m_sprite.m_position.x = positionX; + m_sprites[i].m_sprite.m_position.y = positionY; + m_sprites[i].m_sprite.m_size.x = width; + m_sprites[i].m_sprite.m_size.y = height; + m_sprites[i].m_shadow.m_size.x = width; + m_sprites[i].m_shadow.m_size.y = height; + m_sprites[i].m_sprite.m_color = color; +} + +void +CMenuPictureAndText::Draw(CRGBA const &,CRGBA const &, float x, float y) +{ + int i; + + for(i = 0; i < m_numSprites; i++) + m_sprites[i].Draw(m_position.x+x, m_position.y+y); + + if(m_bSetTextScale) + CFont::SetScale(m_textScale.x, m_textScale.y); + for(i = 0; i < m_numTexts; i++) + if(m_bWrap) + m_texts[i].DrawShWrap(m_position.x+x, m_position.y+y, m_wrapX, m_oldWrapx); + else + m_texts[i].Draw(m_position.x+x, m_position.y+y); + if(m_bSetTextScale) + CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); +} + +void +CMenuPictureAndText::SetAlpha(uint8 alpha) +{ + int i; + + for(i = 0; i < m_numSprites; i++) + m_sprites[i].SetAlpha(alpha); + for(i = 0; i < m_numTexts; i++) + m_texts[i].SetAlpha(alpha); +} + +void +CMenuPictureAndText::SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset) +{ + int i; + + for(i = 0; i < 5; i++) + m_sprites[i].SetShadows(bDropShadows, shadowColor, shadowOffset); + for(i = 0; i < 20; i++) + m_texts[i].SetShadows(bDropShadows, shadowColor, shadowOffset); +} + +/* + * CMenuMultiChoice + */ + +void +CMenuMultiChoice::AddTitle(wchar *text, float positionX, float positionY, bool bRightJustify) +{ + m_title.m_text = text; + m_title.SetPosition(positionX, positionY, bRightJustify); +} + +CPlaceableShOption* +CMenuMultiChoice::AddOption(wchar *text, float positionX, float positionY, bool bSelected, bool bRightJustify) +{ + if(m_numOptions == NUM_MULTICHOICE_OPTIONS) + return nil; + m_options[m_numOptions].m_text = text; + m_options[m_numOptions].SetPosition(positionX, positionY); + m_options[m_numOptions].m_bSelected = bSelected; + m_options[m_numOptions].m_bRightJustify = bRightJustify; + return &m_options[m_numOptions++]; +} + +void +CMenuMultiChoice::SetColors(const CRGBA &title, const CRGBA &normal, const CRGBA &selected) +{ + int i; + m_title.SetColor(title); + for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) + m_options[i].SetColors(normal, selected); +} + +void +CMenuMultiChoice::SetNewOldTextScale(bool bTextScale, const CVector2D &newScale, const CVector2D &oldScale, bool bTitleTextScale) +{ + m_bSetTextScale = bTextScale; + m_textScale = newScale; + m_oldTextScale = oldScale; + m_bSetTitleTextScale = bTitleTextScale; +} + +void +CMenuMultiChoice::Draw(CRGBA const &optionHighlight ,CRGBA const &titleHighlight, float x, float y) +{ + int i; + + if(m_bSetTextScale && m_bSetTitleTextScale) + CFont::SetScale(m_textScale.x, m_textScale.y); + if(m_cursor == -1) + m_title.Draw(m_position.x+x, m_position.y+y); + else + m_title.Draw(titleHighlight, m_position.x+x, m_position.y+y); + + if(m_bSetTextScale) + CFont::SetScale(m_textScale.x, m_textScale.y); + + if(m_cursor == -1) + for(i = 0; i < m_numOptions; i++) + m_options[i].Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); + else + for(i = 0; i < m_numOptions; i++){ + if(i == m_cursor) + m_options[i].Draw(optionHighlight, m_position.x+x, m_position.y+y); + else + m_options[i].Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); + } + + if(m_bSetTextScale){ + CFont::DrawFonts(); + CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); + } +} + +void +CMenuMultiChoice::DrawNormal(float x, float y) +{ + int i; + + if(m_bSetTextScale && m_bSetTitleTextScale) + CFont::SetScale(m_textScale.x, m_textScale.y); + m_title.Draw(m_position.x+x, m_position.y+y); + + if(m_bSetTextScale) + CFont::SetScale(m_textScale.x, m_textScale.y); + + for(i = 0; i < m_numOptions; i++) + m_options[i].Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); + + if(m_bSetTextScale){ + CFont::DrawFonts(); + CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); + } +} + +void +CMenuMultiChoice::DrawHighlighted(CRGBA const &titleHighlight, float x, float y) +{ + int i; + + if(m_bSetTextScale && m_bSetTitleTextScale) + CFont::SetScale(m_textScale.x, m_textScale.y); + if(m_cursor == -1) + m_title.Draw(m_position.x+x, m_position.y+y); + else + m_title.Draw(titleHighlight, m_position.x+x, m_position.y+y); + + if(m_bSetTextScale) + CFont::SetScale(m_textScale.x, m_textScale.y); + + for(i = 0; i < m_numOptions; i++) + m_options[i].Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); + + if(m_bSetTextScale){ + CFont::DrawFonts(); + CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); + } +} + +void +CMenuMultiChoice::SetAlpha(uint8 alpha) +{ + int i; + m_title.SetAlpha(alpha); + for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) + m_options[i].SetAlpha(alpha); +} + +void +CMenuMultiChoice::SetShadows(bool bDropShadows, CRGBA const &shadowColor, CVector2D const &shadowOffset) +{ + int i; + m_title.SetShadows(bDropShadows, shadowColor, shadowOffset); + for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) + m_options[i].SetShadows(bDropShadows, shadowColor, shadowOffset); +} + + +bool +CMenuMultiChoice::GoNext(void) +{ + if(m_cursor == m_numOptions-1){ + m_cursor = -1; + return false; + }else{ + m_cursor++; + return true; + } +} + +bool +CMenuMultiChoice::GoPrev(void) +{ + if(m_cursor == 0){ + m_cursor = -1; + return false; + }else{ + m_cursor--; + return true; + } +} + +void +CMenuMultiChoice::SelectCurrentOptionUnderCursor(void) +{ + int i; + if(m_cursor == -1) + return; + for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) + m_options[i].m_bSelected = false; + m_options[m_cursor].m_bSelected = true; +} + +int +CMenuMultiChoice::GetMenuSelection(void) +{ + int i; + for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) + if(m_options[i].m_bSelected) + return i; + return -1; +} + +void +CMenuMultiChoice::SetMenuSelection(int selection) +{ + int i; + for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) + m_options[i].m_bSelected = false; + m_options[selection%NUM_MULTICHOICE_OPTIONS].m_bSelected = true; +} + +/* + * CMenuMultiChoiceTriggered + */ + +void +CMenuMultiChoiceTriggered::Initialise(void) +{ + int i; + for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) + m_triggers[i] = nil; + m_defaultCancel = nil; +} + +CPlaceableShOption* +CMenuMultiChoiceTriggered::AddOption(wchar *text, float positionX, float positionY, Trigger trigger, bool bSelected, bool bRightJustify) +{ + CPlaceableShOption *option; + option = CMenuMultiChoice::AddOption(text, positionX, positionY, bSelected, bRightJustify); + if(option) + m_triggers[m_numOptions-1] = trigger; + return option; +} + +void +CMenuMultiChoiceTriggered::SelectCurrentOptionUnderCursor(void) +{ + CMenuMultiChoice::SelectCurrentOptionUnderCursor(); + if(m_cursor != -1 && m_triggers[m_cursor] != nil ) + m_triggers[m_cursor](this); +} + +void +CMenuMultiChoiceTriggered::SelectDefaultCancelAction(void) +{ + if(m_defaultCancel) + m_defaultCancel(this); +} + +/* + * CMenuMultiChoiceTriggeredAlways + */ + +void +CMenuMultiChoiceTriggeredAlways::Draw(CRGBA const &optionHighlight, CRGBA const &titleHighlight, float x, float y) +{ + if(m_alwaysTrigger) + m_alwaysTrigger(this); + CMenuMultiChoiceTriggered::Draw(optionHighlight, titleHighlight, x, y); +} + +void +CMenuMultiChoiceTriggeredAlways::DrawNormal(float x, float y) +{ + if(m_alwaysNormalTrigger) + m_alwaysNormalTrigger(this); + CMenuMultiChoiceTriggered::DrawNormal(x, y); +} + +void +CMenuMultiChoiceTriggeredAlways::DrawHighlighted(CRGBA const &titleHighlight, float x, float y) +{ + if(m_alwaysHighlightTrigger) + m_alwaysHighlightTrigger(this); + CMenuMultiChoiceTriggered::DrawHighlighted(titleHighlight, x, y); +} + +/* + * CMenuMultiChoicePictured + */ + +void +CMenuMultiChoicePictured::Initialise(void) +{ + int i; + for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) + m_bHasSprite[i] = false; + for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) + m_sprites[i].m_pSprite = nil; +} + +CPlaceableShOption* +CMenuMultiChoicePictured::AddOption(CSprite2d *sprite, float positionX, float positionY, const CVector2D &size, bool bSelected) +{ + CPlaceableShOption *option; + option = CMenuMultiChoice::AddOption(nil, 0.0f, 0.0f, bSelected, false); + if(option){ + m_sprites[m_numOptions-1].m_pSprite = sprite; + m_sprites[m_numOptions-1].SetPosition(positionX, positionY); + m_sprites[m_numOptions-1].m_size = size; + m_bHasSprite[m_numOptions-1] = true; + } + return option; +} + +void +CMenuMultiChoicePictured::Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y) +{ + int i; + + // The title and all the text + CMenuMultiChoice::Draw(optionHighlight, titleHighlight, x, y); + + CRGBA selectedColor = m_options[0].GetSelectedColor(); + CRGBA color = m_options[0].GetColor(); + + // The sprites + if(m_cursor == -1){ + for(i = 0; i < m_numOptions; i++) + if(m_bHasSprite[i]){ + if(m_options[i].m_bSelected) + m_sprites[i].Draw(selectedColor, m_position.x+x, m_position.y+y); + else + m_sprites[i].Draw(color, m_position.x+x, m_position.y+y); + } + }else{ + for(i = 0; i < m_numOptions; i++) + if(i == m_cursor){ + if(m_bHasSprite[i]) + { + uint8 color = Max(Max(optionHighlight.r, optionHighlight.g), optionHighlight.b); + m_sprites[i].Draw(CRGBA(color, color, color, optionHighlight.a), m_position.x+x, m_position.y+y); + } + }else{ + if(m_bHasSprite[i]){ + if(m_options[i].m_bSelected) + m_sprites[i].Draw(selectedColor, m_position.x+x, m_position.y+y); + else + m_sprites[i].Draw(color, m_position.x+x, m_position.y+y); + } + } + } +} + +void +CMenuMultiChoicePictured::DrawNormal(float x, float y) +{ + int i; + + // The title and all the text + CMenuMultiChoice::DrawNormal(x, y); + + CRGBA selectedColor = m_options[0].GetSelectedColor(); + CRGBA color = m_options[0].GetColor(); + + // The sprites + for(i = 0; i < m_numOptions; i++) + if(m_bHasSprite[i]){ + if(m_options[i].m_bSelected) + m_sprites[i].Draw(selectedColor, m_position.x+x, m_position.y+y); + else + m_sprites[i].Draw(color, m_position.x+x, m_position.y+y); + } +} + +void +CMenuMultiChoicePictured::DrawHighlighted(const CRGBA &titleHighlight, float x, float y) +{ + int i; + + // The title and all the text + CMenuMultiChoice::DrawHighlighted(titleHighlight, x, y); + + CRGBA selectedColor = m_options[0].GetSelectedColor(); + CRGBA color = m_options[0].GetColor(); + + // The sprites + for(i = 0; i < m_numOptions; i++) + if(m_bHasSprite[i]){ + if(m_options[i].m_bSelected) + m_sprites[i].Draw(selectedColor, m_position.x+x, m_position.y+y); + else + m_sprites[i].Draw(color, m_position.x+x, m_position.y+y); + } +} + +void +CMenuMultiChoicePictured::SetAlpha(uint8 alpha) +{ + int i; + CMenuMultiChoice::SetAlpha(alpha); + for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) + m_sprites[i].SetAlpha(alpha); + +} + + +/* + * CMenuMultiChoicePicturedTriggered + */ + +void +CMenuMultiChoicePicturedTriggered::Initialise(void) +{ + int i; + for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) + m_triggers[i] = nil; + m_defaultCancel = nil; // missing on PS2 +} + +CPlaceableShOption* +CMenuMultiChoicePicturedTriggered::AddOption(CSprite2d *sprite, float positionX, float positionY, const CVector2D &size, Trigger trigger, bool bSelected) +{ + CPlaceableShOption *option; + option = CMenuMultiChoicePictured::AddOption(sprite, positionX, positionY, size, bSelected); + if(option) + m_triggers[m_numOptions-1] = trigger; + return option; +} + +void +CMenuMultiChoicePicturedTriggered::SelectCurrentOptionUnderCursor(void) +{ + CMenuMultiChoice::SelectCurrentOptionUnderCursor(); + if(m_cursor != -1) + m_triggers[m_cursor](this); +} + +void +CMenuMultiChoicePicturedTriggered::SelectDefaultCancelAction(void) +{ + if(m_defaultCancel) + m_defaultCancel(this); +} + +/* + * CMenuMultiChoicePicturedTriggeredAnyMove + */ + +void +CMenuMultiChoicePicturedTriggeredAnyMove::Initialise(void) +{ + int i; + CMenuMultiChoicePicturedTriggered::Initialise(); + for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++){ + m_moveTab[i].right = -1; + m_moveTab[i].left = -1; + m_moveTab[i].down = -1; + m_moveTab[i].up = -1; + } +} + +CPlaceableShOption* +CMenuMultiChoicePicturedTriggeredAnyMove::AddOption(CSprite2d *sprite, FEC_MOVETAB *moveTab, float positionX, float positionY, const CVector2D &size, Trigger trigger, bool bSelected) +{ + CPlaceableShOption *option; + option = CMenuMultiChoicePicturedTriggered::AddOption(sprite, positionX, positionY, size, trigger, bSelected); + if(option && moveTab) + m_moveTab[m_numOptions-1] = *moveTab; + return option; +} + +bool +CMenuMultiChoicePicturedTriggeredAnyMove::GoDown(void) +{ + int move = m_moveTab[m_cursor].down; + if(move == -1) + return GoNext(); + m_cursor = move; + return true; +} + +bool +CMenuMultiChoicePicturedTriggeredAnyMove::GoUp(void) +{ + int move = m_moveTab[m_cursor].up; + if(move == -1) + return GoPrev(); + m_cursor = move; + return true; +} + +bool +CMenuMultiChoicePicturedTriggeredAnyMove::GoLeft(void) +{ + int move = m_moveTab[m_cursor].left; + if(move == -1) + return GoPrev(); + m_cursor = move; + return true; +} + +bool +CMenuMultiChoicePicturedTriggeredAnyMove::GoRight(void) +{ + int move = m_moveTab[m_cursor].right; + if(move == -1) + return GoNext(); + m_cursor = move; + return true; +} + + +/* + * CMenuMultiChoiceTwoLines + */ + +void +CMenuMultiChoiceTwoLines::AddTitle(wchar *text, float positionX, float positionY, bool bRightJustify) +{ + m_title.m_text = text; + m_title.SetPosition(positionX, positionY, bRightJustify); +} + +CPlaceableShOptionTwoLines* +CMenuMultiChoiceTwoLines::AddOption(wchar *text, float positionX, float positionY, bool bSelected, bool bRightJustify) +{ + return AddOption(text, positionX, positionY, nil, 0.0f, 0.0f, bSelected, bRightJustify); +} + +CPlaceableShOptionTwoLines* +CMenuMultiChoiceTwoLines::AddOption(wchar *text1, float positionX1, float positionY1, wchar *text2, float positionX2, float positionY2, bool bSelected, bool bRightJustify) +{ + if(m_numOptions == NUM_MULTICHOICE_OPTIONS) + return nil; + m_options[m_numOptions].m_line1.m_text = text1; + m_options[m_numOptions].m_line2.m_text = text2; + m_options[m_numOptions].m_line1.SetPosition(positionX1, positionY1); + m_options[m_numOptions].m_line2.SetPosition(positionX2, positionY2); + m_options[m_numOptions].m_bSelected = bSelected; + m_options[m_numOptions].m_bRightJustify = bRightJustify; + return &m_options[m_numOptions++]; +} + + +void +CMenuMultiChoiceTwoLines::SetColors(const CRGBA &title, const CRGBA &normal, const CRGBA &selected) +{ + int i; + m_title.SetColor(title); + for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) + m_options[i].SetColors(normal, selected); +} + +void +CMenuMultiChoiceTwoLines::SetNewOldTextScale(bool bTextScale, const CVector2D &newScale, const CVector2D &oldScale, bool bTitleTextScale) +{ + m_bSetTextScale = bTextScale; + m_textScale = newScale; + m_oldTextScale = oldScale; + m_bSetTitleTextScale = bTitleTextScale; +} + +void +CMenuMultiChoiceTwoLines::Draw(CRGBA const &optionHighlight ,CRGBA const &titleHighlight, float x, float y) +{ + int i; + + if(m_bSetTextScale && m_bSetTitleTextScale) + CFont::SetScale(m_textScale.x, m_textScale.y); + if(m_cursor == -1) + m_title.Draw(m_position.x+x, m_position.y+y); + else + m_title.Draw(titleHighlight, m_position.x+x, m_position.y+y); + + if(m_bSetTextScale) + CFont::SetScale(m_textScale.x, m_textScale.y); + + if(m_cursor == -1) + for(i = 0; i < m_numOptions; i++) + m_options[i].Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); + else + for(i = 0; i < m_numOptions; i++){ + if(i == m_cursor) + m_options[i].Draw(optionHighlight, m_position.x+x, m_position.y+y); + else + m_options[i].Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); + } + + if(m_bSetTextScale){ + CFont::DrawFonts(); + CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); + } +} + +void +CMenuMultiChoiceTwoLines::DrawNormal(float x, float y) +{ + int i; + + if(m_bSetTextScale && m_bSetTitleTextScale) + CFont::SetScale(m_textScale.x, m_textScale.y); + m_title.Draw(m_position.x+x, m_position.y+y); + + if(m_bSetTextScale) + CFont::SetScale(m_textScale.x, m_textScale.y); + + for(i = 0; i < m_numOptions; i++) + m_options[i].Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); + + if(m_bSetTextScale){ + CFont::DrawFonts(); + CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); + } +} + +void +CMenuMultiChoiceTwoLines::DrawHighlighted(CRGBA const &titleHighlight, float x, float y) +{ + int i; + + if(m_bSetTextScale && m_bSetTitleTextScale) + CFont::SetScale(m_textScale.x, m_textScale.y); + if(m_cursor == -1) + m_title.Draw(m_position.x+x, m_position.y+y); + else + m_title.Draw(titleHighlight, m_position.x+x, m_position.y+y); + + if(m_bSetTextScale) + CFont::SetScale(m_textScale.x, m_textScale.y); + + for(i = 0; i < m_numOptions; i++) + m_options[i].Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); + + if(m_bSetTextScale){ + CFont::DrawFonts(); + CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); + } +} + +void +CMenuMultiChoiceTwoLines::SetAlpha(uint8 alpha) +{ + int i; + m_title.SetAlpha(alpha); + for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) + m_options[i].SetAlpha(alpha); +} + +void +CMenuMultiChoiceTwoLines::SetShadows(bool bDropShadows, CRGBA const &shadowColor, CVector2D const &shadowOffset) +{ + int i; + m_title.SetShadows(bDropShadows, shadowColor, shadowOffset); + for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) + m_options[i].SetShadows(bDropShadows, shadowColor, shadowOffset); +} + + +bool +CMenuMultiChoiceTwoLines::GoNext(void) +{ + if(m_cursor == m_numOptions-1){ + m_cursor = -1; + return false; + }else{ + m_cursor++; + return true; + } +} + +bool +CMenuMultiChoiceTwoLines::GoPrev(void) +{ + if(m_cursor == 0){ + m_cursor = -1; + return false; + }else{ + m_cursor--; + return true; + } +} + +void +CMenuMultiChoiceTwoLines::SelectCurrentOptionUnderCursor(void) +{ + int i; + if(m_cursor == -1) + return; + for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) + m_options[i].m_bSelected = false; + m_options[m_cursor].m_bSelected = true; +} + +int +CMenuMultiChoiceTwoLines::GetMenuSelection(void) +{ + int i; + for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) + if(m_options[i].m_bSelected) + return i; + return -1; +} + +void +CMenuMultiChoiceTwoLines::SetMenuSelection(int selection) +{ + int i; + for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) + m_options[i].m_bSelected = false; + m_options[selection%NUM_MULTICHOICE_OPTIONS].m_bSelected = true; +} + +/* + * CMenuMultiChoiceTwoLinesTriggered + */ + +void +CMenuMultiChoiceTwoLinesTriggered::Initialise(void) +{ + int i; + for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) + m_triggers[i] = nil; + m_defaultCancel = nil; +} + +CPlaceableShOptionTwoLines* +CMenuMultiChoiceTwoLinesTriggered::AddOption(wchar *text, float positionX, float positionY, Trigger trigger, bool bSelected, bool bRightJustify) +{ + CPlaceableShOptionTwoLines *option; + option = CMenuMultiChoiceTwoLines::AddOption(text, positionX, positionY, bSelected, bRightJustify); + if(option) + m_triggers[m_numOptions-1] = trigger; + return option; +} + +CPlaceableShOptionTwoLines* +CMenuMultiChoiceTwoLinesTriggered::AddOption(wchar *text1, float positionX1, float positionY1, wchar *text2, float positionX2, float positionY2, Trigger trigger, bool bSelected, bool bRightJustify) +{ + CPlaceableShOptionTwoLines *option; + option = CMenuMultiChoiceTwoLines::AddOption(text1, positionX1, positionY1, text2, positionX2, positionY2, bSelected, bRightJustify); + if(option) + m_triggers[m_numOptions-1] = trigger; + return option; +} + +void +CMenuMultiChoiceTwoLinesTriggered::SelectCurrentOptionUnderCursor(void) +{ + CMenuMultiChoiceTwoLines::SelectCurrentOptionUnderCursor(); + if(m_cursor != -1) + m_triggers[m_cursor](this); +} + +void +CMenuMultiChoiceTwoLinesTriggered::SelectDefaultCancelAction(void) +{ + if(m_defaultCancel) + m_defaultCancel(this); +} + + +/* + * CMenuOnOff + */ + +void +CMenuOnOff::SetColors(const CRGBA &title, const CRGBA &options) +{ + m_title.SetColors(title, title); + m_options[0].SetColor(options); + m_options[1].SetColor(options); +} + +void +CMenuOnOff::SetNewOldTextScale(bool bTextScale, const CVector2D &newScale, const CVector2D &oldScale, bool bTitleTextScale) +{ + m_bSetTextScale = bTextScale; + m_textScale = newScale; + m_oldTextScale = oldScale; + m_bSetTitleTextScale = bTitleTextScale; +} + +void +CMenuOnOff::SetOptionPosition(float x, float y, bool bRightJustify) +{ + m_options[0].SetPosition(x, y, bRightJustify); + m_options[1].SetPosition(x, y, bRightJustify); +} + +void +CMenuOnOff::AddTitle(wchar *text, bool bSelected, float positionX, float positionY, bool bRightJustify) +{ + m_title.m_text = text; + m_title.m_bSelected = bSelected; + m_title.SetPosition(positionX, positionY, bRightJustify); +} + +void +CMenuOnOff::Draw(CRGBA const &optionHighlight, CRGBA const &titleHighlight, float x, float y) +{ + if(m_type == 1){ + m_options[0].m_text = TheText.Get("FEM_NO"); + m_options[1].m_text = TheText.Get("FEM_YES"); + }else if(m_type == 0){ + m_options[0].m_text = TheText.Get("FEM_OFF"); + m_options[1].m_text = TheText.Get("FEM_ON"); + } + + if(m_bSetTextScale && m_bSetTitleTextScale) + CFont::SetScale(m_textScale.x, m_textScale.y); + if(m_bActive) + m_title.Draw(titleHighlight, m_position.x+x, m_position.y+y); + else + m_title.Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); + + if(m_bSetTextScale) + CFont::SetScale(m_textScale.x, m_textScale.y); + + if(m_bActive){ + if(m_title.m_bSelected) + m_options[1].Draw(optionHighlight, m_position.x+x, m_position.y+y); + else + m_options[0].Draw(optionHighlight, m_position.x+x, m_position.y+y); + }else{ + if(m_title.m_bSelected) + m_options[1].Draw(m_position.x+x, m_position.y+y); + else + m_options[0].Draw(m_position.x+x, m_position.y+y); + } + + if(m_bSetTextScale) + CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); +} + +void +CMenuOnOff::DrawNormal(float x, float y) +{ + if(m_type == 1){ + m_options[0].m_text = TheText.Get("FEM_NO"); + m_options[1].m_text = TheText.Get("FEM_YES"); + }else if(m_type == 0){ + m_options[0].m_text = TheText.Get("FEM_OFF"); + m_options[1].m_text = TheText.Get("FEM_ON"); + } + + if(m_bSetTextScale && m_bSetTitleTextScale) + CFont::SetScale(m_textScale.x, m_textScale.y); + m_title.Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); + + if(m_bSetTextScale) + CFont::SetScale(m_textScale.x, m_textScale.y); + + if(m_title.m_bSelected) + m_options[1].Draw(m_position.x+x, m_position.y+y); + else + m_options[0].Draw(m_position.x+x, m_position.y+y); + + if(m_bSetTextScale) + CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); +} + +void +CMenuOnOff::DrawHighlighted(CRGBA const &titleHighlight, float x, float y) +{ + if(m_type == 1){ + m_options[0].m_text = TheText.Get("FEM_NO"); + m_options[1].m_text = TheText.Get("FEM_YES"); + }else if(m_type == 0){ + m_options[0].m_text = TheText.Get("FEM_OFF"); + m_options[1].m_text = TheText.Get("FEM_ON"); + } + + if(m_bSetTextScale && m_bSetTitleTextScale) + CFont::SetScale(m_textScale.x, m_textScale.y); + if(m_bActive) + m_title.Draw(titleHighlight, m_position.x+x, m_position.y+y); + else + m_title.Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); + + if(m_bSetTextScale) + CFont::SetScale(m_textScale.x, m_textScale.y); + + if(m_title.m_bSelected) + m_options[1].Draw(m_position.x+x, m_position.y+y); + else + m_options[0].Draw(m_position.x+x, m_position.y+y); + + if(m_bSetTextScale) + CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); +} + +void +CMenuOnOff::SetAlpha(uint8 alpha) +{ + m_title.SetAlpha(alpha); + m_options[0].SetAlpha(alpha); + m_options[1].SetAlpha(alpha); +} + +void +CMenuOnOff::SetShadows(bool bDropShadows, CRGBA const &shadowColor, CVector2D const &shadowOffset) +{ + m_title.SetShadows(bDropShadows, shadowColor, shadowOffset); + m_options[0].SetShadows(bDropShadows, shadowColor, shadowOffset); + m_options[1].SetShadows(bDropShadows, shadowColor, shadowOffset); +} + +/* + * CMenuOnOffTriggered + */ + +void +CMenuOnOffTriggered::SetOptionPosition(float x, float y, Trigger trigger, bool bRightJustify) +{ + CMenuOnOff::SetOptionPosition(x, y, bRightJustify); + if(trigger) + m_trigger = trigger; +} + +void +CMenuOnOffTriggered::SelectCurrentOptionUnderCursor(void) +{ + CMenuOnOff::SelectCurrentOptionUnderCursor(); + if(m_trigger) + m_trigger(this); +} + + + +/* + * CMenuSlider + */ + +char CMenuSlider::Buf8[8]; +wchar CMenuSlider::Buf16[8]; + +void +CMenuSlider::SetColors(const CRGBA &title, const CRGBA &percentage, const CRGBA &left, const CRGBA &right) +{ + m_title.SetColor(title); + m_percentageText.SetColor(percentage); + m_colors[0] = left; + m_colors[1] = right; +} + + +void +CMenuSlider::AddTickBox(float positionX, float positionY, float width, float heightLeft, float heightRight) +{ + m_box.SetPosition(positionX, positionY); + m_size[0].x = width; + m_size[0].y = heightLeft; + m_size[1].x = width; + m_size[1].y = heightRight; +} + +void +CMenuSlider::AddTitle(wchar *text, float positionX, float positionY) +{ + m_title.m_text = text; + m_title.SetPosition(positionX, positionY); +} + +static CRGBA SELECTED_TEXT_COLOR_0(255, 182, 48, 255); + +void +CMenuSlider::Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y) +{ + if(m_bActive){ + CRGBA selectionCol = m_colors[0]; + if(optionHighlight.red == SELECTED_TEXT_COLOR_0.red && + optionHighlight.green == SELECTED_TEXT_COLOR_0.green && + optionHighlight.blue == SELECTED_TEXT_COLOR_0.blue && + optionHighlight.alpha == SELECTED_TEXT_COLOR_0.alpha) + selectionCol = m_colors[1]; + + if(m_style == 1){ + // solid bar + CRGBA shadowCol = m_box.GetShadowColor(); + float f = m_value/1000.0f; + CVector2D boxPos = m_box.m_position + m_position + CVector2D(x,y); + if(m_box.m_bDropShadow) + CSprite2d::DrawRect( + CRect(boxPos.x + X(m_box.m_shadowOffset.x), + boxPos.y + Y(m_box.m_shadowOffset.y), + boxPos.x + X(m_box.m_shadowOffset.x) + m_size[0].x, + boxPos.y + Y(m_box.m_shadowOffset.y) + m_size[0].y), + shadowCol); + CSprite2d::DrawRect( + CRect(boxPos.x, boxPos.y, + boxPos.x + m_size[0].x, boxPos.y + m_size[0].y), + m_colors[1]); + CSprite2d::DrawRect( + CRect(boxPos.x, boxPos.y, + boxPos.x + m_size[0].x*f, boxPos.y + m_size[0].y), + selectionCol); + }else if(m_style == 0){ + // ticks... + CVector2D boxPos = m_box.m_position + m_position + CVector2D(x,y); + DrawTicks(boxPos, m_size[0], m_size[1].y, + m_value/1000.0f, m_colors[0], selectionCol, m_colors[1], + m_box.m_bDropShadow, m_box.m_shadowOffset, m_box.GetShadowColor()); + } + + m_title.Draw(titleHighlight, m_position.x+x, m_position.y+y); + + if(m_bDrawPercentage){ + sprintf(Buf8, "%d%%", m_value/10); + AsciiToUnicode(Buf8, Buf16); + m_percentageText.m_text = Buf16; + m_percentageText.Draw(optionHighlight, m_position.x+x, m_position.y+y); + } + }else + CMenuSlider::DrawNormal(x, y); +} + +void +CMenuSlider::DrawNormal(float x, float y) +{ + if(m_style == 1){ + // solid bar + CRGBA shadowCol = m_box.GetShadowColor(); + float f = m_value/1000.0f; + CVector2D boxPos = m_box.m_position + m_position + CVector2D(x,y); + if(m_box.m_bDropShadow) + CSprite2d::DrawRect( + CRect(boxPos.x + X(m_box.m_shadowOffset.x), + boxPos.y + Y(m_box.m_shadowOffset.y), + boxPos.x + X(m_box.m_shadowOffset.x) + m_size[0].x, + boxPos.y + Y(m_box.m_shadowOffset.y) + m_size[0].y), + shadowCol); + CSprite2d::DrawRect( + CRect(boxPos.x, boxPos.y, + boxPos.x + m_size[0].x, boxPos.y + m_size[0].y), + m_colors[1]); + CSprite2d::DrawRect( + CRect(boxPos.x, boxPos.y, + boxPos.x + m_size[0].x*f, boxPos.y + m_size[0].y), + m_colors[0]); + }else if(m_style == 0){ + // ticks... + CVector2D boxPos = m_box.m_position + m_position + CVector2D(x,y); + DrawTicks(boxPos, m_size[0], m_size[1].y, + m_value/1000.0f, m_colors[0], m_colors[1], + m_box.m_bDropShadow, m_box.m_shadowOffset, m_box.GetShadowColor()); + } + + m_title.Draw(m_position.x+x, m_position.y+y); + + if(m_bDrawPercentage){ + sprintf(Buf8, "%d%%", m_value/10); + AsciiToUnicode(Buf8, Buf16); + m_percentageText.m_text = Buf16; + m_percentageText.Draw(m_percentageText.GetColor(), m_position.x+x, m_position.y+y); + } +} + +void +CMenuSlider::DrawHighlighted(const CRGBA &titleHighlight, float x, float y) +{ + if(m_bActive) + m_title.Draw(titleHighlight, m_position.x+x, m_position.y+y); + else + m_title.Draw(m_position.x+x, m_position.y+y); + + if(m_style == 1){ + // solid bar + CRGBA shadowCol = m_box.GetShadowColor(); + float f = m_value/1000.0f; + CVector2D boxPos = m_box.m_position + m_position + CVector2D(x,y); + if(m_box.m_bDropShadow) + CSprite2d::DrawRect( + CRect(boxPos.x + X(m_box.m_shadowOffset.x), + boxPos.y + Y(m_box.m_shadowOffset.y), + boxPos.x + X(m_box.m_shadowOffset.x) + m_size[0].x, + boxPos.y + Y(m_box.m_shadowOffset.y) + m_size[0].y), + shadowCol); + CSprite2d::DrawRect( + CRect(boxPos.x, boxPos.y, + boxPos.x + m_size[0].x, boxPos.y + m_size[0].y), + m_colors[1]); + CSprite2d::DrawRect( + CRect(boxPos.x, boxPos.y, + boxPos.x + m_size[0].x*f, boxPos.y + m_size[0].y), + m_colors[0]); + }else if(m_style == 0){ + // ticks... + CVector2D boxPos = m_box.m_position + m_position + CVector2D(x,y); + DrawTicks(boxPos, m_size[0], m_size[1].y, + m_value/1000.0f, m_colors[0], m_colors[1], + m_box.m_bDropShadow, m_box.m_shadowOffset, m_box.GetShadowColor()); + } + + if(m_bDrawPercentage){ + sprintf(Buf8, "%d%%", m_value/10); + AsciiToUnicode(Buf8, Buf16); + m_percentageText.m_text = Buf16; + m_percentageText.Draw(m_percentageText.GetColor(), m_position.x+x, m_position.y+y); + } +} + +void +CMenuSlider::DrawTicks(const CVector2D &position, const CVector2D &size, float heightRight, float level, const CRGBA &leftCol, const CRGBA &selCol, const CRGBA &rightCol, bool bShadow, const CVector2D &shadowOffset, const CRGBA &shadowColor) +{ + int i; + int numTicks = size.x / X(8.0f); + float dy = heightRight - size.y; + float stepy = dy / numTicks; + int left = level*numTicks; + int drewSelection = 0; + for(i = 0; i < numTicks; i++){ + CRect rect(position.x + X(8.0f)*i, position.y + dy - stepy*i, + position.x + X(8.0f)*i + X(4.0f), position.y + dy + size.y); + if(bShadow){ + CRect shadowRect = rect; + shadowRect.left += X(shadowOffset.x); + shadowRect.right += X(shadowOffset.x); + shadowRect.top += Y(shadowOffset.y); + shadowRect.bottom += Y(shadowOffset.y); + CSprite2d::DrawRect(shadowRect, shadowColor); + } + if(i < left) + CSprite2d::DrawRect(rect, leftCol); + else if(!drewSelection){ + CSprite2d::DrawRect(rect, selCol); + drewSelection = 1; + }else + CSprite2d::DrawRect(rect, rightCol); + } +} + +void +CMenuSlider::DrawTicks(const CVector2D &position, const CVector2D &size, float heightRight, float level, const CRGBA &leftCol, const CRGBA &rightCol, bool bShadow, const CVector2D &shadowOffset, const CRGBA &shadowColor) +{ + int i; + int numTicks = size.x / X(8.0f); + float dy = heightRight - size.y; + float stepy = dy / numTicks; + int left = level*numTicks; + for(i = 0; i < numTicks; i++){ + CRect rect(position.x + X(8.0f)*i, position.y + dy - stepy*i, + position.x + X(8.0f)*i + X(4.0f), position.y + dy + size.y); + if(bShadow){ + CRect shadowRect = rect; + shadowRect.left += X(shadowOffset.x); + shadowRect.right += X(shadowOffset.x); + shadowRect.top += Y(shadowOffset.y); + shadowRect.bottom += Y(shadowOffset.y); + CSprite2d::DrawRect(shadowRect, shadowColor); + } + if(i < left) + CSprite2d::DrawRect(rect, leftCol); + else + CSprite2d::DrawRect(rect, rightCol); + } +} + +void +CMenuSlider::SetAlpha(uint8 alpha) +{ + m_title.SetAlpha(alpha); + m_box.SetAlpha(alpha); + m_someAlpha = alpha; + m_percentageText.SetAlpha(alpha); + m_colors[0].alpha = alpha; + m_colors[1].alpha = alpha; +} + +void +CMenuSlider::SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset) +{ + m_title.SetShadows(bDropShadows, shadowColor, shadowOffset); + m_box.SetShadows(bDropShadows, shadowColor, shadowOffset); + m_percentageText.SetShadows(bDropShadows, shadowColor, shadowOffset); +} + +/* + * CMenuSliderTriggered + */ + +void +CMenuSliderTriggered::AddTickBox(float positionX, float positionY, float width, float heightLeft, float heightRight, Trigger trigger, Trigger alwaysTrigger) +{ + CMenuSlider::AddTickBox(positionX, positionY, width, heightLeft, heightRight); + m_trigger = trigger; + m_alwaysTrigger = alwaysTrigger; +} + +void +CMenuSliderTriggered::Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y) +{ + CMenuSlider::Draw(optionHighlight, titleHighlight, x, y); + if(m_alwaysTrigger) + m_alwaysTrigger(this); +} + +bool +CMenuSliderTriggered::GoLeft(void) +{ + CMenuSlider::GoLeft(); + if(m_trigger) + m_trigger(this); + return true; +} + +bool +CMenuSliderTriggered::GoRight(void) +{ + CMenuSlider::GoRight(); + if(m_trigger) + m_trigger(this); + return true; +} + +bool +CMenuSliderTriggered::GoLeftStill(void) +{ + CMenuSlider::GoLeftStill(); + if(m_trigger) + m_trigger(this); + return true; +} + +bool +CMenuSliderTriggered::GoRightStill(void) +{ + CMenuSlider::GoRightStill(); + if(m_trigger) + m_trigger(this); + return true; +} + +/* + * CMenuLineLister + */ + +CMenuLineLister::CMenuLineLister(void) + : m_numLines(0), m_width(0.0f), m_height(0.0f), + m_scrollPosition(0.0f), m_scrollSpeed(1.0f), m_lineSpacing(15.0f), field_10E8(0) +{ + int i; + for(i = 0; i < NUM_LINELISTER_LINES_TOTAL; i++){ + m_lineAlphas[i] = 0; + m_lineFade[i] = 0; + } +} + + +void +CMenuLineLister::SetLinesColor(const CRGBA &color) +{ + int i; + for(i = 0; i < NUM_LINELISTER_LINES_TOTAL; i++){ + m_linesLeft[i].SetColor(color); + m_linesRight[i].SetColor(color); + } +} + +void +CMenuLineLister::ResetNumberOfTextLines(void) +{ + int i; + m_numLines = 0; + for(i = 0; i < NUM_LINELISTER_LINES_TOTAL; i++){ + m_lineAlphas[i] = 0; + m_lineFade[i] = 0; + } + for(i = 0; i < NUM_LINELISTER_LINES_TOTAL; i++){ + // note this doesn't clear lines 0-14, probably an oversight + GetLeftLine(i)->m_text = nil; + GetRightLine(i)->m_text = nil; + } +} + +bool +CMenuLineLister::AddTextLine(wchar *left, wchar *right) +{ + CPlaceableShText *leftLine, *rightLine; + if(m_numLines == NUM_LINELISTER_LINES) + return false; + leftLine = GetLeftLine(m_numLines); + leftLine->m_text = left; + leftLine->SetPosition(0.0f, m_lineSpacing*(m_numLines+15)); + rightLine = GetRightLine(m_numLines); + rightLine->m_text = right; + rightLine->SetPosition(leftLine->m_position.x, leftLine->m_position.y); + m_numLines++; + return true; +} + +void +CMenuLineLister::Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y) +{ + int i, n; + + m_scrollPosition += m_scrollSpeed; + n = m_numLines + 15; + if(m_scrollSpeed > 0.0f){ + if(m_scrollPosition > n*m_lineSpacing) + m_scrollPosition = 0.0f; + }else{ + if(m_scrollPosition < 0.0f) + m_scrollPosition = n*m_lineSpacing; + } + // this is a weird condition.... + for(i = 0; + m_scrollPosition < i*m_lineSpacing || m_scrollPosition >= (i+1)*m_lineSpacing; + i++); + + float screenPos = 0.0f; + for(; i < n; i++){ + CVector2D linePos = m_linesLeft[i].m_position; + + if(linePos.y+m_position.y - (m_scrollPosition+m_position.y) < Y(64.0f)) + m_lineFade[i] = -4.0f*Abs(m_scrollSpeed); + else + m_lineFade[i] = 4.0f*Abs(m_scrollSpeed); + int newAlpha = m_lineAlphas[i] + m_lineFade[i]; + if(newAlpha < 0) newAlpha = 0; + if(newAlpha > 255) newAlpha = 255; + m_lineAlphas[i] = newAlpha; + + uint8 alpha = m_linesLeft[i].m_shadowColor.alpha; + + // apply alpha + m_linesLeft[i].SetAlpha((alpha*m_lineAlphas[i])>>8); + m_linesRight[i].SetAlpha((alpha*m_lineAlphas[i])>>8); + + m_linesLeft[i].Draw(m_position.x+x, m_position.y+y - m_scrollPosition); + CFont::SetRightJustifyOn(); + m_linesRight[i].Draw(m_position.x+x + m_width, m_position.y+y - m_scrollPosition); + CFont::SetRightJustifyOff(); + + // restore alpha + m_linesLeft[i].SetAlpha(alpha); + m_linesRight[i].SetAlpha(alpha); + + screenPos += m_lineSpacing; + if(screenPos >= m_height) + break; + } + + m_scrollSpeed = 1.0f; +} + +void +CMenuLineLister::SetAlpha(uint8 alpha) +{ + int i; + for(i = 0; i < NUM_LINELISTER_LINES_TOTAL; i++){ + m_linesLeft[i].SetAlpha(alpha); + m_linesRight[i].SetAlpha(alpha); + } +} + +void +CMenuLineLister::SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset) +{ + int i; + for(i = 0; i < NUM_LINELISTER_LINES_TOTAL; i++){ + m_linesLeft[i].SetShadows(bDropShadows, shadowColor, shadowOffset); + m_linesRight[i].SetShadows(bDropShadows, shadowColor, shadowOffset); + } +} + + +/* + * CMenuPage + */ + +void +CMenuPage::Initialise(void) +{ + int i; + m_numControls = 0; + m_pCurrentControl = nil; + m_cursor = 0; + for(i = 0; i < NUM_PAGE_WIDGETS; i++) + m_controls[i] = nil; +} + +bool +CMenuPage::AddMenu(CMenuBase *widget) +{ + if(m_numControls >= NUM_PAGE_WIDGETS) + return false; + m_controls[m_numControls] = widget; + if(m_numControls == 0){ + m_pCurrentControl = widget; + m_cursor = 0; + } + m_numControls++; + return true; +} + +bool +CMenuPage::IsActiveMenuTwoState(void) +{ + return m_pCurrentControl && m_pCurrentControl->m_bTwoState; +} + +void +CMenuPage::ActiveMenuTwoState_SelectNextPosition(void) +{ + int sel; + if(m_pCurrentControl == nil || !m_pCurrentControl->m_bTwoState) + return; + m_pCurrentControl->GoFirst(); + sel = m_pCurrentControl->GetMenuSelection(); + if(sel == 1) + m_pCurrentControl->SelectCurrentOptionUnderCursor(); + else if(sel == 0){ + if ( m_pCurrentControl ) + { + if ( !m_pCurrentControl->GoNext() ) + m_pCurrentControl->GoFirst(); + } + + m_pCurrentControl->SelectCurrentOptionUnderCursor(); + } +} + +void +CMenuPage::Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y) +{ + int i; + for(i = 0; i < m_numControls; i++) + if(m_controls[i]){ + if(i == m_cursor) + m_controls[i]->Draw(optionHighlight, titleHighlight, x, y); + else + m_controls[i]->DrawNormal(x, y); + } +} + +void +CMenuPage::DrawHighlighted(const CRGBA &titleHighlight, float x, float y) +{ + int i; + for(i = 0; i< m_numControls; i++) + if(m_controls[i]){ + if(i == m_cursor) + m_controls[i]->DrawHighlighted(titleHighlight, x, y); + else + m_controls[i]->DrawNormal(x, y); + } +} + +void +CMenuPage::DrawNormal(float x, float y) +{ + int i; + for(i = 0; i< m_numControls; i++) + if(m_controls[i]) + m_controls[i]->DrawNormal(x, y); +} + +void +CMenuPage::ActivatePage(void) +{ + m_cursor = 0; + if(m_numControls == 0) + return; + for(;;){ + m_pCurrentControl = m_controls[m_cursor]; + if(m_pCurrentControl->GoFirst()) + return; + if(m_cursor == m_numControls-1) + m_cursor = 0; + else + m_cursor++; + } +} + +void +CMenuPage::SetAlpha(uint8 alpha) +{ + int i; + for(i = 0; i< m_numControls; i++) + if(m_controls[i]) + m_controls[i]->SetAlpha(alpha); +} + +void +CMenuPage::SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset) +{ + int i; + for(i = 0; i< m_numControls; i++) + if(m_controls[i]) + m_controls[i]->SetShadows(bDropShadows, shadowColor, shadowOffset); +} + +void +CMenuPage::GoUpMenuOnPage(void) +{ + if(m_pCurrentControl == nil) + return; + m_pCurrentControl->DeactivateMenu(); + do{ + if(m_cursor == 0) + m_cursor = m_numControls-1; + else + m_cursor--; + m_pCurrentControl = m_controls[m_cursor]; + }while(!m_pCurrentControl->GoLast()); +} + +void +CMenuPage::GoDownMenuOnPage(void) +{ + if(m_pCurrentControl == nil) + return; + m_pCurrentControl->DeactivateMenu(); + do{ + if(m_cursor == m_numControls-1) + m_cursor = 0; + else + m_cursor++; + m_pCurrentControl = m_controls[m_cursor]; + }while(!m_pCurrentControl->GoFirst()); +} + +void +CMenuPage::GoLeftMenuOnPage(void) +{ + // same as up + if(m_pCurrentControl == nil) + return; + m_pCurrentControl->DeactivateMenu(); + do{ + if(m_cursor == 0) + m_cursor = m_numControls-1; + else + m_cursor--; + m_pCurrentControl = m_controls[m_cursor]; + }while(!m_pCurrentControl->GoLast()); +} + +void +CMenuPage::GoRightMenuOnPage(void) +{ + // same as right + if(m_pCurrentControl == nil) + return; + m_pCurrentControl->DeactivateMenu(); + do{ + if(m_cursor == m_numControls-1) + m_cursor = 0; + else + m_cursor++; + m_pCurrentControl = m_controls[m_cursor]; + }while(!m_pCurrentControl->GoFirst()); +} + +/* + * CMenuPageAnyMove + */ + +void +CMenuPageAnyMove::Initialise(void) +{ + int i; + CMenuPage::Initialise(); + for(i = 0; i < NUM_PAGE_WIDGETS; i++){ + m_moveTab[i].left = -1; + m_moveTab[i].right = -1; + m_moveTab[i].up = -1; + m_moveTab[i].down = -1; + } +} + +bool +CMenuPageAnyMove::AddMenu(CMenuBase *widget, FEC_MOVETAB *moveTab) +{ + if(AddMenu(widget)){ + m_moveTab[m_numControls-1] = *moveTab; + return true; + } + return false; +} + +void +CMenuPageAnyMove::GoUpMenuOnPage(void) +{ + if(m_pCurrentControl == nil) + return; + m_pCurrentControl->DeactivateMenu(); + int move = m_moveTab[m_cursor].up; + if(move == -1) + CMenuPage::GoUpMenuOnPage(); + else{ // BUG: no else in original code + m_cursor = move; + m_pCurrentControl = m_controls[m_cursor]; + m_pCurrentControl->GoLast(); + } +} + +void +CMenuPageAnyMove::GoDownMenuOnPage(void) +{ + if(m_pCurrentControl == nil) + return; + m_pCurrentControl->DeactivateMenu(); + int move = m_moveTab[m_cursor].down; + if(move == -1) + CMenuPage::GoDownMenuOnPage(); + else{ // BUG: no else in original code + m_cursor = move; + m_pCurrentControl = m_controls[m_cursor]; + m_pCurrentControl->GoLast(); + } +} + +void +CMenuPageAnyMove::GoLeftMenuOnPage(void) +{ + if(m_pCurrentControl == nil) + return; + m_pCurrentControl->DeactivateMenu(); + int move = m_moveTab[m_cursor].left; + if(move == -1) + CMenuPage::GoLeftMenuOnPage(); + else{ // BUG: no else in original code + m_cursor = move; + m_pCurrentControl = m_controls[m_cursor]; + m_pCurrentControl->GoLast(); + } +} + +void +CMenuPageAnyMove::GoRightMenuOnPage(void) +{ + if(m_pCurrentControl == nil) + return; + m_pCurrentControl->DeactivateMenu(); + int move = m_moveTab[m_cursor].right; + if(move == -1) + CMenuPage::GoRightMenuOnPage(); + else{ // BUG: no else in original code + m_cursor = move; + m_pCurrentControl = m_controls[m_cursor]; + m_pCurrentControl->GoLast(); + } +} diff --git a/src/miami/core/FrontEndControls.h b/src/miami/core/FrontEndControls.h new file mode 100644 index 00000000..68dab90b --- /dev/null +++ b/src/miami/core/FrontEndControls.h @@ -0,0 +1,750 @@ +#pragma once + +enum { + NUM_MULTICHOICE_OPTIONS = 16, + // 50 actual lines and 15 for spacing + NUM_LINELISTER_LINES = 50, + NUM_LINELISTER_LINES_TOTAL = NUM_LINELISTER_LINES + 15, + NUM_PAGE_WIDGETS = 10, +}; + +class CTriggerCaller +{ + bool bHasTrigger; + void *pTrigger; + void (*pFunc)(void *); + int field_C; +public: + + CTriggerCaller() : bHasTrigger(false), pFunc(nil) + {} + + void SetTrigger(void *func, void *trigger) + { + if ( !bHasTrigger ) + { + pFunc = (void (*)(void *))func; + pTrigger = trigger; + bHasTrigger = true; + } + } + + void CallTrigger(void) + { + if ( bHasTrigger && pFunc != nil ) + pFunc(pTrigger); + + bHasTrigger = false; + pFunc = nil; + } + + bool CanCall() + { + return bHasTrigger; + } +}; + +class CPlaceableText +{ +public: + CVector2D m_position; + CRGBA m_color; + wchar *m_text; + + CPlaceableText(void) + : m_position(0.0f, 0.0f), m_color(255, 255, 255, 255), m_text(nil) {} + void SetPosition(float x, float y) { m_position.x = x; m_position.y = y; } + void SetColor(const CRGBA &color) { m_color = color; } + CRGBA GetColor(void) { return m_color; } + void SetAlpha(uint8 alpha) { m_color.alpha = alpha; } +}; + +// No trace of this in the game but it makes the other classes simpler +class CPlaceableTextTwoLines +{ +public: + CPlaceableText m_line1; + CPlaceableText m_line2; + + void SetColor(const CRGBA &color) { m_line1.SetColor(color); m_line2.SetColor(color); } + void SetAlpha(uint8 alpha) { m_line1.SetAlpha(alpha); m_line2.SetAlpha(alpha); } +}; + +// No trace of this in the game but it makes the other classes simpler +class CShadowInfo +{ +public: + bool m_bRightJustify; + bool m_bDropShadow; + CRGBA m_shadowColor; + CVector2D m_shadowOffset; + + CShadowInfo(void) + : m_bRightJustify(false), m_bDropShadow(false), + m_shadowColor(255, 255, 255, 255), + m_shadowOffset(-1.0f, -1.0f) {} + CRGBA GetShadowColor(void) { return m_shadowColor; } + void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset){ + m_bDropShadow = bDropShadows; + m_shadowColor = shadowColor; + m_shadowOffset = shadowOffset; + } +}; + +// No trace of this in the game but it makes the other classes simpler +class CSelectable +{ +public: + bool m_bSelected; + CRGBA m_selectedColor; + + CSelectable(void) : m_bSelected(false) {} + CRGBA GetSelectedColor(void) { return m_selectedColor; } +}; + +class CPlaceableShText : public CPlaceableText, public CShadowInfo +{ +public: + using CPlaceableText::SetPosition; + void SetPosition(float x, float y, bool bRightJustify) { SetPosition(x, y); m_bRightJustify = bRightJustify; } + void SetAlpha(uint8 alpha) { m_shadowColor.alpha = alpha; CPlaceableText::SetAlpha(alpha); } + + void Draw(float x, float y); + void Draw(const CRGBA &color, float x, float y); + // unused arguments it seems + void DrawShWrap(float x, float y, float wrapX, float wrapY) { Draw(x, y); } +}; + +class CPlaceableShTextTwoLines : public CPlaceableTextTwoLines, public CShadowInfo +{ +public: + void SetAlpha(uint8 alpha) { m_shadowColor.alpha = alpha; CPlaceableTextTwoLines::SetAlpha(alpha); } + + void Draw(float x, float y); + void Draw(const CRGBA &color, float x, float y); +}; + +class CPlaceableShOption : public CPlaceableShText, public CSelectable +{ +public: + void SetColors(const CRGBA &normal, const CRGBA &selection) { CPlaceableShText::SetColor(normal); m_selectedColor = selection; } + void SetAlpha(uint8 alpha) { m_selectedColor.alpha = alpha; CPlaceableShText::SetAlpha(alpha); } + + using CPlaceableShText::Draw; + void Draw(const CRGBA &highlightColor, float x, float y, bool bHighlight); +}; + +class CPlaceableShOptionTwoLines : public CPlaceableShTextTwoLines, public CSelectable +{ +public: + void SetColors(const CRGBA &normal, const CRGBA &selection) { CPlaceableShTextTwoLines::SetColor(normal); m_selectedColor = selection; } + void SetAlpha(uint8 alpha) { m_selectedColor.alpha = alpha; CPlaceableShTextTwoLines::SetAlpha(alpha); } + + using CPlaceableShTextTwoLines::Draw; + void Draw(const CRGBA &highlightColor, float x, float y, bool bHighlight); +}; + +class CPlaceableSprite +{ +public: + CSprite2d *m_pSprite; + CVector2D m_position; + CVector2D m_size; + CRGBA m_color; + + CPlaceableSprite(void) + : m_pSprite(nil), m_position(0.0f, 0.0f), + m_size(0.0f, 0.0f), m_color(255, 255, 255, 255) {} + + void SetPosition(float x, float y) { m_position.x = x; m_position.y = y; } + void SetAlpha(uint8 alpha) { m_color.alpha = alpha; } + + void Draw(float x, float y); + void Draw(const CRGBA &color, float x, float y); +}; + +class CPlaceableShSprite +{ +public: + CPlaceableSprite m_sprite; + CPlaceableSprite m_shadow; + bool m_bDropShadow; + + CPlaceableShSprite(void) : m_bDropShadow(false) {} + + void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset){ + m_bDropShadow = bDropShadows; + m_shadow.m_color = shadowColor; + m_shadow.m_position = shadowOffset; + } + void SetAlpha(uint8 alpha) { m_sprite.SetAlpha(alpha); m_shadow.SetAlpha(alpha); } + + void Draw(float x, float y); +}; + + +class CMenuBase +{ +public: + CVector2D m_position; + bool m_bTwoState; + + CMenuBase(void) + : m_position(0.0f, 0.0f), m_bTwoState(false) {} + void SetPosition(float x, float y) { m_position.x = x; m_position.y = y; } + + virtual void Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y) = 0; + virtual void DrawNormal(float x, float y) = 0; + virtual void DrawHighlighted(const CRGBA &titleHighlight, float x, float y) = 0; + virtual void SetAlpha(uint8 alpha) = 0; + virtual void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset) = 0; + virtual bool GoNext(void) = 0; + virtual bool GoPrev(void) = 0; + virtual bool GoDown(void) = 0; + virtual bool GoUp(void) = 0; + virtual bool GoDownStill(void) = 0; + virtual bool GoUpStill(void) = 0; + virtual bool GoLeft(void) = 0; + virtual bool GoRight(void) = 0; + virtual bool GoLeftStill(void) = 0; + virtual bool GoRightStill(void) = 0; + virtual bool GoFirst(void) = 0; + virtual bool GoLast(void) = 0; + virtual void SelectCurrentOptionUnderCursor(void) = 0; + virtual void SelectDefaultCancelAction(void) = 0; + virtual void ActivateMenu(bool first) = 0; + virtual void DeactivateMenu(void) = 0; + virtual int GetMenuSelection(void) = 0; + virtual void SetMenuSelection(int selection) = 0; +}; + +class CMenuDummy : public CMenuBase +{ +public: + bool m_bActive; + + virtual void Draw(const CRGBA &, const CRGBA &, float x, float y) {} + virtual void DrawNormal(float x, float y) {} + virtual void DrawHighlighted(const CRGBA &, float x, float y) {} + virtual void SetAlpha(uint8 alpha) {} + virtual void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset) {} + virtual bool GoNext(void) { DeactivateMenu(); return false; } + virtual bool GoPrev(void) { DeactivateMenu(); return false; } + virtual bool GoDown(void) { return GoNext(); } + virtual bool GoUp(void) { return GoPrev(); } + virtual bool GoDownStill(void) { return false; } + virtual bool GoUpStill(void) { return false; } + virtual bool GoLeft(void) { return true; } + virtual bool GoRight(void) { return true; } + virtual bool GoLeftStill(void) { return true; } + virtual bool GoRightStill(void) { return true; } + virtual bool GoFirst(void) { ActivateMenu(true); return true; } + virtual bool GoLast(void) { ActivateMenu(true); return true; } + virtual void SelectCurrentOptionUnderCursor(void) {} + virtual void SelectDefaultCancelAction(void) {} + virtual void ActivateMenu(bool first) { m_bActive = true; } + virtual void DeactivateMenu(void) { m_bActive = false; } + virtual int GetMenuSelection(void) { return -1; } + virtual void SetMenuSelection(int) {} +}; + +class CMenuPictureAndText : public CMenuBase +{ +public: + int m_numSprites; + CPlaceableShSprite m_sprites[5]; + int m_numTexts; + CPlaceableShText m_texts[20]; + + CVector2D m_oldTextScale; + CVector2D m_textScale; + bool m_bSetTextScale; + + float m_wrapX; + float m_oldWrapx; + bool m_bWrap; + // missing some? + + + CMenuPictureAndText(void) + : m_numSprites(0), m_numTexts(0), + m_bSetTextScale(false), m_bWrap(false) {} + + void SetNewOldShadowWrapX(bool bWrapX, float newWrapX, float oldWrapX); + void SetNewOldTextScale(bool bTextScale, const CVector2D &newScale, const CVector2D &oldScale); + void SetTextsColor(const CRGBA &color); + void AddText(wchar *text, float positionX, float positionY, const CRGBA &color, bool bRightJustify); + void AddPicture(CSprite2d *sprite, CSprite2d *shadow, float positionX, float positionY, float width, float height, const CRGBA &color); + void AddPicture(CSprite2d *sprite, float positionX, float positionY, float width, float height, const CRGBA &color); + + virtual void Draw(const CRGBA &, const CRGBA &, float x, float y); + virtual void DrawNormal(float x, float y) { Draw(CRGBA(0,0,0,0), CRGBA(0,0,0,0), x, y); } + virtual void DrawHighlighted(const CRGBA &, float x, float y) { Draw(CRGBA(0,0,0,0), CRGBA(0,0,0,0), x, y); } + virtual void SetAlpha(uint8 alpha); + virtual void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset); + virtual bool GoNext(void) { return false; } + virtual bool GoPrev(void) { return false; } + virtual bool GoDown(void) { return GoNext(); } + virtual bool GoUp(void) { return GoPrev(); } + virtual bool GoDownStill(void) { return false; } + virtual bool GoUpStill(void) { return false; } + virtual bool GoLeft(void) { return true; } + virtual bool GoRight(void) { return true; } + virtual bool GoLeftStill(void) { return true; } + virtual bool GoRightStill(void) { return true; } + virtual bool GoFirst(void) { return false; } + virtual bool GoLast(void) { return false; } + virtual void SelectCurrentOptionUnderCursor(void) {} + virtual void SelectDefaultCancelAction(void) {} + virtual void ActivateMenu(bool first) {} + virtual void DeactivateMenu(void) {} + virtual int GetMenuSelection(void) { return -1; } + virtual void SetMenuSelection(int) {} +}; + +class CMenuMultiChoice : public CMenuBase +{ +public: + int m_numOptions; + CPlaceableShText m_title; + CPlaceableShOption m_options[NUM_MULTICHOICE_OPTIONS]; + int m_cursor; + CVector2D m_oldTextScale; + CVector2D m_textScale; + bool m_bSetTextScale; + bool m_bSetTitleTextScale; + + CMenuMultiChoice(void) + : m_numOptions(0), m_cursor(-1), + m_bSetTextScale(false), m_bSetTitleTextScale(false) {} + + void AddTitle(wchar *text, float positionX, float positionY, bool bRightJustify); + CPlaceableShOption *AddOption(wchar *text, float positionX, float positionY, bool bSelected, bool bRightJustify); + void SetColors(const CRGBA &title, const CRGBA &normal, const CRGBA &selected); + void SetNewOldTextScale(bool bTextScale, const CVector2D &newScale, const CVector2D &oldScale, bool bTitleTextScale); + + virtual void Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y); + virtual void DrawNormal(float x, float y); + virtual void DrawHighlighted(const CRGBA &titleHighlight, float x, float y); + virtual void SetAlpha(uint8 alpha); + virtual void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset); + virtual bool GoNext(void); + virtual bool GoPrev(void); + virtual bool GoDown(void) { return GoNext(); } + virtual bool GoUp(void) { return GoPrev(); } + virtual bool GoDownStill(void) { return false; } + virtual bool GoUpStill(void) { return false; } + virtual bool GoLeft(void) { return GoPrev(); } + virtual bool GoRight(void) { return GoNext(); } + virtual bool GoLeftStill(void) { return true; } + virtual bool GoRightStill(void) { return true; } + virtual bool GoFirst(void) { m_cursor = 0; return true; } + virtual bool GoLast(void) { m_cursor = m_numOptions-1; return true; } + virtual void SelectCurrentOptionUnderCursor(void); + virtual void SelectDefaultCancelAction(void) {} + virtual void ActivateMenu(bool first) { m_cursor = first ? 0 : m_numOptions-1; } + virtual void DeactivateMenu(void) { m_cursor = -1; } + virtual int GetMenuSelection(void); + virtual void SetMenuSelection(int selection); +}; + +class CMenuMultiChoiceTriggered : public CMenuMultiChoice +{ +public: + typedef void (*Trigger)(CMenuMultiChoiceTriggered *); + + Trigger m_triggers[NUM_MULTICHOICE_OPTIONS]; + Trigger m_defaultCancel; + + CMenuMultiChoiceTriggered(void) { Initialise(); } + + void Initialise(void); + CPlaceableShOption *AddOption(wchar *text, float positionX, float positionY, Trigger trigger, bool bSelected, bool bRightJustify); + + virtual void SelectCurrentOptionUnderCursor(void); + virtual void SelectDefaultCancelAction(void); +}; + +class CMenuMultiChoiceTriggeredAlways : public CMenuMultiChoiceTriggered +{ +public: + Trigger m_alwaysNormalTrigger; + Trigger m_alwaysHighlightTrigger; + Trigger m_alwaysTrigger; + + CMenuMultiChoiceTriggeredAlways(void) + : m_alwaysNormalTrigger(nil), m_alwaysHighlightTrigger(nil), m_alwaysTrigger(nil) {} + + virtual void Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y); + virtual void DrawNormal(float x, float y); + virtual void DrawHighlighted(const CRGBA &titleHighlight, float x, float y); +}; + +class CMenuMultiChoicePictured : public CMenuMultiChoice +{ +public: + CPlaceableSprite m_sprites[NUM_MULTICHOICE_OPTIONS]; + bool m_bHasSprite[NUM_MULTICHOICE_OPTIONS]; + + CMenuMultiChoicePictured(void) { Initialise(); } + void Initialise(void); + using CMenuMultiChoice::AddOption; + CPlaceableShOption *AddOption(CSprite2d *sprite, float positionX, float positionY, const CVector2D &size, bool bSelected); + + virtual void Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y); + virtual void DrawNormal(float x, float y); + virtual void DrawHighlighted(const CRGBA &titleHighlight, float x, float y); + virtual void SetAlpha(uint8 alpha); + // unnecessary - same as base class +// virtual void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset); +}; + +class CMenuMultiChoicePicturedTriggered : public CMenuMultiChoicePictured +{ +public: + typedef void (*Trigger)(CMenuMultiChoicePicturedTriggered *); + + Trigger m_triggers[NUM_MULTICHOICE_OPTIONS]; + Trigger m_defaultCancel; + + CMenuMultiChoicePicturedTriggered(void) { Initialise(); } + + void Initialise(void); + using CMenuMultiChoicePictured::AddOption; + CPlaceableShOption *AddOption(CSprite2d *sprite, float positionX, float positionY, const CVector2D &size, Trigger trigger, bool bSelected); + + virtual void SelectCurrentOptionUnderCursor(void); + virtual void SelectDefaultCancelAction(void); +}; + +struct FEC_MOVETAB +{ + int8 right; + int8 left; + int8 down; + int8 up; +}; + +class CMenuMultiChoicePicturedTriggeredAnyMove : public CMenuMultiChoicePicturedTriggered +{ +public: + FEC_MOVETAB m_moveTab[NUM_MULTICHOICE_OPTIONS]; + + CMenuMultiChoicePicturedTriggeredAnyMove(void) { Initialise(); } + + void Initialise(void); + using CMenuMultiChoicePicturedTriggered::AddOption; + CPlaceableShOption *AddOption(CSprite2d *sprite, FEC_MOVETAB *moveTab, float positionX, float positionY, const CVector2D &size, Trigger trigger, bool bSelected); + + virtual bool GoDown(void); + virtual bool GoUp(void); + virtual bool GoLeft(void); + virtual bool GoRight(void); +}; + +// copy of CMenuMultiChoice pretty much except for m_options type +class CMenuMultiChoiceTwoLines : public CMenuBase +{ +public: + int m_numOptions; + CPlaceableShText m_title; + CPlaceableShOptionTwoLines m_options[NUM_MULTICHOICE_OPTIONS]; + int m_cursor; + CVector2D m_oldTextScale; + CVector2D m_textScale; + bool m_bSetTextScale; + bool m_bSetTitleTextScale; + + CMenuMultiChoiceTwoLines(void) + : m_numOptions(0), m_cursor(-1), + m_bSetTextScale(false), m_bSetTitleTextScale(false) {} + + void AddTitle(wchar *text, float positionX, float positionY, bool bRightJustify); + CPlaceableShOptionTwoLines *AddOption(wchar *text, float positionX, float positionY, bool bSelected, bool bRightJustify); + CPlaceableShOptionTwoLines *AddOption(wchar *text1, float positionX1, float positionY1, wchar *text2, float positionX2, float positionY2, bool bSelected, bool bRightJustify); + void SetColors(const CRGBA &title, const CRGBA &normal, const CRGBA &selected); + void SetNewOldTextScale(bool bTextScale, const CVector2D &newScale, const CVector2D &oldScale, bool bTitleTextScale); + + virtual void Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y); + virtual void DrawNormal(float x, float y); + virtual void DrawHighlighted(const CRGBA &titleHighlight, float x, float y); + virtual void SetAlpha(uint8 alpha); + virtual void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset); + virtual bool GoNext(void); + virtual bool GoPrev(void); + virtual bool GoDown(void) { return GoNext(); } + virtual bool GoUp(void) { return GoPrev(); } + virtual bool GoDownStill(void) { return true; } + virtual bool GoUpStill(void) { return true; } + virtual bool GoLeft(void) { return GoPrev(); } + virtual bool GoRight(void) { return GoNext(); } + virtual bool GoLeftStill(void) { return true; } + virtual bool GoRightStill(void) { return true; } + virtual bool GoFirst(void) { m_cursor = 0; return true; } + virtual bool GoLast(void) { m_cursor = m_numOptions-1; return true; } + virtual void SelectCurrentOptionUnderCursor(void); + virtual void SelectDefaultCancelAction(void) {} + virtual void ActivateMenu(bool first) { m_cursor = first ? 0 : m_numOptions-1; } + virtual void DeactivateMenu(void) { m_cursor = -1; } + virtual int GetMenuSelection(void); + virtual void SetMenuSelection(int selection); +}; + +// copy of CMenuMultiChoiceTriggered except for m_options +class CMenuMultiChoiceTwoLinesTriggered : public CMenuMultiChoiceTwoLines +{ +public: + typedef void (*Trigger)(CMenuMultiChoiceTwoLinesTriggered *); + + Trigger m_triggers[NUM_MULTICHOICE_OPTIONS]; + Trigger m_defaultCancel; + + CMenuMultiChoiceTwoLinesTriggered(void) { Initialise(); } + + void Initialise(void); + CPlaceableShOptionTwoLines *AddOption(wchar *text, float positionX, float positionY, Trigger trigger, bool bSelected, bool bRightJustify); + CPlaceableShOptionTwoLines *AddOption(wchar *text1, float positionX1, float positionY1, wchar *text2, float positionX2, float positionY2, Trigger trigger, bool bSelected, bool bRightJustify); + + virtual void SelectCurrentOptionUnderCursor(void); + virtual void SelectDefaultCancelAction(void); +}; + + +class CMenuOnOff : public CMenuBase +{ +public: + CPlaceableShOption m_title; + CPlaceableShText m_options[2]; + bool m_bActive; + bool m_bSetTextScale; + bool m_bSetTitleTextScale; + CVector2D m_textScale; + CVector2D m_oldTextScale; + int m_type; // 0: on/off 1: yes/no + + void SetColors(const CRGBA &title, const CRGBA &options); + void SetNewOldTextScale(bool bTextScale, const CVector2D &newScale, const CVector2D &oldScale, bool bTitleTextScale); + void SetOptionPosition(float x, float y, bool bRightJustify); + void AddTitle(wchar *text, bool bSelected, float positionX, float positionY, bool bRightJustify); + + virtual void Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y); + virtual void DrawNormal(float x, float y); + virtual void DrawHighlighted(const CRGBA &titleHighlight, float x, float y); + virtual void SetAlpha(uint8 alpha); + virtual void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset); + virtual bool GoNext(void) { DeactivateMenu(); return false; } + virtual bool GoPrev(void) { DeactivateMenu(); return false; } + virtual bool GoDown(void) { return GoNext(); } + virtual bool GoUp(void) { return GoPrev(); } + virtual bool GoDownStill(void) { return false; } + virtual bool GoUpStill(void) { return false; } + virtual bool GoLeft(void) { SelectCurrentOptionUnderCursor(); return true; } + virtual bool GoRight(void) { SelectCurrentOptionUnderCursor(); return true; } + virtual bool GoLeftStill(void) { return true; } + virtual bool GoRightStill(void) { return true; } + virtual bool GoFirst(void) { ActivateMenu(true); return true; } + virtual bool GoLast(void) { ActivateMenu(true); return true; } + virtual void SelectCurrentOptionUnderCursor(void) { m_title.m_bSelected ^= 1; } + virtual void SelectDefaultCancelAction(void) {} + virtual void ActivateMenu(bool first) { m_bActive = true; } + virtual void DeactivateMenu(void) { m_bActive = false; } + virtual int GetMenuSelection(void) { return m_title.m_bSelected; } + virtual void SetMenuSelection(int selection) { m_title.m_bSelected = selection; } +}; + +class CMenuOnOffTriggered : public CMenuOnOff +{ +public: + typedef void (*Trigger)(CMenuOnOffTriggered *); + + Trigger m_trigger; + + void SetOptionPosition(float x, float y, Trigger trigger, bool bRightJustify); + + virtual void SelectCurrentOptionUnderCursor(void); +}; + +class CMenuSlider : public CMenuBase +{ +public: + CPlaceableShText m_title; + CPlaceableShText m_box; // not really a text + CRGBA m_colors[2]; // left and right + CVector2D m_size[2]; // left and right + int m_value; + CPlaceableShText m_percentageText; + bool m_bDrawPercentage; +// char field_8D; +// char field_8E; +// char field_8F; + uint8 m_someAlpha; +// char field_91; +// char field_92; +// char field_93; + bool m_bActive; + int m_style; + + static char Buf8[8]; + static wchar Buf16[8]; + + CMenuSlider(void) + : m_value(0), m_bDrawPercentage(false), m_bActive(false), m_style(0) + { + AddTickBox(0.0f, 0.0f, 100.0f, 10.0f, 10.0f); //todo + } + + void SetColors(const CRGBA &title, const CRGBA &percentage, const CRGBA &left, const CRGBA &right); + void DrawTicks(const CVector2D &position, const CVector2D &size, float heightRight, float level, const CRGBA &leftCol, const CRGBA &selCol, const CRGBA &rightCol, bool bShadow, const CVector2D &shadowOffset, const CRGBA &shadowColor); + void DrawTicks(const CVector2D &position, const CVector2D &size, float heightRight, float level, const CRGBA &leftCol, const CRGBA &rightCol, bool bShadow, const CVector2D &shadowOffset, const CRGBA &shadowColor); + void AddTickBox(float positionX, float positionY, float width, float heigthLeft, float heightRight); + void AddTitle(wchar *text, float positionX, float positionY); + + virtual void Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y); + virtual void DrawNormal(float x, float y); + virtual void DrawHighlighted(const CRGBA &titleHighlight, float x, float y); + virtual void SetAlpha(uint8 alpha); + virtual void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset); + virtual bool GoNext(void) { DeactivateMenu(); return false; } + virtual bool GoPrev(void) { DeactivateMenu(); return false; } + virtual bool GoDown(void) { return GoNext(); } + virtual bool GoUp(void) { return GoPrev(); } + virtual bool GoDownStill(void) { return false; } + virtual bool GoUpStill(void) { return false; } + virtual bool GoLeft(void) { if(m_value < 0) m_value = 0; return true; } + virtual bool GoRight(void) { if(m_value > 1000) m_value = 1000; return true; } + virtual bool GoLeftStill(void) { m_value -= 8; if(m_value < 0) m_value = 0; return true; } + virtual bool GoRightStill(void) { m_value += 8; if(m_value > 1000) m_value = 1000; return true; } + virtual bool GoFirst(void) { ActivateMenu(true); return true; } + virtual bool GoLast(void) { ActivateMenu(true); return true; } + virtual void SelectCurrentOptionUnderCursor(void) {} + virtual void SelectDefaultCancelAction(void) {} + virtual void ActivateMenu(bool first) { m_bActive = true; } + virtual void DeactivateMenu(void) { m_bActive = false; } + virtual int GetMenuSelection(void) { return m_value/10; } + virtual void SetMenuSelection(int selection) { m_value = selection*10; } +}; + +class CMenuSliderTriggered : public CMenuSlider +{ +public: + typedef void (*Trigger)(CMenuSliderTriggered *); + + Trigger m_trigger; + Trigger m_alwaysTrigger; + + CMenuSliderTriggered(void) + : m_trigger(nil), m_alwaysTrigger(nil) {} + + void AddTickBox(float positionX, float positionY, float width, float heigthLeft, float heightRight, Trigger trigger, Trigger alwaysTrigger); + + virtual void Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y); + virtual bool GoLeft(void); + virtual bool GoRight(void); + virtual bool GoLeftStill(void); + virtual bool GoRightStill(void); +}; + + +class CMenuLineLister : public CMenuBase +{ +public: + float m_width; + float m_height; + int m_numLines; + CPlaceableShText m_linesLeft[NUM_LINELISTER_LINES_TOTAL]; + CPlaceableShText m_linesRight[NUM_LINELISTER_LINES_TOTAL]; + uint8 m_lineAlphas[NUM_LINELISTER_LINES_TOTAL]; + int8 m_lineFade[NUM_LINELISTER_LINES_TOTAL]; + float m_scrollPosition; + float m_scrollSpeed; + int field_10E8; + float m_lineSpacing; + + CMenuLineLister(void); + + void SetLinesColor(const CRGBA &color); + void ResetNumberOfTextLines(void); + bool AddTextLine(wchar *left, wchar *right); + + CPlaceableShText *GetLeftLine(int i) { return &m_linesLeft[(i%NUM_LINELISTER_LINES) + 15]; }; + CPlaceableShText *GetRightLine(int i) { return &m_linesRight[(i%NUM_LINELISTER_LINES) + 15]; }; + + virtual void Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y); + virtual void DrawNormal(float x, float y) { Draw(CRGBA(0,0,0,0), CRGBA(0,0,0,0), x, y); } + virtual void DrawHighlighted(const CRGBA &titleHighlight, float x, float y) { Draw(CRGBA(0,0,0,0), CRGBA(0,0,0,0), x, y); } + virtual void SetAlpha(uint8 alpha); + virtual void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset); + virtual bool GoNext(void) { return false; } + virtual bool GoPrev(void) { return false; } + virtual bool GoDown(void) { return GoNext(); } + virtual bool GoUp(void) { return GoPrev(); } + virtual bool GoDownStill(void) { m_scrollSpeed = 0.0f; return true; } + virtual bool GoUpStill(void) { m_scrollSpeed *= 6.0f; return true; } + virtual bool GoLeft(void) { return true; } + virtual bool GoRight(void) { return true; } + virtual bool GoLeftStill(void) { return true; } + virtual bool GoRightStill(void) { return true; } + virtual bool GoFirst(void) { return true; } + virtual bool GoLast(void) { return true; } + virtual void SelectCurrentOptionUnderCursor(void) {} + virtual void SelectDefaultCancelAction(void) {} + virtual void ActivateMenu(bool first) {} + virtual void DeactivateMenu(void) {} + virtual int GetMenuSelection(void) { return -1; } + virtual void SetMenuSelection(int selection) {} +}; + +class CMenuPage +{ +public: + CMenuBase *m_controls[NUM_PAGE_WIDGETS]; + int m_numControls; + CMenuBase *m_pCurrentControl; + int m_cursor; + + CMenuPage(void) { Initialise(); } + void Initialise(void); + bool AddMenu(CMenuBase *widget); + + bool IsActiveMenuTwoState(void); + void ActiveMenuTwoState_SelectNextPosition(void); + void Draw(const CRGBA &,const CRGBA &, float, float); + void DrawHighlighted(const CRGBA &titleHighlight, float x, float y); + void DrawNormal(float x, float y); + void ActivatePage(void); + void SetAlpha(uint8 alpha); + void SetShadows(bool, const CRGBA &, const CVector2D &); + void GoPrev(void) { if(m_pCurrentControl) { if(!m_pCurrentControl->GoPrev()) m_pCurrentControl->GoLast(); } } + void GoNext(void) { if(m_pCurrentControl) { if(!m_pCurrentControl->GoNext()) m_pCurrentControl->GoFirst(); } } + void GoLeft(void) { if(m_pCurrentControl) { if(!m_pCurrentControl->GoLeft()) m_pCurrentControl->GoLast(); } } + void GoRight(void) { if(m_pCurrentControl) { if(!m_pCurrentControl->GoRight()) m_pCurrentControl->GoFirst(); } } + void GoUp(void) { if(m_pCurrentControl) { if(!m_pCurrentControl->GoUp()) m_pCurrentControl->GoLast(); } } + void GoDown(void) { if(m_pCurrentControl) { if(!m_pCurrentControl->GoDown()) m_pCurrentControl->GoFirst(); } } + void GoLeftStill(void) { if(m_pCurrentControl) m_pCurrentControl->GoLeftStill(); } + void GoRightStill(void) { if(m_pCurrentControl) m_pCurrentControl->GoRightStill(); } + void GoUpStill(void) { if(m_pCurrentControl) m_pCurrentControl->GoUpStill(); } + void GoDownStill(void) { if(m_pCurrentControl) m_pCurrentControl->GoDownStill(); } + void SelectDefaultCancelAction(void) { if(m_pCurrentControl) m_pCurrentControl->SelectDefaultCancelAction(); } + void SelectCurrentOptionUnderCursor(void) { if(m_pCurrentControl) m_pCurrentControl->SelectCurrentOptionUnderCursor(); } + + virtual void GoUpMenuOnPage(void); + virtual void GoDownMenuOnPage(void); + virtual void GoLeftMenuOnPage(void); + virtual void GoRightMenuOnPage(void); +}; + +class CMenuPageAnyMove : public CMenuPage +{ +public: + FEC_MOVETAB m_moveTab[NUM_PAGE_WIDGETS]; + + CMenuPageAnyMove(void) { Initialise(); } + void Initialise(void); + using CMenuPage::AddMenu; + bool AddMenu(CMenuBase *widget, FEC_MOVETAB *moveTab); + + virtual void GoUpMenuOnPage(void); + virtual void GoDownMenuOnPage(void); + virtual void GoLeftMenuOnPage(void); + virtual void GoRightMenuOnPage(void); +}; \ No newline at end of file diff --git a/src/miami/core/Frontend.cpp b/src/miami/core/Frontend.cpp new file mode 100644 index 00000000..8f9d8e0e --- /dev/null +++ b/src/miami/core/Frontend.cpp @@ -0,0 +1,6755 @@ +#define FORCE_PC_SCALING +#define WITHWINDOWS +#define WITHDINPUT +#include "common.h" +#ifndef PS2_MENU +#include "crossplatform.h" +#include "platform.h" +#include "Frontend.h" +#include "Font.h" +#include "Pad.h" +#include "Text.h" +#include "main.h" +#include "RwHelper.h" +#include "Timer.h" +#include "Game.h" +#include "DMAudio.h" +#include "FileMgr.h" +#include "Streaming.h" +#include "TxdStore.h" +#include "General.h" +#include "GenericGameStorage.h" +#include "Script.h" +#include "Camera.h" +#include "ControllerConfig.h" +#include "Vehicle.h" +#include "MBlur.h" +#include "PlayerSkin.h" +#include "PlayerInfo.h" +#include "World.h" +#include "Renderer.h" +#include "CdStream.h" +#include "Radar.h" +#include "Stats.h" +#include "Messages.h" +#include "FileLoader.h" +#include "User.h" +#include "sampman.h" +#include "../skel/dc/dc.h" + +// Similar story to Hud.cpp: +// Game has colors inlined in code. +// For easier modification we collect them here: +const CRGBA LABEL_COLOR(255, 150, 225, 255); +const CRGBA SELECTIONBORDER_COLOR(25, 130, 70, 255); +const CRGBA MENUOPTION_COLOR = LABEL_COLOR; +const CRGBA SELECTEDMENUOPTION_COLOR = LABEL_COLOR; +const CRGBA HEADER_COLOR = LABEL_COLOR; +const CRGBA DARKMENUOPTION_COLOR(195, 90, 165, 255); +const CRGBA SLIDERON_COLOR(97, 194, 247, 255); +const CRGBA SLIDEROFF_COLOR(27, 89, 130, 255); +const CRGBA LIST_BACKGROUND_COLOR(49, 101, 148, 130); +const CRGBA LIST_OPTION_COLOR(155, 155, 155, 255); +const CRGBA RADIO_SELECTOR_COLOR = SLIDEROFF_COLOR; +const CRGBA INACTIVE_RADIO_COLOR(100, 100, 255, 100); +const CRGBA SCROLLBAR_COLOR = LABEL_COLOR; + +#define MAP_MIN_SIZE 162.f +#define MAP_SIZE_TO_ALLOW_X_MOVE 297.f + +#define MAX_VISIBLE_LIST_ROW 30 +#define SCROLLBAR_MAX_HEIGHT 263.0f // not in end result +#define SCROLLABLE_PAGES + +#define hasNativeList(screen) (screen == MENUPAGE_SKIN_SELECT || screen == MENUPAGE_KEYBOARD_CONTROLS) + +#ifdef SCROLLABLE_PAGES +#define MAX_VISIBLE_OPTION 12 +#define MAX_VISIBLE_OPTION_ON_SCREEN (hasNativeList(m_nCurrScreen) ? MAX_VISIBLE_LIST_ROW : MAX_VISIBLE_OPTION) +#define SCREEN_HAS_AUTO_SCROLLBAR (m_nTotalListRow > MAX_VISIBLE_OPTION && !hasNativeList(m_nCurrScreen)) + +int GetOptionCount(int screen) +{ + int i = 0; + for (; i < NUM_MENUROWS && aScreens[screen].m_aEntries[i].m_Action != MENUACTION_NOTHING; i++); + return i; +} + +#define SETUP_SCROLLING(screen) \ + if (!hasNativeList(screen)) { \ + m_nTotalListRow = GetOptionCount(screen); \ + if (m_nTotalListRow > MAX_VISIBLE_OPTION) { \ + m_nSelectedListRow = 0; \ + m_nFirstVisibleRowOnList = 0; \ + m_nScrollbarTopMargin = 0; \ + } \ + } + +#define MINUS_SCROLL_OFFSET - scrollOffset +#else +#define MAX_VISIBLE_OPTION_ON_SCREEN MAX_VISIBLE_LIST_ROW +#define SETUP_SCROLLING(screen) +#define MINUS_SCROLL_OFFSET +#endif + +#ifdef TRIANGLE_BACK_BUTTON +#define GetBackJustUp GetTriangleJustUp +#define GetBackJustDown GetTriangleJustDown +#elif defined(CIRCLE_BACK_BUTTON) +#define GetBackJustUp GetCircleJustUp +#define GetBackJustDown GetCircleJustDown +#else +#define GetBackJustUp GetSquareJustUp +#define GetBackJustDown GetSquareJustDown +#endif + +#ifdef MAP_ENHANCEMENTS +CVector2D mapCrosshair; +#endif + +#ifdef CUTSCENE_BORDERS_SWITCH +bool CMenuManager::m_PrefsCutsceneBorders = true; +#endif + +bool holdingScrollBar; // *(bool*)0x7039B9; // not original name + +CMenuManager FrontEndMenuManager; +MenuTrapezoid menuBg(CGeneral::GetRandomNumber() % 40 + 65, CGeneral::GetRandomNumber() % 40 + 21, + CGeneral::GetRandomNumber() % 40 + 568, CGeneral::GetRandomNumber() % 40 + 44, + CGeneral::GetRandomNumber() % 40 + 36, CGeneral::GetRandomNumber() % 40 + 352, + CGeneral::GetRandomNumber() % 40 + 593, CGeneral::GetRandomNumber() % 40 + 312); + +MenuTrapezoid menuOptionHighlight(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); + +#ifndef MASTER +bool CMenuManager::m_PrefsMarketing = false; +bool CMenuManager::m_PrefsDisableTutorials = false; +#endif // !MASTER + +#ifdef GAMEPAD_MENU +uint32 TimeToStopPadShaking; +#endif + +const char* FrontendFilenames[][2] = { + {"background", ""}, + {"vc_logo", "vc_logom"}, + {"mouse", "mousea"}, + {"mapTop01", "mapTop01A"}, + {"mapTop02", "mapTop02A"}, + {"mapTop03", "mapTop03A"}, + {"mapMid01", "mapMid01A"}, + {"mapMid02", "mapMid02A"}, + {"mapMid03", "mapMid03A"}, + {"mapBot01", "mapBot01A"}, + {"mapBot02", "mapBot02A"}, + {"mapBot03", "mapBot03A"}, + {"wildstyle", "wildstyleA"}, + {"flash", "flashA"}, + {"kchat", "kchatA"}, + {"fever", "feverA"}, + {"vrock", "vrockA"}, + {"vcpr", "vcprA"}, + {"espantoso", "espantosoA"}, + {"emotion", "emotionA"}, + {"wave103", "wave103A"}, + {"mp3", "mp3A"}, + {"downOff", "buttonA"}, + {"downOn", "buttonA"}, + {"upOff", "buttonA"}, + {"upOn", "buttonA"}, +#ifdef GAMEPAD_MENU + {"fe_controller", "" }, + {"fe_arrows1", "" }, + {"fe_arrows2", "" }, + {"fe_arrows3", "" }, + {"fe_arrows4", "" }, +#endif +}; + +#define MENU_X_RIGHT_ALIGNED(x) SCALE_AND_CENTER_X(DEFAULT_SCREEN_WIDTH - (x)) + +#ifdef ASPECT_RATIO_SCALE +// All of the defines below replace the StretchX function. Otherwise use SCREEN_SCALE_X. +#define MENU_X_LEFT_ALIGNED(x) SCALE_AND_CENTER_X(x) +#define MENU_X(x) SCREEN_SCALE_X(x) +#define MENU_Y(y) SCREEN_SCALE_Y(y) +#else +#define MENU_X_LEFT_ALIGNED(x) StretchX(x) +#define MENU_X(x) StretchX(x) +#define MENU_Y(y) StretchY(y) +#endif + +#ifdef XBOX_MESSAGE_SCREEN +bool CMenuManager::m_bDialogOpen = false; +uint32 CMenuManager::m_nDialogHideTimer = 0; +uint32 CMenuManager::m_nDialogHideTimerPauseMode = 0; +bool CMenuManager::m_bSaveWasSuccessful = false; +wchar* CMenuManager::m_pDialogText = nil; +#endif + +#define SET_FONT_FOR_MENU_HEADER \ + CFont::SetRightJustifyOn(); \ + CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); \ + CFont::SetScale(MENU_X(MENUHEADER_WIDTH), MENU_Y(MENUHEADER_HEIGHT)); \ + CFont::SetDropShadowPosition(0); + +#define SET_FONT_FOR_LIST_ITEM \ + CFont::SetRightJustifyOff(); \ + CFont::SetScale(MENU_X(LISTITEM_X_SCALE), MENU_Y(LISTITEM_Y_SCALE)); \ + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); + +#define RESET_FONT_FOR_NEW_PAGE \ + CFont::SetBackgroundOff(); \ + CFont::SetScale(MENU_X(MENUACTION_SCALE_MULT), MENU_Y(MENUACTION_SCALE_MULT)); \ + CFont::SetPropOn(); \ + CFont::SetCentreOff(); \ + CFont::SetJustifyOn(); \ + CFont::SetRightJustifyOff(); \ + CFont::SetBackGroundOnlyTextOn(); \ + CFont::SetWrapx(MENU_X_RIGHT_ALIGNED(MENU_X_MARGIN)); \ + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(MENU_X_MARGIN)); + +// value must be between 0.0-1.0 +#define ProcessSlider(value, origY, increaseAction, decreaseAction, hoverEndX, onlyWhenHoveringRow) \ + do { \ + float y = origY MINUS_SCROLL_OFFSET; \ + lastActiveBarX = DisplaySlider(MENU_X_LEFT_ALIGNED(MENUSLIDER_X), MENU_Y(y), MENU_Y(MENUSLIDER_SMALLEST_BAR), MENU_Y(MENUSLIDER_BIGGEST_BAR), MENU_X(MENUSLIDER_UNK), value, MENU_X(3.0f)); \ + if (i != m_nCurrOption || !itemsAreSelectable) \ + break; \ + \ + if (CheckHover(0, lastActiveBarX - MENU_X(3.0f), MENU_Y(y), MENU_Y(MENUSLIDER_BIGGEST_BAR + y))) { \ + m_nHoverOption = decreaseAction; \ + break; \ + } \ + if (!CheckHover(MENU_X(3.0f) + lastActiveBarX, hoverEndX, MENU_Y(y), MENU_Y(MENUSLIDER_BIGGEST_BAR + y))) { \ + m_nHoverOption = HOVEROPTION_NOT_HOVERING; \ + break; \ + } \ + m_nHoverOption = increaseAction; \ + if (m_nMousePosX < MENU_X_LEFT_ALIGNED(MENUSLIDER_X)) \ + m_nHoverOption = HOVEROPTION_NOT_HOVERING; \ + \ + if (onlyWhenHoveringRow && (m_nMousePosY < MENU_Y(y) || m_nMousePosY > MENU_Y(MENUSLIDER_BIGGEST_BAR + y))) \ + m_nHoverOption = HOVEROPTION_NOT_HOVERING; \ + } while(0) + +// --- Functions not in the game/inlined starts + +inline void +CMenuManager::ScrollUpListByOne() +{ + if (m_nSelectedListRow == m_nFirstVisibleRowOnList) { + if (m_nFirstVisibleRowOnList > 0) { + m_nSelectedListRow--; + m_nFirstVisibleRowOnList--; + m_nScrollbarTopMargin -= SCROLLBAR_MAX_HEIGHT / m_nTotalListRow; + } + } else { + m_nSelectedListRow--; + } +} + +inline void +CMenuManager::ScrollDownListByOne() +{ + if (m_nSelectedListRow == m_nFirstVisibleRowOnList + MAX_VISIBLE_OPTION_ON_SCREEN - 1) { + if (m_nFirstVisibleRowOnList < m_nTotalListRow - MAX_VISIBLE_OPTION_ON_SCREEN) { + m_nSelectedListRow++; + m_nFirstVisibleRowOnList++; + m_nScrollbarTopMargin += SCROLLBAR_MAX_HEIGHT / m_nTotalListRow; + } + } else { + if (m_nSelectedListRow < m_nTotalListRow - 1) { + m_nSelectedListRow++; + } + } +} + +inline void +CMenuManager::PageUpList(bool playSoundOnSuccess) +{ + if (m_nTotalListRow > MAX_VISIBLE_OPTION_ON_SCREEN) { + if (m_nFirstVisibleRowOnList > 0) { + if(playSoundOnSuccess) + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); + + m_nFirstVisibleRowOnList = Max(0, m_nFirstVisibleRowOnList - MAX_VISIBLE_OPTION_ON_SCREEN); + m_nSelectedListRow = Min(m_nSelectedListRow, m_nFirstVisibleRowOnList + MAX_VISIBLE_OPTION_ON_SCREEN - 1); + } else { + m_nFirstVisibleRowOnList = 0; + m_nSelectedListRow = 0; + } + m_nScrollbarTopMargin = (SCROLLBAR_MAX_HEIGHT / m_nTotalListRow) * m_nFirstVisibleRowOnList; + } +} + +inline void +CMenuManager::PageDownList(bool playSoundOnSuccess) +{ + if (m_nTotalListRow > MAX_VISIBLE_OPTION_ON_SCREEN) { + if (m_nFirstVisibleRowOnList < m_nTotalListRow - MAX_VISIBLE_OPTION_ON_SCREEN) { + if(playSoundOnSuccess) + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); + + m_nFirstVisibleRowOnList = Min(m_nFirstVisibleRowOnList + MAX_VISIBLE_OPTION_ON_SCREEN, m_nTotalListRow - MAX_VISIBLE_OPTION_ON_SCREEN); + m_nSelectedListRow = Max(m_nSelectedListRow, m_nFirstVisibleRowOnList); + } else { + m_nFirstVisibleRowOnList = m_nTotalListRow - MAX_VISIBLE_OPTION_ON_SCREEN; + m_nSelectedListRow = m_nTotalListRow - 1; + } + m_nScrollbarTopMargin = (SCROLLBAR_MAX_HEIGHT / m_nTotalListRow) * m_nFirstVisibleRowOnList; + } +} + +#ifdef CUSTOM_FRONTEND_OPTIONS +#define PLUS_LINE_HEIGHT_ON_SCREEN + (aScreens[m_nCurrScreen].layout ? aScreens[m_nCurrScreen].layout->lineHeight : MENU_DEFAULT_LINE_HEIGHT) +bool ScreenHasOption(int screen, const char* gxtKey) +{ + for (int i = 0; i < NUM_MENUROWS; i++) { + if (strcmp(gxtKey, aScreens[screen].m_aEntries[i].m_EntryName) == 0) + return true; + } + return false; +} + +inline void +CMenuManager::ThingsToDoBeforeLeavingPage() +{ + if ((m_nCurrScreen == MENUPAGE_SKIN_SELECT) && strcmp(m_aSkinName, m_PrefsSkinFile) != 0) { + CWorld::Players[0].SetPlayerSkin(m_PrefsSkinFile); + + } else if (m_nCurrScreen == MENUPAGE_SOUND_SETTINGS) { + if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) + m_nPrefsAudio3DProviderIndex = DMAudio.GetCurrent3DProviderIndex(); + + DMAudio.StopFrontEndTrack(); + OutputDebugString("FRONTEND AUDIO TRACK STOPPED"); + + } else if (ScreenHasOption(m_nCurrScreen, "FED_RES")) { + m_nDisplayVideoMode = m_nPrefsVideoMode; + } + + if (m_nCurrScreen == MENUPAGE_SKIN_SELECT) { + CPlayerSkin::EndFrontendSkinEdit(); + } + +#ifdef SCROLLABLE_PAGES + if (SCREEN_HAS_AUTO_SCROLLBAR) { + m_nSelectedListRow = 0; + m_nFirstVisibleRowOnList = 0; + m_nScrollbarTopMargin = 0; + } +#endif + + CMenuScreenCustom::CMenuEntry &option = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption]; + + if (option.m_Action == MENUACTION_CFO_DYNAMIC) + if(option.m_CFODynamic->buttonPressFunc) + option.m_CFODynamic->buttonPressFunc(FEOPTION_ACTION_FOCUSLOSS); + + if (option.m_Action == MENUACTION_CFO_SELECT && option.m_CFOSelect->onlyApplyOnEnter && option.m_CFOSelect->lastSavedValue != option.m_CFOSelect->displayedValue) + option.m_CFOSelect->displayedValue = *(int8*)option.m_CFO->value = option.m_CFOSelect->lastSavedValue; + + if (aScreens[m_nCurrScreen].returnPrevPageFunc) { + aScreens[m_nCurrScreen].returnPrevPageFunc(); + } +} + +inline int8 +CMenuManager::GetPreviousPageOption() +{ + int8 prevPage = !m_bGameNotLoaded ? aScreens[m_nCurrScreen].m_PreviousPage : + (m_nCurrScreen == MENUPAGE_NEW_GAME || m_nCurrScreen == MENUPAGE_OPTIONS || m_nCurrScreen == MENUPAGE_EXIT ? MENUPAGE_START_MENU : aScreens[m_nCurrScreen].m_PreviousPage); + + if (prevPage == -1) // Game also does same + return 0; + + prevPage = prevPage == MENUPAGE_NONE ? (!m_bGameNotLoaded ? MENUPAGE_PAUSE_MENU : MENUPAGE_START_MENU) : prevPage; + + for (int i = 0; i < NUM_MENUROWS; i++) { + if (aScreens[prevPage].m_aEntries[i].m_Action >= MENUACTION_NOTHING) { // CFO check + if (aScreens[prevPage].m_aEntries[i].m_TargetMenu == m_nCurrScreen) { + return i; + } + } + } + + // This shouldn't happen + return 0; +} + +#else +#define PLUS_LINE_HEIGHT_ON_SCREEN + MENU_DEFAULT_LINE_HEIGHT +inline void +CMenuManager::ThingsToDoBeforeLeavingPage() +{ + switch (m_nCurrScreen) { + case MENUPAGE_SOUND_SETTINGS: + if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) + m_nPrefsAudio3DProviderIndex = DMAudio.GetCurrent3DProviderIndex(); + + DMAudio.StopFrontEndTrack(); + OutputDebugString("FRONTEND AUDIO TRACK STOPPED"); + break; + case MENUPAGE_DISPLAY_SETTINGS: + m_nDisplayVideoMode = m_nPrefsVideoMode; + break; + case MENUPAGE_SKIN_SELECT: + if (strcmp(m_aSkinName, m_PrefsSkinFile) != 0) + CWorld::Players[0].SetPlayerSkin(m_PrefsSkinFile); + + CPlayerSkin::EndFrontendSkinEdit(); + break; + } + +#ifdef SCROLLABLE_PAGES + if (SCREEN_HAS_AUTO_SCROLLBAR) { + m_nSelectedListRow = 0; + m_nFirstVisibleRowOnList = 0; + m_nScrollbarTopMargin = 0; + } +#endif +} + +inline int8 +CMenuManager::GetPreviousPageOption() +{ + return (!m_bGameNotLoaded ? aScreens[m_nCurrScreen].m_ParentEntry : + (m_nCurrScreen == MENUPAGE_NEW_GAME ? 0 : (m_nCurrScreen == MENUPAGE_OPTIONS ? 1 : (m_nCurrScreen == MENUPAGE_EXIT ? 2 : aScreens[m_nCurrScreen].m_ParentEntry)))); +} +#endif + +// ------ Functions not in the game/inlined ends + +bool DoRWStuffStartOfFrame(int16 TopRed, int16 TopGreen, int16 TopBlue, int16 BottomRed, int16 BottomGreen, int16 BottomBlue, int16 Alpha); +void DoRWStuffEndOfFrame(void); + +void +CMenuManager::SwitchToNewScreen(int8 screen) +{ + bMenuChangeOngoing = true; + DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); + DrawBackground(true); + DoRWStuffEndOfFrame(); + DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); + DrawBackground(true); + DoRWStuffEndOfFrame(); + m_nPrevScreen = m_nCurrScreen; + m_ShowEmptyBindingError = false; + ResetHelperText(); + + ThingsToDoBeforeLeavingPage(); + + if (screen == -2) { + int oldScreen = aScreens[m_nCurrScreen].m_PreviousPage; + int oldOption = GetPreviousPageOption(); + + m_nCurrOption = oldOption; + m_nCurrScreen = oldScreen; + } else if (screen == 0) { + m_nCurrScreen = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_TargetMenu; + m_nCurrOption = 0; + } else { + m_nCurrOption = 0; + m_nCurrScreen = screen; + } + SETUP_SCROLLING(m_nCurrScreen) + + if (hasNativeList(m_nPrevScreen)) + m_nTotalListRow = 0; + + if (m_nCurrScreen == MENUPAGE_CHOOSE_SAVE_SLOT) + m_nCurrOption = 8; + m_nMenuFadeAlpha = 0; + m_nOptionHighlightTransitionBlend = 0; + m_LastScreenSwitch = CTimer::GetTimeInMillisecondsPauseMode(); +} + +CMenuManager::CMenuManager() +{ + m_StatsScrollSpeed = 150.0f; + m_StatsScrollDirection = 1; + m_PrefsSfxVolume = 49; + m_PrefsMusicVolume = 49; + m_PrefsRadioStation = 0; + m_PrefsStereoMono = 1; + m_PrefsBrightness = 256; + m_PrefsLOD = CRenderer::ms_lodDistScale; + m_KeyPressedCode = -1; + m_bFrontEnd_ReloadObrTxtGxt = false; + m_PrefsMP3BoostVolume = 0; + m_PrefsShowSubtitles = 0; + m_PrefsShowLegends = 1; +#ifdef ASPECT_RATIO_SCALE + m_PrefsUseWideScreen = AR_AUTO; +#else + m_PrefsUseWideScreen = 0; +#endif + m_PrefsVsync = 0; + m_PrefsVsyncDisp = 1; + m_PrefsFrameLimiter = 1; + m_PrefsLanguage = 0; + field_54 = 0; + m_PrefsAllowNastyGame = 1; + m_PrefsSpeakers = 0; + field_8 = 0; + m_PrefsUseVibration = 0; + m_PrefsShowHud = 1; + m_PrefsRadarMode = 0; + m_DisplayControllerOnFoot = false; + m_bShutDownFrontEndRequested = false; + m_bStartUpFrontEndRequested = false; + pEditString = nil; + pControlEdit = nil; + DisplayComboButtonErrMsg = false; + m_PrefsDMA = 1; + OS_Language = LANG_ENGLISH; + m_ControlMethod = CONTROL_STANDARD; +#ifdef PC_PLAYER_CONTROLS + CCamera::m_bUseMouse3rdPerson = true; +#else + CCamera::m_bUseMouse3rdPerson = false; +#endif + m_lastWorking3DAudioProvider = 0; + m_nFirstVisibleRowOnList = 0; + m_nScrollbarTopMargin = 0.0f; + m_nSelectedListRow = 0; + m_nSkinsTotal = 0; + m_nPrefsAudio3DProviderIndex = AUDIO_PROVIDER_NOT_DETERMINED; + m_bGameNotLoaded = true; + m_nMousePosX = m_nMouseTempPosX; + m_nMousePosY = m_nMouseTempPosY; + m_nMouseOldPosX = m_nMousePosX; + m_nMouseOldPosY = m_nMousePosY; + m_bShowMouse = true; + m_nHoverOption = HOVEROPTION_NOT_HOVERING; + + DMAudio.SetMP3BoostVolume(m_PrefsMP3BoostVolume); + m_bMenuActive = false; + m_bActivateSaveMenu = false; + m_bWantToLoad = false; + m_nMenuFadeAlpha = 0; + m_OnlySaveMenu = false; + m_fMapSize = MENU_Y(162.0f); // Y because of HOR+ + m_fMapCenterX = MENU_X_LEFT_ALIGNED(320.0f); + m_fMapCenterY = MENU_Y(225.0f); + DMAudio.SetMusicMasterVolume(m_PrefsMusicVolume); + DMAudio.SetEffectsMasterVolume(m_PrefsSfxVolume); + +#ifdef NO_ISLAND_LOADING + m_PrefsIslandLoading = ISLAND_LOADING_LOW; +#endif + +#ifdef GAMEPAD_MENU +#ifdef __SWITCH__ + m_PrefsControllerType = CONTROLLER_NINTENDO_SWITCH; +#else + m_PrefsControllerType = CONTROLLER_XBOXONE; +#endif +#endif + +#ifdef MISSION_REPLAY + m_bAttemptingMissionRetry = false; +#endif +} + +void +CMenuManager::SetFrontEndRenderStates(void) +{ + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); + RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); +} + +void +CMenuManager::Initialise(void) +{ + DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); + DoRWStuffEndOfFrame(); + m_AllowNavigation = false; + m_firstStartCounter = -50; // to start from black + m_nMenuFadeAlpha = 0; + m_nCurrOption = 0; + m_nOptionHighlightTransitionBlend = 0; + CentreMousePointer(); +#ifdef GTA_HANDHELD + m_bShowMouse = false; +#else + m_bShowMouse = true; +#endif + m_fMapSize = MENU_Y(162.0f); // Y because of HOR+ + m_fMapCenterX = MENU_X_LEFT_ALIGNED(320.0f); + m_fMapCenterY = MENU_Y(225.0f); + CPad::StopPadsShaking(); +#ifdef MISSION_REPLAY + if (!m_OnlySaveMenu) { + if (m_nCurrScreen == MENUPAGE_MISSION_RETRY && m_bAttemptingMissionRetry) + m_bAttemptingMissionRetry = false; + else + m_nCurrScreen = MENUPAGE_NONE; + } +#else + if (!m_OnlySaveMenu) + m_nCurrScreen = MENUPAGE_NONE; +#endif + + DMAudio.ChangeMusicMode(MUSICMODE_FRONTEND); + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_STARTING, 0); + DMAudio.Service(); + DMAudio.SetMusicMasterVolume(m_PrefsMusicVolume); + DMAudio.SetEffectsMasterVolume(m_PrefsSfxVolume); +#ifdef FIX_BUGS + static bool firstTime = true; + if (firstTime) { + DMAudio.SetRadioInCar(m_PrefsRadioStation); + firstTime = false; + } else +#endif + m_PrefsRadioStation = DMAudio.GetRadioInCar(); + + DMAudio.SetMP3BoostVolume(m_PrefsMP3BoostVolume); + if (DMAudio.IsMP3RadioChannelAvailable()) { + if (m_PrefsRadioStation < WILDSTYLE || m_PrefsRadioStation > USERTRACK) + m_PrefsRadioStation = CGeneral::GetRandomNumber() % (USERTRACK + 1); + } else if (m_PrefsRadioStation < WILDSTYLE || m_PrefsRadioStation > WAVE) + m_PrefsRadioStation = CGeneral::GetRandomNumber() % (WAVE + 1); + + CFileMgr::SetDir(""); + //CFileMgr::SetDir(""); + PcSaveHelper.PopulateSlotInfo(); + CTimer::StartUserPause(); +} + +void +CMenuManager::CentreMousePointer() +{ + if (SCREEN_WIDTH * 0.5f != 0.0f && 0.0f != SCREEN_HEIGHT * 0.5f) { +#if defined RW_D3D9 || defined RWLIBS + tagPOINT Point; + Point.x = SCREEN_WIDTH / 2; + Point.y = SCREEN_HEIGHT / 2; + ClientToScreen(PSGLOBAL(window), &Point); + SetCursorPos(Point.x, Point.y); +#elif defined RW_GL3 + glfwSetCursorPos(PSGLOBAL(window), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2); +#endif + + PSGLOBAL(lastMousePos.x) = SCREEN_WIDTH / 2; + PSGLOBAL(lastMousePos.y) = SCREEN_HEIGHT / 2; + } +} + +void +CMenuManager::CheckCodesForControls(int typeOfControl) +{ + DisplayComboButtonErrMsg = false; + bool invalidKey = false; + bool escPressed = false; + eControllerType typeToSave; + // GetStartOptionsCntrlConfigScreens(); + e_ControllerAction action = (e_ControllerAction) m_CurrCntrlAction; + + if (typeOfControl == KEYBOARD) { + if (*pControlEdit == rsESC) { + escPressed = true; + } else if (*pControlEdit != rsF1 && *pControlEdit != rsF2 && *pControlEdit != rsF3 && *pControlEdit != rsF9 && + *pControlEdit != rsLWIN && *pControlEdit != rsRWIN && *pControlEdit != rsRALT) { + typeToSave = KEYBOARD; + if (ControlsManager.GetControllerKeyAssociatedWithAction(action, KEYBOARD) != rsNULL && + *pControlEdit != ControlsManager.GetControllerKeyAssociatedWithAction(action, KEYBOARD)) { + typeToSave = OPTIONAL_EXTRA; + } + } else { + invalidKey = true; + } + } else if (typeOfControl == MOUSE) { + typeToSave = MOUSE; + } else if (typeOfControl == JOYSTICK) { + typeToSave = JOYSTICK; + if (ControlsManager.GetIsActionAButtonCombo(action)) + DisplayComboButtonErrMsg = true; + } + +#ifdef FIX_BUGS + if(!escPressed && !invalidKey) +#endif + ControlsManager.ClearSettingsAssociatedWithAction(action, typeToSave); + + if (!DisplayComboButtonErrMsg && !escPressed && !invalidKey) { + if (typeOfControl == KEYBOARD) { + ControlsManager.DeleteMatchingActionInitiators(action, *pControlEdit, KEYBOARD); + ControlsManager.DeleteMatchingActionInitiators(action, *pControlEdit, OPTIONAL_EXTRA); + } else if (typeOfControl == MOUSE) { + ControlsManager.DeleteMatchingActionInitiators(action, MouseButtonJustClicked, MOUSE); + } else if (typeOfControl == JOYSTICK) { + ControlsManager.DeleteMatchingActionInitiators(action, JoyButtonJustClicked, JOYSTICK); + } + + if (typeOfControl == KEYBOARD) { + ControlsManager.SetControllerKeyAssociatedWithAction(action, *pControlEdit, typeToSave); + } else if (typeOfControl == MOUSE) { + ControlsManager.SetControllerKeyAssociatedWithAction(action, MouseButtonJustClicked, typeToSave); + } else if (typeOfControl == JOYSTICK) { + ControlsManager.SetControllerKeyAssociatedWithAction(action, JoyButtonJustClicked, typeToSave); + } + pControlEdit = nil; + m_bWaitingForNewKeyBind = false; + m_KeyPressedCode = -1; + m_bStartWaitingForKeyBind = false; +#ifdef LOAD_INI_SETTINGS + SaveINIControllerSettings(); +#else + SaveSettings(); +#endif + } +} + +bool +CMenuManager::CheckHover(int x1, int x2, int y1, int y2) +{ + return m_nMousePosX > x1 && m_nMousePosX < x2 && + m_nMousePosY > y1 && m_nMousePosY < y2; +} + +void +CMenuManager::CheckSliderMovement(int value) +{ + switch (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action) { + case MENUACTION_BRIGHTNESS: +#ifdef FIX_BUGS + m_PrefsBrightness += value * (384 / MENUSLIDER_LOGICAL_BARS); +#else + m_PrefsBrightness += value * 24.19f; +#endif + m_PrefsBrightness = Clamp(m_PrefsBrightness, 0, 384); + break; + case MENUACTION_DRAWDIST: + if(value > 0) + m_PrefsLOD += ((1.8f - 0.925f) / MENUSLIDER_LOGICAL_BARS); + else + m_PrefsLOD -= ((1.8f - 0.925f) / MENUSLIDER_LOGICAL_BARS); + m_PrefsLOD = Clamp(m_PrefsLOD, 0.925f, 1.8f); + CRenderer::ms_lodDistScale = m_PrefsLOD; + break; + + // I wonder the idea behind clamping those max to 65 + case MENUACTION_MUSICVOLUME: + if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) { + m_PrefsMusicVolume += value * (64 / MENUSLIDER_LOGICAL_BARS); + m_PrefsMusicVolume = Clamp(m_PrefsMusicVolume, 0, 65); + DMAudio.SetMusicMasterVolume(m_PrefsMusicVolume); + } + break; + case MENUACTION_SFXVOLUME: + if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) { + m_PrefsSfxVolume += value * (64 / MENUSLIDER_LOGICAL_BARS); + m_PrefsSfxVolume = Clamp(m_PrefsSfxVolume, 0, 65); + DMAudio.SetEffectsMasterVolume(m_PrefsSfxVolume); + } + break; + case MENUACTION_MP3VOLUMEBOOST: + if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) { + if (DMAudio.IsMP3RadioChannelAvailable()) { + m_PrefsMP3BoostVolume += value * (64 / MENUSLIDER_LOGICAL_BARS); + m_PrefsMP3BoostVolume = Clamp(m_PrefsMP3BoostVolume, 0, 65); + DMAudio.SetMP3BoostVolume(m_PrefsMP3BoostVolume); + } + } + break; + case MENUACTION_MOUSESENS: + TheCamera.m_fMouseAccelHorzntl += value * 1.0f/200.0f/15.0f; // probably because diving it to 15 instead of 16(MENUSLIDER_LOGICAL_BARS) had more accurate steps + TheCamera.m_fMouseAccelHorzntl = Clamp(TheCamera.m_fMouseAccelHorzntl, 1.0f/3200.0f, 1.0f/200.0f); +#ifdef FIX_BUGS + TheCamera.m_fMouseAccelVertical = TheCamera.m_fMouseAccelHorzntl + 0.0005f; +#endif + break; +#ifdef CUSTOM_FRONTEND_OPTIONS + case MENUACTION_CFO_SLIDER: + { + CMenuScreenCustom::CMenuEntry &option = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption]; + float oldValue = *(float*)option.m_CFOSlider->value; + *(float*)option.m_CFOSlider->value += value * ((option.m_CFOSlider->max - option.m_CFOSlider->min) / MENUSLIDER_LOGICAL_BARS); + *(float*)option.m_CFOSlider->value = Clamp(*(float*)option.m_CFOSlider->value, option.m_CFOSlider->min, option.m_CFOSlider->max); + + if (*(float*)option.m_CFOSlider->value != oldValue && option.m_CFOSlider->changeFunc) + option.m_CFOSlider->changeFunc(oldValue, *(float*)option.m_CFOSlider->value); + + break; + } +#endif + default: + return; + } + SaveSettings(); +} + +void +CMenuManager::DisplayHelperText(char *text) +{ + if (m_nMenuFadeAlpha != 255) + return; + + // there was a unused static bool + static uint32 LastFlash = 0; + int32 alpha = 255; + + CFont::SetRightJustifyOn(); + CFont::SetScale(SCREEN_SCALE_X(SMALLESTTEXT_X_SCALE), SCREEN_SCALE_Y(SMALLESTTEXT_Y_SCALE)); + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); + CFont::SetDropShadowPosition(0); + + // We're using SCREEN_STRETCH_FROM_RIGHT, because we also stretch black borders + if (text) { + CFont::SetColor(CRGBA(255, 255, 255, 255)); + CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), TheText.Get(text)); + return; + } + + if (m_nHelperTextMsgId != 0 && m_nHelperTextMsgId != 1) { + if (CTimer::GetTimeInMillisecondsPauseMode() - LastFlash > 10) { + LastFlash = CTimer::GetTimeInMillisecondsPauseMode(); + m_nHelperTextAlpha -= 2; + } + + if (m_nHelperTextAlpha < 1) + ResetHelperText(); + + alpha = m_nHelperTextAlpha > 255 ? 255 : m_nHelperTextAlpha; + } + + CFont::SetColor(CRGBA(255, 255, 255, alpha)); + // TODO: name this cases? + switch (m_nHelperTextMsgId) { + case 1: + CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), TheText.Get("FET_APP")); + break; + case 2: + CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), TheText.Get("FET_HRD")); + break; + case 3: + CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), TheText.Get("FET_RSO")); + break; + case 4: + CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), TheText.Get("FET_STS")); + break; + case 5: + CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), TheText.Get("FET_RSC")); + break; + default: + if (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_NO) + return; + + if (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_MUSICVOLUME || + aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_SFXVOLUME) { + + CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), + m_nPrefsAudio3DProviderIndex == NO_AUDIO_PROVIDER ? TheText.Get("FEH_NA") : TheText.Get("FET_MIG")); + return; + } + + if (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_KEYBOARDCTRLS) + return; + + if (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_SCREENRES) { + CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), + m_bGameNotLoaded ? TheText.Get("FET_MIG") : TheText.Get("FEH_NA")); + return; + } + + if (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_AUDIOHW || + aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_SPEAKERCONF) { + + CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), + m_nPrefsAudio3DProviderIndex == NO_AUDIO_PROVIDER ? TheText.Get("FEH_NA") : TheText.Get("FET_MIG")); + return; + } + + if (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_RESTOREDEF) + return; + + if (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_MP3VOLUMEBOOST) { + CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), + m_nPrefsAudio3DProviderIndex == NO_AUDIO_PROVIDER ? TheText.Get("FEH_NA") : TheText.Get("FET_MIG")); + return; + } + + CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), + m_nCurrScreen != MENUPAGE_STATS ? TheText.Get("FET_MIG") : TheText.Get("FEH_SSA")); + + break; + } +} + +int +CMenuManager::DisplaySlider(float x, float y, float mostLeftBarSize, float mostRightBarSize, float rectSize, float progress, float spacing) +{ + CRGBA color; + float maxBarHeight; + + int lastActiveBarX = 0; + float curBarX = 0.0f; + for (int i = 0; i < MENUSLIDER_BARS; i++) { + curBarX = i * rectSize/MENUSLIDER_BARS + x; + + if (i / (float)MENUSLIDER_BARS + 1 / (MENUSLIDER_BARS * 2.f) < progress) { + color = CRGBA(SLIDERON_COLOR.r, SLIDERON_COLOR.g, SLIDERON_COLOR.b, FadeIn(255)); + lastActiveBarX = curBarX; + } else + color = CRGBA(SLIDEROFF_COLOR.r, SLIDEROFF_COLOR.g, SLIDEROFF_COLOR.b, FadeIn(255)); + + maxBarHeight = Max(mostLeftBarSize, mostRightBarSize); + + float curBarFreeSpace = ((MENUSLIDER_BARS - i) * mostLeftBarSize + i * mostRightBarSize) / (float)MENUSLIDER_BARS; + float left = curBarX; + float top = y + maxBarHeight - curBarFreeSpace; + float right = spacing + curBarX; + float bottom = y + maxBarHeight; + float shadowOffset = SCREEN_SCALE_X(2.0f); + CSprite2d::DrawRect(CRect(left + shadowOffset, top + shadowOffset, right + shadowOffset, bottom + shadowOffset), CRGBA(0, 0, 0, FadeIn(200))); // Shadow + CSprite2d::DrawRect(CRect(left, top, right, bottom), color); + } + return lastActiveBarX; +} + +void +CMenuManager::DoSettingsBeforeStartingAGame() +{ +#ifdef LEGACY_MENU_OPTIONS + if (m_PrefsVsyncDisp != m_PrefsVsync) + m_PrefsVsync = m_PrefsVsyncDisp; +#endif + DMAudio.DestroyAllGameCreatedEntities(); + DMAudio.Service(); + m_bShutDownFrontEndRequested = true; + m_bWantToRestart = true; + DMAudio.SetEffectsFadeVol(0); + DMAudio.SetMusicFadeVol(0); + for (int i = 0; i < NUM_RADIOS; i++) + CStats::FavoriteRadioStationList[i] = 0.0f; + + SwitchMenuOnAndOff(); + DMAudio.ResetTimers(CTimer::GetTimeInMilliseconds()); +} + +void +CMenuManager::DrawStandardMenus(bool activeScreen) +{ + float nextYToUse = 0.0f; // III leftover, set but unused in VC + bool itemsAreSelectable = true; + CFont::SetBackgroundOff(); + CFont::SetPropOn(); + CFont::SetCentreOff(); + CFont::SetJustifyOn(); + CFont::SetBackGroundOnlyTextOff(); + +#ifdef CUSTOM_FRONTEND_OPTIONS + const int xMargin = aScreens[m_nCurrScreen].layout && aScreens[m_nCurrScreen].layout->xMargin != 0 ? aScreens[m_nCurrScreen].layout->xMargin : MENU_X_MARGIN; +#else + const int xMargin = MENU_X_MARGIN; +#endif + + CFont::SetWrapx(MENU_X_RIGHT_ALIGNED(xMargin)); + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(xMargin)); +#ifdef ASPECT_RATIO_SCALE + CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH)); +#else + CFont::SetCentreSize(SCREEN_WIDTH); +#endif + + switch (m_nCurrScreen) { + case MENUPAGE_CHOOSE_LOAD_SLOT: + case MENUPAGE_CHOOSE_DELETE_SLOT: + case MENUPAGE_CHOOSE_SAVE_SLOT: + CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(38.0f), MENU_Y(85.0f), + MENU_X_LEFT_ALIGNED(615.0f), MENU_Y(75.0f), + MENU_X_LEFT_ALIGNED(30.0f), MENU_Y(320.0f), + MENU_X_LEFT_ALIGNED(605.0f), MENU_Y(330.0f), CRGBA(LIST_BACKGROUND_COLOR.r, LIST_BACKGROUND_COLOR.g, LIST_BACKGROUND_COLOR.b, FadeIn(LIST_BACKGROUND_COLOR.a))); + break; + case MENUPAGE_SOUND_SETTINGS: + PrintRadioSelector(); + break; + case MENUPAGE_STATS: + PrintStats(); + break; + case MENUPAGE_BRIEFS: + PrintBriefs(); + break; + } + + // Page name + if (aScreens[m_nCurrScreen].m_ScreenName[0] != '\0') { + + SET_FONT_FOR_MENU_HEADER + CFont::SetColor(CRGBA(30, 30, 30, FadeIn(255))); + CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(MENUHEADER_POS_X) - MENU_X(7.f), SCREEN_SCALE_Y(MENUHEADER_POS_Y + 7.f), TheText.Get(aScreens[m_nCurrScreen].m_ScreenName)); + + CFont::SetColor(CRGBA(HEADER_COLOR.r, HEADER_COLOR.g, HEADER_COLOR.b, FadeIn(255))); + CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(MENUHEADER_POS_X), SCREEN_SCALE_Y(MENUHEADER_POS_Y), TheText.Get(aScreens[m_nCurrScreen].m_ScreenName)); + } + + // Label + wchar *str; + if (aScreens[m_nCurrScreen].m_aEntries[0].m_Action == MENUACTION_LABEL) { + CFont::SetWrapx(MENU_X_RIGHT_ALIGNED(MENULABEL_X_MARGIN)); + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(MENULABEL_X_MARGIN)); + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); + CFont::SetScale(MENU_X(BIGTEXT2_X_SCALE), MENU_Y(BIGTEXT2_Y_SCALE)); + CFont::SetRightJustifyOff(); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(255))); + CFont::SetColor(CRGBA(LABEL_COLOR.r, LABEL_COLOR.g, LABEL_COLOR.b, FadeIn(255))); + + switch (m_nCurrScreen) { + case MENUPAGE_LOAD_SLOT_CONFIRM: + if (m_bGameNotLoaded) + str = TheText.Get("FES_LCG"); + else + str = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[0].m_EntryName); + break; + case MENUPAGE_DELETE_SLOT_CONFIRM: + str = TheText.Get(aScreens[MENUPAGE_DELETE_SLOT_CONFIRM].m_aEntries[0].m_EntryName); + break; + case MENUPAGE_SAVE_OVERWRITE_CONFIRM: + if (Slots[m_nCurrSaveSlot] == SLOT_OK) + str = TheText.Get("FESZ_QO"); + else if (Slots[m_nCurrSaveSlot] == SLOT_CORRUPTED) + str = TheText.Get("FESZ_QZ"); + else + str = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[0].m_EntryName); + break; + case MENUPAGE_EXIT: + if (m_bGameNotLoaded) + str = TheText.Get("FEQ_SRW"); + else + str = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[0].m_EntryName); + break; + default: + str = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[0].m_EntryName); + break; + } + + CFont::PrintString(MENU_X_LEFT_ALIGNED(100.0f), MENU_Y(97.0f), str); + CFont::SetWrapx(MENU_X_RIGHT_ALIGNED(xMargin)); + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(xMargin)); + } + + if (m_nCurrScreen == MENUPAGE_KEYBOARD_CONTROLS) { + if (m_bWaitingForNewKeyBind) + itemsAreSelectable = false; + + DrawControllerScreenExtraText(-8.0f, MENU_X_LEFT_ALIGNED(350), MENU_DEFAULT_LINE_HEIGHT); + } + + wchar unicodeTemp[64]; +#ifdef ASPECT_RATIO_SCALE + char asciiTemp[32]; +#endif + + bool weHaveLabel = aScreens[m_nCurrScreen].m_aEntries[0].m_Action == MENUACTION_LABEL; + uint8 section = 0; // 0: highlight trapezoid 1: texts + + while (section < 2) { +#endif + +#ifdef SCROLLABLE_PAGES + int firstOption = SCREEN_HAS_AUTO_SCROLLBAR ? m_nFirstVisibleRowOnList : 0; + int scrollOffset = aScreens[m_nCurrScreen].m_aEntries[firstOption].m_Y - aScreens[m_nCurrScreen].m_aEntries[0].m_Y; + for (int i = firstOption; i < firstOption + MAX_VISIBLE_OPTION && i < NUM_MENUROWS; ++i) { +#else + for (int i = 0; i < NUM_MENUROWS; ++i) { +#endif + wchar* rightText = nil; + wchar* leftText; + if (aScreens[m_nCurrScreen].m_aEntries[i].m_SaveSlot >= SAVESLOT_1 && aScreens[m_nCurrScreen].m_aEntries[i].m_SaveSlot <= SAVESLOT_8) { + CFont::SetColor(CRGBA(0, 0, 0, FadeIn(255))); + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); + CFont::SetScale(MENU_X(MEDIUMTEXT_X_SCALE), MENU_Y(MEDIUMTEXT_Y_SCALE)); + CFont::SetDropShadowPosition(0); + } else { + CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); + CFont::SetScale(MENU_X(BIGTEXT_X_SCALE), MENU_Y(BIGTEXT_Y_SCALE)); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(255))); + CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(255))); + } + if (aScreens[m_nCurrScreen].m_aEntries[i].m_Align == MENUALIGN_LEFT) { + CFont::SetCentreOff(); + CFont::SetRightJustifyOff(); + } else if (aScreens[m_nCurrScreen].m_aEntries[i].m_Align == MENUALIGN_RIGHT) { + CFont::SetCentreOff(); + CFont::SetRightJustifyOn(); + } else { + CFont::SetRightJustifyOff(); + CFont::SetCentreOn(); + } + if (aScreens[m_nCurrScreen].m_aEntries[i].m_X == 0 && aScreens[m_nCurrScreen].m_aEntries[i].m_Y == 0) { + if (i == 0 || (i == 1 && weHaveLabel)) { +#ifdef CUSTOM_FRONTEND_OPTIONS + aScreens[m_nCurrScreen].m_aEntries[i].m_X = (aScreens[m_nCurrScreen].layout ? aScreens[m_nCurrScreen].layout->startX : MENU_DEFAULT_CONTENT_X); + aScreens[m_nCurrScreen].m_aEntries[i].m_Y = (aScreens[m_nCurrScreen].layout ? aScreens[m_nCurrScreen].layout->startY : MENU_DEFAULT_CONTENT_Y); +#else + aScreens[m_nCurrScreen].m_aEntries[i].m_X = MENU_DEFAULT_CONTENT_X; + aScreens[m_nCurrScreen].m_aEntries[i].m_Y = MENU_DEFAULT_CONTENT_Y; +#endif + + } else { + aScreens[m_nCurrScreen].m_aEntries[i].m_X = aScreens[m_nCurrScreen].m_aEntries[i-1].m_X; + aScreens[m_nCurrScreen].m_aEntries[i].m_Y = aScreens[m_nCurrScreen].m_aEntries[i-1].m_Y PLUS_LINE_HEIGHT_ON_SCREEN; + } + } +#ifdef CUSTOM_FRONTEND_OPTIONS + else if (aScreens[m_nCurrScreen].m_aEntries[i].m_Y == 0) { + aScreens[m_nCurrScreen].m_aEntries[i].m_Y = aScreens[m_nCurrScreen].m_aEntries[i-1].m_Y PLUS_LINE_HEIGHT_ON_SCREEN; + } +#endif + + if (aScreens[m_nCurrScreen].m_aEntries[i].m_Action != MENUACTION_LABEL && aScreens[m_nCurrScreen].m_aEntries[i].m_EntryName[0] != '\0') { + + if (aScreens[m_nCurrScreen].m_aEntries[i].m_SaveSlot >= SAVESLOT_1 && aScreens[m_nCurrScreen].m_aEntries[i].m_SaveSlot <= SAVESLOT_8) { + CFont::SetRightJustifyOff(); + + leftText = nil; + if (Slots[i] == SLOT_OK) { + leftText = GetNameOfSavedGame(i); + rightText = GetSavedGameDateAndTime(i); + } + + if (!leftText || leftText[0] == '\0') { + sprintf(gString, "FEM_SL%d", i + 1); + leftText = TheText.Get(gString); + } + } else { + leftText = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[i].m_EntryName); + } + + if (m_nPrefsAudio3DProviderIndex == NO_AUDIO_PROVIDER) { + if (strcmp(aScreens[m_nCurrScreen].m_aEntries[i].m_EntryName, "FEO_AUD") == 0) { + CFont::SetColor(CRGBA(DARKMENUOPTION_COLOR.r, DARKMENUOPTION_COLOR.g, DARKMENUOPTION_COLOR.b, FadeIn(255))); + } + } + + switch (aScreens[m_nCurrScreen].m_aEntries[i].m_Action) { +#ifdef GAMEPAD_MENU + case MENUACTION_CTRLVIBRATION: + if (m_PrefsUseVibration) + rightText = TheText.Get("FEM_ON"); + else + rightText = TheText.Get("FEM_OFF"); + break; + case MENUACTION_CTRLCONFIG: + switch (CPad::GetPad(0)->Mode) { + case 0: + rightText = TheText.Get("FEC_CF1"); + break; + case 1: + rightText = TheText.Get("FEC_CF2"); + break; + case 2: + rightText = TheText.Get("FEC_CF3"); + break; + case 3: + rightText = TheText.Get("FEC_CF4"); + break; + } + break; + // This one is still in enum and ProcessOnOffMenuOptions, but removed from other places + case MENUACTION_CTRLDISPLAY: + if (m_DisplayControllerOnFoot) + rightText = TheText.Get("FEC_ONF"); + else + rightText = TheText.Get("FEC_INC"); + break; +#endif + case MENUACTION_FRAMESYNC: + rightText = TheText.Get(m_PrefsVsyncDisp ? "FEM_ON" : "FEM_OFF"); + break; + case MENUACTION_FRAMELIMIT: + rightText = TheText.Get(m_PrefsFrameLimiter ? "FEM_ON" : "FEM_OFF"); + break; + case MENUACTION_TRAILS: + rightText = TheText.Get(CMBlur::BlurOn ? "FEM_ON" : "FEM_OFF"); + break; + case MENUACTION_SUBTITLES: + rightText = TheText.Get(m_PrefsShowSubtitles ? "FEM_ON" : "FEM_OFF"); + break; + case MENUACTION_WIDESCREEN: +#ifndef ASPECT_RATIO_SCALE + rightText = TheText.Get(m_PrefsUseWideScreen ? "FEM_ON" : "FEM_OFF"); +#else + switch (m_PrefsUseWideScreen) { + case AR_AUTO: + rightText = TheText.Get("FEM_AUT"); + break; + case AR_4_3: + sprintf(asciiTemp, "4:3"); + AsciiToUnicode(asciiTemp, unicodeTemp); + rightText = unicodeTemp; + break; + case AR_5_4: + sprintf(asciiTemp, "5:4"); + AsciiToUnicode(asciiTemp, unicodeTemp); + rightText = unicodeTemp; + break; + case AR_16_10: + sprintf(asciiTemp, "16:10"); + AsciiToUnicode(asciiTemp, unicodeTemp); + rightText = unicodeTemp; + break; + case AR_16_9: + sprintf(asciiTemp, "16:9"); + AsciiToUnicode(asciiTemp, unicodeTemp); + rightText = unicodeTemp; + break; + case AR_21_9: + sprintf(asciiTemp, "21:9"); + AsciiToUnicode(asciiTemp, unicodeTemp); + rightText = unicodeTemp; + break; + } +#endif + break; + + case MENUACTION_MUSICVOLUME: + case MENUACTION_SFXVOLUME: + if (m_nPrefsAudio3DProviderIndex == NO_AUDIO_PROVIDER) + rightText = TheText.Get("FEA_NAH"); + + break; + case MENUACTION_RADIO: + switch (m_PrefsRadioStation) { + case WILDSTYLE: + rightText = TheText.Get("FEA_FM0"); + break; + case FLASH_FM: + rightText = TheText.Get("FEA_FM1"); + break; + case KCHAT: + rightText = TheText.Get("FEA_FM2"); + break; + case FEVER: + rightText = TheText.Get("FEA_FM3"); + break; + case V_ROCK: + rightText = TheText.Get("FEA_FM4"); + break; + case VCPR: + rightText = TheText.Get("FEA_FM5"); + break; + case RADIO_ESPANTOSO: + rightText = TheText.Get("FEA_FM6"); + break; + case EMOTION: + rightText = TheText.Get("FEA_FM7"); + break; + case WAVE: + rightText = TheText.Get("FEA_FM8"); + break; + case USERTRACK: + rightText = TheText.Get("FEA_MP3"); + break; + } + break; + case MENUACTION_LEGENDS: + rightText = TheText.Get(m_PrefsShowLegends ? "FEM_ON" : "FEM_OFF"); + break; + case MENUACTION_RADARMODE: + switch (m_PrefsRadarMode) { + case 0: + rightText = TheText.Get("FED_RDM"); + break; + case 1: + rightText = TheText.Get("FED_RDB"); + break; + case 2: + rightText = TheText.Get("FEM_OFF"); + break; + } + break; + case MENUACTION_HUD: + rightText = TheText.Get(m_PrefsShowHud ? "FEM_ON" : "FEM_OFF"); + break; +#ifdef LEGACY_MENU_OPTIONS + case MENUACTION_SETDBGFLAG: + rightText = TheText.Get(CTheScripts::IsDebugOn() ? "FEM_ON" : "FEM_OFF"); + break; + case MENUACTION_SWITCHBIGWHITEDEBUGLIGHT: + rightText = TheText.Get(gbBigWhiteDebugLightSwitchedOn ? "FEM_ON" : "FEM_OFF"); + break; + case MENUACTION_COLLISIONPOLYS: + rightText = TheText.Get(gbShowCollisionPolys ? "FEM_ON" : "FEM_OFF"); + break; +#endif + case MENUACTION_SHOWHEADBOB: + rightText = TheText.Get(TheCamera.m_bHeadBob ? "FEM_ON" : "FEM_OFF"); + break; + case MENUACTION_INVVERT: + rightText = TheText.Get(MousePointerStateHelper.bInvertVertically ? "FEM_OFF" : "FEM_ON"); + break; + case MENUACTION_SCREENRES: + AsciiToUnicode(_psGetVideoModeList()[m_nDisplayVideoMode], unicodeTemp); + rightText = unicodeTemp; + + if (!m_bGameNotLoaded) { + CFont::SetColor(CRGBA(DARKMENUOPTION_COLOR.r, DARKMENUOPTION_COLOR.g, DARKMENUOPTION_COLOR.b, FadeIn(255))); + } + break; + case MENUACTION_AUDIOHW: + if (m_nPrefsAudio3DProviderIndex == NO_AUDIO_PROVIDER) + rightText = TheText.Get("FEA_NAH"); + else if (m_nPrefsAudio3DProviderIndex == -1) + rightText = TheText.Get("FEA_ADP"); + else { + char *rawProvider = DMAudio.Get3DProviderName(m_nPrefsAudio3DProviderIndex); + AsciiToUnicode(rawProvider, unicodeTemp); + char *provider = UnicodeToAscii(unicodeTemp); // genius + strupr(provider); + if (!strcmp(provider, "DIRECTSOUND3D HARDWARE SUPPORT")) { + strcpy(provider, "DSOUND3D HARDWARE SUPPORT"); + } else if (!strcmp(provider, "DIRECTSOUND3D SOFTWARE EMULATION")) { + strcpy(provider, "DSOUND3D SOFTWARE EMULATION"); + } + AsciiToUnicode(provider, unicodeTemp); + rightText = unicodeTemp; + } + break; + case MENUACTION_SPEAKERCONF: { + if (m_nPrefsAudio3DProviderIndex == NO_AUDIO_PROVIDER) + rightText = TheText.Get("FEA_NAH"); + else { + switch (m_PrefsSpeakers) { + case 0: + rightText = TheText.Get("FEA_2SP"); + break; + case 1: + rightText = TheText.Get("FEA_EAR"); + break; + case 2: + rightText = TheText.Get("FEA_4SP"); + break; + } + } + break; + } + case MENUACTION_CTRLMETHOD: { + switch (m_ControlMethod) { + case CONTROL_STANDARD: + leftText = TheText.Get("FET_STI"); + break; + case CONTROL_CLASSIC: + leftText = TheText.Get("FET_CTI"); + break; + } + break; + } + case MENUACTION_DYNAMICACOUSTIC: + rightText = TheText.Get(m_PrefsDMA ? "FEM_ON" : "FEM_OFF"); + break; + case MENUACTION_MOUSESTEER: + rightText = TheText.Get(CVehicle::m_bDisableMouseSteering ? "FEM_OFF" : "FEM_ON"); + if (m_ControlMethod == CONTROL_CLASSIC) { + CFont::SetColor(CRGBA(DARKMENUOPTION_COLOR.r, DARKMENUOPTION_COLOR.g, DARKMENUOPTION_COLOR.b, FadeIn(255))); + } + break; + case MENUACTION_MP3VOLUMEBOOST: + if (!DMAudio.IsMP3RadioChannelAvailable()) { + rightText = TheText.Get("FEA_NM3"); + } + break; +#ifdef CUSTOM_FRONTEND_OPTIONS + case MENUACTION_CFO_DYNAMIC: + case MENUACTION_CFO_SELECT: + CMenuScreenCustom::CMenuEntry &option = aScreens[m_nCurrScreen].m_aEntries[i]; + if (option.m_Action == MENUACTION_CFO_SELECT) { + if (option.m_CFOSelect->disableIfGameLoaded && !m_bGameNotLoaded) + CFont::SetColor(CRGBA(DARKMENUOPTION_COLOR.r, DARKMENUOPTION_COLOR.g, DARKMENUOPTION_COLOR.b, FadeIn(255))); + + // To whom manipulate option.m_CFO->value of static options externally (like RestoreDef functions) + if (*(int8*)option.m_CFO->value != option.m_CFOSelect->lastSavedValue) + option.m_CFOSelect->displayedValue = option.m_CFOSelect->lastSavedValue = *(int8*)option.m_CFO->value; + + if (option.m_CFOSelect->displayedValue >= option.m_CFOSelect->numRightTexts || option.m_CFOSelect->displayedValue < 0) + option.m_CFOSelect->displayedValue = 0; + + rightText = TheText.Get(option.m_CFOSelect->rightTexts[option.m_CFOSelect->displayedValue]); + + } else if (option.m_Action == MENUACTION_CFO_DYNAMIC) { + if (option.m_CFODynamic->drawFunc) { + bool isOptionDisabled = false; + rightText = option.m_CFODynamic->drawFunc(&isOptionDisabled, m_nCurrOption == i); + if (isOptionDisabled) + CFont::SetColor(CRGBA(DARKMENUOPTION_COLOR.r, DARKMENUOPTION_COLOR.g, DARKMENUOPTION_COLOR.b, FadeIn(255))); + } + } + break; +#endif + } + + // Highlight trapezoid + if (activeScreen && i == m_nCurrOption && itemsAreSelectable && section == 0) { + + int leftXMax, rightXMin; + + // FIX: Let's don't scale those so GetStringWidth can give us unscaled width, which will be handy to other calculations below that's done without scaling in mind, + // and scaling will be done eventually. + // CFont::SetScale(MENU_X(BIGTEXT_X_SCALE), MENU_Y(BIGTEXT_Y_SCALE)); + CFont::SetScale(BIGTEXT_X_SCALE, BIGTEXT_Y_SCALE); + + wchar *curOptionName = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_EntryName); + float curOptionWidth = CFont::GetStringWidth(curOptionName, true); + + if (CFont::Details.centre) { + leftXMax = Max(0, aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_X - curOptionWidth / 2.f); + rightXMin = Min(DEFAULT_SCREEN_WIDTH, curOptionWidth / 2.f + aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_X); + + } else if (!CFont::Details.rightJustify) { + leftXMax = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_X; + rightXMin = Min(DEFAULT_SCREEN_WIDTH, curOptionWidth + aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_X); + + } else { + leftXMax = Max(0, aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_X - curOptionWidth); + rightXMin = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_X; + } + CFont::SetScale(MENU_X(BIGTEXT_X_SCALE), MENU_Y(BIGTEXT_Y_SCALE)); + + int action = aScreens[m_nCurrScreen].m_aEntries[i].m_Action; + int saveSlot = aScreens[m_nCurrScreen].m_aEntries[i].m_SaveSlot; + if (rightText || action == MENUACTION_DRAWDIST || action == MENUACTION_BRIGHTNESS || action == MENUACTION_MUSICVOLUME || + action == MENUACTION_SFXVOLUME || action == MENUACTION_MP3VOLUMEBOOST || action == MENUACTION_MOUSESENS || + saveSlot >= SAVESLOT_1 && saveSlot <= SAVESLOT_8 +#ifdef CUSTOM_FRONTEND_OPTIONS + || action == MENUACTION_CFO_SLIDER +#endif + ) { + rightXMin = 600; + leftXMax = 40; + } + + int y = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Y MINUS_SCROLL_OFFSET; + int topYMax = y; + uint32 bottomYMin = y + MENU_DEFAULT_LINE_HEIGHT - 7; // Decreasing is not recommended. Because this actually is dependent to font scale, not line height. + + // Actually bottomRight and bottomLeft should be exchanged here(although this is original code). + // So this shows us either R* didn't use same struct for menu BG and highlight, or they just kept fields as x1,y1 etc. Yikes. + + if (m_nOptionHighlightTransitionBlend == 0) { + if (m_firstStartCounter == 255 && m_nMenuFadeAlpha == 255 && !bMenuChangeOngoing) { + CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(menuOptionHighlight.topLeft_x), MENU_Y(menuOptionHighlight.topLeft_y), + MENU_X_LEFT_ALIGNED(menuOptionHighlight.topRight_x), MENU_Y(menuOptionHighlight.topRight_y), + MENU_X_LEFT_ALIGNED(menuOptionHighlight.bottomRight_x), MENU_Y(menuOptionHighlight.bottomRight_y), + MENU_X_LEFT_ALIGNED(menuOptionHighlight.bottomLeft_x), MENU_Y(menuOptionHighlight.bottomLeft_y), SELECTIONBORDER_COLOR); + } + menuOptionHighlight.SaveCurrentCoors(); + menuOptionHighlight.topLeft_x = leftXMax - 5 - CGeneral::GetRandomNumber() % 10; + menuOptionHighlight.topLeft_y = topYMax - CGeneral::GetRandomNumber() % 7; + menuOptionHighlight.topRight_x = rightXMin + 5 + CGeneral::GetRandomNumber() % 10; + menuOptionHighlight.topRight_y = topYMax - CGeneral::GetRandomNumber() % 7; + menuOptionHighlight.bottomLeft_x = rightXMin + 5 + CGeneral::GetRandomNumber() % 10; + menuOptionHighlight.bottomLeft_y = bottomYMin + CGeneral::GetRandomNumber() % 7; + menuOptionHighlight.bottomRight_x = leftXMax - 5 - CGeneral::GetRandomNumber() % 10; + menuOptionHighlight.bottomRight_y = bottomYMin + CGeneral::GetRandomNumber() % 7; + menuOptionHighlight.UpdateMultipliers(); + menuOptionHighlight.Translate(m_nOptionHighlightTransitionBlend); + + } else if (m_nOptionHighlightTransitionBlend < 255) { + menuOptionHighlight.Translate(m_nOptionHighlightTransitionBlend); + if (m_firstStartCounter == 255 && m_nMenuFadeAlpha == 255 && !bMenuChangeOngoing) { + CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(menuOptionHighlight.topLeft_x), MENU_Y(menuOptionHighlight.topLeft_y), + MENU_X_LEFT_ALIGNED(menuOptionHighlight.topRight_x), MENU_Y(menuOptionHighlight.topRight_y), + MENU_X_LEFT_ALIGNED(menuOptionHighlight.bottomRight_x), MENU_Y(menuOptionHighlight.bottomRight_y), + MENU_X_LEFT_ALIGNED(menuOptionHighlight.bottomLeft_x), MENU_Y(menuOptionHighlight.bottomLeft_y), SELECTIONBORDER_COLOR); + } + } else { + m_nOptionHighlightTransitionBlend = 255; + menuOptionHighlight.Translate(m_nOptionHighlightTransitionBlend); + if (m_firstStartCounter == 255 && m_nMenuFadeAlpha == 255 && !bMenuChangeOngoing) { + CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(menuOptionHighlight.topLeft_x), MENU_Y(menuOptionHighlight.topLeft_y), + MENU_X_LEFT_ALIGNED(menuOptionHighlight.topRight_x), MENU_Y(menuOptionHighlight.topRight_y), + MENU_X_LEFT_ALIGNED(menuOptionHighlight.bottomRight_x), MENU_Y(menuOptionHighlight.bottomRight_y), + MENU_X_LEFT_ALIGNED(menuOptionHighlight.bottomLeft_x), MENU_Y(menuOptionHighlight.bottomLeft_y), SELECTIONBORDER_COLOR); + } + } + + static uint32 lastBlendChange = 0; + if (m_nOptionHighlightTransitionBlend <= 255) { + static uint32 blendChangeCounter = 0; + if (CTimer::GetTimeInMillisecondsPauseMode() - lastBlendChange > 20 + || blendChangeCounter > 20 + ) { + m_nOptionHighlightTransitionBlend += 50; + lastBlendChange = CTimer::GetTimeInMillisecondsPauseMode(); + blendChangeCounter = 0; + } +#ifdef FIX_BUGS + blendChangeCounter += CTimer::GetLogicalFramesPassed(); +#else + ++blendChangeCounter; +#endif + } + } + + if (section == 1) { + if (leftText) { + CFont::PrintString(MENU_X_LEFT_ALIGNED(aScreens[m_nCurrScreen].m_aEntries[i].m_X), MENU_Y(aScreens[m_nCurrScreen].m_aEntries[i].m_Y MINUS_SCROLL_OFFSET), leftText); + } + + if (rightText) { + CFont::SetCentreOff(); + CFont::SetRightJustifyOn(); + if (aScreens[m_nCurrScreen].m_aEntries[i].m_SaveSlot >= SAVESLOT_1 && aScreens[m_nCurrScreen].m_aEntries[i].m_SaveSlot <= SAVESLOT_8) { + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); + CFont::SetScale(MENU_X(MEDIUMTEXT_X_SCALE), MENU_Y(MEDIUMTEXT_Y_SCALE)); + } else { + CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); + CFont::SetScale(MENU_X(BIGTEXT_X_SCALE), MENU_Y(BIGTEXT_Y_SCALE)); + } + CFont::PrintString(MENU_X_LEFT_ALIGNED(DEFAULT_SCREEN_WIDTH - RIGHT_ALIGNED_TEXT_RIGHT_MARGIN(xMargin)), MENU_Y(aScreens[m_nCurrScreen].m_aEntries[i].m_Y MINUS_SCROLL_OFFSET), rightText); + } + + if (m_nPrefsAudio3DProviderIndex == DMAudio.GetCurrent3DProviderIndex()) { + if (!strcmp(aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_EntryName, "FEA_3DH") && m_nHelperTextMsgId == 1) + ResetHelperText(); + } + if (m_nDisplayVideoMode == m_nPrefsVideoMode) { + if (!strcmp(aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_EntryName, "FED_RES") && m_nHelperTextMsgId == 1) + ResetHelperText(); + } + if (m_nPrefsAudio3DProviderIndex != DMAudio.GetCurrent3DProviderIndex()) { + if (!strcmp(aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_EntryName, "FEA_3DH")) + SetHelperText(1); + } + if (m_nDisplayVideoMode != m_nPrefsVideoMode) { + if (!strcmp(aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_EntryName, "FED_RES")) + SetHelperText(1); + } + if (m_nPrefsAudio3DProviderIndex != DMAudio.GetCurrent3DProviderIndex()) { + if (strcmp(aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_EntryName, "FEA_3DH") != 0 +#ifdef CUSTOM_FRONTEND_OPTIONS + && ScreenHasOption(m_nCurrScreen, "FEA_3DH") +#else + && m_nCurrScreen == MENUPAGE_SOUND_SETTINGS +#endif + && m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) { + + m_nPrefsAudio3DProviderIndex = DMAudio.GetCurrent3DProviderIndex(); + SetHelperText(3); + } + } + if (m_nDisplayVideoMode != m_nPrefsVideoMode) { + if (strcmp(aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_EntryName, "FED_RES") != 0 +#ifdef CUSTOM_FRONTEND_OPTIONS + && ScreenHasOption(m_nCurrScreen, "FED_RES")) { +#else + && m_nCurrScreen == MENUPAGE_DISPLAY_SETTINGS) { +#endif + m_nDisplayVideoMode = m_nPrefsVideoMode; + SetHelperText(3); + } + } + +#ifdef CUSTOM_FRONTEND_OPTIONS +#define SLIDER_Y(pos) (aScreens[m_nCurrScreen].m_aEntries[i].m_Y - 5.f) +#else +#define SLIDER_Y(pos) pos +#endif + // Sliders + int lastActiveBarX; + switch (aScreens[m_nCurrScreen].m_aEntries[i].m_Action) { + case MENUACTION_BRIGHTNESS: + ProcessSlider(m_PrefsBrightness / 384.0f, SLIDER_Y(70.0f), HOVEROPTION_INCREASE_BRIGHTNESS, HOVEROPTION_DECREASE_BRIGHTNESS, SCREEN_WIDTH, true); + break; + case MENUACTION_DRAWDIST: + ProcessSlider((m_PrefsLOD - 0.925f) / 0.875f, SLIDER_Y(99.0f), HOVEROPTION_INCREASE_DRAWDIST, HOVEROPTION_DECREASE_DRAWDIST, SCREEN_WIDTH, true); + break; + case MENUACTION_MUSICVOLUME: + if(m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) + ProcessSlider(m_PrefsMusicVolume / 64.0f, SLIDER_Y(70.0f), HOVEROPTION_INCREASE_MUSICVOLUME, HOVEROPTION_DECREASE_MUSICVOLUME, SCREEN_WIDTH, true); + break; + case MENUACTION_SFXVOLUME: + if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) + ProcessSlider(m_PrefsSfxVolume / 64.0f, SLIDER_Y(99.0f), HOVEROPTION_INCREASE_SFXVOLUME, HOVEROPTION_DECREASE_SFXVOLUME, SCREEN_WIDTH, true); + break; + case MENUACTION_MOUSESENS: + ProcessSlider(TheCamera.m_fMouseAccelHorzntl * 200.0f, SLIDER_Y(170.0f), HOVEROPTION_INCREASE_MOUSESENS, HOVEROPTION_DECREASE_MOUSESENS, SCREEN_WIDTH, false); + break; + case MENUACTION_MP3VOLUMEBOOST: + if(m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER && DMAudio.IsMP3RadioChannelAvailable()) + ProcessSlider(m_PrefsMP3BoostVolume / 64.f, SLIDER_Y(128.0f), HOVEROPTION_INCREASE_MP3BOOST, HOVEROPTION_DECREASE_MP3BOOST, SCREEN_WIDTH, true); + break; +#ifdef CUSTOM_FRONTEND_OPTIONS + case MENUACTION_CFO_SLIDER: + CMenuScreenCustom::CMenuEntry &option = aScreens[m_nCurrScreen].m_aEntries[i]; + ProcessSlider((*(float*)option.m_CFOSlider->value - option.m_CFOSlider->min) / (option.m_CFOSlider->max - option.m_CFOSlider->min), SLIDER_Y(0), HOVEROPTION_INCREASE_CFO_SLIDER, HOVEROPTION_DECREASE_CFO_SLIDER, SCREEN_WIDTH, true); + break; +#endif + } + + // Not just unused, but also collides with the bug fix in Font.cpp. Yikes. +#ifndef FIX_BUGS + nextYToUse += MENU_DEFAULT_LINE_HEIGHT * CFont::GetNumberLines(MENU_X_LEFT_ALIGNED(60.0f), MENU_Y(nextYToUse), leftText); +#endif + + if (aScreens[m_nCurrScreen].m_aEntries[i].m_Action == MENUACTION_RADIO) { + nextYToUse += MENURADIO_SELECTOR_HEIGHT + 5.f; // unused + } + } + } + } + section++; + } + +#ifdef SCROLLABLE_PAGES + #define SCROLLBAR_BOTTOM_Y 105.0f // only for background, scrollbar's itself is calculated + #define SCROLLBAR_RIGHT_X 26.0f + #define SCROLLBAR_WIDTH 9.5f + #define SCROLLBAR_TOP_Y 84 + + if (activeScreen && SCREEN_HAS_AUTO_SCROLLBAR) { + // Scrollbar background + CSprite2d::DrawRect(CRect(MENU_X_RIGHT_ALIGNED(SCROLLBAR_RIGHT_X - 2), MENU_Y(SCROLLBAR_TOP_Y), + MENU_X_RIGHT_ALIGNED(SCROLLBAR_RIGHT_X - 2 - SCROLLBAR_WIDTH), SCREEN_SCALE_FROM_BOTTOM(SCROLLBAR_BOTTOM_Y)), CRGBA(30, 30, 30, FadeIn(150))); + + float scrollbarHeight = SCROLLBAR_MAX_HEIGHT / (m_nTotalListRow / (float) MAX_VISIBLE_OPTION); + float scrollbarBottom, scrollbarTop; + + scrollbarBottom = MENU_Y(SCROLLBAR_TOP_Y - 6 + m_nScrollbarTopMargin + scrollbarHeight); + scrollbarTop = MENU_Y(SCROLLBAR_TOP_Y + 2 + m_nScrollbarTopMargin); + // Scrollbar shadow + CSprite2d::DrawRect(CRect(MENU_X_RIGHT_ALIGNED(SCROLLBAR_RIGHT_X - 4), scrollbarTop, + MENU_X_RIGHT_ALIGNED(SCROLLBAR_RIGHT_X - 1 - SCROLLBAR_WIDTH), scrollbarBottom + MENU_Y(1.0f)), + CRGBA(50, 50, 50, FadeIn(255))); + + // Scrollbar + CSprite2d::DrawRect(CRect(MENU_X_RIGHT_ALIGNED(SCROLLBAR_RIGHT_X - 4), scrollbarTop, + MENU_X_RIGHT_ALIGNED(SCROLLBAR_RIGHT_X - SCROLLBAR_WIDTH), scrollbarBottom), + CRGBA(SCROLLBAR_COLOR.r, SCROLLBAR_COLOR.g, SCROLLBAR_COLOR.b, FadeIn(255))); + + } +#endif + + switch (m_nCurrScreen) { +#ifdef GAMEPAD_MENU + case MENUPAGE_CONTROLLER_SETTINGS: + PrintController(); + break; +#endif + case MENUPAGE_STATS: + case MENUPAGE_CONTROLLER_PC: + case MENUPAGE_SOUND_SETTINGS: + case MENUPAGE_DISPLAY_SETTINGS: + case MENUPAGE_MOUSE_CONTROLS: + DisplayHelperText(nil); + break; + case MENUPAGE_OPTIONS: + if (m_nPrefsAudio3DProviderIndex == NO_AUDIO_PROVIDER && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_LOADRADIO) + DisplayHelperText("FEA_NAH"); + break; +#ifdef CUSTOM_FRONTEND_OPTIONS + default: + if (aScreens[m_nCurrScreen].layout) { + if (aScreens[m_nCurrScreen].layout->showLeftRightHelper) { + DisplayHelperText(nil); + } + } + break; +#endif + } + + if (m_nCurrScreen == MENUPAGE_DELETING_IN_PROGRESS) { + SmallMessageScreen("FEDL_WR"); + } +#ifndef XBOX_MESSAGE_SCREEN + else if (m_nCurrScreen == MENUPAGE_SAVING_IN_PROGRESS) { + SmallMessageScreen("FESZ_WR"); + } +#endif +} + +int +CMenuManager::GetNumOptionsCntrlConfigScreens(void) +{ + int number = 0; + switch (m_nCurrScreen) { +#ifdef LEGACY_MENU_OPTIONS + case MENUPAGE_CONTROLLER_PC_OLD3: + number = 2; + break; + case MENUPAGE_CONTROLLER_DEBUG: + number = 4; + break; +#endif + case MENUPAGE_KEYBOARD_CONTROLS: + switch (m_ControlMethod) { + case CONTROL_STANDARD: + number = 27; + break; + case CONTROL_CLASSIC: + number = 32; + break; + } + break; + } + return number; +} + +void +CMenuManager::DrawControllerBound(int32 yStart, int32 xStart, int32 unused, int8 column) +{ + int controllerAction = PED_FIREWEAPON; + // GetStartOptionsCntrlConfigScreens(); + int numOptions = GetNumOptionsCntrlConfigScreens(); + int nextY = MENU_Y(yStart); + int bindingMargin = MENU_X(3.0f); + float rowHeight; + switch (m_ControlMethod) { + case CONTROL_STANDARD: + rowHeight = CONTSETUP_STANDARD_ROW_HEIGHT; + break; + case CONTROL_CLASSIC: + rowHeight = CONTSETUP_CLASSIC_ROW_HEIGHT; + break; + default: + break; + } + + for (int optionIdx = 0; optionIdx < numOptions; nextY = MENU_Y(++optionIdx * rowHeight + yStart)) { + int nextX = xStart; + int bindingsForThisOpt = 0; + int contSetOrder = SETORDER_1; + CFont::SetColor(CRGBA(255, 255, 255, FadeIn(255))); + + if (column == CONTSETUP_PED_COLUMN) { + switch (optionIdx) { + case 0: + controllerAction = PED_FIREWEAPON; + break; + case 1: + controllerAction = PED_CYCLE_WEAPON_RIGHT; + break; + case 2: + controllerAction = PED_CYCLE_WEAPON_LEFT; + break; + case 3: + controllerAction = GO_FORWARD; + break; + case 4: + controllerAction = GO_BACK; + break; + case 5: + controllerAction = GO_LEFT; + break; + case 6: + controllerAction = GO_RIGHT; + break; + case 7: + controllerAction = PED_SNIPER_ZOOM_IN; + break; + case 8: + controllerAction = PED_SNIPER_ZOOM_OUT; + break; + case 9: + controllerAction = VEHICLE_ENTER_EXIT; + break; + case 10: + case 11: + case 12: + case 16: + case 20: + case 21: + case 22: + case 23: + controllerAction = -1; + break; + case 13: + controllerAction = CAMERA_CHANGE_VIEW_ALL_SITUATIONS; + break; + case 14: + controllerAction = PED_JUMPING; + break; + case 15: + controllerAction = PED_SPRINT; + break; + case 17: + controllerAction = PED_LOCK_TARGET; + break; + case 18: + controllerAction = PED_DUCK; + break; + case 19: + controllerAction = PED_ANSWER_PHONE; + break; + case 24: + controllerAction = PED_LOOKBEHIND; + break; + case 25: + if (m_ControlMethod == CONTROL_STANDARD) + controllerAction = -1; + else + controllerAction = PED_1RST_PERSON_LOOK_LEFT; + break; + case 26: + if (m_ControlMethod == CONTROL_STANDARD) + controllerAction = -1; + else + controllerAction = PED_1RST_PERSON_LOOK_RIGHT; + break; + case 27: + controllerAction = PED_1RST_PERSON_LOOK_UP; + break; + case 28: + controllerAction = PED_1RST_PERSON_LOOK_DOWN; + break; + case 29: + controllerAction = PED_CYCLE_TARGET_LEFT; + break; + case 30: + controllerAction = PED_CYCLE_TARGET_RIGHT; + break; + case 31: + controllerAction = PED_CENTER_CAMERA_BEHIND_PLAYER; + break; + default: + break; + } + } else if (column == CONTSETUP_VEHICLE_COLUMN) { + switch (optionIdx) { + case 0: +#ifdef BIND_VEHICLE_FIREWEAPON + controllerAction = VEHICLE_FIREWEAPON; +#else + controllerAction = PED_FIREWEAPON; +#endif + break; + case 1: + case 2: + case 7: + case 8: + case 14: + case 15: + case 17: + case 18: + case 19: + case 27: + case 28: + case 29: + case 30: + case 31: + controllerAction = -1; + break; + case 3: + controllerAction = VEHICLE_ACCELERATE; + break; + case 4: + controllerAction = VEHICLE_BRAKE; + break; + case 5: + controllerAction = GO_LEFT; + break; + case 6: + controllerAction = GO_RIGHT; + break; + case 9: + controllerAction = VEHICLE_ENTER_EXIT; + break; + case 10: + controllerAction = VEHICLE_CHANGE_RADIO_STATION; + break; + case 11: + controllerAction = VEHICLE_HORN; + break; + case 12: + controllerAction = TOGGLE_SUBMISSIONS; + break; + case 13: + controllerAction = CAMERA_CHANGE_VIEW_ALL_SITUATIONS; + break; + case 16: + controllerAction = VEHICLE_HANDBRAKE; + break; + case 20: + controllerAction = VEHICLE_TURRETLEFT; + break; + case 21: + controllerAction = VEHICLE_TURRETRIGHT; + break; + case 22: + controllerAction = VEHICLE_TURRETUP; + break; + case 23: + controllerAction = VEHICLE_TURRETDOWN; + break; + case 24: + controllerAction = -2; + break; + case 25: + controllerAction = VEHICLE_LOOKLEFT; + break; + case 26: + controllerAction = VEHICLE_LOOKRIGHT; + break; + default: + break; + } + } + + // Highlight selected column(and make its text black) + if (m_nSelectedListRow == optionIdx) { + int bgY = m_nSelectedListRow * rowHeight + yStart + 1.0f; + if (m_nCurrExLayer == HOVEROPTION_LIST) { + + if (column == CONTSETUP_PED_COLUMN && m_nSelectedContSetupColumn == CONTSETUP_PED_COLUMN) { +#ifdef FIX_BUGS + CSprite2d::DrawRect(CRect(nextX, MENU_Y(bgY), nextX + MENU_X(CONTSETUP_BOUND_COLUMN_WIDTH), + MENU_Y(bgY + CONTSETUP_BOUND_HIGHLIGHT_HEIGHT)), CRGBA(SELECTIONBORDER_COLOR.r, SELECTIONBORDER_COLOR.g, SELECTIONBORDER_COLOR.b, FadeIn(255))); +#else + CSprite2d::DrawRect(CRect(MENU_X_LEFT_ALIGNED(210.0f), MENU_Y(bgY), + MENU_X_LEFT_ALIGNED(400.0f), MENU_Y(bgY + CONTSETUP_BOUND_HIGHLIGHT_HEIGHT)), + CRGBA(SELECTIONBORDER_COLOR.r, SELECTIONBORDER_COLOR.g, SELECTIONBORDER_COLOR.b, FadeIn(255))); +#endif + CFont::SetColor(CRGBA(255, 255, 255, FadeIn(255))); + + } else if (column == CONTSETUP_VEHICLE_COLUMN && m_nSelectedContSetupColumn == CONTSETUP_VEHICLE_COLUMN) { +#ifdef FIX_BUGS + CSprite2d::DrawRect(CRect(nextX, MENU_Y(bgY), nextX + MENU_X(CONTSETUP_BOUND_COLUMN_WIDTH), + MENU_Y(bgY + CONTSETUP_BOUND_HIGHLIGHT_HEIGHT)), CRGBA(SELECTIONBORDER_COLOR.r, SELECTIONBORDER_COLOR.g, SELECTIONBORDER_COLOR.b, FadeIn(255))); +#else + CSprite2d::DrawRect(CRect(MENU_X_LEFT_ALIGNED(410.0f), MENU_Y(bgY), MENU_X_LEFT_ALIGNED(600.0f), MENU_Y(bgY + CONTSETUP_BOUND_HIGHLIGHT_HEIGHT)), + CRGBA(SELECTIONBORDER_COLOR.r, SELECTIONBORDER_COLOR.g, SELECTIONBORDER_COLOR.b, FadeIn(255))); +#endif + CFont::SetColor(CRGBA(255, 255, 255, FadeIn(255))); + } + } + } + + // Print bindings, including seperator (-) between them + + CFont::SetScale(MENU_X(0.25f), MENU_Y(LISTITEM_Y_SCALE)); +#ifdef FIX_BUGS + for (; contSetOrder < MAX_SETORDERS && controllerAction >= 0; contSetOrder++) { +#else + for (; contSetOrder < MAX_SETORDERS && controllerAction != -1; contSetOrder++) { +#endif + wchar *settingText = ControlsManager.GetControllerSettingTextWithOrderNumber((e_ControllerAction)controllerAction, (eContSetOrder)contSetOrder); + if (settingText) { + ++bindingsForThisOpt; + if (bindingsForThisOpt > 1) { + wchar *seperator = TheText.Get("FEC_IBT"); + CFont::SetColor(CRGBA(255, 255, 255, FadeIn(255))); + CFont::PrintString(nextX, nextY, seperator); + nextX += CFont::GetStringWidth(seperator, true) + bindingMargin; + } + CFont::PrintString(nextX, nextY, settingText); +#ifdef MORE_LANGUAGES + if (CFont::IsJapanese()) + nextX += CFont::GetStringWidth_Jap(settingText) + bindingMargin; + else +#endif + nextX += CFont::GetStringWidth(settingText, true) + bindingMargin; + } + } + if (controllerAction == -1) { + CFont::SetColor(CRGBA(0, 0, 0, FadeIn(255))); + CFont::PrintString(nextX, nextY, TheText.Get("FEC_NUS")); // not used + + } else if (controllerAction == -2) { + CFont::SetColor(CRGBA(0, 0, 0, FadeIn(255))); + CFont::PrintString(nextX, nextY, TheText.Get("FEC_CMP")); // combo: l+r + + } else if (bindingsForThisOpt == 0) { + m_NoEmptyBinding = false; + if (m_nSelectedListRow != optionIdx) { + CFont::SetColor(CRGBA(0, 0, 0, FadeIn(255))); + CFont::PrintString(nextX, nextY, TheText.Get("FEC_UNB")); // unbound + + } else if (m_bWaitingForNewKeyBind) { + if (column != m_nSelectedContSetupColumn) { + CFont::SetColor(CRGBA(0, 0, 0, FadeIn(255))); + CFont::PrintString(nextX, nextY, TheText.Get("FEC_UNB")); // unbound + } + } else { + if (column != m_nSelectedContSetupColumn) { + CFont::SetColor(CRGBA(0, 0, 0, FadeIn(255))); + } + CFont::PrintString(nextX, nextY, TheText.Get("FEC_UNB")); // unbound + } + } + + if (column == CONTSETUP_PED_COLUMN && m_nSelectedContSetupColumn == CONTSETUP_PED_COLUMN || + column == CONTSETUP_VEHICLE_COLUMN && m_nSelectedContSetupColumn == CONTSETUP_VEHICLE_COLUMN) { + + if (optionIdx == m_nSelectedListRow && controllerAction != -1 && controllerAction != -2) { + m_CurrCntrlAction = controllerAction; + if (m_bWaitingForNewKeyBind) { + static bool showWaitingText = false; + if (bindingsForThisOpt > 0) { + wchar *seperator = TheText.Get("FEC_IBT"); + CFont::PrintString(nextX, nextY, seperator); + nextX += CFont::GetStringWidth(seperator, true) + bindingMargin; + } + static uint32 lastWaitingTextFlash = 0; + if (CTimer::GetTimeInMillisecondsPauseMode() - lastWaitingTextFlash > 150) { + showWaitingText = !showWaitingText; + lastWaitingTextFlash = CTimer::GetTimeInMillisecondsPauseMode(); + } + if (showWaitingText) { + CFont::SetColor(CRGBA(255, 255, 255, FadeIn(255))); + CFont::PrintString(nextX, nextY, TheText.Get("FEC_QUE")); // "???" + } + if (m_bKeyChangeNotProcessed) + DisplayHelperText("FET_CIG"); + else + DisplayHelperText("FET_RIG"); + + SET_FONT_FOR_LIST_ITEM + + m_bKeyIsOK = true; + } else { + DisplayHelperText("FET_CIG"); + SET_FONT_FOR_LIST_ITEM + + m_bKeyIsOK = false; + m_bKeyChangeNotProcessed = false; + } + } else if (optionIdx == m_nSelectedListRow) { + DisplayHelperText("FET_EIG"); + SET_FONT_FOR_LIST_ITEM + } + } + } +} + +void +CMenuManager::DrawControllerScreenExtraText(int yStart, int xStart, int lineHeight) +{ + int extraTextStart = GetStartOptionsCntrlConfigScreens(); + int numOpts = GetNumOptionsCntrlConfigScreens(); + int spacing = MENU_X(10.0f); + for (int i = extraTextStart; i < extraTextStart + numOpts; i++) { + int numTextsPrinted = 0; + int nextX = xStart; + for (int j = 1; j < 5; j++) { + wchar *text = ControlsManager.GetControllerSettingTextWithOrderNumber((e_ControllerAction)i, (eContSetOrder)j); + if (text) + ++numTextsPrinted; + + if (text) { + // Seperator + if (numTextsPrinted > 1) { + CFont::PrintString(nextX, MENU_Y(yStart), TheText.Get("FEC_IBT")); + nextX = CFont::GetStringWidth(TheText.Get("FEC_IBT"), true) + spacing + nextX; + } + CFont::PrintString(nextX, MENU_Y(yStart), text); + } + if (text) + nextX = CFont::GetStringWidth(text, true) + spacing + nextX; + } + if (m_nCurrOption == i - extraTextStart && m_bWaitingForNewKeyBind) { + static bool waitingTextVisible = false; + + // Seperator + if (numTextsPrinted > 0) { + CFont::PrintString(nextX, MENU_Y(yStart), TheText.Get("FEC_IBT")); + nextX = CFont::GetStringWidth(TheText.Get("FEC_IBT"), true) + spacing + nextX; + } + static uint32 lastStateChange = 0; + if (CTimer::GetTimeInMillisecondsPauseMode() - lastStateChange > 150) { + waitingTextVisible = !waitingTextVisible; + lastStateChange = CTimer::GetTimeInMillisecondsPauseMode(); + } + if (waitingTextVisible) { + CFont::SetColor(CRGBA(255, 255, 0, FadeIn(255))); + CFont::PrintString(nextX, MENU_Y(yStart), TheText.Get("FEC_QUE")); + CFont::SetColor(CRGBA(LABEL_COLOR.r, LABEL_COLOR.g, LABEL_COLOR.b, FadeIn(255))); + } + } + yStart += lineHeight; + } + wchar *error = nil; + if (DisplayComboButtonErrMsg) + error = ControlsManager.GetButtonComboText((e_ControllerAction)(m_nCurrOption + extraTextStart)); + + if (error) { + CFont::SetColor(CRGBA(233, 22, 159, 255)); + CFont::PrintString(xStart, MENU_Y(yStart + 10), error); + } +} + +void +CMenuManager::DrawControllerSetupScreen() +{ + float rowHeight; + switch (m_ControlMethod) { + case CONTROL_STANDARD: + rowHeight = CONTSETUP_STANDARD_ROW_HEIGHT; + break; + case CONTROL_CLASSIC: + rowHeight = CONTSETUP_CLASSIC_ROW_HEIGHT; + break; + default: + break; + } + RESET_FONT_FOR_NEW_PAGE + SET_FONT_FOR_MENU_HEADER + + // Shadow + CFont::SetColor(CRGBA(30, 30, 30, FadeIn(255))); + + if (m_ControlMethod == CONTROL_STANDARD) + CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(MENUHEADER_POS_X) - MENU_X(7.f), SCREEN_SCALE_Y(MENUHEADER_POS_Y + 7.f), TheText.Get("FET_STI")); + else if (m_ControlMethod == CONTROL_CLASSIC) + CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(MENUHEADER_POS_X) - MENU_X(7.f), SCREEN_SCALE_Y(MENUHEADER_POS_Y + 7.f), TheText.Get("FET_CTI")); + + // Real header + CFont::SetColor(CRGBA(HEADER_COLOR.r, HEADER_COLOR.g, HEADER_COLOR.b, FadeIn(255))); + + if (m_ControlMethod == CONTROL_STANDARD) + CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(MENUHEADER_POS_X), SCREEN_SCALE_Y(MENUHEADER_POS_Y), TheText.Get("FET_STI")); + else if (m_ControlMethod == CONTROL_CLASSIC) + CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(MENUHEADER_POS_X), SCREEN_SCALE_Y(MENUHEADER_POS_Y), TheText.Get("FET_CTI")); + + wchar *actionTexts[33]; + actionTexts[0] = TheText.Get("FEC_FIR"); + actionTexts[1] = TheText.Get("FEC_NWE"); + actionTexts[2] = TheText.Get("FEC_PWE"); + actionTexts[3] = TheText.Get("FEC_FOR"); + actionTexts[4] = TheText.Get("FEC_BAC"); + actionTexts[5] = TheText.Get("FEC_LEF"); + actionTexts[6] = TheText.Get("FEC_RIG"); + actionTexts[7] = TheText.Get("FEC_ZIN"); + actionTexts[8] = TheText.Get("FEC_ZOT"); + actionTexts[9] = TheText.Get("FEC_EEX"); + actionTexts[10] = TheText.Get("FEC_RAD"); + actionTexts[11] = TheText.Get("FEC_HRN"); + actionTexts[12] = TheText.Get("FEC_SUB"); + actionTexts[13] = TheText.Get("FEC_CMR"); + actionTexts[14] = TheText.Get("FEC_JMP"); + actionTexts[15] = TheText.Get("FEC_SPN"); + actionTexts[16] = TheText.Get("FEC_HND"); + actionTexts[17] = TheText.Get("FEC_TAR"); + actionTexts[18] = TheText.Get("FEC_CRO"); + actionTexts[19] = TheText.Get("FEC_ANS"); + if (m_ControlMethod == CONTROL_CLASSIC) { + actionTexts[20] = TheText.Get("FEC_TFL"); + actionTexts[21] = TheText.Get("FEC_TFR"); + actionTexts[22] = TheText.Get("FEC_TFU"); + actionTexts[23] = TheText.Get("FEC_TFD"); + actionTexts[24] = TheText.Get("FEC_LBA"); + actionTexts[25] = TheText.Get("FEC_LOL"); + actionTexts[26] = TheText.Get("FEC_LOR"); + actionTexts[27] = TheText.Get("FEC_LUD"); + actionTexts[28] = TheText.Get("FEC_LDU"); + actionTexts[29] = TheText.Get("FEC_NTR"); + actionTexts[30] = TheText.Get("FEC_PTT"); + actionTexts[31] = TheText.Get("FEC_CEN"); + actionTexts[32] = nil; + } else { + actionTexts[20] = TheText.Get("FEC_TFL"); + actionTexts[21] = TheText.Get("FEC_TFR"); + actionTexts[22] = TheText.Get("FEC_TFU"); + actionTexts[23] = TheText.Get("FEC_TFD"); + actionTexts[24] = TheText.Get("FEC_LBA"); + actionTexts[25] = TheText.Get("FEC_LOL"); + actionTexts[26] = TheText.Get("FEC_LOR"); + actionTexts[27] = nil; + } + + // Blue panel background + CSprite2d::DrawRect(CRect(MENU_X_LEFT_ALIGNED(CONTSETUP_LIST_LEFT), MENU_Y(CONTSETUP_LIST_TOP), + MENU_X_RIGHT_ALIGNED(CONTSETUP_LIST_RIGHT), SCREEN_SCALE_FROM_BOTTOM(CONTSETUP_LIST_BOTTOM)), + CRGBA(LIST_BACKGROUND_COLOR.r, LIST_BACKGROUND_COLOR.g, LIST_BACKGROUND_COLOR.b, FadeIn(LIST_BACKGROUND_COLOR.a))); + + if (m_nCurrExLayer == HOVEROPTION_LIST) + CFont::SetColor(CRGBA(SELECTEDMENUOPTION_COLOR.r, SELECTEDMENUOPTION_COLOR.g, SELECTEDMENUOPTION_COLOR.b, FadeIn(255))); + else + CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(255))); + + // List header + CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); + CFont::SetScale(MENU_X(MENUACTION_SCALE_MULT), MENU_Y(MENUACTION_SCALE_MULT)); + CFont::SetRightJustifyOff(); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(255))); + CFont::PrintString(MENU_X_LEFT_ALIGNED(CONTSETUP_COLUMN_2_X), MENU_Y(CONTSETUP_LIST_TOP), TheText.Get("FET_CFT")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(CONTSETUP_COLUMN_3_X), MENU_Y(CONTSETUP_LIST_TOP), TheText.Get("FET_CCR")); + CFont::SetDropShadowPosition(0); + SET_FONT_FOR_LIST_ITEM + + int yStart; + if (m_ControlMethod == CONTROL_CLASSIC) + yStart = CONTSETUP_LIST_TOP + 18; + else + yStart = CONTSETUP_LIST_TOP + 21; + + float optionYBottom = yStart + rowHeight; + for (int i = 0; i < ARRAY_SIZE(actionTexts); ++i) { + wchar *actionText = actionTexts[i]; + if (!actionText) + break; + + if (!m_bWaitingForNewKeyBind) { + if (m_nMousePosX > MENU_X_LEFT_ALIGNED(CONTSETUP_LIST_LEFT - 10.0f) && + m_nMousePosX < MENU_X_LEFT_ALIGNED(CONTSETUP_COLUMN_3_X + CONTSETUP_BOUND_COLUMN_WIDTH)) { + + if (m_nMousePosY > MENU_Y(i * rowHeight + yStart) && m_nMousePosY < MENU_Y(i * rowHeight + optionYBottom)) { + m_nOptionMouseHovering = i; + if (m_nMouseOldPosX != m_nMousePosX || m_nMouseOldPosY != m_nMousePosY) { + m_nCurrExLayer = HOVEROPTION_LIST; + m_nSelectedListRow = i; + + // why different number for 3rd column hovering X?? this function is a mess +#ifdef FIX_BUGS + if (m_nMousePosX > MENU_X_LEFT_ALIGNED(0.0f) && m_nMousePosX < MENU_X_LEFT_ALIGNED(CONTSETUP_COLUMN_2_X + CONTSETUP_BOUND_COLUMN_WIDTH)) { +#else + if (m_nMousePosX > MENU_X_LEFT_ALIGNED(0.0f) && m_nMousePosX < MENU_X_LEFT_ALIGNED(370.0f)) { +#endif + m_nSelectedContSetupColumn = CONTSETUP_PED_COLUMN; +#ifdef FIX_BUGS + } else if (m_nMousePosX > MENU_X_LEFT_ALIGNED(CONTSETUP_COLUMN_2_X + CONTSETUP_BOUND_COLUMN_WIDTH) && m_nMousePosX < SCREEN_WIDTH) { +#else + } else if (m_nMousePosX > MENU_X_LEFT_ALIGNED(370.0f) && m_nMousePosX < SCREEN_WIDTH) { +#endif + m_nSelectedContSetupColumn = CONTSETUP_VEHICLE_COLUMN; + } + } + // what?? + if (m_nHoverOption == HOVEROPTION_SKIN) { + if (i == m_nSelectedListRow) { + m_nHoverOption = HOVEROPTION_NOT_HOVERING; + m_bWaitingForNewKeyBind = true; + m_bStartWaitingForKeyBind = true; + pControlEdit = &m_KeyPressedCode; + } + } else + m_nHoverOption = HOVEROPTION_NOT_HOVERING; + } + } + } + if (m_nSelectedListRow != i) + CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(255))); + else if (m_nCurrExLayer == HOVEROPTION_LIST) + CFont::SetColor(CRGBA(SELECTEDMENUOPTION_COLOR.r, SELECTEDMENUOPTION_COLOR.g, SELECTEDMENUOPTION_COLOR.b, FadeIn(255))); + + CFont::SetRightJustifyOff(); + if (m_PrefsLanguage == LANGUAGE_GERMAN && (i == 20 || i == 21 || i == 22 || i == 23)) + CFont::SetScale(MENU_X(0.32f), MENU_Y(LISTITEM_Y_SCALE)); + else + CFont::SetScale(MENU_X(LISTITEM_X_SCALE), MENU_Y(LISTITEM_Y_SCALE)); + + CFont::PrintString(MENU_X_LEFT_ALIGNED(CONTSETUP_COLUMN_1_X), MENU_Y(i * rowHeight + yStart), actionText); + } + DrawControllerBound(yStart, MENU_X_LEFT_ALIGNED(CONTSETUP_COLUMN_2_X), rowHeight, CONTSETUP_PED_COLUMN); + DrawControllerBound(yStart, MENU_X_LEFT_ALIGNED(CONTSETUP_COLUMN_3_X), rowHeight, CONTSETUP_VEHICLE_COLUMN); + + if (!m_bWaitingForNewKeyBind) { + CFont::SetScale(MENU_X(BIGTEXT_X_SCALE), MENU_Y(BIGTEXT_Y_SCALE)); + + if ((m_nMousePosX > MENU_X_RIGHT_ALIGNED(CONTSETUP_BACK_RIGHT) - CFont::GetStringWidth(TheText.Get("FEDS_TB"), true) + && m_nMousePosX < MENU_X_RIGHT_ALIGNED(CONTSETUP_BACK_RIGHT) && m_nMousePosY > SCREEN_SCALE_FROM_BOTTOM(CONTSETUP_BACK_BOTTOM) + && m_nMousePosY < SCREEN_SCALE_FROM_BOTTOM(CONTSETUP_BACK_BOTTOM - CONTSETUP_BACK_HEIGHT)) || m_nCurrExLayer == HOVEROPTION_BACK) { + m_nHoverOption = HOVEROPTION_BACK; + + } else if (m_nMousePosX > MENU_X_LEFT_ALIGNED(CONTSETUP_LIST_LEFT - 10.0f) && m_nMousePosX < MENU_X_LEFT_ALIGNED(CONTSETUP_COLUMN_3_X + CONTSETUP_BOUND_COLUMN_WIDTH) + && m_nMousePosY > MENU_Y(CONTSETUP_LIST_TOP - 10.0f) && m_nMousePosY < SCREEN_SCALE_FROM_BOTTOM(CONTSETUP_LIST_BOTTOM)) { + m_nHoverOption = HOVEROPTION_LIST; + + } else { + m_nHoverOption = HOVEROPTION_NOT_HOVERING; + } + } + + // Back button and it's shadow + CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); + CFont::SetScale(MENU_X(BIGTEXT_X_SCALE), MENU_Y(BIGTEXT_Y_SCALE)); + CFont::SetRightJustifyOn(); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(255))); + CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(255))); + CFont::PrintString(MENU_X_RIGHT_ALIGNED(CONTSETUP_BACK_RIGHT - 2.0f), SCREEN_SCALE_FROM_BOTTOM(CONTSETUP_BACK_BOTTOM - 4.0f), TheText.Get("FEDS_TB")); +} + +void +CMenuManager::DrawFrontEnd() +{ + CFont::SetAlphaFade(255.0f); + CSprite2d::InitPerFrame(); + CFont::InitPerFrame(); + SetFrontEndRenderStates(); + m_NoEmptyBinding = true; + + if (m_nCurrScreen == MENUPAGE_NONE) { + if (m_bGameNotLoaded) { + m_nCurrScreen = MENUPAGE_START_MENU; + } else { + m_nCurrScreen = MENUPAGE_PAUSE_MENU; + } + SETUP_SCROLLING(m_nCurrScreen) + } + + if (m_nCurrOption == 0 && aScreens[m_nCurrScreen].m_aEntries[0].m_Action == MENUACTION_LABEL) + m_nCurrOption = 1; + + if (m_firstStartCounter == 255 && m_nMenuFadeAlpha == 255) + bMenuChangeOngoing = false; + + DrawBackground(false); + + char strver[200]; + wchar ustr[200]; + snprintf(strver, sizeof(strver), "dca-miami: %s", getExecutableTag()); + AsciiToUnicode(strver, ustr); + + CFont::SetScale(MENU_X(MENUACTION_SCALE_MULT*3/4), MENU_Y(MENUACTION_SCALE_MULT*3/4)); + CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(255))); + CFont::PrintString(MENU_X_LEFT_ALIGNED(BUILDID_TEXT_LEFT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(BUILDID_TEXT_BOTTOM_MARGIN), ustr); +} + +void +CMenuManager::DrawBackground(bool transitionCall) +{ + if (!m_bSpritesLoaded) + return; + + SetFrontEndRenderStates(); + + if (m_firstStartCounter < 255) { + CSprite2d::DrawRect(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), CRGBA(0, 0, 0, 255)); + } + + if (m_nMenuFadeAlpha != 0) { + + if (m_nMenuFadeAlpha < 255) { + + menuBg.Translate(m_nMenuFadeAlpha); + SetFrontEndRenderStates(); + m_aFrontEndSprites[MENUSPRITE_BACKGROUND].Draw(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), CRGBA(255, 255, 255, FadeIn(255))); + if (m_nCurrScreen == MENUPAGE_MAP) + PrintMap(); + + // Left border + CSprite2d::Draw2DPolygon(SCREEN_STRETCH_X(menuBg.bottomLeft_x), SCREEN_STRETCH_Y(menuBg.bottomLeft_y), 0.0f, SCREEN_HEIGHT, + SCREEN_STRETCH_X(menuBg.topLeft_x), SCREEN_STRETCH_Y(menuBg.topLeft_y), 0.0f, 0.0f, CRGBA(0, 0, 0, 255)); + + // Top border + CSprite2d::Draw2DPolygon(SCREEN_STRETCH_X(menuBg.topRight_x), SCREEN_STRETCH_Y(menuBg.topRight_y), + SCREEN_STRETCH_X(menuBg.topLeft_x), SCREEN_STRETCH_Y(menuBg.topLeft_y), SCREEN_WIDTH, 0.0f, 0.0f, 0.0f, CRGBA(0, 0, 0, 255)); + + // Bottom border + CSprite2d::Draw2DPolygon(SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f, SCREEN_HEIGHT, SCREEN_STRETCH_X(menuBg.bottomRight_x), SCREEN_STRETCH_Y(menuBg.bottomRight_y), + SCREEN_STRETCH_X(menuBg.bottomLeft_x), SCREEN_STRETCH_Y(menuBg.bottomLeft_y), CRGBA(0, 0, 0, 255)); + + // Right border + CSprite2d::Draw2DPolygon(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_STRETCH_X(menuBg.bottomRight_x), SCREEN_STRETCH_Y(menuBg.bottomRight_y), + SCREEN_WIDTH, 0.0f, SCREEN_STRETCH_X(menuBg.topRight_x), SCREEN_STRETCH_Y(menuBg.topRight_y), CRGBA(0, 0, 0, 255)); + } else { + m_nMenuFadeAlpha = 255; + m_firstStartCounter = 255; + m_aFrontEndSprites[MENUSPRITE_BACKGROUND].Draw(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), CRGBA(255, 255, 255, FadeIn(255))); + if (m_nCurrScreen == MENUPAGE_MAP) + PrintMap(); + + // Left border + CSprite2d::Draw2DPolygon(SCREEN_STRETCH_X(menuBg.bottomLeft_x), SCREEN_STRETCH_Y(menuBg.bottomLeft_y), 0.0f, SCREEN_HEIGHT, + SCREEN_STRETCH_X(menuBg.topLeft_x), SCREEN_STRETCH_Y(menuBg.topLeft_y), 0.0f, 0.0f, CRGBA(0, 0, 0, 255)); + + // Top border + CSprite2d::Draw2DPolygon(SCREEN_STRETCH_X(menuBg.topRight_x), SCREEN_STRETCH_Y(menuBg.topRight_y), + SCREEN_STRETCH_X(menuBg.topLeft_x), SCREEN_STRETCH_Y(menuBg.topLeft_y), SCREEN_WIDTH, 0.0f, 0.0f, 0.0f, CRGBA(0, 0, 0, 255)); + + // Bottom border + CSprite2d::Draw2DPolygon(SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f, SCREEN_HEIGHT, SCREEN_STRETCH_X(menuBg.bottomRight_x), SCREEN_STRETCH_Y(menuBg.bottomRight_y), + SCREEN_STRETCH_X(menuBg.bottomLeft_x), SCREEN_STRETCH_Y(menuBg.bottomLeft_y), CRGBA(0, 0, 0, 255)); + + // Right border + CSprite2d::Draw2DPolygon(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_STRETCH_X(menuBg.bottomRight_x), SCREEN_STRETCH_Y(menuBg.bottomRight_y), + SCREEN_WIDTH, 0.0f, SCREEN_STRETCH_X(menuBg.topRight_x), SCREEN_STRETCH_Y(menuBg.topRight_y), CRGBA(0, 0, 0, 255)); + } + } else { + menuBg.SaveCurrentCoors(); + switch (m_nCurrScreen) { + case MENUPAGE_STATS: + menuBg.topLeft_x = 70.0f; + menuBg.topLeft_y = 75.0f; + menuBg.topRight_x = 550.0f; + menuBg.topRight_y = 16.0f; + menuBg.bottomLeft_x = 74.0f; + menuBg.bottomLeft_y = 354.0f; + menuBg.bottomRight_x = 581.0f; + menuBg.bottomRight_y = 340.0f; + break; + case MENUPAGE_SOUND_SETTINGS: + menuBg.topLeft_x = 26.0f; + menuBg.topLeft_y = 59.0f; + menuBg.topRight_x = 629.0f; + menuBg.topRight_y = 29.0f; + menuBg.bottomLeft_x = 15.0f; + menuBg.bottomLeft_y = 438.0f; + menuBg.bottomRight_x = 610.0f; + menuBg.bottomRight_y = 410.0f; + break; + case MENUPAGE_SKIN_SELECT: + case MENUPAGE_KEYBOARD_CONTROLS: + menuBg.topLeft_x = 14.0f; + menuBg.topLeft_y = 39.0f; + menuBg.topRight_x = 636.0f; + menuBg.topRight_y = 29.0f; + menuBg.bottomLeft_x = 15.0f; + menuBg.bottomLeft_y = 426.0f; + menuBg.bottomRight_x = 630.0f; + menuBg.bottomRight_y = 398.0f; + break; + case MENUPAGE_BRIEFS: + case MENUPAGE_DISPLAY_SETTINGS: + case MENUPAGE_MAP: + case MENUPAGE_CHOOSE_LOAD_SLOT: + case MENUPAGE_CHOOSE_DELETE_SLOT: + case MENUPAGE_CHOOSE_SAVE_SLOT: + case MENUPAGE_MOUSE_CONTROLS: + menuBg.topLeft_x = 26.0f; + menuBg.topLeft_y = 59.0f; + menuBg.topRight_x = 629.0f; + menuBg.topRight_y = 29.0f; + menuBg.bottomLeft_x = 15.0f; + menuBg.bottomLeft_y = 426.0f; + menuBg.bottomRight_x = 610.0f; + menuBg.bottomRight_y = 398.0f; + break; + default: +#ifdef CUSTOM_FRONTEND_OPTIONS + if (aScreens[m_nCurrScreen].layout && aScreens[m_nCurrScreen].layout->noInvasiveBorders) { + // Taken from the case above + menuBg.topLeft_x = 26.0f; + menuBg.topLeft_y = 59.0f; + menuBg.topRight_x = 629.0f; + menuBg.topRight_y = 29.0f; + menuBg.bottomLeft_x = 15.0f; + menuBg.bottomLeft_y = 426.0f; + menuBg.bottomRight_x = 610.0f; + menuBg.bottomRight_y = 398.0f; + break; + } +#endif + menuBg.topLeft_x = CGeneral::GetRandomNumber() % 40 + 65; + menuBg.topLeft_y = CGeneral::GetRandomNumber() % 40 + 21; + menuBg.topRight_x = CGeneral::GetRandomNumber() % 40 + 568; + menuBg.topRight_y = CGeneral::GetRandomNumber() % 40 + 44; + menuBg.bottomLeft_x = CGeneral::GetRandomNumber() % 40 + 36; + menuBg.bottomLeft_y = CGeneral::GetRandomNumber() % 40 + 382; + menuBg.bottomRight_x = CGeneral::GetRandomNumber() % 40 + 593; + menuBg.bottomRight_y = CGeneral::GetRandomNumber() % 40 + 342; + break; + } + + menuBg.UpdateMultipliers(); + if (m_firstStartCounter == 255) + m_nOptionHighlightTransitionBlend = 0; + } + + static uint32 LastFade = 0; + + if (m_nMenuFadeAlpha < 255) { + static uint8 forceFadeInCounter = 0; + if (CTimer::GetTimeInMillisecondsPauseMode() - LastFade > 30 + || forceFadeInCounter > 30 + ) { + m_nMenuFadeAlpha += 20; + if (m_firstStartCounter < 255) { + m_firstStartCounter = Min(m_firstStartCounter + 20, 255); + } + LastFade = CTimer::GetTimeInMillisecondsPauseMode(); +#ifdef FIX_BUGS + forceFadeInCounter = 0; +#endif + } +#ifdef FIX_BUGS + forceFadeInCounter += CTimer::GetLogicalFramesPassed(); +#else + forceFadeInCounter++; +#endif + } else if (m_nMenuFadeAlpha > 255) + m_nMenuFadeAlpha = 255; + + if (!transitionCall && m_firstStartCounter == 255) { + int actualAlpha = m_nMenuFadeAlpha; + if (actualAlpha < 255) { + int actualScreen = m_nCurrScreen; + SetFrontEndRenderStates(); + m_nCurrScreen = m_nPrevScreen; + m_nMenuFadeAlpha = 255 - m_nMenuFadeAlpha; + switch (m_nCurrScreen) { + case MENUPAGE_SKIN_SELECT: + DrawPlayerSetupScreen(false); + break; + case MENUPAGE_KEYBOARD_CONTROLS: + DrawControllerSetupScreen(); + break; + case MENUPAGE_OUTRO: + DrawQuitGameScreen(); + break; + default: + DrawStandardMenus(false); + break; + } + m_nCurrScreen = actualScreen; + m_nMenuFadeAlpha = actualAlpha; + } + } + + switch (m_nCurrScreen) { + case MENUPAGE_SKIN_SELECT: + DrawPlayerSetupScreen(true); + break; + case MENUPAGE_KEYBOARD_CONTROLS: + DrawControllerSetupScreen(); + break; + case MENUPAGE_OUTRO: + DrawQuitGameScreen(); + break; + default: + DrawStandardMenus(true); + break; + } + + CFont::DrawFonts(); + SetFrontEndRenderStates(); + + if (m_nCurrScreen != MENUPAGE_OUTRO) + if (m_firstStartCounter == 255) { + m_aFrontEndSprites[MENUSPRITE_VCLOGO].Draw(CRect(SCREEN_STRETCH_X(27.0f), MENU_Y(8.0f), SCREEN_STRETCH_X(27.0f) + MENU_X(130.f), MENU_Y(138.0f)), CRGBA(255, 255, 255, 255)); + } else { + m_aFrontEndSprites[MENUSPRITE_VCLOGO].Draw(CRect(SCREEN_STRETCH_X(27.0f), MENU_Y(8.0f), SCREEN_STRETCH_X(27.0f) + MENU_X(130.f), MENU_Y(138.0f)), CRGBA(255, 255, 255, FadeIn(255))); + } + + if (m_ShowEmptyBindingError) { + static uint32 lastBindingError = CTimer::GetTimeInMillisecondsPauseMode(); + static bool bindingErrorShown = false; + if (bindingErrorShown) { + lastBindingError = CTimer::GetTimeInMillisecondsPauseMode(); + bindingErrorShown = false; + } + SmallMessageScreen("FEC_ERI"); + CFont::DrawFonts(); + if (CTimer::GetTimeInMillisecondsPauseMode() - lastBindingError > 4000) { + m_ShowEmptyBindingError = false; + bindingErrorShown = true; + } + } + + if (m_bShowMouse) { + CRect mouse(0.0f, 0.0f, MENU_X(35.0f), MENU_Y(35.0f)); + CRect shad(MENU_X(10.0f), MENU_Y(3.0f), MENU_X(45.0f), MENU_Y(38.0f)); + + mouse.Translate(m_nMousePosX, m_nMousePosY); + shad.Translate(m_nMousePosX, m_nMousePosY); + m_aFrontEndSprites[MENUSPRITE_MOUSE].Draw(shad, CRGBA(100, 100, 100, 50)); + m_aFrontEndSprites[MENUSPRITE_MOUSE].Draw(mouse, CRGBA(255, 255, 255, 255)); + } +} + +void +CMenuManager::DrawPlayerSetupScreen(bool activeScreen) +{ + RESET_FONT_FOR_NEW_PAGE + + // lstrcpy's changed with strcpy + if (!m_bSkinsEnumerated) { + OutputDebugString("Enumerating skin filenames from skins..."); + m_pSkinListHead.nextSkin = nil; + m_pSelectedSkin = &m_pSkinListHead; + m_pSelectedSkin->nextSkin = new tSkinInfo; + m_pSelectedSkin = m_pSelectedSkin->nextSkin; + m_pSelectedSkin->skinId = 0; + strcpy(m_pSelectedSkin->skinNameOriginal, DEFAULT_SKIN_NAME); + strcpy(m_pSelectedSkin->skinNameDisplayed, UnicodeToAscii(TheText.Get("FET_DSN"))); + int nextSkinId = 1; + m_pSelectedSkin->nextSkin = nil; + + WIN32_FIND_DATA FindFileData; + SYSTEMTIME SystemTime; + HANDLE handle = FindFirstFile("skins\\*.bmp", &FindFileData); + for (int i = 1; handle != INVALID_HANDLE_VALUE && i; i = FindNextFile(handle, &FindFileData)) { + if (strcmp(FindFileData.cFileName, DEFAULT_SKIN_NAME) != 0) { + m_pSelectedSkin->nextSkin = new tSkinInfo; + m_pSelectedSkin = m_pSelectedSkin->nextSkin; + m_pSelectedSkin->skinId = nextSkinId; + strcpy(m_pSelectedSkin->skinNameOriginal, FindFileData.cFileName); + strcpy(m_pSelectedSkin->skinNameDisplayed, FindFileData.cFileName); + FileTimeToSystemTime(&FindFileData.ftLastWriteTime, &SystemTime); + GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &SystemTime, 0, m_pSelectedSkin->date, 255); + ++nextSkinId; + m_pSelectedSkin->nextSkin = nil; + } + } + FindClose(handle); + m_nSkinsTotal = nextSkinId; + char nameTemp[256]; + for (m_pSelectedSkin = m_pSkinListHead.nextSkin; m_pSelectedSkin; m_pSelectedSkin = m_pSelectedSkin->nextSkin) { + // Drop extension + int oldLength = (int)strlen(m_pSelectedSkin->skinNameDisplayed); + m_pSelectedSkin->skinNameDisplayed[oldLength - 4] = '\0'; + m_pSelectedSkin->skinNameOriginal[oldLength - 4] = '\0'; + + // Fill to 40 bytes-39 chars, idk why. This is done in sepearate function in game. + strncpy(nameTemp, m_pSelectedSkin->skinNameDisplayed, 39); // game doesn't do that, but in our day strncpy to same string is forbidden + strncpy(m_pSelectedSkin->skinNameDisplayed, nameTemp, 39); + if (oldLength - 4 > 39) + m_pSelectedSkin->skinNameDisplayed[39] = '\0'; + + // Make string lowercase, except first letter + strlwr(m_pSelectedSkin->skinNameDisplayed); + strncpy(nameTemp, m_pSelectedSkin->skinNameDisplayed, 1); + strupr(nameTemp); + strncpy(m_pSelectedSkin->skinNameDisplayed, nameTemp, 1); + + // Change some chars +#ifdef FIX_BUGS + for (int k = 0; m_pSelectedSkin->skinNameDisplayed[k] != '\0'; ++k) { +#else + for (int k = 0; m_pSelectedSkin->skinNameOriginal[k] != '\0'; ++k) { +#endif + if (!strncmp(&m_pSelectedSkin->skinNameDisplayed[k], "_", 1)) + strncpy(&m_pSelectedSkin->skinNameDisplayed[k], " ", 1); + if (!strncmp(&m_pSelectedSkin->skinNameDisplayed[k], "@", 1)) + strncpy(&m_pSelectedSkin->skinNameDisplayed[k], " ", 1); + if (!strncmp(&m_pSelectedSkin->skinNameDisplayed[k], "{", 1)) + strncpy(&m_pSelectedSkin->skinNameDisplayed[k], "(", 1); + if (!strncmp(&m_pSelectedSkin->skinNameDisplayed[k], "}", 1)) + strncpy(&m_pSelectedSkin->skinNameDisplayed[k], ")", 1); + if (!strncmp(&m_pSelectedSkin->skinNameDisplayed[k], "£", 1)) + strncpy(&m_pSelectedSkin->skinNameDisplayed[k], "$", 1); + } + + // Make letters after whitespace uppercase + for (int l = 0; m_pSelectedSkin->skinNameDisplayed[l] != '\0'; ++l) { + if (!strncmp(&m_pSelectedSkin->skinNameDisplayed[l], " ", 1)) { + if (m_pSelectedSkin->skinNameDisplayed[l + 1]) { + strncpy(nameTemp, &m_pSelectedSkin->skinNameDisplayed[l + 1], 1); + strupr(nameTemp); + strncpy(&m_pSelectedSkin->skinNameDisplayed[l + 1], nameTemp, 1); + } + } + } + } + OutputDebugString("Finished enumerating skin files."); + m_bSkinsEnumerated = true; + } + CSprite2d::DrawRect(CRect(MENU_X_LEFT_ALIGNED(PLAYERSETUP_LIST_LEFT), MENU_Y(PLAYERSETUP_LIST_TOP), + MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT), SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM)), CRGBA(LIST_BACKGROUND_COLOR.r, LIST_BACKGROUND_COLOR.g, LIST_BACKGROUND_COLOR.b, FadeIn(LIST_BACKGROUND_COLOR.a))); + + SET_FONT_FOR_MENU_HEADER + CFont::SetColor(CRGBA(30, 30, 30, FadeIn(255))); + CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(MENUHEADER_POS_X) - MENU_X(7.f), SCREEN_SCALE_Y(MENUHEADER_POS_Y + 7.f), TheText.Get("FET_PS")); + + CFont::SetColor(CRGBA(HEADER_COLOR.r, HEADER_COLOR.g, HEADER_COLOR.b, FadeIn(255))); + CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(MENUHEADER_POS_X), SCREEN_SCALE_Y(MENUHEADER_POS_Y), TheText.Get("FET_PS")); + + // Header (Skin - Date) + if (m_nCurrExLayer == HOVEROPTION_LIST) { + CFont::SetColor(CRGBA(SELECTEDMENUOPTION_COLOR.r, SELECTEDMENUOPTION_COLOR.g, SELECTEDMENUOPTION_COLOR.b, FadeIn(255))); + } else { + CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(255))); + } + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + CFont::SetScale(MENU_X(MENUACTION_SCALE_MULT), MENU_Y(MENUACTION_SCALE_MULT)); + CFont::SetRightJustifyOn(); + CFont::PrintString(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_DATE_COLUMN_RIGHT), MENU_Y(PLAYERSETUP_LIST_TOP), TheText.Get("FES_DAT")); + switch (m_PrefsLanguage) { + case LANGUAGE_FRENCH: + case LANGUAGE_SPANISH: + CFont::SetScale(MENU_X(0.6f), MENU_Y(MENUACTION_SCALE_MULT)); + break; + default: + CFont::SetScale(MENU_X(MENUACTION_SCALE_MULT), MENU_Y(MENUACTION_SCALE_MULT)); + break; + } + CFont::SetRightJustifyOff(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(PLAYERSETUP_SKIN_COLUMN_LEFT), MENU_Y(PLAYERSETUP_LIST_TOP), TheText.Get("FES_SKN")); + CFont::SetDropShadowPosition(0); + + // Skin list + SET_FONT_FOR_LIST_ITEM + if (m_nSkinsTotal > 0) { + for (m_pSelectedSkin = m_pSkinListHead.nextSkin; m_pSelectedSkin->skinId != m_nFirstVisibleRowOnList; + m_pSelectedSkin = m_pSelectedSkin->nextSkin); + + int rowTextY = PLAYERSETUP_LIST_BODY_TOP - 1; + int orderInVisibles = 0; + int rowEndY = PLAYERSETUP_LIST_BODY_TOP + PLAYERSETUP_ROW_HEIGHT + 1; + int rowStartY = PLAYERSETUP_LIST_BODY_TOP; + for (int rowIdx = m_nFirstVisibleRowOnList; + rowIdx < m_nFirstVisibleRowOnList + MAX_VISIBLE_LIST_ROW && m_pSelectedSkin; ) { + + if (m_nMousePosX > MENU_X_LEFT_ALIGNED(PLAYERSETUP_LIST_LEFT) && m_nMousePosX < MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT)) { + if (m_nMousePosY > MENU_Y(rowStartY) && m_nMousePosY < MENU_Y(rowEndY)) { + m_nOptionMouseHovering = rowIdx; + if (m_nMouseOldPosX != m_nMousePosX || m_nMouseOldPosY != m_nMousePosY) { + m_nCurrExLayer = HOVEROPTION_LIST; + } + if (m_nHoverOption == HOVEROPTION_SKIN) { + if (rowIdx == m_nSelectedListRow) { + m_nHoverOption = HOVEROPTION_NOT_HOVERING; + if (m_nSkinsTotal > 0) { + strcpy(m_PrefsSkinFile, m_aSkinName); + CWorld::Players[0].SetPlayerSkin(m_PrefsSkinFile); + SaveSettings(); + } + } else { + m_nCurrExLayer = HOVEROPTION_LIST; + m_nSelectedListRow = rowIdx; + m_nHoverOption = HOVEROPTION_NOT_HOVERING; + } + } + } + } + + // Preview skin/change color of row when we focused on another row. + if (orderInVisibles == m_nSelectedListRow - m_nFirstVisibleRowOnList) { + CFont::SetColor(CRGBA(255, 255, 255, FadeIn(255))); + static int lastSelectedSkin = -1; + if (m_nSelectedListRow != lastSelectedSkin) { + strcpy(m_aSkinName, m_pSelectedSkin->skinNameOriginal); + CWorld::Players[0].SetPlayerSkin(m_aSkinName); + } + lastSelectedSkin = m_nSelectedListRow; + } else if (!strcmp(m_PrefsSkinFile, m_pSelectedSkin->skinNameOriginal)) { + CFont::SetColor(CRGBA(255, 255, 155, FadeIn(255))); + } else { + CFont::SetColor(CRGBA(LIST_OPTION_COLOR.r, LIST_OPTION_COLOR.g, LIST_OPTION_COLOR.b, FadeIn(LIST_OPTION_COLOR.a))); + } + wchar unicodeTemp[80]; + AsciiToUnicode(m_pSelectedSkin->skinNameDisplayed, unicodeTemp); + CFont::SetRightJustifyOff(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(PLAYERSETUP_SKIN_COLUMN_LEFT), MENU_Y(rowTextY), unicodeTemp); + + // If not "Default skin" option + if (rowIdx != 0) { + char dateTemp[32]; + sprintf(dateTemp, "%s", m_pSelectedSkin->date); + AsciiToUnicode(dateTemp, unicodeTemp); + CFont::SetRightJustifyOn(); + CFont::PrintString(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_DATE_COLUMN_RIGHT), MENU_Y(rowTextY), unicodeTemp); + } + ++orderInVisibles; + rowEndY += PLAYERSETUP_ROW_HEIGHT; + rowStartY += PLAYERSETUP_ROW_HEIGHT; + rowTextY += PLAYERSETUP_ROW_HEIGHT; + ++rowIdx; + m_pSelectedSkin = m_pSelectedSkin->nextSkin; + } + // Scrollbar background - it's unchanged since III and still yellowish... + CSprite2d::DrawRect(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2), MENU_Y(PLAYERSETUP_LIST_TOP), + MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2 - PLAYERSETUP_SCROLLBAR_WIDTH), SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM)), CRGBA(100, 100, 66, FadeIn(205))); + + float scrollbarHeight = SCROLLBAR_MAX_HEIGHT / (m_nSkinsTotal / (float) MAX_VISIBLE_LIST_ROW); + float scrollbarBottom, scrollbarTop; + if (m_nSkinsTotal <= MAX_VISIBLE_LIST_ROW) { + scrollbarBottom = SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM + PLAYERSETUP_SCROLLBUTTON_HEIGHT + 4.0f); + scrollbarTop = MENU_Y(PLAYERSETUP_LIST_BODY_TOP); + + // Scrollbar shadow + CSprite2d::DrawRect(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 4), scrollbarTop, + MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 1 - PLAYERSETUP_SCROLLBAR_WIDTH), scrollbarBottom + MENU_Y(1.0f)), CRGBA(50, 50, 50, FadeIn(255))); + } else { +#ifdef FIX_BUGS + scrollbarBottom = MENU_Y(PLAYERSETUP_LIST_BODY_TOP - 8 + m_nScrollbarTopMargin + scrollbarHeight); + scrollbarTop = MENU_Y(PLAYERSETUP_LIST_BODY_TOP + m_nScrollbarTopMargin); +#else + scrollbarBottom = MENU_Y(PLAYERSETUP_LIST_BODY_TOP - 4 + m_nScrollbarTopMargin + scrollbarHeight - SCROLLBAR_MAX_HEIGHT / m_nSkinsTotal); + scrollbarTop = MENU_Y(SCROLLBAR_MAX_HEIGHT / m_nSkinsTotal + PLAYERSETUP_LIST_BODY_TOP - 3 + m_nScrollbarTopMargin); +#endif + // Scrollbar shadow + CSprite2d::DrawRect(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 4), scrollbarTop, + MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 1 - PLAYERSETUP_SCROLLBAR_WIDTH), scrollbarBottom + MENU_Y(1.0f)), + CRGBA(50, 50, 50, FadeIn(255))); + + } + // Scrollbar + CSprite2d::DrawRect(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 4), scrollbarTop, + MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - PLAYERSETUP_SCROLLBAR_WIDTH), scrollbarBottom), + CRGBA(SCROLLBAR_COLOR.r, SCROLLBAR_COLOR.g, SCROLLBAR_COLOR.b, FadeIn(255))); + + // FIX: Scroll button dimensions are buggy, because: + // 1 - stretches the original image + // 2 - leaves gap between button and scrollbar + if (m_nHoverOption == HOVEROPTION_CLICKED_SCROLL_UP) { +#ifdef FIX_BUGS + m_aFrontEndSprites[MENUSPRITE_UPON].Draw(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2), MENU_Y(PLAYERSETUP_LIST_TOP), + MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2 - PLAYERSETUP_SCROLLBUTTON_TXD_DIMENSION), MENU_Y(PLAYERSETUP_LIST_TOP + PLAYERSETUP_SCROLLBUTTON_TXD_DIMENSION)), + CRGBA(255, 255, 255, FadeIn(255))); +#else + m_aFrontEndSprites[MENUSPRITE_UPON].Draw(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2), MENU_Y(PLAYERSETUP_LIST_TOP), + MENU_X_RIGHT_ALIGNED(-20.0f), MENU_Y(PLAYERSETUP_LIST_TOP + 58)), + CRGBA(255, 255, 255, FadeIn(255))); +#endif + } else { +#ifdef FIX_BUGS + m_aFrontEndSprites[MENUSPRITE_UPOFF].Draw(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 3), MENU_Y(PLAYERSETUP_LIST_TOP), + MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 3 - PLAYERSETUP_SCROLLBUTTON_TXD_DIMENSION), MENU_Y(PLAYERSETUP_LIST_TOP + PLAYERSETUP_SCROLLBUTTON_TXD_DIMENSION)), + CRGBA(255, 255, 255, FadeIn(255))); +#else + m_aFrontEndSprites[MENUSPRITE_UPOFF].Draw(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 3), MENU_Y(PLAYERSETUP_LIST_TOP), + MENU_X_RIGHT_ALIGNED(-21.0f), MENU_Y(PLAYERSETUP_LIST_TOP + 58)), + CRGBA(255, 255, 255, FadeIn(255))); +#endif + } + + if (m_nHoverOption == HOVEROPTION_CLICKED_SCROLL_DOWN) { +#ifdef FIX_BUGS + m_aFrontEndSprites[MENUSPRITE_DOWNON].Draw(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2), SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM + PLAYERSETUP_SCROLLBUTTON_HEIGHT + 1), + MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2 - PLAYERSETUP_SCROLLBUTTON_TXD_DIMENSION), SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM + PLAYERSETUP_SCROLLBUTTON_HEIGHT + 1 - PLAYERSETUP_SCROLLBUTTON_TXD_DIMENSION)), + CRGBA(255, 255, 255, FadeIn(255))); +#else + m_aFrontEndSprites[MENUSPRITE_DOWNON].Draw(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2), SCREEN_SCALE_FROM_BOTTOM(141.0f), + MENU_X_RIGHT_ALIGNED(-20.0f), SCREEN_SCALE_FROM_BOTTOM(83.0f)), + CRGBA(255, 255, 255, FadeIn(255))); +#endif + } else { +#ifdef FIX_BUGS + m_aFrontEndSprites[MENUSPRITE_DOWNOFF].Draw(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 3), SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM + PLAYERSETUP_SCROLLBUTTON_HEIGHT + 1), + MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 3 - PLAYERSETUP_SCROLLBUTTON_TXD_DIMENSION), SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM + PLAYERSETUP_SCROLLBUTTON_HEIGHT + 1 - PLAYERSETUP_SCROLLBUTTON_TXD_DIMENSION)), + CRGBA(255, 255, 255, FadeIn(255))); +#else + m_aFrontEndSprites[MENUSPRITE_DOWNOFF].Draw(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 3), SCREEN_SCALE_FROM_BOTTOM(141.0f), + MENU_X_RIGHT_ALIGNED(-21.0f), SCREEN_SCALE_FROM_BOTTOM(83.0f)), + CRGBA(255, 255, 255, FadeIn(255))); +#endif + + } + if (activeScreen) + CPlayerSkin::RenderFrontendSkinEdit(); + + // Big apply button + if (strcmp(m_aSkinName, m_PrefsSkinFile) != 0) { + CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); + switch (m_PrefsLanguage) { + case LANGUAGE_FRENCH: + CFont::SetScale(MENU_X(1.1f), MENU_Y(1.9f)); + break; + case LANGUAGE_GERMAN: + CFont::SetScale(MENU_X(0.85f), MENU_Y(1.9f)); + break; + case LANGUAGE_ITALIAN: + case LANGUAGE_SPANISH: + CFont::SetScale(MENU_X(1.4f), MENU_Y(1.9f)); + break; + default: + CFont::SetScale(MENU_X(1.9f), MENU_Y(1.9f)); + break; + } + CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(120))); + CFont::SetRightJustifyOff(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(24.0f), MENU_Y(220.0f), TheText.Get("FET_APP")); + } + CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); + + CFont::SetScale(MENU_X(BIGTEXT_X_SCALE), MENU_Y(BIGTEXT_Y_SCALE)); + + if ((m_nMousePosX > MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 1) - CFont::GetStringWidth(TheText.Get("FEDS_TB"), true) + && m_nMousePosX < MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 1) + && m_nMousePosY > SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM - 3) + && m_nMousePosY < SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM - 26)) + || m_nCurrExLayer == HOVEROPTION_BACK) { + + m_nHoverOption = HOVEROPTION_BACK; + } else if ((strcmp(m_aSkinName, m_PrefsSkinFile) != 0 + && m_nMousePosX > MENU_X_LEFT_ALIGNED(PLAYERSETUP_LIST_LEFT) + && m_nMousePosX < MENU_X_LEFT_ALIGNED(PLAYERSETUP_LIST_LEFT) + CFont::GetStringWidth(TheText.Get("FES_SET"), true) + && m_nMousePosY > SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM - 3) + && m_nMousePosY < SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM - 26)) + || m_nCurrExLayer == HOVEROPTION_USESKIN) { + + m_nHoverOption = HOVEROPTION_USESKIN; + } else if (m_nMousePosX > MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2) + && m_nMousePosX < MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - PLAYERSETUP_SCROLLBAR_WIDTH - 2) + && m_nMousePosY > MENU_Y(PLAYERSETUP_LIST_TOP) + && m_nMousePosY < MENU_Y(PLAYERSETUP_LIST_BODY_TOP - 3)) { + if (m_nHoverOption != HOVEROPTION_CLICKED_SCROLL_UP && m_nHoverOption != HOVEROPTION_CLICKED_SCROLL_DOWN) + m_nHoverOption = HOVEROPTION_OVER_SCROLL_UP; + + } else if (m_nMousePosX > MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2) + && m_nMousePosX < MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - PLAYERSETUP_SCROLLBAR_WIDTH - 2) + && m_nMousePosY > SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM + PLAYERSETUP_SCROLLBUTTON_HEIGHT + 1) + && m_nMousePosY < SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM)) { + if (m_nHoverOption != HOVEROPTION_CLICKED_SCROLL_UP && m_nHoverOption != HOVEROPTION_CLICKED_SCROLL_DOWN) + m_nHoverOption = HOVEROPTION_OVER_SCROLL_DOWN; + + } else if (m_nMousePosX > MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2) + && m_nMousePosX < MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - PLAYERSETUP_SCROLLBAR_WIDTH - 2) + && m_nMousePosY > MENU_Y(PLAYERSETUP_LIST_BODY_TOP - 3) +#ifdef FIX_BUGS + && m_nMousePosY < MENU_Y(PLAYERSETUP_LIST_BODY_TOP + m_nScrollbarTopMargin)) { +#else + && m_nMousePosY < MENU_Y(SCROLLBAR_MAX_HEIGHT / m_nTotalListRow + PLAYERSETUP_LIST_BODY_TOP - 3 + m_nScrollbarTopMargin)) { +#endif + m_nHoverOption = HOVEROPTION_PAGEUP; + + } else if (m_nMousePosX > MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2) + && m_nMousePosX < MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - PLAYERSETUP_SCROLLBAR_WIDTH - 2) +#ifdef FIX_BUGS + && m_nMousePosY > MENU_Y(PLAYERSETUP_LIST_BODY_TOP - 8 + m_nScrollbarTopMargin + scrollbarHeight) +#else + && m_nMousePosY > MENU_Y(PLAYERSETUP_LIST_BODY_TOP - 3 + m_nScrollbarTopMargin + scrollbarHeight - SCROLLBAR_MAX_HEIGHT / m_nTotalListRow) +#endif + && m_nMousePosY < SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM + PLAYERSETUP_SCROLLBUTTON_HEIGHT + 1)) { + m_nHoverOption = HOVEROPTION_PAGEDOWN; + + } else if (m_nMousePosX > MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 4) + && m_nMousePosX < MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - PLAYERSETUP_SCROLLBAR_WIDTH) +#ifdef FIX_BUGS + && m_nMousePosY > MENU_Y(PLAYERSETUP_LIST_BODY_TOP + m_nScrollbarTopMargin) + && m_nMousePosY < MENU_Y(PLAYERSETUP_LIST_BODY_TOP - 8 + m_nScrollbarTopMargin + scrollbarHeight)) { +#else + && m_nMousePosY > MENU_Y(SCROLLBAR_MAX_HEIGHT / m_nTotalListRow + PLAYERSETUP_LIST_BODY_TOP - 3 + m_nScrollbarTopMargin) + && m_nMousePosY < MENU_Y(PLAYERSETUP_LIST_BODY_TOP - 3 + m_nScrollbarTopMargin + scrollbarHeight - SCROLLBAR_MAX_HEIGHT / m_nTotalListRow)) { +#endif + m_nHoverOption = HOVEROPTION_HOLDING_SCROLLBAR; + + } else if (m_nMousePosX > MENU_X_LEFT_ALIGNED(PLAYERSETUP_LIST_LEFT) && m_nMousePosX < MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT) + && m_nMousePosY > MENU_Y(PLAYERSETUP_LIST_BODY_TOP + 1) && m_nMousePosY < SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM)) { + m_nHoverOption = HOVEROPTION_LIST; + + } else { + m_nHoverOption = HOVEROPTION_NOT_HOVERING; + } + } + CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); + CFont::SetScale(MENU_X(BIGTEXT_X_SCALE), MENU_Y(BIGTEXT_Y_SCALE)); + CFont::SetRightJustifyOn(); + CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(255))); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(255))); + + // Back button + CFont::PrintString(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 3), SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM - 5), TheText.Get("FEDS_TB")); + CFont::SetRightJustifyOff(); + + if (!strcmp(m_aSkinName, m_PrefsSkinFile)) { + CFont::SetColor(CRGBA(DARKMENUOPTION_COLOR.r, DARKMENUOPTION_COLOR.g, DARKMENUOPTION_COLOR.b, FadeIn(255))); + } else { + CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(255))); + } + + // Use skin button + CFont::PrintString(MENU_X_LEFT_ALIGNED(PLAYERSETUP_LIST_LEFT), SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM - 5), TheText.Get("FES_SET")); + CFont::SetDropShadowPosition(0); +} + +int +CMenuManager::FadeIn(int alpha) +{ + return Min(m_nMenuFadeAlpha, alpha); +} + +int +CMenuManager::GetStartOptionsCntrlConfigScreens() +{ + int number = 0; + switch (m_nCurrScreen) { +#ifdef LEGACY_MENU_OPTIONS + case MENUPAGE_CONTROLLER_PC_OLD3: + number = 34; + break; + case MENUPAGE_CONTROLLER_DEBUG: + number = 35; + break; +#endif + case MENUPAGE_KEYBOARD_CONTROLS: + number = 0; + break; + default: + break; + } + return number; +} + +void +CMenuManager::InitialiseChangedLanguageSettings() +{ + if (m_bFrontEnd_ReloadObrTxtGxt) { + m_bFrontEnd_ReloadObrTxtGxt = false; +#ifdef FIX_BUGS + if (gGameState > GS_INIT_ONCE) +#endif + CTimer::Stop(); + TheText.Unload(); + TheText.Load(); +#ifdef FIX_BUGS + if (gGameState > GS_INIT_ONCE) +#endif + CTimer::Update(); + CGame::frenchGame = false; + CGame::germanGame = false; +#ifdef MORE_LANGUAGES + CGame::russianGame = false; + CGame::japaneseGame = false; + switch (m_PrefsLanguage) { + case LANGUAGE_POLISH: + CFont::ReloadFonts(FONT_LANGSET_POLISH); + break; + case LANGUAGE_RUSSIAN: + CFont::ReloadFonts(FONT_LANGSET_RUSSIAN); + break; + case LANGUAGE_JAPANESE: + CFont::ReloadFonts(FONT_LANGSET_JAPANESE); + break; + default: + CFont::ReloadFonts(FONT_LANGSET_EFIGS); + break; + } +#endif + + switch (m_PrefsLanguage) { + case LANGUAGE_FRENCH: + CGame::frenchGame = true; + break; + case LANGUAGE_GERMAN: + CGame::germanGame = true; + break; +#ifdef MORE_LANGUAGES + case LANGUAGE_RUSSIAN: + CGame::russianGame = true; + break; + case LANGUAGE_JAPANESE: + CGame::japaneseGame = true; + break; +#endif + default: + break; + } + + } +} + +void +CMenuManager::LoadAllTextures() +{ + if (m_bSpritesLoaded) + return; + + // First icon is hidden behind arrow + m_LeftMostRadioX = MENU_X_LEFT_ALIGNED(MENURADIO_ICON_FIRST_X - MENURADIO_ICON_SIZE); + CTimer::Stop(); + + CStreaming::MakeSpaceFor(350 * CDSTREAM_SECTOR_SIZE); // twice of it in mobile + CStreaming::ImGonnaUseStreamingMemory(); + CGame::TidyUpMemory(false, true); + CTxdStore::PushCurrentTxd(); + int frontendTxdSlot1 = CTxdStore::FindTxdSlot("frontend1"); + + if(frontendTxdSlot1 == -1) + frontendTxdSlot1 = CTxdStore::AddTxdSlot("frontend1"); + + printf("LOAD frontend1\n"); + CTxdStore::LoadTxd(frontendTxdSlot1, "MODELS/FRONTEN1.TXD"); + CTxdStore::AddRef(frontendTxdSlot1); + CTxdStore::SetCurrentTxd(frontendTxdSlot1); + + for (int i = 0; i < 3; i++) { + m_aFrontEndSprites[i].SetTexture(FrontendFilenames[i][0], FrontendFilenames[i][1]); + m_aFrontEndSprites[i].SetAddressing(rwTEXTUREADDRESSBORDER); + } + + CTxdStore::PopCurrentTxd(); + CStreaming::IHaveUsedStreamingMemory(); + + if (!m_OnlySaveMenu) { + CStreaming::MakeSpaceFor(692 * CDSTREAM_SECTOR_SIZE); // twice of it in mobile + CStreaming::ImGonnaUseStreamingMemory(); + CTxdStore::PushCurrentTxd(); + + int frontendTxdSlot2 = CTxdStore::FindTxdSlot("frontend2"); + + if (frontendTxdSlot2 == -1) + frontendTxdSlot2 = CTxdStore::AddTxdSlot("frontend2"); + + printf("LOAD frontend2\n"); + CTxdStore::LoadTxd(frontendTxdSlot2, "MODELS/FRONTEN2.TXD"); + CTxdStore::AddRef(frontendTxdSlot2); + CTxdStore::SetCurrentTxd(frontendTxdSlot2); + +#ifdef GAMEPAD_MENU + for (int i = 3; i < MENUSPRITE_CONTROLLER; i++) { +#else + for (int i = 3; i < NUM_MENU_SPRITES; i++) { +#endif + m_aFrontEndSprites[i].SetTexture(FrontendFilenames[i][0], FrontendFilenames[i][1]); + m_aFrontEndSprites[i].SetAddressing(rwTEXTUREADDRESSBORDER); + } + + CTxdStore::PopCurrentTxd(); +#ifdef GAMEPAD_MENU + LoadController(m_PrefsControllerType); +#endif + CStreaming::IHaveUsedStreamingMemory(); + } + + m_bSpritesLoaded = true; + CTimer::Update(); +} + +void +CMenuManager::LoadSettings() +{ + CFileMgr::SetDirMyDocuments(); + int fileHandle = CFileMgr::OpenFile("gta_vc.set", "r"); + + int32 prevLang = m_PrefsLanguage; + MousePointerStateHelper.bInvertVertically = true; + CMBlur::BlurOn = false; + + // 50 is silly + char headerText[50]; + int someVersion = 0; + bool fileIsValid = true; + + if (fileHandle) { + CFileMgr::Read(fileHandle, headerText, 29); + + if (strncmp(headerText, TopLineEmptyFile, sizeof(TopLineEmptyFile) - 1) == 0) { + fileIsValid = false; + } else { + CFileMgr::Seek(fileHandle, 0, 0); + CFileMgr::Read(fileHandle, (char*)&someVersion, sizeof(someVersion)); + } + if (fileIsValid && someVersion >= 3) { + ControlsManager.LoadSettings(fileHandle); +#ifdef IMPROVED_VIDEOMODE + CFileMgr::Read(fileHandle, (char*)&m_nPrefsWidth, sizeof(m_nPrefsWidth)); + CFileMgr::Read(fileHandle, (char*)&m_nPrefsHeight, sizeof(m_nPrefsHeight)); + CFileMgr::Read(fileHandle, (char*)&m_nPrefsDepth, sizeof(m_nPrefsDepth)); + CFileMgr::Read(fileHandle, (char*)&m_nPrefsWindowed, sizeof(m_nPrefsWindowed)); + CFileMgr::Read(fileHandle, (char*)&m_nPrefsSubsystem, sizeof(m_nPrefsSubsystem)); + if(m_nPrefsWindowed != 0 && m_nPrefsWindowed != 1){ + // garbage data from vanilla settings file + // let skeleton find something + m_nPrefsWidth = 0; + m_nPrefsHeight = 0; + m_nPrefsDepth = 0; + m_nPrefsWindowed = 0; + m_nPrefsSubsystem = 0; + } + m_nSelectedScreenMode = m_nPrefsWindowed; +#else + CFileMgr::Read(fileHandle, gString, 20); +#endif + CFileMgr::Read(fileHandle, gString, 20); + CFileMgr::Read(fileHandle, gString, 4); + CFileMgr::Read(fileHandle, gString, 4); + CFileMgr::Read(fileHandle, gString, 1); +#ifdef LEGACY_MENU_OPTIONS + CFileMgr::Read(fileHandle, (char*)&m_PrefsVsyncDisp, 1); + CFileMgr::Read(fileHandle, (char*)&CMBlur::BlurOn, 1); +#else + CFileMgr::Read(fileHandle, gString, 1); + CFileMgr::Read(fileHandle, gString, 1); +#endif + CFileMgr::Read(fileHandle, (char*)&TheCamera.m_bHeadBob, 1); + CFileMgr::Read(fileHandle, (char*)&TheCamera.m_fMouseAccelHorzntl, 4); + CFileMgr::Read(fileHandle, (char*)&MousePointerStateHelper.bInvertVertically, 1); + CFileMgr::Read(fileHandle, (char*)&CVehicle::m_bDisableMouseSteering, 1); + CFileMgr::Read(fileHandle, (char*)&m_PrefsSfxVolume, 1); + CFileMgr::Read(fileHandle, (char*)&m_PrefsMusicVolume, 1); + CFileMgr::Read(fileHandle, (char*)&m_PrefsMP3BoostVolume, 1); + CFileMgr::Read(fileHandle, (char*)&m_PrefsRadioStation, 1); + CFileMgr::Read(fileHandle, (char*)&m_PrefsSpeakers, 1); + CFileMgr::Read(fileHandle, (char*)&m_nPrefsAudio3DProviderIndex, 1); + CFileMgr::Read(fileHandle, (char*)&m_PrefsDMA, 1); + CFileMgr::Read(fileHandle, (char*)&m_PrefsBrightness, 2); + CFileMgr::Read(fileHandle, (char*)&m_PrefsLOD, sizeof(m_PrefsLOD)); + CFileMgr::Read(fileHandle, (char*)&m_PrefsShowSubtitles, 1); + CFileMgr::Read(fileHandle, (char*)&m_PrefsUseWideScreen, 1); + CFileMgr::Read(fileHandle, (char*)&m_PrefsFrameLimiter, 1); + CFileMgr::Read(fileHandle, (char*)&m_nDisplayVideoMode, 1); + CFileMgr::Read(fileHandle, m_PrefsSkinFile, 256); + CFileMgr::Read(fileHandle, (char*)&m_ControlMethod, 1); + CFileMgr::Read(fileHandle, (char*)&m_PrefsLanguage, 1); + CFileMgr::Read(fileHandle, (char*)&m_PrefsShowHud, 1); + CFileMgr::Read(fileHandle, (char*)&m_PrefsRadarMode, 1); + CFileMgr::Read(fileHandle, (char*)&m_PrefsShowLegends, 1); + } + } + + CFileMgr::CloseFile(fileHandle); + CFileMgr::SetDir(""); + +#ifdef LOAD_INI_SETTINGS + if (LoadINISettings()) { + LoadINIControllerSettings(); + } +#endif + +#ifdef FIX_BUGS + TheCamera.m_fMouseAccelVertical = TheCamera.m_fMouseAccelHorzntl + 0.0005f; +#endif +#ifdef PC_PLAYER_CONTROLS + CCamera::m_bUseMouse3rdPerson = m_ControlMethod == CONTROL_STANDARD; +#endif +#ifdef LEGACY_MENU_OPTIONS + m_PrefsVsync = m_PrefsVsyncDisp; +#endif + CRenderer::ms_lodDistScale = m_PrefsLOD; + + if (m_nPrefsAudio3DProviderIndex == NO_AUDIO_PROVIDER) + m_nPrefsAudio3DProviderIndex = -2; + + m_lastWorking3DAudioProvider = m_nPrefsAudio3DProviderIndex; + + if (m_PrefsLanguage == prevLang) + m_bLanguageLoaded = false; + else { + m_bLanguageLoaded = true; + TheText.Unload(); + TheText.Load(); + m_bFrontEnd_ReloadObrTxtGxt = true; + InitialiseChangedLanguageSettings(); + + OutputDebugString("The previously saved language is now in use"); + } + + WIN32_FIND_DATA FindFileData; + char skinfile[256+16]; // Stack analysis shows 16 bits gap, but I don't trust it. It may very well be MAX_PATH(260). + bool SkinFound = false; + HANDLE handle = FindFirstFile("skins\\*.bmp", &FindFileData); + for (int i = 1; handle != INVALID_HANDLE_VALUE && i; i = FindNextFile(handle, &FindFileData)) { + strcpy(skinfile, m_PrefsSkinFile); + strcat(skinfile, ".bmp"); + if (strcmp(FindFileData.cFileName, skinfile) == 0) + SkinFound = true; + } + FindClose(handle); + + if (!SkinFound) { + OutputDebugString("Default skin set as no other skins are available OR saved skin not found!"); + strcpy(m_PrefsSkinFile, DEFAULT_SKIN_NAME); + strcpy(m_aSkinName, DEFAULT_SKIN_NAME); + } +} + +void +CMenuManager::SaveSettings() +{ +#ifndef LOAD_INI_SETTINGS + static char RubbishString[48] = "stuffmorestuffevenmorestuff etc"; +#ifdef BIND_VEHICLE_FIREWEAPON + static int SomeVersion = 4; +#else + static int SomeVersion = 3; +#endif + + CFileMgr::SetDirMyDocuments(); + + int fileHandle = CFileMgr::OpenFile("gta_vc.set", "w+"); + if (fileHandle) { + CFileMgr::Write(fileHandle, (char*)&SomeVersion, sizeof(SomeVersion)); + ControlsManager.SaveSettings(fileHandle); +#ifdef IMPROVED_VIDEOMODE + CFileMgr::Write(fileHandle, (char*)&m_nPrefsWidth, sizeof(m_nPrefsWidth)); + CFileMgr::Write(fileHandle, (char*)&m_nPrefsHeight, sizeof(m_nPrefsHeight)); + CFileMgr::Write(fileHandle, (char*)&m_nPrefsDepth, sizeof(m_nPrefsDepth)); + CFileMgr::Write(fileHandle, (char*)&m_nPrefsWindowed, sizeof(m_nPrefsWindowed)); + CFileMgr::Write(fileHandle, (char*)&m_nPrefsSubsystem, sizeof(m_nPrefsSubsystem)); +#else + CFileMgr::Write(fileHandle, RubbishString, 20); +#endif + CFileMgr::Write(fileHandle, RubbishString, 20); + CFileMgr::Write(fileHandle, RubbishString, 4); + CFileMgr::Write(fileHandle, RubbishString, 4); + CFileMgr::Write(fileHandle, RubbishString, 1); +#ifdef LEGACY_MENU_OPTIONS + CFileMgr::Write(fileHandle, (char*)&m_PrefsVsyncDisp, 1); + CFileMgr::Write(fileHandle, (char*)&CMBlur::BlurOn, 1); +#else + CFileMgr::Write(fileHandle, RubbishString, 1); + CFileMgr::Write(fileHandle, RubbishString, 1); +#endif + CFileMgr::Write(fileHandle, (char*)&TheCamera.m_bHeadBob, 1); + CFileMgr::Write(fileHandle, (char*)&TheCamera.m_fMouseAccelHorzntl, 4); + CFileMgr::Write(fileHandle, (char*)&MousePointerStateHelper.bInvertVertically, 1); + CFileMgr::Write(fileHandle, (char*)&CVehicle::m_bDisableMouseSteering, 1); + CFileMgr::Write(fileHandle, (char*)&m_PrefsSfxVolume, 1); + CFileMgr::Write(fileHandle, (char*)&m_PrefsMusicVolume, 1); + CFileMgr::Write(fileHandle, (char*)&m_PrefsMP3BoostVolume, 1); + CFileMgr::Write(fileHandle, (char*)&m_PrefsRadioStation, 1); + CFileMgr::Write(fileHandle, (char*)&m_PrefsSpeakers, 1); + CFileMgr::Write(fileHandle, (char*)&m_nPrefsAudio3DProviderIndex, 1); + CFileMgr::Write(fileHandle, (char*)&m_PrefsDMA, 1); + CFileMgr::Write(fileHandle, (char*)&m_PrefsBrightness, 2); + CFileMgr::Write(fileHandle, (char*)&m_PrefsLOD, sizeof(m_PrefsLOD)); + CFileMgr::Write(fileHandle, (char*)&m_PrefsShowSubtitles, 1); + CFileMgr::Write(fileHandle, (char*)&m_PrefsUseWideScreen, 1); + CFileMgr::Write(fileHandle, (char*)&m_PrefsFrameLimiter, 1); + CFileMgr::Write(fileHandle, (char*)&m_nPrefsVideoMode, 1); + CFileMgr::Write(fileHandle, m_PrefsSkinFile, 256); + CFileMgr::Write(fileHandle, (char*)&m_ControlMethod, 1); + CFileMgr::Write(fileHandle, (char*)&m_PrefsLanguage, 1); + CFileMgr::Write(fileHandle, (char*)&m_PrefsShowHud, 1); + CFileMgr::Write(fileHandle, (char*)&m_PrefsRadarMode, 1); + CFileMgr::Write(fileHandle, (char*)&m_PrefsShowLegends, 1); + } + m_lastWorking3DAudioProvider = m_nPrefsAudio3DProviderIndex; + + CFileMgr::CloseFile(fileHandle); + CFileMgr::SetDir(""); + +#else + m_lastWorking3DAudioProvider = m_nPrefsAudio3DProviderIndex; + SaveINISettings(); +#endif +} + +void +CMenuManager::MessageScreen(const char *text, bool blackBg) +{ + CSprite2d *splash = LoadSplash(nil); + if (!DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255)) + return; + + CSprite2d::SetRecipNearClip(); + CSprite2d::InitPerFrame(); + CFont::InitPerFrame(); + DefinedState(); + // CRGBA unused(255, 255, 255, 255); + if (blackBg) { + CSprite2d::DrawRect(CRect(0, SCREEN_HEIGHT, SCREEN_WIDTH, 0), CRGBA(0, 0, 0, 255)); + } + + m_nMenuFadeAlpha = 255; + SmallMessageScreen(text); + CFont::DrawFonts(); + DoRWStuffEndOfFrame(); +} + +void +CMenuManager::SmallMessageScreen(const char* text) +{ + CFont::SetBackgroundOff(); + CFont::SetPropOn(); + CFont::SetJustifyOn(); + CFont::SetBackGroundOnlyTextOn(); + CSprite2d::DrawRect(CRect(SCREEN_STRETCH_X(95.0f), SCREEN_SCALE_FROM_BOTTOM(165.0f), SCREEN_STRETCH_FROM_RIGHT(95.0f), SCREEN_SCALE_Y(115.0f)), CRGBA(50, 50, 50, FadeIn(210))); + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); + CFont::SetCentreSize(SCREEN_SCALE_X(430.0f)); + CFont::SetCentreOn(); + CFont::SetColor(CRGBA(LABEL_COLOR.r, LABEL_COLOR.g, LABEL_COLOR.b, FadeIn(255))); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + CFont::SetScale(SCREEN_SCALE_X(SMALLTEXT_X_SCALE), SCREEN_SCALE_Y(SMALLTEXT_Y_SCALE)); + + int numOfLines = CFont::GetNumberLines(SCREEN_WIDTH / 2.f, SCREEN_SCALE_Y(135.f), TheText.Get(text)); + float y; + if (numOfLines > 1) + y = SCREEN_SCALE_Y(192.f) - numOfLines * SCREEN_SCALE_Y(8.f); + else + y = SCREEN_SCALE_Y(182.f); + + CFont::PrintString(SCREEN_WIDTH / 2.f, y, TheText.Get(text)); +} + +void +CMenuManager::PrintBriefs() +{ + CFont::SetColor(CRGBA(0, 0, 0, FadeIn(255))); + CFont::SetRightJustifyOff(); + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); + CFont::SetScale(MENU_X(MEDIUMTEXT_X_SCALE), MENU_Y(MEDIUMTEXT_Y_SCALE)); + CFont::SetWrapx(MENU_X_RIGHT_ALIGNED(80.0f)); + CFont::SetDropShadowPosition(0); + + float nextY = BRIEFS_BOTTOM_MARGIN; + for (int i = 4; i >= 0; i--) { + if (nextY < BRIEFS_TOP_MARGIN) + break; + + tPreviousBrief &brief = CMessages::PreviousBriefs[i]; + if (brief.m_pText) { + CMessages::InsertNumberInString(brief.m_pText, + brief.m_nNumber[0], brief.m_nNumber[1], + brief.m_nNumber[2], brief.m_nNumber[3], + brief.m_nNumber[4], brief.m_nNumber[5], gUString); + CMessages::InsertStringInString(gUString, brief.m_pString); + CMessages::InsertPlayerControlKeysInString(gUString); + CFont::FilterOutTokensFromString(gUString); + + nextY -= CFont::GetNumberLines(MENU_X_LEFT_ALIGNED(BRIEFS_LINE_X), nextY, gUString) * BRIEFS_LINE_HEIGHT + BRIEFS_LINE_SPACING; + CFont::PrintString(MENU_X_LEFT_ALIGNED(BRIEFS_LINE_X), MENU_Y(nextY), gUString); + } + } +} + +void +CMenuManager::PrintStats() +{ +#ifdef SECUROM + static uint8 statsPirateCheck = 0; +#endif + static float scrollY = 0; + + int rowNum = CStats::ConstructStatLine(99999); + CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(90.0f), MENU_Y(142.0f), + MENU_X_LEFT_ALIGNED(543.0f), MENU_Y(142.f), + MENU_X_LEFT_ALIGNED(107.0f), MENU_Y(316.f), + MENU_X_LEFT_ALIGNED(531.f), MENU_Y(299.f), CRGBA(LIST_BACKGROUND_COLOR.r, LIST_BACKGROUND_COLOR.g, LIST_BACKGROUND_COLOR.b, FadeIn(LIST_BACKGROUND_COLOR.a))); + + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); + CFont::SetPropOn(); + CFont::SetDropShadowPosition(0); + +#ifdef SECUROM + if (statsPirateCheck == 0) + // if not pirated game + // statsPirateCheck = 46; + // else + statsPirateCheck = 45; +#endif + + if (m_PrefsLanguage == LANGUAGE_AMERICAN) + CFont::SetScale(MENU_X(0.43f), MENU_Y(0.75f)); + else + CFont::SetScale(MENU_X(0.37f), MENU_Y(0.75f)); + + static uint32 lastCheck = 0; + + if (CTimer::GetTimeInMillisecondsPauseMode() - lastCheck > 40) { + + if (m_StatsScrollSpeed > 0.f) { + if (m_StatsScrollDirection == 0) + scrollY -= MENU_Y(100.f) / m_StatsScrollSpeed; + else + scrollY += MENU_Y(100.f) / m_StatsScrollSpeed; + } + lastCheck = CTimer::GetTimeInMillisecondsPauseMode(); + } + +#ifdef SECUROM + if (statsPirateCheck == 45) + return; +#endif + + float nextYChange, y, alpha; + + float totalHeight = (rowNum + 7) * STATS_ROW_HEIGHT; + for (int row = 0; row < rowNum; ++row) { + // Put faded away text at the top back to the bottom, in circular fashion + for (y = MENU_Y(STATS_ROW_HEIGHT) * row + MENU_Y(100.f) - scrollY; MENU_Y(STATS_FADING_AREA_LENGTH) > y; y += nextYChange) { + nextYChange = MENU_Y(totalHeight); + } + + // Put faded away text at the bottom back to the top + while (SCREEN_SCALE_FROM_BOTTOM(STATS_FADING_AREA_LENGTH) < y) { + y -= MENU_Y(totalHeight); + } + alpha = 0.f; + + // If it's still on screen + if (y > MENU_Y(STATS_VISIBLE_START_Y) && y < MENU_Y(STATS_VISIBLE_END_Y)) { + CStats::ConstructStatLine(row); + + // But about to dim from bottom + if (y < MENU_Y(STATS_BOTTOM_Y)) { + if (y > MENU_Y(STATS_BOTTOM_Y - STATS_FADING_AREA_LENGTH)) + alpha = (MENU_Y(STATS_BOTTOM_Y) - y) * 5.f; + } + + // About to dim from top + if (y > MENU_Y(STATS_TOP_Y)) { + if (y < MENU_Y(STATS_TOP_Y + STATS_FADING_AREA_LENGTH)) + alpha = (y - MENU_Y(STATS_TOP_Y)) * 5.f; + } + + // Content + if (y >= MENU_Y(STATS_TOP_Y + STATS_FADING_AREA_LENGTH) && y <= MENU_Y(STATS_BOTTOM_Y - STATS_FADING_AREA_LENGTH)) + alpha = 255.0f; + + CFont::SetColor(CRGBA(0, 0, 0, FadeIn(Min(255.f, alpha)))); + CFont::SetRightJustifyOff(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(STATS_ROW_LEFT_MARGIN), y, gUString); + CFont::SetRightJustifyOn(); + CFont::PrintString(MENU_X_RIGHT_ALIGNED(STATS_ROW_RIGHT_MARGIN), y, gUString2); + } + } + CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(255))); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(255))); + CFont::SetCentreOn(); + CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); + CFont::SetScale(MENU_X(0.65f), MENU_Y(1.05f)); + CFont::PrintString(MENU_X_LEFT_ALIGNED(STATS_RATING_X), MENU_Y(STATS_RATING_Y_1), TheText.Get("CRIMRA")); + + CFont::SetCentreOff(); + CFont::SetRightJustifyOff(); + + // FIX: Game does that in a weird way, alignment and spacing is now ok + + sprintf(gString, "(%d)", CStats::FindCriminalRatingNumber()); + AsciiToUnicode(gString, gUString); + + UnicodeStrcpy(gUString2, CStats::FindCriminalRatingString()); + UnicodeStrcat(gUString2, gUString); + + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); +#ifndef FIX_BUGS + CFont::SetScale(MENU_X(0.5f), MENU_Y(0.9f)); +#else + CFont::SetScale(MENU_X(SMALLTEXT_X_SCALE), MENU_Y(SMALLTEXT_Y_SCALE)); +#endif + CFont::SetColor(CRGBA(0, 0, 0, FadeIn(255))); + CFont::SetDropShadowPosition(0); + + CFont::PrintString(MENU_X_LEFT_ALIGNED(STATS_RATING_X) - CFont::GetStringWidth(gUString2, true) / 2.f, MENU_Y(STATS_RATING_Y_2), gUString2); +} + +void +CMenuManager::Process(void) +{ +#ifdef XBOX_MESSAGE_SCREEN + ProcessDialogTimer(); +#endif + + if (TheCamera.GetScreenFadeStatus() != FADE_0) + return; + + InitialiseChangedLanguageSettings(); + + if (m_bMenuActive) { + UserInput(); + ProcessFileActions(); + DMAudio.Service(); +#ifdef USE_TEXTURE_POOL + // TODO +#endif + } + + SwitchMenuOnAndOff(); +} + +#ifdef MAP_ENHANCEMENTS +#define ZOOM(x, y, in) \ + do { \ + if(m_fMapSize >= MENU_Y(1000.0f) && in) \ + break; \ + float z2 = in? 1.1f : 1.f/1.1f; \ + m_fMapCenterX += (x - m_fMapCenterX) * (1.0f - z2); \ + m_fMapCenterY += (y - m_fMapCenterY) * (1.0f - z2); \ + \ + if (m_fMapSize <= MENU_Y(MAP_MIN_SIZE) && !in) \ + break; \ + \ + m_fMapSize *= z2; \ + m_fMapCenterX = Clamp(m_fMapCenterX, SCREEN_WIDTH/2 - (m_fMapSize - MENU_X(MAP_MIN_SIZE)), m_fMapSize - MENU_X(MAP_MIN_SIZE) + SCREEN_WIDTH/2); \ + m_fMapCenterY = Clamp(m_fMapCenterY, SCREEN_HEIGHT/2 - (m_fMapSize - MENU_Y(MAP_MIN_SIZE)), m_fMapSize - MENU_Y(MAP_MIN_SIZE) + SCREEN_HEIGHT/2); \ + } while(0) + +#endif + +// Handles Map, Audio and Stats +void +CMenuManager::AdditionalOptionInput(bool &goBack) +{ + switch (m_nCurrScreen) { + case MENUPAGE_MAP: + { + static uint32 lastMapTick = 0; + + // FIX: All those macros were hardcoded values originally. + +#ifndef MAP_ENHANCEMENTS + if (CPad::GetPad(0)->GetMouseWheelUpJustDown() || CPad::GetPad(0)->GetMouseWheelUpJustUp() || CPad::GetPad(0)->GetPageUp() || CPad::GetPad(0)->GetRightShoulder1()) { + if (CTimer::GetTimeInMillisecondsPauseMode() - lastMapTick > 10) { + m_fMapSize = Min(MENU_Y(1000.0f), m_fMapSize + MENU_Y(15.f)); + } + } + if (CPad::GetPad(0)->GetMouseWheelDownJustDown() || CPad::GetPad(0)->GetMouseWheelDownJustUp() || CPad::GetPad(0)->GetPageDown() || CPad::GetPad(0)->GetRightShoulder2()) { + if (CTimer::GetTimeInMillisecondsPauseMode() - lastMapTick > 10) { + if (m_fMapSize > MENU_Y(MAP_MIN_SIZE)) { + if (m_fMapCenterY > SCREEN_HEIGHT/2) + m_fMapCenterY -= (m_fMapCenterY - SCREEN_HEIGHT/2) / ((m_fMapSize - MENU_Y(MAP_MIN_SIZE)) * 1/15.f); + + if (m_fMapCenterY < SCREEN_HEIGHT/2) + m_fMapCenterY += (SCREEN_HEIGHT/2 - m_fMapCenterY) / ((m_fMapSize - MENU_Y(MAP_MIN_SIZE)) * 1/15.f); + + if (m_fMapCenterX > SCREEN_WIDTH/2) + m_fMapCenterX -= (m_fMapCenterX - SCREEN_WIDTH/2) / ((m_fMapSize - MENU_X(MAP_MIN_SIZE)) * 1/15.f); + + if (m_fMapCenterX < SCREEN_WIDTH/2) + m_fMapCenterX += (SCREEN_WIDTH/2 - m_fMapCenterX) / ((m_fMapSize - MENU_X(MAP_MIN_SIZE)) * 1/15.f); + + m_fMapSize = Max(MENU_Y(MAP_MIN_SIZE), m_fMapSize - MENU_Y(15.f)); + m_fMapCenterX = Clamp(m_fMapCenterX, SCREEN_WIDTH/2 - (m_fMapSize - MENU_X(MAP_MIN_SIZE)), m_fMapSize - MENU_X(MAP_MIN_SIZE) + SCREEN_WIDTH/2); + m_fMapCenterY = Clamp(m_fMapCenterY, SCREEN_HEIGHT/2 - (m_fMapSize - MENU_Y(MAP_MIN_SIZE)), m_fMapSize - MENU_Y(MAP_MIN_SIZE) + SCREEN_HEIGHT/2); + } else { + m_fMapSize = MENU_Y(MAP_MIN_SIZE); + } + } + } +#else + // Adding marker + if (m_nMenuFadeAlpha == 255) { + if (CPad::GetPad(0)->GetRightMouseJustDown() || CPad::GetPad(0)->GetCrossJustDown()) { + if (mapCrosshair.y > m_fMapCenterY - m_fMapSize && mapCrosshair.y < m_fMapCenterY + m_fMapSize && + mapCrosshair.x > m_fMapCenterX - m_fMapSize && mapCrosshair.x < m_fMapCenterX + m_fMapSize) { + + // Don't ask me the meanings, I don't know. Found them by trying + float diffX = m_fMapCenterX - m_fMapSize, diffY = m_fMapCenterY - m_fMapSize; + float x = ((mapCrosshair.x - diffX) / (m_fMapSize * 2)) * (WORLD_SIZE_X / MENU_MAP_WIDTH_SCALE) - (WORLD_SIZE_X / 2 + MENU_MAP_LEFT_OFFSET * MENU_MAP_LENGTH_UNIT); + float y = (WORLD_SIZE_Y / 2 - MENU_MAP_TOP_OFFSET * MENU_MAP_LENGTH_UNIT) - ((mapCrosshair.y - diffY) / (m_fMapSize * 2)) * (WORLD_SIZE_Y / MENU_MAP_HEIGHT_SCALE); + CRadar::ToggleTargetMarker(x, y); + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_ENTER_OR_ADJUST, 0); + } + } + } + + if (CPad::GetPad(0)->GetMouseWheelDown() || CPad::GetPad(0)->GetPageDown() || CPad::GetPad(0)->GetRightShoulder2()) { + if (CPad::GetPad(0)->GetMouseWheelDown() && m_fMapSize > MENU_X(MAP_SIZE_TO_ALLOW_X_MOVE)) + ZOOM(mapCrosshair.x, mapCrosshair.y, false); + else + ZOOM(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, false); + + } else if (CPad::GetPad(0)->GetMouseWheelUp() || CPad::GetPad(0)->GetPageUp() || CPad::GetPad(0)->GetRightShoulder1()) { + if (CPad::GetPad(0)->GetMouseWheelUp()) + ZOOM(mapCrosshair.x, mapCrosshair.y, true); + else + ZOOM(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, true); + } + + static bool justResetPointer = false; + if (CPad::GetPad(0)->GetLeftMouse()) { + if (!justResetPointer) { + m_fMapCenterX += m_nMousePosX - m_nMouseOldPosX; + m_fMapCenterY += m_nMousePosY - m_nMouseOldPosY; + m_fMapCenterX = Clamp(m_fMapCenterX, SCREEN_WIDTH/2 - (m_fMapSize - MENU_X(MAP_MIN_SIZE)), m_fMapSize - MENU_X(MAP_MIN_SIZE) + SCREEN_WIDTH/2); + m_fMapCenterY = Clamp(m_fMapCenterY, SCREEN_HEIGHT/2 - (m_fMapSize - MENU_Y(MAP_MIN_SIZE)), m_fMapSize - MENU_Y(MAP_MIN_SIZE) + SCREEN_HEIGHT/2); + } + justResetPointer = false; + + } else +#undef ZOOM +#endif + + { + // This is else block of GetLeftMouse() if MAP_ENHANCEMENTS defined, so all of GetLeftMouse() conditions below being rendered useless. + + if (CPad::GetPad(0)->GetLeftMouse() && m_nMousePosY < m_nMouseOldPosY || CPad::GetPad(0)->GetUp() || + CPad::GetPad(0)->GetDPadUp() || CPad::GetPad(0)->GetAnalogueUpDown() < 0) { + if (CTimer::GetTimeInMillisecondsPauseMode() - lastMapTick > 10) { + if ((m_fMapSize - MENU_Y(MAP_MIN_SIZE)) + SCREEN_HEIGHT/2 > m_fMapCenterY) + m_fMapCenterY += MENU_Y(15.f); + m_bShowMouse = false; + } + } + + if (CPad::GetPad(0)->GetLeftMouse() && m_nMousePosY > m_nMouseOldPosY || CPad::GetPad(0)->GetDown() || + CPad::GetPad(0)->GetDPadDown() || CPad::GetPad(0)->GetAnalogueUpDown() > 0) { + if (CTimer::GetTimeInMillisecondsPauseMode() - lastMapTick > 10) { + if (SCREEN_HEIGHT/2 - (m_fMapSize - MENU_Y(MAP_MIN_SIZE)) < m_fMapCenterY) + m_fMapCenterY -= MENU_Y(15.f); + m_bShowMouse = false; + } + } + + if (CPad::GetPad(0)->GetLeftMouse() && m_nMousePosX < m_nMouseOldPosX || CPad::GetPad(0)->GetLeft() || + CPad::GetPad(0)->GetDPadLeft() || CPad::GetPad(0)->GetAnalogueLeftRight() < 0) { + if (CTimer::GetTimeInMillisecondsPauseMode() - lastMapTick > 10) { + if (m_fMapSize > MENU_X(MAP_SIZE_TO_ALLOW_X_MOVE) && m_fMapSize - MENU_X(MAP_MIN_SIZE) + SCREEN_WIDTH/2 > m_fMapCenterX) + m_fMapCenterX += MENU_X(15.f); + m_bShowMouse = false; + } + } + + if (CPad::GetPad(0)->GetLeftMouseJustUp()) { + // The coordinates in aScreens->MENUPAGE_MAP. + if (m_nMousePosX > MENU_X_LEFT_ALIGNED(60.0f) && m_nMousePosX < MENU_X_LEFT_ALIGNED(140.0f)) { + if (m_nMousePosY > MENU_Y(375.0f) && m_nMousePosY < MENU_Y(400.0f)) { + m_nHoverOption = HOVEROPTION_RANDOM_ITEM; + goBack = true; + } + } + } + + if (CPad::GetPad(0)->GetLeftMouse() && m_nMousePosX > m_nMouseOldPosX || CPad::GetPad(0)->GetRight() || + CPad::GetPad(0)->GetDPadRight() || CPad::GetPad(0)->GetAnalogueLeftRight() > 0) { + if (CTimer::GetTimeInMillisecondsPauseMode() - lastMapTick > 10) { + if (m_fMapSize > MENU_X(MAP_SIZE_TO_ALLOW_X_MOVE) && SCREEN_WIDTH/2 - (m_fMapSize - MENU_X(MAP_MIN_SIZE)) < m_fMapCenterX) + m_fMapCenterX -= MENU_X(15.f); + m_bShowMouse = false; + } + } + } + + + if (CTimer::GetTimeInMillisecondsPauseMode() - lastMapTick > 10) + lastMapTick = CTimer::GetTimeInMillisecondsPauseMode(); + +#ifndef MAP_ENHANCEMENTS + if (CPad::GetPad(0)->GetLeftMouseJustUp()) + CentreMousePointer(); +#endif + + if (CPad::GetPad(0)->GetLeftMouse()) { + if (m_nMousePosX < SCREEN_STRETCH_X(20.0f) || m_nMousePosX > SCREEN_STRETCH_X(620.0f) || m_nMousePosY < SCREEN_STRETCH_Y(20.0f) || m_nMousePosY > SCREEN_STRETCH_Y(428.0f)) { +#ifdef MAP_ENHANCEMENTS + justResetPointer = true; +#endif + CentreMousePointer(); + } + } + if (!CPad::GetPad(0)->GetLeftMouse() && !m_bShowMouse && (m_nMouseOldPosX != m_nMousePosX || m_nMouseOldPosY != m_nMousePosY)) { + m_bShowMouse = true; + } + + static bool pressedL = false; + + if (!CPad::GetPad(0)->GetChar('L') && !CPad::GetPad(0)->GetChar('l')) { + pressedL = false; + } + + if (!pressedL) { + if (CPad::GetPad(0)->GetChar('L') || CPad::GetPad(0)->GetChar('l')) { + m_PrefsShowLegends = !m_PrefsShowLegends; + pressedL = true; + } + } + break; + } + case MENUPAGE_SOUND_SETTINGS: + if (CheckHover(MENU_X_LEFT_ALIGNED(177.f), MENU_X_LEFT_ALIGNED(238.f), MENU_Y(MENURADIO_SELECTOR_START_Y - 13.f), MENU_Y(MENURADIO_SELECTOR_START_Y + MENURADIO_SELECTOR_HEIGHT - 8.f))) { + m_nHoverOption = HOVEROPTION_PREV_RADIO; + } + + if (CheckHover(MENU_X_LEFT_ALIGNED(422.f), MENU_X_LEFT_ALIGNED(491.f), MENU_Y(MENURADIO_SELECTOR_START_Y - 13.f), MENU_Y(MENURADIO_SELECTOR_START_Y + MENURADIO_SELECTOR_HEIGHT - 8.f))) { + m_nHoverOption = HOVEROPTION_NEXT_RADIO; + } + break; + case MENUPAGE_STATS: + { + if (CPad::GetPad(0)->GetMouseWheelUpJustDown() || CPad::GetPad(0)->GetMouseWheelUpJustUp() || CPad::GetPad(0)->GetUp() || + CPad::GetPad(0)->GetDPadUp() || CPad::GetPad(0)->GetAnalogueUpDown() < 0) { + + m_StatsScrollSpeed = 20.0f; + m_StatsScrollDirection = 0; + + } else if (CPad::GetPad(0)->GetMouseWheelDownJustDown() || CPad::GetPad(0)->GetMouseWheelDownJustUp() || CPad::GetPad(0)->GetDown() || + CPad::GetPad(0)->GetDPadDown() || CPad::GetPad(0)->GetAnalogueUpDown() > 0) { + + m_StatsScrollSpeed = 20.0f; + m_StatsScrollDirection = 1; + + } else if (CPad::GetPad(0)->GetChar(' ')) { + m_StatsScrollSpeed = 0.0f; + } else + m_StatsScrollSpeed = 150.0f; + + static bool pressedS = false; + + if (!CPad::GetPad(0)->GetChar('S') && !CPad::GetPad(0)->GetChar('s')) { + pressedS = false; + } + + if (!pressedS) { + if (CPad::GetPad(0)->GetChar('S') || CPad::GetPad(0)->GetChar('s')) { + ExportStats(); + m_nHelperTextMsgId = 4; + m_nHelperTextAlpha = 300; + pressedS = true; + } + } + break; + } + } +} + +// Not original name +void +CMenuManager::ExportStats() +{ + char date[10]; + CFileMgr::SetDirMyDocuments(); + _strdate(date); + wchar *lastMission = TheText.Get(CStats::LastMissionPassedName[0] == '\0' ? "ITBEG" : CStats::LastMissionPassedName); + FILE *txtFile = fopen("stats.txt", "w"); + + if (txtFile) { + int statLines = CStats::ConstructStatLine(99999); + fprintf(txtFile, "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n"); + fprintf(txtFile, "\t\t\tGTA VICE CITY %s\n", UnicodeToAscii(TheText.Get("FEH_STA"))); + fprintf(txtFile, "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n\n"); + fprintf(txtFile, "%s: ", UnicodeToAscii(TheText.Get("FES_CMI"))); + fprintf(txtFile, "%s\n", UnicodeToAscii(lastMission)); + fprintf(txtFile, "%s: ", UnicodeToAscii(TheText.Get("FES_DAT"))); + fprintf(txtFile, "%s\n\n\n", date); + fprintf(txtFile, "%s ", UnicodeToAscii(TheText.Get("CRIMRA"))); + UnicodeStrcpy(gUString, CStats::FindCriminalRatingString()); + fprintf(txtFile, "%s (%d)\n\n\n", UnicodeToAscii(gUString), CStats::FindCriminalRatingNumber()); + for (int i = 0; i < statLines; ++i) { + CStats::ConstructStatLine(i); + char *statKey = UnicodeToAscii(gUString); + if (statKey[0] != '\0') + fprintf(txtFile, "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n%s\n", statKey); + + char *statValue = UnicodeToAscii(gUString2); + for (int j = 0; statValue[j] != '\0'; ++j) { + if (statValue[j] == '_') + statValue[j] = '\xBA'; // This is degree symbol, but my editors keeps messing up with it so I wrote hex representation + } + if (statValue) + fprintf(txtFile, "%s\n\n", statValue); + } + fprintf(txtFile, "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n"); + } + fclose(txtFile); + FILE *htmlFile = fopen("stats.html", "w"); + if (htmlFile) { + int statLines = CStats::ConstructStatLine(99999); + fprintf(htmlFile, "Grand Theft Auto Vice City Stats\n"); + fprintf(htmlFile, "\n"); + fprintf(htmlFile, "\n"); + fprintf(htmlFile, " \n"); + fprintf(htmlFile, "\n"); + fprintf(htmlFile, "\n"); + fprintf(htmlFile, " \n"); + fprintf(htmlFile, + "\n", UnicodeToAscii(lastMission)); + fprintf(htmlFile, " \n"); + fprintf(htmlFile, " \n"); + fprintf(htmlFile, "\n", statKey, rating); + + for (int k = 0; k < statLines; ++k) { + CStats::ConstructStatLine(k); + statKey = UnicodeToAscii(gUString); + if (statKey[0] != '\0') + fprintf(htmlFile, "\n"); + + fprintf(htmlFile, "\n"); + fprintf(htmlFile, "
-------------------------------------------------------------------------
\n"); + fprintf(htmlFile, "GRAND THEFT AUTO VICE CITY "); + fprintf(htmlFile, "%s
-------------------------------------------------------------------------
 

" + "%s: \n", UnicodeToAscii(TheText.Get("FES_DAT"))); + fprintf(htmlFile, "%s
%s:
", date, UnicodeToAscii(TheText.Get("FES_CMI"))); + fprintf(htmlFile, "%s

%s\n", UnicodeToAscii(TheText.Get("CRIMRA"))); + + UnicodeStrcpy(gUString, CStats::FindCriminalRatingString()); + char *statKey = UnicodeToAscii(gUString); + int rating = CStats::FindCriminalRatingNumber(); + fprintf(htmlFile, "%s (%d)
\n"); + + if (statKey[0] != '\0') + fprintf(htmlFile, "%s", statKey); + else + fprintf(htmlFile, " "); + + fprintf(htmlFile, "
\n"); + + char *statValue = UnicodeToAscii(gUString2); + for (int l = 0; statValue[l] != '\0'; ++l) { + if (statValue[l] == '_') + statValue[l] = '\xBA'; // This is degree symbol, but my editors keeps messing up with it so I wrote hex representation + } + if (statValue) + fprintf(htmlFile, "%s", statValue); + else + fprintf(htmlFile, " "); + } + fprintf(htmlFile, "

"); + fprintf(htmlFile, "\n"); + fprintf(htmlFile, "\n"); + fprintf(htmlFile, "\n"); + fprintf(htmlFile, "
rockstargames.com/vicecityrockstargames.com rockstarnorth.com
\n\n"); + } + fclose(htmlFile); + CFileMgr::SetDir(""); +} + +// Original name is unknown +void +CMenuManager::PrintRadioSelector(void) +{ + static uint32 lastRadioChange = 0; + + CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(418.f), MENU_Y(MENURADIO_SELECTOR_START_Y + MENURADIO_SELECTOR_HEIGHT), + MENU_X_LEFT_ALIGNED(228.f), MENU_Y(MENURADIO_SELECTOR_START_Y + MENURADIO_SELECTOR_HEIGHT), + MENU_X_LEFT_ALIGNED(428.f), MENU_Y(MENURADIO_SELECTOR_START_Y), + MENU_X_LEFT_ALIGNED(238.f), MENU_Y(MENURADIO_SELECTOR_START_Y), CRGBA(RADIO_SELECTOR_COLOR.r, RADIO_SELECTOR_COLOR.g, RADIO_SELECTOR_COLOR.b, FadeIn(180))); + + int rightMostSprite, rightMostStation; + if (DMAudio.IsMP3RadioChannelAvailable()) { + rightMostSprite = MENUSPRITE_MP3; + rightMostStation = USERTRACK; + } else { + rightMostSprite = MENUSPRITE_WAVE; + rightMostStation = WAVE; + } + #ifdef THIS_IS_STUPID + + // First radio + if (m_ScrollRadioBy == 1) { + if (m_PrefsRadioStation == 1) { + m_aFrontEndSprites[rightMostSprite].Draw(m_LeftMostRadioX, MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), + CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); + } else if ( m_PrefsRadioStation == 0) { + m_aFrontEndSprites[rightMostSprite - 1].Draw(m_LeftMostRadioX, MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), + CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); + } else { + m_aFrontEndSprites[m_PrefsRadioStation + MENUSPRITE_WILDSTYLE - 2].Draw(m_LeftMostRadioX, MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), + CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); + } + } + + // Second + if (m_PrefsRadioStation == 0) { + m_aFrontEndSprites[rightMostSprite].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), + CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); + } else { + m_aFrontEndSprites[m_PrefsRadioStation + MENUSPRITE_WILDSTYLE - 1].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), + MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); + } + + // Third (middle) + int prevStation = m_PrefsRadioStation - 1; + if (prevStation == rightMostStation) { + m_aFrontEndSprites[MENUSPRITE_WILDSTYLE + 1].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 3), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), + CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); + } else if ( prevStation == rightMostStation - 1) { + m_aFrontEndSprites[MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 3), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), + CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); + } else { + m_aFrontEndSprites[m_PrefsRadioStation + MENUSPRITE_WILDSTYLE + 1].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 3), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), + CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); + } + + // Fifth + if (m_ScrollRadioBy == -1) { + int prevStation = m_PrefsRadioStation - 1; + if (prevStation == rightMostStation) { + m_aFrontEndSprites[MENUSPRITE_WILDSTYLE + 4].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 4), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), + CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); + } else if (prevStation == rightMostStation - 1) { + m_aFrontEndSprites[MENUSPRITE_WILDSTYLE + 1].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 4), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), + CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); + } else if ( prevStation == rightMostStation - 2) { + m_aFrontEndSprites[MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 4), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), + CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); + } else { + m_aFrontEndSprites[m_PrefsRadioStation + MENUSPRITE_WILDSTYLE + 2].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 4), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), + CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); + } + } + + // Fourth + if (m_ScrollRadioBy == 0) { + m_aFrontEndSprites[m_PrefsRadioStation + MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 2 - 10.f), MENU_Y(MENURADIO_ICON_Y - 10.f), MENU_X(MENURADIO_ICON_SIZE) + MENU_X(20.f), MENU_Y(MENURADIO_ICON_SIZE) + MENU_Y(20.f), + CRGBA(255, 255, 255, FadeIn(255))); + } else { + if (m_PrefsRadioStation - 1 == rightMostStation) { + m_aFrontEndSprites[MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 2), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), + CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); + } else { + m_aFrontEndSprites[m_PrefsRadioStation + MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 2), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), + CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); + } + } +#else + int first = ((m_PrefsRadioStation - 2) + rightMostStation + 1) % (rightMostStation + 1); + int second = ((m_PrefsRadioStation - 1) + rightMostStation + 1) % (rightMostStation + 1); + int third = ((m_PrefsRadioStation) + rightMostStation + 1) % (rightMostStation + 1); + int fourth = ((m_PrefsRadioStation + 1) + rightMostStation + 1) % (rightMostStation + 1); + int fifth = ((m_PrefsRadioStation + 2) + rightMostStation + 1) % (rightMostStation + 1); + + // First one is only drawn on transition to next + if (m_ScrollRadioBy == 1) { + m_aFrontEndSprites[first + MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX, MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), + CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); + } + + // Second + m_aFrontEndSprites[second + MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), + CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); + + // Fourth + m_aFrontEndSprites[fourth + MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 3), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), + CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); + + // Fifth one is only drawn on transition to prev. + if (m_ScrollRadioBy == -1) { + m_aFrontEndSprites[fifth + MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 4), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), + CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); + } + + // Middle one(third) is colored differently depending on if it's in transition. + // If not in transition then this icon indicates selected radio, and should be on top of all icons. thus drawn last + if (m_ScrollRadioBy != 0) { + m_aFrontEndSprites[third + MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 2), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), + CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); + } else { + m_aFrontEndSprites[third + MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 2 - 10.f), MENU_Y(MENURADIO_ICON_Y - 10.f), MENU_X(MENURADIO_ICON_SIZE) + MENU_X(20.f), MENU_Y(MENURADIO_ICON_SIZE) + MENU_Y(20.f), + CRGBA(255, 255, 255, FadeIn(255))); + } +#endif + + static bool radioChangeRequested = false; + static uint32 lastScrollCheck = 0; + if (CTimer::GetTimeInMillisecondsPauseMode() - lastScrollCheck > 17) { + if (m_ScrollRadioBy == 1) { + if (m_LeftMostRadioX > MENU_X_LEFT_ALIGNED(MENURADIO_ICON_FIRST_X - MENURADIO_ICON_SIZE)) { + m_LeftMostRadioX -= MENU_X(6.f); + } else { + m_ScrollRadioBy = 0; + lastRadioChange = CTimer::GetTimeInMillisecondsPauseMode(); + radioChangeRequested = true; + } + } + if (m_ScrollRadioBy == -1) { + if (m_LeftMostRadioX < MENU_X_LEFT_ALIGNED(MENURADIO_ICON_FIRST_X - MENURADIO_ICON_SIZE)) { + m_LeftMostRadioX += MENU_X(6.f); + } else { + m_ScrollRadioBy = 0; + lastRadioChange = CTimer::GetTimeInMillisecondsPauseMode(); + radioChangeRequested = true; + } + } + lastScrollCheck = CTimer::GetTimeInMillisecondsPauseMode(); + } + // Background behind arrows + CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(228.f), MENU_Y(MENURADIO_SELECTOR_START_Y + MENURADIO_SELECTOR_HEIGHT), + MENU_X_LEFT_ALIGNED(168.f), MENU_Y(MENURADIO_SELECTOR_START_Y + MENURADIO_SELECTOR_HEIGHT), + MENU_X_LEFT_ALIGNED(238.f), MENU_Y(MENURADIO_SELECTOR_START_Y), + MENU_X_LEFT_ALIGNED(178.f), MENU_Y(MENURADIO_SELECTOR_START_Y), + CRGBA(RADIO_SELECTOR_COLOR.r, RADIO_SELECTOR_COLOR.g, RADIO_SELECTOR_COLOR.b, FadeIn(255))); + + CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(478.f), MENU_Y(MENURADIO_SELECTOR_START_Y + MENURADIO_SELECTOR_HEIGHT), + MENU_X_LEFT_ALIGNED(418.f), MENU_Y(MENURADIO_SELECTOR_START_Y + MENURADIO_SELECTOR_HEIGHT), + MENU_X_LEFT_ALIGNED(488.f), MENU_Y(MENURADIO_SELECTOR_START_Y), + MENU_X_LEFT_ALIGNED(428.f), MENU_Y(MENURADIO_SELECTOR_START_Y), + CRGBA(RADIO_SELECTOR_COLOR.r, RADIO_SELECTOR_COLOR.g, RADIO_SELECTOR_COLOR.b, FadeIn(255))); + + // Arrows and their shadows + CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(216.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 48.f), MENU_X_LEFT_ALIGNED(196.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 36.f), MENU_X_LEFT_ALIGNED(216.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 22.f), MENU_X_LEFT_ALIGNED(196.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 36.f), CRGBA(0, 0, 0, FadeIn(255))); + CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(213.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 45.f), MENU_X_LEFT_ALIGNED(193.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 33.f), MENU_X_LEFT_ALIGNED(213.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 19.f), MENU_X_LEFT_ALIGNED(193.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 33.f), CRGBA(97, 194, 247, FadeIn(255))); + CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(440.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 48.f), MENU_X_LEFT_ALIGNED(460.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 36.f), MENU_X_LEFT_ALIGNED(440.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 22.f), MENU_X_LEFT_ALIGNED(460.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 36.f), CRGBA(0, 0, 0, FadeIn(255))); + CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(443.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 45.f), MENU_X_LEFT_ALIGNED(463.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 33.f), MENU_X_LEFT_ALIGNED(443.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 19.f), MENU_X_LEFT_ALIGNED(463.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 33.f), CRGBA(97, 194, 247, FadeIn(255))); + if (radioChangeRequested) { + if (CTimer::GetTimeInMillisecondsPauseMode() - lastRadioChange > 50) { + DMAudio.SetRadioInCar(m_PrefsRadioStation); + DMAudio.PlayFrontEndTrack(m_PrefsRadioStation, TRUE); + OutputDebugString("FRONTEND RADIO STATION CHANGED"); + lastRadioChange = CTimer::GetTimeInMillisecondsPauseMode(); + radioChangeRequested = false; + } + } +} + +// Original name is unknown +void +CMenuManager::ProcessList(bool &optionSelected, bool &goBack) +{ + if (m_bWaitingForNewKeyBind) + return; + + if (m_nCurrScreen == MENUPAGE_SKIN_SELECT) { + m_nTotalListRow = m_nSkinsTotal; + } + if (m_nCurrScreen == MENUPAGE_KEYBOARD_CONTROLS) { + // GetNumOptionsCntrlConfigScreens would have been a better choice + m_nTotalListRow = m_ControlMethod == CONTROL_CLASSIC ? 32 : 27; + if (m_nSelectedListRow > m_nTotalListRow) + m_nSelectedListRow = m_nTotalListRow - 1; + } + + if (CPad::GetPad(0)->GetEnterJustDown() || CPad::GetPad(0)->GetCrossJustDown()) { + m_bShowMouse = 0; + optionSelected = true; + } + if (CPad::GetPad(0)->GetBackspaceJustDown() && m_nCurrScreen == MENUPAGE_KEYBOARD_CONTROLS && !field_159) { + if (m_nCurrExLayer == HOVEROPTION_LIST) { + m_nHoverOption = HOVEROPTION_NOT_HOVERING; + m_bWaitingForNewKeyBind = true; + m_bStartWaitingForKeyBind = true; + m_bKeyChangeNotProcessed = true; + pControlEdit = &m_KeyPressedCode; + } + } else { + field_159 = false; + } + + static uint32 lastTimeClickedScrollButton = 0; + + if (CTimer::GetTimeInMillisecondsPauseMode() - lastTimeClickedScrollButton >= 200) { + m_bPressedPgUpOnList = false; + m_bPressedPgDnOnList = false; + m_bPressedUpOnList = false; + m_bPressedDownOnList = false; + m_bPressedScrollButton = false; + lastTimeClickedScrollButton = CTimer::GetTimeInMillisecondsPauseMode(); + } + + if (CPad::GetPad(0)->GetTabJustDown()) { + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); + m_bShowMouse = false; + switch (m_nCurrExLayer) { + case HOVEROPTION_BACK: + default: + m_nCurrExLayer = HOVEROPTION_LIST; + break; + case HOVEROPTION_LIST: + m_nCurrExLayer = HOVEROPTION_USESKIN; + break; + case HOVEROPTION_USESKIN: + m_nCurrExLayer = HOVEROPTION_BACK; + } + if (((m_nCurrScreen == MENUPAGE_SKIN_SELECT) && (m_nCurrExLayer == HOVEROPTION_USESKIN)) && strcmp(m_aSkinName, m_PrefsSkinFile) == 0) { + m_nCurrExLayer = HOVEROPTION_BACK; + } + if ((m_nCurrScreen == MENUPAGE_KEYBOARD_CONTROLS) && (m_nCurrExLayer == HOVEROPTION_USESKIN)) { + m_nCurrExLayer = HOVEROPTION_BACK; + } + } + + bool pressed = false; + if (CPad::GetPad(0)->GetUp() || CPad::GetPad(0)->GetAnaloguePadUp() || CPad::GetPad(0)->GetDPadUpJustDown()) { + m_bShowMouse = false; + pressed = true; + } else if (CPad::GetPad(0)->GetMouseWheelUpJustUp()) { + m_bShowMouse = true; + pressed = true; + } + + // Up + if (pressed) { + m_nCurrExLayer = HOVEROPTION_LIST; + if (!m_bPressedUpOnList) { + m_bPressedUpOnList = true; + lastTimeClickedScrollButton = CTimer::GetTimeInMillisecondsPauseMode(); + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); + ScrollUpListByOne(); + } + } else { + m_bPressedUpOnList = false; + } + + pressed = false; + if (CPad::GetPad(0)->GetDown() || CPad::GetPad(0)->GetAnaloguePadDown() || CPad::GetPad(0)->GetDPadDownJustDown()) { + m_bShowMouse = false; + pressed = true; + } else if (CPad::GetPad(0)->GetMouseWheelDownJustDown()) { + m_bShowMouse = true; + pressed = true; + } + + // Down + if (pressed) { + m_nCurrExLayer = HOVEROPTION_LIST; + if (!m_bPressedDownOnList) { + m_bPressedDownOnList = true; + lastTimeClickedScrollButton = CTimer::GetTimeInMillisecondsPauseMode(); + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); + ScrollDownListByOne(); + } + } else { + m_bPressedDownOnList = false; + } + + if (m_nCurrScreen != MENUPAGE_KEYBOARD_CONTROLS) { + if (!CPad::GetPad(0)->GetPageUp()) { + m_bPressedPgUpOnList = false; + } else { + m_nCurrExLayer = HOVEROPTION_LIST; + if (!m_bPressedPgUpOnList) { + m_bPressedPgUpOnList = true; + lastTimeClickedScrollButton = CTimer::GetTimeInMillisecondsPauseMode(); + m_bShowMouse = false; + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); + PageUpList(false); + } + } + if (!CPad::GetPad(0)->GetPageDown()) { + m_bPressedPgDnOnList = false; + } else { + m_nCurrExLayer = HOVEROPTION_LIST; + if (!m_bPressedPgDnOnList) { + m_bPressedPgDnOnList = true; + lastTimeClickedScrollButton = CTimer::GetTimeInMillisecondsPauseMode(); + m_bShowMouse = false; + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); + PageDownList(false); + } + } + if (CPad::GetPad(0)->GetHome()) { + m_nCurrExLayer = HOVEROPTION_LIST; + m_bShowMouse = false; + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); + if (m_nTotalListRow >= MAX_VISIBLE_OPTION_ON_SCREEN) { + m_nFirstVisibleRowOnList = 0; + } + m_nSelectedListRow = 0; + m_nScrollbarTopMargin = (SCROLLBAR_MAX_HEIGHT / m_nTotalListRow) * m_nFirstVisibleRowOnList; + } + if (CPad::GetPad(0)->GetEnd()) { + m_nCurrExLayer = HOVEROPTION_LIST; + m_bShowMouse = false; + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); + if (m_nTotalListRow >= MAX_VISIBLE_OPTION_ON_SCREEN) { + m_nFirstVisibleRowOnList = m_nTotalListRow - MAX_VISIBLE_OPTION_ON_SCREEN; + } + m_nSelectedListRow = m_nTotalListRow - 1; + m_nScrollbarTopMargin = (SCROLLBAR_MAX_HEIGHT / m_nTotalListRow) * m_nFirstVisibleRowOnList; + } + } + + if (CPad::GetPad(0)->GetEscapeJustDown() || CPad::GetPad(0)->GetBackJustDown()) { + m_bShowMouse = false; + goBack = true; + } + + if (CPad::GetPad(0)->GetLeftMouseJustDown()) { + switch (m_nHoverOption) { + case HOVEROPTION_BACK: + goBack = true; + break; + case HOVEROPTION_PAGEUP: + PageUpList(true); + break; + case HOVEROPTION_PAGEDOWN: + PageDownList(true); + break; + case HOVEROPTION_USESKIN: + if (m_nSkinsTotal > 0) { + m_pSelectedSkin = m_pSkinListHead.nextSkin; + strcpy(m_PrefsSkinFile, m_aSkinName); + CWorld::Players[0].SetPlayerSkin(m_PrefsSkinFile); + SaveSettings(); + } + } + } + + if (CPad::GetPad(0)->GetLeftMouseJustDown()) { + switch (m_nHoverOption) { + case HOVEROPTION_OVER_SCROLL_UP: + m_nHoverOption = HOVEROPTION_CLICKED_SCROLL_UP; + break; + case HOVEROPTION_OVER_SCROLL_DOWN: + m_nHoverOption = HOVEROPTION_CLICKED_SCROLL_DOWN; + break; + case HOVEROPTION_LIST: + m_nHoverOption = HOVEROPTION_SKIN; + } + } else if ((CPad::GetPad(0)->GetLeftMouseJustUp()) + && ((m_nHoverOption == HOVEROPTION_CLICKED_SCROLL_UP || (m_nHoverOption == HOVEROPTION_CLICKED_SCROLL_DOWN)))) { + m_nHoverOption = HOVEROPTION_NOT_HOVERING; + } + + if (!CPad::GetPad(0)->GetLeftMouse()) { + holdingScrollBar = false; + } else { + if ((m_nHoverOption == HOVEROPTION_HOLDING_SCROLLBAR) || holdingScrollBar) { + holdingScrollBar = true; + // TODO: This part is a bit hard to reverse. Not much code tho + assert(0 && "Holding scrollbar isn't done yet"); + } else { + switch (m_nHoverOption) { + case HOVEROPTION_OVER_SCROLL_UP: + case HOVEROPTION_CLICKED_SCROLL_UP: + if (!m_bPressedScrollButton) { + m_bPressedScrollButton = true; + lastTimeClickedScrollButton = CTimer::GetTimeInMillisecondsPauseMode(); + ScrollUpListByOne(); + } + break; + case HOVEROPTION_OVER_SCROLL_DOWN: + case HOVEROPTION_CLICKED_SCROLL_DOWN: + if (!m_bPressedScrollButton) { + m_bPressedScrollButton = true; + lastTimeClickedScrollButton = CTimer::GetTimeInMillisecondsPauseMode(); + ScrollDownListByOne(); + } + break; + default: + m_bPressedScrollButton = false; + } + } + } +} + +void +CMenuManager::UserInput(void) +{ + bool goBack = false; + bool optionSelected = false; + bool goUp = false; + bool goDown = false; + int8 changeValueBy; + + if (!m_AllowNavigation && m_firstStartCounter == 255) + m_AllowNavigation = true; + if (!m_bShowMouse && m_nCurrScreen != MENUPAGE_MAP && (m_nMouseOldPosX != m_nMousePosX || m_nMouseOldPosY != m_nMousePosY)) { + m_bShowMouse = true; + } + + static int oldOption = -99; + oldOption = m_nCurrOption; +#ifdef SCROLLABLE_PAGES + int firstOption = SCREEN_HAS_AUTO_SCROLLBAR ? m_nFirstVisibleRowOnList : 0; + int scrollOffset = aScreens[m_nCurrScreen].m_aEntries[firstOption].m_Y - aScreens[m_nCurrScreen].m_aEntries[0].m_Y; + for (int rowToCheck = firstOption; rowToCheck < firstOption + MAX_VISIBLE_OPTION && rowToCheck < NUM_MENUROWS; ++rowToCheck) { +#else + for (int rowToCheck = 0; rowToCheck < NUM_MENUROWS; ++rowToCheck) { +#endif + if (aScreens[m_nCurrScreen].m_aEntries[rowToCheck].m_Action == MENUACTION_NOTHING || + aScreens[m_nCurrScreen].m_aEntries[rowToCheck].m_Action == MENUACTION_LABEL) + continue; + + // unused: CFont::GetStringWidth(TheText.Get(aScreens[m_nCurrScreen].m_aEntries[rowToCheck].m_EntryName), true); + // So they also wanted the compare X, but they abandoned the idea later on + + if (m_nMousePosY > MENU_Y(aScreens[m_nCurrScreen].m_aEntries[rowToCheck].m_Y MINUS_SCROLL_OFFSET) && + m_nMousePosY < MENU_Y(aScreens[m_nCurrScreen].m_aEntries[rowToCheck].m_Y MINUS_SCROLL_OFFSET PLUS_LINE_HEIGHT_ON_SCREEN)) { + static int oldScreen = m_nCurrScreen; + + m_nOptionMouseHovering = rowToCheck; + if (m_nMouseOldPosX != m_nMousePosX || m_nMouseOldPosY != m_nMousePosY) { + m_nCurrOption = rowToCheck; + m_bShowMouse = true; + } + + int action = aScreens[m_nCurrScreen].m_aEntries[rowToCheck].m_Action; + if (action != MENUACTION_BRIGHTNESS && action != MENUACTION_DRAWDIST && action != MENUACTION_MUSICVOLUME + && action != MENUACTION_SFXVOLUME && action != MENUACTION_MOUSESENS && action != MENUACTION_MP3VOLUMEBOOST +#ifdef CUSTOM_FRONTEND_OPTIONS + && action != MENUACTION_CFO_SLIDER +#endif + ) + m_nHoverOption = HOVEROPTION_RANDOM_ITEM; + + break; + } + if (m_bShowMouse && m_nMenuFadeAlpha == 255) { + m_nOptionMouseHovering = oldOption; + m_nCurrOption = oldOption; + } + } + + if (m_bShowMouse) { + if (oldOption != m_nCurrOption) { + if (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_LABEL) { + ++m_nCurrOption; + ++m_nOptionMouseHovering; + } + m_nOptionHighlightTransitionBlend = 0; + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); + } + } + + m_nMouseOldPosX = m_nMousePosX; + m_nMouseOldPosY = m_nMousePosY; + m_nMousePosX = m_nMouseTempPosX; + m_nMousePosY = m_nMouseTempPosY; + + if (m_nMousePosX < 0) m_nMousePosX = 0; + if (m_nMousePosX > SCREEN_WIDTH) m_nMousePosX = SCREEN_WIDTH; + if (m_nMousePosY < 0) m_nMousePosY = 0; + if (m_nMousePosY > SCREEN_HEIGHT) m_nMousePosY = SCREEN_HEIGHT; + + changeValueBy = 0; + if (hasNativeList(m_nCurrScreen)) { + ProcessList(optionSelected, goBack); + } else { + AdditionalOptionInput(goBack); + + if (m_AllowNavigation && + (CPad::GetPad(0)->GetDownJustDown() || CPad::GetPad(0)->GetAnaloguePadDown() || CPad::GetPad(0)->GetDPadDownJustDown())) { + m_bShowMouse = false; + goDown = true; + m_nOptionHighlightTransitionBlend = 0; + + } else if (m_AllowNavigation && + (CPad::GetPad(0)->GetUpJustDown() || CPad::GetPad(0)->GetAnaloguePadUp() || CPad::GetPad(0)->GetDPadUpJustDown())) { + m_bShowMouse = false; + goUp = true; + m_nOptionHighlightTransitionBlend = 0; + } + + if ((m_nCurrOption == 0) && (m_nCurrScreen == MENUPAGE_PAUSE_MENU)) { + if (CPad::GetPad(0)->GetEnterJustUp() || CPad::GetPad(0)->GetCrossJustUp()) { + m_bShowMouse = false; + optionSelected = true; + } + } else { + if (CPad::GetPad(0)->GetEnterJustDown() || CPad::GetPad(0)->GetCrossJustDown()) { + m_bShowMouse = false; + optionSelected = true; + } + } + + if (CPad::GetPad(0)->GetLeftMouseJustUp() && m_nCurrScreen != MENUPAGE_MAP) { + if (m_nHoverOption == HOVEROPTION_RANDOM_ITEM) + optionSelected = true; + else if (m_nHoverOption == HOVEROPTION_NEXT_RADIO) + ChangeRadioStation(1); + else if (m_nHoverOption == HOVEROPTION_PREV_RADIO) + ChangeRadioStation(-1); + } + + if (CPad::GetPad(0)->GetLeftMouse()) { + switch (m_nHoverOption) { + case HOVEROPTION_INCREASE_BRIGHTNESS: + case HOVEROPTION_INCREASE_MP3BOOST: + case HOVEROPTION_INCREASE_DRAWDIST: + case HOVEROPTION_INCREASE_MUSICVOLUME: + case HOVEROPTION_INCREASE_SFXVOLUME: + case HOVEROPTION_INCREASE_MOUSESENS: +#ifdef CUSTOM_FRONTEND_OPTIONS + case HOVEROPTION_INCREASE_CFO_SLIDER: +#endif + CheckSliderMovement(1); + break; + case HOVEROPTION_DECREASE_BRIGHTNESS: + case HOVEROPTION_DECREASE_MP3BOOST: + case HOVEROPTION_DECREASE_DRAWDIST: + case HOVEROPTION_DECREASE_MUSICVOLUME: + case HOVEROPTION_DECREASE_SFXVOLUME: + case HOVEROPTION_DECREASE_MOUSESENS: +#ifdef CUSTOM_FRONTEND_OPTIONS + case HOVEROPTION_DECREASE_CFO_SLIDER: +#endif + CheckSliderMovement(-1); + break; + } + } + +#ifdef SCROLLABLE_PAGES + if (m_nTotalListRow > MAX_VISIBLE_OPTION) { + bool temp = false; + + m_nSelectedListRow = m_nCurrOption; + + // ignore detected back/select states, it's our screen's job + ProcessList(temp, temp); + + // and ignore our screen's goUp/Down, now it's ProcessList's job + goUp = false; + goDown = false; + m_nCurrOption = m_nSelectedListRow; + + if (oldOption != m_nCurrOption) + m_nOptionHighlightTransitionBlend = 0; + } + + // Prevent sound on scroll. Mouse wheel is now belongs to us! + if (!(m_nTotalListRow > MAX_VISIBLE_OPTION && (CPad::GetPad(0)->GetMouseWheelUpJustDown() || CPad::GetPad(0)->GetMouseWheelDownJustDown()))) +#endif + if (CPad::GetPad(0)->GetLeftMouseJustUp() || CPad::GetPad(0)->GetLeftJustUp() || CPad::GetPad(0)->GetRightJustUp() + || CPad::GetPad(0)->GetDPadLeftJustUp() || CPad::GetPad(0)->GetDPadRightJustUp() + || CPad::GetPad(0)->GetAnaloguePadLeftJustUp() || CPad::GetPad(0)->GetAnaloguePadRightJustUp() + || CPad::GetPad(0)->GetMouseWheelUpJustDown() || CPad::GetPad(0)->GetMouseWheelDownJustDown()) { + int option = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action; + if (option == MENUACTION_BRIGHTNESS +#ifdef CUSTOM_FRONTEND_OPTIONS + || option == MENUACTION_CFO_SLIDER +#endif + ) + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_ENTER_OR_ADJUST, 0); + else if (option == MENUACTION_SFXVOLUME) + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_AUDIO_TEST, 0); + else if (option == MENUACTION_DRAWDIST || option == MENUACTION_MOUSESENS) + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_ENTER_OR_ADJUST, 0); + + } + if (CPad::GetPad(0)->GetBackJustDown() || CPad::GetPad(0)->GetEscapeJustDown()) { + if (m_nCurrScreen != MENUPAGE_START_MENU && m_nCurrScreen != MENUPAGE_PAUSE_MENU && m_nCurrScreen != MENUPAGE_CHOOSE_SAVE_SLOT + && m_nCurrScreen != MENUPAGE_SAVE_CHEAT_WARNING && m_nCurrScreen != MENUPAGE_SAVING_IN_PROGRESS + && m_nCurrScreen != MENUPAGE_DELETING_IN_PROGRESS && m_nCurrScreen != MENUPAGE_OUTRO +#ifdef MISSION_REPLAY + && m_nCurrScreen != MENUPAGE_MISSION_RETRY +#endif + ) + { + m_bShowMouse = false; + goBack = true; + } + } + + if (((goDown) || (goUp)) || (optionSelected)) { + goBack = false; + } + + } + + int curAction = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action; + if (CPad::GetPad(0)->GetLeft() || CPad::GetPad(0)->GetPedWalkLeftRight() < 0 || CPad::GetPad(0)->GetDPadLeft()) { + static uint32 lastSliderDecrease = 0; + if (CTimer::GetTimeInMillisecondsPauseMode() - lastSliderDecrease > 150) { + if (curAction == MENUACTION_BRIGHTNESS || curAction == MENUACTION_MUSICVOLUME || + curAction == MENUACTION_SFXVOLUME || curAction == MENUACTION_RADIO || + curAction == MENUACTION_DRAWDIST || curAction == MENUACTION_MOUSESENS || + curAction == MENUACTION_MP3VOLUMEBOOST +#ifdef CUSTOM_FRONTEND_OPTIONS + || curAction == MENUACTION_CFO_SLIDER +#endif + ) + changeValueBy = -1; + + lastSliderDecrease = CTimer::GetTimeInMillisecondsPauseMode(); + } + } else if (CPad::GetPad(0)->GetRight() || CPad::GetPad(0)->GetPedWalkLeftRight() > 0 || CPad::GetPad(0)->GetDPadRight()) { + static uint32 lastSliderIncrease = 0; + if (CTimer::GetTimeInMillisecondsPauseMode() - lastSliderIncrease > 150) { + if (curAction == MENUACTION_BRIGHTNESS || curAction == MENUACTION_MUSICVOLUME || + curAction == MENUACTION_SFXVOLUME || curAction == MENUACTION_RADIO || + curAction == MENUACTION_DRAWDIST || curAction == MENUACTION_MOUSESENS || + curAction == MENUACTION_MP3VOLUMEBOOST +#ifdef CUSTOM_FRONTEND_OPTIONS + || curAction == MENUACTION_CFO_SLIDER +#endif + ) + changeValueBy = 1; + lastSliderIncrease = CTimer::GetTimeInMillisecondsPauseMode(); + } + } + +#ifdef SCROLLABLE_PAGES + if (!SCREEN_HAS_AUTO_SCROLLBAR) +#endif + { + if (CPad::GetPad(0)->GetMouseWheelUpJustDown()) { + changeValueBy = 1; + } else if (CPad::GetPad(0)->GetMouseWheelDownJustDown()) { + changeValueBy = -1; + } + } + + if (m_AllowNavigation) { + if (CPad::GetPad(0)->GetRightJustDown() || CPad::GetPad(0)->GetAnaloguePadRight() || CPad::GetPad(0)->GetDPadRightJustDown()) { + m_bShowMouse = false; + changeValueBy = 1; + } + } + + if (m_AllowNavigation) { + if (CPad::GetPad(0)->GetLeftJustDown() || CPad::GetPad(0)->GetAnaloguePadLeft() || CPad::GetPad(0)->GetDPadLeftJustDown()) { + m_bShowMouse = false; + changeValueBy = -1; + } + } + if (changeValueBy != 0) { + if ((m_nCurrScreen == MENUPAGE_SOUND_SETTINGS || m_nCurrScreen == MENUPAGE_DISPLAY_SETTINGS || m_nCurrScreen == MENUPAGE_CONTROLLER_PC || m_nCurrScreen == MENUPAGE_MOUSE_CONTROLS) + && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_NOTHING + && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_LABEL + && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_YES + && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_NO + && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_CHANGEMENU + && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_KEYBOARDCTRLS + && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_GOBACK + && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_RESTOREDEF + && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_DRAWDIST + && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_MOUSESENS + && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_MP3VOLUMEBOOST) { + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_ENTER_OR_ADJUST, 0); + } + } + ProcessUserInput(goDown, goUp, optionSelected, goBack, changeValueBy); +#ifdef CUSTOM_FRONTEND_OPTIONS + if (aScreens[m_nCurrScreen].m_aEntries[oldOption].m_Action < MENUACTION_NOTHING) { // CFO check + CMenuScreenCustom::CMenuEntry &oldEntry = aScreens[m_nCurrScreen].m_aEntries[oldOption]; + if (m_nCurrOption != oldOption) { + if (oldEntry.m_Action == MENUACTION_CFO_DYNAMIC) + if(oldEntry.m_CFODynamic->buttonPressFunc) + oldEntry.m_CFODynamic->buttonPressFunc(FEOPTION_ACTION_FOCUSLOSS); + + if (oldEntry.m_Action == MENUACTION_CFO_SELECT && oldEntry.m_CFOSelect->onlyApplyOnEnter) { + if (oldEntry.m_CFOSelect->displayedValue != oldEntry.m_CFOSelect->lastSavedValue) + SetHelperText(3); // Restored original value + + oldEntry.m_CFOSelect->displayedValue = oldEntry.m_CFOSelect->lastSavedValue = *(int8*)oldEntry.m_CFO->value; + } + } else if (oldEntry.m_Action == MENUACTION_CFO_SELECT && oldEntry.m_CFOSelect->onlyApplyOnEnter) { + if (oldEntry.m_CFOSelect->displayedValue != *(int8*)oldEntry.m_CFO->value) + SetHelperText(1); // Enter to apply + else if (m_nHelperTextMsgId == 1) + ResetHelperText(); // Applied + } + } +#endif +} + +void +CMenuManager::ProcessUserInput(uint8 goDown, uint8 goUp, uint8 optionSelected, uint8 goBack, int8 changeAmount) +{ + if (m_nCurrScreen == MENUPAGE_OUTRO) + return; + + if (m_bWaitingForNewKeyBind) { + if (m_bStartWaitingForKeyBind) + m_bStartWaitingForKeyBind = false; + else { + pControlEdit = CPad::EditCodesForControls(pControlEdit, 1); + JoyButtonJustClicked = false; + MouseButtonJustClicked = false; + + if (CPad::GetPad(0)->GetLeftMouseJustDown()) + MouseButtonJustClicked = rsMOUSELEFTBUTTON; + else if (CPad::GetPad(0)->GetRightMouseJustUp()) + MouseButtonJustClicked = rsMOUSERIGHTBUTTON; + else if (CPad::GetPad(0)->GetMiddleMouseJustUp()) + MouseButtonJustClicked = rsMOUSMIDDLEBUTTON; + else if (CPad::GetPad(0)->GetMouseWheelUpJustUp()) + MouseButtonJustClicked = rsMOUSEWHEELUPBUTTON; + else if (CPad::GetPad(0)->GetMouseWheelDownJustUp()) + MouseButtonJustClicked = rsMOUSEWHEELDOWNBUTTON; + else if (CPad::GetPad(0)->GetMouseX1JustUp()) + MouseButtonJustClicked = rsMOUSEX1BUTTON; + else if (CPad::GetPad(0)->GetMouseX2JustUp()) + MouseButtonJustClicked = rsMOUSEX2BUTTON; + + JoyButtonJustClicked = ControlsManager.GetJoyButtonJustDown(); + + int32 TypeOfControl = KEYBOARD; + if (JoyButtonJustClicked) + TypeOfControl = JOYSTICK; + if (MouseButtonJustClicked) + TypeOfControl = MOUSE; + if (*pControlEdit != rsNULL) + TypeOfControl = KEYBOARD; + + if (!m_bKeyIsOK) { + pControlEdit = nil; + m_bWaitingForNewKeyBind = false; + m_KeyPressedCode = -1; + m_bStartWaitingForKeyBind = false; + } else if (!m_bKeyChangeNotProcessed) { + if (*pControlEdit != rsNULL || MouseButtonJustClicked || JoyButtonJustClicked) + CheckCodesForControls(TypeOfControl); + + field_159 = true; + } else { + for (int i = 0; i < 4; i++) + ControlsManager.ClearSettingsAssociatedWithAction((e_ControllerAction)m_CurrCntrlAction, (eControllerType)i); + m_bKeyIsOK = false; + m_bKeyChangeNotProcessed = false; + } + } + } + + if (pEditString || pControlEdit) + return; + +#ifdef USE_DEBUG_SCRIPT_LOADER + if (m_nCurrScreen == MENUPAGE_START_MENU || m_nCurrScreen == MENUPAGE_NEW_GAME || m_nCurrScreen == MENUPAGE_NEW_GAME_RELOAD) { + if (CPad::GetPad(0)->GetChar('R')) { + CTheScripts::ScriptToLoad = 1; + DoSettingsBeforeStartingAGame(); + return; + } + } +#endif + + int oldOption = m_nCurrOption; + if (goDown) { + if (m_nCurrScreen != MENUPAGE_MAP) + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); + + m_nCurrOption++; + if (m_nCurrOption == NUM_MENUROWS || (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_NOTHING)) { + m_nCurrOption = 0; + } + if (oldOption != m_nCurrOption) + m_nOptionHighlightTransitionBlend = 0; + } + if (goUp) { + if (m_nCurrScreen != MENUPAGE_MAP) + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); + + if (m_nCurrOption == (aScreens[m_nCurrScreen].m_aEntries[0].m_Action == MENUACTION_LABEL)) { + while (m_nCurrOption != NUM_MENUROWS - 1 + && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption + 1].m_Action != MENUACTION_NOTHING) { + m_nCurrOption++; + } + } else { + m_nCurrOption--; + } + if (oldOption != m_nCurrOption) + m_nOptionHighlightTransitionBlend = 0; + } + + if (optionSelected && m_nMenuFadeAlpha == 255) { + if (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_TargetMenu == MENUPAGE_NEW_GAME_RELOAD && m_bGameNotLoaded) { + DoSettingsBeforeStartingAGame(); + } else if (hasNativeList(m_nCurrScreen)) { + switch (m_nCurrExLayer) { + case HOVEROPTION_LIST: + if (m_nCurrScreen == MENUPAGE_KEYBOARD_CONTROLS) { + m_bWaitingForNewKeyBind = true; + m_bStartWaitingForKeyBind = true; + pControlEdit = &m_KeyPressedCode; + } + if (m_nCurrScreen == MENUPAGE_SKIN_SELECT) { + strcpy(m_PrefsSkinFile, m_aSkinName); + CWorld::Players[0].SetPlayerSkin(m_PrefsSkinFile); + m_nCurrExLayer = HOVEROPTION_BACK; + SaveSettings(); + } + m_nHoverOption = HOVEROPTION_NOT_HOVERING; + break; + case HOVEROPTION_USESKIN: + m_nHoverOption = HOVEROPTION_NOT_HOVERING; + strcpy(m_PrefsSkinFile, m_aSkinName); + CWorld::Players[0].SetPlayerSkin(m_PrefsSkinFile); + m_nCurrExLayer = HOVEROPTION_BACK; + SaveSettings(); + break; + case HOVEROPTION_BACK: + default: + goBack = true; + break; + } + if (m_nSkinsTotal > 0) { + m_pSelectedSkin = m_pSkinListHead.nextSkin; + strcpy(m_PrefsSkinFile, m_aSkinName); + CWorld::Players[0].SetPlayerSkin(m_PrefsSkinFile); + SaveSettings(); + } + } + + int option = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action; +#ifdef FIX_BUGS + int currScreen = m_nCurrScreen; + int currOption = m_nCurrOption; +#endif + switch (option) { + case MENUACTION_CHANGEMENU: + case MENUACTION_YES: + case MENUACTION_NO: + SwitchToNewScreen(aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_TargetMenu); + break; + case MENUACTION_RADIO: + ChangeRadioStation(1); + break; + case MENUACTION_LANG_ENG: + m_PrefsLanguage = LANGUAGE_AMERICAN; + m_bFrontEnd_ReloadObrTxtGxt = true; + InitialiseChangedLanguageSettings(); + SaveSettings(); + break; + case MENUACTION_LANG_FRE: + m_PrefsLanguage = LANGUAGE_FRENCH; + m_bFrontEnd_ReloadObrTxtGxt = true; + InitialiseChangedLanguageSettings(); + SaveSettings(); + break; + case MENUACTION_LANG_GER: + m_PrefsLanguage = LANGUAGE_GERMAN; + m_bFrontEnd_ReloadObrTxtGxt = true; + InitialiseChangedLanguageSettings(); + SaveSettings(); + break; + case MENUACTION_LANG_ITA: + m_PrefsLanguage = LANGUAGE_ITALIAN; + m_bFrontEnd_ReloadObrTxtGxt = true; + InitialiseChangedLanguageSettings(); + SaveSettings(); + break; + case MENUACTION_LANG_SPA: + m_PrefsLanguage = LANGUAGE_SPANISH; + m_bFrontEnd_ReloadObrTxtGxt = true; + InitialiseChangedLanguageSettings(); + SaveSettings(); + break; + case MENUACTION_CHECKSAVE: + { + int saveSlot = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_SaveSlot; + + if (saveSlot >= SAVESLOT_1 && saveSlot <= SAVESLOT_8) { + m_nCurrSaveSlot = saveSlot - SAVESLOT_1; + if (Slots[m_nCurrSaveSlot] != SLOT_EMPTY && Slots[m_nCurrSaveSlot] != SLOT_CORRUPTED) { + if (m_nCurrScreen == MENUPAGE_CHOOSE_LOAD_SLOT) { + SwitchToNewScreen(MENUPAGE_LOAD_SLOT_CONFIRM); + } else if (m_nCurrScreen == MENUPAGE_CHOOSE_DELETE_SLOT) { + SwitchToNewScreen(MENUPAGE_DELETE_SLOT_CONFIRM); + } + } + } + break; + } + case MENUACTION_NEWGAME: + DoSettingsBeforeStartingAGame(); + break; +#ifdef LEGACY_MENU_OPTIONS + case MENUACTION_RELOADIDE: + CFileLoader::ReloadObjectTypes("GTA3.IDE"); + break; +#endif + case MENUACTION_RESUME_FROM_SAVEZONE: + RequestFrontEndShutDown(); + break; + case MENUACTION_LOADRADIO: + if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) { + SwitchToNewScreen(MENUPAGE_SOUND_SETTINGS); + DMAudio.PlayFrontEndTrack(m_PrefsRadioStation, TRUE); + OutputDebugString("STARTED PLAYING FRONTEND AUDIO TRACK"); + } + break; +#ifdef MISSION_REPLAY + case MENUACTION_REJECT_RETRY: + doingMissionRetry = false; + AllowMissionReplay = MISSION_RETRY_STAGE_NORMAL; + RequestFrontEndShutDown(); + break; + case MENUACTION_UNK114: + doingMissionRetry = false; + RequestFrontEndShutDown(); + RetryMission(MISSION_RETRY_TYPE_BEGIN_RESTARTING); + return; +#endif + case MENUACTION_SAVEGAME: + { + int saveSlot = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_SaveSlot; + + if (saveSlot >= 2 && saveSlot <= 9) { + m_nCurrSaveSlot = m_nCurrOption; + SwitchToNewScreen(MENUPAGE_SAVE_OVERWRITE_CONFIRM); + } + break; + } + case MENUACTION_RADARMODE: + if (++m_PrefsRadarMode > 2) + m_PrefsRadarMode = 0; + SaveSettings(); + break; + case MENUACTION_GOBACK: + goBack = true; + break; + case MENUACTION_KEYBOARDCTRLS: + SwitchToNewScreen(MENUPAGE_KEYBOARD_CONTROLS); + m_nSelectedListRow = 0; + m_nCurrExLayer = HOVEROPTION_LIST; + break; + case MENUACTION_GETKEY: + m_CurrCntrlAction = GetStartOptionsCntrlConfigScreens() + m_nCurrOption; + m_bKeyIsOK = true; + m_bWaitingForNewKeyBind = true; + m_bStartWaitingForKeyBind = true; + pControlEdit = &m_KeyPressedCode; + break; + case MENUACTION_CANCELGAME: + DMAudio.Service(); + SwitchToNewScreen(MENUPAGE_OUTRO); + break; + case MENUACTION_RESUME: +#ifdef LEGACY_MENU_OPTIONS + if (m_PrefsVsyncDisp != m_PrefsVsync) { + m_PrefsVsync = m_PrefsVsyncDisp; + } +#endif + RequestFrontEndShutDown(); + break; + case MENUACTION_DONTCANCEL: + SwitchToNewScreen(-2); + break; + case MENUACTION_SCREENRES: + if (m_nDisplayVideoMode != m_nPrefsVideoMode) { + m_nPrefsVideoMode = m_nDisplayVideoMode; + _psSelectScreenVM(m_nPrefsVideoMode); + DMAudio.ChangeMusicMode(MUSICMODE_FRONTEND); + DMAudio.Service(); + CentreMousePointer(); + m_bShowMouse = true; + m_nCurrOption = 5; // TODO(Miami): Because selected option is resetted after res. change. We'll need to revisit that. + m_nOptionHighlightTransitionBlend = 0; + SaveSettings(); + } + break; + case MENUACTION_AUDIOHW: + { + int selectedProvider = m_nPrefsAudio3DProviderIndex; + if (selectedProvider != NO_AUDIO_PROVIDER) { + if (selectedProvider == -1) + selectedProvider = m_nPrefsAudio3DProviderIndex = DMAudio.AutoDetect3DProviders(); + + m_nPrefsAudio3DProviderIndex = DMAudio.SetCurrent3DProvider(m_nPrefsAudio3DProviderIndex); + if (selectedProvider != m_nPrefsAudio3DProviderIndex) { + SetHelperText(5); + } + SaveSettings(); + } + break; + } + case MENUACTION_SPEAKERCONF: + if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) { + if (--m_PrefsSpeakers < 0) + m_PrefsSpeakers = 2; + DMAudio.SetSpeakerConfig(m_PrefsSpeakers); + SaveSettings(); + } + break; + case MENUACTION_PLAYERSETUP: + CPlayerSkin::BeginFrontendSkinEdit(); + SwitchToNewScreen(MENUPAGE_SKIN_SELECT); + m_bSkinsEnumerated = false; + m_nCurrExLayer = HOVEROPTION_LIST; + break; + case MENUACTION_RESTOREDEF: + if (m_nCurrScreen == MENUPAGE_SOUND_SETTINGS) { + m_nPrefsAudio3DProviderIndex = DMAudio.AutoDetect3DProviders(); + DMAudio.SetCurrent3DProvider(m_nPrefsAudio3DProviderIndex); + m_PrefsSfxVolume = 49; + m_PrefsMusicVolume = 49; + m_PrefsRadioStation = EMOTION; + m_PrefsMP3BoostVolume = 0; + m_PrefsStereoMono = 1; + m_PrefsSpeakers = 0; + DMAudio.SetMP3BoostVolume(m_PrefsMP3BoostVolume); + DMAudio.SetMusicMasterVolume(m_PrefsMusicVolume); + DMAudio.SetEffectsMasterVolume(m_PrefsSfxVolume); + DMAudio.SetRadioInCar(m_PrefsRadioStation); + DMAudio.PlayFrontEndTrack(m_PrefsRadioStation, TRUE); + SaveSettings(); + } else if (m_nCurrScreen == MENUPAGE_DISPLAY_SETTINGS) { + m_PrefsBrightness = 256; + m_PrefsLOD = 1.2f; +#ifdef LEGACY_MENU_OPTIONS + m_PrefsVsync = true; +#endif + CRenderer::ms_lodDistScale = m_PrefsLOD; + m_PrefsShowSubtitles = false; +#ifdef ASPECT_RATIO_SCALE + m_PrefsUseWideScreen = AR_AUTO; +#else + m_PrefsUseWideScreen = false; +#endif + m_PrefsShowLegends = true; + m_PrefsVsyncDisp = true; + m_PrefsFrameLimiter = true; + m_PrefsRadarMode = 0; + m_PrefsShowHud = true; + m_nDisplayVideoMode = m_nPrefsVideoMode; + CMBlur::BlurOn = false; +#ifdef CUSTOM_FRONTEND_OPTIONS + extern void RestoreDefGraphics(int8); + extern void RestoreDefDisplay(int8); + + RestoreDefGraphics(FEOPTION_ACTION_SELECT); + RestoreDefDisplay(FEOPTION_ACTION_SELECT); +#endif + SaveSettings(); + } else if (m_nCurrScreen == MENUPAGE_CONTROLLER_PC) { + ControlsManager.MakeControllerActionsBlank(); + ControlsManager.InitDefaultControlConfiguration(); + ControlsManager.InitDefaultControlConfigMouse(MousePointerStateHelper.GetMouseSetUp()); +#if defined(RW_DC) + printf("TODO: implement this"); +#elif !defined RW_GL3 + if (AllValidWinJoys.m_aJoys[JOYSTICK1].m_bInitialised) { + DIDEVCAPS devCaps; + devCaps.dwSize = sizeof(DIDEVCAPS); + PSGLOBAL(joy1)->GetCapabilities(&devCaps); + ControlsManager.InitDefaultControlConfigJoyPad(devCaps.dwButtons); + } +#else + if (PSGLOBAL(joy1id) != -1 && glfwJoystickPresent(PSGLOBAL(joy1id))) { + int count; + glfwGetJoystickButtons(PSGLOBAL(joy1id), &count); + ControlsManager.InitDefaultControlConfigJoyPad(count); + } +#endif + MousePointerStateHelper.bInvertVertically = true; + TheCamera.m_bHeadBob = false; +#ifdef FIX_BUGS + TheCamera.m_fMouseAccelVertical = 0.003f; +#endif + TheCamera.m_fMouseAccelHorzntl = 0.0025f; + CVehicle::m_bDisableMouseSteering = true; + m_ControlMethod = CONTROL_STANDARD; +#ifdef PC_PLAYER_CONTROLS + TheCamera.m_bUseMouse3rdPerson = true; +#else + TheCamera.m_bUseMouse3rdPerson = false; +#endif + SaveSettings(); +#ifdef LOAD_INI_SETTINGS + SaveINIControllerSettings(); +#endif + } + SetHelperText(2); + break; + case MENUACTION_CTRLMETHOD: + if (m_ControlMethod == CONTROL_CLASSIC) { + CCamera::m_bUseMouse3rdPerson = true; + m_ControlMethod = CONTROL_STANDARD; + } else { + CCamera::m_bUseMouse3rdPerson = false; + m_ControlMethod = CONTROL_CLASSIC; + } + SaveSettings(); + break; +#ifdef CUSTOM_FRONTEND_OPTIONS + case MENUACTION_CFO_SELECT: + case MENUACTION_CFO_DYNAMIC: + CMenuScreenCustom::CMenuEntry &option = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption]; + if (option.m_Action == MENUACTION_CFO_SELECT) { + if (option.m_CFOSelect->disableIfGameLoaded && !m_bGameNotLoaded) + break; + + if (!option.m_CFOSelect->onlyApplyOnEnter) { + option.m_CFOSelect->displayedValue++; + if (option.m_CFOSelect->displayedValue >= option.m_CFOSelect->numRightTexts || option.m_CFOSelect->displayedValue < 0) + option.m_CFOSelect->displayedValue = 0; + } + int8 oldValue = *(int8*)option.m_CFO->value; + + *(int8*)option.m_CFO->value = option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue; + + // Now everything is saved in .ini, and LOAD_INI_SETTINGS is fundamental for CFO + // if (option.m_CFOSelect->save) + SaveSettings(); + + if (option.m_CFOSelect->displayedValue != oldValue && option.m_CFOSelect->changeFunc) + option.m_CFOSelect->changeFunc(oldValue, option.m_CFOSelect->displayedValue); + + } else if (option.m_Action == MENUACTION_CFO_DYNAMIC) { + if (option.m_CFODynamic->buttonPressFunc) + option.m_CFODynamic->buttonPressFunc(FEOPTION_ACTION_SELECT); + } + + break; +#endif + } + ProcessOnOffMenuOptions(); + if (!goBack) { +#ifdef FIX_BUGS + int saveSlot = aScreens[currScreen].m_aEntries[currOption].m_SaveSlot; + if (saveSlot >= SAVESLOT_1 && saveSlot <= SAVESLOT_8 && Slots[currOption] != SLOT_OK) +#else + int saveSlot = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_SaveSlot; + if (saveSlot >= SAVESLOT_1 && saveSlot <= SAVESLOT_8 && Slots[m_nCurrOption] != SLOT_OK) +#endif + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_FAIL, 0); + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_ENTER_OR_ADJUST, 0); + } + } + + if (goBack) { + if (m_NoEmptyBinding) { + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_BACK, 0); + SwitchToNewScreen(-2); + if (hasNativeList(m_nCurrScreen)) { + m_nTotalListRow = 0; + } + } else { + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_FAIL, 0); + m_ShowEmptyBindingError = true; + } + } + + if (changeAmount != 0) { + switch (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action) { +#ifdef GAMEPAD_MENU + case MENUACTION_CTRLCONFIG: + CPad::GetPad(0)->Mode += changeAmount; + if (CPad::GetPad(0)->Mode > 3) + CPad::GetPad(0)->Mode = 0; + else if (CPad::GetPad(0)->Mode < 0) + CPad::GetPad(0)->Mode = 3; + SaveSettings(); + break; +#endif + case MENUACTION_RADIO: + ChangeRadioStation(changeAmount); + break; + case MENUACTION_RADARMODE: + m_PrefsRadarMode += changeAmount; + if (m_PrefsRadarMode < 0) + m_PrefsRadarMode = 2; + if (m_PrefsRadarMode > 2) + m_PrefsRadarMode = 0; + break; +#ifdef ASPECT_RATIO_SCALE + case MENUACTION_WIDESCREEN: + if (changeAmount > 0) { + m_PrefsUseWideScreen++; + if (m_PrefsUseWideScreen > AR_MAX - 1) + m_PrefsUseWideScreen = 0; + } else { + m_PrefsUseWideScreen--; + if (m_PrefsUseWideScreen < 0) + m_PrefsUseWideScreen = AR_MAX - 1; + } + SaveSettings(); + break; +#endif + case MENUACTION_SCREENRES: + if (m_bGameNotLoaded) { + RwChar** videoMods = _psGetVideoModeList(); + if (changeAmount > 0) { + do { + ++m_nDisplayVideoMode; + + if (m_nDisplayVideoMode >= _psGetNumVideModes()) + m_nDisplayVideoMode = 0; + } while (!videoMods[m_nDisplayVideoMode]); + } else { + do { + --m_nDisplayVideoMode; + + if (m_nDisplayVideoMode < 0) + m_nDisplayVideoMode = _psGetNumVideModes() - 1; + } while (!videoMods[m_nDisplayVideoMode]); + } + } + break; + case MENUACTION_AUDIOHW: + if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) { + m_nPrefsAudio3DProviderIndex += changeAmount; + + bool checkIfForbidden = true; + while (checkIfForbidden) { + checkIfForbidden = false; + + if (m_nPrefsAudio3DProviderIndex < -1) + m_nPrefsAudio3DProviderIndex = DMAudio.GetNum3DProvidersAvailable() - 1; + else if (m_nPrefsAudio3DProviderIndex > DMAudio.GetNum3DProvidersAvailable() - 1) + m_nPrefsAudio3DProviderIndex = -1; + + // what a retarded move... + if (m_nPrefsAudio3DProviderIndex != -1) { + char* provider = DMAudio.Get3DProviderName(m_nPrefsAudio3DProviderIndex); + strupr(provider); + if (!strcmp(provider, "MILES FAST 2D POSITIONAL AUDIO")) { + m_nPrefsAudio3DProviderIndex += changeAmount; + checkIfForbidden = true; + + } else if (!strcmp(provider, "AUREAL A3D 2.0 (TM)")) { + m_nPrefsAudio3DProviderIndex += changeAmount; + checkIfForbidden = true; + + } else if (!strcmp(provider, "AUREAL A3D INTERACTIVE (TM)")) { + m_nPrefsAudio3DProviderIndex += changeAmount; + checkIfForbidden = true; + } + } + } + } + break; + case MENUACTION_SPEAKERCONF: + if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) { + m_PrefsSpeakers -= changeAmount; + m_PrefsSpeakers = Clamp(m_PrefsSpeakers, 0, 2); + DMAudio.SetSpeakerConfig(m_PrefsSpeakers); + SaveSettings(); + } + break; + case MENUACTION_CTRLMETHOD: + m_ControlMethod = !m_ControlMethod; + CCamera::m_bUseMouse3rdPerson = !m_ControlMethod; + SaveSettings(); + break; +#ifdef CUSTOM_FRONTEND_OPTIONS + case MENUACTION_CFO_SELECT: + case MENUACTION_CFO_DYNAMIC: + CMenuScreenCustom::CMenuEntry &option = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption]; + if (option.m_Action == MENUACTION_CFO_SELECT) { + if (option.m_CFOSelect->disableIfGameLoaded && !m_bGameNotLoaded) + break; + + if (changeAmount > 0) { + option.m_CFOSelect->displayedValue++; + if (option.m_CFOSelect->displayedValue >= option.m_CFOSelect->numRightTexts) + option.m_CFOSelect->displayedValue = 0; + } else { + option.m_CFOSelect->displayedValue--; + if (option.m_CFOSelect->displayedValue < 0) + option.m_CFOSelect->displayedValue = option.m_CFOSelect->numRightTexts - 1; + } + if (!option.m_CFOSelect->onlyApplyOnEnter) { + int8 oldValue = *(int8*)option.m_CFO->value; + + *(int8*)option.m_CFO->value = option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue; + + // Now everything is saved in .ini, and LOAD_INI_SETTINGS is fundamental for CFO + // if (option.m_CFOSelect->save) + SaveSettings(); + + if (option.m_CFOSelect->displayedValue != oldValue && option.m_CFOSelect->changeFunc) + option.m_CFOSelect->changeFunc(oldValue, option.m_CFOSelect->displayedValue); + } + } else if (option.m_Action == MENUACTION_CFO_DYNAMIC && option.m_CFODynamic->buttonPressFunc) { + option.m_CFODynamic->buttonPressFunc(changeAmount > 0 ? FEOPTION_ACTION_RIGHT : FEOPTION_ACTION_LEFT); + } + break; +#endif + } + CheckSliderMovement(changeAmount); + ProcessOnOffMenuOptions(); + if (m_nCurrScreen == MENUPAGE_KEYBOARD_CONTROLS) { + if (changeAmount < 1) { + m_nSelectedContSetupColumn = CONTSETUP_PED_COLUMN; + } else { + m_nSelectedContSetupColumn = CONTSETUP_VEHICLE_COLUMN; + } + } + } +} + +void +CMenuManager::ProcessOnOffMenuOptions() +{ + switch (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action) { +#ifdef GAMEPAD_MENU + case MENUACTION_CTRLVIBRATION: + m_PrefsUseVibration = !m_PrefsUseVibration; + if (m_PrefsUseVibration) { + CPad::GetPad(0)->StartShake(350, 150); + TimeToStopPadShaking = CTimer::GetTimeInMillisecondsPauseMode() + 500; + } + SaveSettings(); + break; +#endif + case MENUACTION_INVERTPADY: + CPad::bInvertLook4Pad = !CPad::bInvertLook4Pad; + SaveSettings(); // FIX: Why don't SaveSettings? Because of it's an hidden option? :( + break; + case MENUACTION_CTRLDISPLAY: + m_DisplayControllerOnFoot = !m_DisplayControllerOnFoot; + break; + case MENUACTION_FRAMESYNC: + m_PrefsVsyncDisp = !m_PrefsVsyncDisp; + SaveSettings(); // FIX: Again... This makes me very unhappy + break; + case MENUACTION_FRAMELIMIT: + m_PrefsFrameLimiter = !m_PrefsFrameLimiter; + SaveSettings(); + break; + case MENUACTION_TRAILS: + CMBlur::BlurOn = !CMBlur::BlurOn; + SaveSettings(); + break; + case MENUACTION_SUBTITLES: + m_PrefsShowSubtitles = !m_PrefsShowSubtitles; + SaveSettings(); + break; +#ifndef ASPECT_RATIO_SCALE + case MENUACTION_WIDESCREEN: + m_PrefsUseWideScreen = !m_PrefsUseWideScreen; + SaveSettings(); + break; +#endif + case MENUACTION_LEGENDS: + m_PrefsShowLegends = !m_PrefsShowLegends; + break; + case MENUACTION_HUD: + m_PrefsShowHud = !m_PrefsShowHud; + SaveSettings(); + break; +#ifdef LEGACY_MENU_OPTIONS + case MENUACTION_SETDBGFLAG: + CTheScripts::InvertDebugFlag(); + break; + case MENUACTION_SWITCHBIGWHITEDEBUGLIGHT: + gbBigWhiteDebugLightSwitchedOn = !gbBigWhiteDebugLightSwitchedOn; + break; + case MENUACTION_COLLISIONPOLYS: + gbShowCollisionPolys = !gbShowCollisionPolys; + break; +#endif + case MENUACTION_SHOWHEADBOB: + TheCamera.m_bHeadBob = !TheCamera.m_bHeadBob; + SaveSettings(); + break; + case MENUACTION_INVVERT: + MousePointerStateHelper.bInvertVertically = !MousePointerStateHelper.bInvertVertically; + SaveSettings(); + break; + case MENUACTION_DYNAMICACOUSTIC: + m_PrefsDMA = !m_PrefsDMA; + DMAudio.SetDynamicAcousticModelingStatus(m_PrefsDMA); + SaveSettings(); + break; + case MENUACTION_MOUSESTEER: + if (m_ControlMethod == CONTROL_STANDARD) { + CVehicle::m_bDisableMouseSteering = !CVehicle::m_bDisableMouseSteering; + SaveSettings(); + } + break; + } +} + +void +CMenuManager::RequestFrontEndShutDown() +{ + m_bShutDownFrontEndRequested = true; +} + +void +CMenuManager::RequestFrontEndStartUp() +{ + m_bStartUpFrontEndRequested = true; +} + +void +CMenuManager::ResetHelperText() +{ + m_nHelperTextMsgId = 0; + m_nHelperTextAlpha = 300; +} + +void +CMenuManager::SetHelperText(int text) +{ + m_nHelperTextMsgId = text; + m_nHelperTextAlpha = 300; +} + +float +CMenuManager::StretchX(float x) +{ + if (SCREEN_WIDTH == DEFAULT_SCREEN_WIDTH) + return x; + else + // We won't make this SCREEN_SCALE, because many cases relies on stretching and we want the code to be portable. + // Instead we will use MENU_X_LEFT_ALIGNED or SCREEN_SCALE_X when needed. + return SCREEN_STRETCH_X(x); +} + +float CMenuManager::StretchY(float y) +{ + if (SCREEN_HEIGHT == DEFAULT_SCREEN_HEIGHT) + return y; + else + return SCREEN_STRETCH_Y(y); +} + +#ifdef XBOX_MESSAGE_SCREEN +void +CMenuManager::CloseDialog(void) +{ + // We don't have this on PC GXT :shrug: + static wchar* gameSaved = AllocUnicode("Game saved successfully!"); + + if (m_bSaveWasSuccessful && DialogTextCmp("FESZ_WR")) { + m_bSaveWasSuccessful = false; // i don't know where XBOX resets that + m_pDialogText = gameSaved; + SetDialogTimer(1000); + ProcessDialogTimer(); + } else { + ToggleDialog(false); + } + +} + +void +CMenuManager::ProcessDialogTimer(void) +{ + if (!m_bDialogOpen || m_nDialogHideTimer == 0) + return; + + // Also XBOX has unified time source for in-game/menu, but we don't have that + if (m_bMenuActive && CTimer::GetTimeInMilliseconds() > m_nDialogHideTimer || !m_bMenuActive && CTimer::GetTimeInMillisecondsPauseMode() > m_nDialogHideTimerPauseMode) { + + // This is originally activePage.funcs->closePage() + CloseDialog(); + } +} + +void +CMenuManager::SetDialogTimer(uint32 timer) +{ + // XBOX iterates some page list(actives?) and then sets timer variable of specified page to specified value. We only have dialog right now. + // Also XBOX has unified time source for in-game/menu, but we don't have that, thus 2 timer variables... + + m_nDialogHideTimer = CTimer::GetTimeInMilliseconds() + timer; + m_nDialogHideTimerPauseMode = CTimer::GetTimeInMillisecondsPauseMode() + timer; +} + +void +CMenuManager::SetDialogText(const char* key) +{ + // There are many things going around here, idk why + m_pDialogText = TheText.Get(key); +} + +bool +CMenuManager::DialogTextCmp(const char* key) +{ + wchar *value = TheText.Get(key); + wchar *i = m_pDialogText; + for (; *i != '\0' && *value != '\0'; i++, value++) { + if (*i != *value) + return false; + } + return *i == '\0' && *value == '\0'; +} + +void +CMenuManager::ToggleDialog(bool toggle) +{ + // This originally calls some mysterious function on enable and close CB on disable, along with decreasing some counter. Which is no use for dialog + + // XBOX doesn't do that + if (toggle) + m_nDialogHideTimer = 0; + + m_bDialogOpen = toggle; +} + +void +DrawDialogBg(float offset, uint8 alpha) +{ + CSprite2d::Draw2DPolygon(SCALE_AND_CENTER_X(84.f + offset), MENU_Y(126.f + offset), + SCALE_AND_CENTER_X(512.f + offset), MENU_Y(109.f + offset), + SCALE_AND_CENTER_X(100.f + offset), MENU_Y(303.f + offset), + SCALE_AND_CENTER_X(474.f + offset), MENU_Y(311.f + offset), CRGBA(107, 193, 236, alpha)); + CSprite2d::Draw2DPolygon(SCALE_AND_CENTER_X(523.f + offset), MENU_Y(108.f + offset), + SCALE_AND_CENTER_X(542.f + offset), MENU_Y(107.f + offset), + SCALE_AND_CENTER_X(485.f + offset), MENU_Y(310.f + offset), + SCALE_AND_CENTER_X(516.f + offset), MENU_Y(311.f + offset), CRGBA(107, 193, 236, alpha)); +} + +void +CMenuManager::DrawOverlays(void) +{ + // This is stripped to show only Dialog box, XBOX does much more in here. + + if (!m_bDialogOpen) + return; + + DefinedState(); + + CSprite2d::DrawRect(CRect(0, SCREEN_HEIGHT, SCREEN_WIDTH, 0), CRGBA(0, 0, 0, 160)); + + // Ofc this is not hardcoded like that on Xbox, it should be a texture + DrawDialogBg(20.f, 160); // shadow + DrawDialogBg(0.f, 255); + + CFont::SetBackgroundOff(); + CFont::SetPropOn(); + CFont::SetJustifyOn(); + CFont::SetBackGroundOnlyTextOn(); + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); + CFont::SetCentreSize(SCREEN_SCALE_X(380.0f)); + CFont::SetCentreOn(); + CFont::SetColor(CRGBA(LABEL_COLOR.r, LABEL_COLOR.g, LABEL_COLOR.b, 255)); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + // Both of those are 0.9 on Xbox, which is ofcouse wrong... + CFont::SetScale(SCREEN_SCALE_X(BIGTEXT_X_SCALE), SCREEN_SCALE_Y(BIGTEXT_Y_SCALE)); + + int x = SCREEN_WIDTH / 2.f - SCREEN_SCALE_X(30.0f); + int y = SCREEN_HEIGHT / 2.f - SCREEN_SCALE_Y(30.0f); + int numOfLines = CFont::GetNumberLines(x, y, m_pDialogText); + CFont::PrintString(x, y - SCREEN_SCALE_Y(numOfLines / 2.f), m_pDialogText); + CFont::DrawFonts(); +} +#endif + +void +CMenuManager::ProcessFileActions() +{ + switch (m_nCurrScreen) { + case MENUPAGE_LOADING_IN_PROGRESS: +#ifdef MISSION_REPLAY + if (MissionSkipLevel) { + if (gGameState != GS_PLAYING_GAME) + DoSettingsBeforeStartingAGame(); + RequestFrontEndShutDown(); + break; + } + if (doingMissionRetry) { + RetryMission(MISSION_RETRY_TYPE_BEGIN_RESTARTING); + m_nCurrSaveSlot = SLOT_COUNT; + doingMissionRetry = false; + } +#endif + if (CheckSlotDataValid(m_nCurrSaveSlot)) { +#ifdef USE_DEBUG_SCRIPT_LOADER + CTheScripts::ScriptToLoad = 0; +#endif + +#ifdef XBOX_MESSAGE_SCREEN + SetDialogText("FELD_WR"); + ToggleDialog(true); +#else + if (!m_bGameNotLoaded) + MessageScreen("FELD_WR", true); +#endif + DoSettingsBeforeStartingAGame(); + m_bWantToLoad = true; + } else + SwitchToNewScreen(MENUPAGE_NEW_GAME); + + break; + case MENUPAGE_DELETING_IN_PROGRESS: + { + static bool waitedForScreen = false; + + if (waitedForScreen) { + bool SlotPopulated = false; + if (PcSaveHelper.DeleteSlot(m_nCurrSaveSlot)) { + PcSaveHelper.PopulateSlotInfo(); + SlotPopulated = true; + } + + if (SlotPopulated) { + SwitchToNewScreen(MENUPAGE_DELETE_SUCCESSFUL); + } else { + SwitchToNewScreen(MENUPAGE_SAVE_CUSTOM_WARNING); + strncpy(aScreens[m_nCurrScreen].m_ScreenName, "FES_DEL", 8); + strncpy(aScreens[m_nCurrScreen].m_aEntries[0].m_EntryName, "FES_DEE", 8); + } + waitedForScreen = false; + } else if (m_nMenuFadeAlpha >= 255) + waitedForScreen = true; + + break; + } + case MENUPAGE_SAVING_IN_PROGRESS: + { +#ifdef XBOX_MESSAGE_SCREEN + if (m_bDialogOpen && DialogTextCmp("FESZ_WR")) { + uint32 startTime = CTimer::GetTimeInMillisecondsPauseMode(); + int8 SaveSlot = PcSaveHelper.SaveSlot(m_nCurrSaveSlot); + PcSaveHelper.PopulateSlotInfo(); + + // Original code, but we don't want redundant saving text if it doesn't +#if 0 + CTimer::Update(); // not on Xbox, who updates it? + + // it compensates the lag to show saving text always one second... how cute + int dialogDur = Max(1, startTime - CTimer::GetTimeInMillisecondsPauseMode() + 1000); +#else + int dialogDur = 1; +#endif + + if (SaveSlot) { + // error. PC code + ToggleDialog(false); + SwitchToNewScreen(MENUPAGE_SAVE_CUSTOM_WARNING); + strncpy(aScreens[m_nCurrScreen].m_ScreenName, "FET_SG", 8); + strncpy(aScreens[m_nCurrScreen].m_aEntries[0].m_EntryName, "FES_CMP", 8); + + } else { + m_bSaveWasSuccessful = true; + SetDialogTimer(dialogDur); + ProcessDialogTimer(); + RequestFrontEndShutDown(); + } + + } else { + SetDialogText("FESZ_WR"); + ToggleDialog(true); + } +#else + static bool waitedForScreen = false; + + if (waitedForScreen) { + int8 SaveSlot = PcSaveHelper.SaveSlot(m_nCurrSaveSlot); + PcSaveHelper.PopulateSlotInfo(); + if (SaveSlot) { + SwitchToNewScreen(MENUPAGE_SAVE_CUSTOM_WARNING); + strncpy(aScreens[m_nCurrScreen].m_ScreenName, "FET_SG", 8); + strncpy(aScreens[m_nCurrScreen].m_aEntries[0].m_EntryName, "FES_CMP", 8); + } else + SwitchToNewScreen(MENUPAGE_SAVE_SUCCESSFUL); + + waitedForScreen = false; + } else if (m_nMenuFadeAlpha >= 255) + waitedForScreen = true; +#endif + break; + } + } +} + +void +CMenuManager::SwitchMenuOnAndOff() +{ + if (!TheCamera.m_WideScreenOn) { + + // Reminder: You need REGISTER_START_BUTTON defined to make it work. + if ((CPad::GetPad(0)->GetStartJustDown() || CPad::GetPad(0)->GetEscapeJustDown()) + && (!m_bMenuActive || m_nCurrScreen == MENUPAGE_PAUSE_MENU || m_nCurrScreen == MENUPAGE_CHOOSE_SAVE_SLOT || m_nCurrScreen == MENUPAGE_SAVE_CHEAT_WARNING) + || m_bShutDownFrontEndRequested || m_bStartUpFrontEndRequested +#ifdef REGISTER_START_BUTTON + || CPad::GetPad(0)->GetStartJustDown() && !m_bGameNotLoaded +#endif + ) { + + if (m_nCurrScreen != MENUPAGE_LOADING_IN_PROGRESS +#ifdef XBOX_MESSAGE_SCREEN + && m_nCurrScreen != MENUPAGE_SAVING_IN_PROGRESS +#endif + ) { + DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); + DoRWStuffEndOfFrame(); + DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); + DoRWStuffEndOfFrame(); + } + + if (m_bShutDownFrontEndRequested) + m_bMenuActive = false; + else if (m_bStartUpFrontEndRequested) + m_bMenuActive = true; + else + m_bMenuActive = !m_bMenuActive; + + if (m_bMenuActive) { + if (_InputMouseNeedsExclusive()) { + _InputShutdownMouse(); + _InputInitialiseMouse(false); + } + Initialise(); + LoadAllTextures(); +#ifdef FIX_BUGS + CPad::StopPadsShaking(); +#endif + } else { +#ifdef EXTENDED_COLOURFILTER + // we always expect CPostFX to be open + CMBlur::BlurOn = true; +#endif + if (CMBlur::BlurOn) + CMBlur::MotionBlurOpen(Scene.camera); + else + CMBlur::MotionBlurClose(); + DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); + DoRWStuffEndOfFrame(); + DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); + DoRWStuffEndOfFrame(); + if (_InputMouseNeedsExclusive()) { + _InputShutdownMouse(); + _InputInitialiseMouse(true); + } + + m_StatsScrollSpeed = 150.0f; +#ifdef FIX_BUGS + ThingsToDoBeforeLeavingPage(); +#endif + SaveSettings(); +#ifdef LOAD_INI_SETTINGS + SaveINIControllerSettings(); +#endif + pControlEdit = nil; + pEditString = nil; + DisplayComboButtonErrMsg = false; + m_bShutDownFrontEndRequested = false; + m_bStartUpFrontEndRequested = false; + m_bWaitingForNewKeyBind = false; + +#ifdef REGISTER_START_BUTTON + int16 start1 = CPad::GetPad(0)->PCTempJoyState.Start, start2 = CPad::GetPad(0)->PCTempKeyState.Start, + start3 = CPad::GetPad(0)->OldState.Start, start4 = CPad::GetPad(0)->NewState.Start; +#endif + CPad::GetPad(0)->Clear(false); + CPad::GetPad(1)->Clear(false); +#ifdef REGISTER_START_BUTTON + CPad::GetPad(0)->PCTempJoyState.Start = start1; + CPad::GetPad(0)->PCTempKeyState.Start = start2; + CPad::GetPad(0)->OldState.Start = start3; + CPad::GetPad(0)->NewState.Start = start4; +#endif + UnloadTextures(); + CTimer::EndUserPause(); + CTimer::Update(); + m_OnlySaveMenu = false; + } + } + } + + // Just entered the save/safe zone + if (m_bActivateSaveMenu) { + DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); + DoRWStuffEndOfFrame(); + DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); + DoRWStuffEndOfFrame(); + m_bActivateSaveMenu = false; + m_bMenuActive = true; + m_OnlySaveMenu = true; + + if (_InputMouseNeedsExclusive()) { + _InputShutdownMouse(); + _InputInitialiseMouse(false); + } + + Initialise(); + LoadAllTextures(); + + if (CPad::bHasPlayerCheated) { + m_nCurrScreen = MENUPAGE_SAVE_CHEAT_WARNING; + m_nCurrOption = 0; + } else { + m_nCurrScreen = MENUPAGE_CHOOSE_SAVE_SLOT; + m_nCurrOption = 8; + } + } + + m_bStartUpFrontEndRequested = false; + m_bShutDownFrontEndRequested = false; + +#ifdef GAMEPAD_MENU + // Reset pad shaking. + if (TimeToStopPadShaking && TimeToStopPadShaking < CTimer::GetTimeInMillisecondsPauseMode()) { + CPad::StopPadsShaking(); + TimeToStopPadShaking = 0; + } +#endif +} + +void +CMenuManager::UnloadTextures() +{ + if (m_nCurrScreen == MENUPAGE_SOUND_SETTINGS) + DMAudio.StopFrontEndTrack(); + + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_STARTING, 0); + DMAudio.ChangeMusicMode(MUSICMODE_GAME); + if (m_bSpritesLoaded) { + printf("REMOVE frontend\n"); + int frontend = CTxdStore::FindTxdSlot("frontend1"); + for (int i = 0; i < 3; ++i) + m_aFrontEndSprites[i].Delete(); + + CTxdStore::RemoveTxd(frontend); + + if (!m_OnlySaveMenu) { + int frontend2 = CTxdStore::FindTxdSlot("frontend2"); + for (int i = 3; i < NUM_MENU_SPRITES; ++i) + m_aFrontEndSprites[i].Delete(); + + CTxdStore::RemoveTxd(frontend2); + +#ifdef GAMEPAD_MENU + // Unload controller txd + int frontend_controller = CTxdStore::FindTxdSlot("frontend_controller"); + if (frontend_controller != -1) + CTxdStore::RemoveTxd(frontend_controller); +#endif + } + + m_bSpritesLoaded = false; + } + m_OnlySaveMenu = false; + CUserDisplay::PlaceName.ProcessAfterFrontEndShutDown(); +} + +void +CMenuManager::WaitForUserCD() +{ + CSprite2d *splash; + char *splashscreen = nil; + + splash = LoadSplash(splashscreen); + + if (RsGlobal.quit) + return; + + HandleExit(); + CPad::UpdatePads(); + MessageScreen("NO_PCCD", true); + + if (CPad::GetPad(0)->GetEscapeJustDown()) { + m_bQuitGameNoCD = true; + RsEventHandler(rsQUITAPP, nil); + } +} + +void +CMenuManager::DrawQuitGameScreen(void) +{ + static int32 exitSignalTimer = 0; + +#ifdef FIX_BUGS + int alpha = Clamp(m_nMenuFadeAlpha, 0, 255); +#else + int alpha = m_nMenuFadeAlpha; +#endif + +#ifndef MUCH_SHORTER_OUTRO_SCREEN + static uint32 lastTickIncrease = 0; + if (alpha == 255 && CTimer::GetTimeInMillisecondsPauseMode() - lastTickIncrease > 10) { + exitSignalTimer++; + lastTickIncrease = CTimer::GetTimeInMillisecondsPauseMode(); + } +#else + static uint32 firstTick = CTimer::GetTimeInMillisecondsPauseMode(); + if (alpha == 255 && CTimer::GetTimeInMillisecondsPauseMode() - firstTick > 750) { + exitSignalTimer = 150; + } +#endif + static CSprite2d *splash = nil; + + if (splash == nil) + splash = LoadSplash("OUTRO"); + + m_aFrontEndSprites[MENUSPRITE_VCLOGO].Draw(CRect(SCREEN_STRETCH_X(28.0f), MENU_Y(8.0f), SCREEN_STRETCH_X(27.0f) + MENU_X(130.f), MENU_Y(138.0f)), CRGBA(255, 255, 255, 255 - alpha)); + + // Or we can see menu background from sides +#ifdef ASPECT_RATIO_SCALE + CSprite2d::DrawRect(CRect(0, 0, MENU_X_LEFT_ALIGNED(0.f), SCREEN_HEIGHT), CRGBA(0, 0, 0, alpha)); + CSprite2d::DrawRect(CRect(MENU_X_RIGHT_ALIGNED(0.f), 0, SCREEN_WIDTH, SCREEN_HEIGHT), CRGBA(0, 0, 0, alpha)); +#endif + + splash->Draw(CRect(MENU_X_LEFT_ALIGNED(0.f), 0, MENU_X_RIGHT_ALIGNED(0.f), SCREEN_HEIGHT), CRGBA(255, 255, 255, alpha)); + if (alpha == 255 && exitSignalTimer == 150) + RsEventHandler(rsQUITAPP, nil); + + m_bShowMouse = false; + m_AllowNavigation = false; +} + +void +CMenuManager::PrintMap(void) +{ + m_bMenuMapActive = true; + CRadar::InitFrontEndMap(); + + // Because m_fMapSize is half of the map length(hence * 2), and map consists of 3x3 tiles(hence / 3). + float halfTile = m_fMapSize * 2.f / 3.f / 2.f; + + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); + + if (SCREEN_WIDTH >= m_fMapCenterX - m_fMapSize || SCREEN_HEIGHT >= m_fMapCenterY - m_fMapSize) { + m_aFrontEndSprites[MENUSPRITE_MAPTOP01].Draw(CRect(m_fMapCenterX - m_fMapSize, m_fMapCenterY - m_fMapSize, + m_fMapCenterX - halfTile, m_fMapCenterY - halfTile), CRGBA(255, 255, 255, FadeIn(255))); + } + + if (SCREEN_WIDTH >= m_fMapCenterX - halfTile || SCREEN_HEIGHT >= m_fMapCenterY - m_fMapSize) { + m_aFrontEndSprites[MENUSPRITE_MAPTOP02].Draw(CRect(m_fMapCenterX - halfTile, m_fMapCenterY - m_fMapSize, + m_fMapCenterX + halfTile, m_fMapCenterY - halfTile), CRGBA(255, 255, 255, FadeIn(255))); + } + + if (SCREEN_WIDTH >= m_fMapCenterX + halfTile || SCREEN_HEIGHT >= m_fMapCenterY - m_fMapSize) { + m_aFrontEndSprites[MENUSPRITE_MAPTOP03].Draw(CRect(m_fMapCenterX + halfTile, m_fMapCenterY - m_fMapSize, + m_fMapCenterX + m_fMapSize, m_fMapCenterY - halfTile), CRGBA(255, 255, 255, FadeIn(255))); + } + + if (SCREEN_WIDTH >= m_fMapCenterX - m_fMapSize || SCREEN_HEIGHT >= m_fMapCenterY - halfTile) { + m_aFrontEndSprites[MENUSPRITE_MAPMID01].Draw(CRect(m_fMapCenterX - m_fMapSize, m_fMapCenterY - halfTile, + m_fMapCenterX - halfTile, m_fMapCenterY + halfTile), CRGBA(255, 255, 255, FadeIn(255))); + } + + if (SCREEN_WIDTH >= m_fMapCenterX - halfTile || SCREEN_HEIGHT >= m_fMapCenterY - halfTile) { + m_aFrontEndSprites[MENUSPRITE_MAPMID02].Draw(CRect(m_fMapCenterX - halfTile, m_fMapCenterY - halfTile, + m_fMapCenterX + halfTile, m_fMapCenterY + halfTile), CRGBA(255, 255, 255, FadeIn(255))); + } + + if (SCREEN_WIDTH >= m_fMapCenterX + halfTile || SCREEN_HEIGHT >= m_fMapCenterY - halfTile) { + m_aFrontEndSprites[MENUSPRITE_MAPMID03].Draw(CRect(m_fMapCenterX + halfTile, m_fMapCenterY - halfTile, + m_fMapCenterX + m_fMapSize, m_fMapCenterY + halfTile), CRGBA(255, 255, 255, FadeIn(255))); + } + + if (SCREEN_WIDTH >= m_fMapCenterX - m_fMapSize || SCREEN_HEIGHT >= m_fMapCenterY + halfTile) { + m_aFrontEndSprites[MENUSPRITE_MAPBOT01].Draw(CRect(m_fMapCenterX - m_fMapSize, m_fMapCenterY + halfTile, + m_fMapCenterX - halfTile, m_fMapCenterY + m_fMapSize), CRGBA(255, 255, 255, FadeIn(255))); + } + + if (SCREEN_WIDTH >= m_fMapCenterX - halfTile || SCREEN_HEIGHT >= m_fMapCenterY + halfTile) { + m_aFrontEndSprites[MENUSPRITE_MAPBOT02].Draw(CRect(m_fMapCenterX - halfTile, m_fMapCenterY + halfTile, + m_fMapCenterX + halfTile, m_fMapCenterY + m_fMapSize), CRGBA(255, 255, 255, FadeIn(255))); + } + + if (SCREEN_WIDTH >= m_fMapCenterX + halfTile || SCREEN_HEIGHT >= m_fMapCenterY + halfTile) { + m_aFrontEndSprites[MENUSPRITE_MAPBOT03].Draw(CRect(m_fMapCenterX + halfTile, m_fMapCenterY + halfTile, + m_fMapCenterX + m_fMapSize, m_fMapCenterY + m_fMapSize), CRGBA(255, 255, 255, FadeIn(255))); + } + + CRadar::DrawBlips(); + if (m_PrefsShowLegends) { + CFont::SetWrapx(MENU_X_RIGHT_ALIGNED(40.0f)); + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(84.0f)); + CFont::SetBackGroundOnlyTextOff(); + CFont::SetColor(CRGBA(LABEL_COLOR.r, LABEL_COLOR.g, LABEL_COLOR.b, FadeIn(255))); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(255))); + CFont::SetCentreOn(); + CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); + CFont::SetScale(SCREEN_SCALE_X(0.65f), SCREEN_SCALE_Y(0.95f)); + + int secondColumnStart = (CRadar::MapLegendCounter - 1) / 2; + int boxBottom = MENU_Y(100.0f); + + // + 3, because we want 19*3 px padding + for (int i = 0; i < secondColumnStart + 3; i++) { + boxBottom += MENU_Y(19.f); + } + + CSprite2d::DrawRect(CRect(MENU_X_LEFT_ALIGNED(95.0f), MENU_Y(100.0f), MENU_X_LEFT_ALIGNED(555.f), boxBottom), + CRGBA(0, 0, 0, FadeIn(190))); + + CFont::PrintString(MENU_X_LEFT_ALIGNED(320.0f), MENU_Y(102.0f), TheText.Get("FE_MLG")); + CFont::SetRightJustifyOff(); + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); + if (m_PrefsLanguage == LANGUAGE_AMERICAN) + CFont::SetScale(SCREEN_SCALE_X(0.55f), SCREEN_SCALE_Y(0.55f)); + else + CFont::SetScale(SCREEN_SCALE_X(0.45f), SCREEN_SCALE_Y(0.55f)); + + CFont::SetColor(CRGBA(225, 225, 225, FadeIn(255))); + CFont::SetDropShadowPosition(0); + + int y = MENU_Y(127.0f); + int x = MENU_X_LEFT_ALIGNED(160.0f); + + for (int16 i = 0; i < CRadar::MapLegendCounter; i++) { + CRadar::DrawLegend(x, y, CRadar::MapLegendList[i]); + + if (i == secondColumnStart) { + x = MENU_X_LEFT_ALIGNED(350.0f); + y = MENU_Y(127.0f); + } else { + y += MENU_Y(19.0f); + } + } + } + +#ifdef MAP_ENHANCEMENTS + if (m_nMenuFadeAlpha != 255 && !m_bShowMouse) { + mapCrosshair.x = SCREEN_WIDTH / 2; + mapCrosshair.y = SCREEN_HEIGHT / 2; + } else if (m_bShowMouse) { + mapCrosshair.x = m_nMousePosX; + mapCrosshair.y = m_nMousePosY; + } + + CSprite2d::DrawRect(CRect(mapCrosshair.x - MENU_X(1.0f), 0.0f, + mapCrosshair.x + MENU_X(1.0f), SCREEN_HEIGHT), + CRGBA(0, 0, 0, 150)); + CSprite2d::DrawRect(CRect(0.0f, mapCrosshair.y + MENU_X(1.0f), + SCREEN_WIDTH, mapCrosshair.y - MENU_X(1.0f)), + CRGBA(0, 0, 0, 150)); + +#endif + m_bMenuMapActive = false; + + CFont::SetWrapx(MENU_X_RIGHT_ALIGNED(MENU_X_MARGIN)); + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(MENU_X_MARGIN)); + DisplayHelperText("FEH_MPH"); +} + +void +CMenuManager::ChangeRadioStation(int8 increaseBy) +{ + if (m_ScrollRadioBy != 0) + return; + + m_PrefsRadioStation += increaseBy; + m_ScrollRadioBy = increaseBy; + if (m_ScrollRadioBy == 1) { + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_ENTER_OR_ADJUST, 0); + m_LeftMostRadioX = MENU_X_LEFT_ALIGNED(MENURADIO_ICON_FIRST_X); + } else { + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_ENTER_OR_ADJUST, 0); + m_LeftMostRadioX = MENU_X_LEFT_ALIGNED(MENURADIO_ICON_FIRST_X - (2 * MENURADIO_ICON_SIZE)); + } + + if (DMAudio.IsMP3RadioChannelAvailable()) { + if (m_PrefsRadioStation < WILDSTYLE) + m_PrefsRadioStation = USERTRACK; + if (m_PrefsRadioStation > USERTRACK) + m_PrefsRadioStation = WILDSTYLE; + } else { + if (m_PrefsRadioStation < WILDSTYLE) + m_PrefsRadioStation = WAVE; + if (m_PrefsRadioStation > WAVE) + m_PrefsRadioStation = WILDSTYLE; + } + DMAudio.StopFrontEndTrack(); + DMAudio.PlayFrontEndSound(SOUND_RADIO_CHANGE, 0); +} + +#if 0 +uint8 CMenuManager::GetNumberOfMenuOptions() +{ + uint8 Rows = -1; + for (int i = 0; i < NUM_MENUROWS; i++) { + if (aScreens[m_nCurrScreen].m_aEntries[i].m_Action == MENUACTION_NOTHING) + break; + + ++Rows; + } + return Rows; +} +#endif + +#ifdef GAMEPAD_MENU +const char* controllerTypesPaths[] = { + "MODELS/FRONTEND_DS2.TXD", + "MODELS/FRONTEND_DS3.TXD", + "MODELS/FRONTEND_DS4.TXD", + "MODELS/FRONTEND_X360.TXD", + "MODELS/FRONTEND_XONE.TXD", + "MODELS/FRONTEND_NSW.TXD", +}; + +void +CMenuManager::PrintController(void) +{ + // Don't print anything if controller texture is missing + if (!m_aFrontEndSprites[MENUSPRITE_CONTROLLER].m_pTexture) return; + + const float scale = 0.9f; + const float CONTROLLER_SIZE_X = 235.2f; + const float CONTROLLER_SIZE_Y = 175.2f; + const float CONTROLLER_POS_X = (DEFAULT_SCREEN_WIDTH - CONTROLLER_SIZE_X) / 2.0f; + const float CONTROLLER_POS_Y = 220.0f; + + float centerX = CONTROLLER_POS_X + CONTROLLER_SIZE_X / 2; + float centerY = CONTROLLER_POS_Y + CONTROLLER_SIZE_Y / 2; + +#define X(f) ((f)*scale + centerX) +#define Y(f) ((f)*scale + centerY) + + m_aFrontEndSprites[MENUSPRITE_CONTROLLER].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, FadeIn(255))); + if (m_DisplayControllerOnFoot) { + if ((int)CTimer::GetTimeInMillisecondsPauseMode() & 0x400) + m_aFrontEndSprites[MENUSPRITE_ARROWS1].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, FadeIn(255))); + else + m_aFrontEndSprites[MENUSPRITE_ARROWS3].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, FadeIn(255))); + } + else { + if ((int)CTimer::GetTimeInMillisecondsPauseMode() & 0x400) + m_aFrontEndSprites[MENUSPRITE_ARROWS2].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, FadeIn(255))); + else + m_aFrontEndSprites[MENUSPRITE_ARROWS4].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, FadeIn(255))); + } + + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); + + CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.9f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.9f)); // X + + CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(255))); + CFont::SetDropShadowPosition(0); + CFont::SetColor(CRGBA(0, 0, 0, FadeIn(255))); + CFont::SetWrapx(SCREEN_WIDTH); + + float TEXT_L2_X = 85.0f + CONTROLLER_POS_X - centerX, TEXT_L2_Y = -14.0f + CONTROLLER_POS_Y - centerY; + float TEXT_L1_X = -4.0f + CONTROLLER_POS_X - centerX, TEXT_L1_Y = 27.0f + CONTROLLER_POS_Y - centerY, TEXT_L1_Y_VEH = 3.0f + TEXT_L1_Y; + float TEXT_DPAD_X = -4.0f + CONTROLLER_POS_X - centerX, TEXT_DPAD_Y = 67.0f + CONTROLLER_POS_Y - centerY; + float TEXT_LSTICK_X = -4.0f + CONTROLLER_POS_X - centerX, TEXT_LSTICK_Y = 97.0f + CONTROLLER_POS_Y - centerY; + float TEXT_SELECT_X = 170.0f + CONTROLLER_POS_X - centerX, TEXT_SELECT_Y = 141.0f + CONTROLLER_POS_Y - centerY; + float TEXT_START_X = 130.0f + CONTROLLER_POS_X - centerX, TEXT_START_Y = 128.0f + CONTROLLER_POS_Y - centerY; + float TEXT_R2_X = 164.0f + CONTROLLER_POS_X - centerX, TEXT_R2_Y = -14.0f + CONTROLLER_POS_Y - centerY; + float TEXT_R1_X = 242.0f + CONTROLLER_POS_X - centerX, TEXT_R1_Y = 27.0f + CONTROLLER_POS_Y - centerY; + + float TEXT_SQUARE_X = 147.0f + CONTROLLER_POS_X - centerX, TEXT_SQUARE_Y = 30.0f + CONTROLLER_POS_Y - centerY; + float TEXT_TRIANGLE_X = 242.0f + CONTROLLER_POS_X - centerX, TEXT_TRIANGLE_Y = 55.0f + CONTROLLER_POS_Y - centerY; + float TEXT_CIRCLE_X = 242.0f + CONTROLLER_POS_X - centerX, TEXT_CIRCLE_Y = 67.0f + CONTROLLER_POS_Y - centerY; + float TEXT_CROSS_X = 242.0f + CONTROLLER_POS_X - centerX, TEXT_CROSS_Y = 80.0f + CONTROLLER_POS_Y - centerY; + float TEXT_RSTICK_X = 242.0f + CONTROLLER_POS_X - centerX, TEXT_RSTICK_Y = 97.0f + CONTROLLER_POS_Y - centerY; + float TEXT_R3_X = 242.0f + CONTROLLER_POS_X - centerX, TEXT_R3_Y = 110.0f + CONTROLLER_POS_Y - centerY; + float TEXT_L3_X = 94.0f + CONTROLLER_POS_X - centerX, TEXT_L3_Y = 162.0f + CONTROLLER_POS_Y - centerY; + float TEXT_L2R2_X = 120.0f + CONTROLLER_POS_X - centerX, TEXT_L2R2_Y = -4.0f + CONTROLLER_POS_Y - centerY; + + switch (m_PrefsControllerType) + { + case CONTROLLER_DUALSHOCK4: + TEXT_L1_Y += 7.0f; + TEXT_L1_Y_VEH = TEXT_L1_Y; + TEXT_R1_Y += 7.0f; + TEXT_TRIANGLE_Y -= 1.0f; + TEXT_CIRCLE_Y -= 1.0f; + TEXT_CROSS_Y -= 1.0f; + TEXT_RSTICK_Y -= 4.0f; + TEXT_R3_Y -= 4.0f; + TEXT_DPAD_Y -= 2.0f; + TEXT_LSTICK_Y -= 6.0f; + TEXT_L3_X -= 2.0f; + break; + case CONTROLLER_XBOXONE: + TEXT_L2_X -= 2.0f; + TEXT_R2_X += 2.0f; + TEXT_L1_Y += 15.0f; + TEXT_L1_Y_VEH = TEXT_L1_Y; + TEXT_R1_Y += 15.0f; + TEXT_TRIANGLE_Y += 4.0f; + TEXT_CIRCLE_Y += 4.0f; + TEXT_CROSS_Y += 4.0f; + TEXT_RSTICK_Y += 1.0f; + TEXT_R3_Y += 1.0f; + TEXT_DPAD_Y += 29.0f; + TEXT_LSTICK_Y -= 20.0f; + TEXT_L3_X -= 36.0f; + TEXT_L2R2_Y += 5.0f; + TEXT_SELECT_X += 4.0f; + break; + case CONTROLLER_XBOX360: + TEXT_L2_X += 8.0f; + TEXT_R2_X -= 8.0f; + TEXT_L1_Y += 15.0f; + TEXT_L1_Y_VEH = TEXT_L1_Y; + TEXT_R1_Y += 15.0f; + TEXT_TRIANGLE_Y += 4.0f; + TEXT_CIRCLE_Y += 4.0f; + TEXT_CROSS_Y += 4.0f; + TEXT_RSTICK_Y += 4.0f; + TEXT_R3_Y += 4.0f; + TEXT_DPAD_Y += 30.0f; + TEXT_LSTICK_Y -= 19.0f; + TEXT_L3_X -= 36.0f; + TEXT_L2R2_Y += 5.0f; + TEXT_SELECT_X += 3.0f; + break; + case CONTROLLER_NINTENDO_SWITCH: + TEXT_L1_Y += 5.0f; + TEXT_L1_Y_VEH = TEXT_L1_Y; + TEXT_R1_Y += 5.0f; + TEXT_TRIANGLE_Y += 3.0f; + TEXT_CIRCLE_Y += 3.0f; + TEXT_CROSS_Y += 3.0f; + TEXT_LSTICK_Y -= 23.0f; + TEXT_DPAD_Y += 25.0; + TEXT_RSTICK_Y += 1.0f; + TEXT_R3_Y += 1.0f; + break; + }; + + if (m_DisplayControllerOnFoot) { + switch (CPad::GetPad(0)->Mode) { + case 0: + CFont::SetRightJustifyOn(); + switch (m_PrefsLanguage) + { + case LANGUAGE_FRENCH: + case LANGUAGE_SPANISH: + TEXT_L2_X -= 45.0f; + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)) - SCREEN_SCALE_X(85)); + break; + default: + CFont::SetRightJustifyWrap(0); + break; + } + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_CWL")); + CFont::SetRightJustifyWrap(0); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y)), TheText.Get("FEC_LOF")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_MOV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_MOV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_CR3")); + CFont::SetRightJustifyOn(); + switch (m_PrefsLanguage) + { + case LANGUAGE_GERMAN: + TEXT_SELECT_X += 20.0f; + break; + case LANGUAGE_SPANISH: + TEXT_SELECT_X += 15.0f; + break; + default: + break; + } + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM")); + CFont::SetJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); + switch (m_PrefsLanguage) + { + case LANGUAGE_FRENCH: + case LANGUAGE_SPANISH: + TEXT_R2_X += 30.0f; + CFont::SetJustifyOff(); + CFont::SetWrapx(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)) + SCREEN_SCALE_X(120)); + break; + default: + break; + } + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_CWR")); + CFont::SetJustifyOn(); + CFont::SetWrapx(SCREEN_WIDTH); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_TAR")); + CFont::SetRightJustifyOn(); + CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_JUM")); + CFont::SetJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_ENV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_ATT")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_RUN")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_FPC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_LB3")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y + 13.0f)), TheText.Get("FEC_R3")); + break; + case 1: + CFont::SetRightJustifyOn(); + switch (m_PrefsLanguage) + { + case LANGUAGE_FRENCH: + case LANGUAGE_SPANISH: + TEXT_L2_X -= 45.0f; + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)) - SCREEN_SCALE_X(85)); + break; + default: + CFont::SetRightJustifyWrap(0); + break; + } + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_CWL")); + CFont::SetRightJustifyWrap(0); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y)), TheText.Get("FEC_LOF")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_CAM")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_MOV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_CR3")); + switch (m_PrefsLanguage) + { + case LANGUAGE_GERMAN: + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_NA")); + break; + default: + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X - 50)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_NA")); + break; + } + CFont::SetJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); + switch (m_PrefsLanguage) + { + case LANGUAGE_FRENCH: + case LANGUAGE_SPANISH: + TEXT_R2_X += 30.0f; + CFont::SetJustifyOff(); + CFont::SetWrapx(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)) + SCREEN_SCALE_X(120)); + break; + default: + break; + } + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_CWR")); + CFont::SetJustifyOn(); + CFont::SetWrapx(SCREEN_WIDTH); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_TAR")); + CFont::SetRightJustifyOn(); + CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_JUM")); + CFont::SetJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_ENV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_ATT")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_RUN")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_FPC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_LB3")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y + 13.0f)), TheText.Get("FEC_R3")); + break; + case 2: + CFont::SetRightJustifyOn(); + switch (m_PrefsLanguage) + { + case LANGUAGE_FRENCH: + case LANGUAGE_SPANISH: + TEXT_L2_X -= 45.0f; + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)) - SCREEN_SCALE_X(85)); + break; + default: + CFont::SetRightJustifyWrap(0); + break; + } + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_CWL")); + CFont::SetRightJustifyWrap(0); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y)), TheText.Get("FEC_ENV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_MOV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_MOV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_CR3")); + CFont::SetRightJustifyOn(); + switch (m_PrefsLanguage) + { + case LANGUAGE_GERMAN: + TEXT_SELECT_X += 20.0f; + break; + case LANGUAGE_SPANISH: + TEXT_SELECT_X += 15.0f; + break; + default: + break; + } + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM")); + CFont::SetJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); + switch (m_PrefsLanguage) + { + case LANGUAGE_FRENCH: + case LANGUAGE_SPANISH: + TEXT_R2_X += 30.0f; + CFont::SetJustifyOff(); + CFont::SetWrapx(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)) + SCREEN_SCALE_X(120)); + break; + default: + break; + } + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_CWR")); + CFont::SetJustifyOn(); + CFont::SetWrapx(SCREEN_WIDTH); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_TAR")); + CFont::SetRightJustifyOn(); + CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_JUM")); + CFont::SetJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_LOF")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_RUN")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_ATT")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_FPC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_LB3")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y + 13.0f)), TheText.Get("FEC_R3")); + break; + case 3: + CFont::SetRightJustifyOn(); + switch (m_PrefsLanguage) + { + case LANGUAGE_FRENCH: + case LANGUAGE_SPANISH: + TEXT_L2_X -= 45.0f; + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)) - SCREEN_SCALE_X(85)); + break; + default: + CFont::SetRightJustifyWrap(0); + break; + } + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_CWL")); + CFont::SetRightJustifyWrap(0); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y)), TheText.Get("FEC_TAR")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_NA")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_MOV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_CR3")); + CFont::SetRightJustifyOn(); + switch (m_PrefsLanguage) + { + case LANGUAGE_GERMAN: + TEXT_SELECT_X += 20.0f; + break; + case LANGUAGE_SPANISH: + TEXT_SELECT_X += 15.0f; + break; + default: + break; + } + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM")); + CFont::SetJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); + switch (m_PrefsLanguage) + { + case LANGUAGE_FRENCH: + case LANGUAGE_SPANISH: + TEXT_R2_X += 30.0f; + CFont::SetJustifyOff(); + CFont::SetWrapx(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)) + SCREEN_SCALE_X(120)); + break; + default: + break; + } + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_CWR")); + CFont::SetJustifyOn(); + CFont::SetWrapx(SCREEN_WIDTH); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_ATT")); + CFont::SetRightJustifyOn(); + CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_JUM")); + CFont::SetJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_ENV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_LOF")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_RUN")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_FPC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_LB3")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y + 13.0f)), TheText.Get("FEC_R3")); + break; + default: + return; + } + } + else { + CFont::SetCentreOn(); + switch (m_PrefsLanguage) + { + case LANGUAGE_ITALIAN: + if (m_PrefsControllerType != CONTROLLER_XBOX360) + break; + case LANGUAGE_FRENCH: + case LANGUAGE_GERMAN: + CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); + break; + default: + break; + } + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(0.0f)), MENU_Y(Y(TEXT_L2R2_Y)), TheText.Get("FEC_LB")); + CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.9f), MENU_Y(SMALLESTTEXT_Y_SCALE* scale * 0.9f)); + switch (CPad::GetPad(0)->Mode) { + case 0: + CFont::SetRightJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_LL")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y_VEH)), TheText.Get("FEC_RSC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_VES")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_VES")); + CFont::SetRightJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L3_X)), MENU_Y(Y(TEXT_L3_Y)), TheText.Get("FEC_HO3")); + switch (m_PrefsLanguage) + { + case LANGUAGE_FRENCH: + TEXT_SELECT_X -= 5.0f; + break; + case LANGUAGE_GERMAN: + TEXT_SELECT_X += 20.0f; + break; + case LANGUAGE_SPANISH: + TEXT_SELECT_X += 15.0f; + break; + default: + break; + } + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)) - SCREEN_SCALE_X(80)); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM")); + CFont::SetJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_LR")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_HAB")); + CFont::SetJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_EXV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_CAW")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_ACC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_TUC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_SM3")); + CFont::SetRightJustifyOn(); + switch (m_PrefsControllerType) + { + case CONTROLLER_XBOXONE: + case CONTROLLER_XBOX360: + case CONTROLLER_NINTENDO_SWITCH: + CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); + switch (m_PrefsLanguage) + { + case LANGUAGE_FRENCH: + TEXT_SQUARE_X += 3.0f; + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(90)); + break; + case LANGUAGE_GERMAN: + case LANGUAGE_SPANISH: + TEXT_SQUARE_X += 18.0f; + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(90)); + break; + default: + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(60)); + break; + } + break; + default: + switch (m_PrefsLanguage) + { + case LANGUAGE_FRENCH: + TEXT_SQUARE_X -= 15.0f; + TEXT_SQUARE_Y += 5.0f; + CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(60)); + break; + case LANGUAGE_GERMAN: + TEXT_SQUARE_X -= 15.0f; + TEXT_SQUARE_Y += 10.0f; + CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE* scale * 0.65f)); + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(60)); + break; + case LANGUAGE_SPANISH: + TEXT_SQUARE_X += 15.0f; + case LANGUAGE_ITALIAN: + CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); + default: + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(100)); + break; + } + break; + } + CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_BRA")); + break; + case 1: + CFont::SetRightJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_LL")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y_VEH)), TheText.Get("FEC_HOR")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_CAM")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_VES")); + CFont::SetRightJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L3_X)), MENU_Y(Y(TEXT_L3_Y)), TheText.Get("FEC_NA")); + switch (m_PrefsLanguage) + { + case LANGUAGE_GERMAN: + TEXT_SELECT_X += 20.0f; + break; + case LANGUAGE_SPANISH: + TEXT_SELECT_X += 12.0f; + break; + default: + break; + } + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)) - SCREEN_SCALE_X(80)); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_RSC")); + CFont::SetJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_LR")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_HAB")); + CFont::SetJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_EXV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_CAW")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_ACC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_TUC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_SM3")); + CFont::SetRightJustifyOn(); + switch (m_PrefsControllerType) + { + case CONTROLLER_XBOXONE: + case CONTROLLER_XBOX360: + case CONTROLLER_NINTENDO_SWITCH: + CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); + switch (m_PrefsLanguage) + { + case LANGUAGE_FRENCH: + TEXT_SQUARE_X += 3.0f; + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(90)); + break; + case LANGUAGE_GERMAN: + case LANGUAGE_SPANISH: + TEXT_SQUARE_X += 18.0f; + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(90)); + break; + default: + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(60)); + break; + } + break; + default: + switch (m_PrefsLanguage) + { + case LANGUAGE_FRENCH: + TEXT_SQUARE_X -= 15.0f; + TEXT_SQUARE_Y += 5.0f; + CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(60)); + break; + case LANGUAGE_GERMAN: + TEXT_SQUARE_X -= 15.0f; + TEXT_SQUARE_Y += 10.0f; + CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(60)); + break; + case LANGUAGE_SPANISH: + TEXT_SQUARE_X += 15.0f; + case LANGUAGE_ITALIAN: + CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); + default: + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(100)); + break; + } + break; + } + CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_BRA")); + break; + case 2: + CFont::SetRightJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_LL")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y_VEH)), TheText.Get("FEC_EXV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_VES")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_VES")); + CFont::SetRightJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L3_X)), MENU_Y(Y(TEXT_L3_Y)), TheText.Get("FEC_RS3")); + switch (m_PrefsLanguage) + { + case LANGUAGE_FRENCH: + TEXT_SELECT_X -= 5.0f; + break; + case LANGUAGE_GERMAN: + TEXT_SELECT_X += 20.0f; + break; + case LANGUAGE_SPANISH: + TEXT_SELECT_X += 15.0f; + break; + default: + break; + } + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)) - SCREEN_SCALE_X(80)); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM")); + CFont::SetJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_LR")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_HOR")); + CFont::SetJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_HAB")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_CAW")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_ACC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_TUC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_SM3")); + CFont::SetRightJustifyOn(); + switch (m_PrefsControllerType) + { + case CONTROLLER_XBOXONE: + case CONTROLLER_XBOX360: + case CONTROLLER_NINTENDO_SWITCH: + CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); + switch (m_PrefsLanguage) + { + case LANGUAGE_FRENCH: + TEXT_SQUARE_X += 3.0f; + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(90)); + break; + case LANGUAGE_GERMAN: + case LANGUAGE_SPANISH: + TEXT_SQUARE_X += 18.0f; + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(90)); + break; + default: + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(60)); + break; + } + break; + default: + switch (m_PrefsLanguage) + { + case LANGUAGE_FRENCH: + TEXT_SQUARE_X -= 15.0f; + TEXT_SQUARE_Y += 5.0f; + CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(60)); + break; + case LANGUAGE_GERMAN: + TEXT_SQUARE_X -= 15.0f; + TEXT_SQUARE_Y += 10.0f; + CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(60)); + break; + case LANGUAGE_SPANISH: + TEXT_SQUARE_X += 15.0f; + case LANGUAGE_ITALIAN: + CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); + default: + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(100)); + break; + } + break; + } + CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_BRA")); + break; + case 3: + CFont::SetRightJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_LL")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y_VEH)), TheText.Get("FEC_HAB")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_TUC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_VES")); + CFont::SetRightJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L3_X)), MENU_Y(Y(TEXT_L3_Y)), TheText.Get("FEC_HO3")); + switch (m_PrefsLanguage) + { + case LANGUAGE_FRENCH: + TEXT_SELECT_X -= 5.0f; + break; + case LANGUAGE_GERMAN: + TEXT_SELECT_X += 20.0f; + break; + case LANGUAGE_SPANISH: + TEXT_SELECT_X += 15.0f; + break; + default: + break; + } + CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)) - SCREEN_SCALE_X(80)); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM")); + CFont::SetJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_LR")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_CAW")); + CFont::SetJustifyOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_EXV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_RSC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_NA")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_ACC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_BRA")); + CFont::SetRightJustifyOn(); + CFont::SetRightJustifyWrap(0); + switch (m_PrefsControllerType) + { + case CONTROLLER_XBOXONE: + case CONTROLLER_XBOX360: + case CONTROLLER_NINTENDO_SWITCH: + CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); + CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_SMT")); + break; + default: + switch (m_PrefsLanguage) + { + case LANGUAGE_GERMAN: + TEXT_SQUARE_X += 5.0f; + case LANGUAGE_FRENCH: + case LANGUAGE_ITALIAN: + CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); + CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_SMT")); + break; + default: + CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X + 16.0f)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_SMT")); + break; + } + break; + } + break; + default: + return; + } + } + + CFont::SetDropShadowPosition(0); // X + +#undef X +#undef Y +} + + +void +CMenuManager::LoadController(int8 type) +{ + switch (type) + { + case CONTROLLER_DUALSHOCK2: + case CONTROLLER_DUALSHOCK3: + case CONTROLLER_DUALSHOCK4: + CFont::LoadButtons("MODELS/PS3BTNS.TXD"); + break; + case CONTROLLER_NINTENDO_SWITCH: + CFont::LoadButtons("MODELS/NSWBTNS.TXD"); + break; + default: + CFont::LoadButtons("MODELS/X360BTNS.TXD"); + break; + } + + // Unload current textures + for (int i = MENUSPRITE_CONTROLLER; i <= MENUSPRITE_ARROWS4; i++) + m_aFrontEndSprites[i].Delete(); + + // Unload txd + int frontend_controller = CTxdStore::FindTxdSlot("frontend_controller"); + if (frontend_controller != -1) + CTxdStore::RemoveTxd(frontend_controller); + + // Find the new txd to load + bool bTxdMissing = true; + if (controllerTypesPaths[type]) + if (int file = CFileMgr::OpenFile(controllerTypesPaths[type])) { + CFileMgr::CloseFile(file); + bTxdMissing = false; + } + + int txdSlot = -1; + + if (bTxdMissing) + // Not found, fall back to original textures + txdSlot = CTxdStore::FindTxdSlot("frontend2"); + else { + // Found, load txd + txdSlot = frontend_controller; + if (txdSlot == -1) + txdSlot = CTxdStore::AddTxdSlot("frontend_controller"); + CTxdStore::LoadTxd(txdSlot, controllerTypesPaths[type]); + CTxdStore::AddRef(txdSlot); + } + + assert(txdSlot != -1); + // Load new textures + CTxdStore::SetCurrentTxd(txdSlot); + for (int i = MENUSPRITE_CONTROLLER; i <= MENUSPRITE_ARROWS4; i++) { + m_aFrontEndSprites[i].SetTexture(FrontendFilenames[i][0], FrontendFilenames[i][1]); + m_aFrontEndSprites[i].SetAddressing(rwTEXTUREADDRESSBORDER); + } +} +#endif // GAMEPAD_MENU + +#undef GetBackJustUp +#undef GetBackJustDown diff --git a/src/miami/core/Frontend.h b/src/miami/core/Frontend.h new file mode 100644 index 00000000..6fd3763e --- /dev/null +++ b/src/miami/core/Frontend.h @@ -0,0 +1,874 @@ +#pragma once +#ifdef PS2_MENU +#include "Frontend_PS2.h" +#else + +#include "Sprite2d.h" +#include "Timer.h" + +#define MENUHEADER_POS_X 10.0f +#define MENUHEADER_POS_Y 10.0f +#define MENUHEADER_HEIGHT 2.0f +#define MENUHEADER_WIDTH 1.0f + +#define MENU_X_MARGIN 10.0f + +#define MENUACTION_SCALE_MULT 0.9f + +#define MENUSLIDER_BARS 16 +#define MENUSLIDER_LOGICAL_BARS MENUSLIDER_BARS + +#define MENULABEL_X_MARGIN 80.0f +#define MENULABEL_POS_X 100.0f +#define MENULABEL_POS_Y 97.0f + +#define MENU_DEFAULT_CONTENT_X 320 +#define MENU_DEFAULT_CONTENT_Y 100 +#define MENU_DEFAULT_LINE_HEIGHT 29 + +#define RIGHT_ALIGNED_TEXT_RIGHT_MARGIN(xMargin) (xMargin + 30.0f) + +#define MENURADIO_ICON_FIRST_X 238.f +#ifdef EXTERNAL_3D_SOUND +#define MENURADIO_ICON_Y 288.0f +#else +#define MENURADIO_ICON_Y 248.0f +#endif +#define MENURADIO_ICON_SIZE 60.0f +#ifdef EXTERNAL_3D_SOUND +#define MENURADIO_SELECTOR_START_Y 285.f // other options should leave room on the screen +#else +#define MENURADIO_SELECTOR_START_Y 245.0f +#endif +#define MENURADIO_SELECTOR_HEIGHT 65.f + +#define MENUSLIDER_X 500.0f +#define MENUSLIDER_UNK 100.0f +#define MENUSLIDER_SMALLEST_BAR 8.0f +#define MENUSLIDER_BIGGEST_BAR 25.0f + +#define BIGTEXT2_X_SCALE 0.6f // For FONT_STANDARD +#define BIGTEXT2_Y_SCALE 1.2f +#define BIGTEXT_X_SCALE 0.6f // For FONT_HEADING +#define BIGTEXT_Y_SCALE 1.0f +#define MEDIUMTEXT_X_SCALE 0.48f // For FONT_STANDARD +#define MEDIUMTEXT_Y_SCALE 1.0f +#define SMALLTEXT_X_SCALE 0.42f // For FONT_STANDARD +#define SMALLTEXT_Y_SCALE 0.9f +#define SMALLESTTEXT_X_SCALE 0.3f // For FONT_STANDARD +#define SMALLESTTEXT_Y_SCALE 0.7f + +#define LISTITEM_X_SCALE 0.4f // Only unproportional and commonly used scale for FONT_STANDARD +#define LISTITEM_Y_SCALE 0.6f + +#define HELPER_TEXT_RIGHT_MARGIN MENU_X_MARGIN +#define HELPER_TEXT_BOTTOM_MARGIN 18.f + +#define BUILDID_TEXT_LEFT_MARGIN 320.0f +#define BUILDID_TEXT_BOTTOM_MARGIN 20.0f + +#define PLAYERSETUP_LIST_TOP 58.0f +#define PLAYERSETUP_LIST_BOTTOM 95.0f +#define PLAYERSETUP_LIST_LEFT 200.0f +#define PLAYERSETUP_LIST_RIGHT 36.0f +#ifdef FIX_BUGS // See the scrollbar button drawing code +#define PLAYERSETUP_SCROLLBAR_WIDTH 19.0f +#else +#define PLAYERSETUP_SCROLLBAR_WIDTH 16.0f +#endif +#define PLAYERSETUP_SCROLLBUTTON_HEIGHT 17.0f +#define PLAYERSETUP_SCROLLBUTTON_TXD_DIMENSION 64 +#define PLAYERSETUP_SKIN_COLUMN_LEFT 220.0f +#define PLAYERSETUP_DATE_COLUMN_RIGHT 56.0f +#define PLAYERSETUP_LIST_BODY_TOP 77 +#define PLAYERSETUP_ROW_HEIGHT 9 + +#define STATS_ROW_HEIGHT 17.0f +#define STATS_ROW_LEFT_MARGIN 110.0f +#define STATS_ROW_RIGHT_MARGIN 113.0f +#define STATS_TOP_Y 135.0f // Just faded in +#define STATS_BOTTOM_Y 300.0f // Starts to fade out after that +#define STATS_FADING_AREA_LENGTH 50.0f +#define STATS_VISIBLE_START_Y (STATS_TOP_Y - 10.f) +#define STATS_VISIBLE_END_Y (STATS_BOTTOM_Y + 21.f) +#define STATS_RATING_X 320.0f +#define STATS_RATING_Y_1 85.0f +#define STATS_RATING_Y_2 110.0f + +#define BRIEFS_TOP_MARGIN 140.0f +#define BRIEFS_BOTTOM_MARGIN 280.0f +#define BRIEFS_LINE_X 100.0f +#define BRIEFS_LINE_HEIGHT 20.0f +#define BRIEFS_LINE_SPACING 10.0f + +#define CONTSETUP_STANDARD_ROW_HEIGHT 10.7f +#define CONTSETUP_CLASSIC_ROW_HEIGHT 9.0f +#define CONTSETUP_BOUND_HIGHLIGHT_HEIGHT 10 +#define CONTSETUP_BOUND_COLUMN_WIDTH 190.0f +#define CONTSETUP_LIST_TOP 58.0f +#define CONTSETUP_LIST_RIGHT 18.0f +#define CONTSETUP_LIST_BOTTOM 78.0f +#define CONTSETUP_LIST_LEFT 30.0f +#define CONTSETUP_COLUMN_1_X 40.0f +#define CONTSETUP_COLUMN_2_X 210.0f +#define CONTSETUP_COLUMN_3_X (CONTSETUP_COLUMN_2_X + CONTSETUP_BOUND_COLUMN_WIDTH + 10.0f) +#define CONTSETUP_BACK_RIGHT 35.0f +#define CONTSETUP_BACK_BOTTOM 82.0f +#define CONTSETUP_BACK_HEIGHT 25.0f + +enum +{ + MENUALIGN_LEFT = 1, + MENUALIGN_RIGHT, + MENUALIGN_CENTER, +}; + +enum eMenuSprites +{ + MENUSPRITE_BACKGROUND, + MENUSPRITE_VCLOGO, + MENUSPRITE_MOUSE, + MENUSPRITE_MAPTOP01, + MENUSPRITE_MAPTOP02, + MENUSPRITE_MAPTOP03, + MENUSPRITE_MAPMID01, + MENUSPRITE_MAPMID02, + MENUSPRITE_MAPMID03, + MENUSPRITE_MAPBOT01, + MENUSPRITE_MAPBOT02, + MENUSPRITE_MAPBOT03, + MENUSPRITE_WILDSTYLE, + MENUSPRITE_FLASH, + MENUSPRITE_KCHAT, + MENUSPRITE_FEVER, + MENUSPRITE_VROCK, + MENUSPRITE_VCPR, + MENUSPRITE_ESPANTOSO, + MENUSPRITE_EMOTION, + MENUSPRITE_WAVE, + MENUSPRITE_MP3, + MENUSPRITE_DOWNOFF, + MENUSPRITE_DOWNON, + MENUSPRITE_UPOFF, + MENUSPRITE_UPON, +#ifdef GAMEPAD_MENU + MENUSPRITE_CONTROLLER, + MENUSPRITE_ARROWS1, + MENUSPRITE_ARROWS2, + MENUSPRITE_ARROWS3, + MENUSPRITE_ARROWS4, +#endif + NUM_MENU_SPRITES +}; + +enum eSaveSlot +{ + SAVESLOT_NONE, + SAVESLOT_0, + SAVESLOT_1, + SAVESLOT_2, + SAVESLOT_3, + SAVESLOT_4, + SAVESLOT_5, + SAVESLOT_6, + SAVESLOT_7, + SAVESLOT_8, + SAVESLOT_LABEL = 36 +}; + +enum eMenuScreen +{ + MENUPAGE_DISABLED = -1, + MENUPAGE_STATS = 0, + MENUPAGE_NEW_GAME = 1, + MENUPAGE_BRIEFS = 2, + MENUPAGE_SOUND_SETTINGS = 3, + MENUPAGE_DISPLAY_SETTINGS = 4, + MENUPAGE_LANGUAGE_SETTINGS = 5, + MENUPAGE_MAP = 6, + MENUPAGE_NEW_GAME_RELOAD = 7, + MENUPAGE_CHOOSE_LOAD_SLOT = 8, + MENUPAGE_CHOOSE_DELETE_SLOT = 9, + MENUPAGE_LOAD_SLOT_CONFIRM = 10, + MENUPAGE_DELETE_SLOT_CONFIRM = 11, + MENUPAGE_LOADING_IN_PROGRESS = 12, + MENUPAGE_DELETING_IN_PROGRESS = 13, + MENUPAGE_DELETE_SUCCESSFUL = 14, + MENUPAGE_CHOOSE_SAVE_SLOT = 15, + MENUPAGE_SAVE_OVERWRITE_CONFIRM = 16, + MENUPAGE_SAVING_IN_PROGRESS = 17, + MENUPAGE_SAVE_SUCCESSFUL = 18, + MENUPAGE_SAVE_CUSTOM_WARNING = 19, + MENUPAGE_SAVE_CHEAT_WARNING = 20, + MENUPAGE_SKIN_SELECT = 21, + MENUPAGE_SAVE_UNUSED = 22, + MENUPAGE_SAVE_FAILED = 23, + MENUPAGE_SAVE_FAILED_2 = 24, + MENUPAGE_LOAD_FAILED = 25, + MENUPAGE_CONTROLLER_PC = 26, + MENUPAGE_OPTIONS = 27, + MENUPAGE_EXIT = 28, + MENUPAGE_START_MENU = 29, + MENUPAGE_KEYBOARD_CONTROLS = 30, + MENUPAGE_MOUSE_CONTROLS = 31, + MENUPAGE_PAUSE_MENU = 32, + MENUPAGE_NONE = 33, // Then chooses main menu or pause menu +#ifdef GAMEPAD_MENU + MENUPAGE_CONTROLLER_SETTINGS, +#endif +#ifdef LEGACY_MENU_OPTIONS + MENUPAGE_DEBUG_MENU, + MENUPAGE_CONTROLLER_PC_OLD1, + MENUPAGE_CONTROLLER_PC_OLD2, + MENUPAGE_CONTROLLER_PC_OLD3, + MENUPAGE_CONTROLLER_PC_OLD4, + MENUPAGE_CONTROLLER_DEBUG, +#endif +#ifdef CUSTOM_FRONTEND_OPTIONS + +#ifdef GRAPHICS_MENU_OPTIONS + MENUPAGE_GRAPHICS_SETTINGS, +#endif +#ifdef DETECT_JOYSTICK_MENU + MENUPAGE_DETECT_JOYSTICK, +#endif +#endif +#ifdef MISSION_REPLAY + MENUPAGE_MISSION_RETRY, +#endif + + MENUPAGE_OUTRO, // Originally 34, but CFO needs last screen to be empty to count number of menu pages + MENUPAGES +}; + +enum eMenuAction +{ +#ifdef CUSTOM_FRONTEND_OPTIONS + MENUACTION_CFO_SLIDER = -3, + MENUACTION_CFO_SELECT = -2, + MENUACTION_CFO_DYNAMIC = -1, +#endif + MENUACTION_NOTHING, + MENUACTION_LABEL, + MENUACTION_YES, + MENUACTION_NO, + MENUACTION_CHANGEMENU, + MENUACTION_INVERTPADY, + MENUACTION_CTRLDISPLAY, + MENUACTION_FRAMESYNC, + MENUACTION_FRAMELIMIT, + MENUACTION_TRAILS, + MENUACTION_SUBTITLES, + MENUACTION_WIDESCREEN, + MENUACTION_BRIGHTNESS, + MENUACTION_MUSICVOLUME, + MENUACTION_SFXVOLUME, + MENUACTION_RADIO, + MENUACTION_LANG_ENG, + MENUACTION_LANG_FRE, + MENUACTION_LANG_GER, + MENUACTION_LANG_ITA, + MENUACTION_LANG_SPA, + MENUACTION_POPULATESLOTS_CHANGEMENU, + MENUACTION_CHECKSAVE, + MENUACTION_NEWGAME, + MENUACTION_RESUME_FROM_SAVEZONE, + MENUACTION_RELOADIDE, + MENUACTION_SETDBGFLAG, + MENUACTION_LOADRADIO, + MENUACTION_SAVEGAME, + MENUACTION_SWITCHBIGWHITEDEBUGLIGHT, + MENUACTION_COLLISIONPOLYS, + MENUACTION_LEGENDS, + MENUACTION_RADARMODE, + MENUACTION_HUD, + MENUACTION_GOBACK, + MENUACTION_KEYBOARDCTRLS, + MENUACTION_GETKEY, + MENUACTION_SHOWHEADBOB, + MENUACTION_UNK38, // MENUACTION_PARSEHEAP? MENUACTION_DEBUGSTREAM? MENUACTION_MEMCARDSAVECONFIRM? + MENUACTION_INVVERT, + MENUACTION_CANCELGAME, + MENUACTION_RESUME, + MENUACTION_DONTCANCEL, + MENUACTION_SCREENRES, + MENUACTION_AUDIOHW, + MENUACTION_SPEAKERCONF, + MENUACTION_PLAYERSETUP, + MENUACTION_RESTOREDEF, + MENUACTION_CTRLMETHOD, + MENUACTION_DYNAMICACOUSTIC, + MENUACTION_MOUSESTEER, + MENUACTION_DRAWDIST, + MENUACTION_MOUSESENS, + MENUACTION_MP3VOLUMEBOOST, +#ifdef GAMEPAD_MENU + MENUACTION_CTRLVIBRATION, + MENUACTION_CTRLCONFIG, +#endif +#ifdef MISSION_REPLAY + MENUACTION_REJECT_RETRY, + MENUACTION_UNK114 +#endif +}; + +enum eCheckHover +{ + HOVEROPTION_0, + HOVEROPTION_1, + HOVEROPTION_RANDOM_ITEM, + HOVEROPTION_3, + HOVEROPTION_4, + HOVEROPTION_5, + HOVEROPTION_6, + HOVEROPTION_7, + HOVEROPTION_8, + HOVEROPTION_BACK, // also layer in controller setup and skin menu + HOVEROPTION_10, + HOVEROPTION_11, + HOVEROPTION_OVER_SCROLL_UP, + HOVEROPTION_OVER_SCROLL_DOWN, + HOVEROPTION_CLICKED_SCROLL_UP, + HOVEROPTION_CLICKED_SCROLL_DOWN, + HOVEROPTION_HOLDING_SCROLLBAR, + HOVEROPTION_PAGEUP, + HOVEROPTION_PAGEDOWN, + HOVEROPTION_LIST, // also layer in controller setup and skin menu + HOVEROPTION_SKIN, + HOVEROPTION_USESKIN, // also layer in controller setup and skin menu + HOVEROPTION_NEXT_RADIO, + HOVEROPTION_PREV_RADIO, + HOVEROPTION_INCREASE_BRIGHTNESS, + HOVEROPTION_DECREASE_BRIGHTNESS, + HOVEROPTION_INCREASE_DRAWDIST, + HOVEROPTION_DECREASE_DRAWDIST, + HOVEROPTION_INCREASE_MUSICVOLUME, + HOVEROPTION_DECREASE_MUSICVOLUME, + HOVEROPTION_INCREASE_SFXVOLUME, + HOVEROPTION_DECREASE_SFXVOLUME, + HOVEROPTION_INCREASE_MOUSESENS, + HOVEROPTION_DECREASE_MOUSESENS, + HOVEROPTION_INCREASE_MP3BOOST, + HOVEROPTION_DECREASE_MP3BOOST, +#ifdef CUSTOM_FRONTEND_OPTIONS + HOVEROPTION_INCREASE_CFO_SLIDER, + HOVEROPTION_DECREASE_CFO_SLIDER, +#endif + HOVEROPTION_NOT_HOVERING, +}; + +enum +{ +#if defined LEGACY_MENU_OPTIONS || defined CUSTOM_FRONTEND_OPTIONS + NUM_MENUROWS = 18, +#else + NUM_MENUROWS = 12, +#endif +}; + +enum eControlMethod +{ + CONTROL_STANDARD = 0, + CONTROL_CLASSIC, +}; + +// Why?? +enum ControllerSetupColumn +{ + CONTSETUP_PED_COLUMN = 0, + CONTSETUP_VEHICLE_COLUMN = 16, +}; + +struct tSkinInfo +{ + int32 skinId; + char skinNameDisplayed[256]; + char skinNameOriginal[256]; + char date[256]; + tSkinInfo *nextSkin; +}; + +struct BottomBarOption +{ + char name[8]; + int32 screenId; +}; + +#ifndef CUSTOM_FRONTEND_OPTIONS +struct CMenuScreen +{ + char m_ScreenName[8]; + int32 m_PreviousPage; // eMenuScreen + int32 m_ParentEntry; // row + + struct CMenuEntry + { + int32 m_Action; // eMenuAction + char m_EntryName[8]; + int32 m_SaveSlot; // eSaveSlot + int32 m_TargetMenu; // eMenuScreen + uint16 m_X; + uint16 m_Y; + uint8 m_Align; + } m_aEntries[NUM_MENUROWS]; +}; +extern CMenuScreen aScreens[MENUPAGES]; +#else +#include "frontendoption.h" +struct CCustomScreenLayout { + int startX; // not used at all if first entry has X and Y values + int startY; // not used at all if first entry has X and Y values + int lineHeight; // used to determine next entry's Y coordinate, if it has 0-0 as coordinates + bool showLeftRightHelper; + bool noInvasiveBorders; // not needed on pages already handled by game + int xMargin; // useful for two part texts - 0/empty = MENU_X_MARGIN +}; + +struct CCFO +{ + void *value; + const char *saveCat; + const char *save; +}; + +struct CCFOSelect : CCFO +{ + char** rightTexts; + int8 numRightTexts; + bool onlyApplyOnEnter; + int8 displayedValue; // only if onlyApplyOnEnter enabled for now + int8 lastSavedValue; // only if onlyApplyOnEnter enabled + ChangeFunc changeFunc; + bool disableIfGameLoaded; + + CCFOSelect() {}; + CCFOSelect(int8* value, const char* saveCat, const char* save, const char** rightTexts, int8 numRightTexts, bool onlyApplyOnEnter, ChangeFunc changeFunc = nil, bool disableIfGameLoaded = false){ + this->value = value; + if (value) + this->lastSavedValue = this->displayedValue = *value; + + this->saveCat = saveCat; + this->save = save; + this->rightTexts = (char**)rightTexts; + this->numRightTexts = numRightTexts; + this->onlyApplyOnEnter = onlyApplyOnEnter; + this->changeFunc = changeFunc; + this->disableIfGameLoaded = disableIfGameLoaded; + } +}; + +// Value is float in here +struct CCFOSlider : CCFO +{ + ChangeFuncFloat changeFunc; + float min; + float max; + + CCFOSlider() {}; + CCFOSlider(float* value, const char* saveCat, const char* save, float min, float max, ChangeFuncFloat changeFunc = nil){ + this->value = value; + this->saveCat = saveCat; + this->save = save; + this->changeFunc = changeFunc; + this->min = min; + this->max = max; + } +}; + +struct CCFODynamic : CCFO +{ + DrawFunc drawFunc; + ButtonPressFunc buttonPressFunc; + + CCFODynamic() {}; + CCFODynamic(int8* value, const char* saveCat, const char* save, DrawFunc drawFunc, ButtonPressFunc buttonPressFunc){ + this->value = value; + this->saveCat = saveCat; + this->save = save; + this->drawFunc = drawFunc; + this->buttonPressFunc = buttonPressFunc; + } +}; + +struct CMenuScreenCustom +{ + char m_ScreenName[8]; + int32 m_PreviousPage; // eMenuScreen + CCustomScreenLayout *layout; + ReturnPrevPageFunc returnPrevPageFunc; + + struct CMenuEntry + { + int32 m_Action; // eMenuAction - below zero is CFO + char m_EntryName[8]; + struct { + union { + CCFO *m_CFO; // for initializing + CCFOSelect *m_CFOSelect; + CCFODynamic *m_CFODynamic; + CCFOSlider *m_CFOSlider; + }; + int32 m_SaveSlot; // eSaveSlot + int32 m_TargetMenu; // eMenuScreen + }; + uint16 m_X; + uint16 m_Y; + uint8 m_Align; + } m_aEntries[NUM_MENUROWS]; +}; +extern CMenuScreenCustom aScreens[MENUPAGES]; +#endif + +struct MenuTrapezoid +{ + float topLeft_x; + float topLeft_y; + float topRight_x; + float topRight_y; + float bottomLeft_x; + float bottomLeft_y; + float bottomRight_x; + float bottomRight_y; + float old_topRight_x; + float old_topRight_y; + float old_topLeft_x; + float old_topLeft_y; + float old_bottomLeft_x; + float old_bottomLeft_y; + float old_bottomRight_x; + float old_bottomRight_y; + float mult_topRight_x; + float mult_topRight_y; + float mult_topLeft_x; + float mult_topLeft_y; + float mult_bottomLeft_x; + float mult_bottomLeft_y; + float mult_bottomRight_x; + float mult_bottomRight_y; + + MenuTrapezoid(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { + topLeft_x = x1; + topLeft_y = y1; + topRight_x = x2; + topRight_y = y2; + bottomLeft_x = x3; + bottomLeft_y = y3; + bottomRight_x = x4; + bottomRight_y = y4; + }; + + void SaveCurrentCoors() { + old_topLeft_x = topLeft_x; + old_topLeft_y = topLeft_y; + old_topRight_x = topRight_x; + old_topRight_y = topRight_y; + old_bottomLeft_x = bottomLeft_x; + old_bottomLeft_y = bottomLeft_y; + old_bottomRight_x = bottomRight_x; + old_bottomRight_y = bottomRight_y; + } + + void Translate(int delta) { + bottomRight_x = delta * mult_bottomRight_x + old_bottomRight_x; + bottomRight_y = delta * mult_bottomRight_y + old_bottomRight_y; + bottomLeft_x = delta * mult_bottomLeft_x + old_bottomLeft_x; + bottomLeft_y = delta * mult_bottomLeft_y + old_bottomLeft_y; + topRight_x = delta * mult_topRight_x + old_topRight_x; + topRight_y = delta * mult_topRight_y + old_topRight_y; + topLeft_x = delta * mult_topLeft_x + old_topLeft_x; + topLeft_y = delta * mult_topLeft_y + old_topLeft_y; + } + + void UpdateMultipliers() { + mult_bottomRight_x = (bottomRight_x - old_bottomRight_x) / 255.0f; + mult_bottomRight_y = (bottomRight_y - old_bottomRight_y) / 255.0f; + mult_bottomLeft_x = (bottomLeft_x - old_bottomLeft_x) / 255.0f; + mult_bottomLeft_y = (bottomLeft_y - old_bottomLeft_y) / 255.0f; + mult_topRight_x = (topRight_x - old_topRight_x) / 255.0f; + mult_topRight_y = (topRight_y - old_topRight_y) / 255.0f; + mult_topLeft_x = (topLeft_x - old_topLeft_x) / 255.0f; + mult_topLeft_y = (topLeft_y - old_topLeft_y) / 255.0f; + } +}; + +class CMenuManager +{ +public: + int8 m_StatsScrollDirection; + float m_StatsScrollSpeed; + uint8 field_8; + bool m_PrefsUseVibration; + bool m_PrefsShowHud; + int32 m_PrefsRadarMode; + bool m_DisplayControllerOnFoot; + bool m_bShutDownFrontEndRequested; + bool m_bStartUpFrontEndRequested; + int32 m_KeyPressedCode; + int32 m_PrefsBrightness; + float m_PrefsLOD; + int8 m_PrefsShowSubtitles; + int8 m_PrefsShowLegends; + int8 m_PrefsUseWideScreen; + int8 m_PrefsVsync; + int8 m_PrefsVsyncDisp; + int8 m_PrefsFrameLimiter; + int8 m_nPrefsAudio3DProviderIndex; + int8 m_PrefsSpeakers; + int8 m_PrefsDMA; + int8 m_PrefsSfxVolume; + int8 m_PrefsMusicVolume; + int8 m_PrefsRadioStation; + uint8 m_PrefsStereoMono; // unused except restore settings + int32 m_nCurrOption; + bool m_bQuitGameNoCD; + bool m_bMenuMapActive; + bool m_AllowNavigation; + uint8 field_37; + bool m_bMenuActive; + bool m_bWantToRestart; + bool m_bFirstTime; + bool m_bActivateSaveMenu; + bool m_bWantToLoad; + float m_fMapSize; + float m_fMapCenterX; + float m_fMapCenterY; + uint32 OS_Language; + int32 m_PrefsLanguage; + int32 field_54; + int8 m_bLanguageLoaded; + uint8 m_PrefsAllowNastyGame; + int8 m_PrefsMP3BoostVolume; + int8 m_ControlMethod; + int32 m_nPrefsVideoMode; + int32 m_nDisplayVideoMode; + int32 m_nMouseTempPosX; + int32 m_nMouseTempPosY; + bool m_bGameNotLoaded; + int8 m_lastWorking3DAudioProvider; + bool m_bFrontEnd_ReloadObrTxtGxt; + int32 *pEditString; + uint8 field_74[4]; + int32 *pControlEdit; + bool m_OnlySaveMenu; + int32 m_firstStartCounter; + CSprite2d m_aFrontEndSprites[NUM_MENU_SPRITES]; + bool m_bSpritesLoaded; + int32 m_LeftMostRadioX; + int32 m_ScrollRadioBy; + int32 m_nCurrScreen; + int32 m_nPrevScreen; + int32 m_nCurrSaveSlot; + uint32 m_LastScreenSwitch; + int32 m_nMenuFadeAlpha; + int32 m_nOptionHighlightTransitionBlend; + bool bMenuChangeOngoing; + int32 MouseButtonJustClicked; + int32 JoyButtonJustClicked; + bool DisplayComboButtonErrMsg; + bool m_NoEmptyBinding; + bool m_ShowEmptyBindingError; + int32 m_nHelperTextAlpha; + bool m_bPressedPgUpOnList; + bool m_bPressedPgDnOnList; + bool m_bPressedUpOnList; + bool m_bPressedDownOnList; + bool m_bPressedScrollButton; + uint8 field_129; + uint8 field_12A; + uint8 field_12B; + int32 m_nMousePosX; + int32 m_nMousePosY; + int32 m_nMouseOldPosX; + int32 m_nMouseOldPosY; + int32 m_nHoverOption; + bool m_bShowMouse; + int32 m_nOptionMouseHovering; + bool m_bStartWaitingForKeyBind; + bool m_bWaitingForNewKeyBind; + bool m_bKeyChangeNotProcessed; + int32 m_CurrCntrlAction; + uint8 field_150; + uint8 field_151; + uint8 field_152; + uint8 field_153; + int32 m_nSelectedContSetupColumn; + bool m_bKeyIsOK; + bool field_159; + uint8 m_nCurrExLayer; + char m_PrefsSkinFile[256]; + char m_aSkinName[256]; + uint8 field_35B; + int32 m_nHelperTextMsgId; + tSkinInfo m_pSkinListHead; + tSkinInfo *m_pSelectedSkin; + int32 m_nFirstVisibleRowOnList; + float m_nScrollbarTopMargin; + int32 m_nTotalListRow; + int32 m_nSkinsTotal; + uint8 field_67C[4]; + int32 m_nSelectedListRow; + bool m_bSkinsEnumerated; + +#ifdef IMPROVED_VIDEOMODE + int32 m_nPrefsWidth; + int32 m_nPrefsHeight; + int32 m_nPrefsDepth; + int32 m_nPrefsWindowed; + int32 m_nPrefsSubsystem; + int32 m_nSelectedScreenMode; +#endif +#ifdef MULTISAMPLING + int8 m_nPrefsMSAALevel; + int8 m_nDisplayMSAALevel; +#endif + +#ifdef MISSION_REPLAY + bool m_bAttemptingMissionRetry; +#endif + +#ifdef GAMEPAD_MENU + enum + { + CONTROLLER_DUALSHOCK2 = 0, + CONTROLLER_DUALSHOCK3, + CONTROLLER_DUALSHOCK4, + CONTROLLER_XBOX360, + CONTROLLER_XBOXONE, + CONTROLLER_NINTENDO_SWITCH, + }; + + int8 m_PrefsControllerType; +#endif + enum LANGUAGE + { + LANGUAGE_AMERICAN, + LANGUAGE_FRENCH, + LANGUAGE_GERMAN, + LANGUAGE_ITALIAN, + LANGUAGE_SPANISH, +#ifdef MORE_LANGUAGES + LANGUAGE_POLISH, + LANGUAGE_RUSSIAN, + LANGUAGE_JAPANESE, +#endif + }; + bool GetIsMenuActive() {return !!m_bMenuActive;} + +#ifdef CUTSCENE_BORDERS_SWITCH + static bool m_PrefsCutsceneBorders; +#endif + +#ifndef MASTER + static bool m_PrefsMarketing; + static bool m_PrefsDisableTutorials; +#endif // !MASTER + + CMenuManager(void); + ~CMenuManager(void) { UnloadTextures(); } + +#ifdef NO_ISLAND_LOADING + enum + { + ISLAND_LOADING_LOW = 0, + ISLAND_LOADING_MEDIUM, + ISLAND_LOADING_HIGH + }; + + int8 m_PrefsIslandLoading; + + #define ISLAND_LOADING_IS(p) if (FrontEndMenuManager.m_PrefsIslandLoading == CMenuManager::ISLAND_LOADING_##p) + #define ISLAND_LOADING_ISNT(p) if (FrontEndMenuManager.m_PrefsIslandLoading != CMenuManager::ISLAND_LOADING_##p) +#else + #define ISLAND_LOADING_IS(p) + #define ISLAND_LOADING_ISNT(p) +#endif + +#ifdef XBOX_MESSAGE_SCREEN + static uint32 m_nDialogHideTimer; + static uint32 m_nDialogHideTimerPauseMode; + static bool m_bDialogOpen; + static wchar *m_pDialogText; + static bool m_bSaveWasSuccessful; + + static void SetDialogText(const char*); + static bool DialogTextCmp(const char*); + static void ToggleDialog(bool); + static void SetDialogTimer(uint32); + void ProcessDialogTimer(void); + void DrawOverlays(void); + void CloseDialog(void); +#endif + + void Initialise(); + void PrintMap(); + void SetFrontEndRenderStates(); + static void CentreMousePointer(); + void CheckCodesForControls(int); + bool CheckHover(int x1, int x2, int y1, int y2); + void CheckSliderMovement(int); + void DisplayHelperText(char*); + int DisplaySlider(float, float, float, float, float, float, float); + void DoSettingsBeforeStartingAGame(); + void DrawStandardMenus(bool); + void DrawControllerBound(int32, int32, int32, int8); + void DrawControllerScreenExtraText(int, int, int); + void DrawControllerSetupScreen(); + void DrawQuitGameScreen(); + void DrawFrontEnd(); + void DrawBackground(bool transitionCall); + void DrawPlayerSetupScreen(bool); + int FadeIn(int alpha); + int GetStartOptionsCntrlConfigScreens(); + void InitialiseChangedLanguageSettings(); + void LoadAllTextures(); + void LoadSettings(); + void MessageScreen(const char *str, bool); + void SmallMessageScreen(const char *str); + void PrintBriefs(); + static void PrintErrorMessage(); + void PrintStats(); + void Process(); + void ProcessList(bool &optionSelected, bool &goBack); + void UserInput(); + void ProcessUserInput(uint8, uint8, uint8, uint8, int8); + void ChangeRadioStation(int8); + void ProcessFileActions(); + void ProcessOnOffMenuOptions(); + void RequestFrontEndShutDown(); + void RequestFrontEndStartUp(); + void ResetHelperText(); + void SaveSettings(); + void SetHelperText(int text); + float StretchX(float); + float StretchY(float); + void SwitchMenuOnAndOff(); + void UnloadTextures(); + void WaitForUserCD(); + int GetNumOptionsCntrlConfigScreens(); + void SwitchToNewScreen(int8); + void AdditionalOptionInput(bool &goBack); + void ExportStats(void); + void PrintRadioSelector(void); + + // New (not in function or inlined in the game) + void ThingsToDoBeforeLeavingPage(); + void ScrollUpListByOne(); + void ScrollDownListByOne(); + void PageUpList(bool); + void PageDownList(bool); + int8 GetPreviousPageOption(); + + // uint8 GetNumberOfMenuOptions(); +#ifdef GAMEPAD_MENU + void LoadController(int8 type); + void PrintController(void); +#endif +}; + +#ifndef IMPROVED_VIDEOMODE +VALIDATE_SIZE(CMenuManager, 0x688); +#endif + +extern CMenuManager FrontEndMenuManager; + +#endif diff --git a/src/miami/core/FrontendTriggers.h b/src/miami/core/FrontendTriggers.h new file mode 100644 index 00000000..44bae54f --- /dev/null +++ b/src/miami/core/FrontendTriggers.h @@ -0,0 +1,1393 @@ +CTriggerCaller MemCardAccessTriggerCaller; + +void InitialiseTextsInMenuControllerInCar(CMenuPictureAndText *widget, CMenuManager::CONTRCONFIG cont); +void InitialiseTextsInMenuControllerOnFoot(CMenuPictureAndText *widget, CMenuManager::CONTRCONFIG cont); +void TriggerSave_BackToMainMenu(CMenuMultiChoiceTriggered *widget); +void TriggerSave_BackToMainMenuTwoLines(CMenuMultiChoiceTwoLinesTriggered *widget); +void TriggerSave_LoadGameLoadGameSelect(CMenuMultiChoiceTwoLinesTriggered *widget); +void TriggerSave_DeleteGameDeleteGameSelect(CMenuMultiChoiceTwoLinesTriggered *widget); +void TriggerSaveZone_BackToMainMenuTwoLines(CMenuMultiChoiceTwoLinesTriggered *widget); +void TriggerSaveZone_BackToMainMenuTwoLines(CMenuMultiChoiceTwoLinesTriggered *widget); +void TriggerSaveZone_SaveSlots(CMenuMultiChoiceTwoLinesTriggered *widget); + +void +DisplayWarningControllerMsg() +{ + if ( CPad::bDisplayNoControllerMessage ) + { + CSprite2d::DrawRect(CRect(X(20.0f), Y(140.0f), X(620.0f), Y(328.0)), CRGBA(64, 16, 16, 224)); // CRect(20.0f, 160.0f, 620.0f, 374.857117f) + + CFont::SetFontStyle(FONT_BANK); + CFont::SetBackgroundOff(); + CFont::SetScale(X(0.84f), Y(1.26f)); // 1.440000 + CFont::SetPropOn(); + CFont::SetCentreOff(); + CFont::SetJustifyOn(); + CFont::SetRightJustifyOff(); + CFont::SetBackGroundOnlyTextOn(); + CFont::SetWrapx(SCRW-X(40.0f)); // 600.0f + + CPlaceableShText text; + text.SetPosition(X(60.0f), Y(180.0f), false); // 205.714294 + text.SetColor(CRGBA(152, 152, 152, 255)); + text.m_text = TheText.Get("NOCONTE"); // Please re-insert the analog controller (DUALSHOCK@) or analog controller (DUALSHOCK@2) in controller port 1 to continue + text.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR*2.0f); + text.SetAlpha(255); + text.DrawShWrap(0.0f, 0.0f, X(600.0f+SHADOW_VECTOR.x), YF(600.0f)); //TODO check + + CFont::DrawFonts(); + } + else if ( CPad::bObsoleteControllerMessage ) + { + CSprite2d::DrawRect(CRect(X(20.0f), Y(140.0f), X(620.0f), Y(328.0)), CRGBA(64, 16, 16, 224)); // CRect(20.0f, 160.0f, 620.0f, 374.857117f) + CFont::SetFontStyle(FONT_BANK); + CFont::SetBackgroundOff(); + CFont::SetScale(X(0.84f), Y(1.26f)); // 1.440000 + CFont::SetPropOn(); + CFont::SetCentreOff(); + CFont::SetJustifyOn(); + CFont::SetRightJustifyOff(); + CFont::SetBackGroundOnlyTextOn(); + CFont::SetWrapx(SCRW-X(40.0f)); // 600.0f + + CPlaceableShText text; + text.SetPosition(X(60.0f), Y(180.0f), false); // 205.714294 + text.SetColor(CRGBA(152, 152, 152, 255)); + text.m_text = TheText.Get("WRCONTE"); // Please re-insert the analog controller (DUALSHOCK@) or analog controller (DUALSHOCK@2) in controller port 1 to continue + text.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR*2.0f); + text.SetAlpha(255); + text.DrawShWrap(0.0f, 0.0f, X(600.0f+SHADOW_VECTOR.x), YF(600.0f)); //TODO check + + CFont::DrawFonts(); + } + +} + +void +TriggerMCSUM_Yes(CMenuMultiChoiceTriggered *widget) +{ + if ( widget ) + bMemoryCardStartUpMenus_ExitNow = true; +} + +int32 nStatLinesIndex; +wchar aStatLines[50+1][50]; +wchar *PrintStatLine(char const *text, void *stat, unsigned char itsFloat, void *stat2) +{ + if (text && stat && nStatLinesIndex < 50) + { + char line [64]; + wchar uline[64]; + + memset(line, 0, sizeof(line)); + memset(uline, 0, sizeof(uline)); + + if (stat2) + { + if ( itsFloat ) + sprintf(line, " %.2f %s %.2f", *(float*)stat, UnicodeToAscii(TheText.Get("FEST_OO")), *(float*)stat2); + else + sprintf(line, " %d %s %d", *(int32*)stat, UnicodeToAscii(TheText.Get("FEST_OO")), *(int32*)stat2); + } + else + { + if (itsFloat) + sprintf(line, " %.2f", *(float*)stat); + else + sprintf(line, " %d", *(int32*)stat); + } + + wchar *pStatLine = aStatLines[nStatLinesIndex++]; + + AsciiToUnicode(line, uline); + UnicodeStrcpy(pStatLine, uline); + + return pStatLine; + } + + return nil; +} + +void +DisplayMemoryCardAccessMsg(wchar *msg, CRGBA const &color) +{ + CSprite2d::DrawRect(CRect(X(70.0f), Y(100.0f), X(570.0f), Y(270.0f)), color); + + CFont::SetFontStyle(FONT_BANK); + CFont::SetBackgroundOff(); + CFont::SetScale(X(MEMCARD_ACCESS_MSG_SIZE_X), Y(MEMCARD_ACCESS_MSG_SIZE_Y)); + CFont::SetPropOn(); + CFont::SetJustifyOn(); + CFont::SetRightJustifyOff(); + CFont::SetBackGroundOnlyTextOn(); + CFont::SetWrapx(SCRW-X(90.0f)); // 550.0f + CFont::SetCentreOn(); + CFont::SetCentreSize(SCRW-X(180.0f)); // 460.0f + + CPlaceableShText text; + + text.SetPosition(X(320.0f), Y(120.0f), false); // 137.142868 + text.SetColor(CRGBA(200, 200, 200, 255)); + text.m_text = msg; + + text.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + text.SetAlpha(255); + text.Draw(0.0f, 0.0f); + + CFont::DrawFonts(); + DoRWStuffEndOfFrame(); +} + +void +FillMenuWithMemCardFileListing(CMenuMultiChoiceTwoLinesTriggered *widget, void (*cancelTrigger)(CMenuMultiChoiceTwoLinesTriggered *), void (*selectTrigger)(CMenuMultiChoiceTwoLinesTriggered *), wchar *text, int32 y, int32 height, int32 offset) +{ + if ( widget ) + { + int32 selected = 0; + if ( bMemoryCardSpecialZone ) + selected = widget->m_cursor != -1 ? widget->m_cursor : 0; + + widget->DeactivateMenu(); // TODO check + widget->m_numOptions = 0; + widget->AddTitle(nil, 0.0f, 0.0f, 0); + + TheMemoryCard.PopulateSlotInfo(CARD_ONE); + + if ( TheMemoryCard.GetError() == CMemoryCard::NO_ERR_SUCCESS) + { + widget->AddOption(TheText.Get("FES_CAN"), 0.0f, YF(y), cancelTrigger, 0, 0); + + FrontEndMenuManager.field_3C = 0; + + y += offset; + + char buff[100]; + + for ( int32 i = 0; i < CMemoryCard::MAX_SLOTS; i++ ) + { + // SAVE FILE + sprintf(buff, "%s %d ", UnicodeToAscii(TheText.Get("FES_SLO")), i+1); + AsciiToUnicode(buff, MemoryCard_FileNames[i]); + + wchar *datetime = nil; + + switch ( TheMemoryCard.GetInfoOnSpecificSlot(i) ) + { + case CMemoryCard::SLOT_CORRUPTED: + { + UnicodeStrcat(MemoryCard_FileNames[i], TheText.Get("FES_ISC")); // IS CORRUPTED + datetime = TheMemoryCard.GetDateAndTimeOfSavedGame(i); + break; + } + case CMemoryCard::SLOT_PRESENT: + { + if ( TheMemoryCard.GetNameOfSavedGame(i) != nil ) + { + UnicodeStrcpy(MemoryCard_FileNames[i], TheMemoryCard.GetNameOfSavedGame(i)); + datetime = TheMemoryCard.GetDateAndTimeOfSavedGame(i); + } + else + { + UnicodeStrcpy(MemoryCard_FileNames[i], TheText.Get("FES_SAG")); // PRESENT + datetime = TheMemoryCard.GetDateAndTimeOfSavedGame(i); + } + break; + } + case CMemoryCard::SLOT_NOTPRESENT: + { + UnicodeStrcat(MemoryCard_FileNames[i], TheText.Get("FES_ISF")); + datetime = TheMemoryCard.GetDateAndTimeOfSavedGame(i); + break; + } + } + + widget->AddOption(MemoryCard_FileNames[i], 0.0f, YF(y), datetime, 0.0f, YF(float(y)+(0.44f*height)), selectTrigger, 0, 0); + y += height; + } + } + else + { + if ( !gErrorSampleTriggered ) + { + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_FAIL, 0); + gErrorSampleTriggered = true; + } + + // Cancel + widget->AddOption(TheText.Get("FES_CAN"), 0.0f, YF(y+(height*2)), cancelTrigger, 0, 0); + + FrontEndMenuManager.field_3C = 1; + + y += height; + + TheMemoryCard.PopulateErrorMessage(); + + // Error Reading Memory Card (PS2) in MEMORY CARD slot 1 please check and try again. + if ( TheMemoryCard.GetErrorMessage()) + widget->AddTitle(TheMemoryCard.GetErrorMessage(), 0.0f, YF(y), 0); + else + widget->AddTitle(TheText.Get("FES_GME"), 0.0f, YF(y), 0); + } + + widget->SetMenuSelection(0); + widget->ActivateMenu(1); + + if ( bMemoryCardSpecialZone ) + { + widget->GoFirst(); + + for ( int32 i = 0; i < selected; i++ ) + widget->GoNext(); + } + } +} + +void +TriggerSaveZone_FormatFailedOK(CMenuMultiChoiceTriggered *widget) +{ + if ( widget ) + pActiveMenuPage = &MenuPageSaveZone_SaveGame; +} + +void +TriggerSaveZone_BackToMainMenu(CMenuMultiChoiceTriggered *widget) +{ + bMemoryCardSpecialZone = false; + bIgnoreTriangleButton = false; + pActiveMenuPage = &MenuPageSaveZone_SaveGame; +} + +void +TriggerSaveZone_QuitMenu(CMenuMultiChoiceTriggered *widget) +{ + if ( widget ) + { + FrontEndMenuManager.m_bMenuActive = false; + FrontEndMenuManager.m_bInSaveZone = false; + CTimer::EndUserPause(); + } +} + +void +TriggerSaveZone_FormatCard(CMenuMultiChoiceTriggered *widget) +{ + if ( widget ) + { + FillMenuWithMemCardFileListing(&MenuSaveZoneSSL_1, TriggerSaveZone_BackToMainMenuTwoLines, TriggerSaveZone_SaveSlots, nil, 0, 34, 22); + + if ( TheMemoryCard.GetError() == CMemoryCard::NO_ERR_SUCCESS) + { + MenuSaveZoneMSG_1.m_numTexts = 0; + MenuSaveZoneMSG_1.AddText(TheText.Get("FES_AFO"), X(-80.0f), 0.0f, TEXT_COLOR, 0); + + MenuSaveZoneMSG_2.m_numOptions = 0; + MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), YF(5.0f), TriggerSaveZone_BackToMainMenu, 0, 0); + + MenuPageSaveZone_Message.ActivatePage(); + pActiveMenuPage = &MenuPageSaveZone_Message; + } + else if ( TheMemoryCard.GetError() != CMemoryCard::ERR_NOFORMAT) + { + MenuSaveZoneMSG_1.m_numTexts = 0; + MenuSaveZoneMSG_1.AddText(TheMemoryCard.GetErrorMessage(), X(-80.0f), 0.0f, TEXT_COLOR, 0); + + MenuSaveZoneMSG_2.m_numOptions = 0; + MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), YF(15.0f), TriggerSaveZone_BackToMainMenu, 0, 0); + + MenuPageSaveZone_Message.ActivatePage(); + pActiveMenuPage = &MenuPageSaveZone_Message; + } + else + { + if ( !MemCardAccessTriggerCaller.CanCall() ) + MemCardAccessTriggerCaller.SetTrigger(TriggerSaveZone_FormatCard, widget); + else + { + // Formatting Memory Card (PS2) in MEMORY CARD slot 1. Please do not remove the Memory Card (PS2), reset or switch off the console. + DisplayMemoryCardAccessMsg(TheText.Get("FEFD_WR"), CRGBA(200, 50, 50, 192)); + TheMemoryCard.FormatCard(CARD_ONE); + + if ( TheMemoryCard.GetError() == CMemoryCard::NO_ERR_SUCCESS ) + pActiveMenuPage = &MenuPageSaveZone_SaveGame; + else + { + TheMemoryCard.PopulateErrorMessage(); + + wchar *error = TheText.Get("FESZ_FF"); // Format Failed! Check Memory Card (PS2) in MEMORY CARD slot 1 and please try again. + + // missing switch + + if ( !error ) error = TheText.Get("FES_GME"); // Error Reading Memory Card (PS2) in MEMORY CARD slot 1 please check and try again. + + MenuSaveZoneMSG_1.m_numTexts = 0; + MenuSaveZoneMSG_1.AddText(error, X(-80.0f), 0.0f, TEXT_COLOR, 0); + + MenuSaveZoneMSG_2.m_numOptions = 0; + MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), YF(20.0f), TriggerSaveZone_BackToMainMenu, 0, 0); + + MenuPageSaveZone_Message.ActivatePage(); + pActiveMenuPage = &MenuPageSaveZone_Message; + } + + if ( TheMemoryCard.GetError() == CMemoryCard::NO_ERR_SUCCESS ) + { + FillMenuWithMemCardFileListing(&MenuSaveZoneSSL_1, TriggerSaveZone_BackToMainMenuTwoLines, TriggerSaveZone_SaveSlots, nil, 0, 34, 22); + pActiveMenuPage = &MenuPageSaveZone_SaveSlots; + bMemoryCardSpecialZone = true; + bIgnoreTriangleButton = true; + pActiveMenuPage->ActivatePage(); + } + else + { + TheMemoryCard.PopulateErrorMessage(); + + // Format Failed! Check Memory Card (PS2) in MEMORY CARD slot 1 and please try again. + wchar *error = TheText.Get("FESZ_FF"); + + switch ( TheMemoryCard.GetError() ) + { + case CMemoryCard::ERR_WRITEFULLDEVICE: + case CMemoryCard::ERR_DIRFULLDEVICE: + case CMemoryCard::ERR_SAVEFAILED: + { + error = TheMemoryCard.GetErrorMessage(); + break; + } + } + + // Error Reading Memory Card (PS2) in MEMORY CARD slot 1 please check and try again. + if ( !error ) error = TheText.Get("FES_GME"); + + MenuSaveZoneMSG_1.m_numTexts = 0; + MenuSaveZoneMSG_1.AddText(error, X(-80.0f), 0.0f, TEXT_COLOR, 0); + + MenuSaveZoneMSG_2.m_numOptions = 0; + MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), YF(20.0f), TriggerSaveZone_BackToMainMenu, 0, 0); + + MenuPageSaveZone_Message.ActivatePage(); + pActiveMenuPage = &MenuPageSaveZone_Message; + } + } + } + } +} + +void +TriggerSaveZone_FormatCardSelect(CMenuMultiChoiceTriggered *widget) +{ + if ( widget ) + { + FillMenuWithMemCardFileListing(&MenuSaveZoneSSL_1, TriggerSaveZone_BackToMainMenuTwoLines, TriggerSaveZone_SaveSlots, nil, 0, 34, 22); + + if ( TheMemoryCard.GetError() == CMemoryCard::NO_ERR_SUCCESS ) + { + // This Memory Card (PS2) is already formatted. + MenuSaveZoneMSG_1.m_numTexts = 0; + MenuSaveZoneMSG_1.AddText(TheText.Get("FES_AFO"), X(-80.0f), 0.0f, TEXT_COLOR, 0); + + MenuSaveZoneMSG_2.m_numOptions = 0; + MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), YF(5.0f), TriggerSaveZone_BackToMainMenu, 0, 0); + + MenuPageSaveZone_Message.ActivatePage(); + pActiveMenuPage = &MenuPageSaveZone_Message; + } + else if ( TheMemoryCard.GetError() != CMemoryCard::ERR_NOFORMAT ) + { + MenuSaveZoneMSG_1.m_numTexts = 0; + MenuSaveZoneMSG_1.AddText(TheMemoryCard.GetErrorMessage(), X(-80.0f), 0.0f, TEXT_COLOR, 0); + + MenuSaveZoneMSG_2.m_numOptions = 0; + MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), YF(15.0f), TriggerSaveZone_BackToMainMenu, 0, 0); + + MenuPageSaveZone_Message.ActivatePage(); + pActiveMenuPage = &MenuPageSaveZone_Message; + } + else + { + // Are you sure you wish to format the Memory Card (PS2) in MEMORY CARD slot 1? + MenuSaveZoneQYN_1.m_numTexts = 0; + MenuSaveZoneQYN_1.AddText(TheText.Get("FESZ_QF"), X(-40.0f), 0.0f, TEXT_COLOR, 0); + + MenuSaveZoneQYN_2.m_numOptions = 0; + MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_NO"), X(80.0f), YF(20.0f), TriggerSaveZone_BackToMainMenu, 0, 0); + MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_YES"), X(80.0f), 0.0f, TriggerSaveZone_FormatCard, 0, 0); + + MenuPageSaveZone_QuestionYesNo.ActivatePage(); + pActiveMenuPage = &MenuPageSaveZone_QuestionYesNo; + } + } +} + +void +TriggerSaveZone_DeleteSaveGame(CMenuMultiChoiceTriggered *widget) +{ + if ( widget ) + { + bMemoryCardSpecialZone = false; + bIgnoreTriangleButton = false; + + if ( !MemCardAccessTriggerCaller.CanCall() ) + MemCardAccessTriggerCaller.SetTrigger(TriggerSaveZone_DeleteSaveGame, widget); + else + { + // Overwriting data. Please do not remove the Memory Card (PS2) in MEMORY CARD slot 1, reset or switch off the console. + DisplayMemoryCardAccessMsg(TheText.Get("FESZ_OW"), CRGBA(200, 50, 50, 192)); + + TheMemoryCard.DeleteSlot(MemoryCardSlotSelected); + + if ( TheMemoryCard.GetError() != CMemoryCard::NO_ERR_SUCCESS ) + { + TheMemoryCard.PopulateErrorMessage(); + + wchar *error = TheText.Get("FES_DEE"); // Deleting Failed! Check Memory Card (PS2) in MEMORY CARD slot 1 and please try again. + + // switch missing + + if ( !error ) error = TheText.Get("FES_GME"); // Error Reading Memory Card (PS2) in MEMORY CARD slot 1 please check and try again. + + MenuSaveZoneMSG_1.m_numTexts = 0; + MenuSaveZoneMSG_1.AddText(error, X(-80.0f), 0.0f, TEXT_COLOR, 0); + + MenuSaveZoneMSG_2.m_numOptions = 0; + MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), YF(20.0f), TriggerSaveZone_BackToMainMenu, 0, 0); + + MenuPageSaveZone_Message.ActivatePage(); + pActiveMenuPage = &MenuPageSaveZone_Message; + } + else + { + TheMemoryCard.SaveSlot(MemoryCardSlotSelected); + + if ( TheMemoryCard.GetError() == CMemoryCard::NO_ERR_SUCCESS ) + { + // Game saved successfully! + MenuSaveZoneMSG_1.m_numTexts = 0; + MenuSaveZoneMSG_1.AddText(TheText.Get("FESZ_L1"), X(-20.0f), YF(10.0f), TEXT_COLOR, 0); + + MenuSaveZoneMSG_2.m_numOptions = 0; + MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(110.0f), 0.0f, TriggerSaveZone_QuitMenu, 0, 0); + + MenuPageSaveZone_Message.ActivatePage(); + pActiveMenuPage = &MenuPageSaveZone_Message; + } + else + { + TheMemoryCard.PopulateErrorMessage(); + + wchar *error = TheText.Get("FESZ_SR"); // Save Failed! Check Memory Card (PS2) in MEMORY CARD slot 1 and please try again. + + switch ( TheMemoryCard.GetError() ) + { + case CMemoryCard::ERR_WRITEFULLDEVICE: + case CMemoryCard::ERR_DIRFULLDEVICE: + case CMemoryCard::ERR_SAVEFAILED: + { + error = TheMemoryCard.GetErrorMessage(); + break; + } + } + + if ( !error ) error = TheText.Get("FES_GME"); // Error Reading Memory Card (PS2) in MEMORY CARD slot 1 please check and try again. + + MenuSaveZoneMSG_1.m_numTexts = 0; + MenuSaveZoneMSG_1.AddText(error, X(-80.0f), 0.0f, TEXT_COLOR, 0); + + MenuSaveZoneMSG_2.m_numOptions = 0; + MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(120.0f), YF(30.0f), TriggerSaveZone_BackToMainMenu, 0, 0); + + MenuPageSaveZone_Message.ActivatePage(); + pActiveMenuPage = &MenuPageSaveZone_Message; + } + } + } + } +} + +void +TriggerSaveZone_SaveGame(CMenuMultiChoiceTriggered *widget) +{ + if ( widget ) + { + bMemoryCardSpecialZone = false; + bIgnoreTriangleButton = false; + if ( !MemCardAccessTriggerCaller.CanCall() ) + MemCardAccessTriggerCaller.SetTrigger(TriggerSaveZone_SaveGame, widget); + else + { + DisplayMemoryCardAccessMsg(TheText.Get("FESZ_WR"), CRGBA(200, 50, 50, 192)); + + TheMemoryCard.SaveSlot(MemoryCardSlotSelected); + + if ( TheMemoryCard.GetError() == CMemoryCard::NO_ERR_SUCCESS ) + { + // Game saved successfully! + MenuSaveZoneMSG_1.m_numTexts = 0; + MenuSaveZoneMSG_1.AddText(TheText.Get("FESZ_L1"), X(-20.0f), YF(10.0f), TEXT_COLOR, 0); + + MenuSaveZoneMSG_2.m_numOptions = 0; + MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(110.0f), 0.0f, TriggerSaveZone_QuitMenu, 0, 0); + + MenuPageSaveZone_Message.ActivatePage(); + pActiveMenuPage = &MenuPageSaveZone_Message; + } + else + { + TheMemoryCard.PopulateErrorMessage(); + + wchar *error = TheText.Get("FESZ_SR"); // Save Failed! Check Memory Card (PS2) in MEMORY CARD slot 1 and please try again. + + switch ( TheMemoryCard.GetError() ) + { + case CMemoryCard::ERR_WRITEFULLDEVICE: + case CMemoryCard::ERR_DIRFULLDEVICE: + case CMemoryCard::ERR_SAVEFAILED: + { + error = TheMemoryCard.GetErrorMessage(); + break; + } + } + + if ( !error ) error = TheText.Get("FES_GME"); // Error Reading Memory Card (PS2) in MEMORY CARD slot 1 please check and try again. + + MenuSaveZoneMSG_1.m_numTexts = 0; + MenuSaveZoneMSG_1.AddText(error, X(-80.0f), 0.0f, TEXT_COLOR, 0); + + MenuSaveZoneMSG_2.m_numOptions = 0; + MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(120.0f), YF(30.0f), TriggerSaveZone_BackToMainMenu, 0, 0); + + MenuPageSaveZone_Message.ActivatePage(); + pActiveMenuPage = &MenuPageSaveZone_Message; + } + } + } +} + +void +TriggerSaveZone_SaveSlots(CMenuMultiChoiceTwoLinesTriggered *widget) +{ + if ( widget ) + { + if ( widget->GetMenuSelection() > 0 ) + { + MemoryCardSlotSelected = widget->GetMenuSelection() - 1; + + switch ( TheMemoryCard.GetInfoOnSpecificSlot(MemoryCardSlotSelected) ) + { + case CMemoryCard::SLOT_PRESENT: + case CMemoryCard::SLOT_CORRUPTED: + { + // Proceed with overwriting this saved game? + MenuSaveZoneQYN_1.m_numTexts = 0; + MenuSaveZoneQYN_1.AddText(TheText.Get("FESZ_QO"), X(-40.0f), 0.0f, TEXT_COLOR, 0); + + MenuSaveZoneQYN_2.m_numOptions = 0; + MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_NO"), X(80.0f), YF(20.0f), TriggerSaveZone_BackToMainMenu, 0, 0); + MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_YES"), X(80.0f), 0.0f, TriggerSaveZone_DeleteSaveGame, 0, 0); + + MenuPageSaveZone_QuestionYesNo.ActivatePage(); + bMemoryCardSpecialZone = false; + pActiveMenuPage = &MenuPageSaveZone_QuestionYesNo; + break; + } + + case CMemoryCard::SLOT_NOTPRESENT: + { + // PROCEED WITH SAVE ? + MenuSaveZoneQYN_1.m_numTexts = 0; + MenuSaveZoneQYN_1.AddText(TheText.Get("FESZ_QS"), X(-40.0f), 0.0f, TEXT_COLOR, 0); + + MenuSaveZoneQYN_2.m_numOptions = 0; + MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_NO"), X(80.0f), YF(20.0f), TriggerSaveZone_BackToMainMenu, 0, 0); + MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_YES"), X(80.0f), 0.0f, TriggerSaveZone_SaveGame, 0, 0); + + MenuPageSaveZone_QuestionYesNo.ActivatePage(); + bMemoryCardSpecialZone = false; + pActiveMenuPage = &MenuPageSaveZone_QuestionYesNo; + break; + } + } + } + } +} + +void +TriggerSaveZone_SaveGameSelect(CMenuMultiChoiceTriggered *widget) +{ + if ( widget ) + { + FillMenuWithMemCardFileListing(&MenuSaveZoneSSL_1, TriggerSaveZone_BackToMainMenuTwoLines, TriggerSaveZone_SaveSlots, nil, 0, 34, 22); + + if ( TheMemoryCard.GetError() == CMemoryCard::ERR_NOFORMAT) + { + gErrorSampleTriggered = false; + pActiveMenuPage = &MenuPageSaveZone_FormatCard; + } + else + { + bMemoryCardSpecialZone = true; + bIgnoreTriangleButton = true; + pActiveMenuPage = &MenuPageSaveZone_SaveSlots; + } + + pActiveMenuPage->ActivatePage(); + } +} + +void +TriggerControls_Vibrations(CMenuOnOffTriggered *widget) +{ + if ( widget ) + { + CMenuManager::m_PrefsUseVibration = widget->GetMenuSelection(); + if ( CMenuManager::m_PrefsUseVibration ) + { + CPad::GetPad(0)->StartShake(300, 150); + TimeToStopPadShaking = CTimer::GetTimeInMillisecondsPauseMode() + 500; + } + } +} + +void +TriggerControls_ContrDisplay(CMenuMultiChoiceTriggeredAlways *widget) +{ + if ( widget ) + { + int32 conf = MenuControls_1.GetMenuSelection(); + int32 i = MenuControls_2.GetMenuSelection(); + if ( i == 1 ) + { + if ( conf == CMenuManager::CONFIG_2 ) + MenuPage_Controls.m_controls[0] = &MenuControls_7; + else + MenuPage_Controls.m_controls[0] = &MenuControls_4; + } + else if ( i == 0 ) + { + if ( conf == CMenuManager::CONFIG_2 ) + MenuPage_Controls.m_controls[0] = &MenuControls_6; + else + MenuPage_Controls.m_controls[0] = &MenuControls_3; + } + } +} + +void +TriggerControls_DrawHNContrConfig(CMenuMultiChoiceTriggeredAlways *widget) +{ + if ( widget ) + { + int32 conf = widget->GetMenuSelection(); + + InitialiseTextsInMenuControllerOnFoot(&MenuControls_3, (CMenuManager::CONTRCONFIG)conf); + InitialiseTextsInMenuControllerInCar (&MenuControls_4, (CMenuManager::CONTRCONFIG)conf); + + int32 i = MenuControls_2.GetMenuSelection(); + if ( i == 1 ) + { + if ( conf == CMenuManager::CONFIG_2 ) + MenuPage_Controls.m_controls[0] = &MenuControls_7; + else + MenuPage_Controls.m_controls[0] = &MenuControls_4; + } + else if ( i == 0 ) + { + if ( conf == CMenuManager::CONFIG_2 ) + MenuPage_Controls.m_controls[0] = &MenuControls_6; + else + MenuPage_Controls.m_controls[0] = &MenuControls_3; + } + } +} + +void +TriggerControls_DrawContrConfig(CMenuMultiChoiceTriggeredAlways *widget) +{ + if ( widget ) + { + int32 conf = widget->GetMenuSelection(); + if ( widget->m_cursor != -1 ) + conf = widget->m_cursor; + + InitialiseTextsInMenuControllerOnFoot(&MenuControls_3, (CMenuManager::CONTRCONFIG)conf); + InitialiseTextsInMenuControllerInCar(&MenuControls_4, (CMenuManager::CONTRCONFIG)conf); + + int32 i = MenuControls_2.GetMenuSelection(); + if ( i == 1 ) + { + if ( conf == CMenuManager::CONFIG_2 ) + MenuPage_Controls.m_controls[0] = &MenuControls_7; + else + MenuPage_Controls.m_controls[0] = &MenuControls_4; + } + else if ( i == 0 ) + { + if ( conf == CMenuManager::CONFIG_2 ) + MenuPage_Controls.m_controls[0] = &MenuControls_6; + else + MenuPage_Controls.m_controls[0] = &MenuControls_3; + } + } +} + +void +TriggerControls_ContrConfig(CMenuMultiChoiceTriggered *widget) +{ + if ( widget ) + { + int32 conf = widget->GetMenuSelection(); + + InitialiseTextsInMenuControllerOnFoot(&MenuControls_3, (CMenuManager::CONTRCONFIG)conf); + InitialiseTextsInMenuControllerInCar(&MenuControls_4, (CMenuManager::CONTRCONFIG)conf); + + int32 i = MenuControls_2.GetMenuSelection(); + if ( i == 1 ) + { + if ( conf == CMenuManager::CONFIG_2 ) + MenuPage_Controls.m_controls[0] = &MenuControls_7; + else + MenuPage_Controls.m_controls[0] = &MenuControls_4; + } + else if ( i == 0 ) + { + if ( conf == CMenuManager::CONFIG_2 ) + MenuPage_Controls.m_controls[0] = &MenuControls_6; + else + MenuPage_Controls.m_controls[0] = &MenuControls_3; + } + } +} + +void +TriggerLanguage_Language(CMenuMultiChoiceTriggered *widget) +{ + if ( widget ) + { + if ( CMenuManager::m_PrefsLanguage != widget->GetMenuSelection() ) + { + CMenuManager::m_PrefsLanguage = widget->GetMenuSelection(); + FrontEndMenuManager.m_bInitialised = false; + bFrontEnd_ReloadObrTxtGxt = true; + } + } +} + +void +TriggerAudio_RadioStation(CMenuMultiChoicePicturedTriggered *widget) +{ + if ( widget ) + { + if ( CMenuManager::m_PrefsRadioStation != widget->GetMenuSelection() ) + { + CMenuManager::m_PrefsRadioStation = widget->GetMenuSelection(); + DMAudio.PlayFrontEndTrack(CMenuManager::m_PrefsRadioStation, TRUE); + DMAudio.SetRadioInCar(CMenuManager::m_PrefsRadioStation); + } + } +} + +void +TriggerAudio_StereoMono(CMenuMultiChoiceTriggered *widget) +{ + if ( widget ) + { + if (widget->GetMenuSelection() == 1) + { + DMAudio.SetMonoMode(TRUE); + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MONO, 0); + } + else + { + DMAudio.SetMonoMode(FALSE); + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_STEREO, 0); + } + } +} + +void +TriggerAudio_MusicVolumeAlways(CMenuSliderTriggered *widget) +{ + ; +} + +void +TriggerAudio_SfxVolumeAlways(CMenuSliderTriggered *widget) +{ + if ( widget ) + { + static bool bTriggerTest = false; + + CMenuManager::m_PrefsSfxVolume = float(widget->GetMenuSelection()) / 100.0f * 127.0f + 0.5f; + + if ( CMenuManager::m_PrefsSfxVolume == 102 && !CPad::GetPad(0)->GetDPadLeft()&& !CPad::GetPad(0)->GetDPadRight() ) + { + if ( bTriggerTest ) + { + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_AUDIO_TEST, 0); + bTriggerTest = false; + } + } + else + bTriggerTest = true; + + FrontEndMenuManager.SetSoundLevelsForMusicMenu(); + } +} + +void +TriggerAudio_MusicVolume(CMenuSliderTriggered *widget) +{ + if ( widget ) + { + CMenuManager::m_PrefsMusicVolume = float(widget->GetMenuSelection()) / 100.0f * 127.0f + 0.5f; + FrontEndMenuManager.SetSoundLevelsForMusicMenu(); + } +} + +void +TriggerAudio_SfxVolume(CMenuSliderTriggered *widget) +{ + ; +} + +void +TriggerSave_NewGameNewGame(CMenuMultiChoiceTriggered *widget) +{ + FrontEndMenuManager.m_bWantToRestart = true; + FrontEndMenuManager.m_bMenuActive = false; + FrontEndMenuManager.m_bInSaveZone = false; + bIgnoreTriangleButton = false; + + CTimer::EndUserPause(); + + FrontEndMenuManager.AnaliseMenuContents(); + + DMAudio.SetEffectsFadeVol(0); + DMAudio.SetMusicFadeVol(0); + DMAudio.ResetTimers(CTimer::GetTimeInMilliseconds()); +} + +void +TriggerSave_NewGameSelectYes(CMenuMultiChoiceTriggered *widget) +{ + // Are you sure you want to start a new game? All progress since the last save game will be lost. Proceed? + MenuSaveZoneQYN_1.m_numTexts = 0; + MenuSaveZoneQYN_1.AddText(TheText.Get("FESZ_QR"), X(-100.0f), 0.0f, TEXT_COLOR, 0); + + MenuSaveZoneQYN_2.m_numOptions = 0; + MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_NO"), X(80.0f), YF(30.0f), TriggerSave_BackToMainMenu, 0, 0); + MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_YES"), X(80.0f), YF(10.0f), TriggerSave_NewGameNewGame, 0, 0); + + MenuPageSaveZone_QuestionYesNo.ActivatePage(); + pMenuSave = &MenuPageSaveZone_QuestionYesNo; + bIgnoreTriangleButton = true; +} + +void +TriggerSave_DeleteGameDeleteGame(CMenuMultiChoiceTriggered *widget) +{ + if ( widget ) + { + bMemoryCardSpecialZone = false; + bIgnoreTriangleButton = false; + + if ( !MemCardAccessTriggerCaller.CanCall() ) + MemCardAccessTriggerCaller.SetTrigger(TriggerSave_DeleteGameDeleteGame, widget); + else + { + // Deleting data. Please do not remove the Memory Card (PS2) in MEMORY CARD slot 1, reset or switch off the console. + DisplayMemoryCardAccessMsg(TheText.Get("FEDL_WR"), CRGBA(200, 50, 50, 192)); + + TheMemoryCard.DeleteSlot(MemoryCardSlotSelected); + + if ( TheMemoryCard.GetError() != CMemoryCard::NO_ERR_SUCCESS) + { + // Deleting Failed! Check Memory Card (PS2) in MEMORY CARD slot 1 and please try again. + MenuSaveZoneMSG_1.m_numTexts = 0; + MenuSaveZoneMSG_1.AddText(TheText.Get("FES_DEE"), X(-80.0f), YF(20.0f), TEXT_COLOR, 0); + + MenuSaveZoneMSG_2.m_numOptions = 0; + MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), YF(15.0f), TriggerSave_BackToMainMenu, 0, 0); + + MenuPageSaveZone_Message.ActivatePage(); + pMenuSave = &MenuPageSaveZone_Message; + + bMemoryCardSpecialZone = false; + bIgnoreTriangleButton = true; + } + else + { + FillMenuWithMemCardFileListing(&MenuSaveLG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_LoadGameLoadGameSelect, nil, 0, 34, 22); + FillMenuWithMemCardFileListing(&MenuSaveDG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_DeleteGameDeleteGameSelect, nil, 0, 34, 22); + + pMenuSave = &MenuPage_SaveBasic; + pMenuSave->ActivatePage(); + } + } + } +} + +void +TriggerSave_DeleteGameDeleteGameSelect(CMenuMultiChoiceTwoLinesTriggered *widget) +{ + if ( widget ) + { + if ( widget->GetMenuSelection() > 0 ) + { + MemoryCardSlotSelected = widget->GetMenuSelection() - 1; + + switch ( TheMemoryCard.GetInfoOnSpecificSlot(MemoryCardSlotSelected) ) + { + case CMemoryCard::SLOT_NOTPRESENT: + { + break; + } + case CMemoryCard::SLOT_CORRUPTED: + case CMemoryCard::SLOT_PRESENT: + { + // Proceed with deleting this saved game? + MenuSaveZoneQYN_1.m_numTexts = 0; + MenuSaveZoneQYN_1.AddText(TheText.Get("FESZ_QD"), X(-40.0f), 0.0f, TEXT_COLOR, 0); + + MenuSaveZoneQYN_2.m_numOptions = 0; + MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_NO"), X(80.0f), YF(20.0f), TriggerSave_BackToMainMenu, 0, 0); + MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_YES"), X(80.0f), 0.0f, TriggerSave_DeleteGameDeleteGame, 0, 0); + + MenuPageSaveZone_QuestionYesNo.ActivatePage(); + pMenuSave = &MenuPageSaveZone_QuestionYesNo; + bMemoryCardSpecialZone = false; + break; + } + } + } + } +} + +void +TriggerSave_DeleteGameSelect(CMenuMultiChoiceTriggered *widget) +{ + FillMenuWithMemCardFileListing(&MenuSaveDG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_DeleteGameDeleteGameSelect, nil, 0, 34, 22); + FillMenuWithMemCardFileListing(&MenuSaveLG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_LoadGameLoadGameSelect, nil, 0, 34, 22); + + pMenuSave = &MenuPage_SaveDeleteGame; + pMenuSave->ActivatePage(); + + gErrorSampleTriggered = false; + bMemoryCardSpecialZone = true; + bIgnoreTriangleButton = true; +} + +void +TriggerSave_LoadGameLoadGame(CMenuMultiChoiceTriggered *widget) +{ + if ( widget ) + { + bMemoryCardSpecialZone = false; + bIgnoreTriangleButton = false; + + if ( !MemCardAccessTriggerCaller.CanCall() ) + MemCardAccessTriggerCaller.SetTrigger(TriggerSave_LoadGameLoadGame, widget); + else + { + // Loading data. Please do not remove the Memory Card (PS2) in MEMORY CARD slot 1, reset or switch off the console. + DisplayMemoryCardAccessMsg(TheText.Get("FELD_WR"), CRGBA(200, 50, 50, 192)); + TheMemoryCard.LoadSlotToBuffer(MemoryCardSlotSelected); + + if ( TheMemoryCard.GetError() == CMemoryCard::NO_ERR_SUCCESS) + { + FrontEndMenuManager.m_bWantToRestart = true; + FrontEndMenuManager.AnaliseMenuContents(); + FrontEndMenuManager.m_bMenuActive = false; + FrontEndMenuManager.m_bInSaveZone = false; + + CTimer::EndUserPause(); + + TheMemoryCard.m_bWantToLoad = true; + + DMAudio.SetEffectsFadeVol(0); + DMAudio.SetMusicFadeVol(0); + DMAudio.ResetTimers(CTimer::GetTimeInMilliseconds()); + } + else + { + // Load Failed! Check Memory Card (PS2) in MEMORY CARD slot 1 and please try again. + MenuSaveZoneMSG_1.m_numTexts = 0; + MenuSaveZoneMSG_1.AddText(TheText.Get("FES_LOE"), X(-80.0f), YF(20.0f), TEXT_COLOR, 0); + + MenuSaveZoneMSG_2.m_numOptions = 0; + MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), YF(25.0f), TriggerSave_BackToMainMenu, 0, 0); + + pMenuSave = &MenuPageSaveZone_Message; + pMenuSave->ActivatePage(); + + bMemoryCardSpecialZone = false; + bIgnoreTriangleButton = true; + } + } + } +} + +void +TriggerSave_LoadGameLoadGameSelect(CMenuMultiChoiceTwoLinesTriggered *widget) +{ + if ( widget ) + { + if ( widget->GetMenuSelection() > 0 ) + { + MemoryCardSlotSelected = widget->GetMenuSelection() - 1; + + switch ( TheMemoryCard.GetInfoOnSpecificSlot(MemoryCardSlotSelected) ) + { + case CMemoryCard::SLOT_NOTPRESENT: + { + break; + } + case CMemoryCard::SLOT_CORRUPTED: + { + // Load Failed. + MenuSaveZoneMSG_1.m_numTexts = 0; + MenuSaveZoneMSG_1.AddText(TheText.Get("FES_LOF"), X(50.0f), YF(20.0f), TEXT_COLOR, 0); + + MenuSaveZoneMSG_2.m_numOptions = 0; + MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), 0.0f, TriggerSave_BackToMainMenu, 0, 0); + + MenuPageSaveZone_Message.ActivatePage(); + pMenuSave = &MenuPageSaveZone_Message; + bMemoryCardSpecialZone = false; + break; + } + case CMemoryCard::SLOT_PRESENT: + { + // All unsaved progress in your current game will be lost. Proceed with loading? + MenuSaveZoneQYN_1.m_numTexts = 0; + MenuSaveZoneQYN_1.AddText(TheText.Get("FESZ_QL"), X(-40.0f), 0.0f, TEXT_COLOR, 0); + + MenuSaveZoneQYN_2.m_numOptions = 0; + MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_NO"), X(80.0f), YF(20.0f), TriggerSave_BackToMainMenu, 0, 0); + MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_YES"), X(80.0f), 0.0f, TriggerSave_LoadGameLoadGame, 0, 0); + + MenuPageSaveZone_QuestionYesNo.ActivatePage(); + pMenuSave = &MenuPageSaveZone_QuestionYesNo; + bMemoryCardSpecialZone = false; + break; + } + } + } + } +} + +void +TriggerSave_LoadGameSelect(CMenuMultiChoiceTriggered *widget) +{ + FillMenuWithMemCardFileListing(&MenuSaveLG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_LoadGameLoadGameSelect, nil, 0, 34, 22); + FillMenuWithMemCardFileListing(&MenuSaveDG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_DeleteGameDeleteGameSelect, nil, 0, 34, 22); + + pMenuSave = &MenuPage_SaveLoadGame; + pMenuSave->ActivatePage(); + + gErrorSampleTriggered = false; + bMemoryCardSpecialZone = true; + bIgnoreTriangleButton = true; +} + +void +TriggerSave_BackToMainMenu(CMenuMultiChoiceTriggered *widget) +{ + pMenuSave = &MenuPage_SaveBasic; + pMenuSave->ActivatePage(); + bMemoryCardSpecialZone = false; + bIgnoreTriangleButton = false; +} + +void InitialiseTextsInMenuControllerInCar(CMenuPictureAndText *widget, CMenuManager::CONTRCONFIG cont) +{ + if ( widget ) + { + widget->m_numTexts = 0; + + switch ( cont ) + { + case CMenuManager::CONFIG_1: + { + widget->AddText(TheText.Get("FEC_LL"), X(50.0f), Y(-14.0f), PAD_TEXT_COLOR, true); // -16.0f + widget->AddText(TheText.Get("FEC_RSC"), X(-4.0f), Y(29.0f), PAD_TEXT_COLOR, true); // 33.142860f + widget->AddText(TheText.Get("FEC_VES"), X(-4.0f), Y(65.0f), PAD_TEXT_COLOR, true); // 74.285721f + widget->AddText(TheText.Get("FEC_VES"), X(-4.0f), Y(97.0f), PAD_TEXT_COLOR, true); // 110.857147f + widget->AddText(TheText.Get("FEC_HO3"), X(84.0f), Y(162.0f), PAD_TEXT_COLOR, false); // 185.142868f + widget->AddText(TheText.Get("FEC_CAM"), X(103.0f), Y(141.0f), PAD_TEXT_COLOR, false); // 161.142868f + widget->AddText(TheText.Get("FEC_PAU"), X(130.0f), Y(128.0f), PAD_TEXT_COLOR, false); // 146.285721f + widget->AddText(TheText.Get("FEC_LB"), X(68.0f), Y(-6.0f), PAD_TEXT_COLOR, false); // -6.857143f + widget->AddText(TheText.Get("FEC_LR"), X(184.0f), Y(-14.0f), PAD_TEXT_COLOR, false); // -16.0f + widget->AddText(TheText.Get("FEC_HAB"), X(238.0f), Y(25.0f), PAD_TEXT_COLOR, false); // 28.571430f + widget->AddText(TheText.Get("FEC_BRA"), X(155.0f), Y(18.0f), PAD_TEXT_COLOR, true); // 20.571430f + widget->AddText(TheText.Get("FEC_EXV"), X(238.0f), Y(52.0f), PAD_TEXT_COLOR, false); // 59.428574f + widget->AddText(TheText.Get("FEC_CAW"), X(238.0f), Y(65.0f), PAD_TEXT_COLOR, false); // 74.285721f + widget->AddText(TheText.Get("FEC_ACC"), X(238.0f), Y(78.0f), PAD_TEXT_COLOR, false); // 89.142860f + widget->AddText(TheText.Get("FEC_TUC"), X(238.0f), Y(94.0f), PAD_TEXT_COLOR, false); // 107.428574f + widget->AddText(TheText.Get("FEC_SM3"), X(238.0f), Y(109.0f), PAD_TEXT_COLOR, false); // 124.571434f + + break; + } + + case CMenuManager::CONFIG_2: + { + widget->AddText(TheText.Get("FEC_LL"), X(50.0f), Y(-14.0f), PAD_TEXT_COLOR, true); // -16.0f + widget->AddText(TheText.Get("FEC_HOR"), X(-4.0f), Y(29.0f), PAD_TEXT_COLOR, true); // 33.142860f + widget->AddText(TheText.Get("FEC_CAM"), X(-4.0f), Y(65.0f), PAD_TEXT_COLOR, true); // 74.285721f + widget->AddText(TheText.Get("FEC_VES"), X(-4.0f), Y(97.0f), PAD_TEXT_COLOR, true); // 110.857147f + widget->AddText(TheText.Get("FEC_NA"), X(84.0f), Y(162.0f), PAD_TEXT_COLOR, false); // 185.142868f + widget->AddText(TheText.Get("FEC_RSC"), X(103.0f), Y(141.0f), PAD_TEXT_COLOR, false); // 161.142868f + widget->AddText(TheText.Get("FEC_PAU"), X(130.0f), Y(128.0f), PAD_TEXT_COLOR, false); // 146.285721f + widget->AddText(TheText.Get("FEC_LB"), X(68.0f), Y(-6.0f), PAD_TEXT_COLOR, false); // -6.857143f + widget->AddText(TheText.Get("FEC_LR"), X(184.0f), Y(-14.0f), PAD_TEXT_COLOR, false); // -16.0f + widget->AddText(TheText.Get("FEC_HAB"), X(238.0f), Y(25.0f), PAD_TEXT_COLOR, false); // 28.571430f + widget->AddText(TheText.Get("FEC_BRA"), X(155.0f), Y(18.0f), PAD_TEXT_COLOR, true); // 20.571430f + widget->AddText(TheText.Get("FEC_EXV"), X(238.0f), Y(52.0f), PAD_TEXT_COLOR, false); // 59.428574f + widget->AddText(TheText.Get("FEC_CAW"), X(238.0f), Y(65.0f), PAD_TEXT_COLOR, false); // 74.285721f + widget->AddText(TheText.Get("FEC_ACC"), X(238.0f), Y(78.0f), PAD_TEXT_COLOR, false); // 89.142860f + widget->AddText(TheText.Get("FEC_TUC"), X(238.0f), Y(94.0f), PAD_TEXT_COLOR, false); // 107.428574f + widget->AddText(TheText.Get("FEC_SM3"), X(238.0f), Y(109.0f), PAD_TEXT_COLOR, false); // 124.571434f + + break; + } + + case CMenuManager::CONFIG_3: + { + widget->AddText(TheText.Get("FEC_LL"), X(50.0f), Y(-14.0f), PAD_TEXT_COLOR, true); // -16.0f + widget->AddText(TheText.Get("FEC_EXV"), X(-4.0f), Y(29.0f), PAD_TEXT_COLOR, true); // 33.142860f + widget->AddText(TheText.Get("FEC_VES"), X(-4.0f), Y(65.0f), PAD_TEXT_COLOR, true); // 74.285721f + widget->AddText(TheText.Get("FEC_VES"), X(-4.0f), Y(97.0f), PAD_TEXT_COLOR, true); // 110.857147f + widget->AddText(TheText.Get("FEC_RS3"), X(84.0f), Y(162.0f), PAD_TEXT_COLOR, false); // 185.142868f + widget->AddText(TheText.Get("FEC_CAM"), X(103.0f), Y(141.0f), PAD_TEXT_COLOR, false); // 161.142868f + widget->AddText(TheText.Get("FEC_PAU"), X(130.0f), Y(128.0f), PAD_TEXT_COLOR, false); // 146.285721f + widget->AddText(TheText.Get("FEC_LB"), X(68.0f), Y(-6.0f), PAD_TEXT_COLOR, false); // -6.857143f + widget->AddText(TheText.Get("FEC_LR"), X(184.0f), Y(-14.0f), PAD_TEXT_COLOR, false); // -16.0f + widget->AddText(TheText.Get("FEC_HOR"), X(238.0f), Y(25.0f), PAD_TEXT_COLOR, false); // 28.571430f + widget->AddText(TheText.Get("FEC_BRA"), X(155.0f), Y(18.0f), PAD_TEXT_COLOR, true); // 20.571430f + widget->AddText(TheText.Get("FEC_HAB"), X(238.0f), Y(52.0f), PAD_TEXT_COLOR, false); // 59.428574f + widget->AddText(TheText.Get("FEC_CAW"), X(238.0f), Y(65.0f), PAD_TEXT_COLOR, false); // 74.285721f + widget->AddText(TheText.Get("FEC_ACC"), X(238.0f), Y(78.0f), PAD_TEXT_COLOR, false); // 89.142860f + widget->AddText(TheText.Get("FEC_TUC"), X(238.0f), Y(94.0f), PAD_TEXT_COLOR, false); // 107.428574f + widget->AddText(TheText.Get("FEC_SM3"), X(238.0f), Y(109.0f), PAD_TEXT_COLOR, false); // 124.571434f + + break; + } + + case CMenuManager::CONFIG_4: + { + widget->AddText(TheText.Get("FEC_LL"), X(50.0f), Y(-14.0f), PAD_TEXT_COLOR, true); // -16.0f + widget->AddText(TheText.Get("FEC_HAB"), X(-4.0f), Y(29.0f), PAD_TEXT_COLOR, true); // 33.142860f + widget->AddText(TheText.Get("FEC_TUC"), X(-4.0f), Y(65.0f), PAD_TEXT_COLOR, true); // 74.285721f + widget->AddText(TheText.Get("FEC_VES"), X(-4.0f), Y(97.0f), PAD_TEXT_COLOR, true); // 110.857147f + widget->AddText(TheText.Get("FEC_HO3"), X(84.0f), Y(162.0f), PAD_TEXT_COLOR, false); // 185.142868f + widget->AddText(TheText.Get("FEC_CAM"), X(103.0f), Y(141.0f), PAD_TEXT_COLOR, false); // 161.142868f + widget->AddText(TheText.Get("FEC_PAU"), X(130.0f), Y(128.0f), PAD_TEXT_COLOR, false); // 146.285721f + widget->AddText(TheText.Get("FEC_LB"), X(68.0f), Y(-6.0f), PAD_TEXT_COLOR, false); // -6.857143f + widget->AddText(TheText.Get("FEC_LR"), X(184.0f), Y(-14.0f), PAD_TEXT_COLOR, false); // -16.0f + widget->AddText(TheText.Get("FEC_CAW"), X(238.0f), Y(25.0f), PAD_TEXT_COLOR, false); // 28.571430f + widget->AddText(TheText.Get("FEC_SMT"), X(155.0f), Y(18.0f), PAD_TEXT_COLOR, true); // 20.571430f + widget->AddText(TheText.Get("FEC_EXV"), X(238.0f), Y(52.0f), PAD_TEXT_COLOR, false); // 59.428574f + widget->AddText(TheText.Get("FEC_RSC"), X(238.0f), Y(65.0f), PAD_TEXT_COLOR, false); // 74.285721f + widget->AddText(TheText.Get("FEC_NA"), X(238.0f), Y(78.0f), PAD_TEXT_COLOR, false); // 89.142860f + widget->AddText(TheText.Get("FEC_ACC"), X(238.0f), Y(94.0f), PAD_TEXT_COLOR, false); // 107.428574f + widget->AddText(TheText.Get("FEC_BRA"), X(238.0f), Y(109.0f), PAD_TEXT_COLOR, false); // 124.571434f + + break; + } + } + } +} + +void InitialiseTextsInMenuControllerOnFoot(CMenuPictureAndText *widget, CMenuManager::CONTRCONFIG cont) +{ + if ( widget ) + { + widget->m_numTexts = 0; + + + switch ( cont ) + { + case CMenuManager::CONFIG_1: + { + widget->AddText(TheText.Get("FEC_CWL"), X(50.0f), Y(-14.0f), PAD_TEXT_COLOR, true); // -16.0f + widget->AddText(TheText.Get("FEC_LOF"), X(-4.0f), Y(25.0f), PAD_TEXT_COLOR, true); // 28.571430f + widget->AddText(TheText.Get("FEC_MOV"), X(-4.0f), Y(65.0f), PAD_TEXT_COLOR, true); // 74.285721f + widget->AddText(TheText.Get("FEC_MOV"), X(-4.0f), Y(97.0f), PAD_TEXT_COLOR, true); // 110.857147f + widget->AddText(TheText.Get("FEC_CAM"), X(103.0f), Y(141.0f), PAD_TEXT_COLOR, false); // 161.142868f + widget->AddText(TheText.Get("FEC_PAU"), X(130.0f), Y(128.0f), PAD_TEXT_COLOR, false); // 146.285721f + widget->AddText(TheText.Get("FEC_CWR"), X(184.0f), Y(-14.0f), PAD_TEXT_COLOR, false); // -16.0f + widget->AddText(TheText.Get("FEC_TAR"), X(238.0f), Y(25.0f), PAD_TEXT_COLOR, false); // 28.571430f + widget->AddText(TheText.Get("FEC_JUM"), X(144.0f), Y(18.0f), PAD_TEXT_COLOR, true); // 20.571430f + widget->AddText(TheText.Get("FEC_ENV"), X(238.0f), Y(52.0f), PAD_TEXT_COLOR, false); // 59.428574f + widget->AddText(TheText.Get("FEC_ATT"), X(238.0f), Y(65.0f), PAD_TEXT_COLOR, false); // 74.285721f + widget->AddText(TheText.Get("FEC_RUN"), X(238.0f), Y(78.0f), PAD_TEXT_COLOR, false); // 89.142860f + widget->AddText(TheText.Get("FEC_FPC"), X(238.0f), Y(94.0f), PAD_TEXT_COLOR, false); // 107.428574f + widget->AddText(TheText.Get("FEC_LB3"), X(238.0f), Y(109.0f), PAD_TEXT_COLOR, false); // 124.571434f + widget->AddText(TheText.Get("FEC_R3"), X(238.0f), Y(122.0f), PAD_TEXT_COLOR, false); // 139.428574f + + break; + } + + case CMenuManager::CONFIG_2: + { + widget->AddText(TheText.Get("FEC_CWL"), X(50.0f), Y(-14.0f), PAD_TEXT_COLOR, true); // -16.0f + widget->AddText(TheText.Get("FEC_LOF"), X(-4.0f), Y(25.0f), PAD_TEXT_COLOR, true); // 28.571430f + widget->AddText(TheText.Get("FEC_CAM"), X(-4.0f), Y(65.0f), PAD_TEXT_COLOR, true); // 74.285721f + widget->AddText(TheText.Get("FEC_MOV"), X(-4.0f), Y(97.0f), PAD_TEXT_COLOR, true); // 110.857147f + widget->AddText(TheText.Get("FEC_NA"), X(103.0f), Y(141.0f), PAD_TEXT_COLOR, false); // 161.142868f + widget->AddText(TheText.Get("FEC_PAU"), X(130.0f), Y(128.0f), PAD_TEXT_COLOR, false); // 146.285721f + widget->AddText(TheText.Get("FEC_CWR"), X(184.0f), Y(-14.0f), PAD_TEXT_COLOR, false); // -16.0f + widget->AddText(TheText.Get("FEC_TAR"), X(238.0f), Y(25.0f), PAD_TEXT_COLOR, false); // 28.571430f + widget->AddText(TheText.Get("FEC_JUM"), X(144.0f), Y(18.0f), PAD_TEXT_COLOR, true); // 20.571430f + widget->AddText(TheText.Get("FEC_ENV"), X(238.0f), Y(52.0f), PAD_TEXT_COLOR, false); // 59.428574f + widget->AddText(TheText.Get("FEC_ATT"), X(238.0f), Y(65.0f), PAD_TEXT_COLOR, false); // 74.285721f + widget->AddText(TheText.Get("FEC_RUN"), X(238.0f), Y(78.0f), PAD_TEXT_COLOR, false); // 89.142860f + widget->AddText(TheText.Get("FEC_FPC"), X(238.0f), Y(94.0f), PAD_TEXT_COLOR, false); // 107.428574f + widget->AddText(TheText.Get("FEC_LB3"), X(238.0f), Y(109.0f), PAD_TEXT_COLOR, false); // 124.571434f + widget->AddText(TheText.Get("FEC_R3"), X(238.0f), Y(122.0f), PAD_TEXT_COLOR, false); // 139.428574f + + break; + } + + case CMenuManager::CONFIG_3: + { + widget->AddText(TheText.Get("FEC_CWL"), X(50.0f), Y(-14.0f), PAD_TEXT_COLOR, true); // -16.0f + widget->AddText(TheText.Get("FEC_ENV"), X(-4.0f), Y(25.0f), PAD_TEXT_COLOR, true); // 28.571430f + widget->AddText(TheText.Get("FEC_MOV"), X(-4.0f), Y(65.0f), PAD_TEXT_COLOR, true); // 74.285721f + widget->AddText(TheText.Get("FEC_MOV"), X(-4.0f), Y(97.0f), PAD_TEXT_COLOR, true); // 110.857147f + widget->AddText(TheText.Get("FEC_CAM"), X(103.0f), Y(141.0f), PAD_TEXT_COLOR, false); // 161.142868f + widget->AddText(TheText.Get("FEC_PAU"), X(130.0f), Y(128.0f), PAD_TEXT_COLOR, false); // 146.285721f + widget->AddText(TheText.Get("FEC_CWR"), X(184.0f), Y(-14.0f), PAD_TEXT_COLOR, false); // -16.0f + widget->AddText(TheText.Get("FEC_TAR"), X(238.0f), Y(25.0f), PAD_TEXT_COLOR, false); // 28.571430f + widget->AddText(TheText.Get("FEC_JUM"), X(144.0f), Y(18.0f), PAD_TEXT_COLOR, true); // 20.571430f + widget->AddText(TheText.Get("FEC_LOF"), X(238.0f), Y(52.0f), PAD_TEXT_COLOR, false); // 59.428574f + widget->AddText(TheText.Get("FEC_RUN"), X(238.0f), Y(65.0f), PAD_TEXT_COLOR, false); // 74.285721f + widget->AddText(TheText.Get("FEC_ATT"), X(238.0f), Y(78.0f), PAD_TEXT_COLOR, false); // 89.142860f + widget->AddText(TheText.Get("FEC_FPC"), X(238.0f), Y(94.0f), PAD_TEXT_COLOR, false); // 107.428574f + widget->AddText(TheText.Get("FEC_LB3"), X(238.0f), Y(109.0f), PAD_TEXT_COLOR, false); // 124.571434f + widget->AddText(TheText.Get("FEC_R3"), X(238.0f), Y(122.0f), PAD_TEXT_COLOR, false); // 139.428574f + + break; + } + + case CMenuManager::CONFIG_4: + { + widget->AddText(TheText.Get("FEC_CWL"), X(50.0f), Y(-14.0f), PAD_TEXT_COLOR, true); // -16.0f + widget->AddText(TheText.Get("FEC_TAR"), X(-4.0f), Y(25.0f), PAD_TEXT_COLOR, true); // 28.571430f + widget->AddText(TheText.Get("FEC_NA"), X(-4.0f), Y(65.0f), PAD_TEXT_COLOR, true); // 74.285721f + widget->AddText(TheText.Get("FEC_MOV"), X(-4.0f), Y(97.0f), PAD_TEXT_COLOR, true); // 110.857147f + widget->AddText(TheText.Get("FEC_CAM"), X(103.0f), Y(141.0f), PAD_TEXT_COLOR, false); // 161.142868f + widget->AddText(TheText.Get("FEC_PAU"), X(130.0f), Y(128.0f), PAD_TEXT_COLOR, false); // 146.285721f + widget->AddText(TheText.Get("FEC_CWR"), X(184.0f), Y(-14.0f), PAD_TEXT_COLOR, false); // -16.0f + widget->AddText(TheText.Get("FEC_ATT"), X(238.0f), Y(25.0f), PAD_TEXT_COLOR, false); // 28.571430f + widget->AddText(TheText.Get("FEC_JUM"), X(144.0f), Y(18.0f), PAD_TEXT_COLOR, true); // 20.571430f + widget->AddText(TheText.Get("FEC_ENV"), X(238.0f), Y(52.0f), PAD_TEXT_COLOR, false); // 59.428574f + widget->AddText(TheText.Get("FEC_LOF"), X(238.0f), Y(65.0f), PAD_TEXT_COLOR, false); // 74.285721f + widget->AddText(TheText.Get("FEC_RUN"), X(238.0f), Y(78.0f), PAD_TEXT_COLOR, false); // 89.142860f + widget->AddText(TheText.Get("FEC_FPC"), X(238.0f), Y(94.0f), PAD_TEXT_COLOR, false); // 107.428574f + widget->AddText(TheText.Get("FEC_LB3"), X(238.0f), Y(109.0f), PAD_TEXT_COLOR, false); // 124.571434f + widget->AddText(TheText.Get("FEC_R3"), X(238.0f), Y(122.0f), PAD_TEXT_COLOR, false); // 139.428574f + + break; + } + } + } +} + +void +TriggerSaveZone_BackToMainMenuTwoLines(CMenuMultiChoiceTwoLinesTriggered *widget) +{ + bMemoryCardSpecialZone = false; + bIgnoreTriangleButton = false; + pActiveMenuPage = &MenuPageSaveZone_SaveGame; +} + +void +TriggerSave_BackToMainMenuTwoLines(CMenuMultiChoiceTwoLinesTriggered *widget) +{ + pMenuSave = &MenuPage_SaveBasic; + pMenuSave->ActivatePage(); + bMemoryCardSpecialZone = false; + bIgnoreTriangleButton = false; +} + +void +SetRandomActiveTextlineColor(uint8 bText) +{ + if ( bMemoryCardSpecialZone ) + rgbaATC = SELECTED_TEXT_COLOR; + else + { + bool bSelected = false; + bool bHighlignted = false; + + switch ( FrontEndMenuManager.m_pageState ) + { + case PAGESTATE_NORMAL: + break; + case PAGESTATE_HIGHLIGHTED: + bHighlignted = true; + break; + case PAGESTATE_SELECTED: + bSelected = true; + break; + } + + if ( FrontEndMenuManager.m_bInSaveZone ) + bSelected = true; + + if ( bSelected || bText ) + { + static uint32 delayTime = 0; + static bool bAddVal = true; + + if ( delayTime < CTimer::GetTimeInMillisecondsPauseMode() ) + { + delayTime = CTimer::GetTimeInMillisecondsPauseMode() + 200; + + if ( bAddVal ) + rgbaATC = TEXT_COLOR; + else + rgbaATC = SELECTED_TEXT_COLOR; + + bAddVal = !bAddVal; + } + } + + if ( bHighlignted ) + { + static uint32 delayTime = 0; + static bool bAddVal = true; + + if ( delayTime < CTimer::GetTimeInMillisecondsPauseMode() ) + { + delayTime = CTimer::GetTimeInMillisecondsPauseMode() + 200; + + if ( bAddVal ) + rgbaATC = TITLE_TEXT_COLOR; + else + rgbaATC = MENU_SELECTED_COLOR; + + bAddVal = !bAddVal; + } + } + } +} + +#ifdef GTA_PC + +void +TriggerDisplay_Trails(CMenuOnOffTriggered *widget) +{ + if ( widget ) + { + CMenuManager::m_PrefsShowTrails = widget->GetMenuSelection(); + CMBlur::BlurOn = CMenuManager::m_PrefsShowTrails; + + if ( CMBlur::BlurOn ) + CMBlur::MotionBlurOpen(Scene.camera); + else + CMBlur::MotionBlurClose(); + } +} + +#endif \ No newline at end of file diff --git a/src/miami/core/Frontend_PS2.cpp b/src/miami/core/Frontend_PS2.cpp new file mode 100644 index 00000000..fa238031 --- /dev/null +++ b/src/miami/core/Frontend_PS2.cpp @@ -0,0 +1,3020 @@ +#include "common.h" +#ifdef PS2_MENU +#include "platform.h" +#include "main.h" +#include "Timer.h" +#include "Pad.h" +#include "Sprite2d.h" +#include "Text.h" +#include "Font.h" +#include "Hud.h" +#include "MBlur.h" +#include "DMAudio.h" +#include "Streaming.h" +#include "Camera.h" +#include "Credits.h" +#include "General.h" +#include "TxdStore.h" +#include "FileMgr.h" +#include "Messages.h" +#include "Frontend_PS2.h" +#include "Stats.h" +#include "Game.h" +#include "World.h" +#include "PlayerInfo.h" +#include "FrontEndControls.h" +#include "MemoryCard.h" + +#define CRect_SZ(x, y, w, h) CRect(x, y, x+w, y+h) + +wchar MemoryCard_FileNames[8][100+1]; +CMenuManager FrontEndMenuManager; + +// TEMP: put into header +bool DoRWStuffStartOfFrame_Horizon(int16 TopRed, int16 TopGreen, int16 TopBlue, int16 BottomRed, int16 BottomGreen, int16 BottomBlue, int16 Alpha); +bool DoRWStuffStartOfFrame(int16 TopRed, int16 TopGreen, int16 TopBlue, int16 BottomRed, int16 BottomGreen, int16 BottomBlue, int16 Alpha); +void DoRWStuffEndOfFrame(void); + + +#define SCRW SCREEN_WIDTH +#define SCRH SCREEN_HEIGHT +//#define X SCREEN_STRETCH_X +//#define Y SCREEN_STRETCH_Y +#define X SCREEN_SCALE_X +#define Y SCREEN_SCALE_Y + +#define YF(x) Y(float(x)*(float(DEFAULT_SCREEN_HEIGHT)/float(SCREEN_HEIGHT_PAL))) +//#define X(x) ((x)/640.0f*SCRW) +//#define Y(y) ((y)/448.0f*SCRH) + + +static float MENU_TEXT_SIZE_X = 0.644f; +static float MENU_TEXT_SIZE_Y = 0.84f; //0.96f; +float BUTTONTAB_TEXT_SIZE_X = 0.35f; +float BUTTONTAB_TEXT_SIZE_Y = 0.7f; //0.8f; +float PANEL_TEXT_SIZE_X = 0.8f; +float PANEL_TEXT_SIZE_Y = 1.2f; //0.96f/0.7f; //?? +float MEMCARD_ACCESS_MSG_SIZE_X = 0.84f; +float MEMCARD_ACCESS_MSG_SIZE_Y = 1.12f; //1.28f; + +CRGBA SELECTED_TEXT_COLOR(255, 182, 48, 255); +CRGBA BACKGROUND_SPLASH_COLOR(48, 48, 48, 255); + +CVector2D CONTR_DESCR_NEW_TEXTSCALE(0.4564f, 0.63f); // 0.72 +CVector2D CONFIGS_NEW_TEXTSCALE(0.49f, 0.7f); // 0.8 +CVector2D AUDIO_OUTPUT_POS(0.0f, 0.0f); +CVector2D AUDIO_RSTATION_POS(154.0f, 0.0f); +CVector2D DISPLAY_BRIGHTNESS_POS(0.0f, 0.0f); + +CRGBA TEXT_COLOR(150, 110, 30, 255); +CRGBA PAD_TEXT_COLOR(200, 200, 200, 255); +CRGBA CRIM_RATING_TEXT_COLOR(255, 182, 48, 255); +CRGBA SCROLL_TEXT_COLOR(150, 110, 30, 255); +CRGBA TITLE_TEXT_COLOR(170, 130, 50, 255); +CRGBA TEXT_SHADOW_COLOR(0, 0, 0, 255); +CVector2D SHADOW_VECTOR(1.0f, 1.0f); +CRGBA SLIDER_RIGHT_COLOR(20, 94, 136, 255); +CRGBA SLIDER_LEFT_COLOR(86, 196, 255, 255); +CRGBA MENU_SELECTED_COLOR(255, 212, 88, 255); +CRGBA rgbaATC(96, 96, 96, 255); // active text color. not constant + +float BUTTONTAB_TEXT_X_SCALES[NUM_PAGES] = { 1.0f }; +float PANEL_TEXT_X_SCALES[NUM_PAGES] = { 1.0f }; + +int32 MemoryCardSlotSelected; +uint32 TimeToStopPadShaking; +bool bFrontEnd_ReloadObrTxtGxt; + +bool bMemoryCardStartUpMenus_ExitNow; + +extern CMenuPage MenuPage_SaveBasic; +CMenuPage *pActiveMenuPage; +CMenuPage *pMenuSave = &MenuPage_SaveBasic; +bool bMemoryCardSpecialZone; +bool bIgnoreTriangleButton; +bool gErrorSampleTriggered; + +bool gMusicPlaying; + +CMenuPage MenuPage_Stats; + CMenuLineLister MenuStats_1; + CMenuPictureAndText MenuStats_2; // criminal rating +CMenuPage MenuPage_Briefs; + CMenuPictureAndText MenuBriefs_1; + CMenuDummy MenuBriefs_2; +CMenuPage MenuPage_SaveBasic; + CMenuMultiChoiceTriggered MenuSaveB_1; // "Load Game", "Delete Game", "New Game" +CMenuPage MenuPage_SaveNewGame; + CMenuPictureAndText MenuSaveNG_1; // "Load Game", "Delete Game", "New Game" + CMenuMultiChoiceTriggered MenuSaveNG_2; // "No", "Yes" +CMenuPage MenuPage_SaveLoadGame; + CMenuPictureAndText MenuSaveLG_1; // "Load Game", "Delete Game", "New Game" + CMenuMultiChoiceTwoLinesTriggered MenuSaveLG_2; // save games +CMenuPage MenuPage_SaveDeleteGame; + CMenuPictureAndText MenuSaveDG_1; // "Load Game", "Delete Game", "New Game" + CMenuMultiChoiceTwoLinesTriggered MenuSaveDG_2; // save games +CMenuPage MenuPage_Controls; + CMenuPictureAndText MenuControls_3; // controller images + CMenuPictureAndText MenuControls_6; + CMenuPictureAndText MenuControls_4; + CMenuPictureAndText MenuControls_7; + CMenuMultiChoiceTriggeredAlways MenuControls_1; // "Configuration:" "Setup1", "Setup2", "Setup3", "Setup4" + CMenuMultiChoiceTriggered MenuControls_2; // "Controller Display:" "On Foot", "In Car" + CMenuOnOffTriggered MenuControls_5; // "Vibration:" +CMenuPageAnyMove MenuPage_Audio; + CMenuSliderTriggered MenuAudio_1; // "Music Volume" + CMenuMultiChoiceTriggered MenuAudio_4; // "Output:" "Stereo", "Mono" + CMenuSliderTriggered MenuAudio_2; // "SFX Volume" + CMenuMultiChoicePicturedTriggeredAnyMove MenuAudio_3; // "Radio station select:" +CMenuPage MenuPage_Display; + CMenuSlider MenuDisplay_1; // "Brightness" +#ifdef GTA_PC + CMenuOnOffTriggered MenuDisplay_2; // "Trails:" +#else + CMenuOnOff MenuDisplay_2; // "Trails:" +#endif + CMenuOnOff MenuDisplay_3; // "Subtitles:" + CMenuOnOff MenuDisplay_4; // "Wide Screen:" +CMenuPage MenuPage_Language; + CMenuMultiChoiceTriggered MenuLanguage_1; // "English", "French", "German", "Italian", "Spanish" + +CMenuPage MenuPageSaveZone_SaveGame; + CMenuMultiChoiceTriggered MenuSaveZoneSG_1; // "Save game", "Cancel" +CMenuPage MenuPageSaveZone_SaveSlots; + CMenuMultiChoiceTwoLinesTriggered MenuSaveZoneSSL_1; // "Cancel" +CMenuPage MenuPageSaveZone_SavedSuccessfully; + CMenuPictureAndText MenuSaveZoneSS_1; // "Game saved successfully!" "Your saved filename is:" + CMenuMultiChoiceTriggered MenuSaveZoneSS_2; // "Quit" +CMenuPage MenuPageSaveZone_Message; + CMenuPictureAndText MenuSaveZoneMSG_1; // "Save Failed! Check memory card (PS2) in MEMORY CARD slot 1 and please try again." + CMenuMultiChoiceTriggered MenuSaveZoneMSG_2; // "OK" +CMenuPage MenuPageSaveZone_QuestionYesNo; + CMenuPictureAndText MenuSaveZoneQYN_1; // "Save Failed! Check memory card (PS2) in MEMORY CARD slot 1 and please try again." + CMenuMultiChoiceTriggered MenuSaveZoneQYN_2; // "Yes", "No" +CMenuPage MenuPageSaveZone_FormatCard; + CMenuMultiChoiceTriggered MenuSaveZoneFC_1; // "Memory card (PS2) in MEMORY CARD slot 1 is unformatted. Would you like to format memory card (PS2) in MEMORY CARD slot 1?" "No" "Yes" +CMenuPage MenuPageSaveZone_ErrorFormat; + CMenuMultiChoiceTriggered MenuSaveZoneEF_1; // "Format Failed! Check memory card (PS2) in MEMORY CARD slot 1 and please try again." "OK" + + +VALIDATE_SIZE(CPlaceableText, 0x10); +VALIDATE_SIZE(CPlaceableShText, 0x20); +VALIDATE_SIZE(CPlaceableShTextTwoLines, 0x30); +VALIDATE_SIZE(CPlaceableShOption, 0x28); +VALIDATE_SIZE(CPlaceableShOptionTwoLines, 0x38); +VALIDATE_SIZE(CPlaceableSprite, 0x18); +VALIDATE_SIZE(CPlaceableShSprite, 0x34); +VALIDATE_SIZE(CMenuMultiChoice, 0x2CC); +VALIDATE_SIZE(CMenuMultiChoiceTriggered, 0x310); +VALIDATE_SIZE(CMenuMultiChoiceTwoLines, 0x3CC); +VALIDATE_SIZE(CMenuOnOff, 0x90); + +#include "FrontendTriggers.h" + +static const char* FrontendFilenames[][2] = +{ + {"fe2_mainpanel_ul", "" }, + {"fe2_mainpanel_ur", "" }, + {"fe2_mainpanel_dl", "" }, + {"fe2_mainpanel_dr", "" }, + {"fe2_mainpanel_dr2", "" }, + {"fe2_tabactive", "" }, + {"fe_iconbrief", "" }, + {"fe_iconstats", "" }, + {"fe_iconcontrols", "" }, + {"fe_iconsave", "" }, + {"fe_iconaudio", "" }, + {"fe_icondisplay", "" }, + {"fe_iconlanguage", "" }, + {"fe_controller", "" }, + {"fe_controllersh", "" }, + {"fe_arrows1", "" }, + {"fe_arrows2", "" }, + {"fe_arrows3", "" }, + {"fe_arrows4", "" }, + {"fe_radio1", "" }, + {"fe_radio2", "" }, + {"fe_radio3", "" }, + {"fe_radio4", "" }, + {"fe_radio5", "" }, + {"fe_radio6", "" }, + {"fe_radio7", "" }, + {"fe_radio8", "" }, + {"fe_radio9", "" }, +}; + +#ifdef CUTSCENE_BORDERS_SWITCH +bool CMenuManager::m_PrefsCutsceneBorders = true; +#endif + +int32 CMenuManager::m_PrefsSfxVolume = 102; +int32 CMenuManager::m_PrefsMusicVolume = 102; +int32 CMenuManager::m_PrefsBrightness = 256; +bool CMenuManager::m_PrefsShowTrails = true; +bool CMenuManager::m_PrefsShowSubtitles = true; +bool CMenuManager::m_PrefsAllowNastyGame = true; + +int32 CMenuManager::m_PrefsRadioStation = 0; +int32 CMenuManager::m_PrefsStereoMono = 0; +int8 CMenuManager::m_PrefsUseWideScreen = 0; +int32 CMenuManager::m_PrefsLanguage = 0; +CMenuManager::CONTRCONFIG CMenuManager::m_PrefsControllerConfig = CONFIG_1; +bool CMenuManager::m_PrefsUseVibration = false; + + +#ifdef GTA_PC +#include "PlayerSkin.h" +int32 CMenuManager::OS_Language = 0; +int8 CMenuManager::m_PrefsVsync = 1; +int8 CMenuManager::m_PrefsVsyncDisp = 1; +int8 CMenuManager::m_PrefsFrameLimiter = 1; +int8 CMenuManager::m_PrefsSpeakers; +int32 CMenuManager::m_ControlMethod = CONTROL_CLASSIC; +int8 CMenuManager::m_PrefsDMA = 1; +float CMenuManager::m_PrefsLOD = 1.0f; +char CMenuManager::m_PrefsSkinFile[256] = DEFAULT_SKIN_NAME; + +#ifndef MASTER +bool CMenuManager::m_PrefsMarketing; +bool CMenuManager::m_PrefsDisableTutorials; +#endif // !MASTER + +#ifdef MENU_MAP +bool CMenuManager::bMenuMapActive; +float CMenuManager::fMapSize; +float CMenuManager::fMapCenterY; +float CMenuManager::fMapCenterX; +#endif + +#endif + + +CMenuManager::CMenuManager(void) +{ + int32 i; + + SetSoundLevelsForMusicMenu(); + + m_pageState = PAGESTATE_NORMAL; + m_currentPage = PAGE_FIRST; + m_newPage = PAGE_FIRST; + m_bMenuActive = false; + m_bSaveMenuActive = false; + m_bRenderGameInMenu = false; + m_bTexturesLoaded = false; + m_nPageLeftTimer = 0; + m_nPageRightTimer = 0; + m_nChangePageTimer = 0; + field_18 = 0; + m_fade = 255; + m_someAlpha = 255; + m_position.x = 0.0f; + m_position.y = 0.0f; + m_nSlidingDir = SLIDE_TO_BOTTOM; + m_nStartPauseTimer = 0; + m_nEndPauseTimer = 0; + m_bInitialised = false; + m_bWantToUpdateContent = false; + field_3C = 0; + m_bInSaveZone = false; + + for(i = 0; i < NUM_PAGES; i++){ + BUTTONTAB_TEXT_X_SCALES[i] = 1.0f; + PANEL_TEXT_X_SCALES[i] = 1.0f; + } + +#ifdef GTA_PC + TheCamera.m_bUseMouse3rdPerson = m_ControlMethod == CONTROL_STANDARD; + CMBlur::BlurOn = m_PrefsShowTrails; +#endif +} + +void +CMenuManager::LoadAllTextures(void) +{ + int32 i; + + if(m_bTexturesLoaded) + return; + + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_STARTING, 0); + DMAudio.Service(); + DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); + CSprite2d *splash = LoadSplash(nil); + if(splash) + splash->Draw(CRect(0.0f, 0.0f, SCRW, SCRH), BACKGROUND_SPLASH_COLOR); + else // doesn't exist!! + CHud::Sprites[19].Draw(CRect(0.0f, 0.0f, SCRW, SCRH), BACKGROUND_SPLASH_COLOR); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERMIPNEAREST); + DoRWStuffEndOfFrame(); + + CFileMgr::SetDir(""); + CFileMgr::SetDir(""); + + CTimer::Stop(); + CStreaming::MakeSpaceFor(60*1024); + CStreaming::ImGonnaUseStreamingMemory(); + CGame::TidyUpMemory(false, true); + int32 slot = CTxdStore::FindTxdSlot("frontend"); + if(slot == -1) + slot = CTxdStore::AddTxdSlot("frontend"); + printf("LOAD frontend\n"); + CTxdStore::LoadTxd(slot, "MODELS/FRONTEND.TXD"); + CTxdStore::SetCurrentTxd(slot); + CStreaming::IHaveUsedStreamingMemory(); + CTimer::Update(); + + for(i = 0; i < NUM_SPRIRES; i++) + { + m_sprites[i].SetTexture(FrontendFilenames[i][0], FrontendFilenames[i][1]); + m_sprites[i].SetAddressing(rwTEXTUREADDRESSBORDER); + } + + m_bTexturesLoaded = true; +} + +void +CMenuManager::UnloadTextures(void) +{ + int32 slot; + int32 i; + + if ( !m_bTexturesLoaded ) + return; + + slot = CTxdStore::FindTxdSlot("frontend"); +#ifdef FIX_BUGS + for(i = 0; i < NUM_SPRIRES; i++) + m_sprites[i].Delete(); +#endif + + printf("REMOVE frontend\n"); + CTxdStore::RemoveTxd(slot); + m_bTexturesLoaded = false; +} + +void +CMenuManager::InitialiseMenusOnce(void) +{ + if(m_bInitialised) + return; + m_bInitialised = true; + + InitialiseChangedLanguageSettings(); + + // Normal menu + MenuPage_Stats.Initialise(); + MenuPage_Briefs.Initialise(); + MenuPage_SaveBasic.Initialise(); + MenuPage_SaveNewGame.Initialise(); + MenuPage_SaveLoadGame.Initialise(); + MenuPage_SaveDeleteGame.Initialise(); + MenuPage_Controls.Initialise(); + MenuPage_Audio.Initialise(); + MenuPage_Display.Initialise(); + MenuPage_Language.Initialise(); + + // Save menu + MenuPageSaveZone_SaveGame.Initialise(); + MenuPageSaveZone_SaveSlots.Initialise(); + MenuPageSaveZone_SavedSuccessfully.Initialise(); + MenuPageSaveZone_Message.Initialise(); + MenuPageSaveZone_QuestionYesNo.Initialise(); + MenuPageSaveZone_FormatCard.Initialise(); + MenuPageSaveZone_ErrorFormat.Initialise(); + + /* Stats */ + + MenuStats_1.ResetNumberOfTextLines(); + MenuStats_1.SetPosition(X(75.0f), Y(70.0f)); + MenuStats_1.m_width = X(480.0f); + MenuStats_1.m_height = Y(274.0f); + MenuStats_1.field_10E8 = 0; // unknown + MenuStats_1.m_lineSpacing = Y(20.0f); + MenuStats_1.m_scrollSpeed = 1.0f; + MenuStats_1.SetLinesColor(SCROLL_TEXT_COLOR); + MenuStats_1.ResetNumberOfTextLines(); + MenuPage_Stats.AddMenu(&MenuStats_1); + MenuStats_2.SetPosition(X(75.0f), Y(50.0f)); + MenuStats_2.SetTextsColor(CRIM_RATING_TEXT_COLOR); + MenuPage_Stats.AddMenu(&MenuStats_2); + MenuPage_Stats.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuPage_Stats.ActivatePage(); + + + CVector2D saveGameTextScale(X(0.49f), Y(0.7f)); + CVector2D defaultTextScale(X(MENU_TEXT_SIZE_X), Y(MENU_TEXT_SIZE_Y)); + + /* Basic Load/Delete/New Game */ + + MenuSaveB_1.m_numOptions = 0; + MenuSaveB_1.SetPosition(X(220.0f), Y(110.0f)); + MenuSaveB_1.AddOption(TheText.Get("FES_LGA"), 0.0f, Y(20.0f), TriggerSave_LoadGameSelect, false, true); + MenuSaveB_1.AddOption(TheText.Get("FES_DGA"), 0.0f, Y(40.0f), TriggerSave_DeleteGameSelect, false, true); + MenuSaveB_1.AddOption(TheText.Get("FES_NGA"), 0.0f, Y(60.0f), TriggerSave_NewGameSelectYes, false, true); + MenuSaveB_1.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); + MenuPage_SaveBasic.AddMenu(&MenuSaveB_1); + MenuPage_SaveBasic.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuPage_SaveBasic.ActivatePage(); + + /* New Game - but unused */ + + MenuSaveNG_1.m_numTexts = 0; + MenuSaveNG_1.SetPosition(X(220.0f), Y(110.0f)); + MenuSaveNG_1.AddText(TheText.Get("FES_LGA"), 0.0f, Y(20.0f), TEXT_COLOR, true); + MenuSaveNG_1.AddText(TheText.Get("FES_DGA"), 0.0f, Y(40.0f), TEXT_COLOR, true); + MenuSaveNG_1.AddText(TheText.Get("FES_NGA"), 0.0f, Y(60.0f), SELECTED_TEXT_COLOR, true); + MenuPage_SaveNewGame.AddMenu(&MenuSaveNG_1); + MenuSaveNG_2.m_numOptions = 0; + MenuSaveNG_2.SetPosition(X(250.0f), Y(170.0f)); + MenuSaveNG_2.AddOption(TheText.Get("FEM_NO"), 0.0f, 0.0f, TriggerSave_BackToMainMenu, false, false); + MenuSaveNG_2.AddOption(TheText.Get("FEM_YES"), 0.0f, Y(20.0f), TriggerSave_NewGameSelectYes, false, false); + MenuSaveNG_2.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); + MenuSaveNG_2.m_defaultCancel = TriggerSave_BackToMainMenu; + MenuPage_SaveNewGame.AddMenu(&MenuSaveNG_2); + MenuPage_SaveNewGame.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuPage_SaveNewGame.ActivatePage(); + + /* Load Game */ + + MenuSaveLG_1.m_numTexts = 0; + MenuSaveLG_1.SetPosition(X(220.0f), Y(110.0f)); + MenuSaveLG_1.AddText(TheText.Get("FES_LGA"), 0.0f, Y(20.0f), SELECTED_TEXT_COLOR, true); + MenuSaveLG_1.AddText(TheText.Get("FES_DGA"), 0.0f, Y(40.0f), TEXT_COLOR, true); + MenuSaveLG_1.AddText(TheText.Get("FES_NGA"), 0.0f, Y(60.0f), TEXT_COLOR, true); + MenuPage_SaveLoadGame.AddMenu(&MenuSaveLG_1); + MenuSaveLG_2.m_numOptions = 0; + MenuSaveLG_2.SetPosition(X(250.0f), Y(60.0f)); + MenuSaveLG_2.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); + MenuSaveLG_2.m_defaultCancel = TriggerSave_BackToMainMenuTwoLines; + MenuSaveLG_2.SetNewOldTextScale(true, saveGameTextScale, defaultTextScale, false); + MenuPage_SaveLoadGame.AddMenu(&MenuSaveLG_2); + MenuPage_SaveLoadGame.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuPage_SaveLoadGame.ActivatePage(); + + /* Delete Game */ + + MenuSaveDG_1.m_numTexts = 0; + MenuSaveDG_1.SetPosition(X(220.0f), Y(110.0f)); + MenuSaveDG_1.AddText(TheText.Get("FES_LGA"), 0.0f, Y(20.0f), TEXT_COLOR, true); + MenuSaveDG_1.AddText(TheText.Get("FES_DGA"), 0.0f, Y(40.0f), SELECTED_TEXT_COLOR, true); + MenuSaveDG_1.AddText(TheText.Get("FES_NGA"), 0.0f, Y(60.0f), TEXT_COLOR, true); + MenuPage_SaveDeleteGame.AddMenu(&MenuSaveDG_1); + MenuSaveDG_2.m_numOptions = 0; + MenuSaveDG_2.SetPosition(X(250.0f), Y(60.0f)); + MenuSaveDG_2.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); + MenuSaveDG_2.m_defaultCancel = TriggerSave_BackToMainMenuTwoLines; + MenuSaveDG_2.SetNewOldTextScale(true, saveGameTextScale, defaultTextScale, false); + MenuPage_SaveDeleteGame.AddMenu(&MenuSaveDG_2); + MenuPage_SaveDeleteGame.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuPage_SaveDeleteGame.ActivatePage(); + + + CVector2D briefsTextScale(X(0.525f), Y(0.7f)); + CVector2D defaultTextScale1(X(MENU_TEXT_SIZE_X), Y(MENU_TEXT_SIZE_Y)); + + /* Briefs */ + + MenuBriefs_1.m_numTexts = 0; + MenuBriefs_1.SetPosition(X(60.0f), Y(60.0f)); + MenuBriefs_1.SetTextsColor(TEXT_COLOR); + MenuBriefs_1.SetNewOldTextScale(true, briefsTextScale, defaultTextScale1); + MenuBriefs_1.SetNewOldShadowWrapX(true, X(600.0f+SHADOW_VECTOR.x), X(600.0f)); + MenuPage_Briefs.AddMenu(&MenuBriefs_1); + MenuPage_Briefs.AddMenu(&MenuBriefs_2); + MenuPage_Briefs.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuPage_Briefs.ActivatePage(); + + + CVector2D defaultTextScale2(X(MENU_TEXT_SIZE_X), Y(MENU_TEXT_SIZE_Y)); + CVector2D defaultTextScale3(X(MENU_TEXT_SIZE_X), Y(MENU_TEXT_SIZE_Y)); + CVector2D CONTR_DESCR_NEW_TEXTSCALE_scaled(X(CONTR_DESCR_NEW_TEXTSCALE.x), Y(CONTR_DESCR_NEW_TEXTSCALE.y)); + CVector2D CONFIGS_NEW_TEXTSCALE_scaled(X(CONFIGS_NEW_TEXTSCALE.x), Y(CONFIGS_NEW_TEXTSCALE.y)); + + /* Controls */ + + MenuControls_3.m_numTexts = 0; + MenuControls_3.m_numSprites = 0; + MenuControls_3.SetPosition(X(170.0f), Y(88.0f)); + MenuControls_3.AddPicture(&m_sprites[FE_CONTROLLER], + &m_sprites[FE_CONTROLLERSH], + 0.0f, 0.0f, X(235.2f), Y(175.2), CRGBA(255, 255, 255, 255)); + MenuControls_3.AddPicture(&m_sprites[FE_ARROWS1], + 0.0f, 0.0f, X(235.2f), Y(175.2), CRGBA(255, 255, 255, 255)); + MenuControls_3.SetNewOldTextScale(true, CONTR_DESCR_NEW_TEXTSCALE_scaled, defaultTextScale2); + InitialiseTextsInMenuControllerOnFoot(&MenuControls_3, CMenuManager::m_PrefsControllerConfig); + MenuControls_3.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuControls_3.SetNewOldShadowWrapX(true, X(600.0f+SHADOW_VECTOR.x), X(600.0f)); + MenuPage_Controls.AddMenu(&MenuControls_3); + + MenuControls_6.m_numTexts = 0; + MenuControls_6.m_numSprites = 0; + MenuControls_6.SetPosition(X(170.0f), Y(88.0f)); + MenuControls_6.AddPicture(&m_sprites[FE_CONTROLLER], + &m_sprites[FE_CONTROLLERSH], + 0.0f, 0.0f, X(235.2f), Y(175.2), CRGBA(255, 255, 255, 255)); + MenuControls_6.AddPicture(&m_sprites[FE_ARROWS3], + 0.0f, 0.0f, X(235.2f), Y(175.2), CRGBA(255, 255, 255, 255)); + MenuControls_6.SetNewOldTextScale(true, CONTR_DESCR_NEW_TEXTSCALE_scaled, defaultTextScale2); + InitialiseTextsInMenuControllerOnFoot(&MenuControls_6, CMenuManager::CONFIG_2); + MenuControls_6.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuControls_6.SetNewOldShadowWrapX(true, X(600.0f+SHADOW_VECTOR.x), X(600.0f)); + + MenuControls_4.m_numTexts = 0; + MenuControls_4.m_numSprites = 0; + MenuControls_4.SetPosition(X(170.0f), Y(88.0f)); + MenuControls_4.AddPicture(&m_sprites[FE_CONTROLLER], + &m_sprites[FE_CONTROLLERSH], + 0.0f, 0.0f, X(235.2f), Y(175.2), CRGBA(255, 255, 255, 255)); + MenuControls_4.AddPicture(&m_sprites[FE_ARROWS2], + 0.0f, 0.0f, X(235.2f), Y(175.2), CRGBA(255, 255, 255, 255)); + MenuControls_4.SetNewOldTextScale(true, CONTR_DESCR_NEW_TEXTSCALE_scaled, defaultTextScale2); + InitialiseTextsInMenuControllerInCar(&MenuControls_4, CMenuManager::m_PrefsControllerConfig); + MenuControls_4.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuControls_4.SetNewOldShadowWrapX(true, X(600.0f+SHADOW_VECTOR.x), X(600.0f)); + + MenuControls_7.m_numTexts = 0; + MenuControls_7.m_numSprites = 0; + MenuControls_7.SetPosition(X(170.0f), Y(88.0f)); + MenuControls_7.AddPicture(&m_sprites[FE_CONTROLLER], + &m_sprites[FE_CONTROLLERSH], + 0.0f, 0.0f, X(235.2f), Y(175.2), CRGBA(255, 255, 255, 255)); + MenuControls_7.AddPicture(&m_sprites[FE_ARROWS4], + 0.0f, 0.0f, X(235.2f), Y(175.2), CRGBA(255, 255, 255, 255)); + MenuControls_7.SetNewOldTextScale(true, CONTR_DESCR_NEW_TEXTSCALE_scaled, defaultTextScale2); + InitialiseTextsInMenuControllerInCar(&MenuControls_7, CMenuManager::CONFIG_2); + MenuControls_7.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuControls_7.SetNewOldShadowWrapX(true, X(600.0f+SHADOW_VECTOR.x), X(600.0f)); + + MenuControls_1.m_numOptions = 0; + MenuControls_1.SetPosition(X(284.0f), Y(290.0f)); + MenuControls_1.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, SELECTED_TEXT_COLOR); + MenuControls_1.SetNewOldTextScale(true, CONFIGS_NEW_TEXTSCALE_scaled, defaultTextScale3, false); + MenuControls_1.AddTitle(TheText.Get("FEC_CCF"), 0.0f, 0.0f, true); + MenuControls_1.AddOption(TheText.Get("FEC_CF1"), X(15.0f), Y(2.0f), TriggerControls_ContrConfig, false, false); + MenuControls_1.AddOption(TheText.Get("FEC_CF2"), X(85.0f), Y(2.0f), TriggerControls_ContrConfig, false, false); + MenuControls_1.AddOption(TheText.Get("FEC_CF3"), X(155.0f), Y(2.0f), TriggerControls_ContrConfig, false, false); + MenuControls_1.AddOption(TheText.Get("FEC_CF4"), X(225.0f), Y(2.0f), TriggerControls_ContrConfig, false, false); + MenuPage_Controls.AddMenu(&MenuControls_1); + MenuControls_1.m_alwaysTrigger = (CMenuMultiChoiceTriggered::Trigger)TriggerControls_DrawContrConfig; + MenuControls_1.m_alwaysHighlightTrigger = (CMenuMultiChoiceTriggered::Trigger)TriggerControls_DrawHNContrConfig; + MenuControls_1.m_alwaysNormalTrigger = (CMenuMultiChoiceTriggered::Trigger)TriggerControls_DrawHNContrConfig; + + MenuControls_2.m_numOptions = 0; + MenuControls_2.SetPosition(X(284.0f), Y(310.0f)); + MenuControls_2.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, SELECTED_TEXT_COLOR); + MenuControls_2.SetNewOldTextScale(true, CONFIGS_NEW_TEXTSCALE_scaled, defaultTextScale3, false); + MenuControls_2.AddTitle(TheText.Get("FEC_CDP"), 0.0f, 0.0f, true); + MenuControls_2.AddOption(TheText.Get("FEC_ONF"), X(15.0f), Y(2.0f), (CMenuMultiChoiceTriggered::Trigger)TriggerControls_ContrDisplay, false, false); + MenuControls_2.AddOption(TheText.Get("FEC_INC"), X(105.0f), Y(2.0f), (CMenuMultiChoiceTriggered::Trigger)TriggerControls_ContrDisplay, false, false); + MenuPage_Controls.AddMenu(&MenuControls_2); + MenuControls_2.m_bTwoState = true; + MenuControls_2.SetMenuSelection(0); + + MenuControls_5.SetPosition(X(284.0f), Y(330.0f)); + MenuControls_5.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR); + MenuControls_5.SetNewOldTextScale(true, CONFIGS_NEW_TEXTSCALE_scaled, defaultTextScale3, false); + MenuControls_5.AddTitle(TheText.Get("FEC_VIB"), false, 0.0f, 0.0f, true); + MenuControls_5.SetOptionPosition(X(15.0f), Y(2.0f), TriggerControls_Vibrations, false); + MenuPage_Controls.AddMenu(&MenuControls_5); + MenuPage_Controls.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuPage_Controls.ActivatePage(); + + + /* Audio */ + + CVector2D audioOutputScale(X(0.49f), Y(0.63f)); + CVector2D defaultTextScale4(X(MENU_TEXT_SIZE_X), Y(MENU_TEXT_SIZE_Y)); + + FEC_MOVETAB movetab; + MenuAudio_1.SetPosition(X(70.0f), Y(80.0f)); + MenuAudio_1.SetColors(TEXT_COLOR, TEXT_COLOR, SLIDER_LEFT_COLOR, SLIDER_RIGHT_COLOR); + MenuAudio_1.AddTitle(TheText.Get("FEA_MUS"), 0.0f, 0.0f); + MenuAudio_1.AddTickBox(X(15.0f), Y(20.0f), X(150.0f), Y(5.0f), Y(45.0f), TriggerAudio_MusicVolume, TriggerAudio_MusicVolumeAlways); + movetab.right = 1; + movetab.left = 2; + movetab.down = 3; + movetab.up = 3; + MenuPage_Audio.AddMenu(&MenuAudio_1, &movetab); + + MenuAudio_4.m_numOptions = 0; + MenuAudio_4.SetPosition(X(280.0f), Y(80.0f)); + MenuAudio_4.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, SELECTED_TEXT_COLOR); + MenuAudio_4.SetNewOldTextScale(true, audioOutputScale, defaultTextScale4, false); + MenuAudio_4.AddTitle(TheText.Get("FEA_OUT"), X(AUDIO_OUTPUT_POS.x), Y(AUDIO_OUTPUT_POS.y), false); + MenuAudio_4.AddOption(TheText.Get("FEA_ST"), X(-15.0f), Y(30.0f), TriggerAudio_StereoMono, false, false); + MenuAudio_4.AddOption(TheText.Get("FEA_MNO"), X(55.0f), Y(30.0f), TriggerAudio_StereoMono, false, false); + movetab.right = 2; + movetab.left = 0; + movetab.down = 3; + movetab.up = 3; + MenuPage_Audio.AddMenu(&MenuAudio_4, &movetab); + MenuAudio_4.m_bTwoState = true; + + MenuAudio_2.SetPosition(X(410.0f), Y(80.0f)); + MenuAudio_2.SetColors(TEXT_COLOR, TEXT_COLOR, SLIDER_LEFT_COLOR, SLIDER_RIGHT_COLOR); + MenuAudio_2.AddTitle(TheText.Get("FEA_SFX"), 0.0f, 0.0f); + MenuAudio_2.AddTickBox(X(5.0f), Y(20.0f), X(150.0f), Y(5.0f), Y(45.0f), TriggerAudio_SfxVolume, TriggerAudio_SfxVolumeAlways); + movetab.right = 0; + movetab.left = 1; + movetab.down = 3; + movetab.up = 3; + MenuPage_Audio.AddMenu(&MenuAudio_2, &movetab); + + MenuAudio_3.m_numOptions = 0; + MenuAudio_3.SetPosition(X(50.0f), Y(170.0f)); + MenuAudio_3.SetColors(TITLE_TEXT_COLOR, CRGBA(64, 64, 64, 255), CRGBA(250, 250, 250, 255)); + MenuAudio_3.AddTitle(TheText.Get("FEA_RSS"), X(AUDIO_RSTATION_POS.x), Y(AUDIO_RSTATION_POS.y), false); + // first row + movetab.right = 1; + movetab.left = 4; + movetab.down = 5; + movetab.up = 5; + MenuAudio_3.AddOption(&m_sprites[FE_RADIO1], &movetab, 0.0f, Y(18.0f), + CVector2D(X(96.0f), YF(72.0f)), TriggerAudio_RadioStation, false); + movetab.right = 2; + movetab.left = 0; + movetab.down = 6; + movetab.up = 6; + MenuAudio_3.AddOption(&m_sprites[FE_RADIO2], &movetab, X(106.0f), Y(20.0f), + CVector2D(X(79.2f), YF(81.0f)), TriggerAudio_RadioStation, false); + movetab.right = 3; + movetab.left = 1; + movetab.down = 7; + movetab.up = 7; + MenuAudio_3.AddOption(&m_sprites[FE_RADIO5], &movetab, X(210.0f), Y(20.0f), + CVector2D(X(86.4f), YF(72.0f)), TriggerAudio_RadioStation, false); + movetab.right = 4; + movetab.left = 2; + movetab.down = 8; + movetab.up = 8; + MenuAudio_3.AddOption(&m_sprites[FE_RADIO7], &movetab, X(324.0f), Y(5.0f), + CVector2D(X(115.2f), YF(102.0f)), TriggerAudio_RadioStation, false); + movetab.right = 0; + movetab.left = 3; + movetab.down = 8; + movetab.up = 8; + MenuAudio_3.AddOption(&m_sprites[FE_RADIO8], &movetab, X(446.0f), Y(5.0f), + CVector2D(X(102.96f), YF(101.4f)), TriggerAudio_RadioStation, false); + // second row + movetab.right = 6; + movetab.left = 8; + movetab.down = 0; + movetab.up = 0; + MenuAudio_3.AddOption(&m_sprites[FE_RADIO3], &movetab, X(60.0f), Y(96.0f), + CVector2D(X(87.36f), YF(85.8f)), TriggerAudio_RadioStation, false); + movetab.right = 7; + movetab.left = 5; + movetab.down = 1; + movetab.up = 1; + MenuAudio_3.AddOption(&m_sprites[FE_RADIO4], &movetab, X(130.0f), Y(72.0f), + CVector2D(X(129.6f), YF(129.0f)), TriggerAudio_RadioStation, false); + movetab.right = 8; + movetab.left = 6; + movetab.down = 2; + movetab.up = 2; + MenuAudio_3.AddOption(&m_sprites[FE_RADIO6], &movetab, X(284.0f), Y(108.0f), + CVector2D(X(60.0f), YF(60.0f)), TriggerAudio_RadioStation, false); + movetab.right = 5; + movetab.left = 7; + movetab.down = 3; + movetab.up = 3; + MenuAudio_3.AddOption(&m_sprites[FE_RADIO9], &movetab, X(404.0f), Y(85.0f), + CVector2D(X(81.12f), YF(101.4f)), TriggerAudio_RadioStation, false); + movetab.right = 2; + movetab.left = 0; + movetab.down = 1; + movetab.up = 1; + MenuPage_Audio.AddMenu(&MenuAudio_3, &movetab); + MenuPage_Audio.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuPage_Audio.ActivatePage(); + + + /* Display */ + + MenuDisplay_1.SetPosition(X(240.0f), Y(140.0f)); + MenuDisplay_1.SetColors(TEXT_COLOR, TEXT_COLOR, SLIDER_LEFT_COLOR, SLIDER_RIGHT_COLOR); + MenuDisplay_1.m_style = 0; // ticks + MenuDisplay_1.AddTitle(TheText.Get("FED_BRI"), X(DISPLAY_BRIGHTNESS_POS.x), Y(DISPLAY_BRIGHTNESS_POS.y)); + MenuDisplay_1.AddTickBox(X(-30.0f), Y(20.0f), X(200.0f), Y(40.0f), Y(40.0f)); + MenuPage_Display.AddMenu(&MenuDisplay_1); + MenuDisplay_2.SetPosition(X(290.0f), Y(240.0f)); + MenuDisplay_2.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR); + MenuDisplay_2.AddTitle(TheText.Get("FED_TRA"), false, 0.0f, 0.0f, true); +#ifdef GTA_PC + MenuDisplay_2.SetOptionPosition(X(40.0f), 0.0f, TriggerDisplay_Trails, false); +#else + MenuDisplay_2.SetOptionPosition(X(40.0f), 0.0f, false); +#endif + MenuDisplay_2.m_bTwoState = true; + MenuPage_Display.AddMenu(&MenuDisplay_2); + MenuDisplay_3.SetPosition(X(290.0f), Y(260.0f)); + MenuDisplay_3.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR); + MenuDisplay_3.AddTitle(TheText.Get("FED_SUB"), false, 0.0f, 0.0f, true); + MenuDisplay_3.SetOptionPosition(X(40.0f), 0.0f, false); + MenuDisplay_3.m_bTwoState = true; + MenuPage_Display.AddMenu(&MenuDisplay_3); + MenuDisplay_4.SetPosition(X(290.0f), Y(280.0f)); + MenuDisplay_4.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR); + MenuDisplay_4.AddTitle(TheText.Get("FED_WIS"), false, 0.0f, 0.0f, true); + MenuDisplay_4.SetOptionPosition(X(40.0f), 0.0f, false); + MenuDisplay_4.m_bTwoState = true; + MenuPage_Display.AddMenu(&MenuDisplay_4); + MenuPage_Display.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuPage_Display.ActivatePage(); + + + /* Language */ + MenuLanguage_1.m_numOptions = 0; + MenuLanguage_1.SetPosition(X(288.0f), Y(160.0f)); + MenuLanguage_1.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, SELECTED_TEXT_COLOR); + MenuLanguage_1.AddOption(TheText.Get("FEL_ENG"), 0.0f, 0.0f, TriggerLanguage_Language, false, false); + MenuLanguage_1.AddOption(TheText.Get("FEL_FRE"), 0.0f, Y(20.0f), TriggerLanguage_Language, false, false); + MenuLanguage_1.AddOption(TheText.Get("FEL_GER"), 0.0f, Y(40.0f), TriggerLanguage_Language, false, false); + MenuLanguage_1.AddOption(TheText.Get("FEL_ITA"), 0.0f, Y(60.0f), TriggerLanguage_Language, false, false); + MenuLanguage_1.AddOption(TheText.Get("FEL_SPA"), 0.0f, Y(80.0f), TriggerLanguage_Language, false, false); + MenuPage_Language.AddMenu(&MenuLanguage_1); + MenuPage_Language.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuPage_Language.ActivatePage(); + + + /* + * Save zone menu + */ + + CVector2D saveGameTextScale2(X(0.49f), Y(0.7f)); + CVector2D defaultTextScale5(X(MENU_TEXT_SIZE_X), Y(MENU_TEXT_SIZE_Y)); + + /* Save game */ + + MenuSaveZoneSG_1.m_numOptions = 0; + MenuSaveZoneSG_1.SetPosition(X(200.0f), Y(100.0f)); + MenuSaveZoneSG_1.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); + MenuSaveZoneSG_1.AddOption(TheText.Get("FESZ_SA"), 0.0f, Y(20.0f), TriggerSaveZone_SaveGameSelect, false, false); + MenuSaveZoneSG_1.AddOption(TheText.Get("FESZ_CA"), 0.0f, Y(40.0f), TriggerSaveZone_QuitMenu, false, false); + MenuSaveZoneSG_1.m_defaultCancel = TriggerSaveZone_QuitMenu; + MenuPageSaveZone_SaveGame.AddMenu(&MenuSaveZoneSG_1); + MenuSaveZoneSG_1.SetMenuSelection(1); + MenuPageSaveZone_SaveGame.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuPageSaveZone_SaveGame.ActivatePage(); + + /* Select slot */ + + MenuSaveZoneSSL_1.m_numOptions = 0; + MenuSaveZoneSSL_1.SetPosition(X(160.0f), Y(100.0f)); + MenuSaveZoneSSL_1.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); + MenuSaveZoneSSL_1.AddOption(TheText.Get("FESZ_CA"), 0.0f, 0.0f, TriggerSaveZone_BackToMainMenuTwoLines, false, false); + MenuSaveZoneSSL_1.SetNewOldTextScale(true, saveGameTextScale2, defaultTextScale5, true); + MenuPageSaveZone_SaveSlots.AddMenu(&MenuSaveZoneSSL_1); + MenuSaveZoneSSL_1.SetMenuSelection(0); + MenuPageSaveZone_SaveSlots.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuPageSaveZone_SaveSlots.ActivatePage(); + + /* Save successful */ + + MenuSaveZoneSS_1.m_numTexts = 0; + MenuSaveZoneSS_1.SetPosition(X(200.0f), Y(100.0f)); + MenuSaveZoneSS_1.AddText(TheText.Get("FESZ_L1"), X(-40.0f), 0.0f, TITLE_TEXT_COLOR, false); + MenuSaveZoneSS_1.AddText(TheText.Get("FESZ_L2"), X(-40.0f), Y(20.0f), TITLE_TEXT_COLOR, false); + // twice this line? + MenuSaveZoneSS_1.AddText(TheText.Get("FESZ_L2"), X(-40.0f), Y(40.0f), TEXT_COLOR, false); + MenuPageSaveZone_SavedSuccessfully.AddMenu(&MenuSaveZoneSS_1); + MenuSaveZoneSS_2.m_numOptions = 0; + MenuSaveZoneSS_2.SetPosition(X(200.0f), Y(170.0f)); + MenuSaveZoneSS_2.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); + MenuSaveZoneSS_2.AddOption(TheText.Get("FESZ_QU"), X(60.0f), 0.0f, TriggerSaveZone_QuitMenu, false, false); + MenuPageSaveZone_SavedSuccessfully.AddMenu(&MenuSaveZoneSS_2); + MenuPageSaveZone_SavedSuccessfully.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuPageSaveZone_SavedSuccessfully.ActivatePage(); + + + MenuSaveZoneMSG_1.m_numTexts = 0; + MenuSaveZoneMSG_1.SetPosition(X(170.0f), Y(130.0f)); + MenuSaveZoneMSG_1.AddText(TheText.Get("FESZ_SR"), X(-40.0f), 0.0f, TEXT_COLOR, false); + MenuSaveZoneMSG_1.SetTextsColor(TEXT_COLOR); + MenuSaveZoneMSG_1.SetNewOldShadowWrapX(true, X(600.0f+SHADOW_VECTOR.x-20.0f), X(580.0f)); + MenuPageSaveZone_Message.AddMenu(&MenuSaveZoneMSG_1); + MenuSaveZoneMSG_2.m_numOptions = 0; + MenuSaveZoneMSG_2.SetPosition(X(170.0f), Y(180.0f)); + MenuSaveZoneMSG_2.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); + MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(40.0f), 0.0f, TriggerSaveZone_QuitMenu, false, false); + MenuPageSaveZone_Message.AddMenu(&MenuSaveZoneMSG_2); + MenuPageSaveZone_Message.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuPageSaveZone_Message.ActivatePage(); + + + MenuSaveZoneQYN_1.m_numTexts = 0; + MenuSaveZoneQYN_1.SetPosition(X(170.0f), Y(130.0f)); + MenuSaveZoneQYN_1.AddText(TheText.Get("FESZ_SR"), X(-40.0f), 0.0f, TEXT_COLOR, false); + MenuSaveZoneQYN_1.SetTextsColor(TEXT_COLOR); + MenuSaveZoneQYN_1.SetNewOldShadowWrapX(true, X(600.0f+SHADOW_VECTOR.x-20.0f), X(580.0f)); + MenuPageSaveZone_QuestionYesNo.AddMenu(&MenuSaveZoneQYN_1); + MenuSaveZoneQYN_2.m_numOptions = 0; + MenuSaveZoneQYN_2.SetPosition(X(170.0f), Y(180.0f)); + MenuSaveZoneQYN_2.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); + MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_YES"), X(80.0f), 0.0f, TriggerSaveZone_QuitMenu, false, false); + MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_NO"), X(80.0f), Y(20.0f), TriggerSaveZone_QuitMenu, false, false); + MenuPageSaveZone_QuestionYesNo.AddMenu(&MenuSaveZoneQYN_2); + MenuPageSaveZone_QuestionYesNo.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuPageSaveZone_QuestionYesNo.ActivatePage(); + + /* Format card */ + + MenuSaveZoneFC_1.m_numOptions = 0; + MenuSaveZoneFC_1.SetPosition(X(200.0f), Y(100.0f)); + MenuSaveZoneFC_1.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); + MenuSaveZoneFC_1.AddTitle(TheText.Get("FESZ_FM"), X(-100.0f), 0.0f, false); + MenuSaveZoneFC_1.AddOption(TheText.Get("FEM_NO"), X(40.0f), Y(95.0f), TriggerSaveZone_BackToMainMenu, false, false); + MenuSaveZoneFC_1.AddOption(TheText.Get("FEM_YES"), X(40.0f), Y(75.0f), TriggerSaveZone_FormatCardSelect, false, false); + MenuSaveZoneFC_1.m_defaultCancel = TriggerSaveZone_FormatCardSelect; + MenuPageSaveZone_FormatCard.AddMenu(&MenuSaveZoneFC_1); + MenuPageSaveZone_FormatCard.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuPageSaveZone_FormatCard.ActivatePage(); + + /* Format error */ + + MenuSaveZoneEF_1.m_numOptions = 0; + MenuSaveZoneEF_1.SetPosition(X(200.0f), Y(100.0f)); + MenuSaveZoneEF_1.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); + MenuSaveZoneEF_1.AddTitle(TheText.Get("FESZ_FF"), X(-40.0f), 0.0f, false); + MenuSaveZoneEF_1.AddOption(TheText.Get("FESZ_OK"), X(70.0f), Y(20.0f), TriggerSaveZone_FormatFailedOK, false, false); + MenuPageSaveZone_ErrorFormat.AddMenu(&MenuSaveZoneEF_1); + MenuPageSaveZone_ErrorFormat.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + MenuPageSaveZone_ErrorFormat.ActivatePage(); + + pActiveMenuPage = &MenuPage_Stats; + pActiveMenuPage->ActivatePage(); + + InitialiseMenuContents(); + + m_bWantToUpdateContent = false; +} + +void +CMenuManager::InitialiseChangedLanguageSettings(void) +{ + if ( bFrontEnd_ReloadObrTxtGxt ) + { + bFrontEnd_ReloadObrTxtGxt = false; + + CTimer::Stop(); + TheText.Unload(); + TheText.Load(); + CTimer::Update(); + + FrontEndMenuManager.AnaliseMenuContents(); + CGame::frenchGame = false; + CGame::germanGame = false; + if ( m_PrefsAllowNastyGame ) + CGame::nastyGame = true; + + for ( int32 i = 0; i < NUM_PAGES; i++ ) + { + BUTTONTAB_TEXT_X_SCALES[i] = 1.0f; + PANEL_TEXT_X_SCALES[i] = 1.0f; + } + + switch ( m_PrefsLanguage ) + { + case LANGUAGE_AMERICAN: + { + MENU_TEXT_SIZE_X = 0.644f; + MENU_TEXT_SIZE_Y = 0.84f;//0.96f; + + BUTTONTAB_TEXT_SIZE_X = 0.35f; + BUTTONTAB_TEXT_SIZE_Y = 0.7f;//0.8f; + + BUTTONTAB_TEXT_X_SCALES[6] = 0.94f; + + CONTR_DESCR_NEW_TEXTSCALE.x = 0.4564f; + CONTR_DESCR_NEW_TEXTSCALE.y = 0.63f;//0.72f; + + CONFIGS_NEW_TEXTSCALE.x = 0.49f; + CONFIGS_NEW_TEXTSCALE.y = 0.7f;//0.8f; + + AUDIO_OUTPUT_POS.x = 0.0f; + AUDIO_OUTPUT_POS.y = 0.0f; + + AUDIO_RSTATION_POS.x = 154.0f; + AUDIO_RSTATION_POS.y = 0.0f; + + DISPLAY_BRIGHTNESS_POS.x = 0.0f; + DISPLAY_BRIGHTNESS_POS.y = 0.0f; + + MEMCARD_ACCESS_MSG_SIZE_X = 0.84f; + MEMCARD_ACCESS_MSG_SIZE_Y = 1.12f;//1.28f; + + break; + } + + case LANGUAGE_FRENCH: + { + CGame::frenchGame = true; + if ( m_PrefsAllowNastyGame ) + CGame::nastyGame = false; + + MENU_TEXT_SIZE_X = 0.504f; + MENU_TEXT_SIZE_Y = 0.84f;//0.96f; + + BUTTONTAB_TEXT_SIZE_X = 0.32f; + BUTTONTAB_TEXT_SIZE_Y = 0.7f;//0.8f; + + BUTTONTAB_TEXT_X_SCALES[0] = 0.84f; + BUTTONTAB_TEXT_X_SCALES[3] = 0.84f; + PANEL_TEXT_X_SCALES[1] = 0.8f; + + CONTR_DESCR_NEW_TEXTSCALE.x = 0.385f; + CONTR_DESCR_NEW_TEXTSCALE.y = 0.63f;//0.72f; + + CONFIGS_NEW_TEXTSCALE.x = 0.455f; + CONFIGS_NEW_TEXTSCALE.y = 0.7f;//0.8f; + + AUDIO_OUTPUT_POS.x = -15.0f; + AUDIO_OUTPUT_POS.y = 0.0f; + + AUDIO_RSTATION_POS.x = 184.0f; + AUDIO_RSTATION_POS.y = 0.0f; + + DISPLAY_BRIGHTNESS_POS.x = 20.0f; + DISPLAY_BRIGHTNESS_POS.y = 0.0f; + + MEMCARD_ACCESS_MSG_SIZE_X = 0.84f; + MEMCARD_ACCESS_MSG_SIZE_Y = 1.12f;//1.28f; + + break; + } + + case LANGUAGE_GERMAN: + { + CGame::germanGame = true; + if ( m_PrefsAllowNastyGame ) + CGame::nastyGame = false; + + MENU_TEXT_SIZE_X = 0.546f; + MENU_TEXT_SIZE_Y = 0.84f;//0.96f; + + BUTTONTAB_TEXT_SIZE_X = 0.32f; + BUTTONTAB_TEXT_SIZE_Y = 0.7f;//0.8f; + + CONTR_DESCR_NEW_TEXTSCALE.x = 0.35f; + CONTR_DESCR_NEW_TEXTSCALE.y = 0.63f;//0.72f; + + CONFIGS_NEW_TEXTSCALE.x = 0.434f; + CONFIGS_NEW_TEXTSCALE.y = 0.7f;//0.8f; + + AUDIO_OUTPUT_POS.x = -15.0f; + AUDIO_OUTPUT_POS.y = 0.0f; + + AUDIO_RSTATION_POS.x = 154.0f; + AUDIO_RSTATION_POS.y = 0.0f; + + DISPLAY_BRIGHTNESS_POS.x = 20.0f; + DISPLAY_BRIGHTNESS_POS.y = 0.0f; + + MEMCARD_ACCESS_MSG_SIZE_X = 0.7f; + MEMCARD_ACCESS_MSG_SIZE_Y = 1.12f;//1.28f; + + break; + } + + case LANGUAGE_ITALIAN: + { + MENU_TEXT_SIZE_X = 0.574f; + MENU_TEXT_SIZE_Y = 0.84f;//0.96f; + + BUTTONTAB_TEXT_SIZE_X = 0.32f; + BUTTONTAB_TEXT_SIZE_Y = 0.7f;//0.8f; + + BUTTONTAB_TEXT_X_SCALES[0] = 0.86f; + PANEL_TEXT_X_SCALES[1] = 0.9f; + + CONTR_DESCR_NEW_TEXTSCALE.x = 0.385f; + CONTR_DESCR_NEW_TEXTSCALE.y = 0.63f;//0.72f; + + CONFIGS_NEW_TEXTSCALE.x = 0.42f; + CONFIGS_NEW_TEXTSCALE.y = 0.7f;//0.8f; + + AUDIO_OUTPUT_POS.x = 10.0f; + AUDIO_OUTPUT_POS.y = 0.0f; + + AUDIO_RSTATION_POS.x = 194.0f; + AUDIO_RSTATION_POS.y = 0.0f; + + DISPLAY_BRIGHTNESS_POS.x = 10.0f; + DISPLAY_BRIGHTNESS_POS.y = 0.0f; + + MEMCARD_ACCESS_MSG_SIZE_X = 0.84f; + MEMCARD_ACCESS_MSG_SIZE_Y = 1.12f;//1.28f; + + break; + } + + case LANGUAGE_SPANISH: + { + MENU_TEXT_SIZE_X = 0.546f; + MENU_TEXT_SIZE_Y = 0.84f;//0.96f; + + BUTTONTAB_TEXT_SIZE_X = 0.35f; + BUTTONTAB_TEXT_SIZE_Y = 0.7f;//0.8f; + + BUTTONTAB_TEXT_X_SCALES[0] = 0.78f; + PANEL_TEXT_X_SCALES[1] = 0.95f; + + CONTR_DESCR_NEW_TEXTSCALE.x = 0.364f; + CONTR_DESCR_NEW_TEXTSCALE.y = 0.63f;//0.72f; + + CONFIGS_NEW_TEXTSCALE.x = 0.455f; + CONFIGS_NEW_TEXTSCALE.y = 0.7f;//0.8f; + + AUDIO_OUTPUT_POS.x = 10.0f; + AUDIO_OUTPUT_POS.y = 0.0f; + + AUDIO_RSTATION_POS.x = 124.0f; + AUDIO_RSTATION_POS.y = 0.0f; + + DISPLAY_BRIGHTNESS_POS.x = 30.0f; + DISPLAY_BRIGHTNESS_POS.y = 0.0f; + + MEMCARD_ACCESS_MSG_SIZE_X = 0.84f; + MEMCARD_ACCESS_MSG_SIZE_Y = 1.12f;//1.28f; + + break; + } + } + } +} + +void +CMenuManager::InitialiseMenuContents(void) +{ + if ( m_bWantToUpdateContent == false ) + { + m_bWantToUpdateContent = true; + + m_pageState = PAGESTATE_NORMAL; + + switch ( CPad::GetPad(0)->GetMode() ) + { + case 3: m_PrefsControllerConfig = CONFIG_4; break; + case 2: m_PrefsControllerConfig = CONFIG_3; break; + case 1: m_PrefsControllerConfig = CONFIG_2; break; + case 0: m_PrefsControllerConfig = CONFIG_1; break; + } + + MenuControls_1.SetMenuSelection(m_PrefsControllerConfig); + MenuControls_5.SetMenuSelection(m_PrefsUseVibration); + + MenuAudio_1.SetMenuSelection(m_PrefsMusicVolume / 127.0f * 100.0f + 0.5f); + MenuAudio_2.SetMenuSelection(m_PrefsSfxVolume / 127.0f * 100.0f + 0.5f); + MenuAudio_3.SetMenuSelection(m_PrefsRadioStation); + MenuAudio_4.SetMenuSelection(m_PrefsStereoMono); + + MenuDisplay_1.SetMenuSelection(m_PrefsBrightness / 512.0f * 100.0f + 0.5f); +#ifdef PS2 + m_PrefsShowTrails = BlurOn; +#else + m_PrefsShowTrails = CMBlur::BlurOn; +#endif + MenuDisplay_2.SetMenuSelection(m_PrefsShowTrails); + MenuDisplay_3.SetMenuSelection(m_PrefsShowSubtitles); + MenuDisplay_4.SetMenuSelection(m_PrefsUseWideScreen); + + MenuLanguage_1.SetMenuSelection(m_PrefsLanguage); + + FillMenuWithMemCardFileListing(&MenuSaveLG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_LoadGameLoadGameSelect, nil, 0, 34, 22); + FillMenuWithMemCardFileListing(&MenuSaveDG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_DeleteGameDeleteGameSelect, nil, 0, 34, 22); + + MenuBriefs_1.m_numTexts = 0; + MenuBriefs_1.AddText(TheText.Get("FEB_PMB"), 0.0f, 0.0f, TITLE_TEXT_COLOR, 0); // Previous Mission Briefs: + + static wchar StringsToDisplay[NUMPREVIOUSBRIEFS][256]; + + CRGBA newColor; + int32 brierY = 36; + + for ( int32 i = NUMPREVIOUSBRIEFS-1; i >= 0; i-- ) + { + tPreviousBrief &brief = CMessages::PreviousBriefs[i]; + if (brief.m_pText) + { + CMessages::InsertNumberInString(brief.m_pText, + brief.m_nNumber[0], brief.m_nNumber[1], + brief.m_nNumber[2], brief.m_nNumber[3], + brief.m_nNumber[4], brief.m_nNumber[5], StringsToDisplay[i]); + CMessages::InsertStringInString(StringsToDisplay[i], brief.m_pString); + + newColor = TEXT_COLOR; + FilterOutColorMarkersFromString(StringsToDisplay[i], newColor); + + if (newColor != TEXT_COLOR) + { + newColor.r /= 2; + newColor.g /= 2; + newColor.b /= 2; + } + MenuBriefs_1.AddText(StringsToDisplay[i], 0.0f, YF((float)brierY), newColor, 0); + brierY += 54; + } + } + + MenuStats_1.m_scrollPosition = 0.0f; + MenuStats_1.ResetNumberOfTextLines(); + + nStatLinesIndex = 0; + + #define STAT_HEADER(str) do { MenuStats_1.AddTextLine(TheText.Get(str), nil); } while(0) + #define STAT_PARAM(str) do { MenuStats_1.AddTextLine(nil, TheText.Get(str)); } while(0) + #define STAT_LINE(str, left, isFloat, right) do { MenuStats_1.AddTextLine(TheText.Get(str), PrintStatLine(str, left, isFloat, right)); } while(0) + + int32 nTemp; + + STAT_HEADER("PL_STAT"); + + int32 percentCompleted = (CStats::TotalProgressInGame == 0 ? 0 : CStats::ProgressMade * 100.0f / (CGame::nastyGame ? CStats::TotalProgressInGame : CStats::TotalProgressInGame - 1)); + percentCompleted = Min(percentCompleted, 100); + + STAT_LINE("PER_COM", &percentCompleted, 0, nil); + + STAT_LINE("NMISON", &CStats::MissionsGiven, 0, nil); + + STAT_LINE("FEST_MP", &CStats::MissionsPassed, 0, &CStats::TotalNumberMissions); + + if ( CGame::nastyGame ) + STAT_LINE("FEST_RP", &CStats::NumberKillFrenziesPassed, 0, &CStats::TotalNumberKillFrenzies); + + CPlayerInfo &player = CWorld::Players[CWorld::PlayerInFocus]; + float packagesPercent = 0.0f; + if (player.m_nTotalPackages != 0) + packagesPercent = player.m_nCollectedPackages * 100.0f / player.m_nTotalPackages; + int32 nPackagesPercent = packagesPercent; + nTemp = 100; + + STAT_LINE("PERPIC", &nPackagesPercent, 0, &nTemp); + + STAT_LINE("NOUNIF", &CStats::NumberOfUniqueJumpsFound, 0, &CStats::TotalNumberOfUniqueJumps); + + STAT_LINE("DAYSPS", &CStats::DaysPassed, 0, nil); + + if ( CGame::nastyGame ) + { + STAT_LINE("PE_WAST", &CStats::PeopleKilledByPlayer, 0, nil); + STAT_LINE("PE_WSOT", &CStats::PeopleKilledByOthers, 0, nil); + } + + STAT_LINE("CAR_EXP", &CStats::CarsExploded, 0, nil); + + STAT_LINE("TM_BUST", &CStats::TimesArrested, 0, nil); + + STAT_LINE("TM_DED", &CStats::TimesDied, 0, nil); + + nTemp = CStats::PedsKilledOfThisType[PEDTYPE_GANG9] + CStats::PedsKilledOfThisType[PEDTYPE_GANG8] + + CStats::PedsKilledOfThisType[PEDTYPE_GANG7] + CStats::PedsKilledOfThisType[PEDTYPE_GANG6] + + CStats::PedsKilledOfThisType[PEDTYPE_GANG5] + CStats::PedsKilledOfThisType[PEDTYPE_GANG4] + + CStats::PedsKilledOfThisType[PEDTYPE_GANG3] + CStats::PedsKilledOfThisType[PEDTYPE_GANG2] + + CStats::PedsKilledOfThisType[PEDTYPE_GANG1]; + STAT_LINE("GNG_WST", &nTemp, 0, nil); + + nTemp = CStats::PedsKilledOfThisType[PEDTYPE_CRIMINAL]; + STAT_LINE("DED_CRI", &nTemp, 0, nil); + + STAT_LINE("HEL_DST", &CStats::HelisDestroyed, 0, nil); + + STAT_LINE("KGS_EXP", &CStats::KgsOfExplosivesUsed, 0, nil); + + if (CStats::LongestFlightInDodo > 0) + STAT_LINE("FEST_LF", &CStats::LongestFlightInDodo, 0, nil); + + STAT_LINE("CAR_CRU", &CStats::CarsCrushed, 0, nil); + + if (CStats::HighestScores[0] > 0) + { + STAT_HEADER("FEST_BB"); + STAT_LINE("FEST_H0", &CStats::HighestScores[0], 0, nil); + } + + int32 hs = 0; + for ( int32 i = 1; i < 5; i++ ) + hs += CStats::HighestScores[i]; + + if (hs > 0) + STAT_HEADER("FEST_GC"); + + if (CStats::HighestScores[1] > 0) + STAT_LINE("FEST_H1", &CStats::HighestScores[1], 0, nil); + + if (CStats::HighestScores[2] > 0) + STAT_LINE("FEST_H2", &CStats::HighestScores[2], 0, nil); + + if (CStats::HighestScores[3] > 0) + STAT_LINE("FEST_H3", &CStats::HighestScores[3], 0, nil); + + if (CStats::HighestScores[4] > 0) + STAT_LINE("FEST_H4", &CStats::HighestScores[4], 0, nil); + + STAT_LINE("FESTDFM", &CStats::DistanceTravelledOnFoot, 0, nil); + STAT_LINE("FESTDCM", &CStats::DistanceTravelledByCar, 0, nil); + STAT_LINE("DISTBIM", &CStats::DistanceTravelledByBike, 0, nil); + STAT_LINE("DISTBOM", &CStats::DistanceTravelledByBoat, 0, nil); + STAT_LINE("DISTGOM", &CStats::DistanceTravelledByGolfCart, 0, nil); + STAT_LINE("DISTHEM", &CStats::DistanceTravelledByHelicoptor, 0, nil); + STAT_LINE("MMRAIN", &CStats::mmRain, 0, nil); + nTemp = (int32)CStats::MaximumJumpDistance; + STAT_LINE("MXCARDM", &nTemp, 0, nil); + nTemp = (int32)CStats::MaximumJumpHeight; + STAT_LINE("MXCARJM", &nTemp, 0, nil); + + STAT_LINE("MXFLIP", &CStats::MaximumJumpFlips, 0, nil); + STAT_LINE("MXJUMP", &CStats::MaximumJumpSpins, 0, nil); + + STAT_HEADER("BSTSTU"); + + switch (CStats::BestStuntJump) + { + case 1: STAT_PARAM("INSTUN"); break; + case 2: STAT_PARAM("PRINST"); break; + case 3: STAT_PARAM("DBINST"); break; + case 4: STAT_PARAM("DBPINS"); break; + case 5: STAT_PARAM("TRINST"); break; + case 6: STAT_PARAM("PRTRST"); break; + case 7: STAT_PARAM("QUINST"); break; + case 8: STAT_PARAM("PQUINS"); break; + default: STAT_PARAM("NOSTUC"); break; + } + + STAT_LINE("PASDRO", &CStats::PassengersDroppedOffWithTaxi, 0, nil); + STAT_LINE("MONTAX", &CStats::MoneyMadeWithTaxi, 0, nil); + STAT_LINE("FEST_LS", &CStats::LivesSavedWithAmbulance, 0, nil); + STAT_LINE("FEST_HA", &CStats::HighestLevelAmbulanceMission, 0, nil); + STAT_LINE("FEST_CC", &CStats::CriminalsCaught, 0, nil); + STAT_LINE("FEST_FE", &CStats::FiresExtinguished, 0, nil); + int32 rnd = ((CGeneral::GetRandomNumber() & 255) + 100) * 2384; + STAT_LINE("DAYPLC", &rnd, 0, nil); + + #undef STAT_LINE + + MenuStats_2.m_numTexts = 0; + MenuStats_2.AddText(TheText.Get("CRIMRA"), 0.0f, 0.0f, CRIM_RATING_TEXT_COLOR, 0); + + char rating[16]; + wchar urating[16]; + sprintf(rating, " %d", CStats::FindCriminalRatingNumber()); + AsciiToUnicode(rating, urating); + + wchar *pStatLine = aStatLines[nStatLinesIndex++]; + UnicodeStrcpy(pStatLine, CStats::FindCriminalRatingString()); + UnicodeStrcat(pStatLine, urating); + + MenuStats_2.AddText(pStatLine, X(MenuStats_1.m_width), 0.0f, CRIM_RATING_TEXT_COLOR, 1); + + MenuSaveZoneSG_1.SetMenuSelection(1); + MenuSaveZoneFC_1.SetMenuSelection(1); + } +} + + +void +CMenuManager::AnaliseMenuContents(void) +{ + if ( m_bWantToUpdateContent ) + { + m_bWantToUpdateContent = false; + + m_PrefsControllerConfig = (CONTRCONFIG)MenuControls_1.GetMenuSelection(); + switch ( m_PrefsControllerConfig ) + { + case CONFIG_4: CPad::GetPad(0)->SetMode(3); break; + case CONFIG_3: CPad::GetPad(0)->SetMode(2); break; + case CONFIG_2: CPad::GetPad(0)->SetMode(1); break; + case CONFIG_1: CPad::GetPad(0)->SetMode(0); break; + } + + m_PrefsUseVibration = MenuControls_5.m_title.m_bSelected; + + m_PrefsMusicVolume = float(MenuAudio_1.GetMenuSelection())/100.0f*127.0f+0.5f; + m_PrefsSfxVolume = float(MenuAudio_2.GetMenuSelection())/100.0f*127.0f+0.5f; + m_PrefsRadioStation = MenuAudio_3.GetMenuSelection(); + m_PrefsStereoMono = MenuAudio_4.GetMenuSelection(); + m_PrefsBrightness = float(MenuDisplay_1.GetMenuSelection()) / 100.0f*512.0f + 0.5f; + m_PrefsShowTrails = MenuDisplay_2.GetMenuSelection(); + m_PrefsShowSubtitles = MenuDisplay_3.GetMenuSelection(); + m_PrefsUseWideScreen = MenuDisplay_4.GetMenuSelection(); +#ifdef PS2 + BlurOn = m_PrefsShowTrails; +#else + CMBlur::BlurOn = m_PrefsShowTrails; +#endif + + if ( m_PrefsLanguage != MenuLanguage_1.GetMenuSelection() ) + { + m_PrefsLanguage = MenuLanguage_1.GetMenuSelection(); + m_bInitialised = false; + bFrontEnd_ReloadObrTxtGxt = true; + } + } +} + +void +CMenuManager::InitialiseMenuContentsAfterLoadingGame(void) +{ + if ( MenuLanguage_1.GetMenuSelection() != m_PrefsLanguage ) + { + m_bInitialised = false; + bFrontEnd_ReloadObrTxtGxt = true; + } +} + +void +CMenuManager::DrawFrontEnd(void) +{ + CFont::SetAlphaFade(255.0f); + if(m_bInSaveZone) + DrawFrontEndSaveZone(); + else + DrawFrontEndNormal(); + + if ( MemCardAccessTriggerCaller.CanCall() ) + MemCardAccessTriggerCaller.CallTrigger(); + + DisplayWarningControllerMsg(); +} + +void +CMenuManager::DrawFrontEndNormal(void) +{ + CSprite2d::InitPerFrame(); + CFont::InitPerFrame(); + + if ( bMemoryCardSpecialZone ) + { + static uint8 counter = 0; + + counter++; + + if ( (counter & 63 ) == 0 ) + { + FillMenuWithMemCardFileListing(&MenuSaveLG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_LoadGameLoadGameSelect, nil, 0, 34, 22); + FillMenuWithMemCardFileListing(&MenuSaveDG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_DeleteGameDeleteGameSelect, nil, 0, 34, 22); + } + } + + m_fade = 255; + if ( m_nChangePageTimer != 0 && m_nChangePageTimer >= CTimer::GetTimeInMillisecondsPauseMode() ) + m_fade = uint32(float(m_nChangePageTimer - CTimer::GetTimeInMillisecondsPauseMode()) / 250.0f * 255.0f); + + m_someAlpha = 255; + + m_position.x = 0.0f; + m_position.y = 0.0f; + + if ( m_nStartPauseTimer != 0 && m_nStartPauseTimer >= CTimer::GetTimeInMillisecondsPauseMode() ) + { + float slide = float(m_nStartPauseTimer - CTimer::GetTimeInMillisecondsPauseMode()) / 800.0f; + float alpha = 1.0f; + + if ((m_nStartPauseTimer - CTimer::GetTimeInMillisecondsPauseMode()) <= 1600) + alpha = float(m_nStartPauseTimer - CTimer::GetTimeInMillisecondsPauseMode()) / 400.0f; + + m_someAlpha = 255 - Clamp(alpha, 0.0f, 1.0f) * 255.0f; + + switch ( m_nSlidingDir ) + { + case SLIDE_TO_RIGHT: m_position.x = slide * X(700.0f); break; + case SLIDE_TO_TOP: m_position.y = -(slide * Y(500.0f)); break; + case SLIDE_TO_LEFT: m_position.x = -(slide * X(700.0f)); break; + case SLIDE_TO_BOTTOM: m_position.y = slide * Y(500.0f); break; + default: m_position.y = slide * Y(500.0f); break; + } + } + + if ( m_nEndPauseTimer != 0 && m_nEndPauseTimer >= CTimer::GetTimeInMillisecondsPauseMode() ) + { + float slide = float(m_nEndPauseTimer - CTimer::GetTimeInMillisecondsPauseMode()) / 800.0f; + float alpha = float((int32)(m_nEndPauseTimer - CTimer::GetTimeInMillisecondsPauseMode()) + -266) / 533.0f; + + m_someAlpha = Clamp(alpha, 0.0f, 1.0f) * 255.0f; + + switch ( m_nSlidingDir ) + { + case SLIDE_TO_TOP: m_position.y = (1.0f - slide) * Y(500.0f); break; + case SLIDE_TO_RIGHT: m_position.x = (1.0f - slide) * X(700.0f); break; + case SLIDE_TO_LEFT: m_position.x = (1.0f - slide) * X(700.0f); break; + case SLIDE_TO_BOTTOM: m_position.y = -((1.0f - slide) * Y(500.0f)); break; + default: m_position.y = -((1.0f - slide) * Y(500.0f)); break; + } + } + + if ( m_someAlpha < 255 ) + m_fade = m_someAlpha; + + float posX, posY; + + /* Draw splash */ + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); + CSprite2d *splash = LoadSplash(nil); + if(splash) + splash->Draw(CRect(0.0f, 0.0f, SCRW, SCRH), BACKGROUND_SPLASH_COLOR); + else + // doesn't exist!! + CHud::Sprites[19].Draw(CRect(0.0f, 0.0f, SCRW, SCRH), BACKGROUND_SPLASH_COLOR); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERMIPNEAREST); + + /* Draw main panel */ + RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP); + CRGBA panelColor(255, 255, 255, m_someAlpha); + m_sprites[FE2_MAINPANEL_UL].Draw( + CRect(m_position.x, m_position.y, m_position.x+SCRW/2.0f, m_position.y+SCRH/2.0f), + panelColor); + m_sprites[FE2_MAINPANEL_UR].Draw( + CRect(m_position.x+SCRW/2.0f, m_position.y, m_position.x+SCRW, m_position.y+SCRH/2.0f), + panelColor); + m_sprites[FE2_MAINPANEL_DL].Draw( + CRect(m_position.x, m_position.y+SCRH/2.0f, m_position.x+SCRW/2.0f, m_position.y+SCRH), + panelColor); + m_sprites[FE2_MAINPANEL_DR].Draw( + CRect(m_position.x+SCRW/2.0f, m_position.y+SCRH/2.0f, m_position.x+SCRW, m_position.y+SCRH), + panelColor); + + /* Draw icon backdrop */ + CRGBA iconColor(255, 255, 255, m_fade*0.75f); + float iconX = 48.0f; + float iconY = 54.0f; + float iconWidth = 540.0f; + float iconHeight = 296.0f; + int32 sprite = FE_ICONBRIEF; + +#ifdef PS2_MENU_USEALLPAGEICONS + switch(m_currentPage) + { + case PAGE_STATS: + sprite = FE_ICONSTATS; + break; + case PAGE_LOAD: + sprite = FE_ICONSAVE; + break; + case PAGE_CONTROLS: + sprite = FE_ICONCONTROLS; + break; + case PAGE_BRIEFS: + sprite = FE_ICONBRIEF; + break; + case PAGE_AUDIO: + sprite = FE_ICONAUDIO; + break; + case PAGE_DISPLAY: + sprite = FE_ICONDISPLAY; + break; + case PAGE_LANGUAGE: + sprite = FE_ICONLANGUAGE; + break; + } +#else + switch(m_currentPage) + { + case PAGE_STATS: + case PAGE_LOAD: + case PAGE_CONTROLS: + sprite = FE_ICONSTATS; // PS2 has the same texture for stats and brief + //sprite = FE_ICONBRIEF; + break; + case PAGE_BRIEFS: + sprite = FE_ICONBRIEF; + break; + case PAGE_AUDIO: + sprite = FE_ICONAUDIO; + break; + case PAGE_DISPLAY: + sprite = FE_ICONDISPLAY; + break; + case PAGE_LANGUAGE: + sprite = FE_ICONLANGUAGE; + break; + } +#endif + m_sprites[sprite].Draw( + CRect_SZ(m_position.x+X(iconX), m_position.y+Y(iconY), X(iconWidth), Y(iconHeight)), + iconColor); + + /* Overwrite tab buttons if entered page */ + bool bOverwriteTab = false; + + switch ( m_pageState ) + { + case PAGESTATE_NORMAL: + case PAGESTATE_HIGHLIGHTED: + break; + + case PAGESTATE_SELECTED: + bOverwriteTab = true; + break; + } + + if( bOverwriteTab ) + { + CRGBA shadow(41, 101, 102, m_someAlpha); + CRGBA green(40, 48, 57, m_someAlpha); + CSprite2d::DrawRect( + CRect_SZ(m_position.x+X(82.0f), m_position.y+Y(408.0f), X(476.0f), Y(18.0f)), + shadow); + CSprite2d::DrawRect( + CRect_SZ(m_position.x+X(82.0f), m_position.y+Y(408.0f), X(476.0f), Y(5.0f)), + green); + } +// stats, load, briefs, controls, audio, display, language + + /* Shadow of panel on top of tab buttons */ + CRGBA panelShadow(96, 96, 96, m_someAlpha*0.375f); + CSprite2d::DrawRect(CRect_SZ(m_position.x+X(87.0f), m_position.y+Y(408.0f), X(464.0f), Y(3.0f)), panelShadow); + /* Draw second shadow - seems unused */ + if ( m_nChangePageTimer != 0 && CTimer::GetTimeInMillisecondsPauseMode() < m_nChangePageTimer ) + { + posX = 0.0f; + switch(field_18) + { + case PAGE_STATS: posX = 88.0f; break; + case PAGE_LOAD: posX = 286.0f; break; // actually controls + case PAGE_BRIEFS: posX = 154.0f; break; // actually load + case PAGE_CONTROLS: posX = 220.0f; break; // actually briefs + case PAGE_AUDIO: posX = 352.0f; break; + case PAGE_DISPLAY: posX = 418.0f; break; + case PAGE_LANGUAGE: posX = 484.0f; break; + } + CSprite2d::DrawRect(CRect_SZ(m_position.x+X(posX), m_position.y+Y(411.0f), X(65.0f), Y(3.0f)), panelShadow); + } + + /* Active tab */ + posX = 0.0f; + switch(m_currentPage) + { + case PAGE_STATS: posX = 88.0f; break; + case PAGE_LOAD: posX = 154.0f; break; + case PAGE_BRIEFS: posX = 220.0f; break; + case PAGE_CONTROLS: posX = 286.0f; break; + case PAGE_AUDIO: posX = 352.0f; break; + case PAGE_DISPLAY: posX = 418.0f; break; + case PAGE_LANGUAGE: posX = 484.0f; break; + } + // PAL has 465 for 407 here - and actually 406 seems right + m_sprites[FE2_TABACTIVE].Draw(CRect_SZ(m_position.x+X(posX), m_position.y+YF(465.0f), X(128.0f), Y(32.0f)), CRGBA(255, 255, 255, m_someAlpha)); + + /* Draw page title */ + posX = m_position.x + X(592.0f); + posY = m_position.y + Y(376.0f); + CRGBA fontCol1(255, 193, 71, m_someAlpha); + CRGBA fontCol2(0, 0, 0, m_someAlpha); + CFont::SetFontStyle(FONT_HEADING); + CFont::SetBackgroundOff(); + CFont::SetScale(X(PANEL_TEXT_SIZE_X), Y(PANEL_TEXT_SIZE_Y)); + CFont::SetPropOn(); + CFont::SetCentreOff(); + CFont::SetJustifyOn(); + CFont::SetRightJustifyWrap(0.0f); + CFont::SetRightJustifyOn(); + CFont::SetBackGroundOnlyTextOn(); + CFont::SetWrapx(SCRW-X(40.0f)); // 600.0f + const char *key = nil; + switch(m_currentPage) + { + case PAGE_STATS: key = "FEP_STA"; break; + case PAGE_LOAD: key = "FEP_SAV"; break; + case PAGE_BRIEFS: key = "FEP_BRI"; break; + case PAGE_CONTROLS: key = "FEP_CON"; break; + case PAGE_AUDIO: key = "FEP_AUD"; break; + case PAGE_DISPLAY: key = "FEP_DIS"; break; + case PAGE_LANGUAGE: key = "FEP_LAN"; break; + } + CFont::SetScale(X(PANEL_TEXT_SIZE_X*PANEL_TEXT_X_SCALES[m_currentPage]), Y(PANEL_TEXT_SIZE_Y)); + CFont::SetColor(fontCol1); + CFont::PrintString(posX, posY, TheText.Get(key)); + CFont::SetColor(fontCol2); + CFont::PrintString(posX-X(1.0f), posY-Y(1.0f), TheText.Get(key)); + CFont::DrawFonts(); + + /* Draw controller buttons */ + CFont::SetFontStyle(FONT_BANK); + CFont::SetBackgroundOff(); + CFont::SetScale(X(0.35f), Y(0.64f)); + CFont::SetPropOn(); + CFont::SetCentreOff(); + CFont::SetJustifyOn(); + CFont::SetRightJustifyOff(); + CFont::SetBackGroundOnlyTextOn(); + CFont::SetWrapx(SCRW-X(40.0f)); // 600.0f + CFont::SetColor(CRGBA(16, 16, 16, m_someAlpha)); + switch(m_currentPage) + { + case PAGE_STATS: + CFont::PrintString(m_position.x+X(52.0f), m_position.y+Y(360.0f), TheText.Get("FEDS_ST")); + CFont::PrintString(m_position.x+X(52.0f), m_position.y+Y(372.0f), TheText.Get("FEDS_AM")); + CFont::PrintString(m_position.x+X(242.0f), m_position.y+Y(360.0f), TheText.Get("FEDSSC1")); + CFont::PrintString(m_position.x+X(242.0f), m_position.y+Y(372.0f), TheText.Get("FEDSSC2")); + break; + + case PAGE_BRIEFS: + CFont::PrintString(m_position.x+X(52.0f), m_position.y+Y(360.0f), TheText.Get("FEDS_ST")); + CFont::PrintString(m_position.x+X(52.0f), m_position.y+Y(372.0f), TheText.Get("FEDS_AM")); + break; + + case PAGE_LOAD: + case PAGE_CONTROLS: + case PAGE_AUDIO: + case PAGE_DISPLAY: + case PAGE_LANGUAGE: + { + CFont::PrintString(m_position.x+X(52.0f), m_position.y+Y(360.0f), TheText.Get("FEDS_SE")); + CFont::PrintString(m_position.x+X(52.0f), m_position.y+Y(372.0f), TheText.Get("FEDS_BA")); + CFont::PrintString(m_position.x+X(52.0f), m_position.y+Y(384.0f), TheText.Get("FEDS_ST")); + + switch ( m_pageState ) + { + case PAGESTATE_NORMAL: + CFont::PrintString(m_position.x+X(242.0f), m_position.y+Y(372.0f), TheText.Get("FEDS_AM")); // <>-CHANGE MENU + break; + + case PAGESTATE_HIGHLIGHTED: + case PAGESTATE_SELECTED: + { + CFont::PrintString(m_position.x+X(242.0f), m_position.y+Y(360.0f+3.5f), TheText.Get("FEA_UP")); // ; + CFont::PrintString(m_position.x+X(242.0f), m_position.y+Y(384.0f-3.5f), TheText.Get("FEA_DO")); // = + CFont::PrintString(m_position.x+X(242.0f-10.0f), m_position.y+Y(372.0f), TheText.Get("FEA_LE")); // < + CFont::PrintString(m_position.x+X(242.0f+11.0f), m_position.y+Y(372.0f), TheText.Get("FEA_RI")); // > + CFont::PrintString(m_position.x+X(242.0f+20.0f), m_position.y+Y(372.0f), TheText.Get("FEDSAS3")); // - CHANGE SELECTION + + break; + } + } + + break; + } + } + + CFont::DrawFonts(); + + /* Draw tab button texts */ + CFont::SetFontStyle(FONT_BANK); + CFont::SetBackgroundOff(); + CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X), Y(BUTTONTAB_TEXT_SIZE_Y)); + CFont::SetPropOn(); + CFont::SetCentreOn(); + CFont::SetRightJustifyOff(); + CFont::SetBackGroundOnlyTextOn(); + CFont::SetWrapx(SCRW-X(40.0f)); // 600.0f + + switch ( m_pageState ) + { + case PAGESTATE_NORMAL: + { + CFont::SetColor(CRGBA(16, 16, 16, m_someAlpha)); + CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_STATS]), Y(BUTTONTAB_TEXT_SIZE_Y)); + CFont::PrintString(m_position.x+X(92.0f), m_position.y+Y(408.0f), TheText.Get("FEB_STA")); + CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_LOAD]), Y(BUTTONTAB_TEXT_SIZE_Y)); + CFont::PrintString(m_position.x+X(158.0f), m_position.y+Y(408.0f), TheText.Get("FEB_SAV")); + CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_BRIEFS]), Y(BUTTONTAB_TEXT_SIZE_Y)); + CFont::PrintString(m_position.x+X(224.0f), m_position.y+Y(408.0f), TheText.Get("FEB_BRI")); + CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_CONTROLS]), Y(BUTTONTAB_TEXT_SIZE_Y)); + CFont::PrintString(m_position.x+X(290.0f), m_position.y+Y(408.0f), TheText.Get("FEB_CON")); + CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_AUDIO]), Y(BUTTONTAB_TEXT_SIZE_Y)); + CFont::PrintString(m_position.x+X(356.0f), m_position.y+Y(408.0f), TheText.Get("FEB_AUD")); + CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_DISPLAY]), Y(BUTTONTAB_TEXT_SIZE_Y)); + CFont::PrintString(m_position.x+X(422.0f), m_position.y+Y(408.0f), TheText.Get("FEB_DIS")); + CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_LANGUAGE]), Y(BUTTONTAB_TEXT_SIZE_Y)); + CFont::PrintString(m_position.x+X(488.0f), m_position.y+Y(408.0f), TheText.Get("FEB_LAN")); + + break; + } + + case PAGESTATE_HIGHLIGHTED: + case PAGESTATE_SELECTED: + { + CFont::SetColor(CRGBA(16, 16, 16, m_someAlpha)); + switch(m_currentPage) + { + // PAL has 466 for 408...probably rounded? + case PAGE_STATS: + CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_STATS]), Y(BUTTONTAB_TEXT_SIZE_Y)); + CFont::PrintString(m_position.x+X(92.0f), m_position.y+Y(408.0f), TheText.Get("FEB_STA")); + break; + case PAGE_LOAD: + CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_LOAD]), Y(BUTTONTAB_TEXT_SIZE_Y)); + CFont::PrintString(m_position.x+X(158.0f), m_position.y+Y(408.0f), TheText.Get("FEB_SAV")); + break; + case PAGE_BRIEFS: + CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_BRIEFS]), Y(BUTTONTAB_TEXT_SIZE_Y)); + CFont::PrintString(m_position.x+X(224.0f), m_position.y+Y(408.0f), TheText.Get("FEB_BRI")); + break; + case PAGE_CONTROLS: + CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_CONTROLS]), Y(BUTTONTAB_TEXT_SIZE_Y)); + CFont::PrintString(m_position.x+X(290.0f), m_position.y+Y(408.0f), TheText.Get("FEB_CON")); + break; + case PAGE_AUDIO: + CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_AUDIO]), Y(BUTTONTAB_TEXT_SIZE_Y)); + CFont::PrintString(m_position.x+X(356.0f), m_position.y+Y(408.0f), TheText.Get("FEB_AUD")); + break; + case PAGE_DISPLAY: + CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_DISPLAY]), Y(BUTTONTAB_TEXT_SIZE_Y)); + CFont::PrintString(m_position.x+X(422.0f), m_position.y+Y(408.0f), TheText.Get("FEB_DIS")); + break; + case PAGE_LANGUAGE: + CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_LANGUAGE]), Y(BUTTONTAB_TEXT_SIZE_Y)); + CFont::PrintString(m_position.x+X(488.0f), m_position.y+Y(408.0f), TheText.Get("FEB_LAN")); + break; + } + + break; + } + } + + CFont::DrawFonts(); + + pActiveMenuPage = nil; + switch(m_currentPage) + { + case PAGE_STATS: pActiveMenuPage = &MenuPage_Stats; break; + case PAGE_LOAD: pActiveMenuPage = pMenuSave; break; + case PAGE_BRIEFS: pActiveMenuPage = &MenuPage_Briefs; break; + case PAGE_CONTROLS: pActiveMenuPage = &MenuPage_Controls; break; + case PAGE_AUDIO: pActiveMenuPage = &MenuPage_Audio; break; + case PAGE_DISPLAY: pActiveMenuPage = &MenuPage_Display; break; + case PAGE_LANGUAGE: pActiveMenuPage = &MenuPage_Language; break; + } + + CFont::SetFontStyle(FONT_BANK); + CFont::SetBackgroundOff(); + CFont::SetScale(X(MENU_TEXT_SIZE_X), Y(MENU_TEXT_SIZE_Y)); + CFont::SetPropOn(); + CFont::SetCentreOff(); + CFont::SetJustifyOn(); + CFont::SetRightJustifyOff(); + CFont::SetBackGroundOnlyTextOn(); + CFont::SetWrapx(SCRW-X(40.0f)); // 600.0f + CFont::SetRightJustifyWrap(X(38.0f)); + + if(m_currentPage == PAGE_LANGUAGE) + { + CFont::SetCentreOn(); + CFont::SetCentreSize(SCRW-X(40.0f)); // 600.0f + } + + if ( m_nEndPauseTimer != 0 ) + { + switch ( m_currentPage ) + { + case PAGE_LOAD: + case PAGE_BRIEFS: + case PAGE_CONTROLS: + break; + + default: + CFont::SetWrapx(X(1200.0f)); + break; + } + } + + if(pActiveMenuPage) + { + pActiveMenuPage->SetAlpha(m_fade); + + switch ( m_pageState ) + { + case PAGESTATE_NORMAL: + pActiveMenuPage->DrawNormal(m_position.x, m_position.y); + break; + + case PAGESTATE_HIGHLIGHTED: + pActiveMenuPage->DrawHighlighted(CRGBA(rgbaATC.r, rgbaATC.g, rgbaATC.b, m_fade), m_position.x, m_position.y); + break; + + case PAGESTATE_SELECTED: + pActiveMenuPage->Draw(CRGBA(rgbaATC.r, rgbaATC.g, rgbaATC.b, m_fade), CRGBA(MENU_SELECTED_COLOR.r, MENU_SELECTED_COLOR.g, MENU_SELECTED_COLOR.b, m_fade), m_position.x, m_position.y); + break; + } + } + + CFont::DrawFonts(); + CFont::DrawFonts(); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); + RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSWRAP); +} + +void +CMenuManager::DrawFrontEndSaveZone(void) +{ + if ( bMemoryCardSpecialZone ) + { + static uint8 counter = 0; + counter++; + if ( counter & 63 ) + { + FillMenuWithMemCardFileListing(&MenuSaveZoneSSL_1, TriggerSaveZone_BackToMainMenuTwoLines, TriggerSaveZone_SaveSlots, nil, 0, 34, 22); + + if ( TheMemoryCard.GetError() == CMemoryCard::ERR_NOFORMAT ) + { + pActiveMenuPage = &MenuPageSaveZone_FormatCard; + pActiveMenuPage->ActivatePage(); + bMemoryCardSpecialZone = false; + } + } + } + + CSprite2d::InitPerFrame(); + CFont::InitPerFrame(); + + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST); + + m_fade = 255; + + CSprite2d::DrawRect(CRect(X(50.0f), Y(50.0f), X(590.0f), Y(398.0f)), CRGBA(0, 0, 0, 175)); //CRect(50.0f, 57.142f, 590.0f, 454.857147f) + + CFont::SetFontStyle(FONT_BANK); + CFont::SetBackgroundOff(); + CFont::SetScale(X(MENU_TEXT_SIZE_X), Y(MENU_TEXT_SIZE_Y)); + CFont::SetPropOn(); + CFont::SetCentreOff(); + CFont::SetJustifyOn(); + CFont::SetRightJustifyOff(); + CFont::SetBackGroundOnlyTextOn(); + CFont::SetRightJustifyWrap(X(70.0f)); + CFont::SetWrapx(SCRW-X(70.0f)); // 570.0f + + if ( pActiveMenuPage ) + { + pActiveMenuPage->SetAlpha(m_fade); + pActiveMenuPage->Draw(CRGBA(rgbaATC.r, rgbaATC.g, rgbaATC.b, m_fade), TITLE_TEXT_COLOR, 0.0f, 0.0f); + } + + + CFont::DrawFonts(); + CFont::SetFontStyle(FONT_BANK); + CFont::SetBackgroundOff(); + CFont::SetScale(X(0.44f), Y(0.68f)); // 0.44f, 0.777143f + CFont::SetPropOn(); + CFont::SetCentreOff(); + CFont::SetJustifyOn(); + CFont::SetRightJustifyOff(); + CFont::SetBackGroundOnlyTextOn(); + CFont::SetWrapx(SCRW-X(40.0f)); //600.0f + CFont::SetColor(TEXT_COLOR); + + wchar *text; + if ( pActiveMenuPage == &MenuPageSaveZone_FormatCard + || pActiveMenuPage == &MenuPageSaveZone_SaveSlots + || pActiveMenuPage == &MenuPageSaveZone_SaveGame ) + { + text = TheText.Get("FEDS_SB"); // / button - SELECT " button - BACK + } + else + { + text = TheText.Get("FEDS_SE"); // / button - SELECT + } + + CFont::PrintString(X(180.0f), Y(376.0f), text); // 180.0f, 429.714294f + CFont::DrawFonts(); + + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); +} + +void +CMenuManager::DrawMemoryCardStartUpMenus() +{ + CFont::SetAlphaFade(255.0f); + bMemoryCardStartUpMenus_ExitNow = false; + + CMenuPage page; // + 0x40 data + CMenuMultiChoiceTriggered MCMenu; + MCMenu.SetPosition(X(320.0f), Y(150.0f)); //171.428574f + + switch ( TheMemoryCard.CheckCardStateAtGameStartUp(CARD_ONE) ) + { + case CMemoryCard::MCSTATE_NEED_200KB: // 200KB + { + // There is insufficient space on the Memory Card (PS2) in MEMORY CARD slot 1. At least 200KB is needed to save this application data. Do you wish to start? (YES or NO) + MCMenu.AddTitle(TheText.Get("MCGNSP"), 0.0f, 0.0f, 0); + break; + } + + case CMemoryCard::MCSTATE_NEED_500KB: // 500KB + { + // There is insufficient space on the Memory Card (PS2) in MEMORY CARD slot 1. At least 500KB is needed to save this application data. Do you wish to start? (YES or NO) + MCMenu.AddTitle(TheText.Get("MCDNSP"), 0.0f, 0.0f, 0); + break; + } + + case CMemoryCard::MCSTATE_OK: + case CMemoryCard::MCSTATE_NOCARD: + { + return; + break; + } + } + + MCMenu.AddOption(TheText.Get("FEM_NO"), X(30.0f), Y(110.0f), nil, 0, 0);// 125.714294f + MCMenu.AddOption(TheText.Get("FEM_YES"), X(-30.0f), Y(110.0f), TriggerMCSUM_Yes, 0, 0);// 125.714294f + MCMenu.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); + page.AddMenu(&MCMenu); + + MCMenu.GoFirst(); + + page.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + + CTimer::Initialise(); + CTimer::StartUserPause(); + + while ( !bMemoryCardStartUpMenus_ExitNow ) + { +#ifdef GTA_PC + HandleExit(); + + if(RsGlobal.quit) + return; +#endif + +#ifdef GTA_PC + if ( CPad::GetPad(0)->GetDPadLeftJustDown() ) + page.GoLeft(); + if ( CPad::GetPad(0)->GetDPadRightJustDown() ) + page.GoRight(); + if ( CPad::GetPad(0)->GetDPadUpJustDown() ) + page.GoDown(); + if ( CPad::GetPad(0)->GetDPadDownJustDown() ) + page.GoUp(); + if ( CPad::GetPad(0)->GetCrossJustDown() || CPad::GetPad(0)->GetEnterJustDown() || CPad::GetPad(0)->GetRightMouseJustDown() ) + page.SelectCurrentOptionUnderCursor(); + + if ( CPad::GetPad(0)->GetCircleJustDown() || CPad::GetPad(0)->GetEscapeJustDown() ) + ; +#else + if ( CPad::GetPad(0)->GetDPadLeftJustDown() ) + page.GoLeft(); + if ( CPad::GetPad(0)->GetDPadRightJustDown() ) + page.GoRight(); + if ( CPad::GetPad(0)->GetDPadUpJustDown() ) + page.GoDown(); + if ( CPad::GetPad(0)->GetDPadDownJustDown() ) + page.GoUp(); + if ( CPad::GetPad(0)->GetCrossJustDown() ) + page.SelectCurrentOptionUnderCursor(); + if ( CPad::GetPad(0)->GetCircleJustDown() ) + ; +#endif + + static int32 MemCardStatusWaiter = 0; + + MemCardStatusWaiter++; + + if ( MemCardStatusWaiter > 120 ) + { + MemCardStatusWaiter = 0; + + switch ( TheMemoryCard.CheckCardStateAtGameStartUp(CARD_ONE) ) + { + case CMemoryCard::MCSTATE_NEED_200KB: + { + // There is insufficient space on the Memory Card (PS2) in MEMORY CARD slot 1. At least 200KB is needed to save this application data. Do you wish to start? (YES or NO) + MCMenu.AddTitle(TheText.Get("MCGNSP"), 0.0f, 0.0f, 0); + break; + } + + case CMemoryCard::MCSTATE_NEED_500KB: + { + // There is insufficient space on the Memory Card (PS2) in MEMORY CARD slot 1. At least 500KB is needed to save this application data. Do you wish to start? (YES or NO) + MCMenu.AddTitle(TheText.Get("MCDNSP"), 0.0f, 0.0f, 0); + break; + } + + case CMemoryCard::MCSTATE_NOCARD: + { + // There is no Memory Card (PS2) in MEMORY CARD slot 1. Do you wish to start? (YES or NO) + MCMenu.AddTitle(TheText.Get("MCSTNS"), 0.0f, 0.0f, 0); + break; + } + + case CMemoryCard::MCSTATE_OK: + { + bMemoryCardStartUpMenus_ExitNow = true; + break; + } + } + } + + DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); + CFont::InitPerFrame(); + + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); + CSprite2d *splash = LoadSplash("splash1"); + splash->Draw(CRect(0.0f, 0.0f, SCRW, SCRH), BACKGROUND_SPLASH_COLOR); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST); + + SetRandomActiveTextlineColor(1); + + CRGBA col(rgbaATC.r, rgbaATC.g, rgbaATC.b, 255); + CFont::SetFontStyle(FONT_BANK); + CFont::SetBackgroundOff(); + CFont::SetScale(X(MENU_TEXT_SIZE_X), Y(MENU_TEXT_SIZE_Y)); + CFont::SetPropOn(); + CFont::SetJustifyOn(); + CFont::SetRightJustifyOff(); + CFont::SetBackGroundOnlyTextOn(); + CFont::SetWrapx(SCRW-X(60.0f)); // 580.0f + CFont::SetCentreOn(); + CFont::SetCentreSize(SCRW-X(120.0f)); // 520.0f + + MCMenu.Draw(col, TITLE_TEXT_COLOR, 0.0f, 0.0f); + CFont::DrawFonts(); + + CFont::SetFontStyle(FONT_BANK); + CFont::SetScale(X(0.4f), Y(0.64f)); // 0.731429 + CFont::SetPropOn(); + CFont::SetCentreOff(); + CFont::SetJustifyOn(); + CFont::SetRightJustifyOff(); + CFont::SetBackGroundOnlyTextOn(); + CFont::SetWrapx(SCRW-X(60.0f)); // 580.0f + CFont::SetColor(TEXT_COLOR); + + + CPlaceableShText text; + text.SetPosition(X(240.0f), Y(378.0f), false); // 432.000000 + text.SetColor(TEXT_COLOR); + text.m_text = TheText.Get("FEDS_SE"); // / button - SELECT + text.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); + text.Draw(0.0f, 0.0f); + + CFont::DrawFonts(); + DisplayWarningControllerMsg(); + DoRWStuffEndOfFrame(); + CPad::UpdatePads(); + CTimer::Update(); + } + + CTimer::EndUserPause(); + CTimer::Stop(); + + for ( int32 i = 0; i < 100; i++ ) + { +#ifdef GTA_PC + HandleExit(); +#endif + DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); + + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); + CSprite2d *splash = LoadSplash("splash1"); + splash->Draw(CRect(0.0f, 0.0f, SCRW, SCRH), BACKGROUND_SPLASH_COLOR); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST); + + DoRWStuffEndOfFrame(); + } +} + +void +CMenuManager::Process(void) +{ + if ( m_bSaveMenuActive || m_bInSaveZone || TheCamera.GetScreenFadeStatus() == FADE_0 ) + { + InitialiseMenusOnce(); + m_bWantToRestart = false; + WorkOutMenuState(false); + + if ( m_bMenuActive ) + { + if ( !m_bInSaveZone ) + LoadAllTextures(); + InitialiseMenuContents(); + SetRandomActiveTextlineColor(0); + ProcessControllerInput(); + } + else + { + AnaliseMenuContents(); + pMenuSave = &MenuPage_SaveBasic; + m_pageState = PAGESTATE_NORMAL; + bMemoryCardSpecialZone = false; + bIgnoreTriangleButton = false; + UnloadTextures(); + m_bInSaveZone = false; + m_bRenderGameInMenu = false; + gErrorSampleTriggered = true; + } + } +} + +void +CMenuManager::WorkOutMenuState(uint8 bExit) +{ +#ifdef GTA_PC + bool bIsStartPressed = CPad::GetPad(0)->GetStartJustDown() || (m_pageState == PAGESTATE_NORMAL && CPad::GetPad(0)->GetEscapeJustDown()); +#else + bool bIsStartPressed = CPad::GetPad(0)->GetStartJustDown(); +#endif + bool bIsCreditsOrDraw = CCredits::AreCreditsDone() || m_bMenuActive; + bool bIsDemoOrDraw = m_bMenuActive || CGame::bDemoMode; + + if ( (bIsStartPressed && bIsCreditsOrDraw) || bExit || (!bIsDemoOrDraw && CPad::IsNoOrObsolete()) ) + { + if ( m_nStartPauseTimer == 0 && m_nEndPauseTimer == 0 ) + { + m_bMenuActive = !m_bMenuActive; + + if ( !m_bMenuActive ) + { + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_STARTING, 0); + DMAudio.ChangeMusicMode(MUSICMODE_GAME); + gMusicPlaying = false; + bMemoryCardSpecialZone = false; + bIgnoreTriangleButton = false; + + m_bMenuActive = true; + + m_nEndPauseTimer = CTimer::GetTimeInMillisecondsPauseMode() + 800; + + if ( m_currentPage == PAGE_CONTROLS || m_currentPage == PAGE_BRIEFS || m_currentPage == PAGE_LOAD ) + { + m_nSlidingDir = CGeneral::GetRandomNumber() & (SLIDE_MAX-1); + + switch ( m_nSlidingDir ) //m_nSlidingDir &= ~1; + { + case SLIDE_TO_LEFT: m_nSlidingDir = SLIDE_TO_TOP; break; + case SLIDE_TO_RIGHT: m_nSlidingDir = SLIDE_TO_BOTTOM; break; + } + + m_position.y = Y(500.0f); // 571.428589f; + } + } + else + { + DMAudio.ChangeMusicMode(MUSICMODE_FRONTEND); + + if ( DMAudio.GetRadioInCar() < 9 ) + m_PrefsRadioStation = DMAudio.GetRadioInCar(); + else + m_PrefsRadioStation = CGeneral::GetRandomNumber() % 9; + + CTimer::StartUserPause(); + CPad::StopPadsShaking(); + m_nStartPauseTimer = CTimer::GetTimeInMillisecondsPauseMode() + 800; + m_nSlidingDir = CGeneral::GetRandomNumber() & (SLIDE_MAX-1); + + switch ( m_nSlidingDir ) + { + case SLIDE_TO_RIGHT: m_position.y = Y(612.5f); break; + case SLIDE_TO_LEFT: m_position.y = Y(612.5f); break; + case SLIDE_TO_BOTTOM: m_position.y = Y(500.0f); break; + case SLIDE_TO_TOP: m_position.y = Y(500.0f); break; + default: m_position.y = Y(500.0f); break; + } + + if ( m_currentPage == PAGE_CONTROLS || m_currentPage == PAGE_BRIEFS ) + { + m_nSlidingDir = CGeneral::GetRandomNumber() & (SLIDE_MAX-1); + + switch ( m_nSlidingDir ) //m_nSlidingDir &= ~1; + { + case SLIDE_TO_LEFT: m_nSlidingDir = SLIDE_TO_TOP; break; + case SLIDE_TO_RIGHT: m_nSlidingDir = SLIDE_TO_BOTTOM; break; + } + + m_position.y = Y(500.0f); //571.428589f + } + } + } + } + + if ( m_bSaveMenuActive && !m_bInSaveZone && !TheMemoryCard._bunk2) + { + m_bSaveMenuActive = false; + m_bInSaveZone = true; + m_bRenderGameInMenu = true; + m_bMenuActive = true; + CTimer::StartUserPause(); + pActiveMenuPage = &MenuPageSaveZone_SaveGame; + } + + if ( m_pageState == PAGESTATE_NORMAL && gMusicPlaying ) + { + DMAudio.StopFrontEndTrack(); + gMusicPlaying = false; + } + + if ( m_nChangePageTimer != 0 && CTimer::GetTimeInMillisecondsPauseMode() >= m_nChangePageTimer ) + { + m_nChangePageTimer = 0; + pMenuSave = &MenuPage_SaveBasic; + m_currentPage = m_newPage; + } + + if ( m_nPageLeftTimer != 0 && CTimer::GetTimeInMillisecondsPauseMode() >= m_nPageLeftTimer ) + m_nPageLeftTimer = 0; + + if ( m_nPageRightTimer != 0 && CTimer::GetTimeInMillisecondsPauseMode() >= m_nPageRightTimer ) + m_nPageRightTimer = 0; + + if ( m_nStartPauseTimer != 0 && CTimer::GetTimeInMillisecondsPauseMode() >= m_nStartPauseTimer ) + m_nStartPauseTimer = 0; + + if ( m_nEndPauseTimer != 0 && CTimer::GetTimeInMillisecondsPauseMode() >= m_nEndPauseTimer ) + { + m_nEndPauseTimer = 0; + m_bMenuActive = false; + m_bMenuActive = false; + m_bInSaveZone = false; + CTimer::EndUserPause(); + } +} + +void +CMenuManager::ProcessControllerInput(void) +{ + if ( TimeToStopPadShaking != 0 && TimeToStopPadShaking < CTimer::GetTimeInMillisecondsPauseMode() ) + { + CPad::StopPadsShaking(); + TimeToStopPadShaking = 0; + } + +#ifdef GTA_PC + if ( CPad::GetPad(0)->GetDPadLeft() || CPad::GetPad(0)->GetLeft() ) +#else + if ( CPad::GetPad(0)->GetDPadLeft() ) +#endif + { + switch ( m_pageState ) + { + case PAGESTATE_NORMAL: + case PAGESTATE_HIGHLIGHTED: + break; + + case PAGESTATE_SELECTED: + { + if ( pActiveMenuPage ) + pActiveMenuPage->GoLeftStill(); + break; + } + } + } + +#ifdef GTA_PC + if ( CPad::GetPad(0)->GetDPadRight() || CPad::GetPad(0)->GetRight() ) +#else + if ( CPad::GetPad(0)->GetDPadRight() ) +#endif + { + switch ( m_pageState ) + { + case PAGESTATE_NORMAL: + case PAGESTATE_HIGHLIGHTED: + break; + + case PAGESTATE_SELECTED: + { + if ( pActiveMenuPage ) + pActiveMenuPage->GoRightStill(); + break; + } + } + } + +#ifdef GTA_PC + if ( CPad::GetPad(0)->GetDPadLeftJustDown() || CPad::GetPad(0)->GetLeftJustDown() ) +#else + if ( CPad::GetPad(0)->GetDPadLeftJustDown() ) +#endif + ProcessDPadLeftJustDown(); + +#ifdef GTA_PC + if ( CPad::GetPad(0)->GetDPadRightJustDown() || CPad::GetPad(0)->GetRightJustDown() ) +#else + if ( CPad::GetPad(0)->GetDPadRightJustDown() ) +#endif + ProcessDPadRightJustDown(); + +#ifdef GTA_PC + if ( CPad::GetPad(0)->GetDPadUp() || CPad::GetPad(0)->GetUp() ) +#else + if ( CPad::GetPad(0)->GetDPadUp() ) +#endif + { + switch ( m_pageState ) + { + case PAGESTATE_NORMAL: + { + if ( m_currentPage == PAGE_STATS ) + { + if ( pActiveMenuPage ) + pActiveMenuPage->GoUpStill(); + } + break; + } + + case PAGESTATE_HIGHLIGHTED: + break; + + case PAGESTATE_SELECTED: + { + if ( pActiveMenuPage ) + pActiveMenuPage->GoUpStill(); + break; + } + } + } + +#ifdef GTA_PC + if ( CPad::GetPad(0)->GetDPadDown() || CPad::GetPad(0)->GetDown() ) +#else + if ( CPad::GetPad(0)->GetDPadDown() ) +#endif + { + switch ( m_pageState ) + { + case PAGESTATE_NORMAL: + { + if ( m_currentPage == PAGE_STATS ) + { + if ( pActiveMenuPage ) + pActiveMenuPage->GoDownStill(); + } + + break; + } + case PAGESTATE_HIGHLIGHTED: + break; + + case PAGESTATE_SELECTED: + { + if ( pActiveMenuPage ) + pActiveMenuPage->GoDownStill(); + break; + } + } + } + +#ifdef GTA_PC + if ( CPad::GetPad(0)->GetDPadUpJustDown() || CPad::GetPad(0)->GetUpJustDown() ) +#else + if ( CPad::GetPad(0)->GetDPadUpJustDown() ) +#endif + ProcessDPadUpJustDown(); + +#ifdef GTA_PC + if ( CPad::GetPad(0)->GetDPadDownJustDown() || CPad::GetPad(0)->GetDownJustDown() ) +#else + if ( CPad::GetPad(0)->GetDPadDownJustDown() ) +#endif + ProcessDPadDownJustDown(); + + if ( CPad::GetPad(0)->GetLeftShoulder1JustDown() ) + { + switch ( m_pageState ) + { + case PAGESTATE_NORMAL: + ProcessDPadLeftJustDown(); + break; + + case PAGESTATE_HIGHLIGHTED: + case PAGESTATE_SELECTED: + break; + } + } + + if ( CPad::GetPad(0)->GetRightShoulder1JustDown() ) + { + switch ( m_pageState ) + { + case PAGESTATE_NORMAL: + ProcessDPadRightJustDown(); + break; + + case PAGESTATE_HIGHLIGHTED: + case PAGESTATE_SELECTED: + break; + } + } + +#ifdef GTA_PC + if ( CPad::GetPad(0)->GetCrossJustDown() || CPad::GetPad(0)->GetEnterJustDown() || CPad::GetPad(0)->GetRightMouseJustDown() ) +#else + if ( CPad::GetPad(0)->GetCrossJustDown() ) +#endif + ProcessDPadCrossJustDown(); + +#ifdef GTA_PC + if ( CPad::GetPad(0)->GetTriangleJustDown() || CPad::GetPad(0)->GetBackspaceJustDown() || (m_pageState != PAGESTATE_NORMAL && CPad::GetPad(0)->GetEscapeJustDown()) ) +#else + if ( CPad::GetPad(0)->GetTriangleJustDown() ) +#endif + ProcessDPadTriangleJustDown(); +} + + +void +CMenuManager::ProcessDPadLeftJustDown(void) +{ + if ( m_bInSaveZone ) + { + if ( pActiveMenuPage ) + { + pActiveMenuPage->GoLeft(); + + if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveZoneSSL_1 ) + { + if ( MenuSaveZoneSSL_1.m_numOptions < 2 ) + ; + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + } + else + { + switch ( m_pageState ) + { + case PAGESTATE_NORMAL: + { + if ( !bMemoryCardSpecialZone && !m_bInSaveZone ) + { + if ( m_nChangePageTimer == 0 ) + { + if ( --m_newPage < PAGE_FIRST ) m_newPage = PAGE_LAST; + + m_nPageLeftTimer = CTimer::GetTimeInMillisecondsPauseMode() + 300; + m_nPageRightTimer = 0; + m_nChangePageTimer = CTimer::GetTimeInMillisecondsPauseMode() + 250; + field_18 = m_newPage; + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NEW_PAGE, 0); + } + } + + break; + } + + case PAGESTATE_HIGHLIGHTED: + { + if ( pActiveMenuPage ) + pActiveMenuPage->GoLeftMenuOnPage(); + + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + + break; + } + + case PAGESTATE_SELECTED: + { + if ( pActiveMenuPage ) + { + pActiveMenuPage->GoLeft(); + + if ( m_currentPage == PAGE_AUDIO) + { + if ( pActiveMenuPage->m_pCurrentControl == &MenuAudio_1 ) + ; + else if ( pActiveMenuPage->m_pCurrentControl == &MenuAudio_2 ) + ; + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + else if ( m_currentPage == PAGE_DISPLAY) + { + if ( pActiveMenuPage->m_pCurrentControl == &MenuDisplay_1 ) + ; + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + else + { + if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveDG_2 ) + { + if ( MenuSaveDG_2.m_numOptions < 2 ) + ; + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + else if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveLG_2 ) + { + if ( MenuSaveLG_2.m_numOptions < 2 ) + ; + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + } + + break; + } + } + } +} + +void +CMenuManager::ProcessDPadRightJustDown(void) +{ + if ( m_bInSaveZone ) + { + if ( pActiveMenuPage ) + { + pActiveMenuPage->GoRight(); + + if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveZoneSSL_1 ) + { + if ( MenuSaveZoneSSL_1.m_numOptions < 2 ) + ; + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + } + else + { + switch ( m_pageState ) + { + case PAGESTATE_NORMAL: + { + if ( !bMemoryCardSpecialZone && !m_bInSaveZone ) + { + if ( m_nChangePageTimer == 0 ) + { + if ( ++m_newPage > PAGE_LAST ) m_newPage = PAGE_FIRST; + + m_nPageLeftTimer = 0; + m_nPageRightTimer = CTimer::GetTimeInMillisecondsPauseMode() + 300; + m_nChangePageTimer = CTimer::GetTimeInMillisecondsPauseMode() + 250; + field_18 = m_newPage; + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NEW_PAGE, 0); + } + } + + break; + } + + case PAGESTATE_HIGHLIGHTED: + { + if ( pActiveMenuPage ) + pActiveMenuPage->GoRightMenuOnPage(); + + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + + break; + } + + case PAGESTATE_SELECTED: + { + if ( pActiveMenuPage ) + { + pActiveMenuPage->GoRight(); + + if ( m_currentPage == PAGE_AUDIO) + { + if ( pActiveMenuPage->m_pCurrentControl == &MenuAudio_1 ) + ; + else if ( pActiveMenuPage->m_pCurrentControl == &MenuAudio_2 ) + ; + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + else if ( m_currentPage == PAGE_DISPLAY) + { + if ( pActiveMenuPage->m_pCurrentControl == &MenuDisplay_1 ) + ; + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + else + { + if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveDG_2 ) + { + if ( MenuSaveDG_2.m_numOptions < 2 ) + ; + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + else if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveLG_2 ) + { + if ( MenuSaveLG_2.m_numOptions < 2 ) + ; + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + } + + break; + } + } + } +} + +void +CMenuManager::ProcessDPadUpJustDown(void) +{ + if ( m_bInSaveZone ) + { + if ( pActiveMenuPage ) + { + pActiveMenuPage->GoUp(); + + if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveZoneSSL_1 ) + { + if ( MenuSaveZoneSSL_1.m_numOptions < 2 ) + ; + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + } + else + { + switch ( m_pageState ) + { + case PAGESTATE_NORMAL: + break; + + case PAGESTATE_HIGHLIGHTED: + { + if ( pActiveMenuPage ) + pActiveMenuPage->GoUpMenuOnPage(); + + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + break; + } + + case PAGESTATE_SELECTED: + { + if ( pActiveMenuPage ) + { + pActiveMenuPage->GoUp(); + + if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveDG_2 ) + { + if ( MenuSaveDG_2.m_numOptions < 2 ) + ; + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + else if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveLG_2 ) + { + if ( MenuSaveLG_2.m_numOptions < 2 ) + ; + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + + break; + } + } + } +} + +void +CMenuManager::ProcessDPadDownJustDown(void) +{ + if ( m_bInSaveZone ) + { + if ( pActiveMenuPage ) + { + pActiveMenuPage->GoDown(); + + if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveZoneSSL_1 ) + { + if ( MenuSaveZoneSSL_1.m_numOptions < 2 ) + ; + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + } + else + { + switch ( m_pageState ) + { + case PAGESTATE_NORMAL: + break; + + case PAGESTATE_HIGHLIGHTED: + { + if ( pActiveMenuPage ) + pActiveMenuPage->GoDownMenuOnPage(); + + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + + break; + } + + case PAGESTATE_SELECTED: + { + if ( pActiveMenuPage ) + { + pActiveMenuPage->GoDown(); + + if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveDG_2 ) + { + if ( MenuSaveDG_2.m_numOptions < 2 ) + ; + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + else if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveLG_2 ) + { + if ( MenuSaveLG_2.m_numOptions < 2 ) + ; + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); + } + break; + } + } + } +} + +void +CMenuManager::ProcessDPadTriangleJustDown(void) +{ + if ( pActiveMenuPage ) + { + pActiveMenuPage->SelectDefaultCancelAction(); + + if ( m_bMenuActive || m_bInSaveZone ) + { + if ( bIgnoreTriangleButton ) + { + if ( m_bInSaveZone ) + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_BACK, 0); + else if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveDG_2 || pActiveMenuPage->m_pCurrentControl == &MenuSaveLG_2 ) + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_BACK, 0); + } + else if ( !bIgnoreTriangleButton ) + { + switch ( m_pageState ) + { + case PAGESTATE_NORMAL: + WorkOutMenuState(true); + break; + + case PAGESTATE_HIGHLIGHTED: + m_pageState = PAGESTATE_NORMAL; + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NEW_PAGE, 0); + break; + + case PAGESTATE_SELECTED: + { + m_pageState = PAGESTATE_HIGHLIGHTED; + if ( pActiveMenuPage ) + { + if ( pActiveMenuPage->m_numControls == 1 ) + { + m_pageState = PAGESTATE_NORMAL; + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NEW_PAGE, 0); + } + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_BACK, 0); + } + break; + } + } + } + } + } + else + { + if ( !bIgnoreTriangleButton ) + { + switch ( m_pageState ) + { + case PAGESTATE_NORMAL: + WorkOutMenuState(false); + break; + + case PAGESTATE_HIGHLIGHTED: + m_pageState = PAGESTATE_NORMAL; + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NEW_PAGE, 0); + break; + + case PAGESTATE_SELECTED: + { + m_pageState = PAGESTATE_HIGHLIGHTED; + if ( pActiveMenuPage ) + { + if ( pActiveMenuPage->m_numControls == 1 ) + { + m_pageState = PAGESTATE_NORMAL; + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NEW_PAGE, 0); + } + else + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_BACK, 0); + } + break; + } + } + } + } +} + +void +CMenuManager::ProcessDPadCrossJustDown(void) +{ + if ( m_bInSaveZone ) + { + if ( pActiveMenuPage ) + pActiveMenuPage->SelectCurrentOptionUnderCursor(); + + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SETTING_CHANGE, 0); + } + else + { + if ( m_currentPage != PAGE_STATS && m_currentPage != PAGE_BRIEFS) + { + switch ( m_pageState ) + { + case PAGESTATE_NORMAL: + { + m_pageState = PAGESTATE_HIGHLIGHTED; + if ( pActiveMenuPage ) + { + if ( pActiveMenuPage->m_numControls == 1 ) + m_pageState = PAGESTATE_SELECTED; + } + + switch ( m_currentPage ) + { + case PAGE_AUDIO: + { + if ( pActiveMenuPage->m_pCurrentControl == &MenuAudio_1 + || pActiveMenuPage->m_pCurrentControl == &MenuAudio_2 + || pActiveMenuPage->m_pCurrentControl == &MenuAudio_3 + || pActiveMenuPage->m_pCurrentControl == &MenuAudio_4 ) + { + if ( !gMusicPlaying ) + { + DMAudio.PlayFrontEndTrack(m_PrefsRadioStation, TRUE); + gMusicPlaying = true; + } + } + else + { + DMAudio.StopFrontEndTrack(); + gMusicPlaying = false; + } + break; + } + } + + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SETTING_CHANGE, 0); + break; + } + + case PAGESTATE_HIGHLIGHTED: + { + m_pageState = PAGESTATE_SELECTED; + DoHackingMenusAtPageBrowse(); + if ( pActiveMenuPage ) + { + if ( pActiveMenuPage->IsActiveMenuTwoState()) + { + m_pageState = PAGESTATE_HIGHLIGHTED; + pActiveMenuPage->ActiveMenuTwoState_SelectNextPosition(); + } + } + + switch ( m_currentPage ) + { + case PAGE_AUDIO: + { + if ( pActiveMenuPage->m_pCurrentControl != &MenuAudio_4 ) + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SETTING_CHANGE, 0); + + break; + } + + default: + { + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SETTING_CHANGE, 0); + DMAudio.StopFrontEndTrack(); + gMusicPlaying = false; + break; + } + } + break; + } + + case PAGESTATE_SELECTED: + { + if ( pActiveMenuPage ) + { + pActiveMenuPage->SelectCurrentOptionUnderCursor(); + + switch ( m_currentPage ) + { + case PAGE_AUDIO: + { + if ( pActiveMenuPage->m_pCurrentControl != &MenuAudio_3 ) + m_pageState = PAGESTATE_HIGHLIGHTED; + break; + } + + case PAGE_LOAD: + case PAGE_LANGUAGE: + break; + + default: + m_pageState = PAGESTATE_HIGHLIGHTED; + break; + } + } + + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SETTING_CHANGE, 0); + break; + } + } + } + } +} + +void +CMenuManager::DoHackingMenusAtPageBrowse(void) +{ + if ( pActiveMenuPage ) + { + switch ( m_currentPage ) + { + case PAGE_CONTROLS: + { + if ( pActiveMenuPage->m_pCurrentControl == &MenuControls_1 ) + { + int32 sel = MenuControls_1.GetMenuSelection(); + MenuControls_1.GoFirst(); + + for ( int32 i = 0; i < sel; i++ ) + MenuControls_1.GoNext(); + } + break; + } + + case PAGE_AUDIO: + { + if ( pActiveMenuPage->m_pCurrentControl == &MenuAudio_3 ) + { + int32 sel = MenuAudio_3.GetMenuSelection(); + MenuAudio_3.GoFirst(); + + for ( int32 i = 0; i < sel; i++ ) + MenuAudio_3.GoNext(); + } + break; + } + } + } +} + +void +CMenuManager::SetSoundLevelsForMusicMenu(void) +{ + DMAudio.SetMusicMasterVolume(m_PrefsMusicVolume); + DMAudio.SetEffectsMasterVolume(m_PrefsSfxVolume); +} + +void +CMenuManager::FilterOutColorMarkersFromString(wchar *string, CRGBA &color) +{ + wchar buf[300]; + UnicodeStrcpy(buf, string); + + wchar *src = buf; + wchar *dst = string; + while ( *src != '\0' ) + { + if ( *src == '~' ) + { + src++; + + if ( *src == 'l' ) color = CRGBA(0, 0, 0, 255); + else if ( *src == 'p' ) color = CRGBA(255, 0, 255, 255); + else if ( *src == 'y' ) color = CRGBA(255, 255, 0, 255); + else if ( *src == 'w' ) color = CRGBA(255, 255, 255, 255); + else if ( *src == 'b' ) color = CRGBA(40, 40, 255, 255); + else if ( *src == 'g' ) color = CRGBA(40, 235, 40, 255); + else if ( *src == 'r' ) color = CRGBA(255, 0, 0, 255); + + while ( *src++ != '~' ) + ; + } + else + *dst++ = *src++; + } + + *dst = '\0'; +} + +#endif diff --git a/src/miami/core/Frontend_PS2.h b/src/miami/core/Frontend_PS2.h new file mode 100644 index 00000000..6311d821 --- /dev/null +++ b/src/miami/core/Frontend_PS2.h @@ -0,0 +1,247 @@ +#pragma once +#include "Sprite2d.h" + +enum +{ + PAGE_STATS, + PAGE_LOAD, + PAGE_BRIEFS, + PAGE_CONTROLS, + PAGE_AUDIO, + PAGE_DISPLAY, + PAGE_LANGUAGE, + + NUM_PAGES, + PAGE_FIRST = PAGE_STATS, + PAGE_LAST = PAGE_LANGUAGE, +}; + +enum +{ + PAGESTATE_NORMAL = 0, + PAGESTATE_HIGHLIGHTED, + PAGESTATE_SELECTED +}; + + +enum eFrontendSprites +{ + FE2_MAINPANEL_UL, + FE2_MAINPANEL_UR, + FE2_MAINPANEL_DL, + FE2_MAINPANEL_DR, + FE2_MAINPANEL_DR2, + FE2_TABACTIVE, + FE_ICONBRIEF, + FE_ICONSTATS, + FE_ICONCONTROLS, + FE_ICONSAVE, + FE_ICONAUDIO, + FE_ICONDISPLAY, + FE_ICONLANGUAGE, + FE_CONTROLLER, + FE_CONTROLLERSH, + FE_ARROWS1, + FE_ARROWS2, + FE_ARROWS3, + FE_ARROWS4, + FE_RADIO1, + FE_RADIO2, + FE_RADIO3, + FE_RADIO4, + FE_RADIO5, + FE_RADIO6, + FE_RADIO7, + FE_RADIO8, + FE_RADIO9, + + NUM_FE_SPRITES +}; + + +class CSprite2d; +class CVector2D; + +#ifdef GTA_PC +enum eControlMethod +{ + CONTROL_STANDARD = 0, + CONTROL_CLASSIC, +}; +#endif + +class CMenuManager +{ +public: + enum LANGUAGE + { + LANGUAGE_AMERICAN, + LANGUAGE_FRENCH, + LANGUAGE_GERMAN, + LANGUAGE_ITALIAN, + LANGUAGE_SPANISH, +#ifdef MORE_LANGUAGES + LANGUAGE_POLISH, + LANGUAGE_RUSSIAN, + LANGUAGE_JAPANESE, +#endif + }; + + enum CONTRCONFIG + { + CONFIG_1 = 0, + CONFIG_2, + CONFIG_3, + CONFIG_4, + }; + + enum + { + NUM_SPRIRES = 28, + }; + + enum + { + PAGESTATE_NORMAL = 0, + PAGESTATE_HIGHLIGHTED = 1, + PAGESTATE_SELELECTED = 2, + }; + + enum + { + SLIDE_TO_BOTTOM = 0, + SLIDE_TO_RIGHT, + SLIDE_TO_TOP, + SLIDE_TO_LEFT, + SLIDE_MAX + }; + + int32 m_currentPage; + int32 m_newPage; + int32 m_pageState; + uint32 m_nPageLeftTimer; + uint32 m_nPageRightTimer; + uint32 m_nChangePageTimer; + int field_18; + uint8 m_fade; + uint8 m_someAlpha; + //char field_1E; // unused ? + //char field_1F; // unused ? + uint32 m_nStartPauseTimer; + uint32 m_nEndPauseTimer; + CVector2D m_position; + uint8 m_nSlidingDir; + //char field_31; // unused ? + //char field_32; // unused ? + //char field_33; // unused ? + bool m_bInitialised; + bool m_bWantToUpdateContent; + bool m_bMenuActive; + bool m_bWantToRestart; + //char field_38; //unused ? + bool m_bRenderGameInMenu; + bool m_bSaveMenuActive; + bool m_bInSaveZone; + char field_3C; + bool m_bTexturesLoaded; + //char field_3E; //unused ? + //char field_3F; //unused ? + CSprite2d m_sprites[NUM_SPRIRES]; + + static int32 m_PrefsSfxVolume; + static int32 m_PrefsMusicVolume; + static int32 m_PrefsBrightness; + static bool m_PrefsShowTrails; + static bool m_PrefsShowSubtitles; + static bool m_PrefsAllowNastyGame; + static int32 m_PrefsRadioStation; + static int32 m_PrefsStereoMono; + static int8 m_PrefsUseWideScreen; + static int32 m_PrefsLanguage; + static CONTRCONFIG m_PrefsControllerConfig; + static bool m_PrefsUseVibration; + +#ifdef CUTSCENE_BORDERS_SWITCH + static bool m_PrefsCutsceneBorders; +#endif + +#ifdef GTA_PC + bool m_bQuitGameNoCD; + + int32 m_nMouseTempPosX; + int32 m_nMouseTempPosY; + int32 m_nPrefsVideoMode; + int32 m_nDisplayVideoMode; + int8 m_nPrefsAudio3DProviderIndex; + + static int32 OS_Language; + static int8 m_PrefsVsync; + static int8 m_PrefsVsyncDisp; + static int8 m_PrefsFrameLimiter; + static int8 m_PrefsSpeakers; + static int32 m_ControlMethod; + static int8 m_PrefsDMA; + static float m_PrefsLOD; + static char m_PrefsSkinFile[256]; + +#ifndef MASTER + static bool m_PrefsMarketing; + static bool m_PrefsDisableTutorials; +#endif // !MASTER + +#ifdef MENU_MAP + static bool bMenuMapActive; + static float fMapSize; + static float fMapCenterY; + static float fMapCenterX; +#endif + +#ifdef IMPROVED_VIDEOMODE + int32 m_nPrefsWidth = 640; + int32 m_nPrefsHeight = 480; + int32 m_nPrefsDepth = 32; + int32 m_nPrefsWindowed = 1; + int32 m_nPrefsSubsystem; + int32 m_nSelectedScreenMode; +#endif + + void WaitForUserCD() { } +#endif + + bool GetIsMenuActive() {return !!m_bMenuActive;} + + CMenuManager(void); +#ifdef FIX_BUGS + ~CMenuManager(void) + { + UnloadTextures(); + } +#endif + + void LoadAllTextures(void); + void UnloadTextures(void); + + void InitialiseMenusOnce(void); + void InitialiseChangedLanguageSettings(void); + void InitialiseMenuContents(void); + void AnaliseMenuContents(void); + void InitialiseMenuContentsAfterLoadingGame(void); + void DrawFrontEnd(void); + void DrawFrontEndNormal(void); + void DrawFrontEndSaveZone(void); + void DrawMemoryCardStartUpMenus(void); + void Process(void); + void WorkOutMenuState(uint8 bExit); + void ProcessControllerInput(void); + void ProcessDPadLeftJustDown(void); + void ProcessDPadRightJustDown(void); + void ProcessDPadUpJustDown(void); + void ProcessDPadDownJustDown(void); + void ProcessDPadTriangleJustDown(void); + void ProcessDPadCrossJustDown(void); + void DoHackingMenusAtPageBrowse(void); + void SetSoundLevelsForMusicMenu(void); + void FilterOutColorMarkersFromString(wchar *string, CRGBA &color); +}; + +extern CMenuManager FrontEndMenuManager; \ No newline at end of file diff --git a/src/miami/core/Game.cpp b/src/miami/core/Game.cpp new file mode 100644 index 00000000..77c8965f --- /dev/null +++ b/src/miami/core/Game.cpp @@ -0,0 +1,1352 @@ +#include "common.h" +#include "platform.h" + +#include "Game.h" +#include "main.h" +#include "RwHelper.h" +#include "Accident.h" +#include "Antennas.h" +#include "Bridge.h" +#include "CarCtrl.h" +#include "CarGen.h" +#include "CdStream.h" +#include "Clock.h" +#include "Clouds.h" +#include "Collision.h" +#include "Console.h" +#include "Coronas.h" +#include "Cranes.h" +#include "Credits.h" +#include "CutsceneMgr.h" +#include "DMAudio.h" +#include "Darkel.h" +#include "Debug.h" +#include "EventList.h" +#include "FileLoader.h" +#include "FileMgr.h" +#include "Fire.h" +#include "Fluff.h" +#include "Font.h" +#include "Frontend.h" +#include "frontendoption.h" +#include "GameLogic.h" +#include "Garages.h" +#include "GenericGameStorage.h" +#include "Glass.h" +#include "HandlingMgr.h" +#include "Heli.h" +#include "Hud.h" +#include "IniFile.h" +#include "Lights.h" +#include "MBlur.h" +#include "Messages.h" +#include "MemoryCard.h" +#include "MemoryHeap.h" +#include "Pad.h" +#include "Particle.h" +#include "ParticleObject.h" +#include "PedRoutes.h" +#include "Phones.h" +#include "Pickups.h" +#include "Plane.h" +#include "PlayerSkin.h" +#include "Population.h" +#include "Radar.h" +#include "Record.h" +#include "References.h" +#include "Renderer.h" +#include "Replay.h" +#include "Restart.h" +#include "RoadBlocks.h" +#include "Rubbish.h" +#include "SceneEdit.h" +#include "Script.h" +#include "Shadows.h" +#include "Skidmarks.h" +#include "SetPieces.h" +#include "SpecialFX.h" +#include "Stats.h" +#include "Streaming.h" +#include "SurfaceTable.h" +#include "TempColModels.h" +#include "Timecycle.h" +#include "TrafficLights.h" +#include "Train.h" +#include "TxdStore.h" +#include "User.h" +#include "VisibilityPlugins.h" +#include "WaterCannon.h" +#include "WaterLevel.h" +#include "Weapon.h" +#include "WeaponEffects.h" +#include "Weather.h" +#include "World.h" +#include "ZoneCull.h" +#include "Zones.h" +#include "Occlusion.h" +#include "debugmenu.h" +#include "Ropes.h" +#include "WindModifiers.h" +#include "WaterCreatures.h" +#include "postfx.h" +#include "custompipes.h" +#include "screendroplets.h" +#include "VarConsole.h" +#ifdef USE_TEXTURE_POOL +#include "TexturePools.h" +#endif + +eLevelName CGame::currLevel; +int32 CGame::currArea; +bool CGame::bDemoMode = true; +bool CGame::nastyGame = true; +bool CGame::frenchGame; +bool CGame::germanGame; +bool CGame::noProstitutes; +bool CGame::playingIntro; +char CGame::aDatFile[32]; +#ifdef MORE_LANGUAGES +bool CGame::russianGame = false; +bool CGame::japaneseGame = false; +#endif +#ifndef MASTER +CVector CGame::PlayerCoords; +bool8 CGame::VarUpdatePlayerCoords; +#endif + +int gameTxdSlot; + +#ifdef SECUROM +uint8 gameProcessPirateCheck = 0; +#endif + +bool DoRWStuffStartOfFrame(int16 TopRed, int16 TopGreen, int16 TopBlue, int16 BottomRed, int16 BottomGreen, int16 BottomBlue, int16 Alpha); +void DoRWStuffEndOfFrame(void); +#ifdef PS2_MENU +void MessageScreen(char *msg) +{ + //TODO: stretch_screen + + CRect rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + CRGBA color(255, 255, 255, 255); + + DoRWStuffStartOfFrame(50, 50, 50, 0, 0, 0, 255); + + CSprite2d::InitPerFrame(); + CFont::InitPerFrame(); + DefinedState(); + + CSprite2d *splash = LoadSplash(NULL); + splash->Draw(rect, color, color, color, color); + splash->DrawRect(CRect(SCREEN_SCALE_X(20.0f), SCREEN_SCALE_Y(110.0f), SCREEN_SCALE_X(620.0f), SCREEN_SCALE_Y(300.0f)), CRGBA(50, 50, 50, 192)); + + CFont::SetFontStyle(FONT_BANK); + CFont::SetBackgroundOff(); + CFont::SetWrapx(SCREEN_SCALE_FROM_RIGHT(190.0f)); // 450.0f + CFont::SetScale(SCREEN_SCALE_X(1.0f), SCREEN_SCALE_Y(1.0f)); + CFont::SetCentreOn(); + CFont::SetCentreSize(SCREEN_SCALE_X(450.0f)); + CFont::SetJustifyOff(); + CFont::SetColor(CRGBA(255, 255, 255, 255)); + CFont::SetDropColor(CRGBA(32, 32, 32, 255)); + CFont::SetDropShadowPosition(3); + CFont::SetPropOn(); + CFont::PrintString(SCREEN_SCALE_X(320.0f), SCREEN_SCALE_Y(130.0f), TheText.Get(msg)); + CFont::DrawFonts(); + + DoRWStuffEndOfFrame(); +} +#endif + +bool +CGame::InitialiseOnceBeforeRW(void) +{ + CFileMgr::Initialise(); + CdStreamInit(MAX_CDCHANNELS); + debug("size of matrix %d\n", sizeof(CMatrix)); + debug("size of placeable %d\n", sizeof(CPlaceable)); + debug("size of entity %d\n", sizeof(CEntity)); + debug("size of building %d\n", sizeof(CBuilding)); + debug("size of dummy %d\n", sizeof(CDummy)); +#ifdef EXTENDED_COLOURFILTER + CPostFX::InitOnce(); +#endif +#ifdef CUSTOM_FRONTEND_OPTIONS + // Not needed here but may be needed in future + // if (numCustomFrontendOptions == 0 && numCustomFrontendScreens == 0) + CustomFrontendOptionsPopulate(); +#endif + return true; +} + +#ifndef LIBRW +#ifdef PS2_MATFX +void ReplaceMatFxCallback(); +#endif // PS2_MATFX +#ifdef PS2_ALPHA_TEST +void ReplaceAtomicPipeCallback(); +#endif // PS2_ALPHA_TEST +#endif // !LIBRW + +bool +CGame::InitialiseRenderWare(void) +{ + ValidateVersion(); +#ifdef USE_TEXTURE_POOL + _TexturePoolsInitialise(); +#endif + + CTxdStore::Initialise(); + CVisibilityPlugins::Initialise(); + +#ifdef GTA_PS2 + RpSkySelectTrueTSClipper(TRUE); + RpSkySelectTrueTLClipper(TRUE); + + // PS2ManagerApplyDirectionalLightingCB() uploads the GTA lights + // directly without going through RpWorld and all that + SetupPS2ManagerDefaultLightingCallback(); + PreAllocateRwObjects(); +#endif + + /* Create camera */ + Scene.camera = CameraCreate(SCREEN_WIDTH, SCREEN_HEIGHT, TRUE); + ASSERT(Scene.camera != nil); + if (!Scene.camera) + { + return (false); + } + + RwCameraSetFarClipPlane(Scene.camera, 2000.0f); + RwCameraSetNearClipPlane(Scene.camera, 0.9f); + + CameraSize(Scene.camera, nil, DEFAULT_VIEWWINDOW, DEFAULT_ASPECT_RATIO); + + /* Create a world */ + RwBBox bbox; + + bbox.sup.x = bbox.sup.y = bbox.sup.z = 10000.0f; + bbox.inf.x = bbox.inf.y = bbox.inf.z = -10000.0f; + + Scene.world = RpWorldCreate(&bbox); + ASSERT(Scene.world != nil); + if (!Scene.world) + { + CameraDestroy(Scene.camera); + Scene.camera = nil; + return (false); + } + + /* Add the camera to the world */ + RpWorldAddCamera(Scene.world, Scene.camera); + LightsCreate(Scene.world); + + CreateDebugFont(); + +#ifdef LIBRW +#ifdef PS2_MATFX + rw::MatFX::envMapApplyLight = true; + rw::MatFX::envMapUseMatColor = true; + rw::MatFX::envMapFlipU = true; +#else + rw::MatFX::envMapApplyLight = false; + rw::MatFX::envMapUseMatColor = false; + rw::MatFX::envMapFlipU = false; +#endif + rw::RGBA envcol = { 64, 64, 64, 255 }; + rw::MatFX::envMapColor = envcol; +#else +#ifdef PS2_MATFX + ReplaceMatFxCallback(); +#endif // PS2_MATFX +#ifdef PS2_ALPHA_TEST + ReplaceAtomicPipeCallback(); +#endif // PS2_ALPHA_TEST +#endif // LIBRW + + PUSH_MEMID(MEMID_TEXTURES); + CFont::Initialise(); + CHud::Initialise(); + CPlayerSkin::Initialise(); + POP_MEMID(); + +#ifdef EXTENDED_PIPELINES + CustomPipes::CustomPipeInit(); // need Scene.world for this +#endif +#ifdef SCREEN_DROPLETS + ScreenDroplets::InitDraw(); +#endif + + return (true); +} + +void CGame::ShutdownRenderWare(void) +{ +#ifdef SCREEN_DROPLETS + ScreenDroplets::Shutdown(); +#endif +#ifdef EXTENDED_PIPELINES + CustomPipes::CustomPipeShutdown(); +#endif + + DestroySplashScreen(); + CHud::Shutdown(); + CFont::Shutdown(); + + for ( int32 i = 0; i < NUMPLAYERS; i++ ) + CWorld::Players[i].DeletePlayerSkin(); + + CPlayerSkin::Shutdown(); + + DestroyDebugFont(); + + /* Destroy world */ + LightsDestroy(Scene.world); + RpWorldRemoveCamera(Scene.world, Scene.camera); + RpWorldDestroy(Scene.world); + + /* destroy camera */ + CameraDestroy(Scene.camera); + + Scene.world = nil; + Scene.camera = nil; + + CVisibilityPlugins::Shutdown(); + +#ifdef USE_TEXTURE_POOL + _TexturePoolsShutdown(); +#endif +} + +bool CGame::InitialiseOnceAfterRW(void) +{ + TheText.Load(); + CTimer::Initialise(); + CTempColModels::Initialise(); + mod_HandlingManager.Initialise(); + CSurfaceTable::Initialise("DATA\\SURFACE.DAT"); + CPedStats::Initialise(); + CTimeCycle::Initialise(); +#ifdef GTA_PS2 + LoadingScreen("Loading the Game", "Initialising audio", GetRandomSplashScreen()); +#endif + DMAudio.Initialise(); + +#ifndef GTA_PS2 +#ifdef EXTERNAL_3D_SOUND + if ( DMAudio.GetNum3DProvidersAvailable() == 0 ) + FrontEndMenuManager.m_nPrefsAudio3DProviderIndex = NO_AUDIO_PROVIDER; + + if ( FrontEndMenuManager.m_nPrefsAudio3DProviderIndex == AUDIO_PROVIDER_NOT_DETERMINED || FrontEndMenuManager.m_nPrefsAudio3DProviderIndex == -2 ) + { + FrontEndMenuManager.m_PrefsSpeakers = 0; + FrontEndMenuManager.m_nPrefsAudio3DProviderIndex = DMAudio.AutoDetect3DProviders(); + } + + DMAudio.SetCurrent3DProvider(FrontEndMenuManager.m_nPrefsAudio3DProviderIndex); + DMAudio.SetSpeakerConfig(FrontEndMenuManager.m_PrefsSpeakers); +#endif + DMAudio.SetDynamicAcousticModelingStatus(FrontEndMenuManager.m_PrefsDMA); + DMAudio.SetMusicMasterVolume(FrontEndMenuManager.m_PrefsMusicVolume); + DMAudio.SetEffectsMasterVolume(FrontEndMenuManager.m_PrefsSfxVolume); + DMAudio.SetEffectsFadeVol(127); + DMAudio.SetMusicFadeVol(127); +#endif + return true; +} + +void +CGame::FinalShutdown(void) +{ + CTxdStore::Shutdown(); + CPedStats::Shutdown(); + CdStreamShutdown(); +} + +bool CGame::Initialise(const char* datFile) +{ + ResetLoadingScreenBar(); + strcpy(aDatFile, datFile); + +#ifdef GTA_PS2 + // TODO: upload VU0 collision code here +#endif + + CPools::Initialise(); + +#ifndef GTA_PS2 +#ifdef PED_CAR_DENSITY_SLIDERS + // Load density values from gta3.ini only if our reVC.ini have them 0.6f + if (CIniFile::PedNumberMultiplier == 0.6f && CIniFile::CarNumberMultiplier == 0.6f) +#endif + CIniFile::LoadIniFile(); +#endif +#ifdef USE_TEXTURE_POOL + _TexturePoolsUnknown(false); +#endif + currLevel = LEVEL_BEACH; + currArea = AREA_MAIN_MAP; + + PUSH_MEMID(MEMID_TEXTURES); + LoadingScreen("Loading the Game", "Loading generic textures", GetRandomSplashScreen()); + gameTxdSlot = CTxdStore::AddTxdSlot("generic"); + CTxdStore::Create(gameTxdSlot); + CTxdStore::AddRef(gameTxdSlot); + +#ifdef EXTENDED_PIPELINES + // for generic fallback + CustomPipes::SetTxdFindCallback(); +#endif + + LoadingScreen("Loading the Game", "Loading particles", nil); + int particleTxdSlot = CTxdStore::AddTxdSlot("particle"); + CTxdStore::LoadTxd(particleTxdSlot, "MODELS/PARTICLE.TXD"); + CTxdStore::AddRef(particleTxdSlot); + CTxdStore::SetCurrentTxd(gameTxdSlot); + LoadingScreen("Loading the Game", "Setup game variables", nil); + POP_MEMID(); + +#ifdef GTA_PS2 + CDma::SyncChannel(0, true); +#endif + + CGameLogic::InitAtStartOfGame(); + CReferences::Init(); + TheCamera.Init(); + TheCamera.SetRwCamera(Scene.camera); + CDebug::DebugInitTextBuffer(); + ThePaths.Init(); + ThePaths.AllocatePathFindInfoMem(4500); + CScriptPaths::Init(); + CWeather::Init(); + CCullZones::Init(); + COcclusion::Init(); + CCollision::Init(); + CSetPieces::Init(); + CTheZones::Init(); + CUserDisplay::Init(); + CMessages::Init(); + CMessages::ClearAllMessagesDisplayedByGame(); + CRecordDataForGame::Init(); + CRestart::Initialise(); + + PUSH_MEMID(MEMID_WORLD); + CWorld::Initialise(); + POP_MEMID(); + + PUSH_MEMID(MEMID_TEXTURES); + CParticle::Initialise(); + POP_MEMID(); + + PUSH_MEMID(MEMID_ANIMATION); + CAnimManager::Initialise(); + CCutsceneMgr::Initialise(); + POP_MEMID(); + + PUSH_MEMID(MEMID_CARS); + CCarCtrl::Init(); + POP_MEMID(); + + PUSH_MEMID(MEMID_DEF_MODELS); + InitModelIndices(); + CModelInfo::Initialise(); + CPickups::Init(); + CTheCarGenerators::Init(); + + CdStreamAddImage("MODELS\\GTA3.IMG"); + + CFileLoader::LoadLevel("DATA\\DEFAULT.DAT"); + CFileLoader::LoadLevel(datFile); + + LoadingScreen("Loading the Game", "Add Particles", nil); + CWorld::AddParticles(); + CVehicleModelInfo::LoadVehicleColours(); + CVehicleModelInfo::LoadEnvironmentMaps(); + CTheZones::PostZoneCreation(); + POP_MEMID(); + + LoadingScreen("Loading the Game", "Setup paths", nil); + ThePaths.PreparePathData(); + for (int i = 0; i < NUMPLAYERS; i++) + CWorld::Players[i].Clear(); + CWorld::Players[0].LoadPlayerSkin(); + TestModelIndices(); + + LoadingScreen("Loading the Game", "Setup water", nil); + CWaterLevel::Initialise("DATA\\WATER.DAT"); + TheConsole.Init(); + CDraw::SetFOV(120.0f); + CDraw::ms_fLODDistance = 500.0f; + + LoadingScreen("Loading the Game", "Setup streaming", nil); + CStreaming::LoadInitialVehicles(); + CStreaming::LoadInitialPeds(); + CStreaming::RequestBigBuildings(LEVEL_GENERIC); + CStreaming::LoadAllRequestedModels(false); + CStreaming::RemoveIslandsNotUsed(currLevel); + printf("Streaming uses %zuK of its memory", CStreaming::ms_memoryUsed / 1024); // original modifier was %d + + LoadingScreen("Loading the Game", "Load animations", GetRandomSplashScreen()); + PUSH_MEMID(MEMID_ANIMATION); + CAnimManager::LoadAnimFiles(); + POP_MEMID(); + + CStreaming::LoadInitialWeapons(); + CStreaming::LoadAllRequestedModels(0); + CPed::Initialise(); + CRouteNode::Initialise(); + CEventList::Initialise(); +#ifdef SCREEN_DROPLETS + ScreenDroplets::Initialise(); +#endif + LoadingScreen("Loading the Game", "Find big buildings", nil); + CRenderer::Init(); + + LoadingScreen("Loading the Game", "Setup game variables", nil); + CRadar::Initialise(); + CRadar::LoadTextures(); + CWeapon::InitialiseWeapons(); + + LoadingScreen("Loading the Game", "Setup traffic lights", nil); + CTrafficLights::ScanForLightsOnMap(); + CRoadBlocks::Init(); + + LoadingScreen("Loading the Game", "Setup game variables", nil); + CPopulation::Initialise(); + CWorld::PlayerInFocus = 0; + CCoronas::Init(); + CShadows::Init(); + CWeaponEffects::Init(); + CSkidmarks::Init(); + CAntennas::Init(); + CGlass::Init(); + gPhoneInfo.Initialise(); +#ifdef GTA_SCENE_EDIT + CSceneEdit::Initialise(); +#endif + + LoadingScreen("Loading the Game", "Load scripts", nil); + PUSH_MEMID(MEMID_SCRIPT); + CTheScripts::Init(); + CGangs::Initialise(); + POP_MEMID(); + + LoadingScreen("Loading the Game", "Setup game variables", nil); + CClock::Initialise(1000); + CHeli::InitHelis(); + CCranes::InitCranes(); + CMovingThings::Init(); + CDarkel::Init(); + CStats::Init(); + CPacManPickups::Init(); + CRubbish::Init(); + CClouds::Init(); + CSpecialFX::Init(); + CRopes::Init(); + CWaterCannons::Init(); + CBridge::Init(); + CGarages::Init(); + + LoadingScreen("Loading the Game", "Position dynamic objects", nil); + LoadingScreen("Loading the Game", "Initialise vehicle paths", nil); + + CTrain::InitTrains(); + CPlane::InitPlanes(); + CCredits::Init(); + CRecordDataForChase::Init(); + CReplay::Init(); + + LoadingScreen("Loading the Game", "Start script", nil); +#ifdef PS2_MENU + if ( !TheMemoryCard.m_bWantToLoad ) +#endif + { + CTheScripts::StartTestScript(); + CTheScripts::Process(); + TheCamera.Process(); + } + + LoadingScreen("Loading the Game", "Load scene", nil); + CCollision::ms_collisionInMemory = currLevel; + for (int i = 0; i < MAX_PADS; i++) + CPad::GetPad(i)->Clear(true); +#ifdef USE_TEXTURE_POOL + _TexturePoolsUnknown(true); +#endif + +#ifndef MASTER + PlayerCoords = FindPlayerCoors(); + VarConsole.Add("X PLAYER COORD", &PlayerCoords.x, 10.0f, -10000.0f, 10000.0f, true); + VarConsole.Add("Y PLAYER COORD", &PlayerCoords.y, 10.0f, -10000.0f, 10000.0f, true); + VarConsole.Add("Z PLAYER COORD", &PlayerCoords.z, 10.0f, -10000.0f, 10000.0f, true); + VarConsole.Add("UPDATE PLAYER COORD", &VarUpdatePlayerCoords, true); +#endif + + + DMAudio.SetStartingTrackPositions(TRUE); + DMAudio.ChangeMusicMode(MUSICMODE_GAME); + return true; +} + +bool CGame::ShutDown(void) +{ +#ifdef USE_TEXTURE_POOL + _TexturePoolsUnknown(false); +#endif + CReplay::FinishPlayback(); + CReplay::EmptyReplayBuffer(); + CPlane::Shutdown(); + CTrain::Shutdown(); + CScriptPaths::Shutdown(); + CWaterCreatures::RemoveAll(); + CSpecialFX::Shutdown(); + CGarages::Shutdown(); + CMovingThings::Shutdown(); + gPhoneInfo.Shutdown(); + CWeapon::ShutdownWeapons(); + CPedType::Shutdown(); + + for (int32 i = 0; i < NUMPLAYERS; i++) + { + if ( CWorld::Players[i].m_pPed ) + { + CWorld::Remove(CWorld::Players[i].m_pPed); + delete CWorld::Players[i].m_pPed; + CWorld::Players[i].m_pPed = nil; + } + + CWorld::Players[i].Clear(); + } + + CRenderer::Shutdown(); + CWorld::ShutDown(); + DMAudio.DestroyAllGameCreatedEntities(); + CModelInfo::ShutDown(); + CAnimManager::Shutdown(); + CCutsceneMgr::Shutdown(); + CVehicleModelInfo::DeleteVehicleColourTextures(); + CVehicleModelInfo::ShutdownEnvironmentMaps(); + CRadar::Shutdown(); + CStreaming::Shutdown(); + CTxdStore::GameShutdown(); + CCollision::Shutdown(); + CWaterLevel::Shutdown(); + CRubbish::Shutdown(); + CClouds::Shutdown(); + CShadows::Shutdown(); + CCoronas::Shutdown(); + CSkidmarks::Shutdown(); + CWeaponEffects::Shutdown(); + CParticle::Shutdown(); + CPools::ShutDown(); + CHud::ReInitialise(); + CTxdStore::RemoveTxdSlot(gameTxdSlot); + CMBlur::MotionBlurClose(); + CdStreamRemoveImages(); +#ifdef USE_TEXTURE_POOL + _TexturePoolsFinalShutdown(); +#endif + return true; +} + +void CGame::ReInitGameObjectVariables(void) +{ + CGameLogic::InitAtStartOfGame(); +#ifdef PS2_MENU + if ( !TheMemoryCard.m_bWantToLoad ) +#endif + { + TheCamera.Init(); + TheCamera.SetRwCamera(Scene.camera); + } + CDebug::DebugInitTextBuffer(); + CWeather::Init(); + CUserDisplay::Init(); + CMessages::Init(); + CRestart::Initialise(); + CWorld::bDoingCarCollisions = false; + CHud::ReInitialise(); + CRadar::Initialise(); + CCarCtrl::ReInit(); + CTimeCycle::Initialise(); + CDraw::SetFOV(120.0f); + CDraw::ms_fLODDistance = 500.0f; + CStreaming::RequestBigBuildings(LEVEL_GENERIC); + CStreaming::RemoveIslandsNotUsed(LEVEL_BEACH); + CStreaming::RemoveIslandsNotUsed(LEVEL_MAINLAND); + CStreaming::LoadAllRequestedModels(false); + currArea = AREA_MAIN_MAP; + CPed::Initialise(); + CEventList::Initialise(); +#ifdef SCREEN_DROPLETS + ScreenDroplets::Initialise(); +#endif + CWeapon::InitialiseWeapons(); + CPopulation::Initialise(); + + for (int i = 0; i < NUMPLAYERS; i++) + CWorld::Players[i].Clear(); + + CWorld::PlayerInFocus = 0; + CAntennas::Init(); + CGlass::Init(); + gPhoneInfo.Initialise(); + + PUSH_MEMID(MEMID_SCRIPT); + CTheScripts::Init(); + CGangs::Initialise(); + POP_MEMID(); + + CTimer::Initialise(); + CClock::Initialise(1000); + CTheCarGenerators::Init(); + CHeli::InitHelis(); + CMovingThings::Init(); + CDarkel::Init(); + CStats::Init(); + CPickups::Init(); + CPacManPickups::Init(); + CGarages::Init(); + CSpecialFX::Init(); + CRopes::Init(); + CWaterCannons::Init(); + CScriptPaths::Init(); + CParticle::ReloadConfig(); + +#ifdef PS2_MENU + if ( !TheMemoryCard.m_bWantToLoad ) +#else + if ( !FrontEndMenuManager.m_bWantToLoad ) +#endif + { + CCranes::InitCranes(); + CTheScripts::StartTestScript(); + CTheScripts::Process(); + TheCamera.Process(); + CTrain::InitTrains(); + CPlane::InitPlanes(); + } + + for (int32 i = 0; i < MAX_PADS; i++) + CPad::GetPad(i)->Clear(true); +} + +void CGame::ReloadIPLs(void) +{ + // Empty and unused +} + +void CGame::ShutDownForRestart(void) +{ +#ifdef USE_TEXTURE_POOL + _TexturePoolsUnknown(false); +#endif + CReplay::FinishPlayback(); + CReplay::EmptyReplayBuffer(); + DMAudio.DestroyAllGameCreatedEntities(); + CMovingThings::Shutdown(); + + for (int i = 0; i < NUMPLAYERS; i++) + CWorld::Players[i].Clear(); + + CGarages::SetAllDoorsBackToOriginalHeight(); + CTheScripts::UndoBuildingSwaps(); + CTheScripts::UndoEntityInvisibilitySettings(); + CWorld::ClearForRestart(); + CGameLogic::ClearShortCut(); + CTimer::Shutdown(); + CStreaming::ReInit(); + CRadar::RemoveRadarSections(); + FrontEndMenuManager.UnloadTextures(); + CParticleObject::RemoveAllExpireableParticleObjects(); + CWaterCreatures::RemoveAll(); + CSetPieces::Init(); + CPedType::Shutdown(); + CSpecialFX::Shutdown(); +} + +void CGame::InitialiseWhenRestarting(void) +{ + CRect rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + CRGBA color(255, 255, 255, 255); + + CTimer::Initialise(); + CSprite2d::SetRecipNearClip(); + + if (b_FoundRecentSavedGameWantToLoad || FrontEndMenuManager.m_bWantToLoad) + { + LoadSplash("splash1"); +#ifndef XBOX_MESSAGE_SCREEN + if (FrontEndMenuManager.m_bWantToLoad) + FrontEndMenuManager.MessageScreen("FELD_WR", true); +#endif + } + + b_FoundRecentSavedGameWantToLoad = false; + + TheCamera.Init(); + + if ( FrontEndMenuManager.m_bWantToLoad == true ) + { +#ifdef XBOX_MESSAGE_SCREEN + FrontEndMenuManager.SetDialogTimer(1000); + DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 0); + CSprite2d::InitPerFrame(); + CFont::InitPerFrame(); + FrontEndMenuManager.DrawOverlays(); + DoRWStuffEndOfFrame(); +#endif + RestoreForStartLoad(); + } + + ReInitGameObjectVariables(); + + if ( FrontEndMenuManager.m_bWantToLoad == true ) + { + FrontEndMenuManager.m_bWantToLoad = false; + InitRadioStationPositionList(); + if ( GenericLoad() == true ) + { + DMAudio.ResetTimers(CTimer::GetTimeInMilliseconds()); + CTrain::InitTrains(); + CPlane::InitPlanes(); + } + else + { + for ( int32 i = 0; i < 50; i++ ) + { + HandleExit(); + FrontEndMenuManager.MessageScreen("FED_LFL", true); // Loading save game has failed. The game will restart now. + } + + TheCamera.SetFadeColour(0, 0, 0); + ShutDownForRestart(); + CTimer::Stop(); + CTimer::Initialise(); + FrontEndMenuManager.m_bWantToLoad = false; + ReInitGameObjectVariables(); + currLevel = LEVEL_GENERIC; + CCollision::SortOutCollisionAfterLoad(); + } +#ifdef XBOX_MESSAGE_SCREEN + FrontEndMenuManager.ProcessDialogTimer(); +#endif + } + + CTimer::Update(); + + DMAudio.ChangeMusicMode(MUSICMODE_GAME); +#ifdef USE_TEXTURE_POOL + _TexturePoolsUnknown(true); +#endif +} + +void CGame::Process(void) +{ + CPad::UpdatePads(); +#ifdef USE_CUSTOM_ALLOCATOR + ProcessTidyUpMemory(); +#endif +#ifdef DEBUGMENU + DebugMenuProcess(); +#endif + CCutsceneMgr::Update(); + + if (!CCutsceneMgr::IsCutsceneProcessing() && !CTimer::GetIsCodePaused()) + FrontEndMenuManager.Process(); + + CTheZones::Update(); +#ifdef SECUROM + if (CTimer::GetTimeInMilliseconds() >= (35 * 60 * 1000) && gameProcessPirateCheck == 0){ + // if game not pirated + // gameProcessPirateCheck = 1; + // else + gameProcessPirateCheck = 2; + } +#endif + uint32 startTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); + CStreaming::Update(); + uint32 processTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond() - startTime; + CWindModifiers::Number = 0; + if (!CTimer::GetIsPaused()) + { +#ifndef MASTER + if (VarUpdatePlayerCoords) { + FindPlayerPed()->Teleport(PlayerCoords); + VarUpdatePlayerCoords = false; + } +#endif + CSprite2d::SetRecipNearClip(); + CSprite2d::InitPerFrame(); + CFont::InitPerFrame(); + CRecordDataForGame::SaveOrRetrieveDataForThisFrame(); + CRecordDataForChase::SaveOrRetrieveDataForThisFrame(); + CPad::DoCheats(); + CClock::Update(); + CWeather::Update(); + + PUSH_MEMID(MEMID_SCRIPT); + CTheScripts::Process(); + POP_MEMID(); + + CCollision::Update(); + CScriptPaths::Update(); + CTrain::UpdateTrains(); + CPlane::UpdatePlanes(); + CHeli::UpdateHelis(); + CDarkel::Update(); + CSkidmarks::Update(); + CAntennas::Update(); + CGlass::Update(); +#ifdef GTA_SCENE_EDIT + CSceneEdit::Update(); +#endif + CSetPieces::Update(); + CEventList::Update(); + CParticle::Update(); + gFireManager.Update(); + + // Otherwise even on 30 fps most probably you won't see any peds around Ocean View Hospital +#if defined FIX_BUGS && !defined SQUEEZE_PERFORMANCE + if (processTime > 2) { +#else + if (processTime >= 2) { +#endif + CPopulation::Update(false); + } else { + uint32 startTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); + CPopulation::Update(true); + processTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond() - startTime; + } + CWeapon::UpdateWeapons(); + if (!CCutsceneMgr::IsRunning()) + CTheCarGenerators::Process(); + if (!CReplay::IsPlayingBack()) + CCranes::UpdateCranes(); + CClouds::Update(); + CMovingThings::Update(); + CWaterCannons::Update(); + CUserDisplay::Process(); + CReplay::Update(); + + PUSH_MEMID(MEMID_WORLD); + CWorld::Process(); + POP_MEMID(); + + gAccidentManager.Update(); + CPacManPickups::Update(); + CPickups::Update(); + CGarages::Update(); + CRubbish::Update(); + CSpecialFX::Update(); + CRopes::Update(); + CTimeCycle::Update(); + if (CReplay::ShouldStandardCameraBeProcessed()) + TheCamera.Process(); + CCullZones::Update(); + if (!CReplay::IsPlayingBack()) + CGameLogic::Update(); + CBridge::Update(); + CCoronas::DoSunAndMoon(); + CCoronas::Update(); + CShadows::UpdateStaticShadows(); + CShadows::UpdatePermanentShadows(); + gPhoneInfo.Update(); + if (!CReplay::IsPlayingBack()) + { + PUSH_MEMID(MEMID_CARS); + if (processTime < 2) + CCarCtrl::GenerateRandomCars(); + CRoadBlocks::GenerateRoadBlocks(); + CCarCtrl::RemoveDistantCars(); + CCarCtrl::RemoveCarsIfThePoolGetsFull(); + POP_MEMID(); + } + } +#ifdef GTA_PS2 + CMemCheck::DoTest(); +#endif +} + +#ifdef USE_CUSTOM_ALLOCATOR + +// TODO(MIAMI) + +int32 gNumMemMoved; + +bool +MoveMem(void** ptr) +{ + if (*ptr) { + gNumMemMoved++; + void* newPtr = gMainHeap.MoveMemory(*ptr); + if (*ptr != newPtr) { + *ptr = newPtr; + return true; + } + } + return false; +} + +// Some convenience structs +struct SkyDataPrefix +{ + uint32 pktSize1; + uint32 data; // pointer to data as read from TXD + uint32 pktSize2; + uint32 unused; +}; + +struct DMAGIFUpload +{ + uint32 tag1_qwc, tag1_addr; // dmaref + uint32 nop1, vif_direct1; + + uint32 giftag[4]; + uint32 gs_bitbltbuf[4]; + + uint32 tag2_qwc, tag2_addr; // dmaref + uint32 nop2, vif_direct2; +}; + +// This is very scary. it depends on the exact memory layout of the DMA chains and whatnot +RwTexture* +MoveTextureMemoryCB(RwTexture* texture, void* pData) +{ +#ifdef GTA_PS2 + bool* pRet = (bool*)pData; + RwRaster* raster = RwTextureGetRaster(texture); + _SkyRasterExt* rasterExt = RASTEREXTFROMRASTER(raster); + if (raster->originalPixels == nil || // the raw data + raster->cpPixels == raster->originalPixels || // old format, can't handle it + rasterExt->dmaRefCount != 0 && rasterExt->dmaClrCount != 0) + return texture; + + // this is the allocated pointer we will move + SkyDataPrefix* prefix = (SkyDataPrefix*)raster->originalPixels; + DMAGIFUpload* uploads = (DMAGIFUpload*)(prefix + 1); + + // We have 4qw for each upload, + // i.e. for each buffer width of mip levels, + // and the palette if there is one. + // NB: this code does NOT support mipmaps! + // so we assume two uploads (pixels and palette) + // + // each upload looks like this: + // (DMAcnt; NOP; VIF DIRECT(2)) + // giftag (1, A+D) + // GS_BITBLTBUF + // (DMAref->pixel data; NOP; VIF DIRECT(5)) + // the DMArefs are what we have to adjust + uintptr dataDiff, upload1Diff, upload2Diff, pixelDiff, paletteDiff; + dataDiff = prefix->data - (uintptr)raster->originalPixels; + upload1Diff = uploads[0].tag2_addr - (uintptr)raster->originalPixels; + if (raster->palette) + upload2Diff = uploads[1].tag2_addr - (uintptr)raster->originalPixels; + pixelDiff = (uintptr)raster->cpPixels - (uintptr)raster->originalPixels; + if (raster->palette) + paletteDiff = (uintptr)raster->palette - (uintptr)raster->originalPixels; + uint8* newptr = (uint8*)gMainHeap.MoveMemory(raster->originalPixels); + if (newptr != raster->originalPixels) { + // adjust everything + prefix->data = (uintptr)newptr + dataDiff; + uploads[0].tag2_addr = (uintptr)newptr + upload1Diff; + if (raster->palette) + uploads[1].tag2_addr = (uintptr)newptr + upload2Diff; + raster->originalPixels = newptr; + raster->cpPixels = newptr + pixelDiff; + if (raster->palette) + raster->palette = newptr + paletteDiff; + + if (pRet) { + *pRet = true; + return nil; + } + } +#else + // nothing to do here really, everything should be in videomemory +#endif + return texture; +} + +bool +MoveAtomicMemory(RpAtomic* atomic, bool onlyOne) +{ + RpGeometry* geo = RpAtomicGetGeometry(atomic); + +#if THIS_IS_COMPATIBLE_WITH_GTA3_RW31 + if (MoveMem((void**)&geo->triangles) && onlyOne) + return true; + if (MoveMem((void**)&geo->matList.materials) && onlyOne) + return true; + if (MoveMem((void**)&geo->preLitLum) && onlyOne) + return true; + if (MoveMem((void**)&geo->texCoords[0]) && onlyOne) + return true; + if (MoveMem((void**)&geo->texCoords[1]) && onlyOne) + return true; + + // verts and normals of morph target are allocated together + int vertDiff; + if (geo->morphTarget->normals) + vertDiff = geo->morphTarget->normals - geo->morphTarget->verts; + if (MoveMem((void**)&geo->morphTarget->verts)) { + if (geo->morphTarget->normals) + geo->morphTarget->normals = geo->morphTarget->verts + vertDiff; + if (onlyOne) + return true; + } + + RpMeshHeader* oldmesh = geo->mesh; + if (MoveMem((void**)&geo->mesh)) { + // index pointers are allocated together with meshes, + // have to relocate those too + RpMesh* mesh = (RpMesh*)(geo->mesh + 1); + uintptr reloc = (uintptr)geo->mesh - (uintptr)oldmesh; + for (int i = 0; i < geo->mesh->numMeshes; i++) + mesh[i].indices = (RxVertexIndex*)((uintptr)mesh[i].indices + reloc); + if (onlyOne) + return true; + } +#else + // we could do something in librw here +#endif + return false; +} + +bool +MoveColModelMemory(CColModel& colModel, bool onlyOne) +{ +#if GTA_VERSION >= GTA3_PS2_160 + // hm...should probably only do this if ownsCollisionVolumes + // but it doesn't exist on PS2... + if (!colModel.ownsCollisionVolumes) + return false; +#endif + + if (MoveMem((void**)&colModel.spheres) && onlyOne) + return true; + if (MoveMem((void**)&colModel.lines) && onlyOne) + return true; + if (MoveMem((void**)&colModel.boxes) && onlyOne) + return true; + if (MoveMem((void**)&colModel.vertices) && onlyOne) + return true; + if (MoveMem((void**)&colModel.triangles) && onlyOne) + return true; + if (MoveMem((void**)&colModel.trianglePlanes) && onlyOne) + return true; + return false; +} + +RpAtomic* +MoveAtomicMemoryCB(RpAtomic* atomic, void* pData) +{ + bool* pRet = (bool*)pData; + if (pRet == nil) + MoveAtomicMemory(atomic, false); + else if (MoveAtomicMemory(atomic, true)) { + *pRet = true; + return nil; + } + return atomic; +} + +bool +TidyUpModelInfo(CBaseModelInfo* modelInfo, bool onlyone) +{ + if (modelInfo->GetColModel() && modelInfo->DoesOwnColModel()) + if (MoveColModelMemory(*modelInfo->GetColModel(), onlyone)) + return true; + + RwObject* rwobj = modelInfo->GetRwObject(); + if (RwObjectGetType(rwobj) == rpATOMIC) + if (MoveAtomicMemory((RpAtomic*)rwobj, onlyone)) + return true; + if (RwObjectGetType(rwobj) == rpCLUMP) { + bool ret = false; + if (onlyone) + RpClumpForAllAtomics((RpClump*)rwobj, MoveAtomicMemoryCB, &ret); + else + RpClumpForAllAtomics((RpClump*)rwobj, MoveAtomicMemoryCB, nil); + if (ret) + return true; + } + + if (modelInfo->GetModelType() == MITYPE_PED && ((CPedModelInfo*)modelInfo)->m_hitColModel) + if (MoveColModelMemory(*((CPedModelInfo*)modelInfo)->m_hitColModel, onlyone)) + return true; + + return false; +} +#endif + + +void CGame::DrasticTidyUpMemory(bool flushDraw) +{ +#ifdef USE_CUSTOM_ALLOCATOR + bool removedCol = false; + + TidyUpMemory(true, flushDraw); + + if (gMainHeap.GetLargestFreeBlock() < 200000 && !playingIntro) { + CStreaming::RemoveIslandsNotUsed(LEVEL_INDUSTRIAL); + CStreaming::RemoveIslandsNotUsed(LEVEL_COMMERCIAL); + CStreaming::RemoveIslandsNotUsed(LEVEL_SUBURBAN); + TidyUpMemory(true, flushDraw); + } + + if (gMainHeap.GetLargestFreeBlock() < 200000 && !playingIntro) { + CModelInfo::RemoveColModelsFromOtherLevels(LEVEL_GENERIC); + TidyUpMemory(true, flushDraw); + removedCol = true; + } + + if (gMainHeap.GetLargestFreeBlock() < 200000 && !playingIntro) { + CStreaming::RemoveBigBuildings(LEVEL_INDUSTRIAL); + CStreaming::RemoveBigBuildings(LEVEL_COMMERCIAL); + CStreaming::RemoveBigBuildings(LEVEL_SUBURBAN); + TidyUpMemory(true, flushDraw); + } + + if (removedCol) { + // different on PS2 + CFileLoader::LoadCollisionFromDatFile(CCollision::ms_collisionInMemory); + } + + if (!playingIntro) + CStreaming::RequestBigBuildings(currLevel); + + CStreaming::LoadAllRequestedModels(true); +#endif +} + +void CGame::TidyUpMemory(bool moveTextures, bool flushDraw) +{ +#ifdef USE_CUSTOM_ALLOCATOR + printf("Largest free block before tidy %d\n", gMainHeap.GetLargestFreeBlock()); + + if (moveTextures) { + if (flushDraw) { +#ifdef GTA_PS2 + for (int i = 0; i < sweMaxFlips + 1; i++) { +#else + for (int i = 0; i < 5; i++) { // probably more than needed +#endif + RwCameraBeginUpdate(Scene.camera); + RwCameraEndUpdate(Scene.camera); + RwCameraShowRaster(Scene.camera, nil, 0); + } + } + int fontSlot = CTxdStore::FindTxdSlot("fonts"); + + for (int i = 0; i < TXDSTORESIZE; i++) { + if (i == fontSlot || + CTxdStore::GetSlot(i) == nil) + continue; + RwTexDictionary* txd = CTxdStore::GetSlot(i)->texDict; + if (txd) + RwTexDictionaryForAllTextures(txd, MoveTextureMemoryCB, nil); + } + } + + // animations + for (int i = 0; i < NUMANIMATIONS; i++) { + CAnimBlendHierarchy* anim = CAnimManager::GetAnimation(i); + if (anim == nil) + continue; // cannot happen + anim->MoveMemory(); + } + + // model info + for (int i = 0; i < MODELINFOSIZE; i++) { + CBaseModelInfo* mi = CModelInfo::GetModelInfo(i); + if (mi == nil) + continue; + TidyUpModelInfo(mi, false); + } + + printf("Largest free block after tidy %d\n", gMainHeap.GetLargestFreeBlock()); +#endif + } + +void CGame::ProcessTidyUpMemory(void) +{ +#ifdef USE_CUSTOM_ALLOCATOR + static int32 modelIndex = 0; + static int32 animIndex = 0; + static int32 txdIndex = 0; + bool txdReturn = false; + RwTexDictionary* txd = nil; + gNumMemMoved = 0; + + // model infos + for (int numCleanedUp = 0; numCleanedUp < 10; numCleanedUp++) { + CBaseModelInfo* mi; + do { + mi = CModelInfo::GetModelInfo(modelIndex); + modelIndex++; + if (modelIndex >= MODELINFOSIZE) + modelIndex = 0; + } while (mi == nil); + + if (TidyUpModelInfo(mi, true)) + return; + } + + // tex dicts + for (int numCleanedUp = 0; numCleanedUp < 3; numCleanedUp++) { + if (gNumMemMoved > 80) + break; + + do { +#ifdef FIX_BUGS + txd = nil; +#endif + if (CTxdStore::GetSlot(txdIndex)) + txd = CTxdStore::GetSlot(txdIndex)->texDict; + txdIndex++; + if (txdIndex >= TXDSTORESIZE) + txdIndex = 0; + } while (txd == nil); + + RwTexDictionaryForAllTextures(txd, MoveTextureMemoryCB, &txdReturn); + if (txdReturn) + return; + } + + // animations + CAnimBlendHierarchy* anim; + do { + anim = CAnimManager::GetAnimation(animIndex); + animIndex++; + if (animIndex >= NUMANIMATIONS) + animIndex = 0; + } while (anim == nil); // always != nil + anim->MoveMemory(true); +#endif +} + +void +CGame::InitAfterFocusLoss() +{ + FrontEndMenuManager.m_nPrefsAudio3DProviderIndex = FrontEndMenuManager.m_lastWorking3DAudioProvider; + DMAudio.SetCurrent3DProvider(FrontEndMenuManager.m_lastWorking3DAudioProvider); + + if (!FrontEndMenuManager.m_bGameNotLoaded && !FrontEndMenuManager.m_bMenuActive) + FrontEndMenuManager.m_bStartUpFrontEndRequested = true; +} + +bool +CGame::CanSeeWaterFromCurrArea(void) +{ + return currArea == AREA_MAIN_MAP || currArea == AREA_MANSION + || currArea == AREA_HOTEL; +} + +bool +CGame::CanSeeOutSideFromCurrArea(void) +{ + return currArea == AREA_MAIN_MAP || currArea == AREA_MALL || + currArea == AREA_MANSION || currArea == AREA_HOTEL; +} diff --git a/src/miami/core/Game.h b/src/miami/core/Game.h new file mode 100644 index 00000000..4052eb00 --- /dev/null +++ b/src/miami/core/Game.h @@ -0,0 +1,81 @@ +#pragma once + +enum eLevelName { + LEVEL_IGNORE = -1, // beware, this is only used in CPhysical's m_nZoneLevel + LEVEL_GENERIC = 0, + LEVEL_BEACH, + LEVEL_MAINLAND, + + NUM_LEVELS +}; + +enum eAreaName { + AREA_MAIN_MAP, + AREA_HOTEL, + AREA_MANSION, + AREA_BANK, + AREA_MALL, + AREA_STRIP_CLUB, + AREA_LAWYERS, + AREA_COFFEE_SHOP, + AREA_CONCERT_HALL, + AREA_STUDIO, + AREA_RIFLE_RANGE, + AREA_BIKER_BAR, + AREA_POLICE_STATION, + AREA_EVERYWHERE, + AREA_DIRT, + AREA_BLOOD, + AREA_OVALRING, + AREA_MALIBU_CLUB, + AREA_PRINT_WORKS +}; + +class CGame +{ +public: + static eLevelName currLevel; + static int32 currArea; + static bool bDemoMode; + static bool nastyGame; + static bool frenchGame; + static bool germanGame; +#ifdef MORE_LANGUAGES + static bool russianGame; + static bool japaneseGame; +#endif + static bool noProstitutes; + static bool playingIntro; + static char aDatFile[32]; + +#ifndef MASTER + static CVector PlayerCoords; + static bool8 VarUpdatePlayerCoords; +#endif + + static bool InitialiseOnceBeforeRW(void); + static bool InitialiseRenderWare(void); + static void ShutdownRenderWare(void); + static bool InitialiseOnceAfterRW(void); + static void FinalShutdown(void); + static bool Initialise(const char *datFile); + static bool ShutDown(void); + static void ReInitGameObjectVariables(void); + static void ReloadIPLs(void); + static void ShutDownForRestart(void); + static void InitialiseWhenRestarting(void); + static void Process(void); + + static void InitAfterFocusLoss(void); + + static bool IsInInterior(void) { return currArea != AREA_MAIN_MAP; } + static bool CanSeeWaterFromCurrArea(void); + static bool CanSeeOutSideFromCurrArea(void); + + // NB: these do something on PS2 + static void TidyUpMemory(bool, bool); + static void DrasticTidyUpMemory(bool); + static void ProcessTidyUpMemory(void); +}; + +inline bool IsAreaVisible(int area) { return area == CGame::currArea || area == AREA_EVERYWHERE; } diff --git a/src/miami/core/General.h b/src/miami/core/General.h new file mode 100644 index 00000000..1da18cfd --- /dev/null +++ b/src/miami/core/General.h @@ -0,0 +1,180 @@ +#pragma once + +#include + +class CGeneral +{ +public: + static float GetATanOfXY(float x, float y){ + if(x == 0.0f && y == 0.0f) + return 0.0f; + float xabs = Abs(x); + float yabs = Abs(y); + + if(xabs < yabs){ + if(y > 0.0f){ + if(x > 0.0f) + return 0.5f*PI - Atan2(x / y, 1.0f); + else + return 0.5f*PI + Atan2(-x / y, 1.0f); + }else{ + if(x > 0.0f) + return 1.5f*PI + Atan2(x / -y, 1.0f); + else + return 1.5f*PI - Atan2(-x / -y, 1.0f); + } + }else{ + if(y > 0.0f){ + if(x > 0.0f) + return Atan2(y / x, 1.0f); + else + return PI - Atan2(y / -x, 1.0f); + }else{ + if(x > 0.0f) + return 2.0f*PI - Atan2(-y / x, 1.0f); + else + return PI + Atan2(-y / -x, 1.0f); + } + } + } + + static float LimitAngle(float angle) + { + float result = angle; + + while (result >= 180.0f) { + result -= 2 * 180.0f; + } + + while (result < -180.0f) { + result += 2 * 180.0f; + } + + return result; + } + + + static float LimitRadianAngle(float angle) + { + float result = Clamp(angle, -25.0f, 25.0f); + + while (result >= PI) { + result -= 2 * PI; + } + + while (result < -PI) { + result += 2 * PI; + } + + return result; + } + + // Returns an angle such that x2/y2 looks at x1/y1 with its forward vector if rotated by that angle + static float GetRadianAngleBetweenPoints(float x1, float y1, float x2, float y2) + { + float x = x2 - x1; + float y = y2 - y1; + + if (y == 0.0f) + y = 0.0001f; + + if (x > 0.0f) { + if (y > 0.0f) + return PI - Atan2(x / y, 1.0f); + else + return -Atan2(x / y, 1.0f); + } else { + if (y > 0.0f) + return -(PI + Atan2(x / y, 1.0f)); + else + return -Atan2(x / y, 1.0f); + } + } + + static float GetAngleBetweenPoints(float x1, float y1, float x2, float y2) + { + return RADTODEG(GetRadianAngleBetweenPoints(x1, y1, x2, y2)); + } + + // should return direction in 0-8 range. fits perfectly to peds' path directions. + static int GetNodeHeadingFromVector(float x, float y) + { + float angle = CGeneral::GetRadianAngleBetweenPoints(x, y, 0.0f, 0.0f); + if (angle < 0.0f) + angle += TWOPI; + + angle = DEGTORAD(22.5f) + TWOPI - angle; + + if (angle >= TWOPI) + angle -= TWOPI; + + return (int)Floor(angle / DEGTORAD(45.0f)); + } + + // Unlike usual string comparison functions, these don't care about greater or lesser + static bool faststrcmp(const char *str1, const char *str2) + { + for (; *str1; str1++, str2++) { + if (*str1 != *str2) + return true; + } + return *str2 != '\0'; + } + + static bool faststrncmp(const char *str1, const char *str2, uint32 count) + { + for(uint32 i = 0; *str1 && i < count; str1++, str2++, i++) { + if (*str1 != *str2) + return true; + } + return false; + } + + static bool faststricmp(const char *str1, const char *str2) + { + for (; *str1; str1++, str2++) { +#ifndef ASCII_STRCMP + if (toupper(*str1) != toupper(*str2)) +#else + if (__ascii_toupper(*str1) != __ascii_toupper(*str2)) +#endif + return true; + } + return *str2 != '\0'; + } + + static bool SolveQuadratic(float a, float b, float c, float &root1, float &root2) + { + float discriminant = b * b - 4.f * a * c; + if (discriminant < 0.f) + return false; + + float discriminantSqrt = Sqrt(discriminant); + root2 = (-b + discriminantSqrt) / (2.f * a); + root1 = (-b - discriminantSqrt) / (2.f * a); + return true; + } + + // not too sure about all these... + static uint16 GetRandomNumber(void) + { return myrand() & MYRAND_MAX; } + static bool GetRandomTrueFalse(void) + { return GetRandomNumber() < MYRAND_MAX / 2; } + // Probably don't want to ever reach high + static float GetRandomNumberInRange(float low, float high) + { return low + (high - low)*(GetRandomNumber()/float(MYRAND_MAX + 1)); } + + static int GetRandomNumberInRange(int low, int high) + { return low + (high - low)*(GetRandomNumber()/float(MYRAND_MAX + 1)); } + +#if !defined(INT32_IS_INT) + static int32 GetRandomNumberInRange(int32 low, int32 high) + { return low + (high - low)*(GetRandomNumber()/float(MYRAND_MAX + 1)); } + + static int32 GetRandomNumberInRange(int low, const int32& high) + { return low + (high - low)*(GetRandomNumber()/float(MYRAND_MAX + 1)); } +#endif + + static void SetRandomSeed(int32 seed) + { mysrand(seed); } +}; diff --git a/src/miami/core/IniFile.cpp b/src/miami/core/IniFile.cpp new file mode 100644 index 00000000..5d343ec9 --- /dev/null +++ b/src/miami/core/IniFile.cpp @@ -0,0 +1,29 @@ +#include "common.h" + +#include "IniFile.h" + +#include "CarCtrl.h" +#include "FileMgr.h" +#include "main.h" +#include "Population.h" + +float CIniFile::PedNumberMultiplier = 0.6f; +float CIniFile::CarNumberMultiplier = 0.6f; + +void CIniFile::LoadIniFile() +{ + CFileMgr::SetDir(""); + int f = CFileMgr::OpenFile("gta3.ini", "r"); + if (f){ + CFileMgr::ReadLine(f, gString, 200); + sscanf(gString, "%f", &PedNumberMultiplier); + PedNumberMultiplier = Min(3.0f, Max(0.5f, PedNumberMultiplier)); + CFileMgr::ReadLine(f, gString, 200); + sscanf(gString, "%f", &CarNumberMultiplier); + CarNumberMultiplier = Min(3.0f, Max(0.5f, CarNumberMultiplier)); + CFileMgr::CloseFile(f); + } + CPopulation::MaxNumberOfPedsInUse = DEFAULT_MAX_NUMBER_OF_PEDS * PedNumberMultiplier; + CPopulation::MaxNumberOfPedsInUseInterior = DEFAULT_MAX_NUMBER_OF_PEDS_INTERIOR * PedNumberMultiplier; + CCarCtrl::MaxNumberOfCarsInUse = DEFAULT_MAX_NUMBER_OF_CARS * CarNumberMultiplier; +} \ No newline at end of file diff --git a/src/miami/core/IniFile.h b/src/miami/core/IniFile.h new file mode 100644 index 00000000..dcaed980 --- /dev/null +++ b/src/miami/core/IniFile.h @@ -0,0 +1,14 @@ +#pragma once + +#define DEFAULT_MAX_NUMBER_OF_PEDS 25.0f +#define DEFAULT_MAX_NUMBER_OF_PEDS_INTERIOR 40.0f +#define DEFAULT_MAX_NUMBER_OF_CARS 12.0f + +class CIniFile +{ +public: + static void LoadIniFile(); + + static float PedNumberMultiplier; + static float CarNumberMultiplier; +}; diff --git a/src/miami/core/Lists.cpp b/src/miami/core/Lists.cpp new file mode 100644 index 00000000..448a0ff1 --- /dev/null +++ b/src/miami/core/Lists.cpp @@ -0,0 +1,26 @@ +#include "common.h" +#include "Pools.h" +#include "Lists.h" + +void* +CPtrNode::operator new(size_t){ + CPtrNode *node = CPools::GetPtrNodePool()->New(); + assert(node); + return node; +} + +void +CPtrNode::operator delete(void *p, size_t){ + CPools::GetPtrNodePool()->Delete((CPtrNode*)p); +} + +void* +CEntryInfoNode::operator new(size_t){ + CEntryInfoNode *node = CPools::GetEntryInfoNodePool()->New(); + assert(node); + return node; +} +void +CEntryInfoNode::operator delete(void *p, size_t){ + CPools::GetEntryInfoNodePool()->Delete((CEntryInfoNode*)p); +} diff --git a/src/miami/core/Lists.h b/src/miami/core/Lists.h new file mode 100644 index 00000000..7572e882 --- /dev/null +++ b/src/miami/core/Lists.h @@ -0,0 +1,130 @@ +#pragma once + +class CPtrNode +{ +public: + void *item; + CPtrNode *prev; + CPtrNode *next; + + void *operator new(size_t); + void operator delete(void *p, size_t); +}; + +class CPtrList +{ +public: + CPtrNode *first; + + CPtrList(void) { first = nil; } + ~CPtrList(void) { Flush(); } + CPtrNode *FindItem(void *item){ + CPtrNode *node; + for(node = first; node; node = node->next) + if(node->item == item) + return node; + return nil; + } + CPtrNode *InsertNode(CPtrNode *node){ + node->prev = nil; + node->next = first; + if(first) + first->prev = node; + first = node; + return node; + } + CPtrNode *InsertItem(void *item){ + CPtrNode *node = new CPtrNode; + node->item = item; + InsertNode(node); + return node; + } + void RemoveNode(CPtrNode *node){ + if(node == first) + first = node->next; + if(node->prev) + node->prev->next = node->next; + if(node->next) + node->next->prev = node->prev; + } + void DeleteNode(CPtrNode *node){ + RemoveNode(node); + delete node; + } + void RemoveItem(void *item){ + CPtrNode *node, *next; + for(node = first; node; node = next){ + next = node->next; + if(node->item == item) + DeleteNode(node); + } + } + void Flush(void){ + CPtrNode *node, *next; + for(node = first; node; node = next){ + next = node->next; + DeleteNode(node); + } + } +}; + +class CSector; + +// This records in which sector list a Physical is +class CEntryInfoNode +{ +public: + CPtrList *list; // list in sector + CPtrNode *listnode; // node in list + CSector *sector; + + CEntryInfoNode *prev; + CEntryInfoNode *next; + + void *operator new(size_t); + void operator delete(void *p, size_t); +}; + +class CEntryInfoList +{ +public: + CEntryInfoNode *first; + + CEntryInfoList(void) { first = nil; } + ~CEntryInfoList(void) { Flush(); } + CEntryInfoNode *InsertNode(CEntryInfoNode *node){ + node->prev = nil; + node->next = first; + if(first) + first->prev = node; + first = node; + return node; + } + CEntryInfoNode *InsertItem(CPtrList *list, CPtrNode *listnode, CSector *sect){ + CEntryInfoNode *node = new CEntryInfoNode; + node->list = list; + node->listnode = listnode; + node->sector = sect; + InsertNode(node); + return node; + } + void RemoveNode(CEntryInfoNode *node){ + if(node == first) + first = node->next; + if(node->prev) + node->prev->next = node->next; + if(node->next) + node->next->prev = node->prev; + } + void DeleteNode(CEntryInfoNode *node){ + RemoveNode(node); + delete node; + } + void Flush(void){ + CEntryInfoNode *node, *next; + for(node = first; node; node = next){ + next = node->next; + DeleteNode(node); + } + } +}; diff --git a/src/miami/core/MenuScreens.cpp b/src/miami/core/MenuScreens.cpp new file mode 100644 index 00000000..295038d9 --- /dev/null +++ b/src/miami/core/MenuScreens.cpp @@ -0,0 +1,353 @@ +#include "common.h" +#include "Frontend.h" +#ifdef PC_MENU + +// Please don't touch this file, except for bug fixing or ports. +// Check MenuScreensCustom.cpp + +#ifndef CUSTOM_FRONTEND_OPTIONS +CMenuScreen aScreens[] = { + // MENUPAGE_STATS = 0 + { "FEH_STA", MENUPAGE_NONE, 3, + MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 190, 320, MENUALIGN_RIGHT, + }, + + // MENUPAGE_NEW_GAME = 1 + { "FEP_STG", MENUPAGE_NONE, 1, + MENUACTION_CHANGEMENU, "FES_NGA", SAVESLOT_NONE, MENUPAGE_NEW_GAME_RELOAD, 320, 155, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FES_LOA", SAVESLOT_NONE, MENUPAGE_CHOOSE_LOAD_SLOT, 0, 0, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FES_DEL", SAVESLOT_NONE, MENUPAGE_CHOOSE_DELETE_SLOT, 0, 0, MENUALIGN_CENTER, + MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, 0, 0, 0, MENUALIGN_CENTER, + }, + + // MENUPAGE_BRIEFS = 2 + { "FEH_BRI", MENUPAGE_NONE, 4, + MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 190, 320, MENUALIGN_RIGHT, + }, + + // MENUPAGE_SOUND_SETTINGS = 3 + { "FEH_AUD", MENUPAGE_OPTIONS, 1, + MENUACTION_MUSICVOLUME, "FEA_MUS", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, 40, 76, MENUALIGN_LEFT, + MENUACTION_SFXVOLUME, "FEA_SFX", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, 0, 0, MENUALIGN_LEFT, + MENUACTION_MP3VOLUMEBOOST, "FEA_MPB", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, 0, 0, MENUALIGN_LEFT, +#ifdef EXTERNAL_3D_SOUND + MENUACTION_AUDIOHW, "FEA_3DH", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, 0, 0, MENUALIGN_LEFT, + MENUACTION_SPEAKERCONF, "FEA_SPK", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, 0, 0, MENUALIGN_LEFT, +#endif + MENUACTION_DYNAMICACOUSTIC, "FET_DAM", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, 0, 0, MENUALIGN_LEFT, + MENUACTION_RADIO, "FEA_RSS", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, 0, 0, MENUALIGN_LEFT, +#ifdef EXTERNAL_3D_SOUND + MENUACTION_RESTOREDEF, "FET_DEF", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, 320, 367, MENUALIGN_CENTER, +#else + MENUACTION_RESTOREDEF, "FET_DEF", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, 320, 327, MENUALIGN_CENTER, +#endif + MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, MENUALIGN_CENTER, + }, + + // MENUPAGE_DISPLAY_SETTINGS = 4 +#ifdef LEGACY_MENU_OPTIONS + #define Y_OFFSET 50 +#else + #define Y_OFFSET 0 +#endif + + { "FEH_DIS", MENUPAGE_OPTIONS, 2, + MENUACTION_BRIGHTNESS, "FED_BRI", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 78, MENUALIGN_LEFT, + MENUACTION_DRAWDIST, "FEM_LOD", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 103, MENUALIGN_LEFT, +#ifdef LEGACY_MENU_OPTIONS + MENUACTION_FRAMESYNC, "FEM_VSC", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 128, MENUALIGN_LEFT, +#endif + MENUACTION_FRAMELIMIT, "FEM_FRM", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 128 + Y_OFFSET/2, MENUALIGN_LEFT, +#ifdef LEGACY_MENU_OPTIONS + MENUACTION_TRAILS, "FED_TRA", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 178, MENUALIGN_LEFT, +#endif + MENUACTION_SUBTITLES, "FED_SUB", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 153 + Y_OFFSET, MENUALIGN_LEFT, + MENUACTION_WIDESCREEN, "FED_WIS", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 178 + Y_OFFSET, MENUALIGN_LEFT, + MENUACTION_LEGENDS, "MAP_LEG", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 202 + Y_OFFSET, MENUALIGN_LEFT, + MENUACTION_RADARMODE, "FED_RDR", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 228 + Y_OFFSET, MENUALIGN_LEFT, + MENUACTION_HUD, "FED_HUD", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 253 + Y_OFFSET, MENUALIGN_LEFT, + MENUACTION_SCREENRES, "FED_RES", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 278 + Y_OFFSET, MENUALIGN_LEFT, + MENUACTION_RESTOREDEF, "FET_DEF", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 320, 303 + Y_OFFSET, MENUALIGN_CENTER, + MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 320, 328 + Y_OFFSET, MENUALIGN_CENTER, + }, + +#undef Y_OFFSET + + // MENUPAGE_LANGUAGE_SETTINGS = 5 + { "FEH_LAN", MENUPAGE_OPTIONS, 3, + MENUACTION_LANG_ENG, "FEL_ENG", SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS, 320, 132, MENUALIGN_CENTER, + MENUACTION_LANG_FRE, "FEL_FRE", SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS, 0, 0, MENUALIGN_CENTER, + MENUACTION_LANG_GER, "FEL_GER", SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS, 0, 0, MENUALIGN_CENTER, + MENUACTION_LANG_ITA, "FEL_ITA", SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS, 0, 0, MENUALIGN_CENTER, + MENUACTION_LANG_SPA, "FEL_SPA", SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS, 0, 0, MENUALIGN_CENTER, + MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, MENUALIGN_CENTER, + }, + + // MENUPAGE_MAP = 6 + { "FEH_MAP", MENUPAGE_NONE, 2, + MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 70, 380, MENUALIGN_CENTER, + }, + + // MENUPAGE_NEW_GAME_RELOAD = 7 + { "FES_NGA", MENUPAGE_NEW_GAME, 0, + MENUACTION_LABEL, "FESZ_QR", SAVESLOT_NONE, 0, 0, 0, 0, + MENUACTION_NO, "FEM_NO", SAVESLOT_NONE, MENUPAGE_NEW_GAME, 320, 200, MENUALIGN_CENTER, + MENUACTION_NEWGAME, "FEM_YES", SAVESLOT_NONE, MENUPAGE_NEW_GAME_RELOAD, 320, 225, MENUALIGN_CENTER, + }, + + // MENUPAGE_CHOOSE_LOAD_SLOT = 8 + { "FET_LG", MENUPAGE_NEW_GAME, 1, + MENUACTION_CHECKSAVE, "FEM_SL1", SAVESLOT_1, 0, 40, 90, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL2", SAVESLOT_2, 0, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL3", SAVESLOT_3, 0, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL4", SAVESLOT_4, 0, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL5", SAVESLOT_5, 0, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL6", SAVESLOT_6, 0, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL7", SAVESLOT_7, 0, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL8", SAVESLOT_8, 0, 0, 0, MENUALIGN_LEFT, + MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, 0, 320, 345, MENUALIGN_CENTER, + }, + + // MENUPAGE_CHOOSE_DELETE_SLOT = 9 + { "FES_DEL", MENUPAGE_NEW_GAME, 2, + MENUACTION_CHECKSAVE, "FEM_SL1", SAVESLOT_1, 0, 40, 90, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL2", SAVESLOT_2, 0, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL3", SAVESLOT_3, 0, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL4", SAVESLOT_4, 0, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL5", SAVESLOT_5, 0, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL6", SAVESLOT_6, 0, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL7", SAVESLOT_7, 0, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL8", SAVESLOT_8, 0, 0, 0, MENUALIGN_LEFT, + MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, 0, 320, 345, MENUALIGN_CENTER, + }, + + // MENUPAGE_LOAD_SLOT_CONFIRM = 10 + { "FET_LG", MENUPAGE_CHOOSE_LOAD_SLOT, 0, + MENUACTION_LABEL, "FESZ_QL", SAVESLOT_NONE, 0, 0, 0, 0, + MENUACTION_NO, "FEM_NO", SAVESLOT_NONE, MENUPAGE_CHOOSE_LOAD_SLOT, 320, 200, MENUALIGN_CENTER, + MENUACTION_YES, "FEM_YES", SAVESLOT_NONE, MENUPAGE_LOADING_IN_PROGRESS, 320, 225, MENUALIGN_CENTER, + }, + + // MENUPAGE_DELETE_SLOT_CONFIRM = 11 + { "FES_DEL", MENUPAGE_CHOOSE_DELETE_SLOT, 0, + MENUACTION_LABEL, "FESZ_QD", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, + MENUACTION_NO, "FEM_NO", SAVESLOT_NONE, MENUPAGE_CHOOSE_DELETE_SLOT, 320, 200, MENUALIGN_CENTER, + MENUACTION_YES, "FEM_YES", SAVESLOT_NONE, MENUPAGE_DELETING_IN_PROGRESS, 320, 225, MENUALIGN_CENTER, + }, + + // MENUPAGE_LOADING_IN_PROGRESS = 12 + { "FET_LG", MENUPAGE_CHOOSE_LOAD_SLOT, 0, + }, + + // MENUPAGE_DELETING_IN_PROGRESS = 13 + { "FES_DEL", MENUPAGE_CHOOSE_DELETE_SLOT, 0, + }, + + // MENUPAGE_DELETE_SUCCESSFUL = 14 + { "FES_DEL", MENUPAGE_NEW_GAME, 0, + MENUACTION_LABEL, "FES_DSC", SAVESLOT_NONE, 0, 0, 0, 0, + MENUACTION_CHANGEMENU, "FEM_OK", SAVESLOT_NONE, MENUPAGE_NEW_GAME, 320, 225, MENUALIGN_CENTER, + }, + + // MENUPAGE_CHOOSE_SAVE_SLOT = 15 + { "FET_SG", MENUPAGE_DISABLED, 0, + MENUACTION_SAVEGAME, "FEM_SL1", SAVESLOT_1, MENUPAGE_SAVE_OVERWRITE_CONFIRM, 40, 90, MENUALIGN_LEFT, + MENUACTION_SAVEGAME, "FEM_SL2", SAVESLOT_2, MENUPAGE_SAVE_OVERWRITE_CONFIRM, 0, 0, MENUALIGN_LEFT, + MENUACTION_SAVEGAME, "FEM_SL3", SAVESLOT_3, MENUPAGE_SAVE_OVERWRITE_CONFIRM, 0, 0, MENUALIGN_LEFT, + MENUACTION_SAVEGAME, "FEM_SL4", SAVESLOT_4, MENUPAGE_SAVE_OVERWRITE_CONFIRM, 0, 0, MENUALIGN_LEFT, + MENUACTION_SAVEGAME, "FEM_SL5", SAVESLOT_5, MENUPAGE_SAVE_OVERWRITE_CONFIRM, 0, 0, MENUALIGN_LEFT, + MENUACTION_SAVEGAME, "FEM_SL6", SAVESLOT_6, MENUPAGE_SAVE_OVERWRITE_CONFIRM, 0, 0, MENUALIGN_LEFT, + MENUACTION_SAVEGAME, "FEM_SL7", SAVESLOT_7, MENUPAGE_SAVE_OVERWRITE_CONFIRM, 0, 0, MENUALIGN_LEFT, + MENUACTION_SAVEGAME, "FEM_SL8", SAVESLOT_8, MENUPAGE_SAVE_OVERWRITE_CONFIRM, 0, 0, MENUALIGN_LEFT, + MENUACTION_RESUME_FROM_SAVEZONE,"FESZ_CA", SAVESLOT_NONE, 0, 320, 345, MENUALIGN_CENTER, + }, + + // MENUPAGE_SAVE_OVERWRITE_CONFIRM = 16 + { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, 0, + MENUACTION_LABEL, "FESZ_QZ", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, + MENUACTION_NO, "FEM_NO", SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT, 320, 200, MENUALIGN_CENTER, + MENUACTION_YES, "FEM_YES", SAVESLOT_NONE, MENUPAGE_SAVING_IN_PROGRESS, 320, 225, MENUALIGN_CENTER, + }, + + // MENUPAGE_SAVING_IN_PROGRESS = 17 + { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, 0, + }, + + // MENUPAGE_SAVE_SUCCESSFUL = 18 + { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, 0, + MENUACTION_LABEL, "FES_SSC", SAVESLOT_LABEL, MENUPAGE_NONE, 0, 0, 0, + MENUACTION_RESUME_FROM_SAVEZONE, "FEM_OK", SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT, 320, 225, MENUALIGN_CENTER, + }, + + // MENUPAGE_SAVE_CUSTOM_WARNING = 19 + { "FET_SG", MENUPAGE_NONE, 0, + MENUACTION_LABEL, "", SAVESLOT_NONE, 0, 0, 0, 0, + MENUACTION_CHANGEMENU, "FEM_OK", SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT, 320, 225, MENUALIGN_CENTER, + }, + + // MENUPAGE_SAVE_CHEAT_WARNING = 20 + { "FET_SG", MENUPAGE_NEW_GAME, 0, + MENUACTION_LABEL, "FES_CHE", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, + MENUACTION_CHANGEMENU, "FEM_OK", SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT, 320, 225, MENUALIGN_CENTER, + }, + + // MENUPAGE_SKIN_SELECT = 21 + { "FET_PS", MENUPAGE_OPTIONS, 4, + MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_OPTIONS, 0, 0, 0, + }, + + // MENUPAGE_SAVE_UNUSED = 22 + { "FET_SG", MENUPAGE_NEW_GAME, 0, + MENUACTION_LABEL, "FED_LWR", SAVESLOT_NONE, 0, 0, 0, 0, + MENUACTION_CHANGEMENU, "FEC_OKK", SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT, 0, 0, 0, + }, + + // MENUPAGE_SAVE_FAILED = 23 + { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, 0, + MENUACTION_LABEL, "FEC_SVU", SAVESLOT_NONE, 0, 0, 0, 0, + MENUACTION_CHANGEMENU, "FEC_OKK", SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT, 0, 0, 0, + }, + + // MENUPAGE_SAVE_FAILED_2 = 24 + { "FET_LG", MENUPAGE_CHOOSE_SAVE_SLOT, 0, + MENUACTION_LABEL, "FEC_SVU", SAVESLOT_NONE, 0, 0, 0, 0, + }, + + // MENUPAGE_LOAD_FAILED = 25 + { "FET_LG", MENUPAGE_NEW_GAME, 0, + MENUACTION_LABEL, "FEC_LUN", SAVESLOT_NONE, 0, 0, 0, 0, + MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NEW_GAME, 0, 0, 0, + }, + + // MENUPAGE_CONTROLLER_PC = 26 + { "FET_CTL", MENUPAGE_OPTIONS, 0, +#ifdef PC_PLAYER_CONTROLS + MENUACTION_CTRLMETHOD, "FET_STI", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC, 320, 150, MENUALIGN_CENTER, + MENUACTION_KEYBOARDCTRLS,"FEC_RED", SAVESLOT_NONE, MENUPAGE_KEYBOARD_CONTROLS, 0, 0, MENUALIGN_CENTER, +#else + MENUACTION_KEYBOARDCTRLS,"FEC_RED", SAVESLOT_NONE, MENUPAGE_KEYBOARD_CONTROLS, 320, 150, MENUALIGN_CENTER, +#endif + MENUACTION_CHANGEMENU, "FEC_MOU", SAVESLOT_NONE, MENUPAGE_MOUSE_CONTROLS, 0, 0, MENUALIGN_CENTER, + MENUACTION_RESTOREDEF, "FET_DEF", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC, 0, 0, MENUALIGN_CENTER, + MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, 0, 0, 0, MENUALIGN_CENTER, + }, + + // MENUPAGE_OPTIONS = 27 + { "FET_OPT", MENUPAGE_NONE, 5, + MENUACTION_CHANGEMENU, "FEO_CON", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC, 320, 132, MENUALIGN_CENTER, + MENUACTION_LOADRADIO, "FEO_AUD", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, 0, 0, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FEO_DIS", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 0, 0, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FEO_LAN", SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS, 0, 0, MENUALIGN_CENTER, + MENUACTION_PLAYERSETUP, "FET_PS", SAVESLOT_NONE, MENUPAGE_SKIN_SELECT, 0, 0, MENUALIGN_CENTER, + MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, 0, 0, 0, MENUALIGN_CENTER, + }, + + // MENUPAGE_EXIT = 28 + { "FET_QG", MENUPAGE_NONE, 6, + MENUACTION_LABEL, "FEQ_SRE", SAVESLOT_NONE, 0, 0, 0, 0, + MENUACTION_DONTCANCEL, "FEM_NO", SAVESLOT_NONE, MENUPAGE_NONE, 320, 200, MENUALIGN_CENTER, + MENUACTION_CANCELGAME, "FEM_YES", SAVESLOT_NONE, MENUPAGE_NONE, 320, 225, MENUALIGN_CENTER, + }, + + // MENUPAGE_START_MENU = 29 + { "FEM_MM", MENUPAGE_DISABLED, 0, + MENUACTION_CHANGEMENU, "FEP_STG", SAVESLOT_NONE, MENUPAGE_NEW_GAME, 320, 170, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FEP_OPT", SAVESLOT_NONE, MENUPAGE_OPTIONS, 0, 0, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FEP_QUI", SAVESLOT_NONE, MENUPAGE_EXIT, 0, 0, MENUALIGN_CENTER, + }, + + // MENUPAGE_KEYBOARD_CONTROLS = 30 + { "FET_STI", MENUPAGE_CONTROLLER_PC, 1, + }, + + // MENUPAGE_MOUSE_CONTROLS = 31 + { "FEC_MOU", MENUPAGE_CONTROLLER_PC, 2, + MENUACTION_MOUSESENS, "FEC_MSH", SAVESLOT_NONE, MENUPAGE_MOUSE_CONTROLS, 40, 170, MENUALIGN_LEFT, + MENUACTION_INVVERT, "FEC_IVV", SAVESLOT_NONE, MENUPAGE_MOUSE_CONTROLS, 0, 0, MENUALIGN_LEFT, + MENUACTION_MOUSESTEER, "FET_MST", SAVESLOT_NONE, MENUPAGE_MOUSE_CONTROLS, 0, 0, MENUALIGN_LEFT, + MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, 0, 320, 260, MENUALIGN_CENTER, + }, + + // MENUPAGE_PAUSE_MENU = 32 + { "FET_PAU", MENUPAGE_DISABLED, 0, + MENUACTION_RESUME, "FEP_RES", SAVESLOT_NONE, 0, 320, 120, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FEH_SGA", SAVESLOT_NONE, MENUPAGE_NEW_GAME, 0, 0, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FEH_MAP", SAVESLOT_NONE, MENUPAGE_MAP, 0, 0, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FEP_STA", SAVESLOT_NONE, MENUPAGE_STATS, 0, 0, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FEH_BRI", SAVESLOT_NONE, MENUPAGE_BRIEFS, 0, 0, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FET_OPT", SAVESLOT_NONE, MENUPAGE_OPTIONS, 0, 0, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FEP_QUI", SAVESLOT_NONE, MENUPAGE_EXIT, 0, 0, MENUALIGN_CENTER, + }, + + // MENUPAGE_NONE = 33 + { "", 0, 0, }, + +#ifdef LEGACY_MENU_OPTIONS + // MENUPAGE_DEBUG_MENU + { "FED_DBG", MENUPAGE_NONE, 0, + MENUACTION_RELOADIDE, "FED_RID", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, + MENUACTION_SETDBGFLAG, "FED_DFL", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, + MENUACTION_SWITCHBIGWHITEDEBUGLIGHT, "FED_DLS", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, + MENUACTION_COLLISIONPOLYS, "FED_SCP", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, + MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, + }, + + // MENUPAGE_CONTROLLER_PC_OLD1 + { "FET_CTL", MENUPAGE_CONTROLLER_PC, 0, + MENUACTION_GETKEY, "FEC_PLB", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1, 0, 0, 0, + MENUACTION_GETKEY, "FEC_CWL", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1, 0, 0, 0, + MENUACTION_GETKEY, "FEC_CWR", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1, 0, 0, 0, + MENUACTION_GETKEY, "FEC_LKT", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1, 0, 0, 0, + MENUACTION_GETKEY, "FEC_PJP", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1, 0, 0, 0, + MENUACTION_GETKEY, "FEC_PSP", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1, 0, 0, 0, + MENUACTION_GETKEY, "FEC_TLF", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1, 0, 0, 0, + MENUACTION_GETKEY, "FEC_TRG", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1, 0, 0, 0, + MENUACTION_GETKEY, "FEC_CCM", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1, 0, 0, 0, + MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, + }, + + // MENUPAGE_CONTROLLER_PC_OLD2 + { "FET_CTL", MENUPAGE_CONTROLLER_PC, 1, + + }, + + // MENUPAGE_CONTROLLER_PC_OLD3 + { "FET_CTL", MENUPAGE_CONTROLLER_PC, 2, + MENUACTION_GETKEY, "FEC_LUP", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD3, 0, 0, 0, + MENUACTION_GETKEY, "FEC_LDN", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD3, 0, 0, 0, + MENUACTION_GETKEY, "FEC_SMS", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD3, 0, 0, 0, + MENUACTION_SHOWHEADBOB, "FEC_GSL", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD3, 0, 0, 0, + MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, + }, + + // MENUPAGE_CONTROLLER_PC_OLD4 + { "FET_CTL", MENUPAGE_CONTROLLER_PC, 3, + + }, + + // MENUPAGE_CONTROLLER_DEBUG + { "FEC_DBG", MENUPAGE_CONTROLLER_PC, 3, + MENUACTION_GETKEY, "FEC_TGD", SAVESLOT_NONE, MENUPAGE_CONTROLLER_DEBUG, 0, 0, 0, + MENUACTION_GETKEY, "FEC_TDO", SAVESLOT_NONE, MENUPAGE_CONTROLLER_DEBUG, 0, 0, 0, + MENUACTION_GETKEY, "FEC_TSS", SAVESLOT_NONE, MENUPAGE_CONTROLLER_DEBUG, 0, 0, 0, + MENUACTION_GETKEY, "FEC_SMS", SAVESLOT_NONE, MENUPAGE_CONTROLLER_DEBUG, 0, 0, 0, + MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, + }, +#endif + +#ifdef MISSION_REPLAY + // MENUPAGE_MISSION_RETRY = 57 on mobile + + { "M_FAIL", MENUPAGE_DISABLED, 0, + MENUACTION_LABEL, "FESZ_RM", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, + MENUACTION_CHANGEMENU, "FEM_YES", SAVESLOT_NONE, MENUPAGE_LOADING_IN_PROGRESS, 320, 200, MENUALIGN_CENTER, + MENUACTION_REJECT_RETRY, "FEM_NO", SAVESLOT_NONE, MENUPAGE_NONE, 320, 225, MENUALIGN_CENTER, + }, +#endif + + // MENUPAGE_OUTRO - Originally 34 + { "", 0, 0, }, +}; + +#endif +#endif diff --git a/src/miami/core/MenuScreensCustom.cpp b/src/miami/core/MenuScreensCustom.cpp new file mode 100644 index 00000000..2af06c89 --- /dev/null +++ b/src/miami/core/MenuScreensCustom.cpp @@ -0,0 +1,819 @@ +#include "common.h" +#if defined DETECT_JOYSTICK_MENU && defined XINPUT +#include +#include +#if !defined(PSAPI_VERSION) || (PSAPI_VERSION > 1) +#pragma comment( lib, "Xinput9_1_0.lib" ) +#else +#pragma comment( lib, "Xinput.lib" ) +#endif +#endif +#include "platform.h" +#include "crossplatform.h" +#include "Renderer.h" +#include "Frontend.h" +#include "Font.h" +#include "Camera.h" +#include "main.h" +#include "MBlur.h" +#include "postfx.h" +#include "custompipes.h" +#include "RwHelper.h" +#include "Text.h" +#include "Streaming.h" +#include "FileLoader.h" +#include "Collision.h" +#include "ModelInfo.h" +#include "Pad.h" +#include "ControllerConfig.h" +#include "DMAudio.h" +#include "IniFile.h" +#include "CarCtrl.h" +#include "Population.h" + +// Menu screens array is at the bottom of the file. + +#ifdef PC_MENU + +#ifdef CUSTOM_FRONTEND_OPTIONS + +#if defined(IMPROVED_VIDEOMODE) && !defined(GTA_HANDHELD) + #define VIDEOMODE_SELECTOR MENUACTION_CFO_SELECT, "FEM_SCF", { new CCFOSelect((int8*)&FrontEndMenuManager.m_nPrefsWindowed, "VideoMode", "Windowed", screenModes, 2, true, ScreenModeAfterChange, true) }, 0, 0, MENUALIGN_LEFT, +#else + #define VIDEOMODE_SELECTOR +#endif + +#ifdef MULTISAMPLING + #define MULTISAMPLING_SELECTOR MENUACTION_CFO_DYNAMIC, "FED_AAS", { new CCFODynamic((int8*)&FrontEndMenuManager.m_nPrefsMSAALevel, "Graphics", "MultiSampling", MultiSamplingDraw, MultiSamplingButtonPress) }, 0, 0, MENUALIGN_LEFT, +#else + #define MULTISAMPLING_SELECTOR +#endif + +#ifdef CUTSCENE_BORDERS_SWITCH + #define CUTSCENE_BORDERS_TOGGLE MENUACTION_CFO_SELECT, "FEM_CSB", { new CCFOSelect((int8 *)&FrontEndMenuManager.m_PrefsCutsceneBorders, "Display", "CutsceneBorders", off_on, 2, false) }, 0, 0, MENUALIGN_LEFT, +#else + #define CUTSCENE_BORDERS_TOGGLE +#endif + +#ifdef FREE_CAM + #define FREE_CAM_TOGGLE MENUACTION_CFO_SELECT, "FEC_FRC", { new CCFOSelect((int8*)&TheCamera.bFreeCam, "Display", "FreeCam", off_on, 2, false) }, 0, 0, MENUALIGN_LEFT, +#else + #define FREE_CAM_TOGGLE +#endif + +#ifdef PS2_ALPHA_TEST + #define DUALPASS_SELECTOR MENUACTION_CFO_SELECT, "FEM_2PR", { new CCFOSelect((int8*)&gPS2alphaTest, "Graphics", "PS2AlphaTest", off_on, 2, false) }, 0, 0, MENUALIGN_LEFT, +#else + #define DUALPASS_SELECTOR +#endif + +#ifdef PED_CAR_DENSITY_SLIDERS + // 0.2f - 3.4f makes it possible to have 1.0f somewhere inbetween + #define DENSITY_SLIDERS \ + MENUACTION_CFO_SLIDER, "FEM_PED", { new CCFOSlider(&CIniFile::PedNumberMultiplier, "Display", "PedDensity", 0.2f, 3.4f, PedDensityChange) }, 0, 0, MENUALIGN_LEFT, \ + MENUACTION_CFO_SLIDER, "FEM_CAR", { new CCFOSlider(&CIniFile::CarNumberMultiplier, "Display", "CarDensity", 0.2f, 3.4f, CarDensityChange) }, 0, 0, MENUALIGN_LEFT, +#else + #define DENSITY_SLIDERS +#endif + +#ifdef NO_ISLAND_LOADING + #define ISLAND_LOADING_SELECTOR MENUACTION_CFO_SELECT, "FEM_ISL", { new CCFOSelect((int8*)&FrontEndMenuManager.m_PrefsIslandLoading, "Graphics", "IslandLoading", islandLoadingOpts, ARRAY_SIZE(islandLoadingOpts), true, IslandLoadingAfterChange) }, 0, 0, MENUALIGN_LEFT, +#else + #define ISLAND_LOADING_SELECTOR +#endif + +#ifdef EXTENDED_COLOURFILTER + #define POSTFX_SELECTORS \ + MENUACTION_CFO_SELECT, "FED_CLF", { new CCFOSelect((int8*)&CPostFX::EffectSwitch, "Graphics", "ColourFilter", filterNames, ARRAY_SIZE(filterNames), false) }, 0, 0, MENUALIGN_LEFT, \ + MENUACTION_CFO_SELECT, "FED_MBL", { new CCFOSelect((int8*)&CPostFX::MotionBlurOn, "Graphics", "MotionBlur", off_on, 2, false) }, 0, 0, MENUALIGN_LEFT, +#else + #define POSTFX_SELECTORS +#endif + +#ifdef INVERT_LOOK_FOR_PAD + #define INVERT_PAD_SELECTOR MENUACTION_CFO_SELECT, "FEC_ILU", { new CCFOSelect((int8*)&CPad::bInvertLook4Pad, "Controller", "InvertPad", off_on, 2, false) }, 0, 0, MENUALIGN_LEFT, +#else + #define INVERT_PAD_SELECTOR +#endif + +#ifdef GAMEPAD_MENU + #define SELECT_CONTROLLER_TYPE MENUACTION_CFO_SELECT, "FEC_TYP", { new CCFOSelect((int8*)&FrontEndMenuManager.m_PrefsControllerType, "Controller", "Type", controllerTypes, ARRAY_SIZE(controllerTypes), false, ControllerTypeAfterChange) }, 0, 0, MENUALIGN_LEFT, +#else + #define SELECT_CONTROLLER_TYPE +#endif + +const char *filterNames[] = { "FEM_NON", "FEM_SIM", "FEM_NRM", "FEM_MOB" }; +const char *off_on[] = { "FEM_OFF", "FEM_ON" }; + +void RestoreDefGraphics(int8 action) { + if (action != FEOPTION_ACTION_SELECT) + return; + + #ifdef PS2_ALPHA_TEST + gPS2alphaTest = false; + #endif + #ifdef MULTISAMPLING + FrontEndMenuManager.m_nPrefsMSAALevel = FrontEndMenuManager.m_nDisplayMSAALevel = 0; + #endif + #ifdef NO_ISLAND_LOADING + if (!FrontEndMenuManager.m_bGameNotLoaded) { + FrontEndMenuManager.m_PrefsIslandLoading = FrontEndMenuManager.ISLAND_LOADING_LOW; + CStreaming::RemoveUnusedBigBuildings(CGame::currLevel); + CStreaming::RemoveUnusedBuildings(CGame::currLevel); + CStreaming::RequestIslands(CGame::currLevel); + CStreaming::LoadAllRequestedModels(true); + } else + FrontEndMenuManager.m_PrefsIslandLoading = FrontEndMenuManager.ISLAND_LOADING_LOW; + #endif + #ifdef GRAPHICS_MENU_OPTIONS // otherwise Frontend will handle those + FrontEndMenuManager.m_PrefsFrameLimiter = true; + FrontEndMenuManager.m_PrefsVsyncDisp = true; + #ifdef LEGACY_MENU_OPTIONS + FrontEndMenuManager.m_PrefsVsync = true; + #endif + FrontEndMenuManager.m_PrefsUseWideScreen = false; + FrontEndMenuManager.m_nDisplayVideoMode = FrontEndMenuManager.m_nPrefsVideoMode; + CMBlur::BlurOn = false; + FrontEndMenuManager.SaveSettings(); + #endif +} + +void RestoreDefDisplay(int8 action) { + if (action != FEOPTION_ACTION_SELECT) + return; + + #ifdef CUTSCENE_BORDERS_SWITCH + FrontEndMenuManager.m_PrefsCutsceneBorders = true; + #endif + #ifdef FREE_CAM + TheCamera.bFreeCam = false; + #endif + #ifdef PED_CAR_DENSITY_SLIDERS + CIniFile::LoadIniFile(); + #endif + #ifdef GRAPHICS_MENU_OPTIONS // otherwise Frontend will handle those + FrontEndMenuManager.m_PrefsBrightness = 256; + FrontEndMenuManager.m_PrefsLOD = 1.2f; + CRenderer::ms_lodDistScale = 1.2f; + FrontEndMenuManager.m_PrefsShowSubtitles = false; + FrontEndMenuManager.m_PrefsShowLegends = true; + FrontEndMenuManager.m_PrefsRadarMode = 0; + FrontEndMenuManager.m_PrefsShowHud = true; + FrontEndMenuManager.SaveSettings(); + #endif +} + +#ifdef NO_ISLAND_LOADING +const char *islandLoadingOpts[] = { "FEM_LOW", "FEM_MED", "FEM_HIG" }; +void IslandLoadingAfterChange(int8 before, int8 after) { + if (!FrontEndMenuManager.m_bGameNotLoaded) { + if (after > FrontEndMenuManager.ISLAND_LOADING_LOW) { + FrontEndMenuManager.m_PrefsIslandLoading = before; // calls below needs previous mode :shrug: + + if (after == FrontEndMenuManager.ISLAND_LOADING_HIGH) { + CStreaming::RemoveIslandsNotUsed(LEVEL_BEACH); + CStreaming::RemoveIslandsNotUsed(LEVEL_MAINLAND); + } + if (before == FrontEndMenuManager.ISLAND_LOADING_LOW) { + FrontEndMenuManager.m_PrefsIslandLoading = after; + CStreaming::RequestBigBuildings(CGame::currLevel); + + } else if (before == FrontEndMenuManager.ISLAND_LOADING_HIGH) { + FrontEndMenuManager.m_PrefsIslandLoading = after; + CStreaming::RequestIslands(CGame::currLevel); + } else + FrontEndMenuManager.m_PrefsIslandLoading = after; + + } else { // low + CStreaming::RemoveUnusedBigBuildings(CGame::currLevel); + CStreaming::RemoveUnusedBuildings(CGame::currLevel); + CStreaming::RequestIslands(CGame::currLevel); + } + + CStreaming::LoadAllRequestedModels(true); + } + + FrontEndMenuManager.SetHelperText(0); +} +#endif + +#ifdef PED_CAR_DENSITY_SLIDERS +void PedDensityChange(float before, float after) { + CPopulation::MaxNumberOfPedsInUse = DEFAULT_MAX_NUMBER_OF_PEDS * after; + CPopulation::MaxNumberOfPedsInUseInterior = DEFAULT_MAX_NUMBER_OF_PEDS_INTERIOR * after; +} + +void CarDensityChange(float before, float after) { + CCarCtrl::MaxNumberOfCarsInUse = DEFAULT_MAX_NUMBER_OF_CARS * after; +} +#endif + +#ifndef MULTISAMPLING +void GraphicsGoBack() { +} +#else +void GraphicsGoBack() { + FrontEndMenuManager.m_nDisplayMSAALevel = FrontEndMenuManager.m_nPrefsMSAALevel; +} + +void MultiSamplingButtonPress(int8 action) { + if (action == FEOPTION_ACTION_SELECT) { + if (FrontEndMenuManager.m_nDisplayMSAALevel != FrontEndMenuManager.m_nPrefsMSAALevel) { + FrontEndMenuManager.m_nPrefsMSAALevel = FrontEndMenuManager.m_nDisplayMSAALevel; + _psSelectScreenVM(FrontEndMenuManager.m_nPrefsVideoMode); + DMAudio.ChangeMusicMode(MUSICMODE_FRONTEND); + DMAudio.Service(); + FrontEndMenuManager.SetHelperText(0); + FrontEndMenuManager.SaveSettings(); + } + } else if (action == FEOPTION_ACTION_LEFT || action == FEOPTION_ACTION_RIGHT) { + if (FrontEndMenuManager.m_bGameNotLoaded) { + FrontEndMenuManager.m_nDisplayMSAALevel += (action == FEOPTION_ACTION_RIGHT ? 1 : -1); + + int i = 0; + int maxAA = RwD3D8EngineGetMaxMultiSamplingLevels(); + while (maxAA != 1) { + i++; + maxAA >>= 1; + } + + if (FrontEndMenuManager.m_nDisplayMSAALevel < 0) + FrontEndMenuManager.m_nDisplayMSAALevel = i; + else if (FrontEndMenuManager.m_nDisplayMSAALevel > i) + FrontEndMenuManager.m_nDisplayMSAALevel = 0; + } + } else if (action == FEOPTION_ACTION_FOCUSLOSS) { + if (FrontEndMenuManager.m_nDisplayMSAALevel != FrontEndMenuManager.m_nPrefsMSAALevel) { + FrontEndMenuManager.m_nDisplayMSAALevel = FrontEndMenuManager.m_nPrefsMSAALevel; + FrontEndMenuManager.SetHelperText(3); + } + } +} + +wchar* MultiSamplingDraw(bool *disabled, bool userHovering) { + static wchar unicodeTemp[64]; + if (userHovering) { + if (FrontEndMenuManager.m_nDisplayMSAALevel == FrontEndMenuManager.m_nPrefsMSAALevel) { + if (FrontEndMenuManager.m_nHelperTextMsgId == 1) // Press enter to apply + FrontEndMenuManager.ResetHelperText(); + } else { + FrontEndMenuManager.SetHelperText(1); + } + } else { + if (FrontEndMenuManager.m_nDisplayMSAALevel != FrontEndMenuManager.m_nPrefsMSAALevel) { + FrontEndMenuManager.m_nDisplayMSAALevel = FrontEndMenuManager.m_nPrefsMSAALevel; + } + } + + if (!FrontEndMenuManager.m_bGameNotLoaded) + *disabled = true; + + switch (FrontEndMenuManager.m_nDisplayMSAALevel) { + case 0: + return TheText.Get("FEM_OFF"); + default: + sprintf(gString, "%iX", 1 << (FrontEndMenuManager.m_nDisplayMSAALevel)); + AsciiToUnicode(gString, unicodeTemp); + return unicodeTemp; + } +} +#endif + +#ifdef IMPROVED_VIDEOMODE +const char* screenModes[] = { "FED_FLS", "FED_WND" }; +void ScreenModeAfterChange(int8 before, int8 after) +{ + _psSelectScreenVM(FrontEndMenuManager.m_nPrefsVideoMode); // apply same resolution + DMAudio.ChangeMusicMode(MUSICMODE_FRONTEND); + DMAudio.Service(); + FrontEndMenuManager.SetHelperText(0); +} + +#endif + +#ifdef DETECT_JOYSTICK_MENU +wchar selectedJoystickUnicode[128]; +int cachedButtonNum = -1; + +wchar* DetectJoystickDraw(bool* disabled, bool userHovering) { + +#if defined RW_GL3 && !defined LIBRW_SDL2 + int numButtons; + int found = -1; + const char *joyname; + if (userHovering) { + for (int i = 0; i <= GLFW_JOYSTICK_LAST; i++) { + if ((joyname = glfwGetJoystickName(i))) { + const uint8* buttons = glfwGetJoystickButtons(i, &numButtons); + for (int j = 0; j < numButtons; j++) { + if (buttons[j]) { + found = i; + break; + } + } + if (found != -1) + break; + } + } + + if (found != -1 && PSGLOBAL(joy1id) != found) { + if (PSGLOBAL(joy1id) != -1 && PSGLOBAL(joy1id) != found) + PSGLOBAL(joy2id) = PSGLOBAL(joy1id); + else + PSGLOBAL(joy2id) = -1; + + strcpy(gSelectedJoystickName, joyname); + PSGLOBAL(joy1id) = found; + cachedButtonNum = numButtons; + } + } + if (PSGLOBAL(joy1id) == -1) +#elif defined XINPUT + int found = -1; + XINPUT_STATE xstate; + memset(&xstate, 0, sizeof(XINPUT_STATE)); + if (userHovering) { + for (int i = 0; i <= 3; i++) { + if (XInputGetState(i, &xstate) == ERROR_SUCCESS) { + if (xstate.Gamepad.bLeftTrigger || xstate.Gamepad.bRightTrigger) { + found = i; + break; + } + for (int j = XINPUT_GAMEPAD_DPAD_UP; j != XINPUT_GAMEPAD_Y << 1; j = (j << 1)) { + if (xstate.Gamepad.wButtons & j) { + found = i; + break; + } + } + if (found != -1) + break; + } + } + if (found != -1 && CPad::XInputJoy1 != found) { + // We should never leave pads -1, so we can process them when they're connected and kinda support hotplug. + CPad::XInputJoy2 = (CPad::XInputJoy1 == -1 ? (found + 1) % 4 : CPad::XInputJoy1); + CPad::XInputJoy1 = found; + cachedButtonNum = 0; // fake too, because xinput bypass CControllerConfig + } + } + sprintf(gSelectedJoystickName, "%d", CPad::XInputJoy1); // fake, on xinput we only store gamepad ids(thanks MS) so this is a temp variable to be used below + if (CPad::XInputJoy1 == -1) +#endif + AsciiToUnicode("Not found", selectedJoystickUnicode); + else + AsciiToUnicode(gSelectedJoystickName, selectedJoystickUnicode); + + return selectedJoystickUnicode; +} + +void DetectJoystickGoBack() { + if (cachedButtonNum != -1) { +#ifdef LOAD_INI_SETTINGS + ControlsManager.InitDefaultControlConfigJoyPad(cachedButtonNum); + SaveINIControllerSettings(); +#else + // Otherwise no way to save gSelectedJoystickName or ms_padButtonsInited anyway :shrug: Why do you even use this config.?? +#endif + cachedButtonNum = -1; + } +} +#endif + +#ifdef GAMEPAD_MENU +const char* controllerTypes[] = { "FEC_DS2", "FEC_DS3", "FEC_DS4", "FEC_360", "FEC_ONE", "FEC_NSW" }; +void ControllerTypeAfterChange(int8 before, int8 after) +{ + FrontEndMenuManager.LoadController(after); +} +#endif + +CMenuScreenCustom aScreens[] = { + // MENUPAGE_STATS = 0 + { "FEH_STA", MENUPAGE_NONE, nil, nil, + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 190, 320, MENUALIGN_RIGHT, + }, + + // MENUPAGE_NEW_GAME = 1 + { "FEP_STG", MENUPAGE_NONE, nil, nil, + MENUACTION_CHANGEMENU, "FES_NGA", {nil, SAVESLOT_NONE, MENUPAGE_NEW_GAME_RELOAD}, 320, 155, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FES_LOA", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_LOAD_SLOT}, 0, 0, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FES_DEL", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_DELETE_SLOT}, 0, 0, MENUALIGN_CENTER, + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, 0}, 0, 0, MENUALIGN_CENTER, + }, + + // MENUPAGE_BRIEFS = 2 + { "FEH_BRI", MENUPAGE_NONE, nil, nil, + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 190, 320, MENUALIGN_RIGHT, + }, + + // MENUPAGE_SOUND_SETTINGS = 3 + { "FEH_AUD", MENUPAGE_OPTIONS, nil, nil, + MENUACTION_MUSICVOLUME, "FEA_MUS", {nil, SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS}, 40, 76, MENUALIGN_LEFT, + MENUACTION_SFXVOLUME, "FEA_SFX", {nil, SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS}, 0, 0, MENUALIGN_LEFT, + MENUACTION_MP3VOLUMEBOOST, "FEA_MPB", {nil, SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS}, 0, 0, MENUALIGN_LEFT, +#ifdef EXTERNAL_3D_SOUND + MENUACTION_AUDIOHW, "FEA_3DH", {nil, SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS}, 0, 0, MENUALIGN_LEFT, + MENUACTION_SPEAKERCONF, "FEA_SPK", {nil, SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS}, 0, 0, MENUALIGN_LEFT, +#endif + MENUACTION_DYNAMICACOUSTIC, "FET_DAM", {nil, SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS}, 0, 0, MENUALIGN_LEFT, + MENUACTION_RADIO, "FEA_RSS", {nil, SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS}, 0, 0, MENUALIGN_LEFT, +#ifdef EXTERNAL_3D_SOUND + MENUACTION_RESTOREDEF, "FET_DEF", {nil, SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS}, 320, 367, MENUALIGN_CENTER, +#else + MENUACTION_RESTOREDEF, "FET_DEF", {nil, SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS}, 320, 327, MENUALIGN_CENTER, +#endif + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, MENUALIGN_CENTER, + }, + + // MENUPAGE_DISPLAY_SETTINGS = 4 +#ifndef GRAPHICS_MENU_OPTIONS + { "FEH_DIS", MENUPAGE_OPTIONS, new CCustomScreenLayout({40, 78, 25, true}), nil, + MENUACTION_BRIGHTNESS, "FED_BRI", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, + MENUACTION_DRAWDIST, "FEM_LOD", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, + DENSITY_SLIDERS +#ifdef LEGACY_MENU_OPTIONS + MENUACTION_FRAMESYNC, "FEM_VSC", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, +#endif + MENUACTION_FRAMELIMIT, "FEM_FRM", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, +#if defined LEGACY_MENU_OPTIONS && !defined EXTENDED_COLOURFILTER + MENUACTION_TRAILS, "FED_TRA", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, +#endif + MENUACTION_SUBTITLES, "FED_SUB", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, + MENUACTION_WIDESCREEN, "FED_WIS", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, + MENUACTION_LEGENDS, "MAP_LEG", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, + MENUACTION_RADARMODE, "FED_RDR", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, + MENUACTION_HUD, "FED_HUD", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, + MENUACTION_SCREENRES, "FED_RES", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, + VIDEOMODE_SELECTOR + MULTISAMPLING_SELECTOR + ISLAND_LOADING_SELECTOR + DUALPASS_SELECTOR + CUTSCENE_BORDERS_TOGGLE + FREE_CAM_TOGGLE + POSTFX_SELECTORS + // re3.cpp inserts here pipeline selectors if neo/neo.txd exists and EXTENDED_PIPELINES defined + MENUACTION_RESTOREDEF, "FET_DEF", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 320, 0, MENUALIGN_CENTER, + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 320, 0, MENUALIGN_CENTER, + }, +#else + { "FEH_DIS", MENUPAGE_OPTIONS, new CCustomScreenLayout({40, 78, 25, true}), nil, + MENUACTION_BRIGHTNESS, "FED_BRI", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, 0, 0, MENUALIGN_LEFT, + MENUACTION_DRAWDIST, "FEM_LOD", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, 0, 0, MENUALIGN_LEFT, + DENSITY_SLIDERS + CUTSCENE_BORDERS_TOGGLE + FREE_CAM_TOGGLE + MENUACTION_LEGENDS, "MAP_LEG", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, 0, 0, MENUALIGN_LEFT, + MENUACTION_RADARMODE, "FED_RDR", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, 0, 0, MENUALIGN_LEFT, + MENUACTION_HUD, "FED_HUD", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, 0, 0, MENUALIGN_LEFT, + MENUACTION_SUBTITLES, "FED_SUB", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, 0, 0, MENUALIGN_LEFT, + MENUACTION_CFO_DYNAMIC, "FET_DEF", { new CCFODynamic(nil, nil, nil, nil, RestoreDefDisplay) }, 320, 0, MENUALIGN_CENTER, + MENUACTION_GOBACK, "FEDS_TB", { nil, SAVESLOT_NONE, MENUPAGE_NONE}, 320, 0, MENUALIGN_CENTER, + }, +#endif + + // MENUPAGE_LANGUAGE_SETTINGS = 5 + { "FEH_LAN", MENUPAGE_OPTIONS, nil, nil, + MENUACTION_LANG_ENG, "FEL_ENG", {nil, SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS}, 320, 132, MENUALIGN_CENTER, + MENUACTION_LANG_FRE, "FEL_FRE", {nil, SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS}, 0, 0, MENUALIGN_CENTER, + MENUACTION_LANG_GER, "FEL_GER", {nil, SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS}, 0, 0, MENUALIGN_CENTER, + MENUACTION_LANG_ITA, "FEL_ITA", {nil, SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS}, 0, 0, MENUALIGN_CENTER, + MENUACTION_LANG_SPA, "FEL_SPA", {nil, SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS}, 0, 0, MENUALIGN_CENTER, + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, MENUALIGN_CENTER, + }, + + // MENUPAGE_MAP = 6 + { "FEH_MAP", MENUPAGE_NONE, nil, nil, + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 70, 380, MENUALIGN_CENTER, + }, + + // MENUPAGE_NEW_GAME_RELOAD = 7 + { "FES_NGA", MENUPAGE_NEW_GAME, nil, nil, + MENUACTION_LABEL, "FESZ_QR", {nil, SAVESLOT_NONE, 0}, 0, 0, 0, + MENUACTION_NO, "FEM_NO", {nil, SAVESLOT_NONE, MENUPAGE_NEW_GAME}, 320, 200, MENUALIGN_CENTER, + MENUACTION_NEWGAME, "FEM_YES", {nil, SAVESLOT_NONE, MENUPAGE_NEW_GAME_RELOAD}, 320, 225, MENUALIGN_CENTER, + }, + + // MENUPAGE_CHOOSE_LOAD_SLOT = 8 + { "FET_LG", MENUPAGE_NEW_GAME, nil, nil, + MENUACTION_CHECKSAVE, "FEM_SL1", {nil, SAVESLOT_1, 0}, 40, 90, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL2", {nil, SAVESLOT_2, 0}, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL3", {nil, SAVESLOT_3, 0}, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL4", {nil, SAVESLOT_4, 0}, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL5", {nil, SAVESLOT_5, 0}, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL6", {nil, SAVESLOT_6, 0}, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL7", {nil, SAVESLOT_7, 0}, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL8", {nil, SAVESLOT_8, 0}, 0, 0, MENUALIGN_LEFT, + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, 0}, 320, 345, MENUALIGN_CENTER, + }, + + // MENUPAGE_CHOOSE_DELETE_SLOT = 9 + { "FES_DEL", MENUPAGE_NEW_GAME, nil, nil, + MENUACTION_CHECKSAVE, "FEM_SL1", {nil, SAVESLOT_1, 0}, 40, 90, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL2", {nil, SAVESLOT_2, 0}, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL3", {nil, SAVESLOT_3, 0}, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL4", {nil, SAVESLOT_4, 0}, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL5", {nil, SAVESLOT_5, 0}, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL6", {nil, SAVESLOT_6, 0}, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL7", {nil, SAVESLOT_7, 0}, 0, 0, MENUALIGN_LEFT, + MENUACTION_CHECKSAVE, "FEM_SL8", {nil, SAVESLOT_8, 0}, 0, 0, MENUALIGN_LEFT, + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, 0}, 320, 345, MENUALIGN_CENTER, + }, + + // MENUPAGE_LOAD_SLOT_CONFIRM = 10 + { "FET_LG", MENUPAGE_CHOOSE_LOAD_SLOT, nil, nil, + MENUACTION_LABEL, "FESZ_QL", {nil, SAVESLOT_NONE, 0}, 0, 0, 0, + MENUACTION_NO, "FEM_NO", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_LOAD_SLOT}, 320, 200, MENUALIGN_CENTER, + MENUACTION_YES, "FEM_YES", {nil, SAVESLOT_NONE, MENUPAGE_LOADING_IN_PROGRESS}, 320, 225, MENUALIGN_CENTER, + }, + + // MENUPAGE_DELETE_SLOT_CONFIRM = 11 + { "FES_DEL", MENUPAGE_CHOOSE_DELETE_SLOT, nil, nil, + MENUACTION_LABEL, "FESZ_QD", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, + MENUACTION_NO, "FEM_NO", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_DELETE_SLOT}, 320, 200, MENUALIGN_CENTER, + MENUACTION_YES, "FEM_YES", {nil, SAVESLOT_NONE, MENUPAGE_DELETING_IN_PROGRESS}, 320, 225, MENUALIGN_CENTER, + }, + + // MENUPAGE_LOADING_IN_PROGRESS = 12 + { "FET_LG", MENUPAGE_CHOOSE_LOAD_SLOT, nil, nil, + }, + + // MENUPAGE_DELETING_IN_PROGRESS = 13 + { "FES_DEL", MENUPAGE_CHOOSE_DELETE_SLOT, nil, nil, + }, + + // MENUPAGE_DELETE_SUCCESSFUL = 14 + { "FES_DEL", MENUPAGE_NEW_GAME, nil, nil, + MENUACTION_LABEL, "FES_DSC", {nil, SAVESLOT_NONE, 0}, 0, 0, 0, + MENUACTION_CHANGEMENU, "FEM_OK", {nil, SAVESLOT_NONE, MENUPAGE_NEW_GAME}, 320, 225, MENUALIGN_CENTER, + }, + + // MENUPAGE_CHOOSE_SAVE_SLOT = 15 + { "FET_SG", MENUPAGE_DISABLED, nil, nil, + MENUACTION_SAVEGAME, "FEM_SL1", {nil, SAVESLOT_1, MENUPAGE_SAVE_OVERWRITE_CONFIRM}, 40, 90, MENUALIGN_LEFT, + MENUACTION_SAVEGAME, "FEM_SL2", {nil, SAVESLOT_2, MENUPAGE_SAVE_OVERWRITE_CONFIRM}, 0, 0, MENUALIGN_LEFT, + MENUACTION_SAVEGAME, "FEM_SL3", {nil, SAVESLOT_3, MENUPAGE_SAVE_OVERWRITE_CONFIRM}, 0, 0, MENUALIGN_LEFT, + MENUACTION_SAVEGAME, "FEM_SL4", {nil, SAVESLOT_4, MENUPAGE_SAVE_OVERWRITE_CONFIRM}, 0, 0, MENUALIGN_LEFT, + MENUACTION_SAVEGAME, "FEM_SL5", {nil, SAVESLOT_5, MENUPAGE_SAVE_OVERWRITE_CONFIRM}, 0, 0, MENUALIGN_LEFT, + MENUACTION_SAVEGAME, "FEM_SL6", {nil, SAVESLOT_6, MENUPAGE_SAVE_OVERWRITE_CONFIRM}, 0, 0, MENUALIGN_LEFT, + MENUACTION_SAVEGAME, "FEM_SL7", {nil, SAVESLOT_7, MENUPAGE_SAVE_OVERWRITE_CONFIRM}, 0, 0, MENUALIGN_LEFT, + MENUACTION_SAVEGAME, "FEM_SL8", {nil, SAVESLOT_8, MENUPAGE_SAVE_OVERWRITE_CONFIRM}, 0, 0, MENUALIGN_LEFT, + MENUACTION_RESUME_FROM_SAVEZONE,"FESZ_CA", {nil, SAVESLOT_NONE, 0}, 320, 345, MENUALIGN_CENTER, + }, + + // MENUPAGE_SAVE_OVERWRITE_CONFIRM = 16 + { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, nil, nil, + MENUACTION_LABEL, "FESZ_QZ", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, + MENUACTION_NO, "FEM_NO", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT}, 320, 200, MENUALIGN_CENTER, + MENUACTION_YES, "FEM_YES", {nil, SAVESLOT_NONE, MENUPAGE_SAVING_IN_PROGRESS}, 320, 225, MENUALIGN_CENTER, + }, + + // MENUPAGE_SAVING_IN_PROGRESS = 17 + { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, nil, nil, + }, + + // MENUPAGE_SAVE_SUCCESSFUL = 18 + { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, nil, nil, + MENUACTION_LABEL, "FES_SSC", {nil, SAVESLOT_LABEL, MENUPAGE_NONE}, 0, 0, 0, + MENUACTION_RESUME_FROM_SAVEZONE, "FEM_OK", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT}, 320, 225, MENUALIGN_CENTER, + }, + + // MENUPAGE_SAVE_CUSTOM_WARNING = 19 + { "FET_SG", MENUPAGE_NONE, nil, nil, + MENUACTION_LABEL, "", {nil, SAVESLOT_NONE, 0}, 0, 0, 0, + MENUACTION_CHANGEMENU, "FEM_OK", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT}, 320, 225, MENUALIGN_CENTER, + }, + + // MENUPAGE_SAVE_CHEAT_WARNING = 20 + { "FET_SG", MENUPAGE_NEW_GAME, nil, nil, + MENUACTION_LABEL, "FES_CHE", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, + MENUACTION_CHANGEMENU, "FEM_OK", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT}, 320, 225, MENUALIGN_CENTER, + }, + + // MENUPAGE_SKIN_SELECT = 21 + { "FET_PS", MENUPAGE_OPTIONS, nil, nil, + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_OPTIONS}, 0, 0, 0, + }, + + // MENUPAGE_SAVE_UNUSED = 22 + { "FET_SG", MENUPAGE_NEW_GAME, nil, nil, + MENUACTION_LABEL, "FED_LWR", {nil, SAVESLOT_NONE, 0}, 0, 0, 0, + MENUACTION_CHANGEMENU, "FEC_OKK", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT}, 0, 0, 0, + }, + + // MENUPAGE_SAVE_FAILED = 23 + { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, nil, nil, + MENUACTION_LABEL, "FEC_SVU", {nil, SAVESLOT_NONE, 0}, 0, 0, 0, + MENUACTION_CHANGEMENU, "FEC_OKK", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT}, 0, 0, 0, + }, + + // MENUPAGE_SAVE_FAILED_2 = 24 + { "FET_LG", MENUPAGE_CHOOSE_SAVE_SLOT, nil, nil, + MENUACTION_LABEL, "FEC_SVU", {nil, SAVESLOT_NONE, 0}, 0, 0, 0, + }, + + // MENUPAGE_LOAD_FAILED = 25 + { "FET_LG", MENUPAGE_NEW_GAME, nil, nil, + MENUACTION_LABEL, "FEC_LUN", {nil, SAVESLOT_NONE, 0}, 0, 0, 0, + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NEW_GAME}, 0, 0, 0, + }, + + // MENUPAGE_CONTROLLER_PC = 26 + { "FET_CTL", MENUPAGE_OPTIONS, new CCustomScreenLayout({0, 0, MENU_DEFAULT_LINE_HEIGHT, false, false, 150}), nil, +#ifdef PC_PLAYER_CONTROLS + MENUACTION_CTRLMETHOD, "FET_STI", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC}, 320, 150, MENUALIGN_CENTER, + MENUACTION_KEYBOARDCTRLS,"FEC_RED", {nil, SAVESLOT_NONE, MENUPAGE_KEYBOARD_CONTROLS}, 0, 0, MENUALIGN_CENTER, +#else + MENUACTION_KEYBOARDCTRLS,"FEC_RED", {nil, SAVESLOT_NONE, MENUPAGE_KEYBOARD_CONTROLS}, 320, 150, MENUALIGN_CENTER, +#endif +#ifdef GAMEPAD_MENU + MENUACTION_CHANGEMENU, "FET_AGS", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_SETTINGS}, 0, 0, MENUALIGN_CENTER, +#endif +#ifdef DETECT_JOYSTICK_MENU + MENUACTION_CHANGEMENU, "FEC_JOD", {nil, SAVESLOT_NONE, MENUPAGE_DETECT_JOYSTICK}, 0, 0, MENUALIGN_CENTER, +#endif + MENUACTION_CHANGEMENU, "FEC_MOU", {nil, SAVESLOT_NONE, MENUPAGE_MOUSE_CONTROLS}, 0, 0, MENUALIGN_CENTER, + MENUACTION_RESTOREDEF, "FET_DEF", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC}, 320, 0, MENUALIGN_CENTER, + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, 0}, 320, 0, MENUALIGN_CENTER, + }, + + // MENUPAGE_OPTIONS = 27 + { "FET_OPT", MENUPAGE_NONE, nil, nil, +#ifdef GTA_HANDHELD + MENUACTION_CHANGEMENU, "FEO_CON", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_SETTINGS}, 320, 132, MENUALIGN_CENTER, +#else + MENUACTION_CHANGEMENU, "FEO_CON", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC}, 320, 132, MENUALIGN_CENTER, +#endif + MENUACTION_LOADRADIO, "FEO_AUD", {nil, SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS}, 0, 0, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FEO_DIS", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_CENTER, +#ifdef GRAPHICS_MENU_OPTIONS + MENUACTION_CHANGEMENU, "FET_GFX", {nil, SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS}, 0, 0, MENUALIGN_CENTER, +#endif + MENUACTION_CHANGEMENU, "FEO_LAN", {nil, SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS}, 0, 0, MENUALIGN_CENTER, + MENUACTION_PLAYERSETUP, "FET_PS", {nil, SAVESLOT_NONE, MENUPAGE_SKIN_SELECT}, 0, 0, MENUALIGN_CENTER, + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, 0}, 0, 0, MENUALIGN_CENTER, + }, + + // MENUPAGE_EXIT = 28 + { "FET_QG", MENUPAGE_NONE, nil, nil, + MENUACTION_LABEL, "FEQ_SRE", {nil, SAVESLOT_NONE, 0}, 0, 0, 0, + MENUACTION_DONTCANCEL, "FEM_NO", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 320, 200, MENUALIGN_CENTER, + MENUACTION_CANCELGAME, "FEM_YES", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 320, 225, MENUALIGN_CENTER, + }, + + // MENUPAGE_START_MENU = 29 + { "FEM_MM", MENUPAGE_DISABLED, nil, nil, + MENUACTION_CHANGEMENU, "FEP_STG", {nil, SAVESLOT_NONE, MENUPAGE_NEW_GAME}, 320, 170, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FEP_OPT", {nil, SAVESLOT_NONE, MENUPAGE_OPTIONS}, 0, 0, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FEP_QUI", {nil, SAVESLOT_NONE, MENUPAGE_EXIT}, 0, 0, MENUALIGN_CENTER, + }, + + // MENUPAGE_KEYBOARD_CONTROLS = 30 + { "FET_STI", MENUPAGE_CONTROLLER_PC, nil, nil, + }, + + // MENUPAGE_MOUSE_CONTROLS = 31 + { "FEC_MOU", MENUPAGE_CONTROLLER_PC, nil, nil, + MENUACTION_MOUSESENS, "FEC_MSH", {nil, SAVESLOT_NONE, MENUPAGE_MOUSE_CONTROLS}, 40, 170, MENUALIGN_LEFT, + MENUACTION_INVVERT, "FEC_IVV", {nil, SAVESLOT_NONE, MENUPAGE_MOUSE_CONTROLS}, 0, 0, MENUALIGN_LEFT, +#ifndef GAMEPAD_MENU + INVERT_PAD_SELECTOR +#endif + MENUACTION_MOUSESTEER, "FET_MST", {nil, SAVESLOT_NONE, MENUPAGE_MOUSE_CONTROLS}, 0, 0, MENUALIGN_LEFT, + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, 0}, 320, 0, MENUALIGN_CENTER, + //MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, 0}, 320, 260, MENUALIGN_CENTER, // original y + }, + + // MENUPAGE_PAUSE_MENU = 32 + { "FET_PAU", MENUPAGE_DISABLED, nil, nil, + MENUACTION_RESUME, "FEP_RES", {nil, SAVESLOT_NONE, 0}, 320, 120, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FEH_SGA", {nil, SAVESLOT_NONE, MENUPAGE_NEW_GAME}, 0, 0, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FEH_MAP", {nil, SAVESLOT_NONE, MENUPAGE_MAP}, 0, 0, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FEP_STA", {nil, SAVESLOT_NONE, MENUPAGE_STATS}, 0, 0, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FEH_BRI", {nil, SAVESLOT_NONE, MENUPAGE_BRIEFS}, 0, 0, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FET_OPT", {nil, SAVESLOT_NONE, MENUPAGE_OPTIONS}, 0, 0, MENUALIGN_CENTER, + MENUACTION_CHANGEMENU, "FEP_QUI", {nil, SAVESLOT_NONE, MENUPAGE_EXIT}, 0, 0, MENUALIGN_CENTER, + }, + + // MENUPAGE_NONE = 33 + { "", 0, nil, nil, }, + +#ifdef GAMEPAD_MENU +#ifdef GTA_HANDHELD + { "FET_AGS", MENUPAGE_OPTIONS, new CCustomScreenLayout({40, 78, 25, true, true}), nil, +#else + { "FET_AGS", MENUPAGE_CONTROLLER_PC, new CCustomScreenLayout({40, 78, 25, true, true}), nil, +#endif + MENUACTION_CTRLCONFIG, "FEC_CCF", { nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_SETTINGS }, 40, 76, MENUALIGN_LEFT, + MENUACTION_CTRLDISPLAY, "FEC_CDP", { nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_SETTINGS }, 0, 0, MENUALIGN_LEFT, + INVERT_PAD_SELECTOR + MENUACTION_CTRLVIBRATION, "FEC_VIB", { nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_SETTINGS }, 0, 0, MENUALIGN_LEFT, + SELECT_CONTROLLER_TYPE + MENUACTION_GOBACK, "FEDS_TB", { nil, SAVESLOT_NONE, MENUPAGE_NONE }, 0, 0, MENUALIGN_LEFT, + }, +#endif +#ifdef LEGACY_MENU_OPTIONS + // MENUPAGE_DEBUG_MENU = 18 + { "FED_DBG", MENUPAGE_NONE, nil, nil, + MENUACTION_RELOADIDE, "FED_RID", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, + MENUACTION_SETDBGFLAG, "FED_DFL", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, + MENUACTION_SWITCHBIGWHITEDEBUGLIGHT, "FED_DLS", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, + MENUACTION_COLLISIONPOLYS, "FED_SCP", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, + }, + + // MENUPAGE_CONTROLLER_PC_OLD1 = 36 + { "FET_CTL", MENUPAGE_CONTROLLER_PC, nil, nil, + MENUACTION_GETKEY, "FEC_PLB", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1}, 0, 0, 0, + MENUACTION_GETKEY, "FEC_CWL", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1}, 0, 0, 0, + MENUACTION_GETKEY, "FEC_CWR", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1}, 0, 0, 0, + MENUACTION_GETKEY, "FEC_LKT", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1}, 0, 0, 0, + MENUACTION_GETKEY, "FEC_PJP", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1}, 0, 0, 0, + MENUACTION_GETKEY, "FEC_PSP", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1}, 0, 0, 0, + MENUACTION_GETKEY, "FEC_TLF", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1}, 0, 0, 0, + MENUACTION_GETKEY, "FEC_TRG", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1}, 0, 0, 0, + MENUACTION_GETKEY, "FEC_CCM", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1}, 0, 0, 0, + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, + }, + + // MENUPAGE_CONTROLLER_PC_OLD2 = 37 + { "FET_CTL", MENUPAGE_CONTROLLER_PC, nil, nil, + + }, + + // MENUPAGE_CONTROLLER_PC_OLD3 = 38 + { "FET_CTL", MENUPAGE_CONTROLLER_PC, nil, nil, + MENUACTION_GETKEY, "FEC_LUP", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD3}, 0, 0, 0, + MENUACTION_GETKEY, "FEC_LDN", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD3}, 0, 0, 0, + MENUACTION_GETKEY, "FEC_SMS", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD3}, 0, 0, 0, + MENUACTION_SHOWHEADBOB, "FEC_GSL", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD3}, 0, 0, 0, + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, + }, + + // MENUPAGE_CONTROLLER_PC_OLD4 = 39 + { "FET_CTL", MENUPAGE_CONTROLLER_PC, nil, nil, + + }, + + // MENUPAGE_CONTROLLER_DEBUG = 40 + { "FEC_DBG", MENUPAGE_CONTROLLER_PC, nil, nil, + MENUACTION_GETKEY, "FEC_TGD", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_DEBUG}, 0, 0, 0, + MENUACTION_GETKEY, "FEC_TDO", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_DEBUG}, 0, 0, 0, + MENUACTION_GETKEY, "FEC_TSS", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_DEBUG}, 0, 0, 0, + MENUACTION_GETKEY, "FEC_SMS", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_DEBUG}, 0, 0, 0, + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, + }, +#endif + +#ifdef GRAPHICS_MENU_OPTIONS + // MENUPAGE_GRAPHICS_SETTINGS + { "FET_GFX", MENUPAGE_OPTIONS, new CCustomScreenLayout({40, 78, 25, true, true}), GraphicsGoBack, + +#ifndef GTA_HANDHELD + MENUACTION_SCREENRES, "FED_RES", { nil, SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS }, 0, 0, MENUALIGN_LEFT, +#endif + MULTISAMPLING_SELECTOR + MENUACTION_WIDESCREEN, "FED_WIS", { nil, SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS }, 0, 0, MENUALIGN_LEFT, + VIDEOMODE_SELECTOR +#ifdef LEGACY_MENU_OPTIONS + MENUACTION_FRAMESYNC, "FEM_VSC", {nil, SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS}, 0, 0, MENUALIGN_LEFT, +#endif + MENUACTION_FRAMELIMIT, "FEM_FRM", { nil, SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS }, 0, 0, MENUALIGN_LEFT, + ISLAND_LOADING_SELECTOR + DUALPASS_SELECTOR +#ifdef EXTENDED_COLOURFILTER + POSTFX_SELECTORS +#elif defined LEGACY_MENU_OPTIONS + MENUACTION_TRAILS, "FED_TRA", { nil, SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS }, 0, 0, MENUALIGN_LEFT, +#endif + // re3.cpp inserts here pipeline selectors if neo/neo.txd exists and EXTENDED_PIPELINES defined + MENUACTION_CFO_DYNAMIC, "FET_DEF", { new CCFODynamic(nil, nil, nil, nil, RestoreDefGraphics) }, 320, 0, MENUALIGN_CENTER, + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 320, 0, MENUALIGN_CENTER, + }, +#endif + +#ifdef DETECT_JOYSTICK_MENU + // MENUPAGE_DETECT_JOYSTICK + { "FEC_JOD", MENUPAGE_CONTROLLER_PC, new CCustomScreenLayout({0, 0, 0, false, false, 30}), DetectJoystickGoBack, + MENUACTION_LABEL, "FEC_JPR", { nil, SAVESLOT_NONE, MENUPAGE_NONE }, 0, 0, 0, + MENUACTION_CFO_DYNAMIC, "FEC_JDE", { new CCFODynamic(nil, nil, nil, DetectJoystickDraw, nil) }, 80, 200, MENUALIGN_LEFT, + MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 320, 225, MENUALIGN_CENTER, + }, +#endif + + +#ifdef MISSION_REPLAY + // MENUPAGE_MISSION_RETRY = 57 on mobile + + { "M_FAIL", MENUPAGE_DISABLED, nil, nil, + MENUACTION_LABEL, "FESZ_RM", { nil, SAVESLOT_NONE, MENUPAGE_NONE }, 0, 0, 0, + MENUACTION_CHANGEMENU, "FEM_YES", { nil, SAVESLOT_NONE, MENUPAGE_LOADING_IN_PROGRESS }, 320, 200, MENUALIGN_CENTER, + MENUACTION_REJECT_RETRY, "FEM_NO", { nil, SAVESLOT_NONE, MENUPAGE_NONE }, 320, 225, MENUALIGN_CENTER, + }, +#endif + + // MENUPAGE_OUTRO = 34 + { "", 0, nil, nil, }, +}; + +#endif +#endif diff --git a/src/miami/core/Pad.cpp b/src/miami/core/Pad.cpp new file mode 100644 index 00000000..c265b2db --- /dev/null +++ b/src/miami/core/Pad.cpp @@ -0,0 +1,4752 @@ +#define WITHDINPUT +#include "common.h" +#include "crossplatform.h" +#include "platform.h" +#ifdef RW_DC + +#include +#include +#include + +auto contMaple = maple_enum_type(0, MAPLE_FUNC_CONTROLLER); +auto state = (cont_state_t *)maple_dev_status(contMaple); + +#ifdef XINPUT +#include +#if !defined(PSAPI_VERSION) || (PSAPI_VERSION > 1) +#pragma comment( lib, "Xinput9_1_0.lib" ) +#else +#pragma comment( lib, "Xinput.lib" ) +#endif +#endif +#endif + +#include "Pad.h" +#include "ControllerConfig.h" +#include "Timer.h" +#include "Frontend.h" +#include "Camera.h" +#include "Game.h" +#include "CutsceneMgr.h" +#include "Font.h" +#include "Hud.h" +#include "Text.h" +#include "Timer.h" +#include "Record.h" +#include "World.h" +#include "Vehicle.h" +#include "Ped.h" +#include "Population.h" +#include "Record.h" +#include "Replay.h" +#include "Weather.h" +#include "Streaming.h" +#include "PathFind.h" +#include "Wanted.h" +#include "WaterLevel.h" +#include "General.h" +#include "Fluff.h" +#include "Gangs.h" +#include "platform.h" +#include "Stats.h" +#include "CarCtrl.h" +#include "TrafficLights.h" + +#ifdef GTA_PS2 +#include "eetypes.h" +#include "libpad.h" +#endif + +CPad Pads[MAX_PADS]; +#ifdef GTA_PS2 +u_long128 pad_dma_buf[scePadDmaBufferMax] __attribute__((aligned(64))); +u_long128 pad2_dma_buf[scePadDmaBufferMax] __attribute__((aligned(64))); +#endif + +CMousePointerStateHelper MousePointerStateHelper; + +bool CPad::bDisplayNoControllerMessage; +bool CPad::bObsoleteControllerMessage; +bool CPad::bOldDisplayNoControllerMessage; +bool CPad::m_bMapPadOneToPadTwo; +bool CPad::m_bDebugCamPCOn; +bool CPad::bHasPlayerCheated; +bool CPad::bInvertLook4Pad; +#ifdef GTA_PS2 +unsigned char act_direct[6]; +unsigned char act_align[6]; +#endif + +CKeyboardState CPad::OldKeyState; +CKeyboardState CPad::NewKeyState; +CKeyboardState CPad::TempKeyState; + +char CPad::KeyBoardCheatString[30]; + +CMouseControllerState CPad::OldMouseControllerState; +CMouseControllerState CPad::NewMouseControllerState; +CMouseControllerState CPad::PCTempMouseControllerState; + +#ifdef DETECT_PAD_INPUT_SWITCH +bool CPad::IsAffectedByController = false; +#endif + +_TODO("gbFastTime"); +extern bool gbFastTime; + +#ifdef WALLCLIMB_CHEAT +extern bool gGravityCheat; +#endif + +void SpecialCarCheats() +{ + if ( !CVehicle::bCheat9 ) + { + ((CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_INFERNUS))->m_wheelScale *= 1.3f; + ((CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_CHEETAH))->m_wheelScale *= 1.3f; + ((CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_PHEONIX))->m_wheelScale *= 1.3f; + ((CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_DELUXO))->m_wheelScale *= 1.3f; + mod_HandlingManager.GetHandlingData(HANDLING_LANDSTAL)->Transmission.fEngineAcceleration *= 2.0f; + mod_HandlingManager.GetHandlingData(HANDLING_PATRIOT)->Transmission.fEngineAcceleration *= 2.0f; + mod_HandlingManager.GetHandlingData(HANDLING_BOBCAT)->Transmission.fEngineAcceleration *= 2.0f; + mod_HandlingManager.GetHandlingData(HANDLING_BFINJECT)->Transmission.fEngineAcceleration *= 2.0f; + mod_HandlingManager.GetHandlingData(HANDLING_RANCHER)->Transmission.fEngineAcceleration *= 2.0f; + mod_HandlingManager.GetHandlingData(HANDLING_DESPERAD)->Transmission.fEngineAcceleration *= 2.0f; + mod_HandlingManager.GetHandlingData(HANDLING_DELUXO)->Transmission.fEngineAcceleration *= 2.0f; + + mod_HandlingManager.GetHandlingData(HANDLING_BAGGAGE)->Transmission.fEngineAcceleration *= 2.0f; + mod_HandlingManager.GetHandlingData(HANDLING_BAGGAGE)->Transmission.fMaxVelocity *= 2.0f; + mod_HandlingManager.GetHandlingData(HANDLING_BAGGAGE)->Transmission.InitGearRatios(); + + mod_HandlingManager.GetHandlingData(HANDLING_GOLFCART)->Transmission.fEngineAcceleration *= 2.0f; + mod_HandlingManager.GetHandlingData(HANDLING_GOLFCART)->Transmission.fMaxVelocity *= 2.0f; + mod_HandlingManager.GetHandlingData(HANDLING_GOLFCART)->Transmission.InitGearRatios(); + + mod_HandlingManager.GetHandlingData(HANDLING_STINGER)->fCollisionDamageMultiplier *= 0.25f; + mod_HandlingManager.GetHandlingData(HANDLING_STINGER)->fMass *= 2.5f; + mod_HandlingManager.GetHandlingData(HANDLING_STINGER)->fTurnMass *= 4.0f; + + mod_HandlingManager.GetHandlingData(HANDLING_BANSHEE)->fCollisionDamageMultiplier *= 0.25f; + mod_HandlingManager.GetHandlingData(HANDLING_BANSHEE)->fMass *= 2.5f; + mod_HandlingManager.GetHandlingData(HANDLING_BANSHEE)->fTurnMass *= 4.0f; + + mod_HandlingManager.GetHandlingData(HANDLING_SABRETUR)->fCollisionDamageMultiplier *= 0.25f; + mod_HandlingManager.GetHandlingData(HANDLING_SABRETUR)->fMass *= 2.5f; + mod_HandlingManager.GetHandlingData(HANDLING_SABRETUR)->fTurnMass *= 4.0f; + + mod_HandlingManager.GetHandlingData(HANDLING_COMET)->fCollisionDamageMultiplier *= 0.25f; + mod_HandlingManager.GetHandlingData(HANDLING_COMET)->fMass *= 2.5f; + mod_HandlingManager.GetHandlingData(HANDLING_COMET)->fTurnMass *= 4.0f; + + mod_HandlingManager.GetHandlingData(HANDLING_DELUXO)->fCollisionDamageMultiplier *= 0.25f; + mod_HandlingManager.GetHandlingData(HANDLING_DELUXO)->fMass *= 2.5f; + mod_HandlingManager.GetHandlingData(HANDLING_DELUXO)->fTurnMass *= 4.0f; + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + CVehicle::bCheat9 = true; + CPad::bHasPlayerCheated = true; + } +} + +void PickUpChicksCheat() +{ + if ( FindPlayerVehicle() && (FindPlayerVehicle()->IsCar() || FindPlayerVehicle()->IsBike()) ) + { + CVehicle *vehicle = FindPlayerVehicle(); + if ( FindPlayerVehicle()->IsBike() ) + { + if ( vehicle->pPassengers[0] ) + vehicle->pPassengers[0]->SetObjective(OBJECTIVE_LEAVE_CAR, vehicle); + } + CPed* someoneElse = (CPed*)CWorld::TestSphereAgainstWorld(vehicle->GetPosition(), 6.0f, FindPlayerPed(), false, false, true, false, false, false); + if ( someoneElse && someoneElse->m_nPedState != PED_DRIVING ) + { + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + someoneElse->SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, vehicle); + } + } +} + +void WeaponCheat1() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT2"), true); + + CStreaming::RequestModel(MI_BRASS_KNUCKLES, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_BASEBALL_BAT, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_MOLOTOV, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_COLT45, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_SHOTGUN, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_TEC9, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_RUGER, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_SNIPERRIFLE, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_FLAMETHROWER, STREAMFLAGS_DONT_REMOVE); + CStreaming::LoadAllRequestedModels(false); + + FindPlayerPed()->GiveWeapon(WEAPONTYPE_BRASSKNUCKLE, 1); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_BASEBALLBAT, 1); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_MOLOTOV, 10); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_COLT45, 100); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_SHOTGUN, 50); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_TEC9, 150); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_RUGER, 120); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_SNIPERRIFLE, 25); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_FLAMETHROWER, 200); + + CStreaming::SetModelIsDeletable(MI_BRASS_KNUCKLES); + CStreaming::SetModelIsDeletable(MI_BASEBALL_BAT); + CStreaming::SetModelIsDeletable(MI_MOLOTOV); + CStreaming::SetModelIsDeletable(MI_COLT45); + CStreaming::SetModelIsDeletable(MI_SHOTGUN); + CStreaming::SetModelIsDeletable(MI_TEC9); + CStreaming::SetModelIsDeletable(MI_RUGER); + CStreaming::SetModelIsDeletable(MI_SNIPERRIFLE); + CStreaming::SetModelIsDeletable(MI_FLAMETHROWER); +#ifdef MOBILE_IMPROVEMENTS + if (FindPlayerVehicle()) { + FindPlayerPed()->RemoveWeaponWhenEnteringVehicle(); + } +#endif +} + +void WeaponCheat2() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT2"), true); + + CStreaming::RequestModel(MI_KATANA, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_GRENADE, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_BOMB, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_PYTHON, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_STUBBY_SHOTGUN, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_SILENCEDINGRAM, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_M4, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_LASERSCOPE, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_ROCKETLAUNCHER, STREAMFLAGS_DONT_REMOVE); + CStreaming::LoadAllRequestedModels(false); + +#ifdef FIX_BUGS + FindPlayerPed()->GiveWeapon(WEAPONTYPE_KATANA, 1); +#else + FindPlayerPed()->GiveWeapon(WEAPONTYPE_KATANA, 0); +#endif + FindPlayerPed()->GiveWeapon(WEAPONTYPE_DETONATOR_GRENADE, 10); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_PYTHON, 40); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_STUBBY_SHOTGUN, 25); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_SILENCED_INGRAM, 100); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_M4, 150); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_LASERSCOPE, 21); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_ROCKETLAUNCHER, 5); + + CStreaming::SetModelIsDeletable(MI_KATANA); + CStreaming::SetModelIsDeletable(MI_GRENADE); + CStreaming::SetModelIsDeletable(MI_BOMB); + CStreaming::SetModelIsDeletable(MI_PYTHON); + CStreaming::SetModelIsDeletable(MI_STUBBY_SHOTGUN); + CStreaming::SetModelIsDeletable(MI_SILENCEDINGRAM); + CStreaming::SetModelIsDeletable(MI_M4); + CStreaming::SetModelIsDeletable(MI_LASERSCOPE); + CStreaming::SetModelIsDeletable(MI_ROCKETLAUNCHER); +#ifdef MOBILE_IMPROVEMENTS + if (FindPlayerVehicle()) { + FindPlayerPed()->RemoveWeaponWhenEnteringVehicle(); + } +#endif +} + +void WeaponCheat3() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT2"), true); + + CStreaming::RequestModel(MI_CHAINSAW, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_GRENADE, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_PYTHON, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_SPAS12_SHOTGUN, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_MP5, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_M4, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_LASERSCOPE, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_MINIGUN, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_MINIGUN2, STREAMFLAGS_DONT_REMOVE); + CStreaming::LoadAllRequestedModels(false); + +#ifdef FIX_BUGS + FindPlayerPed()->GiveWeapon(WEAPONTYPE_CHAINSAW, 1); +#else + FindPlayerPed()->GiveWeapon(WEAPONTYPE_CHAINSAW, 0); +#endif + FindPlayerPed()->GiveWeapon(WEAPONTYPE_GRENADE, 10); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_PYTHON, 40); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_SPAS12_SHOTGUN, 30); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_MP5, 100); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_M4, 150); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_LASERSCOPE, 21); + FindPlayerPed()->GiveWeapon(WEAPONTYPE_MINIGUN, 500); + + CStreaming::SetModelIsDeletable(MI_CHAINSAW); + CStreaming::SetModelIsDeletable(MI_GRENADE); + CStreaming::SetModelIsDeletable(MI_PYTHON); + CStreaming::SetModelIsDeletable(MI_SPAS12_SHOTGUN); + CStreaming::SetModelIsDeletable(MI_MP5); + CStreaming::SetModelIsDeletable(MI_M4); + CStreaming::SetModelIsDeletable(MI_LASERSCOPE); + CStreaming::SetModelIsDeletable(MI_MINIGUN); + CStreaming::SetModelIsDeletable(MI_MINIGUN2); + +#ifdef MOBILE_IMPROVEMENTS + if (FindPlayerVehicle()) { + FindPlayerPed()->RemoveWeaponWhenEnteringVehicle(); + } +#endif +} + +void HealthCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT3"), true); + FindPlayerPed()->m_fHealth = CWorld::Players[0].m_nMaxHealth; + if (FindPlayerVehicle()) { + FindPlayerVehicle()->m_fHealth = 1000.0f; + if (FindPlayerVehicle()->m_vehType == VEHICLE_TYPE_CAR) { + ((CAutomobile*)FindPlayerVehicle())->Damage.SetEngineStatus(0); + for (int32 i = 0; i < 4; i++) + ((CAutomobile*)FindPlayerVehicle())->Damage.SetWheelStatus(i, WHEEL_STATUS_OK); + } + } +} + +// TODO(Miami): this is HELLA different on mobile, although it mostly has debug oriented things like player exiting it's current car and enters spawned one etc. +void VehicleCheat(int model) +{ + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + CStreaming::RequestModel(model, STREAMFLAGS_DONT_REMOVE); + CStreaming::LoadAllRequestedModels(false); + if (CStreaming::ms_aInfoForModel[model].m_loadState == STREAMSTATE_LOADED) { + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + + if (!(CStreaming::ms_aInfoForModel[model].m_loadState & STREAMFLAGS_DONT_REMOVE)) { + CStreaming::SetModelIsDeletable(model); + CStreaming::SetModelTxdIsDeletable(model); + } + + int32 node = ThePaths.FindNodeClosestToCoors(FindPlayerCoors(), PATH_CAR, 100.0f); + if (node < 0) return; + +#ifdef FIX_BUGS + CAutomobile* vehicle = new CAutomobile(model, RANDOM_VEHICLE); +#else + CAutomobile* vehicle = new CAutomobile(model, MISSION_VEHICLE); +#endif + if (vehicle != nil) { + CVector pos = ThePaths.m_pathNodes[node].GetPosition(); + pos.z += 4.0f; + vehicle->SetPosition(pos); + vehicle->SetOrientation(0.0f, 0.0f, DEGTORAD(200.0f)); + + vehicle->SetStatus(STATUS_ABANDONED); + vehicle->m_nDoorLock = CARLOCK_UNLOCKED; + CWorld::Add(vehicle); + } + } + CStats::CheatedCount += 1000; + CPad::bHasPlayerCheated = true; +} + + +void BlowUpCarsCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + + int i = CPools::GetVehiclePool()->GetSize(); + while (i-- > 0) { + if (CVehicle *veh = CPools::GetVehiclePool()->GetSlot(i)) + veh->BlowUpCar(nil); + } +} + +void ChangePlayerCheat() +{ + // I don't know wtf is going on in here... + int modelId, anotherModelId; + + if (FindPlayerPed()->IsPedInControl() && CModelInfo::GetModelInfo("player", nil)) { + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + CPlayerPed *ped = FindPlayerPed(); + AssocGroupId AnimGrp = ped->m_animGroup; + do + { + do { + modelId = CGeneral::GetRandomNumberInRange(0, MI_PGA); + anotherModelId = modelId+1; + } while (!CModelInfo::GetModelInfo(anotherModelId)); + } while (anotherModelId >= MI_SPECIAL01 && anotherModelId <= MI_SPECIAL04 || modelId == MI_TAXI_D); + + uint8 flags = CStreaming::ms_aInfoForModel[anotherModelId].m_flags; + ped->DeleteRwObject(); + CStreaming::RequestModel(anotherModelId, STREAMFLAGS_DEPENDENCY| STREAMFLAGS_DONT_REMOVE); + CStreaming::LoadAllRequestedModels(false); + ped->m_modelIndex = -1; + ped->SetModelIndex(anotherModelId); + ped->m_animGroup = AnimGrp; + if (modelId != -1) { + if (!(flags & STREAMFLAGS_DONT_REMOVE)) + CStreaming::SetModelIsDeletable(anotherModelId); + } + } +} + +void ChangePlayerModel(const char* name) { + if (!FindPlayerVehicle()) { + FindPlayerPed()->Undress(name); + CStreaming::LoadAllRequestedModels(0); + FindPlayerPed()->Dress(); + } +} + +void MayhemCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + for (int i = PEDTYPE_CIVMALE; i < PEDTYPE_SPECIAL; i++) + CPedType::SetThreats(i, PED_FLAG_PLAYER1 | PED_FLAG_PLAYER2 | PED_FLAG_PLAYER3 | PED_FLAG_PLAYER4 | + PED_FLAG_CIVMALE | PED_FLAG_CIVFEMALE | PED_FLAG_COP | PED_FLAG_GANG1 | + PED_FLAG_GANG2 | PED_FLAG_GANG3 | PED_FLAG_GANG4 | PED_FLAG_GANG5 | + PED_FLAG_GANG6 | PED_FLAG_GANG7 | PED_FLAG_GANG8 | PED_FLAG_GANG9 | + PED_FLAG_EMERGENCY | PED_FLAG_PROSTITUTE | PED_FLAG_CRIMINAL | PED_FLAG_SPECIAL ); + CStats::CheatedCount += 1000; + CPad::bHasPlayerCheated = true; +} + +void EverybodyAttacksPlayerCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + for (int i = PEDTYPE_CIVMALE; i < PEDTYPE_SPECIAL; i++) + CPedType::AddThreat(i, PED_FLAG_PLAYER1); + + CStats::CheatedCount += 1000; + CPad::bHasPlayerCheated = true; +} + +void WeaponsForAllCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + CPopulation::ms_bGivePedsWeapons = !CPopulation::ms_bGivePedsWeapons; + CStats::CheatedCount += 1000; + CPad::bHasPlayerCheated = true; +} + +void FastTimeCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + if (CTimer::GetTimeScale() < 4.0f) + CTimer::SetTimeScale(CTimer::GetTimeScale() * 2.0f); +} + +void SlowTimeCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + if (CTimer::GetTimeScale() > 0.25f) + CTimer::SetTimeScale(CTimer::GetTimeScale() * 0.5f); +} + +void MoneyCheat() +{ + CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 250000; + CHud::SetHelpMessage(TheText.Get("CHEAT6"), true); +} + +void ArmourCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT4"), true); + FindPlayerPed()->m_fArmour = CWorld::Players[0].m_nMaxArmour; +} + +void WantedLevelUpCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT5"), true); + FindPlayerPed()->m_pWanted->CheatWantedLevel(Min(FindPlayerPed()->m_pWanted->GetWantedLevel() + 2, 6)); +} + +void WantedLevelDownCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT5"), true); + FindPlayerPed()->m_pWanted->CheatWantedLevel(0); +} + +void SunnyWeatherCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT7"), true); + CWeather::ForceWeatherNow(WEATHER_SUNNY); +} + +void ExtraSunnyWeatherCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT7"), true); + CWeather::ForceWeatherNow(WEATHER_EXTRA_SUNNY); +} + +void CloudyWeatherCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT7"), true); + CWeather::ForceWeatherNow(WEATHER_CLOUDY); +} + +void RainyWeatherCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT7"), true); + CWeather::ForceWeatherNow(WEATHER_RAINY); +} + +void FoggyWeatherCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT7"), true); + CWeather::ForceWeatherNow(WEATHER_FOGGY); +} + +void FastWeatherCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + gbFastTime = !gbFastTime; +} + +void OnlyRenderWheelsCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + CVehicle::bWheelsOnlyCheat = !CVehicle::bWheelsOnlyCheat; + CStats::CheatedCount += 1000; + CPad::bHasPlayerCheated = true; +} + +void ChittyChittyBangBangCheat() +{ +#ifdef BETTER_ALLCARSAREDODO_CHEAT + CHud::SetHelpMessage(TheText.Get(!CVehicle::bAllDodosCheat ? "CHEAT1" : "CHEATOF"), true); +#else + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); +#endif + CVehicle::bAllDodosCheat = !CVehicle::bAllDodosCheat; + CStats::CheatedCount += 1000; + CPad::bHasPlayerCheated = true; +} + +void StrongGripCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + CVehicle::bCheat3 = !CVehicle::bCheat3; + CStats::CheatedCount += 1000; + CPad::bHasPlayerCheated = true; +} + +void FannyMagnetCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + CPed::bFannyMagnetCheat = !CPed::bFannyMagnetCheat; + CPad::bHasPlayerCheated = true; +} + +void BlackCarsCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + gbBlackCars = true; + gbPinkCars = false; +} + +void PinkCarsCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + gbBlackCars = false; + gbPinkCars = true; +} + +void TrafficLightsCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + CTrafficLights::bGreenLightsCheat = true; +} + +void MadCarsCheat() +{ + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + CCarCtrl::bMadDriversCheat = true; +} + +void NoSeaBedCheat(void) +{ + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + CWaterLevel::m_bRenderSeaBed = !CWaterLevel::m_bRenderSeaBed; +} + +void RenderWaterLayersCheat(void) +{ + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + if ( ++CWaterLevel::m_nRenderWaterLayers > 5 ) + CWaterLevel::m_nRenderWaterLayers = 0; +} + +void BackToTheFuture(void) +{ + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + CVehicle::bHoverCheat = !CVehicle::bHoverCheat; + CPad::bHasPlayerCheated = true; +} + +void SuicideCheat(void) { + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + FindPlayerPed()->InflictDamage(nil, WEAPONTYPE_UNARMED, 1000.0f, PEDPIECE_TORSO, 0); +} + +void DoChicksWithGunsCheat(void) { + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + CStreaming::SetModelIsDeletable(CGangs::GetGangPedModel1(GANG_PLAYER)); + CStreaming::SetModelIsDeletable(CGangs::GetGangPedModel2(GANG_PLAYER)); + CStreaming::SetModelTxdIsDeletable(CGangs::GetGangPedModel1(GANG_PLAYER)); + CStreaming::SetModelTxdIsDeletable(CGangs::GetGangPedModel2(GANG_PLAYER)); + CStreaming::RemoveCurrentZonesModels(); + CGangs::SetGangPedModels(GANG_PLAYER, MI_HFYBE, MI_WFYBE); + CGangs::SetGangWeapons(GANG_PLAYER, WEAPONTYPE_M4, WEAPONTYPE_M4); + CStats::CheatedCount += 1000; + CPad::bHasPlayerCheated = true; +} + +////////////////////////////////////////////////////////////////////////// + +#ifdef KANGAROO_CHEAT +void KangarooCheat() +{ + wchar *string; + CPed *playerPed = FindPlayerPed(); + int m_fMass; + + if (playerPed->m_ped_flagI80) { + string = TheText.Get("CHEATOF"); + m_fMass = 70.0f; + } else { + string = TheText.Get("CHEAT1"); + m_fMass = 15.0f; + } + CHud::SetHelpMessage(string, true); + playerPed->m_ped_flagI80 = !playerPed->m_ped_flagI80; + + playerPed->m_fMass = m_fMass; + playerPed->m_fAirResistance = 0.4f / m_fMass; +} +#endif + +#ifdef RESTORE_ALLCARSHELI_CHEAT +void AllCarsHeliCheat(void) +{ + wchar* string; + if (bAllCarCheat) { + string = TheText.Get("CHEATOF"); + bAllCarCheat = false; + } + else { + string = TheText.Get("CHEAT1"); + bAllCarCheat = true; + } + CHud::SetHelpMessage(string, true); +} +#endif + +#ifdef WALLCLIMB_CHEAT +void WallClimbingCheat(void) +{ + wchar* string; + if (gGravityCheat) { + string = TheText.Get("CHEATOF"); + gGravityCheat = false; + } + else { + string = TheText.Get("CHEAT1"); + gGravityCheat = true; + } + CHud::SetHelpMessage(string, true); +} +#endif + +void FlyingFishCheat(void) +{ + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + CVehicle::bCheat8 = !CVehicle::bCheat8; + CPad::bHasPlayerCheated = true; +} + +void DoShowChaseStatCheat(void) { + CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); + CStats::ShowChaseStatOnScreen = 1; +} + +bool +CControllerState::CheckForInput(void) +{ + return !!RightStickX || !!RightStickY || !!LeftStickX || !!LeftStickY + || !!DPadUp || !!DPadDown || !!DPadLeft || !!DPadRight + || !!Triangle || !!Cross || !!Circle || !!Square + || !!Start || !!Select + || !!LeftShoulder1 || !!LeftShoulder2 || !!RightShoulder1 || !!RightShoulder2 + || !!LeftShock || !!RightShock; +} + +void +CControllerState::Clear(void) +{ + LeftStickX = LeftStickY = RightStickX = RightStickY = 0; + LeftShoulder1 = LeftShoulder2 = RightShoulder1 = RightShoulder2 = 0; + DPadUp = DPadDown = DPadLeft = DPadRight = 0; + Start = Select = 0; + Square = Triangle = Cross = Circle = 0; + LeftShock = RightShock = 0; + NetworkTalk = 0; +} + +void CKeyboardState::Clear() +{ + for ( int32 i = 0; i < ARRAY_SIZE(F); i++ ) + F[i] = 0; + + for ( int32 i = 0; i < ARRAY_SIZE(VK_KEYS); i++ ) + VK_KEYS[i] = 0; + + ESC = INS = DEL = HOME = END = PGUP = PGDN = 0; + + UP = DOWN = LEFT = RIGHT = 0; + + NUMLOCK = 0; + + DIV = MUL = SUB = ADD = 0; + + DECIMAL = NUM1 = NUM2 = NUM3 = NUM4 = 0; + + NUM5 = NUM6 = NUM7 = NUM8 = 0; + + NUM9 = NUM0 = SCROLLLOCK = PAUSE = 0; + + BACKSP = TAB = CAPSLOCK = EXTENTER = 0; + + LSHIFT = SHIFT = RSHIFT = LCTRL = RCTRL = LALT = RALT = 0; + + LWIN = RWIN = APPS = 0; +} + +#ifdef GTA_PS2_STUFF +void CPad::Initialise(void) +{ +#ifdef GTA_PS2 + scePadInit(0); + + scePadPortOpen(0, 0, pad_dma_buf ); + scePadPortOpen(1, 0, pad2_dma_buf ); +#endif + for (int i = 0; i < MAX_PADS; i++) + { + CPad::GetPad(i)->Clear(true); + CPad::GetPad(i)->Mode = 0; + } + + bObsoleteControllerMessage = false; + bOldDisplayNoControllerMessage = false; + bDisplayNoControllerMessage = false; + m_bMapPadOneToPadTwo = false; + m_bDebugCamPCOn = false; +} +#endif + +void CPad::Clear(bool bResetPlayerControls) +{ + NewState.Clear(); + OldState.Clear(); + + PCTempKeyState.Clear(); + PCTempJoyState.Clear(); + PCTempMouseState.Clear(); + + NewKeyState.Clear(); + OldKeyState.Clear(); + TempKeyState.Clear(); + + NewMouseControllerState.Clear(); + OldMouseControllerState.Clear(); + PCTempMouseControllerState.Clear(); + + Phase = 0; + ShakeFreq = 0; + ShakeDur = 0; + + for (int32 i = 0; i < DRUNK_STEERING_BUFFER_SIZE; i++) + SteeringLeftRightBuffer[i] = 0; + + DrunkDrivingBufferUsed = 0; + + if ( bResetPlayerControls ) + DisablePlayerControls = PLAYERCONTROL_ENABLED; + + JustOutOfFrontend = 0; + bApplyBrakes = false; + + for ( int32 i = 0; i < HORNHISTORY_SIZE; i++ ) + bHornHistory[i] = false; + + iCurrHornHistory = 0; + + for ( int32 i = 0; i < ARRAY_SIZE(CheatString); i++ ) + CheatString[i] = ' '; + + LastTimeTouched = CTimer::GetTimeInMilliseconds(); + AverageWeapon = 0; + AverageEntries = 0; +} + +uint32 CPad::InputHowLongAgo() +{ + return CTimer::GetTimeInMilliseconds() - LastTimeTouched; +} + +void CPad::ClearMouseHistory() +{ + PCTempMouseControllerState.Clear(); + NewMouseControllerState.Clear(); + OldMouseControllerState.Clear(); +} + +// unused +void CPad::ClearKeyBoardHistory() +{ + NewKeyState.Clear(); + OldKeyState.Clear(); + TempKeyState.Clear(); +} + +CMouseControllerState::CMouseControllerState() +{ + LMB = 0; + RMB = 0; + MMB = 0; + WHEELUP = 0; + WHEELDN = 0; + MXB1 = 0; + MXB2 = 0; + + x = 0.0f; + y = 0.0f; +} + +void CMouseControllerState::Clear() +{ + LMB = 0; + RMB = 0; + MMB = 0; + WHEELUP = 0; + WHEELDN = 0; + MXB1 = 0; + MXB2 = 0; +} + +CMouseControllerState CMousePointerStateHelper::GetMouseSetUp() +{ + CMouseControllerState state; + +#if defined RW_D3D9 || defined RWLIBS + if ( PSGLOBAL(mouse) == nil ) + _InputInitialiseMouse(!FrontEndMenuManager.m_bMenuActive && _InputMouseNeedsExclusive()); + + if ( PSGLOBAL(mouse) != nil ) + { + DIDEVCAPS devCaps; + devCaps.dwSize = sizeof(DIDEVCAPS); + + PSGLOBAL(mouse)->GetCapabilities(&devCaps); + switch ( devCaps.dwButtons ) + { + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + state.MMB = true; + + case 2: + state.RMB = true; + + case 1: + state.LMB = true; + } + + if ( devCaps.dwAxes == 3 ) + { + state.WHEELDN = true; + state.WHEELUP = true; + } + } +#elif defined(RW_DC) + printf("TODO this %s\n", __func__); +#else + // It seems there is no way to get number of buttons on mouse, so assign all buttons if we have mouse. + double xpos = 1.0f, ypos; + glfwGetCursorPos(PSGLOBAL(window), &xpos, &ypos); + + if (xpos != 0.f) { + state.MMB = true; + state.RMB = true; + state.LMB = true; + state.WHEELDN = true; + state.WHEELUP = true; + } +#endif + + return state; +} + +void CPad::UpdateMouse() +{ +#if defined RW_D3D9 || defined RWLIBS + if ( IsForegroundApp() ) + { + if ( PSGLOBAL(mouse) == nil ) + _InputInitialiseMouse(!FrontEndMenuManager.m_bMenuActive && _InputMouseNeedsExclusive()); + + DIMOUSESTATE2 state; + + if ( PSGLOBAL(mouse) != nil && SUCCEEDED(_InputGetMouseState(&state)) ) + { + int32 signX = 1; + int32 signy = 1; + + if ( !FrontEndMenuManager.m_bMenuActive ) + { + if ( MousePointerStateHelper.bInvertVertically ) + signy = -1; + if ( MousePointerStateHelper.bInvertHorizontally ) + signX = -1; + } + + PCTempMouseControllerState.Clear(); + + PCTempMouseControllerState.x = (float)(signX * state.lX); + PCTempMouseControllerState.y = (float)(signy * state.lY); + PCTempMouseControllerState.LMB = state.rgbButtons[0] & 128; + PCTempMouseControllerState.RMB = state.rgbButtons[1] & 128; + PCTempMouseControllerState.MMB = state.rgbButtons[2] & 128; + PCTempMouseControllerState.MXB1 = state.rgbButtons[3] & 128; + PCTempMouseControllerState.MXB2 = state.rgbButtons[4] & 128; + + if ( state.lZ > 0 ) + PCTempMouseControllerState.WHEELUP = 1; + else if ( state.lZ < 0 ) + PCTempMouseControllerState.WHEELDN = 1; + + OldMouseControllerState = NewMouseControllerState; + NewMouseControllerState = PCTempMouseControllerState; + } + } +#elif defined(RW_DC) + // Do mouse stuff here? + // printf("TODO this %s\n", __func__); +#else + if ( IsForegroundApp() && PSGLOBAL(cursorIsInWindow) ) + { + double xpos = 1.0f, ypos; + glfwGetCursorPos(PSGLOBAL(window), &xpos, &ypos); + if (xpos == 0.f) + return; + + int32 signX = 1; + int32 signy = 1; + + if (!FrontEndMenuManager.m_bMenuActive) + { + if (MousePointerStateHelper.bInvertVertically) + signy = -1; + if (MousePointerStateHelper.bInvertHorizontally) + signX = -1; + } + + PCTempMouseControllerState.Clear(); + + PCTempMouseControllerState.x = (float)(signX * (xpos - PSGLOBAL(lastMousePos.x))); + PCTempMouseControllerState.y = (float)(signy * (ypos - PSGLOBAL(lastMousePos.y))); + PCTempMouseControllerState.LMB = glfwGetMouseButton(PSGLOBAL(window), GLFW_MOUSE_BUTTON_LEFT); + PCTempMouseControllerState.RMB = glfwGetMouseButton(PSGLOBAL(window), GLFW_MOUSE_BUTTON_RIGHT); + PCTempMouseControllerState.MMB = glfwGetMouseButton(PSGLOBAL(window), GLFW_MOUSE_BUTTON_MIDDLE); + PCTempMouseControllerState.MXB1 = glfwGetMouseButton(PSGLOBAL(window), GLFW_MOUSE_BUTTON_4); + PCTempMouseControllerState.MXB2 = glfwGetMouseButton(PSGLOBAL(window), GLFW_MOUSE_BUTTON_5); + + if (PSGLOBAL(mouseWheel) > 0) + PCTempMouseControllerState.WHEELUP = 1; + else if (PSGLOBAL(mouseWheel) < 0) + PCTempMouseControllerState.WHEELDN = 1; + + PSGLOBAL(lastMousePos.x) = xpos; + PSGLOBAL(lastMousePos.y) = ypos; + PSGLOBAL(mouseWheel) = 0.0f; + + OldMouseControllerState = NewMouseControllerState; + NewMouseControllerState = PCTempMouseControllerState; + } +#endif +} + +CControllerState CPad::ReconcileTwoControllersInput(CControllerState const &State1, CControllerState const &State2) +{ + static CControllerState ReconState; + + ReconState.Clear(); + +#define _RECONCILE_BUTTON(button) \ + { if ( State1.button || State2.button ) ReconState.button = 255; } + +#define _RECONCILE_AXIS_POSITIVE(axis) \ + { if ( State1.axis >= 0 && State2.axis >= 0 ) ReconState.axis = Max(State1.axis, State2.axis); } + +#define _RECONCILE_AXIS_NEGATIVE(axis) \ + { if ( State1.axis <= 0 && State2.axis <= 0 ) ReconState.axis = Min(State1.axis, State2.axis); } + +#define _RECONCILE_AXIS(axis) \ + { _RECONCILE_AXIS_POSITIVE(axis); _RECONCILE_AXIS_NEGATIVE(axis); } + +#define _FIX_AXIS_DIR(axis) \ + { if ( State1.axis > 0 && State2.axis < 0 || State1.axis < 0 && State2.axis > 0 ) ReconState.axis = 0; } + +#define _FIX_RECON_DIR(pos, neg, axis) \ + { if ( (ReconState.pos || ReconState.axis < 0) && (ReconState.neg || ReconState.axis > 0) ) { ReconState.pos = 0; ReconState.neg = 0; ReconState.axis = 0; } } + + _RECONCILE_BUTTON(LeftShoulder1); + _RECONCILE_BUTTON(LeftShoulder2); + _RECONCILE_BUTTON(RightShoulder1); + _RECONCILE_BUTTON(RightShoulder2); + _RECONCILE_BUTTON(Start); + _RECONCILE_BUTTON(Select); + _RECONCILE_BUTTON(Square); + _RECONCILE_BUTTON(Triangle); + _RECONCILE_BUTTON(Cross); + _RECONCILE_BUTTON(Circle); + _RECONCILE_BUTTON(LeftShock); + _RECONCILE_BUTTON(RightShock); + _RECONCILE_BUTTON(NetworkTalk); + _RECONCILE_AXIS(LeftStickX); + _RECONCILE_AXIS(LeftStickY); + _FIX_AXIS_DIR(LeftStickX); + _FIX_AXIS_DIR(LeftStickY); + _RECONCILE_AXIS(RightStickX); + _RECONCILE_AXIS(RightStickY); + _FIX_AXIS_DIR(RightStickX); + _FIX_AXIS_DIR(RightStickY); + _RECONCILE_BUTTON(DPadUp); + _RECONCILE_BUTTON(DPadDown); + _RECONCILE_BUTTON(DPadLeft); + _RECONCILE_BUTTON(DPadRight); + _FIX_RECON_DIR(DPadUp, DPadDown, LeftStickY); + _FIX_RECON_DIR(DPadLeft, DPadRight, LeftStickX); + + return ReconState; + +#undef _RECONCILE_BUTTON +#undef _RECONCILE_AXIS_POSITIVE +#undef _RECONCILE_AXIS_NEGATIVE +#undef _RECONCILE_AXIS +#undef _FIX_AXIS_DIR +#undef _FIX_RECON_DIR +} + +void CPad::StartShake(int16 nDur, uint8 nFreq) +{ + if ( !FrontEndMenuManager.m_PrefsUseVibration ) + return; + + if ( CCutsceneMgr::IsRunning() || CGame::playingIntro ) + return; + + if ( nFreq == 0 ) + { + ShakeDur = 0; + ShakeFreq = 0; + return; + } + + if ( nDur > ShakeDur ) + { + ShakeDur = nDur; + ShakeFreq = nFreq; + } +} + +void CPad::StartShake_Distance(int16 nDur, uint8 nFreq, float fX, float fY, float fZ) +{ + if ( !FrontEndMenuManager.m_PrefsUseVibration ) + return; + + if ( CCutsceneMgr::IsRunning() || CGame::playingIntro ) + return; + + float fDist = ( TheCamera.GetPosition() - CVector(fX, fY, fZ) ).Magnitude(); + + if ( fDist < 70.0f ) + { + if ( nFreq == 0 ) + { + ShakeDur = 0; + ShakeFreq = 0; + return; + } + + if ( nDur > ShakeDur ) + { + ShakeDur = nDur; + ShakeFreq = nFreq; + } + } +} + +void CPad::StartShake_Train(float fX, float fY) +{ + if ( !FrontEndMenuManager.m_PrefsUseVibration ) + return; + + if ( CCutsceneMgr::IsRunning() || CGame::playingIntro ) + return; + + if (FindPlayerVehicle() != nil && FindPlayerVehicle()->IsTrain() ) + return; + + float fDist = ( TheCamera.GetPosition() - CVector(fX, fY, 0.0f) ).Magnitude2D(); + + if ( fDist < 70.0f ) + { + int32 freq = (int32)((70.0f - fDist) * 70.0f / 70.0f + 30.0f); + + if ( ShakeDur < 100 ) + { + ShakeDur = 100; + ShakeFreq = freq; + } + } +} + +#ifdef GTA_PS2_STUFF +void CPad::AddToCheatString(char c) +{ + for ( int32 i = ARRAY_SIZE(CheatString) - 2; i >= 0; i-- ) + CheatString[i + 1] = CheatString[i]; + + CheatString[0] = c; + +#define _CHEATCMP(str) strncmp(str, CheatString, sizeof(str)-1) + // "4414LDRULDRU" - R2 R2 L1 R2 LEFT DOWN RIGHT UP LEFT DOWN RIGHT UP + if ( !_CHEATCMP("URDLURDL4144") ) + WeaponCheat1(); + + // "4411LDRULDRU" - R2 R2 L1 L1 LEFT DOWN RIGHT UP LEFT DOWN RIGHT UP + else if ( !_CHEATCMP("URDLURDL1144") ) + MoneyCheat(); + + // "4412LDRULDRU" - R2 R2 L1 L2 LEFT DOWN RIGHT UP LEFT DOWN RIGHT UP + else if ( !_CHEATCMP("URDLURDL2144") ) + ArmourCheat(); + + // "4413LDRULDRU" - R2 R2 L1 R1 LEFT DOWN RIGHT UP LEFT DOWN RIGHT UP + else if ( !_CHEATCMP("URDLURDL3144") ) + HealthCheat(); + + // "4414LRLRLR" - R2 R2 L1 R2 LEFT RIGHT LEFT RIGHT LEFT RIGHT + else if ( !_CHEATCMP("RLRLRL4144") ) + WantedLevelUpCheat(); + + // "4414UDUDUD" - R2 R2 L1 R2 UP DOWN UP DOWN UP DOWN + else if ( !_CHEATCMP("DUDUDU4144") ) + WantedLevelDownCheat(); + + // "1234432T" - L1 L2 R1 R2 R2 R1 L2 TRIANGLE + else if ( !_CHEATCMP("T2344321") ) + SunnyWeatherCheat(); + + // "1234432S" - L1 L2 R1 R2 R2 R1 L2 SQUARE + else if ( !_CHEATCMP("S2344321") ) + CloudyWeatherCheat(); + + // "1234432C" - L1 L2 R1 R2 R2 R1 L2 CIRCLE + else if ( !_CHEATCMP("C2344321") ) + RainyWeatherCheat(); + + // "1234432X" - L1 L2 R1 R2 R2 R1 L2 CROSS + else if ( !_CHEATCMP("X2344321") ) + FoggyWeatherCheat(); + + // "CCCCCC321TCT" - CIRCLE CIRCLE CIRCLE CIRCLE CIRCLE CIRCLE R1 L2 L1 TRIANGLE CIRCLE TRIANGLE + else if ( !_CHEATCMP("TCT123CCCCCC") ) + VehicleCheat(MI_RHINO); + + // "CCCSSSSS1TCT" - CIRCLE CIRCLE CIRCLE SQUARE SQUARE SQUARE SQUARE SQUARE L1 TRIANGLE CIRCLE TRIANGLE + else if ( !_CHEATCMP("TCT1SSSSSCCC") ) + FastWeatherCheat(); + + // "241324TSCT21" - L2 R2 L1 R1 L2 R2 TRIANGLE SQUARE CIRCLE TRIANGLE L2 L1 + else if ( !_CHEATCMP("12TCST423142") ) + BlowUpCarsCheat(); + + // "RDLU12ULDR" - RIGHT DOWN LEFT UP L1 L2 UP LEFT DOWN RIGHT + else if ( !_CHEATCMP("RDLU21ULDR") ) + ChangePlayerCheat(); + + // "DULUX3421" - DOWN UP LEFT UP CROSS R1 R2 L2 L1 + else if ( !_CHEATCMP("1243XULUD") ) + MayhemCheat(); + + // "DULUX3412" - DOWN UP LEFT UP CROSS R1 R2 L1 L2 + else if ( !_CHEATCMP("2143XULUD") ) + EverybodyAttacksPlayerCheat(); + + // "43TX21UD" - R2 R1 TRIANGLE CROSS L2 L1 UP DOWN + else if ( !_CHEATCMP("DU12XT34") ) + WeaponsForAllCheat(); + + // "TURDS12" - TRIANGLE UP RIGHT DOWN SQUARE L1 L2 + else if ( !_CHEATCMP("21SDRUT") ) + FastTimeCheat(); + + // "TURDS34" - TRIANGLE UP RIGHT DOWN SQUARE R1 R2 + else if ( !_CHEATCMP("43SDRUT") ) + SlowTimeCheat(); + + // "11S4T1T" - L1 L1 SQUARE R2 TRIANGLE L1 TRIANGLE + else if ( !_CHEATCMP("T1T4S11") ) + OnlyRenderWheelsCheat(); + + // "R4C32D13" - RIGHT R2 CIRCLE R1 L2 DOWN L1 R1 + else if ( !_CHEATCMP("31D23C4R") ) + ChittyChittyBangBangCheat(); + + // "3141L33T" - R1 L1 R2 L1 LEFT R1 R1 TRIANGLE + else if ( !_CHEATCMP("T33L1413") ) + StrongGripCheat(); + +#undef _CHEATCMP +} +#endif + +int Cheat_strncmp(char* sourceStr, char* origCheatStr) +{ +#define ccmp(n) if((uint8)sourceStr[i] != (uint8)origCheatStr[i] - n) return 1; + int i = 0; + while(origCheatStr[i]) + { + switch(i) + { + case 0: ccmp(3); break; + case 1: ccmp(5); break; + case 2: ccmp(7); break; + case 3: ccmp(1); break; + case 4: ccmp(13); break; + case 5: ccmp(27); break; + case 6: ccmp(3); break; + case 7: ccmp(7); break; + case 8: ccmp(1); break; + case 9: ccmp(11); break; + case 10: ccmp(13); break; + case 11: ccmp(8); break; + case 12: ccmp(7); break; + case 13: ccmp(32); break; + case 14: ccmp(13); break; + case 15: ccmp(6); break; + case 16: ccmp(28); break; + case 17: ccmp(19); break; + case 18: ccmp(10); break; + case 19: ccmp(3); break; + case 20: ccmp(3); break; + case 21: ccmp(5); break; + case 22: ccmp(7); break; + case 23: ccmp(1); break; + case 24: ccmp(13); break; + case 25: ccmp(27); break; + case 26: ccmp(3); break; + case 27: ccmp(7); break; + default: return 1; + } + i++; + } + return 0; +#undef ccmp +} + +// TODO(Miami): Mobile has changed some of the cheats to include debugging things +void CPad::AddToPCCheatString(char c) +{ + for (int32 i = ARRAY_SIZE(KeyBoardCheatString) - 2; i >= 0; i--) + KeyBoardCheatString[i + 1] = KeyBoardCheatString[i]; + + KeyBoardCheatString[0] = c; + +#define _CHEATCMP(str) strncmp(str, KeyBoardCheatString, sizeof(str)-1) + + // "THUGSTOOLS" + if (!Cheat_strncmp(KeyBoardCheatString, "VQVPanJ\\I_")) { + KeyBoardCheatString[0] = ' '; + WeaponCheat1(); + } + // "PROFESSIONALTOOLS" + else if (!Cheat_strncmp(KeyBoardCheatString, "VQVPagDUPT`[Lf\\Xl")) { + KeyBoardCheatString[0] = ' '; + WeaponCheat2(); + } + // "NUTTERTOOLS" + else if (!Cheat_strncmp(KeyBoardCheatString, "VQVPamH[U`[")) { + KeyBoardCheatString[0] = ' '; + WeaponCheat3(); + } + // "PRECIOUSPROTECTION" + else if (!Cheat_strncmp(KeyBoardCheatString, "QTPUP`WVS[`]ViPKnc")) { + KeyBoardCheatString[0] = ' '; + ArmourCheat(); + } + // "ASPIRINE" + else if (!Cheat_strncmp(KeyBoardCheatString, "HSPSVkVH")) { + KeyBoardCheatString[0] = ' '; + HealthCheat(); + } + // "YOUWONTTAKEMEALIVE" + else if (!Cheat_strncmp(KeyBoardCheatString, "H[PMN`PLLLa\\Uod[kl")) { + KeyBoardCheatString[0] = ' '; + WantedLevelUpCheat(); + } + // "LEAVEMEALONE" + else if (!Cheat_strncmp(KeyBoardCheatString, "HSVMN`PLWLRT")) { + KeyBoardCheatString[0] = ' '; + WantedLevelDownCheat(); + } + // "APLEASANTDAY" + else if (!Cheat_strncmp(KeyBoardCheatString, "\\FKU[\\VHFW]I")) { + KeyBoardCheatString[0] = ' '; + SunnyWeatherCheat(); + } + // "ALOVELYDAY" + else if (!Cheat_strncmp(KeyBoardCheatString, "\\FKZY`YVML")) { + KeyBoardCheatString[0] = ' '; + ExtraSunnyWeatherCheat(); + } + // "ABITDRIEG" + else if (!Cheat_strncmp(KeyBoardCheatString, "JJPSQoLIB")) { + KeyBoardCheatString[0] = ' '; + CloudyWeatherCheat(); + } + // "CATSANDDOGS" + else if (!Cheat_strncmp(KeyBoardCheatString, "VLVEQiDZULP")) { + KeyBoardCheatString[0] = ' '; + RainyWeatherCheat(); + } + // "CANTSEEATHING" + else if (!Cheat_strncmp(KeyBoardCheatString, "JSPIa\\HLT_[IJ")) { + KeyBoardCheatString[0] = ' '; + FoggyWeatherCheat(); + } + // "PANZER" + else if (!Cheat_strncmp(KeyBoardCheatString, "UJaONk")) { + KeyBoardCheatString[0] = ' '; + VehicleCheat(MI_RHINO); + } + // "LIFEISPASSINGMEBY" + else if (!Cheat_strncmp(KeyBoardCheatString, "\\GLNTiLZTL][PeSOh")) { + KeyBoardCheatString[0] = ' '; + FastWeatherCheat(); + } + // "BIGBANG" + else if (!Cheat_strncmp(KeyBoardCheatString, "JSHCTdE")) { + KeyBoardCheatString[0] = ' '; + BlowUpCarsCheat(); + } + // "STILLLIKEDRESSINGUP" + else if (!Cheat_strncmp(KeyBoardCheatString, "SZNOVnVLSORSPlYReg]")) { + KeyBoardCheatString[0] = ' '; + ChangePlayerCheat(); + } + // "FIGHTFIGHTFIGHT" + else if (!Cheat_strncmp(KeyBoardCheatString, "WMNJSoKNJQaPNiS")) { + KeyBoardCheatString[0] = ' '; + MayhemCheat(); + } + // "NOBODYLIKESME" + else if (!Cheat_strncmp(KeyBoardCheatString, "HRZFXdO`EZOWU")) { + KeyBoardCheatString[0] = ' '; + EverybodyAttacksPlayerCheat(); + } + // "OURGODGIVENRIGHTTOBEARARMS" + else if (!Cheat_strncmp(KeyBoardCheatString, "VRYB_\\HIP_aPNi_TaiSJGTNSbj")) { + KeyBoardCheatString[0] = ' '; + WeaponsForAllCheat(); + } + // "ONSPEED" + else if (!Cheat_strncmp(KeyBoardCheatString, "GJLQ`iR")) { + KeyBoardCheatString[0] = ' '; + FastTimeCheat(); + } + // "BOOOOOORING" + else if (!Cheat_strncmp(KeyBoardCheatString, "JSPS\\jRVPZO")) { + KeyBoardCheatString[0] = ' '; + SlowTimeCheat(); + } + // "WHEELSAREALLINEED" + else if (!Cheat_strncmp(KeyBoardCheatString, "GJLOVgOHF]N[SeRNs")) { + KeyBoardCheatString[0] = ' '; + OnlyRenderWheelsCheat(); + } + //COMEFLYWITHME + else if (!Cheat_strncmp(KeyBoardCheatString, "HROUVr\\SGPZWJ")) { + KeyBoardCheatString[0] = ' '; + ChittyChittyBangBangCheat(); + } + // "GRIPISEVERYTHING" + else if (!Cheat_strncmp(KeyBoardCheatString, "JSPIatULWP`QWi_M")) { + KeyBoardCheatString[0] = ' '; + StrongGripCheat(); + } + // "CHASESTAT" + else if (!Cheat_strncmp(KeyBoardCheatString, "WF[TRnDOD")) { + KeyBoardCheatString[0] = ' '; + DoShowChaseStatCheat(); + } + // "CHICKSWITHGUNS" + else if (!Cheat_strncmp(KeyBoardCheatString, "VS\\HUoL^TVPQOc")) { + KeyBoardCheatString[0] = ' '; + DoChicksWithGunsCheat(); + } + // "ICANTTAKEITANYMORE" + else if (!Cheat_strncmp(KeyBoardCheatString, "HWVNfiD[JPXI[t[G_\\")) { + KeyBoardCheatString[0] = ' '; + SuicideCheat(); + } + // "GREENLIGHT" + else if (!Cheat_strncmp(KeyBoardCheatString, "WMNJYiHLSR")) { + KeyBoardCheatString[0] = ' '; + TrafficLightsCheat(); + } + // "MIAMITRAFFIC" + else if (!Cheat_strncmp(KeyBoardCheatString, "FNMGNmWPNLVU")) { + KeyBoardCheatString[0] = ' '; + MadCarsCheat(); + } + // "AHAIRDRESSERSCAR" + else if (!Cheat_strncmp(KeyBoardCheatString, "UFJT_`VZF]QZPaUG")) { + KeyBoardCheatString[0] = ' '; + PinkCarsCheat(); + } + // "IWANTITPAINTEDBLACK" + else if (!Cheat_strncmp(KeyBoardCheatString, "NHHMO_H[OTNX[iaT]jS")) { + KeyBoardCheatString[0] = ' '; + BlackCarsCheat(); + } + // "TRAVELINSTYLE" + else if (!Cheat_strncmp(KeyBoardCheatString, "HQ`U`iLSFaNZ[")) { + KeyBoardCheatString[0] = ' '; + VehicleCheat(MI_BLOODRA); + } + // "THELASTRIDE" + else if (!Cheat_strncmp(KeyBoardCheatString, "HIPSanDSFSa")) { + KeyBoardCheatString[0] = ' '; + VehicleCheat(MI_ROMERO); + } + // "ROCKANDROLLCAR" + else if (!Cheat_strncmp(KeyBoardCheatString, "UFJMYjUKOLXKVr")) { + KeyBoardCheatString[0] = ' '; + VehicleCheat(MI_LOVEFIST); + } + // "RUBBISHCAR" + else if (!Cheat_strncmp(KeyBoardCheatString, "UFJI`dEIV]")) { + KeyBoardCheatString[0] = ' '; + VehicleCheat(MI_TRASH); + } + // "GETTHEREQUICKLY" + else if (!Cheat_strncmp(KeyBoardCheatString, "\\QRDVpTLSPU\\[eT")) { + KeyBoardCheatString[0] = ' '; + VehicleCheat(MI_BLOODRB); + } + // "GETTHEREFAST" + else if (!Cheat_strncmp(KeyBoardCheatString, "WXHGRmHOU_RO")) { + KeyBoardCheatString[0] = ' '; + VehicleCheat(MI_SABRETUR); + } + // "BETTERTHANWALKING" + else if (!Cheat_strncmp(KeyBoardCheatString, "JSPLY\\ZUBSaZLtaK^")) { + KeyBoardCheatString[0] = ' '; + VehicleCheat(MI_CADDY); + } + // "GETTHEREFASTINDEED" + else if (!Cheat_strncmp(KeyBoardCheatString, "GJLE[dWZBQfZLvRXa[^WHL")) { + KeyBoardCheatString[0] = ' '; + VehicleCheat(MI_HOTRINA); + } + // "GETTHEREAMAZINGLYFAST" + else if (!Cheat_strncmp(KeyBoardCheatString, "WXHGfgJUJeNUHe_Kdg^HJ")) { + KeyBoardCheatString[0] = ' '; + VehicleCheat(MI_HOTRINB); + } + // LOOKLIKELANCE + else if (!Cheat_strncmp(KeyBoardCheatString, "HHUBY`NPMV\\WS")) { + KeyBoardCheatString[0] = ' '; + ChangePlayerModel("igbuddy"); + } + // IWANTBIGTITS + else if (!Cheat_strncmp(KeyBoardCheatString, "VYPUTdE[OLdQ")) { + KeyBoardCheatString[0] = ' '; + ChangePlayerModel("igcandy"); + } + // MYSONISALAWYER + else if (!Cheat_strncmp(KeyBoardCheatString, "UJ`XNgDZJY\\[`m")) { + KeyBoardCheatString[0] = ' '; + ChangePlayerModel("igken"); + } + // ILOOKLIKEHILARY + else if (!Cheat_strncmp(KeyBoardCheatString, "\\WHMVcHRJWXWVlV")) { + KeyBoardCheatString[0] = ' '; + ChangePlayerModel("ighlary"); + } + // ROCKANDROLLMAN + else if (!Cheat_strncmp(KeyBoardCheatString, "QFTMYjUKOLXKVr")) { + KeyBoardCheatString[0] = ' '; + ChangePlayerModel("igjezz"); + } + // ONEARMEDBANDIT + else if (!Cheat_strncmp(KeyBoardCheatString, "WNKON]GLN]NMUo")) { + KeyBoardCheatString[0] = ' '; + ChangePlayerModel("igphil"); + } + // IDONTHAVETHEMONEYSONNY + else if (!Cheat_strncmp(KeyBoardCheatString, "\\SUP`tHUPXRP[ecGdgXRGN")) { + KeyBoardCheatString[0] = ' '; + ChangePlayerModel("igsonny"); + } + // FOXYLITTLETHING + else if (!Cheat_strncmp(KeyBoardCheatString, "JSPIa`O[UTYa_oS")) { + KeyBoardCheatString[0] = ' '; + ChangePlayerModel("igmerc"); + } + // WELOVEOURDICK + else if (!Cheat_strncmp(KeyBoardCheatString, "NHPE_pRLWZYM^")) { + KeyBoardCheatString[0] = ' '; + ChangePlayerModel("igdick"); + } + // CHEATSHAVEBEENCRACKED + else if (!Cheat_strncmp(KeyBoardCheatString, "GJRDNmFUFPOM]aUYpTOKF")) { + KeyBoardCheatString[0] = ' '; + ChangePlayerModel("igdiaz"); + } + // DEEPFRIEDMARSBARS + else if (!Cheat_strncmp(KeyBoardCheatString, "VWHC`mDTEPVZMpRK")) { + KeyBoardCheatString[0] = ' '; + gfTommyFatness = 0.26f; + } + // PROGRAMMER + else if (!Cheat_strncmp(KeyBoardCheatString, "UJTNNmJVS[")) { + KeyBoardCheatString[0] = ' '; + gfTommyFatness = -0.3f; + } + // SEAWAYS + else if (!Cheat_strncmp(KeyBoardCheatString, "V^HXN`V")) { + KeyBoardCheatString[0] = ' '; + BackToTheFuture(); + } + // LOADSOFLITTLETHINGS + else if (!Cheat_strncmp(KeyBoardCheatString, "VLUJUoHSU_VTMo`J]bV")) { + KeyBoardCheatString[0] = ' '; + SpecialCarCheats(); + } + // HOPINGIRL + else if (!Cheat_strncmp(KeyBoardCheatString, "OWPH[dSVI")) { + KeyBoardCheatString[0] = ' '; + PickUpChicksCheat(); + } + //CERTAINDEATH + else if (!Cheat_strncmp(KeyBoardCheatString, "KYHFQiLHU]RK")) { + KeyBoardCheatString[0] = ' '; + CSmokeTrails::CigOn = !CSmokeTrails::CigOn; + } + //AIRSHIP + else if (!Cheat_strncmp(KeyBoardCheatString, "SNOT_dD")) { + KeyBoardCheatString[0] = ' '; + FlyingFishCheat(); + } + //FANNYMAGNET + else if (!Cheat_strncmp(KeyBoardCheatString, "WJUHNh\\UOLS")) { + KeyBoardCheatString[0] = ' '; + FannyMagnetCheat(); + } + +#ifdef KANGAROO_CHEAT + // "KANGAROO" + if (!_CHEATCMP("OORAGNAK")) + KangarooCheat(); +#endif + +#ifndef MASTER + // "PEDDEBUG" + if (!_CHEATCMP("GUBEDDEP")) + CPed::SwitchDebugDisplay(); +#endif + +#ifdef RESTORE_ALLCARSHELI_CHEAT + // "CARSAREHELI" + if (!_CHEATCMP("ILEHERASRAC")) + AllCarsHeliCheat(); +#endif + +#ifdef WALLCLIMB_CHEAT + // "SPIDERCAR" + if (!_CHEATCMP("RACREDIPS")) + WallClimbingCheat(); +#endif + +#if !defined(PC_WATER) && defined(WATER_CHEATS) + // SEABEDCHEAT + if (!_CHEATCMP("TAEHCDEBAESON")) + NoSeaBedCheat(); + + // WATERLAYERSCHEAT + if (!_CHEATCMP("TAEHCSREYALRETAW")) + RenderWaterLayersCheat(); +#endif + +#undef _CHEATCMP +} + +#ifdef XINPUT +int CPad::XInputJoy1 = 0; +int CPad::XInputJoy2 = 1; +void CPad::AffectFromXinput(uint32 pad) +{ + pad = pad == 0 ? XInputJoy1 : XInputJoy2; + if (pad == -1) // LoadINIControllerSettings can set it to -1 + return; + + XINPUT_STATE xstate; + memset(&xstate, 0, sizeof(XINPUT_STATE)); + if (XInputGetState(pad, &xstate) == ERROR_SUCCESS) + { + PCTempJoyState.Circle = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_B) ? 255 : 0; + PCTempJoyState.Cross = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_A) ? 255 : 0; + PCTempJoyState.Square = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_X) ? 255 : 0; + PCTempJoyState.Triangle = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_Y) ? 255 : 0; + PCTempJoyState.DPadDown = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) ? 255 : 0; + PCTempJoyState.DPadLeft = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) ? 255 : 0; + PCTempJoyState.DPadRight = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) ? 255 : 0; + PCTempJoyState.DPadUp = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) ? 255 : 0; + PCTempJoyState.LeftShock = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) ? 255 : 0; + PCTempJoyState.LeftShoulder1 = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) ? 255 : 0; + PCTempJoyState.LeftShoulder2 = xstate.Gamepad.bLeftTrigger; + PCTempJoyState.RightShock = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) ? 255 : 0; + PCTempJoyState.RightShoulder1 = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) ? 255 : 0; + PCTempJoyState.RightShoulder2 = xstate.Gamepad.bRightTrigger; + + PCTempJoyState.Select = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) ? 255 : 0; +#ifdef REGISTER_START_BUTTON + PCTempJoyState.Start = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_START) ? 255 : 0; +#endif + float lx = (float)xstate.Gamepad.sThumbLX / (float)0x7FFF; + float ly = (float)xstate.Gamepad.sThumbLY / (float)0x7FFF; + float rx = (float)xstate.Gamepad.sThumbRX / (float)0x7FFF; + float ry = (float)xstate.Gamepad.sThumbRY / (float)0x7FFF; + + if (Abs(lx) > 0.3f || Abs(ly) > 0.3f) { + PCTempJoyState.LeftStickX = (int32)(lx * 128.0f); + PCTempJoyState.LeftStickY = (int32)(-ly * 128.0f); + } + + if (Abs(rx) > 0.3f || Abs(ry) > 0.3f) { + PCTempJoyState.RightStickX = (int32)(rx * 128.0f); + PCTempJoyState.RightStickY = (int32)(-ry * 128.0f); + } + + XINPUT_VIBRATION VibrationState; + + memset(&VibrationState, 0, sizeof(XINPUT_VIBRATION)); + + uint16 iLeftMotor = (uint16)((float)ShakeFreq / 255.0f * (float)0xffff); + uint16 iRightMotor = (uint16)((float)ShakeFreq / 255.0f * (float)0xffff); + + if (ShakeDur < CTimer::GetTimeStepInMilliseconds()) + ShakeDur = 0; + else + ShakeDur -= CTimer::GetTimeStepInMilliseconds(); + if (ShakeDur == 0) ShakeFreq = 0; + + VibrationState.wLeftMotorSpeed = iLeftMotor; + VibrationState.wRightMotorSpeed = iRightMotor; + + XInputSetState(pad, &VibrationState); + } +} +#endif + +void CPad::UpdatePads(void) +{ + bool bUpdate = true; + + GetPad(0)->UpdateMouse(); +#ifdef XINPUT + GetPad(0)->AffectFromXinput(m_bMapPadOneToPadTwo ? 1 : 0); + GetPad(1)->AffectFromXinput(m_bMapPadOneToPadTwo ? 0 : 1); +#else + CapturePad(0); +#endif + + // Improve keyboard input latency part 1 +#ifdef FIX_BUGS + OldKeyState = NewKeyState; + NewKeyState = TempKeyState; +#endif + +#ifdef DETECT_PAD_INPUT_SWITCH + if (GetPad(0)->PCTempJoyState.CheckForInput()) + IsAffectedByController = true; + else { +#endif + ControlsManager.ClearSimButtonPressCheckers(); + ControlsManager.AffectPadFromKeyBoard(); + ControlsManager.AffectPadFromMouse(); + +#ifdef DETECT_PAD_INPUT_SWITCH + } + if (IsAffectedByController && (GetPad(0)->PCTempKeyState.CheckForInput() || GetPad(0)->PCTempMouseState.CheckForInput())) + IsAffectedByController = false; +#endif + + if ( CReplay::IsPlayingBackFromFile() && !FrontEndMenuManager.m_bMenuActive ) + bUpdate = false; + + if ( bUpdate ) + GetPad(0)->Update(0); + +#ifndef MASTER + GetPad(1)->Update(1); +#else + GetPad(1)->NewState.Clear(); + GetPad(1)->OldState.Clear(); +#endif + + // Improve keyboard input latency part 2 +#ifndef FIX_BUGS + OldKeyState = NewKeyState; + NewKeyState = TempKeyState; +#endif +} + +void CPad::ProcessPCSpecificStuff(void) +{ + ; +} + +void CPad::Update(int16 pad) +{ + OldState = NewState; + +#ifdef GTA_PS2 + bObsoleteControllerMessage = false; + + //int iPressureBtn; + int id; + int ext_id=0; + int state; + int rterm_id = 0; + unsigned short paddata, tpad; + unsigned char rdata[32]; + + state = scePadGetState(pad, 0); + + switch(Phase) + { + case 0: + if (state != scePadStateStable && state != scePadStateFindCTP1) + break; + id = scePadInfoMode(pad, 0, InfoModeCurID, 0); + if (id==0) break; + + ext_id = scePadInfoMode(pad, 0, InfoModeCurExID, 0); + if (ext_id>0) id = ext_id; + + switch(id) + { + case 4: // Digital controller + Phase = 40; // Try for analog(dualshock) + break; + case 7: // Dualshock2 controller + Phase = 50; + break; + default: + Phase = 99; + break; + } + break; + + // Analog Controller (old dualshock) + case 40: // Analog Contoller check valid (otherwise fail phase) + if (scePadInfoMode(pad, 0, InfoModeIdTable, -1)==0) + { + Phase = 99; + break; + } + Phase++; + + case 41: // Analog controller: Request Lock analog mode (asynchronous) + if (scePadSetMainMode(pad, 0, 1, 3)==1) Phase++; + break; + + case 42: // Analog controller: Check state of previous request + if (scePadGetReqState(pad, 0)==scePadReqStateFaild) + { + Phase--; + } + + if (scePadGetReqState(pad, 0)==scePadReqStateComplete) + { + // Lock mode complete + Phase=0; // Accept normal dualshock + } + break; + + // DualShock 2 Controller + case 50: // Analog Contoller check valid (otherwise fail phase) + if (scePadInfoMode(pad, 0, InfoModeIdTable, -1)==0) + { + Phase = 99; + break; + } + Phase++; + + case 51: // Analog controller: Request Lock analog mode (asynchronous) + if (scePadSetMainMode(pad, 0, 1, 3)==1) Phase++; + break; + + case 52: // Analog controller: Check state of previous request + if (scePadGetReqState(pad, 0)==scePadReqStateFaild) + { + Phase--; + } + + if (scePadGetReqState(pad, 0)==scePadReqStateComplete) + { + // Lock mode complete + Phase=0; // Accept normal dualshock + } + break; + + case 70: // DualShock 2 check pressure sensitive possible + if (scePadInfoPressMode(pad, 0)==1) + { + Phase = 76; + break; + } + Phase = 99; + break; + + case 76: // DualShock2 enable pressure sensitive mode (asynchronous function) + if (scePadEnterPressMode(pad, 0)==1) Phase++; + break; + + case 77: // Dualshock2 check status of request pressure sensitive mode + if (scePadGetReqState(pad, 0)==scePadReqStateFaild) Phase--; + if (scePadGetReqState(pad, 0)==scePadReqStateComplete) + { + Phase=80; + } + break; + + // DualShock 2 Controller + case 80: // Set motors + if (scePadInfoAct(pad, 0, -1, 0)==0) + { + Phase = 99; + } + + act_align[0] = 0; // Offset 0 for motor0 + act_align[1] = 1; // Offset 1 for motor1 + + act_align[2] = 0xff; + act_align[3] = 0xff; + act_align[4] = 0xff; + act_align[5] = 0xff; + + // Asynchronous function + if (scePadSetActAlign(pad, 0, act_align)==0) break; + Phase++; + break; + + + case 81: + if ( scePadGetState(pad, 0) != scePadStateExecCmd ) + { + Phase = 99; + } + + break; + + default: + if ( state == scePadStateError ) break; + + if ( state == scePadStateStable || state == scePadStateFindCTP1 ) + { + if ( ShakeDur ) + { + ShakeDur = Max(ShakeDur - (int32)CTimer::GetTimeStepInMilliseconds(), 0); + + if ( ShakeDur == 0 ) + { + act_direct[0] = 0; + act_direct[1] = 0; + scePadSetActDirect(pad, 0, act_direct); + } + else + { + act_direct[0] = 0; + act_direct[1] = (unsigned char) ShakeFreq; + scePadSetActDirect(pad, 0, act_direct); + } + } + + if (scePadRead( pad, 0, rdata )==0) + { + NewState.Clear(); + break; + } + + if ((rdata[0] == 0)) + { + paddata = (unsigned short) ( 0xffff ^ ((rdata[2]<<8)|rdata[3]) ); + rterm_id = (rdata[1]); + + if ( (rterm_id>>4) == 7 ) // DUALSHOCK + { + if (!CRecordDataForGame::IsPlayingBack() && !CRecordDataForChase::ShouldThisPadBeLeftAlone(pad)) + { + tpad = paddata; + + NewState.DPadUp = ( tpad & SCE_PADLup ) ? 255 : 0; + NewState.DPadDown = ( tpad & SCE_PADLdown ) ? 255 : 0; + NewState.DPadLeft = ( tpad & SCE_PADLleft ) ? 255 : 0; + NewState.DPadRight = ( tpad & SCE_PADLright ) ? 255 : 0; + NewState.Triangle = ( tpad & SCE_PADRup ) ? 255 : 0; + NewState.Cross = ( tpad & SCE_PADRdown ) ? 255 : 0; + NewState.Square = ( tpad & SCE_PADRleft ) ? 255 : 0; + NewState.Circle = ( tpad & SCE_PADRright ) ? 255 : 0; + NewState.Start = ( tpad & SCE_PADstart ) ? 255 : 0; + NewState.Select = ( tpad & SCE_PADselect ) ? 255 : 0; + NewState.LeftShoulder1 = ( tpad & SCE_PADL1 ) ? 255 : 0; + NewState.LeftShoulder2 = ( tpad & SCE_PADL2 ) ? 255 : 0; + NewState.RightShoulder1 = ( tpad & SCE_PADR1 ) ? 255 : 0; + NewState.RightShoulder2 = ( tpad & SCE_PADR2 ) ? 255 : 0; + NewState.LeftShock = ( tpad & SCE_PADi ) ? 255 : 0; + NewState.RightShock = ( tpad & SCE_PADj ) ? 255 : 0; + NewState.RightStickX = (short)rdata[4]; + NewState.RightStickY = (short)rdata[5]; + NewState.LeftStickX = (short)rdata[6]; + NewState.LeftStickY = (short)rdata[7]; + + #define CLAMP_AXIS(x) (((x) < 43 && (x) >= -42) ? 0 : (((x) > 0) ? (Max((x)-42, 0)*127/85) : Min((x)+42, 0)*127/85)) + #define FIX_AXIS(x) CLAMP_AXIS((x)-128) + + NewState.RightStickX = FIX_AXIS(NewState.RightStickX); + NewState.RightStickY = FIX_AXIS(NewState.RightStickY); + NewState.LeftStickX = FIX_AXIS(NewState.LeftStickX); + NewState.LeftStickY = FIX_AXIS(NewState.LeftStickY); + + #undef FIX_AXIS + #undef CLAMP_AXIS + } + } + else if ( (rterm_id>>4) == 4 ) // Controller (digital) + { + if ( pad == 0 ) + bObsoleteControllerMessage = true; + NewState.Clear(); + } + + if ( NewState.IsAnyButtonPressed() ) + LastTimeTouched = CTimer::GetTimeInMilliseconds(); + + break; + } + + if ( ++iCurrHornHistory >= HORNHISTORY_SIZE ) + iCurrHornHistory = 0; + + bHornHistory[iCurrHornHistory] = GetHorn(); + NewState.Clear(); + return; + } + break; + } + + if ( pad == 0 ) + { + bOldDisplayNoControllerMessage = bDisplayNoControllerMessage; + if ( state == scePadStateDiscon ) + { + bDisplayNoControllerMessage = true; + Phase = 0; + } + else + bDisplayNoControllerMessage = false; + } + + if ( ++iCurrHornHistory >= HORNHISTORY_SIZE ) + iCurrHornHistory = 0; + + bHornHistory[iCurrHornHistory] = GetHorn(); + + if ( !bDisplayNoControllerMessage ) + CGame::bDemoMode = false; +#endif + +#if (defined GTA_PS2 || defined FIX_BUGS) + if (!CRecordDataForGame::IsPlayingBack() && !CRecordDataForChase::ShouldThisPadBeLeftAlone(pad)) +#endif + { +#ifdef RW_DC + if (((NewState.RightStickY > 64 && OldState.RightStickY > 64)) || ((NewState.RightStickY) < -64 && (OldState.RightStickY < -64))) + { + // if (contMaple == nullptr) + // CPad::GetPad(0)->IsDualAnalog = false; + // else + CPad::GetPad(0)->IsDualAnalog = true; + } + + //CPad::IsDualAnalog = cont_has_capabilities(contMaple, CONT_CAPABILITIES_DUAL_ANALOG); //Query controller about Dual analog capabilities + + if (pad == 0) + { + if (contMaple == NULL) + { + CPad::GetPad(0)->IsDualAnalog = false; + NewState.DPadUp = 0; + NewState.DPadDown = 0; + NewState.DPadLeft = 0; + NewState.DPadRight = 0; + NewState.A = 0; + NewState.B = 0; + NewState.C = 0; + NewState.D = 0; + NewState.X = 0; + NewState.Y = 0; + NewState.Z = 0; + NewState.Start = 0; + NewState.RightTrigger = 0; + NewState.LeftTrigger = 0; + NewState.LeftStickX = 0; + NewState.LeftStickY = 0; + NewState.RightStickX = 0; + NewState.RightStickY = 0; + NewState.RightShock = 0; + NewState.LeftShoulder1 = 0; + } + else + { + NewState.DPadUp = state->dpad_up; //This part could be inside a compiler directive to preserve the old code and just use this block if compil + NewState.DPadDown = state->dpad_down; //I also changed CControllerState inside Pad.h and created these values for DC controllers + NewState.DPadLeft = state->dpad_left; + NewState.DPadRight = state->dpad_right; + NewState.A = state->a; + NewState.B = state->b; + NewState.C = state->c; + NewState.D = state->d; + NewState.X = state->x; + NewState.Y = state->y; + NewState.Z = state->z; + NewState.Start = state->start; + NewState.RightTrigger = state->rtrig; + NewState.LeftTrigger = state->ltrig; + NewState.LeftStickX = state->joyx; + NewState.LeftStickY = state->joyy; + NewState.RightStickX = state->joy2x; + NewState.RightStickY = state->joy2y; + NewState.RightShock = state->dpad_left; + + NewState.LeftShoulder1 = (state->rtrig > 128 && state->ltrig > 128) ? 255 : 0; + } + + } + + else + { + NewState.DPadUp = 0; + NewState.DPadDown = 0; + NewState.DPadLeft = 0; + NewState.DPadRight = 0; + NewState.A = 0; + NewState.B = 0; + NewState.C = 0; + NewState.D = 0; + NewState.X = 0; + NewState.Y = 0; + NewState.Z = 0; + NewState.Start = 0; + NewState.RightTrigger = 0; + NewState.LeftTrigger = 0; + NewState.LeftStickX = 0; + NewState.LeftStickY = 0; + NewState.RightStickX = 0; + NewState.RightStickY = 0; + NewState.RightShock = 0; + NewState.LeftShoulder1 = 0; + } + + // if (old_contMaple == nullptr && contMaple != nullptr) + // { + // CPad::GetPad(0)->IsDualAnalog = false; + // NewState.DPadUp = 0; + // NewState.DPadDown = 0; + // NewState.DPadLeft = 0; + // NewState.DPadRight = 0; + // NewState.A = 0; + // NewState.B = 0; + // NewState.C = 0; + // NewState.D = 0; + // NewState.X = 0; + // NewState.Y = 0; + // NewState.Z = 0; + // NewState.Start = 0; + // NewState.RightTrigger = 0; + // NewState.LeftTrigger = 0; + // NewState.LeftStickX = 0; + // NewState.LeftStickY = 0; + // NewState.RightStickX = 0; + // NewState.RightStickY = 0; + // NewState.RightShock = 0; + // } + +#else + NewState = ReconcileTwoControllersInput(PCTempKeyState, PCTempJoyState); + NewState = ReconcileTwoControllersInput(PCTempMouseState, NewState); +#endif + } + + PCTempJoyState.Clear(); + PCTempKeyState.Clear(); + PCTempMouseState.Clear(); + + ProcessPCSpecificStuff(); + + if (NewState.CheckForInput()) + LastTimeTouched = CTimer::GetTimeInMilliseconds(); + + if ( ++iCurrHornHistory >= HORNHISTORY_SIZE ) + iCurrHornHistory = 0; + + bHornHistory[iCurrHornHistory] = GetHorn(); + + for (int32 i = DRUNK_STEERING_BUFFER_SIZE - 2; i >= 0; i--) { + SteeringLeftRightBuffer[i + 1] = SteeringLeftRightBuffer[i]; + } + + if ( !bDisplayNoControllerMessage ) + CGame::bDemoMode = false; + + if ( JustOutOfFrontend != 0 ) + --JustOutOfFrontend; + +#ifdef RW_DC + //auto old_contMaple = contMaple; + //auto n_dev = maple_enum_count(); + contMaple = maple_enum_type(0, MAPLE_FUNC_CONTROLLER); + state = (cont_state_t *)maple_dev_status(contMaple); +#endif +} + +void CPad::DoCheats(void) +{ +#ifdef DETECT_PAD_INPUT_SWITCH + if (IsAffectedByController) +#endif + GetPad(0)->DoCheats(0); +} + +void CPad::DoCheats(int16 unk) +{ +#ifdef GTA_PS2_STUFF + if ( GetTriangleJustDown() ) + AddToCheatString('T'); + + if ( GetCircleJustDown() ) + AddToCheatString('C'); + + if ( GetCrossJustDown() ) + AddToCheatString('X'); + + if ( GetSquareJustDown() ) + AddToCheatString('S'); + + if ( GetDPadUpJustDown() ) + AddToCheatString('U'); + + if ( GetDPadDownJustDown() ) + AddToCheatString('D'); + + if ( GetDPadLeftJustDown() ) + AddToCheatString('L'); + + if ( GetDPadRightJustDown() ) + AddToCheatString('R'); + + if ( GetLeftShoulder1JustDown() ) + AddToCheatString('1'); + + if ( GetLeftShoulder2JustDown() ) + AddToCheatString('2'); + + if ( GetRightShoulder1JustDown() ) + AddToCheatString('3'); + + if ( GetRightShoulder2JustDown() ) + AddToCheatString('4'); +#endif +} + +void CPad::StopPadsShaking(void) +{ + GetPad(0)->StopShaking(0); +} + +void CPad::StopShaking(int16 pad) +{ +#ifdef GTA_PS2_STUFF + ShakeFreq = 0; + ShakeDur = 0; + +#ifdef GTA_PS2 + if ( Phase == 99 ) + { + act_direct[0] = 0; + act_direct[1] = 0; + scePadSetActDirect(pad, 0, act_direct); + } +#endif + +#endif +} + +CPad *CPad::GetPad(int32 pad) +{ + return &Pads[pad]; +} +#ifdef DETECT_PAD_INPUT_SWITCH +#define CURMODE (IsAffectedByController ? Mode : 0) +#else +#define CURMODE (Mode) +#endif + +#ifdef RW_DC +#define DEADZONE 10 +#endif + +//The next are the actuall functions that are checked and produce the values that are used by engine to make the char run, the car turn, etc +//Although initially I didn't want to change them, I think here is the best place to create the custom desired mapping and behavior for the DC inputs +//The switch statement using CURMODE could be used in the future to define diferent control configurations, depending on the type of controller and desired mapping (e.g. Xbox like or PS2 like) +//While i think its possible, creating a system to configure custom mappings inside the game menus like in the PC game is out of my scope in the moment, I don't know if this is really necessary +//Also, the interface controls are not defined here, they are defined in Frontend.cpp unfortunately, using CControllerState values like here; Because of that, the behavior of the Start button and the A button for selecting menu itens are not here + +#ifdef RW_DC + +bool CPad::CameraSinglePress (void) +{ + if (CPad::CameraDoublePress() == false && NewState.X == true) + return true; + return 0; +} + +bool CPad::CameraDoublePress (void) +{ + if ( ArePlayerControlsDisabled() ) //Wont work driving, camera code isnt there + return false; + + if ((OldState.X == 1) && (NewState.X == 0) //Falling edge + && (CPad::GetPad(0)->CameraIsDoublePressed == false)) //Was not in double click state + { + //CPad::GetPad(0)->CameraJustUp = true; + CPad::GetPad(0)->CameraLastPressed = psTimer(); //Set timer to run + } + + if ((OldState.X == 1) && (NewState.X == 0) //Falling edge + && (CPad::GetPad(0)->CameraIsDoublePressed == true)) //Was in double click state + { + CPad::GetPad(0)->CameraIsDoublePressed = false; //ends double click state + return 0; + } + + + if ((OldState.X == 0) && (NewState.X == 1) //Rising edge + && ((psTimer() - CPad::GetPad(0)->CameraLastPressed) < 250)) //Checks timer on the Rising edge of X press + { + CPad::GetPad(0)->CameraIsDoublePressed = true; //Define that there was a double click + return true; + } + + + if ((OldState.X == 1) && (NewState.X == 1) //Button is keep pressed + && (CameraIsDoublePressed == true)) //The last state was double click + { + CPad::GetPad(0)->CameraIsDoublePressed = true; //Keep double click state + return true; + } + + return 0; +} +#endif + +int16 CPad::GetSteeringLeftRight(void) +{ + if ( ArePlayerControlsDisabled() ) + return 0; + int16 value = 0; +#ifdef RW_DC + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.LeftStickX; + } + else + { + if (NewState.X) + return 0; + if (NewState.A && ((NewState.LeftStickX < -64) || (NewState.LeftStickX > 64))) + return 0; + return NewState.LeftStickX; + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.LeftStickX; + } + else + { + if (NewState.LeftTrigger > 128) + return 0; + if (NewState.A && ((NewState.RightTrigger > 128) || (NewState.LeftTrigger > 128))) + return 0; + return NewState.LeftStickX; + } + } + +#else + switch (CURMODE) + { + case 0: + case 2: + { + int16 axis = NewState.LeftStickX; + int16 dpad = (NewState.DPadRight - NewState.DPadLeft) / 2; + + if ( Abs(axis) > Abs(dpad) ) + value = axis; + else + value = dpad; + + SteeringLeftRightBuffer[0] = value; + value = SteeringLeftRightBuffer[DrunkDrivingBufferUsed]; + + break; + } + + case 1: + case 3: + { + SteeringLeftRightBuffer[0] = NewState.LeftStickX; + value = SteeringLeftRightBuffer[DrunkDrivingBufferUsed]; + break; + } + default: + { + value = 0; + break; + } + } +#endif + + return value; +} + +int16 CPad::GetSteeringUpDown(void) +{ + if ( ArePlayerControlsDisabled() ) + return 0; + +#ifdef RW_DC + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.LeftStickY; + } + else + { + if (NewState.X) + return 0; + return NewState.LeftStickY; + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.LeftStickY; + } + else + { + if (NewState.LeftTrigger > 128) + return 0; + return NewState.LeftStickY; + } + } + +#else + switch (CURMODE) + { + case 0: + case 2: + { + int16 axis = NewState.LeftStickY; + int16 dpad = (NewState.DPadDown - NewState.DPadUp) / 2; + + if ( Abs(axis) > Abs(dpad) ) + return axis; + else + return dpad; + + break; + } + + case 1: + case 3: + { + return NewState.LeftStickY; + + break; + } + } +#endif + return 0; +} + +int16 CPad::GetCarGunUpDown(void) +{ + if ( ArePlayerControlsDisabled() ) + return 0; + +#ifdef RW_DC + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.RightStickY; + } + else + { + if (!NewState.X) + return 0; + return NewState.LeftStickY; + break; + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.RightStickY; + } + else + { + if (NewState.LeftTrigger > 128) + return NewState.LeftStickY; + return 0; + break; + } + + } +#else + + switch (CURMODE) + { + case 0: + case 1: + case 2: + { + return NewState.RightStickY; + + break; + } + + case 3: + { + return (NewState.DPadUp - NewState.DPadDown) / 2; + + break; + } + } +#endif + + return 0; +} + +int16 CPad::GetCarGunLeftRight(void) +{ + if ( ArePlayerControlsDisabled() ) + return 0; + +#ifdef RW_DC + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.RightStickX; + } + else + { + if (!NewState.X) + return 0; + return NewState.LeftStickX; + break; + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.RightStickX; + } + else + { + if (NewState.LeftTrigger > 128) + return NewState.LeftStickX; + return 0; + break; + } + + } +#else + + switch (CURMODE) + { + case 0: + case 1: + case 2: + { + return NewState.RightStickX; + + break; + } + + case 3: + { + return (NewState.DPadRight - NewState.DPadLeft) / 2; + + break; + } + } +#endif + + return 0; +} + +int16 CPad::GetPedWalkLeftRight(void) +{ + if ( ArePlayerControlsDisabled() ) + return 0; + +int16 axis = 0; + +#ifdef RW_DC + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + axis = NewState.LeftStickX; + } + else + { + if (CPad::CameraSinglePress()) + return 0; + axis = NewState.LeftStickX; + } + break; + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + axis = NewState.LeftStickX; + } + else + { + if (NewState.LeftTrigger > 128) + return 0; + axis = NewState.LeftStickX; + } + break; + } + + if (axis > DEADZONE || axis < -DEADZONE) + { + return axis; + } +#else + switch (CURMODE) + { + case 0: + case 2: + { + int16 axis = NewState.LeftStickX; + int16 dpad = (NewState.DPadRight - NewState.DPadLeft) / 2; + + if ( Abs(axis) > Abs(dpad) ) + return axis; + else + return dpad; + + break; + } + + case 1: + case 3: + { + return NewState.LeftStickX; + + break; + } + } +#endif + + return 0; +} + +int16 CPad::GetPedWalkUpDown(void) +{ + if ( ArePlayerControlsDisabled() ) + return 0; + +int16 axis = 0; + +#ifdef RW_DC + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + axis = NewState.LeftStickY; + } + else + { + if (CPad::CameraSinglePress()) + return 0; + axis = NewState.LeftStickY; + } + break; + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + axis = NewState.LeftStickY; + } + else + { + if (NewState.LeftTrigger > 128) + return 0; + axis = NewState.LeftStickY; + } + break; + } + + if (axis > DEADZONE || axis < -DEADZONE) + { + return axis; + } +#else + switch (CURMODE) + { + case 0: + case 2: + { + int16 axis = NewState.LeftStickY; + int16 dpad = (NewState.DPadDown - NewState.DPadUp) / 2; + + if ( Abs(axis) > Abs(dpad) ) + return axis; + else + return dpad; + + break; + } + + case 1: + case 3: + { + return NewState.LeftStickY; + + break; + } + } +#endif + + return 0; +} + +int16 CPad::GetAnalogueUpDown(void) +{ + switch (CURMODE) + { + case 0: +#ifdef RW_DC + case 2: + { + return NewState.LeftStickY; + + break; + } +#else + case 2: + { + int16 axis = NewState.LeftStickY; + int16 dpad = (NewState.DPadDown - NewState.DPadUp) / 2; + + if ( Abs(axis) > Abs(dpad) ) + return axis; + else + return dpad; + + break; + } +#endif + case 1: + case 3: + { + return NewState.LeftStickY; + + break; + } + } + + return 0; +} + +int16 CPad::GetAnalogueLeftRight(void) +{ + switch (CURMODE) + { + case 0: + case 2: + { + int16 axis = NewState.LeftStickX; + int16 dpad = (NewState.DPadRight - NewState.DPadLeft) / 2; + + if ( Abs(axis) > Abs(dpad) ) + return axis; + else + return dpad; + + break; + } + + case 1: + case 3: + { + return NewState.LeftStickX; + + break; + } + } + + return 0; +} + +bool CPad::GetLookLeft(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; + +#ifdef RW_DC + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + if (NewState.Z) + return true; + } + else + { + if (NewState.A && (NewState.LeftStickX < -64)) + return true; + } + break; + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + if (NewState.LeftTrigger > 128) + return true; + } + else + { + if (NewState.B && (NewState.LeftTrigger > 128)) + return true; + } + break; + } + return false; +#else + return !!(NewState.LeftShoulder2 && !NewState.RightShoulder2); +#endif +} + +bool CPad::GetLookRight(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; + +#ifdef RW_DC + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + if (NewState.C) + return true; + } + else + { + if (NewState.A && (NewState.LeftStickX > 64)) + return true; + } + break; + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + if (NewState.RightTrigger > 128) + return true; + } + else + { + if (NewState.B && (NewState.RightTrigger > 128)) + return true; + } + break; + } + return false; + #else + + return !!(NewState.RightShoulder2 && !NewState.LeftShoulder2); + #endif +} + + +bool CPad::GetLookBehindForCar(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; + +#ifdef RW_DC + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + if (NewState.C && NewState.Z) //Consider someway to make it work with RS or LS in dual analog + return true; + } + else + { + if (NewState.RightTrigger > 128 && NewState.LeftTrigger > 128) + return true; + } + break; + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + if (NewState.RightTrigger > 128 && NewState.LeftTrigger > 128) //Consider someway to make it work with RS or LS in dual analog + return true; + } + else + { + if (NewState.RightTrigger > 128 && NewState.LeftTrigger > 128) + return true; + } + break; + } + return false; +#else + + return !!(NewState.RightShoulder2 && NewState.LeftShoulder2); + +#endif +} + +bool CPad::GetLookBehindForPed(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; + +#ifdef RW_DC + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.DPadDown; //Consider someway to make it work with RS and LS + } + else + { + return NewState.DPadDown; + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.DPadDown; //Consider someway to make it work with RS and LS + } + else + { + return NewState.DPadDown; + } + } + return false; +#else + + return !!NewState.RightShock; +#endif +} + +bool CPad::GetHorn(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; + +#ifdef RW_DC + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.DPadDown; //Consider someway to make it work with RS and LS + } + else + { + return NewState.DPadDown; + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.DPadDown; //Consider someway to make it work with RS and LS + } + else + { + return NewState.DPadDown; + } + } +#else + switch (CURMODE) + { + case 0: + { + return !!NewState.LeftShock; + + break; + } + + case 1: + { + return !!NewState.LeftShoulder1; + + break; + } + + case 2: + { + return !!NewState.RightShoulder1; + + break; + } + + case 3: + { + return !!NewState.LeftShock; + + break; + } + } +#endif + + return false; +} + +bool CPad::HornJustDown(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; + + switch (CURMODE) + { + case 0: + { + return !!(NewState.LeftShock && !OldState.LeftShock); + + break; + } + + case 1: + { + return !!(NewState.LeftShoulder1 && !OldState.LeftShoulder1); + + break; + } + + case 2: + { + return !!(NewState.RightShoulder1 && !OldState.RightShoulder1); + + break; + } + + case 3: + { + return !!(NewState.LeftShock && !OldState.LeftShock); + + break; + } + } + + return false; +} + +bool CPad::GetCarGunFired(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; + +#ifdef RW_DC + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.A; + } + else + { + return NewState.A; + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.B; + } + else + { + return NewState.B; + } + } +#else + + switch (CURMODE) + { + case 0: + case 1: + case 2: + { + return !!NewState.Circle; + + break; + } + + case 3: + { + return !!NewState.RightShoulder1; + + break; + } + } +#endif + + return false; +} + +bool CPad::CarGunJustDown(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; + +#ifdef RW_DC + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.A; + } + else + { + return NewState.A; + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.B; + } + else + { + return NewState.B; + } + } + +#else + switch (CURMODE) + { + case 0: + case 1: + case 2: + { + return !!(NewState.Circle && !OldState.Circle); + + break; + } + + case 3: + { + return !!(NewState.RightShoulder1 && !OldState.RightShoulder1); + + break; + } + } +#endif + + return false; +} + +int16 CPad::GetHandBrake(void) +{ + if ( ArePlayerControlsDisabled() ) + return 0; + +#ifdef RW_DC + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.B; + } + else + { + return NewState.B; + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.C; + } + else + { + if (NewState.RightTrigger > 128) + return true; + } + } +#else + switch (CURMODE) + { + case 0: + case 1: + { + return NewState.RightShoulder1; + + break; + } + + case 2: + { + return NewState.Triangle; + + break; + } + + case 3: + { + return NewState.LeftShoulder1; + + break; + } + } +#endif + + return 0; +} + +int16 CPad::GetBrake(void) +{ + if ( ArePlayerControlsDisabled() ) + return 0; + +#ifdef RW_DC + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.LeftTrigger; + } + else + { + return NewState.LeftTrigger; + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + if(NewState.X) + return 255; + } + else + { + if(NewState.X) + return 255; + } + } +#else + switch (CURMODE) + { + case 0: + case 2: + { + return NewState.Square; + + break; + } + + case 1: + { + return NewState.Square; + + break; + } + + case 3: + { + int16 axis = 2 * NewState.RightStickY; + + if ( axis < 0 ) + return 0; + else + return axis; + + break; + } + } +#endif + + return 0; +} + +bool CPad::GetExitVehicle(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; + + + if ( JustOutOfFrontend != 0 ) + return false; + + switch (CURMODE) + { +#ifdef RW_DC + case 0: + case 1: + case 3: + { + return !!NewState.Y; + + break; + } + + case 2: + { + return !!NewState.Y; + + break; + } +#else + case 0: + case 1: + case 3: + { + return !!NewState.Triangle; + + break; + } + + case 2: + { + return !!NewState.LeftShoulder1; + + break; + } +#endif + } + + return false; +} + +bool CPad::ExitVehicleJustDown(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; + + if ( JustOutOfFrontend != 0 ) + return false; + + switch (CURMODE) + { + case 0: + case 1: +#ifdef RW_DC + case 3: + { + return !!(NewState.Y && !OldState.Y); + + break; + } + + case 2: + { + return !!(NewState.Y && !OldState.Y); + + break; + } +#else + case 3: + { + return !!(NewState.Triangle && !OldState.Triangle); + + break; + } + + case 2: + { + return !!(NewState.LeftShoulder1 && !OldState.LeftShoulder1); + + break; + } +#endif + } + + return false; +} + +int32 CPad::GetWeapon(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; + +#ifdef RW_DC + + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + if (NewState.RightTrigger > 128) + return true; + } + else + { + if (NewState.RightTrigger > 128) + return true; + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.B; + } + else + { + return NewState.B; + } + } + +#else + switch (CURMODE) + { + case 0: + case 1: + { + return NewState.Circle; + + break; + } + + case 2: + { + return NewState.Cross; + + break; + } + + case 3: + { + return NewState.RightShoulder1; + + break; + } + } + +#endif + + return false; +} + +bool CPad::WeaponJustDown(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; + +#ifdef RW_DC + + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + if (NewState.RightTrigger > 128 && !(OldState.RightTrigger > 128)) + return true; + } + else + { + if (NewState.RightTrigger > 128 && !(OldState.RightTrigger > 128)) + return true; + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.B && !OldState.B; + } + else + { + return NewState.B && !OldState.B; + } + } + +#else + switch (CURMODE) + { + case 0: + case 1: + { + return !!(NewState.Circle && !OldState.Circle); + + break; + } + + case 2: + { + return !!(NewState.Cross && !OldState.Cross); + + break; + } + + case 3: + { + return !!(NewState.RightShoulder1 && !OldState.RightShoulder1); + + break; + } + } +#endif + + return false; +} + +int16 CPad::GetAccelerate(void) +{ + if ( ArePlayerControlsDisabled() ) + return 0; + +#ifdef RW_DC + + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.RightTrigger; + } + else + { + return NewState.RightTrigger; + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + if(NewState.A) + return 255; + } + else + { + if(NewState.A) + return 255; + } + } + +#else + + switch (CURMODE) + { + case 0: + case 2: + { + return NewState.Cross; + + break; + } + + case 1: + { + return NewState.Cross; + + break; + } + + case 3: + { + int16 axis = -2 * NewState.RightStickY; + + if ( axis < 0 ) + return 0; + else + return axis; + + break; + } + } + +#endif + + return 0; +} + +bool CPad::CycleCameraModeJustDown(void) +{ + bool result; + switch (CURMODE) + { + case 0: + case 2: + case 3: + { + result = !!(NewState.Select && !OldState.Select); + + break; + } + + case 1: + { + result = !!(NewState.DPadUp && !OldState.DPadUp); + + break; + } + default: + { + result = false; + break; + } + } + + if (!result) + { + switch (CURMODE) + { + case 1: + { + result = !!(NewState.DPadDown && !OldState.DPadDown); + break; + } + default: + { + result = false; + break; + } + } + } + + return result; +} + +bool CPad::CycleCameraModeUpJustDown(void) +{ + +#ifdef RW_DC + + switch (CPad::GetPad(0)->Mode) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return !!(NewState.DPadUp && !OldState.DPadUp); + + } + else + { + return !!(NewState.DPadUp && !OldState.DPadUp); + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return !!(NewState.DPadUp && !OldState.DPadUp); + } + else + { + return !!(NewState.DPadUp && !OldState.DPadUp); + } + break; + } + return false; + +#else + switch (CURMODE) + { + case 0: + case 2: + case 3: + { + return !!(NewState.Select && !OldState.Select); + + break; + } + + case 1: + { + return !!(NewState.DPadUp && !OldState.DPadUp); + + break; + } + } +#endif + + return false; +} + +bool CPad::CycleCameraModeDownJustDown(void) +{ + switch (CURMODE) + { + case 0: + case 2: + case 3: + { + return false; + + break; + } + + case 1: + { + return !!(NewState.DPadDown && !OldState.DPadDown); + + break; + } + } + + return false; +} + +bool CPad::ChangeStationJustDown(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; +#ifdef RW_DC + + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return !!(NewState.DPadRight&& !OldState.DPadRight); + } + else + { + return !!(NewState.DPadRight && !OldState.DPadRight); + } + break; + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.Z; + } + else + { + return !!(NewState.DPadRight && !OldState.DPadRight); + } + break; + } + +#else + switch (CURMODE) + { + case 0: + { + return !!(NewState.LeftShoulder1 && !OldState.LeftShoulder1); + + break; + } + + case 1: + { + return !!(NewState.Select && !OldState.Select); + + break; + } + + case 2: + { + return !!(NewState.LeftShock && !OldState.LeftShock); + + break; + } + + case 3: + { + return !!(NewState.Circle && !OldState.Circle); + + break; + } + } +#endif + + return false; +} + +bool CPad::CycleWeaponLeftJustDown(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; +#ifdef RW_DC + return !!(NewState.DPadLeft && !OldState.DPadLeft); +#else + return !!(NewState.LeftShoulder2 && !OldState.LeftShoulder2); +#endif +} + +bool CPad::CycleWeaponRightJustDown(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; +#ifdef RW_DC + return !!(NewState.DPadRight && !OldState.DPadRight); +#else + return !!(NewState.RightShoulder2 && !OldState.RightShoulder2); +#endif +} + +bool CPad::GetTarget(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; + +#ifdef RW_DC + + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + if (NewState.LeftTrigger > 128) + return true; + } + else + { + if (NewState.LeftTrigger > 128) + return true; + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.C; + } + else + { + if (NewState.RightTrigger > 128) + return true; + } + } + +#else + + switch (CURMODE) + { + case 0: + case 1: + case 2: + { + return !!NewState.RightShoulder1; + + break; + } + + case 3: + { + return !!NewState.LeftShoulder1; + + break; + } + } + +#endif + return false; +} + +bool CPad::TargetJustDown(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; + +#ifdef RW_DC + + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + if (NewState.LeftTrigger > 128) + return true; + } + else + { + if (NewState.LeftTrigger > 128) + return true; + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return NewState.C; + } + else + { + if (NewState.RightTrigger > 128) + return true; + } + } + +#else + + switch (CURMODE) + { + case 0: + case 1: + case 2: + { + return !!(NewState.RightShoulder1 && !OldState.RightShoulder1); + + break; + } + + case 3: + { + return !!(NewState.LeftShoulder1 && !OldState.LeftShoulder1); + + break; + } + } +#endif + + return false; +} + +bool CPad::CollectPickupJustDown(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; + + switch (CURMODE) + { + case 0: + case 1: + { + return !!((NewState.DPadDown && NewState.A) && !(OldState.DPadDown && OldState.A)); + + break; + } + case 2: + { + return !!((NewState.DPadDown && NewState.A) && !(OldState.DPadDown && OldState.A)); + + break; + } + + case 3: + { + return !!((NewState.DPadDown && NewState.A) && !(OldState.DPadDown && OldState.A)); + + break; + } + } + + return false; +} + +bool CPad::DuckJustDown(void) +{ + if (ArePlayerControlsDisabled()) + return false; +#ifdef RW_DC + switch (CPad::GetPad(0)->Mode) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return !!(NewState.X&& !OldState.X); + } + else + { + return !!(NewState.X&& !OldState.X); + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return !!(NewState.X&& !OldState.X); + } + else + { + return !!(NewState.X&& !OldState.X); + } + } + + return false; +#else + return !!(NewState.LeftShock && !OldState.LeftShock); +#endif +} + +bool CPad::JumpJustDown(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; +#ifdef RW_DC + switch (CPad::GetPad(0)->Mode) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return !!(NewState.B && !OldState.B); + } + else + { + return !!(NewState.B && !OldState.B); + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return !!(NewState.X && !OldState.X); + } + else + { + return !!(NewState.X && !OldState.X); + } + } + + return false; +#else + return !!(NewState.Square && !OldState.Square); +#endif +} + +bool CPad::GetSprint(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; + +#ifdef RW_DC + + switch (CPad::GetPad(0)->Mode) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return !!NewState.A; + } + else + { + return !!NewState.A; + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return !!NewState.A; + } + else + { + return !!NewState.A; + } + } +#else + + switch (CURMODE) + { + case 0: + case 1: + case 3: + { + return !!NewState.Cross; + + break; + } + + case 2: + { + return !!NewState.Circle; + + break; + } + } +#endif + + return false; +} + +bool CPad::ShiftTargetLeftJustDown(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; +#ifdef RW_DC + + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return !!(NewState.DPadLeft && !OldState.DPadLeft); + } + else + { + return !!(NewState.DPadLeft && !OldState.DPadLeft); + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + if (NewState.LeftTrigger > 128) + return true; + } + else + { + return !!(NewState.DPadLeft && !OldState.DPadLeft); + } + } + return 0; +#else + + return !!(NewState.LeftShoulder2 && !OldState.LeftShoulder2); +#endif +} + +bool CPad::ShiftTargetRightJustDown(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; +#ifdef RW_DC + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + return !!(NewState.DPadRight && !OldState.DPadRight); + } + else + { + return !!(NewState.DPadRight && !OldState.DPadRight); + } + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + if (NewState.RightTrigger > 128) + return true; + } + else + { + return !!(NewState.DPadRight && !OldState.DPadRight); + } + } + return 0; +#else + return !!(NewState.LeftShoulder1 && !OldState.LeftShoulder1) || !!(NewState.RightShoulder2 && !OldState.RightShoulder2); +#endif +} + +bool CPad::GetAnaloguePadUp(void) +{ + static int16 oldfStickY = 0; + + int16 leftStickY = CPad::GetPad(0)->GetLeftStickY(); + + if ( leftStickY < -15 && oldfStickY >= -5 ) + { + oldfStickY = leftStickY; + return true; + } + else + { + oldfStickY = leftStickY; + return false; + } +} + +bool CPad::GetAnaloguePadDown(void) +{ + static int16 oldfStickY = 0; + + int16 leftStickY = CPad::GetPad(0)->GetLeftStickY(); + + if ( leftStickY > 15 && oldfStickY <= 5 ) + { + oldfStickY = leftStickY; + return true; + } + else + { + oldfStickY = leftStickY; + return false; + } +} + +bool CPad::GetAnaloguePadLeft(void) +{ + static int16 oldfStickX = 0; + + int16 leftStickX = CPad::GetPad(0)->GetLeftStickX(); + + if ( leftStickX < -15 && oldfStickX >= -5 ) + { + oldfStickX = leftStickX; + return true; + } + else + { + oldfStickX = leftStickX; + return false; + } +} + +bool CPad::GetAnaloguePadRight(void) +{ + static int16 oldfStickX = 0; + + int16 leftStickX = CPad::GetPad(0)->GetLeftStickX(); + + if ( leftStickX > 15 && oldfStickX <= 5 ) + { + oldfStickX = leftStickX; + return true; + } + else + { + oldfStickX = leftStickX; + return false; + } +} + +bool CPad::GetAnaloguePadLeftJustUp(void) +{ + static int16 oldfStickX = 0; + + int16 X = GetPad(0)->GetPedWalkLeftRight(); + + if ( X == 0 && oldfStickX < 0 ) + { + oldfStickX = 0; + + return true; + } + else + { + oldfStickX = X; + + return false; + } +} + +bool CPad::GetAnaloguePadRightJustUp(void) +{ + static int16 oldfStickX = 0; + + int16 X = GetPad(0)->GetPedWalkLeftRight(); + + if ( X == 0 && oldfStickX > 0 ) + { + oldfStickX = 0; + + return true; + } + else + { + oldfStickX = X; + + return false; + } +} + +bool CPad::ForceCameraBehindPlayer(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; + +#ifdef RW_DC + if (CPad::CameraDoublePress()) + return true; +#else + switch (CURMODE) + { + case 0: + case 1: + { + return !!NewState.LeftShoulder1; + + break; + } + + case 2: + { + return !!NewState.Triangle; + + break; + } + + case 3: + { + return !!NewState.Circle; + + break; + } + } +#endif + return false; +} + +bool CPad::SniperZoomIn(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; + + switch (CURMODE) + { + case 0: + case 1: +#ifdef RW_DC + case 3: + { + return !!NewState.X; + + break; + } + + case 2: + { + return !!NewState.X; + + break; + } +#else + case 3: + { + return !!NewState.Square; + + break; + } + + case 2: + { + return !!NewState.Triangle; + + break; + } +#endif + } + + return false; +} + +bool CPad::SniperZoomOut(void) +{ + if ( ArePlayerControlsDisabled() ) + return false; + + switch (CURMODE) + { + case 0: + case 1: +#ifdef RW_DC + case 3: + { + return !!NewState.A; + + break; + } + + case 2: + { + return !!NewState.A; + + break; + } +#else + case 3: + { + return !!NewState.Cross; + + break; + } + + case 2: + { + return !!NewState.Square; + + break; + } +#endif + } + + return false; +} + +int16 CPad::SniperModeLookLeftRight(void) +{ + int16 axis = NewState.LeftStickX; + int16 dpad = (NewState.DPadRight - NewState.DPadLeft) / 2; + + if ( Abs(axis) > Abs(dpad) ) { + if ( Abs(axis) > 35.0f ) { + return (axis > 0.f ? axis - 35.f : axis + 35.f) * (128.f / (128 - 35)); + } else { + return 0; + } + } else + return dpad; +} + +int16 CPad::SniperModeLookUpDown(void) +{ + int16 axis = NewState.LeftStickY; + int16 dpad; + +#ifdef FIX_BUGS + axis = -axis; +#endif + if (CPad::bInvertLook4Pad) { + axis = -axis; + dpad = (NewState.DPadDown - NewState.DPadUp) / 2; + } else { + dpad = (NewState.DPadUp - NewState.DPadDown) / 2; + } + + if ( Abs(axis) > Abs(dpad) ) { + if ( Abs(axis) > 35.0f ) { + return (axis > 0.f ? axis - 35.f : axis + 35.f) * (128.f / (128 - 35)); + } else { + return 0; + } + } else + return dpad; +} + +int16 CPad::LookAroundLeftRight(void) +{ +#ifdef RW_DC + float axis = 0; + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + axis = NewState.RightStickX; + } + else + { + if (!CPad::CameraSinglePress()) + return 0; + axis = NewState.LeftStickX; + } + break; + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + axis = NewState.RightStickX; + } + else + { + if (!(NewState.LeftTrigger > 128)) + return 0; + axis = NewState.LeftStickX; + } + break; + } + +#else + float axis = GetPad(0)->NewState.RightStickX; +#endif + + if ( Abs(axis) > 85 && !GetLookBehindForPed() ) + return (int16) ( (axis + ( ( axis > 0 ) ? -85 : 85) ) + * (127.0f / 32.0f) ); // 3.96875f + + else if ( TheCamera.Cams[0].Using3rdPersonMouseCam() && Abs(axis) > 10 ) + return (int16) ( (axis + ( ( axis > 0 ) ? -10 : 10) ) + * (127.0f / 64.0f) ); // 1.984375f + + return 0; +} + +int16 CPad::LookAroundUpDown(void) +{ +#ifdef RW_DC + int16 axis = 0; + + switch (CURMODE) + { + case 0: //Xbox Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + axis = NewState.RightStickY; + } + else + { + if (!CPad::CameraSinglePress()) + return 0; + axis = NewState.LeftStickY; + } + break; + case 1: //PS2 Mode + if (CPad::GetPad(0)->IsDualAnalog) + { + axis = NewState.RightStickY; + } + else + { + if (!(NewState.LeftTrigger > 128)) + return 0; + axis = NewState.LeftStickY; + } + break; + } + +#else + int16 axis = GetPad(0)->NewState.RightStickY; +#endif +#ifdef FIX_BUGS + axis = -axis; +#endif + if (CPad::bInvertLook4Pad) + axis = -axis; + + if ( Abs(axis) > 85 && !GetLookBehindForPed() ) + return (int16) ( (axis + ( ( axis > 0 ) ? -85 : 85) ) + * (127.0f / 32.0f) ); // 3.96875f + + else if ( TheCamera.Cams[0].Using3rdPersonMouseCam() && Abs(axis) > 40 ) + return (int16) ( (axis + ( ( axis > 0 ) ? -40 : 40) ) + * (127.0f / 64.0f) ); // 1.984375f + + return 0; +} +#undef CURMODE + +void CPad::ResetAverageWeapon(void) +{ + AverageWeapon = GetWeapon(); + AverageEntries = 1; +} + +void CPad::PrintErrorMessage(void) +{ + if (TheCamera.m_WideScreenOn) + return; + + if ( bDisplayNoControllerMessage && !CGame::playingIntro && !FrontEndMenuManager.m_bMenuActive ) + { + CSprite2d::DrawRect(CRect(SCREEN_STRETCH_X(20.0f), SCREEN_SCALE_FROM_BOTTOM(130.0f), SCREEN_STRETCH_FROM_RIGHT(20.0f), SCREEN_SCALE_Y(140.0f)), CRGBA(50, 50, 50, 210)); +#ifdef FIX_BUGS + CFont::SetScale(SCREEN_SCALE_X(0.85f), SCREEN_SCALE_Y(1.0f)); +#else + CFont::SetScale(0.85f, 1.0f); +#endif + CFont::SetJustifyOff(); + CFont::SetBackgroundOff(); +#ifdef FIX_BUGS + CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH - 20)); +#else + CFont::SetCentreSize(SCREEN_WIDTH - 20); +#endif + CFont::SetCentreOn(); + CFont::SetPropOn(); + CFont::SetColor(CRGBA(255, 255, 200, 200)); + CFont::SetFontStyle(FONT_STANDARD); + CFont::PrintString + ( + SCREEN_WIDTH / 2, + SCREEN_HEIGHT / 2 - SCREEN_SCALE_Y(40.0f), + TheText.Get("NOCONT") // Please reconnect an analog controller (DUALSHOCK@) or analog controller (DUALSHOCK@2). to controller port 1 to continue + ); + } + else if ( bObsoleteControllerMessage ) + { + CSprite2d::DrawRect(CRect(SCREEN_STRETCH_X(20.0f), SCREEN_SCALE_FROM_BOTTOM(130.0f), SCREEN_STRETCH_FROM_RIGHT(20.0f), SCREEN_SCALE_Y(140.0f)), CRGBA(50, 50, 50, 210)); +#ifdef FIX_BUGS + CFont::SetScale(SCREEN_SCALE_X(0.85f), SCREEN_SCALE_Y(1.0f)); +#else + CFont::SetScale(0.85f, 1.0f); +#endif + CFont::SetJustifyOff(); + CFont::SetBackgroundOff(); +#ifdef FIX_BUGS + CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH - 20)); +#else + CFont::SetCentreSize(SCREEN_WIDTH - 20); +#endif + CFont::SetCentreOn(); + CFont::SetPropOn(); + CFont::SetColor(CRGBA(255, 255, 200, 200)); + CFont::SetFontStyle(FONT_STANDARD); + CFont::PrintString + ( + SCREEN_WIDTH / 2, + SCREEN_HEIGHT / 2 - SCREEN_SCALE_Y(40.0f), + TheText.Get("WRCONT") // The controller connected to controller port 1 is an unsupported controller. Grand Theft Auto III requires an analog controller (DUALSHOCK@) or analog controller (DUALSHOCK@2). + ); + } + +} + +void LittleTest(void) +{ + static int32 Cunt = 0; + + Cunt++; // ??? +} + +void CPad::ResetCheats(void) +{ + CWeather::ReleaseWeather(); + + gbFastTime = false; + CPopulation::ms_bGivePedsWeapons = false; + CTimer::SetTimeScale(1.0f); + + CVehicle::bWheelsOnlyCheat = false; + CVehicle::bAllDodosCheat = false; + CVehicle::bCheat3 = false; + CVehicle::bCheat4 = false; + CVehicle::bCheat5 = false; + CVehicle::bAllTaxisHaveNitro = false; + CVehicle::bHoverCheat = false; + CVehicle::bCheat8 = false; + CVehicle::bCheat9 = false; + CVehicle::bCheat10 = false; +#ifdef RESTORE_ALLCARSHELI_CHEAT + bAllCarCheat = false; +#endif + gbBlackCars = false; + gbPinkCars = false; + + CCarCtrl::bMadDriversCheat = false; + CTrafficLights::bGreenLightsCheat = false; + CStats::ShowChaseStatOnScreen = 0; + CPed::bNastyLimbsCheat = false; + CPed::bFannyMagnetCheat = false; + CPed::bPedCheat3 = false; + +} + +char *CPad::EditString(char *pStr, int32 nSize) +{ + int32 pos = (int32)strlen(pStr); + + // letters + for ( int32 i = 0; i < ('Z' - 'A' + 1); i++ ) + { + if ( GetPad(0)->GetCharJustDown(i + 'A') && pos < nSize - 1 ) + { + pStr[pos++] = i + 'A'; + pStr[pos] = '\0'; + } + + if ( GetPad(0)->GetCharJustDown(i + 'a') && pos < nSize - 1 ) + { + pStr[pos++] = i + 'a'; + pStr[pos] = '\0'; + } + } + + // numbers + for ( int32 i = 0; i < ('9' - '0' + 1); i++ ) + { + if ( GetPad(0)->GetCharJustDown(i + '0') && pos < nSize - 1 ) + { + pStr[pos++] = i + '0'; + pStr[pos] = '\0'; + } + } + + // space + if ( GetPad(0)->GetCharJustDown(' ') && pos < nSize - 1 ) + { + pStr[pos++] = ' '; + pStr[pos] = '\0'; + } + + + // del + if ( GetPad(0)->GetDeleteJustDown() || GetPad(0)->GetBackspaceJustDown() ) + { + if ( pos > 0 ) + pStr[pos - 1] = '\0'; + } + + // extenter/up/down + if ( GetPad(0)->GetReturnJustDown() || GetPad(0)->GetUpJustDown() || GetPad(0)->GetDownJustDown() ) + return nil; + + return pStr; +} + +int32 *CPad::EditCodesForControls(int32 *pRsKeys, int32 nSize) +{ + *pRsKeys = rsNULL; + + for ( int32 i = 0; i < 255; i++ ) + { + if ( GetPad(0)->GetCharJustDown(i) ) + *pRsKeys = i; + } + + for ( int32 i = 0; i < 12; i++ ) + { + if ( GetPad(0)->GetFJustDown(i) ) + *pRsKeys = i + rsF1; + } + + if ( GetPad(0)->GetEscapeJustDown() ) + *pRsKeys = rsESC; + + if ( GetPad(0)->GetInsertJustDown() ) + *pRsKeys = rsINS; + + if ( GetPad(0)->GetDeleteJustDown() ) + *pRsKeys = rsDEL; + + if ( GetPad(0)->GetHomeJustDown() ) + *pRsKeys = rsHOME; + + if ( GetPad(0)->GetEndJustDown() ) + *pRsKeys = rsEND; + + if ( GetPad(0)->GetPageUpJustDown() ) + *pRsKeys = rsPGUP; + + if ( GetPad(0)->GetPageDownJustDown() ) + *pRsKeys = rsPGDN; + + if ( GetPad(0)->GetUpJustDown() ) + *pRsKeys = rsUP; + + if ( GetPad(0)->GetDownJustDown() ) + *pRsKeys = rsDOWN; + + if ( GetPad(0)->GetLeftJustDown() ) + *pRsKeys = rsLEFT; + + if ( GetPad(0)->GetRightJustDown() ) + *pRsKeys = rsRIGHT; + + if ( GetPad(0)->GetScrollLockJustDown() ) + *pRsKeys = rsSCROLL; + + if ( GetPad(0)->GetPauseJustDown() ) + *pRsKeys = rsPAUSE; + + if ( GetPad(0)->GetNumLockJustDown() ) + *pRsKeys = rsNUMLOCK; + + if ( GetPad(0)->GetDivideJustDown() ) + *pRsKeys = rsDIVIDE; + + if ( GetPad(0)->GetTimesJustDown() ) + *pRsKeys = rsTIMES; + + if ( GetPad(0)->GetMinusJustDown() ) + *pRsKeys = rsMINUS; + + if ( GetPad(0)->GetPlusJustDown() ) + *pRsKeys = rsPLUS; + + if ( GetPad(0)->GetPadEnterJustDown() ) + *pRsKeys = rsPADENTER; + + if ( GetPad(0)->GetPadDelJustDown() ) + *pRsKeys = rsPADDEL; + + if ( GetPad(0)->GetPad1JustDown() ) + *pRsKeys = rsPADEND; + + if ( GetPad(0)->GetPad2JustDown() ) + *pRsKeys = rsPADDOWN; + + if ( GetPad(0)->GetPad3JustDown() ) + *pRsKeys = rsPADPGDN; + + if ( GetPad(0)->GetPad4JustDown() ) + *pRsKeys = rsPADLEFT; + + if ( GetPad(0)->GetPad5JustDown() ) + *pRsKeys = rsPAD5; + + if ( GetPad(0)->GetPad6JustDown() ) + *pRsKeys = rsPADRIGHT; + + if ( GetPad(0)->GetPad7JustDown() ) + *pRsKeys = rsPADHOME; + + if ( GetPad(0)->GetPad8JustDown() ) + *pRsKeys = rsPADUP; + + if ( GetPad(0)->GetPad9JustDown() ) + *pRsKeys = rsPADPGUP; + + if ( GetPad(0)->GetPad0JustDown() ) + *pRsKeys = rsPADINS; + + if ( GetPad(0)->GetBackspaceJustDown() ) + *pRsKeys = rsBACKSP; + + if ( GetPad(0)->GetTabJustDown() ) + *pRsKeys = rsTAB; + + if ( GetPad(0)->GetCapsLockJustDown() ) + *pRsKeys = rsCAPSLK; + + if ( GetPad(0)->GetReturnJustDown() ) + *pRsKeys = rsENTER; + + if ( GetPad(0)->GetLeftShiftJustDown() ) + *pRsKeys = rsLSHIFT; + + if ( GetPad(0)->GetShiftJustDown() ) + *pRsKeys = rsSHIFT; + + if ( GetPad(0)->GetRightShiftJustDown() ) + *pRsKeys = rsRSHIFT; + + if ( GetPad(0)->GetLeftCtrlJustDown() ) + *pRsKeys = rsLCTRL; + + if ( GetPad(0)->GetRightCtrlJustDown() ) + *pRsKeys = rsRCTRL; + + if ( GetPad(0)->GetLeftAltJustDown() ) + *pRsKeys = rsLALT; + + if ( GetPad(0)->GetRightAltJustDown() ) + *pRsKeys = rsRALT; + + if ( GetPad(0)->GetLeftWinJustDown() ) + *pRsKeys = rsLWIN; + + if ( GetPad(0)->GetRightWinJustDown() ) + *pRsKeys = rsRWIN; + + if ( GetPad(0)->GetAppsJustDown() ) + *pRsKeys = rsAPPS; + + return pRsKeys; +} + +void CPad::FixPadsAfterSave(void) +{ + UpdatePads(); + if ( bObsoleteControllerMessage ) + { + bObsoleteControllerMessage = false; + GetPad(0)->Phase = 0; + } +} diff --git a/src/miami/core/Pad.h b/src/miami/core/Pad.h new file mode 100644 index 00000000..aa76eb1f --- /dev/null +++ b/src/miami/core/Pad.h @@ -0,0 +1,569 @@ +#pragma once + +enum { + PLAYERCONTROL_ENABLED = 0, + PLAYERCONTROL_CAMERA = 1, + PLAYERCONTROL_UNK2 = 2, + PLAYERCONTROL_GARAGE = 4, + PLAYERCONTROL_UNK8 = 8, + PLAYERCONTROL_UNK10 = 16, + PLAYERCONTROL_PLAYERINFO = 32, + PLAYERCONTROL_PHONE = 64, + PLAYERCONTROL_CUTSCENE = 128, + PLAYERCONTROL_SHORTCUT_TAXI = 256, +}; + +class CControllerState +{ +public: + int16 LeftStickX, LeftStickY; + int16 RightStickX, RightStickY; + int16 RightTrigger, LeftTrigger; + int16 LeftShoulder1, LeftShoulder2; + int16 RightShoulder1, RightShoulder2; + int16 DPadUp, DPadDown, DPadLeft, DPadRight; + int16 Start, Select; + int16 Square, Triangle, Cross, Circle; + uint32_t A, B, C, D, X, Y, Z; + int16 LeftShock, RightShock; + int16 NetworkTalk; + float GetLeftStickX(void) { return LeftStickX/32767.0f; }; + float GetLeftStickY(void) { return LeftStickY/32767.0f; }; + float GetRightStickX(void) { return RightStickX/32767.0f; }; + float GetRightStickY(void) { return RightStickY/32767.0f; }; + + bool CheckForInput(); + void Clear(void); +}; +VALIDATE_SIZE(CControllerState, 0x2A); + +class CMouseControllerState +{ +public: + //uint32 btns; // bit 0-2 button 1-3 + + bool LMB; + bool RMB; + bool MMB; + bool WHEELUP; + bool WHEELDN; + bool MXB1; + bool MXB2; + char _pad0; + + float x, y; + + CMouseControllerState(); + void Clear(); +}; + +VALIDATE_SIZE(CMouseControllerState, 0x10); + +class CMousePointerStateHelper +{ +public: + bool bInvertHorizontally; + bool bInvertVertically; + + CMouseControllerState GetMouseSetUp(); +}; + +VALIDATE_SIZE(CMousePointerStateHelper, 0x2); + +extern CMousePointerStateHelper MousePointerStateHelper; + + +class CKeyboardState +{ +public: + int16 F[12]; + int16 VK_KEYS[256]; + int16 ESC; + int16 INS; + int16 DEL; + int16 HOME; + int16 END; + int16 PGUP; + int16 PGDN; + int16 UP; + int16 DOWN; + int16 LEFT; + int16 RIGHT; + int16 SCROLLLOCK; + int16 PAUSE; + int16 NUMLOCK; + int16 DIV; + int16 MUL; + int16 SUB; + int16 ADD; + int16 ENTER; + int16 DECIMAL; + int16 NUM1; + int16 NUM2; + int16 NUM3; + int16 NUM4; + int16 NUM5; + int16 NUM6; + int16 NUM7; + int16 NUM8; + int16 NUM9; + int16 NUM0; + int16 BACKSP; + int16 TAB; + int16 CAPSLOCK; + int16 EXTENTER; + int16 LSHIFT; + int16 RSHIFT; + int16 SHIFT; + int16 LCTRL; + int16 RCTRL; + int16 LALT; + int16 RALT; + int16 LWIN; + int16 RWIN; + int16 APPS; + + void Clear(); +}; + +VALIDATE_SIZE(CKeyboardState, 0x270); + +enum +{ + // taken from miss2 + PAD1 = 0, + PAD2, + + MAX_PADS +}; + +class CPad +{ +public: + enum + { + HORNHISTORY_SIZE = 5, + DRUNK_STEERING_BUFFER_SIZE = 10, + }; + CControllerState NewState; + CControllerState OldState; + int16 SteeringLeftRightBuffer[DRUNK_STEERING_BUFFER_SIZE]; + int32 DrunkDrivingBufferUsed; + CControllerState PCTempKeyState; + CControllerState PCTempJoyState; + CControllerState PCTempMouseState; + // straight out of my IDB + int16 Phase; + int16 Mode; + int16 ShakeDur; + uint16 DisablePlayerControls; + uint8 ShakeFreq; + bool bHornHistory[HORNHISTORY_SIZE]; + uint8 iCurrHornHistory; + int8 JustOutOfFrontend; + int8 bApplyBrakes; + char CheatString[12]; + int32 LastTimeTouched; + int32 AverageWeapon; + int32 AverageEntries; + bool IsKeyboardMouse; + bool IsDualAnalog; + bool CameraJustDown; + bool CameraJustUp; + uint32 CameraJustUpTime; + uint32 CameraLastPressed; + bool CameraIsDoublePressed; + +#ifdef DETECT_PAD_INPUT_SWITCH + static bool IsAffectedByController; +#endif + CPad() { } + ~CPad() { } + + static bool bDisplayNoControllerMessage; + static bool bObsoleteControllerMessage; + static bool bOldDisplayNoControllerMessage; + static bool m_bMapPadOneToPadTwo; + static bool m_bDebugCamPCOn; + static bool bHasPlayerCheated; + static bool bInvertLook4Pad; + + static CKeyboardState OldKeyState; + static CKeyboardState NewKeyState; + static CKeyboardState TempKeyState; + static char KeyBoardCheatString[30]; + static CMouseControllerState OldMouseControllerState; + static CMouseControllerState NewMouseControllerState; + static CMouseControllerState PCTempMouseControllerState; + + +#ifdef GTA_PS2_STUFF + static void Initialise(void); +#endif + void Clear(bool bResetPlayerControls); + void ClearMouseHistory(); + void ClearKeyBoardHistory(); + void UpdateMouse(); + CControllerState ReconcileTwoControllersInput(CControllerState const &State1, CControllerState const &State2); + void StartShake(int16 nDur, uint8 nFreq); + void StartShake_Distance(int16 nDur, uint8 nFreq, float fX, float fY, float fz); + void StartShake_Train(float fX, float fY); +#ifdef GTA_PS2_STUFF + void AddToCheatString(char c); +#endif + void AddToPCCheatString(char c); + + static void UpdatePads(void); + void ProcessPCSpecificStuff(void); + void Update(int16 pad); + + static void DoCheats(void); + void DoCheats(int16 unk); + + static void StopPadsShaking(void); + void StopShaking(int16 pad); + + static CPad *GetPad(int32 pad); + +#ifdef RW_DC + bool CameraSinglePress(void); //Checks if camera modifier was double clicked + bool CameraDoublePress(void); +#endif + int16 GetSteeringLeftRight(void); + int16 GetSteeringUpDown(void); + int16 GetCarGunUpDown(void); + int16 GetCarGunLeftRight(void); + int16 GetPedWalkLeftRight(void); + int16 GetPedWalkUpDown(void); + int16 GetAnalogueUpDown(void); + int16 GetAnalogueLeftRight(void); + bool GetLookLeft(void); + bool GetLookRight(void); + bool GetLookBehindForCar(void); + bool GetLookBehindForPed(void); + bool GetHorn(void); + bool HornJustDown(void); + bool GetCarGunFired(void); + bool CarGunJustDown(void); + int16 GetHandBrake(void); + int16 GetBrake(void); + bool GetExitVehicle(void); + bool ExitVehicleJustDown(void); + int32 GetWeapon(void); + bool WeaponJustDown(void); + int16 GetAccelerate(void); + bool CycleCameraModeJustDown(void); + bool CycleCameraModeUpJustDown(void); + bool CycleCameraModeDownJustDown(void); + bool ChangeStationJustDown(void); + bool CycleWeaponLeftJustDown(void); + bool CycleWeaponRightJustDown(void); + bool GetTarget(void); + bool TargetJustDown(void); + bool DuckJustDown(void); + bool CollectPickupJustDown(void); + bool JumpJustDown(void); + bool GetSprint(void); + bool ShiftTargetLeftJustDown(void); + bool ShiftTargetRightJustDown(void); + bool GetAnaloguePadUp(void); + bool GetAnaloguePadDown(void); + bool GetAnaloguePadLeft(void); + bool GetAnaloguePadRight(void); + bool GetAnaloguePadLeftJustUp(void); + bool GetAnaloguePadRightJustUp(void); + bool ForceCameraBehindPlayer(void); + bool SniperZoomIn(void); + bool SniperZoomOut(void); + int16 SniperModeLookLeftRight(void); + int16 SniperModeLookUpDown(void); + int16 LookAroundLeftRight(void); + int16 LookAroundUpDown(void); + void ResetAverageWeapon(void); + static void FixPadsAfterSave(void); + static void PrintErrorMessage(void); + static void ResetCheats(void); + static char *EditString(char *pStr, int32 nSize); + static int32 *EditCodesForControls(int32 *pRsKeys, int32 nSize); + uint32 InputHowLongAgo(void); + void SetDrunkInputDelay(int32 delay) { DrunkDrivingBufferUsed = delay; } + +#ifdef XINPUT + static int XInputJoy1; + static int XInputJoy2; + void AffectFromXinput(uint32 pad); +#endif + + // mouse + bool GetLeftMouseJustDown() { return !!(NewMouseControllerState.LMB && !OldMouseControllerState.LMB); } + bool GetRightMouseJustDown() { return !!(NewMouseControllerState.RMB && !OldMouseControllerState.RMB); } + bool GetMiddleMouseJustDown() { return !!(NewMouseControllerState.MMB && !OldMouseControllerState.MMB); } + bool GetMouseWheelUpJustDown() { return !!(NewMouseControllerState.WHEELUP && !OldMouseControllerState.WHEELUP); } + bool GetMouseWheelDownJustDown() { return !!(NewMouseControllerState.WHEELDN && !OldMouseControllerState.WHEELDN);} + bool GetMouseX1JustDown() { return !!(NewMouseControllerState.MXB1 && !OldMouseControllerState.MXB1); } + bool GetMouseX2JustDown() { return !!(NewMouseControllerState.MXB2 && !OldMouseControllerState.MXB2); } + + bool GetLeftMouseJustUp() { return !!(!NewMouseControllerState.LMB && OldMouseControllerState.LMB); } + bool GetRightMouseJustUp() { return !!(!NewMouseControllerState.RMB && OldMouseControllerState.RMB); } + bool GetMiddleMouseJustUp() { return !!(!NewMouseControllerState.MMB && OldMouseControllerState.MMB); } + bool GetMouseWheelUpJustUp() { return !!(!NewMouseControllerState.WHEELUP && OldMouseControllerState.WHEELUP); } + bool GetMouseWheelDownJustUp() { return !!(!NewMouseControllerState.WHEELDN && OldMouseControllerState.WHEELDN); } + bool GetMouseX1JustUp() { return !!(!NewMouseControllerState.MXB1 && OldMouseControllerState.MXB1); } + bool GetMouseX2JustUp() { return !!(!NewMouseControllerState.MXB2 && OldMouseControllerState.MXB2); } + + bool GetLeftMouse() { return NewMouseControllerState.LMB; } + bool GetRightMouse() { return NewMouseControllerState.RMB; } + bool GetMiddleMouse() { return NewMouseControllerState.MMB; } + bool GetMouseWheelUp() { return NewMouseControllerState.WHEELUP; } + bool GetMouseWheelDown() { return NewMouseControllerState.WHEELDN; } + bool GetMouseX1() { return NewMouseControllerState.MXB1; } + bool GetMouseX2() { return NewMouseControllerState.MXB2; } + + bool GetLeftMouseUp() { return !OldMouseControllerState.LMB; } + bool GetRightMouseUp() { return !OldMouseControllerState.RMB; } + bool GetMiddleMouseUp() { return !OldMouseControllerState.MMB; } + bool GetMouseWheelUpUp() { return !OldMouseControllerState.WHEELUP; } + bool GetMouseWheelDownUp() { return !OldMouseControllerState.WHEELDN; } + bool GetMouseX1Up() { return !OldMouseControllerState.MXB1; } + bool GetMouseX2Up() { return !OldMouseControllerState.MXB2; } + + + float GetMouseX() { return NewMouseControllerState.x; } + float GetMouseY() { return NewMouseControllerState.y; } + + // keyboard + + bool GetCharJustDown(int32 c) { return !!(NewKeyState.VK_KEYS[c] && !OldKeyState.VK_KEYS[c]); } + bool GetFJustDown(int32 n) { return !!(NewKeyState.F[n] && !OldKeyState.F[n]); } + bool GetEscapeJustDown() { return !!(NewKeyState.ESC && !OldKeyState.ESC); } + bool GetInsertJustDown() { return !!(NewKeyState.INS && !OldKeyState.INS); } + bool GetDeleteJustDown() { return !!(NewKeyState.DEL && !OldKeyState.DEL); } + bool GetHomeJustDown() { return !!(NewKeyState.HOME && !OldKeyState.HOME); } + bool GetEndJustDown() { return !!(NewKeyState.END && !OldKeyState.END); } + bool GetPageUpJustDown() { return !!(NewKeyState.PGUP && !OldKeyState.PGUP); } + bool GetPageDownJustDown() { return !!(NewKeyState.PGDN && !OldKeyState.PGDN); } + bool GetUpJustDown() { return !!(NewKeyState.UP && !OldKeyState.UP); } + bool GetDownJustDown() { return !!(NewKeyState.DOWN && !OldKeyState.DOWN); } + bool GetLeftJustDown() { return !!(NewKeyState.LEFT && !OldKeyState.LEFT); } + bool GetRightJustDown() { return !!(NewKeyState.RIGHT && !OldKeyState.RIGHT); } + bool GetScrollLockJustDown() { return !!(NewKeyState.SCROLLLOCK && !OldKeyState.SCROLLLOCK); } + bool GetPauseJustDown() { return !!(NewKeyState.PAUSE && !OldKeyState.PAUSE); } + bool GetNumLockJustDown() { return !!(NewKeyState.NUMLOCK && !OldKeyState.NUMLOCK); } + bool GetDivideJustDown() { return !!(NewKeyState.DIV && !OldKeyState.DIV); } + bool GetTimesJustDown() { return !!(NewKeyState.MUL && !OldKeyState.MUL); } + bool GetMinusJustDown() { return !!(NewKeyState.SUB && !OldKeyState.SUB); } + bool GetPlusJustDown() { return !!(NewKeyState.ADD && !OldKeyState.ADD); } + bool GetPadEnterJustDown() { return !!(NewKeyState.ENTER && !OldKeyState.ENTER); } + bool GetPadDelJustDown() { return !!(NewKeyState.DECIMAL && !OldKeyState.DECIMAL); } + bool GetPad1JustDown() { return !!(NewKeyState.NUM1 && !OldKeyState.NUM1); } + bool GetPad2JustDown() { return !!(NewKeyState.NUM2 && !OldKeyState.NUM2); } + bool GetPad3JustDown() { return !!(NewKeyState.NUM3 && !OldKeyState.NUM3); } + bool GetPad4JustDown() { return !!(NewKeyState.NUM4 && !OldKeyState.NUM4); } + bool GetPad5JustDown() { return !!(NewKeyState.NUM5 && !OldKeyState.NUM5); } + bool GetPad6JustDown() { return !!(NewKeyState.NUM6 && !OldKeyState.NUM6); } + bool GetPad7JustDown() { return !!(NewKeyState.NUM7 && !OldKeyState.NUM7); } + bool GetPad8JustDown() { return !!(NewKeyState.NUM8 && !OldKeyState.NUM8); } + bool GetPad9JustDown() { return !!(NewKeyState.NUM9 && !OldKeyState.NUM9); } + bool GetPad0JustDown() { return !!(NewKeyState.NUM0 && !OldKeyState.NUM0); } + bool GetBackspaceJustDown() { return !!(NewKeyState.BACKSP && !OldKeyState.BACKSP); } + bool GetTabJustDown() { return !!(NewKeyState.TAB && !OldKeyState.TAB); } + bool GetCapsLockJustDown() { return !!(NewKeyState.CAPSLOCK && !OldKeyState.CAPSLOCK); } + bool GetReturnJustDown() { return !!(NewKeyState.EXTENTER && !OldKeyState.EXTENTER); } + bool GetLeftShiftJustDown() { return !!(NewKeyState.LSHIFT && !OldKeyState.LSHIFT); } + bool GetShiftJustDown() { return !!(NewKeyState.SHIFT && !OldKeyState.SHIFT); } + bool GetRightShiftJustDown() { return !!(NewKeyState.RSHIFT && !OldKeyState.RSHIFT); } + bool GetLeftCtrlJustDown() { return !!(NewKeyState.LCTRL && !OldKeyState.LCTRL); } + bool GetRightCtrlJustDown() { return !!(NewKeyState.RCTRL && !OldKeyState.RCTRL); } + bool GetLeftAltJustDown() { return !!(NewKeyState.LALT && !OldKeyState.LALT); } + bool GetRightAltJustDown() { return !!(NewKeyState.RALT && !OldKeyState.RALT); } + bool GetLeftWinJustDown() { return !!(NewKeyState.LWIN && !OldKeyState.LWIN); } + bool GetRightWinJustDown() { return !!(NewKeyState.RWIN && !OldKeyState.RWIN); } + bool GetAppsJustDown() { return !!(NewKeyState.APPS && !OldKeyState.APPS); } + bool GetEnterJustDown() { return GetPadEnterJustDown() || GetReturnJustDown(); } + bool GetAltJustDown() { return GetLeftAltJustDown() || GetRightAltJustDown(); } + + bool GetLeftJustUp() { return !!(!NewKeyState.LEFT && OldKeyState.LEFT); } + bool GetRightJustUp() { return !!(!NewKeyState.RIGHT && OldKeyState.RIGHT); } + bool GetEnterJustUp() { return GetPadEnterJustUp() || GetReturnJustUp(); } + bool GetReturnJustUp() { return !!(!NewKeyState.EXTENTER && OldKeyState.EXTENTER); } + bool GetPadEnterJustUp() { return !!(!NewKeyState.ENTER && OldKeyState.ENTER); } + + bool GetChar(int32 c) { return NewKeyState.VK_KEYS[c]; } + bool GetF(int32 n) { return NewKeyState.F[n]; } + bool GetEscape() { return NewKeyState.ESC; } + bool GetInsert() { return NewKeyState.INS; } + bool GetDelete() { return NewKeyState.DEL; } + bool GetHome() { return NewKeyState.HOME; } + bool GetEnd() { return NewKeyState.END; } + bool GetPageUp() { return NewKeyState.PGUP; } + bool GetPageDown() { return NewKeyState.PGDN; } + bool GetUp() { return NewKeyState.UP; } + bool GetDown() { return NewKeyState.DOWN; } + bool GetLeft() { return NewKeyState.LEFT; } + bool GetRight() { return NewKeyState.RIGHT; } + bool GetScrollLock() { return NewKeyState.SCROLLLOCK; } + bool GetPause() { return NewKeyState.PAUSE; } + bool GetNumLock() { return NewKeyState.NUMLOCK; } + bool GetDivide() { return NewKeyState.DIV; } + bool GetTimes() { return NewKeyState.MUL; } + bool GetMinus() { return NewKeyState.SUB; } + bool GetPlus() { return NewKeyState.ADD; } + bool GetPadEnter() { return NewKeyState.ENTER; } // GetEnterJustDown + bool GetPadDel() { return NewKeyState.DECIMAL; } + bool GetPad1() { return NewKeyState.NUM1; } + bool GetPad2() { return NewKeyState.NUM2; } + bool GetPad3() { return NewKeyState.NUM3; } + bool GetPad4() { return NewKeyState.NUM4; } + bool GetPad5() { return NewKeyState.NUM5; } + bool GetPad6() { return NewKeyState.NUM6; } + bool GetPad7() { return NewKeyState.NUM7; } + bool GetPad8() { return NewKeyState.NUM8; } + bool GetPad9() { return NewKeyState.NUM9; } + bool GetPad0() { return NewKeyState.NUM0; } + bool GetBackspace() { return NewKeyState.BACKSP; } + bool GetTab() { return NewKeyState.TAB; } + bool GetCapsLock() { return NewKeyState.CAPSLOCK; } + bool GetEnter() { return NewKeyState.EXTENTER; } + bool GetLeftShift() { return NewKeyState.LSHIFT; } + bool GetShift() { return NewKeyState.SHIFT; } + bool GetRightShift() { return NewKeyState.RSHIFT; } + bool GetLeftCtrl() { return NewKeyState.LCTRL; } + bool GetRightCtrl() { return NewKeyState.RCTRL; } + bool GetLeftAlt() { return NewKeyState.LALT; } + bool GetRightAlt() { return NewKeyState.RALT; } + bool GetLeftWin() { return NewKeyState.LWIN; } + bool GetRightWin() { return NewKeyState.RWIN; } + bool GetApps() { return NewKeyState.APPS; } + // pad + + +#ifdef RW_DC + bool GetTriangleJustDown() { return !!(NewState.Triangle && !OldState.Triangle); } + bool GetCircleJustDown() { return !!(NewState.Circle && !OldState.Circle); } + bool GetCrossJustDown() { return !!(NewState.A && !OldState.A); } //------------HACKY, CHANGE LATTER, GetCrossJustDown is used by Frontend.cpp to select items on the menus + bool GetSquareJustDown() { return !!(NewState.Square && !OldState.Square); } + bool GetLeftShoulder1JustDown() { return !!(NewState.LeftShoulder1 && !OldState.LeftShoulder1); } + bool GetLeftShoulder2JustDown() { return !!(NewState.LeftShoulder2 && !OldState.LeftShoulder2); } + bool GetRightShoulder1JustDown() { return !!(NewState.RightShoulder1 && !OldState.RightShoulder1); } + bool GetRightShoulder2JustDown() { return !!(NewState.RightShoulder2 && !OldState.RightShoulder2); } + bool GetLeftShockJustDown() { return !!(NewState.LeftShock && !OldState.LeftShock); } + bool GetRightShockJustDown() { return !!(NewState.RightShock && !OldState.RightShock); } + + bool GetTriangleJustUp() { return !!(!NewState.Triangle && OldState.Triangle); } + bool GetCircleJustUp() { return !!(!NewState.Circle && OldState.Circle); } + bool GetCrossJustUp() { return !!(!NewState.Cross && OldState.Cross); } + bool GetSquareJustUp() { return !!(!NewState.Square && OldState.Square); } + + bool GetTriangle() { return !!NewState.Triangle; } + bool GetCircle() { return !!NewState.Circle; } + bool GetCross() { return !!NewState.Cross; } + bool GetSquare() { return !!NewState.Square; } + bool GetLeftShoulder1(void) { return !!NewState.LeftShoulder1; } + bool GetLeftShoulder2(void) { return !!NewState.LeftShoulder2; } + bool GetRightShoulder1(void) { return !!NewState.RightShoulder1; } + bool GetRightShoulder2(void) { return !!NewState.RightShoulder2; } + + bool GetAJustDown() { return !!(NewState.A && !OldState.A); } + bool GetBJustDown() { return !!(NewState.B && !OldState.B); } + bool GetCJustDown() { return !!(NewState.C && !OldState.C); } + bool GetXJustDown() { return !!(NewState.X && !OldState.X); } + bool GetYJustDown() { return !!(NewState.Y && !OldState.Y); } + bool GetZJustDown() { return !!(NewState.Z && !OldState.Z); } + bool GetDPadUpJustDown() { return !!(NewState.DPadUp && !OldState.DPadUp); } + bool GetDPadDownJustDown() { return !!(NewState.DPadDown && !OldState.DPadDown); } + bool GetDPadLeftJustDown() { return !!(NewState.DPadLeft && !OldState.DPadLeft); } + bool GetDPadRightJustDown() { return !!(NewState.DPadRight && !OldState.DPadRight); } + bool GetStartJustDown() { return !!(NewState.Start && !OldState.Start); } + bool GetLeftStickXJustDown() { return !!(NewState.LeftStickX && !OldState.LeftStickX); } + bool GetLeftStickYJustDown() { return !!(NewState.LeftStickY && !OldState.LeftStickY); } + + bool GetAJustUp() { return !!(!NewState.A && OldState.A); } + bool GetBJustUp() { return !!(!NewState.B && OldState.B); } + bool GetCJustUp() { return !!(!NewState.C && OldState.C); } + bool GetXJustUp() { return !!(!NewState.X && OldState.X); } + bool GetYJustUp() { return !!(!NewState.Y && OldState.Y); } + bool GetZJustUp() { return !!(!NewState.Z && OldState.Z); } + bool GetDPadUpJustUp() { return !!(!NewState.DPadUp && OldState.DPadUp); } + bool GetDPadDownJustUp() { return !!(!NewState.DPadDown && OldState.DPadDown); } + bool GetDPadLeftJustUp() { return !!(!NewState.DPadLeft && OldState.DPadLeft); } + bool GetDPadRightJustUp() { return !!(!NewState.DPadRight && OldState.DPadRight); } + + bool GetA() { return !!NewState.A; } + bool GetB() { return !!NewState.B; } + bool GetC() { return !!NewState.C; } + bool GetX() { return !!NewState.X; } + bool GetY() { return !!NewState.Y; } + bool GetZ() { return !!NewState.Z; } + bool GetDPadUp() { return !!NewState.DPadUp; } + bool GetDPadDown() { return !!NewState.DPadDown; } + bool GetDPadLeft() { return !!NewState.DPadLeft; } + bool GetDPadRight() { return !!NewState.DPadRight; } + bool GetStart() { return !!NewState.Start; } + int16 GetLeftStickX(void) { return NewState.LeftStickX; } + int16 GetLeftStickY(void) { return NewState.LeftStickY; } + int16 GetRightStickX(void) { return NewState.RightStickX; } + int16 GetRightStickY(void) { return NewState.RightStickY; } + +#else + + bool GetTriangleJustDown() { return !!(NewState.Triangle && !OldState.Triangle); } + bool GetCircleJustDown() { return !!(NewState.Circle && !OldState.Circle); } + bool GetCrossJustDown() { return !!(NewState.Cross && !OldState.Cross); } + bool GetSquareJustDown() { return !!(NewState.Square && !OldState.Square); } + bool GetDPadUpJustDown() { return !!(NewState.DPadUp && !OldState.DPadUp); } + bool GetDPadDownJustDown() { return !!(NewState.DPadDown && !OldState.DPadDown); } + bool GetDPadLeftJustDown() { return !!(NewState.DPadLeft && !OldState.DPadLeft); } + bool GetDPadRightJustDown() { return !!(NewState.DPadRight && !OldState.DPadRight); } + bool GetLeftShoulder1JustDown() { return !!(NewState.LeftShoulder1 && !OldState.LeftShoulder1); } + bool GetLeftShoulder2JustDown() { return !!(NewState.LeftShoulder2 && !OldState.LeftShoulder2); } + bool GetRightShoulder1JustDown() { return !!(NewState.RightShoulder1 && !OldState.RightShoulder1); } + bool GetRightShoulder2JustDown() { return !!(NewState.RightShoulder2 && !OldState.RightShoulder2); } + bool GetLeftShockJustDown() { return !!(NewState.LeftShock && !OldState.LeftShock); } + bool GetRightShockJustDown() { return !!(NewState.RightShock && !OldState.RightShock); } + bool GetStartJustDown() { return !!(NewState.Start && !OldState.Start); } + bool GetSelectJustDown() { return !!(NewState.Select && !OldState.Select); } + bool GetLeftStickXJustDown() { return !!(NewState.LeftStickX && !OldState.LeftStickX); } + bool GetLeftStickYJustDown() { return !!(NewState.LeftStickY && !OldState.LeftStickY); } + + bool GetTriangleJustUp() { return !!(!NewState.Triangle && OldState.Triangle); } + bool GetCircleJustUp() { return !!(!NewState.Circle && OldState.Circle); } + bool GetCrossJustUp() { return !!(!NewState.Cross && OldState.Cross); } + bool GetSquareJustUp() { return !!(!NewState.Square && OldState.Square); } + bool GetDPadUpJustUp() { return !!(!NewState.DPadUp && OldState.DPadUp); } + bool GetDPadDownJustUp() { return !!(!NewState.DPadDown && OldState.DPadDown); } + bool GetDPadLeftJustUp() { return !!(!NewState.DPadLeft && OldState.DPadLeft); } + bool GetDPadRightJustUp() { return !!(!NewState.DPadRight && OldState.DPadRight); } + + bool GetTriangle() { return !!NewState.Triangle; } + bool GetCircle() { return !!NewState.Circle; } + bool GetCross() { return !!NewState.Cross; } + bool GetSquare() { return !!NewState.Square; } + bool GetDPadUp() { return !!NewState.DPadUp; } + bool GetDPadDown() { return !!NewState.DPadDown; } + bool GetDPadLeft() { return !!NewState.DPadLeft; } + bool GetDPadRight() { return !!NewState.DPadRight; } + bool GetLeftShoulder1(void) { return !!NewState.LeftShoulder1; } + bool GetLeftShoulder2(void) { return !!NewState.LeftShoulder2; } + bool GetRightShoulder1(void) { return !!NewState.RightShoulder1; } + bool GetRightShoulder2(void) { return !!NewState.RightShoulder2; } + bool GetStart() { return !!NewState.Start; } + bool GetSelect() { return !!NewState.Select; } + int16 GetLeftStickX(void) { return NewState.LeftStickX; } + int16 GetLeftStickY(void) { return NewState.LeftStickY; } + int16 GetRightStickX(void) { return NewState.RightStickX; } + int16 GetRightStickY(void) { return NewState.RightStickY; } +#endif + + bool ArePlayerControlsDisabled(void) { return DisablePlayerControls != PLAYERCONTROL_ENABLED; } + void SetDisablePlayerControls(uint16 who) { DisablePlayerControls |= who; } + void SetEnablePlayerControls(uint16 who) { DisablePlayerControls &= ~who; } + bool IsPlayerControlsDisabledBy(uint16 who) { return DisablePlayerControls & who; } + + int16 GetMode() { return Mode; } + void SetMode(int16 mode) { Mode = mode; } + + static bool IsNoOrObsolete() { return bDisplayNoControllerMessage || bObsoleteControllerMessage; } +}; + +VALIDATE_SIZE(CPad, 0xFC); +extern CPad Pads[MAX_PADS]; diff --git a/src/miami/core/Placeable.cpp b/src/miami/core/Placeable.cpp new file mode 100644 index 00000000..6efc1540 --- /dev/null +++ b/src/miami/core/Placeable.cpp @@ -0,0 +1,62 @@ +#include "common.h" +#include "Placeable.h" + + +CPlaceable::CPlaceable(void) +{ + m_matrix.SetScale(1.0f); +} + +void +CPlaceable::SetHeading(float angle) +{ + CVector pos = GetMatrix().GetPosition(); + m_matrix.SetRotateZ(angle); + GetMatrix().Translate(pos); +} + +bool +CPlaceable::IsWithinArea(float x1, float y1, float x2, float y2) +{ + float tmp; + + if(x1 > x2){ + tmp = x1; + x1 = x2; + x2 = tmp; + } + if(y1 > y2){ + tmp = y1; + y1 = y2; + y2 = tmp; + } + + return x1 <= GetPosition().x && GetPosition().x <= x2 && + y1 <= GetPosition().y && GetPosition().y <= y2; +} + +bool +CPlaceable::IsWithinArea(float x1, float y1, float z1, float x2, float y2, float z2) +{ + float tmp; + + if(x1 > x2){ + tmp = x1; + x1 = x2; + x2 = tmp; + } + if(y1 > y2){ + tmp = y1; + y1 = y2; + y2 = tmp; + } + if(z1 > z2){ + tmp = z1; + z1 = z2; + z2 = tmp; + } + + return x1 <= GetPosition().x && GetPosition().x <= x2 && + y1 <= GetPosition().y && GetPosition().y <= y2 && + z1 <= GetPosition().z && GetPosition().z <= z2; +} diff --git a/src/miami/core/Placeable.h b/src/miami/core/Placeable.h new file mode 100644 index 00000000..94be3211 --- /dev/null +++ b/src/miami/core/Placeable.h @@ -0,0 +1,36 @@ +#pragma once + +class CPlaceable +{ +protected: + CMatrix m_matrix; + +public: + // disable allocation + static void *operator new(size_t); + + CPlaceable(void); + const CVector &GetPosition(void) { return m_matrix.GetPosition(); } + void SetPosition(float x, float y, float z) { + m_matrix.GetPosition().x = x; + m_matrix.GetPosition().y = y; + m_matrix.GetPosition().z = z; + } + void SetPosition(const CVector &pos) { m_matrix.GetPosition() = pos; } + CVector &GetRight(void) { return m_matrix.GetRight(); } + CVector &GetForward(void) { return m_matrix.GetForward(); } + CVector &GetUp(void) { return m_matrix.GetUp(); } + CMatrix &GetMatrix(void) { return m_matrix; } + void SetMatrix(CMatrix &newMatrix) { m_matrix = newMatrix; } + void SetTransform(RwMatrix *m) { m_matrix = CMatrix(m, false); } + void SetHeading(float angle); + void SetOrientation(float x, float y, float z){ + CVector pos = m_matrix.GetPosition(); + m_matrix.SetRotate(x, y, z); + m_matrix.Translate(pos); + } + bool IsWithinArea(float x1, float y1, float x2, float y2); + bool IsWithinArea(float x1, float y1, float z1, float x2, float y2, float z2); +}; + +VALIDATE_SIZE(CPlaceable, 0x48); diff --git a/src/miami/core/PlayerInfo.cpp b/src/miami/core/PlayerInfo.cpp new file mode 100644 index 00000000..36d05b82 --- /dev/null +++ b/src/miami/core/PlayerInfo.cpp @@ -0,0 +1,880 @@ +#include "common.h" + +#include "Automobile.h" +#include "Bridge.h" +#include "Camera.h" +#include "CarCtrl.h" +#include "Cranes.h" +#include "Darkel.h" +#include "Explosion.h" +#include "Fire.h" +#include "Frontend.h" +#include "General.h" +#include "HandlingMgr.h" +#include "Messages.h" +#include "Pad.h" +#include "PathFind.h" +#include "PlayerInfo.h" +#include "PlayerPed.h" +#include "PlayerSkin.h" +#include "ProjectileInfo.h" +#include "Remote.h" +#include "Renderer.h" +#include "Replay.h" +#include "Script.h" +#include "SpecialFX.h" +#include "Stats.h" +#include "Streaming.h" +#include "Text.h" +#include "Wanted.h" +#include "WaterLevel.h" +#include "World.h" +#include "ZoneCull.h" +#include "main.h" +#include "Bike.h" +#include "Automobile.h" +#include "GameLogic.h" + +CVector lastPlayerPos; + +void +CPlayerInfo::Clear(void) +{ + m_pPed = nil; + m_pRemoteVehicle = nil; + if (m_pVehicleEx) { + m_pVehicleEx->bUsingSpecialColModel = false; + m_pVehicleEx = nil; + } + m_nVisibleMoney = 0; + m_nMoney = m_nVisibleMoney; + m_WBState = WBSTATE_PLAYING; + m_nWBTime = 0; + m_nTrafficMultiplier = 0; + m_fRoadDensity = 1.0f; + m_bInRemoteMode = false; + field_D5 = false; + field_D6 = false; + m_bUnusedTaxiThing = false; + m_nUnusedTaxiTimer = 0; + m_nCollectedPackages = 0; + m_nTotalPackages = 3; + m_nTimeLastHealthLoss = 0; + m_nTimeLastArmourLoss = 0; + m_nNextSexFrequencyUpdateTime = 0; + m_nNextSexMoneyUpdateTime = 0; + m_nSexFrequency = 0; + m_pHooker = nil; + m_nTimeTankShotGun = 0; + field_EC = 0; + m_nUpsideDownCounter = 0; + m_nTimeCarSpentOnTwoWheels = 0; + m_nDistanceCarTravelledOnTwoWheels = 0; + m_nTimeNotFullyOnGround = 0; + m_nTimeSpentOnWheelie = 0; + m_nDistanceTravelledOnWheelie = 0.0f; + m_nTimeSpentOnStoppie = 0; + m_nDistanceTravelledOnStoppie = 0.0f; + m_nCancelWheelStuntTimer = 0; + m_nLastTimeCarSpentOnTwoWheels = 0; + m_nLastDistanceCarTravelledOnTwoWheels = 0; + m_nLastTimeSpentOnWheelie = 0; + m_nLastDistanceTravelledOnWheelie = 0; + m_nLastTimeSpentOnStoppie = 0; + m_nLastDistanceTravelledOnStoppie = 0; + m_bInfiniteSprint = false; + m_bFastReload = false; + m_bFireproof = false; + m_nMaxHealth = m_nMaxArmour = 100; + m_bGetOutOfJailFree = false; + m_bGetOutOfHospitalFree = false; + m_bDriveByAllowed = true; + m_nPreviousTimeRewardedForExplosion = 0; + m_nExplosionsSinceLastReward = 0; + m_nHavocLevel = 0; + m_fMediaAttention = 0.0f; + m_nCurrentBustedAudio = 1; + m_nBustedAudioStatus = BUSTEDAUDIO_NONE; +} + +void +CPlayerInfo::Process(void) +{ +#ifdef FIX_BUGS + if (CReplay::IsPlayingBack()) + return; +#endif + // Unused taxi feature. Gives you a dollar for every second with a passenger. Can be toggled via 0x29A opcode. + bool startTaxiTimer = true; + if (m_bUnusedTaxiThing && m_pPed->bInVehicle) { + CVehicle *veh = m_pPed->m_pMyVehicle; + if (veh->IsTaxi() + && veh->pDriver == m_pPed && veh->m_nNumPassengers != 0) { + for (uint32 timePassed = CTimer::GetTimeInMilliseconds() - m_nUnusedTaxiTimer; timePassed >= 1000; m_nUnusedTaxiTimer += 1000) { + timePassed -= 1000; + ++m_nMoney; + } + startTaxiTimer = false; + } + } + if (startTaxiTimer) + m_nUnusedTaxiTimer = CTimer::GetTimeInMilliseconds(); + + if (!m_pPed->InVehicle()) { + m_nTimeCarSpentOnTwoWheels = 0; + m_nTimeNotFullyOnGround = 0; + m_nTimeSpentOnWheelie = 0; + m_nTimeSpentOnStoppie = 0; + m_nCancelWheelStuntTimer = 0; + } else if (m_pPed->m_pMyVehicle->IsCar()) { + CAutomobile *car = (CAutomobile*)m_pPed->m_pMyVehicle; + + if (car->m_nWheelsOnGround < 3) + m_nTimeNotFullyOnGround += CTimer::GetTimeStepInMilliseconds(); + else + m_nTimeNotFullyOnGround = 0; + + if (car->m_aSuspensionSpringRatioPrev[2] == 1.f && car->m_aSuspensionSpringRatioPrev[3] == 1.f) { + if (car->m_aSuspensionSpringRatioPrev[0] < 1.0f && car->m_aSuspensionSpringRatioPrev[1] < 1.0f && car->m_fDamageImpulse == 0.0f) { + m_nTimeCarSpentOnTwoWheels += CTimer::GetTimeStepInMilliseconds(); + m_nDistanceCarTravelledOnTwoWheels += car->m_fDistanceTravelled; + m_nCancelWheelStuntTimer = Max(0.0f, m_nCancelWheelStuntTimer - CTimer::GetTimeStepInMilliseconds() * 0.5f); + + } else { + if (m_nTimeCarSpentOnTwoWheels != 0 && m_nCancelWheelStuntTimer < 500) { + m_nCancelWheelStuntTimer += CTimer::GetTimeStepInMilliseconds(); + } else { + if (m_nTimeCarSpentOnTwoWheels >= 2000) { + m_nLastTimeCarSpentOnTwoWheels = m_nTimeCarSpentOnTwoWheels; + m_nLastDistanceCarTravelledOnTwoWheels = m_nDistanceCarTravelledOnTwoWheels; + if (CStats::Longest2Wheel < m_nTimeCarSpentOnTwoWheels / 1000) + CStats::Longest2Wheel = m_nTimeCarSpentOnTwoWheels / 1000; + if (CStats::Longest2WheelDist < m_nDistanceCarTravelledOnTwoWheels) + CStats::Longest2WheelDist = m_nDistanceCarTravelledOnTwoWheels; + } + m_nTimeCarSpentOnTwoWheels = 0; + m_nDistanceCarTravelledOnTwoWheels = 0; + m_nCancelWheelStuntTimer = 0; + } + } + } else if (car->m_aSuspensionSpringRatioPrev[0] == 1.0f && car->m_aSuspensionSpringRatioPrev[1] == 1.0f) { +#ifdef FIX_BUGS + if (car->m_aSuspensionSpringRatioPrev[2] < 1.f +#else + if (car->m_aSuspensionSpringRatioPrev[1] < 1.f +#endif + && car->m_aSuspensionSpringRatioPrev[3] < 1.f && 0.0f == car->m_fDamageImpulse) { + m_nTimeCarSpentOnTwoWheels += CTimer::GetTimeStepInMilliseconds(); + m_nDistanceCarTravelledOnTwoWheels += car->m_fDistanceTravelled; + m_nCancelWheelStuntTimer = Max(0.0f, m_nCancelWheelStuntTimer - CTimer::GetTimeStepInMilliseconds() * 0.2f); + + } else if (m_nTimeCarSpentOnTwoWheels != 0 && m_nCancelWheelStuntTimer < 500) { + m_nCancelWheelStuntTimer += CTimer::GetTimeStepInMilliseconds(); + + } else { + if (m_nTimeCarSpentOnTwoWheels >= 2000) { + m_nLastTimeCarSpentOnTwoWheels = m_nTimeCarSpentOnTwoWheels; + m_nLastDistanceCarTravelledOnTwoWheels = m_nDistanceCarTravelledOnTwoWheels; + if (CStats::Longest2Wheel < m_nTimeCarSpentOnTwoWheels / 1000) + CStats::Longest2Wheel = m_nTimeCarSpentOnTwoWheels / 1000; + if (CStats::Longest2WheelDist < m_nDistanceCarTravelledOnTwoWheels) + CStats::Longest2WheelDist = m_nDistanceCarTravelledOnTwoWheels; + } + m_nTimeCarSpentOnTwoWheels = 0; + m_nDistanceCarTravelledOnTwoWheels = 0; + m_nCancelWheelStuntTimer = 0; + } + } else if (m_nTimeCarSpentOnTwoWheels != 0) { + if (m_nTimeCarSpentOnTwoWheels >= 2000) { + m_nLastTimeCarSpentOnTwoWheels = m_nTimeCarSpentOnTwoWheels; + m_nLastDistanceCarTravelledOnTwoWheels = m_nDistanceCarTravelledOnTwoWheels; + if (CStats::Longest2Wheel < m_nTimeCarSpentOnTwoWheels / 1000) + CStats::Longest2Wheel = m_nTimeCarSpentOnTwoWheels / 1000; + if (CStats::Longest2WheelDist < m_nDistanceCarTravelledOnTwoWheels) + CStats::Longest2WheelDist = m_nDistanceCarTravelledOnTwoWheels; + } + m_nTimeCarSpentOnTwoWheels = 0; + m_nDistanceCarTravelledOnTwoWheels = 0; + m_nCancelWheelStuntTimer = 0; + } + m_nTimeSpentOnWheelie = 0; + m_nTimeSpentOnStoppie = 0; + } else if (m_pPed->m_pMyVehicle->IsBike()) { + CBike *bike = (CBike*)m_pPed->m_pMyVehicle; + if (bike->m_aSuspensionSpringRatioPrev[0] == 1.0f && bike->m_aSuspensionSpringRatioPrev[1] == 1.0f) { + if (bike->m_aSuspensionSpringRatioPrev[2] < 1.0f + || (bike->m_aSuspensionSpringRatioPrev[3] < 1.0f && 0.0f == bike->m_fDamageImpulse)) { + m_nTimeSpentOnWheelie += CTimer::GetTimeStepInMilliseconds(); + m_nDistanceTravelledOnWheelie += bike->m_fDistanceTravelled; + m_nCancelWheelStuntTimer = Max(0.0f, m_nCancelWheelStuntTimer - CTimer::GetTimeStepInMilliseconds() * 0.2f); + + } else { + if (m_nTimeSpentOnWheelie != 0 && m_nCancelWheelStuntTimer < 500) { + m_nCancelWheelStuntTimer += CTimer::GetTimeStepInMilliseconds(); + } else { + if (m_nTimeSpentOnWheelie >= 5000) { + m_nLastTimeSpentOnWheelie = m_nTimeSpentOnWheelie; + m_nLastDistanceTravelledOnWheelie = m_nDistanceTravelledOnWheelie; + if (CStats::LongestWheelie < m_nTimeSpentOnWheelie / 1000) + CStats::LongestWheelie = m_nTimeSpentOnWheelie / 1000; + if (CStats::LongestWheelieDist < m_nDistanceTravelledOnWheelie) + CStats::LongestWheelieDist = m_nDistanceTravelledOnWheelie; + } + m_nTimeSpentOnWheelie = 0; + m_nDistanceTravelledOnWheelie = 0; + m_nCancelWheelStuntTimer = 0; + } + } + } else if (m_nTimeSpentOnWheelie != 0) { + if (m_nTimeSpentOnWheelie >= 5000) { + m_nLastTimeSpentOnWheelie = m_nTimeSpentOnWheelie; + m_nLastDistanceTravelledOnWheelie = m_nDistanceTravelledOnWheelie; + if (CStats::LongestWheelie < m_nTimeSpentOnWheelie / 1000) + CStats::LongestWheelie = m_nTimeSpentOnWheelie / 1000; + if (CStats::LongestWheelieDist < m_nDistanceTravelledOnWheelie) + CStats::LongestWheelieDist = m_nDistanceTravelledOnWheelie; + } + m_nTimeSpentOnWheelie = 0; + m_nDistanceTravelledOnWheelie = 0; + m_nCancelWheelStuntTimer = 0; + + } else if (bike->m_aSuspensionSpringRatioPrev[2] == 1.0f && bike->m_aSuspensionSpringRatioPrev[3] == 1.0f + && 0.0f == bike->m_fDamageImpulse) { + m_nTimeSpentOnStoppie += CTimer::GetTimeStepInMilliseconds(); + m_nDistanceTravelledOnStoppie += bike->m_fDistanceTravelled; + m_nCancelWheelStuntTimer = Max(0.0f, m_nCancelWheelStuntTimer - CTimer::GetTimeStepInMilliseconds() * 0.2f); + + } else { + if (m_nTimeSpentOnStoppie != 0 && m_nCancelWheelStuntTimer < 500) { + m_nCancelWheelStuntTimer += CTimer::GetTimeStepInMilliseconds(); + } else { + if (m_nTimeSpentOnStoppie >= 2000) { + m_nLastTimeSpentOnStoppie = m_nTimeSpentOnStoppie; + m_nLastDistanceTravelledOnStoppie = m_nDistanceTravelledOnStoppie; + if (CStats::LongestStoppie < m_nTimeSpentOnStoppie / 1000) + CStats::LongestStoppie = m_nTimeSpentOnStoppie / 1000; + if (CStats::LongestStoppieDist < m_nDistanceTravelledOnStoppie) + CStats::LongestStoppieDist = m_nDistanceTravelledOnStoppie; + } + m_nTimeSpentOnStoppie = 0; + m_nDistanceTravelledOnStoppie = 0; + m_nCancelWheelStuntTimer = 0; + } + } + m_nTimeCarSpentOnTwoWheels = 0; + m_nTimeNotFullyOnGround = 0; + } else { + m_nTimeCarSpentOnTwoWheels = 0; + m_nTimeNotFullyOnGround = 0; + m_nTimeSpentOnWheelie = 0; + m_nTimeSpentOnStoppie = 0; + m_nCancelWheelStuntTimer = 0; + } + + // The effect that makes money counter does while earning/losing money + if (m_nVisibleMoney != m_nMoney) { + int diff = m_nMoney - m_nVisibleMoney; + int diffAbs = Abs(diff); + int changeBy; + + if (diffAbs > 100000) + changeBy = 12345; + else if (diffAbs > 10000) + changeBy = 1234; + else if (diffAbs > 1000) + changeBy = 123; + else if (diffAbs > 50) + changeBy = 42; + else + changeBy = 1; + + if (diff < 0) + m_nVisibleMoney -= changeBy; + else + m_nVisibleMoney += changeBy; + } + + if (!(CTimer::GetFrameCounter() & 15)) { + CVector2D playerPos = m_pPed->bInVehicle ? m_pPed->m_pMyVehicle->GetPosition() : m_pPed->GetPosition(); + m_fRoadDensity = ThePaths.CalcRoadDensity(playerPos.x, playerPos.y); + } + + m_fRoadDensity = Clamp(m_fRoadDensity, 0.5f, 1.45f); + + // Because vehicle enter/exit use same key binding. + bool enterOrExitVeh; + if (m_pPed->bVehExitWillBeInstant && m_pPed->bInVehicle) + enterOrExitVeh = CPad::GetPad(0)->ExitVehicleJustDown(); + else + enterOrExitVeh = CPad::GetPad(0)->GetExitVehicle(); + + if (enterOrExitVeh && m_pPed->m_nPedState != PED_ANSWER_MOBILE && m_pPed->m_nPedState != PED_SNIPER_MODE && m_pPed->m_nPedState != PED_ROCKET_MODE) { + if (m_pPed->bInVehicle) { + if (!m_pRemoteVehicle) { + CEntity *surfaceBelowVeh = m_pPed->m_pMyVehicle->m_pCurGroundEntity; + if (!surfaceBelowVeh || !CBridge::ThisIsABridgeObjectMovingUp(surfaceBelowVeh->GetModelIndex())) { + CVehicle *veh = m_pPed->m_pMyVehicle; + if (!veh->IsBoat() || veh->m_nDoorLock == CARLOCK_LOCKED_PLAYER_INSIDE) { + if (veh->GetStatus() != STATUS_WRECKED && veh->GetStatus() != STATUS_TRAIN_MOVING && veh->m_nDoorLock != CARLOCK_LOCKED_PLAYER_INSIDE) { + bool canJumpOff = false; + if (veh->m_vehType == VEHICLE_TYPE_BIKE) { + canJumpOff = veh->CanPedJumpOffBike(); + } else if (veh->pDriver == m_pPed) { + canJumpOff = veh->CanPedJumpOutCar(); + } + + if (canJumpOff || veh->m_vecMoveSpeed.Magnitude() < 0.1f) { + if (!veh->bIsInWater) + m_pPed->SetObjective(OBJECTIVE_LEAVE_CAR, veh); + + } else if (veh->GetStatus() != STATUS_PLAYER && veh != CGameLogic::pShortCutTaxi) { + veh->AutoPilot.m_nTempAction = TEMPACT_WAIT; + veh->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 1000; + } + } + } else { + m_pPed->SetExitBoat(veh); + m_pPed->bTryingToReachDryLand = true; + } + } + } + } else { + // Enter vehicle + if (CPad::GetPad(0)->ExitVehicleJustDown()) { + bool weAreOnBoat = false; + float lastCloseness = 0.0f; + CVehicle *carBelow = nil; + CEntity *surfaceBelow = m_pPed->m_pCurrentPhysSurface; + if (surfaceBelow && surfaceBelow->IsVehicle()) { + carBelow = (CVehicle*)surfaceBelow; + if (carBelow->IsBoat() && carBelow->m_modelIndex != MI_SKIMMER) { + weAreOnBoat = true; + m_pPed->bOnBoat = true; + if (carBelow->GetStatus() != STATUS_WRECKED && carBelow->GetUp().z > 0.3f) + m_pPed->SetSeekBoatPosition(carBelow); + } + } + // Find closest car + if (!weAreOnBoat) { + float minX = m_pPed->GetPosition().x - 10.0f; + float maxX = 10.0f + m_pPed->GetPosition().x; + float minY = m_pPed->GetPosition().y - 10.0f; + float maxY = 10.0f + m_pPed->GetPosition().y; + + int minXSector = CWorld::GetSectorIndexX(minX); + if (minXSector < 0) minXSector = 0; + int minYSector = CWorld::GetSectorIndexY(minY); + if (minYSector < 0) minYSector = 0; + int maxXSector = CWorld::GetSectorIndexX(maxX); + if (maxXSector > NUMSECTORS_X - 1) maxXSector = NUMSECTORS_X - 1; + int maxYSector = CWorld::GetSectorIndexY(maxY); + if (maxYSector > NUMSECTORS_Y - 1) maxYSector = NUMSECTORS_Y - 1; + + CWorld::AdvanceCurrentScanCode(); + + for (int curY = minYSector; curY <= maxYSector; curY++) { + for (int curX = minXSector; curX <= maxXSector; curX++) { + CSector *sector = CWorld::GetSector(curX, curY); + FindClosestCarSectorList(sector->m_lists[ENTITYLIST_VEHICLES], m_pPed, + minX, minY, maxX, maxY, &lastCloseness, &carBelow); + FindClosestCarSectorList(sector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], m_pPed, + minX, minY, maxX, maxY, &lastCloseness, &carBelow); + } + } + } + // carBelow is now closest vehicle + if (carBelow && !weAreOnBoat) { + if (carBelow->GetStatus() == STATUS_TRAIN_NOT_MOVING) { + m_pPed->SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, carBelow); + } else if (carBelow->IsBoat()) { + if (!carBelow->pDriver) { + m_pPed->m_vehDoor = 0; + m_pPed->SetEnterCar(carBelow, m_pPed->m_vehDoor); + } + } else { + m_pPed->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, carBelow); + } + } + } + } + } + + if (m_bInRemoteMode) { + uint32 timeWithoutRemoteCar = CTimer::GetTimeInMilliseconds() - m_nTimeLostRemoteCar; + if (CTimer::GetPreviousTimeInMilliseconds() - m_nTimeLostRemoteCar < 1000 && timeWithoutRemoteCar >= 1000 && m_WBState == WBSTATE_PLAYING && field_D6) { + TheCamera.SetFadeColour(0, 0, 0); + TheCamera.Fade(1.0f, FADE_OUT); + } + if (timeWithoutRemoteCar > 2000) { + if (m_WBState == WBSTATE_PLAYING && field_D6) { + TheCamera.RestoreWithJumpCut(); + TheCamera.SetFadeColour(0, 0, 0); + TheCamera.Fade(1.0f, FADE_IN); + TheCamera.Process(); + CTimer::Stop(); + CCullZones::ForceCullZoneCoors(TheCamera.GetPosition()); + CRenderer::RequestObjectsInFrustum(); + CStreaming::LoadAllRequestedModels(false); + CTimer::Update(); + } + m_bInRemoteMode = false; + CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle->bRemoveFromWorld = true; + CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle = nil; + if (FindPlayerVehicle()) { + FindPlayerVehicle()->SetStatus(STATUS_PLAYER); + } + } + } + if (!(CTimer::GetFrameCounter() & 31)) { + CVehicle *veh = FindPlayerVehicle(); + if (veh && m_pPed->bInVehicle && veh->GetUp().z < 0.0f + && veh->m_vecMoveSpeed.Magnitude() < 0.05f && (veh->IsCar() || veh->IsBoat()) && !veh->bIsInWater) { + + if (veh->GetUp().z < -0.5f) { + m_nUpsideDownCounter += 2; + } else { + m_nUpsideDownCounter++; + } + } else { + m_nUpsideDownCounter = 0; + } + + if (m_nUpsideDownCounter > 6 && veh->bCanBeDamaged) { + veh->m_fHealth = 249.0f < veh->m_fHealth ? 249.0f : veh->m_fHealth; + if (veh->IsCar()) { + CAutomobile* car = (CAutomobile*)veh; + car->Damage.SetEngineStatus(225); + car->m_pSetOnFireEntity = nil; + } + } + } + if (FindPlayerVehicle()) { + CVehicle *veh = FindPlayerVehicle(); + veh->m_nZoneLevel = LEVEL_IGNORE; + for (int i = 0; i < ARRAY_SIZE(veh->pPassengers); i++) { + if (veh->pPassengers[i]) + veh->pPassengers[i]->m_nZoneLevel = LEVEL_GENERIC; + } + if(veh->m_modelIndex == MI_CADDY) + CStats::DistanceTravelledByGolfCart += veh->m_fDistanceTravelled; + else { + if(veh->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI) + CStats::DistanceTravelledByHelicoptor += veh->m_fDistanceTravelled; + if (veh->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE) + CStats::DistanceTravelledByPlane += veh->m_fDistanceTravelled; + if (veh->GetVehicleAppearance() == VEHICLE_APPEARANCE_CAR) + CStats::DistanceTravelledByCar += veh->m_fDistanceTravelled; + if (veh->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE) + CStats::DistanceTravelledByBike += veh->m_fDistanceTravelled; + if (veh->GetVehicleAppearance() == VEHICLE_APPEARANCE_BOAT) + CStats::DistanceTravelledByBoat += veh->m_fDistanceTravelled; + + if (veh->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE) { + if (veh->m_vecMoveSpeed.Magnitude() > 0.2f) { + CStats::FlightTime += CTimer::GetTimeStep() * 16.f; // what a weird choice + } + } + } + } else { + CStats::DistanceTravelledOnFoot += FindPlayerPed()->m_fDistanceTravelled; + } + + if (m_pPed->m_pWanted->GetWantedLevel() && !CTheScripts::IsPlayerOnAMission()) { + float maxDelta = 0.0f; + static bool movedSignificantly = true; + static bool thereIsACarPathNear = true; + // there was one more guard without variable's itself??? + + if (CTimer::GetTimeInMilliseconds() / 20000 != CTimer::GetPreviousTimeInMilliseconds() / 20000) { + float posChange = (lastPlayerPos - FindPlayerCoors()).Magnitude(); + movedSignificantly = posChange >= 10.0f; + lastPlayerPos = FindPlayerCoors(); + thereIsACarPathNear = ThePaths.FindNodeClosestToCoors(FindPlayerCoors(), PATH_CAR, 60.0f, true, false, false, false) != 0; + } + switch (m_pPed->m_pWanted->GetWantedLevel()) { + case 1: + maxDelta = 31.f; + break; + case 2: + maxDelta = 62.f; + break; + case 3: + maxDelta = 125.f; + break; + case 4: + maxDelta = 250.f; + break; + case 5: + maxDelta = 500.f; + break; + case 6: + maxDelta = 1000.f; + break; + default: + break; + } + float increaseDelta = maxDelta - m_fMediaAttention; + float increaseAttentionBy = CTimer::GetTimeStep() * 0.0001f * increaseDelta; + if (increaseAttentionBy < 0.0f + || movedSignificantly && thereIsACarPathNear && !CCullZones::NoPolice() && !CCullZones::PoliceAbandonCars() && CGame::currArea == AREA_MAIN_MAP) { + m_fMediaAttention += increaseAttentionBy; + } + } else { + m_fMediaAttention = 0.0f; + } + CStats::HighestChaseValue = Max(m_fMediaAttention, CStats::HighestChaseValue); + m_nMoney = Min(999999999, m_nMoney); + m_nVisibleMoney = Min(999999999, m_nVisibleMoney); +} + +bool +CPlayerInfo::IsPlayerInRemoteMode() +{ + return m_pRemoteVehicle || m_bInRemoteMode; +} + +void +CPlayerInfo::SavePlayerInfo(uint8 *buf, uint32 *size) +{ + // Interesting + *size = sizeof(CPlayerInfo); + +#define CopyToBuf(buf, data) memcpy(buf, &data, sizeof(data)); buf += sizeof(data); + CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nMoney); + CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_WBState); + CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nWBTime); + CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nTrafficMultiplier); + CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_fRoadDensity); + CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney); + CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nCollectedPackages); + CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nTotalPackages); + CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bInfiniteSprint); + CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bFastReload); + CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bFireproof); + CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nMaxHealth); + CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nMaxArmour); + CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bGetOutOfJailFree); + CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bGetOutOfHospitalFree); + CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bDriveByAllowed); + CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_aPlayerName); + CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nBustedAudioStatus); + CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nCurrentBustedAudio); +#undef CopyToBuf +} + +void +CPlayerInfo::LoadPlayerInfo(uint8 *buf, uint32 size) +{ +#define CopyFromBuf(buf, data) memcpy(&data, buf, sizeof(data)); buf += sizeof(data); + CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nMoney); + CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_WBState); + CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nWBTime); + CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nTrafficMultiplier); + CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_fRoadDensity); + CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney); + CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nCollectedPackages); + CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nTotalPackages); + CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bInfiniteSprint); + CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bFastReload); + CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bFireproof); + CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nMaxHealth); + CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nMaxArmour); + CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bGetOutOfJailFree); + CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bGetOutOfHospitalFree); + CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bDriveByAllowed); + CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_aPlayerName); + CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nBustedAudioStatus); + CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nCurrentBustedAudio) +#undef CopyFromBuf +} + +void +CPlayerInfo::FindClosestCarSectorList(CPtrList& carList, CPed* ped, float unk1, float unk2, float unk3, float unk4, float* lastCloseness, CVehicle** closestCarOutput) +{ + for (CPtrNode* node = carList.first; node; node = node->next) { + CVehicle *car = (CVehicle*)node->item; + if(car->m_scanCode != CWorld::GetCurrentScanCode()) { + if (!car->bUsesCollision || !car->IsVehicle()) + continue; + + car->m_scanCode = CWorld::GetCurrentScanCode(); + if (car->GetStatus() != STATUS_WRECKED && car->GetStatus() != STATUS_TRAIN_MOVING + && (car->GetUp().z > 0.3f || (car->IsVehicle() && ((CVehicle*)car)->m_vehType == VEHICLE_TYPE_BIKE))) { + CVector carCentre = car->GetBoundCentre(); + + if (Abs(ped->GetPosition().z - carCentre.z) < 2.0f || car->IsCar() && carCentre.z < ped->GetPosition().z && ped->GetPosition().z - 4.f < carCentre.z) { + float dist = (ped->GetPosition() - carCentre).Magnitude2D(); + if (dist <= 10.0f && !CCranes::IsThisCarBeingCarriedByAnyCrane(car)) { + EvaluateCarPosition(car, ped, dist, lastCloseness, closestCarOutput); + } + } + } + } + } +} + +// lastCloseness is passed to other calls of this function +void +CPlayerInfo::EvaluateCarPosition(CEntity *carToTest, CPed *player, float carBoundCentrePedDist, float *lastCloseness, CVehicle **closestCarOutput) +{ + // This dist used for determining the angle to face + CVector2D dist(carToTest->GetPosition() - player->GetPosition()); + float neededTurn = CGeneral::GetATanOfXY(player->GetForward().x, player->GetForward().y) - CGeneral::GetATanOfXY(dist.x, dist.y); + while (neededTurn >= PI) { + neededTurn -= 2 * PI; + } + + while (neededTurn < -PI) { + neededTurn += 2 * PI; + } + + // This dist used for evaluating cars' distances, weird... + // Accounts inverted needed turn (or needed turn in long way) and car dist. + float closeness = (1.0f - Abs(neededTurn) / TWOPI) * (10.0f - carBoundCentrePedDist); + if (closeness > *lastCloseness) { + *lastCloseness = closeness; + *closestCarOutput = (CVehicle*)carToTest; + } +} + +const CVector & +CPlayerInfo::GetPos() +{ +#ifdef FIX_BUGS + if (!m_pPed) + return TheCamera.GetPosition(); +#endif + if (m_pPed->InVehicle()) + return m_pPed->m_pMyVehicle->GetPosition(); + return m_pPed->GetPosition(); +} + +CVector +FindPlayerCoors(void) +{ +#ifdef FIX_BUGS + if (CReplay::IsPlayingBack()) + return TheCamera.GetPosition(); +#endif + CPlayerPed *ped = FindPlayerPed(); + if(ped->InVehicle()) + return ped->m_pMyVehicle->GetPosition(); + else + return ped->GetPosition(); +} + +const CVector & +FindPlayerSpeed(void) +{ +#ifdef FIX_BUGS + static CVector vecTmpVector(0.0f, 0.0f, 0.0f); + if (CReplay::IsPlayingBack()) + return vecTmpVector; +#endif + CPlayerPed *ped = FindPlayerPed(); + if(ped->InVehicle()) + return ped->m_pMyVehicle->m_vecMoveSpeed; + else + return ped->m_vecMoveSpeed; +} + +CVehicle * +FindPlayerVehicle(void) +{ + CPlayerPed *ped = FindPlayerPed(); + if(ped && ped->InVehicle()) return ped->m_pMyVehicle; + return nil; +} + +CEntity * +FindPlayerEntity(void) +{ + CPlayerPed *ped = FindPlayerPed(); + if(ped->InVehicle()) + return ped->m_pMyVehicle; + else + return ped; +} + +CVehicle * +FindPlayerTrain(void) +{ + if(FindPlayerVehicle() && FindPlayerVehicle()->IsTrain()) + return FindPlayerVehicle(); + else + return nil; +} + +CPlayerPed * +FindPlayerPed(void) +{ + return CWorld::Players[CWorld::PlayerInFocus].m_pPed; +} + +const CVector & +FindPlayerCentreOfWorld(int32 player) +{ +#ifdef FIX_BUGS + if(CReplay::IsPlayingBack()) return TheCamera.GetPosition(); +#endif + if(CCarCtrl::bCarsGeneratedAroundCamera) return TheCamera.GetPosition(); + if(CWorld::Players[player].m_pRemoteVehicle) return CWorld::Players[player].m_pRemoteVehicle->GetPosition(); + if(FindPlayerVehicle()) return FindPlayerVehicle()->GetPosition(); + return CWorld::Players[player].m_pPed->GetPosition(); +} + +const CVector & +FindPlayerCentreOfWorld_NoSniperShift(void) +{ +#ifdef FIX_BUGS + if (CReplay::IsPlayingBack()) return TheCamera.GetPosition(); +#endif + if(CCarCtrl::bCarsGeneratedAroundCamera) return TheCamera.GetPosition(); + if(CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle) + return CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle->GetPosition(); + if(FindPlayerVehicle()) return FindPlayerVehicle()->GetPosition(); + return FindPlayerPed()->GetPosition(); +} + +float +FindPlayerHeading(void) +{ + if(CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle) + return CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle->GetForward().Heading(); + if(FindPlayerVehicle()) return FindPlayerVehicle()->GetForward().Heading(); + return FindPlayerPed()->GetForward().Heading(); +} + +bool +CPlayerInfo::IsRestartingAfterDeath() +{ + return m_WBState == WBSTATE_WASTED; +} + +bool +CPlayerInfo::IsRestartingAfterArrest() +{ + return m_WBState == WBSTATE_BUSTED; +} + +void +CPlayerInfo::KillPlayer() +{ + if (m_WBState != WBSTATE_PLAYING) return; + + m_WBState = WBSTATE_WASTED; + m_nWBTime = CTimer::GetTimeInMilliseconds(); + CDarkel::ResetOnPlayerDeath(); + CMessages::AddBigMessage(TheText.Get("DEAD"), 4000, 2); + CStats::TimesDied++; +} + +void +CPlayerInfo::ArrestPlayer() +{ + if (m_WBState != WBSTATE_PLAYING) return; + + m_WBState = WBSTATE_BUSTED; + m_nWBTime = CTimer::GetTimeInMilliseconds(); + m_nBustedAudioStatus = BUSTEDAUDIO_NONE; + CDarkel::ResetOnPlayerDeath(); + CMessages::AddBigMessage(TheText.Get("BUSTED"), 5000, 2); + CStats::TimesArrested++; +} + +void +CPlayerInfo::PlayerFailedCriticalMission() +{ + if (m_WBState != WBSTATE_PLAYING) + return; + m_WBState = WBSTATE_FAILED_CRITICAL_MISSION; + m_nWBTime = CTimer::GetTimeInMilliseconds(); + CDarkel::ResetOnPlayerDeath(); +} + +void +CPlayerInfo::CancelPlayerEnteringCars(CVehicle *car) +{ + if (!car || car == m_pPed->m_pMyVehicle) { + if (m_pPed->EnteringCar()) + m_pPed->QuitEnteringCar(); + } + if (m_pPed->m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER || m_pPed->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) + m_pPed->ClearObjective(); +} + +void +CPlayerInfo::MakePlayerSafe(bool toggle) +{ + if (toggle) { + m_pPed->m_pWanted->m_bIgnoredByEveryone = true; + CWorld::StopAllLawEnforcersInTheirTracks(); + CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_PLAYERINFO); + CPad::StopPadsShaking(); + m_pPed->bBulletProof = true; + m_pPed->bFireProof = true; + m_pPed->bCollisionProof = true; + m_pPed->bMeleeProof = true; + m_pPed->bOnlyDamagedByPlayer = true; + m_pPed->bExplosionProof = true; + m_pPed->m_bCanBeDamaged = false; + ((CPlayerPed*)m_pPed)->ClearAdrenaline(); + CancelPlayerEnteringCars(nil); + gFireManager.ExtinguishPoint(GetPos(), 4000.0f); + CExplosion::RemoveAllExplosionsInArea(GetPos(), 4000.0f); + CProjectileInfo::RemoveAllProjectiles(); + CWorld::SetAllCarsCanBeDamaged(false); + CWorld::ExtinguishAllCarFiresInArea(GetPos(), 4000.0f); + CReplay::DisableReplays(); + + } else { + m_pPed->m_pWanted->m_bIgnoredByEveryone = false; + CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_PLAYERINFO); + m_pPed->bBulletProof = false; + m_pPed->bFireProof = false; + m_pPed->bCollisionProof = false; + m_pPed->bMeleeProof = false; + m_pPed->bOnlyDamagedByPlayer = false; + m_pPed->bExplosionProof = false; + m_pPed->m_bCanBeDamaged = true; + CWorld::SetAllCarsCanBeDamaged(true); + CReplay::EnableReplays(); + } +} + +void +CPlayerInfo::BlowUpRCBuggy(bool actually) +{ + if (!m_pRemoteVehicle || m_pRemoteVehicle->bRemoveFromWorld) + return; + + CRemote::TakeRemoteControlledCarFromPlayer(actually); + if (actually) + m_pRemoteVehicle->BlowUpCar(FindPlayerPed()); +} + +#ifdef GTA_PC +void +CPlayerInfo::SetPlayerSkin(const char *skin) +{ + strncpy(m_aSkinName, skin, 32); + LoadPlayerSkin(); +} + +void +CPlayerInfo::LoadPlayerSkin() +{ + DeletePlayerSkin(); + + m_pSkinTexture = CPlayerSkin::GetSkinTexture(m_aSkinName); +} + +void +CPlayerInfo::DeletePlayerSkin() +{ + if (m_pSkinTexture) { + RwTextureDestroy(m_pSkinTexture); + m_pSkinTexture = nil; + } +} +#endif diff --git a/src/miami/core/PlayerInfo.h b/src/miami/core/PlayerInfo.h new file mode 100644 index 00000000..fc12267d --- /dev/null +++ b/src/miami/core/PlayerInfo.h @@ -0,0 +1,124 @@ +#pragma once + +#include "ColModel.h" + +enum eWastedBustedState +{ + WBSTATE_PLAYING, + WBSTATE_WASTED, + WBSTATE_BUSTED, + WBSTATE_FAILED_CRITICAL_MISSION, +}; + +enum eBustedAudioState +{ + BUSTEDAUDIO_NONE, + BUSTEDAUDIO_LOADING, + BUSTEDAUDIO_DONE +}; + +class CEntity; +class CPed; +class CVehicle; +class CPlayerPed; +class CCivilianPed; + +class CPlayerInfo +{ +public: + CPlayerPed *m_pPed; + CVehicle *m_pRemoteVehicle; + CColModel m_ColModel; + CVehicle *m_pVehicleEx; // vehicle using the col model above + char m_aPlayerName[70]; + int32 m_nMoney; + int32 m_nVisibleMoney; + int32 m_nCollectedPackages; + int32 m_nTotalPackages; + uint32 m_nLastBumpPlayerCarTimer; + uint32 m_nUnusedTaxiTimer; + bool m_bUnusedTaxiThing; + uint32 m_nNextSexFrequencyUpdateTime; + uint32 m_nNextSexMoneyUpdateTime; + int32 m_nSexFrequency; + CCivilianPed *m_pHooker; + int8 m_WBState; // eWastedBustedState + uint32 m_nWBTime; + bool m_bInRemoteMode; + bool field_D5; + bool field_D6; + uint32 m_nTimeLostRemoteCar; + uint32 m_nTimeLastHealthLoss; + uint32 m_nTimeLastArmourLoss; + uint32 m_nTimeTankShotGun; + int32 m_nUpsideDownCounter; + int32 field_EC; + int32 m_nTimeCarSpentOnTwoWheels; + float m_nDistanceCarTravelledOnTwoWheels; + int32 m_nTimeNotFullyOnGround; + int32 m_nTimeSpentOnWheelie; + float m_nDistanceTravelledOnWheelie; + int32 m_nTimeSpentOnStoppie; + float m_nDistanceTravelledOnStoppie; + int32 m_nCancelWheelStuntTimer; + int32 m_nLastTimeCarSpentOnTwoWheels; + float m_nLastDistanceCarTravelledOnTwoWheels; + int32 m_nLastTimeSpentOnWheelie; + float m_nLastDistanceTravelledOnWheelie; + int32 m_nLastTimeSpentOnStoppie; + float m_nLastDistanceTravelledOnStoppie; + int16 m_nTrafficMultiplier; + int16 field_12A; + float m_fRoadDensity; + uint32 m_nPreviousTimeRewardedForExplosion; + uint32 m_nExplosionsSinceLastReward; + uint32 m_nHavocLevel; + float m_fMediaAttention; + bool m_bInfiniteSprint; + bool m_bFastReload; + bool m_bFireproof; + uint8 m_nMaxHealth; + uint8 m_nMaxArmour; + bool m_bGetOutOfJailFree; + bool m_bGetOutOfHospitalFree; + bool m_bDriveByAllowed; + uint8 m_nBustedAudioStatus; + int16 m_nCurrentBustedAudio; +#ifdef GTA_PC + char m_aSkinName[32]; + RwTexture *m_pSkinTexture; +#endif + + void MakePlayerSafe(bool); + const CVector &GetPos(); + void Process(void); + void KillPlayer(void); + void ArrestPlayer(void); + bool IsPlayerInRemoteMode(void); + void PlayerFailedCriticalMission(void); + void Clear(void); + void BlowUpRCBuggy(bool); + void CancelPlayerEnteringCars(CVehicle*); + bool IsRestartingAfterDeath(void); + bool IsRestartingAfterArrest(void); + void EvaluateCarPosition(CEntity*, CPed*, float, float*, CVehicle**); + void LoadPlayerInfo(uint8 *buf, uint32 size); + void SavePlayerInfo(uint8 *buf, uint32* size); + void FindClosestCarSectorList(CPtrList&, CPed*, float, float, float, float, float*, CVehicle**); + +#ifdef GTA_PC + void LoadPlayerSkin(); + void SetPlayerSkin(const char *skin); + void DeletePlayerSkin(); +#endif +}; + +CPlayerPed *FindPlayerPed(void); +CVehicle *FindPlayerVehicle(void); +CVehicle *FindPlayerTrain(void); +CEntity *FindPlayerEntity(void); +CVector FindPlayerCoors(void); +const CVector &FindPlayerSpeed(void); +const CVector &FindPlayerCentreOfWorld(int32 player); +const CVector &FindPlayerCentreOfWorld_NoSniperShift(void); +float FindPlayerHeading(void); \ No newline at end of file diff --git a/src/miami/core/Pools.cpp b/src/miami/core/Pools.cpp new file mode 100644 index 00000000..e601b3c8 --- /dev/null +++ b/src/miami/core/Pools.cpp @@ -0,0 +1,647 @@ +#include "common.h" + +#include "Pools.h" + +#include "Bike.h" +#include "Boat.h" +#include "CarCtrl.h" +#ifdef MISSION_REPLAY +#include "GenericGameStorage.h" +#endif +#include "Population.h" +#include "ProjectileInfo.h" +#include "SaveBuf.h" +#include "Streaming.h" +#include "Wanted.h" +#include "World.h" +#include "MemoryHeap.h" +#include "SaveBuf.h" + +CCPtrNodePool *CPools::ms_pPtrNodePool; +CEntryInfoNodePool *CPools::ms_pEntryInfoNodePool; +CPedPool *CPools::ms_pPedPool; +CVehiclePool *CPools::ms_pVehiclePool; +CBuildingPool *CPools::ms_pBuildingPool; +CTreadablePool *CPools::ms_pTreadablePool; +CObjectPool *CPools::ms_pObjectPool; +CDummyPool *CPools::ms_pDummyPool; +CAudioScriptObjectPool *CPools::ms_pAudioScriptObjectPool; +CColModelPool *CPools::ms_pColModelPool; + +#if defined GTA_PS2 && !defined MASTER // or USE_CUSTOM_ALLOCATOR +// not in VC. perhaps ifdef'ed away +#define CHECKMEM(msg) CMemCheck::AllocateMemCheckBlock(msg) +#else +#define CHECKMEM(msg) +#endif + +void +CPools::Initialise(void) +{ + PUSH_MEMID(MEMID_POOLS); + CHECKMEM("before pools"); + ms_pPtrNodePool = new CCPtrNodePool(NUMPTRNODES, "PtrNode"); + CHECKMEM("after CPtrNodePool"); + ms_pEntryInfoNodePool = new CEntryInfoNodePool(NUMENTRYINFOS, "EntryInfoNode"); + CHECKMEM("after CEntryInfoNodePool"); + ms_pPedPool = new CPedPool(NUMPEDS, "Peds"); + CHECKMEM("after CPedPool"); + ms_pVehiclePool = new CVehiclePool(NUMVEHICLES, "Vehicles"); + CHECKMEM("after CVehiclePool"); + ms_pBuildingPool = new CBuildingPool(NUMBUILDINGS, "Buildings"); + CHECKMEM("after CBuildingPool"); + ms_pTreadablePool = new CTreadablePool(NUMTREADABLES, "Treadables"); + CHECKMEM("after CTreadablePool"); + ms_pObjectPool = new CObjectPool(NUMOBJECTS, "Objects"); + CHECKMEM("after CObjectPool"); + ms_pDummyPool = new CDummyPool(NUMDUMMIES, "Dummys"); + CHECKMEM("after CDummyPool"); + ms_pAudioScriptObjectPool = new CAudioScriptObjectPool(NUMAUDIOSCRIPTOBJECTS, "AudioScriptObj"); + CHECKMEM("after cAudioScriptObjectPool"); + ms_pColModelPool = new CColModelPool(NUMCOLMODELS, "ColModel"); + CHECKMEM("after pools"); + POP_MEMID(); +} + +void +CPools::ShutDown(void) +{ + debug("PtrNodes left %d\n", ms_pPtrNodePool->GetNoOfUsedSpaces()); + debug("EntryInfoNodes left %d\n", ms_pEntryInfoNodePool->GetNoOfUsedSpaces()); + debug("Peds left %d\n", ms_pPedPool->GetNoOfUsedSpaces()); + debug("Vehicles left %d\n", ms_pVehiclePool->GetNoOfUsedSpaces()); + debug("Buildings left %d\n", ms_pBuildingPool->GetNoOfUsedSpaces()); + debug("Treadables left %d\n", ms_pTreadablePool->GetNoOfUsedSpaces()); + debug("Objects left %d\n", ms_pObjectPool->GetNoOfUsedSpaces()); + debug("Dummys left %d\n", ms_pDummyPool->GetNoOfUsedSpaces()); + debug("AudioScriptObjects left %d\n", ms_pAudioScriptObjectPool->GetNoOfUsedSpaces()); + debug("ColModels left %d\n", ms_pColModelPool->GetNoOfUsedSpaces()); + printf("Shutdown pool started\n"); + + delete ms_pPtrNodePool; + delete ms_pEntryInfoNodePool; + delete ms_pPedPool; + delete ms_pVehiclePool; + delete ms_pBuildingPool; + delete ms_pTreadablePool; + delete ms_pObjectPool; + delete ms_pDummyPool; + delete ms_pAudioScriptObjectPool; + delete ms_pColModelPool; + + printf("Shutdown pool done\n"); +} + +int32 CPools::GetPedRef(CPed *ped) { return ms_pPedPool->GetIndex(ped); } +CPed *CPools::GetPed(int32 handle) { return ms_pPedPool->GetAt(handle); } +int32 CPools::GetVehicleRef(CVehicle *vehicle) { return ms_pVehiclePool->GetIndex(vehicle); } +CVehicle *CPools::GetVehicle(int32 handle) { return ms_pVehiclePool->GetAt(handle); } +int32 CPools::GetObjectRef(CObject *object) { return ms_pObjectPool->GetIndex(object); } +CObject *CPools::GetObject(int32 handle) { return ms_pObjectPool->GetAt(handle); } + +void +CPools::CheckPoolsEmpty() +{ + assert(ms_pPedPool->GetNoOfUsedSpaces() == 0); + assert(ms_pVehiclePool->GetNoOfUsedSpaces() == 0); + printf("pools have been cleared\n"); +} + +// Thankfully unused, it would break the game! +void +CPools::MakeSureSlotInObjectPoolIsEmpty(int32 slot) +{ + if (ms_pObjectPool->GetIsFree(slot)) return; + + CObject *object = ms_pObjectPool->GetSlot(slot); + if (object->ObjectCreatedBy == TEMP_OBJECT) { + CWorld::Remove(object); + delete object; + } else if (!CProjectileInfo::RemoveIfThisIsAProjectile(object)) { + // relocate to another slot?? + CObject *newObject = new CObject(object->GetModelIndex(), false); + CWorld::Remove(object); +#if 0 // todo better + *newObject = *object; +#else + memcpy(newObject, object, ms_pObjectPool->GetMaxEntrySize()); +#endif + CWorld::Add(newObject); + object->m_rwObject = nil; + delete object; + newObject->m_pFirstReference = nil; + } +} + +#define CopyFromBuf(buf, data) memcpy(&data, buf, sizeof(data)); SkipSaveBuf(buf, sizeof(data)); +#define CopyToBuf(buf, data) memcpy(buf, &data, sizeof(data)); SkipSaveBuf(buf, sizeof(data)); + +void CPools::LoadVehiclePool(uint8* buf, uint32 size) +{ +INITSAVEBUF + int nNumCars, nNumBoats, nNumBikes; + ReadSaveBuf(&nNumCars, buf); + ReadSaveBuf(&nNumBoats, buf); + ReadSaveBuf(&nNumBikes, buf); + for (int i = 0; i < nNumCars + nNumBoats + nNumBikes; i++) { + uint32 type; + int16 model; + int32 slot; + + ReadSaveBuf(&type, buf); + ReadSaveBuf(&model, buf); + CStreaming::RequestModel(model, STREAMFLAGS_DEPENDENCY); + CStreaming::LoadAllRequestedModels(false); + ReadSaveBuf(&slot, buf); + CVehicle* pVehicle; +#ifdef COMPATIBLE_SAVES + if (type == VEHICLE_TYPE_BOAT) + pVehicle = new(slot) CBoat(model, RANDOM_VEHICLE); + else if (type == VEHICLE_TYPE_CAR) + pVehicle = new(slot) CAutomobile(model, RANDOM_VEHICLE); + else if (type == VEHICLE_TYPE_BIKE) + pVehicle = new(slot) CBike(model, RANDOM_VEHICLE); + else + assert(0); + --CCarCtrl::NumRandomCars; + pVehicle->Load(buf); + CWorld::Add(pVehicle); +#else + char* vbuf = new char[Max(CBike::nSaveStructSize, Max(CAutomobile::nSaveStructSize, CBoat::nSaveStructSize))]; + if (type == VEHICLE_TYPE_BOAT) { + memcpy(vbuf, buf, sizeof(CBoat)); + SkipSaveBuf(buf, sizeof(CBoat)); + CBoat* pBoat = new(slot) CBoat(model, RANDOM_VEHICLE); + pVehicle = pBoat; + --CCarCtrl::NumRandomCars; + } + else if (type == VEHICLE_TYPE_CAR) { + memcpy(vbuf, buf, sizeof(CAutomobile)); + SkipSaveBuf(buf, sizeof(CAutomobile)); + CStreaming::RequestModel(model, 0); // is it needed? + CStreaming::LoadAllRequestedModels(false); + CAutomobile* pAutomobile = new(slot) CAutomobile(model, RANDOM_VEHICLE); + pVehicle = pAutomobile; + CCarCtrl::NumRandomCars--; // why? + pAutomobile->Damage = ((CAutomobile*)vbuf)->Damage; + pAutomobile->SetupDamageAfterLoad(); + } + else if (type == VEHICLE_TYPE_BIKE) { +#ifdef FIX_BUGS + memcpy(vbuf, buf, sizeof(CBike)); +#else + memcpy(vbuf, buf, sizeof(CAutomobile)); +#endif + SkipSaveBuf(buf, sizeof(CBike)); + CBike* pBike = new(slot) CBike(model, RANDOM_VEHICLE); + pVehicle = pBike; + --CCarCtrl::NumRandomCars; + } + else + assert(0); + CVehicle* pBufferVehicle = (CVehicle*)vbuf; + pVehicle->GetMatrix() = pBufferVehicle->GetMatrix(); + pVehicle->VehicleCreatedBy = pBufferVehicle->VehicleCreatedBy; + pVehicle->m_currentColour1 = pBufferVehicle->m_currentColour1; + pVehicle->m_currentColour2 = pBufferVehicle->m_currentColour2; + pVehicle->m_nAlarmState = pBufferVehicle->m_nAlarmState; + pVehicle->m_nNumMaxPassengers = pBufferVehicle->m_nNumMaxPassengers; + pVehicle->field_1D0[0] = pBufferVehicle->field_1D0[0]; + pVehicle->field_1D0[1] = pBufferVehicle->field_1D0[1]; + pVehicle->field_1D0[2] = pBufferVehicle->field_1D0[2]; + pVehicle->field_1D0[3] = pBufferVehicle->field_1D0[3]; + pVehicle->m_fSteerAngle = pBufferVehicle->m_fSteerAngle; + pVehicle->m_fGasPedal = pBufferVehicle->m_fGasPedal; + pVehicle->m_fBrakePedal = pBufferVehicle->m_fBrakePedal; + pVehicle->bIsLawEnforcer = pBufferVehicle->bIsLawEnforcer; + pVehicle->bIsLocked = pBufferVehicle->bIsLocked; + pVehicle->bEngineOn = pBufferVehicle->bEngineOn; + pVehicle->bIsHandbrakeOn = pBufferVehicle->bIsHandbrakeOn; + pVehicle->bLightsOn = pBufferVehicle->bLightsOn; + pVehicle->bFreebies = pBufferVehicle->bFreebies; + pVehicle->m_fHealth = pBufferVehicle->m_fHealth; + pVehicle->m_nCurrentGear = pBufferVehicle->m_nCurrentGear; + pVehicle->m_fChangeGearTime = pBufferVehicle->m_fChangeGearTime; + pVehicle->m_nTimeOfDeath = pBufferVehicle->m_nTimeOfDeath; +#ifdef FIX_BUGS //must be copypaste + pVehicle->m_nBombTimer = pBufferVehicle->m_nBombTimer; +#else + pVehicle->m_nTimeOfDeath = pBufferVehicle->m_nTimeOfDeath; +#endif + pVehicle->m_nDoorLock = pBufferVehicle->m_nDoorLock; + pVehicle->SetStatus(pBufferVehicle->GetStatus()); + pVehicle->SetType(pBufferVehicle->GetType()); + (pVehicle->GetAddressOfEntityProperties())[0] = (pBufferVehicle->GetAddressOfEntityProperties())[0]; + (pVehicle->GetAddressOfEntityProperties())[1] = (pBufferVehicle->GetAddressOfEntityProperties())[1]; + pVehicle->AutoPilot = pBufferVehicle->AutoPilot; + CCarCtrl::UpdateCarCount(pVehicle, false); + CWorld::Add(pVehicle); + delete[] vbuf; +#endif + } +VALIDATESAVEBUF(size) +} + +void CPools::SaveVehiclePool(uint8* buf, uint32* size) +{ +INITSAVEBUF + int nNumCars = 0; + int nNumBoats = 0; + int nNumBikes = 0; + int nPoolSize = GetVehiclePool()->GetSize(); + for (int i = 0; i < nPoolSize; i++) { + CVehicle* pVehicle = GetVehiclePool()->GetSlot(i); + if (!pVehicle) + continue; + bool bHasPassenger = false; + for (int j = 0; j < ARRAY_SIZE(pVehicle->pPassengers); j++) { + if (pVehicle->pPassengers[j]) + bHasPassenger = true; + } +#ifdef MISSION_REPLAY + bool bForceSaving = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pMyVehicle == pVehicle && IsQuickSave; +#ifdef FIX_BUGS + if ((!pVehicle->pDriver && !bHasPassenger) || bForceSaving) { +#else + if (!pVehicle->pDriver && !bHasPassenger) { +#endif + if (pVehicle->IsCar() && (pVehicle->VehicleCreatedBy == MISSION_VEHICLE || bForceSaving)) + ++nNumCars; + if (pVehicle->IsBoat() && (pVehicle->VehicleCreatedBy == MISSION_VEHICLE || bForceSaving)) + ++nNumBoats; + if (pVehicle->IsBike() && (pVehicle->VehicleCreatedBy == MISSION_VEHICLE || bForceSaving)) + ++nNumBikes; +#else + if (!pVehicle->pDriver && !bHasPassenger) { + if (pVehicle->IsCar() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) + ++nNumCars; + if (pVehicle->IsBoat() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) + ++nNumBoats; + if (pVehicle->IsBike() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) + ++nNumBikes; +#endif + } + } + *size = nNumCars * (sizeof(uint32) + sizeof(int16) + sizeof(int32) + CAutomobile::nSaveStructSize) + sizeof(int) + + nNumBoats * (sizeof(uint32) + sizeof(int16) + sizeof(int32) + CBoat::nSaveStructSize) + sizeof(int) + + nNumBikes * (sizeof(uint32) + sizeof(int16) + sizeof(int32) + CBike::nSaveStructSize) + sizeof(int); + WriteSaveBuf(buf, nNumCars); + WriteSaveBuf(buf, nNumBoats); + WriteSaveBuf(buf, nNumBikes); + for (int i = 0; i < nPoolSize; i++) { + CVehicle* pVehicle = GetVehiclePool()->GetSlot(i); + if (!pVehicle) + continue; + bool bHasPassenger = false; + for (int j = 0; j < ARRAY_SIZE(pVehicle->pPassengers); j++) { + if (pVehicle->pPassengers[j]) + bHasPassenger = true; + } +#ifdef MISSION_REPLAY + bool bForceSaving = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pMyVehicle == pVehicle && IsQuickSave; +#endif +#if defined FIX_BUGS && defined MISSION_REPLAY + if ((!pVehicle->pDriver && !bHasPassenger) || bForceSaving) { +#else + if (!pVehicle->pDriver && !bHasPassenger) { +#endif +#ifdef COMPATIBLE_SAVES +#ifdef MISSION_REPLAY + if ((pVehicle->IsCar() || pVehicle->IsBoat() || pVehicle->IsBike()) && (pVehicle->VehicleCreatedBy == MISSION_VEHICLE || bForceSaving)) { +#else + if ((pVehicle->IsCar() || pVehicle->IsBoat() || pVehicle->IsBike()) && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) { +#endif + WriteSaveBuf(buf, pVehicle->m_vehType); + WriteSaveBuf(buf, pVehicle->GetModelIndex()); + WriteSaveBuf(buf, GetVehicleRef(pVehicle)); + pVehicle->Save(buf); + } +#else +#ifdef MISSION_REPLAY + if (pVehicle->IsCar() && (pVehicle->VehicleCreatedBy == MISSION_VEHICLE || bForceSaving)) { +#else + if (pVehicle->IsCar() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) { +#endif + WriteSaveBuf(buf, pVehicle->m_vehType); + WriteSaveBuf(buf, pVehicle->GetModelIndex()); + WriteSaveBuf(buf, GetVehicleRef(pVehicle)); + memcpy(buf, pVehicle, sizeof(CAutomobile)); + SkipSaveBuf(buf, sizeof(CAutomobile)); + } +#ifdef MISSION_REPLAY + if (pVehicle->IsBoat() && (pVehicle->VehicleCreatedBy == MISSION_VEHICLE || bForceSaving)) { +#else + if (pVehicle->IsBoat() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) { +#endif + WriteSaveBuf(buf, pVehicle->m_vehType); + WriteSaveBuf(buf, pVehicle->GetModelIndex()); + WriteSaveBuf(buf, GetVehicleRef(pVehicle)); + memcpy(buf, pVehicle, sizeof(CBoat)); + SkipSaveBuf(buf, sizeof(CBoat)); + } +#ifdef MISSION_REPLAY + if (pVehicle->IsBike() && (pVehicle->VehicleCreatedBy == MISSION_VEHICLE || bForceSaving)) { +#else + if (pVehicle->IsBike() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) { +#endif + WriteSaveBuf(buf, pVehicle->m_vehType); + WriteSaveBuf(buf, pVehicle->GetModelIndex()); + WriteSaveBuf(buf, GetVehicleRef(pVehicle)); + memcpy(buf, pVehicle, sizeof(CBike)); + SkipSaveBuf(buf, sizeof(CBike)); + } +#endif + } + } +VALIDATESAVEBUF(*size) +} + +void CPools::SaveObjectPool(uint8* buf, uint32* size) +{ +INITSAVEBUF + CProjectileInfo::RemoveAllProjectiles(); + CObject::DeleteAllTempObjects(); + int nObjects = 0; + int nPoolSize = GetObjectPool()->GetSize(); + for (int i = 0; i < nPoolSize; i++) { + CObject* pObject = GetObjectPool()->GetSlot(i); + if (!pObject) + continue; + if (pObject->ObjectCreatedBy == MISSION_OBJECT) + ++nObjects; + } + *size = nObjects * (sizeof(int16) + sizeof(int) + sizeof(CCompressedMatrix) + + sizeof(float) + sizeof(CCompressedMatrix) + sizeof(int8) + 7 * sizeof(bool) + sizeof(int16) + + + sizeof(int8) * 2 + sizeof(float) + sizeof(int8) + sizeof(int8) + + sizeof(uint32) + 2 * sizeof(uint32)) + sizeof(int); + CopyToBuf(buf, nObjects); + for (int i = 0; i < nPoolSize; i++) { + CObject* pObject = GetObjectPool()->GetSlot(i); + if (!pObject) + continue; + if (pObject->ObjectCreatedBy == MISSION_OBJECT) { + bool bIsPickup = pObject->bIsPickup; + bool bPickupObjWithMessage = pObject->bPickupObjWithMessage; + bool bOutOfStock = pObject->bOutOfStock; + bool bGlassCracked = pObject->bGlassCracked; + bool bGlassBroken = pObject->bGlassBroken; + bool bHasBeenDamaged = pObject->bHasBeenDamaged; + bool bUseVehicleColours = pObject->bUseVehicleColours; + CCompressedMatrix tmp; + CopyToBuf(buf, pObject->m_modelIndex); + int32 ref = GetObjectRef(pObject); + CopyToBuf(buf, ref); + tmp.CompressFromFullMatrix(pObject->GetMatrix()); + CopyToBuf(buf, tmp); + CopyToBuf(buf, pObject->m_fUprootLimit); + tmp.CompressFromFullMatrix(pObject->m_objectMatrix); + CopyToBuf(buf, tmp); + CopyToBuf(buf, pObject->ObjectCreatedBy); + CopyToBuf(buf, bIsPickup); + CopyToBuf(buf, bPickupObjWithMessage); + CopyToBuf(buf, bOutOfStock); + CopyToBuf(buf, bGlassCracked); + CopyToBuf(buf, bGlassBroken); + CopyToBuf(buf, bHasBeenDamaged); + CopyToBuf(buf, bUseVehicleColours); + CopyToBuf(buf, pObject->m_nCostValue); + CopyToBuf(buf, pObject->m_nBonusValue); + SkipSaveBuf(buf, 1); + CopyToBuf(buf, pObject->m_fCollisionDamageMultiplier); + CopyToBuf(buf, pObject->m_nCollisionDamageEffect); + CopyToBuf(buf, pObject->m_nSpecialCollisionResponseCases); + CopyToBuf(buf, pObject->m_nEndOfLifeTime); +#ifdef COMPATIBLE_SAVES + pObject->SaveEntityFlags(buf); +#else + CopyToBuf(buf, (pObject->GetAddressOfEntityProperties())[0]); + CopyToBuf(buf, (pObject->GetAddressOfEntityProperties())[1]); +#endif + } + } +VALIDATESAVEBUF(*size) +} + +void CPools::LoadObjectPool(uint8* buf, uint32 size) +{ +INITSAVEBUF + int nObjects; + CopyFromBuf(buf, nObjects); + for (int i = 0; i < nObjects; i++) { + int16 mi; + CopyFromBuf(buf, mi); + int ref; + CopyFromBuf(buf, ref); + char* obuf = new char[sizeof(CObject)]; + CObject* pBufferObject = (CObject*)obuf; + CCompressedMatrix tmp; + CopyFromBuf(buf, tmp); + tmp.DecompressIntoFullMatrix(pBufferObject->GetMatrix()); + CopyFromBuf(buf, pBufferObject->m_fUprootLimit); + CopyFromBuf(buf, tmp); + tmp.DecompressIntoFullMatrix(pBufferObject->m_objectMatrix); + CopyFromBuf(buf, pBufferObject->ObjectCreatedBy); + int8 bitFlag; + CopyFromBuf(buf, bitFlag); + pBufferObject->bIsPickup = bitFlag; + CopyFromBuf(buf, bitFlag); + pBufferObject->bPickupObjWithMessage = bitFlag; + CopyFromBuf(buf, bitFlag); + pBufferObject->bOutOfStock = bitFlag; + CopyFromBuf(buf, bitFlag); + pBufferObject->bGlassCracked = bitFlag; + CopyFromBuf(buf, bitFlag); + pBufferObject->bGlassBroken = bitFlag; + CopyFromBuf(buf, bitFlag); + pBufferObject->bHasBeenDamaged = bitFlag; + CopyFromBuf(buf, bitFlag); + pBufferObject->bUseVehicleColours = bitFlag; + CopyFromBuf(buf, pBufferObject->m_nCostValue); + CopyFromBuf(buf, pBufferObject->m_nBonusValue); + SkipSaveBuf(buf, 1); + CopyFromBuf(buf, pBufferObject->m_fCollisionDamageMultiplier); + CopyFromBuf(buf, pBufferObject->m_nCollisionDamageEffect); + CopyFromBuf(buf, pBufferObject->m_nSpecialCollisionResponseCases); + CopyFromBuf(buf, pBufferObject->m_nEndOfLifeTime); +#ifndef COMPATIBLE_SAVES + CopyFromBuf(buf, (pBufferObject->GetAddressOfEntityProperties())[0]); + CopyFromBuf(buf, (pBufferObject->GetAddressOfEntityProperties())[1]); +#endif + if (GetObjectPool()->GetSlot(ref >> 8)) + CPopulation::ConvertToDummyObject(GetObjectPool()->GetSlot(ref >> 8)); + CObject* pObject = new(ref) CObject(mi, false); + pObject->GetMatrix() = pBufferObject->GetMatrix(); +#ifdef COMPATIBLE_SAVES + pObject->LoadEntityFlags(buf); +#endif + pObject->m_fUprootLimit = pBufferObject->m_fUprootLimit; + pObject->m_objectMatrix = pBufferObject->m_objectMatrix; + pObject->ObjectCreatedBy = pBufferObject->ObjectCreatedBy; + pObject->bIsPickup = pBufferObject->bIsPickup; + pObject->bPickupObjWithMessage = pBufferObject->bPickupObjWithMessage; + pObject->bOutOfStock = pBufferObject->bOutOfStock; + pObject->bGlassCracked = pBufferObject->bGlassCracked; + pObject->bGlassBroken = pBufferObject->bGlassBroken; + pObject->bHasBeenDamaged = pBufferObject->bHasBeenDamaged; + pObject->bUseVehicleColours = pBufferObject->bUseVehicleColours; + pObject->m_fCollisionDamageMultiplier = pBufferObject->m_fCollisionDamageMultiplier; + pObject->m_nCollisionDamageEffect = pBufferObject->m_nCollisionDamageEffect; + pObject->m_nSpecialCollisionResponseCases = pBufferObject->m_nSpecialCollisionResponseCases; + pObject->m_nEndOfLifeTime = pBufferObject->m_nEndOfLifeTime; +#ifndef COMPATIBLE_SAVES + (pObject->GetAddressOfEntityProperties())[0] = (pBufferObject->GetAddressOfEntityProperties())[0]; + (pObject->GetAddressOfEntityProperties())[1] = (pBufferObject->GetAddressOfEntityProperties())[1]; +#endif + pObject->bHasCollided = false; + pObject->m_nCostValue = pBufferObject->m_nCostValue; + pObject->m_nBonusValue = pBufferObject->m_nBonusValue; + CWorld::Add(pObject); + delete[] obuf; + } +VALIDATESAVEBUF(size) +} + +void CPools::SavePedPool(uint8* buf, uint32* size) +{ +INITSAVEBUF + int nNumPeds = 0; + int nPoolSize = GetPedPool()->GetSize(); + for (int i = 0; i < nPoolSize; i++) { + CPed* pPed = GetPedPool()->GetSlot(i); + if (!pPed) + continue; +#ifdef MISSION_REPLAY + if ((!pPed->bInVehicle || (pPed == CWorld::Players[CWorld::PlayerInFocus].m_pPed && IsQuickSave)) && pPed->m_nPedType == PEDTYPE_PLAYER1) +#else + if (!pPed->bInVehicle && pPed->m_nPedType == PEDTYPE_PLAYER1) +#endif + nNumPeds++; + } + *size = sizeof(int) + nNumPeds * (sizeof(uint32) + sizeof(int16) + sizeof(int) + CPlayerPed::nSaveStructSize + + sizeof(CWanted::MaximumWantedLevel) + sizeof(CWanted::nMaximumWantedLevel) + MAX_MODEL_NAME); + CopyToBuf(buf, nNumPeds); + for (int i = 0; i < nPoolSize; i++) { + CPed* pPed = GetPedPool()->GetSlot(i); + if (!pPed) + continue; +#ifdef MISSION_REPLAY + if ((!pPed->bInVehicle || (pPed == CWorld::Players[CWorld::PlayerInFocus].m_pPed && IsQuickSave)) && pPed->m_nPedType == PEDTYPE_PLAYER1) { +#else + if (!pPed->bInVehicle && pPed->m_nPedType == PEDTYPE_PLAYER1) { +#endif + CopyToBuf(buf, pPed->m_nPedType); + CopyToBuf(buf, pPed->m_modelIndex); + int32 ref = GetPedRef(pPed); + CopyToBuf(buf, ref); +#ifdef COMPATIBLE_SAVES + pPed->Save(buf); +#else + memcpy(buf, pPed, sizeof(CPlayerPed)); + SkipSaveBuf(buf, sizeof(CPlayerPed)); +#endif + CopyToBuf(buf, CWanted::MaximumWantedLevel); + CopyToBuf(buf, CWanted::nMaximumWantedLevel); + memcpy(buf, CModelInfo::GetModelInfo(pPed->GetModelIndex())->GetModelName(), MAX_MODEL_NAME); + SkipSaveBuf(buf, MAX_MODEL_NAME); + } + } +VALIDATESAVEBUF(*size); +#undef CopyToBuf +} + +void CPools::LoadPedPool(uint8* buf, uint32 size) +{ +INITSAVEBUF + int nPeds; + CopyFromBuf(buf, nPeds); + for (int i = 0; i < nPeds; i++) { + uint32 pedtype; + int16 model; + int ref; + + CopyFromBuf(buf, pedtype); + CopyFromBuf(buf, model); + CopyFromBuf(buf, ref); +#ifdef COMPATIBLE_SAVES + CPed* pPed; + + char name[MAX_MODEL_NAME]; + // Unfortunate hack: player model is stored after ped structure. + // It could be avoided by just using "player" because in practice it is always true. + memcpy(name, buf + CPlayerPed::nSaveStructSize + 2 * sizeof(int32), MAX_MODEL_NAME); + CStreaming::RequestSpecialModel(model, name, STREAMFLAGS_DONT_REMOVE); + CStreaming::LoadAllRequestedModels(false); + + if (pedtype == PEDTYPE_PLAYER1) + pPed = new(ref) CPlayerPed(); + else + assert(0); + + pPed->Load(buf); + if (pedtype == PEDTYPE_PLAYER1) { + CopyFromBuf(buf, CWanted::MaximumWantedLevel); + CopyFromBuf(buf, CWanted::nMaximumWantedLevel); + SkipSaveBuf(buf, MAX_MODEL_NAME); + } + + if (pedtype == PEDTYPE_PLAYER1) { + pPed->m_wepAccuracy = 100; + CWorld::Players[0].m_pPed = (CPlayerPed*)pPed; + } + CWorld::Add(pPed); +#else + char* pbuf = new char[sizeof(CPlayerPed)]; + CPlayerPed* pBufferPlayer = (CPlayerPed*)pbuf; + CPed* pPed; + char name[MAX_MODEL_NAME]; + // the code implies that there was idea to load non-player ped + if (pedtype == PEDTYPE_PLAYER1) { // always true + memcpy(pbuf, buf, sizeof(CPlayerPed)); + SkipSaveBuf(buf, sizeof(CPlayerPed)); + CopyFromBuf(buf, CWanted::MaximumWantedLevel); + CopyFromBuf(buf, CWanted::nMaximumWantedLevel); + CopyFromBuf(buf, name); + } + CStreaming::RequestSpecialModel(model, name, STREAMFLAGS_DONT_REMOVE); + CStreaming::LoadAllRequestedModels(false); + if (pedtype == PEDTYPE_PLAYER1) { + CPlayerPed* pPlayerPed = new(ref) CPlayerPed(); + for (int i = 0; i < ARRAY_SIZE(pPlayerPed->m_nTargettableObjects); i++) + pPlayerPed->m_nTargettableObjects[i] = pBufferPlayer->m_nTargettableObjects[i]; + pPlayerPed->m_fMaxStamina = pBufferPlayer->m_fMaxStamina; + pPed = pPlayerPed; + } + pPed->SetPosition(pBufferPlayer->GetPosition()); + pPed->m_fHealth = pBufferPlayer->m_fHealth; + pPed->m_fArmour = pBufferPlayer->m_fArmour; + pPed->CharCreatedBy = pBufferPlayer->CharCreatedBy; + pPed->m_currentWeapon = 0; + pPed->m_maxWeaponTypeAllowed = pBufferPlayer->m_maxWeaponTypeAllowed; + for (int i = 0; i < TOTAL_WEAPON_SLOTS; i++) { + if (pBufferPlayer->HasWeaponSlot(i)) { + int modelId = CWeaponInfo::GetWeaponInfo(pBufferPlayer->GetWeapon(i).m_eWeaponType)->m_nModelId; + if (modelId != -1) { + CStreaming::RequestModel(modelId, STREAMFLAGS_DEPENDENCY); + int modelId2 = CWeaponInfo::GetWeaponInfo(pBufferPlayer->GetWeapon(i).m_eWeaponType)->m_nModel2Id; + if (modelId2 != -1) + CStreaming::RequestModel(modelId2, STREAMFLAGS_DEPENDENCY); + + CStreaming::LoadAllRequestedModels(false); + } + pPed->GiveWeapon(pBufferPlayer->GetWeapon(i).m_eWeaponType, pBufferPlayer->GetWeapon(i).m_nAmmoTotal, false); + } + } + + if (pedtype == PEDTYPE_PLAYER1) { + pPed->m_wepAccuracy = 100; + CWorld::Players[0].m_pPed = (CPlayerPed*)pPed; + } + CWorld::Add(pPed); + delete[] pbuf; +#endif + } +VALIDATESAVEBUF(size) +} + +#undef CopyFromBuf +#undef CopyToBuf \ No newline at end of file diff --git a/src/miami/core/Pools.h b/src/miami/core/Pools.h new file mode 100644 index 00000000..afef1b85 --- /dev/null +++ b/src/miami/core/Pools.h @@ -0,0 +1,64 @@ +#pragma once + +#include "templates.h" +#include "Lists.h" +#include "Treadable.h" +#include "Object.h" +#include "CutsceneObject.h" +#include "PlayerPed.h" +#include "Automobile.h" +#include "DummyPed.h" +#include "AudioScriptObject.h" + +typedef CPool CCPtrNodePool; +typedef CPool CEntryInfoNodePool; +typedef CPool CPedPool; +typedef CPool CVehiclePool; +typedef CPool CBuildingPool; +typedef CPool CTreadablePool; +typedef CPool CObjectPool; +typedef CPool CDummyPool; +typedef CPool CAudioScriptObjectPool; +typedef CPool CColModelPool; + +class CPools +{ + static CCPtrNodePool *ms_pPtrNodePool; + static CEntryInfoNodePool *ms_pEntryInfoNodePool; + static CPedPool *ms_pPedPool; + static CVehiclePool *ms_pVehiclePool; + static CBuildingPool *ms_pBuildingPool; + static CTreadablePool *ms_pTreadablePool; + static CObjectPool *ms_pObjectPool; + static CDummyPool *ms_pDummyPool; + static CAudioScriptObjectPool *ms_pAudioScriptObjectPool; + static CColModelPool *ms_pColModelPool; +public: + static CCPtrNodePool *GetPtrNodePool(void) { return ms_pPtrNodePool; } + static CEntryInfoNodePool *GetEntryInfoNodePool(void) { return ms_pEntryInfoNodePool; } + static CPedPool *GetPedPool(void) { return ms_pPedPool; } + static CVehiclePool *GetVehiclePool(void) { return ms_pVehiclePool; } + static CBuildingPool *GetBuildingPool(void) { return ms_pBuildingPool; } + static CTreadablePool *GetTreadablePool(void) { return ms_pTreadablePool; } + static CObjectPool *GetObjectPool(void) { return ms_pObjectPool; } + static CDummyPool *GetDummyPool(void) { return ms_pDummyPool; } + static CAudioScriptObjectPool *GetAudioScriptObjectPool(void) { return ms_pAudioScriptObjectPool; } + static CColModelPool *GetColModelPool(void) { return ms_pColModelPool; } + + static void Initialise(void); + static void ShutDown(void); + static int32 GetPedRef(CPed *ped); + static CPed *GetPed(int32 handle); + static int32 GetVehicleRef(CVehicle *vehicle); + static CVehicle *GetVehicle(int32 handle); + static int32 GetObjectRef(CObject *object); + static CObject *GetObject(int32 handle); + static void CheckPoolsEmpty(); + static void MakeSureSlotInObjectPoolIsEmpty(int32 slot); + static void LoadObjectPool(uint8 *buf, uint32 size); + static void LoadPedPool(uint8 *buf, uint32 size); + static void LoadVehiclePool(uint8 *buf, uint32 size); + static void SaveObjectPool(uint8 *buf, uint32 *size); + static void SavePedPool(uint8 *buf, uint32 *size); + static void SaveVehiclePool(uint8 *buf, uint32 *size); +}; diff --git a/src/miami/core/Profile.cpp b/src/miami/core/Profile.cpp new file mode 100644 index 00000000..0aa18ab8 --- /dev/null +++ b/src/miami/core/Profile.cpp @@ -0,0 +1,71 @@ +#include "common.h" +#include "Profile.h" + +#ifndef MASTER +float CProfile::ms_afStartTime[NUM_PROFILES]; +float CProfile::ms_afCumulativeTime[NUM_PROFILES]; +float CProfile::ms_afEndTime[NUM_PROFILES]; +float CProfile::ms_afMaxEndTime[NUM_PROFILES]; +float CProfile::ms_afMaxCumulativeTime[NUM_PROFILES]; +Const char *CProfile::ms_pProfileString[NUM_PROFILES]; +RwRGBA CProfile::ms_aBarColours[NUM_PROFILES]; + +void CProfile::Initialise() +{ + ms_afMaxEndTime[PROFILE_FRAME_RATE] = 0.0f; + ms_afMaxEndTime[PROFILE_PHYSICS] = 0.0f; + ms_afMaxEndTime[PROFILE_COLLISION] = 0.0f; + ms_afMaxEndTime[PROFILE_PED_AI] = 0.0f; + ms_afMaxEndTime[PROFILE_PROCESSING_TIME] = 0.0f; + ms_afMaxEndTime[PROFILE_RENDERING_TIME] = 0.0f; + ms_afMaxEndTime[PROFILE_TOTAL] = 0.0f; + + ms_pProfileString[PROFILE_FRAME_RATE] = "Frame rate"; + ms_pProfileString[PROFILE_PHYSICS] = "Physics"; + ms_pProfileString[PROFILE_COLLISION] = "Collision"; + ms_pProfileString[PROFILE_PED_AI] = "Ped AI"; + ms_pProfileString[PROFILE_PROCESSING_TIME] = "Processing time"; + ms_pProfileString[PROFILE_RENDERING_TIME] = "Rendering time"; + ms_pProfileString[PROFILE_TOTAL] = "Total"; + + ms_afMaxCumulativeTime[PROFILE_FRAME_RATE] = 0.0f; + ms_afMaxCumulativeTime[PROFILE_PHYSICS] = 0.0f; + ms_afMaxCumulativeTime[PROFILE_COLLISION] = 0.0f; + ms_afMaxCumulativeTime[PROFILE_PED_AI] = 0.0f; + ms_afMaxCumulativeTime[PROFILE_PROCESSING_TIME] = 0.0f; + ms_afMaxCumulativeTime[PROFILE_RENDERING_TIME] = 0.0f; + ms_afMaxCumulativeTime[PROFILE_TOTAL] = 0.0f; + + ms_aBarColours[PROFILE_PHYSICS] = { 0, 127, 255, 255 }; + ms_aBarColours[PROFILE_COLLISION] = { 0, 255, 255, 255 }; + ms_aBarColours[PROFILE_PED_AI] = { 255, 0, 0, 255 }; + ms_aBarColours[PROFILE_PROCESSING_TIME] = { 0, 255, 0, 255 }; + ms_aBarColours[PROFILE_RENDERING_TIME] = { 0, 0, 255, 255 }; + ms_aBarColours[PROFILE_TOTAL] = { 255, 255, 255, 255 }; +} + +void CProfile::SuspendProfile(eProfile profile) +{ + ms_afEndTime[profile] = -ms_afStartTime[profile]; + ms_afCumulativeTime[profile] -= ms_afStartTime[profile]; +} + +void CProfile::ShowResults() +{ + ms_afMaxEndTime[PROFILE_FRAME_RATE] = Max(ms_afMaxEndTime[PROFILE_FRAME_RATE], ms_afEndTime[PROFILE_FRAME_RATE]); + ms_afMaxEndTime[PROFILE_PHYSICS] = Max(ms_afMaxEndTime[PROFILE_PHYSICS], ms_afEndTime[PROFILE_PHYSICS]); + ms_afMaxEndTime[PROFILE_COLLISION] = Max(ms_afMaxEndTime[PROFILE_COLLISION], ms_afEndTime[PROFILE_COLLISION]); + ms_afMaxEndTime[PROFILE_PED_AI] = Max(ms_afMaxEndTime[PROFILE_PED_AI], ms_afEndTime[PROFILE_PED_AI]); + ms_afMaxEndTime[PROFILE_PROCESSING_TIME] = Max(ms_afMaxEndTime[PROFILE_PROCESSING_TIME], ms_afEndTime[PROFILE_PROCESSING_TIME]); + ms_afMaxEndTime[PROFILE_RENDERING_TIME] = Max(ms_afMaxEndTime[PROFILE_RENDERING_TIME], ms_afEndTime[PROFILE_RENDERING_TIME]); + ms_afMaxEndTime[PROFILE_TOTAL] = Max(ms_afMaxEndTime[PROFILE_TOTAL], ms_afEndTime[PROFILE_TOTAL]); + + ms_afMaxCumulativeTime[PROFILE_FRAME_RATE] = Max(ms_afMaxCumulativeTime[PROFILE_FRAME_RATE], ms_afCumulativeTime[PROFILE_FRAME_RATE]); + ms_afMaxCumulativeTime[PROFILE_PHYSICS] = Max(ms_afMaxCumulativeTime[PROFILE_PHYSICS], ms_afCumulativeTime[PROFILE_PHYSICS]); + ms_afMaxCumulativeTime[PROFILE_COLLISION] = Max(ms_afMaxCumulativeTime[PROFILE_COLLISION], ms_afCumulativeTime[PROFILE_COLLISION]); + ms_afMaxCumulativeTime[PROFILE_PED_AI] = Max(ms_afMaxCumulativeTime[PROFILE_PED_AI], ms_afCumulativeTime[PROFILE_PED_AI]); + ms_afMaxCumulativeTime[PROFILE_PROCESSING_TIME] = Max(ms_afMaxCumulativeTime[PROFILE_PROCESSING_TIME], ms_afCumulativeTime[PROFILE_PROCESSING_TIME]); + ms_afMaxCumulativeTime[PROFILE_RENDERING_TIME] = Max(ms_afMaxCumulativeTime[PROFILE_RENDERING_TIME], ms_afCumulativeTime[PROFILE_RENDERING_TIME]); + ms_afMaxCumulativeTime[PROFILE_TOTAL] = Max(ms_afMaxCumulativeTime[PROFILE_TOTAL], ms_afCumulativeTime[PROFILE_TOTAL]); +} +#endif diff --git a/src/miami/core/Profile.h b/src/miami/core/Profile.h new file mode 100644 index 00000000..4fe693ae --- /dev/null +++ b/src/miami/core/Profile.h @@ -0,0 +1,28 @@ +#pragma once + +enum eProfile +{ + PROFILE_FRAME_RATE, + PROFILE_PHYSICS, + PROFILE_COLLISION, + PROFILE_PED_AI, + PROFILE_PROCESSING_TIME, + PROFILE_RENDERING_TIME, + PROFILE_TOTAL, + NUM_PROFILES, +}; + +class CProfile +{ + static float ms_afStartTime[NUM_PROFILES]; + static float ms_afCumulativeTime[NUM_PROFILES]; + static float ms_afEndTime[NUM_PROFILES]; + static float ms_afMaxEndTime[NUM_PROFILES]; + static float ms_afMaxCumulativeTime[NUM_PROFILES]; + static Const char *ms_pProfileString[NUM_PROFILES]; + static RwRGBA ms_aBarColours[NUM_PROFILES]; +public: + static void Initialise(); + static void SuspendProfile(eProfile profile); + static void ShowResults(); +}; diff --git a/src/miami/core/Radar.cpp b/src/miami/core/Radar.cpp new file mode 100644 index 00000000..4b5cd54e --- /dev/null +++ b/src/miami/core/Radar.cpp @@ -0,0 +1,1831 @@ +#if (!defined(GTA_PS2_STUFF) && defined(RWLIBS)) || defined(__MWERKS__) +#define WITHD3D +#endif +#include "config.h" +#include "common.h" + +#include "RwHelper.h" +#include "Radar.h" +#include "Camera.h" +#include "Hud.h" +#include "World.h" +#include "Frontend.h" +#include "General.h" +#include "Vehicle.h" +#include "Pools.h" +#include "Script.h" +#include "TxdStore.h" +#include "World.h" +#include "SaveBuf.h" +#include "Streaming.h" +#include "SpecialFX.h" +#include "Font.h" +#include "SaveBuf.h" + +float CRadar::m_radarRange; +sRadarTrace CRadar::ms_RadarTrace[NUMRADARBLIPS]; +CVector2D vec2DRadarOrigin; +int32 gRadarTxdIds[64]; + +CSprite2d CRadar::CentreSprite; +CSprite2d CRadar::MapHereSprite; +CSprite2d CRadar::NorthSprite; +CSprite2d CRadar::AverySprite; +CSprite2d CRadar::BikerSprite; +CSprite2d CRadar::CortezSprite; +CSprite2d CRadar::DiazSprite; +CSprite2d CRadar::KentSprite; +CSprite2d CRadar::LawyerSprite; +CSprite2d CRadar::PhilSprite; +CSprite2d CRadar::BikersSprite; +CSprite2d CRadar::BoatyardSprite; +CSprite2d CRadar::MalibuClubSprite; +CSprite2d CRadar::CubansSprite; +CSprite2d CRadar::FilmSprite; +CSprite2d CRadar::GunSprite; +CSprite2d CRadar::HaitiansSprite; +CSprite2d CRadar::HardwareSprite; +CSprite2d CRadar::SaveHouseSprite; +CSprite2d CRadar::StripSprite; +CSprite2d CRadar::IceSprite; +CSprite2d CRadar::KCabsSprite; +CSprite2d CRadar::LovefistSprite; +CSprite2d CRadar::PrintworksSprite; +CSprite2d CRadar::PropertySprite; +CSprite2d CRadar::SunYardSprite; +CSprite2d CRadar::SpraySprite; +CSprite2d CRadar::TShirtSprite; +CSprite2d CRadar::TommySprite; +CSprite2d CRadar::PhoneSprite; +CSprite2d CRadar::RadioWildstyleSprite; +CSprite2d CRadar::RadioFlashSprite; +CSprite2d CRadar::RadioKChatSprite; +CSprite2d CRadar::RadioFeverSprite; +CSprite2d CRadar::RadioVRockSprite; +CSprite2d CRadar::RadioVCPRSprite; +CSprite2d CRadar::RadioEspantosoSprite; +CSprite2d CRadar::RadioEmotionSprite; +CSprite2d CRadar::RadioWaveSprite; +#ifdef MAP_ENHANCEMENTS +CSprite2d CRadar::WaypointSprite; +#endif + +CSprite2d *CRadar::RadarSprites[RADAR_SPRITE_COUNT] = { + nil, + &CentreSprite, + &MapHereSprite, + &NorthSprite, + &AverySprite, + &BikerSprite, + &CortezSprite, + &DiazSprite, + &KentSprite, + &LawyerSprite, + &PhilSprite, + &BikersSprite, + &BoatyardSprite, + &MalibuClubSprite, + &CubansSprite, + &FilmSprite, + &GunSprite, + &HaitiansSprite, + &HardwareSprite, + &SaveHouseSprite, + &StripSprite, + &IceSprite, + &KCabsSprite, + &LovefistSprite, + &PrintworksSprite, + &PropertySprite, + &SunYardSprite, + &SpraySprite, + &TShirtSprite, + &TommySprite, + &PhoneSprite, + &RadioWildstyleSprite, + &RadioFlashSprite, + &RadioKChatSprite, + &RadioFeverSprite, + &RadioVRockSprite, + &RadioVCPRSprite, + &RadioEspantosoSprite, + &RadioEmotionSprite, + &RadioWaveSprite, +#ifdef MAP_ENHANCEMENTS + &WaypointSprite, +#endif +}; + +// Why this doesn't coincide with world coordinates i don't know +#define RADAR_MIN_X (-2000.0f) +#define RADAR_MIN_Y (-2000.0f) +#define RADAR_MAX_X (2000.0f) +#define RADAR_MAX_Y (2000.0f) +#define RADAR_SIZE_X (RADAR_MAX_X - RADAR_MIN_X) +#define RADAR_SIZE_Y (RADAR_MAX_Y - RADAR_MIN_Y) + +#define RADAR_NUM_TILES (8) +#define RADAR_TILE_SIZE (RADAR_SIZE_X / RADAR_NUM_TILES) +static_assert(RADAR_TILE_SIZE == (RADAR_SIZE_Y / RADAR_NUM_TILES), "CRadar: not a square"); + +#define RADAR_MIN_RANGE (120.0f) +#define RADAR_MAX_RANGE (350.0f) +#define RADAR_MIN_SPEED (0.3f) +#define RADAR_MAX_SPEED (0.9f) + +CRGBA CRadar::ArrowBlipColour1; +CRGBA CRadar::ArrowBlipColour2; +int16 CRadar::MapLegendCounter; +int16 CRadar::MapLegendList[NUM_MAP_LEGENDS]; +#ifdef MAP_ENHANCEMENTS +int CRadar::TargetMarkerId = -1; +CVector CRadar::TargetMarkerPos; +#endif + +float CRadar::cachedCos; +float CRadar::cachedSin; + +void ClipRadarTileCoords(int32 &x, int32 &y) +{ + if (x < 0) + x = 0; + if (x > RADAR_NUM_TILES-1) + x = RADAR_NUM_TILES-1; + if (y < 0) + y = 0; + if (y > RADAR_NUM_TILES-1) + y = RADAR_NUM_TILES-1; +} + +void RequestMapSection(int32 x, int32 y) +{ + ClipRadarTileCoords(x, y); + CStreaming::RequestTxd(gRadarTxdIds[x + RADAR_NUM_TILES * y], STREAMFLAGS_DONT_REMOVE | STREAMFLAGS_DEPENDENCY); +} + +void RemoveMapSection(int32 x, int32 y) +{ + if (x >= 0 && x <= RADAR_NUM_TILES - 1 && y >= 0 && y <= RADAR_NUM_TILES - 1) + CStreaming::RemoveTxd(gRadarTxdIds[x + RADAR_NUM_TILES * y]); +} + +// Transform from section indices to world coordinates +void GetTextureCorners(int32 x, int32 y, CVector2D *out) +{ + x = x - RADAR_NUM_TILES/2; + y = -(y - RADAR_NUM_TILES/2); + + // bottom left + out[0].x = RADAR_TILE_SIZE * (x); + out[0].y = RADAR_TILE_SIZE * (y - 1); + + // bottom right + out[1].x = RADAR_TILE_SIZE * (x + 1); + out[1].y = RADAR_TILE_SIZE * (y - 1); + + // top right + out[2].x = RADAR_TILE_SIZE * (x + 1); + out[2].y = RADAR_TILE_SIZE * (y); + + // top left + out[3].x = RADAR_TILE_SIZE * (x); + out[3].y = RADAR_TILE_SIZE * (y); +} + +uint8 CRadar::CalculateBlipAlpha(float dist) +{ + if (FrontEndMenuManager.m_bMenuMapActive) + return 255; + + if (dist <= 1.0f) + return 255; + + if (dist <= 10.0f) + return (128.0f * ((dist - 1.0f) / 9.0f)) + ((1.0f - (dist - 1.0f) / 9.0f) * 255.0f); + + return 128; +} + +void CRadar::ChangeBlipBrightness(int32 i, int32 bright) +{ + int index = GetActualBlipArrayIndex(i); + if (index != -1) + ms_RadarTrace[index].m_bDim = bright != 1; +} + +void CRadar::ChangeBlipColour(int32 i, int32 color) +{ + int index = GetActualBlipArrayIndex(i); + if (index != -1) + ms_RadarTrace[index].m_nColor = color; +} + +void CRadar::ChangeBlipDisplay(int32 i, eBlipDisplay display) +{ + int index = GetActualBlipArrayIndex(i); + if (index != -1) + ms_RadarTrace[index].m_eBlipDisplay = display; +} + +void CRadar::ChangeBlipScale(int32 i, int32 scale) +{ + int index = GetActualBlipArrayIndex(i); + if (index != -1) + ms_RadarTrace[index].m_wScale = scale; +} + +void CRadar::ClearBlip(int32 i) +{ + int index = GetActualBlipArrayIndex(i); + if (index != -1) { + SetRadarMarkerState(index, false); + ms_RadarTrace[index].m_bInUse = false; + ms_RadarTrace[index].m_eBlipType = BLIP_NONE; + ms_RadarTrace[index].m_eBlipDisplay = BLIP_DISPLAY_NEITHER; + ms_RadarTrace[index].m_eRadarSprite = RADAR_SPRITE_NONE; + } +} + +void CRadar::ClearBlipForEntity(eBlipType type, int32 id) +{ + for (int i = 0; i < NUMRADARBLIPS; i++) { + if (type == ms_RadarTrace[i].m_eBlipType && id == ms_RadarTrace[i].m_nEntityHandle) { + SetRadarMarkerState(i, false); + ms_RadarTrace[i].m_bInUse = false; + ms_RadarTrace[i].m_eBlipType = BLIP_NONE; + ms_RadarTrace[i].m_eBlipDisplay = BLIP_DISPLAY_NEITHER; + ms_RadarTrace[i].m_eRadarSprite = RADAR_SPRITE_NONE; + } + }; +} + +// Why not a proper clipping algorithm? +#ifdef THIS_IS_STUPID + +bool IsPointInsideRadar(const CVector2D &point) +{ + if (point.x < -1.0f || point.x > 1.0f) return false; + if (point.y < -1.0f || point.y > 1.0f) return false; + return true; +} + +// clip line p1,p2 against (-1.0, 1.0) in x and y, set out to clipped point closest to p1 +int LineRadarBoxCollision(CVector2D &out, const CVector2D &p1, const CVector2D &p2) +{ + float d1, d2; + float t; + float x, y; + float shortest = 1.0f; + int edge = -1; + + // clip against left edge, x = -1.0 + d1 = -1.0f - p1.x; + d2 = -1.0f - p2.x; + if (d1 * d2 < 0.0f) { + // they are on opposite sides, get point of intersection + t = d1 / (d1 - d2); + y = (p2.y - p1.y)*t + p1.y; + if (y >= -1.0f && y <= 1.0f && t <= shortest) { + out.x = -1.0f; + out.y = y; + edge = 3; + shortest = t; + } + } + + // clip against right edge, x = 1.0 + d1 = p1.x - 1.0f; + d2 = p2.x - 1.0f; + if (d1 * d2 < 0.0f) { + // they are on opposite sides, get point of intersection + t = d1 / (d1 - d2); + y = (p2.y - p1.y)*t + p1.y; + if (y >= -1.0f && y <= 1.0f && t <= shortest) { + out.x = 1.0f; + out.y = y; + edge = 1; + shortest = t; + } + } + + // clip against top edge, y = -1.0 + d1 = -1.0f - p1.y; + d2 = -1.0f - p2.y; + if (d1 * d2 < 0.0f) { + // they are on opposite sides, get point of intersection + t = d1 / (d1 - d2); + x = (p2.x - p1.x)*t + p1.x; + if (x >= -1.0f && x <= 1.0f && t <= shortest) { + out.y = -1.0f; + out.x = x; + edge = 0; + shortest = t; + } + } + + // clip against bottom edge, y = 1.0 + d1 = p1.y - 1.0f; + d2 = p2.y - 1.0f; + if (d1 * d2 < 0.0f) { + // they are on opposite sides, get point of intersection + t = d1 / (d1 - d2); + x = (p2.x - p1.x)*t + p1.x; + if (x >= -1.0f && x <= 1.0f && t <= shortest) { + out.y = 1.0f; + out.x = x; + edge = 2; + shortest = t; + } + } + + return edge; +} + +int CRadar::ClipRadarPoly(CVector2D *poly, const CVector2D *rect) +{ + CVector2D corners[4] = { + CVector2D( 1.0f, -1.0f ), // top right + CVector2D( 1.0f, 1.0f ), // bottom right + CVector2D( -1.0f, 1.0f ), // bottom left + CVector2D( -1.0f, -1.0f ), // top left + }; + CVector2D tmp; + int i, j, n; + int laste, e, e1, e2;; + bool inside[4]; + + for (i = 0; i < 4; i++) + inside[i] = IsPointInsideRadar(rect[i]); + + laste = -1; + n = 0; + for (i = 0; i < 4; i++) + if (inside[i]) { + // point is inside, just add + poly[n++] = rect[i]; + } + else { + // point is outside but line to this point might be clipped + e1 = LineRadarBoxCollision(poly[n], rect[i], rect[(i + 4 - 1) % 4]); + if (e1 != -1) { + laste = e1; + n++; + } + // and line from this point might be clipped as well + e2 = LineRadarBoxCollision(poly[n], rect[i], rect[(i + 1) % 4]); + if (e2 != -1) { + if (e1 == -1) { + // if other line wasn't clipped, i.e. it was complete outside, + // we may have to insert another vertex if last clipped line + // was on a different edge + + // find the last intersection if we haven't seen it yet + if (laste == -1) + for (j = 3; j >= i; j--) { + // game uses an if here for j == 0 + e = LineRadarBoxCollision(tmp, rect[j], rect[(j + 4 - 1) % 4]); + if (e != -1) { + laste = e; + break; + } + } + assert(laste != -1); + + // insert corners that were skipped + tmp = poly[n]; + for (e = laste; e != e2; e = (e + 1) % 4) + poly[n++] = corners[e]; + poly[n] = tmp; + } + n++; + } + } + + if (n == 0) { + // If no points, either the rectangle is completely outside or completely surrounds the radar + // no idea what's going on here... + float m = (rect[0].y - rect[1].y) / (rect[0].x - rect[1].x); + if ((m*rect[3].x - rect[3].y) * (m*rect[0].x - rect[0].y) < 0.0f) { + m = (rect[0].y - rect[3].y) / (rect[0].x - rect[3].x); + if ((m*rect[1].x - rect[1].y) * (m*rect[0].x - rect[0].y) < 0.0f) { + poly[0] = corners[0]; + poly[1] = corners[1]; + poly[2] = corners[2]; + poly[3] = corners[3]; + n = 4; + } + } + } + + return n; +} +#else + +int +ClipPolyPlane(const CVector2D *in, int nin, CVector2D *out, CVector *plane) +{ + int j; + int nout; + int x1, x2; + float d1, d2, t; + + nout = 0; + for(j = 0; j < nin; j++){ + x1 = j; + x2 = (j+1) % nin; + + d1 = plane->x*in[x1].x + plane->y*in[x1].y + plane->z; + d2 = plane->x*in[x2].x + plane->y*in[x2].y + plane->z; + if(d1*d2 < 0.0f){ + t = d1/(d1 - d2); + out[nout++] = in[x1]*(1.0f-t) + in[x2]*t; + } + if(d2 >= 0.0f) + out[nout++] = in[x2]; + } + return nout; +} + +int CRadar::ClipRadarPoly(CVector2D *poly, const CVector2D *rect) +{ + CVector planes[4] = { + CVector(-1.0f, 0.0f, 1.0f), + CVector( 1.0f, 0.0f, 1.0f), + CVector(0.0f, -1.0f, 1.0f), + CVector(0.0f, 1.0f, 1.0f) + }; + CVector2D tmp[8]; + int n; + if(n = ClipPolyPlane(rect, 4, tmp, &planes[0]), n == 0) return 0; + if(n = ClipPolyPlane(tmp, n, poly, &planes[1]), n == 0) return 0; + if(n = ClipPolyPlane(poly, n, tmp, &planes[2]), n == 0) return 0; + if(n = ClipPolyPlane(tmp, n, poly, &planes[3]), n == 0) return 0; + return n; +} +#endif + +bool CRadar::DisplayThisBlip(int32 counter) +{ + switch (ms_RadarTrace[counter].m_eRadarSprite) { + case RADAR_SPRITE_SPRAY: + case RADAR_SPRITE_GUN: + return true; + default: + return false; + } +} + +void CRadar::Draw3dMarkers() +{ + for (int i = 0; i < NUMRADARBLIPS; i++) { + if (ms_RadarTrace[i].m_bInUse) { + switch (ms_RadarTrace[i].m_eBlipType) { + case BLIP_CAR: + { + CEntity *entity = CPools::GetVehiclePool()->GetAt(ms_RadarTrace[i].m_nEntityHandle); + if (ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_MARKER_ONLY) { + CVector pos = entity->GetPosition(); + pos.z += 1.2f * CModelInfo::GetColModel(entity->GetModelIndex())->boundingBox.max.z + 2.5f; + C3dMarkers::PlaceMarker(i | (ms_RadarTrace[i].m_BlipIndex << 16), MARKERTYPE_ARROW, pos, 2.5f, CARBLIP_MARKER_COLOR_R, CARBLIP_MARKER_COLOR_G, CARBLIP_MARKER_COLOR_B, CARBLIP_MARKER_COLOR_A, 1024, 0.2f, 5); + } + break; + } + case BLIP_CHAR: + { + CEntity *entity = CPools::GetPedPool()->GetAt(ms_RadarTrace[i].m_nEntityHandle); + if (entity != nil) { + if (((CPed*)entity)->InVehicle()) + entity = ((CPed * )entity)->m_pMyVehicle; + } + if (ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_MARKER_ONLY) { + CVector pos = entity->GetPosition(); + pos.z += 3.0f; + C3dMarkers::PlaceMarker(i | (ms_RadarTrace[i].m_BlipIndex << 16), MARKERTYPE_ARROW, pos, 1.5f, CHARBLIP_MARKER_COLOR_R, CHARBLIP_MARKER_COLOR_G, CHARBLIP_MARKER_COLOR_B, CHARBLIP_MARKER_COLOR_A, 1024, 0.2f, 5); + } + break; + } + case BLIP_OBJECT: + { + CEntity *entity = CPools::GetObjectPool()->GetAt(ms_RadarTrace[i].m_nEntityHandle); + if (ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_MARKER_ONLY) { + CVector pos = entity->GetPosition(); + pos.z += CModelInfo::GetColModel(entity->GetModelIndex())->boundingBox.max.z + 1.0f + 1.0f; + C3dMarkers::PlaceMarker(i | (ms_RadarTrace[i].m_BlipIndex << 16), MARKERTYPE_ARROW, pos, 1.0f, OBJECTBLIP_MARKER_COLOR_R, OBJECTBLIP_MARKER_COLOR_G, OBJECTBLIP_MARKER_COLOR_B, OBJECTBLIP_MARKER_COLOR_A, 1024, 0.2f, 5); + } + break; + } + case BLIP_COORD: + break; + case BLIP_CONTACT_POINT: + if (!CTheScripts::IsPlayerOnAMission()) { + if (ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_MARKER_ONLY) + C3dMarkers::PlaceMarkerSet(i | (ms_RadarTrace[i].m_BlipIndex << 16), MARKERTYPE_CYLINDER, ms_RadarTrace[i].m_vecPos, 2.0f, COORDBLIP_MARKER_COLOR_R, COORDBLIP_MARKER_COLOR_G, COORDBLIP_MARKER_COLOR_B, COORDBLIP_MARKER_COLOR_A, 2048, 0.2f, 0); + } + break; + } + } + } +} + +void CRadar::DrawBlips() +{ + if (!TheCamera.m_WideScreenOn && CHud::m_Wants_To_Draw_Hud) { +#ifdef SECUROM + extern uint8 roadBlocksPirateCheck; + if (roadBlocksPirateCheck == 2) return; +#endif + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); + + CVector2D out; + CVector2D in = CVector2D(0.0f, 0.0f); + TransformRadarPointToScreenSpace(out, in); + + if (!FrontEndMenuManager.m_bMenuMapActive) { + float angle; + if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN) + angle = PI + FindPlayerHeading(); +#ifdef FIX_BUGS + else if (TheCamera.GetLookDirection() != LOOKING_FORWARD) + angle = FindPlayerHeading() - (PI + (TheCamera.Cams[TheCamera.ActiveCam].CamTargetEntity->GetPosition() - TheCamera.Cams[TheCamera.ActiveCam].SourceBeforeLookBehind).Heading()); +#endif + else + angle = FindPlayerHeading() - (PI + TheCamera.GetForward().Heading()); + + DrawRotatingRadarSprite(&CentreSprite, out.x, out.y, angle, 255); + + CVector2D vec2d; + vec2d.x = vec2DRadarOrigin.x; + vec2d.y = M_SQRT2 * m_radarRange + vec2DRadarOrigin.y; + TransformRealWorldPointToRadarSpace(in, vec2d); + LimitRadarPoint(in); + TransformRadarPointToScreenSpace(out, in); + DrawRadarSprite(RADAR_SPRITE_NORTH, out.x, out.y, 255); + } + + for(int blipId = 0; blipId < NUMRADARBLIPS; blipId++) { + if (!ms_RadarTrace[blipId].m_bInUse) + continue; + + switch (ms_RadarTrace[blipId].m_eBlipType) { + case BLIP_CAR: + case BLIP_CHAR: + case BLIP_OBJECT: + if (ms_RadarTrace[blipId].m_eRadarSprite == RADAR_SPRITE_PROPERTY + && (!CTheScripts::bPlayerIsInTheStatium || !FrontEndMenuManager.m_bMenuMapActive)) + DrawEntityBlip(blipId); + + break; + case BLIP_COORD: + case BLIP_CONTACT_POINT: + if (ms_RadarTrace[blipId].m_eRadarSprite == RADAR_SPRITE_PHONE + && (!CTheScripts::bPlayerIsInTheStatium || !FrontEndMenuManager.m_bMenuMapActive)) + DrawCoordBlip(blipId); + + break; + default: + break; + } + } + + // New in VC: Always draw Hardware/gun/pay'n spray/save blips + for(int blipId = 0; blipId < NUMRADARBLIPS; blipId++) { + if (!ms_RadarTrace[blipId].m_bInUse) + continue; + + if (ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_SAVE && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_HARDWARE + && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_SPRAY && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_GUN) + continue; + + switch (ms_RadarTrace[blipId].m_eBlipType) { + case BLIP_CAR: + case BLIP_CHAR: + case BLIP_OBJECT: + if (!CTheScripts::bPlayerIsInTheStatium || !FrontEndMenuManager.m_bMenuMapActive) + DrawEntityBlip(blipId); + + break; + case BLIP_COORD: + case BLIP_CONTACT_POINT: + if (!CTheScripts::bPlayerIsInTheStatium || !FrontEndMenuManager.m_bMenuMapActive) + DrawCoordBlip(blipId); + + break; + default: + break; + } + } + + for(int blipId = 0; blipId < NUMRADARBLIPS; blipId++) { + if (!ms_RadarTrace[blipId].m_bInUse) + continue; + + switch (ms_RadarTrace[blipId].m_eBlipType) { + case BLIP_CAR: + case BLIP_CHAR: + case BLIP_OBJECT: + if (ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_SAVE && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_HARDWARE + && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_SPRAY && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_PROPERTY + && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_GUN + && (!CTheScripts::bPlayerIsInTheStatium || !FrontEndMenuManager.m_bMenuMapActive)) + + DrawEntityBlip(blipId); + break; + default: + break; + } + } + for (int blipId = 0; blipId < NUMRADARBLIPS; blipId++) { + if (!ms_RadarTrace[blipId].m_bInUse) + continue; + + switch (ms_RadarTrace[blipId].m_eBlipType) { + case BLIP_COORD: + case BLIP_CONTACT_POINT: + if (ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_SAVE && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_HARDWARE + && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_SPRAY && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_PROPERTY + && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_GUN && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_PHONE + && (!CTheScripts::bPlayerIsInTheStatium || !FrontEndMenuManager.m_bMenuMapActive)) + + DrawCoordBlip(blipId); + break; + default: + break; + } + } + if (FrontEndMenuManager.m_bMenuMapActive) { + CVector2D in, out; + if (!CTheScripts::bPlayerIsInTheStatium) + TransformRealWorldPointToRadarSpace(in, FindPlayerCentreOfWorld_NoSniperShift()); + else + TransformRealWorldPointToRadarSpace(in, CVector2D(-1302.5f, 1332.8f)); + + LimitRadarPoint(in); + TransformRadarPointToScreenSpace(out, in); + DrawYouAreHereSprite(out.x, out.y); + } + } +} + +void CRadar::DrawMap() +{ + if (!TheCamera.m_WideScreenOn && CHud::m_Wants_To_Draw_Hud) { + CalculateCachedSinCos(); + if (FindPlayerVehicle()) { + float speed = FindPlayerSpeed().Magnitude(); + if (speed < RADAR_MIN_SPEED) + m_radarRange = RADAR_MIN_RANGE; + else if (speed < RADAR_MAX_SPEED) + m_radarRange = (speed - RADAR_MIN_SPEED)/(RADAR_MAX_SPEED-RADAR_MIN_SPEED) * (RADAR_MAX_RANGE-RADAR_MIN_RANGE) + RADAR_MIN_RANGE; + else + m_radarRange = RADAR_MAX_RANGE; + } + else + m_radarRange = RADAR_MIN_RANGE; + + vec2DRadarOrigin = CVector2D(FindPlayerCentreOfWorld_NoSniperShift()); + if (FrontEndMenuManager.m_PrefsRadarMode != 1) + DrawRadarMap(); + } +} + +void CRadar::DrawRadarMap() +{ + DrawRadarMask(); + + // top left ist (0, 0) + int x = Floor((vec2DRadarOrigin.x - RADAR_MIN_X) / RADAR_TILE_SIZE); + int y = Ceil((RADAR_NUM_TILES - 1) - (vec2DRadarOrigin.y - RADAR_MIN_Y) / RADAR_TILE_SIZE); + StreamRadarSections(x, y); + + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); + RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEFLAT); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP); + RwRenderStateSet(rwRENDERSTATETEXTUREPERSPECTIVE, (void*)FALSE); + + DrawRadarSection(x - 1, y - 1); + DrawRadarSection(x, y - 1); + DrawRadarSection(x + 1, y - 1); + DrawRadarSection(x - 1, y); + DrawRadarSection(x, y); + DrawRadarSection(x + 1, y); + DrawRadarSection(x - 1, y + 1); + DrawRadarSection(x, y + 1); + DrawRadarSection(x + 1, y + 1); +} + +void CRadar::DrawRadarMask() +{ + CVector2D corners[4] = { + CVector2D(1.0f, -1.0f), + CVector2D(1.0f, 1.0f), + CVector2D(-1.0f, 1.0f), + CVector2D(-1.0, -1.0f) + }; + + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void*)nil); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); + RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEFLAT); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); +#if !defined(GTA_PS2_STUFF) && defined(RWLIBS) + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_ALWAYS); +#else + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDZERO); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); +#endif + + CVector2D out[8]; + CVector2D in; + + // First draw with near Z to clear nearer Z from any 3d objects + for (int i = 0; i < 4; i++) { + in.x = corners[i].x; + in.y = corners[i].y; + TransformRadarPointToScreenSpace(out[i], in); + } + CSprite2d::SetMaskVertices(4, (float *)out); + + // avoid Z fighting with the map by nudging the clear area bellow the map depth + { + auto vtx = CSprite2d::GetVertices(); + for (int i = 0; i < 4; i++) { + vtx[i].w *= 2; + } + } + RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::GetVertices(), 4); + + // Draw the shape we want to mask out from the radar in four segments + for (int i = 0; i < 4; i++) { + // First point is always the corner itself + in.x = corners[i].x; + in.y = corners[i].y; + TransformRadarPointToScreenSpace(out[0], in); + + // Then generate a quarter of the circle + for (int j = 0; j < 7; j++) { + in.x = corners[i].x * Cos(j * (PI / 2.0f / 6.0f)); + in.y = corners[i].y * Sin(j * (PI / 2.0f / 6.0f)); + TransformRadarPointToScreenSpace(out[j + 1], in); + }; + + CSprite2d::SetMaskVertices(8, (float *)out); + // Make the mask depth slightly above the map depth + // Not sure how this worked in the original code tbh + auto vtx = CSprite2d::GetVertices(); + for (int j = 0; j < 8; j++) { + vtx[j].w /= 5; + } + + RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::GetVertices(), 8); + } +#if !defined(GTA_PS2_STUFF) && defined(RWLIBS) + RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER); +#endif + +} + +void CRadar::DrawRadarSection(int32 x, int32 y) +{ + int i; + RwTexDictionary *txd; + CVector2D worldPoly[8]; + CVector2D radarCorners[4]; + CVector2D radarPoly[8]; + CVector2D texCoords[8]; + CVector2D screenPoly[8]; + int numVertices; + RwTexture *texture = nil; + + GetTextureCorners(x, y, worldPoly); + ClipRadarTileCoords(x, y); + + if (!CTheScripts::bPlayerIsInTheStatium && + (!(txd = CTxdStore::GetSlot(gRadarTxdIds[x + RADAR_NUM_TILES * y])->texDict) || !(texture = GetFirstTexture(txd)))) + return; + + for (i = 0; i < 4; i++) + TransformRealWorldPointToRadarSpace(radarCorners[i], worldPoly[i]); + + numVertices = ClipRadarPoly(radarPoly, radarCorners); + + // FIX: can return earlier here +// if(numVertices == 0) + if (numVertices < 3) + return; + + for (i = 0; i < numVertices; i++) { + TransformRadarPointToRealWorldSpace(worldPoly[i], radarPoly[i]); + TransformRealWorldToTexCoordSpace(texCoords[i], worldPoly[i], x, y); + TransformRadarPointToScreenSpace(screenPoly[i], radarPoly[i]); + } + + if (CTheScripts::bPlayerIsInTheStatium) { + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + CSprite2d::SetVertices(numVertices, (float*)screenPoly, (float*)texCoords, CRGBA(204, 204, 204, 255)); + } else { + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(texture)); + CSprite2d::SetVertices(numVertices, (float*)screenPoly, (float*)texCoords, CRGBA(255, 255, 255, 255)); + } + + // check done above now +// if(numVertices > 2) + RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::GetVertices(), numVertices); +} + +void CRadar::DrawRadarSprite(uint16 sprite, float x, float y, uint8 alpha) +{ +#ifdef MAP_ENHANCEMENTS + if(sprite == RADAR_SPRITE_WAYPOINT) alpha = 255; +#endif + RadarSprites[sprite]->Draw(CRect(x - SCREEN_SCALE_X(8.0f), y - SCREEN_SCALE_Y(8.0f), x + SCREEN_SCALE_X(8.0f), y + SCREEN_SCALE_Y(8.0f)), CRGBA(255, 255, 255, alpha)); + + if (FrontEndMenuManager.m_bMenuMapActive) { + bool alreadyThere = false; + for (int i = 0; i < NUM_MAP_LEGENDS; i++) { + if (MapLegendList[i] == sprite) + alreadyThere = true; + } + if (!alreadyThere) { + MapLegendList[MapLegendCounter] = sprite; + MapLegendCounter++; + } + } +} + +void CRadar::DrawRotatingRadarSprite(CSprite2d* sprite, float x, float y, float angle, int32 alpha) +{ + CVector curPosn[4]; + const float sizeX = SCREEN_SCALE_X(8.0f); + const float correctedAngle = angle - PI / 4.f; + const float sizeY = SCREEN_SCALE_Y(8.0f); + + for (uint32 i = 0; i < 4; i++) { + const float cornerAngle = i * HALFPI + correctedAngle; + curPosn[i].x = x + (0.0f * Cos(cornerAngle) + 1.0f * Sin(cornerAngle)) * sizeX; + curPosn[i].y = y - (0.0f * Sin(cornerAngle) - 1.0f * Cos(cornerAngle)) * sizeY; + } + + sprite->Draw(curPosn[3].x, curPosn[3].y, curPosn[2].x, curPosn[2].y, curPosn[0].x, curPosn[0].y, curPosn[1].x, curPosn[1].y, CRGBA(255, 255, 255, alpha)); +} + +int32 CRadar::GetActualBlipArrayIndex(int32 i) +{ + if (i == -1) + return -1; + else if ((i & 0xFFFF0000) >> 16 != ms_RadarTrace[(uint16)i].m_BlipIndex) + return -1; + else + return (uint16)i; +} + +int32 CRadar::GetNewUniqueBlipIndex(int32 i) +{ + if (ms_RadarTrace[i].m_BlipIndex >= UINT16_MAX - 1) + ms_RadarTrace[i].m_BlipIndex = 1; + else + ms_RadarTrace[i].m_BlipIndex++; + return i | (ms_RadarTrace[i].m_BlipIndex << 16); +} + +uint32 CRadar::GetRadarTraceColour(uint32 color, bool bright) +{ + uint32 c; + switch (color) { + case RADAR_TRACE_RED: + if (bright) + c = 0x712B49FF; + else + c = 0x7F0000FF; + break; + case RADAR_TRACE_GREEN: + if (bright) + c = 0x5FA06AFF; + else + c = 0x007F00FF; + break; + case RADAR_TRACE_LIGHT_BLUE: + if (bright) + c = 0x80A7F3FF; + else + c = 0x00007FFF; + break; + case RADAR_TRACE_GRAY: + if (bright) + c = 0xE1E1E1FF; + else + c = 0x7F7F7FFF; + break; + case RADAR_TRACE_YELLOW: + if (bright) + c = 0xFFFF00FF; + else + c = 0x7F7F00FF; + break; + case RADAR_TRACE_MAGENTA: + if (bright) + c = 0xFF00FFFF; + else + c = 0x7F007FFF; + break; + case RADAR_TRACE_CYAN: + if (bright) + c = 0x00FFFFFF; + else + c = 0x007F7FFF; + break; + default: + c = color; + break; + }; + return c; +} + +const char* gRadarTexNames[] = { + "radar00", "radar01", "radar02", "radar03", "radar04", "radar05", "radar06", "radar07", + "radar08", "radar09", "radar10", "radar11", "radar12", "radar13", "radar14", "radar15", + "radar16", "radar17", "radar18", "radar19", "radar20", "radar21", "radar22", "radar23", + "radar24", "radar25", "radar26", "radar27", "radar28", "radar29", "radar30", "radar31", + "radar32", "radar33", "radar34", "radar35", "radar36", "radar37", "radar38", "radar39", + "radar40", "radar41", "radar42", "radar43", "radar44", "radar45", "radar46", "radar47", + "radar48", "radar49", "radar50", "radar51", "radar52", "radar53", "radar54", "radar55", + "radar56", "radar57", "radar58", "radar59", "radar60", "radar61", "radar62", "radar63", +}; + +void +CRadar::Initialise() +{ +#ifdef MAP_ENHANCEMENTS + TargetMarkerId = -1; +#endif + for (int i = 0; i < NUMRADARBLIPS; i++) { + ms_RadarTrace[i].m_BlipIndex = 1; + SetRadarMarkerState(i, false); + ms_RadarTrace[i].m_bInUse = false; + ms_RadarTrace[i].m_bShortRange = false; + ms_RadarTrace[i].m_eBlipType = BLIP_NONE; + ms_RadarTrace[i].m_eBlipDisplay = BLIP_DISPLAY_NEITHER; + ms_RadarTrace[i].m_eRadarSprite = RADAR_SPRITE_NONE; + } + + m_radarRange = 350.0f; + for (int i = 0; i < 64; i++) + gRadarTxdIds[i] = CTxdStore::FindTxdSlot(gRadarTexNames[i]); +} + +float CRadar::LimitRadarPoint(CVector2D &point) +{ + float dist, invdist; + + dist = point.Magnitude(); + + if (FrontEndMenuManager.m_bMenuMapActive) + return dist; + + if (dist > 1.0f) { + invdist = 1.0f / dist; + point.x *= invdist; + point.y *= invdist; + } + return dist; +} + +void CRadar::LoadAllRadarBlips(uint8 *buf, uint32 size) +{ + Initialise(); +INITSAVEBUF + CheckSaveHeader(buf, 'R', 'D', 'R', '\0', size - SAVE_HEADER_SIZE); + + for (int i = 0; i < NUMRADARBLIPS; i++) { + ReadSaveBuf(&ms_RadarTrace[i].m_nColor, buf); + ReadSaveBuf(&ms_RadarTrace[i].m_Radius, buf); + ReadSaveBuf(&ms_RadarTrace[i].m_eBlipType, buf); + ReadSaveBuf(&ms_RadarTrace[i].m_nEntityHandle, buf); + ReadSaveBuf(&ms_RadarTrace[i].m_vec2DPos.x, buf); + ReadSaveBuf(&ms_RadarTrace[i].m_vec2DPos.y, buf); + ReadSaveBuf(&ms_RadarTrace[i].m_vecPos, buf); + ReadSaveBuf(&ms_RadarTrace[i].m_BlipIndex, buf); + ReadSaveBuf(&ms_RadarTrace[i].m_bDim, buf); + ReadSaveBuf(&ms_RadarTrace[i].m_bInUse, buf); + ReadSaveBuf(&ms_RadarTrace[i].m_bShortRange, buf); + ReadSaveBuf(&ms_RadarTrace[i].m_unused, buf); + ReadSaveBuf(&ms_RadarTrace[i].m_wScale, buf); + ReadSaveBuf(&ms_RadarTrace[i].m_eBlipDisplay, buf); + ReadSaveBuf(&ms_RadarTrace[i].m_eRadarSprite, buf); + } + +VALIDATESAVEBUF(size); +} + +void CRadar::SaveAllRadarBlips(uint8 *buf, uint32 *size) +{ + *size = SAVE_HEADER_SIZE + NUMRADARBLIPS * sizeof(sRadarTraceSave); + +INITSAVEBUF + WriteSaveHeader(buf, 'R', 'D', 'R', '\0', *size - SAVE_HEADER_SIZE); + +#ifdef MAP_ENHANCEMENTS + bool bWaypointDeleted = false; + if (TargetMarkerId != -1) { + ClearBlip(TargetMarkerId); + TargetMarkerId = -1; + bWaypointDeleted = true; + } +#endif + + for (int i = 0; i < NUMRADARBLIPS; i++) { + sRadarTraceSave *saveStruct = (sRadarTraceSave*) buf; + + saveStruct->m_nColor = ms_RadarTrace[i].m_nColor; + saveStruct->m_Radius = ms_RadarTrace[i].m_Radius; + saveStruct->m_eBlipType = ms_RadarTrace[i].m_eBlipType; + saveStruct->m_nEntityHandle = ms_RadarTrace[i].m_nEntityHandle; + saveStruct->m_vec2DPos = ms_RadarTrace[i].m_vec2DPos; + saveStruct->m_vecPos = ms_RadarTrace[i].m_vecPos; + saveStruct->m_BlipIndex = ms_RadarTrace[i].m_BlipIndex; + saveStruct->m_bDim = ms_RadarTrace[i].m_bDim; + saveStruct->m_bInUse = ms_RadarTrace[i].m_bInUse; + saveStruct->m_bShortRange = ms_RadarTrace[i].m_bShortRange; + saveStruct->m_unused = ms_RadarTrace[i].m_unused; + saveStruct->m_wScale = ms_RadarTrace[i].m_wScale; + saveStruct->m_eBlipDisplay = ms_RadarTrace[i].m_eBlipDisplay; + saveStruct->m_eRadarSprite = ms_RadarTrace[i].m_eRadarSprite; + + SkipSaveBuf(buf, sizeof(sRadarTraceSave)); + } + +#ifdef MAP_ENHANCEMENTS + if(bWaypointDeleted) + ToggleTargetMarker(TargetMarkerPos.x, TargetMarkerPos.y); +#endif + +VALIDATESAVEBUF(*size); +} + +void +CRadar::LoadTextures() +{ + CTxdStore::PushCurrentTxd(); + CTxdStore::SetCurrentTxd(CTxdStore::FindTxdSlot("hud")); + CentreSprite.SetTexture("radar_centre"); + MapHereSprite.SetTexture("arrow"); + NorthSprite.SetTexture("radar_north"); + AverySprite.SetTexture("radar_avery"); + BikerSprite.SetTexture("radar_biker"); + CortezSprite.SetTexture("radar_cortez"); + DiazSprite.SetTexture("radar_diaz"); + KentSprite.SetTexture("radar_kent"); + LawyerSprite.SetTexture("radar_lawyer"); + PhilSprite.SetTexture("radar_phil"); + BikersSprite.SetTexture("bikers"); + BoatyardSprite.SetTexture("boatyard"); + MalibuClubSprite.SetTexture("club"); + CubansSprite.SetTexture("cubans"); + FilmSprite.SetTexture("filmstudio"); + GunSprite.SetTexture("gun"); + HaitiansSprite.SetTexture("haitians"); + HardwareSprite.SetTexture("hardware"); + SaveHouseSprite.SetTexture("radar_save"); + StripSprite.SetTexture("radar_strip"); + IceSprite.SetTexture("icecream"); + KCabsSprite.SetTexture("kcabs"); + LovefistSprite.SetTexture("lovefist"); + PrintworksSprite.SetTexture("printworks"); + PropertySprite.SetTexture("property"); + SunYardSprite.SetTexture("SunYard"); + SpraySprite.SetTexture("spray"); + TShirtSprite.SetTexture("tshirt"); + TommySprite.SetTexture("tommy"); + PhoneSprite.SetTexture("phone"); + RadioWildstyleSprite.SetTexture("RWildstyle"); + RadioFlashSprite.SetTexture("RFlash"); + RadioKChatSprite.SetTexture("RKchat"); + RadioFeverSprite.SetTexture("RFever"); + RadioVRockSprite.SetTexture("RVRock"); + RadioVCPRSprite.SetTexture("RVCPR"); + RadioEspantosoSprite.SetTexture("REspantoso"); + RadioEmotionSprite.SetTexture("REmotion"); + RadioWaveSprite.SetTexture("RWave"); +#ifdef MAP_ENHANCEMENTS + WaypointSprite.SetTexture("radar_waypoint"); + if(!WaypointSprite.m_pTexture) { + // create the texture if it's missing in TXD +#define WAYPOINT_R (255>>4) +#define WAYPOINT_G (72>>4) +#define WAYPOINT_B (77>>4) + + RwRaster *raster = RwRasterCreate(16, 16, 16, rwRASTERTYPETEXTURE | rwRASTERFORMAT4444); + + RwUInt16 *pixels = (RwUInt16 *)RwRasterLock(raster, 0, rwRASTERLOCKWRITE); + for(int x = 0; x < 16; x++) + for(int y = 0; y < 16; y++) + { + int x2 = x < 8 ? x : 7 - (x & 7); + int y2 = y < 8 ? y : 7 - (y & 7); + if ((y2 >= 4 && x2 >= 4) // square in the center is transparent + || (x2 < 2 && y2 == 0) // two pixels on each side of first/last line are transparent + || (x2 < 1 && y2 == 1)) // one pixel on each side of second to first/last line is transparent + pixels[x + y * 16] = 0; + else if((x2 == 2 && y2 >= 2)|| (y2 == 2 && x2 >= 2) )// colored square inside + pixels[x + y * 16] = WAYPOINT_B | (WAYPOINT_G << 4) | (WAYPOINT_R << 8) | (15 << 12); + else + pixels[x + y * 16] = 0xF000; // black + } + RwRasterUnlock(raster); + WaypointSprite.m_pTexture = RwTextureCreate(raster); + RwTextureSetFilterMode(WaypointSprite.m_pTexture, rwFILTERLINEAR); +#undef WAYPOINT_R +#undef WAYPOINT_G +#undef WAYPOINT_B + } +#endif + CTxdStore::PopCurrentTxd(); +} + +void CRadar::RemoveRadarSections() +{ + for (int i = 0; i < 8; i++) + for (int j = 0; j < 8; j++) + RemoveMapSection(i, j); +} + +void CRadar::SetBlipSprite(int32 i, int32 icon) +{ + int index = CRadar::GetActualBlipArrayIndex(i); + if (index != -1) { + ms_RadarTrace[index].m_eRadarSprite = icon; + } +} + +int32 CRadar::SetCoordBlip(eBlipType type, CVector pos, uint32 color, eBlipDisplay display) +{ + int nextBlip; + for (nextBlip = 0; nextBlip < NUMRADARBLIPS; nextBlip++) { + if (!ms_RadarTrace[nextBlip].m_bInUse) + break; + } +#ifdef FIX_BUGS + if (nextBlip == NUMRADARBLIPS) + return -1; +#endif + ms_RadarTrace[nextBlip].m_eBlipType = type; + ms_RadarTrace[nextBlip].m_nColor = RADAR_TRACE_MAGENTA; + ms_RadarTrace[nextBlip].m_bDim = true; + ms_RadarTrace[nextBlip].m_bInUse = true; + ms_RadarTrace[nextBlip].m_bShortRange = false; + ms_RadarTrace[nextBlip].m_Radius = 1.0f; + ms_RadarTrace[nextBlip].m_vec2DPos = pos; + ms_RadarTrace[nextBlip].m_vecPos = pos; + ms_RadarTrace[nextBlip].m_nEntityHandle = 0; + ms_RadarTrace[nextBlip].m_wScale = 1; + ms_RadarTrace[nextBlip].m_eBlipDisplay = display; + ms_RadarTrace[nextBlip].m_eRadarSprite = RADAR_SPRITE_NONE; + return CRadar::GetNewUniqueBlipIndex(nextBlip); +} + +int32 CRadar::SetShortRangeCoordBlip(eBlipType type, CVector pos, uint32 color, eBlipDisplay display) +{ + int index = SetCoordBlip(type, pos, color, display); + if (index == -1) + return -1; + ms_RadarTrace[GetActualBlipArrayIndex(index)].m_bShortRange = true; + return index; +} + +int32 CRadar::SetEntityBlip(eBlipType type, int32 handle, uint32 color, eBlipDisplay display) +{ + int nextBlip; + for (nextBlip = 0; nextBlip < NUMRADARBLIPS; nextBlip++) { + if (!ms_RadarTrace[nextBlip].m_bInUse) + break; + } +#ifdef FIX_BUGS + if (nextBlip == NUMRADARBLIPS) + return -1; +#endif + ms_RadarTrace[nextBlip].m_eBlipType = type; + ms_RadarTrace[nextBlip].m_nColor = RADAR_TRACE_YELLOW; + ms_RadarTrace[nextBlip].m_bDim = true; + ms_RadarTrace[nextBlip].m_bInUse = true; + ms_RadarTrace[nextBlip].m_bShortRange = false; + ms_RadarTrace[nextBlip].m_Radius = 1.0f; + ms_RadarTrace[nextBlip].m_nEntityHandle = handle; + ms_RadarTrace[nextBlip].m_wScale = 1; + ms_RadarTrace[nextBlip].m_eBlipDisplay = display; + ms_RadarTrace[nextBlip].m_eRadarSprite = RADAR_SPRITE_NONE; + return GetNewUniqueBlipIndex(nextBlip); +} + +void CRadar::SetRadarMarkerState(int32 counter, bool flag) +{ + CEntity *e; + switch (ms_RadarTrace[counter].m_eBlipType) { + case BLIP_CAR: + e = CPools::GetVehiclePool()->GetAt(ms_RadarTrace[counter].m_nEntityHandle); + break; + case BLIP_CHAR: + e = CPools::GetPedPool()->GetAt(ms_RadarTrace[counter].m_nEntityHandle); + break; + case BLIP_OBJECT: + e = CPools::GetObjectPool()->GetAt(ms_RadarTrace[counter].m_nEntityHandle); + break; + default: + return; + } + + if (e) + e->bHasBlip = flag; +} + +void CRadar::ShowRadarMarker(CVector pos, uint32 color, float radius) { + float f1 = radius * 1.4f; + float f2 = radius * 0.5f; + CVector p1, p2; + + p1 = pos + TheCamera.GetUp()*f1; + p2 = pos + TheCamera.GetUp()*f2; + CTheScripts::ScriptDebugLine3D(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, color, color); + + p1 = pos - TheCamera.GetUp()*f1; + p2 = pos - TheCamera.GetUp()*f2; + CTheScripts::ScriptDebugLine3D(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, color, color); + + p1 = pos + TheCamera.GetRight()*f1; + p2 = pos + TheCamera.GetRight()*f2; + CTheScripts::ScriptDebugLine3D(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, color, color); + + p1 = pos - TheCamera.GetRight()*f1; + p2 = pos - TheCamera.GetRight()*f2; + CTheScripts::ScriptDebugLine3D(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, color, color); +} + +void CRadar::ShowRadarTrace(float x, float y, uint32 size, uint8 red, uint8 green, uint8 blue, uint8 alpha) +{ + if (!CHud::m_Wants_To_Draw_Hud || TheCamera.m_WideScreenOn) + return; + + CSprite2d::DrawRect(CRect(x - SCREEN_SCALE_X(size + 1.0f), y - SCREEN_SCALE_Y(size + 1.0f), SCREEN_SCALE_X(size + 1.0f) + x, SCREEN_SCALE_Y(size + 1.0f) + y), CRGBA(0, 0, 0, alpha)); + CSprite2d::DrawRect(CRect(x - SCREEN_SCALE_X(size), y - SCREEN_SCALE_Y(size), SCREEN_SCALE_X(size) + x, SCREEN_SCALE_Y(size) + y), CRGBA(red, green, blue, alpha)); +} + +void CRadar::ShowRadarTraceWithHeight(float x, float y, uint32 size, uint8 red, uint8 green, uint8 blue, uint8 alpha, uint8 mode) +{ + if (!CHud::m_Wants_To_Draw_Hud || TheCamera.m_WideScreenOn) + return; + + switch (mode) + { + case BLIP_MODE_TRIANGULAR_UP: + size++; + CSprite2d::Draw2DPolygon(x + SCREEN_SCALE_X(size + 3.0f), y + SCREEN_SCALE_Y(size + 2.0f), x - (SCREEN_SCALE_X(size + 3.0f)), y + SCREEN_SCALE_Y(size + 2.0f), x, y - (SCREEN_SCALE_Y(size + 3.0f)), x, y - (SCREEN_SCALE_Y(size + 3.0f)), CRGBA(0, 0, 0, alpha)); + CSprite2d::Draw2DPolygon(x + SCREEN_SCALE_X(size + 1.0f), y + SCREEN_SCALE_Y(size + 1.0f), x - (SCREEN_SCALE_X(size + 1.0f)), y + SCREEN_SCALE_Y(size + 1.0f), x, y - (SCREEN_SCALE_Y(size + 1.0f)), x, y - (SCREEN_SCALE_Y(size + 1.0f)), CRGBA(red, green, blue, alpha)); + break; + case BLIP_MODE_TRIANGULAR_DOWN: + size++; + CSprite2d::Draw2DPolygon(x, y + SCREEN_SCALE_Y(size + 2.0f), x, y + SCREEN_SCALE_Y(size + 3.0f), x + SCREEN_SCALE_X(size + 3.0f), y - (SCREEN_SCALE_Y(size + 2.0f)), x - (SCREEN_SCALE_X(size + 3.0f)), y - (SCREEN_SCALE_Y(size + 2.0f)), CRGBA(0, 0, 0, alpha)); + CSprite2d::Draw2DPolygon(x, y + SCREEN_SCALE_Y(size + 1.0f), x, y + SCREEN_SCALE_Y(size + 1.0f), x + SCREEN_SCALE_X(size + 1.0f), y - (SCREEN_SCALE_Y(size + 1.0f)), x - (SCREEN_SCALE_X(size + 1.0f)), y - (SCREEN_SCALE_Y(size + 1.0f)), CRGBA(red, green, blue, alpha)); + break; + case BLIP_MODE_SQUARE: + CSprite2d::DrawRect(CRect(x - SCREEN_SCALE_X(size + 1.0f), y - SCREEN_SCALE_Y(size + 1.0f), SCREEN_SCALE_X(size + 1.0f) + x, SCREEN_SCALE_Y(size + 1.0f) + y), CRGBA(0, 0, 0, alpha)); + CSprite2d::DrawRect(CRect(x - SCREEN_SCALE_X(size), y - SCREEN_SCALE_Y(size), SCREEN_SCALE_X(size) + x, SCREEN_SCALE_Y(size) + y), CRGBA(red, green, blue, alpha)); + break; + } +} + +void CRadar::Shutdown() +{ + CentreSprite.Delete(); + MapHereSprite.Delete(); + NorthSprite.Delete(); + AverySprite.Delete(); + BikerSprite.Delete(); + CortezSprite.Delete(); + DiazSprite.Delete(); + KentSprite.Delete(); + LawyerSprite.Delete(); + PhilSprite.Delete(); + BikersSprite.Delete(); + BoatyardSprite.Delete(); + MalibuClubSprite.Delete(); + CubansSprite.Delete(); + FilmSprite.Delete(); + GunSprite.Delete(); + HaitiansSprite.Delete(); + HardwareSprite.Delete(); + SaveHouseSprite.Delete(); + StripSprite.Delete(); + IceSprite.Delete(); + KCabsSprite.Delete(); + LovefistSprite.Delete(); + PrintworksSprite.Delete(); + PropertySprite.Delete(); + SunYardSprite.Delete(); + SpraySprite.Delete(); + TShirtSprite.Delete(); + TommySprite.Delete(); + PhoneSprite.Delete(); + RadioWildstyleSprite.Delete(); + RadioFlashSprite.Delete(); + RadioKChatSprite.Delete(); + RadioFeverSprite.Delete(); + RadioVRockSprite.Delete(); + RadioVCPRSprite.Delete(); + RadioEspantosoSprite.Delete(); + RadioEmotionSprite.Delete(); + RadioWaveSprite.Delete(); +#ifdef MAP_ENHANCEMENTS + WaypointSprite.Delete(); +#endif + RemoveRadarSections(); +} + +void CRadar::StreamRadarSections(const CVector &posn) +{ + if (!CStreaming::ms_disableStreaming) + StreamRadarSections(Floor((RADAR_MAX_X + posn.x) / RADAR_TILE_SIZE), Ceil((RADAR_NUM_TILES - 1) - (RADAR_MAX_Y + posn.y) / RADAR_TILE_SIZE)); +} + +void CRadar::StreamRadarSections(int32 x, int32 y) +{ + for (int i = 0; i < RADAR_NUM_TILES; ++i) { + for (int j = 0; j < RADAR_NUM_TILES; ++j) { + if ((i >= x - 1 && i <= x + 1) && (j >= y - 1 && j <= y + 1)) + RequestMapSection(i, j); + else + RemoveMapSection(i, j); + }; + }; +} + +void CRadar::TransformRealWorldToTexCoordSpace(CVector2D &out, const CVector2D &in, int32 x, int32 y) +{ + out.x = in.x - (x * RADAR_TILE_SIZE + RADAR_MIN_X); + out.y = -(in.y - ((RADAR_NUM_TILES - y) * RADAR_TILE_SIZE + RADAR_MIN_Y)); + out.x /= RADAR_TILE_SIZE; + out.y /= RADAR_TILE_SIZE; +} + +void CRadar::TransformRadarPointToRealWorldSpace(CVector2D &out, const CVector2D &in) +{ + float s, c; + s = -cachedSin; + c = cachedCos; + + out.x = s * in.y + c * in.x; + out.y = c * in.y - s * in.x; + + out = out * m_radarRange + vec2DRadarOrigin; +} + +// Radar space goes from -1.0 to 1.0 in x and y, top right is (1.0, 1.0) +void CRadar::TransformRadarPointToScreenSpace(CVector2D &out, const CVector2D &in) +{ + if (FrontEndMenuManager.m_bMenuMapActive) { + out.x = (FrontEndMenuManager.m_fMapCenterX - FrontEndMenuManager.m_fMapSize) + (MENU_MAP_LENGTH / 2 + MENU_MAP_LEFT_OFFSET + in.x) * FrontEndMenuManager.m_fMapSize * MENU_MAP_WIDTH_SCALE * 2.0f / MENU_MAP_LENGTH; + out.y = (FrontEndMenuManager.m_fMapCenterY - FrontEndMenuManager.m_fMapSize) + (MENU_MAP_LENGTH / 2 - MENU_MAP_TOP_OFFSET - in.y) * FrontEndMenuManager.m_fMapSize * MENU_MAP_HEIGHT_SCALE * 2.0f / MENU_MAP_LENGTH; + } else { +#ifdef FIX_BUGS + out.x = (in.x + 1.0f) * 0.5f * SCREEN_SCALE_X(RADAR_WIDTH) + SCREEN_SCALE_X(RADAR_LEFT); +#else + out.x = (in.x + 1.0f) * 0.5f * SCREEN_SCALE_X(RADAR_WIDTH) + RADAR_LEFT; +#endif + out.y = (1.0f - in.y) * 0.5f * SCREEN_SCALE_Y(RADAR_HEIGHT) + SCREEN_SCALE_FROM_BOTTOM(RADAR_BOTTOM + RADAR_HEIGHT); + } +} + +void CRadar::TransformRealWorldPointToRadarSpace(CVector2D &out, const CVector2D &in) +{ + float s, c; + s = cachedSin; + c = cachedCos; + + float x = (in.x - vec2DRadarOrigin.x) * (1.0f / m_radarRange); + float y = (in.y - vec2DRadarOrigin.y) * (1.0f / m_radarRange); + + out.x = s * y + c * x; + out.y = c * y - s * x; +} + +void +CRadar::CalculateCachedSinCos() +{ + if (/*TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED || */ + FrontEndMenuManager.m_bMenuMapActive ) { + cachedSin = 0.0f; + cachedCos = 1.0f; + } else if (TheCamera.GetLookDirection() == LOOKING_FORWARD) { + cachedSin = Sin(TheCamera.GetForward().Heading()); + cachedCos = Cos(TheCamera.GetForward().Heading()); + } else { + CVector forward; + + if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON) { + forward = TheCamera.Cams[TheCamera.ActiveCam].CamTargetEntity->GetForward(); + forward.Normalise(); // a bit useless... + } + else + forward = TheCamera.Cams[TheCamera.ActiveCam].CamTargetEntity->GetPosition() - TheCamera.Cams[TheCamera.ActiveCam].SourceBeforeLookBehind; + + cachedSin = Sin(forward.Heading()); + cachedCos = Cos(forward.Heading()); + } +} + +void +CRadar::InitFrontEndMap() +{ + CalculateCachedSinCos(); + vec2DRadarOrigin.x = 0.0f; + vec2DRadarOrigin.y = 0.0f; + m_radarRange = MENU_MAP_LENGTH_UNIT; // just affects the multiplier in TransformRadarPointToScreenSpace + for (int i = 0; i < NUM_MAP_LEGENDS; i++) { + MapLegendList[i] = RADAR_SPRITE_NONE; + } + MapLegendCounter = 0; + ArrowBlipColour1 = CRGBA(0, 0, 0, 0); + ArrowBlipColour2 = CRGBA(0, 0, 0, 0); +} + +void +CRadar::DrawYouAreHereSprite(float x, float y) +{ + static uint32 lastChange = 0; + static bool show = true; + + if (show) { + if (CTimer::GetTimeInMillisecondsPauseMode() - lastChange > 500) { + lastChange = CTimer::GetTimeInMillisecondsPauseMode(); + show = !show; + } + } else { + if (CTimer::GetTimeInMillisecondsPauseMode() - lastChange > 200) { + lastChange = CTimer::GetTimeInMillisecondsPauseMode(); + show = !show; + } + } + + if (show) { + const float left = x - SCREEN_SCALE_X(8.0f); + const float top = y - SCREEN_SCALE_Y(40.0f); + const float right = x + SCREEN_SCALE_X(40.0); + const float bottom = y + SCREEN_SCALE_Y(8.0f); + MapHereSprite.Draw(CRect(left + SCREEN_SCALE_X(2.f), top + SCREEN_SCALE_Y(2.f), right + SCREEN_SCALE_X(2.f), bottom + SCREEN_SCALE_Y(2.f)), + CRGBA(0, 0, 0, 255)); + + MapHereSprite.Draw(CRect(left, top, right, bottom), CRGBA(255, 255, 255, 255)); + + CFont::SetWrapx(right + SCREEN_SCALE_X(28.0f)); + CFont::SetRightJustifyWrap(right); + CFont::SetBackGroundOnlyTextOff(); + CFont::SetColor(CRGBA(255, 150, 225, 255)); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + CFont::SetCentreOff(); + CFont::SetRightJustifyOff(); + CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); + CFont::SetScale(SCREEN_SCALE_X(0.65f), SCREEN_SCALE_Y(0.95f)); + CFont::PrintString(right, top, TheText.Get("MAP_YAH")); + CFont::SetDropShadowPosition(0); + CFont::DrawFonts(); + } + MapLegendList[MapLegendCounter++] = RADAR_SPRITE_MAP_HERE; +} + +#ifdef MAP_ENHANCEMENTS +void +CRadar::ToggleTargetMarker(float x, float y) +{ + if (TargetMarkerId == -1) { + int nextBlip; + for (nextBlip = NUMRADARBLIPS-1; nextBlip >= 0; nextBlip--) { + if (!ms_RadarTrace[nextBlip].m_bInUse) + break; + } + + if (nextBlip == 0) + return; + + ms_RadarTrace[nextBlip].m_eBlipType = BLIP_COORD; + ms_RadarTrace[nextBlip].m_nColor = RADAR_TRACE_GRAY; + ms_RadarTrace[nextBlip].m_bDim = 0; + ms_RadarTrace[nextBlip].m_bInUse = 1; + ms_RadarTrace[nextBlip].m_Radius = 1.0f; + CVector pos(x, y, 0.0f); + TargetMarkerPos = pos; + ms_RadarTrace[nextBlip].m_vec2DPos = pos; + ms_RadarTrace[nextBlip].m_vecPos = pos; + ms_RadarTrace[nextBlip].m_nEntityHandle = 0; + ms_RadarTrace[nextBlip].m_wScale = 5; + ms_RadarTrace[nextBlip].m_eBlipDisplay = BLIP_DISPLAY_BLIP_ONLY; + ms_RadarTrace[nextBlip].m_eRadarSprite = RADAR_SPRITE_WAYPOINT; + TargetMarkerId = CRadar::GetNewUniqueBlipIndex(nextBlip); + } else { + ClearBlip(TargetMarkerId); + TargetMarkerId = -1; + } +} +#endif + +void +CRadar::DrawEntityBlip(int32 blipId) +{ + CVector2D out; + CVector2D in; + CEntity *blipEntity; + switch (ms_RadarTrace[blipId].m_eBlipType) { + case BLIP_CAR: + blipEntity = CPools::GetVehiclePool()->GetAt(ms_RadarTrace[blipId].m_nEntityHandle); + break; + case BLIP_CHAR: + blipEntity = CPools::GetPedPool()->GetAt(ms_RadarTrace[blipId].m_nEntityHandle); + if (blipEntity != nil) { + if (((CPed*)blipEntity)->InVehicle()) + blipEntity = ((CPed*)blipEntity)->m_pMyVehicle; + } + break; + case BLIP_OBJECT: + blipEntity = CPools::GetObjectPool()->GetAt(ms_RadarTrace[blipId].m_nEntityHandle); + break; + default: + break; + } + if (blipEntity) { + uint32 color = GetRadarTraceColour(ms_RadarTrace[blipId].m_nColor, ms_RadarTrace[blipId].m_bDim); + if (ms_RadarTrace[blipId].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[blipId].m_eBlipDisplay == BLIP_DISPLAY_MARKER_ONLY) { + if (CTheScripts::IsDebugOn()) { + ShowRadarMarker(blipEntity->GetPosition(), color, ms_RadarTrace[blipId].m_Radius); + ms_RadarTrace[blipId].m_Radius = ms_RadarTrace[blipId].m_Radius - 0.1f; + if (ms_RadarTrace[blipId].m_Radius < 1.0f) + ms_RadarTrace[blipId].m_Radius = 5.0f; + } + } + if (ms_RadarTrace[blipId].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[blipId].m_eBlipDisplay == BLIP_DISPLAY_BLIP_ONLY) { + TransformRealWorldPointToRadarSpace(in, blipEntity->GetPosition()); + float dist = LimitRadarPoint(in); + TransformRadarPointToScreenSpace(out, in); + if (!ms_RadarTrace[blipId].m_bShortRange || dist <= 1.0f || FrontEndMenuManager.m_bMenuMapActive) { + if (ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_NONE) { + DrawRadarSprite(ms_RadarTrace[blipId].m_eRadarSprite, out.x, out.y, CalculateBlipAlpha(dist)); + } else { + const CVector& pos = FindPlayerCentreOfWorld_NoSniperShift(); + const CVector& blipPos = blipEntity->GetPosition(); + uint8 mode = BLIP_MODE_TRIANGULAR_UP; + if (blipPos.z - pos.z <= 2.0f) { + if (blipPos.z - pos.z < -4.0f) mode = BLIP_MODE_TRIANGULAR_DOWN; + else mode = BLIP_MODE_SQUARE; + } + ShowRadarTraceWithHeight(out.x, out.y, ms_RadarTrace[blipId].m_wScale, (uint8)(color >> 24), (uint8)(color >> 16), (uint8)(color >> 8), 255, mode); + + if (FrontEndMenuManager.m_bMenuMapActive) { + bool alreadyThere = false; + for (int i = 0; i < NUM_MAP_LEGENDS; i++) { + if (MapLegendList[i] == -2) + alreadyThere = true; + } + if (!alreadyThere) { + MapLegendList[MapLegendCounter] = -2; + MapLegendCounter++; + ArrowBlipColour2 = CRGBA((uint8)(color >> 24), (uint8)(color >> 16), (uint8)(color >> 8), 255); + } + } + } + } + } + } +} + +void +CRadar::DrawCoordBlip(int32 blipId) +{ + CVector2D out; + CVector2D in; + if (ms_RadarTrace[blipId].m_eBlipType != BLIP_CONTACT_POINT || !CTheScripts::IsPlayerOnAMission()) { + + uint32 color = GetRadarTraceColour(ms_RadarTrace[blipId].m_nColor, ms_RadarTrace[blipId].m_bDim); + if (ms_RadarTrace[blipId].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[blipId].m_eBlipDisplay == BLIP_DISPLAY_MARKER_ONLY) { + if (CTheScripts::IsDebugOn()) { + ShowRadarMarker(ms_RadarTrace[blipId].m_vecPos, color, ms_RadarTrace[blipId].m_Radius); + ms_RadarTrace[blipId].m_Radius = ms_RadarTrace[blipId].m_Radius - 0.1f; + if (ms_RadarTrace[blipId].m_Radius < 1.0f) + ms_RadarTrace[blipId].m_Radius = 5.0f; + } + } + if (ms_RadarTrace[blipId].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[blipId].m_eBlipDisplay == BLIP_DISPLAY_BLIP_ONLY) { + TransformRealWorldPointToRadarSpace(in, ms_RadarTrace[blipId].m_vec2DPos); + float dist = LimitRadarPoint(in); + TransformRadarPointToScreenSpace(out, in); + if (!ms_RadarTrace[blipId].m_bShortRange || dist <= 1.0f || FrontEndMenuManager.m_bMenuMapActive) { + if (ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_NONE) { + DrawRadarSprite(ms_RadarTrace[blipId].m_eRadarSprite, out.x, out.y, CalculateBlipAlpha(dist)); + } else { + const CVector& pos = FindPlayerCentreOfWorld_NoSniperShift(); + const CVector& blipPos = ms_RadarTrace[blipId].m_vecPos; + uint8 mode = BLIP_MODE_TRIANGULAR_UP; + if (blipPos.z - pos.z <= 2.0f) { + if (blipPos.z - pos.z < -4.0f) mode = BLIP_MODE_TRIANGULAR_DOWN; + else mode = BLIP_MODE_SQUARE; + } + ShowRadarTraceWithHeight(out.x, out.y, ms_RadarTrace[blipId].m_wScale, (uint8)(color >> 24), (uint8)(color >> 16), (uint8)(color >> 8), 255, mode); + + if (FrontEndMenuManager.m_bMenuMapActive) { + bool alreadyThere = false; + for (int i = 0; i < NUM_MAP_LEGENDS; i++) { + if (MapLegendList[i] == -1) + alreadyThere = true; + } + if (!alreadyThere) { + MapLegendList[MapLegendCounter] = -1; + MapLegendCounter++; + ArrowBlipColour1 = CRGBA((uint8)(color >> 24), (uint8)(color >> 16), (uint8)(color >> 8), 255); + } + } + } + } + } + } +} + +void +CRadar::DrawLegend(int32 x, int32 y, int32 sprite) +{ + if (sprite < 0) { + static uint32 lastChange = 0; + static int8 blipMode = 0; + + CRGBA color; + if (sprite == -1) { + color = ArrowBlipColour1; + } else { + color = ArrowBlipColour2; + } + + if (CTimer::GetTimeInMillisecondsPauseMode() - lastChange > 600) { + lastChange = CTimer::GetTimeInMillisecondsPauseMode(); + if ( blipMode == 2 ) + blipMode = 0; + else + ++blipMode; + } + + switch (blipMode) { + case BLIP_MODE_TRIANGULAR_UP: + CSprite2d::Draw2DPolygon(x + SCREEN_SCALE_X(14.0f), y + SCREEN_SCALE_Y(13.0f), x + SCREEN_SCALE_X(2.0f), y + SCREEN_SCALE_Y(13.0f), x + SCREEN_SCALE_X(8.f), y + SCREEN_SCALE_Y(2.0f), x + SCREEN_SCALE_X(8.f), y + SCREEN_SCALE_Y(2.0f), CRGBA(0, 0, 0, 255)); + CSprite2d::Draw2DPolygon(x + SCREEN_SCALE_X(12.0f), y + SCREEN_SCALE_Y(12.0f), x + SCREEN_SCALE_X(4.0f), y + SCREEN_SCALE_Y(12.0f), x + SCREEN_SCALE_X(8.f), y + SCREEN_SCALE_Y(4.0f), x + SCREEN_SCALE_X(8.f), y + SCREEN_SCALE_Y(4.0f), color); + break; + case BLIP_MODE_TRIANGULAR_DOWN: + CSprite2d::Draw2DPolygon(x + SCREEN_SCALE_X(8.0f), y + SCREEN_SCALE_Y(14.0f), x + SCREEN_SCALE_X(8.0f), y + SCREEN_SCALE_Y(14.0f), x + SCREEN_SCALE_X(2.f), y + SCREEN_SCALE_Y(3.0f), x + SCREEN_SCALE_X(2.f), y + SCREEN_SCALE_Y(3.0f), CRGBA(0, 0, 0, 255)); + CSprite2d::Draw2DPolygon(x + SCREEN_SCALE_X(8.0f), y + SCREEN_SCALE_Y(12.0f), x + SCREEN_SCALE_X(8.0f), y + SCREEN_SCALE_Y(12.0f), x + SCREEN_SCALE_X(12.f), y + SCREEN_SCALE_Y(4.0f), x + SCREEN_SCALE_X(4.f), y + SCREEN_SCALE_Y(4.0f), color); + break; + case BLIP_MODE_SQUARE: + CSprite2d::DrawRect(CRect(x + SCREEN_SCALE_X(4.0f), y + SCREEN_SCALE_Y(3.0f), SCREEN_SCALE_X(12.0f) + x, SCREEN_SCALE_Y(12.0f) + y), CRGBA(0, 0, 0, 255)); + CSprite2d::DrawRect(CRect(x + SCREEN_SCALE_X(5.0f), y + SCREEN_SCALE_Y(4.0f), SCREEN_SCALE_X(11.0f) + x, SCREEN_SCALE_Y(11.0f) + y), color); + break; + } + + } else { + RadarSprites[sprite]->Draw(CRect(x, y, x + SCREEN_SCALE_X(16.f), y + SCREEN_SCALE_X(16.f)), CRGBA(255, 255, 255, 255)); + } + + wchar *text; + switch ( sprite ) { + case RADAR_SPRITE_ENTITY_BLIP: + text = TheText.Get("LG_38"); + break; + case RADAR_SPRITE_COORD_BLIP: + text = TheText.Get("LG_35"); + break; + case RADAR_SPRITE_MAP_HERE: + text = TheText.Get("LG_01"); + break; + case RADAR_SPRITE_AVERY: + text = TheText.Get("LG_02"); + break; + case RADAR_SPRITE_BIKER: + text = TheText.Get("LG_03"); + break; + case RADAR_SPRITE_CORTEZ: + text = TheText.Get("LG_04"); + break; + case RADAR_SPRITE_DIAZ: + text = TheText.Get("LG_05"); + break; + case RADAR_SPRITE_KENT: + text = TheText.Get("LG_06"); + break; + case RADAR_SPRITE_LAWYER: + text = TheText.Get("LG_07"); + break; + case RADAR_SPRITE_PHIL: + text = TheText.Get("LG_08"); + break; + case RADAR_SPRITE_BIKERS: + text = TheText.Get("LG_03"); + break; + case RADAR_SPRITE_BOATYARD: + text = TheText.Get("LG_09"); + break; + case RADAR_SPRITE_MALIBU_CLUB: + text = TheText.Get("LG_10"); + break; + case RADAR_SPRITE_CUBANS: + text = TheText.Get("LG_11"); + break; + case RADAR_SPRITE_FILM: + text = TheText.Get("LG_12"); + break; + case RADAR_SPRITE_GUN: + text = TheText.Get("LG_13"); + break; + case RADAR_SPRITE_HAITIANS: + text = TheText.Get("LG_14"); + break; + case RADAR_SPRITE_HARDWARE: + text = TheText.Get("LG_15"); + break; + case RADAR_SPRITE_SAVE: + text = TheText.Get("LG_16"); + break; + case RADAR_SPRITE_STRIP: + text = TheText.Get("LG_37"); + break; + case RADAR_SPRITE_ICE: + text = TheText.Get("LG_17"); + break; + case RADAR_SPRITE_KCABS: + text = TheText.Get("LG_18"); + break; + case RADAR_SPRITE_LOVEFIST: + text = TheText.Get("LG_19"); + break; + case RADAR_SPRITE_PRINTWORKS: + text = TheText.Get("LG_20"); + break; + case RADAR_SPRITE_PROPERTY: + text = TheText.Get("LG_21"); + break; + case RADAR_SPRITE_SUNYARD: + text = TheText.Get("LG_36"); + break; + case RADAR_SPRITE_SPRAY: + text = TheText.Get("LG_22"); + break; + case RADAR_SPRITE_TSHIRT: + text = TheText.Get("LG_23"); + break; + case RADAR_SPRITE_TOMMY: + text = TheText.Get("LG_24"); + break; + case RADAR_SPRITE_PHONE: + text = TheText.Get("LG_25"); + break; + case RADAR_SPRITE_RADIO_WILDSTYLE: + text = TheText.Get("LG_26"); + break; + case RADAR_SPRITE_RADIO_FLASH: + text = TheText.Get("LG_27"); + break; + case RADAR_SPRITE_RADIO_KCHAT: + text = TheText.Get("LG_28"); + break; + case RADAR_SPRITE_RADIO_FEVER: + text = TheText.Get("LG_29"); + break; + case RADAR_SPRITE_RADIO_VROCK: + text = TheText.Get("LG_30"); + break; + case RADAR_SPRITE_RADIO_VCPR: + text = TheText.Get("LG_31"); + break; + case RADAR_SPRITE_RADIO_ESPANTOSO: + text = TheText.Get("LG_32"); + break; + case RADAR_SPRITE_RADIO_EMOTION: + text = TheText.Get("LG_33"); + break; + case RADAR_SPRITE_RADIO_WAVE: + text = TheText.Get("LG_34"); + break; +#ifdef MAP_ENHANCEMENTS + case RADAR_SPRITE_WAYPOINT: + text = TheText.Get("LG_38"); + break; +#endif + default: + break; + } + CFont::PrintString(SCREEN_SCALE_X(20.f) + x, SCREEN_SCALE_Y(3.0f) + y, text); +} diff --git a/src/miami/core/Radar.h b/src/miami/core/Radar.h new file mode 100644 index 00000000..afb37fe4 --- /dev/null +++ b/src/miami/core/Radar.h @@ -0,0 +1,294 @@ +#pragma once +#include "Sprite2d.h" +#include "Draw.h" + +#define CARBLIP_MARKER_COLOR_R 252 +#define CARBLIP_MARKER_COLOR_G 138 +#define CARBLIP_MARKER_COLOR_B 242 +#define CARBLIP_MARKER_COLOR_A 255 + +#define CHARBLIP_MARKER_COLOR_R 252 +#define CHARBLIP_MARKER_COLOR_G 138 +#define CHARBLIP_MARKER_COLOR_B 242 +#define CHARBLIP_MARKER_COLOR_A 255 + +#define OBJECTBLIP_MARKER_COLOR_R 252 +#define OBJECTBLIP_MARKER_COLOR_G 138 +#define OBJECTBLIP_MARKER_COLOR_B 242 +#define OBJECTBLIP_MARKER_COLOR_A 255 + +#define COORDBLIP_MARKER_COLOR_R 252 +#define COORDBLIP_MARKER_COLOR_G 138 +#define COORDBLIP_MARKER_COLOR_B 242 +#define COORDBLIP_MARKER_COLOR_A 228 + +#define NUM_MAP_LEGENDS 75 + +#define MENU_MAP_LENGTH_UNIT 1190.0f // in game unit +#define MENU_MAP_WIDTH_SCALE 1.112f // in game unit (originally 1.112494151260504f) +#define MENU_MAP_HEIGHT_SCALE 1.119f // in game unit (originally 1.118714268907563f) +#define MENU_MAP_TOP_OFFSET 0.28f // in length unit defined above - ~333 game unit +#define MENU_MAP_LEFT_OFFSET 0.185f // in length unit defined above - ~220 game unit +#define MENU_MAP_LENGTH (4000.f / MENU_MAP_LENGTH_UNIT) + +enum eBlipType +{ + BLIP_NONE, + BLIP_CAR, + BLIP_CHAR, + BLIP_OBJECT, + BLIP_COORD, + BLIP_CONTACT_POINT +}; + +enum eBlipDisplay +{ + BLIP_DISPLAY_NEITHER = 0, + BLIP_DISPLAY_MARKER_ONLY = 1, + BLIP_DISPLAY_BLIP_ONLY = 2, + BLIP_DISPLAY_BOTH = 3, +}; + +enum eRadarSprite +{ + RADAR_SPRITE_ENTITY_BLIP = -2, + RADAR_SPRITE_COORD_BLIP = -1, + RADAR_SPRITE_NONE = 0, + RADAR_SPRITE_CENTRE, + RADAR_SPRITE_MAP_HERE, + RADAR_SPRITE_NORTH, + RADAR_SPRITE_AVERY, + RADAR_SPRITE_BIKER, + RADAR_SPRITE_CORTEZ, + RADAR_SPRITE_DIAZ, + RADAR_SPRITE_KENT, + RADAR_SPRITE_LAWYER, + RADAR_SPRITE_PHIL, + RADAR_SPRITE_BIKERS, + RADAR_SPRITE_BOATYARD, + RADAR_SPRITE_MALIBU_CLUB, + RADAR_SPRITE_CUBANS, + RADAR_SPRITE_FILM, + RADAR_SPRITE_GUN, + RADAR_SPRITE_HAITIANS, + RADAR_SPRITE_HARDWARE, + RADAR_SPRITE_SAVE, + RADAR_SPRITE_STRIP, + RADAR_SPRITE_ICE, + RADAR_SPRITE_KCABS, + RADAR_SPRITE_LOVEFIST, + RADAR_SPRITE_PRINTWORKS, + RADAR_SPRITE_PROPERTY, + RADAR_SPRITE_SUNYARD, + RADAR_SPRITE_SPRAY, + RADAR_SPRITE_TSHIRT, + RADAR_SPRITE_TOMMY, + RADAR_SPRITE_PHONE, + RADAR_SPRITE_RADIO_WILDSTYLE, + RADAR_SPRITE_RADIO_FLASH, + RADAR_SPRITE_RADIO_KCHAT, + RADAR_SPRITE_RADIO_FEVER, + RADAR_SPRITE_RADIO_VROCK, + RADAR_SPRITE_RADIO_VCPR, + RADAR_SPRITE_RADIO_ESPANTOSO, + RADAR_SPRITE_RADIO_EMOTION, + RADAR_SPRITE_RADIO_WAVE, +#ifdef MAP_ENHANCEMENTS + RADAR_SPRITE_WAYPOINT, +#endif + + RADAR_SPRITE_COUNT +}; + +enum +{ + RADAR_TRACE_RED, + RADAR_TRACE_GREEN, + RADAR_TRACE_LIGHT_BLUE, + RADAR_TRACE_GRAY, + RADAR_TRACE_YELLOW, + RADAR_TRACE_MAGENTA, + RADAR_TRACE_CYAN +}; + +enum +{ + BLIP_MODE_TRIANGULAR_UP = 0, + BLIP_MODE_TRIANGULAR_DOWN, + BLIP_MODE_SQUARE, +}; + +struct sRadarTrace +{ + uint32 m_nColor; + uint32 m_eBlipType; // eBlipType + int32 m_nEntityHandle; + CVector m_vec2DPos; + CVector m_vecPos; + uint16 m_BlipIndex; + bool m_bDim; + bool m_bInUse; + bool m_bShortRange; + bool m_unused; + float m_Radius; + int16 m_wScale; + uint16 m_eBlipDisplay; // eBlipDisplay + uint16 m_eRadarSprite; // eRadarSprite +}; + +// Either that was a thing while saving/loading blips, or they added sizes of each field one by one. I want to do the former. +#pragma pack(push,1) +struct sRadarTraceSave +{ + uint32 m_nColor; + float m_Radius; + uint32 m_eBlipType; // eBlipType + int32 m_nEntityHandle; + CVector2D m_vec2DPos; + CVector m_vecPos; + uint16 m_BlipIndex; + bool m_bDim; + bool m_bInUse; + bool m_bShortRange; + bool m_unused; + int16 m_wScale; + uint16 m_eBlipDisplay; // eBlipDisplay + uint16 m_eRadarSprite; // eRadarSprite +}; +#pragma pack(pop) + +// Values for screen space +#define RADAR_LEFT (40.0f) +#define RADAR_BOTTOM (40.0f) + +#ifdef FIX_RADAR +/* + The values are from an early screenshot taken before R* broke radar + #define RADAR_WIDTH (82.0f) + #define RADAR_HEIGHT (82.0f) +*/ +#define RADAR_WIDTH ((CDraw::ms_bFixRadar) ? (82.0f) : (94.0f)) +#define RADAR_HEIGHT ((CDraw::ms_bFixRadar) ? (82.0f) : (76.0f)) +#else +/* + broken since forever, someone tried to fix size for 640x512(PAL) + http://aap.rockstarvision.com/pics/gta3/ps2screens/gta3_interface.jpg + but failed: + http://aap.rockstarvision.com/pics/gta3/artwork/gta3_artwork_16.jpg + most likely the guy used something like this: + int y = 82 * (640.0/512.0)/(640.0/480.0); + int x = y * (640.0/512.0); +*/ +#define RADAR_WIDTH (94.0f) +#define RADAR_HEIGHT (76.0f) +#endif + +class CRadar +{ +public: + static float m_radarRange; + static sRadarTrace ms_RadarTrace[NUMRADARBLIPS]; + static CSprite2d CentreSprite; + static CSprite2d MapHereSprite; + static CSprite2d NorthSprite; + static CSprite2d AverySprite; + static CSprite2d BikerSprite; + static CSprite2d CortezSprite; + static CSprite2d DiazSprite; + static CSprite2d KentSprite; + static CSprite2d LawyerSprite; + static CSprite2d PhilSprite; + static CSprite2d BikersSprite; + static CSprite2d BoatyardSprite; + static CSprite2d MalibuClubSprite; + static CSprite2d CubansSprite; + static CSprite2d FilmSprite; + static CSprite2d GunSprite; + static CSprite2d HaitiansSprite; + static CSprite2d HardwareSprite; + static CSprite2d SaveHouseSprite; + static CSprite2d StripSprite; + static CSprite2d IceSprite; + static CSprite2d KCabsSprite; + static CSprite2d LovefistSprite; + static CSprite2d PrintworksSprite; + static CSprite2d PropertySprite; + static CSprite2d SunYardSprite; + static CSprite2d SpraySprite; + static CSprite2d TShirtSprite; + static CSprite2d TommySprite; + static CSprite2d PhoneSprite; + static CSprite2d RadioWildstyleSprite; + static CSprite2d RadioFlashSprite; + static CSprite2d RadioKChatSprite; + static CSprite2d RadioFeverSprite; + static CSprite2d RadioVRockSprite; + static CSprite2d RadioVCPRSprite; + static CSprite2d RadioEspantosoSprite; + static CSprite2d RadioEmotionSprite; + static CSprite2d RadioWaveSprite; + static CSprite2d *RadarSprites[RADAR_SPRITE_COUNT]; + static float cachedCos; + static float cachedSin; + static CRGBA ArrowBlipColour1; + static CRGBA ArrowBlipColour2; + static int16 MapLegendList[NUM_MAP_LEGENDS]; + static int16 MapLegendCounter; + +#ifdef MAP_ENHANCEMENTS + static CSprite2d WaypointSprite; + static int TargetMarkerId; + static CVector TargetMarkerPos; +#endif + + static void InitFrontEndMap(); + static void DrawYouAreHereSprite(float, float); +#ifdef MAP_ENHANCEMENTS + static void ToggleTargetMarker(float, float); +#endif + static uint8 CalculateBlipAlpha(float dist); + static void ChangeBlipBrightness(int32 i, int32 bright); + static void ChangeBlipColour(int32 i, int32); + static void ChangeBlipDisplay(int32 i, eBlipDisplay display); + static void ChangeBlipScale(int32 i, int32 scale); + static void ClearBlip(int32 i); + static void ClearBlipForEntity(eBlipType type, int32 id); + static int ClipRadarPoly(CVector2D *out, const CVector2D *in); + static bool DisplayThisBlip(int32 i); + static void Draw3dMarkers(); + static void DrawBlips(); + static void DrawMap(); + static void DrawRadarMap(); + static void DrawRadarMask(); + static void DrawRadarSection(int32 x, int32 y); + static void DrawRadarSprite(uint16 sprite, float x, float y, uint8 alpha); + static void DrawRotatingRadarSprite(CSprite2d* sprite, float x, float y, float angle, int32 alpha); + static int32 GetActualBlipArrayIndex(int32 i); + static int32 GetNewUniqueBlipIndex(int32 i); + static uint32 GetRadarTraceColour(uint32 color, bool bright); + static void Initialise(); + static float LimitRadarPoint(CVector2D &point); + static void LoadAllRadarBlips(uint8 *buf, uint32 size); + static void LoadTextures(); + static void RemoveRadarSections(); + static void SaveAllRadarBlips(uint8*, uint32*); + static void SetBlipSprite(int32 i, int32 icon); + static int32 SetCoordBlip(eBlipType type, CVector pos, uint32, eBlipDisplay); + static int32 SetEntityBlip(eBlipType type, int32, uint32, eBlipDisplay); + static int32 SetShortRangeCoordBlip(eBlipType type, CVector pos, uint32, eBlipDisplay); + static void SetRadarMarkerState(int32 i, bool flag); + static void ShowRadarMarker(CVector pos, uint32 color, float radius); + static void ShowRadarTrace(float x, float y, uint32 size, uint8 red, uint8 green, uint8 blue, uint8 alpha); + static void ShowRadarTraceWithHeight(float x, float y, uint32 size, uint8 red, uint8 green, uint8 blue, uint8 alpha, uint8 mode); + static void Shutdown(); + static void StreamRadarSections(const CVector &posn); + static void StreamRadarSections(int32 x, int32 y); + static void TransformRealWorldToTexCoordSpace(CVector2D &out, const CVector2D &in, int32 x, int32 y); + static void TransformRadarPointToRealWorldSpace(CVector2D &out, const CVector2D &in); + static void TransformRadarPointToScreenSpace(CVector2D &out, const CVector2D &in); + static void TransformRealWorldPointToRadarSpace(CVector2D &out, const CVector2D &in); + static void CalculateCachedSinCos(); + static void DrawEntityBlip(int32 blipId); + static void DrawCoordBlip(int32 blipId); + static void DrawLegend(int32, int32, int32); +}; diff --git a/src/miami/core/Range2D.cpp b/src/miami/core/Range2D.cpp new file mode 100644 index 00000000..33eafd0e --- /dev/null +++ b/src/miami/core/Range2D.cpp @@ -0,0 +1,28 @@ +#include "common.h" +#include "Range2D.h" +#include "General.h" + +CRange2D::CRange2D(CVector2D _min, CVector2D _max) : min(_min), max(_max) {} + +bool +CRange2D::IsInRange(CVector2D vec) +{ + return min.x < vec.x && max.x > vec.x && min.y < vec.y && max.y > vec.y; +} + +void +CRange2D::DebugShowRange(float, int) +{ +} + +CVector2D +CRange2D::GetRandomPointInRange() +{ + int distX = Abs(max.x - min.x); + int distY = Abs(max.y - min.y); + + float outX = CGeneral::GetRandomNumber() % distX + min.x; + float outY = CGeneral::GetRandomNumber() % distY + min.y; + + return CVector2D(outX, outY); +} diff --git a/src/miami/core/Range2D.h b/src/miami/core/Range2D.h new file mode 100644 index 00000000..f239e7de --- /dev/null +++ b/src/miami/core/Range2D.h @@ -0,0 +1,11 @@ +#pragma once + +class CRange2D +{ + CVector2D min, max; +public: + CRange2D(CVector2D _min, CVector2D _max); + bool IsInRange(CVector2D vec); + void DebugShowRange(float, int); + CVector2D GetRandomPointInRange(); +}; \ No newline at end of file diff --git a/src/miami/core/Range3D.cpp b/src/miami/core/Range3D.cpp new file mode 100644 index 00000000..7fa28d67 --- /dev/null +++ b/src/miami/core/Range3D.cpp @@ -0,0 +1,30 @@ +#include "common.h" +#include "Range3D.h" +#include "General.h" + +CRange3D::CRange3D(CVector _min, CVector _max) : min(_min), max(_max) {} + +bool +CRange3D::IsInRange(CVector vec) +{ + return min.x < vec.x && max.x > vec.x && min.y < vec.y && max.y > vec.y && min.z < vec.z && max.z > vec.z; +} + +void +CRange3D::DebugShowRange(float, int) +{ +} + +CVector +CRange3D::GetRandomPointInRange() +{ + int distX = Abs(max.x - min.x); + int distY = Abs(max.y - min.y); + int distZ = Abs(max.z - min.z); + + float outX = CGeneral::GetRandomNumber() % distX + min.x; + float outY = CGeneral::GetRandomNumber() % distY + min.y; + float outZ = CGeneral::GetRandomNumber() % distZ + min.z; + + return CVector(outX, outY, outZ); +} diff --git a/src/miami/core/Range3D.h b/src/miami/core/Range3D.h new file mode 100644 index 00000000..f42b523b --- /dev/null +++ b/src/miami/core/Range3D.h @@ -0,0 +1,11 @@ +#pragma once + +class CRange3D +{ + CVector min, max; +public: + CRange3D(CVector _min, CVector _max); + bool IsInRange(CVector vec); + void DebugShowRange(float, int); + CVector GetRandomPointInRange(); +}; \ No newline at end of file diff --git a/src/miami/core/References.cpp b/src/miami/core/References.cpp new file mode 100644 index 00000000..09913817 --- /dev/null +++ b/src/miami/core/References.cpp @@ -0,0 +1,143 @@ +#include "common.h" + +#include "World.h" +#include "Vehicle.h" +#include "PlayerPed.h" +#include "Pools.h" +#include "References.h" + +CReference CReferences::aRefs[NUMREFERENCES]; +CReference *CReferences::pEmptyList; + +void +CReferences::Init(void) +{ + int i; + pEmptyList = &aRefs[0]; + for(i = 0; i < NUMREFERENCES; i++){ + aRefs[i].pentity = nil; + aRefs[i].next = &aRefs[i+1]; + } + aRefs[NUMREFERENCES-1].next = nil; +} + +void +CEntity::RegisterReference(CEntity **pent) +{ + if(IsBuilding()) + return; + CReference *ref; + // check if already registered + for(ref = m_pFirstReference; ref; ref = ref->next) + if(ref->pentity == pent) + return; + // have to allocate new reference + ref = CReferences::pEmptyList; + if(ref){ + CReferences::pEmptyList = ref->next; + + ref->pentity = pent; + ref->next = m_pFirstReference; + m_pFirstReference = ref; + } +} + +// Clean up the reference from *pent -> 'this' +void +CEntity::CleanUpOldReference(CEntity **pent) +{ + CReference* ref, ** lastnextp; + lastnextp = &m_pFirstReference; + for (ref = m_pFirstReference; ref; ref = ref->next) { + if (ref->pentity == pent) { + *lastnextp = ref->next; + ref->next = CReferences::pEmptyList; + CReferences::pEmptyList = ref; + break; + } + lastnextp = &ref->next; + } +} + +// Clear all references to this entity +void +CEntity::ResolveReferences(void) +{ + CReference *ref; + // clear pointers to this entity + for(ref = m_pFirstReference; ref; ref = ref->next) + if(*ref->pentity == this) + *ref->pentity = nil; + // free list + if(m_pFirstReference){ + for(ref = m_pFirstReference; ref->next; ref = ref->next) + ; + ref->next = CReferences::pEmptyList; + CReferences::pEmptyList = m_pFirstReference; + m_pFirstReference = nil; + } +} + +// Free all references that no longer point to this entity +void +CEntity::PruneReferences(void) +{ + CReference *ref, *next, **lastnextp; + lastnextp = &m_pFirstReference; + for(ref = m_pFirstReference; ref; ref = next){ + next = ref->next; + if(*ref->pentity == this) + lastnextp = &ref->next; + else{ + *lastnextp = ref->next; + ref->next = CReferences::pEmptyList; + CReferences::pEmptyList = ref; + } + } +} + +void +CReferences::RemoveReferencesToPlayer(void) +{ + if(FindPlayerVehicle()) + FindPlayerVehicle()->ResolveReferences(); +#ifdef FIX_BUGS + if (FindPlayerPed()) { + CPlayerPed* pPlayerPed = FindPlayerPed(); + FindPlayerPed()->ResolveReferences(); + CWorld::Players[CWorld::PlayerInFocus].m_pPed = pPlayerPed; + pPlayerPed->RegisterReference((CEntity**)&CWorld::Players[CWorld::PlayerInFocus].m_pPed); + } +#else + if(FindPlayerPed()) + FindPlayerPed()->ResolveReferences(); +#endif +} + +void +CReferences::PruneAllReferencesInWorld(void) +{ + int i; + CEntity *e; + + i = CPools::GetPedPool()->GetSize(); + while(--i >= 0){ + e = CPools::GetPedPool()->GetSlot(i); + if(e) + e->PruneReferences(); + } + + i = CPools::GetVehiclePool()->GetSize(); + while(--i >= 0){ + e = CPools::GetVehiclePool()->GetSlot(i); + if(e) + e->PruneReferences(); + } + + i = CPools::GetObjectPool()->GetSize(); + while(--i >= 0){ + e = CPools::GetObjectPool()->GetSlot(i); + if(e) + e->PruneReferences(); + } +} diff --git a/src/miami/core/References.h b/src/miami/core/References.h new file mode 100644 index 00000000..e7a64de7 --- /dev/null +++ b/src/miami/core/References.h @@ -0,0 +1,20 @@ +#pragma once + +class CEntity; + +struct CReference +{ + CReference *next; + CEntity **pentity; +}; + +class CReferences +{ +public: + static CReference aRefs[NUMREFERENCES]; + static CReference *pEmptyList; + + static void Init(void); + static void RemoveReferencesToPlayer(void); + static void PruneAllReferencesInWorld(void); +}; diff --git a/src/miami/core/Ropes.cpp b/src/miami/core/Ropes.cpp new file mode 100644 index 00000000..71297eb1 --- /dev/null +++ b/src/miami/core/Ropes.cpp @@ -0,0 +1,176 @@ +#include "common.h" + +#include "main.h" +#include "Timer.h" +#include "ModelIndices.h" +#include "Streaming.h" +#include "CopPed.h" +#include "Population.h" +#include "RenderBuffer.h" +#include "Camera.h" +#include "Ropes.h" + +CRope CRopes::aRopes[8]; + +RwImVertexIndex RopeIndices[64] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, + 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, + 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, + 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, + 31, 32 // unused +}; + +void +CRope::Update(void) +{ + int i; + float step = Pow(0.85f, CTimer::GetTimeStep()); + if(!m_bWasRegistered && CTimer::GetTimeInMilliseconds() > m_updateTimer){ + m_speed[0].z -= 0.0015f*CTimer::GetTimeStep(); + m_pos[0] += m_speed[0]*CTimer::GetTimeStep(); + } + for(i = 1; i < ARRAY_SIZE(m_pos); i++){ + CVector prevPos = m_pos[i]; + m_pos[i] += m_speed[i]*step*CTimer::GetTimeStep(); + m_pos[i].z -= 0.05f*CTimer::GetTimeStep(); + CVector dist = m_pos[i] - m_pos[i-1]; + m_pos[i] = m_pos[i-1] + (0.625f/dist.Magnitude())*dist; + m_speed[i] = (m_pos[i] - prevPos)/CTimer::GetTimeStep(); + } + if(!m_bWasRegistered && m_pos[0].z < 0.0f) + m_bActive = false; + m_bWasRegistered = false; +} + +void +CRope::Render(void) +{ + int i; + int numVerts = 0; + if(!TheCamera.IsSphereVisible(m_pos[16], 20.0f)) + return; + + for(i = 0; i < ARRAY_SIZE(m_pos); i++){ + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[i], 128, 128, 128, 100); + RwIm3DVertexSetPos(&TempBufferRenderVertices[i], m_pos[i].x, m_pos[i].y, m_pos[i].z); + } + + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + + if(RwIm3DTransform(TempBufferRenderVertices, ARRAY_SIZE(m_pos), nil, rwIM3D_VERTEXXYZ|rwIM3D_VERTEXRGBA)){ +#ifdef FIX_BUGS + RwIm3DRenderIndexedPrimitive(rwPRIMTYPELINELIST, RopeIndices, 2*(ARRAY_SIZE(m_pos)-1)); +#else + RwIm3DRenderIndexedPrimitive(rwPRIMTYPEPOLYLINE, RopeIndices, 2*(ARRAY_SIZE(m_pos)-1)); +#endif + RwIm3DEnd(); + } +} + + +void +CRopes::Init(void) +{ + int i; + for(i = 0; i < ARRAY_SIZE(aRopes); i++) + aRopes[i].m_bActive = false; +} + +void +CRopes::Update(void) +{ + int i; + for(i = 0; i < ARRAY_SIZE(aRopes); i++) + if(aRopes[i].m_bActive) + aRopes[i].Update(); +} + +void +CRopes::Render(void) +{ + int i; + PUSH_RENDERGROUP("CRopes::Render"); + for(i = 0; i < ARRAY_SIZE(aRopes); i++) + if(aRopes[i].m_bActive) + aRopes[i].Render(); + POP_RENDERGROUP(); +} + +bool +CRopes::RegisterRope(uintptr id, CVector pos, bool setUpdateTimer) +{ + int i, j; + for(i = 0; i < ARRAY_SIZE(aRopes); i++){ + if(aRopes[i].m_bActive && aRopes[i].m_id == id){ + aRopes[i].m_pos[0] = pos; + aRopes[i].m_speed[0] = CVector(0.0f, 0.0f, 0.0f); + aRopes[i].m_bWasRegistered = true; + return true; + } + } + for(i = 0; i < ARRAY_SIZE(aRopes); i++) + if(!aRopes[i].m_bActive){ + aRopes[i].m_id = id; + aRopes[i].m_pos[0] = pos; + aRopes[i].m_speed[0] = CVector(0.0f, 0.0f, 0.0f); + aRopes[i].m_unk = false; + aRopes[i].m_bWasRegistered = true; + aRopes[i].m_updateTimer = setUpdateTimer ? CTimer::GetTimeInMilliseconds() + 20000 : 0; + for(j = 1; j < ARRAY_SIZE(aRopes[0].m_pos); j++){ + if(j & 1) + aRopes[i].m_pos[j] = aRopes[i].m_pos[j-1] + CVector(0.0f, 0.0f, 0.625f); + else + aRopes[i].m_pos[j] = aRopes[i].m_pos[j-1] - CVector(0.0f, 0.0f, 0.625f); + aRopes[i].m_speed[j] = CVector(0.0f, 0.0f, 0.0f); + } + aRopes[i].m_bActive = true; + return true; + } + return false; +} + +void +CRopes::SetSpeedOfTopNode(uintptr id, CVector speed) +{ + int i; + for(i = 0; i < ARRAY_SIZE(aRopes); i++) + if(aRopes[i].m_bActive && aRopes[i].m_id == id){ + aRopes[i].m_speed[0] = speed; + return; + } +} + +bool +CRopes::FindCoorsAlongRope(uintptr id, float t, CVector *coors) +{ + int i, j; + float f; + for(i = 0; i < ARRAY_SIZE(aRopes); i++) + if(aRopes[i].m_bActive && aRopes[i].m_id == id){ + t = (ARRAY_SIZE(aRopes[0].m_pos)-1)*Clamp(t, 0.0f, 0.999f); + j = t; + f = t - j; + *coors = (1.0f-f)*aRopes[i].m_pos[j] + f*aRopes[i].m_pos[j+1]; + return true; + } + return false; +} + +bool +CRopes::CreateRopeWithSwatComingDown(CVector pos) +{ + static uint32 ropeId = 0; + + if(!CStreaming::HasModelLoaded(MI_SWAT) || !RegisterRope(ropeId+100, pos, true)) + return false; + CCopPed *swat = (CCopPed*)CPopulation::AddPed(PEDTYPE_COP, COP_HELI_SWAT, pos); + swat->bUsesCollision = false; + swat->m_pRopeEntity = (CEntity*)1; + swat->m_nRopeID = 100 + ropeId; + CAnimManager::BlendAnimation(swat->GetClump(), ASSOCGRP_STD, ANIM_STD_ABSEIL, 4.0f); + ropeId++; + return true; +} diff --git a/src/miami/core/Ropes.h b/src/miami/core/Ropes.h new file mode 100644 index 00000000..7930fa98 --- /dev/null +++ b/src/miami/core/Ropes.h @@ -0,0 +1,31 @@ +#pragma once + +class CRope +{ +public: + bool m_bActive; + bool m_bWasRegistered; + bool m_unk; + uintptr m_id; + uint32 m_updateTimer; + CVector m_pos[32]; + CVector m_speed[32]; + + void Update(void); + void Render(void); +}; + +class CRopes +{ + static CRope aRopes[8]; + +public: + + static void Init(void); + static void Update(void); + static void Render(void); + static bool RegisterRope(uintptr id, CVector pos, bool setUpdateTimer); + static void SetSpeedOfTopNode(uintptr id, CVector speed); + static bool FindCoorsAlongRope(uintptr id, float t, CVector *coors); + static bool CreateRopeWithSwatComingDown(CVector pos); +}; diff --git a/src/miami/core/Stats.cpp b/src/miami/core/Stats.cpp new file mode 100644 index 00000000..8c6137f2 --- /dev/null +++ b/src/miami/core/Stats.cpp @@ -0,0 +1,1443 @@ +#include "common.h" + +#include "Stats.h" +#include "Text.h" +#include "World.h" +#include "Pad.h" +#include "DMAudio.h" +#include "main.h" +#include "Font.h" +#include "Frontend.h" +#include "audio_enums.h" + +#include + +#ifdef USE_PRECISE_MEASUREMENT_CONVERTION +#define MILES_IN_METER 0.000621371192f +#define FEET_IN_METER 3.28084f +#else +#define MILES_IN_METER (1 / 1670.f) +#define FEET_IN_METER 3.33f +#endif + +int32 CStats::SeagullsKilled; +int32 CStats::BoatsExploded; +int32 CStats::WantedStarsAttained; +int32 CStats::WantedStarsEvaded; +int32 CStats::DaysPassed; +int32 CStats::HeadsPopped; +int32 CStats::CommercialPassed; +int32 CStats::IndustrialPassed; +int32 CStats::SuburbanPassed; +int32 CStats::NumberKillFrenziesPassed; +int32 CStats::PeopleKilledByOthers; +int32 CStats::HelisDestroyed; +int32 CStats::PedsKilledOfThisType[NUM_PEDTYPES]; +int32 CStats::TimesDied; +int32 CStats::TimesArrested; +int32 CStats::KillsSinceLastCheckpoint; +float CStats::DistanceTravelledByCar; +float CStats::DistanceTravelledByHelicoptor; +float CStats::DistanceTravelledByBike; +float CStats::DistanceTravelledByBoat; +float CStats::DistanceTravelledByPlane; +float CStats::DistanceTravelledByGolfCart; +float CStats::DistanceTravelledOnFoot; +int32 CStats::FlightTime; +int32 CStats::TimesDrowned; +int32 CStats::PhotosTaken; +float CStats::LoanSharks; +float CStats::StoresKnockedOff; +float CStats::MovieStunts; +float CStats::Assassinations; +float CStats::PizzasDelivered; +float CStats::GarbagePickups; +float CStats::IceCreamSold; +float CStats::TopShootingRangeScore; +float CStats::ShootingRank; +float CStats::ProgressMade; +float CStats::TotalProgressInGame; +int32 CStats::CarsExploded; +int32 CStats::PeopleKilledByPlayer; +float CStats::MaximumJumpDistance; +float CStats::MaximumJumpHeight; +int32 CStats::MaximumJumpFlips; +int32 CStats::MaximumJumpSpins; +int32 CStats::BestStuntJump; +int32 CStats::NumberOfUniqueJumpsFound; +int32 CStats::TotalNumberOfUniqueJumps; +int32 CStats::PassengersDroppedOffWithTaxi; +int32 CStats::MoneyMadeWithTaxi; +int32 CStats::MissionsGiven; +int32 CStats::MissionsPassed; +char CStats::LastMissionPassedName[8]; +int32 CStats::TotalLegitimateKills; +int32 CStats::LivesSavedWithAmbulance; +int32 CStats::CriminalsCaught; +int32 CStats::HighestLevelAmbulanceMission; +int32 CStats::HighestLevelVigilanteMission; +int32 CStats::HighestLevelFireMission; +int32 CStats::FiresExtinguished; +int32 CStats::TotalNumberKillFrenzies; +int32 CStats::TotalNumberMissions; +int32 CStats::RoundsFiredByPlayer; +int32 CStats::KgsOfExplosivesUsed; +int32 CStats::BulletsThatHit; +int32 CStats::BestTimeBombDefusal; +int32 CStats::FastestTimes[CStats::TOTAL_FASTEST_TIMES]; +int32 CStats::HighestScores[CStats::TOTAL_HIGHEST_SCORES]; +int32 CStats::BestPositions[CStats::TOTAL_BEST_POSITIONS]; +bool CStats::PropertyOwned[CStats::TOTAL_PROPERTIES]; +int32 CStats::NumPropertyOwned; +int32 CStats::PropertyDestroyed; +float CStats::HighestChaseValue; +int32 CStats::CheatedCount; +int32 CStats::ShowChaseStatOnScreen; +int32 CStats::PamphletMissionPassed; +bool CStats::abSonyCDs[1]; +int32 CStats::BloodRingKills; +int32 CStats::BloodRingTime; +float CStats::FavoriteRadioStationList[NUM_RADIOS]; + +int32 CStats::Sprayings; +float CStats::AutoPaintingBudget; +int32 CStats::NoMoreHurricanes; +float CStats::FashionBudget; +float CStats::PropertyBudget; +float CStats::WeaponBudget; +int32 CStats::SafeHouseVisits; +int32 CStats::TyresPopped; + +int32 CStats::LongestWheelie; +int32 CStats::LongestStoppie; +int32 CStats::Longest2Wheel; +float CStats::LongestWheelieDist; +float CStats::LongestStoppieDist; +float CStats::Longest2WheelDist; + +void CStats::Init() +{ + PeopleKilledByOthers = 0; + PeopleKilledByPlayer = 0; + CarsExploded = 0; + BoatsExploded = 0; + RoundsFiredByPlayer = 0; + for (int i = 0; i < NUM_PEDTYPES; i++) + PedsKilledOfThisType[i] = 0; + HelisDestroyed = 0; + ProgressMade = 0.0f; + KgsOfExplosivesUsed = 0; + BulletsThatHit = 0; + TyresPopped = 0; + HeadsPopped = 0; + WantedStarsAttained = 0; + WantedStarsEvaded = 0; + TimesArrested = 0; + TimesDied = 0; + DaysPassed = 0; + SafeHouseVisits = 0; + Sprayings = 0; + MaximumJumpDistance = 0; + MaximumJumpHeight = 0; + MaximumJumpFlips = 0; + MaximumJumpSpins = 0; + BestStuntJump = 0; + NumberOfUniqueJumpsFound = 0; + TotalNumberOfUniqueJumps = 0; + MissionsGiven = 0; + MissionsPassed = 0; + PassengersDroppedOffWithTaxi = 0; + MoneyMadeWithTaxi = 0; + DistanceTravelledOnFoot = 0; + DistanceTravelledByCar = 0; + DistanceTravelledByBike = 0; + DistanceTravelledByBoat = 0; + DistanceTravelledByGolfCart = 0; + DistanceTravelledByHelicoptor = 0; +#ifdef FIX_BUGS + DistanceTravelledByPlane = 0; +#endif + LivesSavedWithAmbulance = 0; + CriminalsCaught = 0; + HighestLevelVigilanteMission = 0; + HighestLevelAmbulanceMission = 0; + HighestLevelFireMission = 0; + FiresExtinguished = 0; + PhotosTaken = 0; + NumberKillFrenziesPassed = 0; + TotalNumberKillFrenzies = 0; + TotalNumberMissions = 0; + FlightTime = 0; + TimesDrowned = 0; + SeagullsKilled = 0; + WeaponBudget = 0.0f; + FashionBudget = 0.0f; + LoanSharks = 0.0f; + StoresKnockedOff = 0.0f; + MovieStunts = 0.0f; + Assassinations = 0.0f; + PizzasDelivered = 0.0f; + GarbagePickups = 0.0f; + IceCreamSold = 0.0f; + TopShootingRangeScore = 0.0f; + ShootingRank = 0.0f; + LongestWheelie = 0; + LongestStoppie = 0; + Longest2Wheel = 0; + LongestWheelieDist = 0.0f; + LongestStoppieDist = 0.0f; + Longest2WheelDist = 0.0f; + PropertyBudget = 0.0f; + AutoPaintingBudget = 0.0f; + PropertyDestroyed = 0; + HighestChaseValue = 0.0f; + CheatedCount = 0; + + for (int i = 0; i < TOTAL_FASTEST_TIMES; i++) + FastestTimes[i] = 0; + for (int i = 0; i < TOTAL_HIGHEST_SCORES; i++) + HighestScores[i] = 0; + for (int i = 0; i < TOTAL_BEST_POSITIONS; i++) + BestPositions[i] = INT_MAX; + + KillsSinceLastCheckpoint = 0; + TotalLegitimateKills = 0; + + for (int i = 0; i < ARRAY_SIZE(LastMissionPassedName); i++) + LastMissionPassedName[i] = 0; + + IndustrialPassed = 0; + CommercialPassed = 0; + SuburbanPassed = 0; + PamphletMissionPassed = 0; + NoMoreHurricanes = 0; + ShowChaseStatOnScreen = 0; + for (int i = 0; i < ARRAY_SIZE(abSonyCDs); i++) + abSonyCDs[i] = 0; + PopulateFavoriteRadioStationList(); + + NumPropertyOwned = 0; + for (int i = 0; i < TOTAL_PROPERTIES; i++) + PropertyOwned[i] = false; + + BloodRingKills = 0; + BloodRingTime = 0; +} + +void CStats::RegisterFastestTime(int32 index, int32 time) +{ + assert(index >= 0 && index < TOTAL_FASTEST_TIMES); + if (FastestTimes[index] == 0) + FastestTimes[index] = time; + else + FastestTimes[index] = Min(FastestTimes[index], time); +} + +void CStats::RegisterHighestScore(int32 index, int32 score) +{ + assert(index >= 0 && index < TOTAL_HIGHEST_SCORES); + HighestScores[index] = Max(HighestScores[index], score); +} + +void CStats::RegisterBestPosition(int32 index, int32 position) +{ + assert(index >= 0 && index < TOTAL_BEST_POSITIONS); + BestPositions[index] = Min(BestPositions[index], position); +} + +void CStats::AnotherLifeSavedWithAmbulance() +{ + ++LivesSavedWithAmbulance; +} + +void CStats::AnotherCriminalCaught() +{ + ++CriminalsCaught; +} + +void CStats::RegisterLevelAmbulanceMission(int32 level) +{ + HighestLevelAmbulanceMission = Max(HighestLevelAmbulanceMission, level); +} + +void CStats::RegisterLevelVigilanteMission(int32 level) +{ + HighestLevelVigilanteMission = Max(HighestLevelVigilanteMission, level); +} + +void CStats::RegisterLevelFireMission(int32 level) +{ + HighestLevelFireMission = Max(HighestLevelFireMission, level); +} + +void CStats::AnotherFireExtinguished() +{ + ++FiresExtinguished; +} + +void CStats::AnotherKillFrenzyPassed() +{ + ++NumberKillFrenziesPassed; +} + +void CStats::SetTotalNumberKillFrenzies(int32 total) +{ + TotalNumberKillFrenzies = total; +} + +void CStats::SetTotalNumberMissions(int32 total) +{ + TotalNumberMissions = total; +} + +wchar *CStats::FindCriminalRatingString() +{ + int rating = FindCriminalRatingNumber(); + + if (rating < 0) { + if (rating > -500) return TheText.Get("RATNG53"); + if (rating > -2000) return TheText.Get("RATNG54"); + if (rating > -4000) return TheText.Get("RATNG55"); + if (rating > -6000) return TheText.Get("RATNG56"); + return TheText.Get("RATNG57"); + } + if (rating < 20) return TheText.Get("RATNG1"); + if (rating < 50) return TheText.Get("RATNG2"); + if (rating < 75) return TheText.Get("RATNG3"); + if (rating < 100) return TheText.Get("RATNG4"); + if (rating < 120) return TheText.Get("RATNG5"); + if (rating < 150) return TheText.Get("RATNG6"); + if (rating < 200) return TheText.Get("RATNG7"); + if (rating < 240) return TheText.Get("RATNG8"); + if (rating < 270) return TheText.Get("RATNG9"); + if (rating < 300) return TheText.Get("RATNG10"); + if (rating < 335) return TheText.Get("RATNG11"); + if (rating < 370) return TheText.Get("RATNG12"); + if (rating < 400) return TheText.Get("RATNG13"); + if (rating < 450) return TheText.Get("RATNG14"); + if (rating < 500) return TheText.Get("RATNG15"); + if (rating < 550) return TheText.Get("RATNG16"); + if (rating < 600) return TheText.Get("RATNG17"); + if (rating < 610) return TheText.Get("RATNG18"); + if (rating < 650) return TheText.Get("RATNG19"); + if (rating < 700) return TheText.Get("RATNG20"); + if (rating < 850) return TheText.Get("RATNG21"); + if (rating < 1000) return TheText.Get("RATNG22"); + if (rating < 1005) return TheText.Get("RATNG23"); + if (rating < 1150) return TheText.Get("RATNG24"); + if (rating < 1300) return TheText.Get(TimesArrested > 0 ? "RATNG25" : "RATNG24"); + if (rating < 1500) return TheText.Get("RATNG26"); + if (rating < 1700) return TheText.Get("RATNG27"); + if (rating < 2000) return TheText.Get("RATNG28"); + if (rating < 2100) return TheText.Get("RATNG29"); + if (rating < 2300) return TheText.Get("RATNG30"); + if (rating < 2500) return TheText.Get("RATNG31"); + if (rating < 2750) return TheText.Get("RATNG32"); + if (rating < 3000) return TheText.Get("RATNG33"); + if (rating < 3500) return TheText.Get("RATNG34"); + if (rating < 4000) return TheText.Get("RATNG35"); + if (rating < 5000) return TheText.Get("RATNG36"); + if (rating < 7500) return TheText.Get("RATNG37"); + if (rating < 10000) return TheText.Get("RATNG38"); + if (rating < 20000) return TheText.Get("RATNG39"); + if (rating < 30000) return TheText.Get("RATNG40"); + if (rating < 40000) return TheText.Get("RATNG41"); + if (rating < 50000) return TheText.Get("RATNG42"); + if (rating < 65000) return TheText.Get("RATNG43"); + if (rating < 80000) return TheText.Get("RATNG44"); + if (rating < 100000) return TheText.Get("RATNG45"); + if (rating < 150000) return TheText.Get("RATNG46"); + if (rating < 200000) return TheText.Get("RATNG47"); + if (rating < 300000) return TheText.Get("RATNG48"); + if (rating < 375000) return TheText.Get("RATNG49"); + if (rating < 500000) return TheText.Get(FlightTime / 60000 / 60 > 10 ? "RATNG50" : "RATNG49"); + if (rating < 1000000) return TheText.Get("RATNG51"); + return TheText.Get(CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney > 10000000 ? "RATNG52" : "RATNG51"); +} + +wchar *CStats::FindChaseString(float fMediaLevel) { + if (fMediaLevel < 20.0f) return TheText.Get("CHASE1"); + if (fMediaLevel < 50.0f) return TheText.Get("CHASE2"); + if (fMediaLevel < 75.0f) return TheText.Get("CHASE3"); + if (fMediaLevel < 100.0f) return TheText.Get("CHASE4"); + if (fMediaLevel < 150.0f) return TheText.Get("CHASE5"); + if (fMediaLevel < 200.0f) return TheText.Get("CHASE6"); + if (fMediaLevel < 250.0f) return TheText.Get("CHASE7"); + if (fMediaLevel < 300.0f) return TheText.Get("CHASE8"); + if (fMediaLevel < 350.0f) return TheText.Get("CHASE9"); + if (fMediaLevel < 400.0f) return TheText.Get("CHASE10"); + if (fMediaLevel < 500.0f) return TheText.Get("CHASE11"); + if (fMediaLevel < 600.0f) return TheText.Get("CHASE12"); + if (fMediaLevel < 700.0f) return TheText.Get("CHASE13"); + if (fMediaLevel < 800.0f) return TheText.Get("CHASE14"); + if (fMediaLevel < 900.0f) return TheText.Get("CHASE15"); + if (fMediaLevel < 1000.0f) return TheText.Get("CHASE16"); + if (fMediaLevel < 1200.0f) return TheText.Get("CHASE17"); + if (fMediaLevel < 1400.0f) return TheText.Get("CHASE18"); + if (fMediaLevel < 1600.0f) return TheText.Get("CHASE19"); + if (fMediaLevel < 1800.0f) return TheText.Get("CHASE20"); + return TheText.Get("CHASE21"); +} + +int32 CStats::FindCriminalRatingNumber() +{ + int32 rating; + + rating = FiresExtinguished + 10 * HighestLevelFireMission + 10 * HighestLevelAmbulanceMission + + CriminalsCaught + LivesSavedWithAmbulance + + 30 * HelisDestroyed + TotalLegitimateKills - 3 * TimesArrested - 3 * TimesDied + + CWorld::Players[CWorld::PlayerInFocus].m_nMoney / 5000; + if (CPad::bHasPlayerCheated || CheatedCount > 0) { + rating -= CheatedCount; + if (rating <= -10000) + rating = -10000; + + } else if (rating <= 0) { + rating = 0; + } + + if (RoundsFiredByPlayer > 100) + rating += (float)BulletsThatHit / (float)RoundsFiredByPlayer * 500.0f; + if (TotalProgressInGame) + rating += ProgressMade / TotalProgressInGame * 1000.0f; + return rating; +} + +float CStats::GetPercentageProgress() +{ + float percentCompleted = (TotalProgressInGame == 0.f ? 0.f : + 100.0f * ProgressMade / (CGame::nastyGame ? TotalProgressInGame : TotalProgressInGame - 1.0f)); + + return Min(percentCompleted, 100.0f); +} + +void CStats::MoneySpentOnWeapons(int32 money) +{ + WeaponBudget += money; +} + +void CStats::MoneySpentOnProperty(int32 money) +{ + PropertyBudget += money; +} + +void CStats::MoneySpentOnAutoPainting(int32 money) +{ + AutoPaintingBudget += money; +} + +void CStats::MoneySpentOnFashion(int32 money) +{ + FashionBudget += money; +} + +void CStats::NumOfVisitsFromLoanSharks(int32 num) +{ + LoanSharks += num; +} + +void CStats::NumOfStoresKnockedOff(int32 num) +{ + StoresKnockedOff += num; +} + +void CStats::NumOfMovieStunts(int32 num) +{ + MovieStunts += num; +} + +void CStats::NumOfAssassinations(int32 num) +{ + Assassinations += num; +} + +void CStats::NumOfPizzasDelivered(int32 num) +{ + PizzasDelivered += num; +} + +void CStats::NumOfGarbagePickups(int32 num) +{ + GarbagePickups += num; +} + +void CStats::NumOfIceCreamSold(int32 num) +{ + IceCreamSold += num; +} + +void CStats::AddNumBloodRingKills(int32 num) +{ + BloodRingKills += num; +} + +void CStats::LongestTimeInBloodRing(int32 time) +{ + if (BloodRingTime < time) + BloodRingTime = time; +} + +void CStats::AddPropertyAsOwned(int32 id) +{ + if (!PropertyOwned[id]) { + PropertyOwned[id] = true; + ++NumPropertyOwned; + } +} + +float CStats::GetFavoriteRadioStationList(int32 station) +{ + return FavoriteRadioStationList[station]; +} + +void CStats::SaveStats(uint8 *buf, uint32 *size) +{ + CheckPointReachedSuccessfully(); + uint8 *buf_start = buf; + *size = sizeof(PeopleKilledByPlayer) + + sizeof(PeopleKilledByOthers) + + sizeof(CarsExploded) + + sizeof(BoatsExploded) + + sizeof(TyresPopped) + + sizeof(RoundsFiredByPlayer) + + sizeof(PedsKilledOfThisType) + + sizeof(HelisDestroyed) + + sizeof(ProgressMade) + + sizeof(TotalProgressInGame) + + sizeof(KgsOfExplosivesUsed) + + sizeof(BulletsThatHit) + + sizeof(HeadsPopped) + + sizeof(WantedStarsAttained) + + sizeof(WantedStarsEvaded) + + sizeof(TimesArrested) + + sizeof(TimesDied) + + sizeof(DaysPassed) + + sizeof(SafeHouseVisits) + + sizeof(Sprayings) + + sizeof(MaximumJumpDistance) + + sizeof(MaximumJumpHeight) + + sizeof(MaximumJumpFlips) + + sizeof(MaximumJumpSpins) + + sizeof(BestStuntJump) + + sizeof(NumberOfUniqueJumpsFound) + + sizeof(TotalNumberOfUniqueJumps) + + sizeof(MissionsGiven) + + sizeof(PassengersDroppedOffWithTaxi) + + sizeof(MoneyMadeWithTaxi) + + sizeof(IndustrialPassed) + + sizeof(CommercialPassed) + + sizeof(SuburbanPassed) + + sizeof(PamphletMissionPassed) + + sizeof(NoMoreHurricanes) + + sizeof(DistanceTravelledOnFoot) + + sizeof(DistanceTravelledByCar) + + sizeof(DistanceTravelledByBike) + + sizeof(DistanceTravelledByBoat) + + sizeof(DistanceTravelledByGolfCart) + + sizeof(DistanceTravelledByHelicoptor) + + sizeof(DistanceTravelledByPlane) + + sizeof(LivesSavedWithAmbulance) + + sizeof(CriminalsCaught) + + sizeof(FiresExtinguished) + + sizeof(HighestLevelVigilanteMission) + + sizeof(HighestLevelAmbulanceMission) + + sizeof(HighestLevelFireMission) + + sizeof(PhotosTaken) + + sizeof(NumberKillFrenziesPassed) + + sizeof(TotalNumberKillFrenzies) + + sizeof(TotalNumberMissions) + + sizeof(FlightTime) + + sizeof(TimesDrowned) + + sizeof(SeagullsKilled) + + sizeof(WeaponBudget) + + sizeof(FashionBudget) + + sizeof(LoanSharks) + + sizeof(StoresKnockedOff) + + sizeof(MovieStunts) + + sizeof(Assassinations) + + sizeof(PizzasDelivered) + + sizeof(GarbagePickups) + + sizeof(IceCreamSold) + + sizeof(TopShootingRangeScore) + + sizeof(ShootingRank) + + sizeof(LongestWheelie) + + sizeof(LongestStoppie) + + sizeof(Longest2Wheel) + + sizeof(LongestWheelieDist) + + sizeof(LongestStoppieDist) + + sizeof(Longest2WheelDist) + + sizeof(PropertyBudget) + + sizeof(AutoPaintingBudget) + + sizeof(PropertyDestroyed) + + sizeof(NumPropertyOwned) + + sizeof(BloodRingKills) + + sizeof(BloodRingTime) + + sizeof(PropertyOwned) + + sizeof(HighestChaseValue) + + sizeof(FastestTimes) + + sizeof(HighestScores) + + sizeof(BestPositions) + + sizeof(KillsSinceLastCheckpoint) + + sizeof(TotalLegitimateKills) + + sizeof(LastMissionPassedName) + + sizeof(CheatedCount) + + sizeof(FavoriteRadioStationList); + +#define CopyToBuf(buf, data) memcpy(buf, &data, sizeof(data)); buf += sizeof(data); + CopyToBuf(buf, PeopleKilledByPlayer); + CopyToBuf(buf, PeopleKilledByOthers); + CopyToBuf(buf, CarsExploded); + CopyToBuf(buf, BoatsExploded); + CopyToBuf(buf, TyresPopped); + CopyToBuf(buf, RoundsFiredByPlayer); + CopyToBuf(buf, PedsKilledOfThisType); + CopyToBuf(buf, HelisDestroyed); + CopyToBuf(buf, ProgressMade); + CopyToBuf(buf, TotalProgressInGame); + CopyToBuf(buf, KgsOfExplosivesUsed); + CopyToBuf(buf, BulletsThatHit); + CopyToBuf(buf, HeadsPopped); + CopyToBuf(buf, WantedStarsAttained); + CopyToBuf(buf, WantedStarsEvaded); + CopyToBuf(buf, TimesArrested); + CopyToBuf(buf, TimesDied); + CopyToBuf(buf, DaysPassed); + CopyToBuf(buf, SafeHouseVisits); + CopyToBuf(buf, Sprayings); + CopyToBuf(buf, MaximumJumpDistance); + CopyToBuf(buf, MaximumJumpHeight); + CopyToBuf(buf, MaximumJumpFlips); + CopyToBuf(buf, MaximumJumpSpins); + CopyToBuf(buf, BestStuntJump); + CopyToBuf(buf, NumberOfUniqueJumpsFound); + CopyToBuf(buf, TotalNumberOfUniqueJumps); + CopyToBuf(buf, MissionsGiven); + CopyToBuf(buf, PassengersDroppedOffWithTaxi); + CopyToBuf(buf, MoneyMadeWithTaxi); + CopyToBuf(buf, IndustrialPassed); + CopyToBuf(buf, CommercialPassed); + CopyToBuf(buf, SuburbanPassed); + CopyToBuf(buf, PamphletMissionPassed); + CopyToBuf(buf, NoMoreHurricanes); + CopyToBuf(buf, DistanceTravelledOnFoot); + CopyToBuf(buf, DistanceTravelledByCar); + CopyToBuf(buf, DistanceTravelledByBike); + CopyToBuf(buf, DistanceTravelledByBoat); + CopyToBuf(buf, DistanceTravelledByGolfCart); + CopyToBuf(buf, DistanceTravelledByHelicoptor); + CopyToBuf(buf, DistanceTravelledByPlane); + CopyToBuf(buf, LivesSavedWithAmbulance); + CopyToBuf(buf, CriminalsCaught); + CopyToBuf(buf, FiresExtinguished); + CopyToBuf(buf, HighestLevelVigilanteMission); + CopyToBuf(buf, HighestLevelAmbulanceMission); + CopyToBuf(buf, HighestLevelFireMission); + CopyToBuf(buf, PhotosTaken); + CopyToBuf(buf, NumberKillFrenziesPassed); + CopyToBuf(buf, TotalNumberKillFrenzies); + CopyToBuf(buf, TotalNumberMissions); + CopyToBuf(buf, FlightTime); + CopyToBuf(buf, TimesDrowned); + CopyToBuf(buf, SeagullsKilled); + CopyToBuf(buf, WeaponBudget); + CopyToBuf(buf, FashionBudget); + CopyToBuf(buf, LoanSharks); + CopyToBuf(buf, StoresKnockedOff); + CopyToBuf(buf, MovieStunts); + CopyToBuf(buf, Assassinations); + CopyToBuf(buf, PizzasDelivered); + CopyToBuf(buf, GarbagePickups); + CopyToBuf(buf, IceCreamSold); + CopyToBuf(buf, TopShootingRangeScore); + CopyToBuf(buf, ShootingRank); + CopyToBuf(buf, LongestWheelie); + CopyToBuf(buf, LongestStoppie); + CopyToBuf(buf, Longest2Wheel); + CopyToBuf(buf, LongestWheelieDist); + CopyToBuf(buf, LongestStoppieDist); + CopyToBuf(buf, Longest2WheelDist); + CopyToBuf(buf, PropertyBudget); + CopyToBuf(buf, AutoPaintingBudget); + CopyToBuf(buf, PropertyDestroyed); + CopyToBuf(buf, NumPropertyOwned); + CopyToBuf(buf, BloodRingKills); + CopyToBuf(buf, BloodRingTime); + CopyToBuf(buf, PropertyOwned); + CopyToBuf(buf, HighestChaseValue); + CopyToBuf(buf, FastestTimes); + CopyToBuf(buf, HighestScores); + CopyToBuf(buf, BestPositions); + CopyToBuf(buf, KillsSinceLastCheckpoint); + CopyToBuf(buf, TotalLegitimateKills); + CopyToBuf(buf, LastMissionPassedName); + CopyToBuf(buf, CheatedCount); + PopulateFavoriteRadioStationList(); + CopyToBuf(buf, FavoriteRadioStationList); + + assert(buf - buf_start == *size); +#undef CopyToBuf +} + +void CStats::LoadStats(uint8 *buf, uint32 size) +{ + uint8* buf_start = buf; + +#define CopyFromBuf(buf, data) memcpy(&data, buf, sizeof(data)); buf += sizeof(data); + + CopyFromBuf(buf, PeopleKilledByPlayer); + CopyFromBuf(buf, PeopleKilledByOthers); + CopyFromBuf(buf, CarsExploded); + CopyFromBuf(buf, BoatsExploded); + CopyFromBuf(buf, TyresPopped); + CopyFromBuf(buf, RoundsFiredByPlayer); + CopyFromBuf(buf, PedsKilledOfThisType); + CopyFromBuf(buf, HelisDestroyed); + CopyFromBuf(buf, ProgressMade); + CopyFromBuf(buf, TotalProgressInGame); + CopyFromBuf(buf, KgsOfExplosivesUsed); + CopyFromBuf(buf, BulletsThatHit); + CopyFromBuf(buf, HeadsPopped); + CopyFromBuf(buf, WantedStarsAttained); + CopyFromBuf(buf, WantedStarsEvaded); + CopyFromBuf(buf, TimesArrested); + CopyFromBuf(buf, TimesDied); + CopyFromBuf(buf, DaysPassed); + CopyFromBuf(buf, SafeHouseVisits); + CopyFromBuf(buf, Sprayings); + CopyFromBuf(buf, MaximumJumpDistance); + CopyFromBuf(buf, MaximumJumpHeight); + CopyFromBuf(buf, MaximumJumpFlips); + CopyFromBuf(buf, MaximumJumpSpins); + CopyFromBuf(buf, BestStuntJump); + CopyFromBuf(buf, NumberOfUniqueJumpsFound); + CopyFromBuf(buf, TotalNumberOfUniqueJumps); + CopyFromBuf(buf, MissionsGiven); + CopyFromBuf(buf, PassengersDroppedOffWithTaxi); + CopyFromBuf(buf, MoneyMadeWithTaxi); + CopyFromBuf(buf, IndustrialPassed); + CopyFromBuf(buf, CommercialPassed); + CopyFromBuf(buf, SuburbanPassed); + CopyFromBuf(buf, PamphletMissionPassed); + CopyFromBuf(buf, NoMoreHurricanes); + CopyFromBuf(buf, DistanceTravelledOnFoot); + CopyFromBuf(buf, DistanceTravelledByCar); + CopyFromBuf(buf, DistanceTravelledByBike); + CopyFromBuf(buf, DistanceTravelledByBoat); + CopyFromBuf(buf, DistanceTravelledByGolfCart); + CopyFromBuf(buf, DistanceTravelledByHelicoptor); + CopyFromBuf(buf, DistanceTravelledByPlane); + CopyFromBuf(buf, LivesSavedWithAmbulance); + CopyFromBuf(buf, CriminalsCaught); + CopyFromBuf(buf, FiresExtinguished); + CopyFromBuf(buf, HighestLevelVigilanteMission); + CopyFromBuf(buf, HighestLevelAmbulanceMission); + CopyFromBuf(buf, HighestLevelFireMission); + CopyFromBuf(buf, PhotosTaken); + CopyFromBuf(buf, NumberKillFrenziesPassed); + CopyFromBuf(buf, TotalNumberKillFrenzies); + CopyFromBuf(buf, TotalNumberMissions); + CopyFromBuf(buf, FlightTime); + CopyFromBuf(buf, TimesDrowned); + CopyFromBuf(buf, SeagullsKilled); + CopyFromBuf(buf, WeaponBudget); + CopyFromBuf(buf, FashionBudget); + CopyFromBuf(buf, LoanSharks); + CopyFromBuf(buf, StoresKnockedOff); + CopyFromBuf(buf, MovieStunts); + CopyFromBuf(buf, Assassinations); + CopyFromBuf(buf, PizzasDelivered); + CopyFromBuf(buf, GarbagePickups); + CopyFromBuf(buf, IceCreamSold); + CopyFromBuf(buf, TopShootingRangeScore); + CopyFromBuf(buf, ShootingRank); + CopyFromBuf(buf, LongestWheelie); + CopyFromBuf(buf, LongestStoppie); + CopyFromBuf(buf, Longest2Wheel); + CopyFromBuf(buf, LongestWheelieDist); + CopyFromBuf(buf, LongestStoppieDist); + CopyFromBuf(buf, Longest2WheelDist); + CopyFromBuf(buf, PropertyBudget); + CopyFromBuf(buf, AutoPaintingBudget); + CopyFromBuf(buf, PropertyDestroyed); + CopyFromBuf(buf, NumPropertyOwned); + CopyFromBuf(buf, BloodRingKills); + CopyFromBuf(buf, BloodRingTime); + CopyFromBuf(buf, PropertyOwned); + CopyFromBuf(buf, HighestChaseValue); + CopyFromBuf(buf, FastestTimes); + CopyFromBuf(buf, HighestScores); + CopyFromBuf(buf, BestPositions); + CopyFromBuf(buf, KillsSinceLastCheckpoint); + CopyFromBuf(buf, TotalLegitimateKills); + CopyFromBuf(buf, LastMissionPassedName); + CopyFromBuf(buf, CheatedCount); + CopyFromBuf(buf, FavoriteRadioStationList); + + assert(buf - buf_start == size); +#undef CopyFromBuf +} + +void +CStats::PopulateFavoriteRadioStationList() +{ + float* pListenTimeArray = DMAudio.GetListenTimeArray(); + for (int i = 0; i < NUM_RADIOS; i++) + FavoriteRadioStationList[i] = pListenTimeArray[i]; +} + +void +CStats::BuildStatLine(Const char *text, void *stat, int displayType, void *stat2, int isTime) +{ +#define STAT_D *(int*)stat +#define STAT_F *(float*)stat +#define STAT2_D *(int*)stat2 +#define STAT2_F *(float*)stat2 + if (!text) + return; + + gString2[0] = '\0'; + if (isTime == 1) { + if (*((int*)stat2) >= 10) + sprintf(gString2, " %d:%d", STAT_D, STAT2_D); + else + sprintf(gString2, " %d:0%d", STAT_D, STAT2_D); + + } else if (stat2) { +#ifdef MORE_LANGUAGES + if (CFont::IsJapanese()) { + switch (displayType) { + case 0: + case 4: + sprintf(gString2, " %d/%d", STAT_D, STAT2_D); + break; + case 1: + sprintf(gString2, " %.2f/%.2f", STAT_F, STAT2_F); + break; + case 2: + sprintf(gString2, " %d%%/%d%%", STAT_D, STAT2_D); + break; + case 3: + sprintf(gString2, " $%.2f/$%.2f", STAT_F, STAT2_F); + break; + default: + break; + } + } else +#endif + { + switch (displayType) { + case 0: + sprintf(gString2, " %d %s %d", STAT_D, UnicodeToAscii(TheText.Get("FEST_OO")), STAT2_D); + break; + case 1: + sprintf(gString2, " %.2f %s %.2f", STAT_F, UnicodeToAscii(TheText.Get("FEST_OO")), STAT2_F); + break; + case 2: + sprintf(gString2, " %d%% %s %d%%", STAT_D, UnicodeToAscii(TheText.Get("FEST_OO")), STAT2_D); + break; + case 3: + sprintf(gString2, " $%.2f %s $%.2f", STAT_F, UnicodeToAscii(TheText.Get("FEST_OO")), STAT2_F); + break; + case 4: + sprintf(gString2, " %d_ %s %d_", STAT_D, UnicodeToAscii(TheText.Get("FEST_OO")), STAT2_D); + break; + default: + break; + } + } + } else if (stat) { + switch (displayType) { + case 0: + sprintf(gString2, "%d", STAT_D); + break; + case 1: + sprintf(gString2, "%.2f", STAT_F); + break; + case 2: + sprintf(gString2, "%d%%", STAT_D); + break; + case 3: + sprintf(gString2, "$%.2f", STAT_F); + break; + case 4: +#ifdef MORE_LANGUAGES + if (CFont::IsJapanese()) + sprintf(gString2, "%d", STAT_D); + else +#endif + sprintf(gString2, "%d_", STAT_D); + break; + default: + break; + } + } + UnicodeStrcpy(gUString, TheText.Get(text)); + CFont::FilterOutTokensFromString(gUString); + AsciiToUnicode(gString2, gUString2); +#undef STAT_D +#undef STAT_F +#undef STAT2_D +#undef STAT2_F +} + +// rowIdx 99999 returns total numbers of rows. otherwise it returns 0. +int +CStats::ConstructStatLine(int rowIdx) +{ + +#define STAT_LINE_1(varType, left, right1, type) \ + do { \ + if(counter == rowIdx){ \ + varType a = right1; \ + BuildStatLine(left, &a, type, nil, 0); \ + return 0; \ + } counter++; \ + } while(0) + +#define STAT_LINE_2(varType, left, right1, type, right2, time) \ + do { \ + if(counter == rowIdx){ \ + varType a = right1; \ + varType b = right2; \ + BuildStatLine(left, &a, type, &b, time); \ + return 0; \ + } counter++; \ + } while(0) + +#define TEXT_ON_LEFT_GXT(name) \ + do { \ + if(counter == rowIdx){ \ + BuildStatLine(name, nil, 0, nil, 0); \ + return 0; \ + } counter++; \ + } while(0) + +#define TEXT_ON_RIGHT(text) \ + do { \ + if(counter == rowIdx){ \ + gUString[0] = '\0'; \ + UnicodeStrcpy(gUString2, text); \ + return 0; \ + } counter++; \ + } while(0) + +#define FASTEST_TIME(id, str) \ + do { \ + if(FastestTimes[id]) { \ + if(counter == rowIdx){ \ + int hour = 0, minute; \ + for (int i = FastestTimes[id]; i > 59; i -= 60) hour++; \ + for (minute = FastestTimes[id]; minute > 59; minute -= 60); \ + if (minute < 0) minute = -minute; \ + BuildStatLine(str, &hour, 0, &minute, 1); \ + return 0; \ + } \ + counter++; \ + } \ + } while(0) + + switch (rowIdx) { + case 0: { + int percentCompleted = GetPercentageProgress(); + BuildStatLine("PER_COM", &percentCompleted, 2, nil, 0); + return 0; + } + case 1: { + BuildStatLine("NMISON", &MissionsGiven, 0, nil, 0); + return 0; + } + case 2: { + int hour = (CTimer::GetTimeInMilliseconds() / 60000) / 60; + int minute = (CTimer::GetTimeInMilliseconds() / 60000) % 60; + BuildStatLine("ST_TIME", &hour, 0, &minute, 1); + return 0; + } + case 3: { + BuildStatLine("DAYSPS", &DaysPassed, 0, nil, 0); + return 0; + } + case 4: { + BuildStatLine("NUMSHV", &SafeHouseVisits, 0, nil, 0); + return 0; + } + } + int counter = 5; + + if (CGame::nastyGame) { + STAT_LINE_2(int, "FEST_RP", NumberKillFrenziesPassed, 0, TotalNumberKillFrenzies, 0); + } + + CPlayerInfo &player = CWorld::Players[CWorld::PlayerInFocus]; + + // Hidden packages shouldn't be shown with percent +#ifdef FIX_BUGS + STAT_LINE_2(int, "PERPIC", player.m_nCollectedPackages, 0, player.m_nTotalPackages, 0); +#else + float fPackagesPercent = 0.0f; + if (player.m_nTotalPackages != 0) + fPackagesPercent = player.m_nCollectedPackages * 100.0f / player.m_nTotalPackages; + + STAT_LINE_2(int, "PERPIC", fPackagesPercent, 0, 100, 0); +#endif + + if (CGame::nastyGame) { + STAT_LINE_1(int, "PE_WAST", PeopleKilledByPlayer, 0); + STAT_LINE_1(int, "PE_WSOT", PeopleKilledByOthers, 0); + } + STAT_LINE_1(int, "CAR_EXP", CarsExploded, 0); + STAT_LINE_1(int, "BOA_EXP", BoatsExploded, 0); + STAT_LINE_1(int, "HEL_DST", HelisDestroyed, 0); + STAT_LINE_1(int, "TYREPOP", TyresPopped, 0); + STAT_LINE_1(int, "ST_STAR", WantedStarsAttained, 0); + STAT_LINE_1(int, "ST_STGN", WantedStarsEvaded, 0); + STAT_LINE_1(int, "TM_BUST", TimesArrested, 0); + STAT_LINE_1(int, "TM_DED", TimesDied, 0); + +#ifdef MORE_LANGUAGES + // JP version removed it altogether actually + if (!CFont::IsJapanese()) +#endif + STAT_LINE_1(int, "ST_HEAD", HeadsPopped, 0); + + static uint32 lastProcessedDay = UINT32_MAX; + static uint32 lastPoliceSpending = 0; + + // What a random stat... + if (lastProcessedDay != DaysPassed) { + lastProcessedDay = DaysPassed; + lastPoliceSpending = (CTimer::GetTimeInMilliseconds() & 255 + 80) * 255.44f; + } + STAT_LINE_1(float, "DAYPLC", lastPoliceSpending, 3); + + int mostPatheticGang = 0; + int mostKill = 0; + for (int i = PEDTYPE_GANG1; i < PEDTYPE_GANG9; ++i) { + if (CStats::PedsKilledOfThisType[i] > mostKill) { + mostKill = CStats::PedsKilledOfThisType[i]; + mostPatheticGang = i; + } + } + if (mostPatheticGang > 0) { + TEXT_ON_LEFT_GXT("ST_GANG"); + + switch (mostPatheticGang) { + case PEDTYPE_GANG1: + TEXT_ON_RIGHT(TheText.Get("ST_GNG1")); + break; + case PEDTYPE_GANG2: + TEXT_ON_RIGHT(TheText.Get("ST_GNG2")); + break; + case PEDTYPE_GANG3: + TEXT_ON_RIGHT(TheText.Get("ST_GNG3")); + break; + case PEDTYPE_GANG4: + TEXT_ON_RIGHT(TheText.Get("ST_GNG4")); + break; + case PEDTYPE_GANG5: + TEXT_ON_RIGHT(TheText.Get("ST_GNG5")); + break; + case PEDTYPE_GANG6: + TEXT_ON_RIGHT(TheText.Get("ST_GNG6")); + break; + case PEDTYPE_GANG7: + TEXT_ON_RIGHT(TheText.Get("ST_GNG7")); + break; + case PEDTYPE_GANG8: + TEXT_ON_RIGHT(TheText.Get("ST_GNG8")); + break; + default: + break; + } + } + + STAT_LINE_1(int, "GNG_WST", PedsKilledOfThisType[PEDTYPE_GANG9] + PedsKilledOfThisType[PEDTYPE_GANG8] + + PedsKilledOfThisType[PEDTYPE_GANG7] + PedsKilledOfThisType[PEDTYPE_GANG6] + + PedsKilledOfThisType[PEDTYPE_GANG5] + PedsKilledOfThisType[PEDTYPE_GANG4] + + PedsKilledOfThisType[PEDTYPE_GANG3] + PedsKilledOfThisType[PEDTYPE_GANG2] + + PedsKilledOfThisType[PEDTYPE_GANG1], 0); + + STAT_LINE_1(int, "DED_CRI", PedsKilledOfThisType[PEDTYPE_CRIMINAL], 0); + STAT_LINE_1(int, "KGS_EXP", KgsOfExplosivesUsed, 0); + STAT_LINE_1(int, "BUL_FIR", RoundsFiredByPlayer, 0); + STAT_LINE_1(int, "BUL_HIT", BulletsThatHit, 0); +; + STAT_LINE_1(int, "ACCURA", RoundsFiredByPlayer == 0 ? 0 : (BulletsThatHit * 100.0f / (float)RoundsFiredByPlayer), 2); + + switch (FrontEndMenuManager.m_PrefsLanguage) { + case CMenuManager::LANGUAGE_AMERICAN: +#ifndef USE_MEASUREMENTS_IN_METERS + STAT_LINE_1(float, "FEST_DF", DistanceTravelledOnFoot * MILES_IN_METER, 1); + STAT_LINE_1(float, "FEST_DC", DistanceTravelledByCar * MILES_IN_METER, 1); + STAT_LINE_1(float, "DISTBIK", DistanceTravelledByBike * MILES_IN_METER, 1); + STAT_LINE_1(float, "DISTBOA", DistanceTravelledByBoat * MILES_IN_METER, 1); + STAT_LINE_1(float, "DISTGOL", DistanceTravelledByGolfCart * MILES_IN_METER, 1); + STAT_LINE_1(float, "DISTHEL", DistanceTravelledByHelicoptor * MILES_IN_METER, 1); +#ifdef FIX_BUGS + STAT_LINE_1(float, "TOT_DIS", (DistanceTravelledOnFoot + DistanceTravelledByCar + DistanceTravelledByBoat + DistanceTravelledByBike + + DistanceTravelledByGolfCart + DistanceTravelledByHelicoptor + DistanceTravelledByPlane) * MILES_IN_METER, 1); + STAT_LINE_1(float, "MXCARD", MaximumJumpDistance * FEET_IN_METER, 1); + STAT_LINE_1(float, "MXCARJ", MaximumJumpHeight * FEET_IN_METER, 1); +#else + STAT_LINE_1(float, "TOT_DIS", (DistanceTravelledOnFoot + DistanceTravelledByCar + DistanceTravelledByBoat + DistanceTravelledByBike + + DistanceTravelledByGolfCart + DistanceTravelledByHelicoptor) * MILES_IN_METER, 1); +#endif + break; +#endif + case CMenuManager::LANGUAGE_FRENCH: + case CMenuManager::LANGUAGE_GERMAN: + case CMenuManager::LANGUAGE_ITALIAN: + case CMenuManager::LANGUAGE_SPANISH: +#ifdef MORE_LANGUAGES + case CMenuManager::LANGUAGE_POLISH: + case CMenuManager::LANGUAGE_RUSSIAN: + case CMenuManager::LANGUAGE_JAPANESE: +#endif + STAT_LINE_1(float, "FESTDFM", DistanceTravelledOnFoot, 1); + STAT_LINE_1(float, "FESTDCM", DistanceTravelledByCar, 1); + STAT_LINE_1(float, "DISTBIM", DistanceTravelledByBike, 1); + STAT_LINE_1(float, "DISTBOM", DistanceTravelledByBoat, 1); + STAT_LINE_1(float, "DISTGOM", DistanceTravelledByGolfCart, 1); + STAT_LINE_1(float, "DISTHEM", DistanceTravelledByHelicoptor, 1); +#ifdef FIX_BUGS + STAT_LINE_1(float, "TOTDISM", DistanceTravelledOnFoot + DistanceTravelledByCar + DistanceTravelledByBoat + + DistanceTravelledByBike + DistanceTravelledByGolfCart + DistanceTravelledByHelicoptor + DistanceTravelledByPlane, 1); + STAT_LINE_1(float, "MXCARDM", MaximumJumpDistance, 1); + STAT_LINE_1(float, "MXCARJM", MaximumJumpHeight, 1); +#else + STAT_LINE_1(float, "TOTDISM", DistanceTravelledOnFoot + DistanceTravelledByCar + DistanceTravelledByBoat + + DistanceTravelledByGolfCart + DistanceTravelledByHelicoptor, 1); +#endif + break; + default: + break; + } + + // They were selecting the unit according to language in III, but they deleted the feet code in VC. Weird +#ifndef FIX_BUGS + STAT_LINE_1(float, "MXCARDM", MaximumJumpDistance, 1); + STAT_LINE_1(float, "MXCARJM", MaximumJumpHeight, 1); +#endif + STAT_LINE_1(int, "MXFLIP", MaximumJumpFlips, 0); + STAT_LINE_2(int, "NOUNIF", NumberOfUniqueJumpsFound, 0, TotalNumberOfUniqueJumps, 0); + STAT_LINE_1(int, "MXJUMP", MaximumJumpSpins, 4); + + TEXT_ON_LEFT_GXT("BSTSTU"); + switch (BestStuntJump) { + case 1: + TEXT_ON_RIGHT(TheText.Get("INSTUN")); + break; + case 2: + TEXT_ON_RIGHT(TheText.Get("PRINST")); + break; + case 3: + TEXT_ON_RIGHT(TheText.Get("DBINST")); + break; + case 4: + TEXT_ON_RIGHT(TheText.Get("DBPINS")); + break; + case 5: + TEXT_ON_RIGHT(TheText.Get("TRINST")); + break; + case 6: + TEXT_ON_RIGHT(TheText.Get("PRTRST")); + break; + case 7: + TEXT_ON_RIGHT(TheText.Get("QUINST")); + break; + case 8: + TEXT_ON_RIGHT(TheText.Get("PQUINS")); + break; + default: + TEXT_ON_RIGHT(TheText.Get("NOSTUC")); + break; + } + STAT_LINE_1(int, "ST_WHEE", LongestWheelie, 0); + STAT_LINE_1(float, "ST_WHED", LongestWheelieDist, 1); + STAT_LINE_1(int, "ST_STOP", LongestStoppie, 0); + STAT_LINE_1(float, "ST_STOD", LongestStoppieDist, 1); + STAT_LINE_1(int, "ST_2WHE", Longest2Wheel, 0); + STAT_LINE_1(float, "ST_2WHD", Longest2WheelDist, 1); + + if (LoanSharks > 0.0f) + STAT_LINE_1(int, "ST_LOAN", LoanSharks, 0); + + STAT_LINE_1(int, "FEST_CC", CriminalsCaught, 0); + STAT_LINE_1(int, "FEST_HV", HighestLevelVigilanteMission, 0); + STAT_LINE_1(int, "PASDRO", PassengersDroppedOffWithTaxi, 0); + STAT_LINE_1(float, "MONTAX", MoneyMadeWithTaxi, 3); + STAT_LINE_1(int, "FEST_LS", LivesSavedWithAmbulance, 0); + STAT_LINE_1(int, "FEST_HA", HighestLevelAmbulanceMission, 0); + STAT_LINE_1(int, "FEST_FE", FiresExtinguished, 0); + STAT_LINE_1(int, "FIRELVL", HighestLevelFireMission, 0); + + STAT_LINE_2(int, "ST_STOR", StoresKnockedOff, 0, 15, 0); + + if (MovieStunts > 0.0f) + STAT_LINE_1(int, "ST_MOVI", MovieStunts, 0); + + STAT_LINE_2(int, "ST_ASSI", Assassinations, 0, 5, 0); + + if (PhotosTaken > 0) + STAT_LINE_1(int, "ST_PHOT", PhotosTaken, 0); + + if (PizzasDelivered > 0.0f) + STAT_LINE_1(int, "ST_PIZZ", PizzasDelivered, 0); + + if (GarbagePickups > 0.0f) + STAT_LINE_1(int, "ST_GARB", GarbagePickups, 0); + + if (IceCreamSold > 0.0f) + STAT_LINE_1(int, "ST_ICEC", IceCreamSold, 0); + + if (HighestScores[1]) + STAT_LINE_1(int, "STHC_02", HighestScores[1], 0); + + FASTEST_TIME(0, "STFT_01"); + FASTEST_TIME(1, "STFT_02"); + FASTEST_TIME(2, "STFT_03"); + FASTEST_TIME(3, "STFT_04"); + FASTEST_TIME(4, "STFT_05"); + FASTEST_TIME(5, "STFT_06"); + FASTEST_TIME(6, "STFT_07"); + FASTEST_TIME(7, "STFT_08"); + FASTEST_TIME(8, "STFT_09"); + FASTEST_TIME(9, "STFT_10"); + FASTEST_TIME(10, "STFT_11"); + FASTEST_TIME(11, "STFT_12"); + FASTEST_TIME(12, "STFT_13"); + FASTEST_TIME(13, "STFT_14"); + FASTEST_TIME(14, "STFT_15"); + FASTEST_TIME(15, "STFT_16"); + FASTEST_TIME(16, "STFT_17"); + FASTEST_TIME(17, "STFT_18"); + FASTEST_TIME(18, "STFT_19"); + FASTEST_TIME(19, "STFT_20"); + FASTEST_TIME(22, "STFT_23"); + + if (HighestScores[0]) + STAT_LINE_1(int, "STHC_01", HighestScores[0], 0); + + if (HighestScores[3]) + STAT_LINE_1(int, "STHC_04", HighestScores[3], 0); + + if (HighestScores[2]) + STAT_LINE_1(int, "STHC_03", HighestScores[2], 0); + + if (BestPositions[0] != INT_MAX) + STAT_LINE_1(int, "STHC_05", BestPositions[0], 0); + + FASTEST_TIME(20, "STFT_21"); + + if (FastestTimes[21]) +#ifdef FIX_BUGS + STAT_LINE_1(float, "STFT_22", Floor(FastestTimes[21] / 10) / 100, 1); +#else + STAT_LINE_1(float, "STFT_22", FastestTimes[21] / 1000, 1); +#endif + + if (TopShootingRangeScore > 0.0f) + STAT_LINE_1(int, "TOP_SHO", TopShootingRangeScore, 0); + + if (ShootingRank > 0.0f) + STAT_LINE_1(int, "SHO_RAN", ShootingRank, 0); + + int flightMinute = (FlightTime / 60000) % 60; + int flightHour = (FlightTime / 60000) / 60; + STAT_LINE_2(int, "ST_FTIM", flightHour, 0, flightMinute, 1); + + // We always have pilot rank if we flew more then 5 minutes +#ifndef FIX_BUGS + if (flightHour != 0) + TEXT_ON_LEFT_GXT("ST_PRAN"); +#endif + +#ifdef FIX_BUGS +#define FL_TIME_MORE_THAN(hour, minute) (flightHour > hour || (flightHour == hour && flightMinute >= minute)) +#else +#define FL_TIME_MORE_THAN(hour, minute) (flightHour > hour || flightMinute >= minute) +#endif + + if (FL_TIME_MORE_THAN(0,5)) { + +#ifdef FIX_BUGS + TEXT_ON_LEFT_GXT("ST_PRAN"); +#endif + if (!FL_TIME_MORE_THAN(0,10)) TEXT_ON_RIGHT(TheText.Get("ST_PR01")); + else if (!FL_TIME_MORE_THAN(0,20)) TEXT_ON_RIGHT(TheText.Get("ST_PR02")); + else if (!FL_TIME_MORE_THAN(0,30)) TEXT_ON_RIGHT(TheText.Get("ST_PR03")); + else if (!FL_TIME_MORE_THAN(1,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR04")); + else if (!FL_TIME_MORE_THAN(1,30)) TEXT_ON_RIGHT(TheText.Get("ST_PR05")); + else if (!FL_TIME_MORE_THAN(2,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR06")); + else if (!FL_TIME_MORE_THAN(2,30)) TEXT_ON_RIGHT(TheText.Get("ST_PR07")); + else if (!FL_TIME_MORE_THAN(3,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR08")); + else if (!FL_TIME_MORE_THAN(3,30)) TEXT_ON_RIGHT(TheText.Get("ST_PR09")); + else if (!FL_TIME_MORE_THAN(4,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR10")); + else if (!FL_TIME_MORE_THAN(5,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR11")); + else if (!FL_TIME_MORE_THAN(10,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR12")); + else if (!FL_TIME_MORE_THAN(20,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR13")); + else if (!FL_TIME_MORE_THAN(25,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR14")); + else if (!FL_TIME_MORE_THAN(30,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR15")); + else if (!FL_TIME_MORE_THAN(49,2)) TEXT_ON_RIGHT(TheText.Get("ST_PR16")); + else if (!FL_TIME_MORE_THAN(50,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR17")); + else if (!FL_TIME_MORE_THAN(100,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR18")); + else TEXT_ON_RIGHT(TheText.Get("ST_PR19")); + } +#undef FL_TIME_MORE_THAN + + if (BloodRingKills > 0) + STAT_LINE_1(int, "ST_BRK", BloodRingKills, 0); + + if (BloodRingTime > 0) + STAT_LINE_1(int, "ST_LTBR", BloodRingTime, 0); + + STAT_LINE_1(int, "ST_DRWN", TimesDrowned, 0); + + if (SeagullsKilled > 0) + STAT_LINE_1(int, "SEAGULL", SeagullsKilled, 0); + + bool playerHatesRadio = true; + float* pListenTimeArray = DMAudio.GetListenTimeArray(); + for (int i = 0; i < NUM_RADIOS; i++) { + FavoriteRadioStationList[i] = pListenTimeArray[i]; + if (FavoriteRadioStationList[i] != 0.0) // double + playerHatesRadio = false; + } + + if (!playerHatesRadio) { + // Most listened + TEXT_ON_LEFT_GXT("FST_MFR"); + float mostListenTime = FavoriteRadioStationList[0]; + int mostListenedRadio = 0; + for (int i = 0; i < NUM_RADIOS; i++) { + if (FavoriteRadioStationList[i] > mostListenTime) { + mostListenTime = FavoriteRadioStationList[i]; + mostListenedRadio = i; + } + } + switch (mostListenedRadio) { + case WILDSTYLE: + TEXT_ON_RIGHT(TheText.Get("FEA_FM0")); + break; + case FLASH_FM: + TEXT_ON_RIGHT(TheText.Get("FEA_FM1")); + break; + case KCHAT: + TEXT_ON_RIGHT(TheText.Get("FEA_FM2")); + break; + case FEVER: + TEXT_ON_RIGHT(TheText.Get("FEA_FM3")); + break; + case V_ROCK: + TEXT_ON_RIGHT(TheText.Get("FEA_FM4")); + break; + case VCPR: + TEXT_ON_RIGHT(TheText.Get("FEA_FM5")); + break; + case RADIO_ESPANTOSO: + TEXT_ON_RIGHT(TheText.Get("FEA_FM6")); + break; + case EMOTION: + TEXT_ON_RIGHT(TheText.Get("FEA_FM7")); + break; + case WAVE: + TEXT_ON_RIGHT(TheText.Get("FEA_FM8")); + break; + case USERTRACK: + TEXT_ON_RIGHT(TheText.Get("FEA_MP3")); + break; + default: + TEXT_ON_RIGHT(TheText.Get("FEA_FM8")); // heh + break; + } + + // Least listened + TEXT_ON_LEFT_GXT("FST_LFR"); + float leastListenTime = FavoriteRadioStationList[0]; + int leastListenedRadio = 0; + for (int i = 0; i < NUM_RADIOS; i++) { +#ifdef FIX_BUGS + if (!DMAudio.IsMP3RadioChannelAvailable() && i == USERTRACK) + continue; +#endif + if (FavoriteRadioStationList[i] < leastListenTime) { + leastListenTime = FavoriteRadioStationList[i]; + leastListenedRadio = i; + } + } +#ifndef FIX_BUGS + if (!DMAudio.IsMP3RadioChannelAvailable() && leastListenedRadio == USERTRACK) + leastListenedRadio = WAVE; +#endif + + switch (leastListenedRadio) { + case WILDSTYLE: + TEXT_ON_RIGHT(TheText.Get("FEA_FM0")); + break; + case FLASH_FM: + TEXT_ON_RIGHT(TheText.Get("FEA_FM1")); + break; + case KCHAT: + TEXT_ON_RIGHT(TheText.Get("FEA_FM2")); + break; + case FEVER: + TEXT_ON_RIGHT(TheText.Get("FEA_FM3")); + break; + case V_ROCK: + TEXT_ON_RIGHT(TheText.Get("FEA_FM4")); + break; + case VCPR: + TEXT_ON_RIGHT(TheText.Get("FEA_FM5")); + break; + case RADIO_ESPANTOSO: + TEXT_ON_RIGHT(TheText.Get("FEA_FM6")); + break; + case EMOTION: + TEXT_ON_RIGHT(TheText.Get("FEA_FM7")); + break; + case WAVE: + TEXT_ON_RIGHT(TheText.Get("FEA_FM8")); + break; + case USERTRACK: + TEXT_ON_RIGHT(TheText.Get("FEA_MP3")); + break; + default: + TEXT_ON_RIGHT(TheText.Get("FEA_FM8")); // heh + break; + } + } + STAT_LINE_1(int, "SPRAYIN", Sprayings, 0); + STAT_LINE_1(float, "ST_WEAP", WeaponBudget, 3); + STAT_LINE_1(float, "ST_FASH", FashionBudget, 3); + STAT_LINE_1(float, "ST_PROP", PropertyBudget, 3); + STAT_LINE_1(float, "ST_AUTO", AutoPaintingBudget, 3); + STAT_LINE_1(float, "ST_DAMA", PropertyBudget, 3); + + if (NumPropertyOwned > 0) { + STAT_LINE_1(int, "PROPOWN", NumPropertyOwned, 0); + if (PropertyOwned[0]) TEXT_ON_RIGHT(TheText.Get("STPR_1")); + if (PropertyOwned[1]) TEXT_ON_RIGHT(TheText.Get("STPR_2")); + if (PropertyOwned[2]) TEXT_ON_RIGHT(TheText.Get("STPR_3")); + if (PropertyOwned[3]) TEXT_ON_RIGHT(TheText.Get("STPR_4")); + if (PropertyOwned[4]) TEXT_ON_RIGHT(TheText.Get("STPR_5")); + if (PropertyOwned[5]) TEXT_ON_RIGHT(TheText.Get("STPR_6")); + if (PropertyOwned[6]) TEXT_ON_RIGHT(TheText.Get("STPR_7")); + if (PropertyOwned[7]) TEXT_ON_RIGHT(TheText.Get("STPR_8")); + if (PropertyOwned[8]) TEXT_ON_RIGHT(TheText.Get("STPR_9")); + if (PropertyOwned[9]) TEXT_ON_RIGHT(TheText.Get("STPR_10")); + if (PropertyOwned[10]) TEXT_ON_RIGHT(TheText.Get("STPR_11")); + if (PropertyOwned[11]) TEXT_ON_RIGHT(TheText.Get("STPR_12")); + if (PropertyOwned[12]) TEXT_ON_RIGHT(TheText.Get("STPR_13")); + if (PropertyOwned[13]) TEXT_ON_RIGHT(TheText.Get("STPR_14")); + if (PropertyOwned[14]) TEXT_ON_RIGHT(TheText.Get("STPR_15")); + } + STAT_LINE_1(int, "CHASE", HighestChaseValue, 0); + TEXT_ON_RIGHT(FindChaseString(HighestChaseValue)); + + return counter; + +#undef STAT_LINE_1 +#undef STAT_LINE_2 +#undef TEXT_ON_LEFT_GXT +#undef TEXT_ON_RIGHT +#undef FASTEST_TIME +} diff --git a/src/miami/core/Stats.h b/src/miami/core/Stats.h new file mode 100644 index 00000000..243ff0ec --- /dev/null +++ b/src/miami/core/Stats.h @@ -0,0 +1,152 @@ +#pragma once + +#include "PedType.h" +#include "audio_enums.h" + +class CStats +{ +public: + enum { + TOTAL_FASTEST_TIMES = 23, + TOTAL_HIGHEST_SCORES = 5, + TOTAL_BEST_POSITIONS = 1, + TOTAL_PROPERTIES = 15 + }; + static int32 SeagullsKilled; + static int32 DaysPassed; + static int32 HeadsPopped; + static int32 CommercialPassed; + static int32 IndustrialPassed; + static int32 SuburbanPassed; + static int32 NumberKillFrenziesPassed; + static int32 PeopleKilledByOthers; + static int32 HelisDestroyed; + static int32 PedsKilledOfThisType[NUM_PEDTYPES]; + static int32 TimesDied; + static int32 TimesArrested; + static int32 KillsSinceLastCheckpoint; + static float DistanceTravelledByCar; + static float DistanceTravelledByHelicoptor; + static float DistanceTravelledByBike; + static float DistanceTravelledByBoat; + static float DistanceTravelledByPlane; + static float DistanceTravelledByGolfCart; + static float DistanceTravelledOnFoot; + static int32 FlightTime; + static int32 TimesDrowned; + static int32 PhotosTaken; + static float LoanSharks; + static float StoresKnockedOff; + static float MovieStunts; + static float Assassinations; + static float PizzasDelivered; + static float GarbagePickups; + static float IceCreamSold; + static float TopShootingRangeScore; + static float ShootingRank; + static int32 CarsExploded; + static int32 BoatsExploded; + static int32 WantedStarsAttained; + static int32 WantedStarsEvaded; + static int32 PeopleKilledByPlayer; + static float ProgressMade; + static float TotalProgressInGame; + static float MaximumJumpDistance; + static float MaximumJumpHeight; + static int32 MaximumJumpFlips; + static int32 MaximumJumpSpins; + static int32 BestStuntJump; + static int32 NumberOfUniqueJumpsFound; + static int32 TotalNumberOfUniqueJumps; + static int32 PassengersDroppedOffWithTaxi; + static int32 MoneyMadeWithTaxi; + static int32 MissionsGiven; + static int32 MissionsPassed; + static char LastMissionPassedName[8]; + static int32 TotalLegitimateKills; + static int32 LivesSavedWithAmbulance; + static int32 CriminalsCaught; + static int32 HighestLevelAmbulanceMission; + static int32 HighestLevelVigilanteMission; + static int32 HighestLevelFireMission; + static int32 FiresExtinguished; + static int32 TotalNumberKillFrenzies; + static int32 TotalNumberMissions; + static int32 RoundsFiredByPlayer; + static int32 KgsOfExplosivesUsed; + static int32 BulletsThatHit; + static int32 BestTimeBombDefusal; + static int32 FastestTimes[TOTAL_FASTEST_TIMES]; + static int32 HighestScores[TOTAL_HIGHEST_SCORES]; + static int32 BestPositions[TOTAL_BEST_POSITIONS]; + static bool PropertyOwned[TOTAL_PROPERTIES]; + static int32 NumPropertyOwned; + static int32 PropertyDestroyed; + static float HighestChaseValue; + static int32 CheatedCount; + static int32 ShowChaseStatOnScreen; + static int32 PamphletMissionPassed; + static bool abSonyCDs[1]; + static int32 BloodRingKills; + static int32 BloodRingTime; + static float FavoriteRadioStationList[NUM_RADIOS]; + static int32 Sprayings; + static float AutoPaintingBudget; + static int32 NoMoreHurricanes; + static float FashionBudget; + static float PropertyBudget; + static float WeaponBudget; + static int32 SafeHouseVisits; + static int32 TyresPopped; + + static int32 LongestWheelie; + static int32 LongestStoppie; + static int32 Longest2Wheel; + static float LongestWheelieDist; + static float LongestStoppieDist; + static float Longest2WheelDist; + +public: + static void Init(void); + static void RegisterFastestTime(int32, int32); + static void RegisterHighestScore(int32, int32); + static void RegisterBestPosition(int32, int32); + static void AnotherLifeSavedWithAmbulance(); + static void AnotherCriminalCaught(); + static void RegisterLevelAmbulanceMission(int32); + static void RegisterLevelVigilanteMission(int32); + static void RegisterLevelFireMission(int32); + static void AnotherFireExtinguished(); + static wchar *FindCriminalRatingString(); + static wchar *FindChaseString(float fMediaLevel); + static void AnotherKillFrenzyPassed(); + static void SetTotalNumberKillFrenzies(int32); + static void SetTotalNumberMissions(int32); + static void CheckPointReachedSuccessfully() { TotalLegitimateKills += KillsSinceLastCheckpoint; KillsSinceLastCheckpoint = 0; }; + static void CheckPointReachedUnsuccessfully() { KillsSinceLastCheckpoint = 0; }; + static int32 FindCriminalRatingNumber(); + static void SaveStats(uint8 *buf, uint32 *size); + static void LoadStats(uint8 *buf, uint32 size); + static float GetPercentageProgress(); + + static void MoneySpentOnWeapons(int32); + static void MoneySpentOnProperty(int32); + static void MoneySpentOnAutoPainting(int32); + static void MoneySpentOnFashion(int32); + + static void NumOfVisitsFromLoanSharks(int32); + static void NumOfStoresKnockedOff(int32); + static void NumOfMovieStunts(int32); + static void NumOfAssassinations(int32); + static void NumOfPizzasDelivered(int32); + static void NumOfGarbagePickups(int32); + static void NumOfIceCreamSold(int32); + static void AddNumBloodRingKills(int32); + + static void LongestTimeInBloodRing(int32); + static void AddPropertyAsOwned(int32); + static void PopulateFavoriteRadioStationList(); + static float GetFavoriteRadioStationList(int32); + static void BuildStatLine(Const char *, void *, int, void *, int); + static int ConstructStatLine(int); +}; diff --git a/src/miami/core/Streaming.cpp b/src/miami/core/Streaming.cpp new file mode 100644 index 00000000..5833592e --- /dev/null +++ b/src/miami/core/Streaming.cpp @@ -0,0 +1,3278 @@ +#include "common.h" + +#include "General.h" +#include "Pad.h" +#include "Hud.h" +#include "Text.h" +#include "Clock.h" +#include "Renderer.h" +#include "ModelInfo.h" +#include "TxdStore.h" +#include "ModelIndices.h" +#include "Pools.h" +#include "Wanted.h" +#include "Directory.h" +#include "RwHelper.h" +#include "World.h" +#include "Entity.h" +#include "FileMgr.h" +#include "FileLoader.h" +#include "Zones.h" +#include "Radar.h" +#include "Camera.h" +#include "Record.h" +#include "CarCtrl.h" +#include "Population.h" +#include "Gangs.h" +#include "CutsceneMgr.h" +#include "CdStream.h" +#include "Streaming.h" +#include "Replay.h" +#include "main.h" +#include "ColStore.h" +#include "DMAudio.h" +#include "Script.h" +#include "MemoryMgr.h" +#include "MemoryHeap.h" +#include "Font.h" +#include "Frontend.h" +#include "VarConsole.h" + +bool CStreaming::ms_disableStreaming; +bool CStreaming::ms_bLoadingBigModel; +int32 CStreaming::ms_numModelsRequested; +CStreamingInfo CStreaming::ms_aInfoForModel[NUMSTREAMINFO]; +CStreamingInfo CStreaming::ms_startLoadedList; +CStreamingInfo CStreaming::ms_endLoadedList; +CStreamingInfo CStreaming::ms_startRequestedList; +CStreamingInfo CStreaming::ms_endRequestedList; +int32 CStreaming::ms_oldSectorX; +int32 CStreaming::ms_oldSectorY; +int32 CStreaming::ms_streamingBufferSize; +#ifndef ONE_THREAD_PER_CHANNEL +int8 *CStreaming::ms_pStreamingBuffer[2]; +#else +int8 *CStreaming::ms_pStreamingBuffer[4]; +#endif +size_t CStreaming::ms_memoryUsed; +CStreamingChannel CStreaming::ms_channel[2]; +int32 CStreaming::ms_channelError; +int32 CStreaming::ms_numVehiclesLoaded; +int32 CStreaming::ms_numPedsLoaded; +int32 CStreaming::ms_vehiclesLoaded[MAXVEHICLESLOADED]; +int32 CStreaming::ms_lastVehicleDeleted; +bool CStreaming::ms_bIsPedFromPedGroupLoaded[NUMMODELSPERPEDGROUP]; +CDirectory *CStreaming::ms_pExtraObjectsDir; +int32 CStreaming::ms_numPriorityRequests; +int32 CStreaming::ms_currentPedGrp; +int32 CStreaming::ms_currentPedLoading; +int32 CStreaming::ms_lastCullZone; +uint16 CStreaming::ms_loadedGangs; +uint16 CStreaming::ms_loadedGangCars; +int32 CStreaming::ms_imageOffsets[NUMCDIMAGES]; +int32 CStreaming::ms_lastImageRead; +int32 CStreaming::ms_imageSize; +size_t CStreaming::ms_memoryAvailable; + +int32 desiredNumVehiclesLoaded = 12; + +CEntity *pIslandLODmainlandEntity; +CEntity *pIslandLODbeachEntity; +int32 islandLODmainland; +int32 islandLODbeach; + +#ifndef MASTER +bool gbPrintStats; +bool gbPrintVehiclesInMemory; // TODO +bool gbPrintStreamingBuffer; // TODO +#endif + +#define memory_logf(...) // printf(__VA_ARGS__) + +#define STREAMING_MEM_SIZE (4 * 1024 * 1024) + +bool +CStreamingInfo::GetCdPosnAndSize(uint32 &posn, uint32 &size) +{ + if(m_size == 0) + return false; + posn = m_position; + size = m_size; + return true; +} + +void +CStreamingInfo::SetCdPosnAndSize(uint32 posn, uint32 size) +{ + m_position = posn; + m_size = size; +} + +void +CStreamingInfo::AddToList(CStreamingInfo *link) +{ + // Insert this after link + m_next = link->m_next; + m_prev = link; + link->m_next = this; + m_next->m_prev = this; +} + +void +CStreamingInfo::RemoveFromList(void) +{ + m_next->m_prev = m_prev; + m_prev->m_next = m_next; + m_next = nil; + m_prev = nil; +} + +void +CStreaming::Init2(void) +{ + int i; + + for(i = 0; i < NUMSTREAMINFO; i++){ + ms_aInfoForModel[i].m_loadState = STREAMSTATE_NOTLOADED; + ms_aInfoForModel[i].m_next = nil; + ms_aInfoForModel[i].m_prev = nil; + ms_aInfoForModel[i].m_nextID = -1; + ms_aInfoForModel[i].m_size = 0; + ms_aInfoForModel[i].m_position = 0; + } + + ms_channelError = -1; + + // init lists + + ms_startLoadedList.m_next = &ms_endLoadedList; + ms_startLoadedList.m_prev = nil; + ms_endLoadedList.m_prev = &ms_startLoadedList; + ms_endLoadedList.m_next = nil; + + ms_startRequestedList.m_next = &ms_endRequestedList; + ms_startRequestedList.m_prev = nil; + ms_endRequestedList.m_prev = &ms_startRequestedList; + ms_endRequestedList.m_next = nil; + + // init misc + + ms_oldSectorX = 0; + ms_oldSectorY = 0; + ms_streamingBufferSize = 0; + ms_disableStreaming = false; + ms_memoryUsed = 0; + ms_bLoadingBigModel = false; + + // init channels + + ms_channel[0].state = CHANNELSTATE_IDLE; + ms_channel[1].state = CHANNELSTATE_IDLE; + for(i = 0; i < 4; i++){ + ms_channel[0].streamIds[i] = -1; + ms_channel[0].offsets[i] = -1; + ms_channel[1].streamIds[i] = -1; + ms_channel[1].offsets[i] = -1; + } + + // init stream info, mark things that are already loaded + + for(i = 0; i < MODELINFOSIZE; i++){ + CBaseModelInfo *mi = CModelInfo::GetModelInfo(i); + if(mi && mi->GetRwObject()){ + ms_aInfoForModel[i].m_loadState = STREAMSTATE_LOADED; + ms_aInfoForModel[i].m_flags = STREAMFLAGS_DONT_REMOVE; + if(mi->IsSimple()) + ((CSimpleModelInfo*)mi)->m_alpha = 255; + } + } + + for(i = 0; i < TXDSTORESIZE; i++) + if(CTxdStore::GetSlot(i) && CTxdStore::GetSlot(i)->texDict) + ms_aInfoForModel[i + STREAM_OFFSET_TXD].m_loadState = STREAMSTATE_LOADED; + + + for(i = 0; i < MAXVEHICLESLOADED; i++) + ms_vehiclesLoaded[i] = -1; + ms_numVehiclesLoaded = 0; + ms_numPedsLoaded = 8; + + for(i = 0; i < ARRAY_SIZE(ms_bIsPedFromPedGroupLoaded); i++) + ms_bIsPedFromPedGroupLoaded[i] = false; + + ms_pExtraObjectsDir = new CDirectory(EXTRADIRSIZE); + ms_numPriorityRequests = 0; + ms_currentPedGrp = -1; + ms_lastCullZone = -1; // unused because RemoveModelsNotVisibleFromCullzone is gone + ms_loadedGangs = 0; + ms_currentPedLoading = NUMMODELSPERPEDGROUP; // unused, whatever it is + + LoadCdDirectory(); + + // allocate streaming buffers + if(ms_streamingBufferSize & 1) ms_streamingBufferSize++; +#ifndef ONE_THREAD_PER_CHANNEL + ms_pStreamingBuffer[0] = (int8*)RwMallocAlign(ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE, CDSTREAM_SECTOR_SIZE); + ms_streamingBufferSize /= 2; + ms_pStreamingBuffer[1] = ms_pStreamingBuffer[0] + ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE; +#else + ms_pStreamingBuffer[0] = (int8*)RwMallocAlign(ms_streamingBufferSize*2*CDSTREAM_SECTOR_SIZE, CDSTREAM_SECTOR_SIZE); + ms_streamingBufferSize /= 2; + ms_pStreamingBuffer[1] = ms_pStreamingBuffer[0] + ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE; + ms_pStreamingBuffer[2] = ms_pStreamingBuffer[1] + ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE; + ms_pStreamingBuffer[3] = ms_pStreamingBuffer[2] + ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE; +#endif + debug("Streaming buffer size is %d sectors", ms_streamingBufferSize); + + // PC only, figure out how much memory we got +#ifdef GTA_PC +#if defined(RW_DC) + ms_memoryAvailable = STREAMING_MEM_SIZE; + desiredNumVehiclesLoaded = 12; +#else +#define MB (1024*1024) +#ifdef FIX_BUGS + // do what gta3 does + extern size_t _dwMemAvailPhys; + ms_memoryAvailable = (_dwMemAvailPhys - 10*MB)/2; + if(ms_memoryAvailable < 65*MB) + ms_memoryAvailable = 65*MB; + desiredNumVehiclesLoaded = (int32)((ms_memoryAvailable / MB - 65) / 3 + 12); + if(desiredNumVehiclesLoaded > MAXVEHICLESLOADED) + desiredNumVehiclesLoaded = MAXVEHICLESLOADED; +#else + ms_memoryAvailable = 65 * MB; + desiredNumVehiclesLoaded = 25; + debug("Memory allocated to Streaming is %zuMB", ms_memoryAvailable/MB); // original modifier was %d +#endif +#undef MB +#endif +#endif + + // find island LODs + + pIslandLODmainlandEntity = nil; + pIslandLODbeachEntity = nil; + islandLODmainland = -1; + islandLODbeach = -1; + CModelInfo::GetModelInfo("IslandLODmainland", &islandLODmainland); + CModelInfo::GetModelInfo("IslandLODbeach", &islandLODbeach); + +#ifndef MASTER + VarConsole.Add("Streaming Debug", &gbPrintStats, true); + VarConsole.Add("Streaming Vehicle Debug", &gbPrintVehiclesInMemory, true); + VarConsole.Add("Printf Streaming Buffer contents", &gbPrintStreamingBuffer, true); +#endif +} + +void +CStreaming::Init(void) +{ +#ifdef USE_TXD_CDIMAGE + if(!CanVideoCardDoDXT()){ + int txdHandle = CFileMgr::OpenFile("MODELS\\TXD.IMG", "r"); + if (txdHandle) + CFileMgr::CloseFile(txdHandle); + if (!CheckVideoCardCaps() && txdHandle) { + CdStreamAddImage("MODELS\\TXD.IMG"); + CStreaming::Init2(); + } else { + CStreaming::Init2(); + if (CreateTxdImageForVideoCard()) { + CStreaming::Shutdown(); + CdStreamAddImage("MODELS\\TXD.IMG"); + CStreaming::Init2(); + } + } + } else + CStreaming::Init2(); +#else + CStreaming::Init2(); +#endif +} + +void +CStreaming::ReInit(void) +{ + int i; + CStreaming::FlushRequestList(); + CStreaming::DeleteAllRwObjects(); + CStreaming::RemoveAllUnusedModels(); + for(i = 0; i < MODELINFOSIZE; i++) + if(CModelInfo::GetModelInfo(i) && ms_aInfoForModel[i].m_flags & STREAMFLAGS_SCRIPTOWNED) + SetMissionDoesntRequireModel(i); + CStreaming::ms_disableStreaming = false; +} + +void +CStreaming::Shutdown(void) +{ + RwFreeAlign(ms_pStreamingBuffer[0]); + ms_streamingBufferSize = 0; + if(ms_pExtraObjectsDir) { + delete ms_pExtraObjectsDir; +#ifdef FIX_BUGS + ms_pExtraObjectsDir = nil; +#endif + } +} + +#ifndef MASTER +uint64 timeProcessingTXD; +uint64 timeProcessingDFF; +#endif + +void +CStreaming::Update(void) +{ + CStreamingInfo *si, *prev; + bool requestedSubway = false; + +#ifndef MASTER + timeProcessingTXD = 0; + timeProcessingDFF = 0; +#endif + + UpdateMemoryUsed(); + + if(ms_channelError != -1){ + RetryLoadFile(ms_channelError); + return; + } + + if(CTimer::GetIsPaused()) + return; + + LoadBigBuildingsWhenNeeded(); + if(!ms_disableStreaming && TheCamera.GetPosition().z < 55.0f) + AddModelsToRequestList(TheCamera.GetPosition(), 0); + + DeleteFarAwayRwObjects(TheCamera.GetPosition()); + + if(!ms_disableStreaming && + !CCutsceneMgr::IsCutsceneProcessing() && + ms_numModelsRequested < 5 && + !CRenderer::m_loadingPriority && + CGame::currArea == AREA_MAIN_MAP && + !CReplay::IsPlayingBack()){ + StreamVehiclesAndPeds(); + StreamZoneModels(FindPlayerCoors()); + } + + LoadRequestedModels(); + + if(CWorld::Players[0].m_pRemoteVehicle){ + CColStore::AddCollisionNeededAtPosn(FindPlayerCoors()); + CColStore::LoadCollision(CWorld::Players[0].m_pRemoteVehicle->GetPosition()); + CColStore::EnsureCollisionIsInMemory(CWorld::Players[0].m_pRemoteVehicle->GetPosition()); + }else{ + CColStore::LoadCollision(FindPlayerCoors()); + CColStore::EnsureCollisionIsInMemory(FindPlayerCoors()); + } + + // TODO: PrintRequestList + //if (CPad::GetPad(1)->GetLeftShoulder2JustDown() && CPad::GetPad(1)->GetRightShoulder1() && CPad::GetPad(1)->GetRightShoulder2()) + // PrintRequestList(); + + for(si = ms_endRequestedList.m_prev; si != &ms_startRequestedList; si = prev){ + prev = si->m_prev; + if((si->m_flags & (STREAMFLAGS_KEEP_IN_MEMORY|STREAMFLAGS_PRIORITY)) == 0) + RemoveModel(si - ms_aInfoForModel); + } +} + +void +CStreaming::LoadCdDirectory(void) +{ + char dirname[132]; + int i; + +#ifdef GTA_PC + ms_imageOffsets[0] = 0; + ms_imageOffsets[1] = -1; + ms_imageOffsets[2] = -1; + ms_imageOffsets[3] = -1; + ms_imageOffsets[4] = -1; + ms_imageOffsets[5] = -1; + ms_imageSize = GetGTA3ImgSize(); + // PS2 uses CFileMgr::GetCdFile on all IMG files to fill the array +#endif + + i = CdStreamGetNumImages(); + while(i-- >= 1){ + strcpy(dirname, CdStreamGetImageName(i)); + strncpy(strrchr(dirname, '.') + 1, "DIR", 3); + LoadCdDirectory(dirname, i); + } + + ms_lastImageRead = 0; + ms_imageSize /= CDSTREAM_SECTOR_SIZE; +} + +void +CStreaming::LoadCdDirectory(const char *dirname, int32 n) +{ + int fd, lastID, imgSelector; + int32 modelId; + CDirectory::DirectoryInfo direntry; + char *dot; + + lastID = -1; + fd = CFileMgr::OpenFile(dirname, "rb"); + assert(fd > 0); + + imgSelector = n<<24; + assert(sizeof(direntry) == 32); + while(CFileMgr::Read(fd, (char*)&direntry, sizeof(direntry))){ + bool bAddToStreaming = false; + + if(direntry.size > (uint32)ms_streamingBufferSize) + ms_streamingBufferSize = direntry.size; + direntry.name[23] = '\0'; + dot = strchr(direntry.name, '.'); + if(dot == nil || dot-direntry.name > 20){ + debug("%s is too long\n", direntry.name); + lastID = -1; + continue; + } + + *dot = '\0'; + + if(strncasecmp(dot+1, "DFF", 3) == 0){ + if(CModelInfo::GetModelInfo(direntry.name, &modelId)){ + bAddToStreaming = true; + }else{ +#ifdef FIX_BUGS + // remember which cdimage this came from + ms_pExtraObjectsDir->AddItem(direntry, n); +#else + ms_pExtraObjectsDir->AddItem(direntry); +#endif + lastID = -1; + } + }else if(strncasecmp(dot+1, "TXD", 3) == 0){ + modelId = CTxdStore::FindTxdSlot(direntry.name); + if(modelId == -1) + modelId = CTxdStore::AddTxdSlot(direntry.name); + modelId += STREAM_OFFSET_TXD; + bAddToStreaming = true; + }else if(strncasecmp(dot+1, "COL", 3) == 0){ + modelId = CColStore::FindColSlot(direntry.name); + if(modelId == -1) + modelId = CColStore::AddColSlot(direntry.name); + modelId += STREAM_OFFSET_COL; + bAddToStreaming = true; + }else if(strncasecmp(dot+1, "IFP", 3) == 0){ + modelId = CAnimManager::RegisterAnimBlock(direntry.name); + modelId += STREAM_OFFSET_ANIM; + bAddToStreaming = true; + }else{ + *dot = '.'; + lastID = -1; + } + + if(bAddToStreaming){ + if(ms_aInfoForModel[modelId].GetCdSize()){ + debug("%s.%s appears more than once in %s\n", direntry.name, dot+1, dirname); + lastID = -1; + }else{ + direntry.offset |= imgSelector; + ms_aInfoForModel[modelId].SetCdPosnAndSize(direntry.offset, direntry.size); + if(lastID != -1) + ms_aInfoForModel[lastID].m_nextID = modelId; + lastID = modelId; + } + } + } + + CFileMgr::CloseFile(fd); +} + +static char* +GetObjectName(int streamId) +{ + static char objname[32]; + if(streamId < STREAM_OFFSET_TXD) + sprintf(objname, "%s.dff", CModelInfo::GetModelInfo(streamId)->GetModelName()); + else if(streamId >= STREAM_OFFSET_TXD && streamId < STREAM_OFFSET_COL) + sprintf(objname, "%s.txd", CTxdStore::GetTxdName(streamId-STREAM_OFFSET_TXD)); + else if(streamId >= STREAM_OFFSET_COL && streamId < STREAM_OFFSET_ANIM) + sprintf(objname, "%s.col", CColStore::GetColName(streamId-STREAM_OFFSET_COL)); + else{ + assert(streamId < NUMSTREAMINFO); + sprintf(objname, "%s.ifp", CAnimManager::GetAnimationBlock(streamId-STREAM_OFFSET_ANIM)->name); + } + return objname; +} + +#ifdef USE_CUSTOM_ALLOCATOR +RpAtomic* +RegisterAtomicMemPtrsCB(RpAtomic *atomic, void *data) +{ + // empty because we expect models to be pre-instanced + return atomic; +} +#endif + +bool +CStreaming::ConvertBufferToObject(int8 *buf, int32 streamId) +{ + RwMemory mem; + RwStream *stream; + int cdsize; + uint32 startTime, endTime, timeDiff; + CBaseModelInfo *mi; + bool success; + + startTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); + + cdsize = ms_aInfoForModel[streamId].GetCdSize(); + mem.start = (uint8*)buf; + mem.length = cdsize * CDSTREAM_SECTOR_SIZE; + stream = RwStreamOpen(rwSTREAMMEMORY, rwSTREAMREAD, &mem); + + if(streamId < STREAM_OFFSET_TXD){ + // Model + mi = CModelInfo::GetModelInfo(streamId); + + // Txd and anim have to be loaded + int animId = mi->GetAnimFileIndex(); +#ifdef FIX_BUGS + if(!HasTxdLoaded(mi->GetTxdSlot()) || +#else + // texDict will exist even if only first part has loaded + if(CTxdStore::GetSlot(mi->GetTxdSlot())->texDict == nil || +#endif + animId != -1 && !CAnimManager::GetAnimationBlock(animId)->isLoaded){ + RemoveModel(streamId); + ReRequestModel(streamId); + RwStreamClose(stream, &mem); + return false; + } + + // Set Txd and anims to use + CTxdStore::AddRef(mi->GetTxdSlot()); +#if GTA_VERSION > GTAVC_PS2 + if(animId != -1) + CAnimManager::AddAnimBlockRef(animId); +#endif + + PUSH_MEMID(MEMID_STREAM_MODELS); + CTxdStore::SetCurrentTxd(mi->GetTxdSlot()); + if(mi->IsSimple()){ + success = CFileLoader::LoadAtomicFile(stream, streamId); + // TODO(MIAMI)? complain if file is not pre-instanced. we hardly are interested in that + } else if (mi->GetModelType() == MITYPE_VEHICLE) { + // load vehicles in two parts + CModelInfo::GetModelInfo(streamId)->AddRef(); + success = CFileLoader::StartLoadClumpFile(stream, streamId); + if(success) + ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_STARTED; + }else{ + success = CFileLoader::LoadClumpFile(stream, streamId); +#ifdef USE_CUSTOM_ALLOCATOR + if(success) + RpClumpForAllAtomics((RpClump*)mi->GetRwObject(), RegisterAtomicMemPtrsCB, nil); +#endif + } + POP_MEMID(); + UpdateMemoryUsed(); + + // Txd and anims no longer needed unless we only read part of the file + if(ms_aInfoForModel[streamId].m_loadState != STREAMSTATE_STARTED){ + CTxdStore::RemoveRefWithoutDelete(mi->GetTxdSlot()); +#if GTA_VERSION > GTAVC_PS2 + if(animId != -1) + CAnimManager::RemoveAnimBlockRefWithoutDelete(animId); +#endif + } + + if(!success){ + debug("Failed to load %s\n", CModelInfo::GetModelInfo(streamId)->GetModelName()); + RemoveModel(streamId); + ReRequestModel(streamId); + RwStreamClose(stream, &mem); + return false; + } + }else if(streamId >= STREAM_OFFSET_TXD && streamId < STREAM_OFFSET_COL){ + // Txd + if((ms_aInfoForModel[streamId].m_flags & STREAMFLAGS_KEEP_IN_MEMORY) == 0 && + !IsTxdUsedByRequestedModels(streamId - STREAM_OFFSET_TXD)){ + RemoveModel(streamId); + RwStreamClose(stream, &mem); + return false; + } + + PUSH_MEMID(MEMID_STREAM_TEXUTRES); + if(ms_bLoadingBigModel || cdsize > 200){ + success = CTxdStore::StartLoadTxd(streamId - STREAM_OFFSET_TXD, stream); + if(success) + ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_STARTED; + }else + success = CTxdStore::LoadTxd(streamId - STREAM_OFFSET_TXD, stream); + POP_MEMID(); + UpdateMemoryUsed(); + + if(!success){ + debug("Failed to load %s.txd\n", CTxdStore::GetTxdName(streamId - STREAM_OFFSET_TXD)); + RemoveModel(streamId); + ReRequestModel(streamId); + RwStreamClose(stream, &mem); + return false; + } + }else if(streamId >= STREAM_OFFSET_COL && streamId < STREAM_OFFSET_ANIM){ + PUSH_MEMID(MEMID_STREAM_COLLISION); + bool success = CColStore::LoadCol(streamId-STREAM_OFFSET_COL, mem.start, mem.length); + POP_MEMID(); + if(!success){ + debug("Failed to load %s.col\n", CColStore::GetColName(streamId - STREAM_OFFSET_COL)); + RemoveModel(streamId); + ReRequestModel(streamId); + RwStreamClose(stream, &mem); + return false; + } + }else if(streamId >= STREAM_OFFSET_ANIM){ + assert(streamId < NUMSTREAMINFO); + if((ms_aInfoForModel[streamId].m_flags & STREAMFLAGS_KEEP_IN_MEMORY) == 0 && + !AreAnimsUsedByRequestedModels(streamId - STREAM_OFFSET_ANIM)){ + RemoveModel(streamId); + RwStreamClose(stream, &mem); + return false; + } + PUSH_MEMID(MEMID_STREAM_ANIMATION); + CAnimManager::LoadAnimFile(stream, true, nil); + CAnimManager::CreateAnimAssocGroups(); + POP_MEMID(); + } + + RwStreamClose(stream, &mem); + + if(streamId < STREAM_OFFSET_TXD){ + // Model + // Vehicles and Peds not in loaded list + if (mi->GetModelType() != MITYPE_VEHICLE && mi->GetModelType() != MITYPE_PED) { + CSimpleModelInfo *smi = (CSimpleModelInfo*)mi; + + // Set fading for some objects + if(mi->IsSimple() && !smi->m_isBigBuilding){ + if(ms_aInfoForModel[streamId].m_flags & STREAMFLAGS_NOFADE) + smi->m_alpha = 255; + else + smi->m_alpha = 0; + } + + if(CanRemoveModel(streamId)) + ms_aInfoForModel[streamId].AddToList(&ms_startLoadedList); + } + }else if(streamId >= STREAM_OFFSET_TXD && streamId < STREAM_OFFSET_COL || + streamId >= STREAM_OFFSET_ANIM){ + assert(streamId < NUMSTREAMINFO); + // Txd and anims + if(CanRemoveModel(streamId)) + ms_aInfoForModel[streamId].AddToList(&ms_startLoadedList); + } + + // Mark objects as loaded + if(ms_aInfoForModel[streamId].m_loadState != STREAMSTATE_STARTED){ + ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_LOADED; +#ifndef USE_CUSTOM_ALLOCATOR + if (streamId < STREAM_OFFSET_TXD || streamId >= STREAM_OFFSET_COL) { + ms_memoryUsed += ms_aInfoForModel[streamId].GetCdSize() * CDSTREAM_SECTOR_SIZE; + memory_logf("ConvertBufferToObject: Memory used: %d\n", ms_memoryUsed); + } +#endif + } + + endTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); + timeDiff = endTime - startTime; + if(timeDiff > 5) + debug("%s took %d ms\n", GetObjectName(streamId), timeDiff); + + return true; +} + +bool +CStreaming::FinishLoadingLargeFile(int8 *buf, int32 streamId) +{ + RwMemory mem; + RwStream *stream; + uint32 startTime, endTime, timeDiff; + CBaseModelInfo *mi; + bool success; + + startTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); + + if(ms_aInfoForModel[streamId].m_loadState != STREAMSTATE_STARTED){ + if(streamId < STREAM_OFFSET_TXD) + CModelInfo::GetModelInfo(streamId)->RemoveRef(); + return false; + } + + mem.start = (uint8*)buf; + mem.length = ms_aInfoForModel[streamId].GetCdSize() * CDSTREAM_SECTOR_SIZE; + stream = RwStreamOpen(rwSTREAMMEMORY, rwSTREAMREAD, &mem); + + if(streamId < STREAM_OFFSET_TXD){ + // Model + mi = CModelInfo::GetModelInfo(streamId); + PUSH_MEMID(MEMID_STREAM_MODELS); + CTxdStore::SetCurrentTxd(mi->GetTxdSlot()); + success = CFileLoader::FinishLoadClumpFile(stream, streamId); + if(success){ +#ifdef USE_CUSTOM_ALLOCATOR + RpClumpForAllAtomics((RpClump*)mi->GetRwObject(), RegisterAtomicMemPtrsCB, nil); +#endif + success = AddToLoadedVehiclesList(streamId); + } + POP_MEMID(); + mi->RemoveRef(); + CTxdStore::RemoveRefWithoutDelete(mi->GetTxdSlot()); +#if GTA_VERSION > GTAVC_PS2 + if(mi->GetAnimFileIndex() != -1) + CAnimManager::RemoveAnimBlockRefWithoutDelete(mi->GetAnimFileIndex()); +#endif + }else if(streamId >= STREAM_OFFSET_TXD && streamId < STREAM_OFFSET_COL){ + // Txd + CTxdStore::AddRef(streamId - STREAM_OFFSET_TXD); + PUSH_MEMID(MEMID_STREAM_TEXUTRES); + success = CTxdStore::FinishLoadTxd(streamId - STREAM_OFFSET_TXD, stream); + POP_MEMID(); + CTxdStore::RemoveRefWithoutDelete(streamId - STREAM_OFFSET_TXD); + }else{ + assert(0 && "invalid streamId"); + } + + RwStreamClose(stream, &mem); + + ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_LOADED; +#ifndef USE_CUSTOM_ALLOCATOR + if (streamId < STREAM_OFFSET_TXD || streamId >= STREAM_OFFSET_COL) { + ms_memoryUsed += ms_aInfoForModel[streamId].GetCdSize() * CDSTREAM_SECTOR_SIZE; + memory_logf("FinishLoadingLargeFile: Memory used: %d\n", ms_memoryUsed); + } +#endif + + if(!success){ + RemoveModel(streamId); + ReRequestModel(streamId); + UpdateMemoryUsed(); + return false; + } + + UpdateMemoryUsed(); + + endTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); + timeDiff = endTime - startTime; + if(timeDiff > 5) + debug("%s took %d ms\n", GetObjectName(streamId), timeDiff); + + return true; +} + +void +CStreaming::RequestModel(int32 id, int32 flags) +{ + CSimpleModelInfo *mi; + + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_INQUEUE){ + // updgrade to priority + if(flags & STREAMFLAGS_PRIORITY && !ms_aInfoForModel[id].IsPriority()){ + ms_numPriorityRequests++; + ms_aInfoForModel[id].m_flags |= STREAMFLAGS_PRIORITY; + } + }else if(ms_aInfoForModel[id].m_loadState != STREAMSTATE_NOTLOADED){ + flags &= ~STREAMFLAGS_PRIORITY; + } + ms_aInfoForModel[id].m_flags |= flags; + + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_LOADED){ + // Already loaded, only check changed flags + + if(ms_aInfoForModel[id].m_flags & STREAMFLAGS_NOFADE && id < STREAM_OFFSET_TXD){ + mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(id); + if(mi->IsSimple()) + mi->m_alpha = 255; + } + + // reinsert into list + if(ms_aInfoForModel[id].m_next){ + ms_aInfoForModel[id].RemoveFromList(); + if(CanRemoveModel(id)) + ms_aInfoForModel[id].AddToList(&ms_startLoadedList); + } + }else if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_NOTLOADED || + ms_aInfoForModel[id].m_loadState == STREAMSTATE_LOADED){ // how can this be true again? + + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_NOTLOADED){ + if(id < STREAM_OFFSET_TXD){ + mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(id); + RequestTxd(mi->GetTxdSlot(), flags); + int anim = mi->GetAnimFileIndex(); + if(anim != -1) + RequestAnim(anim, STREAMFLAGS_DEPENDENCY); + } + ms_aInfoForModel[id].AddToList(&ms_startRequestedList); + ms_numModelsRequested++; + if(flags & STREAMFLAGS_PRIORITY) + ms_numPriorityRequests++; + } + + ms_aInfoForModel[id].m_loadState = STREAMSTATE_INQUEUE; + ms_aInfoForModel[id].m_flags = flags; + } +} + +#define BIGBUILDINGFLAGS STREAMFLAGS_DONT_REMOVE + +void +CStreaming::RequestBigBuildings(eLevelName level) +{ + int i, n; + CBuilding *b; + + n = CPools::GetBuildingPool()->GetSize()-1; + for(i = n; i >= 0; i--){ + b = CPools::GetBuildingPool()->GetSlot(i); + if(b && b->bIsBIGBuilding +#ifdef NO_ISLAND_LOADING + && (((FrontEndMenuManager.m_PrefsIslandLoading != CMenuManager::ISLAND_LOADING_LOW) && (b != pIslandLODmainlandEntity) && + (b != pIslandLODbeachEntity)) || + (b->m_level == level)) +#else + && b->m_level == level +#endif + ) + if(!b->bStreamBIGBuilding) + RequestModel(b->GetModelIndex(), BIGBUILDINGFLAGS); + } + RequestIslands(level); +} + +void +CStreaming::RequestBigBuildings(eLevelName level, const CVector &pos) +{ + int i, n; + CBuilding *b; + + n = CPools::GetBuildingPool()->GetSize()-1; + for(i = n; i >= 0; i--){ + b = CPools::GetBuildingPool()->GetSlot(i); + if(b && b->bIsBIGBuilding +#ifdef NO_ISLAND_LOADING + && (((FrontEndMenuManager.m_PrefsIslandLoading != CMenuManager::ISLAND_LOADING_LOW) && (b != pIslandLODmainlandEntity) && (b != pIslandLODbeachEntity) + ) || (b->m_level == level)) +#else + && b->m_level == level +#endif + ) + if(b->bStreamBIGBuilding){ + if(CRenderer::ShouldModelBeStreamed(b, pos)) + RequestModel(b->GetModelIndex(), 0); + }else + RequestModel(b->GetModelIndex(), BIGBUILDINGFLAGS); + } + RequestIslands(level); +} + +void +CStreaming::InstanceBigBuildings(eLevelName level, const CVector &pos) +{ + int i, n; + CBuilding *b; + + n = CPools::GetBuildingPool()->GetSize()-1; + for(i = n; i >= 0; i--){ + b = CPools::GetBuildingPool()->GetSlot(i); + if(b && b->bIsBIGBuilding && b->m_level == level && + b->bStreamBIGBuilding && b->m_rwObject == nil) + if(CRenderer::ShouldModelBeStreamed(b, pos)) + b->CreateRwObject(); + } +} + +void +CStreaming::InstanceLoadedModelsInSectorList(CPtrList &list) +{ + CPtrNode *node; + CEntity *e; + for(node = list.first; node; node = node->next) { + e = (CEntity *)node->item; + if(IsAreaVisible(e->m_area) && e->m_rwObject == nil) + e->CreateRwObject(); + } +} + +void +CStreaming::InstanceLoadedModels(const CVector &pos) +{ + int minX = CWorld::GetSectorIndexX(pos.x - 80.0f); + if(minX <= 0) minX = 0; + + int minY = CWorld::GetSectorIndexY(pos.y - 80.0f); + if(minY <= 0) minY = 0; + + int maxX = CWorld::GetSectorIndexX(pos.x + 80.0f); + if(maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X - 1; + + int maxY = CWorld::GetSectorIndexY(pos.y + 80.0f); + if(maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y - 1; + + int x, y; + for(y = minY; y <= maxY; y++){ + for(x = minX; x <= maxX; x++){ + CSector *sector = CWorld::GetSector(x, y); + InstanceLoadedModelsInSectorList(sector->m_lists[ENTITYLIST_BUILDINGS]); + InstanceLoadedModelsInSectorList(sector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP]); + InstanceLoadedModelsInSectorList(sector->m_lists[ENTITYLIST_OBJECTS]); + InstanceLoadedModelsInSectorList(sector->m_lists[ENTITYLIST_DUMMIES]); + } + } +} + +void +CStreaming::RequestIslands(eLevelName level) +{ + ISLAND_LOADING_ISNT(HIGH) + switch(level){ + case LEVEL_MAINLAND: + if(islandLODbeach != -1) + RequestModel(islandLODbeach, BIGBUILDINGFLAGS); + break; + case LEVEL_BEACH: + if(islandLODmainland != -1) + RequestModel(islandLODmainland, BIGBUILDINGFLAGS); + break; + default: break; + } +} + +static char *IGnames[] = { + "player", + "player2", + "player3", + "player4", + "player5", + "player6", + "player7", + "player8", + "player9", + "play10", + "play11", + "igken", + "igcandy", + "igsonny", + "igbuddy", + "igjezz", + "ighlary", + "igphil", + "igmerc", + "igdick", + "igdiaz", + "" +}; + +static char *CSnames[] = { + "csplay", + "csplay2", + "csplay3", + "csplay4", + "csplay5", + "csplay6", + "csplay7", + "csplay8", + "csplay9", + "csplay10", + "csplay11", + "csken", + "cscandy", + "cssonny", + "csbuddy", + "csjezz", + "cshlary", + "csphil", + "csmerc", + "csdick", + "csdiaz", + "" +}; + +void +CStreaming::RequestSpecialModel(int32 modelId, const char *modelName, int32 flags) +{ + CBaseModelInfo *mi; + int txdId; + char oldName[48]; + uint32 pos, size; + int i, n; + + mi = CModelInfo::GetModelInfo(modelId); + if(strncasecmp("CSPlay", modelName, 6) == 0){ + char *curname = CModelInfo::GetModelInfo(MI_PLAYER)->GetModelName(); + for(int i = 0; CSnames[i][0]; i++){ + if(strcasecmp(curname, IGnames[i]) == 0){ + modelName = CSnames[i]; + break; + } + } + } + if(!CGeneral::faststrcmp(mi->GetModelName(), modelName)){ + // Already have the correct name, just request it + RequestModel(modelId, flags); + return; + } + + if(mi->GetNumRefs() > 0){ + n = CPools::GetPedPool()->GetSize()-1; + for(i = n; i >= 0 && mi->GetNumRefs() > 0; i--){ + CPed *ped = CPools::GetPedPool()->GetSlot(i); + if(ped && ped->GetModelIndex() == modelId && + !ped->IsPlayer() && ped->CanBeDeletedEvenInVehicle()) + CTheScripts::RemoveThisPed(ped); + } + n = CPools::GetObjectPool()->GetSize()-1; + for(i = n; i >= 0 && mi->GetNumRefs() > 0; i--){ + CObject *obj = CPools::GetObjectPool()->GetSlot(i); + if(obj && obj->GetModelIndex() == modelId && obj->CanBeDeleted()){ + CWorld::Remove(obj); + CWorld::RemoveReferencesToDeletedObject(obj); + delete obj; + } + } + } + + strcpy(oldName, mi->GetModelName()); + mi->SetModelName(modelName); + + // What exactly is going on here? + if(CModelInfo::GetModelInfo(oldName, nil)){ + txdId = CTxdStore::FindTxdSlot(oldName); + if(txdId != -1 && CTxdStore::GetSlot(txdId)->texDict){ + CTxdStore::AddRef(txdId); + RemoveModel(modelId); + CTxdStore::RemoveRefWithoutDelete(txdId); + }else + RemoveModel(modelId); + }else + RemoveModel(modelId); + + bool found = ms_pExtraObjectsDir->FindItem(modelName, pos, size); + assert(found); + mi->ClearTexDictionary(); + if(CTxdStore::FindTxdSlot(modelName) == -1) + mi->SetTexDictionary("generic"); + else + mi->SetTexDictionary(modelName); + ms_aInfoForModel[modelId].SetCdPosnAndSize(pos, size); + RequestModel(modelId, flags); +} + +void +CStreaming::RequestSpecialChar(int32 charId, const char *modelName, int32 flags) +{ + RequestSpecialModel(charId + MI_SPECIAL01, modelName, flags); +} + +bool +CStreaming::HasSpecialCharLoaded(int32 id) +{ + return HasModelLoaded(id + MI_SPECIAL01); +} + +void +CStreaming::SetMissionDoesntRequireSpecialChar(int32 id) +{ + return SetMissionDoesntRequireModel(id + MI_SPECIAL01); +} + +void +CStreaming::DecrementRef(int32 id) +{ + ms_numModelsRequested--; + if(ms_aInfoForModel[id].IsPriority()){ + ms_aInfoForModel[id].m_flags &= ~STREAMFLAGS_PRIORITY; + ms_numPriorityRequests--; + } +} + +void +CStreaming::RemoveModel(int32 id) +{ + int i; + + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_NOTLOADED) + return; + + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_LOADED){ + if(id < STREAM_OFFSET_TXD) + CModelInfo::GetModelInfo(id)->DeleteRwObject(); + else if(id >= STREAM_OFFSET_TXD && id < STREAM_OFFSET_COL) + CTxdStore::RemoveTxd(id - STREAM_OFFSET_TXD); + else if(id >= STREAM_OFFSET_COL && id < STREAM_OFFSET_ANIM) + CColStore::RemoveCol(id - STREAM_OFFSET_COL); + else if(id >= STREAM_OFFSET_ANIM){ + assert(id < NUMSTREAMINFO); + CAnimManager::RemoveAnimBlock(id - STREAM_OFFSET_ANIM); + } + if (id < STREAM_OFFSET_TXD || id >= STREAM_OFFSET_COL) { + ms_memoryUsed -= ms_aInfoForModel[id].GetCdSize()*CDSTREAM_SECTOR_SIZE; + memory_logf("Remove Model: %d\n", ms_memoryUsed); + } + } + + if(ms_aInfoForModel[id].m_next){ + // Remove from list, model is neither loaded nor requested + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_INQUEUE) + DecrementRef(id); + ms_aInfoForModel[id].RemoveFromList(); + }else if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_READING){ + for(i = 0; i < 4; i++){ + if(ms_channel[0].streamIds[i] == id) + ms_channel[0].streamIds[i] = -1; + if(ms_channel[1].streamIds[i] == id) + ms_channel[1].streamIds[i] = -1; + } + } + + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_STARTED){ + if(id < STREAM_OFFSET_TXD) + RpClumpGtaCancelStream(); + else if(id >= STREAM_OFFSET_TXD && id < STREAM_OFFSET_COL) + CTxdStore::RemoveTxd(id - STREAM_OFFSET_TXD); + else if(id >= STREAM_OFFSET_COL && id < STREAM_OFFSET_ANIM) + CColStore::RemoveCol(id - STREAM_OFFSET_COL); + else if(id >= STREAM_OFFSET_ANIM){ + assert(id < NUMSTREAMINFO); + CAnimManager::RemoveAnimBlock(id - STREAM_OFFSET_ANIM); + } + } + + ms_aInfoForModel[id].m_loadState = STREAMSTATE_NOTLOADED; +} + +void +CStreaming::RemoveUnusedBuildings(eLevelName level) +{ + if(level != LEVEL_BEACH) + RemoveBuildings(LEVEL_BEACH); + if(level != LEVEL_MAINLAND) + RemoveBuildings(LEVEL_MAINLAND); +} + +void +CStreaming::RemoveBuildings(eLevelName level) +{ + int i, n; + CEntity *e; + CBaseModelInfo *mi; + + n = CPools::GetBuildingPool()->GetSize()-1; + for(i = n; i >= 0; i--){ + e = CPools::GetBuildingPool()->GetSlot(i); + if(e && e->m_level == level){ + mi = CModelInfo::GetModelInfo(e->GetModelIndex()); + if(!e->bImBeingRendered){ + e->DeleteRwObject(); + if (mi->GetNumRefs() == 0) + RemoveModel(e->GetModelIndex()); + } + } + } + + n = CPools::GetTreadablePool()->GetSize()-1; + for(i = n; i >= 0; i--){ + e = CPools::GetTreadablePool()->GetSlot(i); + if(e && e->m_level == level){ + mi = CModelInfo::GetModelInfo(e->GetModelIndex()); + if(!e->bImBeingRendered){ + e->DeleteRwObject(); + if (mi->GetNumRefs() == 0) + RemoveModel(e->GetModelIndex()); + } + } + } + + n = CPools::GetObjectPool()->GetSize()-1; + for(i = n; i >= 0; i--){ + e = CPools::GetObjectPool()->GetSlot(i); + if(e && e->m_level == level){ + mi = CModelInfo::GetModelInfo(e->GetModelIndex()); + if(!e->bImBeingRendered && ((CObject*)e)->ObjectCreatedBy == GAME_OBJECT){ + e->DeleteRwObject(); + if (mi->GetNumRefs() == 0) + RemoveModel(e->GetModelIndex()); + } + } + } + + n = CPools::GetDummyPool()->GetSize()-1; + for(i = n; i >= 0; i--){ + e = CPools::GetDummyPool()->GetSlot(i); + if(e && e->m_level == level){ + mi = CModelInfo::GetModelInfo(e->GetModelIndex()); + if(!e->bImBeingRendered){ + e->DeleteRwObject(); + if (mi->GetNumRefs() == 0) + RemoveModel(e->GetModelIndex()); + } + } + } +} + +void +CStreaming::RemoveBuildingsNotInArea(int32 area) +{ + int i, n; + CEntity *e; + + n = CPools::GetBuildingPool()->GetSize()-1; + for(i = n; i >= 0; i--){ + e = CPools::GetBuildingPool()->GetSlot(i); + if(e && e->m_rwObject && !IsAreaVisible(area) && + (!e->bIsBIGBuilding || e->bStreamBIGBuilding)){ + if(e->bIsBIGBuilding) + RequestModel(e->GetModelIndex(), 0); + if(!e->bImBeingRendered) + e->DeleteRwObject(); + } + } + + n = CPools::GetTreadablePool()->GetSize()-1; + for(i = n; i >= 0; i--){ + e = CPools::GetTreadablePool()->GetSlot(i); + if(e && e->m_rwObject && !IsAreaVisible(area) && + (!e->bIsBIGBuilding || e->bStreamBIGBuilding)){ + if(e->bIsBIGBuilding) + RequestModel(e->GetModelIndex(), 0); + if(!e->bImBeingRendered) + e->DeleteRwObject(); + } + } + + n = CPools::GetObjectPool()->GetSize()-1; + for(i = n; i >= 0; i--){ + e = CPools::GetObjectPool()->GetSlot(i); + if(e && e->m_rwObject && !IsAreaVisible(area) && + (!e->bIsBIGBuilding || e->bStreamBIGBuilding)){ + if(e->bIsBIGBuilding) + RequestModel(e->GetModelIndex(), 0); + if(!e->bImBeingRendered) + e->DeleteRwObject(); + } + } + + n = CPools::GetDummyPool()->GetSize()-1; + for(i = n; i >= 0; i--){ + e = CPools::GetDummyPool()->GetSlot(i); + if(e && e->m_rwObject && !IsAreaVisible(area) && + (!e->bIsBIGBuilding || e->bStreamBIGBuilding)){ + if(e->bIsBIGBuilding) + RequestModel(e->GetModelIndex(), 0); + if(!e->bImBeingRendered) + e->DeleteRwObject(); + } + } +} + +void +CStreaming::RemoveUnusedBigBuildings(eLevelName level) +{ + ISLAND_LOADING_IS(LOW) + { + if(level != LEVEL_BEACH) + RemoveBigBuildings(LEVEL_BEACH); + if(level != LEVEL_MAINLAND) + RemoveBigBuildings(LEVEL_MAINLAND); + } + RemoveIslandsNotUsed(level); +} + +void +DeleteIsland(CEntity *island) +{ + if(island == nil) + return; + if(island->bImBeingRendered) + debug("Didn't delete island because it was being rendered\n"); + else{ + island->DeleteRwObject(); + CStreaming::RemoveModel(island->GetModelIndex()); + } +} + +void +CStreaming::RemoveIslandsNotUsed(eLevelName level) +{ + int i; + if(pIslandLODmainlandEntity == nil) + for(i = CPools::GetBuildingPool()->GetSize()-1; i >= 0; i--){ + CBuilding *building = CPools::GetBuildingPool()->GetSlot(i); + if(building == nil) + continue; + if(building->GetModelIndex() == islandLODmainland) + pIslandLODmainlandEntity = building; + if(building->GetModelIndex() == islandLODbeach) + pIslandLODbeachEntity = building; + } +#ifdef NO_ISLAND_LOADING + if(FrontEndMenuManager.m_PrefsIslandLoading == CMenuManager::ISLAND_LOADING_HIGH) { + DeleteIsland(pIslandLODmainlandEntity); + DeleteIsland(pIslandLODbeachEntity); + } else +#endif + switch(level){ + case LEVEL_MAINLAND: + DeleteIsland(pIslandLODmainlandEntity); + break; + case LEVEL_BEACH: + DeleteIsland(pIslandLODbeachEntity); + + break; + } +} + +void +CStreaming::RemoveBigBuildings(eLevelName level) +{ + int i, n; + CEntity *e; + CBaseModelInfo *mi; + + n = CPools::GetBuildingPool()->GetSize()-1; + for(i = n; i >= 0; i--){ + e = CPools::GetBuildingPool()->GetSlot(i); + if(e && e->bIsBIGBuilding && e->m_level == level){ + mi = CModelInfo::GetModelInfo(e->GetModelIndex()); + if(!e->bImBeingRendered){ + e->DeleteRwObject(); + if (mi->GetNumRefs() == 0) + RemoveModel(e->GetModelIndex()); + } + } + } +} + +bool +CStreaming::RemoveLoadedVehicle(void) +{ + int i, id; + + for(i = 0; i < MAXVEHICLESLOADED; i++){ + ms_lastVehicleDeleted++; + if(ms_lastVehicleDeleted == MAXVEHICLESLOADED) + ms_lastVehicleDeleted = 0; + id = ms_vehiclesLoaded[ms_lastVehicleDeleted]; + if(id != -1 && CanRemoveModel(id) && CModelInfo::GetModelInfo(id)->GetNumRefs() == 0 && + ms_aInfoForModel[id].m_loadState == STREAMSTATE_LOADED) + goto found; + } + return false; +found: + RemoveModel(ms_vehiclesLoaded[ms_lastVehicleDeleted]); + ms_numVehiclesLoaded--; + ms_vehiclesLoaded[ms_lastVehicleDeleted] = -1; + CVehicleModelInfo* pVehicleInfo = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); + if (pVehicleInfo->m_vehicleClass != -1) + CCarCtrl::RemoveFromLoadedVehicleArray(id, pVehicleInfo->m_vehicleClass); + return true; +} + +bool re3RemoveLeastUsedModel() { + return CStreaming::RemoveLeastUsedModel(STREAMFLAGS_20); +} + +bool re3EmergencyRemoveModel() { + auto usedmem = CStreaming::ms_memoryUsed; + CStreaming::DeleteRwObjectsBehindCamera(CStreaming::ms_memoryUsed-1); + return usedmem != CStreaming::ms_memoryUsed; +} + +bool +CStreaming::RemoveLeastUsedModel(uint32 excludeMask) +{ + CStreamingInfo *si; + int streamId; + + for(si = ms_endLoadedList.m_prev; si != &ms_startLoadedList; si = si->m_prev){ + if(si->m_flags & excludeMask) + continue; + streamId = si - ms_aInfoForModel; + if(streamId < STREAM_OFFSET_TXD){ + if (CModelInfo::GetModelInfo(streamId)->GetNumRefs() == 0) { + RemoveModel(streamId); + return true; + } + }else if(streamId >= STREAM_OFFSET_TXD && streamId < STREAM_OFFSET_COL){ + if(CTxdStore::GetNumRefs(streamId - STREAM_OFFSET_TXD) == 0 && + !IsTxdUsedByRequestedModels(streamId - STREAM_OFFSET_TXD)){ + RemoveModel(streamId); + return true; + } + }else if(streamId >= STREAM_OFFSET_ANIM){ + assert(streamId < NUMSTREAMINFO); + if(CAnimManager::GetNumRefsToAnimBlock(streamId - STREAM_OFFSET_ANIM) == 0 && + !AreAnimsUsedByRequestedModels(streamId - STREAM_OFFSET_ANIM)){ + RemoveModel(streamId); + return true; + } + } + } + return (ms_numVehiclesLoaded > 7 || CGame::currArea != AREA_MAIN_MAP && ms_numVehiclesLoaded > 4) && RemoveLoadedVehicle(); +} + +void +CStreaming::RemoveAllUnusedModels(void) +{ + int i; + + for(i = 0; i < MAXVEHICLESLOADED; i++) + RemoveLoadedVehicle(); + + for(i = NUM_DEFAULT_MODELS; i < MODELINFOSIZE; i++){ + if(ms_aInfoForModel[i].m_loadState == STREAMSTATE_LOADED && + CModelInfo::GetModelInfo(i)->GetNumRefs() == 0) { + RemoveModel(i); + ms_aInfoForModel[i].m_loadState = STREAMSTATE_NOTLOADED; + } + } +} + +void +CStreaming::RemoveUnusedModelsInLoadedList(void) +{ + // empty +} + +bool +CStreaming::RemoveLoadedZoneModel(void) +{ + int i; + + if(ms_currentPedGrp == -1) + return false; + + for(i = 0; i < NUMMODELSPERPEDGROUP; i++){ + int mi = CPopulation::ms_pPedGroups[ms_currentPedGrp].models[i]; + if(mi != -1 && ms_bIsPedFromPedGroupLoaded[i] && + HasModelLoaded(mi) && CanRemoveModel(mi) && + CModelInfo::GetModelInfo(mi)->GetNumRefs() == 0){ + RemoveModel(CPopulation::ms_pPedGroups[ms_currentPedGrp].models[i]); + ms_numPedsLoaded--; + ms_bIsPedFromPedGroupLoaded[i] = false; + return true; + } + } + + return false; +} + +bool +CStreaming::IsTxdUsedByRequestedModels(int32 txdId) +{ + CStreamingInfo *si; + int streamId; + int i; + + for(si = ms_startRequestedList.m_next; si != &ms_endRequestedList; si = si->m_next){ + streamId = si - ms_aInfoForModel; + if(streamId < STREAM_OFFSET_TXD && + CModelInfo::GetModelInfo(streamId)->GetTxdSlot() == txdId) + return true; + } + + for(i = 0; i < 4; i++){ + streamId = ms_channel[0].streamIds[i]; + if(streamId != -1 && streamId < STREAM_OFFSET_TXD && + CModelInfo::GetModelInfo(streamId)->GetTxdSlot() == txdId) + return true; + streamId = ms_channel[1].streamIds[i]; + if(streamId != -1 && streamId < STREAM_OFFSET_TXD && + CModelInfo::GetModelInfo(streamId)->GetTxdSlot() == txdId) + return true; + } + + return false; +} + +bool +CStreaming::AreAnimsUsedByRequestedModels(int32 animId) +{ + CStreamingInfo *si; + int streamId; + int i; + + for(si = ms_startRequestedList.m_next; si != &ms_endRequestedList; si = si->m_next){ + streamId = si - ms_aInfoForModel; + if(streamId < STREAM_OFFSET_TXD && + CModelInfo::GetModelInfo(streamId)->GetAnimFileIndex() == animId) + return true; + } + + for(i = 0; i < 4; i++){ + streamId = ms_channel[0].streamIds[i]; + if(streamId != -1 && streamId < STREAM_OFFSET_TXD && + CModelInfo::GetModelInfo(streamId)->GetAnimFileIndex() == animId) + return true; + streamId = ms_channel[1].streamIds[i]; + if(streamId != -1 && streamId < STREAM_OFFSET_TXD && + CModelInfo::GetModelInfo(streamId)->GetAnimFileIndex() == animId) + return true; + } + + return false; +} + +int32 +CStreaming::GetAvailableVehicleSlot(void) +{ + int i; + for(i = 0; i < MAXVEHICLESLOADED; i++) + if(ms_vehiclesLoaded[i] == -1) + return i; + return -1; +} + +bool +CStreaming::AddToLoadedVehiclesList(int32 modelId) +{ + int i; + int id; + + if(ms_numVehiclesLoaded < desiredNumVehiclesLoaded){ + // still room for vehicles + for(i = 0; i < MAXVEHICLESLOADED; i++){ + if(ms_vehiclesLoaded[ms_lastVehicleDeleted] == -1) + break; + ms_lastVehicleDeleted++; + if(ms_lastVehicleDeleted == MAXVEHICLESLOADED) + ms_lastVehicleDeleted = 0; + } + assert(ms_vehiclesLoaded[ms_lastVehicleDeleted] == -1); + ms_numVehiclesLoaded++; + }else{ + // find vehicle we can remove + for(i = 0; i < MAXVEHICLESLOADED; i++){ + id = ms_vehiclesLoaded[ms_lastVehicleDeleted]; + if(id != -1 && CanRemoveModel(id) && + CModelInfo::GetModelInfo(id)->GetNumRefs() == 0) + goto found; + ms_lastVehicleDeleted++; + if(ms_lastVehicleDeleted == MAXVEHICLESLOADED) + ms_lastVehicleDeleted = 0; + } + id = -1; +found: + if(id == -1){ + // didn't find anything, try a free slot + id = GetAvailableVehicleSlot(); + if(id == -1) + return false; // still no luck + ms_lastVehicleDeleted = id; + // this is more than we wanted actually + ms_numVehiclesLoaded++; + } + else{ + RemoveModel(id); + CVehicleModelInfo* pVehicleInfo = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); + if (pVehicleInfo->m_vehicleClass != -1) + CCarCtrl::RemoveFromLoadedVehicleArray(id, pVehicleInfo->m_vehicleClass); + } + } + + ms_vehiclesLoaded[ms_lastVehicleDeleted++] = modelId; + if(ms_lastVehicleDeleted == MAXVEHICLESLOADED) + ms_lastVehicleDeleted = 0; + CVehicleModelInfo* pVehicleInfo = (CVehicleModelInfo*)CModelInfo::GetModelInfo(modelId); + if (pVehicleInfo->m_vehicleClass != -1) + CCarCtrl::AddToLoadedVehicleArray(modelId, pVehicleInfo->m_vehicleClass, pVehicleInfo->m_frequency); + return true; +} + +bool +CStreaming::IsObjectInCdImage(int32 id) +{ + uint32 posn, size; + return ms_aInfoForModel[id].GetCdPosnAndSize(posn, size); +} + +void +CStreaming::SetModelIsDeletable(int32 id) +{ + ms_aInfoForModel[id].m_flags &= ~STREAMFLAGS_DONT_REMOVE; + if ((id >= STREAM_OFFSET_TXD && id < STREAM_OFFSET_COL || CModelInfo::GetModelInfo(id)->GetModelType() != MITYPE_VEHICLE) && + (ms_aInfoForModel[id].m_flags & STREAMFLAGS_SCRIPTOWNED) == 0){ + if(ms_aInfoForModel[id].m_loadState != STREAMSTATE_LOADED) + RemoveModel(id); + else if(ms_aInfoForModel[id].m_next == nil) + ms_aInfoForModel[id].AddToList(&ms_startLoadedList); + } +} + +void +CStreaming::SetModelTxdIsDeletable(int32 id) +{ + SetModelIsDeletable(CModelInfo::GetModelInfo(id)->GetTxdSlot() + STREAM_OFFSET_TXD); +} + +void +CStreaming::SetMissionDoesntRequireModel(int32 id) +{ + ms_aInfoForModel[id].m_flags &= ~STREAMFLAGS_SCRIPTOWNED; + if ((id >= STREAM_OFFSET_TXD || CModelInfo::GetModelInfo(id)->GetModelType() != MITYPE_VEHICLE) && + (ms_aInfoForModel[id].m_flags & STREAMFLAGS_DONT_REMOVE) == 0){ + if(ms_aInfoForModel[id].m_loadState != STREAMSTATE_LOADED) + RemoveModel(id); + else if(ms_aInfoForModel[id].m_next == nil) + ms_aInfoForModel[id].AddToList(&ms_startLoadedList); + } +} + +void +CStreaming::LoadInitialPeds(void) +{ + RequestModel(MI_COP, STREAMFLAGS_DONT_REMOVE); + RequestModel(MI_MALE01, STREAMFLAGS_DONT_REMOVE); + RequestModel(MI_TAXI_D, STREAMFLAGS_DONT_REMOVE); +} + +void +CStreaming::LoadInitialWeapons(void) +{ + CStreaming::RequestModel(MI_NIGHTSTICK, STREAMFLAGS_DONT_REMOVE); + CStreaming::RequestModel(MI_MISSILE, STREAMFLAGS_DONT_REMOVE); +} + +void +CStreaming::LoadInitialVehicles(void) +{ + ms_numVehiclesLoaded = 0; + ms_lastVehicleDeleted = 0; + + RequestModel(MI_POLICE, STREAMFLAGS_DONT_REMOVE); +} + +void +CStreaming::StreamVehiclesAndPeds(void) +{ + int i, model; + static int timeBeforeNextLoad = 0; + static int modelQualityClass = 0; + + if(CRecordDataForGame::IsRecording() || + CRecordDataForGame::IsPlayingBack() +#ifdef FIX_BUGS + || CReplay::IsPlayingBack() +#endif + ) + return; + + if(FindPlayerPed()->m_pWanted->AreSwatRequired()){ + RequestModel(MI_ENFORCER, STREAMFLAGS_DONT_REMOVE); + RequestModel(MI_SWAT, STREAMFLAGS_DONT_REMOVE); + }else{ + SetModelIsDeletable(MI_ENFORCER); + if(!HasModelLoaded(MI_ENFORCER)) + SetModelIsDeletable(MI_SWAT); + } + + if(FindPlayerPed()->m_pWanted->AreFbiRequired()){ + RequestModel(MI_FBIRANCH, STREAMFLAGS_DONT_REMOVE); + RequestModel(MI_FBI, STREAMFLAGS_DONT_REMOVE); + }else{ + SetModelIsDeletable(MI_FBIRANCH); + if(!HasModelLoaded(MI_FBIRANCH)) + SetModelIsDeletable(MI_FBI); + } + + if(FindPlayerPed()->m_pWanted->AreArmyRequired()){ + RequestModel(MI_RHINO, STREAMFLAGS_DONT_REMOVE); + RequestModel(MI_BARRACKS, STREAMFLAGS_DONT_REMOVE); + RequestModel(MI_ARMY, STREAMFLAGS_DONT_REMOVE); + }else{ + SetModelIsDeletable(MI_RHINO); + SetModelIsDeletable(MI_BARRACKS); + if(!HasModelLoaded(MI_RHINO) && !HasModelLoaded(MI_BARRACKS)) + SetModelIsDeletable(MI_ARMY); + } + + if(FindPlayerPed()->m_pWanted->NumOfHelisRequired() > 0) + RequestModel(MI_CHOPPER, STREAMFLAGS_DONT_REMOVE); + else + SetModelIsDeletable(MI_CHOPPER); + + if (FindPlayerPed()->m_pWanted->AreMiamiViceRequired()) { + SetModelIsDeletable(MI_VICE1); + SetModelIsDeletable(MI_VICE2); + SetModelIsDeletable(MI_VICE3); + SetModelIsDeletable(MI_VICE4); + SetModelIsDeletable(MI_VICE5); + SetModelIsDeletable(MI_VICE6); + SetModelIsDeletable(MI_VICE7); + SetModelIsDeletable(MI_VICE8); + RequestModel(MI_VICECHEE, STREAMFLAGS_DONT_REMOVE); + if(CPopulation::NumMiamiViceCops == 0) + switch (CCarCtrl::MiamiViceCycle) { + case 0: + RequestModel(MI_VICE1, STREAMFLAGS_DONT_REMOVE); + RequestModel(MI_VICE2, STREAMFLAGS_DONT_REMOVE); + break; + case 1: + RequestModel(MI_VICE3, STREAMFLAGS_DONT_REMOVE); + RequestModel(MI_VICE4, STREAMFLAGS_DONT_REMOVE); + break; + case 2: + RequestModel(MI_VICE5, STREAMFLAGS_DONT_REMOVE); + RequestModel(MI_VICE6, STREAMFLAGS_DONT_REMOVE); + break; + case 3: + RequestModel(MI_VICE7, STREAMFLAGS_DONT_REMOVE); + RequestModel(MI_VICE8, STREAMFLAGS_DONT_REMOVE); + break; + } + } + else { + SetModelIsDeletable(MI_VICECHEE); + SetModelIsDeletable(MI_VICE1); + SetModelIsDeletable(MI_VICE2); + SetModelIsDeletable(MI_VICE3); + SetModelIsDeletable(MI_VICE4); + SetModelIsDeletable(MI_VICE5); + SetModelIsDeletable(MI_VICE6); + SetModelIsDeletable(MI_VICE7); + SetModelIsDeletable(MI_VICE8); + } + + if(timeBeforeNextLoad >= 0) + timeBeforeNextLoad--; + else if(ms_numVehiclesLoaded <= desiredNumVehiclesLoaded){ + CZoneInfo zone; + CVector coors = FindPlayerCoors(); + CTheZones::GetZoneInfoForTimeOfDay(&coors, &zone); + int32 maxReq = -1; + int32 mostRequestedRating = 0; + for(i = 0; i < CCarCtrl::TOTAL_CUSTOM_CLASSES; i++){ + if(CCarCtrl::NumRequestsOfCarRating[i] > maxReq && + ((i == 0 && zone.carThreshold[0] != 0) || +#ifdef FIX_BUGS + (i < CCarCtrl::NUM_CAR_CLASSES && zone.carThreshold[i] != zone.carThreshold[i-1]) || + (i == CCarCtrl::NUM_CAR_CLASSES && zone.boatThreshold[i - CCarCtrl::NUM_CAR_CLASSES] != 0) || + (i > CCarCtrl::NUM_CAR_CLASSES && i < CCarCtrl::TOTAL_CUSTOM_CLASSES && zone.boatThreshold[i - CCarCtrl::NUM_CAR_CLASSES] != zone.boatThreshold[i - CCarCtrl::NUM_CAR_CLASSES - 1]))) { +#else + (i != 0 && zone.carThreshold[i] != zone.carThreshold[i-1]))) { +#endif + maxReq = CCarCtrl::NumRequestsOfCarRating[i]; + mostRequestedRating = i; + } + } + model = CCarCtrl::ChooseCarModelToLoad(mostRequestedRating); + if(!HasModelLoaded(model)){ + RequestModel(model, STREAMFLAGS_DEPENDENCY); + timeBeforeNextLoad = 350; + } + CCarCtrl::NumRequestsOfCarRating[mostRequestedRating] = 0; + } +} + +void +CStreaming::StreamZoneModels(const CVector &pos) +{ + int i, j; + uint16 gangsToLoad, gangCarsToLoad, bit; + CZoneInfo info; + static int timeBeforeNextLoad = 0; + + CTheZones::GetZoneInfoForTimeOfDay(&pos, &info); + + if(info.pedGroup != ms_currentPedGrp){ + + // unload pevious group + if(ms_currentPedGrp != -1) + for(i = 0; i < NUMMODELSPERPEDGROUP; i++){ + ms_bIsPedFromPedGroupLoaded[i] = false; + if(CPopulation::ms_pPedGroups[ms_currentPedGrp].models[i] != -1){ + SetModelIsDeletable(CPopulation::ms_pPedGroups[ms_currentPedGrp].models[i]); + SetModelTxdIsDeletable(CPopulation::ms_pPedGroups[ms_currentPedGrp].models[i]); + } + } + + ms_currentPedGrp = info.pedGroup; + + for(i = 0; i < MAXZONEPEDSLOADED; i++){ + do + j = CGeneral::GetRandomNumberInRange(0, NUMMODELSPERPEDGROUP); + while(ms_bIsPedFromPedGroupLoaded[j]); + ms_bIsPedFromPedGroupLoaded[j] = true; + if(CPopulation::ms_pPedGroups[ms_currentPedGrp].models[j] != -1) + RequestModel(CPopulation::ms_pPedGroups[ms_currentPedGrp].models[j], STREAMFLAGS_DEPENDENCY); + } + ms_numPedsLoaded = MAXZONEPEDSLOADED; + timeBeforeNextLoad = 300; + } + + if(timeBeforeNextLoad >= 0) + timeBeforeNextLoad--; + else{ + // Switch a ped + int oldMI; + // Find a ped to unload + for(i = 0; i < NUMMODELSPERPEDGROUP; i++) + if(ms_bIsPedFromPedGroupLoaded[i]){ + oldMI = CPopulation::ms_pPedGroups[ms_currentPedGrp].models[i]; + if(oldMI != -1 && CModelInfo::GetModelInfo(oldMI)->GetNumRefs() == 0) + break; + } + // And load a new one + if(i != NUMMODELSPERPEDGROUP || ms_numPedsLoaded < MAXZONEPEDSLOADED){ + do + j = CGeneral::GetRandomNumberInRange(0, NUMMODELSPERPEDGROUP); + while(ms_bIsPedFromPedGroupLoaded[j]); + if(ms_numPedsLoaded == MAXZONEPEDSLOADED) + ms_bIsPedFromPedGroupLoaded[i] = false; + ms_bIsPedFromPedGroupLoaded[j] = true; + int newMI = CPopulation::ms_pPedGroups[ms_currentPedGrp].models[j]; + if(newMI != oldMI){ + RequestModel(newMI, STREAMFLAGS_DEPENDENCY); + debug("Request Ped %s\n", CModelInfo::GetModelInfo(newMI)->GetModelName()); + if(ms_numPedsLoaded == MAXZONEPEDSLOADED){ + SetModelIsDeletable(oldMI); + SetModelTxdIsDeletable(oldMI); + debug("Remove Ped %s\n", CModelInfo::GetModelInfo(oldMI)->GetModelName()); + }else + ms_numPedsLoaded++; + timeBeforeNextLoad = 300; + } + } + } + + RequestModel(MI_MALE01, STREAMFLAGS_DONT_REMOVE); + RequestModel(MI_TAXI_D, STREAMFLAGS_DONT_REMOVE); + + gangsToLoad = 0; + gangCarsToLoad = 0; + if(info.gangPedThreshold[0] != info.copPedThreshold) + gangsToLoad = 1; + for(i = 1; i < NUM_GANGS; i++) + if(info.gangPedThreshold[i] != info.gangPedThreshold[i-1]) + gangsToLoad |= 1< offset + ms_imageSize){ + // last read position is not in last image + for(i = 0; i < NUMCDIMAGES; i++){ + off = ms_imageOffsets[i]; + if(off == -1) continue; + if((uint32)lastPosn > (uint32)off) + // after start of image, get distance from end + // negative if before end! + dist = lastPosn - (off + ms_imageSize); + else + // before image, get offset to start + // this will never be negative + dist = off - lastPosn; + if(dist < mindist){ + img = i; + mindist = dist; + } + } + assert(img >= 0); + offset = ms_imageOffsets[img]; + ms_lastImageRead = img; + } + return offset; +} + +inline bool +ModelNotLoaded(int32 modelId) +{ + CStreamingInfo *si = &CStreaming::ms_aInfoForModel[modelId]; + return si->m_loadState != STREAMSTATE_LOADED && si->m_loadState != STREAMSTATE_READING; +} + +inline bool TxdNotLoaded(int32 txdId) { return ModelNotLoaded(txdId + STREAM_OFFSET_TXD); } +inline bool AnimNotLoaded(int32 animId) { return animId != -1 && ModelNotLoaded(animId + STREAM_OFFSET_ANIM); } + +// Find stream id of next requested file in cdimage +int32 +CStreaming::GetNextFileOnCd(int32 lastPosn, bool priority) +{ + CStreamingInfo *si, *next; + int streamId; + uint32 posn, size; + int streamIdFirst, streamIdNext; + uint32 posnFirst, posnNext; + + streamIdFirst = -1; + streamIdNext = -1; + posnFirst = UINT32_MAX; + posnNext = UINT32_MAX; + + for(si = ms_startRequestedList.m_next; si != &ms_endRequestedList; si = next){ + next = si->m_next; + streamId = si - ms_aInfoForModel; + + // only priority requests if there are any + if(priority && ms_numPriorityRequests != 0 && !si->IsPriority()) + continue; + + // request Txds or anims if necessary + if(streamId < STREAM_OFFSET_TXD){ + int txdId = CModelInfo::GetModelInfo(streamId)->GetTxdSlot(); + if(TxdNotLoaded(txdId)){ + ReRequestTxd(txdId); + continue; + } + int animId = CModelInfo::GetModelInfo(streamId)->GetAnimFileIndex(); + if(AnimNotLoaded(animId)){ + ReRequestAnim(animId); + continue; + } + }else if(streamId >= STREAM_OFFSET_ANIM && CCutsceneMgr::IsCutsceneProcessing()) + continue; + + if(ms_aInfoForModel[streamId].GetCdPosnAndSize(posn, size)){ + if(posn < posnFirst){ + // find first requested file in image + streamIdFirst = streamId; + posnFirst = posn; + } + if(posn < posnNext && posn >= (uint32)lastPosn){ + // find first requested file after last read position + streamIdNext = streamId; + posnNext = posn; + } + }else{ + // empty file + DecrementRef(streamId); + si->RemoveFromList(); + si->m_loadState = STREAMSTATE_LOADED; + } + } + + // wrap around + if(streamIdNext == -1) + streamIdNext = streamIdFirst; + + if(streamIdNext == -1 && ms_numPriorityRequests != 0){ + // try non-priority files + ms_numPriorityRequests = 0; + streamIdNext = GetNextFileOnCd(lastPosn, false); + } + + return streamIdNext; +} + +/* + * Streaming buffer size is half of the largest file. + * Files larger than the buffer size can only be loaded by channel 0, + * which then uses both buffers, while channel 1 is idle. + * ms_bLoadingBigModel is set to true to indicate this state. + */ + +// Make channel read from disc +void +CStreaming::RequestModelStream(int32 ch) +{ + int lastPosn, imgOffset, streamId; + int totalSize; + uint32 posn, size, unused; + int i; + int haveBigFile, havePed; + + lastPosn = CdStreamGetLastPosn(); + imgOffset = GetCdImageOffset(lastPosn); + streamId = GetNextFileOnCd(lastPosn - imgOffset, true); + + // remove Txds and Anims that aren't requested anymore + while(streamId != -1){ + if(ms_aInfoForModel[streamId].m_flags & STREAMFLAGS_KEEP_IN_MEMORY) + break; + if(streamId >= STREAM_OFFSET_TXD && streamId < STREAM_OFFSET_COL){ + if(IsTxdUsedByRequestedModels(streamId - STREAM_OFFSET_TXD)) + break; + }else if(streamId >= STREAM_OFFSET_ANIM){ + assert(streamId < NUMSTREAMINFO); + if(AreAnimsUsedByRequestedModels(streamId - STREAM_OFFSET_ANIM)) + break; + }else + break; + RemoveModel(streamId); + // so try next file + ms_aInfoForModel[streamId].GetCdPosnAndSize(posn, size); + streamId = GetNextFileOnCd(posn + size, true); + } + + if(streamId == -1) + return; + + ms_aInfoForModel[streamId].GetCdPosnAndSize(posn, size); + if(size > (uint32)ms_streamingBufferSize){ + // Can only load big models on channel 0, and 1 has to be idle + if(ch == 1 || ms_channel[1].state != CHANNELSTATE_IDLE) + return; + ms_bLoadingBigModel = true; + } + + // Load up to 4 adjacent files + haveBigFile = 0; + havePed = 0; + totalSize = 0; + for(i = 0; i < 4; i++){ + // no more files we can read + if(streamId == -1 || ms_aInfoForModel[streamId].m_loadState != STREAMSTATE_INQUEUE) + break; + + // also stop at non-priority files + ms_aInfoForModel[streamId].GetCdPosnAndSize(unused, size); + if(ms_numPriorityRequests != 0 && !ms_aInfoForModel[streamId].IsPriority()) + break; + + // Can't load certain combinations of files together + if(streamId < STREAM_OFFSET_TXD){ + if (havePed && CModelInfo::GetModelInfo(streamId)->GetModelType() == MITYPE_PED || + haveBigFile && CModelInfo::GetModelInfo(streamId)->GetModelType() == MITYPE_VEHICLE || + TxdNotLoaded(CModelInfo::GetModelInfo(streamId)->GetTxdSlot()) || + AnimNotLoaded(CModelInfo::GetModelInfo(streamId)->GetAnimFileIndex())) + break; + }else{ + if(haveBigFile && size > 200) + break; + } + + // Now add the file + ms_channel[ch].streamIds[i] = streamId; + ms_channel[ch].offsets[i] = totalSize; + totalSize += size; + + // To big for buffer, remove again + if(totalSize > ms_streamingBufferSize && i > 0){ + totalSize -= size; + break; + } + if(streamId < STREAM_OFFSET_TXD){ + if (CModelInfo::GetModelInfo(streamId)->GetModelType() == MITYPE_PED) + havePed = 1; + if (CModelInfo::GetModelInfo(streamId)->GetModelType() == MITYPE_VEHICLE) + haveBigFile = 1; + }else{ + if(size > 200) + haveBigFile = 1; + } + ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_READING; + ms_aInfoForModel[streamId].RemoveFromList(); + DecrementRef(streamId); + + streamId = ms_aInfoForModel[streamId].m_nextID; + } + + // clear remaining slots + for(; i < 4; i++) + ms_channel[ch].streamIds[i] = -1; + // Now read the data + assert(!(ms_bLoadingBigModel && ch == 1)); // this would clobber the buffer + if(CdStreamRead(ch, ms_pStreamingBuffer[ch], imgOffset+posn, totalSize) == STREAM_NONE) + debug("FUCKFUCKFUCK\n"); + ms_channel[ch].state = CHANNELSTATE_READING; + ms_channel[ch].field24 = 0; + ms_channel[ch].size = totalSize; + ms_channel[ch].position = imgOffset+posn; + ms_channel[ch].numTries = 0; +} + +// Load data previously read from disc +bool +CStreaming::ProcessLoadingChannel(int32 ch) +{ + int status; + int i, id, cdsize; + + status = CdStreamGetStatus(ch); + if(status != STREAM_NONE){ + // busy + if(status != STREAM_READING && status != STREAM_WAITING){ + ms_channelError = ch; + ms_channel[ch].state = CHANNELSTATE_ERROR; + ms_channel[ch].status = status; + } + return false; + } + + if(ms_channel[ch].state == CHANNELSTATE_STARTED){ + ms_channel[ch].state = CHANNELSTATE_IDLE; + FinishLoadingLargeFile(&ms_pStreamingBuffer[ch][ms_channel[ch].offsets[0]*CDSTREAM_SECTOR_SIZE], + ms_channel[ch].streamIds[0]); + ms_channel[ch].streamIds[0] = -1; + }else{ + ms_channel[ch].state = CHANNELSTATE_IDLE; + for(i = 0; i < 4; i++){ + id = ms_channel[ch].streamIds[i]; + if(id == -1) + continue; + + cdsize = ms_aInfoForModel[id].GetCdSize(); + if(id < STREAM_OFFSET_TXD && CModelInfo::GetModelInfo(id)->GetModelType() == MITYPE_VEHICLE && + ms_numVehiclesLoaded >= desiredNumVehiclesLoaded && + !RemoveLoadedVehicle() && + (CanRemoveModel(id) || GetAvailableVehicleSlot() == -1)){ + // can't load vehicle + RemoveModel(id); + if(!CanRemoveModel(id)) + ReRequestModel(id); + else if(CTxdStore::GetNumRefs(CModelInfo::GetModelInfo(id)->GetTxdSlot()) == 0) + RemoveTxd(CModelInfo::GetModelInfo(id)->GetTxdSlot()); + }else{ + MakeSpaceFor(cdsize * CDSTREAM_SECTOR_SIZE); + ConvertBufferToObject(&ms_pStreamingBuffer[ch][ms_channel[ch].offsets[i]*CDSTREAM_SECTOR_SIZE], + id); + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_STARTED){ + // queue for second part + ms_channel[ch].state = CHANNELSTATE_STARTED; + ms_channel[ch].offsets[0] = ms_channel[ch].offsets[i]; + ms_channel[ch].streamIds[0] = id; + if(i != 0) + ms_channel[ch].streamIds[i] = -1; + }else + ms_channel[ch].streamIds[i] = -1; + } + } + } + + if(ms_bLoadingBigModel && ms_channel[ch].state != CHANNELSTATE_STARTED){ + ms_bLoadingBigModel = false; + // reset channel 1 after loading a big model + for(i = 0; i < 4; i++) + ms_channel[1].streamIds[i] = -1; + ms_channel[1].state = CHANNELSTATE_IDLE; + } + + return true; +} + +void +CStreaming::RetryLoadFile(int32 ch) +{ + Const char *key; + + CPad::StopPadsShaking(); + + if(ms_channel[ch].numTries >= 3){ + switch(ms_channel[ch].status){ + case STREAM_ERROR_NOCD: key = "NOCD"; break; + case STREAM_ERROR_OPENCD: key = "OPENCD"; break; + case STREAM_ERROR_WRONGCD: key = "WRONGCD"; break; + default: key = "CDERROR"; break; + } + CHud::SetMessage(TheText.Get(key)); + CTimer::SetCodePause(true); + } + + switch(ms_channel[ch].state){ + case CHANNELSTATE_ERROR: + ms_channel[ch].numTries++; + if (CdStreamGetStatus(ch) == STREAM_READING || CdStreamGetStatus(ch) == STREAM_WAITING) break; + case CHANNELSTATE_IDLE: + CdStreamRead(ch, ms_pStreamingBuffer[ch], ms_channel[ch].position, ms_channel[ch].size); + ms_channel[ch].state = CHANNELSTATE_READING; + ms_channel[ch].field24 = -600; + break; + case CHANNELSTATE_READING: + if(ProcessLoadingChannel(ch)){ + ms_channelError = -1; + CTimer::SetCodePause(false); + } + break; + } +} + +void +CStreaming::LoadRequestedModels(void) +{ + static int currentChannel = 0; + + // We can't read with channel 1 while channel 0 is using its buffer + if(ms_bLoadingBigModel) + currentChannel = 0; + + // We have data, load + if(ms_channel[currentChannel].state == CHANNELSTATE_READING || + ms_channel[currentChannel].state == CHANNELSTATE_STARTED) + ProcessLoadingChannel(currentChannel); + + if(ms_channelError == -1){ + // Channel is idle, read more data + if(ms_channel[currentChannel].state == CHANNELSTATE_IDLE) + RequestModelStream(currentChannel); + // Switch channel + if(ms_channel[currentChannel].state != CHANNELSTATE_STARTED) + currentChannel = 1 - currentChannel; + } +} + + +// Let's load models in 4 threads; when one of them becomes idle, process the file, and fill thread with another file. Unfortunately processing models are still single-threaded. +// Currently only supported on POSIX streamer. +// WIP - some files are loaded swapped (CdStreamPosix problem?) +#if 0 //def ONE_THREAD_PER_CHANNEL +void +CStreaming::LoadAllRequestedModels(bool priority) +{ + static bool bInsideLoadAll = false; + int imgOffset, streamId, status; + int i; + uint32 posn, size; + + if(bInsideLoadAll) + return; + bInsideLoadAll = true; + + FlushChannels(); + imgOffset = GetCdImageOffset(CdStreamGetLastPosn()); + + int streamIds[ARRAY_SIZE(ms_pStreamingBuffer)]; + int streamSizes[ARRAY_SIZE(ms_pStreamingBuffer)]; + int streamPoses[ARRAY_SIZE(ms_pStreamingBuffer)]; + int readOrder[4] = {-1}; // Channel IDs ordered by read time + int readI = 0; + int processI = 0; + bool first = true; + + // All those "first" checks are because of variables aren't initialized in first pass. + + while (true) { + for (int i=0; i (uint32)ms_streamingBufferSize) { + if (i + 1 == ARRAY_SIZE(ms_pStreamingBuffer)) + break; + else if (!first && streamIds[i+1] != -1) + continue; + + } else { + // Buffer of current channel is part of a "big file", pass + if (i != 0 && streamIds[i-1] != -1 && streamSizes[i-1] > (uint32)ms_streamingBufferSize) + continue; + } + ms_aInfoForModel[streamId].RemoveFromList(); + DecrementRef(streamId); + + streamIds[i] = streamId; + streamSizes[i] = size; + streamPoses[i] = posn; + + if (!first) + assert(readOrder[readI] == -1); + + //printf("read: order %d, ch %d, id %d, size %d\n", readI, i, streamId, size); + + CdStreamRead(i, ms_pStreamingBuffer[i], imgOffset+posn, size); + readOrder[readI] = i; + if (first && readI+1 != ARRAY_SIZE(readOrder)) + readOrder[readI+1] = -1; + + readI = (readI + 1) % ARRAY_SIZE(readOrder); + } else { + ms_aInfoForModel[streamId].RemoveFromList(); + DecrementRef(streamId); + + ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_LOADED; + streamIds[i] = -1; + } + } else { + streamIds[i] = -1; + break; + } + } + + first = false; + int nextChannel = readOrder[processI]; + + // Now start processing + if (nextChannel == -1 || streamIds[nextChannel] == -1) + break; + + //printf("process: order %d, ch %d, id %d\n", processI, nextChannel, streamIds[nextChannel]); + + // Try again on error + while (CdStreamSync(nextChannel) != STREAM_NONE) { + CdStreamRead(nextChannel, ms_pStreamingBuffer[nextChannel], imgOffset+streamPoses[nextChannel], streamSizes[nextChannel]); + } + ms_aInfoForModel[streamIds[nextChannel]].m_loadState = STREAMSTATE_READING; + + MakeSpaceFor(streamSizes[nextChannel] * CDSTREAM_SECTOR_SIZE); + ConvertBufferToObject(ms_pStreamingBuffer[nextChannel], streamIds[nextChannel]); + if(ms_aInfoForModel[streamIds[nextChannel]].m_loadState == STREAMSTATE_STARTED) + FinishLoadingLargeFile(ms_pStreamingBuffer[nextChannel], streamIds[nextChannel]); + + if(streamIds[nextChannel] < STREAM_OFFSET_TXD){ + CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(streamIds[nextChannel]); + if(mi->IsSimple()) + mi->m_alpha = 255; + } + streamIds[nextChannel] = -1; + readOrder[processI] = -1; + processI = (processI + 1) % ARRAY_SIZE(readOrder); + } + + ms_bLoadingBigModel = false; + for(i = 0; i < 4; i++){ + ms_channel[1].streamIds[i] = -1; + ms_channel[1].offsets[i] = -1; + } + ms_channel[1].state = CHANNELSTATE_IDLE; + bInsideLoadAll = false; +} +#else +void +CStreaming::LoadAllRequestedModels(bool priority) +{ + static bool bInsideLoadAll = false; + int imgOffset, streamId, status; + int i; + uint32 posn, size; + + int numRequests = 4*ms_numModelsRequested; + + if(bInsideLoadAll) + return; + bInsideLoadAll = true; + + if(priority) + numRequests = ms_numPriorityRequests; + + FlushChannels(); + imgOffset = GetCdImageOffset(CdStreamGetLastPosn()); + + while(ms_endRequestedList.m_prev != &ms_startRequestedList && numRequests > 0){ + numRequests--; + streamId = GetNextFileOnCd(0, priority); + if(streamId == -1) + break; + + ms_aInfoForModel[streamId].RemoveFromList(); + ms_channel[0].streamIds[0] = streamId; + DecrementRef(streamId); + + if(ms_aInfoForModel[streamId].GetCdPosnAndSize(posn, size)){ + do + status = CdStreamRead(0, ms_pStreamingBuffer[0], imgOffset+posn, size); + while(CdStreamSync(0) || status == STREAM_NONE); + ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_READING; + + MakeSpaceFor(size * CDSTREAM_SECTOR_SIZE); + ConvertBufferToObject(ms_pStreamingBuffer[0], streamId); + if(ms_aInfoForModel[streamId].m_loadState == STREAMSTATE_STARTED) + FinishLoadingLargeFile(ms_pStreamingBuffer[0], streamId); + + if(streamId < STREAM_OFFSET_TXD){ + CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(streamId); + if(mi->IsSimple()) + mi->m_alpha = 255; + } + }else{ + // empty + ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_LOADED; + } + } + + ms_bLoadingBigModel = false; + for(i = 0; i < 4; i++){ + ms_channel[1].streamIds[i] = -1; + ms_channel[1].offsets[i] = -1; + } + ms_channel[1].state = CHANNELSTATE_IDLE; + bInsideLoadAll = false; +} +#endif + +void +CStreaming::FlushChannels(void) +{ + if(ms_channel[1].state == CHANNELSTATE_STARTED) + ProcessLoadingChannel(1); + + if(ms_channel[0].state == CHANNELSTATE_READING){ + CdStreamSync(0); + ProcessLoadingChannel(0); + } + if(ms_channel[0].state == CHANNELSTATE_STARTED) + ProcessLoadingChannel(0); + + if(ms_channel[1].state == CHANNELSTATE_READING){ + CdStreamSync(1); + ProcessLoadingChannel(1); + } + if(ms_channel[1].state == CHANNELSTATE_STARTED) + ProcessLoadingChannel(1); +} + +void +CStreaming::FlushRequestList(void) +{ + CStreamingInfo *si, *next; + + for(si = ms_startRequestedList.m_next; si != &ms_endRequestedList; si = next){ + next = si->m_next; + RemoveModel(si - ms_aInfoForModel); + } +#ifdef FLUSHABLE_STREAMING + if(ms_channel[0].state == CHANNELSTATE_READING) { + flushStream[0] = 1; + } + if(ms_channel[1].state == CHANNELSTATE_READING) { + flushStream[1] = 1; + } +#endif + FlushChannels(); +} + + +void +CStreaming::ImGonnaUseStreamingMemory(void) +{ + PUSH_MEMID(MEMID_STREAM); +} + +void +CStreaming::IHaveUsedStreamingMemory(void) +{ + POP_MEMID(); + UpdateMemoryUsed(); +} + +void +CStreaming::UpdateMemoryUsed(void) +{ +#ifdef USE_CUSTOM_ALLOCATOR + ms_memoryUsed = + gMainHeap.GetMemoryUsed(MEMID_STREAM) + + gMainHeap.GetMemoryUsed(MEMID_STREAM_MODELS) + + gMainHeap.GetMemoryUsed(MEMID_STREAM_TEXUTRES) + + gMainHeap.GetMemoryUsed(MEMID_STREAM_COLLISION) + + gMainHeap.GetMemoryUsed(MEMID_STREAM_ANIMATION); +#endif +} + +#define STREAM_DIST 80.0f + +void +CStreaming::AddModelsToRequestList(const CVector &pos, int32 flags) +{ + float xmin, xmax, ymin, ymax; + int ixmin, ixmax, iymin, iymax; + int ix, iy; + int dx, dy, d; + CSector *sect; + + xmin = pos.x - STREAM_DIST; + ymin = pos.y - STREAM_DIST; + xmax = pos.x + STREAM_DIST; + ymax = pos.y + STREAM_DIST; + + ixmin = CWorld::GetSectorIndexX(xmin); + if(ixmin < 0) ixmin = 0; + ixmax = CWorld::GetSectorIndexX(xmax); + if(ixmax >= NUMSECTORS_X) ixmax = NUMSECTORS_X-1; + iymin = CWorld::GetSectorIndexY(ymin); + if(iymin < 0) iymin = 0; + iymax = CWorld::GetSectorIndexY(ymax); + if(iymax >= NUMSECTORS_Y) iymax = NUMSECTORS_Y-1; + + CWorld::AdvanceCurrentScanCode(); + + for(iy = iymin; iy <= iymax; iy++){ + dy = iy - CWorld::GetSectorIndexY(pos.y); + for(ix = ixmin; ix <= ixmax; ix++){ + + if(CRenderer::m_loadingPriority && ms_numModelsRequested > 5) + return; + + dx = ix - CWorld::GetSectorIndexX(pos.x); + d = dx*dx + dy*dy; + sect = CWorld::GetSector(ix, iy); + if(d <= 0){ + ProcessEntitiesInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS], flags); + ProcessEntitiesInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], flags); + ProcessEntitiesInSectorList(sect->m_lists[ENTITYLIST_OBJECTS], flags); + ProcessEntitiesInSectorList(sect->m_lists[ENTITYLIST_DUMMIES], flags); + }else if(d <= 3*3){ + ProcessEntitiesInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS], pos.x, pos.y, xmin, ymin, xmax, ymax, flags); + ProcessEntitiesInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], pos.x, pos.y, xmin, ymin, xmax, ymax, flags); + ProcessEntitiesInSectorList(sect->m_lists[ENTITYLIST_OBJECTS], pos.x, pos.y, xmin, ymin, xmax, ymax, flags); + ProcessEntitiesInSectorList(sect->m_lists[ENTITYLIST_DUMMIES], pos.x, pos.y, xmin, ymin, xmax, ymax, flags); + } + } + } +} + +void +CStreaming::ProcessEntitiesInSectorList(CPtrList &list, float x, float y, float xmin, float ymin, float xmax, float ymax, int32 flags) +{ + CPtrNode *node; + CEntity *e; + float lodDistSq; + CVector2D pos; + + for(node = list.first; node; node = node->next){ + e = (CEntity*)node->item; + + if(e->m_scanCode == CWorld::GetCurrentScanCode()) + continue; + + e->m_scanCode = CWorld::GetCurrentScanCode(); + if(!e->bStreamingDontDelete && IsAreaVisible(e->m_area) && !e->bDontStream && e->bIsVisible){ + CTimeModelInfo *mi = (CTimeModelInfo*)CModelInfo::GetModelInfo(e->GetModelIndex()); + if (mi->GetModelType() != MITYPE_TIME || CClock::GetIsTimeInRange(mi->GetTimeOn(), mi->GetTimeOff())) { + lodDistSq = sq(mi->GetLargestLodDistance()); + lodDistSq = Min(lodDistSq, sq(STREAM_DIST)); + pos = CVector2D(e->GetPosition()); + if(xmin < pos.x && pos.x < xmax && + ymin < pos.y && pos.y < ymax && + (CVector2D(x, y) - pos).MagnitudeSqr() < lodDistSq) + RequestModel(e->GetModelIndex(), flags); + } + } + } +} + +void +CStreaming::ProcessEntitiesInSectorList(CPtrList &list, int32 flags) +{ + CPtrNode *node; + CEntity *e; + + for(node = list.first; node; node = node->next){ + e = (CEntity*)node->item; + + if(e->m_scanCode == CWorld::GetCurrentScanCode()) + continue; + + e->m_scanCode = CWorld::GetCurrentScanCode(); + if(!e->bStreamingDontDelete && IsAreaVisible(e->m_area) && !e->bDontStream && e->bIsVisible){ + CTimeModelInfo *mi = (CTimeModelInfo*)CModelInfo::GetModelInfo(e->GetModelIndex()); + if (mi->GetModelType() != MITYPE_TIME || CClock::GetIsTimeInRange(mi->GetTimeOn(), mi->GetTimeOff())) + RequestModel(e->GetModelIndex(), flags); + } + } +} + +void +CStreaming::DeleteFarAwayRwObjects(const CVector &pos) +{ + int posx, posy; + int x, y; + int r, i; + CSector *sect; + + posx = CWorld::GetSectorIndexX(pos.x); + posy = CWorld::GetSectorIndexY(pos.y); + + // Move oldSectorX/Y to new sector and delete RW objects in its "wake" for every step: + // O is the old sector, <- is the direction in which we move it, + // X are the sectors we delete RW objects from (except we go up to 10) + // X + // X X + // X X X + // X X X + // <- O X X X + // X X X + // X X X + // X X + // X + + while(posx != ms_oldSectorX){ + if(posx < ms_oldSectorX){ + for(r = 2; r <= 10; r++){ + x = ms_oldSectorX + r; + if(x < 0) + continue; + if(x >= NUMSECTORS_X) + break; + + for(i = -r; i <= r; i++){ + y = ms_oldSectorY + i; + if(y < 0) + continue; + if(y >= NUMSECTORS_Y) + break; + + sect = CWorld::GetSector(x, y); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS]); + DeleteRwObjectsInOverlapSectorList(sect->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], ms_oldSectorX, ms_oldSectorY); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_OBJECTS]); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_DUMMIES]); + } + } + ms_oldSectorX--; + }else{ + for(r = 2; r <= 10; r++){ + x = ms_oldSectorX - r; + if(x < 0) + break; + if(x >= NUMSECTORS_X) + continue; + + for(i = -r; i <= r; i++){ + y = ms_oldSectorY + i; + if(y < 0) + continue; + if(y >= NUMSECTORS_Y) + break; + + sect = CWorld::GetSector(x, y); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS]); + DeleteRwObjectsInOverlapSectorList(sect->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], ms_oldSectorX, ms_oldSectorY); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_OBJECTS]); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_DUMMIES]); + } + } + ms_oldSectorX++; + } + } + + while(posy != ms_oldSectorY){ + if(posy < ms_oldSectorY){ + for(r = 2; r <= 10; r++){ + y = ms_oldSectorY + r; + if(y < 0) + continue; + if(y >= NUMSECTORS_Y) + break; + + for(i = -r; i <= r; i++){ + x = ms_oldSectorX + i; + if(x < 0) + continue; + if(x >= NUMSECTORS_X) + break; + + sect = CWorld::GetSector(x, y); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS]); + DeleteRwObjectsInOverlapSectorList(sect->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], ms_oldSectorX, ms_oldSectorY); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_OBJECTS]); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_DUMMIES]); + } + } + ms_oldSectorY--; + }else{ + for(r = 2; r <= 10; r++){ + y = ms_oldSectorY - r; + if(y < 0) + break; + if(y >= NUMSECTORS_Y) + continue; + + for(i = -r; i <= r; i++){ + x = ms_oldSectorX + i; + if(x < 0) + continue; + if(x >= NUMSECTORS_X) + break; + + sect = CWorld::GetSector(x, y); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS]); + DeleteRwObjectsInOverlapSectorList(sect->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], ms_oldSectorX, ms_oldSectorY); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_OBJECTS]); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_DUMMIES]); + } + } + ms_oldSectorY++; + } + } +} + +void +CStreaming::DeleteAllRwObjects(void) +{ + int x, y; + CSector *sect; + + for(x = 0; x < NUMSECTORS_X; x++) + for(y = 0; y < NUMSECTORS_Y; y++){ + sect = CWorld::GetSector(x, y); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS]); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS_OVERLAP]); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_OBJECTS]); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_OBJECTS_OVERLAP]); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_DUMMIES]); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_DUMMIES_OVERLAP]); + } +} + +void +CStreaming::DeleteRwObjectsAfterDeath(const CVector &pos) +{ + int ix, iy; + int x, y; + CSector *sect; + + ix = CWorld::GetSectorIndexX(pos.x); + iy = CWorld::GetSectorIndexY(pos.y); + + for(x = 0; x < NUMSECTORS_X; x++) + for(y = 0; y < NUMSECTORS_Y; y++) + if(Abs(ix - x) > 3.0f && + Abs(iy - y) > 3.0f){ + sect = CWorld::GetSector(x, y); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS]); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS_OVERLAP]); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_OBJECTS]); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_OBJECTS_OVERLAP]); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_DUMMIES]); + DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_DUMMIES_OVERLAP]); + } +} + +void +CStreaming::DeleteRwObjectsBehindCamera(size_t mem) +{ + int ix, iy; + int x, y; + int xmin, xmax, ymin, ymax; + int inc; + CSector *sect; + + if(ms_memoryUsed < mem) + return; + + ix = CWorld::GetSectorIndexX(TheCamera.GetPosition().x); + iy = CWorld::GetSectorIndexY(TheCamera.GetPosition().y); + + if(Abs(TheCamera.GetForward().x) > Abs(TheCamera.GetForward().y)){ + // looking west/east + + ymin = Max(iy - 10, 0); + ymax = Min(iy + 10, NUMSECTORS_Y - 1); + assert(ymin <= ymax); + + // Delete a block of sectors that we know is behind the camera + if(TheCamera.GetForward().x > 0.0f){ + // looking east + xmax = Max(ix - 2, 0); + xmin = Max(ix - 10, 0); + inc = 1; + }else{ + // looking west + xmax = Min(ix + 2, NUMSECTORS_X - 1); + xmin = Min(ix + 10, NUMSECTORS_X - 1); + inc = -1; + } + for(y = ymin; y <= ymax; y++){ + for(x = xmin; x != xmax; x += inc){ + sect = CWorld::GetSector(x, y); + if(DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS], mem) || + DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_DUMMIES], mem) || + DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_OBJECTS], mem)) + return; + } + } + + + while(RemoveLoadedZoneModel()) + if(ms_memoryUsed < mem) + return; + + // Now a block that intersects with the camera's frustum + if(TheCamera.GetForward().x > 0.0f){ + // looking east + xmax = Max(ix + 10, 0); + xmin = Max(ix - 2, 0); + inc = 1; + }else{ + // looking west + xmax = Min(ix - 10, NUMSECTORS_X - 1); + xmin = Min(ix + 2, NUMSECTORS_X - 1); + inc = -1; + } + for(y = ymin; y <= ymax; y++){ + for(x = xmin; x != xmax; x += inc){ + sect = CWorld::GetSector(x, y); + if(DeleteRwObjectsNotInFrustumInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS], mem) || + DeleteRwObjectsNotInFrustumInSectorList(sect->m_lists[ENTITYLIST_DUMMIES], mem) || + DeleteRwObjectsNotInFrustumInSectorList(sect->m_lists[ENTITYLIST_OBJECTS], mem)) + return; + } + } + + // As last resort, delete objects from the last step more aggressively + for(y = ymin; y <= ymax; y++){ + for(x = xmax; x != xmin; x -= inc){ + sect = CWorld::GetSector(x, y); + if(DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS], mem) || + DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_DUMMIES], mem) || + DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_OBJECTS], mem)) + return; + } + } + }else{ + // looking north/south + + xmin = Max(ix - 10, 0); + xmax = Min(ix + 10, NUMSECTORS_X - 1); + assert(xmin <= xmax); + + // Delete a block of sectors that we know is behind the camera + if(TheCamera.GetForward().y > 0.0f){ + // looking north + ymax = Max(iy - 2, 0); + ymin = Max(iy - 10, 0); + inc = 1; + }else{ + // looking south + ymax = Min(iy + 2, NUMSECTORS_Y - 1); + ymin = Min(iy + 10, NUMSECTORS_Y - 1); + inc = -1; + } + for(x = xmin; x <= xmax; x++){ + for(y = ymin; y != ymax; y += inc){ + sect = CWorld::GetSector(x, y); + if(DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS], mem) || + DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_DUMMIES], mem) || + DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_OBJECTS], mem)) + return; + } + } + + while(RemoveLoadedZoneModel()) + if(ms_memoryUsed < mem) + return; + + // Now a block that intersects with the camera's frustum + if(TheCamera.GetForward().y > 0.0f){ + // looking north + ymax = Max(iy + 10, 0); + ymin = Max(iy - 2, 0); + inc = 1; + }else{ + // looking south + ymax = Min(iy - 10, NUMSECTORS_Y - 1); + ymin = Min(iy + 2, NUMSECTORS_Y - 1); + inc = -1; + } + for(x = xmin; x <= xmax; x++){ + for(y = ymin; y != ymax; y += inc){ + sect = CWorld::GetSector(x, y); + if(DeleteRwObjectsNotInFrustumInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS], mem) || + DeleteRwObjectsNotInFrustumInSectorList(sect->m_lists[ENTITYLIST_DUMMIES], mem) || + DeleteRwObjectsNotInFrustumInSectorList(sect->m_lists[ENTITYLIST_OBJECTS], mem)) + return; + } + } + +// this is gone in mobile together with RemoveReferencedTxds +// if(RemoveReferencedTxds(mem)) +// return; + + // As last resort, delete objects from the last step more aggressively + for(x = xmin; x <= xmax; x++){ + for(y = ymax; y != ymin; y -= inc){ + sect = CWorld::GetSector(x, y); + if(DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS], mem) || + DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_DUMMIES], mem) || + DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_OBJECTS], mem)) + return; + } + } + } + + while(ms_memoryUsed >= mem && RemoveLeastUsedModel(0)); +} + +void +CStreaming::DeleteRwObjectsInSectorList(CPtrList &list) +{ + CPtrNode *node; + CEntity *e; + + for(node = list.first; node; node = node->next){ + e = (CEntity*)node->item; + if(!e->bStreamingDontDelete && !e->bImBeingRendered) + e->DeleteRwObject(); + } +} + +void +CStreaming::DeleteRwObjectsInOverlapSectorList(CPtrList &list, int32 x, int32 y) +{ + CPtrNode *node; + CEntity *e; + + for(node = list.first; node; node = node->next){ + e = (CEntity*)node->item; + if(e->m_rwObject && !e->bStreamingDontDelete && !e->bImBeingRendered){ + // Now this is pretty weird... + if(Abs(CWorld::GetSectorIndexX(e->GetPosition().x) - x) >= 1.6f) +// { + e->DeleteRwObject(); +// return; // BUG? +// } + else // FIX? + if(Abs(CWorld::GetSectorIndexY(e->GetPosition().y) - y) >= 1.6f) + e->DeleteRwObject(); + } + } +} + +bool +CStreaming::DeleteRwObjectsBehindCameraInSectorList(CPtrList &list, size_t mem) +{ + CPtrNode *node; + CEntity *e; + + for(node = list.first; node; node = node->next){ + e = (CEntity*)node->item; + if(!e->bStreamingDontDelete && !e->bImBeingRendered && + e->m_rwObject && ms_aInfoForModel[e->GetModelIndex()].m_next && + FindPlayerPed()->m_pCurSurface != e){ + e->DeleteRwObject(); + if (CModelInfo::GetModelInfo(e->GetModelIndex())->GetNumRefs() == 0) { + RemoveModel(e->GetModelIndex()); + if(ms_memoryUsed < mem) + return true; + } + } + } + return false; +} + +bool +CStreaming::DeleteRwObjectsNotInFrustumInSectorList(CPtrList &list, size_t mem) +{ + CPtrNode *node; + CEntity *e; + + for(node = list.first; node; node = node->next){ + e = (CEntity*)node->item; + if(!e->bStreamingDontDelete && !e->bImBeingRendered && + e->m_rwObject && (!e->IsVisible() || e->bOffscreen) && ms_aInfoForModel[e->GetModelIndex()].m_next){ + e->DeleteRwObject(); + if (CModelInfo::GetModelInfo(e->GetModelIndex())->GetNumRefs() == 0) { + RemoveModel(e->GetModelIndex()); + if(ms_memoryUsed < mem) + return true; + } + } + } + return false; +} + +void +CStreaming::MakeSpaceFor(int32 size) +{ +#ifdef FIX_BUGS + if(ms_memoryAvailable == 0) { + ms_memoryAvailable = STREAMING_MEM_SIZE; + } +#endif + while(ms_memoryUsed >= ms_memoryAvailable - size) + if(!RemoveLeastUsedModel(STREAMFLAGS_20)){ + DeleteRwObjectsBehindCamera(ms_memoryAvailable - size); + return; + } +} + +void +CStreaming::LoadScene(const CVector &pos) +{ + CStreamingInfo *si, *prev; + eLevelName level; + + level = CTheZones::GetLevelFromPosition(&pos); + debug("Start load scene\n"); + for(si = ms_endRequestedList.m_prev; si != &ms_startRequestedList; si = prev){ + prev = si->m_prev; + if((si->m_flags & (STREAMFLAGS_KEEP_IN_MEMORY|STREAMFLAGS_PRIORITY)) == 0) + RemoveModel(si - ms_aInfoForModel); + } + CRenderer::m_loadingPriority = false; + DeleteAllRwObjects(); + if(level == LEVEL_GENERIC) + level = CGame::currLevel; + CGame::currLevel = level; + RemoveUnusedBigBuildings(level); + RequestBigBuildings(level, pos); + RequestBigBuildings(LEVEL_GENERIC, pos); + RemoveIslandsNotUsed(level); + LoadAllRequestedModels(false); + InstanceBigBuildings(level, pos); + InstanceBigBuildings(LEVEL_GENERIC, pos); + AddModelsToRequestList(pos, STREAMFLAGS_20); + CRadar::StreamRadarSections(pos); + + if (!CGame::IsInInterior()) { + for (int i = 0; i < 5; i++) { + CZoneInfo zone; + CTheZones::GetZoneInfoForTimeOfDay(&pos, &zone); + int32 model = CCarCtrl::ChooseCarModelToLoad(CCarCtrl::ChooseCarRating(&zone)); + CStreaming::RequestModel(model, STREAMFLAGS_DEPENDENCY); + } + } + LoadAllRequestedModels(false); + InstanceLoadedModels(pos); + + for(int i = 0; i < NUMSTREAMINFO; i++) + ms_aInfoForModel[i].m_flags &= ~STREAMFLAGS_20; + debug("End load scene\n"); +} + +void +CStreaming::LoadSceneCollision(const CVector &pos) +{ + CColStore::LoadCollision(pos); + CStreaming::LoadAllRequestedModels(false); +} + +void +CStreaming::MemoryCardSave(uint8 *buf, uint32 *size) +{ + int i; + + *size = NUM_DEFAULT_MODELS; + for(i = 0; i < NUM_DEFAULT_MODELS; i++) + if(ms_aInfoForModel[i].m_loadState == STREAMSTATE_LOADED) + buf[i] = ms_aInfoForModel[i].m_flags; + else + buf[i] = 0xFF; +} + +void +CStreaming::MemoryCardLoad(uint8 *buf, uint32 size) +{ + uint32 i; + + assert(size == NUM_DEFAULT_MODELS); + for(i = 0; i < size; i++) + if(ms_aInfoForModel[i].m_loadState == STREAMSTATE_LOADED) + if(buf[i] != 0xFF) + ms_aInfoForModel[i].m_flags = buf[i]; +} + +void +CStreaming::UpdateForAnimViewer(void) +{ + if (CStreaming::ms_channelError == -1) { + CStreaming::AddModelsToRequestList(CVector(0.0f, 0.0f, 0.0f), 0); + CStreaming::LoadRequestedModels(); + // original modifier was %d + sprintf(gString, "Requested %d, memory size %zuK\n", CStreaming::ms_numModelsRequested, 2 * CStreaming::ms_memoryUsed); + } + else { + CStreaming::RetryLoadFile(CStreaming::ms_channelError); + } +} + + +void +CStreaming::PrintStreamingBufferState() +{ + char str[128]; + wchar wstr[128]; + uint32 offset, size; + + CTimer::Stop(); + int i = 0; + while (i < NUMSTREAMINFO) { + while (true) { + int j = 0; + DoRWStuffStartOfFrame(50, 50, 50, 0, 0, 0, 255); + CPad::UpdatePads(); + CSprite2d::InitPerFrame(); + CFont::InitPerFrame(); + DefinedState(); + + CRect unusedRect(0, 0, RsGlobal.maximumWidth, RsGlobal.maximumHeight); + CRGBA unusedColor(255, 255, 255, 255); + CFont::SetFontStyle(FONT_BANK); + CFont::SetBackgroundOff(); + CFont::SetWrapx(DEFAULT_SCREEN_WIDTH); + CFont::SetScale(0.5f, 0.75f); + CFont::SetCentreOff(); + CFont::SetCentreSize(DEFAULT_SCREEN_WIDTH); + CFont::SetJustifyOff(); + CFont::SetColor(CRGBA(200, 200, 200, 200)); + CFont::SetBackGroundOnlyTextOff(); + int modelIndex = i; + if (modelIndex < NUMSTREAMINFO) { + int y = 24; + for ( ; j < 34 && modelIndex < NUMSTREAMINFO; modelIndex++) { + CStreamingInfo *streamingInfo = &ms_aInfoForModel[modelIndex]; + CBaseModelInfo *modelInfo = CModelInfo::GetModelInfo(modelIndex); + if (streamingInfo->m_loadState != STREAMSTATE_LOADED || !streamingInfo->GetCdPosnAndSize(offset, size)) + continue; + + if (modelIndex >= STREAM_OFFSET_TXD) + sprintf(str, "txd %s, refs %d, size %dK, flags 0x%x", CTxdStore::GetTxdName(modelIndex - STREAM_OFFSET_TXD), + CTxdStore::GetNumRefs(modelIndex - STREAM_OFFSET_TXD), 2 * size, streamingInfo->m_flags); + else + sprintf(str, "model %d,%s, refs%d, size%dK, flags%x", modelIndex, modelInfo->GetModelName(), modelInfo->GetNumRefs(), 2 * size, + streamingInfo->m_flags); + AsciiToUnicode(str, wstr); + CFont::PrintString(24.0f, y, wstr); + y += 12; + j++; + } + } + + if (CPad::GetPad(1)->GetCrossJustDown()) + i = modelIndex; + + if (!CPad::GetPad(1)->GetTriangleJustDown()) + break; + + i = 0; + CFont::DrawFonts(); + DoRWStuffEndOfFrame(); + } + CFont::DrawFonts(); + DoRWStuffEndOfFrame(); + } + CTimer::Update(); +} diff --git a/src/miami/core/Streaming.h b/src/miami/core/Streaming.h new file mode 100644 index 00000000..b3198d80 --- /dev/null +++ b/src/miami/core/Streaming.h @@ -0,0 +1,221 @@ +#pragma once + +#include "Game.h" + +enum { + STREAM_OFFSET_TXD = MODELINFOSIZE, + STREAM_OFFSET_COL = STREAM_OFFSET_TXD+TXDSTORESIZE, + STREAM_OFFSET_ANIM = STREAM_OFFSET_COL+COLSTORESIZE, + NUMSTREAMINFO = STREAM_OFFSET_ANIM+NUMANIMBLOCKS +}; + +enum StreamFlags +{ + STREAMFLAGS_DONT_REMOVE = 0x01, + STREAMFLAGS_SCRIPTOWNED = 0x02, + STREAMFLAGS_DEPENDENCY = 0x04, // Is this right? + STREAMFLAGS_PRIORITY = 0x08, + STREAMFLAGS_NOFADE = 0x10, + STREAMFLAGS_20 = 0x20, // TODO(MIAMI): what's this + + STREAMFLAGS_CANT_REMOVE = STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_SCRIPTOWNED, + STREAMFLAGS_KEEP_IN_MEMORY = STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_SCRIPTOWNED|STREAMFLAGS_DEPENDENCY, +}; + +enum StreamLoadState +{ + STREAMSTATE_NOTLOADED = 0, + STREAMSTATE_LOADED = 1, + STREAMSTATE_INQUEUE = 2, + STREAMSTATE_READING = 3, // channel is reading + STREAMSTATE_STARTED = 4, // first part loaded +}; + +enum ChannelState +{ + CHANNELSTATE_IDLE = 0, + CHANNELSTATE_READING = 1, + CHANNELSTATE_STARTED = 2, + CHANNELSTATE_ERROR = 3, +}; + +class CStreamingInfo +{ +public: + CStreamingInfo *m_next; + CStreamingInfo *m_prev; + uint8 m_loadState; + uint8 m_flags; + + int16 m_nextID; + uint32 m_position; + uint32 m_size; + + bool GetCdPosnAndSize(uint32 &posn, uint32 &size); + void SetCdPosnAndSize(uint32 posn, uint32 size); + void AddToList(CStreamingInfo *link); + void RemoveFromList(void); + uint32 GetCdSize(void) { return m_size; } + bool IsPriority(void) { return !!(m_flags & STREAMFLAGS_PRIORITY); } +}; + +struct CStreamingChannel +{ + int32 streamIds[4]; + int32 offsets[4]; + int32 state; + int32 field24; + int32 position; + int32 size; + int32 numTries; + int32 status; // from CdStream +}; + +class CDirectory; +class CPtrList; + +class CStreaming +{ +public: + static bool ms_disableStreaming; + static bool ms_bLoadingBigModel; + static int32 ms_numModelsRequested; + static CStreamingInfo ms_aInfoForModel[NUMSTREAMINFO]; + static CStreamingInfo ms_startLoadedList; + static CStreamingInfo ms_endLoadedList; + static CStreamingInfo ms_startRequestedList; + static CStreamingInfo ms_endRequestedList; + static int32 ms_oldSectorX; + static int32 ms_oldSectorY; + static int32 ms_streamingBufferSize; +#ifndef ONE_THREAD_PER_CHANNEL + static int8 *ms_pStreamingBuffer[2]; +#else + static int8 *ms_pStreamingBuffer[4]; +#endif + static size_t ms_memoryUsed; + static CStreamingChannel ms_channel[2]; + static int32 ms_channelError; + static int32 ms_numVehiclesLoaded; + static int32 ms_numPedsLoaded; + static int32 ms_vehiclesLoaded[MAXVEHICLESLOADED]; + static int32 ms_lastVehicleDeleted; + static bool ms_bIsPedFromPedGroupLoaded[NUMMODELSPERPEDGROUP]; + static CDirectory *ms_pExtraObjectsDir; + static int32 ms_numPriorityRequests; + static int32 ms_currentPedGrp; + static int32 ms_lastCullZone; + static uint16 ms_loadedGangs; + static uint16 ms_loadedGangCars; + static int32 ms_currentPedLoading; + static int32 ms_imageOffsets[NUMCDIMAGES]; + static int32 ms_lastImageRead; + static int32 ms_imageSize; + static size_t ms_memoryAvailable; + + static void Init(void); + static void Init2(void); + static void ReInit(void); + static void Shutdown(void); + static void Update(void); + static void LoadCdDirectory(void); + static void LoadCdDirectory(const char *dirname, int32 n); + static bool ConvertBufferToObject(int8 *buf, int32 streamId); + static bool FinishLoadingLargeFile(int8 *buf, int32 streamId); + static bool HasModelLoaded(int32 id) { return ms_aInfoForModel[id].m_loadState == STREAMSTATE_LOADED; } + static bool HasTxdLoaded(int32 id) { return HasModelLoaded(id+STREAM_OFFSET_TXD); } + static bool HasColLoaded(int32 id) { return HasModelLoaded(id+STREAM_OFFSET_COL); } + static bool HasAnimLoaded(int32 id) { return HasModelLoaded(id+STREAM_OFFSET_ANIM); } + static bool CanRemoveModel(int32 id) { return (ms_aInfoForModel[id].m_flags & STREAMFLAGS_CANT_REMOVE) == 0; } + static bool CanRemoveTxd(int32 id) { return CanRemoveModel(id+STREAM_OFFSET_TXD); } + static bool CanRemoveCol(int32 id) { return CanRemoveModel(id+STREAM_OFFSET_COL); } + static bool CanRemoveAnim(int32 id) { return CanRemoveModel(id+STREAM_OFFSET_ANIM); } + static void RequestModel(int32 model, int32 flags); + static void ReRequestModel(int32 model) { RequestModel(model, ms_aInfoForModel[model].m_flags); } + static void RequestTxd(int32 txd, int32 flags) { RequestModel(txd + STREAM_OFFSET_TXD, flags); } + static void ReRequestTxd(int32 txd) { ReRequestModel(txd + STREAM_OFFSET_TXD); } + static void RequestCol(int32 col, int32 flags) { RequestModel(col + STREAM_OFFSET_COL, flags); } + static void ReRequestCol(int32 col) { ReRequestModel(col + STREAM_OFFSET_COL); } + static void RequestAnim(int32 col, int32 flags) { RequestModel(col + STREAM_OFFSET_ANIM, flags); } + static void ReRequestAnim(int32 col) { ReRequestModel(col + STREAM_OFFSET_ANIM); } + static void RequestBigBuildings(eLevelName level); + static void RequestBigBuildings(eLevelName level, const CVector &pos); + static void InstanceBigBuildings(eLevelName level, const CVector &pos); + static void InstanceLoadedModelsInSectorList(CPtrList &list); + static void InstanceLoadedModels(const CVector &pos); + static void RequestIslands(eLevelName level); + static void RequestSpecialModel(int32 modelId, const char *modelName, int32 flags); + static void RequestSpecialChar(int32 charId, const char *modelName, int32 flags); + static bool HasSpecialCharLoaded(int32 id); + static void SetMissionDoesntRequireSpecialChar(int32 id); + static void DecrementRef(int32 id); + static void RemoveModel(int32 id); + #if !defined(DC_TEXCONV) + static void RemoveTxd(int32 id) { RemoveModel(id + STREAM_OFFSET_TXD); } + static void RemoveCol(int32 id) { RemoveModel(id + STREAM_OFFSET_COL); } + static void RemoveAnim(int32 id) { RemoveModel(id + STREAM_OFFSET_ANIM); } + #endif + static void RemoveUnusedBuildings(eLevelName level); + static void RemoveBuildings(eLevelName level); + static void RemoveBuildingsNotInArea(int32 area); + static void RemoveUnusedBigBuildings(eLevelName level); + static void RemoveIslandsNotUsed(eLevelName level); + static void RemoveBigBuildings(eLevelName level); + static bool RemoveLoadedVehicle(void); + static bool RemoveLeastUsedModel(uint32 excludeMask); + static void RemoveAllUnusedModels(void); + static void RemoveUnusedModelsInLoadedList(void); + static bool RemoveLoadedZoneModel(void); + static int32 GetAvailableVehicleSlot(void); + static bool IsTxdUsedByRequestedModels(int32 txdId); + static bool AreAnimsUsedByRequestedModels(int32 animId); + static bool AddToLoadedVehiclesList(int32 modelId); + static bool IsObjectInCdImage(int32 id); + static void SetModelIsDeletable(int32 id); + static void SetModelTxdIsDeletable(int32 id); + static void SetMissionDoesntRequireModel(int32 id); + static void LoadInitialPeds(void); + static void LoadInitialWeapons(void); + static void LoadInitialVehicles(void); + static void StreamVehiclesAndPeds(void); + static void StreamZoneModels(const CVector &pos); + static void RemoveCurrentZonesModels(void); + static void LoadBigBuildingsWhenNeeded(void); + + static int32 GetCdImageOffset(int32 lastPosn); + static int32 GetNextFileOnCd(int32 position, bool priority); + static void RequestModelStream(int32 ch); + static bool ProcessLoadingChannel(int32 ch); + static void RetryLoadFile(int32 ch); + static void LoadRequestedModels(void); + static void LoadAllRequestedModels(bool priority); + static void FlushChannels(void); + static void FlushRequestList(void); + + static void MakeSpaceFor(int32 size); + static void ImGonnaUseStreamingMemory(void); + static void IHaveUsedStreamingMemory(void); + static void UpdateMemoryUsed(void); + + static void AddModelsToRequestList(const CVector &pos, int32 flags); + static void ProcessEntitiesInSectorList(CPtrList &list, float x, float y, float xmin, float ymin, float xmax, float ymax, int32 flags); + static void ProcessEntitiesInSectorList(CPtrList &list, int32 flags); + static void DeleteFarAwayRwObjects(const CVector &pos); + static void DeleteAllRwObjects(void); + static void DeleteRwObjectsAfterDeath(const CVector &pos); + static void DeleteRwObjectsBehindCamera(size_t mem); // originally signed + static void DeleteRwObjectsInSectorList(CPtrList &list); + static void DeleteRwObjectsInOverlapSectorList(CPtrList &list, int32 x, int32 y); + static bool DeleteRwObjectsBehindCameraInSectorList(CPtrList &list, size_t mem); // originally signed + static bool DeleteRwObjectsNotInFrustumInSectorList(CPtrList &list, size_t mem); // originally signed + + static void LoadScene(const CVector &pos); + static void LoadSceneCollision(const CVector &pos); + + static void MemoryCardSave(uint8 *buffer, uint32 *length); + static void MemoryCardLoad(uint8 *buffer, uint32 length); + + static void UpdateForAnimViewer(void); + + static void PrintStreamingBufferState(); +}; diff --git a/src/miami/core/SurfaceTable.cpp b/src/miami/core/SurfaceTable.cpp new file mode 100644 index 00000000..c4b184bf --- /dev/null +++ b/src/miami/core/SurfaceTable.cpp @@ -0,0 +1,156 @@ +#include "common.h" + +#include "main.h" +#include "FileMgr.h" +#include "Weather.h" +#include "Collision.h" +#include "SurfaceTable.h" + +float CSurfaceTable::ms_aAdhesiveLimitTable[NUMADHESIVEGROUPS][NUMADHESIVEGROUPS]; + +void +CSurfaceTable::Initialise(Const char *filename) +{ + int lineno, fieldno; + char *line; + char surfname[256]; + float adhesiveLimit; + + CFileMgr::SetDir(""); + CFileMgr::LoadFile(filename, work_buff, sizeof(work_buff), "r"); + + line = (char*)work_buff; + for(lineno = 0; lineno < NUMADHESIVEGROUPS; lineno++){ + // skip white space and comments + while(*line == ' ' || *line == '\t' || *line == '\n' || *line == '\r' || *line == ';'){ + if(*line == ';'){ + while(*line != '\n' && *line != '\r') + line++; + }else + line++; + } + + sscanf(line, "%s", surfname); + // skip what we just read + while(!(*line == ' ' || *line == '\t' || *line == ',')) + line++; + + for(fieldno = 0; fieldno <= lineno; fieldno++){ + // skip white space + while(*line == ' ' || *line == '\t' || *line == ',') + line++; + adhesiveLimit = 0.0f; + if(*line != '-') + sscanf(line, "%f", &adhesiveLimit); + // skip what we just read + while(!(*line == ' ' || *line == '\t' || *line == ',' || *line == '\n')) + line++; + + ms_aAdhesiveLimitTable[lineno][fieldno] = adhesiveLimit; + ms_aAdhesiveLimitTable[fieldno][lineno] = adhesiveLimit; + } + } +} + +int +CSurfaceTable::GetAdhesionGroup(uint8 surfaceType) +{ + switch(surfaceType){ + case SURFACE_DEFAULT: return ADHESIVE_ROAD; + case SURFACE_TARMAC: return ADHESIVE_ROAD; + case SURFACE_GRASS: return ADHESIVE_LOOSE; + case SURFACE_GRAVEL: return ADHESIVE_LOOSE; + case SURFACE_MUD_DRY: return ADHESIVE_HARD; + case SURFACE_PAVEMENT: return ADHESIVE_ROAD; + case SURFACE_CAR: return ADHESIVE_HARD; + case SURFACE_GLASS: return ADHESIVE_HARD; + case SURFACE_TRANSPARENT_CLOTH: return ADHESIVE_HARD; + case SURFACE_GARAGE_DOOR: return ADHESIVE_HARD; + case SURFACE_CAR_PANEL: return ADHESIVE_HARD; + case SURFACE_THICK_METAL_PLATE: return ADHESIVE_HARD; + case SURFACE_SCAFFOLD_POLE: return ADHESIVE_HARD; + case SURFACE_LAMP_POST: return ADHESIVE_HARD; + case SURFACE_FIRE_HYDRANT: return ADHESIVE_HARD; + case SURFACE_GIRDER: return ADHESIVE_HARD; + case SURFACE_METAL_CHAIN_FENCE: return ADHESIVE_HARD; + case SURFACE_PED: return ADHESIVE_RUBBER; + case SURFACE_SAND: return ADHESIVE_SAND; + case SURFACE_WATER: return ADHESIVE_WET; + case SURFACE_WOOD_CRATES: return ADHESIVE_ROAD; + case SURFACE_WOOD_BENCH: return ADHESIVE_ROAD; + case SURFACE_WOOD_SOLID: return ADHESIVE_ROAD; + case SURFACE_RUBBER: return ADHESIVE_RUBBER; + case SURFACE_PLASTIC: return ADHESIVE_HARD; + case SURFACE_HEDGE: return ADHESIVE_LOOSE; + case SURFACE_STEEP_CLIFF: return ADHESIVE_LOOSE; + case SURFACE_CONTAINER: return ADHESIVE_HARD; + case SURFACE_NEWS_VENDOR: return ADHESIVE_HARD; + case SURFACE_WHEELBASE: return ADHESIVE_RUBBER; + case SURFACE_CARDBOARDBOX: return ADHESIVE_LOOSE; + case SURFACE_TRANSPARENT_STONE: return ADHESIVE_HARD; + case SURFACE_METAL_GATE: return ADHESIVE_HARD; + case SURFACE_SAND_BEACH: return ADHESIVE_SAND; + case SURFACE_CONCRETE_BEACH: return ADHESIVE_ROAD; + default: return ADHESIVE_ROAD; + } +} + +float +CSurfaceTable::GetWetMultiplier(uint8 surfaceType) +{ + switch(surfaceType){ + case SURFACE_DEFAULT: + case SURFACE_TARMAC: + case SURFACE_MUD_DRY: + case SURFACE_PAVEMENT: + case SURFACE_TRANSPARENT_CLOTH: + case SURFACE_WOOD_CRATES: + case SURFACE_WOOD_BENCH: + case SURFACE_WOOD_SOLID: + case SURFACE_HEDGE: + case SURFACE_CARDBOARDBOX: + case SURFACE_TRANSPARENT_STONE: + case SURFACE_CONCRETE_BEACH: + return 1.0f - CWeather::WetRoads*0.25f; + + case SURFACE_GRASS: + case SURFACE_CAR: + case SURFACE_GLASS: + case SURFACE_GARAGE_DOOR: + case SURFACE_CAR_PANEL: + case SURFACE_THICK_METAL_PLATE: + case SURFACE_SCAFFOLD_POLE: + case SURFACE_LAMP_POST: + case SURFACE_FIRE_HYDRANT: + case SURFACE_GIRDER: + case SURFACE_METAL_CHAIN_FENCE: + case SURFACE_PED: + case SURFACE_RUBBER: + case SURFACE_PLASTIC: + case SURFACE_STEEP_CLIFF: + case SURFACE_CONTAINER: + case SURFACE_NEWS_VENDOR: + case SURFACE_WHEELBASE: + case SURFACE_METAL_GATE: + return 1.0f - CWeather::WetRoads*0.4f; + + case SURFACE_SAND: + case SURFACE_SAND_BEACH: + return 1.0f + CWeather::WetRoads*0.5f; + + default: + return 1.0f; + } +} + +float +CSurfaceTable::GetAdhesiveLimit(CColPoint &colpoint) +{ + return ms_aAdhesiveLimitTable[GetAdhesionGroup(colpoint.surfaceB)][GetAdhesionGroup(colpoint.surfaceA)]; +} + +bool +CSurfaceTable::IsSoftLanding(uint8 surf) +{ + return surf == SURFACE_GRASS || surf == SURFACE_SAND || surf == SURFACE_SAND_BEACH; +} diff --git a/src/miami/core/SurfaceTable.h b/src/miami/core/SurfaceTable.h new file mode 100644 index 00000000..8ff43106 --- /dev/null +++ b/src/miami/core/SurfaceTable.h @@ -0,0 +1,101 @@ +#pragma once + +enum eSurfaceType +{ + SURFACE_DEFAULT, + SURFACE_TARMAC, + SURFACE_GRASS, + SURFACE_GRAVEL, + SURFACE_MUD_DRY, + SURFACE_PAVEMENT, + SURFACE_CAR, + SURFACE_GLASS, + SURFACE_TRANSPARENT_CLOTH, + SURFACE_GARAGE_DOOR, + SURFACE_CAR_PANEL, + SURFACE_THICK_METAL_PLATE, + SURFACE_SCAFFOLD_POLE, + SURFACE_LAMP_POST, + SURFACE_FIRE_HYDRANT, + SURFACE_GIRDER, + SURFACE_METAL_CHAIN_FENCE, + SURFACE_PED, + SURFACE_SAND, + SURFACE_WATER, + SURFACE_WOOD_CRATES, + SURFACE_WOOD_BENCH, + SURFACE_WOOD_SOLID, + SURFACE_RUBBER, + SURFACE_PLASTIC, + SURFACE_HEDGE, + SURFACE_STEEP_CLIFF, + SURFACE_CONTAINER, + SURFACE_NEWS_VENDOR, + SURFACE_WHEELBASE, + SURFACE_CARDBOARDBOX, + SURFACE_TRANSPARENT_STONE, + SURFACE_METAL_GATE, + SURFACE_SAND_BEACH, + SURFACE_CONCRETE_BEACH, +}; + +enum +{ + ADHESIVE_RUBBER, + ADHESIVE_HARD, + ADHESIVE_ROAD, + ADHESIVE_LOOSE, + ADHESIVE_SAND, + ADHESIVE_WET, + + NUMADHESIVEGROUPS +}; + +struct CColPoint; + +inline bool +IsSeeThrough(uint8 surfType) +{ + switch(surfType) + case SURFACE_GLASS: + case SURFACE_TRANSPARENT_CLOTH: + case SURFACE_METAL_CHAIN_FENCE: + case SURFACE_TRANSPARENT_STONE: + case SURFACE_SCAFFOLD_POLE: + return true; + return false; +} + +// I think the necessity of this function is really a bug +inline bool +IsSeeThroughVertical(uint8 surfType) +{ + switch(surfType) + case SURFACE_GLASS: + case SURFACE_TRANSPARENT_CLOTH: + return true; + return false; +} + +inline bool +IsShootThrough(uint8 surfType) +{ + switch(surfType) + case SURFACE_TRANSPARENT_CLOTH: + case SURFACE_METAL_CHAIN_FENCE: + case SURFACE_TRANSPARENT_STONE: + case SURFACE_SCAFFOLD_POLE: + return true; + return false; +} + +class CSurfaceTable +{ + static float ms_aAdhesiveLimitTable[NUMADHESIVEGROUPS][NUMADHESIVEGROUPS]; +public: + static void Initialise(Const char *filename); + static int GetAdhesionGroup(uint8 surfaceType); + static float GetWetMultiplier(uint8 surfaceType); + static float GetAdhesiveLimit(CColPoint &colpoint); + static bool IsSoftLanding(uint8 surf); +}; diff --git a/src/miami/core/TimeStep.cpp b/src/miami/core/TimeStep.cpp new file mode 100644 index 00000000..09dae911 --- /dev/null +++ b/src/miami/core/TimeStep.cpp @@ -0,0 +1,5 @@ +#include "TimeStep.h" + +float CTimeStep::ms_fTimeScale = 1.0f; +float CTimeStep::ms_fFramesPerUpdate = 1.0f; +float CTimeStep::ms_fTimeStep = 1.0f; diff --git a/src/miami/core/TimeStep.h b/src/miami/core/TimeStep.h new file mode 100644 index 00000000..6101b4c2 --- /dev/null +++ b/src/miami/core/TimeStep.h @@ -0,0 +1,10 @@ +#pragma once + +// Pretty sure this class is not used by the game +class CTimeStep +{ +public: + static float ms_fTimeScale; + static float ms_fFramesPerUpdate; + static float ms_fTimeStep; +}; diff --git a/src/miami/core/Timer.cpp b/src/miami/core/Timer.cpp new file mode 100644 index 00000000..77f26a8b --- /dev/null +++ b/src/miami/core/Timer.cpp @@ -0,0 +1,331 @@ +#define WITHWINDOWS +#include "common.h" +#include "crossplatform.h" + +#include "DMAudio.h" +#include "Record.h" +#include "Timer.h" +#include "SpecialFX.h" + +uint32 CTimer::m_snTimeInMilliseconds; +uint32 CTimer::m_snTimeInMillisecondsPauseMode = 1; + +uint32 CTimer::m_snTimeInMillisecondsNonClipped; +uint32 CTimer::m_snPreviousTimeInMilliseconds; +uint32 CTimer::m_FrameCounter; +float CTimer::ms_fTimeScale; +float CTimer::ms_fTimeStep; +float CTimer::ms_fTimeStepNonClipped; +bool CTimer::m_UserPause; +bool CTimer::m_CodePause; +#ifdef FIX_BUGS +uint32 CTimer::m_LogicalFrameCounter; +uint32 CTimer::m_LogicalFramesPassed; +#endif + +uint32 _nCyclesPerMS = 1; + +#ifdef _WIN32 +LARGE_INTEGER _oldPerfCounter; +LARGE_INTEGER perfSuspendCounter; +#define RsTimerType uint32 +#else +#define RsTimerType double +#endif + +RsTimerType oldPcTimer; + +RsTimerType suspendPcTimer; + +uint32 suspendDepth; + +void CTimer::Initialise(void) +{ + debug("Initialising CTimer...\n"); + + ms_fTimeScale = 1.0f; + ms_fTimeStep = 1.0f; + suspendDepth = 0; + m_UserPause = false; + m_CodePause = false; + m_snTimeInMillisecondsNonClipped = 0; + m_snPreviousTimeInMilliseconds = 0; + m_snTimeInMilliseconds = 1; +#ifdef FIX_BUGS + m_LogicalFrameCounter = 0; + m_LogicalFramesPassed = 0; +#endif + +#ifdef _WIN32 + LARGE_INTEGER perfFreq; + if ( QueryPerformanceFrequency(&perfFreq) ) + { + OutputDebugString("Performance counter available\n"); + _nCyclesPerMS = uint32(perfFreq.QuadPart / 1000); + QueryPerformanceCounter(&_oldPerfCounter); + } + else +#endif + { + OutputDebugString("Performance counter not available, using millesecond timer\n"); + _nCyclesPerMS = 0; + oldPcTimer = RsTimer(); + } + + m_snTimeInMilliseconds = m_snPreviousTimeInMilliseconds; + + m_FrameCounter = 0; + + DMAudio.ResetTimers(m_snPreviousTimeInMilliseconds); + + debug("CTimer ready\n"); +} + +void CTimer::Shutdown(void) +{ + ; +} + +#ifdef FIX_BUGS +void CTimer::Update(void) +{ + static double frameTimeLogical = 0.0; + static double frameTimeFraction = 0.0; + static double frameTimeFractionScaled = 0.0; + double frameTime; + double dblUpdInMs; + + m_snPreviousTimeInMilliseconds = m_snTimeInMilliseconds; + +#ifdef _WIN32 + if ( (double)_nCyclesPerMS != 0.0 ) + { + LARGE_INTEGER pc; + QueryPerformanceCounter(&pc); + + int32 updInCycles = (pc.LowPart - _oldPerfCounter.LowPart); // & 0x7FFFFFFF; pointless + + _oldPerfCounter = pc; + + float updInCyclesScaled = GetIsPaused() ? updInCycles : updInCycles * ms_fTimeScale; + + frameTime = updInCyclesScaled / (double)_nCyclesPerMS; + + dblUpdInMs = (double)updInCycles / (double)_nCyclesPerMS; + } + else +#endif + { + RsTimerType timer = RsTimer(); + + RsTimerType updInMs = timer - oldPcTimer; + + frameTime = (double)updInMs * ms_fTimeScale; + + oldPcTimer = timer; + + dblUpdInMs = (double)updInMs; + } + + // count frames as if we're running at 30 fps + m_LogicalFramesPassed = 0; + frameTimeLogical += dblUpdInMs; + while (frameTimeLogical >= 1000.0 / 30.0) { + frameTimeLogical -= 1000.0 / 30.0; + m_LogicalFramesPassed++; + } + m_LogicalFrameCounter += m_LogicalFramesPassed; + + frameTimeFraction += dblUpdInMs; + frameTimeFractionScaled += frameTime; + + m_snTimeInMillisecondsPauseMode += uint32(frameTimeFraction); + + if ( GetIsPaused() ) + ms_fTimeStep = 0.0f; + else + { + m_snTimeInMilliseconds += uint32(frameTimeFractionScaled); + m_snTimeInMillisecondsNonClipped += uint32(frameTimeFractionScaled); + ms_fTimeStep = frameTime / 1000.0f * 50.0f; + } + frameTimeFraction -= uint32(frameTimeFraction); + frameTimeFractionScaled -= uint32(frameTimeFractionScaled); + + if ( ms_fTimeStep < 0.01f && !GetIsPaused() && !CSpecialFX::bSnapShotActive) + ms_fTimeStep = 0.01f; + + ms_fTimeStepNonClipped = ms_fTimeStep; + + if ( !CRecordDataForGame::IsPlayingBack() ) + { + ms_fTimeStep = Min(3.0f, ms_fTimeStep); + + if ( (m_snTimeInMilliseconds - m_snPreviousTimeInMilliseconds) > 60 ) + m_snTimeInMilliseconds = m_snPreviousTimeInMilliseconds + 60; + } + + if ( CRecordDataForChase::IsRecording() ) + { + ms_fTimeStep = 1.0f; + m_snTimeInMilliseconds = m_snPreviousTimeInMilliseconds + 16; + } + + m_FrameCounter++; +} +#else +void CTimer::Update(void) +{ + m_snPreviousTimeInMilliseconds = m_snTimeInMilliseconds; + +#ifdef _WIN32 + if ( (double)_nCyclesPerMS != 0.0 ) + { + LARGE_INTEGER pc; + QueryPerformanceCounter(&pc); + + int32 updInCycles = (pc.LowPart - _oldPerfCounter.LowPart); // & 0x7FFFFFFF; pointless + + _oldPerfCounter = pc; + + float updInCyclesScaled = GetIsPaused() ? updInCycles : updInCycles * ms_fTimeScale; + + double frameTime = updInCyclesScaled / (double)_nCyclesPerMS; + + m_snTimeInMillisecondsPauseMode = m_snTimeInMillisecondsPauseMode + frameTime; + + if ( GetIsPaused() ) + ms_fTimeStep = 0.0f; + else + { + m_snTimeInMilliseconds = m_snTimeInMilliseconds + frameTime; + m_snTimeInMillisecondsNonClipped = m_snTimeInMillisecondsNonClipped + frameTime; + ms_fTimeStep = frameTime / 1000.0f * 50.0f; + } + } + else +#endif + { + RsTimerType timer = RsTimer(); + + RsTimerType updInMs = timer - oldPcTimer; + + double frameTime = (double)updInMs * ms_fTimeScale; + + oldPcTimer = timer; + + m_snTimeInMillisecondsPauseMode = m_snTimeInMillisecondsPauseMode + frameTime; + + if ( GetIsPaused() ) + ms_fTimeStep = 0.0f; + else + { + m_snTimeInMilliseconds = m_snTimeInMilliseconds + frameTime; + m_snTimeInMillisecondsNonClipped = m_snTimeInMillisecondsNonClipped + frameTime; + ms_fTimeStep = frameTime / 1000.0f * 50.0f; + } + } + + if ( ms_fTimeStep < 0.01f && !GetIsPaused() && !CSpecialFX::bSnapShotActive) + ms_fTimeStep = 0.01f; + + ms_fTimeStepNonClipped = ms_fTimeStep; + + if ( !CRecordDataForGame::IsPlayingBack() ) + { + ms_fTimeStep = Min(3.0f, ms_fTimeStep); + + if ( (m_snTimeInMilliseconds - m_snPreviousTimeInMilliseconds) > 60 ) + m_snTimeInMilliseconds = m_snPreviousTimeInMilliseconds + 60; + } + + if ( CRecordDataForChase::IsRecording() ) + { + ms_fTimeStep = 1.0f; + m_snTimeInMilliseconds = m_snPreviousTimeInMilliseconds + 16; + } + + m_FrameCounter++; +} +#endif + +void CTimer::Suspend(void) +{ + if ( ++suspendDepth > 1 ) + return; + +#ifdef _WIN32 + if ( (double)_nCyclesPerMS != 0.0 ) + QueryPerformanceCounter(&perfSuspendCounter); + else +#endif + suspendPcTimer = RsTimer(); +} + +void CTimer::Resume(void) +{ + if ( --suspendDepth != 0 ) + return; + +#ifdef _WIN32 + if ( (double)_nCyclesPerMS != 0.0 ) + { + LARGE_INTEGER pc; + QueryPerformanceCounter(&pc); + + _oldPerfCounter.LowPart += pc.LowPart - perfSuspendCounter.LowPart; + } + else +#endif + oldPcTimer += RsTimer() - suspendPcTimer; +} + +uint32 CTimer::GetCyclesPerMillisecond(void) +{ +#ifdef _WIN32 + if (_nCyclesPerMS != 0) + return _nCyclesPerMS; + else +#endif + return 1; +} + +uint32 CTimer::GetCurrentTimeInCycles(void) +{ +#ifdef _WIN32 + if ( _nCyclesPerMS != 0 ) + { + LARGE_INTEGER pc; + QueryPerformanceCounter(&pc); + return (pc.LowPart - _oldPerfCounter.LowPart); // & 0x7FFFFFFF; pointless + } + else +#endif + return RsTimer() - oldPcTimer; +} + +bool CTimer::GetIsSlowMotionActive(void) +{ + return ms_fTimeScale < 1.0f; +} + +void CTimer::Stop(void) +{ + m_snPreviousTimeInMilliseconds = m_snTimeInMilliseconds; +} + +void CTimer::StartUserPause(void) +{ + m_UserPause = true; +} + +void CTimer::EndUserPause(void) +{ + m_UserPause = false; +} + +uint32 CTimer::GetCyclesPerFrame() +{ + return 20; +} + diff --git a/src/miami/core/Timer.h b/src/miami/core/Timer.h new file mode 100644 index 00000000..819bd30c --- /dev/null +++ b/src/miami/core/Timer.h @@ -0,0 +1,71 @@ +#pragma once + +class CTimer +{ + + static uint32 m_snTimeInMilliseconds; + static uint32 m_snTimeInMillisecondsPauseMode; + static uint32 m_snTimeInMillisecondsNonClipped; + static uint32 m_snPreviousTimeInMilliseconds; + static uint32 m_FrameCounter; + static float ms_fTimeScale; + static float ms_fTimeStep; + static float ms_fTimeStepNonClipped; +#ifdef FIX_BUGS + static uint32 m_LogicalFrameCounter; + static uint32 m_LogicalFramesPassed; +#endif +public: + static bool m_UserPause; + static bool m_CodePause; + + static const float &GetTimeStep(void) { return ms_fTimeStep; } + static void SetTimeStep(float ts) { ms_fTimeStep = ts; } + static float GetTimeStepInSeconds() { return ms_fTimeStep / 50.0f; } + static uint32 GetTimeStepInMilliseconds() { return ms_fTimeStep / 50.0f * 1000.0f; } + static const float &GetTimeStepNonClipped(void) { return ms_fTimeStepNonClipped; } + static float GetTimeStepNonClippedInSeconds(void) { return ms_fTimeStepNonClipped / 50.0f; } + static float GetTimeStepNonClippedInMilliseconds(void) { return ms_fTimeStepNonClipped / 50.0f * 1000.0f; } + static void SetTimeStepNonClipped(float ts) { ms_fTimeStepNonClipped = ts; } + static const uint32 &GetFrameCounter(void) { return m_FrameCounter; } + static void SetFrameCounter(uint32 fc) { m_FrameCounter = fc; } + static const uint32 &GetTimeInMilliseconds(void) { return m_snTimeInMilliseconds; } + static void SetTimeInMilliseconds(uint32 t) { m_snTimeInMilliseconds = t; } + static uint32 GetTimeInMillisecondsNonClipped(void) { return m_snTimeInMillisecondsNonClipped; } + static void SetTimeInMillisecondsNonClipped(uint32 t) { m_snTimeInMillisecondsNonClipped = t; } + static uint32 GetTimeInMillisecondsPauseMode(void) { return m_snTimeInMillisecondsPauseMode; } + static void SetTimeInMillisecondsPauseMode(uint32 t) { m_snTimeInMillisecondsPauseMode = t; } + static uint32 GetPreviousTimeInMilliseconds(void) { return m_snPreviousTimeInMilliseconds; } + static void SetPreviousTimeInMilliseconds(uint32 t) { m_snPreviousTimeInMilliseconds = t; } + static const float &GetTimeScale(void) { return ms_fTimeScale; } + static void SetTimeScale(float ts) { ms_fTimeScale = ts; } + static uint32 GetCyclesPerFrame(); + + static bool GetIsPaused() { return m_UserPause || m_CodePause; } + static bool GetIsUserPaused() { return m_UserPause; } + static bool GetIsCodePaused() { return m_CodePause; } + static void SetCodePause(bool pause) { m_CodePause = pause; } + + static void Initialise(void); + static void Shutdown(void); + static void Update(void); + static void Suspend(void); + static void Resume(void); + static uint32 GetCyclesPerMillisecond(void); + static uint32 GetCurrentTimeInCycles(void); + static bool GetIsSlowMotionActive(void); + static void Stop(void); + static void StartUserPause(void); + static void EndUserPause(void); + + friend bool GenericLoad(void); + friend bool GenericSave(int file); + friend class CMemoryCard; + +#ifdef FIX_BUGS + static float GetDefaultTimeStep(void) { return 50.0f / 30.0f; } + static float GetTimeStepFix(void) { return GetTimeStep() / GetDefaultTimeStep(); } + static uint32 GetLogicalFrameCounter(void) { return m_LogicalFrameCounter; } + static uint32 GetLogicalFramesPassed(void) { return m_LogicalFramesPassed; } +#endif +}; diff --git a/src/miami/core/User.cpp b/src/miami/core/User.cpp new file mode 100644 index 00000000..53196d03 --- /dev/null +++ b/src/miami/core/User.cpp @@ -0,0 +1,135 @@ +#include "common.h" + +#include "GameLogic.h" +#include "Hud.h" +#include "PlayerPed.h" +#include "Replay.h" +#include "Text.h" +#include "User.h" +#include "Vehicle.h" +#include "World.h" +#include "Zones.h" + +CPlaceName CUserDisplay::PlaceName; +COnscreenTimer CUserDisplay::OnscnTimer; +CPager CUserDisplay::Pager; +CCurrentVehicle CUserDisplay::CurrentVehicle; + +CPlaceName::CPlaceName() +{ + Init(); +} + +void +CPlaceName::Init() +{ + m_pZone = nil; + m_pZone2 = nil; + m_nAdditionalTimer = 0; +} + +void +CPlaceName::Process() +{ + CVector pos = CWorld::Players[CWorld::PlayerInFocus].GetPos(); + CZone *navigZone = CTheZones::FindSmallestNavigationZoneForPosition(&pos, false, true); + CZone *defaultZone = CTheZones::FindSmallestNavigationZoneForPosition(&pos, true, false); + + if (navigZone == nil) m_pZone = nil; + if (defaultZone == nil) m_pZone2 = nil; + + if (navigZone == m_pZone) { + if (defaultZone == m_pZone2 || m_pZone != nil) { + if (navigZone != nil || defaultZone != nil) { + if (m_nAdditionalTimer != 0) + m_nAdditionalTimer--; + } else { + m_nAdditionalTimer = 0; + m_pZone = nil; + m_pZone2 = nil; + } + } else { + m_pZone2 = defaultZone; + m_nAdditionalTimer = 250; + } + } else { + m_pZone = navigZone; + m_nAdditionalTimer = 250; + } + Display(); +} + +void +CPlaceName::Display() +{ + wchar *text; + if (m_pZone != nil) + text = m_pZone->GetTranslatedName(); + else if (m_pZone2 != nil) + text = m_pZone2->GetTranslatedName(); +#ifdef FIX_BUGS + else + text = nil; +#endif + CHud::SetZoneName(text); +} + +void +CPlaceName::ProcessAfterFrontEndShutDown(void) +{ + CHud::m_pLastZoneName = nil; + CHud::m_ZoneState = 0; + m_nAdditionalTimer = 250; +} + +CCurrentVehicle::CCurrentVehicle() +{ + Init(); +} + +void +CCurrentVehicle::Init() +{ + m_pCurrentVehicle = nil; +} + +void +CCurrentVehicle::Process() +{ + if (CWorld::Players[CWorld::PlayerInFocus].m_pPed->InVehicle()) + m_pCurrentVehicle = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pMyVehicle; + else + m_pCurrentVehicle = nil; + Display(); +} + +void +CCurrentVehicle::Display() +{ + wchar *text = nil; + if (m_pCurrentVehicle != nil && m_pCurrentVehicle != CGameLogic::pShortCutTaxi) + text = TheText.Get(((CVehicleModelInfo*)CModelInfo::GetModelInfo(m_pCurrentVehicle->GetModelIndex()))->m_gameName); + CHud::SetVehicleName(text); +} + +void +CUserDisplay::Init() +{ + PlaceName.Init(); + OnscnTimer.Init(); + Pager.Init(); + CurrentVehicle.Init(); +} + +void +CUserDisplay::Process() +{ +#ifdef FIX_BUGS + if (CReplay::IsPlayingBack()) + return; +#endif + PlaceName.Process(); + OnscnTimer.Process(); + Pager.Process(); + CurrentVehicle.Process(); +} diff --git a/src/miami/core/User.h b/src/miami/core/User.h new file mode 100644 index 00000000..dd53a40a --- /dev/null +++ b/src/miami/core/User.h @@ -0,0 +1,42 @@ +#pragma once + +#include "Pager.h" +#include "OnscreenTimer.h" + +class CZone; +class CVehicle; + +class CPlaceName +{ + CZone *m_pZone; + CZone *m_pZone2; + int16 m_nAdditionalTimer; +public: + CPlaceName(); + void Init(); + void Process(); + void Display(); + void ProcessAfterFrontEndShutDown(); +}; + +class CCurrentVehicle +{ + CVehicle *m_pCurrentVehicle; +public: + CCurrentVehicle(); + void Init(); + void Process(); + void Display(); +}; + +class CUserDisplay +{ +public: + static CPlaceName PlaceName; + static COnscreenTimer OnscnTimer; + static CPager Pager; + static CCurrentVehicle CurrentVehicle; + + static void Init(); + static void Process(); +}; diff --git a/src/miami/core/Wanted.cpp b/src/miami/core/Wanted.cpp new file mode 100644 index 00000000..65bc84d8 --- /dev/null +++ b/src/miami/core/Wanted.cpp @@ -0,0 +1,510 @@ +#include "common.h" + +#include "Pools.h" +#include "ModelIndices.h" +#include "Timer.h" +#include "World.h" +#include "ZoneCull.h" +#include "Darkel.h" +#include "DMAudio.h" +#include "CopPed.h" +#include "Wanted.h" +#include "General.h" +#include "Stats.h" + +int32 CWanted::MaximumWantedLevel = 6; +int32 CWanted::nMaximumWantedLevel = 9600; + +void +CWanted::Initialise() +{ + m_nChaos = 0; + m_nMinChaos = 0; + m_nLastUpdateTime = 0; + m_nLastWantedLevelChange = 0; + m_nLastTimeSuspended = 0; + m_CurrentCops = 0; + m_MaxCops = 0; + m_MaximumLawEnforcerVehicles = 0; + m_RoadblockDensity = 0; + m_bIgnoredByCops = false; + m_bIgnoredByEveryone = false; + m_bSwatRequired = false; + m_bFbiRequired = false; + m_bArmyRequired = false; + m_fCrimeSensitivity = 1.0f; + m_nWantedLevel = 0; + m_nMinWantedLevel = 0; + m_CopsBeatingSuspect = 0; + + for (int i = 0; i < ARRAY_SIZE(m_pCops); i++) + m_pCops[i] = nil; + + ClearQdCrimes(); +} + +bool +CWanted::AreMiamiViceRequired() +{ + return m_nWantedLevel >= 3; +} + +bool +CWanted::AreSwatRequired() +{ + return m_nWantedLevel == 4 || m_bSwatRequired; +} + +bool +CWanted::AreFbiRequired() +{ + return m_nWantedLevel == 5 || m_bFbiRequired; +} + +bool +CWanted::AreArmyRequired() +{ + return m_nWantedLevel == 6 || m_bArmyRequired; +} + +int32 +CWanted::NumOfHelisRequired() +{ + if (m_bIgnoredByCops || m_bIgnoredByEveryone) + return 0; + + switch (m_nWantedLevel) { + case 3: + case 4: + return 1; + case 5: + case 6: + return 1; + default: + return 0; + } +} + +void +CWanted::SetWantedLevel(int32 level) +{ + if (level > MaximumWantedLevel) + level = MaximumWantedLevel; + + ClearQdCrimes(); + switch (level) { + case 0: + m_nChaos = 0; + break; + case 1: + m_nChaos = 70; + break; + case 2: + m_nChaos = 200; + break; + case 3: + m_nChaos = 570; + break; + case 4: + m_nChaos = 1220; + break; + case 5: + m_nChaos = 2420; + break; + case 6: + m_nChaos = 4820; + break; + default: + break; + } + UpdateWantedLevel(); +} + +void +CWanted::SetWantedLevelNoDrop(int32 level) +{ + if (m_nWantedLevel < m_nMinWantedLevel) + SetWantedLevel(m_nMinWantedLevel); + + if (level > m_nWantedLevel) + SetWantedLevel(level); +} + +void +CWanted::CheatWantedLevel(int32 level) +{ + SetWantedLevel(level); + UpdateWantedLevel(); +} + +void +CWanted::SetMaximumWantedLevel(int32 level) +{ + switch(level){ + case 0: + nMaximumWantedLevel = 0; + MaximumWantedLevel = 0; + break; + case 1: + nMaximumWantedLevel = 115; + MaximumWantedLevel = 1; + break; + case 2: + nMaximumWantedLevel = 365; + MaximumWantedLevel = 2; + break; + case 3: + nMaximumWantedLevel = 875; + MaximumWantedLevel = 3; + break; + case 4: + nMaximumWantedLevel = 1800; + MaximumWantedLevel = 4; + break; + case 5: + nMaximumWantedLevel = 3600; + MaximumWantedLevel = 5; + break; + case 6: + nMaximumWantedLevel = 7200; + MaximumWantedLevel = 6; + break; + } +} + +void +CWanted::RegisterCrime(eCrimeType type, const CVector &coors, uint32 id, bool policeDoesntCare) +{ + AddCrimeToQ(type, id, coors, false, policeDoesntCare); +} + +void +CWanted::RegisterCrime_Immediately(eCrimeType type, const CVector &coors, uint32 id, bool policeDoesntCare) +{ +#ifdef FIX_SIGNIFICANT_BUGS + if(!AddCrimeToQ(type, id, coors, true, policeDoesntCare)) +#else + if(!AddCrimeToQ(type, id, coors, false, policeDoesntCare)) +#endif + ReportCrimeNow(type, coors, policeDoesntCare); +} + +void +CWanted::ClearQdCrimes() +{ + for (int i = 0; i < 16; i++) + m_aCrimes[i].m_nType = CRIME_NONE; +} + +// returns whether the crime had been reported already +bool +CWanted::AddCrimeToQ(eCrimeType type, int32 id, const CVector &coors, bool reported, bool policeDoesntCare) +{ + int i; + + for(i = 0; i < 16; i++) + if(m_aCrimes[i].m_nType == type && m_aCrimes[i].m_nId == id){ + if(m_aCrimes[i].m_bReported) + return true; + if(reported) + m_aCrimes[i].m_bReported = reported; + return false; + } + + for(i = 0; i < 16; i++) + if(m_aCrimes[i].m_nType == CRIME_NONE) + break; + if(i < 16){ + m_aCrimes[i].m_nType = type; + m_aCrimes[i].m_nId = id; + m_aCrimes[i].m_vecPosn = coors; + m_aCrimes[i].m_nTime = CTimer::GetTimeInMilliseconds(); + m_aCrimes[i].m_bReported = reported; + m_aCrimes[i].m_bPoliceDoesntCare = policeDoesntCare; + } + return false; +} + +void +CWanted::ReportCrimeNow(eCrimeType type, const CVector &coors, bool policeDoesntCare) +{ + float sensitivity, chaos; + int wantedLevelDrop; + + if(CDarkel::FrenzyOnGoing()) + sensitivity = m_fCrimeSensitivity*0.3f; + else + sensitivity = m_fCrimeSensitivity; + + wantedLevelDrop = Min(CCullZones::GetWantedLevelDrop(), 100); + + chaos = (1.0f - wantedLevelDrop/100.0f) * sensitivity; + if (policeDoesntCare) + chaos *= 0.333f; + switch(type){ + case CRIME_POSSESSION_GUN: + break; + case CRIME_HIT_PED: + m_nChaos += 5.0f*chaos; + break; + case CRIME_HIT_COP: + m_nChaos += 45.0f*chaos; + break; + case CRIME_SHOOT_PED: + m_nChaos += 30.0f*chaos; + break; + case CRIME_SHOOT_COP: + m_nChaos += 80.0f*chaos; + break; + case CRIME_STEAL_CAR: + m_nChaos += 15.0f*chaos; + break; + case CRIME_RUN_REDLIGHT: + m_nChaos += 10.0f*chaos; + break; + case CRIME_RECKLESS_DRIVING: + m_nChaos += 5.0f*chaos; + break; + case CRIME_SPEEDING: + m_nChaos += 5.0f*chaos; + break; + case CRIME_RUNOVER_PED: + m_nChaos += 18.0f*chaos; + break; + case CRIME_RUNOVER_COP: + m_nChaos += 80.0f*chaos; + break; + case CRIME_SHOOT_HELI: + m_nChaos += 400.0f*chaos; + break; + case CRIME_PED_BURNED: + m_nChaos += 20.0f*chaos; + break; + case CRIME_COP_BURNED: + m_nChaos += 80.0f*chaos; + break; + case CRIME_VEHICLE_BURNED: + m_nChaos += 20.0f*chaos; + break; + case CRIME_DESTROYED_CESSNA: + m_nChaos += 500.0f*chaos; + break; + case CRIME_EXPLOSION: + m_nChaos += 25.0f * chaos; + break; + case CRIME_HIT_PED_NASTYWEAPON: + m_nChaos += 35.0f * chaos; + break; + case CRIME_HIT_COP_NASTYWEAPON: + m_nChaos += 100.0f * chaos; + break; + default: + // Error("Undefined crime type, RegisterCrime, Crime.cpp"); // different file for some reason + Error("Undefined crime type, RegisterCrime, Wanted.cpp"); + } + m_nChaos = Max(m_nChaos, m_nMinChaos); + DMAudio.ReportCrime(type, coors); + UpdateWantedLevel(); +} + +void +CWanted::UpdateWantedLevel() +{ + int32 CurrWantedLevel = m_nWantedLevel; + + if (m_nChaos > nMaximumWantedLevel) + m_nChaos = nMaximumWantedLevel; + + if (m_nChaos >= 0 && m_nChaos < 50) { + if (m_nWantedLevel == 1) + ++CStats::WantedStarsEvaded; + m_nWantedLevel = 0; + m_MaximumLawEnforcerVehicles = 0; + m_MaxCops = 0; + m_RoadblockDensity = 0; + } else if (m_nChaos >= 50 && m_nChaos < 180) { + CStats::WantedStarsAttained += 1 - m_nWantedLevel; + m_nWantedLevel = 1; + m_MaximumLawEnforcerVehicles = 1; + m_MaxCops = 1; + m_RoadblockDensity = 0; + } else if (m_nChaos >= 180 && m_nChaos < 550) { + CStats::WantedStarsAttained += 2 - m_nWantedLevel; + m_nWantedLevel = 2; + m_MaximumLawEnforcerVehicles = 2; + m_MaxCops = 3; + m_RoadblockDensity = 0; + } else if (m_nChaos >= 550 && m_nChaos < 1200) { + CStats::WantedStarsAttained += 3 - m_nWantedLevel; + m_nWantedLevel = 3; + m_MaximumLawEnforcerVehicles = 2; + m_MaxCops = 4; + m_RoadblockDensity = 12; + } else if (m_nChaos >= 1200 && m_nChaos < 2400) { + CStats::WantedStarsAttained += 4 - m_nWantedLevel; + m_nWantedLevel = 4; + m_MaximumLawEnforcerVehicles = 2; + m_MaxCops = 6; + m_RoadblockDensity = 18; + } else if (m_nChaos >= 2400 && m_nChaos < 4800) { + CStats::WantedStarsAttained += 5 - m_nWantedLevel; + m_nWantedLevel = 5; + m_MaximumLawEnforcerVehicles = 3; + m_MaxCops = 8; + m_RoadblockDensity = 24; + } else if (m_nChaos >= 4800) { + CStats::WantedStarsAttained += 6 - m_nWantedLevel; + m_nWantedLevel = 6; + m_MaximumLawEnforcerVehicles = 3; + m_MaxCops = 10; + m_RoadblockDensity = 30; + } + + if (CurrWantedLevel != m_nWantedLevel) + m_nLastWantedLevelChange = CTimer::GetTimeInMilliseconds(); +} + +int32 +CWanted::WorkOutPolicePresence(CVector posn, float radius) +{ + int i; + CPed *ped; + CVehicle *vehicle; + int numPolice = 0; + + i = CPools::GetPedPool()->GetSize(); + while(--i >= 0){ + ped = CPools::GetPedPool()->GetSlot(i); + if(ped && + IsPolicePedModel(ped->GetModelIndex()) && + (posn - ped->GetPosition()).Magnitude() < radius) + numPolice++; + } + + i = CPools::GetVehiclePool()->GetSize(); + while(--i >= 0){ + vehicle = CPools::GetVehiclePool()->GetSlot(i); + if(vehicle && + vehicle->bIsLawEnforcer && + IsPoliceVehicleModel(vehicle->GetModelIndex()) && + vehicle != FindPlayerVehicle() && + vehicle->GetStatus() != STATUS_ABANDONED && vehicle->GetStatus() != STATUS_WRECKED && + (posn - vehicle->GetPosition()).Magnitude() < radius) + numPolice++; + } + + return numPolice; +} + +void +CWanted::Update(void) +{ + if (CTimer::GetTimeInMilliseconds() > m_nLastTimeSuspended + 20000) { + m_nMinChaos = 0; + m_nMinWantedLevel = 0; + } + if (CTimer::GetTimeInMilliseconds() - m_nLastUpdateTime > 1000) { + if (m_nWantedLevel > 1) { + m_nLastUpdateTime = CTimer::GetTimeInMilliseconds(); + } else { + float radius = 18.0f; + CVector playerPos = FindPlayerCoors(); + if (WorkOutPolicePresence(playerPos, radius) == 0) { + m_nLastUpdateTime = CTimer::GetTimeInMilliseconds(); + m_nChaos = Max(0, m_nChaos - 1); + UpdateWantedLevel(); + } + } + UpdateCrimesQ(); + bool orderMessedUp = false; + int currCopNum = 0; + bool foundEmptySlot = false; + for (int i = 0; i < ARRAY_SIZE(m_pCops); i++) { + if (m_pCops[i]) { + ++currCopNum; + if (foundEmptySlot) + orderMessedUp = true; + } else { + foundEmptySlot = true; + } + } + if (currCopNum != m_CurrentCops) { + printf("CopPursuit total messed up: re-setting\n"); + m_CurrentCops = currCopNum; + } + if (orderMessedUp) { + printf("CopPursuit pointer list messed up: re-sorting\n"); + bool fixed = true; + for (int i = 0; i < ARRAY_SIZE(m_pCops); i++) { + if (!m_pCops[i]) { + for (int j = i; j < ARRAY_SIZE(m_pCops); j++) { + if (m_pCops[j]) { + m_pCops[i] = m_pCops[j]; + m_pCops[j] = nil; + fixed = false; + break; + } + } + if (fixed) + break; + } + } + } + } +} + +void +CWanted::ResetPolicePursuit(void) +{ + for(int i = 0; i < ARRAY_SIZE(m_pCops); i++) { + CCopPed *cop = m_pCops[i]; + if (!cop) + continue; + + cop->m_bIsInPursuit = false; + cop->m_objective = OBJECTIVE_NONE; + cop->m_prevObjective = OBJECTIVE_NONE; + cop->m_nLastPedState = PED_NONE; + if (!cop->DyingOrDead()) { + cop->SetWanderPath(CGeneral::GetRandomNumberInRange(0.0f, 8.0f)); + } + m_pCops[i] = nil; + } + m_CurrentCops = 0; +} + +void +CWanted::Reset(void) +{ + ResetPolicePursuit(); + Initialise(); +} + +void +CWanted::UpdateCrimesQ(void) +{ + for(int i = 0; i < ARRAY_SIZE(m_aCrimes); i++) { + + CCrimeBeingQd &crime = m_aCrimes[i]; + if (crime.m_nType != CRIME_NONE) { + if (CTimer::GetTimeInMilliseconds() > crime.m_nTime + 500 && !crime.m_bReported) { + ReportCrimeNow(crime.m_nType, crime.m_vecPosn, crime.m_bPoliceDoesntCare); + crime.m_bReported = true; + } + if (CTimer::GetTimeInMilliseconds() > crime.m_nTime + 10000) + crime.m_nType = CRIME_NONE; + } + } +} + +void +CWanted::Suspend(void) +{ + CStats::WantedStarsEvaded += m_nWantedLevel; + m_nMinChaos = m_nChaos; + m_nMinWantedLevel = m_nWantedLevel; + m_nLastTimeSuspended = CTimer::GetTimeInMilliseconds(); + m_nChaos = 0; + m_nWantedLevel = 0; + ResetPolicePursuit(); +} diff --git a/src/miami/core/Wanted.h b/src/miami/core/Wanted.h new file mode 100644 index 00000000..f2da23e3 --- /dev/null +++ b/src/miami/core/Wanted.h @@ -0,0 +1,65 @@ +#pragma once + +#include "Crime.h" + +class CEntity; +class CCopPed; + +class CWanted +{ +public: + int32 m_nChaos; + int32 m_nMinChaos; + int32 m_nLastUpdateTime; + uint32 m_nLastWantedLevelChange; + uint32 m_nLastTimeSuspended; + float m_fCrimeSensitivity; + uint8 m_CurrentCops; + uint8 m_MaxCops; + uint8 m_MaximumLawEnforcerVehicles; + uint8 m_CopsBeatingSuspect; + int16 m_RoadblockDensity; + uint8 m_bIgnoredByCops : 1; + uint8 m_bIgnoredByEveryone : 1; + uint8 m_bSwatRequired : 1; + uint8 m_bFbiRequired : 1; + uint8 m_bArmyRequired : 1; + int32 m_nWantedLevel; + int32 m_nMinWantedLevel; + CCrimeBeingQd m_aCrimes[16]; + CCopPed *m_pCops[10]; + + static int32 MaximumWantedLevel; + static int32 nMaximumWantedLevel; + +public: + void Initialise(); + bool AreMiamiViceRequired(); + bool AreSwatRequired(); + bool AreFbiRequired(); + bool AreArmyRequired(); + int32 NumOfHelisRequired(); + void SetWantedLevel(int32); + void SetWantedLevelNoDrop(int32 level); + int32 GetWantedLevel() { return m_nWantedLevel; } + void CheatWantedLevel(int32 level); + void RegisterCrime(eCrimeType type, const CVector &coors, uint32 id, bool policeDoesntCare); + void RegisterCrime_Immediately(eCrimeType type, const CVector &coors, uint32 id, bool policeDoesntCare); + void ClearQdCrimes(); + bool AddCrimeToQ(eCrimeType type, int32 id, const CVector &pos, bool reported, bool policeDoesntCare); + void ReportCrimeNow(eCrimeType type, const CVector &coors, bool policeDoesntCare); + void UpdateWantedLevel(); + void Reset(); + void ResetPolicePursuit(); + void UpdateCrimesQ(); + void Update(); + + void Suspend(); + + bool IsIgnored(void) { return m_bIgnoredByCops || m_bIgnoredByEveryone; } + + static int32 WorkOutPolicePresence(CVector posn, float radius); + static void SetMaximumWantedLevel(int32 level); +}; + +VALIDATE_SIZE(CWanted, 0x204); diff --git a/src/miami/core/World.cpp b/src/miami/core/World.cpp new file mode 100644 index 00000000..549c2cc0 --- /dev/null +++ b/src/miami/core/World.cpp @@ -0,0 +1,2312 @@ +#include "common.h" +#include "Camera.h" +#include "CarCtrl.h" +#include "CopPed.h" +#include "CutsceneMgr.h" +#include "DMAudio.h" +#include "EventList.h" +#include "Explosion.h" +#include "Fire.h" +#include "Garages.h" +#include "Glass.h" +#include "Messages.h" +#include "ModelIndices.h" +#include "ParticleObject.h" +#include "Pickups.h" +#include "Population.h" +#include "ProjectileInfo.h" +#include "Record.h" +#include "References.h" +#include "Replay.h" +#include "RpAnimBlend.h" +#include "Shadows.h" +#include "TempColModels.h" +#include "WaterLevel.h" +#include "World.h" + +#define OBJECT_REPOSITION_OFFSET_Z 2.0f + +CColPoint gaTempSphereColPoints[MAX_COLLISION_POINTS]; + +CPtrList CWorld::ms_bigBuildingsList[NUM_LEVELS]; +CPtrList CWorld::ms_listMovingEntityPtrs; +CSector CWorld::ms_aSectors[NUMSECTORS_Y][NUMSECTORS_X]; +uint16 CWorld::ms_nCurrentScanCode; + +uint8 CWorld::PlayerInFocus; +CPlayerInfo CWorld::Players[NUMPLAYERS]; +bool CWorld::bNoMoreCollisionTorque; +CEntity *CWorld::pIgnoreEntity; +bool CWorld::bIncludeDeadPeds; +bool CWorld::bSecondShift; +bool CWorld::bForceProcessControl; +bool CWorld::bProcessCutsceneOnly; + +bool CWorld::bDoingCarCollisions; +bool CWorld::bIncludeCarTyres; +bool CWorld::bIncludeBikers; + +CColPoint CWorld::m_aTempColPts[MAX_COLLISION_POINTS]; + +void +CWorld::Initialise() +{ + pIgnoreEntity = nil; + bDoingCarCollisions = false; + bSecondShift = false; + bNoMoreCollisionTorque = false; + bProcessCutsceneOnly = false; + bIncludeDeadPeds = false; + bForceProcessControl = false; + bIncludeCarTyres = false; + bIncludeBikers = false; +} + +void +CWorld::Add(CEntity *ent) +{ + if(ent->IsVehicle() || ent->IsPed()) DMAudio.SetEntityStatus(((CPhysical *)ent)->m_audioEntityId, TRUE); + + if(ent->bIsBIGBuilding) + ms_bigBuildingsList[ent->m_level].InsertItem(ent); + else + ent->Add(); + + if(ent->IsBuilding() || ent->IsDummy()) return; + + if(!ent->GetIsStatic()) ((CPhysical *)ent)->AddToMovingList(); +} + +void +CWorld::Remove(CEntity *ent) +{ + if(ent->IsVehicle() || ent->IsPed()) DMAudio.SetEntityStatus(((CPhysical *)ent)->m_audioEntityId, FALSE); + + if(ent->bIsBIGBuilding) + ms_bigBuildingsList[ent->m_level].RemoveItem(ent); + else + ent->Remove(); + + if(ent->IsBuilding() || ent->IsDummy()) return; + + if(!ent->GetIsStatic()) ((CPhysical *)ent)->RemoveFromMovingList(); +} + +void +CWorld::ClearScanCodes(void) +{ + CPtrNode *node; + for(int i = 0; i < NUMSECTORS_Y; i++) + for(int j = 0; j < NUMSECTORS_X; j++) { + CSector *s = &ms_aSectors[i][j]; + for(node = s->m_lists[ENTITYLIST_BUILDINGS].first; node; node = node->next) + ((CEntity *)node->item)->m_scanCode = 0; + for(node = s->m_lists[ENTITYLIST_VEHICLES].first; node; node = node->next) + ((CEntity *)node->item)->m_scanCode = 0; + for(node = s->m_lists[ENTITYLIST_PEDS].first; node; node = node->next) + ((CEntity *)node->item)->m_scanCode = 0; + for(node = s->m_lists[ENTITYLIST_OBJECTS].first; node; node = node->next) + ((CEntity *)node->item)->m_scanCode = 0; + for(node = s->m_lists[ENTITYLIST_DUMMIES].first; node; node = node->next) + ((CEntity *)node->item)->m_scanCode = 0; + } +} + +void +CWorld::ClearExcitingStuffFromArea(const CVector &pos, float radius, bool bRemoveProjectilesAndTidyUpShadows) +{ + CPedPool *pedPool = CPools::GetPedPool(); + for(int32 i = 0; i < pedPool->GetSize(); i++) { + CPed *pPed = pedPool->GetSlot(i); + if(pPed && !pPed->IsPlayer() && pPed->CanBeDeleted() && + CVector2D(pPed->GetPosition() - pos).MagnitudeSqr() < SQR(radius)) { + CPopulation::RemovePed(pPed); + } + } + CVehiclePool *VehiclePool = CPools::GetVehiclePool(); + for(int32 i = 0; i < VehiclePool->GetSize(); i++) { + CVehicle *pVehicle = VehiclePool->GetSlot(i); + if(pVehicle && CVector2D(pVehicle->GetPosition() - pos).MagnitudeSqr() < SQR(radius) && + !pVehicle->bIsLocked && pVehicle->CanBeDeleted()) { + if(pVehicle->pDriver) { + CPopulation::RemovePed(pVehicle->pDriver); + pVehicle->pDriver = nil; + } + for(int32 j = 0; j < pVehicle->m_nNumMaxPassengers; ++j) { + if(pVehicle->pPassengers[j]) { + CPopulation::RemovePed(pVehicle->pPassengers[j]); + pVehicle->pPassengers[j] = nil; + --pVehicle->m_nNumPassengers; + } + } + CCarCtrl::RemoveFromInterestingVehicleList(pVehicle); + Remove(pVehicle); + delete pVehicle; + } + } + CObject::DeleteAllTempObjectsInArea(pos, radius); + gFireManager.ExtinguishPoint(pos, radius); + ExtinguishAllCarFiresInArea(pos, radius); + CExplosion::RemoveAllExplosionsInArea(pos, radius); + if(bRemoveProjectilesAndTidyUpShadows) { + CProjectileInfo::RemoveAllProjectiles(); + CShadows::TidyUpShadows(); + } + CPickups::RemoveUnnecessaryPickups(pos, radius); +} + +bool +CWorld::CameraToIgnoreThisObject(CEntity *ent) +{ + if(CGarages::IsModelIndexADoor(ent->GetModelIndex())) return false; + return ((CObject *)ent)->m_bCameraToAvoidThisObject != 1; +} + +bool +CWorld::ProcessLineOfSight(const CVector &point1, const CVector &point2, CColPoint &point, CEntity *&entity, + bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, + bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects, bool ignoreShootThrough) +{ + int x, xstart, xend; + int y, ystart, yend; + int y1, y2; + float dist; + + AdvanceCurrentScanCode(); + + entity = nil; + dist = 1.0f; + + xstart = GetSectorIndexX(point1.x); + ystart = GetSectorIndexY(point1.y); + xend = GetSectorIndexX(point2.x); + yend = GetSectorIndexY(point2.y); + +#define LOSARGS \ + CColLine(point1, point2), point, dist, entity, checkBuildings, checkVehicles, checkPeds, checkObjects, \ + checkDummies, ignoreSeeThrough, ignoreSomeObjects, ignoreShootThrough + + if(xstart == xend && ystart == yend) { + // Only one sector + return ProcessLineOfSightSector(*GetSector(xstart, ystart), LOSARGS); + } else if(xstart == xend) { + // Only step in y + if(ystart < yend) + for(y = ystart; y <= yend; y++) ProcessLineOfSightSector(*GetSector(xstart, y), LOSARGS); + else + for(y = ystart; y >= yend; y--) ProcessLineOfSightSector(*GetSector(xstart, y), LOSARGS); + return dist < 1.0f; + } else if(ystart == yend) { + // Only step in x + if(xstart < xend) + for(x = xstart; x <= xend; x++) ProcessLineOfSightSector(*GetSector(x, ystart), LOSARGS); + else + for(x = xstart; x >= xend; x--) ProcessLineOfSightSector(*GetSector(x, ystart), LOSARGS); + return dist < 1.0f; + } else { + if(point1.x < point2.x) { + // Step from left to right + float m = (point2.y - point1.y) / (point2.x - point1.x); + + y1 = ystart; + y2 = GetSectorIndexY((GetWorldX(xstart + 1) - point1.x) * m + point1.y); + if(y1 < y2) + for(y = y1; y <= y2; y++) ProcessLineOfSightSector(*GetSector(xstart, y), LOSARGS); + else + for(y = y1; y >= y2; y--) ProcessLineOfSightSector(*GetSector(xstart, y), LOSARGS); + + for(x = xstart + 1; x < xend; x++) { + y1 = y2; + y2 = GetSectorIndexY((GetWorldX(x + 1) - point1.x) * m + point1.y); + if(y1 < y2) + for(y = y1; y <= y2; y++) ProcessLineOfSightSector(*GetSector(x, y), LOSARGS); + else + for(y = y1; y >= y2; y--) ProcessLineOfSightSector(*GetSector(x, y), LOSARGS); + } + + y1 = y2; + y2 = yend; + if(y1 < y2) + for(y = y1; y <= y2; y++) ProcessLineOfSightSector(*GetSector(xend, y), LOSARGS); + else + for(y = y1; y >= y2; y--) ProcessLineOfSightSector(*GetSector(xend, y), LOSARGS); + } else { + // Step from right to left + float m = (point2.y - point1.y) / (point2.x - point1.x); + + y1 = ystart; + y2 = GetSectorIndexY((GetWorldX(xstart) - point1.x) * m + point1.y); + if(y1 < y2) + for(y = y1; y <= y2; y++) ProcessLineOfSightSector(*GetSector(xstart, y), LOSARGS); + else + for(y = y1; y >= y2; y--) ProcessLineOfSightSector(*GetSector(xstart, y), LOSARGS); + + for(x = xstart - 1; x > xend; x--) { + y1 = y2; + y2 = GetSectorIndexY((GetWorldX(x) - point1.x) * m + point1.y); + if(y1 < y2) + for(y = y1; y <= y2; y++) ProcessLineOfSightSector(*GetSector(x, y), LOSARGS); + else + for(y = y1; y >= y2; y--) ProcessLineOfSightSector(*GetSector(x, y), LOSARGS); + } + + y1 = y2; + y2 = yend; + if(y1 < y2) + for(y = y1; y <= y2; y++) ProcessLineOfSightSector(*GetSector(xend, y), LOSARGS); + else + for(y = y1; y >= y2; y--) ProcessLineOfSightSector(*GetSector(xend, y), LOSARGS); + } + return dist < 1.0f; + } + +#undef LOSARGS +} + +bool +CWorld::ProcessLineOfSightSector(CSector §or, const CColLine &line, CColPoint &point, float &dist, CEntity *&entity, + bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, + bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects, bool ignoreShootThrough) +{ + float mindist = dist; + bool deadPeds = !!bIncludeDeadPeds; + bool bikers = !!bIncludeBikers; + bIncludeDeadPeds = false; + bIncludeBikers = false; + + if(checkBuildings) { + ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_BUILDINGS], line, point, mindist, entity, + ignoreSeeThrough, false, ignoreShootThrough); + ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_BUILDINGS_OVERLAP], line, point, mindist, entity, + ignoreSeeThrough, false, ignoreShootThrough); + } + + if(checkVehicles) { + ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_VEHICLES], line, point, mindist, entity, + ignoreSeeThrough, false, ignoreShootThrough); + ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_VEHICLES_OVERLAP], line, point, mindist, entity, + ignoreSeeThrough, false, ignoreShootThrough); + } + + if(checkPeds) { + if(deadPeds) bIncludeDeadPeds = true; + if(bikers) bIncludeBikers = true; + ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_PEDS], line, point, mindist, entity, + ignoreSeeThrough, false, ignoreShootThrough); + ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_PEDS_OVERLAP], line, point, mindist, entity, + ignoreSeeThrough, false, ignoreShootThrough); + bIncludeDeadPeds = false; + bIncludeBikers = false; + } + + if(checkObjects) { + ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_OBJECTS], line, point, mindist, entity, + ignoreSeeThrough, ignoreSomeObjects, ignoreShootThrough); + ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_OBJECTS_OVERLAP], line, point, mindist, entity, + ignoreSeeThrough, ignoreSomeObjects, ignoreShootThrough); + } + + if(checkDummies) { + ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_DUMMIES], line, point, mindist, entity, + ignoreSeeThrough, false, ignoreShootThrough); + ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_DUMMIES_OVERLAP], line, point, mindist, entity, + ignoreSeeThrough, false, ignoreShootThrough); + } + + bIncludeDeadPeds = deadPeds; + bIncludeBikers = bikers; + + if(mindist < dist) { + dist = mindist; + return true; + } else + return false; +} + +bool +CWorld::ProcessLineOfSightSectorList(CPtrList &list, const CColLine &line, CColPoint &point, float &dist, + CEntity *&entity, bool ignoreSeeThrough, bool ignoreSomeObjects, bool ignoreShootThrough) +{ + bool deadPeds = false; + bool bikers = false; + bool carTyres = false; + float mindist = dist; + CPtrNode *node; + CEntity *e; + CColModel *colmodel; + CColModel tyreCol; + CColSphere tyreSpheres[6]; + CColPoint tyreColPoint; + float tyreDist; + + if(bIncludeCarTyres && list.first && ((CEntity*)list.first->item)->IsVehicle()){ + carTyres = true; + tyreCol.numTriangles = 0; + tyreCol.numBoxes = 0; + tyreCol.numLines = 0; + tyreCol.spheres = tyreSpheres; + tyreCol.numSpheres = ARRAY_SIZE(tyreSpheres); + } + if(list.first && bIncludeDeadPeds && ((CEntity *)list.first->item)->IsPed()) deadPeds = true; + if(list.first && bIncludeBikers && ((CEntity *)list.first->item)->IsPed()) bikers = true; + + for(node = list.first; node; node = node->next) { + e = (CEntity *)node->item; + if(e->m_scanCode != GetCurrentScanCode() && e != pIgnoreEntity && (e->bUsesCollision || deadPeds || bikers) && + !(ignoreSomeObjects && CameraToIgnoreThisObject(e))) { + colmodel = nil; + tyreDist = mindist; + e->m_scanCode = GetCurrentScanCode(); + + if(e->IsPed()) { + if(e->bUsesCollision || deadPeds && ((CPed *)e)->m_nPedState == PED_DEAD || bikers && ((CPed*)e)->InVehicle() && (((CPed*)e)->m_pMyVehicle->IsBike() || ((CPed*)e)->m_pMyVehicle->IsBoat())) { + colmodel = ((CPedModelInfo *)CModelInfo::GetModelInfo(e->GetModelIndex()))->AnimatePedColModelSkinned(e->GetClump()); + } else + colmodel = nil; + + } else if(e->bUsesCollision) + colmodel = CModelInfo::GetColModel(e->GetModelIndex()); + + if(colmodel && CCollision::ProcessLineOfSight(line, e->GetMatrix(), *colmodel, point, mindist, + ignoreSeeThrough, ignoreShootThrough)) + entity = e; + if(carTyres && ((CVehicle*)e)->SetUpWheelColModel(&tyreCol) && CCollision::ProcessLineOfSight(line, e->GetMatrix(), tyreCol, tyreColPoint, tyreDist, false, ignoreShootThrough)){ + float dp1 = DotProduct(line.p1 - line.p0, e->GetRight()); + float dp2 = DotProduct(point.point - e->GetPosition(), e->GetRight()); + if(tyreDist < mindist || dp1 < -0.85f && dp2 > 0.0f || dp1 > 0.85f && dp2 < 0.0f){ + mindist = tyreDist; + point = tyreColPoint; + entity = e; + } + } + } + } + tyreCol.spheres = nil; + + if(mindist < dist) { + dist = mindist; + return true; + } else + return false; +} + +bool +CWorld::ProcessVerticalLine(const CVector &point1, float z2, CColPoint &point, CEntity *&entity, bool checkBuildings, + bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, + bool ignoreSeeThrough, CStoredCollPoly *poly) +{ + AdvanceCurrentScanCode(); + CVector point2(point1.x, point1.y, z2); + int secX = GetSectorIndexX(point1.x); + int secY = GetSectorIndexY(point1.y); + secX = Clamp(secX, 0, NUMSECTORS_X-1); + secY = Clamp(secY, 0, NUMSECTORS_Y-1); + return ProcessVerticalLineSector(*GetSector(secX, secY), + CColLine(point1, point2), point, entity, checkBuildings, checkVehicles, + checkPeds, checkObjects, checkDummies, ignoreSeeThrough, poly); +} + +bool +CWorld::ProcessVerticalLineSector(CSector §or, const CColLine &line, CColPoint &point, CEntity *&entity, + bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, + bool checkDummies, bool ignoreSeeThrough, CStoredCollPoly *poly) +{ + float mindist = 1.0f; + + if(checkBuildings) { + ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_BUILDINGS], line, point, mindist, entity, + ignoreSeeThrough, poly); + ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_BUILDINGS_OVERLAP], line, point, mindist, + entity, ignoreSeeThrough, poly); + } + + if(checkVehicles) { + ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_VEHICLES], line, point, mindist, entity, + ignoreSeeThrough, poly); + ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_VEHICLES_OVERLAP], line, point, mindist, entity, + ignoreSeeThrough, poly); + } + + if(checkPeds) { + ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_PEDS], line, point, mindist, entity, + ignoreSeeThrough, poly); + ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_PEDS_OVERLAP], line, point, mindist, entity, + ignoreSeeThrough, poly); + } + + if(checkObjects) { + ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_OBJECTS], line, point, mindist, entity, + ignoreSeeThrough, poly); + ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_OBJECTS_OVERLAP], line, point, mindist, entity, + ignoreSeeThrough, poly); + } + + if(checkDummies) { + ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_DUMMIES], line, point, mindist, entity, + ignoreSeeThrough, poly); + ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_DUMMIES_OVERLAP], line, point, mindist, entity, + ignoreSeeThrough, poly); + } + + return mindist < 1.0f; +} + +bool +CWorld::ProcessVerticalLineSectorList(CPtrList &list, const CColLine &line, CColPoint &point, float &dist, + CEntity *&entity, bool ignoreSeeThrough, CStoredCollPoly *poly) +{ + float mindist = dist; + CPtrNode *node; + CEntity *e; + CColModel *colmodel; + + for(node = list.first; node; node = node->next) { + e = (CEntity *)node->item; + if(e->m_scanCode != GetCurrentScanCode() && e->bUsesCollision) { + e->m_scanCode = GetCurrentScanCode(); + + colmodel = CModelInfo::GetColModel(e->GetModelIndex()); + if(CCollision::ProcessVerticalLine(line, e->GetMatrix(), *colmodel, point, mindist, + ignoreSeeThrough, false, poly)) + entity = e; + } + } + + if(mindist < dist) { + dist = mindist; + return true; + } else + return false; +} + +bool +CWorld::GetIsLineOfSightClear(const CVector &point1, const CVector &point2, bool checkBuildings, bool checkVehicles, + bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, + bool ignoreSomeObjects) +{ + int x, xstart, xend; + int y, ystart, yend; + int y1, y2; + + AdvanceCurrentScanCode(); + + xstart = GetSectorIndexX(point1.x); + ystart = GetSectorIndexY(point1.y); + xend = GetSectorIndexX(point2.x); + yend = GetSectorIndexY(point2.y); + +#define LOSARGS \ + CColLine(point1, point2), checkBuildings, checkVehicles, checkPeds, checkObjects, checkDummies, \ + ignoreSeeThrough, ignoreSomeObjects + + if(xstart == xend && ystart == yend) { + // Only one sector + return GetIsLineOfSightSectorClear(*GetSector(xstart, ystart), LOSARGS); + } else if(xstart == xend) { + // Only step in y + if(ystart < yend) { + for(y = ystart; y <= yend; y++) + if(!GetIsLineOfSightSectorClear(*GetSector(xstart, y), LOSARGS)) return false; + } else { + for(y = ystart; y >= yend; y--) + if(!GetIsLineOfSightSectorClear(*GetSector(xstart, y), LOSARGS)) return false; + } + } else if(ystart == yend) { + // Only step in x + if(xstart < xend) { + for(x = xstart; x <= xend; x++) + if(!GetIsLineOfSightSectorClear(*GetSector(x, ystart), LOSARGS)) return false; + } else { + for(x = xstart; x >= xend; x--) + if(!GetIsLineOfSightSectorClear(*GetSector(x, ystart), LOSARGS)) return false; + } + } else { + if(point1.x < point2.x) { + // Step from left to right + float m = (point2.y - point1.y) / (point2.x - point1.x); + + y1 = ystart; + y2 = GetSectorIndexY((GetWorldX(xstart + 1) - point1.x) * m + point1.y); + if(y1 < y2) { + for(y = y1; y <= y2; y++) + if(!GetIsLineOfSightSectorClear(*GetSector(xstart, y), LOSARGS)) return false; + } else { + for(y = y1; y >= y2; y--) + if(!GetIsLineOfSightSectorClear(*GetSector(xstart, y), LOSARGS)) return false; + } + + for(x = xstart + 1; x < xend; x++) { + y1 = y2; + y2 = GetSectorIndexY((GetWorldX(x + 1) - point1.x) * m + point1.y); + if(y1 < y2) { + for(y = y1; y <= y2; y++) + if(!GetIsLineOfSightSectorClear(*GetSector(x, y), LOSARGS)) + return false; + } else { + for(y = y1; y >= y2; y--) + if(!GetIsLineOfSightSectorClear(*GetSector(x, y), LOSARGS)) + return false; + } + } + + y1 = y2; + y2 = yend; + if(y1 < y2) { + for(y = y1; y <= y2; y++) + if(!GetIsLineOfSightSectorClear(*GetSector(xend, y), LOSARGS)) return false; + } else { + for(y = y1; y >= y2; y--) + if(!GetIsLineOfSightSectorClear(*GetSector(xend, y), LOSARGS)) return false; + } + } else { + // Step from right to left + float m = (point2.y - point1.y) / (point2.x - point1.x); + + y1 = ystart; + y2 = GetSectorIndexY((GetWorldX(xstart) - point1.x) * m + point1.y); + if(y1 < y2) { + for(y = y1; y <= y2; y++) + if(!GetIsLineOfSightSectorClear(*GetSector(xstart, y), LOSARGS)) return false; + } else { + for(y = y1; y >= y2; y--) + if(!GetIsLineOfSightSectorClear(*GetSector(xstart, y), LOSARGS)) return false; + } + + for(x = xstart - 1; x > xend; x--) { + y1 = y2; + y2 = GetSectorIndexY((GetWorldX(x) - point1.x) * m + point1.y); + if(y1 < y2) { + for(y = y1; y <= y2; y++) + if(!GetIsLineOfSightSectorClear(*GetSector(x, y), LOSARGS)) + return false; + } else { + for(y = y1; y >= y2; y--) + if(!GetIsLineOfSightSectorClear(*GetSector(x, y), LOSARGS)) + return false; + } + } + + y1 = y2; + y2 = yend; + if(y1 < y2) { + for(y = y1; y <= y2; y++) + if(!GetIsLineOfSightSectorClear(*GetSector(xend, y), LOSARGS)) return false; + } else { + for(y = y1; y >= y2; y--) + if(!GetIsLineOfSightSectorClear(*GetSector(xend, y), LOSARGS)) return false; + } + } + } + + return true; + +#undef LOSARGS +} + +bool +CWorld::GetIsLineOfSightSectorClear(CSector §or, const CColLine &line, bool checkBuildings, bool checkVehicles, + bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, + bool ignoreSomeObjects) +{ + if(checkBuildings) { + if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_BUILDINGS], line, ignoreSeeThrough)) + return false; + if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_BUILDINGS_OVERLAP], line, + ignoreSeeThrough)) + return false; + } + + if(checkVehicles) { + if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_VEHICLES], line, ignoreSeeThrough)) + return false; + if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_VEHICLES_OVERLAP], line, + ignoreSeeThrough)) + return false; + } + + if(checkPeds) { + if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_PEDS], line, ignoreSeeThrough)) + return false; + if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_PEDS_OVERLAP], line, ignoreSeeThrough)) + return false; + } + + if(checkObjects) { + if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_OBJECTS], line, ignoreSeeThrough, + ignoreSomeObjects)) + return false; + if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_OBJECTS_OVERLAP], line, ignoreSeeThrough, + ignoreSomeObjects)) + return false; + } + + if(checkDummies) { + if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_DUMMIES], line, ignoreSeeThrough)) + return false; + if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_DUMMIES_OVERLAP], line, ignoreSeeThrough)) + return false; + } + + return true; +} + +bool +CWorld::GetIsLineOfSightSectorListClear(CPtrList &list, const CColLine &line, bool ignoreSeeThrough, + bool ignoreSomeObjects) +{ + CPtrNode *node; + CEntity *e; + CColModel *colmodel; + + for(node = list.first; node; node = node->next) { + e = (CEntity *)node->item; + if(e->m_scanCode != GetCurrentScanCode() && e->bUsesCollision) { + + e->m_scanCode = GetCurrentScanCode(); + + if(e != pIgnoreEntity && !(ignoreSomeObjects && CameraToIgnoreThisObject(e))) { + + colmodel = CModelInfo::GetColModel(e->GetModelIndex()); + + if(CCollision::TestLineOfSight(line, e->GetMatrix(), *colmodel, ignoreSeeThrough, false)) + return false; + } + } + } + + return true; +} + +void +CWorld::FindObjectsInRangeSectorList(CPtrList &list, Const CVector ¢re, float radius, bool ignoreZ, int16 *numObjects, + int16 lastObject, CEntity **objects) +{ + float radiusSqr = radius * radius; + float objDistSqr; + + for(CPtrNode *node = list.first; node; node = node->next) { + CEntity *object = (CEntity *)node->item; + if(object->m_scanCode != GetCurrentScanCode()) { + object->m_scanCode = GetCurrentScanCode(); + + CVector diff = centre - object->GetPosition(); + if(ignoreZ) + objDistSqr = diff.MagnitudeSqr2D(); + else + objDistSqr = diff.MagnitudeSqr(); + + if(objDistSqr < radiusSqr && *numObjects < lastObject) { + if(objects) { objects[*numObjects] = object; } + (*numObjects)++; + } + } + } +} + +void +CWorld::FindObjectsInRange(Const CVector ¢re, float radius, bool ignoreZ, int16 *numObjects, int16 lastObject, + CEntity **objects, bool checkBuildings, bool checkVehicles, bool checkPeds, + bool checkObjects, bool checkDummies) +{ + int minX = GetSectorIndexX(centre.x - radius); + if(minX <= 0) minX = 0; + + int minY = GetSectorIndexY(centre.y - radius); + if(minY <= 0) minY = 0; + + int maxX = GetSectorIndexX(centre.x + radius); + if(maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X - 1; + + int maxY = GetSectorIndexY(centre.y + radius); + if(maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y - 1; + + AdvanceCurrentScanCode(); + + *numObjects = 0; + for(int curY = minY; curY <= maxY; curY++) { + for(int curX = minX; curX <= maxX; curX++) { + CSector *sector = GetSector(curX, curY); + if(checkBuildings) { + FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_BUILDINGS], centre, radius, + ignoreZ, numObjects, lastObject, objects); + FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], centre, + radius, ignoreZ, numObjects, lastObject, objects); + } + if(checkVehicles) { + FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_VEHICLES], centre, radius, + ignoreZ, numObjects, lastObject, objects); + FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], centre, + radius, ignoreZ, numObjects, lastObject, objects); + } + if(checkPeds) { + FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_PEDS], centre, radius, ignoreZ, + numObjects, lastObject, objects); + FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_PEDS_OVERLAP], centre, radius, + ignoreZ, numObjects, lastObject, objects); + } + if(checkObjects) { + FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_OBJECTS], centre, radius, + ignoreZ, numObjects, lastObject, objects); + FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], centre, + radius, ignoreZ, numObjects, lastObject, objects); + } + if(checkDummies) { + FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_DUMMIES], centre, radius, + ignoreZ, numObjects, lastObject, objects); + FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_DUMMIES_OVERLAP], centre, + radius, ignoreZ, numObjects, lastObject, objects); + } + } + } +} + +void +CWorld::FindObjectsOfTypeInRangeSectorList(uint32 modelId, CPtrList &list, const CVector &position, float radius, + bool bCheck2DOnly, int16 *nEntitiesFound, int16 maxEntitiesToFind, + CEntity **aEntities) +{ + for(CPtrNode *pNode = list.first; pNode; pNode = pNode->next) { + CEntity *pEntity = (CEntity *)pNode->item; + if(pEntity->m_scanCode != GetCurrentScanCode()) { + pEntity->m_scanCode = GetCurrentScanCode(); + if (modelId == pEntity->GetModelIndex()) { + float fMagnitude = 0.0f; + if(bCheck2DOnly) + fMagnitude = (position - pEntity->GetPosition()).MagnitudeSqr2D(); + else + fMagnitude = (position - pEntity->GetPosition()).MagnitudeSqr(); + if(fMagnitude < radius * radius && *nEntitiesFound < maxEntitiesToFind) { + if(aEntities) aEntities[*nEntitiesFound] = pEntity; + ++*nEntitiesFound; + } + } + } + } +} + +void +CWorld::FindObjectsOfTypeInRange(uint32 modelId, const CVector &position, float radius, bool bCheck2DOnly, + int16 *nEntitiesFound, int16 maxEntitiesToFind, CEntity **aEntities, bool bBuildings, + bool bVehicles, bool bPeds, bool bObjects, bool bDummies) +{ + AdvanceCurrentScanCode(); + *nEntitiesFound = 0; + const CVector2D vecSectorStartPos(position.x - radius, position.y - radius); + const CVector2D vecSectorEndPos(position.x + radius, position.y + radius); + const int32 nStartX = Max(GetSectorIndexX(vecSectorStartPos.x), 0); + const int32 nStartY = Max(GetSectorIndexY(vecSectorStartPos.y), 0); +#ifdef FIX_BUGS + const int32 nEndX = Min(GetSectorIndexX(vecSectorEndPos.x), NUMSECTORS_X - 1); + const int32 nEndY = Min(GetSectorIndexY(vecSectorEndPos.y), NUMSECTORS_Y - 1); +#else + const int32 nEndX = Min(GetSectorIndexX(vecSectorEndPos.x), NUMSECTORS_X); + const int32 nEndY = Min(GetSectorIndexY(vecSectorEndPos.y), NUMSECTORS_Y); +#endif + for(int32 y = nStartY; y <= nEndY; y++) { + for(int32 x = nStartX; x <= nEndX; x++) { + CSector *pSector = GetSector(x, y); + if(bBuildings) { + FindObjectsOfTypeInRangeSectorList( + modelId, pSector->m_lists[ENTITYLIST_BUILDINGS], position, radius, bCheck2DOnly, + nEntitiesFound, maxEntitiesToFind, aEntities); + FindObjectsOfTypeInRangeSectorList( + modelId, pSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], position, radius, + bCheck2DOnly, nEntitiesFound, maxEntitiesToFind, aEntities); + } + if(bVehicles) { + FindObjectsOfTypeInRangeSectorList( + modelId, pSector->m_lists[ENTITYLIST_VEHICLES], position, radius, bCheck2DOnly, + nEntitiesFound, maxEntitiesToFind, aEntities); + FindObjectsOfTypeInRangeSectorList( + modelId, pSector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], position, radius, + bCheck2DOnly, nEntitiesFound, maxEntitiesToFind, aEntities); + } + if(bPeds) { + FindObjectsOfTypeInRangeSectorList( + modelId, pSector->m_lists[ENTITYLIST_PEDS], position, radius, bCheck2DOnly, + nEntitiesFound, maxEntitiesToFind, aEntities); + FindObjectsOfTypeInRangeSectorList( + modelId, pSector->m_lists[ENTITYLIST_PEDS_OVERLAP], position, radius, bCheck2DOnly, + nEntitiesFound, maxEntitiesToFind, aEntities); + } + if(bObjects) { + FindObjectsOfTypeInRangeSectorList( + modelId, pSector->m_lists[ENTITYLIST_OBJECTS], position, radius, bCheck2DOnly, + nEntitiesFound, maxEntitiesToFind, aEntities); + FindObjectsOfTypeInRangeSectorList( + modelId, pSector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], position, radius, + bCheck2DOnly, nEntitiesFound, maxEntitiesToFind, aEntities); + } + if(bDummies) { + FindObjectsOfTypeInRangeSectorList( + modelId, pSector->m_lists[ENTITYLIST_DUMMIES], position, radius, bCheck2DOnly, + nEntitiesFound, maxEntitiesToFind, aEntities); + FindObjectsOfTypeInRangeSectorList( + modelId, pSector->m_lists[ENTITYLIST_DUMMIES_OVERLAP], position, radius, + bCheck2DOnly, nEntitiesFound, maxEntitiesToFind, aEntities); + } + } + } +} + +CEntity * +CWorld::TestSphereAgainstWorld(CVector centre, float radius, CEntity *entityToIgnore, bool checkBuildings, + bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, + bool ignoreSomeObjects) +{ + CEntity *foundE = nil; + + int minX = GetSectorIndexX(centre.x - radius); + if(minX <= 0) minX = 0; + + int minY = GetSectorIndexY(centre.y - radius); + if(minY <= 0) minY = 0; + + int maxX = GetSectorIndexX(centre.x + radius); +#ifdef FIX_BUGS + if(maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X - 1; +#else + if(maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X; +#endif + + int maxY = GetSectorIndexY(centre.y + radius); +#ifdef FIX_BUGS + if(maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y - 1; +#else + if(maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y; +#endif + + AdvanceCurrentScanCode(); + + for(int curY = minY; curY <= maxY; curY++) { + for(int curX = minX; curX <= maxX; curX++) { + CSector *sector = GetSector(curX, curY); + if(checkBuildings) { + foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_BUILDINGS], centre, + radius, entityToIgnore, false); + if(foundE) return foundE; + + foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], + centre, radius, entityToIgnore, false); + if(foundE) return foundE; + } + if(checkVehicles) { + foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_VEHICLES], centre, + radius, entityToIgnore, false); + if(foundE) return foundE; + + foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], + centre, radius, entityToIgnore, false); + if(foundE) return foundE; + } + if(checkPeds) { + foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_PEDS], centre, radius, + entityToIgnore, false); + if(foundE) return foundE; + + foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_PEDS_OVERLAP], centre, + radius, entityToIgnore, false); + if(foundE) return foundE; + } + if(checkObjects) { + foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_OBJECTS], centre, + radius, entityToIgnore, ignoreSomeObjects); + if(foundE) return foundE; + + foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], + centre, radius, entityToIgnore, ignoreSomeObjects); + if(foundE) return foundE; + } + if(checkDummies) { + foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_DUMMIES], centre, + radius, entityToIgnore, false); + if(foundE) return foundE; + + foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_DUMMIES_OVERLAP], + centre, radius, entityToIgnore, false); + if(foundE) return foundE; + } + } + } + return foundE; +} + +CEntity * +CWorld::TestSphereAgainstSectorList(CPtrList &list, CVector spherePos, float radius, CEntity *entityToIgnore, + bool ignoreSomeObjects) +{ + static CColModel OurColModel; + CColSphere sphere; + + OurColModel.boundingSphere.center.x = 0.0f; + OurColModel.boundingSphere.center.y = 0.0f; + OurColModel.boundingSphere.center.z = 0.0f; + OurColModel.boundingSphere.radius = radius; + OurColModel.boundingBox.min.x = -radius; + OurColModel.boundingBox.min.y = -radius; + OurColModel.boundingBox.min.z = -radius; + OurColModel.boundingBox.max.x = radius; + OurColModel.boundingBox.max.y = radius; + OurColModel.boundingBox.max.z = radius; + OurColModel.numSpheres = 1; + sphere.Set(radius, CVector(0.0f, 0.0f, 0.0f)); + OurColModel.spheres = &sphere; + OurColModel.numLines = 0; + OurColModel.numBoxes = 0; + OurColModel.numTriangles = 0; + OurColModel.ownsCollisionVolumes = false; + + CMatrix sphereMat; + sphereMat.SetTranslate(spherePos); + + for(CPtrNode *node = list.first; node; node = node->next) { + CEntity *e = (CEntity *)node->item; + + if(e->m_scanCode != GetCurrentScanCode()) { + e->m_scanCode = GetCurrentScanCode(); + + if(e != entityToIgnore && e->bUsesCollision && + !(ignoreSomeObjects && CameraToIgnoreThisObject(e))) { + CVector diff = spherePos - e->GetBoundCentre(); + float distance = diff.Magnitude(); + + if(e->GetBoundRadius() + radius > distance) { + CColModel *eCol = CModelInfo::GetColModel(e->GetModelIndex()); + int collidedSpheres = + CCollision::ProcessColModels(sphereMat, OurColModel, e->GetMatrix(), *eCol, + gaTempSphereColPoints, nil, nil); + + if(collidedSpheres != 0 || + (e->IsVehicle() && ((CVehicle *)e)->m_vehType == VEHICLE_TYPE_CAR && e->GetModelIndex() != MI_DODO && + radius + eCol->boundingBox.max.x > distance)) { + return e; + } + } + } + } + } + + return nil; +} + +float +CWorld::FindGroundZForCoord(float x, float y) +{ + CColPoint point; + CEntity *ent; + if(ProcessVerticalLine(CVector(x, y, 1000.0f), -1000.0f, point, ent, true, false, false, false, true, false, + nil)) + return point.point.z; + else + return 20.0f; +} + +float +CWorld::FindGroundZFor3DCoord(float x, float y, float z, bool *found) +{ + CColPoint point; + CEntity *ent; + if(ProcessVerticalLine(CVector(x, y, z), -1000.0f, point, ent, true, false, false, false, false, false, nil)) { + if(found) *found = true; + return point.point.z; + } else { + if(found) *found = false; + return 0.0f; + } +} + +float +CWorld::FindRoofZFor3DCoord(float x, float y, float z, bool *found) +{ + CColPoint point; + CEntity *ent; + if(ProcessVerticalLine(CVector(x, y, z), 1000.0f, point, ent, true, false, false, false, true, false, nil)) { + if(found) *found = true; + return point.point.z; + } else { + if(found == nil) + printf("THERE IS NO MAP BELOW THE FOLLOWING COORS:%f %f %f. (FindGroundZFor3DCoord)\n", x, y, + z); + if(found) *found = false; + return 20.0f; + } +} + +void +CWorld::RemoveReferencesToDeletedObject(CEntity *pDeletedObject) +{ + int32 i = CPools::GetPedPool()->GetSize(); + while(--i >= 0) { + CPed *pPed = CPools::GetPedPool()->GetSlot(i); + if(pPed && pPed != pDeletedObject) { + pPed->RemoveRefsToEntity(pDeletedObject); + if(pPed->m_pCurrentPhysSurface == pDeletedObject) pPed->m_pCurrentPhysSurface = nil; + } + } + i = CPools::GetVehiclePool()->GetSize(); + while(--i >= 0) { + CVehicle *pVehicle = CPools::GetVehiclePool()->GetSlot(i); + if(pVehicle && pVehicle != pDeletedObject) { + pVehicle->RemoveRefsToEntity(pDeletedObject); + pVehicle->RemoveRefsToVehicle(pDeletedObject); + } + } + i = CPools::GetObjectPool()->GetSize(); + while(--i >= 0) { + CObject *pObject = CPools::GetObjectPool()->GetSlot(i); + if(pObject && pObject != pDeletedObject) { pObject->RemoveRefsToEntity(pDeletedObject); } + } +} + +void +CWorld::FindObjectsKindaColliding(const CVector &position, float radius, bool bCheck2DOnly, int16 *nCollidingEntities, + int16 maxEntitiesToFind, CEntity **aEntities, bool bBuildings, bool bVehicles, + bool bPeds, bool bObjects, bool bDummies) +{ + AdvanceCurrentScanCode(); + *nCollidingEntities = 0; + const CVector2D vecSectorStartPos(position.x - radius, position.y - radius); + const CVector2D vecSectorEndPos(position.x + radius, position.y + radius); + const int32 nStartX = Max(GetSectorIndexX(vecSectorStartPos.x), 0); + const int32 nStartY = Max(GetSectorIndexY(vecSectorStartPos.y), 0); +#ifdef FIX_BUGS + const int32 nEndX = Min(GetSectorIndexX(vecSectorEndPos.x), NUMSECTORS_X - 1); + const int32 nEndY = Min(GetSectorIndexY(vecSectorEndPos.y), NUMSECTORS_Y - 1); +#else + const int32 nEndX = Min(GetSectorIndexX(vecSectorEndPos.x), NUMSECTORS_X); + const int32 nEndY = Min(GetSectorIndexY(vecSectorEndPos.y), NUMSECTORS_Y); +#endif + for(int32 y = nStartY; y <= nEndY; y++) { + for(int32 x = nStartX; x <= nEndX; x++) { + CSector *pSector = GetSector(x, y); + if(bBuildings) { + FindObjectsKindaCollidingSectorList( + pSector->m_lists[ENTITYLIST_BUILDINGS], position, radius, bCheck2DOnly, + nCollidingEntities, maxEntitiesToFind, aEntities); + FindObjectsKindaCollidingSectorList( + pSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], position, radius, bCheck2DOnly, + nCollidingEntities, maxEntitiesToFind, aEntities); + } + if(bVehicles) { + FindObjectsKindaCollidingSectorList( + pSector->m_lists[ENTITYLIST_VEHICLES], position, radius, bCheck2DOnly, + nCollidingEntities, maxEntitiesToFind, aEntities); + FindObjectsKindaCollidingSectorList( + pSector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], position, radius, bCheck2DOnly, + nCollidingEntities, maxEntitiesToFind, aEntities); + } + if(bPeds) { + FindObjectsKindaCollidingSectorList(pSector->m_lists[ENTITYLIST_PEDS], position, + radius, bCheck2DOnly, nCollidingEntities, + maxEntitiesToFind, aEntities); + FindObjectsKindaCollidingSectorList( + pSector->m_lists[ENTITYLIST_PEDS_OVERLAP], position, radius, bCheck2DOnly, + nCollidingEntities, maxEntitiesToFind, aEntities); + } + if(bObjects) { + FindObjectsKindaCollidingSectorList( + pSector->m_lists[ENTITYLIST_OBJECTS], position, radius, bCheck2DOnly, + nCollidingEntities, maxEntitiesToFind, aEntities); + FindObjectsKindaCollidingSectorList( + pSector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], position, radius, bCheck2DOnly, + nCollidingEntities, maxEntitiesToFind, aEntities); + } + if(bDummies) { + FindObjectsKindaCollidingSectorList( + pSector->m_lists[ENTITYLIST_DUMMIES], position, radius, bCheck2DOnly, + nCollidingEntities, maxEntitiesToFind, aEntities); + FindObjectsKindaCollidingSectorList( + pSector->m_lists[ENTITYLIST_DUMMIES_OVERLAP], position, radius, bCheck2DOnly, + nCollidingEntities, maxEntitiesToFind, aEntities); + } + } + } +} + +void +CWorld::FindObjectsKindaCollidingSectorList(CPtrList &list, const CVector &position, float radius, bool bCheck2DOnly, + int16 *nCollidingEntities, int16 maxEntitiesToFind, CEntity **aEntities) +{ + for(CPtrNode *pNode = list.first; pNode; pNode = pNode->next) { + CEntity *pEntity = (CEntity *)pNode->item; + if(pEntity->m_scanCode != GetCurrentScanCode()) { + pEntity->m_scanCode = GetCurrentScanCode(); + float fMagnitude = 0.0f; + if(bCheck2DOnly) + fMagnitude = (position - pEntity->GetPosition()).Magnitude2D(); + else + fMagnitude = (position - pEntity->GetPosition()).Magnitude(); + if(pEntity->GetBoundRadius() + radius > fMagnitude && *nCollidingEntities < maxEntitiesToFind) { + if(aEntities) aEntities[*nCollidingEntities] = pEntity; + ++*nCollidingEntities; + } + } + } +} + +void +CWorld::FindObjectsIntersectingCube(const CVector &vecStartPos, const CVector &vecEndPos, int16 *nIntersecting, + int16 maxEntitiesToFind, CEntity **aEntities, bool bBuildings, bool bVehicles, + bool bPeds, bool bObjects, bool bDummies) +{ + AdvanceCurrentScanCode(); + *nIntersecting = 0; + const int32 nStartX = Max(GetSectorIndexX(vecStartPos.x), 0); + const int32 nStartY = Max(GetSectorIndexY(vecStartPos.y), 0); +#ifdef FIX_BUGS + const int32 nEndX = Min(GetSectorIndexX(vecStartPos.x), NUMSECTORS_X - 1); + const int32 nEndY = Min(GetSectorIndexY(vecStartPos.y), NUMSECTORS_Y - 1); +#else + const int32 nEndX = Min(GetSectorIndexX(vecStartPos.x), NUMSECTORS_X); + const int32 nEndY = Min(GetSectorIndexY(vecStartPos.y), NUMSECTORS_Y); +#endif + for(int32 y = nStartY; y <= nEndY; y++) { + for(int32 x = nStartX; x <= nEndX; x++) { + CSector *pSector = GetSector(x, y); + if(bBuildings) { + FindObjectsIntersectingCubeSectorList(pSector->m_lists[ENTITYLIST_BUILDINGS], + vecStartPos, vecEndPos, nIntersecting, + maxEntitiesToFind, aEntities); + FindObjectsIntersectingCubeSectorList( + pSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], vecStartPos, vecEndPos, + nIntersecting, maxEntitiesToFind, aEntities); + } + if(bVehicles) { + FindObjectsIntersectingCubeSectorList(pSector->m_lists[ENTITYLIST_VEHICLES], + vecStartPos, vecEndPos, nIntersecting, + maxEntitiesToFind, aEntities); + FindObjectsIntersectingCubeSectorList( + pSector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], vecStartPos, vecEndPos, + nIntersecting, maxEntitiesToFind, aEntities); + } + if(bPeds) { + FindObjectsIntersectingCubeSectorList(pSector->m_lists[ENTITYLIST_PEDS], + vecStartPos, vecEndPos, nIntersecting, + maxEntitiesToFind, aEntities); + FindObjectsIntersectingCubeSectorList(pSector->m_lists[ENTITYLIST_PEDS_OVERLAP], + vecStartPos, vecEndPos, nIntersecting, + maxEntitiesToFind, aEntities); + } + if(bObjects) { + FindObjectsIntersectingCubeSectorList(pSector->m_lists[ENTITYLIST_OBJECTS], + vecStartPos, vecEndPos, nIntersecting, + maxEntitiesToFind, aEntities); + FindObjectsIntersectingCubeSectorList( + pSector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], vecStartPos, vecEndPos, nIntersecting, + maxEntitiesToFind, aEntities); + } + if(bDummies) { + FindObjectsIntersectingCubeSectorList(pSector->m_lists[ENTITYLIST_DUMMIES], + vecStartPos, vecEndPos, nIntersecting, + maxEntitiesToFind, aEntities); + FindObjectsIntersectingCubeSectorList( + pSector->m_lists[ENTITYLIST_DUMMIES_OVERLAP], vecStartPos, vecEndPos, nIntersecting, + maxEntitiesToFind, aEntities); + } + } + } +} + +void +CWorld::FindObjectsIntersectingCubeSectorList(CPtrList &list, const CVector &vecStartPos, const CVector &vecEndPos, + int16 *nIntersecting, int16 maxEntitiesToFind, CEntity **aEntities) +{ + for(CPtrNode *pNode = list.first; pNode; pNode = pNode->next) { + CEntity *pEntity = (CEntity *)pNode->item; + if(pEntity->m_scanCode != GetCurrentScanCode()) { + pEntity->m_scanCode = GetCurrentScanCode(); + float fRadius = pEntity->GetBoundRadius(); + const CVector &entityPos = pEntity->GetPosition(); + if(fRadius + entityPos.x >= vecStartPos.x && entityPos.x - fRadius <= vecEndPos.x && + fRadius + entityPos.y >= vecStartPos.y && entityPos.y - fRadius <= vecEndPos.y && + fRadius + entityPos.z >= vecStartPos.z && entityPos.z - fRadius <= vecEndPos.z && + *nIntersecting < maxEntitiesToFind) { + if(aEntities) aEntities[*nIntersecting] = pEntity; + ++*nIntersecting; + } + } + } +} + +void +CWorld::FindObjectsIntersectingAngledCollisionBox(const CBox &boundingBox, const CMatrix &matrix, + const CVector &position, float fStartX, float fStartY, float fEndX, + float fEndY, int16 *nEntitiesFound, int16 maxEntitiesToFind, + CEntity **aEntities, bool bBuildings, bool bVehicles, bool bPeds, + bool bObjects, bool bDummies) +{ + AdvanceCurrentScanCode(); + *nEntitiesFound = 0; + const int32 nStartX = Max(GetSectorIndexX(fStartX), 0); + const int32 nStartY = Max(GetSectorIndexY(fStartY), 0); +#ifdef FIX_BUGS + const int32 nEndX = Min(GetSectorIndexX(fEndX), NUMSECTORS_X - 1); + const int32 nEndY = Min(GetSectorIndexY(fEndY), NUMSECTORS_Y - 1); +#else + const int32 nEndX = Min(GetSectorIndexX(fEndX), NUMSECTORS_X); + const int32 nEndY = Min(GetSectorIndexY(fEndY), NUMSECTORS_Y); +#endif + for(int32 y = nStartY; y <= nEndY; y++) { + for(int32 x = nStartX; x <= nEndX; x++) { + CSector *pSector = GetSector(x, y); + if(bBuildings) { + FindObjectsIntersectingAngledCollisionBoxSectorList( + pSector->m_lists[ENTITYLIST_BUILDINGS], boundingBox, matrix, position, + nEntitiesFound, maxEntitiesToFind, aEntities); + FindObjectsIntersectingAngledCollisionBoxSectorList( + pSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], boundingBox, matrix, position, + nEntitiesFound, maxEntitiesToFind, aEntities); + } + if(bVehicles) { + FindObjectsIntersectingAngledCollisionBoxSectorList( + pSector->m_lists[ENTITYLIST_VEHICLES], boundingBox, matrix, position, + nEntitiesFound, maxEntitiesToFind, aEntities); + FindObjectsIntersectingAngledCollisionBoxSectorList( + pSector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], boundingBox, matrix, position, + nEntitiesFound, maxEntitiesToFind, aEntities); + } + if(bPeds) { + FindObjectsIntersectingAngledCollisionBoxSectorList( + pSector->m_lists[ENTITYLIST_PEDS], boundingBox, matrix, position, nEntitiesFound, + maxEntitiesToFind, aEntities); + FindObjectsIntersectingAngledCollisionBoxSectorList( + pSector->m_lists[ENTITYLIST_PEDS_OVERLAP], boundingBox, matrix, position, + nEntitiesFound, maxEntitiesToFind, aEntities); + } + if(bObjects) { + FindObjectsIntersectingAngledCollisionBoxSectorList( + pSector->m_lists[ENTITYLIST_OBJECTS], boundingBox, matrix, position, nEntitiesFound, + maxEntitiesToFind, aEntities); + FindObjectsIntersectingAngledCollisionBoxSectorList( + pSector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], boundingBox, matrix, position, + nEntitiesFound, maxEntitiesToFind, aEntities); + } + if(bDummies) { + FindObjectsIntersectingAngledCollisionBoxSectorList( + pSector->m_lists[ENTITYLIST_DUMMIES], boundingBox, matrix, position, nEntitiesFound, + maxEntitiesToFind, aEntities); + FindObjectsIntersectingAngledCollisionBoxSectorList( + pSector->m_lists[ENTITYLIST_DUMMIES_OVERLAP], boundingBox, matrix, position, + nEntitiesFound, maxEntitiesToFind, aEntities); + } + } + } +} + +void +CWorld::FindObjectsIntersectingAngledCollisionBoxSectorList(CPtrList &list, const CBox &boundingBox, + const CMatrix &matrix, const CVector &position, + int16 *nEntitiesFound, int16 maxEntitiesToFind, + CEntity **aEntities) +{ + for(CPtrNode *pNode = list.first; pNode; pNode = pNode->next) { + CEntity *pEntity = (CEntity *)pNode->item; + if(pEntity->m_scanCode != GetCurrentScanCode()) { + pEntity->m_scanCode = GetCurrentScanCode(); + CColSphere sphere; + CVector vecDistance = pEntity->GetPosition() - position; + sphere.radius = pEntity->GetBoundRadius(); + sphere.center = Multiply3x3(vecDistance, matrix); + if(CCollision::TestSphereBox(sphere, boundingBox) && *nEntitiesFound < maxEntitiesToFind) { + if(aEntities) aEntities[*nEntitiesFound] = pEntity; + ++*nEntitiesFound; + } + } + } +} + +void +CWorld::FindMissionEntitiesIntersectingCube(const CVector &vecStartPos, const CVector &vecEndPos, int16 *nIntersecting, + int16 maxEntitiesToFind, CEntity **aEntities, bool bVehicles, bool bPeds, + bool bObjects) +{ + AdvanceCurrentScanCode(); + *nIntersecting = 0; + const int32 nStartX = Max(GetSectorIndexX(vecStartPos.x), 0); + const int32 nStartY = Max(GetSectorIndexY(vecStartPos.y), 0); +#ifdef FIX_BUGS + const int32 nEndX = Min(GetSectorIndexX(vecEndPos.x), NUMSECTORS_X - 1); + const int32 nEndY = Min(GetSectorIndexY(vecEndPos.y), NUMSECTORS_Y - 1); +#else + const int32 nEndX = Min(GetSectorIndexX(vecEndPos.x), NUMSECTORS_X); + const int32 nEndY = Min(GetSectorIndexY(vecEndPos.y), NUMSECTORS_Y); +#endif + for(int32 y = nStartY; y <= nEndY; y++) { + for(int32 x = nStartX; x <= nEndX; x++) { + CSector *pSector = GetSector(x, y); + if(bVehicles) { + FindMissionEntitiesIntersectingCubeSectorList( + pSector->m_lists[ENTITYLIST_VEHICLES], vecStartPos, vecEndPos, nIntersecting, + maxEntitiesToFind, aEntities, true, false); + FindMissionEntitiesIntersectingCubeSectorList( + pSector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], vecStartPos, vecEndPos, + nIntersecting, maxEntitiesToFind, aEntities, true, false); + } + if(bPeds) { + FindMissionEntitiesIntersectingCubeSectorList( + pSector->m_lists[ENTITYLIST_PEDS], vecStartPos, vecEndPos, nIntersecting, + maxEntitiesToFind, aEntities, false, true); + FindMissionEntitiesIntersectingCubeSectorList( + pSector->m_lists[ENTITYLIST_PEDS_OVERLAP], vecStartPos, vecEndPos, nIntersecting, + maxEntitiesToFind, aEntities, false, true); + } + if(bObjects) { + FindMissionEntitiesIntersectingCubeSectorList( + pSector->m_lists[ENTITYLIST_OBJECTS], vecStartPos, vecEndPos, nIntersecting, + maxEntitiesToFind, aEntities, false, false); + FindMissionEntitiesIntersectingCubeSectorList( + pSector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], vecStartPos, vecEndPos, nIntersecting, + maxEntitiesToFind, aEntities, false, false); + } + } + } +} + +void +CWorld::FindMissionEntitiesIntersectingCubeSectorList(CPtrList &list, const CVector &vecStartPos, + const CVector &vecEndPos, int16 *nIntersecting, + int16 maxEntitiesToFind, CEntity **aEntities, bool bIsVehicleList, + bool bIsPedList) +{ + for(CPtrNode *pNode = list.first; pNode; pNode = pNode->next) { + CEntity *pEntity = (CEntity *)pNode->item; + if(pEntity->m_scanCode != GetCurrentScanCode()) { + pEntity->m_scanCode = GetCurrentScanCode(); + bool bIsMissionEntity = false; + if(bIsVehicleList) + bIsMissionEntity = ((CVehicle *)pEntity)->VehicleCreatedBy == MISSION_VEHICLE; + else if(bIsPedList) + bIsMissionEntity = ((CPed *)pEntity)->CharCreatedBy == MISSION_CHAR; + else + bIsMissionEntity = ((CObject *)pEntity)->ObjectCreatedBy == MISSION_OBJECT; + float fRadius = pEntity->GetBoundRadius(); + const CVector &entityPos = pEntity->GetPosition(); + if(bIsMissionEntity && fRadius + entityPos.x >= vecStartPos.x && + entityPos.x - fRadius <= vecEndPos.x && fRadius + entityPos.y >= vecStartPos.y && + entityPos.y - fRadius <= vecEndPos.y && fRadius + entityPos.z >= vecStartPos.z && + entityPos.z - fRadius <= vecEndPos.z && *nIntersecting < maxEntitiesToFind) { + if(aEntities) aEntities[*nIntersecting] = pEntity; + ++*nIntersecting; + } + } + } +} + +void +CWorld::ClearCarsFromArea(float x1, float y1, float z1, float x2, float y2, float z2) +{ + CVehiclePool *pVehiclePool = CPools::GetVehiclePool(); + for(int32 i = 0; i < pVehiclePool->GetSize(); i++) { + CVehicle *pVehicle = CPools::GetVehiclePool()->GetSlot(i); + if(pVehicle) { + const CVector &position = pVehicle->GetPosition(); + if(position.x >= x1 && position.x <= x2 && position.y >= y1 && position.y <= y2 && + position.z >= z1 && position.z <= z2 && !pVehicle->bIsLocked && pVehicle->CanBeDeleted()) { + if(pVehicle->pDriver) { + CPopulation::RemovePed(pVehicle->pDriver); + pVehicle->pDriver = nil; + } + for(int32 j = 0; j < pVehicle->m_nNumMaxPassengers; ++j) { + if(pVehicle->pPassengers[j]) { + CPopulation::RemovePed(pVehicle->pPassengers[j]); + pVehicle->pPassengers[j] = nil; + --pVehicle->m_nNumPassengers; + } + } + CCarCtrl::RemoveFromInterestingVehicleList(pVehicle); + Remove(pVehicle); + delete pVehicle; + } + } + } +} + +void +CWorld::ClearPedsFromArea(float x1, float y1, float z1, float x2, float y2, float z2) +{ + CPedPool *pPedPool = CPools::GetPedPool(); + for(int32 i = 0; i < pPedPool->GetSize(); i++) { + CPed *pPed = CPools::GetPedPool()->GetSlot(i); + if(pPed) { + const CVector &position = pPed->GetPosition(); + if(!pPed->IsPlayer() && pPed->CanBeDeleted() && position.x >= x1 && position.x <= x2 && + position.y >= y1 && position.y <= y2 && position.z >= z1 && position.z <= z2) { + CPopulation::RemovePed(pPed); + } + } + } +} + +void +CWorld::CallOffChaseForArea(float x1, float y1, float x2, float y2) +{ + AdvanceCurrentScanCode(); + float fStartX = x1 - 10.0f; + float fStartY = y1 - 10.0f; + float fEndX = x2 + 10.0f; + float fEndY = y2 + 10.0f; + const int32 nStartX = Max(GetSectorIndexX(fStartX), 0); + const int32 nStartY = Max(GetSectorIndexY(fStartY), 0); +#ifdef FIX_BUGS + const int32 nEndX = Min(GetSectorIndexX(fEndX), NUMSECTORS_X - 1); + const int32 nEndY = Min(GetSectorIndexY(fEndY), NUMSECTORS_Y - 1); +#else + const int32 nEndX = Min(GetSectorIndexX(fEndX), NUMSECTORS_X); + const int32 nEndY = Min(GetSectorIndexY(fEndY), NUMSECTORS_Y); +#endif + for(int32 y = nStartY; y <= nEndY; y++) { + for(int32 x = nStartX; x <= nEndX; x++) { + CSector *pSector = GetSector(x, y); + CallOffChaseForAreaSectorListVehicles(pSector->m_lists[ENTITYLIST_VEHICLES], x1, y1, x2, + y2, fStartX, fStartY, fEndX, fEndY); + CallOffChaseForAreaSectorListVehicles(pSector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], x1, + y1, x2, y2, fStartX, fStartY, fEndX, fEndY); + CallOffChaseForAreaSectorListPeds(pSector->m_lists[ENTITYLIST_PEDS], x1, y1, x2, y2); + CallOffChaseForAreaSectorListPeds(pSector->m_lists[ENTITYLIST_PEDS_OVERLAP], x1, y1, x2, + y2); + } + } +} + +void +CWorld::CallOffChaseForAreaSectorListVehicles(CPtrList &list, float x1, float y1, float x2, float y2, float fStartX, + float fStartY, float fEndX, float fEndY) +{ + for(CPtrNode *pNode = list.first; pNode; pNode = pNode->next) { + CVehicle *pVehicle = (CVehicle *)pNode->item; + if(pVehicle->m_scanCode != GetCurrentScanCode()) { + pVehicle->m_scanCode = GetCurrentScanCode(); + const CVector &vehiclePos = pVehicle->GetPosition(); + uint8 carMission = pVehicle->AutoPilot.m_nCarMission; + if(pVehicle != FindPlayerVehicle() && vehiclePos.x > fStartX && vehiclePos.x < fEndX && + vehiclePos.y > fStartY && vehiclePos.y < fEndY && pVehicle->bIsLawEnforcer && + (carMission == MISSION_RAMPLAYER_FARAWAY || carMission == MISSION_RAMPLAYER_CLOSE || + carMission == MISSION_BLOCKPLAYER_FARAWAY || carMission == MISSION_BLOCKPLAYER_CLOSE)) { + pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 2000; + CColModel *pColModel = pVehicle->GetColModel(); + bool bInsideSphere = false; + for(int32 i = 0; i < pColModel->numSpheres; i++) { + CVector pos = pVehicle->GetMatrix() * pColModel->spheres[i].center; + float fRadius = pColModel->spheres[i].radius; + if(pos.x + fRadius > x1 && pos.x - fRadius < x2 && pos.y + fRadius > y1 && + pos.y - fRadius < y2) + bInsideSphere = true; + // Maybe break the loop when bInsideSphere is set to true? + } + if(bInsideSphere) { + if(pVehicle->GetPosition().x <= (x1 + x2) * 0.5f) + pVehicle->m_vecMoveSpeed.x = Min(pVehicle->m_vecMoveSpeed.x, 0.0f); + else + pVehicle->m_vecMoveSpeed.x = Max(pVehicle->m_vecMoveSpeed.x, 0.0f); + if(pVehicle->GetPosition().y <= (y1 + y2) * 0.5f) + pVehicle->m_vecMoveSpeed.y = Min(pVehicle->m_vecMoveSpeed.y, 0.0f); + else + pVehicle->m_vecMoveSpeed.y = Max(pVehicle->m_vecMoveSpeed.y, 0.0f); + } + } + } + } +} + +void +CWorld::CallOffChaseForAreaSectorListPeds(CPtrList &list, float x1, float y1, float x2, float y2) +{ + for(CPtrNode *pNode = list.first; pNode; pNode = pNode->next) { + CPed *pPed = (CPed *)pNode->item; + const CVector &pedPos = pPed->GetPosition(); + if(pPed->m_scanCode != GetCurrentScanCode()) { + pPed->m_scanCode = GetCurrentScanCode(); + if(pPed != FindPlayerPed() && pPed->m_leader != FindPlayerPed() && pedPos.x > x1 && + pedPos.x < x2 && pedPos.y > y1 && pedPos.y < y2 && + (pPed->m_pedInObjective == FindPlayerPed() || + pPed->m_carInObjective && pPed->m_carInObjective == FindPlayerVehicle()) && + pPed->m_nPedState != PED_DEAD && pPed->m_nPedState != PED_DIE && + (pPed->m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || + pPed->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || + pPed->m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS)) { + if(pPed->IsPedInControl()) { + if(pPed->m_nPedType == PEDTYPE_COP) + ((CCopPed *)pPed)->ClearPursuit(); + else + pPed->SetIdle(); + pPed->SetObjective(OBJECTIVE_NONE); + } else { + pPed->m_prevObjective = OBJECTIVE_NONE; + pPed->m_nLastPedState = PED_IDLE; + } + } + } + } +} + +void +CWorld::RemoveEntityInsteadOfProcessingIt(CEntity *ent) +{ + if(ent->IsPed()) { + if(FindPlayerPed() == ent) + Remove(ent); + else + CPopulation::RemovePed((CPed *)ent); + } else { + Remove(ent); + delete ent; + } +} + +void +CWorld::RemoveFallenPeds(void) +{ + int poolSize = CPools::GetPedPool()->GetSize(); + for(int poolIndex = poolSize - 1; poolIndex >= 0; poolIndex--) { + CPed *ped = CPools::GetPedPool()->GetSlot(poolIndex); + if(ped) { + if(ped->GetPosition().z < MAP_Z_LOW_LIMIT) { + if(ped->CharCreatedBy != RANDOM_CHAR || ped->IsPlayer()) { + int closestNode = ThePaths.FindNodeClosestToCoors(ped->GetPosition(), PATH_PED, + 999999.9f, false, false); + CVector newPos = ThePaths.m_pathNodes[closestNode].GetPosition(); + newPos.z += 2.0f; + ped->Teleport(newPos); + ped->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + } else { + CPopulation::RemovePed(ped); + } + } + } + } +} + +void +CWorld::RemoveFallenCars(void) +{ + int poolSize = CPools::GetVehiclePool()->GetSize(); + for(int poolIndex = poolSize - 1; poolIndex >= 0; poolIndex--) { + CVehicle *veh = CPools::GetVehiclePool()->GetSlot(poolIndex); + if(veh) { + if(veh->GetPosition().z < MAP_Z_LOW_LIMIT) { + if(veh->VehicleCreatedBy == MISSION_VEHICLE && !veh->bRenderScorched || veh == FindPlayerVehicle() || + (veh->pDriver && veh->pDriver->IsPlayer())) { + int closestNode = ThePaths.FindNodeClosestToCoors(veh->GetPosition(), PATH_CAR, + 999999.9f, false, false); + CVector newPos = ThePaths.m_pathNodes[closestNode].GetPosition(); + newPos.z += 3.0f; + veh->Teleport(newPos); + veh->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + } else if(veh->VehicleCreatedBy == RANDOM_VEHICLE || + veh->VehicleCreatedBy == PARKED_VEHICLE) { + Remove(veh); + delete veh; + } + } + } + } +} + +void +CWorld::StopAllLawEnforcersInTheirTracks(void) +{ + int poolSize = CPools::GetVehiclePool()->GetSize(); + for(int poolIndex = poolSize - 1; poolIndex >= 0; poolIndex--) { + CVehicle *veh = CPools::GetVehiclePool()->GetSlot(poolIndex); + if(veh) { + if(veh->bIsLawEnforcer) veh->SetMoveSpeed(0.0f, 0.0f, 0.0f); + } + } +} + +void +CWorld::SetAllCarsCanBeDamaged(bool toggle) +{ + int poolSize = CPools::GetVehiclePool()->GetSize(); + for(int poolIndex = 0; poolIndex < poolSize; poolIndex++) { + CVehicle *veh = CPools::GetVehiclePool()->GetSlot(poolIndex); + if(veh) veh->bCanBeDamaged = toggle; + } +} + +void +CWorld::ExtinguishAllCarFiresInArea(CVector point, float range) +{ + int poolSize = CPools::GetVehiclePool()->GetSize(); + for(int poolIndex = 0; poolIndex < poolSize; poolIndex++) { + CVehicle *veh = CPools::GetVehiclePool()->GetSlot(poolIndex); + if(veh) { + if((point - veh->GetPosition()).MagnitudeSqr() < sq(range)) veh->ExtinguishCarFire(); + } + } +} + +inline void +AddSteamsFromGround(CPtrList& list) +{ + CPtrNode* pNode = list.first; + while (pNode) { + ((CEntity*)pNode->item)->AddSteamsFromGround(nil); + pNode = pNode->next; + } +} + +void +CWorld::AddParticles(void) +{ + for(int32 y = 0; y < NUMSECTORS_Y; y++) { + for(int32 x = 0; x < NUMSECTORS_X; x++) { + CSector *pSector = GetSector(x, y); + AddSteamsFromGround(pSector->m_lists[ENTITYLIST_BUILDINGS]); + AddSteamsFromGround(pSector->m_lists[ENTITYLIST_DUMMIES]); + } + } +} + +void +CWorld::ShutDown(void) +{ + for(int i = 0; i < NUMSECTORS_X * NUMSECTORS_Y; i++) { + CSector *pSector = GetSector(i % NUMSECTORS_X, i / NUMSECTORS_Y); + for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_BUILDINGS].first; pNode; pNode = pNode->next) { + CEntity *pEntity = (CEntity *)pNode->item; + Remove(pEntity); + delete pEntity; + } + for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_VEHICLES].first; pNode; pNode = pNode->next) { + CEntity *pEntity = (CEntity *)pNode->item; + Remove(pEntity); + delete pEntity; + } + for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_PEDS].first; pNode; pNode = pNode->next) { + CEntity *pEntity = (CEntity *)pNode->item; + Remove(pEntity); + delete pEntity; + } + for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_OBJECTS].first; pNode; pNode = pNode->next) { + CEntity *pEntity = (CEntity *)pNode->item; + Remove(pEntity); + delete pEntity; + } + for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_DUMMIES].first; pNode; pNode = pNode->next) { + CEntity *pEntity = (CEntity *)pNode->item; + Remove(pEntity); + delete pEntity; + } +#ifndef FIX_BUGS + pSector->m_lists[ENTITYLIST_BUILDINGS].Flush(); + pSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP].Flush(); + pSector->m_lists[ENTITYLIST_DUMMIES].Flush(); + pSector->m_lists[ENTITYLIST_DUMMIES_OVERLAP].Flush(); +#endif + } + for(int32 i = 0; i < NUM_LEVELS; i++) { + for(CPtrNode *pNode = ms_bigBuildingsList[i].first; pNode; pNode = pNode->next) { + CEntity *pEntity = (CEntity *)pNode->item; + // Maybe remove from world here? + delete pEntity; + } + ms_bigBuildingsList[i].Flush(); + } + for(int i = 0; i < NUMSECTORS_X * NUMSECTORS_Y; i++) { + CSector *pSector = GetSector(i % NUMSECTORS_X, i / NUMSECTORS_Y); +#ifdef FIX_BUGS + pSector->m_lists[ENTITYLIST_BUILDINGS].Flush(); + pSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP].Flush(); + pSector->m_lists[ENTITYLIST_DUMMIES].Flush(); + pSector->m_lists[ENTITYLIST_DUMMIES_OVERLAP].Flush(); +#endif + if(pSector->m_lists[ENTITYLIST_BUILDINGS].first) { + sprintf(gString, "Building list %d,%d not empty\n", i % NUMSECTORS_X, i / NUMSECTORS_Y); + pSector->m_lists[ENTITYLIST_BUILDINGS].Flush(); + } + if(pSector->m_lists[ENTITYLIST_DUMMIES].first) { + sprintf(gString, "Dummy list %d,%d not empty\n", i % NUMSECTORS_X, i / NUMSECTORS_Y); + pSector->m_lists[ENTITYLIST_DUMMIES].Flush(); + } + if(pSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP].first) { + sprintf(gString, "Building overlap list %d,%d not empty\n", i % NUMSECTORS_X, i / NUMSECTORS_Y); + pSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP].Flush(); + } + if(pSector->m_lists[ENTITYLIST_VEHICLES_OVERLAP].first) { + sprintf(gString, "Vehicle overlap list %d,%d not empty\n", i % NUMSECTORS_X, i / NUMSECTORS_Y); + pSector->m_lists[ENTITYLIST_VEHICLES_OVERLAP].Flush(); + } + if(pSector->m_lists[ENTITYLIST_PEDS_OVERLAP].first) { + sprintf(gString, "Ped overlap list %d,%d not empty\n", i % NUMSECTORS_X, i / NUMSECTORS_Y); + pSector->m_lists[ENTITYLIST_PEDS_OVERLAP].Flush(); + } + if(pSector->m_lists[ENTITYLIST_OBJECTS_OVERLAP].first) { + sprintf(gString, "Object overlap list %d,%d not empty\n", i % NUMSECTORS_X, i / NUMSECTORS_Y); + pSector->m_lists[ENTITYLIST_OBJECTS_OVERLAP].Flush(); + } + if(pSector->m_lists[ENTITYLIST_DUMMIES_OVERLAP].first) { + sprintf(gString, "Dummy overlap list %d,%d not empty\n", i % NUMSECTORS_X, i / NUMSECTORS_Y); + pSector->m_lists[ENTITYLIST_DUMMIES_OVERLAP].Flush(); + } + } + ms_listMovingEntityPtrs.Flush(); +} + +void +CWorld::ClearForRestart(void) +{ + if(CCutsceneMgr::HasLoaded()) CCutsceneMgr::DeleteCutsceneData(); + CProjectileInfo::RemoveAllProjectiles(); + CObject::DeleteAllTempObjects(); + CObject::DeleteAllMissionObjects(); + CPopulation::ConvertAllObjectsToDummyObjects(); + for(int i = 0; i < NUMSECTORS_X * NUMSECTORS_Y; i++) { + CSector *pSector = GetSector(i % NUMSECTORS_X, i / NUMSECTORS_Y); + for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_PEDS].first; pNode; pNode = pNode->next) { + CEntity *pEntity = (CEntity *)pNode->item; + Remove(pEntity); + delete pEntity; + } + for(CPtrNode *pNode = GetBigBuildingList(LEVEL_GENERIC).first; pNode; pNode = pNode->next) { + CVehicle *pVehicle = (CVehicle *)pNode->item; + if(pVehicle && pVehicle->IsVehicle() && pVehicle->IsPlane()) { + Remove(pVehicle); + delete pVehicle; + } + } + for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_VEHICLES].first; pNode; pNode = pNode->next) { + CEntity *pEntity = (CEntity *)pNode->item; + Remove(pEntity); + delete pEntity; + } + } + CPools::CheckPoolsEmpty(); +} + +void +CWorld::RepositionCertainDynamicObjects() +{ + int32 i = CPools::GetDummyPool()->GetSize(); + while(--i >= 0) { + CDummy *dummy = CPools::GetDummyPool()->GetSlot(i); + if(dummy) { RepositionOneObject(dummy); } + } +} + +void +CWorld::RepositionOneObject(CEntity *pEntity) +{ + int16 modelId = pEntity->GetModelIndex(); + if (modelId == MI_PARKINGMETER || modelId == MI_PHONEBOOTH1 || modelId == MI_WASTEBIN || + modelId == MI_BIN || modelId == MI_POSTBOX1 || modelId == MI_NEWSSTAND || modelId == MI_TRAFFICCONE || + modelId == MI_DUMP1 || modelId == MI_ROADWORKBARRIER1 || modelId == MI_BUSSIGN1 || modelId == MI_NOPARKINGSIGN1 || + modelId == MI_PHONESIGN || modelId == MI_FIRE_HYDRANT || modelId == MI_BOLLARDLIGHT || + modelId == MI_PARKTABLE || modelId == MI_PARKINGMETER2 || modelId == MI_TELPOLE02 || + modelId == MI_PARKBENCH || modelId == MI_BARRIER1 || IsTreeModel(modelId) + ) { + CVector& position = pEntity->GetMatrix().GetPosition(); + CColModel* pColModel = pEntity->GetColModel(); + float fBoundingBoxMinZ = pColModel->boundingBox.min.z; + float fHeight = pColModel->boundingBox.max.z - pColModel->boundingBox.min.z; + if (fHeight < OBJECT_REPOSITION_OFFSET_Z) fHeight = OBJECT_REPOSITION_OFFSET_Z; + position.z = FindGroundZFor3DCoord(position.x, position.y, + position.z + fHeight, nil) - + fBoundingBoxMinZ; + pEntity->GetMatrix().UpdateRW(); + pEntity->UpdateRwFrame(); + } else if(IsLightThatNeedsRepositioning(modelId)) { + CVector position = pEntity->GetMatrix().GetPosition(); + CColModel* pColModel = pEntity->GetColModel(); + float fBoundingBoxMinZ = pColModel->boundingBox.min.z; + float fHeight = pColModel->boundingBox.max.z - pColModel->boundingBox.min.z; + if (fHeight < OBJECT_REPOSITION_OFFSET_Z) fHeight = OBJECT_REPOSITION_OFFSET_Z; + if (pColModel->numBoxes == 1) + position = pEntity->GetMatrix() * CVector( + (pColModel->boxes[0].min.x + pColModel->boxes[0].max.x) / 2, + (pColModel->boxes[0].min.y + pColModel->boxes[0].max.y) / 2, + pColModel->boxes[0].min.z); + else if (pColModel->numSpheres > 0) { + position.z = 1000.0f; + for (int i = 0; i < pColModel->numSpheres; i++) { + if (pColModel->spheres[i].center.z < position.z) + position = pColModel->spheres[i].center; + } + if (position.z < 1000.0f) + position = pEntity->GetMatrix() * position; + } + pEntity->GetMatrix().GetPosition().z = FindGroundZFor3DCoord(position.x, position.y, pEntity->GetMatrix().GetPosition().z + fHeight, nil) - fBoundingBoxMinZ; + pEntity->GetMatrix().UpdateRW(); + pEntity->UpdateRwFrame(); + + } + if(modelId == MI_BUOY) { + bool bFound = true; + const CVector &position = pEntity->GetPosition(); + float fGroundZ = FindGroundZFor3DCoord(position.x, position.y, + position.z + OBJECT_REPOSITION_OFFSET_Z, &bFound); + CColModel *pColModel = pEntity->GetColModel(); + float fHeight = pColModel->boundingBox.max.z - pColModel->boundingBox.min.z; + pEntity->GetMatrix().GetPosition().z = 0.2f * fHeight + 6.0f - 0.5f * fHeight; + } +} + +void +CWorld::SetCarsOnFire(float x, float y, float z, float radius, CEntity *reason) +{ + int poolSize = CPools::GetVehiclePool()->GetSize(); + for(int poolIndex = poolSize - 1; poolIndex >= 0; poolIndex--) { + CVehicle *veh = CPools::GetVehiclePool()->GetSlot(poolIndex); + if(veh && veh->GetStatus() != STATUS_WRECKED && !veh->m_pCarFire && !veh->bFireProof) { + if(Abs(veh->GetPosition().z - z) < 5.0f && Abs(veh->GetPosition().x - x) < radius && + Abs(veh->GetPosition().y - y) < radius) + gFireManager.StartFire(veh, reason, 0.8f, true); + } + } +} + +void +CWorld::SetPedsChoking(float x, float y, float z, float radius, CEntity* reason) +{ + int32 poolSize = CPools::GetPedPool()->GetSize(); + for (int32 i = poolSize - 1; i >= 0; i--) { + CPed* pPed = CPools::GetPedPool()->GetSlot(i); + // suspicious copypaste + if (pPed && pPed->m_nPedState != PED_DEAD && !pPed->bInVehicle && !pPed->m_pFire && !pPed->bFireProof && pPed->CharCreatedBy != MISSION_CHAR) { + if (Abs(pPed->GetPosition().z - z) < 5.0f && Abs(pPed->GetPosition().x - x) < radius && + Abs(pPed->GetPosition().y - y) < radius) { + if (!pPed->IsPlayer()) + pPed->SetFlee(CVector2D(x, y), 10000); +#ifdef FIX_BUGS + pPed->InflictDamage(reason, WEAPONTYPE_TEARGAS, 1.0f, PEDPIECE_TORSO, 0); +#else + pPed->InflictDamage(nil, WEAPONTYPE_TEARGAS, 1.0f, PEDPIECE_TORSO, 0); +#endif + } + } + } +} + +void +CWorld::SetPedsOnFire(float x, float y, float z, float radius, CEntity *reason) +{ + int32 poolSize = CPools::GetPedPool()->GetSize(); + for(int32 i = poolSize - 1; i >= 0; i--) { + CPed *pPed = CPools::GetPedPool()->GetSlot(i); + if(pPed && pPed->m_nPedState != PED_DEAD && !pPed->bInVehicle && !pPed->m_pFire && !pPed->bFireProof) { + if(Abs(pPed->GetPosition().z - z) < 5.0f && Abs(pPed->GetPosition().x - x) < radius && + Abs(pPed->GetPosition().y - y) < radius) + gFireManager.StartFire(pPed, reason, 0.8f, true); + } + } +} + +void +CWorld::RemoveStaticObjects() +{ + for(int i = 0; i < NUMSECTORS_X * NUMSECTORS_Y; i++) { + CSector *pSector = GetSector(i % NUMSECTORS_X, i / NUMSECTORS_Y); + for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_BUILDINGS].first; pNode; pNode = pNode->next) { + CEntity *pEntity = (CEntity *)pNode->item; + Remove(pEntity); + delete pEntity; + } + for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_OBJECTS].first; pNode; pNode = pNode->next) { + CEntity *pEntity = (CEntity *)pNode->item; + Remove(pEntity); + delete pEntity; + } + for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_DUMMIES].first; pNode; pNode = pNode->next) { + CEntity *pEntity = (CEntity *)pNode->item; + Remove(pEntity); + delete pEntity; + } + pSector->m_lists[ENTITYLIST_BUILDINGS].Flush(); + pSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP].Flush(); + pSector->m_lists[ENTITYLIST_DUMMIES].Flush(); + pSector->m_lists[ENTITYLIST_DUMMIES_OVERLAP].Flush(); + } +} + +void +CWorld::Process(void) +{ + if(!(CTimer::GetFrameCounter() & 63)) CReferences::PruneAllReferencesInWorld(); + + if(bProcessCutsceneOnly) { + for(int i = 0; i < NUMCUTSCENEOBJECTS; i++) { + CCutsceneObject *csObj = CCutsceneMgr::GetCutsceneObject(i); + if(csObj && csObj->m_entryInfoList.first) { + if(csObj->m_rwObject && RwObjectGetType(csObj->m_rwObject) == rpCLUMP && + RpAnimBlendClumpGetFirstAssociation(csObj->GetClump())) { + if (csObj->IsObject()) + RpAnimBlendClumpUpdateAnimations(csObj->GetClump(), CTimer::GetTimeStepNonClippedInSeconds()); + else { + if (!csObj->bOffscreen) + csObj->bOffscreen = !csObj->GetIsOnScreen(); + RpAnimBlendClumpUpdateAnimations(csObj->GetClump(), CTimer::GetTimeStepInSeconds(), !csObj->bOffscreen); + } + } + csObj->ProcessControl(); + csObj->ProcessCollision(); + csObj->GetMatrix().UpdateRW(); + csObj->UpdateRwFrame(); + } + } + CRecordDataForChase::ProcessControlCars(); + CRecordDataForChase::SaveOrRetrieveCarPositions(); + } else { + for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { + CEntity *movingEnt = (CEntity *)node->item; + if(!movingEnt->bRemoveFromWorld && movingEnt->m_rwObject && RwObjectGetType(movingEnt->m_rwObject) == rpCLUMP && + RpAnimBlendClumpGetFirstAssociation(movingEnt->GetClump())) { + if (movingEnt->IsObject()) + RpAnimBlendClumpUpdateAnimations(movingEnt->GetClump(), CTimer::GetTimeStepNonClippedInSeconds()); + else { + if (!movingEnt->bOffscreen) + movingEnt->bOffscreen = !movingEnt->GetIsOnScreen(); + RpAnimBlendClumpUpdateAnimations(movingEnt->GetClump(), CTimer::GetTimeStepInSeconds(), !movingEnt->bOffscreen); + } + } + } + for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { + CPhysical *movingEnt = (CPhysical *)node->item; + if(movingEnt->bRemoveFromWorld) { + RemoveEntityInsteadOfProcessingIt(movingEnt); + } else { + movingEnt->ProcessControl(); + if(movingEnt->GetIsStatic()) { movingEnt->RemoveFromMovingList(); } + } + } + bForceProcessControl = true; + for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { + CPhysical *movingEnt = (CPhysical *)node->item; + if(movingEnt->bWasPostponed) { + if(movingEnt->bRemoveFromWorld) { + RemoveEntityInsteadOfProcessingIt(movingEnt); + } else { + movingEnt->ProcessControl(); + if(movingEnt->GetIsStatic()) { movingEnt->RemoveFromMovingList(); } + } + } + } + bForceProcessControl = false; + if(CReplay::IsPlayingBack()) { + for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { + CEntity *movingEnt = (CEntity *)node->item; + movingEnt->bIsInSafePosition = true; + movingEnt->GetMatrix().UpdateRW(); + movingEnt->UpdateRwFrame(); + } + } else { + bNoMoreCollisionTorque = false; + for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { + CEntity *movingEnt = (CEntity *)node->item; + if(!movingEnt->bIsInSafePosition) { + movingEnt->ProcessCollision(); + movingEnt->GetMatrix().UpdateRW(); + movingEnt->UpdateRwFrame(); + } + } + bNoMoreCollisionTorque = true; + for(int i = 0; i < 4; i++) { + for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { + CEntity *movingEnt = (CEntity *)node->item; + if(!movingEnt->bIsInSafePosition) { + movingEnt->ProcessCollision(); + movingEnt->GetMatrix().UpdateRW(); + movingEnt->UpdateRwFrame(); + } + } + } + for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { + CEntity *movingEnt = (CEntity *)node->item; + if(!movingEnt->bIsInSafePosition) { + movingEnt->bIsStuck = true; + movingEnt->ProcessCollision(); + movingEnt->GetMatrix().UpdateRW(); + movingEnt->UpdateRwFrame(); + if(!movingEnt->bIsInSafePosition) { movingEnt->bIsStuck = true; } + } + } + bSecondShift = false; + for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { + CEntity *movingEnt = (CEntity *)node->item; + if(!movingEnt->bIsInSafePosition) { + movingEnt->ProcessShift(); + movingEnt->GetMatrix().UpdateRW(); + movingEnt->UpdateRwFrame(); + if(!movingEnt->bIsInSafePosition) { movingEnt->bIsStuck = true; } + } + } + bSecondShift = true; + for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { + CPhysical *movingEnt = (CPhysical *)node->item; + if(!movingEnt->bIsInSafePosition) { + movingEnt->ProcessShift(); + movingEnt->GetMatrix().UpdateRW(); + movingEnt->UpdateRwFrame(); + if(!movingEnt->bIsInSafePosition) { + movingEnt->bIsStuck = true; + if(movingEnt->GetStatus() == STATUS_PLAYER) { + printf("STUCK: Final Step: Player Entity %d Is Stuck\n", movingEnt->GetModelIndex()); + movingEnt->m_vecMoveSpeed *= Pow(0.707f, CTimer::GetTimeStep()); + movingEnt->ApplyMoveSpeed(); + movingEnt->ApplyTurnSpeed(); + } + } + } + } + } + for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { + CPed *movingPed = (CPed *)node->item; + if(movingPed->IsPed()) { + if(movingPed->bInVehicle && movingPed->m_nPedState != PED_EXIT_TRAIN || + movingPed->EnteringCar()) { + CVehicle *movingCar = movingPed->m_pMyVehicle; + if(movingCar) { +#ifdef GTA_TRAIN + if(movingCar->IsTrain()) { + movingPed->SetPedPositionInTrain(); + } else +#endif + { + switch(movingPed->m_nPedState) { + case PED_ENTER_CAR: + case PED_CARJACK: movingPed->EnterCar(); break; + case PED_DRAG_FROM_CAR: movingPed->BeingDraggedFromCar(); break; + case PED_EXIT_CAR: movingPed->ExitCar(); break; + case PED_ARRESTED: + if(movingPed->m_nLastPedState == PED_DRAG_FROM_CAR) { + movingPed->BeingDraggedFromCar(); + break; + } + // fall through + default: movingPed->SetPedPositionInCar(); break; + } + } + movingPed->GetMatrix().UpdateRW(); + movingPed->UpdateRwFrame(); + } else { + movingPed->bInVehicle = false; + movingPed->QuitEnteringCar(); + } + } else if (movingPed->m_attachedTo) { + movingPed->PositionAttachedPed(); + movingPed->GetMatrix().UpdateRW(); + movingPed->UpdateRwFrame(); + } + } + } + CMessages::Process(); + Players[PlayerInFocus].Process(); + CRecordDataForChase::SaveOrRetrieveCarPositions(); + if((CTimer::GetFrameCounter() & 7) == 1) { + RemoveFallenPeds(); + } else if((CTimer::GetFrameCounter() & 7) == 5) { + RemoveFallenCars(); + } + } +} + +void +CWorld::TriggerExplosion(const CVector &position, float fRadius, float fPower, CEntity *pCreator, + bool bProcessVehicleBombTimer) +{ + CVector2D vecStartPos(position.x - fRadius, position.y - fRadius); + CVector2D vecEndPos(position.x + fRadius, position.y + fRadius); + const int32 nStartX = Max(GetSectorIndexX(vecStartPos.x), 0); + const int32 nStartY = Max(GetSectorIndexY(vecStartPos.y), 0); + const int32 nEndX = Min(GetSectorIndexX(vecEndPos.x), NUMSECTORS_X - 1); + const int32 nEndY = Min(GetSectorIndexY(vecEndPos.y), NUMSECTORS_Y - 1); + for(int32 y = nStartY; y <= nEndY; y++) { + for(int32 x = nStartX; x <= nEndX; x++) { + CSector *pSector = GetSector(x, y); + TriggerExplosionSectorList(pSector->m_lists[ENTITYLIST_VEHICLES], position, fRadius, + fPower, pCreator, bProcessVehicleBombTimer); + TriggerExplosionSectorList(pSector->m_lists[ENTITYLIST_PEDS], position, fRadius, fPower, + pCreator, bProcessVehicleBombTimer); + TriggerExplosionSectorList(pSector->m_lists[ENTITYLIST_OBJECTS], position, fRadius, + fPower, pCreator, bProcessVehicleBombTimer); + } + } +} + +void +CWorld::TriggerExplosionSectorList(CPtrList &list, const CVector &position, float fRadius, float fPower, + CEntity *pCreator, bool bProcessVehicleBombTimer) +{ + for(CPtrNode *pNode = list.first; pNode; pNode = pNode->next) { + CPhysical *pEntity = (CPhysical *)pNode->item; + CVector vecDistance = pEntity->GetPosition() - position; + float fMagnitude = vecDistance.Magnitude(); + if(fRadius > fMagnitude) { + CWeapon::BlowUpExplosiveThings(pEntity); + CPed *pPed = (CPed *)pEntity; + CObject *pObject = (CObject *)pEntity; + CVehicle *pVehicle = (CVehicle *)pEntity; + if(!pEntity->bExplosionProof && (!pEntity->IsPed() || !pPed->bInVehicle)) { + if(pEntity->GetIsStatic()) { + if(pEntity->IsObject()) { + if (fPower > pObject->m_fUprootLimit || IsFence(pObject->GetModelIndex())) { + if (IsGlass(pObject->GetModelIndex())) { + CGlass::WindowRespondsToExplosion(pObject, position); + } else { + pObject->SetIsStatic(false); + pObject->AddToMovingList(); + int16 modelId = pEntity->GetModelIndex(); + if(modelId != MI_FIRE_HYDRANT || + pObject->bHasBeenDamaged) { + if(pEntity->IsObject() && + modelId != MI_EXPLODINGBARREL && + modelId != MI_PETROLPUMP && modelId != MI_PETROLPUMP2) + pObject->bHasBeenDamaged = true; + } else { + CVector pos = pEntity->GetPosition(); + pos.z -= 0.5f; + CParticleObject::AddObject(POBJECT_FIRE_HYDRANT, + pos, true); + pObject->bHasBeenDamaged = true; + } + } + } + if(pEntity->GetIsStatic()) { + float fDamageMultiplier = + (fRadius - fMagnitude) * 2.0f / fRadius; + float fDamage = 300.0f * Min(fDamageMultiplier, 1.0f); + pObject->ObjectDamage(fDamage); + } + } else { + pEntity->SetIsStatic(false); + pEntity->AddToMovingList(); + } + } + if(!pEntity->GetIsStatic()) { + float fDamageMultiplier = Min((fRadius - fMagnitude) * 2.0f / fRadius, 1.0f); + CVector vecForceDir = + vecDistance * (fPower * pEntity->m_fMass / 1400.0f * fDamageMultiplier / + Max(fMagnitude, 0.01f)); + vecForceDir.z = Max(vecForceDir.z, 0.0f); + if(pEntity == FindPlayerPed()) vecForceDir.z = Min(vecForceDir.z, 1.0f); + pEntity->ApplyMoveForce(vecForceDir); + if(!pEntity->bPedPhysics) { + float fBoundRadius = pEntity->GetBoundRadius(); + float fDistanceZ = position.z - pEntity->GetPosition().z; + float fPointZ = fBoundRadius; + if(Max(fDistanceZ, -fBoundRadius) < fBoundRadius) { + if(fDistanceZ <= -fBoundRadius) + fPointZ = -fBoundRadius; + else + fPointZ = fDistanceZ; + } + pEntity->ApplyTurnForce(vecForceDir.x, vecForceDir.y, vecForceDir.z, + 0.0f, 0.0f, fPointZ); + } + switch(pEntity->GetType()) { + case ENTITY_TYPE_VEHICLE: + if(pEntity->GetStatus() == STATUS_SIMPLE) { + pEntity->SetStatus(STATUS_PHYSICS); + CCarCtrl::SwitchVehicleToRealPhysics(pVehicle); + } + pVehicle->InflictDamage(pCreator, WEAPONTYPE_EXPLOSION, + 1100.0f * fDamageMultiplier); + if(bProcessVehicleBombTimer) { + if(pVehicle->m_nBombTimer) pVehicle->m_nBombTimer /= 10; + } + break; + case ENTITY_TYPE_PED: { + int8 direction = pPed->GetLocalDirection(-vecForceDir); + pPed->bIsStanding = false; + pPed->ApplyMoveForce(0.0, 0.0, 2.0f); + float fDamage = 250.0f * fDamageMultiplier; + pPed->InflictDamage(pCreator, WEAPONTYPE_EXPLOSION, fDamage, + PEDPIECE_TORSO, direction); + if(pPed->m_nPedState != PED_DIE) + pPed->SetFall(2000, + (AnimationId)(direction + ANIM_STD_HIGHIMPACT_FRONT), 0); + if(pCreator && pCreator->IsPed()) { + eEventType eventType = EVENT_SHOOT_PED; + if(pPed->m_nPedType == PEDTYPE_COP) eventType = EVENT_SHOOT_COP; + CEventList::RegisterEvent(eventType, EVENT_ENTITY_PED, pEntity, + (CPed *)pCreator, 10000); + pPed->RegisterThreatWithGangPeds(pCreator); + } + break; + } + case ENTITY_TYPE_OBJECT: + pObject->ObjectDamage(300.0f * fDamageMultiplier); + break; + default: break; + } + } + } + } + } +} + +void +CWorld::UseDetonator(CEntity *pEntity) +{ + int32 i = CPools::GetVehiclePool()->GetSize(); + while(--i >= 0) { +#ifdef FIX_BUGS + CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); +#else + CAutomobile *pVehicle = (CAutomobile *)CPools::GetVehiclePool()->GetSlot(i); +#endif + if(pVehicle && pVehicle->m_bombType == CARBOMB_REMOTE && + pVehicle->m_pBombRigger == pEntity) { + pVehicle->m_bombType = CARBOMB_NONE; + pVehicle->m_nBombTimer = 500; + pVehicle->m_pBlowUpEntity = pVehicle->m_pBombRigger; + if(pVehicle->m_pBlowUpEntity) + pVehicle->m_pBlowUpEntity->RegisterReference(&pVehicle->m_pBlowUpEntity); + } + } + CProjectileInfo::RemoveDetonatorProjectiles(); +} + +bool +CWorld::IsWanderPathClear(CVector const& point1, CVector const& point2, float distance, int maxSteps) +{ + if (Abs(point1.z - point2.z) > distance) + return false; + if (!GetIsLineOfSightClear(point1, point2, true, false, false, false, false, false, false)) + return false; + CVector vecBetween = point2 - point1; + uint32 nSteps = Max(vecBetween.Magnitude(), maxSteps); + if (nSteps == 0) + return true; + vecBetween.Normalise(); + uint32 step = 1; + for (step = 1; step < nSteps; step++) { + CVector posThisStep = point1 + vecBetween * step; + float level; + if (!CWaterLevel::GetWaterLevel(posThisStep, &level, false)) + continue; + posThisStep.z = level; + AdvanceCurrentScanCode(); + + CVector vecCheckedPos(posThisStep.x, posThisStep.y, Max(point1.z, point2.z)); + CColPoint colpoint; + CEntity* entity; + if (!ProcessVerticalLineSector(*GetSector(GetSectorIndexX(posThisStep.x), GetSectorIndexY(posThisStep.y)), + CColLine(posThisStep, vecCheckedPos), colpoint, entity, true, false, false, false, false, false, nil)) + return false; + } + + CVector posThisStep = point1; + AdvanceCurrentScanCode(); + CVector vecCheckedPos(posThisStep.x, posThisStep.y, point1.z - 5.0f); + + CColPoint colpoint; + CEntity* entity; + if (!ProcessVerticalLineSector(*GetSector(GetSectorIndexX(posThisStep.x), GetSectorIndexY(posThisStep.y)), + CColLine(posThisStep, vecCheckedPos), colpoint, entity, true, false, false, false, false, false, nil)) + return false; + + float heightNextStep = colpoint.point.z + 0.5f; + for (step = 1; step < nSteps; step++) { + CVector posThisStep = point1 + vecBetween * step; + posThisStep.z = heightNextStep; + AdvanceCurrentScanCode(); + CVector vecCheckedPos(posThisStep.x, posThisStep.y, heightNextStep - 2.0f); + if (!ProcessVerticalLineSector(*GetSector(GetSectorIndexX(posThisStep.x), GetSectorIndexY(posThisStep.y)), + CColLine(posThisStep, vecCheckedPos), colpoint, entity, true, false, false, false, false, false, nil)) + return false; + if (Abs(colpoint.point.z - heightNextStep) > 1.0f) + return false; + heightNextStep = colpoint.point.z + 0.5f; + } + return true; +} diff --git a/src/miami/core/World.h b/src/miami/core/World.h new file mode 100644 index 00000000..81eb5d4c --- /dev/null +++ b/src/miami/core/World.h @@ -0,0 +1,176 @@ +#pragma once + +#include "Game.h" +#include "Lists.h" +#include "PlayerInfo.h" +#include "Collision.h" + +/* Sectors span from -2400 to 1600 in x and -2000 to 2000 y. + * With 80x80 sectors, each is 50x50 units. */ + +#define SECTOR_SIZE_X (50.0f) +#define SECTOR_SIZE_Y (50.0f) + +#define NUMSECTORS_X (80) +#define NUMSECTORS_Y (80) + +#define WORLD_SIZE_X (NUMSECTORS_X * SECTOR_SIZE_X) +#define WORLD_SIZE_Y (NUMSECTORS_Y * SECTOR_SIZE_Y) + +#define WORLD_MIN_X (-2400.0f) +#define WORLD_MIN_Y (-2000.0f) + +#define WORLD_MAX_X (WORLD_MIN_X + WORLD_SIZE_X) +#define WORLD_MAX_Y (WORLD_MIN_Y + WORLD_SIZE_Y) + +#define MAP_Z_LOW_LIMIT -100.0f + +enum +{ + ENTITYLIST_BUILDINGS, + ENTITYLIST_BUILDINGS_OVERLAP, + ENTITYLIST_OBJECTS, + ENTITYLIST_OBJECTS_OVERLAP, + ENTITYLIST_VEHICLES, + ENTITYLIST_VEHICLES_OVERLAP, + ENTITYLIST_PEDS, + ENTITYLIST_PEDS_OVERLAP, + ENTITYLIST_DUMMIES, + ENTITYLIST_DUMMIES_OVERLAP, + + NUMSECTORENTITYLISTS +}; + +class CSector +{ +public: + CPtrList m_lists[NUMSECTORENTITYLISTS]; +}; + +VALIDATE_SIZE(CSector, 0x28); + +class CWorld +{ + static CPtrList ms_bigBuildingsList[NUM_LEVELS]; + static CPtrList ms_listMovingEntityPtrs; + static CSector ms_aSectors[NUMSECTORS_Y][NUMSECTORS_X]; + static uint16 ms_nCurrentScanCode; + +public: + static uint8 PlayerInFocus; + static CPlayerInfo Players[NUMPLAYERS]; + static CEntity *pIgnoreEntity; + static bool bIncludeDeadPeds; + static bool bNoMoreCollisionTorque; + static bool bSecondShift; + static bool bForceProcessControl; + static bool bProcessCutsceneOnly; + static bool bDoingCarCollisions; + static bool bIncludeCarTyres; + static bool bIncludeBikers; + static CColPoint m_aTempColPts[MAX_COLLISION_POINTS]; + + static void Remove(CEntity *entity); + static void Add(CEntity *entity); + + static CSector *GetSector(int x, int y) { if (x > NUMSECTORS_X - 1 || y > NUMSECTORS_Y - 1) return &ms_aSectors[0][0]; return &ms_aSectors[y][x]; } + static CPtrList &GetBigBuildingList(eLevelName i) { return ms_bigBuildingsList[i]; } + static CPtrList &GetMovingEntityList(void) { return ms_listMovingEntityPtrs; } + static uint16 GetCurrentScanCode(void) { return ms_nCurrentScanCode; } + static void AdvanceCurrentScanCode(void){ + if(++CWorld::ms_nCurrentScanCode == 0){ + CWorld::ClearScanCodes(); + CWorld::ms_nCurrentScanCode = 1; + } + } + static void ClearScanCodes(void); + static void ClearExcitingStuffFromArea(const CVector &pos, float radius, bool bRemoveProjectilesAndTidyUpShadows); + + static bool CameraToIgnoreThisObject(CEntity *ent); + + static bool ProcessLineOfSight(const CVector &point1, const CVector &point2, CColPoint &point, CEntity *&entity, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects = false, bool ignoreShootThrough = false); + static bool ProcessLineOfSightSector(CSector §or, const CColLine &line, CColPoint &point, float &dist, CEntity *&entity, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects, bool ignoreShootThrough); + static bool ProcessLineOfSightSectorList(CPtrList &list, const CColLine &line, CColPoint &point, float &dist, CEntity *&entity, bool ignoreSeeThrough, bool ignoreSomeObjects, bool ignoreShootThrough); + static bool ProcessVerticalLine(const CVector &point1, float z2, CColPoint &point, CEntity *&entity, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, CStoredCollPoly *poly); + static bool ProcessVerticalLineSector(CSector §or, const CColLine &line, CColPoint &point, CEntity *&entity, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, CStoredCollPoly *poly); + static bool ProcessVerticalLineSectorList(CPtrList &list, const CColLine &line, CColPoint &point, float &dist, CEntity *&entity, bool ignoreSeeThrough, CStoredCollPoly *poly); + static bool GetIsLineOfSightClear(const CVector &point1, const CVector &point2, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects = false); + static bool GetIsLineOfSightSectorClear(CSector §or, const CColLine &line, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects = false); + static bool GetIsLineOfSightSectorListClear(CPtrList &list, const CColLine &line, bool ignoreSeeThrough, bool ignoreSomeObjects = false); + + static CEntity *TestSphereAgainstWorld(CVector centre, float radius, CEntity *entityToIgnore, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSomeObjects); + static CEntity *TestSphereAgainstSectorList(CPtrList&, CVector, float, CEntity*, bool); + static void FindObjectsInRangeSectorList(CPtrList &list, Const CVector ¢re, float radius, bool ignoreZ, int16 *numObjects, int16 lastObject, CEntity **objects); + static void FindObjectsInRange(Const CVector ¢re, float radius, bool ignoreZ, int16 *numObjects, int16 lastObject, CEntity **objects, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies); + static void FindObjectsOfTypeInRangeSectorList(uint32 modelId, CPtrList& list, const CVector& position, float radius, bool bCheck2DOnly, int16* nEntitiesFound, int16 maxEntitiesToFind, CEntity** aEntities); + static void FindObjectsOfTypeInRange(uint32 modelId, const CVector& position, float radius, bool bCheck2DOnly, int16* nEntitiesFound, int16 maxEntitiesToFind, CEntity** aEntities, bool bBuildings, bool bVehicles, bool bPeds, bool bObjects, bool bDummies); + static float FindGroundZForCoord(float x, float y); + static float FindGroundZFor3DCoord(float x, float y, float z, bool *found); + static float FindRoofZFor3DCoord(float x, float y, float z, bool *found); + static void RemoveReferencesToDeletedObject(CEntity*); + static void FindObjectsKindaColliding(const CVector& position, float radius, bool bCheck2DOnly, int16* nCollidingEntities, int16 maxEntitiesToFind, CEntity** aEntities, bool bBuildings, bool bVehicles, bool bPeds, bool bObjects, bool bDummies); + static void FindObjectsKindaCollidingSectorList(CPtrList& list, const CVector& position, float radius, bool bCheck2DOnly, int16* nCollidingEntities, int16 maxEntitiesToFind, CEntity** aEntities); + static void FindObjectsIntersectingCube(const CVector& vecStartPos, const CVector& vecEndPos, int16* nIntersecting, int16 maxEntitiesToFind, CEntity** aEntities, bool bBuildings, bool bVehicles, bool bPeds, bool bObjects, bool bDummies); + static void FindObjectsIntersectingCubeSectorList(CPtrList& list, const CVector& vecStartPos, const CVector& vecEndPos, int16* nIntersecting, int16 maxEntitiesToFind, CEntity** aEntities); + static void FindObjectsIntersectingAngledCollisionBox(const CBox &, const CMatrix &, const CVector &, float, float, float, float, int16*, int16, CEntity **, bool, bool, bool, bool, bool); + static void FindObjectsIntersectingAngledCollisionBoxSectorList(CPtrList& list, const CBox& boundingBox, const CMatrix& matrix, const CVector& position, int16* nEntitiesFound, int16 maxEntitiesToFind, CEntity** aEntities); + static void FindMissionEntitiesIntersectingCube(const CVector& vecStartPos, const CVector& vecEndPos, int16* nIntersecting, int16 maxEntitiesToFind, CEntity** aEntities, bool bVehicles, bool bPeds, bool bObjects); + static void FindMissionEntitiesIntersectingCubeSectorList(CPtrList& list, const CVector& vecStartPos, const CVector& vecEndPos, int16* nIntersecting, int16 maxEntitiesToFind, CEntity** aEntities, bool bIsVehicleList, bool bIsPedList); + + static void ClearCarsFromArea(float x1, float y1, float z1, float x2, float y2, float z2); + static void ClearPedsFromArea(float x1, float y1, float z1, float x2, float y2, float z2); + static void CallOffChaseForArea(float x1, float y1, float x2, float y2); + static void CallOffChaseForAreaSectorListVehicles(CPtrList& list, float x1, float y1, float x2, float y2, float fStartX, float fStartY, float fEndX, float fEndY); + static void CallOffChaseForAreaSectorListPeds(CPtrList& list, float x1, float y1, float x2, float y2); + + static bool IsWanderPathClear(CVector const&, CVector const&, float, int); + + static float GetSectorX(float f) { return ((f - WORLD_MIN_X)/SECTOR_SIZE_X); } + static float GetSectorY(float f) { return ((f - WORLD_MIN_Y)/SECTOR_SIZE_Y); } + static int GetSectorIndexX(float f) { return (int)GetSectorX(f); } + static int GetSectorIndexY(float f) { return (int)GetSectorY(f); } + static float GetWorldX(int x) { return x*SECTOR_SIZE_X + WORLD_MIN_X; } + static float GetWorldY(int y) { return y*SECTOR_SIZE_Y + WORLD_MIN_Y; } + + static void RemoveEntityInsteadOfProcessingIt(CEntity* ent); + static void RemoveFallenPeds(); + static void RemoveFallenCars(); + + static void StopAllLawEnforcersInTheirTracks(); + static void SetAllCarsCanBeDamaged(bool); + static void ExtinguishAllCarFiresInArea(CVector, float); + static void SetCarsOnFire(float x, float y, float z, float radius, CEntity* reason); + static void SetPedsChoking(float x, float y, float z, float radius, CEntity* reason); + static void SetPedsOnFire(float x, float y, float z, float radius, CEntity* reason); + + static void Initialise(); + static void AddParticles(); + static void ShutDown(); + static void ClearForRestart(void); + static void RepositionCertainDynamicObjects(); + static void RepositionOneObject(CEntity* pEntity); + static void RemoveStaticObjects(); + static void Process(); + static void TriggerExplosion(const CVector& position, float fRadius, float fPower, CEntity* pCreator, bool bProcessVehicleBombTimer); + static void TriggerExplosionSectorList(CPtrList& list, const CVector& position, float fRadius, float fPower, CEntity* pCreator, bool bProcessVehicleBombTimer); + static void UseDetonator(CEntity *pEntity); + + // NB: following functions are unused (TODO?) + static void CastShadow(float, float, float, float); + static void CastShadowSectorList(CPtrList&, float, float, float, float); + static void FindLowestZForCoord(float, float); + static void CheckBlockListIntegrity(void); + static void ProcessVerticalLineSectorList_FillGlobeColPoints(CPtrList&, const CColLine&, CEntity*&, bool, CStoredCollPoly*); + static void ProcessVerticalLineSector_FillGlobeColPoints(CSector&, const CColLine&, CEntity*&, bool, bool, bool, bool, bool, bool, CStoredCollPoly*); + static void ProcessVerticalLine_FillGlobeColPoints(const CVector&, float, CEntity*&, bool, bool, bool, bool, bool, bool, CStoredCollPoly*); + static void PrintCarChanges(void); + static void TestForBuildingsOnTopOfEachOther(CPtrList&); + static void TestForBuildingsOnTopOfEachOther(void); + static void TestForUnusedModels(CPtrList&, int*); + static void TestForUnusedModels(void); + static void HandleCollisionZoneChange(eLevelName, eLevelName); + static void DoZoneTestForChaser(class CPhysical*); + static void FindPlayerSlotWithPedPointer(void*); +}; + +extern CColPoint gaTempSphereColPoints[MAX_COLLISION_POINTS]; diff --git a/src/miami/core/ZoneCull.cpp b/src/miami/core/ZoneCull.cpp new file mode 100644 index 00000000..db3577ad --- /dev/null +++ b/src/miami/core/ZoneCull.cpp @@ -0,0 +1,172 @@ +#include "common.h" + +#include "Building.h" +#include "Treadable.h" +#include "Train.h" +#include "Pools.h" +#include "Timer.h" +#include "Camera.h" +#include "World.h" +#include "FileMgr.h" +#include "ZoneCull.h" +#include "Zones.h" + +int32 CCullZones::NumAttributeZones; +CAttributeZone CCullZones::aAttributeZones[NUMATTRIBZONES]; + +int32 CCullZones::CurrentWantedLevelDrop_Player; +int32 CCullZones::CurrentFlags_Camera; +int32 CCullZones::CurrentFlags_Player; +bool CCullZones::bCurrentSubwayIsInvisible; +bool CCullZones::bAtBeachForAudio; + +void +CCullZones::Init(void) +{ + NumAttributeZones = 0; + CurrentWantedLevelDrop_Player = 0; + CurrentFlags_Camera = 0; + CurrentFlags_Player = 0; + bCurrentSubwayIsInvisible = false; +} + +void +CCullZones::Update(void) +{ + bool invisible; + + switch(CTimer::GetFrameCounter() & 7){ + case 0: + case 4: + UpdateAtBeachForAudio(); + break; + + case 2: + /* Update camera attributes */ + CurrentFlags_Camera = FindAttributesForCoors(TheCamera.GetGameCamPosition(), nil); + invisible = (CurrentFlags_Camera & ATTRZONE_SUBWAYVISIBLE) == 0; + if(invisible != bCurrentSubwayIsInvisible){ + MarkSubwayAsInvisible(!invisible); + bCurrentSubwayIsInvisible = invisible; + } + break; + + case 6: + /* Update player attributes */ + CurrentFlags_Player = FindAttributesForCoors(FindPlayerCoors(), + &CurrentWantedLevelDrop_Player); + break; + } +} + +// TODO? put somewhere else? +bool +IsPointWithinArbitraryArea(float px, float py, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) +{ + if((px-x1)*(x2-x1) - (py-y1)*(y2-y1) < 0.0f) return false; + if((px-x2)*(x3-x2) - (py-y2)*(y3-y2) < 0.0f) return false; + if((px-x3)*(x4-x3) - (py-y3)*(y4-y3) < 0.0f) return false; + if((px-x4)*(x1-x4) - (py-y4)*(y1-y4) < 0.0f) return false; + return true; +} + +void +CCullZones::UpdateAtBeachForAudio(void) +{ + bAtBeachForAudio = IsPointWithinArbitraryArea(TheCamera.GetPosition().x, TheCamera.GetPosition().y, + 400.0f, -1644.4f, + 751.9f, 1267.8f, + 971.9f, 1216.2f, + 840.0f, -1744.0f); +} + +void +CCullZones::ForceCullZoneCoors(CVector coors) +{ +} + +int32 +CCullZones::FindAttributesForCoors(CVector coors, int32 *wantedLevel) +{ + int i; + int32 attribs; + + if (wantedLevel) + *wantedLevel = 0; + attribs = 0; + for(i = 0; i < NumAttributeZones; i++) + if(coors.x >= aAttributeZones[i].minx && coors.x <= aAttributeZones[i].maxx && + coors.y >= aAttributeZones[i].miny && coors.y <= aAttributeZones[i].maxy && + coors.z >= aAttributeZones[i].minz && coors.z <= aAttributeZones[i].maxz){ + attribs |= aAttributeZones[i].attributes; + if(wantedLevel) + *wantedLevel = Max(*wantedLevel, aAttributeZones[i].wantedLevel); + } + return attribs; +} + +CAttributeZone* +CCullZones::FindZoneWithStairsAttributeForPlayer(void) +{ + int i; + CVector coors; + + coors = FindPlayerCoors(); + for(i = 0; i < NumAttributeZones; i++) + if(aAttributeZones[i].attributes & ATTRZONE_STAIRS && + coors.x >= aAttributeZones[i].minx && coors.x <= aAttributeZones[i].maxx && + coors.y >= aAttributeZones[i].miny && coors.y <= aAttributeZones[i].maxy && + coors.z >= aAttributeZones[i].minz && coors.z <= aAttributeZones[i].maxz) + return &aAttributeZones[i]; + return nil; +} + +void +CCullZones::MarkSubwayAsInvisible(bool visible) +{ + int i, n; + CEntity *e; + CVehicle *v; + + n = CPools::GetBuildingPool()->GetSize()-1; + for(i = n; i >= 0; i--){ + e = CPools::GetBuildingPool()->GetSlot(i); + if(e && e->bIsSubway) + e->bIsVisible = visible; + } + + n = CPools::GetTreadablePool()->GetSize()-1; + for(i = n; i >= 0; i--){ + e = CPools::GetTreadablePool()->GetSlot(i); + if(e && e->bIsSubway) + e->bIsVisible = visible; + } + + n = CPools::GetVehiclePool()->GetSize()-1; + for(i = n; i >= 0; i--){ + v = CPools::GetVehiclePool()->GetSlot(i); + if(v && v->IsTrain() && ((CTrain*)v)->m_nTrackId != TRACK_ELTRAIN) + v->bIsVisible = visible; + } +} + +void +CCullZones::AddCullZone(CVector const &position, + float minx, float maxx, + float miny, float maxy, + float minz, float maxz, + uint16 flag, int16 wantedLevel) +{ + CAttributeZone *attrib; + + assert(NumAttributeZones < NUMATTRIBZONES); + attrib = &aAttributeZones[NumAttributeZones++]; + attrib->minx = minx; + attrib->maxx = maxx; + attrib->miny = miny; + attrib->maxy = maxy; + attrib->minz = minz; + attrib->maxz = maxz; + attrib->attributes = flag; + attrib->wantedLevel = wantedLevel; +} diff --git a/src/miami/core/ZoneCull.h b/src/miami/core/ZoneCull.h new file mode 100644 index 00000000..d7780caf --- /dev/null +++ b/src/miami/core/ZoneCull.h @@ -0,0 +1,65 @@ +class CEntity; + +enum eZoneAttribs +{ + ATTRZONE_CAMCLOSEIN = 1, + ATTRZONE_STAIRS = 2, + ATTRZONE_1STPERSON = 4, + ATTRZONE_NORAIN = 8, + ATTRZONE_NOPOLICE = 0x10, + ATTRZONE_NOTCULLZONE = 0x20, + ATTRZONE_DOINEEDCOLLISION = 0x40, + ATTRZONE_SUBWAYVISIBLE = 0x80, + ATTRZONE_POLICEABANDONCARS = 0x100, + ATTRZONE_ROOMFORAUDIO = 0x200, + ATTRZONE_WATERFUDGE = 0x400, +}; + +struct CAttributeZone +{ + int16 minx; + int16 maxx; + int16 miny; + int16 maxy; + int16 minz; + int16 maxz; + int16 attributes; + int16 wantedLevel; +}; + +class CCullZones +{ +public: + static int32 NumAttributeZones; + static CAttributeZone aAttributeZones[NUMATTRIBZONES]; + + static int32 CurrentWantedLevelDrop_Player; + static int32 CurrentFlags_Camera; + static int32 CurrentFlags_Player; + static bool bCurrentSubwayIsInvisible; + static bool bAtBeachForAudio; + + static void Init(void); + static void Update(void); + static void UpdateAtBeachForAudio(void); + static void ForceCullZoneCoors(CVector coors); + static int32 FindAttributesForCoors(CVector coors, int32 *wantedLevel); + static CAttributeZone *FindZoneWithStairsAttributeForPlayer(void); + static void MarkSubwayAsInvisible(bool visible); + static void AddCullZone(CVector const &position, + float minx, float maxx, + float miny, float maxy, + float minz, float maxz, + uint16 flag, int16 wantedLevel); + static bool CamCloseInForPlayer(void) { return (CurrentFlags_Player & ATTRZONE_CAMCLOSEIN) != 0; } + static bool CamStairsForPlayer(void) { return (CurrentFlags_Player & ATTRZONE_STAIRS) != 0; } + static bool Cam1stPersonForPlayer(void) { return (CurrentFlags_Player & ATTRZONE_1STPERSON) != 0; } + static bool NoPolice(void) { return (CurrentFlags_Player & ATTRZONE_NOPOLICE) != 0; } + static bool DoINeedToLoadCollision(void) { return (CurrentFlags_Player & ATTRZONE_DOINEEDCOLLISION) != 0; } + static bool PlayerNoRain(void) { return (CurrentFlags_Player & ATTRZONE_NORAIN) != 0; } + static bool CamNoRain(void) { return (CurrentFlags_Camera & ATTRZONE_NORAIN) != 0; } + static bool PoliceAbandonCars(void) { return (CurrentFlags_Camera & ATTRZONE_POLICEABANDONCARS) != 0; } + static bool InRoomForAudio(void) { return (CurrentFlags_Camera & ATTRZONE_ROOMFORAUDIO) != 0; } + static bool WaterFudge(void) { return (CurrentFlags_Camera & ATTRZONE_WATERFUDGE) != 0; } + static int32 GetWantedLevelDrop(void) { return CurrentWantedLevelDrop_Player; } +}; diff --git a/src/miami/core/Zones.cpp b/src/miami/core/Zones.cpp new file mode 100644 index 00000000..93eca199 --- /dev/null +++ b/src/miami/core/Zones.cpp @@ -0,0 +1,800 @@ +#include "common.h" + +#include + +#include "Zones.h" + +#include "Clock.h" +#include "Text.h" +#include "World.h" +#include "Timer.h" +#include "SaveBuf.h" + +eLevelName CTheZones::m_CurrLevel; +int16 CTheZones::FindIndex; + +uint16 CTheZones::NumberOfAudioZones; +int16 CTheZones::AudioZoneArray[NUMAUDIOZONES]; +uint16 CTheZones::TotalNumberOfMapZones; +uint16 CTheZones::TotalNumberOfInfoZones; +uint16 CTheZones::TotalNumberOfNavigationZones; +CZone CTheZones::InfoZoneArray[NUMINFOZONES]; +CZone CTheZones::MapZoneArray[NUMMAPZONES]; +CZone CTheZones::NavigationZoneArray[NUMNAVIGZONES]; +uint16 CTheZones::TotalNumberOfZoneInfos; +CZoneInfo CTheZones::ZoneInfoArray[2*NUMINFOZONES]; + + +#define SWAPF(a, b) { float t; t = a; a = b; b = t; } + +wchar* +CZone::GetTranslatedName(void) +{ + return TheText.Get(name); +} + +void +CTheZones::Init(void) +{ + int i, j; + for(i = 0; i < NUMAUDIOZONES; i++) + AudioZoneArray[i] = -1; + NumberOfAudioZones = 0; + + for(i = 0; i < NUMNAVIGZONES; i++) + memset(&NavigationZoneArray[i], 0, sizeof(CZone)); + + for(i = 0; i < NUMINFOZONES; i++) + memset(&InfoZoneArray[i], 0, sizeof(CZone)); + + int x = 1000/9; + for(i = 0; i < 2*NUMINFOZONES; i++){ + // Cars + + ZoneInfoArray[i].carDensity = 10; + ZoneInfoArray[i].carThreshold[0] = x; + ZoneInfoArray[i].carThreshold[1] = ZoneInfoArray[i].carThreshold[0] + x; + ZoneInfoArray[i].carThreshold[2] = ZoneInfoArray[i].carThreshold[1] + x; + ZoneInfoArray[i].carThreshold[3] = ZoneInfoArray[i].carThreshold[2] + x; + ZoneInfoArray[i].carThreshold[4] = ZoneInfoArray[i].carThreshold[3] + x; + ZoneInfoArray[i].carThreshold[5] = ZoneInfoArray[i].carThreshold[4] + x; + ZoneInfoArray[i].carThreshold[6] = ZoneInfoArray[i].carThreshold[5] + x; + ZoneInfoArray[i].carThreshold[7] = ZoneInfoArray[i].carThreshold[6] + x; + ZoneInfoArray[i].carThreshold[8] = 1000; + + ZoneInfoArray[i].boatThreshold[0] = 500; + ZoneInfoArray[i].boatThreshold[1] = 1000; + + // What's going on here? this looks more like density + ZoneInfoArray[i].copThreshold = 50; + for(j = 0; j < NUM_GANGS; j++) + ZoneInfoArray[i].gangThreshold[j] = ZoneInfoArray[i].copThreshold; + + // Peds + + ZoneInfoArray[i].pedDensity = 12; + + // What's going on here? this looks more like density + ZoneInfoArray[i].copPedThreshold = 50; + for(j = 0; j < NUM_GANGS; j++) + ZoneInfoArray[i].gangPedThreshold[j] = ZoneInfoArray[i].copPedThreshold; + + ZoneInfoArray[i].pedGroup = 0; + } + + TotalNumberOfZoneInfos = 1; // why 1? + TotalNumberOfNavigationZones = 1; + TotalNumberOfInfoZones = 1; + + strcpy(InfoZoneArray[0].name, "CITYINF"); + InfoZoneArray[0].minx = -2400.0f; + InfoZoneArray[0].miny = -2000.0f; + InfoZoneArray[0].minz = -500.0f; + InfoZoneArray[0].maxx = 1600.0f; + InfoZoneArray[0].maxy = 2000.0f; + InfoZoneArray[0].maxz = 500.0f; + InfoZoneArray[0].level = LEVEL_GENERIC; + InfoZoneArray[0].type = ZONE_INFO; + + strcpy(NavigationZoneArray[0].name, "VICE_C"); + NavigationZoneArray[0].minx = -2400.0f; + NavigationZoneArray[0].miny = -2000.0f; + NavigationZoneArray[0].minz = -500.0f; + NavigationZoneArray[0].maxx = 1600.0f; + NavigationZoneArray[0].maxy = 2000.0f; + NavigationZoneArray[0].maxz = 500.0f; + NavigationZoneArray[0].level = LEVEL_GENERIC; + NavigationZoneArray[0].type = ZONE_DEFAULT; + + m_CurrLevel = LEVEL_GENERIC; + + for(i = 0; i < NUMMAPZONES; i++){ + memset(&MapZoneArray[i], 0, sizeof(CZone)); + MapZoneArray[i].type = ZONE_MAPZONE; + } + TotalNumberOfMapZones = 1; + strcpy(MapZoneArray[0].name, "THEMAP"); + MapZoneArray[0].minx = -2400.0f; + MapZoneArray[0].miny = -2000.0f; + MapZoneArray[0].minz = -500.0f; + MapZoneArray[0].maxx = 1600.0f; + MapZoneArray[0].maxy = 2000.0f; + MapZoneArray[0].maxz = 500.0f; + MapZoneArray[0].level = LEVEL_GENERIC; +} + +void +CTheZones::Update(void) +{ +#ifdef SQUEEZE_PERFORMANCE + if (CTimer::GetFrameCounter() % 5 != 0) + return; +#endif + CVector pos; + pos = FindPlayerCoors(); + m_CurrLevel = GetLevelFromPosition(&pos); +} + +void +CTheZones::CreateZone(char *name, eZoneType type, + float minx, float miny, float minz, + float maxx, float maxy, float maxz, + eLevelName level) +{ + char tmpname[24]; + char *p; + + if(minx > maxx) SWAPF(minx, maxx); + if(miny > maxy) SWAPF(miny, maxy); + if(minz > maxz) SWAPF(minz, maxz); + + // make upper case + for(p = name; *p; p++) if(islower(*p)) *p = toupper(*p); + + strncpy(tmpname, name, 7); + tmpname[7] = '\0'; + + // add zone + switch(type){ + case ZONE_DEFAULT: + case ZONE_NAVIG: + assert(TotalNumberOfNavigationZones < NUMNAVIGZONES); + strcpy(NavigationZoneArray[TotalNumberOfNavigationZones].name, tmpname); + NavigationZoneArray[TotalNumberOfNavigationZones].type = type; + NavigationZoneArray[TotalNumberOfNavigationZones].minx = minx; + NavigationZoneArray[TotalNumberOfNavigationZones].miny = miny; + NavigationZoneArray[TotalNumberOfNavigationZones].minz = minz; + NavigationZoneArray[TotalNumberOfNavigationZones].maxx = maxx; + NavigationZoneArray[TotalNumberOfNavigationZones].maxy = maxy; + NavigationZoneArray[TotalNumberOfNavigationZones].maxz = maxz; + NavigationZoneArray[TotalNumberOfNavigationZones].level = level; + TotalNumberOfNavigationZones++; + break; + case ZONE_INFO: + assert(TotalNumberOfInfoZones < NUMINFOZONES); + strcpy(InfoZoneArray[TotalNumberOfInfoZones].name, tmpname); + InfoZoneArray[TotalNumberOfInfoZones].type = type; + InfoZoneArray[TotalNumberOfInfoZones].minx = minx; + InfoZoneArray[TotalNumberOfInfoZones].miny = miny; + InfoZoneArray[TotalNumberOfInfoZones].minz = minz; + InfoZoneArray[TotalNumberOfInfoZones].maxx = maxx; + InfoZoneArray[TotalNumberOfInfoZones].maxy = maxy; + InfoZoneArray[TotalNumberOfInfoZones].maxz = maxz; + InfoZoneArray[TotalNumberOfInfoZones].level = level; + InfoZoneArray[TotalNumberOfInfoZones].zoneinfoDay = TotalNumberOfZoneInfos++; + InfoZoneArray[TotalNumberOfInfoZones].zoneinfoNight = TotalNumberOfZoneInfos++; + TotalNumberOfInfoZones++; + break; + case ZONE_MAPZONE: + assert(TotalNumberOfMapZones < NUMMAPZONES); + strcpy(MapZoneArray[TotalNumberOfMapZones].name, tmpname); + MapZoneArray[TotalNumberOfMapZones].type = type; + MapZoneArray[TotalNumberOfMapZones].minx = minx; + MapZoneArray[TotalNumberOfMapZones].miny = miny; + MapZoneArray[TotalNumberOfMapZones].minz = minz; + MapZoneArray[TotalNumberOfMapZones].maxx = maxx; + MapZoneArray[TotalNumberOfMapZones].maxy = maxy; + MapZoneArray[TotalNumberOfMapZones].maxz = maxz; + MapZoneArray[TotalNumberOfMapZones].level = level; + TotalNumberOfMapZones++; + break; + } +} + +void +CTheZones::PostZoneCreation(void) +{ + int i; + for(i = 1; i < TotalNumberOfNavigationZones; i++) + InsertZoneIntoZoneHierarchy(&NavigationZoneArray[i]); + InitialiseAudioZoneArray(); +#ifndef MASTER + CheckZonesForOverlap(); +#endif +} + +void +CTheZones::CheckZonesForOverlap(void) +{ + int i, j; + char str[116]; + + for(i = 1; i < TotalNumberOfInfoZones; i++){ + ZoneIsEntirelyContainedWithinOtherZone(&InfoZoneArray[i], &InfoZoneArray[0]); + + for(j = 1; j < TotalNumberOfInfoZones; j++) + if(i != j && ZoneIsEntirelyContainedWithinOtherZone(&InfoZoneArray[i], &InfoZoneArray[j])) + sprintf(str, "Info zone %s contains %s\n", InfoZoneArray[j].name, InfoZoneArray[i].name); + } +} + +void +CTheZones::InsertZoneIntoZoneHierarchy(CZone *zone) +{ + zone->child = nil; + zone->parent = nil; + zone->next = nil; + InsertZoneIntoZoneHierRecursive(zone, &NavigationZoneArray[0]); +} + +bool +CTheZones::InsertZoneIntoZoneHierRecursive(CZone *inner, CZone *outer) +{ + int n; + CZone *child, *next, *insert; + + // return false if inner was not inserted into outer + if(outer == nil || + !ZoneIsEntirelyContainedWithinOtherZone(inner, outer)) + return false; + + // try to insert inner into children of outer + for(child = outer->child; child; child = child->next) + if(InsertZoneIntoZoneHierRecursive(inner, child)) + return true; + + // insert inner as child of outer + // count number of outer's children contained within inner + n = 0; + for(child = outer->child; child; child = child->next) + if(ZoneIsEntirelyContainedWithinOtherZone(child, inner)) + n++; + inner->next = outer->child; + inner->parent = outer; + outer->child = inner; + // move children from outer to inner + if(n){ + insert = inner; + for(child = inner->next; child; child = next){ + next = child->next; + if(ZoneIsEntirelyContainedWithinOtherZone(child,inner)){ + insert->next = child->next; + child->parent = inner; + child->next = inner->child; + inner->child = child; + }else + insert = child; + } + } + + return true; +} + +bool +CTheZones::ZoneIsEntirelyContainedWithinOtherZone(CZone *inner, CZone *outer) +{ + char tmp[100]; + + if(inner->minx < outer->minx || + inner->maxx > outer->maxx || + inner->miny < outer->miny || + inner->maxy > outer->maxy || + inner->minz < outer->minz || + inner->maxz > outer->maxz){ + CVector vmin(inner->minx, inner->miny, inner->minz); + if(PointLiesWithinZone(&vmin, outer)) + sprintf(tmp, "Overlapping zones %s and %s\n", + inner->name, outer->name); + CVector vmax(inner->maxx, inner->maxy, inner->maxz); + if(PointLiesWithinZone(&vmax, outer)) + sprintf(tmp, "Overlapping zones %s and %s\n", + inner->name, outer->name); + return false; + } + return true; +} + +bool +CTheZones::PointLiesWithinZone(const CVector *v, CZone *zone) +{ + return zone->minx <= v->x && v->x <= zone->maxx && + zone->miny <= v->y && v->y <= zone->maxy && + zone->minz <= v->z && v->z <= zone->maxz; +} + +eLevelName +CTheZones::GetLevelFromPosition(CVector const *v) +{ + int i; +// char tmp[116]; +// if(!PointLiesWithinZone(v, &MapZoneArray[0])) +// sprintf(tmp, "x = %.3f y= %.3f z = %.3f\n", v.x, v.y, v.z); + for(i = 1; i < TotalNumberOfMapZones; i++) + if(PointLiesWithinZone(v, &MapZoneArray[i])) + return MapZoneArray[i].level; + return MapZoneArray[0].level; +} + +CZone* +CTheZones::FindInformationZoneForPosition(const CVector *v) +{ + int i; +// char tmp[116]; +// if(!PointLiesWithinZone(v, &InfoZoneArray[0])) +// sprintf(tmp, "x = %.3f y= %.3f z = %.3f\n", v.x, v.y, v.z); + for(i = 1; i < TotalNumberOfInfoZones; i++) + if(PointLiesWithinZone(v, &InfoZoneArray[i])) + return &InfoZoneArray[i]; + return &InfoZoneArray[0]; +} + +CZone* +CTheZones::FindSmallestNavigationZoneForPosition(const CVector *v, bool findDefault, bool findNavig) +{ + CZone *best = nil; + if(findDefault && NavigationZoneArray[0].type == ZONE_DEFAULT || + findNavig && NavigationZoneArray[0].type == ZONE_NAVIG) + best = &NavigationZoneArray[0]; + // zone to test next + CZone *zone = NavigationZoneArray[0].child; + while(zone) + // if in zone, descent into children + if(PointLiesWithinZone(v, zone)){ + if(findDefault && zone->type == ZONE_DEFAULT || + findNavig && zone->type == ZONE_NAVIG) + best = zone; + zone = zone->child; + // otherwise try next zone + }else + zone = zone->next; + return best; +} + +int16 +CTheZones::FindZoneByLabelAndReturnIndex(char *name, eZoneType type) +{ + char str[8]; + memset(str, 0, 8); + strncpy(str, name, 8); + switch(type){ + case ZONE_DEFAULT: + case ZONE_NAVIG: + for(FindIndex = 0; FindIndex < TotalNumberOfNavigationZones; FindIndex++) + if(strcmp(GetNavigationZone(FindIndex)->name, name) == 0) + return FindIndex; + break; + + case ZONE_INFO: + for(FindIndex = 0; FindIndex < TotalNumberOfInfoZones; FindIndex++) + if(strcmp(GetInfoZone(FindIndex)->name, name) == 0) + return FindIndex; + break; + + case ZONE_MAPZONE: + for(FindIndex = 0; FindIndex < TotalNumberOfMapZones; FindIndex++) + if(strcmp(GetMapZone(FindIndex)->name, name) == 0) + return FindIndex; + break; + } + return -1; +} + +int16 +CTheZones::FindNextZoneByLabelAndReturnIndex(char *name, eZoneType type) +{ + char str[8]; + ++FindIndex; + memset(str, 0, 8); + strncpy(str, name, 8); + switch(type){ + case ZONE_DEFAULT: + case ZONE_NAVIG: + for(; FindIndex < TotalNumberOfNavigationZones; FindIndex++) + if(strcmp(GetNavigationZone(FindIndex)->name, name) == 0) + return FindIndex; + break; + + case ZONE_INFO: + for(; FindIndex < TotalNumberOfInfoZones; FindIndex++) + if(strcmp(GetInfoZone(FindIndex)->name, name) == 0) + return FindIndex; + break; + + case ZONE_MAPZONE: + for(; FindIndex < TotalNumberOfMapZones; FindIndex++) + if(strcmp(GetMapZone(FindIndex)->name, name) == 0) + return FindIndex; + break; + } + return -1; +} + +CZoneInfo* +CTheZones::GetZoneInfo(const CVector *v, uint8 day) +{ + CZone *zone; + zone = FindInformationZoneForPosition(v); + if(zone == nil) + return &ZoneInfoArray[0]; + return &ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight]; +} + +void +CTheZones::GetZoneInfoForTimeOfDay(const CVector *pos, CZoneInfo *info) +{ + CZoneInfo *day, *night; + float d, n; + int i; + + day = GetZoneInfo(pos, 1); + night = GetZoneInfo(pos, 0); + + if(CClock::GetIsTimeInRange(8, 19)) + *info = *day; + else if(CClock::GetIsTimeInRange(22, 5)) + *info = *night; + else{ + if(CClock::GetIsTimeInRange(19, 22)){ + n = (CClock::GetHours() - 19) / 3.0f; + assert(n >= 0.0f && n <= 1.0f); + d = 1.0f - n; + }else{ + d = (CClock::GetHours() - 5) / 3.0f; + assert(d >= 0.0f && d <= 1.0f); + n = 1.0f - d; + } +#ifdef FIX_BUGS + info->carDensity = day->carDensity * d + night->carDensity * n; + for(i = 0; i < ARRAY_SIZE(info->carThreshold); i++) + info->carThreshold[i] = day->carThreshold[i] * d + night->carThreshold[i] * n; + for(i = 0; i < ARRAY_SIZE(info->boatThreshold); i++) + info->boatThreshold[i] = day->boatThreshold[i] * d + night->boatThreshold[i] * n; + for(i = 0; i < ARRAY_SIZE(info->gangThreshold); i++) + info->gangThreshold[i] = day->gangThreshold[i] * d + night->gangThreshold[i] * n; + + info->copThreshold = day->copThreshold * d + night->copThreshold * n; + info->pedDensity = day->pedDensity * d + night->pedDensity * n; + info->copPedThreshold = day->copPedThreshold * d + night->copPedThreshold * n; + for(i = 0; i < ARRAY_SIZE(info->gangPedThreshold); i++) + info->gangPedThreshold[i] = day->gangPedThreshold[i] * d + night->gangPedThreshold[i] * n; +#else + // This is a complete mess. + info->carDensity = day->carDensity * n + night->carDensity * d; + for(i = 0; i < ARRAY_SIZE(info->carThreshold); i++) + info->carThreshold[i] = night->carThreshold[i] * d + night->carThreshold[i] * n; + for(i = 0; i < ARRAY_SIZE(info->boatThreshold); i++) + info->boatThreshold[i] = night->boatThreshold[i] * d + night->boatThreshold[i] * n; + for(i = 0; i < ARRAY_SIZE(info->gangThreshold); i++) + info->gangThreshold[i] = night->gangThreshold[i] * d + night->gangThreshold[i] * n; + + info->copThreshold = night->copThreshold * d + night->copThreshold * n; + info->pedDensity = night->pedDensity * d + night->pedDensity * n; + info->copPedThreshold = night->copPedThreshold * d + night->copPedThreshold * n; + for(i = 0; i < ARRAY_SIZE(info->gangPedThreshold); i++) + info->gangPedThreshold[i] = night->gangPedThreshold[i] * d + night->gangPedThreshold[i] * n; +#endif + } + if(CClock::GetIsTimeInRange(5, 19)) + info->pedGroup = day->pedGroup; + else + info->pedGroup = night->pedGroup; +} + +void +CTheZones::SetZoneCarInfo(uint16 zoneid, uint8 day, int16 carDensity, + int16 copCarDensity, const int16 *gangCarDensities) +{ + CZone *zone; + CZoneInfo *info; + zone = GetInfoZone(zoneid); + info = &ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight]; + + info->carDensity = carDensity; + info->copThreshold = copCarDensity; + info->gangThreshold[0] = gangCarDensities[0] + copCarDensity; + info->gangThreshold[1] = gangCarDensities[1] + info->gangThreshold[0]; + info->gangThreshold[2] = gangCarDensities[2] + info->gangThreshold[1]; + info->gangThreshold[3] = gangCarDensities[3] + info->gangThreshold[2]; + info->gangThreshold[4] = gangCarDensities[4] + info->gangThreshold[3]; + info->gangThreshold[5] = gangCarDensities[5] + info->gangThreshold[4]; + info->gangThreshold[6] = gangCarDensities[6] + info->gangThreshold[5]; + info->gangThreshold[7] = gangCarDensities[7] + info->gangThreshold[6]; + info->gangThreshold[8] = gangCarDensities[8] + info->gangThreshold[7]; +} + +void CTheZones::SetZoneCivilianCarInfo(uint16 zoneid, uint8 day, + const int16* carDensities, const int16* boatDensities) +{ + CZone* zone; + CZoneInfo* info; + zone = GetInfoZone(zoneid); + info = &ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight]; + info->carThreshold[0] = carDensities[0]; + for (int i = 1; i < CCarCtrl::NUM_CAR_CLASSES; i++) + info->carThreshold[i] = carDensities[i] + info->carThreshold[i-1]; + info->boatThreshold[0] = boatDensities[0]; + for (int i = 1; i < CCarCtrl::NUM_BOAT_CLASSES; i++) + info->boatThreshold[i] = boatDensities[i] + info->boatThreshold[i - 1]; +} + +void +CTheZones::SetZonePedInfo(uint16 zoneid, uint8 day, int16 pedDensity, + int16 gang0Density, int16 gang1Density, int16 gang2Density, int16 gang3Density, + int16 gang4Density, int16 gang5Density, int16 gang6Density, int16 gang7Density, + int16 gang8Density, int16 copDensity) +{ + CZone *zone; + CZoneInfo *info; + zone = GetInfoZone(zoneid); + info = &ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight]; + info->pedDensity = pedDensity; + info->copPedThreshold = copDensity; + info->gangPedThreshold[0] = gang0Density; + info->gangPedThreshold[1] = gang1Density; + info->gangPedThreshold[2] = gang2Density; + info->gangPedThreshold[3] = gang3Density; + info->gangPedThreshold[4] = gang4Density; + info->gangPedThreshold[5] = gang5Density; + info->gangPedThreshold[6] = gang6Density; + info->gangPedThreshold[7] = gang7Density; + info->gangPedThreshold[8] = gang8Density; + + info->gangPedThreshold[0] += info->copPedThreshold; + info->gangPedThreshold[1] += info->gangPedThreshold[0]; + info->gangPedThreshold[2] += info->gangPedThreshold[1]; + info->gangPedThreshold[3] += info->gangPedThreshold[2]; + info->gangPedThreshold[4] += info->gangPedThreshold[3]; + info->gangPedThreshold[5] += info->gangPedThreshold[4]; + info->gangPedThreshold[6] += info->gangPedThreshold[5]; + info->gangPedThreshold[7] += info->gangPedThreshold[6]; + info->gangPedThreshold[8] += info->gangPedThreshold[7]; +} + +// unused +void +CTheZones::SetCarDensity(uint16 zoneid, uint8 day, uint16 cardensity) +{ + CZone *zone; + zone = GetInfoZone(zoneid); + ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight].carDensity = cardensity; +} + +// unused +void +CTheZones::SetPedDensity(uint16 zoneid, uint8 day, uint16 peddensity) +{ + CZone *zone; + zone = GetInfoZone(zoneid); + ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight].pedDensity = peddensity; +} + +void +CTheZones::SetPedGroup(uint16 zoneid, uint8 day, uint16 pedgroup) +{ + CZone *zone; + zone = GetInfoZone(zoneid); + ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight].pedGroup = pedgroup; +} + +int16 +CTheZones::FindAudioZone(CVector *pos) +{ + int i; + + for(i = 0; i < NumberOfAudioZones; i++) + if(PointLiesWithinZone(pos, GetAudioZone(i))) + return i; + return -1; +} + +void +CTheZones::AddZoneToAudioZoneArray(CZone *zone) +{ + int i, z; + + if(zone->type != ZONE_DEFAULT) + return; + + /* This is a bit stupid */ + z = -1; + for(i = 0; i < NUMNAVIGZONES; i++) + if(&NavigationZoneArray[i] == zone) + z = i; + assert(NumberOfAudioZones < NUMAUDIOZONES); + AudioZoneArray[NumberOfAudioZones++] = z; +} + +void +CTheZones::InitialiseAudioZoneArray(void) +{ + bool gonext; + CZone *zone; + + gonext = false; + zone = &NavigationZoneArray[0]; + // Go deep first, + // set gonext when backing up a level to visit the next child + while(zone) + if(gonext){ + AddZoneToAudioZoneArray(zone); + if(zone->next){ + gonext = false; + zone = zone->next; + }else + zone = zone->parent; + }else if(zone->child) + zone = zone->child; + else{ + AddZoneToAudioZoneArray(zone); + if(zone->next) + zone = zone->next; + else{ + gonext = true; + zone = zone->parent; + } + } +} + +void +CTheZones::SaveAllZones(uint8 *buffer, uint32 *size) +{ + INITSAVEBUF + int i; + +#define CZONE_SAVE_SIZE (sizeof(char)*8+sizeof(float)+sizeof(float)+sizeof(float)+sizeof(float)+sizeof(float)+sizeof(float)+sizeof(eZoneType)+sizeof(eLevelName)+sizeof(int16)+sizeof(int16)+sizeof(int32)+sizeof(int32)+sizeof(int32)) + + *size = SAVE_HEADER_SIZE + + sizeof(m_CurrLevel) + sizeof(FindIndex) + + sizeof(int16) // padding + + CZONE_SAVE_SIZE * ARRAY_SIZE(NavigationZoneArray) + CZONE_SAVE_SIZE * ARRAY_SIZE(InfoZoneArray) + sizeof(ZoneInfoArray) + + sizeof(TotalNumberOfNavigationZones) + sizeof(TotalNumberOfInfoZones) + sizeof(TotalNumberOfZoneInfos) + + sizeof(int16) // padding + + CZONE_SAVE_SIZE * ARRAY_SIZE(MapZoneArray) + sizeof(AudioZoneArray) + + sizeof(TotalNumberOfMapZones) + sizeof(NumberOfAudioZones); +#undef CZONE_SAVE_SIZE + + uint32 length = 0; + WriteSaveHeaderWithLength(buffer, length, 'Z', 'N', 'S', '\0', *size - SAVE_HEADER_SIZE); + + WriteSaveBuf(buffer, length, m_CurrLevel); + WriteSaveBuf(buffer, length, FindIndex); + WriteSaveBuf(buffer, length, (int16)0); // padding + + for(i = 0; i < ARRAY_SIZE(NavigationZoneArray); i++) + SaveOneZone(&NavigationZoneArray[i], &buffer, &length, ZONE_NAVIG); + + for(i = 0; i < ARRAY_SIZE(InfoZoneArray); i++) + SaveOneZone(&InfoZoneArray[i], &buffer, &length, ZONE_INFO); + + for(i = 0; i < ARRAY_SIZE(ZoneInfoArray); i++) + WriteSaveBuf(buffer, length, ZoneInfoArray[i]); + + WriteSaveBuf(buffer, length, TotalNumberOfNavigationZones); + WriteSaveBuf(buffer, length, TotalNumberOfInfoZones); + WriteSaveBuf(buffer, length, TotalNumberOfZoneInfos); + WriteSaveBuf(buffer, length, (int16)0); // padding + + for(i = 0; i < ARRAY_SIZE(MapZoneArray); i++) + SaveOneZone(&MapZoneArray[i], &buffer, &length, ZONE_MAPZONE); + + for(i = 0; i < ARRAY_SIZE(AudioZoneArray); i++) + WriteSaveBuf(buffer, length, AudioZoneArray[i]); + + WriteSaveBuf(buffer, length, TotalNumberOfMapZones); + WriteSaveBuf(buffer, length, NumberOfAudioZones); + + VALIDATESAVEBUF(*size) +} + +void +CTheZones::SaveOneZone(CZone *zone, uint8 **buffer, uint32 *length, eZoneType zoneType) +{ + WriteSaveBuf(*buffer, *length, *(uint32*)&zone->name[0]); + WriteSaveBuf(*buffer, *length, *(uint32*)&zone->name[4]); + + WriteSaveBuf(*buffer, *length, zone->minx); + WriteSaveBuf(*buffer, *length, zone->miny); + WriteSaveBuf(*buffer, *length, zone->minz); + WriteSaveBuf(*buffer, *length, zone->maxx); + WriteSaveBuf(*buffer, *length, zone->maxy); + WriteSaveBuf(*buffer, *length, zone->maxz); + + WriteSaveBuf(*buffer, *length, zone->type); + WriteSaveBuf(*buffer, *length, zone->level); + WriteSaveBuf(*buffer, *length, zone->zoneinfoDay); + WriteSaveBuf(*buffer, *length, zone->zoneinfoNight); + + int32 zoneId; + zoneId = GetIndexForNavigationZonePointer(zone->child); + WriteSaveBuf(*buffer, *length, zoneId); + zoneId = GetIndexForNavigationZonePointer(zone->parent); + WriteSaveBuf(*buffer, *length, zoneId); + zoneId = GetIndexForNavigationZonePointer(zone->next); + WriteSaveBuf(*buffer, *length, zoneId); +} + +void +CTheZones::LoadAllZones(uint8 *buffer, uint32 size) +{ + INITSAVEBUF + int i; + + uint32 length = 0; + CheckSaveHeaderWithLength(buffer, length, 'Z', 'N', 'S', '\0', size - SAVE_HEADER_SIZE); + + ReadSaveBuf(&m_CurrLevel, buffer); + ReadSaveBuf(&FindIndex, buffer); + SkipSaveBuf(buffer, 2); + + for(i = 0; i < ARRAY_SIZE(NavigationZoneArray); i++) + LoadOneZone(&NavigationZoneArray[i], &buffer, &length, ZONE_NAVIG); + + for (i = 0; i < ARRAY_SIZE(InfoZoneArray); i++) + LoadOneZone(&InfoZoneArray[i], &buffer, &length, ZONE_INFO); + + for(i = 0; i < ARRAY_SIZE(ZoneInfoArray); i++) + ReadSaveBuf(&ZoneInfoArray[i], buffer); + + ReadSaveBuf(&TotalNumberOfNavigationZones, buffer); + ReadSaveBuf(&TotalNumberOfInfoZones, buffer); + ReadSaveBuf(&TotalNumberOfZoneInfos, buffer); + SkipSaveBuf(buffer, 2); + + for(i = 0; i < ARRAY_SIZE(MapZoneArray); i++) + LoadOneZone(&MapZoneArray[i], &buffer, &length, ZONE_MAPZONE); + + for(i = 0; i < ARRAY_SIZE(AudioZoneArray); i++) + ReadSaveBuf(&AudioZoneArray[i], buffer); + + ReadSaveBuf(&TotalNumberOfMapZones, buffer); + ReadSaveBuf(&NumberOfAudioZones, buffer); + + VALIDATESAVEBUF(size) +} + +void +CTheZones::LoadOneZone(CZone *zone, uint8 **buffer, uint32 *length, eZoneType zoneType) +{ +#ifdef THIS_IS_STUPID + uint32 part1, part2; + ReadSaveBuf(&part1, *buffer, *length); + ReadSaveBuf(&part2, *buffer, *length); + + *(uint64 *)&zone->name[0] = (uint64)part2; + *(uint64 *)&zone->name[0] <<= 32; + *(uint64 *)&zone->name[0] |= (uint64)part1; +#else + for(int i = 0; i < sizeof(zone->name); i++) + ReadSaveBuf(&zone->name[i], *buffer, *length); +#endif + + ReadSaveBuf(&zone->minx, *buffer, *length); + ReadSaveBuf(&zone->miny, *buffer, *length); + ReadSaveBuf(&zone->minz, *buffer, *length); + ReadSaveBuf(&zone->maxx, *buffer, *length); + ReadSaveBuf(&zone->maxy, *buffer, *length); + ReadSaveBuf(&zone->maxz, *buffer, *length); + + ReadSaveBuf(&zone->type, *buffer, *length); + ReadSaveBuf(&zone->level, *buffer, *length); + ReadSaveBuf(&zone->zoneinfoDay, *buffer, *length); + ReadSaveBuf(&zone->zoneinfoNight, *buffer, *length); + + int32 zoneId; + ReadSaveBuf(&zoneId, *buffer, *length); + zone->child = GetPointerForNavigationZoneIndex(zoneId); + ReadSaveBuf(&zoneId, *buffer, *length); + zone->parent = GetPointerForNavigationZoneIndex(zoneId); + ReadSaveBuf(&zoneId, *buffer, *length); + zone->next = GetPointerForNavigationZoneIndex(zoneId); +} \ No newline at end of file diff --git a/src/miami/core/Zones.h b/src/miami/core/Zones.h new file mode 100644 index 00000000..2316eeef --- /dev/null +++ b/src/miami/core/Zones.h @@ -0,0 +1,114 @@ +#pragma once + +#include "Game.h" +#include "Gangs.h" +#include "CarCtrl.h" + +enum eZoneType +{ + ZONE_DEFAULT, + ZONE_NAVIG, + ZONE_INFO, + ZONE_MAPZONE, +}; + +class CZone +{ +public: + char name[8]; + float minx; + float miny; + float minz; + float maxx; + float maxy; + float maxz; + eZoneType type; + eLevelName level; + int16 zoneinfoDay; + int16 zoneinfoNight; + CZone *child; + CZone *parent; + CZone *next; + + wchar *GetTranslatedName(void); +}; + +class CZoneInfo +{ +public: + // Car data + int16 carDensity; + int16 carThreshold[CCarCtrl::NUM_CAR_CLASSES]; + int16 boatThreshold[CCarCtrl::NUM_BOAT_CLASSES]; + int16 gangThreshold[NUM_GANGS]; + int16 copThreshold; + + // Ped data + uint16 pedDensity; + uint16 gangPedThreshold[NUM_GANGS]; + uint16 copPedThreshold; + uint16 pedGroup; +}; + + +class CTheZones +{ + static int16 FindIndex; + + static uint16 NumberOfAudioZones; + static int16 AudioZoneArray[NUMAUDIOZONES]; + static uint16 TotalNumberOfMapZones; + static uint16 TotalNumberOfInfoZones; + static uint16 TotalNumberOfNavigationZones; + static CZone InfoZoneArray[NUMINFOZONES]; + static CZone MapZoneArray[NUMMAPZONES]; + static CZone NavigationZoneArray[NUMNAVIGZONES]; + static uint16 TotalNumberOfZoneInfos; + static CZoneInfo ZoneInfoArray[2*NUMINFOZONES]; +public: + static eLevelName m_CurrLevel; + + static void Init(void); + static void Update(void); + static void CreateZone(char *name, eZoneType type, + float minx, float miny, float minz, + float maxx, float maxy, float maxz, + eLevelName level); + static CZone *GetInfoZone(uint16 i) { return &InfoZoneArray[i]; } + static CZone *GetNavigationZone(uint16 i) { return &NavigationZoneArray[i]; } + static CZone *GetMapZone(uint16 i) { return &MapZoneArray[i]; } + static CZone *GetAudioZone(uint16 i) { return &NavigationZoneArray[AudioZoneArray[i]]; } + static void PostZoneCreation(void); + static void CheckZonesForOverlap(void); + static void InsertZoneIntoZoneHierarchy(CZone *zone); + static bool InsertZoneIntoZoneHierRecursive(CZone *z1, CZone *z2); + static bool ZoneIsEntirelyContainedWithinOtherZone(CZone *z1, CZone *z2); + static bool PointLiesWithinZone(const CVector *v, CZone *zone); + static eLevelName GetLevelFromPosition(const CVector *v); + static CZone *FindInformationZoneForPosition(const CVector *v); + static CZone *FindSmallestNavigationZoneForPosition(const CVector *v, bool findDefault, bool findNavig); + static int16 FindZoneByLabelAndReturnIndex(char *name, eZoneType type); + static int16 FindNextZoneByLabelAndReturnIndex(char *name, eZoneType type); + static CZoneInfo *GetZoneInfo(const CVector *v, uint8 day); + static void GetZoneInfoForTimeOfDay(const CVector *pos, CZoneInfo *info); + static void SetZoneCarInfo(uint16 zoneid, uint8 day, int16 carDensity, + int16 copCarDensity, const int16 *gangCarDensities /*[NUMGANGS]*/); + static void SetZoneCivilianCarInfo(uint16 zoneid, uint8 day, + const int16* carDensities, const int16* boatDensities); + static void SetZonePedInfo(uint16 zoneid, uint8 day, int16 pedDensity, + int16 gang0Density, int16 gang1Density, int16 gang2Density, int16 gang3Density, + int16 gang4Density, int16 gang5Density, int16 gang6Density, int16 gang7Density, + int16 gang8Density, int16 copDensity); + static void SetCarDensity(uint16 zoneid, uint8 day, uint16 cardensity); + static void SetPedDensity(uint16 zoneid, uint8 day, uint16 peddensity); + static void SetPedGroup(uint16 zoneid, uint8 day, uint16 pedgroup); + static int16 FindAudioZone(CVector *pos); + static CZone *GetPointerForNavigationZoneIndex(ssize_t i) { return i == -1 ? nil : &NavigationZoneArray[i]; } + static ssize_t GetIndexForNavigationZonePointer(CZone *zone) { return zone == nil ? -1 : zone - NavigationZoneArray; } + static void AddZoneToAudioZoneArray(CZone *zone); + static void InitialiseAudioZoneArray(void); + static void SaveAllZones(uint8 *buffer, uint32 *length); + static void SaveOneZone(CZone *zone, uint8 **buffer, uint32 *length, eZoneType zoneType); + static void LoadAllZones(uint8 *buffer, uint32 length); + static void LoadOneZone(CZone *zone, uint8 **buffer, uint32 *length, eZoneType zoneType); +}; diff --git a/src/miami/core/common.h b/src/miami/core/common.h new file mode 100644 index 00000000..63b63122 --- /dev/null +++ b/src/miami/core/common.h @@ -0,0 +1,420 @@ +#pragma once + +#define _CRT_SECURE_NO_WARNINGS +#define _USE_MATH_DEFINES +#pragma warning(disable: 4244) // int to float +#pragma warning(disable: 4800) // int to bool +#pragma warning(disable: 4838) // narrowing conversion +#pragma warning(disable: 4996) // POSIX names + +#ifdef __MWERKS__ +#define __STDC_LIMIT_MACROS // so we get UINT32_MAX etc +#endif + + +#ifdef __SWITCH__ +#include +#endif + +#include +#include +#include + +#ifdef __MWERKS__ +#define AUDIO_MSS +#define RWLIBS // codewarrior doesn't support project level defines - so not even this is enough, but still catches most ifdefs +#endif + +#if !defined RW_D3D9 && defined LIBRW +#undef WITHD3D +#undef WITHDINPUT +#endif + +#if (defined WITHD3D && !defined LIBRW) +#define WITHWINDOWS +#endif + +#if defined _WIN32 && defined WITHWINDOWS && !defined _INC_WINDOWS +#include +#endif + +#ifdef WITHD3D + #ifdef LIBRW + #define WITH_D3D // librw includes d3d9 itself via this right now + #else + #ifndef USE_D3D9 + #include + #else + #include + #endif + #endif +#endif + +#ifdef WITHDINPUT +#define DIRECTINPUT_VERSION 0x0800 +#include +#endif + +#include +#include + +// gotta put this somewhere +#ifdef LIBRW +#define STREAMPOS(str) ((str)->tell()) +#define STREAMFILE(str) (((rw::StreamFile*)(str))->file) +#define HIERNODEINFO(hier) ((hier)->nodeInfo) +#define HIERNODEID(hier, i) ((hier)->nodeInfo[i].id) +#define HANIMFRAME(anim, i) ((RwUInt8*)(anim)->keyframes + (i)*(anim)->interpInfo->animKeyFrameSize) +#else +#define RWHALFPIXEL // always d3d +#define STREAMPOS(str) ((str)->Type.memory.position) +#define STREAMFILE(str) ((str)->Type.file.fpFile) +#define HIERNODEINFO(hier) ((hier)->pNodeInfo) +#define HIERNODEID(hier, i) ((hier)->pNodeInfo[i].nodeID) +#define HANIMFRAME(anim, i) ((RwUInt8*)(anim)->pFrames + (i)*(anim)->interpInfo->keyFrameSize) +#define RpHAnimStdInterpFrame RpHAnimStdKeyFrame +#endif + +#ifdef RWHALFPIXEL +#define HALFPX (0.5f) +#else +#define HALFPX (0.0f) +#endif + +#define rwVENDORID_ROCKSTAR 0x0253F2 + +#define Max(a,b) ((a) > (b) ? (a) : (b)) +#define Min(a,b) ((a) < (b) ? (a) : (b)) + +// Use this to add const that wasn't there in the original code +#define Const const + +#ifndef RW_DC +typedef uint8_t uint8; +typedef int8_t int8; +typedef uint16_t uint16; +typedef int16_t int16; +#ifndef __MWERKS__ +typedef uint32_t uint32; +typedef int32_t int32; +#else +typedef unsigned int uint32; +typedef int int32; +#endif +typedef uintptr_t uintptr; +typedef intptr_t intptr; +typedef uint64_t uint64; +typedef int64_t int64; +#endif +#ifdef DC_SIM +#include "dc_hle_types.h" +#endif +typedef uintptr_t uintptr; +typedef intptr_t intptr; +// hardcode ucs-2 +typedef uint16_t wchar; + +typedef uint8 bool8; +typedef uint16 bool16; +typedef uint32 bool32; + +#if defined(_MSC_VER) || defined (__MWERKS__) +typedef ptrdiff_t ssize_t; +#endif + +#ifndef nil +#define nil NULL +#endif + +#include "config.h" + +#include +#include + +#ifdef __GNUC__ +#define TYPEALIGN(n) __attribute__ ((aligned (n))) +#else +#ifdef _MSC_VER +#define TYPEALIGN(n) __declspec(align(n)) +#else +#define TYPEALIGN(n) // unknown compiler...ignore +#endif +#endif + +#define ALIGNPTR(p) (void*)((((uintptr)(void*)p) + sizeof(void*)-1) & ~(sizeof(void*)-1)) + +// PDP-10 like byte functions +#define MASK(p, s) (((1<<(s))-1) << (p)) +inline uint32 dpb(uint32 b, uint32 p, uint32 s, uint32 w) +{ + uint32 m = MASK(p,s); + return (w & ~m) | ((b<>p & (1<r == right.r && this->g == right.g && this->b == right.b && this->a == right.a; + } + + bool operator !=(const CRGBA &right) + { + return !(*this == right); + } + + CRGBA &operator =(const CRGBA &right) + { + this->r = right.r; + this->g = right.g; + this->b = right.b; + this->a = right.a; + return *this; + } +#ifdef RWCORE_H + operator RwRGBA &(void) { + return rwRGBA; + } + + operator RwRGBA *(void) { + return &rwRGBA; + } + + operator RwRGBA (void) const { + return rwRGBA; + } + + CRGBA &operator =(const RwRGBA &right) + { + this->r = right.red; + this->g = right.green; + this->b = right.blue; + this->a = right.alpha; + return *this; + } +#endif +}; + +#if (defined(_MSC_VER)) +extern int strcasecmp(const char *str1, const char *str2); +extern int strncasecmp(const char *str1, const char *str2, size_t len); +#endif + +extern wchar *AllocUnicode(const char*src); + +#define Clamp(v, low, high) ((v)<(low) ? (low) : (v)>(high) ? (high) : (v)) + +#define Clamp2(v, center, radius) ((v) > (center) ? Min(v, center + radius) : Max(v, center - radius)) + +inline float sq(float x) { return x*x; } +#define SQR(x) ((x) * (x)) + +#ifdef __MWERKS__ +#define M_E 2.71828182845904523536 // e +#define M_LOG2E 1.44269504088896340736 // log2(e) +#define M_LOG10E 0.434294481903251827651 // log10(e) +#define M_LN2 0.693147180559945309417 // ln(2) +#define M_LN10 2.30258509299404568402 // ln(10) +#define M_PI 3.14159265358979323846 // pi +#define M_PI_2 1.57079632679489661923 // pi/2 +#define M_PI_4 0.785398163397448309616 // pi/4 +#define M_1_PI 0.318309886183790671538 // 1/pi +#define M_2_PI 0.636619772367581343076 // 2/pi +#define M_2_SQRTPI 1.12837916709551257390 // 2/sqrt(pi) +#define M_SQRT2 1.41421356237309504880 // sqrt(2) +#define M_SQRT1_2 0.707106781186547524401 // 1/sqrt(2) +#endif + +#define PI (float)M_PI +#define TWOPI (PI*2) +#define HALFPI (PI/2) +#define DEGTORAD(x) ((x) * PI / 180.0f) +#define RADTODEG(x) ((x) * 180.0f / PI) + +#ifdef USE_PS2_RAND +#define MYRAND_MAX 65535 +#else +#define MYRAND_MAX 32767 +#endif + +int myrand(void); +void mysrand(unsigned int seed); + +void re3_debug(const char *format, ...); +void re3_trace(const char *filename, unsigned int lineno, const char *func, const char *format, ...); +void re3_assert(const char *expr, const char *filename, unsigned int lineno, const char *func); +void re3_usererror(const char *format, ...); + +#define DEBUGBREAK() __debugbreak(); + +// Switch to enable development messages. +#if 1 +#define DEV(f, ...) +#else +#define DEV(f, ...) re3_debug("[DEV]: " f, ## __VA_ARGS__) +#endif + +#ifndef WITH_LOGGING +#define printf(...) +#define perror(...) +#define re3_debug(...) +#define re3_trace(...) +#define re3_usererror(...) +#endif + +#ifdef __MWERKS__ +void debug(char *f, ...); +void Error(char *f, ...); +__inline__ void TRACE(char *f, ...) { } // this is re3 only, and so the function needs to be inline - this way no call actually gets placed +// USERERROR only gets used in oal builds ... once +#else +#define debug(f, ...) re3_debug("[DBG]: " f, ## __VA_ARGS__) +#define Error(f, ...) re3_debug("[ERROR]: " f, ## __VA_ARGS__) +#ifndef MASTER +#define TRACE(f, ...) re3_trace(__FILE__, __LINE__, __FUNCTION__, f, ## __VA_ARGS__) +#define USERERROR(f, ...) re3_usererror(f, ## __VA_ARGS__) +#else +#define TRACE(f, ...) +#define USERERROR(f, ...) +#endif +#endif + +#ifndef MASTER +#define assert(_Expression) (void)( (!!(_Expression)) || (re3_assert(#_Expression, __FILE__, __LINE__, __FUNCTION__), 0) ) +#else +#define assert(_Expression) (_Expression) +#endif +#define ASSERT assert + +#ifdef __MWERKS__ +#define static_assert(bool_constexpr, message) +#endif + +#define _TODO(x) +#define _TODOCONST(x) (x) + +#ifdef CHECK_STRUCT_SIZES +template struct check_size { + static_assert(s == t, "Invalid structure size"); +}; +#define VALIDATE_SIZE(struc, size) check_size struc ## Check +#else +#define VALIDATE_SIZE(struc, size) +#endif +#define VALIDATE_OFFSET(struc, member, offset) static_assert(offsetof(struc, member) == offset, "The offset of " #member " in " #struc " is not " #offset "...") + +#define PERCENT(x, p) ((float(x) * (float(p) / 100.0f))) +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) +#ifdef BIT +#undef BIT +#endif +#define BIT(num) (1<<(num)) + +#define ABS(a) (((a) < 0) ? (-(a)) : (a)) +#define norm(value, min, max) (((value) < (min)) ? 0 : (((value) > (max)) ? 1 : (((value) - (min)) / ((max) - (min))))) +#define Lerp(norm, min, max) ( (norm) * ((max) - (min)) + (min) ) + +#define STRINGIFY(x) #x +#define STR(x) STRINGIFY(x) +#define CONCAT_(x,y) x##y +#define CONCAT(x,y) CONCAT_(x,y) + diff --git a/src/miami/core/config.h b/src/miami/core/config.h new file mode 100644 index 00000000..f3cf4853 --- /dev/null +++ b/src/miami/core/config.h @@ -0,0 +1,528 @@ +#pragma once + +// disables (most) stuff that wasn't in original gta-vc.exe +#ifdef __MWERKS__ +#define VANILLA_DEFINES +#endif + +enum Config { + NUMPLAYERS = 1, + +#if defined(DC_SQUEEZE_RAM) + NUMCDIMAGES = 1, // gta3.img duplicates (not used on PC) + MAX_CDIMAGES = 1, // additional cdimages + MAX_CDCHANNELS = 2, + + MODELINFOSIZE = 4900, // 4900 on PS2 +#else + NUMCDIMAGES = 6, // gta3.img duplicates (not used on PC) + MAX_CDIMAGES = 8, // additional cdimages + MAX_CDCHANNELS = 5, + + MODELINFOSIZE = 6500, // 4900 on PS2 +#endif + TXDSTORESIZE = 1385, + COLSTORESIZE = 31, + EXTRADIRSIZE = 256, + CUTSCENEDIRSIZE = 512, + + SIMPLEMODELSIZE = 3885, + TIMEMODELSIZE = 385, + CLUMPMODELSIZE = 5, + WEAPONMODELSIZE = 37, + PEDMODELSIZE = 130, + VEHICLEMODELSIZE = 110, + TWODFXSIZE = 1210, + +#if defined(DC_SQUEEZE_RAM) + MAXVEHICLESLOADED = 25, // 70 on mobile +#else + MAXVEHICLESLOADED = 50, // 70 on mobile +#endif + + NUMOBJECTINFO = 210, + + // Pool sizes + #if defined(DC_SQUEEZE_RAM) + NUMPTRNODES = 27500, + #elif defined(DC_ORIGINAL_RAM) + NUMPTRNODES = 50000, + #else + NUMPTRNODES = 40000, // This is the most important one to reduce + #endif + + #if defined(DC_SQUEEZE_RAM) + NUMENTRYINFOS = 3200, + NUMPEDS = 50, + NUMVEHICLES = 50, + #else + NUMENTRYINFOS = 3200, + NUMPEDS = 140, + NUMVEHICLES = 110, + #endif + NUMBUILDINGS = 7000, + NUMTREADABLES = 1, + NUMOBJECTS = 460, + NUMDUMMIES = 2340, + NUMAUDIOSCRIPTOBJECTS = 192, + NUMCOLMODELS = 4400, + NUMCUTSCENEOBJECTS = 50, // not a pool in VC + + NUMANIMBLOCKS = 35, + NUMANIMATIONS = 450, + + NUMTEMPOBJECTS = 40, + + // Path data + NUM_PATHNODES = 9650, + NUM_CARPATHLINKS = 3500, + NUM_MAPOBJECTS = 1250, + NUM_PATHCONNECTIONS = 20400, + + // Link list lengths + NUMALPHALIST = 20, + NUMBOATALPHALIST = 20, + NUMALPHAENTITYLIST = 200, + NUMALPHAUNTERWATERENTITYLIST = 30, + NUMCOLCACHELINKS = 50, + NUMREFERENCES = 800, + + // Zones + NUMAUDIOZONES = 14, + NUMINFOZONES = 169, + NUMMAPZONES = 39, + NUMNAVIGZONES = 20, + + // Cull zones + NUMATTRIBZONES = 704, + + NUMOCCLUSIONVOLUMES = 350, + NUMACTIVEOCCLUDERS = 48, + + PATHNODESIZE = 4500, + + NUMWEATHERS = 7, + NUMHOURS = 24, + + NUMEXTRADIRECTIONALS = 4, + NUMANTENNAS = 8, + NUMCORONAS = 56, + NUMPOINTLIGHTS = 32, + NUM3DMARKERS = 32, + NUMBRIGHTLIGHTS = 32, + NUMSHINYTEXTS = 32, + NUMMONEYMESSAGES = 16, + NUMPICKUPMESSAGES = 16, + NUMBULLETTRACES = 16, + NUMMBLURSTREAKS = 4, + NUMSKIDMARKS = 32, + + NUMONSCREENCLOCKS = 1, + NUMONSCREENCOUNTERS = 3, + NUMRADARBLIPS = 75, + NUMGENERALPICKUPS = 320, + NUMSCRIPTEDPICKUPS = 16, + NUMPICKUPS = NUMGENERALPICKUPS + NUMSCRIPTEDPICKUPS, + NUMCOLLECTEDPICKUPS = 20, + NUMPACMANPICKUPS = 256, + NUMEVENTS = 64, + + NUM_CARGENS = 185, + + NUM_PATH_NODES_IN_AUTOPILOT = 8, + + NUM_ACCIDENTS = 20, + NUM_FIRES = 40, + NUM_GARAGES = 32, + NUM_PROJECTILES = 32, + + NUM_GLASSPANES = 45, + NUM_GLASSENTITIES = 32, + NUM_WATERCANNONS = 3, + + NUMPEDROUTES = 200, + NUMPHONES = 50, + NUMPEDGROUPS = 67, + NUMMODELSPERPEDGROUP = 16, + MAXZONEPEDSLOADED = 8, + NUMSHOTINFOS = 100, + + NUMROADBLOCKS = 300, + NUM_SCRIPT_ROADBLOCKS = 16, + + NUMVISIBLEENTITIES = 2000, + NUMINVISIBLEENTITIES = 150, + + NUM_AUDIOENTITY_EVENTS = 4, + NUM_PED_COMMENTS_SLOTS = 20, + + NUM_SOUND_QUEUES = 2, + NUM_AUDIOENTITIES = 250, + + NUM_SCRIPT_MAX_ENTITIES = 40, + + NUM_GARAGE_STORED_CARS = 4, + + NUM_CRANES = 8, + NUM_ESCALATORS = 22, + NUM_WATER_CREATURES = 8, + + NUM_EXPLOSIONS = 48, + + NUM_SETPIECES = 96, + NUM_SHORTCUT_START_POINTS = 16 +}; + +// We don't expect to compile for PS2 or Xbox +// but it might be interesting for documentation purposes +#define GTA_PC +//#define GTA_PS2 +//#define GTA_XBOX + +// Version defines +#define GTAVC_PS2 400 +#define GTAVC_PC_10 410 +#define GTAVC_PC_11 411 +#define GTAVC_PC_JAP 412 +// TODO? maybe something for xbox or android? + +#define GTA_VERSION GTAVC_PC_11 + +// Enable configuration for handheld console ports +#if defined(__SWITCH__) || defined(PSP2) + #define GTA_HANDHELD +#endif + +// TODO(MIAMI): someone ought to find and check out uses of these defines: +//#define GTA3_STEAM_PATCH +//#define GTAVC_JP_PATCH + +#if defined GTA_PS2 +# define GTA_PS2_STUFF +# define RANDOMSPLASH +//# define USE_CUSTOM_ALLOCATOR +# define VU_COLLISION +# define PS2_MENU +#elif defined GTA_PC +# define EXTERNAL_3D_SOUND +# define AUDIO_REVERB +# ifndef GTA_HANDHELD +// # define PC_PLAYER_CONTROLS // mouse player/cam mode +# endif +//# define GTA_REPLAY +//# define GTA_SCENE_EDIT +# define PC_MENU +# define PC_WATER +# define DISABLE_WAVY_WATER +#elif defined GTA_XBOX +#elif defined GTA_MOBILE +# define MISSION_REPLAY +# define SIMPLER_MISSIONS +#endif + +// This is enabled for all released games. +// any debug stuff that isn't left in any game is not in FINAL +//#define FINAL + +// This is enabled for all released games except mobile +// any debug stuff that is only left in mobile, is not in MASTER +//#define MASTER + +// once and for all: +// pc: FINAL & MASTER +// mobile: FINAL + +// MASTER builds must be FINAL +#ifdef MASTER +#define FINAL +#endif + +// these are placed here to work with VANILLA_DEFINES for compatibility +#define NO_CDCHECK // skip audio CD check +// #define DEFAULT_NATIVE_RESOLUTION // Set default video mode to your native resolution (fixes Windows 10 launch) + +#ifdef VANILLA_DEFINES +#if !defined(_WIN32) || defined(__LP64__) || defined(_WIN64) +#error Vanilla can only be built for win-x86 +#endif + +#define FINAL +#define MASTER +//#define USE_MY_DOCUMENTS +#define THIS_IS_STUPID +#define DONT_FIX_REPLAY_BUGS +#define USE_TXD_CDIMAGE // generate and load textures from txd.img +//#define USE_TEXTURE_POOL // not possible because R* used custom RW33 +#define AUDIO_REFLECTIONS +#else +// This enables things from the PS2 version on PC +#define GTA_PS2_STUFF + +// quality of life fixes that should also be in FINAL +#define NASTY_GAME // nasty game for all languages + +// those infamous texts +#define DRAW_GAME_VERSION_TEXT +#ifdef DRAW_GAME_VERSION_TEXT + // unlike R* development builds, ours has runtime switch on debug menu & .ini, and disabled as default. + // If you disable this then game will fetch version from peds.col, as R* did while in development. + //#define USE_OUR_VERSIONING // enabled from buildfiles by default +#endif + +// Memory allocation and compression +// #define USE_CUSTOM_ALLOCATOR // use CMemoryHeap for allocation. use with care, not finished yet +#define COMPRESSED_COL_VECTORS // use compressed vectors for collision vertices +//#define ANIM_COMPRESSION // only keep most recently used anims uncompressed + +#if defined GTA_PC && defined GTA_PS2_STUFF +# define USE_PS2_RAND +# define RANDOMSPLASH // use random splash as on PS2 +# define PS2_MATFX +#endif + +#ifdef VU_COLLISION +#define COMPRESSED_COL_VECTORS // currently need compressed vectors in this code +#endif + +#ifdef MASTER + // only in master builds + #undef DRAW_GAME_VERSION_TEXT +#else + // not in master builds + #define VALIDATE_SAVE_SIZE + + #define DEBUGMENU +#endif + +#ifdef FINAL + // in all games +# define USE_MY_DOCUMENTS // use my documents directory for user files +#else + // not in any game +# define CHATTYSPLASH // print what the game is loading +# define TIMEBARS // print debug timers +#endif + +#define FIX_BUGS // fixes bugs that we've came across during reversing. You can undefine this only on release builds. +#define MORE_LANGUAGES // Add more translations to the game +#define COMPATIBLE_SAVES // this allows changing structs while keeping saves compatible, and keeps saves compatible between platforms +// #define FIX_INCOMPATIBLE_SAVES // try to fix incompatible saves, requires COMPATIBLE_SAVES +#define LOAD_INI_SETTINGS // as the name suggests. fundamental for CUSTOM_FRONTEND_OPTIONS + +#define NO_MOVIES // add option to disable intro videos + +#define EXTENDED_OFFSCREEN_DESPAWN_RANGE // Use onscreen despawn range for offscreen peds and vehicles to avoid them despawning in the distance when you look + // away + +#if defined(__LP64__) || defined(_WIN64) +#define FIX_BUGS_64 // Must have fixes to be able to run 64 bit build +#endif + +#define ASCII_STRCMP // use faster ascii str comparisons + +#if !defined _WIN32 || defined __MINGW32__ +#undef ASCII_STRCMP +#endif + +// Just debug menu entries +#ifdef DEBUGMENU +#define RELOADABLES // some debug menu options to reload TXD files +#define MISSION_SWITCHER // from debug menu +#endif + +// Rendering/display +#define ASPECT_RATIO_SCALE // Not just makes everything scale with aspect ratio, also adds support for all aspect ratios +#define PROPER_SCALING // use original DEFAULT_SCREEN_WIDTH/DEFAULT_SCREEN_HEIGHT from PS2 instead of PC(R* changed HEIGHT here to make radar look better, but broke other hud elements aspect ratio). +// #define DEFAULT_NATIVE_RESOLUTION // Set default video mode to your native resolution (fixes Windows 10 launch) +#define USE_TXD_CDIMAGE // generate and load textures from txd.img +#define PS2_ALPHA_TEST // emulate ps2 alpha test +// #define IMPROVED_VIDEOMODE // save and load videomode parameters instead of a magic number +//#define DISABLE_LOADING_SCREEN // disable the loading screen which vastly improves the loading time +#define DISABLE_VSYNC_ON_TEXTURE_CONVERSION // make texture conversion work faster by disabling vsync +#define ANISOTROPIC_FILTERING // set all textures to max anisotropic filtering +//#define USE_TEXTURE_POOL +#ifdef LIBRW +// #define EXTENDED_COLOURFILTER // more options for colour filter (replaces mblur) +// #define EXTENDED_PIPELINES // custom render pipelines (includes Neo) +// #define SCREEN_DROPLETS // neo water droplets +// #define NEW_RENDERER // leeds-like world rendering, needs librw +#endif + +#define FIX_SPRITES // fix sprites aspect ratio(moon, coronas, particle etc) + +#ifndef EXTENDED_COLOURFILTER +#undef SCREEN_DROPLETS // we need the backbuffer for this effect +#endif + +// Water & Particle +#undef PC_WATER +#define WATER_CHEATS + +//#define USE_CUTSCENE_SHADOW_FOR_PED // requires COMPATIBLE_SAVES +#define DISABLE_CUTSCENE_SHADOWS + +// Pad +#if !defined(RW_GL3) && defined(_WIN32) +#define XINPUT +#endif +#if defined XINPUT || (defined RW_GL3 && !defined LIBRW_SDL2 && !defined GTA_HANDHELD) +#define DETECT_JOYSTICK_MENU // Then we'll expect user to enter Controller->Detect joysticks if his joystick isn't detected at the start. +#endif +#define DETECT_PAD_INPUT_SWITCH // Adds automatic switch of pad related stuff between controller and kb/m +#define KANGAROO_CHEAT +#define RESTORE_ALLCARSHELI_CHEAT +#define BETTER_ALLCARSAREDODO_CHEAT +#define WALLCLIMB_CHEAT +#define REGISTER_START_BUTTON +#define BIND_VEHICLE_FIREWEAPON // Adds ability to rebind fire key for 'in vehicle' controls +#define BUTTON_ICONS // use textures to show controller buttons + +// Hud, frontend and radar +#define PC_MENU +#define FIX_RADAR // use radar size from early version before R* broke it +#define RADIO_OFF_TEXT // Won't work without FIX_BUGS + +#ifndef PC_MENU +# define PS2_MENU +//# define PS2_MENU_USEALLPAGEICONS +#else +# define MAP_ENHANCEMENTS // Adding waypoint and better mouse support +# if defined(XINPUT) || defined(GTA_HANDHELD) +# define GAMEPAD_MENU // Add gamepad menu +# endif +# define TRIANGLE_BACK_BUTTON +//# define CIRCLE_BACK_BUTTON +#define LEGACY_MENU_OPTIONS // i.e. frame sync(vsync) +#define MUCH_SHORTER_OUTRO_SCREEN +// #define XBOX_MESSAGE_SCREEN // Blue background, no "saved successfully press OK" screen etc. +# define CUSTOM_FRONTEND_OPTIONS + +# ifdef CUSTOM_FRONTEND_OPTIONS +# define GRAPHICS_MENU_OPTIONS // otherwise Display settings will be scrollable +# define NO_ISLAND_LOADING // disable loadscreen between islands via loading all island data at once, consumes more memory and CPU +# define CUTSCENE_BORDERS_SWITCH +# define MULTISAMPLING // adds MSAA option +# define INVERT_LOOK_FOR_PAD // enable the hidden option +# define PED_CAR_DENSITY_SLIDERS +# endif +#endif + +// Script +#define USE_DEBUG_SCRIPT_LOADER // Loads main.scm by default. Hold R for main_freeroam.scm and D for main_d.scm +#define USE_MEASUREMENTS_IN_METERS // makes game use meters instead of feet in script +#define USE_PRECISE_MEASUREMENT_CONVERTION // makes game convert feet to meeters more precisely +#define SUPPORT_JAPANESE_SCRIPT +//#define SUPPORT_XBOX_SCRIPT +#define SUPPORT_MOBILE_SCRIPT +#define SUPPORT_GINPUT_SCRIPT +#if (defined SUPPORT_XBOX_SCRIPT && defined SUPPORT_MOBILE_SCRIPT) +static_assert(false, "SUPPORT_XBOX_SCRIPT and SUPPORT_MOBILE_SCRIPT are mutually exclusive"); +#endif +#ifdef PC_MENU +#define MISSION_REPLAY // mobile feature +//#define SIMPLER_MISSIONS // apply simplifications from mobile +#define USE_MISSION_REPLAY_OVERRIDE_FOR_NON_MOBILE_SCRIPT +#endif +#define USE_ADVANCED_SCRIPT_DEBUG_OUTPUT +#define SCRIPT_LOG_FILE_LEVEL 0 // 0 == no log, 1 == overwrite every frame, 2 == full log + +#if SCRIPT_LOG_FILE_LEVEL == 0 +#undef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT +#endif + +#ifndef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT +#define USE_BASIC_SCRIPT_DEBUG_OUTPUT +#endif + +#ifdef MASTER +#undef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT +#undef USE_BASIC_SCRIPT_DEBUG_OUTPUT +#endif + +#ifndef MISSION_REPLAY +#undef USE_MISSION_REPLAY_OVERRIDE_FOR_NON_MOBILE_SCRIPT +#endif + +// Replay +//#define DONT_FIX_REPLAY_BUGS // keeps various bugs in CReplay, some of which are fairly cool! +//#define USE_BETA_REPLAY_MODE // adds another replay mode, a few seconds slomo (caution: buggy!) + +// Vehicles +#define EXPLODING_AIRTRAIN // can blow up jumbo jet with rocket launcher +#define CPLANE_ROTORS // make the rotors of the NPC police heli rotate + +// Pickups +//#define MONEY_MESSAGES +#define CAMERA_PICKUP + +// Peds +#define CANCELLABLE_CAR_ENTER + +// Camera +#define IMPROVED_CAMERA // Better Debug cam, and maybe more in the future +#define FREE_CAM // Rotating cam + +// Audio +#define EXTERNAL_3D_SOUND // use external engine to simulate 3d audio spatialization. OpenAL would not work without it (because it works in a 3d space + // originally and making it work in 2d only requires more resource). Will not work on PS2 +#define AUDIO_REFLECTIONS // Enable audio reflections. This is enabled in all vanilla versions +#define AUDIO_REVERB // Enable audio reverb. It was disabled in PS2 and mobile versions +#define RADIO_SCROLL_TO_PREV_STATION // Won't work without FIX_BUGS +#define AUDIO_CACHE // cache sound lengths to speed up the cold boot +#define PS2_AUDIO_CHANNELS // increases the maximum number of audio channels to PS2 value of 43 (PC has 28 originally) +// #define PS2_AUDIO_PATHS // changes audio paths for cutscenes and radio to PS2 paths (needs vbdec on MSS builds) +//#define AUDIO_OAL_USE_SNDFILE // use libsndfile to decode WAVs instead of our internal decoder +// #define AUDIO_OAL_USE_MPG123 // use mpg123 to support mp3 files +#define PAUSE_RADIO_IN_FRONTEND // pause radio when game is paused +#define ATTACH_RELEASING_SOUNDS_TO_ENTITIES // sounds would follow ped and vehicles coordinates if not being queued otherwise +#define USE_TIME_SCALE_FOR_AUDIO // slow down/speed up sounds according to the speed of the game +#define MULTITHREADED_AUDIO // for streams. requires C++11 or later + +#ifdef AUDIO_OPUS +#define AUDIO_OAL_USE_OPUS // enable support of opus files +//#define OPUS_AUDIO_PATHS // (not supported on VC yet) changes audio paths to opus paths (doesn't work if AUDIO_OAL_USE_OPUS isn't enabled) +//#define OPUS_SFX // enable if your sfx.raw is encoded with opus (doesn't work if AUDIO_OAL_USE_OPUS isn't enabled) + +#ifndef AUDIO_OAL_USE_OPUS +#undef OPUS_AUDIO_PATHS +#undef OPUS_SFX +#endif + +#endif + +// Streaming +#if !defined(_WIN32) && !defined(__SWITCH__) + //#define ONE_THREAD_PER_CHANNEL // Don't use if you're not on SSD/Flash - also not utilized too much right now(see commented LoadAllRequestedModels in Streaming.cpp) + // #define FLUSHABLE_STREAMING // Make it possible to interrupt reading when processing file isn't needed anymore. +#endif +#define BIG_IMG // Not complete - allows to read larger img files + +#define SQUEEZE_PERFORMANCE +#ifdef SQUEEZE_PERFORMANCE + #undef PS2_ALPHA_TEST + #undef NO_ISLAND_LOADING + // #undef PS2_AUDIO_CHANNELS + #undef EXTENDED_OFFSCREEN_DESPAWN_RANGE +#endif + +// if these defines are enabled saves are not vanilla compatible without COMPATIBLE_SAVES +#ifndef COMPATIBLE_SAVES +#undef USE_CUTSCENE_SHADOW_FOR_PED +#endif + +#ifdef GTA_HANDHELD + #define IGNORE_MOUSE_KEYBOARD // ignore mouse & keyboard input +#endif + +#ifdef __SWITCH__ + #define USE_UNNAMED_SEM // named semaphores are unsupported on the switch +#endif + +#endif // VANILLA_DEFINES + +#if defined(AUDIO_OAL) && !defined(EXTERNAL_3D_SOUND) +#error AUDIO_OAL cannot work without EXTERNAL_3D_SOUND +#endif +#if defined(GTA_PS2) && defined(EXTERNAL_3D_SOUND) +#error EXTERNAL_3D_SOUND cannot work on PS2 +#endif diff --git a/src/miami/core/main.cpp b/src/miami/core/main.cpp new file mode 100644 index 00000000..9d63f8ee --- /dev/null +++ b/src/miami/core/main.cpp @@ -0,0 +1,2447 @@ +#include "common.h" +#include +#include "rpmatfx.h" +#include "rphanim.h" +#include "rpskin.h" +#include "rtbmp.h" +#include "rtpng.h" +#ifdef ANISOTROPIC_FILTERING +#include "rpanisot.h" +#endif + +#include "main.h" +#include "CdStream.h" +#include "General.h" +#include "RwHelper.h" +#include "Clouds.h" +#include "Draw.h" +#include "Sprite2d.h" +#include "Renderer.h" +#include "Coronas.h" +#include "WaterLevel.h" +#include "Weather.h" +#include "Glass.h" +#include "WaterCannon.h" +#include "SpecialFX.h" +#include "Shadows.h" +#include "Skidmarks.h" +#include "Antennas.h" +#include "Rubbish.h" +#include "Particle.h" +#include "Pickups.h" +#include "WeaponEffects.h" +#include "PointLights.h" +#include "Fluff.h" +#include "Replay.h" +#include "Camera.h" +#include "World.h" +#include "Ped.h" +#include "Font.h" +#include "Pad.h" +#include "Hud.h" +#include "User.h" +#include "Messages.h" +#include "Darkel.h" +#include "Garages.h" +#include "MusicManager.h" +#include "VisibilityPlugins.h" +#include "NodeName.h" +#include "DMAudio.h" +#include "CutsceneMgr.h" +#include "Lights.h" +#include "Credits.h" +#include "ZoneCull.h" +#include "Timecycle.h" +#include "TxdStore.h" +#include "FileMgr.h" +#include "Text.h" +#include "RpAnimBlend.h" +#include "Frontend.h" +#include "AnimViewer.h" +#include "Script.h" +#include "PathFind.h" +#include "Debug.h" +#include "Console.h" +#include "timebars.h" +#include "GenericGameStorage.h" +#include "MemoryCard.h" +#include "MemoryHeap.h" +#include "SceneEdit.h" +#include "debugmenu.h" +#include "Clock.h" +#include "Occlusion.h" +#include "Ropes.h" +#include "postfx.h" +#include "custompipes.h" +#include "screendroplets.h" +#include "VarConsole.h" +#ifdef USE_OUR_VERSIONING +#include "GitSHA1.h" +#endif + +GlobalScene Scene; + +uint8 work_buff[55000]; +char gString[256]; +char gString2[512]; +wchar gUString[256]; +wchar gUString2[256]; + +float FramesPerSecond = 30.0f; + +bool gbPrintShite = false; +bool gbModelViewer; +#ifdef TIMEBARS +bool gbShowTimebars; +#endif +#ifdef DRAW_GAME_VERSION_TEXT +bool gbDrawVersionText; // Our addition, we think it was always enabled on !MASTER builds +#endif +#ifdef NO_MOVIES +bool gbNoMovies; +#endif + +volatile int32 frameCount; + +RwRGBA gColourTop; + +bool gameAlreadyInitialised; + +float NumberOfChunksLoaded; +#define TOTALNUMCHUNKS 95.0f + +bool g_SlowMode = false; +char version_name[64]; + + +void GameInit(void); +void SystemInit(void); +void TheGame(void); + +#ifdef DEBUGMENU +void DebugMenuPopulate(void); +#endif + +#ifndef FINAL +bool gbPrintMemoryUsage; +#endif + +#ifdef GTA_PS2 +#define WANT_TO_LOAD TheMemoryCard.m_bWantToLoad +#define FOUND_GAME_TO_LOAD TheMemoryCard.b_FoundRecentSavedGameWantToLoad +#else +#define WANT_TO_LOAD FrontEndMenuManager.m_bWantToLoad +#define FOUND_GAME_TO_LOAD b_FoundRecentSavedGameWantToLoad +#endif + +#ifdef NEW_RENDERER +bool gbNewRenderer; +#endif +#ifdef FIX_BUGS +// need to clear stencil for mblur fx. no idea why it works in the original game +// also for clearing out water rects in new renderer +#define CLEARMODE (rwCAMERACLEARZ | rwCAMERACLEARSTENCIL) +#else +#define CLEARMODE (rwCAMERACLEARZ) +#endif + +bool bDisplayNumOfAtomicsRendered = false; +bool bDisplayPosn = false; + +#ifdef __MWERKS__ +void +debug(char *fmt, ...) +{ +#ifndef MASTER + // TODO put something here +#endif +} + +void +Error(char *fmt, ...) +{ +#ifndef MASTER + // TODO put something here +#endif +} +#endif + +void +ValidateVersion() +{ + // int32 file = CFileMgr::OpenFile("models\\coll\\peds.col", "rb"); + // char buff[128]; + + // if ( file != -1 ) + // { + // CFileMgr::Seek(file, 100, SEEK_SET); + + // for ( int i = 0; i < 128; i++ ) + // { + // CFileMgr::Read(file, &buff[i], sizeof(char)); + // buff[i] -= 23; + // if ( buff[i] == '\0' ) + // break; + // CFileMgr::Seek(file, 99, SEEK_CUR); + // } + + // if ( !strncmp(buff, "grandtheftauto3", 15) ) + // { + // strncpy(version_name, &buff[15], 64); + // CFileMgr::CloseFile(file); + // return; + // } + // } + + // LoadingScreen("Invalid version", NULL, NULL); + + // while(true) + // { + // ; + // } +} + +bool +DoRWStuffStartOfFrame(int16 TopRed, int16 TopGreen, int16 TopBlue, int16 BottomRed, int16 BottomGreen, int16 BottomBlue, int16 Alpha) +{ + CRGBA TopColor(TopRed, TopGreen, TopBlue, Alpha); + CRGBA BottomColor(BottomRed, BottomGreen, BottomBlue, Alpha); + + CDraw::CalculateAspectRatio(); + CameraSize(Scene.camera, nil, SCREEN_VIEWWINDOW, SCREEN_ASPECT_RATIO); + CVisibilityPlugins::SetRenderWareCamera(Scene.camera); + RwCameraClear(Scene.camera, &TopColor.rwRGBA, CLEARMODE); + + if(!RsCameraBeginUpdate(Scene.camera)) + return false; + + CSprite2d::InitPerFrame(); + + if(Alpha != 0) + CSprite2d::DrawRect(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), BottomColor, BottomColor, TopColor, TopColor); + + return true; +} + +bool +DoRWStuffStartOfFrame_Horizon(int16 TopRed, int16 TopGreen, int16 TopBlue, int16 BottomRed, int16 BottomGreen, int16 BottomBlue, int16 Alpha) +{ + CDraw::CalculateAspectRatio(); + CameraSize(Scene.camera, nil, SCREEN_VIEWWINDOW, SCREEN_ASPECT_RATIO); + CVisibilityPlugins::SetRenderWareCamera(Scene.camera); + RwCameraClear(Scene.camera, &gColourTop, CLEARMODE); + + if(!RsCameraBeginUpdate(Scene.camera)) + return false; + + TheCamera.m_viewMatrix.Update(); + CClouds::RenderBackground(TopRed, TopGreen, TopBlue, BottomRed, BottomGreen, BottomBlue, Alpha); + + return true; +} + +// This is certainly a very useful function +void +DoRWRenderHorizon(void) +{ + CClouds::RenderHorizon(); +} + +void +DoFade(void) +{ + if(CTimer::GetIsPaused()) + return; + +#ifdef PS2_MENU + if(TheMemoryCard.JustLoadedDontFadeInYet){ + TheMemoryCard.JustLoadedDontFadeInYet = false; + TheMemoryCard.TimeStartedCountingForFade = CTimer::GetTimeInMilliseconds(); + } +#else + if(JustLoadedDontFadeInYet){ + JustLoadedDontFadeInYet = false; + TimeStartedCountingForFade = CTimer::GetTimeInMilliseconds(); + } +#endif + +#ifdef PS2_MENU + if(TheMemoryCard.StillToFadeOut){ + if(CTimer::GetTimeInMilliseconds() - TheMemoryCard.TimeStartedCountingForFade > TheMemoryCard.TimeToStayFadedBeforeFadeOut){ + TheMemoryCard.StillToFadeOut = false; +#else + if(StillToFadeOut){ + if(CTimer::GetTimeInMilliseconds() - TimeStartedCountingForFade > TimeToStayFadedBeforeFadeOut){ + StillToFadeOut = false; +#endif + TheCamera.Fade(3.0f, FADE_IN); + TheCamera.ProcessFade(); + TheCamera.ProcessMusicFade(); + }else{ + TheCamera.SetFadeColour(0, 0, 0); + TheCamera.Fade(0.0f, FADE_OUT); + TheCamera.ProcessFade(); + } + } + + if(CDraw::FadeValue != 0 || FrontEndMenuManager.m_PrefsBrightness < 256){ + CSprite2d *splash = LoadSplash(nil); + + CRGBA fadeColor; + CRect rect; + int fadeValue = CDraw::FadeValue; + float brightness = Min(FrontEndMenuManager.m_PrefsBrightness, 256); + if(brightness <= 50) + brightness = 50; + if(FrontEndMenuManager.m_bMenuActive) + brightness = 256; + + if(TheCamera.m_FadeTargetIsSplashScreen) + fadeValue = 0; + + float fade = fadeValue + 256 - brightness; + if(fade == 0){ + fadeColor.r = 0; + fadeColor.g = 0; + fadeColor.b = 0; + fadeColor.a = 0; + }else{ + fadeColor.r = fadeValue * CDraw::FadeRed / fade; + fadeColor.g = fadeValue * CDraw::FadeGreen / fade; + fadeColor.b = fadeValue * CDraw::FadeBlue / fade; + int alpha = 255 - brightness*(256 - fadeValue)/256; + if(alpha < 0) + alpha = 0; + fadeColor.a = alpha; + } + + TheCamera.GetScreenRect(rect); + CSprite2d::DrawRect(rect, fadeColor); + + if(CDraw::FadeValue != 0 && TheCamera.m_FadeTargetIsSplashScreen){ + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); + fadeColor.r = 255; + fadeColor.g = 255; + fadeColor.b = 255; + fadeColor.a = CDraw::FadeValue; + splash->Draw(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), fadeColor, fadeColor, fadeColor, fadeColor); + } + } +} + +#if 0 +bool +RwGrabScreen(RwCamera *camera, RwChar *filename) +{ + char temp[255]; + RwImage *pImage = RsGrabScreen(camera); + bool result = true; + + if (pImage == nil) + return false; + + strcpy(temp, CFileMgr::GetRootDirName()); + strcat(temp, filename); + +#ifndef LIBRW + if (RtBMPImageWrite(pImage, &temp[0]) == nil) +#else + if (RtPNGImageWrite(pImage, &temp[0]) == nil) +#endif + result = false; + RwImageDestroy(pImage); + return result; +} +#endif + +#define TILE_WIDTH 576 +#define TILE_HEIGHT 432 + +void +DoRWStuffEndOfFrame(void) +{ + CDebug::DisplayScreenStrings(); // custom + CDebug::DebugDisplayTextBuffer(); + FlushObrsPrintfs(); + RwCameraEndUpdate(Scene.camera); + RsCameraShowRaster(Scene.camera); +#ifndef MASTER + char s[48]; +#ifdef THIS_IS_STUPID + if (CPad::GetPad(1)->GetLeftShockJustDown()) { + // try using both controllers for this thing... crazy bastards + if (CPad::GetPad(0)->GetRightStickY() > 0) { + sprintf(s, "screen%d%d.ras", CClock::ms_nGameClockHours, CClock::ms_nGameClockMinutes); + // TODO + //RtTileRender(Scene.camera, TILE_WIDTH * 2, TILE_HEIGHT * 2, TILE_WIDTH, TILE_HEIGHT, &NewTileRendererCB, nil, s); + } else { + sprintf(s, "screen%d%d.bmp", CClock::ms_nGameClockHours, CClock::ms_nGameClockMinutes); + RwGrabScreen(Scene.camera, s); + } + } +#else +#if 0 + if (CPad::GetPad(1)->GetLeftShockJustDown() || CPad::GetPad(0)->GetFJustDown(11)) { + sprintf(s, "screen_%011lld.png", time(nil)); + RwGrabScreen(Scene.camera, s); + } +#endif +#endif +#endif // !MASTER +} + +static RwBool +PluginAttach(void) +{ + if( !RpWorldPluginAttach() ) + { + printf("Couldn't attach world plugin\n"); + + return FALSE; + } + + if( !RpSkinPluginAttach() ) + { + printf("Couldn't attach RpSkin plugin\n"); + + return FALSE; + } +#ifndef LIBRW + if (!RtAnimInitialize()) + { + return FALSE; + } +#endif + if( !RpHAnimPluginAttach() ) + { + printf("Couldn't attach RpHAnim plugin\n"); + + return FALSE; + } + + if( !NodeNamePluginAttach() ) + { + printf("Couldn't attach node name plugin\n"); + + return FALSE; + } + + if( !CVisibilityPlugins::PluginAttach() ) + { + printf("Couldn't attach visibility plugins\n"); + + return FALSE; + } + + if( !RpAnimBlendPluginAttach() ) + { + printf("Couldn't attach RpAnimBlend plugin\n"); + + return FALSE; + } + + if( !RpMatFXPluginAttach() ) + { + printf("Couldn't attach RpMatFX plugin\n"); + + return FALSE; + } +#ifdef ANISOTROPIC_FILTERING + RpAnisotPluginAttach(); +#endif +#ifdef EXTENDED_PIPELINES + CustomPipes::CustomPipeRegister(); +#endif + + return TRUE; +} + +#ifdef GTA_PS2 +#define NUM_PREALLOC_ATOMICS 1800 +#define NUM_PREALLOC_CLUMPS 80 +#define NUM_PREALLOC_FRAMES 2600 +#define NUM_PREALLOC_GEOMETRIES 850 +#define NUM_PREALLOC_TEXDICTS 121 +#define NUM_PREALLOC_TEXTURES 1700 +#define NUM_PREALLOC_MATERIALS 2600 +bool preAlloc; + +void +PreAllocateRwObjects(void) +{ + int i; + + PUSH_MEMID(MEMID_PRE_ALLOC); + void **tmp = new void*[0x8000]; + preAlloc = true; + + for(i = 0; i < NUM_PREALLOC_ATOMICS; i++) + tmp[i] = RpAtomicCreate(); + for(i = 0; i < NUM_PREALLOC_ATOMICS; i++) + RpAtomicDestroy((RpAtomic*)tmp[i]); + + for(i = 0; i < NUM_PREALLOC_CLUMPS; i++) + tmp[i] = RpClumpCreate(); + for(i = 0; i < NUM_PREALLOC_CLUMPS; i++) + RpClumpDestroy((RpClump*)tmp[i]); + + for(i = 0; i < NUM_PREALLOC_FRAMES; i++) + tmp[i] = RwFrameCreate(); + for(i = 0; i < NUM_PREALLOC_FRAMES; i++) + RwFrameDestroy((RwFrame*)tmp[i]); + + for(i = 0; i < NUM_PREALLOC_GEOMETRIES; i++) + tmp[i] = RpGeometryCreate(0, 0, 0); + for(i = 0; i < NUM_PREALLOC_GEOMETRIES; i++) + RpGeometryDestroy((RpGeometry*)tmp[i]); + + for(i = 0; i < NUM_PREALLOC_TEXDICTS; i++) + tmp[i] = RwTexDictionaryCreate(); + for(i = 0; i < NUM_PREALLOC_TEXDICTS; i++) + RwTexDictionaryDestroy((RwTexDictionary*)tmp[i]); + + for(i = 0; i < NUM_PREALLOC_TEXTURES; i++) + tmp[i] = RwTextureCreate(RwRasterCreate(0, 0, 0, 0)); + for(i = 0; i < NUM_PREALLOC_TEXDICTS; i++) + RwTextureDestroy((RwTexture*)tmp[i]); + + for(i = 0; i < NUM_PREALLOC_MATERIALS; i++) + tmp[i] = RpMaterialCreate(); + for(i = 0; i < NUM_PREALLOC_MATERIALS; i++) + RpMaterialDestroy((RpMaterial*)tmp[i]); + + delete[] tmp; + preAlloc = false; + POP_MEMID(); +} +#endif + +static RwBool +Initialise3D(void *param) +{ + PUSH_MEMID(MEMID_RENDER); + +#ifndef MASTER + VarConsole.Add("Display number of atomics rendered", &bDisplayNumOfAtomicsRendered, true); + VarConsole.Add("Display posn and framerate", &bDisplayPosn, true); +#endif + + if (RsRwInitialize(param)) + { + POP_MEMID(); + +#ifdef DEBUGMENU + DebugMenuInit(); + DebugMenuPopulate(); +#endif // !DEBUGMENU + return CGame::InitialiseRenderWare(); + } + POP_MEMID(); + + return (FALSE); +} + +static void +Terminate3D(void) +{ + CGame::ShutdownRenderWare(); +#ifdef DEBUGMENU + DebugMenuShutdown(); +#endif // !DEBUGMENU + + RsRwTerminate(); + + return; +} + +CSprite2d splash; +int splashTxdId = -1; + +CSprite2d* +LoadSplash(const char *name) +{ + RwTexDictionary *txd; + char filename[140]; + RwTexture *tex = nil; + + if(name == nil) + return &splash; + if(splashTxdId == -1) + splashTxdId = CTxdStore::AddTxdSlot("splash"); + + txd = CTxdStore::GetSlot(splashTxdId)->texDict; + if(txd) + tex = RwTexDictionaryFindNamedTexture(txd, name); + // if texture is found, splash was already set up below + + if(tex == nil){ + CFileMgr::SetDir("TXD\\"); + sprintf(filename, "%s.txd", name); + if(splash.m_pTexture) + splash.Delete(); + if(txd) + CTxdStore::RemoveTxd(splashTxdId); + CTxdStore::LoadTxd(splashTxdId, filename); + CTxdStore::AddRef(splashTxdId); + CTxdStore::PushCurrentTxd(); + CTxdStore::SetCurrentTxd(splashTxdId); + splash.SetTexture(name); + CTxdStore::PopCurrentTxd(); + CFileMgr::SetDir(""); + } + + return &splash; +} + +void +DestroySplashScreen(void) +{ + splash.Delete(); + if(splashTxdId != -1) + CTxdStore::RemoveTxdSlot(splashTxdId); + splashTxdId = -1; +} + +Const char* +GetRandomSplashScreen(void) +{ + int index; + static int index2 = 0; + static char splashName[128]; + static int splashIndex[12] = { + 1, 2, + 3, 4, + 5, 11, + 6, 8, + 9, 10, + 7, 12 + }; + + index = splashIndex[2*index2 + CGeneral::GetRandomNumberInRange(0, 2)]; + index2++; + if(index2 == 6) + index2 = 0; + sprintf(splashName, "loadsc%d", index); + return splashName; +} + +Const char* +GetLevelSplashScreen(int level) +{ + static Const char *splashScreens[4] = { + nil, + "splash1", + "splash2", + "splash3", + }; + + return splashScreens[level]; +} + +void +ResetLoadingScreenBar() +{ + NumberOfChunksLoaded = 0.0f; +} + +void +LoadingScreen(const char *str1, const char *str2, const char *splashscreen) +{ + CSprite2d *splash; + +#ifdef DISABLE_LOADING_SCREEN + if (str1 && str2) + return; +#endif + +#ifndef RANDOMSPLASH + splashscreen = "LOADSC0"; +#endif + + splash = LoadSplash(splashscreen); + +#ifndef GTA_PS2 + if(RsGlobal.quit) + return; +#endif + + if(DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255)){ + CSprite2d::SetRecipNearClip(); + CSprite2d::InitPerFrame(); + CFont::InitPerFrame(); + DefinedState(); + RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP); + splash->Draw(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), CRGBA(255, 255, 255, 255)); + + if(str1){ + NumberOfChunksLoaded += 1; + +#ifndef RANDOMSPLASH + float hpos = SCREEN_SCALE_X(40); + float length = SCREEN_WIDTH - SCREEN_SCALE_X(80); + float top = SCREEN_HEIGHT - SCREEN_SCALE_Y(14); + float bottom = top + SCREEN_SCALE_Y(5); +#else + float hpos = SCREEN_STRETCH_X(40); + float length = SCREEN_STRETCH_X(440); + // this is rather weird + float top = SCREEN_STRETCH_Y(407.4f - 7.0f/3.0f); + float bottom = SCREEN_STRETCH_Y(407.4f + 7.0f/3.0f); +#endif + + CSprite2d::DrawRect(CRect(hpos-1.0f, top-1.0f, hpos+length+1.0f, bottom+1.0f), CRGBA(40, 53, 68, 255)); + + CSprite2d::DrawRect(CRect(hpos, top, hpos+length, bottom), CRGBA(155, 50, 125, 255)); + + length *= NumberOfChunksLoaded/TOTALNUMCHUNKS; + CSprite2d::DrawRect(CRect(hpos, top, hpos+length, bottom), CRGBA(255, 150, 225, 255)); + + // this is done by the game but is unused + CFont::SetBackgroundOff(); + CFont::SetScale(SCREEN_SCALE_X(2), SCREEN_SCALE_Y(2)); + CFont::SetPropOn(); + CFont::SetRightJustifyOn(); + CFont::SetDropShadowPosition(1); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + CFont::SetFontStyle(FONT_HEADING); + +#ifdef CHATTYSPLASH + // my attempt + static wchar tmpstr[80]; + float yscale = SCREEN_SCALE_Y(0.9f); + top -= 45*yscale; + CFont::SetScale(SCREEN_SCALE_X(0.75f), yscale); + CFont::SetPropOn(); + CFont::SetRightJustifyOff(); + CFont::SetFontStyle(FONT_STANDARD); + CFont::SetColor(CRGBA(255, 255, 255, 255)); + AsciiToUnicode(str1, tmpstr); + CFont::PrintString(hpos, top, tmpstr); + top += 22*yscale; + if (str2) { + AsciiToUnicode(str2, tmpstr); + CFont::PrintString(hpos, top, tmpstr); + } +#endif + } + + CFont::DrawFonts(); + DoRWStuffEndOfFrame(); + } +} + +void +LoadingIslandScreen(const char *levelName) +{ + CSprite2d *splash; + + splash = LoadSplash(nil); + if(!DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255)) + return; + + CSprite2d::SetRecipNearClip(); + CSprite2d::InitPerFrame(); + CFont::InitPerFrame(); + DefinedState(); + CRGBA col = CRGBA(255, 255, 255, 255); + CRGBA col2 = CRGBA(0, 0, 0, 255); + CSprite2d::DrawRect(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), col2); + splash->Draw(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), col, col, col, col); + CFont::DrawFonts(); + DoRWStuffEndOfFrame(); +} + +void +ProcessSlowMode(void) +{ + int16 lX = CPad::GetPad(0)->NewState.LeftStickX; + int16 lY = CPad::GetPad(0)->NewState.LeftStickY; + int16 rX = CPad::GetPad(0)->NewState.RightStickX; + int16 rY = CPad::GetPad(0)->NewState.RightStickY; + int16 L1 = CPad::GetPad(0)->NewState.LeftShoulder1; + int16 L2 = CPad::GetPad(0)->NewState.LeftShoulder2; + int16 R1 = CPad::GetPad(0)->NewState.RightShoulder1; + int16 R2 = CPad::GetPad(0)->NewState.RightShoulder2; + int16 up = CPad::GetPad(0)->NewState.DPadUp; + int16 down = CPad::GetPad(0)->NewState.DPadDown; + int16 left = CPad::GetPad(0)->NewState.DPadLeft; + int16 right = CPad::GetPad(0)->NewState.DPadRight; + int16 start = CPad::GetPad(0)->NewState.Start; + int16 select = CPad::GetPad(0)->NewState.Select; + int16 square = CPad::GetPad(0)->NewState.Square; + int16 triangle = CPad::GetPad(0)->NewState.Triangle; + int16 cross = CPad::GetPad(0)->NewState.Cross; + int16 circle = CPad::GetPad(0)->NewState.Circle; + int16 L3 = CPad::GetPad(0)->NewState.LeftShock; + int16 R3 = CPad::GetPad(0)->NewState.RightShock; + int16 networktalk = CPad::GetPad(0)->NewState.NetworkTalk; + int16 stop = true; + + do + { + if ( CPad::GetPad(1)->GetLeftShoulder1JustDown() || CPad::GetPad(1)->GetRightShoulder1() ) + break; + + if ( stop ) + { + CTimer::Stop(); + stop = false; + } + + CPad::UpdatePads(); + + RwCameraBeginUpdate(Scene.camera); + RwCameraEndUpdate(Scene.camera); + + } while (!CPad::GetPad(1)->GetLeftShoulder1JustDown() && !CPad::GetPad(1)->GetRightShoulder1()); + + + CPad::GetPad(0)->OldState.LeftStickX = lX; + CPad::GetPad(0)->OldState.LeftStickY = lY; + CPad::GetPad(0)->OldState.RightStickX = rX; + CPad::GetPad(0)->OldState.RightStickY = rY; + CPad::GetPad(0)->OldState.LeftShoulder1 = L1; + CPad::GetPad(0)->OldState.LeftShoulder2 = L2; + CPad::GetPad(0)->OldState.RightShoulder1 = R1; + CPad::GetPad(0)->OldState.RightShoulder2 = R2; + CPad::GetPad(0)->OldState.DPadUp = up; + CPad::GetPad(0)->OldState.DPadDown = down; + CPad::GetPad(0)->OldState.DPadLeft = left; + CPad::GetPad(0)->OldState.DPadRight = right; + CPad::GetPad(0)->OldState.Start = start; + CPad::GetPad(0)->OldState.Select = select; + CPad::GetPad(0)->OldState.Square = square; + CPad::GetPad(0)->OldState.Triangle = triangle; + CPad::GetPad(0)->OldState.Cross = cross; + CPad::GetPad(0)->OldState.Circle = circle; + CPad::GetPad(0)->OldState.LeftShock = L3; + CPad::GetPad(0)->OldState.RightShock = R3; + CPad::GetPad(0)->OldState.NetworkTalk = networktalk; + CPad::GetPad(0)->NewState.LeftStickX = lX; + CPad::GetPad(0)->NewState.LeftStickY = lY; + CPad::GetPad(0)->NewState.RightStickX = rX; + CPad::GetPad(0)->NewState.RightStickY = rY; + CPad::GetPad(0)->NewState.LeftShoulder1 = L1; + CPad::GetPad(0)->NewState.LeftShoulder2 = L2; + CPad::GetPad(0)->NewState.RightShoulder1 = R1; + CPad::GetPad(0)->NewState.RightShoulder2 = R2; + CPad::GetPad(0)->NewState.DPadUp = up; + CPad::GetPad(0)->NewState.DPadDown = down; + CPad::GetPad(0)->NewState.DPadLeft = left; + CPad::GetPad(0)->NewState.DPadRight = right; + CPad::GetPad(0)->NewState.Start = start; + CPad::GetPad(0)->NewState.Select = select; + CPad::GetPad(0)->NewState.Square = square; + CPad::GetPad(0)->NewState.Triangle = triangle; + CPad::GetPad(0)->NewState.Cross = cross; + CPad::GetPad(0)->NewState.Circle = circle; + CPad::GetPad(0)->NewState.LeftShock = L3; + CPad::GetPad(0)->NewState.RightShock = R3; + CPad::GetPad(0)->NewState.NetworkTalk = networktalk; +} + + +float FramesPerSecondCounter; +int32 FrameSamples; + +#ifndef MASTER +struct tZonePrint +{ + char name[11]; + char area[5]; + CRect rect; +}; + +tZonePrint ZonePrint[] = +{ + { "DOWNTOWN", "GM", CRect(-1500.0f, 1500.0f, -300.0f, 980.0f)}, + { "DOWNTOWS", "KB", CRect(-1200.0f, 980.0f, -300.0f, 435.0f)}, + { "GOLF", "NT", CRect(-300.0f, 660.0f, 320.0f, -255.0f)}, + { "LITTLEHA", "AG", CRect(-1250.0f, -310.0f, -746.0f, -926.0f)}, + { "HAITI", "CJ", CRect(-1355.0f, 30.0f, -637.0f, -304.0f)}, + { "HAITIN", "SM", CRect(-1355.0f, 435.0f, -637.0f, 30.0f)}, + { "DOCKS", "AW", CRect(-1122.0f, -926.0f, -609.0f, -1575.0f)}, + { "AIRPORT", "NT", CRect(-2000.0f, 200.0f, -871.0f, -2000.0f)}, + { "STARISL", "CJ", CRect(-724.0f, -320.0f, -40.0f, -380.0f)}, + { "CENT.ISLA", "NT", CRect(-163.0f, 1260.0f, 120.0f, 830.0f)}, + { "MALL", "AW", CRect( 300.0f, 1266.0f, 483.0f, 995.0f)}, + { "MANSION", "KB", CRect(-724.0f, -500.0f, -40.0f, -670.0f)}, + { "NBEACH", "AS", CRect( 120.0f, 1340.0f, 900.0f, 600.0f)}, + { "NBEACHBT", "AS", CRect( 200.0f, 680.0f, 660.0f, -50.0f)}, + { "NBEACHW", "AS", CRect(-93.0f, 80.0f, 410.0f, -680.0f)}, + { "OCEANDRV", "AC", CRect( 200.0f, -964.0f, 955.0f, -1797.0f)}, + { "OCEANDN", "WS", CRect( 400.0f, 50.0f, 955.0f, -964.0f)}, + { "WASHINGTN", "AC", CRect(-320.0f, -487.0f, 500.0f, -1200.0f)}, + { "WASHINBTM", "AC", CRect(-255.0f, -1200.0f, 500.0f, -1690.0f)} +}; + +void +PrintMemoryUsage(void) +{ +// little hack +if(CPools::GetPtrNodePool() == nil) +return; + + // Style taken from LCS, modified for III +// CFont::SetFontStyle(FONT_PAGER); + CFont::SetFontStyle(FONT_BANK); + CFont::SetBackgroundOff(); + CFont::SetWrapx(640.0f); +// CFont::SetScale(0.5f, 0.75f); + CFont::SetScale(0.4f, 0.75f); + CFont::SetCentreOff(); + CFont::SetCentreSize(640.0f); + CFont::SetJustifyOff(); + CFont::SetPropOn(); + CFont::SetColor(CRGBA(200, 200, 200, 200)); + CFont::SetBackGroundOnlyTextOff(); + CFont::SetDropShadowPosition(0); + + float y; + +#ifdef USE_CUSTOM_ALLOCATOR + y = 24.0f; + sprintf(gString, "Total: %d blocks, %d bytes", gMainHeap.m_totalBlocksUsed, gMainHeap.m_totalMemUsed); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Game: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_GAME), gMainHeap.GetMemoryUsed(MEMID_GAME)); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "World: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_WORLD), gMainHeap.GetMemoryUsed(MEMID_WORLD)); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Render: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_RENDER), gMainHeap.GetMemoryUsed(MEMID_RENDER)); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "PreAlloc: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_PRE_ALLOC), gMainHeap.GetMemoryUsed(MEMID_PRE_ALLOC)); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Default Models: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_DEF_MODELS), gMainHeap.GetMemoryUsed(MEMID_DEF_MODELS)); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Textures: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_TEXTURES), gMainHeap.GetMemoryUsed(MEMID_TEXTURES)); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Streaming: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_STREAM), gMainHeap.GetMemoryUsed(MEMID_STREAM)); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Streamed Models: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_STREAM_MODELS), gMainHeap.GetMemoryUsed(MEMID_STREAM_MODELS)); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Streamed LODs: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_STREAM_LODS), gMainHeap.GetMemoryUsed(MEMID_STREAM_LODS)); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Streamed Textures: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_STREAM_TEXUTRES), gMainHeap.GetMemoryUsed(MEMID_STREAM_TEXUTRES)); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Streamed Collision: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_STREAM_COLLISION), gMainHeap.GetMemoryUsed(MEMID_STREAM_COLLISION)); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Streamed Animation: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_STREAM_ANIMATION), gMainHeap.GetMemoryUsed(MEMID_STREAM_ANIMATION)); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Ped Attr: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_PED_ATTR), gMainHeap.GetMemoryUsed(MEMID_PED_ATTR)); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Animation: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_ANIMATION), gMainHeap.GetMemoryUsed(MEMID_ANIMATION)); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Pools: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_POOLS), gMainHeap.GetMemoryUsed(MEMID_POOLS)); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Collision: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_COLLISION), gMainHeap.GetMemoryUsed(MEMID_COLLISION)); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Game Process: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_GAME_PROCESS), gMainHeap.GetMemoryUsed(MEMID_GAME_PROCESS)); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Script: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_SCRIPT), gMainHeap.GetMemoryUsed(MEMID_SCRIPT)); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Cars: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_CARS), gMainHeap.GetMemoryUsed(MEMID_CARS)); + AsciiToUnicode(gString, gUString); + CFont::PrintString(24.0f, y, gUString); + y += 12.0f; +#endif + + y = 132.0f; + AsciiToUnicode("Pools usage:", gUString); + CFont::PrintString(400.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "PtrNode: %d/%d", CPools::GetPtrNodePool()->GetNoOfUsedSpaces(), CPools::GetPtrNodePool()->GetSize()); + AsciiToUnicode(gString, gUString); + CFont::PrintString(400.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "EntryInfoNode: %d/%d", CPools::GetEntryInfoNodePool()->GetNoOfUsedSpaces(), CPools::GetEntryInfoNodePool()->GetSize()); + AsciiToUnicode(gString, gUString); + CFont::PrintString(400.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Ped: %d/%d", CPools::GetPedPool()->GetNoOfUsedSpaces(), CPools::GetPedPool()->GetSize()); + AsciiToUnicode(gString, gUString); + CFont::PrintString(400.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Vehicle: %d/%d", CPools::GetVehiclePool()->GetNoOfUsedSpaces(), CPools::GetVehiclePool()->GetSize()); + AsciiToUnicode(gString, gUString); + CFont::PrintString(400.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Building: %d/%d", CPools::GetBuildingPool()->GetNoOfUsedSpaces(), CPools::GetBuildingPool()->GetSize()); + AsciiToUnicode(gString, gUString); + CFont::PrintString(400.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Treadable: %d/%d", CPools::GetTreadablePool()->GetNoOfUsedSpaces(), CPools::GetTreadablePool()->GetSize()); + AsciiToUnicode(gString, gUString); + CFont::PrintString(400.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Object: %d/%d", CPools::GetObjectPool()->GetNoOfUsedSpaces(), CPools::GetObjectPool()->GetSize()); + AsciiToUnicode(gString, gUString); + CFont::PrintString(400.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "Dummy: %d/%d", CPools::GetDummyPool()->GetNoOfUsedSpaces(), CPools::GetDummyPool()->GetSize()); + AsciiToUnicode(gString, gUString); + CFont::PrintString(400.0f, y, gUString); + y += 12.0f; + + sprintf(gString, "AudioScriptObjects: %d/%d", CPools::GetAudioScriptObjectPool()->GetNoOfUsedSpaces(), CPools::GetAudioScriptObjectPool()->GetSize()); + AsciiToUnicode(gString, gUString); + CFont::PrintString(400.0f, y, gUString); + y += 12.0f; +} + +void +DisplayGameDebugText() +{ + static bool bDisplayCheatStr = false; // custom + +#ifndef FINAL + { + SETTWEAKPATH("Debug"); + TWEAKBOOL(bDisplayPosn); + TWEAKBOOL(bDisplayCheatStr); + } + + if(gbPrintMemoryUsage) + PrintMemoryUsage(); +#endif + + char str[200]; + wchar ustr[200]; + +#ifdef DRAW_GAME_VERSION_TEXT + wchar ver[200]; + + if(gbDrawVersionText) // This realtime switch is our thing + { + +#ifdef USE_OUR_VERSIONING + char verA[200]; + sprintf(verA, +#if defined _WIN32 + "Win " +#elif defined __linux__ + "Linux " +#elif defined __APPLE__ + "Mac OS X " +#elif defined __FreeBSD__ + "FreeBSD " +#else + "Posix-compliant " +#endif +#if defined __LP64__ || defined _WIN64 + "64-bit " +#else + "32-bit " +#endif +#if defined RW_D3D9 + "D3D9 " +#elif defined RWLIBS + "D3D8 " +#elif defined RW_GL3 + "OpenGL " +#endif +#if defined AUDIO_OAL + "OAL " +#elif defined AUDIO_MSS + "MSS " +#endif +#if defined _DEBUG || defined DEBUG + "DEBUG " +#endif + "%.8s", + g_GIT_SHA1); + AsciiToUnicode(verA, ver); + CFont::SetScale(SCREEN_SCALE_X(0.5f), SCREEN_SCALE_Y(0.7f)); +#else + AsciiToUnicode(version_name, ver); + CFont::SetScale(SCREEN_SCALE_X(0.5f), SCREEN_SCALE_Y(0.5f)); +#endif + + CFont::SetPropOn(); + CFont::SetBackgroundOff(); + CFont::SetFontStyle(FONT_STANDARD); + CFont::SetCentreOff(); + CFont::SetRightJustifyOff(); + CFont::SetWrapx(SCREEN_WIDTH); + CFont::SetJustifyOff(); + CFont::SetBackGroundOnlyTextOff(); + CFont::SetColor(CRGBA(255, 108, 0, 255)); + CFont::PrintString(SCREEN_SCALE_X(10.0f), SCREEN_SCALE_Y(10.0f), ver); + } +#endif // #ifdef DRAW_GAME_VERSION_TEXT + + FrameSamples++; +#ifdef FIX_BUGS + // this is inaccurate with over 1000 fps + static uint32 PreviousTimeInMillisecondsPauseMode = 0; + FramesPerSecondCounter += (CTimer::GetTimeInMillisecondsPauseMode() - PreviousTimeInMillisecondsPauseMode) / 1000.0f; // convert to seconds + PreviousTimeInMillisecondsPauseMode = CTimer::GetTimeInMillisecondsPauseMode(); + FramesPerSecond = FrameSamples / FramesPerSecondCounter; +#else + FramesPerSecondCounter += 1000.0f / CTimer::GetTimeStepNonClippedInMilliseconds(); + FramesPerSecond = FramesPerSecondCounter / FrameSamples; +#endif + + if ( FrameSamples > 30 ) + { + FramesPerSecondCounter = 0.0f; + FrameSamples = 0; + } + + if ( bDisplayPosn ) + { + CVector pos = FindPlayerCoors(); + int32 ZoneId = ARRAY_SIZE(ZonePrint)-1; // no zone + + for ( int32 i = 0; i < ARRAY_SIZE(ZonePrint)-1; i++ ) + { + if ( pos.x > ZonePrint[i].rect.left + && pos.x < ZonePrint[i].rect.right + && pos.y > ZonePrint[i].rect.bottom + && pos.y < ZonePrint[i].rect.top ) + { + ZoneId = i; + } + } + + //NOTE: fps should be 30, but its 29 due to different fp2int conversion + sprintf(str, "X:%4.0f Y:%4.0f Z:%4.0f F-%d %s-%s", pos.x, pos.y, pos.z, (int32)FramesPerSecond, + ZonePrint[ZoneId].name, ZonePrint[ZoneId].area); + + AsciiToUnicode(str, ustr); + + CFont::SetPropOn(); + CFont::SetBackgroundOff(); + CFont::SetScale(SCREEN_SCALE_X(0.6f), SCREEN_SCALE_Y(0.8f)); + CFont::SetCentreOff(); + CFont::SetRightJustifyOff(); + CFont::SetJustifyOff(); + CFont::SetBackGroundOnlyTextOff(); + CFont::SetWrapx(SCREEN_STRETCH_X(DEFAULT_SCREEN_WIDTH)); + CFont::SetFontStyle(FONT_STANDARD); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + CFont::SetDropShadowPosition(2); + CFont::SetColor(CRGBA(0, 0, 0, 255)); + CFont::PrintString(41.0f, 41.0f, ustr); + + CFont::SetColor(CRGBA(205, 205, 0, 255)); + CFont::PrintString(40.0f, 40.0f, ustr); + } + + // custom + if (bDisplayCheatStr) + { + sprintf(str, "%s", CPad::KeyBoardCheatString); + AsciiToUnicode(str, ustr); + + CFont::SetPropOn(); + CFont::SetBackgroundOff(); + CFont::SetScale(SCREEN_SCALE_X(0.6f), SCREEN_SCALE_Y(0.8f)); + CFont::SetCentreOn(); + CFont::SetBackGroundOnlyTextOff(); + CFont::SetWrapx(SCREEN_STRETCH_X(DEFAULT_SCREEN_WIDTH)); + CFont::SetFontStyle(FONT_STANDARD); + + CFont::SetColor(CRGBA(0, 0, 0, 255)); + CFont::PrintString(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH * 0.5f)+2.f, SCREEN_SCALE_FROM_BOTTOM(20.0f)+2.f, ustr); + + CFont::SetColor(CRGBA(255, 150, 225, 255)); + CFont::PrintString(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH * 0.5f), SCREEN_SCALE_FROM_BOTTOM(20.0f), ustr); + } +} +#endif + +#ifdef NEW_RENDERER +bool gbRenderRoads = true; +bool gbRenderEverythingBarRoads = true; +bool gbRenderFadingInUnderwaterEntities = true; +bool gbRenderFadingInEntities = true; +bool gbRenderWater = true; +bool gbRenderBoats = true; +bool gbRenderVehicles = true; +bool gbRenderWorld0 = true; +bool gbRenderWorld1 = true; +bool gbRenderWorld2 = true; + +void +MattRenderScene(void) +{ + // this calls CMattRenderer::Render + /// CWorld::AdvanceCurrentScanCode(); + // CMattRenderer::ResetRenderStates + /// CRenderer::ClearForFrame(); // before ConstructRenderList + // CClock::CalcEnvMapTimeMultiplicator + RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE); + CWaterLevel::RenderWater(); // actually CMattRenderer::RenderWater + // CClock::ms_EnvMapTimeMultiplicator = 1.0f; + // cWorldStream::ClearDynamics + /// CRenderer::ConstructRenderList(); // before PreRender +if(gbRenderWorld0) + CRenderer::RenderWorld(0); // roads + // CMattRenderer::ResetRenderStates + /// CRenderer::PreRender(); // has to be called before BeginUpdate because of cutscene shadows + CCoronas::RenderReflections(); +if(gbRenderWorld1) + CRenderer::RenderWorld(1); // opaque +if(gbRenderRoads) + CRenderer::RenderRoads(); + + CRenderer::RenderPeds(); + + // not sure where to put these since LCS has no underwater entities +if(gbRenderBoats) + CRenderer::RenderBoats(); +if(gbRenderFadingInUnderwaterEntities) + CRenderer::RenderFadingInUnderwaterEntities(); + RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE); +if(gbRenderWater) + CRenderer::RenderTransparentWater(); + +if(gbRenderEverythingBarRoads) + CRenderer::RenderEverythingBarRoads(); + // seam fixer + // moved this: + // CRenderer::RenderFadingInEntities(); +} + +void +RenderScene_new(void) +{ + PUSH_RENDERGROUP("RenderScene_new"); + CClouds::Render(); + DoRWRenderHorizon(); + + MattRenderScene(); + DefinedState(); + // CMattRenderer::ResetRenderStates + // moved CRenderer::RenderBoats to before transparent water + POP_RENDERGROUP(); +} + +// TODO +bool FredIsInFirstPersonCam(void) { return false; } +void +RenderEffects_new(void) +{ + PUSH_RENDERGROUP("RenderEffects_new"); +/* // stupid to do this before the whole world is drawn! + CShadows::RenderStaticShadows(); + // CRenderer::GenerateEnvironmentMap + CShadows::RenderStoredShadows(); + CSkidmarks::Render(); + CRubbish::Render(); +*/ + + // these aren't really effects + DefinedState(); + if(FredIsInFirstPersonCam()){ + DefinedState(); + C3dMarkers::Render(); // normally rendered in CSpecialFX::Render() +if(gbRenderWorld2) + CRenderer::RenderWorld(2); // transparent +if(gbRenderVehicles) + CRenderer::RenderVehicles(); + }else{ + // flipped these two, seems to give the best result +if(gbRenderWorld2) + CRenderer::RenderWorld(2); // transparent +if(gbRenderVehicles) + CRenderer::RenderVehicles(); + } + // better render these after transparent world +if(gbRenderFadingInEntities) + CRenderer::RenderFadingInEntities(); + + // actual effects here + + // from above + CShadows::RenderStaticShadows(); + CShadows::RenderStoredShadows(); + CSkidmarks::Render(); + CRubbish::Render(); + + CGlass::Render(); + // CMattRenderer::ResetRenderStates + DefinedState(); + CCoronas::RenderSunReflection(); + CWeather::RenderRainStreaks(); + // CWeather::AddSnow + CWaterCannons::Render(); + CAntennas::Render(); + CSpecialFX::Render(); + CRopes::Render(); + CCoronas::Render(); + CParticle::Render(); + CPacManPickups::Render(); + CWeaponEffects::Render(); + CPointLights::RenderFogEffect(); + CMovingThings::Render(); + CRenderer::RenderFirstPersonVehicle(); + POP_RENDERGROUP(); +} +#endif + +void +RenderScene(void) +{ +#ifdef NEW_RENDERER + if(gbNewRenderer){ + RenderScene_new(); + return; + } +#endif + PUSH_RENDERGROUP("RenderScene"); + CClouds::Render(); + DoRWRenderHorizon(); + CRenderer::RenderRoads(); + CCoronas::RenderReflections(); + CRenderer::RenderEverythingBarRoads(); + RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE); + CWaterLevel::RenderWater(); + CRenderer::RenderBoats(); + CRenderer::RenderFadingInUnderwaterEntities(); + RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE); + CWaterLevel::RenderTransparentWater(); + CRenderer::RenderFadingInEntities(); + RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE); + CWeather::RenderRainStreaks(); + CCoronas::RenderSunReflection(); + POP_RENDERGROUP(); +} + +void +RenderDebugShit(void) +{ + PUSH_RENDERGROUP("RenderDebugShit"); + CTheScripts::RenderTheScriptDebugLines(); +#ifndef FINAL + if(gbShowCollisionLines) + CRenderer::RenderCollisionLines(); + ThePaths.DisplayPathData(); + CDebug::DrawLines(); + DefinedState(); +#endif + POP_RENDERGROUP(); +} + +void +RenderEffects(void) +{ +#ifdef NEW_RENDERER + if(gbNewRenderer){ + RenderEffects_new(); + return; + } +#endif + PUSH_RENDERGROUP("RenderEffects"); + CGlass::Render(); + CWaterCannons::Render(); + CSpecialFX::Render(); + CRopes::Render(); + CShadows::RenderStaticShadows(); + CShadows::RenderStoredShadows(); + CSkidmarks::Render(); + CAntennas::Render(); + CRubbish::Render(); + CCoronas::Render(); + CParticle::Render(); + CPacManPickups::Render(); + CWeaponEffects::Render(); + CPointLights::RenderFogEffect(); + CMovingThings::Render(); + CRenderer::RenderFirstPersonVehicle(); + POP_RENDERGROUP(); +} + +void +Render2dStuff(void) +{ + PUSH_RENDERGROUP("Render2dStuff"); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE); + + CReplay::Display(); + CPickups::RenderPickUpText(); + + if(TheCamera.m_WideScreenOn +#ifdef CUTSCENE_BORDERS_SWITCH + && CMenuManager::m_PrefsCutsceneBorders +#endif + ) + TheCamera.DrawBordersForWideScreen(); + + CPed *player = FindPlayerPed(); + int weaponType = 0; + if(player) + weaponType = player->GetWeapon()->m_eWeaponType; + + bool firstPersonWeapon = false; + int cammode = TheCamera.Cams[TheCamera.ActiveCam].Mode; + if(cammode == CCam::MODE_SNIPER || + cammode == CCam::MODE_SNIPER_RUNABOUT || + cammode == CCam::MODE_ROCKETLAUNCHER || + cammode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || + cammode == CCam::MODE_CAMERA) + firstPersonWeapon = true; + + // Draw black border for sniper and rocket launcher + if((weaponType == WEAPONTYPE_SNIPERRIFLE || weaponType == WEAPONTYPE_ROCKETLAUNCHER || weaponType == WEAPONTYPE_LASERSCOPE) && firstPersonWeapon){ + CRGBA black(0, 0, 0, 255); + + // top and bottom strips + if (weaponType == WEAPONTYPE_ROCKETLAUNCHER) { + CSprite2d::DrawRect(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT / 2 - SCREEN_SCALE_Y(180)), black); + CSprite2d::DrawRect(CRect(0.0f, SCREEN_HEIGHT / 2 + SCREEN_SCALE_Y(170), SCREEN_WIDTH, SCREEN_HEIGHT), black); + } + else { + CSprite2d::DrawRect(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT / 2 - SCREEN_SCALE_Y(210)), black); + CSprite2d::DrawRect(CRect(0.0f, SCREEN_HEIGHT / 2 + SCREEN_SCALE_Y(210), SCREEN_WIDTH, SCREEN_HEIGHT), black); + } + CSprite2d::DrawRect(CRect(0.0f, 0.0f, SCREEN_WIDTH / 2 - SCREEN_SCALE_X(210), SCREEN_HEIGHT), black); + CSprite2d::DrawRect(CRect(SCREEN_WIDTH / 2 + SCREEN_SCALE_X(210), 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), black); + } + + MusicManager.DisplayRadioStationName(); + TheConsole.Display(); +#ifdef GTA_SCENE_EDIT + if(CSceneEdit::m_bEditOn) + CSceneEdit::Draw(); + else +#endif + CHud::Draw(); + + CSpecialFX::Render2DFXs(); + CUserDisplay::OnscnTimer.ProcessForDisplay(); + CMessages::Display(); + CDarkel::DrawMessages(); + CGarages::PrintMessages(); + CPad::PrintErrorMessage(); + CFont::DrawFonts(); +#ifndef MASTER + COcclusion::Render(); +#endif + +#ifdef DEBUGMENU + DebugMenuRender(); +#endif + POP_RENDERGROUP(); +} + +void +RenderMenus(void) +{ + if (FrontEndMenuManager.m_bMenuActive) + { + PUSH_RENDERGROUP("RenderMenus"); + FrontEndMenuManager.DrawFrontEnd(); + POP_RENDERGROUP(); + } +#ifndef MASTER + else + VarConsole.Check(); +#endif +} + +void +Render2dStuffAfterFade(void) +{ + PUSH_RENDERGROUP("Render2dStuffAfterFade"); +#ifndef MASTER + DisplayGameDebugText(); +#endif + +#ifdef MOBILE_IMPROVEMENTS + if (CDraw::FadeValue != 0) +#endif + CHud::DrawAfterFade(); + CFont::DrawFonts(); + CCredits::Render(); + POP_RENDERGROUP(); +} + +void +Idle(void *arg) +{ + CTimer::Update(); + + tbInit(); + + CSprite2d::InitPerFrame(); + CFont::InitPerFrame(); + + PUSH_MEMID(MEMID_GAME_PROCESS); + CPointLights::InitPerFrame(); + + tbStartTimer(0, "CGame::Process"); + CGame::Process(); + tbEndTimer("CGame::Process"); + POP_MEMID(); + + tbStartTimer(0, "DMAudio.Service"); + DMAudio.Service(); + tbEndTimer("DMAudio.Service"); + + if(CGame::bDemoMode && CTimer::GetTimeInMilliseconds() > (3*60 + 30)*1000 && !CCutsceneMgr::IsCutsceneProcessing()){ + WANT_TO_LOAD = false; + FrontEndMenuManager.m_bWantToRestart = true; + return; + } + + if(FrontEndMenuManager.m_bWantToRestart || FOUND_GAME_TO_LOAD) + { + return; + } + + SetLightsWithTimeOfDayColour(Scene.world); + + if(arg == nil) + return; + + PUSH_MEMID(MEMID_RENDER); + + if(!FrontEndMenuManager.m_bMenuActive && TheCamera.GetScreenFadeStatus() != FADE_2) + { + // This is from SA, but it's nice for windowed mode +#if defined(GTA_PC) && !defined(RW_GL3) + RwV2d pos; + pos.x = SCREEN_WIDTH / 2.0f; + pos.y = SCREEN_HEIGHT / 2.0f; + RsMouseSetPos(&pos); +#endif + + tbStartTimer(0, "CnstrRenderList"); +#ifdef PC_WATER + CWaterLevel::PreCalcWaterGeometry(); +#endif +#ifdef NEW_RENDERER + if(gbNewRenderer){ + CWorld::AdvanceCurrentScanCode(); // don't think this is even necessary + CRenderer::ClearForFrame(); + } +#endif + CRenderer::ConstructRenderList(); + tbEndTimer("CnstrRenderList"); + + tbStartTimer(0, "PreRender"); + CRenderer::PreRender(); + tbEndTimer("PreRender"); + +#ifdef FIX_BUGS + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE); // TODO: temp? this fixes OpenGL render but there should be a better place for this + // This has to be done BEFORE RwCameraBeginUpdate + RwCameraSetFarClipPlane(Scene.camera, CTimeCycle::GetFarClip()); + RwCameraSetFogDistance(Scene.camera, CTimeCycle::GetFogStart()); +#endif + + if(CWeather::LightningFlash && !CCullZones::CamNoRain()){ + if(!DoRWStuffStartOfFrame_Horizon(255, 255, 255, 255, 255, 255, 255)) + goto popret; + }else{ + if(!DoRWStuffStartOfFrame_Horizon(CTimeCycle::GetSkyTopRed(), CTimeCycle::GetSkyTopGreen(), CTimeCycle::GetSkyTopBlue(), + CTimeCycle::GetSkyBottomRed(), CTimeCycle::GetSkyBottomGreen(), CTimeCycle::GetSkyBottomBlue(), + 255)) + goto popret; + } + + DefinedState(); + +#ifndef FIX_BUGS + RwCameraSetFarClipPlane(Scene.camera, CTimeCycle::GetFarClip()); + RwCameraSetFogDistance(Scene.camera, CTimeCycle::GetFogStart()); +#endif + + tbStartTimer(0, "RenderScene"); + RenderScene(); + tbEndTimer("RenderScene"); + +#ifdef EXTENDED_PIPELINES + CustomPipes::EnvMapRender(); +#endif + + RenderDebugShit(); + RenderEffects(); + + if((TheCamera.m_BlurType == MOTION_BLUR_NONE || TheCamera.m_BlurType == MOTION_BLUR_LIGHT_SCENE) && + TheCamera.m_ScreenReductionPercentage > 0.0f) + TheCamera.SetMotionBlurAlpha(150); + +#ifdef SCREEN_DROPLETS + CPostFX::GetBackBuffer(Scene.camera); + ScreenDroplets::Process(); + ScreenDroplets::Render(); +#endif + + tbStartTimer(0, "RenderMotionBlur"); + TheCamera.RenderMotionBlur(); + tbEndTimer("RenderMotionBlur"); + + tbStartTimer(0, "Render2dStuff"); + Render2dStuff(); + tbEndTimer("Render2dStuff"); + }else{ + CDraw::CalculateAspectRatio(); +#ifdef ASPECT_RATIO_SCALE + CameraSize(Scene.camera, nil, SCREEN_VIEWWINDOW, SCREEN_ASPECT_RATIO); +#else + CameraSize(Scene.camera, nil, SCREEN_VIEWWINDOW, DEFAULT_ASPECT_RATIO); +#endif + CVisibilityPlugins::SetRenderWareCamera(Scene.camera); + RwCameraClear(Scene.camera, &gColourTop, CLEARMODE); + if(!RsCameraBeginUpdate(Scene.camera)) + goto popret; + } + + tbStartTimer(0, "RenderMenus"); + RenderMenus(); + tbEndTimer("RenderMenus"); + +#ifdef PS2_MENU + if ( TheMemoryCard.m_bWantToLoad ) + goto popret; +#endif + + tbStartTimer(0, "DoFade"); + DoFade(); + tbEndTimer("DoFade"); + + tbStartTimer(0, "Render2dStuff-Fade"); + Render2dStuffAfterFade(); + tbEndTimer("Render2dStuff-Fade"); + // CCredits::Render(); // They added it to function above and also forgot it here +#ifdef XBOX_MESSAGE_SCREEN + FrontEndMenuManager.DrawOverlays(); +#endif + + if (gbShowTimebars) + tbDisplay(); + + DoRWStuffEndOfFrame(); + + POP_MEMID(); // MEMID_RENDER + + if(g_SlowMode) + ProcessSlowMode(); + return; + +popret: POP_MEMID(); // MEMID_RENDER +} + +void +FrontendIdle(void) +{ + CDraw::CalculateAspectRatio(); + CTimer::Update(); + CSprite2d::SetRecipNearClip(); // this should be on InitialiseRenderWare according to PS2 asm. seems like a bug fix + CSprite2d::InitPerFrame(); + CFont::InitPerFrame(); + CPad::UpdatePads(); + FrontEndMenuManager.Process(); + + if(RsGlobal.quit) + return; + + CameraSize(Scene.camera, nil, SCREEN_VIEWWINDOW, SCREEN_ASPECT_RATIO); + CVisibilityPlugins::SetRenderWareCamera(Scene.camera); + RwCameraClear(Scene.camera, &gColourTop, CLEARMODE); + if(!RsCameraBeginUpdate(Scene.camera)) + return; + + DefinedState(); // seems redundant, but breaks resolution change. + RenderMenus(); +#ifdef XBOX_MESSAGE_SCREEN + FrontEndMenuManager.DrawOverlays(); +#endif + DoFade(); + Render2dStuffAfterFade(); + CFont::DrawFonts(); + DoRWStuffEndOfFrame(); +} + +void +InitialiseGame(void) +{ + LoadingScreen(nil, nil, "loadsc0"); + CGame::Initialise("DATA\\GTA_VC.DAT"); +} + +RsEventStatus +AppEventHandler(RsEvent event, void *param) +{ + switch( event ) + { + case rsINITIALIZE: + { + CGame::InitialiseOnceBeforeRW(); + return RsInitialize() ? rsEVENTPROCESSED : rsEVENTERROR; + } + + case rsCAMERASIZE: + { + + CameraSize(Scene.camera, (RwRect *)param, + SCREEN_VIEWWINDOW, DEFAULT_ASPECT_RATIO); + + return rsEVENTPROCESSED; + } + + case rsRWINITIALIZE: + { + return Initialise3D(param) ? rsEVENTPROCESSED : rsEVENTERROR; + } + + case rsRWTERMINATE: + { + Terminate3D(); + + return rsEVENTPROCESSED; + } + + case rsTERMINATE: + { + CGame::FinalShutdown(); + + return rsEVENTPROCESSED; + } + + case rsPLUGINATTACH: + { + return PluginAttach() ? rsEVENTPROCESSED : rsEVENTERROR; + } + + case rsINPUTDEVICEATTACH: + { + AttachInputDevices(); + + return rsEVENTPROCESSED; + } + + case rsIDLE: + { + Idle(param); + + return rsEVENTPROCESSED; + } + + case rsFRONTENDIDLE: + { + FrontendIdle(); + + return rsEVENTPROCESSED; + } + + case rsACTIVATE: + { + param ? DMAudio.ReacquireDigitalHandle() : DMAudio.ReleaseDigitalHandle(); + + return rsEVENTPROCESSED; + } + + default: + { + return rsEVENTNOTPROCESSED; + } + } +} + +#if !defined(RW_DC) +#ifndef MASTER +void +TheModelViewer(void) +{ +#if (defined(GTA_PS2) || defined(GTA_XBOX)) + //TODO +#else + + // This is not original. Because; + // 1- We want 2D things to be initalized, whereas original AnimViewer doesn't use them. my additions marked with X + // 2- VC Mobile code run it like main function(as opposed to III and LCS), so it has it's own loop inside it, but our func. already called in a loop. + + CDraw::CalculateAspectRatio(); // X + CAnimViewer::Update(); + SetLightsWithTimeOfDayColour(Scene.world); + CRenderer::ConstructRenderList(); + DoRWStuffStartOfFrame(CTimeCycle::GetSkyTopRed()*0.5f, CTimeCycle::GetSkyTopGreen()*0.5f, CTimeCycle::GetSkyTopBlue()*0.5f, + CTimeCycle::GetSkyBottomRed(), CTimeCycle::GetSkyBottomGreen(), CTimeCycle::GetSkyBottomBlue(), + 255); + + CSprite2d::SetRecipNearClip(); // X + CSprite2d::InitPerFrame(); // X + CFont::InitPerFrame(); // X + DefinedState(); + CVisibilityPlugins::InitAlphaEntityList(); + CAnimViewer::Render(); + Render2dStuff(); // X + DoRWStuffEndOfFrame(); + CTimer::Update(); +#endif +} +#endif +#endif + + +#ifdef GTA_PS2 +void TheGame(void) +{ + printf("Into TheGame!!!\n"); + + PUSH_MEMID(MEMID_GAME); // NB: not popped + + CTimer::Initialise(); + + CGame::Initialise("DATA\\GTA3.DAT"); + + Const char *splash = GetRandomSplashScreen(); // inlined here + + LoadingScreen("Starting Game", NULL, splash); + +#ifdef GTA_PS2 + // TODO(MIAMI): not checked yet + if ( TheMemoryCard.CheckCardInserted(CARD_ONE) == CMemoryCard::NO_ERR_SUCCESS + && TheMemoryCard.ChangeDirectory(CARD_ONE, TheMemoryCard.Cards[CARD_ONE].dir) + && TheMemoryCard.FindMostRecentFileName(CARD_ONE, TheMemoryCard.MostRecentFile) == true + && TheMemoryCard.CheckDataNotCorrupt(TheMemoryCard.MostRecentFile)) + { + strcpy(TheMemoryCard.LoadFileName, TheMemoryCard.MostRecentFile); + TheMemoryCard.b_FoundRecentSavedGameWantToLoad = true; + + if (FrontEndMenuManager.m_PrefsLanguage != TheMemoryCard.GetLanguageToLoad()) + { + FrontEndMenuManager.m_PrefsLanguage = TheMemoryCard.GetLanguageToLoad(); + TheText.Unload(); + TheText.Load(); + } + + CGame::currLevel = TheMemoryCard.GetLevelToLoad(); + } +#else + //TODO +#endif + + while (true) + { + if (FOUND_GAME_TO_LOAD) + { + Const char *splash1 = GetLevelSplashScreen(CGame::currLevel); + LoadSplash(splash1); + } + + WANT_TO_LOAD = false; + + CTimer::Update(); + + while (!(FrontEndMenuManager.m_bWantToRestart || FOUND_GAME_TO_LOAD)) + { + CSprite2d::InitPerFrame(); + CFont::InitPerFrame(); + + PUSH_MEMID(MEMID_GAME_PROCESS) + CPointLights::InitPerFrame(); + CGame::Process(); + POP_MEMID(); + + DMAudio.Service(); + + if (CGame::bDemoMode && CTimer::GetTimeInMilliseconds() > (3*60 + 30)*1000 && !CCutsceneMgr::IsCutsceneProcessing()) + { + WANT_TO_LOAD = false; + FrontEndMenuManager.m_bWantToRestart = true; + break; + } + + if (FrontEndMenuManager.m_bWantToRestart || FOUND_GAME_TO_LOAD) + break; + + SetLightsWithTimeOfDayColour(Scene.world); + + PUSH_MEMID(MEMID_RENDER); + + CRenderer::ConstructRenderList(); + + if ((!FrontEndMenuManager.m_bMenuActive || FrontEndMenuManager.m_bRenderGameInMenu == true) && TheCamera.GetScreenFadeStatus() != FADE_2 ) + { + CRenderer::PreRender(); + // TODO(MIAMI): something ps2all specific + +#ifdef FIX_BUGS + // This has to be done BEFORE RwCameraBeginUpdate + RwCameraSetFarClipPlane(Scene.camera, CTimeCycle::GetFarClip()); + RwCameraSetFogDistance(Scene.camera, CTimeCycle::GetFogStart()); +#endif + + if (CWeather::LightningFlash && !CCullZones::CamNoRain()) + DoRWStuffStartOfFrame_Horizon(255, 255, 255, 255, 255, 255, 255); + else + DoRWStuffStartOfFrame_Horizon(CTimeCycle::GetSkyTopRed(), CTimeCycle::GetSkyTopGreen(), CTimeCycle::GetSkyTopBlue(), CTimeCycle::GetSkyBottomRed(), CTimeCycle::GetSkyBottomGreen(), CTimeCycle::GetSkyBottomBlue(), 255); + + DefinedState(); +#ifndef FIX_BUGS + RwCameraSetFarClipPlane(Scene.camera, CTimeCycle::GetFarClip()); + RwCameraSetFogDistance(Scene.camera, CTimeCycle::GetFogStart()); +#endif + + RenderScene(); + RenderDebugShit(); + RenderEffects(); + + if ((TheCamera.m_BlurType == MOTION_BLUR_NONE || TheCamera.m_BlurType == MOTION_BLUR_LIGHT_SCENE) && TheCamera.m_ScreenReductionPercentage > 0.0f) + TheCamera.SetMotionBlurAlpha(150); + TheCamera.RenderMotionBlur(); + + Render2dStuff(); + } + else + { + CameraSize(Scene.camera, NULL, SCREEN_VIEWWINDOW, SCREEN_ASPECT_RATIO); + CVisibilityPlugins::SetRenderWareCamera(Scene.camera); + RwCameraClear(Scene.camera, &gColourTop, CLEARMODE); + RsCameraBeginUpdate(Scene.camera); + } + + RenderMenus(); + + if (WANT_TO_LOAD) + { + POP_MEMID(); // MEMID_RENDER + break; + } + + DoFade(); + Render2dStuffAfterFade(); + CCredits::Render(); + + DoRWStuffEndOfFrame(); + + while (frameCount < 2) + ; + + frameCount = 0; + + CTimer::Update(); + + POP_MEMID(): // MEMID_RENDER + + if (g_SlowMode) + ProcessSlowMode(); + } + + CPad::ResetCheats(); + CPad::StopPadsShaking(); + DMAudio.ChangeMusicMode(MUSICMODE_DISABLE); + CGame::ShutDownForRestart(); + CTimer::Stop(); + + if (FrontEndMenuManager.m_bWantToRestart || FOUND_GAME_TO_LOAD) + { + if (FOUND_GAME_TO_LOAD) + { + FrontEndMenuManager.m_bWantToRestart = true; + WANT_TO_LOAD = true; + } + + CGame::InitialiseWhenRestarting(); + DMAudio.ChangeMusicMode(MUSICMODE_GAME); + FrontEndMenuManager.m_bWantToRestart = false; + + continue; + } + + break; + } + + DMAudio.Terminate(); +} + + +void SystemInit() +{ +#ifdef USE_CUSTOM_ALLOCATOR + InitMemoryMgr(); +#endif + +#ifdef GTA_PS2 + CFileMgr::InitCdSystem(); + + char path[256]; + + sprintf(path, "cdrom0:\\%s%s;1", "SYSTEM\\", "IOPRP241.IMG"); + + sceSifInitRpc(0); + + while ( !sceSifRebootIop(path) ) + ; + while( !sceSifSyncIop() ) + ; + + sceSifInitRpc(0); + + CFileMgr::InitCdSystem(); + + sceFsReset(); + + CFileMgr::InitCd(); + + char modulepath[256]; + + strcpy(modulepath, "cdrom0:\\"); + strcat(modulepath, "SYSTEM\\"); + strcat(modulepath, "SIO2MAN.IRX"); + LoadModule(modulepath); + + strcpy(modulepath, "cdrom0:\\"); + strcat(modulepath, "SYSTEM\\"); + strcat(modulepath, "PADMAN.IRX"); + LoadModule(modulepath); + + strcpy(modulepath, "cdrom0:\\"); + strcat(modulepath, "SYSTEM\\"); + strcat(modulepath, "LIBSD.IRX"); + LoadModule(modulepath); + + strcpy(modulepath, "cdrom0:\\"); + strcat(modulepath, "SYSTEM\\"); + strcat(modulepath, "SDRDRV.IRX"); + LoadModule(modulepath); + + strcpy(modulepath, "cdrom0:\\"); + strcat(modulepath, "SYSTEM\\"); + strcat(modulepath, "MCMAN.IRX"); + LoadModule(modulepath); + + strcpy(modulepath, "cdrom0:\\"); + strcat(modulepath, "SYSTEM\\"); + strcat(modulepath, "MCSERV.IRX"); + LoadModule(modulepath); + + strcpy(modulepath, "cdrom0:\\"); + strcat(modulepath, "SYSTEM\\"); + strcat(modulepath, "CDSTREAM.IRX"); + LoadModule(modulepath); + + strcpy(modulepath, "cdrom0:\\"); + strcat(modulepath, "SYSTEM\\"); + strcat(modulepath, "SAMPMAN2.IRX"); + LoadModule(modulepath); +#endif + + +#ifdef GTA_PS2 + ThreadParam param; + + param.entry = &IdleThread; + param.stack = idleThreadStack; + param.stackSize = 2048; + param.initPriority = 127; + param.gpReg = &_gp; + + int thread = CreateThread(¶m); + StartThread(thread, NULL); +#else + // +#endif + +#ifdef GTA_PS2_STUFF + CPad::Initialise(); +#endif + CPad::GetPad(0)->Mode = 0; + + CGame::frenchGame = false; + CGame::germanGame = false; + CGame::nastyGame = true; + FrontEndMenuManager.m_PrefsAllowNastyGame = true; + +#ifdef GTA_PS2 + // TODO(MIAMI): this code probably went elsewhere? + int32 lang = sceScfGetLanguage(); + if ( lang == SCE_ITALIAN_LANGUAGE ) + FrontEndMenuManager.m_PrefsLanguage = LANGUAGE_ITALIAN; + else if ( lang == SCE_SPANISH_LANGUAGE ) + FrontEndMenuManager.m_PrefsLanguage = LANGUAGE_SPANISH; + else if ( lang == SCE_GERMAN_LANGUAGE ) + { + CGame::germanGame = true; + CGame::nastyGame = false; + FrontEndMenuManager.m_PrefsAllowNastyGame = false; + FrontEndMenuManager.m_PrefsLanguage = LANGUAGE_GERMAN; + } + else if ( lang == SCE_FRENCH_LANGUAGE ) + { + CGame::frenchGame = true; + CGame::nastyGame = false; + FrontEndMenuManager.m_PrefsAllowNastyGame = false; + FrontEndMenuManager.m_PrefsLanguage = LANGUAGE_FRENCH; + } + else + FrontEndMenuManager.m_PrefsLanguage = LANGUAGE_AMERICAN; + + FrontEndMenuManager.InitialiseMenuContentsAfterLoadingGame(); +#else + // +#endif + +#ifdef GTA_PS2 + TheMemoryCard.Init(); +#endif +} + +int VBlankCounter(int ca) +{ + frameCount++; + ExitHandler(); + return 0; +} + +// linked against by RW! +extern "C" void WaitVBlank(void) +{ + int32 startFrame = frameCount; + while(startFrame == frameCount); +} + +void GameInit(bool onlyRW) +{ + if(onlyRW) + { +#ifdef GTA_PS2 + Initialise3D(nil); +#else + Initialise3D(nil); //TODO: window parameter +#endif + gameAlreadyInitialised = true; + } + else + { + if ( !gameAlreadyInitialised ) +#ifdef GTA_PS2 + Initialise3D(nil); +#else + Initialise3D(nil); //TODO: window parameter +#endif + } + +#ifdef GTA_PS2 + char *files[] = + { + "\\ANIM\\CUTS.IMG;1", + "\\ANIM\\CUTS.DIR;1", + "\\ANIM\\PED.IFP;1", + "\\MODELS\\FRONTEN1.TXD;1", + "\\MODELS\\FRONTEN2.TXD;1", + "\\MODELS\\FONTS.TXD;1", + "\\MODELS\\HUD.TXD;1", + "\\MODELS\\PARTICLE.TXD;1", + "\\MODELS\\MISC.TXD;1", + "\\MODELS\\GENERIC.TXD;1", + "\\MODELS\\GTA3.DIR;1", + // TODO: japanese? +#ifdef GTA_PAL + "\\TEXT\\ENGLISH.GXT;1", + "\\TEXT\\FRENCH.GXT;1", + "\\TEXT\\GERMAN.GXT;1", + "\\TEXT\\ITALIAN.GXT;1", + "\\TEXT\\SPANISH.GXT;1", +#else + "\\TEXT\\AMERICAN.GXT;1", +#endif + "\\MODELS\\COLL\\GENERIC.COL;1", + "\\MODELS\\COLL\\VEHICLES.COL;1", + "\\MODELS\\COLL\\PEDS.COL;1", + "\\MODELS\\COLL\\WEAPONS.COL;1", + "\\MODELS\\GENERIC\\AIR_VLO.DFF;1", + "\\MODELS\\GENERIC\\WHEELS.DFF;1", + "\\MODELS\\GENERIC\\ARROW.DFF;1", + "\\MODELS\\GENERIC\\ZONECYLB.DFF;1", + "\\DATA\\HANDLING.CFG;1", + "\\DATA\\SURFACE.DAT;1", + "\\DATA\\PEDSTATS.DAT;1", + "\\DATA\\TIMECYC.DAT;1", + "\\DATA\\PARTICLE.CFG;1", + "\\DATA\\DEFAULT.DAT;1", + "\\DATA\\DEFAULT.IDE;1", + "\\DATA\\GTA_VC.DAT;1", + "\\DATA\\OBJECT.DAT;1", + "\\DATA\\MAP.ZON;1", + "\\DATA\\NAVIG.ZON;1", + "\\DATA\\INFO.ZON;1", + "\\DATA\\WATERPRO.DAT;1", + "\\DATA\\MAIN.SCM;1", + "\\DATA\\CARCOLS.DAT;1", + "\\DATA\\PED.DAT;1", + "\\DATA\\FISTFITE.DAT;1", + "\\DATA\\WEAPON.DAT;1", + "\\DATA\\PEDGRP.DAT;1", + "\\DATA\\PATHS\\FLIGHT.DAT;1", + "\\DATA\\PATHS\\FLIGHT2.DAT;1", + "\\DATA\\PATHS\\FLIGHT3.DAT;1", + "\\DATA\\PATHS\\SPATH0.DAT;1", + "\\DATA\\MAPS\\LITTLEHA\\LITTLEHA.IDE;1", + "\\DATA\\MAPS\\DOWNTOWN\\DOWNTOWN.IDE;1", + "\\DATA\\MAPS\\DOWNTOWS\\DOWNTOWS.IDE;1", + "\\DATA\\MAPS\\DOCKS\\DOCKS.IDE;1", + "\\DATA\\MAPS\\WASHINTN\\WASHINTN.IDE;1", + "\\DATA\\MAPS\\WASHINTS\\WASHINTS.IDE;1", + "\\DATA\\MAPS\\OCEANDRV\\OCEANDRV.IDE;1", + "\\DATA\\MAPS\\OCEANDN\\OCEANDN.IDE;1", + "\\DATA\\MAPS\\GOLF\\GOLF.IDE;1", + "\\DATA\\MAPS\\BRIDGE\\BRIDGE.IDE;1", + "\\DATA\\MAPS\\STARISL\\STARISL.IDE;1", + "\\DATA\\MAPS\\NBEACHBT\\NBEACHBT.IDE;1", + "\\DATA\\MAPS\\NBEACHW\\NBEACHW.IDE;1", + "\\DATA\\MAPS\\NBEACH\\NBEACH.IDE;1", + "\\DATA\\MAPS\\BANK\\BANK.IDE;1", + "\\DATA\\MAPS\\MALL\\MALL.IDE;1", + "\\DATA\\MAPS\\YACHT\\YACHT.IDE;1", + "\\DATA\\MAPS\\CISLAND\\CISLAND.IDE;1", + "\\DATA\\MAPS\\CLUB\\CLUB.IDE;1", + "\\DATA\\MAPS\\HOTEL\\HOTEL.IDE;1", + "\\DATA\\MAPS\\LAWYERS\\LAWYERS.IDE;1", + "\\DATA\\MAPS\\STRIPCLB\\STRIPCLB.IDE;1", + "\\DATA\\MAPS\\AIRPORT\\AIRPORT.IDE;1", + "\\DATA\\MAPS\\HAITI\\HAITI.IDE;1", + "\\DATA\\MAPS\\HAITIN\\HAITIN.IDE;1", + "\\DATA\\MAPS\\CONCERTH\\CONCERTH.IDE;1", + "\\DATA\\MAPS\\MANSION\\MANSION.IDE;1", + "\\DATA\\MAPS\\ISLANDSF\\ISLANDSF.IDE;1", + "\\DATA\\MAPS\\LITTLEHA\\LITTLEHA.IPL;1", + "\\DATA\\MAPS\\DOWNTOWN\\DOWNTOWN.IPL;1", + "\\DATA\\MAPS\\DOWNTOWS\\DOWNTOWS.IPL;1", + "\\DATA\\MAPS\\DOCKS\\DOCKS.IPL;1", + "\\DATA\\MAPS\\WASHINTN\\WASHINTN.IPL;1", + "\\DATA\\MAPS\\WASHINTS\\WASHINTS.IPL;1", + "\\DATA\\MAPS\\OCEANDRV\\OCEANDRV.IPL;1", + "\\DATA\\MAPS\\OCEANDN\\OCEANDN.IPL;1", + "\\DATA\\MAPS\\GOLF\\GOLF.IPL;1", + "\\DATA\\MAPS\\BRIDGE\\BRIDGE.IPL;1", + "\\DATA\\MAPS\\STARISL\\STARISL.IPL;1", + "\\DATA\\MAPS\\NBEACHBT\\NBEACHBT.IPL;1", + "\\DATA\\MAPS\\NBEACH\\NBEACH.IPL;1", + "\\DATA\\MAPS\\NBEACHW\\NBEACHW.IPL;1", + "\\DATA\\MAPS\\CISLAND\\CISLAND.IPL;1", + "\\DATA\\MAPS\\AIRPORT\\AIRPORT.IPL;1", + "\\DATA\\MAPS\\HAITI\\HAITI.IPL;1", + "\\DATA\\MAPS\\HAITIN\\HAITIN.IPL;1", + "\\DATA\\MAPS\\ISLANDSF\\ISLANDSF.IPL;1", + "\\DATA\\MAPS\\BANK\\BANK.IPL;1", + "\\DATA\\MAPS\\MALL\\MALL.IPL;1", + "\\DATA\\MAPS\\YACHT\\YACHT.IPL;1", + "\\DATA\\MAPS\\CLUB\\CLUB.IPL;1", + "\\DATA\\MAPS\\HOTEL\\HOTEL.IPL;1", + "\\DATA\\MAPS\\LAWYERS\\LAWYERS.IPL;1", + "\\DATA\\MAPS\\STRIPCLB\\STRIPCLB.IPL;1", + "\\DATA\\MAPS\\CONCERTH\\CONCERTH.IPL;1", + "\\DATA\\MAPS\\MANSION\\MANSION.IPL;1", + "\\DATA\\MAPS\\GENERIC.IDE;1", + "\\DATA\\OCCLU.IPL;1", + "\\DATA\\MAPS\\PATHS.IPL;1", + "\\TXD\\LOADSC0.TXD;1", + "\\TXD\\LOADSC1.TXD;1", + "\\TXD\\LOADSC2.TXD;1", + "\\TXD\\LOADSC3.TXD;1", + "\\TXD\\LOADSC4.TXD;1", + "\\TXD\\LOADSC5.TXD;1", + "\\TXD\\LOADSC6.TXD;1", + "\\TXD\\LOADSC7.TXD;1", + "\\TXD\\LOADSC8.TXD;1", + "\\TXD\\LOADSC9.TXD;1", + "\\TXD\\LOADSC10.TXD;1", + "\\TXD\\LOADSC11.TXD;1", + "\\TXD\\LOADSC12.TXD;1", + "\\TXD\\LOADSC13.TXD;1", + "\\TXD\\SPLASH1.TXD;1" + }; + + for ( int32 i = 0; i < ARRAY_SIZE(files); i++ ) + SkyRegisterFileOnCd([i]); +#endif + +#ifdef GTA_PS2 + AddIntcHandler(INTC_VBLANK_S, VBlankCounter, 0); +#endif + +#ifdef GTA_PS2 + sceCdCLOCK rtc; + sceCdReadClock(&rtc); + uint32 seed = rtc.minute + rtc.day; + uint32 seed2 = (seed << 4)-seed; + uint32 seed3 = (seed2 << 4)-seed2; + srand ((seed3<<4)+rtc.second); +#else + //TODO: mysrand(); +#endif + + // gameAlreadyInitialised = true; // why is this gone? + } +} + +int32 SkipAllMPEGs; +int32 gMemoryStickLoadOK; + +void PlayIntroMPEGs() +{ +#ifdef GTA_PS2 + if (gameAlreadyInitialised) + RpSkySuspend(); + + InitMPEGPlayer(); + + float skipTime; // wrong type, should be int +#ifdef GTA_PAL + if(gMemoryStickLoadOK) + skipTime = 2500000; + else + skipTime = 5300000; + + if(!SkipAllMPEGs) + PlayMPEG("cdrom0:\\MOVIES\\VCPAL.PSS;1", false, unk); + + if(!SkipAllMPEGs){ + SkipAllMPEGs = true; + PlayMPEG("cdrom0:\\MOVIES\\VICEPAL.PSS;1", true, 0); + } +#else + if(gMemoryStickLoadOK) + skipTime = 2750000; + else + skipTime = 5500000; + + if(!SkipAllMPEGs) + PlayMPEG("cdrom0:\\MOVIES\\VCNTSC.PSS;1", false, unk); + + if(!SkipAllMPEGs){ + SkipAllMPEGs = true; + PlayMPEG("cdrom0:\\MOVIES\\VICE.PSS;1", true, 0); + } +#endif + + ShutdownMPEGPlayer(); + + if ( gameAlreadyInitialised ) + RpSkyResume(); +#else + //TODO +#endif +} + +int +main(int argc, char *argv[]) +{ +#ifdef __MWERKS__ + mwInit(); // metrowerks initialisation +#endif + + SystemInit(); + + if(RsEventHandler(rsINITIALIZE, nil) == rsEVENTERROR) + return 0; + +#ifdef GTA_PS2 + int32 r = TheMemoryCard.CheckCardStateAtGameStartUp(CARD_ONE); + + if ( r == CMemoryCard::ERR_DIRNOENTRY || r == CMemoryCard::ERR_NOFORMAT ) + { + GameInit(true); + + TheText.Unload(); + TheText.Load(); + + CFont::Initialise(); + + FrontEndMenuManager.DrawMemoryCardStartUpMenus(); + }else if(r == CMemoryCard::ERR_OPENNOENTRY) + gMemoryStickLoadOK = false; + else if(r == CMemoryCard::ERR_NONE) + gMemoryStickLoadOK = true; +#endif + + PlayIntroMPEGs(); + + GameInit(false); + + frameCount = 0; + while(frameCount < 100); + + CGame::InitialiseOnceAfterRW(); + + TheGame(); + +#if 0 // maybe ifndef FINAL or MASTER? + CGame::ShutDown(); + + RwEngineStop(); + RwEngineClose(); + RwEngineTerm(); + +#ifdef __MWERKS__ + mwExit(); // metrowerks shutdown +#endif +#endif + return 0; +} +#endif diff --git a/src/miami/core/main.h b/src/miami/core/main.h new file mode 100644 index 00000000..803afb14 --- /dev/null +++ b/src/miami/core/main.h @@ -0,0 +1,76 @@ +#pragma once + +#ifndef FINAL +// defined in RwHelpder.cpp +void PushRendergroup(const char *name); +void PopRendergroup(void); +#define PUSH_RENDERGROUP(str) PushRendergroup(str) +#define POP_RENDERGROUP() PopRendergroup() +#else +#define PUSH_RENDERGROUP(str) +#define POP_RENDERGROUP() +#endif + +struct GlobalScene +{ + RpWorld *world; + RwCamera *camera; +}; +extern GlobalScene Scene; + +extern uint8 work_buff[55000]; +extern char gString[256]; +extern char gString2[512]; +extern wchar gUString[256]; +extern wchar gUString2[256]; +extern bool gbPrintShite; +extern bool gbModelViewer; +#ifdef TIMEBARS +extern bool gbShowTimebars; +#else +#define gbShowTimebars false +#endif + +#ifndef FINAL +extern bool gbPrintMemoryUsage; +#endif + +class CSprite2d; + +bool DoRWStuffStartOfFrame(int16 TopRed, int16 TopGreen, int16 TopBlue, int16 BottomRed, int16 BottomGreen, int16 BottomBlue, int16 Alpha); +bool DoRWStuffStartOfFrame_Horizon(int16 TopRed, int16 TopGreen, int16 TopBlue, int16 BottomRed, int16 BottomGreen, int16 BottomBlue, int16 Alpha); +void DoRWStuffEndOfFrame(void); +void PreAllocateRwObjects(void); +void InitialiseGame(void); +void LoadingScreen(const char *str1, const char *str2, const char *splashscreen); +void LoadingIslandScreen(const char *levelName); +CSprite2d *LoadSplash(const char *name); +void DestroySplashScreen(void); +Const char *GetLevelSplashScreen(int level); +Const char *GetRandomSplashScreen(void); +void LittleTest(void); +void ValidateVersion(); +void ResetLoadingScreenBar(void); +#ifndef MASTER +void TheModelViewer(void); +#endif + +#ifdef LOAD_INI_SETTINGS +bool LoadINISettings(); +void SaveINISettings(); +void LoadINIControllerSettings(); +void SaveINIControllerSettings(); +#endif + +#ifdef NEW_RENDERER +extern bool gbNewRenderer; +bool FredIsInFirstPersonCam(void); +#endif + +#ifdef DRAW_GAME_VERSION_TEXT +extern bool gbDrawVersionText; +#endif + +#ifdef NO_MOVIES +extern bool gbNoMovies; +#endif diff --git a/src/miami/core/obrstr.cpp b/src/miami/core/obrstr.cpp new file mode 100644 index 00000000..d9f7e9b4 --- /dev/null +++ b/src/miami/core/obrstr.cpp @@ -0,0 +1,119 @@ +#include "common.h" +#include "Debug.h" +#include "obrstr.h" + +char obrstr[128]; +char obrstr2[128]; + +void ObrInt(int32 n1) +{ + IntToStr(n1, obrstr); + CDebug::DebugAddText(obrstr); +} + +void ObrInt2(int32 n1, int32 n2) +{ + IntToStr(n1, obrstr); + strcat(obrstr, " "); + IntToStr(n2, obrstr2); + strcat(obrstr, obrstr2); + CDebug::DebugAddText(obrstr); +} + +void ObrInt3(int32 n1, int32 n2, int32 n3) +{ + IntToStr(n1, obrstr); + strcat(obrstr, " "); + IntToStr(n2, obrstr2); + strcat(obrstr, obrstr2); + strcat(obrstr, " "); + IntToStr(n3, obrstr2); + strcat(obrstr, obrstr2); + CDebug::DebugAddText(obrstr); +} + +void ObrInt4(int32 n1, int32 n2, int32 n3, int32 n4) +{ + IntToStr(n1, obrstr); + strcat(obrstr, " "); + IntToStr(n2, obrstr2); + strcat(obrstr, obrstr2); + strcat(obrstr, " "); + IntToStr(n3, obrstr2); + strcat(obrstr, obrstr2); + strcat(obrstr, " "); + IntToStr(n4, obrstr2); + strcat(obrstr, obrstr2); + CDebug::DebugAddText(obrstr); +} + +void ObrInt5(int32 n1, int32 n2, int32 n3, int32 n4, int32 n5) +{ + IntToStr(n1, obrstr); + strcat(obrstr, " "); + IntToStr(n2, obrstr2); + strcat(obrstr, obrstr2); + strcat(obrstr, " "); + IntToStr(n3, obrstr2); + strcat(obrstr, obrstr2); + strcat(obrstr, " "); + IntToStr(n4, obrstr2); + strcat(obrstr, obrstr2); + strcat(obrstr, " "); + IntToStr(n5, obrstr2); + strcat(obrstr, obrstr2); + CDebug::DebugAddText(obrstr); +} + +void ObrInt6(int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6) +{ + IntToStr(n1, obrstr); + strcat(obrstr, " "); + IntToStr(n2, obrstr2); + strcat(obrstr, obrstr2); + strcat(obrstr, " "); + IntToStr(n3, obrstr2); + strcat(obrstr, obrstr2); + strcat(obrstr, " "); + IntToStr(n4, obrstr2); + strcat(obrstr, obrstr2); + strcat(obrstr, " "); + IntToStr(n5, obrstr2); + strcat(obrstr, obrstr2); + strcat(obrstr, " "); + IntToStr(n6, obrstr2); + strcat(obrstr, obrstr2); + CDebug::DebugAddText(obrstr); +} + +void IntToStr(int32 inNum, char *outStr) +{ + bool isNeg = inNum < 0; + + if (isNeg) { + inNum = -inNum; + *outStr = '-'; + } + + int16 digits = 1; + + if (inNum > 9) { + int32 _inNum = inNum; + do { + digits++; + _inNum /= 10; + } while (_inNum > 9); + } + + int32 strSize = digits; + if (isNeg) + strSize++; + + char *pStr = &outStr[strSize]; + int32 i = 0; + do { + *(pStr-- - 1) = (inNum % 10) + '0'; + inNum /= 10; + } while (++i < strSize); + outStr[strSize] = '\0'; +} \ No newline at end of file diff --git a/src/miami/core/obrstr.h b/src/miami/core/obrstr.h new file mode 100644 index 00000000..c1633614 --- /dev/null +++ b/src/miami/core/obrstr.h @@ -0,0 +1,9 @@ +#pragma once + +void ObrInt(int32 n1); +void ObrInt2(int32 n1, int32 n2); +void ObrInt3(int32 n1, int32 n2, int32 n3); +void ObrInt4(int32 n1, int32 n2, int32 n3, int32 n4); +void ObrInt5(int32 n1, int32 n2, int32 n3, int32 n4, int32 n5); +void ObrInt6(int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6); +void IntToStr(int32 inNum, char *outStr); \ No newline at end of file diff --git a/src/miami/core/re3.cpp b/src/miami/core/re3.cpp new file mode 100644 index 00000000..d3a6b471 --- /dev/null +++ b/src/miami/core/re3.cpp @@ -0,0 +1,1329 @@ +#include +#include +#define WITHWINDOWS +#include "common.h" +#if defined DETECT_JOYSTICK_MENU && defined XINPUT +#include +#if !defined(PSAPI_VERSION) || (PSAPI_VERSION > 1) +#pragma comment( lib, "Xinput9_1_0.lib" ) +#else +#pragma comment( lib, "Xinput.lib" ) +#endif +#endif +#include "Renderer.h" +#include "Occlusion.h" +#include "Credits.h" +#include "Camera.h" +#include "Weather.h" +#include "Timecycle.h" +#include "Clock.h" +#include "World.h" +#include "Vehicle.h" +#include "ModelIndices.h" +#include "Streaming.h" +#include "Boat.h" +#include "Heli.h" +#include "Automobile.h" +#include "Bike.h" +#include "Console.h" +#include "Debug.h" +#include "Hud.h" +#include "SceneEdit.h" +#include "Pad.h" +#include "PlayerPed.h" +#include "Radar.h" +#include "debugmenu.h" +#include "Frontend.h" +#include "WaterLevel.h" +#include "main.h" +#include "Script.h" +#include "MBlur.h" +#include "postfx.h" +#include "custompipes.h" +#include "MemoryHeap.h" +#include "FileMgr.h" +#include "Camera.h" +#include "MBlur.h" +#include "ControllerConfig.h" +#include "CarCtrl.h" +#include "Population.h" +#include "IniFile.h" +#include "Zones.h" + +#include "crossplatform.h" + +#ifndef _WIN32 +#include "assert.h" +#include +#endif + +#include + +#ifdef RWLIBS +extern "C" int vsprintf(char* const _Buffer, char const* const _Format, va_list _ArgList); +#endif + + +#ifdef USE_PS2_RAND +unsigned long long myrand_seed = 1; +#else +unsigned long int myrand_seed = 1; +#endif + +int +myrand(void) +{ +#ifdef USE_PS2_RAND + // Use our own implementation of rand, stolen from PS2 + myrand_seed = 0x5851F42D4C957F2D * myrand_seed + 1; + return ((myrand_seed >> 32) & 0x7FFFFFFF); +#else + // or original codewarrior rand + myrand_seed = myrand_seed * 1103515245 + 12345; + return((myrand_seed >> 16) & 0x7FFF); +#endif +} + +void +mysrand(unsigned int seed) +{ + myrand_seed = seed; +} + +#ifdef CUSTOM_FRONTEND_OPTIONS +#include "frontendoption.h" + + + +#ifdef MORE_LANGUAGES +void LangPolSelect(int8 action) +{ + if (action == FEOPTION_ACTION_SELECT) { + FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_POLISH; + FrontEndMenuManager.m_bFrontEnd_ReloadObrTxtGxt = true; + FrontEndMenuManager.InitialiseChangedLanguageSettings(); + FrontEndMenuManager.SaveSettings(); + } +} + +void LangRusSelect(int8 action) +{ + if (action == FEOPTION_ACTION_SELECT) { + FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_RUSSIAN; + FrontEndMenuManager.m_bFrontEnd_ReloadObrTxtGxt = true; + FrontEndMenuManager.InitialiseChangedLanguageSettings(); + FrontEndMenuManager.SaveSettings(); + } +} + +void LangJapSelect(int8 action) +{ + if (action == FEOPTION_ACTION_SELECT) { + FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_JAPANESE; + FrontEndMenuManager.m_bFrontEnd_ReloadObrTxtGxt = true; + FrontEndMenuManager.InitialiseChangedLanguageSettings(); + FrontEndMenuManager.SaveSettings(); + } +} +#endif + +void +CustomFrontendOptionsPopulate(void) +{ + // Most of custom options are done statically in MenuScreensCustom.cpp, we add them here only if they're dependent to extra files + + int fd; + // These work only if we have neo folder, so they're dynamically added +#ifdef EXTENDED_PIPELINES + const char *vehPipelineNames[] = { "FED_MFX", "FED_NEO" }; + const char *off_on[] = { "FEM_OFF", "FEM_ON" }; + fd = CFileMgr::OpenFile("neo/neo.txd","r"); + if (fd) { +#ifdef GRAPHICS_MENU_OPTIONS + FrontendOptionSetCursor(MENUPAGE_GRAPHICS_SETTINGS, -3, false); + FrontendOptionAddSelect("FED_VPL", 0, 0, MENUALIGN_LEFT, vehPipelineNames, ARRAY_SIZE(vehPipelineNames), (int8*)&CustomPipes::VehiclePipeSwitch, false, nil, "Graphics", "VehiclePipeline"); + FrontendOptionAddSelect("FED_PRM", 0, 0, MENUALIGN_LEFT, off_on, 2, (int8*)&CustomPipes::RimlightEnable, false, nil, "Graphics", "NeoRimLight"); + FrontendOptionAddSelect("FED_WLM", 0, 0, MENUALIGN_LEFT, off_on, 2, (int8*)&CustomPipes::LightmapEnable, false, nil, "Graphics", "NeoLightMaps"); + FrontendOptionAddSelect("FED_RGL", 0, 0, MENUALIGN_LEFT, off_on, 2, (int8*)&CustomPipes::GlossEnable, false, nil, "Graphics", "NeoRoadGloss"); +#else + FrontendOptionSetCursor(MENUPAGE_DISPLAY_SETTINGS, -3, false); + FrontendOptionAddSelect("FED_VPL", 0, 0, MENUALIGN_LEFT, vehPipelineNames, ARRAY_SIZE(vehPipelineNames), (int8*)&CustomPipes::VehiclePipeSwitch, false, nil, "Graphics", "VehiclePipeline"); + FrontendOptionAddSelect("FED_PRM", 0, 0, MENUALIGN_LEFT, off_on, 2, (int8*)&CustomPipes::RimlightEnable, false, nil, "Graphics", "NeoRimLight"); + FrontendOptionAddSelect("FED_WLM", 0, 0, MENUALIGN_LEFT, off_on, 2, (int8*)&CustomPipes::LightmapEnable, false, nil, "Graphics", "NeoLightMaps"); + FrontendOptionAddSelect("FED_RGL", 0, 0, MENUALIGN_LEFT, off_on, 2, (int8*)&CustomPipes::GlossEnable, false, nil, "Graphics", "NeoRoadGloss"); +#endif + CFileMgr::CloseFile(fd); + } +#endif + // Add outsourced language translations, if files are found +#ifdef MORE_LANGUAGES + int fd2; + FrontendOptionSetCursor(MENUPAGE_LANGUAGE_SETTINGS, 5, false); +#if 0 + if (fd = CFileMgr::OpenFile("text/polish.gxt")) { + if (fd2 = CFileMgr::OpenFile("models/fonts_p.txd")) { + FrontendOptionAddDynamic("FEL_POL", 0, 0, MENUALIGN_CENTER, nil, nil, LangPolSelect, nil, nil); + CFileMgr::CloseFile(fd2); + } + CFileMgr::CloseFile(fd); + } +#endif + + if (fd = CFileMgr::OpenFile("text/russian.gxt")) { + if (fd2 = CFileMgr::OpenFile("models/fonts_r.txd")) { + FrontendOptionAddDynamic("FEL_RUS", 0, 0, MENUALIGN_CENTER, nil, nil, LangRusSelect, nil, nil); + CFileMgr::CloseFile(fd2); + } + CFileMgr::CloseFile(fd); + } + +#if 0 + if (fd = CFileMgr::OpenFile("text/japanese.gxt")) { + if (fd2 = CFileMgr::OpenFile("models/fonts_j.txd")) { + FrontendOptionAddDynamic("FEL_JAP", 0, 0, MENUALIGN_CENTER, nil, nil, LangJapSelect, nil, nil); + CFileMgr::CloseFile(fd2); + } + CFileMgr::CloseFile(fd); + } +#endif +#endif + +} +#endif + +#ifdef LOAD_INI_SETTINGS +#define MINI_CASE_SENSITIVE +#include "ini.h" + +mINI::INIFile ini("reVC.ini"); +mINI::INIStructure cfg; + +bool ReadIniIfExists(const char *cat, const char *key, uint32 *out) +{ + mINI::INIMap section = cfg.get(cat); + if (section.has(key)) { + char *endPtr; + *out = strtoul(section.get(key).c_str(), &endPtr, 0); + return true; + } + return false; +} + +bool ReadIniIfExists(const char *cat, const char *key, uint8 *out) +{ + mINI::INIMap section = cfg.get(cat); + if (section.has(key)) { + char *endPtr; + *out = strtoul(section.get(key).c_str(), &endPtr, 0); + return true; + } + return false; +} + +bool ReadIniIfExists(const char *cat, const char *key, bool *out) +{ + mINI::INIMap section = cfg.get(cat); + if (section.has(key)) { + char *endPtr; + *out = strtoul(section.get(key).c_str(), &endPtr, 0); + return true; + } + return false; +} + +bool ReadIniIfExists(const char *cat, const char *key, int32 *out) +{ + mINI::INIMap section = cfg.get(cat); + if (section.has(key)) { + char *endPtr; + *out = strtol(section.get(key).c_str(), &endPtr, 0); + return true; + } + return false; +} + +bool ReadIniIfExists(const char *cat, const char *key, int8 *out) +{ + mINI::INIMap section = cfg.get(cat); + if (section.has(key)) { + char *endPtr; + *out = strtol(section.get(key).c_str(), &endPtr, 0); + return true; + } + return false; +} + +bool ReadIniIfExists(const char *cat, const char *key, float *out) +{ + mINI::INIMap section = cfg.get(cat); + if (section.has(key)) { + char *endPtr; + *out = strtof(section.get(key).c_str(), &endPtr); + return true; + } + return false; +} + +bool ReadIniIfExists(const char *cat, const char *key, char *out, int size) +{ + mINI::INIMap section = cfg.get(cat); + if (section.has(key)) { + strncpy(out, section.get(key).c_str(), size - 1); + out[size - 1] = '\0'; + return true; + } + return false; +} + +void StoreIni(const char *cat, const char *key, uint32 val) +{ + char temp[11]; + sprintf(temp, "%u", val); + cfg[cat][key] = temp; +} + +void StoreIni(const char *cat, const char *key, uint8 val) +{ + char temp[11]; + sprintf(temp, "%u", val); + cfg[cat][key] = temp; +} + +void StoreIni(const char *cat, const char *key, int32 val) +{ + char temp[11]; + sprintf(temp, "%d", val); + cfg[cat][key] = temp; +} + +void StoreIni(const char *cat, const char *key, int8 val) +{ + char temp[11]; + sprintf(temp, "%d", val); + cfg[cat][key] = temp; +} +void StoreIni(const char *cat, const char *key, bool val) +{ + char temp[11]; + sprintf(temp, "%d", val); + cfg[cat][key] = temp; +} + +void StoreIni(const char *cat, const char *key, float val) +{ + char temp[50]; + sprintf(temp, "%f", val); + cfg[cat][key] = temp; +} + +void StoreIni(const char *cat, const char *key, char *val, int size) +{ + cfg[cat][key] = val; +} + +const char *iniControllerActions[] = { "PED_FIREWEAPON", "PED_CYCLE_WEAPON_RIGHT", "PED_CYCLE_WEAPON_LEFT", "GO_FORWARD", "GO_BACK", "GO_LEFT", "GO_RIGHT", "PED_SNIPER_ZOOM_IN", + "PED_SNIPER_ZOOM_OUT", "VEHICLE_ENTER_EXIT", "CAMERA_CHANGE_VIEW_ALL_SITUATIONS", "PED_JUMPING", "PED_SPRINT", "PED_LOOKBEHIND", "PED_DUCK", "PED_ANSWER_PHONE", +#ifdef BIND_VEHICLE_FIREWEAPON + "VEHICLE_FIREWEAPON", +#endif + "VEHICLE_ACCELERATE", "VEHICLE_BRAKE", "VEHICLE_CHANGE_RADIO_STATION", "VEHICLE_HORN", "TOGGLE_SUBMISSIONS", "VEHICLE_HANDBRAKE", "PED_1RST_PERSON_LOOK_LEFT", + "PED_1RST_PERSON_LOOK_RIGHT", "VEHICLE_LOOKLEFT", "VEHICLE_LOOKRIGHT", "VEHICLE_LOOKBEHIND", "VEHICLE_TURRETLEFT", "VEHICLE_TURRETRIGHT", "VEHICLE_TURRETUP", "VEHICLE_TURRETDOWN", + "PED_CYCLE_TARGET_LEFT", "PED_CYCLE_TARGET_RIGHT", "PED_CENTER_CAMERA_BEHIND_PLAYER", "PED_LOCK_TARGET", "NETWORK_TALK", "PED_1RST_PERSON_LOOK_UP", "PED_1RST_PERSON_LOOK_DOWN", + "_CONTROLLERACTION_36", "TOGGLE_DPAD", "SWITCH_DEBUG_CAM_ON", "TAKE_SCREEN_SHOT", "SHOW_MOUSE_POINTER_TOGGLE", "UNKNOWN_ACTION" }; + +const char *iniControllerTypes[] = { "kbd:", "2ndKbd:", "mouse:", "joy:" }; + +const char *iniMouseButtons[] = {"LEFT","MIDDLE","RIGHT","WHLUP","WHLDOWN","X1","X2"}; + +const char *iniKeyboardButtons[] = {"ESC","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12", + "INS","DEL","HOME","END","PGUP","PGDN","UP","DOWN","LEFT","RIGHT","DIVIDE","TIMES","PLUS","MINUS","PADDEL", + "PADEND","PADDOWN","PADPGDN","PADLEFT","PAD5","NUMLOCK","PADRIGHT","PADHOME","PADUP","PADPGUP","PADINS", + "PADENTER", "SCROLL","PAUSE","BACKSP","TAB","CAPSLK","ENTER","LSHIFT","RSHIFT","SHIFT","LCTRL","RCTRL","LALT", + "RALT", "LWIN", "RWIN", "APPS", "NULL"}; + +void LoadINIControllerSettings() +{ +#ifdef DETECT_JOYSTICK_MENU +#ifdef XINPUT + int storedJoy1 = -1; + if (ReadIniIfExists("Controller", "JoystickName", &storedJoy1)) { + CPad::XInputJoy1 = -1; + CPad::XInputJoy2 = -1; + XINPUT_STATE xstate; + memset(&xstate, 0, sizeof(XINPUT_STATE)); + + // Firstly confirm & set joy 1 + if (XInputGetState(storedJoy1, &xstate) == ERROR_SUCCESS) { + CPad::XInputJoy1 = storedJoy1; + } + + for (int i = 0; i <= 3; i++) { + if (XInputGetState(i, &xstate) == ERROR_SUCCESS) { + if (CPad::XInputJoy1 == -1) + CPad::XInputJoy1 = i; + else if (CPad::XInputJoy2 == -1 && i != CPad::XInputJoy1) + CPad::XInputJoy2 = i; + } + } + + // There is no plug event on XInput, so let's leave XInputJoy1/2 as 0/1 respectively, and hotplug will be possible. + if (CPad::XInputJoy1 == -1) { + CPad::XInputJoy1 = 0; + CPad::XInputJoy2 = 1; + } else if (CPad::XInputJoy2 == -1) { + CPad::XInputJoy2 = (CPad::XInputJoy1 + 1) % 4; + } + } +#else + ReadIniIfExists("Controller", "JoystickName", gSelectedJoystickName, 128); +#endif +#endif + // force to default GTA behaviour (never overwrite bindings on joy change/initialization) if user init'ed/set bindings before we introduced that + if (!ReadIniIfExists("Controller", "PadButtonsInited", &ControlsManager.ms_padButtonsInited)) { + ControlsManager.ms_padButtonsInited = cfg.get("Bindings").size() != 0 ? 16 : 0; + } + + for (int32 i = 0; i < MAX_CONTROLLERACTIONS; i++) { + char value[128]; + if (ReadIniIfExists("Bindings", iniControllerActions[i], value, 128)) { + for (int32 j = 0; j < MAX_CONTROLLERTYPES; j++){ + ControlsManager.ClearSettingsAssociatedWithAction((e_ControllerAction)i, (eControllerType)j); + } + + for (char *binding = strtok(value,", "); binding != nil; binding = strtok(nil, ", ")) { + int contType = -1; + for (int32 k = 0; k < ARRAY_SIZE(iniControllerTypes); k++) { + int len = strlen(iniControllerTypes[k]); + if (strncmp(binding, iniControllerTypes[k], len) == 0) { + contType = k; + binding += len; + break; + } + } + if (contType == -1) + continue; + + int contKey; + if (contType == JOYSTICK) { + char *temp; + contKey = strtol(binding, &temp, 0); + + } else if (contType == KEYBOARD || contType == OPTIONAL_EXTRA) { + if (strlen(binding) == 1) { + contKey = binding[0]; + } else if(strcmp(binding, "SPC") == 0) { + contKey = ' '; + } else { + for (int32 k = 0; k < ARRAY_SIZE(iniKeyboardButtons); k++) { + if(strcmp(binding, iniKeyboardButtons[k]) == 0) { + contKey = 1000 + k; + break; + } + } + } + } else if (contType == MOUSE) { + for (int32 k = 0; k < ARRAY_SIZE(iniMouseButtons); k++) { + if(strcmp(binding, iniMouseButtons[k]) == 0) { + contKey = 1 + k; + break; + } + } + } + + ControlsManager.SetControllerKeyAssociatedWithAction((e_ControllerAction)i, contKey, (eControllerType)contType); + } + } + } +} + +void SaveINIControllerSettings() +{ + for (int32 i = 0; i < MAX_CONTROLLERACTIONS; i++) { + char value[128] = { '\0' }; + + // upper limit should've been GetNumOfSettingsForAction(i), but sadly even R* doesn't use it's own system correctly, and there are gaps between orders. + for (int32 j = SETORDER_1; j < MAX_SETORDERS; j++){ + + // We respect the m_ContSetOrder, and join/implode/order the bindings according to that; using comma as seperator. + for (int32 k = 0; k < MAX_CONTROLLERTYPES; k++){ + if (ControlsManager.m_aSettings[i][k].m_ContSetOrder == j) { + char next[32]; + if (k == JOYSTICK) { + snprintf(next, 32, "%s%d,", iniControllerTypes[k], ControlsManager.m_aSettings[i][k].m_Key); + + } else if (k == KEYBOARD || k == OPTIONAL_EXTRA) { + if (ControlsManager.m_aSettings[i][k].m_Key == ' ') + snprintf(next, 32, "%sSPC,", iniControllerTypes[k]); + else if (ControlsManager.m_aSettings[i][k].m_Key < 256) + snprintf(next, 32, "%s%c,", iniControllerTypes[k], ControlsManager.m_aSettings[i][k].m_Key); + else + snprintf(next, 32, "%s%s,", iniControllerTypes[k], iniKeyboardButtons[ControlsManager.m_aSettings[i][k].m_Key - 1000]); + + } else if (k == MOUSE) { + snprintf(next, 32, "%s%s,", iniControllerTypes[k], iniMouseButtons[ControlsManager.m_aSettings[i][k].m_Key - 1]); + } + strcat(value, next); + break; + } + } + } + int len = strlen(value); + if (len > 0) + value[len - 1] = '\0'; // to remove comma + + StoreIni("Bindings", iniControllerActions[i], value, 128); + } + +#ifdef DETECT_JOYSTICK_MENU +#ifdef XINPUT + StoreIni("Controller", "JoystickName", CPad::XInputJoy1); +#else + StoreIni("Controller", "JoystickName", gSelectedJoystickName, 128); +#endif +#endif + StoreIni("Controller", "PadButtonsInited", ControlsManager.ms_padButtonsInited); + + ini.write(cfg); +} + +bool LoadINISettings() +{ + if (!ini.read(cfg)) + return false; + +#ifdef IMPROVED_VIDEOMODE + ReadIniIfExists("VideoMode", "Width", &FrontEndMenuManager.m_nPrefsWidth); + ReadIniIfExists("VideoMode", "Height", &FrontEndMenuManager.m_nPrefsHeight); + ReadIniIfExists("VideoMode", "Depth", &FrontEndMenuManager.m_nPrefsDepth); + ReadIniIfExists("VideoMode", "Subsystem", &FrontEndMenuManager.m_nPrefsSubsystem); + // Windowed mode is loaded below in CUSTOM_FRONTEND_OPTIONS section +#else + ReadIniIfExists("Graphics", "VideoMode", &FrontEndMenuManager.m_nDisplayVideoMode); +#endif + ReadIniIfExists("Controller", "HeadBob1stPerson", &TheCamera.m_bHeadBob); + ReadIniIfExists("Controller", "HorizantalMouseSens", &TheCamera.m_fMouseAccelHorzntl); + ReadIniIfExists("Controller", "InvertMouseVertically", &MousePointerStateHelper.bInvertVertically); + ReadIniIfExists("Controller", "DisableMouseSteering", &CVehicle::m_bDisableMouseSteering); + ReadIniIfExists("Controller", "Vibration", &FrontEndMenuManager.m_PrefsUseVibration); + ReadIniIfExists("Audio", "SfxVolume", &FrontEndMenuManager.m_PrefsSfxVolume); + ReadIniIfExists("Audio", "MusicVolume", &FrontEndMenuManager.m_PrefsMusicVolume); + ReadIniIfExists("Audio", "MP3BoostVolume", &FrontEndMenuManager.m_PrefsMP3BoostVolume); + ReadIniIfExists("Audio", "Radio", &FrontEndMenuManager.m_PrefsRadioStation); +#ifdef EXTERNAL_3D_SOUND + ReadIniIfExists("Audio", "SpeakerType", &FrontEndMenuManager.m_PrefsSpeakers); + ReadIniIfExists("Audio", "Provider", &FrontEndMenuManager.m_nPrefsAudio3DProviderIndex); +#endif + ReadIniIfExists("Audio", "DynamicAcoustics", &FrontEndMenuManager.m_PrefsDMA); + ReadIniIfExists("Display", "Brightness", &FrontEndMenuManager.m_PrefsBrightness); + ReadIniIfExists("Display", "DrawDistance", &FrontEndMenuManager.m_PrefsLOD); + ReadIniIfExists("Display", "Subtitles", &FrontEndMenuManager.m_PrefsShowSubtitles); + ReadIniIfExists("Graphics", "AspectRatio", &FrontEndMenuManager.m_PrefsUseWideScreen); + ReadIniIfExists("Graphics", "FrameLimiter", &FrontEndMenuManager.m_PrefsFrameLimiter); +#ifdef LEGACY_MENU_OPTIONS + ReadIniIfExists("Graphics", "VSync", &FrontEndMenuManager.m_PrefsVsyncDisp); + ReadIniIfExists("Graphics", "Trails", &CMBlur::BlurOn); +#endif + ReadIniIfExists("General", "SkinFile", FrontEndMenuManager.m_PrefsSkinFile, 256); + ReadIniIfExists("Controller", "Method", &FrontEndMenuManager.m_ControlMethod); + ReadIniIfExists("General", "Language", &FrontEndMenuManager.m_PrefsLanguage); + ReadIniIfExists("Display", "ShowHud", &FrontEndMenuManager.m_PrefsShowHud); + ReadIniIfExists("Display", "RadarMode", &FrontEndMenuManager.m_PrefsRadarMode); + ReadIniIfExists("Display", "ShowLegends", &FrontEndMenuManager.m_PrefsShowLegends); + +#ifdef EXTENDED_COLOURFILTER + ReadIniIfExists("CustomPipesValues", "PostFXIntensity", &CPostFX::Intensity); +#endif +#ifdef EXTENDED_PIPELINES + ReadIniIfExists("CustomPipesValues", "NeoVehicleShininess", &CustomPipes::VehicleShininess); + ReadIniIfExists("CustomPipesValues", "NeoVehicleSpecularity", &CustomPipes::VehicleSpecularity); + ReadIniIfExists("CustomPipesValues", "RimlightMult", &CustomPipes::RimlightMult); + ReadIniIfExists("CustomPipesValues", "LightmapMult", &CustomPipes::LightmapMult); + ReadIniIfExists("CustomPipesValues", "GlossMult", &CustomPipes::GlossMult); +#endif + ReadIniIfExists("Rendering", "BackfaceCulling", &gBackfaceCulling); +#ifdef NEW_RENDERER + ReadIniIfExists("Rendering", "NewRenderer", &gbNewRenderer); +#endif + +#ifdef PROPER_SCALING + ReadIniIfExists("Draw", "ProperScaling", &CDraw::ms_bProperScaling); +#endif +#ifdef FIX_RADAR + ReadIniIfExists("Draw", "FixRadar", &CDraw::ms_bFixRadar); +#endif +#ifdef FIX_SPRITES + ReadIniIfExists("Draw", "FixSprites", &CDraw::ms_bFixSprites); +#endif +#ifdef DRAW_GAME_VERSION_TEXT + ReadIniIfExists("General", "DrawVersionText", &gbDrawVersionText); +#endif +#ifdef NO_MOVIES + ReadIniIfExists("General", "NoMovies", &gbNoMovies); +#endif + +#ifdef CUSTOM_FRONTEND_OPTIONS + bool migrate = cfg.get("FrontendOptions").size() != 0; + for (int i = 0; i < MENUPAGES; i++) { + for (int j = 0; j < NUM_MENUROWS; j++) { + CMenuScreenCustom::CMenuEntry &option = aScreens[i].m_aEntries[j]; + if (option.m_Action == MENUACTION_NOTHING) + break; + + // CFO check + if (option.m_Action < MENUACTION_NOTHING && option.m_CFO->save) { + // Migrate from old .ini to new .ini + // Old values can only be int8, new ones can contain float if it is slider + if (migrate && ReadIniIfExists("FrontendOptions", option.m_CFO->save, (int8*)option.m_CFO->value)) + cfg["FrontendOptions"].remove(option.m_CFO->save); + else if (option.m_Action == MENUACTION_CFO_SLIDER) + ReadIniIfExists(option.m_CFO->saveCat, option.m_CFO->save, (float*)option.m_CFO->value); + else + ReadIniIfExists(option.m_CFO->saveCat, option.m_CFO->save, (int8*)option.m_CFO->value); + + if (option.m_Action == MENUACTION_CFO_SELECT) { + option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue = *(int8*)option.m_CFO->value; + } + } + } + } +#endif + + // Fetched in above block, but needs evaluation +#ifdef PED_CAR_DENSITY_SLIDERS + CPopulation::MaxNumberOfPedsInUse = DEFAULT_MAX_NUMBER_OF_PEDS * CIniFile::PedNumberMultiplier; + CPopulation::MaxNumberOfPedsInUseInterior = DEFAULT_MAX_NUMBER_OF_PEDS_INTERIOR * CIniFile::PedNumberMultiplier; + CCarCtrl::MaxNumberOfCarsInUse = DEFAULT_MAX_NUMBER_OF_CARS * CIniFile::CarNumberMultiplier; +#endif + + return true; +} + +void SaveINISettings() +{ +#ifdef IMPROVED_VIDEOMODE + StoreIni("VideoMode", "Width", FrontEndMenuManager.m_nPrefsWidth); + StoreIni("VideoMode", "Height", FrontEndMenuManager.m_nPrefsHeight); + StoreIni("VideoMode", "Depth", FrontEndMenuManager.m_nPrefsDepth); + StoreIni("VideoMode", "Subsystem", FrontEndMenuManager.m_nPrefsSubsystem); + // Windowed mode is loaded below in CUSTOM_FRONTEND_OPTIONS section +#else + StoreIni("Graphics", "VideoMode", FrontEndMenuManager.m_nDisplayVideoMode); +#endif + StoreIni("Controller", "HeadBob1stPerson", TheCamera.m_bHeadBob); + StoreIni("Controller", "HorizantalMouseSens", TheCamera.m_fMouseAccelHorzntl); + StoreIni("Controller", "InvertMouseVertically", MousePointerStateHelper.bInvertVertically); + StoreIni("Controller", "DisableMouseSteering", CVehicle::m_bDisableMouseSteering); + StoreIni("Controller", "Vibration", FrontEndMenuManager.m_PrefsUseVibration); + StoreIni("Audio", "SfxVolume", FrontEndMenuManager.m_PrefsSfxVolume); + StoreIni("Audio", "MusicVolume", FrontEndMenuManager.m_PrefsMusicVolume); + StoreIni("Audio", "MP3BoostVolume", FrontEndMenuManager.m_PrefsMP3BoostVolume); + StoreIni("Audio", "Radio", FrontEndMenuManager.m_PrefsRadioStation); +#ifdef EXTERNAL_3D_SOUND + StoreIni("Audio", "SpeakerType", FrontEndMenuManager.m_PrefsSpeakers); + StoreIni("Audio", "Provider", FrontEndMenuManager.m_nPrefsAudio3DProviderIndex); +#endif + StoreIni("Audio", "DynamicAcoustics", FrontEndMenuManager.m_PrefsDMA); + StoreIni("Display", "Brightness", FrontEndMenuManager.m_PrefsBrightness); + StoreIni("Display", "DrawDistance", FrontEndMenuManager.m_PrefsLOD); + StoreIni("Display", "Subtitles", FrontEndMenuManager.m_PrefsShowSubtitles); + StoreIni("Graphics", "AspectRatio", FrontEndMenuManager.m_PrefsUseWideScreen); +#ifdef LEGACY_MENU_OPTIONS + StoreIni("Graphics", "VSync", FrontEndMenuManager.m_PrefsVsyncDisp); + StoreIni("Graphics", "Trails", CMBlur::BlurOn); +#endif + StoreIni("Graphics", "FrameLimiter", FrontEndMenuManager.m_PrefsFrameLimiter); + StoreIni("General", "SkinFile", FrontEndMenuManager.m_PrefsSkinFile, 256); + StoreIni("Controller", "Method", FrontEndMenuManager.m_ControlMethod); + StoreIni("General", "Language", FrontEndMenuManager.m_PrefsLanguage); + StoreIni("Display", "ShowHud", FrontEndMenuManager.m_PrefsShowHud); + StoreIni("Display", "RadarMode", FrontEndMenuManager.m_PrefsRadarMode); + StoreIni("Display", "ShowLegends", FrontEndMenuManager.m_PrefsShowLegends); + +#ifdef EXTENDED_COLOURFILTER + StoreIni("CustomPipesValues", "PostFXIntensity", CPostFX::Intensity); +#endif +#ifdef EXTENDED_PIPELINES + StoreIni("CustomPipesValues", "NeoVehicleShininess", CustomPipes::VehicleShininess); + StoreIni("CustomPipesValues", "NeoVehicleSpecularity", CustomPipes::VehicleSpecularity); + StoreIni("CustomPipesValues", "RimlightMult", CustomPipes::RimlightMult); + StoreIni("CustomPipesValues", "LightmapMult", CustomPipes::LightmapMult); + StoreIni("CustomPipesValues", "GlossMult", CustomPipes::GlossMult); +#endif + StoreIni("Rendering", "BackfaceCulling", gBackfaceCulling); +#ifdef NEW_RENDERER + StoreIni("Rendering", "NewRenderer", gbNewRenderer); +#endif + +#ifdef PROPER_SCALING + StoreIni("Draw", "ProperScaling", CDraw::ms_bProperScaling); +#endif +#ifdef FIX_RADAR + StoreIni("Draw", "FixRadar", CDraw::ms_bFixRadar); +#endif +#ifdef FIX_SPRITES + StoreIni("Draw", "FixSprites", CDraw::ms_bFixSprites); +#endif +#ifdef DRAW_GAME_VERSION_TEXT + StoreIni("General", "DrawVersionText", gbDrawVersionText); +#endif +#ifdef NO_MOVIES + StoreIni("General", "NoMovies", gbNoMovies); +#endif +#ifdef CUSTOM_FRONTEND_OPTIONS + for (int i = 0; i < MENUPAGES; i++) { + for (int j = 0; j < NUM_MENUROWS; j++) { + CMenuScreenCustom::CMenuEntry &option = aScreens[i].m_aEntries[j]; + if (option.m_Action == MENUACTION_NOTHING) + break; + + if (option.m_Action < MENUACTION_NOTHING && option.m_CFO->save) { + if (option.m_Action == MENUACTION_CFO_SLIDER) + StoreIni(option.m_CFO->saveCat, option.m_CFO->save, *(float*)option.m_CFO->value); + else + StoreIni(option.m_CFO->saveCat, option.m_CFO->save, *(int8*)option.m_CFO->value); + } + } + } +#endif + + ini.write(cfg); +} + +#endif + +#ifdef DEBUGMENU +void WeaponCheat1(); +void WeaponCheat2(); +void WeaponCheat3(); +void HealthCheat(); +void VehicleCheat(int model); +void BlowUpCarsCheat(); +void ChangePlayerCheat(); +void MayhemCheat(); +void EverybodyAttacksPlayerCheat(); +void WeaponsForAllCheat(); +void FastTimeCheat(); +void SlowTimeCheat(); +void MoneyCheat(); +void ArmourCheat(); +void WantedLevelUpCheat(); +void WantedLevelDownCheat(); +void SunnyWeatherCheat(); +void CloudyWeatherCheat(); +void RainyWeatherCheat(); +void FoggyWeatherCheat(); +void FastWeatherCheat(); +void OnlyRenderWheelsCheat(); +void ChittyChittyBangBangCheat(); +void StrongGripCheat(); +void SpecialCarCheats(); +void PickUpChicksCheat(); + +DebugMenuEntry *carCol1; +DebugMenuEntry *carCol2; + +void +SpawnCar(int id) +{ + CVector playerpos; + CStreaming::RequestModel(id, 0); + CStreaming::LoadAllRequestedModels(false); + if(CStreaming::HasModelLoaded(id)){ + playerpos = FindPlayerCoors(); + int node; + if(!CModelInfo::IsBoatModel(id)){ + node = ThePaths.FindNodeClosestToCoors(playerpos, 0, 100.0f, false, false); + if(node < 0) + return; + } + + CVehicle *v; + if(CModelInfo::IsBoatModel(id)) + v = new CBoat(id, RANDOM_VEHICLE); + else if(CModelInfo::IsBikeModel(id)) + v = new CBike(id, RANDOM_VEHICLE); + else + v = new CAutomobile(id, RANDOM_VEHICLE); + + v->bHasBeenOwnedByPlayer = true; + if(carCol1) + DebugMenuEntrySetAddress(carCol1, &v->m_currentColour1); + if(carCol2) + DebugMenuEntrySetAddress(carCol2, &v->m_currentColour2); + + if(CModelInfo::IsBoatModel(id)) + v->SetPosition(TheCamera.GetPosition() + TheCamera.GetForward()*15.0f); + else + v->SetPosition(ThePaths.m_pathNodes[node].GetPosition()); + + v->GetMatrix().GetPosition().z += 4.0f; + v->SetOrientation(0.0f, 0.0f, 3.49f); + v->SetStatus(STATUS_ABANDONED); + v->m_nDoorLock = CARLOCK_UNLOCKED; + CWorld::Add(v); + } +} + +static void +FixCar(void) +{ + CVehicle *veh = FindPlayerVehicle(); + if(veh == nil) + return; + veh->m_fHealth = 1000.0f; + if(veh->IsCar()){ + ((CAutomobile*)veh)->Damage.SetEngineStatus(0); + ((CAutomobile*)veh)->Fix(); + }else if(veh->IsBike()){ + ((CBike*)veh)->Fix(); + } +} + +#ifdef MAP_ENHANCEMENTS +static void +TeleportToWaypoint(void) +{ + if (CRadar::TargetMarkerId == -1) + return; + CEntity* pEntityToTeleport = FindPlayerEntity(); + CVector vNewPos = CRadar::TargetMarkerPos; + CStreaming::LoadScene(vNewPos); + CStreaming::LoadSceneCollision(vNewPos); + vNewPos.z = CWorld::FindGroundZForCoord(vNewPos.x, vNewPos.y) + pEntityToTeleport->GetDistanceFromCentreOfMassToBaseOfModel(); + pEntityToTeleport->Teleport(vNewPos); +} +#endif + +static void +SwitchCarCollision(void) +{ + if (FindPlayerVehicle() && FindPlayerVehicle()->IsCar()) + FindPlayerVehicle()->bUsesCollision = !FindPlayerVehicle()->bUsesCollision; +} + +static void +ToggleComedy(void) +{ + CVehicle *veh = FindPlayerVehicle(); + if(veh == nil) + return; + veh->bComedyControls = !veh->bComedyControls; +} + +static void +PlaceOnRoad(void) +{ + CVehicle *veh = FindPlayerVehicle(); + if(veh == nil) + return; + + if(veh->IsCar()) + ((CAutomobile*)veh)->PlaceOnRoadProperly(); +} + +static void +ResetCamStatics(void) +{ + TheCamera.Cams[TheCamera.ActiveCam].ResetStatics = true; +} + +#ifdef MISSION_SWITCHER +int8 nextMissionToSwitch = 0; +static void +SwitchToMission(void) +{ + CTheScripts::SwitchToMission(nextMissionToSwitch); +} +#endif + +static const char *carnames[] = { + "landstal", "idaho", "stinger", "linerun", "peren", "sentinel", "rio", "firetruk", "trash", "stretch", "manana", + "infernus", "voodoo", "pony", "mule", "cheetah", "ambulan", "fbicar", "moonbeam", "esperant", "taxi", "washing", + "bobcat", "mrwhoop", "bfinject", "hunter", "police", "enforcer", "securica", "banshee", "predator", "bus", + "rhino", "barracks", "cuban", "chopper", "angel", "coach", "cabbie", "stallion", "rumpo", "rcbandit", "romero", + "packer", "sentxs", "admiral", "squalo", "seaspar", "pizzaboy", "gangbur", "airtrain", "deaddodo", "speeder", + "reefer", "tropic", "flatbed", "yankee", "caddy", "zebra", "topfun", "skimmer", "pcj600", "faggio", "freeway", + "rcbaron", "rcraider", "glendale", "oceanic", "sanchez", "sparrow", "patriot", "lovefist", "coastg", "dinghy", + "hermes", "sabre", "sabretur", "pheonix", "walton", "regina", "comet", "deluxo", "burrito", "spand", "marquis", + "baggage", "kaufman", "maverick", "vcnmav", "rancher", "fbiranch", "virgo", "greenwoo", "jetmax", "hotring", + "sandking", "blistac", "polmav", "boxville", "benson", "mesa", "rcgoblin", "hotrina", "hotrinb", + "bloodra", "bloodrb", "vicechee" +}; + +static CTweakVar** TweakVarsList; +static int TweakVarsListSize = -1; +static bool bAddTweakVarsNow = false; +static const char *pTweakVarsDefaultPath = NULL; + +void CTweakVars::Add(CTweakVar *var) +{ + if(TweakVarsListSize == -1) { + TweakVarsList = (CTweakVar**)malloc(64 * sizeof(CTweakVar*)); + TweakVarsListSize = 0; + } + if(TweakVarsListSize > 63) + TweakVarsList = (CTweakVar**) realloc(TweakVarsList, (TweakVarsListSize + 1) * sizeof(CTweakVar*)); + + TweakVarsList[TweakVarsListSize++] = var; +// TweakVarsList.push_back(var); + + if ( bAddTweakVarsNow ) + var->AddDBG(pTweakVarsDefaultPath); +} + +void CTweakVars::AddDBG(const char *path) +{ + pTweakVarsDefaultPath = path; + + for(int i = 0; i < TweakVarsListSize; ++i) + TweakVarsList[i]->AddDBG(pTweakVarsDefaultPath); + + bAddTweakVarsNow = true; +} + +void CTweakSwitch::AddDBG(const char *path) +{ + DebugMenuEntry *e = DebugMenuAddVar(m_pPath == NULL ? path : m_pPath, m_pVarName, (int32_t *)m_pIntVar, m_pFunc, 1, m_nMin, m_nMax, m_aStr); + DebugMenuEntrySetWrap(e, true); +} + +void CTweakFunc::AddDBG (const char *path) { DebugMenuAddCmd (m_pPath == NULL ? path : m_pPath, m_pVarName, m_pFunc); } +void CTweakBool::AddDBG (const char *path) { DebugMenuAddVarBool8(m_pPath == NULL ? path : m_pPath, m_pVarName, (int8_t *)m_pBoolVar, NULL); } +void CTweakInt8::AddDBG (const char *path) { DebugMenuAddVar (m_pPath == NULL ? path : m_pPath, m_pVarName, (int8_t *)m_pIntVar, NULL, m_nStep, m_nLoawerBound, m_nUpperBound, NULL); } +void CTweakUInt8::AddDBG (const char *path) { DebugMenuAddVar (m_pPath == NULL ? path : m_pPath, m_pVarName, (uint8_t *)m_pIntVar, NULL, m_nStep, m_nLoawerBound, m_nUpperBound, NULL); } +void CTweakInt16::AddDBG (const char *path) { DebugMenuAddVar (m_pPath == NULL ? path : m_pPath, m_pVarName, (int16_t *)m_pIntVar, NULL, m_nStep, m_nLoawerBound, m_nUpperBound, NULL); } +void CTweakUInt16::AddDBG(const char *path) { DebugMenuAddVar (m_pPath == NULL ? path : m_pPath, m_pVarName, (uint16_t *)m_pIntVar, NULL, m_nStep, m_nLoawerBound, m_nUpperBound, NULL); } +void CTweakInt32::AddDBG (const char *path) { DebugMenuAddVar (m_pPath == NULL ? path : m_pPath, m_pVarName, (int32_t *)m_pIntVar, NULL, m_nStep, m_nLoawerBound, m_nUpperBound, NULL); } +void CTweakUInt32::AddDBG(const char *path) { DebugMenuAddVar (m_pPath == NULL ? path : m_pPath, m_pVarName, (uint32_t *)m_pIntVar, NULL, m_nStep, m_nLoawerBound, m_nUpperBound, NULL); } +void CTweakFloat::AddDBG (const char *path) { DebugMenuAddVar (m_pPath == NULL ? path : m_pPath, m_pVarName, (float *)m_pIntVar, NULL, m_nStep, m_nLoawerBound, m_nUpperBound); } + +/* +static const char *wt[] = { + "Sunny", "Cloudy", "Rainy", "Foggy" + }; + +SETTWEAKPATH("TEST"); +TWEAKSWITCH(CWeather::NewWeatherType, 0, 3, wt, NULL); +*/ + +void +switchWeather(void) +{ + CWeather::StreamAfterRainTimer = 0; +} + +void +DebugMenuPopulate(void) +{ + if(1){ + static const char *weathers[] = { + "Sunny", "Cloudy", "Rainy", "Foggy", "Extrasunny", "Stormy" + }; + static const char *extracols[] = { + "1 - Malibu club", + "2 - Strib club", + "3 - Hotel", + "4 - Bank", + "5 - Police HQ", + "6 - Mall", + "7 - Rifle Range", + "8 - Mansion", + "9 - Dirt ring", + "10 - Blood ring", + "11 - Hot ring", + "12 - Concert hall", + "13 - Auntie Poulets", + "14 - Intro at docks", + "15 - Biker bar", + "16 - Intro cafe", + "17 - Studio", + "18", "19", "20", "21", "22", "23", "24" + }; + DebugMenuEntry *e; + e = DebugMenuAddVar("Time & Weather", "Current Hour", &CClock::GetHoursRef(), nil, 1, 0, 23, nil); + DebugMenuEntrySetWrap(e, true); + e = DebugMenuAddVar("Time & Weather", "Current Minute", &CClock::GetMinutesRef(), + [](){ CWeather::InterpolationValue = CClock::GetMinutes()/60.0f; }, 1, 0, 59, nil); + DebugMenuEntrySetWrap(e, true); + e = DebugMenuAddVar("Time & Weather", "Old Weather", (int16*)&CWeather::OldWeatherType, switchWeather, 1, 0, 5, weathers); + DebugMenuEntrySetWrap(e, true); + e = DebugMenuAddVar("Time & Weather", "New Weather", (int16*)&CWeather::NewWeatherType, switchWeather, 1, 0, 5, weathers); + DebugMenuEntrySetWrap(e, true); + DebugMenuAddVarBool32("Time & Weather", "Extracolours On", &CTimeCycle::m_bExtraColourOn, nil); + DebugMenuAddVar("Time & Weather", "Extracolour", &CTimeCycle::m_ExtraColour, nil, 1, 0, 23, extracols); + DebugMenuAddVar("Time & Weather", "Time scale", (float*)&CTimer::GetTimeScale(), nil, 0.1f, 0.0f, 10.0f); + + DebugMenuAddCmd("Cheats", "Weapon set 1", WeaponCheat1); + DebugMenuAddCmd("Cheats", "Weapon set 2", WeaponCheat2); + DebugMenuAddCmd("Cheats", "Weapon set 3", WeaponCheat3); + DebugMenuAddCmd("Cheats", "Money", MoneyCheat); + DebugMenuAddCmd("Cheats", "Health", HealthCheat); + DebugMenuAddCmd("Cheats", "Wanted level up", WantedLevelUpCheat); + DebugMenuAddCmd("Cheats", "Wanted level down", WantedLevelDownCheat); + DebugMenuAddCmd("Cheats", "Tank", []() { VehicleCheat(MI_TAXI); }); + DebugMenuAddCmd("Cheats", "Blow up cars", BlowUpCarsCheat); + DebugMenuAddCmd("Cheats", "Change player", ChangePlayerCheat); + DebugMenuAddCmd("Cheats", "Mayhem", MayhemCheat); + DebugMenuAddCmd("Cheats", "Everybody attacks player", EverybodyAttacksPlayerCheat); + DebugMenuAddCmd("Cheats", "Weapons for all", WeaponsForAllCheat); + DebugMenuAddCmd("Cheats", "Fast time", FastTimeCheat); + DebugMenuAddCmd("Cheats", "Slow time", SlowTimeCheat); + DebugMenuAddCmd("Cheats", "Armour", ArmourCheat); + DebugMenuAddCmd("Cheats", "Sunny weather", SunnyWeatherCheat); + DebugMenuAddCmd("Cheats", "Cloudy weather", CloudyWeatherCheat); + DebugMenuAddCmd("Cheats", "Rainy weather", RainyWeatherCheat); + DebugMenuAddCmd("Cheats", "Foggy weather", FoggyWeatherCheat); + DebugMenuAddCmd("Cheats", "Fast weather", FastWeatherCheat); + DebugMenuAddCmd("Cheats", "Only render wheels", OnlyRenderWheelsCheat); + DebugMenuAddCmd("Cheats", "Chitty chitty bang bang", ChittyChittyBangBangCheat); + DebugMenuAddCmd("Cheats", "Strong grip", StrongGripCheat); + DebugMenuAddCmd("Cheats", "Special car", SpecialCarCheats); + DebugMenuAddCmd("Cheats", "Pickup chicks", PickUpChicksCheat); + + static int32 spawnCarId = MI_LANDSTAL; + e = DebugMenuAddVar("Spawn", "Spawn Car ID", &spawnCarId, nil, 1, MI_LANDSTAL, MI_VICECHEE, carnames); + DebugMenuEntrySetWrap(e, true); + DebugMenuAddCmd("Spawn", "Spawn Car", [](){ + if(spawnCarId == MI_CHOPPER || + spawnCarId == MI_AIRTRAIN || + spawnCarId == MI_DEADDODO) + return; + SpawnCar(spawnCarId); + }); + static uint8 dummy; + carCol1 = DebugMenuAddVar("Spawn", "First colour", &dummy, nil, 1, 0, 255, nil); + carCol2 = DebugMenuAddVar("Spawn", "Second colour", &dummy, nil, 1, 0, 255, nil); + DebugMenuAddCmd("Spawn", "Spawn Stinger", [](){ SpawnCar(MI_STINGER); }); + DebugMenuAddCmd("Spawn", "Spawn Infernus", [](){ SpawnCar(MI_INFERNUS); }); + DebugMenuAddCmd("Spawn", "Spawn Cheetah", [](){ SpawnCar(MI_CHEETAH); }); + DebugMenuAddCmd("Spawn", "Spawn Phoenix", [](){ SpawnCar(MI_PHEONIX); }); + DebugMenuAddCmd("Spawn", "Spawn Banshee", [](){ SpawnCar(MI_BANSHEE); }); + DebugMenuAddCmd("Spawn", "Spawn Esperanto", [](){ SpawnCar(MI_ESPERANT); }); + DebugMenuAddCmd("Spawn", "Spawn Stallion", [](){ SpawnCar(MI_STALLION); }); + DebugMenuAddCmd("Spawn", "Spawn Admiral", [](){ SpawnCar(MI_ADMIRAL); }); + DebugMenuAddCmd("Spawn", "Spawn Washington", [](){ SpawnCar(MI_WASHING); }); + DebugMenuAddCmd("Spawn", "Spawn Taxi", [](){ SpawnCar(MI_TAXI); }); + DebugMenuAddCmd("Spawn", "Spawn Police", [](){ SpawnCar(MI_POLICE); }); + DebugMenuAddCmd("Spawn", "Spawn Enforcer", [](){ SpawnCar(MI_ENFORCER); }); + DebugMenuAddCmd("Spawn", "Spawn Cuban", [](){ SpawnCar(MI_CUBAN); }); + DebugMenuAddCmd("Spawn", "Spawn Voodoo", [](){ SpawnCar(MI_VOODOO); }); + DebugMenuAddCmd("Spawn", "Spawn BF injection", [](){ SpawnCar(MI_BFINJECT); }); + DebugMenuAddCmd("Spawn", "Spawn Maverick", [](){ SpawnCar(MI_MAVERICK); }); + DebugMenuAddCmd("Spawn", "Spawn VCN Maverick", [](){ SpawnCar(MI_VCNMAV); }); + DebugMenuAddCmd("Spawn", "Spawn Sparrow", [](){ SpawnCar(MI_SPARROW); }); + DebugMenuAddCmd("Spawn", "Spawn Sea Sparrow", [](){ SpawnCar(MI_SEASPAR); }); + DebugMenuAddCmd("Spawn", "Spawn Hunter", [](){ SpawnCar(MI_HUNTER); }); + DebugMenuAddCmd("Spawn", "Spawn Rhino", [](){ SpawnCar(MI_RHINO); }); + DebugMenuAddCmd("Spawn", "Spawn Firetruck", [](){ SpawnCar(MI_FIRETRUCK); }); + DebugMenuAddCmd("Spawn", "Spawn Predator", [](){ SpawnCar(MI_PREDATOR); }); + DebugMenuAddCmd("Spawn", "Spawn PCJ 600", [](){ SpawnCar(MI_PCJ600); }); + DebugMenuAddCmd("Spawn", "Spawn Faggio", [](){ SpawnCar(MI_FAGGIO); }); + DebugMenuAddCmd("Spawn", "Spawn Freeway", [](){ SpawnCar(MI_FREEWAY); }); + DebugMenuAddCmd("Spawn", "Spawn Squalo", [](){ SpawnCar(MI_SQUALO); }); + DebugMenuAddCmd("Spawn", "Spawn Skimmer", [](){ SpawnCar(MI_SKIMMER); }); + + DebugMenuAddVarBool8("Render", "Draw hud", &CHud::m_Wants_To_Draw_Hud, nil); +#ifdef PROPER_SCALING + DebugMenuAddVarBool8("Render", "Proper Scaling", &CDraw::ms_bProperScaling, nil); +#endif +#ifdef FIX_RADAR + DebugMenuAddVarBool8("Render", "Fix Radar", &CDraw::ms_bFixRadar, nil); +#endif +#ifdef FIX_SPRITES + DebugMenuAddVarBool8("Render", "Fix Sprites", &CDraw::ms_bFixSprites, nil); +#endif + DebugMenuAddVarBool8("Render", "Backface Culling", &gBackfaceCulling, nil); + DebugMenuAddVarBool8("Render", "PS2 Alpha test Emu", &gPS2alphaTest, nil); + DebugMenuAddVarBool8("Render", "Frame limiter", (int8_t*)&FrontEndMenuManager.m_PrefsFrameLimiter, nil); + DebugMenuAddVarBool8("Render", "VSynch", (int8_t*)&FrontEndMenuManager.m_PrefsVsync, nil); + DebugMenuAddVar("Render", "Max FPS", &RsGlobal.maxFPS, nil, 1, 1, 1000, nil); +#ifdef NEW_RENDERER + DebugMenuAddVarBool8("Render", "New Renderer", &gbNewRenderer, nil); +extern bool gbRenderRoads; +extern bool gbRenderEverythingBarRoads; +extern bool gbRenderFadingInUnderwaterEntities; +extern bool gbRenderFadingInEntities; +extern bool gbRenderWater; +extern bool gbRenderBoats; +extern bool gbRenderVehicles; +extern bool gbRenderWorld0; +extern bool gbRenderWorld1; +extern bool gbRenderWorld2; + DebugMenuAddVarBool8("Debug Render", "gbRenderRoads", &gbRenderRoads, nil); + DebugMenuAddVarBool8("Debug Render", "gbRenderEverythingBarRoads", &gbRenderEverythingBarRoads, nil); + DebugMenuAddVarBool8("Debug Render", "gbRenderFadingInUnderwaterEntities", &gbRenderFadingInUnderwaterEntities, nil); + DebugMenuAddVarBool8("Debug Render", "gbRenderFadingInEntities", &gbRenderFadingInEntities, nil); + DebugMenuAddVarBool8("Debug Render", "gbRenderWater", &gbRenderWater, nil); + DebugMenuAddVarBool8("Debug Render", "gbRenderBoats", &gbRenderBoats, nil); + DebugMenuAddVarBool8("Debug Render", "gbRenderVehicles", &gbRenderVehicles, nil); + DebugMenuAddVarBool8("Debug Render", "gbRenderWorld0", &gbRenderWorld0, nil); + DebugMenuAddVarBool8("Debug Render", "gbRenderWorld1", &gbRenderWorld1, nil); + DebugMenuAddVarBool8("Debug Render", "gbRenderWorld2", &gbRenderWorld2, nil); +#endif + +#ifdef EXTENDED_COLOURFILTER + static const char *filternames[] = { "None", "Simple", "Normal", "Mobile" }; + e = DebugMenuAddVar("Render", "Colourfilter", &CPostFX::EffectSwitch, nil, 1, CPostFX::POSTFX_OFF, CPostFX::POSTFX_MOBILE, filternames); + DebugMenuEntrySetWrap(e, true); + DebugMenuAddVar("Render", "Intensity", &CPostFX::Intensity, nil, 0.05f, 0, 10.0f); + DebugMenuAddVarBool8("Render", "Blur", &CPostFX::BlurOn, nil); + DebugMenuAddVarBool8("Render", "Motion Blur", &CPostFX::MotionBlurOn, nil); +#endif + DebugMenuAddVar("Render", "Drunkness", &CMBlur::Drunkness, nil, 0.05f, 0, 1.0f); +#ifndef MASTER + DebugMenuAddVarBool8("Render", "Occlusion debug", &bDispayOccDebugStuff, nil); +#endif +#ifdef LIBRW + DebugMenuAddVarBool32("Render", "MatFX env map apply light", &rw::MatFX::envMapApplyLight, nil); + DebugMenuAddVarBool32("Render", "MatFX env map flip U", &rw::MatFX::envMapFlipU, nil); + DebugMenuAddVarBool32("Render", "MatFX env map use matcolor", &rw::MatFX::envMapUseMatColor, nil); +#endif +#ifdef EXTENDED_PIPELINES + static const char *vehpipenames[] = { "MatFX", "Neo" }; + e = DebugMenuAddVar("Render", "Vehicle Pipeline", &CustomPipes::VehiclePipeSwitch, nil, + 1, CustomPipes::VEHICLEPIPE_MATFX, CustomPipes::VEHICLEPIPE_NEO, vehpipenames); + DebugMenuEntrySetWrap(e, true); + DebugMenuAddVar("Render", "Neo Vehicle Shininess", &CustomPipes::VehicleShininess, nil, 0.1f, 0, 1.0f); + DebugMenuAddVar("Render", "Neo Vehicle Specularity", &CustomPipes::VehicleSpecularity, nil, 0.1f, 0, 1.0f); + DebugMenuAddVarBool8("Render", "Neo Ped Rim light enable", &CustomPipes::RimlightEnable, nil); + DebugMenuAddVar("Render", "Mult", &CustomPipes::RimlightMult, nil, 0.1f, 0, 1.0f); + DebugMenuAddVarBool8("Render", "Neo World Lightmaps enable", &CustomPipes::LightmapEnable, nil); + DebugMenuAddVar("Render", "Mult", &CustomPipes::LightmapMult, nil, 0.1f, 0, 1.0f); + DebugMenuAddVarBool8("Render", "Neo Road Gloss enable", &CustomPipes::GlossEnable, nil); + DebugMenuAddVar("Render", "Mult", &CustomPipes::GlossMult, nil, 0.1f, 0, 1.0f); +#endif + DebugMenuAddVarBool8("Debug Render", "Show Ped Paths", &gbShowPedPaths, nil); + DebugMenuAddVarBool8("Debug Render", "Show Car Paths", &gbShowCarPaths, nil); + DebugMenuAddVarBool8("Debug Render", "Show Car Path Links", &gbShowCarPathsLinks, nil); + DebugMenuAddVarBool8("Debug Render", "Show Collision Lines", &gbShowCollisionLines, nil); + DebugMenuAddVarBool8("Debug Render", "Show Collision Polys", &gbShowCollisionPolys, nil); + DebugMenuAddVarBool8("Debug Render", "Don't render Buildings", &gbDontRenderBuildings, nil); + DebugMenuAddVarBool8("Debug Render", "Don't render Big Buildings", &gbDontRenderBigBuildings, nil); + DebugMenuAddVarBool8("Debug Render", "Don't render Peds", &gbDontRenderPeds, nil); + DebugMenuAddVarBool8("Debug Render", "Don't render Vehicles", &gbDontRenderVehicles, nil); + DebugMenuAddVarBool8("Debug Render", "Don't render Objects", &gbDontRenderObjects, nil); + DebugMenuAddVarBool8("Debug Render", "Don't Render Water", &gbDontRenderWater, nil); + + +#ifdef DRAW_GAME_VERSION_TEXT + DebugMenuAddVarBool8("Debug", "Version Text", &gbDrawVersionText, nil); +#endif + DebugMenuAddVarBool8("Debug", "Show DebugStuffInRelease", &gbDebugStuffInRelease, nil); +#ifdef TIMEBARS + DebugMenuAddVarBool8("Debug", "Show Timebars", &gbShowTimebars, nil); +#endif +#ifndef FINAL + DebugMenuAddVarBool8("Debug", "Use debug render groups", &bDebugRenderGroups, nil); + DebugMenuAddVarBool8("Debug", "Print Memory Usage", &gbPrintMemoryUsage, nil); +#ifdef USE_CUSTOM_ALLOCATOR + DebugMenuAddCmd("Debug", "Parse Heap", ParseHeap); +#endif +#endif + + DebugMenuAddVarBool8("Debug", "pad 1 -> pad 2", &CPad::m_bMapPadOneToPadTwo, nil); +#ifdef GTA_SCENE_EDIT + DebugMenuAddVarBool8("Debug", "Edit on", &CSceneEdit::m_bEditOn, nil); +#endif + //DebugMenuAddCmd("Debug", "Start Credits", CCredits::Start); + //DebugMenuAddCmd("Debug", "Stop Credits", CCredits::Stop); + +#ifdef RELOADABLES +// maybe put it back if we have more to reload +// DebugMenuAddCmd("Reload", "HUD.TXD", CHud::ReloadTXD); +#endif + +#ifdef MAP_ENHANCEMENTS + DebugMenuAddCmd("Game", "Teleport to map waypoint", TeleportToWaypoint); +#endif + DebugMenuAddCmd("Game", "Fix Car", FixCar); + DebugMenuAddCmd("Game", "Place Car on Road", PlaceOnRoad); + DebugMenuAddCmd("Game", "Switch car collision", SwitchCarCollision); + DebugMenuAddCmd("Game", "Toggle Comedy Controls", ToggleComedy); + + +#ifdef MISSION_SWITCHER + DebugMenuEntry *missionEntry; + static const char* missions[] = { + "Initial", "Intro", "An Old Friend", "The Party", "Back Alley Brawl", "Jury Fury", "Riot", + "Treacherous Swine", "Mall Shootout", "Guardian Angels", "Sir, Yes Sir!", "All Hands On Deck!", + "The Chase", "Phnom Penh '86", "The Fastest Boat", "Supply & Demand", "Rub Out", "Death Row", + "Four Iron", "Demolition Man", "Two Bit Hit", "No Escape?", "The Shootist", "The Driver", + "The Job", "Gun Runner", "Boomshine Saigon", "Recruitment Drive", "Dildo Dodo", "Martha's Mug Shot", + "G-spotlight", "Shakedown", "Bar Brawl", "Cop Land", "Spilling the Beans", "Hit the Courier", + "Printworks Buy", "Sunshine Autos", "Interglobal Films Buy", "Cherry Popper Icecreams Buy", + "Kaufman Cabs Buy", "Malibu Club Buy", "The Boatyard Buy", "Pole Position Club Buy", "El Swanko Casa Buy", + "Links View Apartment Buy", "Hyman Condo Buy", "Ocean Heighs Aprt. Buy", "1102 Washington Street Buy", + "Vice Point Buy", "Skumole Shack Buy", "Cap the Collector", "Keep your Friends Close...", + "Alloy Wheels of Steel", "Messing with the Man", "Hog Tied", "Stunt Boat Challenge", "Cannon Fodder", + "Naval Engagement", "Trojan Voodoo", "Juju Scramble", "Bombs Away!", "Dirty Lickin's", "Love Juice", + "Psycho Killer", "Publicity Tour", "Weapon Range", "Road Kill", "Waste the Wife", "Autocide", + "Check Out at the Check In", "Loose Ends", "V.I.P.", "Friendly Rivalry", "Cabmaggedon", "TAXI DRIVER", + "PARAMEDIC", "FIREFIGHTER", "VIGILANTE", "HOTRING", "BLOODRING", "DIRTRING", "Sunshine Autos Races", + "Distribution", "Downtown Chopper Checkpoint", "Ocean Beach Chopper Checkpoint", "Vice Point Chopper Checkpoint", + "Little Haiti Chopper Checkpoint", "Trial by Dirt", "Test Track", "PCJ Playground", "Cone Crazy", + "PIZZA BOY", "RC Raider Pickup", "RC Bandit Race", "RC Baron Race", "Checkpoint Charlie" + }; + + missionEntry = DebugMenuAddVar("Game", "Select mission", (uint32_t*)&nextMissionToSwitch, nil, 1, 0, ARRAY_SIZE(missions) - 1, missions); + DebugMenuEntrySetWrap(missionEntry, true); + DebugMenuAddCmd("Game", "Start selected mission ", SwitchToMission); +#endif + extern bool PrintDebugCode; + extern int16 DebugCamMode; + DebugMenuAddVarBool8("Cam", "Use mouse Cam", &CCamera::m_bUseMouse3rdPerson, nil); +#ifdef FREE_CAM + DebugMenuAddVarBool8("Cam", "Free Cam", &CCamera::bFreeCam, nil); +#endif + DebugMenuAddVarBool8("Cam", "Print Debug Code", &PrintDebugCode, nil); + DebugMenuAddVar("Cam", "Cam Mode", &DebugCamMode, nil, 1, 0, CCam::MODE_EDITOR, nil); + DebugMenuAddCmd("Cam", "Normal", []() { DebugCamMode = 0; }); + DebugMenuAddCmd("Cam", "Reset Statics", ResetCamStatics); + + CTweakVars::AddDBG("Debug"); + } +} +#endif + +#ifndef __MWERKS__ +#ifndef MASTER +const int re3_buffsize = 1024; +static char re3_buff[re3_buffsize]; +#endif + +extern void stacktrace(); + +#ifndef MASTER +void re3_assert(const char *expr, const char *filename, unsigned int lineno, const char *func) +{ +#ifdef _WIN32 + int nCode; + + strcpy_s(re3_buff, re3_buffsize, "Assertion failed!" ); + strcat_s(re3_buff, re3_buffsize, "\n" ); + + strcat_s(re3_buff, re3_buffsize, "File: "); + strcat_s(re3_buff, re3_buffsize, filename ); + strcat_s(re3_buff, re3_buffsize, "\n" ); + + strcat_s(re3_buff, re3_buffsize, "Line: " ); + _itoa_s( lineno, re3_buff + strlen(re3_buff), re3_buffsize - strlen(re3_buff), 10 ); + strcat_s(re3_buff, re3_buffsize, "\n"); + + strcat_s(re3_buff, re3_buffsize, "Function: "); + strcat_s(re3_buff, re3_buffsize, func ); + strcat_s(re3_buff, re3_buffsize, "\n" ); + + strcat_s(re3_buff, re3_buffsize, "Expression: "); + strcat_s(re3_buff, re3_buffsize, expr); + strcat_s(re3_buff, re3_buffsize, "\n"); + + strcat_s(re3_buff, re3_buffsize, "\n" ); + strcat_s(re3_buff, re3_buffsize, "(Press Retry to debug the application)"); + + + nCode = ::MessageBoxA(nil, re3_buff, "REVC Assertion Failed!", + MB_ABORTRETRYIGNORE|MB_ICONHAND|MB_SETFOREGROUND|MB_TASKMODAL); + + if (nCode == IDABORT) + { + raise(SIGABRT); + _exit(3); + } + + if (nCode == IDRETRY) + { + __debugbreak(); + return; + } + + if (nCode == IDIGNORE) + return; + + abort(); +#else + // TODO + fflush(stdout); + fflush(stderr); + dbglog(DBG_CRITICAL, "\nRE3 ASSERT FAILED\n\tFile: %s\n\tLine: %d\n\tFunction: %s\n\tExpression: %s\n",filename,lineno,func,expr); + dbglog(DBG_CRITICAL, "POSIX error (may not be relevant): %s\n", strerror(errno)); + #if defined(DC_SIM) || defined(DC_TEXCONV) + for(;;); + #else + stacktrace(); + dbgio_dev_select("fb"); + sleep(1); + dbglog(DBG_CRITICAL, "RE3 ASSERT FAILED\n\tFile: %s\n\tLine: %d\n\tFunction: %s\n\tExpression: %s\n",filename,lineno,func,expr); + dbglog(DBG_CRITICAL, "POSIX error (may not be relevant): %s\n", strerror(errno)); + stacktrace(); + dbgio_flush(); + abort(); + #endif +#endif +} +#endif + +void (re3_debug)(const char *format, ...) +{ +#ifndef MASTER + va_list va; + va_start(va, format); +#ifdef _WIN32 + vsprintf_s(re3_buff, re3_buffsize, format, va); +#else + vsprintf(re3_buff, format, va); +#endif + va_end(va); + + printf("%s", re3_buff); + CDebug::DebugAddText(re3_buff); +#endif +} + +#ifndef MASTER +void (re3_trace)(const char *filename, unsigned int lineno, const char *func, const char *format, ...) +{ + char buff[re3_buffsize *2]; + va_list va; + va_start(va, format); +#ifdef _WIN32 + vsprintf_s(re3_buff, re3_buffsize, format, va); + va_end(va); + + sprintf_s(buff, re3_buffsize * 2, "[%s.%s:%d]: %s", filename, func, lineno, re3_buff); +#else + vsprintf(re3_buff, format, va); + va_end(va); + + sprintf(buff, "[%s.%s:%d]: %s", filename, func, lineno, re3_buff); +#endif + + OutputDebugString(buff); +} +#endif + +#ifndef MASTER +void (re3_usererror)(const char *format, ...) +{ + va_list va; + va_start(va, format); +#ifdef _WIN32 + vsprintf_s(re3_buff, re3_buffsize, format, va); + va_end(va); + + ::MessageBoxA(nil, re3_buff, "REVC Error!", + MB_OK|MB_ICONHAND|MB_SETFOREGROUND|MB_TASKMODAL); + + raise(SIGABRT); + _exit(3); +#else + vsprintf(re3_buff, format, va); + printf("\nREVC Error!\n\t%s\n",re3_buff); + assert(false); +#endif +} +#endif +#endif + +#ifdef VALIDATE_SAVE_SIZE +int32 _saveBufCount; +#endif diff --git a/src/miami/core/templates.h b/src/miami/core/templates.h new file mode 100644 index 00000000..7bc85ee6 --- /dev/null +++ b/src/miami/core/templates.h @@ -0,0 +1,270 @@ +#pragma once + +template +class CStore +{ +public: + int32 allocPtr; + T store[n]; + + T *Alloc(void){ + if(allocPtr >= n){ + printf("Size of this thing:%d needs increasing\n", n); + assert(0); + } + return &store[allocPtr++]; + } + void Clear(void){ + allocPtr = 0; + } + int32 GetIndex(T *item){ + assert(item >= &store[0]); + assert(item < &store[n]); + return item - store; + } + T *GetItem(int32 index){ + assert(index >= 0); + assert(index < n); + return &store[index]; + } +}; + +#define POOLFLAG_ID 0x7f +#define POOLFLAG_ISFREE 0x80 + +template +class CPool +{ + U *m_entries; + uint8 *m_flags; + int32 m_size; + int32 m_allocPtr; + +public: + CPool(int32 size, const char *name){ + m_entries = (U*)new uint8[sizeof(U)*size]; + m_flags = new uint8[size]; + m_size = size; + m_allocPtr = -1; + for(int i = 0; i < size; i++){ + SetId(i, 0); + SetIsFree(i, true); + } + } + + int GetId(int i) const + { + return m_flags[i] & POOLFLAG_ID; + } + + bool GetIsFree(int i) const + { + return !!(m_flags[i] & POOLFLAG_ISFREE); + } + + void SetId(int i, int id) + { + m_flags[i] = (m_flags[i] & POOLFLAG_ISFREE) | (id & POOLFLAG_ID); + } + + void SetIsFree(int i, bool isFree) + { + if (isFree) + m_flags[i] |= POOLFLAG_ISFREE; + else + m_flags[i] &= ~POOLFLAG_ISFREE; + } + ~CPool() { + Flush(); + } + void Flush() { + if (m_size > 0) { + delete[] (uint8*)m_entries; + delete[] m_flags; + m_entries = nil; + m_flags = nil; + m_size = 0; + m_allocPtr = 0; + } + } + int32 GetSize(void) const { return m_size; } + T *New(void){ + bool wrapped = false; + do +#ifdef FIX_BUGS + if (++m_allocPtr >= m_size) { + m_allocPtr = 0; + if (wrapped) + return nil; + wrapped = true; + } +#else + if(++m_allocPtr == m_size){ + if(wrapped) + return nil; + wrapped = true; + m_allocPtr = 0; + } +#endif + while(!GetIsFree(m_allocPtr)); + SetIsFree(m_allocPtr, false); + SetId(m_allocPtr, GetId(m_allocPtr)+1); + return (T*)&m_entries[m_allocPtr]; + } + T *New(int32 handle){ + T *entry = (T*)&m_entries[handle>>8]; + SetNotFreeAt(handle); + return entry; + } + void SetNotFreeAt(int32 handle){ + int idx = handle>>8; + SetIsFree(idx, false); + SetId(idx, handle & POOLFLAG_ID); + for(m_allocPtr = 0; m_allocPtr < m_size; m_allocPtr++) + if(GetIsFree(m_allocPtr)) + return; + } + void Delete(T *entry){ + int i = GetJustIndex(entry); + SetIsFree(i, true); + if(i < m_allocPtr) + m_allocPtr = i; + } + T *GetSlot(int i){ + return GetIsFree(i) ? nil : (T*)&m_entries[i]; + } + T *GetAt(int handle){ +#ifdef FIX_BUGS + if (handle == -1) + return nil; +#endif + return m_flags[handle>>8] == (handle & 0xFF) ? + (T*)&m_entries[handle >> 8] : nil; + } + int32 GetIndex(T* entry) { + int i = GetJustIndex_NoFreeAssert(entry); + return m_flags[i] + (i<<8); + } + int32 GetJustIndex(T* entry) { + int index = GetJustIndex_NoFreeAssert(entry); + assert((U*)entry == (U*)&m_entries[index]); // cast is unsafe - check required + assert(!GetIsFree(index)); + return index; + } + int32 GetJustIndex_NoFreeAssert(T* entry) { + int index = ((U*)entry - m_entries); + // Please don't add unsafe assert here, because at least one func. use this to check if entity is ped or vehicle. + return index; + } + int32 GetNoOfUsedSpaces(void) const { + int i; + int n = 0; + for(i = 0; i < m_size; i++) + if(!GetIsFree(i)) + n++; + return n; + } + void ClearStorage(uint8 *&flags, U *&entries){ + delete[] flags; + delete[] (uint8*)entries; + flags = nil; + entries = nil; + } + uint32 GetMaxEntrySize() const { return sizeof(U); } + void CopyBack(uint8 *&flags, U *&entries){ + memcpy(m_flags, flags, sizeof(uint8)*m_size); + memcpy(m_entries, entries, sizeof(U)*m_size); + debug("Size copied:%d (%d)\n", sizeof(U)*m_size, m_size); + m_allocPtr = 0; + ClearStorage(flags, entries); + debug("CopyBack:%d (/%d)\n", GetNoOfUsedSpaces(), m_size); /* Assumed inlining */ + } + void Store(uint8 *&flags, U *&entries){ + flags = (uint8*)new uint8[sizeof(uint8)*m_size]; + entries = (U*)new uint8[sizeof(U)*m_size]; + memcpy(flags, m_flags, sizeof(uint8)*m_size); + memcpy(entries, m_entries, sizeof(U)*m_size); + debug("Stored:%d (/%d)\n", GetNoOfUsedSpaces(), m_size); /* Assumed inlining */ + } + int32 GetNoOfFreeSpaces() const { return GetSize() - GetNoOfUsedSpaces(); } +}; + +template +class CLink +{ +public: + T item; + CLink *prev; + CLink *next; + + void Insert(CLink *link){ + link->next = this->next; + this->next->prev = link; + link->prev = this; + this->next = link; + } + void Remove(void){ + this->prev->next = this->next; + this->next->prev = this->prev; + } +}; + +template +class CLinkList +{ +public: + CLink head, tail; + CLink freeHead, freeTail; + CLink *links; + + void Init(int n){ + links = new CLink[n]; + head.next = &tail; + tail.prev = &head; + freeHead.next = &freeTail; + freeTail.prev = &freeHead; + while(n--) + freeHead.Insert(&links[n]); + } + void Shutdown(void){ + delete[] links; + links = nil; + } + void Clear(void){ + while(head.next != &tail) + Remove(head.next); + } + CLink *Insert(T const &item){ + CLink *node = freeHead.next; + if(node == &freeTail) + return nil; + node->item = item; + node->Remove(); // remove from free list + head.Insert(node); + return node; + } + CLink *InsertSorted(T const &item){ + CLink *sort; + for(sort = head.next; sort != &tail; sort = sort->next) + if(sort->item.sort >= item.sort) + break; + CLink *node = freeHead.next; + if(node == &freeTail) + return nil; + node->item = item; + node->Remove(); // remove from free list + sort->prev->Insert(node); + return node; + } + void Remove(CLink *link){ + link->Remove(); // remove from list + freeHead.Insert(link); // insert into free list + } + int32 Count(void){ + int n = 0; + CLink *lnk; + for(lnk = head.next; lnk != &tail; lnk = lnk->next) + n++; + return n; + } +}; diff --git a/src/miami/core/timebars.cpp b/src/miami/core/timebars.cpp new file mode 100644 index 00000000..169fef8c --- /dev/null +++ b/src/miami/core/timebars.cpp @@ -0,0 +1,121 @@ +#include "common.h" +#ifndef MASTER +#include "Font.h" +#include "Frontend.h" +#include "Timer.h" +#include "Text.h" + +#define MAX_TIMERS (50) +#define MAX_MS_COLLECTED (40) + +// enables frame time output +#define FRAMETIME + +struct sTimeBar +{ + char name[20]; + float startTime; + float endTime; + int32 unk; +}; + +struct +{ + sTimeBar Timers[MAX_TIMERS]; + uint32 count; +} TimerBar; +float MaxTimes[MAX_TIMERS]; +float MaxFrameTime; + +uint32 curMS; +uint32 msCollected[MAX_MS_COLLECTED]; +#ifdef FRAMETIME +float FrameInitTime; +#endif + +void tbInit() +{ + TimerBar.count = 0; + uint32 i = CTimer::GetFrameCounter() & 0x7F; + if (i == 0) { + do + MaxTimes[i++] = 0.0f; + while (i != MAX_TIMERS); +#ifdef FRAMETIME + MaxFrameTime = 0.0f; +#endif + } +#ifdef FRAMETIME + FrameInitTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame(); +#endif +} + +void tbStartTimer(int32 unk, Const char *name) +{ + strcpy(TimerBar.Timers[TimerBar.count].name, name); + TimerBar.Timers[TimerBar.count].unk = unk; + TimerBar.Timers[TimerBar.count].startTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame(); + TimerBar.count++; +} + +void tbEndTimer(Const char* name) +{ + uint32 n = 1500; + for (uint32 i = 0; i < TimerBar.count; i++) { + if (strcmp(name, TimerBar.Timers[i].name) == 0) + n = i; + } + assert(n != 1500); + TimerBar.Timers[n].endTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame(); +} + +float Diag_GetFPS() +{ + return 39000.0f / (msCollected[(curMS - 1) % MAX_MS_COLLECTED] - msCollected[curMS % MAX_MS_COLLECTED]); +} + +void tbDisplay() +{ + char temp[200]; + wchar wtemp[200]; + +#ifdef FRAMETIME + float FrameEndTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame(); +#endif + + msCollected[(curMS++) % MAX_MS_COLLECTED] = RsTimer(); + CFont::SetBackgroundOff(); + CFont::SetBackgroundColor(CRGBA(0, 0, 0, 128)); + CFont::SetScale(0.48f, 1.12f); + CFont::SetCentreOff(); + CFont::SetJustifyOff(); + CFont::SetWrapx(SCREEN_STRETCH_X(DEFAULT_SCREEN_WIDTH)); + CFont::SetRightJustifyOff(); + CFont::SetPropOn(); + CFont::SetFontStyle(FONT_STANDARD); + sprintf(temp, "FPS: %.2f", Diag_GetFPS()); + AsciiToUnicode(temp, wtemp); + CFont::SetColor(CRGBA(255, 255, 255, 255)); + if (!CMenuManager::m_PrefsMarketing || !CMenuManager::m_PrefsDisableTutorials) { + CFont::PrintString(RsGlobal.maximumWidth * (4.0f / DEFAULT_SCREEN_WIDTH), RsGlobal.maximumHeight * (4.0f / DEFAULT_SCREEN_HEIGHT), wtemp); + +#ifndef FINAL + // Timers output (my own implementation) + for (uint32 i = 0; i < TimerBar.count; i++) { + MaxTimes[i] = Max(MaxTimes[i], TimerBar.Timers[i].endTime - TimerBar.Timers[i].startTime); + sprintf(temp, "%s: %.2f", &TimerBar.Timers[i].name[0], MaxTimes[i]); + AsciiToUnicode(temp, wtemp); + CFont::PrintString(RsGlobal.maximumWidth * (4.0f / DEFAULT_SCREEN_WIDTH), RsGlobal.maximumHeight * ((8.0f * (i + 2)) / DEFAULT_SCREEN_HEIGHT), wtemp); + } + +#ifdef FRAMETIME + MaxFrameTime = Max(MaxFrameTime, FrameEndTime - FrameInitTime); + sprintf(temp, "Frame Time: %.2f", MaxFrameTime); + AsciiToUnicode(temp, wtemp); + + CFont::PrintString(RsGlobal.maximumWidth * (4.0f / DEFAULT_SCREEN_WIDTH), RsGlobal.maximumHeight * ((8.0f * (TimerBar.count + 4)) / DEFAULT_SCREEN_HEIGHT), wtemp); +#endif // FRAMETIME +#endif // !FINAL + } +} +#endif // !MASTER \ No newline at end of file diff --git a/src/miami/core/timebars.h b/src/miami/core/timebars.h new file mode 100644 index 00000000..c4939802 --- /dev/null +++ b/src/miami/core/timebars.h @@ -0,0 +1,13 @@ +#pragma once + +#ifdef TIMEBARS +void tbInit(); +void tbStartTimer(int32, Const char*); +void tbEndTimer(Const char*); +void tbDisplay(); +#else +#define tbInit() +#define tbStartTimer(a, b) +#define tbEndTimer(a) +#define tbDisplay() +#endif diff --git a/src/miami/entities/Dummy.cpp b/src/miami/entities/Dummy.cpp new file mode 100644 index 00000000..d62d2434 --- /dev/null +++ b/src/miami/entities/Dummy.cpp @@ -0,0 +1,67 @@ +#include "common.h" + +#include "Pools.h" +#include "World.h" +#include "Dummy.h" + +void *CDummy::operator new(size_t sz) throw() { return CPools::GetDummyPool()->New(); } +void CDummy::operator delete(void *p, size_t sz) throw() { CPools::GetDummyPool()->Delete((CDummy*)p); } + +void +CDummy::Add(void) +{ + int x, xstart, xmid, xend; + int y, ystart, ymid, yend; + CSector *s; + CPtrList *list; + + CRect bounds = GetBoundRect(); + xstart = CWorld::GetSectorIndexX(bounds.left); + xend = CWorld::GetSectorIndexX(bounds.right); + xmid = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f); + ystart = CWorld::GetSectorIndexY(bounds.top); + yend = CWorld::GetSectorIndexY(bounds.bottom); + ymid = CWorld::GetSectorIndexY((bounds.top + bounds.bottom)/2.0f); + assert(xstart >= 0); + assert(xend < NUMSECTORS_X); + assert(ystart >= 0); + assert(yend < NUMSECTORS_Y); + + for(y = ystart; y <= yend; y++) + for(x = xstart; x <= xend; x++){ + s = CWorld::GetSector(x, y); + if(x == xmid && y == ymid) + list = &s->m_lists[ENTITYLIST_DUMMIES]; + else + list = &s->m_lists[ENTITYLIST_DUMMIES_OVERLAP]; + CPtrNode *node = list->InsertItem(this); + assert(node); + m_entryInfoList.InsertItem(list, node, s); + } +} + +void +CDummy::Remove(void) +{ + CEntryInfoNode *node, *next; + for(node = m_entryInfoList.first; node; node = next){ + next = node->next; + node->list->DeleteNode(node->listnode); + m_entryInfoList.DeleteNode(node); + } +} + +bool +IsDummyPointerValid(CDummy* pDummy) +{ + if (!pDummy) + return false; + int index = CPools::GetDummyPool()->GetJustIndex_NoFreeAssert(pDummy); +#ifdef FIX_BUGS + if (index < 0 || index >= CPools::GetDummyPool()->GetSize()) +#else + if (index < 0 || index > CPools::GetDummyPool()->GetSize()) +#endif + return false; + return pDummy->m_entryInfoList.first; +} diff --git a/src/miami/entities/Dummy.h b/src/miami/entities/Dummy.h new file mode 100644 index 00000000..9b73eefc --- /dev/null +++ b/src/miami/entities/Dummy.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Lists.h" +#include "Entity.h" + +class CDummy : public CEntity +{ +public: + CEntryInfoList m_entryInfoList; + + CDummy(void) { m_type = ENTITY_TYPE_DUMMY; } + void Add(void); + void Remove(void); + + static void *operator new(size_t) throw(); + static void operator delete(void*, size_t) throw(); +}; + +bool IsDummyPointerValid(CDummy* pDummy); diff --git a/src/miami/entities/Entity.cpp b/src/miami/entities/Entity.cpp new file mode 100644 index 00000000..b2fcfbc7 --- /dev/null +++ b/src/miami/entities/Entity.cpp @@ -0,0 +1,906 @@ +#include "common.h" + +#include "VuVector.h" +#include "General.h" +#include "RwHelper.h" +#include "ModelIndices.h" +#include "Timer.h" +#include "Entity.h" +#include "Object.h" +#include "World.h" +#include "Camera.h" +#include "Glass.h" +#include "Weather.h" +#include "Timecycle.h" +#include "TrafficLights.h" +#include "Coronas.h" +#include "PointLights.h" +#include "Shadows.h" +#include "Pickups.h" +#include "SpecialFX.h" +#include "TxdStore.h" +#include "Zones.h" +#include "MemoryHeap.h" +#include "Bones.h" +#include "Debug.h" +#include "Ped.h" +#include "Dummy.h" +#include "WindModifiers.h" +#include "SaveBuf.h" + +int gBuildings; + +CEntity::CEntity(void) +{ + m_type = ENTITY_TYPE_NOTHING; + m_status = STATUS_ABANDONED; + + bUsesCollision = false; + bCollisionProcessed = false; + bIsStatic = false; + bHasContacted = false; + bPedPhysics = false; + bIsStuck = false; + bIsInSafePosition = false; + bUseCollisionRecords = false; + + bWasPostponed = false; + bExplosionProof = false; + bIsVisible = true; + bHasCollided = false; + bRenderScorched = false; + bHasBlip = false; + bIsBIGBuilding = false; + bStreamBIGBuilding = false; + + bRenderDamaged = false; + bBulletProof = false; + bFireProof = false; + bCollisionProof = false; + bMeleeProof = false; + bOnlyDamagedByPlayer = false; + bStreamingDontDelete = false; + bRemoveFromWorld = false; + + bHasHitWall = false; + bImBeingRendered = false; + bTouchingWater = false; + bIsSubway = false; + bDrawLast = false; + bNoBrightHeadLights = false; + bDoNotRender = false; + bDistanceFade = false; + + m_flagE1 = false; + bDontCastShadowsOn = false; + bOffscreen = false; + bIsStaticWaitingForCollision = false; + bDontStream = false; + bUnderwater = false; + bHasPreRenderEffects = false; + + m_scanCode = 0; + m_modelIndex = -1; + m_rwObject = nil; + m_area = AREA_MAIN_MAP; + m_randomSeed = CGeneral::GetRandomNumber(); + m_pFirstReference = nil; +} + +CEntity::~CEntity(void) +{ + DeleteRwObject(); + ResolveReferences(); +} + +void +CEntity::SetModelIndex(uint32 id) +{ + m_modelIndex = id; + bHasPreRenderEffects = HasPreRenderEffects(); + CreateRwObject(); +} + +void +CEntity::SetModelIndexNoCreate(uint32 id) +{ + m_modelIndex = id; + bHasPreRenderEffects = HasPreRenderEffects(); +} + +void +CEntity::CreateRwObject(void) +{ + CBaseModelInfo *mi; + + mi = CModelInfo::GetModelInfo(m_modelIndex); + + PUSH_MEMID(MEMID_WORLD); + m_rwObject = mi->CreateInstance(); + POP_MEMID(); + + if(m_rwObject){ + if(IsBuilding()) + gBuildings++; + if(RwObjectGetType(m_rwObject) == rpATOMIC) + GetMatrix().AttachRW(RwFrameGetMatrix(RpAtomicGetFrame((RpAtomic *)m_rwObject)), false); + else if(RwObjectGetType(m_rwObject) == rpCLUMP) + GetMatrix().AttachRW(RwFrameGetMatrix(RpClumpGetFrame((RpClump *)m_rwObject)), false); + + mi->AddRef(); + } +} + +void +CEntity::AttachToRwObject(RwObject *obj) +{ + m_rwObject = obj; + if(m_rwObject){ + if(RwObjectGetType(m_rwObject) == rpATOMIC) + GetMatrix().Attach(RwFrameGetMatrix(RpAtomicGetFrame((RpAtomic *)m_rwObject)), false); + else if(RwObjectGetType(m_rwObject) == rpCLUMP) + GetMatrix().Attach(RwFrameGetMatrix(RpClumpGetFrame((RpClump *)m_rwObject)), false); + + CModelInfo::GetModelInfo(m_modelIndex)->AddRef(); + } +} + +void +CEntity::DetachFromRwObject(void) +{ + if(m_rwObject) + CModelInfo::GetModelInfo(m_modelIndex)->RemoveRef(); + m_rwObject = nil; + GetMatrix().Detach(); +} + +RpAtomic* +AtomicRemoveAnimFromSkinCB(RpAtomic *atomic, void *data) +{ + if(RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic))){ + RpHAnimHierarchy *hier = RpSkinAtomicGetHAnimHierarchy(atomic); +#ifdef LIBRW + if(hier && hier->interpolator->currentAnim){ + RpHAnimAnimationDestroy(hier->interpolator->currentAnim); + hier->interpolator->currentAnim = nil; + } +#else + if(hier && hier->currentAnim){ + RpHAnimAnimationDestroy(hier->currentAnim->pCurrentAnim); + hier->currentAnim = nil; + } +#endif + } + return atomic; +} + +void +CEntity::DeleteRwObject(void) +{ + RwFrame *f; + + GetMatrix().Detach(); + if(m_rwObject){ + if(RwObjectGetType(m_rwObject) == rpATOMIC){ + f = RpAtomicGetFrame((RpAtomic*)m_rwObject); + RpAtomicDestroy((RpAtomic*)m_rwObject); + RwFrameDestroy(f); + }else if(RwObjectGetType(m_rwObject) == rpCLUMP){ + if(IsClumpSkinned((RpClump*)m_rwObject)) + RpClumpForAllAtomics((RpClump*)m_rwObject, AtomicRemoveAnimFromSkinCB, nil); + RpClumpDestroy((RpClump*)m_rwObject); + } + m_rwObject = nil; + CModelInfo::GetModelInfo(m_modelIndex)->RemoveRef(); + if(IsBuilding()) + gBuildings--; + } +} + +CRect +CEntity::GetBoundRect(void) +{ + CRect rect; + CVector v; + CColModel *col = CModelInfo::GetColModel(m_modelIndex); + + rect.ContainPoint(GetMatrix() * col->boundingBox.min); + rect.ContainPoint(GetMatrix() * col->boundingBox.max); + + v = col->boundingBox.min; + v.x = col->boundingBox.max.x; + rect.ContainPoint(GetMatrix() * v); + + v = col->boundingBox.max; + v.x = col->boundingBox.min.x; + rect.ContainPoint(GetMatrix() * v); + + return rect; +} + +CVector +CEntity::GetBoundCentre(void) +{ + return GetMatrix() * CModelInfo::GetColModel(m_modelIndex)->boundingSphere.center; +} + +#ifdef GTA_PS2 +void +CEntity::GetBoundCentre(CVuVector &out) +{ + TransformPoint(out, GetMatrix(), CModelInfo::GetColModel(m_modelIndex)->boundingSphere.center); +} +#else +void +CEntity::GetBoundCentre(CVector &out) +{ + out = GetMatrix() * CModelInfo::GetColModel(m_modelIndex)->boundingSphere.center; +} +#endif + +float +CEntity::GetBoundRadius(void) +{ + return CModelInfo::GetColModel(m_modelIndex)->boundingSphere.radius; +} + +void +CEntity::UpdateRwFrame(void) +{ + if(m_rwObject) + RwFrameUpdateObjects((RwFrame*)rwObjectGetParent(m_rwObject)); +} + +void +CEntity::UpdateRpHAnim(void) +{ + if(IsClumpSkinned(GetClump())){ + RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(GetClump()); + RpHAnimHierarchyUpdateMatrices(hier); +#if 0 + int i; + char buf[256]; + if(this == (CEntity*)FindPlayerPed()) + for(i = 0; i < hier->numNodes; i++){ + RpHAnimStdInterpFrame *kf = (RpHAnimStdInterpFrame*)rpHANIMHIERARCHYGETINTERPFRAME(hier, i); + sprintf(buf, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %d %s", + kf->q.imag.x, kf->q.imag.y, kf->q.imag.z, kf->q.real, + kf->t.x, kf->t.y, kf->t.z, + HIERNODEID(hier, i), + ConvertBoneTag2BoneName(HIERNODEID(hier, i))); + CDebug::PrintAt(buf, 10, 1+i*3); + + RwMatrix *m = &RpHAnimHierarchyGetMatrixArray(hier)[i]; + sprintf(buf, "%6.3f %6.3f %6.3f %6.3f", + m->right.x, m->up.x, m->at.x, m->pos.x); + CDebug::PrintAt(buf, 80, 1+i*3+0); + sprintf(buf, "%6.3f %6.3f %6.3f %6.3f", + m->right.y, m->up.y, m->at.y, m->pos.y); + CDebug::PrintAt(buf, 80, 1+i*3+1); + sprintf(buf, "%6.3f %6.3f %6.3f %6.3f", + m->right.z, m->up.z, m->at.z, m->pos.z); + CDebug::PrintAt(buf, 80, 1+i*3+2); + } + + void RenderSkeleton(RpHAnimHierarchy *hier); + RenderSkeleton(hier); +#endif + } +} + +bool +CEntity::HasPreRenderEffects(void) +{ + return IsTreeModel(GetModelIndex()) || + GetModelIndex() == MI_COLLECTABLE1 || + GetModelIndex() == MI_MONEY || + GetModelIndex() == MI_CARMINE || + GetModelIndex() == MI_NAUTICALMINE || + GetModelIndex() == MI_BRIEFCASE || + GetModelIndex() == MI_GRENADE || + GetModelIndex() == MI_MOLOTOV || + GetModelIndex() == MI_MISSILE || + GetModelIndex() == MI_BEACHBALL || + IsGlass(GetModelIndex()) || + IsObject() && ((CObject*)this)->bIsPickup || + IsLightWithPreRenderEffects(GetModelIndex()); +} + +void +CEntity::PreRender(void) +{ + if (CModelInfo::GetModelInfo(GetModelIndex())->GetNum2dEffects() != 0) + ProcessLightsForEntity(); + + if(!bHasPreRenderEffects) + return; + + switch(m_type){ + case ENTITY_TYPE_BUILDING: + if(IsTreeModel(GetModelIndex())){ + float dist = (TheCamera.GetPosition() - GetPosition()).Magnitude2D(); + CObject::fDistToNearestTree = Min(CObject::fDistToNearestTree, dist); + ModifyMatrixForTreeInWind(); + } + break; + case ENTITY_TYPE_OBJECT: + if(GetModelIndex() == MI_COLLECTABLE1){ + CPickups::DoCollectableEffects(this); + GetMatrix().UpdateRW(); + UpdateRwFrame(); + }else if(GetModelIndex() == MI_MONEY){ + CPickups::DoMoneyEffects(this); + GetMatrix().UpdateRW(); + UpdateRwFrame(); + }else if(GetModelIndex() == MI_NAUTICALMINE || + GetModelIndex() == MI_CARMINE || + GetModelIndex() == MI_BRIEFCASE){ + if(((CObject*)this)->bIsPickup){ + CPickups::DoMineEffects(this); + GetMatrix().UpdateRW(); + UpdateRwFrame(); + } + }else if(GetModelIndex() == MI_MISSILE){ + CVector pos = GetPosition(); + float flicker = (CGeneral::GetRandomNumber() & 0xF)/(float)0x10; + CShadows::StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, + gpShadowExplosionTex, &pos, + 8.0f, 0.0f, 0.0f, -8.0f, + 255, 200.0f*flicker, 160.0f*flicker, 120.0f*flicker, + 20.0f, false, 1.0f, nil, false); + CPointLights::AddLight(CPointLights::LIGHT_POINT, + pos, CVector(0.0f, 0.0f, 0.0f), + 8.0f, + 1.0f*flicker, + 0.8f*flicker, + 0.6f*flicker, + CPointLights::FOG_NONE, true); + CCoronas::RegisterCorona((uintptr)this, + 255.0f*flicker, 220.0f*flicker, 190.0f*flicker, 255, + pos, 6.0f*flicker, 80.0f, gpCoronaTexture[CCoronas::TYPE_STAR], + CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + }else if(IsGlass(GetModelIndex())){ + PreRenderForGlassWindow(); + }else if (((CObject*)this)->bIsPickup) { + CPickups::DoPickUpEffects(this); + GetMatrix().UpdateRW(); + UpdateRwFrame(); + } else if (GetModelIndex() == MI_GRENADE) { + CMotionBlurStreaks::RegisterStreak((uintptr)this, + 100, 100, 100, + GetPosition() - 0.07f * TheCamera.GetRight(), + GetPosition() + 0.07f * TheCamera.GetRight()); + } else if (GetModelIndex() == MI_MOLOTOV) { + CMotionBlurStreaks::RegisterStreak((uintptr)this, + 0, 100, 0, + GetPosition() - 0.07f * TheCamera.GetRight(), + GetPosition() + 0.07f * TheCamera.GetRight()); + }else if(GetModelIndex() == MI_BEACHBALL){ + CVector pos = GetPosition(); + CShadows::StoreShadowToBeRendered(SHADOWTYPE_DARK, + gpShadowPedTex, &pos, + 0.4f, 0.0f, 0.0f, 0.4f, + CTimeCycle::GetShadowStrength(), + CTimeCycle::GetShadowStrength(), + CTimeCycle::GetShadowStrength(), + CTimeCycle::GetShadowStrength(), + 20.0f, false, 1.0f, nil, false); + } + // fall through + case ENTITY_TYPE_DUMMY: + if(GetModelIndex() == MI_TRAFFICLIGHTS){ + CTrafficLights::DisplayActualLight(this); + CShadows::StoreShadowForPole(this, 2.957f, 0.147f, 0.0f, 16.0f, 0.4f, 0); + }else if(GetModelIndex() == MI_TRAFFICLIGHTS_VERTICAL){ + CTrafficLights::DisplayActualLight(this); + }else if(GetModelIndex() == MI_TRAFFICLIGHTS_MIAMI){ + CTrafficLights::DisplayActualLight(this); + CShadows::StoreShadowForPole(this, 4.819f, 1.315f, 0.0f, 16.0f, 0.4f, 0); + }else if(GetModelIndex() == MI_TRAFFICLIGHTS_TWOVERTICAL){ + CTrafficLights::DisplayActualLight(this); + CShadows::StoreShadowForPole(this, 7.503f, 0.0f, 0.0f, 16.0f, 0.4f, 0); + }else if(GetModelIndex() == MI_SINGLESTREETLIGHTS1) + CShadows::StoreShadowForPole(this, 0.744f, 0.0f, 0.0f, 16.0f, 0.4f, 0); + else if(GetModelIndex() == MI_SINGLESTREETLIGHTS2) + CShadows::StoreShadowForPole(this, 0.043f, 0.0f, 0.0f, 16.0f, 0.4f, 0); + else if(GetModelIndex() == MI_SINGLESTREETLIGHTS3) + CShadows::StoreShadowForPole(this, 1.143f, 0.145f, 0.0f, 16.0f, 0.4f, 0); + else if(GetModelIndex() == MI_DOUBLESTREETLIGHTS) + CShadows::StoreShadowForPole(this, 0.0f, -0.048f, 0.0f, 16.0f, 0.4f, 0); + break; + } +} + +void +CEntity::Render(void) +{ + if(m_rwObject){ + bImBeingRendered = true; + if(RwObjectGetType(m_rwObject) == rpATOMIC) + RpAtomicRender((RpAtomic*)m_rwObject); + else + RpClumpRender((RpClump*)m_rwObject); + bImBeingRendered = false; + } +} + +bool +CEntity::GetIsTouching(CVUVECTOR const ¢er, float radius) +{ + CVUVECTOR boundCenter; + GetBoundCentre(boundCenter); + return sq(GetBoundRadius()+radius) > (boundCenter-center).MagnitudeSqr(); +} + +bool +CEntity::IsVisible(void) +{ + return m_rwObject && bIsVisible && GetIsOnScreen(); +} + +bool +CEntity::IsVisibleComplex(void) +{ + return m_rwObject && bIsVisible && GetIsOnScreenComplex(); +} + +bool +CEntity::GetIsOnScreen(void) +{ + return TheCamera.IsSphereVisible(GetBoundCentre(), GetBoundRadius()); +} + +bool +CEntity::GetIsOnScreenComplex(void) +{ +#ifdef GTA_PS2 + CVuVector boundBox[8]; +#else + CVector boundBox[8]; +#endif + + if(TheCamera.IsPointVisible(GetBoundCentre(), &TheCamera.GetCameraMatrix())) + return true; + + CRect rect = GetBoundRect(); + CColModel *colmodel = CModelInfo::GetColModel(m_modelIndex); + float z = GetPosition().z; + float minz = z + colmodel->boundingBox.min.z; + float maxz = z + colmodel->boundingBox.max.z; + boundBox[0].x = rect.left; + boundBox[0].y = rect.bottom; + boundBox[0].z = minz; + boundBox[1].x = rect.left; + boundBox[1].y = rect.top; + boundBox[1].z = minz; + boundBox[2].x = rect.right; + boundBox[2].y = rect.bottom; + boundBox[2].z = minz; + boundBox[3].x = rect.right; + boundBox[3].y = rect.top; + boundBox[3].z = minz; + boundBox[4].x = rect.left; + boundBox[4].y = rect.bottom; + boundBox[4].z = maxz; + boundBox[5].x = rect.left; + boundBox[5].y = rect.top; + boundBox[5].z = maxz; + boundBox[6].x = rect.right; + boundBox[6].y = rect.bottom; + boundBox[6].z = maxz; + boundBox[7].x = rect.right; + boundBox[7].y = rect.top; + boundBox[7].z = maxz; + + return TheCamera.IsBoxVisible(boundBox, &TheCamera.GetCameraMatrix()); +} + +void +CEntity::Add(void) +{ + int x, xstart, xmid, xend; + int y, ystart, ymid, yend; + CSector *s; + CPtrList *list; + + CRect bounds = GetBoundRect(); + xstart = CWorld::GetSectorIndexX(bounds.left); + xend = CWorld::GetSectorIndexX(bounds.right); + xmid = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f); + ystart = CWorld::GetSectorIndexY(bounds.top); + yend = CWorld::GetSectorIndexY(bounds.bottom); + ymid = CWorld::GetSectorIndexY((bounds.top + bounds.bottom)/2.0f); + assert(xstart >= 0); + assert(xend < NUMSECTORS_X); + assert(ystart >= 0); + assert(yend < NUMSECTORS_Y); + + for(y = ystart; y <= yend; y++) + for(x = xstart; x <= xend; x++){ + s = CWorld::GetSector(x, y); + if(x == xmid && y == ymid) switch(m_type){ + case ENTITY_TYPE_BUILDING: + list = &s->m_lists[ENTITYLIST_BUILDINGS]; + break; + case ENTITY_TYPE_VEHICLE: + list = &s->m_lists[ENTITYLIST_VEHICLES]; + break; + case ENTITY_TYPE_PED: + list = &s->m_lists[ENTITYLIST_PEDS]; + break; + case ENTITY_TYPE_OBJECT: + list = &s->m_lists[ENTITYLIST_OBJECTS]; + break; + case ENTITY_TYPE_DUMMY: + list = &s->m_lists[ENTITYLIST_DUMMIES]; + break; + }else switch(m_type){ + case ENTITY_TYPE_BUILDING: + list = &s->m_lists[ENTITYLIST_BUILDINGS_OVERLAP]; + break; + case ENTITY_TYPE_VEHICLE: + list = &s->m_lists[ENTITYLIST_VEHICLES_OVERLAP]; + break; + case ENTITY_TYPE_PED: + list = &s->m_lists[ENTITYLIST_PEDS_OVERLAP]; + break; + case ENTITY_TYPE_OBJECT: + list = &s->m_lists[ENTITYLIST_OBJECTS_OVERLAP]; + break; + case ENTITY_TYPE_DUMMY: + list = &s->m_lists[ENTITYLIST_DUMMIES_OVERLAP]; + break; + } + list->InsertItem(this); + } +} + +void +CEntity::Remove(void) +{ + int x, xstart, xmid, xend; + int y, ystart, ymid, yend; + CSector *s; + CPtrList *list; + + CRect bounds = GetBoundRect(); + xstart = CWorld::GetSectorIndexX(bounds.left); + xend = CWorld::GetSectorIndexX(bounds.right); + xmid = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f); + ystart = CWorld::GetSectorIndexY(bounds.top); + yend = CWorld::GetSectorIndexY(bounds.bottom); + ymid = CWorld::GetSectorIndexY((bounds.top + bounds.bottom)/2.0f); + assert(xstart >= 0); + assert(xend < NUMSECTORS_X); + assert(ystart >= 0); + assert(yend < NUMSECTORS_Y); + + for(y = ystart; y <= yend; y++) + for(x = xstart; x <= xend; x++){ + s = CWorld::GetSector(x, y); + if(x == xmid && y == ymid) switch(m_type){ + case ENTITY_TYPE_BUILDING: + list = &s->m_lists[ENTITYLIST_BUILDINGS]; + break; + case ENTITY_TYPE_VEHICLE: + list = &s->m_lists[ENTITYLIST_VEHICLES]; + break; + case ENTITY_TYPE_PED: + list = &s->m_lists[ENTITYLIST_PEDS]; + break; + case ENTITY_TYPE_OBJECT: + list = &s->m_lists[ENTITYLIST_OBJECTS]; + break; + case ENTITY_TYPE_DUMMY: + list = &s->m_lists[ENTITYLIST_DUMMIES]; + break; + }else switch(m_type){ + case ENTITY_TYPE_BUILDING: + list = &s->m_lists[ENTITYLIST_BUILDINGS_OVERLAP]; + break; + case ENTITY_TYPE_VEHICLE: + list = &s->m_lists[ENTITYLIST_VEHICLES_OVERLAP]; + break; + case ENTITY_TYPE_PED: + list = &s->m_lists[ENTITYLIST_PEDS_OVERLAP]; + break; + case ENTITY_TYPE_OBJECT: + list = &s->m_lists[ENTITYLIST_OBJECTS_OVERLAP]; + break; + case ENTITY_TYPE_DUMMY: + list = &s->m_lists[ENTITYLIST_DUMMIES_OVERLAP]; + break; + } + list->RemoveItem(this); + } +} + +float +CEntity::GetDistanceFromCentreOfMassToBaseOfModel(void) +{ + return -CModelInfo::GetColModel(m_modelIndex)->boundingBox.min.z; +} + +void +CEntity::SetupBigBuilding(void) +{ + CSimpleModelInfo *mi; + + mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(m_modelIndex); + bIsBIGBuilding = true; + bStreamingDontDelete = true; + bUsesCollision = false; + m_level = CTheZones::GetLevelFromPosition(&GetPosition()); + if(mi->m_lodDistances[0] <= 2000.0f) + bStreamBIGBuilding = true; + if(mi->m_lodDistances[0] > 2500.0f || mi->m_ignoreDrawDist) + m_level = LEVEL_GENERIC; + else if(m_level == LEVEL_GENERIC) + printf("%s isn't in a level\n", mi->GetModelName()); +} + +float WindTabel[] = { + 1.0f, 0.5f, 0.2f, 0.7f, 0.4f, 1.0f, 0.5f, 0.3f, + 0.2f, 0.1f, 0.7f, 0.6f, 0.3f, 1.0f, 0.5f, 0.2f, +}; + +void +CEntity::ModifyMatrixForTreeInWind(void) +{ + uint16 t; + float f; + float strength, flutter; + + if(CTimer::GetIsPaused()) + return; + + CMatrix mat(GetMatrix().m_attachment); + + if(CWeather::Wind >= 0.5){ + t = m_randomSeed + 8*CTimer::GetTimeInMilliseconds(); + f = (t & 0xFFF)/(float)0x1000; + flutter = f * WindTabel[(t>>12)+1 & 0xF] + + (1.0f - f) * WindTabel[(t>>12) & 0xF] + + 1.0f; + strength = -0.015f*CWeather::Wind; + }else if(CWeather::Wind >= 0.2){ + t = (uintptr)this + CTimer::GetTimeInMilliseconds(); + f = (t & 0xFFF)/(float)0x1000; + flutter = Sin(f * 6.28f); + strength = -0.008f; + }else{ + t = (uintptr)this + CTimer::GetTimeInMilliseconds(); + f = (t & 0xFFF)/(float)0x1000; + flutter = Sin(f * 6.28f); + strength = -0.005f; + } + + mat.GetUp().x = strength * flutter; + if(IsPalmTreeModel(GetModelIndex())) + mat.GetUp().x += -0.07f*CWeather::Wind; + mat.GetUp().y = mat.GetUp().x; + + CWindModifiers::FindWindModifier(GetPosition(), &mat.GetUp().x, &mat.GetUp().y); + + mat.UpdateRW(); + UpdateRwFrame(); +} + +float BannerWindTabel[] = { + 0.0f, 0.3f, 0.6f, 0.85f, 0.99f, 0.97f, 0.65f, 0.15f, + -0.1f, 0.0f, 0.35f, 0.57f, 0.55f, 0.35f, 0.45f, 0.67f, + 0.73f, 0.45f, 0.25f, 0.35f, 0.35f, 0.11f, 0.13f, 0.21f, + 0.28f, 0.28f, 0.22f, 0.1f, 0.0f, -0.1f, -0.17f, -0.12f +}; + +// unused +void +CEntity::ModifyMatrixForBannerInWind(void) +{ + uint16 t; + float f; + float strength, flutter; + CVector right, up; + + if(CTimer::GetIsPaused()) + return; + + if(CWeather::Wind < 0.1f) + strength = 0.2f; + else if(CWeather::Wind < 0.8f) + strength = 0.43f; + else + strength = 0.66f; + + t = ((int)(GetMatrix().GetPosition().x + GetMatrix().GetPosition().y) << 10) + 16*CTimer::GetTimeInMilliseconds(); + f = (t & 0x7FF)/(float)0x800; + flutter = f * BannerWindTabel[(t>>11)+1 & 0x1F] + + (1.0f - f) * BannerWindTabel[(t>>11) & 0x1F]; + flutter *= strength; + + right = CrossProduct(GetForward(), GetUp()); + right.z = 0.0f; + right.Normalise(); + up = right * flutter; + up.z = Sqrt(sq(1.0f) - sq(flutter)); + GetRight() = CrossProduct(GetForward(), up); + GetUp() = up; + + GetMatrix().UpdateRW(); + UpdateRwFrame(); +} + +void +CEntity::PreRenderForGlassWindow(void) +{ + if(((CSimpleModelInfo*)CModelInfo::GetModelInfo(m_modelIndex))->m_isArtistGlass) + return; + CGlass::AskForObjectToBeRenderedInGlass(this); + bIsVisible = false; +} + +RpMaterial* +SetAtomicAlphaCB(RpMaterial *material, void *data) +{ + ((RwRGBA*)RpMaterialGetColor(material))->alpha = (uint8)(uintptr)data; + return material; +} + +RpAtomic* +SetClumpAlphaCB(RpAtomic *atomic, void *data) +{ + RpGeometry *geometry = RpAtomicGetGeometry(atomic); + RpGeometrySetFlags(geometry, RpGeometryGetFlags(geometry) | rpGEOMETRYMODULATEMATERIALCOLOR); + RpGeometryForAllMaterials(geometry, SetAtomicAlphaCB, (void*)data); + return atomic; +} + +void +CEntity::SetRwObjectAlpha(int32 alpha) +{ + if (m_rwObject != nil) { + switch (RwObjectGetType(m_rwObject)) { + case rpATOMIC: { + RpGeometry *geometry = RpAtomicGetGeometry((RpAtomic*)m_rwObject); + RpGeometrySetFlags(geometry, RpGeometryGetFlags(geometry) | rpGEOMETRYMODULATEMATERIALCOLOR); + RpGeometryForAllMaterials(geometry, SetAtomicAlphaCB, (void*)alpha); + break; + } + case rpCLUMP: + RpClumpForAllAtomics((RpClump*)m_rwObject, SetClumpAlphaCB, (void*)alpha); + break; + } + } +} + +bool IsEntityPointerValid(CEntity* pEntity) +{ + if (!pEntity) + return false; + switch (pEntity->GetType()) { + case ENTITY_TYPE_NOTHING: return false; + case ENTITY_TYPE_BUILDING: return IsBuildingPointerValid((CBuilding*)pEntity); + case ENTITY_TYPE_VEHICLE: return IsVehiclePointerValid((CVehicle*)pEntity); + case ENTITY_TYPE_PED: return IsPedPointerValid((CPed*)pEntity); + case ENTITY_TYPE_OBJECT: return IsObjectPointerValid((CObject*)pEntity); + case ENTITY_TYPE_DUMMY: return IsDummyPointerValid((CDummy*)pEntity); + } + return false; +} + +#ifdef COMPATIBLE_SAVES +void +CEntity::SaveEntityFlags(uint8*& buf) +{ + uint32 tmp = 0; + tmp |= (m_type & (BIT(3) - 1)); + tmp |= (m_status & (BIT(5) - 1)) << 3; + + if (bUsesCollision) tmp |= BIT(8); + if (bCollisionProcessed) tmp |= BIT(9); + if (bIsStatic) tmp |= BIT(10); + if (bHasContacted) tmp |= BIT(11); + if (bPedPhysics) tmp |= BIT(12); + if (bIsStuck) tmp |= BIT(13); + if (bIsInSafePosition) tmp |= BIT(14); + if (bUseCollisionRecords) tmp |= BIT(15); + + if (bWasPostponed) tmp |= BIT(16); + if (bExplosionProof) tmp |= BIT(17); + if (bIsVisible) tmp |= BIT(18); + if (bHasCollided) tmp |= BIT(19); + if (bRenderScorched) tmp |= BIT(20); + if (bHasBlip) tmp |= BIT(21); + if (bIsBIGBuilding) tmp |= BIT(22); + if (bStreamBIGBuilding) tmp |= BIT(23); + + if (bRenderDamaged) tmp |= BIT(24); + if (bBulletProof) tmp |= BIT(25); + if (bFireProof) tmp |= BIT(26); + if (bCollisionProof) tmp |= BIT(27); + if (bMeleeProof) tmp |= BIT(28); + if (bOnlyDamagedByPlayer) tmp |= BIT(29); + if (bStreamingDontDelete) tmp |= BIT(30); + if (bRemoveFromWorld) tmp |= BIT(31); + + WriteSaveBuf(buf, tmp); + + tmp = 0; + + if (bHasHitWall) tmp |= BIT(0); + if (bImBeingRendered) tmp |= BIT(1); + if (bTouchingWater) tmp |= BIT(2); + if (bIsSubway) tmp |= BIT(3); + if (bDrawLast) tmp |= BIT(4); + if (bNoBrightHeadLights) tmp |= BIT(5); + if (bDoNotRender) tmp |= BIT(6); + if (bDistanceFade) tmp |= BIT(7); + + if (m_flagE1) tmp |= BIT(8); + if (bDontCastShadowsOn) tmp |= BIT(9); + if (bOffscreen) tmp |= BIT(10); + if (bIsStaticWaitingForCollision) tmp |= BIT(11); + if (bDontStream) tmp |= BIT(12); + if (bUnderwater) tmp |= BIT(13); + if (bHasPreRenderEffects) tmp |= BIT(14); + + WriteSaveBuf(buf, tmp); +} + +void +CEntity::LoadEntityFlags(uint8*& buf) +{ + uint32 tmp; + ReadSaveBuf(&tmp, buf); + m_type = (tmp & ((BIT(3) - 1))); + m_status = ((tmp >> 3) & (BIT(5) - 1)); + + bUsesCollision = !!(tmp & BIT(8)); + bCollisionProcessed = !!(tmp & BIT(9)); + bIsStatic = !!(tmp & BIT(10)); + bHasContacted = !!(tmp & BIT(11)); + bPedPhysics = !!(tmp & BIT(12)); + bIsStuck = !!(tmp & BIT(13)); + bIsInSafePosition = !!(tmp & BIT(14)); + bUseCollisionRecords = !!(tmp & BIT(15)); + + bWasPostponed = !!(tmp & BIT(16)); + bExplosionProof = !!(tmp & BIT(17)); + bIsVisible = !!(tmp & BIT(18)); + bHasCollided = !!(tmp & BIT(19)); + bRenderScorched = !!(tmp & BIT(20)); + bHasBlip = !!(tmp & BIT(21)); + bIsBIGBuilding = !!(tmp & BIT(22)); + bStreamBIGBuilding = !!(tmp & BIT(23)); + + bRenderDamaged = !!(tmp & BIT(24)); + bBulletProof = !!(tmp & BIT(25)); + bFireProof = !!(tmp & BIT(26)); + bCollisionProof = !!(tmp & BIT(27)); + bMeleeProof = !!(tmp & BIT(28)); + bOnlyDamagedByPlayer = !!(tmp & BIT(29)); + bStreamingDontDelete = !!(tmp & BIT(30)); + bRemoveFromWorld = !!(tmp & BIT(31)); + + ReadSaveBuf(&tmp, buf); + + bHasHitWall = !!(tmp & BIT(0)); + bImBeingRendered = !!(tmp & BIT(1)); + bTouchingWater = !!(tmp & BIT(2)); + bIsSubway = !!(tmp & BIT(3)); + bDrawLast = !!(tmp & BIT(4)); + bNoBrightHeadLights = !!(tmp & BIT(5)); + bDoNotRender = !!(tmp & BIT(6)); + bDistanceFade = !!(tmp & BIT(7)); + + m_flagE1 = !!(tmp & BIT(8)); + bDontCastShadowsOn = !!(tmp & BIT(9)); + bOffscreen = !!(tmp & BIT(10)); + bIsStaticWaitingForCollision = !!(tmp & BIT(11)); + bDontStream = !!(tmp & BIT(12)); + bUnderwater = !!(tmp & BIT(13)); + bHasPreRenderEffects = !!(tmp & BIT(14)); +} + +#endif diff --git a/src/miami/entities/Entity.h b/src/miami/entities/Entity.h new file mode 100644 index 00000000..957ee3bf --- /dev/null +++ b/src/miami/entities/Entity.h @@ -0,0 +1,184 @@ +#pragma once + +#include "ModelInfo.h" +#include "Placeable.h" + +struct CReference; +class CPtrList; + +enum eEntityType +{ + ENTITY_TYPE_NOTHING = 0, + ENTITY_TYPE_BUILDING, + ENTITY_TYPE_VEHICLE, + ENTITY_TYPE_PED, + ENTITY_TYPE_OBJECT, + ENTITY_TYPE_DUMMY, +}; + +enum eEntityStatus +{ + STATUS_PLAYER, + STATUS_PLAYER_PLAYBACKFROMBUFFER, + STATUS_SIMPLE, + STATUS_PHYSICS, + STATUS_ABANDONED, + STATUS_WRECKED, + STATUS_TRAIN_MOVING, + STATUS_TRAIN_NOT_MOVING, + STATUS_HELI, + STATUS_PLANE, + STATUS_PLAYER_REMOTE, + STATUS_PLAYER_DISABLED, + STATUS_GHOST +}; + +class CEntity : public CPlaceable +{ +public: + RwObject *m_rwObject; +protected: + uint32 m_type : 3; +private: + uint32 m_status : 5; +public: + // flagsA + uint32 bUsesCollision : 1; // does entity use collision + uint32 bCollisionProcessed : 1; // has object been processed by a ProcessEntityCollision function + uint32 bIsStatic : 1; // is entity static + uint32 bHasContacted : 1; // has entity processed some contact forces + uint32 bPedPhysics : 1; + uint32 bIsStuck : 1; // is entity stuck + uint32 bIsInSafePosition : 1; // is entity in a collision free safe position + uint32 bUseCollisionRecords : 1; + + // flagsB + uint32 bWasPostponed : 1; // was entity control processing postponed + uint32 bExplosionProof : 1; + uint32 bIsVisible : 1; //is the entity visible + uint32 bHasCollided : 1; + uint32 bRenderScorched : 1; + uint32 bHasBlip : 1; + uint32 bIsBIGBuilding : 1; // Set if this entity is a big building + uint32 bStreamBIGBuilding : 1; // set when draw dist <= 2000 + + // flagsC + uint32 bRenderDamaged : 1; // use damaged LOD models for objects with applicable damage + uint32 bBulletProof : 1; + uint32 bFireProof : 1; + uint32 bCollisionProof : 1; + uint32 bMeleeProof : 1; + uint32 bOnlyDamagedByPlayer : 1; + uint32 bStreamingDontDelete : 1; // Dont let the streaming remove this + uint32 bRemoveFromWorld : 1; // remove this entity next time it should be processed + + // flagsD + uint32 bHasHitWall : 1; // has collided with a building (changes subsequent collisions) + uint32 bImBeingRendered : 1; // don't delete me because I'm being rendered + uint32 bTouchingWater : 1; // used by cBuoyancy::ProcessBuoyancy + uint32 bIsSubway : 1; // set when subway, but maybe different meaning? + uint32 bDrawLast : 1; // draw object last + uint32 bNoBrightHeadLights : 1; + uint32 bDoNotRender : 1; //-- only applies to CObjects apparently + uint32 bDistanceFade : 1; // Fade entity because it is far away + + // flagsE + uint32 m_flagE1 : 1; + uint32 bDontCastShadowsOn : 1; // Dont cast shadows on this object + uint32 bOffscreen : 1; // offscreen flag. This can only be trusted when it is set to true + uint32 bIsStaticWaitingForCollision : 1; // this is used by script created entities - they are static until the collision is loaded below them + uint32 bDontStream : 1; // tell the streaming not to stream me + uint32 bUnderwater : 1; // this object is underwater change drawing order + uint32 bHasPreRenderEffects : 1; // Object has a prerender effects attached to it + + uint16 m_scanCode; + uint16 m_randomSeed; + int16 m_modelIndex; + int8 m_level; + int8 m_area; + CReference *m_pFirstReference; + +public: + uint8 GetType() const { return m_type; } + void SetType(uint8 type) { m_type = type; } + uint8 GetStatus() const { return m_status; } + void SetStatus(uint8 status) { m_status = status; } + CColModel *GetColModel(void) { return CModelInfo::GetModelInfo(m_modelIndex)->GetColModel(); } + bool GetIsStatic(void) const { return bIsStatic || bIsStaticWaitingForCollision; } + void SetIsStatic(bool state) { bIsStatic = state; } +#ifdef COMPATIBLE_SAVES + void SaveEntityFlags(uint8*& buf); + void LoadEntityFlags(uint8*& buf); +#else + uint32* GetAddressOfEntityProperties() { /* AWFUL */ return (uint32*)((char*)&m_rwObject + sizeof(m_rwObject)); } +#endif + + CEntity(void); + virtual ~CEntity(void); + + virtual void Add(void); + virtual void Remove(void); + virtual void SetModelIndex(uint32 id); + virtual void SetModelIndexNoCreate(uint32 id); + virtual void CreateRwObject(void); + virtual void DeleteRwObject(void); + virtual CRect GetBoundRect(void); + virtual void ProcessControl(void) {} + virtual void ProcessCollision(void) {} + virtual void ProcessShift(void) {} + virtual void Teleport(CVector v) {} + virtual void PreRender(void); + virtual void Render(void); + virtual bool SetupLighting(void); + virtual void RemoveLighting(bool); + virtual void FlagToDestroyWhenNextProcessed(void) {} + + bool IsBuilding(void) { return m_type == ENTITY_TYPE_BUILDING; } + bool IsVehicle(void) { return m_type == ENTITY_TYPE_VEHICLE; } + bool IsPed(void) { return m_type == ENTITY_TYPE_PED; } + bool IsObject(void) { return m_type == ENTITY_TYPE_OBJECT; } + bool IsDummy(void) { return m_type == ENTITY_TYPE_DUMMY; } + + RpAtomic *GetAtomic(void) { + assert(RwObjectGetType(m_rwObject) == rpATOMIC); + return (RpAtomic*)m_rwObject; + } + RpClump *GetClump(void) { + assert(RwObjectGetType(m_rwObject) == rpCLUMP); + return (RpClump*)m_rwObject; + } + + void GetBoundCentre(CVUVECTOR &out); + CVector GetBoundCentre(void); + float GetBoundRadius(void); + float GetDistanceFromCentreOfMassToBaseOfModel(void); + bool GetIsTouching(CVUVECTOR const ¢er, float r); + bool GetIsOnScreen(void); + bool GetIsOnScreenComplex(void); + bool IsVisible(void); + bool IsVisibleComplex(void); + bool IsEntityOccluded(void); + int16 GetModelIndex(void) const { return m_modelIndex; } + void UpdateRwFrame(void); + void SetupBigBuilding(void); + bool HasPreRenderEffects(void); + + void AttachToRwObject(RwObject *obj); + void DetachFromRwObject(void); + + void RegisterReference(CEntity **pent); + void ResolveReferences(void); + void PruneReferences(void); + void CleanUpOldReference(CEntity **pent); + + void UpdateRpHAnim(void); + + void PreRenderForGlassWindow(void); + void AddSteamsFromGround(CVector *unused); + void ModifyMatrixForTreeInWind(void); + void ModifyMatrixForBannerInWind(void); + void ProcessLightsForEntity(void); + void SetRwObjectAlpha(int32 alpha); +}; + +bool IsEntityPointerValid(CEntity*); diff --git a/src/miami/entities/Physical.cpp b/src/miami/entities/Physical.cpp new file mode 100644 index 00000000..fb796fcd --- /dev/null +++ b/src/miami/entities/Physical.cpp @@ -0,0 +1,2295 @@ +#include "common.h" + +#include "World.h" +#include "General.h" +#include "Timer.h" +#include "Stats.h" +#include "ModelIndices.h" +#include "Treadable.h" +#include "Vehicle.h" +#include "Ped.h" +#include "Object.h" +#include "Glass.h" +#include "ParticleObject.h" +#include "Particle.h" +#include "SurfaceTable.h" +#include "PathFind.h" +#include "CarCtrl.h" +#include "DMAudio.h" +#include "Automobile.h" +#include "Bike.h" +#include "Pickups.h" +#include "Physical.h" + +#ifdef WALLCLIMB_CHEAT +bool gGravityCheat; +#endif + + +CPhysical::CPhysical(void) +{ + int i; + +#ifdef FIX_BUGS + m_nLastTimeCollided = 0; +#endif + + m_fForceMultiplier = 1.0f; + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); + m_vecMoveSpeedAvg = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnSpeedAvg = CVector(0.0f, 0.0f, 0.0f); + + m_movingListNode = nil; + m_nStaticFrames = 0; + + m_nCollisionRecords = 0; + for(i = 0; i < 6; i++) + m_aCollisionRecords[i] = nil; + + m_bIsVehicleBeingShifted = false; + bJustCheckCollision = false; + + m_nDamagePieceType = 0; + m_fDamageImpulse = 0.0f; + m_pDamageEntity = nil; + m_vecDamageNormal = CVector(0.0f, 0.0f, 0.0f); + + bUsesCollision = true; + m_audioEntityId = -5; + m_phys_unused1 = 100.0f; + m_vecCentreOfMass = CVector(0.0f, 0.0f, 0.0f); + m_phys_unused2 = 0; + + bIsHeavy = false; + bAffectedByGravity = true; + bInfiniteMass = false; + m_phy_flagA08 = false; + bIsInWater = false; + bHitByTrain = false; + bSkipLineCol = false; + + m_fDistanceTravelled = 0.0f; + + m_phy_flagA20 = false; + +#ifdef FIX_BUGS + m_nSurfaceTouched = SURFACE_DEFAULT; +#endif + m_nZoneLevel = LEVEL_GENERIC; + + bIsFrozen = false; + bDontLoadCollision = false; +} + +CPhysical::~CPhysical(void) +{ + m_entryInfoList.Flush(); +} + +void +CPhysical::Add(void) +{ + int x, xstart, xmid, xend; + int y, ystart, ymid, yend; + CSector *s; + CPtrList *list; + + CRect bounds = GetBoundRect(); + xstart = CWorld::GetSectorIndexX(bounds.left); + xend = CWorld::GetSectorIndexX(bounds.right); + xmid = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f); + ystart = CWorld::GetSectorIndexY(bounds.top); + yend = CWorld::GetSectorIndexY(bounds.bottom); + ymid = CWorld::GetSectorIndexY((bounds.top + bounds.bottom)/2.0f); + assert(xstart >= 0); + assert(xend < NUMSECTORS_X); + assert(ystart >= 0); + assert(yend < NUMSECTORS_Y); + + for(y = ystart; y <= yend; y++) + for(x = xstart; x <= xend; x++){ + s = CWorld::GetSector(x, y); + if(x == xmid && y == ymid) switch(m_type){ + case ENTITY_TYPE_VEHICLE: + list = &s->m_lists[ENTITYLIST_VEHICLES]; + break; + case ENTITY_TYPE_PED: + list = &s->m_lists[ENTITYLIST_PEDS]; + break; + case ENTITY_TYPE_OBJECT: + list = &s->m_lists[ENTITYLIST_OBJECTS]; + break; + default: + assert(0); + }else switch(m_type){ + case ENTITY_TYPE_VEHICLE: + list = &s->m_lists[ENTITYLIST_VEHICLES_OVERLAP]; + break; + case ENTITY_TYPE_PED: + list = &s->m_lists[ENTITYLIST_PEDS_OVERLAP]; + break; + case ENTITY_TYPE_OBJECT: + list = &s->m_lists[ENTITYLIST_OBJECTS_OVERLAP]; + break; + default: + assert(0); + } + CPtrNode *node = list->InsertItem(this); + assert(node); + m_entryInfoList.InsertItem(list, node, s); + } +} + +void +CPhysical::Remove(void) +{ + CEntryInfoNode *node, *next; + for(node = m_entryInfoList.first; node; node = next){ + next = node->next; + node->list->DeleteNode(node->listnode); + m_entryInfoList.DeleteNode(node); + } +} + +void +CPhysical::RemoveAndAdd(void) +{ + int x, xstart, xmid, xend; + int y, ystart, ymid, yend; + CSector *s; + CPtrList *list; + + CRect bounds = GetBoundRect(); + xstart = CWorld::GetSectorIndexX(bounds.left); + xend = CWorld::GetSectorIndexX(bounds.right); + xmid = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f); + ystart = CWorld::GetSectorIndexY(bounds.top); + yend = CWorld::GetSectorIndexY(bounds.bottom); + ymid = CWorld::GetSectorIndexY((bounds.top + bounds.bottom)/2.0f); + assert(xstart >= 0); + assert(xend < NUMSECTORS_X); + assert(ystart >= 0); + assert(yend < NUMSECTORS_Y); + + // we'll try to recycle nodes from here + CEntryInfoNode *next = m_entryInfoList.first; + + for(y = ystart; y <= yend; y++) + for(x = xstart; x <= xend; x++){ + s = CWorld::GetSector(x, y); + if(x == xmid && y == ymid) switch(m_type){ + case ENTITY_TYPE_VEHICLE: + list = &s->m_lists[ENTITYLIST_VEHICLES]; + break; + case ENTITY_TYPE_PED: + list = &s->m_lists[ENTITYLIST_PEDS]; + break; + case ENTITY_TYPE_OBJECT: + list = &s->m_lists[ENTITYLIST_OBJECTS]; + break; + }else switch(m_type){ + case ENTITY_TYPE_VEHICLE: + list = &s->m_lists[ENTITYLIST_VEHICLES_OVERLAP]; + break; + case ENTITY_TYPE_PED: + list = &s->m_lists[ENTITYLIST_PEDS_OVERLAP]; + break; + case ENTITY_TYPE_OBJECT: + list = &s->m_lists[ENTITYLIST_OBJECTS_OVERLAP]; + break; + } + if(next){ + // If we still have old nodes, use them + next->list->RemoveNode(next->listnode); + list->InsertNode(next->listnode); + next->list = list; + next->sector = s; + next = next->next; + }else{ + CPtrNode *node = list->InsertItem(this); + m_entryInfoList.InsertItem(list, node, s); + } + } + + // Remove old nodes we no longer need + CEntryInfoNode *node; + for(node = next; node; node = next){ + next = node->next; + node->list->DeleteNode(node->listnode); + m_entryInfoList.DeleteNode(node); + } +} + +CRect +CPhysical::GetBoundRect(void) +{ + CVector center; + float radius; + center = GetBoundCentre(); + radius = GetBoundRadius(); + return CRect(center.x-radius, center.y-radius, center.x+radius, center.y+radius); +} + +void +CPhysical::AddToMovingList(void) +{ + if (!bIsStaticWaitingForCollision) + m_movingListNode = CWorld::GetMovingEntityList().InsertItem(this); +} + +void +CPhysical::RemoveFromMovingList(void) +{ + if(m_movingListNode){ + CWorld::GetMovingEntityList().DeleteNode(m_movingListNode); + m_movingListNode = nil; + } +} + +void +CPhysical::SetDamagedPieceRecord(uint16 piece, float impulse, CEntity *entity, CVector dir) +{ + m_nDamagePieceType = piece; + m_fDamageImpulse = impulse; + m_pDamageEntity = entity; + entity->RegisterReference(&m_pDamageEntity); + m_vecDamageNormal = dir; +} + +void +CPhysical::AddCollisionRecord(CEntity *ent) +{ + AddCollisionRecord_Treadable(ent); + this->bHasCollided = true; + ent->bHasCollided = true; + this->m_nLastTimeCollided = CTimer::GetTimeInMilliseconds(); + if(IsVehicle() && ent->IsVehicle()){ + if(((CVehicle*)this)->m_nAlarmState == -1) + ((CVehicle*)this)->m_nAlarmState = 15000; + if(((CVehicle*)ent)->m_nAlarmState == -1) + ((CVehicle*)ent)->m_nAlarmState = 15000; + } + if(bUseCollisionRecords){ + int i; + for(i = 0; i < m_nCollisionRecords; i++) + if(m_aCollisionRecords[i] == ent) + return; + if(m_nCollisionRecords < PHYSICAL_MAX_COLLISIONRECORDS) + m_aCollisionRecords[m_nCollisionRecords++] = ent; + } +} + +void +CPhysical::AddCollisionRecord_Treadable(CEntity *ent) +{ + if(ent->IsBuilding() && ((CBuilding*)ent)->GetIsATreadable()){ + } +} + +bool +CPhysical::GetHasCollidedWith(CEntity *ent) +{ + int i; + if(bUseCollisionRecords) + for(i = 0; i < m_nCollisionRecords; i++) + if(m_aCollisionRecords[i] == ent) + return true; + return false; +} + +void +CPhysical::RemoveRefsToEntity(CEntity *ent) +{ + int i = 0, j; + + while (i < m_nCollisionRecords){ + if(m_aCollisionRecords[i] == ent){ + for(j = i; j < m_nCollisionRecords-1; j++) + m_aCollisionRecords[j] = m_aCollisionRecords[j+1]; + m_nCollisionRecords--; + } else + i++; + } +} + +void +CPhysical::PlacePhysicalRelativeToOtherPhysical(CPhysical *other, CPhysical *phys, CVector localPos) +{ + CVector worldPos = other->GetMatrix() * localPos; + float step = 0.9f * CTimer::GetTimeStep(); + CVector pos = other->m_vecMoveSpeed*step + worldPos; + + CWorld::Remove(phys); + phys->GetMatrix() = other->GetMatrix(); + phys->SetPosition(pos); + phys->m_vecMoveSpeed = other->m_vecMoveSpeed; + phys->GetMatrix().UpdateRW(); + phys->UpdateRwFrame(); + CWorld::Add(phys); +} + +int32 +CPhysical::ProcessEntityCollision(CEntity *ent, CColPoint *colpoints) +{ + int32 numSpheres = CCollision::ProcessColModels( + GetMatrix(), *GetColModel(), + ent->GetMatrix(), *ent->GetColModel(), + colpoints, + nil, nil); // No Lines allowed! + if(numSpheres > 0){ + AddCollisionRecord(ent); + if(!ent->IsBuilding()) // Can't this catch dummies too? + ((CPhysical*)ent)->AddCollisionRecord(this); + if(ent->IsBuilding() || ent->GetIsStatic()) + this->bHasHitWall = true; + } + return numSpheres; +} + +void +CPhysical::ProcessControl(void) +{ + if(!IsPed()) + bIsInWater = false; + bHasContacted = false; + bIsInSafePosition = false; + bWasPostponed = false; + bHasHitWall = false; + + if(GetStatus() == STATUS_SIMPLE) + return; + + m_nCollisionRecords = 0; + bHasCollided = false; + m_nDamagePieceType = 0; + m_fDamageImpulse = 0.0f; + m_pDamageEntity = nil; + + if(!bIsStuck){ + if(IsObject() || + IsPed() && !bPedPhysics){ + m_vecMoveSpeedAvg = (m_vecMoveSpeedAvg + m_vecMoveSpeed)/2.0f; + m_vecTurnSpeedAvg = (m_vecTurnSpeedAvg + m_vecTurnSpeed)/2.0f; + float step = CTimer::GetTimeStep() * 0.003f; + if(m_vecMoveSpeedAvg.MagnitudeSqr() < step*step && + m_vecTurnSpeedAvg.MagnitudeSqr() < step*step){ + m_nStaticFrames++; + if(m_nStaticFrames > 10){ + m_nStaticFrames = 10; + SetIsStatic(true); + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecMoveFriction = m_vecMoveSpeed; + m_vecTurnFriction = m_vecTurnSpeed; + return; + } + }else + m_nStaticFrames = 0; + } + } + ApplyGravity(); + ApplyFriction(); + ApplyAirResistance(); +} + +/* + * Some quantities (german in parens): + * + * acceleration: distance/time^2: a + * velocity: distance/time: v (GTA: speed) + * momentum (impuls): velocity*mass: p + * impulse (kraftstoss): delta momentum, force*time: J + * + * angular equivalents: + * velocity -> angular velocity (GTA: turn speed) + * momentum -> angular momentum (drehimpuls): L = r cross p + * force -> torque (drehmoment): tau = r cross F + * mass -> moment of inertia, angular mass (drehmoment, drehmasse): I = L/omega (GTA: turn mass) + */ + +CVector +CPhysical::GetSpeed(const CVector &r) +{ + return m_vecMoveSpeed + m_vecMoveFriction + CrossProduct(m_vecTurnFriction + m_vecTurnSpeed, r); +} + +void +CPhysical::ApplyMoveSpeed(void) +{ + if(bIsFrozen) + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + else + GetMatrix().Translate(m_vecMoveSpeed * CTimer::GetTimeStep()); +} + +void +CPhysical::ApplyTurnSpeed(void) +{ + if(bIsFrozen){ + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + }else{ + // Move the coordinate axes by their speed + // Note that this denormalizes the matrix + CVector turnvec = m_vecTurnSpeed*CTimer::GetTimeStep(); + GetRight() += CrossProduct(turnvec, GetRight()); + GetForward() += CrossProduct(turnvec, GetForward()); + GetUp() += CrossProduct(turnvec, GetUp()); + } +} + +void +CPhysical::ApplyMoveForce(float jx, float jy, float jz) +{ + m_vecMoveSpeed += CVector(jx, jy, jz)*(1.0f/m_fMass); +} + +void +CPhysical::ApplyTurnForce(float jx, float jy, float jz, float px, float py, float pz) +{ + CVector com = Multiply3x3(GetMatrix(), m_vecCentreOfMass); + CVector turnimpulse = CrossProduct(CVector(px, py, pz)-com, CVector(jx, jy, jz)); + m_vecTurnSpeed += turnimpulse*(1.0f/m_fTurnMass); +} + +void +CPhysical::ApplyFrictionMoveForce(float jx, float jy, float jz) +{ + m_vecMoveFriction += CVector(jx, jy, jz)*(1.0f/m_fMass); +} + +void +CPhysical::ApplyFrictionTurnForce(float jx, float jy, float jz, float px, float py, float pz) +{ + CVector com = Multiply3x3(GetMatrix(), m_vecCentreOfMass); + CVector turnimpulse = CrossProduct(CVector(px, py, pz)-com, CVector(jx, jy, jz)); + m_vecTurnFriction += turnimpulse*(1.0f/m_fTurnMass); +} + +bool +CPhysical::ApplySpringCollision(float springConst, CVector &springDir, CVector &point, float springRatio, float bias) +{ + float compression = 1.0f - springRatio; + if(compression > 0.0f){ + float step = Min(CTimer::GetTimeStep(), 3.0f); + float impulse = -GRAVITY*m_fMass*step * springConst * compression * bias*2.0f; + ApplyMoveForce(springDir*impulse); + ApplyTurnForce(springDir*impulse, point); + } + return true; +} + +bool +CPhysical::ApplySpringCollisionAlt(float springConst, CVector &springDir, CVector &point, float springRatio, float bias, CVector &forceDir) +{ + float compression = 1.0f - springRatio; + if(compression > 0.0f){ + if(DotProduct(springDir, forceDir) > 0.0f) + forceDir *= -1.0f; + float step = Min(CTimer::GetTimeStep(), 3.0f); + float impulse = GRAVITY*m_fMass*step * springConst * compression * bias*2.0f; + if(bIsHeavy) + impulse *= 0.75f; + ApplyMoveForce(forceDir*impulse); + ApplyTurnForce(forceDir*impulse, point); + } + return true; +} + +// What exactly is speed? +bool +CPhysical::ApplySpringDampening(float damping, CVector &springDir, CVector &point, CVector &speed) +{ + float speedA = DotProduct(speed, springDir); + float speedB = DotProduct(GetSpeed(point), springDir); +#ifdef FIX_BUGS + if (speedB == 0.0f) + return true; +#endif + float step = Min(CTimer::GetTimeStep(), 3.0f); + float impulse = -damping * (speedA + speedB)/2.0f * m_fMass * step * 0.53f; + if(bIsHeavy) + impulse *= 2.0f; + + // what is this? + float a = m_fTurnMass / ((point.MagnitudeSqr() + 1.0f) * 2.0f * m_fMass); + a = Min(a, 1.0f); + float b = Abs(impulse / (speedB * m_fMass)); + if(a < b) + impulse *= a/b; + + ApplyMoveForce(springDir*impulse); + ApplyTurnForce(springDir*impulse, point); + return true; +} + +void +CPhysical::ApplyGravity(void) +{ + if (!bAffectedByGravity) + return; +#ifdef WALLCLIMB_CHEAT + if (gGravityCheat && this == FindPlayerVehicle()) { + static CVector gravityUp(0.0f, 0.0f, 1.0f), surfaceUp(0.0f, 0.0f, 1.0f); + CVector belowCar = GetPosition() - 2.0f*GetUp(); + CColPoint point; + CEntity* entity; + if (CWorld::ProcessLineOfSight(GetPosition(), belowCar, point, entity, true, false, false, false, false, false)) + surfaceUp = point.normal; + else + surfaceUp = CVector(0.0f, 0.0f, 1.0f); + float t = Clamp(CTimer::GetTimeStep() * 0.5f, 0.05f, 0.8f); + gravityUp = gravityUp * (1.0f - t) + surfaceUp * t; + if (gravityUp.MagnitudeSqr() < 0.1f) + gravityUp = CVector(0.0f, 0.0f, 1.0f); + else + gravityUp.Normalise(); + m_vecMoveSpeed -= GRAVITY * CTimer::GetTimeStep() * gravityUp; + return; + } +#endif + m_vecMoveSpeed.z -= GRAVITY * CTimer::GetTimeStep(); +} + +void +CPhysical::ApplyFriction(void) +{ + m_vecMoveSpeed += m_vecMoveFriction; + m_vecTurnSpeed += m_vecTurnFriction; + m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); +} + +void +CPhysical::ApplyAirResistance(void) +{ + if(m_fAirResistance > 0.1f){ + float f = Pow(m_fAirResistance, CTimer::GetTimeStep()); + m_vecMoveSpeed *= f; + m_vecTurnSpeed *= f; + }else if(GetStatus() != STATUS_GHOST){ + float f = Pow(1.0f/Abs(1.0f + m_fAirResistance*0.5f*m_vecMoveSpeed.MagnitudeSqr()), CTimer::GetTimeStep()); + m_vecMoveSpeed *= f; + m_vecTurnSpeed *= 0.99f; + } +} + +bool +CPhysical::ApplyCollision(CPhysical *B, CColPoint &colpoint, float &impulseA, float &impulseB) +{ + float eA, eB; + CPhysical *A = this; + CObject *Bobj = (CObject*)B; + + bool foo = false; // TODO: what does this mean? + bool ispedcontactA = false; + bool ispedcontactB = false; + + float massFactorA; + if(B->bPedPhysics){ + massFactorA = 10.0f; + if(B->IsPed() && ((CPed*)B)->m_pCurrentPhysSurface == A) + ispedcontactA = true; + }else + massFactorA = A->bIsHeavy ? 2.0f : 1.0f; + + float massFactorB; + if(A->bPedPhysics){ + if(A->IsPed() && ((CPed*)A)->IsPlayer() && B->IsVehicle() && + (B->GetStatus() == STATUS_ABANDONED || B->GetStatus() == STATUS_WRECKED || A->bHasHitWall)) + massFactorB = 1.0f/(Max(B->m_fMass - 2000.0f, 0.0f)/5000.0f + 1.0f); + else + massFactorB = 10.0f; + + if(A->IsPed() && ((CPed*)A)->m_pCurrentPhysSurface == B) + ispedcontactB = true; + }else + massFactorB = B->bIsHeavy ? 2.0f : 1.0f; + + if(B->bInfiniteMass && !B->m_phy_flagA08){ + ispedcontactB = false; + foo = true; + } + + float speedA, speedB; + if(B->GetIsStatic() && !foo){ + if(A->bPedPhysics){ + speedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal); + if(speedA < 0.0f){ + if(B->IsObject()){ + impulseA = -speedA * A->m_fMass; + impulseB = impulseA; + if(impulseA > Bobj->m_fUprootLimit){ + if(IsGlass(B->GetModelIndex())) + CGlass::WindowRespondsToCollision(B, impulseA, A->m_vecMoveSpeed, colpoint.point, false); + else if(!B->bInfiniteMass){ + B->SetIsStatic(false); + CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel += 2; + CStats::PropertyDestroyed += CGeneral::GetRandomNumberInRange(30, 60); + } + }else{ + if(IsGlass(B->GetModelIndex())) + CGlass::WindowRespondsToSoftCollision(B, impulseA); + if(!A->bInfiniteMass) + A->ApplyMoveForce(colpoint.GetNormal() * (1.0f + A->m_fElasticity) * impulseA); + return true; + } + }else if(!B->bInfiniteMass) + B->SetIsStatic(false); + + if(B->bInfiniteMass){ + impulseA = -speedA * A->m_fMass; + impulseB = 0.0f; + if(!A->bInfiniteMass) + A->ApplyMoveForce(colpoint.normal*(1.0f + A->m_fElasticity)*impulseA); + return true; + } + } + }else{ + CVector pointposA = colpoint.point - A->GetPosition(); + speedA = DotProduct(A->GetSpeed(pointposA), colpoint.normal); + if(speedA < 0.0f){ + if(B->IsObject()){ + if(A->bHasHitWall) + eA = -1.0f; + else + eA = -(1.0f + A->m_fElasticity); + impulseA = eA * speedA * A->GetMass(pointposA, colpoint.normal); + impulseB = impulseA; + + if(Bobj->m_nCollisionDamageEffect && impulseA > 20.0f){ + Bobj->ObjectDamage(impulseA); + if(!B->bUsesCollision){ + if(!A->bInfiniteMass){ + A->ApplyMoveForce(colpoint.normal*0.2f*impulseA); + A->ApplyTurnForce(colpoint.normal*0.2f*impulseA, pointposA); + } + return false; + } + } + + if((impulseA > Bobj->m_fUprootLimit || A->bIsStuck) && + !B->bInfiniteMass){ + if(IsGlass(B->GetModelIndex())) + CGlass::WindowRespondsToCollision(B, impulseA, A->m_vecMoveSpeed, colpoint.point, false); + else + B->SetIsStatic(false); + int16 model = B->GetModelIndex(); + if(model == MI_FIRE_HYDRANT && !Bobj->bHasBeenDamaged){ + CParticleObject::AddObject(POBJECT_FIRE_HYDRANT, B->GetPosition() - CVector(0.0f, 0.0f, 0.5f), true); + Bobj->bHasBeenDamaged = true; + }else if((model == MI_PARKINGMETER || model == MI_PARKINGMETER2) && !Bobj->bHasBeenDamaged){ + CPickups::CreateSomeMoney(GetPosition(), CGeneral::GetRandomNumber()%100); + Bobj->bHasBeenDamaged = true; + }else if(B->IsObject() && !IsExplosiveThingModel(model)) + Bobj->bHasBeenDamaged = true; + }else{ + if(IsGlass(B->GetModelIndex())) + CGlass::WindowRespondsToSoftCollision(B, impulseA); + CVector f = colpoint.GetNormal() * impulseA; + if(A->IsVehicle() && colpoint.normal.z < 0.7f) + f.z *= 0.3f; + if(!A->bInfiniteMass){ + A->ApplyMoveForce(f); + if(!A->IsVehicle() || !CWorld::bNoMoreCollisionTorque) + A->ApplyTurnForce(f, pointposA); + } + return true; + } + }else if(!B->bInfiniteMass){ + B->SetIsStatic(false); + CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel += 2; + CStats::PropertyDestroyed += CGeneral::GetRandomNumberInRange(30, 60); + } + } + } + + if(B->GetIsStatic()) + return false; + if(!B->bInfiniteMass && !B->bIsStaticWaitingForCollision) + B->AddToMovingList(); + } + + // B is not static + + if(A->bPedPhysics && B->bPedPhysics){ + // negative if A is moving towards B + speedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal); + // positive if B is moving towards A + speedB = DotProduct(B->m_vecMoveSpeed, colpoint.normal); + + bool affectB = false; + float mA = A->m_fMass; + float mB = B->m_fMass; + float speedSum; + if(((CPed*)A)->GetPedState() == PED_FOLLOW_PATH){ + affectB = true; + speedSum = (2.0f*mA*speedA + mB*speedB)/(2.0f*mA + mB); + }else{ + speedSum = Max(speedB, 0.0f); + } + + if(speedA < speedSum){ + if(A->bHasHitWall) + eA = speedSum; + else + eA = speedSum - (speedA - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f; + impulseA = (eA-speedA) * mA; + if(!A->bInfiniteMass) + A->ApplyMoveForce(colpoint.normal*impulseA); + if(affectB && speedB < speedSum){ + if(B->bHasHitWall) + eB = speedSum; + else + eB = speedSum - (speedB - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f; + impulseB = -(eB-speedB) * mB; + if(!B->bInfiniteMass) + B->ApplyMoveForce(colpoint.normal*-impulseB); + } + return true; + } + }else if(A->bPedPhysics){ + CVector pointposB = colpoint.point - B->GetPosition(); + speedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal); + speedB = DotProduct(B->GetSpeed(pointposB), colpoint.normal); + + float mA = A->m_fMass*massFactorA; + float mB = B->GetMassTweak(pointposB, colpoint.normal, massFactorB); + float speedSum; + if(foo) + speedSum = speedB; + else + speedSum = (mB*speedB + mA*speedA)/(mA + mB); + if(speedA < speedSum){ + if(A->bHasHitWall) + eA = speedSum; + else + eA = speedSum - (speedA - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f; + if(B->bHasHitWall) + eB = speedSum; + else + eB = speedSum - (speedB - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f; + impulseA = (eA - speedA) * mA; + impulseB = -(eB - speedB) * mB; + CVector fA = colpoint.normal*(impulseA/massFactorA); + CVector fB = colpoint.normal*(-impulseB/massFactorB); + if(!A->bInfiniteMass){ + if(fA.z < 0.0f) fA.z = 0.0f; + if(ispedcontactB){ + fA.x *= 2.0f; + fA.y *= 2.0f; + } + A->ApplyMoveForce(fA); + } + if(!B->bInfiniteMass && !ispedcontactB){ + B->ApplyMoveForce(fB); + B->ApplyTurnForce(fB, pointposB); + } + return true; + } + }else if(B->bPedPhysics){ + CVector pointposA = colpoint.point - A->GetPosition(); + speedA = DotProduct(A->GetSpeed(pointposA), colpoint.normal); + speedB = DotProduct(B->m_vecMoveSpeed, colpoint.normal); + + float mA = A->GetMassTweak(pointposA, colpoint.normal, massFactorA); + float mB = B->m_fMass*massFactorB; + float speedSum = (mB*speedB + mA*speedA)/(mA + mB); + if(speedA < speedSum){ + if(A->bHasHitWall) + eA = speedSum; + else + eA = speedSum - (speedA - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f; + if(B->bHasHitWall) + eB = speedSum; + else + eB = speedSum - (speedB - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f; + impulseA = (eA - speedA) * mA; + impulseB = -(eB - speedB) * mB; + CVector fA = colpoint.normal*(impulseA/massFactorA); + CVector fB = colpoint.normal*(-impulseB/massFactorB); + if(!A->bInfiniteMass && !ispedcontactA){ + if(fA.z < 0.0f) fA.z = 0.0f; + A->ApplyMoveForce(fA); + A->ApplyTurnForce(fA, pointposA); + } + if(!B->bInfiniteMass){ + if(fB.z < 0.0f){ + fB.z = 0.0f; + if(Abs(speedA) < 0.01f) + fB *= 0.5f; + } + if(ispedcontactA){ + fB.x *= 2.0f; + fB.y *= 2.0f; + } + B->ApplyMoveForce(fB); + } + return true; + } + }else{ + CVector pointposA = colpoint.point - A->GetPosition(); + CVector pointposB = colpoint.point - B->GetPosition(); + speedA = DotProduct(A->GetSpeed(pointposA), colpoint.normal); + speedB = DotProduct(B->GetSpeed(pointposB), colpoint.normal); + float mA = A->GetMassTweak(pointposA, colpoint.normal, massFactorA); + float mB = B->GetMassTweak(pointposB, colpoint.normal, massFactorB); + float speedSum = (mB*speedB + mA*speedA)/(mA + mB); + if(speedA < speedSum){ + if(A->bHasHitWall) + eA = speedSum; + else + eA = speedSum - (speedA - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f; + if(B->bHasHitWall) + eB = speedSum; + else + eB = speedSum - (speedB - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f; + impulseA = (eA - speedA) * mA; + impulseB = -(eB - speedB) * mB; + CVector fA = colpoint.normal*(impulseA/massFactorA); + CVector fB = colpoint.normal*(-impulseB/massFactorB); + if(A->IsVehicle() && !A->bHasHitWall){ + fA.x *= 1.4f; + fA.y *= 1.4f; + if(colpoint.normal.z < 0.7f) + fA.z *= 0.3f; + if(A->GetStatus() == STATUS_PLAYER) + pointposA *= 0.8f; + if(CWorld::bNoMoreCollisionTorque){ + A->ApplyFrictionMoveForce(fA*-0.3f); + A->ApplyFrictionTurnForce(fA*-0.3f, pointposA); + } + } + if(B->IsVehicle() && !B->bHasHitWall){ + fB.x *= 1.4f; + fB.y *= 1.4f; + if(-colpoint.normal.z < 0.7f) + fB.z *= 0.3f; + if(B->GetStatus() == STATUS_PLAYER) + pointposB *= 0.8f; + if(CWorld::bNoMoreCollisionTorque){ +#ifdef FIX_BUGS + B->ApplyFrictionMoveForce(fB*-0.3f); + B->ApplyFrictionTurnForce(fB*-0.3f, pointposB); +#else + A->ApplyFrictionMoveForce(fB*-0.3f); + A->ApplyFrictionTurnForce(fB*-0.3f, pointposB); +#endif + } + } + if(!A->bInfiniteMass){ + A->ApplyMoveForce(fA); + A->ApplyTurnForce(fA, pointposA); + } + if(!B->bInfiniteMass){ + if(B->bIsInSafePosition) + B->UnsetIsInSafePosition(); + B->ApplyMoveForce(fB); + B->ApplyTurnForce(fB, pointposB); + } + return true; + } + } + return false; +} + +bool +CPhysical::ApplyCollision(CColPoint &colpoint, float &impulse) +{ + float speed; + if(bPedPhysics){ + speed = DotProduct(m_vecMoveSpeed, colpoint.normal); + if(speed < 0.0f){ + impulse = -speed * m_fMass; + ApplyMoveForce(colpoint.normal*impulse); + return true; + } + }else{ + CVector pointpos = colpoint.point - GetPosition(); + speed = DotProduct(GetSpeed(pointpos), colpoint.normal); + + if(speed < 0.0f){ + float mass = GetMass(pointpos, colpoint.normal); + impulse = -(m_fElasticity + 1.0f) * speed * mass; + CVector f = colpoint.normal*impulse; + if(IsVehicle()){ + f.x *= 1.4f; + f.y *= 1.4f; + if(colpoint.normal.z < 0.7f) + f.z *= 0.3f; + } + if(!bInfiniteMass){ + ApplyMoveForce(f); + if(!IsVehicle() || !CWorld::bNoMoreCollisionTorque) + ApplyTurnForce(f, pointpos); + } + return true; + } + } + + return false; +} + +bool +CPhysical::ApplyCollisionAlt(CEntity *B, CColPoint &colpoint, float &impulse, CVector &moveSpeed, CVector &turnSpeed) +{ + float normalSpeed; + CVector speed; + CVector vImpulse; + + if(GetModelIndex() == MI_BEACHBALL && B != (CEntity*)FindPlayerPed()) + ((CObject*)this)->m_nBeachballBounces = 0; + + if(bPedPhysics){ + normalSpeed = DotProduct(m_vecMoveSpeed, colpoint.normal); + if(normalSpeed < 0.0f){ + impulse = -normalSpeed * m_fMass; + ApplyMoveForce(colpoint.normal * impulse); + return true; + } + }else{ + CVector pointpos = colpoint.point - GetPosition(); + speed = GetSpeed(pointpos); + normalSpeed = DotProduct(speed, colpoint.normal); + if(normalSpeed < 0.0f){ + int16 elasticityType = 0; + float mass = GetMass(pointpos, colpoint.normal); + float minspeed = GRAVITY * CTimer::GetTimeStep(); + + if(IsObject()) + elasticityType = 1; + else if(IsVehicle() && !bIsInWater){ + if(((CVehicle*)this)->IsBike() && (GetStatus() == STATUS_ABANDONED || GetStatus() == STATUS_WRECKED)){ + minspeed *= 1.3f; + elasticityType = 3; + }else if(((CVehicle*)this)->IsBoat()){ + minspeed *= 1.2f; + elasticityType = 4; + }else if(GetUp().z < -0.3f){ + minspeed *= 1.1f; + elasticityType = 2; + } + } + + if(elasticityType == 1 && !bHasContacted && + Abs(m_vecMoveSpeed.x) < minspeed && + Abs(m_vecMoveSpeed.y) < minspeed && + Abs(m_vecMoveSpeed.z) < minspeed*2.0f) + impulse = -0.98f * normalSpeed * mass; + if(elasticityType == 3 && + Abs(m_vecMoveSpeed.x) < minspeed && + Abs(m_vecMoveSpeed.y) < minspeed && + Abs(m_vecMoveSpeed.z) < minspeed*2.0f) + impulse = -0.8f * normalSpeed * mass; + else if(elasticityType == 2 && + Abs(m_vecMoveSpeed.x) < minspeed && + Abs(m_vecMoveSpeed.y) < minspeed && + Abs(m_vecMoveSpeed.z) < minspeed*2.0f) + impulse = -0.92f * normalSpeed * mass; + else if(elasticityType == 4 && + Abs(m_vecMoveSpeed.x) < minspeed && + Abs(m_vecMoveSpeed.y) < minspeed && + Abs(m_vecMoveSpeed.z) < minspeed*2.0f) + impulse = -0.8f * normalSpeed * mass; + else if(IsVehicle() && ((CVehicle*)this)->IsBoat() && + (colpoint.surfaceB == SURFACE_WOOD_SOLID || colpoint.normal.z < 0.5f)) + impulse = -(2.0f * m_fElasticity + 1.0f) * normalSpeed * mass; + else + impulse = -(m_fElasticity + 1.0f) * normalSpeed * mass; + + // ApplyMoveForce + vImpulse = colpoint.normal*impulse; + if(IsVehicle()){ + if(!bHasHitWall || + !(m_vecMoveSpeed.MagnitudeSqr() > 0.1 || !(B->IsBuilding() || ((CPhysical*)B)->bInfiniteMass))) + moveSpeed += vImpulse * 1.2f * (1.0f/m_fMass); + else + moveSpeed += vImpulse * (1.0f/m_fMass); + vImpulse *= 0.8f; + }else + moveSpeed += vImpulse * (1.0f/m_fMass); + + // ApplyTurnForce + CVector com = Multiply3x3(GetMatrix(), m_vecCentreOfMass); + CVector turnimpulse = CrossProduct(pointpos-com, vImpulse); + turnSpeed += turnimpulse*(1.0f/m_fTurnMass); + + return true; + } + } + return false; +} + +bool +CPhysical::ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint) +{ + CVector speedA, speedB; + float normalSpeedA, normalSpeedB; + CVector vOtherSpeedA, vOtherSpeedB; + float fOtherSpeedA, fOtherSpeedB; + float speedSum; + CVector frictionDir; + float impulseA, impulseB; + float impulseLimit; + CPhysical *A = this; + + if(A->bPedPhysics && B->bPedPhysics){ + normalSpeedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal); + normalSpeedB = DotProduct(B->m_vecMoveSpeed, colpoint.normal); + vOtherSpeedA = A->m_vecMoveSpeed - colpoint.normal*normalSpeedA; + vOtherSpeedB = B->m_vecMoveSpeed - colpoint.normal*normalSpeedB; + + fOtherSpeedA = vOtherSpeedA.Magnitude(); + fOtherSpeedB = vOtherSpeedB.Magnitude(); + +#ifdef FIX_BUGS // division by 0 + frictionDir = vOtherSpeedA; + frictionDir.Normalise(); +#else + frictionDir = vOtherSpeedA * (1.0f/fOtherSpeedA); +#endif + + speedSum = (B->m_fMass*fOtherSpeedB + A->m_fMass*fOtherSpeedA)/(B->m_fMass + A->m_fMass); + if(fOtherSpeedA > speedSum){ + impulseA = (speedSum - fOtherSpeedA) * A->m_fMass; + impulseB = (speedSum - fOtherSpeedB) * B->m_fMass; + impulseLimit = adhesiveLimit*CTimer::GetTimeStep(); + if(impulseA < -impulseLimit) impulseA = -impulseLimit; +#ifdef FIX_BUGS + if(impulseB > impulseLimit) impulseB = impulseLimit; +#else + if(impulseA < -impulseLimit) impulseA = -impulseLimit; // duplicate +#endif + A->ApplyFrictionMoveForce(frictionDir*impulseA); + B->ApplyFrictionMoveForce(frictionDir*impulseB); + return true; + } + }else if(A->bPedPhysics){ + if(B->IsVehicle()) + return false; + CVector pointposB = colpoint.point - B->GetPosition(); + speedB = B->GetSpeed(pointposB); + + normalSpeedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal); + normalSpeedB = DotProduct(speedB, colpoint.normal); + vOtherSpeedA = A->m_vecMoveSpeed - colpoint.normal*normalSpeedA; + vOtherSpeedB = speedB - colpoint.normal*normalSpeedB; + + fOtherSpeedA = vOtherSpeedA.Magnitude(); + fOtherSpeedB = vOtherSpeedB.Magnitude(); + +#ifdef FIX_BUGS // division by 0 + frictionDir = vOtherSpeedA; + frictionDir.Normalise(); +#else + frictionDir = vOtherSpeedA * (1.0f/fOtherSpeedA); +#endif + float massB = B->GetMass(pointposB, frictionDir); + speedSum = (massB*fOtherSpeedB + A->m_fMass*fOtherSpeedA)/(massB + A->m_fMass); + if(fOtherSpeedA > speedSum){ + impulseA = (speedSum - fOtherSpeedA) * A->m_fMass; + impulseB = (speedSum - fOtherSpeedB) * massB; + impulseLimit = adhesiveLimit*CTimer::GetTimeStep(); + if(impulseA < -impulseLimit) impulseA = -impulseLimit; + if(impulseB > impulseLimit) impulseB = impulseLimit; + A->ApplyFrictionMoveForce(frictionDir*impulseA); + B->ApplyFrictionMoveForce(frictionDir*impulseB); + B->ApplyFrictionTurnForce(frictionDir*impulseB, pointposB); + return true; + } + }else if(B->bPedPhysics){ + if(A->IsVehicle()) + return false; + CVector pointposA = colpoint.point - A->GetPosition(); + speedA = A->GetSpeed(pointposA); + + normalSpeedA = DotProduct(speedA, colpoint.normal); + normalSpeedB = DotProduct(B->m_vecMoveSpeed, colpoint.normal); + vOtherSpeedA = speedA - colpoint.normal*normalSpeedA; + vOtherSpeedB = B->m_vecMoveSpeed - colpoint.normal*normalSpeedB; + + fOtherSpeedA = vOtherSpeedA.Magnitude(); + fOtherSpeedB = vOtherSpeedB.Magnitude(); + +#ifdef FIX_BUGS // division by 0 + frictionDir = vOtherSpeedA; + frictionDir.Normalise(); +#else + frictionDir = vOtherSpeedA * (1.0f/fOtherSpeedA); +#endif + float massA = A->GetMass(pointposA, frictionDir); + speedSum = (B->m_fMass*fOtherSpeedB + massA*fOtherSpeedA)/(B->m_fMass + massA); + if(fOtherSpeedA > speedSum){ + impulseA = (speedSum - fOtherSpeedA) * massA; + impulseB = (speedSum - fOtherSpeedB) * B->m_fMass; + impulseLimit = adhesiveLimit*CTimer::GetTimeStep(); + if(impulseA < -impulseLimit) impulseA = -impulseLimit; + if(impulseB > impulseLimit) impulseB = impulseLimit; + A->ApplyFrictionMoveForce(frictionDir*impulseA); + A->ApplyFrictionTurnForce(frictionDir*impulseA, pointposA); + B->ApplyFrictionMoveForce(frictionDir*impulseB); + return true; + } + }else{ + CVector pointposA = colpoint.point - A->GetPosition(); + CVector pointposB = colpoint.point - B->GetPosition(); + speedA = A->GetSpeed(pointposA); + speedB = B->GetSpeed(pointposB); + + normalSpeedA = DotProduct(speedA, colpoint.normal); + normalSpeedB = DotProduct(speedB, colpoint.normal); + vOtherSpeedA = speedA - colpoint.normal*normalSpeedA; + vOtherSpeedB = speedB - colpoint.normal*normalSpeedB; + + fOtherSpeedA = vOtherSpeedA.Magnitude(); + fOtherSpeedB = vOtherSpeedB.Magnitude(); + +#ifdef FIX_BUGS // division by 0 + frictionDir = vOtherSpeedA; + frictionDir.Normalise(); +#else + frictionDir = vOtherSpeedA * (1.0f/fOtherSpeedA); +#endif + float massA = A->GetMass(pointposA, frictionDir); + float massB = B->GetMass(pointposB, frictionDir); + speedSum = (massB*fOtherSpeedB + massA*fOtherSpeedA)/(massB + massA); + if(fOtherSpeedA > speedSum){ + impulseA = (speedSum - fOtherSpeedA) * massA; + impulseB = (speedSum - fOtherSpeedB) * massB; + impulseLimit = adhesiveLimit*CTimer::GetTimeStep(); + if(impulseA < -impulseLimit) impulseA = -impulseLimit; + if(impulseB > impulseLimit) impulseB = impulseLimit; + A->ApplyFrictionMoveForce(frictionDir*impulseA); + A->ApplyFrictionTurnForce(frictionDir*impulseA, pointposA); + B->ApplyFrictionMoveForce(frictionDir*impulseB); + B->ApplyFrictionTurnForce(frictionDir*impulseB, pointposB); + return true; + } + } + return false; +} + +bool +CPhysical::ApplyFriction(float adhesiveLimit, CColPoint &colpoint) +{ + CVector speed; + float normalSpeed; + CVector vOtherSpeed; + float fOtherSpeed; + CVector frictionDir; + float fImpulse; + float impulseLimit; + + if(bPedPhysics){ + normalSpeed = DotProduct(m_vecMoveSpeed, colpoint.normal); + vOtherSpeed = m_vecMoveSpeed - colpoint.normal*normalSpeed; + + fOtherSpeed = vOtherSpeed.Magnitude(); + if(fOtherSpeed > 0.0f){ +#ifdef FIX_BUGS // division by 0 + frictionDir = vOtherSpeed; + frictionDir.Normalise(); +#else + frictionDir = vOtherSpeed * (1.0f/fOtherSpeed); +#endif + // not really impulse but speed + // maybe use ApplyFrictionMoveForce instead? + fImpulse = -fOtherSpeed; + impulseLimit = adhesiveLimit*CTimer::GetTimeStep() / m_fMass; + if(fImpulse < -impulseLimit) fImpulse = -impulseLimit; + CVector vImpulse = frictionDir*fImpulse; + m_vecMoveFriction += CVector(vImpulse.x, vImpulse.y, 0.0f); + return true; + } + }else{ + CVector pointpos = colpoint.point - GetPosition(); + speed = GetSpeed(pointpos); + normalSpeed = DotProduct(speed, colpoint.normal); + vOtherSpeed = speed - colpoint.normal*normalSpeed; + + fOtherSpeed = vOtherSpeed.Magnitude(); + if(fOtherSpeed > 0.0f){ +#ifdef FIX_BUGS // division by 0 + frictionDir = vOtherSpeed; + frictionDir.Normalise(); +#else + frictionDir = vOtherSpeed * (1.0f/fOtherSpeed); +#endif + fImpulse = -fOtherSpeed * m_fMass; + impulseLimit = adhesiveLimit*CTimer::GetTimeStep() * 1.5; + if(fImpulse < -impulseLimit) fImpulse = -impulseLimit; + ApplyFrictionMoveForce(frictionDir*fImpulse); + ApplyFrictionTurnForce(frictionDir*fImpulse, pointpos); + + if(fOtherSpeed > 0.1f && + colpoint.surfaceB != SURFACE_GRASS && colpoint.surfaceB != SURFACE_MUD_DRY && + CSurfaceTable::GetAdhesionGroup(colpoint.surfaceA) == ADHESIVE_HARD){ + CVector v = frictionDir * fOtherSpeed * 0.25f; + for(int i = 0; i < 4; i++) + CParticle::AddParticle(PARTICLE_SPARK_SMALL, colpoint.point, v); + } + return true; + } + } + return false; +} + +bool +CPhysical::ProcessShiftSectorList(CPtrList *lists) +{ + int i, j; + CPtrList *list; + CPtrNode *node; + CPhysical *A, *B; + CObject *Bobj; + bool canshift; + CVUVECTOR center; + float radius; + + int numCollisions; + int mostColliding; + CColPoint colpoints[MAX_COLLISION_POINTS]; + CVector shift = CVector(0.0f, 0.0f, 0.0f); + bool doShift = false; + CEntity *boat = nil; + + bool skipShift; + + A = this; + + A->GetBoundCentre(center); + radius = A->GetBoundRadius(); + for(i = 0; i <= ENTITYLIST_PEDS_OVERLAP; i++){ + list = &lists[i]; + for(node = list->first; node; node = node->next){ + B = (CPhysical*)node->item; + Bobj = (CObject*)B; + skipShift = false; + + if(B->IsBuilding() || + B->IsObject() && B->bInfiniteMass || + A->IsPed() && B->IsObject() && B->GetIsStatic() && !Bobj->bHasBeenDamaged) + canshift = true; + else + canshift = false; + + if(B == A || + B->m_scanCode == CWorld::GetCurrentScanCode() || + !B->bUsesCollision || + (A->bHasHitWall && !canshift) || + !B->GetIsTouching(center, radius)) + continue; + + // This could perhaps be done a bit nicer + + if(B->IsBuilding()) + skipShift = false; + else if(IsLightWithoutShift(A->GetModelIndex()) && + (B->IsVehicle() || B->IsPed()) && + A->GetUp().z < 0.66f) + skipShift = true; + else if((A->IsVehicle() || A->IsPed()) && + B->GetUp().z < 0.66f && + IsLightWithoutShift(B->GetModelIndex())) + skipShift = true; + else if(A->IsObject() && B->IsVehicle()){ + CObject *Aobj = (CObject*)A; + if(Aobj->ObjectCreatedBy != TEMP_OBJECT && + !Aobj->bHasBeenDamaged && + Aobj->GetIsStatic()){ + if(Aobj->m_pCollidingEntity == B) + Aobj->m_pCollidingEntity = nil; + }else if(Aobj->m_pCollidingEntity != B){ + CMatrix inv; + CVector size = CModelInfo::GetColModel(A->GetModelIndex())->boundingBox.GetSize(); + size = A->GetMatrix() * size; + if(size.z < B->GetPosition().z || + (Invert(B->GetMatrix(), inv) * size).z < 0.0f){ + skipShift = true; + Aobj->m_pCollidingEntity = B; + } + } else + skipShift = true; + }else if(B->IsObject() && A->IsVehicle()){ + CObject *Bobj = (CObject*)B; + if(Bobj->ObjectCreatedBy != TEMP_OBJECT && + !Bobj->bHasBeenDamaged && + Bobj->GetIsStatic()){ + if(Bobj->m_pCollidingEntity == A) + Bobj->m_pCollidingEntity = nil; + }else if(Bobj->m_pCollidingEntity != A){ + CMatrix inv; + CVector size = CModelInfo::GetColModel(B->GetModelIndex())->boundingBox.GetSize(); + size = B->GetMatrix() * size; + if(size.z < A->GetPosition().z || + (Invert(A->GetMatrix(), inv) * size).z < 0.0f) + skipShift = true; + } else + skipShift = true; + }else if(IsBodyPart(A->GetModelIndex()) && B->IsPed()) + skipShift = true; + else if(A->IsPed() && IsBodyPart(B->GetModelIndex())) + skipShift = true; + else if(A->IsPed() && ((CPed*)A)->m_pCollidingEntity == B || + B->IsPed() && ((CPed*)B)->m_pCollidingEntity == A) + skipShift = true; + else if(A->GetModelIndex() == MI_RCBANDIT && B->IsVehicle() || + B->GetModelIndex() == MI_RCBANDIT && (A->IsPed() || A->IsVehicle())) + skipShift = true; + + if(skipShift) + continue; + + B->m_scanCode = CWorld::GetCurrentScanCode(); + numCollisions = A->ProcessEntityCollision(B, colpoints); + if(numCollisions <= 0) + continue; + + mostColliding = 0; + for(j = 1; j < numCollisions; j++) + if (colpoints[j].GetDepth() > colpoints[mostColliding].GetDepth()) + mostColliding = j; + + if(CWorld::bSecondShift) + for(j = 0; j < numCollisions; j++) + shift += colpoints[j].GetNormal() * colpoints[j].GetDepth() * 1.5f / numCollisions; + else + for(j = 0; j < numCollisions; j++) + shift += colpoints[j].GetNormal() * colpoints[j].GetDepth() * 1.2f / numCollisions; + + if(A->IsVehicle() && B->IsVehicle()){ + CVector dir = A->GetPosition() - B->GetPosition(); + dir.Normalise(); + if(dir.z < 0.0f && dir.z < A->GetForward().z && dir.z < A->GetRight().z) + dir.z = Min(0.0f, Min(A->GetForward().z, A->GetRight().z)); + shift += dir * colpoints[mostColliding].GetDepth() * 0.5f; + }else if(A->IsPed() && B->IsVehicle() && ((CVehicle*)B)->IsBoat()){ + CVector dir = colpoints[mostColliding].GetNormal(); + float f = Min(Abs(dir.z), 0.9f); + dir.z = 0.0f; + dir.Normalise(); + shift += dir * colpoints[mostColliding].GetDepth() / (1.0f - f); + boat = B; + }else if(B->IsPed() && A->IsVehicle() && ((CVehicle*)A)->IsBoat()){ + CVector dir = colpoints[mostColliding].GetNormal() * -1.0f; + float f = Min(Abs(dir.z), 0.9f); + dir.z = 0.0f; + dir.Normalise(); + B->GetMatrix().Translate(dir * colpoints[mostColliding].GetDepth() / (1.0f - f)); + // BUG? how can that ever happen? A is a Ped + if(B->IsVehicle()) + B->ProcessEntityCollision(A, colpoints); + }else{ + if(CWorld::bSecondShift) + shift += colpoints[mostColliding].GetNormal() * colpoints[mostColliding].GetDepth() * 0.4f; + else + shift += colpoints[mostColliding].GetNormal() * colpoints[mostColliding].GetDepth() * 0.2f; + } + + doShift = true; + } + } + + if(!doShift) + return false; + GetMatrix().Translate(shift); + if(boat) + ProcessEntityCollision(boat, colpoints); + return true; +} + +bool +CPhysical::ProcessCollisionSectorList_SimpleCar(CPtrList *lists) +{ + static CColPoint aColPoints[MAX_COLLISION_POINTS]; + float radius; + CVUVECTOR center; + int listtype; + CPhysical *A, *B; + int numCollisions; + int i; + float impulseA = -1.0f; + float impulseB = -1.0f; + + A = (CPhysical*)this; + + if(!A->bUsesCollision) + return false; + + radius = A->GetBoundRadius(); + A->GetBoundCentre(center); + + for(listtype = 3; listtype >= 0; listtype--){ + // Go through vehicles and objects + CPtrList *list; + switch(listtype){ + case 0: list = &lists[ENTITYLIST_VEHICLES]; break; + case 1: list = &lists[ENTITYLIST_VEHICLES_OVERLAP]; break; + case 2: list = &lists[ENTITYLIST_OBJECTS]; break; + case 3: list = &lists[ENTITYLIST_OBJECTS_OVERLAP]; break; + } + + // Find first collision in list + CPtrNode *listnode; + for(listnode = list->first; listnode; listnode = listnode->next){ + B = (CPhysical*)listnode->item; + if(B != A && + !(B->IsObject() && ((CObject*)B)->bIsStreetLight && B->GetUp().z < 0.66f) && + B->m_scanCode != CWorld::GetCurrentScanCode() && + B->bUsesCollision && + B->GetIsTouching(center, radius)){ + B->m_scanCode = CWorld::GetCurrentScanCode(); + numCollisions = A->ProcessEntityCollision(B, aColPoints); + if(numCollisions > 0) + goto collision; + } + } + } + // no collision + return false; + +collision: + + if(A->bHasContacted && B->bHasContacted){ + for(i = 0; i < numCollisions; i++){ + if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB)) + continue; + + if(impulseA > A->m_fDamageImpulse) + A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); + + if(impulseB > B->m_fDamageImpulse) + B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal); + + float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr(); + float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr(); + + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); + } + }else if(A->bHasContacted){ + CVector savedMoveFriction = A->m_vecMoveFriction; + CVector savedTurnFriction = A->m_vecTurnFriction; + A->m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); + A->m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); + A->bHasContacted = false; + + for(i = 0; i < numCollisions; i++){ + if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB)) + continue; + + if(impulseA > A->m_fDamageImpulse) + A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); + + if(impulseB > B->m_fDamageImpulse) + B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal); + + float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr(); + float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr(); + + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); + + if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){ + A->bHasContacted = true; + B->bHasContacted = true; + } + } + + if(!A->bHasContacted){ + A->bHasContacted = true; + A->m_vecMoveFriction = savedMoveFriction; + A->m_vecTurnFriction = savedTurnFriction; + } + }else if(B->bHasContacted){ + CVector savedMoveFriction = B->m_vecMoveFriction; + CVector savedTurnFriction = B->m_vecTurnFriction; + B->m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); + B->m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); + B->bHasContacted = false; + + for(i = 0; i < numCollisions; i++){ + if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB)) + continue; + + if(impulseA > A->m_fDamageImpulse) + A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); + + if(impulseB > B->m_fDamageImpulse) + B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal); + + float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr(); + float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr(); + + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); + + if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){ + A->bHasContacted = true; + B->bHasContacted = true; + } + } + + if(!B->bHasContacted){ + B->bHasContacted = true; + B->m_vecMoveFriction = savedMoveFriction; + B->m_vecTurnFriction = savedTurnFriction; + } + }else{ + for(i = 0; i < numCollisions; i++){ + if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB)) + continue; + + if(impulseA > A->m_fDamageImpulse) + A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); + + if(impulseB > B->m_fDamageImpulse) + B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal); + + float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr(); + float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr(); + + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); + + if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){ + A->bHasContacted = true; + B->bHasContacted = true; + } + } + } + + if(B->GetStatus() == STATUS_SIMPLE){ + B->SetStatus(STATUS_PHYSICS); + if(B->IsVehicle()) + CCarCtrl::SwitchVehicleToRealPhysics((CVehicle*)B); + } + + return true; +} + +bool +CPhysical::ProcessCollisionSectorList(CPtrList *lists) +{ + static CColPoint aColPoints[MAX_COLLISION_POINTS]; + float radius; + CVUVECTOR center; + CPtrList *list; + CPhysical *A, *B; + CObject *Aobj, *Bobj; + CPed *Aped, *Bped; + int numCollisions; + int numResponses; + int i, j; + bool skipCollision, altcollision; + bool ret = false; + float impulseA = -1.0f; + float impulseB = -1.0f; + + A = (CPhysical*)this; + Aobj = (CObject*)A; + Aped = (CPed*)A; + + radius = A->GetBoundRadius(); + A->GetBoundCentre(center); + + for(j = 0; j <= ENTITYLIST_PEDS_OVERLAP; j++){ + list = &lists[j]; + + CPtrNode *listnode; + for(listnode = list->first; listnode; listnode = listnode->next){ + B = (CPhysical*)listnode->item; + Bobj = (CObject*)B; + Bped = (CPed*)B; + + bool isTouching = true; + if(!B->bUsesCollision || + B->m_scanCode == CWorld::GetCurrentScanCode() || + B == A) + continue; + if(!B->GetIsTouching(center, radius)){ + if(A->IsObject() && Aobj->m_pCollidingEntity == B) + Aobj->m_pCollidingEntity = nil; + else if(B->IsObject() && Bobj->m_pCollidingEntity == A) + Bobj->m_pCollidingEntity = nil; + else if(A->IsPed() && Aped->m_pCollidingEntity == B) + Aped->m_pCollidingEntity = nil; + else if(B->IsPed() && Bped->m_pCollidingEntity == A) + Bped->m_pCollidingEntity = nil; + continue; + } + + A->bSkipLineCol = false; + skipCollision = false; + altcollision = false; + + if(B->IsBuilding()) + skipCollision = false; + else if(A->IsObject() && Aobj->bIsStreetLight && + (B->IsVehicle() || B->IsPed()) && + A->GetUp().z < 0.66f){ + skipCollision = true; + A->bSkipLineCol = true; + Aobj->m_pCollidingEntity = B; + }else if(B->IsObject() && Bobj->bIsStreetLight && + (A->IsVehicle() || A->IsPed()) && + B->GetUp().z < 0.66f){ + skipCollision = true; + A->bSkipLineCol = true; + Bobj->m_pCollidingEntity = A; + }else if(A->IsObject() && B->IsVehicle()){ + if(A->GetModelIndex() == MI_CAR_BUMPER) + skipCollision = true; + else if(Aobj->ObjectCreatedBy == TEMP_OBJECT || + Aobj->bHasBeenDamaged || + !Aobj->GetIsStatic()){ + if(Aobj->m_pCollidingEntity == B) + skipCollision = true; + else if(Aobj->m_nCollisionDamageEffect < DAMAGE_EFFECT_SMASH_COMPLETELY){ + CMatrix inv; + CVector size = CModelInfo::GetColModel(A->GetModelIndex())->boundingBox.GetSize(); + size = A->GetMatrix() * size; + if(size.z < B->GetPosition().z || + (Invert(B->GetMatrix(), inv) * size).z < 0.0f){ + skipCollision = true; + Aobj->m_pCollidingEntity = B; + } + } + } + }else if(B->IsObject() && A->IsVehicle()){ + if(B->GetModelIndex() == MI_CAR_BUMPER) + skipCollision = true; + else if(Bobj->ObjectCreatedBy == TEMP_OBJECT || + Bobj->bHasBeenDamaged || + !Bobj->GetIsStatic()){ + if(Bobj->m_pCollidingEntity == A) + skipCollision = true; + else if(Bobj->m_nCollisionDamageEffect < DAMAGE_EFFECT_SMASH_COMPLETELY){ + CMatrix inv; + CVector size = CModelInfo::GetColModel(B->GetModelIndex())->boundingBox.GetSize(); + size = B->GetMatrix() * size; + if(size.z < A->GetPosition().z || + (Invert(A->GetMatrix(), inv) * size).z < 0.0f){ + skipCollision = true; + } + } + } + }else if(A->GetModelIndex() == MI_GRENADE && B->IsPed() && + A->GetPosition().z < B->GetPosition().z){ + skipCollision = true; + }else if(B->GetModelIndex() == MI_GRENADE && A->IsPed() && + B->GetPosition().z < A->GetPosition().z){ + skipCollision = true; + A->bSkipLineCol = true; + }else if(A->IsPed() && Aped->m_pCollidingEntity == B){ + skipCollision = true; + if(!Aped->bKnockedUpIntoAir || Aped->bKnockedOffBike) + A->bSkipLineCol = true; + }else if(B->IsPed() && Bped->m_pCollidingEntity == A){ + skipCollision = true; + A->bSkipLineCol = true; + }else if(A->GetModelIndex() == MI_RCBANDIT && (B->IsPed() || B->IsVehicle()) || + B->GetModelIndex() == MI_RCBANDIT && (A->IsPed() || A->IsVehicle())){ + skipCollision = true; + A->bSkipLineCol = true; + }else if(A->IsPed() && B->IsObject() && Bobj->m_fUprootLimit > 0.0f) + altcollision = true; + + + if(!A->bUsesCollision || skipCollision){ + B->m_scanCode = CWorld::GetCurrentScanCode(); + numCollisions = A->ProcessEntityCollision(B, aColPoints); + if(A->bJustCheckCollision && numCollisions > 0) + return true; + if(numCollisions == 0 && A == (CEntity*)FindPlayerPed() && Aped->m_pCollidingEntity == B) + Aped->m_pCollidingEntity = nil; + }else if(B->IsBuilding() || B->bIsStuck || B->m_phy_flagA08 || altcollision){ + // This is the case where B doesn't move + + B->m_scanCode = CWorld::GetCurrentScanCode(); + numCollisions = A->ProcessEntityCollision(B, aColPoints); + if(numCollisions <= 0) + continue; + + CVector moveSpeed = CVector(0.0f, 0.0f, 0.0f); + CVector turnSpeed = CVector(0.0f, 0.0f, 0.0f); + float maxImpulseA = 0.0f; + numResponses = 0; + if(A->bHasContacted){ + for(i = 0; i < numCollisions; i++){ + if(!A->ApplyCollisionAlt(B, aColPoints[i], impulseA, moveSpeed, turnSpeed)) + continue; + + numResponses++; + if(impulseA > maxImpulseA) maxImpulseA = impulseA; + + if(A->IsVehicle()){ + if(!(((CVehicle*)A)->IsBoat() && aColPoints[i].surfaceB == SURFACE_WOOD_SOLID) && + impulseA > A->m_fDamageImpulse) + A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); + + if(CSurfaceTable::GetAdhesionGroup(aColPoints[i].surfaceB) == ADHESIVE_SAND) + aColPoints[i].surfaceB = SURFACE_SAND; + + float turnSpeedDiff = A->m_vecTurnSpeed.MagnitudeSqr(); + float moveSpeedDiff = A->m_vecMoveSpeed.MagnitudeSqr(); + + if(A->GetUp().z < -0.6f && + Abs(A->m_vecMoveSpeed.x) < 0.05f && + Abs(A->m_vecMoveSpeed.y) < 0.05f) + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, 0.1f*impulseA, Max(turnSpeedDiff, moveSpeedDiff)); + else + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); + + }else{ + if(impulseA > A->m_fDamageImpulse) + A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); + + float turnSpeedDiff = A->m_vecTurnSpeed.MagnitudeSqr(); + float moveSpeedDiff = A->m_vecMoveSpeed.MagnitudeSqr(); + + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); + } + } + }else{ + for(i = 0; i < numCollisions; i++){ + if(!A->ApplyCollisionAlt(B, aColPoints[i], impulseA, moveSpeed, turnSpeed)) + continue; + + numResponses++; + if(impulseA > maxImpulseA) maxImpulseA = impulseA; + float adhesion = CSurfaceTable::GetAdhesiveLimit(aColPoints[i]) / numCollisions; + + if(A->IsVehicle()){ + if(((CVehicle*)A)->IsBoat() && aColPoints[i].surfaceB == SURFACE_WOOD_SOLID) + adhesion = 0.0f; + else if(impulseA > A->m_fDamageImpulse) + A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); + + if(CSurfaceTable::GetAdhesionGroup(aColPoints[i].surfaceB) == ADHESIVE_SAND) + aColPoints[i].surfaceB = SURFACE_SAND; + + float turnSpeedDiff = A->m_vecTurnSpeed.MagnitudeSqr(); + float moveSpeedDiff = A->m_vecMoveSpeed.MagnitudeSqr(); + + if(A->GetUp().z < -0.6f && + Abs(A->m_vecMoveSpeed.x) < 0.05f && + Abs(A->m_vecMoveSpeed.y) < 0.05f) + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, 0.1f*impulseA, Max(turnSpeedDiff, moveSpeedDiff)); + else + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); + + + if(A->GetModelIndex() == MI_RCBANDIT) + adhesion *= 0.2f; + else if(((CVehicle*)A)->IsBoat()){ + if(aColPoints[i].normal.z > 0.6f){ + if(CSurfaceTable::GetAdhesionGroup(aColPoints[i].surfaceB) == ADHESIVE_LOOSE || + CSurfaceTable::GetAdhesionGroup(aColPoints[i].surfaceB) == ADHESIVE_SAND) + adhesion *= 3.0f; + }else + adhesion = 0.0f; + }else if(A->GetStatus() == STATUS_WRECKED) + adhesion *= 3.0f; + else if(A->GetUp().z > 0.3f) + adhesion = 0.0f; + else + adhesion *= Min(5.0f, 0.03f*impulseA + 1.0f); + }else{ + if(impulseA > A->m_fDamageImpulse) + A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); + + float turnSpeedDiff = A->m_vecTurnSpeed.MagnitudeSqr(); + float moveSpeedDiff = A->m_vecMoveSpeed.MagnitudeSqr(); + + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); + } + + if(A->ApplyFriction(adhesion, aColPoints[i])) + A->bHasContacted = true; + } + } + + if(numResponses){ + m_vecMoveSpeed += moveSpeed / numResponses; + m_vecTurnSpeed += turnSpeed / numResponses; + if(!CWorld::bNoMoreCollisionTorque && + A->GetStatus() == STATUS_PLAYER && A->IsVehicle() && + Abs(A->m_vecMoveSpeed.x) > 0.2f && + Abs(A->m_vecMoveSpeed.y) > 0.2f && !A->bIsInWater){ + A->m_vecMoveFriction.x += moveSpeed.x * -0.3f / numCollisions; + A->m_vecMoveFriction.y += moveSpeed.y * -0.3f / numCollisions; + A->m_vecTurnFriction += turnSpeed * -0.3f / numCollisions; + } + + if(B->IsObject() && Bobj->m_nCollisionDamageEffect && maxImpulseA > 20.0f) + Bobj->ObjectDamage(maxImpulseA); + + if(!CWorld::bSecondShift) + return true; + ret = true; + } + }else{ + + // B can move + + B->m_scanCode = CWorld::GetCurrentScanCode(); + numCollisions = A->ProcessEntityCollision(B, aColPoints); + if(numCollisions <= 0) + continue; + + float maxImpulseA = 0.0f; + float maxImpulseB = 0.0f; + if(A->bHasContacted && B->bHasContacted){ + for(i = 0; i < numCollisions; i++){ + if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB)) + continue; + + if(impulseA > maxImpulseA) maxImpulseA = impulseA; + if(impulseB > maxImpulseB) maxImpulseB = impulseB; + + if(impulseA > A->m_fDamageImpulse) + A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); + + if(impulseB > B->m_fDamageImpulse) + B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal); + + float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr(); + float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr(); + + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); + } + }else if(A->bHasContacted){ + CVector savedMoveFriction = A->m_vecMoveFriction; + CVector savedTurnFriction = A->m_vecTurnFriction; + A->m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); + A->m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); + A->bHasContacted = false; + + for(i = 0; i < numCollisions; i++){ + if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB)) + continue; + + if(impulseA > maxImpulseA) maxImpulseA = impulseA; + if(impulseB > maxImpulseB) maxImpulseB = impulseB; + + if(impulseA > A->m_fDamageImpulse) + A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); + + if(impulseB > B->m_fDamageImpulse) + B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal); + + float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr(); + float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr(); + + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); + + if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){ + A->bHasContacted = true; + B->bHasContacted = true; + } + } + + if(!A->bHasContacted){ + A->bHasContacted = true; + A->m_vecMoveFriction = savedMoveFriction; + A->m_vecTurnFriction = savedTurnFriction; + } + }else if(B->bHasContacted){ + CVector savedMoveFriction = B->m_vecMoveFriction; + CVector savedTurnFriction = B->m_vecTurnFriction; + B->m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); + B->m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); + B->bHasContacted = false; + + for(i = 0; i < numCollisions; i++){ + if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB)) + continue; + + if(impulseA > maxImpulseA) maxImpulseA = impulseA; + if(impulseB > maxImpulseB) maxImpulseB = impulseB; + + if(impulseA > A->m_fDamageImpulse) + A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); + + if(impulseB > B->m_fDamageImpulse) + B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal); + + float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr(); + float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr(); + + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); + + if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){ + A->bHasContacted = true; + B->bHasContacted = true; + } + } + + if(!B->bHasContacted){ + B->bHasContacted = true; + B->m_vecMoveFriction = savedMoveFriction; + B->m_vecTurnFriction = savedTurnFriction; + } + }else{ + for(i = 0; i < numCollisions; i++){ + if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB)) + continue; + + if(impulseA > maxImpulseA) maxImpulseA = impulseA; + if(impulseB > maxImpulseB) maxImpulseB = impulseB; + + if(impulseA > A->m_fDamageImpulse) + A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); + + if(impulseB > B->m_fDamageImpulse) + B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal); + + float turnSpeedDiff = (B->m_vecTurnSpeed - A->m_vecTurnSpeed).MagnitudeSqr(); + float moveSpeedDiff = (B->m_vecMoveSpeed - A->m_vecMoveSpeed).MagnitudeSqr(); + + DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); + + if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){ + A->bHasContacted = true; + B->bHasContacted = true; + } + } + } + + if(B->IsPed() && A->IsVehicle() && + (!Bped->IsPlayer() || B->bHasHitWall && A->m_vecMoveSpeed.MagnitudeSqr() > SQR(0.05f))) + Bped->KillPedWithCar((CVehicle*)A, maxImpulseB); + else if(B->GetModelIndex() == MI_TRAIN && A->IsPed() && + (!Aped->IsPlayer() || A->bHasHitWall)) + Aped->KillPedWithCar((CVehicle*)B, maxImpulseA*2.0f); + else if(B->IsObject() && B->bUsesCollision && A->IsVehicle()){ + // BUG? not impulseA? + if(Bobj->m_nCollisionDamageEffect && maxImpulseB > 20.0f) + Bobj->ObjectDamage(maxImpulseB); + else if(Bobj->m_nCollisionDamageEffect >= DAMAGE_EFFECT_SMASH_COMPLETELY){ + CMatrix inv; + CVector size = CModelInfo::GetColModel(B->GetModelIndex())->boundingBox.GetSize(); + size = B->GetMatrix() * size; + if(size.z < A->GetPosition().z || + (Invert(A->GetMatrix(), inv) * size).z < 0.0f) + Bobj->ObjectDamage(50.0f); + } + }else if(A->IsObject() && A->bUsesCollision && B->IsVehicle()){ + if(Aobj->m_nCollisionDamageEffect && maxImpulseB > 20.0f) + Aobj->ObjectDamage(maxImpulseB); +#ifdef FIX_BUGS + else if(Aobj->m_nCollisionDamageEffect >= DAMAGE_EFFECT_SMASH_COMPLETELY){ +#else + else if(Bobj->m_nCollisionDamageEffect >= DAMAGE_EFFECT_SMASH_COMPLETELY){ +#endif + CMatrix inv; + CVector size = CModelInfo::GetColModel(A->GetModelIndex())->boundingBox.GetSize(); + size = A->GetMatrix() * size; + if(size.z < B->GetPosition().z || + (Invert(B->GetMatrix(), inv) * size).z < 0.0f) + Aobj->ObjectDamage(50.0f); + } + } + + if(B->GetStatus() == STATUS_SIMPLE){ + B->SetStatus(STATUS_PHYSICS); + if(B->IsVehicle()) + CCarCtrl::SwitchVehicleToRealPhysics((CVehicle*)B); + } + + if(!CWorld::bSecondShift) + return true; + ret = true; + } + + } + } + + return ret; +} + +bool +CPhysical::CheckCollision(void) +{ + CEntryInfoNode *node; + + bCollisionProcessed = false; + CWorld::AdvanceCurrentScanCode(); + for(node = m_entryInfoList.first; node; node = node->next) + if(ProcessCollisionSectorList(node->sector->m_lists)) + return true; + return false; +} + +bool +CPhysical::CheckCollision_SimpleCar(void) +{ + CEntryInfoNode *node; + + bCollisionProcessed = false; + CWorld::AdvanceCurrentScanCode(); + for(node = m_entryInfoList.first; node; node = node->next) + if(ProcessCollisionSectorList_SimpleCar(node->sector->m_lists)) + return true; + return false; +} + +float PHYSICAL_SHIFT_SPEED_DAMP = 0.707f; + +void +CPhysical::ProcessShift(void) +{ + m_fDistanceTravelled = 0.0f; + if(GetStatus() == STATUS_SIMPLE){ + bIsStuck = false; + bIsInSafePosition = true; + RemoveAndAdd(); + }else{ + CPhysical *surf; + if(bHasHitWall && (IsPed() && (surf = ((CPed*)this)->m_pCurrentPhysSurface, surf == nil || !surf->bInfiniteMass || surf->m_phy_flagA08) || + CWorld::bSecondShift)){ + m_vecMoveSpeed *= Pow(PHYSICAL_SHIFT_SPEED_DAMP, CTimer::GetTimeStep()); + m_vecTurnSpeed *= Pow(PHYSICAL_SHIFT_SPEED_DAMP, CTimer::GetTimeStep()); + } + + CMatrix matrix(GetMatrix()); + ApplyMoveSpeed(); + ApplyTurnSpeed(); + GetMatrix().Reorthogonalise(); + + CWorld::AdvanceCurrentScanCode(); + + if(IsVehicle()) + m_bIsVehicleBeingShifted = true; + + CEntryInfoNode *node; + bool hasshifted = false; + for(node = m_entryInfoList.first; node; node = node->next) + hasshifted |= ProcessShiftSectorList(node->sector->m_lists); + m_bIsVehicleBeingShifted = false; + if(hasshifted){ + CWorld::AdvanceCurrentScanCode(); + bool hadCollision = false; + for(node = m_entryInfoList.first; node; node = node->next) + if(ProcessCollisionSectorList(node->sector->m_lists)){ + if(!CWorld::bSecondShift){ + GetMatrix() = matrix; + return; + } + hadCollision = true; + } + if(hadCollision){ + GetMatrix() = matrix; + return; + } + } + bIsStuck = false; + bIsInSafePosition = true; + m_fDistanceTravelled = (GetPosition() - matrix.GetPosition()).Magnitude(); + RemoveAndAdd(); + } +} + +// x is the number of units (m) we would like to step +#define NUMSTEPS(x) Ceil(Sqrt(distSq) * (1.0f/(x))) + +float HIGHSPEED_ELASTICITY_MULT_PED = 2.0f; +float HIGHSPEED_ELASTICITY_MULT_COPCAR = 2.0f; + +void +CPhysical::ProcessCollision(void) +{ + int i; + CPed *ped = (CPed*)this; + + m_fDistanceTravelled = 0.0f; + m_bIsVehicleBeingShifted = false; + bSkipLineCol = false; + + if(!bUsesCollision){ + bIsStuck = false; + bIsInSafePosition = true; + RemoveAndAdd(); + return; + } + + if(GetStatus() == STATUS_SIMPLE){ + if(CheckCollision_SimpleCar() && GetStatus() == STATUS_SIMPLE){ + SetStatus(STATUS_PHYSICS); + if(IsVehicle()) + CCarCtrl::SwitchVehicleToRealPhysics((CVehicle*)this); + } + bIsStuck = false; + bIsInSafePosition = true; + RemoveAndAdd(); + return; + } + + // Save current state + CMatrix savedMatrix(GetMatrix()); + float savedElasticity = m_fElasticity; + CVector savedMoveSpeed = m_vecMoveSpeed; + float savedTimeStep = CTimer::GetTimeStep(); + + int8 n = 1; // The number of steps we divide the time step into + float step = 0.0f; // divided time step + float distSq = m_vecMoveSpeed.MagnitudeSqr() * sq(CTimer::GetTimeStep()); + + if(IsPed() && (distSq >= sq(0.3f) || ped->IsPlayer())){ + if(ped->IsPlayer()){ + if(ped->m_pCurrentPhysSurface) + n = Max(NUMSTEPS(0.15f), 4.0f); + else + n = Max(NUMSTEPS(0.3f), 2.0f); + }else + n = NUMSTEPS(0.45f); + step = savedTimeStep / n; + if(!ped->IsPlayer()) + ped->m_fElasticity *= HIGHSPEED_ELASTICITY_MULT_PED; + }else if(IsVehicle() && distSq >= sq(0.4f)){ + if(GetStatus() == STATUS_PLAYER) + n = NUMSTEPS(0.2f); + else + n = distSq > 0.32f ? NUMSTEPS(0.3f) : NUMSTEPS(0.4f); + step = savedTimeStep / n; + + CVector bbox = GetColModel()->boundingBox.GetSize(); + float relDistX = Abs(DotProduct(m_vecMoveSpeed, GetRight())) * CTimer::GetTimeStep() / bbox.x; + float relDistY = Abs(DotProduct(m_vecMoveSpeed, GetForward())) * CTimer::GetTimeStep() / bbox.y; + float relDistZ = Abs(DotProduct(m_vecMoveSpeed, GetUp())) * CTimer::GetTimeStep() / bbox.z; + if(Max(relDistX, Max(relDistY, relDistZ)) < 1.0f){ + // check if we can get away with simplified processing + + ApplyMoveSpeed(); + ApplyTurnSpeed(); + GetMatrix().Reorthogonalise(); + bSkipLineCol = false; + m_bIsVehicleBeingShifted = false; + + bJustCheckCollision = true; + bool savedUsesCollision = bUsesCollision; + bUsesCollision = false; + if(!CheckCollision()){ + bJustCheckCollision = false; + bUsesCollision = savedUsesCollision; + if(IsVehicle()) + ((CVehicle*)this)->bVehicleColProcessed = true; + + bHitByTrain = false; + m_fDistanceTravelled = (GetPosition() - savedMatrix.GetPosition()).Magnitude(); + bSkipLineCol = false; + + bIsStuck = false; + bIsInSafePosition = true; + m_fElasticity = savedElasticity; + RemoveAndAdd(); + return; + } + bJustCheckCollision = false; + bUsesCollision = savedUsesCollision; + GetMatrix() = savedMatrix; + m_vecMoveSpeed = savedMoveSpeed; + if(IsVehicle() && ((CVehicle*)this)->bIsLawEnforcer) + m_fElasticity *= HIGHSPEED_ELASTICITY_MULT_COPCAR; + } + }else if(IsObject() && ((CObject*)this)->ObjectCreatedBy != TEMP_OBJECT){ + int responsecase = ((CObject*)this)->m_nSpecialCollisionResponseCases; + if(responsecase == COLLRESPONSE_LAMPOST){ + CVector speedUp = CVector(0.0f, 0.0f, 0.0f); + CVector speedDown = CVector(0.0f, 0.0f, 0.0f); + CColModel *colModel = GetColModel(); + speedUp.z = colModel->boundingBox.max.z; + speedDown.z = colModel->boundingBox.min.z; + speedUp = Multiply3x3(GetMatrix(), speedUp); + speedDown = Multiply3x3(GetMatrix(), speedDown); + speedUp = GetSpeed(speedUp); + speedDown = GetSpeed(speedDown); + distSq = Max(speedUp.MagnitudeSqr(), speedDown.MagnitudeSqr()) * sq(CTimer::GetTimeStep()); + if(distSq >= sq(0.3f)){ + n = NUMSTEPS(0.3f); + step = savedTimeStep / n; + } + }else if(responsecase == COLLRESPONSE_UNKNOWN5){ + if(distSq >= 0.009f){ + n = NUMSTEPS(0.09f); + step = savedTimeStep / n; + } + }else if(responsecase == COLLRESPONSE_SMALLBOX || responsecase == COLLRESPONSE_FENCEPART){ + if(distSq >= sq(0.15f)){ + n = NUMSTEPS(0.15f); + step = savedTimeStep / n; + } + }else{ + if(distSq >= sq(0.3f)){ + n = NUMSTEPS(0.3f); + step = savedTimeStep / n; + } + } + } + + for(i = 1; i < n; i++){ + CTimer::SetTimeStep(i * step); + ApplyMoveSpeed(); + ApplyTurnSpeed(); + // TODO: get rid of copy paste? + if(CheckCollision()){ + if(IsPed() && m_vecMoveSpeed.z == 0.0f && + !ped->bWasStanding && + ped->bIsStanding) + savedMatrix.GetPosition().z = GetPosition().z; + GetMatrix() = savedMatrix; + CTimer::SetTimeStep(savedTimeStep); + m_fElasticity = savedElasticity; + return; + } + if(IsPed() && m_vecMoveSpeed.z == 0.0f && + !ped->bWasStanding && + ped->bIsStanding) + savedMatrix.GetPosition().z = GetPosition().z; + GetMatrix() = savedMatrix; + CTimer::SetTimeStep(savedTimeStep); + if(IsVehicle()){ + CVehicle *veh = (CVehicle*)this; + if(veh->m_vehType == VEHICLE_TYPE_CAR){ + CAutomobile *car = (CAutomobile*)this; + car->m_aSuspensionSpringRatio[0] = 1.0f; + car->m_aSuspensionSpringRatio[1] = 1.0f; + car->m_aSuspensionSpringRatio[2] = 1.0f; + car->m_aSuspensionSpringRatio[3] = 1.0f; + }else if(veh->m_vehType == VEHICLE_TYPE_BIKE){ + CBike *bike = (CBike*)this; + bike->m_aSuspensionSpringRatio[0] = 1.0f; + bike->m_aSuspensionSpringRatio[1] = 1.0f; + bike->m_aSuspensionSpringRatio[2] = 1.0f; + bike->m_aSuspensionSpringRatio[3] = 1.0f; + } + } + } + + ApplyMoveSpeed(); + ApplyTurnSpeed(); + GetMatrix().Reorthogonalise(); + m_bIsVehicleBeingShifted = false; + bSkipLineCol = false; + if(!m_vecMoveSpeed.IsZero() || + !m_vecTurnSpeed.IsZero() || + bHitByTrain || + GetStatus() == STATUS_PLAYER || + IsVehicle() && ((CVehicle*)this)->bRestingOnPhysical || + IsPed() && ped->IsPlayer()){ + if(IsVehicle()) + ((CVehicle*)this)->bVehicleColProcessed = true; + if(CheckCollision()){ + GetMatrix() = savedMatrix; + m_fElasticity = savedElasticity; + return; + } + } + bHitByTrain = false; + m_fDistanceTravelled = (GetPosition() - savedMatrix.GetPosition()).Magnitude(); + bSkipLineCol = false; + + bIsStuck = false; + bIsInSafePosition = true; + m_fElasticity = savedElasticity; + RemoveAndAdd(); +} diff --git a/src/miami/entities/Physical.h b/src/miami/entities/Physical.h new file mode 100644 index 00000000..f552da6c --- /dev/null +++ b/src/miami/entities/Physical.h @@ -0,0 +1,175 @@ +#pragma once + +#include "Lists.h" +#include "Timer.h" +#include "Entity.h" + +enum { + PHYSICAL_MAX_COLLISIONRECORDS = 6 +}; + +#define GRAVITY (0.008f) + +class CTreadable; + +class CPhysical : public CEntity +{ +public: + int32 m_audioEntityId; + float m_phys_unused1; + uint32 m_nLastTimeCollided; + CVector m_vecMoveSpeed; // velocity + CVector m_vecTurnSpeed; // angular velocity + CVector m_vecMoveFriction; + CVector m_vecTurnFriction; + CVector m_vecMoveSpeedAvg; + CVector m_vecTurnSpeedAvg; + float m_fMass; + float m_fTurnMass; // moment of inertia + float m_fForceMultiplier; + float m_fAirResistance; + float m_fElasticity; + float m_fBuoyancy; + CVector m_vecCentreOfMass; + CEntryInfoList m_entryInfoList; + CPtrNode *m_movingListNode; + + int8 m_phys_unused2; + uint8 m_nStaticFrames; + uint8 m_nCollisionRecords; + CEntity *m_aCollisionRecords[PHYSICAL_MAX_COLLISIONRECORDS]; + + float m_fDistanceTravelled; + + // damaged piece + float m_fDamageImpulse; + CEntity *m_pDamageEntity; + CVector m_vecDamageNormal; + int16 m_nDamagePieceType; + + uint8 bIsHeavy : 1; + uint8 bAffectedByGravity : 1; + uint8 bInfiniteMass : 1; + uint8 m_phy_flagA08 : 1; + uint8 bIsInWater : 1; + uint8 m_phy_flagA20 : 1; // unused + uint8 bHitByTrain : 1; + uint8 bSkipLineCol : 1; + + uint8 bIsFrozen : 1; + uint8 bDontLoadCollision : 1; + uint8 m_bIsVehicleBeingShifted : 1; // wrong name - also used on but never set for peds + uint8 bJustCheckCollision : 1; // just see if there is a collision + + uint8 m_nSurfaceTouched; + int8 m_nZoneLevel; + + CPhysical(void); + ~CPhysical(void); + + // from CEntity + void Add(void); + void Remove(void); + CRect GetBoundRect(void); + void ProcessControl(void); + void ProcessShift(void); + void ProcessCollision(void); + + virtual int32 ProcessEntityCollision(CEntity *ent, CColPoint *colpoints); + + void RemoveAndAdd(void); + void AddToMovingList(void); + void RemoveFromMovingList(void); + void SetDamagedPieceRecord(uint16 piece, float impulse, CEntity *entity, CVector dir); + void AddCollisionRecord(CEntity *ent); + void AddCollisionRecord_Treadable(CEntity *ent); + bool GetHasCollidedWith(CEntity *ent); + void RemoveRefsToEntity(CEntity *ent); + static void PlacePhysicalRelativeToOtherPhysical(CPhysical *other, CPhysical *phys, CVector localPos); + + // get speed of point p relative to entity center + CVector GetSpeed(const CVector &r); + CVector GetSpeed(void) { return GetSpeed(CVector(0.0f, 0.0f, 0.0f)); } + float GetMass(const CVector &pos, const CVector &dir) { + return 1.0f / (CrossProduct(pos, dir).MagnitudeSqr()/m_fTurnMass + + 1.0f/m_fMass); + } + float GetMassTweak(const CVector &pos, const CVector &dir, float t) { + return 1.0f / (CrossProduct(pos, dir).MagnitudeSqr()/(m_fTurnMass*t) + + 1.0f/(m_fMass*t)); + } + void UnsetIsInSafePosition(void) { + m_vecMoveSpeed *= -1.0f; + m_vecTurnSpeed *= -1.0f; + ApplyTurnSpeed(); + ApplyMoveSpeed(); + m_vecMoveSpeed *= -1.0f; + m_vecTurnSpeed *= -1.0f; + bIsInSafePosition = false; + } + + const CVector &GetMoveSpeed() { return m_vecMoveSpeed; } + void SetMoveSpeed(float x, float y, float z) { + m_vecMoveSpeed.x = x; + m_vecMoveSpeed.y = y; + m_vecMoveSpeed.z = z; + } + void SetMoveSpeed(const CVector& speed) { + m_vecMoveSpeed = speed; + } + void AddToMoveSpeed(float x, float y, float z) { + m_vecMoveSpeed.x += x; + m_vecMoveSpeed.y += y; + m_vecMoveSpeed.z += z; + } + void AddToMoveSpeed(const CVector& addition) { + m_vecMoveSpeed += addition; + } + void AddToMoveSpeed(const CVector2D& addition) { + m_vecMoveSpeed += CVector(addition.x, addition.y, 0.0f); + } + const CVector &GetTurnSpeed() { return m_vecTurnSpeed; } + void SetTurnSpeed(float x, float y, float z) { + m_vecTurnSpeed.x = x; + m_vecTurnSpeed.y = y; + m_vecTurnSpeed.z = z; + } + const CVector &GetCenterOfMass() { return m_vecCentreOfMass; } + void SetCenterOfMass(float x, float y, float z) { + m_vecCentreOfMass.x = x; + m_vecCentreOfMass.y = y; + m_vecCentreOfMass.z = z; + } + + void ApplyMoveSpeed(void); + void ApplyTurnSpeed(void); + // Force actually means Impulse here + void ApplyMoveForce(float jx, float jy, float jz); + void ApplyMoveForce(const CVector &j) { ApplyMoveForce(j.x, j.y, j.z); } + // j(x,y,z) is direction of force, p(x,y,z) is point relative to model center where force is applied + void ApplyTurnForce(float jx, float jy, float jz, float px, float py, float pz); + // j is direction of force, p is point relative to model center where force is applied + void ApplyTurnForce(const CVector &j, const CVector &p) { ApplyTurnForce(j.x, j.y, j.z, p.x, p.y, p.z); } + void ApplyFrictionMoveForce(float jx, float jy, float jz); + void ApplyFrictionMoveForce(const CVector &j) { ApplyFrictionMoveForce(j.x, j.y, j.z); } + void ApplyFrictionTurnForce(float jx, float jy, float jz, float rx, float ry, float rz); + void ApplyFrictionTurnForce(const CVector &j, const CVector &p) { ApplyFrictionTurnForce(j.x, j.y, j.z, p.x, p.y, p.z); } + // springRatio: 1.0 fully extended, 0.0 fully compressed + bool ApplySpringCollision(float springConst, CVector &springDir, CVector &point, float springRatio, float bias); + bool ApplySpringCollisionAlt(float springConst, CVector &springDir, CVector &point, float springRatio, float bias, CVector &forceDir); + bool ApplySpringDampening(float damping, CVector &springDir, CVector &point, CVector &speed); + void ApplyGravity(void); + void ApplyFriction(void); + void ApplyAirResistance(void); + bool ApplyCollision(CPhysical *B, CColPoint &colpoint, float &impulseA, float &impulseB); + bool ApplyCollision(CColPoint &colpoint, float &impulse); + bool ApplyCollisionAlt(CEntity *B, CColPoint &colpoint, float &impulse, CVector &moveSpeed, CVector &turnSpeed); + bool ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint); + bool ApplyFriction(float adhesiveLimit, CColPoint &colpoint); + + bool ProcessShiftSectorList(CPtrList *ptrlists); + bool ProcessCollisionSectorList_SimpleCar(CPtrList *lists); + bool ProcessCollisionSectorList(CPtrList *lists); + bool CheckCollision(void); + bool CheckCollision_SimpleCar(void); +}; diff --git a/src/miami/extras/GitSHA1.cpp.in b/src/miami/extras/GitSHA1.cpp.in new file mode 100644 index 00000000..6168dc79 --- /dev/null +++ b/src/miami/extras/GitSHA1.cpp.in @@ -0,0 +1,2 @@ +#define GIT_SHA1 "@GIT_SHA1@" +const char* g_GIT_SHA1 = GIT_SHA1; diff --git a/src/miami/extras/GitSHA1.h b/src/miami/extras/GitSHA1.h new file mode 100644 index 00000000..359bfaff --- /dev/null +++ b/src/miami/extras/GitSHA1.h @@ -0,0 +1 @@ +extern const char* g_GIT_SHA1; \ No newline at end of file diff --git a/src/miami/extras/arrow.inc b/src/miami/extras/arrow.inc new file mode 100644 index 00000000..8ea78283 --- /dev/null +++ b/src/miami/extras/arrow.inc @@ -0,0 +1,16 @@ +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/src/miami/extras/cursor.inc b/src/miami/extras/cursor.inc new file mode 100644 index 00000000..e8afd394 --- /dev/null +++ b/src/miami/extras/cursor.inc @@ -0,0 +1,16 @@ +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, +255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, +255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, +255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, +255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, +255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, +255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, +255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, +255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, +255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, +255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, +255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, +255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, +255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, +255, 255, 255, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, diff --git a/src/miami/extras/custompipes.cpp b/src/miami/extras/custompipes.cpp new file mode 100644 index 00000000..a485138e --- /dev/null +++ b/src/miami/extras/custompipes.cpp @@ -0,0 +1,543 @@ +#define WITHD3D +#include "common.h" + +#ifdef EXTENDED_PIPELINES + +#include "main.h" +#include "RwHelper.h" +#include "Lights.h" +#include "Timecycle.h" +#include "FileMgr.h" +#include "Clock.h" +#include "Weather.h" +#include "TxdStore.h" +#include "Renderer.h" +#include "World.h" +#include "custompipes.h" + +#ifndef LIBRW +#error "Need librw for EXTENDED_PIPELINES" +#endif + +namespace CustomPipes { + +rw::int32 CustomMatOffset; + +void* +CustomMatCtor(void *object, int32, int32) +{ + CustomMatExt *ext = GetCustomMatExt((rw::Material*)object); + ext->glossTex = nil; + ext->haveGloss = false; + return object; +} + +void* +CustomMatCopy(void *dst, void *src, int32, int32) +{ + CustomMatExt *srcext = GetCustomMatExt((rw::Material*)src); + CustomMatExt *dstext = GetCustomMatExt((rw::Material*)dst); + dstext->glossTex = srcext->glossTex; + dstext->haveGloss = srcext->haveGloss; + return dst; +} + + + +rw::TexDictionary *neoTxd; + +bool bRenderingEnvMap; +int32 EnvMapSize = 128; +rw::Camera *EnvMapCam; +rw::Texture *EnvMapTex; +rw::Texture *EnvMaskTex; +static rw::RWDEVICE::Im2DVertex EnvScreenQuad[4]; +static int16 QuadIndices[6] = { 0, 1, 2, 0, 2, 3 }; + +static rw::Camera* +CreateEnvMapCam(rw::World *world) +{ + rw::Raster *fbuf = rw::Raster::create(EnvMapSize, EnvMapSize, 0, rw::Raster::CAMERATEXTURE); + if(fbuf){ + rw::Raster *zbuf = rw::Raster::create(EnvMapSize, EnvMapSize, 0, rw::Raster::ZBUFFER); + if(zbuf){ + rw::Frame *frame = rw::Frame::create(); + if(frame){ + rw::Camera *cam = rw::Camera::create(); + if(cam){ + cam->frameBuffer = fbuf; + cam->zBuffer = zbuf; + cam->setFrame(frame); + cam->setNearPlane(0.1f); + cam->setFarPlane(250.0f); + rw::V2d vw = { 2.0f, 2.0f }; + cam->setViewWindow(&vw); + world->addCamera(cam); + EnvMapTex = rw::Texture::create(fbuf); + EnvMapTex->setFilter(rw::Texture::LINEAR); + + frame->matrix.right.x = -1.0f; + frame->matrix.up.y = -1.0f; + frame->matrix.update(); + return cam; + } + frame->destroy(); + } + zbuf->destroy(); + } + fbuf->destroy(); + } + return nil; +} + +static void +DestroyCam(rw::Camera *cam) +{ + if(cam == nil) + return; + if(cam->frameBuffer){ + cam->frameBuffer->destroy(); + cam->frameBuffer = nil; + } + if(cam->zBuffer){ + cam->zBuffer->destroy(); + cam->zBuffer = nil; + } + rw::Frame *f = cam->getFrame(); + if(f){ + cam->setFrame(nil); + f->destroy(); + } + cam->world->removeCamera(cam); + cam->destroy(); +} + +void +RenderEnvMapScene(void) +{ + CRenderer::RenderRoads(); + CRenderer::RenderEverythingBarRoads(); + CRenderer::RenderFadingInEntities(); +} + +void +EnvMapRender(void) +{ + if(VehiclePipeSwitch != VEHICLEPIPE_NEO) + return; + + RwCameraEndUpdate(Scene.camera); + + // Neo does this differently, but i'm not quite convinced it's much better + rw::V3d camPos = FindPlayerCoors(); + EnvMapCam->getFrame()->matrix.pos = camPos; + EnvMapCam->getFrame()->transform(&EnvMapCam->getFrame()->matrix, rw::COMBINEREPLACE); + + rw::RGBA skycol; + skycol.red = CTimeCycle::GetSkyBottomRed(); + skycol.green = CTimeCycle::GetSkyBottomGreen(); + skycol.blue = CTimeCycle::GetSkyBottomBlue(); + skycol.alpha = 255; + EnvMapCam->clear(&skycol, rwCAMERACLEARZ|rwCAMERACLEARIMAGE); + RwCameraBeginUpdate(EnvMapCam); + bRenderingEnvMap = true; + RenderEnvMapScene(); + bRenderingEnvMap = false; + + if(EnvMaskTex){ + rw::SetRenderState(rw::VERTEXALPHA, TRUE); + rw::SetRenderState(rw::SRCBLEND, rw::BLENDZERO); + rw::SetRenderState(rw::DESTBLEND, rw::BLENDSRCCOLOR); + rw::SetRenderStatePtr(rw::TEXTURERASTER, EnvMaskTex->raster); + rw::im2d::RenderIndexedPrimitive(rw::PRIMTYPETRILIST, EnvScreenQuad, 4, QuadIndices, 6); + rw::SetRenderState(rw::SRCBLEND, rw::BLENDSRCALPHA); + rw::SetRenderState(rw::DESTBLEND, rw::BLENDINVSRCALPHA); + } + RwCameraEndUpdate(EnvMapCam); + + + RwCameraBeginUpdate(Scene.camera); + + // debug env map +// rw::SetRenderStatePtr(rw::TEXTURERASTER, EnvMapTex->raster); +// rw::im2d::RenderIndexedPrimitive(rw::PRIMTYPETRILIST, EnvScreenQuad, 4, QuadIndices, 6); +} + +static void +EnvMapInit(void) +{ + if(neoTxd) + EnvMaskTex = neoTxd->find("CarReflectionMask"); + + EnvMapCam = CreateEnvMapCam(Scene.world); + + int width = EnvMapCam->frameBuffer->width; + int height = EnvMapCam->frameBuffer->height; + float screenZ = RwIm2DGetNearScreenZ(); + float recipZ = 1.0f/EnvMapCam->nearPlane; + + EnvScreenQuad[0].setScreenX(0.0f); + EnvScreenQuad[0].setScreenY(0.0f); + EnvScreenQuad[0].setScreenZ(screenZ); + EnvScreenQuad[0].setCameraZ(EnvMapCam->nearPlane); + EnvScreenQuad[0].setRecipCameraZ(recipZ); + EnvScreenQuad[0].setColor(255, 255, 255, 255); + EnvScreenQuad[0].setU(0.0f, recipZ); + EnvScreenQuad[0].setV(0.0f, recipZ); + + EnvScreenQuad[1].setScreenX(0.0f); + EnvScreenQuad[1].setScreenY(height); + EnvScreenQuad[1].setScreenZ(screenZ); + EnvScreenQuad[1].setCameraZ(EnvMapCam->nearPlane); + EnvScreenQuad[1].setRecipCameraZ(recipZ); + EnvScreenQuad[1].setColor(255, 255, 255, 255); + EnvScreenQuad[1].setU(0.0f, recipZ); + EnvScreenQuad[1].setV(1.0f, recipZ); + + EnvScreenQuad[2].setScreenX(width); + EnvScreenQuad[2].setScreenY(height); + EnvScreenQuad[2].setScreenZ(screenZ); + EnvScreenQuad[2].setCameraZ(EnvMapCam->nearPlane); + EnvScreenQuad[2].setRecipCameraZ(recipZ); + EnvScreenQuad[2].setColor(255, 255, 255, 255); + EnvScreenQuad[2].setU(1.0f, recipZ); + EnvScreenQuad[2].setV(1.0f, recipZ); + + EnvScreenQuad[3].setScreenX(width); + EnvScreenQuad[3].setScreenY(0.0f); + EnvScreenQuad[3].setScreenZ(screenZ); + EnvScreenQuad[3].setCameraZ(EnvMapCam->nearPlane); + EnvScreenQuad[3].setRecipCameraZ(recipZ); + EnvScreenQuad[3].setColor(255, 255, 255, 255); + EnvScreenQuad[3].setU(1.0f, recipZ); + EnvScreenQuad[3].setV(0.0f, recipZ); +} + +static void +EnvMapShutdown(void) +{ + EnvMapTex->raster = nil; + EnvMapTex->destroy(); + EnvMapTex = nil; + DestroyCam(EnvMapCam); + EnvMapCam = nil; +} + +/* + * Tweak values + */ + +#define INTERP_SETUP \ + int h1 = CClock::GetHours(); \ + int h2 = (h1+1)%24; \ + int w1 = CWeather::OldWeatherType; \ + int w2 = CWeather::NewWeatherType; \ + float timeInterp = (CClock::GetSeconds()/60.0f + CClock::GetMinutes())/60.0f; \ + float c0 = (1.0f-timeInterp)*(1.0f-CWeather::InterpolationValue); \ + float c1 = timeInterp*(1.0f-CWeather::InterpolationValue); \ + float c2 = (1.0f-timeInterp)*CWeather::InterpolationValue; \ + float c3 = timeInterp*CWeather::InterpolationValue; +#define INTERP(v) v[h1][w1]*c0 + v[h2][w1]*c1 + v[h1][w2]*c2 + v[h2][w2]*c3; +#define INTERPF(v,f) v[h1][w1].f*c0 + v[h2][w1].f*c1 + v[h1][w2].f*c2 + v[h2][w2].f*c3; + +InterpolatedFloat::InterpolatedFloat(float init) +{ + curInterpolator = 61; // compared against second + for(int h = 0; h < 24; h++) + for(int w = 0; w < NUMWEATHERS; w++) + data[h][w] = init; +} + +void +InterpolatedFloat::Read(char *s, int line, int field) +{ + sscanf(s, "%f", &data[line][field]); +} + +float +InterpolatedFloat::Get(void) +{ + if(curInterpolator != CClock::GetSeconds()){ + INTERP_SETUP + curVal = INTERP(data); + curInterpolator = CClock::GetSeconds(); + } + return curVal; +} + +InterpolatedColor::InterpolatedColor(const Color &init) +{ + curInterpolator = 61; // compared against second + for(int h = 0; h < 24; h++) + for(int w = 0; w < NUMWEATHERS; w++) + data[h][w] = init; +} + +void +InterpolatedColor::Read(char *s, int line, int field) +{ + int r, g, b, a; + sscanf(s, "%i, %i, %i, %i", &r, &g, &b, &a); + data[line][field] = Color(r/255.0f, g/255.0f, b/255.0f, a/255.0f); +} + +Color +InterpolatedColor::Get(void) +{ + if(curInterpolator != CClock::GetSeconds()){ + INTERP_SETUP + curVal.r = INTERPF(data, r); + curVal.g = INTERPF(data, g); + curVal.b = INTERPF(data, b); + curVal.a = INTERPF(data, a); + curInterpolator = CClock::GetSeconds(); + } + return curVal; +} + +void +InterpolatedLight::Read(char *s, int line, int field) +{ + int r, g, b, a; + sscanf(s, "%i, %i, %i, %i", &r, &g, &b, &a); + data[line][field] = Color(r/255.0f, g/255.0f, b/255.0f, a/100.0f); +} + +char* +ReadTweakValueTable(char *fp, InterpolatedValue &interp) +{ + char buf[24], *p; + int c; + int line, field; + + line = 0; + c = *fp++; + while(c != '\0' && line < 24){ + field = 0; + if(c != '\0' && c != '#'){ + while(c != '\0' && c != '\n' && field < NUMWEATHERS){ + p = buf; + while(c != '\0' && c == '\t') + c = *fp++; + *p++ = c; + while(c = *fp++, c != '\0' && c != '\t' && c != '\n') + *p++ = c; + *p++ = '\0'; + interp.Read(buf, line, field); + field++; + } + line++; + } + while(c != '\0' && c != '\n') + c = *fp++; + c = *fp++; + } + return fp-1; +} + + + +/* + * Neo Vehicle pipe + */ + +int32 VehiclePipeSwitch = VEHICLEPIPE_MATFX; +float VehicleShininess = 1.0f; +float VehicleSpecularity = 1.0f; +InterpolatedFloat Fresnel(0.4f); +InterpolatedFloat Power(18.0f); +InterpolatedLight DiffColor(Color(0.0f, 0.0f, 0.0f, 0.0f)); +InterpolatedLight SpecColor(Color(0.7f, 0.7f, 0.7f, 1.0f)); +rw::ObjPipeline *vehiclePipe; + +void +AttachVehiclePipe(rw::Atomic *atomic) +{ + atomic->pipeline = vehiclePipe; +} + +void +AttachVehiclePipe(rw::Clump *clump) +{ + FORLIST(lnk, clump->atomics) + AttachVehiclePipe(rw::Atomic::fromClump(lnk)); +} + + + +/* + * Neo World pipe + */ + +bool LightmapEnable; +float LightmapMult = 1.0f; +InterpolatedFloat WorldLightmapBlend(1.0f); +rw::ObjPipeline *worldPipe; + +void +AttachWorldPipe(rw::Atomic *atomic) +{ + atomic->pipeline = worldPipe; +} + +void +AttachWorldPipe(rw::Clump *clump) +{ + FORLIST(lnk, clump->atomics) + AttachWorldPipe(rw::Atomic::fromClump(lnk)); +} + + + + +/* + * Neo Gloss pipe + */ + +bool GlossEnable; +float GlossMult = 1.0f; +rw::ObjPipeline *glossPipe; + +rw::Texture* +GetGlossTex(rw::Material *mat) +{ + if(neoTxd == nil) + return nil; + CustomMatExt *ext = GetCustomMatExt(mat); + if(!ext->haveGloss){ + char glossname[128]; + strcpy(glossname, mat->texture->name); + strcat(glossname, "_gloss"); + ext->glossTex = neoTxd->find(glossname); + ext->haveGloss = true; + } + return ext->glossTex; +} + +void +AttachGlossPipe(rw::Atomic *atomic) +{ + atomic->pipeline = glossPipe; +} + +void +AttachGlossPipe(rw::Clump *clump) +{ + FORLIST(lnk, clump->atomics) + AttachWorldPipe(rw::Atomic::fromClump(lnk)); +} + + + +/* + * Neo Rim pipes + */ + +bool RimlightEnable; +float RimlightMult = 1.0f; +InterpolatedColor RampStart(Color(0.0f, 0.0f, 0.0f, 1.0f)); +InterpolatedColor RampEnd(Color(1.0f, 1.0f, 1.0f, 1.0f)); +InterpolatedFloat Offset(0.5f); +InterpolatedFloat Scale(1.5f); +InterpolatedFloat Scaling(2.0f); +rw::ObjPipeline *rimPipe; +rw::ObjPipeline *rimSkinPipe; + +void +AttachRimPipe(rw::Atomic *atomic) +{ + if(rw::Skin::get(atomic->geometry)) + atomic->pipeline = rimSkinPipe; + else + atomic->pipeline = rimPipe; +} + +void +AttachRimPipe(rw::Clump *clump) +{ + FORLIST(lnk, clump->atomics) + AttachRimPipe(rw::Atomic::fromClump(lnk)); +} + +/* + * High level stuff + */ + +void +CustomPipeInit(void) +{ + RwStream *stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, "neo/neo.txd"); + if(stream == nil) + printf("Error: couldn't open 'neo/neo.txd'\n"); + else{ + if(RwStreamFindChunk(stream, rwID_TEXDICTIONARY, nil, nil)) + neoTxd = RwTexDictionaryGtaStreamRead(stream); + RwStreamClose(stream, nil); + } + + EnvMapInit(); + + CreateVehiclePipe(); + CreateWorldPipe(); + CreateGlossPipe(); + CreateRimLightPipes(); +} + +void +CustomPipeShutdown(void) +{ + DestroyVehiclePipe(); + DestroyWorldPipe(); + DestroyGlossPipe(); + DestroyRimLightPipes(); + + EnvMapShutdown(); + + if(neoTxd){ + neoTxd->destroy(); + neoTxd = nil; + } +} + +void +CustomPipeRegister(void) +{ +#ifdef RW_OPENGL + CustomPipeRegisterGL(); +#endif + + CustomMatOffset = rw::Material::registerPlugin(sizeof(CustomMatExt), MAKECHUNKID(rwVENDORID_ROCKSTAR, 0x80), + CustomMatCtor, nil, CustomMatCopy); +} + + +// Load textures from generic as fallback + +rw::TexDictionary *genericTxd; +rw::Texture *(*defaultFindCB)(const char *name); + +static rw::Texture* +customFindCB(const char *name) +{ + rw::Texture *res = defaultFindCB(name); + if(res == nil) + res = genericTxd->find(name); + return res; +} + +void +SetTxdFindCallback(void) +{ + int slot = CTxdStore::FindTxdSlot("generic"); + CTxdStore::AddRef(slot); + // TODO: function for this + genericTxd = CTxdStore::GetSlot(slot)->texDict; + assert(genericTxd); + if(defaultFindCB == nil) + defaultFindCB = rw::Texture::findCB; + rw::Texture::findCB = customFindCB; +} + +} + +#endif diff --git a/src/miami/extras/custompipes.h b/src/miami/extras/custompipes.h new file mode 100644 index 00000000..7ad239f0 --- /dev/null +++ b/src/miami/extras/custompipes.h @@ -0,0 +1,145 @@ +#pragma once + +#ifdef LIBRW +#ifdef EXTENDED_PIPELINES + +namespace CustomPipes { + + +extern rw::TexDictionary *neoTxd; + +struct CustomMatExt +{ + rw::Texture *glossTex; + bool haveGloss; +}; +extern rw::int32 CustomMatOffset; +inline CustomMatExt *GetCustomMatExt(rw::Material *mat) { + return PLUGINOFFSET(CustomMatExt, mat, CustomMatOffset); +} + + +struct Color +{ + float r, g, b, a; + Color(void) {} + Color(float r, float g, float b, float a) : r(r), g(g), b(b), a(a) {} +}; + +class InterpolatedValue +{ +public: + virtual void Read(char *s, int line, int field) = 0; +}; + +class InterpolatedFloat : public InterpolatedValue +{ +public: + float data[24][NUMWEATHERS]; + float curInterpolator; + float curVal; + + InterpolatedFloat(float init); + void Read(char *s, int line, int field); + float Get(void); +}; + +class InterpolatedColor : public InterpolatedValue +{ +public: + Color data[24][NUMWEATHERS]; + float curInterpolator; + Color curVal; + + InterpolatedColor(const Color &init); + void Read(char *s, int line, int field); + Color Get(void); +}; + +class InterpolatedLight : public InterpolatedColor +{ +public: + InterpolatedLight(const Color &init) : InterpolatedColor(init) {} + void Read(char *s, int line, int field); +}; + +char *ReadTweakValueTable(char *fp, InterpolatedValue &interp); + + + + + +void CustomPipeRegister(void); +void CustomPipeRegisterGL(void); +void CustomPipeInit(void); +void CustomPipeShutdown(void); +void SetTxdFindCallback(void); + +extern bool bRenderingEnvMap; +extern int32 EnvMapSize; +extern rw::Camera *EnvMapCam; +extern rw::Texture *EnvMapTex; +extern rw::Texture *EnvMaskTex; +void EnvMapRender(void); + +enum { + VEHICLEPIPE_MATFX, + VEHICLEPIPE_NEO +}; +extern int32 VehiclePipeSwitch; +extern float VehicleShininess; +extern float VehicleSpecularity; +extern InterpolatedFloat Fresnel; +extern InterpolatedFloat Power; +extern InterpolatedLight DiffColor; +extern InterpolatedLight SpecColor; +extern rw::ObjPipeline *vehiclePipe; +void CreateVehiclePipe(void); +void DestroyVehiclePipe(void); +void AttachVehiclePipe(rw::Atomic *atomic); +void AttachVehiclePipe(rw::Clump *clump); + +extern bool LightmapEnable; +extern float LightmapMult; +extern InterpolatedFloat WorldLightmapBlend; +extern rw::ObjPipeline *worldPipe; +void CreateWorldPipe(void); +void DestroyWorldPipe(void); +void AttachWorldPipe(rw::Atomic *atomic); +void AttachWorldPipe(rw::Clump *clump); + +extern bool GlossEnable; +extern float GlossMult; +extern rw::ObjPipeline *glossPipe; +void CreateGlossPipe(void); +void DestroyGlossPipe(void); +void AttachGlossPipe(rw::Atomic *atomic); +void AttachGlossPipe(rw::Clump *clump); +rw::Texture *GetGlossTex(rw::Material *mat); + +extern bool RimlightEnable; +extern float RimlightMult; +extern InterpolatedColor RampStart; +extern InterpolatedColor RampEnd; +extern InterpolatedFloat Offset; +extern InterpolatedFloat Scale; +extern InterpolatedFloat Scaling; +extern rw::ObjPipeline *rimPipe; +extern rw::ObjPipeline *rimSkinPipe; +void CreateRimLightPipes(void); +void DestroyRimLightPipes(void); +void AttachRimPipe(rw::Atomic *atomic); +void AttachRimPipe(rw::Clump *clump); + +} + +#endif + +namespace WorldRender{ +extern int numBlendInsts[3]; +void AtomicFirstPass(RpAtomic *atomic, int pass); +void AtomicFullyTransparent(RpAtomic *atomic, int pass, int fadeAlpha); +void RenderBlendPass(int pass); +} + +#endif diff --git a/src/miami/extras/custompipes_d3d9.cpp b/src/miami/extras/custompipes_d3d9.cpp new file mode 100644 index 00000000..3ad824e3 --- /dev/null +++ b/src/miami/extras/custompipes_d3d9.cpp @@ -0,0 +1,724 @@ +#define WITHD3D +#include "common.h" + +#ifdef RW_D3D9 +#include "main.h" +#include "RwHelper.h" +#include "Lights.h" +#include "Timecycle.h" +#include "FileMgr.h" +#include "Clock.h" +#include "Weather.h" +#include "TxdStore.h" +#include "Renderer.h" +#include "World.h" +#include "custompipes.h" + +#ifdef EXTENDED_PIPELINES + +#ifndef LIBRW +#error "Need librw for EXTENDED_PIPELINES" +#endif + +extern RwTexture *gpWhiteTexture; // from vehicle model info + +namespace CustomPipes { + +enum { + // rim pipe + VSLOC_boneMatrices = rw::d3d::VSLOC_afterLights, + VSLOC_viewVec = VSLOC_boneMatrices + 64*3, + VSLOC_rampStart, + VSLOC_rampEnd, + VSLOC_rimData, + + // gloss pipe + VSLOC_eye = rw::d3d::VSLOC_afterLights, + + VSLOC_reflProps, + VSLOC_specLights +}; + +/* + * Neo Vehicle pipe + */ + +static void *neoVehicle_VS; +static void *neoVehicle_PS; + +void +uploadSpecLights(void) +{ + struct VsLight { + rw::RGBAf color; + float pos[4]; // unused + rw::V3d dir; + float power; + } specLights[1 + NUMEXTRADIRECTIONALS]; + memset(specLights, 0, sizeof(specLights)); + for(int i = 0; i < 1+NUMEXTRADIRECTIONALS; i++) + specLights[i].power = 1.0f; + float power = Power.Get(); + Color speccol = SpecColor.Get(); + specLights[0].color.red = speccol.r; + specLights[0].color.green = speccol.g; + specLights[0].color.blue = speccol.b; + specLights[0].dir = pDirect->getFrame()->getLTM()->at; + specLights[0].power = power; + for(int i = 0; i < NUMEXTRADIRECTIONALS; i++){ + if(pExtraDirectionals[i]->getFlags() & rw::Light::LIGHTATOMICS){ + specLights[1+i].color = pExtraDirectionals[i]->color; + specLights[1+i].dir = pExtraDirectionals[i]->getFrame()->getLTM()->at; + specLights[1+i].power = power*2.0f; + } + } + rw::d3d::d3ddevice->SetVertexShaderConstantF(VSLOC_specLights, (float*)&specLights, 3*(1 + NUMEXTRADIRECTIONALS)); +} + +void +vehicleRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header) +{ + using namespace rw; + using namespace rw::d3d; + using namespace rw::d3d9; + + // TODO: make this less of a kludge + if(VehiclePipeSwitch == VEHICLEPIPE_MATFX){ + matFXGlobals.pipelines[rw::platform]->render(atomic); + return; + } + + int vsBits; + rw::uint32 flags = atomic->geometry->flags; + setStreamSource(0, header->vertexStream[0].vertexBuffer, 0, header->vertexStream[0].stride); + setIndices(header->indexBuffer); + setVertexDeclaration(header->vertexDeclaration); + + vsBits = lightingCB_Shader(atomic); + uploadSpecLights(); + uploadMatrices(atomic->getFrame()->getLTM()); + + setVertexShader(neoVehicle_VS); + + V3d eyePos = rw::engine->currentCamera->getFrame()->getLTM()->pos; + d3ddevice->SetVertexShaderConstantF(VSLOC_eye, (float*)&eyePos, 1); + + float reflProps[4]; + reflProps[0] = Fresnel.Get(); + reflProps[1] = SpecColor.Get().a; + + d3d::setTexture(1, EnvMapTex); + + SetRenderState(SRCBLEND, BLENDONE); + + InstanceData *inst = header->inst; + for(rw::uint32 i = 0; i < header->numMeshes; i++){ + Material *m = inst->material; + + SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 255); + + reflProps[2] = m->surfaceProps.specular * VehicleShininess; + reflProps[3] = m->surfaceProps.specular == 0.0f ? 0.0f : VehicleSpecularity; + d3ddevice->SetVertexShaderConstantF(VSLOC_reflProps, reflProps, 1); + + setMaterial(flags, m->color, m->surfaceProps); + + if(m->texture) + d3d::setTexture(0, m->texture); + else + d3d::setTexture(0, gpWhiteTexture); + setPixelShader(neoVehicle_PS); + + drawInst(header, inst); + inst++; + } + + d3d::setTexture(1, nil); + + SetRenderState(SRCBLEND, BLENDSRCALPHA); +} + +void +CreateVehiclePipe(void) +{ + if(CFileMgr::LoadFile("neo/carTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0) + printf("Error: couldn't open 'neo/carTweakingTable.dat'\n"); + else{ + char *fp = (char*)work_buff; + fp = ReadTweakValueTable(fp, Fresnel); + fp = ReadTweakValueTable(fp, Power); + fp = ReadTweakValueTable(fp, DiffColor); + fp = ReadTweakValueTable(fp, SpecColor); + } + +#include "shaders/obj/neoVehicle_VS.inc" + neoVehicle_VS = rw::d3d::createVertexShader(neoVehicle_VS_cso); + assert(neoVehicle_VS); + +#include "shaders/obj/neoVehicle_PS.inc" + neoVehicle_PS = rw::d3d::createPixelShader(neoVehicle_PS_cso); + assert(neoVehicle_PS); + + + rw::d3d9::ObjPipeline *pipe = rw::d3d9::ObjPipeline::create(); + pipe->instanceCB = rw::d3d9::defaultInstanceCB; + pipe->uninstanceCB = rw::d3d9::defaultUninstanceCB; + pipe->renderCB = vehicleRenderCB; + vehiclePipe = pipe; +} + +void +DestroyVehiclePipe(void) +{ + rw::d3d::destroyVertexShader(neoVehicle_VS); + neoVehicle_VS = nil; + + rw::d3d::destroyPixelShader(neoVehicle_PS); + neoVehicle_PS = nil; + + ((rw::d3d9::ObjPipeline*)vehiclePipe)->destroy(); + vehiclePipe = nil; +} + + + +/* + * Neo World pipe + */ + +static void *neoWorld_VS; +static void *neoWorldVC_PS; + +static void +worldRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header) +{ + using namespace rw; + using namespace rw::d3d; + using namespace rw::d3d9; + + if(!LightmapEnable){ + defaultRenderCB_Shader(atomic, header); + return; + } + + int vsBits; + setStreamSource(0, header->vertexStream[0].vertexBuffer, 0, header->vertexStream[0].stride); + setIndices(header->indexBuffer); + setVertexDeclaration(header->vertexDeclaration); + + vsBits = lightingCB_Shader(atomic); + uploadMatrices(atomic->getFrame()->getLTM()); + + + float lightfactor[4]; + + InstanceData *inst = header->inst; + for(rw::uint32 i = 0; i < header->numMeshes; i++){ + Material *m = inst->material; + + if(MatFX::getEffects(m) == MatFX::DUAL){ + setVertexShader(neoWorld_VS); + + MatFX *matfx = MatFX::get(m); + Texture *dualtex = matfx->getDualTexture(); + if(dualtex == nil) + goto notex; + d3d::setTexture(1, dualtex); + lightfactor[0] = lightfactor[1] = lightfactor[2] = WorldLightmapBlend.Get()*LightmapMult; + }else{ + notex: + setVertexShader(default_amb_VS); + + d3d::setTexture(1, nil); + lightfactor[0] = lightfactor[1] = lightfactor[2] = 0.0f; + } + lightfactor[3] = m->color.alpha/255.0f; + d3d::setTexture(0, m->texture); + d3ddevice->SetPixelShaderConstantF(1, lightfactor, 1); + + SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 255); + + RGBA color = { 255, 255, 255, m->color.alpha }; + setMaterial(color, m->surfaceProps); + + if(m->texture) + d3d::setTexture(0, m->texture); + else + d3d::setTexture(0, gpWhiteTexture); + setPixelShader(neoWorldVC_PS); + + drawInst(header, inst); + inst++; + } + d3d::setTexture(1, nil); +} + +void +CreateWorldPipe(void) +{ + if(CFileMgr::LoadFile("neo/worldTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0) + printf("Error: couldn't open 'neo/worldTweakingTable.dat'\n"); + else + ReadTweakValueTable((char*)work_buff, WorldLightmapBlend); + +#include "shaders/obj/default_UV2_VS.inc" + neoWorld_VS = rw::d3d::createVertexShader(default_UV2_VS_cso); + assert(neoWorld_VS); + +#include "shaders/obj/neoWorldVC_PS.inc" + neoWorldVC_PS = rw::d3d::createPixelShader(neoWorldVC_PS_cso); + assert(neoWorldVC_PS); + + + rw::d3d9::ObjPipeline *pipe = rw::d3d9::ObjPipeline::create(); + pipe->instanceCB = rw::d3d9::defaultInstanceCB; + pipe->uninstanceCB = rw::d3d9::defaultUninstanceCB; + pipe->renderCB = worldRenderCB; + worldPipe = pipe; +} + +void +DestroyWorldPipe(void) +{ + rw::d3d::destroyVertexShader(neoWorld_VS); + neoWorld_VS = nil; + rw::d3d::destroyPixelShader(neoWorldVC_PS); + neoWorldVC_PS = nil; + + + ((rw::d3d9::ObjPipeline*)worldPipe)->destroy(); + worldPipe = nil; +} + + + + +/* + * Neo Gloss pipe + */ + +static void *neoGloss_VS; +static void *neoGloss_PS; + +static void +glossRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header) +{ + worldRenderCB(atomic, header); + + using namespace rw; + using namespace rw::d3d; + using namespace rw::d3d9; + + if(!GlossEnable) + return; + + setVertexShader(neoGloss_VS); + setPixelShader(neoGloss_PS); + + V3d eyePos = rw::engine->currentCamera->getFrame()->getLTM()->pos; + d3ddevice->SetVertexShaderConstantF(VSLOC_eye, (float*)&eyePos, 1); + d3ddevice->SetPixelShaderConstantF(1, (float*)&GlossMult, 1); + + SetRenderState(VERTEXALPHA, TRUE); + SetRenderState(SRCBLEND, BLENDONE); + SetRenderState(DESTBLEND, BLENDONE); + SetRenderState(ZWRITEENABLE, FALSE); + SetRenderState(ALPHATESTFUNC, ALPHAALWAYS); + + InstanceData *inst = header->inst; + for(rw::uint32 i = 0; i < header->numMeshes; i++){ + Material *m = inst->material; + + if(m->texture){ + Texture *tex = GetGlossTex(m); + if(tex){ + d3d::setTexture(0, tex); + drawInst(header, inst); + } + } + inst++; + } + + SetRenderState(ZWRITEENABLE, TRUE); + SetRenderState(ALPHATESTFUNC, ALPHAGREATEREQUAL); + SetRenderState(SRCBLEND, BLENDSRCALPHA); + SetRenderState(DESTBLEND, BLENDINVSRCALPHA); +} + +void +CreateGlossPipe(void) +{ +#include "shaders/obj/neoGloss_VS.inc" + neoGloss_VS = rw::d3d::createVertexShader(neoGloss_VS_cso); + assert(neoGloss_VS); + +#include "shaders/obj/neoGloss_PS.inc" + neoGloss_PS = rw::d3d::createPixelShader(neoGloss_PS_cso); + assert(neoGloss_PS); + + + rw::d3d9::ObjPipeline *pipe = rw::d3d9::ObjPipeline::create(); + pipe->instanceCB = rw::d3d9::defaultInstanceCB; + pipe->uninstanceCB = rw::d3d9::defaultUninstanceCB; + pipe->renderCB = glossRenderCB; + glossPipe = pipe; +} + +void +DestroyGlossPipe(void) +{ + rw::d3d::destroyVertexShader(neoGloss_VS); + neoGloss_VS = nil; + + rw::d3d::destroyPixelShader(neoGloss_PS); + neoGloss_PS = nil; + + ((rw::d3d9::ObjPipeline*)glossPipe)->destroy(); + glossPipe = nil; +} + + + +/* + * Neo Rim pipes + */ + +static void *neoRim_VS; +static void *neoRimSkin_VS; + +static void +uploadRimData(bool enable) +{ + using namespace rw; + using namespace rw::d3d; + + V3d viewVec = rw::engine->currentCamera->getFrame()->getLTM()->at; + d3ddevice->SetVertexShaderConstantF(VSLOC_viewVec, (float*)&viewVec, 1); + float rimData[4]; + rimData[0] = Offset.Get(); + rimData[1] = Scale.Get(); + if(enable) + rimData[2] = Scaling.Get()*RimlightMult; + else + rimData[2] = 0.0f; + rimData[3] = 0.0f; + d3ddevice->SetVertexShaderConstantF(VSLOC_rimData, rimData, 1); + Color col = RampStart.Get(); + d3ddevice->SetVertexShaderConstantF(VSLOC_rampStart, (float*)&col, 1); + col = RampEnd.Get(); + d3ddevice->SetVertexShaderConstantF(VSLOC_rampEnd, (float*)&col, 1); +} + +static void +rimRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header) +{ + using namespace rw; + using namespace rw::d3d; + using namespace rw::d3d9; + + if(!RimlightEnable){ + defaultRenderCB_Shader(atomic, header); + return; + } + + int vsBits; + rw::uint32 flags = atomic->geometry->flags; + setStreamSource(0, header->vertexStream[0].vertexBuffer, 0, header->vertexStream[0].stride); + setIndices(header->indexBuffer); + setVertexDeclaration(header->vertexDeclaration); + + vsBits = lightingCB_Shader(atomic); + uploadMatrices(atomic->getFrame()->getLTM()); + + setVertexShader(neoRim_VS); + + uploadRimData(atomic->geometry->flags & Geometry::LIGHT); + + InstanceData *inst = header->inst; + for(rw::uint32 i = 0; i < header->numMeshes; i++){ + Material *m = inst->material; + + SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 255); + + setMaterial(flags, m->color, m->surfaceProps); + + if(m->texture){ + d3d::setTexture(0, m->texture); + setPixelShader(default_tex_PS); + }else + setPixelShader(default_PS); + + drawInst(header, inst); + inst++; + } +} + +static void +rimSkinRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header) +{ + using namespace rw; + using namespace rw::d3d; + using namespace rw::d3d9; + + if(!RimlightEnable){ + skinRenderCB(atomic, header); + return; + } + + int vsBits; + rw::uint32 flags = atomic->geometry->flags; + setStreamSource(0, (IDirect3DVertexBuffer9*)header->vertexStream[0].vertexBuffer, + 0, header->vertexStream[0].stride); + setIndices((IDirect3DIndexBuffer9*)header->indexBuffer); + setVertexDeclaration((IDirect3DVertexDeclaration9*)header->vertexDeclaration); + + vsBits = lightingCB_Shader(atomic); + uploadMatrices(atomic->getFrame()->getLTM()); + + uploadSkinMatrices(atomic); + + setVertexShader(neoRimSkin_VS); + + uploadRimData(atomic->geometry->flags & Geometry::LIGHT); + + InstanceData *inst = header->inst; + for(rw::uint32 i = 0; i < header->numMeshes; i++){ + Material *m = inst->material; + + SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 255); + + setMaterial(flags, m->color, m->surfaceProps); + + if(inst->material->texture){ + d3d::setTexture(0, m->texture); + setPixelShader(default_tex_PS); + }else + setPixelShader(default_PS); + + drawInst(header, inst); + inst++; + } +} + +void +CreateRimLightPipes(void) +{ + if(CFileMgr::LoadFile("neo/rimTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0) + printf("Error: couldn't open 'neo/rimTweakingTable.dat'\n"); + else{ + char *fp = (char*)work_buff; + fp = ReadTweakValueTable(fp, RampStart); + fp = ReadTweakValueTable(fp, RampEnd); + fp = ReadTweakValueTable(fp, Offset); + fp = ReadTweakValueTable(fp, Scale); + fp = ReadTweakValueTable(fp, Scaling); + } + + +#include "shaders/obj/neoRim_VS.inc" + neoRim_VS = rw::d3d::createVertexShader(neoRim_VS_cso); + assert(neoRim_VS); + +#include "shaders/obj/neoRimSkin_VS.inc" + neoRimSkin_VS = rw::d3d::createVertexShader(neoRimSkin_VS_cso); + assert(neoRimSkin_VS); + + + rw::d3d9::ObjPipeline *pipe = rw::d3d9::ObjPipeline::create(); + pipe->instanceCB = rw::d3d9::defaultInstanceCB; + pipe->uninstanceCB = rw::d3d9::defaultUninstanceCB; + pipe->renderCB = rimRenderCB; + rimPipe = pipe; + + pipe = rw::d3d9::ObjPipeline::create(); + pipe->instanceCB = rw::d3d9::skinInstanceCB; + pipe->uninstanceCB = nil; + pipe->renderCB = rimSkinRenderCB; + rimSkinPipe = pipe; +} + +void +DestroyRimLightPipes(void) +{ + rw::d3d::destroyVertexShader(neoRim_VS); + neoRim_VS = nil; + + rw::d3d::destroyVertexShader(neoRimSkin_VS); + neoRimSkin_VS = nil; + + ((rw::d3d9::ObjPipeline*)rimPipe)->destroy(); + rimPipe = nil; + + ((rw::d3d9::ObjPipeline*)rimSkinPipe)->destroy(); + rimSkinPipe = nil; +} + +} + +#endif + +#ifdef NEW_RENDERER +#ifndef LIBRW +#error "Need librw for NEW_PIPELINES" +#endif + +namespace WorldRender +{ + +struct BuildingInst +{ + rw::RawMatrix combinedMat; + rw::d3d9::InstanceDataHeader *instHeader; + uint8 fadeAlpha; + bool lighting; +}; +BuildingInst blendInsts[3][2000]; +int numBlendInsts[3]; + +static RwRGBAReal black; + +static void +SetMatrix(BuildingInst *building, rw::Matrix *worldMat) +{ + using namespace rw; + RawMatrix world, worldview; + Camera *cam = engine->currentCamera; + convMatrix(&world, worldMat); + RawMatrix::mult(&worldview, &world, &cam->devView); + RawMatrix::mult(&building->combinedMat, &worldview, &cam->devProj); +} + +static bool +IsTextureTransparent(RwTexture *tex) +{ + if(tex == nil || tex->raster == nil) + return false; + return PLUGINOFFSET(rw::d3d::D3dRaster, tex->raster, rw::d3d::nativeRasterOffset)->hasAlpha; +} + +// Render all opaque meshes and put atomics that needs blending +// into the deferred list. +void +AtomicFirstPass(RpAtomic *atomic, int pass) +{ + using namespace rw; + using namespace rw::d3d; + using namespace rw::d3d9; + + BuildingInst *building = &blendInsts[pass][numBlendInsts[pass]]; + + atomic->getPipeline()->instance(atomic); + building->instHeader = (d3d9::InstanceDataHeader*)atomic->geometry->instData; + assert(building->instHeader != nil); + assert(building->instHeader->platform == PLATFORM_D3D9); + building->fadeAlpha = 255; + building->lighting = !!(atomic->geometry->flags & rw::Geometry::LIGHT); + rw::uint32 flags = atomic->geometry->flags; + + bool setupDone = false; + bool defer = false; + SetMatrix(building, atomic->getFrame()->getLTM()); + + InstanceData *inst = building->instHeader->inst; + for(rw::uint32 i = 0; i < building->instHeader->numMeshes; i++, inst++){ + Material *m = inst->material; + + if(inst->vertexAlpha || m->color.alpha != 255 || + IsTextureTransparent(m->texture)){ + defer = true; + continue; + } + + // alright we're rendering this atomic + if(!setupDone){ + setStreamSource(0, building->instHeader->vertexStream[0].vertexBuffer, 0, building->instHeader->vertexStream[0].stride); + setIndices(building->instHeader->indexBuffer); + setVertexDeclaration(building->instHeader->vertexDeclaration); + setVertexShader(default_amb_VS); + d3ddevice->SetVertexShaderConstantF(VSLOC_combined, (float*)&building->combinedMat, 4); + if(building->lighting) + setAmbient(pAmbient->color); + else + setAmbient(black); + setupDone = true; + } + + setMaterial(flags, m->color, m->surfaceProps); + + if(m->texture){ + d3d::setTexture(0, m->texture); + setPixelShader(default_tex_PS); + }else + setPixelShader(default_PS); + + drawInst(building->instHeader, inst); + } + if(defer) + numBlendInsts[pass]++; +} + +void +AtomicFullyTransparent(RpAtomic *atomic, int pass, int fadeAlpha) +{ + using namespace rw; + using namespace rw::d3d; + using namespace rw::d3d9; + + BuildingInst *building = &blendInsts[pass][numBlendInsts[pass]]; + + atomic->getPipeline()->instance(atomic); + building->instHeader = (d3d9::InstanceDataHeader*)atomic->geometry->instData; + assert(building->instHeader != nil); + assert(building->instHeader->platform == PLATFORM_D3D9); + building->fadeAlpha = fadeAlpha; + building->lighting = !!(atomic->geometry->flags & rw::Geometry::LIGHT); + SetMatrix(building, atomic->getFrame()->getLTM()); + numBlendInsts[pass]++; +} + +void +RenderBlendPass(int pass) +{ + using namespace rw; + using namespace rw::d3d; + using namespace rw::d3d9; + + setVertexShader(default_amb_VS); + + int i; + for(i = 0; i < numBlendInsts[pass]; i++){ + BuildingInst *building = &blendInsts[pass][i]; + + setStreamSource(0, building->instHeader->vertexStream[0].vertexBuffer, 0, building->instHeader->vertexStream[0].stride); + setIndices(building->instHeader->indexBuffer); + setVertexDeclaration(building->instHeader->vertexDeclaration); + d3ddevice->SetVertexShaderConstantF(VSLOC_combined, (float*)&building->combinedMat, 4); + if(building->lighting) + setAmbient(pAmbient->color); + else + setAmbient(black); + + InstanceData *inst = building->instHeader->inst; + for(rw::uint32 j = 0; j < building->instHeader->numMeshes; j++, inst++){ + Material *m = inst->material; + if(!inst->vertexAlpha && m->color.alpha == 255 && !IsTextureTransparent(m->texture) && building->fadeAlpha == 255) + continue; // already done this one + + rw::RGBA color = m->color; + color.alpha = (color.alpha * building->fadeAlpha)/255; + setMaterial(color, m->surfaceProps); // always modulate here + + if(m->texture){ + d3d::setTexture(0, m->texture); + setPixelShader(default_tex_PS); + }else + setPixelShader(default_PS); + + drawInst(building->instHeader, inst); + } + } +} +} +#endif + +#endif diff --git a/src/miami/extras/custompipes_gl.cpp b/src/miami/extras/custompipes_gl.cpp new file mode 100644 index 00000000..d74e40db --- /dev/null +++ b/src/miami/extras/custompipes_gl.cpp @@ -0,0 +1,738 @@ +#include "common.h" + +#ifdef RW_OPENGL +#include "main.h" +#include "RwHelper.h" +#include "Lights.h" +#include "Timecycle.h" +#include "FileMgr.h" +#include "Clock.h" +#include "Weather.h" +#include "TxdStore.h" +#include "Renderer.h" +#include "World.h" +#include "custompipes.h" + +#ifdef EXTENDED_PIPELINES + +#ifndef LIBRW +#error "Need librw for EXTENDED_PIPELINES" +#endif + +namespace CustomPipes { + +static int32 u_viewVec; +static int32 u_rampStart; +static int32 u_rampEnd; +static int32 u_rimData; + +static int32 u_lightMap; + +static int32 u_eye; +static int32 u_reflProps; +static int32 u_specDir; +static int32 u_specColor; + +#define U(i) currentShader->uniformLocations[i] + +/* + * Neo Vehicle pipe + */ + +rw::gl3::Shader *neoVehicleShader; + +static void +uploadSpecLights(void) +{ + using namespace rw::gl3; + + rw::RGBAf colors[1 + NUMEXTRADIRECTIONALS]; + struct { + rw::V3d dir; + float power; + } dirs[1 + NUMEXTRADIRECTIONALS]; + memset(colors, 0, sizeof(colors)); + memset(dirs, 0, sizeof(dirs)); + for(int i = 0; i < 1+NUMEXTRADIRECTIONALS; i++) + dirs[i].power = 1.0f; + float power = Power.Get(); + Color speccol = SpecColor.Get(); + colors[0].red = speccol.r; + colors[0].green = speccol.g; + colors[0].blue = speccol.b; + dirs[0].dir = pDirect->getFrame()->getLTM()->at; + dirs[0].power = power; + for(int i = 0; i < NUMEXTRADIRECTIONALS; i++){ + if(pExtraDirectionals[i]->getFlags() & rw::Light::LIGHTATOMICS){ + colors[1+i] = pExtraDirectionals[i]->color; + dirs[1+i].dir = pExtraDirectionals[i]->getFrame()->getLTM()->at; + dirs[1+i].power = power*2.0f; + } + } + glUniform4fv(U(u_specDir), 1 + NUMEXTRADIRECTIONALS, (float*)&dirs); + glUniform4fv(U(u_specColor), 1 + NUMEXTRADIRECTIONALS, (float*)&colors); +} + +static void +vehicleRenderCB(rw::Atomic *atomic, rw::gl3::InstanceDataHeader *header) +{ + using namespace rw; + using namespace rw::gl3; + + // TODO: make this less of a kludge + if(VehiclePipeSwitch == VEHICLEPIPE_MATFX){ + matFXGlobals.pipelines[rw::platform]->render(atomic); + return; + } + + Material *m; + + rw::uint32 flags = atomic->geometry->flags; + setWorldMatrix(atomic->getFrame()->getLTM()); + lightingCB(atomic); + + setupVertexInput(header); + + InstanceData *inst = header->inst; + rw::int32 n = header->numMeshes; + + neoVehicleShader->use(); + + V3d eyePos = rw::engine->currentCamera->getFrame()->getLTM()->pos; + glUniform3fv(U(u_eye), 1, (float*)&eyePos); + + uploadSpecLights(); + + float reflProps[4]; + reflProps[0] = Fresnel.Get(); + reflProps[1] = SpecColor.Get().a; + + setTexture(1, EnvMapTex); + + SetRenderState(SRCBLEND, BLENDONE); + + while(n--){ + m = inst->material; + + setMaterial(flags, m->color, m->surfaceProps); + + setTexture(0, m->texture); + + rw::SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 0xFF); + + reflProps[2] = m->surfaceProps.specular * VehicleShininess; + reflProps[3] = m->surfaceProps.specular == 0.0f ? 0.0f : VehicleSpecularity; + glUniform4fv(U(u_reflProps), 1, reflProps); + + drawInst(header, inst); + inst++; + } + + setTexture(1, nil); + + SetRenderState(SRCBLEND, BLENDSRCALPHA); + + teardownVertexInput(header); +} + +void +CreateVehiclePipe(void) +{ + using namespace rw; + using namespace rw::gl3; + + if(CFileMgr::LoadFile("neo/carTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0) + printf("Error: couldn't open 'neo/carTweakingTable.dat'\n"); + else{ + char *fp = (char*)work_buff; + fp = ReadTweakValueTable(fp, Fresnel); + fp = ReadTweakValueTable(fp, Power); + fp = ReadTweakValueTable(fp, DiffColor); + fp = ReadTweakValueTable(fp, SpecColor); + } + + + { +#include "shaders/obj/neoVehicle_frag.inc" +#include "shaders/obj/neoVehicle_vert.inc" + const char *vs[] = { shaderDecl, "#define DIRECTIONALS\n", header_vert_src, neoVehicle_vert_src, nil }; + const char *fs[] = { shaderDecl, header_frag_src, neoVehicle_frag_src, nil }; + neoVehicleShader = Shader::create(vs, fs); + assert(neoVehicleShader); + } + + + rw::gl3::ObjPipeline *pipe = rw::gl3::ObjPipeline::create(); + pipe->instanceCB = rw::gl3::defaultInstanceCB; + pipe->uninstanceCB = nil; + pipe->renderCB = vehicleRenderCB; + vehiclePipe = pipe; +} + +void +DestroyVehiclePipe(void) +{ + neoVehicleShader->destroy(); + neoVehicleShader = nil; + + ((rw::gl3::ObjPipeline*)vehiclePipe)->destroy(); + vehiclePipe = nil; +} + + + +/* + * Neo World pipe + */ + +rw::gl3::Shader *neoWorldShader; + +static void +worldRenderCB(rw::Atomic *atomic, rw::gl3::InstanceDataHeader *header) +{ + using namespace rw; + using namespace rw::gl3; + + if(!LightmapEnable){ + gl3::defaultRenderCB(atomic, header); + return; + } + + Material *m; + + setWorldMatrix(atomic->getFrame()->getLTM()); + lightingCB(atomic); + + setupVertexInput(header); + + InstanceData *inst = header->inst; + rw::int32 n = header->numMeshes; + + neoWorldShader->use(); + + float lightfactor[4]; + + while(n--){ + m = inst->material; + + if(MatFX::getEffects(m) == MatFX::DUAL){ + MatFX *matfx = MatFX::get(m); + Texture *dualtex = matfx->getDualTexture(); + if(dualtex == nil) + goto notex; + setTexture(1, dualtex); + lightfactor[0] = lightfactor[1] = lightfactor[2] = WorldLightmapBlend.Get()*LightmapMult; + }else{ + notex: + setTexture(1, nil); + lightfactor[0] = lightfactor[1] = lightfactor[2] = 0.0f; + } + lightfactor[3] = m->color.alpha/255.0f; + glUniform4fv(U(u_lightMap), 1, lightfactor); + + RGBA color = { 255, 255, 255, m->color.alpha }; + setMaterial(color, m->surfaceProps); + + setTexture(0, m->texture); + + rw::SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 0xFF); + + drawInst(header, inst); + inst++; + } + setTexture(1, nil); + teardownVertexInput(header); +} + +void +CreateWorldPipe(void) +{ + using namespace rw; + using namespace rw::gl3; + + if(CFileMgr::LoadFile("neo/worldTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0) + printf("Error: couldn't open 'neo/worldTweakingTable.dat'\n"); + else + ReadTweakValueTable((char*)work_buff, WorldLightmapBlend); + + { +#include "shaders/obj/neoWorldVC_frag.inc" +#include "shaders/obj/default_UV2_vert.inc" + const char *vs[] = { shaderDecl, header_vert_src, default_UV2_vert_src, nil }; + const char *fs[] = { shaderDecl, header_frag_src, neoWorldVC_frag_src, nil }; + neoWorldShader = Shader::create(vs, fs); + assert(neoWorldShader); + } + + + rw::gl3::ObjPipeline *pipe = rw::gl3::ObjPipeline::create(); + pipe->instanceCB = rw::gl3::defaultInstanceCB; + pipe->uninstanceCB = nil; + pipe->renderCB = worldRenderCB; + worldPipe = pipe; +} + +void +DestroyWorldPipe(void) +{ + neoWorldShader->destroy(); + neoWorldShader = nil; + + ((rw::gl3::ObjPipeline*)worldPipe)->destroy(); + worldPipe = nil; +} + + + + +/* + * Neo Gloss pipe + */ + +rw::gl3::Shader *neoGlossShader; + +static void +glossRenderCB(rw::Atomic *atomic, rw::gl3::InstanceDataHeader *header) +{ + using namespace rw; + using namespace rw::gl3; + + worldRenderCB(atomic, header); + if(!GlossEnable) + return; + + Material *m; + + setupVertexInput(header); + + InstanceData *inst = header->inst; + rw::int32 n = header->numMeshes; + + neoGlossShader->use(); + + V3d eyePos = rw::engine->currentCamera->getFrame()->getLTM()->pos; + glUniform3fv(U(u_eye), 1, (float*)&eyePos); + float reflProps[4]; + reflProps[0] = GlossMult; + reflProps[1] = 0.0f; + reflProps[2] = 0.0f; + reflProps[3] = 0.0f; + glUniform4fv(U(u_reflProps), 1, reflProps); + + SetRenderState(VERTEXALPHA, TRUE); + SetRenderState(SRCBLEND, BLENDONE); + SetRenderState(DESTBLEND, BLENDONE); + SetRenderState(ZWRITEENABLE, FALSE); + SetRenderState(ALPHATESTFUNC, ALPHAALWAYS); + + while(n--){ + m = inst->material; + + RGBA color = { 255, 255, 255, m->color.alpha }; + setMaterial(color, m->surfaceProps); + + if(m->texture){ + Texture *tex = GetGlossTex(m); + if(tex){ + setTexture(0, tex); + drawInst(header, inst); + } + } + inst++; + } + + SetRenderState(ZWRITEENABLE, TRUE); + SetRenderState(ALPHATESTFUNC, ALPHAGREATEREQUAL); + SetRenderState(SRCBLEND, BLENDSRCALPHA); + SetRenderState(DESTBLEND, BLENDINVSRCALPHA); + + teardownVertexInput(header); +} + +void +CreateGlossPipe(void) +{ + using namespace rw; + using namespace rw::gl3; + + { +#include "shaders/obj/neoGloss_frag.inc" +#include "shaders/obj/neoGloss_vert.inc" + const char *vs[] = { shaderDecl, header_vert_src, neoGloss_vert_src, nil }; + const char *fs[] = { shaderDecl, header_frag_src, neoGloss_frag_src, nil }; + neoGlossShader = Shader::create(vs, fs); + assert(neoGlossShader); + } + + rw::gl3::ObjPipeline *pipe = rw::gl3::ObjPipeline::create(); + pipe->instanceCB = rw::gl3::defaultInstanceCB; + pipe->uninstanceCB = nil; + pipe->renderCB = glossRenderCB; + glossPipe = pipe; +} + +void +DestroyGlossPipe(void) +{ + neoGlossShader->destroy(); + neoGlossShader = nil; + + ((rw::gl3::ObjPipeline*)glossPipe)->destroy(); + glossPipe = nil; +} + + + +/* + * Neo Rim pipes + */ + +rw::gl3::Shader *neoRimShader; +rw::gl3::Shader *neoRimSkinShader; + +static void +uploadRimData(bool enable) +{ + using namespace rw; + using namespace rw::gl3; + + V3d viewVec = rw::engine->currentCamera->getFrame()->getLTM()->at; + glUniform3fv(U(u_viewVec), 1, (float*)&viewVec); + float rimData[4]; + rimData[0] = Offset.Get(); + rimData[1] = Scale.Get(); + if(enable) + rimData[2] = Scaling.Get()*RimlightMult; + else + rimData[2] = 0.0f; + rimData[3] = 0.0f; + glUniform3fv(U(u_rimData), 1, rimData); + Color col = RampStart.Get(); + glUniform4fv(U(u_rampStart), 1, (float*)&col); + col = RampEnd.Get(); + glUniform4fv(U(u_rampEnd), 1, (float*)&col); +} + +static void +rimSkinRenderCB(rw::Atomic *atomic, rw::gl3::InstanceDataHeader *header) +{ + using namespace rw; + using namespace rw::gl3; + + if(!RimlightEnable){ + gl3::skinRenderCB(atomic, header); + return; + } + + Material *m; + + rw::uint32 flags = atomic->geometry->flags; + setWorldMatrix(atomic->getFrame()->getLTM()); + lightingCB(atomic); + + setupVertexInput(header); + + InstanceData *inst = header->inst; + rw::int32 n = header->numMeshes; + + neoRimSkinShader->use(); + + uploadRimData(atomic->geometry->flags & Geometry::LIGHT); + + uploadSkinMatrices(atomic); + + while(n--){ + m = inst->material; + + setMaterial(flags, m->color, m->surfaceProps); + + setTexture(0, m->texture); + + rw::SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 0xFF); + + drawInst(header, inst); + inst++; + } + teardownVertexInput(header); +} + +static void +rimRenderCB(rw::Atomic *atomic, rw::gl3::InstanceDataHeader *header) +{ + using namespace rw; + using namespace rw::gl3; + + if(!RimlightEnable){ + gl3::defaultRenderCB(atomic, header); + return; + } + + Material *m; + + rw::uint32 flags = atomic->geometry->flags; + setWorldMatrix(atomic->getFrame()->getLTM()); + lightingCB(atomic); + + setupVertexInput(header); + + InstanceData *inst = header->inst; + rw::int32 n = header->numMeshes; + + neoRimShader->use(); + + uploadRimData(atomic->geometry->flags & Geometry::LIGHT); + + while(n--){ + m = inst->material; + + setMaterial(flags, m->color, m->surfaceProps); + + setTexture(0, m->texture); + + rw::SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 0xFF); + + drawInst(header, inst); + inst++; + } + teardownVertexInput(header); +} + +void +CreateRimLightPipes(void) +{ + using namespace rw::gl3; + + if(CFileMgr::LoadFile("neo/rimTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0) + printf("Error: couldn't open 'neo/rimTweakingTable.dat'\n"); + else{ + char *fp = (char*)work_buff; + fp = ReadTweakValueTable(fp, RampStart); + fp = ReadTweakValueTable(fp, RampEnd); + fp = ReadTweakValueTable(fp, Offset); + fp = ReadTweakValueTable(fp, Scale); + fp = ReadTweakValueTable(fp, Scaling); + } + + { +#include "shaders/obj/simple_frag.inc" +#include "shaders/obj/neoRimSkin_vert.inc" + const char *vs[] = { shaderDecl, "#define DIRECTIONALS\n", header_vert_src, neoRimSkin_vert_src, nil }; + const char *fs[] = { shaderDecl, header_frag_src, simple_frag_src, nil }; + neoRimSkinShader = Shader::create(vs, fs); + assert(neoRimSkinShader); + } + + { +#include "shaders/obj/simple_frag.inc" +#include "shaders/obj/neoRim_vert.inc" + const char *vs[] = { shaderDecl, "#define DIRECTIONALS\n", header_vert_src, neoRim_vert_src, nil }; + const char *fs[] = { shaderDecl, header_frag_src, simple_frag_src, nil }; + neoRimShader = Shader::create(vs, fs); + assert(neoRimShader); + } + + + rw::gl3::ObjPipeline *pipe = rw::gl3::ObjPipeline::create(); + pipe->instanceCB = rw::gl3::defaultInstanceCB; + pipe->uninstanceCB = nil; + pipe->renderCB = rimRenderCB; + rimPipe = pipe; + + pipe = rw::gl3::ObjPipeline::create(); + pipe->instanceCB = rw::gl3::skinInstanceCB; + pipe->uninstanceCB = nil; + pipe->renderCB = rimSkinRenderCB; + rimSkinPipe = pipe; +} + +void +DestroyRimLightPipes(void) +{ + neoRimShader->destroy(); + neoRimShader = nil; + + neoRimSkinShader->destroy(); + neoRimSkinShader = nil; + + ((rw::gl3::ObjPipeline*)rimPipe)->destroy(); + rimPipe = nil; + + ((rw::gl3::ObjPipeline*)rimSkinPipe)->destroy(); + rimSkinPipe = nil; +} + + + +void +CustomPipeRegisterGL(void) +{ + u_viewVec = rw::gl3::registerUniform("u_viewVec"); + u_rampStart = rw::gl3::registerUniform("u_rampStart"); + u_rampEnd = rw::gl3::registerUniform("u_rampEnd"); + u_rimData = rw::gl3::registerUniform("u_rimData"); + + u_lightMap = rw::gl3::registerUniform("u_lightMap"); + + u_eye = rw::gl3::registerUniform("u_eye"); + u_reflProps = rw::gl3::registerUniform("u_reflProps"); + u_specDir = rw::gl3::registerUniform("u_specDir"); + u_specColor = rw::gl3::registerUniform("u_specColor"); +} + + +} + +#endif + +#ifdef NEW_RENDERER +#ifndef LIBRW +#error "Need librw for NEW_PIPELINES" +#endif + +namespace WorldRender +{ + +struct BuildingInst +{ + rw::Matrix matrix; + rw::gl3::InstanceDataHeader *instHeader; + uint8 fadeAlpha; + bool lighting; +}; +BuildingInst blendInsts[3][2000]; +int numBlendInsts[3]; + +static RwRGBAReal black; + +static bool +IsTextureTransparent(RwTexture *tex) +{ + if(tex == nil || tex->raster == nil) + return false; + return PLUGINOFFSET(rw::gl3::Gl3Raster, tex->raster, rw::gl3::nativeRasterOffset)->hasAlpha; +} + +// Render all opaque meshes and put atomics that needs blending +// into the deferred list. +void +AtomicFirstPass(RpAtomic *atomic, int pass) +{ + using namespace rw; + using namespace rw::gl3; + + BuildingInst *building = &blendInsts[pass][numBlendInsts[pass]]; + + atomic->getPipeline()->instance(atomic); + building->instHeader = (gl3::InstanceDataHeader*)atomic->geometry->instData; + assert(building->instHeader != nil); + assert(building->instHeader->platform == PLATFORM_GL3); + building->fadeAlpha = 255; + building->lighting = !!(atomic->geometry->flags & rw::Geometry::LIGHT); + rw::uint32 flags = atomic->geometry->flags; + + WorldLights lights; + lights.numAmbients = 1; + lights.numDirectionals = 0; + lights.numLocals = 0; + if(building->lighting) + lights.ambient = pAmbient->color; + else + lights.ambient = black; + + bool setupDone = false; + bool defer = false; + building->matrix = *atomic->getFrame()->getLTM(); + + InstanceData *inst = building->instHeader->inst; + for(rw::uint32 i = 0; i < building->instHeader->numMeshes; i++, inst++){ + Material *m = inst->material; + + if(inst->vertexAlpha || m->color.alpha != 255 || + IsTextureTransparent(m->texture)){ + defer = true; + continue; + } + + // alright we're rendering this atomic + if(!setupDone){ + defaultShader->use(); + setWorldMatrix(&building->matrix); + setupVertexInput(building->instHeader); + setLights(&lights); + setupDone = true; + } + + setMaterial(flags, m->color, m->surfaceProps); + + setTexture(0, m->texture); + + drawInst(building->instHeader, inst); + } + teardownVertexInput(building->instHeader); + if(defer) + numBlendInsts[pass]++; +} + +void +AtomicFullyTransparent(RpAtomic *atomic, int pass, int fadeAlpha) +{ + using namespace rw; + using namespace rw::gl3; + + BuildingInst *building = &blendInsts[pass][numBlendInsts[pass]]; + + atomic->getPipeline()->instance(atomic); + building->instHeader = (gl3::InstanceDataHeader*)atomic->geometry->instData; + assert(building->instHeader != nil); + assert(building->instHeader->platform == PLATFORM_GL3); + building->fadeAlpha = fadeAlpha; + building->lighting = !!(atomic->geometry->flags & rw::Geometry::LIGHT); + building->matrix = *atomic->getFrame()->getLTM(); + numBlendInsts[pass]++; +} + +void +RenderBlendPass(int pass) +{ + using namespace rw; + using namespace rw::gl3; + + defaultShader->use(); + WorldLights lights; + lights.numAmbients = 1; + lights.numDirectionals = 0; + lights.numLocals = 0; + + int i; + for(i = 0; i < numBlendInsts[pass]; i++){ + BuildingInst *building = &blendInsts[pass][i]; + + setupVertexInput(building->instHeader); + setWorldMatrix(&building->matrix); + if(building->lighting) + lights.ambient = pAmbient->color; + else + lights.ambient = black; + setLights(&lights); + + InstanceData *inst = building->instHeader->inst; + for(rw::uint32 j = 0; j < building->instHeader->numMeshes; j++, inst++){ + Material *m = inst->material; + if(!inst->vertexAlpha && m->color.alpha == 255 && !IsTextureTransparent(m->texture) && building->fadeAlpha == 255) + continue; // already done this one + + rw::RGBA color = m->color; + color.alpha = (color.alpha * building->fadeAlpha)/255; + setMaterial(color, m->surfaceProps); // always modulate here + + setTexture(0, m->texture); + + drawInst(building->instHeader, inst); + } + teardownVertexInput(building->instHeader); + } +} +} +#endif + +#endif diff --git a/src/miami/extras/debugmenu.cpp b/src/miami/extras/debugmenu.cpp new file mode 100644 index 00000000..533b97f5 --- /dev/null +++ b/src/miami/extras/debugmenu.cpp @@ -0,0 +1,1312 @@ +#include "common.h" +#ifdef DEBUGMENU +#include "RwHelper.h" +#include "Pad.h" +#include "ControllerConfig.h" +#include "Timer.h" +#include "rtcharse.h" +#include "re3_inttypes.h" +#include "debugmenu.h" +#include + +#ifdef _WIN32 +#define snprintf _snprintf + +#define strdup _strdup +#endif + + +// Font stuff +struct Pt +{ + int x, y; +}; + +enum MenuFontStyle +{ + MENUFONT_NORMAL, + MENUFONT_SEL_ACTIVE, + MENUFONT_SEL_INACTIVE, + MENUFONT_MOUSE +}; + +RtCharset *fontStyles[4]; +RtCharsetDesc fontDesc; +int fontscale = 1; // not supported right now + +Pt +fontGetStringSize(const char *s) +{ + Pt sz = { 0, 0 }; + int x; + char c; + sz.y = fontDesc.height*fontscale; // always assume one line; + x = 0; + while(c = *s++){ + if(c == '\n'){ + sz.y += fontDesc.height*fontscale; + if(x > sz.x) + sz.x = x; + x = 0; + }else + x += fontDesc.width*fontscale; + } + if(x > sz.x) + sz.x = x; + return sz; +} + +Pt +fontPrint(const char *s, float x, float y, int style) +{ + RtCharsetPrintBuffered(fontStyles[style], s, x, y, false); + return fontGetStringSize(s); +} + +int +fontGetLen(int len) +{ + return len*fontDesc.width*fontscale; +} + + +void +createMenuFont(void) +{ + OpenCharsetSafe(); + + RwRGBA fg_normal = { 255, 255, 255, 255 }; + RwRGBA bg_normal = { 255, 255, 255, 0 }; + fontStyles[MENUFONT_NORMAL] = RtCharsetCreate(&fg_normal, &bg_normal); + assert(fontStyles[MENUFONT_NORMAL]); + + RwRGBA fg_sel_active = { 200, 200, 200, 255 }; + RwRGBA bg_sel_active = { 132, 132, 132, 255 }; + fontStyles[MENUFONT_SEL_ACTIVE] = RtCharsetCreate(&fg_sel_active, &bg_sel_active); + assert(fontStyles[MENUFONT_SEL_ACTIVE]); + + RwRGBA fg_sel_inactive = { 200, 200, 200, 255 }; + RwRGBA bg_sel_inactive = { 200, 200, 200, 0 }; + fontStyles[MENUFONT_SEL_INACTIVE] = RtCharsetCreate(&fg_sel_inactive, &bg_sel_inactive); + assert(fontStyles[MENUFONT_SEL_INACTIVE]); + + RwRGBA fg_mouse = { 255, 255, 255, 255 }; + RwRGBA bg_mouse = { 132, 132, 132, 255 }; + fontStyles[MENUFONT_MOUSE] = RtCharsetCreate(&fg_mouse, &bg_mouse); + assert(fontStyles[MENUFONT_MOUSE]); + + RtCharsetGetDesc(fontStyles[MENUFONT_NORMAL], &fontDesc); +} + +void +destroyMenuFont(void) +{ + RtCharsetDestroy(fontStyles[MENUFONT_NORMAL]); + fontStyles[MENUFONT_NORMAL] = nil; + RtCharsetDestroy(fontStyles[MENUFONT_SEL_ACTIVE]); + fontStyles[MENUFONT_SEL_ACTIVE] = nil; + RtCharsetDestroy(fontStyles[MENUFONT_SEL_INACTIVE]); + fontStyles[MENUFONT_SEL_INACTIVE] = nil; + RtCharsetDestroy(fontStyles[MENUFONT_MOUSE]); + fontStyles[MENUFONT_MOUSE] = nil; +} + + + + + + +enum EntryType +{ + MENUEMPTY = 0, + MENUSUB, + MENUVAR, + + MENUVAR_INT, + MENUVAR_FLOAT, + MENUVAR_CMD, + + MENUSCROLL // dummy +}; + +struct Menu +{ + Menu *parent; + RwRect r; + MenuEntry *entries; + int numEntries; + int maxNameWidth, maxValWidth; + + MenuEntry *findEntry(const char *entryname); + void insertEntrySorted(MenuEntry *entry); + void appendEntry(MenuEntry *entry); + + bool isScrollingUp, isScrollingDown; + int scrollStart; + int numVisible; + RwRect scrollUpR, scrollDownR; + void scroll(int off); + + int selection; + MenuEntry *selectedEntry; // updated by update + void changeSelection(int newsel); + void changeSelection(MenuEntry *e); + + void update(void); + void draw(void); + Menu(void){ memset(this, 0, sizeof(Menu)); } + ~Menu(void); +}; +extern Menu toplevel; + +struct MenuEntry_Sub : MenuEntry +{ + Menu *submenu; + + MenuEntry_Sub(const char *name, Menu *menu); + ~MenuEntry_Sub(void) { delete submenu; } +}; + +struct MenuEntry_Var : MenuEntry +{ + int maxvallen; + int vartype; + bool wrapAround; + + virtual void processInput(bool mouseOver, bool selected) = 0; + int getValWidth(void) { return maxvallen; } + virtual void getValStr(char *str, int len) = 0; + MenuEntry_Var(const char *name, int type); +}; + +struct MenuEntry_Int : MenuEntry_Var +{ + virtual void setStrings(const char **strings) = 0; + virtual int findStringLen(void) = 0; + MenuEntry_Int(const char *name); +}; + +#define INTTYPES \ + X(Int8, int8, 4, "%4" PRId8) \ + X(Int16, int16, 6, "%6" PRId16) \ + X(Int32, int32, 11, "%11" PRId32) \ + X(Int64, int64, 21, "%21" PRId64) \ + X(UInt8, uint8, 4, "%4" PRIu8) \ + X(UInt16, uint16, 6, "%6" PRIu16) \ + X(UInt32, uint32, 11, "%11" PRIu32) \ + X(UInt64, uint64, 21, "%21" PRIu64) +#define FLOATTYPES \ + X(Float32, float, 11, "%11.3f") \ + X(Float64, double, 11, "%11.3lf") + +#define X(NAME, TYPE, MAXLEN, FMT) \ +struct MenuEntry_##NAME : MenuEntry_Int \ +{ \ + TYPE *variable; \ + TYPE lowerBound, upperBound; \ + TYPE step; \ + TriggerFunc triggerFunc; \ + const char *fmt; \ + const char **strings; \ + \ + void processInput(bool mouseOver, bool selected); \ + void getValStr(char *str, int len); \ + \ + void setStrings(const char **strings); \ + int findStringLen(void); \ + MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound, const char **strings); \ +}; +INTTYPES +#undef X + +#define X(NAME, TYPE, MAXLEN, FMT) \ +struct MenuEntry_##NAME : MenuEntry_Var \ +{ \ + TYPE *variable; \ + TYPE lowerBound, upperBound; \ + TYPE step; \ + TriggerFunc triggerFunc; \ + const char *fmt; \ + \ + void processInput(bool mouseOver, bool selected); \ + void getValStr(char *str, int len); \ + \ + MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound); \ +}; +FLOATTYPES +#undef X + +struct MenuEntry_Cmd : MenuEntry_Var +{ + TriggerFunc triggerFunc; + + void processInput(bool mouseOver, bool selected); + void getValStr(char *str, int len); + + MenuEntry_Cmd(const char *name, TriggerFunc triggerFunc); +}; + + +Menu *findMenu(const char *name); + + + +#define MUHKEYS \ + X(leftjustdown, rsLEFT) \ + X(rightjustdown, rsRIGHT) \ + X(upjustdown, rsUP) \ + X(downjustdown, rsDOWN) \ + X(pgupjustdown, rsPGUP) \ + X(pgdnjustdown, rsPGDN) + +#define MUHBUTTONS \ + X(button1justdown, 1) \ + X(button2justdown, 2) \ + X(button3justdown, 3) + +#define REPEATDELAY 700 +#define REPEATINTERVAL 50 +#define X(var, keycode) static int var; +MUHKEYS +#undef X +static int downtime; +static int repeattime; +static int lastkeydown; +static int *keyptr; + +static int buttondown[3]; +static int lastbuttondown; +static int *buttonptr; +static int button1justdown, button2justdown, button3justdown; +static float mouseX, mouseY; + +static int menuOn; +static int menuInitialized; +static int screenWidth, screenHeight; +static RwRaster *cursor, *arrow; + +static int firstBorder = 10; +static int topBorder = 40; //10; +static int leading = 4; +static int gap = 10; +static int minwidth = 100; + +void drawMouse(void); +void drawArrow(RwRect r, int direction, int style); + +Menu toplevel; +Menu *activeMenu = &toplevel; +Menu *deepestMenu = &toplevel; +Menu *mouseOverMenu; +MenuEntry *mouseOverEntry; +MenuEntry scrollUpEntry("SCROLLUP"), scrollDownEntry("SCROLLDOWN"); // dummies + + +#define KEYJUSTDOWN(k) ControlsManager.GetIsKeyboardKeyJustDown((RsKeyCodes)k) +#define KEYDOWN(k) ControlsManager.GetIsKeyboardKeyDown((RsKeyCodes)k) +#define CTRLJUSTDOWN(key) \ + ((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYJUSTDOWN((RsKeyCodes)key) || \ + (KEYJUSTDOWN(rsLCTRL) || KEYJUSTDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key)) +#define CTRLDOWN(key) ((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key)) + + +bool +isMouseInRect(RwRect r) +{ + return (mouseX >= r.x && mouseX < r.x+r.w && + mouseY >= r.y && mouseY < r.y+r.h); +} + +/* + * MenuEntry + */ + +MenuEntry::MenuEntry(const char *name) +{ + this->type = MENUEMPTY; + this->name = strdup(name); + this->next = nil; + this->menu = nil; +} + +MenuEntry_Sub::MenuEntry_Sub(const char *name, Menu *menu) +: MenuEntry(name) +{ + this->type = MENUSUB; + this->submenu = menu; +} + +MenuEntry_Var::MenuEntry_Var(const char *name, int vartype) +: MenuEntry(name) +{ + this->type = MENUVAR; + this->vartype = vartype; + this->maxvallen = 0; + this->wrapAround = false; +} + +/* + * ***************************** + * MenuEntry_Int + * ***************************** + */ + +MenuEntry_Int::MenuEntry_Int(const char *name) +: MenuEntry_Var(name, MENUVAR_INT) +{ +} + +#define X(NAME, TYPE, MAXLEN, FMT) \ +int \ +MenuEntry_##NAME::findStringLen(void){ \ + TYPE i; \ + int len, maxlen = 0; \ + for(i = this->lowerBound; i <= this->upperBound; i++){ \ + len = strlen(this->strings[i-this->lowerBound]); \ + if(len > maxlen) \ + maxlen = len; \ + } \ + return maxlen; \ +} \ +void \ +MenuEntry_##NAME::processInput(bool mouseOver, bool selected) \ +{ \ + TYPE v, oldv; \ + int overflow = 0; \ + int underflow = 0; \ + \ + v = *this->variable; \ + oldv = v; \ + \ + if((selected && leftjustdown) || (mouseOver && button3justdown)){ \ + v -= this->step; \ + if(v > oldv) \ + underflow = 1; \ + } \ + if((selected && rightjustdown) || (mouseOver && button1justdown)){ \ + v += this->step; \ + if(v < oldv) \ + overflow = 1; \ + } \ + if(this->wrapAround){ \ + if(v > this->upperBound || overflow) v = this->lowerBound; \ + if(v < this->lowerBound || underflow) v = this->upperBound; \ + }else{ \ + if(v > this->upperBound || overflow) v = this->upperBound; \ + if(v < this->lowerBound || underflow) v = this->lowerBound; \ + } \ + \ + *this->variable = v; \ + if(oldv != v && this->triggerFunc) \ + this->triggerFunc(); \ +} \ +void \ +MenuEntry_##NAME::getValStr(char *str, int len) \ +{ \ + static char tmp[20]; \ + if(this->strings){ \ + snprintf(tmp, 20, "%%%ds", this->maxvallen); \ + if(*this->variable < this->lowerBound || *this->variable > this->upperBound){ \ + snprintf(str, len, "ERROR"); \ + return; \ + } \ + snprintf(str, len, tmp, this->strings[*this->variable-this->lowerBound]); \ + }else \ + snprintf(str, len, this->fmt, *this->variable); \ +} \ +void \ +MenuEntry_##NAME::setStrings(const char **strings) \ +{ \ + this->strings = strings; \ + if(this->strings) \ + this->maxvallen = findStringLen(); \ +} \ +MenuEntry_##NAME::MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound, const char **strings) \ +: MenuEntry_Int(name) \ +{ \ + this->variable = ptr; \ + this->step = step; \ + this->lowerBound = lowerBound; \ + this->upperBound = upperBound; \ + this->triggerFunc = triggerFunc; \ + this->maxvallen = MAXLEN; \ + this->fmt = FMT; \ + this->setStrings(strings); \ +} +INTTYPES +#undef X + +/* + * ***************************** + * MenuEntry_Float + * ***************************** + */ + +#define X(NAME, TYPE, MAXLEN, FMT) \ +MenuEntry_##NAME::MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound) \ +: MenuEntry_Var(name, MENUVAR_FLOAT) \ +{ \ + this->variable = ptr; \ + this->step = step; \ + this->lowerBound = lowerBound; \ + this->upperBound = upperBound; \ + this->triggerFunc = triggerFunc; \ + this->maxvallen = MAXLEN; \ + this->fmt = FMT; \ +} \ +void \ +MenuEntry_##NAME::getValStr(char *str, int len) \ +{ \ + snprintf(str, len, this->fmt, *this->variable); \ +} \ +void \ +MenuEntry_##NAME::processInput(bool mouseOver, bool selected) \ +{ \ + float v, oldv; \ + int overflow = 0; \ + int underflow = 0; \ + \ + v = *this->variable; \ + oldv = v; \ + \ + if((selected && leftjustdown) || (mouseOver && button3justdown)){ \ + v -= this->step; \ + if(v > oldv) \ + underflow = 1; \ + } \ + if((selected && rightjustdown) || (mouseOver && button1justdown)){ \ + v += this->step; \ + if(v < oldv) \ + overflow = 1; \ + } \ + if(this->wrapAround){ \ + if(v > this->upperBound || overflow) v = this->lowerBound; \ + if(v < this->lowerBound || underflow) v = this->upperBound; \ + }else{ \ + if(v > this->upperBound || overflow) v = this->upperBound; \ + if(v < this->lowerBound || underflow) v = this->lowerBound; \ + } \ + \ + *this->variable = v; \ + if(oldv != v && this->triggerFunc) \ + this->triggerFunc(); \ +} + +FLOATTYPES +#undef X + +/* + * ***************************** + * MenuEntry_Cmd + * ***************************** + */ + +void +MenuEntry_Cmd::processInput(bool mouseOver, bool selected) +{ + // Don't execute on button3 + if(this->triggerFunc && (selected && (leftjustdown || rightjustdown) || (mouseOver && button1justdown))) + this->triggerFunc(); +} + +void +MenuEntry_Cmd::getValStr(char *str, int len) +{ + strncpy(str, "<", len); +} + +MenuEntry_Cmd::MenuEntry_Cmd(const char *name, TriggerFunc triggerFunc) +: MenuEntry_Var(name, MENUVAR_CMD) +{ + this->maxvallen = 1; + this->triggerFunc = triggerFunc; +} + + +/* + * ***************************** + * Menu + * ***************************** + */ + +void +Menu::scroll(int off) { + if(isScrollingUp && off < 0) + scrollStart += off; + if(isScrollingDown && off > 0) + scrollStart += off; + if(scrollStart < 0) scrollStart = 0; + if(scrollStart > numEntries-numVisible) scrollStart = numEntries-numVisible; +} + +void +Menu::changeSelection(int newsel){ + selection = newsel; + if(selection < 0) selection = 0; + if(selection >= numEntries) selection = numEntries-1; + if(selection < scrollStart) scrollStart = selection; + if(selection >= scrollStart+numVisible) scrollStart = selection-numVisible+1; +} + +void +Menu::changeSelection(MenuEntry *sel) +{ + MenuEntry *e; + int i = 0; + for(e = this->entries; e; e = e->next){ + if(e == sel){ + this->selection = i; + this->selectedEntry = sel; + break; + } + i++; + } +} + + + +MenuEntry* +Menu::findEntry(const char *entryname) +{ + MenuEntry *m; + for(m = this->entries; m; m = m->next) + if(strcmp(entryname, m->name) == 0) + return m; + return nil; +} + +void +Menu::insertEntrySorted(MenuEntry *entry) +{ + MenuEntry **mp; + int cmp; + for(mp = &this->entries; *mp; mp = &(*mp)->next){ + cmp = strcmp(entry->name, (*mp)->name); + if(cmp == 0) + return; + if(cmp < 0) + break; + } + entry->next = *mp; + *mp = entry; + entry->menu = this; + this->numEntries++; +} + +void +Menu::appendEntry(MenuEntry *entry) +{ + MenuEntry **mp; + for(mp = &this->entries; *mp; mp = &(*mp)->next); + entry->next = *mp; + *mp = entry; + entry->menu = this; + this->numEntries++; +} + +void +Menu::update(void) +{ + int i; + int x, y; + Pt sz; + MenuEntry *e; + int onscreen; + x = this->r.x; + y = this->r.y + 18; + int end = this->r.y+this->r.h - 18; + this->numVisible = 0; + + deepestMenu = this; + + int bottomy = end; + onscreen = 1; + i = 0; + this->maxNameWidth = 0; + this->maxValWidth = 0; + this->isScrollingUp = this->scrollStart > 0; + this->isScrollingDown = false; + this->selectedEntry = nil; + for(e = this->entries; e; e = e->next){ + sz = fontGetStringSize(e->name); + e->r.x = x; + e->r.y = y; + e->r.w = sz.x; + e->r.h = sz.y; + + if(i == this->selection) + this->selectedEntry = e; + + if(i >= this->scrollStart) + y += sz.y + leading*fontscale; + if(y >= end){ + this->isScrollingDown = true; + onscreen = 0; + }else + bottomy = y; + if(i >= this->scrollStart && onscreen) + this->numVisible++; + + if(e->type == MENUVAR){ + int valwidth = fontGetLen(((MenuEntry_Var*)e)->getValWidth()); + if(valwidth > maxValWidth) + maxValWidth = valwidth; + } + if(e->r.w > maxNameWidth) + maxNameWidth = e->r.w; + i++; + } + if(this->r.w < maxNameWidth + maxValWidth + gap*fontscale) + this->r.w = maxNameWidth + maxValWidth + gap*fontscale; + + this->scrollUpR = this->r; + this->scrollUpR.h = 16; + this->scrollDownR = this->scrollUpR; + this->scrollDownR.y = bottomy; + + // Update active submenu + if(this->selectedEntry && this->selectedEntry->type == MENUSUB){ + Menu *submenu = ((MenuEntry_Sub*)this->selectedEntry)->submenu; + submenu->r.x = this->r.x+this->r.w + 10; + submenu->r.y = this->r.y; + submenu->r.w = minwidth; // update menu will expand + submenu->r.h = this->r.h; + submenu->update(); + } +} + +void +Menu::draw(void) +{ + static char val[100]; + int i; + MenuEntry *e; + i = 0; + for(e = this->entries; e; e = e->next){ + if(i >= this->scrollStart+this->numVisible) + break; + if(i >= this->scrollStart){ + int style = MENUFONT_NORMAL; + if(i == this->selection) + style = this == activeMenu ? MENUFONT_SEL_ACTIVE : MENUFONT_SEL_INACTIVE; + if(style != MENUFONT_SEL_ACTIVE && e == mouseOverEntry) + style = MENUFONT_MOUSE; + fontPrint(e->name, e->r.x, e->r.y, style); + if(e->type == MENUVAR){ + int valw = fontGetLen(((MenuEntry_Var*)e)->getValWidth()); + ((MenuEntry_Var*)e)->getValStr(val, 100); + fontPrint(val, e->r.x+this->r.w-valw, e->r.y, style); + } + } + i++; + } + + if(this->isScrollingUp) + drawArrow(this->scrollUpR, -1, isMouseInRect(this->scrollUpR)); + if(this->isScrollingDown) + drawArrow(this->scrollDownR, 1, isMouseInRect(this->scrollDownR)); + + if(this->selectedEntry && this->selectedEntry->type == MENUSUB) + ((MenuEntry_Sub*)this->selectedEntry)->submenu->draw(); +} + +Menu::~Menu(void) +{ + MenuEntry *e, *next; + for(e = entries; e; e = next){ + next = e->next; + delete e; + } + memset(this, 0, sizeof(Menu)); +} + +Menu* +findMenu(const char *name) +{ + Menu *m; + MenuEntry *e; + char *tmppath = strdup(name); + char *next, *curname; + + curname = tmppath; + next = curname; + + m = &toplevel; + while(*next){ + curname = next; + while(*next){ + if(*next == '|'){ + *next++ = '\0'; + break; + } + next++; + } + e = m->findEntry(curname); + if(e){ + // return an error if the entry exists but isn't a menu + if(e->type != MENUSUB){ + free(tmppath); + return nil; + } + m = ((MenuEntry_Sub*)e)->submenu; + }else{ + // Create submenus that don't exist yet + Menu *submenu = new Menu(); + submenu->parent = m; + MenuEntry *me = new MenuEntry_Sub(curname, submenu); + // Don't sort submenus outside the toplevel menu + if(m == &toplevel) + m->insertEntrySorted(me); + else + m->appendEntry(me); + m = submenu; + } + } + + free(tmppath); + return m; +} + +/* + * **************** + * debug menu + * **************** + */ + +static uint8 cursorPx[] = { +#include "cursor.inc" +}; + +static uint8 arrowPx[] = { +#include "arrow.inc" +}; + +void +DebugMenuInit(void) +{ + createMenuFont(); + + RwInt32 w, h, d, flags; + RwImage *img = RwImageCreate(16, 16, 32); + assert(img); + RwImageSetPixels(img, cursorPx); + RwImageSetStride(img, RwImageGetWidth(img)*4); + RwImageFindRasterFormat(img, rwRASTERTYPETEXTURE, &w, &h, &d, &flags); + cursor = RwRasterCreate(w, h, d, flags); + cursor = RwRasterSetFromImage(cursor, img); + assert(cursor); + RwImageDestroy(img); + + img = RwImageCreate(32, 16, 32); + assert(img); + RwImageSetPixels(img, arrowPx); + RwImageSetStride(img, RwImageGetWidth(img)*4); + RwImageFindRasterFormat(img, rwRASTERTYPETEXTURE, &w, &h, &d, &flags); + arrow = RwRasterCreate(w, h, d, flags); + arrow = RwRasterSetFromImage(arrow, img); + assert(arrow); + RwImageDestroy(img); + + + menuInitialized = true; +} + +void +DebugMenuShutdown(void) +{ + if(menuInitialized){ + destroyMenuFont(); + RwRasterDestroy(cursor); + cursor = nil; + RwRasterDestroy(arrow); + arrow = nil; + + toplevel.~Menu(); + new (&toplevel) Menu(); + + activeMenu = &toplevel; + deepestMenu = &toplevel; + mouseOverMenu = nil; + mouseOverEntry = nil; + } + menuInitialized = false; +} + +void +processInput(void) +{ + int shift = KEYDOWN(rsRSHIFT) || KEYDOWN(rsLSHIFT); +#define X(var, keycode) var = KEYJUSTDOWN(keycode); + MUHKEYS +#undef X + + // Implement auto-repeat +#define X(var, keycode) \ + if(var){ \ + repeattime = downtime = CTimer::GetTimeInMilliseconds(); \ + lastkeydown = keycode; \ + keyptr = &var; \ + } + MUHKEYS +#undef X + if(lastkeydown){ + if(KEYDOWN(lastkeydown)){ + int curtime = CTimer::GetTimeInMilliseconds(); + if(curtime - downtime > REPEATDELAY){ + if(curtime - repeattime > REPEATINTERVAL){ + repeattime = curtime; + *keyptr = 1; + } + } + }else{ + lastkeydown = 0; + } + } + + // Also for mouse buttons +#define X(var, num) \ + if(var){ \ + repeattime = downtime = CTimer::GetTimeInMilliseconds(); \ + lastbuttondown = num; \ + buttonptr = &var; \ + } + MUHBUTTONS +#undef X + if(lastbuttondown){ + if(buttondown[lastbuttondown-1]){ + int curtime = CTimer::GetTimeInMilliseconds(); + if(curtime - downtime > REPEATDELAY){ + if(curtime - repeattime > REPEATINTERVAL){ + repeattime = curtime; + *buttonptr = 1; + } + } + }else{ + lastbuttondown = 0; + } + } + + // Walk through all visible menus and figure out which one the mouse is over + mouseOverMenu = nil; + mouseOverEntry = nil; + Menu *menu; + for(menu = deepestMenu; menu; menu = menu->parent) + if(isMouseInRect(menu->r)){ + mouseOverMenu = menu; + break; + } + if(mouseOverMenu){ + // Walk all visibile entries and figure out which one the mouse is over + MenuEntry *e; + int i = 0; + for(e = mouseOverMenu->entries; e; e = e->next){ + if(i >= mouseOverMenu->scrollStart+mouseOverMenu->numVisible) + break; + if(i >= mouseOverMenu->scrollStart){ + RwRect r = e->r; + r.w = mouseOverMenu->r.w; // span the whole menu + if(isMouseInRect(r)){ + mouseOverEntry = e; + break; + } + } + i++; + } + if(mouseOverMenu->isScrollingUp && isMouseInRect(mouseOverMenu->scrollUpR)){ + mouseOverEntry = &scrollUpEntry; + mouseOverEntry->r = mouseOverMenu->scrollUpR; + mouseOverEntry->menu = mouseOverMenu; + mouseOverEntry->type = MENUSCROLL; + } + if(mouseOverMenu->isScrollingDown && isMouseInRect(mouseOverMenu->scrollDownR)){ + mouseOverEntry = &scrollDownEntry; + mouseOverEntry->r = mouseOverMenu->scrollDownR; + mouseOverEntry->menu = mouseOverMenu; + mouseOverEntry->type = MENUSCROLL; + } + } + + if(pgupjustdown) + activeMenu->scroll(shift ? -5 : -1); + if(pgdnjustdown) + activeMenu->scroll(shift ? 5 : 1); + if(downjustdown) + activeMenu->changeSelection(activeMenu->selection + (shift ? 5 : 1)); + if(upjustdown) + activeMenu->changeSelection(activeMenu->selection - (shift ? 5 : 1)); + + if(CPad::NewMouseControllerState.WHEELUP){ + if(mouseOverMenu) + activeMenu = mouseOverMenu; + activeMenu->scroll(shift ? -5 : -1); + } + if(CPad::NewMouseControllerState.WHEELDN){ + if(mouseOverMenu) + activeMenu = mouseOverMenu; + activeMenu->scroll(shift ? 5 : 1); + } + + if(mouseOverEntry == &scrollUpEntry){ + if(button1justdown){ + activeMenu = mouseOverEntry->menu; + activeMenu->scroll(shift ? -5 : -1); + } + } + if(mouseOverEntry == &scrollDownEntry){ + if(button1justdown){ + activeMenu = mouseOverEntry->menu; + activeMenu->scroll(shift ? 5 : 1); + } + } + + // Have to call this before processInput below because menu entry can change + if((button1justdown || button3justdown) && mouseOverEntry){ + activeMenu = mouseOverEntry->menu; + activeMenu->changeSelection(mouseOverEntry); + } + if(KEYJUSTDOWN(rsENTER)){ + if(activeMenu->selectedEntry && activeMenu->selectedEntry->type == MENUSUB) + activeMenu = ((MenuEntry_Sub*)activeMenu->selectedEntry)->submenu; + }else if(KEYJUSTDOWN(rsBACKSP)){ + if(activeMenu->parent) + activeMenu = activeMenu->parent; + }else{ + if(mouseOverEntry && mouseOverEntry->type == MENUVAR) + ((MenuEntry_Var*)mouseOverEntry)->processInput(true, mouseOverEntry == activeMenu->selectedEntry); + if(activeMenu->selectedEntry && activeMenu->selectedEntry->type == MENUVAR && + mouseOverEntry != activeMenu->selectedEntry) + ((MenuEntry_Var*)activeMenu->selectedEntry)->processInput(false, true); + } +} + +void +updateMouse(void) +{ + CPad *pad = CPad::GetPad(0); + int dirX = 1; + int dirY = 1; + + if(MousePointerStateHelper.bInvertHorizontally) dirX = -1; + if(MousePointerStateHelper.bInvertVertically) dirY = -1; + + mouseX += pad->NewMouseControllerState.x*dirX; + mouseY += pad->NewMouseControllerState.y*dirY; + + if(mouseX < 0.0f) mouseX = 0.0f; + if(mouseY < 0.0f) mouseY = 0.0f; + if(mouseX >= screenWidth) mouseX = screenWidth; + if(mouseY >= screenHeight) mouseY = screenHeight; + + button1justdown = pad->NewMouseControllerState.LMB && !pad->OldMouseControllerState.LMB; + button2justdown = pad->NewMouseControllerState.MMB && !pad->OldMouseControllerState.MMB; + button3justdown = pad->NewMouseControllerState.RMB && !pad->OldMouseControllerState.RMB; + buttondown[0] = pad->NewMouseControllerState.LMB; + buttondown[1] = pad->NewMouseControllerState.MMB; + buttondown[2] = pad->NewMouseControllerState.RMB; + + // Zero the mouse position so the camera won't move + pad->NewMouseControllerState.x = 0.0f; + pad->NewMouseControllerState.y = 0.0f; +} + +void +DebugMenuProcess(void) +{ + // We only process some input here + + CPad *pad = CPad::GetPad(0); + if(CTRLJUSTDOWN('M')) + menuOn = !menuOn; + if(!menuOn) + return; + + pad->DisablePlayerControls = 1; + // TODO: this could happen earlier + if(!menuInitialized) + DebugMenuInit(); + updateMouse(); + +} + +void +DebugMenuRender(void) +{ + if(!menuOn) + return; + + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, 0); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, 0); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, 0); + RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE); + + RwCamera *cam = RwCameraGetCurrentCamera(); + screenWidth = RwRasterGetWidth(RwCameraGetRaster(cam)); + screenHeight = RwRasterGetHeight(RwCameraGetRaster(cam)); + +// if(screenHeight > 1080) +// fontscale = 2; +// else + fontscale = 1; + + Pt sz; + sz = fontPrint("Debug Menu", firstBorder*fontscale+30, topBorder, 0); + + toplevel.r.x = firstBorder*fontscale; + toplevel.r.y = topBorder + sz.y + 10; + toplevel.r.w = minwidth; // update menu will expand + toplevel.r.h = screenHeight - 10 - toplevel.r.y; + toplevel.update(); + toplevel.draw(); + processInput(); + RtCharsetBufferFlush(); + + drawMouse(); +} + + + +void +drawArrow(RwRect r, int direction, int style) +{ + static RwImVertexIndex indices[] = { 0, 1, 2, 2, 1, 3 }; + static RwIm2DVertex arrowVerts[4]; + + RwCamera *cam = RwCameraGetCurrentCamera(); + float recipz = 1.0f/RwCameraGetNearClipPlane(cam); + + int width = RwRasterGetWidth(arrow); + int height = RwRasterGetHeight(arrow); + + int left = r.x + (r.w - width)/2; + int right = left + width; + int top = r.y; + int bottom = r.y+r.h; + + float umin = HALFPX / width; + float vmin = HALFPX / height; + float umax = (width + HALFPX) / width; + float vmax = (height + HALFPX) / height; + if(direction < 0){ + vmin = (height - HALFPX) / height; + vmax = -HALFPX / height; + } + + if(style){ + RwIm2DVertexSetScreenX(&arrowVerts[0], r.x); + RwIm2DVertexSetScreenY(&arrowVerts[0], r.y-1); + RwIm2DVertexSetScreenZ(&arrowVerts[0], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&arrowVerts[0], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&arrowVerts[0], recipz); + RwIm2DVertexSetIntRGBA(&arrowVerts[0], 132, 132, 132, 255); + + RwIm2DVertexSetScreenX(&arrowVerts[1], r.x+r.w); + RwIm2DVertexSetScreenY(&arrowVerts[1], r.y-1); + RwIm2DVertexSetScreenZ(&arrowVerts[1], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&arrowVerts[1], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&arrowVerts[1], recipz); + RwIm2DVertexSetIntRGBA(&arrowVerts[1], 132, 132, 132, 255); + + RwIm2DVertexSetScreenX(&arrowVerts[2], r.x); + RwIm2DVertexSetScreenY(&arrowVerts[2], r.y+r.h+1); + RwIm2DVertexSetScreenZ(&arrowVerts[2], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&arrowVerts[2], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&arrowVerts[2], recipz); + RwIm2DVertexSetIntRGBA(&arrowVerts[2], 132, 132, 132, 255); + + RwIm2DVertexSetScreenX(&arrowVerts[3], r.x+r.w); + RwIm2DVertexSetScreenY(&arrowVerts[3], r.y+r.h+1); + RwIm2DVertexSetScreenZ(&arrowVerts[3], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&arrowVerts[3], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&arrowVerts[3], recipz); + RwIm2DVertexSetIntRGBA(&arrowVerts[3], 132, 132, 132, 255); + + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, arrowVerts, 4, indices, 6); + } + + + RwIm2DVertexSetScreenX(&arrowVerts[0], left); + RwIm2DVertexSetScreenY(&arrowVerts[0], top); + RwIm2DVertexSetScreenZ(&arrowVerts[0], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&arrowVerts[0], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&arrowVerts[0], recipz); + RwIm2DVertexSetIntRGBA(&arrowVerts[0], 255, 255, 255, 255); + RwIm2DVertexSetU(&arrowVerts[0], umin, recipz); + RwIm2DVertexSetV(&arrowVerts[0], vmin, recipz); + + RwIm2DVertexSetScreenX(&arrowVerts[1], right); + RwIm2DVertexSetScreenY(&arrowVerts[1], top); + RwIm2DVertexSetScreenZ(&arrowVerts[1], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&arrowVerts[1], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&arrowVerts[1], recipz); + RwIm2DVertexSetIntRGBA(&arrowVerts[1], 255, 255, 255, 255); + RwIm2DVertexSetU(&arrowVerts[1], umax, recipz); + RwIm2DVertexSetV(&arrowVerts[1], vmin, recipz); + + RwIm2DVertexSetScreenX(&arrowVerts[2], left); + RwIm2DVertexSetScreenY(&arrowVerts[2], bottom); + RwIm2DVertexSetScreenZ(&arrowVerts[2], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&arrowVerts[2], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&arrowVerts[2], recipz); + RwIm2DVertexSetIntRGBA(&arrowVerts[2], 255, 255, 255, 255); + RwIm2DVertexSetU(&arrowVerts[2], umin, recipz); + RwIm2DVertexSetV(&arrowVerts[2], vmax, recipz); + + RwIm2DVertexSetScreenX(&arrowVerts[3], right); + RwIm2DVertexSetScreenY(&arrowVerts[3], bottom); + RwIm2DVertexSetScreenZ(&arrowVerts[3], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&arrowVerts[3], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&arrowVerts[3], recipz); + RwIm2DVertexSetIntRGBA(&arrowVerts[3], 255, 255, 255, 255); + RwIm2DVertexSetU(&arrowVerts[3], umax, recipz); + RwIm2DVertexSetV(&arrowVerts[3], vmax, recipz); + + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, arrow); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, arrowVerts, 4, indices, 6); +} + +void +drawMouse(void) +{ + static RwImVertexIndex indices[] = { 0, 1, 2, 2, 1, 3 }; + static RwIm2DVertex vertices[4]; + RwIm2DVertex *vert; + RwCamera *cam; + cam = RwCameraGetCurrentCamera(); + float x = mouseX; + float y = mouseY; + float w = RwRasterGetWidth(cursor); + float h = RwRasterGetHeight(cursor); + float recipz = 1.0f/RwCameraGetNearClipPlane(cam); + + float umin = HALFPX / w; + float vmin = HALFPX / h; + float umax = (w + HALFPX) / w; + float vmax = (h + HALFPX) / h; + + vert = vertices; + RwIm2DVertexSetScreenX(vert, x); + RwIm2DVertexSetScreenY(vert, y); + RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(vert, recipz); + RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255); + RwIm2DVertexSetU(vert, umin, recipz); + RwIm2DVertexSetV(vert, vmin, recipz); + vert++; + + RwIm2DVertexSetScreenX(vert, x+w); + RwIm2DVertexSetScreenY(vert, y); + RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(vert, recipz); + RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255); + RwIm2DVertexSetU(vert, umax, recipz); + RwIm2DVertexSetV(vert, vmin, recipz); + vert++; + + RwIm2DVertexSetScreenX(vert, x); + RwIm2DVertexSetScreenY(vert, y+h); + RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(vert, recipz); + RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255); + RwIm2DVertexSetU(vert, umin, recipz); + RwIm2DVertexSetV(vert, vmax, recipz); + vert++; + + RwIm2DVertexSetScreenX(vert, x+w); + RwIm2DVertexSetScreenY(vert, y+h); + RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(vert, recipz); + RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255); + RwIm2DVertexSetU(vert, umax, recipz); + RwIm2DVertexSetV(vert, vmax, recipz); + vert++; + + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, cursor); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, vertices, 4, indices, 6); +} + + + + +/* + * Generate interfaces + */ + + +#define X(NAME, TYPE, unused1, unused2) \ +MenuEntry* \ +DebugMenuAdd##NAME(const char *path, const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound, const char **strings) \ +{ \ + Menu *m = findMenu(path); \ + if(m == nil) \ + return nil; \ + MenuEntry *e = new MenuEntry_##NAME(name, ptr, triggerFunc, step, lowerBound, upperBound, strings); \ + m->appendEntry(e); \ + return e; \ +} +INTTYPES +#undef X + +#define X(NAME, TYPE, unused1, unused2) \ +MenuEntry* \ +DebugMenuAdd##NAME(const char *path, const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound) \ +{ \ + Menu *m = findMenu(path); \ + if(m == nil) \ + return nil; \ + MenuEntry *e = new MenuEntry_##NAME(name, ptr, triggerFunc, step, lowerBound, upperBound); \ + m->appendEntry(e); \ + return e; \ +} +FLOATTYPES +#undef X + +MenuEntry* \ +DebugMenuAddCmd(const char *path, const char *name, TriggerFunc triggerFunc) +{ + Menu *m = findMenu(path); + if(m == nil) + return nil; + MenuEntry *e = new MenuEntry_Cmd(name, triggerFunc); + m->appendEntry(e); + return e; +} + +void +DebugMenuEntrySetWrap(MenuEntry *e, bool wrap) +{ + if(e && e->type == MENUVAR) + ((MenuEntry_Var*)e)->wrapAround = wrap; +} + +void +DebugMenuEntrySetStrings(MenuEntry *e, const char **strings) +{ + if(e && e->type == MENUVAR_INT) + ((MenuEntry_Int*)e)->setStrings(strings); +} + +void +DebugMenuEntrySetAddress(MenuEntry *e, void *addr) +{ + if(e && e->type == MENUVAR){ + MenuEntry_Var *ev = (MenuEntry_Var*)e; + // HACK - we know the variable field is at the same address + // for all int/float classes. let's hope it stays that way. + if(ev->vartype = MENUVAR_INT) + ((MenuEntry_Int32*)e)->variable = (int32*)addr; + else if(ev->vartype = MENUVAR_FLOAT) + ((MenuEntry_Float32*)e)->variable = (float*)addr; + } +} +#endif \ No newline at end of file diff --git a/src/miami/extras/debugmenu.h b/src/miami/extras/debugmenu.h new file mode 100644 index 00000000..8aa15512 --- /dev/null +++ b/src/miami/extras/debugmenu.h @@ -0,0 +1,204 @@ +#pragma once + +#ifdef DEBUGMENU + +// Tweaking stuff for debugmenu +#define TWEAKPATH ___tw___TWEAKPATH +#define SETTWEAKPATH(path) static const char *___tw___TWEAKPATH = path; +#define TWEAKFUNC(v) static CTweakFunc CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), TWEAKPATH); +#define TWEAKFUNCN(v, name) static CTweakFunc CONCAT(___tw___tweak, __COUNTER__)(&v, name, TWEAKPATH); +#define TWEAKBOOL(v) static CTweakBool CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), TWEAKPATH); +#define TWEAKBOOLN(v, name) static CTweakBool CONCAT(___tw___tweak, __COUNTER__)(&v, name, TWEAKPATH); +#define TWEAKINT32(v, lower, upper, step) static CTweakInt32 CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), lower, upper, step, TWEAKPATH); +#define TWEAKINT32N(v, lower, upper, step, name) static CTweakInt32 CONCAT(___tw___tweak, __COUNTER__)(&v, name, lower, upper, step, TWEAKPATH); +#define TWEAKUINT32(v, lower, upper, step) static CTweakUInt32 CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), lower, upper, step, TWEAKPATH); +#define TWEAKUINT32N(v, lower, upper, step, name) static CTweakUInt32 CONCAT(___tw___tweak, __COUNTER__)(&v, name, lower, upper, step, TWEAKPATH); +#define TWEAKINT16(v, lower, upper, step) static CTweakInt16 CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), lower, upper, step, TWEAKPATH); +#define TWEAKINT16N(v, lower, upper, step, name) static CTweakInt16 CONCAT(___tw___tweak, __COUNTER__)(&v, name, lower, upper, step, TWEAKPATH); +#define TWEAKUINT16(v, lower, upper, step) static CTweakUInt16 CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), lower, upper, step, TWEAKPATH); +#define TWEAKUINT16N(v, lower, upper, step, name) static CTweakUInt16 CONCAT(___tw___tweak, __COUNTER__)(&v, name, lower, upper, step, TWEAKPATH); +#define TWEAKINT8(v, lower, upper, step) static CTweakInt8 CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), lower, upper, step, TWEAKPATH); +#define TWEAKINT8N(v, lower, upper, step, name) static CTweakInt8 CONCAT(___tw___tweak, __COUNTER__)(&v, name, lower, upper, step, TWEAKPATH); +#define TWEAKUINT8(v, lower, upper, step) static CTweakUInt8 CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), lower, upper, step, TWEAKPATH); +#define TWEAKUINT8N(v, lower, upper, step, name) static CTweakUInt8 CONCAT(___tw___tweak, __COUNTER__)(&v, name, lower, upper, step, TWEAKPATH); +#define TWEAKFLOAT(v, lower, upper, step) static CTweakFloat CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), lower, upper, step, TWEAKPATH); +#define TWEAKFLOATN(v, lower, upper, step, name) static CTweakFloat CONCAT(___tw___tweak, __COUNTER__)(&v, name, lower, upper, step, TWEAKPATH); +#define TWEAKSWITCH(v, lower, upper, str, f) static CTweakSwitch CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), lower, upper, str, f, TWEAKPATH); +#define TWEAKSWITCHN(v, lower, upper, str, f, name) static CTweakSwitch CONCAT(___tw___tweak, __COUNTER__)(&v, name, lower, upper, str, f, TWEAKPATH); + +// interface +class CTweakVar +{ +public: + virtual void AddDBG(const char *path) = 0; +}; + +class CTweakVars +{ +public: + static void Add(CTweakVar *var); + static void AddDBG(const char *path); +}; + +class CTweakFunc : public CTweakVar +{ + const char *m_pPath, *m_pVarName; + void (*m_pFunc)(); +public: + CTweakFunc(void (*pFunc)(), const char *strName, const char *strPath) : + m_pPath(strPath), m_pVarName(strName), m_pFunc(pFunc) + { + CTweakVars::Add(this); + } + + void AddDBG(const char *path); +}; + +class CTweakBool : public CTweakVar +{ + const char *m_pPath, *m_pVarName; + bool *m_pBoolVar; +public: + CTweakBool(bool *pBool, const char *strName, const char *strPath) : + m_pPath(strPath), m_pVarName(strName), m_pBoolVar(pBool) + { + CTweakVars::Add(this); + } + + void AddDBG(const char *path); +}; + +class CTweakSwitch : public CTweakVar +{ + const char *m_pPath, *m_pVarName; + void *m_pIntVar; + int32 m_nMin, m_nMax; + const char **m_aStr; + void (*m_pFunc)(); +public: + CTweakSwitch(void *pInt, const char *strName, int32 nMin, int32 nMax, const char **aStr, + void (*pFunc)(), const char *strPath) + : m_pPath(strPath), m_pVarName(strName), m_pIntVar(pInt), m_nMin(nMin), m_nMax(nMax), + m_aStr(aStr) + { + CTweakVars::Add(this); + } + + void AddDBG(const char *path); +}; + +#define _TWEEKCLASS(name, type) \ + class name : public CTweakVar \ + { \ + public: \ + const char *m_pPath, *m_pVarName; \ + type *m_pIntVar, m_nLoawerBound, m_nUpperBound, m_nStep; \ + \ + name(type *pInt, const char *strName, type nLower, type nUpper, type nStep, \ + const char *strPath) \ + : m_pPath(strPath), m_pVarName(strName), m_pIntVar(pInt), \ + m_nLoawerBound(nLower), m_nUpperBound(nUpper), m_nStep(nStep) \ + \ + { \ + CTweakVars::Add(this); \ + } \ + \ + void AddDBG(const char *path); \ + }; + +_TWEEKCLASS(CTweakInt8, int8); +_TWEEKCLASS(CTweakUInt8, uint8); +_TWEEKCLASS(CTweakInt16, int16); +_TWEEKCLASS(CTweakUInt16, uint16); +_TWEEKCLASS(CTweakInt32, int32); +_TWEEKCLASS(CTweakUInt32, uint32); +_TWEEKCLASS(CTweakFloat, float); + +#undef _TWEEKCLASS + +typedef void (*TriggerFunc)(void); + +struct Menu; + +struct MenuEntry +{ + int type; + const char *name; + MenuEntry *next; + RwRect r; + Menu *menu; + + MenuEntry(const char *name); + virtual ~MenuEntry(void) { free((void*)name); } +}; + +typedef MenuEntry DebugMenuEntry; + +MenuEntry *DebugMenuAddInt8(const char *path, const char *name, int8 *ptr, TriggerFunc triggerFunc, int8 step, int8 lowerBound, int8 upperBound, const char **strings); +MenuEntry *DebugMenuAddInt16(const char *path, const char *name, int16 *ptr, TriggerFunc triggerFunc, int16 step, int16 lowerBound, int16 upperBound, const char **strings); +MenuEntry *DebugMenuAddInt32(const char *path, const char *name, int32 *ptr, TriggerFunc triggerFunc, int32 step, int32 lowerBound, int32 upperBound, const char **strings); +MenuEntry *DebugMenuAddInt64(const char *path, const char *name, int64 *ptr, TriggerFunc triggerFunc, int64 step, int64 lowerBound, int64 upperBound, const char **strings); +MenuEntry *DebugMenuAddUInt8(const char *path, const char *name, uint8 *ptr, TriggerFunc triggerFunc, uint8 step, uint8 lowerBound, uint8 upperBound, const char **strings); +MenuEntry *DebugMenuAddUInt16(const char *path, const char *name, uint16 *ptr, TriggerFunc triggerFunc, uint16 step, uint16 lowerBound, uint16 upperBound, const char **strings); +MenuEntry *DebugMenuAddUInt32(const char *path, const char *name, uint32 *ptr, TriggerFunc triggerFunc, uint32 step, uint32 lowerBound, uint32 upperBound, const char **strings); +MenuEntry *DebugMenuAddUInt64(const char *path, const char *name, uint64 *ptr, TriggerFunc triggerFunc, uint64 step, uint64 lowerBound, uint64 upperBound, const char **strings); +MenuEntry *DebugMenuAddFloat32(const char *path, const char *name, float *ptr, TriggerFunc triggerFunc, float step, float lowerBound, float upperBound); +MenuEntry *DebugMenuAddFloat64(const char *path, const char *name, double *ptr, TriggerFunc triggerFunc, double step, double lowerBound, double upperBound); +MenuEntry *DebugMenuAddCmd(const char *path, const char *name, TriggerFunc triggerFunc); +void DebugMenuEntrySetWrap(MenuEntry *e, bool wrap); +void DebugMenuEntrySetStrings(MenuEntry *e, const char **strings); +void DebugMenuEntrySetAddress(MenuEntry *e, void *addr); +void DebugMenuInit(void); +void DebugMenuShutdown(void); +void DebugMenuProcess(void); +void DebugMenuRender(void); + + +// Some overloads for simplicity +inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int8_t *ptr, TriggerFunc triggerFunc, int8_t step, int8_t lowerBound, int8_t upperBound, const char **strings) +{ return DebugMenuAddInt8(path, name, (int8*)ptr, triggerFunc, step, lowerBound, upperBound, strings); } +inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int16_t *ptr, TriggerFunc triggerFunc, int16_t step, int16_t lowerBound, int16_t upperBound, const char **strings) +{ return DebugMenuAddInt16(path, name, (int16*)ptr, triggerFunc, step, lowerBound, upperBound, strings); } +inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int32_t *ptr, TriggerFunc triggerFunc, int32_t step, int32_t lowerBound, int32_t upperBound, const char **strings) +{ return DebugMenuAddInt32(path, name, (int32*)ptr, triggerFunc, step, lowerBound, upperBound, strings); } +inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int64_t *ptr, TriggerFunc triggerFunc, int64_t step, int64_t lowerBound, int64_t upperBound, const char **strings) +{ return DebugMenuAddInt64(path, name, (int64*)ptr, triggerFunc, step, lowerBound, upperBound, strings); } +inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint8_t *ptr, TriggerFunc triggerFunc, uint8_t step, uint8_t lowerBound, uint8_t upperBound, const char **strings) +{ return DebugMenuAddUInt8(path, name, (uint8*)ptr, triggerFunc, step, lowerBound, upperBound, strings); } +inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint16_t *ptr, TriggerFunc triggerFunc, uint16_t step, uint16_t lowerBound, uint16_t upperBound, const char **strings) +{ return DebugMenuAddUInt16(path, name, (uint16*)ptr, triggerFunc, step, lowerBound, upperBound, strings); } +inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint32_t *ptr, TriggerFunc triggerFunc, uint32_t step, uint32_t lowerBound, uint32_t upperBound, const char **strings) +{ return DebugMenuAddUInt32(path, name, (uint32*)ptr, triggerFunc, step, lowerBound, upperBound, strings); } +inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint64_t *ptr, TriggerFunc triggerFunc, uint64_t step, uint64_t lowerBound, uint64_t upperBound, const char **strings) +{ return DebugMenuAddUInt64(path, name, (uint64*)ptr, triggerFunc, step, lowerBound, upperBound, strings); } +inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, float *ptr, TriggerFunc triggerFunc, float step, float lowerBound, float upperBound) +{ return DebugMenuAddFloat32(path, name, ptr, triggerFunc, step, lowerBound, upperBound); } +inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, double *ptr, TriggerFunc triggerFunc, double step, double lowerBound, double upperBound) +{ return DebugMenuAddFloat64(path, name, ptr, triggerFunc, step, lowerBound, upperBound); } + +inline DebugMenuEntry *DebugMenuAddVarBool32(const char *path, const char *name, int32_t *ptr, TriggerFunc triggerFunc) +{ + static const char *boolstr[] = { "Off", "On" }; + DebugMenuEntry *e = DebugMenuAddVar(path, name, ptr, triggerFunc, 1, 0, 1, boolstr); + DebugMenuEntrySetWrap(e, true); + return e; +} +inline DebugMenuEntry *DebugMenuAddVarBool16(const char *path, const char *name, int16_t *ptr, TriggerFunc triggerFunc) +{ + static const char *boolstr[] = { "Off", "On" }; + DebugMenuEntry *e = DebugMenuAddVar(path, name, ptr, triggerFunc, 1, 0, 1, boolstr); + DebugMenuEntrySetWrap(e, true); + return e; +} +inline DebugMenuEntry *DebugMenuAddVarBool8(const char *path, const char *name, int8_t *ptr, TriggerFunc triggerFunc) +{ + static const char *boolstr[] = { "Off", "On" }; + DebugMenuEntry *e = DebugMenuAddVar(path, name, ptr, triggerFunc, 1, 0, 1, boolstr); + DebugMenuEntrySetWrap(e, true); + return e; +} +inline DebugMenuEntry *DebugMenuAddVarBool8(const char *path, const char *name, bool *ptr, TriggerFunc triggerFunc) +{ + return DebugMenuAddVarBool8(path, name, (int8_t*)ptr, triggerFunc); +} +#endif \ No newline at end of file diff --git a/src/miami/extras/frontendoption.cpp b/src/miami/extras/frontendoption.cpp new file mode 100644 index 00000000..2660e75e --- /dev/null +++ b/src/miami/extras/frontendoption.cpp @@ -0,0 +1,170 @@ +#include "common.h" + +#ifdef CUSTOM_FRONTEND_OPTIONS +#include "Frontend.h" +#include "Text.h" + +int lastOgScreen = MENUPAGES; // means no new pages + +int numCustomFrontendOptions = 0; +int numCustomFrontendScreens = 0; + +int optionCursor = -2; +int currentMenu; +bool optionOverwrite = false; + +void GoBack() +{ + FrontEndMenuManager.SwitchToNewScreen(-1); +} + +uint8 +GetNumberOfMenuOptions(int screen) +{ + uint8 Rows = 0; + for (int i = 0; i < NUM_MENUROWS; i++) { + if (aScreens[screen].m_aEntries[i].m_Action == MENUACTION_NOTHING) + break; + + ++Rows; + } + return Rows; +} + +uint8 +GetLastMenuScreen() +{ + int8 page = -1; + for (int i = 0; i < MENUPAGES; i++) { + if (strcmp(aScreens[i].m_ScreenName, "") == 0 && aScreens[i].m_PreviousPage == MENUPAGE_NONE) + break; + + ++page; + } + return page; +} + +int8 RegisterNewScreen(const char *name, int prevPage, ReturnPrevPageFunc returnPrevPageFunc) +{ + if (lastOgScreen == MENUPAGES) + lastOgScreen = GetLastMenuScreen(); + + numCustomFrontendScreens++; + int id = lastOgScreen + numCustomFrontendScreens; + assert(id < MENUPAGES && "No room for new custom frontend screens! Increase MENUPAGES"); + strncpy(aScreens[id].m_ScreenName, name, 8); + aScreens[id].m_PreviousPage = prevPage; + aScreens[id].returnPrevPageFunc = returnPrevPageFunc; + return id; +} + +int8 RegisterNewOption() +{ + numCustomFrontendOptions++; + uint8 numOptions = GetNumberOfMenuOptions(currentMenu); + uint8 curIdx; + if (optionCursor < 0) { + optionCursor = curIdx = numOptions + optionCursor + 1; + } else + curIdx = optionCursor; + + if (!optionOverwrite) { + if (aScreens[currentMenu].m_aEntries[curIdx].m_Action != MENUACTION_NOTHING) { + for (int i = numOptions - 1; i >= curIdx; i--) { + memcpy(&aScreens[currentMenu].m_aEntries[i + 1], &aScreens[currentMenu].m_aEntries[i], sizeof(CMenuScreenCustom::CMenuEntry)); + } + } + } + optionCursor++; + return curIdx; +} + +void FrontendOptionSetCursor(int screen, int8 option, bool overwrite) +{ + currentMenu = screen; + optionCursor = option; + optionOverwrite = overwrite; +} + +void FrontendOptionAddBuiltinAction(const char* gxtKey, uint16 x, uint16 y, uint8 align, int action, int targetMenu, int saveSlot) { + int8 screenOptionOrder = RegisterNewOption(); + + CMenuScreenCustom::CMenuEntry &option = aScreens[currentMenu].m_aEntries[screenOptionOrder]; + + // We can't use custom text on those :shrug: + switch (action) { + case MENUACTION_SCREENRES: + strcpy(option.m_EntryName, "FED_RES"); + break; + case MENUACTION_AUDIOHW: + strcpy(option.m_EntryName, "FEA_3DH"); + break; + default: + strncpy(option.m_EntryName, gxtKey, 8); + break; + } + option.m_X = x; + option.m_Y = y; + option.m_Align = align; + option.m_Action = action; + option.m_SaveSlot = saveSlot; + option.m_TargetMenu = targetMenu; +} + +void FrontendOptionAddSelect(const char* gxtKey, uint16 x, uint16 y, uint8 align, const char** rightTexts, int8 numRightTexts, int8 *var, bool onlyApplyOnEnter, ChangeFunc changeFunc, const char* saveCat, const char* saveName, bool disableIfGameLoaded) +{ + int8 screenOptionOrder = RegisterNewOption(); + + CMenuScreenCustom::CMenuEntry &option = aScreens[currentMenu].m_aEntries[screenOptionOrder]; + option.m_Action = MENUACTION_CFO_SELECT; + option.m_X = x; + option.m_Y = y; + option.m_Align = align; + strncpy(option.m_EntryName, gxtKey, 8); + option.m_CFOSelect = new CCFOSelect(); + option.m_CFOSelect->rightTexts = (char**)malloc(numRightTexts * sizeof(char*)); + memcpy(option.m_CFOSelect->rightTexts, rightTexts, numRightTexts * sizeof(char*)); + option.m_CFOSelect->numRightTexts = numRightTexts; + option.m_CFOSelect->value = var; + if (var) { + option.m_CFOSelect->displayedValue = *var; + option.m_CFOSelect->lastSavedValue = *var; + } + option.m_CFOSelect->saveCat = saveCat; + option.m_CFOSelect->save = saveName; + option.m_CFOSelect->onlyApplyOnEnter = onlyApplyOnEnter; + option.m_CFOSelect->changeFunc = changeFunc; + option.m_CFOSelect->disableIfGameLoaded = disableIfGameLoaded; +} + +void FrontendOptionAddDynamic(const char* gxtKey, uint16 x, uint16 y, uint8 align, DrawFunc drawFunc, int8 *var, ButtonPressFunc buttonPressFunc, const char* saveCat, const char* saveName) +{ + int8 screenOptionOrder = RegisterNewOption(); + + CMenuScreenCustom::CMenuEntry &option = aScreens[currentMenu].m_aEntries[screenOptionOrder]; + option.m_Action = MENUACTION_CFO_DYNAMIC; + option.m_X = x; + option.m_Y = y; + option.m_Align = align; + strncpy(option.m_EntryName, gxtKey, 8); + option.m_CFODynamic = new CCFODynamic(); + option.m_CFODynamic->drawFunc = drawFunc; + option.m_CFODynamic->buttonPressFunc = buttonPressFunc; + option.m_CFODynamic->value = var; + option.m_CFODynamic->saveCat = saveCat; + option.m_CFODynamic->save = saveName; +} + +// lineHeight = 0 means game will use MENU_DEFAULT_LINE_HEIGHT +uint8 FrontendScreenAdd(const char* gxtKey, int prevPage, int lineHeight, bool showLeftRightHelper, ReturnPrevPageFunc returnPrevPageFunc) { + + uint8 screenOrder = RegisterNewScreen(gxtKey, prevPage, returnPrevPageFunc); + + CCustomScreenLayout *screen = new CCustomScreenLayout(); + aScreens[screenOrder].layout = screen; + screen->lineHeight = lineHeight; + screen->showLeftRightHelper = showLeftRightHelper; + + return screenOrder; +} +#endif diff --git a/src/miami/extras/frontendoption.h b/src/miami/extras/frontendoption.h new file mode 100644 index 00000000..db1b9021 --- /dev/null +++ b/src/miami/extras/frontendoption.h @@ -0,0 +1,88 @@ +#pragma once +#include "common.h" + +#ifdef CUSTOM_FRONTEND_OPTIONS + +// ! There are 2 ways to use CFO, +// 1st; by adding a new option to the array in MenuScreensCustom.cpp and passing attributes/CBs to it +// 2nd; by calling the functions listed at the bottom of this file. + +// -- Option types +// +// Static/select: You allocate the variable, pass it to function and game sets it from user input among the strings given to function, +// optionally you can add post-change event via ChangeFunc(only called on enter if onlyApplyOnEnter set, or set immediately) +// You can store the option in an INI file if you pass the key(as a char array) to corresponding parameter. +// +// Dynamic: Passing variable to function is only needed if you want to store it, otherwise you should do +// all the operations with ButtonPressFunc, this includes allocating the variable. +// Left-side text is passed while creating and static, but ofc right-side text is dynamic - +// you should return it in DrawFunc, which is called on every draw. +// +// Built-in action: As the name suggests, any action that game has built-in. But as an extra you can set the option text, + +// -- Returned via ButtonPressFunc() action param. +#define FEOPTION_ACTION_LEFT 0 +#define FEOPTION_ACTION_RIGHT 1 +#define FEOPTION_ACTION_SELECT 2 +#define FEOPTION_ACTION_FOCUSLOSS 3 + +// -- Callbacks + +// pretty much in everything I guess, and optional in all of them +typedef void (*ReturnPrevPageFunc)(); + +// for static options +typedef void (*ChangeFunc)(int8 before, int8 after); // called after updating the value. + // only called on enter if onlyApplyOnEnter set, otherwise called on every value change + +typedef void (*ChangeFuncFloat)(float before, float after); // called after updating the value. + +// for dynamic options +typedef wchar* (*DrawFunc)(bool* disabled, bool userHovering); // you must return a pointer for right text. + // you can also set *disabled if you want to gray it out. +typedef void (*ButtonPressFunc)(int8 action); // see FEOPTION_ACTIONs above + +// -- Internal things +void CustomFrontendOptionsPopulate(); +extern int lastOgScreen; // for reloading +extern int numCustomFrontendOptions; +extern int numCustomFrontendScreens; + +// -- To be used in ButtonPressFunc / ChangeFunc(this one would be weird): +void GoBack(void); + +uint8 GetNumberOfMenuOptions(int screen); + +// !!! We're now moved to MenuScreensCustom.cpp, which houses an array that keeps all original+custom options. +// But you can still use the APIs below, and manipulate aScreens while in game. + +// Limits: +// The code relies on that you won't use more then NUM_MENUROWS(18) options on one page, and won't exceed the MENUPAGES of pages. +// Also congrats if you can make 18 options visible at once. + +// Texts: +// All text parameters accept char[8] GXT key. + +// Execute direction: +// All of the calls below eventually manipulate the aScreens array, so keep in mind to add/replace options in order, +// i.e. don't set cursor to 8 first and then 3. + + +// -- Placing the cursor to append/overwrite option +// +// Done via FrontendOptionSetCursor(screen, position, overwrite = false), parameters explained below: +// Screen: as the name suggests. Also accepts the screen IDs returned from FrontendScreenAdd. +// Option: if positive, next AddOption call will put the option to there and progress the cursor. +// if negative, cursor will be placed on bottom-(pos+1), so -1 means the very bottom, -2 means before the back button etc. +// Overwrite: Use to overwrite the options, not appending a new one. AddOption calls will still progress the cursor. + +void FrontendOptionSetCursor(int screen, int8 option, bool overwrite = false); + +// var is optional in AddDynamic, enables you to save them in an INI file(also needs passing char array to saveCat and saveKey param. obv), otherwise pass nil/0 +void FrontendOptionAddBuiltinAction(const char* gxtKey, uint16 x, uint16 y, uint8 align, int action, int targetMenu = MENUPAGE_NONE, int saveSlot = SAVESLOT_NONE); +void FrontendOptionAddSelect(const char* gxtKey, uint16 x, uint16 y, uint8 align, const char** rightTexts, int8 numRightTexts, int8 *var, bool onlyApplyOnEnter, ChangeFunc changeFunc, const char* saveCat = nil, const char* saveKey = nil, bool disableIfGameLoaded = false); +void FrontendOptionAddDynamic(const char* gxtKey, uint16 x, uint16 y, uint8 align, DrawFunc rightTextDrawFunc, int8 *var, ButtonPressFunc buttonPressFunc, const char* saveCat = nil, const char* saveKey = nil); + +// lineHeight = 0 means game will use MENU_DEFAULT_LINE_HEIGHT +uint8 FrontendScreenAdd(const char* gxtKey, int prevPage, int lineHeight, bool showLeftRightHelper, ReturnPrevPageFunc returnPrevPageFunc = nil); +#endif diff --git a/src/extras/ini.h b/src/miami/extras/ini.h similarity index 91% rename from src/extras/ini.h rename to src/miami/extras/ini.h index 9e5e36ea..44dd3d57 100644 --- a/src/extras/ini.h +++ b/src/miami/extras/ini.h @@ -94,10 +94,6 @@ #include #include -#ifdef RW_DC -#include -#endif - namespace mINI { namespace INIStringUtil @@ -339,7 +335,6 @@ namespace mINI private: std::ifstream fileReadStream; T_LineDataPtr lineData; - std::size_t start_offt; T_LineData readFile() { @@ -357,17 +352,7 @@ namespace mINI } std::string buffer; buffer.reserve(50); - -#ifdef RW_DC - { - vmu_pkg_t vmu_pkg; - vmu_pkg_parse(reinterpret_cast(const_cast(fileContents.c_str())), &vmu_pkg); - start_offt = reinterpret_cast(vmu_pkg.data) - - reinterpret_cast(fileContents.c_str()); - } -#endif - - for (std::size_t i = start_offt; i < fileSize; ++i) + for (std::size_t i = 0; i < fileSize; ++i) { char& c = fileContents[i]; if (c == '\n') @@ -393,8 +378,6 @@ namespace mINI { lineData = std::make_shared(); } - - start_offt = 0; } ~INIReader() { } @@ -443,54 +426,22 @@ namespace mINI { private: std::ofstream fileWriteStream; - std::stringstream memStream; public: bool prettyPrint = false; INIGenerator(std::string const& filename) - : fileWriteStream(filename, std::ios::out | std::ios::binary), - memStream(std::ios::out | std::ios::binary) { + fileWriteStream.open(filename, std::ios::out | std::ios::binary); } - - ~INIGenerator() - { - const char *buf = memStream.str().c_str(); - int buf_size = static_cast(memStream.tellg()); - -#ifdef RW_DC - uint8_t *data; - uint8_t icon_buf[512 * 3]; - vmu_pkg_t vmu_pkg = { - .desc_short = "DCA3 Settings", - .desc_long = "DCA3 Settings", - .app_id = "DCA3", - .icon_cnt = 3, - .icon_anim_speed = 8, - .data_len = buf_size, - .icon_data = icon_buf, - .data = reinterpret_cast(buf), - }; - - if (vmu_pkg_load_icon(&vmu_pkg, "settings.ico") < 0) - vmu_pkg.icon_cnt = 0; - - vmu_pkg_build(const_cast(&vmu_pkg), &data, &buf_size); - buf = reinterpret_cast(data); -#endif - - fileWriteStream.write(buf, buf_size); - } - - bool operator<<(const std::string& str) - { - memStream << str; - return true; - } + ~INIGenerator() { } bool operator<<(INIStructure const& data) { + if (!fileWriteStream.is_open()) + { + return false; + } if (!data.size()) { return true; @@ -500,13 +451,13 @@ namespace mINI { auto const& section = it->first; auto const& collection = it->second; - memStream + fileWriteStream << "[" << section << "]"; if (collection.size()) { - memStream << INIStringUtil::endl; + fileWriteStream << INIStringUtil::endl; auto it2 = collection.begin(); for (;;) { @@ -514,7 +465,7 @@ namespace mINI INIStringUtil::replace(key, "=", "\\="); auto value = it2->second; INIStringUtil::trim(value); - memStream + fileWriteStream << key << ((prettyPrint) ? " = " : "=") << value; @@ -522,26 +473,21 @@ namespace mINI { break; } - memStream << INIStringUtil::endl; + fileWriteStream << INIStringUtil::endl; } } if (++it == data.end()) { break; } - memStream << INIStringUtil::endl; + fileWriteStream << INIStringUtil::endl; if (prettyPrint) { - memStream << INIStringUtil::endl; + fileWriteStream << INIStringUtil::endl; } } return true; } - - operator bool() const - { - return fileWriteStream.is_open(); - } }; class INIWriter @@ -721,15 +667,12 @@ namespace mINI { struct stat buf; bool fileExists = (stat(filename.c_str(), &buf) == 0); - - INIGenerator generator(filename); - generator.prettyPrint = prettyPrint; - if (!fileExists) { + INIGenerator generator(filename); + generator.prettyPrint = prettyPrint; return generator << data; } - INIStructure originalData; T_LineDataPtr lineData; bool readSuccess = false; @@ -745,19 +688,20 @@ namespace mINI return false; } T_LineData output = getLazyOutput(lineData, data, originalData); - if (generator) + std::ofstream fileWriteStream(filename, std::ios::out | std::ios::binary); + if (fileWriteStream.is_open()) { if (output.size()) { auto line = output.begin(); for (;;) { - generator << *line; + fileWriteStream << *line; if (++line == output.end()) { break; } - generator << INIStringUtil::endl; + fileWriteStream << INIStringUtil::endl; } } return true; diff --git a/src/miami/extras/postfx.cpp b/src/miami/extras/postfx.cpp new file mode 100644 index 00000000..ee6c3964 --- /dev/null +++ b/src/miami/extras/postfx.cpp @@ -0,0 +1,494 @@ +#define WITHD3D +#include "common.h" + +#ifdef EXTENDED_COLOURFILTER + +#ifndef LIBRW +#error "Need librw for EXTENDED_COLOURFILTER" +#endif + +#include "main.h" +#include "RwHelper.h" +#include "Camera.h" +#include "MBlur.h" +#include "postfx.h" + +RwRaster *CPostFX::pFrontBuffer; +RwRaster *CPostFX::pBackBuffer; +bool CPostFX::bJustInitialised; +int CPostFX::EffectSwitch = POSTFX_NORMAL; +bool CPostFX::BlurOn = false; +bool CPostFX::MotionBlurOn = false; + +static RwIm2DVertex Vertex[4]; +static RwIm2DVertex Vertex2[4]; +static RwImVertexIndex Index[6] = { 0, 1, 2, 0, 2, 3 }; + +#ifdef RW_D3D9 +void *colourfilterVC_PS; +void *contrast_PS; +#endif +#ifdef RW_OPENGL +int32 u_blurcolor; +int32 u_contrastAdd; +int32 u_contrastMult; +rw::gl3::Shader *colourFilterVC; +rw::gl3::Shader *contrast; +#endif + +void +CPostFX::InitOnce(void) +{ +#ifdef RW_OPENGL + u_blurcolor = rw::gl3::registerUniform("u_blurcolor"); + u_contrastAdd = rw::gl3::registerUniform("u_contrastAdd"); + u_contrastMult = rw::gl3::registerUniform("u_contrastMult"); +#endif +} + +void +CPostFX::Open(RwCamera *cam) +{ + if(pFrontBuffer) + Close(); + + uint32 width = Pow(2.0f, int32(log2(RwRasterGetWidth (RwCameraGetRaster(cam))))+1); + uint32 height = Pow(2.0f, int32(log2(RwRasterGetHeight(RwCameraGetRaster(cam))))+1); + uint32 depth = RwRasterGetDepth(RwCameraGetRaster(cam)); + pFrontBuffer = RwRasterCreate(width, height, depth, rwRASTERTYPECAMERATEXTURE); + pBackBuffer = RwRasterCreate(width, height, depth, rwRASTERTYPECAMERATEXTURE); + bJustInitialised = true; + + float zero, xmax, ymax; + + if(RwRasterGetDepth(RwCameraGetRaster(cam)) == 16){ + zero = HALFPX; + xmax = width + HALFPX; + ymax = height + HALFPX; + }else{ + zero = -HALFPX; + xmax = width - HALFPX; + ymax = height - HALFPX; + } + + RwIm2DVertexSetScreenX(&Vertex[0], zero); + RwIm2DVertexSetScreenY(&Vertex[0], zero); + RwIm2DVertexSetScreenZ(&Vertex[0], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&Vertex[0], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&Vertex[0], 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetU(&Vertex[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetV(&Vertex[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetIntRGBA(&Vertex[0], 255, 255, 255, 255); + + RwIm2DVertexSetScreenX(&Vertex[1], zero); + RwIm2DVertexSetScreenY(&Vertex[1], ymax); + RwIm2DVertexSetScreenZ(&Vertex[1], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&Vertex[1], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&Vertex[1], 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetU(&Vertex[1], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetV(&Vertex[1], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetIntRGBA(&Vertex[1], 255, 255, 255, 255); + + RwIm2DVertexSetScreenX(&Vertex[2], xmax); + RwIm2DVertexSetScreenY(&Vertex[2], ymax); + RwIm2DVertexSetScreenZ(&Vertex[2], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&Vertex[2], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&Vertex[2], 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetU(&Vertex[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetV(&Vertex[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetIntRGBA(&Vertex[2], 255, 255, 255, 255); + + RwIm2DVertexSetScreenX(&Vertex[3], xmax); + RwIm2DVertexSetScreenY(&Vertex[3], zero); + RwIm2DVertexSetScreenZ(&Vertex[3], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&Vertex[3], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&Vertex[3], 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetU(&Vertex[3], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetV(&Vertex[3], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetIntRGBA(&Vertex[3], 255, 255, 255, 255); + + + RwIm2DVertexSetScreenX(&Vertex2[0], zero + 2.0f); + RwIm2DVertexSetScreenY(&Vertex2[0], zero + 2.0f); + RwIm2DVertexSetScreenZ(&Vertex2[0], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&Vertex2[0], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&Vertex2[0], 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetU(&Vertex2[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetV(&Vertex2[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetIntRGBA(&Vertex2[0], 255, 255, 255, 255); + + RwIm2DVertexSetScreenX(&Vertex2[1], 2.0f); + RwIm2DVertexSetScreenY(&Vertex2[1], ymax + 2.0f); + RwIm2DVertexSetScreenZ(&Vertex2[1], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&Vertex2[1], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&Vertex2[1], 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetU(&Vertex2[1], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetV(&Vertex2[1], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetIntRGBA(&Vertex2[1], 255, 255, 255, 255); + + RwIm2DVertexSetScreenX(&Vertex2[2], xmax + 2.0f); + RwIm2DVertexSetScreenY(&Vertex2[2], ymax + 2.0f); + RwIm2DVertexSetScreenZ(&Vertex2[2], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&Vertex2[2], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&Vertex2[2], 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetU(&Vertex2[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetV(&Vertex2[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetIntRGBA(&Vertex2[2], 255, 255, 255, 255); + + RwIm2DVertexSetScreenX(&Vertex2[3], xmax + 2.0f); + RwIm2DVertexSetScreenY(&Vertex2[3], zero + 2.0f); + RwIm2DVertexSetScreenZ(&Vertex2[3], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&Vertex2[3], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&Vertex2[3], 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetU(&Vertex2[3], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetV(&Vertex2[3], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetIntRGBA(&Vertex2[3], 255, 255, 255, 255); + + +#ifdef RW_D3D9 +#include "shaders/obj/colourfilterVC_PS.inc" + colourfilterVC_PS = rw::d3d::createPixelShader(colourfilterVC_PS_cso); +#include "shaders/obj/contrastPS.inc" + contrast_PS = rw::d3d::createPixelShader(contrastPS_cso); +#endif +#ifdef RW_OPENGL + using namespace rw::gl3; + + { +#include "shaders/obj/im2d_vert.inc" +#include "shaders/obj/colourfilterVC_frag.inc" + const char *vs[] = { shaderDecl, header_vert_src, im2d_vert_src, nil }; + const char *fs[] = { shaderDecl, header_frag_src, colourfilterVC_frag_src, nil }; + colourFilterVC = Shader::create(vs, fs); + assert(colourFilterVC); + } + + { +#include "shaders/obj/im2d_vert.inc" +#include "shaders/obj/contrast_frag.inc" + const char *vs[] = { shaderDecl, header_vert_src, im2d_vert_src, nil }; + const char *fs[] = { shaderDecl, header_frag_src, contrast_frag_src, nil }; + contrast = Shader::create(vs, fs); + assert(contrast); + } + +#endif +} + +void +CPostFX::Close(void) +{ + if(pFrontBuffer){ + RwRasterDestroy(pFrontBuffer); + pFrontBuffer = nil; + } + if(pBackBuffer){ + RwRasterDestroy(pBackBuffer); + pBackBuffer = nil; + } +#ifdef RW_D3D9 + if(colourfilterVC_PS){ + rw::d3d::destroyPixelShader(colourfilterVC_PS); + colourfilterVC_PS = nil; + } + if(contrast_PS){ + rw::d3d::destroyPixelShader(contrast_PS); + contrast_PS = nil; + } +#endif +#ifdef RW_OPENGL + if(colourFilterVC){ + colourFilterVC->destroy(); + colourFilterVC = nil; + } + if(contrast){ + contrast->destroy(); + contrast = nil; + } +#endif +} + +void +CPostFX::RenderOverlayBlur(RwCamera *cam, int32 r, int32 g, int32 b, int32 a) +{ + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, pFrontBuffer); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + + RwIm2DVertexSetIntRGBA(&Vertex[0], r*2, g*2, b*2, 30); + RwIm2DVertexSetIntRGBA(&Vertex[1], r*2, g*2, b*2, 30); + RwIm2DVertexSetIntRGBA(&Vertex[2], r*2, g*2, b*2, 30); + RwIm2DVertexSetIntRGBA(&Vertex[3], r*2, g*2, b*2, 30); + RwIm2DVertexSetIntRGBA(&Vertex2[0], r*2, g*2, b*2, 30); + RwIm2DVertexSetIntRGBA(&Vertex2[1], r*2, g*2, b*2, 30); + RwIm2DVertexSetIntRGBA(&Vertex2[2], r*2, g*2, b*2, 30); + RwIm2DVertexSetIntRGBA(&Vertex2[3], r*2, g*2, b*2, 30); + + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, BlurOn ? Vertex2 : Vertex, 4, Index, 6); + + + RwIm2DVertexSetIntRGBA(&Vertex2[0], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex[0], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex2[1], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex[1], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex2[2], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex[2], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex2[3], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex[3], r, g, b, a); + + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6); + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, BlurOn ? Vertex2 : Vertex, 4, Index, 6); +} + +void +CPostFX::RenderOverlaySniper(RwCamera *cam, int32 r, int32 g, int32 b, int32 a) +{ + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, pFrontBuffer); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + + RwIm2DVertexSetIntRGBA(&Vertex[0], r, g, b, 80); + RwIm2DVertexSetIntRGBA(&Vertex[1], r, g, b, 80); + RwIm2DVertexSetIntRGBA(&Vertex[2], r, g, b, 80); + RwIm2DVertexSetIntRGBA(&Vertex[3], r, g, b, 80); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6); +} + +float CPostFX::Intensity = 1.0f; + +void +CPostFX::RenderOverlayShader(RwCamera *cam, int32 r, int32 g, int32 b, int32 a) +{ + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, pBackBuffer); + + if(EffectSwitch == POSTFX_MOBILE){ + float mult[3], add[3]; + mult[0] = (r-64)/256.0f + 1.4f; + mult[1] = (g-64)/256.0f + 1.4f; + mult[2] = (b-64)/256.0f + 1.4f; + add[0] = r/1536.f - 0.05f; + add[1] = g/1536.f - 0.05f; + add[2] = b/1536.f - 0.05f; +#ifdef RW_D3D9 + rw::d3d::d3ddevice->SetPixelShaderConstantF(10, mult, 1); + rw::d3d::d3ddevice->SetPixelShaderConstantF(11, add, 1); + + rw::d3d::im2dOverridePS = contrast_PS; +#endif +#ifdef RW_OPENGL + rw::gl3::im2dOverrideShader = contrast; + contrast->use(); + glUniform3fv(contrast->uniformLocations[u_contrastMult], 1, mult); + glUniform3fv(contrast->uniformLocations[u_contrastAdd], 1, add); +#endif + }else{ + float f = Intensity; + float blurcolors[4]; + blurcolors[0] = r*f/255.0f; + blurcolors[1] = g*f/255.0f; + blurcolors[2] = b*f/255.0f; + blurcolors[3] = 30/255.0f; +#ifdef RW_D3D9 + rw::d3d::d3ddevice->SetPixelShaderConstantF(10, blurcolors, 1); + rw::d3d::im2dOverridePS = colourfilterVC_PS; +#endif +#ifdef RW_OPENGL + rw::gl3::im2dOverrideShader = colourFilterVC; + colourFilterVC->use(); + glUniform4fv(colourFilterVC->uniformLocations[u_blurcolor], 1, blurcolors); +#endif + } + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6); +#ifdef RW_D3D9 + rw::d3d::im2dOverridePS = nil; +#endif +#ifdef RW_OPENGL + rw::gl3::im2dOverrideShader = nil; +#endif +} + +void +CPostFX::RenderMotionBlur(RwCamera *cam, uint32 blur) +{ + if(blur == 0) + return; + + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, pFrontBuffer); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + + RwIm2DVertexSetIntRGBA(&Vertex[0], 255, 255, 255, blur); + RwIm2DVertexSetIntRGBA(&Vertex[1], 255, 255, 255, blur); + RwIm2DVertexSetIntRGBA(&Vertex[2], 255, 255, 255, blur); + RwIm2DVertexSetIntRGBA(&Vertex[3], 255, 255, 255, blur); + + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6); +} + +bool +CPostFX::NeedBackBuffer(void) +{ + // Current frame -- needed for non-blur effect + switch(EffectSwitch){ + case POSTFX_OFF: + case POSTFX_SIMPLE: + // no actual rendering here + return false; + case POSTFX_NORMAL: + if(MotionBlurOn) + return false; + else + return true; + case POSTFX_MOBILE: + return true; + } + return false; +} + +bool +CPostFX::NeedFrontBuffer(int32 type) +{ + // Last frame -- needed for motion blur + if(CMBlur::Drunkness > 0.0f) + return true; + if(type == MOTION_BLUR_SNIPER) + return true; + + switch(EffectSwitch){ + case POSTFX_OFF: + case POSTFX_SIMPLE: + // no actual rendering here + return false; + case POSTFX_NORMAL: + if(MotionBlurOn) + return true; + else + return false; + case POSTFX_MOBILE: + return false; + } + return false; +} + +void +CPostFX::GetBackBuffer(RwCamera *cam) +{ + RwRasterPushContext(pBackBuffer); + RwRasterRenderFast(RwCameraGetRaster(cam), 0, 0); + RwRasterPopContext(); +} + +void +CPostFX::Render(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 blur, int32 type, uint32 bluralpha) +{ + PUSH_RENDERGROUP("CPostFX::Render"); + + if(pFrontBuffer == nil) + Open(cam); + assert(pFrontBuffer); + assert(pBackBuffer); + + if(type == MOTION_BLUR_LIGHT_SCENE){ + SmoothColor(red, green, blue, blur); + red = AvgRed; + green = AvgGreen; + blue = AvgBlue; + blur = AvgAlpha; + } + + if(NeedBackBuffer()) + GetBackBuffer(cam); + + DefinedState(); + + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + + if(type == MOTION_BLUR_SNIPER){ + if(!bJustInitialised) + RenderOverlaySniper(cam, red, green, blue, blur); + }else switch(EffectSwitch){ + case POSTFX_OFF: + case POSTFX_SIMPLE: + // no actual rendering here + break; + case POSTFX_NORMAL: + if(MotionBlurOn){ + if(!bJustInitialised) + RenderOverlayBlur(cam, red, green, blue, blur); + }else{ + RenderOverlayShader(cam, red, green, blue, blur); + } + break; + case POSTFX_MOBILE: + RenderOverlayShader(cam, red, green, blue, blur); + break; + } + + if(!bJustInitialised) + RenderMotionBlur(cam, 175.0f * CMBlur::Drunkness); + + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + + if(NeedFrontBuffer(type)){ + RwRasterPushContext(pFrontBuffer); + RwRasterRenderFast(RwCameraGetRaster(cam), 0, 0); + RwRasterPopContext(); + bJustInitialised = false; + }else + bJustInitialised = true; + + POP_RENDERGROUP(); +} + +int CPostFX::PrevRed[NUMAVERAGE], CPostFX::AvgRed; +int CPostFX::PrevGreen[NUMAVERAGE], CPostFX::AvgGreen; +int CPostFX::PrevBlue[NUMAVERAGE], CPostFX::AvgBlue; +int CPostFX::PrevAlpha[NUMAVERAGE], CPostFX::AvgAlpha; +int CPostFX::Next; +int CPostFX::NumValues; + +// This is rather annoying...the blur color can flicker slightly +// which becomes very visible when amplified by the shader +void +CPostFX::SmoothColor(uint32 red, uint32 green, uint32 blue, uint32 alpha) +{ + PrevRed[Next] = red; + PrevGreen[Next] = green; + PrevBlue[Next] = blue; + PrevAlpha[Next] = alpha; + Next = (Next+1) % NUMAVERAGE; + NumValues = Min(NumValues+1, NUMAVERAGE); + + AvgRed = 0; + AvgGreen = 0; + AvgBlue = 0; + AvgAlpha = 0; + for(int i = 0; i < NumValues; i++){ + AvgRed += PrevRed[i]; + AvgGreen += PrevGreen[i]; + AvgBlue += PrevBlue[i]; + AvgAlpha += PrevAlpha[i]; + } + AvgRed /= NumValues; + AvgGreen /= NumValues; + AvgBlue /= NumValues; + AvgAlpha /= NumValues; +} + +#endif diff --git a/src/miami/extras/postfx.h b/src/miami/extras/postfx.h new file mode 100644 index 00000000..db702bf3 --- /dev/null +++ b/src/miami/extras/postfx.h @@ -0,0 +1,46 @@ +#pragma once + +#ifdef EXTENDED_COLOURFILTER + +class CPostFX +{ +public: + enum { + POSTFX_OFF, + POSTFX_SIMPLE, + POSTFX_NORMAL, + POSTFX_MOBILE + }; + static RwRaster *pFrontBuffer; + static RwRaster *pBackBuffer; + static bool bJustInitialised; + static int EffectSwitch; + static bool BlurOn; // or use CMblur for that? + static bool MotionBlurOn; // or use CMblur for that? + static float Intensity; + + // smooth blur color + enum { NUMAVERAGE = 20 }; + static int PrevRed[NUMAVERAGE], AvgRed; + static int PrevGreen[NUMAVERAGE], AvgGreen; + static int PrevBlue[NUMAVERAGE], AvgBlue; + static int PrevAlpha[NUMAVERAGE], AvgAlpha; + static int Next; + static int NumValues; + + static void InitOnce(void); + static void Open(RwCamera *cam); + static void Close(void); + static void RenderOverlayBlur(RwCamera *cam, int32 r, int32 g, int32 b, int32 a); + static void RenderOverlaySniper(RwCamera *cam, int32 r, int32 g, int32 b, int32 a); + static void RenderOverlayShader(RwCamera *cam, int32 r, int32 g, int32 b, int32 a); + static void RenderMotionBlur(RwCamera *cam, uint32 blur); + static void Render(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 blur, int32 type, uint32 bluralpha); + static void SmoothColor(uint32 red, uint32 green, uint32 blue, uint32 alpha); + static bool NeedBackBuffer(void); + static bool NeedFrontBuffer(int32 type); + static void GetBackBuffer(RwCamera *cam); + static bool UseBlurColours(void) { return EffectSwitch != POSTFX_SIMPLE; } +}; + +#endif diff --git a/src/miami/extras/re3_inttypes.h b/src/miami/extras/re3_inttypes.h new file mode 100644 index 00000000..bf0c53e2 --- /dev/null +++ b/src/miami/extras/re3_inttypes.h @@ -0,0 +1,216 @@ +#define PRId8 "hhd" +#define PRId16 "hd" +#define PRId32 "ld" +#define PRId64 "lld" + +#define PRIdFAST8 "hhd" +#define PRIdFAST16 "hd" +#define PRIdFAST32 "ld" +#define PRIdFAST64 "lld" + +#define PRIdLEAST8 "hhd" +#define PRIdLEAST16 "hd" +#define PRIdLEAST32 "ld" +#define PRIdLEAST64 "lld" + +#define PRIdMAX "lld" +#define PRIdPTR "lld" + +#define PRIi8 "hhi" +#define PRIi16 "hi" +#define PRIi32 "li" +#define PRIi64 "lli" + +#define PRIiFAST8 "hhi" +#define PRIiFAST16 "hi" +#define PRIiFAST32 "li" +#define PRIiFAST64 "lli" + +#define PRIiLEAST8 "hhi" +#define PRIiLEAST16 "hi" +#define PRIiLEAST32 "li" +#define PRIiLEAST64 "lli" + +#define PRIiMAX "lli" +#define PRIiPTR "lli" + +#define PRIo8 "hho" +#define PRIo16 "ho" +#define PRIo32 "lo" +#define PRIo64 "llo" + +#define PRIoFAST8 "hho" +#define PRIoFAST16 "ho" +#define PRIoFAST32 "lo" +#define PRIoFAST64 "llo" + +#define PRIoLEAST8 "hho" +#define PRIoLEAST16 "ho" +#define PRIoLEAST32 "lo" +#define PRIoLEAST64 "llo" + +#define PRIoMAX "llo" +#define PRIoPTR "llo" + +#define PRIu8 "hhu" +#define PRIu16 "hu" +#define PRIu32 "lu" +#define PRIu64 "llu" + +#define PRIuFAST8 "hhu" +#define PRIuFAST16 "hu" +#define PRIuFAST32 "lu" +#define PRIuFAST64 "llu" + +#define PRIuLEAST8 "hhu" +#define PRIuLEAST16 "hu" +#define PRIuLEAST32 "lu" +#define PRIuLEAST64 "llu" + +#define PRIuMAX "llu" +#define PRIuPTR "llu" + +#define PRIx8 "hhx" +#define PRIx16 "hx" +#define PRIx32 "lx" +#define PRIx64 "llx" + +#define PRIxFAST8 "hhx" +#define PRIxFAST16 "hx" +#define PRIxFAST32 "lx" +#define PRIxFAST64 "llx" + +#define PRIxLEAST8 "hhx" +#define PRIxLEAST16 "hx" +#define PRIxLEAST32 "lx" +#define PRIxLEAST64 "llx" + +#define PRIxMAX "llx" +#define PRIxPTR "llx" + +#define PRIX8 "hhX" +#define PRIX16 "hX" +#define PRIX32 "lX" +#define PRIX64 "llX" + +#define PRIXFAST8 "hhX" +#define PRIXFAST16 "hX" +#define PRIXFAST32 "lX" +#define PRIXFAST64 "llX" + +#define PRIXLEAST8 "hhX" +#define PRIXLEAST16 "hX" +#define PRIXLEAST32 "lX" +#define PRIXLEAST64 "llX" + +#define PRIXMAX "llX" +#define PRIXPTR "llX" + + /* SCAN FORMAT MACROS */ +#define SCNd8 "hhd" +#define SCNd16 "hd" +#define SCNd32 "ld" +#define SCNd64 "lld" + +#define SCNdFAST8 "hhd" +#define SCNdFAST16 "hd" +#define SCNdFAST32 "ld" +#define SCNdFAST64 "lld" + +#define SCNdLEAST8 "hhd" +#define SCNdLEAST16 "hd" +#define SCNdLEAST32 "ld" +#define SCNdLEAST64 "lld" + +#define SCNdMAX "lld" +#define SCNdPTR "lld" + +#define SCNi8 "hhi" +#define SCNi16 "hi" +#define SCNi32 "li" +#define SCNi64 "lli" + +#define SCNiFAST8 "hhi" +#define SCNiFAST16 "hi" +#define SCNiFAST32 "li" +#define SCNiFAST64 "lli" + +#define SCNiLEAST8 "hhi" +#define SCNiLEAST16 "hi" +#define SCNiLEAST32 "li" +#define SCNiLEAST64 "lli" + +#define SCNiMAX "lli" +#define SCNiPTR "lli" + +#define SCNo8 "hho" +#define SCNo16 "ho" +#define SCNo32 "lo" +#define SCNo64 "llo" + +#define SCNoFAST8 "hho" +#define SCNoFAST16 "ho" +#define SCNoFAST32 "lo" +#define SCNoFAST64 "llo" + +#define SCNoLEAST8 "hho" +#define SCNoLEAST16 "ho" +#define SCNoLEAST32 "lo" +#define SCNoLEAST64 "llo" + +#define SCNoMAX "llo" +#define SCNoPTR "llo" + +#define SCNu8 "hhu" +#define SCNu16 "hu" +#define SCNu32 "lu" +#define SCNu64 "llu" + +#define SCNuFAST8 "hhu" +#define SCNuFAST16 "hu" +#define SCNuFAST32 "lu" +#define SCNuFAST64 "llu" + +#define SCNuLEAST8 "hhu" +#define SCNuLEAST16 "hu" +#define SCNuLEAST32 "lu" +#define SCNuLEAST64 "llu" + +#define SCNuMAX "llu" +#define SCNuPTR "llu" + +#define SCNx8 "hhx" +#define SCNx16 "hx" +#define SCNx32 "lx" +#define SCNx64 "llx" + +#define SCNxFAST8 "hhx" +#define SCNxFAST16 "hx" +#define SCNxFAST32 "lx" +#define SCNxFAST64 "llx" + +#define SCNxLEAST8 "hhx" +#define SCNxLEAST16 "hx" +#define SCNxLEAST32 "lx" +#define SCNxLEAST64 "llx" + +#define SCNxMAX "llx" +#define SCNxPTR "llx" + +#define SCNX8 "hhX" +#define SCNX16 "hX" +#define SCNX32 "lX" +#define SCNX64 "llX" + +#define SCNXFAST8 "hhX" +#define SCNXFAST16 "hX" +#define SCNXFAST32 "lX" +#define SCNXFAST64 "llX" + +#define SCNXLEAST8 "hhX" +#define SCNXLEAST16 "hX" +#define SCNXLEAST32 "lX" +#define SCNXLEAST64 "llX" + +#define SCNXMAX "llX" +#define SCNXPTR "llX" \ No newline at end of file diff --git a/src/miami/extras/screendroplets.cpp b/src/miami/extras/screendroplets.cpp new file mode 100644 index 00000000..cc86808f --- /dev/null +++ b/src/miami/extras/screendroplets.cpp @@ -0,0 +1,817 @@ +#define WITHD3D +#include "common.h" + +#ifdef SCREEN_DROPLETS + +#ifndef LIBRW +#error "Need librw for SCREEN_DROPLETS" +#endif + +#include "General.h" +#include "main.h" +#include "RwHelper.h" +#include "Timer.h" +#include "Camera.h" +#include "World.h" +#include "ZoneCull.h" +#include "Weather.h" +#include "ParticleObject.h" + #include "Pad.h" +#include "RenderBuffer.h" +#include "custompipes.h" +#include "postfx.h" +#include "screendroplets.h" + +// for 640 +#define MAXSIZE 15 +#define MINSIZE 4 + +int ScreenDroplets::ms_initialised; +RwTexture *ScreenDroplets::ms_maskTex; +RwTexture *ScreenDroplets::ms_screenTex; + +bool ScreenDroplets::ms_enabled = true; +bool ScreenDroplets::ms_movingEnabled = true; + +ScreenDroplets::ScreenDrop ScreenDroplets::ms_drops[ScreenDroplets::MAXDROPS]; +int ScreenDroplets::ms_numDrops; +ScreenDroplets::ScreenDropMoving ScreenDroplets::ms_dropsMoving[ScreenDroplets::MAXDROPSMOVING]; +int ScreenDroplets::ms_numDropsMoving; + +CVector ScreenDroplets::ms_prevCamUp; +CVector ScreenDroplets::ms_prevCamPos; +CVector ScreenDroplets::ms_camMoveDelta; +float ScreenDroplets::ms_camMoveDist; +CVector ScreenDroplets::ms_screenMoveDelta; +float ScreenDroplets::ms_screenMoveDist; +float ScreenDroplets::ms_camUpAngle; + +int ScreenDroplets::ms_splashDuration; +CParticleObject *ScreenDroplets::ms_splashObject; + +struct Im2DVertexUV2 : rw::RWDEVICE::Im2DVertex +{ + rw::float32 u2, v2; +}; + +#ifdef RW_D3D9 +static void *screenDroplet_PS; +#endif +#ifdef RW_GL3 +static rw::gl3::Shader *screenDroplet; +#endif + +// platform specific +static void openim2d_uv2(void); +static void closeim2d_uv2(void); +static void RenderIndexedPrimitive_UV2(RwPrimitiveType primType, Im2DVertexUV2 *vertices, RwInt32 numVertices, RwImVertexIndex *indices, RwInt32 numIndices); + +static Im2DVertexUV2 VertexBuffer[TEMPBUFFERVERTSIZE]; + +void +ScreenDroplets::Initialise(void) +{ + Clear(); + ms_splashDuration = -1; + ms_splashObject = nil; +} + +// Create white circle mask for rain drops +static RwTexture* +CreateDropMask(int32 size) +{ + RwImage *img = RwImageCreate(size, size, 32); + RwImageAllocatePixels(img); + + uint8 *pixels = RwImageGetPixels(img); + int32 stride = RwImageGetStride(img); + + for(int y = 0; y < size; y++){ + float yf = ((y + 0.5f)/size - 0.5f)*2.0f; + for(int x = 0; x < size; x++){ + float xf = ((x + 0.5f)/size - 0.5f)*2.0f; + memset(&pixels[y*stride + x*4], xf*xf + yf*yf < 1.0f ? 0xFF : 0x00, 4); + } + } + + int32 width, height, depth, format; + RwImageFindRasterFormat(img, rwRASTERTYPETEXTURE, &width, &height, &depth, &format); + RwRaster *ras = RwRasterCreate(width, height, depth, format); + RwRasterSetFromImage(ras, img); + RwImageDestroy(img); + return RwTextureCreate(ras); +} + +void +ScreenDroplets::InitDraw(void) +{ + ms_maskTex = CreateDropMask(64); + + ms_screenTex = RwTextureCreate(nil); + RwTextureSetFilterMode(ms_screenTex, rwFILTERLINEAR); + + openim2d_uv2(); +#ifdef RW_D3D9 +#include "shaders/obj/screenDroplet_PS.inc" + screenDroplet_PS = rw::d3d::createPixelShader(screenDroplet_PS_cso); +#endif +#ifdef RW_GL3 + using namespace rw::gl3; + { +#include "shaders/obj/im2d_UV2_vert.inc" +#include "shaders/obj/screenDroplet_frag.inc" + const char *vs[] = { shaderDecl, header_vert_src, im2d_UV2_vert_src, nil }; + const char *fs[] = { shaderDecl, header_frag_src, screenDroplet_frag_src, nil }; + screenDroplet = Shader::create(vs, fs); + assert(screenDroplet); + } +#endif + + ms_initialised = 1; +} + +void +ScreenDroplets::Shutdown(void) +{ + if(ms_maskTex){ + RwTextureDestroy(ms_maskTex); + ms_maskTex = nil; + } + if(ms_screenTex){ + RwTextureSetRaster(ms_screenTex, nil); + RwTextureDestroy(ms_screenTex); + ms_screenTex = nil; + } +#ifdef RW_D3D9 + if(screenDroplet_PS){ + rw::d3d::destroyPixelShader(screenDroplet_PS); + screenDroplet_PS = nil; + } +#endif +#ifdef RW_GL3 + if(screenDroplet){ + screenDroplet->destroy(); + screenDroplet = nil; + } +#endif + + closeim2d_uv2(); +} + +void +ScreenDroplets::Process(void) +{ + ProcessCameraMovement(); + SprayDrops(); + ProcessMoving(); + Fade(); +} + +static void +FlushBuffer(void) +{ + if(TempBufferIndicesStored){ + RenderIndexedPrimitive_UV2(rwPRIMTYPETRILIST, + VertexBuffer, TempBufferVerticesStored, + TempBufferRenderIndexList, TempBufferIndicesStored); + TempBufferVerticesStored = 0; + TempBufferIndicesStored = 0; + } +} + +static int +StartStoring(int numIndices, int numVertices, RwImVertexIndex **indexStart, Im2DVertexUV2 **vertexStart) +{ + if(TempBufferIndicesStored + numIndices >= TEMPBUFFERINDEXSIZE || + TempBufferVerticesStored + numVertices >= TEMPBUFFERVERTSIZE) + FlushBuffer(); + *indexStart = &TempBufferRenderIndexList[TempBufferIndicesStored]; + *vertexStart = &VertexBuffer[TempBufferVerticesStored]; + int vertOffset = TempBufferVerticesStored; + TempBufferIndicesStored += numIndices; + TempBufferVerticesStored += numVertices; + return vertOffset; +} + +void +ScreenDroplets::Render(void) +{ + ScreenDrop *drop; + + DefinedState(); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(ms_maskTex)); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, FALSE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + + RwTextureSetRaster(ms_screenTex, CPostFX::pBackBuffer); +#ifdef RW_D3D9 + rw::d3d::im2dOverridePS = screenDroplet_PS; + rw::d3d::setTexture(1, ms_screenTex); +#endif +#ifdef RW_GL3 + rw::gl3::im2dOverrideShader = screenDroplet; + rw::gl3::setTexture(1, ms_screenTex); +#endif + + RenderBuffer::ClearRenderBuffer(); + for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++) + if(drop->active) + AddToRenderList(drop); + FlushBuffer(); + +#ifdef RW_D3D9 + rw::d3d::im2dOverridePS = nil; + rw::d3d::setTexture(1, nil); +#endif +#ifdef RW_GL3 + rw::gl3::im2dOverrideShader = nil; + rw::gl3::setTexture(1, nil); +#endif + + RwRenderStateSet(rwRENDERSTATEFOGENABLE, FALSE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, FALSE); +} + +void +ScreenDroplets::AddToRenderList(ScreenDroplets::ScreenDrop *drop) +{ + static float xy[] = { + -1.0f, -1.0f, + -1.0f, 1.0f, + 1.0f, 1.0f, + 1.0f, -1.0f + }; + static float uv[] = { + 0.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 1.0f, + 1.0f, 0.0f + }; + + int i; + RwImVertexIndex *indices; + Im2DVertexUV2 *verts; + int first = StartStoring(6, 4, &indices, &verts); + + float scale = 0.5f*SCREEN_SCALE_X(drop->size); + + float screenz = RwIm2DGetNearScreenZ(); + float z = RwCameraGetNearClipPlane(Scene.camera); + float recipz = 1.0f/z; + + float magSize = SCREEN_SCALE_Y(drop->magnification*(300.0f-40.0f) + 40.0f); + float ul = drop->x - magSize; + float vt = drop->y - magSize; + float ur = drop->x + magSize; + float vb = drop->y + magSize; + ul = Max(ul, 0.0f)/RwRasterGetWidth(CPostFX::pBackBuffer); + vt = Max(vt, 0.0f)/RwRasterGetHeight(CPostFX::pBackBuffer); + ur = Min(ur, SCREEN_WIDTH)/RwRasterGetWidth(CPostFX::pBackBuffer); + vb = Min(vb, SCREEN_HEIGHT)/RwRasterGetHeight(CPostFX::pBackBuffer); + + for(i = 0; i < 4; i++){ + RwIm2DVertexSetScreenX(&verts[i], drop->x + xy[i*2]*scale); + RwIm2DVertexSetScreenY(&verts[i], drop->y + xy[i*2+1]*scale); + RwIm2DVertexSetScreenZ(&verts[i], screenz); + RwIm2DVertexSetCameraZ(&verts[i], z); + RwIm2DVertexSetRecipCameraZ(&verts[i], recipz); + RwIm2DVertexSetIntRGBA(&verts[i], drop->color.r, drop->color.g, drop->color.b, drop->color.a); + RwIm2DVertexSetU(&verts[i], uv[i*2], recipz); + RwIm2DVertexSetV(&verts[i], uv[i*2+1], recipz); + + verts[i].u2 = i < 2 ? ul : ur; + verts[i].v2 = i % 3 ? vt : vb; + } + indices[0] = first + 0; + indices[1] = first + 1; + indices[2] = first + 2; + indices[3] = first + 2; + indices[4] = first + 3; + indices[5] = first + 0; +} + +void +ScreenDroplets::Clear(void) +{ + ScreenDrop *drop; + for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++) + drop->active = false; + ms_numDrops = 0; +} + +ScreenDroplets::ScreenDrop* +ScreenDroplets::NewDrop(float x, float y, float size, float lifetime, bool fades, int r, int g, int b) +{ + ScreenDrop *drop; + int i; + + for(i = 0, drop = ms_drops; i < MAXDROPS; i++, drop++) + if(!ms_drops[i].active) + goto found; + return nil; +found: + ms_numDrops++; + drop->x = x; + drop->y = y; + drop->size = size; + drop->magnification = (MAXSIZE - size + 1.0f) / (MAXSIZE - MINSIZE + 1.0f); + drop->fades = fades; + drop->active = true; + drop->color.r = r; + drop->color.g = g; + drop->color.b = b; + drop->color.a = 255; + drop->time = 0.0f; + drop->lifetime = lifetime; + return drop; +} + +void +ScreenDroplets::SetMoving(ScreenDroplets::ScreenDrop *drop) +{ + ScreenDropMoving *moving; + for(moving = ms_dropsMoving; moving < &ms_dropsMoving[MAXDROPSMOVING]; moving++) + if(moving->drop == nil) + goto found; + return; +found: + ms_numDropsMoving++; + moving->drop = drop; + moving->dist = 0.0f; +} + +void +ScreenDroplets::FillScreen(int n) +{ + float x, y, size; + ScreenDrop *drop; + + if(!ms_initialised) + return; + ms_numDrops = 0; + for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++){ + drop->active = false; + if(drop < &ms_drops[n]){ + x = CGeneral::GetRandomNumber() % (int)SCREEN_WIDTH; + y = CGeneral::GetRandomNumber() % (int)SCREEN_HEIGHT; + size = CGeneral::GetRandomNumberInRange(MINSIZE, MAXSIZE); + NewDrop(x, y, size, 2000.0f, true); + } + } +} + +void +ScreenDroplets::FillScreenMoving(float amount, bool isBlood) +{ + int n = (ms_screenMoveDelta.z > 5.0f ? 1.5f : 1.0f)*amount*20.0f; + float x, y, size; + ScreenDrop *drop; + + while(n--) + if(ms_numDrops < MAXDROPS && ms_numDropsMoving < MAXDROPSMOVING){ + x = CGeneral::GetRandomNumber() % (int)SCREEN_WIDTH; + y = CGeneral::GetRandomNumber() % (int)SCREEN_HEIGHT; + size = CGeneral::GetRandomNumberInRange(MINSIZE, MAXSIZE); + drop = nil; + if(isBlood) + drop = NewDrop(x, y, size, 2000.0f, true, 255, 0, 0); + else + drop = NewDrop(x, y, size, 2000.0f, true); + if(drop) + SetMoving(drop); + } +} + +void +ScreenDroplets::RegisterSplash(CParticleObject *pobj) +{ + CVector dist = pobj->GetPosition() - ms_prevCamPos; + if(dist.MagnitudeSqr() < 50.0f){ // 20 originally + ms_splashDuration = 14; + ms_splashObject = pobj; + } +} + +void +ScreenDroplets::ProcessCameraMovement(void) +{ + RwMatrix *camMat = RwFrameGetMatrix(RwCameraGetFrame(Scene.camera)); + CVector camPos = camMat->pos; + CVector camUp = camMat->at; + ms_camMoveDelta = camPos - ms_prevCamPos; + ms_camMoveDist = ms_camMoveDelta.Magnitude(); + + ms_prevCamUp = camUp; + ms_prevCamPos = camPos; + + ms_screenMoveDelta.x = -RwV3dDotProduct(&camMat->right, &ms_camMoveDelta); + ms_screenMoveDelta.y = RwV3dDotProduct(&camMat->up, &ms_camMoveDelta); + ms_screenMoveDelta.z = RwV3dDotProduct(&camMat->at, &ms_camMoveDelta); + ms_screenMoveDelta *= 10.0f; + ms_screenMoveDist = ms_screenMoveDelta.Magnitude2D(); + + uint16 mode = TheCamera.Cams[TheCamera.ActiveCam].Mode; + bool isTopDown = mode == CCam::MODE_TOPDOWN || mode == CCam::MODE_GTACLASSIC || mode == CCam::MODE_TOP_DOWN_PED; + bool isLookingInDirection = FindPlayerVehicle() && mode == CCam::MODE_1STPERSON && + (CPad::GetPad(0)->GetLookBehindForCar() || CPad::GetPad(0)->GetLookLeft() || CPad::GetPad(0)->GetLookRight()); + ms_enabled = !isTopDown && !isLookingInDirection; + ms_movingEnabled = !isTopDown && !isLookingInDirection; + + // 0 when looking stright up, 180 when looking up or down + ms_camUpAngle = RADTODEG(Acos(Clamp(camUp.z, -1.0f, 1.0f))); +} + +void +ScreenDroplets::SprayDrops(void) +{ + bool noRain = CCullZones::PlayerNoRain() || CCullZones::CamNoRain(); + if(!noRain && CWeather::Rain > 0.0f && ms_enabled){ + // 180 when looking stright up, 0 when looking up or down + float angle = 180.0f - ms_camUpAngle; + angle = Max(angle, 40.0f); // want at least some rain + FillScreenMoving((angle - 40.0f) / 150.0f * CWeather::Rain * 0.5f); + } + + int i; + for(i = 0; i < MAX_AUDIOHYDRANTS; i++){ + CAudioHydrant *hyd = CAudioHydrant::Get(i); + if (hyd->pParticleObject){ + CVector dist = hyd->pParticleObject->GetPosition() - ms_prevCamPos; + if(dist.MagnitudeSqr() > 40.0f || + DotProduct(dist, ms_prevCamUp) < 0.0f) continue; + + FillScreenMoving(1.0f); + } + } + + static int ndrops[] = { + 125, 250, 500, 1000, 1000, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if(ms_splashDuration >= 0){ + if(ms_numDrops < MAXDROPS) { + float numDropMult = 1.0f; + if(ms_splashObject){ + float dist = (ms_splashObject->GetPosition() - ms_prevCamPos).Magnitude(); + numDropMult = 1.0f - (dist - 5.0f)/15.0f; + if(numDropMult < 0) numDropMult = 0.0f; // fix + } + int n = ndrops[ms_splashDuration] * numDropMult; + while(n--) + if(ms_numDrops < MAXDROPS){ + float x = CGeneral::GetRandomNumber() % (int)SCREEN_WIDTH; + float y = CGeneral::GetRandomNumber() % (int)SCREEN_HEIGHT; + float size = CGeneral::GetRandomNumberInRange(MINSIZE, MAXSIZE); + NewDrop(x, y, size, 10000.0f, false); + } + } + ms_splashDuration--; + } +} + +void +ScreenDroplets::NewTrace(ScreenDroplets::ScreenDropMoving *moving) +{ + if(ms_numDrops < MAXDROPS){ + moving->dist = 0.0f; + NewDrop(moving->drop->x, moving->drop->y, MINSIZE, 500.0f, true, + moving->drop->color.r, moving->drop->color.g, moving->drop->color.b); + } +} + +void +ScreenDroplets::MoveDrop(ScreenDroplets::ScreenDropMoving *moving) +{ + ScreenDrop *drop = moving->drop; + if(!ms_movingEnabled) + return; + if(!drop->active){ + moving->drop = nil; + ms_numDropsMoving--; + return; + } + if(ms_screenMoveDelta.z > 0.0f && ms_camMoveDist > 0.3f){ + if(ms_screenMoveDist > 0.5f && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_1STPERSON){ + // movement when camera turns + moving->dist += ms_screenMoveDist; + if(moving->dist > 20.0f && drop->color.a > 100) + NewTrace(moving); + + drop->x -= ms_screenMoveDelta.x; + drop->y += ms_screenMoveDelta.y; + }else{ + // movement out of center + float d = ms_screenMoveDelta.z*0.2f; + float dx, dy, sum; + dx = drop->x - SCREEN_WIDTH*0.5f + ms_screenMoveDelta.x; + if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON) + dy = drop->y - SCREEN_HEIGHT*1.2f - ms_screenMoveDelta.y; + else + dy = drop->y - SCREEN_HEIGHT*0.5f - ms_screenMoveDelta.y; + sum = fabs(dx) + fabs(dy); + if(sum > 0.001f){ + dx /= sum; + dy /= sum; + } + moving->dist += d; + if(moving->dist > 20.0f && drop->color.a > 100) + NewTrace(moving); + drop->x += dx * d; + drop->y += dy * d; + } + + if(drop->x < 0.0f || drop->y < 0.0f || + drop->x > SCREEN_WIDTH || drop->y > SCREEN_HEIGHT){ + moving->drop = nil; + ms_numDropsMoving--; + } + } +} + +void +ScreenDroplets::ProcessMoving(void) +{ + ScreenDropMoving *moving; + if(!ms_movingEnabled) + return; + for(moving = ms_dropsMoving; moving < &ms_dropsMoving[MAXDROPSMOVING]; moving++) + if(moving->drop) + MoveDrop(moving); +} + +void +ScreenDroplets::Fade(void) +{ + ScreenDrop *drop; + for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++) + if(drop->active) + drop->Fade(); +} + +void +ScreenDroplets::ScreenDrop::Fade(void) +{ + int delta = CTimer::GetTimeStepInMilliseconds(); + time += delta; + if(time < lifetime){ + color.a = 255 - time/lifetime*255; + }else if(fades){ + ScreenDroplets::ms_numDrops--; + active = false; + } +} + + +/* + * Im2D with two uv coors + */ + +#ifdef RW_D3D9 +// stolen from RW, not in a public header +namespace rw { +namespace d3d { +void addDynamicVB(uint32 length, uint32 fvf, IDirect3DVertexBuffer9 **buf); // NB: don't share this pointer +void removeDynamicVB(IDirect3DVertexBuffer9 **buf); +void addDynamicIB(uint32 length, IDirect3DIndexBuffer9 **buf); // NB: don't share this pointer +void removeDynamicIB(IDirect3DIndexBuffer9 **buf); +} +} +// different than im2d +#define NUMINDICES 1024 +#define NUMVERTICES 1024 + +static int primTypeMap[] = { + D3DPT_POINTLIST, // invalid + D3DPT_LINELIST, + D3DPT_LINESTRIP, + D3DPT_TRIANGLELIST, + D3DPT_TRIANGLESTRIP, + D3DPT_TRIANGLEFAN, + D3DPT_POINTLIST, // actually not supported! +}; +// end of stolen stuff + + +static IDirect3DVertexDeclaration9 *im2ddecl_uv2; +static IDirect3DVertexBuffer9 *im2dvertbuf_uv2; +static IDirect3DIndexBuffer9 *im2dindbuf_uv2; + +void +openim2d_uv2(void) +{ + using namespace rw; + using namespace d3d; + D3DVERTEXELEMENT9 elements[5] = { + { 0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITIONT, 0 }, + { 0, offsetof(Im2DVertexUV2, color), D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 }, + { 0, offsetof(Im2DVertexUV2, u), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, + { 0, offsetof(Im2DVertexUV2, u2), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 }, + D3DDECL_END() + }; + assert(im2ddecl_uv2 == nil); + im2ddecl_uv2 = (IDirect3DVertexDeclaration9*)d3d9::createVertexDeclaration((d3d9::VertexElement*)elements); + assert(im2ddecl_uv2); + + assert(im2dvertbuf_uv2 == nil); + im2dvertbuf_uv2 = (IDirect3DVertexBuffer9*)createVertexBuffer(NUMVERTICES*sizeof(Im2DVertexUV2), 0, true); + assert(im2dvertbuf_uv2); + addDynamicVB(NUMVERTICES*sizeof(Im2DVertexUV2), 0, &im2dvertbuf_uv2); + + assert(im2dindbuf_uv2 == nil); + im2dindbuf_uv2 = (IDirect3DIndexBuffer9*)createIndexBuffer(NUMINDICES*sizeof(rw::uint16), true); + assert(im2dindbuf_uv2); + addDynamicIB(NUMINDICES*sizeof(rw::uint16), &im2dindbuf_uv2); +} + +void +closeim2d_uv2(void) +{ + using namespace rw; + using namespace d3d; + + d3d9::destroyVertexDeclaration(im2ddecl_uv2); + im2ddecl_uv2 = nil; + + removeDynamicVB(&im2dvertbuf_uv2); + destroyVertexBuffer(im2dvertbuf_uv2); + im2dvertbuf_uv2 = nil; + + removeDynamicIB(&im2dindbuf_uv2); + destroyIndexBuffer(im2dindbuf_uv2); + im2dindbuf_uv2 = nil; +} + +void +RenderIndexedPrimitive_UV2(RwPrimitiveType primType, Im2DVertexUV2 *vertices, RwInt32 numVertices, RwImVertexIndex *indices, RwInt32 numIndices) +{ + using namespace rw; + using namespace d3d; + + if(numVertices > NUMVERTICES || + numIndices > NUMINDICES){ + // TODO: error + return; + } + rw::uint16 *lockedindices = lockIndices(im2dindbuf_uv2, 0, numIndices*sizeof(rw::uint16), D3DLOCK_DISCARD); + memcpy(lockedindices, indices, numIndices*sizeof(rw::uint16)); + unlockIndices(im2dindbuf_uv2); + + rw::uint8 *lockedvertices = lockVertices(im2dvertbuf_uv2, 0, numVertices*sizeof(Im2DVertexUV2), D3DLOCK_DISCARD); + memcpy(lockedvertices, vertices, numVertices*sizeof(Im2DVertexUV2)); + unlockVertices(im2dvertbuf_uv2); + + setStreamSource(0, im2dvertbuf_uv2, 0, sizeof(Im2DVertexUV2)); + setIndices(im2dindbuf_uv2); + setVertexDeclaration(im2ddecl_uv2); + + if(im2dOverridePS) + setPixelShader(im2dOverridePS); + else if(engine->device.getRenderState(TEXTURERASTER)) + setPixelShader(im2d_tex_PS); + else + setPixelShader(im2d_PS); + + d3d::flushCache(); + + rw::uint32 primCount = 0; + switch(primType){ + case PRIMTYPELINELIST: + primCount = numIndices/2; + break; + case PRIMTYPEPOLYLINE: + primCount = numIndices-1; + break; + case PRIMTYPETRILIST: + primCount = numIndices/3; + break; + case PRIMTYPETRISTRIP: + primCount = numIndices-2; + break; + case PRIMTYPETRIFAN: + primCount = numIndices-2; + break; + case PRIMTYPEPOINTLIST: + primCount = numIndices; + break; + } + d3ddevice->DrawIndexedPrimitive((D3DPRIMITIVETYPE)primTypeMap[primType], 0, + 0, numVertices, + 0, primCount); +} +#endif + +#ifdef RW_GL3 +// different than im2d +#define NUMINDICES 1024 +#define NUMVERTICES 1024 + +static rw::gl3::AttribDesc im2d_UV2_attribDesc[4] = { + { rw::gl3::ATTRIB_POS, GL_FLOAT, GL_FALSE, 4, + sizeof(Im2DVertexUV2), 0 }, + { rw::gl3::ATTRIB_COLOR, GL_UNSIGNED_BYTE, GL_TRUE, 4, + sizeof(Im2DVertexUV2), offsetof(Im2DVertexUV2, r) }, + { rw::gl3::ATTRIB_TEXCOORDS0, GL_FLOAT, GL_FALSE, 2, + sizeof(Im2DVertexUV2), offsetof(Im2DVertexUV2, u) }, + { rw::gl3::ATTRIB_TEXCOORDS1, GL_FLOAT, GL_FALSE, 2, + sizeof(Im2DVertexUV2), offsetof(Im2DVertexUV2, u2) } +}; + +static int primTypeMap[] = { + GL_POINTS, // invalid + GL_LINES, + GL_LINE_STRIP, + GL_TRIANGLES, + GL_TRIANGLE_STRIP, + GL_TRIANGLE_FAN, + GL_POINTS +}; + +static int32 u_xform; + +uint32 im2D_UV2_Vbo, im2D_UV2_Ibo; +#ifdef RW_GL_USE_VAOS +uint32 im2D_UV2_Vao; +#endif + +void +openim2d_uv2(void) +{ + u_xform = rw::gl3::registerUniform("u_xform", rw::gl3::UNIFORM_VEC4); // this doesn't add a new one, so it's safe + + glGenBuffers(1, &im2D_UV2_Ibo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, im2D_UV2_Ibo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, NUMINDICES*2, nil, GL_STREAM_DRAW); + + glGenBuffers(1, &im2D_UV2_Vbo); + glBindBuffer(GL_ARRAY_BUFFER, im2D_UV2_Vbo); + glBufferData(GL_ARRAY_BUFFER, NUMVERTICES*sizeof(Im2DVertexUV2), nil, GL_STREAM_DRAW); + +#ifdef RW_GL_USE_VAOS + glGenVertexArrays(1, &im2D_UV2_Vao); + glBindVertexArray(im2D_UV2_Vao); + setAttribPointers(im2d_UV2_attribDesc, 4); +#endif +} + +void +closeim2d_uv2(void) +{ + glDeleteBuffers(1, &im2D_UV2_Ibo); + glDeleteBuffers(1, &im2D_UV2_Vbo); +#ifdef RW_GL_USE_VAOS + glDeleteVertexArrays(1, &im2D_UV2_Vao); +#endif +} + +void +RenderIndexedPrimitive_UV2(RwPrimitiveType primType, Im2DVertexUV2 *vertices, RwInt32 numVertices, RwImVertexIndex *indices, RwInt32 numIndices) +{ + using namespace rw; + using namespace gl3; + + GLfloat xform[4]; + Camera *cam; + cam = (Camera*)engine->currentCamera; + +#ifdef RW_GL_USE_VAOS + glBindVertexArray(im2D_UV2_Vao); +#endif + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, im2D_UV2_Ibo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, NUMINDICES*2, nil, GL_STREAM_DRAW); + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, numIndices*2, indices); + + glBindBuffer(GL_ARRAY_BUFFER, im2D_UV2_Vbo); + glBufferData(GL_ARRAY_BUFFER, NUMVERTICES*sizeof(Im2DVertexUV2), nil, GL_STREAM_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, numVertices*sizeof(Im2DVertexUV2), vertices); + + xform[0] = 2.0f/cam->frameBuffer->width; + xform[1] = -2.0f/cam->frameBuffer->height; + xform[2] = -1.0f; + xform[3] = 1.0f; + + if(im2dOverrideShader) + im2dOverrideShader->use(); + else + assert(0);//im2dShader->use(); +#ifndef RW_GL_USE_VAOS + setAttribPointers(im2d_UV2_attribDesc, 4); +#endif + + setUniform(u_xform, xform); + + flushCache(); + glDrawElements(primTypeMap[primType], numIndices, + GL_UNSIGNED_SHORT, nil); +#ifndef RW_GL_USE_VAOS + disableAttribPointers(im2d_UV2_attribDesc, 4); +#endif +} +#endif + +#endif diff --git a/src/miami/extras/screendroplets.h b/src/miami/extras/screendroplets.h new file mode 100644 index 00000000..090b1923 --- /dev/null +++ b/src/miami/extras/screendroplets.h @@ -0,0 +1,78 @@ +#pragma once + +#ifdef SCREEN_DROPLETS + +class CParticleObject; + +class ScreenDroplets +{ +public: + enum { + MAXDROPS = 2000, + MAXDROPSMOVING = 700 + }; + + class ScreenDrop + { + public: + float x, y, time; // shorts on xbox (short float?) + float size, magnification, lifetime; // " + CRGBA color; + bool active; + bool fades; + + void Fade(void); + }; + + struct ScreenDropMoving + { + ScreenDrop *drop; + float dist; + }; + + static int ms_initialised; + static RwTexture *ms_maskTex; + static RwTexture *ms_screenTex; + + static bool ms_enabled; + static bool ms_movingEnabled; + + static ScreenDrop ms_drops[MAXDROPS]; + static int ms_numDrops; + static ScreenDropMoving ms_dropsMoving[MAXDROPSMOVING]; + static int ms_numDropsMoving; + + static CVector ms_prevCamUp; + static CVector ms_prevCamPos; + static CVector ms_camMoveDelta; + static float ms_camMoveDist; + static CVector ms_screenMoveDelta; + static float ms_screenMoveDist; + static float ms_camUpAngle; + + static int ms_splashDuration; + static CParticleObject *ms_splashObject; + + static void Initialise(void); + static void InitDraw(void); + static void Shutdown(void); + static void Process(void); + static void Render(void); + static void AddToRenderList(ScreenDrop *drop); + + static void Clear(void); + static ScreenDrop *NewDrop(float x, float y, float size, float lifetime, bool fades, int r = 255, int g = 255, int b = 255); + static void SetMoving(ScreenDroplets::ScreenDrop *drop); + static void FillScreen(int n); + static void FillScreenMoving(float amount, bool isBlood = false); + static void RegisterSplash(CParticleObject *pobj); + + static void ProcessCameraMovement(void); + static void SprayDrops(void); + static void NewTrace(ScreenDroplets::ScreenDropMoving *moving); + static void MoveDrop(ScreenDropMoving *moving); + static void ProcessMoving(void); + static void Fade(void); +}; + +#endif diff --git a/src/miami/extras/shaders/colourfilterVC.frag b/src/miami/extras/shaders/colourfilterVC.frag new file mode 100644 index 00000000..283aa817 --- /dev/null +++ b/src/miami/extras/shaders/colourfilterVC.frag @@ -0,0 +1,27 @@ +uniform sampler2D tex0; +uniform vec4 u_blurcolor; + +FSIN vec4 v_color; +FSIN vec2 v_tex0; +FSIN float v_fog; + +void +main(void) +{ + float a = u_blurcolor.a; + vec4 doublec = clamp(u_blurcolor*2.0, 0.0, 1.0); + vec4 dst = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y)); + vec4 prev = dst; + for(int i = 0; i < 5; i++){ + vec4 tmp = dst*(1.0-a) + prev*doublec*a; + tmp += prev*u_blurcolor; + tmp += prev*u_blurcolor; + prev = clamp(tmp, 0.0, 1.0); + } + vec4 color; + color.rgb = prev.rgb; + color.a = 1.0; + + FRAGCOLOR(color); +} + diff --git a/src/miami/extras/shaders/colourfilterVC_PS.hlsl b/src/miami/extras/shaders/colourfilterVC_PS.hlsl new file mode 100644 index 00000000..90d3b50c --- /dev/null +++ b/src/miami/extras/shaders/colourfilterVC_PS.hlsl @@ -0,0 +1,23 @@ +sampler2D tex : register(s0); +float4 blurcol : register(c10); + +//float4 blurcols[10] : register(c15); + + +float4 main(in float2 texcoord : TEXCOORD0) : COLOR0 +{ + float a = blurcol.a; + + float4 doublec = saturate(blurcol*2); + float4 dst = tex2D(tex, texcoord.xy); + float4 prev = dst; + for(int i = 0; i < 5; i++){ +// float4 doublec = saturate(blurcol*2); + float4 tmp = dst*(1-a) + prev*doublec*a; + tmp += prev*blurcol; + tmp += prev*blurcol; + prev = saturate(tmp); + } + prev.a = 1.0; + return prev; +} diff --git a/src/miami/extras/shaders/contrast.frag b/src/miami/extras/shaders/contrast.frag new file mode 100644 index 00000000..2d394f66 --- /dev/null +++ b/src/miami/extras/shaders/contrast.frag @@ -0,0 +1,19 @@ +uniform sampler2D tex0; +uniform vec3 u_contrastAdd; +uniform vec3 u_contrastMult; + +FSIN vec4 v_color; +FSIN vec2 v_tex0; +FSIN float v_fog; + +void +main(void) +{ + vec4 dst = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y)); + vec4 color; + color.rgb = dst.rgb*u_contrastMult + u_contrastAdd; + color.a = 1.0; + + FRAGCOLOR(color); +} + diff --git a/src/miami/extras/shaders/contrastPS.hlsl b/src/miami/extras/shaders/contrastPS.hlsl new file mode 100644 index 00000000..a1de1d81 --- /dev/null +++ b/src/miami/extras/shaders/contrastPS.hlsl @@ -0,0 +1,21 @@ +struct PS_INPUT +{ + float4 position : POSITION; + float3 texcoord0 : TEXCOORD0; + float4 color : COLOR0; +}; + +uniform float3 contrastMult : register(c10); +uniform float3 contrastAdd : register(c11); + +sampler2D tex : register(s0); + +float4 +main(PS_INPUT In) : COLOR +{ + float4 dst = tex2D(tex, In.texcoord0.xy); + + dst.rgb = dst.rgb*contrastMult + contrastAdd; + dst.a = 1.0; + return dst; +} diff --git a/src/miami/extras/shaders/default_UV2.vert b/src/miami/extras/shaders/default_UV2.vert new file mode 100644 index 00000000..694c012b --- /dev/null +++ b/src/miami/extras/shaders/default_UV2.vert @@ -0,0 +1,25 @@ +VSIN(ATTRIB_POS) vec3 in_pos; + +VSOUT vec4 v_color; +VSOUT vec2 v_tex0; +VSOUT vec2 v_tex1; +VSOUT float v_fog; + +void +main(void) +{ + vec4 Vertex = u_world * vec4(in_pos, 1.0); + gl_Position = u_proj * u_view * Vertex; + vec3 Normal = mat3(u_world) * in_normal; + + v_tex0 = in_tex0; + v_tex1 = in_tex1; + + v_color = in_color; + v_color.rgb += u_ambLight.rgb*surfAmbient; + v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse; + v_color = clamp(v_color, 0.0, 1.0); + v_color *= u_matColor; + + v_fog = DoFog(gl_Position.w); +} diff --git a/src/miami/extras/shaders/default_UV2_VS.hlsl b/src/miami/extras/shaders/default_UV2_VS.hlsl new file mode 100644 index 00000000..e78a9907 --- /dev/null +++ b/src/miami/extras/shaders/default_UV2_VS.hlsl @@ -0,0 +1,54 @@ +#include "standardConstants.h" + +struct VS_in +{ + float4 Position : POSITION; + float3 Normal : NORMAL; + float2 TexCoord : TEXCOORD0; + float2 TexCoord1 : TEXCOORD1; + float4 Prelight : COLOR0; +}; + +struct VS_out { + float4 Position : POSITION; + float3 TexCoord0 : TEXCOORD0; // also fog + float2 TexCoord1 : TEXCOORD1; + float4 Color : COLOR0; +}; + + +VS_out main(in VS_in input) +{ + VS_out output; + + output.Position = mul(combinedMat, input.Position); + float3 Vertex = mul(worldMat, input.Position).xyz; + float3 Normal = mul(normalMat, input.Normal); + + output.TexCoord0.xy = input.TexCoord; + output.TexCoord1.xy = input.TexCoord1; + + output.Color = input.Prelight; + output.Color.rgb += ambientLight.rgb * surfAmbient; + + int i; +#ifdef DIRECTIONALS + for(i = 0; i < numDirLights; i++) + output.Color.xyz += DoDirLight(lights[i+firstDirLight], Normal)*surfDiffuse; +#endif +#ifdef POINTLIGHTS + for(i = 0; i < numPointLights; i++) + output.Color.xyz += DoPointLight(lights[i+firstPointLight], Vertex.xyz, Normal)*surfDiffuse; +#endif +#ifdef SPOTLIGHTS + for(i = 0; i < numSpotLights; i++) + output.Color.xyz += DoSpotLight(lights[i+firstSpotLight], Vertex.xyz, Normal)*surfDiffuse; +#endif + // PS2 clamps before material color + output.Color = clamp(output.Color, 0.0, 1.0); + output.Color *= matCol; + + output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0); + + return output; +} diff --git a/src/miami/extras/shaders/im2d.vert b/src/miami/extras/shaders/im2d.vert new file mode 100644 index 00000000..fcd81c2c --- /dev/null +++ b/src/miami/extras/shaders/im2d.vert @@ -0,0 +1,19 @@ +uniform vec4 u_xform; + +VSIN(ATTRIB_POS) vec4 in_pos; + +VSOUT vec4 v_color; +VSOUT vec2 v_tex0; +VSOUT float v_fog; + +void +main(void) +{ + gl_Position = in_pos; + gl_Position.w = 1.0; + gl_Position.xy = gl_Position.xy * u_xform.xy + u_xform.zw; + v_fog = DoFog(gl_Position.z); + gl_Position.xyz *= gl_Position.w; + v_color = in_color; + v_tex0 = in_tex0; +} diff --git a/src/miami/extras/shaders/im2d_UV2.vert b/src/miami/extras/shaders/im2d_UV2.vert new file mode 100644 index 00000000..e5fd4d08 --- /dev/null +++ b/src/miami/extras/shaders/im2d_UV2.vert @@ -0,0 +1,21 @@ +uniform vec4 u_xform; + +VSIN(ATTRIB_POS) vec4 in_pos; + +VSOUT vec4 v_color; +VSOUT vec2 v_tex0; +VSOUT vec2 v_tex1; +VSOUT float v_fog; + +void +main(void) +{ + gl_Position = in_pos; + gl_Position.w = 1.0; + gl_Position.xy = gl_Position.xy * u_xform.xy + u_xform.zw; + v_fog = DoFog(gl_Position.z); + gl_Position.xyz *= gl_Position.w; + v_color = in_color; + v_tex0 = in_tex0; + v_tex1 = in_tex1; +} diff --git a/src/miami/extras/shaders/lighting.h b/src/miami/extras/shaders/lighting.h new file mode 100644 index 00000000..4b081962 --- /dev/null +++ b/src/miami/extras/shaders/lighting.h @@ -0,0 +1,44 @@ +struct Light +{ + float4 color; // and radius + float4 position; // and -cos(angle) + float4 direction; // and falloff clamp +}; + +float3 DoDirLight(Light L, float3 N) +{ + float l = max(0.0, dot(N, -L.direction.xyz)); + return l*L.color.xyz; +} + +float3 DoDirLightSpec(Light L, float3 N, float3 V, float power) +{ + return pow(saturate(dot(N, normalize(V + -L.direction.xyz))), power)*L.color.xyz; +} + +float3 DoPointLight(Light L, float3 V, float3 N) +{ + // As on PS2 + float3 dir = V - L.position.xyz; + float dist = length(dir); + float atten = max(0.0, (1.0 - dist/L.color.w)); + float l = max(0.0, dot(N, -normalize(dir))); + return l*L.color.xyz*atten; +} + +float3 DoSpotLight(Light L, float3 V, float3 N) +{ + // As on PS2 + float3 dir = V - L.position.xyz; + float dist = length(dir); + float atten = max(0.0, (1.0 - dist/L.color.w)); + dir /= dist; + float l = max(0.0, dot(N, -dir)); + float pcos = dot(dir, L.direction.xyz); // cos to point + float ccos = -L.position.w; // cos of cone + float falloff = (pcos-ccos)/(1.0-ccos); + if(falloff < 0) // outside of cone + l = 0; + l *= max(falloff, L.direction.w); // falloff clamp + return l*L.color.xyz*atten; +} diff --git a/src/miami/extras/shaders/make_glsl.sh b/src/miami/extras/shaders/make_glsl.sh new file mode 100644 index 00000000..0af98961 --- /dev/null +++ b/src/miami/extras/shaders/make_glsl.sh @@ -0,0 +1,9 @@ +#!sh +for i in *.vert; do + echo $i + ./makeinc_glsl.sh $i +done +for i in *.frag; do + echo $i + ./makeinc_glsl.sh $i +done diff --git a/src/miami/extras/shaders/make_hlsl.cmd b/src/miami/extras/shaders/make_hlsl.cmd new file mode 100644 index 00000000..dee95283 --- /dev/null +++ b/src/miami/extras/shaders/make_hlsl.cmd @@ -0,0 +1,3 @@ +@echo off +for %%f in (*PS.hlsl) do "%DXSDK_DIR%\Utilities\bin\x86\fxc.exe" /T ps_2_0 /nologo /E main /Fo obj\%%~nf.cso %%f +for %%f in (*VS.hlsl) do "%DXSDK_DIR%\Utilities\bin\x86\fxc.exe" /T vs_2_0 /nologo /E main /Fo obj\%%~nf.cso %%f diff --git a/src/miami/extras/shaders/makeinc_glsl.sh b/src/miami/extras/shaders/makeinc_glsl.sh new file mode 100644 index 00000000..2bc6a387 --- /dev/null +++ b/src/miami/extras/shaders/makeinc_glsl.sh @@ -0,0 +1,6 @@ +#!sh +ext=${1##*.} +name=${1%.*} +(echo "const char *${name}_${ext}_src =";\ +sed 's/..*/"&\\n"/' $1;\ +echo ';') > obj/${name}_${ext}.inc diff --git a/src/miami/extras/shaders/makeinc_hlsl.sh b/src/miami/extras/shaders/makeinc_hlsl.sh new file mode 100644 index 00000000..a5b12867 --- /dev/null +++ b/src/miami/extras/shaders/makeinc_hlsl.sh @@ -0,0 +1,6 @@ +#!sh +cd obj +for i in *cso; do + (echo -n 'static ' + xxd -i $i | grep -v '_len = ') > ${i%cso}inc +done diff --git a/src/miami/extras/shaders/neoGloss.frag b/src/miami/extras/shaders/neoGloss.frag new file mode 100644 index 00000000..4f097b0b --- /dev/null +++ b/src/miami/extras/shaders/neoGloss.frag @@ -0,0 +1,26 @@ +uniform sampler2D tex0; + +uniform vec4 u_reflProps; + +#define glossMult (u_reflProps.x) + +FSIN vec3 v_normal; +FSIN vec3 v_light; +FSIN vec2 v_tex0; +FSIN float v_fog; + +void +main(void) +{ + vec4 color = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y)); + vec3 n = 2.0*v_normal-1.0; // unpack + vec3 v = 2.0*v_light-1.0; // + + float s = dot(n, v); + color = s*s*s*s*s*s*s*s*color*v_fog*glossMult; + + DoAlphaTest(color.a); + + FRAGCOLOR(color); +} + diff --git a/src/miami/extras/shaders/neoGloss.vert b/src/miami/extras/shaders/neoGloss.vert new file mode 100644 index 00000000..41102f3f --- /dev/null +++ b/src/miami/extras/shaders/neoGloss.vert @@ -0,0 +1,25 @@ +uniform vec3 u_eye; + +VSIN(ATTRIB_POS) vec3 in_pos; + +VSOUT vec3 v_normal; +VSOUT vec3 v_light; +VSOUT vec2 v_tex0; +VSOUT float v_fog; + +void +main(void) +{ + vec4 Vertex = u_world * vec4(in_pos, 1.0); + gl_Position = u_proj * u_view * Vertex; + vec3 Normal = mat3(u_world) * in_normal; + + v_tex0 = in_tex0; + + vec3 viewVec = normalize(u_eye - Vertex.xyz); + vec3 Light = normalize(viewVec - u_lightDirection[0].xyz); + v_normal = 0.5*(1.0 + vec3(0.0, 0.0, 1.0)); // compress + v_light = 0.5*(1.0 + Light); // + + v_fog = DoFog(gl_Position.w); +} diff --git a/src/miami/extras/shaders/neoGloss_PS.hlsl b/src/miami/extras/shaders/neoGloss_PS.hlsl new file mode 100644 index 00000000..b3c97639 --- /dev/null +++ b/src/miami/extras/shaders/neoGloss_PS.hlsl @@ -0,0 +1,20 @@ +sampler2D tex0 : register(s0); +float glossMult : register(c1); + +struct VS_out +{ + float4 Position : POSITION; + float3 TexCoord0 : TEXCOORD0; + float3 Normal : COLOR0; + float3 Light : COLOR1; +}; + +float4 main(VS_out input) : COLOR +{ + float4 color = tex2D(tex0, input.TexCoord0.xy); + float3 n = 2.0*input.Normal-1.0; // unpack + float3 v = 2.0*input.Light-1.0; // + + float s = dot(n, v); + return s*s*s*s*s*s*s*s*color*input.TexCoord0.z*glossMult; +} diff --git a/src/miami/extras/shaders/neoGloss_VS.hlsl b/src/miami/extras/shaders/neoGloss_VS.hlsl new file mode 100644 index 00000000..d166171c --- /dev/null +++ b/src/miami/extras/shaders/neoGloss_VS.hlsl @@ -0,0 +1,35 @@ +#include "standardConstants.h" + +struct VS_in +{ + float4 Position : POSITION; + float2 TexCoord : TEXCOORD0; +}; + +struct VS_out +{ + float4 Position : POSITION; + float3 TexCoord0 : TEXCOORD0; + float3 Normal : COLOR0; + float3 Light : COLOR1; +}; + +float3 eye : register(c41); + +VS_out main(in VS_in input) +{ + VS_out output; + + output.Position = mul(combinedMat, input.Position); + float3 Vertex = mul(worldMat, input.Position).xyz; + output.TexCoord0.xy = input.TexCoord; + + float3 viewVec = normalize(eye - Vertex); + float3 Light = normalize(viewVec - lights[0].direction.xyz); + output.Normal = 0.5*(1.0 + float3(0.0, 0.0, 1.0)); // compress + output.Light = 0.5*(1.0 + Light); // + + output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0); + + return output; +} diff --git a/src/miami/extras/shaders/neoRim.vert b/src/miami/extras/shaders/neoRim.vert new file mode 100644 index 00000000..81ee1090 --- /dev/null +++ b/src/miami/extras/shaders/neoRim.vert @@ -0,0 +1,34 @@ +uniform vec3 u_viewVec; +uniform vec4 u_rampStart; +uniform vec4 u_rampEnd; +uniform vec3 u_rimData; + +VSIN(ATTRIB_POS) vec3 in_pos; + +VSOUT vec4 v_color; +VSOUT vec2 v_tex0; +VSOUT float v_fog; + +void +main(void) +{ + vec4 Vertex = u_world * vec4(in_pos, 1.0); + gl_Position = u_proj * u_view * Vertex; + vec3 Normal = mat3(u_world) * in_normal; + + v_tex0 = in_tex0; + + v_color = in_color; + v_color.rgb += u_ambLight.rgb*surfAmbient; + v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse; + + // rim light + float f = u_rimData.x - u_rimData.y*dot(Normal, u_viewVec); + vec4 rimlight = clamp(mix(u_rampEnd, u_rampStart, f)*u_rimData.z, 0.0, 1.0); + v_color.rgb += rimlight.rgb; + + v_color = clamp(v_color, 0.0, 1.0); + v_color *= u_matColor; + + v_fog = DoFog(gl_Position.w); +} diff --git a/src/miami/extras/shaders/neoRimSkin.vert b/src/miami/extras/shaders/neoRimSkin.vert new file mode 100644 index 00000000..1515ad71 --- /dev/null +++ b/src/miami/extras/shaders/neoRimSkin.vert @@ -0,0 +1,43 @@ +uniform mat4 u_boneMatrices[64]; + +uniform vec3 u_viewVec; +uniform vec4 u_rampStart; +uniform vec4 u_rampEnd; +uniform vec3 u_rimData; + +VSIN(ATTRIB_POS) vec3 in_pos; + +VSOUT vec4 v_color; +VSOUT vec2 v_tex0; +VSOUT float v_fog; + +void +main(void) +{ + vec3 SkinVertex = vec3(0.0, 0.0, 0.0); + vec3 SkinNormal = vec3(0.0, 0.0, 0.0); + for(int i = 0; i < 4; i++){ + SkinVertex += (u_boneMatrices[int(in_indices[i])] * vec4(in_pos, 1.0)).xyz * in_weights[i]; + SkinNormal += (mat3(u_boneMatrices[int(in_indices[i])]) * in_normal) * in_weights[i]; + } + + vec4 Vertex = u_world * vec4(SkinVertex, 1.0); + gl_Position = u_proj * u_view * Vertex; + vec3 Normal = mat3(u_world) * SkinNormal; + + v_tex0 = in_tex0; + + v_color = in_color; + v_color.rgb += u_ambLight.rgb*surfAmbient; + v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse; + + // rim light + float f = u_rimData.x - u_rimData.y*dot(Normal, u_viewVec); + vec4 rimlight = clamp(mix(u_rampEnd, u_rampStart, f)*u_rimData.z, 0.0, 1.0); + v_color.rgb += rimlight.rgb; + + v_color = clamp(v_color, 0.0, 1.0); + v_color *= u_matColor; + + v_fog = DoFog(gl_Position.z); +} diff --git a/src/miami/extras/shaders/neoRimSkin_VS.hlsl b/src/miami/extras/shaders/neoRimSkin_VS.hlsl new file mode 100644 index 00000000..87cc0931 --- /dev/null +++ b/src/miami/extras/shaders/neoRimSkin_VS.hlsl @@ -0,0 +1,73 @@ +#include "standardConstants.h" + +float4x3 boneMatrices[64] : register(c41); + +struct VS_in +{ + float4 Position : POSITION; + float3 Normal : NORMAL; + float2 TexCoord : TEXCOORD0; + float4 Prelight : COLOR0; + float4 Weights : BLENDWEIGHT; + int4 Indices : BLENDINDICES; +}; + +struct VS_out { + float4 Position : POSITION; + float3 TexCoord0 : TEXCOORD0; // also fog + float4 Color : COLOR0; +}; + +float3 viewVec : register(c233); +float4 rampStart : register(c234); +float4 rampEnd : register(c235); +float3 rimData : register(c236); + +VS_out main(in VS_in input) +{ + VS_out output; + + int j; + float3 SkinVertex = float3(0.0, 0.0, 0.0); + float3 SkinNormal = float3(0.0, 0.0, 0.0); + for(j = 0; j < 4; j++){ + SkinVertex += mul(input.Position, boneMatrices[input.Indices[j]]).xyz * input.Weights[j]; + SkinNormal += mul(input.Normal, (float3x3)boneMatrices[input.Indices[j]]).xyz * input.Weights[j]; + } + + output.Position = mul(combinedMat, float4(SkinVertex, 1.0)); + float3 Vertex = mul(worldMat, float4(SkinVertex, 1.0)).xyz; + float3 Normal = mul(normalMat, SkinNormal); + + output.TexCoord0.xy = input.TexCoord; + + output.Color = input.Prelight; + output.Color.rgb += ambientLight.rgb * surfAmbient; + + int i; +//#ifdef DIRECTIONALS + for(i = 0; i < numDirLights; i++) + output.Color.xyz += DoDirLight(lights[i+firstDirLight], Normal)*surfDiffuse; +//#endif +//#ifdef POINTLIGHTS +// for(i = 0; i < numPointLights; i++) +// output.Color.xyz += DoPointLight(lights[i+firstPointLight], Vertex.xyz, Normal)*surfDiffuse; +//#endif +//#ifdef SPOTLIGHTS +// for(i = 0; i < numSpotLights; i++) +// output.Color.xyz += DoSpotLight(lights[i+firstSpotLight], Vertex.xyz, Normal)*surfDiffuse; +//#endif + + // rim light + float f = rimData.x - rimData.y*dot(Normal, viewVec); + float4 rimlight = saturate(lerp(rampEnd, rampStart, f)*rimData.z); + output.Color.xyz += rimlight.xyz; + + // PS2 clamps before material color + output.Color = clamp(output.Color, 0.0, 1.0); + output.Color *= matCol; + + output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0); + + return output; +} diff --git a/src/miami/extras/shaders/neoRim_VS.hlsl b/src/miami/extras/shaders/neoRim_VS.hlsl new file mode 100644 index 00000000..7f95166d --- /dev/null +++ b/src/miami/extras/shaders/neoRim_VS.hlsl @@ -0,0 +1,61 @@ +#include "standardConstants.h" + +struct VS_in +{ + float4 Position : POSITION; + float3 Normal : NORMAL; + float2 TexCoord : TEXCOORD0; + float4 Prelight : COLOR0; +}; + +struct VS_out { + float4 Position : POSITION; + float3 TexCoord0 : TEXCOORD0; // also fog + float4 Color : COLOR0; +}; + +float3 viewVec : register(c233); +float4 rampStart : register(c234); +float4 rampEnd : register(c235); +float3 rimData : register(c236); + +VS_out main(in VS_in input) +{ + VS_out output; + + output.Position = mul(combinedMat, input.Position); + float3 Vertex = mul(worldMat, input.Position).xyz; + float3 Normal = mul(normalMat, input.Normal); + + output.TexCoord0.xy = input.TexCoord; + + output.Color = input.Prelight; + output.Color.rgb += ambientLight.rgb * surfAmbient; + + int i; +//#ifdef DIRECTIONALS + for(i = 0; i < numDirLights; i++) + output.Color.xyz += DoDirLight(lights[i+firstDirLight], Normal)*surfDiffuse; +//#endif +//#ifdef POINTLIGHTS +// for(i = 0; i < numPointLights; i++) +// output.Color.xyz += DoPointLight(lights[i+firstPointLight], Vertex.xyz, Normal)*surfDiffuse; +//#endif +//#ifdef SPOTLIGHTS +// for(i = 0; i < numSpotLights; i++) +// output.Color.xyz += DoSpotLight(lights[i+firstSpotLight], Vertex.xyz, Normal)*surfDiffuse; +//#endif + + // rim light + float f = rimData.x - rimData.y*dot(Normal, viewVec); + float4 rimlight = saturate(lerp(rampEnd, rampStart, f)*rimData.z); + output.Color.xyz += rimlight.xyz; + + // PS2 clamps before material color + output.Color = clamp(output.Color, 0.0, 1.0); + output.Color *= matCol; + + output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0); + + return output; +} diff --git a/src/miami/extras/shaders/neoVehicle.frag b/src/miami/extras/shaders/neoVehicle.frag new file mode 100644 index 00000000..2ac24f70 --- /dev/null +++ b/src/miami/extras/shaders/neoVehicle.frag @@ -0,0 +1,29 @@ +uniform sampler2D tex0; +uniform sampler2D tex1; + +FSIN vec4 v_color; +FSIN vec4 v_reflcolor; +FSIN vec2 v_tex0; +FSIN vec2 v_tex1; +FSIN float v_fog; + +void +main(void) +{ + vec4 pass1 = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y)); + vec3 envmap = texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y)).rgb; + pass1.rgb = mix(pass1.rgb, envmap, v_reflcolor.a); + pass1.rgb = mix(u_fogColor.rgb, pass1.rgb, v_fog); +// pass1.rgb += v_reflcolor.rgb * v_fog; + + vec3 pass2 = v_reflcolor.rgb * v_fog; + + vec4 color; + color.rgb = pass1.rgb*pass1.a + pass2; + color.a = pass1.a; + +// color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog); + DoAlphaTest(color.a); + + FRAGCOLOR(color); +} diff --git a/src/miami/extras/shaders/neoVehicle.vert b/src/miami/extras/shaders/neoVehicle.vert new file mode 100644 index 00000000..6985a689 --- /dev/null +++ b/src/miami/extras/shaders/neoVehicle.vert @@ -0,0 +1,51 @@ +uniform vec3 u_eye; +uniform vec4 u_reflProps; +uniform vec4 u_specDir[5]; +uniform vec4 u_specColor[5]; + +#define fresnel (u_reflProps.x) +#define lightStrength (u_reflProps.y) // speclight alpha +#define shininess (u_reflProps.z) +#define specularity (u_reflProps.w) + +VSIN(ATTRIB_POS) vec3 in_pos; + +VSOUT vec4 v_color; +VSOUT vec4 v_reflcolor; +VSOUT vec2 v_tex0; +VSOUT vec2 v_tex1; +VSOUT float v_fog; + +vec3 DoDirLightSpec(vec3 Ldir, vec3 Lcol, vec3 N, vec3 V, float power) +{ + return pow(clamp(dot(N, normalize(V + -Ldir)), 0.0, 1.0), power)*Lcol; +} + +void +main(void) +{ + vec4 Vertex = u_world * vec4(in_pos, 1.0); + gl_Position = u_proj * u_view * Vertex; + vec3 Normal = mat3(u_world) * in_normal; + vec3 viewVec = normalize(u_eye - Vertex.xyz); + + v_tex0 = in_tex0; + + v_color = in_color; + v_color.rgb += u_ambLight.rgb*surfAmbient; + v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse*lightStrength; + v_color = clamp(v_color, 0.0, 1.0); + v_color *= u_matColor; + + // reflect V along Normal + vec3 uv2 = Normal*dot(viewVec, Normal)*2.0 - viewVec; + v_tex1 = uv2.xy*0.5 + 0.5; + float b = 1.0 - clamp(dot(viewVec, Normal), 0.0, 1.0); + v_reflcolor = vec4(0.0, 0.0, 0.0, 1.0); + v_reflcolor.a = mix(b*b*b*b*b, 1.0, fresnel)*shininess; + + for(int i = 0; i < 5; i++) + v_reflcolor.rgb += DoDirLightSpec(u_specDir[i].xyz, u_specColor[i].rgb, Normal, viewVec, u_specDir[i].w)*specularity*lightStrength; + + v_fog = DoFog(gl_Position.w); +} diff --git a/src/miami/extras/shaders/neoVehicle_PS.hlsl b/src/miami/extras/shaders/neoVehicle_PS.hlsl new file mode 100644 index 00000000..fa030dd6 --- /dev/null +++ b/src/miami/extras/shaders/neoVehicle_PS.hlsl @@ -0,0 +1,34 @@ +struct VS_out { + float4 Position : POSITION; + float3 TexCoord0 : TEXCOORD0; + float2 TexCoord1 : TEXCOORD1; + float4 Color : COLOR0; + float4 ReflColor : COLOR1; +}; + +sampler2D tex0 : register(s0); +sampler2D tex1 : register(s1); + +float4 fogColor : register(c0); + +float4 main(VS_out input) : COLOR +{ + float4 pass1 = input.Color; +//#ifdef TEX + pass1 *= tex2D(tex0, input.TexCoord0.xy); +//#endif + float3 envmap = tex2D(tex1, input.TexCoord1).rgb; + pass1.rgb = lerp(pass1.rgb, envmap, input.ReflColor.a); +// pass1.rgb = envmap; +// pass1.rgb *= input.ReflColor.a; + pass1.rgb = lerp(fogColor.rgb, pass1.rgb, input.TexCoord0.z); +// pass1.rgb += input.ReflColor.rgb * input.TexCoord0.z; + + float3 pass2 = input.ReflColor.rgb*input.TexCoord0.z; + + float4 color; + color.rgb = pass1.rgb*pass1.a + pass2; + color.a = pass1.a; + + return color; +} diff --git a/src/miami/extras/shaders/neoVehicle_VS.hlsl b/src/miami/extras/shaders/neoVehicle_VS.hlsl new file mode 100644 index 00000000..fb730092 --- /dev/null +++ b/src/miami/extras/shaders/neoVehicle_VS.hlsl @@ -0,0 +1,64 @@ +#include "standardConstants.h" + +struct VS_in +{ + float4 Position : POSITION; + float3 Normal : NORMAL; + float2 TexCoord : TEXCOORD0; + float4 Prelight : COLOR0; +}; + +struct VS_out { + float4 Position : POSITION; + float3 TexCoord0 : TEXCOORD0; // also fog + float2 TexCoord1 : TEXCOORD1; + float4 Color : COLOR0; + float4 ReflColor : COLOR1; +}; + +float3 eye : register(c41); +float4 reflProps : register(c42); +Light specLights[5] : register(c43); + + +#define fresnel (reflProps.x) +#define lightStrength (reflProps.y) // speclight alpha +#define shininess (reflProps.z) +#define specularity (reflProps.w) + +VS_out main(in VS_in input) +{ + VS_out output; + + output.Position = mul(combinedMat, input.Position); + float3 Vertex = mul(worldMat, input.Position).xyz; + float3 Normal = mul(normalMat, input.Normal); + float3 viewVec = normalize(eye - Vertex); + + output.TexCoord0.xy = input.TexCoord; + + output.Color = input.Prelight; + output.Color.rgb += ambientLight.rgb * surfAmbient*lightStrength; + + int i; + for(i = 0; i < numDirLights; i++) + output.Color.xyz += DoDirLight(lights[i+firstDirLight], Normal)*surfDiffuse*lightStrength; + // PS2 clamps before material color + output.Color = clamp(output.Color, 0.0, 1.0); + output.Color *= matCol; + + // reflect V along Normal + float3 uv2 = Normal*dot(viewVec, Normal)*2.0 - viewVec; + output.TexCoord1 = uv2.xy*0.5 + 0.5; + float b = 1.0 - saturate(dot(viewVec, Normal)); + output.ReflColor = float4(0.0, 0.0, 0.0, 1.0); + output.ReflColor.a = lerp(b*b*b*b*b, 1.0, fresnel)*shininess; + + //Light mainLight = lights[0]; + for(i = 0; i < 5; i++) + output.ReflColor.xyz += DoDirLightSpec(specLights[i], Normal, viewVec, specLights[i].direction.w)*specularity*lightStrength; + + output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0); + + return output; +} diff --git a/src/miami/extras/shaders/neoWorldVC.frag b/src/miami/extras/shaders/neoWorldVC.frag new file mode 100644 index 00000000..08cae743 --- /dev/null +++ b/src/miami/extras/shaders/neoWorldVC.frag @@ -0,0 +1,26 @@ +uniform sampler2D tex0; +uniform sampler2D tex1; + +uniform vec4 u_lightMap; + +FSIN vec4 v_color; +FSIN vec2 v_tex0; +FSIN vec2 v_tex1; +FSIN float v_fog; + +void +main(void) +{ + vec4 t0 = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y)); + vec4 t1 = texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y)); + + vec4 color; + color = t0*v_color*(1.0 + u_lightMap*(t1-1.0)); + color.a = v_color.a*t0.a*u_lightMap.a; + + color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog); + DoAlphaTest(color.a); + + FRAGCOLOR(color); +} + diff --git a/src/miami/extras/shaders/neoWorldVC_PS.hlsl b/src/miami/extras/shaders/neoWorldVC_PS.hlsl new file mode 100644 index 00000000..fc4f1de9 --- /dev/null +++ b/src/miami/extras/shaders/neoWorldVC_PS.hlsl @@ -0,0 +1,25 @@ +sampler2D Diffuse : register(s0); +sampler2D Light : register(s1); +float4 fogColor : register(c0); +float4 lm : register(c1); + +struct PS_INPUT +{ + float4 Color : COLOR0; + float3 Tex0 : TEXCOORD0; + float2 Tex1 : TEXCOORD1; +}; + +float4 +main(PS_INPUT IN) : COLOR +{ + float4 t0 = tex2D(Diffuse, IN.Tex0.xy); + float4 t1 = tex2D(Light, IN.Tex1); + + float4 col = t0*IN.Color*(1 + lm*(t1-1)); + col.a = IN.Color.a*t0.a*lm.a; + + col.rgb = lerp(fogColor.rgb, col.rgb, IN.Tex0.z); + + return col; +} diff --git a/src/miami/extras/shaders/screenDroplet.frag b/src/miami/extras/shaders/screenDroplet.frag new file mode 100644 index 00000000..84d30bd5 --- /dev/null +++ b/src/miami/extras/shaders/screenDroplet.frag @@ -0,0 +1,18 @@ +uniform sampler2D tex0; +uniform sampler2D tex1; + +FSIN vec4 v_color; +FSIN vec2 v_tex0; +FSIN vec2 v_tex1; +FSIN float v_fog; + +void +main(void) +{ + vec4 color; + color = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y)); + color *= texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y)); + + FRAGCOLOR(color); +} + diff --git a/src/miami/extras/shaders/screenDroplet_PS.hlsl b/src/miami/extras/shaders/screenDroplet_PS.hlsl new file mode 100644 index 00000000..4d41da68 --- /dev/null +++ b/src/miami/extras/shaders/screenDroplet_PS.hlsl @@ -0,0 +1,17 @@ +struct VS_out { + float4 Position : POSITION; + float2 TexCoord0 : TEXCOORD0; + float2 TexCoord1 : TEXCOORD1; + float4 Color : COLOR0; +}; + +sampler2D tex0 : register(s0); +sampler2D tex1 : register(s1); + +float4 main(VS_out input) : COLOR +{ + float4 color = input.Color; + color *= tex2D(tex0, input.TexCoord0.xy); + color *= tex2D(tex1, input.TexCoord1.xy); + return color; +} diff --git a/src/miami/extras/shaders/simple.frag b/src/miami/extras/shaders/simple.frag new file mode 100644 index 00000000..c85bf089 --- /dev/null +++ b/src/miami/extras/shaders/simple.frag @@ -0,0 +1,17 @@ +uniform sampler2D tex0; + +FSIN vec4 v_color; +FSIN vec2 v_tex0; +FSIN float v_fog; + +void +main(void) +{ + vec4 color; + color = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y)); + color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog); + DoAlphaTest(color.a); + + FRAGCOLOR(color); +} + diff --git a/src/miami/extras/shaders/standardConstants.h b/src/miami/extras/shaders/standardConstants.h new file mode 100644 index 00000000..088df7dd --- /dev/null +++ b/src/miami/extras/shaders/standardConstants.h @@ -0,0 +1,28 @@ +float4x4 combinedMat : register(c0); +float4x4 worldMat : register(c4); +float3x3 normalMat : register(c8); +float4 matCol : register(c12); +float4 surfProps : register(c13); +float4 fogData : register(c14); +float4 ambientLight : register(c15); + +#define surfAmbient (surfProps.x) +#define surfSpecular (surfProps.y) +#define surfDiffuse (surfProps.z) + +#define fogStart (fogData.x) +#define fogEnd (fogData.y) +#define fogRange (fogData.z) +#define fogDisable (fogData.w) + +#include "lighting.h" + +int numDirLights : register(i0); +int numPointLights : register(i1); +int numSpotLights : register(i2); +int4 firstLight : register(c16); +Light lights[8] : register(c17); + +#define firstDirLight (firstLight.x) +#define firstPointLight (firstLight.y) +#define firstSpotLight (firstLight.z) diff --git a/src/miami/fakerw/fake.cpp b/src/miami/fakerw/fake.cpp new file mode 100644 index 00000000..53c965a9 --- /dev/null +++ b/src/miami/fakerw/fake.cpp @@ -0,0 +1,1014 @@ +#define _CRT_SECURE_NO_WARNINGS +#define WITH_D3D // not WITHD3D, so it's librw define +#include +#include +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include "crossplatform.h" +#endif + +using namespace rw; + +RwUInt8 RwObjectGetType(const RwObject *obj) { return obj->type; } +RwFrame* rwObjectGetParent(const RwObject *obj) { return (RwFrame*)obj->parent; } + +void *RwMalloc(size_t size) { return engine->memfuncs.rwmalloc(size, 0); } +void *RwCalloc(size_t numObj, size_t sizeObj) { + void *mem = RwMalloc(numObj*sizeObj); + if(mem) + memset(mem, 0, numObj*sizeObj); + return mem; +} +void RwFree(void *mem) { engine->memfuncs.rwfree(mem); } + + +//RwReal RwV3dNormalize(RwV3d * out, const RwV3d * in); +RwReal RwV3dLength(const RwV3d * in) { return length(*in); } +//RwReal RwV2dLength(const RwV2d * in); +//RwReal RwV2dNormalize(RwV2d * out, const RwV2d * in); +//void RwV2dAssign(RwV2d * out, const RwV2d * ina); +//void RwV2dAdd(RwV2d * out, const RwV2d * ina, const RwV2d * inb); +//void RwV2dLineNormal(RwV2d * out, const RwV2d * ina, const RwV2d * inb); +//void RwV2dSub(RwV2d * out, const RwV2d * ina, const RwV2d * inb); +//void RwV2dPerp(RwV2d * out, const RwV2d * in); +//void RwV2dScale(RwV2d * out, const RwV2d * in, RwReal scalar); +//RwReal RwV2dDotProduct(const RwV2d * ina, const RwV2d * inb); +//void RwV3dAssign(RwV3d * out, const RwV3d * ina); +void RwV3dAdd(RwV3d * out, const RwV3d * ina, const RwV3d * inb) { *out = add(*ina, *inb); } +void RwV3dSub(RwV3d * out, const RwV3d * ina, const RwV3d * inb) { *out = sub(*ina, *inb); } +void RwV3dScale(RwV3d * out, const RwV3d * in, RwReal scalar) { *out = scale(*in, scalar); } +void RwV3dIncrementScaled(RwV3d * out, const RwV3d * in, RwReal scalar) { *out = add(*out, scale(*in, scalar)); } +void RwV3dNegate(RwV3d * out, const RwV3d * in) { *out = neg(*in); } +RwReal RwV3dDotProduct(const RwV3d * ina, const RwV3d * inb) { return dot(*ina, *inb); } +//void RwV3dCrossProduct(RwV3d * out, const RwV3d * ina, const RwV3d * inb); +RwV3d *RwV3dTransformPoints(RwV3d * pointsOut, const RwV3d * pointsIn, RwInt32 numPoints, const RwMatrix * matrix) + { V3d::transformPoints(pointsOut, pointsIn, numPoints, matrix); return pointsOut; } +//RwV3d *RwV3dTransformVectors(RwV3d * vectorsOut, const RwV3d * vectorsIn, RwInt32 numPoints, const RwMatrix * matrix); + + + +RwBool RwMatrixDestroy(RwMatrix *mpMat) { mpMat->destroy(); return true; } +RwMatrix *RwMatrixCreate(void) { return Matrix::create(); } +void RwMatrixCopy(RwMatrix * dstMatrix, const RwMatrix * srcMatrix) { *dstMatrix = *srcMatrix; } +void RwMatrixSetIdentity(RwMatrix * matrix) { matrix->setIdentity(); } +RwMatrix *RwMatrixMultiply(RwMatrix * matrixOut, const RwMatrix * MatrixIn1, const RwMatrix * matrixIn2); +RwMatrix *RwMatrixTransform(RwMatrix * matrix, const RwMatrix * transform, RwOpCombineType combineOp) + { matrix->transform(transform, (rw::CombineOp)combineOp); return matrix; } +//RwMatrix *RwMatrixOrthoNormalize(RwMatrix * matrixOut, const RwMatrix * matrixIn); +RwMatrix *RwMatrixInvert(RwMatrix * matrixOut, const RwMatrix * matrixIn) { Matrix::invert(matrixOut, matrixIn); return matrixOut; } +RwMatrix *RwMatrixScale(RwMatrix * matrix, const RwV3d * scale, RwOpCombineType combineOp) + { matrix->scale(scale, (rw::CombineOp)combineOp); return matrix; } +RwMatrix *RwMatrixTranslate(RwMatrix * matrix, const RwV3d * translation, RwOpCombineType combineOp) + { matrix->translate(translation, (rw::CombineOp)combineOp); return matrix; } +RwMatrix *RwMatrixRotate(RwMatrix * matrix, const RwV3d * axis, RwReal angle, RwOpCombineType combineOp) + { matrix->rotate(axis, angle, (rw::CombineOp)combineOp); return matrix; } +//RwMatrix *RwMatrixRotateOneMinusCosineSine(RwMatrix * matrix, const RwV3d * unitAxis, RwReal oneMinusCosine, RwReal sine, RwOpCombineType combineOp); +//const RwMatrix *RwMatrixQueryRotate(const RwMatrix * matrix, RwV3d * unitAxis, RwReal * angle, RwV3d * center); +RwV3d *RwMatrixGetRight(RwMatrix * matrix) { return &matrix->right; } +RwV3d *RwMatrixGetUp(RwMatrix * matrix) { return &matrix->up; } +RwV3d *RwMatrixGetAt(RwMatrix * matrix) { return &matrix->at; } +RwV3d *RwMatrixGetPos(RwMatrix * matrix) { return &matrix->pos; } +RwMatrix *RwMatrixUpdate(RwMatrix * matrix) { matrix->update(); return matrix; } +//RwMatrix *RwMatrixOptimize(RwMatrix * matrix, const RwMatrixTolerance *tolerance); + + + + +RwFrame *RwFrameForAllObjects(RwFrame * frame, RwObjectCallBack callBack, void *data) { + FORLIST(lnk, frame->objectList) + if(callBack(&ObjectWithFrame::fromFrame(lnk)->object, data) == nil) + break; + return frame; +} +RwFrame *RwFrameTranslate(RwFrame * frame, const RwV3d * v, RwOpCombineType combine) { frame->translate(v, (CombineOp)combine); return frame; } +RwFrame *RwFrameRotate(RwFrame * frame, const RwV3d * axis, RwReal angle, RwOpCombineType combine) { frame->rotate(axis, angle, (CombineOp)combine); return frame; } +RwFrame *RwFrameScale(RwFrame * frame, const RwV3d * v, RwOpCombineType combine) { frame->scale(v, (CombineOp)combine); return frame; } +RwFrame *RwFrameTransform(RwFrame * frame, const RwMatrix * m, RwOpCombineType combine) { frame->transform(m, (CombineOp)combine); return frame; } +//TODO: actually implement this! +RwFrame *RwFrameOrthoNormalize(RwFrame * frame) { return frame; } +RwFrame *RwFrameSetIdentity(RwFrame * frame) { frame->matrix.setIdentity(); frame->updateObjects(); return frame; } +//RwFrame *RwFrameCloneHierarchy(RwFrame * root); +//RwBool RwFrameDestroyHierarchy(RwFrame * frame); +RwFrame *RwFrameForAllChildren(RwFrame * frame, RwFrameCallBack callBack, void *data) + { return frame->forAllChildren(callBack, data); } +RwFrame *RwFrameRemoveChild(RwFrame * child) { child->removeChild(); return child; } +RwFrame *RwFrameAddChild(RwFrame * parent, RwFrame * child) { parent->addChild(child); return parent; } +RwFrame *RwFrameGetParent(const RwFrame * frame) { return frame->getParent(); } +//RwFrame *RwFrameGetRoot(const RwFrame * frame); +RwMatrix *RwFrameGetLTM(RwFrame * frame) { return frame->getLTM(); } +RwMatrix *RwFrameGetMatrix(RwFrame * frame) { return &frame->matrix; } +RwFrame *RwFrameUpdateObjects(RwFrame * frame) { frame->updateObjects(); return frame; } +RwFrame *RwFrameCreate(void) { return rw::Frame::create(); } +//RwBool RwFrameInit(RwFrame *frame); +//RwBool RwFrameDeInit(RwFrame *frame); +RwBool RwFrameDestroy(RwFrame * frame) { frame->destroy(); return true; } +//void _rwFrameInit(RwFrame *frame); +//void _rwFrameDeInit(RwFrame *frame); +//RwBool RwFrameDirty(const RwFrame * frame); +//RwInt32 RwFrameCount(RwFrame * frame); +//RwBool RwFrameSetStaticPluginsSize(RwInt32 size); +RwInt32 RwFrameRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB) + { return Frame::registerPlugin(size, pluginID, constructCB, destructCB, (CopyConstructor)copyCB); } +//RwInt32 RwFrameGetPluginOffset(RwUInt32 pluginID); +//RwBool RwFrameValidatePlugins(const RwFrame * frame); +//RwFrame *_rwFrameCloneAndLinkClones(RwFrame * root); +//RwFrame *_rwFramePurgeClone(RwFrame *root); + +RwInt32 RwFrameRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB) + { return Frame::registerPluginStream(pluginID, readCB, (StreamWrite)writeCB, (StreamGetSize)getSizeCB); } + + +rwFrameList *rwFrameListDeinitialize(rwFrameList *frameList) { + rwFree(frameList->frames); + frameList->frames = nil; + return frameList; +} +rwFrameList *rwFrameListStreamRead(RwStream *stream, rwFrameList *fl) { return fl->streamRead(stream); } + + + + +RwCamera *RwCameraBeginUpdate(RwCamera * camera) { camera->beginUpdate(); return camera; } +RwCamera *RwCameraEndUpdate(RwCamera * camera) { camera->endUpdate(); return camera; } +RwCamera *RwCameraClear(RwCamera * camera, RwRGBA * colour, RwInt32 clearMode) { camera->clear(colour, clearMode); return camera; } +// WARNING: ignored argument +RwCamera *RwCameraShowRaster(RwCamera * camera, void *pDev, RwUInt32 flags) { camera->showRaster(flags); return camera; } +RwBool RwCameraDestroy(RwCamera * camera) { camera->destroy(); return true; } +RwCamera *RwCameraCreate(void) { return rw::Camera::create(); } +RwCamera *RwCameraClone(RwCamera * camera) { return camera->clone(); } +RwCamera *RwCameraSetViewOffset(RwCamera *camera, const RwV2d *offset) { camera->setViewOffset(offset); return camera; } +RwCamera *RwCameraSetViewWindow(RwCamera *camera, const RwV2d *viewWindow) { camera->setViewWindow(viewWindow); return camera; } +RwCamera *RwCameraSetProjection(RwCamera *camera, RwCameraProjection projection) { camera->projection = projection; return camera; } +RwCamera *RwCameraSetNearClipPlane(RwCamera *camera, RwReal nearClip) { camera->setNearPlane(nearClip); return camera; } +RwCamera *RwCameraSetFarClipPlane(RwCamera *camera, RwReal farClip) { camera->setFarPlane(farClip); return camera; } +RwInt32 RwCameraRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); +RwInt32 RwCameraGetPluginOffset(RwUInt32 pluginID); +RwBool RwCameraValidatePlugins(const RwCamera * camera); +RwFrustumTestResult RwCameraFrustumTestSphere(const RwCamera * camera, const RwSphere * sphere) { return (RwFrustumTestResult)camera->frustumTestSphere(sphere); } +const RwV2d *RwCameraGetViewOffset(const RwCamera *camera) { return &camera->viewOffset; } +RwCamera *RwCameraSetRaster(RwCamera *camera, RwRaster *raster) { camera->frameBuffer = raster; return camera; } +RwRaster *RwCameraGetRaster(const RwCamera *camera) { return camera->frameBuffer; } +RwCamera *RwCameraSetZRaster(RwCamera *camera, RwRaster *zRaster) { camera->zBuffer = zRaster; return camera; } +RwRaster *RwCameraGetZRaster(const RwCamera *camera) { return camera->zBuffer; } +RwReal RwCameraGetNearClipPlane(const RwCamera *camera) { return camera->nearPlane; } +RwReal RwCameraGetFarClipPlane(const RwCamera *camera) { return camera->farPlane; } +RwCamera *RwCameraSetFogDistance(RwCamera *camera, RwReal fogDistance) { camera->fogPlane = fogDistance; return camera; } +RwReal RwCameraGetFogDistance(const RwCamera *camera) { return camera->fogPlane; } +RwCamera *RwCameraGetCurrentCamera(void) { return rw::engine->currentCamera; } +RwCameraProjection RwCameraGetProjection(const RwCamera *camera); +const RwV2d *RwCameraGetViewWindow(const RwCamera *camera) { return &camera->viewWindow; } +RwMatrix *RwCameraGetViewMatrix(RwCamera *camera) { return &camera->viewMatrix; } +RwCamera *RwCameraSetFrame(RwCamera *camera, RwFrame *frame) { camera->setFrame(frame); return camera; } +RwFrame *RwCameraGetFrame(const RwCamera *camera) { return camera->getFrame(); } + + + + + +RwImage *RwImageCreate(RwInt32 width, RwInt32 height, RwInt32 depth) { return Image::create(width, height, depth); } +RwBool RwImageDestroy(RwImage * image) { image->destroy(); return true; } +RwImage *RwImageAllocatePixels(RwImage * image) { image->allocate(); return image; } +RwImage *RwImageFreePixels(RwImage * image) { image->free(); return image; } +RwImage *RwImageCopy(RwImage * destImage, const RwImage * sourceImage); +RwImage *RwImageResize(RwImage * image, RwInt32 width, RwInt32 height); +RwImage *RwImageApplyMask(RwImage * image, const RwImage * mask); +RwImage *RwImageMakeMask(RwImage * image); +RwImage *RwImageReadMaskedImage(const RwChar * imageName, const RwChar * maskname); +RwImage *RwImageRead(const RwChar * imageName); +RwImage *RwImageWrite(RwImage * image, const RwChar * imageName); +RwChar *RwImageGetPath(void); +const RwChar *RwImageSetPath(const RwChar * path) { Image::setSearchPath(path); return path; } +RwImage *RwImageSetStride(RwImage * image, RwInt32 stride) { image->stride = stride; return image; } +RwImage *RwImageSetPixels(RwImage * image, RwUInt8 * pixels) { image->pixels = pixels; return image; } +RwImage *RwImageSetPalette(RwImage * image, RwRGBA * palette) { image->palette = (uint8*)palette; return image; } +RwInt32 RwImageGetWidth(const RwImage * image) { return image->width; } +RwInt32 RwImageGetHeight(const RwImage * image) { return image->height; } +RwInt32 RwImageGetDepth(const RwImage * image) { return image->depth; } +RwInt32 RwImageGetStride(const RwImage * image) { return image->stride; } +RwUInt8 *RwImageGetPixels(const RwImage * image) { return image->pixels; } +RwRGBA *RwImageGetPalette(const RwImage * image) { return (RwRGBA*)image->palette; } +RwUInt32 RwRGBAToPixel(RwRGBA * rgbIn, RwInt32 rasterFormat); +RwRGBA *RwRGBASetFromPixel(RwRGBA * rgbOut, RwUInt32 pixelValue, RwInt32 rasterFormat); +RwBool RwImageSetGamma(RwReal gammaValue); +RwReal RwImageGetGamma(void); +RwImage *RwImageGammaCorrect(RwImage * image); +RwRGBA *RwRGBAGammaCorrect(RwRGBA * rgb); +RwInt32 RwImageRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); +RwInt32 RwImageGetPluginOffset(RwUInt32 pluginID); +RwBool RwImageValidatePlugins(const RwImage * image); +//RwBool RwImageRegisterImageFormat(const RwChar * extension, RwImageCallBackRead imageRead, RwImageCallBackWrite imageWrite); +const RwChar *RwImageFindFileType(const RwChar * imageName); +RwInt32 RwImageStreamGetSize(const RwImage * image); +RwImage *RwImageStreamRead(RwStream * stream); +const RwImage *RwImageStreamWrite(const RwImage * image, RwStream * stream); + +RwImage *RwImageFindRasterFormat(RwImage *ipImage,RwInt32 nRasterType, RwInt32 *npWidth,RwInt32 *npHeight, RwInt32 *npDepth,RwInt32 *npFormat) +{ + return Raster::imageFindRasterFormat(ipImage, nRasterType, npWidth, npHeight, npDepth, npFormat) ? ipImage : nil; +} + + + + +RwRaster *RwRasterCreate(RwInt32 width, RwInt32 height, RwInt32 depth, RwInt32 flags) { return Raster::create(width, height, depth, flags); } +RwBool RwRasterDestroy(RwRaster * raster) { raster->destroy(); return true; } +RwInt32 RwRasterGetWidth(const RwRaster *raster) { return raster->width; } +RwInt32 RwRasterGetHeight(const RwRaster *raster) { return raster->height; } +RwInt32 RwRasterGetStride(const RwRaster *raster); +RwInt32 RwRasterGetDepth(const RwRaster *raster) { return raster->depth; } +RwInt32 RwRasterGetFormat(const RwRaster *raster); +RwInt32 RwRasterGetType(const RwRaster *raster); +RwRaster *RwRasterGetParent(const RwRaster *raster) { return raster->parent; } +RwRaster *RwRasterGetOffset(RwRaster *raster, RwInt16 *xOffset, RwInt16 *yOffset); +RwInt32 RwRasterGetNumLevels(RwRaster * raster); +RwRaster *RwRasterSubRaster(RwRaster * subRaster, RwRaster * raster, RwRect * rect); +RwRaster *RwRasterRenderFast(RwRaster * raster, RwInt32 x, RwInt32 y) { return raster->renderFast(x, y) ? raster : nil; } +RwRaster *RwRasterRender(RwRaster * raster, RwInt32 x, RwInt32 y); +RwRaster *RwRasterRenderScaled(RwRaster * raster, RwRect * rect); +RwRaster *RwRasterPushContext(RwRaster * raster) { return Raster::pushContext(raster); } +RwRaster *RwRasterPopContext(void) { return Raster::popContext(); } +RwRaster *RwRasterGetCurrentContext(void) { return Raster::getCurrentContext(); } +RwBool RwRasterClear(RwInt32 pixelValue); +RwBool RwRasterClearRect(RwRect * rpRect, RwInt32 pixelValue); +RwRaster *RwRasterShowRaster(RwRaster * raster, void *dev, RwUInt32 flags); +RwUInt8 *RwRasterLock(RwRaster * raster, RwUInt8 level, RwInt32 lockMode) { return raster->lock(level, lockMode); } +RwRaster *RwRasterUnlock(RwRaster * raster) { raster->unlock(0); return raster; } +RwUInt8 *RwRasterLockPalette(RwRaster * raster, RwInt32 lockMode); +RwRaster *RwRasterUnlockPalette(RwRaster * raster); +RwInt32 RwRasterRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); +RwInt32 RwRasterGetPluginOffset(RwUInt32 pluginID); +RwBool RwRasterValidatePlugins(const RwRaster * raster); + +RwRaster *RwRasterSetFromImage(RwRaster *raster, RwImage *image) { return raster->setFromImage(image); } + + + + +RwTexture *RwTextureCreate(RwRaster * raster) { return Texture::create(raster); } +RwBool RwTextureDestroy(RwTexture * texture) { texture->destroy(); return true; } +RwTexture *RwTextureAddRef(RwTexture *texture) { texture->addRef(); return texture; } +// TODO +RwBool RwTextureSetMipmapping(RwBool enable) { return true; } +RwBool RwTextureGetMipmapping(void); +// TODO +RwBool RwTextureSetAutoMipmapping(RwBool enable) { return true; } +RwBool RwTextureGetAutoMipmapping(void); +RwBool RwTextureSetMipmapGenerationCallBack(RwTextureCallBackMipmapGeneration callback); +RwTextureCallBackMipmapGeneration RwTextureGetMipmapGenerationCallBack(void); +RwBool RwTextureSetMipmapNameCallBack(RwTextureCallBackMipmapName callback); +RwTextureCallBackMipmapName RwTextureGetMipmapNameCallBack(void); +RwBool RwTextureGenerateMipmapName(RwChar * name, RwChar * maskName, RwUInt8 mipLevel, RwInt32 format); +RwBool RwTextureRasterGenerateMipmaps(RwRaster * raster, RwImage * image); +RwTextureCallBackRead RwTextureGetReadCallBack(void); +RwBool RwTextureSetReadCallBack(RwTextureCallBackRead fpCallBack); +RwTexture *RwTextureSetName(RwTexture * texture, const RwChar * name) { strncpy(texture->name, name, 32); return texture; } +RwTexture *RwTextureSetMaskName(RwTexture * texture, const RwChar * maskName); +RwChar *RwTextureGetName(RwTexture *texture) { return texture->name; } +RwChar *RwTextureGetMaskName(RwTexture *texture); +RwTexture *RwTextureSetRaster(RwTexture * texture, RwRaster * raster) { texture->raster = raster; return texture; } +RwTexture *RwTextureRead(const RwChar * name, const RwChar * maskName) { return Texture::read(name, maskName); } +RwRaster *RwTextureGetRaster(const RwTexture *texture) { return texture->raster; } +RwInt32 RwTextureRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); +RwInt32 RwTextureGetPluginOffset(RwUInt32 pluginID); +RwBool RwTextureValidatePlugins(const RwTexture * texture); + +RwTexDictionary *RwTextureGetDictionary(RwTexture *texture); +RwTexture *RwTextureSetFilterMode(RwTexture *texture, RwTextureFilterMode filtering) { texture->setFilter((Texture::FilterMode)filtering); return texture; } +RwTextureFilterMode RwTextureGetFilterMode(const RwTexture *texture); +RwTexture *RwTextureSetAddressing(RwTexture *texture, RwTextureAddressMode addressing) { + texture->setAddressU((Texture::Addressing)addressing); + texture->setAddressV((Texture::Addressing)addressing); + return texture; +} +RwTexture *RwTextureSetAddressingU(RwTexture *texture, RwTextureAddressMode addressing) { + texture->setAddressU((Texture::Addressing)addressing); + return texture; +} +RwTexture *RwTextureSetAddressingV(RwTexture *texture, RwTextureAddressMode addressing) { + texture->setAddressV((Texture::Addressing)addressing); + return texture; +} +RwTextureAddressMode RwTextureGetAddressing(const RwTexture *texture); +RwTextureAddressMode RwTextureGetAddressingU(const RwTexture *texture); +RwTextureAddressMode RwTextureGetAddressingV(const RwTexture *texture); + +// TODO +void _rwD3D8TexDictionaryEnableRasterFormatConversion(bool enable) { } + + +// hack for reading native textures +RwBool rwNativeTextureHackRead(RwStream *stream, RwTexture **tex, RwInt32 size) +{ + *tex = Texture::streamReadNative(stream); +#ifdef LIBRW + (*tex)->raster = rw::Raster::convertTexToCurrentPlatform((*tex)->raster); +#endif + return *tex != nil; +} + + + + + +RwTexDictionary *RwTexDictionaryCreate(void) { return TexDictionary::create(); } +RwBool RwTexDictionaryDestroy(RwTexDictionary * dict) { dict->destroy(); return true; } +RwTexture *RwTexDictionaryAddTexture(RwTexDictionary * dict, RwTexture * texture) { dict->addFront(texture); return texture; } +//RwTexture *RwTexDictionaryRemoveTexture(RwTexture * texture); +RwTexture *RwTexDictionaryFindNamedTexture(RwTexDictionary * dict, const RwChar * name) { return dict->find(name); } +RwTexDictionary *RwTexDictionaryGetCurrent(void) { return TexDictionary::getCurrent(); } +RwTexDictionary *RwTexDictionarySetCurrent(RwTexDictionary * dict) { TexDictionary::setCurrent(dict); return dict; } +const RwTexDictionary *RwTexDictionaryForAllTextures(const RwTexDictionary * dict, RwTextureCallBack fpCallBack, void *pData) { + FORLIST(lnk, ((RwTexDictionary*)dict)->textures) + if(fpCallBack(Texture::fromDict(lnk), pData) == nil) + break; + return dict; +} +RwBool RwTexDictionaryForAllTexDictionaries(RwTexDictionaryCallBack fpCallBack, void *pData); +RwInt32 RwTexDictionaryRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); +RwInt32 RwTexDictionaryGetPluginOffset(RwUInt32 pluginID); +RwBool RwTexDictionaryValidatePlugins(const RwTexDictionary * dict); +RwUInt32 RwTexDictionaryStreamGetSize(const RwTexDictionary *texDict); +RwTexDictionary *RwTexDictionaryStreamRead(RwStream *stream); +const RwTexDictionary *RwTexDictionaryStreamWrite(const RwTexDictionary *texDict, RwStream *stream) { + ((RwTexDictionary*)texDict)->streamWrite(stream); + return texDict; +} + + + + + +RwStream *RwStreamOpen(RwStreamType type, RwStreamAccessType accessType, const void *pData) { + StreamFile *file; + StreamMemory *mem; + RwMemory *memargs; + const char *mode; + + switch(accessType){ + case rwSTREAMREAD: mode = "rb"; break; + case rwSTREAMWRITE: mode = "wb"; break; + case rwSTREAMAPPEND: mode = "ab"; break; + default: return nil; + } + + // oh god this is horrible. librw streams really need fixing + switch(type){ + case rwSTREAMFILENAME:{ + StreamFile fakefile; + file = rwNewT(StreamFile, 1, 0); + memcpy(file, &fakefile, sizeof(StreamFile)); +#ifndef _WIN32 + char *r = casepath((char*)pData); + if (r) { + if (file->open((char*)r, mode)) { + free(r); + return file; + } + free(r); + } else +#endif + { + if (file->open((char*)pData, mode)) + return file; + } + rwFree(file); + return nil; + } + case rwSTREAMMEMORY:{ + StreamMemory fakemem; + memargs = (RwMemory*)pData; + mem = rwNewT(StreamMemory, 1, 0); + memcpy(mem, &fakemem, sizeof(StreamMemory)); + if(mem->open(memargs->start, memargs->length)) + return mem; + rwFree(mem); + return nil; + } + default: + assert(0 && "unknown type"); + return nil; + } +} +RwBool RwStreamClose(RwStream * stream, void *pData) { stream->close(); rwFree(stream); return true; } +RwUInt32 RwStreamRead(RwStream * stream, void *buffer, RwUInt32 length) { return stream->read8(buffer, length); } +RwStream *RwStreamWrite(RwStream * stream, const void *buffer, RwUInt32 length) { stream->write8(buffer, length); return stream; } +RwStream *RwStreamSkip(RwStream * stream, RwUInt32 offset) { stream->seek(offset); return stream; } + +RwBool RwStreamFindChunk(RwStream *stream, RwUInt32 type, RwUInt32 *lengthOut, RwUInt32 *versionOut) + { return findChunk(stream, type, lengthOut, versionOut); } + + + +void RwIm2DVertexSetCameraX(RwIm2DVertex *vert, RwReal camx) { } +void RwIm2DVertexSetCameraY(RwIm2DVertex *vert, RwReal camy) { } +void RwIm2DVertexSetCameraZ(RwIm2DVertex *vert, RwReal camz) { vert->setCameraZ(camz); } +void RwIm2DVertexSetRecipCameraZ(RwIm2DVertex *vert, RwReal recipz) { vert->setRecipCameraZ(recipz); } +void RwIm2DVertexSetScreenX(RwIm2DVertex *vert, RwReal scrnx) { vert->setScreenX(scrnx); } +void RwIm2DVertexSetScreenY(RwIm2DVertex *vert, RwReal scrny) { vert->setScreenY(scrny); } +void RwIm2DVertexSetScreenZ(RwIm2DVertex *vert, RwReal scrnz) { vert->setScreenZ(scrnz); } +void RwIm2DVertexSetU(RwIm2DVertex *vert, RwReal texU, RwReal recipz) { vert->setU(texU, recipz); } +void RwIm2DVertexSetV(RwIm2DVertex *vert, RwReal texV, RwReal recipz) { vert->setV(texV, recipz); } +void RwIm2DVertexSetIntRGBA(RwIm2DVertex *vert, RwUInt8 red, RwUInt8 green, RwUInt8 blue, RwUInt8 alpha) { vert->setColor(red, green, blue, alpha); } + +RwReal RwIm2DGetNearScreenZ(void) { return im2d::GetNearZ(); } +RwReal RwIm2DGetFarScreenZ(void) { return im2d::GetFarZ(); } +RwBool RwIm2DRenderLine(RwIm2DVertex *vertices, RwInt32 numVertices, RwInt32 vert1, RwInt32 vert2) + { im2d::RenderLine(vertices, numVertices, vert1, vert2); return true; } +RwBool RwIm2DRenderTriangle(RwIm2DVertex *vertices, RwInt32 numVertices, RwInt32 vert1, RwInt32 vert2, RwInt32 vert3 ) + { im2d::RenderTriangle(vertices, numVertices, vert1, vert2, vert3); return true; } +RwBool RwIm2DRenderPrimitive(RwPrimitiveType primType, RwIm2DVertex *vertices, RwInt32 numVertices) + { im2d::RenderPrimitive((PrimitiveType)primType, vertices, numVertices); return true; } +RwBool RwIm2DRenderIndexedPrimitive(RwPrimitiveType primType, RwIm2DVertex *vertices, RwInt32 numVertices, RwImVertexIndex *indices, RwInt32 numIndices) + { im2d::RenderIndexedPrimitive((PrimitiveType)primType, vertices, numVertices, indices, numIndices); return true; } + + +void RwIm3DVertexSetPos(RwIm3DVertex *vert, RwReal x, RwReal y, RwReal z) { vert->setX(x); vert->setY(y); vert->setZ(z); } +void RwIm3DVertexSetU(RwIm3DVertex *vert, RwReal u) { vert->setU(u); } +void RwIm3DVertexSetV(RwIm3DVertex *vert, RwReal v) { vert->setV(v); } +void RwIm3DVertexSetRGBA(RwIm3DVertex *vert, RwUInt8 r, RwUInt8 g, RwUInt8 b, RwUInt8 a) { vert->setColor(r, g, b, a); } + +void *RwIm3DTransform(RwIm3DVertex *pVerts, RwUInt32 numVerts, RwMatrix *ltm, RwUInt32 flags) { im3d::Transform(pVerts, numVerts, ltm, flags); return pVerts; } +RwBool RwIm3DEnd(void) { im3d::End(); return true; } +RwBool RwIm3DRenderLine(RwInt32 vert1, RwInt32 vert2) { + RwImVertexIndex indices[2]; + indices[0] = vert1; + indices[1] = vert2; + im3d::RenderIndexedPrimitive((PrimitiveType)PRIMTYPELINELIST, indices, 2); + return true; +} +RwBool RwIm3DRenderTriangle(RwInt32 vert1, RwInt32 vert2, RwInt32 vert3); +RwBool RwIm3DRenderIndexedPrimitive(RwPrimitiveType primType, RwImVertexIndex *indices, RwInt32 numIndices) { im3d::RenderIndexedPrimitive((PrimitiveType)primType, indices, numIndices); return true; } +RwBool RwIm3DRenderPrimitive(RwPrimitiveType primType); + + + + + +RwBool RwRenderStateGet(RwRenderState state, void *value) +{ + uint32 *uival = (uint32*)value; + uint32 fog; + switch(state){ + case rwRENDERSTATETEXTURERASTER: *(void**)value = GetRenderStatePtr(TEXTURERASTER); return true; + case rwRENDERSTATETEXTUREADDRESS: *uival = GetRenderState(TEXTUREADDRESS); return true; + case rwRENDERSTATETEXTUREADDRESSU: *uival = GetRenderState(TEXTUREADDRESSU); return true; + case rwRENDERSTATETEXTUREADDRESSV: *uival = GetRenderState(TEXTUREADDRESSV); return true; + case rwRENDERSTATETEXTUREPERSPECTIVE: *uival = 1; return true; + case rwRENDERSTATEZTESTENABLE: *uival = GetRenderState(ZTESTENABLE); return true; + case rwRENDERSTATESHADEMODE: *uival = rwSHADEMODEGOURAUD; return true; + case rwRENDERSTATEZWRITEENABLE: *uival = GetRenderState(ZWRITEENABLE); return true; + case rwRENDERSTATETEXTUREFILTER: *uival = GetRenderState(TEXTUREFILTER); return true; + case rwRENDERSTATESRCBLEND: *uival = GetRenderState(SRCBLEND); return true; + case rwRENDERSTATEDESTBLEND: *uival = GetRenderState(DESTBLEND); return true; + case rwRENDERSTATEVERTEXALPHAENABLE: *uival = GetRenderState(VERTEXALPHA); return true; + case rwRENDERSTATEBORDERCOLOR: *uival = 0; return true; + case rwRENDERSTATEFOGENABLE: *uival = GetRenderState(FOGENABLE); return true; + case rwRENDERSTATEFOGCOLOR: + // have to swap R and B here + fog = GetRenderState(FOGCOLOR); + *uival = (fog>>16)&0xFF; + *uival |= (fog&0xFF)<<16; + *uival |= fog&0xFF00; + *uival |= fog&0xFF000000; + return true; + case rwRENDERSTATEFOGTYPE: *uival = rwFOGTYPELINEAR; return true; + case rwRENDERSTATEFOGDENSITY: *(float*)value = 1.0f; return true; + case rwRENDERSTATECULLMODE: *uival = GetRenderState(CULLMODE); return true; + + // all unsupported + case rwRENDERSTATEFOGTABLE: + case rwRENDERSTATEALPHAPRIMITIVEBUFFER: + + case rwRENDERSTATESTENCILENABLE: + case rwRENDERSTATESTENCILFAIL: + case rwRENDERSTATESTENCILZFAIL: + case rwRENDERSTATESTENCILPASS: + case rwRENDERSTATESTENCILFUNCTION: + case rwRENDERSTATESTENCILFUNCTIONREF: + case rwRENDERSTATESTENCILFUNCTIONMASK: + case rwRENDERSTATESTENCILFUNCTIONWRITEMASK: + default: + return false; + } +} +RwBool RwRenderStateSet(RwRenderState state, void *value) +{ + uint32 uival = (uintptr)value; + uint32 fog; + switch(state){ + case rwRENDERSTATETEXTURERASTER: SetRenderStatePtr(TEXTURERASTER, value); return true; + case rwRENDERSTATETEXTUREADDRESS: SetRenderState(TEXTUREADDRESS, uival); return true; + case rwRENDERSTATETEXTUREADDRESSU: SetRenderState(TEXTUREADDRESSU, uival); return true; + case rwRENDERSTATETEXTUREADDRESSV: SetRenderState(TEXTUREADDRESSV, uival); return true; + case rwRENDERSTATETEXTUREPERSPECTIVE: return true; + case rwRENDERSTATEZTESTENABLE: SetRenderState(ZTESTENABLE, uival); return true; + case rwRENDERSTATESHADEMODE: return true; + case rwRENDERSTATEZWRITEENABLE: SetRenderState(ZWRITEENABLE, uival); return true; + case rwRENDERSTATETEXTUREFILTER: SetRenderState(TEXTUREFILTER, uival); return true; + case rwRENDERSTATESRCBLEND: SetRenderState(SRCBLEND, uival); return true; + case rwRENDERSTATEDESTBLEND: SetRenderState(DESTBLEND, uival); return true; + case rwRENDERSTATEVERTEXALPHAENABLE: SetRenderState(VERTEXALPHA, uival); return true; + case rwRENDERSTATEBORDERCOLOR: return true; + case rwRENDERSTATEFOGENABLE: SetRenderState(FOGENABLE, uival); return true; + case rwRENDERSTATEFOGCOLOR: + // have to swap R and B here + fog = (uival>>16)&0xFF; + fog |= (uival&0xFF)<<16; + fog |= uival&0xFF00; + fog |= uival&0xFF000000; + SetRenderState(FOGCOLOR, fog); + return true; + case rwRENDERSTATEFOGTYPE: return true; + case rwRENDERSTATEFOGDENSITY: return true; + case rwRENDERSTATEFOGTABLE: return true; + case rwRENDERSTATEALPHAPRIMITIVEBUFFER: return true; + case rwRENDERSTATECULLMODE: SetRenderState(CULLMODE, uival); return true; + + // all unsupported + case rwRENDERSTATESTENCILENABLE: + case rwRENDERSTATESTENCILFAIL: + case rwRENDERSTATESTENCILZFAIL: + case rwRENDERSTATESTENCILPASS: + case rwRENDERSTATESTENCILFUNCTION: + case rwRENDERSTATESTENCILFUNCTIONREF: + case rwRENDERSTATESTENCILFUNCTIONMASK: + case rwRENDERSTATESTENCILFUNCTIONWRITEMASK: + default: + return true; + } +} + +static rw::MemoryFunctions gMemfuncs; +static void *(*real_malloc)(size_t size); +static void *(*real_realloc)(void *mem, size_t newSize); +static void *mallocWrap(size_t sz, uint32 hint) { if(sz == 0) return nil; return real_malloc(sz); } +static void *reallocWrap(void *p, size_t sz, uint32 hint) { return real_realloc(p, sz); } + + +// WARNING: unused parameters +RwBool RwEngineInit(RwMemoryFunctions *memFuncs, RwUInt32 initFlags, RwUInt32 resArenaSize) { + if(memFuncs){ + real_malloc = memFuncs->rwmalloc; + real_realloc = memFuncs->rwrealloc; + gMemfuncs.rwmalloc = mallocWrap; + gMemfuncs.rwrealloc = reallocWrap; + gMemfuncs.rwfree = memFuncs->rwfree; + Engine::init(&gMemfuncs); + }else{ + Engine::init(nil); + } + return true; +} +// TODO: this is platform dependent +RwBool RwEngineOpen(RwEngineOpenParams *initParams) { + static EngineOpenParams openParams; +#ifdef RW_D3D9 + openParams.window = (HWND)initParams->displayID; +#else + openParams = *(EngineOpenParams*)initParams->displayID; +#endif + return Engine::open(&openParams); +} +RwBool RwEngineStart(void) { + // rw::d3d::isP8supported = false; + return Engine::start(); +} +RwBool RwEngineStop(void) { Engine::stop(); return true; } +RwBool RwEngineClose(void) { Engine::close(); return true; } +RwBool RwEngineTerm(void) { Engine::term(); return true; } +RwInt32 RwEngineRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor initCB, RwPluginObjectDestructor termCB); +RwInt32 RwEngineGetPluginOffset(RwUInt32 pluginID); +RwInt32 RwEngineGetNumSubSystems(void) { return Engine::getNumSubSystems(); } +RwSubSystemInfo *RwEngineGetSubSystemInfo(RwSubSystemInfo *subSystemInfo, RwInt32 subSystemIndex) + { return Engine::getSubSystemInfo(subSystemInfo, subSystemIndex); } +RwInt32 RwEngineGetCurrentSubSystem(void) { return Engine::getCurrentSubSystem(); } +RwBool RwEngineSetSubSystem(RwInt32 subSystemIndex) { return Engine::setSubSystem(subSystemIndex); } +RwInt32 RwEngineGetNumVideoModes(void) { return Engine::getNumVideoModes(); } +RwVideoMode *RwEngineGetVideoModeInfo(RwVideoMode *modeinfo, RwInt32 modeIndex) + { return Engine::getVideoModeInfo(modeinfo, modeIndex); } +RwInt32 RwEngineGetCurrentVideoMode(void) { return Engine::getCurrentVideoMode(); } +RwBool RwEngineSetVideoMode(RwInt32 modeIndex) { return Engine::setVideoMode(modeIndex); } +RwInt32 RwEngineGetTextureMemorySize(void); +RwInt32 RwEngineGetMaxTextureSize(void); + + + +// TODO +void RwD3D8EngineSetRefreshRate(RwUInt32 refreshRate) {} +RwBool RwD3D8DeviceSupportsDXTTexture(void) { return true; } + + +void RwD3D8EngineSetMultiSamplingLevels(RwUInt32 level) { Engine::setMultiSamplingLevels(level); } +RwUInt32 RwD3D8EngineGetMaxMultiSamplingLevels(void) { return Engine::getMaxMultiSamplingLevels(); } + + +RpMaterial *RpMaterialCreate(void) { return Material::create(); } +RwBool RpMaterialDestroy(RpMaterial *material) { material->destroy(); return true; } +//RpMaterial *RpMaterialClone(RpMaterial *material); +RpMaterial *RpMaterialSetTexture(RpMaterial *material, RwTexture *texture) { material->setTexture(texture); return material; } +//RpMaterial *RpMaterialAddRef(RpMaterial *material); +RwTexture *RpMaterialGetTexture(const RpMaterial *material) { return material->texture; } +RpMaterial *RpMaterialSetColor(RpMaterial *material, const RwRGBA *color) { material->color = *color; return material; } +const RwRGBA *RpMaterialGetColor(const RpMaterial *material) { return &material->color; } +RpMaterial *RpMaterialSetSurfaceProperties(RpMaterial *material, const RwSurfaceProperties *surfaceProperties); +const RwSurfaceProperties *RpMaterialGetSurfaceProperties(const RpMaterial *material) { return &material->surfaceProps; } +//RwInt32 RpMaterialRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); +//RwInt32 RpMaterialRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); +//RwInt32 RpMaterialSetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); +//RwInt32 RpMaterialGetPluginOffset(RwUInt32 pluginID); +//RwBool RpMaterialValidatePlugins(const RpMaterial *material); +//RwUInt32 RpMaterialStreamGetSize(const RpMaterial *material); +//RpMaterial *RpMaterialStreamRead(RwStream *stream); +//const RpMaterial *RpMaterialStreamWrite(const RpMaterial *material, RwStream *stream); +//RpMaterialChunkInfo *_rpMaterialChunkInfoRead(RwStream *stream, RpMaterialChunkInfo *materialChunkInfo, RwInt32 *bytesRead); + + + + + +RwReal RpLightGetRadius(const RpLight *light) { return light->radius; } +//const RwRGBAReal *RpLightGetColor(const RpLight *light); +RpLight *RpLightSetFrame(RpLight *light, RwFrame *frame) { light->setFrame(frame); return light; } +RwFrame *RpLightGetFrame(const RpLight *light) { return light->getFrame(); } +//RpLightType RpLightGetType(const RpLight *light); +RpLight *RpLightSetFlags(RpLight *light, RwUInt32 flags) { light->setFlags(flags); return light; } +//RwUInt32 RpLightGetFlags(const RpLight *light); +RpLight *RpLightCreate(RwInt32 type) { return rw::Light::create(type); } +RwBool RpLightDestroy(RpLight *light) { light->destroy(); return true; } +RpLight *RpLightSetRadius(RpLight *light, RwReal radius) { light->radius = radius; return light; } +RpLight *RpLightSetColor(RpLight *light, const RwRGBAReal *color) { light->setColor(color->red, color->green, color->blue); return light; } +//RwReal RpLightGetConeAngle(const RpLight *light); +//RpLight *RpLightSetConeAngle(RpLight * ight, RwReal angle); +//RwUInt32 RpLightStreamGetSize(const RpLight *light); +//RpLight *RpLightStreamRead(RwStream *stream); +//const RpLight *RpLightStreamWrite(const RpLight *light, RwStream *stream); +//RpLightChunkInfo *_rpLightChunkInfoRead(RwStream *stream, RpLightChunkInfo *lightChunkInfo, RwInt32 *bytesRead); +//RwInt32 RpLightRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); +//RwInt32 RpLightRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); +//RwInt32 RpLightSetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); +//RwInt32 RpLightGetPluginOffset(RwUInt32 pluginID); +//RwBool RpLightValidatePlugins(const RpLight * light); + + + + + +RpGeometry *RpGeometryCreate(RwInt32 numVert, RwInt32 numTriangles, RwUInt32 format) { return Geometry::create(numVert, numTriangles, format); } +RwBool RpGeometryDestroy(RpGeometry *geometry) { geometry->destroy(); return true; } +RpGeometry *_rpGeometryAddRef(RpGeometry *geometry); +RpGeometry *RpGeometryLock(RpGeometry *geometry, RwInt32 lockMode) { geometry->lock(lockMode); return geometry; } +RpGeometry *RpGeometryUnlock(RpGeometry *geometry) { geometry->unlock(); return geometry; } +RpGeometry *RpGeometryTransform(RpGeometry *geometry, const RwMatrix *matrix); +RpGeometry *RpGeometryCreateSpace(RwReal radius); +RpMorphTarget *RpMorphTargetSetBoundingSphere(RpMorphTarget *morphTarget, const RwSphere *boundingSphere) { morphTarget->boundingSphere = *boundingSphere; return morphTarget; } +RwSphere *RpMorphTargetGetBoundingSphere(RpMorphTarget *morphTarget) { return &morphTarget->boundingSphere; } +const RpMorphTarget *RpMorphTargetCalcBoundingSphere(const RpMorphTarget *morphTarget, RwSphere *boundingSphere) { *boundingSphere = morphTarget->calculateBoundingSphere(); return morphTarget; } +RwInt32 RpGeometryAddMorphTargets(RpGeometry *geometry, RwInt32 mtcount) { RwInt32 n = geometry->numMorphTargets; geometry->addMorphTargets(mtcount); return n; } +RwInt32 RpGeometryAddMorphTarget(RpGeometry *geometry) { return RpGeometryAddMorphTargets(geometry, 1); } +RpGeometry *RpGeometryRemoveMorphTarget(RpGeometry *geometry, RwInt32 morphTarget); +RwInt32 RpGeometryGetNumMorphTargets(const RpGeometry *geometry); +RpMorphTarget *RpGeometryGetMorphTarget(const RpGeometry *geometry, RwInt32 morphTarget) { return &geometry->morphTargets[morphTarget]; } +RwRGBA *RpGeometryGetPreLightColors(const RpGeometry *geometry) { return geometry->colors; } +RwTexCoords *RpGeometryGetVertexTexCoords(const RpGeometry *geometry, RwTextureCoordinateIndex uvIndex) { + if(uvIndex == rwNARWTEXTURECOORDINATEINDEX) + return nil; + return geometry->texCoords[uvIndex-rwTEXTURECOORDINATEINDEX0]; +} +RwInt32 RpGeometryGetNumTexCoordSets(const RpGeometry *geometry) { return geometry->numTexCoordSets; } +RwInt32 RpGeometryGetNumVertices (const RpGeometry *geometry) { return geometry->numVertices; } +RwV3d *RpMorphTargetGetVertices(const RpMorphTarget *morphTarget) { return morphTarget->vertices; } +RwV3d *RpMorphTargetGetVertexNormals(const RpMorphTarget *morphTarget) { return morphTarget->normals; } +RpTriangle *RpGeometryGetTriangles(const RpGeometry *geometry) { return geometry->triangles; } +RwInt32 RpGeometryGetNumTriangles(const RpGeometry *geometry) { return geometry->numTriangles; } +RpMaterial *RpGeometryGetMaterial(const RpGeometry *geometry, RwInt32 matNum) { return geometry->matList.materials[matNum]; } +const RpGeometry *RpGeometryTriangleSetVertexIndices(const RpGeometry *geometry, RpTriangle *triangle, RwUInt16 vert1, RwUInt16 vert2, RwUInt16 vert3) + { triangle->v[0] = vert1; triangle->v[1] = vert2; triangle->v[2] = vert3; return geometry; } +RpGeometry *RpGeometryTriangleSetMaterial(RpGeometry *geometry, RpTriangle *triangle, RpMaterial *material) { + int id = geometry->matList.findIndex(material); + if(id < 0) + id = geometry->matList.appendMaterial(material); + if(id < 0) + return nil; + triangle->matId = id; + return geometry; +} +const RpGeometry *RpGeometryTriangleGetVertexIndices(const RpGeometry *geometry, const RpTriangle *triangle, RwUInt16 *vert1, RwUInt16 *vert2, RwUInt16 *vert3); +RpMaterial *RpGeometryTriangleGetMaterial(const RpGeometry *geometry, const RpTriangle *triangle); +RwInt32 RpGeometryGetNumMaterials(const RpGeometry *geometry); +RpGeometry *RpGeometryForAllMaterials(RpGeometry *geometry, RpMaterialCallBack fpCallBack, void *pData) { + int i; + for(i = 0; i < geometry->matList.numMaterials; i++) + if(fpCallBack(geometry->matList.materials[i], pData) == nil) + break; + return geometry; +} +//const RpGeometry *RpGeometryForAllMeshes(const RpGeometry *geometry, RpMeshCallBack fpCallBack, void *pData); +RwInt32 RpGeometryRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); +RwInt32 RpGeometryRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); +RwInt32 RpGeometrySetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); +RwInt32 RpGeometryGetPluginOffset(RwUInt32 pluginID); +RwBool RpGeometryValidatePlugins(const RpGeometry *geometry); +RwUInt32 RpGeometryStreamGetSize(const RpGeometry *geometry); +const RpGeometry *RpGeometryStreamWrite(const RpGeometry *geometry, RwStream *stream); +RpGeometry *RpGeometryStreamRead(RwStream *stream) { return Geometry::streamRead(stream); } +//RpGeometryChunkInfo *_rpGeometryChunkInfoRead(RwStream *stream, RpGeometryChunkInfo *geometryChunkInfo, RwInt32 *bytesRead); +RwUInt32 RpGeometryGetFlags(const RpGeometry *geometry) { return geometry->flags; } +RpGeometry *RpGeometrySetFlags(RpGeometry *geometry, RwUInt32 flags) { geometry->flags = flags; return geometry; } +const RwSurfaceProperties *_rpGeometryGetSurfaceProperties(const RpGeometry *geometry); +RpGeometry *_rpGeometrySetSurfaceProperties(RpGeometry *geometry, const RwSurfaceProperties *surfaceProperties); + + + + + +RwFrame *RpClumpGetFrame(const RpClump * clump) { return clump->getFrame(); } +RpClump *RpClumpSetFrame(RpClump * clump, RwFrame * frame) { clump->setFrame(frame); return clump; } +RpClump *RpClumpForAllAtomics(RpClump * clump, RpAtomicCallBack callback, void *pData) { + FORLIST(lnk, clump->atomics) + if(callback(Atomic::fromClump(lnk), pData) == nil) + break; + return clump; +} +RpClump *RpClumpForAllLights(RpClump * clump, RpLightCallBack callback, void *pData); +RpClump *RpClumpForAllCameras(RpClump * clump, RwCameraCallBack callback, void *pData); +//RpClump *RpClumpCreateSpace(const RwV3d * position, RwReal radius); +RpClump *RpClumpRender(RpClump * clump) { clump->render(); return clump; } +RpClump *RpClumpRemoveAtomic(RpClump * clump, RpAtomic * atomic) { clump->removeAtomic(atomic); return clump; } +RpClump *RpClumpAddAtomic(RpClump * clump, RpAtomic * atomic) { clump->addAtomic(atomic); return clump; } +//RpClump *RpClumpRemoveLight(RpClump * clump, RpLight * light); +//RpClump *RpClumpAddLight(RpClump * clump, RpLight * light); +//RpClump *RpClumpRemoveCamera(RpClump * clump, RwCamera * camera); +//RpClump *RpClumpAddCamera(RpClump * clump, RwCamera * camera); +RwBool RpClumpDestroy(RpClump * clump) { clump->destroy(); return true; } +RpClump *RpClumpCreate(void) { return rw::Clump::create(); } +RpClump *RpClumpClone(RpClump * clump) { return clump->clone(); } +//RpClump *RpClumpSetCallBack(RpClump * clump, RpClumpCallBack callback); +//RpClumpCallBack RpClumpGetCallBack(const RpClump * clump); +RwInt32 RpClumpGetNumAtomics(RpClump * clump) { return clump->countAtomics(); } +//RwInt32 RpClumpGetNumLights(RpClump * clump); +//RwInt32 RpClumpGetNumCameras(RpClump * clump); +RpClump *RpClumpStreamRead(RwStream * stream) { return rw::Clump::streamRead(stream); } +RwBool RpClumpStreamWrite(RpClump * clump, RwStream * stream) { return clump->streamWrite(stream); } +//RpClump *RpClumpStreamWrite(RpClump * clump, RwStream * stream); +RwInt32 RpClumpRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB) + { return Clump::registerPlugin(size, pluginID, constructCB, destructCB, (CopyConstructor)copyCB); } +RwInt32 RpClumpRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB) + { return Clump::registerPluginStream(pluginID, readCB, (StreamWrite)writeCB, (StreamGetSize)getSizeCB); } +//RwInt32 RpClumpSetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); +//RwInt32 RpClumpGetPluginOffset(RwUInt32 pluginID); +//RwBool RpClumpValidatePlugins(const RpClump * clump); + + + +RpAtomic *RpAtomicCreate(void) { return rw::Atomic::create(); } +RwBool RpAtomicDestroy(RpAtomic * atomic) { atomic->destroy(); return true; } +RpAtomic *RpAtomicClone(RpAtomic * atomic) { return atomic->clone(); } +RpAtomic *RpAtomicSetFrame(RpAtomic * atomic, RwFrame * frame) { atomic->setFrame(frame); return atomic; } +RpAtomic *RpAtomicSetGeometry(RpAtomic * atomic, RpGeometry * geometry, RwUInt32 flags) { atomic->setGeometry(geometry, flags); return atomic; } + +RwFrame *RpAtomicGetFrame(const RpAtomic * atomic) { return atomic->getFrame(); } +RpAtomic *RpAtomicSetFlags(RpAtomic * atomic, RwUInt32 flags) { atomic->setFlags(flags); return atomic; } +RwUInt32 RpAtomicGetFlags(const RpAtomic * atomic) { return atomic->getFlags(); } +RwSphere *RpAtomicGetBoundingSphere(RpAtomic * atomic) { return &atomic->boundingSphere; } +RpAtomic *RpAtomicRender(RpAtomic * atomic) { atomic->render(); return atomic; } +RpClump *RpAtomicGetClump(const RpAtomic * atomic) { return atomic->clump; } +//RpInterpolator *RpAtomicGetInterpolator(RpAtomic * atomic); +RpGeometry *RpAtomicGetGeometry(const RpAtomic * atomic) { return atomic->geometry; } +// WARNING: illegal cast +void RpAtomicSetRenderCallBack(RpAtomic * atomic, RpAtomicCallBackRender callback) { atomic->setRenderCB((Atomic::RenderCB)callback); } +RpAtomicCallBackRender RpAtomicGetRenderCallBack(const RpAtomic * atomic) { return (RpAtomicCallBackRender)atomic->renderCB; } +//RwBool RpAtomicInstance(RpAtomic *atomic); +//RwUInt32 RpAtomicStreamGetSize(RpAtomic * atomic); +//RpAtomic *RpAtomicStreamRead(RwStream * stream); +//RpAtomic *RpAtomicStreamWrite(RpAtomic * atomic, RwStream * stream); +RwInt32 RpAtomicRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB) + { return Atomic::registerPlugin(size, pluginID, constructCB, destructCB, (CopyConstructor)copyCB); } +//RwInt32 RpAtomicRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); +//RwInt32 RpAtomicSetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); +//RwInt32 RpAtomicSetStreamRightsCallBack(RwUInt32 pluginID, RwPluginDataChunkRightsCallBack rightsCB); +//RwInt32 RpAtomicGetPluginOffset(RwUInt32 pluginID); +//RwBool RpAtomicValidatePlugins(const RpAtomic * atomic); + +RpAtomic *AtomicDefaultRenderCallBack(RpAtomic * atomic) { Atomic::defaultRenderCB(atomic); return atomic; } + + +// TODO: this is extremely simplified +RpWorld *RpWorldCreate(RwBBox * boundingBox) { return World::create(); } +RwBool RpWorldDestroy(RpWorld * world) { world->destroy(); return true; } + +RwBool RpWorldPluginAttach(void) { + registerMeshPlugin(); + registerNativeDataPlugin(); + registerAtomicRightsPlugin(); + registerMaterialRightsPlugin(); + + // not sure if this goes here + // rw::xbox::registerVertexFormatPlugin(); + return true; +} + +RpWorld *RpWorldRemoveCamera(RpWorld *world, RwCamera *camera) { world->removeCamera(camera); return world; } +RpWorld *RpWorldAddCamera(RpWorld *world, RwCamera *camera) { world->addCamera(camera); return world; } +RpWorld *RwCameraGetWorld(const RwCamera *camera); +RpWorld *RpWorldRemoveAtomic(RpWorld *world, RpAtomic *atomic); +RpWorld *RpWorldAddAtomic(RpWorld *world, RpAtomic *atomic); +RpWorld *RpAtomicGetWorld(const RpAtomic *atomic); +RpWorld *RpWorldAddClump(RpWorld *world, RpClump *clump); +RpWorld *RpWorldRemoveClump(RpWorld *world, RpClump *clump); +RpWorld *RpClumpGetWorld(const RpClump *clump); +RpWorld *RpWorldAddLight(RpWorld *world, RpLight *light) { world->addLight(light); return world; } +RpWorld *RpWorldRemoveLight(RpWorld *world, RpLight *light) { world->removeLight(light); return world; } +RpWorld *RpLightGetWorld(const RpLight *light); +RwCamera *RwCameraForAllClumpsInFrustum(RwCamera *camera, void *data); +RwCamera *RwCameraForAllClumpsNotInFrustum(RwCamera *camera, RwInt32 numClumps, void *data); + + + + +RwBool RpMatFXPluginAttach( void ) { registerMatFXPlugin(); return true; } +RpAtomic *RpMatFXAtomicEnableEffects( RpAtomic *atomic ) { MatFX::enableEffects(atomic); return atomic; } +RpMatFXMaterialFlags RpMatFXMaterialGetEffects( const RpMaterial *material ){ return (RpMatFXMaterialFlags)MatFX::getEffects(material); } +RpMaterial *RpMatFXMaterialSetEffects( RpMaterial *material, RpMatFXMaterialFlags flags ) { MatFX::setEffects(material, (uint32)flags); return material; } +RpMaterial *RpMatFXMaterialSetupEnvMap( RpMaterial *material, RwTexture *texture, RwFrame *frame, RwBool useFrameBufferAlpha, RwReal coef ) { + MatFX *mfx = MatFX::get(material); + mfx->setEnvTexture(texture); + mfx->setEnvFrame(frame); + mfx->setEnvCoefficient(coef); + return material; +} +RpMaterial *RpMatFXMaterialSetEnvMapFrame( RpMaterial *material, RwFrame *frame ) +{ + MatFX *mfx = MatFX::get(material); + mfx->setEnvFrame(frame); + return material; +} +RpMaterial *RpMatFXMaterialSetEnvMapFrameBufferAlpha( RpMaterial *material, RwBool useFrameBufferAlpha ) +{ + MatFX *mfx = MatFX::get(material); + mfx->setEnvFBAlpha(useFrameBufferAlpha); + return material; +} +RpMaterial *RpMatFXMaterialSetEnvMapCoefficient( RpMaterial *material, RwReal coef ) +{ + MatFX *mfx = MatFX::get(material); + mfx->setEnvCoefficient(coef); + return material; +} +RwReal RpMatFXMaterialGetEnvMapCoefficient( const RpMaterial *material ) +{ + MatFX *mfx = MatFX::get(material); + return mfx->getEnvCoefficient(); +} + + + +RwBool RpHAnimPluginAttach(void) { + registerHAnimPlugin(); + return true; +} + +RwInt32 RpHAnimFrameGetID(RwFrame *frame) { return HAnimData::get(frame)->id; } + +RwInt32 RpHAnimIDGetIndex(RpHAnimHierarchy *hierarchy, RwInt32 ID) { return hierarchy->getIndex(ID); } + +RwBool RpHAnimFrameSetHierarchy(RwFrame *frame, RpHAnimHierarchy *hierarchy) { HAnimData::get(frame)->hierarchy = hierarchy; return true; } +RpHAnimHierarchy *RpHAnimFrameGetHierarchy(RwFrame *frame) { return HAnimHierarchy::get(frame); } + +RpHAnimHierarchy *RpHAnimHierarchySetFlags(RpHAnimHierarchy *hierarchy, RpHAnimHierarchyFlag flags) { hierarchy->flags = flags; return hierarchy; } + +RwBool RpHAnimHierarchySetCurrentAnim(RpHAnimHierarchy *hierarchy, RpHAnimAnimation *anim) { hierarchy->interpolator->setCurrentAnim(anim); return true; } +RwBool RpHAnimHierarchyAddAnimTime(RpHAnimHierarchy *hierarchy, RwReal time) { hierarchy->interpolator->addTime(time); return true; } + +RwMatrix *RpHAnimHierarchyGetMatrixArray(RpHAnimHierarchy *hierarchy) { return hierarchy->matrices; } +RwBool RpHAnimHierarchyUpdateMatrices(RpHAnimHierarchy *hierarchy) { hierarchy->updateMatrices(); return true; } + +RpHAnimAnimation *RpHAnimAnimationCreate(RwInt32 typeID, RwInt32 numFrames, RwInt32 flags, RwReal duration) + { return Animation::create(AnimInterpolatorInfo::find(typeID), numFrames, flags, duration); } +RpHAnimAnimation *RpHAnimAnimationDestroy(RpHAnimAnimation *animation) { animation->destroy(); return animation; } +RpHAnimAnimation *RpHAnimAnimationStreamRead(RwStream *stream) { return Animation::streamRead(stream); } + + + + + + +RwBool RpSkinPluginAttach(void) { + registerSkinPlugin(); + return true; +} + +RwUInt32 RpSkinGetNumBones( RpSkin *skin ) { return skin->numBones; } +const RwMatrixWeights *RpSkinGetVertexBoneWeights( RpSkin *skin ) { return (RwMatrixWeights*)skin->weights; } +const RwUInt32 *RpSkinGetVertexBoneIndices( RpSkin *skin ) { return (RwUInt32*)skin->indices; } +const RwMatrix *RpSkinGetSkinToBoneMatrices( RpSkin *skin ) { return (const RwMatrix*)skin->inverseMatrices; } + +RpSkin *RpSkinGeometryGetSkin( RpGeometry *geometry ) { return Skin::get(geometry); } + +RpAtomic *RpSkinAtomicSetHAnimHierarchy( RpAtomic *atomic, RpHAnimHierarchy *hierarchy ) { Skin::setHierarchy(atomic, hierarchy); return atomic; } +RpHAnimHierarchy *RpSkinAtomicGetHAnimHierarchy( const RpAtomic *atomic ) { return Skin::getHierarchy(atomic); } + +#if 0 +RwImage * +RtBMPImageWrite(RwImage *image, const RwChar *imageName) +{ +#ifndef _WIN32 + char *r = casepath(imageName); + if (r) { + rw::writeBMP(image, r); + free(r); + } else { + rw::writeBMP(image, imageName); + } + +#else + rw::writeBMP(image, imageName); +#endif + return image; +} +RwImage * +RtBMPImageRead(const RwChar *imageName) +{ +#ifndef _WIN32 + RwImage *image; + char *r = casepath(imageName); + if (r) { + image = rw::readBMP(r); + free(r); + } else { + image = rw::readBMP(imageName); + } + return image; + +#else + return rw::readBMP(imageName); +#endif +} + + +RwImage * +RtPNGImageWrite(RwImage *image, const RwChar *imageName) +{ +#ifndef _WIN32 + char *r = casepath(imageName); + if (r) { + rw::writePNG(image, r); + free(r); + } else { + rw::writePNG(image, imageName); + } + +#else + rw::writePNG(image, imageName); +#endif + return image; +} +RwImage * +RtPNGImageRead(const RwChar *imageName) +{ +#ifndef _WIN32 + RwImage *image; + char *r = casepath(imageName); + if (r) { + image = rw::readPNG(r); + free(r); + } else { + image = rw::readPNG(imageName); + } + return image; + +#else + return rw::readPNG(imageName); +#endif +} +#endif + +#include "rtquat.h" + +RtQuat *RtQuatRotate(RtQuat * quat, const RwV3d * axis, RwReal angle, RwOpCombineType combineOp) { return (RtQuat*)((rw::Quat*)quat)->rotate(axis, angle/180.0f*3.14159f, (CombineOp)combineOp); } +void RtQuatConvertToMatrix(const RtQuat * const qpQuat, RwMatrix * const mpMatrix) { mpMatrix->rotate(*(rw::Quat*)qpQuat, COMBINEREPLACE); } + + +#include "rtcharse.h" + +RwBool RtCharsetOpen(void) { return Charset::open(); } +void RtCharsetClose(void) { return Charset::close(); } +RtCharset *RtCharsetPrint(RtCharset * charSet, const RwChar * string, RwInt32 x, RwInt32 y) { charSet->print(string, x, y, true); return charSet; } +RtCharset *RtCharsetPrintBuffered(RtCharset * charSet, const RwChar * string, RwInt32 x, RwInt32 y, RwBool hideSpaces) { charSet->printBuffered(string, x, y, hideSpaces); return charSet; } +RwBool RtCharsetBufferFlush(void) { Charset::flushBuffer(); return true; } +RtCharset *RtCharsetSetColors(RtCharset * charSet, const RwRGBA * foreGround, const RwRGBA * backGround) { return charSet->setColors(foreGround, backGround); } +RtCharset *RtCharsetGetDesc(RtCharset * charset, RtCharsetDesc * desc) { *desc = charset->desc; return charset; } +RtCharset *RtCharsetCreate(const RwRGBA * foreGround, const RwRGBA * backGround) { return Charset::create(foreGround, backGround); } +RwBool RtCharsetDestroy(RtCharset * charSet) { charSet->destroy(); return true; } + + + +#include + +RwInt8 RpAnisotGetMaxSupportedMaxAnisotropy(void) { return rw::getMaxSupportedMaxAnisotropy(); } +RwTexture *RpAnisotTextureSetMaxAnisotropy(RwTexture *tex, RwInt8 val) { tex->setMaxAnisotropy(val); return tex; } +RwInt8 RpAnisotTextureGetMaxAnisotropy(RwTexture *tex) { return tex->getMaxAnisotropy(); } +RwBool RpAnisotPluginAttach(void) { rw::registerAnisotropyPlugin(); return true; } diff --git a/src/miami/fakerw/rpanisot.h b/src/miami/fakerw/rpanisot.h new file mode 100644 index 00000000..a886512f --- /dev/null +++ b/src/miami/fakerw/rpanisot.h @@ -0,0 +1,6 @@ +#pragma once + +RwInt8 RpAnisotGetMaxSupportedMaxAnisotropy(void); +RwTexture *RpAnisotTextureSetMaxAnisotropy(RwTexture *tex, RwInt8 val); +RwInt8 RpAnisotTextureGetMaxAnisotropy(RwTexture *tex); +RwBool RpAnisotPluginAttach(void); diff --git a/src/miami/fakerw/rphanim.h b/src/miami/fakerw/rphanim.h new file mode 100644 index 00000000..63059800 --- /dev/null +++ b/src/miami/fakerw/rphanim.h @@ -0,0 +1,72 @@ +#pragma once + +#include "rtquat.h" + +//struct RpHAnimHierarchy; +typedef rw::HAnimHierarchy RpHAnimHierarchy; +//struct RpHAnimAnimation; +typedef rw::Animation RpHAnimAnimation; + +#define rpHANIMSTDKEYFRAMETYPEID 0x1 + +// same as rw::HAnimKeyFrame, but we need RtQuat in this one +struct RpHAnimStdKeyFrame +{ + RpHAnimStdKeyFrame *prevFrame; + RwReal time; + RtQuat q; + RwV3d t; +}; +// same story, this one only exists in later RW versions +// but we need it for 64 bit builds because offset and size differs! +struct RpHAnimStdInterpFrame +{ + RpHAnimStdKeyFrame *keyFrame1; + RpHAnimStdKeyFrame *keyFrame2; + RtQuat q; + RwV3d t; +}; + +enum RpHAnimHierarchyFlag +{ + rpHANIMHIERARCHYSUBHIERARCHY = rw::HAnimHierarchy::SUBHIERARCHY, + rpHANIMHIERARCHYNOMATRICES = rw::HAnimHierarchy::NOMATRICES, + + rpHANIMHIERARCHYUPDATEMODELLINGMATRICES = rw::HAnimHierarchy::UPDATEMODELLINGMATRICES, + rpHANIMHIERARCHYUPDATELTMS = rw::HAnimHierarchy::UPDATELTMS, + rpHANIMHIERARCHYLOCALSPACEMATRICES = rw::HAnimHierarchy::LOCALSPACEMATRICES +}; + +#define rpHANIMPOPPARENTMATRIX rw::HAnimHierarchy::POP +#define rpHANIMPUSHPARENTMATRIX rw::HAnimHierarchy::PUSH + +RwBool RpHAnimPluginAttach(void); + +RwBool RpHAnimFrameSetID(RwFrame *frame, RwInt32 id); +RwInt32 RpHAnimFrameGetID(RwFrame *frame); + +RwInt32 RpHAnimIDGetIndex(RpHAnimHierarchy *hierarchy, RwInt32 ID); + +RwBool RpHAnimFrameSetHierarchy(RwFrame *frame, RpHAnimHierarchy *hierarchy); +RpHAnimHierarchy *RpHAnimFrameGetHierarchy(RwFrame *frame); + +RpHAnimHierarchy *RpHAnimHierarchySetFlags(RpHAnimHierarchy *hierarchy, RpHAnimHierarchyFlag flags); +RpHAnimHierarchyFlag RpHAnimHierarchyGetFlags(RpHAnimHierarchy *hierarchy); + +RwBool RpHAnimHierarchySetCurrentAnim(RpHAnimHierarchy *hierarchy, RpHAnimAnimation *anim); +RwBool RpHAnimHierarchySetCurrentAnimTime(RpHAnimHierarchy *hierarchy, RwReal time); +RwBool RpHAnimHierarchySubAnimTime(RpHAnimHierarchy *hierarchy, RwReal time); +RwBool RpHAnimHierarchyAddAnimTime(RpHAnimHierarchy *hierarchy, RwReal time); + +RwMatrix *RpHAnimHierarchyGetMatrixArray(RpHAnimHierarchy *hierarchy); +RwBool RpHAnimHierarchyUpdateMatrices(RpHAnimHierarchy *hierarchy); + +#define rpHANIMHIERARCHYGETINTERPFRAME( hierarchy, nodeIndex ) \ + ( (void *)( ( (RwUInt8 *)&(hierarchy->interpolator[1]) + \ + ((nodeIndex) * \ + hierarchy->interpolator->currentInterpKeyFrameSize) ) ) ) + + +RpHAnimAnimation *RpHAnimAnimationCreate(RwInt32 typeID, RwInt32 numFrames, RwInt32 flags, RwReal duration); +RpHAnimAnimation *RpHAnimAnimationDestroy(RpHAnimAnimation *animation); +RpHAnimAnimation *RpHAnimAnimationStreamRead(RwStream *stream); diff --git a/src/miami/fakerw/rpmatfx.h b/src/miami/fakerw/rpmatfx.h new file mode 100644 index 00000000..87c8fb2e --- /dev/null +++ b/src/miami/fakerw/rpmatfx.h @@ -0,0 +1,43 @@ +#pragma once + +enum RpMatFXMaterialFlags +{ + rpMATFXEFFECTNULL = rw::MatFX::NOTHING, + rpMATFXEFFECTBUMPMAP = rw::MatFX::BUMPMAP, + rpMATFXEFFECTENVMAP = rw::MatFX::ENVMAP, + rpMATFXEFFECTBUMPENVMAP = rw::MatFX::BUMPENVMAP, + rpMATFXEFFECTDUAL = rw::MatFX::DUAL, + + rpMATFXEFFECTMAX, + rpMATFXNUMEFFECTS = rpMATFXEFFECTMAX - 1, +}; + +RwBool RpMatFXPluginAttach( void ); +RpAtomic *RpMatFXAtomicEnableEffects( RpAtomic *atomic ); +RwBool RpMatFXAtomicQueryEffects( RpAtomic *atomic ); +//RpWorldSector *RpMatFXWorldSectorEnableEffects( RpWorldSector *worldSector ); +//RwBool RpMatFXWorldSectorQueryEffects( RpWorldSector *worldSector ); +RpMaterial *RpMatFXMaterialSetEffects( RpMaterial *material, RpMatFXMaterialFlags flags ); +RpMaterial *RpMatFXMaterialSetupBumpMap( RpMaterial *material, RwTexture *texture, RwFrame *frame, RwReal coef ); +RpMaterial *RpMatFXMaterialSetupEnvMap( RpMaterial *material, RwTexture *texture, RwFrame *frame, RwBool useFrameBufferAlpha, RwReal coef ); +RpMaterial *RpMatFXMaterialSetupDualTexture( RpMaterial *material, RwTexture *texture, RwBlendFunction srcBlendMode, RwBlendFunction dstBlendMode ); +RpMatFXMaterialFlags RpMatFXMaterialGetEffects( const RpMaterial *material ); +RpMaterial *RpMatFXMaterialSetBumpMapTexture( RpMaterial *material, RwTexture *texture ); +RpMaterial *RpMatFXMaterialSetBumpMapFrame( RpMaterial *material, RwFrame *frame ); +RpMaterial *RpMatFXMaterialSetBumpMapCoefficient( RpMaterial *material, RwReal coef ); +RwTexture *RpMatFXMaterialGetBumpMapTexture( const RpMaterial *material ); +RwTexture *RpMatFXMaterialGetBumpMapBumpedTexture( const RpMaterial *material ); +RwFrame *RpMatFXMaterialGetBumpMapFrame( const RpMaterial *material ); +RwReal RpMatFXMaterialGetBumpMapCoefficient( const RpMaterial *material ); +RpMaterial *RpMatFXMaterialSetEnvMapTexture( RpMaterial *material, RwTexture *texture ); +RpMaterial *RpMatFXMaterialSetEnvMapFrame( RpMaterial *material, RwFrame *frame ); +RpMaterial *RpMatFXMaterialSetEnvMapFrameBufferAlpha( RpMaterial *material, RwBool useFrameBufferAlpha ); +RpMaterial *RpMatFXMaterialSetEnvMapCoefficient( RpMaterial *material, RwReal coef ); +RwTexture *RpMatFXMaterialGetEnvMapTexture( const RpMaterial *material ); +RwFrame *RpMatFXMaterialGetEnvMapFrame( const RpMaterial *material ); +RwBool RpMatFXMaterialGetEnvMapFrameBufferAlpha( const RpMaterial *material ); +RwReal RpMatFXMaterialGetEnvMapCoefficient( const RpMaterial *material ); +RpMaterial *RpMatFXMaterialSetDualTexture( RpMaterial *material, RwTexture *texture ); +RpMaterial *RpMatFXMaterialSetDualBlendModes( RpMaterial *material, RwBlendFunction srcBlendMode, RwBlendFunction dstBlendMode ); +RwTexture *RpMatFXMaterialGetDualTexture( const RpMaterial *material ); +const RpMaterial *RpMatFXMaterialGetDualBlendModes( const RpMaterial *material, RwBlendFunction *srcBlendMode, RwBlendFunction *dstBlendMode ); diff --git a/src/miami/fakerw/rpskin.h b/src/miami/fakerw/rpskin.h new file mode 100644 index 00000000..1ffc9f27 --- /dev/null +++ b/src/miami/fakerw/rpskin.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +//struct RpSkin; +typedef rw::Skin RpSkin; + +struct RwMatrixWeights +{ + RwReal w0; + RwReal w1; + RwReal w2; + RwReal w3; +}; + +RwBool RpSkinPluginAttach(void); + +RwUInt32 RpSkinGetNumBones( RpSkin *skin ); +const RwMatrixWeights *RpSkinGetVertexBoneWeights( RpSkin *skin ); +const RwUInt32 *RpSkinGetVertexBoneIndices( RpSkin *skin ); +const RwMatrix *RpSkinGetSkinToBoneMatrices( RpSkin *skin ); + +RpSkin *RpSkinGeometryGetSkin( RpGeometry *geometry ); + +RpAtomic *RpSkinAtomicSetHAnimHierarchy( RpAtomic *atomic, RpHAnimHierarchy *hierarchy ); +RpHAnimHierarchy *RpSkinAtomicGetHAnimHierarchy( const RpAtomic *atomic ); diff --git a/src/miami/fakerw/rpworld.h b/src/miami/fakerw/rpworld.h new file mode 100644 index 00000000..e5d4de97 --- /dev/null +++ b/src/miami/fakerw/rpworld.h @@ -0,0 +1,336 @@ +#pragma once + +#define rpATOMIC rw::Atomic::ID +#define rpCLUMP rw::Clump::ID + +/* + *********************************************** + * + * RpMaterial + * + *********************************************** + */ + +//struct RpMaterial; +typedef rw::Material RpMaterial; + +typedef RpMaterial *(*RpMaterialCallBack)(RpMaterial *material, void *data); + +RpMaterial *RpMaterialCreate(void); +RwBool RpMaterialDestroy(RpMaterial *material); +RpMaterial *RpMaterialClone(RpMaterial *material); +RpMaterial *RpMaterialSetTexture(RpMaterial *material, RwTexture *texture); +RpMaterial *RpMaterialAddRef(RpMaterial *material); +RwTexture *RpMaterialGetTexture(const RpMaterial *material); +RpMaterial *RpMaterialSetColor(RpMaterial *material, const RwRGBA *color); +const RwRGBA *RpMaterialGetColor(const RpMaterial *material); +RpMaterial *RpMaterialSetSurfaceProperties(RpMaterial *material, const RwSurfaceProperties *surfaceProperties); +const RwSurfaceProperties *RpMaterialGetSurfaceProperties(const RpMaterial *material); +RwInt32 RpMaterialRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); +RwInt32 RpMaterialRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); +RwInt32 RpMaterialSetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); +RwInt32 RpMaterialGetPluginOffset(RwUInt32 pluginID); +RwBool RpMaterialValidatePlugins(const RpMaterial *material); +RwUInt32 RpMaterialStreamGetSize(const RpMaterial *material); +RpMaterial *RpMaterialStreamRead(RwStream *stream); +const RpMaterial *RpMaterialStreamWrite(const RpMaterial *material, RwStream *stream); +//RpMaterialChunkInfo *_rpMaterialChunkInfoRead(RwStream *stream, RpMaterialChunkInfo *materialChunkInfo, RwInt32 *bytesRead); + + +/* + *********************************************** + * + * RpLight + * + *********************************************** + */ + +//struct RpLight; +typedef rw::Light RpLight; + +enum RpLightType +{ + rpNALIGHTTYPE = 0, + rpLIGHTDIRECTIONAL, + rpLIGHTAMBIENT, + rpLIGHTPOINT = 0x80, + rpLIGHTSPOT, + rpLIGHTSPOTSOFT, +}; + +enum RpLightFlag +{ + rpLIGHTLIGHTATOMICS = 0x01, + rpLIGHTLIGHTWORLD = 0x02, +}; + +typedef RpLight *(*RpLightCallBack) (RpLight * light, void *data); + +RwReal RpLightGetRadius(const RpLight *light); +const RwRGBAReal *RpLightGetColor(const RpLight *light); +RpLight *RpLightSetFrame(RpLight *light, RwFrame *frame); +RwFrame *RpLightGetFrame(const RpLight *light); +RpLightType RpLightGetType(const RpLight *light); +RpLight *RpLightSetFlags(RpLight *light, RwUInt32 flags); +RwUInt32 RpLightGetFlags(const RpLight *light); +RpLight *RpLightCreate(RwInt32 type); +RwBool RpLightDestroy(RpLight *light); +RpLight *RpLightSetRadius(RpLight *light, RwReal radius); +RpLight *RpLightSetColor(RpLight *light, const RwRGBAReal *color); +RwReal RpLightGetConeAngle(const RpLight *light); +RpLight *RpLightSetConeAngle(RpLight * ight, RwReal angle); +RwUInt32 RpLightStreamGetSize(const RpLight *light); +RpLight *RpLightStreamRead(RwStream *stream); +const RpLight *RpLightStreamWrite(const RpLight *light, RwStream *stream); +//RpLightChunkInfo *_rpLightChunkInfoRead(RwStream *stream, RpLightChunkInfo *lightChunkInfo, RwInt32 *bytesRead); +RwInt32 RpLightRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); +RwInt32 RpLightRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); +RwInt32 RpLightSetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); +RwInt32 RpLightGetPluginOffset(RwUInt32 pluginID); +RwBool RpLightValidatePlugins(const RpLight * light); + +/* + *********************************************** + * + * RpGeometry + * + *********************************************** + */ + +typedef rw::Triangle RpTriangle; + +//struct RpGeometry; +typedef rw::Geometry RpGeometry; +//struct RpMorphTarget; +typedef rw::MorphTarget RpMorphTarget; + +enum RpGeometryFlag +{ + rpGEOMETRYTRISTRIP = rw::Geometry::TRISTRIP, + rpGEOMETRYPOSITIONS = rw::Geometry::POSITIONS, + rpGEOMETRYTEXTURED = rw::Geometry::TEXTURED, + rpGEOMETRYPRELIT = rw::Geometry::PRELIT, + rpGEOMETRYNORMALS = rw::Geometry::NORMALS, + rpGEOMETRYLIGHT = rw::Geometry::LIGHT, + rpGEOMETRYMODULATEMATERIALCOLOR = rw::Geometry::MODULATE, + rpGEOMETRYTEXTURED2 = rw::Geometry::TEXTURED2, + rpGEOMETRYNATIVE = rw::Geometry::NATIVE, + rpGEOMETRYNATIVEINSTANCE = rw::Geometry::NATIVEINSTANCE, + rpGEOMETRYFLAGSMASK = 0x000000FF, + rpGEOMETRYNATIVEFLAGSMASK = 0x0F000000, +}; + +enum RpGeometryLockMode +{ + rpGEOMETRYLOCKPOLYGONS = rw::Geometry::LOCKPOLYGONS, + rpGEOMETRYLOCKVERTICES = rw::Geometry::LOCKVERTICES, + rpGEOMETRYLOCKNORMALS = rw::Geometry::LOCKNORMALS, + rpGEOMETRYLOCKPRELIGHT = rw::Geometry::LOCKPRELIGHT, + rpGEOMETRYLOCKTEXCOORDS = rw::Geometry::LOCKTEXCOORDS, + rpGEOMETRYLOCKTEXCOORDS1 = rw::Geometry::LOCKTEXCOORDS1, + rpGEOMETRYLOCKTEXCOORDS2 = rw::Geometry::LOCKTEXCOORDS2, + rpGEOMETRYLOCKTEXCOORDS3 = rw::Geometry::LOCKTEXCOORDS3, + rpGEOMETRYLOCKTEXCOORDS4 = rw::Geometry::LOCKTEXCOORDS4, + rpGEOMETRYLOCKTEXCOORDS5 = rw::Geometry::LOCKTEXCOORDS5, + rpGEOMETRYLOCKTEXCOORDS6 = rw::Geometry::LOCKTEXCOORDS6, + rpGEOMETRYLOCKTEXCOORDS7 = rw::Geometry::LOCKTEXCOORDS7, + rpGEOMETRYLOCKTEXCOORDS8 = rw::Geometry::LOCKTEXCOORDS8, + rpGEOMETRYLOCKTEXCOORDSALL = rw::Geometry::LOCKTEXCOORDSALL, + rpGEOMETRYLOCKALL = rw::Geometry::LOCKALL +}; + +RpGeometry *RpGeometryCreate(RwInt32 numVert, RwInt32 numTriangles, RwUInt32 format); +RwBool RpGeometryDestroy(RpGeometry *geometry); +RpGeometry *_rpGeometryAddRef(RpGeometry *geometry); +RpGeometry *RpGeometryLock(RpGeometry *geometry, RwInt32 lockMode); +RpGeometry *RpGeometryUnlock(RpGeometry *geometry); +RpGeometry *RpGeometryTransform(RpGeometry *geometry, const RwMatrix *matrix); +RpGeometry *RpGeometryCreateSpace(RwReal radius); +RpMorphTarget *RpMorphTargetSetBoundingSphere(RpMorphTarget *morphTarget, const RwSphere *boundingSphere); +RwSphere *RpMorphTargetGetBoundingSphere(RpMorphTarget *morphTarget); +const RpMorphTarget *RpMorphTargetCalcBoundingSphere(const RpMorphTarget *morphTarget, RwSphere *boundingSphere); +RwInt32 RpGeometryAddMorphTargets(RpGeometry *geometry, RwInt32 mtcount); +RwInt32 RpGeometryAddMorphTarget(RpGeometry *geometry); +RpGeometry *RpGeometryRemoveMorphTarget(RpGeometry *geometry, RwInt32 morphTarget); +RwInt32 RpGeometryGetNumMorphTargets(const RpGeometry *geometry); +RpMorphTarget *RpGeometryGetMorphTarget(const RpGeometry *geometry, RwInt32 morphTarget); +RwRGBA *RpGeometryGetPreLightColors(const RpGeometry *geometry); +RwTexCoords *RpGeometryGetVertexTexCoords(const RpGeometry *geometry, RwTextureCoordinateIndex uvIndex); +RwInt32 RpGeometryGetNumTexCoordSets(const RpGeometry *geometry); +RwInt32 RpGeometryGetNumVertices (const RpGeometry *geometry); +RwV3d *RpMorphTargetGetVertices(const RpMorphTarget *morphTarget); +RwV3d *RpMorphTargetGetVertexNormals(const RpMorphTarget *morphTarget); +RpTriangle *RpGeometryGetTriangles(const RpGeometry *geometry); +RwInt32 RpGeometryGetNumTriangles(const RpGeometry *geometry); +RpMaterial *RpGeometryGetMaterial(const RpGeometry *geometry, RwInt32 matNum); +const RpGeometry *RpGeometryTriangleSetVertexIndices(const RpGeometry *geometry, RpTriangle *triangle, RwUInt16 vert1, RwUInt16 vert2, RwUInt16 vert3); +RpGeometry *RpGeometryTriangleSetMaterial(RpGeometry *geometry, RpTriangle *triangle, RpMaterial *material); +const RpGeometry *RpGeometryTriangleGetVertexIndices(const RpGeometry *geometry, const RpTriangle *triangle, RwUInt16 *vert1, RwUInt16 *vert2, RwUInt16 *vert3); +RpMaterial *RpGeometryTriangleGetMaterial(const RpGeometry *geometry, const RpTriangle *triangle); +RwInt32 RpGeometryGetNumMaterials(const RpGeometry *geometry); +RpGeometry *RpGeometryForAllMaterials(RpGeometry *geometry, RpMaterialCallBack fpCallBack, void *pData); +//const RpGeometry *RpGeometryForAllMeshes(const RpGeometry *geometry, RpMeshCallBack fpCallBack, void *pData); +RwInt32 RpGeometryRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); +RwInt32 RpGeometryRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); +RwInt32 RpGeometrySetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); +RwInt32 RpGeometryGetPluginOffset(RwUInt32 pluginID); +RwBool RpGeometryValidatePlugins(const RpGeometry *geometry); +RwUInt32 RpGeometryStreamGetSize(const RpGeometry *geometry); +const RpGeometry *RpGeometryStreamWrite(const RpGeometry *geometry, RwStream *stream); +RpGeometry *RpGeometryStreamRead(RwStream *stream); +//RpGeometryChunkInfo *_rpGeometryChunkInfoRead(RwStream *stream, RpGeometryChunkInfo *geometryChunkInfo, RwInt32 *bytesRead); +RwUInt32 RpGeometryGetFlags(const RpGeometry *geometry); +RpGeometry *RpGeometrySetFlags(RpGeometry *geometry, RwUInt32 flags); +const RwSurfaceProperties *_rpGeometryGetSurfaceProperties(const RpGeometry *geometry); +RpGeometry *_rpGeometrySetSurfaceProperties(RpGeometry *geometry, const RwSurfaceProperties *surfaceProperties); + + +/* + *********************************************** + * + * RpAtomic and RpClump + * + *********************************************** + */ + +//struct RpAtomic; +typedef rw::Atomic RpAtomic; + +enum RpAtomicFlag +{ + rpATOMICCOLLISIONTEST = 0x01, + rpATOMICRENDER = 0x04, +}; + +enum RpAtomicSetGeomFlag +{ + rpATOMICSAMEBOUNDINGSPHERE = 0x01, +}; + +typedef RpAtomic *(*RpAtomicCallBack) (RpAtomic * atomic, void *data); +typedef RpAtomic *(*RpAtomicCallBackRender) (RpAtomic * atomic); + + +//struct RpClump; +typedef rw::Clump RpClump; + +struct RpClumpChunkInfo +{ + RwInt32 numAtomics; + RwInt32 numLights; + RwInt32 numCameras; +}; + +typedef RpClump *(*RpClumpCallBack) (RpClump * clump, void *data); + + +RpAtomic *AtomicDefaultRenderCallBack(RpAtomic * atomic); +//void _rpAtomicResyncInterpolatedSphere(RpAtomic * atomic); +//const RwSphere *RpAtomicGetWorldBoundingSphere(RpAtomic * atomic); + +RwFrame *RpClumpGetFrame(const RpClump * clump); +RpClump *RpClumpSetFrame(RpClump * clump, RwFrame * frame); +RpClump *RpClumpForAllAtomics(RpClump * clump, RpAtomicCallBack callback, void *pData); +RpClump *RpClumpForAllLights(RpClump * clump, RpLightCallBack callback, void *pData); +RpClump *RpClumpForAllCameras(RpClump * clump, RwCameraCallBack callback, void *pData); +RpClump *RpClumpCreateSpace(const RwV3d * position, RwReal radius); +RpClump *RpClumpRender(RpClump * clump); +RpClump *RpClumpRemoveAtomic(RpClump * clump, RpAtomic * atomic); +RpClump *RpClumpAddAtomic(RpClump * clump, RpAtomic * atomic); +RpClump *RpClumpRemoveLight(RpClump * clump, RpLight * light); +RpClump *RpClumpAddLight(RpClump * clump, RpLight * light); +RpClump *RpClumpRemoveCamera(RpClump * clump, RwCamera * camera); +RpClump *RpClumpAddCamera(RpClump * clump, RwCamera * camera); +RwBool RpClumpDestroy(RpClump * clump); +RpClump *RpClumpCreate(void); +RpClump *RpClumpClone(RpClump * clump); +RpClump *RpClumpSetCallBack(RpClump * clump, RpClumpCallBack callback); +RpClumpCallBack RpClumpGetCallBack(const RpClump * clump); +RwInt32 RpClumpGetNumAtomics(RpClump * clump); +RwInt32 RpClumpGetNumLights(RpClump * clump); +RwInt32 RpClumpGetNumCameras(RpClump * clump); +RwUInt32 RpClumpStreamGetSize(RpClump * clump); +RpClump *RpClumpStreamRead(RwStream * stream); +RwBool RpClumpStreamWrite(RpClump * clump, RwStream * stream); +RwInt32 RpClumpRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); +RwInt32 RpClumpRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); +RwInt32 RpClumpSetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); +RwInt32 RpClumpGetPluginOffset(RwUInt32 pluginID); +RwBool RpClumpValidatePlugins(const RpClump * clump); + +RpAtomic *RpAtomicCreate(void); +RwBool RpAtomicDestroy(RpAtomic * atomic); +RpAtomic *RpAtomicClone(RpAtomic * atomic); +RpAtomic *RpAtomicSetFrame(RpAtomic * atomic, RwFrame * frame); +RpAtomic *RpAtomicSetGeometry(RpAtomic * atomic, RpGeometry * geometry, RwUInt32 flags); + +RwFrame *RpAtomicGetFrame(const RpAtomic * atomic); +RpAtomic *RpAtomicSetFlags(RpAtomic * atomic, RwUInt32 flags); +RwUInt32 RpAtomicGetFlags(const RpAtomic * atomic); +RwSphere *RpAtomicGetBoundingSphere(RpAtomic * atomic); +RpAtomic *RpAtomicRender(RpAtomic * atomic); +RpClump *RpAtomicGetClump(const RpAtomic * atomic); +//RpInterpolator *RpAtomicGetInterpolator(RpAtomic * atomic); +RpGeometry *RpAtomicGetGeometry(const RpAtomic * atomic); +void RpAtomicSetRenderCallBack(RpAtomic * atomic, RpAtomicCallBackRender callback); +RpAtomicCallBackRender RpAtomicGetRenderCallBack(const RpAtomic * atomic); +RwBool RpAtomicInstance(RpAtomic *atomic); +RwUInt32 RpAtomicStreamGetSize(RpAtomic * atomic); +RpAtomic *RpAtomicStreamRead(RwStream * stream); +RpAtomic *RpAtomicStreamWrite(RpAtomic * atomic, RwStream * stream); +RwInt32 RpAtomicRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); +RwInt32 RpAtomicRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); +RwInt32 RpAtomicSetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); +RwInt32 RpAtomicSetStreamRightsCallBack(RwUInt32 pluginID, RwPluginDataChunkRightsCallBack rightsCB); +RwInt32 RpAtomicGetPluginOffset(RwUInt32 pluginID); +RwBool RpAtomicValidatePlugins(const RpAtomic * atomic); + +//RwInt32 RpInterpolatorGetEndMorphTarget(const RpInterpolator * interpolator); +//RwInt32 RpInterpolatorGetStartMorphTarget(const RpInterpolator * interpolator); +//RwReal RpInterpolatorGetValue(const RpInterpolator * interpolator); +//RwReal RpInterpolatorGetScale(const RpInterpolator * interpolator); +//RpInterpolator *RpInterpolatorSetEndMorphTarget(RpInterpolator * interpolator, RwInt32 morphTarget, RpAtomic * atomic); +//RpInterpolator *RpInterpolatorSetStartMorphTarget(RpInterpolator * interpolator, RwInt32 morphTarget, RpAtomic * atomic); +//RpInterpolator *RpInterpolatorSetValue(RpInterpolator * interpolator, RwReal value, RpAtomic *atomic); +//RpInterpolator *RpInterpolatorSetScale(RpInterpolator * interpolator, RwReal scale, RpAtomic *atomic); + + +RpClump *RpLightGetClump(const RpLight *light); +RpClump *RwCameraGetClump(const RwCamera *camera); + +/* + *********************************************** + * + * RpWorld + * + *********************************************** + */ + +//struct RpWorld; +typedef rw::World RpWorld; + +RwBool RpWorldDestroy(RpWorld * world); +RpWorld *RpWorldCreate(RwBBox * boundingBox); + +RwBool RpWorldPluginAttach(void); + +RpWorld *RpWorldRemoveCamera(RpWorld *world, RwCamera *camera); +RpWorld *RpWorldAddCamera(RpWorld *world, RwCamera *camera); +RpWorld *RwCameraGetWorld(const RwCamera *camera); +RpWorld *RpWorldRemoveAtomic(RpWorld *world, RpAtomic *atomic); +RpWorld *RpWorldAddAtomic(RpWorld *world, RpAtomic *atomic); +RpWorld *RpAtomicGetWorld(const RpAtomic *atomic); +RpWorld *RpWorldAddClump(RpWorld *world, RpClump *clump); +RpWorld *RpWorldRemoveClump(RpWorld *world, RpClump *clump); +RpWorld *RpClumpGetWorld(const RpClump *clump); +RpWorld *RpWorldAddLight(RpWorld *world, RpLight *light); +RpWorld *RpWorldRemoveLight(RpWorld *world, RpLight *light); +RpWorld *RpLightGetWorld(const RpLight *light); +RwCamera *RwCameraForAllClumpsInFrustum(RwCamera *camera, void *data); +RwCamera *RwCameraForAllClumpsNotInFrustum(RwCamera *camera, RwInt32 numClumps, void *data); +//RwCamera *RwCameraForAllSectorsInFrustum(RwCamera *camera, RpWorldSectorCallBack callBack, void *pData); +//RpLight *RpLightForAllWorldSectors(RpLight *light, RpWorldSectorCallBack callback, void *data); +//RpAtomic *RpAtomicForAllWorldSectors(RpAtomic *atomic, RpWorldSectorCallBack callback, void *data); +//RpWorldSector *RpWorldSectorForAllAtomics(RpWorldSector *sector, RpAtomicCallBack callback, void *data); +//RpWorldSector *RpWorldSectorForAllCollisionAtomics(RpWorldSector *sector, RpAtomicCallBack callback, void *data); +//RpWorldSector *RpWorldSectorForAllLights(RpWorldSector *sector, RpLightCallBack callback, void *data); diff --git a/src/miami/fakerw/rtbmp.h b/src/miami/fakerw/rtbmp.h new file mode 100644 index 00000000..896d276b --- /dev/null +++ b/src/miami/fakerw/rtbmp.h @@ -0,0 +1,4 @@ +#pragma once + +RwImage *RtBMPImageWrite(RwImage * image, const RwChar * imageName); +RwImage *RtBMPImageRead(const RwChar * imageName); diff --git a/src/miami/fakerw/rtcharse.h b/src/miami/fakerw/rtcharse.h new file mode 100644 index 00000000..10eb1f32 --- /dev/null +++ b/src/miami/fakerw/rtcharse.h @@ -0,0 +1,14 @@ +#pragma once + +typedef rw::Charset RtCharset; +typedef rw::Charset::Desc RtCharsetDesc; + +RwBool RtCharsetOpen(void); +void RtCharsetClose(void); +RtCharset *RtCharsetPrint(RtCharset * charSet, const RwChar * string, RwInt32 x, RwInt32 y); +RtCharset *RtCharsetPrintBuffered(RtCharset * charSet, const RwChar * string, RwInt32 x, RwInt32 y, RwBool hideSpaces); +RwBool RtCharsetBufferFlush(void); +RtCharset *RtCharsetSetColors(RtCharset * charSet, const RwRGBA * foreGround, const RwRGBA * backGround); +RtCharset *RtCharsetGetDesc(RtCharset * charset, RtCharsetDesc * desc); +RtCharset *RtCharsetCreate(const RwRGBA * foreGround, const RwRGBA * backGround); +RwBool RtCharsetDestroy(RtCharset * charSet); diff --git a/src/miami/fakerw/rtpng.h b/src/miami/fakerw/rtpng.h new file mode 100644 index 00000000..80f29020 --- /dev/null +++ b/src/miami/fakerw/rtpng.h @@ -0,0 +1,4 @@ +#pragma once + +RwImage *RtPNGImageWrite(RwImage * image, const RwChar * imageName); +RwImage *RtPNGImageRead(const RwChar * imageName); diff --git a/src/miami/fakerw/rtquat.h b/src/miami/fakerw/rtquat.h new file mode 100644 index 00000000..450342b2 --- /dev/null +++ b/src/miami/fakerw/rtquat.h @@ -0,0 +1,15 @@ +#pragma once + +// Same layout as rw::Quat but with ugly imag,real separation which i don't want in librw +struct RtQuat +{ + rw::V3d imag; + rw::float32 real; +}; + +RwBool RtQuatConvertFromMatrix(RtQuat * const qpQuat, const RwMatrix * const mpMatrix); +RtQuat *RtQuatRotate(RtQuat * quat, const RwV3d * axis, RwReal angle, RwOpCombineType combineOp); +const RtQuat *RtQuatQueryRotate(const RtQuat *quat, RwV3d * unitAxis, RwReal * angle); +RwV3d *RtQuatTransformVectors(RwV3d * vectorsOut, const RwV3d * vectorsIn, const RwInt32 numPoints, const RtQuat *quat); + +void RtQuatConvertToMatrix(const RtQuat * const qpQuat, RwMatrix * const mpMatrix); diff --git a/src/fakerw/rwcore.h b/src/miami/fakerw/rwcore.h similarity index 100% rename from src/fakerw/rwcore.h rename to src/miami/fakerw/rwcore.h diff --git a/src/miami/fakerw/rwplcore.h b/src/miami/fakerw/rwplcore.h new file mode 100644 index 00000000..69c921cc --- /dev/null +++ b/src/miami/fakerw/rwplcore.h @@ -0,0 +1,498 @@ +#pragma once + +typedef rw::int8 RwInt8; +typedef rw::int16 RwInt16; +typedef rw::int32 RwInt32; +typedef rw::uint8 RwUInt8; +typedef rw::uint16 RwUInt16; +typedef rw::uint32 RwUInt32; +typedef rw::float32 RwReal; + +typedef char RwChar; +typedef RwInt32 RwBool; + +#define __RWUNUSED__ + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE !FALSE +#endif + +// used for unicode +#define RWSTRING(x) x + +typedef rw::V2d RwV2d; + +typedef rw::V3d RwV3d; + +typedef rw::Rect RwRect; + +typedef rw::Sphere RwSphere; + +enum RwTextureCoordinateIndex +{ + rwNARWTEXTURECOORDINATEINDEX = 0, + rwTEXTURECOORDINATEINDEX0, + rwTEXTURECOORDINATEINDEX1, + rwTEXTURECOORDINATEINDEX2, + rwTEXTURECOORDINATEINDEX3, + rwTEXTURECOORDINATEINDEX4, + rwTEXTURECOORDINATEINDEX5, + rwTEXTURECOORDINATEINDEX6, + rwTEXTURECOORDINATEINDEX7, +}; + +typedef rw::TexCoords RwTexCoords; + +typedef rw::SurfaceProperties RwSurfaceProperties; + +#define RWRGBALONG(r,g,b,a) \ + ((RwUInt32) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))) + + +#define MAKECHUNKID(vendorID, chunkID) (((vendorID & 0xFFFFFF) << 8) | (chunkID & 0xFF)) + +enum RwCorePluginID +{ + rwID_NAOBJECT = 0x00, + rwID_STRUCT = 0x01, + rwID_STRING = 0x02, + rwID_EXTENSION = 0x03, + rwID_CAMERA = 0x05, + rwID_TEXTURE = 0x06, + rwID_MATERIAL = 0x07, + rwID_MATLIST = 0x08, + rwID_ATOMICSECT = 0x09, + rwID_PLANESECT = 0x0A, + rwID_WORLD = 0x0B, + rwID_SPLINE = 0x0C, + rwID_MATRIX = 0x0D, + rwID_FRAMELIST = 0x0E, + rwID_GEOMETRY = 0x0F, + rwID_CLUMP = 0x10, + rwID_LIGHT = 0x12, + rwID_UNICODESTRING = 0x13, + rwID_ATOMIC = 0x14, + rwID_TEXTURENATIVE = 0x15, + rwID_TEXDICTIONARY = 0x16, + rwID_ANIMDATABASE = 0x17, + rwID_IMAGE = 0x18, + rwID_SKINANIMATION = 0x19, + rwID_GEOMETRYLIST = 0x1A, + rwID_HANIMANIMATION = 0x1B, + rwID_TEAM = 0x1C, + rwID_CROWD = 0x1D, + rwID_DMORPHANIMATION = 0x1E, + rwID_RIGHTTORENDER = 0x1f, + rwID_MTEFFECTNATIVE = 0x20, + rwID_MTEFFECTDICT = 0x21, + rwID_TEAMDICTIONARY = 0x22, + rwID_PITEXDICTIONARY = 0x23, + rwID_TOC = 0x24, + rwID_PRTSTDGLOBALDATA = 0x25, + /* Insert before MAX and increment MAX */ + rwID_COREPLUGINIDMAX = 0x26, +}; + + +/* + *********************************************** + * + * RwObject + * + *********************************************** + */ + +//struct RwObject; +typedef rw::Object RwObject; +typedef rw::Frame RwFrame; + +typedef RwObject *(*RwObjectCallBack)(RwObject *object, void *data); + +RwUInt8 RwObjectGetType(const RwObject *obj); +RwFrame* rwObjectGetParent(const RwObject *obj); + +#define rwsprintf sprintf +#define rwvsprintf vsprintf +#define rwstrcpy strcpy +#define rwstrncpy strncpy +#define rwstrcat strcat +#define rwstrncat strncat +#define rwstrrchr strrchr +#define rwstrchr strchr +#define rwstrstr strstr +#define rwstrcmp strcmp +#define rwstricmp stricmp +#define rwstrlen strlen +#define rwstrupr strupr +#define rwstrlwr strlwr +#define rwstrtok strtok +#define rwsscanf sscanf + + +/* + *********************************************** + * + * Memory + * + *********************************************** + */ + +struct RwMemoryFunctions +{ + // NB: from RW 3.6 on the allocating functions take + // a hint parameter! + void *(*rwmalloc)(size_t size); + void (*rwfree)(void *mem); + void *(*rwrealloc)(void *mem, size_t newSize); + void *(*rwcalloc)(size_t numObj, size_t sizeObj); +}; + +void *RwMalloc(size_t size); +void RwFree(void *mem); +void *RwRealloc(void *mem, size_t newSize); +void *RwCalloc(size_t numObj, size_t sizeObj); + +/* + *********************************************** + * + * RwStream + * + *********************************************** + */ + +//struct RwStream; +typedef rw::Stream RwStream; + +struct RwMemory +{ + RwUInt8 *start; + RwUInt32 length; +}; + +enum RwStreamType +{ + rwNASTREAM = 0, + rwSTREAMFILE, + rwSTREAMFILENAME, + rwSTREAMMEMORY, + rwSTREAMCUSTOM +}; + +enum RwStreamAccessType +{ + rwNASTREAMACCESS = 0, + rwSTREAMREAD, + rwSTREAMWRITE, + rwSTREAMAPPEND +}; + +RwStream *RwStreamOpen(RwStreamType type, RwStreamAccessType accessType, const void *pData); +RwBool RwStreamClose(RwStream * stream, void *pData); +RwUInt32 RwStreamRead(RwStream * stream, void *buffer, RwUInt32 length); +RwStream *RwStreamWrite(RwStream * stream, const void *buffer, RwUInt32 length); +RwStream *RwStreamSkip(RwStream * stream, RwUInt32 offset); + + +/* + *********************************************** + * + * Plugin Registry + * + *********************************************** + */ + +#define RWPLUGINOFFSET(_type, _base, _offset) \ + ((_type *)((RwUInt8 *)(_base) + (_offset))) + +typedef RwStream *(*RwPluginDataChunkWriteCallBack)(RwStream *stream, RwInt32 binaryLength, const void *object, RwInt32 offsetInObject, RwInt32 sizeInObject); +typedef RwStream *(*RwPluginDataChunkReadCallBack)(RwStream *stream, RwInt32 binaryLength, void *object, RwInt32 offsetInObject, RwInt32 sizeInObject); +typedef RwInt32(*RwPluginDataChunkGetSizeCallBack)(const void *object, RwInt32 offsetInObject, RwInt32 sizeInObject); +typedef RwBool(*RwPluginDataChunkAlwaysCallBack)(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject); +typedef RwBool(*RwPluginDataChunkRightsCallBack)(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject, RwUInt32 extraData); +typedef void *(*RwPluginObjectConstructor)(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject); +typedef void *(*RwPluginObjectCopy)(void *dstObject, const void *srcObject, RwInt32 offsetInObject, RwInt32 sizeInObject); +typedef void *(*RwPluginObjectDestructor)(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject); + +/* + *********************************************** + * + * RwMatrix + * + *********************************************** + */ + +typedef rw::Matrix RwMatrix; + +enum RwOpCombineType +{ + rwCOMBINEREPLACE = rw::COMBINEREPLACE, + rwCOMBINEPRECONCAT = rw::COMBINEPRECONCAT, + rwCOMBINEPOSTCONCAT = rw::COMBINEPOSTCONCAT +}; + +enum RwMatrixType +{ + rwMATRIXTYPENORMAL = rw::Matrix::TYPENORMAL, + rwMATRIXTYPEORTHOGANAL = rw::Matrix::TYPEORTHOGONAL, + rwMATRIXTYPEORTHONORMAL = rw::Matrix::TYPEORTHONORMAL, + rwMATRIXTYPEMASK = 0x00000003, +}; + +typedef rw::Matrix::Tolerance RwMatrixTolerance; + +RwBool RwMatrixDestroy(RwMatrix *mpMat); +RwMatrix *RwMatrixCreate(void); +void RwMatrixCopy(RwMatrix * dstMatrix, const RwMatrix * srcMatrix); +void RwMatrixSetIdentity(RwMatrix * matrix); +RwMatrix *RwMatrixMultiply(RwMatrix * matrixOut, const RwMatrix * MatrixIn1, const RwMatrix * matrixIn2); +RwMatrix *RwMatrixTransform(RwMatrix * matrix, const RwMatrix * transform, RwOpCombineType combineOp); +RwMatrix *RwMatrixOrthoNormalize(RwMatrix * matrixOut, const RwMatrix * matrixIn); +RwMatrix *RwMatrixInvert(RwMatrix * matrixOut, const RwMatrix * matrixIn); +RwMatrix *RwMatrixScale(RwMatrix * matrix, const RwV3d * scale, RwOpCombineType combineOp); +RwMatrix *RwMatrixTranslate(RwMatrix * matrix, const RwV3d * translation, RwOpCombineType combineOp); +RwMatrix *RwMatrixRotate(RwMatrix * matrix, const RwV3d * axis, RwReal angle, RwOpCombineType combineOp); +RwMatrix *RwMatrixRotateOneMinusCosineSine(RwMatrix * matrix, const RwV3d * unitAxis, RwReal oneMinusCosine, RwReal sine, RwOpCombineType combineOp); +const RwMatrix *RwMatrixQueryRotate(const RwMatrix * matrix, RwV3d * unitAxis, RwReal * angle, RwV3d * center); +RwV3d *RwMatrixGetRight(RwMatrix * matrix); +RwV3d *RwMatrixGetUp(RwMatrix * matrix); +RwV3d *RwMatrixGetAt(RwMatrix * matrix); +RwV3d *RwMatrixGetPos(RwMatrix * matrix); +RwMatrix *RwMatrixUpdate(RwMatrix * matrix); +RwMatrix *RwMatrixOptimize(RwMatrix * matrix, const RwMatrixTolerance *tolerance); + +/* + *********************************************** + * + * RwRGBA + * + *********************************************** + */ + +typedef rw::RGBA RwRGBA; +typedef rw::RGBAf RwRGBAReal; + + +inline void RwRGBAAssign(RwRGBA *target, const RwRGBA *source) { *target = *source; } + + +RwReal RwV3dNormalize(RwV3d * out, const RwV3d * in); +RwReal RwV3dLength(const RwV3d * in); +RwReal RwV2dLength(const RwV2d * in); +RwReal RwV2dNormalize(RwV2d * out, const RwV2d * in); +void RwV2dAssign(RwV2d * out, const RwV2d * ina); +void RwV2dAdd(RwV2d * out, const RwV2d * ina, const RwV2d * inb); +void RwV2dLineNormal(RwV2d * out, const RwV2d * ina, const RwV2d * inb); +void RwV2dSub(RwV2d * out, const RwV2d * ina, const RwV2d * inb); +void RwV2dPerp(RwV2d * out, const RwV2d * in); +void RwV2dScale(RwV2d * out, const RwV2d * in, RwReal scalar); +RwReal RwV2dDotProduct(const RwV2d * ina, const RwV2d * inb); +void RwV3dAssign(RwV3d * out, const RwV3d * ina); +void RwV3dAdd(RwV3d * out, const RwV3d * ina, const RwV3d * inb); +void RwV3dSub(RwV3d * out, const RwV3d * ina, const RwV3d * inb); +void RwV3dScale(RwV3d * out, const RwV3d * in, RwReal scalar); +void RwV3dIncrementScaled(RwV3d * out, const RwV3d * in, RwReal scalar); +void RwV3dNegate(RwV3d * out, const RwV3d * in); +RwReal RwV3dDotProduct(const RwV3d * ina, const RwV3d * inb); +void RwV3dCrossProduct(RwV3d * out, const RwV3d * ina, const RwV3d * inb); +RwV3d *RwV3dTransformPoints(RwV3d * pointsOut, const RwV3d * pointsIn, RwInt32 numPoints, const RwMatrix * matrix); +RwV3d *RwV3dTransformVectors(RwV3d * vectorsOut, const RwV3d * vectorsIn, RwInt32 numPoints, const RwMatrix * matrix); + + +/* + *********************************************** + * + * Render States + * + *********************************************** + */ + +// not librw because we don't support all of them (yet?) - mapping in wrapper functions +enum RwRenderState +{ + rwRENDERSTATENARENDERSTATE = 0, + rwRENDERSTATETEXTURERASTER, + rwRENDERSTATETEXTUREADDRESS, + rwRENDERSTATETEXTUREADDRESSU, + rwRENDERSTATETEXTUREADDRESSV, + rwRENDERSTATETEXTUREPERSPECTIVE, + rwRENDERSTATEZTESTENABLE, + rwRENDERSTATESHADEMODE, + rwRENDERSTATEZWRITEENABLE, + rwRENDERSTATETEXTUREFILTER, + rwRENDERSTATESRCBLEND, + rwRENDERSTATEDESTBLEND, + rwRENDERSTATEVERTEXALPHAENABLE, + rwRENDERSTATEBORDERCOLOR, + rwRENDERSTATEFOGENABLE, + rwRENDERSTATEFOGCOLOR, + rwRENDERSTATEFOGTYPE, + rwRENDERSTATEFOGDENSITY, + rwRENDERSTATEFOGTABLE, + rwRENDERSTATEALPHAPRIMITIVEBUFFER, + rwRENDERSTATECULLMODE, + rwRENDERSTATESTENCILENABLE, + rwRENDERSTATESTENCILFAIL, + rwRENDERSTATESTENCILZFAIL, + rwRENDERSTATESTENCILPASS, + rwRENDERSTATESTENCILFUNCTION, + rwRENDERSTATESTENCILFUNCTIONREF, + rwRENDERSTATESTENCILFUNCTIONMASK, + rwRENDERSTATESTENCILFUNCTIONWRITEMASK +}; + +// not supported - we only do gouraud +enum RwShadeMode +{ + rwSHADEMODENASHADEMODE = 0, + rwSHADEMODEFLAT, + rwSHADEMODEGOURAUD +}; + +enum RwBlendFunction +{ + rwBLENDNABLEND = 0, + rwBLENDZERO = rw::BLENDZERO, + rwBLENDONE = rw::BLENDONE, + rwBLENDSRCCOLOR = rw::BLENDSRCCOLOR, + rwBLENDINVSRCCOLOR = rw::BLENDINVSRCCOLOR, + rwBLENDSRCALPHA = rw::BLENDSRCALPHA, + rwBLENDINVSRCALPHA = rw::BLENDINVSRCALPHA, + rwBLENDDESTALPHA = rw::BLENDDESTALPHA, + rwBLENDINVDESTALPHA = rw::BLENDINVDESTALPHA, + rwBLENDDESTCOLOR = rw::BLENDDESTCOLOR, + rwBLENDINVDESTCOLOR = rw::BLENDINVDESTCOLOR, + rwBLENDSRCALPHASAT = rw::BLENDSRCALPHASAT +}; + +// unsupported - we only need linear +enum RwFogType +{ + rwFOGTYPENAFOGTYPE = 0, + rwFOGTYPELINEAR, + rwFOGTYPEEXPONENTIAL, + rwFOGTYPEEXPONENTIAL2 +}; + +enum RwTextureFilterMode +{ + rwFILTERNAFILTERMODE = 0, + rwFILTERNEAREST = rw::Texture::NEAREST, + rwFILTERLINEAR = rw::Texture::LINEAR, + rwFILTERMIPNEAREST = rw::Texture::MIPNEAREST, + rwFILTERMIPLINEAR = rw::Texture::MIPLINEAR, + rwFILTERLINEARMIPNEAREST = rw::Texture::LINEARMIPNEAREST, + rwFILTERLINEARMIPLINEAR = rw::Texture::LINEARMIPLINEAR +}; + +enum RwTextureAddressMode +{ + rwTEXTUREADDRESSNATEXTUREADDRESS = 0, + rwTEXTUREADDRESSWRAP = rw::Texture::WRAP, + rwTEXTUREADDRESSMIRROR = rw::Texture::MIRROR, + rwTEXTUREADDRESSCLAMP = rw::Texture::CLAMP, + rwTEXTUREADDRESSBORDER = rw::Texture::BORDER +}; + +enum RwCullMode +{ + rwCULLMODENACULLMODE = 0, + rwCULLMODECULLNONE = rw::CULLNONE, + rwCULLMODECULLBACK = rw::CULLBACK, + rwCULLMODECULLFRONT = rw::CULLFRONT +}; + +enum RwPrimitiveType +{ + rwPRIMTYPENAPRIMTYPE = rw::PRIMTYPENONE, + rwPRIMTYPELINELIST = rw::PRIMTYPELINELIST, + rwPRIMTYPEPOLYLINE = rw::PRIMTYPEPOLYLINE, + rwPRIMTYPETRILIST = rw::PRIMTYPETRILIST, + rwPRIMTYPETRISTRIP = rw::PRIMTYPETRISTRIP, + rwPRIMTYPETRIFAN = rw::PRIMTYPETRIFAN, + rwPRIMTYPEPOINTLIST = rw::PRIMTYPEPOINTLIST +}; + + +RwBool RwRenderStateGet(RwRenderState state, void *value); +RwBool RwRenderStateSet(RwRenderState state, void *value); + + +/* + *********************************************** + * + * Engine + * + *********************************************** + */ + +struct RwEngineOpenParams +{ + void *displayID; +}; + +typedef rw::SubSystemInfo RwSubSystemInfo; + +enum RwVideoModeFlag +{ + rwVIDEOMODEEXCLUSIVE = rw::VIDEOMODEEXCLUSIVE, +/* + rwVIDEOMODEINTERLACE = 0x2, + rwVIDEOMODEFFINTERLACE = 0x4, + rwVIDEOMODEFSAA0 = 0x8, + rwVIDEOMODEFSAA1 = 0x10 +*/ +}; + +typedef rw::VideoMode RwVideoMode; + +#if 0 +struct RwFileFunctions +{ + rwFnFexist rwfexist; /**< Pointer to fexist function */ + rwFnFopen rwfopen; /**< Pointer to fopen function */ + rwFnFclose rwfclose; /**< Pointer to fclose function */ + rwFnFread rwfread; /**< Pointer to fread function */ + rwFnFwrite rwfwrite; /**< Pointer to fwrite function */ + rwFnFgets rwfgets; /**< Pointer to fgets function */ + rwFnFputs rwfputs; /**< Pointer to puts function */ + rwFnFeof rwfeof; /**< Pointer to feof function */ + rwFnFseek rwfseek; /**< Pointer to fseek function */ + rwFnFflush rwfflush; /**< Pointer to fflush function */ + rwFnFtell rwftell; /**< Pointer to ftell function */ +}; +RwFileFunctions *RwOsGetFileInterface(void); +#endif + +RwBool RwEngineInit(RwMemoryFunctions *memFuncs, RwUInt32 initFlags, RwUInt32 resArenaSize); +RwInt32 RwEngineRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor initCB, RwPluginObjectDestructor termCB); +RwInt32 RwEngineGetPluginOffset(RwUInt32 pluginID); +RwBool RwEngineOpen(RwEngineOpenParams *initParams); +RwBool RwEngineStart(void); +RwBool RwEngineStop(void); +RwBool RwEngineClose(void); +RwBool RwEngineTerm(void); +RwInt32 RwEngineGetNumSubSystems(void); +RwSubSystemInfo *RwEngineGetSubSystemInfo(RwSubSystemInfo *subSystemInfo, RwInt32 subSystemIndex); +RwInt32 RwEngineGetCurrentSubSystem(void); +RwBool RwEngineSetSubSystem(RwInt32 subSystemIndex); +RwInt32 RwEngineGetNumVideoModes(void); +RwVideoMode *RwEngineGetVideoModeInfo(RwVideoMode *modeinfo, RwInt32 modeIndex); +RwInt32 RwEngineGetCurrentVideoMode(void); +RwBool RwEngineSetVideoMode(RwInt32 modeIndex); +RwInt32 RwEngineGetTextureMemorySize(void); +RwInt32 RwEngineGetMaxTextureSize(void); + + +/* + *********************************************** + * + * Binary stream + * + *********************************************** + */ + +RwBool RwStreamFindChunk(RwStream *stream, RwUInt32 type, RwUInt32 *lengthOut, RwUInt32 *versionOut); diff --git a/src/miami/math/Matrix.cpp b/src/miami/math/Matrix.cpp new file mode 100644 index 00000000..c0d909cb --- /dev/null +++ b/src/miami/math/Matrix.cpp @@ -0,0 +1,531 @@ +#include "common.h" + +CMatrix::CMatrix(void) +{ + m_attachment = nil; + m_hasRwMatrix = false; +} + +CMatrix::CMatrix(CMatrix const &m) +{ + m_attachment = nil; + m_hasRwMatrix = false; + *this = m; +} + +CMatrix::CMatrix(RwMatrix *matrix, bool owner) +{ + m_attachment = nil; + Attach(matrix, owner); +} + +CMatrix::~CMatrix(void) +{ + if (m_hasRwMatrix && m_attachment) + RwMatrixDestroy(m_attachment); +} + +void +CMatrix::Attach(RwMatrix *matrix, bool owner) +{ +#ifdef FIX_BUGS + if (m_attachment && m_hasRwMatrix) +#else + if (m_hasRwMatrix && m_attachment) +#endif + RwMatrixDestroy(m_attachment); + m_attachment = matrix; + m_hasRwMatrix = owner; + Update(); +} + +void +CMatrix::AttachRW(RwMatrix *matrix, bool owner) +{ + if (m_hasRwMatrix && m_attachment) + RwMatrixDestroy(m_attachment); + m_attachment = matrix; + m_hasRwMatrix = owner; + UpdateRW(); +} + +void +CMatrix::Detach(void) +{ + if (m_hasRwMatrix && m_attachment) + RwMatrixDestroy(m_attachment); + m_attachment = nil; +} + +void +CMatrix::Update(void) +{ + GetRight() = m_attachment->right; + GetForward() = m_attachment->up; + GetUp() = m_attachment->at; + GetPosition() = m_attachment->pos; +} + +void +CMatrix::UpdateRW(void) +{ + if (m_attachment) { + m_attachment->right = GetRight(); + m_attachment->up = GetForward(); + m_attachment->at = GetUp(); + m_attachment->pos = GetPosition(); + RwMatrixUpdate(m_attachment); + } +} + +void +CMatrix::operator=(CMatrix const &rhs) +{ + memcpy(this, &rhs, sizeof(f)); + if (m_attachment) + UpdateRW(); +} + +void +CMatrix::CopyOnlyMatrix(const CMatrix &other) +{ + memcpy(this, &other, sizeof(f)); +} + +CMatrix & +CMatrix::operator+=(CMatrix const &rhs) +{ + GetRight() += rhs.GetRight(); + GetForward() += rhs.GetForward(); + GetUp() += rhs.GetUp(); + GetPosition() += rhs.GetPosition(); + return *this; +} + +void +CMatrix::SetUnity(void) +{ + rx = 1.0f; + ry = 0.0f; + rz = 0.0f; + fx = 0.0f; + fy = 1.0f; + fz = 0.0f; + ux = 0.0f; + uy = 0.0f; + uz = 1.0f; + px = 0.0f; + py = 0.0f; + pz = 0.0f; +} + +void +CMatrix::ResetOrientation(void) +{ + rx = 1.0f; + ry = 0.0f; + rz = 0.0f; + fx = 0.0f; + fy = 1.0f; + fz = 0.0f; + ux = 0.0f; + uy = 0.0f; + uz = 1.0f; +} + +void +CMatrix::SetScale(float s) +{ + rx = s; + ry = 0.0f; + rz = 0.0f; + + fx = 0.0f; + fy = s; + fz = 0.0f; + + ux = 0.0f; + uy = 0.0f; + uz = s; + + px = 0.0f; + py = 0.0f; + pz = 0.0f; +} + +void +CMatrix::SetTranslate(float x, float y, float z) +{ + rx = 1.0f; + ry = 0.0f; + rz = 0.0f; + + fx = 0.0f; + fy = 1.0f; + fz = 0.0f; + + ux = 0.0f; + uy = 0.0f; + uz = 1.0f; + + px = x; + py = y; + pz = z; +} + +void +CMatrix::SetRotateXOnly(float angle) +{ + float c = Cos(angle); + float s = Sin(angle); + + rx = 1.0f; + ry = 0.0f; + rz = 0.0f; + + fx = 0.0f; + fy = c; + fz = s; + + ux = 0.0f; + uy = -s; + uz = c; +} + +void +CMatrix::SetRotateYOnly(float angle) +{ + float c = Cos(angle); + float s = Sin(angle); + + rx = c; + ry = 0.0f; + rz = -s; + + fx = 0.0f; + fy = 1.0f; + fz = 0.0f; + + ux = s; + uy = 0.0f; + uz = c; +} + +void +CMatrix::SetRotateZOnly(float angle) +{ + float c = Cos(angle); + float s = Sin(angle); + + rx = c; + ry = s; + rz = 0.0f; + + fx = -s; + fy = c; + fz = 0.0f; + + ux = 0.0f; + uy = 0.0f; + uz = 1.0f; +} + +void +CMatrix::SetRotateX(float angle) +{ + SetRotateXOnly(angle); + px = 0.0f; + py = 0.0f; + pz = 0.0f; +} + + +void +CMatrix::SetRotateY(float angle) +{ + SetRotateYOnly(angle); + px = 0.0f; + py = 0.0f; + pz = 0.0f; +} + +void +CMatrix::SetRotateZ(float angle) +{ + SetRotateZOnly(angle); + px = 0.0f; + py = 0.0f; + pz = 0.0f; +} + +void +CMatrix::SetRotate(float xAngle, float yAngle, float zAngle) +{ + float cX = Cos(xAngle); + float sX = Sin(xAngle); + float cY = Cos(yAngle); + float sY = Sin(yAngle); + float cZ = Cos(zAngle); + float sZ = Sin(zAngle); + + rx = cZ * cY - (sZ * sX) * sY; + ry = (cZ * sX) * sY + sZ * cY; + rz = -cX * sY; + + fx = -sZ * cX; + fy = cZ * cX; + fz = sX; + + ux = (sZ * sX) * cY + cZ * sY; + uy = sZ * sY - (cZ * sX) * cY; + uz = cX * cY; + + px = 0.0f; + py = 0.0f; + pz = 0.0f; +} + +void +CMatrix::RotateX(float x) +{ + float c = Cos(x); + float s = Sin(x); + + float ry = this->ry; + float rz = this->rz; + float uy = this->fy; + float uz = this->fz; + float ay = this->uy; + float az = this->uz; + float py = this->py; + float pz = this->pz; + + this->ry = c * ry - s * rz; + this->rz = c * rz + s * ry; + this->fy = c * uy - s * uz; + this->fz = c * uz + s * uy; + this->uy = c * ay - s * az; + this->uz = c * az + s * ay; + this->py = c * py - s * pz; + this->pz = c * pz + s * py; +} + +void +CMatrix::RotateY(float y) +{ + float c = Cos(y); + float s = Sin(y); + + float rx = this->rx; + float rz = this->rz; + float ux = this->fx; + float uz = this->fz; + float ax = this->ux; + float az = this->uz; + float px = this->px; + float pz = this->pz; + + this->rx = c * rx + s * rz; + this->rz = c * rz - s * rx; + this->fx = c * ux + s * uz; + this->fz = c * uz - s * ux; + this->ux = c * ax + s * az; + this->uz = c * az - s * ax; + this->px = c * px + s * pz; + this->pz = c * pz - s * px; +} + +void +CMatrix::RotateZ(float z) +{ + float c = Cos(z); + float s = Sin(z); + + float ry = this->ry; + float rx = this->rx; + float uy = this->fy; + float ux = this->fx; + float ay = this->uy; + float ax = this->ux; + float py = this->py; + float px = this->px; + + this->rx = c * rx - s * ry; + this->ry = c * ry + s * rx; + this->fx = c * ux - s * uy; + this->fy = c * uy + s * ux; + this->ux = c * ax - s * ay; + this->uy = c * ay + s * ax; + this->px = c * px - s * py; + this->py = c * py + s * px; + +} + +void +CMatrix::Rotate(float x, float y, float z) +{ + float cX = Cos(x); + float sX = Sin(x); + float cY = Cos(y); + float sY = Sin(y); + float cZ = Cos(z); + float sZ = Sin(z); + + float rx = this->rx; + float ry = this->ry; + float rz = this->rz; + float ux = this->fx; + float uy = this->fy; + float uz = this->fz; + float ax = this->ux; + float ay = this->uy; + float az = this->uz; + float px = this->px; + float py = this->py; + float pz = this->pz; + + float x1 = cZ * cY - (sZ * sX) * sY; + float x2 = (cZ * sX) * sY + sZ * cY; + float x3 = -cX * sY; + float y1 = -sZ * cX; + float y2 = cZ * cX; + float y3 = sX; + float z1 = (sZ * sX) * cY + cZ * sY; + float z2 = sZ * sY - (cZ * sX) * cY; + float z3 = cX * cY; + + this->rx = x1 * rx + y1 * ry + z1 * rz; + this->ry = x2 * rx + y2 * ry + z2 * rz; + this->rz = x3 * rx + y3 * ry + z3 * rz; + this->fx = x1 * ux + y1 * uy + z1 * uz; + this->fy = x2 * ux + y2 * uy + z2 * uz; + this->fz = x3 * ux + y3 * uy + z3 * uz; + this->ux = x1 * ax + y1 * ay + z1 * az; + this->uy = x2 * ax + y2 * ay + z2 * az; + this->uz = x3 * ax + y3 * ay + z3 * az; + this->px = x1 * px + y1 * py + z1 * pz; + this->py = x2 * px + y2 * py + z2 * pz; + this->pz = x3 * px + y3 * py + z3 * pz; +} + +CMatrix & +CMatrix::operator*=(CMatrix const &rhs) +{ + // TODO: VU0 code + *this = *this * rhs; + return *this; +} + +void +CMatrix::Reorthogonalise(void) +{ + CVector &r = GetRight(); + CVector &f = GetForward(); + CVector &u = GetUp(); + u = CrossProduct(r, f); + u.Normalise(); + r = CrossProduct(f, u); + r.Normalise(); + f = CrossProduct(u, r); +} + +CMatrix +operator*(const CMatrix &m1, const CMatrix &m2) +{ + // TODO: VU0 code + CMatrix out; + out.rx = m1.rx * m2.rx + m1.fx * m2.ry + m1.ux * m2.rz; + out.ry = m1.ry * m2.rx + m1.fy * m2.ry + m1.uy * m2.rz; + out.rz = m1.rz * m2.rx + m1.fz * m2.ry + m1.uz * m2.rz; + out.fx = m1.rx * m2.fx + m1.fx * m2.fy + m1.ux * m2.fz; + out.fy = m1.ry * m2.fx + m1.fy * m2.fy + m1.uy * m2.fz; + out.fz = m1.rz * m2.fx + m1.fz * m2.fy + m1.uz * m2.fz; + out.ux = m1.rx * m2.ux + m1.fx * m2.uy + m1.ux * m2.uz; + out.uy = m1.ry * m2.ux + m1.fy * m2.uy + m1.uy * m2.uz; + out.uz = m1.rz * m2.ux + m1.fz * m2.uy + m1.uz * m2.uz; + out.px = m1.rx * m2.px + m1.fx * m2.py + m1.ux * m2.pz + m1.px; + out.py = m1.ry * m2.px + m1.fy * m2.py + m1.uy * m2.pz + m1.py; + out.pz = m1.rz * m2.px + m1.fz * m2.py + m1.uz * m2.pz + m1.pz; + return out; +} + +CMatrix & +Invert(const CMatrix &src, CMatrix &dst) +{ + // TODO: VU0 code + dst.f[3][0] = dst.f[3][1] = dst.f[3][2] = 0.0f; + + dst.f[0][0] = src.f[0][0]; + dst.f[0][1] = src.f[1][0]; + dst.f[0][2] = src.f[2][0]; + + dst.f[1][0] = src.f[0][1]; + dst.f[1][1] = src.f[1][1]; + dst.f[1][2] = src.f[2][1]; + + dst.f[2][0] = src.f[0][2]; + dst.f[2][1] = src.f[1][2]; + dst.f[2][2] = src.f[2][2]; + + + dst.f[3][0] += dst.f[0][0] * src.f[3][0]; + dst.f[3][1] += dst.f[0][1] * src.f[3][0]; + dst.f[3][2] += dst.f[0][2] * src.f[3][0]; + + dst.f[3][0] += dst.f[1][0] * src.f[3][1]; + dst.f[3][1] += dst.f[1][1] * src.f[3][1]; + dst.f[3][2] += dst.f[1][2] * src.f[3][1]; + + dst.f[3][0] += dst.f[2][0] * src.f[3][2]; + dst.f[3][1] += dst.f[2][1] * src.f[3][2]; + dst.f[3][2] += dst.f[2][2] * src.f[3][2]; + + dst.f[3][0] = -dst.f[3][0]; + dst.f[3][1] = -dst.f[3][1]; + dst.f[3][2] = -dst.f[3][2]; + + return dst; +} + +void +CMatrix::CopyToRwMatrix(RwMatrix* matrix) +{ + matrix->right = GetRight(); + matrix->up = GetForward(); + matrix->at = GetUp(); + matrix->pos = GetPosition(); + RwMatrixUpdate(matrix); +} + +CMatrix +Invert(const CMatrix &matrix) +{ + CMatrix inv; + return Invert(matrix, inv); +} + +void +CCompressedMatrixNotAligned::CompressFromFullMatrix(CMatrix &other) +{ + m_rightX = 127.0f * other.GetRight().x; + m_rightY = 127.0f * other.GetRight().y; + m_rightZ = 127.0f * other.GetRight().z; + m_upX = 127.0f * other.GetForward().x; + m_upY = 127.0f * other.GetForward().y; + m_upZ = 127.0f * other.GetForward().z; + m_vecPos = other.GetPosition(); +} + +void +CCompressedMatrixNotAligned::DecompressIntoFullMatrix(CMatrix &other) +{ + other.GetRight().x = m_rightX / 127.0f; + other.GetRight().y = m_rightY / 127.0f; + other.GetRight().z = m_rightZ / 127.0f; + other.GetForward().x = m_upX / 127.0f; + other.GetForward().y = m_upY / 127.0f; + other.GetForward().z = m_upZ / 127.0f; + other.GetUp() = CrossProduct(other.GetRight(), other.GetForward()); + other.GetPosition() = m_vecPos; + other.Reorthogonalise(); +} \ No newline at end of file diff --git a/src/miami/math/Matrix.h b/src/miami/math/Matrix.h new file mode 100644 index 00000000..0adcf32c --- /dev/null +++ b/src/miami/math/Matrix.h @@ -0,0 +1,154 @@ +#pragma once + +class CMatrix +{ +public: +#ifdef GTA_PS2 + union + { + float f[4][4]; + struct + { + float rx, ry, rz; + RwMatrix *m_attachment; + float fx, fy, fz; + bool m_hasRwMatrix; // are we the owner? + float ux, uy, uz, uw; + float px, py, pz, pw; + }; + }; +#else + union + { + float f[4][4]; + struct + { + float rx, ry, rz, rw; + float fx, fy, fz, fw; + float ux, uy, uz, uw; + float px, py, pz, pw; + }; + }; + + RwMatrix *m_attachment; + bool m_hasRwMatrix; // are we the owner? +#endif + + CMatrix(void); + CMatrix(CMatrix const &m); + CMatrix(RwMatrix *matrix, bool owner = false); + CMatrix(float scale){ + m_attachment = nil; + m_hasRwMatrix = false; + SetScale(scale); + } + ~CMatrix(void); + void Attach(RwMatrix *matrix, bool owner = false); + void AttachRW(RwMatrix *matrix, bool owner = false); + void Detach(void); + void Update(void); + void UpdateRW(void); + void operator=(CMatrix const &rhs); + CMatrix &operator+=(CMatrix const &rhs); + CMatrix &operator*=(CMatrix const &rhs); + + CVector &GetPosition(void) { return *(CVector*)&px; } + CVector &GetRight(void) { return *(CVector*)℞ } + CVector &GetForward(void) { return *(CVector*)&fx; } + CVector &GetUp(void) { return *(CVector*)&ux; } + + const CVector &GetPosition(void) const { return *(CVector*)&px; } + const CVector &GetRight(void) const { return *(CVector*)℞ } + const CVector &GetForward(void) const { return *(CVector*)&fx; } + const CVector &GetUp(void) const { return *(CVector*)&ux; } + + + void SetTranslate(float x, float y, float z); + void SetTranslate(const CVector &trans){ SetTranslate(trans.x, trans.y, trans.z); } + void Translate(float x, float y, float z){ + px += x; + py += y; + pz += z; + } + void Translate(const CVector &trans){ Translate(trans.x, trans.y, trans.z); } + + void SetScale(float s); + void Scale(float scale) + { + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + f[i][j] *= scale; + } + void Scale(float sx, float sy, float sz) + { + for (int i = 0; i < 3; i++){ + f[i][0] *= sx; + f[i][1] *= sy; + f[i][2] *= sz; + } + } + + + void SetRotateXOnly(float angle); + void SetRotateYOnly(float angle); + void SetRotateZOnly(float angle); + void SetRotateX(float angle); + void SetRotateY(float angle); + void SetRotateZ(float angle); + void SetRotate(float xAngle, float yAngle, float zAngle); + void Rotate(float x, float y, float z); + void RotateX(float x); + void RotateY(float y); + void RotateZ(float z); + + void Reorthogonalise(void); + void CopyOnlyMatrix(const CMatrix &other); + void SetUnity(void); + void ResetOrientation(void); + + void CopyToRwMatrix(RwMatrix* matrix); + + void SetTranslateOnly(float x, float y, float z) { + px = x; + py = y; + pz = z; + } + void SetTranslateOnly(const CVector& pos) { + SetTranslateOnly(pos.x, pos.y, pos.z); + } + void CheckIntegrity(){} +}; + + +CMatrix &Invert(const CMatrix &src, CMatrix &dst); +CMatrix Invert(const CMatrix &matrix); +CMatrix operator*(const CMatrix &m1, const CMatrix &m2); +inline CVector MultiplyInverse(const CMatrix &mat, const CVector &vec) +{ + CVector v(vec.x - mat.px, vec.y - mat.py, vec.z - mat.pz); + return CVector( + mat.rx * v.x + mat.ry * v.y + mat.rz * v.z, + mat.fx * v.x + mat.fy * v.y + mat.fz * v.z, + mat.ux * v.x + mat.uy * v.y + mat.uz * v.z); +} + + + +class CCompressedMatrixNotAligned +{ + CVector m_vecPos; + int8 m_rightX; + int8 m_rightY; + int8 m_rightZ; + int8 m_upX; + int8 m_upY; + int8 m_upZ; +public: + void CompressFromFullMatrix(CMatrix &other); + void DecompressIntoFullMatrix(CMatrix &other); +}; + +class CCompressedMatrix : public CCompressedMatrixNotAligned +{ + int _alignment; // no clue what would this align to +}; \ No newline at end of file diff --git a/src/miami/math/Quaternion.cpp b/src/miami/math/Quaternion.cpp new file mode 100644 index 00000000..b0e782e2 --- /dev/null +++ b/src/miami/math/Quaternion.cpp @@ -0,0 +1,177 @@ +#include "common.h" +#include "Quaternion.h" + +void +CQuaternion::Normalise(void) +{ + float sq = MagnitudeSqr(); + if (sq == 0.0f) + w = 1.0f; + else { + float invsqrt = RecipSqrt(sq); + x *= invsqrt; + y *= invsqrt; + z *= invsqrt; + w *= invsqrt; + } +} + +void +CQuaternion::Slerp(const CQuaternion &q1, const CQuaternion &q2, float theta, float invSin, float t) +{ + if (theta == 0.0f) + *this = q2; + else { + float w1, w2; + if (theta > PI / 2) { + theta = PI - theta; + w1 = Sin((1.0f - t) * theta) * invSin; + w2 = -Sin(t * theta) * invSin; + } else { + w1 = Sin((1.0f - t) * theta) * invSin; + w2 = Sin(t * theta) * invSin; + } + // TODO: VU0 code + *this = w1 * q1 + w2 * q2; + } +} + +void +CQuaternion::Multiply(const CQuaternion &q1, const CQuaternion &q2) +{ + x = (q2.z * q1.y) - (q1.z * q2.y) + (q1.x * q2.w) + (q2.x * q1.w); + y = (q2.x * q1.z) - (q1.x * q2.z) + (q1.y * q2.w) + (q2.y * q1.w); + z = (q2.y * q1.x) - (q1.y * q2.x) + (q1.z * q2.w) + (q2.z * q1.w); + w = (q2.w * q1.w) - (q2.x * q1.x) - (q2.y * q1.y) - (q2.z * q1.z); +} + +void +CQuaternion::Get(RwV3d *axis, float *angle) +{ + *angle = Acos(w); + float s = Sin(*angle); + + axis->x = x * (1.0f / s); + axis->y = y * (1.0f / s); + axis->z = z * (1.0f / s); +} + +void +CQuaternion::Set(RwV3d *axis, float angle) +{ + float halfCos = Cos(angle * 0.5f); + float halfSin = Sin(angle * 0.5f); + x = axis->x * halfSin; + y = axis->y * halfSin; + z = axis->z * halfSin; + w = halfCos; +} + +void +CQuaternion::Get(RwMatrix *matrix) +{ + float x2 = x + x; + float y2 = y + y; + float z2 = z + z; + + float x_2x = x * x2; + float x_2y = x * y2; + float x_2z = x * z2; + float y_2y = y * y2; + float y_2z = y * z2; + float z_2z = z * z2; + float w_2x = w * x2; + float w_2y = w * y2; + float w_2z = w * z2; + + matrix->right.x = 1.0f - (y_2y + z_2z); + matrix->up.x = x_2y - w_2z; + matrix->at.x = x_2z + w_2y; + matrix->right.y = x_2y + w_2z; + matrix->up.y = 1.0f - (x_2x + z_2z); + matrix->at.y = y_2z - w_2x; + matrix->right.z = x_2z - w_2y; + matrix->up.z = y_2z + w_2x; + matrix->at.z = 1.0f - (x_2x + y_2y); +} + +void +CQuaternion::Set(const RwMatrix &matrix) +{ + float f, s, m; + + f = matrix.up.y + matrix.right.x + matrix.at.z; + if (f >= 0.0f) { + s = Sqrt(f + 1.0f); + w = 0.5f * s; + m = 0.5f / s; + x = (matrix.up.z - matrix.at.y) * m; + y = (matrix.at.x - matrix.right.z) * m; + z = (matrix.right.y - matrix.up.x) * m; + return; + } + + f = matrix.right.x - matrix.up.y - matrix.at.z; + if (f >= 0.0f) { + s = Sqrt(f + 1.0f); + x = 0.5f * s; + m = 0.5f / s; + y = (matrix.up.x + matrix.right.y) * m; + z = (matrix.at.x + matrix.right.z) * m; + w = (matrix.up.z - matrix.at.y) * m; + return; + } + + f = matrix.up.y - matrix.right.x - matrix.at.z; + if (f >= 0.0f) { + s = Sqrt(f + 1.0f); + y = 0.5f * s; + m = 0.5f / s; + w = (matrix.at.x - matrix.right.z) * m; + x = (matrix.up.x - matrix.right.y) * m; + z = (matrix.at.y + matrix.up.z) * m; + return; + } + + f = matrix.at.z - (matrix.up.y + matrix.right.x); + s = Sqrt(f + 1.0f); + z = 0.5f * s; + m = 0.5f / s; + w = (matrix.right.y - matrix.up.x) * m; + x = (matrix.at.x + matrix.right.z) * m; + y = (matrix.at.y + matrix.up.z) * m; +} + +void +CQuaternion::Get(float *f1, float *f2, float *f3) +{ + RwMatrix matrix; + + Get(&matrix); + *f3 = Atan2(matrix.right.y, matrix.up.y); + if (*f3 < 0.0f) + *f3 += TWOPI; + float s = Sin(*f3); + float c = Cos(*f3); + *f1 = Atan2(-matrix.at.y, s * matrix.right.y + c * matrix.up.y); + if (*f1 < 0.0f) + *f1 += TWOPI; + *f2 = Atan2(-(matrix.right.z * c - matrix.up.z * s), matrix.right.x * c - matrix.up.x * s); + if (*f2 < 0.0f) + *f2 += TWOPI; +} + +void +CQuaternion::Set(float f1, float f2, float f3) +{ + float c1 = Cos(f1 * 0.5f); + float c2 = Cos(f2 * 0.5f); + float c3 = Cos(f3 * 0.5f); + float s1 = Sin(f1 * 0.5f); + float s2 = Sin(f2 * 0.5f); + float s3 = Sin(f3 * 0.5f); + x = ((c2 * c1) * s3) - ((s2 * s1) * c3); + y = ((s1 * c2) * c3) + ((s2 * c1) * s3); + z = ((s2 * c1) * c3) - ((s1 * c2) * s3); + w = ((c2 * c1) * c3) + ((s2 * s1) * s3); +} \ No newline at end of file diff --git a/src/miami/math/Quaternion.h b/src/miami/math/Quaternion.h new file mode 100644 index 00000000..47c94f7c --- /dev/null +++ b/src/miami/math/Quaternion.h @@ -0,0 +1,95 @@ +#pragma once + +// TODO: actually implement this +class CQuaternion +{ +public: + float x, y, z, w; + CQuaternion(void) {} + CQuaternion(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {} + + float Magnitude(void) const { return Sqrt(x*x + y*y + z*z + w*w); } + float MagnitudeSqr(void) const { return x*x + y*y + z*z + w*w; } + void Normalise(void); + void Multiply(const CQuaternion &q1, const CQuaternion &q2); + void Invert(void){ // Conjugate would have been a better name + x = -x; + y = -y; + z = -z; + } + + const CQuaternion &operator+=(CQuaternion const &right) { + x += right.x; + y += right.y; + z += right.z; + w += right.w; + return *this; + } + + const CQuaternion &operator-=(CQuaternion const &right) { + x -= right.x; + y -= right.y; + z -= right.z; + w -= right.w; + return *this; + } + + const CQuaternion &operator*=(float right) { + x *= right; + y *= right; + z *= right; + w *= right; + return *this; + } + + const CQuaternion &operator/=(float right) { + x /= right; + y /= right; + z /= right; + w /= right; + return *this; + } + + CQuaternion operator-() const { + return CQuaternion(-x, -y, -z, -w); + } + + void Slerp(const CQuaternion &q1, const CQuaternion &q2, float theta, float invSin, float t); + void Get(RwV3d *axis, float *angle); + void Set(RwV3d *axis, float angle); + void Get(RwMatrix *matrix); + void Set(const RwMatrix &matrix); + void Set(float f1, float f2, float f3); + void Get(float *f1, float *f2, float *f3); +}; + +inline float +DotProduct(const CQuaternion &q1, const CQuaternion &q2) +{ + return q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; +} + +inline CQuaternion operator+(const CQuaternion &left, const CQuaternion &right) +{ + return CQuaternion(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w); +} + +inline CQuaternion operator-(const CQuaternion &left, const CQuaternion &right) +{ + return CQuaternion(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w); +} + +inline CQuaternion operator*(const CQuaternion &left, float right) +{ + return CQuaternion(left.x * right, left.y * right, left.z * right, left.w * right); +} + +inline CQuaternion operator*(float left, const CQuaternion &right) +{ + return CQuaternion(left * right.x, left * right.y, left * right.z, left * right.w); +} + +inline CQuaternion operator/(const CQuaternion &left, float right) +{ + return CQuaternion(left.x / right, left.y / right, left.z / right, left.w / right); +} diff --git a/src/miami/math/Rect.cpp b/src/miami/math/Rect.cpp new file mode 100644 index 00000000..de6320ad --- /dev/null +++ b/src/miami/math/Rect.cpp @@ -0,0 +1,17 @@ +#include "common.h" + +CRect::CRect(void) +{ + left = 1000000.0f; + top = 1000000.0f; + right = -1000000.0f; + bottom = -1000000.0f; +} + +CRect::CRect(float l, float t, float r, float b) +{ + left = l; + top = t; + right = r; + bottom = b; +} \ No newline at end of file diff --git a/src/miami/math/Rect.h b/src/miami/math/Rect.h new file mode 100644 index 00000000..e9b25896 --- /dev/null +++ b/src/miami/math/Rect.h @@ -0,0 +1,71 @@ +#pragma once + +class CRect +{ +public: + float left; // x min + float bottom; // y max + float right; // x max + float top; // y min + + CRect(void); + CRect(float l, float t, float r, float b); + void ContainPoint(CVector const &v){ + if(v.x < left) left = v.x; + if(v.x > right) right = v.x; + if(v.y < top) top = v.y; + if(v.y > bottom) bottom = v.y; + } + void ContainRect(const CRect &r){ + if(r.left < left) left = r.left; + if(r.right > right) right = r.right; + if(r.top < top) top = r.top; + if(r.bottom > bottom) bottom = r.bottom; + } + + bool IsPointInside(const CVector2D &p){ + return p.x >= left && + p.x <= right && + p.y >= top && + p.y <= bottom; + } + bool IsPointInside(const CVector2D &p, float extraRadius){ + return p.x >= left-extraRadius && + p.x <= right+extraRadius && + p.y >= top-extraRadius && + p.y <= bottom+extraRadius; + } + + void Translate(float x, float y){ + left += x; + right += x; + bottom += y; + top += y; + } + + void Grow(float r) { + left -= r; + right += r; + top -= r; + bottom += r; + } + + void Grow(float l, float r) + { + left -= l; + top -= l; + right += r; + bottom += r; + } + + void Grow(float l, float r, float t, float b) + { + left -= l; + top -= t; + right += r; + bottom += b; + } + + float GetWidth(void) { return right - left; } + float GetHeight(void) { return bottom - top; } +}; diff --git a/src/miami/math/Vector.cpp b/src/miami/math/Vector.cpp new file mode 100644 index 00000000..ee76e555 --- /dev/null +++ b/src/miami/math/Vector.cpp @@ -0,0 +1,46 @@ +#include "common.h" + +void +CVector::Normalise(void) +{ + float sq = MagnitudeSqr(); + if (sq > 0.0f) { + float invsqrt = RecipSqrt(sq); + x *= invsqrt; + y *= invsqrt; + z *= invsqrt; + } else + x = 1.0f; +} + +CVector +CrossProduct(const CVector &v1, const CVector &v2) +{ + return CVector(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x); +} + +CVector +Multiply3x3(const CMatrix &mat, const CVector &vec) +{ + // TODO: VU0 code + return CVector(mat.rx * vec.x + mat.fx * vec.y + mat.ux * vec.z, + mat.ry * vec.x + mat.fy * vec.y + mat.uy * vec.z, + mat.rz * vec.x + mat.fz * vec.y + mat.uz * vec.z); +} + +CVector +Multiply3x3(const CVector &vec, const CMatrix &mat) +{ + return CVector(mat.rx * vec.x + mat.ry * vec.y + mat.rz * vec.z, + mat.fx * vec.x + mat.fy * vec.y + mat.fz * vec.z, + mat.ux * vec.x + mat.uy * vec.y + mat.uz * vec.z); +} + +CVector +operator*(const CMatrix &mat, const CVector &vec) +{ + // TODO: VU0 code + return CVector(mat.rx * vec.x + mat.fx * vec.y + mat.ux * vec.z + mat.px, + mat.ry * vec.x + mat.fy * vec.y + mat.uy * vec.z + mat.py, + mat.rz * vec.x + mat.fz * vec.y + mat.uz * vec.z + mat.pz); +} diff --git a/src/miami/math/Vector.h b/src/miami/math/Vector.h new file mode 100644 index 00000000..02128454 --- /dev/null +++ b/src/miami/math/Vector.h @@ -0,0 +1,129 @@ +#pragma once + +class CVector : public RwV3d +{ +public: + CVector(void) {} + CVector(float x, float y, float z) + { + this->x = x; + this->y = y; + this->z = z; + } + + CVector(const RwV3d &v) + { + x = v.x; + y = v.y; + z = v.z; + } + // (0,1,0) means no rotation. So get right vector and its atan + float Heading(void) const { return Atan2(-x, y); } + float Magnitude(void) const { return Sqrt(x*x + y*y + z*z); } + float MagnitudeSqr(void) const { return x*x + y*y + z*z; } + float Magnitude2D(void) const { return Sqrt(x*x + y*y); } + float MagnitudeSqr2D(void) const { return x*x + y*y; } + void Normalise(void); + + void Normalise2D(void) { + float sq = MagnitudeSqr2D(); + float invsqrt = RecipSqrt(sq); + x *= invsqrt; + y *= invsqrt; + } + + const CVector &operator+=(CVector const &right) { + x += right.x; + y += right.y; + z += right.z; + return *this; + } + + const CVector &operator-=(CVector const &right) { + x -= right.x; + y -= right.y; + z -= right.z; + return *this; + } + + const CVector &operator*=(float right) { + x *= right; + y *= right; + z *= right; + return *this; + } + + const CVector &operator/=(float right) { + x /= right; + y /= right; + z /= right; + return *this; + } + + CVector operator-() const { + return CVector(-x, -y, -z); + } + + const bool operator==(CVector const &right) const { + return x == right.x && y == right.y && z == right.z; + } + + const bool operator!=(CVector const &right) const { + return x != right.x || y != right.y || z != right.z; + } + + bool IsZero(void) const { return x == 0.0f && y == 0.0f && z == 0.0f; } +}; + +inline CVector operator+(const CVector &left, const CVector &right) +{ + return CVector(left.x + right.x, left.y + right.y, left.z + right.z); +} + +inline CVector operator-(const CVector &left, const CVector &right) +{ + return CVector(left.x - right.x, left.y - right.y, left.z - right.z); +} + +inline CVector operator*(const CVector &left, float right) +{ + return CVector(left.x * right, left.y * right, left.z * right); +} + +inline CVector operator*(float left, const CVector &right) +{ + return CVector(left * right.x, left * right.y, left * right.z); +} + +inline CVector operator/(const CVector &left, float right) +{ + return CVector(left.x / right, left.y / right, left.z / right); +} + +inline float +DotProduct(const CVector &v1, const CVector &v2) +{ + return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; +} + +CVector CrossProduct(const CVector &v1, const CVector &v2); + +inline float +Distance(const CVector &v1, const CVector &v2) +{ + return (v2 - v1).Magnitude(); +} + +inline float +Distance2D(const CVector &v1, const CVector &v2) +{ + float x = v2.x - v1.x; + float y = v2.y - v1.y; + return Sqrt(x*x + y*y); +} + +class CMatrix; + +CVector Multiply3x3(const CMatrix &mat, const CVector &vec); +CVector Multiply3x3(const CVector &vec, const CMatrix &mat); +CVector operator*(const CMatrix &mat, const CVector &vec); \ No newline at end of file diff --git a/src/miami/math/Vector2D.h b/src/miami/math/Vector2D.h new file mode 100644 index 00000000..deabd0b1 --- /dev/null +++ b/src/miami/math/Vector2D.h @@ -0,0 +1,104 @@ +#pragma once + +class CVector2D +{ +public: + float x, y; + CVector2D(void) {} + CVector2D(float x, float y) : x(x), y(y) {} + CVector2D(const CVector &v) : x(v.x), y(v.y) {} + float Heading(void) const { return Atan2(-x, y); } + float Magnitude(void) const { return Sqrt(x*x + y*y); } + float MagnitudeSqr(void) const { return x*x + y*y; } + + void Normalise(void) { + float sq = MagnitudeSqr(); + if(sq > 0.0f){ + float invsqrt = RecipSqrt(sq); + x *= invsqrt; + y *= invsqrt; + }else + x = 1.0f; + } + + const CVector2D &operator+=(CVector2D const &right) { + x += right.x; + y += right.y; + return *this; + } + + const CVector2D &operator-=(CVector2D const &right) { + x -= right.x; + y -= right.y; + return *this; + } + + const CVector2D &operator*=(float right) { + x *= right; + y *= right; + return *this; + } + + const CVector2D &operator/=(float right) { + x /= right; + y /= right; + return *this; + } + CVector2D operator-(const CVector2D &rhs) const { + return CVector2D(x-rhs.x, y-rhs.y); + } + CVector2D operator+(const CVector2D &rhs) const { + return CVector2D(x+rhs.x, y+rhs.y); + } + CVector2D operator/(float t) const { + return CVector2D(x/t, y/t); + } + CVector2D operator-() const { + return CVector2D(-x, -y); + } +}; + +inline float +DotProduct2D(const CVector2D &v1, const CVector2D &v2) +{ + return v1.x*v2.x + v1.y*v2.y; +} + +inline float +CrossProduct2D(const CVector2D &v1, const CVector2D &v2) +{ + return v1.x*v2.y - v1.y*v2.x; +} + +inline float +Distance2D(const CVector2D &v, float x, float y) +{ + return Sqrt((v.x-x)*(v.x-x) + (v.y-y)*(v.y-y)); +} + +inline float +DistanceSqr2D(const CVector2D &v, float x, float y) +{ + return (v.x-x)*(v.x-x) + (v.y-y)*(v.y-y); +} + +inline void +NormalizeXY(float &x, float &y) +{ + float l = Sqrt(x*x + y*y); + if(l != 0.0f){ + x /= l; + y /= l; + }else + x = 1.0f; +} + +inline CVector2D operator*(const CVector2D &left, float right) +{ + return CVector2D(left.x * right, left.y * right); +} + +inline CVector2D operator*(float left, const CVector2D &right) +{ + return CVector2D(left * right.x, left * right.y); +} diff --git a/src/miami/math/VuVector.h b/src/miami/math/VuVector.h new file mode 100644 index 00000000..41584095 --- /dev/null +++ b/src/miami/math/VuVector.h @@ -0,0 +1,32 @@ +#pragma once + +class TYPEALIGN(16) CVuVector : public CVector +{ +public: + float w; + CVuVector(void) {} + CVuVector(float x, float y, float z) : CVector(x, y, z) {} + CVuVector(float x, float y, float z, float w) : CVector(x, y, z), w(w) {} + CVuVector(const CVector &v) : CVector(v.x, v.y, v.z) {} + CVuVector(const RwV3d &v) : CVector(v) {} +/* + void Normalise(void) { + float sq = MagnitudeSqr(); + // TODO: VU0 code + if(sq > 0.0f){ + float invsqrt = RecipSqrt(sq); + x *= invsqrt; + y *= invsqrt; + z *= invsqrt; + }else + x = 1.0f; + } +*/ + + // TODO: operator- +}; + +void TransformPoint(CVuVector &out, const CMatrix &mat, const CVuVector &in); +void TransformPoint(CVuVector &out, const CMatrix &mat, const RwV3d &in); +void TransformPoints(CVuVector *out, int n, const CMatrix &mat, const RwV3d *in, int stride); +void TransformPoints(CVuVector *out, int n, const CMatrix &mat, const CVuVector *in); diff --git a/src/miami/math/math.cpp b/src/miami/math/math.cpp new file mode 100644 index 00000000..8cb56dab --- /dev/null +++ b/src/miami/math/math.cpp @@ -0,0 +1,118 @@ +#include "common.h" + +#include "VuVector.h" + +// TODO: move more stuff into here + + +void TransformPoint(CVuVector &out, const CMatrix &mat, const CVuVector &in) +{ +#ifdef GTA_PS2 + __asm__ __volatile__("\n\ + lqc2 vf01,0x0(%2)\n\ + lqc2 vf02,0x0(%1)\n\ + lqc2 vf03,0x10(%1)\n\ + lqc2 vf04,0x20(%1)\n\ + lqc2 vf05,0x30(%1)\n\ + vmulax.xyz ACC, vf02,vf01\n\ + vmadday.xyz ACC, vf03,vf01\n\ + vmaddaz.xyz ACC, vf04,vf01\n\ + vmaddw.xyz vf06,vf05,vf00\n\ + sqc2 vf06,0x0(%0)\n\ + ": : "r" (&out) , "r" (&mat) ,"r" (&in): "memory"); +#else + out = mat * in; +#endif +} + +void TransformPoint(CVuVector &out, const CMatrix &mat, const RwV3d &in) +{ +#ifdef GTA_PS2 + __asm__ __volatile__("\n\ + ldr $8,0x0(%2)\n\ + ldl $8,0x7(%2)\n\ + lw $9,0x8(%2)\n\ + pcpyld $10,$9,$8\n\ + qmtc2 $10,vf01\n\ + lqc2 vf02,0x0(%1)\n\ + lqc2 vf03,0x10(%1)\n\ + lqc2 vf04,0x20(%1)\n\ + lqc2 vf05,0x30(%1)\n\ + vmulax.xyz ACC, vf02,vf01\n\ + vmadday.xyz ACC, vf03,vf01\n\ + vmaddaz.xyz ACC, vf04,vf01\n\ + vmaddw.xyz vf06,vf05,vf00\n\ + sqc2 vf06,0x0(%0)\n\ + ": : "r" (&out) , "r" (&mat) ,"r" (&in): "memory"); +#else + out = mat * in; +#endif +} + +void TransformPoints(CVuVector *out, int n, const CMatrix &mat, const RwV3d *in, int stride) +{ +#ifdef GTA_PS3 + __asm__ __volatile__("\n\ + paddub $3,%4,$0\n\ + lqc2 vf02,0x0(%2)\n\ + lqc2 vf03,0x10(%2)\n\ + lqc2 vf04,0x20(%2)\n\ + lqc2 vf05,0x30(%2)\n\ + ldr $8,0x0(%3)\n\ + ldl $8,0x7(%3)\n\ + lw $9,0x8(%3)\n\ + pcpyld $10,$9,$8\n\ + qmtc2 $10,vf01\n\ + 1: vmulax.xyz ACC, vf02,vf01\n\ + vmadday.xyz ACC, vf03,vf01\n\ + vmaddaz.xyz ACC, vf04,vf01\n\ + vmaddw.xyz vf06,vf05,vf00\n\ + add %3,%3,$3\n\ + ldr $8,0x0(%3)\n\ + ldl $8,0x7(%3)\n\ + lw $9,0x8(%3)\n\ + pcpyld $10,$9,$8\n\ + qmtc2 $10,vf01\n\ + addi %1,%1,-1\n\ + addiu %0,%0,0x10\n\ + sqc2 vf06,-0x10(%0)\n\ + bnez %1,1b\n\ + ": : "r" (out) , "r" (n), "r" (&mat), "r" (in), "r" (stride): "memory"); +#else + while(n--){ + *out = mat * *in; + in = (RwV3d*)((uint8*)in + stride); + out++; + } +#endif +} + +void TransformPoints(CVuVector *out, int n, const CMatrix &mat, const CVuVector *in) +{ +#ifdef GTA_PS2 + __asm__ __volatile__("\n\ + lqc2 vf02,0x0(%2)\n\ + lqc2 vf03,0x10(%2)\n\ + lqc2 vf04,0x20(%2)\n\ + lqc2 vf05,0x30(%2)\n\ + lqc2 vf01,0x0(%3)\n\ + nop\n\ + 1: vmulax.xyz ACC, vf02,vf01\n\ + vmadday.xyz ACC, vf03,vf01\n\ + vmaddaz.xyz ACC, vf04,vf01\n\ + vmaddw.xyz vf06,vf05,vf00\n\ + lqc2 vf01,0x10(%3)\n\ + addiu %3,%3,0x10\n\ + addi %1,%1,-1\n\ + addiu %0,%0,0x10\n\ + sqc2 vf06,-0x10(%0)\n\ + bnez %1,1b\n\ + ": : "r" (out) , "r" (n), "r" (&mat) ,"r" (in): "memory"); +#else + while(n--){ + *out = mat * *in; + in++; + out++; + } +#endif +} diff --git a/src/miami/math/maths.h b/src/miami/math/maths.h new file mode 100644 index 00000000..6a228036 --- /dev/null +++ b/src/miami/math/maths.h @@ -0,0 +1,19 @@ +#pragma once + +// wrapper around float versions of functions +// in gta they are in CMaths but that makes the code rather noisy + +inline float Sin(float x) { return sinf(x); } +inline float Asin(float x) { return asinf(x); } +inline float Cos(float x) { return cosf(x); } +inline float Acos(float x) { return acosf(x); } +inline float Tan(float x) { return tanf(x); } +inline float Atan(float x) { return atanf(x); } +inline float Atan2(float y, float x) { return atan2f(y, x); } +inline float Abs(float x) { return fabsf(x); } +inline float Sqrt(float x) { return sqrtf(x); } +inline float RecipSqrt(float x, float y) { return x/Sqrt(y); } +inline float RecipSqrt(float x) { return RecipSqrt(1.0f, x); } +inline float Pow(float x, float y) { return powf(x, y); } +inline float Floor(float x) { return floorf(x); } +inline float Ceil(float x) { return ceilf(x); } diff --git a/src/miami/modelinfo/BaseModelInfo.cpp b/src/miami/modelinfo/BaseModelInfo.cpp new file mode 100644 index 00000000..709420fd --- /dev/null +++ b/src/miami/modelinfo/BaseModelInfo.cpp @@ -0,0 +1,103 @@ +#include "common.h" + +#include "templates.h" +#include "TxdStore.h" +#include "2dEffect.h" +#include "BaseModelInfo.h" +#include "ModelInfo.h" +#include "ColModel.h" + +CBaseModelInfo::CBaseModelInfo(ModelInfoType type) +{ + m_colModel = nil; + m_2dEffectsID = -1; + m_objectId = -1; + m_refCount = 0; + m_txdSlot = -1; + m_type = type; + m_num2dEffects = 0; + m_bOwnsColModel = false; +} + +void +CBaseModelInfo::Shutdown(void) +{ + DeleteCollisionModel(); + DeleteRwObject(); + m_2dEffectsID = -1; + m_num2dEffects = 0; + m_txdSlot = -1; + m_objectId = -1; +} + +void +CBaseModelInfo::DeleteCollisionModel(void) +{ + if(m_colModel && m_bOwnsColModel){ + if(m_colModel) + delete m_colModel; + m_colModel = nil; + } +} + +void +CBaseModelInfo::AddRef(void) +{ + m_refCount++; + AddTexDictionaryRef(); +} + +void +CBaseModelInfo::RemoveRef(void) +{ + m_refCount--; + RemoveTexDictionaryRef(); +} + +void +CBaseModelInfo::SetTexDictionary(const char *name) +{ + int slot = CTxdStore::FindTxdSlot(name); + if(slot == -1) + slot = CTxdStore::AddTxdSlot(name); + m_txdSlot = slot; +} + +void +CBaseModelInfo::AddTexDictionaryRef(void) +{ + CTxdStore::AddRef(m_txdSlot); +} + +void +CBaseModelInfo::RemoveTexDictionaryRef(void) +{ + CTxdStore::RemoveRef(m_txdSlot); +} + +void +CBaseModelInfo::Init2dEffects(void) +{ + m_2dEffectsID = -1; + m_num2dEffects = 0; +} + +void +CBaseModelInfo::Add2dEffect(C2dEffect *fx) +{ + if(m_2dEffectsID >= 0) + m_num2dEffects++; + else{ + m_2dEffectsID = CModelInfo::Get2dEffectStore().GetIndex(fx); + m_num2dEffects = 1; + } +} + +C2dEffect* +CBaseModelInfo::Get2dEffect(int n) +{ + if(m_2dEffectsID >= 0) + return CModelInfo::Get2dEffectStore().GetItem(m_2dEffectsID+n); + else + return nil; +} diff --git a/src/miami/modelinfo/BaseModelInfo.h b/src/miami/modelinfo/BaseModelInfo.h new file mode 100644 index 00000000..2d1dc8ac --- /dev/null +++ b/src/miami/modelinfo/BaseModelInfo.h @@ -0,0 +1,74 @@ +#pragma once + +struct CColModel; + +#define MAX_MODEL_NAME (21) + +enum ModelInfoType +{ + MITYPE_NA, + MITYPE_SIMPLE, + MITYPE_MLO, // unused but still in enum + MITYPE_TIME, + MITYPE_WEAPON, + MITYPE_CLUMP, + MITYPE_VEHICLE, + MITYPE_PED, + MITYPE_XTRACOMPS, // unused but still in enum + MITYPE_HAND // xbox and mobile +}; + +class C2dEffect; + +class CBaseModelInfo +{ +protected: + char m_name[MAX_MODEL_NAME]; + uint8 m_type; + uint8 m_num2dEffects; + bool m_bOwnsColModel; + CColModel *m_colModel; + int16 m_2dEffectsID; + int16 m_objectId; + uint16 m_refCount; + int16 m_txdSlot; + +public: + CBaseModelInfo(ModelInfoType type); + virtual ~CBaseModelInfo() {} + virtual void Shutdown(void); + virtual void DeleteRwObject(void) = 0; + virtual RwObject *CreateInstance(void) = 0; + virtual RwObject *CreateInstance(RwMatrix *) = 0; + virtual RwObject *GetRwObject(void) = 0; + virtual void SetAnimFile(const char *file) {} + virtual void ConvertAnimFileIndex(void) {} + virtual int GetAnimFileIndex(void) { return -1; } + + // one day it becomes virtual + uint8 GetModelType() const { return m_type; } + bool IsBuilding(void) { return m_type == MITYPE_SIMPLE || m_type == MITYPE_TIME; } + bool IsSimple(void) { return m_type == MITYPE_SIMPLE || m_type == MITYPE_TIME || m_type == MITYPE_WEAPON; } + bool IsClump(void) { return m_type == MITYPE_CLUMP || m_type == MITYPE_PED || m_type == MITYPE_VEHICLE; } + char *GetModelName(void) { return m_name; } + void SetModelName(const char *name) { strncpy(m_name, name, MAX_MODEL_NAME); } + void SetColModel(CColModel *col, bool owns = false){ + m_colModel = col; m_bOwnsColModel = owns; } + CColModel *GetColModel(void) { return m_colModel; } + bool DoesOwnColModel(void) { return m_bOwnsColModel; } + void DeleteCollisionModel(void); + void ClearTexDictionary(void) { m_txdSlot = -1; } + int16 GetObjectID(void) { return m_objectId; } + void SetObjectID(int16 id) { m_objectId = id; } + int16 GetTxdSlot(void) { return m_txdSlot; } + void AddRef(void); + void RemoveRef(void); + void SetTexDictionary(const char *name); + void AddTexDictionaryRef(void); + void RemoveTexDictionaryRef(void); + void Init2dEffects(void); + void Add2dEffect(C2dEffect *fx); + C2dEffect *Get2dEffect(int n); + uint8 GetNum2dEffects() const { return m_num2dEffects; } + uint16 GetNumRefs() const { return m_refCount; } +}; diff --git a/src/miami/modelinfo/ClumpModelInfo.cpp b/src/miami/modelinfo/ClumpModelInfo.cpp new file mode 100644 index 00000000..d13f85ba --- /dev/null +++ b/src/miami/modelinfo/ClumpModelInfo.cpp @@ -0,0 +1,208 @@ +#include "common.h" + +#include "RwHelper.h" +#include "General.h" +#include "NodeName.h" +#include "VisibilityPlugins.h" +#include "ModelInfo.h" +#include "AnimManager.h" + +void +CClumpModelInfo::DeleteRwObject(void) +{ + if(m_clump){ + RpClumpDestroy(m_clump); + m_clump = nil; + RemoveTexDictionaryRef(); + if(GetAnimFileIndex() != -1) + CAnimManager::RemoveAnimBlockRef(GetAnimFileIndex()); + } +} + +static RpAtomic* +SetHierarchyForSkinAtomic(RpAtomic *atomic, void *data) +{ + RpSkinAtomicSetHAnimHierarchy(atomic, (RpHAnimHierarchy*)data); + return nil; +} + +RwObject* +CClumpModelInfo::CreateInstance(void) +{ + if(m_clump == nil) + return nil; + RpClump *clone = RpClumpClone(m_clump); + if(IsClumpSkinned(clone)){ + RpHAnimHierarchy *hier; + RpHAnimAnimation *anim; + + hier = GetAnimHierarchyFromClump(clone); + assert(hier); + RpClumpForAllAtomics(clone, SetHierarchyForSkinAtomic, hier); + anim = HAnimAnimationCreateForHierarchy(hier); + RpHAnimHierarchySetCurrentAnim(hier, anim); + RpHAnimHierarchySetFlags(hier, (RpHAnimHierarchyFlag)(rpHANIMHIERARCHYUPDATEMODELLINGMATRICES|rpHANIMHIERARCHYUPDATELTMS)); + } + return (RwObject*)clone; +} + +RwObject* +CClumpModelInfo::CreateInstance(RwMatrix *m) +{ + if(m_clump){ + RpClump *clump = (RpClump*)CreateInstance(); + *RwFrameGetMatrix(RpClumpGetFrame(clump)) = *m; + return (RwObject*)clump; + } + return nil; +} + +RpAtomic* +CClumpModelInfo::SetAtomicRendererCB(RpAtomic *atomic, void *data) +{ + CVisibilityPlugins::SetAtomicRenderCallback(atomic, (RpAtomicCallBackRender)data); + return atomic; +} + +void +CClumpModelInfo::SetClump(RpClump *clump) +{ + m_clump = clump; + CVisibilityPlugins::SetClumpModelInfo(m_clump, this); + AddTexDictionaryRef(); + if(GetAnimFileIndex() != -1) + CAnimManager::AddAnimBlockRef(GetAnimFileIndex()); + if(IsClumpSkinned(clump)){ + int i; + RpHAnimHierarchy *hier; + RpAtomic *skinAtomic; + RpSkin *skin; + + hier = GetAnimHierarchyFromClump(clump); + assert(hier); + RpClumpForAllAtomics(clump, SetHierarchyForSkinAtomic, hier); + #if !defined(RW_DC) + // why is this here? should be already normalized ~ skmp + skinAtomic = GetFirstAtomic(clump); + + assert(skinAtomic); + skin = RpSkinGeometryGetSkin(RpAtomicGetGeometry(skinAtomic)); + // ignore const + for(i = 0; i < RpGeometryGetNumVertices(RpAtomicGetGeometry(skinAtomic)); i++){ + RwMatrixWeights *weights = (RwMatrixWeights*)&RpSkinGetVertexBoneWeights(skin)[i]; + float sum = weights->w0 + weights->w1 + weights->w2 + weights->w3; + weights->w0 /= sum; + weights->w1 /= sum; + weights->w2 /= sum; + weights->w3 /= sum; + } + #endif + RpHAnimHierarchySetFlags(hier, (RpHAnimHierarchyFlag)(rpHANIMHIERARCHYUPDATEMODELLINGMATRICES|rpHANIMHIERARCHYUPDATELTMS)); + } +} + +void +CClumpModelInfo::SetAnimFile(const char *file) +{ + if(strcasecmp(file, "null") == 0) + return; + + m_animFileName = new char[strlen(file)+1]; + strcpy(m_animFileName, file); +} + +void +CClumpModelInfo::ConvertAnimFileIndex(void) +{ + if(m_animFileIndex != -1){ + // we have a string pointer in that union + int32 index = CAnimManager::GetAnimationBlockIndex(m_animFileName); + delete[] m_animFileName; + m_animFileIndex = index; + } +} + +void +CClumpModelInfo::SetFrameIds(RwObjectNameIdAssocation *assocs) +{ + int32 i; + RwObjectNameAssociation objname; + + for(i = 0; assocs[i].name; i++) + if((assocs[i].flags & CLUMP_FLAG_NO_HIERID) == 0){ + objname.frame = nil; + objname.name = assocs[i].name; + RwFrameForAllChildren(RpClumpGetFrame(m_clump), FindFrameFromNameWithoutIdCB, &objname); + if(objname.frame) + CVisibilityPlugins::SetFrameHierarchyId(objname.frame, assocs[i].hierId); + } +} + +RwFrame* +CClumpModelInfo::FindFrameFromIdCB(RwFrame *frame, void *data) +{ + RwObjectIdAssociation *assoc = (RwObjectIdAssociation*)data; + + if(CVisibilityPlugins::GetFrameHierarchyId(frame) == assoc->id){ + assoc->frame = frame; + return nil; + } + RwFrameForAllChildren(frame, FindFrameFromIdCB, assoc); + return assoc->frame ? nil : frame; +} + +// unused +RwFrame* +CClumpModelInfo::FindFrameFromNameCB(RwFrame *frame, void *data) +{ + RwObjectNameAssociation *assoc = (RwObjectNameAssociation*)data; + + if(!CGeneral::faststricmp(GetFrameNodeName(frame), assoc->name)){ + assoc->frame = frame; + return nil; + } + RwFrameForAllChildren(frame, FindFrameFromNameCB, assoc); + return assoc->frame ? nil : frame; +} + +RwFrame* +CClumpModelInfo::FindFrameFromNameWithoutIdCB(RwFrame *frame, void *data) +{ + RwObjectNameAssociation *assoc = (RwObjectNameAssociation*)data; + + if(CVisibilityPlugins::GetFrameHierarchyId(frame) == 0 && + !CGeneral::faststricmp(GetFrameNodeName(frame), assoc->name)){ + assoc->frame = frame; + return nil; + } + RwFrameForAllChildren(frame, FindFrameFromNameWithoutIdCB, assoc); + return assoc->frame ? nil : frame; +} + +RwFrame* +CClumpModelInfo::FillFrameArrayCB(RwFrame *frame, void *data) +{ + int32 id; + RwFrame **frames = (RwFrame**)data; + id = CVisibilityPlugins::GetFrameHierarchyId(frame); + if(id > 0) + frames[id] = frame; + RwFrameForAllChildren(frame, FillFrameArrayCB, data); + return frame; +} + +void +CClumpModelInfo::FillFrameArray(RpClump *clump, RwFrame **frames) +{ + RwFrameForAllChildren(RpClumpGetFrame(clump), FillFrameArrayCB, frames); +} + +RwFrame* +CClumpModelInfo::GetFrameFromId(RpClump *clump, int32 id) +{ + RwObjectIdAssociation assoc; + assoc.id = id; + assoc.frame = nil; + RwFrameForAllChildren(RpClumpGetFrame(clump), FindFrameFromIdCB, &assoc); + return assoc.frame; +} diff --git a/src/miami/modelinfo/ClumpModelInfo.h b/src/miami/modelinfo/ClumpModelInfo.h new file mode 100644 index 00000000..0113d340 --- /dev/null +++ b/src/miami/modelinfo/ClumpModelInfo.h @@ -0,0 +1,60 @@ +#pragma once + +#include "BaseModelInfo.h" + +struct RwObjectNameIdAssocation +{ + const char *name; + int32 hierId; + uint32 flags; +}; + +struct RwObjectNameAssociation +{ + const char *name; + RwFrame *frame; +}; + +struct RwObjectIdAssociation +{ + int32 id; + RwFrame *frame; +}; + +enum { + CLUMP_FLAG_NO_HIERID = 0x1, +}; + + +class CClumpModelInfo : public CBaseModelInfo +{ +public: + RpClump *m_clump; + union { + int32 m_animFileIndex; + char *m_animFileName; + }; + + CClumpModelInfo(void) : CBaseModelInfo(MITYPE_CLUMP) { m_animFileIndex = -1; } + CClumpModelInfo(ModelInfoType id) : CBaseModelInfo(id) { m_animFileIndex = -1; } + ~CClumpModelInfo() {} + void DeleteRwObject(void); + RwObject *CreateInstance(void); + RwObject *CreateInstance(RwMatrix *); + RwObject *GetRwObject(void) { return (RwObject*)m_clump; } + + virtual void SetClump(RpClump *); + virtual void SetAnimFile(const char *file); + virtual void ConvertAnimFileIndex(void); + virtual int GetAnimFileIndex(void) { return m_animFileIndex; } + + static RpAtomic *SetAtomicRendererCB(RpAtomic *atomic, void *data); + void SetFrameIds(RwObjectNameIdAssocation *assocs); + static RwFrame *FindFrameFromNameCB(RwFrame *frame, void *data); + static RwFrame *FindFrameFromNameWithoutIdCB(RwFrame *frame, void *data); + static RwFrame *FindFrameFromIdCB(RwFrame *frame, void *data); + static void FillFrameArray(RpClump *clump, RwFrame **frames); + static RwFrame *FillFrameArrayCB(RwFrame *frame, void *data); + static RwFrame *GetFrameFromId(RpClump *clump, int32 id); +}; +//static_assert(sizeof(CClumpModelInfo) == 0x34, "CClumpModelInfo: error"); diff --git a/src/miami/modelinfo/MloModelInfo.cpp b/src/miami/modelinfo/MloModelInfo.cpp new file mode 100644 index 00000000..fa12b900 --- /dev/null +++ b/src/miami/modelinfo/MloModelInfo.cpp @@ -0,0 +1,41 @@ +#include "common.h" + +#include "VisibilityPlugins.h" +#include "ModelInfo.h" + +/* +void +CMloModelInfo::ConstructClump() +{ + m_clump = RpClumpCreate(); + RwFrame *mainFrame = RwFrameCreate(); + RwFrameSetIdentity(mainFrame); + RpClumpSetFrame(m_clump, mainFrame); + + for (int i = firstInstance; i < lastInstance; i++) { + int modelId = CModelInfo::GetMloInstanceStore().store[i].m_modelIndex; + RwMatrix *attMat = CModelInfo::GetMloInstanceStore().store[i].GetMatrix().m_attachment; + CSimpleModelInfo *minfo = (CSimpleModelInfo*)CModelInfo::GetModelInfo(modelId); + + if (minfo->m_atomics[0] != nil) { + RpAtomic *newAtomic = RpAtomicClone(minfo->m_atomics[0]); + RwFrame *newFrame = RwFrameCreate(); + if (newAtomic != nil && newFrame != nil) { + *RwFrameGetMatrix(newFrame) = *attMat; + RpAtomicSetFrame(newAtomic, newFrame); + RwFrameAddChild(mainFrame, newFrame); + RpClumpAddAtomic(m_clump, newAtomic); + } else { + debug("Failed to allocate memory while creating template MLO.\n"); + } + } + } + + if (RpClumpGetNumAtomics(m_clump) != 0) { + CVisibilityPlugins::SetClumpModelInfo(m_clump, this); + } else { + RpClumpDestroy(m_clump); + m_clump = nil; + } +} +*/ \ No newline at end of file diff --git a/src/miami/modelinfo/MloModelInfo.h b/src/miami/modelinfo/MloModelInfo.h new file mode 100644 index 00000000..b1ae3298 --- /dev/null +++ b/src/miami/modelinfo/MloModelInfo.h @@ -0,0 +1,14 @@ +#pragma once + +#include "ClumpModelInfo.h" + +class CMloModelInfo : public CClumpModelInfo +{ +public: + float drawDist; + int firstInstance; + int lastInstance; +public: + CMloModelInfo(void) : CClumpModelInfo(MITYPE_MLO) {} + void ConstructClump(); +}; \ No newline at end of file diff --git a/src/miami/modelinfo/ModelIndices.cpp b/src/miami/modelinfo/ModelIndices.cpp new file mode 100644 index 00000000..98c7fb38 --- /dev/null +++ b/src/miami/modelinfo/ModelIndices.cpp @@ -0,0 +1,34 @@ +#include "common.h" + +#include "General.h" +#include "ModelIndices.h" + +#define X(name, var) int16 var = -1; + MODELINDICES +#undef X + +void +InitModelIndices(void) +{ +#define X(name, var) var = -1; + MODELINDICES +#undef X +} + +void +MatchModelString(const char *modelname, int16 id) +{ +#define X(name, var) \ + if(!CGeneral::faststrcmp(name, modelname)){ \ + var = id; \ + return; \ + } + MODELINDICES +#undef X +} + +void +TestModelIndices(void) +{ + ; +} diff --git a/src/miami/modelinfo/ModelIndices.h b/src/miami/modelinfo/ModelIndices.h new file mode 100644 index 00000000..836c4092 --- /dev/null +++ b/src/miami/modelinfo/ModelIndices.h @@ -0,0 +1,621 @@ +#pragma once + +#include "ModelInfo.h" + +#define MODELINDICES \ + X("fire_hydrant", MI_FIRE_HYDRANT) \ + X("phonesign", MI_PHONESIGN) \ + X("noparkingsign1", MI_NOPARKINGSIGN1) \ + X("bussign1", MI_BUSSIGN1) \ + X("roadworkbarrier1", MI_ROADWORKBARRIER1) \ + X("dump1", MI_DUMP1) \ + X("trafficcone", MI_TRAFFICCONE) \ + X("newsstand1", MI_NEWSSTAND) \ + X("postbox1", MI_POSTBOX1) \ + X("bin1", MI_BIN) \ + X("wastebin", MI_WASTEBIN) \ + X("phonebooth1", MI_PHONEBOOTH1) \ + X("parkingmeter", MI_PARKINGMETER) \ + X("parkingmeterg", MI_PARKINGMETER2) \ + X("mall_fans", MI_MALLFAN) \ + X("htl_fan_rotate_nt", MI_HOTELFAN_NIGHT) \ + X("htl_fan_rotate_dy", MI_HOTELFAN_DAY) \ + X("hotroomfan", MI_HOTROOMFAN) \ + X("trafficlight1", MI_TRAFFICLIGHTS) \ + X("MTraffic4", MI_TRAFFICLIGHTS_VERTICAL) \ + X("MTraffic1", MI_TRAFFICLIGHTS_MIAMI) \ + X("MTraffic2", MI_TRAFFICLIGHTS_TWOVERTICAL) \ + X("lamppost1", MI_SINGLESTREETLIGHTS1) \ + X("lamppost2", MI_SINGLESTREETLIGHTS2) \ + X("lamppost3", MI_SINGLESTREETLIGHTS3) \ + X("doublestreetlght1", MI_DOUBLESTREETLIGHTS) \ + X("Streetlamp1", MI_STREETLAMP1) \ + X("Streetlamp2", MI_STREETLAMP2) \ + X("veg_tree3", MI_TREE2) \ + X("veg_treea1", MI_TREE3) \ + X("veg_treeb1", MI_TREE6) \ + X("veg_treea3", MI_TREE8) \ + X("doc_crane_cab0", MODELID_CRANE_1) \ + X("doc_crane_cab01", MODELID_CRANE_2) \ + X("doc_crane_cab02", MODELID_CRANE_3) \ + X("doc_crane_cab03", MODELID_CRANE_4) \ + X("boatcranelg0", MODELID_CRANE_5) \ + X("LODnetopa0", MODELID_CRANE_6) \ + X("package1", MI_COLLECTABLE1) \ + X("Money", MI_MONEY) \ + X("barrel1", MI_CARMINE) \ + X("dk_paynspraydoor", MI_GARAGEDOOR2) \ + X("dk_waretankdoor1", MI_GARAGEDOOR3) \ + X("hav_garagedoor1", MI_GARAGEDOOR4) \ + X("hav_garagedoor02", MI_GARAGEDOOR5) \ + X("hav_garagedoor03", MI_GARAGEDOOR6) \ + X("hav_garagedoor04", MI_GARAGEDOOR7) \ + X("lh_showdoor03", MI_GARAGEDOOR9) \ + X("lh_showdoor1", MI_GARAGEDOOR10) \ + X("lhtankdoor", MI_GARAGEDOOR11) \ + X("nbtgardoor", MI_GARAGEDOOR12) \ + X("dk_camjonesdoor", MI_GARAGEDOOR13) \ + X("nbtgardoor02", MI_GARAGEDOOR14) \ + X("dt_savedra", MI_GARAGEDOOR15) \ + X("dt_savedrb", MI_GARAGEDOOR16) \ + X("dk_bombdoor", MI_GARAGEDOOR18) \ + X("haiwshpnsdoor", MI_GARAGEDOOR19) \ + X("wshpnsdoor", MI_GARAGEDOOR20) \ + X("nbecpnsdoor", MI_GARAGEDOOR21) \ + X("nbtgardoor03", MI_GARAGEDOOR22) \ + X("dt_savedrc", MI_GARAGEDOOR23) \ + X("dt_savedrd", MI_GARAGEDOOR24) \ + X("man_frntstepGD", MI_GARAGEDOOR25) \ + X("svegrgedoor", MI_GARAGEDOOR26) \ + X("barrel2", MI_NAUTICALMINE) \ + X("briefcase", MI_BRIEFCASE) \ + X("wglasssmash", MI_GLASS1) \ + X("glassfx_composh", MI_GLASS8) \ + X("barrel4", MI_EXPLODINGBARREL) \ + X("adrenaline", MI_PICKUP_ADRENALINE) \ + X("bodyarmour", MI_PICKUP_BODYARMOUR) \ + X("info", MI_PICKUP_INFO) \ + X("health", MI_PICKUP_HEALTH) \ + X("bonus", MI_PICKUP_BONUS) \ + X("bribe", MI_PICKUP_BRIBE) \ + X("killfrenzy", MI_PICKUP_KILLFRENZY) \ + X("camerapickup", MI_PICKUP_CAMERA) \ + X("bigdollar", MI_PICKUP_REVENUE) \ + X("pickupsave", MI_PICKUP_SAVEGAME) \ + X("property_locked", MI_PICKUP_PROPERTY) \ + X("property_fsale", MI_PICKUP_PROPERTY_FORSALE) \ + X("clothesp", MI_PICKUP_CLOTHES) \ + X("bollardlight", MI_BOLLARDLIGHT) \ + X("bar_barrier10", MI_FENCE) \ + X("bar_barrier12", MI_FENCE2) \ + X("petrolpump", MI_PETROLPUMP) \ + X("washgaspump", MI_PETROLPUMP2) \ + X("bouy", MI_BUOY) \ + X("parktable1", MI_PARKTABLE) \ + X("lamppost1", MI_LAMPPOST1) \ + X("veg_palm04", MI_VEG_PALM01) \ + X("veg_palwee02", MI_VEG_PALM02) \ + X("veg_palmkbb11", MI_VEG_PALM03) \ + X("veg_palmkb4", MI_VEG_PALM04) \ + X("veg_palm02", MI_VEG_PALM05) \ + X("veg_palmkb3", MI_VEG_PALM06) \ + X("veg_palmbig14", MI_VEG_PALM07) \ + X("veg_palm01", MI_VEG_PALM08) \ + X("mlamppost", MI_MLAMPPOST) \ + X("roadworkbarrier1", MI_BARRIER1) \ + X("littleha_police", MI_LITTLEHA_POLICE) \ + X("telgrphpole02", MI_TELPOLE02) \ + X("trafficlight1", MI_TRAFFICLIGHT01) \ + X("parkbench1", MI_PARKBENCH) \ + X("plc_stinger", MI_PLC_STINGER) \ + X("od_lightbeam", MI_LIGHTBEAM) \ + X("ap_radar1_01", MI_AIRPORTRADAR) \ + X("rcbomb", MI_RCBOMB) \ + X("beachball", MI_BEACHBALL) \ + X("sandcastle1", MI_SANDCASTLE1) \ + X("sandcastle2", MI_SANDCASTLE2) \ + X("jellyfish", MI_JELLYFISH) \ + X("jellyfish01", MI_JELLYFISH01) \ + X("fish1single", MI_FISH1SINGLE) \ + X("fish1s", MI_FISH1S) \ + X("fish2single", MI_FISH2SINGLE) \ + X("fish2s", MI_FISH2S) \ + X("fish3single", MI_FISH3SINGLE) \ + X("fish3s", MI_FISH3S) \ + X("turtle", MI_TURTLE) \ + X("dolphin", MI_DOLPHIN) \ + X("shark", MI_SHARK) \ + X("submarine", MI_SUBMARINE) \ + X("Esc_step", MI_ESCALATORSTEP) \ + X("lounge_wood_up", MI_LOUNGE_WOOD_UP) \ + X("lounge_towel_up", MI_LOUNGE_TOWEL_UP) \ + X("lounge_wood_dn", MI_LOUNGE_WOOD_DN) \ + X("lotion", MI_LOTION) \ + X("beachtowel01", MI_BEACHTOWEL01) \ + X("beachtowel02", MI_BEACHTOWEL02) \ + X("beachtowel03", MI_BEACHTOWEL03) \ + X("beachtowel04", MI_BEACHTOWEL04) \ + X("blimp_night", MI_BLIMP_NIGHT) \ + X("blimp_day", MI_BLIMP_DAY) \ + X("yt_main_body", MI_YT_MAIN_BODY) \ + X("yt_main_body2", MI_YT_MAIN_BODY2) + +#define X(name, var) extern int16 var; + MODELINDICES +#undef X + +// and some hardcoded ones +// expand as needed +enum +{ + MI_PLAYER = 0, + MI_COP, + MI_SWAT, + MI_FBI, + MI_ARMY, + MI_MEDIC, + MI_FIREMAN, + MI_MALE01, + + MI_HFYST = 9, + MI_HFOST, + MI_HMYST, + MI_HMOST, + MI_HFYRI, + MI_HFORI, + MI_HMYRI, + MI_HMORI, + MI_HFYBE, + MI_HFOBE, + MI_HMYBE, + MI_HMOBE, + MI_HFYBU, + MI_HFYMD, + MI_HFYCG, + MI_HFYPR, + MI_HFOTR, + MI_HMOTR, + MI_HMYAP, + MI_HMOCA, + MI_TAXI_D = MI_HMOCA, + MI_BMODK, + MI_BMYKR, + MI_BFYST, + MI_BFOST, + MI_BMYST, + MI_BMOST, + MI_BFYRI, + MI_BFORI, + MI_BMYRI, + MI_BFYBE, + MI_BMYBE, + MI_BFOBE, + MI_BMOBE, + MI_BMYBU, + MI_BFYPR, + MI_BFOTR, + MI_BMOTR, + MI_BMYPI, + MI_BMYBB, + MI_WMYCR, + MI_WFYST, + MI_WFOST, + MI_WMYST, + MI_WMOST, + MI_WFYRI, + MI_WFORI, + MI_WMYRI, + MI_WMORI, + MI_WFYBE, + MI_WMYBE, + MI_WFOBE, + MI_WMOBE, + MI_WMYCW, + MI_WMYGO, + MI_WFOGO, + MI_WMOGO, + MI_WFYLG, + MI_WMYLG, + MI_WFYBU, + MI_WMYBU, + MI_WMOBU, + MI_WFYPR, + MI_WFOTR, + MI_WMOTR, + MI_WMYPI, + MI_WMOCA, + MI_WFYJG, + MI_WMYJG, + MI_WFYSK, + MI_WMYSK, + MI_WFYSH, + MI_WFOSH, + MI_JFOTO, + MI_JMOTO, + + MI_CBA,// = 83, + MI_CBB, + MI_HNA, + MI_HNB, + MI_SGA, + MI_SGB, + MI_CLA, + MI_CLB, + MI_GDA, + MI_GDB, + MI_BKA, + MI_BKB, + MI_PGA, + MI_PGB, + MI_VICE1, + MI_VICE2, + MI_VICE3, + MI_VICE4, + MI_VICE5, + MI_VICE6, + MI_VICE7, + MI_VICE8, + MI_WFYG1, + MI_WFYG2,// = 106, // last regular ped + // three more peds possible + MI_SPECIAL01 = 109, + MI_SPECIAL02, + MI_SPECIAL03, + MI_SPECIAL04, + MI_SPECIAL05, + MI_SPECIAL06, + MI_SPECIAL07, + MI_SPECIAL08, + MI_SPECIAL09, + MI_SPECIAL10, + MI_SPECIAL11, + MI_SPECIAL12, + MI_SPECIAL13, + MI_SPECIAL14, + MI_SPECIAL15, + MI_SPECIAL16, + MI_SPECIAL17, + MI_SPECIAL18, + MI_SPECIAL19, + MI_SPECIAL20, + MI_SPECIAL21,// = 129, + + MI_LAST_PED = MI_SPECIAL21, + MI_FIRST_VEHICLE, + + MI_LANDSTAL = MI_FIRST_VEHICLE, + MI_IDAHO, + MI_STINGER, + MI_LINERUN, + MI_PEREN, + MI_SENTINEL, + MI_RIO, + MI_FIRETRUCK, + MI_TRASH, + MI_STRETCH, + MI_MANANA, + MI_INFERNUS, + MI_VOODOO, + MI_PONY, + MI_MULE, + MI_CHEETAH, + MI_AMBULAN, + MI_FBICAR, + MI_MOONBEAM, + MI_ESPERANT, + MI_TAXI, + MI_WASHING, + MI_BOBCAT, + MI_MRWHOOP, + MI_BFINJECT, + MI_HUNTER, + MI_POLICE, + MI_ENFORCER, + MI_SECURICA, + MI_BANSHEE, + MI_PREDATOR, + MI_BUS, + MI_RHINO, + MI_BARRACKS, + MI_CUBAN, + MI_CHOPPER, + MI_ANGEL, + MI_COACH, + MI_CABBIE, + MI_STALLION, + MI_RUMPO, + MI_RCBANDIT, + MI_ROMERO, + MI_PACKER, + MI_SENTXS, + MI_ADMIRAL, + MI_SQUALO, + MI_SEASPAR, + MI_PIZZABOY, + MI_GANGBUR, + MI_AIRTRAIN, + MI_DEADDODO, + MI_SPEEDER, + MI_REEFER, + MI_TROPIC, + MI_FLATBED, + MI_YANKEE, + MI_CADDY, + MI_ZEBRA, + MI_TOPFUN, + MI_SKIMMER, + MI_PCJ600, + MI_FAGGIO, + MI_FREEWAY, + MI_RCBARON, + MI_RCRAIDER, + MI_GLENDALE, + MI_OCEANIC, + MI_SANCHEZ, + MI_SPARROW, + MI_PATRIOT, + MI_LOVEFIST, + MI_COASTG, + MI_DINGHY, + MI_HERMES, + MI_SABRE, + MI_SABRETUR, + MI_PHEONIX, + MI_WALTON, + MI_REGINA, + MI_COMET, + MI_DELUXO, + MI_BURRITO, + MI_SPAND, + MI_MARQUIS, + MI_BAGGAGE, + MI_KAUFMAN, + MI_MAVERICK, + MI_VCNMAV, + MI_RANCHER, + MI_FBIRANCH, + MI_VIRGO, + MI_GREENWOO, + MI_JETMAX, + MI_HOTRING, + MI_SANDKING, + MI_BLISTAC, + MI_POLMAV, + MI_BOXVILLE, + MI_BENSON, + MI_MESA, + MI_RCGOBLIN, + MI_HOTRINA, + MI_HOTRINB, + MI_BLOODRA, + MI_BLOODRB, + MI_VICECHEE, + + // HACK + MI_TRAIN = -1, + MI_DODO = -2, + + MI_LAST_VEHICLE = MI_VICECHEE, + + MI_WHEEL_RIM, + MI_WHEEL_OFFROAD, + MI_WHEEL_TRUCK, + + MI_CAR_DOOR,// = 240, + MI_CAR_BUMPER, + MI_CAR_PANEL, + MI_CAR_BONNET, + MI_CAR_BOOT, + MI_CAR_WHEEL, + MI_BODYPARTA, + MI_BODYPARTB, + + MI_WHEEL_SPORT = 250, + MI_WHEEL_SALOON, + MI_WHEEL_LIGHTVAN, + MI_WHEEL_CLASSIC, + MI_WHEEL_ALLOY, + MI_WHEEL_LIGHTTRUCK, + MI_WHEEL_SMALLCAR, + + MI_AIRTRAIN_VLO, // = 257, + MI_MOBILE, + + MI_BRASS_KNUCKLES, // 259 + MI_SCREWDRIVER, + MI_GOLFCLUB, + MI_NIGHTSTICK, + MI_KNIFE, + MI_BASEBALL_BAT, + MI_HAMMER, + MI_MEAT_CLEAVER, + MI_MACHETE, + MI_KATANA, + MI_CHAINSAW, + MI_GRENADE, + MI_TEARGAS, + MI_MOLOTOV, + MI_MISSILE, + MI_COLT45, + MI_PYTHON, + MI_RUGER, + MI_SHOTGUN, + MI_SPAS12_SHOTGUN, + MI_STUBBY_SHOTGUN, + MI_M4, + MI_TEC9, + MI_UZI, + MI_SILENCEDINGRAM, + MI_MP5, + MI_SNIPERRIFLE, + MI_LASERSCOPE, + MI_ROCKETLAUNCHER, + MI_FLAMETHROWER, + MI_M60, + MI_MINIGUN, + MI_BOMB, + MI_CAMERA, + MI_FINGERS, + MI_MINIGUN2, + + MI_CUTOBJ01,// = 295, + MI_CUTOBJ02, + MI_CUTOBJ03, + MI_CUTOBJ04, + MI_CUTOBJ05, + + + NUM_DEFAULT_MODELS,// = 300 +}; + +enum{ + NUM_OF_SPECIAL_CHARS = 21, + NUM_OF_CUTSCENE_OBJECTS = 5 +}; + +void InitModelIndices(void); +void MatchModelString(const char *name, int16 id); +void TestModelIndices(void); + +inline bool +IsGlass(int16 id) +{ + CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(id); + return mi->IsBuilding() && (mi->m_isCodeGlass || mi->m_isArtistGlass); +} + +inline bool +IsTrafficLight(int16 id) +{ + return id == MI_TRAFFICLIGHTS || + id == MI_TRAFFICLIGHTS_VERTICAL || + id == MI_TRAFFICLIGHTS_MIAMI || + id == MI_TRAFFICLIGHTS_TWOVERTICAL; +} + +inline bool +IsLightWithoutShift(int16 id) +{ + return id == MI_TRAFFICLIGHTS || + id == MI_SINGLESTREETLIGHTS1 || + id == MI_SINGLESTREETLIGHTS2 || + id == MI_SINGLESTREETLIGHTS3 || + id == MI_DOUBLESTREETLIGHTS; +} + +inline bool +IsLightWithPreRenderEffects(int16 id) +{ + return IsTrafficLight(id) || + id == MI_SINGLESTREETLIGHTS1 || + id == MI_SINGLESTREETLIGHTS2 || + id == MI_SINGLESTREETLIGHTS3 || + id == MI_DOUBLESTREETLIGHTS; +} + +inline bool +IsLightThatNeedsRepositioning(int16 id) +{ + return id == MI_SINGLESTREETLIGHTS1 || + id == MI_SINGLESTREETLIGHTS2 || + id == MI_SINGLESTREETLIGHTS3 || + id == MI_TRAFFICLIGHTS_MIAMI || + id == MI_TRAFFICLIGHTS_TWOVERTICAL || + id == MI_MLAMPPOST || + id == MI_STREETLAMP1 || + id == MI_STREETLAMP2; +} + +inline bool +IsLightObject(int16 id) +{ + return id == MI_TRAFFICLIGHTS_MIAMI || + id == MI_MLAMPPOST || + id == MI_SINGLESTREETLIGHTS1 || + id == MI_SINGLESTREETLIGHTS2 || + id == MI_SINGLESTREETLIGHTS3 || + id == MI_DOUBLESTREETLIGHTS || + id == MI_TRAFFICLIGHTS_TWOVERTICAL; +} + +inline bool +IsLampPost(int16 id) +{ + return id == MI_SINGLESTREETLIGHTS1 || + id == MI_SINGLESTREETLIGHTS2 || + id == MI_SINGLESTREETLIGHTS3 || + id == MI_BOLLARDLIGHT || + id == MI_MLAMPPOST || + id == MI_STREETLAMP1 || + id == MI_STREETLAMP2 || + id == MI_TELPOLE02 || + id == MI_TRAFFICLIGHTS_MIAMI || + id == MI_TRAFFICLIGHTS_TWOVERTICAL; +} + +inline bool +IsBodyPart(int16 id) +{ + return id == MI_BODYPARTA || id == MI_BODYPARTB; +} + +inline bool +IsPedModel(int16 id) +{ + return id >= MI_PLAYER && id <= MI_LAST_PED; +} +inline bool +IsPalmTreeModel(int16 id) +{ + return id == MI_VEG_PALM01 || + id == MI_VEG_PALM02 || + id == MI_VEG_PALM03 || + id == MI_VEG_PALM04 || + id == MI_VEG_PALM05 || + id == MI_VEG_PALM06 || + id == MI_VEG_PALM07 || + id == MI_VEG_PALM08; +} + +inline bool +IsTreeModel(int16 id) +{ + return id == MI_TREE2 || + id == MI_TREE3 || + id == MI_TREE6 || + id == MI_TREE8 || + IsPalmTreeModel(id); +} + +inline bool +IsPolicePedModel(int16 id) +{ + return id == MI_COP || + id == MI_SWAT || + id == MI_FBI || + id == MI_ARMY; +} + +inline bool +IsPoliceVehicleModel(int16 id) +{ + return id == MI_CHOPPER || + id == MI_PREDATOR || + id == MI_POLICE || + id == MI_ENFORCER; +} + +inline bool +IsExplosiveThingModel(int16 id) +{ + return id == MI_EXPLODINGBARREL || + id == MI_PETROLPUMP || + id == MI_PETROLPUMP2; +} + +inline bool +IsFence(int16 id) +{ + return id == MI_FENCE || + id == MI_FENCE2; +} diff --git a/src/miami/modelinfo/ModelInfo.cpp b/src/miami/modelinfo/ModelInfo.cpp new file mode 100644 index 00000000..14976cdb --- /dev/null +++ b/src/miami/modelinfo/ModelInfo.cpp @@ -0,0 +1,258 @@ +#include "common.h" + +#include "General.h" +#include "TempColModels.h" +#include "ModelIndices.h" +#include "ModelInfo.h" + +CBaseModelInfo *CModelInfo::ms_modelInfoPtrs[MODELINFOSIZE]; + +CStore CModelInfo::ms_simpleModelStore; +CStore CModelInfo::ms_timeModelStore; +CStore CModelInfo::ms_weaponModelStore; +CStore CModelInfo::ms_clumpModelStore; +CStore CModelInfo::ms_pedModelStore; +CStore CModelInfo::ms_vehicleModelStore; +CStore CModelInfo::ms_2dEffectStore; + +void +CModelInfo::Initialise(void) +{ + int i; + CSimpleModelInfo *m; + + debug("sizeof SimpleModelStore %d\n", sizeof(ms_simpleModelStore)); + debug("sizeof TimeModelStore %d\n", sizeof(ms_timeModelStore)); + debug("sizeof WeaponModelStore %d\n", sizeof(ms_weaponModelStore)); + debug("sizeof ClumpModelStore %d\n", sizeof(ms_clumpModelStore)); + debug("sizeof VehicleModelStore %d\n", sizeof(ms_vehicleModelStore)); + debug("sizeof PedModelStore %d\n", sizeof(ms_pedModelStore)); + debug("sizeof 2deffectsModelStore %d\n", sizeof(ms_2dEffectStore)); + + for(i = 0; i < MODELINFOSIZE; i++) + ms_modelInfoPtrs[i] = nil; + ms_2dEffectStore.Clear(); + ms_simpleModelStore.Clear(); + ms_timeModelStore.Clear(); + ms_weaponModelStore.Clear(); + ms_clumpModelStore.Clear(); + ms_pedModelStore.Clear(); + ms_vehicleModelStore.Clear(); + + m = AddSimpleModel(MI_CAR_DOOR); + m->SetColModel(&CTempColModels::ms_colModelDoor1); + m->SetTexDictionary("generic"); + m->SetNumAtomics(1); + m->m_lodDistances[0] = 80.0f; + + m = AddSimpleModel(MI_CAR_BUMPER); + m->SetColModel(&CTempColModels::ms_colModelBumper1); + m->SetTexDictionary("generic"); + m->SetNumAtomics(1); + m->m_lodDistances[0] = 80.0f; + + m = AddSimpleModel(MI_CAR_PANEL); + m->SetColModel(&CTempColModels::ms_colModelPanel1); + m->SetTexDictionary("generic"); + m->SetNumAtomics(1); + m->m_lodDistances[0] = 80.0f; + + m = AddSimpleModel(MI_CAR_BONNET); + m->SetColModel(&CTempColModels::ms_colModelBonnet1); + m->SetTexDictionary("generic"); + m->SetNumAtomics(1); + m->m_lodDistances[0] = 80.0f; + + m = AddSimpleModel(MI_CAR_BOOT); + m->SetColModel(&CTempColModels::ms_colModelBoot1); + m->SetTexDictionary("generic"); + m->SetNumAtomics(1); + m->m_lodDistances[0] = 80.0f; + + m = AddSimpleModel(MI_CAR_WHEEL); + m->SetColModel(&CTempColModels::ms_colModelWheel1); + m->SetTexDictionary("generic"); + m->SetNumAtomics(1); + m->m_lodDistances[0] = 80.0f; + + m = AddSimpleModel(MI_BODYPARTA); + m->SetColModel(&CTempColModels::ms_colModelBodyPart1); + m->SetTexDictionary("generic"); + m->SetNumAtomics(1); + m->m_lodDistances[0] = 80.0f; + + m = AddSimpleModel(MI_BODYPARTB); + m->SetColModel(&CTempColModels::ms_colModelBodyPart2); + m->SetTexDictionary("generic"); + m->SetNumAtomics(1); + m->m_lodDistances[0] = 80.0f; +} + +void +CModelInfo::ShutDown(void) +{ + int i; + for(i = 0; i < ms_simpleModelStore.allocPtr; i++) + ms_simpleModelStore.store[i].Shutdown(); + for(i = 0; i < ms_timeModelStore.allocPtr; i++) + ms_timeModelStore.store[i].Shutdown(); + for(i = 0; i < ms_weaponModelStore.allocPtr; i++) + ms_weaponModelStore.store[i].Shutdown(); + for(i = 0; i < ms_clumpModelStore.allocPtr; i++) + ms_clumpModelStore.store[i].Shutdown(); + for(i = 0; i < ms_vehicleModelStore.allocPtr; i++) + ms_vehicleModelStore.store[i].Shutdown(); + for(i = 0; i < ms_pedModelStore.allocPtr; i++) + ms_pedModelStore.store[i].Shutdown(); + for(i = 0; i < ms_2dEffectStore.allocPtr; i++) + ms_2dEffectStore.store[i].Shutdown(); + + ms_2dEffectStore.Clear(); + ms_simpleModelStore.Clear(); + ms_timeModelStore.Clear(); + ms_weaponModelStore.Clear(); + ms_pedModelStore.Clear(); + ms_clumpModelStore.Clear(); + ms_vehicleModelStore.Clear(); +} + +CSimpleModelInfo* +CModelInfo::AddSimpleModel(int id) +{ + CSimpleModelInfo *modelinfo; + modelinfo = CModelInfo::ms_simpleModelStore.Alloc(); + CModelInfo::ms_modelInfoPtrs[id] = modelinfo; + modelinfo->Init(); + return modelinfo; +} + +CTimeModelInfo* +CModelInfo::AddTimeModel(int id) +{ + CTimeModelInfo *modelinfo; + modelinfo = CModelInfo::ms_timeModelStore.Alloc(); + CModelInfo::ms_modelInfoPtrs[id] = modelinfo; + modelinfo->Init(); + return modelinfo; +} + +CWeaponModelInfo* +CModelInfo::AddWeaponModel(int id) +{ + CWeaponModelInfo *modelinfo; + modelinfo = CModelInfo::ms_weaponModelStore.Alloc(); + CModelInfo::ms_modelInfoPtrs[id] = modelinfo; + modelinfo->Init(); + return modelinfo; +} + +CClumpModelInfo* +CModelInfo::AddClumpModel(int id) +{ + CClumpModelInfo *modelinfo; + modelinfo = CModelInfo::ms_clumpModelStore.Alloc(); + CModelInfo::ms_modelInfoPtrs[id] = modelinfo; + modelinfo->m_clump = nil; + return modelinfo; +} + +CPedModelInfo* +CModelInfo::AddPedModel(int id) +{ + CPedModelInfo *modelinfo; + modelinfo = CModelInfo::ms_pedModelStore.Alloc(); + CModelInfo::ms_modelInfoPtrs[id] = modelinfo; + modelinfo->m_clump = nil; + return modelinfo; +} + +CVehicleModelInfo* +CModelInfo::AddVehicleModel(int id) +{ + CVehicleModelInfo *modelinfo; + modelinfo = CModelInfo::ms_vehicleModelStore.Alloc(); + CModelInfo::ms_modelInfoPtrs[id] = modelinfo; + modelinfo->m_clump = nil; + modelinfo->m_vehicleType = -1; + modelinfo->m_wheelId = -1; + modelinfo->m_materials1[0] = nil; + modelinfo->m_materials2[0] = nil; + modelinfo->m_bikeSteerAngle = 999.99f; + return modelinfo; +} + +CBaseModelInfo* +CModelInfo::GetModelInfo(const char *name, int32 *id) +{ + CBaseModelInfo *modelinfo; + for(int i = 0; i < MODELINFOSIZE; i++){ + modelinfo = CModelInfo::ms_modelInfoPtrs[i]; + if(modelinfo && !CGeneral::faststricmp(modelinfo->GetModelName(), name)){ + if(id) + *id = i; + return modelinfo; + } + } + return nil; +} + +CBaseModelInfo* +CModelInfo::GetModelInfo(const char *name, int minIndex, int maxIndex) +{ + if (minIndex > maxIndex) + return 0; + + CBaseModelInfo *modelinfo; + for(int i = minIndex; i <= maxIndex; i++){ + modelinfo = CModelInfo::ms_modelInfoPtrs[i]; + if(modelinfo && !CGeneral::faststricmp(modelinfo->GetModelName(), name)) + return modelinfo; + } + return nil; +} + +bool +CModelInfo::IsBoatModel(int32 id) +{ + return GetModelInfo(id)->GetModelType() == MITYPE_VEHICLE && + ((CVehicleModelInfo*)GetModelInfo(id))->m_vehicleType == VEHICLE_TYPE_BOAT; +} + +bool +CModelInfo::IsBikeModel(int32 id) +{ + return GetModelInfo(id)->GetModelType() == MITYPE_VEHICLE && + ((CVehicleModelInfo*)GetModelInfo(id))->m_vehicleType == VEHICLE_TYPE_BIKE; +} + +bool +CModelInfo::IsCarModel(int32 id) +{ + return GetModelInfo(id)->GetModelType() == MITYPE_VEHICLE && + ((CVehicleModelInfo*)GetModelInfo(id))->m_vehicleType == VEHICLE_TYPE_CAR; +} + +bool +CModelInfo::IsHeliModel(int32 id) +{ + return GetModelInfo(id)->GetModelType() == MITYPE_VEHICLE && + ((CVehicleModelInfo*)GetModelInfo(id))->m_vehicleType == VEHICLE_TYPE_HELI; +} + +bool +CModelInfo::IsPlaneModel(int32 id) +{ + return GetModelInfo(id)->GetModelType() == MITYPE_VEHICLE && + ((CVehicleModelInfo*)GetModelInfo(id))->m_vehicleType == VEHICLE_TYPE_PLANE; +} + +void +CModelInfo::ReInit2dEffects() +{ + ms_2dEffectStore.Clear(); + + for (int i = 0; i < MODELINFOSIZE; i++) { + if (ms_modelInfoPtrs[i]) + ms_modelInfoPtrs[i]->Init2dEffects(); + } +} diff --git a/src/miami/modelinfo/ModelInfo.h b/src/miami/modelinfo/ModelInfo.h new file mode 100644 index 00000000..6a6cd7ab --- /dev/null +++ b/src/miami/modelinfo/ModelInfo.h @@ -0,0 +1,52 @@ +#pragma once + +#include "2dEffect.h" +#include "SimpleModelInfo.h" +#include "MloModelInfo.h" +#include "TimeModelInfo.h" +#include "WeaponModelInfo.h" +#include "ClumpModelInfo.h" +#include "PedModelInfo.h" +#include "VehicleModelInfo.h" +#include "templates.h" + +class CModelInfo +{ + static CBaseModelInfo *ms_modelInfoPtrs[MODELINFOSIZE]; + static CStore ms_simpleModelStore; + static CStore ms_timeModelStore; + static CStore ms_weaponModelStore; + static CStore ms_clumpModelStore; + static CStore ms_pedModelStore; + static CStore ms_vehicleModelStore; + static CStore ms_2dEffectStore; + +public: + static void Initialise(void); + static void ShutDown(void); + + static CSimpleModelInfo *AddSimpleModel(int id); + static CTimeModelInfo *AddTimeModel(int id); + static CWeaponModelInfo *AddWeaponModel(int id); + static CClumpModelInfo *AddClumpModel(int id); + static CPedModelInfo *AddPedModel(int id); + static CVehicleModelInfo *AddVehicleModel(int id); + + static CStore &Get2dEffectStore(void) { return ms_2dEffectStore; } + + static CBaseModelInfo *GetModelInfo(const char *name, int32 *id); + static CBaseModelInfo *GetModelInfo(int32 id){ + return ms_modelInfoPtrs[id]; + } + static CBaseModelInfo *GetModelInfo(const char *name, int minIndex, int maxIndex); + static CColModel *GetColModel(int id){ + return ms_modelInfoPtrs[id]->GetColModel(); + } + + static bool IsBoatModel(int32 id); + static bool IsBikeModel(int32 id); + static bool IsCarModel(int32 id); + static bool IsHeliModel(int32 id); + static bool IsPlaneModel(int32 id); + static void ReInit2dEffects(); +}; diff --git a/src/miami/modelinfo/PedModelInfo.cpp b/src/miami/modelinfo/PedModelInfo.cpp new file mode 100644 index 00000000..25b260d3 --- /dev/null +++ b/src/miami/modelinfo/PedModelInfo.cpp @@ -0,0 +1,168 @@ +#include "common.h" + +#include "RwHelper.h" +#include "General.h" +#include "Bones.h" +#include "SurfaceTable.h" +#include "Ped.h" +#include "NodeName.h" +#include "VisibilityPlugins.h" +#include "ModelInfo.h" +#include "custompipes.h" + +void +CPedModelInfo::DeleteRwObject(void) +{ + CClumpModelInfo::DeleteRwObject(); + if(m_hitColModel) + delete m_hitColModel; + m_hitColModel = nil; +} + +// leftover... +RwObjectNameIdAssocation CPedModelInfo::m_pPedIds[PED_NODE_MAX] = { + { "Smid", PED_MID, 0, }, // that is strange... + { "Shead", PED_HEAD, 0, }, + { "Supperarml", PED_UPPERARML, 0, }, + { "Supperarmr", PED_UPPERARMR, 0, }, + { "SLhand", PED_HANDL, 0, }, + { "SRhand", PED_HANDR, 0, }, + { "Supperlegl", PED_UPPERLEGL, 0, }, + { "Supperlegr", PED_UPPERLEGR, 0, }, + { "Sfootl", PED_FOOTL, 0, }, + { "Sfootr", PED_FOOTR, 0, }, + { "Slowerlegr", PED_LOWERLEGR, 0, }, + { nil, 0, 0, }, +}; + +void +CPedModelInfo::SetClump(RpClump *clump) +{ +#ifdef EXTENDED_PIPELINES + CustomPipes::AttachRimPipe(clump); +#endif + CClumpModelInfo::SetClump(clump); + SetFrameIds(m_pPedIds); // not needed in VC actually + if(m_hitColModel == nil) + CreateHitColModelSkinned(clump); + RpClumpForAllAtomics(m_clump, SetAtomicRendererCB, (void*)CVisibilityPlugins::RenderPedCB); + if(strcmp(GetModelName(), "player") == 0) + RpClumpForAllAtomics(m_clump, SetAtomicRendererCB, (void*)CVisibilityPlugins::RenderPlayerCB); +} + +struct ColNodeInfo +{ + Const char *name; + int pedNode; + int pieceType; + float x, z; + float radius; +}; + +#define NUMPEDINFONODES 10 +ColNodeInfo m_pColNodeInfos[NUMPEDINFONODES] = { + { nil, PED_HEAD, PEDPIECE_HEAD, 0.0f, 0.05f, 0.15f }, + { nil, PED_MID, PEDPIECE_TORSO, 0.0f, 0.15f, 0.2f }, + { nil, PED_MID, PEDPIECE_TORSO, 0.0f, -0.05f, 0.25f }, + { nil, PED_MID, PEDPIECE_MID, 0.0f, -0.25f, 0.25f }, + { nil, PED_UPPERARML, PEDPIECE_LEFTARM, 0.03f, -0.05f, 0.16f }, + { nil, PED_UPPERARMR, PEDPIECE_RIGHTARM, -0.03f, -0.05f, 0.16f }, + { nil, PED_LOWERLEGL, PEDPIECE_LEFTLEG, 0.0f, 0.15f, 0.2f }, + { nil, PED_LOWERLEGR, PEDPIECE_RIGHTLEG, 0.0f, 0.15f, 0.2f }, + { nil, PED_FOOTL, PEDPIECE_LEFTLEG, 0.0f, 0.15f, 0.15f }, + { nil, PED_FOOTR, PEDPIECE_RIGHTLEG, 0.0f, 0.15f, 0.15f }, +}; + +void +CPedModelInfo::CreateHitColModelSkinned(RpClump *clump) +{ + RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(clump); + CColModel *colmodel = new CColModel; + CColSphere *spheres = (CColSphere*)RwMalloc(NUMPEDINFONODES*sizeof(CColSphere)); + RwFrame *root = RpClumpGetFrame(m_clump); + RwMatrix *invmat = RwMatrixCreate(); + RwMatrix *mat = RwMatrixCreate(); + RwMatrixInvert(invmat, RwFrameGetMatrix(RpClumpGetFrame(clump))); + + for(int i = 0; i < NUMPEDINFONODES; i++){ + *mat = *invmat; + + // From LCS. Otherwise gives FPE +#ifdef FIX_BUGS + spheres[i].center = CVector(0.0f, 0.0f, 0.0f); +#else + int id = ConvertPedNode2BoneTag(m_pColNodeInfos[i].pedNode); // this is wrong, wtf R* ??? + int idx = RpHAnimIDGetIndex(hier, id); + + // This doesn't really work as the positions are not initialized yet + RwMatrixTransform(mat, &RpHAnimHierarchyGetMatrixArray(hier)[idx], rwCOMBINEPRECONCAT); + RwV3d pos = { 0.0f, 0.0f, 0.0f }; + RwV3dTransformPoints(&pos, &pos, 1, mat); + + spheres[i].center = pos + CVector(m_pColNodeInfos[i].x, 0.0f, m_pColNodeInfos[i].z); +#endif + spheres[i].radius = m_pColNodeInfos[i].radius; + spheres[i].surface = SURFACE_PED; + spheres[i].piece = m_pColNodeInfos[i].pieceType; + } + RwMatrixDestroy(invmat); + RwMatrixDestroy(mat); + colmodel->spheres = spheres; + colmodel->numSpheres = NUMPEDINFONODES; + colmodel->boundingSphere.Set(2.0f, CVector(0.0f, 0.0f, 0.0f)); + colmodel->boundingBox.Set(CVector(-0.5f, -0.5f, -1.2f), CVector(0.5f, 0.5f, 1.2f)); + colmodel->level = LEVEL_GENERIC; + m_hitColModel = colmodel; +} + +CColModel* +CPedModelInfo::AnimatePedColModelSkinned(RpClump *clump) +{ + if(m_hitColModel == nil){ + CreateHitColModelSkinned(clump); + return m_hitColModel; + } + RwMatrix *invmat, *mat; + CColSphere *spheres = m_hitColModel->spheres; + RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(clump); + invmat = RwMatrixCreate(); + mat = RwMatrixCreate(); + RwMatrixInvert(invmat, RwFrameGetMatrix(RpClumpGetFrame(clump))); + + for(int i = 0; i < NUMPEDINFONODES; i++){ + *mat = *invmat; + int id = ConvertPedNode2BoneTag(m_pColNodeInfos[i].pedNode); + int idx = RpHAnimIDGetIndex(hier, id); + + RwMatrixTransform(mat, &RpHAnimHierarchyGetMatrixArray(hier)[idx], rwCOMBINEPRECONCAT); + RwV3d pos = { 0.0f, 0.0f, 0.0f }; + RwV3dTransformPoints(&pos, &pos, 1, mat); + + spheres[i].center = pos + CVector(m_pColNodeInfos[i].x, 0.0f, m_pColNodeInfos[i].z); + } + RwMatrixDestroy(invmat); + RwMatrixDestroy(mat); + return m_hitColModel; +} + +CColModel* +CPedModelInfo::AnimatePedColModelSkinnedWorld(RpClump *clump) +{ + if(m_hitColModel == nil) + CreateHitColModelSkinned(clump); + CColSphere *spheres = m_hitColModel->spheres; + RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(clump); + RwMatrix *mat; + + for(int i = 0; i < NUMPEDINFONODES; i++){ + int id = ConvertPedNode2BoneTag(m_pColNodeInfos[i].pedNode); + int idx = RpHAnimIDGetIndex(hier, id); + + mat = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwV3d pos = { 0.0f, 0.0f, 0.0f }; + RwV3dTransformPoints(&pos, &pos, 1, mat); + + spheres[i].center = pos + CVector(m_pColNodeInfos[i].x, 0.0f, m_pColNodeInfos[i].z); + } + return m_hitColModel; +} diff --git a/src/miami/modelinfo/PedModelInfo.h b/src/miami/modelinfo/PedModelInfo.h new file mode 100644 index 00000000..79bd7eaa --- /dev/null +++ b/src/miami/modelinfo/PedModelInfo.h @@ -0,0 +1,52 @@ +#pragma once + +#include "ClumpModelInfo.h" +#include "ColModel.h" +#include "PedType.h" + +enum PedNode { + PED_TORSO = 0, // has no bone! + PED_MID, + PED_HEAD, + PED_UPPERARML, + PED_UPPERARMR, + PED_HANDL, + PED_HANDR, + PED_UPPERLEGL, + PED_UPPERLEGR, + PED_FOOTL, + PED_FOOTR, + PED_LOWERLEGR, + PED_LOWERLEGL, + + PED_FOREARML, + PED_FOREARMR, + PED_CLAVICLEL, + PED_CLAVICLER, + PED_NECK, + + PED_NODE_MAX +}; + +class CPedModelInfo : public CClumpModelInfo +{ +public: + uint32 m_animGroup; + ePedType m_pedType; + ePedStats m_pedStatType; + uint32 m_carsCanDrive; + CColModel *m_hitColModel; + int8 radio1, radio2; + + static RwObjectNameIdAssocation m_pPedIds[PED_NODE_MAX]; + + CPedModelInfo(void) : CClumpModelInfo(MITYPE_PED) { m_hitColModel = nil; } + ~CPedModelInfo(void) { delete m_hitColModel; } + void DeleteRwObject(void); + void SetClump(RpClump *); + + void CreateHitColModelSkinned(RpClump *clump); + CColModel *GetHitColModel(void) { return m_hitColModel; } + CColModel *AnimatePedColModelSkinned(RpClump *clump); + CColModel *AnimatePedColModelSkinnedWorld(RpClump *clump); +}; diff --git a/src/miami/modelinfo/SimpleModelInfo.cpp b/src/miami/modelinfo/SimpleModelInfo.cpp new file mode 100644 index 00000000..18eb7e5f --- /dev/null +++ b/src/miami/modelinfo/SimpleModelInfo.cpp @@ -0,0 +1,194 @@ +#include "common.h" + +#include "General.h" +#include "Camera.h" +#include "Renderer.h" +#include "ModelInfo.h" +#include "AnimManager.h" +#include "custompipes.h" + +void +CSimpleModelInfo::DeleteRwObject(void) +{ + int i; + RwFrame *f; + for(i = 0; i < m_numAtomics; i++) + if(m_atomics[i]){ + f = RpAtomicGetFrame(m_atomics[i]); + RpAtomicDestroy(m_atomics[i]); + RwFrameDestroy(f); + m_atomics[i] = nil; + RemoveTexDictionaryRef(); + if(GetAnimFileIndex() != -1) + CAnimManager::RemoveAnimBlockRef(GetAnimFileIndex()); + } +} + +RwObject* +CSimpleModelInfo::CreateInstance(void) +{ + RpAtomic *atomic; + if(m_atomics[0] == nil) + return nil; + atomic = RpAtomicClone(m_atomics[0]); + RpAtomicSetFrame(atomic, RwFrameCreate()); + return (RwObject*)atomic; +} + +RwObject* +CSimpleModelInfo::CreateInstance(RwMatrix *matrix) +{ + RpAtomic *atomic; + RwFrame *frame; + + if(m_atomics[0] == nil) + return nil; + atomic = RpAtomicClone(m_atomics[0]); + frame = RwFrameCreate(); + *RwFrameGetMatrix(frame) = *matrix; + RpAtomicSetFrame(atomic, frame); + return (RwObject*)atomic; +} + +void +CSimpleModelInfo::Init(void) +{ + m_atomics[0] = nil; + m_atomics[1] = nil; + m_atomics[2] = nil; + m_numAtomics = 0; + m_firstDamaged = 0; + m_wetRoadReflection = 0; + m_isDamaged = 0; + m_isBigBuilding = 0; + m_noFade = 0; + m_drawLast = 0; + m_additive = 0; + m_isSubway = 0; + m_ignoreLight = 0; + m_noZwrite = 0; + m_noShadows = 0; + m_ignoreDrawDist = 0; + m_isCodeGlass = 0; + m_isArtistGlass = 0; +} + +void +CSimpleModelInfo::SetAtomic(int n, RpAtomic *atomic) +{ + AddTexDictionaryRef(); + m_atomics[n] = atomic; + if(GetAnimFileIndex() != -1) + CAnimManager::AddAnimBlockRef(GetAnimFileIndex()); + RpGeometry *geo = RpAtomicGetGeometry(atomic); + if(m_ignoreLight) + RpGeometrySetFlags(geo, RpGeometryGetFlags(geo) & ~rpGEOMETRYLIGHT); + if(RpGeometryGetFlags(geo) & rpGEOMETRYNORMALS && + RpGeometryGetNumTriangles(geo) > 200) + debug("%s has %d polys\n", m_name, RpGeometryGetNumTriangles(geo)); + +#ifdef EXTENDED_PIPELINES + if(m_wetRoadReflection) + CustomPipes::AttachGlossPipe(atomic); + else + CustomPipes::AttachWorldPipe(atomic); +#endif +} + +void +CSimpleModelInfo::SetLodDistances(float *dist) +{ + m_lodDistances[0] = dist[0]; + m_lodDistances[1] = dist[1]; + m_lodDistances[2] = dist[2]; +} + +void +CSimpleModelInfo::IncreaseAlpha(void) +{ + if(m_alpha >= 0xEF) + m_alpha = 0xFF; + else + m_alpha += 0x10; +} + +float +CSimpleModelInfo::GetLodDistance(int i) +{ + return m_lodDistances[i] * TheCamera.LODDistMultiplier; +} + +float +CSimpleModelInfo::GetNearDistance(void) +{ + return m_lodDistances[2] * TheCamera.LODDistMultiplier; +} + +float +CSimpleModelInfo::GetLargestLodDistance(void) +{ + float d; + if(m_firstDamaged == 0 || m_isDamaged) + d = m_lodDistances[m_numAtomics-1]; + else + d = m_lodDistances[m_firstDamaged-1]; + return d * TheCamera.LODDistMultiplier; +} + +RpAtomic* +CSimpleModelInfo::GetAtomicFromDistance(float dist) +{ + int i; + i = 0; + if(m_isDamaged) + i = m_firstDamaged; + for(; i < m_numAtomics; i++) + if(dist < m_lodDistances[i] * TheCamera.LODDistMultiplier) + return m_atomics[i]; + return nil; +} + +RpAtomic* +CSimpleModelInfo::GetFirstAtomicFromDistance(float dist) +{ + if(dist < m_lodDistances[0] * TheCamera.LODDistMultiplier) + return m_atomics[0]; + return nil; +} + +void +CSimpleModelInfo::FindRelatedModel(int32 minID, int32 maxID) +{ + int i; + CBaseModelInfo *mi; + for(i = minID; i <= maxID; i++){ + mi = CModelInfo::GetModelInfo(i); + if(mi && mi != this && + !CGeneral::faststrcmp(GetModelName()+3, mi->GetModelName()+3)){ + assert(mi->IsSimple()); + this->SetRelatedModel((CSimpleModelInfo*)mi); + return; + } + } +} + +#define NEAR_DRAW_DIST 0.0f // 100.0f in liberty city + +void +CSimpleModelInfo::SetupBigBuilding(int32 minID, int32 maxID) +{ + CSimpleModelInfo *related; + if(m_lodDistances[0] > LOD_DISTANCE && GetRelatedModel() == nil){ + m_isBigBuilding = 1; + FindRelatedModel(minID, maxID); + related = GetRelatedModel(); + if(related){ + m_lodDistances[2] = related->GetLargestLodDistance()/TheCamera.LODDistMultiplier; + if(m_drawLast){ + m_drawLast = false; + debug("%s was draw last\n", GetModelName()); + } + }else + m_lodDistances[2] = NEAR_DRAW_DIST; + } +} diff --git a/src/miami/modelinfo/SimpleModelInfo.h b/src/miami/modelinfo/SimpleModelInfo.h new file mode 100644 index 00000000..b511c235 --- /dev/null +++ b/src/miami/modelinfo/SimpleModelInfo.h @@ -0,0 +1,64 @@ +#pragma once + +#include "BaseModelInfo.h" + +class CSimpleModelInfo : public CBaseModelInfo +{ +public: + // atomics[2] is often a pointer to the non-LOD modelinfo + RpAtomic *m_atomics[3]; + // m_lodDistances[2] holds the near distance for LODs + float m_lodDistances[3]; + uint8 m_numAtomics; + uint8 m_alpha; + uint16 m_firstDamaged : 2; // 0: no damage model + // 1: 1 and 2 are damage models + // 2: 2 is damage model + uint16 m_wetRoadReflection : 1; + uint16 m_isDamaged : 1; + + uint16 m_isBigBuilding : 1; + uint16 m_noFade : 1; + uint16 m_drawLast : 1; + uint16 m_additive : 1; + + uint16 m_isSubway : 1; + uint16 m_ignoreLight : 1; + uint16 m_noZwrite : 1; + uint16 m_noShadows : 1; + + uint16 m_ignoreDrawDist : 1; + uint16 m_isCodeGlass : 1; + uint16 m_isArtistGlass : 1; +#ifdef RW_DC + bool m_isAlphaTest; +#endif + + CSimpleModelInfo(void) : CBaseModelInfo(MITYPE_SIMPLE) {} + CSimpleModelInfo(ModelInfoType id) : CBaseModelInfo(id) {} + ~CSimpleModelInfo() {} + void DeleteRwObject(void); + RwObject *CreateInstance(void); + RwObject *CreateInstance(RwMatrix *); + RwObject *GetRwObject(void) { return (RwObject*)m_atomics[0]; } + + virtual void SetAtomic(int n, RpAtomic *atomic); + + void Init(void); + void IncreaseAlpha(void); + void SetLodDistances(float *dist); + float GetLodDistance(int i); + float GetNearDistance(void); + float GetLargestLodDistance(void); + RpAtomic *GetAtomicFromDistance(float dist); + RpAtomic *GetFirstAtomicFromDistance(float dist); + void FindRelatedModel(int32 minID, int32 maxID); + void SetupBigBuilding(int32 minID, int32 maxID); + + void SetNumAtomics(int n) { m_numAtomics = n; } + CSimpleModelInfo *GetRelatedModel(void){ + return (CSimpleModelInfo*)m_atomics[2]; } + void SetRelatedModel(CSimpleModelInfo *m){ + m_atomics[2] = (RpAtomic*)m; } +}; +//static_assert(sizeof(CSimpleModelInfo) == 0x4C, "CSimpleModelInfo: error"); diff --git a/src/miami/modelinfo/TimeModelInfo.cpp b/src/miami/modelinfo/TimeModelInfo.cpp new file mode 100644 index 00000000..0db5fb78 --- /dev/null +++ b/src/miami/modelinfo/TimeModelInfo.cpp @@ -0,0 +1,33 @@ +#include "common.h" + +#include "Camera.h" +#include "ModelInfo.h" +#include "General.h" + +CTimeModelInfo* +CTimeModelInfo::FindOtherTimeModel(void) +{ + char name[40]; + char *p; + int i; + + strcpy(name, GetModelName()); + // change _nt to _dy + if(p = strstr(name, "_nt")) + strncpy(p, "_dy", 4); + // change _dy to _nt + else if(p = strstr(name, "_dy")) + strncpy(p, "_nt", 4); + else + return nil; + + for(i = 0; i < MODELINFOSIZE; i++){ + CBaseModelInfo *mi = CModelInfo::GetModelInfo(i); + if (mi && mi->GetModelType() == MITYPE_TIME && + !CGeneral::faststrncmp(name, mi->GetModelName(), MAX_MODEL_NAME)){ + m_otherTimeModelID = i; + return (CTimeModelInfo*)mi; + } + } + return nil; +} diff --git a/src/miami/modelinfo/TimeModelInfo.h b/src/miami/modelinfo/TimeModelInfo.h new file mode 100644 index 00000000..6e3c64fb --- /dev/null +++ b/src/miami/modelinfo/TimeModelInfo.h @@ -0,0 +1,20 @@ +#pragma once + +#include "SimpleModelInfo.h" + +class CTimeModelInfo : public CSimpleModelInfo +{ + int32 m_timeOn; + int32 m_timeOff; + int32 m_otherTimeModelID; +public: + CTimeModelInfo(void) : CSimpleModelInfo(MITYPE_TIME) { m_otherTimeModelID = -1; } + + int32 GetTimeOn(void) { return m_timeOn; } + int32 GetTimeOff(void) { return m_timeOff; } + void SetTimes(int32 on, int32 off) { m_timeOn = on; m_timeOff = off; } + int32 GetOtherTimeModel(void) { return m_otherTimeModelID; } + void SetOtherTimeModel(int32 other) { m_otherTimeModelID = other; } + CTimeModelInfo *FindOtherTimeModel(void); +}; +//static_assert(sizeof(CTimeModelInfo) == 0x58, "CTimeModelInfo: error"); diff --git a/src/miami/modelinfo/VehicleModelInfo.cpp b/src/miami/modelinfo/VehicleModelInfo.cpp new file mode 100644 index 00000000..d31962ce --- /dev/null +++ b/src/miami/modelinfo/VehicleModelInfo.cpp @@ -0,0 +1,1193 @@ +#include "common.h" +#include + +#include "RwHelper.h" +#include "General.h" +#include "NodeName.h" +#include "TxdStore.h" +#include "Weather.h" +#include "HandlingMgr.h" +#include "VisibilityPlugins.h" +#include "FileMgr.h" +#include "World.h" +#include "Vehicle.h" +#include "Automobile.h" +#include "Boat.h" +#include "Train.h" +#include "Plane.h" +#include "Heli.h" +#include "Bike.h" +#include "ModelIndices.h" +#include "ModelInfo.h" +#include "custompipes.h" + +int8 CVehicleModelInfo::ms_compsToUse[2] = { -2, -2 }; +int8 CVehicleModelInfo::ms_compsUsed[2]; +RwRGBA CVehicleModelInfo::ms_vehicleColourTable[256]; +RwTexture *CVehicleModelInfo::ms_colourTextureTable[256]; + +RwTexture *gpWhiteTexture; +RwFrame *pMatFxIdentityFrame; + +enum { + VEHICLE_FLAG_COLLAPSE = 0x2, + VEHICLE_FLAG_ADD_WHEEL = 0x4, + VEHICLE_FLAG_POS = 0x8, + VEHICLE_FLAG_DOOR = 0x10, + VEHICLE_FLAG_LEFT = 0x20, + VEHICLE_FLAG_RIGHT = 0x40, + VEHICLE_FLAG_FRONT = 0x80, + VEHICLE_FLAG_REAR = 0x100, + VEHICLE_FLAG_COMP = 0x200, + VEHICLE_FLAG_DRAWLAST = 0x400, + VEHICLE_FLAG_WINDSCREEN = 0x800, + VEHICLE_FLAG_ANGLECULL = 0x1000, + VEHICLE_FLAG_REARDOOR = 0x2000, + VEHICLE_FLAG_FRONTDOOR = 0x4000, +}; + +RwObjectNameIdAssocation carIds[] = { + { "wheel_rf_dummy", CAR_WHEEL_RF, VEHICLE_FLAG_RIGHT | VEHICLE_FLAG_ADD_WHEEL }, + { "wheel_rm_dummy", CAR_WHEEL_RM, VEHICLE_FLAG_RIGHT | VEHICLE_FLAG_ADD_WHEEL }, + { "wheel_rb_dummy", CAR_WHEEL_RB, VEHICLE_FLAG_RIGHT | VEHICLE_FLAG_ADD_WHEEL }, + { "wheel_lf_dummy", CAR_WHEEL_LF, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_ADD_WHEEL }, + { "wheel_lm_dummy", CAR_WHEEL_LM, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_ADD_WHEEL }, + { "wheel_lb_dummy", CAR_WHEEL_LB, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_ADD_WHEEL }, + { "bump_front_dummy", CAR_BUMP_FRONT, VEHICLE_FLAG_FRONT | VEHICLE_FLAG_COLLAPSE }, + { "bonnet_dummy", CAR_BONNET, VEHICLE_FLAG_COLLAPSE }, + { "wing_rf_dummy", CAR_WING_RF, VEHICLE_FLAG_COLLAPSE }, + { "wing_rr_dummy", CAR_WING_RR, VEHICLE_FLAG_RIGHT | VEHICLE_FLAG_COLLAPSE }, + { "door_rf_dummy", CAR_DOOR_RF, VEHICLE_FLAG_FRONTDOOR | VEHICLE_FLAG_ANGLECULL | VEHICLE_FLAG_RIGHT | VEHICLE_FLAG_DOOR | VEHICLE_FLAG_COLLAPSE }, + { "door_rr_dummy", CAR_DOOR_RR, VEHICLE_FLAG_REARDOOR | VEHICLE_FLAG_ANGLECULL | VEHICLE_FLAG_REAR | VEHICLE_FLAG_RIGHT | VEHICLE_FLAG_DOOR | VEHICLE_FLAG_COLLAPSE }, + { "wing_lf_dummy", CAR_WING_LF, VEHICLE_FLAG_COLLAPSE }, + { "wing_lr_dummy", CAR_WING_LR, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_COLLAPSE }, + { "door_lf_dummy", CAR_DOOR_LF, VEHICLE_FLAG_FRONTDOOR | VEHICLE_FLAG_ANGLECULL | VEHICLE_FLAG_LEFT | VEHICLE_FLAG_DOOR | VEHICLE_FLAG_COLLAPSE }, + { "door_lr_dummy", CAR_DOOR_LR, VEHICLE_FLAG_REARDOOR | VEHICLE_FLAG_ANGLECULL | VEHICLE_FLAG_REAR | VEHICLE_FLAG_LEFT | VEHICLE_FLAG_DOOR | VEHICLE_FLAG_COLLAPSE }, + { "boot_dummy", CAR_BOOT, VEHICLE_FLAG_REAR | VEHICLE_FLAG_COLLAPSE }, + { "bump_rear_dummy", CAR_BUMP_REAR, VEHICLE_FLAG_REAR | VEHICLE_FLAG_COLLAPSE }, + { "windscreen_dummy", CAR_WINDSCREEN, VEHICLE_FLAG_WINDSCREEN | VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_FRONT | VEHICLE_FLAG_COLLAPSE }, + + { "ped_frontseat", CAR_POS_FRONTSEAT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { "ped_backseat", CAR_POS_BACKSEAT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { "headlights", CAR_POS_HEADLIGHTS, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { "taillights", CAR_POS_TAILLIGHTS, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { "exhaust", CAR_POS_EXHAUST, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { "extra1", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, + { "extra2", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, + { "extra3", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, + { "extra4", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, + { "extra5", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, + { "extra6", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, + { nil, 0, 0 } +}; + +RwObjectNameIdAssocation boatIds[] = { + { "boat_moving_hi", BOAT_MOVING, 0 }, + { "boat_rudder_hi", BOAT_RUDDER, 0 }, + { "boat_flap_left", BOAT_FLAP_LEFT, 0 }, + { "boat_flap_right", BOAT_FLAP_RIGHT, 0 }, + { "boat_rearflap_left", BOAT_REARFLAP_LEFT, 0 }, + { "boat_rearflap_right", BOAT_REARFLAP_RIGHT, 0 }, +#ifdef FIX_BUGS + // let's just accept both + { "windscreen", BOAT_WINDSCREEN, VEHICLE_FLAG_WINDSCREEN | VEHICLE_FLAG_DRAWLAST }, + { "windscreen_hi_ok", BOAT_WINDSCREEN, VEHICLE_FLAG_WINDSCREEN | VEHICLE_FLAG_DRAWLAST }, +#else +#ifdef GTA_PS2 + { "windscreen", BOAT_WINDSCREEN, VEHICLE_FLAG_WINDSCREEN | VEHICLE_FLAG_DRAWLAST }, +#else + { "windscreen_hi_ok", BOAT_WINDSCREEN, VEHICLE_FLAG_WINDSCREEN | VEHICLE_FLAG_DRAWLAST }, +#endif +#endif + { "ped_frontseat", BOAT_POS_FRONTSEAT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { nil, 0, 0 } +}; + +RwObjectNameIdAssocation trainIds[] = { + { "door_lhs_dummy", TRAIN_DOOR_LHS, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_COLLAPSE }, + { "door_rhs_dummy", TRAIN_DOOR_RHS, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_COLLAPSE }, + { "light_front", TRAIN_POS_LIGHT_FRONT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { "light_rear", TRAIN_POS_LIGHT_REAR, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { "ped_left_entry", TRAIN_POS_LEFT_ENTRY, VEHICLE_FLAG_DOOR | VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { "ped_mid_entry", TRAIN_POS_MID_ENTRY, VEHICLE_FLAG_DOOR | VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { "ped_right_entry", TRAIN_POS_RIGHT_ENTRY, VEHICLE_FLAG_DOOR | VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { nil, 0, 0 } +}; + +RwObjectNameIdAssocation heliIds[] = { + { "chassis_dummy", HELI_CHASSIS, VEHICLE_FLAG_COLLAPSE }, + { "toprotor", HELI_TOPROTOR, 0 }, + { "backrotor", HELI_BACKROTOR, 0 }, + { "tail", HELI_TAIL, 0 }, + { "topknot", HELI_TOPKNOT, 0 }, + { "skid_left", HELI_SKID_LEFT, 0 }, + { "skid_right", HELI_SKID_RIGHT, 0 }, + { nil, 0, 0 } +}; + +RwObjectNameIdAssocation planeIds[] = { + { "wheel_front_dummy", PLANE_WHEEL_FRONT, 0 }, + { "wheel_rear_dummy", PLANE_WHEEL_READ, 0 }, + { "light_tailplane", PLANE_POS_LIGHT_TAIL, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { "light_left", PLANE_POS_LIGHT_LEFT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { "light_right", PLANE_POS_LIGHT_RIGHT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { nil, 0, 0 } +}; + +RwObjectNameIdAssocation bikeIds[] = { + { "chassis_dummy", BIKE_CHASSIS, 0 }, + { "forks_front", BIKE_FORKS_FRONT, 0 }, + { "forks_rear", BIKE_FORKS_REAR, 0 }, + { "wheel_front", BIKE_WHEEL_FRONT, 0 }, + { "wheel_rear", BIKE_WHEEL_REAR, 0 }, + { "mudguard", BIKE_MUDGUARD, 0 }, + { "handlebars", BIKE_HANDLEBARS, 0 }, + { "ped_frontseat", CAR_POS_FRONTSEAT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { "ped_backseat", CAR_POS_BACKSEAT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { "headlights", CAR_POS_HEADLIGHTS, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { "taillights", CAR_POS_TAILLIGHTS, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { "exhaust", CAR_POS_EXHAUST, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, + { "extra1", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, + { "extra2", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, + { "extra3", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, + { "extra4", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, + { "extra5", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, + { "extra6", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, + { nil, 0, 0 } +}; + +RwObjectNameIdAssocation *CVehicleModelInfo::ms_vehicleDescs[] = { + carIds, + boatIds, + trainIds, + heliIds, + planeIds, + bikeIds +}; + +bool gbBlackCars; +bool gbPinkCars; + +CVehicleModelInfo::CVehicleModelInfo(void) + : CClumpModelInfo(MITYPE_VEHICLE) +{ + int32 i; + for(i = 0; i < NUM_VEHICLE_POSITIONS; i++){ + m_positions[i].x = 0.0f; + m_positions[i].y = 0.0f; + m_positions[i].z = 0.0f; + } + m_numColours = 0; + m_animFileIndex = -1; +} + +void +CVehicleModelInfo::DeleteRwObject(void) +{ + int32 i; + RwFrame *f; + + for(i = 0; i < m_numComps; i++){ + f = RpAtomicGetFrame(m_comps[i]); + RpAtomicDestroy(m_comps[i]); + RwFrameDestroy(f); + } + m_numComps = 0; + CClumpModelInfo::DeleteRwObject(); +} + +RwObject* +CVehicleModelInfo::CreateInstance(void) +{ + RpClump *clump; + RpAtomic *atomic; + RwFrame *clumpframe, *f; + int32 comp1, comp2; + + clump = (RpClump*)CClumpModelInfo::CreateInstance(); + if(m_numComps != 0){ + clumpframe = RpClumpGetFrame(clump); + + comp1 = ChooseComponent(); + if(comp1 != -1 && m_comps[comp1]){ + atomic = RpAtomicClone(m_comps[comp1]); + f = RwFrameCreate(); + RwFrameTransform(f, + RwFrameGetMatrix(RpAtomicGetFrame(m_comps[comp1])), + rwCOMBINEREPLACE); + RpAtomicSetFrame(atomic, f); + RpClumpAddAtomic(clump, atomic); + RwFrameAddChild(clumpframe, f); + } + ms_compsUsed[0] = comp1; + + comp2 = ChooseSecondComponent(); + if(comp2 != -1 && m_comps[comp2]){ + atomic = RpAtomicClone(m_comps[comp2]); + f = RwFrameCreate(); + RwFrameTransform(f, + RwFrameGetMatrix(RpAtomicGetFrame(m_comps[comp2])), + rwCOMBINEREPLACE); + RpAtomicSetFrame(atomic, f); + RpClumpAddAtomic(clump, atomic); + RwFrameAddChild(clumpframe, f); + } + ms_compsUsed[1] = comp2; + }else{ + ms_compsUsed[0] = -1; + ms_compsUsed[1] = -1; + } + return (RwObject*)clump; +} + +void +CVehicleModelInfo::SetClump(RpClump *clump) +{ + CClumpModelInfo::SetClump(clump); + SetAtomicRenderCallbacks(); + SetFrameIds(ms_vehicleDescs[m_vehicleType]); + PreprocessHierarchy(); + FindEditableMaterialList(); + SetEnvironmentMap(); +} + +void +CVehicleModelInfo::SetAnimFile(const char *file) +{ + if(strcasecmp(file, "null") == 0) + return; + + m_animFileName = new char[strlen(file)+1]; + strcpy(m_animFileName, file); +} + +void +CVehicleModelInfo::ConvertAnimFileIndex(void) +{ + if(m_animFileIndex != -1){ + // we have a string pointer in that union + int32 index = CAnimManager::GetAnimationBlockIndex(m_animFileName); + delete[] m_animFileName; + m_animFileIndex = index; + } +} + +RwFrame* +CVehicleModelInfo::CollapseFramesCB(RwFrame *frame, void *data) +{ + RwFrameForAllChildren(frame, CollapseFramesCB, data); + RwFrameForAllObjects(frame, MoveObjectsCB, data); + RwFrameDestroy(frame); + return frame; +} + +RwObject* +CVehicleModelInfo::MoveObjectsCB(RwObject *object, void *data) +{ + RpAtomicSetFrame((RpAtomic*)object, (RwFrame*)data); + return object; +} + +RpAtomic* +CVehicleModelInfo::HideDamagedAtomicCB(RpAtomic *atomic, void *data) +{ + if(strstr(GetFrameNodeName(RpAtomicGetFrame(atomic)), "_dam")){ + RpAtomicSetFlags(atomic, 0); + CVisibilityPlugins::SetAtomicFlag(atomic, ATOMIC_FLAG_DAM); + }else if(strstr(GetFrameNodeName(RpAtomicGetFrame(atomic)), "_ok")) + CVisibilityPlugins::SetAtomicFlag(atomic, ATOMIC_FLAG_OK); + return atomic; +} + +RpAtomic* +CVehicleModelInfo::HideAllComponentsAtomicCB(RpAtomic *atomic, void *data) +{ + if(CVisibilityPlugins::GetAtomicId(atomic) & (uintptr)data) + RpAtomicSetFlags(atomic, 0); + else + RpAtomicSetFlags(atomic, rpATOMICRENDER); + return atomic; +} + +RpMaterial* +CVehicleModelInfo::HasAlphaMaterialCB(RpMaterial *material, void *data) +{ + if(RpMaterialGetColor(material)->alpha != 0xFF){ + *(bool*)data = true; + return nil; + } + return material; +} + + +RpAtomic* +CVehicleModelInfo::SetAtomicRendererCB(RpAtomic *atomic, void *data) +{ + RpClump *clump; + char *name; + bool alpha; + + clump = (RpClump*)data; + name = GetFrameNodeName(RpAtomicGetFrame(atomic)); + alpha = false; + RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), HasAlphaMaterialCB, &alpha); + if(strstr(name, "_hi") || !CGeneral::faststrncmp(name, "extra", 5)) { + if(alpha || strncmp(name, "windscreen", 10) == 0) + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailAlphaCB); + else + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailCB); + }else if(strstr(name, "_lo")){ + RpClumpRemoveAtomic(clump, atomic); + RpAtomicDestroy(atomic); + return atomic; // BUG: nil in gta + }else if(strstr(name, "_vlo")) + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleReallyLowDetailCB); + else + CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); + HideDamagedAtomicCB(atomic, nil); + return atomic; +} + +RpAtomic* +CVehicleModelInfo::SetAtomicRendererCB_BigVehicle(RpAtomic *atomic, void *data) +{ + char *name; + bool alpha; + + name = GetFrameNodeName(RpAtomicGetFrame(atomic)); + alpha = false; + RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), HasAlphaMaterialCB, &alpha); + if(strstr(name, "_hi") || !CGeneral::faststrncmp(name, "extra", 5)) { + if(alpha) + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailAlphaCB_BigVehicle); + else + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailCB_BigVehicle); + }else if(strstr(name, "_lo")){ + if(alpha) + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleLowDetailAlphaCB_BigVehicle); + else + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleLowDetailCB_BigVehicle); + }else if(strstr(name, "_vlo")) + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleReallyLowDetailCB_BigVehicle); + else + CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); + HideDamagedAtomicCB(atomic, nil); + return atomic; +} + +RpAtomic* +CVehicleModelInfo::SetAtomicRendererCB_Train(RpAtomic *atomic, void *data) +{ + char *name; + bool alpha; + + name = GetFrameNodeName(RpAtomicGetFrame(atomic)); + alpha = false; + RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), HasAlphaMaterialCB, &alpha); + if(strstr(name, "_hi")){ + if(alpha) + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderTrainHiDetailAlphaCB); + else + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderTrainHiDetailCB); + }else if(strstr(name, "_vlo")) + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleReallyLowDetailCB_BigVehicle); + else + CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); + HideDamagedAtomicCB(atomic, nil); + return atomic; +} + +RpAtomic* +CVehicleModelInfo::SetAtomicRendererCB_Boat(RpAtomic *atomic, void *data) +{ + RpClump *clump; + char *name; + bool alpha; + + clump = (RpClump*)data; + name = GetFrameNodeName(RpAtomicGetFrame(atomic)); + alpha = false; + RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), HasAlphaMaterialCB, &alpha); + if(strcmp(name, "boat_hi") == 0 || !CGeneral::faststrncmp(name, "extra", 5)) + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailCB_Boat); + else if(strstr(name, "_hi")){ + if(alpha) + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailAlphaCB_Boat); + else + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailCB); + }else if(strstr(name, "_lo")){ + RpClumpRemoveAtomic(clump, atomic); + RpAtomicDestroy(atomic); + return atomic; // BUG: not done by gta + }else if(strstr(name, "_vlo")) + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleLoDetailCB_Boat); + else{ + if(alpha) + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailAlphaCB_Boat); + else + CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); + } + HideDamagedAtomicCB(atomic, nil); + return atomic; +} + +RpAtomic* +CVehicleModelInfo::SetAtomicRendererCB_Heli(RpAtomic *atomic, void *data) +{ + char *name; + + name = GetFrameNodeName(RpAtomicGetFrame(atomic)); + if(strncmp(name, "toprotor", 8) == 0) + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleRotorAlphaCB); + else if(strncmp(name, "rearrotor", 9) == 0) + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleTailRotorAlphaCB); + else + CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); + return atomic; +} + +RpAtomic* +CVehicleModelInfo::SetAtomicRendererCB_RealHeli(RpAtomic *atomic, void *data) +{ + RpClump *clump; + char *name; + bool alpha; + + clump = (RpClump*)data; + name = GetFrameNodeName(RpAtomicGetFrame(atomic)); + alpha = false; + RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), HasAlphaMaterialCB, &alpha); + if(strncmp(name, "toprotor", 8) == 0) + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleRotorAlphaCB); + else if(strncmp(name, "rearrotor", 9) == 0) + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleTailRotorAlphaCB); + else if(strstr(name, "_hi") || !CGeneral::faststrncmp(name, "extra", 5)) { + if(alpha || strncmp(name, "windscreen", 10) == 0) + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailAlphaCB); + else + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailCB); + }else if(strstr(name, "_lo")){ + RpClumpRemoveAtomic(clump, atomic); + RpAtomicDestroy(atomic); + return atomic; // BUG: nil in gta + }else if(strstr(name, "_vlo")) + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleReallyLowDetailCB); + else + CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); + HideDamagedAtomicCB(atomic, nil); + return atomic; +} + +void +CVehicleModelInfo::SetAtomicRenderCallbacks(void) +{ +#ifdef GTA_TRAIN + if(m_vehicleType == VEHICLE_TYPE_TRAIN) + RpClumpForAllAtomics(m_clump, SetAtomicRendererCB_Train, nil); + else +#endif + if(m_vehicleType == VEHICLE_TYPE_HELI) + RpClumpForAllAtomics(m_clump, SetAtomicRendererCB_Heli, nil); + else if(m_vehicleType == VEHICLE_TYPE_PLANE) + RpClumpForAllAtomics(m_clump, SetAtomicRendererCB_BigVehicle, nil); + else if(m_vehicleType == VEHICLE_TYPE_BOAT) + RpClumpForAllAtomics(m_clump, SetAtomicRendererCB_Boat, m_clump); + else if(mod_HandlingManager.GetHandlingData((tVehicleType)m_handlingId)->Flags & HANDLING_IS_HELI) + RpClumpForAllAtomics(m_clump, SetAtomicRendererCB_RealHeli, m_clump); + else + RpClumpForAllAtomics(m_clump, SetAtomicRendererCB, m_clump); +} + +RwObject* +CVehicleModelInfo::SetAtomicFlagCB(RwObject *object, void *data) +{ + RpAtomic *atomic = (RpAtomic*)object; + assert(RwObjectGetType(object) == rpATOMIC); + CVisibilityPlugins::SetAtomicFlag(atomic, (uintptr)data); + return object; +} + +RwObject* +CVehicleModelInfo::ClearAtomicFlagCB(RwObject *object, void *data) +{ + RpAtomic *atomic = (RpAtomic*)object; + assert(RwObjectGetType(object) == rpATOMIC); + CVisibilityPlugins::ClearAtomicFlag(atomic, (uintptr)data); + return object; +} + +RwObject* +GetOkAndDamagedAtomicCB(RwObject *object, void *data) +{ + RpAtomic *atomic = (RpAtomic*)object; + if(CVisibilityPlugins::GetAtomicId(atomic) & ATOMIC_FLAG_OK) + ((RpAtomic**)data)[0] = atomic; + else if(CVisibilityPlugins::GetAtomicId(atomic) & ATOMIC_FLAG_DAM) + ((RpAtomic**)data)[1] = atomic; + return object; +} + +void +CVehicleModelInfo::PreprocessHierarchy(void) +{ + int32 i; + RwObjectNameIdAssocation *desc; + RwFrame *f; + RpAtomic *atomic; + RwV3d *rwvec; + + desc = ms_vehicleDescs[m_vehicleType]; + m_numDoors = 0; + m_numComps = 0; + + for(i = 0; desc[i].name; i++){ + RwObjectNameAssociation assoc; + + if((desc[i].flags & (VEHICLE_FLAG_COMP|VEHICLE_FLAG_POS)) == 0) + continue; + assoc.frame = nil; + assoc.name = desc[i].name; + RwFrameForAllChildren(RpClumpGetFrame(m_clump), + FindFrameFromNameWithoutIdCB, &assoc); + if(assoc.frame == nil) + continue; + + if(desc[i].flags & VEHICLE_FLAG_DOOR) + m_numDoors++; + + if(desc[i].flags & VEHICLE_FLAG_POS){ + f = assoc.frame; + rwvec = &m_positions[desc[i].hierId]; + *rwvec = *RwMatrixGetPos(RwFrameGetMatrix(f)); + for(f = RwFrameGetParent(f); f; f = RwFrameGetParent(f)) + RwV3dTransformPoints(rwvec, rwvec, 1, RwFrameGetMatrix(f)); + RwFrameDestroy(assoc.frame); + }else{ + atomic = (RpAtomic*)GetFirstObject(assoc.frame); + RpClumpRemoveAtomic(m_clump, atomic); + RwFrameRemoveChild(assoc.frame); + SetVehicleComponentFlags(assoc.frame, desc[i].flags); + m_comps[m_numComps++] = atomic; + } + } + + for(i = 0; desc[i].name; i++){ + RwObjectIdAssociation assoc; + + if(desc[i].flags & (VEHICLE_FLAG_COMP|VEHICLE_FLAG_POS)) + continue; + assoc.frame = nil; + assoc.id = desc[i].hierId; + RwFrameForAllChildren(RpClumpGetFrame(m_clump), + FindFrameFromIdCB, &assoc); + if(assoc.frame == nil) + continue; + + if(desc[i].flags & VEHICLE_FLAG_DOOR) + m_numDoors++; + + if(desc[i].flags & VEHICLE_FLAG_COLLAPSE){ + RpAtomic *okdam[2] = { nil, nil }; + RwFrameForAllChildren(assoc.frame, CollapseFramesCB, assoc.frame); + RwFrameUpdateObjects(assoc.frame); + RwFrameForAllObjects(assoc.frame, GetOkAndDamagedAtomicCB, okdam); + if(okdam[0] && okdam[1]) + RpAtomicSetRenderCallBack(okdam[1], RpAtomicGetRenderCallBack(okdam[0])); + } + + SetVehicleComponentFlags(assoc.frame, desc[i].flags); + + if(desc[i].flags & VEHICLE_FLAG_ADD_WHEEL){ + if(m_wheelId == -1) + RwFrameDestroy(assoc.frame); + else{ + RwV3d scale; + atomic = (RpAtomic*)CModelInfo::GetModelInfo(m_wheelId)->CreateInstance(); + RwFrameDestroy(RpAtomicGetFrame(atomic)); + RpAtomicSetFrame(atomic, assoc.frame); + RpClumpAddAtomic(m_clump, atomic); + CVisibilityPlugins::SetAtomicRenderCallback(atomic, + CVisibilityPlugins::RenderWheelAtomicCB); + scale.x = m_wheelScale; + scale.y = m_wheelScale; + scale.z = m_wheelScale; + RwFrameScale(assoc.frame, &scale, rwCOMBINEPRECONCAT); + } + } + } +} + +void +CVehicleModelInfo::SetVehicleComponentFlags(RwFrame *frame, uint32 flags) +{ + tHandlingData *handling; + + handling = mod_HandlingManager.GetHandlingData((tVehicleType)m_handlingId); + +#define SETFLAGS(f) RwFrameForAllObjects(frame, SetAtomicFlagCB, (void*)(f)) + + if(flags & VEHICLE_FLAG_WINDSCREEN){ + if(this == CModelInfo::GetModelInfo(MI_RHINO)) + return; + SETFLAGS(ATOMIC_FLAG_WINDSCREEN); + } + + if(flags & VEHICLE_FLAG_ANGLECULL) + SETFLAGS(ATOMIC_FLAG_ANGLECULL); + + if(flags & VEHICLE_FLAG_FRONT) + SETFLAGS(ATOMIC_FLAG_FRONT); + else if(flags & VEHICLE_FLAG_REAR && (handling->Flags & HANDLING_IS_VAN || (flags & (VEHICLE_FLAG_LEFT|VEHICLE_FLAG_RIGHT)) == 0)) + SETFLAGS(ATOMIC_FLAG_REAR); + else if(flags & VEHICLE_FLAG_LEFT) + SETFLAGS(ATOMIC_FLAG_LEFT); + else if(flags & VEHICLE_FLAG_RIGHT) + SETFLAGS(ATOMIC_FLAG_RIGHT); + + if(flags & VEHICLE_FLAG_REARDOOR) + SETFLAGS(ATOMIC_FLAG_REARDOOR); + else if(flags & VEHICLE_FLAG_FRONTDOOR) + SETFLAGS(ATOMIC_FLAG_FRONTDOOR); + + if(flags & VEHICLE_FLAG_DRAWLAST) + SETFLAGS(ATOMIC_FLAG_DRAWLAST); +} + +#define COMPRULE_RULE(comprule) (((comprule) >> 12) & 0xF) +#define COMPRULE_COMPS(comprule) ((comprule) & 0xFFF) +#define COMPRULE_COMPN(comps, n) (((comps) >> 4*(n)) & 0xF) +#define COMPRULE2_RULE(comprule) (((comprule) >> (12+16)) & 0xF) +#define COMPRULE2_COMPS(comprule) ((comprule >> 16) & 0xFFF) +#define COMPRULE2_COMPN(comps, n) (((comps >> 16) >> 4*(n)) & 0xF) + +bool +IsValidCompRule(int rule) +{ + if(rule == 2) + return CWeather::OldWeatherType == WEATHER_RAINY || + CWeather::NewWeatherType == WEATHER_RAINY; + return true; +} + +int32 +CountCompsInRule(int comps) +{ + int32 n; + for(n = 0; comps != 0; comps >>= 4) + if((comps & 0xF) != 0xF) + n++; + return n; +} + +int32 +ChooseComponent(int32 rule, int32 comps) +{ + int32 n; + switch(rule){ + // identical cases.... + case 1: + n = CGeneral::GetRandomNumberInRange(0, CountCompsInRule(comps)); + return COMPRULE_COMPN(comps, n); + case 2: + // only valid in rain + n = CGeneral::GetRandomNumberInRange(0, CountCompsInRule(comps)); + return COMPRULE_COMPN(comps, n); + case 3: + n = CGeneral::GetRandomNumberInRange(0, 1+CountCompsInRule(comps)); + if(n != 0) + return COMPRULE_COMPN(comps, n-1); + return -1; + case 4: +#ifdef FIX_BUGS + return CGeneral::GetRandomNumberInRange(0, 6); +#else + return CGeneral::GetRandomNumberInRange(0, 5); +#endif + } + return -1; +} + +int32 +GetListOfComponentsNotUsedByRules(uint32 comprules, int32 numComps, int32 *comps) +{ + int32 i, n; + int32 unused[6] = { 0, 1, 2, 3, 4, 5 }; + + // first comprule + if(COMPRULE_RULE(comprules) && IsValidCompRule(COMPRULE_RULE(comprules))) + for(i = 0; i < 3; i++){ + n = COMPRULE_COMPN(comprules, i); + if(n != 0xF) + unused[n] = 0xF; + } + // second comprule + comprules >>= 16; + if(COMPRULE_RULE(comprules) && IsValidCompRule(COMPRULE_RULE(comprules))) + for(i = 0; i < 3; i++){ + n = COMPRULE_COMPN(comprules, i); + if(n != 0xF) + unused[n] = 0xF; + } + + n = 0; + for(i = 0; i < numComps; i++) + if(unused[i] != 0xF) + comps[n++] = unused[i]; + return n; +} + +int32 wheelIds[] = { CAR_WHEEL_LF, CAR_WHEEL_LB, CAR_WHEEL_RF, CAR_WHEEL_RB }; + +void +CVehicleModelInfo::GetWheelPosn(int32 n, CVector &pos) +{ + RwMatrix *m = RwFrameGetMatrix(GetFrameFromId(m_clump, wheelIds[n])); + pos.x = RwMatrixGetPos(m)->x; + pos.y = RwMatrixGetPos(m)->y; + pos.z = RwMatrixGetPos(m)->z; +} + + +int32 +CVehicleModelInfo::ChooseComponent(void) +{ + int32 comp; + int32 comps[8]; + int32 n; + + comp = -1; + if(ms_compsToUse[0] == -2){ + if(COMPRULE_RULE(m_compRules) && IsValidCompRule(COMPRULE_RULE(m_compRules))) + comp = ::ChooseComponent(COMPRULE_RULE(m_compRules), COMPRULE_COMPS(m_compRules)); + else if(CGeneral::GetRandomNumberInRange(0, 3) < 2){ + n = GetListOfComponentsNotUsedByRules(m_compRules, m_numComps, comps); + if(n) + comp = comps[(int)CGeneral::GetRandomNumberInRange(0, n)]; + } + }else{ + comp = ms_compsToUse[0]; + ms_compsToUse[0] = -2; + } + return comp; +} + +int32 +CVehicleModelInfo::ChooseSecondComponent(void) +{ + int32 comp; + int32 comps[8]; + int32 n; + + comp = -1; + if(ms_compsToUse[1] == -2){ + if(COMPRULE2_RULE(m_compRules) && IsValidCompRule(COMPRULE2_RULE(m_compRules))) + comp = ::ChooseComponent(COMPRULE2_RULE(m_compRules), COMPRULE2_COMPS(m_compRules)); + else if(COMPRULE_RULE(m_compRules) && IsValidCompRule(COMPRULE_RULE(m_compRules)) && + CGeneral::GetRandomNumberInRange(0, 3) < 2){ + + n = GetListOfComponentsNotUsedByRules(m_compRules, m_numComps, comps); + if(n) + comp = comps[(int)CGeneral::GetRandomNumberInRange(0, n)]; + } + }else{ + comp = ms_compsToUse[1]; + ms_compsToUse[1] = -2; + } + return comp; +} + +struct editableMatCBData +{ + CVehicleModelInfo *vehicle; + int32 numMats1; + int32 numMats2; +}; + +RpMaterial* +CVehicleModelInfo::GetEditableMaterialListCB(RpMaterial *material, void *data) +{ + RwRGBA white = { 255, 255, 255, 255 }; + const RwRGBA *col; + editableMatCBData *cbdata; + + cbdata = (editableMatCBData*)data; + col = RpMaterialGetColor(material); + if(col->red == 0x3C && col->green == 0xFF && col->blue == 0){ + cbdata->vehicle->m_materials1[cbdata->numMats1++] = material; + RpMaterialSetColor(material, &white); + }else if(col->red == 0xFF && col->green == 0 && col->blue == 0xAF){ + cbdata->vehicle->m_materials2[cbdata->numMats2++] = material; + RpMaterialSetColor(material, &white); + } + return material; +} + +RpAtomic* +CVehicleModelInfo::GetEditableMaterialListCB(RpAtomic *atomic, void *data) +{ + RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), GetEditableMaterialListCB, data); + return atomic; +} + +static int maxFirstMaterials; +static int maxSecondMaterials; + +void +CVehicleModelInfo::FindEditableMaterialList(void) +{ + editableMatCBData cbdata; + int32 i; + + cbdata.vehicle = this; + cbdata.numMats1 = 0; + cbdata.numMats2 = 0; + RpClumpForAllAtomics(m_clump, GetEditableMaterialListCB, &cbdata); + for(i = 0; i < m_numComps; i++) + GetEditableMaterialListCB(m_comps[i], &cbdata); + m_materials1[cbdata.numMats1] = nil; + m_materials2[cbdata.numMats2] = nil; + maxFirstMaterials = Max(maxFirstMaterials, cbdata.numMats1); + maxSecondMaterials = Max(maxSecondMaterials, cbdata.numMats2); + m_currentColour1 = -1; + m_currentColour2 = -1; +} + +void +CVehicleModelInfo::SetVehicleColour(uint8 c1, uint8 c2) +{ + RwRGBA col, *colp; + RpMaterial **matp; + + if(c1 != m_currentColour1){ + col = ms_vehicleColourTable[c1]; + for(matp = m_materials1; *matp; matp++){ + colp = (RwRGBA*)RpMaterialGetColor(*matp); // get rid of const + colp->red = col.red; + colp->green = col.green; + colp->blue = col.blue; + } + m_currentColour1 = c1; + } + + if(c2 != m_currentColour2){ + col = ms_vehicleColourTable[c2]; + for(matp = m_materials2; *matp; matp++){ + colp = (RwRGBA*)RpMaterialGetColor(*matp); // get rid of const + colp->red = col.red; + colp->green = col.green; + colp->blue = col.blue; + } + m_currentColour2 = c2; + } +} + +void +CVehicleModelInfo::ChooseVehicleColour(uint8 &col1, uint8 &col2) +{ + if(m_numColours == 0 || gbBlackCars){ + col1 = 0; + col2 = 0; + }else if(gbPinkCars){ + col1 = 68; + col2 = 68; + }else{ + m_lastColorVariation = (m_lastColorVariation+1) % m_numColours; + col1 = m_colours1[m_lastColorVariation]; + col2 = m_colours2[m_lastColorVariation]; + if(m_numColours > 1){ + CVehicle *veh = FindPlayerVehicle(); + if(veh && CModelInfo::GetModelInfo(veh->GetModelIndex()) == this && + veh->m_currentColour1 == col1 && + veh->m_currentColour2 == col2){ + m_lastColorVariation = (m_lastColorVariation+1) % m_numColours; + col1 = m_colours1[m_lastColorVariation]; + col2 = m_colours2[m_lastColorVariation]; + } + } + } +} + +void +CVehicleModelInfo::AvoidSameVehicleColour(uint8 *col1, uint8 *col2) +{ + int i, n; + + if(gbBlackCars){ + *col1 = 0; + *col2 = 0; + }else if(gbPinkCars){ + *col1 = 68; + *col2 = 68; + }else{ + if(m_numColours > 1) + for(i = 0; i < 8; i++){ + if(*col1 != m_lastColour1 || *col2 != m_lastColour2) + break; + n = CGeneral::GetRandomNumberInRange(0, m_numColours); + *col1 = m_colours1[n]; + *col2 = m_colours2[n]; + } + m_lastColour1 = *col1; + m_lastColour2 = *col2; + } +} + +// unused +RwTexture* +CreateCarColourTexture(uint8 r, uint8 g, uint8 b) +{ + RwImage *img; + RwRaster *ras; + RwTexture *tex; + RwUInt8 *pixels; + RwInt32 width, height, depth, format; + + img = RwImageCreate(2, 2, 32); + pixels = (RwUInt8*)RwMalloc(2*2*4); + pixels[0] = r; + pixels[1] = g; + pixels[2] = b; + pixels[3] = 0xFF; + pixels[4] = r; + pixels[5] = g; + pixels[6] = b; + pixels[7] = 0xFF; + pixels[8] = r; + pixels[9] = g; + pixels[10] = b; + pixels[11] = 0xFF; + pixels[12] = r; + pixels[13] = g; + pixels[14] = b; + pixels[15] = 0xFF; + RwImageSetPixels(img, pixels); + RwImageSetStride(img, 8); + RwImageSetPalette(img, nil); + RwImageFindRasterFormat(img, rwRASTERTYPETEXTURE, &width, &height, &depth, &format); + ras = RwRasterCreate(width, height, depth, format); + RwRasterSetFromImage(ras, img); + RwImageDestroy(img); + RwFree(pixels); + tex = RwTextureCreate(ras); + RwTextureGetName(tex)[0] = '@'; + return tex; +} + +void +CVehicleModelInfo::LoadVehicleColours(void) +{ + int fd; + int i; + char line[1024]; + int start, end; + int section, numCols; + enum { + NONE, + COLOURS, + CARS + }; + int r, g, b; + char name[64]; + int colors[16]; + int n; + + CFileMgr::ChangeDir("\\DATA\\"); + fd = CFileMgr::OpenFile("CARCOLS.DAT", "r"); + CFileMgr::ChangeDir("\\"); + + for(i = 0; i < 256; i++) + ms_colourTextureTable[i] = nil; + + section = 0; + numCols = 0; + while(CFileMgr::ReadLine(fd, line, sizeof(line))){ + // find first valid character in line + for(start = 0; ; start++) + if(line[start] > ' ' || line[start] == '\0' || line[start] == '\n') + break; + // find end of line + for(end = start; ; end++){ + if(line[end] == '\0' || line[end] == '\n') + break; + if(line[end] == ',' || line[end] == '\r') + line[end] = ' '; + } + line[end] = '\0'; + + // empty line + if(line[start] == '#' || line[start] == '\0') + continue; + + if(section == NONE){ + if(line[start] == 'c' && line[start + 1] == 'o' && line[start + 2] == 'l') + section = COLOURS; + if(line[start] == 'c' && line[start + 1] == 'a' && line[start + 2] == 'r') + section = CARS; + }else if(line[start] == 'e' && line[start + 1] == 'n' && line[start + 2] == 'd'){ + section = NONE; + }else if(section == COLOURS){ + sscanf(&line[start], // BUG: games doesn't add start + "%d %d %d", &r, &g, &b); + ms_vehicleColourTable[numCols].red = r; + ms_vehicleColourTable[numCols].green = g; + ms_vehicleColourTable[numCols].blue = b; + ms_vehicleColourTable[numCols].alpha = 0xFF; + numCols++; + }else if(section == CARS){ + n = sscanf(&line[start], // BUG: games doesn't add start + "%s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", + name, + &colors[0], &colors[1], + &colors[2], &colors[3], + &colors[4], &colors[5], + &colors[6], &colors[7], + &colors[8], &colors[9], + &colors[10], &colors[11], + &colors[12], &colors[13], + &colors[14], &colors[15]); + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(name, nil); + assert(mi); + mi->m_numColours = (n-1)/2; + for(i = 0; i < mi->m_numColours; i++){ + mi->m_colours1[i] = colors[i*2 + 0]; + mi->m_colours2[i] = colors[i*2 + 1]; + } + } + } + + CFileMgr::CloseFile(fd); +} + +void +CVehicleModelInfo::DeleteVehicleColourTextures(void) +{ + int i; + + for(i = 0; i < 256; i++){ + if(ms_colourTextureTable[i]){ + RwTextureDestroy(ms_colourTextureTable[i]); + ms_colourTextureTable[i] = nil; + } + } +} + +RpMaterial* +CVehicleModelInfo::GetMatFXEffectMaterialCB(RpMaterial *material, void *data) +{ + if(RpMatFXMaterialGetEffects(material) == rpMATFXEFFECTNULL) + return material; + *(int*)data = RpMatFXMaterialGetEffects(material); + return nil; +} + +RpMaterial* +CVehicleModelInfo::SetDefaultEnvironmentMapCB(RpMaterial *material, void *data) +{ + if(RpMatFXMaterialGetEffects(material) == rpMATFXEFFECTENVMAP){ + RpMatFXMaterialSetEnvMapFrame(material, pMatFxIdentityFrame); + if(RpMaterialGetTexture(material) == nil) + RpMaterialSetTexture(material, gpWhiteTexture); + RpMatFXMaterialSetEffects(material, rpMATFXEFFECTENVMAP); +#ifndef PS2_MATFX + float coef = RpMatFXMaterialGetEnvMapCoefficient(material); + coef *= 0.25f; // Tone down a bit for PC + RpMatFXMaterialSetEnvMapCoefficient(material, coef); +#endif + } + return material; +} + +RpAtomic* +CVehicleModelInfo::SetEnvironmentMapCB(RpAtomic *atomic, void *data) +{ + int fx; + RpGeometry *geo; + + geo = RpAtomicGetGeometry(atomic); + fx = rpMATFXEFFECTNULL; + RpGeometryForAllMaterials(geo, GetMatFXEffectMaterialCB, &fx); + if(fx != rpMATFXEFFECTNULL){ + RpMatFXAtomicEnableEffects(atomic); + RpGeometryForAllMaterials(geo, SetDefaultEnvironmentMapCB, data); + } + return atomic; +} + +void +CVehicleModelInfo::SetEnvironmentMap(void) +{ + CSimpleModelInfo *wheelmi; + int32 i; + + if(pMatFxIdentityFrame == nil){ + RwV3d axis = { 1.0f, 0.0f, 0.0f }; + pMatFxIdentityFrame = RwFrameCreate(); + RwMatrixRotate(RwFrameGetMatrix(pMatFxIdentityFrame), &axis, 60.0f, rwCOMBINEREPLACE); + RwFrameUpdateObjects(pMatFxIdentityFrame); + RwFrameGetLTM(pMatFxIdentityFrame); + } + + RpClumpForAllAtomics(m_clump, SetEnvironmentMapCB, nil); + if(m_wheelId != -1){ + wheelmi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(m_wheelId); + for(i = 0; i < wheelmi->m_numAtomics; i++) + SetEnvironmentMapCB(wheelmi->m_atomics[i], nil); + } + +#ifdef EXTENDED_PIPELINES + CustomPipes::AttachVehiclePipe(m_clump); +#endif +} + +void +CVehicleModelInfo::LoadEnvironmentMaps(void) +{ + int32 txdslot; + + txdslot = CTxdStore::FindTxdSlot("particle"); + CTxdStore::PushCurrentTxd(); + CTxdStore::SetCurrentTxd(txdslot); + if(gpWhiteTexture == nil){ + gpWhiteTexture = RwTextureRead("white", nil); + RwTextureGetName(gpWhiteTexture)[0] = '@'; + RwTextureSetFilterMode(gpWhiteTexture, rwFILTERLINEAR); + } + CTxdStore::PopCurrentTxd(); +} + +void +CVehicleModelInfo::ShutdownEnvironmentMaps(void) +{ + RwTextureDestroy(gpWhiteTexture); + gpWhiteTexture = nil; + RwFrameDestroy(pMatFxIdentityFrame); + pMatFxIdentityFrame = nil; +} + +int +CVehicleModelInfo::GetMaximumNumberOfPassengersFromNumberOfDoors(int id) +{ + int n; + + switch(id){ + case MI_TRAIN: + n = 3; + break; + case MI_FIRETRUCK: + n = 2; + break; + case MI_HUNTER: + n = 1; + break; + default: + n = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(id))->m_numDoors; + } + + if(n == 0) + return id == MI_RCBANDIT || id == MI_PIZZABOY || id == MI_BAGGAGE ? 0 : 1; + + if(id == MI_COACH) + return 8; + + return n - 1; +} diff --git a/src/miami/modelinfo/VehicleModelInfo.h b/src/miami/modelinfo/VehicleModelInfo.h new file mode 100644 index 00000000..c7a41126 --- /dev/null +++ b/src/miami/modelinfo/VehicleModelInfo.h @@ -0,0 +1,166 @@ +#pragma once + +#include "ClumpModelInfo.h" + +enum { + NUM_FIRST_MATERIALS = 24, + NUM_SECOND_MATERIALS = 20, + NUM_VEHICLE_COLOURS = 8, +}; + +enum { + ATOMIC_FLAG_NONE = 0x0, + ATOMIC_FLAG_OK = 0x1, + ATOMIC_FLAG_DAM = 0x2, + ATOMIC_FLAG_LEFT = 0x4, + ATOMIC_FLAG_RIGHT = 0x8, + ATOMIC_FLAG_FRONT = 0x10, + ATOMIC_FLAG_REAR = 0x20, + ATOMIC_FLAG_DRAWLAST = 0x40, + ATOMIC_FLAG_WINDSCREEN = 0x80, + ATOMIC_FLAG_ANGLECULL = 0x100, + ATOMIC_FLAG_REARDOOR = 0x200, + ATOMIC_FLAG_FRONTDOOR = 0x400, + ATOMIC_FLAG_NOCULL = 0x800, +}; + +enum eVehicleType { + VEHICLE_TYPE_CAR, + VEHICLE_TYPE_BOAT, + VEHICLE_TYPE_TRAIN, + VEHICLE_TYPE_HELI, + VEHICLE_TYPE_PLANE, + VEHICLE_TYPE_BIKE, + NUM_VEHICLE_TYPES +}; + +enum eCarPositions +{ + CAR_POS_HEADLIGHTS, + CAR_POS_TAILLIGHTS, + CAR_POS_FRONTSEAT, + CAR_POS_BACKSEAT, + CAR_POS_EXHAUST +}; + +enum eBoatPositions +{ + BOAT_POS_FRONTSEAT +}; + +enum eTrainPositions +{ + TRAIN_POS_LIGHT_FRONT, + TRAIN_POS_LIGHT_REAR, + TRAIN_POS_LEFT_ENTRY, + TRAIN_POS_MID_ENTRY, + TRAIN_POS_RIGHT_ENTRY +}; + +enum ePlanePositions +{ + PLANE_POS_LIGHT_LEFT, + PLANE_POS_LIGHT_RIGHT, + PLANE_POS_LIGHT_TAIL, +}; + +enum { + NUM_VEHICLE_POSITIONS = 5 +}; + +class CVehicleModelInfo : public CClumpModelInfo +{ +public: + uint8 m_lastColour1; + uint8 m_lastColour2; + char m_gameName[10]; + int32 m_vehicleType; + float m_wheelScale; + union { + int16 m_wheelId; + int16 m_planeLodId; + }; + int16 m_handlingId; + int8 m_numDoors; + int8 m_vehicleClass; + int8 m_level; + int8 m_numComps; + int16 m_frequency; + CVector m_positions[NUM_VEHICLE_POSITIONS]; + uint32 m_compRules; + float m_bikeSteerAngle; + RpMaterial *m_materials1[NUM_FIRST_MATERIALS]; + RpMaterial *m_materials2[NUM_SECOND_MATERIALS]; + uint8 m_colours1[NUM_VEHICLE_COLOURS]; + uint8 m_colours2[NUM_VEHICLE_COLOURS]; + uint8 m_numColours; + uint8 m_lastColorVariation; + uint8 m_currentColour1; + uint8 m_currentColour2; + RpAtomic *m_comps[6]; + // This is stupid, CClumpModelInfo already has it! + union { + int32 m_animFileIndex; + char *m_animFileName; + }; + + static int8 ms_compsToUse[2]; + static int8 ms_compsUsed[2]; + static RwRGBA ms_vehicleColourTable[256]; + static RwTexture *ms_colourTextureTable[256]; + static RwObjectNameIdAssocation *ms_vehicleDescs[NUM_VEHICLE_TYPES]; + + CVehicleModelInfo(void); + void DeleteRwObject(void); + RwObject *CreateInstance(void); + void SetClump(RpClump *); + void SetAnimFile(const char *file); + void ConvertAnimFileIndex(void); + int GetAnimFileIndex(void) { return m_animFileIndex; } + + static RwFrame *CollapseFramesCB(RwFrame *frame, void *data); + static RwObject *MoveObjectsCB(RwObject *object, void *data); + static RpAtomic *HideDamagedAtomicCB(RpAtomic *atomic, void *data); + static RpAtomic *HideAllComponentsAtomicCB(RpAtomic *atomic, void *data); + static RpMaterial *HasAlphaMaterialCB(RpMaterial *material, void *data); + + static RpAtomic *SetAtomicRendererCB(RpAtomic *atomic, void *data); + static RpAtomic *SetAtomicRendererCB_BigVehicle(RpAtomic *atomic, void *data); + static RpAtomic *SetAtomicRendererCB_Train(RpAtomic *atomic, void *data); + static RpAtomic *SetAtomicRendererCB_Boat(RpAtomic *atomic, void *data); + static RpAtomic *SetAtomicRendererCB_Heli(RpAtomic *atomic, void *data); + static RpAtomic *SetAtomicRendererCB_RealHeli(RpAtomic *atomic, void *data); + void SetAtomicRenderCallbacks(void); + + static RwObject *SetAtomicFlagCB(RwObject *object, void *data); + static RwObject *ClearAtomicFlagCB(RwObject *atomic, void *data); + void SetVehicleComponentFlags(RwFrame *frame, uint32 flags); + void PreprocessHierarchy(void); + void GetWheelPosn(int32 n, CVector &pos); + const CVector &GetFrontSeatPosn(void) { return m_vehicleType == VEHICLE_TYPE_BOAT ? m_positions[BOAT_POS_FRONTSEAT] : m_positions[CAR_POS_FRONTSEAT]; } + + int32 ChooseComponent(void); + int32 ChooseSecondComponent(void); + + static RpMaterial *GetEditableMaterialListCB(RpMaterial *material, void *data); + static RpAtomic *GetEditableMaterialListCB(RpAtomic *atomic, void *data); + void FindEditableMaterialList(void); + void SetVehicleColour(uint8 c1, uint8 c2); + void ChooseVehicleColour(uint8 &col1, uint8 &col2); + void AvoidSameVehicleColour(uint8 *col1, uint8 *col2); + static void LoadVehicleColours(void); + static void DeleteVehicleColourTextures(void); + + static RpAtomic *SetEnvironmentMapCB(RpAtomic *atomic, void *data); + static RpMaterial *SetDefaultEnvironmentMapCB(RpMaterial *material, void *data); + static RpMaterial *GetMatFXEffectMaterialCB(RpMaterial *material, void *data); + void SetEnvironmentMap(void); + static void LoadEnvironmentMaps(void); + static void ShutdownEnvironmentMaps(void); + + static int GetMaximumNumberOfPassengersFromNumberOfDoors(int id); + static void SetComponentsToUse(int8 c1, int8 c2) { ms_compsToUse[0] = c1; ms_compsToUse[1] = c2; } +}; + +extern bool gbBlackCars; +extern bool gbPinkCars; diff --git a/src/miami/modelinfo/WeaponModelInfo.cpp b/src/miami/modelinfo/WeaponModelInfo.cpp new file mode 100644 index 00000000..d9294c3f --- /dev/null +++ b/src/miami/modelinfo/WeaponModelInfo.cpp @@ -0,0 +1,53 @@ +#include "common.h" + +#include "ModelInfo.h" +#include "AnimManager.h" +#include "VisibilityPlugins.h" + +void +CWeaponModelInfo::SetAnimFile(const char *file) +{ + if(strcasecmp(file, "null") == 0) + return; + + m_animFileName = new char[strlen(file)+1]; + strcpy(m_animFileName, file); +} + +void +CWeaponModelInfo::ConvertAnimFileIndex(void) +{ + if(m_animFileIndex != -1){ + // we have a string pointer in that union + int32 index = CAnimManager::GetAnimationBlockIndex(m_animFileName); + delete[] m_animFileName; + m_animFileIndex = index; + } +} + +void +CWeaponModelInfo::Init(void) +{ + CSimpleModelInfo::Init(); + SetWeaponInfo(0); +} + +void +CWeaponModelInfo::SetWeaponInfo(int32 weaponId) +{ + m_atomics[2] = (RpAtomic*)weaponId; +} + +eWeaponType +CWeaponModelInfo::GetWeaponInfo(void) +{ + return (eWeaponType)(uintptr)m_atomics[2]; +} + +void +CWeaponModelInfo::SetAtomic(int n, RpAtomic *atomic) +{ + CSimpleModelInfo::SetAtomic(n, atomic); + CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderWeaponCB); +} + diff --git a/src/miami/modelinfo/WeaponModelInfo.h b/src/miami/modelinfo/WeaponModelInfo.h new file mode 100644 index 00000000..548bf8a6 --- /dev/null +++ b/src/miami/modelinfo/WeaponModelInfo.h @@ -0,0 +1,23 @@ +#pragma once + +#include "SimpleModelInfo.h" +#include "WeaponType.h" + +class CWeaponModelInfo : public CSimpleModelInfo +{ + union { + int32 m_animFileIndex; + char *m_animFileName; + }; +public: + CWeaponModelInfo(void) : CSimpleModelInfo(MITYPE_WEAPON) { m_animFileIndex = -1; } + + virtual void SetAnimFile(const char *file); + virtual void ConvertAnimFileIndex(void); + virtual int GetAnimFileIndex(void) { return m_animFileIndex; } + virtual void SetAtomic(int n, RpAtomic *atomic); + + void Init(void); + void SetWeaponInfo(int32 weaponId); + eWeaponType GetWeaponInfo(void); +}; diff --git a/src/miami/objects/CutsceneObject.cpp b/src/miami/objects/CutsceneObject.cpp new file mode 100644 index 00000000..7d9fe640 --- /dev/null +++ b/src/miami/objects/CutsceneObject.cpp @@ -0,0 +1,182 @@ +#include "common.h" + +#include "main.h" +#include "RwHelper.h" +#include "Lights.h" +#include "PointLights.h" +#include "RpAnimBlend.h" +#include "AnimBlendClumpData.h" +#include "Bones.h" +#include "Renderer.h" +#include "ModelIndices.h" +#include "Shadows.h" +#include "Timecycle.h" +#include "CutsceneShadow.h" +#include "CutsceneObject.h" +#include "ModelIndices.h" +#include "RpAnimBlend.h" + + +CCutsceneObject::CCutsceneObject(void) +{ + SetStatus(STATUS_SIMPLE); + bUsesCollision = false; + bStreamingDontDelete = true; + ObjectCreatedBy = CUTSCENE_OBJECT; + m_fMass = 1.0f; + m_fTurnMass = 1.0f; + + m_pAttachTo = nil; + m_pAttachmentObject = nil; + m_pShadow = nil; +} + +CCutsceneObject::~CCutsceneObject(void) +{ + if ( m_pShadow ) + { + delete m_pShadow; + m_pShadow = nil; + } +} + +void +CCutsceneObject::SetModelIndex(uint32 id) +{ + CEntity::SetModelIndex(id); + assert(RwObjectGetType(m_rwObject) == rpCLUMP); + RpAnimBlendClumpInit((RpClump*)m_rwObject); + (*RPANIMBLENDCLUMPDATA(m_rwObject))->velocity3d = &m_vecMoveSpeed; + (*RPANIMBLENDCLUMPDATA(m_rwObject))->frames[0].flag |= AnimBlendFrameData::VELOCITY_EXTRACTION_3D; +} + +void +CCutsceneObject::CreateShadow(void) +{ + if ( IsPedModel(GetModelIndex()) ) + { + m_pShadow = new CCutsceneShadow(); + if (!m_pShadow->IsInitialized()) + m_pShadow->Create(m_rwObject, 6, true, 4, true); + } +} + +void +CCutsceneObject::ProcessControl(void) +{ + CPhysical::ProcessControl(); + + if ( m_pAttachTo ) + { + if ( m_pAttachmentObject ) + GetMatrix() = CMatrix((RwMatrix*)m_pAttachTo); + else + GetMatrix() = CMatrix(RwFrameGetLTM((RwFrame*)m_pAttachTo)); + } + else + { + if(CTimer::GetTimeStep() < 1/100.0f) + m_vecMoveSpeed *= 100.0f; + else + m_vecMoveSpeed *= 1.0f/CTimer::GetTimeStep(); + + ApplyMoveSpeed(); + } +} + +static RpMaterial* +MaterialSetAlpha(RpMaterial *material, void *data) +{ + ((RwRGBA*)RpMaterialGetColor(material))->alpha = (uint8)(uintptr)data; + return material; +} + +void +CCutsceneObject::PreRender(void) +{ + if ( m_pAttachTo ) + { + if ( m_pAttachmentObject ) + { + m_pAttachmentObject->UpdateRpHAnim(); + GetMatrix() = CMatrix((RwMatrix*)m_pAttachTo); + } + else + GetMatrix() = CMatrix(RwFrameGetLTM((RwFrame*)m_pAttachTo)); + + if ( RwObjectGetType(m_rwObject) == rpCLUMP && IsClumpSkinned(GetClump()) ) + { + RpAtomic *atomic = GetFirstAtomic(GetClump()); + atomic->boundingSphere.center = (*RPANIMBLENDCLUMPDATA(GetClump()))->frames[0].hanimFrame->t; + } + } + + if ( RwObjectGetType(m_rwObject) == rpCLUMP ) + UpdateRpHAnim(); + + if(IsPedModel(GetModelIndex())) + { + if ( m_pShadow == nil ) + { + CShadows::StoreShadowForPedObject(this, + CTimeCycle::m_fShadowDisplacementX[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowDisplacementY[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowFrontX[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowFrontY[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowSideX[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowSideY[CTimeCycle::m_CurrentStoredValue]); + } + else + { + if ( m_pShadow->IsInitialized() ) + m_pShadow->UpdateForCutscene(); + + CShadows::StoreShadowForCutscenePedObject(this, + CTimeCycle::m_fShadowDisplacementX[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowDisplacementY[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowFrontX[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowFrontY[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowSideX[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowSideY[CTimeCycle::m_CurrentStoredValue]); + } + + // For some reason xbox/android limbs are transparent here... + RpGeometry *geometry = RpAtomicGetGeometry(GetFirstAtomic(GetClump())); + RpGeometrySetFlags(geometry, RpGeometryGetFlags(geometry) | rpGEOMETRYMODULATEMATERIALCOLOR); + RpGeometryForAllMaterials(geometry, MaterialSetAlpha, (void*)255); + } +} + +void +CCutsceneObject::Render(void) +{ + SetCullMode(rwCULLMODECULLNONE); + CObject::Render(); + SetCullMode(rwCULLMODECULLBACK); +} + +bool +CCutsceneObject::SetupLighting(void) +{ + ActivateDirectional(); + SetAmbientColoursForPedsCarsAndObjects(); + + if(bRenderScorched){ + WorldReplaceNormalLightsWithScorched(Scene.world, 0.1f); + }else{ + CVector coors = GetPosition(); + float lighting = CPointLights::GenerateLightsAffectingObject(&coors); + if(lighting != 1.0f){ + SetAmbientAndDirectionalColours(lighting); + return true; + } + } + + return false; +} + +void +CCutsceneObject::RemoveLighting(bool reset) +{ + CRenderer::RemoveVehiclePedLights(this, reset); +} diff --git a/src/miami/objects/CutsceneObject.h b/src/miami/objects/CutsceneObject.h new file mode 100644 index 00000000..af24c0a6 --- /dev/null +++ b/src/miami/objects/CutsceneObject.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Object.h" + +class CCutsceneShadow; + +class CCutsceneObject : public CObject +{ +public: + CCutsceneShadow *m_pShadow; + void *m_pAttachTo; + CObject *m_pAttachmentObject; + + CCutsceneObject(void); + ~CCutsceneObject(void); + + void SetModelIndex(uint32 id); + void CreateShadow(void); + void ProcessControl(void); + void PreRender(void); + void Render(void); + bool SetupLighting(void); + void RemoveLighting(bool reset); +}; diff --git a/src/miami/objects/DummyObject.cpp b/src/miami/objects/DummyObject.cpp new file mode 100644 index 00000000..8dd1643d --- /dev/null +++ b/src/miami/objects/DummyObject.cpp @@ -0,0 +1,14 @@ +#include "common.h" + +#include "DummyObject.h" +#include "Pools.h" + +CDummyObject::CDummyObject(CObject *obj) +{ + SetModelIndexNoCreate(obj->GetModelIndex()); + if(obj->m_rwObject) + AttachToRwObject(obj->m_rwObject); + obj->DetachFromRwObject(); + m_level = obj->m_level; + m_area = obj->m_area; +} diff --git a/src/miami/objects/DummyObject.h b/src/miami/objects/DummyObject.h new file mode 100644 index 00000000..680df685 --- /dev/null +++ b/src/miami/objects/DummyObject.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Dummy.h" + +class CObject; + +class CDummyObject : public CDummy +{ +public: + CDummyObject(void) {} + CDummyObject(CObject *obj); +}; diff --git a/src/miami/objects/Object.cpp b/src/miami/objects/Object.cpp new file mode 100644 index 00000000..64c9e256 --- /dev/null +++ b/src/miami/objects/Object.cpp @@ -0,0 +1,858 @@ +#include "common.h" + +#include "main.h" +#include "Lights.h" +#include "Pools.h" +#include "Radar.h" +#include "Object.h" +#include "DummyObject.h" +#include "Particle.h" +#include "General.h" +#include "ObjectData.h" +#include "World.h" +#include "Floater.h" +#include "soundlist.h" +#include "WaterLevel.h" +#include "Timecycle.h" +#include "Stats.h" +#include "SpecialFX.h" + +#define BEACHBALL_MAX_SCORE 250 +// the proportion of the ball speed compared to the player speed when it hits the player +#define BEACHBALL_SPEED_PROPORTION 0.4f + +int16 CObject::nNoTempObjects; +//int16 CObject::nBodyCastHealth = 1000; +float CObject::fDistToNearestTree; + +// Object pools tends to be full sometimes, let's free a temp. object in this case. +#ifdef FIX_BUGS +void *CObject::operator new(size_t sz) throw() { + CObject *obj = CPools::GetObjectPool()->New(); + if (!obj) { + CObjectPool *objectPool = CPools::GetObjectPool(); + for (int32 i = 0; i < objectPool->GetSize(); i++) { + CObject *existing = objectPool->GetSlot(i); + if (existing && existing->ObjectCreatedBy == TEMP_OBJECT) { + int32 handle = objectPool->GetIndex(existing); + CWorld::Remove(existing); + delete existing; + obj = objectPool->New(handle); + break; + } + } + } + return obj; +} +#else +void *CObject::operator new(size_t sz) throw() { return CPools::GetObjectPool()->New(); } +#endif +void *CObject::operator new(size_t sz, int handle) throw() { return CPools::GetObjectPool()->New(handle); }; + +void CObject::operator delete(void *p, size_t sz) throw() { CPools::GetObjectPool()->Delete((CObject*)p); } +void CObject::operator delete(void *p, int handle) throw() { CPools::GetObjectPool()->Delete((CObject*)p); } + +CObject::CObject(void) +{ + m_type = ENTITY_TYPE_OBJECT; + m_fUprootLimit = 0.0f; + m_nCollisionDamageEffect = 0; + m_nSpecialCollisionResponseCases = COLLRESPONSE_NONE; + m_bCameraToAvoidThisObject = false; + ObjectCreatedBy = UNKNOWN_OBJECT; + m_nEndOfLifeTime = 0; + // m_nRefModelIndex = -1; // duplicate + // bUseVehicleColours = false; // duplicate + m_colour2 = 0; + m_colour1 = m_colour2; + m_nBonusValue = 0; + m_nCostValue = 0; + bIsPickup = false; + bPickupObjWithMessage = false; + bOutOfStock = false; + bGlassCracked = false; + bGlassBroken = false; + bHasBeenDamaged = false; + m_nRefModelIndex = -1; + bUseVehicleColours = false; + // bIsStreetLight = false; // duplicate + m_pCurSurface = nil; + m_pCollidingEntity = nil; + m_nBeachballBounces = 0; + bIsStreetLight = false; + m_area = AREA_EVERYWHERE; +} + +CObject::CObject(int32 mi, bool createRW) +{ + if (createRW) + SetModelIndex(mi); + else + SetModelIndexNoCreate(mi); + Init(); +} + +CObject::CObject(CDummyObject *dummy) +{ + SetModelIndexNoCreate(dummy->GetModelIndex()); + + if (dummy->m_rwObject) + AttachToRwObject(dummy->m_rwObject); + else + SetMatrix(dummy->GetMatrix()); + + m_objectMatrix = dummy->GetMatrix(); + dummy->DetachFromRwObject(); + Init(); + m_level = dummy->m_level; + m_area = dummy->m_area; +} + +CObject::~CObject(void) +{ + CRadar::ClearBlipForEntity(BLIP_OBJECT, CPools::GetObjectPool()->GetIndex(this)); + + if (m_nRefModelIndex != -1) + CModelInfo::GetModelInfo(m_nRefModelIndex)->RemoveRef(); + + if (ObjectCreatedBy == TEMP_OBJECT && nNoTempObjects != 0) + nNoTempObjects--; +} + +void +CObject::ProcessControl(void) +{ + CVector point, impulse; + if (m_nCollisionDamageEffect) + ObjectDamage(m_fDamageImpulse); + CPhysical::ProcessControl(); + if (mod_Buoyancy.ProcessBuoyancy(this, m_fBuoyancy, &point, &impulse)) { + bIsInWater = true; + SetIsStatic(false); + ApplyMoveForce(impulse); + ApplyTurnForce(impulse, point); + float fTimeStep = Pow(0.97f, CTimer::GetTimeStep()); + m_vecMoveSpeed *= fTimeStep; + m_vecTurnSpeed *= fTimeStep; + } + int16 mi = GetModelIndex(); + if ((mi == MI_EXPLODINGBARREL || mi == MI_PETROLPUMP || mi == MI_PETROLPUMP2) && bHasBeenDamaged && bIsVisible + && (CGeneral::GetRandomNumber() & 0x1F) == 10) { + bExplosionProof = true; + bIsVisible = false; + bUsesCollision = false; + bAffectedByGravity = false; + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + } + if (mi == MI_RCBOMB) { + float fTurnForce = -(m_fTurnMass / 20.0f); + CPhysical::ApplyTurnForce(m_vecMoveSpeed * fTurnForce, -GetForward()); + float fScalar = 1.0f - m_vecMoveSpeed.MagnitudeSqr() / 5.0f; + float fScalarTimed = Pow(fScalar, CTimer::GetTimeStep()); + m_vecMoveSpeed *= fScalarTimed; + } + if (mi == MI_BEACHBALL) { + float fTimeStep = Pow(0.95f, CTimer::GetTimeStep()); + float fPreviousVecSpeedMag = m_vecMoveSpeed.Magnitude2D(); + m_vecMoveSpeed.x *= fTimeStep; + m_vecMoveSpeed.y *= fTimeStep; + m_vecMoveSpeed.z += fPreviousVecSpeedMag - m_vecMoveSpeed.Magnitude2D(); + if (!FindPlayerVehicle()) { + CVector distance; + distance.x = FindPlayerCoors().x - GetPosition().x; + distance.y = FindPlayerCoors().y - GetPosition().y; + distance.z = FindPlayerCoors().z - GetPosition().z; + if (distance.z > 0.0 && distance.z < 1.5f && distance.Magnitude2D() < 1.0f) { + CVector playerSpeed = FindPlayerSpeed(); + if (fPreviousVecSpeedMag < 0.05f && playerSpeed.Magnitude() > 0.1f) { + playerSpeed.z = 0.0f; + playerSpeed.Normalise(); + playerSpeed.z = 0.3f; + m_vecMoveSpeed = CVector( + playerSpeed.x * BEACHBALL_SPEED_PROPORTION, + playerSpeed.y * BEACHBALL_SPEED_PROPORTION, + 0.3f * BEACHBALL_SPEED_PROPORTION + ); + PlayOneShotScriptObject(SCRIPT_SOUND_HIT_BALL, GetPosition()); + m_vecTurnSpeed += CVector( + ((CGeneral::GetRandomNumber() % 16) - 7) / 10.0f, + ((CGeneral::GetRandomNumber() % 16) - 7) / 10.0f, + 0.0f); + if (m_nBeachballBounces > 0) { + m_nBeachballBounces++; + } + if (m_nBeachballBounces > 0) { + sprintf(gString, "%d", m_nBeachballBounces); + CMoneyMessages::RegisterOne(GetPosition(), gString, 255, 50, 0, 0.6f, 0.5f); + CStats::RegisterHighestScore(3, m_nBeachballBounces); + } + } + } + if (distance.z > -1.05 && distance.z < -0.6 && m_vecMoveSpeed.z < 0.0f && distance.Magnitude2D() < 0.9f) { + m_vecMoveSpeed.x += (CGeneral::GetRandomNumber() % 8 - 3) / 100.0f; + m_vecMoveSpeed.y += (CGeneral::GetRandomNumber() % 8 - 3) / 100.0f; + m_vecMoveSpeed.z = Max(m_vecMoveSpeed.z + 0.3f, 0.2f); + PlayOneShotScriptObject(SCRIPT_SOUND_HIT_BALL, GetPosition()); + m_vecTurnSpeed.x += (CGeneral::GetRandomNumber() % 16 - 7) / 10.0f; + m_vecTurnSpeed.y += (CGeneral::GetRandomNumber() % 16 - 7) / 10.0f; + m_nBeachballBounces++; + m_nBeachballBounces = Min(m_nBeachballBounces, BEACHBALL_MAX_SCORE); + sprintf(gString, "%d", m_nBeachballBounces); + CMoneyMessages::RegisterOne(GetPosition(), gString, 255, 50, 0, 0.6f, 0.5f); + CStats::RegisterHighestScore(3, m_nBeachballBounces); + } + } + } + if (bIsBIGBuilding) { + bIsInSafePosition = true; + } +} + +void +CObject::Teleport(CVector vecPos) +{ + CWorld::Remove(this); + GetMatrix().GetPosition() = vecPos; + GetMatrix().UpdateRW(); + UpdateRwFrame(); + CWorld::Add(this); +} + +void +CObject::Render(void) +{ + if (bDoNotRender) + return; + + if (m_nRefModelIndex != -1 && ObjectCreatedBy == TEMP_OBJECT && bUseVehicleColours) { + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(m_nRefModelIndex); + assert(mi->GetModelType() == MITYPE_VEHICLE); + mi->SetVehicleColour(m_colour1, m_colour2); + } + + float red = (0.8f * CTimeCycle::GetDirectionalRed() + CTimeCycle::GetAmbientRed_Obj()) * 165.75f; + float green = (0.8f * CTimeCycle::GetDirectionalGreen() + CTimeCycle::GetAmbientGreen_Obj()) * 165.75f; + float blue = (0.8f * CTimeCycle::GetDirectionalBlue() + CTimeCycle::GetAmbientBlue_Obj()) * 165.75f; + + red = Clamp(red, 0.0f, 255.0f); + green = Clamp(green, 0.0f, 255.0f); + blue = Clamp(blue, 0.0f, 255.0f); + + int alpha = CGeneral::GetRandomNumberInRange(196, 225); + + RwRGBA color = { (uint8)red, (uint8)green, (uint8)blue, (uint8)alpha }; + + if (this->GetModelIndex() == MI_YT_MAIN_BODY) { + float moveSpeedMagnitude = this->GetMoveSpeed().Magnitude(); + if (moveSpeedMagnitude > 0.0f) { + float scaleMax = GetColModel()->boundingBox.max.y * 0.85f; + + CVector dir = this->GetMoveSpeed() + 0.3f * this->GetRight() - 0.5f * this->GetForward(); + dir.z += 0.05f * moveSpeedMagnitude; + + CVector pos = scaleMax * this->GetForward() + 2.25f * this->GetRight() + this->GetPosition(); + + float fWaterLevel; + CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); + pos.z = fWaterLevel + 0.75f; + + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 1.2f * moveSpeedMagnitude, color, + CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); + + float scaleMin = GetColModel()->boundingBox.min.y; + + dir = this->GetMoveSpeed() - 0.5f * this->GetForward(); + dir.z += 0.05f * moveSpeedMagnitude; + + pos = scaleMin * this->GetForward() + 4.5f * this->GetRight() + this->GetPosition(); + + CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); + pos.z = fWaterLevel + 0.55f; + + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 0.9f, color, + CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); + + pos = scaleMin * 1.1f * this->GetForward() + 2.25f * this->GetRight() + this->GetPosition(); + + CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); + pos.z = fWaterLevel + 0.55f; + + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 0.9f, color, + CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); + + pos = scaleMin * 1.1f * this->GetForward() - 0.05f * this->GetRight() + this->GetPosition(); + + CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); + pos.z = fWaterLevel + 0.55f; + + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 0.9f, color, + CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); + } + } + + if (this->GetModelIndex() == MI_YT_MAIN_BODY2) { + float moveSpeedMagnitude = this->GetMoveSpeed().Magnitude(); + if (moveSpeedMagnitude > 0.0f) { + float scaleMax = GetColModel()->boundingBox.max.y * 0.85f; + + CVector dir = this->GetMoveSpeed() - 0.3f * this->GetRight() - 0.5f * this->GetForward(); + dir.z += 0.05f * moveSpeedMagnitude; + + CVector pos = scaleMax * this->GetForward() - 2.25f * this->GetRight() + this->GetPosition(); + + float fWaterLevel; + CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); + pos.z = fWaterLevel + 0.75f; + + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 1.2f * moveSpeedMagnitude, color, + CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); + + float scaleMin = GetColModel()->boundingBox.min.y; + + dir = this->GetMoveSpeed() - 0.5f * this->GetForward(); + dir.z += 0.05f * moveSpeedMagnitude; + + pos = scaleMin * this->GetForward() - 4.5f * this->GetRight() + this->GetPosition(); + + CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); + pos.z = fWaterLevel + 0.55f; + + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 0.9f, color, + CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); + + pos = scaleMin * 1.1f * this->GetForward() - 2.25f * this->GetRight() + this->GetPosition(); + + CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); + pos.z = fWaterLevel + 0.55f; + + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 0.9f, color, + CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); + } + } + + CEntity::Render(); +} + +bool +CObject::SetupLighting(void) +{ + if (bRenderScorched) { + WorldReplaceNormalLightsWithScorched(Scene.world, 0.1f); + return true; + } else if (bIsPickup) { + SetFullAmbient(); + return true; + } else if (bIsWeapon) { + ActivateDirectional(); + SetAmbientColoursForPedsCarsAndObjects(); + return true; + } + return false; +} + +void +CObject::RemoveLighting(bool reset) +{ + if (reset) { + SetAmbientColours(); + DeActivateDirectional(); + } +} + +void +CObject::ObjectDamage(float amount) +{ + if (!m_nCollisionDamageEffect || !bUsesCollision) + return; + static int8 nFrameGen = 0; + bool bBodyCastDamageEffect = false; +#if 0 + if (GetModelIndex() == MI_BODYCAST) { + if (amount > 50.0f) + nBodyCastHealth = (int16)(nBodyCastHealth - 0.5f * amount); + if (nBodyCastHealth < 0) + nBodyCastHealth = 0; + if (nBodyCastHealth < 200) + bBodyCastDamageEffect = true; + amount = 0.0f; + } +#endif + if ((amount * m_fCollisionDamageMultiplier > 150.0f || bBodyCastDamageEffect) && m_nCollisionDamageEffect) { + const CVector& vecPos = GetMatrix().GetPosition(); + const float fDirectionZ = 0.0002f * amount; + switch (m_nCollisionDamageEffect) { + case DAMAGE_EFFECT_CHANGE_MODEL: + bRenderDamaged = true; + return; + case DAMAGE_EFFECT_SPLIT_MODEL: + return; + case DAMAGE_EFFECT_SMASH_AND_DAMAGE_TRAFFICLIGHTS: + { + static RwRGBA debrisColor = { 0xc8,0xc8,0xc8,0xff }; + if (bRenderDamaged) { + break; + } + bRenderDamaged = true; + CVector min = 0.85f * GetColModel()->boundingBox.min; + CVector max = 0.85f * GetColModel()->boundingBox.max; + min.z = max.z; + min = GetMatrix() * min; + max = GetMatrix() * max; + CVector temp = (max - min) * 0.02f; + for (int32 i = 0; i < 50; i++) { + CVector vecDir = CVector( + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(0.10f, 0.25f) + ); + ++nFrameGen; + int32 currentFrame = nFrameGen & 3; + CVector pos = min + temp * (float)i; + float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); + float fColorFactor = CGeneral::GetRandomNumberInRange(0.6f, 1.2f); + RwRGBA color = debrisColor; + color.red *= fColorFactor; + color.green *= fColorFactor; + int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-0.40f, 0.40f); + CParticle::AddParticle(PARTICLE_CAR_DEBRIS, pos, vecDir, nil, fSize, color, nRotationSpeed, 0, currentFrame, 0); + } + PlayOneShotScriptObject(SCRIPT_SOUND_METAL_COLLISION, min); + break; + } + case DAMAGE_EFFECT_CHANGE_THEN_SMASH: { + if (!bRenderDamaged) { + bRenderDamaged = true; + return; + } + // fall through + } + case DAMAGE_EFFECT_SMASH_COMPLETELY: { + bIsVisible = false; + bUsesCollision = false; + if (!GetIsStatic()) { + RemoveFromMovingList(); + } + SetIsStatic(true); + bExplosionProof = true; + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + break; + } + case DAMAGE_EFFECT_SMASH_CARDBOARD_COMPLETELY: + case DAMAGE_EFFECT_SMASH_YELLOW_TARGET_COMPLETELY: + { + bIsVisible = false; + bUsesCollision = false; + if (!GetIsStatic()) { + RemoveFromMovingList(); + } + SetIsStatic(true); + bExplosionProof = true; + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + const RwRGBA color = { 96, 48, 0, 255 }; + for (int32 i = 0; i < 25; i++) { + CVector vecDir = CVector( + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(0.10f, 0.25f) + fDirectionZ + ); + ++nFrameGen; + int32 currentFrame = nFrameGen & 3; + RwRGBA randomColor = color; + switch (m_nCollisionDamageEffect) { + case DAMAGE_EFFECT_SMASH_CARDBOARD_COMPLETELY: { + float fRandom = CGeneral::GetRandomNumberInRange(0.01f, 1.0f); + randomColor.red *= fRandom; + randomColor.green *= fRandom; + randomColor.blue *= fRandom; + break; + } + case DAMAGE_EFFECT_SMASH_YELLOW_TARGET_COMPLETELY: { + randomColor.red = 0xff; + randomColor.green = 0xfc; + break; + } + } + float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); + int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); + CParticle::AddParticle(PARTICLE_CAR_DEBRIS, vecPos, vecDir, nil, fSize, randomColor, nRotationSpeed, 0, currentFrame, 0); + } + PlayOneShotScriptObject(SCRIPT_SOUND_BOX_DESTROYED_2, vecPos); + break; + } + case DAMAGE_EFFECT_SMASH_WOODENBOX_COMPLETELY: + { + bIsVisible = false; + bUsesCollision = false; + if (!GetIsStatic()) { + RemoveFromMovingList(); + } + SetIsStatic(true); + bExplosionProof = true; + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + static const RwRGBA color = { 128, 128, 128, 255 }; + CVector position = GetPosition(); + for (int32 i = 0; i < 45; i++) { + CVector vecDir = CVector( + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(0.10f, 0.25f) + fDirectionZ + ); + ++nFrameGen; + int32 currentFrame = nFrameGen & 3; + float fRandom = CGeneral::GetRandomNumberInRange(0.5f, 1.0f); + RwRGBA randomColor = { uint8(color.red * fRandom), uint8(color.green * fRandom), uint8(color.blue * fRandom), color.alpha }; + float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); + int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); + CParticle::AddParticle(PARTICLE_CAR_DEBRIS, vecPos, vecDir, nil, fSize, randomColor, nRotationSpeed, 0, currentFrame, 0); + } + PlayOneShotScriptObject(SCRIPT_SOUND_BOX_DESTROYED_1, vecPos); + break; + } + case DAMAGE_EFFECT_SMASH_TRAFFICCONE_COMPLETELY: + case DAMAGE_EFFECT_BURST_BEACHBALL: + { + bIsVisible = false; + bUsesCollision = false; + if (!GetIsStatic()) { + RemoveFromMovingList(); + } + SetIsStatic(true); + bExplosionProof = true; + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + const RwRGBA color1 = { 200, 0, 0, 255 }; + const RwRGBA color2 = { 200, 200, 200, 255 }; + for (int32 i = 0; i < 10; i++) { + CVector vecDir = CVector( + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(0.10f, 0.25f) + fDirectionZ + ); + ++nFrameGen; + int32 currentFrame = nFrameGen & 3; + RwRGBA color = color2; + if (nFrameGen & 1) + color = color1; + float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); + int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); + CParticle::AddParticle(PARTICLE_CAR_DEBRIS, vecPos, vecDir, nil, fSize, color, nRotationSpeed, 0, currentFrame, 0); + } + if (m_nCollisionDamageEffect == DAMAGE_EFFECT_BURST_BEACHBALL) { + PlayOneShotScriptObject(SCRIPT_SOUND_HIT_BALL, vecPos); + } else { + PlayOneShotScriptObject(SCRIPT_SOUND_TIRE_COLLISION, vecPos); + } + break; + } + case DAMAGE_EFFECT_SMASH_BARPOST_COMPLETELY: + { + bIsVisible = false; + bUsesCollision = false; + if (!GetIsStatic()) { + RemoveFromMovingList(); + } + SetIsStatic(true); + bExplosionProof = true; + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + const RwRGBA color1 = { 200, 0, 0, 255 }; + const RwRGBA color2 = { 200, 200, 200, 255 }; + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + for (int32 i = 0; i < 32; i++) { + CVector vecDir = CVector( + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(0.10f, 0.25f) + fDirectionZ + ); + ++nFrameGen; + int32 currentFrame = nFrameGen & 3; + const RwRGBA &color = nFrameGen & 1 ? color1 : color2; + float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); + int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); + CParticle::AddParticle(PARTICLE_CAR_DEBRIS, vecPos, vecDir, nil, fSize, color, nRotationSpeed, 0, currentFrame, 0); + } + PlayOneShotScriptObject(SCRIPT_SOUND_METAL_COLLISION, vecPos); + break; + } + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW1: + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW2: + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW3: + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW4: + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW5: + { + bIsVisible = false; + bUsesCollision = false; + if (!GetIsStatic()) { + RemoveFromMovingList(); + } + SetIsStatic(true); + bExplosionProof = true; + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + CRGBA possibleColor1; + CRGBA possibleColor2; + switch (m_nCollisionDamageEffect) { + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW1: + possibleColor1 = CRGBA(0xC0, 0x3E, 0xC, 0xFF); + possibleColor2 = possibleColor1; + break; + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW2: + possibleColor1 = CRGBA(0xA3, 0x36, 0x21, 0xFF); + possibleColor2 = possibleColor1; + break; + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW3: + possibleColor1 = CRGBA(0x12, 0x31, 0x24, 0xFF); + possibleColor2 = possibleColor1; + break; + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW4: + possibleColor1 = CRGBA(0xC0, 0xC8, 0xBE, 0xFF); + possibleColor2 = CRGBA(0x10, 0x57, 0x85, 0xFF); + break; + case DAMAGE_EFFECT_SMASH_NEWSTANDNEW5: + possibleColor1 = CRGBA(0xD0, 0x94, 0x1B, 0xFF); + possibleColor2 = possibleColor1; + break; + } + for (int32 i = 0; i < 16; i++) { + CVector vecDir( + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(0.10f, 0.15f) + fDirectionZ + ); + float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); + int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); + ++nFrameGen; + int32 nCurFrame = nFrameGen & 0x3; + CRGBA &selectedColor = nFrameGen & 0x1 ? possibleColor1 : possibleColor2; + CParticle::AddParticle(PARTICLE_CAR_DEBRIS, vecPos, vecDir, nil, fSize, selectedColor, nRotationSpeed, 0, nCurFrame, 0); + if (!(i % 7)) { + static CRGBA secondParticleColors[4] = { + CRGBA(0xA0, 0x60, 0x60, 0xFF), + CRGBA(0x60, 0xA0, 0x60, 0xFF), + CRGBA(0x60, 0x60, 0xA0, 0xFF), + CRGBA(0xA0, 0xA0, 0xA0, 0xFF) + }; + vecDir *= 0.5f; + CRGBA &secondParticleColor = secondParticleColors[nFrameGen & 3]; + int32 nSecondRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); + CParticle::AddParticle(PARTICLE_DEBRIS, vecPos, vecDir, nil, 0.1f, secondParticleColor, nSecondRotationSpeed, 0, 1, 0); + } + } + PlayOneShotScriptObject(SCRIPT_SOUND_METAL_COLLISION, vecPos); + break; + } + case DAMAGE_EFFECT_SMASH_VEGPALM: + { + static RwRGBA primaryColor1 = { 0x39, 0x4D, 0x29, 0xff }; + static RwRGBA primaryColor2 = { 0x94, 0x7D, 0x73, 0xff }; + bIsVisible = false; + bUsesCollision = false; + if (!GetIsStatic()) { + RemoveFromMovingList(); + } + SetIsStatic(true); + bExplosionProof = true; + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + float fRadius = GetColModel()->boundingSphere.radius; + for (int32 i = 0; i < 32; i++) { + CVector particleDir = CVector( + CGeneral::GetRandomNumberInRange(-0.25f, 0.25f), + CGeneral::GetRandomNumberInRange(-0.25f, 0.25f), + CGeneral::GetRandomNumberInRange(-0.05f, 0.05f) + fDirectionZ + ); + CVector particlePos = vecPos; + particlePos.z += CGeneral::GetRandomNumberInRange(0.0f, 1.0f) * fRadius; + ++nFrameGen; + int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); + int32 nCurFrame = nFrameGen & 0x3; + float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); + RwRGBA& particleColor = nFrameGen & 1 ? primaryColor1 : primaryColor2; + CParticle::AddParticle(PARTICLE_CAR_DEBRIS, particlePos, particleDir, nil, fSize, particleColor, nRotationSpeed, 0, nCurFrame, 0); + if ((i % 7) == 0) { + static RwRGBA secondaryColor = { 0x9A, 0x99, 0x99, 0x3E }; + CParticle::AddParticle(PARTICLE_DEBRIS, particlePos, particleDir, nil, 0.3f, secondaryColor, nRotationSpeed); + } + } + PlayOneShotScriptObject(SCRIPT_SOUND_BOX_DESTROYED_2, vecPos); + break; + } + case DAMAGE_EFFECT_SMASH_BLACKBAG: + case DAMAGE_EFFECT_SMASH_BEACHLOUNGE_WOOD: + case DAMAGE_EFFECT_SMASH_BEACHLOUNGE_TOWEL: + { + bIsVisible = false; + bUsesCollision = false; + if (!GetIsStatic()) { + RemoveFromMovingList(); + } + SetIsStatic(true); + bExplosionProof = true; + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + CRGBA possibleColor1; + CRGBA possibleColor2; + switch (m_nCollisionDamageEffect) { + case DAMAGE_EFFECT_SMASH_BLACKBAG: + possibleColor1 = CRGBA(0, 0, 0, 0xFF); + possibleColor2 = possibleColor1; + break; + case DAMAGE_EFFECT_SMASH_BEACHLOUNGE_WOOD: + possibleColor1 = CRGBA(0x8F, 0x8A, 0x8C, 0xFF); + possibleColor2 = CRGBA(0x73, 0x75, 0x7B, 0xFF); + break; + case DAMAGE_EFFECT_SMASH_BEACHLOUNGE_TOWEL: + possibleColor1 = CRGBA(0x52, 0x92, 0x4A, 0xFF); + possibleColor2 = CRGBA(0xCE, 0xCF, 0xCE, 0xFF); + break; + } + for (int32 i = 0; i < 16; i++) { + CVector vecDir( + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), + CGeneral::GetRandomNumberInRange(0.10f, 0.25f) + fDirectionZ + ); + ++nFrameGen; + int32 nCurFrame = nFrameGen & 0x3; + CRGBA &selectedColor = nFrameGen & 0x1 ? possibleColor1 : possibleColor2; + float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); + int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); + CParticle::AddParticle(PARTICLE_CAR_DEBRIS, vecPos, vecDir, nil, fSize, selectedColor, nRotationSpeed, 0, nCurFrame, 0); + } + if (m_nCollisionDamageEffect == DAMAGE_EFFECT_SMASH_BLACKBAG) { + PlayOneShotScriptObject(SCRIPT_SOUND_BOX_DESTROYED_2, vecPos); + } else if (m_nCollisionDamageEffect == DAMAGE_EFFECT_SMASH_BEACHLOUNGE_WOOD) { + PlayOneShotScriptObject(SCRIPT_SOUND_METAL_COLLISION, vecPos); + } + break; + } + default: + DEV("Unhandled collision damage effect id: %d\n", m_nCollisionDamageEffect); + return; + } + } +} + +void +CObject::RefModelInfo(int32 modelId) +{ + m_nRefModelIndex = modelId; + CModelInfo::GetModelInfo(modelId)->AddRef(); +} + +void +CObject::Init(void) +{ + m_type = ENTITY_TYPE_OBJECT; + CObjectData::SetObjectData(GetModelIndex(), *this); + m_nEndOfLifeTime = 0; + ObjectCreatedBy = GAME_OBJECT; + SetIsStatic(true); + bIsPickup = false; + bPickupObjWithMessage = false; + bOutOfStock = false; + bGlassCracked = false; + bGlassBroken = false; + bHasBeenDamaged = false; + bUseVehicleColours = false; + m_nRefModelIndex = -1; + m_colour1 = 0; + m_colour2 = 0; + m_nBonusValue = 0; + bIsWeapon = false; + m_nCostValue = 0; + m_pCollidingEntity = nil; + CColPoint point; + CEntity *outEntity = nil; + const CVector& vecPos = GetMatrix().GetPosition(); + if (CWorld::ProcessVerticalLine(vecPos, vecPos.z - 10.0f, point, outEntity, true, false, false, false, false, false, nil)) + m_pCurSurface = outEntity; + else + m_pCurSurface = nil; + + if (GetModelIndex() == MI_BUOY) + bTouchingWater = true; + + if (CModelInfo::GetModelInfo(GetModelIndex())->GetModelType() == MITYPE_WEAPON) + bIsWeapon = true; + bIsStreetLight = IsLightObject(GetModelIndex()); + + m_area = AREA_EVERYWHERE; +} + +bool +CObject::CanBeDeleted(void) +{ + switch (ObjectCreatedBy) { + case GAME_OBJECT: + return true; + case MISSION_OBJECT: + return false; + case TEMP_OBJECT: + return true; + case CUTSCENE_OBJECT: + return false; + case CONTROLLED_SUB_OBJECT: + return false; + default: + return true; + } +} + +void +CObject::DeleteAllMissionObjects() +{ + CObjectPool *objectPool = CPools::GetObjectPool(); + for (int32 i = 0; i < objectPool->GetSize(); i++) { + CObject *pObject = objectPool->GetSlot(i); + if (pObject && pObject->ObjectCreatedBy == MISSION_OBJECT) { + CWorld::Remove(pObject); + delete pObject; + } + } +} + +void +CObject::DeleteAllTempObjects() +{ + CObjectPool *objectPool = CPools::GetObjectPool(); + for (int32 i = 0; i < objectPool->GetSize(); i++) { + CObject *pObject = objectPool->GetSlot(i); + if (pObject && pObject->ObjectCreatedBy == TEMP_OBJECT) { + CWorld::Remove(pObject); + delete pObject; + } + } +} + +void +CObject::DeleteAllTempObjectsInArea(CVector point, float fRadius) +{ + CObjectPool *objectPool = CPools::GetObjectPool(); + for (int32 i = 0; i < objectPool->GetSize(); i++) { + CObject *pObject = objectPool->GetSlot(i); + if (pObject && pObject->ObjectCreatedBy == TEMP_OBJECT && (point - pObject->GetPosition()).MagnitudeSqr() < SQR(fRadius)) { + CWorld::Remove(pObject); + delete pObject; + } + } +} + +bool +IsObjectPointerValid(CObject *pObject) +{ + if (!pObject) + return false; + int index = CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(pObject); +#ifdef FIX_BUGS + if (index < 0 || index >= CPools::GetObjectPool()->GetSize()) +#else + if (index < 0 || index > CPools::GetObjectPool()->GetSize()) +#endif + return false; + return pObject->bIsBIGBuilding || pObject->m_entryInfoList.first; +} diff --git a/src/miami/objects/Object.h b/src/miami/objects/Object.h new file mode 100644 index 00000000..f59379bf --- /dev/null +++ b/src/miami/objects/Object.h @@ -0,0 +1,116 @@ +#pragma once + +#include "Physical.h" + +enum { + UNKNOWN_OBJECT = 0, + GAME_OBJECT = 1, + MISSION_OBJECT = 2, + TEMP_OBJECT = 3, + CUTSCENE_OBJECT = 4, + CONTROLLED_SUB_OBJECT = 5, +}; + +enum CollisionSpecialResponseCase +{ + COLLRESPONSE_NONE, + COLLRESPONSE_LAMPOST, + COLLRESPONSE_SMALLBOX, + COLLRESPONSE_BIGBOX, + COLLRESPONSE_FENCEPART, + COLLRESPONSE_UNKNOWN5 +}; + +enum CollisionDamageEffect +{ + DAMAGE_EFFECT_NONE, + DAMAGE_EFFECT_CHANGE_MODEL, + DAMAGE_EFFECT_SPLIT_MODEL, + DAMAGE_EFFECT_SMASH_AND_DAMAGE_TRAFFICLIGHTS, + + DAMAGE_EFFECT_SMASH_COMPLETELY = 20, + DAMAGE_EFFECT_CHANGE_THEN_SMASH, + + DAMAGE_EFFECT_SMASH_CARDBOARD_COMPLETELY = 50, + DAMAGE_EFFECT_SMASH_YELLOW_TARGET_COMPLETELY = 51, + + DAMAGE_EFFECT_SMASH_WOODENBOX_COMPLETELY = 60, + DAMAGE_EFFECT_SMASH_TRAFFICCONE_COMPLETELY = 70, + DAMAGE_EFFECT_SMASH_BARPOST_COMPLETELY = 80, + + DAMAGE_EFFECT_SMASH_NEWSTANDNEW1 = 91, + DAMAGE_EFFECT_SMASH_NEWSTANDNEW2 = 92, + DAMAGE_EFFECT_SMASH_NEWSTANDNEW3 = 93, + DAMAGE_EFFECT_SMASH_NEWSTANDNEW4 = 94, + DAMAGE_EFFECT_SMASH_NEWSTANDNEW5 = 95, + + DAMAGE_EFFECT_SMASH_BLACKBAG = 100, + DAMAGE_EFFECT_SMASH_VEGPALM = 110, + DAMAGE_EFFECT_BURST_BEACHBALL = 120, + DAMAGE_EFFECT_SMASH_BEACHLOUNGE_WOOD = 131, + DAMAGE_EFFECT_SMASH_BEACHLOUNGE_TOWEL = 132, +}; + +class CVehicle; +class CDummyObject; + +class CObject : public CPhysical +{ +public: + CMatrix m_objectMatrix; + float m_fUprootLimit; + int8 ObjectCreatedBy; + uint8 bIsPickup : 1; + uint8 bAmmoCollected : 1; + uint8 bPickupObjWithMessage : 1; + uint8 bOutOfStock : 1; + uint8 bGlassCracked : 1; + uint8 bGlassBroken : 1; + uint8 bHasBeenDamaged : 1; + uint8 bUseVehicleColours : 1; + uint8 bIsWeapon : 1; + uint8 bIsStreetLight : 1; + int8 m_nBonusValue; + uint16 m_nCostValue; + float m_fCollisionDamageMultiplier; + uint8 m_nCollisionDamageEffect; + uint8 m_nSpecialCollisionResponseCases; + bool m_bCameraToAvoidThisObject; + uint8 m_nBeachballBounces; + uint32 m_obj_unused1; + uint32 m_nEndOfLifeTime; + int16 m_nRefModelIndex; + CEntity *m_pCurSurface; + CEntity *m_pCollidingEntity; + int8 m_colour1, m_colour2; + + static int16 nNoTempObjects; + static float fDistToNearestTree; + + static void *operator new(size_t) throw(); + static void *operator new(size_t, int) throw(); + static void operator delete(void*, size_t) throw(); + static void operator delete(void*, int) throw(); + + CObject(void); + CObject(int32, bool); + CObject(CDummyObject*); + ~CObject(void); + + void ProcessControl(void); + void Teleport(CVector vecPos); + void Render(void); + bool SetupLighting(void); + void RemoveLighting(bool reset); + + void ObjectDamage(float amount); + void RefModelInfo(int32 modelId); + void Init(void); + bool CanBeDeleted(void); + + static void DeleteAllMissionObjects(); + static void DeleteAllTempObjects(); + static void DeleteAllTempObjectsInArea(CVector point, float fRadius); +}; + +bool IsObjectPointerValid(CObject* pObject); diff --git a/src/miami/objects/ObjectData.cpp b/src/miami/objects/ObjectData.cpp new file mode 100644 index 00000000..8b23f0ae --- /dev/null +++ b/src/miami/objects/ObjectData.cpp @@ -0,0 +1,161 @@ +#include "common.h" + +#include "main.h" +#include "ModelInfo.h" +#include "Object.h" +#include "FileMgr.h" +#include "ObjectData.h" + +CObjectInfo CObjectData::ms_aObjectInfo[NUMOBJECTINFO]; + +// Another ugly file reader +void +CObjectData::Initialise(const char *filename) +{ + char *p, *lp; + char line[1024], name[256]; + int id; + float percentSubmerged; + int damageEffect, responseCase, camAvoid; + CBaseModelInfo *mi; + + ms_aObjectInfo[0].m_fMass = 99999.0f; + ms_aObjectInfo[0].m_fTurnMass = 99999.0f; + ms_aObjectInfo[0].m_fAirResistance = 0.99f; + ms_aObjectInfo[0].m_fElasticity = 0.1f; + ms_aObjectInfo[0].m_fBuoyancy = GRAVITY * ms_aObjectInfo[0].m_fMass * 2.0f; + ms_aObjectInfo[0].m_fUprootLimit = 0.0f; + ms_aObjectInfo[0].m_fCollisionDamageMultiplier = 1.0f; + ms_aObjectInfo[0].m_nCollisionDamageEffect = 0; + ms_aObjectInfo[0].m_nSpecialCollisionResponseCases = 0; + ms_aObjectInfo[0].m_bCameraToAvoidThisObject = false; + + ms_aObjectInfo[1].m_fMass = 99999.0f; + ms_aObjectInfo[1].m_fTurnMass = 99999.0f; + ms_aObjectInfo[1].m_fAirResistance = 0.99f; + ms_aObjectInfo[1].m_fElasticity = 0.1f; + ms_aObjectInfo[1].m_fBuoyancy = ms_aObjectInfo[0].m_fBuoyancy; + ms_aObjectInfo[1].m_fUprootLimit = 0.0f; + ms_aObjectInfo[1].m_fCollisionDamageMultiplier = 1.0f; + ms_aObjectInfo[1].m_nCollisionDamageEffect = 0; + ms_aObjectInfo[1].m_nSpecialCollisionResponseCases = 0; + ms_aObjectInfo[1].m_bCameraToAvoidThisObject = true; + + ms_aObjectInfo[2].m_fMass = 99999.0f; + ms_aObjectInfo[2].m_fTurnMass = 99999.0f; + ms_aObjectInfo[2].m_fAirResistance = 0.99f; + ms_aObjectInfo[2].m_fElasticity = 0.1f; + ms_aObjectInfo[2].m_fBuoyancy = ms_aObjectInfo[0].m_fBuoyancy; + ms_aObjectInfo[2].m_fUprootLimit = 0.0f; + ms_aObjectInfo[2].m_fCollisionDamageMultiplier = 1.0f; + ms_aObjectInfo[2].m_nCollisionDamageEffect = 0; + ms_aObjectInfo[2].m_bCameraToAvoidThisObject = false; + ms_aObjectInfo[2].m_nSpecialCollisionResponseCases = 4; + + ms_aObjectInfo[3].m_fMass = 99999.0f; + ms_aObjectInfo[3].m_fTurnMass = 99999.0f; + ms_aObjectInfo[3].m_fAirResistance = 0.99f; + ms_aObjectInfo[3].m_fElasticity = 0.1f; + ms_aObjectInfo[3].m_fBuoyancy = ms_aObjectInfo[0].m_fBuoyancy; + ms_aObjectInfo[3].m_fUprootLimit = 0.0f; + ms_aObjectInfo[3].m_fCollisionDamageMultiplier = 1.0f; + ms_aObjectInfo[3].m_nCollisionDamageEffect = 0; + ms_aObjectInfo[3].m_nSpecialCollisionResponseCases = 4; + ms_aObjectInfo[3].m_bCameraToAvoidThisObject = true; + + CFileMgr::SetDir(""); + CFileMgr::LoadFile(filename, work_buff, sizeof(work_buff), "r"); + + id = 4; + p = (char*)work_buff; + while(*p != '*'){ + // skip over white space and comments + while(*p == ' ' || *p == '\n' || *p == '\r' || *p == ';') + if(*p == ';') + while(*p != '\n' && *p != '*') + p++; + else + p++; + + if(*p == '*') + break; + + // read one line + lp = line; + while(*p != '\n' && *p != '*'){ + *lp++ = *p == ',' ? ' ' : *p; + p++; + } + if(*p == '\n') + p++; +#ifdef FIX_BUGS + *lp = '\0'; // FIX: game wrote '\n' here +#else + *lp = '\n'; +#endif + + assert(id < NUMOBJECTINFO); + sscanf(line, "%s %f %f %f %f %f %f %f %d %d %d", name, + &ms_aObjectInfo[id].m_fMass, + &ms_aObjectInfo[id].m_fTurnMass, + &ms_aObjectInfo[id].m_fAirResistance, + &ms_aObjectInfo[id].m_fElasticity, + &percentSubmerged, + &ms_aObjectInfo[id].m_fUprootLimit, + &ms_aObjectInfo[id].m_fCollisionDamageMultiplier, + &damageEffect, &responseCase, &camAvoid); + + ms_aObjectInfo[id].m_fBuoyancy = 100.0f/percentSubmerged * GRAVITY *ms_aObjectInfo[id].m_fMass; + ms_aObjectInfo[id].m_nCollisionDamageEffect = damageEffect; + ms_aObjectInfo[id].m_nSpecialCollisionResponseCases = responseCase; + ms_aObjectInfo[id].m_bCameraToAvoidThisObject = camAvoid; + + mi = CModelInfo::GetModelInfo(name, nil); + if (mi) { + if (ms_aObjectInfo[0].m_fMass != ms_aObjectInfo[id].m_fMass + || ms_aObjectInfo[0].m_fCollisionDamageMultiplier != ms_aObjectInfo[id].m_fCollisionDamageMultiplier + || ms_aObjectInfo[0].m_nCollisionDamageEffect != ms_aObjectInfo[id].m_nCollisionDamageEffect + || ((ms_aObjectInfo[0].m_nSpecialCollisionResponseCases != ms_aObjectInfo[id].m_nSpecialCollisionResponseCases) + && (ms_aObjectInfo[2].m_nSpecialCollisionResponseCases != ms_aObjectInfo[id].m_nSpecialCollisionResponseCases))) { + mi->SetObjectID(id++); + } else if (ms_aObjectInfo[0].m_nSpecialCollisionResponseCases == ms_aObjectInfo[id].m_nSpecialCollisionResponseCases) { + if (ms_aObjectInfo[0].m_bCameraToAvoidThisObject == ms_aObjectInfo[id].m_bCameraToAvoidThisObject) + mi->SetObjectID(0); + else + mi->SetObjectID(1); + } else if (ms_aObjectInfo[2].m_bCameraToAvoidThisObject == ms_aObjectInfo[id].m_bCameraToAvoidThisObject) + mi->SetObjectID(2); + else + mi->SetObjectID(3); + } else + debug("CObjectData: Cannot find object %s\n", name); + } +} + +void +CObjectData::SetObjectData(int32 modelId, CObject &object) +{ + CObjectInfo *objinfo; + + if(CModelInfo::GetModelInfo(modelId)->GetObjectID() == -1) + return; + + objinfo = &ms_aObjectInfo[CModelInfo::GetModelInfo(modelId)->GetObjectID()]; + + object.m_fMass = objinfo->m_fMass; + object.m_fTurnMass = objinfo->m_fTurnMass; + object.m_fAirResistance = objinfo->m_fAirResistance; + object.m_fElasticity = objinfo->m_fElasticity; + object.m_fBuoyancy = objinfo->m_fBuoyancy; + object.m_fUprootLimit = objinfo->m_fUprootLimit; + object.m_fCollisionDamageMultiplier = objinfo->m_fCollisionDamageMultiplier; + object.m_nCollisionDamageEffect = objinfo->m_nCollisionDamageEffect; + object.m_nSpecialCollisionResponseCases = objinfo->m_nSpecialCollisionResponseCases; + object.m_bCameraToAvoidThisObject = objinfo->m_bCameraToAvoidThisObject; + if(object.m_fMass >= 99998.0f){ + object.bInfiniteMass = true; + object.m_phy_flagA08 = true; + object.bAffectedByGravity = false; + object.bExplosionProof = true; + } +} diff --git a/src/miami/objects/ObjectData.h b/src/miami/objects/ObjectData.h new file mode 100644 index 00000000..e25c1aeb --- /dev/null +++ b/src/miami/objects/ObjectData.h @@ -0,0 +1,27 @@ +#pragma once + +class CObject; + +class CObjectInfo +{ +public: + float m_fMass; + float m_fTurnMass; + float m_fAirResistance; + float m_fElasticity; + float m_fBuoyancy; + float m_fUprootLimit; + float m_fCollisionDamageMultiplier; + uint8 m_nCollisionDamageEffect; + uint8 m_nSpecialCollisionResponseCases; + bool m_bCameraToAvoidThisObject; +}; +VALIDATE_SIZE(CObjectInfo, 0x20); + +class CObjectData +{ + static CObjectInfo ms_aObjectInfo[NUMOBJECTINFO]; +public: + static void Initialise(const char *filename); + static void SetObjectData(int32 modelId, CObject &object); +}; diff --git a/src/miami/objects/ParticleObject.cpp b/src/miami/objects/ParticleObject.cpp new file mode 100644 index 00000000..28c5240f --- /dev/null +++ b/src/miami/objects/ParticleObject.cpp @@ -0,0 +1,1345 @@ +#include "common.h" + +#include "ParticleObject.h" +#include "Timer.h" +#include "General.h" +#include "ParticleMgr.h" +#include "Particle.h" +#include "Camera.h" +#include "Game.h" +#include "DMAudio.h" +#include "screendroplets.h" + +#ifdef COMPATIBLE_SAVES +#define PARTICLE_OBJECT_SIZEOF 0x84 +#else +#define PARTICLE_OBJECT_SIZEOF sizeof(CParticleObject) +#endif + + +CParticleObject gPObjectArray[MAX_PARTICLEOBJECTS]; + +CParticleObject *CParticleObject::pCloseListHead; +CParticleObject *CParticleObject::pFarListHead; +CParticleObject *CParticleObject::pUnusedListHead; + +CAudioHydrant List[MAX_AUDIOHYDRANTS]; + +CAudioHydrant *CAudioHydrant::Get(int n) { return &List[n]; } + +bool +CAudioHydrant::Add(CParticleObject *particleobject) +{ + for ( int32 i = 0; i < MAX_AUDIOHYDRANTS; i++ ) + { + if ( List[i].AudioEntity == AEHANDLE_NONE ) + { + List[i].AudioEntity = DMAudio.CreateEntity(AUDIOTYPE_FIREHYDRANT, particleobject); + + if ( AEHANDLE_IS_FAILED(List[i].AudioEntity) ) + return false; + + DMAudio.SetEntityStatus(List[i].AudioEntity, TRUE); + + List[i].pParticleObject = particleobject; + + return true; + } + } + + return false; +} + +void +CAudioHydrant::Remove(CParticleObject *particleobject) +{ + for ( int32 i = 0; i < MAX_AUDIOHYDRANTS; i++ ) + { + if ( List[i].pParticleObject == particleobject ) + { + DMAudio.DestroyEntity(List[i].AudioEntity); + List[i].AudioEntity = AEHANDLE_NONE; + List[i].pParticleObject = nil; + } + } +} + +CParticleObject::CParticleObject() : + CPlaceable(), + m_nFrameCounter(0), + m_nState(POBJECTSTATE_INITIALISED), + m_pNext(nil), + m_pPrev(nil), + m_nRemoveTimer(0) + +{ + ; +} + +CParticleObject::~CParticleObject() +{ + +} + +void +CParticleObject::Initialise() +{ + pCloseListHead = nil; + pFarListHead = nil; + + pUnusedListHead = &gPObjectArray[0]; + + for ( int32 i = 0; i < MAX_PARTICLEOBJECTS; i++ ) + { + if ( i == 0 ) + gPObjectArray[i].m_pPrev = nil; + else + gPObjectArray[i].m_pPrev = &gPObjectArray[i - 1]; + + if ( i == MAX_PARTICLEOBJECTS-1 ) + gPObjectArray[i].m_pNext = nil; + else + gPObjectArray[i].m_pNext = &gPObjectArray[i + 1]; + + gPObjectArray[i].m_nState = POBJECTSTATE_FREE; + } +} + +CParticleObject * +CParticleObject::AddObject(uint16 type, CVector const &pos, uint8 remove) +{ + CRGBA color(0, 0, 0, 0); + CVector target(0.0f, 0.0f, 0.0f); + return AddObject(type, pos, target, 0.0f, 0, color, remove); +} + +CParticleObject * +CParticleObject::AddObject(uint16 type, CVector const &pos, float size, uint8 remove) +{ + CRGBA color(0, 0, 0, 0); + CVector target(0.0f, 0.0f, 0.0f); + return AddObject(type, pos, target, size, 0, color, remove); +} + +CParticleObject * +CParticleObject::AddObject(uint16 type, CVector const &pos, CVector const &target, float size, uint8 remove) +{ + CRGBA color(0, 0, 0, 0); + return AddObject(type, pos, target, size, 0, color, remove); +} + +CParticleObject * +CParticleObject::AddObject(uint16 type, CVector const &pos, CVector const &target, float size, uint32 lifeTime, RwRGBA const &color, uint8 remove) +{ + CParticleObject *pobj = pUnusedListHead; + + if ( pobj == nil ) + { + printf("Error: No particle objects available!\n"); + return nil; + } + + MoveToList(&pUnusedListHead, &pCloseListHead, pobj); + + pobj->m_nState = POBJECTSTATE_UPDATE_CLOSE; + pobj->m_Type = (eParticleObjectType)type; + + pobj->SetPosition(pos); + pobj->m_vecTarget = target; + + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 0; + pobj->m_nFrameCounter = 0; + + pobj->m_bRemove = remove; + + pobj->m_pParticle = nil; + + if ( lifeTime != 0 ) + pobj->m_nRemoveTimer = CTimer::GetTimeInMilliseconds() + lifeTime; + else + pobj->m_nRemoveTimer = 0; + + if ( color.alpha != 0 ) + pobj->m_Color = color; + else + pobj->m_Color.alpha = 0; + + pobj->m_fSize = size; + pobj->m_fRandVal = 0.0f; + + switch ( type ) + { + case POBJECT_PAVEMENT_STEAM: + { + pobj->m_ParticleType = PARTICLE_STEAM_NY; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 3; + pobj->m_nCreationChance = 8; + break; + } + + case POBJECT_PAVEMENT_STEAM_SLOWMOTION: + { + pobj->m_ParticleType = PARTICLE_STEAM_NY_SLOWMOTION; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 8; + break; + } + + case POBJECT_WALL_STEAM: + { + pobj->m_ParticleType = PARTICLE_STEAM_NY; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 3; + pobj->m_nCreationChance = 8; + break; + } + + case POBJECT_WALL_STEAM_SLOWMOTION: + { + pobj->m_ParticleType = PARTICLE_STEAM_NY_SLOWMOTION; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 8; + break; + } + + case POBJECT_DARK_SMOKE: + { + pobj->m_ParticleType = PARTICLE_STEAM_NY; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 3; + pobj->m_nCreationChance = 8; + pobj->m_Color = CRGBA(16, 16, 16, 255); + break; + } + + case POBJECT_WATER_FOUNTAIN_VERT: + { + pobj->m_ParticleType = PARTICLE_WATER_HYDRANT; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 0; + pobj->m_vecTarget = CVector(0.0f, 0.0f, 0.1f); + break; + } + + case POBJECT_WATER_FOUNTAIN_HORIZ: + { + pobj->m_ParticleType = PARTICLE_WATER_HYDRANT; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 0; + break; + } + + case POBJECT_FIRE_HYDRANT: + { + pobj->m_ParticleType = PARTICLE_WATER_HYDRANT; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 0; + pobj->m_vecTarget = CVector(0.0f, 0.0f, 0.3f); + pobj->m_nRemoveTimer = CTimer::GetTimeInMilliseconds() + 5000; + CAudioHydrant::Add(pobj); + break; + } + + case POBJECT_CAR_WATER_SPLASH: + case POBJECT_PED_WATER_SPLASH: + { + pobj->m_ParticleType = PARTICLE_CAR_SPLASH; + pobj->m_nNumEffectCycles = 0; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 0; +#ifdef SCREEN_DROPLETS + ScreenDroplets::RegisterSplash(pobj); +#endif + break; + } + + case POBJECT_SPLASHES_AROUND: + { + pobj->m_ParticleType = PARTICLE_SPLASH; + pobj->m_nNumEffectCycles = 15; + pobj->m_nSkipFrames = 2; + pobj->m_nCreationChance = 0; + break; + } + + case POBJECT_SMALL_FIRE: + { + pobj->m_ParticleType = PARTICLE_FLAME; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 2; + pobj->m_nCreationChance = 2; + pobj->m_vecTarget = CVector(0.0f, 0.0f, 0.0f); + break; + } + + case POBJECT_BIG_FIRE: + { + pobj->m_ParticleType = PARTICLE_FLAME; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 2; + pobj->m_nCreationChance = 4; + pobj->m_vecTarget = CVector(0.0f, 0.0f, 0.0f); + break; + } + + case POBJECT_DRY_ICE: + { + pobj->m_ParticleType = PARTICLE_SMOKE; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 0; + pobj->m_vecTarget = CVector(0.0f, 0.0f, 0.0f); + break; + } + + case POBJECT_DRY_ICE_SLOWMOTION: + { + pobj->m_ParticleType = PARTICLE_SMOKE_SLOWMOTION; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 0; + pobj->m_vecTarget = CVector(0.0f, 0.0f, 0.0f); + break; + } + + case POBJECT_FIRE_TRAIL: + { + pobj->m_ParticleType = PARTICLE_EXPLOSION_MEDIUM; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 3; + pobj->m_nCreationChance = 2; + pobj->m_fRandVal = 0.01f; + break; + } + + case POBJECT_SMOKE_TRAIL: + { + pobj->m_ParticleType = PARTICLE_FIREBALL_SMOKE; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 2; + pobj->m_fRandVal = 0.02f; + break; + } + + case POBJECT_FIREBALL_AND_SMOKE: + { + pobj->m_ParticleType = PARTICLE_FLAME; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 0; + pobj->m_fRandVal = 0.1f; + break; + } + + case POBJECT_ROCKET_TRAIL: + { + pobj->m_ParticleType = PARTICLE_FLAME; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 2; + pobj->m_nCreationChance = 8; + pobj->m_fRandVal = 0.1f; + break; + } + + case POBJECT_EXPLOSION_ONCE: + { + pobj->m_ParticleType = PARTICLE_EXPLOSION_LARGE; + pobj->m_nNumEffectCycles = 1; + pobj->m_nSkipFrames = 1; + pobj->m_nCreationChance = 0; + pobj->m_nRemoveTimer = CTimer::GetTimeInMilliseconds(); + break; + } + } + + return pobj; +} + +CParticleObject * +CParticleObject::AddObject(tParticleType type, CVector const &pos, CVector const &target, float size, uint32 lifeTime, uint8 numEffectCycles, uint8 skipFrames, uint16 creationChance, uint8 remove) +{ + CParticleObject *pobj = pUnusedListHead; + + ASSERT(pobj != nil); + + if ( pobj == nil ) + { + printf("Error: No particle objects available!\n"); + return nil; + } + + MoveToList(&pUnusedListHead, &pCloseListHead, pobj); + + pobj->m_nState = POBJECTSTATE_UPDATE_CLOSE; + pobj->m_Type = (eParticleObjectType)-1; + pobj->m_ParticleType = type; + + pobj->SetPosition(pos); + pobj->m_vecTarget = target; + + pobj->m_nNumEffectCycles = numEffectCycles; + pobj->m_nSkipFrames = skipFrames; + pobj->m_nCreationChance = creationChance; + pobj->m_nFrameCounter = 0; + + pobj->m_bRemove = remove; + + if ( lifeTime != 0 ) + pobj->m_nRemoveTimer = CTimer::GetTimeInMilliseconds() + lifeTime; + else + pobj->m_nRemoveTimer = 0; + + pobj->m_Color.alpha = 0; + + pobj->m_fSize = size; + pobj->m_fRandVal = 0.0f; + + return pobj; +} + +void +CParticleObject::RemoveObject(void) +{ + switch ( this->m_nState ) + { + case POBJECTSTATE_UPDATE_CLOSE: + { + MoveToList(&pCloseListHead, &pUnusedListHead, this); + this->m_nState = POBJECTSTATE_FREE; + break; + } + case POBJECTSTATE_UPDATE_FAR: + { + MoveToList(&pFarListHead, &pUnusedListHead, this); + this->m_nState = POBJECTSTATE_FREE; + break; + } + } +} + +void +CParticleObject::UpdateAll(void) +{ + { + CParticleObject *pobj = pCloseListHead; + CParticleObject *nextpobj; + if ( pobj != nil ) + { + do + { + nextpobj = pobj->m_pNext; + pobj->UpdateClose(); + pobj = nextpobj; + } + while ( nextpobj != nil ); + } + } + + { + int32 frame = CTimer::GetFrameCounter() & 31; + int32 counter = 0; + + CParticleObject *pobj = pFarListHead; + CParticleObject *nextpobj; + if ( pobj != nil ) + { + do + { + nextpobj = pobj->m_pNext; + + if ( counter == frame ) + { + pobj->UpdateFar(); + frame += 32; + } + + counter++; + + pobj = nextpobj; + } + while ( nextpobj != nil ); + } + } +} + +void CParticleObject::UpdateClose(void) +{ + if ( !CGame::playingIntro ) + { + if ( (this->GetPosition() - TheCamera.GetPosition()).MagnitudeSqr2D() > SQR(100.0f) ) + { + if ( this->m_bRemove ) + { + if ( this->m_Type == POBJECT_FIRE_HYDRANT ) + CAudioHydrant::Remove(this); + + MoveToList(&pCloseListHead, &pUnusedListHead, this); + this->m_nState = POBJECTSTATE_FREE; + } + else + { + MoveToList(&pCloseListHead, &pFarListHead, this); + this->m_nState = POBJECTSTATE_UPDATE_FAR; + } + + return; + } + } + + if ( ++this->m_nFrameCounter >= this->m_nSkipFrames ) + { + this->m_nFrameCounter = 0; + + int32 randVal; + if ( this->m_nCreationChance != 0 ) + randVal = CGeneral::GetRandomNumber() % this->m_nCreationChance; + + if ( this->m_nCreationChance == 0 + || randVal == 0 && this->m_nCreationChance < 0 + || randVal != 0 && this->m_nCreationChance > 0) + { + switch ( this->m_Type ) + { + case POBJECT_SMALL_FIRE: + { + CVector pos = this->GetPosition(); + CVector vel = this->m_vecTarget; + float size = this->m_fSize; + + CVector flamevel; + + flamevel.x = vel.x; + flamevel.y = vel.y; + flamevel.z = CGeneral::GetRandomNumberInRange(0.0125f*size, 0.1f*size); + + CParticle::AddParticle(PARTICLE_FLAME, pos, flamevel, nil, size); + + + CVector possmoke = pos; + + possmoke.x += CGeneral::GetRandomNumberInRange(0.625f*-size, size*0.625f); + possmoke.y += CGeneral::GetRandomNumberInRange(0.625f*-size, size*0.625f); + possmoke.z += CGeneral::GetRandomNumberInRange(0.625f* size, size*2.5f); + + CParticle::AddParticle(PARTICLE_CARFLAME_SMOKE, possmoke, vel); + + break; + } + + case POBJECT_BIG_FIRE: + { + CVector pos = this->GetPosition(); + CVector vel = this->m_vecTarget; + float size = this->m_fSize; + + + float s = 0.7f*size; + + CVector flamevel; + + flamevel.x = vel.x; + flamevel.y = vel.y; + flamevel.z = CGeneral::GetRandomNumberInRange(0.0125f*s, 0.1f*s); + + float flamesize = 0.8f*size; + + CParticle::AddParticle(PARTICLE_FLAME, pos, flamevel, nil, flamesize); + + + for ( int32 i = 0; i < 4; i++ ) + { + CVector smokepos = pos; + + smokepos.x += CGeneral::GetRandomNumberInRange(0.625f*-size, 0.625f*size); + smokepos.y += CGeneral::GetRandomNumberInRange(0.625f*-size, 0.625f*size); + smokepos.z += CGeneral::GetRandomNumberInRange(0.625f* size, 3.5f *size); + + CParticle::AddParticle(PARTICLE_CARFLAME_SMOKE, smokepos, vel); + } + + break; + } + + case POBJECT_FIREBALL_AND_SMOKE: + { + if ( this->m_pParticle == nil ) + { + CVector pos = this->GetPosition(); + CVector vel = this->m_vecTarget; + float size = this->m_fSize; + + CVector expvel = 1.2f*vel; + float expsize = 1.2f*size; + this->m_pParticle = CParticle::AddParticle(PARTICLE_EXPLOSION_MEDIUM, pos, expvel, nil, expsize); + } + else + { + CVector pos = this->GetPosition(); // this->m_pParticle->m_vecPosition ? + CVector vel = this->m_vecTarget; + float size = this->m_fSize; + + CVector veloffset = 0.35f*vel; + CVector fireballvel = vel; + + for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) + { + fireballvel.x += CGeneral::GetRandomNumberInRange(-veloffset.x, veloffset.x); + fireballvel.y += CGeneral::GetRandomNumberInRange(-veloffset.y, veloffset.y); + fireballvel.z += CGeneral::GetRandomNumberInRange(-veloffset.z, veloffset.z); + + CParticle::AddParticle(PARTICLE_FIREBALL_SMOKE, pos, fireballvel, nil, size); + } + } + + break; + } + + case POBJECT_ROCKET_TRAIL: + { + if ( this->m_pParticle == nil ) + { + CVector pos = this->GetPosition(); + CVector vel = this->m_vecTarget; + float size = this->m_fSize; + + this->m_pParticle = CParticle::AddParticle(PARTICLE_EXPLOSION_MEDIUM, pos, vel, nil, size); + } + else + { + CVector pos = this->m_pParticle->m_vecPosition; + CVector vel = this->m_vecTarget; + float size = this->m_fSize; + + float fireballsize = size * 1.5f; + CVector fireballvel = vel * -0.8f; + + for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) + { + CParticle::AddParticle(PARTICLE_FIREBALL_SMOKE, pos, fireballvel, nil, fireballsize); + } + } + + break; + } + + case POBJECT_FIRE_TRAIL: + { + for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) + { + CVector vel = this->m_vecTarget; + + if ( vel.x != 0.0f ) + vel.x += CGeneral::GetRandomNumberInRange(-this->m_fRandVal, this->m_fRandVal); + + if ( vel.y != 0.0f ) + vel.y += CGeneral::GetRandomNumberInRange(-this->m_fRandVal, this->m_fRandVal); + + if ( vel.z != 0.0f ) + vel.z += CGeneral::GetRandomNumberInRange(-this->m_fRandVal, this->m_fRandVal); + + CParticle::AddParticle(this->m_ParticleType, this->GetPosition(), vel, nil, this->m_fSize, + CGeneral::GetRandomNumberInRange(-6.0f, 6.0f)); + } + + break; + } + + case POBJECT_PED_WATER_SPLASH: + { + CRGBA colorsmoke(255, 255, 255, 196); + + CVector pos = this->GetPosition(); + CVector vel = this->m_vecTarget; + + for ( int32 i = 0; i < 3; i++ ) + { + int32 angle = 90 * i; + float fCos = CParticle::Cos(angle); + float fSin = CParticle::Sin(angle); + + CVector splashpos; + CVector splashvel; + + splashpos = pos + CVector(0.75f*fCos, 0.75f*fSin, 0.0f); + splashvel = vel + CVector(0.05f*fCos, 0.05f*fSin, CGeneral::GetRandomNumberInRange(0.04f, 0.08f)); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.8f), colorsmoke); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.5f), this->m_Color); + + + splashpos = pos + CVector(0.75f*fCos, 0.75f*-fSin, 0.0f); + splashvel = vel + CVector(0.05f*fCos, 0.05f*-fSin, CGeneral::GetRandomNumberInRange(0.04f, 0.08f)); + + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.8f), colorsmoke); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.5f), this->m_Color); + + + splashpos = pos + CVector(0.75f*-fCos, 0.75f*fSin, 0.0f); + splashvel = vel + CVector(0.05f*-fCos, 0.05f*fSin, CGeneral::GetRandomNumberInRange(0.04f, 0.08f)); + + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.8f), colorsmoke); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.5f), this->m_Color); + + + splashpos = pos + CVector(0.75f*-fCos, 0.75f*-fSin, 0.0f); + splashvel = vel + CVector(0.05f*-fCos, 0.05f*-fSin, CGeneral::GetRandomNumberInRange(0.04f, 0.08f)); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.8f), colorsmoke); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.5f), this->m_Color); + } + + for ( int32 i = 0; i < 1; i++ ) + { + int32 angle = 180 * (i + 1); + + float fCos = CParticle::Cos(angle); + float fSin = CParticle::Sin(angle); + + CVector splashpos; + CVector splashvel; + + splashpos = pos + CVector(0.5f*fCos, 0.5f*fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.05f, 0.25f); + + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.4f, 1.0f), this->m_Color); + + + splashpos = pos + CVector(0.5f*fCos, 0.5f*-fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * -fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.05f, 0.25f); + + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.4f, 1.0f), this->m_Color); + + + splashpos = pos + CVector(0.5f*-fCos, 0.5f*fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * -fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.05f, 0.25f); + + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.4f, 1.0f), this->m_Color); + + + splashpos = pos + CVector(0.5f*-fCos, 0.5f*-fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * -fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * -fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.05f, 0.25f); + + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.4f, 1.0f), this->m_Color); + } + + break; + } + + case POBJECT_CAR_WATER_SPLASH: + { + CRGBA colorsmoke(255, 255, 255, 196); + + CVector pos = this->GetPosition(); + CVector vel = this->m_vecTarget; + + float size = CGeneral::GetRandomNumberInRange(1.0f, 2.5f); + + for ( int32 i = 0; i < 3; i++ ) + { + int32 angle = 90 * i; + + float fCos = CParticle::Cos(angle); + float fSin = CParticle::Sin(angle); + + CVector splashpos; + CVector splashvel; + + splashpos = pos + CVector(2.0f*fCos, 2.0f*fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.01f, 0.03f); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, size, colorsmoke); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); + + + splashpos = pos + CVector(2.0f*fCos, 2.0f*-fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * -fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.01f, 0.03f); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, size, colorsmoke); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); + + splashpos = pos + CVector(2.0f*-fCos, 2.0f*fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * -fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.01f, 0.03f); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, size, colorsmoke); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); + + splashpos = pos + CVector(2.0f*-fCos, 2.0f*-fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * -fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * -fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.01f, 0.03f); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, size, colorsmoke); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); + } + + for ( int32 i = 0; i < 1; i++ ) + { + int32 angle = 180 * (i + 1); + + float fCos = CParticle::Cos(angle); + float fSin = CParticle::Sin(angle); + + CVector splashpos; + CVector splashvel; + + + splashpos = pos + CVector(1.25f*fCos, 1.25f*fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.26f, 0.53f); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); + + splashpos = pos + CVector(1.25f*fCos, 1.25f*-fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * -fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.26f, 0.53f); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); + + splashpos = pos + CVector(1.25f*-fCos, 1.25f*fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * -fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.26f, 0.53f); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); + + splashpos = pos + CVector(1.25f*-fCos, 1.25f*-fSin, 0.0f); + splashvel = vel; + splashvel.x += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * -fCos; + splashvel.y += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * -fSin; + splashvel.z += CGeneral::GetRandomNumberInRange(0.26f, 0.53f); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); + } + + break; + } + + case POBJECT_SPLASHES_AROUND: + { + CVector pos = this->GetPosition(); + float size = this->m_fSize; + + for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) + { + CVector splashpos = pos; + + splashpos.x += CGeneral::GetRandomNumberInRange(-size, size); + splashpos.y += CGeneral::GetRandomNumberInRange(-size, size); + + if ( CGeneral::GetRandomNumber() & 1 ) + { + CParticle::AddParticle(PARTICLE_RAIN_SPLASH, splashpos, CVector(0.0f, 0.0f, 0.0f), + nil, 0.1f, this->m_Color); + } + else + { + CParticle::AddParticle(PARTICLE_RAIN_SPLASHUP, splashpos, CVector(0.0f, 0.0f, 0.0f), + nil, 0.12f, this->m_Color); + } + } + + break; + } + + case POBJECT_FIRE_HYDRANT: + { + CVector pos = this->GetPosition(); + CVector vel = this->m_vecTarget; + + if ( (TheCamera.GetPosition() - pos).Magnitude() > 5.0f ) + { + for ( int32 i = 0; i < 1; i++ ) + { + int32 angle = 180 * i; + + float fCos = CParticle::Cos(angle); + float fSin = CParticle::Sin(angle); + + CVector splashpos, splashvel; + + splashpos = pos + CVector(0.01f*fCos, 0.01f*fSin, 0.0f); + splashvel = vel + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); + + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.005f, 0.0075f), this->m_Color, 0, 0, 1, 300); + + splashpos = pos + CVector(0.01f*fCos, 0.01f*-fSin, 0.0f); + splashvel = vel + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); + + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.005f, 0.0075f), this->m_Color, 0, 0, 1, 300); + + splashpos = pos + CVector(0.01f*-fCos, 0.01f*fSin, 0.0f); + splashvel = vel + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); + + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.005f, 0.0075f), this->m_Color, 0, 0, 1, 300); + + splashpos = pos + CVector(0.01f*-fCos, 0.01f*-fSin, 0.0f); + splashvel = vel + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); + + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.005f, 0.0075f), this->m_Color, 0, 0, 1, 300); + } + for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) + { + CParticle::AddParticle(this->m_ParticleType, pos, vel, nil, 0.0f, this->m_Color); + } + } + + break; + } + + case POBJECT_WATER_FOUNTAIN_VERT: + { + CVector pos = this->GetPosition(); + CVector vel = this->m_vecTarget; + + for ( int32 i = 0; i < 2; i++ ) + { + int32 angle = 180 * i; + + float fCos = CParticle::Cos(angle); + float fSin = CParticle::Sin(angle); + + CVector splashpos, splashvel; + + splashpos = pos + CVector(0.015f*fCos, 0.015f*fSin, 0.0f); + splashvel = vel + CVector(0.015f*fCos, 0.015f*fSin, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); + + CParticle::AddParticle(PARTICLE_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.001f, 0.005f), this->m_Color, 0, 0, 1, 1000); + + splashpos = pos + CVector(0.015f*fCos, 0.015f*-fSin, 0.0f); + splashvel = vel + CVector(0.015f*fCos, 0.015f*-fSin, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); + + CParticle::AddParticle(PARTICLE_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.001f, 0.005f), this->m_Color, 0, 0, 1, 1000); + + splashpos = pos + CVector(0.015f*-fCos, 0.015f*fSin, 0.0f); + splashvel = vel + CVector(0.015f*-fCos, 0.015f*fSin, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); + + CParticle::AddParticle(PARTICLE_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.001f, 0.005f), this->m_Color, 0, 0, 1, 1000); + + splashpos = pos + CVector(0.015f*-fCos, 0.015f*-fSin, 0.0f); + splashvel = vel + CVector(0.015f*-fCos, 0.015f*-fSin, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); + + CParticle::AddParticle(PARTICLE_SPLASH, splashpos, splashvel, nil, + CGeneral::GetRandomNumberInRange(0.001f, 0.005f), this->m_Color, 0, 0, 1, 1000); + } + + break; + } + + case POBJECT_WATER_FOUNTAIN_HORIZ: + { + CVector pos = this->GetPosition(); + CVector vel = this->m_vecTarget; + + for ( int32 i = 0; i < 3; i++ ) + { + CParticle::AddParticle(PARTICLE_CAR_SPLASH, pos, vel, nil, 0.001f, this->m_Color, 0, 0, 1, 1000); + } + + break; + } + + default: + { + if ( this->m_fRandVal != 0.0f ) + { + for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) + { + CVector vel = this->m_vecTarget; + + if ( vel.x != 0.0f ) + vel.x += CGeneral::GetRandomNumberInRange(-this->m_fRandVal, this->m_fRandVal); + + if ( vel.y != 0.0f ) + vel.y += CGeneral::GetRandomNumberInRange(-this->m_fRandVal, this->m_fRandVal); + + if ( vel.z != 0.0f ) + vel.z += CGeneral::GetRandomNumberInRange(-this->m_fRandVal, this->m_fRandVal); + + CParticle::AddParticle(this->m_ParticleType, this->GetPosition(), vel, nil, + this->m_fSize, this->m_Color); + } + } + else + { + for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) + { + CParticle::AddParticle(this->m_ParticleType, this->GetPosition(), this->m_vecTarget, nil, + this->m_fSize, this->m_Color); + } + } + + break; + } + } + } + } + + if ( this->m_nRemoveTimer != 0 && this->m_nRemoveTimer < CTimer::GetTimeInMilliseconds() ) + { + MoveToList(&pCloseListHead, &pUnusedListHead, this); + this->m_nState = POBJECTSTATE_FREE; + + if ( this->m_Type == POBJECT_FIRE_HYDRANT ) + CAudioHydrant::Remove(this); + } +} + +void +CParticleObject::UpdateFar(void) +{ + if ( this->m_nRemoveTimer != 0 && this->m_nRemoveTimer < CTimer::GetTimeInMilliseconds() ) + { + MoveToList(&pFarListHead, &pUnusedListHead, this); + this->m_nState = POBJECTSTATE_FREE; + + if ( this->m_Type == POBJECT_FIRE_HYDRANT ) + CAudioHydrant::Remove(this); + } + + CVector2D dist = this->GetPosition() - TheCamera.GetPosition(); + if ( dist.MagnitudeSqr() < SQR(100.0f)/*10000.0f*/ ) + { + MoveToList(&pFarListHead, &pCloseListHead, this); + this->m_nState = POBJECTSTATE_UPDATE_CLOSE; + } +} + +#ifdef COMPATIBLE_SAVES +static inline void +SaveOneParticle(CParticleObject *p, uint8 *&buffer) +{ +#define SkipBuf(buf, num) buf += num +#define ZeroBuf(buf, num) memset(buf, 0, num); SkipBuf(buf, num) +#define CopyToBuf(buf, data) memcpy(buf, &data, sizeof(data)); SkipBuf(buf, sizeof(data)) + // CPlaceable + { + CopyToBuf(buffer, p->GetMatrix().f); + ZeroBuf(buffer, 4); + CopyToBuf(buffer, p->GetMatrix().m_hasRwMatrix); + ZeroBuf(buffer, 3); + } + + // CParticleObject + { + ZeroBuf(buffer, 4); + ZeroBuf(buffer, 4); + ZeroBuf(buffer, 4); + CopyToBuf(buffer, p->m_nRemoveTimer); + CopyToBuf(buffer, p->m_Type); + CopyToBuf(buffer, p->m_ParticleType); + CopyToBuf(buffer, p->m_nNumEffectCycles); + CopyToBuf(buffer, p->m_nSkipFrames); + CopyToBuf(buffer, p->m_nFrameCounter); + CopyToBuf(buffer, p->m_nState); + ZeroBuf(buffer, 2); + CopyToBuf(buffer, p->m_vecTarget); + CopyToBuf(buffer, p->m_fRandVal); + CopyToBuf(buffer, p->m_fSize); + CopyToBuf(buffer, p->m_Color); + CopyToBuf(buffer, p->m_bRemove); + CopyToBuf(buffer, p->m_nCreationChance); + ZeroBuf(buffer, 2); + } +#undef SkipBuf +#undef ZeroBuf +#undef CopyToBuf +} +#endif + +bool +CParticleObject::SaveParticle(uint8 *buffer, uint32 *length) +{ + ASSERT( buffer != nil ); + ASSERT( length != nil ); + + int32 numObjects = 0; + + for ( CParticleObject *p = pCloseListHead; p != nil; p = p->m_pNext ) + ++numObjects; + + for ( CParticleObject *p = pFarListHead; p != nil; p = p->m_pNext ) + ++numObjects; + + *(int32 *)buffer = numObjects; + buffer += sizeof(int32); + + int32 objectsLength = PARTICLE_OBJECT_SIZEOF * (numObjects + 1); + int32 dataLength = objectsLength + sizeof(int32); + + for ( CParticleObject *p = pCloseListHead; p != nil; p = p->m_pNext ) + { +#ifdef COMPATIBLE_SAVES + SaveOneParticle(p, buffer); +#else +#ifdef THIS_IS_STUPID + *(CParticleObject*)buffer = *p; +#else + memcpy(buffer, p, sizeof(CParticleObject)); +#endif + buffer += sizeof(CParticleObject); +#endif + } + + for ( CParticleObject *p = pFarListHead; p != nil; p = p->m_pNext ) + { +#ifdef COMPATIBLE_SAVES + SaveOneParticle(p, buffer); +#else +#ifdef THIS_IS_STUPID + *(CParticleObject*)buffer = *p; +#else + memcpy(buffer, p, sizeof(CParticleObject)); +#endif + buffer += sizeof(CParticleObject); +#endif + } + + *length = dataLength; + + return true; +} + +bool +CParticleObject::LoadParticle(uint8 *buffer, uint32 length) +{ + ASSERT( buffer != nil ); + + RemoveAllParticleObjects(); + + int32 numObjects = *(int32 *)buffer; + buffer += sizeof(int32); + + if ( length != PARTICLE_OBJECT_SIZEOF * (numObjects + 1) + sizeof(int32) ) + return false; + + if ( numObjects == 0 ) + return true; + + + int32 i = 0; + while ( i < numObjects ) + { + CParticleObject *dst = pUnusedListHead; +#ifndef COMPATIBLE_SAVES + CParticleObject *src = (CParticleObject *)buffer; + buffer += sizeof(CParticleObject); +#endif + + if ( dst == nil ) + return false; + + MoveToList(&pUnusedListHead, &pCloseListHead, dst); + +#ifndef COMPATIBLE_SAVES + dst->m_nState = POBJECTSTATE_UPDATE_CLOSE; + dst->m_Type = src->m_Type; + dst->m_ParticleType = src->m_ParticleType; + dst->SetPosition(src->GetPosition()); + dst->m_vecTarget = src->m_vecTarget; + dst->m_nFrameCounter = src->m_nFrameCounter; + dst->m_bRemove = src->m_bRemove; + dst->m_pParticle = nil; + dst->m_nRemoveTimer = src->m_nRemoveTimer; + dst->m_Color = src->m_Color; + dst->m_fSize = src->m_fSize; + dst->m_fRandVal = src->m_fRandVal; + dst->m_nNumEffectCycles = src->m_nNumEffectCycles; + dst->m_nSkipFrames = src->m_nSkipFrames; + dst->m_nCreationChance = src->m_nCreationChance; +#else + dst->m_nState = POBJECTSTATE_UPDATE_CLOSE; + dst->m_pParticle = NULL; + +#define SkipBuf(buf, num) buf += num +#define CopyFromBuf(buf, data) memcpy(&data, buf, sizeof(data)); SkipBuf(buf, sizeof(data)) + // CPlaceable + { + CMatrix matrix; + CopyFromBuf(buffer, matrix.f); + SkipBuf(buffer, 4); + CopyFromBuf(buffer, matrix.m_hasRwMatrix); + SkipBuf(buffer, 3); + dst->SetPosition(matrix.GetPosition()); + } + + // CParticleObject + { + SkipBuf(buffer, 4); + SkipBuf(buffer, 4); + SkipBuf(buffer, 4); + CopyFromBuf(buffer, dst->m_nRemoveTimer); + CopyFromBuf(buffer, dst->m_Type); + CopyFromBuf(buffer, dst->m_ParticleType); + CopyFromBuf(buffer, dst->m_nNumEffectCycles); + CopyFromBuf(buffer, dst->m_nSkipFrames); + CopyFromBuf(buffer, dst->m_nFrameCounter); + SkipBuf(buffer, 2); + SkipBuf(buffer, 2); + CopyFromBuf(buffer, dst->m_vecTarget); + CopyFromBuf(buffer, dst->m_fRandVal); + CopyFromBuf(buffer, dst->m_fSize); + CopyFromBuf(buffer, dst->m_Color); + CopyFromBuf(buffer, dst->m_bRemove); + CopyFromBuf(buffer, dst->m_nCreationChance); + SkipBuf(buffer, 2); + } +#undef CopyFromBuf +#undef SkipBuf +#endif + + i++; + } + + return true; +} + +void +CParticleObject::RemoveAllExpireableParticleObjects(void) +{ + { + CParticleObject *pobj = pCloseListHead; + CParticleObject *nextpobj; + if ( pobj != nil ) + { + do + { + nextpobj = pobj->m_pNext; + if ( pobj->m_nRemoveTimer != 0 ) + { + MoveToList(&pCloseListHead, &pUnusedListHead, pobj); + pobj->m_nState = POBJECTSTATE_FREE; + } + pobj = nextpobj; + } + while ( nextpobj != nil ); + } + } + + { + CParticleObject *pobj = pFarListHead; + CParticleObject *nextpobj; + if ( pobj != nil ) + { + do + { + nextpobj = pobj->m_pNext; + if ( pobj->m_nRemoveTimer != 0 ) + { + MoveToList(&pFarListHead, &pUnusedListHead, pobj); + pobj->m_nState = POBJECTSTATE_FREE; + } + pobj = nextpobj; + } + while ( nextpobj != nil ); + } + } +} + +void +CParticleObject::RemoveAllParticleObjects(void) +{ + pUnusedListHead = &gPObjectArray[0]; + + pCloseListHead = nil; + pFarListHead = nil; + + for ( int32 i = 0; i < MAX_PARTICLEOBJECTS; i++ ) + { + if ( i == 0 ) + gPObjectArray[i].m_pPrev = nil; + else + gPObjectArray[i].m_pPrev = &gPObjectArray[i - 1]; + + if ( i == MAX_PARTICLEOBJECTS-1 ) + gPObjectArray[i].m_pNext = nil; + else + gPObjectArray[i].m_pNext = &gPObjectArray[i + 1]; + + gPObjectArray[i].m_nState = POBJECTSTATE_FREE; + } +} + +void +CParticleObject::MoveToList(CParticleObject **from, CParticleObject **to, CParticleObject *obj) +{ + ASSERT( from != nil ); + ASSERT( to != nil ); + ASSERT( obj != nil ); + + if ( obj->m_pPrev == nil ) + { + *from = obj->m_pNext; + if ( *from ) + (*from)->m_pPrev = nil; + } + else + { + if ( obj->m_pNext == nil ) + obj->m_pPrev->m_pNext = nil; + else + { + obj->m_pNext->m_pPrev = obj->m_pPrev; + obj->m_pPrev->m_pNext = obj->m_pNext; + } + } + + obj->m_pNext = *to; + obj->m_pPrev = nil; + *to = obj; + + if ( obj->m_pNext ) + obj->m_pNext->m_pPrev = obj; +} diff --git a/src/miami/objects/ParticleObject.h b/src/miami/objects/ParticleObject.h new file mode 100644 index 00000000..f199e533 --- /dev/null +++ b/src/miami/objects/ParticleObject.h @@ -0,0 +1,112 @@ +#pragma once + +#include "AudioManager.h" +#include "ParticleType.h" +#include "Placeable.h" + +#define MAX_PARTICLEOBJECTS 70 +#define MAX_AUDIOHYDRANTS 8 + +enum eParticleObjectType +{ + POBJECT_PAVEMENT_STEAM = 0, + POBJECT_PAVEMENT_STEAM_SLOWMOTION, + POBJECT_WALL_STEAM, + POBJECT_WALL_STEAM_SLOWMOTION, + POBJECT_DARK_SMOKE, + POBJECT_FIRE_HYDRANT, + POBJECT_CAR_WATER_SPLASH, + POBJECT_PED_WATER_SPLASH, + POBJECT_SPLASHES_AROUND, + POBJECT_SMALL_FIRE, + POBJECT_BIG_FIRE, + POBJECT_DRY_ICE, + POBJECT_DRY_ICE_SLOWMOTION, + POBJECT_WATER_FOUNTAIN_VERT, + POBJECT_WATER_FOUNTAIN_HORIZ, + POBJECT_FIRE_TRAIL, + POBJECT_SMOKE_TRAIL, + POBJECT_FIREBALL_AND_SMOKE, + POBJECT_ROCKET_TRAIL, + POBJECT_EXPLOSION_ONCE, + POBJECT_CATALINAS_GUNFLASH, + POBJECT_CATALINAS_SHOTGUNFLASH, +}; + +enum eParticleObjectState +{ + POBJECTSTATE_INITIALISED = 0, + POBJECTSTATE_UPDATE_CLOSE, + POBJECTSTATE_UPDATE_FAR, + POBJECTSTATE_FREE, +}; + +class CParticle; + +class CParticleObject : public CPlaceable +{ +public: + CParticleObject *m_pNext; + CParticleObject *m_pPrev; + CParticle *m_pParticle; + uint32 m_nRemoveTimer; + eParticleObjectType m_Type; + tParticleType m_ParticleType; + uint8 m_nNumEffectCycles; + uint8 m_nSkipFrames; + uint16 m_nFrameCounter; + uint16 m_nState; + CVector m_vecTarget; + float m_fRandVal; + float m_fSize; + CRGBA m_Color; + uint8 m_bRemove; + int8 m_nCreationChance; + + static CParticleObject *pCloseListHead; + static CParticleObject *pFarListHead; + static CParticleObject *pUnusedListHead; + + CParticleObject(); + ~CParticleObject(); + + static void Initialise(void); + + static CParticleObject *AddObject(uint16 type, CVector const &pos, uint8 remove); + static CParticleObject *AddObject(uint16 type, CVector const &pos, float size, uint8 remove); + static CParticleObject *AddObject(uint16 type, CVector const &pos, CVector const &target, float size, uint8 remove); + static CParticleObject *AddObject(uint16 type, CVector const &pos, CVector const &target, float size, uint32 lifeTime, RwRGBA const &color, uint8 remove); + static CParticleObject *AddObject(tParticleType type, CVector const &pos, CVector const &target, float size, uint32 lifeTime, uint8 numEffectCycles, uint8 skipFrames, uint16 creationChance, uint8 remove); + + void RemoveObject(void); + + static void UpdateAll(void); + void UpdateClose(void); + void UpdateFar(void); + + static bool SaveParticle(uint8 *buffer, uint32 *length); + static bool LoadParticle(uint8 *buffer, uint32 length); + + static void RemoveAllExpireableParticleObjects(void); + static void RemoveAllParticleObjects(void); + static void MoveToList(CParticleObject **from, CParticleObject **to, CParticleObject *obj); +}; + +extern CParticleObject gPObjectArray[MAX_PARTICLEOBJECTS]; + +class CAudioHydrant +{ +public: + int32 AudioEntity; + CParticleObject *pParticleObject; + + CAudioHydrant() : + AudioEntity(AEHANDLE_NONE), + pParticleObject(nil) + { } + + static bool Add (CParticleObject *particleobject); + static void Remove(CParticleObject *particleobject); + + static CAudioHydrant *Get(int n); // for neo screen droplets +}; \ No newline at end of file diff --git a/src/miami/objects/Projectile.cpp b/src/miami/objects/Projectile.cpp new file mode 100644 index 00000000..fe8b0c68 --- /dev/null +++ b/src/miami/objects/Projectile.cpp @@ -0,0 +1,15 @@ +#include "common.h" + +#include "Projectile.h" + +CProjectile::CProjectile(int32 model) : CObject() +{ + m_fMass = 1.0f; + m_fTurnMass = 1.0f; + m_fAirResistance = 0.99999f; + m_fElasticity = 0.75f; + m_fBuoyancy = GRAVITY * m_fMass * 0.1f; + bExplosionProof = true; + SetModelIndex(model); + ObjectCreatedBy = MISSION_OBJECT; +} diff --git a/src/miami/objects/Projectile.h b/src/miami/objects/Projectile.h new file mode 100644 index 00000000..4b3eb4b8 --- /dev/null +++ b/src/miami/objects/Projectile.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Object.h" + +class CProjectile : public CObject +{ +public: + CProjectile(int32); +}; diff --git a/src/miami/objects/Stinger.cpp b/src/miami/objects/Stinger.cpp new file mode 100644 index 00000000..d3eee416 --- /dev/null +++ b/src/miami/objects/Stinger.cpp @@ -0,0 +1,259 @@ +#include "common.h" +#include "Stinger.h" +#include "CopPed.h" +#include "ModelIndices.h" +#include "RpAnimBlend.h" +#include "World.h" +#include "Automobile.h" +#include "Bike.h" +#include "Particle.h" +#include "AnimBlendAssociation.h" +#include "General.h" + +uint32 NumOfStingerSegments; + +/* -- CStingerSegment -- */ + +CStingerSegment::CStingerSegment() +{ + m_fMass = 1.0f; + m_fTurnMass = 1.0f; + m_fAirResistance = 0.99999f; + m_fElasticity = 0.75f; + m_fBuoyancy = GRAVITY * m_fMass * 0.1f; + bExplosionProof = true; + SetModelIndex(MI_PLC_STINGER); + ObjectCreatedBy = CONTROLLED_SUB_OBJECT; + NumOfStingerSegments++; +} + +CStingerSegment::~CStingerSegment() +{ + NumOfStingerSegments--; +} + +/* -- CStinger -- */ + +CStinger::CStinger() +{ + bIsDeployed = false; +} + +void +CStinger::Init(CPed *pPed) +{ + int32 i; + + pOwner = pPed; + for (i = 0; i < NUM_STINGER_SEGMENTS; i++) { + pSpikes[i] = new CStingerSegment(); +#ifdef FIX_BUGS + if (!pSpikes[i]) { + // Abort!! Pool is full + Remove(); + return; + } +#endif + pSpikes[i]->bUsesCollision = false; + } + bIsDeployed = true; + m_vPos = pPed->GetPosition(); + m_vPos.z -= 1.0f; + m_fMax_Z = Atan2(-pPed->GetForward().x, pPed->GetForward().y) + HALFPI; + + for (i = 0; i < NUM_STINGER_SEGMENTS; i++) { + pSpikes[i]->SetOrientation(0.0f, 0.0f, Atan2(-pPed->GetForward().x, pPed->GetForward().y)); + pSpikes[i]->SetPosition(m_vPos); + } + + CVector2D fwd2d(pPed->GetForward().x, pPed->GetForward().y); + + for (i = 0; i < ARRAY_SIZE(m_vPositions); i++) + m_vPositions[i] = fwd2d * 1.8f * Sin(DEGTORAD(i)); + + m_nSpikeState = STINGERSTATE_NONE; + m_nTimeOfDeploy = CTimer::GetTimeInMilliseconds(); +} + +void +CStinger::Remove() +{ + if (!bIsDeployed) return; + + for (int32 i = 0; i < NUM_STINGER_SEGMENTS; i++) { + CStingerSegment *spikeSegment = pSpikes[i]; + +#ifdef FIX_BUGS + if (spikeSegment) { + CWorld::Remove(spikeSegment); + delete spikeSegment; + pSpikes[i] = nil; + } +#else + if (spikeSegment->m_entryInfoList.first != nil) + spikeSegment->bRemoveFromWorld = true; + else + delete spikeSegment; +#endif + } + bIsDeployed = false; +} + +void +CStinger::Deploy(CPed *pPed) +{ + // So total number of stingers allowed at the same time is 2, each by different CCopPed. + if (NumOfStingerSegments < NUM_STINGER_SEGMENTS*2 && !pPed->bInVehicle && pPed->IsPedInControl()) { + if (!bIsDeployed && RpAnimBlendClumpGetAssociation(pPed->GetClump(), ANIM_STD_THROW_UNDER) == nil) { + Init(pPed); +#ifdef FIX_BUGS + // Above call won't set it to true no more when object pool is full + if (!bIsDeployed) + return; +#endif + pPed->SetPedState(PED_DEPLOY_STINGER); + CAnimManager::AddAnimation(pPed->GetClump(), ASSOCGRP_STD, ANIM_STD_THROW_UNDER); + } + } +} + +void +CStinger::CheckForBurstTyres() +{ + CVector firstPos = pSpikes[0]->GetPosition(); + firstPos.z += 0.2f; + CVector lastPos = pSpikes[NUM_STINGER_SEGMENTS - 1]->GetPosition(); + lastPos.z += 0.2f; + float dist = (lastPos - firstPos).Magnitude(); + if (dist < 0.1f) return; + + CVehicle *vehsInRange[16]; + int16 numObjects; + CEntity entity; + + CWorld::FindObjectsInRange((lastPos + firstPos) / 2.0f, + dist, true, &numObjects, 15, (CEntity**)vehsInRange, + false, true, false, false, false); + + for (int32 i = 0; i < numObjects; i++) { + CAutomobile *pAutomobile = nil; + CBike *pBike = nil; + + if (vehsInRange[i]->IsCar()) + pAutomobile = (CAutomobile*)vehsInRange[i]; + else if (vehsInRange[i]->IsBike()) + pBike = (CBike*)vehsInRange[i]; + + if (pAutomobile == nil && pBike == nil) continue; + + float maxWheelDistToSpike = sq(((CVehicleModelInfo*)CModelInfo::GetModelInfo(vehsInRange[i]->GetModelIndex()))->m_wheelScale + 0.1f); + + for (int wheelId = 0; wheelId < 4; wheelId++) { + if ((pAutomobile != nil && pAutomobile->m_aSuspensionSpringRatioPrev[wheelId] < 1.0f) || + (pBike != nil && pBike->m_aSuspensionSpringRatioPrev[wheelId] < 1.0f)) { + CVector vecWheelPos; + if (pAutomobile != nil) + vecWheelPos = pAutomobile->m_aWheelColPoints[wheelId].point; + else if (pBike != nil) + vecWheelPos = pBike->m_aWheelColPoints[wheelId].point; + + for (int32 spike = 0; spike < NUM_STINGER_SEGMENTS; spike++) { + if ((pSpikes[spike]->GetPosition() - vecWheelPos).Magnitude() < maxWheelDistToSpike) { + if (pBike) { + if (wheelId < 2) + vehsInRange[i]->BurstTyre(CAR_PIECE_WHEEL_LF, true); + else + vehsInRange[i]->BurstTyre(CAR_PIECE_WHEEL_LR, true); + } + else { + switch (wheelId) { + case 0: vehsInRange[i]->BurstTyre(CAR_PIECE_WHEEL_LF, true); break; + case 1: vehsInRange[i]->BurstTyre(CAR_PIECE_WHEEL_LR, true); break; + case 2: vehsInRange[i]->BurstTyre(CAR_PIECE_WHEEL_RF, true); break; + case 3: vehsInRange[i]->BurstTyre(CAR_PIECE_WHEEL_RR, true); break; + } + } + vecWheelPos.z += 0.15f; + for (int j = 0; j < 4; j++) + CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, vecWheelPos, vehsInRange[i]->GetRight() * 0.1f); + } + } + } + } + } +} + +// Only called when bIsDeployed +void +CStinger::Process() +{ + switch (m_nSpikeState) + { + case STINGERSTATE_NONE: + if (pOwner != nil + && !pOwner->bInVehicle + && pOwner->GetPedState() == PED_DEPLOY_STINGER + && RpAnimBlendClumpGetAssociation(pOwner->GetClump(), ANIM_STD_THROW_UNDER)->currentTime > 0.39f) + { + m_nSpikeState = STINGERSTATE_DEPLOYING; + for (int i = 0; i < NUM_STINGER_SEGMENTS; i++) + CWorld::Add(pSpikes[i]); + pOwner->SetIdle(); + } + break; + case STINGERSTATE_DEPLOYED: + if (pOwner != nil && pOwner->m_nPedType == PEDTYPE_COP) + ((CCopPed*)pOwner)->m_bThrowsSpikeTrap = false; + break; + case STINGERSTATE_UNDEPLOYING: + if (CTimer::GetTimeInMilliseconds() > m_nTimeOfDeploy + 2500) + m_nSpikeState = STINGERSTATE_REMOVE; + // no break + case STINGERSTATE_DEPLOYING: + if (m_nSpikeState == STINGERSTATE_DEPLOYING && CTimer::GetTimeInMilliseconds() > m_nTimeOfDeploy + 2500) + m_nSpikeState = STINGERSTATE_DEPLOYED; + else { + float progress = (CTimer::GetTimeInMilliseconds() - m_nTimeOfDeploy) / 2500.0f; + if (m_nSpikeState != STINGERSTATE_DEPLOYING) + progress = 1.0f - progress; + + float degangle = progress * ARRAY_SIZE(m_vPositions); + float angle1 = m_fMax_Z + DEGTORAD(degangle); + float angle2 = m_fMax_Z - DEGTORAD(degangle); + int pos = Clamp(degangle, 0, ARRAY_SIZE(m_vPositions)-1); + + CVector2D pos2d = m_vPositions[pos]; + CVector pos3d = m_vPos; + CColPoint colPoint; + CEntity *pEntity; + if (CWorld::ProcessVerticalLine(CVector(pos3d.x, pos3d.y, pos3d.z - 10.0f), pos3d.z, colPoint, pEntity, true, false, false, false, true, false, nil)) + pos3d.z = colPoint.point.z + 0.15f; + + angle1 = CGeneral::LimitRadianAngle(angle1); + angle2 = CGeneral::LimitRadianAngle(angle2); + + for (int spike = 0; spike < NUM_STINGER_SEGMENTS; spike++) { + if (CWorld::TestSphereAgainstWorld(pos3d + CVector(pos2d.x, pos2d.y, 0.6f), 0.3f, nil, true, false, false, true, false, false)) + pos2d = CVector2D(0.0f, 0.0f); + + if (spike % 2 == 0) { + pSpikes[spike]->SetOrientation(0.0f, 0.0f, angle1); + pos3d.x += pos2d.x; + pos3d.y += pos2d.y; + } else { + pSpikes[spike]->SetOrientation(0.0f, 0.0f, angle2); + } + pSpikes[spike]->SetPosition(pos3d); + } + } + break; + case STINGERSTATE_REMOVE: + Remove(); +#ifdef FIX_BUGS + return; +#else + break; +#endif + } + CheckForBurstTyres(); +} \ No newline at end of file diff --git a/src/miami/objects/Stinger.h b/src/miami/objects/Stinger.h new file mode 100644 index 00000000..250cf62d --- /dev/null +++ b/src/miami/objects/Stinger.h @@ -0,0 +1,40 @@ +#pragma once + +#include "Object.h" + +class CStingerSegment : public CObject +{ +public: + CStingerSegment(); + ~CStingerSegment(); +}; + +#define NUM_STINGER_SEGMENTS (12) + +enum { + STINGERSTATE_NONE = 0, + STINGERSTATE_DEPLOYING, + STINGERSTATE_DEPLOYED, + STINGERSTATE_UNDEPLOYING, + STINGERSTATE_REMOVE, +}; + +class CStinger +{ +public: + bool bIsDeployed; + uint32 m_nTimeOfDeploy; + CVector m_vPos; + float m_fMax_Z; + float m_fMin_Z; + CVector2D m_vPositions[60]; + CStingerSegment *pSpikes[NUM_STINGER_SEGMENTS]; + class CPed *pOwner; + uint8 m_nSpikeState; + CStinger(); + void Init(CPed *pPed); + void Remove(); + void Deploy(CPed *pPed); + void CheckForBurstTyres(); + void Process(); +}; \ No newline at end of file diff --git a/src/miami/peds/CivilianPed.cpp b/src/miami/peds/CivilianPed.cpp new file mode 100644 index 00000000..f3511366 --- /dev/null +++ b/src/miami/peds/CivilianPed.cpp @@ -0,0 +1,610 @@ +#include "common.h" + +#include "CivilianPed.h" +#include "Phones.h" +#include "General.h" +#include "PlayerPed.h" +#include "Wanted.h" +#include "DMAudio.h" +#include "World.h" +#include "Vehicle.h" +#include "SurfaceTable.h" +#include "Weather.h" +#include "PedAttractor.h" +#include "Object.h" +#include "CarCtrl.h" + +#ifndef _WIN32 +#include +#endif + +CCivilianPed::CCivilianPed(ePedType pedtype, uint32 mi) : CPed(pedtype) +{ + SetModelIndex(mi); + for (int i = 0; i < ARRAY_SIZE(m_nearPeds); i++) { + m_nearPeds[i] = nil; + } + m_bLookForVacantCars = false; + if (pedtype == PEDTYPE_CRIMINAL) + m_bLookForVacantCars = true; + + m_nLookForVacantCarsCounter = 0; + m_bJustStoleACar = false; + m_bStealCarEvenIfThereIsSomeoneInIt = false; + for (int i = 0; i < ARRAY_SIZE(m_nStealWishList); i++) { +#ifdef FIX_BUGS + uint32 randomCarModel = CGeneral::GetRandomNumberInRange(MI_LANDSTAL, MI_LAST_VEHICLE); +#else + uint32 randomCarModel = CGeneral::GetRandomNumberInRange(MI_LANDSTAL, MI_LANDSTAL + VEHICLEMODELSIZE); +#endif + if (CModelInfo::IsCarModel(randomCarModel) || CModelInfo::IsBikeModel(randomCarModel)) + m_nStealWishList[i] = randomCarModel; + else + m_nStealWishList[i] = MI_CHEETAH; + } + m_nAttractorCycleState = 0; + m_bAttractorUnk = (CGeneral::GetRandomNumberInRange(0.0f, 1.0f) < 1.25f); +} + +void +CCivilianPed::CivilianAI(void) +{ + if (CTimer::GetTimeInMilliseconds() <= m_fleeTimer || m_objective != OBJECTIVE_NONE && !bRespondsToThreats + || !IsPedInControl()) { + + if (m_objective == OBJECTIVE_GUARD_SPOT) + return; + + if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS) { + if (m_pedInObjective && m_pedInObjective->IsPlayer()) + return; + } + if (CTimer::GetTimeInMilliseconds() <= m_lookTimer) + return; + + ScanForDelayedResponseThreats(); + if (!m_threatFlags || CTimer::GetTimeInMilliseconds() <= m_threatCheckTimer) + return; + CheckThreatValidity(); + uint32 closestThreatFlag = m_threatFlags; + m_threatFlags = 0; + m_threatCheckTimer = 0; + if (closestThreatFlag == PED_FLAG_EXPLOSION) { + float angleToFace = CGeneral::GetRadianAngleBetweenPoints( + m_eventOrThreat.x, m_eventOrThreat.y, + GetPosition().x, GetPosition().y); + SetLookFlag(angleToFace, true); + SetLookTimer(500); + + } else if (closestThreatFlag == PED_FLAG_GUN) { + SetLookFlag(m_threatEntity, true); + SetLookTimer(500); + } + return; + } + ScanForDelayedResponseThreats(); + if (!m_threatFlags || CTimer::GetTimeInMilliseconds() <= m_threatCheckTimer) + return; + CheckThreatValidity(); + uint32 closestThreatFlag = m_threatFlags; + m_threatFlags = 0; + m_threatCheckTimer = 0; + if (closestThreatFlag == PED_FLAG_GUN) { + if (!m_threatEntity || !m_threatEntity->IsPed()) + return; + + CPed *threatPed = (CPed*)m_threatEntity; + float threatDistSqr = (m_threatEntity->GetPosition() - GetPosition()).MagnitudeSqr2D(); + if (CharCreatedBy == MISSION_CHAR && bCrouchWhenScared) { + SetDuck(10000, true); + SetLookFlag(m_threatEntity, false); + SetLookTimer(500); + return; + } + if (m_pedStats->m_fear <= m_pedStats->m_lawfulness) { + if (m_pedStats->m_temper <= m_pedStats->m_fear) { + if (!threatPed->IsPlayer() || !RunToReportCrime(CRIME_POSSESSION_GUN)) { + if (threatDistSqr < sq(30.0f)) { + bMakeFleeScream = true; + SetFindPathAndFlee(m_threatEntity, 10000); + } else { + SetFindPathAndFlee(m_threatEntity->GetPosition(), 5000, true); + } + } + } else if (m_objective != OBJECTIVE_NONE || GetWeapon()->IsTypeMelee()) { + SetFindPathAndFlee(m_threatEntity, 5000); + if (threatDistSqr < sq(20.0f)) { + SetMoveState(PEDMOVE_RUN); + bMakeFleeScream = true; + } else { + SetMoveState(PEDMOVE_WALK); + } + } else if (threatPed->IsPlayer() && IsGangMember() && bCanAttackPlayerWithCops) { + SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity); + + } else if (threatPed->IsPlayer() && FindPlayerPed()->m_pWanted->m_CurrentCops != 0) { + SetFindPathAndFlee(m_threatEntity, 5000); + if (threatDistSqr < sq(30.0f)) { + SetMoveState(PEDMOVE_RUN); + } else { + SetMoveState(PEDMOVE_WALK); + } + } else { + SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity); + } + } else { + if (threatDistSqr < sq(30.0f)) { + bMakeFleeScream = true; + SetFindPathAndFlee(m_threatEntity, 10000); + SetMoveState(PEDMOVE_SPRINT); + } else { + bMakeFleeScream = false; + SetFindPathAndFlee(m_threatEntity, 5000); + SetMoveState(PEDMOVE_RUN); + } + } + SetLookFlag(m_threatEntity, false); + SetLookTimer(500); + } else if (closestThreatFlag == PED_FLAG_DEADPEDS) { + float eventDistSqr = (m_pEventEntity->GetPosition() - GetPosition()).MagnitudeSqr2D(); + if (CharCreatedBy == MISSION_CHAR && bCrouchWhenScared && eventDistSqr < sq(5.0f)) { + SetDuck(10000, true); + + } else if (((CPed*)m_pEventEntity)->bIsDrowning || IsGangMember() && m_nPedType == ((CPed*)m_pEventEntity)->m_nPedType) { + if (eventDistSqr < sq(5.0f)) { + SetFindPathAndFlee(m_pEventEntity, 2000); + SetMoveState(PEDMOVE_RUN); + } + } else if (IsGangMember() || eventDistSqr > sq(5.0f)) { + bool investigateDeadPed = true; + CEntity *killerOfDeadPed = ((CPed*)m_pEventEntity)->m_threatEntity; + if (killerOfDeadPed && killerOfDeadPed->IsPed()) { + CVector killerPos = killerOfDeadPed->GetPosition(); + CVector deadPedPos = m_pEventEntity->GetPosition(); + if (CVector2D(killerPos - deadPedPos).MagnitudeSqr() < sq(10.0f)) + investigateDeadPed = false; + } + + if (investigateDeadPed) + SetInvestigateEvent(EVENT_DEAD_PED, CVector2D(m_pEventEntity->GetPosition()), 1.0f, 20000, 0.0f); + + } else { + SetFindPathAndFlee(m_pEventEntity, 5000); + SetMoveState(PEDMOVE_RUN); + } + } else if (closestThreatFlag == PED_FLAG_EXPLOSION) { + CVector2D eventDistVec = m_eventOrThreat - GetPosition(); + float eventDistSqr = eventDistVec.MagnitudeSqr(); + if (CharCreatedBy == MISSION_CHAR && bCrouchWhenScared && eventDistSqr < sq(20.0f)) { + SetDuck(10000, true); + + } else if (eventDistSqr < sq(20.0f)) { + bMakeFleeScream = true; + SetFlee(m_eventOrThreat, 2000); + float angleToFace = CGeneral::GetRadianAngleBetweenPoints( + m_eventOrThreat.x, m_eventOrThreat.y, + GetPosition().x, GetPosition().y); + SetLookFlag(angleToFace, true); + SetLookTimer(500); + } else if (eventDistSqr < sq(40.0f)) { + if (bGonnaInvestigateEvent) { + if (CharCreatedBy != MISSION_CHAR && !IsGangMember()) + SetInvestigateEvent(EVENT_EXPLOSION, m_eventOrThreat, 6.0f, 30000, 0.0f); + + } else { + float eventHeading = CGeneral::GetRadianAngleBetweenPoints(eventDistVec.x, eventDistVec.y, 0.0f, 0.0f); + eventHeading = CGeneral::LimitRadianAngle(eventHeading); + if (eventHeading < 0.0f) + eventHeading = eventHeading + TWOPI; + + SetWanderPath(eventHeading / 8.0f); + } + } + } else { + if (m_threatEntity && m_threatEntity->IsPed()) { + CPed *threatPed = (CPed*)m_threatEntity; + if (m_pedStats->m_fear <= 100 - threatPed->m_pedStats->m_temper && threatPed->m_nPedType != PEDTYPE_COP) { + if (threatPed->GetWeapon()->IsTypeMelee() || !GetWeapon()->IsTypeMelee()) { + if (threatPed->IsPlayer() && IsGangMember() && bCanAttackPlayerWithCops) { + SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity); + + } else if (threatPed->IsPlayer() && FindPlayerPed()->m_pWanted->m_CurrentCops != 0) { + if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS) { + SetFindPathAndFlee(m_threatEntity, 10000); + } + } else { + SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity); + } + } + } else { + SetFindPathAndFlee(m_threatEntity, 10000, true); + } + } + } +} + +void +CCivilianPed::ProcessControl(void) +{ + if (CharCreatedBy == UNK_CHAR) + return; + + CPed::ProcessControl(); + + if (bWasPostponed) + return; + + if (DyingOrDead()) + return; + + GetWeapon()->Update(m_audioEntityId, nil); + switch (m_nPedState) { + case PED_WANDER_RANGE: + case PED_WANDER_PATH: + if (IsVisible()) + ScanForInterestingStuff(); + break; + case PED_SEEK_ENTITY: + if (!m_pSeekTarget) { + RestorePreviousState(); + break; + } + m_vecSeekPos = m_pSeekTarget->GetPosition(); + + // fall through + case PED_SEEK_POS: + if (Seek()) { + if ((m_objective == OBJECTIVE_GOTO_AREA_ON_FOOT || m_objective == OBJECTIVE_RUN_TO_AREA || m_objective == OBJECTIVE_SPRINT_TO_AREA || + IsUseAttractorObjective(m_objective)) && m_pNextPathNode) { + m_pNextPathNode = nil; + + } else if (bRunningToPhone) { + if (gPhoneInfo.m_aPhones[m_phoneId].m_nState != PHONE_STATE_FREE) { + RestorePreviousState(); + m_phoneId = -1; + } else { + gPhoneInfo.m_aPhones[m_phoneId].m_nState = PHONE_STATE_REPORTING_CRIME; + SetPedState(PED_FACE_PHONE); + } + } else if (m_objective != OBJECTIVE_KILL_CHAR_ANY_MEANS && m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT) { + if (m_pedInObjective && m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION) { + if (m_moved.Magnitude() == 0.0f) { + if (m_pedInObjective->m_nMoveState == PEDMOVE_STILL) + m_fRotationDest = m_pedInObjective->m_fRotationCur; + } + } else if (m_objective == OBJECTIVE_GOTO_CHAR_ON_FOOT + && m_pedInObjective && m_pedInObjective->m_nMoveState != PEDMOVE_STILL) { + SetMoveState(m_pedInObjective->m_nMoveState); + } else if (m_objective == OBJECTIVE_GOTO_AREA_ON_FOOT || m_objective == OBJECTIVE_RUN_TO_AREA || m_objective == OBJECTIVE_SPRINT_TO_AREA || + IsUseAttractorObjective(m_objective)) { + SetIdle(); + } else { + RestorePreviousState(); + } + } + } + break; + case PED_FACE_PHONE: + if (FacePhone()) + SetPedState(PED_MAKE_CALL); + break; + case PED_MAKE_CALL: + if (MakePhonecall()) + SetWanderPath(CGeneral::GetRandomNumber() & 7); + break; + case PED_MUG: + Mug(); + break; + case PED_SOLICIT: + Solicit(); + break; + case PED_UNKNOWN: + { + int pedsInSameState = 0; + Idle(); + for (int i = 0; i < m_numNearPeds; ++i) { + CPed *nearPed = m_nearPeds[i]; + if (nearPed->m_nPedType == m_nPedType && nearPed->m_nPedState == PED_UNKNOWN) { + ++pedsInSameState; + } + } + if (pedsInSameState < 5) { + for (int j = 0; j < m_numNearPeds; ++j) { + CPed *nearPed = m_nearPeds[j]; + if (nearPed->m_nPedType == m_nPedType && nearPed->m_nPedState == PED_WANDER_PATH) { + nearPed->SetPedState(PED_UNKNOWN); + } + } + } + break; + } + case PED_DRIVING: + if (m_nPedType != PEDTYPE_PROSTITUTE) + break; + + if (CWorld::Players[CWorld::PlayerInFocus].m_pHooker != this) + break; + + if (CTimer::GetTimeInMilliseconds() > CWorld::Players[CWorld::PlayerInFocus].m_nNextSexFrequencyUpdateTime) { + if (m_nPedState == PED_DRIVING + && m_pMyVehicle->pDriver && m_pMyVehicle->pDriver->IsPlayer() && m_pMyVehicle->pDriver->m_nPedState == PED_DRIVING) { + CColPoint foundCol; + CEntity* foundEnt; + + CWorld::ProcessVerticalLine(m_pMyVehicle->GetPosition(), -100.0f, + foundCol, foundEnt, true, false, false, false, false, false, nil); + + if (m_pMyVehicle->m_vecMoveSpeed.MagnitudeSqr() < sq(0.01f) + && foundCol.surfaceB != SURFACE_DEFAULT && foundCol.surfaceB != SURFACE_TARMAC && foundCol.surfaceB != SURFACE_PAVEMENT) { + + if (m_pMyVehicle->CarHasRoof()) { + m_pMyVehicle->ApplyTurnForce(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(-0.8f, -1.2f) * m_fMass, + GetPosition().x - m_pMyVehicle->GetPosition().x, GetPosition().y - m_pMyVehicle->GetPosition().y, 0.0f); + + DMAudio.PlayOneShot(m_pMyVehicle->m_audioEntityId, SOUND_CAR_JERK, 0.0f); + m_pMyVehicle->pDriver->Say(SOUND_PED_PLAYER_BEFORESEX); + Say(SOUND_PED_PLAYER_BEFORESEX); + + int playerSexFrequency = CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency; + if (CWorld::Players[CWorld::PlayerInFocus].m_nMoney >= 10 && playerSexFrequency > 250) { + CWorld::Players[CWorld::PlayerInFocus].m_nNextSexFrequencyUpdateTime = CTimer::GetTimeInMilliseconds() + playerSexFrequency; + if (playerSexFrequency >= 350) { + CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency = Max(250, playerSexFrequency - 30); + } else { + CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency = Max(250, playerSexFrequency - 10); + } + + m_pMyVehicle->pDriver->m_fHealth = Min(CWorld::Players[0].m_nMaxHealth + 25.0f, 1.0f + m_pMyVehicle->pDriver->m_fHealth); + if (CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency == 250) + CWorld::Players[CWorld::PlayerInFocus].m_nNextSexFrequencyUpdateTime = CTimer::GetTimeInMilliseconds() + 3000; + } else { + bWanderPathAfterExitingCar = true; + CWorld::Players[CWorld::PlayerInFocus].m_pHooker = nil; + ClearLeader(); + SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); + m_pMyVehicle->pDriver->Say(SOUND_PED_PLAYER_AFTERSEX); + } + } else { + bWanderPathAfterExitingCar = true; + CWorld::Players[CWorld::PlayerInFocus].m_pHooker = nil; + m_pMyVehicle->pDriver->m_fHealth = CWorld::Players[0].m_nMaxHealth + 25.0f; + ClearLeader(); + SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); + m_pMyVehicle->pDriver->Say(SOUND_PED_PLAYER_AFTERSEX); + } + } else { + CWorld::Players[CWorld::PlayerInFocus].m_nNextSexFrequencyUpdateTime = CTimer::GetTimeInMilliseconds() + 3000; + int playerSexFrequency = CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency; + if (playerSexFrequency >= 1000 || playerSexFrequency <= 250) + CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency = 1200; + else + CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency = 250; + } + } else { + bWanderPathAfterExitingCar = true; + CWorld::Players[CWorld::PlayerInFocus].m_pHooker = nil; + ClearLeader(); + SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); + } + } + + if (CTimer::GetTimeInMilliseconds() > CWorld::Players[CWorld::PlayerInFocus].m_nNextSexMoneyUpdateTime) { + int playerMoney = CWorld::Players[CWorld::PlayerInFocus].m_nMoney; + if (playerMoney <= 1) { + CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency = 250; + } else { + CWorld::Players[CWorld::PlayerInFocus].m_nMoney = Max(0, playerMoney - 1); + } + CWorld::Players[CWorld::PlayerInFocus].m_nNextSexMoneyUpdateTime = CTimer::GetTimeInMilliseconds() + 1000; + } + break; + default: + break; + } + if (IsPedInControl()) + CivilianAI(); + + if (CharCreatedBy == RANDOM_CHAR) { + EnterVacantNearbyCars(); + UseNearbyAttractors(); + } + + if (CTimer::GetTimeInMilliseconds() > m_timerUnused) { + m_stateUnused = 0; + m_timerUnused = 0; + } + + if (m_moved.Magnitude() > 0.0f) + Avoid(); +} + +bool +CPed::RunToReportCrime(eCrimeType crimeToReport) +{ + // They changed true into false to make this function unusable. So running to phone actually starts but first frame after that cancels it. + if (m_nPedState == PED_SEEK_POS) + return false; + + CVector pos = GetPosition(); + int phoneId = gPhoneInfo.FindNearestFreePhone(&pos); + + if (phoneId == -1) + return false; + + CPhone* phone = &gPhoneInfo.m_aPhones[phoneId]; + if (phone->m_nState != PHONE_STATE_FREE) + return false; + + bRunningToPhone = true; + SetSeek(phone->m_vecPos, 0.3f); + SetMoveState(PEDMOVE_RUN); + m_phoneId = phoneId; + m_crimeToReportOnPhone = crimeToReport; + return true; +} + +const int32 gFrequencyOfAttractorAttempt = 11; +const float gDistanceToSeekAttractors = 50.0f; +const float gMaxDistanceToAttract = 10.0f; + +/* Probably this was inlined */ +void CCivilianPed::FindNearbyAttractorsSectorList(CPtrList& list, float& minDistance, C2dEffect*& pClosestAttractor, CEntity*& pAttractorEntity) +{ + for (CPtrNode* pNode = list.first; pNode != nil; pNode = pNode->next) { + CEntity* pEntity = (CEntity*)pNode->item; + if (pEntity->IsObject() && (!pEntity->GetIsStatic() || ((CObject*)pEntity)->bHasBeenDamaged)) + continue; + CBaseModelInfo* pModelInfo = CModelInfo::GetModelInfo(pEntity->GetModelIndex()); + for (int i = 0; i < pModelInfo->GetNum2dEffects(); i++) { + C2dEffect* pEffect = pModelInfo->Get2dEffect(i); + if (pEffect->type != EFFECT_PED_ATTRACTOR) + continue; + if (!IsAttractedTo(pEffect->pedattr.type)) + continue; + CVector pos; + CPedAttractorManager::ComputeEffectPos(pEffect, pEntity->GetMatrix(), pos); + if ((pos - GetPosition()).MagnitudeSqr() < minDistance) { + CPedAttractorManager* pManager = GetPedAttractorManager(); + if (pManager->HasEmptySlot(pEffect) && pManager->IsApproachable(pEffect, pEntity->GetMatrix(), 0, this)) { + pClosestAttractor = pEffect; + pAttractorEntity = pEntity; + minDistance = (pos - GetPosition()).MagnitudeSqr(); + } + } + } + } +} + +void CCivilianPed::UseNearbyAttractors() +{ + if (CWeather::Rain < 0.2f && !m_bAttractorUnk) + return; + if (HasAttractor()) + return; + if (m_nAttractorCycleState != gFrequencyOfAttractorAttempt) { + m_nAttractorCycleState++; + return; + } + m_nAttractorCycleState = 0; + if (!IsPedInControl()) + return; + if (m_nPedState == PED_FLEE_ENTITY) + return; + + float left = GetPosition().x - gDistanceToSeekAttractors; + float right = GetPosition().x + gDistanceToSeekAttractors; + float top = GetPosition().y - gDistanceToSeekAttractors; + float bottom = GetPosition().y + gDistanceToSeekAttractors; + int xstart = Max(0, CWorld::GetSectorIndexX(left)); + int xend = Min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(right)); + int ystart = Max(0, CWorld::GetSectorIndexY(top)); + int yend = Min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(bottom)); + assert(xstart <= xend); + assert(ystart <= yend); + + float minDistance = SQR(gMaxDistanceToAttract); + C2dEffect* pClosestAttractor = nil; + CEntity* pAttractorEntity = nil; + + for (int y = ystart; y <= yend; y++) { + for (int x = xstart; x <= xend; x++) { + CSector* s = CWorld::GetSector(x, y); + FindNearbyAttractorsSectorList(s->m_lists[ENTITYLIST_BUILDINGS], minDistance, pClosestAttractor, pAttractorEntity); + FindNearbyAttractorsSectorList(s->m_lists[ENTITYLIST_OBJECTS], minDistance, pClosestAttractor, pAttractorEntity); + } + } + if (pClosestAttractor) + GetPedAttractorManager()->RegisterPedWithAttractor(this, pClosestAttractor, pAttractorEntity->GetMatrix()); +} + +bool CCivilianPed::IsAttractedTo(int8 type) +{ + switch (type) { + case ATTRACTOR_ATM: return true; + case ATTRACTOR_SEAT: return true; + case ATTRACTOR_STOP: return true; + case ATTRACTOR_PIZZA: return true; + case ATTRACTOR_SHELTER: return CWeather::Rain >= 0.2f; + case ATTRACTOR_ICECREAM: return false; + } + return false; +} + +void +CCivilianPed::EnterVacantNearbyCars(void) +{ + if (!m_bLookForVacantCars) + return; + + if (m_bJustStoleACar && bInVehicle && m_carInObjective == m_pMyVehicle) { + m_bJustStoleACar = false; + m_pMyVehicle->SetStatus(STATUS_PHYSICS); + m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; + m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 10; + m_pMyVehicle->bEngineOn = true; + + } else if (!bHasAlreadyStoleACar) { + if (m_nLookForVacantCarsCounter == 8) { + m_nLookForVacantCarsCounter = 0; + if (IsPedInControl() && m_objective == OBJECTIVE_NONE) { + + CVehicle *foundCar = nil; + float closestDist = FLT_MAX; + int minX = CWorld::GetSectorIndexX(GetPosition().x - 10.0f); + if (minX < 0) minX = 0; + int minY = CWorld::GetSectorIndexY(GetPosition().y - 10.0f); + if (minY < 0) minY = 0; + int maxX = CWorld::GetSectorIndexX(GetPosition().x + 10.0f); + if (maxX > NUMSECTORS_X - 1) maxX = NUMSECTORS_X - 1; + int maxY = CWorld::GetSectorIndexY(GetPosition().y + 10.0f); + if (maxY > NUMSECTORS_Y - 1) maxY = NUMSECTORS_Y - 1; + + for (int curY = minY; curY <= maxY; curY++) { + for (int curX = minX; curX <= maxX; curX++) { + CSector* sector = CWorld::GetSector(curX, curY); + for (CPtrNode* node = sector->m_lists[ENTITYLIST_VEHICLES].first; node; node = node->next) { + CVehicle* veh = (CVehicle*)node->item; + if (veh && veh->IsCar()) { + + // Looks like PARKED_VEHICLE condition isn't there in Mobile. + if (veh->VehicleCreatedBy == RANDOM_VEHICLE || veh->VehicleCreatedBy == PARKED_VEHICLE) { + if (IsOnStealWishList(veh->GetModelIndex()) && !veh->IsLawEnforcementVehicle() + && (m_bStealCarEvenIfThereIsSomeoneInIt || !veh->pDriver && !veh->m_nNumPassengers) + && !veh->m_nNumGettingIn && !veh->m_nGettingInFlags && !veh->m_nGettingOutFlags + && !veh->m_pCarFire && veh->m_fHealth > 800.0f + && !veh->IsUpsideDown() && !veh->IsOnItsSide() && veh->CanPedEnterCar()) { + float dist = (GetPosition() - veh->GetPosition()).MagnitudeSqr(); + if (dist < sq(10.0f) && dist < closestDist && veh->IsClearToDriveAway()) { + foundCar = veh; + closestDist = dist; + } + } + } + } + } + } + } + if (foundCar) { + m_bJustStoleACar = true; + bHasAlreadyStoleACar = true; + CCarCtrl::JoinCarWithRoadSystem(foundCar); + SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, foundCar); + SetObjectiveTimer(10000); + } + } + } else { + ++m_nLookForVacantCarsCounter; + } + } +} + +bool +CCivilianPed::IsOnStealWishList(int32 model) +{ + for (int i = 0; i < ARRAY_SIZE(m_nStealWishList); i++) { + if (model == m_nStealWishList[i]) { + return true; + } + } + return false; +} \ No newline at end of file diff --git a/src/miami/peds/CivilianPed.h b/src/miami/peds/CivilianPed.h new file mode 100644 index 00000000..dcd49a96 --- /dev/null +++ b/src/miami/peds/CivilianPed.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Ped.h" + +class CCivilianPed : public CPed +{ + bool m_bLookForVacantCars; + uint32 m_nLookForVacantCarsCounter; + bool m_bStealCarEvenIfThereIsSomeoneInIt; // unused + bool m_bJustStoleACar; + uint32 m_nStealWishList[16]; + bool m_bAttractorUnk; + int32 m_nAttractorCycleState; +public: + CCivilianPed(ePedType, uint32); + ~CCivilianPed(void) { } + + void CivilianAI(void); + void ProcessControl(void); + void UseNearbyAttractors(void); + void FindNearbyAttractorsSectorList(CPtrList&, float&, C2dEffect*&, CEntity*&); + bool IsAttractedTo(int8); + void EnterVacantNearbyCars(void); + bool IsOnStealWishList(int32); +}; +//VALIDATE_SIZE(CCivilianPed, 0x53C); diff --git a/src/miami/peds/CopPed.cpp b/src/miami/peds/CopPed.cpp new file mode 100644 index 00000000..1efd7733 --- /dev/null +++ b/src/miami/peds/CopPed.cpp @@ -0,0 +1,874 @@ +#include "common.h" + +#include "World.h" +#include "PlayerPed.h" +#include "CopPed.h" +#include "Wanted.h" +#include "DMAudio.h" +#include "ModelIndices.h" +#include "Vehicle.h" +#include "RpAnimBlend.h" +#include "AnimBlendAssociation.h" +#include "General.h" +#include "ZoneCull.h" +#include "PathFind.h" +#include "RoadBlocks.h" +#include "CarCtrl.h" +#include "Renderer.h" +#include "Camera.h" +#include "PedPlacement.h" +#include "Ropes.h" +#include "Stinger.h" + +CCopPed::CCopPed(eCopType copType, int32 modifier) : CPed(PEDTYPE_COP) +{ + m_nCopType = copType; + switch (copType) { + case COP_STREET: + SetModelIndex(MI_COP); + GiveWeapon(WEAPONTYPE_NIGHTSTICK, 1000, true); + GiveDelayedWeapon(WEAPONTYPE_COLT45, 1000); + m_currentWeapon = WEAPONTYPE_UNARMED; + m_fArmour = 0.0f; + m_wepSkills = 208; /* TODO: what is this? seems unused */ + m_wepAccuracy = 60; + break; + case COP_FBI: + SetModelIndex(MI_FBI); + GiveDelayedWeapon(WEAPONTYPE_MP5, 1000); + SetCurrentWeapon(WEAPONTYPE_MP5); + m_fArmour = 100.0f; + m_wepSkills = 176; /* TODO: what is this? seems unused */ + m_wepAccuracy = 76; + break; + case COP_SWAT: + case COP_HELI_SWAT: + SetModelIndex(MI_SWAT); + GiveDelayedWeapon(WEAPONTYPE_UZI, 1000); + SetCurrentWeapon(WEAPONTYPE_UZI); + m_fArmour = 50.0f; + m_wepSkills = 32; /* TODO: what is this? seems unused */ + m_wepAccuracy = 68; + break; + case COP_ARMY: + SetModelIndex(MI_ARMY); + GiveDelayedWeapon(WEAPONTYPE_MP5, 1000); + SetCurrentWeapon(WEAPONTYPE_MP5); + m_fArmour = 100.0f; + m_wepSkills = 32; /* TODO: what is this? seems unused */ + m_wepAccuracy = 84; + break; + case COP_MIAMIVICE: + switch (modifier) { + case 0: SetModelIndex(MI_VICE1); break; + case 1: SetModelIndex(MI_VICE2); break; + case 2: SetModelIndex(MI_VICE3); break; + case 3: SetModelIndex(MI_VICE4); break; + case 4: SetModelIndex(MI_VICE5); break; + case 5: SetModelIndex(MI_VICE6); break; + case 6: SetModelIndex(MI_VICE7); break; + case 7: SetModelIndex(MI_VICE8); break; + default: assert(0); break; + } + GiveDelayedWeapon(WEAPONTYPE_UZI, 1000); + SetCurrentWeapon(WEAPONTYPE_UZI); + m_fArmour = 100.0f; + m_wepSkills = 176; + m_wepAccuracy = 76; + break; + } + m_bIsInPursuit = false; + field_5FE = 1; + m_bIsDisabledCop = false; + m_attackTimer = 0; + m_bBeatingSuspect = false; + m_bStopAndShootDisabledZone = false; + m_bDragsPlayerFromCar = false; + m_bZoneDisabled = false; + field_628 = -1; + m_nRoadblockVeh = nil; + m_bThrowsSpikeTrap = false; + m_pRopeEntity = nil; + m_fAbseilPos = 0.0f; + m_nHassleTimer = 0; + field_61C = 0; + field_624 = 0; + m_pStinger = new CStinger(); + SetWeaponLockOnTarget(nil); +} + +CCopPed::~CCopPed() +{ + ClearPursuit(); + m_pStinger->Remove(); + delete m_pStinger; +} + +// Parameter should always be CPlayerPed, but it seems they considered making civilians arrestable at some point +void +CCopPed::SetArrestPlayer(CPed *player) +{ + if (!IsPedInControl() || !player) + return; + + player->Say(SOUND_PED_PLAYER_REACTTOCOP); + Say(SOUND_PED_ARREST_COP); + + if (player->EnteringCar()) { + if (CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) + return; + + // why? + player->bGonnaKillTheCarJacker = true; + + // Genius + FindPlayerPed()->m_bCanBeDamaged = false; + ((CPlayerPed*)player)->m_pArrestingCop = this; + this->RegisterReference((CEntity**) &((CPlayerPed*)player)->m_pArrestingCop); + + } else if (player->m_nPedState != PED_DIE && player->m_nPedState != PED_DEAD && player->m_nPedState != PED_ARRESTED) { + player->m_nLastPedState = player->m_nPedState; + player->SetPedState(PED_ARRESTED); + + FindPlayerPed()->m_bCanBeDamaged = false; + ((CPlayerPed*)player)->m_pArrestingCop = this; + this->RegisterReference((CEntity**) &((CPlayerPed*)player)->m_pArrestingCop); + } + + SetPedState(PED_ARREST_PLAYER); + SetObjective(OBJECTIVE_NONE); + m_prevObjective = OBJECTIVE_NONE; + bIsPointingGunAt = false; + m_pSeekTarget = player; + m_pSeekTarget->RegisterReference((CEntity**) &m_pSeekTarget); + SetCurrentWeapon(WEAPONTYPE_COLT45); + if (player->InVehicle()) { + player->m_pMyVehicle->m_nNumGettingIn = 0; + player->m_pMyVehicle->m_nGettingInFlags = 0; + player->m_pMyVehicle->bIsHandbrakeOn = true; + player->m_pMyVehicle->SetStatus(STATUS_PLAYER_DISABLED); + } + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED || GetWeapon()->m_eWeaponType == WEAPONTYPE_BRASSKNUCKLE) + SetCurrentWeapon(WEAPONTYPE_COLT45); +} + +void +CCopPed::ClearPursuit(void) +{ + CPlayerPed *player = FindPlayerPed(); + if (!player) + return; + + CWanted *wanted = player->m_pWanted; + int ourCopId = 0; + bool foundMyself = false; + int biggestCopId = 0; + if (!m_bIsInPursuit) + return; + + m_bIsInPursuit = false; + for (int i = 0; i < Max(wanted->m_MaxCops, wanted->m_CurrentCops); ++i) { + if (!foundMyself && wanted->m_pCops[i] == this) { + wanted->m_pCops[i] = nil; + --wanted->m_CurrentCops; + foundMyself = true; + ourCopId = i; + biggestCopId = i; + } else { + if (wanted->m_pCops[i]) + biggestCopId = i; + } + } + if (foundMyself && biggestCopId > ourCopId) { + wanted->m_pCops[ourCopId] = wanted->m_pCops[biggestCopId]; + wanted->m_pCops[biggestCopId] = nil; + } + m_objective = OBJECTIVE_NONE; + m_prevObjective = OBJECTIVE_NONE; + m_nLastPedState = PED_NONE; + bIsRunning = false; + bNotAllowedToDuck = false; + bKindaStayInSamePlace = false; + m_bStopAndShootDisabledZone = false; + m_bDragsPlayerFromCar = false; + m_bZoneDisabled = false; + ClearObjective(); + if (IsPedInControl()) { + if (!m_pMyVehicle || wanted->GetWantedLevel() != 0) { + if (m_pMyVehicle && (m_pMyVehicle->GetPosition() - GetPosition()).MagnitudeSqr() < sq(5.0f)) { + m_nLastPedState = PED_IDLE; + SetSeek((CEntity*)m_pMyVehicle, 2.5f); + } else { + m_nLastPedState = PED_WANDER_PATH; + SetFindPathAndFlee(FindPlayerPed()->GetPosition(), 10000, true); + } + } else { + SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pMyVehicle); + } + } +} + +// TODO: I don't know why they needed that parameter. +void +CCopPed::SetPursuit(bool ignoreCopLimit) +{ + if (CTimer::GetTimeInMilliseconds() < field_61C) + return; + + CWanted *wanted = FindPlayerPed()->m_pWanted; + if (m_bIsInPursuit || !IsPedInControl()) + return; + + if (wanted->m_CurrentCops < wanted->m_MaxCops || ignoreCopLimit) { + for (int i = 0; i < wanted->m_MaxCops; ++i) { + if (!wanted->m_pCops[i]) { + m_bIsInPursuit = true; + ++wanted->m_CurrentCops; + wanted->m_pCops[i] = this; + break; + } + } + if (m_bIsInPursuit) { + ClearObjective(); + m_prevObjective = OBJECTIVE_NONE; + SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, FindPlayerPed()); + SetObjectiveTimer(0); + bNotAllowedToDuck = true; + bIsRunning = true; + m_bStopAndShootDisabledZone = false; + } + } +} + +void +CCopPed::ArrestPlayer(void) +{ + m_pVehicleAnim = nil; + CPed *suspect = (CPed*)m_pSeekTarget; + if (suspect) { + if (suspect->CanSetPedState()) + suspect->SetPedState(PED_ARRESTED); + + if (suspect->bInVehicle && m_pMyVehicle && suspect->m_pMyVehicle == m_pMyVehicle) { + + // BUG? I will never understand why they used LINE_UP_TO_CAR_2... + LineUpPedWithCar(LINE_UP_TO_CAR_2); + } + + if (suspect && (suspect->m_nPedState == PED_ARRESTED || suspect->DyingOrDead() || suspect->EnteringCar())) { + + CAnimBlendAssociation *arrestAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ARREST); + if (!arrestAssoc || arrestAssoc->blendDelta < 0.0f) + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ARREST, 4.0f); + + CVector suspMidPos; + suspect->m_pedIK.GetComponentPosition(suspMidPos, PED_MID); + m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(suspMidPos.x, suspMidPos.y, + GetPosition().x, GetPosition().y); + + m_fRotationCur = m_fRotationDest; + SetOrientation(0.0f, 0.0f, m_fRotationCur); + } else { + ClearPursuit(); + } + } else { + ClearPursuit(); + } +} + +void +CCopPed::ScanForCrimes(void) +{ + CVehicle *playerVeh = FindPlayerVehicle(); + + // Look for car alarms + if (playerVeh && playerVeh->IsCar()) { + if (playerVeh->IsAlarmOn()) { + if ((playerVeh->GetPosition() - GetPosition()).MagnitudeSqr() < sq(20.0f)) + FindPlayerPed()->SetWantedLevelNoDrop(1); + } + } + + // Look for stolen cop cars + if (!m_bIsInPursuit) { + CPlayerPed *player = FindPlayerPed(); + if ((m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER) + && player->m_pWanted->GetWantedLevel() == 0) { + + if (player->m_pMyVehicle +#ifdef FIX_BUGS + && m_pMyVehicle == player->m_pMyVehicle +#endif + && player->m_pMyVehicle->bIsLawEnforcer) + player->SetWantedLevelNoDrop(1); + } + } +} + +void +CCopPed::CopAI(void) +{ + CWanted *wanted = FindPlayerPed()->m_pWanted; + int wantedLevel = wanted->GetWantedLevel(); + CPhysical *playerOrHisVeh = FindPlayerVehicle() ? (CPhysical*)FindPlayerVehicle() : (CPhysical*)FindPlayerPed(); + + if (wanted->m_bIgnoredByEveryone || wanted->m_bIgnoredByCops) { + if (m_nPedState != PED_ARREST_PLAYER) + ClearPursuit(); + + return; + } + if (CCullZones::NoPolice() && m_bIsInPursuit && !m_bIsDisabledCop) { + if (bHitSomethingLastFrame) { + m_bZoneDisabled = true; + m_bIsDisabledCop = true; + bKindaStayInSamePlace = true; + bIsRunning = false; + bNotAllowedToDuck = false; + bCrouchWhenShooting = false; + SetIdle(); + ClearObjective(); + ClearPursuit(); + m_prevObjective = OBJECTIVE_NONE; + m_nLastPedState = PED_NONE; + SetAttackTimer(0); + + // Safe distance for disabled zone? Or to just make game easier? + if (m_fDistanceToTarget > 15.0f) + m_bStopAndShootDisabledZone = true; + } + } else if (m_bZoneDisabled && !CCullZones::NoPolice()) { + m_bZoneDisabled = false; + m_bIsDisabledCop = false; + m_bStopAndShootDisabledZone = false; + bKindaStayInSamePlace = false; + bCrouchWhenShooting = false; + bDuckAndCover = false; + ClearPursuit(); + } + if (wantedLevel > 0) { + if (!m_bIsDisabledCop) { + // Turn and shoot the player's vehicle, if possible + if (!m_bIsInPursuit && !GetWeapon()->IsTypeMelee() && FindPlayerVehicle() && m_fDistanceToTarget < CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_fRange) { + if (FindPlayerVehicle()->m_vecMoveSpeed.Magnitude2D() > 0.1f) { + CVector2D distToVeh = GetPosition() - FindPlayerVehicle()->GetPosition(); + distToVeh.Normalise(); + CVector2D vehSpeed = FindPlayerVehicle()->m_vecMoveSpeed; + vehSpeed.Normalise(); + + if (DotProduct2D(distToVeh, vehSpeed) > 0.8f) { + SetLookFlag(playerOrHisVeh, true); + SetMoveState(PEDMOVE_STILL); + if (TurnBody()) { + SetAttack(FindPlayerVehicle()); + SetShootTimer(CGeneral::GetRandomNumberInRange(500.0f, 1000.0f)); + SetAttackTimer(CGeneral::GetRandomNumberInRange(200.0f, 300.0f)); + } + } else if (m_nPedState == PED_ATTACK) + RestorePreviousState(); + } + } + + if (!m_bIsInPursuit || wanted->m_CurrentCops > wanted->m_MaxCops) { + CCopPed *copFarthestToTarget = nil; + float copFarthestToTargetDist = m_fDistanceToTarget; + + int oldCopNum = wanted->m_CurrentCops; + int maxCops = wanted->m_MaxCops; + + for (int i = 0; i < Max(maxCops, oldCopNum); i++) { + CCopPed *cop = wanted->m_pCops[i]; + if (cop && cop->m_fDistanceToTarget > copFarthestToTargetDist) { + copFarthestToTargetDist = cop->m_fDistanceToTarget; + copFarthestToTarget = wanted->m_pCops[i]; + } + } + + if (m_bIsInPursuit) { + if (copFarthestToTarget && oldCopNum > maxCops) { + if (copFarthestToTarget == this && m_fDistanceToTarget > 10.0f) { + ClearPursuit(); + } else if(copFarthestToTargetDist > 10.0f) + copFarthestToTarget->ClearPursuit(); + } + } else { + if (oldCopNum < maxCops) { + SetPursuit(true); + } else { + if (m_fDistanceToTarget <= 10.0f || copFarthestToTarget && m_fDistanceToTarget < copFarthestToTargetDist) { + if (copFarthestToTarget && copFarthestToTargetDist > 10.0f) + copFarthestToTarget->ClearPursuit(); + + SetPursuit(true); + } + } + } + } else + SetPursuit(false); + + if (!m_bIsInPursuit) + return; + + if (wantedLevel > 1 && GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED) + SetCurrentWeapon(WEAPONTYPE_COLT45); + else if (wantedLevel == 1 && GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED && !FindPlayerPed()->m_pCurrentPhysSurface) { + // i.e. if player is on top of car, cop will still use colt45. + SetCurrentWeapon(GetWeaponSlot(WEAPONTYPE_NIGHTSTICK) >= 0 ? WEAPONTYPE_NIGHTSTICK : WEAPONTYPE_UNARMED); + } + + if (m_bBeatingSuspect && GetWeapon()->m_eWeaponType == WEAPONTYPE_NIGHTSTICK) + Say(SOUND_PED_PULLOUTWEAPON); + + if (FindPlayerVehicle()) { + if (m_bBeatingSuspect) { + --wanted->m_CopsBeatingSuspect; + m_bBeatingSuspect = false; + } + if (m_fDistanceToTarget * FindPlayerSpeed().Magnitude() > 4.0f) + ClearPursuit(); + } + return; + } + SetCurrentWeapon(WEAPONTYPE_COLT45); + CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + float weaponRange = weaponInfo->m_fRange; + SetLookFlag(playerOrHisVeh, true); + TurnBody(); + if (!bIsDucking || bCrouchWhenShooting && GetCrouchFireAnim(weaponInfo)) { + if (m_attackTimer >= CTimer::GetTimeInMilliseconds()) { + if (m_nPedState != PED_ATTACK && m_nPedState != PED_FIGHT && !m_bZoneDisabled) { + CVector targetDist = playerOrHisVeh->GetPosition() - GetPosition(); + if (m_fDistanceToTarget > 30.0f) { + if (bIsDucking) + ClearDuck(); + + // Target is coming onto us + if (DotProduct(playerOrHisVeh->m_vecMoveSpeed, targetDist) > 0.0f) { + m_bIsDisabledCop = false; + bKindaStayInSamePlace = false; + bNotAllowedToDuck = false; + bDuckAndCover = false; + SetPursuit(false); + SetObjective(OBJECTIVE_KILL_CHAR_ANY_MEANS, FindPlayerPed()); + } + } else if (m_fDistanceToTarget < 5.0f + && (!FindPlayerVehicle() || FindPlayerVehicle()->m_vecMoveSpeed.MagnitudeSqr() < sq(1.f/200.f))) { + m_bIsDisabledCop = false; + bKindaStayInSamePlace = false; + bNotAllowedToDuck = false; + bDuckAndCover = false; + } else { + float dotProd; + if (m_nRoadblockVeh) { + dotProd = DotProduct2D(playerOrHisVeh->GetPosition() - m_nRoadblockVeh->GetPosition(), GetPosition() - m_nRoadblockVeh->GetPosition()); + } else + dotProd = -1.0f; + + if(dotProd < 0.0f) { + if (bIsDucking) + ClearDuck(); + m_bIsDisabledCop = false; + bKindaStayInSamePlace = false; + bNotAllowedToDuck = false; + bCrouchWhenShooting = false; + bDuckAndCover = false; + SetPursuit(false); + } else { + bIsPointingGunAt = true; + } + } + } + } else { + if (m_fDistanceToTarget < weaponRange) { + CVector gunPos = weaponInfo->m_vecFireOffset; + TransformToNode(gunPos, PED_HANDR); + + CColPoint foundCol; + CEntity *foundEnt; + if (!CWorld::ProcessLineOfSight(gunPos, playerOrHisVeh->GetPosition(), foundCol, foundEnt, + false, true, false, false, true, false, false) + || foundEnt && foundEnt == playerOrHisVeh) { + + SetWeaponLockOnTarget(playerOrHisVeh); + SetAttack(playerOrHisVeh); + SetShootTimer(CGeneral::GetRandomNumberInRange(500, 1000)); + } + SetAttackTimer(CGeneral::GetRandomNumberInRange(200, 300)); + } + SetMoveState(PEDMOVE_STILL); + } + } + } else { + if (!m_bIsDisabledCop || m_bZoneDisabled) { + if (m_nPedState != PED_AIM_GUN) { + if (m_bIsInPursuit) + ClearPursuit(); + + if (IsPedInControl()) { + // Entering the vehicle + if (m_pMyVehicle && !bInVehicle) { + if (m_pMyVehicle->IsLawEnforcementVehicle()) { + if (m_pMyVehicle->pDriver) { + if (m_pMyVehicle->pDriver->m_nPedType == PEDTYPE_COP) { + if (m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER) + SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, m_pMyVehicle); + } else if (m_pMyVehicle->pDriver->IsPlayer()) { + FindPlayerPed()->SetWantedLevelNoDrop(1); + } + } else if (m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER) { + SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pMyVehicle); + } + } else { + m_pMyVehicle = nil; + ClearObjective(); + SetWanderPath(CGeneral::GetRandomNumber() & 7); + } + } else { + if (m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT && m_objective != OBJECTIVE_HASSLE_CHAR && CharCreatedBy == RANDOM_CHAR) { + for (int i = 0; i < m_numNearPeds; i++) { + CPed *nearPed = m_nearPeds[i]; + if (nearPed->CharCreatedBy == RANDOM_CHAR) { + if ((nearPed->m_nPedType == PEDTYPE_CRIMINAL || nearPed->IsGangMember()) + && nearPed->IsPedInControl()) { + + bool anotherCopChasesHim = false; + if (nearPed->m_nPedState == PED_FLEE_ENTITY) { + if (nearPed->m_fleeFrom && nearPed->m_fleeFrom->IsPed() && + ((CPed*)nearPed->m_fleeFrom)->m_nPedType == PEDTYPE_COP) { + anotherCopChasesHim = true; + } + } + if (!anotherCopChasesHim) { + SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, nearPed); + nearPed->SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, this); + nearPed->bBeingChasedByPolice = true; + return; + } + } else { + if (nearPed->m_nPedType != PEDTYPE_COP && !nearPed->IsPlayer() + && nearPed->IsPedInControl() && m_nHassleTimer < CTimer::GetTimeInMilliseconds()) { + + if (nearPed->m_objective == OBJECTIVE_NONE && nearPed->m_nPedState == PED_WANDER_PATH + && !nearPed->m_pLookTarget && nearPed->m_lookTimer < CTimer::GetTimeInMilliseconds()) { + + if ((GetPosition() - nearPed->GetPosition()).MagnitudeSqr() < sq(5.0f)) { + + if (CWorld::GetIsLineOfSightClear(GetPosition(), nearPed->GetPosition(), + true, false, false, false, false, false, false)) { + Say(SOUND_PED_COP_ASK_FOR_ID); + SetObjective(OBJECTIVE_HASSLE_CHAR, nearPed); + nearPed->SetObjective(OBJECTIVE_WAIT_ON_FOOT_FOR_COP, this); + m_nHassleTimer = CTimer::GetTimeInMilliseconds() + 100000; + } + } + } + } + } + } + } + } + } + } + } + } else { + if (m_bIsInPursuit && m_nPedState != PED_AIM_GUN) + ClearPursuit(); + + m_bIsDisabledCop = false; + bKindaStayInSamePlace = false; + bNotAllowedToDuck = false; + bCrouchWhenShooting = false; + bDuckAndCover = false; + if (bIsDucking) + ClearDuck(); + if (m_pMyVehicle) + SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pMyVehicle); + } + } +} + +void +CCopPed::ProcessControl(void) +{ + if (m_nCopType == COP_HELI_SWAT) + ProcessHeliSwat(); + + CPed::ProcessControl(); + + if (m_bThrowsSpikeTrap) { + if (CGame::currArea != AREA_MALL) + ProcessStingerCop(); + return; + } + + if (m_pStinger && m_pStinger->bIsDeployed && m_pStinger->m_nSpikeState == STINGERSTATE_DEPLOYED && CGame::currArea != AREA_MALL) + m_pStinger->Process(); + + if (bWasPostponed) + return; + + if (m_nPedState == PED_DEAD) { + ClearPursuit(); + m_objective = OBJECTIVE_NONE; + return; + } + if (m_nPedState == PED_DIE) + return; + + if (m_nPedState == PED_ARREST_PLAYER) { + ArrestPlayer(); + return; + } + GetWeapon()->Update(m_audioEntityId, nil); + if (m_moved.Magnitude() > 0.0f) + Avoid(); + + CPhysical *playerOrHisVeh = FindPlayerVehicle() ? (CPhysical*)FindPlayerVehicle() : (CPhysical*)FindPlayerPed(); + CPlayerPed *player = FindPlayerPed(); + + m_fDistanceToTarget = (playerOrHisVeh->GetPosition() - GetPosition()).Magnitude(); + if (player->m_nPedState == PED_ARRESTED || player->DyingOrDead()) { + if (m_fDistanceToTarget < 5.0f) { + SetArrestPlayer(player); + return; + } + if (IsPedInControl()) + SetIdle(); + } + + if (m_bIsInPursuit) { + if (player->m_nPedState != PED_ARRESTED && !player->DyingOrDead()) { + if (player->m_pWanted->m_CurrentCops == 1) { + Say(SOUND_PED_COP_ALONE); + } else { + int numCopsNear = 0; + for (int i = 0; i < player->m_numNearPeds; ++i) { + CPed *nearPed = player->m_nearPeds[i]; + if (nearPed->m_nPedType == PEDTYPE_COP && nearPed->m_nPedState != PED_DEAD) + ++numCopsNear; + } + if (numCopsNear <= 3) { + Say(SOUND_PED_COP_LITTLECOPSAROUND); + if (!player->bInVehicle) { + CVector distToPlayer = player->GetPosition() - GetPosition(); + if (distToPlayer.MagnitudeSqr() < sq(20.0f)) { + player->Say(SOUND_PED_PLAYER_FARFROMCOPS); + if (player->m_nPedState != PED_ATTACK && player->m_nPedState != PED_AIM_GUN) { + player->SetLookFlag(this, false); + player->SetLookTimer(1000); + } + } + } + } else if ((CGeneral::GetRandomNumber() % 16) == 1) { + Say(SOUND_PED_COP_MANYCOPSAROUND); + } + } + } + } + + if (IsPedInControl()) { + CopAI(); + /* switch (m_nCopType) + { + case COP_FBI: + CopAI(); + break; + case COP_SWAT: + CopAI(); + break; + case COP_ARMY: + CopAI(); + break; + default: + CopAI(); + break; + } */ + } else if (InVehicle()) { + if (m_pMyVehicle->pDriver == this && m_pMyVehicle->AutoPilot.m_nCarMission == MISSION_NONE && + CanPedDriveOff() && m_pMyVehicle->VehicleCreatedBy != MISSION_VEHICLE) { + + CCarCtrl::JoinCarWithRoadSystem(m_pMyVehicle); + m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; + m_pMyVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; + m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 17; + } + } + if (IsPedInControl() || m_nPedState == PED_DRIVING) + ScanForCrimes(); + + // They may have used goto to jump here in case of PED_ATTACK. + if (m_nPedState == PED_IDLE || m_nPedState == PED_ATTACK) { + if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT && + player && player->EnteringCar() && m_fDistanceToTarget < 1.3f) { + SetArrestPlayer(player); + } + } else { + if (m_nPedState == PED_SEEK_POS) { + if (player->m_nPedState == PED_ARRESTED) { + SetIdle(); + SetLookFlag(player, false); + SetLookTimer(1000); + RestorePreviousObjective(); + } else { + if (player->m_pMyVehicle && player->m_pMyVehicle->m_nNumGettingIn != 0) { + m_distanceToCountSeekDone = 1.3f; + } + + if (!bDuckAndCover && Seek()) { + CVehicle *playerVeh = FindPlayerVehicle(); + if (!playerVeh && player && player->EnteringCar()) { + SetArrestPlayer(player); + } else if (1.5f + GetPosition().z <= m_vecSeekPos.z || GetPosition().z - 0.3f >= m_vecSeekPos.z) { + SetMoveState(PEDMOVE_STILL); + } else if (playerVeh && playerVeh->CanPedEnterCar() && playerVeh->m_nNumGettingIn == 0) { + SetCarJack(playerVeh); + } + } + } + } else if (m_nPedState == PED_SEEK_ENTITY) { + if (!m_pSeekTarget) { + RestorePreviousState(); + } else { + m_vecSeekPos = m_pSeekTarget->GetPosition(); + if (Seek()) { + if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT && m_fDistanceToTarget < 2.5f && player) { + if (player->m_nPedState == PED_ARRESTED || player->m_nPedState == PED_ENTER_CAR || + (player->m_nPedState == PED_CARJACK && m_fDistanceToTarget < 1.3f)) { + SetArrestPlayer(player); + } else + RestorePreviousState(); + } else { + RestorePreviousState(); + } + + } + } + } + } + + if (m_pPointGunAt) + Say(SOUND_PED_COP_TARGETING); + + if (m_bStopAndShootDisabledZone) { + bool dontShoot = false; + if (GetIsOnScreen()) { + if (((CTimer::GetFrameCounter() + m_randomSeed) & 0x1F) == 17) { + CEntity* foundBuilding = nil; + CColPoint foundCol; + CVector lookPos = GetPosition() + CVector(0.0f, 0.0f, 0.7f); + CVector camPos = TheCamera.GetGameCamPosition(); + CWorld::ProcessLineOfSight(camPos, lookPos, foundCol, foundBuilding, + true, false, false, false, false, false, false); + + // He's at least 15.0 far, in disabled zone, collided into somewhere (that's why m_bStopAndShootDisabledZone set), + // and now has building on front of him. He's stupid, we don't need him. + if (foundBuilding) { + FlagToDestroyWhenNextProcessed(); + dontShoot = true; + } + } + } else { + FlagToDestroyWhenNextProcessed(); + dontShoot = true; + } + + if (!dontShoot) { + bStopAndShoot = true; + bKindaStayInSamePlace = true; + bIsPointingGunAt = true; + SetAttack(m_pedInObjective); + } + } + + if (field_624 >= 2 && m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT) { + CVector centre = GetPosition() + CVector(0.f, 0.f, 0.65f); + if (CWorld::TestSphereAgainstWorld(centre, 0.35f, this, true, false, false, false, false, false)) { + field_624 = 0; + m_bStopAndShootDisabledZone = true; + ClearPursuit(); + SetObjective(OBJECTIVE_NONE); + SetWanderPath(CGeneral::GetRandomNumberInRange(0,8)); + field_61C = CTimer::GetTimeInMilliseconds() + 30000; + } else { + field_624 = 0; + if (GetWeapon()->IsTypeMelee()) { + // TODO(Miami): enum + for (int i = 3; i < 7; i++) { + if (HasWeaponSlot(i)) { + SetCurrentWeapon(i); + break; + } + } + SetMoveState(PEDMOVE_STILL); + bStopAndShoot = true; + } + } + } else if (CTimer::GetTimeStep() / 100.f <= m_fDistanceTravelled) + field_624 = 0; +} + +void +CCopPed::ProcessHeliSwat(void) +{ + CVector bestPos = GetPosition(); + SetPedState(PED_ABSEIL); + CPedPlacement::FindZCoorForPed(&bestPos); + if (GetPosition().z - 2.0f >= bestPos.z && m_pRopeEntity) { + m_fAbseilPos += 0.003f * CTimer::GetTimeStep(); + m_vecMoveSpeed.z = -0.03f; + m_vecTurnSpeed = CVector(0.f, 0.f, (m_randomSeed % 32) * 0.003f - 0.05f); + CPhysical::ApplyTurnSpeed(); + GetMatrix().Reorthogonalise(); + CVector posOnRope; + + if (CRopes::FindCoorsAlongRope(m_nRopeID, m_fAbseilPos, &posOnRope)) { + SetPosition(posOnRope); + } else { + bUsesCollision = true; + m_vecMoveSpeed = CVector(0.f, 0.f, 0.f); + SetPedState(PED_IDLE); + m_nCopType = COP_SWAT; + SetInTheAir(); + bKnockedUpIntoAir = true; + } + Say(SOUND_PED_COP_HELIPILOTPHRASE); + } else { + bUsesCollision = true; + m_vecMoveSpeed = CVector(0.f, 0.f, 0.f); + SetPedState(PED_IDLE); + m_nCopType = COP_SWAT; + SetInTheAir(); + bKnockedUpIntoAir = true; + } +} + +void +CCopPed::ProcessStingerCop(void) +{ + if (m_pStinger->bIsDeployed || FindPlayerVehicle() && (FindPlayerVehicle()->IsCar() || FindPlayerVehicle()->IsBike())) { + if (m_pStinger->bIsDeployed) { + m_pStinger->Process(); + } else { + CVector2D vehDist = GetPosition() - FindPlayerVehicle()->GetPosition(); + CVector2D dirVehGoing = FindPlayerVehicle()->m_vecMoveSpeed; + if (vehDist.MagnitudeSqr() < sq(30.0f)) { + if (dirVehGoing.MagnitudeSqr() > 0.0f) { + vehDist.Normalise(); + dirVehGoing.Normalise(); + if (DotProduct2D(vehDist, dirVehGoing) > 0.8f) { + float angle = (CrossProduct2D(vehDist, dirVehGoing - vehDist) < 0.0f ? + FindPlayerVehicle()->GetForward().Heading() - HALFPI : + HALFPI + FindPlayerVehicle()->GetForward().Heading()); + + SetHeading(angle); + m_fRotationCur = angle; + m_fRotationDest = angle; + m_pStinger->Deploy(this); + } + } + } + } + } else { + ClearPursuit(); + } +} diff --git a/src/miami/peds/CopPed.h b/src/miami/peds/CopPed.h new file mode 100644 index 00000000..3f5ae06d --- /dev/null +++ b/src/miami/peds/CopPed.h @@ -0,0 +1,51 @@ +#pragma once +#include "Ped.h" + +enum eCopType +{ + COP_STREET = 0, + COP_FBI = 1, + COP_SWAT = 2, + COP_HELI_SWAT = 3, + COP_ARMY = 4, + COP_MIAMIVICE = 5 +}; + +class CCopPed : public CPed +{ +public: + CVehicle* m_nRoadblockVeh; + float m_fDistanceToTarget; + bool m_bIsInPursuit; + bool m_bIsDisabledCop; + int8 field_5FE; + bool m_bBeatingSuspect; + bool m_bStopAndShootDisabledZone; + bool m_bDragsPlayerFromCar; + bool m_bZoneDisabled; + float m_fAbseilPos; + eCopType m_nCopType; + bool m_bThrowsSpikeTrap; + CEntity *m_pRopeEntity; // CHeli or 1 + uintptr m_nRopeID; + uint32 m_nHassleTimer; + uint32 field_61C; + class CStinger *m_pStinger; + int32 field_624; + int8 field_628; + + CCopPed(eCopType, int32 modifier = 0); + ~CCopPed(); + + void ClearPursuit(void); + void ProcessControl(void); + void SetArrestPlayer(CPed*); + void SetPursuit(bool); + void ArrestPlayer(void); + void ScanForCrimes(void); + void CopAI(void); + void ProcessHeliSwat(void); + void ProcessStingerCop(void); +}; + +VALIDATE_SIZE(CCopPed, 0x62C); diff --git a/src/miami/peds/DummyPed.h b/src/miami/peds/DummyPed.h new file mode 100644 index 00000000..cace4ead --- /dev/null +++ b/src/miami/peds/DummyPed.h @@ -0,0 +1,10 @@ +#pragma once + +#include "Dummy.h" + +// actually unused +class CDummyPed : CDummy +{ + int32 pedType; + int32 unknown; +}; diff --git a/src/miami/peds/EmergencyPed.cpp b/src/miami/peds/EmergencyPed.cpp new file mode 100644 index 00000000..0257d291 --- /dev/null +++ b/src/miami/peds/EmergencyPed.cpp @@ -0,0 +1,412 @@ +#include "common.h" + +#include "EmergencyPed.h" +#include "DMAudio.h" +#include "ModelIndices.h" +#include "Vehicle.h" +#include "Fire.h" +#include "General.h" +#include "CarCtrl.h" +#include "Accident.h" + +CEmergencyPed::CEmergencyPed(uint32 type) : CPed(type) +{ + switch (type){ + case PEDTYPE_EMERGENCY: + SetModelIndex(MI_MEDIC); + m_pRevivedPed = nil; + field_1360 = 0; + break; + case PEDTYPE_FIREMAN: + SetModelIndex(MI_FIREMAN); + m_pRevivedPed = nil; + break; + default: + break; + } + m_nEmergencyPedState = EMERGENCY_PED_READY; + m_pAttendedAccident = nil; + m_bStartedToCPR = false; +} + +bool +CEmergencyPed::InRange(CPed *victim) +{ + if (!m_pMyVehicle) + return true; + + if ((m_pMyVehicle->GetPosition() - victim->GetPosition()).Magnitude() > 30.0f) + return false; + + return true; +} + +void +CEmergencyPed::ProcessControl(void) +{ + CPed::ProcessControl(); + if (bWasPostponed) + return; + + if(!DyingOrDead()) { + GetWeapon()->Update(m_audioEntityId, nil); + + if (IsPedInControl() && m_moved.Magnitude() > 0.0f) + Avoid(); + + switch (m_nPedState) { + case PED_SEEK_POS: + Seek(); + break; + case PED_SEEK_ENTITY: + if (m_pSeekTarget) { + m_vecSeekPos = m_pSeekTarget->GetPosition(); + Seek(); + } else { + ClearSeek(); + } + break; + default: + break; + } + + switch (m_nPedType) { + case PEDTYPE_EMERGENCY: + if (IsPedInControl() || m_nPedState == PED_DRIVING) + MedicAI(); + break; + case PEDTYPE_FIREMAN: + if (IsPedInControl()) + FiremanAI(); + break; + default: + return; + } + } +} + +// This function was buggy and incomplete in both III and VC, firemen had to be in 5.0m range of fire etc. etc. +// Copied some code from MedicAI to make it work. +void +CEmergencyPed::FiremanAI(void) +{ + float fireDist; + CFire *nearestFire; + + switch (m_nEmergencyPedState) { + case EMERGENCY_PED_READY: + nearestFire = gFireManager.FindNearestFire(GetPosition(), &fireDist); + if (nearestFire) { + SetPedState(PED_NONE); + SetSeek(nearestFire->m_vecPos, 1.0f); + SetMoveState(PEDMOVE_RUN); + m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; + m_pAttendedFire = nearestFire; +#ifdef FIX_BUGS + bIsRunning = true; +#endif + } + break; + case EMERGENCY_PED_DETERMINE_NEXT_STATE: + nearestFire = gFireManager.FindNearestFire(GetPosition(), &fireDist); + if (nearestFire && nearestFire != m_pAttendedFire) { + SetPedState(PED_NONE); + SetSeek(nearestFire->m_vecPos, 1.0f); + SetMoveState(PEDMOVE_RUN); +#ifdef FIX_BUGS + bIsRunning = true; + m_pAttendedFire = nearestFire; + } else if (!nearestFire) { +#else + m_pAttendedFire = nearestFire; + } else { +#endif + m_nEmergencyPedState = EMERGENCY_PED_STOP; + } + + // "Extinguish" the fire (Will overwrite the stop decision above if the attended and nearest fires are same) + if (fireDist < 5.0f) { + SetIdle(); + m_nEmergencyPedState = EMERGENCY_PED_STAND_STILL; + } + break; + case EMERGENCY_PED_STAND_STILL: + if (!m_pAttendedFire->m_bIsOngoing) + m_nEmergencyPedState = EMERGENCY_PED_STOP; + + // Leftover + // fireDist = 30.0f; + nearestFire = gFireManager.FindNearestFire(GetPosition(), &fireDist); + if (nearestFire) { +#ifdef FIX_BUGS + if(nearestFire != m_pAttendedFire && (nearestFire->m_vecPos - GetPosition()).Magnitude() < 30.0f) +#endif + m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; + } + Say(SOUND_PED_EXTINGUISHING_FIRE); + break; + case EMERGENCY_PED_STOP: +#ifdef FIX_BUGS + bIsRunning = false; +#endif + SetPedState(PED_NONE); + SetWanderPath(CGeneral::GetRandomNumber() & 7); + m_pAttendedFire = nil; + m_nEmergencyPedState = EMERGENCY_PED_READY; + SetMoveState(PEDMOVE_WALK); + break; + default: break; + } +} + +void +CEmergencyPed::MedicAI(void) +{ + float distToEmergency; + if (!bInVehicle && IsPedInControl()) { + ScanForDelayedResponseThreats(); + if (m_threatFlags && CTimer::GetTimeInMilliseconds() > m_threatCheckTimer) { + CheckThreatValidity(); + m_threatFlags = 0; + m_threatCheckTimer = 0; + if (m_threatEntity && m_threatEntity->IsPed() && ((CPed*)m_threatEntity)->IsPlayer()) { + if (((CPed*)m_threatEntity)->GetWeapon()->IsTypeMelee()) { + SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity); + } else { + SetFlee(m_threatEntity, 6000); + Say(SOUND_PED_FLEE_SPRINT); + } + return; + } + } + } + + if (InVehicle()) { + if (m_pMyVehicle->IsCar() && m_objective != OBJECTIVE_LEAVE_CAR) { + if (gAccidentManager.FindNearestAccident(m_pMyVehicle->GetPosition(), &distToEmergency) + && distToEmergency < 25.0f && m_pMyVehicle->m_vecMoveSpeed.Magnitude() < 0.01f) { + + m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_NONE; + SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); + Say(SOUND_PED_LEAVE_VEHICLE); + } else if (m_pMyVehicle->pDriver == this && m_nPedState == PED_DRIVING + && m_pMyVehicle->AutoPilot.m_nCarMission == MISSION_NONE && !(CGeneral::GetRandomNumber() & 31)) { + + bool waitUntilMedicEntersCar = false; + for (int i = 0; i < m_numNearPeds; ++i) { + CPed *nearPed = m_nearPeds[i]; + if (nearPed->m_nPedType == PEDTYPE_EMERGENCY) { + if ((nearPed->m_nPedState == PED_SEEK_CAR || nearPed->m_nPedState == PED_ENTER_CAR) + && nearPed->m_pMyVehicle == m_pMyVehicle) { + waitUntilMedicEntersCar = true; + break; + } + } + } + if (!waitUntilMedicEntersCar) { + CCarCtrl::JoinCarWithRoadSystem(m_pMyVehicle); + m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; + m_pMyVehicle->m_bSirenOrAlarm = false; + m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 12; + m_pMyVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_SLOW_DOWN_FOR_CARS; + if (m_pMyVehicle->bIsAmbulanceOnDuty) { + m_pMyVehicle->bIsAmbulanceOnDuty = false; + --CCarCtrl::NumAmbulancesOnDuty; + } + } + } + } + } + + CVector headPos, midPos; + CAccident *nearestAccident; + if (IsPedInControl()) { + switch (m_nEmergencyPedState) { + case EMERGENCY_PED_READY: + nearestAccident = gAccidentManager.FindNearestAccident(GetPosition(), &distToEmergency); + field_1360 = 0; + if (nearestAccident) { + m_pRevivedPed = nearestAccident->m_pVictim; + m_pRevivedPed->RegisterReference((CEntity**)&m_pRevivedPed); + m_pRevivedPed->m_pedIK.GetComponentPosition(midPos, PED_MID); + m_pRevivedPed->m_pedIK.GetComponentPosition(headPos, PED_HEAD); + SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, CVector((headPos + midPos) * 0.5f)); + bIsRunning = true; + m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; + m_pAttendedAccident = nearestAccident; + ++m_pAttendedAccident->m_nMedicsAttending; + } else { + if (m_pMyVehicle) { + if (!bInVehicle) { + if (m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || m_pMyVehicle->pDriver || m_pMyVehicle->m_nGettingInFlags) { + + CPed* driver = m_pMyVehicle->pDriver; + if (driver && driver->m_nPedType != PEDTYPE_EMERGENCY && m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT) { + SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, driver); + + } else if (m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER + && m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER + && m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT) { + SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, m_pMyVehicle); + } + } else { + SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pMyVehicle); + } + } + } else if (m_nPedState != PED_WANDER_PATH) { + SetWanderPath(CGeneral::GetRandomNumber() & 7); + } + } + break; + case EMERGENCY_PED_DETERMINE_NEXT_STATE: + nearestAccident = gAccidentManager.FindNearestAccident(GetPosition(), &distToEmergency); + if (nearestAccident) { + if (nearestAccident != m_pAttendedAccident || m_nPedState != PED_SEEK_POS) { + m_pRevivedPed = nearestAccident->m_pVictim; + m_pRevivedPed->RegisterReference((CEntity**)&m_pRevivedPed); + if (!InRange(m_pRevivedPed)) { + m_nEmergencyPedState = EMERGENCY_PED_STOP; + break; + } + m_pRevivedPed->m_pedIK.GetComponentPosition(midPos, PED_MID); + m_pRevivedPed->m_pedIK.GetComponentPosition(headPos, PED_HEAD); + SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, CVector((headPos + midPos) * 0.5f)); + bIsRunning = true; + --m_pAttendedAccident->m_nMedicsAttending; + ++nearestAccident->m_nMedicsAttending; + m_pAttendedAccident = nearestAccident; + } + } else { + m_nEmergencyPedState = EMERGENCY_PED_STOP; + bIsRunning = false; + } + if (distToEmergency < 5.0f) { + if (m_pRevivedPed->m_pFire) { + bIsRunning = false; + SetMoveState(PEDMOVE_STILL); + } else if (distToEmergency < 4.5f) { + bIsRunning = false; + SetMoveState(PEDMOVE_WALK); + if (distToEmergency < 1.0f + || distToEmergency < 4.5f && m_pAttendedAccident->m_nMedicsPerformingCPR) { + m_nEmergencyPedState = EMERGENCY_PED_START_CPR; + } + } + } + break; + case EMERGENCY_PED_START_CPR: + if (!m_pRevivedPed || m_pRevivedPed->m_fHealth > 0.0f || m_pRevivedPed->bFadeOut) { + m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; + } else { + m_pRevivedPed->m_bloodyFootprintCountOrDeathTime = CTimer::GetTimeInMilliseconds(); + SetMoveState(PEDMOVE_STILL); + SetPedState(PED_CPR); + m_nLastPedState = PED_CPR; + SetLookFlag(m_pRevivedPed, 0); + SetLookTimer(500); + Say(SOUND_PED_HEALING); + if (m_pAttendedAccident->m_nMedicsPerformingCPR) { + SetIdle(); + m_nEmergencyPedState = EMERGENCY_PED_STAND_STILL; + } else { + m_nEmergencyPedState = EMERGENCY_PED_FACE_TO_PATIENT; + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_MEDIC, ANIM_MEDIC_CPR, 4.0f); + bIsDucking = true; + } + SetLookTimer(2000); + ++m_pAttendedAccident->m_nMedicsPerformingCPR; + m_bStartedToCPR = true; + } + break; + case EMERGENCY_PED_FACE_TO_PATIENT: + if (!m_pRevivedPed || m_pRevivedPed->m_fHealth > 0.0f) + m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; + else { + m_pRevivedPed->m_pedIK.GetComponentPosition(midPos, PED_MID); + m_pRevivedPed->m_pedIK.GetComponentPosition(headPos, PED_HEAD); + midPos = (headPos + midPos) * 0.5f; + m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( + midPos.x, midPos.y, + GetPosition().x, GetPosition().y); + m_fRotationDest = CGeneral::LimitAngle(m_fRotationDest); + m_pLookTarget = m_pRevivedPed; + m_pLookTarget->RegisterReference((CEntity**)&m_pLookTarget); + TurnBody(); + + if (Abs(m_fRotationCur - m_fRotationDest) < DEGTORAD(45.0f)) + m_nEmergencyPedState = EMERGENCY_PED_PERFORM_CPR; + else + m_fRotationCur = (m_fRotationCur + m_fRotationDest) * 0.5f; + } + break; + case EMERGENCY_PED_PERFORM_CPR: + if (!m_pRevivedPed || m_pRevivedPed->m_fHealth > 0.0f) { + m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; + break; + } + m_pRevivedPed->m_pedIK.GetComponentPosition(midPos, PED_MID); + m_pRevivedPed->m_pedIK.GetComponentPosition(headPos, PED_HEAD); + midPos = (headPos + midPos) * 0.5f; + m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( + midPos.x, midPos.y, + GetPosition().x, GetPosition().y); + m_fRotationDest = CGeneral::LimitAngle(m_fRotationDest); + m_pLookTarget = m_pRevivedPed; + m_pLookTarget->RegisterReference((CEntity**)&m_pLookTarget); + TurnBody(); + if (CTimer::GetTimeInMilliseconds() <= m_lookTimer) { + SetMoveState(PEDMOVE_STILL); + break; + } + m_nEmergencyPedState = EMERGENCY_PED_STOP_CPR; + SetPedState(PED_NONE); + SetMoveState(PEDMOVE_WALK); + m_pVehicleAnim = nil; + if (!m_pRevivedPed->bBodyPartJustCameOff) { + m_pRevivedPed->m_fHealth = 100.0f; + m_pRevivedPed->SetPedState(PED_NONE); + m_pRevivedPed->m_nLastPedState = PED_WANDER_PATH; + m_pRevivedPed->SetGetUp(); + m_pRevivedPed->bUsesCollision = true; + m_pRevivedPed->SetMoveState(PEDMOVE_WALK); + m_pRevivedPed->RestartNonPartialAnims(); + m_pRevivedPed->bIsPedDieAnimPlaying = false; + m_pRevivedPed->bKnockedUpIntoAir = false; + m_pRevivedPed->m_pCollidingEntity = nil; + m_pRevivedPed->bKnockedOffBike = false; + m_pRevivedPed->Say(SOUND_PED_ACCIDENTREACTION1); + } + break; + case EMERGENCY_PED_STOP_CPR: + m_nEmergencyPedState = EMERGENCY_PED_STOP; + bIsDucking = true; + break; + case EMERGENCY_PED_STAND_STILL: + if (!m_pRevivedPed || m_pRevivedPed->m_fHealth > 0.0f) + m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; + else { + if (!m_pAttendedAccident->m_pVictim) + m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; + if (!m_pAttendedAccident->m_nMedicsPerformingCPR) + m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; + if (gAccidentManager.UnattendedAccidents()) + m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; + } + break; + case EMERGENCY_PED_STOP: + m_bStartedToCPR = false; + SetPedState(PED_NONE); + if (m_pAttendedAccident) { + m_pAttendedAccident->m_pVictim = nil; + --m_pAttendedAccident->m_nMedicsAttending; + m_pAttendedAccident = nil; + } + SetWanderPath(CGeneral::GetRandomNumber() & 7); + m_pRevivedPed = nil; + m_nEmergencyPedState = EMERGENCY_PED_READY; + SetMoveState(PEDMOVE_WALK); + break; + default: break; + } + } +} diff --git a/src/miami/peds/EmergencyPed.h b/src/miami/peds/EmergencyPed.h new file mode 100644 index 00000000..41bc86e5 --- /dev/null +++ b/src/miami/peds/EmergencyPed.h @@ -0,0 +1,39 @@ +#pragma once + +#include "Ped.h" + +class CAccident; +class CFire; + +enum EmergencyPedState +{ + EMERGENCY_PED_READY = 0x0, + EMERGENCY_PED_DETERMINE_NEXT_STATE = 0x1, // you can set that anytime you want + EMERGENCY_PED_START_CPR = 0x2, + EMERGENCY_PED_FLAG_4 = 0x4, // unused + EMERGENCY_PED_FLAG_8 = 0x8, // unused + EMERGENCY_PED_FACE_TO_PATIENT = 0x10, // for CPR + EMERGENCY_PED_PERFORM_CPR = 0x20, + EMERGENCY_PED_STOP_CPR = 0x40, + EMERGENCY_PED_STAND_STILL = 0x80, // waiting colleagues for medics, "extinguishing" fire for firemen + EMERGENCY_PED_STOP = 0x100, +}; + +class CEmergencyPed : public CPed +{ +public: + CPed *m_pRevivedPed; + EmergencyPedState m_nEmergencyPedState; + CAccident *m_pAttendedAccident; + CFire *m_pAttendedFire; + bool m_bStartedToCPR; // set but unused + int32 field_1360; // set to 0 but unused + + CEmergencyPed(uint32); + ~CEmergencyPed() { } + bool InRange(CPed*); + void ProcessControl(void); + void FiremanAI(void); + void MedicAI(void); +}; +//VALIDATE_SIZE(CEmergencyPed, 0x554); diff --git a/src/miami/peds/Gangs.cpp b/src/miami/peds/Gangs.cpp new file mode 100644 index 00000000..240f6b37 --- /dev/null +++ b/src/miami/peds/Gangs.cpp @@ -0,0 +1,125 @@ +#include "common.h" + +#include "ModelIndices.h" +#include "Gangs.h" +#include "General.h" +#include "Streaming.h" +#include "Weapon.h" +#include "SaveBuf.h" + +CGangInfo CGangs::Gang[NUM_GANGS]; +bool CGangs::GangAttackWithCops[NUM_GANGS]; + +CGangInfo::CGangInfo() : + m_nVehicleMI(-1), + m_nPedModel1MI(-1), + m_nPedModel2MI(-1), + m_nPedModelOverride(-1), + m_Weapon1(WEAPONTYPE_UNARMED), + m_Weapon2(WEAPONTYPE_UNARMED) +{} + +void CGangs::Initialise(void) +{ + SetGangPedModels(GANG_CUBAN, MI_CBA, MI_CBB); + SetGangPedModels(GANG_HAITIAN, MI_HNA, MI_HNB); + SetGangPedModels(GANG_STREET, MI_SGA, MI_SGB); + SetGangPedModels(GANG_DIAZ, MI_CLA, MI_CLB); + SetGangPedModels(GANG_SECURITY, MI_GDA, MI_GDB); + SetGangPedModels(GANG_BIKER, MI_BKA, MI_BKB); + SetGangPedModels(GANG_PLAYER, MI_PGA, MI_PGB); + SetGangPedModels(GANG_GOLFER, MI_WFOGO, MI_WMOGO); + SetGangVehicleModel(GANG_CUBAN, MI_CUBAN); + SetGangVehicleModel(GANG_HAITIAN, MI_VOODOO); + SetGangVehicleModel(GANG_STREET, MI_GANGBUR); + SetGangVehicleModel(GANG_DIAZ, -1); + SetGangVehicleModel(GANG_SECURITY, -1); + SetGangVehicleModel(GANG_BIKER, MI_ANGEL); + SetGangVehicleModel(GANG_PLAYER, -1); + SetGangVehicleModel(GANG_GOLFER, MI_CADDY); + SetGangWeapons(GANG_GOLFER, WEAPONTYPE_GOLFCLUB, WEAPONTYPE_GOLFCLUB); +#ifdef FIX_BUGS + for (int i = 0; i < NUM_GANGS; i++) + SetGangPedModelOverride(i, -1); +#endif +} + +bool CGangs::HaveGangModelsLoaded(int16 gang) +{ + CGangInfo* pGangInfo = GetGangInfo(gang); + return CStreaming::HasModelLoaded(pGangInfo->m_nPedModel1MI) && CStreaming::HasModelLoaded(pGangInfo->m_nPedModel2MI); +} + +void CGangs::SetGangPedModels(int16 gang, int32 mi1, int32 mi2) +{ + GetGangInfo(gang)->m_nPedModel1MI = mi1; + GetGangInfo(gang)->m_nPedModel2MI = mi2; +} + +void CGangs::SetWillAttackPlayerWithCops(ePedType type, bool will) +{ + if (type >= PEDTYPE_GANG1 && type <= PEDTYPE_GANG9) + GangAttackWithCops[type - PEDTYPE_GANG1] = will; +} + +bool CGangs::GetWillAttackPlayerWithCops(ePedType type) +{ + if (type >= PEDTYPE_GANG1 && type <= PEDTYPE_GANG9) + return GangAttackWithCops[type - PEDTYPE_GANG1]; + return false; +} + +int32 CGangs::ChooseGangPedModel(int16 gang) +{ + CGangInfo* pGangInfo = GetGangInfo(gang); + if (pGangInfo->m_nPedModelOverride != -1 || CGeneral::GetRandomTrueFalse()) + return pGangInfo->m_nPedModel1MI; + else + return pGangInfo->m_nPedModel2MI; +} + +void CGangs::SetGangVehicleModel(int16 gang, int32 model) +{ + GetGangInfo(gang)->m_nVehicleMI = model; +} + +void CGangs::SetGangWeapons(int16 gang, int32 weapon1, int32 weapon2) +{ + CGangInfo *gi = GetGangInfo(gang); + gi->m_Weapon1 = weapon1; + gi->m_Weapon2 = weapon2; +} + +void CGangs::SetGangPedModelOverride(int16 gang, int8 ovrd) +{ + GetGangInfo(gang)->m_nPedModelOverride = ovrd; +} + +int8 CGangs::GetGangPedModelOverride(int16 gang) +{ + return GetGangInfo(gang)->m_nPedModelOverride; +} + +void CGangs::SaveAllGangData(uint8 *buf, uint32 *size) +{ +INITSAVEBUF + + *size = SAVE_HEADER_SIZE + sizeof(Gang); + WriteSaveHeader(buf, 'G','N','G','\0', *size - SAVE_HEADER_SIZE); + for (int i = 0; i < NUM_GANGS; i++) + WriteSaveBuf(buf, Gang[i]); + +VALIDATESAVEBUF(*size); +} + +void CGangs::LoadAllGangData(uint8 *buf, uint32 size) +{ + Initialise(); + +INITSAVEBUF + CheckSaveHeader(buf, 'G','N','G','\0', size - SAVE_HEADER_SIZE); + + for (int i = 0; i < NUM_GANGS; i++) + ReadSaveBuf(&Gang[i], buf); +VALIDATESAVEBUF(size); +} diff --git a/src/miami/peds/Gangs.h b/src/miami/peds/Gangs.h new file mode 100644 index 00000000..c6381343 --- /dev/null +++ b/src/miami/peds/Gangs.h @@ -0,0 +1,57 @@ +#pragma once + +#include "PedType.h" + +struct CGangInfo +{ + int32 m_nVehicleMI; + int32 m_nPedModel1MI; + int32 m_nPedModel2MI; + int8 m_nPedModelOverride; + int32 m_Weapon1; + int32 m_Weapon2; + + CGangInfo(); +}; + +VALIDATE_SIZE(CGangInfo, 0x10); + +enum { + GANG_CUBAN = 0, + GANG_HAITIAN, + GANG_STREET, + GANG_DIAZ, + GANG_SECURITY, + GANG_BIKER, + GANG_PLAYER, + GANG_GOLFER, + GANG_9, + NUM_GANGS +}; + +class CGangs +{ +public: + static void Initialise(void); + static void SetGangVehicleModel(int16, int32); + static void SetGangWeapons(int16, int32, int32); + static void SetGangPedModelOverride(int16, int8); + static int8 GetGangPedModelOverride(int16); + static void SaveAllGangData(uint8 *, uint32 *); + static void LoadAllGangData(uint8 *, uint32); + + static void SetGangPedModels(int16, int32, int32); + static void SetWillAttackPlayerWithCops(ePedType type, bool will); + static bool GetWillAttackPlayerWithCops(ePedType type); + static int32 ChooseGangPedModel(int16); + + static bool HaveGangModelsLoaded(int16 gang); + static int32 GetGangPedModel1(int16 gang) { return Gang[gang].m_nPedModel1MI; } + static int32 GetGangPedModel2(int16 gang) { return Gang[gang].m_nPedModel2MI; } + static int32 GetGangVehicleModel(int16 gang) { return Gang[gang].m_nVehicleMI; } + static CGangInfo *GetGangInfo(int16 gang) { return &Gang[gang]; } + +private: + static CGangInfo Gang[NUM_GANGS]; + static bool GangAttackWithCops[NUM_GANGS]; +}; diff --git a/src/miami/peds/Ped.cpp b/src/miami/peds/Ped.cpp new file mode 100644 index 00000000..aace82e8 --- /dev/null +++ b/src/miami/peds/Ped.cpp @@ -0,0 +1,9662 @@ +#include "common.h" + +#include "main.h" +#include "Pools.h" +#include "Particle.h" +#include "RpAnimBlend.h" +#include "Bones.h" +#include "Ped.h" +#include "AnimBlendAssociation.h" +#include "Fire.h" +#include "DMAudio.h" +#include "General.h" +#include "VisibilityPlugins.h" +#include "HandlingMgr.h" +#include "Replay.h" +#include "Radar.h" +#include "PedPlacement.h" +#include "Shadows.h" +#include "Weather.h" +#include "ZoneCull.h" +#include "Population.h" +#include "Pad.h" +#include "Phones.h" +#include "TrafficLights.h" +#include "CopPed.h" +#include "Script.h" +#include "CarCtrl.h" +#include "Garages.h" +#include "WaterLevel.h" +#include "Timecycle.h" +#include "ParticleObject.h" +#include "Floater.h" +#include "Range2D.h" +#include "Streaming.h" +#include "PedAttractor.h" +#include "GameLogic.h" +#include "Bike.h" +#include "WindModifiers.h" +#include "CutsceneShadow.h" +#include "Clock.h" +#include "Wanted.h" +#include "SaveBuf.h" + +CPed *gapTempPedList[50]; +uint16 gnNumTempPedList; + +static CColPoint aTempPedColPts[MAX_COLLISION_POINTS]; + +uint16 CPed::nThreatReactionRangeMultiplier = 1; +uint16 CPed::nEnterCarRangeMultiplier = 1; + +bool CPed::bNastyLimbsCheat; +bool CPed::bFannyMagnetCheat; +bool CPed::bPedCheat3; +CVector2D CPed::ms_vec2DFleePosition; + +void *CPed::operator new(size_t sz) throw() { return CPools::GetPedPool()->New(); } +void *CPed::operator new(size_t sz, int handle) throw() { return CPools::GetPedPool()->New(handle); } +void CPed::operator delete(void *p, size_t sz) throw() { CPools::GetPedPool()->Delete((CPed*)p); } +void CPed::operator delete(void *p, int handle) throw() { CPools::GetPedPool()->Delete((CPed*)p); } + +float gfTommyFatness = 1.0f; + +CPed::CPed(uint32 pedType) : m_pedIK(this) +{ +#ifdef USE_CUTSCENE_SHADOW_FOR_PED + m_pRTShadow = nil; +#endif + m_vecAnimMoveDelta.x = 0.0f; + m_vecAnimMoveDelta.y = 0.0f; + m_fHealth = 100.0f; + m_fArmour = 0.0f; + m_nPedType = pedType; + m_lastSoundStart = 0; + m_soundStart = 0; + m_lastQueuedSound = SOUND_NO_SOUND; + m_queuedSound = SOUND_NO_SOUND; + m_canTalk = true; + + m_type = ENTITY_TYPE_PED; + bPedPhysics = true; + bUseCollisionRecords = true; + + m_objective = OBJECTIVE_NONE; + m_prevObjective = OBJECTIVE_NONE; +#ifdef FIX_BUGS + m_objectiveTimer = 0; +#endif + CharCreatedBy = RANDOM_CHAR; + m_leader = nil; + m_pedInObjective = nil; + m_attractorHeading = 0.0f; + m_carInObjective = nil; + m_attractorHeading = 0.0f; + bInVehicle = false; + m_pMyVehicle = nil; + m_pVehicleAnim = nil; + m_vecOffsetSeek.x = 0.0f; + m_vecOffsetSeek.y = 0.0f; + m_vecOffsetSeek.z = 0.0f; + m_attractor = nil; + m_positionInQueue = -1; + m_pedFormation = FORMATION_UNDEFINED; + m_collidingThingTimer = 0; + m_nPedStateTimer = 0; + m_actionX = 0.0f; + m_actionY = 0.0f; + m_phoneTalkTimer = 0; + m_stateUnused = 0; + m_leaveCarTimer = 0; + m_getUpTimer = 0; + m_attackTimer = 0; + m_timerUnused = 0; + m_lookTimer = 0; + m_chatTimer = 0; + m_shootTimer = 0; + m_carJackTimer = 0; + m_duckAndCoverTimer = 0; + m_moved = CVector2D(0.0f, 0.0f); + m_fRotationCur = 0.0f; + m_headingRate = 15.0f; + m_fRotationDest = 0.0f; + m_vehDoor = CAR_DOOR_LF; + m_walkAroundType = 0; + m_pCurrentPhysSurface = nil; + m_vecOffsetFromPhysSurface = CVector(0.0f, 0.0f, 0.0f); + m_pSeekTarget = nil; + m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f); + m_wepSkills = 0; + m_distanceToCountSeekDone = 1.0f; + m_acceptableHeadingOffset = 0.1f; + m_followPathDestPos = CVector(0.f, 0.f, 0.f); + m_followPathAbortDist = 0.0f; + m_followPathMoveState = PEDMOVE_NONE; + bRunningToPhone = false; + m_phoneId = -1; + m_lastAccident = 0; + m_fleeFrom = nil; + m_fleeFromPos = CVector2D(0.0f, 0.0f); + m_fleeTimer = 0; + m_threatEx = nil; + m_vecSpotToGuard = CVector(0.0f, 0.0f, 0.0f); + m_radiusToGuard = 0.0f; + m_nWaitState = WAITSTATE_FALSE; + m_nWaitTimer = 0; + m_pCollidingEntity = nil; + m_nPedState = PED_IDLE; + m_nLastPedState = PED_NONE; + m_nMoveState = PEDMOVE_STILL; +#ifdef FIX_BUGS + m_nPrevMoveState = PEDMOVE_NONE; +#endif + m_nStoredMoveState = PEDMOVE_NONE; + m_pFire = nil; + m_pPointGunAt = nil; + m_pLookTarget = nil; + m_fLookDirection = 0.0f; + m_pCurSurface = nil; + m_wanderRangeBounds = nil; + + for (int i = 0; i < ARRAY_SIZE(m_pathNodesToGo); i++) { + m_pathNodesToGo[i] = nil; + } + m_nNumPathNodes = 0; + m_nCurPathNodeId = 0; + m_nPathDir = 0; + m_pLastPathNode = nil; + m_pNextPathNode = nil; + m_followPathWalkAroundEnt = nil; + m_followPathTargetEnt = nil; + m_pathNodeTimer = 0; + m_pCurPathNode = nil; + + m_threatFlags = 0; + m_threatCheckTimer = 0; + m_threatCheckInterval = CGeneral::GetRandomNumberInRange(250, 1000); + + m_routeLastPoint = -1; + m_routeStartPoint = 0; + m_routePointsPassed = 0; + m_routeType = 0; + + m_fMass = 70.0f; + m_fTurnMass = 100.0f; + m_fAirResistance = 0.4f / m_fMass; + m_fElasticity = 0.05f; + + m_ceaseAttackTimer = 0; + m_bodyPartBleeding = -1; + + bIsStanding = false; + bWasStanding = false; + bIsAttacking = false; + bIsPointingGunAt = false; + bIsLooking = false; + bKeepTryingToLook = false; + bIsRestoringLook = false; + bIsAimingGun = false; + + bIsRestoringGun = false; + bCanPointGunAtTarget = false; + bIsTalking = false; + bIsInTheAir = false; + bIsLanding = false; + bIsRunning = false; + bHitSomethingLastFrame = false; + bVehEnterDoorIsBlocked = false; + + bCanPedEnterSeekedCar = false; + bRespondsToThreats = true; + bRenderPedInCar = true; + bChangedSeat = false; + bUpdateAnimHeading = false; + bBodyPartJustCameOff = false; + bIsShooting = false; + bFindNewNodeAfterStateRestore = false; + + bGonnaInvestigateEvent = false; + bPedIsBleeding = false; + bStopAndShoot = false; + bIsPedDieAnimPlaying = false; + bUsePedNodeSeek = false; + bObjectiveCompleted = false; + bScriptObjectiveCompleted = false; + + bKindaStayInSamePlace = false; + bBeingChasedByPolice = false; + bNotAllowedToDuck = false; + bCrouchWhenShooting = false; + bIsDucking = false; + bGetUpAnimStarted = false; + bDoBloodyFootprints = false; + bFleeAfterExitingCar = false; + + bWanderPathAfterExitingCar = false; + bIsLeader = false; + bDontDragMeOutCar = false; + bWillBeQuickJacked = false; + bCancelEnteringCar = false; + bObstacleShowedUpDuringKillObjective = false; + bDuckAndCover = false; + + bStillOnValidPoly = false; + bAllowMedicsToReviveMe = true; + bResetWalkAnims = false; + bStartWanderPathOnFoot = false; + bOnBoat = false; + bBusJacked = false; + bGonnaKillTheCarJacker = false; + bFadeOut = false; + + bKnockedUpIntoAir = false; + bHitSteepSlope = false; + bCullExtraFarAway = false; + bClearObjective = false; + bTryingToReachDryLand = false; + bCollidedWithMyVehicle = false; + bRichFromMugging = false; + bChrisCriminal = false; + + bShakeFist = false; + bNoCriticalHits = false; + bVehExitWillBeInstant = false; + bHasAlreadyBeenRecorded = false; + bFallenDown = false; + bDontAcceptIKLookAts = false; + bReachedAttractorHeadingTarget = false; + bTurnedAroundOnAttractor = false; +#ifdef KANGAROO_CHEAT + m_ped_flagI80 = false; +#endif + + bHasAlreadyUsedAttractor = false; + bHasAlreadyStoleACar = false; + bCarPassenger = false; + bFleeWhenStanding = false; + bGotUpOfMyOwnAccord = false; + bMiamiViceCop = false; + bMoneyHasBeenGivenByScript = false; + bHasBeenPhotographed = false; + + bIsDrowning = false; + bDrownsInWater = true; + bWaitForLeaderToComeCloser = false; + bHeldHostageInCar = false; + bIsPlayerFriend = true; + bHeadStuckInCollision = false; + bDeadPedInFrontOfCar = false; + + m_gangFlags = ~0; + + bStayInCarOnJack = false; + + bDontFight = false; + bDoomAim = true; + bCanBeShotInVehicle = true; + bPushedAlongByCar = false; + bRemoveMeWhenIGotIntoCar = false; + bIgnoreThreatsBehindObjects = false; + + bNeverEverTargetThisPed = false; + bCrouchWhenScared = false; + bKnockedOffBike = false; + b158_8 = false; + bCollectBusFare = false; + bBoughtIceCream = false; + bDonePositionOutOfCollision = false; + bCanAttackPlayerWithCops = false; + + if (CGeneral::GetRandomNumber() & 3) + bHasACamera = false; + else + bHasACamera = true; + + if (CGeneral::GetRandomNumberInRange(0.0f, 1.0f) <= 0.95f) + bCanGiveUpSunbathing = false; + else + bCanGiveUpSunbathing = true; + + m_audioEntityId = DMAudio.CreateEntity(AUDIOTYPE_PHYSICAL, this); + DMAudio.SetEntityStatus(m_audioEntityId, TRUE); + m_fearFlags = CPedType::GetThreats(m_nPedType); + m_threatEntity = nil; + m_eventOrThreat = CVector2D(0.0f, 0.0f); + m_pEventEntity = nil; + m_fAngleToEvent = 0.0f; + m_numNearPeds = 0; + + for (int i = 0; i < ARRAY_SIZE(m_nearPeds); i++) { + m_nearPeds[i] = nil; + } + m_maxWeaponTypeAllowed = WEAPONTYPE_UNARMED; + m_currentWeapon = WEAPONTYPE_UNARMED; + m_storedWeapon = WEAPONTYPE_UNIDENTIFIED; + m_delayedWeapon = WEAPONTYPE_UNIDENTIFIED; + + for(int i = 0; i < TOTAL_WEAPON_SLOTS; i++) { + CWeapon &weapon = GetWeapon(i); + weapon.m_eWeaponType = WEAPONTYPE_UNARMED; + weapon.m_eWeaponState = WEAPONSTATE_READY; + weapon.m_nAmmoInClip = 0; + weapon.m_nAmmoTotal = 0; + weapon.m_nTimer = 0; + } + + m_curFightMove = m_lastFightMove = FIGHTMOVE_IDLE; + GiveWeapon(WEAPONTYPE_UNARMED, 0, true); + m_wepAccuracy = 60; + m_lastWepDam = -1; + m_lastDamEntity = nil; + m_attachedTo = nil; + m_attachWepAmmo = 0; + m_collPoly.valid = false; + m_fCollisionSpeed = 0.0f; + m_wepModelID = -1; + uint16 random = CGeneral::GetRandomNumber(); + m_nPedMoney = random % 25; + if (m_nPedMoney == 23) + m_nPedMoney = 400; + m_bleedCounter = 0; + m_nExtendedRangeTimer = 0; + m_vehicleInAccident = nil; + m_attractor = nil; + m_positionInQueue = -1; + m_pWeaponModel = nil; + m_delayedSoundID = -1; + m_delayedSoundTimer = 0; + CPopulation::UpdatePedCount((ePedType)m_nPedType, false); + m_lastComment = UINT32_MAX; +} + +CPed::~CPed(void) +{ +#ifdef USE_CUTSCENE_SHADOW_FOR_PED + if ( m_pRTShadow ) delete m_pRTShadow; +#endif + CWorld::Remove(this); + if (m_attractor) + GetPedAttractorManager()->DeRegisterPed(this, m_attractor); + CRadar::ClearBlipForEntity(BLIP_CHAR, CPools::GetPedPool()->GetIndex(this)); + if (InVehicle()){ + uint8 door_flag = GetCarDoorFlag(m_vehDoor); + if (m_pMyVehicle->pDriver == this) + m_pMyVehicle->pDriver = nil; + else { + // FIX: Passenger counter now being decreased after removing ourself from vehicle. + m_pMyVehicle->RemovePassenger(this); + } + if (m_nPedState == PED_EXIT_CAR || m_nPedState == PED_DRAG_FROM_CAR) + m_pMyVehicle->m_nGettingOutFlags &= ~door_flag; + bInVehicle = false; + m_pMyVehicle = nil; + } else if (EnteringCar()) { + QuitEnteringCar(); + } + if (m_pFire) + m_pFire->Extinguish(); + + ClearWeapons(); + if (bCarPassenger) + CPopulation::ms_nTotalCarPassengerPeds--; + if (bMiamiViceCop) + CPopulation::NumMiamiViceCops--; + CPopulation::UpdatePedCount((ePedType)m_nPedType, true); + DMAudio.DestroyEntity(m_audioEntityId); + + // Because of the nature of ped lists in GTA, it can sometimes be outdated. + // Remove ourself from nearPeds list of the Peds in our nearPeds list. +#ifdef FIX_BUGS + for(int i = 0; i < m_numNearPeds; i++) { + CPed *nearPed = m_nearPeds[i]; + assert(nearPed != nil); + if (!nearPed->IsPointerValid()) + continue; + + for(int j = 0; j < nearPed->m_numNearPeds;) { + assert(j == ARRAY_SIZE(m_nearPeds) - 1 || nearPed->m_nearPeds[j] || !nearPed->m_nearPeds[j+1]); // ensure nil comes after nil + + if (nearPed->m_nearPeds[j] == this) { + for (int k = j; k < ARRAY_SIZE(m_nearPeds) - 1; k++) { + nearPed->m_nearPeds[k] = nearPed->m_nearPeds[k + 1]; + nearPed->m_nearPeds[k + 1] = nil; + } + nearPed->m_nearPeds[ARRAY_SIZE(m_nearPeds) - 1] = nil; + nearPed->m_numNearPeds--; + } else + j++; + } + } +#endif +} + +void +CPed::Initialise(void) +{ + debug("Initialising CPed...\n"); + CPedType::Initialise(); + LoadFightData(); + SetAnimOffsetForEnterOrExitVehicle(); + debug("CPed ready\n"); +} + +void +CPed::SetModelIndex(uint32 mi) +{ + CEntity::SetModelIndex(mi); + RpAnimBlendClumpInit(GetClump()); + RpAnimBlendClumpFillFrameArray(GetClump(), m_pFrames); + CPedModelInfo *modelInfo = (CPedModelInfo *)CModelInfo::GetModelInfo(GetModelIndex()); + SetPedStats(modelInfo->m_pedStatType); + m_headingRate = m_pedStats->m_headingChangeRate; + m_animGroup = (AssocGroupId) modelInfo->m_animGroup; + CAnimManager::AddAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE); + + if (!CanUseTorsoWhenLooking()) + m_pedIK.m_flags |= CPedIK::LOOKAROUND_HEAD_ONLY; + + (*RPANIMBLENDCLUMPDATA(m_rwObject))->velocity2d = &m_vecAnimMoveDelta; + + if(modelInfo->GetHitColModel() == nil) + modelInfo->CreateHitColModelSkinned(GetClump()); + + UpdateRpHAnim(); +#ifdef USE_CUTSCENE_SHADOW_FOR_PED + if (!m_pRTShadow) + { + m_pRTShadow = new CCutsceneShadow; + m_pRTShadow->Create(m_rwObject, 10, 1, 1, 1); + //m_pRTShadow->Create(m_rwObject, 8, 0, 0, 0); + } +#endif +} + +void +CPed::SetPedStats(ePedStats pedStat) +{ + m_pedStats = CPedStats::ms_apPedStats[pedStat]; +} + +void +CPed::DeleteRwObject() +{ + CEntity::DeleteRwObject(); +} + +void +CPed::BuildPedLists(void) +{ + if (((CTimer::GetFrameCounter() + m_randomSeed) % 16) == 0) { + CVector centre = CEntity::GetBoundCentre(); + int deadsRegistered = 0; + CRect rect(centre.x - 20.f * nThreatReactionRangeMultiplier, + centre.y - 20.f * nThreatReactionRangeMultiplier, + centre.x + 20.f * nThreatReactionRangeMultiplier, + centre.y + 20.f * nThreatReactionRangeMultiplier); + int xstart = CWorld::GetSectorIndexX(rect.left); + int ystart = CWorld::GetSectorIndexY(rect.top); + int xend = CWorld::GetSectorIndexX(rect.right); + int yend = CWorld::GetSectorIndexY(rect.bottom); + gnNumTempPedList = 0; + + for(int y = ystart; y <= yend; y++) { + for(int x = xstart; x <= xend; x++) { + for (CPtrNode *pedPtrNode = CWorld::GetSector(x,y)->m_lists[ENTITYLIST_PEDS].first; pedPtrNode; pedPtrNode = pedPtrNode->next) { + CPed *ped = (CPed*)pedPtrNode->item; + if (ped != this && (!ped->bInVehicle || (ped->m_pMyVehicle && ped->m_pMyVehicle->IsBike()))) { + + if (nThreatReactionRangeMultiplier * 30.0f > (ped->GetPosition() - GetPosition()).Magnitude2D()) { + if (ped->m_nPedState == PED_DEAD) { + if (deadsRegistered > 3) + continue; + deadsRegistered++; + } +#ifdef FIX_BUGS + // If the gap ped list is full, sort it and truncate it + // before pushing more unsorted peds + if( gnNumTempPedList == ARRAY_SIZE(gapTempPedList) - 1 ) + { + gapTempPedList[gnNumTempPedList] = nil; + SortPeds(gapTempPedList, 0, gnNumTempPedList - 1); + gnNumTempPedList = ARRAY_SIZE(m_nearPeds); + } +#endif + + gapTempPedList[gnNumTempPedList] = ped; + gnNumTempPedList++; + // NOTE: We cannot absolutely fill the gap list, as the list is null-terminated before being passed to SortPeds + assert(gnNumTempPedList < ARRAY_SIZE(gapTempPedList)); + } + } + } + } + } + gapTempPedList[gnNumTempPedList] = nil; + SortPeds(gapTempPedList, 0, gnNumTempPedList - 1); + for (m_numNearPeds = 0; m_numNearPeds < ARRAY_SIZE(m_nearPeds); m_numNearPeds++) { + CPed *ped = gapTempPedList[m_numNearPeds]; + if (!ped) + break; + + m_nearPeds[m_numNearPeds] = ped; + } + for (int pedToClear = m_numNearPeds; pedToClear < ARRAY_SIZE(m_nearPeds); pedToClear++) + m_nearPeds[pedToClear] = nil; + } + + for(int i = 0; i < ARRAY_SIZE(m_nearPeds); ) { + bool removePed = false; + if (m_nearPeds[i]) { + if (m_nearPeds[i]->IsPointerValid()) { + float distSqr = (GetPosition() - m_nearPeds[i]->GetPosition()).MagnitudeSqr2D(); + if (distSqr > sq(nThreatReactionRangeMultiplier * 30.f)) { + removePed = true; + } + } else { + removePed = true; + } + } + + assert(i == ARRAY_SIZE(m_nearPeds) - 1 || m_nearPeds[i] || !m_nearPeds[i+1]); // ensure nil comes after nil + + if (removePed) { + // If we arrive here, the ped we're checking isn't "near", so we should remove it. + for (int j = i; j < ARRAY_SIZE(m_nearPeds) - 1; j++) { + m_nearPeds[j] = m_nearPeds[j + 1]; + m_nearPeds[j + 1] = nil; + } + m_nearPeds[ARRAY_SIZE(m_nearPeds) - 1] = nil; + m_numNearPeds--; + } else + i++; + } +} + +bool +CPed::OurPedCanSeeThisOne(CEntity *target, bool shootablesDoBlock) +{ + CColPoint colpoint; + CEntity *ent; + + CVector2D dist = CVector2D(target->GetPosition()) - CVector2D(GetPosition()); + + // Check if target is behind ped + if (DotProduct2D(dist, CVector2D(GetForward())) < 0.0f) + return false; + + // Check if target is too far away + if (dist.Magnitude() >= 40.0f) + return false; + + // Check line of sight from head + return !CWorld::ProcessLineOfSight(GetPosition() + CVector(0.f, 0.f, 1.f), target->GetPosition() + CVector(0.f, 0.f, 1.f), + colpoint, ent, true, false, false, shootablesDoBlock, false, false, false, shootablesDoBlock); +} + +// Some kind of binary sort +void +CPed::SortPeds(CPed **list, int min, int max) +{ + if (min >= max) + return; + + CVector leftDiff, rightDiff; + CVector middleDiff = GetPosition() - list[(max + min) / 2]->GetPosition(); + float middleDist = middleDiff.Magnitude(); + + int left = max; + int right = min; + while(right <= left){ + float rightDist, leftDist; + do { + rightDiff = GetPosition() - list[right]->GetPosition(); + rightDist = rightDiff.Magnitude(); + } while (middleDist > rightDist && ++right); + + do { + leftDiff = GetPosition() - list[left]->GetPosition(); + leftDist = leftDiff.Magnitude(); + } while (middleDist < leftDist && left--); + + if (right <= left) { + CPed *ped = list[right]; + list[right] = list[left]; + list[left] = ped; + right++; + left--; + } + } + SortPeds(list, min, left); + SortPeds(list, right, max); +} + +void +CPed::SetMoveState(eMoveState state) +{ + m_nMoveState = state; +} + +void +CPed::SetMoveAnim(void) +{ + if (m_nStoredMoveState == m_nMoveState || !IsPedInControl() || m_attachedTo) + return; + + if (m_nMoveState == PEDMOVE_NONE) { + m_nStoredMoveState = PEDMOVE_NONE; + return; + } + + AssocGroupId animGroupToUse; + if (m_leader && m_leader->IsPlayer()) + animGroupToUse = ASSOCGRP_PLAYER; + else + animGroupToUse = m_animGroup; + + CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_BLOCK); + if (!animAssoc) { + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FIGHT_IDLE); + if (!animAssoc) + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_MELEE_IDLE_FIGHTMODE); + + if (animAssoc && m_nPedState == PED_FIGHT) + return; + + if (animAssoc) { + CAnimBlendAssociation *idleAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE); + if (!idleAssoc || idleAssoc->blendDelta <= 0.0f) { + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + animAssoc = CAnimManager::BlendAnimation(GetClump(), animGroupToUse, ANIM_STD_IDLE, 8.0f); + } + } + } + if (!animAssoc) { + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_TIRED); + if (animAssoc) + if (m_nWaitState == WAITSTATE_STUCK || m_nWaitState == WAITSTATE_FINISH_FLEE) + return; + + if (animAssoc) { + CAnimBlendAssociation *idleAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE); + if (!idleAssoc || idleAssoc->blendDelta <= 0.0f) { + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + animAssoc = CAnimManager::BlendAnimation(GetClump(), animGroupToUse, ANIM_STD_IDLE, 4.0f); + } + } + } + if (!animAssoc) { + m_nStoredMoveState = m_nMoveState; + if (m_nMoveState == PEDMOVE_WALK || m_nMoveState == PEDMOVE_RUN || m_nMoveState == PEDMOVE_SPRINT) { + for (CAnimBlendAssociation *assoc = RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_PARTIAL); + assoc; assoc = RpAnimBlendGetNextAssociation(assoc, ASSOC_PARTIAL)) { + + if (!(assoc->flags & ASSOC_FADEOUTWHENDONE)) { + assoc->blendDelta = -2.0f; + assoc->flags |= ASSOC_DELETEFADEDOUT; + } + } + + ClearAimFlag(); + ClearLookFlag(); + } + + switch (m_nMoveState) { + case PEDMOVE_STILL: + animAssoc = CAnimManager::BlendAnimation(GetClump(), animGroupToUse, ANIM_STD_IDLE, 4.0f); + break; + case PEDMOVE_WALK: + animAssoc = CAnimManager::BlendAnimation(GetClump(), animGroupToUse, ANIM_STD_WALK, 1.0f); + break; + case PEDMOVE_RUN: + if (m_nPedState == PED_FLEE_ENTITY) { + animAssoc = CAnimManager::BlendAnimation(GetClump(), animGroupToUse, ANIM_STD_RUN, 3.0f); + } else { + animAssoc = CAnimManager::BlendAnimation(GetClump(), animGroupToUse, ANIM_STD_RUN, 1.0f); + } + break; + case PEDMOVE_SPRINT: + animAssoc = CAnimManager::BlendAnimation(GetClump(), animGroupToUse, ANIM_STD_RUNFAST, 1.0f); + break; + default: + break; + } + + if (animAssoc) { + if (m_leader) { + CAnimBlendAssociation *walkAssoc = RpAnimBlendClumpGetAssociation(m_leader->GetClump(), ANIM_STD_WALK); + if (!walkAssoc) + walkAssoc = RpAnimBlendClumpGetAssociation(m_leader->GetClump(), ANIM_STD_RUN); + + if (!walkAssoc) + walkAssoc = RpAnimBlendClumpGetAssociation(m_leader->GetClump(), ANIM_STD_RUNFAST); + + if (walkAssoc) { + animAssoc->speed = walkAssoc->speed; + } else { + if (CharCreatedBy == MISSION_CHAR) + animAssoc->speed = 1.0f; + else + animAssoc->speed = 1.2f - m_randomSeed * 0.4f / MYRAND_MAX; + + } + } else { + if (CharCreatedBy == MISSION_CHAR) + animAssoc->speed = 1.0f; + else + animAssoc->speed = 1.2f - m_randomSeed * 0.4f / MYRAND_MAX; + } + } + } +} + +void +CPed::StopNonPartialAnims(void) +{ + CAnimBlendAssociation *assoc; + + for (assoc = RpAnimBlendClumpGetFirstAssociation(GetClump()); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { + if (!assoc->IsPartial()) + assoc->flags &= ~ASSOC_RUNNING; + } +} + +void +CPed::RestartNonPartialAnims(void) +{ + CAnimBlendAssociation *assoc; + + for (assoc = RpAnimBlendClumpGetFirstAssociation(GetClump()); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { + if (!assoc->IsPartial()) + assoc->SetRun(); + } +} + +void +CPed::SetStoredState(void) +{ + if (m_nLastPedState != PED_NONE || !CanPedReturnToState()) + return; + + if (m_nPedState == PED_WANDER_PATH) { + bFindNewNodeAfterStateRestore = true; + if (m_nMoveState == PEDMOVE_NONE || m_nMoveState == PEDMOVE_STILL) + m_nMoveState = PEDMOVE_WALK; + } + if (m_nPedState != PED_IDLE) { + m_nLastPedState = m_nPedState; + if (m_nMoveState >= m_nPrevMoveState) + m_nPrevMoveState = m_nMoveState; + } +} + +void +CPed::RestorePreviousState(void) +{ + if (!CanSetPedState() || m_nPedState == PED_FALL) + return; + + if (m_nPedState == PED_GETUP && !bGetUpAnimStarted) + return; + + if (InVehicle()) { + SetPedState(PED_DRIVING); + m_nLastPedState = PED_NONE; + } else { + if (m_nLastPedState == PED_NONE) { + if (!IsPlayer() && CharCreatedBy != MISSION_CHAR && m_objective == OBJECTIVE_NONE) { + if (SetWanderPath(CGeneral::GetRandomNumber() & 7) != 0) + return; + } + SetIdle(); + return; + } + + switch (m_nLastPedState) { + case PED_IDLE: + SetIdle(); + break; + case PED_WANDER_PATH: + SetPedState(PED_WANDER_PATH); + bIsRunning = false; + if (bFindNewNodeAfterStateRestore) { + if (m_pNextPathNode) { + CVector nextNode = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); + CVector diff = nextNode - GetPosition(); + if (diff.MagnitudeSqr() < sq(7.0f)) { + SetMoveState(PEDMOVE_WALK); + break; + } + } + } + SetWanderPath(m_nPedState == PED_FOLLOW_PATH ? m_nPathDir : CGeneral::GetRandomNumber() & 7); + break; + default: + SetPedState(m_nLastPedState); + SetMoveState((eMoveState) m_nPrevMoveState); + break; + } + m_nLastPedState = PED_NONE; + } +} + +uint32 +CPed::ScanForThreats(void) +{ + int fearFlags = m_fearFlags; + CVector ourPos = GetPosition(); + float closestPedDist = 60.0f; + CVector2D explosionPos = GetPosition(); + if (fearFlags & PED_FLAG_EXPLOSION && CheckForExplosions(explosionPos)) { + m_eventOrThreat = explosionPos; + return PED_FLAG_EXPLOSION; + } + + if (fearFlags & PED_FLAG_GUN) { + CPed *shooter = CheckForGunShots(); + if (shooter && (m_nPedType != shooter->m_nPedType || m_nPedType == PEDTYPE_CIVMALE || m_nPedType == PEDTYPE_CIVFEMALE)) { + if (!IsGangMember()) { + m_threatEntity = shooter; + m_threatEntity->RegisterReference((CEntity**)&m_threatEntity); + return PED_FLAG_GUN; + } + + if (CPedType::GetFlag(shooter->m_nPedType) & fearFlags || m_nPedType == PEDTYPE_GANG5) { + if (m_threatEntity) + m_threatEntity->CleanUpOldReference(&m_threatEntity); + m_threatEntity = shooter; + m_threatEntity->RegisterReference((CEntity**)&m_threatEntity); + return CPedType::GetFlag(shooter->m_nPedType); + } + } + } + + CPed *deadPed; + if (fearFlags & PED_FLAG_DEADPEDS && CharCreatedBy != MISSION_CHAR + && (deadPed = CheckForDeadPeds()) != nil && (deadPed->GetPosition() - ourPos).MagnitudeSqr() < sq(20.0f) +#ifdef FIX_BUGS + && !deadPed->bIsInWater +#endif + ) { + m_pEventEntity = deadPed; + m_pEventEntity->RegisterReference((CEntity **) &m_pEventEntity); + return PED_FLAG_DEADPEDS; + } else { + uint32 flagsOfNearPed = 0; + + CPed *pedToFearFrom = nil; + bool weSawOurEnemy = false; + bool weMaySeeOurEnemy = false; + float closestEnemyDist = 60.0f; + if ((CTimer::GetFrameCounter() + (uint8)m_randomSeed + 16) & 4) { + + for (int i = 0; i < m_numNearPeds; ++i) { + if (CharCreatedBy == RANDOM_CHAR && m_nearPeds[i]->CharCreatedBy == MISSION_CHAR && !m_nearPeds[i]->IsPlayer()) { + continue; + } + + // BUG: Putting this here will result in returning the flags of farthest ped to us, since m_nearPeds is sorted by distance. + // Fixed at the bottom of the function. + flagsOfNearPed = CPedType::GetFlag(m_nearPeds[i]->m_nPedType); + + if (flagsOfNearPed & fearFlags) { + if (m_nearPeds[i]->m_fHealth > 0.0f) { + if (OurPedCanSeeThisOne(m_nearPeds[i], !!bIgnoreThreatsBehindObjects)) { + if (m_nearPeds[i]->m_nPedState == PED_ATTACK) { + if (m_nearPeds[i]->m_pedInObjective == this) { + + float enemyDistSqr = (m_nearPeds[i]->GetPosition() - ourPos).MagnitudeSqr2D(); + if (sq(closestEnemyDist) > enemyDistSqr) { + float enemyDist = Sqrt(enemyDistSqr); + weSawOurEnemy = true; + closestPedDist = enemyDist; + closestEnemyDist = enemyDist; + pedToFearFrom = m_nearPeds[i]; + } + } + } else { + float nearPedDistSqr = (m_nearPeds[i]->GetPosition() - ourPos).MagnitudeSqr2D(); + if (sq(closestPedDist) > nearPedDistSqr && !weSawOurEnemy) { + closestPedDist = Sqrt(nearPedDistSqr); + pedToFearFrom = m_nearPeds[i]; + } + } + } else if (!weSawOurEnemy) { + CPed *nearPed = m_nearPeds[i]; + if (nearPed->m_nPedState == PED_ATTACK) { + CColPoint foundCol; + CEntity *foundEnt; + + // We don't see him yet but he's behind a ped, vehicle or object + if (!CWorld::ProcessLineOfSight(ourPos, nearPed->GetPosition(), foundCol, foundEnt, + true, false, false, !!bIgnoreThreatsBehindObjects, false, false, false)) { + + if (nearPed->m_pedInObjective == this) { + float enemyDistSqr = (m_nearPeds[i]->GetPosition() - ourPos).MagnitudeSqr2D(); + if (sq(closestEnemyDist) > enemyDistSqr) { + float enemyDist = Sqrt(enemyDistSqr); + weMaySeeOurEnemy = true; + closestPedDist = enemyDist; + closestEnemyDist = enemyDist; + pedToFearFrom = m_nearPeds[i]; + } + } else if (!nearPed->GetWeapon()->IsTypeMelee() && !weMaySeeOurEnemy) { + float nearPedDistSqr = (m_nearPeds[i]->GetPosition() - ourPos).MagnitudeSqr2D(); + if (sq(closestPedDist) > nearPedDistSqr) { + weMaySeeOurEnemy = true; + closestPedDist = Sqrt(nearPedDistSqr); + pedToFearFrom = m_nearPeds[i]; + } + } + } + } + } + } + } + } + } + + int16 lastVehicle; + CEntity* vehicles[8]; + CWorld::FindObjectsInRange(ourPos, 20.0f, true, &lastVehicle, 6, vehicles, false, true, false, false, false); + CVehicle* foundVeh = nil; + for (int i = 0; i < lastVehicle; i++) { + CVehicle* nearVeh = (CVehicle*)vehicles[i]; + + CPed *driver = nearVeh->pDriver; + if (driver) { + + // BUG: Same bug as above. Fixed at the bottom of function. + flagsOfNearPed = CPedType::GetFlag(driver->m_nPedType); + if (flagsOfNearPed & fearFlags) { + + if (driver->m_fHealth > 0.0f && OurPedCanSeeThisOne(nearVeh->pDriver)) { + // FIX: Taken from VC +#ifdef FIX_BUGS + float driverDistSqr = (driver->GetPosition() - ourPos).MagnitudeSqr2D(); +#else + float driverDistSqr = (CVector2D(ourPos) - explosionPos).MagnitudeSqr(); +#endif + if (sq(closestPedDist) > driverDistSqr) { + closestPedDist = Sqrt(driverDistSqr); + pedToFearFrom = nearVeh->pDriver; + } + } + } + } + } + m_threatEntity = pedToFearFrom; + if (m_threatEntity) + m_threatEntity->RegisterReference((CEntity **) &m_threatEntity); + +#ifdef FIX_BUGS + if (pedToFearFrom) + flagsOfNearPed = CPedType::GetFlag(((CPed*)m_threatEntity)->m_nPedType); + else + flagsOfNearPed = 0; +#endif + + return flagsOfNearPed; + } +} + +void +CPed::ScanForDelayedResponseThreats(void) +{ + if (m_threatFlags) + return; + + m_threatEntity = nil; + m_pEventEntity = nil; + m_threatFlags = ScanForThreats(); + if (m_threatFlags) { + if (m_threatEntity || m_pEventEntity) { + m_threatCheckTimer = CTimer::GetTimeInMilliseconds() + m_threatCheckInterval; + return; + } + m_threatFlags = 0; + } + m_threatCheckTimer = 0; +} + +void +CPed::CheckThreatValidity(void) +{ + if (m_threatEntity && !IsEntityPointerValid(m_threatEntity)) { + m_threatFlags = 0; + m_threatEntity = nil; + } + if (m_pEventEntity && !IsEntityPointerValid(m_pEventEntity)) { + m_threatFlags = 0; + m_pEventEntity = nil; + } + if (!m_threatEntity && !m_pEventEntity) + m_threatFlags = 0; +} + +bool +CPed::CanUseTorsoWhenLooking(void) +{ + if (m_nPedState != PED_DRIVING && m_nPedState != PED_DRAG_FROM_CAR && !bIsDucking) { + if (m_animGroup != ASSOCGRP_SEXYWOMAN && m_animGroup != ASSOCGRP_WOMAN) + return true; + } + return false; +} + +void +CPed::SetLookFlag(float direction, bool keepTryingToLook, bool cancelPrevious) +{ + if (m_lookTimer < CTimer::GetTimeInMilliseconds() || cancelPrevious) { + bIsLooking = true; + bIsRestoringLook = false; + m_fLookDirection = direction; + m_pLookTarget = nil; + m_lookTimer = 0; + bKeepTryingToLook = keepTryingToLook; + if (CanUseTorsoWhenLooking()) { + m_pedIK.m_flags &= ~CPedIK::LOOKAROUND_HEAD_ONLY; + } + } +} + +void +CPed::SetLookFlag(CEntity *target, bool keepTryingToLook, bool cancelPrevious) +{ + if (m_lookTimer < CTimer::GetTimeInMilliseconds() || cancelPrevious) { + bIsLooking = true; + bIsRestoringLook = false; + m_pLookTarget = target; + m_pLookTarget->RegisterReference((CEntity**)&m_pLookTarget); + m_fLookDirection = 999999.0f; + m_lookTimer = 0; + bKeepTryingToLook = keepTryingToLook; + if (CanUseTorsoWhenLooking()) { + m_pedIK.m_flags &= ~CPedIK::LOOKAROUND_HEAD_ONLY; + } + } +} + +void +CPed::ClearLookFlag(void) { + if (bIsLooking) { + bIsLooking = false; + bIsRestoringLook = true; + bShakeFist = false; + + if (CanUseTorsoWhenLooking()) + m_pedIK.m_flags &= ~CPedIK::LOOKAROUND_HEAD_ONLY; + + if (IsPlayer()) + m_lookTimer = CTimer::GetTimeInMilliseconds() + 2000; + else + m_lookTimer = CTimer::GetTimeInMilliseconds() + 4000; + + if (m_nPedState == PED_LOOK_HEADING || m_nPedState == PED_LOOK_ENTITY) { + ClearLook(); + } + } +} + +void +CPed::MoveHeadToLook(void) +{ + CVector lookPos; + + if (m_lookTimer && CTimer::GetTimeInMilliseconds() > m_lookTimer) { + ClearLookFlag(); + } + + if (bIsLooking || bIsRestoringLook) + if (!CanUseTorsoWhenLooking()) + m_pedIK.m_flags |= CPedIK::LOOKAROUND_HEAD_ONLY; + + if (m_pLookTarget) { + if (m_pLookTarget->IsPed()) { + ((CPed*)m_pLookTarget)->m_pedIK.GetComponentPosition(lookPos, PED_MID); + } else { + lookPos = m_pLookTarget->GetPosition(); + } + + if (!m_pedIK.LookAtPosition(lookPos)) { + if (!bKeepTryingToLook) { + ClearLookFlag(); + } + return; + } + + if (!bShakeFist || bIsAimingGun || bIsRestoringGun) + return; + + if (m_nPedState == PED_ANSWER_MOBILE) + return; + + if (m_lookTimer - CTimer::GetTimeInMilliseconds() >= 1000) + return; + + bool handFreeToMove = false; + AnimationId animToPlay = ANIM_STD_NUM; + + if (!GetWeapon()->IsType2Handed() && GetWeapon()->m_eWeaponType != WEAPONTYPE_ROCKETLAUNCHER) + handFreeToMove = true; + + if (IsPlayer() && handFreeToMove) { + + if (m_pLookTarget->IsPed()) { +#ifdef FIX_BUGS + if (m_pedStats->m_temper > 49 || ((CPed*)m_pLookTarget)->m_nPedType == PEDTYPE_COP) +#else + if (m_pedStats->m_temper < 49 || ((CPed*)m_pLookTarget)->m_nPedType == PEDTYPE_COP) +#endif + animToPlay = ANIM_STD_PARTIAL_FUCKU; + else if(m_pedStats->m_temper < 47) + animToPlay = ANIM_STD_PARTIAL_PUNCH; + } else { + if (m_pedStats->m_temper > 49 || m_pLookTarget->GetModelIndex() == MI_POLICE) + animToPlay = ANIM_STD_PARTIAL_FUCKU; + } + } else if (handFreeToMove && (CGeneral::GetRandomNumber() & 1)) { + animToPlay = ANIM_STD_PARTIAL_FUCKU; + } + + if (animToPlay != ANIM_STD_NUM) { + CAnimBlendAssociation *newAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, animToPlay, 4.0f); + + if (newAssoc) { + newAssoc->flags |= ASSOC_FADEOUTWHENDONE; + newAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + } + bShakeFist = false; + } else if (m_fLookDirection == 999999.0f) { + ClearLookFlag(); + } else if (!m_pedIK.LookInDirection(m_fLookDirection, 0.0f)) { + if (!bKeepTryingToLook) + ClearLookFlag(); + } +} + +void +CPed::RestoreHeadPosition(void) +{ + if(!CanUseTorsoWhenLooking()) + m_pedIK.m_flags |= CPedIK::LOOKAROUND_HEAD_ONLY; + + if (m_pedIK.RestoreLookAt()) { + bIsRestoringLook = false; + if(CanUseTorsoWhenLooking()) + m_pedIK.m_flags &= ~CPedIK::LOOKAROUND_HEAD_ONLY; + } +} + +void +CPed::SetAimFlag(float angle) +{ + bIsAimingGun = true; + bIsRestoringGun = false; + m_fLookDirection = angle; + m_lookTimer = 0; + m_pLookTarget = nil; + m_pSeekTarget = nil; + + if (bIsDucking) + m_pedIK.m_flags &= ~CPedIK::AIMS_WITH_ARM; + + if (CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM)) + m_pedIK.m_flags |= CPedIK::AIMS_WITH_ARM; + else + m_pedIK.m_flags &= ~CPedIK::AIMS_WITH_ARM; +} + +void +CPed::SetAimFlag(CEntity *to) +{ + bIsAimingGun = true; + bIsRestoringGun = false; + if (m_pLookTarget) + m_pLookTarget->CleanUpOldReference(&m_pLookTarget); + m_pLookTarget = to; + m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); + if (m_pSeekTarget) + m_pSeekTarget->CleanUpOldReference(&m_pSeekTarget); + m_pSeekTarget = to; + m_pSeekTarget->RegisterReference((CEntity **) &m_pSeekTarget); + m_lookTimer = 0; +} + +void +CPed::ClearAimFlag(void) +{ + if (bIsAimingGun) { + bIsAimingGun = false; + bIsRestoringGun = true; + m_pedIK.m_flags &= ~CPedIK::AIMS_WITH_ARM; + m_lookTimer = 0; + } + + if (IsPlayer()) { + ((CPlayerPed*)this)->m_fFPSMoveHeading = 0.0f; +#ifdef FREE_CAM + ((CPlayerPed*)this)->m_bFreeAimActive = false; +#endif + } +} + +void +CPed::AimGun(void) +{ + CVector vector; + + if (IsPlayer() && bIsDucking) + m_pedIK.m_flags &= ~CPedIK::AIMS_WITH_ARM; + + if (m_pSeekTarget) { + if (m_pSeekTarget->IsPed()) { + ((CPed*)m_pSeekTarget)->m_pedIK.GetComponentPosition(vector, PED_MID); + } else { + vector = m_pSeekTarget->GetPosition(); + } + + if (!IsPlayer()) + Say(SOUND_PED_ATTACK); + + bCanPointGunAtTarget = m_pedIK.PointGunAtPosition(vector); + if (m_pLookTarget != m_pSeekTarget) { + SetLookFlag(m_pSeekTarget, true, true); + } + + } else { + if (IsPlayer()) { + bCanPointGunAtTarget = m_pedIK.PointGunInDirection(m_fLookDirection, ((CPlayerPed*)this)->m_fFPSMoveHeading); + } else { + bCanPointGunAtTarget = m_pedIK.PointGunInDirection(m_fLookDirection, 0.0f); + } + } +} + +void +CPed::RestoreGunPosition(void) +{ + if (bIsLooking) { + m_pedIK.m_flags &= ~CPedIK::LOOKAROUND_HEAD_ONLY; + bIsRestoringGun = false; + } else if (m_pedIK.RestoreGunPosn()) { + bIsRestoringGun = false; + } else { + if (IsPlayer()) + ((CPlayerPed*)this)->m_fFPSMoveHeading = 0.0f; + } +} + +bool +CPed::CanWeRunAndFireWithWeapon(void) +{ + return CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM); +} + +void +CPed::ScanForInterestingStuff(void) +{ + if (!IsPedInControl()) + return; + + if (m_objective != OBJECTIVE_NONE) + return; + + if (CharCreatedBy == MISSION_CHAR) + return; + + LookForSexyPeds(); + LookForSexyCars(); + if (LookForInterestingNodes()) + return; + + if (m_nPedType == PEDTYPE_CRIMINAL && m_carJackTimer < CTimer::GetTimeInMilliseconds()) { + // Find a car to steal or a ped to mug if we haven't already decided to steal a car + if (CGeneral::GetRandomNumber() % 100 < 10) { + int mostExpensiveVehAround = -1; + int bestMonetaryValue = 0; + + CVector pos = GetPosition(); + int16 lastVehicle; + CEntity *vehicles[8]; + CWorld::FindObjectsInRange(pos, 10.0f, true, &lastVehicle, 6, vehicles, false, true, false, false, false); + + for (int i = 0; i < lastVehicle; i++) { + CVehicle* veh = (CVehicle*)vehicles[i]; + + if (veh->VehicleCreatedBy != MISSION_VEHICLE) { + if (veh->m_vecMoveSpeed.Magnitude() <= 0.1f && veh->IsVehicleNormal() + && veh->IsCar() && bestMonetaryValue < veh->pHandling->nMonetaryValue) { + mostExpensiveVehAround = i; + bestMonetaryValue = veh->pHandling->nMonetaryValue; + } + } + } + if (bestMonetaryValue > 2000 && mostExpensiveVehAround != -1 && vehicles[mostExpensiveVehAround]) { + SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, vehicles[mostExpensiveVehAround]); + m_carJackTimer = CTimer::GetTimeInMilliseconds() + 5000; + return; + } + m_carJackTimer = CTimer::GetTimeInMilliseconds() + 5000; + } else if (m_objective != OBJECTIVE_MUG_CHAR && !(CGeneral::GetRandomNumber() & 7)) { + CPed *charToMug = nil; + for (int i = 0; i < m_numNearPeds; ++i) { + CPed *nearPed = m_nearPeds[i]; + + if ((nearPed->GetPosition() - GetPosition()).MagnitudeSqr() > sq(7.0f)) + break; + + if ((nearPed->m_nPedType == PEDTYPE_CIVFEMALE || nearPed->m_nPedType == PEDTYPE_CIVMALE + || nearPed->m_nPedType == PEDTYPE_CRIMINAL || nearPed->m_nPedType == PEDTYPE_UNUSED1 + || nearPed->m_nPedType == PEDTYPE_PROSTITUTE) + && nearPed->CharCreatedBy != MISSION_CHAR + && nearPed->IsPedShootable() + && nearPed->m_objective != OBJECTIVE_MUG_CHAR) { + charToMug = nearPed; + break; + } + } + if (charToMug) + SetObjective(OBJECTIVE_MUG_CHAR, charToMug); + + m_carJackTimer = CTimer::GetTimeInMilliseconds() + 5000; + } + } + + if (m_nPedState == PED_WANDER_PATH) { + if (CGeneral::GetRandomNumberInRange(0.0f, 1.0f) < 0.5f) { + if (CTimer::GetTimeInMilliseconds() > m_chatTimer) { + for (int i = 0; i < m_numNearPeds; i ++) { + if (m_nearPeds[i] && m_nearPeds[i]->m_nPedState == PED_WANDER_PATH) { + if ((GetPosition() - m_nearPeds[i]->GetPosition()).Magnitude() < 1.8f + && CanSeeEntity(m_nearPeds[i]) + && m_nearPeds[i]->CanSeeEntity(this) + && WillChat(m_nearPeds[i])) { + + int time = CGeneral::GetRandomNumber() % 4000 + 10000; + SetChat(m_nearPeds[i], time); + m_nearPeds[i]->SetChat(this, time); + } + } + } + } + } else { + m_chatTimer = CTimer::GetTimeInMilliseconds() + 200; + } + } +} + +bool +CPed::WillChat(CPed *stranger) +{ + if (m_pNextPathNode && m_pLastPathNode) { + if (m_pNextPathNode != m_pLastPathNode && ThePaths.TestCrossesRoad(m_pNextPathNode, m_pLastPathNode)) { + return false; + } + } + if (m_nSurfaceTouched == SURFACE_TARMAC) + return false; + if (stranger == this) + return false; + if (m_nPedType == stranger->m_nPedType) + return true; + if (m_nPedType == PEDTYPE_CRIMINAL) + return false; + if (stranger->m_nPedType == PEDTYPE_COP) + return false; + if (stranger->IsPlayer()) + return false; + if ((IsGangMember() || stranger->IsGangMember()) && m_nPedType != stranger->m_nPedType) + return false; + return true; +} + +void +CPed::CalculateNewVelocity(void) +{ + if (IsPedInControl()) { + float headAmount = DEGTORAD(m_headingRate) * CTimer::GetTimeStep(); + m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); + float limitedRotDest = CGeneral::LimitRadianAngle(m_fRotationDest); + + if (m_fRotationCur - PI > limitedRotDest) { + limitedRotDest += 2 * PI; + } else if(PI + m_fRotationCur < limitedRotDest) { + limitedRotDest -= 2 * PI; + } + + float neededTurn = limitedRotDest - m_fRotationCur; + if (neededTurn <= headAmount) { + if (neededTurn > (-headAmount)) + m_fRotationCur += neededTurn; + else + m_fRotationCur -= headAmount; + } else { + m_fRotationCur += headAmount; + } + } + + CVector2D forward(Sin(m_fRotationCur), Cos(m_fRotationCur)); + + m_moved.x = CrossProduct2D(m_vecAnimMoveDelta, forward); // (m_vecAnimMoveDelta.x * Cos(m_fRotationCur)) + -Sin(m_fRotationCur) * m_vecAnimMoveDelta.y; + m_moved.y = DotProduct2D(m_vecAnimMoveDelta, forward); // m_vecAnimMoveDelta.y* Cos(m_fRotationCur) + (m_vecAnimMoveDelta.x * Sin(m_fRotationCur)); + + if (CTimer::GetTimeStep() >= 0.01f) { + m_moved = m_moved * (1 / CTimer::GetTimeStep()); + } else { + m_moved = m_moved * (1 / 100.0f); + } + + if ((!TheCamera.Cams[TheCamera.ActiveCam].GetWeaponFirstPersonOn() && !TheCamera.Cams[0].Using3rdPersonMouseCam()) + || FindPlayerPed() != this || !CanStrafeOrMouseControl()) { + + if (FindPlayerPed() == this) + FindPlayerPed()->m_fWalkAngle = 0.0f; + return; + } + + float walkAngle = WorkOutHeadingForMovingFirstPerson(m_fRotationCur); + float pedSpeed = m_moved.Magnitude(); + float localWalkAngle = CGeneral::LimitRadianAngle(walkAngle - m_fRotationCur); + + if (localWalkAngle < -0.5f * PI) { + localWalkAngle += PI; + } else if (localWalkAngle > 0.5f * PI) { + localWalkAngle -= PI; + } + + // Interestingly this part is responsible for diagonal walking. + if (localWalkAngle > -DEGTORAD(50.0f) && localWalkAngle < DEGTORAD(50.0f)) { + TheCamera.Cams[TheCamera.ActiveCam].m_fPlayerVelocity = pedSpeed; + m_moved = CVector2D(-Sin(walkAngle), Cos(walkAngle)) * pedSpeed; + } + + CAnimBlendAssociation *idleAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE); + CAnimBlendAssociation *fightAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FIGHT_IDLE); + if(!fightAssoc) + fightAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_TIRED); + + if(!fightAssoc) + fightAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_MELEE_IDLE_FIGHTMODE); + + if ((!idleAssoc || idleAssoc->blendAmount < 0.5f) && !fightAssoc && !bIsDucking) { + LimbOrientation newUpperLegs; + newUpperLegs.yaw = localWalkAngle; + + if (newUpperLegs.yaw < -DEGTORAD(100.0f)) { + newUpperLegs.yaw += PI; + } else if (newUpperLegs.yaw > DEGTORAD(100.0f)) { + newUpperLegs.yaw -= PI; + } + + if (newUpperLegs.yaw > -DEGTORAD(50.0f) && newUpperLegs.yaw < DEGTORAD(50.0f)) { + newUpperLegs.pitch = 0.1f; + RwV3d Xaxis = { 1.0f, 0.0f, 0.0f }; + RwV3d Zaxis = { 0.0f, 0.0f, 1.0f }; + RtQuatRotate(&m_pFrames[PED_UPPERLEGL]->hanimFrame->q, &Zaxis, RADTODEG(newUpperLegs.pitch), rwCOMBINEPOSTCONCAT); + RtQuatRotate(&m_pFrames[PED_UPPERLEGL]->hanimFrame->q, &Xaxis, RADTODEG(newUpperLegs.yaw), rwCOMBINEPOSTCONCAT); + RtQuatRotate(&m_pFrames[PED_UPPERLEGR]->hanimFrame->q, &Zaxis, RADTODEG(newUpperLegs.pitch), rwCOMBINEPOSTCONCAT); + RtQuatRotate(&m_pFrames[PED_UPPERLEGR]->hanimFrame->q, &Xaxis, RADTODEG(newUpperLegs.yaw), rwCOMBINEPOSTCONCAT); + bDontAcceptIKLookAts = true; + } + } +} + +float +CPed::WorkOutHeadingForMovingFirstPerson(float offset) +{ + if (!IsPlayer()) + return 0.0f; + + CPad *pad0 = CPad::GetPad(0); + float leftRight = pad0->GetPedWalkLeftRight(); + float upDown = pad0->GetPedWalkUpDown(); + float &angle = ((CPlayerPed*)this)->m_fWalkAngle; + + if (upDown != 0.0f) { + angle = CGeneral::GetRadianAngleBetweenPoints(0.0f, 0.0f, -leftRight, upDown); + } else { + if (leftRight < 0.0f) + angle = HALFPI; + else if (leftRight > 0.0f) + angle = -HALFPI; + } + + return CGeneral::LimitRadianAngle(offset + angle); +} + +void +CPed::UpdatePosition(void) +{ + if (CReplay::IsPlayingBack() || !bIsStanding || m_attachedTo) + return; + + CVector2D velocityChange; + + SetHeading(m_fRotationCur); + if (m_pCurrentPhysSurface) { + CVector2D velocityOfSurface; + if (!IsPlayer() && m_pCurrentPhysSurface->IsVehicle() && ((CVehicle*)m_pCurrentPhysSurface)->IsBoat()) { + + // It seems R* didn't like m_vecOffsetFromPhysSurface for boats + CVector offsetToSurface = GetPosition() - m_pCurrentPhysSurface->GetPosition(); + offsetToSurface.z -= FEET_OFFSET; + + CVector surfaceMoveVelocity = m_pCurrentPhysSurface->m_vecMoveSpeed; + CVector surfaceTurnVelocity = CrossProduct(m_pCurrentPhysSurface->m_vecTurnSpeed, offsetToSurface); + + // Also we use that weird formula instead of friction if it's boat + float slideMult = -m_pCurrentPhysSurface->m_vecTurnSpeed.MagnitudeSqr(); + velocityOfSurface = slideMult * offsetToSurface * CTimer::GetTimeStep() + (surfaceTurnVelocity + surfaceMoveVelocity); + m_vecMoveSpeed.z = slideMult * offsetToSurface.z * CTimer::GetTimeStep() + (surfaceTurnVelocity.z + surfaceMoveVelocity.z); + } else { + velocityOfSurface = m_pCurrentPhysSurface->GetSpeed(m_vecOffsetFromPhysSurface); + } + // Reminder: m_moved is displacement from walking/running. + velocityChange = m_moved + velocityOfSurface - m_vecMoveSpeed; + m_fRotationCur += m_pCurrentPhysSurface->m_vecTurnSpeed.z * CTimer::GetTimeStep(); + m_fRotationDest += m_pCurrentPhysSurface->m_vecTurnSpeed.z * CTimer::GetTimeStep(); + } else if (m_nSurfaceTouched == SURFACE_STEEP_CLIFF && (m_vecDamageNormal.x != 0.0f || m_vecDamageNormal.y != 0.0f)) { + // Ped got damaged by steep slope + m_vecMoveSpeed = CVector(0.0f, 0.0f, -0.001f); + // some kind of + CVector2D reactionForce = m_vecDamageNormal; + reactionForce.Normalise(); + + velocityChange = 0.02f * reactionForce + m_moved; + + float reactionAndVelocityDotProd = DotProduct2D(reactionForce, velocityChange); + // they're in same direction + if (reactionAndVelocityDotProd < 0.0f) { + velocityChange -= reactionAndVelocityDotProd * reactionForce; + } + } else { + velocityChange = m_moved - m_vecMoveSpeed; + } + + // Take time step into account + if (m_pCurrentPhysSurface && (!m_pCurrentPhysSurface->bInfiniteMass || m_pCurrentPhysSurface->m_phy_flagA08)) { + float speedChange = velocityChange.Magnitude(); + float changeMult = speedChange; + if (m_nPedState == PED_DIE && m_pCurrentPhysSurface->IsVehicle()) { + changeMult = 0.002f * CTimer::GetTimeStep(); + } else if (!(m_pCurrentPhysSurface->IsVehicle() && ((CVehicle*)m_pCurrentPhysSurface)->IsBoat())) { + changeMult = 0.01f * CTimer::GetTimeStep(); + } + + if (speedChange > changeMult) { + velocityChange = velocityChange * (changeMult / speedChange); + } + } + m_vecMoveSpeed.x += velocityChange.x; + m_vecMoveSpeed.y += velocityChange.y; +} + +void +CPed::CalculateNewOrientation(void) +{ + if (CReplay::IsPlayingBack() || !IsPedInControl()) + return; + + SetHeading(m_fRotationCur); +} + +void +CPed::ClearAll(void) +{ + if (!IsPedInControl() && m_nPedState != PED_DEAD) + return; + + SetPedState(PED_NONE); + SetMoveState(PEDMOVE_NONE); + m_pSeekTarget = nil; + m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f); + m_fleeFromPos = CVector2D(0.0f, 0.0f); + m_fleeFrom = nil; + m_fleeTimer = 0; + m_threatEx = nil; + bUsesCollision = true; + ClearPointGunAt(); + bIsPointingGunAt = false; + bRenderPedInCar = true; + bKnockedUpIntoAir = false; + bKnockedOffBike = false; + m_pCollidingEntity = nil; +} + +void +CPed::ProcessBuoyancy(void) +{ + float buoyancyLevel = 1.1f; + static uint32 nGenerateRaindrops = 0; + static uint32 nGenerateWaterCircles = 0; + CRGBA color; + + if (bInVehicle) + return; + + CVector buoyancyPoint; + CVector buoyancyImpulse; + + if (DyingOrDead()) + buoyancyLevel = 1.8f; + + if (mod_Buoyancy.ProcessBuoyancy(this, GRAVITY * m_fMass * buoyancyLevel, &buoyancyPoint, &buoyancyImpulse)) { + bTouchingWater = true; + CEntity *entity; + CColPoint point; + if (CWorld::ProcessVerticalLine(GetPosition(), GetPosition().z - 3.0f, point, entity, false, true, false, false, false, false, nil) + && entity->IsVehicle() && ((CVehicle*)entity)->IsBoat() && !entity->bRenderScorched) { + bIsInWater = false; + return; + } + color.r = (0.5f * CTimeCycle::GetDirectionalRed() + CTimeCycle::GetAmbientRed()) * 127.5f; + color.g = (0.5f * CTimeCycle::GetDirectionalBlue() + CTimeCycle::GetAmbientBlue()) * 127.5f; + color.b = (0.5f * CTimeCycle::GetDirectionalGreen() + CTimeCycle::GetAmbientGreen()) * 127.5f; + color.a = CGeneral::GetRandomNumberInRange(48.0f, 96.0f); + bIsInWater = true; + ApplyMoveForce(buoyancyImpulse); + if (!DyingOrDead()) { + if (bTryingToReachDryLand) { + if (buoyancyImpulse.z / m_fMass > GRAVITY * 0.4f * CTimer::GetTimeStep()) { + bTryingToReachDryLand = false; + CVector pos = GetPosition(); + if (PlacePedOnDryLand()) { + if (m_fHealth > 20.0f) + InflictDamage(nil, WEAPONTYPE_DROWNING, 15.0f, PEDPIECE_TORSO, false); + + if (bIsInTheAir) { + RpAnimBlendClumpSetBlendDeltas(GetClump(), ASSOC_PARTIAL, -1000.0f); + bIsInTheAir = false; + } + pos.z = pos.z - 0.8f; + CParticleObject::AddObject(POBJECT_PED_WATER_SPLASH, pos, CVector(0.0f, 0.0f, 0.0f), 0.0f, 50, color, true); + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + SetPedState(PED_IDLE); + return; + } + } + } + } + float speedMult = 0.0f; + if (buoyancyImpulse.z / m_fMass > GRAVITY * CTimer::GetTimeStep() + || mod_Buoyancy.m_waterlevel > GetPosition().z + 0.6f) { + speedMult = pow(0.9f, CTimer::GetTimeStep()); + m_vecMoveSpeed.x *= speedMult; + m_vecMoveSpeed.y *= speedMult; + m_vecMoveSpeed.z *= speedMult; + bIsStanding = false; + bIsDrowning = true; + InflictDamage(nil, WEAPONTYPE_DROWNING, 3.0f * CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); + } + if (buoyancyImpulse.z / m_fMass > GRAVITY * 0.25f * CTimer::GetTimeStep()) { + if (speedMult == 0.0f) { + speedMult = pow(0.9f, CTimer::GetTimeStep()); + } + m_vecMoveSpeed.x *= speedMult; + m_vecMoveSpeed.y *= speedMult; + if (m_vecMoveSpeed.z >= -0.1f) { + if (m_vecMoveSpeed.z < -0.04f) + m_vecMoveSpeed.z = -0.02f; + } else { + m_vecMoveSpeed.z = -0.01f; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_SPLASH, 0.0f); + CVector aBitForward = 2.2f * m_vecMoveSpeed + GetPosition(); + float level = 0.0f; + if (CWaterLevel::GetWaterLevel(aBitForward, &level, false)) + aBitForward.z = level; + + CParticleObject::AddObject(POBJECT_PED_WATER_SPLASH, aBitForward, CVector(0.0f, 0.0f, 0.1f), 0.0f, 200, color, true); + nGenerateRaindrops = CTimer::GetTimeInMilliseconds() + 80; + nGenerateWaterCircles = CTimer::GetTimeInMilliseconds() + 100; + } + } + if (nGenerateWaterCircles && CTimer::GetTimeInMilliseconds() >= nGenerateWaterCircles) { + CVector pos = GetPosition(); + float level = 0.0f; + if (CWaterLevel::GetWaterLevel(pos, &level, false)) + pos.z = level; + + if (pos.z != 0.0f) { + nGenerateWaterCircles = 0; + for(int i = 0; i < 4; i++) { + pos.x += CGeneral::GetRandomNumberInRange(-0.75f, 0.75f); + pos.y += CGeneral::GetRandomNumberInRange(-0.75f, 0.75f); + CParticle::AddParticle(PARTICLE_RAIN_SPLASH_BIGGROW, pos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, color, 0, 0, 0, 0); + } + } + } + if (nGenerateRaindrops && CTimer::GetTimeInMilliseconds() >= nGenerateRaindrops) { + CVector pos = GetPosition(); + float level = 0.0f; + if (CWaterLevel::GetWaterLevel(pos, &level, false)) + pos.z = level; + + if (pos.z >= 0.0f) { + pos.z += 0.25f; + nGenerateRaindrops = 0; + CParticleObject::AddObject(POBJECT_SPLASHES_AROUND, pos, CVector(0.0f, 0.0f, 0.0f), 4.5f, 1500, CRGBA(0,0,0,0), true); + } + } + } else + bTouchingWater = false; +} + +void +CPed::ProcessControl(void) +{ + CColPoint foundCol; + CEntity *foundEnt = nil; + + if (CTimer::GetFrameCounter() + m_randomSeed % 32 == 0) + PruneReferences(); + + int alpha = CVisibilityPlugins::GetClumpAlpha(GetClump()); + if (!bFadeOut) { + if (alpha < 255) { + alpha += 16; + if (alpha > 255) + alpha = 255; + } + } else { + alpha -= 8; + if (alpha < 0) + alpha = 0; + } + + CVisibilityPlugins::SetClumpAlpha(GetClump(), alpha); + bIsShooting = false; + bDonePositionOutOfCollision = false; + BuildPedLists(); + bIsInWater = false; + bIsDrowning = false; + ProcessBuoyancy(); + + if (m_nPedState != PED_ARRESTED) { + if (m_nPedState == PED_DEAD) { + DeadPedMakesTyresBloody(); + if (CGame::nastyGame && !bIsInWater) { + uint32 remainingBloodyFpTime = CTimer::GetTimeInMilliseconds() - m_bloodyFootprintCountOrDeathTime; + float timeDependentDist; + if (remainingBloodyFpTime >= 2000) { + if (remainingBloodyFpTime <= 7000) + timeDependentDist = (remainingBloodyFpTime - 2000) / 5000.0f * 0.75f; + else + timeDependentDist = 0.75f; + } else { + timeDependentDist = 0.0f; + } + + for (int i = 0; i < m_numNearPeds; ++i) { + CPed *nearPed = m_nearPeds[i]; + if (!nearPed->DyingOrDead()) { + CVector dist = nearPed->GetPosition() - GetPosition(); + if (dist.MagnitudeSqr() < sq(timeDependentDist)) { + nearPed->m_bloodyFootprintCountOrDeathTime = 200; + nearPed->bDoBloodyFootprints = true; + if (nearPed->IsPlayer()) { + if (!nearPed->bIsLooking && nearPed->m_nPedState != PED_ATTACK) { + int16 camMode = TheCamera.Cams[TheCamera.ActiveCam].Mode; + if (camMode != CCam::MODE_SNIPER + && camMode != CCam::MODE_CAMERA + && camMode != CCam::MODE_ROCKETLAUNCHER + && camMode != CCam::MODE_M16_1STPERSON + && camMode != CCam::MODE_1STPERSON + && camMode != CCam::MODE_HELICANNON_1STPERSON + && !TheCamera.Cams[TheCamera.ActiveCam].GetWeaponFirstPersonOn()) { + + nearPed->SetLookFlag(this, true); + nearPed->SetLookTimer(500); + } + } + } + } + } + } + + if (remainingBloodyFpTime > 2000) { + CVector bloodPos = GetPosition(); + if (remainingBloodyFpTime - 2000 >= 5000) { + if (!m_deadBleeding) { + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &bloodPos, + 0.75f, 0.0f, 0.0f, -0.75f, 255, 255, 0, 0, 4.0f, 40000, 1.0f); + m_deadBleeding = true; + } + } else { + CShadows::StoreStaticShadow( + (uintptr)this + 17, SHADOWTYPE_DARK, gpBloodPoolTex, &bloodPos, + (remainingBloodyFpTime - 2000) / 5000.0f * 0.75f, 0.0f, + 0.0f, (remainingBloodyFpTime - 2000) / 5000.0f * -0.75f, + 255, 255, 0, 0, 4.0f, 1.0f, 40.0f, false, 0.0f); + } + } + } + if (ServiceTalkingWhenDead()) + ServiceTalking(); + + if (bIsInWater) { + bIsStanding = false; + bWasStanding = false; + CPhysical::ProcessControl(); + } + return; + } + + bWasStanding = false; + if (bIsStanding) { + if (!CWorld::bForceProcessControl) { + if (m_pCurrentPhysSurface && m_pCurrentPhysSurface->bIsInSafePosition) { + bWasPostponed = true; + return; + } + } + } + + if (!IsPedInControl() || m_nWaitState != WAITSTATE_FALSE || 0.01f * CTimer::GetTimeStep() <= m_fDistanceTravelled + || (m_nStoredMoveState != PEDMOVE_WALK && m_nStoredMoveState != PEDMOVE_RUN && m_nStoredMoveState != PEDMOVE_SPRINT)) + m_panicCounter = 0; + else if (m_panicCounter < 50) + ++m_panicCounter; + + if (m_fHealth <= 1.0f && m_nPedState <= PED_STATES_NO_AI && !bIsInTheAir && !bIsLanding) + SetDie(); + + if (bIsStanding) + bPushedAlongByCar = false; + + bCollidedWithMyVehicle = false; + + CEntity *collidingEnt = m_pDamageEntity; + if (!bUsesCollision || ((!collidingEnt || m_fDamageImpulse <= 0.0f) && (!IsPlayer() || !bIsStuck)) || m_nPedState == PED_DIE) { + bHitSomethingLastFrame = false; + if (m_nPedStateTimer <= 500 && bIsInTheAir) { + if (m_nPedStateTimer) + m_nPedStateTimer--; + } else if (m_nPedStateTimer < 1001) { + m_nPedStateTimer = 0; + } + } else if (!GetPedAttractorManager()->IsInQueue(this, m_attractor)) { + if (m_panicCounter == 50 && IsPedInControl()) { + SetWaitState(WAITSTATE_STUCK, nil); + // Leftover + /* + if (m_nPedType < PEDTYPE_COP) { + + } else { + + } + */ + } else if (collidingEnt) { + switch (collidingEnt->GetType()) + { + case ENTITY_TYPE_BUILDING: + case ENTITY_TYPE_OBJECT: + { + CBaseModelInfo *collidingModel = CModelInfo::GetModelInfo(collidingEnt->GetModelIndex()); + CColModel *collidingCol = collidingModel->GetColModel(); + if (collidingEnt->IsObject() && ((CObject*)collidingEnt)->m_nSpecialCollisionResponseCases != COLLRESPONSE_FENCEPART + || collidingCol->boundingBox.max.x < 3.0f + && collidingCol->boundingBox.max.y < 3.0f) { + + if (!IsPlayer()) { + SetDirectionToWalkAroundObject(collidingEnt); + break; + } + } + if (IsPlayer()) { + bHitSomethingLastFrame = true; + break; + } + + float angleToFaceWhenHit = CGeneral::GetRadianAngleBetweenPoints( + GetPosition().x, + GetPosition().y, + m_vecDamageNormal.x + GetPosition().x, + m_vecDamageNormal.y + GetPosition().y); + + float neededTurn = Abs(m_fRotationCur - angleToFaceWhenHit); + + if (neededTurn > PI) + neededTurn = TWOPI - neededTurn; + + float oldDestRot = CGeneral::LimitRadianAngle(m_fRotationDest); + + if (m_nPedState == PED_FOLLOW_PATH) { + if (DotProduct(m_vecDamageNormal, GetForward()) < -0.866f && CanPedJumpThis(collidingEnt, &m_vecDamageNormal)) { + SetJump(); + } + break; + } + + if (m_pedInObjective && + (m_objective == OBJECTIVE_GOTO_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT)) { + + if (m_pedInObjective->IsPlayer() + && (neededTurn < DEGTORAD(20.0f) || m_panicCounter > 10)) { + if (CanPedJumpThis(collidingEnt)) { + SetJump(); + } else if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT) { + if (m_nPedType == PEDTYPE_COP && m_nWaitState != WAITSTATE_LOOK_ABOUT) + ((CCopPed*)this)->field_624++; + + SetWaitState(WAITSTATE_LOOK_ABOUT, nil); + } else { + SetWaitState(WAITSTATE_PLAYANIM_TAXI, nil); + m_headingRate = 0.0f; + SetLookFlag(m_pedInObjective, true); + SetLookTimer(3000); + Say(SOUND_PED_TAXI_CALL); + } + } else { + if (m_pLookTarget) + m_pLookTarget->CleanUpOldReference(&m_pLookTarget); + + m_pLookTarget = m_pedInObjective; + m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); + TurnBody(); + } + } else { + if (m_nPedType != PEDTYPE_COP && neededTurn < DEGTORAD(15.0f) && m_nWaitState == WAITSTATE_FALSE) { + if ((m_nStoredMoveState == PEDMOVE_RUN || m_nStoredMoveState == PEDMOVE_SPRINT) && m_vecDamageNormal.z < 0.3f) { + CAnimBlendAssociation *runAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUN); + if (!runAssoc) + runAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNFAST); + + if (runAssoc && runAssoc->blendAmount > 0.9f && runAssoc->IsRunning()) { + SetWaitState(WAITSTATE_HITWALL, nil); + } + } + } + if (m_nPedState == PED_FLEE_POS) { + CVector2D fleePos = collidingEnt->GetPosition(); + uint32 oldFleeTimer = m_fleeTimer; + SetFlee(fleePos, 5000); + if (oldFleeTimer != m_fleeTimer) + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 500; + + } else { + if (m_nPedState == PED_FLEE_ENTITY && (neededTurn < DEGTORAD(25.0f) || m_panicCounter > 10)) { + m_collidingThingTimer = CTimer::GetTimeInMilliseconds() + 2500; + m_collidingEntityWhileFleeing = collidingEnt; + m_collidingEntityWhileFleeing->RegisterReference((CEntity **) &m_collidingEntityWhileFleeing); + + if (m_nWaitState != WAITSTATE_HITWALL) + SetWaitState(WAITSTATE_TURN180, nil); + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 5000; + Flee(); + } else { + if (neededTurn < DEGTORAD(60.0f)) { + CVector posToHead = m_vecDamageNormal * 4.0f; + posToHead.z = 0.0f; + posToHead += GetPosition(); + int closestNodeId = ThePaths.FindNodeClosestToCoors(posToHead, PATH_PED, + 999999.9f, false, false); + float angleToFace; + + if (m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT && m_objective != OBJECTIVE_KILL_CHAR_ANY_MEANS) { + if (m_nPedState != PED_SEEK_POS && m_nPedState != PED_SEEK_CAR) { + if (m_nPedState == PED_WANDER_PATH) { + m_pNextPathNode = &ThePaths.m_pathNodes[closestNodeId]; + CVector bestCoords = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); + angleToFace = CGeneral::GetRadianAngleBetweenPoints( + bestCoords.x, bestCoords.y, + GetPosition().x, GetPosition().y); + + } else if (m_nPedState == PED_FOLLOW_PATH) { + CVector bestCoords = m_pathNodesToGo[m_nCurPathNodeId]->GetPosition(); + angleToFace = CGeneral::GetRadianAngleBetweenPoints( + bestCoords.x, bestCoords.y, + GetPosition().x, GetPosition().y); + + } else { + if (ThePaths.m_pathNodes[closestNodeId].GetX() == 0.0f + || ThePaths.m_pathNodes[closestNodeId].GetY() == 0.0f) { + posToHead = (3.0f * m_vecDamageNormal) + GetPosition(); + posToHead.x += (CGeneral::GetRandomNumber() % 512) / 250.0f - 1.0f; + posToHead.y += (CGeneral::GetRandomNumber() % 512) / 250.0f - 1.0f; + } else { + posToHead.x = ThePaths.m_pathNodes[closestNodeId].GetX(); + posToHead.y = ThePaths.m_pathNodes[closestNodeId].GetY(); + posToHead.z = ThePaths.m_pathNodes[closestNodeId].GetZ(); + } + angleToFace = CGeneral::GetRadianAngleBetweenPoints( + posToHead.x, posToHead.y, + GetPosition().x, GetPosition().y); + + if (m_nPedState != PED_FOLLOW_PATH) + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 500; + } + } else { + angleToFace = CGeneral::GetRadianAngleBetweenPoints( + ThePaths.m_pathNodes[closestNodeId].GetX(), + ThePaths.m_pathNodes[closestNodeId].GetY(), + GetPosition().x, + GetPosition().y); + + CVector2D distToNode = ThePaths.m_pathNodes[closestNodeId].GetPosition() - GetPosition(); + CVector2D distToSeekPos = m_vecSeekPos - GetPosition(); + + if (DotProduct2D(distToNode, distToSeekPos) < 0.0f) { + m_fRotationCur = m_fRotationDest; + break; + } + } + } else { + float angleToFaceAwayDamage = CGeneral::GetRadianAngleBetweenPoints( + m_vecDamageNormal.x, + m_vecDamageNormal.y, + 0.0f, + 0.0f); + + if (angleToFaceAwayDamage < m_fRotationCur) + angleToFaceAwayDamage += TWOPI; + + float neededTurn = angleToFaceAwayDamage - m_fRotationCur; + + if (neededTurn <= PI) { + angleToFace = 0.5f * neededTurn + m_fRotationCur; + m_fRotationCur += DEGTORAD(m_pedStats->m_headingChangeRate) * 2.0f; + } else { + angleToFace = m_fRotationCur - (TWOPI - neededTurn) * 0.5f; + m_fRotationCur -= DEGTORAD(m_pedStats->m_headingChangeRate) * 2.0f; + } + + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 200; + if (m_nPedType == PEDTYPE_COP) { + if (m_pedInObjective) { + float angleToLookCriminal = CGeneral::GetRadianAngleBetweenPoints( + m_pedInObjective->GetPosition().x, + m_pedInObjective->GetPosition().y, + GetPosition().x, + GetPosition().y); + + angleToLookCriminal = CGeneral::LimitRadianAngle(angleToLookCriminal); + angleToFace = CGeneral::LimitRadianAngle(angleToFace); + + if (angleToLookCriminal < angleToFace) + angleToLookCriminal += TWOPI; + + float neededTurnToCriminal = angleToLookCriminal - angleToFace; + + if (neededTurnToCriminal > DEGTORAD(150.0f) && neededTurnToCriminal < DEGTORAD(210.0f)) { + ((CCopPed*)this)->m_bStopAndShootDisabledZone = true; + } + } + } + } + m_fRotationDest = CGeneral::LimitRadianAngle(angleToFace); + + if (m_fRotationCur - PI > m_fRotationDest) { + m_fRotationDest += TWOPI; + } else if (PI + m_fRotationCur < m_fRotationDest) { + m_fRotationDest -= TWOPI; + } + + if (oldDestRot == m_fRotationDest && CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) { + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 200; + m_fRotationDest += HALFPI; + } + } + } + } + } + + if (m_nPedState != PED_WANDER_PATH && m_nPedState != PED_FLEE_ENTITY) + m_pNextPathNode = nil; + + bHitSomethingLastFrame = true; + break; + } + case ENTITY_TYPE_VEHICLE: + { + CVehicle* collidingVeh = ((CVehicle*)collidingEnt); + float collidingVehSpeedSqr = collidingVeh->m_vecMoveSpeed.MagnitudeSqr(); + + if (collidingVeh == m_pMyVehicle) + bCollidedWithMyVehicle = true; + + float oldHealth = m_fHealth; + bool playerSufferSound = false; + + if (collidingVehSpeedSqr <= 1.0f / 400.0f) { + if (IsPedInControl() + && (!IsPlayer() + || m_objective == OBJECTIVE_GOTO_AREA_ON_FOOT + || m_objective == OBJECTIVE_RUN_TO_AREA + || m_objective == OBJECTIVE_SPRINT_TO_AREA + || m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER + || IsUseAttractorObjective(m_objective))) { + + if (collidingVeh != m_pCurrentPhysSurface || IsPlayer()) { + if (!bVehEnterDoorIsBlocked) { + if (collidingVeh->GetStatus() != STATUS_PLAYER || CharCreatedBy == MISSION_CHAR) { + + if (m_nPedState == PED_SEEK_CAR) { + SetDirectionToWalkAroundObject(collidingVeh); + CWorld::Players[CWorld::PlayerInFocus].m_nLastBumpPlayerCarTimer = m_nPedStateTimer; + } else { + SetDirectionToWalkAroundVehicle(collidingVeh); + } + } else { + if (CTimer::GetTimeInMilliseconds() >= CWorld::Players[CWorld::PlayerInFocus].m_nLastBumpPlayerCarTimer + || m_nPedStateTimer >= CTimer::GetTimeInMilliseconds()) { + + if (m_nPedState == PED_SEEK_CAR) { + SetDirectionToWalkAroundObject(collidingVeh); + CWorld::Players[CWorld::PlayerInFocus].m_nLastBumpPlayerCarTimer = m_nPedStateTimer; + } else { + SetDirectionToWalkAroundVehicle(collidingVeh); + } + } else if (m_fleeFrom != collidingVeh) { + SetFlee(collidingVeh, 4000); + bUsePedNodeSeek = false; + SetMoveState(PEDMOVE_WALK); + } + } + } + } else { + float angleLeftToCompleteTurn = Abs(m_fRotationCur - m_fRotationDest); + if (angleLeftToCompleteTurn < 0.01f && CanPedJumpThis(collidingVeh)) { + SetJump(); + } + } + } else if (IsPlayer() && !bIsInTheAir) { + + if (IsPedInControl() && ((CPlayerPed*)this)->m_fMoveSpeed == 0.0f + && !bIsLooking && CTimer::GetTimeInMilliseconds() > m_lookTimer && collidingVeh->pDriver) { + + ((CPlayerPed*)this)->AnnoyPlayerPed(false); + SetLookFlag(collidingVeh, true); + SetLookTimer(1300); + + eWeaponType weaponType = GetWeapon()->m_eWeaponType; + uint32 weaponSlot = CWeaponInfo::GetWeaponInfo(weaponType)->m_nWeaponSlot; + if (weaponType == WEAPONTYPE_UNARMED + || weaponSlot == 3 + || weaponSlot == 5 + || weaponSlot == 1) { + bShakeFist = true; + } + } else { + SetLookFlag(collidingVeh, true); + SetLookTimer(500); + } + } + } else { + float adjustedImpulse = m_fDamageImpulse; + if (IsPlayer()) { + if (bIsStanding) { + float forwardVecAndDamageDirDotProd = DotProduct(m_vecAnimMoveDelta.y * GetForward(), m_vecDamageNormal); + if (forwardVecAndDamageDirDotProd < 0.0f) { + adjustedImpulse = forwardVecAndDamageDirDotProd * m_fMass + m_fDamageImpulse; + if (adjustedImpulse < 0.0f) + adjustedImpulse = 0.0f; + } + } + } + if (m_fMass / 20.0f < adjustedImpulse) + DMAudio.PlayOneShot(collidingVeh->m_audioEntityId, SOUND_CAR_PED_COLLISION, adjustedImpulse); + + if (IsPlayer()) { + if (adjustedImpulse > 20.0f) + adjustedImpulse = 20.0f; + + if (adjustedImpulse > 5.0f) { + if (adjustedImpulse <= 13.0f) + playerSufferSound = true; + else + Say(SOUND_PED_DAMAGE); + } + + CColModel *collidingCol = CModelInfo::GetColModel(collidingVeh->m_modelIndex); + CVector colMinVec = collidingCol->boundingBox.min; + CVector colMaxVec = collidingCol->boundingBox.max; + + CVector vehColCenterDist = collidingVeh->GetMatrix() * ((colMinVec + colMaxVec) * 0.5f) - GetPosition(); + + // TLVC = To look vehicle center + + float angleToVehFront = collidingVeh->GetForward().Heading(); + float angleDiffFromLookingFrontTLVC = angleToVehFront - vehColCenterDist.Heading(); + angleDiffFromLookingFrontTLVC = CGeneral::LimitRadianAngle(angleDiffFromLookingFrontTLVC); + + // I don't know why do we use that + float vehTopRightHeading = Atan2(colMaxVec.x - colMinVec.x, colMaxVec.y - colMinVec.y); + + CVector vehDist = GetPosition() - collidingVeh->GetPosition(); + vehDist.Normalise(); + + float vehRightVecAndSpeedDotProd; + + if (Abs(angleDiffFromLookingFrontTLVC) >= vehTopRightHeading && Abs(angleDiffFromLookingFrontTLVC) < PI - vehTopRightHeading) { + if (angleDiffFromLookingFrontTLVC <= 0.0f) { + vehRightVecAndSpeedDotProd = DotProduct(collidingVeh->GetRight(), collidingVeh->m_vecMoveSpeed); + + // vehRightVecAndSpeedDotProd < 0.1f = Vehicle being overturned or spinning to it's right? + if (collidingVehSpeedSqr > 1.0f / 100.0f && vehRightVecAndSpeedDotProd < 0.1f) { + + // Car's right faces towards us and isn't coming directly to us + if (DotProduct(collidingVeh->GetRight(), GetForward()) < 0.0f + && DotProduct(vehDist, collidingVeh->m_vecMoveSpeed) > 0.0f) { + SetEvasiveStep(collidingVeh, 1); + } + } + } else { + vehRightVecAndSpeedDotProd = DotProduct(-1.0f * collidingVeh->GetRight(), collidingVeh->m_vecMoveSpeed); + + if (collidingVehSpeedSqr > 1.0f / 100.0f && vehRightVecAndSpeedDotProd < 0.1f) { + if (DotProduct(collidingVeh->GetRight(), GetForward()) > 0.0f + && DotProduct(vehDist, collidingVeh->m_vecMoveSpeed) > 0.0f) { + SetEvasiveStep(collidingVeh, 1); + } + } + } + } else { + vehRightVecAndSpeedDotProd = DotProduct(vehDist, collidingVeh->m_vecMoveSpeed); + } + + if (vehRightVecAndSpeedDotProd <= 0.1f) { + if (m_nPedState != PED_FIGHT) { + SetLookFlag(collidingVeh, true); + SetLookTimer(700); + } + } else { + bIsStanding = false; + CVector2D collidingEntMoveDir = -collidingVeh->m_vecMoveSpeed; + int dir = GetLocalDirection(collidingEntMoveDir); + SetFall(1000, (AnimationId)(dir + ANIM_STD_HIGHIMPACT_FRONT), false); + + float damage; + if (collidingVeh->m_modelIndex == MI_TRAIN) { + damage = 50.0f; + } else { + damage = 20.0f; + } + + InflictDamage(collidingVeh, WEAPONTYPE_RAMMEDBYCAR, damage, PEDPIECE_TORSO, dir); + Say(SOUND_PED_DAMAGE); + } + } else { + KillPedWithCar(collidingVeh, m_fDamageImpulse); + } + + if (m_pCollidingEntity != collidingEnt) + bPushedAlongByCar = true; + } + if (m_fHealth < oldHealth && playerSufferSound) + Say(SOUND_PED_HIT); + + break; + } + case ENTITY_TYPE_PED: + { + CollideWithPed((CPed*)collidingEnt); + if (((CPed*)collidingEnt)->IsPlayer()) { + CPlayerPed *player = ((CPlayerPed*)collidingEnt); + Say(SOUND_PED_CHAT); + if (m_nMoveState > PEDMOVE_STILL && player->IsPedInControl()) { + if (player->m_fMoveSpeed < 1.0f) { + if (!player->bIsLooking) { + if (CTimer::GetTimeInMilliseconds() > player->m_lookTimer) { + player->AnnoyPlayerPed(false); + player->SetLookFlag(this, true); + player->SetLookTimer(1300); + eWeaponType weaponType = player->GetWeapon()->m_eWeaponType; + uint32 weaponSlot = CWeaponInfo::GetWeaponInfo(weaponType)->m_nWeaponSlot; + if (weaponType == WEAPONTYPE_UNARMED + || weaponSlot == 3 + || weaponSlot == 5 + || weaponSlot == 1) { + player->bShakeFist = true; + } + } + } + } + } + } + break; + } + default: + break; + } + } + CVector forceDir; + if (!bIsInTheAir && m_nPedState != PED_JUMP && m_fDamageImpulse > 0.0f) { + forceDir = m_vecDamageNormal; + forceDir.z = 0.0f; + if (!bIsStanding) { + forceDir *= 4.0f; + } else { + forceDir *= 0.5f; + } + + ApplyMoveForce(forceDir); + } + if ((bIsInTheAir && !DyingOrDead()) || (!bIsStanding && !bWasStanding && m_nPedState == PED_FALL)) { + if (m_nPedStateTimer > 0 && m_nPedStateTimer <= 1000) { + forceDir = GetPosition() - m_vecHitLastPos; + } else { + m_nPedStateTimer = 0; + m_vecHitLastPos = GetPosition(); + forceDir = CVector(0.0f, 0.0f, 0.0f); + } + + CVector offsetToCheck; + m_nPedStateTimer++; + + float adjustedTs = Max(CTimer::GetTimeStep(), 0.01f); + + CPad *pad0 = CPad::GetPad(0); + if ((m_nPedStateTimer <= 50.0f / (4.0f * adjustedTs) || m_nPedStateTimer * 0.01f <= forceDir.MagnitudeSqr()) + && (m_nCollisionRecords <= 1 || m_nPedStateTimer <= 50.0f / (2.0f * adjustedTs) || m_nPedStateTimer * 1.0f / 250.0f <= Abs(forceDir.z))) { + + if (m_nCollisionRecords == 1 && m_aCollisionRecords[0] != nil && m_aCollisionRecords[0]->IsBuilding() + && m_nPedStateTimer > 50.0f / (2.0f * adjustedTs) && m_nPedStateTimer * 1.0f / 250.0f > Abs(forceDir.z)) { + offsetToCheck.x = -forceDir.y; + offsetToCheck.z = 1.0f; + offsetToCheck.y = forceDir.x; + offsetToCheck.Normalise(); + + CVector posToCheck = GetPosition() + offsetToCheck; + + // These are either obstacle or ground to land, I don't know which one. + float obstacleForFlyingZ, obstacleForFlyingOtherDirZ; + CColPoint obstacleForFlying, obstacleForFlyingOtherDir; + + // Check is there any room for being knocked up in reverse direction of force + if (CWorld::ProcessVerticalLine(posToCheck, -20.0f, obstacleForFlying, foundEnt, true, false, false, false, false, false, nil)) { + obstacleForFlyingZ = obstacleForFlying.point.z; + } else { + obstacleForFlyingZ = 500.0f; + } + + posToCheck = GetPosition() - offsetToCheck; + + // Now check for direction of force this time + if (CWorld::ProcessVerticalLine(posToCheck, -20.0f, obstacleForFlyingOtherDir, foundEnt, true, false, false, false, false, false, nil)) { + obstacleForFlyingOtherDirZ = obstacleForFlyingOtherDir.point.z; + } else { + obstacleForFlyingOtherDirZ = 501.0f; + } + int16 flyDir = 0; + float feetZ = GetPosition().z - FEET_OFFSET; +#ifdef FIX_BUGS + if (obstacleForFlyingZ > feetZ && obstacleForFlyingZ < 500.0f) + flyDir = 1; + else if (obstacleForFlyingOtherDirZ > feetZ && obstacleForFlyingOtherDirZ < 501.0f) + flyDir = 2; +#else + if ((obstacleForFlyingZ > feetZ && obstacleForFlyingOtherDirZ < 500.0f) || (obstacleForFlyingZ > feetZ && obstacleForFlyingOtherDirZ > feetZ)) + flyDir = 1; + else if (obstacleForFlyingOtherDirZ > feetZ && obstacleForFlyingZ < 499.0f) + flyDir = 2; +#endif + + if (flyDir > 0 && !bHeadStuckInCollision) { + GetMatrix().SetTranslateOnly(flyDir == 2 ? obstacleForFlyingOtherDir.point : obstacleForFlying.point); + GetMatrix().GetPosition().z += FEET_OFFSET; + GetMatrix().UpdateRW(); + SetLanding(); + bIsStanding = true; + } + if (obstacleForFlyingZ < obstacleForFlyingOtherDirZ) { + offsetToCheck *= -1.0f; + } + offsetToCheck.z = 1.0f; + forceDir = 4.0f * offsetToCheck; + forceDir.z = 4.0f; + ApplyMoveForce(forceDir); + + // What was that for?? It pushes player inside of collision sometimes and kills him. +#ifdef FIX_BUGS + if (!IsPlayer()) +#endif + GetMatrix().GetPosition() += 0.25f * offsetToCheck; + + m_fRotationCur = CGeneral::GetRadianAngleBetweenPoints(offsetToCheck.x, offsetToCheck.y, 0.0f, 0.0f); + m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); + m_fRotationDest = m_fRotationCur; + SetHeading(m_fRotationCur); + + if (m_nPedState != PED_FALL && !bIsPedDieAnimPlaying) { + SetFall(1000, ANIM_STD_HIGHIMPACT_BACK, true); + } + bIsInTheAir = false; + } else if (m_vecDamageNormal.z > 0.4f) { + if (m_nPedState == PED_JUMP) { + if (m_nWaitTimer <= 2000) { + if (m_nWaitTimer < 1000) + m_nWaitTimer += CTimer::GetTimeStepInMilliseconds(); + } else { + m_nWaitTimer = 0; + } + } + forceDir = m_vecDamageNormal; + forceDir.z = 0.0f; + forceDir.Normalise(); + if (m_nPedState != PED_JUMP || m_nWaitTimer >= 300) { + ApplyMoveForce(2.0f * forceDir); + } else { + ApplyMoveForce(-4.0f * forceDir); + } + } + } else if ((CTimer::GetFrameCounter() + m_randomSeed % 256 + 3) & 7) { + if (IsPlayer() && m_nPedState != PED_JUMP && pad0->JumpJustDown()) { + int16 padWalkX = pad0->GetPedWalkLeftRight(); + int16 padWalkY = pad0->GetPedWalkUpDown(); + if (Abs(padWalkX) > 0.0f || Abs(padWalkY) > 0.0f) { + m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(0.0f, 0.0f, -padWalkX, padWalkY); + m_fRotationDest -= TheCamera.Orientation; + m_fRotationDest = CGeneral::LimitRadianAngle(m_fRotationDest); + m_fRotationCur = m_fRotationDest; + SetHeading(m_fRotationCur); + } + SetJump(); + m_nPedStateTimer = 0; + m_vecHitLastPos = GetPosition(); + + // Why? forceDir is unused after this point. + forceDir = CVector(0.0f, 0.0f, 0.0f); + } else if (IsPlayer()) { + int16 padWalkX = pad0->GetPedWalkLeftRight(); + int16 padWalkY = pad0->GetPedWalkUpDown(); + if (Abs(padWalkX) > 0.0f || Abs(padWalkY) > 0.0f) { + m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(0.0f, 0.0f, -padWalkX, padWalkY); + m_fRotationDest -= TheCamera.Orientation; + m_fRotationDest = CGeneral::LimitRadianAngle(m_fRotationDest); + m_fRotationCur = m_fRotationDest; + SetHeading(m_fRotationCur); + } + CAnimBlendAssociation *jumpAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_JUMP_GLIDE); + + if (!jumpAssoc) + jumpAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL_GLIDE); + + if (jumpAssoc) { + jumpAssoc->blendDelta = -3.0f; + jumpAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + if (m_nPedState == PED_JUMP) + m_nPedState = PED_IDLE; + } else { + CAnimBlendAssociation *jumpAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_JUMP_GLIDE); + + if (!jumpAssoc) + jumpAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL_GLIDE); + + if (jumpAssoc) { + jumpAssoc->blendDelta = -3.0f; + jumpAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + } + } else { + offsetToCheck = GetPosition(); + offsetToCheck.z += 0.5f; + + if (CWorld::ProcessVerticalLine(offsetToCheck, GetPosition().z - FEET_OFFSET, foundCol, foundEnt, true, true, false, true, false, false, nil)) { + if (!bHeadStuckInCollision || FEET_OFFSET + foundCol.point.z < GetPosition().z) { + GetMatrix().GetPosition().z = FEET_OFFSET + foundCol.point.z; + GetMatrix().UpdateRW(); + if (bHeadStuckInCollision) + bHeadStuckInCollision = false; + } + SetLanding(); + bIsStanding = true; + } + } + } else if (m_nPedStateTimer < 1001) { + m_nPedStateTimer = 0; + } + } + + if (bIsDucking) + Duck(); + + if (bStartWanderPathOnFoot) { + if (IsPedInControl()) { + ClearAll(); + SetWanderPath(m_nPathDir); + bStartWanderPathOnFoot = false; + } else if (m_nPedState == PED_DRIVING) { + bWanderPathAfterExitingCar = true; + SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); + bStartWanderPathOnFoot = false; + } + } + + if (!bIsStanding && m_vecMoveSpeed.z > 0.25) { + float airResistance = Pow(0.95f, CTimer::GetTimeStep()); + + m_vecMoveSpeed *= airResistance; + } + if (IsPlayer() || !bIsStanding || m_vecMoveSpeed.x != 0.0f || m_vecMoveSpeed.y != 0.0f || m_vecMoveSpeed.z != 0.0f + || (m_nMoveState != PEDMOVE_NONE && m_nMoveState != PEDMOVE_STILL) + || m_vecAnimMoveDelta.x != 0.0f || m_vecAnimMoveDelta.y != 0.0f + || m_nPedState == PED_JUMP + || bIsInTheAir + || m_pCurrentPhysSurface) { + + CPhysical::ProcessControl(); + } else { + bHasContacted = false; + bIsInSafePosition = false; + bWasPostponed = false; + bHasHitWall = false; + m_nCollisionRecords = 0; + bHasCollided = false; + m_nDamagePieceType = 0; + m_fDamageImpulse = 0.0f; + m_pDamageEntity = nil; + m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); + m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); + } + + if (m_nPedState != PED_DIE || bIsPedDieAnimPlaying) { + RequestDelayedWeapon(); + PlayFootSteps(); + if (m_nPedState != PED_DEAD) { + CalculateNewVelocity(); + CalculateNewOrientation(); + } + UpdatePosition(); + if (IsPedInControl() && !bIsStanding && !m_pDamageEntity) { + if (m_attachedTo) { + bIsInTheAir = false; + } else if (CheckIfInTheAir()) { + SetInTheAir(); + bHeadStuckInCollision = false; + } + } + if (bHeadStuckInCollision) { + CVector posToCheck = GetPosition(); + posToCheck.z += 0.9f; + if (!CWorld::TestSphereAgainstWorld(posToCheck, 0.2f, this, true, true, false, true, false, false)) + bHeadStuckInCollision = false; + } + ProcessObjective(); + if (!bIsAimingGun) { + if (bIsRestoringGun) + RestoreGunPosition(); + } else { + AimGun(); + } + + if (bIsLooking) { + MoveHeadToLook(); + } else if (bIsRestoringLook) { + RestoreHeadPosition(); + } + + if (bIsInTheAir) + InTheAir(); + + if (bUpdateAnimHeading) { + if (m_nPedState != PED_GETUP && m_nPedState != PED_FALL) { + m_fRotationCur -= HALFPI; + m_fRotationDest = m_fRotationCur; + bUpdateAnimHeading = false; + } + } + + if (m_nWaitState != WAITSTATE_FALSE) + Wait(); + +#ifdef CANCELLABLE_CAR_ENTER + static bool cancelJack = false; + if (IsPlayer()) { + if (EnteringCar() && m_pVehicleAnim) { + CPad *pad = CPad::GetPad(0); + + if (!pad->ArePlayerControlsDisabled()) { + int vehAnim = m_pVehicleAnim->animId; + + int16 padWalkX = pad->GetPedWalkLeftRight(); + int16 padWalkY = pad->GetPedWalkUpDown(); + if (Abs(padWalkX) > 0.0f || Abs(padWalkY) > 0.0f) { + if (vehAnim == ANIM_STD_CAR_OPEN_DOOR_LHS || vehAnim == ANIM_STD_CAR_OPEN_DOOR_RHS || vehAnim == ANIM_STD_COACH_OPEN_LHS || vehAnim == ANIM_STD_COACH_OPEN_RHS || + vehAnim == ANIM_STD_VAN_OPEN_DOOR_REAR_LHS || vehAnim == ANIM_STD_VAN_OPEN_DOOR_REAR_RHS) { + + if (!m_pMyVehicle->pDriver) { + cancelJack = false; + bCancelEnteringCar = true; + } else + cancelJack = true; + } else if (vehAnim == ANIM_STD_QUICKJACK && m_pVehicleAnim->GetTimeLeft() > 0.75f) { + cancelJack = true; + } else if (vehAnim == ANIM_STD_CAR_PULL_OUT_PED_LHS || vehAnim == ANIM_STD_CAR_PULL_OUT_PED_LO_LHS || vehAnim == ANIM_STD_CAR_PULL_OUT_PED_LO_RHS || vehAnim == ANIM_STD_CAR_PULL_OUT_PED_RHS) { + bCancelEnteringCar = true; + cancelJack = false; + } + } + if (cancelJack && vehAnim == ANIM_STD_QUICKJACK && m_pVehicleAnim->GetTimeLeft() > 0.75f && m_pVehicleAnim->GetTimeLeft() < 0.78f) { + cancelJack = false; + QuitEnteringCar(); + RestorePreviousObjective(); + } + if (cancelJack && (vehAnim == ANIM_STD_CAR_PULL_OUT_PED_LHS || vehAnim == ANIM_STD_CAR_PULL_OUT_PED_LO_LHS || vehAnim == ANIM_STD_CAR_PULL_OUT_PED_LO_RHS || vehAnim == ANIM_STD_CAR_PULL_OUT_PED_RHS)) { + cancelJack = false; + bCancelEnteringCar = true; + } + } + } else + cancelJack = false; + } +#endif + + switch (m_nPedState) { + case PED_IDLE: + Idle(); + break; + case PED_LOOK_ENTITY: + case PED_LOOK_HEADING: + Look(); + break; + case PED_WANDER_RANGE: + // III has these in here(and they were unused): + /* + WanderRange(); + CheckAroundForPossibleCollisions(); + */ + break; + case PED_WANDER_PATH: + WanderPath(); + break; + case PED_ENTER_CAR: + case PED_CARJACK: + { + break; + } + case PED_FLEE_POS: + ms_vec2DFleePosition = m_fleeFromPos; + Flee(); + break; + case PED_FLEE_ENTITY: + if (!m_fleeFrom) { + bMakeFleeScream = false; + SetIdle(); + break; + } + + if (CTimer::GetTimeInMilliseconds() <= m_nPedStateTimer) + break; + + ms_vec2DFleePosition = m_fleeFrom->GetPosition(); + Flee(); + break; + case PED_FOLLOW_PATH: + FollowPath(); + break; + case PED_PAUSE: + Pause(); + break; + case PED_ATTACK: + Attack(); + break; + case PED_FIGHT: + Fight(); + break; + case PED_CHAT: + Chat(); + break; + case PED_AIM_GUN: + if (m_pPointGunAt && m_pPointGunAt->IsPed() +#ifdef FIX_BUGS + && !GetWeapon()->IsTypeMelee() +#endif + && ((CPed*)m_pPointGunAt)->CanSeeEntity(this, CAN_SEE_ENTITY_ANGLE_THRESHOLD * 2)) { + ((CPed*)m_pPointGunAt)->ReactToPointGun(this); + } + PointGunAt(); + break; + case PED_SEEK_CAR: + SeekCar(); + break; + case PED_SEEK_IN_BOAT: + SeekBoatPosition(); + break; + case PED_BUY_ICECREAM: + BuyIceCream(); + break; + case PED_INVESTIGATE: + InvestigateEvent(); + break; + case PED_ON_FIRE: + if (IsPlayer()) + break; + + if (CTimer::GetTimeInMilliseconds() <= m_fleeTimer) { + if (m_pFire) { + if (m_fleeFrom) { + ms_vec2DFleePosition = m_fleeFrom->GetPosition(); + } else { + ms_vec2DFleePosition = m_fleeFromPos; + } + Flee(); + } else { + m_nLastPedState = PED_NONE; + SetWanderPath(0); + SetWaitState(WAITSTATE_FINISH_FLEE, 0); + } + } else { + if (m_pFire) + m_pFire->Extinguish(); + } + break; + case PED_ANSWER_MOBILE: + AnswerMobile(); + break; + case PED_FALL: + Fall(); + break; + case PED_GETUP: + SetGetUp(); + break; +#ifdef GTA_TRAIN + case PED_ENTER_TRAIN: + EnterTrain(); + break; + case PED_EXIT_TRAIN: + ExitTrain(); + break; +#endif + case PED_DRIVING: + { + if (!m_pMyVehicle) { + bInVehicle = false; + FlagToDestroyWhenNextProcessed(); + return; + } + +#ifdef CAR_AIRBREAK + if (IsPlayer()) { + CPad* pad = CPad::GetPad(0); + if (!pad->ArePlayerControlsDisabled()) { + if (pad->GetHorn()) { + float c = Cos(m_fRotationCur); + float s = Sin(m_fRotationCur); + m_pMyVehicle->GetRight() = CVector(1.0f, 0.0f, 0.0f); + m_pMyVehicle->GetForward() = CVector(0.0f, 1.0f, 0.0f); + m_pMyVehicle->GetUp() = CVector(0.0f, 0.0f, 1.0f); + if (pad->GetAccelerate()) { + m_pMyVehicle->ApplyMoveForce(GetForward() * 30.0f); + } + else if (pad->GetBrake()) { + m_pMyVehicle->ApplyMoveForce(-GetForward() * 30.0f); + } + else { + int16 lr = pad->GetSteeringLeftRight(); + if (lr < 0) { + //m_pMyVehicle->ApplyTurnForce(20.0f * -GetRight(), GetForward()); + m_pMyVehicle->ApplyMoveForce(-GetRight() * 30.0f); + } + else if (lr > 0) { + m_pMyVehicle->ApplyMoveForce(GetRight() * 30.0f); + } + else { + m_pMyVehicle->ApplyMoveForce(0.0f, 0.0f, 50.0f); + } + } + } + } + } +#endif + + if (m_pMyVehicle->pDriver == this) { + DriveVehicle(); + if (!m_pMyVehicle) + return; + } else { + LookForSexyPeds(); + LookForSexyCars(); + } + + if (!IsPlayer() && m_pMyVehicle->IsBoat() + && FindPlayerPed()->m_pCurrentPhysSurface == m_pMyVehicle + && (CharCreatedBy != MISSION_CHAR || !bIsPlayerFriend)) { + SetObjective(OBJECTIVE_KILL_CHAR_ON_BOAT, FindPlayerPed()); + Say(SOUND_PED_CAR_JACKED); + } + + break; + } + case PED_DIE: + Die(); + break; + case PED_HANDS_UP: + if (m_pedStats->m_flags & STAT_GUN_PANIC) { + if (!RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_HANDSCOWER)) { + CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HANDSCOWER); + Say(SOUND_PED_HANDS_COWER); + } + } else if (!RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_HANDSUP)) { + CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HANDSUP); + Say(SOUND_PED_HANDS_UP); + } + break; + default: + break; + } + SetMoveAnim(); + if (bPedIsBleeding || m_bleedCounter != 0) { + if (CGame::nastyGame) { + if (m_bleedCounter != 0) + m_bleedCounter--; + + if (!(CTimer::GetFrameCounter() & 3)) { + CVector cameraDist = GetPosition() - TheCamera.GetPosition(); + if (cameraDist.MagnitudeSqr() < sq(50.0f)) { + + float length = (CGeneral::GetRandomNumber() & 127) * 0.0015f + 0.15f; + CVector bloodPos( + ((CGeneral::GetRandomNumber() & 127) - 64) * 0.007f, + ((CGeneral::GetRandomNumber() & 127) - 64) * 0.007f, + 1.0f); + bloodPos += GetPosition(); + + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &bloodPos, length, 0.0f, + 0.0f, -length, 255, 255, 0, 0, 4.0f, (CGeneral::GetRandomNumber() & 4095) + 2000, 1.0f); + } + } + } + } + ServiceTalking(); + if (bInVehicle && !m_pMyVehicle) + bInVehicle = false; + + if (bHeldHostageInCar) { + if (m_pMyVehicle && m_pMyVehicle->pDriver && m_pMyVehicle->pDriver->IsPlayer()) { + Say(SOUND_PED_FLEE_SPRINT); + } + } + + if (m_delayedSoundID >= 0 && CTimer::GetTimeInMilliseconds() > m_delayedSoundTimer) { + Say(m_delayedSoundID); + m_delayedSoundID = -1; + m_delayedSoundTimer = 0; + } + + if (bFannyMagnetCheat && m_nPedType == PEDTYPE_CIVFEMALE + && m_pedStats->m_sexiness > 40 && !m_leader) { + SetLeader(FindPlayerPed()); + } + + } else { + if (bIsStanding && (!m_pCurrentPhysSurface || IsPlayer()) + || bIsInWater || !bUsesCollision) { + SetDead(); + } + m_pCurrentPhysSurface = nil; + } + } else + ServiceTalking(); +} + +int32 +CPed::ProcessEntityCollision(CEntity *collidingEnt, CColPoint *collidingPoints) +{ + bool collidedWithBoat = false; + bool belowTorsoCollided = false; + float gravityEffect = -0.15f * CTimer::GetTimeStep(); + CColPoint intersectionPoint; + CColLine ourLine; + + CColModel *ourCol = CModelInfo::GetColModel(GetModelIndex()); + CColModel *hisCol = CModelInfo::GetColModel(collidingEnt->GetModelIndex()); + + if (!bUsesCollision && !bJustCheckCollision) + return 0; + + if (collidingEnt->IsVehicle() && ((CVehicle*)collidingEnt)->IsBoat()) + collidedWithBoat = true; + + // ofc we're not vehicle + if (!m_bIsVehicleBeingShifted && !bSkipLineCol && !collidingEnt->IsPed()) { + if (!bCollisionProcessed) { + m_pCurrentPhysSurface = nil; + if (bIsStanding) { + bIsStanding = false; + bWasStanding = true; + } + bCollisionProcessed = true; + m_fCollisionSpeed += m_vecMoveSpeed.Magnitude2D() * CTimer::GetTimeStep(); + bStillOnValidPoly = false; + if (IsPlayer() || m_fCollisionSpeed >= 1.0f + && (m_fCollisionSpeed >= 2.0f || m_nPedState != PED_WANDER_PATH)) { + m_collPoly.valid = false; + m_fCollisionSpeed = 0.0f; + bHitSteepSlope = false; + } else { + CVector pos = GetPosition(); + float potentialGroundZ = GetPosition().z - FEET_OFFSET; + if (bWasStanding) { + pos.z += -0.25f; + potentialGroundZ += gravityEffect; + } + if (CCollision::IsStoredPolyStillValidVerticalLine(pos, potentialGroundZ, intersectionPoint, &m_collPoly)) { + bStillOnValidPoly = true; + if(!bHeadStuckInCollision || FEET_OFFSET + intersectionPoint.point.z < GetPosition().z) { + GetMatrix().GetPosition().z = FEET_OFFSET + intersectionPoint.point.z; + if (bHeadStuckInCollision) + bHeadStuckInCollision = false; + } + m_vecMoveSpeed.z = 0.0f; + bIsStanding = true; + } else { + m_collPoly.valid = false; + m_fCollisionSpeed = 0.0f; + bHitSteepSlope = false; + } + } + } + + if (!bStillOnValidPoly) { + CVector potentialCenter = GetPosition(); + potentialCenter.z = GetPosition().z - 0.52f; + + // 0.52f should be a ped's approx. radius + float totalRadiusWhenCollided = collidingEnt->GetBoundRadius() + 0.52f - gravityEffect; + if (bWasStanding) { + if (collidedWithBoat) { + potentialCenter.z += 2.0f * gravityEffect; + totalRadiusWhenCollided += Abs(gravityEffect); + } else { + potentialCenter.z += gravityEffect; + } + } + if (sq(totalRadiusWhenCollided) > (potentialCenter - collidingEnt->GetBoundCentre()).MagnitudeSqr()) { + ourLine.p0 = GetPosition(); + ourLine.p1 = GetPosition(); + ourLine.p1.z = GetPosition().z - FEET_OFFSET; + if (bWasStanding) { + ourLine.p1.z = ourLine.p1.z + gravityEffect; + ourLine.p0.z = ourLine.p0.z + -0.25f; + } + float minDist = 1.0f; + belowTorsoCollided = CCollision::ProcessVerticalLine(ourLine, collidingEnt->GetMatrix(), *hisCol, + intersectionPoint, minDist, false, false, &m_collPoly); + + if (collidedWithBoat && bWasStanding && !belowTorsoCollided) { + ourLine.p0.z = ourLine.p1.z; + ourLine.p1.z = ourLine.p1.z + gravityEffect; + belowTorsoCollided = CCollision::ProcessVerticalLine(ourLine, collidingEnt->GetMatrix(), *hisCol, + intersectionPoint, minDist, false, false, &m_collPoly); + } + if (belowTorsoCollided) { + if (!bIsStanding + || FEET_OFFSET + intersectionPoint.point.z > GetPosition().z + || collidedWithBoat && 3.12f + intersectionPoint.point.z > GetPosition().z) { + + if (!collidingEnt->IsVehicle() && !collidingEnt->IsObject()) { + m_pCurSurface = collidingEnt; + collidingEnt->RegisterReference((CEntity**)&m_pCurSurface); + bTryingToReachDryLand = false; + bOnBoat = false; + } else { + m_pCurrentPhysSurface = (CPhysical*)collidingEnt; + collidingEnt->RegisterReference((CEntity**)&m_pCurrentPhysSurface); + m_vecOffsetFromPhysSurface = intersectionPoint.point - collidingEnt->GetPosition(); + m_pCurSurface = collidingEnt; + collidingEnt->RegisterReference((CEntity**)&m_pCurSurface); + m_collPoly.valid = false; + if (collidingEnt->IsVehicle() && ((CVehicle*)collidingEnt)->IsBoat()) { + bOnBoat = true; + } else { + bOnBoat = false; + } + } + + if (!bHeadStuckInCollision || FEET_OFFSET + intersectionPoint.point.z < GetPosition().z) { + GetMatrix().GetPosition().z = FEET_OFFSET + intersectionPoint.point.z; + if (bHeadStuckInCollision) + bHeadStuckInCollision = false; + } + m_nSurfaceTouched = intersectionPoint.surfaceB; + if (m_nSurfaceTouched == SURFACE_STEEP_CLIFF) { + bHitSteepSlope = true; + m_vecDamageNormal = intersectionPoint.normal; + } + } + + float upperSpeedLimit = 0.33f; + float lowerSpeedLimit = -0.25f; + float speed = m_vecMoveSpeed.Magnitude2D(); + if (m_nPedState == PED_IDLE) { + upperSpeedLimit *= 2.0f; + lowerSpeedLimit *= 1.5f; + } + CAnimBlendAssociation *fallAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL); + if (!bWasStanding && ((speed > upperSpeedLimit && !bPushedAlongByCar) || (m_vecMoveSpeed.z < lowerSpeedLimit)) + && m_pCollidingEntity != collidingEnt) { + + float damage = 100.0f * Max(speed - 0.25f, 0.0f); + float damage2 = damage; + if (m_vecMoveSpeed.z < -0.25f) + damage += (-0.25f - m_vecMoveSpeed.z) * 150.0f; + + uint8 dir = 2; // from backward + if (m_vecMoveSpeed.x > 0.01f || m_vecMoveSpeed.x < -0.01f || m_vecMoveSpeed.y > 0.01f || m_vecMoveSpeed.y < -0.01f) { + CVector2D offset = -m_vecMoveSpeed; + dir = GetLocalDirection(offset); + } + if (CSurfaceTable::IsSoftLanding(intersectionPoint.surfaceB)) + damage *= 0.5f; + + InflictDamage(collidingEnt, WEAPONTYPE_FALL, damage, PEDPIECE_TORSO, dir); + if (IsPlayer() && damage2 > 5.0f) + Say(SOUND_PED_LAND); + + } else if (!bWasStanding && fallAnim && -0.016f * CTimer::GetTimeStep() > m_vecMoveSpeed.z) { + InflictDamage(collidingEnt, WEAPONTYPE_FALL, 15.0f, PEDPIECE_TORSO, 2); + } + m_vecMoveSpeed.z = 0.0f; + bIsStanding = true; + } else { + bOnBoat = false; + } + } + } + } + + int ourCollidedSpheres = CCollision::ProcessColModels(GetMatrix(), *ourCol, collidingEnt->GetMatrix(), *hisCol, collidingPoints, nil, nil); + if (ourCollidedSpheres > 0 || belowTorsoCollided) { + AddCollisionRecord(collidingEnt); + if (!collidingEnt->IsBuilding()) + ((CPhysical*)collidingEnt)->AddCollisionRecord(this); + + if (ourCollidedSpheres > 0 && (collidingEnt->IsBuilding() || collidingEnt->GetIsStatic())) { + bHasHitWall = true; + } + } + if (collidingEnt->IsBuilding() || collidingEnt->GetIsStatic()) { + if (bWasStanding) { + CVector sphereNormal; + float normalLength; + for(int sphere = 0; sphere < ourCollidedSpheres; sphere++) { + sphereNormal = collidingPoints[sphere].normal; + if (sphereNormal.z >= -1.0f || !IsPlayer()) { + normalLength = sphereNormal.Magnitude2D(); + if (normalLength != 0.0f) { + sphereNormal.x = sphereNormal.x / normalLength; + sphereNormal.y = sphereNormal.y / normalLength; + } + } else { + float speed = m_vecMoveSpeed.Magnitude2D(); + sphereNormal.x = -m_vecMoveSpeed.x / Max(0.001f, speed); + sphereNormal.y = -m_vecMoveSpeed.y / Max(0.001f, speed); + GetMatrix().GetPosition().z -= 0.05f; + bHeadStuckInCollision = true; + } + sphereNormal.Normalise(); + collidingPoints[sphere].normal = sphereNormal; + if (collidingPoints[sphere].surfaceB == SURFACE_STEEP_CLIFF) + bHitSteepSlope = true; + } + } + } + return ourCollidedSpheres; +} + +static void +particleProduceFootSplash(CPed *ped, CVector const &pos, float size, int times) +{ + for (int i = 0; i < times; i++) { + CVector adjustedPos = pos; + adjustedPos.x += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); + adjustedPos.y += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); + + CVector direction = ped->GetForward() * -0.05f; + CParticle::AddParticle(PARTICLE_RAIN_SPLASHUP, adjustedPos, direction, nil, size, CRGBA(32, 32, 32, 32), 0, 0, CGeneral::GetRandomNumber() & 1, 200); + } +} + +static void +particleProduceFootDust(CPed *ped, CVector const &pos, float size, int times) +{ + switch (ped->m_nSurfaceTouched) + { + case SURFACE_TARMAC: + case SURFACE_GRAVEL: + case SURFACE_PAVEMENT: + case SURFACE_SAND: + case SURFACE_SAND_BEACH: + case SURFACE_CONCRETE_BEACH: + for (int i = 0; i < times; ++i) { + CVector adjustedPos = pos; + adjustedPos.x += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); + adjustedPos.y += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); + // ?? + CGeneral::GetRandomNumber(); + CGeneral::GetRandomNumber(); + CParticle::AddParticle(PARTICLE_PEDFOOT_DUST, adjustedPos, CVector(0.0f, 0.0f, 0.0f), nil, size, CRGBA(0, 0, 0, 0), 0, 0, 0, 0); + } + break; + default: + break; + } +} + +void +CPed::PlayFootSteps(void) +{ + CAnimBlendAssociation *assoc = RpAnimBlendClumpGetFirstAssociation(GetClump()); + CAnimBlendAssociation *walkRunAssoc = nil; + float walkRunAssocBlend = 0.0f, idleAssocBlend = 0.0f; + bool isSkater = m_pedStats == CPedStats::ms_apPedStats[PEDSTAT_SKATER]; + + CVector footPosL(0.0f, 0.0f, 0.0f), footPosR(0.0f, 0.0f, 0.0f); + bool footPosLok = false, footPosRok = false; + + if (bDoBloodyFootprints) { + if (m_bloodyFootprintCountOrDeathTime > 0 && m_bloodyFootprintCountOrDeathTime < 300) { + m_bloodyFootprintCountOrDeathTime--; + + if (m_bloodyFootprintCountOrDeathTime == 0) + bDoBloodyFootprints = false; + } + } + + if (!bIsStanding) + return; + + for (; assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { + if (assoc->flags & ASSOC_WALK) { + walkRunAssoc = assoc; + walkRunAssocBlend += assoc->blendAmount; + } else if ((assoc->flags & ASSOC_NOWALK) == 0) { + idleAssocBlend += assoc->blendAmount; + } + } + + if (walkRunAssoc && walkRunAssocBlend > 0.5f && idleAssocBlend < 1.0f) { + + float stepStart = 1.f / 15.f; + float stepEnd = walkRunAssoc->hierarchy->totalLength / 2.0f + stepStart; + float currentTime = walkRunAssoc->currentTime; + + if (isSkater) { + // both are unused + static float stepStartSection = 1.0f; + static float animSections = 15.f; + + float moveStart, soundVolume, skateTime; + if (walkRunAssoc->animId == ANIM_STD_WALK) { + moveStart = 0.0f; + skateTime = 8.f / 15.f; + } else { + moveStart = 0.0f; + skateTime = 5.f / 15.f; + } + switch (CSurfaceTable::GetAdhesionGroup(m_nSurfaceTouched)) { + case ADHESIVE_LOOSE: + if (CGeneral::GetRandomNumber() % 128) { + m_vecAnimMoveDelta *= 0.5f; + } else { + SetFall(0, ANIM_STD_HIGHIMPACT_BACK, false); + } + soundVolume = 0.5f; + break; + case ADHESIVE_SAND: + if (CGeneral::GetRandomNumber() % 64) { + m_vecAnimMoveDelta *= 0.2f; + } else { + SetFall(0, ANIM_STD_HIGHIMPACT_BACK, false); + } + soundVolume = 0.2f; + break; + case ADHESIVE_WET: + m_vecAnimMoveDelta *= 0.3f; + soundVolume = 0.2f; + break; + default: + soundVolume = 1.f; + break; + } + if (soundVolume > 0.2f && currentTime > moveStart && currentTime - walkRunAssoc->timeStep <= moveStart) { + DMAudio.PlayOneShot(m_audioEntityId, SOUND_SKATING, ((int)(127.f * soundVolume) | (walkRunAssoc->animId << 8))); + } else if (soundVolume > 0.2f) { + if (currentTime > skateTime && currentTime - walkRunAssoc->timeStep <= skateTime) { + DMAudio.PlayOneShot(m_audioEntityId, SOUND_SKATING, ((int)(127.f * soundVolume) | (walkRunAssoc->animId << 8))); + } + } + + } else { + int stepPart = 0; + + // This section is shortened/optimized for sanity. + + if (currentTime >= stepStart && currentTime - walkRunAssoc->timeStep < stepStart) + stepPart = 1; + else if (currentTime >= stepEnd && currentTime - walkRunAssoc->timeStep < stepEnd) + stepPart = 2; + + if (stepPart != 0) { + CVector adjustedFootPos; + if (stepPart == 1) { + DMAudio.PlayOneShot(m_audioEntityId, SOUND_STEP_START, 1.0f); + TransformToNode(footPosL, PED_FOOTL); + footPosLok = true; + adjustedFootPos = footPosL; + } else { + DMAudio.PlayOneShot(m_audioEntityId, SOUND_STEP_END, 1.0f); + TransformToNode(footPosR, PED_FOOTR); + footPosRok = true; + adjustedFootPos = footPosR; + } + + CVector forward = GetForward(); + + adjustedFootPos.z -= 0.1f; + adjustedFootPos += 0.2f * forward; + + if (bDoBloodyFootprints) { + CVector2D top(forward * 0.26f); + CVector2D right(GetRight() * (stepPart == 1 ? 0.14f : 0.1f)); + + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &adjustedFootPos, + top.x, top.y, + right.x, right.y, + 255, 255, 0, 0, 4.0f, 3000, 1.0f); + + if (m_bloodyFootprintCountOrDeathTime <= 20) { + m_bloodyFootprintCountOrDeathTime = 0; + bDoBloodyFootprints = false; + } else { + m_bloodyFootprintCountOrDeathTime -= 20; + } + } + if (m_nSurfaceTouched == SURFACE_SAND || m_nSurfaceTouched == SURFACE_SAND_BEACH) { + CVector2D top(forward * -0.26f); + CVector2D right(GetRight() * (stepPart == 1 ? 0.1f : 0.14f)); + + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpShadowPedTex, &adjustedFootPos, + top.x, top.y, + right.x, right.y, + 120, 250, 250, 50, 4.0f, 5000, 1.0f); + } + if (CWeather::Rain <= 0.1f || CCullZones::CamNoRain() || CCullZones::PlayerNoRain()) { + if (IsPlayer()) + particleProduceFootDust(this, adjustedFootPos, 0.0f, 4); + + } else if (stepPart == 2) { + particleProduceFootSplash(this, adjustedFootPos, 0.15f, 4); + } + } + } + } + + if (IsPlayer() && !walkRunAssoc && bIsLanding) { + if (!footPosLok) + TransformToNode(footPosL, PED_FOOTL); + + CVector forward = GetForward(); + + CVector adjustedFootPosL = footPosL; + adjustedFootPosL.z -= 0.1f; + adjustedFootPosL += 0.2f * forward; + if (bDoBloodyFootprints) { + CVector2D top(forward * 0.26f); + CVector2D right(GetRight() * 0.14f); + + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &adjustedFootPosL, + top.x, top.y, + right.x, right.y, + 255, 255, 0, 0, 4.0f, 3000, 1.0f); + + if (m_bloodyFootprintCountOrDeathTime <= 20) { + m_bloodyFootprintCountOrDeathTime = 0; + bDoBloodyFootprints = false; + } + else { + m_bloodyFootprintCountOrDeathTime -= 20; + } + } + if (!isSkater) { + if (m_nSurfaceTouched == SURFACE_SAND || m_nSurfaceTouched == SURFACE_SAND_BEACH) { + CVector2D top(forward * -0.26f); + CVector2D right(GetRight() * 0.14f); + + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpShadowPedTex, &adjustedFootPosL, + top.x, top.y, + right.x, right.y, + 120, 250, 250, 50, 4.0f, 5000, 1.0f); + } + } + if(!footPosRok) + TransformToNode(footPosR, PED_FOOTR); + + CVector adjustedFootPosR = footPosR; + adjustedFootPosR.z -= 0.1f; + adjustedFootPosR += 0.2f * forward; + + if (bDoBloodyFootprints) { + CVector2D top(forward * 0.26f); + CVector2D right(GetRight() * 0.1f); + + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &adjustedFootPosR, + top.x, top.y, + right.x, right.y, + 255, 255, 0, 0, 4.0f, 3000, 1.0f); + + if (m_bloodyFootprintCountOrDeathTime <= 20) { + m_bloodyFootprintCountOrDeathTime = 0; + bDoBloodyFootprints = false; + } else { + m_bloodyFootprintCountOrDeathTime -= 20; + } + } + if (!isSkater) { + if (m_nSurfaceTouched == SURFACE_SAND || m_nSurfaceTouched == SURFACE_SAND_BEACH) { + CVector2D top(forward * -0.26f); + CVector2D right(GetRight() * 0.14f); + + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpShadowPedTex, &adjustedFootPosR, + top.x, top.y, + right.x, right.y, + 120, 250, 250, 50, 4.0f, 5000, 1.0f); + } + } + } + + if (m_nSurfaceTouched == SURFACE_WATER) { + CRGBA rubberSmokeColor(255, 255, 255, 196); + float pedSpeed = CVector2D(m_vecMoveSpeed).Magnitude(); + if (pedSpeed > 0.03f && CTimer::GetFrameCounter() % 2 == 0 && pedSpeed > 0.13f) { + float particleSize = pedSpeed * 2.0f; + + if (particleSize < 0.25f) + particleSize = 0.25f; + + if (particleSize > 0.75f) + particleSize = 0.75f; + + CVector particlePos = GetPosition() + GetForward() * 0.3f; + particlePos.z -= 1.2f; + + CVector particleDir = m_vecMoveSpeed * -0.75f; + + particleDir.z = CGeneral::GetRandomNumberInRange(0.01f, 0.03f); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, particlePos, particleDir, nil, 0.5f * particleSize, CRGBA(0,0,0,0), 0, 0, 0, 0); + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, particlePos, particleDir, nil, particleSize, rubberSmokeColor, 0, 0, 0, 0); + } + + if (m_nPedState == PED_JUMP) { + CVector particlePos = GetPosition(); + particlePos.z -= 0.1f; + + CVector particleDir(0.0f, 0.075f, 0.0f); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, particlePos, particleDir, nil, 0.005f, CRGBA(0, 0, 0, 0), 0, 0, 0, 0); + particleDir.x += CGeneral::GetRandomNumberInRange(-0.05f, 0.05f); + particleDir.y += CGeneral::GetRandomNumberInRange(-0.05f, 0.05f); + particleDir.z -= CGeneral::GetRandomNumberInRange(0.025f, 0.05f); + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, particlePos, particleDir, nil, 0.5f, rubberSmokeColor, 0, 0, 0, 0); + } + } +} + +// Actually GetLocalDirectionTo(Turn/Look) +int +CPed::GetLocalDirection(const CVector2D &posOffset) +{ + int direction; + float angle; + + for (angle = posOffset.Heading() - m_fRotationCur + DEGTORAD(45.0f); angle < 0.0f; angle += TWOPI); + + for (direction = RADTODEG(angle)/90.0f; direction > 3; direction -= 4); + + // 0-forward, 1-left, 2-backward, 3-right. + return direction; +} + +bool +CPed::SetDirectionToWalkAroundVehicle(CVehicle* veh) +{ + return SetFollowPath(m_vecSeekPos, 0.0f, m_nMoveState, veh, m_pedInObjective, m_nMoveState == PEDMOVE_WALK ? 2000 : 250); +} + +void +CPed::SetDirectionToWalkAroundObject(CEntity *obj) +{ + float distLimitForTimer = 8.0f; + CColModel *objCol = CModelInfo::GetColModel(obj->GetModelIndex()); + CVector objColMin = objCol->boundingBox.min; + CVector objColMax = objCol->boundingBox.max; + CVector objColCenter = (objColMin + objColMax) / 2.0f; + CMatrix objMat(obj->GetMatrix()); + float dirToSet = obj->GetForward().Heading(); + bool goingToEnterCarAndItsVan = false; + bool goingToEnterCar = false; + bool objUpsideDown = false; + + float checkIntervalInDist = (objColMax.y - objColMin.y) * 0.1f; + float checkIntervalInTime; + + if (m_nMoveState == PEDMOVE_NONE || m_nMoveState == PEDMOVE_STILL) + return; + + if (CharCreatedBy != MISSION_CHAR && obj->GetModelIndex() == MI_PHONEBOOTH1) { + bool isRunning = m_nMoveState == PEDMOVE_RUN || m_nMoveState == PEDMOVE_SPRINT; + SetFindPathAndFlee(obj, 5000, !isRunning); + return; + } + + CVector2D adjustedColMin(objColMin.x - 0.35f, objColMin.y - 0.35f); + CVector2D adjustedColMax(objColMax.x + 0.35f, objColMax.y + 0.35f); + + checkIntervalInDist = Max(checkIntervalInDist, 0.5f); + checkIntervalInDist = Min(checkIntervalInDist, (objColMax.z - objColMin.z) / 2.0f); + checkIntervalInDist = Min(checkIntervalInDist, (adjustedColMax.x - adjustedColMin.x) / 2.0f); + + if (objMat.GetUp().z < 0.0f) + objUpsideDown = true; + + if (obj->GetModelIndex() != MI_TRAFFICLIGHTS && obj->GetModelIndex() != MI_SINGLESTREETLIGHTS1 && obj->GetModelIndex() != MI_SINGLESTREETLIGHTS2) { + objColCenter = obj->GetMatrix() * objColCenter; + } else { + checkIntervalInDist = 0.4f; + if (objMat.GetUp().z <= 0.57f) { + + // Specific calculations for traffic lights, didn't get a bit. + adjustedColMin.x = 1.2f * (adjustedColMin.x < adjustedColMin.y ? adjustedColMin.x : adjustedColMin.y); + adjustedColMax.x = 1.2f * (adjustedColMax.x > adjustedColMax.y ? adjustedColMax.x : adjustedColMax.y); + adjustedColMin.y = 1.2f * objColMin.z; + adjustedColMax.y = 1.2f * objColMax.z; + dirToSet = objMat.GetUp().Heading(); + objMat.SetUnity(); + objMat.RotateZ(dirToSet); + objMat.GetPosition() += obj->GetPosition(); + objColCenter = obj->GetPosition(); + } else { + objColCenter.x = adjustedColMax.x - 0.25f; + objColCenter = obj->GetMatrix() * objColCenter; + distLimitForTimer = 0.75f; + } + objUpsideDown = false; + } + float oldRotDest = m_fRotationDest; + float angleToFaceObjCenter = (objColCenter - GetPosition()).Heading(); + float angleDiffBtwObjCenterAndForward = CGeneral::LimitRadianAngle(dirToSet - angleToFaceObjCenter); + float objTopRightHeading = Atan2(adjustedColMax.x - adjustedColMin.x, adjustedColMax.y - adjustedColMin.y); + + if (IsPlayer()) { + if (FindPlayerPed()->m_fMoveSpeed <= 0.0f) + checkIntervalInTime = 0.0f; + else + checkIntervalInTime = 2.0f / FindPlayerPed()->m_fMoveSpeed; + } else { + switch (m_nMoveState) { + case PEDMOVE_WALK: + checkIntervalInTime = 2.0f; + break; + case PEDMOVE_RUN: + checkIntervalInTime = 0.5f; + break; + case PEDMOVE_SPRINT: + checkIntervalInTime = 0.5f; + break; + default: + checkIntervalInTime = 0.0f; + break; + } + } + if (m_pSeekTarget == obj && obj->IsVehicle()) { + if (m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER + || m_objective == OBJECTIVE_SOLICIT_VEHICLE) { + goingToEnterCar = true; + if (IsPlayer()) + checkIntervalInTime = 0.0f; + + if (((CVehicle*)obj)->bIsVan) + goingToEnterCarAndItsVan = true; + } + } + + int entityOnTopLeftOfObj = 0; + int entityOnBottomLeftOfObj = 0; + int entityOnTopRightOfObj = 0; + int entityOnBottomRightOfObj = 0; + + if (CTimer::GetTimeInMilliseconds() > m_collidingThingTimer || m_collidingEntityWhileFleeing != obj) { + bool collidingThingChanged = true; + CEntity *obstacle; + + if (!obj->IsVehicle() || objUpsideDown) { + collidingThingChanged = false; + } else { + float adjustedCheckInterval = 0.7f * checkIntervalInDist; + CVector posToCheck; + + // Top left of obj + posToCheck.x = adjustedColMin.x + adjustedCheckInterval; + posToCheck.y = adjustedColMax.y - adjustedCheckInterval; + posToCheck.z = 0.0f; + posToCheck = objMat * posToCheck; + posToCheck.z += 0.6f; + obstacle = CWorld::TestSphereAgainstWorld(posToCheck, checkIntervalInDist, obj, + true, true, false, true, false, false); + if (obstacle) { + if (obstacle->IsBuilding()) { + entityOnTopLeftOfObj = 1; + } else if (obstacle->IsVehicle()) { + entityOnTopLeftOfObj = 2; + } else { + entityOnTopLeftOfObj = 3; + } + } + + // Top right of obj + posToCheck.x = adjustedColMax.x - adjustedCheckInterval; + posToCheck.y = adjustedColMax.y - adjustedCheckInterval; + posToCheck.z = 0.0f; + posToCheck = objMat * posToCheck; + posToCheck.z += 0.6f; + obstacle = CWorld::TestSphereAgainstWorld(posToCheck, checkIntervalInDist, obj, + true, true, false, true, false, false); + if (obstacle) { + if (obstacle->IsBuilding()) { + entityOnTopRightOfObj = 1; + } else if (obstacle->IsVehicle()) { + entityOnTopRightOfObj = 2; + } else { + entityOnTopRightOfObj = 3; + } + } + + // Bottom right of obj + posToCheck.x = adjustedColMax.x - adjustedCheckInterval; + posToCheck.y = adjustedColMin.y + adjustedCheckInterval; + posToCheck.z = 0.0f; + posToCheck = objMat * posToCheck; + posToCheck.z += 0.6f; + obstacle = CWorld::TestSphereAgainstWorld(posToCheck, checkIntervalInDist, obj, + true, true, false, true, false, false); + if (obstacle) { + if (obstacle->IsBuilding()) { + entityOnBottomRightOfObj = 1; + } else if (obstacle->IsVehicle()) { + entityOnBottomRightOfObj = 2; + } else { + entityOnBottomRightOfObj = 3; + } + } + + // Bottom left of obj + posToCheck.x = adjustedColMin.x + adjustedCheckInterval; + posToCheck.y = adjustedColMin.y + adjustedCheckInterval; + posToCheck.z = 0.0f; + posToCheck = objMat * posToCheck; + posToCheck.z += 0.6f; + obstacle = CWorld::TestSphereAgainstWorld(posToCheck, checkIntervalInDist, obj, + true, true, false, true, false, false); + if (obstacle) { + if (obstacle->IsBuilding()) { + entityOnBottomLeftOfObj = 1; + } else if (obstacle->IsVehicle()) { + entityOnBottomLeftOfObj = 2; + } else { + entityOnBottomLeftOfObj = 3; + } + } + } + + if (!entityOnTopLeftOfObj && !entityOnTopRightOfObj) { + CVector topLeftCorner(adjustedColMin.x - 0.3f, adjustedColMax.y + 0.3f, 0.0f); + topLeftCorner = objMat * topLeftCorner; + CVector topRightCorner(adjustedColMax.x + 0.3f, adjustedColMax.y + 0.3f, 0.0f); + topRightCorner = objMat * topRightCorner; + CColPoint foundCol; + CEntity *foundEnt; + if (CWorld::ProcessLineOfSight(topLeftCorner, topRightCorner, foundCol, foundEnt, true, true, false, true, false, false, false, false)) { + switch (foundEnt->GetType()) { + case ENTITY_TYPE_VEHICLE: + entityOnTopRightOfObj = 2; + entityOnTopLeftOfObj = 2; + break; + case ENTITY_TYPE_BUILDING: + entityOnTopRightOfObj = 1; + entityOnTopLeftOfObj = 1; + break; + case ENTITY_TYPE_OBJECT: + entityOnTopRightOfObj = 3; + entityOnTopLeftOfObj = 3; + break; + } + } + } + if (!entityOnBottomRightOfObj && !entityOnBottomLeftOfObj) { + CVector bottomRightCorner(adjustedColMax.x + 0.3f, adjustedColMin.y - 0.3f, 0.0f); + bottomRightCorner = objMat * bottomRightCorner; + CVector bottomLeftCorner(adjustedColMin.x - 0.3f, adjustedColMin.y - 0.3f, 0.0f); + bottomLeftCorner = objMat * bottomLeftCorner; + CColPoint foundCol; + CEntity* foundEnt; + if (CWorld::ProcessLineOfSight(bottomRightCorner, bottomLeftCorner, foundCol, foundEnt, true, true, false, true, false, false, false, false)) { + switch (foundEnt->GetType()) { + case ENTITY_TYPE_VEHICLE: + entityOnBottomLeftOfObj = 2; + entityOnBottomRightOfObj = 2; + break; + case ENTITY_TYPE_BUILDING: + entityOnBottomLeftOfObj = 1; + entityOnBottomRightOfObj = 1; + break; + case ENTITY_TYPE_OBJECT: + entityOnBottomLeftOfObj = 3; + entityOnBottomRightOfObj = 3; + break; + } + } + } + + if (entityOnTopLeftOfObj && entityOnTopRightOfObj && entityOnBottomRightOfObj && entityOnBottomLeftOfObj) { + collidingThingChanged = false; + entityOnTopLeftOfObj = 0; + entityOnBottomLeftOfObj = 0; + entityOnTopRightOfObj = 0; + entityOnBottomRightOfObj = 0; + } + + if (!collidingThingChanged) { + m_walkAroundType = 0; + } else { + if (Abs(angleDiffBtwObjCenterAndForward) >= objTopRightHeading) { + if (PI - objTopRightHeading >= Abs(angleDiffBtwObjCenterAndForward)) { + if ((angleDiffBtwObjCenterAndForward <= 0.0f || objUpsideDown) && (angleDiffBtwObjCenterAndForward < 0.0f || !objUpsideDown)) { + if (goingToEnterCar && (m_vehDoor == CAR_DOOR_RF || m_vehDoor == CAR_DOOR_RR)) { + m_walkAroundType = 0; + } else { + if (CGeneral::LimitRadianAngle(m_fRotationDest - angleToFaceObjCenter) >= 0.0f) { + if (entityOnBottomRightOfObj == 1 || entityOnBottomRightOfObj && !entityOnTopLeftOfObj && !entityOnTopRightOfObj) { + m_walkAroundType = 1; + } else if (entityOnBottomLeftOfObj == 1 || entityOnBottomLeftOfObj && !entityOnTopLeftOfObj && !entityOnTopRightOfObj) { + m_walkAroundType = 1; + } + } else { + if (entityOnTopRightOfObj == 1 || entityOnTopRightOfObj && !entityOnBottomRightOfObj && !entityOnBottomLeftOfObj) { + m_walkAroundType = 4; + } else if (entityOnTopLeftOfObj == 1 || entityOnTopLeftOfObj && !entityOnBottomRightOfObj && !entityOnBottomLeftOfObj) { + m_walkAroundType = 4; + } + } + } + } else { + if (goingToEnterCar && (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR)) { + m_walkAroundType = 0; + } else { + if (CGeneral::LimitRadianAngle(m_fRotationDest - angleToFaceObjCenter) <= 0.0f) { + if (entityOnBottomLeftOfObj == 1 || entityOnBottomLeftOfObj && !entityOnTopLeftOfObj && !entityOnTopRightOfObj) { + m_walkAroundType = 2; + } else if (entityOnBottomRightOfObj == 1 || entityOnBottomRightOfObj && !entityOnTopLeftOfObj && !entityOnTopRightOfObj) { + m_walkAroundType = 2; + } + } else { + if (entityOnTopLeftOfObj == 1 || entityOnTopLeftOfObj && !entityOnBottomRightOfObj && !entityOnBottomLeftOfObj) { + m_walkAroundType = 3; + } else if (entityOnTopRightOfObj == 1 || entityOnTopRightOfObj && !entityOnBottomRightOfObj && !entityOnBottomLeftOfObj) { + m_walkAroundType = 3; + } + } + } + } + } else if (goingToEnterCar && (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR) + || CGeneral::LimitRadianAngle(m_fRotationDest - angleToFaceObjCenter) < 0.0f) { + if (entityOnTopLeftOfObj == 1 || entityOnTopLeftOfObj && !entityOnTopRightOfObj && !entityOnBottomRightOfObj) { + m_walkAroundType = 3; + } + } else if (entityOnTopRightOfObj == 1 || entityOnTopRightOfObj && !entityOnTopLeftOfObj && !entityOnBottomLeftOfObj) { + m_walkAroundType = 4; + } + } else if (goingToEnterCar && (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR) + || CGeneral::LimitRadianAngle(m_fRotationDest - angleToFaceObjCenter) > 0.0f) { + if (entityOnBottomLeftOfObj == 1 || entityOnBottomLeftOfObj && !entityOnTopRightOfObj && !entityOnBottomRightOfObj) { + m_walkAroundType = 2; + } + } else if (entityOnBottomRightOfObj == 1 || entityOnBottomRightOfObj && !entityOnTopLeftOfObj && !entityOnBottomLeftOfObj) { + m_walkAroundType = 1; + } else { + m_walkAroundType = 0; + } + } + } + m_collidingEntityWhileFleeing = obj; + m_collidingEntityWhileFleeing->RegisterReference((CEntity**) &m_collidingEntityWhileFleeing); + + // TODO: This random may need to be changed. + m_collidingThingTimer = CTimer::GetTimeInMilliseconds() + 512 + CGeneral::GetRandomNumber(); + + CVector localPosToHead; + + if (Abs(angleDiffBtwObjCenterAndForward) < objTopRightHeading) { + if (goingToEnterCar) { + if (goingToEnterCarAndItsVan) { + if (m_vehDoor == CAR_DOOR_LR || m_vehDoor == CAR_DOOR_RR) + return; + } + if (m_vehDoor != CAR_DOOR_LF && m_vehDoor != CAR_DOOR_LR && (!entityOnBottomRightOfObj || entityOnBottomLeftOfObj)) { + m_fRotationDest = CGeneral::LimitRadianAngle(dirToSet - HALFPI); + localPosToHead.x = adjustedColMax.x; + localPosToHead.z = 0.0f; + localPosToHead.y = adjustedColMin.y; + } else { + m_fRotationDest = CGeneral::LimitRadianAngle(HALFPI + dirToSet); + localPosToHead.x = adjustedColMin.x; + localPosToHead.z = 0.0f; + localPosToHead.y = adjustedColMin.y; + } + } else { + if (m_walkAroundType != 1 && m_walkAroundType != 4 + && (m_walkAroundType || CGeneral::LimitRadianAngle(m_fRotationDest - angleToFaceObjCenter) <= 0.0f)) { + + m_fRotationDest = CGeneral::LimitRadianAngle(dirToSet - HALFPI); + localPosToHead.x = adjustedColMax.x; + localPosToHead.z = 0.0f; + localPosToHead.y = adjustedColMin.y; + } else { + m_fRotationDest = CGeneral::LimitRadianAngle(HALFPI + dirToSet); + localPosToHead.x = adjustedColMin.x; + localPosToHead.z = 0.0f; + localPosToHead.y = adjustedColMin.y; + } + } + } else { + if (PI - objTopRightHeading >= Abs(angleDiffBtwObjCenterAndForward)) { + if (angleDiffBtwObjCenterAndForward <= 0.0f) { + if (!goingToEnterCar || !goingToEnterCarAndItsVan || m_vehDoor != CAR_DOOR_LR && m_vehDoor != CAR_DOOR_RR) { + if (goingToEnterCar) { + if (m_vehDoor == CAR_DOOR_RF || (m_vehDoor == CAR_DOOR_RR && !goingToEnterCarAndItsVan)) + return; + } + if (m_walkAroundType == 4 || m_walkAroundType == 3 + || !m_walkAroundType && CGeneral::LimitRadianAngle(m_fRotationDest - angleToFaceObjCenter) > 0.0f) { + + m_fRotationDest = CGeneral::LimitRadianAngle(PI + dirToSet); + localPosToHead.x = adjustedColMax.x; + localPosToHead.z = 0.0f; + localPosToHead.y = adjustedColMin.y; + } else { + m_fRotationDest = dirToSet; + localPosToHead.x = adjustedColMax.x; + localPosToHead.z = 0.0f; + localPosToHead.y = adjustedColMax.y; + } + } else { + m_fRotationDest = CGeneral::LimitRadianAngle(PI + dirToSet); + localPosToHead.x = adjustedColMax.x; + localPosToHead.z = 0.0f; + localPosToHead.y = adjustedColMin.y; + } + } else if (goingToEnterCar && goingToEnterCarAndItsVan && (m_vehDoor == CAR_DOOR_LR || m_vehDoor == CAR_DOOR_RR)) { + m_fRotationDest = CGeneral::LimitRadianAngle(PI + dirToSet); + localPosToHead.x = adjustedColMin.x; + localPosToHead.z = 0.0f; + localPosToHead.y = adjustedColMin.y; + } else { + if (goingToEnterCar) { + if (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR && !goingToEnterCarAndItsVan) + return; + } + if (m_walkAroundType == 1 || m_walkAroundType == 2 + || !m_walkAroundType && CGeneral::LimitRadianAngle(m_fRotationDest - angleToFaceObjCenter) > 0.0f) { + + m_fRotationDest = dirToSet; + localPosToHead.x = adjustedColMin.x; + localPosToHead.z = 0.0f; + localPosToHead.y = adjustedColMax.y; + } else { + m_fRotationDest = CGeneral::LimitRadianAngle(PI + dirToSet); + localPosToHead.x = adjustedColMin.x; + localPosToHead.z = 0.0f; + localPosToHead.y = adjustedColMin.y; + } + } + } else { + if (goingToEnterCar && (!goingToEnterCarAndItsVan || m_vehDoor != CAR_DOOR_LR && m_vehDoor != CAR_DOOR_RR)) { + if (m_vehDoor != CAR_DOOR_LF && m_vehDoor != CAR_DOOR_LR && (!entityOnTopRightOfObj || entityOnTopLeftOfObj)) { + + m_fRotationDest = CGeneral::LimitRadianAngle(dirToSet - HALFPI); + localPosToHead.x = adjustedColMax.x; + localPosToHead.z = 0.0f; + localPosToHead.y = adjustedColMax.y; + } else { + m_fRotationDest = CGeneral::LimitRadianAngle(HALFPI + dirToSet); + localPosToHead.x = adjustedColMin.x; + localPosToHead.z = 0.0f; + localPosToHead.y = adjustedColMax.y; + } + } else { + if (m_walkAroundType == 2 || m_walkAroundType == 3 + || !m_walkAroundType && CGeneral::LimitRadianAngle(m_fRotationDest - angleToFaceObjCenter) > 0.0f) { + + m_fRotationDest = CGeneral::LimitRadianAngle(dirToSet - HALFPI); + localPosToHead.x = adjustedColMax.x; + localPosToHead.z = 0.0f; + localPosToHead.y = adjustedColMax.y; + } else { + m_fRotationDest = CGeneral::LimitRadianAngle(HALFPI + dirToSet); + localPosToHead.x = adjustedColMin.x; + localPosToHead.z = 0.0f; + localPosToHead.y = adjustedColMax.y; + } + } + } + } + if (objUpsideDown) + localPosToHead.x = localPosToHead.x * -1.0f; + + localPosToHead = objMat * localPosToHead; + m_actionX = localPosToHead.x; + m_actionY = localPosToHead.y; + localPosToHead -= GetPosition(); + m_fRotationDest = CGeneral::LimitRadianAngle(localPosToHead.Heading()); + + if (m_fRotationDest != m_fRotationCur && bHitSomethingLastFrame) { + if (m_fRotationDest == oldRotDest) { + m_fRotationDest = oldRotDest; + } else { + m_fRotationDest = CGeneral::LimitRadianAngle(PI + dirToSet); + } + } + + float dist = localPosToHead.Magnitude2D(); + if (dist < 0.5f) + dist = 0.5f; + + if (dist > distLimitForTimer) + dist = distLimitForTimer; + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 280.0f * dist * checkIntervalInTime; +} + +bool +CPed::IsPedInControl(void) +{ + return m_nPedState <= PED_STATES_NO_AI + && !bIsInTheAir && !bIsLanding + && m_fHealth > 0.0f; +} + +bool +CPed::IsPedShootable(void) +{ + return m_nPedState <= PED_STATES_NO_ST; +} + +bool +CPed::UseGroundColModel(void) +{ + return m_nPedState == PED_FALL || + m_nPedState == PED_DIVE_AWAY || + m_nPedState == PED_DIE || + m_nPedState == PED_DEAD; +} + +bool +CPed::CanPedReturnToState(void) +{ + return m_nPedState <= PED_STATES_NO_AI && m_nPedState != PED_AIM_GUN && m_nPedState != PED_ATTACK && + m_nPedState != PED_FIGHT && m_nPedState != PED_STEP_AWAY && m_nPedState != PED_SNIPER_MODE && m_nPedState != PED_LOOK_ENTITY; +} + +bool +CPed::CanSetPedState(void) +{ + return !DyingOrDead() && m_nPedState != PED_ARRESTED && !EnteringCar() && m_nPedState != PED_STEAL_CAR; +} + +bool +CPed::CanStrafeOrMouseControl(void) +{ +#ifdef FREE_CAM + if (CCamera::bFreeCam) + return false; +#endif + return m_nPedState == PED_NONE || m_nPedState == PED_IDLE || m_nPedState == PED_FLEE_POS || m_nPedState == PED_FLEE_ENTITY || + m_nPedState == PED_ATTACK || m_nPedState == PED_FIGHT || m_nPedState == PED_AIM_GUN || m_nPedState == PED_JUMP || m_nPedState == PED_ANSWER_MOBILE; +} + +void +CPed::PedSetPreviousStateCB(CAnimBlendAssociation* assoc, void* arg) +{ + CPed* ped = (CPed*)arg; + ped->RestorePreviousState(); + ped->m_pVehicleAnim = nil; +} + +void +CPed::PedGetupCB(CAnimBlendAssociation* animAssoc, void* arg) +{ + CPed* ped = (CPed*)arg; + + if (ped->m_nPedState == PED_GETUP) + RpAnimBlendClumpSetBlendDeltas(ped->GetClump(), ASSOC_PARTIAL, -1000.0f); + + ped->bFallenDown = false; + animAssoc->blendDelta = -1000.0f; + if (ped->m_nPedState == PED_GETUP) + ped->RestorePreviousState(); + + if (ped->bFleeWhenStanding && ped->m_threatEx) { + ped->SetFlee(ped->m_threatEx, 10000); + ped->Say(SOUND_PED_FLEE_SPRINT); + ped->bFleeWhenStanding = false; + ped->m_threatEx = nil; + + } else if (ped->bGotUpOfMyOwnAccord) { + ped->SetObjective(OBJECTIVE_NONE); + ped->SetWanderPath(CGeneral::GetRandomNumberInRange(0.f, 8.f)); + ped->bGotUpOfMyOwnAccord = false; + + } else { + if (ped->m_nPedState != PED_FLEE_POS && ped->m_nPedState != PED_FLEE_ENTITY) + ped->SetMoveState(PEDMOVE_STILL); + else + ped->SetMoveState(PEDMOVE_RUN); + ped->SetMoveAnim(); + } + + ped->bGetUpAnimStarted = false; +} + +void +CPed::PedLandCB(CAnimBlendAssociation* animAssoc, void* arg) +{ + CPed* ped = (CPed*)arg; + + animAssoc->blendDelta = -1000.0f; + ped->bIsLanding = false; + + if (ped->m_nPedState == PED_JUMP) + ped->RestorePreviousState(); +} + +void +CPed::PedStaggerCB(CAnimBlendAssociation* animAssoc, void* arg) +{ + /* + CPed *ped = (CPed*)arg; + + if (ped->m_nPedState == PED_STAGGER) + // nothing + */ +} +void +CPed::PedSetOutCarCB(CAnimBlendAssociation *animAssoc, void *arg) +{ + CPed *ped = (CPed*)arg; + + CVehicle *veh = ped->m_pMyVehicle; + + bool startedToRun = false; + ped->bUsesCollision = true; + ped->m_actionX = 0.0f; + ped->m_actionY = 0.0f; + ped->bVehExitWillBeInstant = false; + if (veh && veh->IsBoat()) + ped->ApplyMoveSpeed(); + + if (ped->m_objective == OBJECTIVE_LEAVE_CAR) + ped->RestorePreviousObjective(); + else if (ped->m_objective == OBJECTIVE_LEAVE_CAR_AND_DIE) { + ped->m_fHealth = 0.0f; + ped->SetDie(ANIM_STD_HIT_FLOOR, 4.0f, 0.5f); + } + + ped->bInVehicle = false; + if (veh && (veh->IsCar() || veh->IsBike())) { + CWorld::pIgnoreEntity = veh; + if (CWorld::TestSphereAgainstWorld(ped->GetPosition() - CVector(0.f, 0.f, 0.2f), + 0.4f, veh, true, true, false, false, false, false) + || CWorld::TestSphereAgainstWorld(ped->GetPosition() + CVector(0.f, 0.f, 0.2f), + 0.4f, veh, true, true, false, false, false, false) + || !CWorld::GetIsLineOfSightClear(veh->GetPosition(), ped->GetPosition(), true, false, false, true, false, false, false)) { + CWorld::pIgnoreEntity = nil; + ped->PositionPedOutOfCollision(); + } + CWorld::pIgnoreEntity = nil; + } + + if (ped->m_nPedState == PED_EXIT_CAR) { + if (ped->m_nPedType == PEDTYPE_COP) { + ped->SetIdle(); + if (((CCopPed*)ped)->m_nCopType == COP_MIAMIVICE && ped->m_pMyVehicle && ped->m_pMyVehicle->pDriver == ped) { + DMAudio.PlayOneShot(ped->m_audioEntityId, SOUND_PED_MIAMIVICE_EXITING_CAR, 0.f); + } + } else + ped->RestorePreviousState(); + + veh = ped->m_pMyVehicle; + if (ped->bFleeAfterExitingCar && veh) { + ped->bFleeAfterExitingCar = false; + ped->SetFlee(veh->GetPosition(), 12000); + ped->bUsePedNodeSeek = true; + ped->m_pNextPathNode = nil; + if (CGeneral::GetRandomNumber() & 1 || ped->m_pedStats->m_fear > 70) { + ped->SetMoveState(PEDMOVE_SPRINT); + ped->Say(SOUND_PED_FLEE_SPRINT); + } else { + ped->SetMoveState(PEDMOVE_RUN); + ped->Say(SOUND_PED_FLEE_RUN); + } + startedToRun = true; + + // This is not a good way to do this... + ped->m_nLastPedState = PED_WANDER_PATH; + + } else if (ped->bWanderPathAfterExitingCar) { + ped->SetWanderPath(CGeneral::GetRandomNumberInRange(0.0f, 8.0f)); + ped->bWanderPathAfterExitingCar = false; + if (ped->m_nPedType == PEDTYPE_PROSTITUTE) + ped->SetObjectiveTimer(30000); + ped->m_nLastPedState = PED_NONE; + + } else if (ped->bGonnaKillTheCarJacker) { + + // Kill objective is already given at this point. + ped->bGonnaKillTheCarJacker = false; + if (ped->m_pedInObjective) { + if (!(CGeneral::GetRandomNumber() & 1) + && ped->m_nPedType != PEDTYPE_COP + && (!ped->m_pedInObjective->IsPlayer() || !CTheScripts::IsPlayerOnAMission())) { + ped->ClearObjective(); + ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, veh); + } + ped->m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 1500; + } + int waitTime = 1500; + ped->SetWaitState(WAITSTATE_PLAYANIM_COWER, &waitTime); + ped->SetMoveState(PEDMOVE_RUN); + startedToRun = true; + } else if (ped->m_objective == OBJECTIVE_NONE && ped->CharCreatedBy != MISSION_CHAR && ped->m_nPedState == PED_IDLE && !ped->IsPlayer()) { + ped->SetWanderPath(CGeneral::GetRandomNumberInRange(0.0f, 8.0f)); + } + } else if (ped->m_nPedState == PED_DRIVING) { + ped->m_nPedState = PED_IDLE; + } + + if (animAssoc) + animAssoc->blendDelta = -1000.0f; + + ped->RestartNonPartialAnims(); + ped->m_pVehicleAnim = nil; + ped->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + veh = ped->m_pMyVehicle; + if (veh) { + if (ped->m_nPedType == PEDTYPE_PROSTITUTE) { + if (veh->pDriver) { + if (veh->pDriver->IsPlayer() && ped->CharCreatedBy == RANDOM_CHAR) { + CWorld::Players[CWorld::PlayerInFocus].m_nNextSexMoneyUpdateTime = 0; + CWorld::Players[CWorld::PlayerInFocus].m_nNextSexFrequencyUpdateTime = 0; + CWorld::Players[CWorld::PlayerInFocus].m_pHooker = nil; + CWorld::Players[CWorld::PlayerInFocus].m_nMoney -= 100; + if (CWorld::Players[CWorld::PlayerInFocus].m_nMoney < 0) + CWorld::Players[CWorld::PlayerInFocus].m_nMoney = 0; + } + } + } + if (veh && veh->IsBike()) + // BUG? + veh->m_nGettingOutFlags &= ~GetBikeDoorFlag(ped->m_vehDoor); + else + veh->m_nGettingOutFlags &= ~GetCarDoorFlag(ped->m_vehDoor); + + if (veh->pDriver == ped) { + veh->RemoveDriver(); +#ifndef FIX_BUGS // RemoveDriver does it anyway + veh->SetStatus(STATUS_ABANDONED); +#endif + if (veh->m_nDoorLock == CARLOCK_LOCKED_INITIALLY) + veh->m_nDoorLock = CARLOCK_UNLOCKED; + if (ped->m_nPedType == PEDTYPE_COP && veh->IsLawEnforcementVehicle()) + veh->ChangeLawEnforcerState(false); + if (veh->IsBike()) { + if (Abs(veh->m_vecMoveSpeed.x) < 0.1 && Abs(veh->m_vecMoveSpeed.y) < 0.1f) { + ((CBike*)veh)->bIsStanding = true; + } + } + } else { + veh->RemovePassenger(ped); + } + + if (veh->bIsBus && !veh->IsUpsideDown() && !veh->IsOnItsSide()) { + float angleAfterExit; + if (ped->m_vehDoor == CAR_DOOR_LF) { + angleAfterExit = HALFPI + veh->GetForward().Heading(); + } else { + angleAfterExit = veh->GetForward().Heading() - HALFPI; + } + ped->SetHeading(angleAfterExit); + ped->m_fRotationDest = angleAfterExit; + ped->m_fRotationCur = angleAfterExit; + if (!ped->bBusJacked) + ped->SetMoveState(PEDMOVE_WALK); + } + if (CGarages::IsPointWithinAnyGarage(ped->GetPosition())) + veh->bLightsOn = false; + } + + if (ped->IsPlayer()) + AudioManager.PlayerJustLeftCar(); + + ped->ReplaceWeaponWhenExitingVehicle(); + + ped->bOnBoat = false; + if (ped->bBusJacked) { + ped->SetFall(1500, ANIM_STD_HIGHIMPACT_BACK, false); + ped->bBusJacked = false; + } + ped->m_nStoredMoveState = PEDMOVE_NONE; + if (!ped->IsPlayer()) { + // It's a shame... +#ifdef FIX_BUGS + int createdBy = ped->CharCreatedBy; +#else + int createdBy = !ped->CharCreatedBy; +#endif + + if (createdBy == MISSION_CHAR && !startedToRun) + ped->SetMoveState(PEDMOVE_WALK); + } + ped->bHeldHostageInCar = false; +} + +void +CPed::PedSetDraggedOutCarCB(CAnimBlendAssociation *dragAssoc, void *arg) +{ + CAnimBlendAssociation *quickJackedAssoc; + CVehicle *vehicle; + CPed *ped = (CPed*)arg; + + uint8 exitFlags = 0; + quickJackedAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), ANIM_STD_QUICKJACKED); + if (dragAssoc && dragAssoc->animId == ANIM_BIKE_HIT && ped->m_pMyVehicle) { + if (ped->m_vehDoor == CAR_DOOR_LF || ped->m_vehDoor == CAR_DOOR_RF) { + CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_BIKE_FALLOFF, 100.0f); + ped->m_pMyVehicle->m_nGettingOutFlags &= ~(CAR_DOOR_FLAG_RF | CAR_DOOR_FLAG_LF); + } else { + CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_BIKE_FALLBACK, 100.0f); + ped->m_pMyVehicle->m_nGettingOutFlags &= ~(CAR_DOOR_FLAG_RR | CAR_DOOR_FLAG_LR); + } + ((CBike*)ped->m_pMyVehicle)->KnockOffRider(WEAPONTYPE_UNIDENTIFIED, 0, ped, true); + return; + } + + if (ped->m_nPedState != PED_ARRESTED) { + ped->m_nLastPedState = PED_NONE; + if (dragAssoc) + dragAssoc->blendDelta = -1000.0f; + } + ped->RestartNonPartialAnims(); + ped->m_pVehicleAnim = nil; + ped->m_pSeekTarget = nil; + vehicle = ped->m_pMyVehicle; + + if (vehicle && vehicle->IsBike()) + exitFlags = GetBikeDoorFlagInclJumpInFromFront(ped->m_vehDoor); + else + exitFlags = GetCarDoorFlag(ped->m_vehDoor); + + if (vehicle) + vehicle->m_nGettingOutFlags &= ~exitFlags; + + if (vehicle) { + if (vehicle->pDriver == ped) { + vehicle->RemoveDriver(); + if (vehicle->m_nDoorLock == CARLOCK_LOCKED_INITIALLY) + vehicle->m_nDoorLock = CARLOCK_UNLOCKED; + + if (ped->m_nPedType == PEDTYPE_COP && vehicle->IsLawEnforcementVehicle()) + vehicle->ChangeLawEnforcerState(false); + } else { + vehicle->RemovePassenger(ped); + } + } + ped->bInVehicle = false; + if (ped->IsPlayer()) + AudioManager.PlayerJustLeftCar(); + + if (ped->m_objective == OBJECTIVE_LEAVE_CAR_AND_DIE) { + dragAssoc->SetDeleteCallback(PedSetDraggedOutCarPositionCB, ped); + ped->m_fHealth = 0.0f; + ped->SetDie(ANIM_STD_HIT_FLOOR, 1000.0f, 0.5f); + return; + } + + if (quickJackedAssoc) { + dragAssoc->SetDeleteCallback(PedSetQuickDraggedOutCarPositionCB, ped); + } else { + dragAssoc->SetDeleteCallback(PedSetDraggedOutCarPositionCB, ped); + if (ped->CanSetPedState()) + CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_GET_UP, 1000.0f); + } + + ped->ReplaceWeaponWhenExitingVehicle(); + + ped->m_nStoredMoveState = PEDMOVE_NONE; + ped->bVehExitWillBeInstant = false; +} + +void +CPed::PedSetInCarCB(CAnimBlendAssociation *animAssoc, void *arg) +{ + CPed *ped = (CPed*)arg; + + CVehicle *veh = ped->m_pMyVehicle; + + // Pointless code + if (!veh) + return; + + // Situation of entering car as a driver while there is already a driver exiting atm. + CPed *driver = veh->pDriver; + if (driver && driver->m_nPedState == PED_DRIVING && !veh->bIsBus && driver->m_objective == OBJECTIVE_LEAVE_CAR + && (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || ped->m_nPedState == PED_CARJACK)) { + + if (!ped->IsPlayer() && (ped->CharCreatedBy != MISSION_CHAR || driver->IsPlayer())) { + ped->QuitEnteringCar(); + return; + } + if (driver->CharCreatedBy == MISSION_CHAR) { + PedSetOutCarCB(nil, veh->pDriver); + if (driver->m_pMyVehicle) { + driver->PositionPedOutOfCollision(); + } else { + driver->m_pMyVehicle = veh; + driver->PositionPedOutOfCollision(); + driver->m_pMyVehicle = nil; + } + veh->pDriver = nil; + } else { + driver->SetDead(); + driver->FlagToDestroyWhenNextProcessed(); + veh->pDriver = nil; + } + } + + if (ped->bRemoveMeWhenIGotIntoCar) { + ped->bRemoveMeWhenIGotIntoCar = false; + ped->bRemoveFromWorld = true; + } + if (ped->bCollectBusFare) { + ped->bCollectBusFare = false; + if (FindPlayerPed()) + FindPlayerPed()->m_nLastBusFareCollected += 5; + } + + if (!ped->IsNotInWreckedVehicle() || ped->DyingOrDead()) + return; + + ped->bInVehicle = true; + if (ped->m_nPedType == PEDTYPE_PROSTITUTE) { + if (veh->pDriver) { + if (veh->pDriver->IsPlayer() && ped->CharCreatedBy == RANDOM_CHAR) { + CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency = 1000; + CWorld::Players[CWorld::PlayerInFocus].m_nNextSexMoneyUpdateTime = CTimer::GetTimeInMilliseconds() + 1000; + CWorld::Players[CWorld::PlayerInFocus].m_nNextSexFrequencyUpdateTime = CTimer::GetTimeInMilliseconds() + 3000; + CWorld::Players[CWorld::PlayerInFocus].m_pHooker = (CCivilianPed*)ped; + } + } + } + if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || ped->m_nPedState == PED_CARJACK) + veh->bIsBeingCarJacked = false; + + if (veh->m_nNumGettingIn) + --veh->m_nNumGettingIn; + + if (ped->IsPlayer() && ((CPlayerPed*)ped)->m_bAdrenalineActive) + ((CPlayerPed*)ped)->ClearAdrenaline(); + + if (veh->IsBoat()) { + if (ped->IsPlayer()) { + CCarCtrl::RegisterVehicleOfInterest(veh); + if (veh->GetStatus() == STATUS_SIMPLE) { + veh->m_vecMoveSpeed = CVector(0.0f, 0.0f, -0.00001f); + veh->m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + } + veh->SetStatus(STATUS_PLAYER); + AudioManager.PlayerJustGotInCar(); + } + veh->SetDriver(ped); + if (!veh->bEngineOn) + veh->bEngineOn = true; + + ped->SetPedState(PED_DRIVING); + ped->StopNonPartialAnims(); + ped->RemoveWeaponWhenEnteringVehicle(); + return; + } + + if (ped->m_pVehicleAnim) + ped->m_pVehicleAnim->blendDelta = -1000.0f; + + ped->bDoBloodyFootprints = false; + if (veh->m_nAlarmState == -1) + veh->m_nAlarmState = 15000; + + if (ped->IsPlayer()) { + if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || veh->IsBike()) { + if (veh->GetStatus() == STATUS_SIMPLE) { + veh->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + veh->m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + } + veh->SetStatus(STATUS_PLAYER); + } + AudioManager.PlayerJustGotInCar(); + } else if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) { + if (veh->GetStatus() == STATUS_SIMPLE) { + veh->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + veh->m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + } + veh->SetStatus(STATUS_PHYSICS); + } + + if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) { + for (int i = 0; i < veh->m_nNumMaxPassengers; ++i) { + CPed *passenger = veh->pPassengers[i]; + if (passenger && !passenger->bStayInCarOnJack && !passenger->bHeldHostageInCar && (passenger->m_leader != ped || !ped->bIsLeader)) { + passenger->SetObjective(OBJECTIVE_LEAVE_CAR, veh); + passenger->m_leaveCarTimer = CTimer::GetTimeInMilliseconds(); + } + } + } + + if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || ped->m_nPedState == PED_CARJACK) { + veh->SetDriver(ped); + if (veh->VehicleCreatedBy == PARKED_VEHICLE) { + veh->VehicleCreatedBy = RANDOM_VEHICLE; + ++CCarCtrl::NumRandomCars; + --CCarCtrl::NumParkedCars; + } + if (veh->bIsAmbulanceOnDuty) { + veh->bIsAmbulanceOnDuty = false; + --CCarCtrl::NumAmbulancesOnDuty; + } + if (veh->bIsFireTruckOnDuty) { + veh->bIsFireTruckOnDuty = false; + --CCarCtrl::NumFiretrucksOnDuty; + } + if (ped->m_nPedType == PEDTYPE_COP && veh->IsLawEnforcementVehicle()) + veh->ChangeLawEnforcerState(true); + + if (!veh->bEngineOn) { + veh->bEngineOn = true; + DMAudio.PlayOneShot(ped->m_audioEntityId, SOUND_CAR_ENGINE_START, 1.0f); + } + if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER && ped->CharCreatedBy == RANDOM_CHAR + && ped != FindPlayerPed() && ped->m_nPedType != PEDTYPE_EMERGENCY) { + + CCarCtrl::JoinCarWithRoadSystem(veh); + veh->AutoPilot.m_nCarMission = MISSION_CRUISE; + veh->AutoPilot.m_nTempAction = TEMPACT_NONE; + veh->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + veh->AutoPilot.m_nCruiseSpeed = 25; + } + ped->SetPedState(PED_DRIVING); + if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) { + + if (ped->m_prevObjective == OBJECTIVE_RUN_TO_AREA || ped->m_prevObjective == OBJECTIVE_GOTO_CHAR_ON_FOOT + || ped->m_prevObjective == OBJECTIVE_SPRINT_TO_AREA || ped->m_prevObjective == OBJECTIVE_KILL_CHAR_ON_FOOT) + ped->m_prevObjective = OBJECTIVE_NONE; + + ped->RestorePreviousObjective(); + } + + } else { + + bool slowDown = false; + if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER && veh->pDriver && ped->CharCreatedBy == RANDOM_CHAR) + slowDown = true; + + // VC also has a dead condition in here. + + if (veh->IsBike()) { + veh->AddPassenger(ped, 0); + } else if (veh->bIsBus) { + veh->AddPassenger(ped); + } else { + switch (ped->m_vehDoor) { + case CAR_DOOR_RF: + veh->AddPassenger(ped, 0); + break; + case CAR_DOOR_RR: + veh->AddPassenger(ped, 2); + break; + case CAR_DOOR_LR: + veh->AddPassenger(ped, 1); + break; + default: + veh->AddPassenger(ped); + break; + } + } + ped->SetPedState(PED_DRIVING); + if (ped->m_prevObjective == OBJECTIVE_RUN_TO_AREA || ped->m_prevObjective == OBJECTIVE_GOTO_CHAR_ON_FOOT + || ped->m_prevObjective == OBJECTIVE_SPRINT_TO_AREA || ped->m_prevObjective == OBJECTIVE_KILL_CHAR_ON_FOOT) + ped->m_prevObjective = OBJECTIVE_NONE; + + ped->RestorePreviousObjective(); + + // VC has conditional OBJECTIVE_LEAVE_CAR here, which runs if it entered the first dead condition. + + if(slowDown) + veh->AutoPilot.m_nCruiseSpeed = 17; + } + + int8 doorFlag; + if (veh->IsBike()) { + doorFlag = GetBikeDoorFlagInclJumpInFromFront(ped->m_vehDoor); + } else { + doorFlag = GetEnterCarDoorFlag(ped->m_vehDoor, veh->m_nNumMaxPassengers); + } + + veh->m_nGettingInFlags &= ~doorFlag; + + if (veh->bIsBus && !veh->m_nGettingInFlags) + ((CAutomobile*)veh)->SetBusDoorTimer(1000, 1); + + switch (ped->m_objective) { + case OBJECTIVE_KILL_CHAR_ON_FOOT: + case OBJECTIVE_KILL_CHAR_ANY_MEANS: + case OBJECTIVE_LEAVE_CAR: + case OBJECTIVE_FOLLOW_CAR_IN_CAR: + case OBJECTIVE_GOTO_AREA_ANY_MEANS: + case OBJECTIVE_GOTO_AREA_ON_FOOT: + case OBJECTIVE_RUN_TO_AREA: + case OBJECTIVE_GOTO_SEAT_ON_FOOT: + case OBJECTIVE_GOTO_ATM_ON_FOOT: + case OBJECTIVE_GOTO_BUS_STOP_ON_FOOT: + case OBJECTIVE_GOTO_PIZZA_ON_FOOT: + case OBJECTIVE_GOTO_SHELTER_ON_FOOT: + case OBJECTIVE_SPRINT_TO_AREA: + case OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT: + break; + default: + ped->SetObjective(OBJECTIVE_NONE); + } + + ped->AddInCarAnims(veh, veh->pDriver == ped); + if (veh->bIsBus) + ped->bRenderPedInCar = false; + + // FIX: RegisterVehicleOfInterest not just registers the vehicle, but also updates register time. So remove the IsThisVehicleInteresting check. +#ifndef FIX_BUGS + if (ped->IsPlayer() && !CCarCtrl::IsThisVehicleInteresting(veh) && veh->VehicleCreatedBy != MISSION_VEHICLE) { +#else + if (ped->IsPlayer() && veh->VehicleCreatedBy != MISSION_VEHICLE) { +#endif + CCarCtrl::RegisterVehicleOfInterest(veh); + + if (!veh->bHasBeenOwnedByPlayer && veh->VehicleCreatedBy != MISSION_VEHICLE) + CEventList::RegisterEvent(EVENT_STEAL_CAR, EVENT_ENTITY_VEHICLE, veh, ped, 1500); + + veh->bHasBeenOwnedByPlayer = true; + } + ped->bChangedSeat = true; +} + +bool +CPed::CanBeDeleted(void) +{ + if (bInVehicle) + return false; + + switch (CharCreatedBy) { + case RANDOM_CHAR: + return true; + case MISSION_CHAR: + return false; + case UNK_CHAR: + return false; + default: + return true; + } +} + +bool +CPed::CanBeDeletedEvenInVehicle(void) +{ + switch (CharCreatedBy) { + case RANDOM_CHAR: + return true; + case MISSION_CHAR: + return false; + case UNK_CHAR: + return false; + default: + return true; + } +} + +void +CPed::AddWeaponModel(int id) +{ + if (id != -1) { + if (m_pWeaponModel) + RemoveWeaponModel(-1); + + m_pWeaponModel = (RpAtomic*)CModelInfo::GetModelInfo(id)->CreateInstance(); + CModelInfo::GetModelInfo(id)->AddRef(); + m_wepModelID = id; + + if (IsPlayer() && id == MI_MINIGUN) + ((CPlayerPed*)this)->m_pMinigunTopAtomic = (RpAtomic*)CModelInfo::GetModelInfo(MI_MINIGUN2)->CreateInstance(); + } +} + +static RwObject* +RemoveAllModelCB(RwObject *object, void *data) +{ + RpAtomic *atomic = (RpAtomic*)object; + if (CVisibilityPlugins::GetAtomicModelInfo(atomic)) { + RpClumpRemoveAtomic(RpAtomicGetClump(atomic), atomic); + RpAtomicDestroy(atomic); + } + return object; +} + +void +CPed::RemoveWeaponModel(int modelId) +{ + // modelId is not used!! This function just removes the current weapon. + if(m_pWeaponModel){ + if (modelId == -1 + || CVisibilityPlugins::GetAtomicModelInfo(m_pWeaponModel) == CModelInfo::GetModelInfo(modelId)) { + CVisibilityPlugins::GetAtomicModelInfo(m_pWeaponModel)->RemoveRef(); + RwFrame* frm = RpAtomicGetFrame(m_pWeaponModel); + RpAtomicDestroy(m_pWeaponModel); + RwFrameDestroy(frm); + m_pWeaponModel = nil; + } + } + + if (IsPlayer() && (modelId == -1 || modelId == MI_MINIGUN)) { + RpAtomic* &atm = ((CPlayerPed*)this)->m_pMinigunTopAtomic; + if (atm) { + RwFrame *frm = RpAtomicGetFrame(atm); + RpAtomicDestroy(atm); + RwFrameDestroy(frm); + atm = nil; + } + } + m_wepModelID = -1; +} + +void +CPed::RequestDelayedWeapon() +{ + if (m_delayedWeapon != WEAPONTYPE_UNIDENTIFIED) { + int modelId1 = CWeaponInfo::GetWeaponInfo(m_delayedWeapon)->m_nModelId; + int modelId2 = CWeaponInfo::GetWeaponInfo(m_delayedWeapon)->m_nModel2Id; + if (modelId1 != -1) + CStreaming::RequestModel(modelId1, STREAMFLAGS_DEPENDENCY); + if (modelId2 != -1) + CStreaming::RequestModel(modelId2, STREAMFLAGS_DEPENDENCY); + + if ((modelId1 == -1 || CStreaming::HasModelLoaded(modelId1)) + && (modelId2 == -1 || CStreaming::HasModelLoaded(modelId2))) { + GiveWeapon(m_delayedWeapon, m_delayedWeaponAmmo, 1); + m_delayedWeapon = WEAPONTYPE_UNIDENTIFIED; + } + } +} + +void +CPed::GiveDelayedWeapon(eWeaponType weapon, uint32 ammo) +{ + m_delayedWeapon = weapon; + m_delayedWeaponAmmo = ammo; + if (m_delayedWeapon != WEAPONTYPE_UNIDENTIFIED) { + int modelId1 = CWeaponInfo::GetWeaponInfo(m_delayedWeapon)->m_nModelId; + int modelId2 = CWeaponInfo::GetWeaponInfo(m_delayedWeapon)->m_nModel2Id; + if (modelId1 != -1) + CStreaming::RequestModel(modelId1, STREAMFLAGS_DEPENDENCY); + if (modelId2 != -1) + CStreaming::RequestModel(modelId2, STREAMFLAGS_DEPENDENCY); + + if ((modelId1 == -1 || CStreaming::HasModelLoaded(modelId1)) + && (modelId2 == -1 || CStreaming::HasModelLoaded(modelId2))) { + GiveWeapon(m_delayedWeapon, m_delayedWeaponAmmo, true); + m_delayedWeapon = WEAPONTYPE_UNIDENTIFIED; + } + } +} + +int32 +CPed::GiveWeapon(eWeaponType weaponType, uint32 ammo, bool unused) +{ + int slot = GetWeaponSlot(weaponType); + + if (m_weapons[slot].m_eWeaponType == weaponType) { + GetWeapon(slot).m_nAmmoTotal += ammo; + if (weaponType < WEAPONTYPE_TOTALWEAPONS && weaponType > WEAPONTYPE_UNARMED && CWeaponInfo::ms_aMaxAmmoForWeapon[weaponType] >= 0) { + + // Looks like abandoned idea. This block never runs, ms_aMaxAmmoForWeapon is always -1. + GetWeapon(slot).m_nAmmoTotal = Min(CWeaponInfo::ms_aMaxAmmoForWeapon[weaponType], GetWeapon(slot).m_nAmmoTotal); + } else { + GetWeapon(slot).m_nAmmoTotal = Min(99999, GetWeapon(slot).m_nAmmoTotal); + } + GetWeapon(slot).Reload(); + if (GetWeapon(slot).m_eWeaponState == WEAPONSTATE_OUT_OF_AMMO && GetWeapon(slot).m_nAmmoTotal > 0) + GetWeapon(slot).m_eWeaponState = WEAPONSTATE_READY; + } else { + if (HasWeaponSlot(slot)) { + if (CWeaponInfo::IsWeaponSlotAmmoMergeable(slot)) + ammo += GetWeapon(slot).m_nAmmoTotal; + + RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(GetWeapon(slot).m_eWeaponType)->m_nModelId); + GetWeapon(slot).Shutdown(); + } + GetWeapon(slot).Initialise(weaponType, ammo); + if (slot == m_currentWeapon && !bInVehicle) { + AddWeaponModel(CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_nModelId); + } + } + if (GetWeapon(slot).m_eWeaponState != WEAPONSTATE_OUT_OF_AMMO) + GetWeapon(slot).m_eWeaponState = WEAPONSTATE_READY; + + return slot; +} + +int +CPed::GetWeaponSlot(eWeaponType weaponType) +{ + return CWeaponInfo::GetWeaponInfo(weaponType)->m_nWeaponSlot; +} + +void +CPed::SetCurrentWeapon(int slot) +{ + if (slot == -1) + return; + + CWeaponInfo* weaponInfo; + if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) { + weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + RemoveWeaponModel(weaponInfo->m_nModelId); + } + m_currentWeapon = slot; + + if (FindPlayerPed() && IsPlayer()) + ((CPlayerPed*)this)->m_nSelectedWepSlot = m_currentWeapon; + + if (HasWeaponSlot(slot)) { + weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + AddWeaponModel(weaponInfo->m_nModelId); + } +} + +void +CPed::SetCurrentWeapon(eWeaponType weaponType) +{ + SetCurrentWeapon(CWeaponInfo::GetWeaponInfo(weaponType)->m_nWeaponSlot); +} + +void +CPed::GrantAmmo(eWeaponType weaponType, uint32 ammo) +{ + int slot = CWeaponInfo::GetWeaponInfo(weaponType)->m_nWeaponSlot; + if (slot == -1) + return; + + GetWeapon(slot).m_nAmmoTotal += ammo; + if (weaponType < WEAPONTYPE_TOTALWEAPONS && weaponType > WEAPONTYPE_UNARMED && CWeaponInfo::ms_aMaxAmmoForWeapon[weaponType] >= 0) { + + // Looks like abandoned idea. This block never runs, ms_aMaxAmmoForWeapon is always -1. + GetWeapon(slot).m_nAmmoTotal = Min(CWeaponInfo::ms_aMaxAmmoForWeapon[weaponType], GetWeapon(slot).m_nAmmoTotal); + } else { + GetWeapon(slot).m_nAmmoTotal = Min(99999, GetWeapon(slot).m_nAmmoTotal); + } + + if (GetWeapon(slot).m_eWeaponState == WEAPONSTATE_OUT_OF_AMMO && GetWeapon(slot).m_nAmmoTotal > 0) + GetWeapon(slot).m_eWeaponState = WEAPONSTATE_READY; +} + +void +CPed::SetAmmo(eWeaponType weaponType, uint32 ammo) +{ + int slot = CWeaponInfo::GetWeaponInfo(weaponType)->m_nWeaponSlot; + if (slot == -1) + return; + + GetWeapon(slot).m_nAmmoTotal = ammo; + if (weaponType < WEAPONTYPE_TOTALWEAPONS && weaponType > WEAPONTYPE_UNARMED && CWeaponInfo::ms_aMaxAmmoForWeapon[weaponType] >= 0) { + + // Looks like abandoned idea. This block never runs, ms_aMaxAmmoForWeapon is always -1. + GetWeapon(slot).m_nAmmoTotal = Min(CWeaponInfo::ms_aMaxAmmoForWeapon[weaponType], GetWeapon(slot).m_nAmmoTotal); + } else { + GetWeapon(slot).m_nAmmoTotal = Min(99999, GetWeapon(slot).m_nAmmoTotal); + } + int32 newClip = GetWeapon(slot).m_nAmmoTotal; + if (newClip >= GetWeapon(slot).m_nAmmoInClip) + newClip = GetWeapon(slot).m_nAmmoInClip; + GetWeapon(slot).m_nAmmoInClip = newClip; + + if (GetWeapon(slot).m_eWeaponState == WEAPONSTATE_OUT_OF_AMMO && GetWeapon(slot).m_nAmmoTotal > 0) + GetWeapon(slot).m_eWeaponState = WEAPONSTATE_READY; +} + +void +CPed::ClearWeapons(void) +{ + RemoveWeaponModel(-1); + for (int i = 0; i < ARRAY_SIZE(m_weapons); i++) { + GetWeapon(i).Shutdown(); + } + SetCurrentWeapon(WEAPONTYPE_UNARMED); +} + +void +CPed::RemoveWeaponWhenEnteringVehicle(void) +{ + if (IsPlayer() && HasWeaponSlot(5) && GetWeapon(5).m_nAmmoTotal > 0 && ((CPlayerPed*)this)->GetPlayerInfoForThisPlayerPed()->m_bDriveByAllowed) { + if (m_storedWeapon == WEAPONTYPE_UNIDENTIFIED) + m_storedWeapon = GetWeapon()->m_eWeaponType; + SetCurrentWeapon(GetWeapon(5).m_eWeaponType); + } else { + CWeaponInfo *ourWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + RemoveWeaponModel(ourWeapon->m_nModelId); + } +} +void +CPed::ReplaceWeaponWhenExitingVehicle(void) +{ + eWeaponType weaponType = GetWeapon()->m_eWeaponType; + + // If it's Uzi, we may have stored weapon. Uzi is the only gun we can use in car. + if (IsPlayer() && GetWeaponSlot(weaponType) == WEAPONSLOT_SUBMACHINEGUN) { + if (m_storedWeapon != WEAPONTYPE_UNIDENTIFIED) { + SetCurrentWeapon(m_storedWeapon); + m_storedWeapon = WEAPONTYPE_UNIDENTIFIED; + } + } else { + AddWeaponModel(CWeaponInfo::GetWeaponInfo(weaponType)->m_nModelId); + } +} + +void +CPed::PreRender(void) +{ + CShadows::StoreShadowForPed(this, + CTimeCycle::m_fShadowDisplacementX[CTimeCycle::m_CurrentStoredValue], CTimeCycle::m_fShadowDisplacementY[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowFrontX[CTimeCycle::m_CurrentStoredValue], CTimeCycle::m_fShadowFrontY[CTimeCycle::m_CurrentStoredValue], + CTimeCycle::m_fShadowSideX[CTimeCycle::m_CurrentStoredValue], CTimeCycle::m_fShadowSideY[CTimeCycle::m_CurrentStoredValue]); + + UpdateRpHAnim(); + + bool bIsWindModifierTurnedOn = false; + float fAnyDirectionShift = 1.0f; + bool bIsPedDrivingBikeOrOpenTopCar = false; + if (IsPlayer() && CWindModifiers::FindWindModifier(GetPosition(), &fAnyDirectionShift, &fAnyDirectionShift) + && !CCullZones::PlayerNoRain() && GetPedState() != PED_DRIVING) + bIsWindModifierTurnedOn = true; + + if (GetPedState() == PED_DRIVING && m_pMyVehicle) { + if (m_pMyVehicle->m_vehType == VEHICLE_TYPE_BIKE + || (m_pMyVehicle->m_vehType == VEHICLE_TYPE_CAR && m_pMyVehicle->IsOpenTopCar())) + bIsPedDrivingBikeOrOpenTopCar = true; + } + + if (bIsWindModifierTurnedOn || bIsPedDrivingBikeOrOpenTopCar) { + float fWindMult = 0.0f; + if (bIsPedDrivingBikeOrOpenTopCar) { + fWindMult = DotProduct(m_pMyVehicle->m_vecMoveSpeed, GetForward()); + if (fWindMult > 0.4f) { + float volume = (fWindMult - 0.4f) / 0.6f; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_SHIRT_WIND_FLAP, volume); + } + } + + if (bIsWindModifierTurnedOn) + fWindMult = Min(fWindMult, Abs(fAnyDirectionShift - 1.0f)); + + RpHAnimHierarchy* hier = GetAnimHierarchyFromSkinClump(GetClump()); + int32 idx; + RwV3d scale; + float fScaleOffset; + + fScaleOffset = fWindMult * 0.2f; + scale.x = CGeneral::GetRandomNumberInRange(1.0f - fScaleOffset, 1.0f + fScaleOffset); + scale.y = CGeneral::GetRandomNumberInRange(1.0f - fScaleOffset, 1.0f + fScaleOffset); + scale.z = CGeneral::GetRandomNumberInRange(1.0f - fScaleOffset, 1.0f + fScaleOffset); + + idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_NECK)); + RwMatrix* neck = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwMatrixScale(neck, &scale, rwCOMBINEPRECONCAT); + + fScaleOffset = fWindMult * 0.1f; + scale.x = CGeneral::GetRandomNumberInRange(1.0f - fScaleOffset, 1.0f + fScaleOffset); + scale.y = CGeneral::GetRandomNumberInRange(1.0f - fScaleOffset, 1.0f + fScaleOffset); + scale.z = CGeneral::GetRandomNumberInRange(1.0f - fScaleOffset, 1.0f + fScaleOffset); + + idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_CLAVICLEL)); + RwMatrix* clavicleL = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwMatrixScale(clavicleL, &scale, rwCOMBINEPRECONCAT); + + idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_CLAVICLER)); + RwMatrix* clavicleR = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwMatrixScale(clavicleR, &scale, rwCOMBINEPRECONCAT); + + idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_MID)); + RwMatrix* mid = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwMatrixScale(mid, &scale, rwCOMBINEPRECONCAT); + + fScaleOffset = fWindMult * 0.2f; + scale.x = CGeneral::GetRandomNumberInRange(1.0f - fScaleOffset, 1.0f + fScaleOffset); + scale.y = CGeneral::GetRandomNumberInRange(1.0f - fScaleOffset, 1.0f + fScaleOffset); + scale.z = CGeneral::GetRandomNumberInRange(1.0f - fScaleOffset, 1.0f + fScaleOffset); + + idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_UPPERARML)); + RwMatrix* upperArmL = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwMatrixScale(upperArmL, &scale, rwCOMBINEPRECONCAT); + + idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_UPPERARMR)); + RwMatrix* upperArmR = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwMatrixScale(upperArmR, &scale, rwCOMBINEPRECONCAT); + } + + if (bBodyPartJustCameOff && m_bodyPartBleeding == PED_HEAD) { + // scale head to 0 if shot off + RpHAnimHierarchy* hier = GetAnimHierarchyFromSkinClump(GetClump()); + int32 idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_HEAD)); + RwMatrix* head = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwV3d zero = { 0.0f, 0.0f, 0.0f }; + RwMatrixScale(head, &zero, rwCOMBINEPRECONCAT); + } + + if (IsPlayer() && gfTommyFatness != 1.0f) { + RpHAnimHierarchy* hier = GetAnimHierarchyFromSkinClump(GetClump()); + int32 idx; + RwV3d scale; + + scale.x = 1.0f; + scale.y = 1.0f + gfTommyFatness * 0.7f; + scale.z = 1.0f + gfTommyFatness * 0.7f; + idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_HEAD)); + RwMatrix* head = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwMatrixScale(head, &scale, rwCOMBINEPRECONCAT); + + scale.y = 1.0f + gfTommyFatness * 0.2f; + scale.z = 1.0f + gfTommyFatness * 0.2f; + idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_NECK)); + RwMatrix* neck = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwMatrixScale(neck, &scale, rwCOMBINEPRECONCAT); + + scale.y = 1.0f + gfTommyFatness * 0.5f; + scale.z = 1.0f + gfTommyFatness * 0.5f; + idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_MID)); + RwMatrix* mid = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwMatrixScale(mid, &scale, rwCOMBINEPRECONCAT); + + scale.y = 1.0f + gfTommyFatness; + scale.z = 1.0f + gfTommyFatness; + idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_UPPERLEGL)); + RwMatrix* upperLegL = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwMatrixScale(upperLegL, &scale, rwCOMBINEPRECONCAT); + + idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_UPPERLEGR)); + RwMatrix* upperLegR = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwMatrixScale(upperLegR, &scale, rwCOMBINEPRECONCAT); + + scale.y = 1.0f + gfTommyFatness * 0.5f; + scale.z = 1.0f + gfTommyFatness * 0.5f; + idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_LOWERLEGR)); + RwMatrix* lowerLegR = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwMatrixScale(lowerLegR, &scale, rwCOMBINEPRECONCAT); + + idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_LOWERLEGL)); + RwMatrix* lowerLegL = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwMatrixScale(lowerLegL, &scale, rwCOMBINEPRECONCAT); + + scale.y = 1.0f + gfTommyFatness * 0.23f; + scale.z = 1.0f + gfTommyFatness * 0.23f; + idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_FOOTL)); + RwMatrix* footL = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwMatrixScale(footL, &scale, rwCOMBINEPRECONCAT); + + idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_FOOTR)); + RwMatrix* footR = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwMatrixScale(footR, &scale, rwCOMBINEPRECONCAT); + + idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_UPPERARML)); + RwMatrix* upperArmL = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwMatrixScale(upperArmL, &scale, rwCOMBINEPRECONCAT); + + idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_UPPERARMR)); + RwMatrix* upperArmR = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwMatrixScale(upperArmR, &scale, rwCOMBINEPRECONCAT); + + scale.y = 1.0f + gfTommyFatness * 0.2f; + scale.z = 1.0f + gfTommyFatness * 0.2f; + idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_FOREARML)); + RwMatrix* foreArmL = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwMatrixScale(foreArmL, &scale, rwCOMBINEPRECONCAT); + + idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_FOREARMR)); + RwMatrix* foreArmR = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwMatrixScale(foreArmR, &scale, rwCOMBINEPRECONCAT); + } + + if (bBodyPartJustCameOff && bIsPedDieAnimPlaying && m_bodyPartBleeding != -1 && (CTimer::GetFrameCounter() & 7) > 3) { + CVector bloodDir(0.0f, 0.0f, 0.0f); + CVector bloodPos(0.0f, 0.0f, 0.0f); + + TransformToNode(bloodPos, m_bodyPartBleeding); + + switch (m_bodyPartBleeding) { + case PED_HEAD: + bloodDir = 0.1f * GetUp(); + break; + case PED_UPPERARML: + bloodDir = 0.04f * GetUp() - 0.04f * GetRight(); + break; + case PED_UPPERARMR: + bloodDir = 0.04f * GetUp() - 0.04f * GetRight(); + break; + case PED_UPPERLEGL: + bloodDir = 0.04f * GetUp() + 0.05f * GetForward(); + break; + case PED_UPPERLEGR: + bloodDir = 0.04f * GetUp() + 0.05f * GetForward(); + break; + default: + bloodDir = CVector(0.0f, 0.0f, 0.0f); + break; + } + + for(int i = 0; i < 4; i++) + CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, bloodDir, nil, 0.0f, 0, 0, 0, 0); + } + if (CWeather::Rain > 0.3f && TheCamera.SoundDistUp > 15.0f) { + if ((TheCamera.GetPosition() - GetPosition()).Magnitude() < 25.0f) { + bool doSplashUp = true; + CColModel *ourCol = CModelInfo::GetColModel(GetModelIndex()); + CVector speed = FindPlayerSpeed(); + + if (Abs(speed.x) <= 0.05f && Abs(speed.y) <= 0.05f) { + if (!OnGround() && m_nPedState != PED_ATTACK && m_nPedState != PED_FIGHT) { + if (!IsPedHeadAbovePos(0.3f) || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_TIRED)) { + doSplashUp = false; + } + } else + doSplashUp = false; + } else + doSplashUp = false; + + if (doSplashUp && ourCol->numSpheres > 0) { + for(int i = 0; i < ourCol->numSpheres; i++) { + CColSphere *sphere = &ourCol->spheres[i]; + CVector splashPos; + switch (sphere->piece) { + case PEDPIECE_LEFTARM: + case PEDPIECE_RIGHTARM: + case PEDPIECE_HEAD: + splashPos = GetMatrix() * ourCol->spheres[i].center; + splashPos.z += 0.7f * sphere->radius; + splashPos.x += CGeneral::GetRandomNumberInRange(-0.15f, 0.15f); + splashPos.y += CGeneral::GetRandomNumberInRange(-0.15f, 0.15f); + CParticle::AddParticle(PARTICLE_RAIN_SPLASHUP, splashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 0, 0, CGeneral::GetRandomNumber() & 1, 0); + break; + default: + break; + } + } + } + } + } +} + +CVector vecTestTemp(-1.0f, -1.0f, -1.0f); + +void +CPed::Render(void) +{ + if (bInVehicle && m_pMyVehicle && m_nPedState != PED_EXIT_CAR && m_nPedState != PED_DRAG_FROM_CAR) { + if (!bRenderPedInCar) + return; + + if (!m_pMyVehicle->IsBike() && !IsPlayer()) { + float camDistSq = (TheCamera.GetPosition() - GetPosition()).MagnitudeSqr(); + if (camDistSq > SQR((m_pMyVehicle->IsBoat() ? 40.0f : 25.0f) * TheCamera.LODDistMultiplier)) + return; + } + } + + CEntity::Render(); + + if(m_pWeaponModel){ + RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(GetClump()); + int idx = RpHAnimIDGetIndex(hier, m_pFrames[PED_HANDR]->nodeID); + RwMatrix *mat = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwFrame *frame = RpAtomicGetFrame(m_pWeaponModel); + *RwFrameGetMatrix(frame) = *mat; + RwFrameUpdateObjects(frame); + RpAtomicRender(m_pWeaponModel); + if (IsPlayer()) { + CPlayerPed *player = (CPlayerPed*)this; + if (player->m_pMinigunTopAtomic) { + frame = RpAtomicGetFrame(player->m_pMinigunTopAtomic); + *RwFrameGetMatrix(frame) = *mat; + + player->m_fGunSpinAngle = player->m_fGunSpinSpeed * CTimer::GetTimeStep() + player->m_fGunSpinAngle; + if (player->m_fGunSpinAngle > TWOPI) + player->m_fGunSpinAngle -= TWOPI; + + CMatrix mgTopMat, localAdjMat; + mgTopMat.Attach(RwFrameGetMatrix(frame)); + localAdjMat.SetRotateX(player->m_fGunSpinAngle); + localAdjMat.Rotate(DEGTORAD(-4.477f)* vecTestTemp.x, DEGTORAD(29.731f) * vecTestTemp.y, DEGTORAD(1.064f) * vecTestTemp.z); + localAdjMat.GetPosition() += CVector(0.829f, -0.001f, 0.226f); + mgTopMat = mgTopMat * localAdjMat; + mgTopMat.UpdateRW(); + + RwFrameUpdateObjects(frame); + RpAtomicRender(player->m_pMinigunTopAtomic); + } + } + } +} + +void +CPed::CheckAroundForPossibleCollisions(void) +{ + CVector ourCentre, objCentre; + CEntity *objects[8]; + int16 maxObject; + + if (CTimer::GetTimeInMilliseconds() <= m_nPedStateTimer) + return; + + GetBoundCentre(ourCentre); + + CWorld::FindObjectsInRange(ourCentre, 10.0f, true, &maxObject, 6, objects, false, true, false, true, false); + for (int i = 0; i < maxObject; i++) { + CEntity *object = objects[i]; + if (bRunningToPhone) { + if (gPhoneInfo.PhoneAtThisPosition(object->GetPosition())) + break; + } + object->GetBoundCentre(objCentre); + float radius = object->GetBoundRadius(); + if (radius > 4.5f || radius < 1.0f) + radius = 1.0f; + + // Developers gave up calculating Z diff. later according to asm. + float diff = CVector(ourCentre - objCentre).MagnitudeSqr2D(); + + if (sq(radius + 1.0f) > diff) + m_fRotationDest += DEGTORAD(22.5f); + } +} + +void +CPed::SetIdle(void) +{ + if (m_nPedState != PED_IDLE && m_nPedState != PED_MUG && m_nPedState != PED_FLEE_ENTITY) { + if (m_nPedState == PED_AIM_GUN) + ClearPointGunAt(); + + SetPedState(PED_IDLE); + SetMoveState(PEDMOVE_STILL); + m_nLastPedState = PED_NONE; + } + if (m_nWaitState == WAITSTATE_FALSE) { + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(2000, 4000); + } +} + +void +CPed::Idle(void) +{ + CVehicle *veh = m_pMyVehicle; + if (veh && veh->m_nGettingOutFlags && m_vehDoor) { + + if (veh->m_nGettingOutFlags & GetCarDoorFlag(m_vehDoor)) { + + if (m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT) { + + CVector doorPos = GetPositionToOpenCarDoor(veh, m_vehDoor); + CVector doorDist = GetPosition() - doorPos; + + if (doorDist.MagnitudeSqr() < sq(0.5f)) { + SetMoveState(PEDMOVE_WALK); + return; + } + } + } + } + + if (m_nMoveState != PEDMOVE_STILL && !IsPlayer()) + SetMoveState(PEDMOVE_STILL); + + m_moved = CVector2D(0.0f, 0.0f); +} + +void +CPed::ClearPause(void) +{ + RestorePreviousState(); +} + +void +CPed::Pause(void) +{ + m_moved = CVector2D(0.0f, 0.0f); + if (CTimer::GetTimeInMilliseconds() > m_leaveCarTimer) + ClearPause(); +} + +void +CPed::SetFall(int extraTime, AnimationId animId, uint8 evenIfNotInControl) +{ + if (m_attachedTo) + return; + + if (!IsPedInControl() && (!evenIfNotInControl || DyingOrDead())) + return; + + ClearLookFlag(); + ClearAimFlag(); + SetStoredState(); + SetPedState(PED_FALL); + CAnimBlendAssociation *fallAssoc = nil; + if (animId == ANIM_STD_NUM) { + if (IsPlayer()) { + fallAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROLLOUT_LHS); + if (!fallAssoc) + fallAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROLLOUT_RHS); + } + } else { + fallAssoc = RpAnimBlendClumpGetAssociation(GetClump(), animId); + + if (fallAssoc) { + fallAssoc->SetCurrentTime(0.0f); + fallAssoc->blendAmount = 0.0f; + fallAssoc->blendDelta = 8.0f; + fallAssoc->SetRun(); + } + else { + fallAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, animId, 8.0f); + } + if (animId == ANIM_STD_BIKE_FALLBACK) + fallAssoc->SetCurrentTime(0.4f); + } + + if (extraTime == -1) { + m_getUpTimer = UINT32_MAX; + } else if (fallAssoc) { + if (IsPlayer()) { + if (fallAssoc->animId == ANIM_STD_ROLLOUT_LHS || fallAssoc->animId == ANIM_STD_ROLLOUT_RHS) { + m_getUpTimer = 1000.0f * fallAssoc->hierarchy->totalLength + + CTimer::GetTimeInMilliseconds() + - 1000.0f * fallAssoc->currentTime + + 100.0f; + } else { + m_getUpTimer = 1000.0f * fallAssoc->hierarchy->totalLength + + CTimer::GetTimeInMilliseconds() + + 500.0f; + } + } else { + m_getUpTimer = 1000.0f * fallAssoc->hierarchy->totalLength + + CTimer::GetTimeInMilliseconds() + + extraTime + + ((m_randomSeed + CTimer::GetFrameCounter()) % 1000); + } + } else { + m_getUpTimer = extraTime + + CTimer::GetTimeInMilliseconds() + + 1000 + + ((m_randomSeed + CTimer::GetFrameCounter()) % 1000); + } + bFallenDown = true; +} + +void +CPed::ClearFall(void) +{ + SetGetUp(); +} + +void +CPed::Fall(void) +{ + if (m_getUpTimer != UINT32_MAX && CTimer::GetTimeInMilliseconds() > m_getUpTimer && bIsStanding) + ClearFall(); + + CAnimBlendAssociation *firstPartialAssoc; + CAnimBlendAssociation *fallAssoc; + + if (IsPlayer() && (bKnockedUpIntoAir || bKnockedOffBike) && !bIsStanding) { + firstPartialAssoc = RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_PARTIAL); + + // What??? + if (firstPartialAssoc && (firstPartialAssoc->animId == ANIM_STD_FALL_ONBACK || firstPartialAssoc->animId == ANIM_STD_FALL_ONFRONT)) + fallAssoc = firstPartialAssoc; + else + fallAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL_ONBACK); + + if (!fallAssoc) + fallAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL_ONFRONT); + + if (!fallAssoc && firstPartialAssoc && 0.8f * firstPartialAssoc->hierarchy->totalLength < firstPartialAssoc->currentTime) { + if (firstPartialAssoc->flags & ASSOC_FRONTAL) { + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FALL_ONFRONT, 8.0f); + } else { + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FALL_ONBACK, 8.0f); + } + } else if (fallAssoc && fallAssoc->blendAmount > 0.3f && fallAssoc->blendDelta >= 0.0f) { + float time = fallAssoc->currentTime; + + if (time > 0.667f && time - fallAssoc->timeStep <= 0.667f) { + fallAssoc->SetCurrentTime(0.0f); + fallAssoc->SetRun(); + } + } + } else if ((bKnockedUpIntoAir || bKnockedOffBike) && bIsStanding && !bWasStanding) { + fallAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL_ONBACK); + + if (!fallAssoc) + fallAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL_ONFRONT); + + if (fallAssoc) { + bKnockedUpIntoAir = false; + bKnockedOffBike = false; + fallAssoc->speed = 3.0f; + if (IsPlayer()) + Say(SOUND_PED_LAND); + + } else { + firstPartialAssoc = RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_PARTIAL); + if (firstPartialAssoc && !firstPartialAssoc->IsRunning()) { + bKnockedUpIntoAir = false; + bKnockedOffBike = false; + } + } + } +} + +bool +CPed::CheckIfInTheAir(void) +{ + if (bInVehicle) + return false; + + CVector pos = GetPosition(); + CColPoint foundColPoint; + CEntity *foundEntity; + + float startZ = pos.z - 1.54f; + bool foundGround = CWorld::ProcessVerticalLine(pos, startZ, foundColPoint, foundEntity, true, true, false, true, false, false, nil); + if (!foundGround && m_nPedState != PED_JUMP) + { + pos.z -= FEET_OFFSET; + if (CWorld::TestSphereAgainstWorld(pos, 0.15f, this, true, false, false, false, false, false)) + foundGround = true; + } + return !foundGround; +} + +void +CPed::SetInTheAir(void) +{ + if (bIsInTheAir) + return; + + bIsInTheAir = true; + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FALL_GLIDE, 4.0f); + + if (m_nPedState == PED_ATTACK) { + ClearAttack(); + ClearPointGunAt(); + } else if (m_nPedState == PED_FIGHT) { + EndFight(ENDFIGHT_FAST); + } + +} + +void +CPed::InTheAir(void) +{ + CColPoint foundCol; + CEntity *foundEnt; + + CVector ourPos = GetPosition(); + CVector bitBelow = GetPosition(); + bitBelow.z -= 4.04f; + + if (m_vecMoveSpeed.z < 0.0f && !bIsPedDieAnimPlaying) { + if (!DyingOrDead()) { + if (CWorld::ProcessLineOfSight(ourPos, bitBelow, foundCol, foundEnt, true, true, false, true, false, false, false)) { + if (GetPosition().z - foundCol.point.z < 1.3f || bIsStanding) + SetLanding(); + } else if (m_nPedState != PED_ABSEIL && !RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL)) { + if (m_vecMoveSpeed.z < -0.1f) + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FALL, 4.0f); + } + } + } +} + +void +CPed::SetLanding(void) +{ + if (DyingOrDead()) + return; + + CAnimBlendAssociation *fallAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL); + CAnimBlendAssociation *landAssoc; + + if (fallAssoc && bIsDrowning) + return; + + RpAnimBlendClumpSetBlendDeltas(GetClump(), ASSOC_PARTIAL, -1000.0f); + if (fallAssoc || m_nPedType == PEDTYPE_COP && bKnockedUpIntoAir) { + landAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FALL_COLLAPSE); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_FALL_COLLAPSE, 1.0f); + + if (IsPlayer()) + Say(SOUND_PED_LAND); + + if (m_nPedType == PEDTYPE_COP) { + if (bKnockedUpIntoAir) + bKnockedUpIntoAir = false; + } + + } else { + landAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FALL_LAND); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_FALL_LAND, 1.0f); + } + + landAssoc->SetFinishCallback(PedLandCB, this); + bIsInTheAir = false; + bIsLanding = true; +} + +void +CPed::SetGetUp(void) +{ + if (m_nPedState == PED_GETUP && bGetUpAnimStarted) + return; + + if (!CanSetPedState()) + return; + + if (m_fHealth >= 1.0f || IsPedHeadAbovePos(-0.3f)) { + if (bUpdateAnimHeading) { + m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); + m_fRotationCur -= HALFPI; + bUpdateAnimHeading = false; + } + if (m_nPedState != PED_GETUP) { + SetStoredState(); + SetPedState(PED_GETUP); + } + + CVehicle *collidingVeh = (CVehicle*)m_pCollidingEntity; + CVehicle *veh = (CVehicle*)CPedPlacement::IsPositionClearOfCars(&GetPosition()); + if (veh && veh->m_vehType != VEHICLE_TYPE_BIKE && veh != m_attachedTo || + collidingVeh && collidingVeh->IsVehicle() && collidingVeh->m_vehType != VEHICLE_TYPE_BIKE + && ((uint8)(CTimer::GetFrameCounter() + m_randomSeed + 5) % 8 || + CCollision::ProcessColModels(GetMatrix(), *GetColModel(), collidingVeh->GetMatrix(), *collidingVeh->GetColModel(), + aTempPedColPts, nil, nil) > 0)) { + + bGetUpAnimStarted = false; + if (IsPlayer()) + InflictDamage(nil, WEAPONTYPE_RUNOVERBYCAR, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); + else if(CPad::GetPad(0)->ArePlayerControlsDisabled()) + InflictDamage(nil, WEAPONTYPE_RUNOVERBYCAR, 1000.0f, PEDPIECE_TORSO, 0); + return; + } + bGetUpAnimStarted = true; + m_pCollidingEntity = nil; + bKnockedUpIntoAir = false; + bKnockedOffBike = false; + CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNFAST); + if (animAssoc) { + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUN)) { + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_RUN, 8.0f); + } else { + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 8.0f); + } + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + + if (m_nWaitState == WAITSTATE_SUN_BATHE_IDLE) { + m_headingRate = 0.0f; + + // Seemingly they planned to use different getup anims for different conditions, but sadly in final game all getup anims(GETUP1, GETUP2, GETUP3) are same... + if (bFleeWhenStanding && m_threatEx) + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GET_UP, 1000.0f); + else + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GET_UP, 1000.0f); + + } else if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL)) + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GET_UP_FRONT, 1000.0f); + else + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GET_UP, 1000.0f); + + animAssoc->SetFinishCallback(PedGetupCB, this); + } else { + m_fHealth = 0.0f; + SetDie(ANIM_STD_NUM, 4.0f, 0.0f); + } +} + +void +CPed::Mug(void) +{ + if (m_pSeekTarget && m_pSeekTarget->IsPed()) { + + if (CTimer::GetTimeInMilliseconds() <= m_attackTimer - 2000) { + if ((m_pSeekTarget->GetPosition() - GetPosition()).Magnitude() > 3.0f) + m_wepSkills = 50; + + Say(SOUND_PED_MUGGING); + ((CPed*)m_pSeekTarget)->Say(SOUND_PED_ROBBED); + } else { + SetWanderPath(CGeneral::GetRandomNumber() & 7); + SetFlee(m_pSeekTarget, 20000); + } + + } else { + SetIdle(); + } +} + +// Unused +void +CPed::SetLook(float direction) +{ + if (IsPedInControl()) { + SetStoredState(); + SetPedState(PED_LOOK_HEADING); + SetLookFlag(direction, false); + } +} + +void +CPed::SetLook(CEntity* to) +{ + if (IsPedInControl()) { + SetStoredState(); + SetPedState(PED_LOOK_ENTITY); + SetLookFlag(to, false); + } +} + +void +CPed::SetLookTimer(int time) +{ + if (CTimer::GetTimeInMilliseconds() > m_lookTimer) { + m_lookTimer = CTimer::GetTimeInMilliseconds() + time; + } +} + +void +CPed::SetAttackTimer(uint32 time) +{ + if (CTimer::GetTimeInMilliseconds() > m_attackTimer) + m_attackTimer = Max(m_shootTimer, CTimer::GetTimeInMilliseconds()) + time; +} + +void +CPed::SetShootTimer(uint32 time) +{ + if (CTimer::GetTimeInMilliseconds() > m_shootTimer) { + m_shootTimer = CTimer::GetTimeInMilliseconds() + time; + } +} + +void +CPed::ClearLook(void) +{ + RestorePreviousState(); + ClearLookFlag(); +} + +void +CPed::Look(void) +{ + TurnBody(); +} + +bool +CPed::TurnBody(void) +{ + bool turnDone = true; + + if (m_pLookTarget) + m_fLookDirection = CGeneral::GetRadianAngleBetweenPoints( + m_pLookTarget->GetPosition().x, + m_pLookTarget->GetPosition().y, + GetPosition().x, + GetPosition().y); + + float limitedLookDir = CGeneral::LimitRadianAngle(m_fLookDirection); + float currentRot = m_fRotationCur; + + if (currentRot - PI > limitedLookDir) + limitedLookDir += 2 * PI; + else if (PI + currentRot < limitedLookDir) + limitedLookDir -= 2 * PI; + + float neededTurn = currentRot - limitedLookDir; + m_fRotationDest = limitedLookDir; + + if (Abs(neededTurn) > 0.05f) { + turnDone = false; + currentRot -= neededTurn * 0.2f; + } + + m_fRotationCur = currentRot; + m_fLookDirection = limitedLookDir; + return turnDone; +} + +void +CPed::SetSeek(CVector pos, float distanceToCountDone) +{ + if (!IsPedInControl() + || (m_nPedState == PED_SEEK_POS && m_vecSeekPos.x == pos.x && m_vecSeekPos.y == pos.y) || m_nPedState == PED_FOLLOW_PATH) + return; + + if (!CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM)) { + ClearPointGunAt(); + } + + if (m_nPedState != PED_SEEK_POS) + SetStoredState(); + + SetPedState(PED_SEEK_POS); + m_distanceToCountSeekDone = distanceToCountDone; + m_vecSeekPos = pos; +} +void +CPed::SetSeek(CEntity *seeking, float distanceToCountDone) +{ + if (!IsPedInControl()) + return; + + if (m_nPedState == PED_SEEK_ENTITY && m_pSeekTarget == seeking) + return; + + if (!seeking || m_nPedState == PED_FOLLOW_PATH) + return; + + if (m_nPedState != PED_SEEK_ENTITY) + SetStoredState(); + + SetPedState(PED_SEEK_ENTITY); + m_distanceToCountSeekDone = distanceToCountDone; + m_pSeekTarget = seeking; + m_pSeekTarget->RegisterReference((CEntity **) &m_pSeekTarget); + SetMoveState(PEDMOVE_STILL); +} + +void +CPed::ClearSeek(void) +{ + SetIdle(); + bRunningToPhone = false; +} + +bool +CPed::Seek(void) +{ + float distanceToCountItDone = m_distanceToCountSeekDone; + eMoveState nextMove = PEDMOVE_NONE; + + if (m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER) { + + if (m_nPedState != PED_EXIT_TRAIN && m_nPedState != PED_ENTER_TRAIN && m_nPedState != PED_SEEK_IN_BOAT && + m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER && m_objective != OBJECTIVE_SOLICIT_VEHICLE && !bDuckAndCover) { + + if ((!m_pedInObjective || !m_pedInObjective->bInVehicle) + && (CTimer::GetFrameCounter() + m_randomSeed + 60) % 32 == 0) { + + CEntity *obstacle = CWorld::TestSphereAgainstWorld(m_vecSeekPos, 0.4f, nil, + false, true, false, false, false, false); + + if (obstacle) { + if (!obstacle->IsVehicle() || ((CVehicle*)obstacle)->IsCar()) { + distanceToCountItDone = 2.5f; + } else { + CVehicleModelInfo *vehModel = (CVehicleModelInfo *)CModelInfo::GetModelInfo(obstacle->GetModelIndex()); + float yLength = vehModel->GetColModel()->boundingBox.max.y + - vehModel->GetColModel()->boundingBox.min.y; + distanceToCountItDone = yLength * 0.55f; + } + } + } + } + } + + if (!m_pSeekTarget && m_nPedState == PED_SEEK_ENTITY) + ClearSeek(); + + if (m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION && !m_pedInObjective) { + m_objective = OBJECTIVE_NONE; + ClearObjective(); + SetWanderPath(0); + return false; + } + + float seekPosDist = (m_vecSeekPos - GetPosition()).Magnitude2D(); + if (seekPosDist < 2.0f || m_objective == OBJECTIVE_GOTO_AREA_ON_FOOT) { + + if (m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION) { + + if (m_pedInObjective->m_nMoveState != PEDMOVE_STILL) + nextMove = m_pedInObjective->m_nMoveState; + } else + nextMove = PEDMOVE_WALK; + + } else if (m_objective != OBJECTIVE_FOLLOW_CHAR_IN_FORMATION) { + + if (m_objective == OBJECTIVE_SPRINT_TO_AREA) + nextMove = PEDMOVE_SPRINT; + else if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS || m_objective == OBJECTIVE_RUN_TO_AREA || bIsRunning) + nextMove = PEDMOVE_RUN; + else + nextMove = PEDMOVE_WALK; + + } else if (seekPosDist <= 2.0f) { + + if (m_pedInObjective->m_nMoveState != PEDMOVE_STILL) + nextMove = m_pedInObjective->m_nMoveState; + + } else { + nextMove = PEDMOVE_RUN; + } + + if (m_nPedState == PED_SEEK_ENTITY) { + if (m_pSeekTarget->IsPed()) { + if (((CPed*)m_pSeekTarget)->bInVehicle) + distanceToCountItDone += 2.0f; + } + } + + CVector *nextNode = SeekFollowingPath(); + + if (nextNode || seekPosDist >= distanceToCountItDone) { + if (bIsRunning && nextMove != PEDMOVE_SPRINT) + nextMove = PEDMOVE_RUN; + + if (CTimer::GetTimeInMilliseconds() <= m_nPedStateTimer) { + + if (m_actionX != 0.0f && m_actionY != 0.0f) { + + m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( + m_actionX, m_actionY, + GetPosition().x, GetPosition().y); + + float neededTurn = Abs(m_fRotationDest - m_fRotationCur); + + if (neededTurn > PI) + neededTurn = TWOPI - neededTurn; + + if (neededTurn > HALFPI) { + if (seekPosDist >= 1.0f) { + if (seekPosDist < 2.0f) { + if (bIsRunning) + nextMove = PEDMOVE_RUN; + else + nextMove = PEDMOVE_WALK; + } + } else { + nextMove = PEDMOVE_STILL; + } + } + + CVector2D moveDist(GetPosition().x - m_actionX, GetPosition().y - m_actionY); + if (moveDist.Magnitude() < 0.5f) { + m_nPedStateTimer = 0; + m_actionX = 0.f; + m_actionY = 0.f; + } + } + + } else { + if (nextNode) { + m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( + nextNode->x, nextNode->y, + GetPosition().x, GetPosition().y); + } else { + m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( + m_vecSeekPos.x, m_vecSeekPos.y, + GetPosition().x, GetPosition().y); + } + + float neededTurn = Abs(m_fRotationDest - m_fRotationCur); + + if (neededTurn > PI) + neededTurn = TWOPI - neededTurn; + + if (neededTurn > HALFPI) { + if (seekPosDist >= 1.0f && neededTurn <= DEGTORAD(135.0f)) { + if (seekPosDist < 2.0f) + nextMove = PEDMOVE_WALK; + } else { + nextMove = PEDMOVE_STILL; + } + } + } + + if (((m_nPedState == PED_FLEE_POS || m_nPedState == PED_FLEE_ENTITY) && m_nMoveState < nextMove) + || (m_nPedState != PED_FLEE_POS && m_nPedState != PED_FLEE_ENTITY && m_nPedState != PED_FOLLOW_PATH && m_objective != OBJECTIVE_GOTO_CHAR_ON_FOOT && m_nWaitState == WAITSTATE_FALSE)) { + + SetMoveState(nextMove); + } + + SetMoveAnim(); + return false; + } + + if ((m_objective != OBJECTIVE_FOLLOW_CHAR_IN_FORMATION || m_pedInObjective->m_nMoveState == PEDMOVE_STILL) && m_nMoveState != PEDMOVE_STILL) { + m_nPedStateTimer = 0; + m_actionX = 0.f; + m_actionY = 0.f; + } + + if (m_objective == OBJECTIVE_GOTO_AREA_ON_FOOT || m_objective == OBJECTIVE_RUN_TO_AREA || m_objective == OBJECTIVE_SPRINT_TO_AREA || + m_objective == OBJECTIVE_GOTO_AREA_ANY_MEANS || IsUseAttractorObjective(m_objective)) { + + if (m_pNextPathNode) + m_pNextPathNode = nil; + else + bScriptObjectiveCompleted = true; + + bUsePedNodeSeek = true; + } + + return true; +} + +void +CPed::SetFlee(CVector2D const &from, int time) +{ + if (CTimer::GetTimeInMilliseconds() < m_nPedStateTimer || !IsPedInControl() || bKindaStayInSamePlace) + return; + + if (m_nPedState != PED_FLEE_ENTITY) { + SetStoredState(); + SetPedState(PED_FLEE_POS); + SetMoveState(PEDMOVE_RUN); + m_fleeFromPos = from; + } + + bUsePedNodeSeek = true; + m_pNextPathNode = nil; + m_fleeTimer = CTimer::GetTimeInMilliseconds() + time; + + float angleToFace = CGeneral::GetRadianAngleBetweenPoints( + GetPosition().x, GetPosition().y, + from.x, from.y); + + m_fRotationDest = CGeneral::LimitRadianAngle(angleToFace); + if (m_fRotationCur - PI > m_fRotationDest) { + m_fRotationDest += 2 * PI; + } else if (PI + m_fRotationCur < m_fRotationDest) { + m_fRotationDest -= 2 * PI; + } +} + +void +CPed::SetFlee(CEntity *fleeFrom, int time) +{ + if (!IsPedInControl() || bKindaStayInSamePlace || !fleeFrom) + return; + + SetStoredState(); + SetPedState(PED_FLEE_ENTITY); + bUsePedNodeSeek = true; + SetMoveState(PEDMOVE_RUN); + m_fleeFrom = fleeFrom; + m_fleeFrom->RegisterReference((CEntity **) &m_fleeFrom); + + if (time <= 0) + m_fleeTimer = 0; + else + m_fleeTimer = CTimer::GetTimeInMilliseconds() + time; + + float angleToFace = CGeneral::GetRadianAngleBetweenPoints( + GetPosition().x, GetPosition().y, + fleeFrom->GetPosition().x, fleeFrom->GetPosition().y); + + m_fRotationDest = CGeneral::LimitRadianAngle(angleToFace); + if (m_fRotationCur - PI > m_fRotationDest) { + m_fRotationDest += 2 * PI; + } else if (PI + m_fRotationCur < m_fRotationDest) { + m_fRotationDest -= 2 * PI; + } +} + +void +CPed::ClearFlee(void) +{ + RestorePreviousState(); + bUsePedNodeSeek = false; + m_chatTimer = 0; + m_fleeTimer = 0; +} + +void +CPed::Flee(void) +{ + if (CTimer::GetTimeInMilliseconds() > m_fleeTimer && m_fleeTimer) { + bool mayFinishFleeing = true; + if (m_nPedState == PED_FLEE_ENTITY) { + if ((CVector2D(GetPosition()) - ms_vec2DFleePosition).MagnitudeSqr() < sq(30.0f)) + mayFinishFleeing = false; + } + + if (mayFinishFleeing) { + bMakeFleeScream = false; + eMoveState moveState = m_nMoveState; + ClearFlee(); + + if (m_objective == OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE || m_objective == OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS) { + bBeingChasedByPolice = false; + RestorePreviousObjective(); + } + + if ((m_nPedState == PED_IDLE || m_nPedState == PED_WANDER_PATH) && CGeneral::GetRandomNumber() & 1) { + SetWaitState(moveState <= PEDMOVE_WALK ? WAITSTATE_CROSS_ROAD_LOOK : WAITSTATE_FINISH_FLEE, nil); + } + return; + } + m_fleeTimer = CTimer::GetTimeInMilliseconds() + 5000; + } + + if (bMakeFleeScream && !((CTimer::GetFrameCounter() + m_randomSeed) & 7)) { + Say(SOUND_PED_FLEE_SPRINT); + bMakeFleeScream = false; + } + + if (bUsePedNodeSeek) { + CPathNode *realLastNode = nil; + uint8 nextDirection = 0; + uint8 curDirectionShouldBe = 9; // means not defined yet + + if (m_nPedStateTimer < CTimer::GetTimeInMilliseconds() + && m_collidingThingTimer < CTimer::GetTimeInMilliseconds()) { + + if (m_pNextPathNode && CTimer::GetTimeInMilliseconds() > m_chatTimer) { + + curDirectionShouldBe = CGeneral::GetNodeHeadingFromVector(GetPosition().x - ms_vec2DFleePosition.x, GetPosition().y - ms_vec2DFleePosition.y); + if (m_nPathDir < curDirectionShouldBe) + m_nPathDir += 8; + + int dirDiff = m_nPathDir - curDirectionShouldBe; + if (dirDiff > 2 && dirDiff < 6) { + realLastNode = nil; + m_pLastPathNode = m_pNextPathNode; + m_pNextPathNode = nil; + } + } + + if (m_pNextPathNode) { + m_vecSeekPos = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); + if (m_nMoveState == PEDMOVE_RUN) + bIsRunning = true; + + eMoveState moveState = m_nMoveState; + if (Seek()) { + realLastNode = m_pLastPathNode; + m_pLastPathNode = m_pNextPathNode; + m_pNextPathNode = nil; + } + bIsRunning = false; + SetMoveState(moveState); + } + } + + if (!m_pNextPathNode) { + if (curDirectionShouldBe == 9) { + curDirectionShouldBe = CGeneral::GetNodeHeadingFromVector(GetPosition().x - ms_vec2DFleePosition.x, GetPosition().y - ms_vec2DFleePosition.y); + } + ThePaths.FindNextNodeWandering(PATH_PED, GetPosition(), &m_pLastPathNode, &m_pNextPathNode, + curDirectionShouldBe, + &nextDirection); + + if (curDirectionShouldBe < nextDirection) + curDirectionShouldBe += 8; + + if (m_pNextPathNode && m_pNextPathNode != realLastNode && m_pNextPathNode != m_pLastPathNode && curDirectionShouldBe - nextDirection != 4) { + m_nPathDir = nextDirection; + m_chatTimer = CTimer::GetTimeInMilliseconds() + 2000; + } else { + bUsePedNodeSeek = false; + SetMoveState(PEDMOVE_RUN); + Flee(); + } + } + return; + } + + if ((m_nPedState == PED_FLEE_ENTITY || m_nPedState == PED_ON_FIRE) && m_nPedStateTimer < CTimer::GetTimeInMilliseconds()) { + + float angleToFleeFromPos = CGeneral::GetRadianAngleBetweenPoints( + GetPosition().x, + GetPosition().y, + ms_vec2DFleePosition.x, + ms_vec2DFleePosition.y); + + m_fRotationDest = CGeneral::LimitRadianAngle(angleToFleeFromPos); + + if (m_fRotationCur - PI > m_fRotationDest) + m_fRotationDest += TWOPI; + else if (PI + m_fRotationCur < m_fRotationDest) + m_fRotationDest -= TWOPI; + } + + if (CTimer::GetTimeInMilliseconds() >= m_collidingThingTimer) + return; + + if (!m_collidingEntityWhileFleeing) + return; + + double collidingThingPriorityMult = (double)(m_collidingThingTimer - CTimer::GetTimeInMilliseconds()) * 2.0 / 2500; + + if (collidingThingPriorityMult <= 1.5) { + double angleToFleeEntity = CGeneral::GetRadianAngleBetweenPoints( + GetPosition().x, + GetPosition().y, + m_collidingEntityWhileFleeing->GetPosition().x, + m_collidingEntityWhileFleeing->GetPosition().y); + angleToFleeEntity = CGeneral::LimitRadianAngle(angleToFleeEntity); + + double angleToFleeCollidingThing = CGeneral::GetRadianAngleBetweenPoints( + m_vecDamageNormal.x, + m_vecDamageNormal.y, + 0.0f, + 0.0f); + angleToFleeCollidingThing = CGeneral::LimitRadianAngle(angleToFleeCollidingThing); + + if (angleToFleeEntity - PI > angleToFleeCollidingThing) + angleToFleeCollidingThing += TWOPI; + else if (PI + angleToFleeEntity < angleToFleeCollidingThing) + angleToFleeCollidingThing -= TWOPI; + + if (collidingThingPriorityMult <= 1.0f) { + // Range [0.0, 1.0] + + float angleToFleeBoth = (angleToFleeCollidingThing + angleToFleeEntity) * 0.5f; + + if (m_fRotationDest - PI > angleToFleeBoth) + angleToFleeBoth += TWOPI; + else if (PI + m_fRotationDest < angleToFleeBoth) + angleToFleeBoth -= TWOPI; + + m_fRotationDest = (1.0f - collidingThingPriorityMult) * m_fRotationDest + collidingThingPriorityMult * angleToFleeBoth; + } else { + // Range (1.0, 1.5] + + double adjustedMult = (collidingThingPriorityMult - 1.0f) * 2.0f; + m_fRotationDest = angleToFleeEntity * (1.0 - adjustedMult) + adjustedMult * angleToFleeCollidingThing; + } + } else { + m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( + m_vecDamageNormal.x, + m_vecDamageNormal.y, + 0.0f, + 0.0f); + m_fRotationDest = CGeneral::LimitRadianAngle(m_fRotationDest); + } + + m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); + + if (m_fRotationCur - PI > m_fRotationDest) + m_fRotationDest += TWOPI; + else if (PI + m_fRotationCur < m_fRotationDest) + m_fRotationDest -= TWOPI; + +} + +// "Wander range" state is unused in game, and you can't use it without SetWanderRange anyway +void +CPed::WanderRange(void) +{ + bool arrived = Seek(); + if (arrived) { + Idle(); + if ((m_randomSeed + 3 * CTimer::GetFrameCounter()) % 1000 > 997) { + CVector2D newCoords2D = m_wanderRangeBounds->GetRandomPointInRange(); + SetSeek(CVector(newCoords2D.x, newCoords2D.y, GetPosition().z), 2.5f); + } + } +} + +bool +CPed::SetWanderPath(int8 pathStateDest) +{ + uint8 nextPathState; + + if (IsPlayer()) + return false; + + if (IsPedInControl()) { + if (bKindaStayInSamePlace) { + SetIdle(); + return false; + } else { + m_nPathDir = pathStateDest; + if (pathStateDest == 0) + pathStateDest = CGeneral::GetRandomNumberInRange(1, 7); + + ThePaths.FindNextNodeWandering(PATH_PED, GetPosition(), &m_pLastPathNode, &m_pNextPathNode, + m_nPathDir, &nextPathState); + + // Circular loop until we find a node for current m_nPathDir + while (!m_pNextPathNode) { + m_nPathDir = (m_nPathDir+1) % 8; + + // We're at where we started and couldn't find any node + if (m_nPathDir == pathStateDest) { + ClearAll(); + SetIdle(); + return false; + } + ThePaths.FindNextNodeWandering(PATH_PED, GetPosition(), &m_pLastPathNode, &m_pNextPathNode, + m_nPathDir, &nextPathState); + } + + // We did it, save next path state and return true + m_nPathDir = nextPathState; + SetPedState(PED_WANDER_PATH); + SetMoveState(PEDMOVE_WALK); + bIsRunning = false; + return true; + } + } else { + m_nPathDir = pathStateDest; + bStartWanderPathOnFoot = true; + return false; + } +} + +void +CPed::WanderPath(void) +{ + if (!m_pNextPathNode) { + printf("THIS SHOULDN@T HAPPEN TOO OFTEN\n"); + SetIdle(); + return; + } + if (m_nWaitState == WAITSTATE_FALSE) { + if (m_nMoveState == PEDMOVE_STILL || m_nMoveState == PEDMOVE_NONE) + SetMoveState(PEDMOVE_WALK); + } + m_vecSeekPos = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); + m_vecSeekPos.z += 1.0f; + + // Only returns true when ped is stuck(not stopped) I think, then we should assign new direction or wait state to him. + if (!Seek()) + return; + + CPathNode *previousLastNode = m_pLastPathNode; + uint8 randVal = (m_randomSeed + 3 * CTimer::GetFrameCounter()) % 100; + + // We don't prefer 180-degree turns in normal situations + uint8 dirWeWouldntPrefer = m_nPathDir; + if (dirWeWouldntPrefer <= 3) + dirWeWouldntPrefer += 4; + else + dirWeWouldntPrefer -= 4; + + CPathNode *nodeWeWouldntPrefer = nil; + uint8 dirToSet = 9; // means undefined + uint8 dirWeWouldntPrefer2 = 9; // means undefined + uint8 tryCount = 0; + + if (randVal <= 90) { + if (randVal > 80) { + m_nPathDir += 2; + m_nPathDir %= 8; + } + } else { + m_nPathDir -= 2; + if (m_nPathDir < 0) + m_nPathDir += 8; + } + + m_pLastPathNode = m_pNextPathNode; + ThePaths.FindNextNodeWandering(PATH_PED, GetPosition(), &m_pLastPathNode, &m_pNextPathNode, + m_nPathDir, &dirToSet); + + if (((CPedModelInfo*)CModelInfo::GetModelInfo(m_modelIndex))->m_pedStatType == PEDSTAT_SKATER) { + if (m_pNextPathNode) { + CVector unpacked(m_pNextPathNode->GetPosition() / 8.f); + if (!CPopulation::IsSkateable(unpacked)) + m_pNextPathNode = nil; + } + } + + // NB: SetWanderPath checks for m_nPathDir == dirToStartWith, this one checks for tryCount > 7 + while (!m_pNextPathNode) { + tryCount++; + m_nPathDir = (m_nPathDir + 1) % 8; + + // We're at where we started and couldn't find any node + if (tryCount > 7) { + if (!nodeWeWouldntPrefer) { + ClearAll(); + SetIdle(); + // Probably this text carried over here after copy-pasting this loop from early version of SetWanderPath. + Error("Can't find valid path node, SetWanderPath, Ped.cpp"); + return; + } + m_pNextPathNode = nodeWeWouldntPrefer; + dirToSet = dirWeWouldntPrefer2; + } else { + ThePaths.FindNextNodeWandering(PATH_PED, GetPosition(), &m_pLastPathNode, &m_pNextPathNode, + m_nPathDir, &dirToSet); + if (m_pNextPathNode) { + if (dirToSet == dirWeWouldntPrefer) { + nodeWeWouldntPrefer = m_pNextPathNode; + dirWeWouldntPrefer2 = dirToSet; + m_pNextPathNode = nil; + } + } + if (((CPedModelInfo*)CModelInfo::GetModelInfo(m_modelIndex))->m_pedStatType == PEDSTAT_SKATER) { + if (m_pNextPathNode) { + CVector unpacked(m_pNextPathNode->GetPosition() / 8.f); + if (!CPopulation::IsSkateable(unpacked)) + m_pNextPathNode = nil; + } + } + } + } + + m_nPathDir = dirToSet; + if (m_pLastPathNode == m_pNextPathNode) { + m_pNextPathNode = previousLastNode; + SetWaitState(WAITSTATE_DOUBLEBACK, nil); + Say(SOUND_PED_WAIT_DOUBLEBACK); + } else if (ThePaths.TestForPedTrafficLight(m_pLastPathNode, m_pNextPathNode)) { + SetWaitState(WAITSTATE_TRAFFIC_LIGHTS, nil); + } else if (ThePaths.TestCrossesRoad(m_pLastPathNode, m_pNextPathNode)) { + SetWaitState(WAITSTATE_CROSS_ROAD, nil); + } else if (m_pNextPathNode == previousLastNode) { + SetWaitState(WAITSTATE_DOUBLEBACK, nil); + Say(SOUND_PED_WAIT_DOUBLEBACK); + } +} +void +CPed::Avoid(void) +{ + CPed *nearestPed; + + if(m_pedStats->m_temper > m_pedStats->m_fear && m_pedStats->m_temper > 50) + return; + + if (CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) { + + if (m_nMoveState != PEDMOVE_NONE && m_nMoveState != PEDMOVE_STILL) { + nearestPed = m_nearPeds[0]; + + if (nearestPed && nearestPed->m_nPedState != PED_DEAD && nearestPed != m_pSeekTarget && nearestPed != m_pedInObjective) { + + // Check if this ped wants to avoid the nearest one + if (CPedType::GetAvoid(m_nPedType) & CPedType::GetFlag(nearestPed->m_nPedType)) { + + // Further codes checks whether the distance between us and ped will be equal or below 1.0, if we walk up to him by 1.25 meters. + // If so, we want to avoid it, so we turn our body 45 degree and look to somewhere else. + + // Game converts from radians to degress and back again here, doesn't make much sense + CVector2D forward(-Sin(m_fRotationCur), Cos(m_fRotationCur)); + forward.Normalise(); // this is kinda pointless + + // Move forward 1.25 meters + CVector2D testPosition = CVector2D(GetPosition()) + forward*1.25f; + + // Get distance to ped we want to avoid + CVector2D distToPed = CVector2D(nearestPed->GetPosition()) - testPosition; + + if (distToPed.Magnitude() <= 1.0f && OurPedCanSeeThisOne((CEntity*)nearestPed)) { + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + + 500 + (m_randomSeed + 3 * CTimer::GetFrameCounter()) + % 1000 / 5; + + m_fRotationDest += DEGTORAD(45.0f); + if (!bIsLooking) { + SetLookFlag(nearestPed, false); + SetLookTimer(CGeneral::GetRandomNumberInRange(500, 800)); + } + } + } + } + } + } +} + +CVector* +CPed::SeekFollowingPath(void) +{ + static CVector vecNextPathNode; + + if (m_nCurPathNodeId >= m_nNumPathNodes || m_nNumPathNodes == 0) + return nil; + + vecNextPathNode = m_pathNodesToGo[m_nCurPathNodeId]->GetPosition(); + + if ((vecNextPathNode - GetPosition()).Magnitude2D() < m_distanceToCountSeekDone) { + m_nCurPathNodeId++; + if (m_nCurPathNodeId < m_nNumPathNodes) + m_pCurPathNode = m_pathNodesToGo[m_nCurPathNodeId]; + } + if (m_nCurPathNodeId == m_nNumPathNodes) + return nil; + else + return &vecNextPathNode; +} + +bool +CPed::SetFollowPath(CVector dest, float radius, eMoveState state, CEntity* walkAroundEnt, CEntity* targetEnt, int time) +{ + if (m_nPedState == PED_FOLLOW_PATH) { + bool stopFollow = false; + if (walkAroundEnt && walkAroundEnt != m_followPathWalkAroundEnt || !walkAroundEnt && m_followPathWalkAroundEnt + || targetEnt && targetEnt != m_followPathTargetEnt || !targetEnt && m_followPathTargetEnt) { + stopFollow = true; + + } else if (targetEnt) { + if ((targetEnt->GetPosition() - m_followPathDestPos).MagnitudeSqr() > 1.f) + stopFollow = true; + + } else if (!walkAroundEnt && !targetEnt) { + if ((dest - m_followPathDestPos).MagnitudeSqr() > 1.f) + stopFollow = true; + } + + if (!stopFollow) + return false; + } + m_pathNodeTimer = CTimer::GetTimeInMilliseconds() + time; + m_followPathWalkAroundEnt = walkAroundEnt; + m_followPathTargetEnt = targetEnt; + m_distanceToCountSeekDone = 0.5f; + + bool weHaveTargetPed = targetEnt && targetEnt->IsPed(); + bool useDestVec = !weHaveTargetPed; + + if (useDestVec) + m_followPathDestPos = dest; + else + m_followPathDestPos = targetEnt->GetPosition(); + + if (targetEnt && m_nPedState == PED_SEEK_POS) { + m_followPathDestPos = m_vecSeekPos; + } + + m_followPathAbortDist = radius > 0.f ? radius : 20.f; + + bool useGivenPedMove = state == PEDMOVE_RUN || state == PEDMOVE_WALK; + if (useGivenPedMove) + m_followPathMoveState = state; + else + m_followPathMoveState = PEDMOVE_WALK; + + if (m_followPathWalkAroundEnt) + return SetFollowPathDynamic(); + else + return SetFollowPathStatic(); +} + +bool +CPed::SetFollowPathStatic(void) +{ + ClearFollowPath(); + if (sq(m_followPathAbortDist) > (GetPosition() - m_followPathDestPos).MagnitudeSqr() + && CWorld::IsWanderPathClear(GetPosition(), m_followPathDestPos, 0.5f, 4)) { + + RestorePreviousState(); + if (m_objective == OBJECTIVE_NONE) { + if (m_followPathMoveState == PEDMOVE_RUN) + SetObjective(OBJECTIVE_RUN_TO_AREA, m_followPathDestPos); + else + SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, m_followPathDestPos); + } + SetPedState(PED_NONE); + } else { + ThePaths.DoPathSearch(PATH_PED, GetPosition(), -1, m_followPathDestPos, m_pathNodesToGo, &m_nNumPathNodes, + ARRAY_SIZE(m_pathNodesToGo), nil, nil, 999999.9f, -1); + + if (m_nNumPathNodes != 0) { + if (m_nNumPathNodes > 0 && m_pathNodesToGo[0] != m_pCurPathNode) { + for (int i = 0; i < ARRAY_SIZE(m_pathNodesToGo) - 1; i++) { + m_pathNodesToGo[i] = m_pathNodesToGo[i+1]; + } + --m_nNumPathNodes; + } + for (int i = 0; i < m_nNumPathNodes; ++i) { + CVector nodePos = m_pathNodesToGo[i]->GetPosition(); + if (sq(m_followPathAbortDist) > (nodePos - m_followPathDestPos).MagnitudeSqr() + && CWorld::IsWanderPathClear(nodePos, m_followPathDestPos, 0.5f, 4)) { + + m_nNumPathNodes = i + 1; + break; + } + } + + m_nCurPathNodeId = 0; + if (m_pCurPathNode) { + for (int j = 0; j < m_nNumPathNodes; ++j) { + if (m_pathNodesToGo[j] == m_pCurPathNode) { + m_nCurPathNodeId = j; + break; + } + } + } + m_pCurPathNode = m_pathNodesToGo[m_nCurPathNodeId]; + PedState oldLastState = m_nLastPedState; + m_nLastPedState = PED_NONE; + SetStoredState(); + if (m_nLastPedState == PED_NONE) + m_nLastPedState = oldLastState; + + SetPedState(PED_FOLLOW_PATH); + SetMoveState(m_followPathMoveState); + } else { + RestorePreviousState(); + if (m_objective == OBJECTIVE_NONE) { + if (m_followPathMoveState == PEDMOVE_RUN) + SetObjective(OBJECTIVE_RUN_TO_AREA, m_followPathDestPos); + else + SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, m_followPathDestPos); + } + SetPedState(PED_NONE); + } + } + return true; +} + +bool +CPed::SetFollowPathDynamic(void) +{ + CVector colBoxMin = m_followPathWalkAroundEnt->GetColModel()->boundingBox.min + CVector(-0.35f, -0.35f, 0.f); + CVector colBoxMax = m_followPathWalkAroundEnt->GetColModel()->boundingBox.max + CVector(0.35f, 0.35f, 0.f); + + CVector colCornerOffsets[4]; // BL, BR, TR, TL + colCornerOffsets[0] = CVector(colBoxMin.x, colBoxMin.y, 0.f); + colCornerOffsets[1] = CVector(colBoxMax.x, colBoxMin.y, 0.f); + colCornerOffsets[2] = CVector(colBoxMax.x, colBoxMax.y, 0.f); + colCornerOffsets[3] = CVector(colBoxMin.x, colBoxMax.y, 0.f); + + if (m_followPathWalkAroundEnt->IsVehicle() && ((CVehicle*)m_followPathWalkAroundEnt)->IsUpsideDown()) { + CVector old0 = colCornerOffsets[0]; + colCornerOffsets[0] = colCornerOffsets[1]; + colCornerOffsets[1] = old0; + CVector old2 = colCornerOffsets[2]; + colCornerOffsets[2] = colCornerOffsets[3]; + colCornerOffsets[3] = old2; + } + + CVector colCornerPos[4]; // global. again BL, BR, TR, TL + float dotProdCorrection[4]; + CVector colBoxPlaneNormal[4]; + + for (int i=0; i<4; i++) { + colCornerPos[i] = m_followPathWalkAroundEnt->GetMatrix() * colCornerOffsets[i]; + colCornerPos[i].z = GetPosition().z; + } + + CVector prevColCorner = colCornerPos[3]; // top left + CVector *curCornerPos; + CVector fwdToNextCorner; + + for (int i=0; i<4; i++) { + curCornerPos = &colCornerPos[i]; + fwdToNextCorner = *curCornerPos - prevColCorner; + fwdToNextCorner.Normalise(); + colBoxPlaneNormal[i] = CrossProduct(fwdToNextCorner, CVector(0.f, 0.f, 1.f)); + dotProdCorrection[i] = -DotProduct(prevColCorner, colBoxPlaneNormal[i]); // yes, dp with global coord, as if in distance to plane calculation + prevColCorner = *curCornerPos; + } + + bool weReGoingGreat = false; + CVector startVecCandidate = GetPosition(); + CVector targetVecCandidate = m_followPathDestPos; + CVector dirToGo = targetVecCandidate - startVecCandidate; + dirToGo.Normalise(); + CVector ourPos = startVecCandidate; + + for (int i=0; i<4; i++) { + CVector curPlaneNormal = colBoxPlaneNormal[i]; + float minusGlobalCornerPos = dotProdCorrection[i]; + float startVecDistToPlane = DotProduct(curPlaneNormal, startVecCandidate) + minusGlobalCornerPos; + +#define FRONT_OF_PLANE 1 +#define ON_THE_PLANE 0 +#define BEHIND_THE_PLANE -1 + + int8 startVecStatus; + int8 targetVecStatus; + + if (startVecDistToPlane > 0.1f) + startVecStatus = FRONT_OF_PLANE; + else if (startVecDistToPlane < -0.1f) + startVecStatus = BEHIND_THE_PLANE; + else + startVecStatus = ON_THE_PLANE; + + float targetVecDistToPlane = DotProduct(curPlaneNormal, targetVecCandidate) + minusGlobalCornerPos; + if (targetVecDistToPlane > 0.1f) + targetVecStatus = FRONT_OF_PLANE; + else if (targetVecDistToPlane < -0.1f) + targetVecStatus = BEHIND_THE_PLANE; + else + targetVecStatus = ON_THE_PLANE; + + + if (startVecStatus == BEHIND_THE_PLANE || targetVecStatus == BEHIND_THE_PLANE) { + if (startVecStatus == BEHIND_THE_PLANE && targetVecStatus == FRONT_OF_PLANE) { + targetVecCandidate = -(DotProduct(ourPos, curPlaneNormal) + minusGlobalCornerPos) / DotProduct(dirToGo, curPlaneNormal) * dirToGo + ourPos; + + } else if (startVecStatus == FRONT_OF_PLANE && targetVecStatus == BEHIND_THE_PLANE) { + startVecCandidate = -(DotProduct(ourPos, curPlaneNormal) + minusGlobalCornerPos) / DotProduct(dirToGo, curPlaneNormal) * dirToGo + ourPos; + } + } else { + weReGoingGreat = true; + if (startVecStatus == ON_THE_PLANE) + startVecCandidate += (0.1f - startVecDistToPlane) * curPlaneNormal; + + if (targetVecStatus == ON_THE_PLANE) + targetVecCandidate += (0.1f - targetVecDistToPlane) * curPlaneNormal; + } +#undef FRONT_OF_PLANE +#undef ON_THE_PLANE +#undef BEHIND_THE_PLANE + } + + if (!weReGoingGreat) { + CVector avgOfColPoints = (colCornerPos[0] + colCornerPos[1] + colCornerPos[2] + colCornerPos[3]) / 4.f; + float radius = 0.0f; + + // Find radius of col box of the entity we follow + for (int i=0; i<4; i++) { + float cornerDist = (colCornerPos[i] - avgOfColPoints).MagnitudeSqr(); + + if (cornerDist > radius) + radius = cornerDist; + } + CColSphere followedEntSphere; + followedEntSphere.Set(Sqrt(radius) * 1.1f, avgOfColPoints, 0, 0); + CVector distToDest = m_followPathDestPos - GetPosition(); + distToDest.z = 0.f; + + if (distToDest.Magnitude() == 0.0f) + return false; + + distToDest.Normalise(); + + // Entity we follow doesn't go toward destination anymore, abort the following. + if (!followedEntSphere.IntersectRay(GetPosition(), distToDest, startVecCandidate, targetVecCandidate)) { + m_pathNodeTimer = 0; + if (m_nPedState == PED_FOLLOW_PATH) + RestorePreviousState(); + + return false; + } + } + + int lastPlaneBehindUs = -1; + int lastPlaneInFrontOfUs = -1; + CVector oldstartVecCandidate = startVecCandidate; + CVector oldDirToGo = targetVecCandidate - startVecCandidate; + oldDirToGo.Normalise(); + + + // At least one plane should be between target and us. + for (int i=0; i<4; i++) { + CVector curPlaneNormal = colBoxPlaneNormal[i]; + float minusGlobalCornerPos = dotProdCorrection[i]; + float startVecDistToPlane = DotProduct(curPlaneNormal, startVecCandidate) + minusGlobalCornerPos; + float targetVecDistToPlane = DotProduct(curPlaneNormal, targetVecCandidate) + minusGlobalCornerPos; + + if (startVecDistToPlane > 0.0f && targetVecDistToPlane < 0.0f) { + lastPlaneInFrontOfUs = i; + startVecCandidate = -(DotProduct(oldstartVecCandidate, curPlaneNormal) + minusGlobalCornerPos) / DotProduct(oldDirToGo, curPlaneNormal) * oldDirToGo + oldstartVecCandidate; + + } else if (startVecDistToPlane < 0.0f && targetVecDistToPlane > 0.0f) { + lastPlaneBehindUs = i; + targetVecCandidate = -(DotProduct(oldstartVecCandidate, curPlaneNormal) + minusGlobalCornerPos) / DotProduct(oldDirToGo, curPlaneNormal) * oldDirToGo + oldstartVecCandidate; + } + } + + CVector destsVariant1[5]; + CVector destsVariant2[5]; + + // If not, followed entity diverged from route and we should abort the following. + if (lastPlaneBehindUs >= 0 && lastPlaneInFrontOfUs >= 0) { + + int planeInFrontCircular = (lastPlaneInFrontOfUs + 4) % -4; + int planeInFrontCircularMinusOne = (lastPlaneInFrontOfUs + 3) % -4; + int planeInBehindCircular = (lastPlaneBehindUs + 4) % -4; + int planeInBehindCircularMinusOne = (lastPlaneBehindUs + 3) % -4; + + destsVariant1[0] = GetPosition(); + destsVariant1[1] = colCornerPos[planeInFrontCircularMinusOne]; + + int destsVar1LastNode = 2; + for(; planeInFrontCircularMinusOne != planeInBehindCircular; destsVar1LastNode++) { + planeInFrontCircularMinusOne = (planeInFrontCircularMinusOne + 3) % -4; + destsVariant1[destsVar1LastNode] = colCornerPos[planeInFrontCircularMinusOne]; + } + destsVariant1[destsVar1LastNode] = m_followPathDestPos; + + destsVariant2[0] = GetPosition(); + destsVariant2[1] = colCornerPos[planeInFrontCircular]; + + int destsVar2LastNode = 2; + for (; planeInFrontCircular != planeInBehindCircularMinusOne; destsVar2LastNode++) { + planeInFrontCircular = (planeInFrontCircular + 5) % -4; + destsVariant2[destsVar2LastNode] = colCornerPos[planeInFrontCircular]; + } + destsVariant2[destsVar2LastNode] = m_followPathDestPos; + CEntity *foundEnt1 = nil; + int dests1isOk = true; + int nodeToStopDestsVar1 = destsVar1LastNode + 1; + CVector avgOfColPoints2 = (colCornerPos[0] + colCornerPos[1] + colCornerPos[2] + colCornerPos[3]) / 4.f; + + CVector prevDestVar1 = destsVariant1[0]; + + for (int i = 1; i < destsVar1LastNode + 1; i++) { + CVector *curDestVar1 = &destsVariant1[i]; + + CVector routeNormalHalf = *curDestVar1 - prevDestVar1; + routeNormalHalf.z = 0.f; + routeNormalHalf.Normalise(); + routeNormalHalf *= 0.5f; + + float oldX = -routeNormalHalf.x; + routeNormalHalf.z = 0.0f; + routeNormalHalf.x = routeNormalHalf.y; + routeNormalHalf.y = oldX; + + if (DotProduct(*curDestVar1 - avgOfColPoints2, routeNormalHalf) < 0.0f) + routeNormalHalf *= -1.f; + + CColPoint foundCol; + bool foundObstacle = CWorld::ProcessLineOfSight(prevDestVar1, *curDestVar1, foundCol, foundEnt1, + true, true, true, true, false, false, false, false); + + if (!foundObstacle) + foundObstacle = CWorld::ProcessLineOfSight(prevDestVar1 + routeNormalHalf, *curDestVar1 + routeNormalHalf, foundCol, foundEnt1, true, true, true, true, false, false, false, false); + + if (foundObstacle) { + if (foundEnt1 == m_followPathWalkAroundEnt || foundEnt1 == this || foundEnt1 == m_pSeekTarget) { + foundEnt1 = nil; + + } else { + if (!foundEnt1->IsPed()) { + dests1isOk = false; + nodeToStopDestsVar1 = i; + break; + } + if (((CPed*)foundEnt1)->m_nPedState == PED_IDLE) { + dests1isOk = false; + nodeToStopDestsVar1 = i; + break; + } + if (DotProduct(*curDestVar1 - prevDestVar1, foundEnt1->GetForward()) < 0.f) { + dests1isOk = false; + nodeToStopDestsVar1 = i; + break; + } + if (((CPed*)foundEnt1)->m_pedInObjective == this) { + dests1isOk = false; + nodeToStopDestsVar1 = i; + break; + } + } + } + prevDestVar1 = *curDestVar1; + } + CEntity *foundEnt2 = nil; + int dests2isOk = true; + int nodeToStopDestsVar2 = destsVar2LastNode + 1; + + CVector prevDestVar2 = destsVariant2[0]; + + for (int i = 1; i < destsVar2LastNode + 1; i++) { + CVector *curDestVar2 = &destsVariant2[i]; + + CVector routeNormalHalf = *curDestVar2 - prevDestVar2; + routeNormalHalf.z = 0.f; + routeNormalHalf.Normalise(); + routeNormalHalf *= 0.5f; + + float oldX = -routeNormalHalf.x; + routeNormalHalf.z = 0.0f; + routeNormalHalf.x = routeNormalHalf.y; + routeNormalHalf.y = oldX; + + if (DotProduct(*curDestVar2 - avgOfColPoints2, routeNormalHalf) < 0.0f) + routeNormalHalf *= -1.f; + + CColPoint foundCol; + bool foundObstacle = CWorld::ProcessLineOfSight(prevDestVar2, *curDestVar2, foundCol, foundEnt2, + true, true, true, true, false, false, false, false); + + if (!foundObstacle) + foundObstacle = CWorld::ProcessLineOfSight(prevDestVar2 + routeNormalHalf, *curDestVar2 + routeNormalHalf, foundCol, foundEnt2, true, true, true, true, false, false, false, false); + + if (foundObstacle) { + if (foundEnt2 == m_followPathWalkAroundEnt || foundEnt2 == this || foundEnt2 == m_pSeekTarget) { + foundEnt2 = 0; + } else { + if (!foundEnt2->IsPed()) { + dests2isOk = false; + nodeToStopDestsVar2 = i; + break; + } + if (((CPed*)foundEnt2)->m_nPedState == PED_IDLE) { + dests2isOk = false; + nodeToStopDestsVar2 = i; + break; + } + if (DotProduct(*curDestVar2 - prevDestVar2, foundEnt2->GetForward()) < 0.f) { + dests2isOk = false; + nodeToStopDestsVar2 = i; + break; + } + if (((CPed*)foundEnt2)->m_pedInObjective == this) { + dests2isOk = false; + nodeToStopDestsVar2 = i; + break; + } + } + } + prevDestVar2 = *curDestVar2; + } + + float destTotalLengthVar1 = 0.0f; + for(int i=0; i < destsVar1LastNode; i++){ + destTotalLengthVar1 += (destsVariant1[i + 1] - destsVariant1[i]).Magnitude(); + } + + float destTotalLengthVar2 = 0.0f; + for (int i = 0; i < destsVar2LastNode; i++) { + destTotalLengthVar2 += (destsVariant2[i + 1] - destsVariant2[i]).Magnitude(); + } + + int destVariantToUse; + if (dests1isOk && dests2isOk) { + if (destTotalLengthVar1 < destTotalLengthVar2) + destVariantToUse = 1; + else + destVariantToUse = 2; + + } else if (dests1isOk) { + destVariantToUse = 1; + + } else if (dests2isOk) { + destVariantToUse = 2; + + } else if (nodeToStopDestsVar1 == 1 && nodeToStopDestsVar2 > 1) { + destVariantToUse = 2; + + } else if (nodeToStopDestsVar1 > 1 && nodeToStopDestsVar2 == 1) { + destVariantToUse = 1; + + } else if (foundEnt1 == foundEnt2) { + if (destTotalLengthVar1 < destTotalLengthVar2) + destVariantToUse = 1; + else + destVariantToUse = 2; + + } else if (foundEnt1->GetColModel()->boundingSphere.radius >= foundEnt2->GetColModel()->boundingSphere.radius) { + destVariantToUse = 2; + } else { + destVariantToUse = 1; + } + + if (destVariantToUse == 1) { + ClearFollowPath(); + for (int i = 1; i < destsVar1LastNode; i++) { + CPathNode* nextNode = &m_pathNodeObjPool[m_nNumPathNodes]; + nextNode->SetPosition(destsVariant1[i]); + m_pathNodesToGo[m_nNumPathNodes++] = nextNode; + } + } else if (destVariantToUse == 2) { + ClearFollowPath(); + for (int i = 1; i < destsVar2LastNode; i++) { + CPathNode *nextNode = &m_pathNodeObjPool[m_nNumPathNodes]; + nextNode->SetPosition(destsVariant2[i]); + m_pathNodesToGo[m_nNumPathNodes++] = nextNode; + } + } + if (m_nNumPathNodes != 0) { + PedState oldLastState = m_nLastPedState; + m_nLastPedState = PED_NONE; + SetStoredState(); + if (m_nLastPedState == PED_NONE) + m_nLastPedState = oldLastState; + + SetPedState(PED_FOLLOW_PATH); + SetMoveState(m_followPathMoveState); + return true; + + } else { + m_pathNodeTimer = 0; + if (m_nPedState == PED_FOLLOW_PATH) + RestorePreviousState(); + + return false; + } + } else { + m_pathNodeTimer = 0; + if (m_nPedState == PED_FOLLOW_PATH) + RestorePreviousState(); + + return false; + } +} + +void +CPed::ClearFollowPath() +{ + for (int i = 0; i < ARRAY_SIZE(m_pathNodesToGo); i++) { + m_pathNodesToGo[i] = nil; + } + m_nNumPathNodes = 0; + m_nCurPathNodeId = 0; +} + +void +CPed::FollowPath(void) +{ + m_pCurPathNode = m_pathNodesToGo[m_nCurPathNodeId]; + if (m_pathNodeTimer > 0 && CTimer::GetTimeInMilliseconds() > m_pathNodeTimer) { + RestorePreviousState(); + ClearFollowPath(); + m_pathNodeTimer = 0; + } else { + if (m_pathNodesToGo[m_nCurPathNodeId]) { + m_vecSeekPos.x = m_pathNodesToGo[m_nCurPathNodeId]->GetPosition().x; + m_vecSeekPos.y = m_pathNodesToGo[m_nCurPathNodeId]->GetPosition().y; + m_vecSeekPos.z = GetPosition().z; + + if (Seek()) { + if (m_nCurPathNodeId == m_nNumPathNodes) { + RestorePreviousState(); + ClearFollowPath(); + SetFollowPath(m_followPathDestPos, m_followPathAbortDist, m_followPathMoveState, m_followPathWalkAroundEnt, + m_followPathTargetEnt, m_pathNodeTimer - CTimer::GetTimeInMilliseconds()); + } + } + } else { + RestorePreviousState(); + ClearFollowPath(); + m_pathNodeTimer = 0; + } + } +} + +void +CPed::SetEvasiveStep(CPhysical *reason, uint8 animType) +{ + AnimationId stepAnim; + + if (m_nPedState == PED_STEP_AWAY || !IsPedInControl() || ((IsPlayer() || !bRespondsToThreats) && animType == 0)) + return; + + float angleToFace = CGeneral::GetRadianAngleBetweenPoints( + reason->GetPosition().x, reason->GetPosition().y, + GetPosition().x, GetPosition().y); + angleToFace = CGeneral::LimitRadianAngle(angleToFace); + m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); + float neededTurn = Abs(angleToFace - m_fRotationCur); + bool vehPressedHorn = false; + + if (neededTurn > PI) + neededTurn = TWOPI - neededTurn; + + if (reason->IsVehicle() && ((CVehicle*)reason)->IsCar()) { + CVehicle *veh = (CVehicle*)reason; + if (veh->m_nCarHornTimer != 0) { + vehPressedHorn = true; + if (!IsPlayer()) + animType = 1; + } + } + + if (neededTurn <= DEGTORAD(90.0f) || reason->GetModelIndex() == MI_RCBANDIT || vehPressedHorn || animType != 0) { + SetLookFlag(reason, true); + if ((CGeneral::GetRandomNumber() & 1) && reason->GetModelIndex() != MI_RCBANDIT && animType == 0) { + stepAnim = ANIM_STD_HAILTAXI; + + } else { + float dangerDirection = CGeneral::GetRadianAngleBetweenPoints( + reason->m_vecMoveSpeed.x, reason->m_vecMoveSpeed.y, + 0.0f, 0.0f); + + // Let's turn our back to the "reason" + angleToFace += PI; + + if (angleToFace > PI) + angleToFace -= TWOPI; + + // We don't want to run towards car's direction + float dangerZone = angleToFace - dangerDirection; + dangerZone = CGeneral::LimitRadianAngle(dangerZone); + + // So, add or subtract 90deg (jump to left/right) according to that + if (dangerZone > 0.0f) + angleToFace = dangerDirection - HALFPI; + else + angleToFace = dangerDirection + HALFPI; + + stepAnim = ANIM_STD_NUM; + if (animType == 0 || animType == 1) + stepAnim = ANIM_STD_EVADE_STEP; + else if (animType == 2) + stepAnim = ANIM_STD_HANDSCOWER; + } + if (!RpAnimBlendClumpGetAssociation(GetClump(), stepAnim)) { + CAnimBlendAssociation *stepAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, stepAnim, 8.0f); + stepAssoc->flags &= ~ASSOC_DELETEFADEDOUT; + stepAssoc->SetFinishCallback(PedEvadeCB, this); + + if (animType == 0) + Say(SOUND_PED_EVADE); + + m_fRotationCur = CGeneral::LimitRadianAngle(angleToFace); + ClearAimFlag(); + SetStoredState(); + SetPedState(PED_STEP_AWAY); + } + } +} + +void +CPed::SetEvasiveDive(CPhysical *reason, uint8 onlyRandomJump) +{ + if (!IsPedInControl() || !bRespondsToThreats) + return; + + CAnimBlendAssociation *animAssoc; + float angleToFace, neededTurn; + bool handsUp = false; + + angleToFace = m_fRotationCur; + CVehicle *veh = (CVehicle*) reason; + if (reason->IsVehicle() && veh->m_vehType == VEHICLE_TYPE_CAR && veh->m_nCarHornTimer != 0 && !IsPlayer()) { + onlyRandomJump = true; + } + + if (onlyRandomJump) { + if (reason) { + // Simple version of my bug fix below. Doesn't calculate "danger zone", selects jump direction randomly. + // Also doesn't include random hands up, sound etc. Only used on player ped and peds running from gun shots. + + float vehDirection = CGeneral::GetRadianAngleBetweenPoints( + veh->m_vecMoveSpeed.x, veh->m_vecMoveSpeed.y, + 0.0f, 0.0f); + angleToFace = (CGeneral::GetRandomNumber() & 1) * PI + (-0.5f*PI) + vehDirection; + angleToFace = CGeneral::LimitRadianAngle(angleToFace); + } + } else { + if (IsPlayer()) { + ((CPlayerPed*)this)->m_nEvadeAmount = 5; + ((CPlayerPed*)this)->m_pEvadingFrom = reason; + reason->RegisterReference((CEntity**) &((CPlayerPed*)this)->m_pEvadingFrom); + return; + } + + angleToFace = CGeneral::GetRadianAngleBetweenPoints( + reason->GetPosition().x, reason->GetPosition().y, + GetPosition().x, GetPosition().y); + angleToFace = CGeneral::LimitRadianAngle(angleToFace); + m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); + + // FIX: Peds no more select dive direction randomly. Taken from SetEvasiveStep, last if statement inverted +#ifdef FIX_BUGS + float vehDirection = CGeneral::GetRadianAngleBetweenPoints( + veh->m_vecMoveSpeed.x, veh->m_vecMoveSpeed.y, + 0.0f, 0.0f); + + // Let's turn our back to the "reason" + angleToFace += PI; + + if (angleToFace > PI) + angleToFace -= 2 * PI; + + // We don't want to dive towards car's direction + float dangerZone = angleToFace - vehDirection; + dangerZone = CGeneral::LimitRadianAngle(dangerZone); + + // So, add or subtract 90deg (jump to left/right) according to that + if (dangerZone > 0.0f) + angleToFace = 0.5f * PI + vehDirection; + else + angleToFace = vehDirection - 0.5f * PI; +#endif + + neededTurn = Abs(angleToFace - m_fRotationCur); + + if (neededTurn > PI) + neededTurn = 2 * PI - neededTurn; + + if (neededTurn <= 0.5f*PI) { + if (CGeneral::GetRandomNumber() & 1) + handsUp = true; + } else { + if (CGeneral::GetRandomNumber() & 7) + return; + } + + // VC's primitve solution to dive direction problem, see above for better one. This part doesn't exist on III at all +#ifndef FIX_BUGS + angleToFace += HALFPI; + if (CGeneral::GetRandomNumber() & 1) + angleToFace -= PI; +#endif + Say(SOUND_PED_EVADE); + } + + if (handsUp || !IsPlayer() && m_pedStats->m_flags & STAT_NO_DIVE) { + m_fRotationCur = angleToFace; + ClearLookFlag(); + ClearAimFlag(); + SetLookFlag(reason, true); + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_HANDSUP); + if (animAssoc) + return; + + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HANDSUP, 8.0f); + animAssoc->flags &= ~ASSOC_DELETEFADEDOUT; + animAssoc->SetFinishCallback(PedEvadeCB, this); + SetStoredState(); + SetPedState(PED_STEP_AWAY); + } else { + m_fRotationCur = angleToFace; + ClearLookFlag(); + ClearAimFlag(); + SetStoredState(); + SetPedState(PED_DIVE_AWAY); + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_EVADE_DIVE, 8.0f); + animAssoc->SetFinishCallback(PedEvadeCB, this); + } + + if (reason->IsVehicle() && m_nPedType == PEDTYPE_COP) { + if (veh->pDriver && veh->pDriver->IsPlayer()) { + CWanted *wanted = FindPlayerPed()->m_pWanted; + wanted->RegisterCrime_Immediately(CRIME_RECKLESS_DRIVING, GetPosition(), (uintptr)this, false); + wanted->RegisterCrime_Immediately(CRIME_SPEEDING, GetPosition(), (uintptr)this, false); + } + } +} + +void +CPed::PedEvadeCB(CAnimBlendAssociation* animAssoc, void* arg) +{ + CPed* ped = (CPed*)arg; + + if (!animAssoc) { + ped->ClearLookFlag(); + if (ped->m_nPedState == PED_DIVE_AWAY || ped->m_nPedState == PED_STEP_AWAY) + ped->RestorePreviousState(); + + } else if (animAssoc->animId == ANIM_STD_EVADE_DIVE) { + ped->bUpdateAnimHeading = true; + ped->ClearLookFlag(); + if (ped->m_nPedState == PED_DIVE_AWAY) { + ped->m_getUpTimer = CTimer::GetTimeInMilliseconds() + 1; + ped->SetPedState(PED_FALL); + } + animAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + + } else if (animAssoc->flags & ASSOC_FADEOUTWHENDONE) { + ped->ClearLookFlag(); + if (ped->m_nPedState == PED_DIVE_AWAY || ped->m_nPedState == PED_STEP_AWAY) + ped->RestorePreviousState(); + + } else if (ped->m_nPedState != PED_ARRESTED) { + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + if (animAssoc->blendDelta >= 0.0f) + animAssoc->blendDelta = -4.0f; + + ped->ClearLookFlag(); + if (ped->m_nPedState == PED_DIVE_AWAY || ped->m_nPedState == PED_STEP_AWAY) { + ped->RestorePreviousState(); + } + } +} + +void +CPed::SetDie(AnimationId animId, float delta, float speed) +{ + if (m_attractor) + GetPedAttractorManager()->DeRegisterPed(this, m_attractor); + CPlayerPed *player = FindPlayerPed(); + if (player == this) { + if (!player->m_bCanBeDamaged) + return; + } + + m_threatEntity = nil; + if (DyingOrDead()) + return; + + CAnimBlendAssociation *dieAssoc = nil; + if (m_nPedState == PED_FALL || m_nPedState == PED_GETUP) + delta *= 0.5f; + + SetStoredState(); + ClearAll(); + m_fHealth = 0.0f; + if (m_nPedState == PED_DRIVING) { + if (!IsPlayer() && (!m_pMyVehicle || !m_pMyVehicle->IsBike())) + FlagToDestroyWhenNextProcessed(); + } else if (bInVehicle) { + if (m_pVehicleAnim) + m_pVehicleAnim->blendDelta = -1000.0f; + } else if (EnteringCar()) { + QuitEnteringCar(); + } + + SetPedState(PED_DIE); + if (animId == ANIM_STD_NUM) { + bIsPedDieAnimPlaying = false; + } else { + dieAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, animId, delta); + if (speed > 0.0f) + dieAssoc->speed = speed; + + dieAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; + if (dieAssoc->IsRunning()) { + dieAssoc->SetFinishCallback(FinishDieAnimCB, this); + bIsPedDieAnimPlaying = true; + } + } + + Say(SOUND_PED_DEATH); + if (m_nLastPedState == PED_ENTER_CAR || m_nLastPedState == PED_CARJACK) + QuitEnteringCar(); + + if (!bInVehicle) + StopNonPartialAnims(); + + m_bloodyFootprintCountOrDeathTime = CTimer::GetTimeInMilliseconds(); + if (!CGame::nastyGame && animId == ANIM_STD_HIT_FLOOR) { + if (dieAssoc) { + dieAssoc->SetCurrentTime(dieAssoc->hierarchy->totalLength - 0.01f); + dieAssoc->SetRun(); + } + } +} + +void +CPed::FinishDieAnimCB(CAnimBlendAssociation *animAssoc, void *arg) +{ + CPed *ped = (CPed*)arg; + + if (ped->bIsPedDieAnimPlaying) + ped->bIsPedDieAnimPlaying = false; +} + +void +CPed::SetDead(void) +{ + if (!RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DROWN)) + bUsesCollision = false; + + m_fHealth = 0.0f; + if (m_nPedState == PED_DRIVING) + bIsVisible = false; + + SetPedState(PED_DEAD); + m_pVehicleAnim = nil; + m_pCollidingEntity = nil; + + CWeaponInfo *weapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + RemoveWeaponModel(weapon->m_nModelId); + + m_currentWeapon = WEAPONTYPE_UNARMED; + CEventList::RegisterEvent(EVENT_INJURED_PED, EVENT_ENTITY_PED, this, nil, 250); + if (this != FindPlayerPed()) { + RemoveWeaponAnims(0, -1000.0f); + CreateDeadPedWeaponPickups(); + CreateDeadPedMoney(); + } + + m_bloodyFootprintCountOrDeathTime = CTimer::GetTimeInMilliseconds(); + m_deadBleeding = false; + bDoBloodyFootprints = false; + bVehExitWillBeInstant = false; + CEventList::RegisterEvent(EVENT_DEAD_PED, EVENT_ENTITY_PED, this, nil, 1000); +} + +void +CPed::Die(void) +{ + // UNUSED: This is a perfectly empty function. +} + +void +CPed::SetChat(CEntity *chatWith, uint32 time) +{ + if (m_nPedState != PED_CHAT) { + m_nLastPedState = PED_NONE; + SetStoredState(); + } + + SetPedState(PED_CHAT); + SetMoveState(PEDMOVE_STILL); + m_lookTimer = 0; + SetLookFlag(chatWith, true); + m_chatTimer = CTimer::GetTimeInMilliseconds() + time; + m_lookTimer = CTimer::GetTimeInMilliseconds() + 3000; +} + +void +CPed::Chat(void) +{ + // We're already looking to our partner + if (bIsLooking && TurnBody()) + ClearLookFlag(); + + if (!m_pLookTarget || !m_pLookTarget->IsPed()) { + ClearChat(); + return; + } + + CPed *partner = (CPed*) m_pLookTarget; + + if (partner->m_nPedState != PED_CHAT) { + ClearChat(); + m_chatTimer = CTimer::GetTimeInMilliseconds() + 30000; + if (partner->m_pedInObjective) { + if (partner->m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || + partner->m_objective == OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE) + ReactToAttack(partner->m_pedInObjective); + } + return; + } + if (bIsTalking) { + if (CGeneral::GetRandomNumber() < 512) { + CAnimBlendAssociation *chatAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CHAT); + if (chatAssoc) { + chatAssoc->blendDelta = -4.0f; + chatAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + bIsTalking = false; + } else + Say(SOUND_PED_CHAT); + + } else { + + if (CGeneral::GetRandomNumber() < 20 && !RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_IDLE)) { + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_XPRESS_SCRATCH, 4.0f); + } + if (!bIsTalking && !RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_IDLE)) { + CAnimBlendAssociation *chatAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CHAT, 4.0f); + float chatTime = CGeneral::GetRandomNumberInRange(0.0f, 3.0f); + chatAssoc->SetCurrentTime(chatTime); + + bIsTalking = true; + Say(SOUND_PED_CHAT); + } + } + if (m_chatTimer && CTimer::GetTimeInMilliseconds() > m_chatTimer) { + ClearChat(); + m_chatTimer = CTimer::GetTimeInMilliseconds() + 30000; + } +} + +void +CPed::ClearChat(void) +{ + CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CHAT); + if (animAssoc) { + animAssoc->blendDelta = -8.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + bIsTalking = false; + ClearLookFlag(); + RestorePreviousState(); + if (m_objective == OBJECTIVE_BUY_ICE_CREAM) { + bBoughtIceCream = true; + SetObjective(OBJECTIVE_NONE); + SetWanderPath(CGeneral::GetRandomNumberInRange(0, 8)); + } +} + +bool +CPed::FacePhone(void) +{ + // FIX: This function was broken since it's left unused early in development. +#ifdef FIX_BUGS + float phoneDir = CGeneral::GetRadianAngleBetweenPoints( + gPhoneInfo.m_aPhones[m_phoneId].m_vecPos.x, gPhoneInfo.m_aPhones[m_phoneId].m_vecPos.y, + GetPosition().x, GetPosition().y); + + SetLookFlag(phoneDir, false); + bool turnDone = TurnBody(); + if (turnDone) { + SetIdle(); + ClearLookFlag(); + m_phoneTalkTimer = CTimer::GetTimeInMilliseconds() + 10000; + } + return turnDone; +#else + float currentRot = RADTODEG(m_fRotationCur); + float phoneDir = CGeneral::GetRadianAngleBetweenPoints( + gPhoneInfo.m_aPhones[m_phoneId].m_vecPos.x, + gPhoneInfo.m_aPhones[m_phoneId].m_vecPos.y, + GetPosition().x, + GetPosition().y); + + SetLookFlag(phoneDir, false); + phoneDir = CGeneral::LimitAngle(phoneDir); + m_moved = CVector2D(0.0f, 0.0f); + + if (currentRot - 180.0f > phoneDir) + phoneDir += 2 * 180.0f; + else if (180.0f + currentRot < phoneDir) + phoneDir -= 2 * 180.0f; + + float neededTurn = currentRot - phoneDir; + + if (Abs(neededTurn) <= 0.75f) { + SetIdle(); + ClearLookFlag(); + m_phoneTalkTimer = CTimer::GetTimeInMilliseconds() + 10000; + return true; + } else { + m_fRotationCur = DEGTORAD(currentRot - neededTurn * 0.2f); + return false; + } +#endif +} + +bool +CPed::MakePhonecall(void) +{ + if (CTimer::GetTimeInMilliseconds() <= m_phoneTalkTimer) + return false; + + SetIdle(); + gPhoneInfo.m_aPhones[m_phoneId].m_nState = PHONE_STATE_FREE; + m_phoneId = -1; + return true; +} + +void +StartTalkingOnMobileCB(CAnimBlendAssociation* assoc, void* arg) +{ + CPed* ped = (CPed*)arg; + if (ped->m_nPedState == PED_ANSWER_MOBILE) + CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_PHONE_TALK, 4.0f); +} + +void +FinishTalkingOnMobileCB(CAnimBlendAssociation *assoc, void *arg) +{ + CPed *ped = (CPed*)arg; + if (ped->m_storedWeapon != WEAPONTYPE_UNIDENTIFIED) { + ped->RemoveWeaponModel(MI_MOBILE); + ped->SetCurrentWeapon(ped->m_storedWeapon); + ped->m_storedWeapon = WEAPONTYPE_UNIDENTIFIED; + } + ped->m_lookTimer = 0; +} + +void +CPed::SetAnswerMobile(void) +{ + if (m_nPedState != PED_ANSWER_MOBILE && !DyingOrDead()) { + SetPedState(PED_ANSWER_MOBILE); +#ifdef FIX_BUGS + ClearLookFlag(); +#endif + RemoveWeaponAnims(GetWeapon()->m_eWeaponType, -4.0f); + CAnimBlendAssociation *assoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_PHONE_IN, 4.0f); + assoc->SetFinishCallback(StartTalkingOnMobileCB, this); + m_lookTimer = INT32_MAX; + if (m_storedWeapon == WEAPONTYPE_UNIDENTIFIED) + m_storedWeapon = GetWeapon()->m_eWeaponType; + +#ifdef FIX_BUGS + SetCurrentWeapon(0); +#endif + RemoveWeaponModel(-1); + } +} + +void +CPed::ClearAnswerMobile(void) +{ + if (m_nLastPedState == PED_ANSWER_MOBILE) + m_nLastPedState = PED_NONE; + + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_PHONE_TALK)) { + CAnimBlendAssociation *assoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_PHONE_OUT, 8.0f); + assoc->SetFinishCallback(FinishTalkingOnMobileCB, this); + } else + FinishTalkingOnMobileCB(nil, this); + + if (m_nPedState == PED_ANSWER_MOBILE) { + m_nPedState = PED_IDLE; + RestorePreviousState(); + m_pVehicleAnim = nil; + } +} + +void +CPed::AnswerMobile(void) +{ + if (!IsPedInControl()) + return; + + CAnimBlendAssociation *phoneInAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_PHONE_IN); + CAnimBlendAssociation *phoneOutAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_PHONE_OUT); + CAnimBlendAssociation *phoneTalkAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_PHONE_TALK); + if (phoneInAssoc || phoneTalkAssoc || phoneOutAssoc) { + if (phoneInAssoc) { + if (phoneInAssoc->currentTime >= 0.85f && !m_pWeaponModel) { + CBaseModelInfo *phoneModel = CModelInfo::GetModelInfo(MI_MOBILE); + m_pWeaponModel = (RpAtomic*)phoneModel->CreateInstance(); + phoneModel->AddRef(); + m_wepModelID = MI_MOBILE; + + // They copied AddWeaponModel and forgot that here + // bool unused = IsPlayer(); + } + } else if (phoneOutAssoc) { + if (phoneOutAssoc->currentTime >= 0.5f && phoneOutAssoc->currentTime - phoneOutAssoc->timeStep < 0.5f) { + RemoveWeaponModel(MI_MOBILE); + SetCurrentWeapon(m_storedWeapon); + m_storedWeapon = WEAPONTYPE_UNIDENTIFIED; + } + } + } else { + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_PHONE_TALK, 4.0f); + } +} + +void +CPed::Teleport(CVector pos) +{ + CWorld::Remove(this); + SetPosition(pos); + bIsStanding = false; + m_nPedStateTimer = 0; + m_actionX = 0.0f; + m_actionY = 0.0f; + m_pDamageEntity = nil; + CWorld::Add(this); +} + +void +CPed::SetSeekCar(CVehicle *car, uint32 doorNode) +{ + if (m_nPedState == PED_SEEK_CAR) + return; + + if (!CanSetPedState() || m_nPedState == PED_DRIVING) + return; + + SetStoredState(); + m_pSeekTarget = car; + m_pSeekTarget->RegisterReference((CEntity**) &m_pSeekTarget); + m_carInObjective = car; + m_carInObjective->RegisterReference((CEntity**) &m_carInObjective); + m_pMyVehicle = car; + m_pMyVehicle->RegisterReference((CEntity**) &m_pMyVehicle); + // m_pSeekTarget->RegisterReference((CEntity**) &m_pSeekTarget); + m_vehDoor = doorNode; + m_distanceToCountSeekDone = 0.5f; + SetPedState(PED_SEEK_CAR); + +} + +void +CPed::SeekCar(void) +{ + CVehicle *vehToSeek = m_carInObjective; + CVector dest(0.0f, 0.0f, 0.0f); + if (!vehToSeek) { + RestorePreviousState(); + return; + } + + if (m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER) { + if (!vehToSeek->IsBike() && m_vehDoor && m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER) { + if (IsRoomToBeCarJacked()) { + dest = GetPositionToOpenCarDoor(vehToSeek, m_vehDoor); + } else if (m_nPedType == PEDTYPE_COP) { + dest = GetPositionToOpenCarDoor(vehToSeek, CAR_DOOR_RF); + } else { + SetMoveState(PEDMOVE_STILL); + } + } else + GetNearestDoor(vehToSeek, dest); + } else { + if (m_carJackTimer > CTimer::GetTimeInMilliseconds()) { + SetMoveState(PEDMOVE_STILL); + return; + } + if (vehToSeek->GetModelIndex() == MI_COACH) { + GetNearestDoor(vehToSeek, dest); + } else { + if (vehToSeek->IsTrain()) { + if (vehToSeek->GetStatus() != STATUS_TRAIN_NOT_MOVING) { + RestorePreviousObjective(); + RestorePreviousState(); + return; + } + if (!GetNearestTrainDoor(vehToSeek, dest)) { + RestorePreviousObjective(); + RestorePreviousState(); + return; + } + } else { + if (!GetNearestPassengerDoor(vehToSeek, dest)) { + if (vehToSeek->m_nNumPassengers == vehToSeek->m_nNumMaxPassengers) { + RestorePreviousObjective(); + RestorePreviousState(); + } else { + SetMoveState(PEDMOVE_STILL); + } + bVehEnterDoorIsBlocked = true; + return; + } + bVehEnterDoorIsBlocked = false; + } + } + } + + if (dest.x == 0.0f && dest.y == 0.0f) { +#ifdef FIX_BUGS + if ((!IsPlayer() && CharCreatedBy != MISSION_CHAR) || vehToSeek->VehicleCreatedBy != MISSION_VEHICLE || vehToSeek->pDriver || !vehToSeek->CanPedOpenLocks(this)) { +#else + if ((!IsPlayer() && CharCreatedBy != MISSION_CHAR) || vehToSeek->VehicleCreatedBy != MISSION_VEHICLE || vehToSeek->pDriver) { +#endif + RestorePreviousState(); + if (IsPlayer()) { + ClearObjective(); + } else if (CharCreatedBy == RANDOM_CHAR) { + m_carJackTimer = CTimer::GetTimeInMilliseconds() + 30000; + } + SetMoveState(PEDMOVE_STILL); + TheCamera.ClearPlayerWeaponMode(); + CCarCtrl::RemoveFromInterestingVehicleList(vehToSeek); + return; + } + dest = vehToSeek->GetPosition(); + if (bCollidedWithMyVehicle) { + WarpPedIntoCar(m_pMyVehicle); + return; + } + } + bool foundBetterPosToSeek = PossiblyFindBetterPosToSeekCar(&dest, vehToSeek); + m_vecSeekPos = dest; + float distToDestSqr = (m_vecSeekPos - GetPosition()).MagnitudeSqr(); + + if (bIsRunning || + vehToSeek->pDriver && distToDestSqr > sq(2.0f) && (Abs(vehToSeek->m_vecMoveSpeed.x) > 0.01f || Abs(vehToSeek->m_vecMoveSpeed.y) > 0.01f)) + SetMoveState(PEDMOVE_RUN); + else if (distToDestSqr < sq(2.0f)) + SetMoveState(PEDMOVE_WALK); + + if (distToDestSqr >= 1.0f) + bCanPedEnterSeekedCar = false; + else if (2.0f * vehToSeek->GetColModel()->boundingBox.max.x > distToDestSqr) + bCanPedEnterSeekedCar = true; + + if (vehToSeek->m_nGettingInFlags & GetCarDoorFlag(m_vehDoor)) + bVehEnterDoorIsBlocked = true; + else + bVehEnterDoorIsBlocked = false; + + // Arrived to the car + if (Seek()) { + if (!foundBetterPosToSeek) { + if (1.6f + GetPosition().z > dest.z && GetPosition().z - 0.5f < dest.z) { +#ifdef GTA_TRAIN + if (vehToSeek->IsTrain()) { + SetEnterTrain(vehToSeek, m_vehDoor); + } else +#endif + { + m_fRotationCur = m_fRotationDest; + if (!bVehEnterDoorIsBlocked) { + vehToSeek->SetIsStatic(false); + if (m_objective == OBJECTIVE_SOLICIT_VEHICLE) { + SetSolicit(1000); + } else if (m_objective == OBJECTIVE_BUY_ICE_CREAM) { + SetBuyIceCream(); + } else if (vehToSeek->m_nNumGettingIn < vehToSeek->m_nNumMaxPassengers + 1 + && vehToSeek->CanPedEnterCar()) { + + switch (vehToSeek->GetStatus()) { + case STATUS_PLAYER: + case STATUS_SIMPLE: + case STATUS_PHYSICS: + case STATUS_PLAYER_DISABLED: + if (vehToSeek->IsBike()) { + if ((!m_leader || m_leader != vehToSeek->pDriver) && + ((m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_RF || m_vehDoor == CAR_WINDSCREEN) && vehToSeek->pDriver || + (m_vehDoor == CAR_DOOR_LR || m_vehDoor == CAR_DOOR_RR) && vehToSeek->pPassengers[0])) { + SetCarJack(vehToSeek); + } else { + SetEnterCar(vehToSeek, m_vehDoor); + } + } else if (!vehToSeek->bIsBus && (!m_leader || m_leader != vehToSeek->pDriver) && + (m_vehDoor == CAR_DOOR_LF && vehToSeek->pDriver || m_vehDoor == CAR_DOOR_RF && vehToSeek->pPassengers[0] || m_vehDoor == CAR_DOOR_LR && vehToSeek->pPassengers[1] || m_vehDoor == CAR_DOOR_RR && vehToSeek->pPassengers[2])) { + SetCarJack(vehToSeek); + if (m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER && m_vehDoor != CAR_DOOR_LF) + vehToSeek->pDriver->bFleeAfterExitingCar = true; + } else { + SetEnterCar(vehToSeek, m_vehDoor); + } + break; + case STATUS_ABANDONED: + if (vehToSeek->IsBike()) { + if ((m_vehDoor == CAR_DOOR_LR || m_vehDoor == CAR_DOOR_RR) && vehToSeek->pPassengers[0]) { + if (vehToSeek->pPassengers[0]->bDontDragMeOutCar) { + if (IsPlayer()) + SetEnterCar(vehToSeek, m_vehDoor); + } else { + SetCarJack(vehToSeek); + } + } else { + SetEnterCar(vehToSeek, m_vehDoor); + } + } else if (m_vehDoor == CAR_DOOR_RF && vehToSeek->pPassengers[0]) { + if (vehToSeek->pPassengers[0]->bDontDragMeOutCar) { + if (IsPlayer()) + SetEnterCar(vehToSeek, m_vehDoor); + } else { + SetCarJack(vehToSeek); + } + } else { + SetEnterCar(vehToSeek, m_vehDoor); + } + break; + case STATUS_WRECKED: + SetIdle(); + break; + default: + return; + } + } else { + RestorePreviousState(); + } + } else { + SetMoveState(PEDMOVE_STILL); + } + } + } + } + } +} + +bool +CPed::CheckForExplosions(CVector2D &area) +{ + int32 event = 0; + if (CEventList::FindClosestEvent(EVENT_EXPLOSION, GetPosition(), &event)) { + area.x = gaEvent[event].posn.x; + area.y = gaEvent[event].posn.y; + CEntity *actualEntity = nil; + + switch (gaEvent[event].entityType) { + case EVENT_ENTITY_PED: + actualEntity = CPools::GetPed(gaEvent[event].entityRef); + break; + case EVENT_ENTITY_VEHICLE: + actualEntity = CPools::GetVehicle(gaEvent[event].entityRef); + break; + case EVENT_ENTITY_OBJECT: + actualEntity = CPools::GetObject(gaEvent[event].entityRef); + break; + default: + break; + } + + if (actualEntity) { + m_pEventEntity = actualEntity; + m_pEventEntity->RegisterReference((CEntity **) &m_pEventEntity); + bGonnaInvestigateEvent = true; + } else + bGonnaInvestigateEvent = false; + + CEventList::ClearEvent(event); + return true; + } else if (CEventList::FindClosestEvent(EVENT_FIRE, GetPosition(), &event)) { + area.x = gaEvent[event].posn.x; + area.y = gaEvent[event].posn.y; + CEventList::ClearEvent(event); + bGonnaInvestigateEvent = false; + return true; + } + + bGonnaInvestigateEvent = false; + return false; +} + +CPed * +CPed::CheckForGunShots(void) +{ + int32 event; + if (CEventList::FindClosestEvent(EVENT_GUNSHOT, GetPosition(), &event)) { + if (gaEvent[event].entityType == EVENT_ENTITY_PED) { + // Probably due to we don't want peds to go gunshot area? (same on VC) + bGonnaInvestigateEvent = false; + return CPools::GetPed(gaEvent[event].entityRef); + } + } + bGonnaInvestigateEvent = false; + return nil; +} + +CPed * +CPed::CheckForDeadPeds(void) +{ + int32 event; + if (CEventList::FindClosestEvent(EVENT_DEAD_PED, GetPosition(), &event)) { + int pedHandle = gaEvent[event].entityRef; + if (pedHandle && gaEvent[event].entityType == EVENT_ENTITY_PED) { + bGonnaInvestigateEvent = true; + return CPools::GetPed(pedHandle); + } + } + bGonnaInvestigateEvent = false; + return nil; +} + +bool +CPed::IsPlayer(void) const +{ +#if 0 + return m_nPedType == PEDTYPE_PLAYER1; // Original +#else + // We still have those in enum, so let's also check for them. + return m_nPedType == PEDTYPE_PLAYER1 || m_nPedType == PEDTYPE_PLAYER2 || + m_nPedType == PEDTYPE_PLAYER3 || m_nPedType == PEDTYPE_PLAYER4; +#endif +} + +bool +CPed::IsGangMember(void) const +{ + return m_nPedType >= PEDTYPE_GANG1 && m_nPedType <= PEDTYPE_GANG9; +} + +bool +IsPedPointerValid(CPed* pPed) +{ + if (!IsPedPointerValid_NotInWorld(pPed)) + return false; + if (pPed->bInVehicle && pPed->m_pMyVehicle) + return IsEntityPointerValid(pPed->m_pMyVehicle); + return pPed->m_entryInfoList.first || pPed == FindPlayerPed(); +} + +bool +IsPedPointerValid_NotInWorld(CPed* pPed) +{ + if (!pPed) + return false; + int index = CPools::GetPedPool()->GetJustIndex_NoFreeAssert(pPed); +#ifdef FIX_BUGS + if (index < 0 || index >= NUMPEDS) +#else + if (index < 0 || index > NUMPEDS) +#endif + return false; + return true; +} + +bool +CPed::IsPointerValid(void) +{ + int pedIndex = CPools::GetPedPool()->GetIndex(this) >> 8; + if (pedIndex < 0 || pedIndex >= NUMPEDS) + return false; + + if (m_entryInfoList.first || FindPlayerPed() == this) + return true; + + return false; +} + +void +CPed::SetPedPositionInCar(void) +{ + bool notYet = false; + if (CReplay::IsPlayingBack()) + return; + + if (bChangedSeat) { + if (m_pMyVehicle->IsBike()) { + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_JUMPON_LHS) + || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_JUMPON_RHS) + || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_KICK)) { + LineUpPedWithCar(LINE_UP_TO_CAR_START); + return; + } + bChangedSeat = false; + } else { + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_GET_IN_LHS) + || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_GET_IN_LO_LHS) + || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_CLOSE_DOOR_LHS) + || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_CLOSE_DOOR_LO_LHS) + || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_SHUFFLE_RHS) + || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_SHUFFLE_LO_RHS) + || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_VAN_CLOSE_DOOR_REAR_LHS) + || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_VAN_CLOSE_DOOR_REAR_RHS) + || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_VAN_GET_IN_REAR_LHS) + || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_VAN_GET_IN_REAR_RHS) + || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_COACH_GET_IN_LHS) + || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_COACH_GET_IN_RHS) + || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_JUMP_IN_LO_LHS)) { + notYet = true; + } + } + if (notYet) { + LineUpPedWithCar(LINE_UP_TO_CAR_START); + bChangedSeat = false; + return; + } + } + CVehicleModelInfo *vehModel = (CVehicleModelInfo *)CModelInfo::GetModelInfo(m_pMyVehicle->GetModelIndex()); + CMatrix newMat(m_pMyVehicle->GetMatrix()); + CVector seatPos; + if (m_pMyVehicle->pDriver == this) { + seatPos = vehModel->GetFrontSeatPosn(); + if (!m_pMyVehicle->IsBoat() && !m_pMyVehicle->IsBike()) + seatPos.x = -seatPos.x; + + } else if (m_pMyVehicle->pPassengers[0] == this) { + seatPos = m_pMyVehicle->IsBike() ? vehModel->m_positions[CAR_POS_BACKSEAT]: vehModel->GetFrontSeatPosn(); + + } else if (m_pMyVehicle->pPassengers[1] == this) { + seatPos = vehModel->m_positions[CAR_POS_BACKSEAT]; + seatPos.x = -seatPos.x; + + } else { + if (m_pMyVehicle->pPassengers[2] == this) { + seatPos = vehModel->m_positions[CAR_POS_BACKSEAT]; + } else { + seatPos = vehModel->GetFrontSeatPosn(); + } + } + if (m_pMyVehicle->IsBike()) { + ((CBike*)m_pMyVehicle)->CalculateLeanMatrix(); + newMat = ((CBike*)m_pMyVehicle)->m_leanMatrix; + } + newMat.GetPosition() += Multiply3x3(newMat, seatPos); + // Already done below (SetTranslate(0.0f, 0.0f, 0.0f)) + // tempMat.SetUnity(); + + // Rear seats on vans don't face to front, so rotate them HALFPI. + if (m_pMyVehicle->bIsVan) { + CMatrix tempMat; + if (m_pMyVehicle->pPassengers[1] == this) { + m_fRotationCur = m_pMyVehicle->GetForward().Heading() - HALFPI; + tempMat.SetTranslate(0.0f, 0.0f, 0.0f); + tempMat.RotateZ(-HALFPI); + tempMat.Translate(0.0f, 0.6f, 0.0f); + newMat = newMat * tempMat; + } else if (m_pMyVehicle->pPassengers[2] == this) { + m_fRotationCur = m_pMyVehicle->GetForward().Heading() + HALFPI; + tempMat.SetTranslate(0.0f, 0.0f, 0.0f); + tempMat.RotateZ(HALFPI); + newMat = newMat * tempMat; + } else { + m_fRotationCur = m_pMyVehicle->GetForward().Heading(); + } + } else { + m_fRotationCur = m_pMyVehicle->GetForward().Heading(); + } + SetMatrix(newMat); +} + +void +CPed::LookForSexyPeds(void) +{ + if ((!IsPedInControl() && m_nPedState != PED_DRIVING) + || m_lookTimer >= CTimer::GetTimeInMilliseconds() || +#ifdef FIX_BUGS + (m_nPedType != PEDTYPE_CIVMALE) && !IsFemale() && (m_nPedType != PEDTYPE_CRIMINAL) && !IsGangMember() +#else + m_nPedType != PEDTYPE_CIVMALE +#endif + ) + return; + + for (int i = 0; i < m_numNearPeds; i++) { + if (CanSeeEntity(m_nearPeds[i])) { + if ((GetPosition() - m_nearPeds[i]->GetPosition()).Magnitude() < 10.0f) { + CPed *nearPed = m_nearPeds[i]; + if((nearPed->m_pedStats->m_sexiness > m_pedStats->m_sexiness) +#ifdef FIX_BUGS + && ((IsFemale() && !nearPed->IsFemale()) || (!IsFemale() && nearPed->IsFemale()))) { +#else + && nearPed->m_nPedType == PEDTYPE_CIVFEMALE) { +#endif + + SetLookFlag(nearPed, true); + m_lookTimer = CTimer::GetTimeInMilliseconds() + 4000; +#ifdef FIX_BUGS + Say(IsFemale() ? SOUND_PED_CHAT_SEXY_FEMALE : SOUND_PED_CHAT_SEXY_MALE); +#else + Say(SOUND_PED_CHAT_SEXY_MALE); +#endif + return; + } + } + } + } + m_lookTimer = CTimer::GetTimeInMilliseconds() + 10000; +} + +void +CPed::LookForSexyCars(void) +{ + CEntity *vehicles[8]; + CVehicle *veh; + int foundVehId = 0; + int bestPriceYet = 0; + int16 lastVehicle; + + if (!IsPedInControl() && m_nPedState != PED_DRIVING) + return; + + if (m_lookTimer < CTimer::GetTimeInMilliseconds()) { + CWorld::FindObjectsInRange(GetPosition(), 10.0f, true, &lastVehicle, 6, vehicles, false, true, false, false, false); + + for (int vehId = 0; vehId < lastVehicle; vehId++) { + veh = (CVehicle*)vehicles[vehId]; + if (veh != m_pMyVehicle && bestPriceYet < veh->pHandling->nMonetaryValue) { + foundVehId = vehId; + bestPriceYet = veh->pHandling->nMonetaryValue; + } + } + if (lastVehicle > 0 && bestPriceYet > 40000) + SetLookFlag(vehicles[foundVehId], false); + + m_lookTimer = CTimer::GetTimeInMilliseconds() + 10000; + } +} + +bool +CPed::LookForInterestingNodes(void) +{ + CBaseModelInfo *model; + CPtrNode *ptrNode; + CVector effectDist; + C2dEffect *effect; + CMatrix *objMat; + + if ((CTimer::GetFrameCounter() + (m_randomSeed % 256)) & 7 || CTimer::GetTimeInMilliseconds() <= m_chatTimer) { + return false; + } + bool found = false; + uint8 randVal = CGeneral::GetRandomNumber() % 256; + + int minX = CWorld::GetSectorIndexX(GetPosition().x - CHECK_NEARBY_THINGS_MAX_DIST); + if (minX < 0) minX = 0; + int minY = CWorld::GetSectorIndexY(GetPosition().y - CHECK_NEARBY_THINGS_MAX_DIST); + if (minY < 0) minY = 0; + int maxX = CWorld::GetSectorIndexX(GetPosition().x + CHECK_NEARBY_THINGS_MAX_DIST); +#ifdef FIX_BUGS + if (maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X - 1; +#else + if (maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X; +#endif + + int maxY = CWorld::GetSectorIndexY(GetPosition().y + CHECK_NEARBY_THINGS_MAX_DIST); +#ifdef FIX_BUGS + if (maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y - 1; +#else + if (maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y; +#endif + + for (int curY = minY; curY <= maxY && !found; curY++) { + for (int curX = minX; curX <= maxX && !found; curX++) { + CSector *sector = CWorld::GetSector(curX, curY); + + for (ptrNode = sector->m_lists[ENTITYLIST_VEHICLES].first; ptrNode && !found; ptrNode = ptrNode->next) { + CVehicle *veh = (CVehicle*)ptrNode->item; + model = veh->GetModelInfo(); + if (model->GetNum2dEffects() != 0) { + for (int e = 0; e < model->GetNum2dEffects(); e++) { + effect = model->Get2dEffect(e); + if (effect->type == EFFECT_ATTRACTOR && effect->attractor.probability >= randVal) { + objMat = &veh->GetMatrix(); + CVector effectPos = veh->GetMatrix() * effect->pos; + effectDist = effectPos - GetPosition(); + if (effectDist.MagnitudeSqr() < sq(8.0f)) { + found = true; + break; + } + } + } + } + } + for (ptrNode = sector->m_lists[ENTITYLIST_OBJECTS].first; ptrNode && !found; ptrNode = ptrNode->next) { + CObject *obj = (CObject*)ptrNode->item; + model = CModelInfo::GetModelInfo(obj->GetModelIndex()); + if (model->GetNum2dEffects() != 0) { + for (int e = 0; e < model->GetNum2dEffects(); e++) { + effect = model->Get2dEffect(e); + if (effect->type == EFFECT_ATTRACTOR && effect->attractor.probability >= randVal) { + objMat = &obj->GetMatrix(); + CVector effectPos = obj->GetMatrix() * effect->pos; + effectDist = effectPos - GetPosition(); + if (effectDist.MagnitudeSqr() < sq(8.0f)) { + found = true; + break; + } + } + } + } + } + for (ptrNode = sector->m_lists[ENTITYLIST_BUILDINGS].first; ptrNode && !found; ptrNode = ptrNode->next) { + CBuilding *building = (CBuilding*)ptrNode->item; + model = CModelInfo::GetModelInfo(building->GetModelIndex()); + if (model->GetNum2dEffects() != 0) { + for (int e = 0; e < model->GetNum2dEffects(); e++) { + effect = model->Get2dEffect(e); + if (effect->type == EFFECT_ATTRACTOR && effect->attractor.probability >= randVal) { + objMat = &building->GetMatrix(); + CVector effectPos = building->GetMatrix() * effect->pos; + effectDist = effectPos - GetPosition(); + if (effectDist.MagnitudeSqr() < sq(8.0f)) { + found = true; + break; + } + } + } + } + } + for (ptrNode = sector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP].first; ptrNode && !found; ptrNode = ptrNode->next) { + CBuilding *building = (CBuilding*)ptrNode->item; + model = CModelInfo::GetModelInfo(building->GetModelIndex()); + if (model->GetNum2dEffects() != 0) { + for (int e = 0; e < model->GetNum2dEffects(); e++) { + effect = model->Get2dEffect(e); + if (effect->type == EFFECT_ATTRACTOR && effect->attractor.probability >= randVal) { + objMat = &building->GetMatrix(); + CVector effectPos = building->GetMatrix() * effect->pos; + effectDist = effectPos - GetPosition(); + if (effectDist.MagnitudeSqr() < sq(8.0f)) { + found = true; + break; + } + } + } + } + } + } + } + + if (!found) + return false; + + CVector effectFrontLocal = Multiply3x3(*objMat, effect->attractor.dir); + float angleToFace = CGeneral::GetRadianAngleBetweenPoints(effectFrontLocal.x, effectFrontLocal.y, 0.0f, 0.0f); + randVal = CGeneral::GetRandomNumber() % 256; + if (randVal <= m_randomSeed % 256) { + m_chatTimer = CTimer::GetTimeInMilliseconds() + 2000; + SetLookFlag(angleToFace, true); + SetLookTimer(1000); + return false; + } + + CVector2D effectPos = *objMat * effect->pos; + switch (effect->attractor.type) { + case ATTRACTORTYPE_ICECREAM: + SetInvestigateEvent(EVENT_ICECREAM, effectPos, 0.1f, 15000, angleToFace); + break; + case ATTRACTORTYPE_STARE: + SetInvestigateEvent(EVENT_SHOPSTALL, effectPos, 1.0f, + CGeneral::GetRandomNumberInRange(8000, 10 * effect->attractor.probability + 8500), + angleToFace); + break; + default: + return true; + } + return true; +} + +void +PlayRandomAnimationsFromAnimBlock(CPed* ped, AssocGroupId animGroup, uint32 first, uint32 amount) +{ + if (!ped->IsPedInControl()) + return; + + const char *groupName = CAnimManager::GetAnimGroupName(animGroup); + CAnimBlock *animBlock = CAnimManager::GetAnimationBlock(groupName); + CAnimBlendAssociation *assoc; + for (assoc = RpAnimBlendClumpGetFirstAssociation(ped->GetClump()); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { + int first = animBlock->firstIndex; + int index = assoc->hierarchy - CAnimManager::GetAnimation(0); + if (index >= first && index < first + animBlock->numAnims) { + break; + } + } + + if (CTimer::GetTimeInMilliseconds() > ped->m_nWaitTimer && assoc) + assoc->flags &= ~ASSOC_REPEAT; + + if (!assoc || assoc->blendDelta < 0.0f) { + int selectedAnimOffset; + do + selectedAnimOffset = CGeneral::GetRandomNumberInRange(0, (int32)amount); + while (assoc && first + selectedAnimOffset == assoc->animId); + + assoc = CAnimManager::BlendAnimation(ped->GetClump(), animGroup, (AnimationId)(first + selectedAnimOffset), 3.0f); + + assoc->SetFinishCallback(CPed::FinishedWaitCB, ped); + if (assoc->flags & ASSOC_REPEAT) + ped->m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(3000, 8000); + else + ped->m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 8000; + } +} + +void +CPed::ClearWaitState(void) +{ + CAnimBlendAssociation *assoc; + switch (m_nWaitState) { + case WAITSTATE_PLAYANIM_CHAT: + case WAITSTATE_SIT_DOWN: + case WAITSTATE_SIT_DOWN_RVRS: + case WAITSTATE_SIT_UP: + case WAITSTATE_SIT_IDLE: + case WAITSTATE_USE_ATM: + if (CTimer::GetTimeInMilliseconds() <= m_nWaitTimer) { + if (m_nWaitState == WAITSTATE_USE_ATM) { + assoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ATM); + if (assoc) + assoc->blendDelta = -8.0f; + if (m_attractor) + GetPedAttractorManager()->DeRegisterPed(this, m_attractor); + + } else if (m_nWaitState == WAITSTATE_PLAYANIM_CHAT) { + assoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CHAT); + if (assoc) + assoc->blendDelta = -8.0f; + if (m_attractor) + GetPedAttractorManager()->DeRegisterPed(this, m_attractor); + + } else if (m_nWaitState == WAITSTATE_SIT_DOWN || m_nWaitState == WAITSTATE_SIT_DOWN_RVRS || m_nWaitState == WAITSTATE_SIT_IDLE || m_nWaitState == WAITSTATE_SIT_UP) { + switch (m_nWaitState) { + case WAITSTATE_SIT_DOWN: + assoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_SEAT_DOWN); + if (assoc) + assoc->blendDelta = -8.0f; + break; + case WAITSTATE_SIT_IDLE: + assoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_SEAT_IDLE); + if (assoc) + assoc->blendDelta = -8.0f; + break; + case WAITSTATE_SIT_UP: + assoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_SEAT_UP); + if (assoc) + assoc->blendDelta = -8.0f; + break; + default: + break; + } + if (m_attractor) + GetPedAttractorManager()->DeRegisterPed(this, m_attractor); + } + } + break; + case WAITSTATE_RIOT: + { + CAnimBlock* riotAnimBlock = CAnimManager::GetAnimationBlock("riot"); + + for (assoc = RpAnimBlendClumpGetFirstAssociation(GetClump()); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { + int first = riotAnimBlock->firstIndex; + int index = assoc->hierarchy - CAnimManager::GetAnimation(0); + if (index >= first && index < first + riotAnimBlock->numAnims) { + assoc->blendDelta = -1000.0f; + } + } + break; + } + case WAITSTATE_FAST_FALL: + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_HIGHIMPACT_FRONT)) + SetGetUp(); + + break; + case WAITSTATE_BOMBER: + assoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DETONATE); + if (assoc) + assoc->blendDelta = -8.0f; + break; + case WAITSTATE_STRIPPER: + { + CAnimBlock* stripAnimBlock = CAnimManager::GetAnimationBlock("strip"); + + for (assoc = RpAnimBlendClumpGetFirstAssociation(GetClump()); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { + int first = stripAnimBlock->firstIndex; + int index = assoc->hierarchy - CAnimManager::GetAnimation(0); + if (index >= first && index < first + stripAnimBlock->numAnims) { + assoc->blendDelta = -1000.0f; + } + } + break; + } + case WAITSTATE_LANCESITTING: + assoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_SUNBATHE_IDLE); + if (assoc) + assoc->blendDelta = -8.0f; + break; + case WAITSTATE_PLAYANIM_HANDSUP_SIMPLE: + assoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_HANDSUP); + if (assoc) + assoc->blendDelta = -8.0f; + break; + default: + break; + } + m_nWaitState = WAITSTATE_FALSE; +} + +void +CPed::SetWaitState(eWaitState state, void *time) +{ + AnimationId waitAnim = ANIM_STD_NUM; + CAnimBlendAssociation *animAssoc; + + if (!IsPedInControl()) + return; + + if (m_nWaitState == WAITSTATE_RIOT && state != WAITSTATE_FALSE) + return; + + if (state != m_nWaitState) + FinishedWaitCB(nil, this); + + switch (state) { + case WAITSTATE_TRAFFIC_LIGHTS: + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 500; + SetMoveState(PEDMOVE_STILL); + break; + case WAITSTATE_CROSS_ROAD: + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 1000; + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE_HBHB, 4.0f); + break; + case WAITSTATE_CROSS_ROAD_LOOK: + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ROADCROSS, 8.0f); + + if (time) + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + *(int*)time; + else + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(2000,5000); + + break; + case WAITSTATE_LOOK_PED: + case WAITSTATE_LOOK_SHOP: + case WAITSTATE_LOOK_ACCIDENT: + case WAITSTATE_FACEOFF_GANG: + case WAITSTATE_RIOT: + case WAITSTATE_STRIPPER: + break; + case WAITSTATE_DOUBLEBACK: + m_headingRate = 0.0f; + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 3500; + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE_HBHB, 4.0f); +#ifdef FIX_BUGS + animAssoc->SetFinishCallback(RestoreHeadingRateCB, this); +#endif + break; + case WAITSTATE_HITWALL: + m_headingRate = 2.0f; + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 5000; + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_WALL, 16.0f); + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + animAssoc->flags |= ASSOC_FADEOUTWHENDONE; + animAssoc->SetDeleteCallback(FinishedWaitCB, this); + + if (m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER && CharCreatedBy == RANDOM_CHAR && m_nPedState == PED_SEEK_CAR) { + ClearObjective(); + RestorePreviousState(); + m_carJackTimer = CTimer::GetTimeInMilliseconds() + 30000; + } + break; + case WAITSTATE_TURN180: + m_headingRate = 0.0f; + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 5000; + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_TURN180, 4.0f); + animAssoc->SetFinishCallback(FinishedWaitCB, this); + break; + case WAITSTATE_SURPRISE: + m_headingRate = 0.0f; + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 2000; + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_WALL, 4.0f); + animAssoc->SetFinishCallback(FinishedWaitCB, this); + break; + case WAITSTATE_STUCK: + SetMoveState(PEDMOVE_STILL); + SetMoveAnim(); + m_headingRate = 0.0f; + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 5000; + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE_TIRED, 4.0f); +#ifdef FIX_BUGS + animAssoc->SetFinishCallback(RestoreHeadingRateCB, this); +#endif + + // Random char as passenger? Cop, medic etc.? + if (m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER && CharCreatedBy == RANDOM_CHAR && m_nPedState == PED_SEEK_CAR) { + ClearObjective(); + RestorePreviousState(); + m_carJackTimer = CTimer::GetTimeInMilliseconds() + 30000; + } + break; + case WAITSTATE_LOOK_ABOUT: + SetMoveState(PEDMOVE_STILL); + SetMoveAnim(); + m_headingRate = 0.0f; + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 5000; + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE_HBHB, 4.0f); +#ifdef FIX_BUGS + animAssoc->SetFinishCallback(RestoreHeadingRateCB, this); +#endif + + break; + case WAITSTATE_PLAYANIM_COWER: + waitAnim = ANIM_STD_HANDSCOWER; + case WAITSTATE_PLAYANIM_HANDSUP: + if (waitAnim == ANIM_STD_NUM) + waitAnim = ANIM_STD_HANDSUP; + case WAITSTATE_PLAYANIM_HANDSCOWER: + if (waitAnim == ANIM_STD_NUM) + waitAnim = ANIM_STD_HANDSCOWER; + m_headingRate = 0.0f; + if (time) + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + *(int*)time; + else + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 3000; + + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, waitAnim, 4.0f); + animAssoc->SetDeleteCallback(FinishedWaitCB, this); + break; + case WAITSTATE_PLAYANIM_DUCK: + waitAnim = ANIM_STD_DUCK_DOWN; + case WAITSTATE_PLAYANIM_TAXI: + if (waitAnim == ANIM_STD_NUM) + waitAnim = ANIM_STD_HAILTAXI; + case WAITSTATE_PLAYANIM_CHAT: + if (waitAnim == ANIM_STD_NUM) + waitAnim = ANIM_STD_CHAT; + if (time) + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + *(int*)time; + else + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 3000; + + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, waitAnim, 4.0f); + animAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + animAssoc->SetDeleteCallback(FinishedWaitCB, this); + break; + case WAITSTATE_FINISH_FLEE: + SetMoveState(PEDMOVE_STILL); + SetMoveAnim(); + m_headingRate = 0.0f; + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 2500; + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE_TIRED, 4.0f); +#ifdef FIX_BUGS + animAssoc->SetFinishCallback(RestoreHeadingRateCB, this); +#endif + break; + case WAITSTATE_SIT_DOWN: + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_SEAT_DOWN, 4.0f); + animAssoc->SetFinishCallback(FinishedWaitCB, this); + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 100000; + break; + case WAITSTATE_SIT_UP: + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_SEAT_UP, 4.0f); + animAssoc->SetFinishCallback(FinishedWaitCB, this); + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 100000; + break; + case WAITSTATE_SIT_IDLE: + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_SEAT_IDLE, 128.f); + animAssoc->SetFinishCallback(FinishedWaitCB, this); + if (time) + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + *(int*)time; + else + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(25000, 30000); + break; + case WAITSTATE_USE_ATM: + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ATM, 4.0f); + animAssoc->SetFinishCallback(FinishedWaitCB, this); + if (time) + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + *(int*)time; + else + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 100000; + break; + case WAITSTATE_SUN_BATHE_IDLE: + m_headingRate = 0.0f; + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_SUNBATHE, ANIM_SUNBATHE_IDLE, 4.0f); + animAssoc->SetDeleteCallback(DeleteSunbatheIdleAnimCB, this); + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(50000, 100000); + break; + case WAITSTATE_FAST_FALL: + SetFall(-1, ANIM_STD_HIGHIMPACT_FRONT, true); + break; + case WAITSTATE_BOMBER: + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_DETONATE, 4.0f); + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + *(int*)time; + break; + case WAITSTATE_GROUND_ATTACK: + { + CWeaponInfo* currentWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + if (!currentWeapon) + break; + if (GetFireAnimGround(currentWeapon, false)) { + if (!RpAnimBlendClumpGetAssociation(GetClump(), GetFireAnimGround(currentWeapon, false))) { + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 5000; + CAnimBlendAssociation* newAnim = CAnimManager::BlendAnimation(GetClump(), + currentWeapon->m_AnimToPlay, GetFireAnimGround(currentWeapon, false), 8.0f); + newAnim->SetDeleteCallback(FinishedWaitCB, this); + } + } + break; + } + case WAITSTATE_LANCESITTING: + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_LANCE, ANIM_SUNBATHE_IDLE, 4.0f); + break; + case WAITSTATE_PLAYANIM_HANDSUP_SIMPLE: + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HANDSUP, 4.0f); + animAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + animAssoc->SetDeleteCallback(FinishedWaitCB, this); + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + *(int*)time; + break; + default: + ClearWaitState(); + RestoreHeadingRate(); + return; + } + m_nWaitState = state; +} + +void +CPed::Wait(void) +{ + AnimationId mustHaveAnim = ANIM_STD_NUM; + CAnimBlendAssociation *animAssoc; + CPed *pedWeLook; + + if (DyingOrDead()) { + ClearWaitState(); + RestoreHeadingRate(); + return; + } + + switch (m_nWaitState) { + + case WAITSTATE_TRAFFIC_LIGHTS: + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + if (CTrafficLights::LightForPeds() == PED_LIGHTS_WALK) { + ClearWaitState(); + SetMoveState(PEDMOVE_WALK); + } + } + break; + + case WAITSTATE_CROSS_ROAD: + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + if (CGeneral::GetRandomNumber() & 1 || !m_nWaitTimer) + ClearWaitState(); + else + SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, nil); + + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_HBHB); + if (animAssoc) { + animAssoc->blendDelta = -8.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + } + break; + + case WAITSTATE_CROSS_ROAD_LOOK: + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + ClearWaitState(); + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROADCROSS); + if (animAssoc) { + animAssoc->blendDelta = -8.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + } + break; + + case WAITSTATE_DOUBLEBACK: + if (CTimer::GetTimeInMilliseconds() <= m_nWaitTimer) { + uint32 timeLeft = m_nWaitTimer - CTimer::GetTimeInMilliseconds(); + if (timeLeft < 2500 && timeLeft > 2000) { + m_nWaitTimer -= 500; + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_XPRESS_SCRATCH, 4.0f); + } + } else { + ClearWaitState(); + SetMoveState(PEDMOVE_WALK); + } + break; + + case WAITSTATE_HITWALL: + if (CTimer::GetTimeInMilliseconds() <= m_nWaitTimer) { + if (m_collidingThingTimer > CTimer::GetTimeInMilliseconds()) { + m_collidingThingTimer = CTimer::GetTimeInMilliseconds() + 2500; + } + } else { + ClearWaitState(); + } + break; + + case WAITSTATE_TURN180: + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + ClearWaitState(); + m_fRotationCur = m_fRotationCur + PI; + if (m_nPedState == PED_INVESTIGATE) + ClearInvestigateEvent(); + } + + if (m_collidingThingTimer > CTimer::GetTimeInMilliseconds()) { + m_collidingThingTimer = CTimer::GetTimeInMilliseconds() + 2500; + } + break; + + case WAITSTATE_SURPRISE: + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_HIT_WALL)) { + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_XPRESS_SCRATCH, 4.0f); + animAssoc->SetFinishCallback(FinishedWaitCB, this); + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 5000; + } else { + ClearWaitState(); + } + } + break; + + case WAITSTATE_STUCK: + if (CTimer::GetTimeInMilliseconds() <= m_nWaitTimer) + break; + + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_TIRED); + + if (!animAssoc) + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_TURN180); + if (!animAssoc) + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_XPRESS_SCRATCH); + if (!animAssoc) + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROADCROSS); + + if (animAssoc) { + if (animAssoc->IsPartial()) { + animAssoc->blendDelta = -8.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } else { + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 4.0f); + } + + if (animAssoc->animId == ANIM_STD_TURN180) { + m_fRotationCur = CGeneral::LimitRadianAngle(PI + m_fRotationCur); + ClearWaitState(); + SetMoveState(PEDMOVE_WALK); + m_nStoredMoveState = PEDMOVE_NONE; + m_panicCounter = 0; + return; + } + } + + AnimationId animToPlay; + + switch (CGeneral::GetRandomNumber() & 3) { + case 0: + animToPlay = ANIM_STD_ROADCROSS; + break; + case 1: + animToPlay = ANIM_STD_IDLE_TIRED; + break; + case 2: + animToPlay = ANIM_STD_XPRESS_SCRATCH; + break; + case 3: + animToPlay = ANIM_STD_TURN180; + break; + default: + break; + } + + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, animToPlay, 4.0f); + + if (animToPlay == ANIM_STD_TURN180) + animAssoc->SetFinishCallback(FinishedWaitCB, this); + + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(1500, 5000); + break; + + case WAITSTATE_LOOK_ABOUT: + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + ClearWaitState(); + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_HBHB); + if (animAssoc) { + animAssoc->blendDelta = -8.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + } + break; + + case WAITSTATE_PLAYANIM_HANDSUP: + mustHaveAnim = ANIM_STD_HANDSUP; + + case WAITSTATE_PLAYANIM_HANDSCOWER: + if (mustHaveAnim == ANIM_STD_NUM) + mustHaveAnim = ANIM_STD_HANDSCOWER; + + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), mustHaveAnim); + pedWeLook = (CPed*) m_pLookTarget; + + if ((!m_pLookTarget || !m_pLookTarget->IsPed() || pedWeLook->m_pPointGunAt) + && m_nPedState != PED_FLEE_ENTITY + && m_nPedState != PED_ATTACK + && CTimer::GetTimeInMilliseconds() <= m_nWaitTimer + && animAssoc) { + + if (pedWeLook) + TurnBody(); + } else { + ClearWaitState(); + m_nWaitTimer = 0; + if (m_pLookTarget && m_pLookTarget->IsPed()) { + if (m_nPedState != PED_FLEE_ENTITY && m_nPedState != PED_ATTACK) { + if (bCrouchWhenScared) { + if (bIsDucking) { + ClearDuck(false); + SetDuck(10000, true); + } + + } else if (m_pedStats->m_fear <= 100 - pedWeLook->m_pedStats->m_temper) { + if (GetWeapon()->IsTypeMelee()) { + if(m_pedStats->m_flags & STAT_GUN_PANIC) { + SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, m_pLookTarget); + if (m_nPedState == PED_FLEE_ENTITY || m_nPedState == PED_FLEE_POS) { + + bUsePedNodeSeek = true; + m_pNextPathNode = nil; + } + if (m_nMoveState != PEDMOVE_RUN) + SetMoveState(PEDMOVE_WALK); + + if (m_nPedType != PEDTYPE_COP) { + ProcessObjective(); + SetMoveState(PEDMOVE_WALK); + } + } else { + SetObjective(OBJECTIVE_NONE); + SetWanderPath(CGeneral::GetRandomNumberInRange(0.0f, 8.0f)); + } + } else { + SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_pLookTarget); + SetObjectiveTimer(20000); + } + } else { + SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, m_pLookTarget); + if (m_nPedState == PED_FLEE_ENTITY || m_nPedState == PED_FLEE_POS) + { + bUsePedNodeSeek = true; + m_pNextPathNode = nil; + } + SetMoveState(PEDMOVE_RUN); + Say(SOUND_PED_FLEE_RUN); + } + } + } + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), mustHaveAnim); + if (animAssoc) { + animAssoc->blendDelta = -4.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + } + break; + case WAITSTATE_PLAYANIM_COWER: + mustHaveAnim = ANIM_STD_HANDSCOWER; + + case WAITSTATE_PLAYANIM_DUCK: + if (mustHaveAnim == ANIM_STD_NUM) + mustHaveAnim = ANIM_STD_DUCK_DOWN; + + case WAITSTATE_PLAYANIM_TAXI: + if (mustHaveAnim == ANIM_STD_NUM) + mustHaveAnim = ANIM_STD_HAILTAXI; + + case WAITSTATE_PLAYANIM_CHAT: + if (mustHaveAnim == ANIM_STD_NUM) + mustHaveAnim = ANIM_STD_CHAT; + + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), mustHaveAnim); + if (animAssoc) { + animAssoc->blendDelta = -4.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + if (m_attractor && m_objective == OBJECTIVE_WAIT_ON_FOOT_AT_ICE_CREAM_VAN) { + GetPedAttractorManager()->BroadcastDeparture(this, m_attractor); + bBoughtIceCream = true; + } + ClearWaitState(); + } else if (m_nWaitState == WAITSTATE_PLAYANIM_TAXI) { + if (m_pedInObjective) { + if (m_objective == OBJECTIVE_GOTO_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT) { + + if (m_pLookTarget) + m_pLookTarget->CleanUpOldReference(&m_pLookTarget); + m_pLookTarget = m_pedInObjective; + m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); + TurnBody(); + } + } + } + break; + + case WAITSTATE_FINISH_FLEE: + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_TIRED); + if (animAssoc) { + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 4.0f); + int timer = 2000; + ClearWaitState(); + SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &timer); + } + } else { + ClearWaitState(); + } + break; + case WAITSTATE_SIT_DOWN: + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + ClearWaitState(); + SetWaitState(WAITSTATE_SIT_IDLE, 0); + } + break; + //case WAITSTATE_SIT_DOWN_RVRS: + case WAITSTATE_SIT_UP: + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + if (m_attractor) + GetPedAttractorManager()->BroadcastDeparture(this, m_attractor); + ClearWaitState(); + if (bFleeWhenStanding) { + if (m_threatEx) { + SetFlee(m_threatEx, 10000); + bFleeWhenStanding = false; + m_threatEx = nil; + Say(SOUND_PED_FLEE_SPRINT); + } + } + } + break; + case WAITSTATE_SIT_IDLE: + if (bTurnedAroundOnAttractor) { + m_fRotationCur += PI; + m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); + m_fRotationDest = m_fRotationCur; + bTurnedAroundOnAttractor = false; + } + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + ClearWaitState(); + SetWaitState(WAITSTATE_SIT_UP, 0); + } else { + if (m_fleeFrom && m_fleeFrom->IsVehicle()) { + m_pNextPathNode = nil; + m_threatEx = m_threatEntity; + bFleeWhenStanding = true; + ClearWaitState(); + SetWaitState(WAITSTATE_SIT_UP, 0); + } else { + uint32 threatFlag = ScanForThreats(); + if (threatFlag == PED_FLAG_GUN || threatFlag == PED_FLAG_EXPLOSION || threatFlag == PED_FLAG_DEADPEDS) { + m_pNextPathNode = nil; + m_threatEx = m_threatEntity; + bFleeWhenStanding = true; + ClearWaitState(); + SetWaitState(WAITSTATE_SIT_UP, 0); + } + } + } + break; + case WAITSTATE_USE_ATM: + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + if (m_attractor) + GetPedAttractorManager()->BroadcastDeparture(this, m_attractor); + ClearWaitState(); + } + break; + case WAITSTATE_SUN_BATHE_IDLE: + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer && bCanGiveUpSunbathing) { + m_pNextPathNode = nil; + bGotUpOfMyOwnAccord = true; + SetGetUp(); + ClearWaitState(); + + } else if (CWeather::Rain <= 0.1f) { + if (CClock::GetHours() <= 18 || CGeneral::GetRandomNumberInRange(0.f, 1.0f) < 0.005f) { + uint32 threatFlag = ScanForThreats(); + if (threatFlag == PED_FLAG_GUN || threatFlag == PED_FLAG_EXPLOSION || threatFlag == PED_FLAG_DEADPEDS) { + // Get up in case of danger + m_pNextPathNode = nil; + m_threatEx = m_threatEntity; + bFleeWhenStanding = true; + SetGetUp(); + ClearWaitState(); + } + CPlayerPed *player = FindPlayerPed(); + if (player) { + // Get up if player coming towards us with a car + if (player->InVehicle()){ + CVector vehSpeedPerSec = player->m_pMyVehicle->m_vecMoveSpeed * GAME_SPEED_TO_METERS_PER_SECOND; + CVector vehPos = player->m_pMyVehicle->GetPosition(); + CVector ourPos = GetPosition(); + float timeUntilVehReachPed = DotProduct(ourPos - vehPos, vehSpeedPerSec) / vehSpeedPerSec.MagnitudeSqr(); + if (timeUntilVehReachPed > 0.0 && timeUntilVehReachPed < 8.0f) { + if ((ourPos - (timeUntilVehReachPed * vehSpeedPerSec + vehPos)).Magnitude() < 5.0f) { + m_pNextPathNode = nil; + m_threatEx = player; + bFleeWhenStanding = true; + SetGetUp(); + ClearWaitState(); + } + } + } + } + } else { + m_pNextPathNode = nil; + bGotUpOfMyOwnAccord = true; + SetGetUp(); + ClearWaitState(); + } + } else { + m_pNextPathNode = nil; + bGotUpOfMyOwnAccord = true; + SetGetUp(); + ClearWaitState(); + } + break; + case WAITSTATE_RIOT: + if (m_nPedState == PED_FLEE_ENTITY || m_nPedState == PED_ATTACK) { + ClearWaitState(); + break; + } + + PlayRandomAnimationsFromAnimBlock(this, ASSOCGRP_RIOT, ANIM_RIOT_ANGRY, ANIM_RIOT_FUCKYOU - ANIM_RIOT_ANGRY + 1); + if (IsPedInControl() && CGeneral::GetRandomNumberInRange(0.f,1.f) < 0.25f + && CPopulation::CanJeerAtStripper(m_modelIndex)) { + for (int i = 0; i < m_numNearPeds; ++i) { + CPed *nearPed = m_nearPeds[i]; + if (nearPed) { + if ((GetPosition() - nearPed->GetPosition()).MagnitudeSqr() < sq(10.f)) { + for (int anim = ANIM_STRIP_A; anim <= ANIM_STRIP_G; anim++) { + if (RpAnimBlendClumpGetAssociation(nearPed->GetClump(), anim)) + Say(SOUND_PED_JEER); + } + } + } + } + } + break; + case WAITSTATE_BOMBER: + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) + ClearWaitState(); + break; + case WAITSTATE_STRIPPER: + PlayRandomAnimationsFromAnimBlock(this, ASSOCGRP_STRIP, ANIM_STRIP_A, ANIM_STRIP_G - ANIM_STRIP_A + 1); + break; + case WAITSTATE_PLAYANIM_HANDSUP_SIMPLE: + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) + ClearWaitState(); + break; + default: + break; + } + + if(!m_nWaitState) + RestoreHeadingRate(); +} + +void +CPed::DeleteSunbatheIdleAnimCB(CAnimBlendAssociation *assoc, void *arg) +{ + CPed *ped = (CPed*) arg; + + if (CTimer::GetTimeInMilliseconds() <= ped->m_nWaitTimer + && !ped->bGotUpOfMyOwnAccord && !ped->bFleeWhenStanding && !ped->m_threatEx) { + ped->m_pNextPathNode = nil; + ped->bFleeWhenStanding = true; + ped->m_threatEx = FindPlayerPed(); + ped->SetGetUp(); + ped->ClearWaitState(); + } + ped->m_nWaitTimer = 0; + ped->RestoreHeadingRate(); + ped->Wait(); +} + +void +CPed::FinishedWaitCB(CAnimBlendAssociation *animAssoc, void *arg) +{ + CPed *ped = (CPed*)arg; + + ped->m_nWaitTimer = 0; + ped->RestoreHeadingRate(); + ped->Wait(); +} + +void +CPed::RestoreHeadingRate(void) +{ + m_headingRate = m_pedStats->m_headingChangeRate; +} + +void +CPed::RestoreHeadingRateCB(CAnimBlendAssociation *assoc, void *arg) +{ + ((CPed*)arg)->RestoreHeadingRate(); +} + +void +CPed::FlagToDestroyWhenNextProcessed(void) +{ + bRemoveFromWorld = true; + if (!InVehicle()) + return; + if (m_pMyVehicle->pDriver == this){ + m_pMyVehicle->pDriver = nil; + if (IsPlayer() && m_pMyVehicle->GetStatus() != STATUS_WRECKED) + m_pMyVehicle->SetStatus(STATUS_ABANDONED); + }else{ + m_pMyVehicle->RemovePassenger(this); + } + bInVehicle = false; + m_pMyVehicle = nil; + + if (CharCreatedBy == MISSION_CHAR) + SetPedState(PED_DEAD); + else + SetPedState(PED_NONE); + m_pVehicleAnim = nil; +} + +void +CPed::SetSolicit(uint32 time) +{ + if (m_nPedState == PED_SOLICIT || !IsPedInControl() || !m_carInObjective) + return; + + if (CharCreatedBy != MISSION_CHAR && m_carInObjective->m_nNumGettingIn == 0 + && CTimer::GetTimeInMilliseconds() < m_objectiveTimer) { + if (m_vehDoor == CAR_DOOR_LF) { + m_fRotationDest = m_carInObjective->GetForward().Heading() - HALFPI; + } else { + m_fRotationDest = m_carInObjective->GetForward().Heading() + HALFPI; + } + + if (Abs(m_fRotationDest - m_fRotationCur) < HALFPI) { + m_chatTimer = CTimer::GetTimeInMilliseconds() + time; + + if(!m_carInObjective->bIsVan && !m_carInObjective->bIsBus) + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_HOOKERTALK, 4.0f); + + SetPedState(PED_SOLICIT); + } + } +} + +void +CPed::Solicit(void) +{ + if (m_chatTimer >= CTimer::GetTimeInMilliseconds() && m_carInObjective) { + CVector doorPos = GetPositionToOpenCarDoor(m_carInObjective, m_vehDoor, 0.0f); + Say(SOUND_PED_SOLICIT); + if (FindPlayerVehicle() == m_carInObjective) { + FindPlayerPed()->Say(SOUND_PED_SOLICIT); + } + SetMoveState(PEDMOVE_STILL); + + // Game uses GetAngleBetweenPoints and converts it to radian + m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( + doorPos.x, doorPos.y, + GetPosition().x, GetPosition().y); + + if (m_fRotationDest < 0.0f) { + m_fRotationDest += TWOPI; + } else if (m_fRotationDest > TWOPI) { + m_fRotationDest -= TWOPI; + } + + if ((GetPosition() - doorPos).MagnitudeSqr() <= 1.0f) + return; + CAnimBlendAssociation *talkAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_HOOKERTALK); + if (talkAssoc) { + talkAssoc->blendDelta = -1000.0f; + talkAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + RestorePreviousState(); + RestorePreviousObjective(); + SetObjectiveTimer(10000); + } else if (!m_carInObjective) { + RestorePreviousState(); + RestorePreviousObjective(); + SetObjectiveTimer(10000); + } else if (CWorld::Players[CWorld::PlayerInFocus].m_nMoney <= 100) { + m_carInObjective = nil; + } else { + m_pVehicleAnim = nil; + SetLeader(m_carInObjective->pDriver); + Say(SOUND_PED_SOLICIT); + } +} + +void +CPed::SetBuyIceCream(void) +{ + if (m_nPedState == PED_BUY_ICECREAM || !IsPedInControl()) + return; + + if (!m_carInObjective) + return; + + SetPedState(PED_BUY_ICECREAM); +} + +void +CPed::BuyIceCream(void) +{ + if (m_carInObjective) { + CPed *driver = m_carInObjective->pDriver; + if (driver && CTimer::GetTimeInMilliseconds() > m_chatTimer) { + SetChat(driver, 8000); + driver->SetChat(this, 8000); + return; + } + SetObjective(OBJECTIVE_NONE); + SetWanderPath(CGeneral::GetRandomNumberInRange(0, 8)); + } else { + SetObjective(OBJECTIVE_NONE); + SetWanderPath(CGeneral::GetRandomNumberInRange(0, 8)); + } +} + +bool +CPed::PossiblyFindBetterPosToSeekCar(CVector *pos, CVehicle *veh) +{ + bool foundIt = false; + + CVector helperPos = GetPosition(); + helperPos.z = pos->z - 0.5f; + + CVector foundPos = *pos; + foundPos.z -= 0.5f; + + // If there is another car between target car and us. + if (CWorld::TestSphereAgainstWorld((foundPos + helperPos) / 2.0f, 0.25f, veh, false, true, false, false, false, false)) { + + CColModel *vehCol = veh->GetModelInfo()->GetColModel(); + CVector *colMin = &vehCol->boundingBox.min; + CVector *colMax = &vehCol->boundingBox.max; + + CVector leftRearPos = CVector(colMin->x - 0.5f, colMin->y - 0.5f, 0.0f); + CVector rightRearPos = CVector(0.5f + colMax->x, colMin->y - 0.5f, 0.0f); + CVector leftFrontPos = CVector(colMin->x - 0.5f, 0.5f + colMax->y, 0.0f); + CVector rightFrontPos = CVector(0.5f + colMax->x, 0.5f + colMax->y, 0.0f); + + leftRearPos = veh->GetMatrix() * leftRearPos; + rightRearPos = veh->GetMatrix() * rightRearPos; + leftFrontPos = veh->GetMatrix() * leftFrontPos; + rightFrontPos = veh->GetMatrix() * rightFrontPos; + + // Makes helperPos veh-ped distance vector. + helperPos -= veh->GetPosition(); + + // ?!? I think it's absurd to use this unless another function like SeekCar finds next pos. with it and we're trying to simulate it's behaviour. + // On every run it returns another pos. for ped, with same distance to the veh. + // Sequence of positions are not guaranteed, it depends on global pos. (So sometimes it returns positions to make ped draw circle, sometimes don't) + helperPos = veh->GetMatrix() * helperPos; + + float vehForwardHeading = veh->GetForward().Heading(); + + // I'm absolutely not sure about these namings. + // NTVF = needed turn if we're looking to vehicle front and wanna look to... + + float potentialLrHeading = Atan2(leftRearPos.x - helperPos.x, leftRearPos.y - helperPos.y); + float NTVF_LR = CGeneral::LimitRadianAngle(potentialLrHeading - vehForwardHeading); + + float potentialRrHeading = Atan2(rightRearPos.x - helperPos.x, rightRearPos.y - helperPos.y); + float NTVF_RR = CGeneral::LimitRadianAngle(potentialRrHeading - vehForwardHeading); + + float potentialLfHeading = Atan2(leftFrontPos.x - helperPos.x, leftFrontPos.y - helperPos.y); + float NTVF_LF = CGeneral::LimitRadianAngle(potentialLfHeading - vehForwardHeading); + + float potentialRfHeading = Atan2(rightFrontPos.x - helperPos.x, rightFrontPos.y - helperPos.y); + float NTVF_RF = CGeneral::LimitRadianAngle(potentialRfHeading - vehForwardHeading); + + bool canHeadToLr = NTVF_LR <= -PI || NTVF_LR >= -HALFPI; + + bool canHeadToRr = NTVF_RR <= HALFPI || NTVF_RR >= PI; + + bool canHeadToLf = NTVF_LF >= 0.0f || NTVF_LF <= -HALFPI; + + bool canHeadToRf = NTVF_RF <= 0.0f || NTVF_RF >= HALFPI; + + // Only order of conditions are different among enterTypes. + if (m_vehDoor == CAR_DOOR_RR) { + if (canHeadToRr) { + foundPos = rightRearPos; + foundIt = true; + } else if (canHeadToRf) { + foundPos = rightFrontPos; + foundIt = true; + } else if (canHeadToLr) { + foundPos = leftRearPos; + foundIt = true; + } else if (canHeadToLf) { + foundPos = leftFrontPos; + foundIt = true; + } + } else if(m_vehDoor == CAR_DOOR_RF) { + if (canHeadToRf) { + foundPos = rightFrontPos; + foundIt = true; + } else if (canHeadToRr) { + foundPos = rightRearPos; + foundIt = true; + } else if (canHeadToLf) { + foundPos = leftFrontPos; + foundIt = true; + } else if (canHeadToLr) { + foundPos = leftRearPos; + foundIt = true; + } + } else if (m_vehDoor == CAR_DOOR_LF) { + if (canHeadToLf) { + foundPos = leftFrontPos; + foundIt = true; + } else if (canHeadToLr) { + foundPos = leftRearPos; + foundIt = true; + } else if (canHeadToRf) { + foundPos = rightFrontPos; + foundIt = true; + } else if (canHeadToRr) { + foundPos = rightRearPos; + foundIt = true; + } + } else if (m_vehDoor == CAR_DOOR_LR) { + if (canHeadToLr) { + foundPos = leftRearPos; + foundIt = true; + } else if (canHeadToLf) { + foundPos = leftFrontPos; + foundIt = true; + } else if (canHeadToRr) { + foundPos = rightRearPos; + foundIt = true; + } else if (canHeadToRf) { + foundPos = rightFrontPos; + foundIt = true; + } + } + } + if (!foundIt) + return false; + + helperPos = GetPosition() - foundPos; + helperPos.z = 0.0f; + if (helperPos.MagnitudeSqr() <= sq(0.5f)) + return false; + + pos->x = foundPos.x; + pos->y = foundPos.y; + return true; +} + +void +CPed::SetLeader(CEntity *leader) +{ + m_leader = (CPed*)leader; + + if (m_leader) { + m_leader->bIsLeader = true; + m_leader->RegisterReference((CEntity**)&m_leader); + } +} + +bool +CPed::CanPedJumpThis(CEntity *unused, CVector *damageNormal) +{ + if (m_nSurfaceTouched == SURFACE_WATER) + return true; + + CVector pos = GetPosition(); + CVector forwardOffset = GetForward(); + if (damageNormal && damageNormal->z > 0.17f) { + if (damageNormal->z > 0.9f) + return false; + + CColModel *ourCol = GetColModel(); + pos.z = ourCol->spheres->center.z - ourCol->spheres->radius * damageNormal->z + pos.z; + pos.z = pos.z + 0.05f; + float collPower = damageNormal->Magnitude2D(); + if (damageNormal->z > 0.5f) { + CVector invDamageNormal(-damageNormal->x, -damageNormal->y, 0.0f); + invDamageNormal *= 1.0f / collPower; + CVector estimatedJumpDist = invDamageNormal + collPower * invDamageNormal * ourCol->spheres->radius; + forwardOffset = estimatedJumpDist * Min(2.0f / collPower, 4.0f); + } else { + forwardOffset += collPower * ourCol->spheres->radius * forwardOffset; + } + } else { + pos.z -= 0.15f; + } + + CVector forwardPos = pos + forwardOffset; + return CWorld::GetIsLineOfSightClear(pos, forwardPos, true, false, false, true, false, false, false); +} + +void +CPed::SetJump(void) +{ + if (!bInVehicle && m_nPedState != PED_JUMP && !RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_JUMP_LAUNCH) && + (m_nSurfaceTouched != SURFACE_STEEP_CLIFF || DotProduct(GetForward(), m_vecDamageNormal) >= 0.0f)) { + + SetStoredState(); + SetPedState(PED_JUMP); + CAnimBlendAssociation *jumpAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_JUMP_LAUNCH, 8.0f); + jumpAssoc->SetFinishCallback(FinishLaunchCB, this); + m_fRotationDest = m_fRotationCur; + } +} + +void +CPed::FinishLaunchCB(CAnimBlendAssociation *animAssoc, void *arg) +{ + CPed *ped = (CPed*)arg; + + if (ped->m_nPedState != PED_JUMP) + return; + + CVector forward(0.09f * ped->GetForward() + ped->GetPosition()); + forward.z += CModelInfo::GetColModel(ped->GetModelIndex())->spheres[2].center.z + 0.35f; + + CEntity *obstacle = CWorld::TestSphereAgainstWorld(forward, 0.25f, nil, true, true, false, true, false, false); + if (!obstacle) { + // Forward of forward + forward += 0.15f * ped->GetForward(); + forward.z += 0.15f; + obstacle = CWorld::TestSphereAgainstWorld(forward, 0.25f, nil, true, true, false, true, false, false); + } + + if (!obstacle && CCullZones::CamStairsForPlayer() && CCullZones::FindZoneWithStairsAttributeForPlayer()) + obstacle = ped; + + if (obstacle) { + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + CAnimBlendAssociation *handsCoverAssoc = CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_WALL, 8.0f); + handsCoverAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; + handsCoverAssoc->SetFinishCallback(FinishHitHeadCB, ped); + ped->bIsLanding = true; + return; + } + + float velocityFromAnim = 0.1f; + CAnimBlendAssociation *sprintAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), ANIM_STD_RUNFAST); + + if (sprintAssoc) { + velocityFromAnim = 0.05f * sprintAssoc->blendAmount + 0.17f; + } else { + CAnimBlendAssociation *runAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), ANIM_STD_RUN); + if (runAssoc) { + velocityFromAnim = 0.07f * runAssoc->blendAmount + 0.1f; + } + } + + if (ped->IsPlayer() || ped->m_pedInObjective && ped->m_pedInObjective->IsPlayer()) + ped->ApplyMoveForce(0.0f, 0.0f, 8.5f); + else + ped->ApplyMoveForce(0.0f, 0.0f, 4.5f); + + if (sq(velocityFromAnim) > ped->m_vecMoveSpeed.MagnitudeSqr2D() || ped->m_pCurrentPhysSurface) { + +#ifdef FREE_CAM + if (TheCamera.Cams[0].Using3rdPersonMouseCam() && !CCamera::bFreeCam) { +#else + if (TheCamera.Cams[0].Using3rdPersonMouseCam()) { +#endif + float fpsAngle = ped->WorkOutHeadingForMovingFirstPerson(ped->m_fRotationCur); + ped->m_vecMoveSpeed.x = -velocityFromAnim * Sin(fpsAngle); + ped->m_vecMoveSpeed.y = velocityFromAnim * Cos(fpsAngle); + } else { + ped->m_vecMoveSpeed.x = -velocityFromAnim * Sin(ped->m_fRotationCur); + ped->m_vecMoveSpeed.y = velocityFromAnim * Cos(ped->m_fRotationCur); + } + + if (ped->m_pCurrentPhysSurface) { + ped->m_vecMoveSpeed.x += ped->m_pCurrentPhysSurface->m_vecMoveSpeed.x; + ped->m_vecMoveSpeed.y += ped->m_pCurrentPhysSurface->m_vecMoveSpeed.y; + } + } + + ped->bIsStanding = false; + ped->bIsInTheAir = true; + animAssoc->blendDelta = -1000.0f; + CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_JUMP_GLIDE); + + if (ped->bDoBloodyFootprints) { + CVector bloodPos(0.0f, 0.0f, 0.0f); + ped->TransformToNode(bloodPos, PED_FOOTL); + + bloodPos.z -= 0.1f; + bloodPos += 0.2f * ped->GetForward(); + + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &bloodPos, + 0.26f * ped->GetForward().x, + 0.26f * ped->GetForward().y, + 0.14f * ped->GetRight().x, + 0.14f * ped->GetRight().y, + 255, 255, 0, 0, 4.0f, 3000, 1.0f); + + bloodPos = CVector(0.0f, 0.0f, 0.0f); + ped->TransformToNode(bloodPos, PED_FOOTR); + + bloodPos.z -= 0.1f; + bloodPos += 0.2f * ped->GetForward(); + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &bloodPos, + 0.26f * ped->GetForward().x, + 0.26f * ped->GetForward().y, + 0.14f * ped->GetRight().x, + 0.14f * ped->GetRight().y, + 255, 255, 0, 0, 4.0f, 3000, 1.0f); + + if (ped->m_bloodyFootprintCountOrDeathTime <= 40) { + ped->m_bloodyFootprintCountOrDeathTime = 0; + ped->bDoBloodyFootprints = false; + } else { + ped->m_bloodyFootprintCountOrDeathTime -= 40; + } + } +} + +void +CPed::FinishJumpCB(CAnimBlendAssociation *animAssoc, void *arg) +{ + CPed *ped = (CPed*)arg; + + ped->bResetWalkAnims = true; + ped->bIsLanding = false; + + animAssoc->blendDelta = -1000.0f; +} + +void +CPed::FinishHitHeadCB(CAnimBlendAssociation *animAssoc, void *arg) +{ + CPed *ped = (CPed*)arg; + + if (animAssoc) { + animAssoc->blendDelta = -4.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + + if (ped->m_nPedState == PED_JUMP) + ped->RestorePreviousState(); + + ped->bIsLanding = false; +} + +bool +CPed::CanPedDriveOff(void) +{ + if (m_nPedState != PED_DRIVING || m_lookTimer > CTimer::GetTimeInMilliseconds()) + return false; + + for (int i = 0; i < m_numNearPeds; i++) { + CPed *nearPed = m_nearPeds[i]; + if (nearPed->m_nPedType == m_nPedType && nearPed->m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER && nearPed->m_carInObjective == m_carInObjective) { + m_lookTimer = CTimer::GetTimeInMilliseconds() + 1000; + return false; + } + } + return true; +} +void +CPed::SetRadioStation(void) +{ + CPedModelInfo* modelInfo = (CPedModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + if (IsPlayer() || !m_pMyVehicle || m_pMyVehicle->pDriver != this) + return; + + if (GetModelIndex() != MI_PGA && GetModelIndex() != MI_PGB) { + if (m_pMyVehicle->m_nRadioStation != modelInfo->radio1 && m_pMyVehicle->m_nRadioStation != modelInfo->radio2) { + if (CGeneral::GetRandomTrueFalse()) + m_pMyVehicle->m_nRadioStation = modelInfo->radio1; + else + m_pMyVehicle->m_nRadioStation = modelInfo->radio2; + } + } else { + m_pMyVehicle->m_nRadioStation = DMAudio.GetFavouriteRadioStation(); + } +} + +void +CPed::WarpPedIntoCar(CVehicle *car) +{ + bInVehicle = true; + m_pMyVehicle = car; + m_pMyVehicle->RegisterReference((CEntity **) &m_pMyVehicle); + m_carInObjective = car; + m_carInObjective->RegisterReference((CEntity **) &m_carInObjective); + SetPedState(PED_DRIVING); + bUsesCollision = false; + bIsInTheAir = false; + bVehExitWillBeInstant = true; + if (m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) { + car->SetDriver(this); + car->pDriver->RegisterReference((CEntity **) &car->pDriver); + + } else if (m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER) { + if (car->IsBike() && !car->pPassengers[0]) { + car->pPassengers[0] = this; + car->pPassengers[0]->RegisterReference((CEntity**) &car->pPassengers[0]); + } + for (int i = 0; i < 4; i++) { + if (!car->pPassengers[i]) { + car->pPassengers[i] = this; + car->pPassengers[i]->RegisterReference((CEntity **) &car->pPassengers[i]); + break; + } + } + } else + return; + + if (IsPlayer()) { + car->SetStatus(STATUS_PLAYER); + AudioManager.PlayerJustGotInCar(); + CCarCtrl::RegisterVehicleOfInterest(car); + } else { + car->SetStatus(STATUS_PHYSICS); + } + + CWorld::Remove(this); + SetPosition(car->GetPosition()); + CWorld::Add(this); + + if (car->bIsAmbulanceOnDuty) { + car->bIsAmbulanceOnDuty = false; + --CCarCtrl::NumAmbulancesOnDuty; + } + if (car->bIsFireTruckOnDuty) { + car->bIsFireTruckOnDuty = false; + --CCarCtrl::NumFiretrucksOnDuty; + } + if (!car->bEngineOn) { + car->bEngineOn = true; + DMAudio.PlayOneShot(car->m_audioEntityId, SOUND_CAR_ENGINE_START, 1.0f); + } + + RpAnimBlendClumpSetBlendDeltas(GetClump(), ASSOC_PARTIAL, -1000.0f); + + AddInCarAnims(car, car->pDriver == this); + RemoveWeaponWhenEnteringVehicle(); + + if (car->bIsBus) + bRenderPedInCar = false; + + bChangedSeat = true; +} + +bool +CPed::HasAttractor(void) +{ + return m_attractor != nil; +} + +void +CPed::SetNewAttraction(CPedAttractor* pAttractor, const CVector& pos, float heading, float time, int qid) +{ + if (!m_attractor) + m_attractor = pAttractor; + if (m_attractor != pAttractor) + return; + switch (pAttractor->GetEffect()->pedattr.type) { + case ATTRACTOR_ATM: SetObjective(OBJECTIVE_GOTO_ATM_ON_FOOT, heading, pos); break; + case ATTRACTOR_SEAT: SetObjective(OBJECTIVE_GOTO_SEAT_ON_FOOT, heading, pos); break; + case ATTRACTOR_STOP: SetObjective(OBJECTIVE_GOTO_BUS_STOP_ON_FOOT, heading, pos); break; + case ATTRACTOR_PIZZA: SetObjective(OBJECTIVE_GOTO_PIZZA_ON_FOOT, heading, pos); break; + case ATTRACTOR_SHELTER: SetObjective(OBJECTIVE_GOTO_SHELTER_ON_FOOT, heading, pos); break; + case ATTRACTOR_ICECREAM: SetObjective(OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT, heading, pos); break; + default: return; + } + SetObjectiveTimer(time); + m_positionInQueue = qid; +} + +void +CPed::AttachPedToEntity(CEntity *ent, CVector offset, uint16 type, float rot, eWeaponType weapon) +{ + if (!ent || bInVehicle) + return; + + m_attachedTo = ent; + m_attachedTo->RegisterReference(&m_attachedTo); + m_vecAttachOffset = offset; + m_attachType = type; + m_attachRotStep = rot; + if (IsPlayer()) { + bUsesCollision = false; + } else if (ent->IsVehicle()) { + m_pCollidingEntity = ent; + } + + if (IsPlayer()) { + m_objective = OBJECTIVE_NONE; + m_prevObjective = OBJECTIVE_NONE; + } + SetStoredState(); + SetPedState(PED_IDLE); + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 1000.0f); + + if (m_storedWeapon == WEAPONTYPE_UNIDENTIFIED) { + m_storedWeapon = GetWeapon()->m_eWeaponType; + m_attachWepAmmo = GetWeapon()->m_nAmmoTotal; + } + if (IsPlayer()) { + GiveWeapon(weapon, 30000, 1); +#ifndef FIX_BUGS + ((CPlayerPed*)this)->m_nSelectedWepSlot = weapon; +#else + ((CPlayerPed*)this)->m_nSelectedWepSlot = GetWeaponSlot(weapon); +#endif + ((CPlayerPed*)this)->MakeChangesForNewWeapon(weapon); + TheCamera.SetNewPlayerWeaponMode(CCam::MODE_HELICANNON_1STPERSON, 0, 0); + SetPedState(PED_SNIPER_MODE); + } else { + GiveWeapon(weapon, 30000, true); + SetCurrentWeapon(weapon); + } + + PositionAttachedPed(); +} + +void +CPed::DettachPedFromEntity(void) +{ + CEntity* pVehicleAttachedTo = m_attachedTo; + m_attachedTo = nil; + if (m_nPedState == PED_DIE) { + m_pCollidingEntity = pVehicleAttachedTo; + ApplyMoveForce(pVehicleAttachedTo->GetForward() * -4.0f + CVector(0.0f, 0.0f, 4.0f)); + bIsStanding = false; + } else if (m_nPedState != PED_DEAD) { + RestorePreviousState(); + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 1000.0f); + bUsesCollision = true; + if (m_storedWeapon != WEAPONTYPE_UNIDENTIFIED) { + GetWeapon()->m_nAmmoInClip = 0; + GetWeapon()->m_nAmmoTotal = 0; + SetCurrentWeapon(m_storedWeapon); + GetWeapon()->m_nAmmoTotal = m_attachWepAmmo; + m_storedWeapon = WEAPONTYPE_UNIDENTIFIED; + } + } +} + +void +CPed::PositionAttachedPed() +{ + if(!m_attachedTo) + return; + + CMatrix rotMatrix, targetMat; + targetMat = m_attachedTo->GetMatrix(); + targetMat.GetPosition() += Multiply3x3(m_attachedTo->GetMatrix(), m_vecAttachOffset); + float objAngle = m_attachedTo->GetForward().Heading(); + + if (!IsPlayer()) { + float targetAngle = objAngle; + switch (m_attachType) { + case 1: + targetAngle += HALFPI; + break; + case 2: + targetAngle += PI; + break; + case 3: + targetAngle -= HALFPI; + break; + default: + break; + } + targetAngle = CGeneral::LimitRadianAngle(targetAngle); + m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); + float neededTurn = m_fRotationCur - targetAngle; + + if (neededTurn > PI) + neededTurn -= TWOPI; + else if (neededTurn < -PI) + neededTurn += TWOPI; + + if (neededTurn > m_attachRotStep) + m_fRotationCur = CGeneral::LimitRadianAngle(targetAngle + m_attachRotStep); + else if (-m_attachRotStep > neededTurn) + m_fRotationCur = CGeneral::LimitRadianAngle(targetAngle - m_attachRotStep); + else + m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); + } + rotMatrix.SetRotateZ(m_fRotationCur - objAngle); + targetMat = targetMat * rotMatrix; + GetMatrix() = targetMat; + if (m_attachedTo->IsVehicle() || m_attachedTo->IsObject()) { + m_vecMoveSpeed = ((CPhysical*)m_attachedTo)->m_vecMoveSpeed; + m_vecTurnSpeed = ((CPhysical*)m_attachedTo)->m_vecTurnSpeed; + } +} + +void +CPed::Undress(const char* name) +{ + int mi = GetModelIndex(); + CAnimBlendAssociation* pAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_PHONE_OUT); + if (pAnim) + FinishTalkingOnMobileCB(pAnim, this); + + DeleteRwObject(); + if (IsPlayer()) + mi = MI_PLAYER; + CStreaming::RequestSpecialModel(mi, name, STREAMFLAGS_DEPENDENCY | STREAMFLAGS_SCRIPTOWNED); + CWorld::Remove(this); +} + +void +CPed::Dress(void) +{ + int mi = GetModelIndex(); + m_modelIndex = -1; + SetModelIndex(mi); + m_nPedState = PED_IDLE; + m_nLastPedState = PED_NONE; + m_objective = OBJECTIVE_NONE; + m_prevObjective = OBJECTIVE_NONE; + m_nWaitState = WAITSTATE_FALSE; + CWorld::Add(this); + RestoreHeadingRate(); +} + +void +CPed::Say(uint16 audio, int32 time) +{ + if (m_delayedSoundID == -1) { + m_delayedSoundID = audio; + m_delayedSoundTimer = CTimer::GetTimeInMilliseconds() + time; + } +} + +#ifdef COMPATIBLE_SAVES +#define CopyFromBuf(buf, data) memcpy(&data, buf, sizeof(data)); SkipSaveBuf(buf, sizeof(data)); +#define CopyToBuf(buf, data) memcpy(buf, &data, sizeof(data)); SkipSaveBuf(buf, sizeof(data)); +void +CPed::Save(uint8*& buf) +{ + ZeroSaveBuf(buf, 52); + CopyToBuf(buf, GetPosition().x); + CopyToBuf(buf, GetPosition().y); + CopyToBuf(buf, GetPosition().z); + ZeroSaveBuf(buf, 288); + CopyToBuf(buf, CharCreatedBy); + ZeroSaveBuf(buf, 499); + CopyToBuf(buf, m_fHealth); + CopyToBuf(buf, m_fArmour); + ZeroSaveBuf(buf, 172); + for (int i = 0; i < 10; i++) // has to be hardcoded + m_weapons[i].Save(buf); + ZeroSaveBuf(buf, 252); +} + +void +CPed::Load(uint8*& buf) +{ + SkipSaveBuf(buf, 52); + CopyFromBuf(buf, GetMatrix().GetPosition().x); + CopyFromBuf(buf, GetMatrix().GetPosition().y); + CopyFromBuf(buf, GetMatrix().GetPosition().z); + SkipSaveBuf(buf, 288); + CopyFromBuf(buf, CharCreatedBy); + SkipSaveBuf(buf, 499); + CopyFromBuf(buf, m_fHealth); + CopyFromBuf(buf, m_fArmour); + SkipSaveBuf(buf, 172); + m_currentWeapon = WEAPONTYPE_UNARMED; + + CWeapon bufWeapon; + for (int i = 0; i < 10; i++) { // has to be hardcoded + bufWeapon.Load(buf); + + if (bufWeapon.m_eWeaponType != WEAPONTYPE_UNARMED) { + int modelId = CWeaponInfo::GetWeaponInfo(bufWeapon.m_eWeaponType)->m_nModelId; + if (modelId != -1) { + CStreaming::RequestModel(modelId, STREAMFLAGS_DEPENDENCY); + int modelId2 = CWeaponInfo::GetWeaponInfo(bufWeapon.m_eWeaponType)->m_nModel2Id; + if (modelId2 != -1) + CStreaming::RequestModel(modelId2, STREAMFLAGS_DEPENDENCY); + + CStreaming::LoadAllRequestedModels(false); + } + GiveWeapon(bufWeapon.m_eWeaponType, bufWeapon.m_nAmmoTotal, false); + } + } + SkipSaveBuf(buf, 252); +} +#undef CopyFromBuf +#undef CopyToBuf +#endif diff --git a/src/miami/peds/Ped.h b/src/miami/peds/Ped.h new file mode 100644 index 00000000..26cdf03f --- /dev/null +++ b/src/miami/peds/Ped.h @@ -0,0 +1,1192 @@ +#pragma once + +#include "RwHelper.h" +#include "AnimManager.h" +#include "Crime.h" +#include "EventList.h" +#include "PedIK.h" +#include "PedType.h" +#include "Physical.h" +#include "Weapon.h" +#include "WeaponInfo.h" +#include "PathFind.h" +#include "Collision.h" + +#define FEET_OFFSET 1.04f +#define CHECK_NEARBY_THINGS_MAX_DIST 15.0f +#define ENTER_CAR_MAX_DIST 30.0f +#define CAN_SEE_ENTITY_ANGLE_THRESHOLD DEGTORAD(60.0f) + +class CAccident; +class CObject; +class CFire; +struct AnimBlendFrameData; +class CAnimBlendAssociation; +class CPedAttractor; + +struct PedAudioData +{ + int m_nFixedDelayTime; + int m_nOverrideFixedDelayTime; + int m_nOverrideMaxRandomDelayTime; + int m_nMaxRandomDelayTime; +}; + +enum +{ + ATTACK_IN_PROGRESS, + CANT_ATTACK, + WATCH_UNTIL_HE_DISAPPEARS, +}; + +enum eFormation +{ + FORMATION_UNDEFINED, + FORMATION_REAR, + FORMATION_REAR_LEFT, + FORMATION_REAR_RIGHT, + FORMATION_FRONT_LEFT, + FORMATION_FRONT_RIGHT, + FORMATION_LEFT, + FORMATION_RIGHT, + FORMATION_FRONT +}; + +enum FightState { + FIGHTSTATE_MOVE_FINISHED = -2, + FIGHTSTATE_JUST_ATTACKED, + FIGHTSTATE_NO_MOVE, + FIGHTSTATE_1 +}; + +enum +{ + ENDFIGHT_NORMAL, + ENDFIGHT_WITH_A_STEP, + ENDFIGHT_FAST +}; + +enum PedRouteType +{ + PEDROUTE_STOP_WHEN_DONE = 1, + PEDROUTE_GO_BACKWARD_WHEN_DONE, + PEDROUTE_GO_TO_START_WHEN_DONE +}; + +enum FightMoveHitLevel +{ + HITLEVEL_NULL, + HITLEVEL_GROUND, + HITLEVEL_LOW, + HITLEVEL_MEDIUM, + HITLEVEL_HIGH +}; + +struct FightMove +{ + AnimationId animId; + float startFireTime; + float endFireTime; + float comboFollowOnTime; + float strikeRadius; + float extendReachMultiplier; + uint8 hitLevel; // FightMoveHitLevel + uint8 damage; + uint8 flags; +}; + +// TODO: This is eFightState on mobile. +enum PedFightMoves +{ + FIGHTMOVE_NULL, + // Attacker + FIGHTMOVE_STDPUNCH, + FIGHTMOVE_IDLE, + FIGHTMOVE_SHUFFLE_F, + FIGHTMOVE_KNEE, + FIGHTMOVE_PUNCHHOOK, + FIGHTMOVE_PUNCHJAB, + FIGHTMOVE_PUNCH, + FIGHTMOVE_LONGKICK, + FIGHTMOVE_ROUNDHOUSE, + // Directionals + FIGHTMOVE_FWDLEFT, + FIGHTMOVE_FWDRIGHT, + FIGHTMOVE_BACKKICK, + FIGHTMOVE_BACKFLIP, + FIGHTMOVE_BACKLEFT, + FIGHTMOVE_BACKRIGHT, + FIGHTMOVE_RIGHTSWEEP, + // Special + FIGHTMOVE_GROUNDKICK, + // Opponent + FIGHTMOVE_HITFRONT, + FIGHTMOVE_HITBACK, + FIGHTMOVE_HITRIGHT, + FIGHTMOVE_HITLEFT, + FIGHTMOVE_HITBODY, + FIGHTMOVE_HITCHEST, + FIGHTMOVE_HITHEAD, + FIGHTMOVE_HITBIGSTEP, + FIGHTMOVE_HITONFLOOR, + FIGHTMOVE_HITBEHIND, + FIGHTMOVE_MELEE1, + FIGHTMOVE_MELEE2, + FIGHTMOVE_MELEE3, + FIGHTMOVE_IDLE2NORM, + NUM_FIGHTMOVES +}; + +enum ePedPieceTypes +{ + PEDPIECE_TORSO, + PEDPIECE_MID, + PEDPIECE_LEFTARM, + PEDPIECE_RIGHTARM, + PEDPIECE_LEFTLEG, + PEDPIECE_RIGHTLEG, + PEDPIECE_HEAD, +}; + +enum eWaitState { + WAITSTATE_FALSE, + WAITSTATE_TRAFFIC_LIGHTS, + WAITSTATE_CROSS_ROAD, + WAITSTATE_CROSS_ROAD_LOOK, + WAITSTATE_LOOK_PED, + WAITSTATE_LOOK_SHOP, + WAITSTATE_LOOK_ACCIDENT, + WAITSTATE_FACEOFF_GANG, + WAITSTATE_DOUBLEBACK, + WAITSTATE_HITWALL, + WAITSTATE_TURN180, + WAITSTATE_SURPRISE, + WAITSTATE_STUCK, + WAITSTATE_LOOK_ABOUT, + WAITSTATE_PLAYANIM_DUCK, + WAITSTATE_PLAYANIM_COWER, + WAITSTATE_PLAYANIM_TAXI, + WAITSTATE_PLAYANIM_HANDSUP, + WAITSTATE_PLAYANIM_HANDSCOWER, + WAITSTATE_PLAYANIM_CHAT, + WAITSTATE_FINISH_FLEE, + WAITSTATE_SIT_DOWN, + WAITSTATE_SIT_DOWN_RVRS, + WAITSTATE_SIT_UP, + WAITSTATE_SIT_IDLE, + WAITSTATE_USE_ATM, + WAITSTATE_SUN_BATHE_PRE, + WAITSTATE_SUN_BATHE_DOWN, + WAITSTATE_SUN_BATHE_IDLE, + WAITSTATE_RIOT, + WAITSTATE_FAST_FALL, + WAITSTATE_BOMBER, + WAITSTATE_STRIPPER, + WAITSTATE_GROUND_ATTACK, + WAITSTATE_LANCESITTING, + WAITSTATE_PLAYANIM_HANDSUP_SIMPLE, +}; + +enum eObjective { + OBJECTIVE_NONE, + OBJECTIVE_WAIT_ON_FOOT, + OBJECTIVE_WAIT_ON_FOOT_FOR_COP, + OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE, + OBJECTIVE_GUARD_SPOT, + OBJECTIVE_GUARD_AREA, + OBJECTIVE_WAIT_IN_CAR, + OBJECTIVE_WAIT_IN_CAR_THEN_GET_OUT, + OBJECTIVE_KILL_CHAR_ON_FOOT, + OBJECTIVE_KILL_CHAR_ANY_MEANS, + OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, + OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS, + OBJECTIVE_GOTO_CHAR_ON_FOOT, + OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING, + OBJECTIVE_HASSLE_CHAR, + OBJECTIVE_FOLLOW_CHAR_IN_FORMATION, + OBJECTIVE_LEAVE_CAR, + OBJECTIVE_ENTER_CAR_AS_PASSENGER, + OBJECTIVE_ENTER_CAR_AS_DRIVER, + OBJECTIVE_FOLLOW_CAR_IN_CAR, + OBJECTIVE_FIRE_AT_OBJECT_FROM_VEHICLE, + OBJECTIVE_DESTROY_OBJECT, + OBJECTIVE_DESTROY_CAR, + OBJECTIVE_GOTO_AREA_ANY_MEANS, + OBJECTIVE_GOTO_AREA_ON_FOOT, + OBJECTIVE_RUN_TO_AREA, + OBJECTIVE_GOTO_AREA_IN_CAR, + OBJECTIVE_FOLLOW_CAR_ON_FOOT_WITH_OFFSET, + OBJECTIVE_GUARD_ATTACK, + OBJECTIVE_SET_LEADER, + OBJECTIVE_FOLLOW_ROUTE, + OBJECTIVE_SOLICIT_VEHICLE, + OBJECTIVE_HAIL_TAXI, + OBJECTIVE_CATCH_TRAIN, + OBJECTIVE_BUY_ICE_CREAM, + OBJECTIVE_STEAL_ANY_CAR, + OBJECTIVE_STEAL_ANY_MISSION_CAR, + OBJECTIVE_MUG_CHAR, + OBJECTIVE_LEAVE_CAR_AND_DIE, + OBJECTIVE_GOTO_SEAT_ON_FOOT, + OBJECTIVE_GOTO_ATM_ON_FOOT, + OBJECTIVE_FLEE_CAR, + OBJECTIVE_SUN_BATHE, + OBJECTIVE_GOTO_BUS_STOP_ON_FOOT, + OBJECTIVE_GOTO_PIZZA_ON_FOOT, + OBJECTIVE_GOTO_SHELTER_ON_FOOT, + OBJECTIVE_AIM_GUN_AT, + OBJECTIVE_WANDER, + OBJECTIVE_WAIT_ON_FOOT_AT_SHELTER, + OBJECTIVE_SPRINT_TO_AREA, + OBJECTIVE_KILL_CHAR_ON_BOAT, + OBJECTIVE_SOLICIT_FOOT, + OBJECTIVE_WAIT_ON_FOOT_AT_BUS_STOP, + OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT, + OBJECTIVE_WAIT_ON_FOOT_AT_ICE_CREAM_VAN, + OBJ_55, + OBJ_56, + OBJ_57, + OBJ_58, + OBJ_59 + +}; + +enum { + RANDOM_CHAR = 1, + MISSION_CHAR, + UNK_CHAR, +}; + +enum PedLineUpPhase { + LINE_UP_TO_CAR_START, + LINE_UP_TO_CAR_END, + LINE_UP_TO_CAR_2, // Buggy. Used for cops arresting you from passenger door + LINE_UP_TO_CAR_FALL +}; + +enum PedOnGroundState { + NO_PED, + PED_IN_FRONT_OF_ATTACKER, + PED_ON_THE_FLOOR, + PED_DEAD_ON_THE_FLOOR +}; + +enum PointBlankNecessity { + NO_POINT_BLANK_PED, + POINT_BLANK_FOR_WANTED_PED, + POINT_BLANK_FOR_SOMEONE_ELSE +}; + +enum PedState +{ + PED_NONE, + PED_IDLE, + PED_LOOK_ENTITY, + PED_LOOK_HEADING, + PED_WANDER_RANGE, + PED_WANDER_PATH, + PED_SEEK_POS, + PED_SEEK_ENTITY, + PED_FLEE_POS, + PED_FLEE_ENTITY, + PED_PURSUE, + PED_FOLLOW_PATH, + PED_SNIPER_MODE, + PED_ROCKET_MODE, + PED_DUMMY, + PED_PAUSE, + PED_ATTACK, + PED_FIGHT, + PED_FACE_PHONE, + PED_MAKE_CALL, + PED_CHAT, + PED_MUG, + PED_AIM_GUN, + PED_AI_CONTROL, + PED_SEEK_CAR, + PED_SEEK_IN_BOAT, + PED_FOLLOW_ROUTE, + PED_CPR, + PED_SOLICIT, + PED_BUY_ICECREAM, + PED_INVESTIGATE, + PED_STEP_AWAY, + PED_ON_FIRE, + PED_SUN_BATHE, + PED_FLASH, + PED_JOG, + PED_ANSWER_MOBILE, + + PED_UNKNOWN, // Same with IDLE, but also infects up to 5 peds with same pedType and WANDER_PATH, so they become stone too. HANG_OUT in Fire_Head's idb + + PED_STATES_NO_AI, + + PED_ABSEIL, + PED_SIT, + PED_JUMP, + PED_FALL, + PED_GETUP, + PED_STAGGER, + PED_DIVE_AWAY, + + PED_STATES_NO_ST, + PED_ENTER_TRAIN, + PED_EXIT_TRAIN, + PED_ARREST_PLAYER, + + PED_DRIVING, + PED_PASSENGER, + PED_TAXI_PASSENGER, + PED_OPEN_DOOR, + PED_DIE, + PED_DEAD, + PED_CARJACK, + PED_DRAG_FROM_CAR, + PED_ENTER_CAR, + PED_STEAL_CAR, + PED_EXIT_CAR, + PED_HANDS_UP, + PED_ARRESTED, + PED_DEPLOY_STINGER +}; + +enum eMoveState { + PEDMOVE_NONE, + PEDMOVE_STILL, + PEDMOVE_WALK, + PEDMOVE_JOG, + PEDMOVE_RUN, + PEDMOVE_SPRINT, + PEDMOVE_THROWN +}; + +extern float gfTommyFatness; + +class CVehicle; + +class CPed : public CPhysical +{ +public: +#ifdef USE_CUTSCENE_SHADOW_FOR_PED + class CCutsceneShadow *m_pRTShadow; +#endif + // 0x128 + CStoredCollPoly m_collPoly; + float m_fCollisionSpeed; + + // cf. https://github.com/DK22Pac/plugin-sdk/blob/master/plugin_sa/game_sa/CPed.h from R* + uint32 bIsStanding : 1; + uint32 bWasStanding : 1; + uint32 bIsAttacking : 1; // doesn't reset after fist fight + uint32 bIsPointingGunAt : 1; + uint32 bIsLooking : 1; + uint32 bKeepTryingToLook : 1; // if we can't look somewhere due to unreachable angles + uint32 bIsRestoringLook : 1; + uint32 bIsAimingGun : 1; + + uint32 bIsRestoringGun : 1; + uint32 bCanPointGunAtTarget : 1; + uint32 bIsTalking : 1; + uint32 bIsInTheAir : 1; + uint32 bIsLanding : 1; + uint32 bIsRunning : 1; // on some conditions + uint32 bHitSomethingLastFrame : 1; + uint32 bVehEnterDoorIsBlocked : 1; // because someone else enters/exits from there + + uint32 bCanPedEnterSeekedCar : 1; + uint32 bRespondsToThreats : 1; + uint32 bRenderPedInCar : 1; + uint32 bChangedSeat : 1; + uint32 bUpdateAnimHeading : 1; + uint32 bBodyPartJustCameOff : 1; + uint32 bIsShooting : 1; + uint32 bFindNewNodeAfterStateRestore : 1; + + uint32 bHasACamera : 1; // does ped possess a camera to document accidents involves fire/explosion + uint32 bGonnaInvestigateEvent : 1; + uint32 bPedIsBleeding : 1; + uint32 bStopAndShoot : 1; // Ped cannot reach target to attack with fist, need to use gun + uint32 bIsPedDieAnimPlaying : 1; + uint32 bUsePedNodeSeek : 1; + uint32 bObjectiveCompleted : 1; + uint32 bScriptObjectiveCompleted : 1; + + uint32 bKindaStayInSamePlace : 1; + uint32 bBeingChasedByPolice : 1; + uint32 bNotAllowedToDuck : 1; + uint32 bCrouchWhenShooting : 1; + uint32 bIsDucking : 1; + uint32 bGetUpAnimStarted : 1; + uint32 bDoBloodyFootprints : 1; + uint32 bFleeAfterExitingCar : 1; + + uint32 bWanderPathAfterExitingCar : 1; + uint32 bIsLeader : 1; + uint32 bDontDragMeOutCar : 1; // unfinished feature + uint32 m_ped_flagF8 : 1; + uint32 bWillBeQuickJacked : 1; + uint32 bCancelEnteringCar : 1; // after door is opened or couldn't be opened due to it's locked + uint32 bObstacleShowedUpDuringKillObjective : 1; + uint32 bDuckAndCover : 1; + + uint32 bStillOnValidPoly : 1; // set if the polygon the ped is on is still valid for collision + uint32 bAllowMedicsToReviveMe : 1; + uint32 bResetWalkAnims : 1; + uint32 bStartWanderPathOnFoot : 1; // exits the car if he's in it, reset after path found + uint32 bOnBoat : 1; // not just driver, may be just standing + uint32 bBusJacked : 1; + uint32 bGonnaKillTheCarJacker : 1; // only set when car is jacked from right door and when arrested by police + uint32 bFadeOut : 1; + + uint32 bKnockedUpIntoAir : 1; // has ped been knocked up into the air by a car collision + uint32 bHitSteepSlope : 1; // has ped collided/is standing on a steep slope (surface type) + uint32 bCullExtraFarAway : 1; // special ped only gets culled if it's extra far away (for roadblocks) + uint32 bClearObjective : 1; + uint32 bTryingToReachDryLand : 1; // has ped just exited boat and trying to get to dry land + uint32 bCollidedWithMyVehicle : 1; + uint32 bRichFromMugging : 1; // ped has lots of cash cause they've been mugging people + uint32 bChrisCriminal : 1; // Is a criminal as killed during Chris' police mission (should be counted as such) + + uint32 bShakeFist : 1; // test shake hand at look entity + uint32 bNoCriticalHits : 1; // if set, limbs won't came off + uint32 bVehExitWillBeInstant : 1; + uint32 bHasAlreadyBeenRecorded : 1; + uint32 bFallenDown : 1; + uint32 bDontAcceptIKLookAts : 1; + uint32 bReachedAttractorHeadingTarget : 1; + uint32 bTurnedAroundOnAttractor : 1; + + uint32 bHasAlreadyUsedAttractor : 1; + uint32 bHasAlreadyStoleACar : 1; + uint32 bCarPassenger : 1; + uint32 bFleeWhenStanding : 1; + uint32 bGotUpOfMyOwnAccord : 1; + uint32 bMiamiViceCop : 1; + uint32 bMoneyHasBeenGivenByScript : 1; // + uint32 bHasBeenPhotographed : 1; // + + uint32 bIsDrowning : 1; + uint32 bDrownsInWater : 1; + uint32 bWaitForLeaderToComeCloser : 1; + uint32 bHeldHostageInCar : 1; + uint32 bIsPlayerFriend : 1; + uint32 bHeadStuckInCollision : 1; + uint32 bDeadPedInFrontOfCar : 1; + uint32 bStayInCarOnJack : 1; + + uint32 bDontFight : 1; + uint32 bDoomAim : 1; + uint32 bCanBeShotInVehicle : 1; + uint32 bCanGiveUpSunbathing : 1; + uint32 bMakeFleeScream : 1; + uint32 bPushedAlongByCar : 1; + uint32 bRemoveMeWhenIGotIntoCar : 1; + uint32 bIgnoreThreatsBehindObjects : 1; + + uint32 bNeverEverTargetThisPed : 1; + uint32 bCrouchWhenScared : 1; + uint32 bKnockedOffBike : 1; + uint32 b158_8 : 1; + uint32 bCollectBusFare : 1; + uint32 bBoughtIceCream : 1; + uint32 bDonePositionOutOfCollision : 1; + uint32 bCanAttackPlayerWithCops : 1; + +#ifdef KANGAROO_CHEAT + // our own flags + uint32 m_ped_flagI80 : 1; // KANGAROO_CHEAT define makes use of this as cheat toggle +#endif + + uint8 m_gangFlags; + uint8 m_unused15D; // these 3 can't be padding but had to actually have been members ... + uint8 m_unused15E; + uint8 m_unused15F; + uint8 CharCreatedBy; + eObjective m_objective; + eObjective m_prevObjective; + CPed *m_pedInObjective; + CVehicle *m_carInObjective; + CVector m_nextRoutePointPos; + float m_attractorHeading; + CPed *m_leader; + eFormation m_pedFormation; + uint32 m_fearFlags; + CEntity *m_threatEntity; + CVector2D m_eventOrThreat; + uint32 m_eventType; + CEntity* m_pEventEntity; + float m_fAngleToEvent; + AnimBlendFrameData *m_pFrames[PED_NODE_MAX]; + RpAtomic *m_pWeaponModel; + AssocGroupId m_animGroup; + CAnimBlendAssociation *m_pVehicleAnim; + CVector2D m_vecAnimMoveDelta; + CVector m_vecOffsetSeek; + CPedIK m_pedIK; + float m_actionX; + float m_actionY; + uint32 m_nPedStateTimer; + PedState m_nPedState; + PedState m_nLastPedState; + eMoveState m_nMoveState; + int32 m_nStoredMoveState; + int32 m_nPrevMoveState; + eWaitState m_nWaitState; + uint32 m_nWaitTimer; + CPathNode* m_pathNodesToGo[8]; + int16 m_nNumPathNodes; + int16 m_nCurPathNodeId; + CEntity* m_followPathWalkAroundEnt; + CEntity* m_followPathTargetEnt; + uint32 m_pathNodeTimer; + CPathNode m_pathNodeObjPool[8]; + CPathNode* m_pCurPathNode; + int8 m_nPathDir; + CPathNode* m_pLastPathNode; + CPathNode* m_pNextPathNode; + CVector m_followPathDestPos; + float m_followPathAbortDist; + eMoveState m_followPathMoveState; + float m_fHealth; + float m_fArmour; + uint32 m_nExtendedRangeTimer; + int16 m_routeLastPoint; + uint16 m_routeStartPoint; + int16 m_routePointsPassed; + int16 m_routeType; // See PedRouteType + int16 m_routePointsBeingPassed; + CVector2D m_moved; + float m_fRotationCur; + float m_fRotationDest; + float m_headingRate; + uint16 m_vehDoor; + int16 m_walkAroundType; + CPhysical *m_pCurrentPhysSurface; + CVector m_vecOffsetFromPhysSurface; + CEntity *m_pCurSurface; + CVector m_vecSeekPos; + CEntity *m_pSeekTarget; + CVehicle *m_pMyVehicle; + bool bInVehicle; + float m_distanceToCountSeekDone; + float m_acceptableHeadingOffset; + CVehicle* m_vehicleInAccident; + CPedAttractor* m_attractor; + int32 m_positionInQueue; + bool bRunningToPhone; + int16 m_phoneId; + eCrimeType m_crimeToReportOnPhone; + uint32 m_phoneTalkTimer; + CAccident *m_lastAccident; + uint32 m_nPedType; + CPedStats *m_pedStats; + CVector2D m_fleeFromPos; + CEntity *m_fleeFrom; + uint32 m_fleeTimer; + CEntity* m_threatEx; // TODO(Miami): What is this? + CEntity* m_collidingEntityWhileFleeing; + uint32 m_collidingThingTimer; + CEntity *m_pCollidingEntity; + uint8 m_stateUnused; + uint32 m_timerUnused; + class CRange2D *m_wanderRangeBounds; + CWeapon m_weapons[TOTAL_WEAPON_SLOTS]; + eWeaponType m_storedWeapon; + eWeaponType m_delayedWeapon; + uint32 m_delayedWeaponAmmo; + uint8 m_currentWeapon; // eWeaponType + uint8 m_maxWeaponTypeAllowed; // eWeaponType + uint8 m_wepSkills; + uint8 m_wepAccuracy; + CEntity *m_pPointGunAt; + CVector m_vecHitLastPos; + uint32 m_curFightMove; + uint32 m_lastFightMove; + uint8 m_fightButtonPressure; + int8 m_fightState; + bool m_takeAStepAfterAttack; + uint8 m_bleedCounter; + CFire *m_pFire; + CEntity *m_pLookTarget; + float m_fLookDirection; + int32 m_wepModelID; + uint32 m_leaveCarTimer; + uint32 m_getUpTimer; + uint32 m_lookTimer; + uint32 m_chatTimer; + uint32 m_attackTimer; + uint32 m_shootTimer; // shooting is a part of attack + uint32 m_carJackTimer; + uint32 m_objectiveTimer; + uint32 m_duckTimer; + uint32 m_duckAndCoverTimer; + uint32 m_bloodyFootprintCountOrDeathTime; // Death time when bDoBloodyFootprints is false. Weird decision + uint32 m_shotTime; + uint32 m_ceaseAttackTimer; + uint8 m_panicCounter; + bool m_deadBleeding; + int8 m_bodyPartBleeding; // PedNode, but -1 if there isn't + CPed *m_nearPeds[10]; + uint16 m_numNearPeds; + uint16 m_nPedMoney; + int8 m_lastWepDam; + CEntity *m_lastDamEntity; + CEntity *m_attachedTo; + CVector m_vecAttachOffset; + uint16 m_attachType; + float m_attachRotStep; + uint32 m_attachWepAmmo; + uint32 m_threatFlags; + uint32 m_threatCheckTimer; + uint32 m_threatCheckInterval; + int32 m_delayedSoundID; + uint32 m_delayedSoundTimer; + uint32 m_lastSoundStart; + uint32 m_soundStart; + uint16 m_lastQueuedSound; + uint16 m_queuedSound; + bool m_canTalk; + uint32 m_lastComment; + CVector m_vecSpotToGuard; + float m_radiusToGuard; + + static void *operator new(size_t) throw(); + static void *operator new(size_t, int) throw(); + static void operator delete(void*, size_t) throw(); + static void operator delete(void*, int) throw(); + + CPed(uint32 pedType); + ~CPed(void); + + void DeleteRwObject(); + void SetModelIndex(uint32 mi); + void ProcessControl(void); + void Teleport(CVector); + void PreRender(void); + void Render(void); + bool SetupLighting(void); + void RemoveLighting(bool); + void FlagToDestroyWhenNextProcessed(void); + int32 ProcessEntityCollision(CEntity*, CColPoint*); + + virtual void SetMoveAnim(void); + + void AddWeaponModel(int id); + void AimGun(void); + void KillPedWithCar(CVehicle *veh, float impulse); + void Say(uint16 audio); + void Say(uint16 audio, int32 time); + void SetLookFlag(CEntity* target, bool keepTryingToLook, bool cancelPrevious = false); + void SetLookFlag(float direction, bool keepTryingToLook, bool cancelPrevious = false); + void SetLookTimer(int time); + void SetDie(AnimationId anim = ANIM_STD_KO_FRONT, float arg1 = 4.0f, float arg2 = 0.0f); + void SetDead(void); + void ApplyHeadShot(eWeaponType weaponType, CVector pos, bool evenOnPlayer); + void RemoveBodyPart(PedNode nodeId, int8 direction); + bool OurPedCanSeeThisOne(CEntity *target, bool shootablesDoBlock = false); + void Avoid(void); + void Attack(void); + void ClearAimFlag(void); + void ClearLookFlag(void); + void RestorePreviousState(void); + void ClearAttack(void); + bool IsPedHeadAbovePos(float zOffset); + void RemoveWeaponModel(int modelId); + void SetCurrentWeapon(eWeaponType weaponType); + void SetCurrentWeapon(int weapon); + void Duck(void); + void ClearDuck(bool = false); + void ClearPointGunAt(void); + void BeingDraggedFromCar(void); + void RestartNonPartialAnims(void); + void LineUpPedWithCar(PedLineUpPhase phase); + void SetPedPositionInCar(void); + void PlayFootSteps(void); + void QuitEnteringCar(void); + void BuildPedLists(void); + int32 GiveWeapon(eWeaponType weaponType, uint32 ammo, bool unused = true); + void CalculateNewOrientation(void); + float WorkOutHeadingForMovingFirstPerson(float); + void CalculateNewVelocity(void); + bool CanSeeEntity(CEntity*, float threshold = CAN_SEE_ENTITY_ANGLE_THRESHOLD); + void RestorePreviousObjective(void); + void SetIdle(void); +#ifdef _MSC_VER +#if _MSC_VER >= 1920 && _MSC_VER < 1929 + __declspec(noinline) // workaround for a compiler bug, hooray MS :P +#endif +#endif + void SetObjective(eObjective, void*); + void SetObjective(eObjective); + void SetObjective(eObjective, int16, int16); + void SetObjective(eObjective, CVector); + void SetObjective(eObjective, float, const CVector&); + void ClearChat(void); + void InformMyGangOfAttack(CEntity*); + void ReactToAttack(CEntity*); + void SetDuck(uint32, bool = false); + void RegisterThreatWithGangPeds(CEntity*); + bool TurnBody(void); + void Chat(void); + void CheckAroundForPossibleCollisions(void); + void SetSeek(CVector, float); + void SetSeek(CEntity*, float); + bool MakePhonecall(void); + bool FacePhone(void); + CPed *CheckForDeadPeds(void); + bool CheckForExplosions(CVector2D &area); + CPed *CheckForGunShots(void); + uint8 CheckForPointBlankPeds(CPed*); + bool CheckIfInTheAir(void); + void ClearAll(void); + void SetPointGunAt(CEntity*); + bool Seek(void); + bool SetWanderPath(int8); + bool SetFollowPath(CVector dest, float radius, eMoveState state, CEntity*, CEntity*, int); + bool SetFollowPathStatic(void); + bool SetFollowPathDynamic(void); + void ClearAttackByRemovingAnim(void); + void SetStoredState(void); + void StopNonPartialAnims(void); + bool InflictDamage(CEntity*, eWeaponType, float, ePedPieceTypes, uint8); + void ClearFlee(void); + void ClearFall(void); + void SetGetUp(void); + void ClearInvestigateEvent(void); + void ClearLeader(void); + void ClearLook(void); + void ClearObjective(void); + void ClearPause(void); + void ClearSeek(void); + void ClearWeapons(void); + void RestoreGunPosition(void); + void RestoreHeadingRate(void); + void SetAimFlag(CEntity* to); + void SetAimFlag(float angle); + void SetAmmo(eWeaponType weaponType, uint32 ammo); + void SetEvasiveStep(CPhysical*, uint8); + void GrantAmmo(eWeaponType, uint32); + void SetEvasiveDive(CPhysical*, uint8); + void SetAttack(CEntity*); + void StartFightAttack(uint8); + void SetWaitState(eWaitState, void*); + bool FightStrike(CVector&, bool); + void FightHitPed(CPed*, CVector&, CVector&, int16); + int32 ChooseAttackPlayer(uint8, bool); + int32 ChooseAttackAI(uint8, bool); + int GetLocalDirection(const CVector2D &); + void StartFightDefend(uint8, uint8, uint8); + void PlayHitSound(CPed*); + void SetFall(int, AnimationId, uint8); + void SetFlee(CEntity*, int); + void SetFlee(CVector2D const &, int); + void RemoveDrivebyAnims(void); + void RemoveInCarAnims(void); + void CollideWithPed(CPed*); + void SetDirectionToWalkAroundObject(CEntity*); + bool SetDirectionToWalkAroundVehicle(CVehicle*); + void RemoveWeaponAnims(int, float); + void CreateDeadPedMoney(void); + void CreateDeadPedWeaponPickups(void); + void CreateDeadPedPickupCoors(float *x, float *y, float *z); + void SetAttackTimer(uint32); + void SetBeingDraggedFromCar(CVehicle*, uint32, bool); + void SetRadioStation(void); + void SetBuyIceCream(void); + void SetChat(CEntity*, uint32); + void DeadPedMakesTyresBloody(void); + void MakeTyresMuddySectorList(CPtrList&); + bool DuckAndCover(void); + void EndFight(uint8); + void EnterCar(void); + uint8 GetNearestTrainPedPosition(CVehicle*, CVector&); + uint8 GetNearestTrainDoor(CVehicle*, CVector&); + void ExitCar(void); + void Fight(void); + bool FindBestCoordsFromNodes(CVector, CVector*); + void Wait(void); + void ProcessObjective(void); + CVector *SeekFollowingPath(void); + void Flee(void); + void FollowPath(void); + CVector GetFormationPosition(void); + void GetNearestDoor(CVehicle*, CVector&); + bool GetNearestPassengerDoor(CVehicle*, CVector&); + int GetNextPointOnRoute(void); + int GetWeaponSlot(eWeaponType); + bool CanWeRunAndFireWithWeapon(void); + void GoToNearestDoor(CVehicle*); + bool HaveReachedNextPointOnRoute(float); + void Idle(void); + void InTheAir(void); + void SetLanding(void); + void InvestigateEvent(void); + bool IsPedDoingDriveByShooting(void); + bool IsRoomToBeCarJacked(void); + void SetInvestigateEvent(eEventType, CVector2D, float, uint16, float); + bool LookForInterestingNodes(void); + void LookForSexyCars(void); + void LookForSexyPeds(void); + void Mug(void); + void MoveHeadToLook(void); + void Pause(void); + void ProcessBuoyancy(void); + void ServiceTalking(void); + void SetJump(void); + void WanderPath(void); + void ReactToPointGun(CEntity*); + void SeekCar(void); + bool PositionPedOutOfCollision(void); + bool PositionAnyPedOutOfCollision(void); + bool RunToReportCrime(eCrimeType); + bool PlacePedOnDryLand(void); + bool PossiblyFindBetterPosToSeekCar(CVector*, CVehicle*); + void UpdateFromLeader(void); + uint32 ScanForThreats(void); + void SetEnterCar(CVehicle*, uint32); + bool WarpPedToNearEntityOffScreen(CEntity*); + void SetExitCar(CVehicle*, uint32); + void SetFormation(eFormation); + bool WillChat(CPed*); + void SetEnterCar_AllClear(CVehicle*, uint32, uint32); + void SetSolicit(uint32 time); + void ScanForInterestingStuff(void); + void WarpPedIntoCar(CVehicle*); + void SetCarJack(CVehicle*); + bool WarpPedToNearLeaderOffScreen(void); + void Solicit(void); + void SetExitBoat(CVehicle*); + void ClearFollowPath(); + void GiveDelayedWeapon(eWeaponType weapon, uint32 ammo); + void RequestDelayedWeapon(); + void AddInCarAnims(CVehicle* car, bool isDriver); + bool CanBeDamagedByThisGangMember(CPed*); + void AnswerMobile(void); + void BuyIceCream(void); + void CheckThreatValidity(void); + void ClearAnswerMobile(void); + void SetAnswerMobile(void); + void AttachPedToEntity(CEntity*, CVector, uint16, float, eWeaponType); + void DettachPedFromEntity(); + void PedShuffle(); + void DriveVehicle(); + void PositionAttachedPed(); + bool CanUseTorsoWhenLooking(); + void ScanForDelayedResponseThreats(); + + // Static methods + static CVector GetLocalPositionToOpenCarDoor(CVehicle *veh, uint32 component, float offset); + static CVector GetPositionToOpenCarDoor(CVehicle *veh, uint32 component, float seatPosMult); + static CVector GetPositionToOpenCarDoor(CVehicle* veh, uint32 component); + static void Initialise(void); + static void SetAnimOffsetForEnterOrExitVehicle(void); + static void LoadFightData(void); + + // Callbacks + static void PedGetupCB(CAnimBlendAssociation *assoc, void *arg); + static void PedStaggerCB(CAnimBlendAssociation *assoc, void *arg); + static void PedEvadeCB(CAnimBlendAssociation *assoc, void *arg); + static void FinishDieAnimCB(CAnimBlendAssociation *assoc, void *arg); + static void FinishedWaitCB(CAnimBlendAssociation *assoc, void *arg); + static void FinishLaunchCB(CAnimBlendAssociation *assoc, void *arg); + static void FinishHitHeadCB(CAnimBlendAssociation *assoc, void *arg); + static void PedAnimGetInCB(CAnimBlendAssociation *assoc, void *arg); + static void PedAnimDoorOpenCB(CAnimBlendAssociation *assoc, void *arg); + static void PedAnimPullPedOutCB(CAnimBlendAssociation *assoc, void *arg); + static void PedAnimDoorCloseCB(CAnimBlendAssociation *assoc, void *arg); + static void PedSetInCarCB(CAnimBlendAssociation *assoc, void *arg); + static void PedSetOutCarCB(CAnimBlendAssociation *assoc, void *arg); + static void PedAnimAlignCB(CAnimBlendAssociation *assoc, void *arg); + static void PedSetDraggedOutCarCB(CAnimBlendAssociation *assoc, void *arg); + static void PedAnimStepOutCarCB(CAnimBlendAssociation *assoc, void *arg); + static void PedSetInTrainCB(CAnimBlendAssociation *assoc, void *arg); +#ifdef GTA_TRAIN + static void PedSetOutTrainCB(CAnimBlendAssociation *assoc, void *arg); +#endif + static void FinishedAttackCB(CAnimBlendAssociation *assoc, void *arg); + static void FinishedReloadCB(CAnimBlendAssociation *assoc, void *arg); + static void FinishFightMoveCB(CAnimBlendAssociation *assoc, void *arg); + static void PedAnimDoorCloseRollingCB(CAnimBlendAssociation *assoc, void *arg); + static void FinishJumpCB(CAnimBlendAssociation *assoc, void *arg); + static void PedLandCB(CAnimBlendAssociation *assoc, void *arg); + static void RestoreHeadingRateCB(CAnimBlendAssociation *assoc, void *arg); + static void PedSetQuickDraggedOutCarPositionCB(CAnimBlendAssociation *assoc, void *arg); + static void PedSetDraggedOutCarPositionCB(CAnimBlendAssociation *assoc, void *arg); + static void DeleteSunbatheIdleAnimCB(CAnimBlendAssociation *assoc, void *arg); + static void PedSetPreviousStateCB(CAnimBlendAssociation *assoc, void *arg); + static void PedAnimShuffleCB(CAnimBlendAssociation *assoc, void *arg); + static void PedSetGetInCarPositionCB(CAnimBlendAssociation* assoc, void* arg); + + bool IsPlayer(void) const; + bool IsFemale(void) { return m_nPedType == PEDTYPE_CIVFEMALE || m_nPedType == PEDTYPE_PROSTITUTE; } + bool UseGroundColModel(void); + bool CanSetPedState(void); + bool IsPedInControl(void); + bool CanPedDriveOff(void); + bool CanBeDeleted(void); + bool CanBeDeletedEvenInVehicle(void); + bool CanStrafeOrMouseControl(void); + bool CanPedReturnToState(void); + void SetMoveState(eMoveState); + bool IsTemporaryObjective(eObjective objective); + void SetObjectiveTimer(int); + bool SelectGunIfArmed(void); + bool IsPointerValid(void); + void SortPeds(CPed**, int, int); + void ForceStoredObjective(eObjective); + void SetStoredObjective(void); + void SetLeader(CEntity* leader); + void SetPedStats(ePedStats); + bool IsGangMember(void) const; + void Die(void); +#ifdef GTA_TRAIN + void EnterTrain(void); + void ExitTrain(void); + void SetExitTrain(CVehicle*); + void SetPedPositionInTrain(void); + void LineUpPedWithTrain(void); + void SetEnterTrain(CVehicle*, uint32); +#endif + void Fall(void); + bool IsPedShootable(void); + void Look(void); + void SetInTheAir(void); + void RestoreHeadPosition(void); + void PointGunAt(void); + bool ServiceTalkingWhenDead(void); + void SetShootTimer(uint32); + void SetSeekCar(CVehicle*, uint32); + void SetSeekBoatPosition(CVehicle*); + void WanderRange(void); + void SetFollowRoute(int16, int16); + void SeekBoatPosition(void); + void UpdatePosition(void); + CObject *SpawnFlyingComponent(int, int8); + void SetCarJack_AllClear(CVehicle*, uint32, uint32); + bool CanPedJumpThis(CEntity *unused, CVector *damageNormal = nil); + void SetNewAttraction(CPedAttractor* pAttractor, const CVector& pos, float, float, int); + void ClearWaitState(void); + void Undress(const char*); + void Dress(void); + int32 KillCharOnFootMelee(CVector&, CVector&, CVector&); + int32 KillCharOnFootArmed(CVector&, CVector&, CVector&); + void SetLook(CEntity* to); + void SetLook(float direction); + + bool HasWeaponSlot(uint8 slot) { return m_weapons[slot].m_eWeaponType != WEAPONTYPE_UNARMED; } + CWeapon& GetWeapon(uint8 slot) { return m_weapons[slot]; } + CWeapon *GetWeapon(void) { return &m_weapons[m_currentWeapon]; } + + PedState GetPedState(void) { return m_nPedState; } + void SetPedState(PedState state) + { + if (GetPedState() == PED_FOLLOW_PATH && state != PED_FOLLOW_PATH) + ClearFollowPath(); + m_nPedState = state; + } + bool Dead(void) { return m_nPedState == PED_DEAD; } + bool Dying(void) { return m_nPedState == PED_DIE; } + bool DyingOrDead(void) { return m_nPedState == PED_DIE || m_nPedState == PED_DEAD; } + bool OnGround(void) { return m_nPedState == PED_FALL || m_nPedState == PED_DIE || m_nPedState == PED_DEAD; } + bool OnGroundOrGettingUp(void) { return OnGround() || m_nPedState == PED_GETUP; } + + bool Driving(void) { return m_nPedState == PED_DRIVING; } + bool InVehicle(void) { return bInVehicle && m_pMyVehicle; } // True when ped is sitting/standing in vehicle, not in enter/exit state. + bool EnteringCar(void) { return m_nPedState == PED_ENTER_CAR || m_nPedState == PED_CARJACK; } + bool HasAttractor(void); + bool IsUseAttractorObjective(eObjective obj) { + return obj == OBJECTIVE_GOTO_ATM_ON_FOOT || obj == OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT || + obj == OBJECTIVE_GOTO_PIZZA_ON_FOOT || obj == OBJECTIVE_GOTO_SEAT_ON_FOOT || + obj == OBJECTIVE_GOTO_SHELTER_ON_FOOT || obj == OBJECTIVE_GOTO_BUS_STOP_ON_FOOT; + } + + void ReplaceWeaponWhenExitingVehicle(void); + void RemoveWeaponWhenEnteringVehicle(void); + bool IsNotInWreckedVehicle() + { + return m_pMyVehicle != nil && ((CEntity*)m_pMyVehicle)->GetStatus() != STATUS_WRECKED; + } + + // My names. Inlined in VC + AnimationId GetFireAnimNotDucking(CWeaponInfo* weapon) { + if (m_nPedType == PEDTYPE_COP && weapon->IsFlagSet(WEAPONFLAG_COP3_RD)) + return Get3rdFireAnim(weapon); + else + return GetPrimaryFireAnim(weapon); + } + + static AnimationId Get3rdFireAnim(CWeaponInfo* weapon) { + if (weapon->IsFlagSet(WEAPONFLAG_COP3_RD)) + return ANIM_WEAPON_FIRE_3RD; + else + return (AnimationId)0; + } + + static AnimationId GetFireAnimGround(CWeaponInfo* weapon, bool kickFloorIfNone = true) { + if (weapon->IsFlagSet(WEAPONFLAG_GROUND_2ND)) + return ANIM_WEAPON_CROUCHFIRE; + else if (weapon->IsFlagSet(WEAPONFLAG_GROUND_3RD)) + return ANIM_WEAPON_FIRE_3RD; + else if (kickFloorIfNone) + return ANIM_STD_KICKGROUND; + else + return (AnimationId)0; + } + + static AnimationId GetPrimaryFireAnim(CWeaponInfo* weapon) { + if (weapon->IsFlagSet(WEAPONFLAG_ANIMDETONATE)) + return ANIM_STD_DETONATE; + else + return ANIM_WEAPON_FIRE; + } + + static AnimationId GetCrouchReloadAnim(CWeaponInfo* weapon) { + if (weapon->IsFlagSet(WEAPONFLAG_RELOAD)) + return ANIM_WEAPON_CROUCHRELOAD; + else + return (AnimationId)0; + } + + static AnimationId GetCrouchFireAnim(CWeaponInfo* weapon) { + if (weapon->IsFlagSet(WEAPONFLAG_CROUCHFIRE)) + return ANIM_WEAPON_CROUCHFIRE; + else + return (AnimationId)0; + } + + static AnimationId GetReloadAnim(CWeaponInfo* weapon) { + if (weapon->IsFlagSet(WEAPONFLAG_RELOAD)) + return ANIM_WEAPON_RELOAD; + else + return (AnimationId)0; + } + + static AnimationId GetFightIdleWithMeleeAnim(CWeaponInfo* weapon) { + if (weapon->IsFlagSet(WEAPONFLAG_FIGHTMODE)) + return ANIM_MELEE_IDLE_FIGHTMODE; + else + return (AnimationId)0; + } + + static AnimationId GetFinishingAttackAnim(CWeaponInfo* weapon) { + if (weapon->IsFlagSet(WEAPONFLAG_FINISH_3RD)) + return ANIM_MELEE_ATTACK_FINISH; + else + return (AnimationId)0; + } + + static AnimationId GetSecondFireAnim(CWeaponInfo* weapon) { + if (weapon->IsFlagSet(WEAPONFLAG_USE_2ND)) + return ANIM_WEAPON_FIRE_2ND; + else + return (AnimationId)0; + } + + static AnimationId GetMeleeStartAnim(CWeaponInfo* weapon) { + if (weapon->IsFlagSet(WEAPONFLAG_PARTIALATTACK)) + return ANIM_MELEE_ATTACK_START; + else + return (AnimationId)0; + } + + static AnimationId GetThrowAnim(CWeaponInfo *weapon) + { + if (weapon->IsFlagSet(WEAPONFLAG_THROW)) + return ANIM_THROWABLE_START_THROW; + else + return (AnimationId)0; + } + // -- + + // My additions, because there were many, many instances of that. + inline void SetFindPathAndFlee(CEntity *fleeFrom, int time, bool walk = false) + { + SetFlee(fleeFrom, time); + bUsePedNodeSeek = true; + m_pNextPathNode = nil; + if (walk) + SetMoveState(PEDMOVE_WALK); + } + + inline void SetFindPathAndFlee(CVector2D const &from, int time, bool walk = false) + { + SetFlee(from, time); + bUsePedNodeSeek = true; + m_pNextPathNode = nil; + if (walk) + SetMoveState(PEDMOVE_WALK); + } + // -- + + inline void SetWeaponLockOnTarget(CEntity *target) + { + if (m_pPointGunAt) + m_pPointGunAt->CleanUpOldReference(&m_pPointGunAt); + + m_pPointGunAt = (CPed*)target; + if (target) + ((CEntity*)target)->RegisterReference(&m_pPointGunAt); + } + + // Using this to abstract nodes of skinned and non-skinned meshes + CVector GetNodePosition(int32 node) + { + RwV3d pos = { 0.0f, 0.0f, 0.0f }; + RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(GetClump()); + int32 idx = RpHAnimIDGetIndex(hier, m_pFrames[node]->nodeID); + RwMatrix *mats = RpHAnimHierarchyGetMatrixArray(hier); + pos = mats[idx].pos; + return pos; + } + void TransformToNode(CVector &pos, int32 node) + { + RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(GetClump()); + int32 idx = RpHAnimIDGetIndex(hier, m_pFrames[node]->nodeID); + RwMatrix *mats = RpHAnimHierarchyGetMatrixArray(hier); + RwV3dTransformPoints(&pos, &pos, 1, &mats[idx]); + } + + // set by 0482:set_threat_reaction_range_multiplier opcode + static uint16 nThreatReactionRangeMultiplier; + + // set by 0481:set_enter_car_range_multiplier opcode + static uint16 nEnterCarRangeMultiplier; + + static bool bNastyLimbsCheat; + static bool bFannyMagnetCheat; + static bool bPedCheat3; + static CVector2D ms_vec2DFleePosition; + +#ifndef MASTER + // Mobile things + void DebugDrawPedDestination(CPed *, int, int); + void DebugDrawPedDesiredHeading(CPed *, int, int); + void DebugDrawCollisionRadius(float, float, float, float, int); + void DebugDrawVisionRange(CVector, float); + void DebugDrawVisionSimple(CVector, float); + void DebugDrawLook(); + void DebugDrawPedPsyche(); + void DebugDrawDebugLines(); + + static void SwitchDebugDisplay(void); + static int GetDebugDisplay(void); + + void DebugDrawLookAtPoints(); + void DebugRenderOnePedText(void); + void DebugRenderClosePedText(); +#endif + +#ifdef COMPATIBLE_SAVES + virtual void Save(uint8*& buf); + virtual void Load(uint8*& buf); +#endif +}; + +void FinishTalkingOnMobileCB(CAnimBlendAssociation* assoc, void* arg); +void StartTalkingOnMobileCB(CAnimBlendAssociation* assoc, void* arg); +void PlayRandomAnimationsFromAnimBlock(CPed* ped, AssocGroupId animGroup, uint32 first, uint32 amount); + +VALIDATE_SIZE(CPed, 0x5F4); + +bool IsPedPointerValid(CPed*); +bool IsPedPointerValid_NotInWorld(CPed*); diff --git a/src/miami/peds/PedAI.cpp b/src/miami/peds/PedAI.cpp new file mode 100644 index 00000000..e6ffc97a --- /dev/null +++ b/src/miami/peds/PedAI.cpp @@ -0,0 +1,6926 @@ +#include "common.h" + +#include "main.h" +#include "Particle.h" +#include "RpAnimBlend.h" +#include "Ped.h" +#include "Wanted.h" +#include "AnimBlendAssociation.h" +#include "DMAudio.h" +#include "General.h" +#include "HandlingMgr.h" +#include "Replay.h" +#include "Camera.h" +#include "PedPlacement.h" +#include "ZoneCull.h" +#include "Pad.h" +#include "Pickups.h" +#include "Train.h" +#include "PedRoutes.h" +#include "CopPed.h" +#include "Script.h" +#include "CarCtrl.h" +#include "WaterLevel.h" +#include "CarAI.h" +#include "Zones.h" +#include "Cranes.h" +#include "PedAttractor.h" +#include "Bike.h" +#include "Weather.h" +#include "GameLogic.h" +#include "Streaming.h" + +CVector vecPedCarDoorAnimOffset; +CVector vecPedCarDoorLoAnimOffset; +CVector vecPedVanRearDoorAnimOffset; +CVector vecPedQuickDraggedOutCarAnimOffset; +CVector vecPedDraggedOutCarAnimOffset; +CVector vecPedTrainDoorAnimOffset; +CVector vecPedStdBikeJumpRhsAnimOffset; +CVector vecPedVespaBikeJumpRhsAnimOffset; +CVector vecPedHarleyBikeJumpRhsAnimOffset; +CVector vecPedDirtBikeJumpRhsAnimOffset; +CVector vecPedBikeKickAnimOffset; + +void +CPed::SetObjectiveTimer(int time) +{ + if (time == 0) { + m_objectiveTimer = 0; + } else if (CTimer::GetTimeInMilliseconds() > m_objectiveTimer) { + m_objectiveTimer = CTimer::GetTimeInMilliseconds() + time; + } +} + +void +CPed::SetStoredObjective(void) +{ + if (m_objective == m_prevObjective) + return; + + switch (m_objective) { + case OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE: + case OBJECTIVE_KILL_CHAR_ON_FOOT: + case OBJECTIVE_KILL_CHAR_ANY_MEANS: + case OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE: + case OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS: + case OBJECTIVE_GOTO_CHAR_ON_FOOT: + case OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING: + case OBJECTIVE_HASSLE_CHAR: + case OBJECTIVE_FOLLOW_CHAR_IN_FORMATION: + case OBJECTIVE_LEAVE_CAR: + case OBJECTIVE_ENTER_CAR_AS_PASSENGER: + case OBJECTIVE_ENTER_CAR_AS_DRIVER: + case OBJECTIVE_GOTO_AREA_ON_FOOT: + case OBJECTIVE_RUN_TO_AREA: + case OBJECTIVE_GOTO_SEAT_ON_FOOT: + case OBJECTIVE_GOTO_ATM_ON_FOOT: + case OBJECTIVE_GOTO_BUS_STOP_ON_FOOT: + case OBJECTIVE_GOTO_PIZZA_ON_FOOT: + case OBJECTIVE_GOTO_SHELTER_ON_FOOT: + case OBJECTIVE_SPRINT_TO_AREA: + case OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT: + return; + default: + m_prevObjective = m_objective; + } +} + +void +CPed::ForceStoredObjective(eObjective objective) +{ + if (objective != OBJECTIVE_ENTER_CAR_AS_DRIVER && objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER) { + m_prevObjective = m_objective; + return; + } + + switch (m_objective) { + case OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE: + case OBJECTIVE_KILL_CHAR_ON_FOOT: + case OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE: + case OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS: + case OBJECTIVE_GOTO_CHAR_ON_FOOT: + case OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING: + case OBJECTIVE_HASSLE_CHAR: + case OBJECTIVE_ENTER_CAR_AS_PASSENGER: + case OBJECTIVE_ENTER_CAR_AS_DRIVER: + case OBJECTIVE_GOTO_AREA_ON_FOOT: + case OBJECTIVE_RUN_TO_AREA: + case OBJECTIVE_GOTO_SEAT_ON_FOOT: + case OBJECTIVE_GOTO_ATM_ON_FOOT: + case OBJECTIVE_GOTO_BUS_STOP_ON_FOOT: + case OBJECTIVE_GOTO_PIZZA_ON_FOOT: + case OBJECTIVE_GOTO_SHELTER_ON_FOOT: + case OBJECTIVE_SPRINT_TO_AREA: + case OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT: + return; + default: + m_prevObjective = m_objective; + } +} + +bool +CPed::IsTemporaryObjective(eObjective objective) +{ + return objective == OBJECTIVE_LEAVE_CAR || objective == OBJECTIVE_SET_LEADER || + objective == OBJECTIVE_LEAVE_CAR_AND_DIE || objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || + objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER; +} + +void +CPed::SetObjective(eObjective newObj) +{ + if (DyingOrDead() || m_attachedTo) + return; + + if (newObj == OBJECTIVE_NONE) { + if ((m_objective == OBJECTIVE_LEAVE_CAR || m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER + || m_objective == OBJECTIVE_LEAVE_CAR_AND_DIE) && !IsPlayer() && !IsPedInControl()) { + + bStartWanderPathOnFoot = true; + } else { + m_objective = OBJECTIVE_NONE; + m_prevObjective = OBJECTIVE_NONE; + } + } else if (m_prevObjective != newObj || m_prevObjective == OBJECTIVE_NONE) { + SetObjectiveTimer(0); + + if (m_objective == newObj) + return; + + if (IsTemporaryObjective(m_objective)) { + m_prevObjective = newObj; + } else { + if (m_objective != newObj) + SetStoredObjective(); + + m_objective = newObj; + } + bObjectiveCompleted = false; + + switch (newObj) { + case OBJECTIVE_NONE: + m_prevObjective = OBJECTIVE_NONE; + break; + case OBJECTIVE_HAIL_TAXI: + m_nWaitTimer = 0; + SetIdle(); + SetMoveState(PEDMOVE_STILL); + break; + default: + break; + } + } +} + +void +CPed::SetObjective(eObjective newObj, void *entity) +{ + if (DyingOrDead()) + return; + + if (m_prevObjective == newObj && m_prevObjective != OBJECTIVE_NONE) + return; + + if (entity == this) + return; + + if (m_attachedTo && newObj != OBJECTIVE_KILL_CHAR_ON_FOOT && newObj != OBJECTIVE_KILL_CHAR_ANY_MEANS && newObj != OBJECTIVE_DESTROY_OBJECT && newObj != OBJECTIVE_DESTROY_CAR) + return; + + if (m_objective == newObj) { + switch (newObj) { + case OBJECTIVE_KILL_CHAR_ON_FOOT: + case OBJECTIVE_KILL_CHAR_ANY_MEANS: + case OBJECTIVE_GOTO_CHAR_ON_FOOT: + case OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING: + case OBJECTIVE_HASSLE_CHAR: + case OBJECTIVE_FOLLOW_CHAR_IN_FORMATION: + case OBJECTIVE_GOTO_AREA_ANY_MEANS: + case OBJECTIVE_GUARD_ATTACK: + case OBJECTIVE_KILL_CHAR_ON_BOAT: + case OBJECTIVE_SOLICIT_FOOT: + if (m_pedInObjective == entity) + return; + break; + case OBJECTIVE_LEAVE_CAR: + case OBJECTIVE_FLEE_CAR: + case OBJECTIVE_LEAVE_CAR_AND_DIE: + return; + case OBJECTIVE_ENTER_CAR_AS_PASSENGER: + case OBJECTIVE_ENTER_CAR_AS_DRIVER: + case OBJECTIVE_DESTROY_CAR: + case OBJECTIVE_SOLICIT_VEHICLE: + case OBJECTIVE_BUY_ICE_CREAM: + if (m_carInObjective == entity) + return; + + if (newObj == OBJECTIVE_BUY_ICE_CREAM && bBoughtIceCream) + return; + + break; + case OBJECTIVE_SET_LEADER: + if (m_leader == entity) + return; + break; + case OBJECTIVE_AIM_GUN_AT: + if (m_pedInObjective == entity) + return; + break; + default: + break; + } + } else { + if (newObj != OBJECTIVE_WAIT_IN_CAR_THEN_GET_OUT && (newObj == OBJECTIVE_LEAVE_CAR || newObj == OBJECTIVE_LEAVE_CAR_AND_DIE) + && !bInVehicle) + return; + } + + bObjectiveCompleted = false; + ClearPointGunAt(); + m_objectiveTimer = 0; + if (IsTemporaryObjective(m_objective) && !IsTemporaryObjective(newObj)) { + m_prevObjective = newObj; + } else { + if (m_objective != newObj) { + if (IsTemporaryObjective(newObj)) + ForceStoredObjective(newObj); + else + SetStoredObjective(); + } + m_objective = newObj; + } + + switch (newObj) { + case OBJECTIVE_WAIT_ON_FOOT_FOR_COP: + m_pedInObjective = (CPed*)entity; + m_pedInObjective->RegisterReference((CEntity**)&m_pedInObjective); + SetIdle(); + SetLook(m_pedInObjective); + break; + case OBJECTIVE_WAIT_IN_CAR_THEN_GET_OUT: + + // In this special case, entity parameter isn't CEntity, but int. + SetObjectiveTimer((uintptr)entity); + break; + case OBJECTIVE_KILL_CHAR_ON_FOOT: + case OBJECTIVE_KILL_CHAR_ANY_MEANS: + case OBJECTIVE_MUG_CHAR: + case OBJECTIVE_KILL_CHAR_ON_BOAT: + m_pNextPathNode = nil; + bUsePedNodeSeek = false; + + if (m_pedInObjective) + m_pedInObjective->CleanUpOldReference((CEntity**)&m_pedInObjective); + if (m_pLookTarget) + m_pLookTarget->CleanUpOldReference(&m_pLookTarget); + + m_pLookTarget = (CEntity*)entity; + m_pedInObjective = (CPed*)entity; + m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f); + m_pedInObjective->RegisterReference((CEntity**)&m_pedInObjective); + // m_pLookTarget = (CEntity*)entity; // duplicate + m_pLookTarget->RegisterReference((CEntity**)&m_pLookTarget); + break; + case OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE: + case OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS: + case OBJECTIVE_GOTO_CHAR_ON_FOOT: + case OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING: + case OBJECTIVE_HASSLE_CHAR: + case OBJECTIVE_GUARD_ATTACK: + + if (m_pedInObjective) + m_pedInObjective->CleanUpOldReference((CEntity**)&m_pedInObjective); + m_pedInObjective = (CPed*)entity; + m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f); + m_pedInObjective->RegisterReference((CEntity**)&m_pedInObjective); + break; + case OBJECTIVE_FOLLOW_CHAR_IN_FORMATION: + m_pedInObjective = (CPed*)entity; + m_pedInObjective->RegisterReference((CEntity**)&m_pedInObjective); + m_pedFormation = FORMATION_REAR; + break; + case OBJECTIVE_LEAVE_CAR: + case OBJECTIVE_LEAVE_CAR_AND_DIE: + case OBJECTIVE_FLEE_CAR: + m_carInObjective = (CVehicle*)entity; + m_carInObjective->RegisterReference((CEntity **)&m_carInObjective); + if (m_carInObjective->bIsBus && m_leaveCarTimer == 0) { + for (int i = 0; i < m_carInObjective->m_nNumMaxPassengers; i++) { + if (m_carInObjective->pPassengers[i] == this) { + m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 1200 * i; + break; + } + } + } + + break; + case OBJECTIVE_DESTROY_OBJECT: + SetWeaponLockOnTarget((CEntity*)entity); + break; + case OBJECTIVE_ENTER_CAR_AS_PASSENGER: + case OBJECTIVE_ENTER_CAR_AS_DRIVER: + if (m_nMoveState == PEDMOVE_STILL) + SetMoveState(PEDMOVE_RUN); + + if (((CVehicle*)entity)->IsBoat() && !IsPlayer() && m_pCurrentPhysSurface != entity) { + RestorePreviousObjective(); + break; + } + // fall through + case OBJECTIVE_DESTROY_CAR: + case OBJECTIVE_SOLICIT_VEHICLE: + case OBJECTIVE_BUY_ICE_CREAM: + m_carInObjective = (CVehicle*)entity; + m_carInObjective->RegisterReference((CEntity**)&m_carInObjective); + m_pSeekTarget = m_carInObjective; + m_pSeekTarget->RegisterReference((CEntity**)&m_pSeekTarget); + m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f); + if (newObj == OBJECTIVE_SOLICIT_VEHICLE) { + m_objectiveTimer = CTimer::GetTimeInMilliseconds() + 10000; + } else if (m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER && CharCreatedBy == MISSION_CHAR && + (m_carInObjective->GetStatus() == STATUS_PLAYER_DISABLED || CPad::GetPad(CWorld::PlayerInFocus)->ArePlayerControlsDisabled())) { + SetObjectiveTimer(14000); + } else { + m_objectiveTimer = 0; + } + break; + case OBJECTIVE_SET_LEADER: + SetLeader((CEntity*)entity); + RestorePreviousObjective(); + break; + case OBJECTIVE_AIM_GUN_AT: + m_pedInObjective = (CPed*)entity; + m_pedInObjective->RegisterReference((CEntity**)&m_pedInObjective); + break; + case OBJECTIVE_SOLICIT_FOOT: + m_pedInObjective = (CPed*)entity; + m_pedInObjective->RegisterReference((CEntity**)&m_pedInObjective); + break; + default: + break; + } +} + +// Only used in 01E1: SET_CHAR_OBJ_FOLLOW_ROUTE opcode +// IDA fails very badly in here, puts a fake loop and ignores SetFollowRoute call... +void +CPed::SetObjective(eObjective newObj, int16 routePoint, int16 routeType) +{ + if (DyingOrDead()) + return; + + if (m_prevObjective == newObj && m_prevObjective != OBJECTIVE_NONE) + return; + + if (m_objective == newObj && newObj == OBJECTIVE_FOLLOW_ROUTE && m_routeLastPoint == routePoint && m_routeType == routeType) + return; + + ClearPointGunAt(); + SetObjectiveTimer(0); + + bObjectiveCompleted = false; + if (IsTemporaryObjective(m_objective)) { + m_prevObjective = newObj; + } else { + if (m_objective != newObj) + SetStoredObjective(); + + m_objective = newObj; + } + + if (newObj == OBJECTIVE_FOLLOW_ROUTE) { + SetFollowRoute(routePoint, routeType); + } +} + +void +CPed::SetObjective(eObjective newObj, CVector dest) +{ + if (DyingOrDead()) + return; + + if (m_prevObjective != OBJECTIVE_NONE && m_prevObjective == newObj) + return; + + if (m_objective == newObj) { + if (newObj == OBJECTIVE_GOTO_AREA_ANY_MEANS || newObj == OBJECTIVE_GOTO_AREA_ON_FOOT || newObj == OBJECTIVE_RUN_TO_AREA || newObj == OBJECTIVE_SPRINT_TO_AREA) { + if (m_nextRoutePointPos == dest) + return; + } else if (newObj == OBJECTIVE_GUARD_SPOT) { + if (m_vecSpotToGuard == dest) + return; + } + } + + ClearPointGunAt(); + m_objectiveTimer = 0; + bObjectiveCompleted = false; + switch (newObj) { + case OBJECTIVE_GUARD_SPOT: + m_vecSpotToGuard = dest; + m_radiusToGuard = 5.0f; + SetMoveState(PEDMOVE_STILL); + break; + case OBJECTIVE_GUARD_AREA: + case OBJECTIVE_WAIT_IN_CAR: + case OBJECTIVE_WAIT_IN_CAR_THEN_GET_OUT: + case OBJECTIVE_KILL_CHAR_ON_FOOT: + case OBJECTIVE_KILL_CHAR_ANY_MEANS: + case OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE: + case OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS: + case OBJECTIVE_GOTO_CHAR_ON_FOOT: + case OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING: + case OBJECTIVE_HASSLE_CHAR: + case OBJECTIVE_FOLLOW_CHAR_IN_FORMATION: + case OBJECTIVE_LEAVE_CAR: + case OBJECTIVE_ENTER_CAR_AS_PASSENGER: + case OBJECTIVE_ENTER_CAR_AS_DRIVER: + case OBJECTIVE_FOLLOW_CAR_IN_CAR: + case OBJECTIVE_FIRE_AT_OBJECT_FROM_VEHICLE: + case OBJECTIVE_DESTROY_OBJECT: + case OBJECTIVE_DESTROY_CAR: + case OBJECTIVE_GOTO_AREA_IN_CAR: + case OBJECTIVE_FOLLOW_CAR_ON_FOOT_WITH_OFFSET: + case OBJECTIVE_GUARD_ATTACK: + case OBJECTIVE_SET_LEADER: + case OBJECTIVE_FOLLOW_ROUTE: + case OBJECTIVE_SOLICIT_VEHICLE: + case OBJECTIVE_HAIL_TAXI: + case OBJECTIVE_CATCH_TRAIN: + case OBJECTIVE_BUY_ICE_CREAM: + case OBJECTIVE_STEAL_ANY_CAR: + case OBJECTIVE_STEAL_ANY_MISSION_CAR: + case OBJECTIVE_MUG_CHAR: + case OBJECTIVE_LEAVE_CAR_AND_DIE: + case OBJECTIVE_FLEE_CAR: + case OBJECTIVE_SUN_BATHE: + case OBJECTIVE_AIM_GUN_AT: + case OBJECTIVE_WANDER: + case OBJECTIVE_WAIT_ON_FOOT_AT_SHELTER: + case OBJECTIVE_KILL_CHAR_ON_BOAT: + case OBJECTIVE_SOLICIT_FOOT: + case OBJECTIVE_WAIT_ON_FOOT_AT_BUS_STOP: + break; + case OBJECTIVE_GOTO_AREA_ANY_MEANS: + case OBJECTIVE_GOTO_AREA_ON_FOOT: + case OBJECTIVE_GOTO_SEAT_ON_FOOT: + case OBJECTIVE_GOTO_ATM_ON_FOOT: + case OBJECTIVE_GOTO_BUS_STOP_ON_FOOT: + case OBJECTIVE_GOTO_PIZZA_ON_FOOT: + case OBJECTIVE_GOTO_SHELTER_ON_FOOT: + case OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT: + bIsRunning = false; + m_pNextPathNode = nil; + m_nextRoutePointPos = dest; + m_vecSeekPos = m_nextRoutePointPos; + m_distanceToCountSeekDone = 0.5f; + if (newObj == OBJECTIVE_GOTO_ATM_ON_FOOT) { + m_distanceToCountSeekDone = m_attractor->GetDistanceToCountSeekDone(); + m_acceptableHeadingOffset = m_attractor->GetAcceptableHeading(); + } + if (newObj == OBJECTIVE_GOTO_SEAT_ON_FOOT) { + m_distanceToCountSeekDone = m_attractor->GetDistanceToCountSeekDone(); + m_acceptableHeadingOffset = m_attractor->GetAcceptableHeading(); + } + if (newObj == OBJECTIVE_GOTO_BUS_STOP_ON_FOOT) { + m_distanceToCountSeekDone = m_attractor->GetDistanceToCountSeekDone(); + m_acceptableHeadingOffset = m_attractor->GetAcceptableHeading(); + } + if (newObj == OBJECTIVE_GOTO_PIZZA_ON_FOOT) { + m_distanceToCountSeekDone = m_attractor->GetDistanceToCountSeekDone(); + m_acceptableHeadingOffset = m_attractor->GetAcceptableHeading(); + } + if (newObj == OBJECTIVE_GOTO_SHELTER_ON_FOOT) { + bIsRunning = true; + m_distanceToCountSeekDone = m_attractor->GetDistanceToCountSeekDone(); + m_acceptableHeadingOffset = m_attractor->GetAcceptableHeading(); + } + if (newObj == OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT) { + bIsRunning = true; + m_distanceToCountSeekDone = m_attractor->GetDistanceToCountSeekDone(); + m_acceptableHeadingOffset = m_attractor->GetAcceptableHeading(); + } + bUsePedNodeSeek = false; + if (sq(m_distanceToCountSeekDone) > (m_nextRoutePointPos - GetPosition()).MagnitudeSqr2D()) { + if (!IsUseAttractorObjective(m_objective)) + return; + if (Abs(m_fRotationCur - m_attractorHeading) < m_acceptableHeadingOffset) + return; + } + break; + case OBJECTIVE_RUN_TO_AREA: + case OBJECTIVE_SPRINT_TO_AREA: + bIsRunning = true; + m_pNextPathNode = nil; + m_nextRoutePointPos = dest; + m_vecSeekPos = m_nextRoutePointPos; + m_distanceToCountSeekDone = 0.5f; + bUsePedNodeSeek = true; + if (sq(m_distanceToCountSeekDone) > (m_nextRoutePointPos - GetPosition()).MagnitudeSqr2D()) + return; + break; + default: break; + } + + if (IsTemporaryObjective(m_objective)) { + m_prevObjective = newObj; + } else { + if (m_objective != newObj) + SetStoredObjective(); + + m_objective = newObj; + } +} + +void +CPed::SetObjective(eObjective newObj, float heading, const CVector& pos) +{ + switch (newObj) { + case OBJECTIVE_GOTO_SEAT_ON_FOOT: + case OBJECTIVE_GOTO_ATM_ON_FOOT: + case OBJECTIVE_GOTO_BUS_STOP_ON_FOOT: + case OBJECTIVE_GOTO_PIZZA_ON_FOOT: + case OBJECTIVE_GOTO_SHELTER_ON_FOOT: + case OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT: + ClearPointGunAt(); + SetObjective(newObj, pos); + m_attractorHeading = heading; + } +} + +void +CPed::ClearObjective(void) +{ + if (IsPedInControl() || m_nPedState == PED_DRIVING) { + m_objective = OBJECTIVE_NONE; + m_pedInObjective = nil; + m_carInObjective = nil; + + if (m_nPedState == PED_DRIVING && m_pMyVehicle) { + if (m_pMyVehicle->pDriver != this) { + if(!IsPlayer()) + bWanderPathAfterExitingCar = true; + + SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); + } + m_nLastPedState = PED_NONE; + } else { + SetIdle(); + SetMoveState(PEDMOVE_STILL); + } + } else { + bClearObjective = true; + } +} + +void +CPed::ClearLeader(void) +{ + if (!m_leader) + return; + + m_leader = nil; + if (IsPedInControl()) { + SetObjective(OBJECTIVE_NONE); + if (CharCreatedBy == MISSION_CHAR) { + SetIdle(); + } else { + SetWanderPath(CGeneral::GetRandomNumberInRange(0,8)); + } + } else if (m_objective != OBJECTIVE_NONE) { + bClearObjective = true; + } +} + +void +CPed::UpdateFromLeader(void) +{ + if (CTimer::GetTimeInMilliseconds() <= m_objectiveTimer) + return; + + if (!m_leader) + return; + + CVector leaderDist; + if (m_leader->InVehicle()) + leaderDist = m_leader->m_pMyVehicle->GetPosition() - GetPosition(); + else + leaderDist = m_leader->GetPosition() - GetPosition(); + + if (leaderDist.Magnitude() > 30.0f) { + if (bWaitForLeaderToComeCloser) { + if (IsPedInControl()) { + SetObjective(OBJECTIVE_NONE); + SetIdle(); + SetMoveState(PEDMOVE_STILL); + } + return; + } + bWaitForLeaderToComeCloser = true; + } else + bWaitForLeaderToComeCloser = false; + + if (IsPedInControl()) { + if (m_nWaitState == WAITSTATE_PLAYANIM_TAXI) + WarpPedToNearLeaderOffScreen(); + + if (m_leader->m_nPedState == PED_DEAD) { + SetLeader(nil); + SetObjective(OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE); + return; + } + if (!m_leader->bInVehicle) { + if (m_leader->m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER) { + if (bInVehicle) { + if (m_objective != OBJECTIVE_WAIT_IN_CAR_THEN_GET_OUT && m_objective != OBJECTIVE_LEAVE_CAR) + SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); + + return; + } + if (m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER) { + RestorePreviousObjective(); + RestorePreviousState(); + } + } + if (m_nPedType == PEDTYPE_PROSTITUTE && CharCreatedBy == RANDOM_CHAR) { + SetLeader(nil); + return; + } + } + if (!bInVehicle && m_leader->bInVehicle && m_leader->m_nPedState == PED_DRIVING) { + if (m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER && m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER) { + if (m_leader->m_pMyVehicle->m_nNumPassengers < m_leader->m_pMyVehicle->m_nNumMaxPassengers) + SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, m_leader->m_pMyVehicle); + } + } else if (m_leader->m_objective == OBJECTIVE_NONE || (m_leader->IsPlayer() && m_leader->m_objective == OBJECTIVE_WAIT_ON_FOOT) + || m_objective == m_leader->m_objective) { + + if (m_leader->m_nPedState == PED_ATTACK && !bDontFight) { + CEntity *lookTargetOfLeader = m_leader->m_pLookTarget; + + if (lookTargetOfLeader && m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT && lookTargetOfLeader->IsPed() && lookTargetOfLeader != this) { + SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, lookTargetOfLeader); + SetObjectiveTimer(8000); + SetLookFlag(m_leader->m_pLookTarget, false); + SetLookTimer(500); + } + } else { + if (IsPedInControl() && m_nPedState != PED_ATTACK) { + if (m_leader->m_objective == OBJECTIVE_NONE && m_objective == OBJECTIVE_NONE && m_leader->m_nPedState == PED_CHAT && m_nPedState == PED_CHAT) { + SetObjective(OBJECTIVE_NONE); + } else { + SetObjective(OBJECTIVE_GOTO_CHAR_ON_FOOT, m_leader); + SetObjectiveTimer(0); + } + } + if (m_nPedState == PED_IDLE && m_leader->IsPlayer() && !bDontFight) { + if (ScanForThreats() && m_threatEntity) { + m_pLookTarget = m_threatEntity; + m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); + TurnBody(); + if (m_attackTimer < CTimer::GetTimeInMilliseconds() && !GetWeapon()->IsTypeMelee()) { + SetWeaponLockOnTarget(m_threatEntity); + SetAttack(m_threatEntity); + } + } + } + } + } else { + switch (m_leader->m_objective) { + case OBJECTIVE_WAIT_ON_FOOT: + case OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE: + case OBJECTIVE_WAIT_IN_CAR: + case OBJECTIVE_FOLLOW_ROUTE: + SetObjective(m_leader->m_objective); + m_objectiveTimer = m_leader->m_objectiveTimer; + break; + case OBJECTIVE_GUARD_SPOT: + SetObjective(OBJECTIVE_GUARD_SPOT, m_leader->m_vecSpotToGuard); + m_objectiveTimer = m_leader->m_objectiveTimer; + break; + case OBJECTIVE_KILL_CHAR_ON_FOOT: + case OBJECTIVE_KILL_CHAR_ANY_MEANS: + case OBJECTIVE_GOTO_CHAR_ON_FOOT: + case OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING: + case OBJECTIVE_HASSLE_CHAR: + if (m_leader->m_pedInObjective) { + SetObjective(m_leader->m_objective, m_leader->m_pedInObjective); + m_objectiveTimer = m_leader->m_objectiveTimer; + } + break; + case OBJECTIVE_ENTER_CAR_AS_PASSENGER: + case OBJECTIVE_ENTER_CAR_AS_DRIVER: + if (m_leader->m_carInObjective) { + m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 150; + SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, m_leader->m_carInObjective); + return; + } + break; + case OBJECTIVE_GUARD_ATTACK: + return; + case OBJECTIVE_HAIL_TAXI: + m_leader = nil; + SetObjective(OBJECTIVE_NONE); + break; + default: + SetObjective(OBJECTIVE_GOTO_CHAR_ON_FOOT, m_leader); + SetObjectiveTimer(0); + break; + } + } + } else if (bInVehicle) { + if ((!m_leader->bInVehicle || m_leader->m_nPedState == PED_EXIT_CAR) && m_objective != OBJECTIVE_WAIT_IN_CAR_THEN_GET_OUT) { + + switch (m_leader->m_objective) { + case OBJECTIVE_ENTER_CAR_AS_PASSENGER: + case OBJECTIVE_ENTER_CAR_AS_DRIVER: + if (m_pMyVehicle == m_leader->m_pMyVehicle || m_pMyVehicle == m_leader->m_carInObjective) + break; + + // fall through + default: + if (m_pMyVehicle && m_objective != OBJECTIVE_LEAVE_CAR) { + m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 250; + SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); + } + + break; + } + } + } +} + +void +CPed::RestorePreviousObjective(void) +{ + if (m_objective == OBJECTIVE_NONE) + return; + + if (m_objective != OBJECTIVE_LEAVE_CAR && m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER && m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER + && m_nPedState != PED_CARJACK) + m_pedInObjective = nil; + + if (m_objective == OBJECTIVE_WAIT_IN_CAR_THEN_GET_OUT) { + m_objective = OBJECTIVE_NONE; + if (m_pMyVehicle) + SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); + + } else { + m_objective = m_prevObjective; + m_prevObjective = OBJECTIVE_NONE; + } + bObjectiveCompleted = false; +} + +void +CPed::ProcessObjective(void) +{ + if (bClearObjective && (IsPedInControl() || m_nPedState == PED_DRIVING)) { + ClearObjective(); + bClearObjective = false; + } + UpdateFromLeader(); + + CVector carOrOurPos; + CVector targetCarOrHisPos; + CVector distWithTarget; + + if (m_objective != OBJECTIVE_NONE && (IsPedInControl() || m_nPedState == PED_DRIVING)) { + if (bInVehicle) { + if (!m_pMyVehicle) { + bInVehicle = false; + return; + } + carOrOurPos = m_pMyVehicle->GetPosition(); + } else { + carOrOurPos = GetPosition(); + } + + if (m_pedInObjective) { + if (m_pedInObjective->InVehicle() && m_pedInObjective->m_nPedState != PED_DRAG_FROM_CAR) { + targetCarOrHisPos = m_pedInObjective->m_pMyVehicle->GetPosition(); + } else { + targetCarOrHisPos = m_pedInObjective->GetPosition(); + } + distWithTarget = targetCarOrHisPos - carOrOurPos; + } else if (m_carInObjective) { + targetCarOrHisPos = m_carInObjective->GetPosition(); + distWithTarget = targetCarOrHisPos - carOrOurPos; + } + + switch (m_objective) { + case OBJECTIVE_WAIT_ON_FOOT: + if (GetPedState() == PED_DRIVING) + m_objective = OBJECTIVE_NONE; + else { + SetIdle(); + if (m_attractor) { + if (m_objectiveTimer && CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + GetPedAttractorManager()->BroadcastDeparture(this, m_attractor); + m_objectiveTimer = 0; + } + } else { + m_objective = OBJECTIVE_NONE; + SetMoveState(PEDMOVE_STILL); + } + } + break; + case OBJECTIVE_WAIT_ON_FOOT_FOR_COP: + if (!m_pedInObjective) { + m_objective = OBJECTIVE_NONE; + SetWanderPath(CGeneral::GetRandomNumberInRange(0.f, 8.f)); + } else if (m_pedInObjective->m_nPedType == PEDTYPE_COP && m_pedInObjective->m_nPedState == PED_DEAD) { + m_objective = OBJECTIVE_NONE; + SetWanderPath(CGeneral::GetRandomNumberInRange(0.f, 8.f)); + m_pedInObjective = nil; + } + break; + case OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE: + if (m_leaveCarTimer >= CTimer::GetTimeInMilliseconds()) + break; + + if (InVehicle()) { + SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); + bFleeAfterExitingCar = true; + } else if (m_nPedState != PED_FLEE_POS) { + CVector2D fleePos = GetPosition(); + SetFlee(fleePos, 10000); + bUsePedNodeSeek = true; + m_pNextPathNode = nil; + } + break; + case OBJECTIVE_GUARD_SPOT: + { + distWithTarget = m_vecSpotToGuard - GetPosition(); + if (m_pedInObjective) { + SetLookFlag(m_pedInObjective, true); + m_pLookTarget = m_pedInObjective; + m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); + TurnBody(); + } + float distWithTargetSc = distWithTarget.Magnitude(); + if (2.0f * m_radiusToGuard >= distWithTargetSc) { + if (m_pedInObjective && m_pedInObjective->m_nPedState != PED_DEAD) { + if (distWithTargetSc <= m_radiusToGuard) + SetIdle(); + else { + CVector seekPos = m_vecSpotToGuard; + SetSeek(seekPos, m_radiusToGuard); + } + } else if (CTimer::GetTimeInMilliseconds() > m_lookTimer) { + int threatType = ScanForThreats(); + SetLookTimer(CGeneral::GetRandomNumberInRange(500, 1500)); + + // Second condition is pointless and isn't there in Mobile. + if (threatType == PED_FLAG_GUN || (threatType == PED_FLAG_EXPLOSION && m_threatEntity) || m_threatEntity) { + if (m_threatEntity->IsPed()) + SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity); + } + } + } else { + CVector seekPos = m_vecSpotToGuard; + SetSeek(seekPos, m_radiusToGuard); + } + break; + } + case OBJECTIVE_WAIT_IN_CAR: + SetPedState(PED_DRIVING); + break; + case OBJECTIVE_WAIT_IN_CAR_THEN_GET_OUT: + SetPedState(PED_DRIVING); + break; + case OBJECTIVE_KILL_CHAR_ANY_MEANS: + { + if (m_pedInObjective) { + if (m_pedInObjective->IsPlayer() && CharCreatedBy != MISSION_CHAR + && m_nPedType != PEDTYPE_COP && FindPlayerPed()->m_pWanted->m_CurrentCops != 0 + && !bKindaStayInSamePlace) { + + SetObjective(OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE); + break; + } + if (InVehicle()) { + if (distWithTarget.Magnitude() >= 20.0f || m_pMyVehicle->m_vecMoveSpeed.MagnitudeSqr() >= sq(0.02f)) { + + if (((m_pMyVehicle->pDriver == this && m_pMyVehicle->AutoPilot.m_nCarMission == MISSION_NONE) || m_pMyVehicle->AutoPilot.m_nCarMission == MISSION_CRUISE) + && !m_pMyVehicle->m_nGettingInFlags) { + m_pMyVehicle->SetStatus(STATUS_PHYSICS); + m_pMyVehicle->AutoPilot.m_nPrevRouteNode = 0; + if (m_nPedType == PEDTYPE_COP) { + m_pMyVehicle->AutoPilot.m_nCruiseSpeed = (FindPlayerPed()->m_pWanted->GetWantedLevel() * 0.1f + 0.6f) * (GAME_SPEED_TO_CARAI_SPEED * m_pMyVehicle->pHandling->Transmission.fMaxCruiseVelocity); + m_pMyVehicle->AutoPilot.m_nCarMission = CCarAI::FindPoliceCarMissionForWantedLevel(); + } else { + m_pMyVehicle->AutoPilot.m_nCruiseSpeed = GAME_SPEED_TO_CARAI_SPEED * m_pMyVehicle->pHandling->Transmission.fMaxCruiseVelocity * 0.8f; + m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_RAMPLAYER_FARAWAY; + } + m_pMyVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + } + } else { + bool targetHasVeh = m_pedInObjective->bInVehicle; + if (!targetHasVeh || targetHasVeh && m_pedInObjective->m_pMyVehicle->CanPedExitCar(false)) { + m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; + m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_NONE; + SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); + } + } + break; + } + if (distWithTarget.Magnitude() > 30.0f && !bKindaStayInSamePlace) { + if (m_pMyVehicle) { + m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; + SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pMyVehicle); + } else { + float closestVehDist = 60.0f; + int16 lastVehicle; + CEntity* vehicles[8]; + CWorld::FindObjectsInRange(GetPosition(), ENTER_CAR_MAX_DIST, true, &lastVehicle, 6, vehicles, false, true, false, false, false); + CVehicle *foundVeh = nil; + for(int i = 0; i < lastVehicle; i++) { + CVehicle *nearVeh = (CVehicle*)vehicles[i]; + /* + Not used. + CVector vehSpeed = nearVeh->GetSpeed(); + CVector ourSpeed = GetSpeed(); + */ + CVector vehDistVec = nearVeh->GetPosition() - GetPosition(); + if (vehDistVec.Magnitude() < closestVehDist && m_pedInObjective->m_pMyVehicle != nearVeh + && nearVeh->CanPedOpenLocks(this) && nearVeh->m_fHealth > 250.f) { + + foundVeh = nearVeh; + closestVehDist = vehDistVec.Magnitude(); + } + } + m_pMyVehicle = foundVeh; + if (m_pMyVehicle) { + m_pMyVehicle->RegisterReference((CEntity **) &m_pMyVehicle); + m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; + SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pMyVehicle); + } else if (!GetIsOnScreen()) { + CVector ourPos = GetPosition(); + int closestNode = ThePaths.FindNodeClosestToCoors(ourPos, PATH_CAR, 20.0f); + if (closestNode >= 0) { + int16 colliding; + CWorld::FindObjectsKindaColliding( + ThePaths.m_pathNodes[closestNode].GetPosition(), 10.0f, true, &colliding, 2, nil, false, true, true, false, false); + if (!colliding) { + CZoneInfo zoneInfo; + int chosenCarClass; + CTheZones::GetZoneInfoForTimeOfDay(&ourPos, &zoneInfo); + int chosenModel = CCarCtrl::ChooseModel(&zoneInfo, &chosenCarClass); + CVehicle *newVeh = nil; + if (chosenModel != -1) { + if (CModelInfo::IsBikeModel(chosenModel)) { + newVeh = new CBike(chosenModel, RANDOM_VEHICLE); + } else { + newVeh = new CAutomobile(chosenModel, RANDOM_VEHICLE); + } + } + if (newVeh) { + newVeh->GetMatrix().GetPosition() = ThePaths.m_pathNodes[closestNode].GetPosition(); + newVeh->GetMatrix().GetPosition().z += 4.0f; + newVeh->SetHeading(DEGTORAD(200.0f)); + newVeh->SetStatus(STATUS_ABANDONED); + newVeh->m_nDoorLock = CARLOCK_UNLOCKED; + CWorld::Add(newVeh); + m_pMyVehicle = newVeh; + if (m_pMyVehicle) { + m_pMyVehicle->RegisterReference((CEntity **) &m_pMyVehicle); + m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; + SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pMyVehicle); + } + } + } + } + } + } + break; + } + } else { + ClearLookFlag(); + bObjectiveCompleted = true; + } + } + case OBJECTIVE_KILL_CHAR_ON_FOOT: + { + if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT && InVehicle()) { + SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); + break; + } + if (!m_pedInObjective || m_pedInObjective->DyingOrDead()) { + bObjectiveCompleted = true; + ClearLookFlag(); + SetMoveAnim(); + break; + } + if (m_pedInObjective) { + int status; + if (GetWeapon()->IsTypeMelee()) + status = KillCharOnFootMelee(carOrOurPos, targetCarOrHisPos, distWithTarget); + else + status = KillCharOnFootArmed(carOrOurPos, targetCarOrHisPos, distWithTarget); + + if (status == WATCH_UNTIL_HE_DISAPPEARS) + return; + if (status == CANT_ATTACK) + break; + } + SetMoveAnim(); + break; + } + case OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE: + case OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS: + { + if (InVehicle()) { + if (m_nPedState == PED_DRIVING) + SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); + } else if (m_nPedState != PED_FLEE_ENTITY) { + int time; + if (m_objective == OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS) + time = 0; + else + time = 6000; + + SetFindPathAndFlee(m_pedInObjective, time); + if (m_pedStats == CPedStats::ms_apPedStats[PEDSTAT_FIREMAN]) + bMakeFleeScream = true; + } + break; + } + case OBJECTIVE_GOTO_CHAR_ON_FOOT: + case OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING: + case OBJECTIVE_HASSLE_CHAR: + { + if (m_pedInObjective) { + float safeDistance = 2.0f; + if (m_pedInObjective->bInVehicle) + safeDistance = 3.0f; + if (m_objective == OBJECTIVE_HASSLE_CHAR) + safeDistance = 1.0f; + + float distWithTargetSc = distWithTarget.Magnitude(); + if (m_nPedStateTimer < CTimer::GetTimeInMilliseconds()) { + if (distWithTargetSc <= safeDistance) { + bScriptObjectiveCompleted = true; + if (m_nPedState != PED_ATTACK) { + SetIdle(); + if (m_pLookTarget) + m_pLookTarget->CleanUpOldReference(&m_pLookTarget); + m_pLookTarget = m_pedInObjective; + m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); + TurnBody(); + } + if (distWithTargetSc > 2.0f) + SetMoveState(m_pedInObjective->m_nMoveState); + else + SetMoveState(PEDMOVE_STILL); + + if (m_objective == OBJECTIVE_HASSLE_CHAR) { + Say(SOUND_PED_COP_ASK_FOR_ID); + m_pedInObjective->Say(SOUND_PED_INNOCENT); + m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 3000; + m_pedInObjective->m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 3000; + SetObjective(OBJECTIVE_WANDER); + m_pedInObjective->SetObjective(OBJECTIVE_WANDER); + CVector2D dist = GetPosition() - m_pedInObjective->GetPosition(); + m_nPathDir = CGeneral::GetNodeHeadingFromVector(dist.x, dist.y); + m_pedInObjective->m_nPathDir = CGeneral::GetNodeHeadingFromVector(-dist.x, -dist.y); + } + } else { + SetSeek(m_pedInObjective, safeDistance); + if (distWithTargetSc >= 5.0f) { + if (m_leader && m_leader->m_nMoveState == PEDMOVE_SPRINT) + SetMoveState(PEDMOVE_SPRINT); + else + SetMoveState(PEDMOVE_RUN); + } else { + if (m_leader && m_leader->m_nMoveState != PEDMOVE_STILL + && m_leader->m_nMoveState != PEDMOVE_NONE) { + if (m_leader->IsPlayer()) { + if (distWithTargetSc >= 3.0f && FindPlayerPed()->m_fMoveSpeed >= 1.3f) + SetMoveState(PEDMOVE_RUN); + else + SetMoveState(PEDMOVE_WALK); + } else { + SetMoveState(m_leader->m_nMoveState); + } + } else if (distWithTargetSc <= 3.0f) { + SetMoveState(PEDMOVE_WALK); + } else { + SetMoveState(PEDMOVE_RUN); + } + } + } + if (m_objective == OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING && m_nMoveState > PEDMOVE_STILL) + SetMoveState(PEDMOVE_WALK); + } + } else { + SetObjective(OBJECTIVE_NONE); + } + break; + } + case OBJECTIVE_FOLLOW_CHAR_IN_FORMATION: + { + if (m_pedInObjective) { + CVector posToGo = GetFormationPosition(); + distWithTarget = posToGo - carOrOurPos; + SetSeek(posToGo, 1.0f); + if (distWithTarget.Magnitude() <= 3.0f) { + SetSeek(posToGo, 1.0f); + if (m_pedInObjective && m_pedInObjective->m_nMoveState != PEDMOVE_STILL) + SetMoveState(m_pedInObjective->m_nMoveState); + } else { + SetSeek(posToGo, 1.0f); + SetMoveState(PEDMOVE_RUN); + } + } else { + SetObjective(OBJECTIVE_NONE); + } + break; + } + case OBJECTIVE_ENTER_CAR_AS_PASSENGER: + { + if (m_carInObjective) { + if (!bInVehicle && m_carInObjective->m_nNumPassengers >= m_carInObjective->m_nNumMaxPassengers) { + RestorePreviousObjective(); + RestorePreviousState(); + if (IsPedInControl()) + m_pMyVehicle = nil; + + break; + } + + if (m_prevObjective == OBJECTIVE_HAIL_TAXI && !((CAutomobile*)m_carInObjective)->bTaxiLight) { + RestorePreviousObjective(); + ClearObjective(); + SetWanderPath(CGeneral::GetRandomNumber() & 7); + bIsRunning = false; + break; + } + if (m_objectiveTimer && m_objectiveTimer < CTimer::GetTimeInMilliseconds()) { + if (!EnteringCar()) { + bool foundSeat = false; + if (m_carInObjective->IsBike()) { + if (!m_carInObjective->pPassengers[0] && !(m_carInObjective->m_nGettingInFlags & (CAR_DOOR_FLAG_RR | CAR_DOOR_FLAG_LR))) { + m_vehDoor = CAR_DOOR_RR; + foundSeat = true; + } + } else { + if (m_carInObjective->pPassengers[0] || m_carInObjective->m_nGettingInFlags & CAR_DOOR_FLAG_RF) { + if (m_carInObjective->pPassengers[1] || m_carInObjective->m_nGettingInFlags & CAR_DOOR_FLAG_LR) { + if (m_carInObjective->pPassengers[2] || m_carInObjective->m_nGettingInFlags & CAR_DOOR_FLAG_RR) { + foundSeat = false; + } else { + m_vehDoor = CAR_DOOR_RR; + foundSeat = true; + } + } else { + m_vehDoor = CAR_DOOR_LR; + foundSeat = true; + } + } else { + m_vehDoor = CAR_DOOR_RF; + foundSeat = true; + } + } + for (int i = 2; i < m_carInObjective->m_nNumMaxPassengers; ++i) { + if (!m_carInObjective->pPassengers[i] && !(m_carInObjective->m_nGettingInFlags & CAR_DOOR_FLAG_RF)) { + m_vehDoor = CAR_DOOR_RF; + foundSeat = true; + } + } + if (foundSeat) { + SetPosition(GetPositionToOpenCarDoor(m_carInObjective, m_vehDoor)); + SetEnterCar(m_carInObjective, m_vehDoor); + } + } + m_objectiveTimer = 0; + } + } + // fall through + } + case OBJECTIVE_ENTER_CAR_AS_DRIVER: + { + if (!m_carInObjective || bInVehicle) { + if (bInVehicle && m_pMyVehicle != m_carInObjective) { + SetExitCar(m_pMyVehicle, 0); + } else { + bObjectiveCompleted = true; + bScriptObjectiveCompleted = true; + RestorePreviousState(); + } + } else { + if (m_leaveCarTimer > CTimer::GetTimeInMilliseconds()) { + SetMoveState(PEDMOVE_STILL); + break; + } + if (IsPedInControl()) { + if (m_prevObjective == OBJECTIVE_KILL_CHAR_ANY_MEANS) { + if (distWithTarget.Magnitude() < 20.0f) { + RestorePreviousObjective(); + RestorePreviousState(); + return; + } + if (m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) { + if (m_carInObjective->pDriver && !IsPlayer()) { + if (m_carInObjective->pDriver->m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS && m_carInObjective->pDriver != m_pedInObjective) { + SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, m_carInObjective); + m_carInObjective->bIsBeingCarJacked = false; + } + } + } + } else if (m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER) { + if (m_carInObjective->pDriver + && (CharCreatedBy != MISSION_CHAR || m_carInObjective->pDriver->CharCreatedBy != RANDOM_CHAR) + ) { + if (m_carInObjective->pDriver->m_nPedType == m_nPedType) { + SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, m_carInObjective); + m_carInObjective->bIsBeingCarJacked = false; + } + } + } + if (m_carInObjective->IsUpsideDown() && m_carInObjective->m_vehType != VEHICLE_TYPE_BIKE) { + RestorePreviousObjective(); + RestorePreviousState(); + return; + } + if (!m_carInObjective->IsBoat() || m_nPedState == PED_SEEK_IN_BOAT) { + if (m_nPedState != PED_SEEK_CAR) + SetSeekCar(m_carInObjective, 0); + } else { + SetSeekBoatPosition(m_carInObjective); + } + if (m_nMoveState == PEDMOVE_STILL && !bVehEnterDoorIsBlocked) + SetMoveState(PEDMOVE_RUN); + + if (m_carInObjective && m_carInObjective->m_fHealth > 0.0f) { + distWithTarget = m_carInObjective->GetPosition() - GetPosition(); + if (!bInVehicle) { + if (nEnterCarRangeMultiplier * ENTER_CAR_MAX_DIST < distWithTarget.Magnitude()) { + if (!m_carInObjective->pDriver && !m_carInObjective->GetIsOnScreen() && !GetIsOnScreen()) + WarpPedToNearEntityOffScreen(m_carInObjective); + + if (CharCreatedBy != MISSION_CHAR || m_prevObjective == OBJECTIVE_KILL_CHAR_ANY_MEANS + || IsPlayer() && !CPad::GetPad(0)->ArePlayerControlsDisabled()) { + RestorePreviousObjective(); + RestorePreviousState(); + if (IsPedInControl()) + m_pMyVehicle = nil; + } else { + SetIdle(); + SetMoveState(PEDMOVE_STILL); + } + } + } + } else if (!bInVehicle) { + RestorePreviousObjective(); + RestorePreviousState(); + if (IsPedInControl()) + m_pMyVehicle = nil; + } + } + } + break; + } + case OBJECTIVE_DESTROY_OBJECT: + if (m_pPointGunAt) { + if (m_nPedState != PED_ATTACK) + SetAttack(m_pPointGunAt); + } else { + bScriptObjectiveCompleted = true; + RestorePreviousObjective(); + } + break; + case OBJECTIVE_DESTROY_CAR: + { + if (!m_carInObjective) { + ClearLookFlag(); + bObjectiveCompleted = true; + break; + } + float distWithTargetSc = distWithTarget.Magnitude(); + CWeaponInfo *wepInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + float wepRange = wepInfo->m_fRange; + m_pLookTarget = m_carInObjective; + m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); + + m_pSeekTarget = m_carInObjective; + m_pSeekTarget->RegisterReference((CEntity**) &m_pSeekTarget); + + TurnBody(); + if (m_carInObjective->m_fHealth <= 0.0f) { + ClearLookFlag(); + bScriptObjectiveCompleted = true; + break; + } + + if (m_attackTimer < CTimer::GetTimeInMilliseconds() && distWithTargetSc < wepRange) { + // I hope so + CVector ourHead = GetMatrix() * CVector(0.5f, 0.0f, 0.6f); + CVector maxShotPos = m_carInObjective->GetPosition() - ourHead; + maxShotPos *= wepInfo->m_fRange / maxShotPos.Magnitude(); + maxShotPos += ourHead; + + CColPoint foundCol; + CEntity *foundEnt; + + CWorld::bIncludeDeadPeds = true; + CWorld::ProcessLineOfSight(ourHead, maxShotPos, foundCol, foundEnt, true, true, true, true, false, true, false); + CWorld::bIncludeDeadPeds = false; + if (foundEnt == m_carInObjective) { + SetAttack(m_carInObjective); + SetWeaponLockOnTarget(m_carInObjective); + SetShootTimer(CGeneral::GetRandomNumberInRange(500, 2000)); + if (distWithTargetSc > 10.0f && !bKindaStayInSamePlace) { + SetAttackTimer(CGeneral::GetRandomNumberInRange(2000, 5000)); + } else { + SetAttackTimer(CGeneral::GetRandomNumberInRange(50, 300)); + SetMoveState(PEDMOVE_STILL); + } + } + } else if (m_nPedState != PED_ATTACK && !bKindaStayInSamePlace) { + if (wepRange <= 5.0f) { + if (Abs(distWithTarget.x) > wepRange || Abs(distWithTarget.y) > wepRange || + (distWithTarget.z > -1.0f && distWithTarget.z < 0.3)) { + SetSeek(m_carInObjective, 3.0f); + SetMoveState(PEDMOVE_RUN); + } else { + SetIdle(); + } + } else { + float safeDistance = wepRange * 0.25f; + + SetSeek(m_carInObjective, safeDistance); + SetMoveState(PEDMOVE_RUN); + } + } + SetLookFlag(m_carInObjective, false); + TurnBody(); + break; + } + case OBJECTIVE_GOTO_AREA_ON_FOOT: + case OBJECTIVE_RUN_TO_AREA: + case OBJECTIVE_SPRINT_TO_AREA: + { + if (InVehicle()) { + SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); + } else { + distWithTarget = m_nextRoutePointPos - GetPosition(); + distWithTarget.z = 0.0f; + if (sq(m_distanceToCountSeekDone) >= distWithTarget.MagnitudeSqr()) { + bObjectiveCompleted = true; + bScriptObjectiveCompleted = true; + SetMoveState(PEDMOVE_STILL); + } else if (CTimer::GetTimeInMilliseconds() > m_nPedStateTimer || m_nPedState != PED_SEEK_POS) { + if (bUsePedNodeSeek) { + CVector bestCoords(0.0f, 0.0f, 0.0f); + m_vecSeekPos = m_nextRoutePointPos; + + if (!m_pNextPathNode) { + bool found = FindBestCoordsFromNodes(m_vecSeekPos, &bestCoords); + if (m_pNextPathNode) { + // Because it already does that if it finds better coords. + if (!found) { + bestCoords = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); + } + if ((bestCoords - GetPosition()).Magnitude2D() < m_distanceToCountSeekDone) { + m_pNextPathNode = nil; + bUsePedNodeSeek = false; + } + } + } + if (m_pNextPathNode) + m_vecSeekPos = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); + } + CVector seekPos = m_vecSeekPos; + SetSeek(m_vecSeekPos, m_distanceToCountSeekDone); + } + } + + break; + } + case OBJECTIVE_GUARD_ATTACK: + { + if (m_pedInObjective) { + SetLookFlag(m_pedInObjective, true); + m_pLookTarget = m_pedInObjective; + m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); + m_lookTimer = m_attackTimer; + TurnBody(); + float distWithTargetSc = distWithTarget.Magnitude(); + if (distWithTargetSc >= 20.0f) { + RestorePreviousObjective(); + } else if (m_attackTimer < CTimer::GetTimeInMilliseconds()) { + if (m_nPedState != PED_SEEK_ENTITY && distWithTargetSc >= 2.0f) { + SetSeek(m_pedInObjective, 1.0f); + } else { + SetAttack(m_pedInObjective); + SetShootTimer(CGeneral::GetRandomNumberInRange(500.0f, 1500.0f)); + } + SetAttackTimer(1000); + } + } else { + RestorePreviousObjective(); + } + break; + } + case OBJECTIVE_FOLLOW_ROUTE: + if (HaveReachedNextPointOnRoute(1.0f)) { + int nextPoint = GetNextPointOnRoute(); + m_nextRoutePointPos = CRouteNode::GetPointPosition(nextPoint); + } else { + CVector seekPos = m_nextRoutePointPos; + SetSeek(seekPos, 0.8f); + } + break; + case OBJECTIVE_SOLICIT_VEHICLE: + if (m_carInObjective) { + if (m_objectiveTimer <= CTimer::GetTimeInMilliseconds()) { + if (!bInVehicle) { + SetObjective(OBJECTIVE_NONE); + SetWanderPath(CGeneral::GetRandomNumber() & 7); + m_objectiveTimer = CTimer::GetTimeInMilliseconds() + 10000; + if (IsPedInControl()) + m_pMyVehicle = nil; + } + } else { + if (m_nPedState != PED_FLEE_ENTITY && m_nPedState != PED_SOLICIT) + SetSeekCar(m_carInObjective, 0); + } + } else { + RestorePreviousObjective(); + RestorePreviousState(); + if (IsPedInControl()) + m_pMyVehicle = nil; + } + break; + case OBJECTIVE_HAIL_TAXI: + if (!RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_HAILTAXI) && CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + Say(SOUND_PED_TAXI_WAIT); + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HAILTAXI, 4.0f); + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 2000; + } + break; + case OBJECTIVE_CATCH_TRAIN: + { + if (m_carInObjective) { + SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, m_carInObjective); + } else { + CVehicle* trainToEnter = nil; + float closestCarDist = CHECK_NEARBY_THINGS_MAX_DIST; + CVector pos = GetPosition(); + int16 lastVehicle; + CEntity* vehicles[8]; + + CWorld::FindObjectsInRange(pos, 10.0f, true, &lastVehicle, 6, vehicles, false, true, false, false, false); + for (int i = 0; i < lastVehicle; i++) { + CVehicle* nearVeh = (CVehicle*)vehicles[i]; + if (nearVeh->IsTrain()) { + CVector vehDistVec = GetPosition() - nearVeh->GetPosition(); + float vehDist = vehDistVec.Magnitude(); + if (vehDist < closestCarDist && m_pedInObjective->m_pMyVehicle != nearVeh) + { + trainToEnter = nearVeh; + closestCarDist = vehDist; + } + } + } + if (trainToEnter) { + m_carInObjective = trainToEnter; + m_carInObjective->RegisterReference((CEntity **) &m_carInObjective); + } + } + break; + } + case OBJECTIVE_BUY_ICE_CREAM: + if (m_carInObjective) { + if (m_nPedState != PED_FLEE_ENTITY && m_nPedState != PED_BUY_ICECREAM && m_nPedState != PED_CHAT) + SetSeekCar(m_carInObjective, 0); + } + break; + case OBJECTIVE_STEAL_ANY_CAR: + case OBJECTIVE_STEAL_ANY_MISSION_CAR: + { + if (bInVehicle) { + bScriptObjectiveCompleted = true; + RestorePreviousObjective(); + } else if (m_carJackTimer < CTimer::GetTimeInMilliseconds()) { + CVehicle *carToSteal = nil; + float closestCarDist = nEnterCarRangeMultiplier * ENTER_CAR_MAX_DIST; + CVector pos = GetPosition(); + int16 lastVehicle; + CEntity *vehicles[8]; + + CWorld::FindObjectsInRange(pos, closestCarDist, true, &lastVehicle, 6, vehicles, false, true, false, false, false); + for(int i = 0; i < lastVehicle; i++) { + CVehicle *nearVeh = (CVehicle*)vehicles[i]; + if (m_objective == OBJECTIVE_STEAL_ANY_MISSION_CAR || nearVeh->VehicleCreatedBy != MISSION_VEHICLE) { + if (nearVeh->m_vecMoveSpeed.Magnitude() <= 0.1f) { + if (nearVeh->CanPedOpenLocks(this)) { + CVector vehDistVec = GetPosition() - nearVeh->GetPosition(); + float vehDist = vehDistVec.Magnitude(); + if (vehDist < closestCarDist) { + carToSteal = nearVeh; + closestCarDist = vehDist; + } + } + } + } + } + if (carToSteal) { + SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, carToSteal); + m_carJackTimer = CTimer::GetTimeInMilliseconds() + 5000; + } else { + RestorePreviousObjective(); + RestorePreviousState(); + } + } + break; + } + case OBJECTIVE_MUG_CHAR: + { + if (m_pedInObjective) { + if (m_pedInObjective->IsPlayer() || m_pedInObjective->bInVehicle || m_pedInObjective->m_fHealth <= 0.0f) { + ClearObjective(); + return; + } + if (m_pedInObjective->m_nMoveState > PEDMOVE_WALK) { + ClearObjective(); + return; + } + if (m_pedInObjective->m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT && m_pedInObjective->m_pedInObjective == this) { + SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, m_pedInObjective); + SetMoveState(PEDMOVE_SPRINT); + return; + } + if (m_pedInObjective->m_nPedState == PED_FLEE_ENTITY && m_fleeFrom == this + || m_pedInObjective->m_objective == OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE && m_pedInObjective->m_pedInObjective == this) { + ClearObjective(); + SetFindPathAndFlee(m_pedInObjective, 15000, true); + return; + } + float distWithTargetScSqr = distWithTarget.MagnitudeSqr(); + if (distWithTargetScSqr <= sq(10.0f)) { + if (distWithTargetScSqr <= sq(1.4f)) { + CAnimBlendAssociation *reloadAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_PARTIAL_FUCKU); + m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( + m_pedInObjective->GetPosition().x, m_pedInObjective->GetPosition().y, + GetPosition().x, GetPosition().y); + + if (reloadAssoc || !m_pedInObjective->IsPedShootable()) { + if (reloadAssoc && + (!reloadAssoc->IsRunning() || reloadAssoc->GetProgress() > 0.8f)) { + CAnimBlendAssociation *punchAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_PARTIAL_PUNCH, 8.0f); + punchAssoc->flags |= ASSOC_DELETEFADEDOUT; + punchAssoc->flags |= ASSOC_FADEOUTWHENDONE; + CVector2D offset(distWithTarget.x, distWithTarget.y); + int dir = m_pedInObjective->GetLocalDirection(offset); + m_pedInObjective->StartFightDefend(dir, HITLEVEL_HIGH, 5); + m_pedInObjective->ReactToAttack(this); + m_pedInObjective->Say(SOUND_PED_ROBBED); + Say(SOUND_PED_MUGGING); + bRichFromMugging = true; + + // FIX: ClearObjective() clears m_pedInObjective, so get it before call + CPed *victim = m_pedInObjective; + ClearObjective(); + if (victim->m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT + || victim->m_pedInObjective != this) { + SetFindPathAndFlee(victim, 15000, true); + m_nLastPedState = PED_WANDER_PATH; + } else { + SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, victim); + SetMoveState(PEDMOVE_SPRINT); + m_nLastPedState = PED_WANDER_PATH; + } + } + } else { + eWeaponType weaponType = GetWeapon()->m_eWeaponType; + if (weaponType != WEAPONTYPE_UNARMED && weaponType != WEAPONTYPE_BASEBALLBAT) + SetCurrentWeapon(WEAPONTYPE_UNARMED); + + CAnimBlendAssociation *newReloadAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_PARTIAL_FUCKU, 8.0f); + newReloadAssoc->flags |= ASSOC_DELETEFADEDOUT; + newReloadAssoc->flags |= ASSOC_FADEOUTWHENDONE; + } + } else { + SetSeek(m_pedInObjective, 1.0f); + CAnimBlendAssociation *walkAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_WALK); + + if (walkAssoc) + walkAssoc->speed = 1.3f; + } + } else { + ClearObjective(); + SetWanderPath(CGeneral::GetRandomNumber() & 7); + } + } else { + m_objective = OBJECTIVE_NONE; + ClearObjective(); + } + } + // fall through + case OBJECTIVE_WANDER: + if (CTimer::GetTimeInMilliseconds() > m_leaveCarTimer && !bInVehicle) { + m_leaveCarTimer = 0; + m_objective = OBJECTIVE_NONE; + SetWanderPath(m_nPathDir); + } + break; + case OBJECTIVE_LEAVE_CAR_AND_DIE: + { + if (CTimer::GetTimeInMilliseconds() > m_leaveCarTimer) { + if (InVehicle()) { + if (m_nPedState != PED_EXIT_CAR && m_nPedState != PED_DRAG_FROM_CAR && m_nPedState != PED_EXIT_TRAIN) { + if (m_pMyVehicle->IsBoat()) + SetExitBoat(m_pMyVehicle); + else if (m_pMyVehicle->bIsBus) + SetExitCar(m_pMyVehicle, 0); + else { + eCarNodes doorNode = CAR_DOOR_LF; + if (m_pMyVehicle->pDriver != this) { + if (m_pMyVehicle->pPassengers[0] == this) { + doorNode = CAR_DOOR_RF; + } else if (m_pMyVehicle->pPassengers[1] == this) { + doorNode = CAR_DOOR_LR; + } else if (m_pMyVehicle->pPassengers[2] == this) { + doorNode = CAR_DOOR_RR; + } + } + SetBeingDraggedFromCar(m_pMyVehicle, doorNode, false); + } + } + } + } + break; + } + case OBJECTIVE_GOTO_AREA_ANY_MEANS: + { + distWithTarget = m_nextRoutePointPos - GetPosition(); + distWithTarget.z = 0.0f; + if (InVehicle()) { + CCarAI::GetCarToGoToCoors(m_pMyVehicle, &m_nextRoutePointPos); + CCarCtrl::RegisterVehicleOfInterest(m_pMyVehicle); + if (distWithTarget.MagnitudeSqr() < sq(20.0f)) { + m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; + ForceStoredObjective(OBJECTIVE_GOTO_AREA_ANY_MEANS); + SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); + } + break; + } + if (distWithTarget.Magnitude() > 30.0f) { + if (m_pMyVehicle) { + m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; + } else { + float closestVehDist = SQR(60.0f); + int16 lastVehicle; + CEntity* vehicles[8]; + // NB: 25.0f in here is prolly a forgotten setting, all other places now use 30.0f (ENTER_CAR_MAX_DIST) + CWorld::FindObjectsInRange(GetPosition(), 25.0f, true, &lastVehicle, 6, vehicles, false, true, false, false, false); + CVehicle* foundVeh = nil; + for (int i = 0; i < lastVehicle; i++) { + CVehicle* nearVeh = (CVehicle*)vehicles[i]; + /* + Not used. + CVector vehSpeed = nearVeh->GetSpeed(); + CVector ourSpeed = GetSpeed(); + */ + CVector vehDistVec = nearVeh->GetPosition() - GetPosition(); + if (vehDistVec.MagnitudeSqr() < closestVehDist && m_pedInObjective->m_pMyVehicle != nearVeh) { + foundVeh = nearVeh; + closestVehDist = vehDistVec.MagnitudeSqr(); + } + } + m_pMyVehicle = foundVeh; + if (m_pMyVehicle) { + m_pMyVehicle->RegisterReference((CEntity **) &m_pMyVehicle); + m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; + SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pMyVehicle); + } + } + break; + } + // Falls to different objectives in III and VC +#ifdef FIX_BUGS + break; +#else + // fall through +#endif + } + case OBJECTIVE_GOTO_SEAT_ON_FOOT: + case OBJECTIVE_GOTO_ATM_ON_FOOT: + case OBJECTIVE_GOTO_BUS_STOP_ON_FOOT: + case OBJECTIVE_GOTO_PIZZA_ON_FOOT: + case OBJECTIVE_GOTO_SHELTER_ON_FOOT: + case OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT: + if (CTimer::GetTimeInMilliseconds() > m_objectiveTimer) { + m_objectiveTimer = 0; + if (m_attractor) + GetPedAttractorManager()->DeRegisterPed(this, m_attractor); + } else { + CVector distance = m_nextRoutePointPos - GetPosition(); + distance.z = 0.0f; + if (m_objective == OBJECTIVE_GOTO_SHELTER_ON_FOOT) { + if (m_nMoveState == PEDMOVE_RUN && distance.MagnitudeSqr() < SQR(2.0f)) { + SetMoveState(PEDMOVE_WALK); + bIsRunning = false; + } + if (CWeather::Rain < 0.2f && m_attractor) { + GetPedAttractorManager()->DeRegisterPed(this, m_attractor); + return; + } + } + else if (m_objective == OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT) { + if (m_nMoveState == PEDMOVE_RUN && distance.MagnitudeSqr() < SQR(4.0f)) { + SetMoveState(PEDMOVE_WALK); + bIsRunning = false; + } + CVehicle* pIceCreamVan = GetPedAttractorManager()->GetIceCreamVanForEffect(m_attractor->GetEffect()); + if (0.01f * CTimer::GetTimeStep() * 5.0f < pIceCreamVan->m_fDistanceTravelled) { + GetPedAttractorManager()->DeRegisterPed(this, m_attractor); + return; + } + if (!pIceCreamVan->pDriver || + !pIceCreamVan->pDriver->IsPlayer() || + pIceCreamVan->pDriver->GetPedState() == PED_ARRESTED || + pIceCreamVan->pDriver->GetPedState() == PED_DEAD) { + GetPedAttractorManager()->DeRegisterPed(this, m_attractor); + return; + } + if (!pIceCreamVan->m_bSirenOrAlarm) { + GetPedAttractorManager()->DeRegisterPed(this, m_attractor); + return; + } + if (pIceCreamVan->GetStatus() == STATUS_WRECKED) { + GetPedAttractorManager()->DeRegisterPed(this, m_attractor); + return; + } + } + if (sq(m_distanceToCountSeekDone) < distance.MagnitudeSqr()) { + if (CTimer::GetTimeInMilliseconds() > m_nPedStateTimer || GetPedState() != PED_SEEK_POS) { + m_vecSeekPos = m_nextRoutePointPos; + SetSeek(m_vecSeekPos, m_distanceToCountSeekDone); + } + } + else { + if (!bReachedAttractorHeadingTarget) { + float fHeadingDistance = m_fRotationCur - m_attractorHeading; + float fSinHeading = Sin(fHeadingDistance); + float fCosHeading = Cos(fHeadingDistance); + if (fSinHeading > 0.0f) { + if (fCosHeading > 0.0f) + m_attractorHeading = m_fRotationCur - Asin(fSinHeading); + else + m_attractorHeading = m_fRotationCur - Acos(fCosHeading); + } + else { + if (fCosHeading > 0.0f) + m_attractorHeading = m_fRotationCur - Asin(fSinHeading); + else + m_attractorHeading = m_fRotationCur + Acos(fCosHeading); + } + m_fRotationDest = m_attractorHeading; + m_headingRate = 3.5f; + bReachedAttractorHeadingTarget = true; + bTurnedAroundOnAttractor = false; + } + if (Abs(m_fRotationCur - m_attractorHeading) >= m_acceptableHeadingOffset && + Abs(m_fRotationCur - m_attractorHeading + TWOPI) >= m_acceptableHeadingOffset && + Abs(m_fRotationCur - m_attractorHeading - TWOPI) >= m_acceptableHeadingOffset) + SetMoveState(PEDMOVE_STILL); + else { + m_fRotationDest = m_fRotationCur; + bReachedAttractorHeadingTarget = false; + bObjectiveCompleted = true; + bScriptObjectiveCompleted = true; + RestoreHeadingRate(); + GetPedAttractorManager()->BroadcastArrival(this, m_attractor); + if (GetPedAttractorManager()->IsAtHeadOfQueue(this, m_attractor)) { + switch (m_objective) { + case OBJECTIVE_GOTO_SEAT_ON_FOOT: + if (!bTurnedAroundOnAttractor) { + ClearObjective(); + SetWaitState(WAITSTATE_SIT_DOWN, 0); + } + else { + ClearObjective(); + SetWaitState(WAITSTATE_SIT_DOWN_RVRS, 0); + } + break; + case OBJECTIVE_GOTO_ATM_ON_FOOT: + ClearObjective(); + SetWaitState(WAITSTATE_USE_ATM, 0); + break; + case OBJECTIVE_GOTO_BUS_STOP_ON_FOOT: + ClearObjective(); + SetObjective(OBJECTIVE_WAIT_ON_FOOT_AT_BUS_STOP); + break; + case OBJECTIVE_GOTO_PIZZA_ON_FOOT: + ClearObjective(); + m_prevObjective = OBJECTIVE_NONE; + SetObjective(OBJECTIVE_WAIT_ON_FOOT); + m_objectiveTimer = CTimer::GetTimeInMilliseconds() + m_attractor->GetHeadOfQueueWaitTime(); + break; + case OBJECTIVE_GOTO_SHELTER_ON_FOOT: + m_prevObjective = OBJECTIVE_NONE; + SetObjective(OBJECTIVE_WAIT_ON_FOOT_AT_SHELTER); + break; + case OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT: + m_prevObjective = OBJECTIVE_NONE; + SetObjective(OBJECTIVE_WAIT_ON_FOOT_AT_ICE_CREAM_VAN); + break; + } + } else { + m_prevObjective = OBJECTIVE_NONE; + SetObjective(OBJECTIVE_WAIT_ON_FOOT); + m_objectiveTimer = 0; + } + } + } + } + return; + case OBJECTIVE_FLEE_CAR: + if (!bInVehicle && m_nPedState != PED_FLEE_ENTITY && m_pMyVehicle) { + RestorePreviousObjective(); + SetFlee(m_pMyVehicle, 6000); + break; + } + // fall through + case OBJECTIVE_LEAVE_CAR: + if (CTimer::GetTimeInMilliseconds() > m_leaveCarTimer) { + if (InVehicle() && + (FindPlayerPed() != this || !CPad::GetPad(0)->GetAccelerate() || bBusJacked)) { + + if (m_nPedState != PED_EXIT_CAR && m_nPedState != PED_DRAG_FROM_CAR && m_nPedState != PED_EXIT_TRAIN + && (m_nPedType != PEDTYPE_COP + || m_pMyVehicle->IsBoat() + || m_pMyVehicle->m_vecMoveSpeed.MagnitudeSqr2D() < sq(0.005f))) { +#ifdef GTA_TRAIN + if (m_pMyVehicle->IsTrain()) + SetExitTrain(m_pMyVehicle); + else +#endif + if (m_pMyVehicle->IsBoat()) + SetExitBoat(m_pMyVehicle); + else + SetExitCar(m_pMyVehicle, 0); + } + } else { + RestorePreviousObjective(); + } + } + if (bHeldHostageInCar) { + if (CTheScripts::IsPlayerOnAMission()) { + CVehicle *playerVeh = FindPlayerVehicle(); + if (playerVeh && playerVeh->IsPassenger(this)) { + if (m_leaveCarTimer != 0) + m_leaveCarTimer = 0; + } + } + } + break; + case OBJECTIVE_AIM_GUN_AT: + if (m_pedInObjective) { + if (!bObstacleShowedUpDuringKillObjective) + SetPointGunAt(m_pedInObjective); + + if (m_nMoveState == PEDMOVE_STILL && IsPedInControl()) { + SetLookFlag(m_pedInObjective, false); + TurnBody(); + } + } else { + ClearObjective(); + } + break; + case OBJECTIVE_WAIT_ON_FOOT_AT_SHELTER: + SetIdle(); + if (m_attractor && CWeather::Rain < 0.2f) + GetPedAttractorManager()->DeRegisterPed(this, m_attractor); + break; + case OBJECTIVE_KILL_CHAR_ON_BOAT: + SetPedStats(PEDSTAT_TOUGH_GUY); + if (bInVehicle) { + if (m_pedInObjective && m_pedInObjective->m_pCurrentPhysSurface != m_pMyVehicle) { + bObjectiveCompleted = true; + ClearObjective(); + CCarCtrl::SwitchVehicleToRealPhysics(m_pMyVehicle); + m_pMyVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + m_pMyVehicle->AutoPilot.m_nCruiseSpeed = GAME_SPEED_TO_CARAI_SPEED * m_pMyVehicle->pHandling->Transmission.fMaxCruiseVelocity; + m_pMyVehicle->SetStatus(STATUS_PHYSICS); + } else { + SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); + } + } else if (m_pedInObjective && !m_pedInObjective->DyingOrDead() && + (!m_pCurrentPhysSurface || !m_pedInObjective->m_pCurrentPhysSurface || + m_pedInObjective->m_pCurrentPhysSurface == m_pCurrentPhysSurface)) { + + CBoat *boatWeAreOn = nil; + if (m_pCurrentPhysSurface && m_pCurrentPhysSurface->IsVehicle() && ((CVehicle*)m_pCurrentPhysSurface)->IsBoat()) + boatWeAreOn = (CBoat*)m_pCurrentPhysSurface; + + if (boatWeAreOn) { + SetObjective(OBJECTIVE_RUN_TO_AREA, boatWeAreOn->GetPosition() - (boatWeAreOn->GetForward() * 10.f)); + } else if (m_pedInObjective) { + SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS, m_pedInObjective); + } + SetMoveAnim(); + } else { + ClearLookFlag(); + SetMoveAnim(); + if (m_pCurrentPhysSurface && m_pCurrentPhysSurface->IsVehicle()) + SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pCurrentPhysSurface); + } + break; + case OBJECTIVE_SOLICIT_FOOT: + if (m_pedInObjective) { + if (m_objectiveTimer < CTimer::GetTimeInMilliseconds()) + bScriptObjectiveCompleted = true; + } else { + bScriptObjectiveCompleted = true; + } + break; + case OBJECTIVE_WAIT_ON_FOOT_AT_BUS_STOP: + SetIdle(); + if (m_attractor) { + float left = GetPosition().x - 10.0f; + float right = GetPosition().x + 10.0f; + float top = GetPosition().y - 10.0f; + float bottom = GetPosition().y + 10.0f; + int xstart = Max(0, CWorld::GetSectorIndexX(left)); + int xend = Min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(right)); + int ystart = Max(0, CWorld::GetSectorIndexY(top)); + int yend = Min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(bottom)); + assert(xstart <= xend); + assert(ystart <= yend); + + float minDistance = SQR(10.0f); + CVehicle* pBus = nil; + + for (int y = ystart; y <= yend; y++) { + for (int x = xstart; x <= xend; x++) { + CSector* s = CWorld::GetSector(x, y); + for (CPtrNode* pNode = s->m_lists[ENTITYLIST_VEHICLES].first; pNode != nil; pNode = pNode->next) { + CEntity* pEntity = (CEntity*)pNode->item; + if (!pEntity->IsVehicle()) + continue; + CVehicle* pVehicle = (CVehicle*)pEntity; + if (!pVehicle->bIsBus) + continue; + if (pVehicle->GetMoveSpeed().MagnitudeSqr() >= SQR(0.005f)) + continue; + float distanceSq = (GetPosition() - pVehicle->GetPosition()).MagnitudeSqr(); + if (distanceSq < minDistance) { + minDistance = distanceSq; + pBus = pVehicle; + } + } + } + } + + if (pBus) { + if (pBus->m_nNumPassengers >= pBus->m_nNumMaxPassengers - 1) + SetObjective(OBJECTIVE_WAIT_ON_FOOT); + else { + GetPedAttractorManager()->DeRegisterPed(this, m_attractor); + SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, pBus); + bDontDragMeOutCar = true; + bRemoveMeWhenIGotIntoCar = true; + CPlayerPed *player = FindPlayerPed(); + if (pBus->IsPassenger(player) || pBus->IsDriver(player)) { + bCollectBusFare = true; + } + } + } + } + break; + case OBJECTIVE_WAIT_ON_FOOT_AT_ICE_CREAM_VAN: + { + SetIdle(); + CVehicle* pIceCreamVan = GetPedAttractorManager()->GetIceCreamVanForEffect(m_attractor->GetEffect()); + if (m_attractor && m_nWaitState != WAITSTATE_PLAYANIM_CHAT && pIceCreamVan && pIceCreamVan->pDriver && pIceCreamVan->pDriver->IsPlayer()) { + int time = 5000; + SetWaitState(WAITSTATE_PLAYANIM_CHAT, &time); + break; + } + if (!m_attractor) + break; + CVehicle* pVan = GetPedAttractorManager()->GetIceCreamVanForEffect(m_attractor->GetEffect()); + if (!pVan) + break; + if (0.01f * CTimer::GetTimeStep() * 5.0f < pVan->m_fDistanceTravelled) { + GetPedAttractorManager()->DeRegisterPed(this, m_attractor); + break; + } + if (!pVan->pDriver || !pVan->pDriver->IsPlayer() || pVan->pDriver->GetPedState() == PED_ARRESTED || pVan->pDriver->GetPedState() == PED_DEAD) { + GetPedAttractorManager()->DeRegisterPed(this, m_attractor); + break; + } + if (!pVan->m_bSirenOrAlarm) { + GetPedAttractorManager()->DeRegisterPed(this, m_attractor); + return; // Why? + } + if (pVan->GetStatus() == STATUS_WRECKED) { + GetPedAttractorManager()->DeRegisterPed(this, m_attractor); + return; // Why? + } + break; + } + } + if (bObjectiveCompleted + || m_objectiveTimer > 0 && CTimer::GetTimeInMilliseconds() > m_objectiveTimer) { + RestorePreviousObjective(); + if (m_objectiveTimer > CTimer::GetTimeInMilliseconds() || !m_objectiveTimer) + m_objectiveTimer = CTimer::GetTimeInMilliseconds() - 1; + + if (CharCreatedBy != RANDOM_CHAR || bInVehicle) { + if (IsPedInControl()) + RestorePreviousState(); + } else { + SetWanderPath(CGeneral::GetRandomNumber() & 7); + } + ClearAimFlag(); + ClearLookFlag(); + } + } +} + +void +CPed::SetFollowRoute(int16 currentPoint, int16 routeType) +{ + m_routeLastPoint = currentPoint; + m_routeType = routeType; + m_routePointsBeingPassed = 1; + m_routePointsPassed = 0; + m_objective = OBJECTIVE_FOLLOW_ROUTE; + m_routeStartPoint = CRouteNode::GetRouteStart(currentPoint); + m_nextRoutePointPos = CRouteNode::GetPointPosition(GetNextPointOnRoute()); +} + +int +CPed::GetNextPointOnRoute(void) +{ + int16 nextPoint = m_routePointsBeingPassed + m_routePointsPassed + m_routeStartPoint; + + // Route is complete + if (nextPoint < 0 || nextPoint > NUMPEDROUTES || m_routeLastPoint != CRouteNode::GetRouteThisPointIsOn(nextPoint)) { + + switch (m_routeType) { + case PEDROUTE_STOP_WHEN_DONE: + nextPoint = -1; + break; + case PEDROUTE_GO_BACKWARD_WHEN_DONE: + m_routePointsBeingPassed = -m_routePointsBeingPassed; + nextPoint = m_routePointsBeingPassed + m_routePointsPassed + m_routeStartPoint; + break; + case PEDROUTE_GO_TO_START_WHEN_DONE: + m_routePointsPassed = -1; + nextPoint = m_routePointsBeingPassed + m_routePointsPassed + m_routeStartPoint; + break; + default: + break; + } + } + return nextPoint; +} + +bool +CPed::HaveReachedNextPointOnRoute(float distToCountReached) +{ + if ((m_nextRoutePointPos - GetPosition()).Magnitude2D() < distToCountReached) { + m_routePointsPassed += m_routePointsBeingPassed; + return true; + } + return false; +} + +bool +CPed::CanSeeEntity(CEntity *entity, float threshold) +{ + float neededAngle = CGeneral::GetRadianAngleBetweenPoints( + entity->GetPosition().x, + entity->GetPosition().y, + GetPosition().x, + GetPosition().y); + + if (neededAngle < 0.0f) + neededAngle += TWOPI; + else if (neededAngle > TWOPI) + neededAngle -= TWOPI; + + float ourAngle = m_fRotationCur; + if (ourAngle < 0.0f) + ourAngle += TWOPI; + else if (ourAngle > TWOPI) + ourAngle -= TWOPI; + + float neededTurn = Abs(neededAngle - ourAngle); + + return neededTurn < threshold || TWOPI - threshold < neededTurn; +} + +// Only used while deciding which gun ped should switch to, if no ammo left. +bool +CPed::SelectGunIfArmed(void) +{ + for (int i = 0; i < TOTAL_WEAPON_SLOTS; i++) { + if (GetWeapon(i).m_nAmmoTotal > 0) { + eWeaponType weaponType = GetWeapon(i).m_eWeaponType; + + if (weaponType == WEAPONTYPE_COLT45 || weaponType == WEAPONTYPE_UZI || weaponType == WEAPONTYPE_MP5 || weaponType == WEAPONTYPE_M4 || + weaponType == WEAPONTYPE_COLT45 || weaponType == WEAPONTYPE_PYTHON || weaponType == WEAPONTYPE_SHOTGUN || + weaponType == WEAPONTYPE_SPAS12_SHOTGUN || weaponType == WEAPONTYPE_STUBBY_SHOTGUN || + weaponType == WEAPONTYPE_ROCKETLAUNCHER || weaponType == WEAPONTYPE_SNIPERRIFLE || weaponType == WEAPONTYPE_FLAMETHROWER) { + SetCurrentWeapon(i); + return true; + } + } + } + SetCurrentWeapon(WEAPONTYPE_UNARMED); + return false; +} +void +CPed::ReactToPointGun(CEntity *entWithGun) +{ + CPed *pedWithGun = (CPed*)entWithGun; + int waitTime; + + if (IsPlayer() || !IsPedInControl() || (CharCreatedBy == MISSION_CHAR && !bCrouchWhenScared)) + return; + + if (m_leader == pedWithGun) + return; + + if (m_nWaitState == WAITSTATE_PLAYANIM_HANDSUP || m_nWaitState == WAITSTATE_PLAYANIM_HANDSCOWER || + (GetPosition() - pedWithGun->GetPosition()).MagnitudeSqr2D() > 225.0f || (m_nPedType == PEDTYPE_GANG7 && pedWithGun == FindPlayerPed())) + return; + + if (m_leader) { + if (FindPlayerPed() == m_leader) + return; + + ClearLeader(); + } + if (m_pedStats->m_flags & STAT_GUN_PANIC + && (m_nPedState != PED_ATTACK || GetWeapon()->IsTypeMelee()) + && m_nPedState != PED_FLEE_ENTITY && m_nPedState != PED_AIM_GUN) { + + waitTime = CGeneral::GetRandomNumberInRange(3000, 6000); + SetWaitState(WAITSTATE_PLAYANIM_HANDSCOWER, &waitTime); + Say(SOUND_PED_HANDS_COWER); + m_pLookTarget = pedWithGun; + m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); + SetMoveState(PEDMOVE_NONE); + + } else if (m_nPedType != pedWithGun->m_nPedType) { + if (IsGangMember() || m_nPedType == PEDTYPE_EMERGENCY || m_nPedType == PEDTYPE_FIREMAN) { + RegisterThreatWithGangPeds(pedWithGun); + } + + if (m_nPedType == PEDTYPE_COP) { + if (pedWithGun->IsPlayer()) { + ((CPlayerPed*)pedWithGun)->m_pWanted->SetWantedLevelNoDrop(2); + if (bCrouchWhenShooting || bKindaStayInSamePlace) { + SetDuck(CGeneral::GetRandomNumberInRange(1000, 3000)); + return; + } + } + } + + if (m_nPedType != PEDTYPE_COP + && (m_nPedState != PED_ATTACK || GetWeapon()->IsTypeMelee()) + && (m_nPedState != PED_FLEE_ENTITY || pedWithGun->IsPlayer() && m_fleeFrom != pedWithGun) + && m_nPedState != PED_AIM_GUN && m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT) { + + waitTime = CGeneral::GetRandomNumberInRange(3000, 6000); + SetWaitState(WAITSTATE_PLAYANIM_HANDSUP, &waitTime); + Say(SOUND_PED_HANDS_UP); + m_pLookTarget = pedWithGun; + m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); + SetMoveState(PEDMOVE_NONE); + if (m_nPedState == PED_FLEE_ENTITY) { + m_fleeFrom = pedWithGun; + m_fleeFrom->RegisterReference((CEntity **) &m_fleeFrom); + } + + if (FindPlayerPed() == pedWithGun && bRichFromMugging) { + int money = CGeneral::GetRandomNumberInRange(100, 300); + int pickupCount = money / 40 + 1; + int moneyPerPickup = money / pickupCount; + + for (int i = 0; i < pickupCount; i++) { + float pickupX = 1.5f * Sin((CGeneral::GetRandomNumber() % 256)/256.0f * TWOPI) + GetPosition().x; + float pickupY = 1.5f * Cos((CGeneral::GetRandomNumber() % 256)/256.0f * TWOPI) + GetPosition().y; + bool found = false; + float groundZ = CWorld::FindGroundZFor3DCoord(pickupX, pickupY, GetPosition().z, &found) + 0.5f; + if (found) { + CPickups::GenerateNewOne(CVector(pickupX, pickupY, groundZ), MI_MONEY, PICKUP_MONEY, moneyPerPickup + (CGeneral::GetRandomNumber() & 7)); + } + } + bRichFromMugging = false; + } + } + } +} + +void +CPed::ReactToAttack(CEntity *attacker) +{ + if (IsPlayer() && attacker->IsPed()) { + InformMyGangOfAttack(attacker); + SetLookFlag(attacker, true); + SetLookTimer(700); + return; + } + + if (m_nPedType == PEDTYPE_GANG7 && attacker->IsPed() && ((CPed*)attacker)->IsPlayer()) { + if (m_nPedState != PED_FALL) { + SetFall(15000, (AnimationId)(ANIM_STD_KO_FRONT + CGeneral::GetRandomNumberInRange(0, 5)), 0); + } + + } else if (m_nPedState == PED_DRIVING && InVehicle() + && (m_pMyVehicle->pDriver == this || m_pMyVehicle->pDriver && m_pMyVehicle->pDriver->m_nPedState == PED_DRIVING && m_pMyVehicle->pDriver->m_objective != OBJECTIVE_LEAVE_CAR_AND_DIE)) { + + if (m_pMyVehicle->VehicleCreatedBy == RANDOM_VEHICLE + && (m_pMyVehicle->GetStatus() == STATUS_SIMPLE || m_pMyVehicle->GetStatus() == STATUS_PHYSICS) + && m_pMyVehicle->AutoPilot.m_nCarMission == MISSION_CRUISE) { + + CCarCtrl::SwitchVehicleToRealPhysics(m_pMyVehicle); + m_pMyVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + m_pMyVehicle->AutoPilot.m_nCruiseSpeed = GAME_SPEED_TO_CARAI_SPEED * m_pMyVehicle->pHandling->Transmission.fMaxCruiseVelocity; + m_pMyVehicle->SetStatus(STATUS_PHYSICS); + } + + } else if ((IsPedInControl() || m_nPedState == PED_DRIVING) && (CharCreatedBy != MISSION_CHAR || bRespondsToThreats)) { + if (m_leader != attacker && (!m_leader || FindPlayerPed() != m_leader) && attacker->IsPed()) { + + CPed *attackerPed = (CPed*)attacker; + if (bNotAllowedToDuck) { + if (!attackerPed->GetWeapon()->IsTypeMelee()) { + m_duckAndCoverTimer = CTimer::GetTimeInMilliseconds(); + return; + } + } else if (bCrouchWhenShooting || bKindaStayInSamePlace) { + SetDuck(CGeneral::GetRandomNumberInRange(1000,3000)); + return; + } + + if (m_nWaitState == WAITSTATE_STRIPPER) { + ClearWaitState(); + SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, attacker); + SetObjectiveTimer(20000); + + } else { + if (m_pedStats->m_fear <= 100 - attackerPed->m_pedStats->m_temper) { + if (m_pedStats != attackerPed->m_pedStats) { + if (IsGangMember() || m_nPedType == PEDTYPE_EMERGENCY || m_nPedType == PEDTYPE_FIREMAN) { + RegisterThreatWithGangPeds(attackerPed); + } + if (!attackerPed->GetWeapon()->IsTypeMelee() && GetWeapon()->IsTypeMelee()) { + SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, attacker); + SetMoveState(PEDMOVE_RUN); + } else { + SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, attacker); + SetObjectiveTimer(20000); + } + } + } else { + SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, attackerPed); + SetMoveState(PEDMOVE_RUN); + if (attackerPed->GetWeapon()->IsTypeMelee()) + Say(SOUND_PED_FLEE_RUN); + } + } + } + } +} + +void +CPed::PedAnimAlignCB(CAnimBlendAssociation *animAssoc, void *arg) +{ + CPed *ped = (CPed*)arg; + + CVehicle *veh = ped->m_pMyVehicle; + if (animAssoc) + animAssoc->blendDelta = -1000.0f; + + if (!ped->IsNotInWreckedVehicle()) + return; + + if (!ped->EnteringCar()) { + if (ped->m_nPedState != PED_DRIVING) + ped->QuitEnteringCar(); + + return; + } + + if (!ped->m_vehDoor) { + ped->QuitEnteringCar(); + return; + } + + if (ped->m_fHealth == 0.0f) { + ped->QuitEnteringCar(); + return; + } + bool itsVan = !!veh->bIsVan; + bool itsBus = !!veh->bIsBus; + bool itsLow = !!veh->bLowVehicle; + eDoors enterDoor; + + switch (ped->m_vehDoor) { + case CAR_DOOR_RF: + itsVan = false; + enterDoor = DOOR_FRONT_RIGHT; + break; + case CAR_DOOR_RR: + enterDoor = DOOR_REAR_RIGHT; + break; + case CAR_DOOR_LF: + itsVan = false; + enterDoor = DOOR_FRONT_LEFT; + break; + case CAR_DOOR_LR: + enterDoor = DOOR_REAR_LEFT; + break; + default: + break; + } + + if (veh->IsBike()) { + CPed *pedToDragOut = nil; + if (veh->GetStatus() == STATUS_ABANDONED) { + if (ped->m_vehDoor == CAR_WINDSCREEN) { + ped->m_pVehicleAnim = CAnimManager::BlendAnimation(ped->GetClump(), ((CBike*)veh)->m_bikeAnimType, ANIM_BIKE_KICK, 6.0f); + ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); + ((CBike*)veh)->bIsBeingPickedUp = true; + + } else if (veh->GetRight().z >= 0.5f || veh->GetRight().z <= -0.5f || veh->GetUp().z <= 0.0f) { + if (enterDoor == DOOR_FRONT_LEFT || enterDoor == DOOR_REAR_LEFT) { + if (veh->GetRight().z > 0.0f) + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_BIKE_PICKUP_LHS); + else + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_BIKE_PULLUP_LHS); + + } else { + if (veh->GetRight().z < 0.0f) + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_BIKE_PICKUP_RHS); + else + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_BIKE_PULLUP_RHS); + } + ped->m_pVehicleAnim->SetFinishCallback(PedAnimDoorOpenCB, ped); + + } else { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ((CBike*)veh)->m_bikeAnimType, + enterDoor == DOOR_FRONT_LEFT || enterDoor == DOOR_REAR_LEFT ? ANIM_BIKE_JUMPON_LHS : ANIM_BIKE_JUMPON_RHS); + ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); + ((CBike*)veh)->bIsBeingPickedUp = true; + } + } else if (ped->m_vehDoor == CAR_WINDSCREEN) { + if (veh->pDriver->m_nPedState != PED_DRIVING || veh->pDriver->bDontDragMeOutCar) { + ped->QuitEnteringCar(); + } else { + ped->m_pVehicleAnim = CAnimManager::BlendAnimation(ped->GetClump(), ((CBike*)veh)->m_bikeAnimType, ANIM_BIKE_KICK, 6.0f); + ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); + pedToDragOut = veh->pDriver; + } + ((CBike*)veh)->bIsBeingPickedUp = true; + } else { + if (enterDoor == DOOR_FRONT_LEFT || enterDoor == DOOR_FRONT_RIGHT) { + if (veh->pDriver) { + if (veh->m_vecMoveSpeed.Magnitude() > 0.2f) { + ped->QuitEnteringCar(); + ped->SetFall(1000, ped->m_vehDoor == CAR_DOOR_LF || ped->m_vehDoor == CAR_DOOR_LR ? ANIM_STD_HIGHIMPACT_RIGHT : ANIM_STD_HIGHIMPACT_LEFT, false); + return; + } + if (veh->pDriver->m_nPedState != PED_DRIVING || veh->pDriver->bDontDragMeOutCar) { + ped->QuitEnteringCar(); + } else { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, enterDoor == DOOR_FRONT_LEFT ? ANIM_STD_BIKE_ELBOW_LHS : ANIM_STD_BIKE_ELBOW_RHS); + ped->m_pVehicleAnim->SetFinishCallback(PedAnimPullPedOutCB, ped); + pedToDragOut = veh->pDriver; + } + ((CBike*)veh)->bIsBeingPickedUp = true; + + } else { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ((CBike*)veh)->m_bikeAnimType, enterDoor == DOOR_FRONT_LEFT ? ANIM_BIKE_JUMPON_LHS : ANIM_BIKE_JUMPON_RHS); + ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); + ((CBike*)veh)->bIsBeingPickedUp = true; + } + } else { + if (veh->pPassengers[0]) { + if (veh->m_vecMoveSpeed.Magnitude() > 0.2f) { + ped->QuitEnteringCar(); + ped->SetFall(1000, ped->m_vehDoor == CAR_DOOR_LF || ped->m_vehDoor == CAR_DOOR_LR ? ANIM_STD_HIGHIMPACT_RIGHT : ANIM_STD_HIGHIMPACT_LEFT, false); + return; + } + if (veh->pPassengers[0]->m_nPedState != PED_DRIVING || veh->pPassengers[0]->bDontDragMeOutCar) { + ped->QuitEnteringCar(); + } else { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, + enterDoor == DOOR_REAR_LEFT ? ANIM_STD_BIKE_ELBOW_LHS : ANIM_STD_BIKE_ELBOW_RHS); + ped->m_pVehicleAnim->SetFinishCallback(PedAnimPullPedOutCB, ped); + pedToDragOut = veh->pPassengers[0]; + } + ((CBike*)veh)->bIsBeingPickedUp = true; + + } else { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), + ((CBike*)veh)->m_bikeAnimType, enterDoor == DOOR_REAR_LEFT ? ANIM_BIKE_JUMPON_LHS : ANIM_BIKE_JUMPON_RHS); + ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); + ((CBike*)veh)->bIsBeingPickedUp = true; + } + } + } + + if (pedToDragOut) { + pedToDragOut->SetBeingDraggedFromCar(veh, ped->m_vehDoor, false); + if (pedToDragOut->IsGangMember()) + pedToDragOut->RegisterThreatWithGangPeds(ped); + + if (ped->m_nPedType == PEDTYPE_COP && pedToDragOut == FindPlayerPed() && veh->IsBike()) + ((CCopPed*)ped)->m_bDragsPlayerFromCar = 1; + + if (pedToDragOut == veh->pDriver) { + if (veh->pPassengers[0]) + veh->pPassengers[0]->SetBeingDraggedFromCar(veh, CAR_DOOR_LR, false); + } + } + return; + } + + if (veh->IsDoorMissing(enterDoor) || veh->IsDoorFullyOpen(enterDoor)) { + + veh->AutoPilot.m_nCruiseSpeed = 0; + if (ped->m_nPedState == PED_CARJACK || veh->m_nNumMaxPassengers == 0 && veh->pDriver && enterDoor == DOOR_FRONT_RIGHT) { + ped->PedAnimDoorOpenCB(nil, ped); + return; + } + if (enterDoor != DOOR_FRONT_LEFT && enterDoor != DOOR_REAR_LEFT) { + if (itsVan) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_GET_IN_REAR_RHS); + } else if (itsBus) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_COACH, ANIM_STD_COACH_GET_IN_RHS); + } else if (itsLow) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LO_RHS); + } else { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_RHS); + } + } else if (itsVan) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_GET_IN_REAR_LHS); + } else if (itsBus) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_COACH, ANIM_STD_COACH_GET_IN_LHS); + } else if (itsLow) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LO_LHS); + } else { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LHS); + } + ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); + + } else if (veh->CanPedOpenLocks(ped)) { + + veh->AutoPilot.m_nCruiseSpeed = 0; + if (enterDoor != DOOR_FRONT_LEFT && enterDoor != DOOR_REAR_LEFT) { + if (itsVan) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_OPEN_DOOR_REAR_RHS); + } else if (itsBus) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_COACH, ANIM_STD_COACH_OPEN_RHS); + } else { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_OPEN_DOOR_RHS); + } + } else if (itsVan) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_OPEN_DOOR_REAR_LHS); + } else if (itsBus) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_COACH, ANIM_STD_COACH_OPEN_LHS); + } else { + + if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER && veh->pDriver) { + + if (!veh->bLowVehicle + && veh->pDriver->CharCreatedBy != MISSION_CHAR + && veh->pDriver->m_nPedState == PED_DRIVING) { + + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_QUICKJACK); + ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); + + CPlayerPed *player = nil; + CCopPed *cop = nil; + veh->MakeNonDraggedPedsLeaveVehicle(veh->pDriver, ped, player, cop); + if (player && cop) { + cop->QuitEnteringCar(); + cop->SetArrestPlayer(player); + } + + if (player != veh->pDriver) { + veh->pDriver->SetBeingDraggedFromCar(veh, ped->m_vehDoor, true); + if (veh->pDriver->IsGangMember()) + veh->pDriver->RegisterThreatWithGangPeds(ped); + } + return; + } + } + if (veh->IsOpenTopCar() && !veh->pDriver && ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_JUMP_IN_LO_LHS); + ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); + return; + } + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_OPEN_DOOR_LHS); + } + ped->m_pVehicleAnim->SetFinishCallback(PedAnimDoorOpenCB, ped); + + } else { + if (enterDoor != DOOR_FRONT_LEFT && enterDoor != DOOR_REAR_LEFT) + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CARDOOR_LOCKED_RHS); + else + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CARDOOR_LOCKED_LHS); + + ped->bCancelEnteringCar = true; + ped->m_pVehicleAnim->SetFinishCallback(PedAnimDoorOpenCB, ped); + } +} + +void +CPed::PedAnimDoorOpenCB(CAnimBlendAssociation* animAssoc, void* arg) +{ + CPed* ped = (CPed*)arg; + + CVehicle* veh = ped->m_pMyVehicle; + + if (animAssoc) + animAssoc->blendDelta = -1000.0f; + + if (!ped->IsNotInWreckedVehicle()) + return; + + if (!ped->EnteringCar()) { + if(ped->m_nPedState != PED_DRIVING) + ped->QuitEnteringCar(); + + return; + } + + eDoors door; + CPed *pedInSeat = nil; + switch (ped->m_vehDoor) { + case CAR_DOOR_RF: + door = DOOR_FRONT_RIGHT; + pedInSeat = veh->pPassengers[0]; + if (!veh->pPassengers[0] && ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) + pedInSeat = veh->pDriver; + break; + case CAR_DOOR_RR: + door = DOOR_REAR_RIGHT; + pedInSeat = veh->pPassengers[2]; + break; + case CAR_DOOR_LF: + door = DOOR_FRONT_LEFT; + pedInSeat = veh->pDriver; + if (veh->bIsBus && ped->m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER) + pedInSeat = nil; + break; + case CAR_DOOR_LR: + door = DOOR_REAR_LEFT; + pedInSeat = veh->pPassengers[1]; + break; + default: assert(0); + } + + if (ped->m_fHealth == 0.0f || CPad::GetPad(0)->ArePlayerControlsDisabled() && pedInSeat && pedInSeat->IsPlayer()) { + ped->QuitEnteringCar(); + return; + } + + bool isVan = veh->bIsVan; + bool isBus = veh->bIsBus; + bool isLow = veh->bLowVehicle; + bool vehUpsideDown = veh->IsUpsideDown(); + if (ped->bCancelEnteringCar) { + if (ped->IsPlayer()) { + if (veh->pDriver) { + if (veh->pDriver->m_nPedType == PEDTYPE_COP) { + FindPlayerPed()->SetWantedLevelNoDrop(1); + } + } + } +#ifdef CANCELLABLE_CAR_ENTER + if (!veh->IsDoorMissing(door) && veh->CanPedOpenLocks(ped) && veh->IsCar()) { + ((CAutomobile*)veh)->Damage.SetDoorStatus(door, DOOR_STATUS_SWINGING); + } +#endif + ped->QuitEnteringCar(); + ped->RestorePreviousObjective(); + ped->bCancelEnteringCar = false; + return; + } + if (!veh->IsDoorMissing(door) && veh->IsCar()) { + ((CAutomobile*)veh)->Damage.SetDoorStatus(door, DOOR_STATUS_SWINGING); + } + + if (veh->m_vecMoveSpeed.Magnitude() > 0.2f || + veh->IsCar() && veh->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI && ((CAutomobile*)veh)->m_nWheelsOnGround == 0) { + ped->QuitEnteringCar(); + if (ped->m_vehDoor != CAR_DOOR_LF && ped->m_vehDoor != CAR_DOOR_LR) + ped->SetFall(1000, ANIM_STD_HIGHIMPACT_LEFT, false); + else + ped->SetFall(1000, ANIM_STD_HIGHIMPACT_RIGHT, false); + + return; + } + veh->ProcessOpenDoor(ped->m_vehDoor, ANIM_STD_CAR_OPEN_DOOR_LHS, 1.0f); + + if (ped->m_vehDoor == CAR_DOOR_LF || ped->m_vehDoor == CAR_DOOR_RF) + isVan = false; + + if (ped->m_nPedState != PED_CARJACK || isBus) { + + if (ped->m_vehDoor == CAR_DOOR_LF || ped->m_vehDoor == CAR_DOOR_LR) { + if (veh->IsBike()) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ((CBike*)veh)->m_bikeAnimType, ANIM_BIKE_JUMPON_LHS); + } else if (isVan) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_GET_IN_REAR_LHS); + } else if (isBus) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_COACH, ANIM_STD_COACH_GET_IN_LHS); + } else if (isLow) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LO_LHS); + } else { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LHS); + } + } else { + if (veh->IsBike()) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ((CBike*)veh)->m_bikeAnimType, ANIM_BIKE_JUMPON_RHS); + } else if (isVan) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_GET_IN_REAR_RHS); + } else if (isBus) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_COACH, ANIM_STD_COACH_GET_IN_RHS); + } else if (isLow) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LO_RHS); + } else { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_RHS); + } + + if (ped->m_vehDoor == CAR_DOOR_RF && pedInSeat && veh->IsCar()) + pedInSeat->SetObjective(OBJECTIVE_LEAVE_CAR, veh); + } + + ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); + } else { + CPed *pedToDragOut = nil; + switch (ped->m_vehDoor) { + case CAR_DOOR_RF: pedToDragOut = veh->pPassengers[0]; break; + case CAR_DOOR_RR: pedToDragOut = veh->pPassengers[2]; break; + case CAR_DOOR_LF: pedToDragOut = veh->pDriver; break; + case CAR_DOOR_LR: pedToDragOut = veh->pPassengers[1]; break; + default: assert(0); + } + + if (vehUpsideDown) { + ped->QuitEnteringCar(); + if (ped->m_nPedType == PEDTYPE_COP) + ((CCopPed*)ped)->SetArrestPlayer(ped->m_pedInObjective); + } + + if (ped->m_vehDoor != CAR_DOOR_LF && ped->m_vehDoor != CAR_DOOR_LR) { + + if (pedToDragOut && !pedToDragOut->bDontDragMeOutCar) { + if (pedToDragOut->m_nPedState != PED_DRIVING) { + ped->QuitEnteringCar(); + pedToDragOut = nil; + } else { + if (isLow) + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_PULL_OUT_PED_LO_RHS); + else + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_PULL_OUT_PED_RHS); + + ped->m_pVehicleAnim->SetFinishCallback(PedAnimPullPedOutCB, ped); + } + } else if (ped->m_nPedType == PEDTYPE_COP) { + ped->QuitEnteringCar(); + if (ped->m_pedInObjective && ped->m_pedInObjective->m_nPedState == PED_DRIVING) { + veh->SetStatus(STATUS_PLAYER_DISABLED); + + if (ped->m_pedInObjective->IsPlayer()) { + ((CCopPed*)ped)->SetArrestPlayer(ped->m_pedInObjective); + } else { + ped->ClearObjective(); + ped->SetWanderPath(CGeneral::GetRandomNumberInRange(0.f, 8.f)); + } + + } else if (!veh->IsDoorMissing(DOOR_FRONT_RIGHT)) { + ((CAutomobile*)veh)->Damage.SetDoorStatus(DOOR_FRONT_RIGHT, DOOR_STATUS_SWINGING); + } + } else { + if (isLow) + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LO_RHS); + else + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_RHS); + + ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); + } + } else if (pedToDragOut) { + + if (pedToDragOut->m_nPedState != PED_DRIVING || pedToDragOut->bDontDragMeOutCar) { + ped->QuitEnteringCar(); + pedToDragOut = nil; + } else { + if (isLow) + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_PULL_OUT_PED_LO_LHS); + else + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_PULL_OUT_PED_LHS); + + ped->m_pVehicleAnim->SetFinishCallback(PedAnimPullPedOutCB, ped); + } + } else { + if (isLow) + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LO_LHS); + else + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LHS); + + ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); + } + + if (pedToDragOut) { + CPlayerPed* player = nil; + CCopPed* cop = nil; + veh->MakeNonDraggedPedsLeaveVehicle(pedToDragOut, ped, player, cop); + if (player && cop) { + cop->QuitEnteringCar(); + veh->SetStatus(STATUS_PLAYER_DISABLED); + cop->SetArrestPlayer(player); + } + + if (player != pedToDragOut) { + pedToDragOut->SetBeingDraggedFromCar(veh, ped->m_vehDoor, false); + if (pedToDragOut->IsGangMember()) + pedToDragOut->RegisterThreatWithGangPeds(ped); + } + } + } + return; +} + +void +CPed::PedAnimPullPedOutCB(CAnimBlendAssociation* animAssoc, void* arg) +{ + CPed* ped = (CPed*)arg; + + CVehicle* veh = ped->m_pMyVehicle; + if (animAssoc) + animAssoc->blendDelta = -1000.0f; + + if (ped->EnteringCar()) { + if (!ped->IsNotInWreckedVehicle()) + return; + +#ifdef CANCELLABLE_CAR_ENTER + if (ped->bCancelEnteringCar) { + ped->QuitEnteringCar(); + ped->RestorePreviousObjective(); + ped->bCancelEnteringCar = false; + return; + } +#endif + + bool isLow = !!veh->bLowVehicle; + + int padNo; + if (ped->IsPlayer()) { + + // BUG? This will cause crash if m_nPedType is bigger then 1, there are only 2 pads + switch (ped->m_nPedType) { + case PEDTYPE_PLAYER1: + padNo = 0; + break; + case PEDTYPE_PLAYER2: + padNo = 1; + break; + case PEDTYPE_PLAYER3: + padNo = 2; + break; + case PEDTYPE_PLAYER4: + padNo = 3; + break; + } + CPad *pad = CPad::GetPad(padNo); + + if (!pad->ArePlayerControlsDisabled()) { + + if (pad->GetTarget() + || pad->NewState.LeftStickX + || pad->NewState.LeftStickY + || pad->NewState.DPadUp + || pad->NewState.DPadDown + || pad->NewState.DPadLeft + || pad->NewState.DPadRight) { + ped->QuitEnteringCar(); + ped->RestorePreviousObjective(); + return; + } + } + } + + if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) { + if (ped->m_vehDoor == CAR_DOOR_LF || ped->m_vehDoor == CAR_DOOR_LR) { + if (veh->IsBike()) + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ((CBike*)veh)->m_bikeAnimType, ANIM_BIKE_JUMPON_LHS); + else if (isLow) + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LO_LHS); + else + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LHS); + } else { + if (veh->IsBike()) + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ((CBike*)veh)->m_bikeAnimType, ANIM_BIKE_JUMPON_RHS); + else if (isLow) + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LO_RHS); + else + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_RHS); + } + ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); + } else { + ped->QuitEnteringCar(); + } + } else if(ped->m_nPedState != PED_DRIVING) { + ped->QuitEnteringCar(); + } +} + +void +CPed::PedAnimGetInCB(CAnimBlendAssociation *animAssoc, void *arg) +{ + CPed *ped = (CPed*) arg; + + CVehicle *veh = ped->m_pMyVehicle; + if (animAssoc) + animAssoc->blendDelta = -1000.0f; + + if (!ped->IsNotInWreckedVehicle() || ped->DyingOrDead()) + return; + + if (!ped->EnteringCar()) { + if(ped->m_nPedState != PED_DRIVING) + ped->QuitEnteringCar(); + return; + } + + ped->RemoveWeaponWhenEnteringVehicle(); + if (ped->IsPlayer() && ped->bGonnaKillTheCarJacker && ((CPlayerPed*)ped)->m_pArrestingCop) { + PedSetInCarCB(nil, ped); + ped->m_nLastPedState = ped->m_nPedState; + ped->SetPedState(PED_ARRESTED); + ped->bGonnaKillTheCarJacker = false; + if (veh) { + veh->m_nNumGettingIn = 0; + veh->m_nGettingInFlags = 0; + veh->bIsHandbrakeOn = true; + veh->SetStatus(STATUS_PLAYER_DISABLED); + } + return; + } + if (ped->IsPlayer() && ped->m_vehDoor == CAR_DOOR_LF + && (Pads[0].GetAccelerate() >= 255.0f || Pads[0].GetBrake() >= 255.0f) + && veh->IsCar() && !veh->pDriver) { + + if (!animAssoc || animAssoc->animId != ANIM_STD_CAR_JUMP_IN_LO_LHS) + if (((CAutomobile*)veh)->Damage.GetDoorStatus(DOOR_FRONT_LEFT) != DOOR_STATUS_MISSING) + ((CAutomobile*)veh)->Damage.SetDoorStatus(DOOR_FRONT_LEFT, DOOR_STATUS_SWINGING); + + PedSetInCarCB(nil, ped); + return; + } + if (veh->IsBike()) { + ped->PedSetInCarCB(nil, ped); + return; + } + bool isVan = !!veh->bIsVan; + bool isBus = !!veh->bIsBus; + bool isLow = !!veh->bLowVehicle; + eDoors enterDoor; + switch (ped->m_vehDoor) { + case CAR_DOOR_RF: + isVan = false; + enterDoor = DOOR_FRONT_RIGHT; + break; + case CAR_DOOR_RR: + enterDoor = DOOR_REAR_RIGHT; + break; + case CAR_DOOR_LF: + isVan = false; + enterDoor = DOOR_FRONT_LEFT; + break; + case CAR_DOOR_LR: + enterDoor = DOOR_REAR_LEFT; + break; + default: + break; + } + bool doorClosed = true; + if (veh->IsOpenTopCar() && enterDoor == DOOR_FRONT_LEFT && veh->IsDoorClosed(DOOR_FRONT_LEFT)) { + doorClosed = false; + + } else if (!veh->IsDoorMissing(enterDoor)) { + if (veh->IsCar()) + ((CAutomobile*)veh)->Damage.SetDoorStatus(enterDoor, DOOR_STATUS_SWINGING); + } + + CPed *driver = veh->pDriver; + if (driver && (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || ped->m_nPedState == PED_CARJACK)) { + if (veh->bIsBus) { + driver->SetObjective(OBJECTIVE_LEAVE_CAR, veh); + if (driver->IsPlayer()) { + veh->bIsHandbrakeOn = true; + veh->SetStatus(STATUS_PLAYER_DISABLED); + } + driver->bBusJacked = true; + veh->bIsBeingCarJacked = false; + PedSetInCarCB(nil, ped); + if (ped->m_nPedType == PEDTYPE_COP + || ped->m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT + || ped->m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS) { + ped->SetObjective(OBJECTIVE_LEAVE_CAR, veh); + } + ped->m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 400; + return; + } + if (driver != ped && ped->m_vehDoor != CAR_DOOR_LF) { + if (!driver->IsPlayer()) { + driver->bUsePedNodeSeek = true; + driver->m_pLastPathNode = nil; + if (driver->m_pedStats->m_temper <= driver->m_pedStats->m_fear + || driver->CharCreatedBy == MISSION_CHAR + || veh->VehicleCreatedBy == MISSION_VEHICLE) { + driver->bFleeAfterExitingCar = true; + } else { + driver->bGonnaKillTheCarJacker = true; + veh->pDriver->SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, ped); + + if (veh->pDriver->m_nPedType == PEDTYPE_COP && ped->IsPlayer()) { + FindPlayerPed()->SetWantedLevelNoDrop(1); + } + } + } + if ((ped->m_nPedType != PEDTYPE_EMERGENCY || veh->pDriver->m_nPedType != PEDTYPE_EMERGENCY) + && (ped->m_nPedType != PEDTYPE_COP || veh->pDriver->m_nPedType != PEDTYPE_COP)) { + veh->pDriver->SetObjective(OBJECTIVE_LEAVE_CAR, veh); + veh->pDriver->Say(SOUND_PED_CAR_JACKED); + veh->pDriver->SetRadioStation(); + if (veh->m_nDoorLock == CARLOCK_UNLOCKED) + ped->Say(SOUND_PED_CAR_JACKING); + + } else { + ped->m_objective = OBJECTIVE_ENTER_CAR_AS_PASSENGER; + } + } + } + if (veh->IsDoorMissing(enterDoor) || !doorClosed || isBus) { + PedAnimDoorCloseCB(nil, ped); + } else { + if (enterDoor != DOOR_FRONT_LEFT && enterDoor != DOOR_REAR_LEFT) { + if (isVan) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_CLOSE_DOOR_REAR_RHS); + } else if (isLow) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_DOOR_LO_RHS); + } else { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_DOOR_RHS); + } + } else if (isVan) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_CLOSE_DOOR_REAR_LHS); + } else if (isLow) { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_DOOR_LO_LHS); + } else { + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_DOOR_LHS); + } + ped->m_pVehicleAnim->SetFinishCallback(PedAnimDoorCloseCB, ped); + } +} + +void +CPed::PedShuffle(void) +{ + if (m_pMyVehicle->pPassengers[0] == this) { + CPed *driver = m_pMyVehicle->pDriver; + if (!driver || driver->m_objective == OBJECTIVE_LEAVE_CAR) { + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, m_pMyVehicle->bLowVehicle ? ANIM_STD_CAR_SHUFFLE_LO_RHS : ANIM_STD_CAR_SHUFFLE_RHS); + m_objective = OBJECTIVE_ENTER_CAR_AS_DRIVER; + m_pMyVehicle->RemovePassenger(this); + bInVehicle = false; + m_pVehicleAnim->SetFinishCallback(PedSetInCarCB, this); + } + } +} + +void +CPed::PedAnimDoorCloseCB(CAnimBlendAssociation *animAssoc, void *arg) +{ + CPed *ped = (CPed*)arg; + + CAutomobile *veh = (CAutomobile*)(ped->m_pMyVehicle); + + if (!ped->IsNotInWreckedVehicle() || ped->DyingOrDead()) + return; + + if (ped->EnteringCar()) { + bool isLow = !!veh->bLowVehicle; + + if (!veh->bIsBus) + veh->ProcessOpenDoor(ped->m_vehDoor, ANIM_STD_CAR_CLOSE_DOOR_LHS, 1.0f); + + eDoors door; + switch (ped->m_vehDoor) { + case CAR_DOOR_RF: door = DOOR_FRONT_RIGHT; break; + case CAR_DOOR_RR: door = DOOR_REAR_RIGHT; break; + case CAR_DOOR_LF: door = DOOR_FRONT_LEFT; break; + case CAR_DOOR_LR: door = DOOR_REAR_LEFT; break; + default: assert(0); + } + + if (veh->Damage.GetDoorStatus(door) == DOOR_STATUS_SWINGING) + veh->Damage.SetDoorStatus(door, DOOR_STATUS_OK); + + if (door == DOOR_FRONT_LEFT || ped->m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER || veh->bIsBus || veh->m_nNumMaxPassengers == 0) { + PedSetInCarCB(nil, ped); + } else if (ped->m_vehDoor == CAR_DOOR_RF + && (veh->m_nGettingInFlags & CAR_DOOR_FLAG_LF || + (veh->pDriver != nil && + (veh->pDriver->m_objective != OBJECTIVE_LEAVE_CAR + && veh->pDriver->m_objective != OBJECTIVE_LEAVE_CAR_AND_DIE + || !veh->IsRoomForPedToLeaveCar(CAR_DOOR_LF, nil))))) { + + if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || ped->m_nPedState == PED_CARJACK) + veh->bIsBeingCarJacked = false; + + ped->m_objective = OBJECTIVE_ENTER_CAR_AS_PASSENGER; + PedSetInCarCB(nil, ped); + + ped->SetObjective(OBJECTIVE_LEAVE_CAR, veh); + if (!ped->IsPlayer()) + ped->bFleeAfterExitingCar = true; + + ped->bUsePedNodeSeek = true; + ped->m_pNextPathNode = nil; + + } else { + if (animAssoc) + animAssoc->blendDelta = -1000.0f; + + if (isLow) + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_SHUFFLE_LO_RHS); + else + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_SHUFFLE_RHS); + + ped->m_pVehicleAnim->SetFinishCallback(PedSetInCarCB, ped); + } + } else if (ped->m_nPedState != PED_DRIVING) { + ped->QuitEnteringCar(); + } +} + +void +CPed::PedAnimShuffleCB(CAnimBlendAssociation* assoc, void* arg) +{ + CPed *ped = (CPed*)arg; + if (ped->EnteringCar()) { + PedSetInCarCB(nil, ped); + } else if (ped->m_nPedState != PED_DRIVING) { + ped->QuitEnteringCar(); + } +} + +void +CPed::SetFormation(eFormation type) +{ + // FIX: Formations in GetFormationPosition were in range 1-8, whereas in here it's 0-7. + // To not change the behaviour, range in here tweaked by 1 with the use of enum. + + switch (m_pedFormation) { + case FORMATION_REAR: + case FORMATION_REAR_LEFT: + case FORMATION_REAR_RIGHT: + case FORMATION_FRONT_LEFT: + case FORMATION_FRONT_RIGHT: + case FORMATION_LEFT: + case FORMATION_RIGHT: + case FORMATION_FRONT: + break; + default: + Error("Unknown formation type, PedAI.cpp"); + break; + } + m_pedFormation = type; +} + +CVector +CPed::GetFormationPosition(void) +{ + if (!m_pedInObjective) + return GetPosition(); + + if (m_pedInObjective->m_nPedState == PED_DEAD) { + if (!m_pedInObjective->m_pedInObjective) { + m_pedInObjective = nil; + return GetPosition(); + } + m_pedInObjective = m_pedInObjective->m_pedInObjective; + } + + CVector formationOffset; + float offset = CGeneral::GetRandomNumberInRange(1.f, 1.25f) * 1.75f; + switch (m_pedFormation) { + case FORMATION_REAR: + formationOffset = CVector(0.0f, -offset, 0.0f); + break; + case FORMATION_REAR_LEFT: + formationOffset = CVector(-offset, -offset, 0.0f); + break; + case FORMATION_REAR_RIGHT: + formationOffset = CVector(offset, -offset, 0.0f); + break; + case FORMATION_FRONT_LEFT: + formationOffset = CVector(-offset, offset, 0.0f); + break; + case FORMATION_FRONT_RIGHT: + formationOffset = CVector(offset, offset, 0.0f); + break; + case FORMATION_LEFT: + formationOffset = CVector(-offset, 0.0f, 0.0f); + break; + case FORMATION_RIGHT: + formationOffset = CVector(offset, 0.0f, 0.0f); + break; + case FORMATION_FRONT: + formationOffset = CVector(0.0f, offset, 0.0f); + break; + default: + formationOffset = CVector(0.0f, 0.0f, 0.0f); + break; + } + return m_pedInObjective->GetMatrix() * formationOffset; +} + +void +CPed::PedAnimStepOutCarCB(CAnimBlendAssociation* animAssoc, void* arg) +{ + CPed* ped = (CPed*)arg; + + CVehicle* veh = ped->m_pMyVehicle; + if (animAssoc) { + if ((animAssoc->animId == ANIM_STD_ROLLOUT_LHS || animAssoc->animId == ANIM_STD_ROLLOUT_RHS) && ped && ped->m_nPedState == PED_FALL) { + ped->RestoreHeadingRate(); + return; + } + animAssoc->blendDelta = -1000.0f; + if (animAssoc->animId == ANIM_BIKE_GETOFF_BACK) + ped->RestoreHeadingRate(); + } + + if (!veh) { + PedSetOutCarCB(nil, ped); + return; + } + CVector posForZ = ped->GetPosition(); + CPedPlacement::FindZCoorForPed(&posForZ); + if (ped->GetPosition().z - 0.5f > posForZ.z) { + PedSetOutCarCB(nil, ped); + return; + } + + veh->m_nStaticFrames = 0; + veh->m_vecMoveSpeed += CVector(0.001f, 0.001f, 0.001f); + veh->m_vecTurnSpeed += CVector(0.001f, 0.001f, 0.001f); + if (!veh->bIsBus) + veh->ProcessOpenDoor(ped->m_vehDoor, ANIM_STD_GETOUT_LHS, 1.0f); + + /* + // Duplicate and only in PC for some reason + if (!veh) { + PedSetOutCarCB(nil, ped); + return; + } + */ + eDoors door; + switch (ped->m_vehDoor) { + case CAR_DOOR_RF: + door = DOOR_FRONT_RIGHT; + break; + case CAR_DOOR_RR: + door = DOOR_REAR_RIGHT; + break; + case CAR_DOOR_LF: + door = DOOR_FRONT_LEFT; + break; + case CAR_DOOR_LR: + door = DOOR_REAR_LEFT; + break; + default: + break; + } + bool closeDoor = !veh->IsDoorMissing(door); + + int padNo; + if (ped->IsPlayer()) { + + // BUG? This will cause crash if m_nPedType is bigger then 1, there are only 2 pads + switch (ped->m_nPedType) { + case PEDTYPE_PLAYER1: + padNo = 0; + break; + case PEDTYPE_PLAYER2: + padNo = 1; + break; + case PEDTYPE_PLAYER3: + padNo = 2; + break; + case PEDTYPE_PLAYER4: + padNo = 3; + break; + } + CPad* pad = CPad::GetPad(padNo); + bool engineIsIntact = veh->IsCar() && ((CAutomobile*)veh)->Damage.GetEngineStatus() >= 225; + if (!pad->ArePlayerControlsDisabled() && veh->m_nDoorLock != CARLOCK_FORCE_SHUT_DOORS + && (pad->GetTarget() + || pad->NewState.LeftStickX + || pad->NewState.LeftStickY + || pad->NewState.DPadUp + || pad->NewState.DPadDown + || pad->NewState.DPadLeft + || pad->NewState.DPadRight) + || veh->bIsBus + || veh->m_pCarFire + || engineIsIntact) { + closeDoor = false; + } + } + + if (ped->m_objective == OBJECTIVE_LEAVE_CAR_AND_DIE) + closeDoor = false; + + if (!closeDoor) { + if (!veh->IsDoorMissing(door) && !veh->bIsBus) { + ((CAutomobile*)veh)->Damage.SetDoorStatus(door, DOOR_STATUS_SWINGING); + } + PedSetOutCarCB(nil, ped); + return; + } + + if (ped->bFleeAfterExitingCar || ped->bGonnaKillTheCarJacker) { +#ifdef FIX_BUGS + if (!veh->IsDoorMissing(door)) + ((CAutomobile*)veh)->Damage.SetDoorStatus(door, DOOR_STATUS_SWINGING); + PedSetOutCarCB(nil, ped); + return; +#else + if (!veh->IsDoorMissing(door)) + ((CAutomobile*)veh)->Damage.SetDoorStatus(DOOR_FRONT_LEFT, DOOR_STATUS_SWINGING); +#endif + } else { + switch (door) { + case DOOR_FRONT_LEFT: + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_LHS); + break; + case DOOR_FRONT_RIGHT: + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_RHS); + break; + case DOOR_REAR_LEFT: + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_LHS); + break; + case DOOR_REAR_RIGHT: + ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_RHS); + break; + default: + break; + } + } + + if (ped->m_pVehicleAnim) + ped->m_pVehicleAnim->SetFinishCallback(PedSetOutCarCB, ped); + return; +} + +void +CPed::LineUpPedWithCar(PedLineUpPhase phase) +{ + bool vehIsUpsideDown = false; + bool stillGettingInOut = false; + int vehAnim; + float seatPosMult = 0.0f; + float currentZ; + float adjustedTimeStep; + CVector autoZPos; + + if (CReplay::IsPlayingBack()) + return; + + if (!bChangedSeat && phase != LINE_UP_TO_CAR_2) { + if (m_pMyVehicle->IsBike()) { + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_RIDE) || + RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_RIDE_P)) { + SetPedPositionInCar(); + return; + } + } else { + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_SIT)) { + SetPedPositionInCar(); + return; + } + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_SIT_LO)) { + SetPedPositionInCar(); + return; + } + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_SIT_P)) { + SetPedPositionInCar(); + return; + } + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_SIT_P_LO)) { + SetPedPositionInCar(); + return; + } + } + bChangedSeat = true; + } + if (phase == LINE_UP_TO_CAR_FALL) { + SetPedPositionInCar(); + autoZPos = GetPosition(); + CPedPlacement::FindZCoorForPed(&autoZPos); + if (m_pVehicleAnim && (m_pVehicleAnim->animId == ANIM_STD_ROLLOUT_LHS || m_pVehicleAnim->animId == ANIM_STD_ROLLOUT_RHS) + && autoZPos.z > GetPosition().z) { + m_matrix.GetPosition().z = autoZPos.z; + } + if (m_pVehicleAnim && m_pVehicleAnim->animId == ANIM_BIKE_HIT) { + if (autoZPos.z > GetPosition().z) + m_matrix.GetPosition().z += m_pVehicleAnim->GetProgress() * (autoZPos.z - GetPosition().z); + + } else if (m_pVehicleAnim) { + if (m_pVehicleAnim->animId == ANIM_BIKE_GETOFF_BACK) { + if (autoZPos.z > GetPosition().z) { + m_matrix.GetPosition().z += (m_pVehicleAnim->currentTime * (20.f / 7.f)) * (autoZPos.z - GetPosition().z); + } + } + } + return; + } + if (phase == LINE_UP_TO_CAR_START) { + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + } + CVehicle *veh = m_pMyVehicle; + + // Not quite right, IsUpsideDown func. checks for <= -0.9f. + if (veh->GetUp().z <= -0.8f) + vehIsUpsideDown = true; + + if (m_vehDoor == CAR_DOOR_RF || m_vehDoor == CAR_DOOR_RR) { + if (vehIsUpsideDown) { + m_fRotationDest = -PI + veh->GetForward().Heading(); + } else if (veh->bIsBus) { + m_fRotationDest = 0.5f * PI + veh->GetForward().Heading(); + } else { + m_fRotationDest = veh->GetForward().Heading(); + } + } else if (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR) { + if (vehIsUpsideDown) { + m_fRotationDest = veh->GetForward().Heading(); + } else if (veh->bIsBus) { + m_fRotationDest = -0.5f * PI + veh->GetForward().Heading(); + } else { + m_fRotationDest = veh->GetForward().Heading(); + } + } else { + // I don't know will this part ever run(maybe boats?), but the game also handles that. I don't know is it intentional. + + if (vehIsUpsideDown) { + m_fRotationDest = veh->GetForward().Heading(); + } else if (veh->bIsBus) { + m_fRotationDest = 0.5f * PI + veh->GetForward().Heading(); + } else if (m_vehDoor == CAR_WINDSCREEN) { + m_fRotationDest = veh->GetForward().Heading() + PI; + } else { + m_fRotationDest = veh->GetForward().Heading(); + } + } + + bool multExtractedFromAnim = false; + bool multExtractedFromAnimBus = false; + float zBlend; + if (m_pVehicleAnim) { + vehAnim = m_pVehicleAnim->animId; + + switch (vehAnim) { + case ANIM_STD_JACKEDCAR_RHS: + case ANIM_STD_JACKEDCAR_LO_RHS: + case ANIM_STD_JACKEDCAR_LHS: + case ANIM_STD_JACKEDCAR_LO_LHS: + case ANIM_STD_VAN_GET_IN_REAR_LHS: + case ANIM_STD_VAN_GET_IN_REAR_RHS: + multExtractedFromAnim = true; + zBlend = Max(m_pVehicleAnim->GetProgress() - 0.3f, 0.0f) / (1.0f - 0.3f); + // fall through + + case ANIM_STD_QUICKJACKED: + case ANIM_STD_GETOUT_LHS: + case ANIM_STD_GETOUT_LO_LHS: + case ANIM_STD_GETOUT_RHS: + case ANIM_STD_GETOUT_LO_RHS: + + if (!multExtractedFromAnim) { + multExtractedFromAnim = true; + zBlend = Max(m_pVehicleAnim->GetProgress() - 0.5f, 0.0f) / (1.0f - 0.5f); + } + // fall through + + case ANIM_STD_CRAWLOUT_LHS: + case ANIM_STD_CRAWLOUT_RHS: + case ANIM_STD_VAN_GET_OUT_REAR_LHS: + case ANIM_STD_VAN_GET_OUT_REAR_RHS: + case ANIM_BIKE_GETOFF_LHS: + case ANIM_BIKE_GETOFF_RHS: + seatPosMult = m_pVehicleAnim->GetProgress(); + break; + case ANIM_STD_CAR_GET_IN_RHS: + case ANIM_STD_CAR_GET_IN_LHS: + if (veh && veh->IsCar() && veh->bIsBus) { + multExtractedFromAnimBus = true; + zBlend = Min(m_pVehicleAnim->GetProgress(), 0.5f) / 0.5f; + } + // fall through + + case ANIM_STD_QUICKJACK: + case ANIM_STD_CAR_GET_IN_LO_LHS: + case ANIM_STD_CAR_GET_IN_LO_RHS: + case ANIM_STD_BOAT_DRIVE: + seatPosMult = m_pVehicleAnim->GetTimeLeft() / m_pVehicleAnim->hierarchy->totalLength; + break; + case ANIM_STD_CAR_CLOSE_DOOR_LHS: + case ANIM_STD_CAR_CLOSE_DOOR_LO_LHS: + case ANIM_STD_CAR_CLOSE_DOOR_RHS: + case ANIM_STD_CAR_CLOSE_DOOR_LO_RHS: + case ANIM_STD_CAR_SHUFFLE_RHS: + case ANIM_STD_CAR_SHUFFLE_LO_RHS: + seatPosMult = 0.0f; + break; + case ANIM_STD_CAR_JUMP_IN_LO_LHS: + { + float animLength = m_pVehicleAnim->hierarchy->totalLength; + seatPosMult = Max(0.0f, 0.5f * animLength - m_pVehicleAnim->currentTime) / animLength; + break; + } + case ANIM_STD_CAR_CLOSE_LHS: + case ANIM_STD_CAR_CLOSE_RHS: + case ANIM_STD_COACH_OPEN_LHS: + case ANIM_STD_COACH_OPEN_RHS: + case ANIM_STD_COACH_GET_IN_LHS: + case ANIM_STD_COACH_GET_IN_RHS: + case ANIM_STD_COACH_GET_OUT_LHS: + seatPosMult = 1.0f; + break; + default: + if (veh->IsBike()) { + seatPosMult = 0.0f; + } else { + if (bInVehicle) + seatPosMult = 0.0f; + else + seatPosMult = 1.0f; + } + break; + } + } else { + if (veh->IsBike()) { + seatPosMult = 0.0f; + } else { + if (bInVehicle) + seatPosMult = 0.0f; + else + seatPosMult = 1.0f; + } + } + + CVector neededPos; + + if (phase == LINE_UP_TO_CAR_2) { + neededPos = GetPosition(); + } else { + neededPos = GetPositionToOpenCarDoor(veh, m_vehDoor, seatPosMult); + } + + autoZPos = neededPos; + + if (veh->bIsInWater) { + if (veh->m_vehType == VEHICLE_TYPE_BOAT && veh->IsUpsideDown()) + autoZPos.z += 1.0f; + } else { + CPedPlacement::FindZCoorForPed(&autoZPos); + } + + if (phase == LINE_UP_TO_CAR_END || phase == LINE_UP_TO_CAR_2) { + neededPos.z = GetPosition().z; + + // Getting out + if (!veh->bIsBus || (veh->bIsBus && vehIsUpsideDown)) { + float nextZSpeed = m_vecMoveSpeed.z - GRAVITY * CTimer::GetTimeStep(); + + // If we're not in ground at next step, apply animation + if (neededPos.z + nextZSpeed >= autoZPos.z) { + m_vecMoveSpeed.z = nextZSpeed; + ApplyMoveSpeed(); + // Removing below line breaks the animation + neededPos.z = GetPosition().z; + } else { + neededPos.z = autoZPos.z; + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + } + } + } + + if (autoZPos.z > neededPos.z) { + vehAnim = m_pVehicleAnim->animId; + if (veh->IsBike() && (m_pVehicleAnim && vehAnim != ANIM_BIKE_KICK)) { + float zBlend; + if (vehAnim != ANIM_BIKE_GETOFF_LHS && vehAnim != ANIM_BIKE_GETOFF_RHS) { + if (vehAnim != ANIM_BIKE_JUMPON_LHS && vehAnim != ANIM_BIKE_JUMPON_RHS) { + zBlend = 0.0f; + } else { + float animLength = m_pVehicleAnim->hierarchy->totalLength; + zBlend = Min(1.0f, 2.0f * m_pVehicleAnim->currentTime / animLength); + } + } else { + zBlend = 1.0f - seatPosMult; + } + float curZ = veh->GetPosition().z + FEET_OFFSET; + neededPos.z = ((curZ - autoZPos.z) - veh->GetHeightAboveRoad()) * zBlend + autoZPos.z; + } else if (multExtractedFromAnim) { + neededPos.z += (autoZPos.z - neededPos.z) * zBlend; + } else { + currentZ = GetPosition().z; + if (m_pVehicleAnim && vehAnim != ANIM_STD_VAN_GET_IN_REAR_LHS && vehAnim != ANIM_STD_VAN_CLOSE_DOOR_REAR_LHS && vehAnim != ANIM_STD_VAN_CLOSE_DOOR_REAR_RHS && vehAnim != ANIM_STD_VAN_GET_IN_REAR_RHS) { + neededPos.z = autoZPos.z; + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + } else if (neededPos.z < currentZ && m_pVehicleAnim && vehAnim != ANIM_STD_VAN_CLOSE_DOOR_REAR_LHS && vehAnim != ANIM_STD_VAN_CLOSE_DOOR_REAR_RHS) { + adjustedTimeStep = Max(m_pVehicleAnim->timeStep, 0.1f); + + // Smoothly change ped position + neededPos.z = currentZ - (currentZ - neededPos.z) / (m_pVehicleAnim->GetTimeLeft() / adjustedTimeStep); + } + } + } else { + // We may need to raise up the ped + if (phase == LINE_UP_TO_CAR_START) { + currentZ = GetPosition().z; + + if (neededPos.z > currentZ) { + if (multExtractedFromAnimBus) { + neededPos.z = (neededPos.z - currentZ) * zBlend + currentZ; + } else { + if (m_pVehicleAnim && + (vehAnim == ANIM_STD_CAR_GET_IN_RHS || vehAnim == ANIM_STD_CAR_GET_IN_LO_RHS || vehAnim == ANIM_STD_CAR_GET_IN_LHS || vehAnim == ANIM_STD_CAR_GET_IN_LO_LHS + || vehAnim == ANIM_STD_QUICKJACK || vehAnim == ANIM_STD_VAN_GET_IN_REAR_LHS || vehAnim == ANIM_STD_VAN_GET_IN_REAR_RHS)) { + adjustedTimeStep = Max(m_pVehicleAnim->timeStep, 0.1f); + + // Smoothly change ped position + neededPos.z = (neededPos.z - currentZ) / (m_pVehicleAnim->GetTimeLeft() / adjustedTimeStep) + currentZ; + } else if (EnteringCar() || m_nPedState == PED_DRIVING && veh->IsBike()) { + neededPos.z = Max(currentZ, autoZPos.z); + } + } + } + } + } + + if (CTimer::GetTimeInMilliseconds() < m_nPedStateTimer) + stillGettingInOut = veh->m_vehType != VEHICLE_TYPE_BOAT || bOnBoat; + + if (!stillGettingInOut) { + m_fRotationCur = m_fRotationDest; + } else { + float limitedDest = CGeneral::LimitRadianAngle(m_fRotationDest); + float timeUntilStateChange = (m_nPedStateTimer - CTimer::GetTimeInMilliseconds())/600.0f; + + if (timeUntilStateChange <= 0.0f) { + m_vecOffsetSeek.x = 0.0f; + m_vecOffsetSeek.y = 0.0f; + } + m_vecOffsetSeek.z = 0.0f; + + neededPos -= timeUntilStateChange * m_vecOffsetSeek; + + if (PI + m_fRotationCur < limitedDest) { + limitedDest -= 2 * PI; + } else if (m_fRotationCur - PI > limitedDest) { + limitedDest += 2 * PI; + } + m_fRotationCur -= (m_fRotationCur - limitedDest) * (1.0f - timeUntilStateChange); + } + + if (seatPosMult > 0.2f || vehIsUpsideDown || veh->IsBike()) { + SetPosition(neededPos); + SetHeading(m_fRotationCur); + } else { + CMatrix vehDoorMat(veh->GetMatrix()); + vehDoorMat.GetPosition() += Multiply3x3(vehDoorMat, GetLocalPositionToOpenCarDoor(veh, m_vehDoor, 0.0f)); + + if (m_vehDoor == CAR_WINDSCREEN || veh->bIsBus) { + CMatrix correctionMat; + if (veh->bIsBus && (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR)) + correctionMat.SetRotateZ(-HALFPI); + else if (veh->bIsBus && (m_vehDoor == CAR_DOOR_RF || m_vehDoor == CAR_DOOR_RR)) + correctionMat.SetRotateZ(HALFPI); + else + correctionMat.SetRotateZ(PI); + + vehDoorMat = vehDoorMat * correctionMat; + } + GetMatrix() = vehDoorMat; + } + +} + +void +CPed::SetCarJack(CVehicle* car) +{ + uint8 doorFlag; + eDoors door; + CPed *pedInSeat = nil; + + if (car->IsBoat()) + return; + + if (car->IsBike()) { + switch (m_vehDoor) { + case CAR_DOOR_RF: + doorFlag = CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_RF; + door = DOOR_FRONT_RIGHT; + pedInSeat = car->pDriver; + break; + case CAR_DOOR_RR: + doorFlag = CAR_DOOR_FLAG_LR | CAR_DOOR_FLAG_RR; + door = DOOR_REAR_RIGHT; + pedInSeat = car->pPassengers[0]; + break; + case CAR_DOOR_LF: + case CAR_WINDSCREEN: + doorFlag = CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_RF; + door = DOOR_FRONT_LEFT; + pedInSeat = car->pDriver; + break; + case CAR_DOOR_LR: + doorFlag = CAR_DOOR_FLAG_LR | CAR_DOOR_FLAG_RR; + door = DOOR_REAR_LEFT; + pedInSeat = car->pPassengers[0]; + break; + default: + doorFlag = CAR_DOOR_FLAG_UNKNOWN; + break; + } + } else { + switch (m_vehDoor) { + case CAR_DOOR_RF: + doorFlag = CAR_DOOR_FLAG_RF; + door = DOOR_FRONT_RIGHT; + if (car->pPassengers[0]) { + pedInSeat = car->pPassengers[0]; + } + else if (m_nPedType == PEDTYPE_COP) { + pedInSeat = car->pDriver; + } + break; + case CAR_DOOR_RR: + doorFlag = CAR_DOOR_FLAG_RR; + door = DOOR_REAR_RIGHT; + pedInSeat = car->pPassengers[2]; + break; + case CAR_DOOR_LF: + doorFlag = CAR_DOOR_FLAG_LF; + door = DOOR_FRONT_LEFT; + pedInSeat = car->pDriver; + break; + case CAR_DOOR_LR: + doorFlag = CAR_DOOR_FLAG_LR; + door = DOOR_REAR_LEFT; + pedInSeat = car->pPassengers[1]; + break; + default: + doorFlag = CAR_DOOR_FLAG_UNKNOWN; + break; + } + } + + if(car->bIsBus) + pedInSeat = car->pDriver; + + if (m_fHealth > 0.0f && (IsPlayer() || m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS || + CharCreatedBy == MISSION_CHAR || (car->VehicleCreatedBy != MISSION_VEHICLE && car->GetModelIndex() != MI_DODO))) + if (pedInSeat && !pedInSeat->IsPedDoingDriveByShooting() && pedInSeat->m_nPedState == PED_DRIVING) + if (m_nPedState != PED_CARJACK && !m_pVehicleAnim) + if ((car->IsDoorReady(door) || car->IsDoorFullyOpen(door))) + if (!car->bIsBeingCarJacked && !(doorFlag & car->m_nGettingInFlags) && !(doorFlag & car->m_nGettingOutFlags)) + SetCarJack_AllClear(car, m_vehDoor, doorFlag); +} + +void +CPed::SetCarJack_AllClear(CVehicle* car, uint32 doorNode, uint32 doorFlag) +{ + if (m_nPedState != PED_SEEK_CAR) + SetStoredState(); + + m_pSeekTarget = car; + m_pSeekTarget->RegisterReference((CEntity**)&m_pSeekTarget); + SetPedState(PED_CARJACK); + car->bIsBeingCarJacked = true; + m_pMyVehicle = (CVehicle*)m_pSeekTarget; + m_pMyVehicle->RegisterReference((CEntity**)&m_pMyVehicle); + ((CVehicle*)m_pSeekTarget)->m_nNumGettingIn++; + + if (m_nPedType == PEDTYPE_COP) + Say(SOUND_PED_ARREST_COP); + else if (car->m_nDoorLock == CARLOCK_UNLOCKED) + Say(SOUND_PED_CAR_JACKING, 1000); + + CVector carEnterPos; + carEnterPos = GetPositionToOpenCarDoor(car, m_vehDoor); + + car->m_nGettingInFlags |= doorFlag; + m_vecOffsetSeek = carEnterPos - GetPosition(); + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 600; + + if(car->IsBike()){ + bUsesCollision = false; + PedAnimAlignCB(nil, this); + } else { + float zDiff = Max(0.0f, carEnterPos.z - GetPosition().z); + bUsesCollision = false; + + if (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR) + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, zDiff > 4.4f ? ANIM_STD_CAR_ALIGNHI_DOOR_LHS : ANIM_STD_CAR_ALIGN_DOOR_LHS, 4.0f); + else + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, zDiff > 4.4f ? ANIM_STD_CAR_ALIGNHI_DOOR_RHS : ANIM_STD_CAR_ALIGN_DOOR_RHS, 4.0f); + + m_pVehicleAnim->SetFinishCallback(PedAnimAlignCB, this); + } +} + +void +CPed::SetBeingDraggedFromCar(CVehicle *veh, uint32 vehEnterType, bool quickJack) +{ + if (m_nPedState == PED_DRAG_FROM_CAR) + return; + + bUsesCollision = false; + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + m_nLastPedState = PED_IDLE; + SetMoveState(PEDMOVE_STILL); + m_pSeekTarget = veh; + m_pSeekTarget->RegisterReference((CEntity **) &m_pSeekTarget); + + if (veh->IsBike()) { + ((CBike*)veh)->bIsBeingPickedUp = true; + if (veh->pPassengers[0] != this && (vehEnterType != CAR_WINDSCREEN || veh->pPassengers[0])) + m_vehDoor = CAR_DOOR_LF; + else + m_vehDoor = CAR_DOOR_LR; + } else { + m_vehDoor = vehEnterType; + } + + if (m_vehDoor == CAR_DOOR_LF) { + if (veh->pDriver && veh->pDriver->IsPlayer()) + veh->SetStatus(STATUS_PLAYER_DISABLED); + else + veh->SetStatus(STATUS_ABANDONED); + } + RemoveInCarAnims(); + SetMoveState(PEDMOVE_NONE); + LineUpPedWithCar(veh->IsBike() ? LINE_UP_TO_CAR_FALL : LINE_UP_TO_CAR_START); + m_pVehicleAnim = nil; + SetPedState(PED_DRAG_FROM_CAR); + bChangedSeat = false; + bWillBeQuickJacked = quickJack; + + SetHeading(m_fRotationCur); + + Say(SOUND_PED_CAR_JACKED); + SetRadioStation(); + + if(veh->IsBike()) + veh->m_nGettingOutFlags |= GetBikeDoorFlagInclJumpInFromFront(m_vehDoor); + else + veh->m_nGettingOutFlags |= GetCarDoorFlag(m_vehDoor); +} + +void +CPed::BeingDraggedFromCar(void) +{ + AnimationId enterAnim; + bool dontRunAnim = false; + + if (!m_pVehicleAnim) { + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 1000.0f); + + AssocGroupId assocGroup; + if (m_pMyVehicle && m_pMyVehicle->IsBike()) { + enterAnim = ANIM_BIKE_HIT; + assocGroup = ((CBike*)m_pMyVehicle)->m_bikeAnimType; + + } else { + if (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR) { + if (bWillBeQuickJacked && m_vehDoor == CAR_DOOR_LF) { + enterAnim = ANIM_STD_QUICKJACKED; + } else if (m_pMyVehicle->bLowVehicle) { + enterAnim = ANIM_STD_JACKEDCAR_LO_LHS; + } else { + enterAnim = ANIM_STD_JACKEDCAR_LHS; + } + } else if (m_vehDoor == CAR_DOOR_RF || m_vehDoor == CAR_DOOR_RR) { + if (m_pMyVehicle->bLowVehicle) + enterAnim = ANIM_STD_JACKEDCAR_LO_RHS; + else + enterAnim = ANIM_STD_JACKEDCAR_RHS; + } else + dontRunAnim = true; + + assocGroup = ASSOCGRP_STD; + } + + if (!dontRunAnim) + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), assocGroup, enterAnim); + + m_pVehicleAnim->SetFinishCallback(PedSetDraggedOutCarCB, this); + + if (m_pMyVehicle && m_pMyVehicle->IsBike()) { + LineUpPedWithCar(LINE_UP_TO_CAR_FALL); + } else { + LineUpPedWithCar(LINE_UP_TO_CAR_START); + } + return; + + } else if (m_pVehicleAnim->animId == ANIM_BIKE_HIT) { + LineUpPedWithCar(LINE_UP_TO_CAR_FALL); + + } else if (m_pVehicleAnim->currentTime <= 1.4f) { + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + LineUpPedWithCar(LINE_UP_TO_CAR_START); + + } else { + LineUpPedWithCar(LINE_UP_TO_CAR_2); + } + + static float mult = 5.f; + if (m_objective == OBJECTIVE_LEAVE_CAR_AND_DIE) { + if (m_pMyVehicle) { + m_pMyVehicle->ProcessOpenDoor(m_vehDoor, ANIM_STD_NUM, m_pVehicleAnim->currentTime * mult); + } + } +} + +void +CPed::SetEnterCar(CVehicle *car, uint32 unused) +{ + if (CCranes::IsThisCarBeingCarriedByAnyCrane(car)) { + RestorePreviousState(); + RestorePreviousObjective(); + } else { + uint8 doorFlag; + eDoors door; + if (car->IsBike()) { + switch (m_vehDoor) { + case CAR_DOOR_RF: + doorFlag = CAR_DOOR_FLAG_RF | CAR_DOOR_FLAG_LF; + door = DOOR_FRONT_RIGHT; + break; + case CAR_DOOR_RR: + doorFlag = CAR_DOOR_FLAG_RR | CAR_DOOR_FLAG_LR; + door = DOOR_REAR_RIGHT; + break; + case CAR_WING_LF: + case CAR_WING_LR: + case CAR_BONNET: + case CAR_BOOT: + doorFlag = CAR_DOOR_FLAG_UNKNOWN; + break; + case CAR_DOOR_LF: + case CAR_WINDSCREEN: + doorFlag = CAR_DOOR_FLAG_RF | CAR_DOOR_FLAG_LF; + door = DOOR_FRONT_LEFT; + break; + case CAR_DOOR_LR: + doorFlag = CAR_DOOR_FLAG_RR | CAR_DOOR_FLAG_LR; + door = DOOR_REAR_LEFT; + break; + } + } else { + switch (m_vehDoor) { + case CAR_DOOR_RF: + doorFlag = CAR_DOOR_FLAG_RF; + door = DOOR_FRONT_RIGHT; + break; + case CAR_DOOR_RR: + doorFlag = CAR_DOOR_FLAG_RR; + door = DOOR_REAR_RIGHT; + break; + case CAR_DOOR_LF: + if(car->m_nNumMaxPassengers != 0) + doorFlag = CAR_DOOR_FLAG_LF; + else + doorFlag = CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_LR; + + door = DOOR_FRONT_LEFT; + break; + case CAR_DOOR_LR: + if (car->m_nNumMaxPassengers != 0) + doorFlag = CAR_DOOR_FLAG_LR; + else + doorFlag = CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_LR; + + door = DOOR_REAR_LEFT; + break; + default: + doorFlag = CAR_DOOR_FLAG_UNKNOWN; + break; + } + } + if (!IsPedInControl() || m_fHealth <= 0.0f + || doorFlag & car->m_nGettingInFlags || doorFlag & car->m_nGettingOutFlags + || car->bIsBeingCarJacked || m_pVehicleAnim + || doorFlag && !car->IsDoorReady(door) && !car->IsDoorFullyOpen(door)) + SetMoveState(PEDMOVE_STILL); + else + SetEnterCar_AllClear(car, m_vehDoor, doorFlag); + } +} + +void +CPed::SetEnterCar_AllClear(CVehicle *car, uint32 doorNode, uint32 doorFlag) +{ + float zDiff = 0.0f; + car->m_nGettingInFlags |= doorFlag; + bVehEnterDoorIsBlocked = false; + if (m_nPedState != PED_SEEK_CAR && m_nPedState != PED_SEEK_IN_BOAT) + SetStoredState(); + + m_pSeekTarget = car; + m_pSeekTarget->RegisterReference((CEntity **) &m_pSeekTarget); + m_vehDoor = doorNode; + SetPedState(PED_ENTER_CAR); + if (m_vehDoor == CAR_DOOR_RF && m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER && !car->IsBike()) { + car->bIsBeingCarJacked = true; + } + + m_pMyVehicle = (CVehicle*)m_pSeekTarget; + m_pMyVehicle->RegisterReference((CEntity**) &m_pMyVehicle); + ((CVehicle*)m_pSeekTarget)->m_nNumGettingIn++; + bUsesCollision = false; + CVector doorOpenPos = GetPositionToOpenCarDoor(car, m_vehDoor); + + // Because buses have stairs + if (!m_pMyVehicle->bIsBus) + zDiff = Max(0.0f, doorOpenPos.z - GetPosition().z); + + m_vecOffsetSeek = doorOpenPos - GetPosition(); + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 600; + + if (car->IsBoat()) { + if (car->pHandling->Flags & HANDLING_SIT_IN_BOAT) + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_SIT, 100.0f); + else + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_BOAT_DRIVE, 100.0f); + + PedSetInCarCB(nil, this); + bVehExitWillBeInstant = true; + + } else if (car->IsBike()) { + PedAnimAlignCB(0, this); + car->AutoPilot.m_nCruiseSpeed = 0; + + } else { + if (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR) + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, zDiff > 4.4f ? ANIM_STD_CAR_ALIGNHI_DOOR_RHS : ANIM_STD_CAR_ALIGN_DOOR_RHS, 4.0f); + else + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, zDiff > 4.4f ? ANIM_STD_CAR_ALIGNHI_DOOR_LHS : ANIM_STD_CAR_ALIGN_DOOR_LHS, 4.0f); + m_pVehicleAnim->SetFinishCallback(PedAnimAlignCB, this); + } +} + +void +CPed::EnterCar(void) +{ + if (IsNotInWreckedVehicle() && m_fHealth > 0.0f) { + CVehicle *veh = m_pMyVehicle; + + // Not used. + // CVector posForDoor = GetPositionToOpenCarDoor(veh, m_vehDoor); + + if (veh->CanPedOpenLocks(this)) { + if (m_vehDoor && m_pVehicleAnim) { + veh->ProcessOpenDoor(m_vehDoor, m_pVehicleAnim->animId, m_pVehicleAnim->currentTime); + } + } + bIsInTheAir = false; + LineUpPedWithCar(LINE_UP_TO_CAR_START); + if (veh->IsBike()) { + CBike *bike = (CBike*)veh; + if (bike->GetStatus() == STATUS_ABANDONED && !bike->bIsBeingPickedUp && m_pVehicleAnim) { + int anim = m_pVehicleAnim->animId; + + // One is pickup and other one is pullup, not same :p + if ((anim == ANIM_STD_BIKE_PICKUP_LHS || anim == ANIM_STD_BIKE_PICKUP_RHS) && m_pVehicleAnim->currentTime > 0.4667f) + bike->bIsBeingPickedUp = true; + else if ((anim == ANIM_STD_BIKE_PULLUP_LHS || anim == ANIM_STD_BIKE_PULLUP_RHS) && m_pVehicleAnim->currentTime > 0.4667f) + bike->bIsBeingPickedUp = true; + } else if (m_nPedState == PED_CARJACK && m_pVehicleAnim) { + if (m_pVehicleAnim->currentTime > 0.4f && m_pVehicleAnim->currentTime - m_pVehicleAnim->timeStep <= 0.4f) { + int anim = m_pVehicleAnim->animId; + if (anim == ANIM_BIKE_KICK) { + DMAudio.PlayOneShot(m_audioEntityId, SOUND_187, 3.0f); + } else if (anim == ANIM_STD_BIKE_ELBOW_LHS || anim == ANIM_STD_BIKE_ELBOW_RHS) { + DMAudio.PlayOneShot(m_audioEntityId, SOUND_186, 3.0f); + } + } + } + } + } else { + QuitEnteringCar(); + SetDie(); + } +} + +void +CPed::QuitEnteringCar(void) +{ + CVehicle *veh = m_pMyVehicle; + if (m_pVehicleAnim) + m_pVehicleAnim->blendDelta = -1000.0f; + + RestartNonPartialAnims(); + + if (!RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE)) + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 100.0f); + + if (veh) { + if (m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || m_nPedState == PED_CARJACK) + veh->bIsBeingCarJacked = false; + + if (veh->m_nNumGettingIn != 0) + veh->m_nNumGettingIn--; + + if (m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER) + RestorePreviousObjective(); + + if (veh->IsBike()) { + veh->m_nGettingInFlags &= ~GetBikeDoorFlag(m_vehDoor); + ((CBike*)veh)->bIsBeingPickedUp = false; + } else + veh->m_nGettingInFlags &= ~GetEnterCarDoorFlag(m_vehDoor, veh->m_nNumMaxPassengers); + } + + bUsesCollision = true; + + if (DyingOrDead()) { + if (m_pVehicleAnim) { + m_pVehicleAnim->blendDelta = -4.0f; + m_pVehicleAnim->flags |= ASSOC_DELETEFADEDOUT; + m_pVehicleAnim->flags &= ~ASSOC_RUNNING; + } + } else + SetIdle(); + + m_pVehicleAnim = nil; + + if (veh) { + if (veh->AutoPilot.m_nCruiseSpeed == 0 && veh->VehicleCreatedBy == RANDOM_VEHICLE) + veh->AutoPilot.m_nCruiseSpeed = 17; + } +} + +void +CPed::SetExitBoat(CVehicle *boat) +{ + SetPedState(PED_IDLE); + CVector newPos = GetPosition(); + RemoveInCarAnims(); + CColModel* boatCol = boat->GetColModel(); + if (boat->IsUpsideDown()) { + newPos = CVector(0.0f, 0.0f, boatCol->boundingBox.min.z); + newPos = boat->GetMatrix() * newPos; + newPos.z += 1.0f; + m_vehDoor = CAR_DOOR_RF; + PedSetOutCarCB(nil, this); + bIsStanding = true; + m_pCurSurface = boat; + m_pCurSurface->RegisterReference((CEntity**)&m_pCurSurface); + m_pCurrentPhysSurface = boat; + } else { + if (boat->m_modelIndex == MI_SKIMMER) { + if (!boat->bIsInWater) { + m_vehDoor = CAR_DOOR_RF; + PedSetOutCarCB(nil, this); + bIsStanding = true; + SetMoveState(PEDMOVE_STILL); + bTryingToReachDryLand = true; + float upMult = 1.04f + boatCol->boundingBox.min.z; + float rightMult = 0.6f * boatCol->boundingBox.max.x; + newPos = upMult * boat->GetUp() + rightMult * boat->GetRight() + boat->GetPosition(); + SetPosition(newPos); + if (m_pMyVehicle) { + PositionPedOutOfCollision(); + } else { + m_pMyVehicle = boat; + PositionPedOutOfCollision(); + m_pMyVehicle = nil; + } + return; + } + + newPos.z += 2.0f; + } + m_vehDoor = CAR_DOOR_RF; + PedSetOutCarCB(nil, this); + bIsStanding = true; + m_pCurSurface = boat; + m_pCurSurface->RegisterReference((CEntity**)&m_pCurSurface); + m_pCurrentPhysSurface = boat; + CColPoint foundCol; + CEntity *foundEnt = nil; + if (CWorld::ProcessVerticalLine(newPos, newPos.z - 1.4f, foundCol, foundEnt, false, true, false, false, false, false, nil)) + newPos.z = FEET_OFFSET + foundCol.point.z; + } + SetPosition(newPos); + SetMoveState(PEDMOVE_STILL); + m_vecMoveSpeed = boat->m_vecMoveSpeed; +} + +// wantedDoorNode = 0 means that func. will determine it +void +CPed::SetExitCar(CVehicle *veh, uint32 wantedDoorNode) +{ + uint32 optedDoorNode = wantedDoorNode; + bool teleportNeeded = false; + bool isLow = !!veh->bLowVehicle; + + bool canJumpOut = false; + if (!veh->CanPedExitCar(false) && !bBusJacked) { + if (IsPlayer()) { + canJumpOut = veh->IsBike() ? veh->CanPedJumpOffBike() : veh->CanPedJumpOutCar(); + } + if (!canJumpOut) { + if (veh->pDriver) { + if (veh->pDriver->IsPlayer()) { + if (veh->pDriver != this) { + m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 5000; + bHeldHostageInCar = true; + } + } else { + veh->AutoPilot.m_nCruiseSpeed = 0; + veh->AutoPilot.m_nCarMission = MISSION_NONE; + } + } + return; + } + } + + if (m_nPedState == PED_EXIT_CAR || m_nPedState == PED_DRAG_FROM_CAR) + return; + + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + if (wantedDoorNode == 0) { + optedDoorNode = CAR_DOOR_LF; + if (veh->IsBike()) { + if (canJumpOut) { + optedDoorNode = veh->pPassengers[0] == this ? CAR_BUMP_REAR : CAR_BOOT; + } else if (veh->pPassengers[0] == this) { + optedDoorNode = CAR_DOOR_LR; + } else { + optedDoorNode = CAR_DOOR_LF; + } + } else if (veh->bIsBus) { + optedDoorNode = CAR_DOOR_LF; + } else if (veh->pDriver == this) { + optedDoorNode = CAR_DOOR_LF; + } else if (veh->pPassengers[0] == this) { + optedDoorNode = CAR_DOOR_RF; + } else if (veh->pPassengers[1] == this) { + optedDoorNode = CAR_DOOR_LR; + } else if (veh->pPassengers[2] == this) { + optedDoorNode = CAR_DOOR_RR; + } else { + for (int i = 3; i < veh->m_nNumMaxPassengers; ++i) { + if (veh->pPassengers[i] == this) { + if (i & 1) + optedDoorNode = CAR_DOOR_RR; + else + optedDoorNode = CAR_DOOR_LR; + + break; + } + } + } + } + bool someoneExitsFromOurExitDoor = false; + bool someoneEntersFromOurExitDoor = false; + if (veh->IsBike()) { + switch (optedDoorNode) { + case CAR_BUMP_REAR: + case CAR_DOOR_RR: + case CAR_DOOR_LR: + if (veh->m_nGettingInFlags & (CAR_DOOR_FLAG_LR | CAR_DOOR_FLAG_RR)) + someoneEntersFromOurExitDoor = true; + break; + case CAR_DOOR_RF: + case CAR_DOOR_LF: + case CAR_BOOT: + if (veh->m_nGettingInFlags & (CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_RF)) + someoneEntersFromOurExitDoor = true; + break; + default: + break; + } + } else { + switch (optedDoorNode) { + case CAR_DOOR_RF: + if (veh->m_nGettingInFlags & CAR_DOOR_FLAG_RF) + someoneEntersFromOurExitDoor = true; + if (veh->m_nGettingOutFlags & CAR_DOOR_FLAG_RF) + someoneExitsFromOurExitDoor = true; + break; + case CAR_DOOR_RR: + if (veh->m_nGettingInFlags & CAR_DOOR_FLAG_RR) + someoneEntersFromOurExitDoor = true; + if (veh->m_nGettingOutFlags & CAR_DOOR_FLAG_RR) + someoneExitsFromOurExitDoor = true; + break; + case CAR_DOOR_LF: + if (veh->m_nGettingInFlags & CAR_DOOR_FLAG_LF) + someoneEntersFromOurExitDoor = true; + if (veh->m_nGettingOutFlags & CAR_DOOR_FLAG_LF) + someoneExitsFromOurExitDoor = true; + break; + case CAR_DOOR_LR: + if (veh->m_nGettingInFlags & CAR_DOOR_FLAG_LR) + someoneEntersFromOurExitDoor = true; + if (veh->m_nGettingOutFlags & CAR_DOOR_FLAG_LR) + someoneExitsFromOurExitDoor = true; + break; + default: + break; + } + } + if (someoneEntersFromOurExitDoor && m_objective == OBJECTIVE_LEAVE_CAR) { + RestorePreviousObjective(); + return; + } + if (!someoneExitsFromOurExitDoor || m_nPedType == PEDTYPE_COP && veh->bIsBus) { +#if defined GTAVC_JP_PATCH || defined FIX_BUGS + if (veh->pDriver == this && !IsPlayer() && veh == CGameLogic::pShortCutTaxi) { + m_objective = OBJECTIVE_NONE; + return; + } +#endif + bool thereIsRoom; + if (canJumpOut) { + thereIsRoom = 1; + } else { + // Again, unused... + // CVector exitPos = GetPositionToOpenCarDoor(veh, optedDoorNode); + thereIsRoom = veh->IsRoomForPedToLeaveCar(optedDoorNode, nil); + } + + if (!thereIsRoom) { + bool trySideSeat = false; + CPed *pedOnSideSeat; + int firstOptedDoor = optedDoorNode; + if (veh->IsBike()) { + switch (optedDoorNode) { + case CAR_DOOR_RF: + optedDoorNode = CAR_DOOR_LF; + break; + case CAR_DOOR_RR: + optedDoorNode = CAR_DOOR_LR; + break; + case CAR_DOOR_LF: + optedDoorNode = CAR_DOOR_RF; + break; + case CAR_DOOR_LR: + optedDoorNode = CAR_DOOR_RR; + break; + default: + break; + } + } else { + switch (optedDoorNode) { + case CAR_DOOR_RF: + if (veh->pDriver || veh->m_nGettingInFlags & CAR_DOOR_FLAG_LF) { + pedOnSideSeat = veh->pDriver; + trySideSeat = true; + } else + optedDoorNode = CAR_DOOR_LF; + + break; + case CAR_DOOR_RR: + if (veh->pPassengers[1] || veh->m_nGettingInFlags & CAR_DOOR_FLAG_LR) { + pedOnSideSeat = veh->pPassengers[1]; + trySideSeat = true; + } else + optedDoorNode = CAR_DOOR_LR; + + break; + case CAR_DOOR_LF: + if (veh->pPassengers[0] || veh->m_nGettingInFlags & CAR_DOOR_FLAG_RF) { + pedOnSideSeat = veh->pPassengers[0]; + trySideSeat = true; + } else + optedDoorNode = CAR_DOOR_RF; + + break; + case CAR_DOOR_LR: + if (veh->pPassengers[2] || veh->m_nGettingInFlags & CAR_DOOR_FLAG_RR) { + pedOnSideSeat = (CPed*)veh->pPassengers[2]; + trySideSeat = true; + } else + optedDoorNode = CAR_DOOR_RR; + + break; + default: + break; + } + } + if (trySideSeat) { + if (!pedOnSideSeat || !IsPlayer() && CharCreatedBy != MISSION_CHAR) + return; + + switch (optedDoorNode) { + case CAR_DOOR_RF: + optedDoorNode = CAR_DOOR_LF; + break; + case CAR_DOOR_RR: + optedDoorNode = CAR_DOOR_LR; + break; + case CAR_DOOR_LF: + optedDoorNode = CAR_DOOR_RF; + break; + case CAR_DOOR_LR: + optedDoorNode = CAR_DOOR_RR; + break; + default: + break; + } + } + // ... + // CVector exitPos = GetPositionToOpenCarDoor(veh, optedDoorNode); + if (!veh->IsRoomForPedToLeaveCar(optedDoorNode, nil)) { + if (!IsPlayer() && CharCreatedBy != MISSION_CHAR) + return; + + // needed for PositionPedOutOfCollision() + optedDoorNode = firstOptedDoor; + m_vehDoor = firstOptedDoor; + PositionPedOutOfCollision(); + teleportNeeded = true; + } + } + + if (!teleportNeeded && veh->IsOnItsSide()) { + m_vehDoor = optedDoorNode; + PositionPedOutOfCollision(); + teleportNeeded = true; + } + + if (m_nPedState == PED_FLEE_POS) { + m_nLastPedState = PED_FLEE_POS; + m_nPrevMoveState = PEDMOVE_RUN; + SetMoveState(PEDMOVE_SPRINT); + } else { + m_nLastPedState = PED_IDLE; + m_nPrevMoveState = PEDMOVE_STILL; + SetMoveState(PEDMOVE_STILL); + } + + bUsesCollision = false; + m_pSeekTarget = veh; + m_pSeekTarget->RegisterReference((CEntity**) &m_pSeekTarget); + m_vehDoor = optedDoorNode; + SetPedState(PED_EXIT_CAR); + if (m_pVehicleAnim && m_pVehicleAnim->flags & ASSOC_PARTIAL) + m_pVehicleAnim->blendDelta = -1000.0f; + RemoveInCarAnims(); + SetMoveState(PEDMOVE_NONE); + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 100.0f); + veh->AutoPilot.m_nCruiseSpeed = 0; + + if (teleportNeeded) { + PedSetOutCarCB(nil, this); + } else { + if (veh->GetUp().z <= -0.8f && !veh->IsBike()) { + if (m_vehDoor == CAR_DOOR_RF || m_vehDoor == CAR_DOOR_RR) { + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CRAWLOUT_RHS); + } else if (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR) { + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CRAWLOUT_LHS); + } + m_pVehicleAnim->SetFinishCallback(PedSetOutCarCB, this); + + } else if (veh->IsBike()) { + CBike* bike = (CBike*)veh; + switch (m_vehDoor) { + case CAR_BUMP_REAR: + case CAR_BOOT: + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_GETOFF_BACK); + break; + case CAR_DOOR_RF: + case CAR_DOOR_RR: + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_GETOFF_RHS); + break; + case CAR_DOOR_LF: + case CAR_DOOR_LR: + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_GETOFF_LHS); + break; + default: + break; + } + int8 exitFlags = 0; + + // Bike door flags incl. passenger jump off + switch (m_vehDoor) { + case CAR_BUMP_REAR: + case CAR_DOOR_RR: + case CAR_DOOR_LR: + exitFlags = CAR_DOOR_FLAG_RR | CAR_DOOR_FLAG_LR; + break; + case CAR_DOOR_RF: + case CAR_DOOR_LF: + case CAR_BOOT: + exitFlags = CAR_DOOR_FLAG_RF | CAR_DOOR_FLAG_LF; + break; + } + + // Passenger get off + if (m_vehDoor == CAR_BUMP_REAR || m_vehDoor == CAR_BOOT) { + m_pVehicleAnim->SetFinishCallback(RestoreHeadingRateCB, this); + m_headingRate = 0.0f; + + } else { + veh->m_nGettingOutFlags |= exitFlags; + m_pVehicleAnim->SetFinishCallback(PedAnimStepOutCarCB, this); + } + + } else { + switch (m_vehDoor) { + case CAR_DOOR_RF: + if (canJumpOut) { + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ROLLOUT_RHS); + } else if (veh->bIsBus) { + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_COACH, ANIM_STD_COACH_GET_OUT_LHS); + } else { + if (isLow) + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GETOUT_LO_RHS); + else + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GETOUT_RHS); + } + break; + case CAR_DOOR_RR: + if (canJumpOut) { + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ROLLOUT_RHS); + } else if (veh->bIsVan) { + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_GET_OUT_REAR_RHS); + } else if (isLow) { + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GETOUT_LO_RHS); + } else { + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GETOUT_RHS); + } + break; + case CAR_DOOR_LF: + if (canJumpOut) { + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ROLLOUT_LHS); + } else if (veh->bIsBus) { + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_COACH, ANIM_STD_COACH_GET_OUT_LHS); + } else { + if (isLow) + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GETOUT_LO_LHS); + else + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GETOUT_LHS); + } + break; + case CAR_DOOR_LR: + if (canJumpOut) { + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ROLLOUT_LHS); + } else if (veh->bIsVan) { + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_GET_OUT_REAR_LHS); + } else if (isLow) { + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GETOUT_LO_LHS); + } else { + m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GETOUT_LHS); + } + break; + default: + break; + } + if (!bBusJacked && !canJumpOut) { + veh->m_nGettingOutFlags |= GetCarDoorFlag(m_vehDoor); + } + m_pVehicleAnim->SetFinishCallback(canJumpOut ? RestoreHeadingRateCB : PedAnimStepOutCarCB, this); + } + } + bChangedSeat = false; + if (veh->bIsBus) + bRenderPedInCar = true; + + SetRadioStation(); + if (veh->pDriver == this) { + if (IsPlayer()) + veh->SetStatus(STATUS_PLAYER_DISABLED); + else + veh->SetStatus(STATUS_ABANDONED); + } + } +} + +void +CPed::ExitCar(void) +{ + if (!m_pVehicleAnim) { + if (InVehicle()) { + if (m_pMyVehicle->IsBike()) { + if (m_vehDoor == CAR_BOOT || m_vehDoor == CAR_BUMP_REAR) { + ((CBike*)m_pMyVehicle)->KnockOffRider(WEAPONTYPE_UNARMED, 0, this, false); + } + } else if (m_pMyVehicle->IsCar()) { + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROLLOUT_LHS)) { + ((CAutomobile*)m_pMyVehicle)->KnockPedOutCar(WEAPONTYPE_UNIDENTIFIED, CAR_DOOR_LF, this); + } else if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROLLOUT_RHS)) { + ((CAutomobile*)m_pMyVehicle)->KnockPedOutCar(WEAPONTYPE_UNIDENTIFIED, CAR_DOOR_RF, this); + } + } + } + return; + } + + AnimationId exitAnim = (AnimationId) m_pVehicleAnim->animId; + float animTime = m_pVehicleAnim->currentTime; + + if (exitAnim == ANIM_BIKE_GETOFF_BACK) { + if (animTime > 0.35f && m_pMyVehicle && m_pMyVehicle->IsBike()) + ((CBike*)m_pMyVehicle)->KnockOffRider(WEAPONTYPE_UNARMED, 0, this, false); + else + LineUpPedWithCar(LINE_UP_TO_CAR_FALL); + + } else if (exitAnim == ANIM_STD_ROLLOUT_LHS || exitAnim == ANIM_STD_ROLLOUT_RHS) { + if (animTime > 0.07f && m_pMyVehicle && m_pMyVehicle->IsCar()) { + if (exitAnim == ANIM_STD_ROLLOUT_LHS) { + ((CAutomobile*)m_pMyVehicle)->KnockPedOutCar(WEAPONTYPE_UNIDENTIFIED, CAR_DOOR_LF, this); + } else { + ((CAutomobile*)m_pMyVehicle)->KnockPedOutCar(WEAPONTYPE_UNIDENTIFIED, CAR_DOOR_RF, this); + } + } else { + LineUpPedWithCar(LINE_UP_TO_CAR_FALL); + } + } else { + m_pMyVehicle->ProcessOpenDoor(m_vehDoor, exitAnim, animTime); + + if (m_pSeekTarget) { + // Car is upside down + if (m_pMyVehicle->GetUp().z > -0.8f) { + if (exitAnim == ANIM_STD_CAR_CLOSE_RHS || exitAnim == ANIM_STD_CAR_CLOSE_LHS || animTime > 0.3f) + LineUpPedWithCar(LINE_UP_TO_CAR_END); + else + LineUpPedWithCar((m_pMyVehicle->GetModelIndex() == MI_DODO ? LINE_UP_TO_CAR_END : LINE_UP_TO_CAR_START)); + } + else { + LineUpPedWithCar(LINE_UP_TO_CAR_END); + } + } + + // If there is someone in front of the door, make him fall while we exit. + if (m_nPedState == PED_EXIT_CAR) { + CPed* foundPed = nil; + for (int i = 0; i < m_numNearPeds; i++) { + if ((m_nearPeds[i]->GetPosition() - GetPosition()).MagnitudeSqr2D() < SQR(0.2f)) { + foundPed = m_nearPeds[i]; + break; + } + } + if(foundPed && (!foundPed->IsPlayer() || m_nPedType == PEDTYPE_COP || m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS)) + if (animTime > 0.4f && foundPed->IsPedInControl()) + foundPed->SetFall(1000, ANIM_STD_HIGHIMPACT_FRONT, 1); + } + } +} + +CVector +CPed::GetPositionToOpenCarDoor(CVehicle *veh, uint32 component) +{ + CVector doorPos; + CVector vehDoorOffset; + CVehicleModelInfo* vehModel = veh->GetModelInfo(); + + if (veh->IsBike()) { + CBike* bike = (CBike*)veh; + + if (component == CAR_WINDSCREEN) { + doorPos = vehModel->GetFrontSeatPosn(); + return bike->GetMatrix() * (doorPos + + CVector(-vecPedBikeKickAnimOffset.x, vecPedBikeKickAnimOffset.y, -vecPedBikeKickAnimOffset.z)); + } else { + switch (bike->m_bikeAnimType) { + case ASSOCGRP_BIKE_VESPA: + vehDoorOffset = vecPedVespaBikeJumpRhsAnimOffset; + break; + case ASSOCGRP_BIKE_HARLEY: + vehDoorOffset = vecPedHarleyBikeJumpRhsAnimOffset; + break; + case ASSOCGRP_BIKE_DIRT: + vehDoorOffset = vecPedDirtBikeJumpRhsAnimOffset; + break; + default: + vehDoorOffset = vecPedStdBikeJumpRhsAnimOffset; + break; + } + } + + doorPos = vehModel->GetFrontSeatPosn(); + if (component == CAR_DOOR_LR || component == CAR_DOOR_RR) { + doorPos = vehModel->m_positions[CAR_POS_BACKSEAT]; + } + + if (component == CAR_DOOR_LR || component == CAR_DOOR_LF) { + vehDoorOffset.x *= -1.f; + } + + CVector correctedPos; + bike->GetCorrectedWorldDoorPosition(correctedPos, vehDoorOffset, doorPos); + return correctedPos; + } else { + float seatOffset; + if (veh->bIsVan && (component == CAR_DOOR_LR || component == CAR_DOOR_RR)) { + seatOffset = 0.0f; + vehDoorOffset = vecPedVanRearDoorAnimOffset; + } else { + seatOffset = veh->pHandling->fSeatOffsetDistance; + if (veh->bLowVehicle) { + vehDoorOffset = vecPedCarDoorLoAnimOffset; + } else { + vehDoorOffset = vecPedCarDoorAnimOffset; + } + } + + switch (component) { + case CAR_DOOR_RF: + doorPos = vehModel->GetFrontSeatPosn(); + doorPos.x += seatOffset; + vehDoorOffset.x = -vehDoorOffset.x; + break; + + case CAR_DOOR_RR: + doorPos = vehModel->m_positions[CAR_POS_BACKSEAT]; + doorPos.x += seatOffset; + vehDoorOffset.x = -vehDoorOffset.x; + break; + + case CAR_DOOR_LF: + doorPos = vehModel->GetFrontSeatPosn(); + doorPos.x += seatOffset; + doorPos.x = -doorPos.x; + break; + + case CAR_DOOR_LR: + doorPos = vehModel->m_positions[CAR_POS_BACKSEAT]; + doorPos.x += seatOffset; + doorPos.x = -doorPos.x; + break; + + default: + doorPos = vehModel->GetFrontSeatPosn(); + vehDoorOffset = CVector(0.0f, 0.0f, 0.0f); + break; + } + + CVector diffVec = doorPos - vehDoorOffset; + return Multiply3x3(veh->GetMatrix(), diffVec) + veh->GetPosition(); + + //unused + //doorPos = Multiply3x3(veh->GetMatrix(), doorPos) + veh->GetMatrix(); + } +} + +void +CPed::GetNearestDoor(CVehicle *veh, CVector &posToOpen) +{ + CVector *enterOffset = nil; + if (veh->IsBike()) { + if (m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER) { + + // If bike didn't fall to ground + if (Abs(veh->GetRight().z) < 0.1f) { + float angleDiff = (GetPosition() - veh->GetPosition()).Heading() - veh->GetForward().Heading(); + + if (angleDiff > PI) + angleDiff -= TWOPI; + else if (angleDiff < -PI) + angleDiff += TWOPI; + + if (Abs(angleDiff) < DEGTORAD(30.f) + && (IsPlayer() && ((CPlayerPed*)this)->m_fMoveSpeed > 1.5f && !m_vehDoor || + !IsPlayer() && m_nPedType != PEDTYPE_COP && m_nMoveState == PEDMOVE_RUN + && m_pedStats->m_temper > 65 + && !m_vehDoor || m_vehDoor == CAR_WINDSCREEN)) { + m_vehDoor = CAR_WINDSCREEN; + posToOpen = GetPositionToOpenCarDoor(veh, CAR_WINDSCREEN); + return; + } + } + } + } else if (m_vehDoor == CAR_DOOR_LF && veh->pDriver && !veh->bLowVehicle && !veh->bIsBus) { + enterOffset = &vecPedQuickDraggedOutCarAnimOffset; + } + + CVector lfPos = GetPositionToOpenCarDoor(veh, CAR_DOOR_LF); + CVector rfPos = GetPositionToOpenCarDoor(veh, CAR_DOOR_RF); + + // Left front door is closer + if ((lfPos - GetPosition()).MagnitudeSqr2D() < (rfPos - GetPosition()).MagnitudeSqr2D()) { + + if (veh->IsRoomForPedToLeaveCar(CAR_DOOR_LF, enterOffset)) { + m_vehDoor = CAR_DOOR_LF; + posToOpen = lfPos; + } else if (veh->IsRoomForPedToLeaveCar(CAR_DOOR_RF, enterOffset)) { + m_vehDoor = CAR_DOOR_RF; + posToOpen = rfPos; + } + } else { + + if (veh->IsRoomForPedToLeaveCar(CAR_DOOR_RF, enterOffset)) { + + CPed *rfPassenger = veh->pPassengers[0]; + if (rfPassenger && !veh->IsBike() + && (rfPassenger->m_leader == this || rfPassenger->bDontDragMeOutCar || + veh->VehicleCreatedBy == MISSION_VEHICLE && m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) + && veh->IsRoomForPedToLeaveCar(CAR_DOOR_LF, enterOffset) + || (veh->m_nGettingInFlags & CAR_DOOR_FLAG_RF) && veh->IsRoomForPedToLeaveCar(CAR_DOOR_LF, enterOffset)) { + + m_vehDoor = CAR_DOOR_LF; + posToOpen = lfPos; + } else { + m_vehDoor = CAR_DOOR_RF; + posToOpen = rfPos; + } + } else if (veh->IsRoomForPedToLeaveCar(CAR_DOOR_LF, enterOffset)) { + m_vehDoor = CAR_DOOR_LF; + posToOpen = lfPos; + } + } +} + +bool +CPed::GetNearestPassengerDoor(CVehicle *veh, CVector &posToOpen) +{ + CVector rfPos, lrPos, rrPos; + bool canEnter = false; + + CVehicleModelInfo *vehModel = (CVehicleModelInfo *)CModelInfo::GetModelInfo(veh->GetModelIndex()); + + switch (veh->GetModelIndex()) { + case MI_BUS: + m_vehDoor = CAR_DOOR_RF; + posToOpen = GetPositionToOpenCarDoor(veh, CAR_DOOR_RF); + return true; + case MI_RHINO: + default: + break; + } + + CVector2D rfPosDist(999.0f, 999.0f); + CVector2D lrPosDist(999.0f, 999.0f); + CVector2D rrPosDist(999.0f, 999.0f); + + if (veh->IsBike()) { + if (!veh->pPassengers[0] + && !(veh->m_nGettingInFlags & CAR_DOOR_FLAG_LR) + && veh->IsRoomForPedToLeaveCar(CAR_DOOR_LR, nil)) { + lrPos = GetPositionToOpenCarDoor(veh, CAR_DOOR_LR); + canEnter = true; + lrPosDist = lrPos - GetPosition(); + } + if (!veh->pPassengers[0] + && !(veh->m_nGettingInFlags & CAR_DOOR_FLAG_RR) + && veh->IsRoomForPedToLeaveCar(CAR_DOOR_RR, nil)) { + rrPos = GetPositionToOpenCarDoor(veh, CAR_DOOR_RR); + canEnter = true; + rrPosDist = rrPos - GetPosition(); + } + } else if (!veh->pPassengers[0] + && !(veh->m_nGettingInFlags & CAR_DOOR_FLAG_RF) + && veh->IsRoomForPedToLeaveCar(CAR_DOOR_RF, nil)) { + + rfPos = GetPositionToOpenCarDoor(veh, CAR_DOOR_RF); + canEnter = true; + rfPosDist = rfPos - GetPosition(); + } + + if (vehModel->m_numDoors == 4) { + if (!veh->pPassengers[1] + && !(veh->m_nGettingInFlags & CAR_DOOR_FLAG_LR) + && veh->IsRoomForPedToLeaveCar(CAR_DOOR_LR, nil)) { + lrPos = GetPositionToOpenCarDoor(veh, CAR_DOOR_LR); + canEnter = true; + lrPosDist = lrPos - GetPosition(); + } + if (!veh->pPassengers[2] + && !(veh->m_nGettingInFlags & CAR_DOOR_FLAG_RR) + && veh->IsRoomForPedToLeaveCar(CAR_DOOR_RR, nil)) { + rrPos = GetPositionToOpenCarDoor(veh, CAR_DOOR_RR); + canEnter = true; + rrPosDist = rrPos - GetPosition(); + } + + // When the door we should enter is blocked by some object. + if (!canEnter) + veh->ShufflePassengersToMakeSpace(); + } + + CVector2D nextToCompare = rfPosDist; + posToOpen = rfPos; + m_vehDoor = CAR_DOOR_RF; + if (lrPosDist.MagnitudeSqr() < nextToCompare.MagnitudeSqr()) { + m_vehDoor = CAR_DOOR_LR; + posToOpen = lrPos; + nextToCompare = lrPosDist; + } + + if (rrPosDist.MagnitudeSqr() < nextToCompare.MagnitudeSqr()) { + m_vehDoor = CAR_DOOR_RR; + posToOpen = rrPos; + } + return canEnter; +} + +void +CPed::GoToNearestDoor(CVehicle *veh) +{ + CVector posToOpen; + GetNearestDoor(veh, posToOpen); + SetSeek(posToOpen, 0.5f); + SetMoveState(PEDMOVE_RUN); +} + +// Unused +void CPed::PedSetGetInCarPositionCB(CAnimBlendAssociation* assoc, void* arg) +{ + CPed* pPed = (CPed*)arg; + CMatrix mat(pPed->GetMatrix()); + CVehicle* pVehicle = pPed->m_pMyVehicle; + const CVector& offset = (pVehicle->bIsVan && (pPed->m_vehDoor == CAR_DOOR_RR || pPed->m_vehDoor == CAR_DOOR_LR)) ? vecPedVanRearDoorAnimOffset : vecPedCarDoorAnimOffset; + CVector position = Multiply3x3(mat, offset) + pPed->GetPosition(); + CPedPlacement::FindZCoorForPed(&position); + pPed->SetMoveSpeed(0.0f, 0.0f, 0.0f); + pPed->SetPosition(position); +} + +void +CPed::SetAnimOffsetForEnterOrExitVehicle(void) +{ + // FIX_BUGS: If there were no translations on enter anims, there were overflows all over this function. + + int vanBlock = CAnimManager::GetAnimationBlockIndex("van"); + int bikesBlock = CAnimManager::GetAnimationBlockIndex("bikes"); + int bikevBlock = CAnimManager::GetAnimationBlockIndex("bikev"); + int bikehBlock = CAnimManager::GetAnimationBlockIndex("bikeh"); + int bikedBlock = CAnimManager::GetAnimationBlockIndex("biked"); + CStreaming::RequestAnim(vanBlock, STREAMFLAGS_DEPENDENCY); + CStreaming::RequestAnim(bikesBlock, STREAMFLAGS_DEPENDENCY); + CStreaming::RequestAnim(bikevBlock, STREAMFLAGS_DEPENDENCY); + CStreaming::RequestAnim(bikehBlock, STREAMFLAGS_DEPENDENCY); + CStreaming::RequestAnim(bikedBlock, STREAMFLAGS_DEPENDENCY); + CStreaming::LoadAllRequestedModels(false); + CAnimManager::AddAnimBlockRef(vanBlock); + CAnimManager::AddAnimBlockRef(bikesBlock); + CAnimManager::AddAnimBlockRef(bikevBlock); + CAnimManager::AddAnimBlockRef(bikehBlock); + CAnimManager::AddAnimBlockRef(bikedBlock); + + CAnimBlendHierarchy *enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, ANIM_STD_JACKEDCAR_LHS)->hierarchy; + CAnimBlendSequence *seq = enterAssoc->sequences; + if (seq->numFrames > 0) { + if (!seq->HasTranslation()) + vecPedDraggedOutCarAnimOffset = CVector(0.0f, 0.0f, 0.0f); + else { + vecPedDraggedOutCarAnimOffset = seq->GetEndTranslation(); + } + } + + enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LHS)->hierarchy; + seq = enterAssoc->sequences; + if (seq->numFrames > 0) { + if (!seq->HasTranslation()) + vecPedCarDoorAnimOffset = CVector(0.0f, 0.0f, 0.0f); + else { + vecPedCarDoorAnimOffset = seq->GetEndTranslation(); + } + } + + enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LO_LHS)->hierarchy; + seq = enterAssoc->sequences; + if (seq->numFrames > 0) { + if (!seq->HasTranslation()) + vecPedCarDoorLoAnimOffset = CVector(0.0f, 0.0f, 0.0f); + else { + vecPedCarDoorLoAnimOffset = seq->GetEndTranslation(); + } + } + + enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, ANIM_STD_QUICKJACKED)->hierarchy; + seq = enterAssoc->sequences; + if (seq->numFrames > 0) { + if (!seq->HasTranslation()) + vecPedQuickDraggedOutCarAnimOffset = CVector(0.0f, 0.0f, 0.0f); + else { + vecPedQuickDraggedOutCarAnimOffset = seq->GetEndTranslation(); + } + } + + enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_VAN, ANIM_STD_VAN_GET_IN_REAR_LHS)->hierarchy; + seq = enterAssoc->sequences; + if (seq->numFrames > 0) { + if (!seq->HasTranslation()) + vecPedVanRearDoorAnimOffset = CVector(0.0f, 0.0f, 0.0f); + else { + vecPedVanRearDoorAnimOffset = seq->GetEndTranslation(); + } + } + + enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, ANIM_STD_TRAIN_GETOUT)->hierarchy; + seq = enterAssoc->sequences; + if (seq->numFrames > 0) { + if (!seq->HasTranslation()) + vecPedTrainDoorAnimOffset = CVector(0.0f, 0.0f, 0.0f); + else { + vecPedTrainDoorAnimOffset = seq->GetEndTranslation(); + } + } + + enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_BIKE_STANDARD, ANIM_BIKE_JUMPON_LHS)->hierarchy; + seq = enterAssoc->sequences; + if (seq->numFrames > 0) { + if (!seq->HasTranslation()) + vecPedStdBikeJumpRhsAnimOffset = CVector(0.0f, 0.0f, 0.0f); + else { + vecPedStdBikeJumpRhsAnimOffset = seq->GetEndTranslation(); + } + } + + enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_BIKE_VESPA, ANIM_BIKE_JUMPON_LHS)->hierarchy; + seq = enterAssoc->sequences; + if (seq->numFrames > 0) { + if (!seq->HasTranslation()) + vecPedVespaBikeJumpRhsAnimOffset = CVector(0.0f, 0.0f, 0.0f); + else { + vecPedVespaBikeJumpRhsAnimOffset = seq->GetEndTranslation(); + } + } + + enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_BIKE_HARLEY, ANIM_BIKE_JUMPON_LHS)->hierarchy; + seq = enterAssoc->sequences; + if (seq->numFrames > 0) { + if (!seq->HasTranslation()) + vecPedHarleyBikeJumpRhsAnimOffset = CVector(0.0f, 0.0f, 0.0f); + else { + vecPedHarleyBikeJumpRhsAnimOffset = seq->GetEndTranslation(); + } + } + + enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_BIKE_DIRT, ANIM_BIKE_JUMPON_LHS)->hierarchy; + seq = enterAssoc->sequences; + if (seq->numFrames > 0) { + if (!seq->HasTranslation()) + vecPedDirtBikeJumpRhsAnimOffset = CVector(0.0f, 0.0f, 0.0f); + else { + vecPedDirtBikeJumpRhsAnimOffset = seq->GetEndTranslation(); + } + } + + enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_BIKE_HARLEY, ANIM_BIKE_KICK)->hierarchy; + seq = enterAssoc->sequences; + if (seq->numFrames > 0) { + if (!seq->HasTranslation()) + vecPedBikeKickAnimOffset = CVector(0.0f, 0.0f, 0.0f); + else { + vecPedBikeKickAnimOffset = seq->GetEndTranslation(); + } + } + + CAnimManager::RemoveAnimBlockRef(vanBlock); + CAnimManager::RemoveAnimBlockRef(bikesBlock); + CAnimManager::RemoveAnimBlockRef(bikevBlock); + CAnimManager::RemoveAnimBlockRef(bikehBlock); + CAnimManager::RemoveAnimBlockRef(bikedBlock); +} + +void +CPed::PedSetQuickDraggedOutCarPositionCB(CAnimBlendAssociation *animAssoc, void *arg) +{ + CPed *ped = (CPed*)arg; + + CVehicle *veh = ped->m_pMyVehicle; + + CVector finalPos; + CVector draggedOutOffset; + + CMatrix pedMat(ped->GetMatrix()); + ped->bUsesCollision = true; + ped->RestartNonPartialAnims(); + draggedOutOffset = vecPedQuickDraggedOutCarAnimOffset; + if (ped->m_vehDoor == CAR_DOOR_RF || ped->m_vehDoor == CAR_DOOR_RR) + draggedOutOffset.x = -draggedOutOffset.x; + + finalPos = Multiply3x3(pedMat, draggedOutOffset) + ped->GetPosition(); + CPedPlacement::FindZCoorForPed(&finalPos); + ped->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + ped->SetPosition(finalPos); + + if (veh) { + ped->m_fRotationDest = veh->GetForward().Heading() - HALFPI; + ped->m_fRotationCur = ped->m_fRotationDest; + ped->CalculateNewOrientation(); + + if (!veh->IsRoomForPedToLeaveCar(ped->m_vehDoor, &vecPedQuickDraggedOutCarAnimOffset)) + ped->PositionPedOutOfCollision(); + } + + if (!ped->CanSetPedState()) + return; + + ped->SetIdle(); + if (veh) { + if (ped->bFleeAfterExitingCar) { + ped->bFleeAfterExitingCar = false; + ped->SetFlee(veh->GetPosition(), 14000); + + } else if (ped->bWanderPathAfterExitingCar) { + ped->SetWanderPath(CGeneral::GetRandomNumberInRange(0.0f, 8.0f)); + ped->bWanderPathAfterExitingCar = false; + + } else if (ped->bGonnaKillTheCarJacker) { + ped->bGonnaKillTheCarJacker = false; + if (ped->m_pedInObjective && CGeneral::GetRandomNumber() & 1) { + if (ped->m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT) + ped->SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, ped->m_pedInObjective); + + } else { + CPed *driver = veh->pDriver; + if (!driver || driver == ped || driver->IsPlayer() && CTheScripts::IsPlayerOnAMission()) { + ped->SetFlee(veh->GetPosition(), 14000); + } else { + ped->ClearObjective(); + ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, veh); + } + ped->bUsePedNodeSeek = true; + ped->m_pNextPathNode = nil; + ped->Say(SOUND_PED_FLEE_RUN); + } + } else if (ped->m_pedStats->m_temper > ped->m_pedStats->m_fear + && ped->CharCreatedBy != MISSION_CHAR && veh->VehicleCreatedBy != MISSION_VEHICLE + && veh->pDriver && veh->pDriver->IsPlayer() + && !CTheScripts::IsPlayerOnAMission()) { + ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, veh); + + } else if (ped->m_pedStats->m_temper > ped->m_pedStats->m_fear + && ped->CharCreatedBy != MISSION_CHAR && veh->VehicleCreatedBy != MISSION_VEHICLE + && !veh->pDriver && FindPlayerPed()->m_carInObjective == veh + && !CTheScripts::IsPlayerOnAMission()) { + ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, veh); + + } else { + ped->SetFindPathAndFlee(veh->GetPosition(), 10000); + if (CGeneral::GetRandomNumber() & 1 || ped->m_pedStats->m_fear > 70) { + ped->SetMoveState(PEDMOVE_SPRINT); + ped->Say(SOUND_PED_FLEE_SPRINT); + } else { + ped->Say(SOUND_PED_FLEE_RUN); + } + } + } + if (ped->m_nLastPedState == PED_IDLE) + ped->m_nLastPedState = PED_WANDER_PATH; +} + +void +CPed::PedSetDraggedOutCarPositionCB(CAnimBlendAssociation* animAssoc, void* arg) +{ + CPed *ped = (CPed*)arg; + + ped->bUsesCollision = true; + ped->RestartNonPartialAnims(); + + CMatrix pedMat(ped->GetMatrix()); + CVector draggedOutOffset; + if (ped->m_pMyVehicle && ped->m_pMyVehicle->IsBike()) { + AssocGroupId animGroup = ((CBike*)ped->m_pMyVehicle)->m_bikeAnimType; + switch (animGroup) { + case ASSOCGRP_BIKE_VESPA: + draggedOutOffset = vecPedVespaBikeJumpRhsAnimOffset; + break; + case ASSOCGRP_BIKE_HARLEY: + draggedOutOffset = vecPedHarleyBikeJumpRhsAnimOffset; + break; + case ASSOCGRP_BIKE_DIRT: + draggedOutOffset = vecPedDirtBikeJumpRhsAnimOffset; + break; + default: + draggedOutOffset = vecPedStdBikeJumpRhsAnimOffset; + break; + } + } else { + draggedOutOffset = CVector(vecPedDraggedOutCarAnimOffset.x, vecPedDraggedOutCarAnimOffset.y, 0.0f); + } + if (ped->m_vehDoor == CAR_DOOR_RF || ped->m_vehDoor == CAR_DOOR_RR) + draggedOutOffset.x = -draggedOutOffset.x; + + CVector posAfterBeingDragged = Multiply3x3(pedMat, draggedOutOffset); + posAfterBeingDragged += ped->GetPosition(); + CPedPlacement::FindZCoorForPed(&posAfterBeingDragged); + ped->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + ped->SetPosition(posAfterBeingDragged); + + if (ped->m_pMyVehicle && !ped->m_pMyVehicle->IsBike() && !ped->m_pMyVehicle->IsRoomForPedToLeaveCar(ped->m_vehDoor, &draggedOutOffset)) { + ped->PositionPedOutOfCollision(); + } + + if (!ped->CanSetPedState()) + return; + + if (!ped->m_pMyVehicle) { + ped->SetIdle(); + ped->SetGetUp(); + return; + } + + CPed *driver = ped->m_pMyVehicle->pDriver; + + if (ped->IsPlayer()) { + ped->SetIdle(); + + } else if (ped->bFleeAfterExitingCar) { + ped->bFleeAfterExitingCar = false; + ped->SetFlee(ped->m_pMyVehicle->GetPosition(), 4000); + + } else if (ped->bWanderPathAfterExitingCar) { + ped->SetWanderPath(CGeneral::GetRandomNumberInRange(0.0f, 8.0f)); + ped->bWanderPathAfterExitingCar = false; + + } else if (ped->bGonnaKillTheCarJacker) { + // Kill objective is already set at this point. + + ped->bGonnaKillTheCarJacker = false; + if (!ped->m_pedInObjective || !(CGeneral::GetRandomNumber() & 1)) { + if (!driver || driver == ped || driver->IsPlayer() && CTheScripts::IsPlayerOnAMission()) { + ped->SetPedState(PED_NONE); + ped->m_nLastPedState = PED_NONE; + ped->SetFlee(ped->m_pMyVehicle->GetPosition(), 4000); + } else { + ped->ClearObjective(); + ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, ped->m_pMyVehicle); + } + } + + } else if (ped->m_pedStats->m_temper > ped->m_pedStats->m_fear && ped->CharCreatedBy != MISSION_CHAR + && ped->m_pMyVehicle->VehicleCreatedBy != MISSION_VEHICLE && driver + && driver->IsPlayer() && !CTheScripts::IsPlayerOnAMission()) { + ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, ped->m_pMyVehicle); + + } else if (ped->m_pedStats->m_temper > ped->m_pedStats->m_fear && ped->CharCreatedBy != MISSION_CHAR + && ped->m_pMyVehicle->VehicleCreatedBy != MISSION_VEHICLE && !driver + && FindPlayerPed()->m_carInObjective == ped->m_pMyVehicle && !CTheScripts::IsPlayerOnAMission()) + ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, ped->m_pMyVehicle); + else { + ped->SetPedState(PED_NONE); + ped->m_nLastPedState = PED_NONE; + ped->SetFindPathAndFlee(ped->m_pMyVehicle->GetPosition(), 10000); + } + ped->SetGetUp(); +} + +uint8 +CPed::GetNearestTrainDoor(CVehicle *train, CVector &doorPos) +{ + GetNearestTrainPedPosition(train, doorPos); +/* + // Not used. + CVehicleModelInfo* trainModel = (CVehicleModelInfo*)CModelInfo::GetModelInfo(train->m_modelIndex); + CMatrix trainMat = CMatrix(train->GetMatrix()); + + doorPos = trainModel->m_positions[m_vehDoor]; + doorPos.x -= 1.5f; + doorPos = Multiply3x3(trainMat, doorPos); + doorPos += train->GetPosition(); +*/ + return 1; +} + +uint8 +CPed::GetNearestTrainPedPosition(CVehicle *train, CVector &enterPos) +{ + CVector enterStepOffset; + CVehicleModelInfo *trainModel = (CVehicleModelInfo *)CModelInfo::GetModelInfo(train->GetModelIndex()); + CMatrix trainMat = CMatrix(train->GetMatrix()); + CVector leftEntryPos, rightEntryPos, midEntryPos; + float distLeftEntry, distRightEntry, distMidEntry; + + // enterStepOffset = vecPedCarDoorAnimOffset; + enterStepOffset = CVector(1.5f, 0.0f, 0.0f); + + if (train->pPassengers[TRAIN_POS_LEFT_ENTRY]) { + distLeftEntry = 999.0f; + } else { + leftEntryPos = trainModel->m_positions[TRAIN_POS_LEFT_ENTRY] - enterStepOffset; + leftEntryPos = Multiply3x3(trainMat, leftEntryPos); + leftEntryPos += train->GetPosition(); + distLeftEntry = (leftEntryPos - GetPosition()).Magnitude(); + } + + if (train->pPassengers[TRAIN_POS_MID_ENTRY]) { + distMidEntry = 999.0f; + } else { + midEntryPos = trainModel->m_positions[TRAIN_POS_MID_ENTRY] - enterStepOffset; + midEntryPos = Multiply3x3(trainMat, midEntryPos); + midEntryPos += train->GetPosition(); + distMidEntry = (midEntryPos - GetPosition()).Magnitude(); + } + + if (train->pPassengers[TRAIN_POS_RIGHT_ENTRY]) { + distRightEntry = 999.0f; + } else { + rightEntryPos = trainModel->m_positions[TRAIN_POS_RIGHT_ENTRY] - enterStepOffset; + rightEntryPos = Multiply3x3(trainMat, rightEntryPos); + rightEntryPos += train->GetPosition(); + distRightEntry = (rightEntryPos - GetPosition()).Magnitude(); + } + + if (distMidEntry < distLeftEntry) { + if (distMidEntry < distRightEntry) { + enterPos = midEntryPos; + m_vehDoor = TRAIN_POS_MID_ENTRY; + } else { + enterPos = rightEntryPos; + m_vehDoor = TRAIN_POS_RIGHT_ENTRY; + } + } else if (distRightEntry < distLeftEntry) { + enterPos = rightEntryPos; + m_vehDoor = TRAIN_POS_RIGHT_ENTRY; + } else { + enterPos = leftEntryPos; + m_vehDoor = TRAIN_POS_LEFT_ENTRY; + } + + return 1; +} + +void +CPed::PedSetInTrainCB(CAnimBlendAssociation* animAssoc, void* arg) +{ + CPed *ped = (CPed*)arg; + CTrain *veh = (CTrain*)ped->m_pMyVehicle; + + if (!veh) + return; + + ped->bInVehicle = true; + ped->SetPedState(PED_DRIVING); + ped->RestorePreviousObjective(); + ped->SetMoveState(PEDMOVE_STILL); + veh->AddPassenger(ped); +} + +#ifdef GTA_TRAIN +void +CPed::SetEnterTrain(CVehicle *train, uint32 unused) +{ + if (m_nPedState == PED_ENTER_TRAIN || !((CTrain*)train)->Doors[0].IsFullyOpen()) + return; + + /* + // Not used + CVector enterPos; + GetNearestTrainPedPosition(train, enterPos); + */ + m_fRotationCur = train->GetForward().Heading() - HALFPI; + m_pMyVehicle = train; + m_pMyVehicle->RegisterReference((CEntity **) &m_pMyVehicle); + + SetPedState(PED_ENTER_TRAIN); + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_TRAIN_GETIN, 4.0f); + m_pVehicleAnim->SetFinishCallback(PedSetInTrainCB, this); + bUsesCollision = false; + LineUpPedWithTrain(); + if (IsPlayer()) { + if (((CPlayerPed*)this)->m_bAdrenalineActive) + ((CPlayerPed*)this)->ClearAdrenaline(); + } +} + +void +CPed::EnterTrain(void) +{ + LineUpPedWithTrain(); +} + +void +CPed::SetPedPositionInTrain(void) +{ + LineUpPedWithTrain(); +} + +void +CPed::LineUpPedWithTrain(void) +{ + CVector lineUpPos; + CVehicleModelInfo *trainModel = (CVehicleModelInfo *)CModelInfo::GetModelInfo(m_pMyVehicle->GetModelIndex()); + CVector enterOffset(1.5f, 0.0f, -0.2f); + + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + m_fRotationCur = m_pMyVehicle->GetForward().Heading() - HALFPI; + m_fRotationDest = m_fRotationCur; + + if (!bInVehicle) { + GetNearestTrainDoor(m_pMyVehicle, lineUpPos); + lineUpPos.z += 0.2f; + } else { + if (m_pMyVehicle->pPassengers[TRAIN_POS_LEFT_ENTRY] == this) { + + lineUpPos = trainModel->m_positions[TRAIN_POS_LEFT_ENTRY] - enterOffset; + + } else if (m_pMyVehicle->pPassengers[TRAIN_POS_MID_ENTRY] == this) { + + lineUpPos = trainModel->m_positions[TRAIN_POS_MID_ENTRY] - enterOffset; + + } else if (m_pMyVehicle->pPassengers[TRAIN_POS_RIGHT_ENTRY] == this) { + + lineUpPos = trainModel->m_positions[TRAIN_POS_RIGHT_ENTRY] - enterOffset; + } + lineUpPos = Multiply3x3(m_pMyVehicle->GetMatrix(), lineUpPos); + lineUpPos += m_pMyVehicle->GetPosition(); + } + + if (m_pVehicleAnim) { + float percentageLeft = m_pVehicleAnim->GetTimeLeft() / m_pVehicleAnim->hierarchy->totalLength; + lineUpPos += (GetPosition() - lineUpPos) * percentageLeft; + } + + SetPosition(lineUpPos); + SetHeading(m_fRotationCur); +} + +void +CPed::SetExitTrain(CVehicle* train) +{ + if (m_nPedState == PED_EXIT_TRAIN || train->GetStatus() != STATUS_TRAIN_NOT_MOVING || !((CTrain*)train)->Doors[0].IsFullyOpen()) + return; + + /* + // Not used + CVector exitPos; + GetNearestTrainPedPosition(train, exitPos); + */ + SetPedState(PED_EXIT_TRAIN); + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_TRAIN_GETOUT, 4.0f); + m_pVehicleAnim->SetFinishCallback(PedSetOutTrainCB, this); + bUsesCollision = false; + LineUpPedWithTrain(); +} + +void +CPed::ExitTrain(void) +{ + LineUpPedWithTrain(); +} + +void +CPed::PedSetOutTrainCB(CAnimBlendAssociation *animAssoc, void *arg) +{ + CPed *ped = (CPed*)arg; + + CVehicle *veh = ped->m_pMyVehicle; + + if (ped->m_pVehicleAnim) + ped->m_pVehicleAnim->blendDelta = -1000.0f; + + ped->bUsesCollision = true; + ped->m_pVehicleAnim = nil; + ped->bInVehicle = false; + ped->SetPedState(PED_IDLE); + ped->RestorePreviousObjective(); + ped->SetMoveState(PEDMOVE_STILL); + + CMatrix pedMat(ped->GetMatrix()); + ped->m_fRotationCur = HALFPI + veh->GetForward().Heading(); + ped->m_fRotationDest = ped->m_fRotationCur; + CVector posAfterExit = Multiply3x3(pedMat, vecPedTrainDoorAnimOffset); + posAfterExit += ped->GetPosition(); + CPedPlacement::FindZCoorForPed(&posAfterExit); + ped->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + ped->SetPosition(posAfterExit); + ped->SetHeading(ped->m_fRotationCur); + veh->RemovePassenger(ped); +} +#endif + +void +CPed::RegisterThreatWithGangPeds(CEntity *attacker) +{ + CPed *attackerPed = nil; + if ((CharCreatedBy == MISSION_CHAR && bIsPlayerFriend && (attacker == FindPlayerPed() || attacker == FindPlayerVehicle())) + || (attacker && m_leader == attacker) + || (m_nPedType == PEDTYPE_GANG7 && attacker == FindPlayerPed())) + return; + + if (attacker) { + if (m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT && m_objective != OBJECTIVE_KILL_CHAR_ANY_MEANS) { + if (attacker->IsPed()) { + attackerPed = (CPed*)attacker; + } else if (attacker->IsVehicle()) { + attackerPed = ((CVehicle*)attacker)->pDriver; + if (!attackerPed) + return; + } else + return; + + if (attackerPed && (attackerPed->IsPlayer() || attackerPed->IsGangMember())) { + for (int i = 0; i < m_numNearPeds; ++i) { + CPed *nearPed = m_nearPeds[i]; + if (nearPed->IsPointerValid()) { + if (nearPed->CharCreatedBy == RANDOM_CHAR && nearPed != this && nearPed->m_nPedType == m_nPedType) + nearPed->m_fearFlags |= CPedType::GetFlag(attackerPed->m_nPedType); + } + } + } + } + } + + if (attackerPed && attackerPed->IsPlayer() && (attackerPed->m_nPedState == PED_CARJACK || attackerPed->bInVehicle)) { + if (!attackerPed->m_pMyVehicle || attackerPed->m_pMyVehicle->GetModelIndex() != MI_TOPFUN) { + int16 lastVehicle; + CEntity *vehicles[8]; + CWorld::FindObjectsInRange(GetPosition(), 30.0f, true, &lastVehicle, 6, vehicles, false, true, false, false, false); + + if (lastVehicle > 8) + lastVehicle = 8; + + for (int j = 0; j < lastVehicle; ++j) { + CVehicle *nearVeh = (CVehicle*) vehicles[j]; + + if (nearVeh->VehicleCreatedBy != MISSION_VEHICLE) { + CPed *nearVehDriver = nearVeh->pDriver; + + if (nearVehDriver && nearVehDriver != this && nearVehDriver->m_nPedType == m_nPedType && nearVehDriver->CharCreatedBy == RANDOM_CHAR) { + + if (nearVeh->IsVehicleNormal() && nearVeh->IsCar()) { + nearVeh->AutoPilot.m_nCruiseSpeed = GAME_SPEED_TO_CARAI_SPEED * nearVeh->pHandling->Transmission.fMaxCruiseVelocity * 0.8f; + nearVeh->AutoPilot.m_nCarMission = MISSION_RAMPLAYER_FARAWAY; + nearVeh->SetStatus(STATUS_PHYSICS); + nearVeh->AutoPilot.m_nTempAction = TEMPACT_NONE; + nearVeh->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + } + } + } + } + } + } +} + +// Some helper function which doesn't exist in og game. +inline void +SelectClosestNodeForSeek(CPed *ped, CPathNode *node, CVector2D closeDist, CVector2D farDist, CPathNode *closeNode, CPathNode *closeNode2, int runCount = 3) +{ + for (int i = 0; i < node->numLinks; i++) { + + CPathNode *testNode = &ThePaths.m_pathNodes[ThePaths.ConnectedNode(i + node->firstLink)]; + + if (testNode && testNode != closeNode && testNode != closeNode2) { + CVector2D posDiff(ped->m_vecSeekPos - testNode->GetPosition()); + float dist = posDiff.MagnitudeSqr(); + + if (farDist.MagnitudeSqr() > dist) { + + if (closeDist.MagnitudeSqr() > dist) { + ped->m_pNextPathNode = (closeNode2 ? closeNode2 : testNode); + farDist = posDiff; + } else { + ped->m_pNextPathNode = closeNode; + closeDist = posDiff; + } + } + + if (--runCount > 0) + SelectClosestNodeForSeek(ped, testNode, closeDist, farDist, closeNode, (closeNode2 ? closeNode2 : testNode), runCount); + } + } +} + +bool +CPed::FindBestCoordsFromNodes(CVector unused, CVector *bestCoords) +{ + if (m_pNextPathNode || !bUsePedNodeSeek) + return false; + + const CVector &ourPos = GetPosition(); + + int closestNodeId = ThePaths.FindNodeClosestToCoors(GetPosition(), 1, 999999.9f); + + CVector seekObjPos = m_vecSeekPos; + seekObjPos.z += 1.0f; + + if (CWorld::GetIsLineOfSightClear(ourPos, seekObjPos, true, false, false, true, false, false, false)) + return false; + + m_pNextPathNode = nil; + + CVector2D seekPosDist (m_vecSeekPos - ourPos); + + CPathNode *closestNode = &ThePaths.m_pathNodes[closestNodeId]; + CVector2D closeDist(m_vecSeekPos - closestNode->GetPosition()); + + SelectClosestNodeForSeek(this, closestNode, closeDist, seekPosDist, closestNode, nil); + + if (m_pNextPathNode) { + + // Function above decided that directly going to next node makes more sense then seeking the object. + CVector correctedCoords = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); + + if ((correctedCoords - ourPos).MagnitudeSqr2D() < seekPosDist.MagnitudeSqr()) { + *bestCoords = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); + return true; + } + m_pNextPathNode = nil; + } + + return false; +} + +bool +CPed::DuckAndCover(void) +{ + if (!m_pedInObjective || CTimer::GetTimeInMilliseconds() <= m_duckAndCoverTimer) + return false; + + if (bKindaStayInSamePlace){ + + if (CTimer::GetTimeInMilliseconds() <= m_leaveCarTimer) { + if (!m_pLookTarget || m_pLookTarget != m_pedInObjective) { + m_pLookTarget = m_pedInObjective; + m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); + } + if (!bIsAimingGun) + SetAimFlag(m_pedInObjective); + + } else { + bKindaStayInSamePlace = false; + if (bIsDucking) + ClearDuck(true); + + bCrouchWhenShooting = false; + bDuckAndCover = false; + m_headingRate = 10.0f; + m_duckAndCoverTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(20000,30000); + if (m_pSeekTarget && m_pSeekTarget->IsVehicle()) + ((CVehicle*)m_pSeekTarget)->m_numPedsUseItAsCover--; + } + return false; + } + + int16 lastVehicle = 0; + CEntity* vehicles[8]; + + bool justDucked = false; + CVehicle *foundVeh = nil; + float maxDist = 225.0f; + if (bIsDucking) + ClearDuck(true); + + bCrouchWhenShooting = false; + bool duckingWithoutVeh = false; + if (CTimer::GetTimeInMilliseconds() > m_leaveCarTimer) { + + for(int i = 0; i < 6; i++) { + CPlayerPed *player = (CPlayerPed*)m_pedInObjective; + + if (player->m_pPedAtSafePos[i] == this) { + duckingWithoutVeh = true; + CVector &safePos = player->m_vecSafePos[i]; + bool notRunningToSafePos = false; + + if (m_vecSeekPos.x != safePos.x && m_vecSeekPos.y != safePos.y && m_vecSeekPos.z != safePos.z) + notRunningToSafePos = true; + + if (!notRunningToSafePos) { + CVector target = player->m_vecSafePos[i]; + SetSeek(target, 1.0f); + duckingWithoutVeh = true; + m_attackTimer = CTimer::GetTimeInMilliseconds() + 6000; + bDuckAndCover = true; + } + break; + } + } + if (!duckingWithoutVeh) { + for (int i = 0; i < 6; i++) { + CPlayerPed* player = (CPlayerPed*)m_pedInObjective; + if (!player->m_pPedAtSafePos[i] && player->m_vecSafePos[i].x != 0.0f) { + player->m_pPedAtSafePos[i] = this; + CVector target = player->m_vecSafePos[i]; + SetSeek(target, 1.0f); + m_headingRate = 15.0f; + ClearPointGunAt(); + duckingWithoutVeh = 1; + bIsRunning = true; + bDuckAndCover = true; + justDucked = true; + m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 500; + break; + } + } + } + if (!duckingWithoutVeh) { + CVector pos = GetPosition(); + CWorld::FindObjectsInRange(pos, CHECK_NEARBY_THINGS_MAX_DIST, true, &lastVehicle, 6, vehicles, false, true, false, false, false); + } + + for (int i = 0; i < lastVehicle; i++) { + CVehicle *veh = (CVehicle*) vehicles[i]; + if (veh->IsCar() && veh->m_vecMoveSpeed.Magnitude() <= 0.02f + && !veh->bIsBus && !veh->bIsVan && !veh->bIsBig + && veh->m_numPedsUseItAsCover < 3) { + + float dist = (GetPosition() - veh->GetPosition()).MagnitudeSqr(); + if (dist < maxDist) { + maxDist = dist; + foundVeh = veh; + } + } + } + if (foundVeh) { + // Unused. + // CVector lfWheelPos, rfWheelPos; + // foundVeh->GetComponentWorldPosition(CAR_WHEEL_RF, rfWheelPos); + // foundVeh->GetComponentWorldPosition(CAR_WHEEL_LF, lfWheelPos); + CVector rightSide, leftSide; + + // 3 persons can use the car as cover. Found the correct position for us. + if (foundVeh->m_numPedsUseItAsCover == 2) { + rightSide = CVector(1.5f, -0.5f, 0.0f); + leftSide = CVector(-1.5f, -0.5f, 0.0f); + } else if (foundVeh->m_numPedsUseItAsCover == 1) { + rightSide = CVector(1.5f, 0.5f, 0.0f); + leftSide = CVector(-1.5f, 0.5f, 0.0f); + } else if (foundVeh->m_numPedsUseItAsCover == 0) { + rightSide = CVector(1.5f, 0.0f, 0.0f); + leftSide = CVector(-1.5f, 0.0f, 0.0f); + } + + CMatrix vehMatrix(foundVeh->GetMatrix()); + CVector duckAtRightSide = Multiply3x3(vehMatrix, rightSide) + foundVeh->GetPosition(); + + CVector duckAtLeftSide = Multiply3x3(vehMatrix, leftSide) + foundVeh->GetPosition(); + + CVector distWithPedRightSide = m_pedInObjective->GetPosition() - duckAtRightSide; + CVector distWithPedLeftSide = m_pedInObjective->GetPosition() - duckAtLeftSide; + + CVector duckPos; + if (distWithPedRightSide.MagnitudeSqr() <= distWithPedLeftSide.MagnitudeSqr()) + duckPos = duckAtLeftSide; + else + duckPos = duckAtRightSide; + + if (CWorld::TestSphereAgainstWorld(duckPos, 0.5f, nil, true, true, true, false, false, false)) { + SetSeek(duckPos, 1.0f); + m_headingRate = 15.0f; + bIsRunning = true; + bDuckAndCover = true; + justDucked = true; + m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 500; + if (foundVeh->bIsLawEnforcer) { + m_carInObjective = foundVeh; + m_carInObjective->RegisterReference((CEntity**)&m_carInObjective); + } + m_pSeekTarget = foundVeh; + m_pSeekTarget->RegisterReference((CEntity**)&m_pSeekTarget); + ClearPointGunAt(); + } else { + m_duckAndCoverTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(10000, 15000); + bDuckAndCover = false; + } + } else if (!duckingWithoutVeh) { + bDuckAndCover = false; + } + } + + if (!justDucked && !bDuckAndCover) + return false; + + if (!Seek()) { + if (m_nMoveState == PEDMOVE_STILL) { + bDuckAndCover = false; + return false; + } else + return true; + } + + bKindaStayInSamePlace = true; + bDuckAndCover = false; + m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f); + if (m_pSeekTarget && m_pSeekTarget->IsVehicle()) + ((CVehicle*)m_pSeekTarget)->m_numPedsUseItAsCover++; + + SetIdle(); + SetMoveState(PEDMOVE_STILL); + SetMoveAnim(); + if (!m_pLookTarget || m_pLookTarget != m_pedInObjective) { + m_pLookTarget = m_pedInObjective; + m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); + } + + m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(3000, 6000); + bCrouchWhenShooting = true; + SetDuck(CGeneral::GetRandomNumberInRange(2000, 5000), true); + return false; +} + +CVector +CPed::GetPositionToOpenCarDoor(CVehicle *veh, uint32 component, float offset) +{ + CVector doorPos; + CMatrix vehMat(veh->GetMatrix()); + + if (veh->IsBike()) { + CVehicleModelInfo* vehModel = (CVehicleModelInfo*)CModelInfo::GetModelInfo(veh->GetModelIndex()); + CVector vehDoorOffset; + CBike* bike = (CBike*)veh; + doorPos = vehModel->GetFrontSeatPosn(); + + if (component == CAR_WINDSCREEN) { +#ifdef FIX_BUGS + return bike->GetMatrix() * (doorPos + + CVector(-vecPedBikeKickAnimOffset.x, vecPedBikeKickAnimOffset.y, -vecPedBikeKickAnimOffset.z)); +#else + return bike->GetMatrix() * (doorPos + vecPedBikeKickAnimOffset); +#endif + } else { + switch (bike->m_bikeAnimType) { + case ASSOCGRP_BIKE_VESPA: + vehDoorOffset = vecPedVespaBikeJumpRhsAnimOffset; + break; + case ASSOCGRP_BIKE_HARLEY: + vehDoorOffset = vecPedHarleyBikeJumpRhsAnimOffset; + break; + case ASSOCGRP_BIKE_DIRT: + vehDoorOffset = vecPedDirtBikeJumpRhsAnimOffset; + break; + default: + vehDoorOffset = vecPedStdBikeJumpRhsAnimOffset; + break; + } + vehDoorOffset.x += offset * bike->pHandling->fSeatOffsetDistance; + if (component == CAR_DOOR_LR || component == CAR_DOOR_RR) { + doorPos = vehModel->m_positions[CAR_POS_BACKSEAT]; + } + + if (component == CAR_DOOR_LR || component == CAR_DOOR_LF) + vehDoorOffset.x *= -1.f; + + CVector correctedPos; + bike->GetCorrectedWorldDoorPosition(correctedPos, vehDoorOffset, doorPos); + return correctedPos; + } + } + doorPos = Multiply3x3(vehMat, GetLocalPositionToOpenCarDoor(veh, component, offset)); + + return veh->GetPosition() + doorPos; +} + +CVector +CPed::GetLocalPositionToOpenCarDoor(CVehicle *veh, uint32 component, float seatPosMult) +{ + CVehicleModelInfo *vehModel; + CVector vehDoorPos; + CVector vehDoorOffset; + float seatOffset; + + vehModel = (CVehicleModelInfo *)CModelInfo::GetModelInfo(veh->GetModelIndex()); + + if (veh->IsBike()) { + CBike *bike = (CBike*)veh; + vehDoorPos = vehModel->GetFrontSeatPosn(); + + if (component == CAR_WINDSCREEN) { + return bike->GetMatrix() * (vehDoorPos + vecPedBikeKickAnimOffset); + } else { + switch (bike->m_bikeAnimType) { + case ASSOCGRP_BIKE_VESPA: + vehDoorOffset = vecPedVespaBikeJumpRhsAnimOffset; + break; + case ASSOCGRP_BIKE_HARLEY: + vehDoorOffset = vecPedHarleyBikeJumpRhsAnimOffset; + break; + case ASSOCGRP_BIKE_DIRT: + vehDoorOffset = vecPedDirtBikeJumpRhsAnimOffset; + break; + default: + vehDoorOffset = vecPedStdBikeJumpRhsAnimOffset; + break; + } + float xOffsetFromAnim = vehDoorOffset.x + seatPosMult * bike->pHandling->fSeatOffsetDistance; + if (component == CAR_DOOR_LR || component == CAR_DOOR_RR) { + vehDoorPos = vehModel->m_positions[CAR_POS_BACKSEAT]; + } + + if (component == CAR_DOOR_LR || component == CAR_DOOR_LF) + xOffsetFromAnim *= -1.f; + + return bike->GetMatrix() * (vehDoorPos + CVector(xOffsetFromAnim, vehDoorOffset.y, vehDoorOffset.z)); + } + } else { + if (veh->bIsVan && (component == CAR_DOOR_LR || component == CAR_DOOR_RR)) { + seatOffset = 0.0f; + vehDoorOffset = vecPedVanRearDoorAnimOffset; + } else { + seatOffset = veh->pHandling->fSeatOffsetDistance * seatPosMult; + if (veh->bLowVehicle) { + vehDoorOffset = vecPedCarDoorLoAnimOffset; + } else { + vehDoorOffset = vecPedCarDoorAnimOffset; + } + } + + switch (component) { + case CAR_DOOR_RF: + vehDoorPos = vehModel->GetFrontSeatPosn(); + vehDoorPos.x += seatOffset; + vehDoorOffset.x = -vehDoorOffset.x; + break; + + case CAR_DOOR_RR: + vehDoorPos = vehModel->m_positions[CAR_POS_BACKSEAT]; + vehDoorPos.x += seatOffset; + vehDoorOffset.x = -vehDoorOffset.x; + break; + + case CAR_DOOR_LF: + vehDoorPos = vehModel->GetFrontSeatPosn(); + vehDoorPos.x = -(vehDoorPos.x + seatOffset); + break; + + case CAR_DOOR_LR: + vehDoorPos = vehModel->m_positions[CAR_POS_BACKSEAT]; + vehDoorPos.x = -(vehDoorPos.x + seatOffset); + break; + + default: + vehDoorPos = vehModel->GetFrontSeatPosn(); + vehDoorOffset = CVector(0.0f, 0.0f, 0.0f); + } + return vehDoorPos - vehDoorOffset; + } +} + +// TODO: what is this parameter for? +void +CPed::SetDuck(uint32 time, bool sth) +{ + if (bIsDucking || CTimer::GetTimeInMilliseconds() <= m_duckTimer && !sth) { + if (sth && CTimer::GetTimeInMilliseconds() + time > m_duckTimer) + m_duckTimer = CTimer::GetTimeInMilliseconds() + time; + return; + } + + CAnimBlendAssociation *duckAssoc; + if (bCrouchWhenShooting) { + duckAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_DUCK_WEAPON, 4.0f); + duckAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; + bIsDucking = true; + m_duckTimer = CTimer::GetTimeInMilliseconds() + time; + } else { + CAnimBlendAssociation *duckAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_DOWN); + if (!duckAssoc || duckAssoc->blendDelta < 0.0f) { + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_DUCK_DOWN, 4.0f); + bIsDucking = true; + m_duckTimer = CTimer::GetTimeInMilliseconds() + time; + } + } +} + +void +CPed::Duck(void) +{ + if (CTimer::GetTimeInMilliseconds() > m_duckTimer) + ClearDuck(); + else if (bIsDucking && bCrouchWhenShooting) { + CWeaponInfo *weapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + CAnimBlendAssociation *crouchAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_WEAPON); + if (!crouchAnim) { + if(GetCrouchFireAnim(weapon)) + crouchAnim = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(weapon)); + } + if (!crouchAnim) { + if(GetCrouchReloadAnim(weapon)) + crouchAnim = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchReloadAnim(weapon)); + } + if (!crouchAnim) { + bIsDucking = false; +#if defined FIX_BUGS || defined FREE_CAM + if (IsPlayer()) + bCrouchWhenShooting = false; +#endif + } + } +} + +void +CPed::ClearDuck(bool clearTimer) +{ + CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_DOWN); + if (!animAssoc) { + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_LOW); + } + if (!animAssoc) { + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_WEAPON); + } + + if (animAssoc) { + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + animAssoc->blendDelta = -4.0f; + } + bIsDucking = false; + + if (clearTimer) { + m_duckTimer = 0; + } +} + +void +CPed::InformMyGangOfAttack(CEntity *attacker) +{ + CPed *attackerPed; + + if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS) + return; + + if (attacker->IsPed()) { + attackerPed = (CPed*)attacker; + } else { + if (!attacker->IsVehicle()) + return; + + attackerPed = ((CVehicle*)attacker)->pDriver; + if (!attackerPed) + return; + } + + if (attackerPed->m_nPedType == PEDTYPE_COP) + return; + + for (int i = 0; i < m_numNearPeds; i++) { + CPed *nearPed = m_nearPeds[i]; + if (nearPed && nearPed != this) { + CPed *leader = nearPed->m_leader; + if (leader && leader == this && nearPed->m_pedStats->m_fear < nearPed->m_pedStats->m_temper) { + nearPed->SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, attackerPed); + nearPed->SetObjectiveTimer(30000); + } + } + } +} + +void +CPed::PedAnimDoorCloseRollingCB(CAnimBlendAssociation* animAssoc, void* arg) +{ + CPed* ped = (CPed*)arg; + + CAutomobile* veh = (CAutomobile*)(ped->m_pMyVehicle); + + if (animAssoc) + animAssoc->blendDelta = -1000.0f; + + if (veh->bLowVehicle) { + veh->ProcessOpenDoor(CAR_DOOR_LF, ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LO_LHS, 1.0f); + } else { + veh->ProcessOpenDoor(CAR_DOOR_LF, ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LHS, 1.0f); + } + + veh->m_nGettingOutFlags &= ~CAR_DOOR_FLAG_LF; + + if (veh->Damage.GetDoorStatus(DOOR_FRONT_LEFT) == DOOR_STATUS_SWINGING) + veh->Damage.SetDoorStatus(DOOR_FRONT_LEFT, DOOR_STATUS_OK); +} + +void +CPed::SetSeekBoatPosition(CVehicle *boat) +{ + if (m_nPedState == PED_SEEK_IN_BOAT || boat->pDriver || !IsPedInControl()) + return; + + SetStoredState(); + m_carInObjective = boat; + m_carInObjective->RegisterReference((CEntity **) &m_carInObjective); + m_pMyVehicle = boat; + m_pMyVehicle->RegisterReference((CEntity **) &m_pMyVehicle); + m_distanceToCountSeekDone = 0.5f; + SetPedState(PED_SEEK_IN_BOAT); +} + +void +CPed::SeekBoatPosition(void) +{ + if (m_carInObjective && !m_carInObjective->pDriver) { + CVehicleModelInfo *boatModel = m_carInObjective->GetModelInfo(); + + CVector enterOffset; + enterOffset = boatModel->GetFrontSeatPosn(); + enterOffset.x = 0.0f; + CMatrix boatMat(m_carInObjective->GetMatrix()); + SetMoveState(PEDMOVE_WALK); + m_vecSeekPos = boatMat * enterOffset; + if (Seek()) { + // We arrived to the boat + m_vehDoor = 0; + SetEnterCar(m_carInObjective, 0); + } + } else + RestorePreviousState(); +} + +bool +CPed::IsRoomToBeCarJacked(void) +{ + if (!m_pMyVehicle) + return false; + + CVector offset; + if (m_pMyVehicle->IsBike()) { + offset = vecPedStdBikeJumpRhsAnimOffset; + } else if (m_pMyVehicle->bLowVehicle || m_nPedType == PEDTYPE_COP) { + offset = vecPedDraggedOutCarAnimOffset; + } else { + offset = vecPedQuickDraggedOutCarAnimOffset; + } + + offset.z = 0.0f; + if (m_pMyVehicle->IsRoomForPedToLeaveCar(CAR_DOOR_LF, &offset)) { + return true; + } + + return false; +} + +void +CPed::AddInCarAnims(CVehicle* car, bool isDriver) +{ + if (car->IsBoat()) { + if (car->pHandling->Flags & HANDLING_SIT_IN_BOAT) { + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_SIT, 100.0f); + } else { + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_BOAT_DRIVE, 100.0f); + } + } else if (car->IsBike()) { + if (isDriver) { + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ((CBike*)car)->m_bikeAnimType, ANIM_BIKE_RIDE, 100.0f); + } else { + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ((CBike*)car)->m_bikeAnimType, ANIM_BIKE_RIDE_P, 100.0f); + } + } else { + if (isDriver) { + if (car->bLowVehicle) { + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_SIT_LO, 100.0f); + } else { + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_SIT, 100.0f); + } + } else { + if (car->bLowVehicle) { + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_SIT_P_LO, 100.0f); + } else { + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_SIT_P, 100.0f); + } + } + } + + StopNonPartialAnims(); +} + +void +CPed::RemoveDrivebyAnims() +{ + CAnimBlendAssociation *animAssoc; + + AnimationId LeftAnim = ANIM_STD_CAR_DRIVEBY_LEFT; + AnimationId RightAnim = ANIM_STD_CAR_DRIVEBY_RIGHT; + + if (m_pMyVehicle->pHandling->Flags & HANDLING_IS_BIKE) { + LeftAnim = ANIM_BIKE_DRIVEBY_RHS; + RightAnim = ANIM_BIKE_DRIVEBY_LHS; + } else if (m_pMyVehicle->bLowVehicle) { + LeftAnim = ANIM_STD_CAR_DRIVEBY_LEFT_LO; + RightAnim = ANIM_STD_CAR_DRIVEBY_RIGHT_LO; + } + + animAssoc = RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ANIM_BIKE_DRIVEBY_RHS); + if (animAssoc) + animAssoc->blendDelta = -1000.0f; + animAssoc = RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ANIM_BIKE_DRIVEBY_LHS); + if (animAssoc) + animAssoc->blendDelta = -1000.0f; + animAssoc = RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ANIM_BIKE_DRIVEBY_FORWARD); + if (animAssoc) + animAssoc->blendDelta = -1000.0f; +} + +void +CPed::RemoveInCarAnims(void) +{ + CAnimBlendAssociation* assoc; + + for (assoc = RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_DRIVING); assoc; + assoc = RpAnimBlendGetNextAssociation(assoc, ASSOC_DRIVING)) { + assoc->flags |= ASSOC_DELETEFADEDOUT; + assoc->blendDelta = -1000.0f; + } +} + +bool +CPed::PositionPedOutOfCollision(void) +{ + CVehicle *veh = m_pMyVehicle; + if (!veh) + return false; + + if (bDonePositionOutOfCollision) + return true; + + bool foundAPos = false; + CColModel *vehCol = veh->GetColModel(); + CVector vehPos = veh->GetPosition(); + CVector ourPos = GetPosition(); + CVector newPos = ourPos; + CWorld::pIgnoreEntity = veh; + bUsesCollision = false; + bJustCheckCollision = true; + m_vecMoveSpeed = CVector(0.f, 0.f, 0.f); + if (veh->IsOnItsSide()) { + // Top of the veh. + newPos = vehPos; + newPos.z = FEET_OFFSET + vehCol->boundingBox.max.x + vehPos.z; + GetMatrix().SetTranslate(newPos); + if (!CheckCollision()) { + if (CWorld::GetIsLineOfSightClear(vehPos, newPos, true, false, false, true, false, false, false)) + foundAPos = true; + } + + } else if (m_vehDoor != 0) { + // Try the normal way + CVector pos = GetPositionToOpenCarDoor(m_pMyVehicle, m_vehDoor); + newPos = pos; + GetMatrix().SetTranslate(newPos); + if (!CheckCollision()) { + if (CWorld::GetIsLineOfSightClear(vehPos, newPos, true, false, false, true, false, false, false)) + foundAPos = true; + } + } + + float vehRelativeExitX = vehCol->boundingBox.min.x - 0.355f; + if (m_vehDoor == CAR_DOOR_RF || m_vehDoor == CAR_DOOR_RR) + vehRelativeExitX = 0.355f + vehCol->boundingBox.max.x; + + if (!foundAPos) { + // Check sides of veh., respective to seat column-veh. center difference(why?) + float exitOffset = vehRelativeExitX - DotProduct(ourPos - vehPos, veh->GetRight()); + newPos = exitOffset * veh->GetRight() + ourPos; + GetMatrix().SetTranslate(newPos); + if (!CheckCollision()) { + if (CWorld::GetIsLineOfSightClear(vehPos, newPos, true, false, false, true, false, false, false)) + foundAPos = true; + } + } + if (!foundAPos) { + // Iterate through sections of veh. length + static offset on X + float minY = vehCol->boundingBox.min.y; + float ySection = (vehCol->boundingBox.max.y - minY) / 3.f; + for (int i = 0; i < 4; i++) { + float fwdMult = i * ySection + minY; + newPos = vehRelativeExitX * veh->GetRight() + fwdMult * veh->GetForward() + vehPos; + GetMatrix().SetTranslate(newPos); + if (!CheckCollision()) { + if (CWorld::GetIsLineOfSightClear(vehPos, newPos, true, false, false, true, false, false, false)) { + foundAPos = true; + break; + } + } + } + } + if (!foundAPos) { + // Back of veh. + newPos = (vehCol->boundingBox.min.y - 0.355f) * veh->GetForward() + vehPos; + GetMatrix().SetTranslate(newPos); + if (!CheckCollision()) { + if (CWorld::GetIsLineOfSightClear(vehPos, newPos, true, false, false, true, false, false, false)) + foundAPos = true; + } + } + if (!foundAPos) { + // Front of veh. + newPos = (0.355f + vehCol->boundingBox.max.y) * veh->GetForward() + vehPos; + GetMatrix().SetTranslate(newPos); + if (!CheckCollision()) { + if (CWorld::GetIsLineOfSightClear(vehPos, newPos, true, false, false, true, false, false, false)) + foundAPos = true; + } + } + if (!foundAPos) { + // Opposite X side + back + newPos = vehCol->boundingBox.min.y * veh->GetForward() + vehPos - vehRelativeExitX * veh->GetRight(); + GetMatrix().SetTranslate(newPos); + if (!CheckCollision()) { + if (CWorld::GetIsLineOfSightClear(vehPos, newPos, true, false, false, true, false, false, false)) + foundAPos = true; + } + } + if (!foundAPos) { + // Opposite X side + front + newPos = vehCol->boundingBox.max.y * veh->GetForward() + vehPos - vehRelativeExitX * veh->GetRight(); + GetMatrix().SetTranslate(newPos); + if (!CheckCollision()) { + if (CWorld::GetIsLineOfSightClear(vehPos, newPos, true, false, false, true, false, false, false)) + foundAPos = true; + } + } + if (!foundAPos) { + // Top of veh. + if (veh->m_vehType == 0) { + newPos = vehCol->boundingBox.max.z * veh->GetUp() + vehPos; + newPos.z += FEET_OFFSET; + GetMatrix().SetTranslate(newPos); + if (!CheckCollision()) { + if (CWorld::GetIsLineOfSightClear(vehPos, newPos, true, false, false, true, false, false, false)) + foundAPos = true; + } + } + } + m_vecMoveSpeed = CVector(0.f, 0.f, 0.f); + m_vecTurnSpeed = CVector(0.f, 0.f, 0.f); + veh->m_vecMoveSpeed = CVector(0.f, 0.f, 0.f); + veh->m_vecTurnSpeed = CVector(0.f, 0.f, 0.f); + CWorld::pIgnoreEntity = nil; + bUsesCollision = true; + bJustCheckCollision = false; + bDonePositionOutOfCollision = true; + if (foundAPos) + return true; + int foundNode = ThePaths.FindNodeClosestToCoors(vehPos, PATH_PED, 999999.9f, true, false, false, false); + if (foundNode < 0) + return false; + newPos = ThePaths.m_pathNodes[foundNode].GetPosition(); + CPedPlacement::FindZCoorForPed(&newPos); + GetMatrix().SetTranslate(newPos); + SetHeading(m_pMyVehicle->GetForward().Heading()); + return true; +} + +// "Any" means he shouldn't have to be in vehicle. +bool +CPed::PositionAnyPedOutOfCollision(void) +{ + CVehicle *veh; + CVector posNearVeh; + CVector posSomewhereClose; + bool putNearVeh = false; + bool putSomewhereClose = false; + int smallestDistNearVeh = 999; + int smallestDistSomewhereClose = 999; + + CVector potentialPos; + potentialPos.y = GetPosition().y - 3.5f; + potentialPos.z = GetPosition().z; + + for (int yTry = 0; yTry < 15; yTry++) { + potentialPos.x = GetPosition().x - 3.5f; + + for (int xTry = 0; xTry < 15; xTry++) { + CPedPlacement::FindZCoorForPed(&potentialPos); + if (!CWorld::TestSphereAgainstWorld(potentialPos, 0.6f, this, true, false, false, true, false, false)) { + float potentialChangeSqr = (potentialPos - GetPosition()).MagnitudeSqr(); + veh = (CVehicle*)CWorld::TestSphereAgainstWorld(potentialPos, 0.6f, this, false, true, false, false, false, false); + if (veh) { + if (potentialChangeSqr < smallestDistNearVeh) { + posNearVeh = potentialPos; + putNearVeh = true; + smallestDistNearVeh = potentialChangeSqr; + } + } else if (potentialChangeSqr < smallestDistSomewhereClose) { + smallestDistSomewhereClose = potentialChangeSqr; + posSomewhereClose = potentialPos; + putSomewhereClose = true; + } + } + potentialPos.x += 0.5f; + } + potentialPos.y += 0.5f; + } + + if (!putSomewhereClose && !putNearVeh) + return false; + + // We refrain from using posNearVeh, probably because of it may be top of the vehicle. + if (putSomewhereClose) { + SetPosition(posSomewhereClose); + } else { + CVector vehSize = veh->GetModelInfo()->GetColModel()->boundingBox.max; + posNearVeh.z += vehSize.z; + SetPosition(posNearVeh); + } + return true; +} + +bool +CPed::WarpPedToNearLeaderOffScreen(void) +{ + bool teleported = false; + if (GetIsOnScreen() || m_leaveCarTimer > CTimer::GetTimeInMilliseconds()) + return false; + + CVector warpToPos = m_leader->GetPosition(); + CVector distVec = warpToPos - GetPosition(); + float halfOfDist = distVec.Magnitude() * 0.5f; + CVector halfNormalizedDist = distVec / halfOfDist; + + CVector appropriatePos = GetPosition(); + int tryCount = Min(10, (int)halfOfDist); + for (int i = 0; i < tryCount; ++i) { + appropriatePos += halfNormalizedDist; + CVector zCorrectedPos = appropriatePos; + CPedPlacement::FindZCoorForPed(&zCorrectedPos); + + if (Abs(zCorrectedPos.z - warpToPos.z) < 3.0f || Abs(zCorrectedPos.z - appropriatePos.z) < 3.0f) { + appropriatePos.z = zCorrectedPos.z; + if (!TheCamera.IsSphereVisible(appropriatePos, 0.6f) + && CWorld::GetIsLineOfSightClear(appropriatePos, warpToPos, true, true, false, true, false, false, false) + && !CWorld::TestSphereAgainstWorld(appropriatePos, 0.6f, this, true, true, false, true, false, false)) { + teleported = true; + Teleport(appropriatePos); + } + } + } + m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 3000; + return teleported; +} + +bool +CPed::WarpPedToNearEntityOffScreen(CEntity *warpTo) +{ + bool teleported = false; + if (GetIsOnScreen() || m_leaveCarTimer > CTimer::GetTimeInMilliseconds()) + return false; + + CVector warpToPos = warpTo->GetPosition(); + CVector distVec = warpToPos - GetPosition(); + float halfOfDist = distVec.Magnitude() * 0.5f; + CVector halfNormalizedDist = distVec / halfOfDist; + + CVector appropriatePos = GetPosition(); + int tryCount = Min(10, (int)halfOfDist); + for (int i = 0; i < tryCount; ++i) { + appropriatePos += halfNormalizedDist; + CVector zCorrectedPos = appropriatePos; + CPedPlacement::FindZCoorForPed(&zCorrectedPos); + + if (Abs(zCorrectedPos.z - warpToPos.z) < 3.0f || Abs(zCorrectedPos.z - appropriatePos.z) < 3.0f) { + appropriatePos.z = zCorrectedPos.z; + if (!TheCamera.IsSphereVisible(appropriatePos, 0.6f) + && CWorld::GetIsLineOfSightClear(appropriatePos, warpToPos, true, true, false, true, false, false, false) + && !CWorld::TestSphereAgainstWorld(appropriatePos, 0.6f, this, true, true, false, true, false, false)) { + teleported = true; + Teleport(appropriatePos); + } + } + } + m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 3000; + return teleported; +} + +int32 +CPed::KillCharOnFootArmed(CVector &ourPos, CVector &targetPos, CVector &distWithTarget) +{ + bool killPlayerInNoPoliceZone = false; + if (m_pedInObjective->IsPlayer() && CCullZones::NoPolice()) + killPlayerInNoPoliceZone = true; + + if (!bNotAllowedToDuck || killPlayerInNoPoliceZone) { + if (m_nPedType == PEDTYPE_COP && !m_pedInObjective->GetWeapon()->IsTypeMelee()) + bNotAllowedToDuck = true; + } else { + if (!m_pedInObjective->bInVehicle) { + if (m_pedInObjective->GetWeapon()->IsTypeMelee()) { + bNotAllowedToDuck = false; + bCrouchWhenShooting = false; + } else if (DuckAndCover()) { + return CANT_ATTACK; + } + } else { + bNotAllowedToDuck = false; + bCrouchWhenShooting = false; + } + } + if (m_leaveCarTimer > CTimer::GetTimeInMilliseconds() && !bKindaStayInSamePlace) { + SetMoveState(PEDMOVE_STILL); + return CANT_ATTACK; + } + if (m_pedInObjective->IsPlayer()) { + CPlayerPed *player = FindPlayerPed(); + if (m_nPedType == PEDTYPE_COP && player->m_pWanted->m_bIgnoredByCops + || player->m_pWanted->m_bIgnoredByEveryone + || m_pedInObjective->bIsInWater + || m_pedInObjective->m_nPedState == PED_ARRESTED) { + + if (m_nPedState != PED_ARREST_PLAYER) + SetIdle(); + + return CANT_ATTACK; + } + } + if (m_pedInObjective->IsPlayer() && m_nPedType != PEDTYPE_COP + && CharCreatedBy != MISSION_CHAR && FindPlayerPed()->m_pWanted->m_CurrentCops != 0) { + SetObjective(OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE); + + return CANT_ATTACK; + } + if (m_pedInObjective->m_fHealth <= 0.0f) { + bObjectiveCompleted = true; + bScriptObjectiveCompleted = true; + return CANT_ATTACK; + } + CWeaponInfo *wepInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + float wepRange = wepInfo->m_fRange; + float wepRangeAdjusted = wepRange / 3.f; + + float distWithTargetSc = distWithTarget.Magnitude(); + if (m_pedInObjective->bInVehicle && m_pedInObjective->m_nPedState != PED_DRAG_FROM_CAR) { + CVehicle *vehOfTarget = m_pedInObjective->m_pMyVehicle; + if (vehOfTarget->bIsInWater || vehOfTarget->GetStatus() == STATUS_PLAYER_DISABLED + || m_pedInObjective->IsPlayer() && CPad::GetPad(0)->ArePlayerControlsDisabled()) { + SetIdle(); + return WATCH_UNTIL_HE_DISAPPEARS; + } + SetLookFlag(vehOfTarget, false); + if (m_nMoveState == PEDMOVE_STILL && IsPedInControl()) + TurnBody(); + + if (m_nPedState != PED_CARJACK) { + if (m_pedInObjective->m_nPedState != PED_ARRESTED) { + if (m_attackTimer < CTimer::GetTimeInMilliseconds() && distWithTargetSc < wepRange && distWithTargetSc > 3.0f) { + + SetAttack(vehOfTarget); + SetWeaponLockOnTarget(vehOfTarget); + SetShootTimer(CGeneral::GetRandomNumberInRange(500, 2000)); + + CVector2D dirVehGoing = vehOfTarget->m_vecMoveSpeed; + if (dirVehGoing.Magnitude() > 0.2f) { + CVector2D vehDist = GetPosition() - vehOfTarget->GetPosition(); + vehDist.Normalise(); + dirVehGoing.Normalise(); + if (DotProduct2D(vehDist, dirVehGoing) > 0.8f) { + SetAttackTimer(CGeneral::GetRandomNumberInRange(200, 500)); + SetMoveState(PEDMOVE_STILL); + } + return ATTACK_IN_PROGRESS; + } + if (distWithTargetSc <= m_distanceToCountSeekDone) { + SetAttackTimer(CGeneral::GetRandomNumberInRange(200, 500)); + SetMoveState(PEDMOVE_STILL); + } else { + SetAttackTimer(CGeneral::GetRandomNumberInRange(2000, 5000)); + } + return ATTACK_IN_PROGRESS; + } else if (m_nPedState != PED_ATTACK && !bKindaStayInSamePlace && !killPlayerInNoPoliceZone) { + if (vehOfTarget) { + if (m_nPedType == PEDTYPE_COP || vehOfTarget->bIsBus) { + GoToNearestDoor(vehOfTarget); + } else { + m_vehDoor = 0; + if (m_pedInObjective == vehOfTarget->pDriver || vehOfTarget->bIsBus) { + m_vehDoor = CAR_DOOR_LF; + } else if (m_pedInObjective == vehOfTarget->pPassengers[0]) { + m_vehDoor = CAR_DOOR_RF; + } else if (m_pedInObjective == vehOfTarget->pPassengers[1]) { + m_vehDoor = CAR_DOOR_LR; + } else if (m_pedInObjective == vehOfTarget->pPassengers[2]) { + m_vehDoor = CAR_DOOR_RR; + } + // Unused + // GetPositionToOpenCarDoor(vehOfTarget, m_vehDoor); + SetSeekCar(vehOfTarget, m_vehDoor); + SetMoveState(PEDMOVE_RUN); + } + } + } + } + } + return ATTACK_IN_PROGRESS; + } + if (m_nMoveState == PEDMOVE_STILL && IsPedInControl()) { + SetLookFlag(m_pedInObjective, false); + TurnBody(); + } + if (m_nPedType == PEDTYPE_COP && m_pedInObjective->IsPlayer()) { + float maxArrestDist = 1.5f; + if (((CCopPed*)this)->m_bDragsPlayerFromCar) { + if (m_nPedState == PED_FALL) { + maxArrestDist = 3.5f; + } else if (m_nPedState != PED_DRAG_FROM_CAR) { + ((CCopPed*)this)->m_bDragsPlayerFromCar = 0; + } + } + + if (distWithTargetSc < maxArrestDist) { + if (m_pedInObjective->m_getUpTimer > CTimer::GetTimeInMilliseconds() + || m_pedInObjective->m_nPedState == PED_DRAG_FROM_CAR) { + + ((CCopPed*)this)->SetArrestPlayer(m_pedInObjective); + return WATCH_UNTIL_HE_DISAPPEARS; + } + } + } + /* + if (distWithTargetSc > 0.1f) { + junk code + } */ + + if (m_shotTime != 0 && m_ceaseAttackTimer != 0) { + if (CTimer::GetTimeInMilliseconds() > m_ceaseAttackTimer + m_shotTime) { + ClearLookFlag(); + bObjectiveCompleted = true; + m_shotTime = 0; + return CANT_ATTACK; + } + } + + if (!bKindaStayInSamePlace && !bStopAndShoot && m_nPedState != PED_ATTACK && !bDuckAndCover && !killPlayerInNoPoliceZone) { + if (distWithTargetSc > wepRange + || m_pedInObjective->m_getUpTimer > CTimer::GetTimeInMilliseconds() + || m_pedInObjective->m_nPedState == PED_ARRESTED + || m_pedInObjective->EnteringCar() && distWithTargetSc < 3.0f) { + + if (m_pedInObjective->EnteringCar()) + wepRangeAdjusted = 2.0f; + + if (bUsePedNodeSeek) { + CVector bestCoords(0.0f, 0.0f, 0.0f); + m_vecSeekPos = m_pedInObjective->GetPosition(); + + if (!m_pNextPathNode) + FindBestCoordsFromNodes(m_vecSeekPos, &bestCoords); + + if (m_pNextPathNode) + m_vecSeekPos = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); + + SetSeek(m_vecSeekPos, m_distanceToCountSeekDone); + } else { + SetSeek(m_pedInObjective, wepRangeAdjusted); + } + if (m_pedInObjective->m_pCurrentPhysSurface && distWithTargetSc < 5.0f) { + bStopAndShoot = true; + b158_8 = true; + SetMoveState(PEDMOVE_STILL); + } else if (b158_8) { + bStopAndShoot = false; + b158_8 = false; + } + return ATTACK_IN_PROGRESS; + } + } + if (m_attackTimer < CTimer::GetTimeInMilliseconds() + && distWithTargetSc < wepRange && m_pedInObjective->m_nPedState != PED_GETUP && m_pedInObjective->m_nPedState != PED_DRAG_FROM_CAR) { + + if (bIsDucking && !bCrouchWhenShooting) { + CAnimBlendAssociation* duckAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_DOWN); + if (!duckAnim) + duckAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_LOW); + if (!duckAnim) + duckAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_WEAPON); + + if (duckAnim) { + duckAnim->flags |= ASSOC_DELETEFADEDOUT; + duckAnim->blendDelta = -4.0f; + } + bIsDucking = false; + return CANT_ATTACK; + } + bObstacleShowedUpDuringKillObjective = false; + SetAttack(m_pedInObjective); + SetWeaponLockOnTarget(m_pedInObjective); + SetShootTimer(CGeneral::GetRandomNumberInRange(600.0f, 1500.0f)); + + int time; + if (distWithTargetSc <= wepRangeAdjusted) + time = CGeneral::GetRandomNumberInRange(100.0f, 500.0f); + else + time = CGeneral::GetRandomNumberInRange(1500.0f, 3000.0f); + + SetAttackTimer(time); + } else { + if (!m_pedInObjective->m_pCurrentPhysSurface && b158_8) { + b158_8 = false; + bStopAndShoot = false; + } + + if (m_nPedState != PED_ATTACK && m_nPedState != PED_FIGHT) { + if (bNotAllowedToDuck && bKindaStayInSamePlace && !bIsDucking && bCrouchWhenShooting) { + SetDuck(CGeneral::GetRandomNumberInRange(1000, 5000), false); + return CANT_ATTACK; + } + if (bObstacleShowedUpDuringKillObjective) { + if (m_nPedType == PEDTYPE_COP) { + if (GetWeapon()->m_eWeaponType > WEAPONTYPE_COLT45 + || m_fleeFrom && m_fleeFrom->IsObject()) { + wepRangeAdjusted = 6.0f; + } else if (m_fleeFrom && m_fleeFrom->IsVehicle()) { + wepRangeAdjusted = 4.0f; + } else { + wepRangeAdjusted = 2.0f; + } + } else { + wepRangeAdjusted = 2.0f; + } + } + if (distWithTargetSc <= wepRangeAdjusted) { + SetMoveState(PEDMOVE_STILL); + bIsPointingGunAt = true; + if (m_nPedState != PED_AIM_GUN && !bDuckAndCover) { + m_attackTimer = CTimer::GetTimeInMilliseconds(); + SetIdle(); + } + } else { + if (m_nPedState != PED_SEEK_ENTITY && m_nPedState != PED_SEEK_POS + && !bStopAndShoot && !killPlayerInNoPoliceZone && !bKindaStayInSamePlace) { + Say(SOUND_PED_ATTACK); + SetSeek(m_pedInObjective, wepRangeAdjusted); + bIsRunning = true; + if (m_nPedType == PEDTYPE_COP && FindPlayerPed()->m_pWanted->m_CurrentCops > 1) { + if (CGeneral::GetRandomNumber() & 1) + Say(SOUND_PED_GUNAIMEDAT3); + else + Say(SOUND_PED_GUNAIMEDAT2); + } + } + } + } + } + + if (CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) + m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(m_pedInObjective->GetPosition().x, m_pedInObjective->GetPosition().y, GetPosition().x, GetPosition().y); + + return ATTACK_IN_PROGRESS; +} + +int32 +CPed::KillCharOnFootMelee(CVector &ourPos, CVector &targetPos, CVector &distWithTarget) +{ + bool killPlayerInNoPoliceZone = false; + float distWithTargetSc = distWithTarget.Magnitude(); + CPlayerPed *victimPlayer = nil; + if (m_pedInObjective->IsPlayer()) + victimPlayer = (CPlayerPed*)m_pedInObjective; + + if (victimPlayer) { + if (CCullZones::NoPolice() + || m_pedInObjective->m_pCurrentPhysSurface + && m_pedInObjective->m_pCurrentPhysSurface != m_pCurrentPhysSurface + && distWithTargetSc < 5.f) { + + if (victimPlayer && victimPlayer->m_bSpeedTimerFlag && (IsGangMember() || m_nPedType == PEDTYPE_COP) + && CharCreatedBy != MISSION_CHAR) { + GiveWeapon(WEAPONTYPE_COLT45, 1000, 1); + SetCurrentWeapon(WEAPONTYPE_COLT45); + SetMoveState(PEDMOVE_STILL); + bStopAndShoot = true; + b158_8 = true; + return CANT_ATTACK; + } + killPlayerInNoPoliceZone = true; + } + } + bNotAllowedToDuck = false; + bStopAndShoot = false; + b158_8 = false; + if (m_leaveCarTimer > CTimer::GetTimeInMilliseconds() && !bKindaStayInSamePlace) { + SetMoveState(PEDMOVE_STILL); + return CANT_ATTACK; + } + if (victimPlayer) { + CPlayerPed *player = FindPlayerPed(); + if (m_nPedType == PEDTYPE_COP && player->m_pWanted->m_bIgnoredByCops + || player->m_pWanted->m_bIgnoredByEveryone + || m_pedInObjective->bIsInWater + || m_pedInObjective->m_nPedState == PED_ARRESTED) { + + if (m_nPedState != PED_ARREST_PLAYER) + SetIdle(); + + return CANT_ATTACK; + } + } + if (victimPlayer && m_nPedType != PEDTYPE_COP && CharCreatedBy != MISSION_CHAR + && FindPlayerPed()->m_pWanted->m_CurrentCops != 0) { + SetObjective(OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE); + + return CANT_ATTACK; + } + if (m_pedInObjective->m_fHealth <= 0.0f) { + bObjectiveCompleted = true; + bScriptObjectiveCompleted = true; + return ATTACK_IN_PROGRESS; + } + bool canReachVictim = false; + uint32 endOfAttack = 0; + CWeaponInfo *wepInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + + // Already calculated at the start + // float distWithTargetSc = distWithTarget.Magnitude(); + float maxDistToKeep = 0.3f; + float wepRange = wepInfo->m_fRange / 2.f; + + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED && !IsPlayer() && !(m_pedStats->m_flags & STAT_CAN_KICK)) + wepRange -= 0.3f; + + if (distWithTargetSc <= 5.f && victimPlayer && !victimPlayer->m_bNoPosForMeleeAttack) { + + if (m_pedInObjective->EnteringCar() && wepRange > 2.f) { + m_vecSeekPos = m_pedInObjective->GetPosition(); + wepRange = 1.0f; + maxDistToKeep = 0.5f; + } else { + int8 attackDir = victimPlayer->FindMeleeAttackPoint(this, distWithTarget, endOfAttack); + if (attackDir == -1) { + m_vecSeekPos = victimPlayer->GetPosition(); + maxDistToKeep = 4.0f; + } else { + victimPlayer->GetMeleeAttackCoords(m_vecSeekPos, attackDir, wepRange); + distWithTargetSc = (m_vecSeekPos - GetPosition()).Magnitude(); + canReachVictim = true; + } + } + } else { + m_vecSeekPos = m_pedInObjective->GetPosition(); + maxDistToKeep = Max(0.8f, 0.9f * wepRange); + wepRange *= 1.1f; + if (victimPlayer && victimPlayer->m_bNoPosForMeleeAttack) + victimPlayer = nil; + } + + if (m_pedInObjective->bInVehicle && m_pedInObjective->m_nPedState != PED_DRAG_FROM_CAR) { + CVehicle *vehOfTarget = m_pedInObjective->m_pMyVehicle; + + if (vehOfTarget){ + if (vehOfTarget->bIsInWater || vehOfTarget->GetStatus() == STATUS_PLAYER_DISABLED + || m_pedInObjective->IsPlayer() && CPad::GetPad(0)->ArePlayerControlsDisabled()) { + SetIdle(); + return WATCH_UNTIL_HE_DISAPPEARS; + } + SetLookFlag(vehOfTarget, false); + + if (m_nPedState != PED_CARJACK) { + if (m_pedInObjective->m_nPedState != PED_ARRESTED) { + if (m_nPedState != PED_ATTACK && !bKindaStayInSamePlace && !killPlayerInNoPoliceZone) { + + if (m_nPedType == PEDTYPE_COP || vehOfTarget->bIsBus) { + GoToNearestDoor(vehOfTarget); + } else { + m_vehDoor = 0; + if (m_pedInObjective == vehOfTarget->pDriver || vehOfTarget->bIsBus) { + m_vehDoor = CAR_DOOR_LF; + } else if (m_pedInObjective == vehOfTarget->pPassengers[0]) { + m_vehDoor = CAR_DOOR_RF; + } else if (m_pedInObjective == vehOfTarget->pPassengers[1]) { + m_vehDoor = CAR_DOOR_LR; + } else if (m_pedInObjective == vehOfTarget->pPassengers[2]) { + m_vehDoor = CAR_DOOR_RR; + } + // Unused + // GetPositionToOpenCarDoor(vehOfTarget, m_vehDoor); + SetSeekCar(vehOfTarget, m_vehDoor); + SetMoveState(PEDMOVE_RUN); + } + } + } + } + } + return ATTACK_IN_PROGRESS; + } + if (m_nMoveState == PEDMOVE_STILL && IsPedInControl()) { + SetLookFlag(m_pedInObjective, false); + if(m_nPedState == PED_IDLE || m_nPedState == PED_ATTACK || m_nPedState == PED_FIGHT) + TurnBody(); + } + if (m_nPedType == PEDTYPE_COP && m_pedInObjective->IsPlayer()) { + float maxArrestDist = 1.5f; + if (((CCopPed*)this)->m_bDragsPlayerFromCar) { + if (m_nPedState == PED_FALL) { + maxArrestDist = 3.5f; + } else if (m_nPedState != PED_DRAG_FROM_CAR) { + ((CCopPed*)this)->m_bDragsPlayerFromCar = 0; + } + } + + if (distWithTargetSc < maxArrestDist) { + if (m_pedInObjective->m_getUpTimer > CTimer::GetTimeInMilliseconds() + || m_pedInObjective->m_nPedState == PED_DRAG_FROM_CAR) { + + ((CCopPed*)this)->SetArrestPlayer(m_pedInObjective); + return WATCH_UNTIL_HE_DISAPPEARS; + } + } + } + + if (distWithTargetSc > maxDistToKeep && !bKindaStayInSamePlace && m_nPedState != PED_ATTACK && + (m_nPedState != PED_FIGHT || m_curFightMove == FIGHTMOVE_IDLE) && !killPlayerInNoPoliceZone) { + + bool goForward = false; + + if (m_nPedState == PED_FIGHT) { + if (canReachVictim) { + CVector attackAndVictimDist = m_vecSeekPos - m_pedInObjective->GetPosition(); + CVector victimFarness = attackAndVictimDist / wepRange; + CVector distVec = GetPosition() - m_pedInObjective->GetPosition(); + float distSqr = distVec.MagnitudeSqr(); + if (sq(wepRange) > distSqr && distSqr > 0.05f) { + distVec.Normalise(); + if (DotProduct2D(victimFarness, distVec) > Cos(DEGTORAD(30.f))) + goForward = true; + } + } + } + if (goForward) { + m_curFightMove = FIGHTMOVE_SHUFFLE_F; + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FIGHT_SHUFFLE_B, 16.f)->SetFinishCallback(FinishFightMoveCB,this); + m_fightState = FIGHTSTATE_NO_MOVE; + m_fightButtonPressure = 0; + m_takeAStepAfterAttack = false; + + } else if (bUsePedNodeSeek && !canReachVictim) { + CVector bestCoords(0.0f, 0.0f, 0.0f); + + if (!m_pNextPathNode) + FindBestCoordsFromNodes(m_vecSeekPos, &bestCoords); + + if (m_pNextPathNode) + m_vecSeekPos = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); + + SetSeek(m_vecSeekPos, m_distanceToCountSeekDone); + } else { + if (canReachVictim) + SetSeek(m_vecSeekPos, maxDistToKeep); + else + SetSeek(m_pedInObjective, maxDistToKeep); + } + return ATTACK_IN_PROGRESS; + } + + if (m_attackTimer < CTimer::GetTimeInMilliseconds() + && distWithTargetSc < wepRange && m_pedInObjective->m_nPedState != PED_GETUP && m_pedInObjective->m_nPedState != PED_DRAG_FROM_CAR) { + + if (bIsDucking) { + CAnimBlendAssociation* duckAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_DOWN); + if (!duckAnim) + duckAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_LOW); + if (!duckAnim) + duckAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_WEAPON); + + if (duckAnim) { + duckAnim->flags |= ASSOC_DELETEFADEDOUT; + duckAnim->blendDelta = -4.0f; + } + bIsDucking = false; + return CANT_ATTACK; + } + + if (canReachVictim || !victimPlayer) { + SetMoveState(PEDMOVE_STILL); + SetAttack(m_pedInObjective); + m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( + m_pedInObjective->GetPosition().x, m_pedInObjective->GetPosition().y, + GetPosition().x, GetPosition().y); + SetShootTimer(CGeneral::GetRandomNumberInRange(0.f, 500.f)); + + int time; + if (endOfAttack <= CTimer::GetTimeInMilliseconds()) + time = CGeneral::GetRandomNumberInRange(100.0f, 1500.0f); + else + time = endOfAttack - CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(400.0f, 1500.0f); + + SetAttackTimer(time); + bObstacleShowedUpDuringKillObjective = false; + } + return ATTACK_IN_PROGRESS; + } else { + if (!m_pedInObjective->m_pCurrentPhysSurface && m_pCurrentPhysSurface && b158_8) { + b158_8 = false; + bStopAndShoot = false; + } + + if (m_nPedState != PED_ATTACK && m_nPedState != PED_FIGHT) { + SetMoveState(PEDMOVE_STILL); + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED) + StartFightAttack(0); + } + return ATTACK_IN_PROGRESS; + } +} + +bool +CPed::CanBeDamagedByThisGangMember(CPed* who) +{ + return m_gangFlags & (1 << (who->m_nPedType - PEDTYPE_GANG1)); +} diff --git a/src/miami/peds/PedAttractor.cpp b/src/miami/peds/PedAttractor.cpp new file mode 100644 index 00000000..427a7165 --- /dev/null +++ b/src/miami/peds/PedAttractor.cpp @@ -0,0 +1,802 @@ +#include "common.h" +#include "PedAttractor.h" + +#include "General.h" +#include "Vehicle.h" +#include "World.h" +#include "MemoryHeap.h" + +const int gcMaxSizeOfAtmQueue = 1; +const int gcMaxSizeOfSeatQueue = 1; +const int gcMaxSizeOfStopQueue = 5; +const int gcMaxSizeOfPizzaQueue = 5; +const int gcMaxSizeOfShelterQueue = 5; +const int gcMaxSizeOfIceCreamQueue = 1; + +std::vector CPedShelterAttractor::ms_displacements; + +CPedAttractorManager* GetPedAttractorManager() +{ +// mobile just has a static here: +/* + static CPedAttractorManager pedAttrMgr; + return &pedAttrMgr; +*/ + static CPedAttractorManager *pedAttrMgr; + if(pedAttrMgr == nil){ + PUSH_MEMID(MEMID_PED_ATTR); + pedAttrMgr = new CPedAttractorManager; + POP_MEMID(); + } + return pedAttrMgr; +} + +CVehicleToEffect::CVehicleToEffect(CVehicle* pVehicle) : m_pVehicle(pVehicle) +{ + m_effects[1].col = CRGBA(0, 0, 0, 0); + m_effects[1].type = EFFECT_PED_ATTRACTOR; + m_effects[1].pos = CVector(2.0f, 1.0f, 0.0f); + m_effects[1].pedattr.useDir = CVector(-1.0f, 0.0f, 0.0f); + m_effects[1].pedattr.queueDir = CVector(-1.0f, 0.0f, 0.0f); + m_effects[1].pedattr.type = ATTRACTOR_ICECREAM; + + m_effects[3].col = CRGBA(0, 0, 0, 0); + m_effects[3].type = EFFECT_PED_ATTRACTOR; + m_effects[3].pos = CVector(2.0f, -0.5f, 0.0f); + m_effects[3].pedattr.useDir = CVector(-1.0f, 0.0f, 0.0f); + m_effects[3].pedattr.queueDir = CVector(-1.0f, 0.0f, 0.0f); + m_effects[3].pedattr.type = ATTRACTOR_ICECREAM; + + m_effects[0].col = CRGBA(0, 0, 0, 0); + m_effects[0].type = EFFECT_PED_ATTRACTOR; + m_effects[0].pos = CVector(-2.0f, 1.0f, 0.0f); + m_effects[0].pedattr.useDir = CVector(1.0f, 0.0f, 0.0f); + m_effects[0].pedattr.queueDir = CVector(1.0f, 0.0f, 0.0f); + m_effects[0].pedattr.type = ATTRACTOR_ICECREAM; + + m_effects[2].col = CRGBA(0, 0, 0, 0); + m_effects[2].type = EFFECT_PED_ATTRACTOR; + m_effects[2].pos = CVector(-2.0f, -0.5f, 0.0f); + m_effects[2].pedattr.useDir = CVector(1.0f, 0.0f, 0.0f); + m_effects[2].pedattr.queueDir = CVector(1.0f, 0.0f, 0.0f); + m_effects[2].pedattr.type = ATTRACTOR_ICECREAM; +} + +CVehicleToEffect& CVehicleToEffect::From(const CVehicleToEffect& other) +{ + m_pVehicle = other.m_pVehicle; + for (int i = 0; i < NUM_ATTRACTORS_FOR_ICECREAM_VAN; i++) { + m_effects[i].col = other.m_effects[i].col; + m_effects[i].type = other.m_effects[i].type; + m_effects[i].pos = other.m_effects[i].pos; + m_effects[i].pedattr = other.m_effects[i].pedattr; + } + return *this; +} + +const C2dEffect* CVehicleToEffect::ChooseEffect(const CVector& pos) const +{ + if (!m_pVehicle) + return nil; + if (DotProduct(pos - m_pVehicle->GetPosition(), m_pVehicle->GetRight()) > 0.0f) { + if (DotProduct(pos - m_pVehicle->GetPosition(), m_pVehicle->GetForward()) > 0.0f) + return &m_effects[1]; + else + return &m_effects[3]; + } + else { + if (DotProduct(pos - m_pVehicle->GetPosition(), m_pVehicle->GetForward()) > 0.0f) + return &m_effects[0]; + else + return &m_effects[2]; + } +} + +bool CVehicleToEffect::HasThisEffect(C2dEffect* pEffect) const +{ + for (int i = 0; i < NUM_ATTRACTORS_FOR_ICECREAM_VAN; i++) { + if (pEffect == &m_effects[i]) + return true; + } + return false; +} + +const C2dEffect* CPedAttractorManager::GetEffectForIceCreamVan(CVehicle* pVehicle, const CVector& pos) +{ + if (!vVehicleToEffect.empty()) { + for (std::vector::const_iterator assoc = vVehicleToEffect.begin(); assoc != vVehicleToEffect.end(); ++assoc) { + if (assoc->GetVehicle() == pVehicle) + return assoc->ChooseEffect(pos); + } + } + PUSH_MEMID(MEMID_PED_ATTR); + CVehicleToEffect effect(pVehicle); + vVehicleToEffect.push_back(effect); + POP_MEMID(); +#ifdef FIX_BUGS + return vVehicleToEffect.back().ChooseEffect(pos); +#else + return effect.ChooseEffect(pos); +#endif +} + +CVehicle* CPedAttractorManager::GetIceCreamVanForEffect(C2dEffect* pEffect) +{ + if (vVehicleToEffect.empty()) + return nil; + for (std::vector::const_iterator assoc = vVehicleToEffect.begin(); assoc != vVehicleToEffect.end(); ++assoc) { + if (assoc->HasThisEffect(pEffect)) + return assoc->GetVehicle(); + } + return nil; +} + +const CPedAttractor* CPedAttractorManager::FindAssociatedAttractor(const C2dEffect* pEffect, std::vector& vecAttractors) +{ + if (vecAttractors.empty()) + return nil; + for (std::vector::const_iterator attractor = vecAttractors.begin(); attractor != vecAttractors.end(); ++attractor) { + if ((*attractor)->GetEffect() == pEffect) + return *attractor; + } + return nil; +} + +void CPedAttractorManager::RemoveIceCreamVanEffects(C2dEffect* pEffect) +{ + CVehicle* pVehicle = GetIceCreamVanForEffect(pEffect); + if (!pVehicle) + return; + if (vVehicleToEffect.empty()) + return; + for (std::vector::iterator assoc = vVehicleToEffect.begin(); assoc != vVehicleToEffect.end();) { + if (assoc->GetVehicle() != pVehicle) { + ++assoc; + continue; + } + uint32 total = 0; + for (uint32 j = 0; j < NUM_ATTRACTORS_FOR_ICECREAM_VAN; j++) { + if (FindAssociatedAttractor(assoc->GetEffect(j), vIceCreamAttractors)) + total++; + } + if (total > 0) + ++assoc; + else + assoc = vVehicleToEffect.erase(assoc); + } +} + +CPedAttractor::CPedAttractor(C2dEffect* pEffect, const CMatrix& matrix, int32 maxpeds, float qdist, float waitTime, float approachTime, float distance, float headingdiff, float posdisp, float headdisp) : + p2dEffect(pEffect), + m_nMaxPedsInAttractor(maxpeds), + m_fQueueDistance(qdist), + m_fTimeInWaitQueue(waitTime), + m_fTimeInApproachingQueue(approachTime), + m_fDistanceToUseAttractor(distance), + m_fAcceptableHeading(headingdiff), + m_fMaxPositionDisplacement(posdisp), + m_fMaxHeadingDisplacement(headdisp) +{ + CPedAttractorManager::ComputeEffectPos(pEffect, matrix, vecEffectPos); + CPedAttractorManager::ComputeEffectQueueDir(pEffect, matrix, vecQueueDir); + CPedAttractorManager::ComputeEffectUseDir(pEffect, matrix, vecUseDir); +} + +void CPedPizzaAttractor::UpdatePedStateOnDeparture(CPed* pPed) const +{ + if (pPed->m_nPedMoney > 10) + pPed->m_nPedMoney -= 10; + else + pPed->m_nPedMoney = 0; +} + +void CPedAtmAttractor::UpdatePedStateOnDeparture(CPed* pPed) const +{ + pPed->m_nPedMoney += 20 * CGeneral::GetRandomNumberInRange(1, 51); +}; + +float CPedAttractor::ComputeDeltaHeading() const +{ + return CGeneral::GetRandomNumberInRange(-m_fMaxHeadingDisplacement, m_fMaxHeadingDisplacement); +} + +float CPedAttractor::ComputeDeltaPos() const +{ + return CGeneral::GetRandomNumberInRange(-m_fMaxPositionDisplacement, m_fMaxPositionDisplacement); +} + +void CPedAttractor::ComputeAttractTime(int32 id, bool approacher, float& time) const +{ + if (approacher) + time = m_fTimeInApproachingQueue; + else + time = m_fTimeInWaitQueue; +} + +void CPedAttractor::ComputeAttractPos(int32 qid, CVector& pos) const +{ + if (!p2dEffect) + return; + pos = vecEffectPos - qid * vecQueueDir * m_fQueueDistance; + if (qid != 0) { + pos.x += ComputeDeltaPos(); + pos.y += ComputeDeltaPos(); + } +} + +CVector CPedShelterAttractor::GetDisplacement(int32 qid) const +{ + if (ms_displacements.empty()) { + int i = 0; + while (i < gcMaxSizeOfShelterQueue) { + float fRandomAngle = CGeneral::GetRandomNumberInRange(0.0f, TWOPI); + float fRandomOffset = CGeneral::GetRandomNumberInRange(0.0f, 2.0f); + CVector vecDisplacement(fRandomOffset * Sin(fRandomAngle), fRandomOffset * Cos(fRandomAngle), 0.0f); + bool close = false; + for (std::vector::const_iterator v = ms_displacements.begin(); v != ms_displacements.end(); ++v) { + if ((*v - vecDisplacement).Magnitude() < 1.0f) { + close = true; + break; + } + } + if (!close) { + ms_displacements.push_back(vecDisplacement); + i++; + } + } + } + return ms_displacements[qid]; +} + +void CPedShelterAttractor::ComputeAttractPos(int qid, CVector& pos) const +{ + if (!p2dEffect) + return; + pos = vecEffectPos + GetDisplacement(qid); +} + +void CPedAttractor::ComputeAttractHeading(int32 qid, float& heading) const +{ + heading = CGeneral::GetRadianAngleBetweenPoints(qid != 0 ? vecQueueDir.x : vecUseDir.x, qid != 0 ? vecQueueDir.y : vecUseDir.y, 0.0f, 0.0f); + if (qid != 0) + heading += ComputeDeltaHeading(); +} + +void CPedShelterAttractor::ComputeAttractHeading(int qid, float& heading) const +{ + heading = CGeneral::GetRandomNumberInRange(0.0f, TWOPI); +} + +bool CPedAttractor::RegisterPed(CPed* pPed) +{ + for (std::vector::iterator pPedIt = vApproachingQueue.begin(); pPedIt != vApproachingQueue.end(); ++pPedIt) { + if (*pPedIt == pPed) { + vApproachingQueue.erase(pPedIt); + return false; + } + } + if (GetNoOfRegisteredPeds() >= m_nMaxPedsInAttractor) + return 0; + vApproachingQueue.push_back(pPed); + CVector pos; + float heading; + float time; + int32 slot = ComputeFreeSlot(); + ComputeAttractPos(slot, pos); + ComputeAttractHeading(slot, heading); + ComputeAttractTime(slot, false, time); + pPed->SetNewAttraction(this, pos, heading, time, slot); + return true; +} + +static bool IsPedUsingAttractorOfThisType(int8 type, CPed* pPed) +{ + switch (type) { + case ATTRACTOR_ATM: + if (pPed->m_objective == OBJECTIVE_GOTO_ATM_ON_FOOT) + return true; + break; + case ATTRACTOR_SEAT: + if (pPed->m_objective == OBJECTIVE_GOTO_SEAT_ON_FOOT) + return true; + break; + case ATTRACTOR_STOP: + if (pPed->m_objective == OBJECTIVE_GOTO_BUS_STOP_ON_FOOT || pPed->m_objective == OBJECTIVE_WAIT_ON_FOOT_AT_BUS_STOP || pPed->m_objective == OBJECTIVE_WAIT_ON_FOOT) + return true; + break; + case ATTRACTOR_PIZZA: + if (pPed->m_objective == OBJECTIVE_GOTO_PIZZA_ON_FOOT || pPed->m_objective == OBJECTIVE_WAIT_ON_FOOT) + return true; + break; + case ATTRACTOR_SHELTER: + if (pPed->m_objective == OBJECTIVE_GOTO_SHELTER_ON_FOOT || pPed->m_objective == OBJECTIVE_WAIT_ON_FOOT_AT_SHELTER) + return true; + break; + case ATTRACTOR_ICECREAM: + if (pPed->m_objective == OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT || pPed->m_objective == OBJECTIVE_WAIT_ON_FOOT_AT_ICE_CREAM_VAN) + return true; + break; + } + return false; +} + +bool CPedAttractor::DeRegisterPed(CPed* pPed) +{ + for (std::vector::iterator pPedIt = vApproachingQueue.begin(); pPedIt != vApproachingQueue.end(); ++pPedIt) { + if (*pPedIt != pPed) + continue; + pPed->m_attractor = nil; + pPed->m_positionInQueue = -1; + pPed->bHasAlreadyUsedAttractor = true; + + if (IsPedUsingAttractorOfThisType(p2dEffect->pedattr.type, pPed)) + pPed->SetObjective(OBJECTIVE_NONE); + else if (pPed->GetPedState() != PED_IDLE && pPed->GetPedState() != PED_NONE) { + vApproachingQueue.erase(pPedIt); + return true; + } + pPed->SetWanderPath(CGeneral::GetNodeHeadingFromVector(-vecQueueDir.x, -vecQueueDir.y)); + vApproachingQueue.erase(pPedIt); + return true; + } + return BroadcastDeparture(pPed); +} + +bool CPedAttractor::BroadcastArrival(CPed* pPed) +{ + for (std::vector::const_iterator pPedIt = vWaitingQueue.begin(); pPedIt != vWaitingQueue.end(); ++pPedIt) { + if (*pPedIt == pPed) + return false; + } + vWaitingQueue.push_back(pPed); + for (std::vector::iterator pPedIt = vApproachingQueue.begin(); pPedIt != vApproachingQueue.end(); ++pPedIt) { + if (*pPedIt == pPed) { + vApproachingQueue.erase(pPedIt); + break; + } + } + for (std::vector::iterator pPedIt = vApproachingQueue.begin(); pPedIt != vApproachingQueue.end(); ++pPedIt) { + CPed* pPed = *pPedIt; + CVector pos; + float heading; + float time; + int32 slot = ComputeFreeSlot(); + ComputeAttractPos(slot, pos); + ComputeAttractHeading(slot, heading); + ComputeAttractTime(slot, false, time); + pPed->SetNewAttraction(this, pos, heading, time, slot); + } + return true; +} + +bool CPedAttractor::BroadcastDeparture(CPed* pPed) +{ + int qid = -1; + for (uint32 i = 0; i < vWaitingQueue.size(); i++){ + if (vWaitingQueue[i] == pPed) + qid = i; + } + if (qid < 0) + return false; + for (uint32 i = qid + 1; i < vWaitingQueue.size(); i++) { + CVector pos; + float heading; + float time; + ComputeAttractPos(i - 1, pos); + ComputeAttractHeading(i - 1, heading); + ComputeAttractTime(i - 1, true, time); + pPed->SetNewAttraction(this, pos, heading, time, i - 1); + } + pPed->m_attractor = nil; + pPed->m_positionInQueue = -1; + pPed->bHasAlreadyUsedAttractor = true; + if (!IsPedUsingAttractorOfThisType(p2dEffect->pedattr.type, pPed)) { + if (pPed->GetPedState() == PED_IDLE || pPed->GetPedState() == PED_NONE) + pPed->SetWanderPath(CGeneral::GetNodeHeadingFromVector(-vecQueueDir.x, -vecQueueDir.y)); + } + else { + pPed->SetObjective(OBJECTIVE_NONE); + if (qid == 0) + pPed->SetWanderPath(CGeneral::GetNodeHeadingFromVector(vecQueueDir.x, vecQueueDir.y)); + else if (qid == vWaitingQueue.size() - 1) + pPed->SetWanderPath(CGeneral::GetNodeHeadingFromVector(-vecQueueDir.x, -vecQueueDir.y)); + else + pPed->SetWanderPath(CGeneral::GetNodeHeadingFromVector(-vecQueueDir.y, -vecQueueDir.x)); + UpdatePedStateOnDeparture(pPed); + } + vWaitingQueue.erase(vWaitingQueue.begin() + qid); + for (std::vector::iterator pPedIt = vApproachingQueue.begin(); pPedIt != vApproachingQueue.end(); ++pPedIt) { + CPed* pPed = *pPedIt; + CVector pos; + float heading; + float time; + int32 slot = ComputeFreeSlot(); + ComputeAttractPos(slot, pos); + ComputeAttractHeading(slot, heading); + ComputeAttractTime(slot, false, time); + pPed->SetNewAttraction(this, pos, heading, time, slot); + } + return true; +} + +bool CPedShelterAttractor::BroadcastDeparture(CPed* pPed) +{ + int qid = -1; + for (uint32 i = 0; i < vWaitingQueue.size(); i++) { + if (vWaitingQueue[i] == pPed) + qid = i; + } + if (qid < 0) + return false; + pPed->m_attractor = nil; + pPed->m_positionInQueue = -1; + pPed->bHasAlreadyUsedAttractor = true; + if (!IsPedUsingAttractorOfThisType(p2dEffect->pedattr.type, pPed)) { + if (pPed->GetPedState() == PED_IDLE || pPed->GetPedState() == PED_NONE) + pPed->SetWanderPath(CGeneral::GetNodeHeadingFromVector(-vecQueueDir.x, -vecQueueDir.y)); + } + else { + pPed->SetObjective(OBJECTIVE_NONE); + if (qid == 0) + pPed->SetWanderPath(CGeneral::GetNodeHeadingFromVector(vecQueueDir.x, vecQueueDir.y)); + else if (qid == vWaitingQueue.size() - 1) + pPed->SetWanderPath(CGeneral::GetNodeHeadingFromVector(-vecQueueDir.x, -vecQueueDir.y)); + else + pPed->SetWanderPath(CGeneral::GetNodeHeadingFromVector(-vecQueueDir.y, -vecQueueDir.x)); + UpdatePedStateOnDeparture(pPed); + } + vWaitingQueue.erase(vWaitingQueue.begin() + qid); + for (std::vector::iterator pPedIt = vApproachingQueue.begin(); pPedIt != vApproachingQueue.end(); ++pPedIt) { + CPed* pPed = *pPedIt; + CVector pos; + float heading; + float time; + int32 slot = ComputeFreeSlot(); + ComputeAttractPos(slot, pos); + ComputeAttractHeading(slot, heading); + ComputeAttractTime(slot, false, time); + pPed->SetNewAttraction(this, pos, heading, time, slot); + } + return true; +} + +bool CPedAttractor::IsRegisteredWithPed(CPed* pPed) const +{ + for (std::vector::const_iterator pPedIt = vWaitingQueue.begin(); pPedIt != vWaitingQueue.end(); ++pPedIt) { + if (*pPedIt == pPed) + return true; + } + for (std::vector::const_iterator pPedIt = vApproachingQueue.begin(); pPedIt != vApproachingQueue.end(); ++pPedIt) { + if (*pPedIt == pPed) { + return true; + } + } + return false; +} + +bool CPedAttractor::IsInQueue(CPed* pPed) const +{ + for (std::vector::const_iterator pPedIt = vWaitingQueue.begin(); pPedIt != vWaitingQueue.end(); ++pPedIt) { + if (*pPedIt == pPed) + return true; + } + return false; +} + +CPedAttractor* CPedAttractorManager::RegisterPedWithAttractor(CPed* pPed, C2dEffect* pEffect, const CMatrix& matrix) +{ + if (pEffect->type != EFFECT_PED_ATTRACTOR) + return nil; + if (IsPedRegisteredWithEffect(pPed)) + return nil; + switch (pEffect->pedattr.type) { + case ATTRACTOR_ATM: return RegisterPed(pPed, pEffect, matrix, vAtmAttractors); + case ATTRACTOR_SEAT: return RegisterPed(pPed, pEffect, matrix, vSeatAttractors); + case ATTRACTOR_STOP: return RegisterPed(pPed, pEffect, matrix, vStopAttractors); + case ATTRACTOR_PIZZA: return RegisterPed(pPed, pEffect, matrix, vPizzaAttractors); + case ATTRACTOR_SHELTER: return RegisterPed(pPed, pEffect, matrix, vShelterAttractors); + case ATTRACTOR_ICECREAM: return RegisterPed(pPed, pEffect, matrix, vIceCreamAttractors); + } + return nil; +} + +bool CPedAttractorManager::DeRegisterPed(CPed* pPed, CPedAttractor* pAttractor) +{ + if (!pAttractor) + return false; + if (pAttractor->GetEffect()->type != EFFECT_PED_ATTRACTOR) + return nil; + if (!IsPedRegisteredWithEffect(pPed)) + return nil; + switch (pAttractor->GetEffect()->pedattr.type) { + case ATTRACTOR_ATM: return DeRegisterPed(pPed, pAttractor, vAtmAttractors); + case ATTRACTOR_SEAT: return DeRegisterPed(pPed, pAttractor, vSeatAttractors); + case ATTRACTOR_STOP: return DeRegisterPed(pPed, pAttractor, vStopAttractors); + case ATTRACTOR_PIZZA: return DeRegisterPed(pPed, pAttractor, vPizzaAttractors); + case ATTRACTOR_SHELTER: return DeRegisterPed(pPed, pAttractor, vShelterAttractors); + case ATTRACTOR_ICECREAM: return DeRegisterPed(pPed, pAttractor, vIceCreamAttractors); + } + return nil; +} + +bool CPedAttractorManager::BroadcastArrival(CPed* pPed, CPedAttractor* pAttractor) +{ + if (!pAttractor) + return false; + if (pAttractor->GetEffect()->type != EFFECT_PED_ATTRACTOR) + return nil; + if (!IsPedRegisteredWithEffect(pPed)) + return nil; + switch (pAttractor->GetEffect()->pedattr.type) { + case ATTRACTOR_ATM: return BroadcastArrival(pPed, pAttractor, vAtmAttractors); + case ATTRACTOR_SEAT: return BroadcastArrival(pPed, pAttractor, vSeatAttractors); + case ATTRACTOR_STOP: return BroadcastArrival(pPed, pAttractor, vStopAttractors); + case ATTRACTOR_PIZZA: return BroadcastArrival(pPed, pAttractor, vPizzaAttractors); + case ATTRACTOR_SHELTER: return BroadcastArrival(pPed, pAttractor, vShelterAttractors); + case ATTRACTOR_ICECREAM: return BroadcastArrival(pPed, pAttractor, vIceCreamAttractors); + } + return nil; +} + +bool CPedAttractorManager::BroadcastDeparture(CPed* pPed, CPedAttractor* pAttractor) +{ + if (!pAttractor) + return false; + if (pAttractor->GetEffect()->type != EFFECT_PED_ATTRACTOR) + return nil; + if (!IsPedRegisteredWithEffect(pPed)) + return nil; + switch (pAttractor->GetEffect()->pedattr.type) { + case ATTRACTOR_ATM: return BroadcastDeparture(pPed, pAttractor, vAtmAttractors); + case ATTRACTOR_SEAT: return BroadcastDeparture(pPed, pAttractor, vSeatAttractors); + case ATTRACTOR_STOP: return BroadcastDeparture(pPed, pAttractor, vStopAttractors); + case ATTRACTOR_PIZZA: return BroadcastDeparture(pPed, pAttractor, vPizzaAttractors); + case ATTRACTOR_SHELTER: return BroadcastDeparture(pPed, pAttractor, vShelterAttractors); + case ATTRACTOR_ICECREAM: return BroadcastDeparture(pPed, pAttractor, vIceCreamAttractors); + } + return nil; +} + +bool CPedAttractorManager::IsAtHeadOfQueue(CPed* pPed, CPedAttractor* pAttractor) +{ + if (!pAttractor) + return false; + if (pAttractor->GetEffect()->type != EFFECT_PED_ATTRACTOR) + return nil; + if (!IsPedRegisteredWithEffect(pPed)) + return nil; + switch (pAttractor->GetEffect()->pedattr.type) { + case ATTRACTOR_ATM: return IsAtHeadOfQueue(pPed, pAttractor, vAtmAttractors); + case ATTRACTOR_SEAT: return IsAtHeadOfQueue(pPed, pAttractor, vSeatAttractors); + case ATTRACTOR_STOP: return IsAtHeadOfQueue(pPed, pAttractor, vStopAttractors); + case ATTRACTOR_PIZZA: return IsAtHeadOfQueue(pPed, pAttractor, vPizzaAttractors); + case ATTRACTOR_SHELTER: return IsAtHeadOfQueue(pPed, pAttractor, vShelterAttractors); + case ATTRACTOR_ICECREAM: return IsAtHeadOfQueue(pPed, pAttractor, vIceCreamAttractors); + } + return nil; +} + +bool CPedAttractorManager::IsInQueue(CPed* pPed, CPedAttractor* pAttractor) +{ + if (!pAttractor) + return false; + if (pAttractor->GetEffect()->type != EFFECT_PED_ATTRACTOR) + return nil; + if (!IsPedRegisteredWithEffect(pPed)) + return nil; + switch (pAttractor->GetEffect()->pedattr.type) { + case ATTRACTOR_ATM: return IsInQueue(pPed, pAttractor, vAtmAttractors); + case ATTRACTOR_SEAT: return IsInQueue(pPed, pAttractor, vSeatAttractors); + case ATTRACTOR_STOP: return IsInQueue(pPed, pAttractor, vStopAttractors); + case ATTRACTOR_PIZZA: return IsInQueue(pPed, pAttractor, vPizzaAttractors); + case ATTRACTOR_SHELTER: return IsInQueue(pPed, pAttractor, vShelterAttractors); + case ATTRACTOR_ICECREAM: return IsInQueue(pPed, pAttractor, vIceCreamAttractors); + } + return nil; +} + +bool CPedAttractorManager::HasEmptySlot(const C2dEffect* pEffect) +{ + if (!pEffect) + return false; + if (pEffect->type != EFFECT_PED_ATTRACTOR) + return nil; + const CPedAttractor* pAttractor; + switch (pEffect->pedattr.type) { + case ATTRACTOR_ATM: pAttractor = FindAssociatedAttractor(pEffect, vAtmAttractors); break; + case ATTRACTOR_SEAT: pAttractor = FindAssociatedAttractor(pEffect, vSeatAttractors); break; + case ATTRACTOR_STOP: pAttractor = FindAssociatedAttractor(pEffect, vStopAttractors); break; + case ATTRACTOR_PIZZA: pAttractor = FindAssociatedAttractor(pEffect, vPizzaAttractors); break; + case ATTRACTOR_SHELTER: pAttractor = FindAssociatedAttractor(pEffect, vShelterAttractors); break; + case ATTRACTOR_ICECREAM: pAttractor = FindAssociatedAttractor(pEffect, vIceCreamAttractors); break; + default: return true; + } + if (!pAttractor) + return true; + return pAttractor->GetNoOfRegisteredPeds() < pAttractor->GetMaxPedsInAttractor(); +} + +bool CPedAttractorManager::IsPedRegisteredWithEffect(CPed* pPed) +{ + return IsPedRegistered(pPed, vAtmAttractors) || + IsPedRegistered(pPed, vSeatAttractors) || + IsPedRegistered(pPed, vStopAttractors) || + IsPedRegistered(pPed, vPizzaAttractors) || + IsPedRegistered(pPed, vShelterAttractors) || + IsPedRegistered(pPed, vIceCreamAttractors); +} + +void CPedAttractorManager::ComputeEffectPos(const C2dEffect* pEffect, const CMatrix& matrix, CVector& pos) +{ + pos = matrix.GetPosition() + Multiply3x3(matrix, pEffect->pos); +} + +void CPedAttractorManager::ComputeEffectQueueDir(const C2dEffect* pEffect, const CMatrix& matrix, CVector& pos) +{ + pos = Multiply3x3(matrix, pEffect->pedattr.queueDir); +} + +void CPedAttractorManager::ComputeEffectUseDir(const C2dEffect* pEffect, const CMatrix& matrix, CVector& pos) +{ + pos = Multiply3x3(matrix, pEffect->pedattr.useDir); +} + +CPedAttractor* CPedAttractorManager::RegisterPed(CPed* pPed, C2dEffect* pEffect, const CMatrix& matrix, std::vector& vecAttractors) +{ + CPedAttractor* pRegisteredAttractor = nil; + for (std::vector::const_iterator pAttractorIt = vecAttractors.begin(); pAttractorIt != vecAttractors.end(); ++pAttractorIt) { + CPedAttractor* pAttractor = *pAttractorIt; + CVector vEffectPos; + ComputeEffectPos(pAttractor->GetEffect(), matrix, vEffectPos); + if (pAttractor->GetEffect() == pEffect && vEffectPos == pAttractor->GetEffectPos()) { + if (!IsApproachable(pEffect, matrix, pAttractor->ComputeFreeSlot(), pPed)) + return nil; + pRegisteredAttractor = pAttractor; + break; + } + } + if (pRegisteredAttractor || !IsApproachable(pEffect, matrix, 0, pPed)) { + if (pRegisteredAttractor) + pRegisteredAttractor->RegisterPed(pPed); + return pRegisteredAttractor; + } + PUSH_MEMID(MEMID_PED_ATTR); + switch (pEffect->pedattr.type) { + case ATTRACTOR_ATM: pRegisteredAttractor = new CPedAtmAttractor(pEffect, matrix, gcMaxSizeOfAtmQueue, 1.0f, 30000.0f, 3000.0f, 0.2f, 0.15f, 0.1f, 0.1f); vecAttractors.push_back(pRegisteredAttractor); break; + case ATTRACTOR_SEAT: pRegisteredAttractor = new CPedSeatAttractor(pEffect, matrix, gcMaxSizeOfSeatQueue, 1.0f, 30000.0f, 3000.0f, 0.125f, 0.1f, 0.1f, 0.1f); vecAttractors.push_back(pRegisteredAttractor); break; + case ATTRACTOR_STOP: pRegisteredAttractor = new CPedStopAttractor(pEffect, matrix, gcMaxSizeOfStopQueue, 1.0f, 30000.0f, 3000.0f, 0.2f, 0.1f, 0.1f, 0.1f); vecAttractors.push_back(pRegisteredAttractor); break; + case ATTRACTOR_PIZZA: pRegisteredAttractor = new CPedPizzaAttractor(pEffect, matrix, gcMaxSizeOfPizzaQueue, 1.0f, 30000.0f, 3000.0f, 0.2f, 0.1f, 0.1f, 0.1f); vecAttractors.push_back(pRegisteredAttractor); break; + case ATTRACTOR_SHELTER: pRegisteredAttractor = new CPedShelterAttractor(pEffect, matrix, gcMaxSizeOfShelterQueue, 1.0f, 30000.0f, 3000.0f, 0.5f, 6.28f, 0.1f, 0.1f); vecAttractors.push_back(pRegisteredAttractor); break; + case ATTRACTOR_ICECREAM: pRegisteredAttractor = new CPedIceCreamAttractor(pEffect, matrix, gcMaxSizeOfIceCreamQueue, 1.0f, 30000.0f, 3000.0f, 0.2f, 0.3f, 0.1f, 0.1f); vecAttractors.push_back(pRegisteredAttractor); break; + } + POP_MEMID(); + if (pRegisteredAttractor) + pRegisteredAttractor->RegisterPed(pPed); + return pRegisteredAttractor; +} + +bool CPedAttractorManager::DeRegisterPed(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors) +{ + if (!pAttractor) + return false; + CPedAttractor* pFound = nil; + for (std::vector::const_iterator pAttractorIt = vecAttractors.begin(); pAttractorIt != vecAttractors.end(); ++pAttractorIt) { + if (*pAttractorIt == pAttractor) { + pFound = *pAttractorIt; + break; + } + } + if (!pFound) + return false; + pFound->DeRegisterPed(pPed); + if (pFound->GetNoOfRegisteredPeds() != 0) + return true; + for (std::vector::iterator pAttractorIt = vecAttractors.begin(); pAttractorIt != vecAttractors.end(); ++pAttractorIt) { + if (*pAttractorIt == pAttractor) { + vecAttractors.erase(pAttractorIt); + break; + } + } + delete pAttractor; + return true; +} + +bool CPedAttractorManager::BroadcastArrival(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors) +{ + if (!pAttractor) + return false; + CPedAttractor* pFound = nil; + for (std::vector::const_iterator pAttractorIt = vecAttractors.begin(); pAttractorIt != vecAttractors.end(); ++pAttractorIt) { + if (*pAttractorIt == pAttractor) { + pFound = *pAttractorIt; + break; + } + } + if (!pFound) + return false; + pFound->BroadcastArrival(pPed); + return true; +} + +bool CPedAttractorManager::BroadcastDeparture(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors) +{ + if (!pAttractor) + return false; + CPedAttractor* pFound = nil; + for (std::vector::const_iterator pAttractorIt = vecAttractors.begin(); pAttractorIt != vecAttractors.end(); ++pAttractorIt) { + if (*pAttractorIt == pAttractor) { + pFound = *pAttractorIt; + break; + } + } + if (!pFound) + return false; + pFound->DeRegisterPed(pPed); + if (pFound->GetNoOfRegisteredPeds() != 0) + return true; + for (std::vector::iterator pAttractorIt = vecAttractors.begin(); pAttractorIt != vecAttractors.end(); ++pAttractorIt) { + if (*pAttractorIt == pAttractor) { + vecAttractors.erase(pAttractorIt); + break; + } + } + delete pAttractor; + return true; +} + +bool CPedAttractorManager::IsInQueue(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors) +{ + if (!pAttractor) + return false; + for (std::vector::const_iterator pAttractorIt = vecAttractors.begin(); pAttractorIt != vecAttractors.end(); ++pAttractorIt) { + if (*pAttractorIt == pAttractor) { + return (*pAttractorIt)->IsInQueue(pPed); + } + } + return false; +} + +bool CPedAttractorManager::IsAtHeadOfQueue(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors) +{ + if (!pAttractor) + return false; + for (std::vector::const_iterator pAttractorIt = vecAttractors.begin(); pAttractorIt != vecAttractors.end(); ++pAttractorIt) { + if (*pAttractorIt == pAttractor) { + return (*pAttractorIt)->IsAtHeadOfQueue(pPed); + } + } + return false; +} + +bool CPedAttractorManager::IsPedRegistered(CPed* pPed, std::vector& vecAttractors) +{ + for (std::vector::const_iterator pAttractorIt = vecAttractors.begin(); pAttractorIt != vecAttractors.end(); ++pAttractorIt) { + if ((*pAttractorIt)->IsRegisteredWithPed(pPed)) + return true; + } + return false; +} + +bool CPedAttractorManager::IsApproachable(C2dEffect* pEffect, const CMatrix& matrix, int32, CPed* pPed) +{ + if (pEffect->pedattr.type == ATTRACTOR_SHELTER) { + CVector pos; + ComputeEffectPos(pEffect, matrix, pos); + return CWorld::GetIsLineOfSightClear(pPed->GetPosition(), pos, true, false, false, false, false, false); + } + CVector vecUseDir, vecEffectPos; + ComputeEffectUseDir(pEffect, matrix, vecUseDir); + ComputeEffectPos(pEffect, matrix, vecEffectPos); + float dp = -DotProduct(vecUseDir, vecEffectPos); + if (pEffect->pedattr.type == ATTRACTOR_ATM || pEffect->pedattr.type == ATTRACTOR_PIZZA || pEffect->pedattr.type == ATTRACTOR_ICECREAM) { + vecUseDir = -vecUseDir; + dp = -dp; + } + if (dp + DotProduct(vecEffectPos, pPed->GetPosition()) > 0.0f) { + CVector vecPedToAttractor = pPed->GetPosition() - vecEffectPos; + vecPedToAttractor.Normalise(); + if (DotProduct(vecPedToAttractor, vecUseDir) > 0.25f && CWorld::IsWanderPathClear(pPed->GetPosition(), vecEffectPos, 2.0f, 0)) + return true; + } + return false; +} diff --git a/src/miami/peds/PedAttractor.h b/src/miami/peds/PedAttractor.h new file mode 100644 index 00000000..c55e4028 --- /dev/null +++ b/src/miami/peds/PedAttractor.h @@ -0,0 +1,196 @@ +#pragma once +#include "common.h" +#include + +#include "2dEffect.h" +#include "Ped.h" + +#define NUM_ATTRACTORS_FOR_ICECREAM_VAN 4 + +class CPedAttractor; + +class CVehicleToEffect +{ + CVehicle* m_pVehicle; + C2dEffect m_effects[NUM_ATTRACTORS_FOR_ICECREAM_VAN]; + +public: + CVehicleToEffect(CVehicle* pVehicle); + const C2dEffect* ChooseEffect(const CVector& pos) const; + CVehicleToEffect& From(const CVehicleToEffect& other); + CVehicleToEffect& operator=(const CVehicleToEffect& other) { return From(other); } + ~CVehicleToEffect() { m_pVehicle = nil; } + CVehicle* GetVehicle() const { return m_pVehicle; } + bool HasThisEffect(C2dEffect* pEffect) const; + const C2dEffect* GetEffect(int32 i) const { return &m_effects[i]; } +}; + +class CPedAttractorManager +{ + std::vector vAtmAttractors; + std::vector vSeatAttractors; + std::vector vStopAttractors; + std::vector vPizzaAttractors; + std::vector vShelterAttractors; + std::vector vIceCreamAttractors; + std::vector vVehicleToEffect; + +public: + CPedAttractor* RegisterPedWithAttractor(CPed* pPed, C2dEffect* pEffect, const CMatrix& matrix); + CPedAttractor* RegisterPed(CPed* pPed, C2dEffect* pEffect, const CMatrix& matrix, std::vector& vecAttractors); + bool BroadcastArrival(CPed* pPed, CPedAttractor* pAttractor); + bool BroadcastArrival(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors); + const C2dEffect* GetEffectForIceCreamVan(CVehicle* pVehicle, const CVector& pos); + bool IsApproachable(C2dEffect* pEffect, const CMatrix& matrix, int32, CPed* pPed); + void RemoveIceCreamVanEffects(C2dEffect* pEffect); + bool HasEmptySlot(const C2dEffect* pEffect); + const CPedAttractor* FindAssociatedAttractor(const C2dEffect* pEffect, std::vector& vecAttractors); + bool IsInQueue(CPed* pPed, CPedAttractor* pAttractor); + bool IsInQueue(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors); + bool IsAtHeadOfQueue(CPed* pPed, CPedAttractor* pAttractor); + bool IsAtHeadOfQueue(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors); + bool BroadcastDeparture(CPed* pPed, CPedAttractor* pAttractor); + bool BroadcastDeparture(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors); + bool DeRegisterPed(CPed* pPed, CPedAttractor* pAttractor); + bool DeRegisterPed(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors); + bool IsPedRegisteredWithEffect(CPed* pPed); + bool IsPedRegistered(CPed* pPed, std::vector& vecAttractors); + CVehicle* GetIceCreamVanForEffect(C2dEffect* pEffect); + + static void ComputeEffectPos(const C2dEffect* pEffect, const CMatrix& matrix, CVector& pos); + static void ComputeEffectQueueDir(const C2dEffect* pEffect, const CMatrix& matrix, CVector& pos); + static void ComputeEffectUseDir(const C2dEffect* pEffect, const CMatrix& matrix, CVector& pos); + +}; + +CPedAttractorManager* GetPedAttractorManager(); + +enum ePedAttractorType +{ + ATTRACTOR_ATM = 0, + ATTRACTOR_SEAT, + ATTRACTOR_STOP, + ATTRACTOR_PIZZA, + ATTRACTOR_SHELTER, + ATTRACTOR_ICECREAM +}; + +class CPedAttractor +{ +protected: + C2dEffect* p2dEffect; + std::vector vApproachingQueue; + std::vector vWaitingQueue; + int32 m_nMaxPedsInAttractor; + float m_fQueueDistance; + float m_fTimeInWaitQueue; + float m_fTimeInApproachingQueue; + float m_fDistanceToUseAttractor; + float m_fAcceptableHeading; + float m_fMaxPositionDisplacement; + float m_fMaxHeadingDisplacement; + CVector vecEffectPos; + CVector vecQueueDir; + CVector vecUseDir; + +public: + virtual float GetHeadOfQueueWaitTime() { return 0.0f; } + virtual ~CPedAttractor() {}; + virtual ePedAttractorType GetType() const = 0; + virtual void UpdatePedStateOnDeparture(CPed* pPed) const = 0; + virtual bool IsAtHeadOfQueue(CPed* pPed) const { return vWaitingQueue.front() == pPed; } + virtual void ComputeAttractPos(int32 id, CVector& pos) const; + virtual void ComputeAttractHeading(int32 id, float& pHeading) const; + virtual bool BroadcastDeparture(CPed* pPed); + + bool IsRegisteredWithPed(CPed* pPed) const; + bool DeRegisterPed(CPed* pPed); + float ComputeDeltaHeading() const; + float ComputeDeltaPos() const; + void ComputeAttractTime(int32 id, bool, float& time) const; + int32 GetNoOfRegisteredPeds() const { return (int32)(vWaitingQueue.size() + vApproachingQueue.size()); } + int32 ComputeFreeSlot() const { return (int32)vWaitingQueue.size(); } + bool IsInQueue(CPed*) const; + bool RegisterPed(CPed*); + bool BroadcastArrival(CPed*); + + CPedAttractor(C2dEffect* pEffect, const CMatrix& matrix, int32 maxpeds, float qdist, float waitTime, float approachTime, float distance, float headingdiff, float posdisp, float headdisp); + + C2dEffect* GetEffect() const { return p2dEffect; } + const CVector& GetEffectPos() const { return vecEffectPos; } + int32 GetMaxPedsInAttractor() const { return m_nMaxPedsInAttractor; } + float GetDistanceToCountSeekDone() const { return m_fDistanceToUseAttractor; } + float GetAcceptableHeading() const { return m_fAcceptableHeading; } +}; + +class CPedAtmAttractor : public CPedAttractor +{ +public: + virtual ePedAttractorType GetType() const { return ATTRACTOR_ATM; }; + virtual void UpdatePedStateOnDeparture(CPed* pPed) const; + CPedAtmAttractor(C2dEffect* pEffect, const CMatrix& matrix, int32 maxpeds, float qdist, float waitTime, float approachTime, float distance, float headingdiff, float posdisp, float headdisp) : + CPedAttractor(pEffect, matrix, maxpeds, qdist, waitTime, approachTime, distance, headingdiff, posdisp, headdisp) + {}; +}; + +class CPedIceCreamAttractor : public CPedAttractor +{ +public: + virtual ~CPedIceCreamAttractor() { GetPedAttractorManager()->RemoveIceCreamVanEffects(p2dEffect); } + virtual ePedAttractorType GetType() const { return ATTRACTOR_ICECREAM; } + virtual void UpdatePedStateOnDeparture(CPed* pPed) const {}; + CPedIceCreamAttractor(C2dEffect* pEffect, const CMatrix& matrix, int32 maxpeds, float qdist, float waitTime, float approachTime, float distance, float headingdiff, float posdisp, float headdisp) : + CPedAttractor(pEffect, matrix, maxpeds, qdist, waitTime, approachTime, distance, headingdiff, posdisp, headdisp) + {}; +}; + +class CPedPizzaAttractor : public CPedAttractor +{ +public: + virtual float GetHeadOfQueueWaitTime() { return 2000.0f; } + virtual ePedAttractorType GetType() const { return ATTRACTOR_PIZZA; } + virtual void UpdatePedStateOnDeparture(CPed* pPed) const; + CPedPizzaAttractor(C2dEffect* pEffect, const CMatrix& matrix, int32 maxpeds, float qdist, float waitTime, float approachTime, float distance, float headingdiff, float posdisp, float headdisp) : + CPedAttractor(pEffect, matrix, maxpeds, qdist, waitTime, approachTime, distance, headingdiff, posdisp, headdisp) + {}; +}; + +class CPedSeatAttractor : public CPedAttractor +{ +public: + virtual ePedAttractorType GetType() const { return ATTRACTOR_SEAT; } + virtual void UpdatePedStateOnDeparture(CPed* pPed) const {}; + CPedSeatAttractor(C2dEffect* pEffect, const CMatrix& matrix, int32 maxpeds, float qdist, float waitTime, float approachTime, float distance, float headingdiff, float posdisp, float headdisp) : + CPedAttractor(pEffect, matrix, maxpeds, qdist, waitTime, approachTime, distance, headingdiff, posdisp, headdisp) + {}; +}; + +class CPedShelterAttractor : public CPedAttractor +{ + static std::vector ms_displacements; +public: + virtual ePedAttractorType GetType() const { return ATTRACTOR_SHELTER; } + virtual bool BroadcastDeparture(CPed*); + virtual void UpdatePedStateOnDeparture(CPed* pPed) const {}; + virtual bool IsAtHeadOfQueue(CPed* pPed) const { return true; } + virtual void ComputeAttractPos(int qid, CVector& pos) const; + virtual void ComputeAttractHeading(int qid, float& heading) const; + + CPedShelterAttractor(C2dEffect* pEffect, const CMatrix& matrix, int32 maxpeds, float qdist, float waitTime, float approachTime, float distance, float headingdiff, float posdisp, float headdisp) : + CPedAttractor(pEffect, matrix, maxpeds, qdist, waitTime, approachTime, distance, headingdiff, posdisp, headdisp) + {}; + + + CVector GetDisplacement(int32 qid) const; +}; + +class CPedStopAttractor : public CPedAttractor +{ +public: + virtual ePedAttractorType GetType() const { return ATTRACTOR_STOP; } + virtual void UpdatePedStateOnDeparture(CPed* pPed) const {}; + + CPedStopAttractor(C2dEffect* pEffect, const CMatrix& matrix, int32 maxpeds, float qdist, float waitTime, float approachTime, float distance, float headingdiff, float posdisp, float headdisp) : + CPedAttractor(pEffect, matrix, maxpeds, qdist, waitTime, approachTime, distance, headingdiff, posdisp, headdisp) + {}; +}; \ No newline at end of file diff --git a/src/miami/peds/PedChat.cpp b/src/miami/peds/PedChat.cpp new file mode 100644 index 00000000..ec6719c6 --- /dev/null +++ b/src/miami/peds/PedChat.cpp @@ -0,0 +1,153 @@ +#include "common.h" +#include "Camera.h" +#include "DMAudio.h" +#include "General.h" +#include "Ped.h" + +// Corresponds to ped sounds (from SOUND_PED_DEATH to SOUND_PED_TAXI_CALL) +PedAudioData CommentWaitTime[56] = { + { 500, 800, 500, 2 }, + { 500, 800, 500, 2 }, + { 500, 800, 500, 2 }, + { 500, 800, 500, 2 }, + { 100, 2, 100, 2 }, + { 500, 500, 2000, 1000 }, + { 2000, 50, 2050, 1000 }, + { 5000, 2000, 7000, 3000 }, + { 5000, 2000, 7000, 3000 }, + { 300, 200, 500, 200 }, + { 3000, 1000, 4000, 1000 }, + { 6000, 6000, 6000, 6000 }, + { 4000, 1000, 5000, 1000 }, + { 3000, 1000, 4000, 1000 }, + { 1000, 1000, 2000, 2000 }, + { 1000, 500, 2000, 1500 }, + { 1700, 1000, 3000, 1000 }, + { 800, 200, 1000, 500 }, + { 800, 200, 1000, 500 }, + { 800, 400, 2000, 1000 }, + { 800, 400, 2000, 1000 }, + { 2000, 2000, 4000, 4000 }, + { 2000, 2000, 4000, 1000 }, + { 4000, 1000, 5000, 1000 }, + { 800, 400, 1200, 500 }, + { 5000, 1000, 6000, 2000 }, + { 5000, 1000, 6000, 2000 }, + { 5000, 1000, 6000, 2000 }, + { 5000, 1000, 6000, 2000 }, + { 5000, 1000, 6000, 2000 }, + { 5000, 1000, 6000, 2000 }, + { 5000, 1000, 6000, 2000 }, + { 4000, 2000, 7000, 2000 }, + { 1000, 300, 2000, 1000 }, + { 1500, 1000, 2500, 1000 }, + { 200, 200, 200, 200 }, + { 2000, 1000, 4000, 1000 }, + { 2000, 1000, 4000, 1000 }, + { 1000, 500, 3000, 1000 }, + { 1000, 500, 1000, 1000 }, + { 3000, 2000, 5000, 1000 }, + { 3000, 2000, 5000, 1000 }, + { 3000, 2000, 3000, 2000 }, + { 2000, 1000, 3000, 1000 }, + { 2500, 1000, 5000, 5000 }, + { 2000, 1000, 3000, 2000 }, + { 4000, 1000, 5000, 1000 }, + { 1000, 500, 2000, 4000 }, + { 1000, 500, 2000, 5000 }, + { 2000, 500, 2500, 500 }, + { 1000, 500, 3000, 2000 }, + { 1600, 1000, 2000, 2000 }, + { 2000, 1000, 4000, 2000 }, + { 1500, 1000, 2500, 1000 }, + { 1000, 1000, 5000, 5000 }, + { 0, 0, 0, 0 } +}; + +bool +CPed::ServiceTalkingWhenDead(void) +{ + return m_queuedSound == SOUND_PED_DEATH; +} + +void +CPed::ServiceTalking(void) +{ + if (bBodyPartJustCameOff && m_bodyPartBleeding == PED_HEAD) + return; + + if (!CGame::germanGame && m_pFire) + m_queuedSound = SOUND_PED_BURNING; + + if (m_queuedSound != SOUND_NO_SOUND) { + if (m_queuedSound == SOUND_PED_DEATH) + m_soundStart = CTimer::GetTimeInMilliseconds() - 1; + + if (CTimer::GetTimeInMilliseconds() > m_soundStart) { + DMAudio.PlayOneShot(m_audioEntityId, m_queuedSound, 1.0f); + m_lastSoundStart = CTimer::GetTimeInMilliseconds(); + m_soundStart = + CommentWaitTime[m_queuedSound - SOUND_PED_DEATH].m_nFixedDelayTime + + CTimer::GetTimeInMilliseconds() + + CGeneral::GetRandomNumberInRange(0, CommentWaitTime[m_queuedSound - SOUND_PED_DEATH].m_nOverrideFixedDelayTime); + + if (m_queuedSound == SOUND_PED_PLAYER_BEFORESEX && IsPlayer()) + m_soundStart += 2000; + + m_lastQueuedSound = m_queuedSound; + m_queuedSound = SOUND_NO_SOUND; + } + } +} + +void +CPed::Say(uint16 audio) +{ + if (3.0f + TheCamera.GetPosition().z < GetPosition().z) + return; + + if (TheCamera.m_CameraAverageSpeed > 1.65f) { + if (audio != SOUND_PED_DAMAGE && audio != SOUND_PED_HIT && audio != SOUND_PED_LAND) + return; + + } else if (TheCamera.m_CameraAverageSpeed > 1.25f) { + if (audio != SOUND_PED_DEATH && + audio != SOUND_PED_DAMAGE && audio != SOUND_PED_HIT && audio != SOUND_PED_LAND && + audio != SOUND_PED_TAXI_WAIT && audio != SOUND_PED_EVADE) + return; + + } else if (TheCamera.m_CameraAverageSpeed > 0.9f) { + switch (audio) { + case SOUND_PED_DEATH: + case SOUND_PED_DAMAGE: + case SOUND_PED_HIT: + case SOUND_PED_LAND: + case SOUND_PED_BURNING: + case SOUND_PED_FLEE_SPRINT: + case SOUND_PED_TAXI_WAIT: + case SOUND_PED_EVADE: + case SOUND_PED_CRASH_VEHICLE: + case SOUND_PED_CRASH_CAR: + case SOUND_PED_ANNOYED_DRIVER: + break; + default: + return; + } + } + + if (audio < m_queuedSound) { + if (audio != m_lastQueuedSound || audio == SOUND_PED_DEATH + + // See VC Ped Speech patch +#ifdef FIX_BUGS + || CommentWaitTime[audio - SOUND_PED_DEATH].m_nOverrideMaxRandomDelayTime + + (uint32)CGeneral::GetRandomNumberInRange(0, CommentWaitTime[audio - SOUND_PED_DEATH].m_nMaxRandomDelayTime) +#else + || CommentWaitTime[m_queuedSound - SOUND_PED_DEATH].m_nOverrideMaxRandomDelayTime + + (uint32)CGeneral::GetRandomNumberInRange(0, CommentWaitTime[m_queuedSound - SOUND_PED_DEATH].m_nMaxRandomDelayTime) +#endif + + m_lastSoundStart <= CTimer::GetTimeInMilliseconds()) { + m_queuedSound = audio; + } + } +} \ No newline at end of file diff --git a/src/miami/peds/PedDebug.cpp b/src/miami/peds/PedDebug.cpp new file mode 100644 index 00000000..0dbabb58 --- /dev/null +++ b/src/miami/peds/PedDebug.cpp @@ -0,0 +1,349 @@ +#include "common.h" +#ifndef MASTER +#include "main.h" +#include "Camera.h" +#include "Font.h" +#include "Ped.h" +#include "Sprite.h" +#include "Text.h" + +static char ObjectiveText[][28] = { + "No Obj", + "Wait on Foot", + "Wait on Foot for cop", + "Flee on Foot Till Safe", + "Guard Spot", + "Guard Area", + "Wait in Car", + "Wait in Car then Getout", + "Kill Char on Foot", + "Kill Char Any Means", + "Flee Char on Foot Till Safe", + "Flee Char on Foot Always", + "GoTo Char on Foot", + "GoTo Char on Foot walking", + "Hassle char", + "Follow Char in Formation", + "Leave Car", + "Enter Car as Passenger", + "Enter Car as Driver", + "Follow Car in Car", + "Fire at Obj from Vehicle", + "Destroy Obj", + "Destroy Car", + "GoTo Area Any Means", + "GoTo Area on Foot", + "Run to Area", + "GoTo Area in Car", + "Follow Car on Foot Woffset", + "Guard Attack", + "Set Leader", + "Follow Route", + "Solicit vehicle", + "Take Taxi", + "Catch Train", + "Buy IceCream", + "Steal Any Car", + "Steal any mission car", + "Mug Char", + "Lv car die", + "Goto seat", + "Goto atm", + "Flee car", + "Sunbathe", + "Goto bus stop", + "Goto pizza", + "Goto shelter", + "Aim gun at", + "Wander", + "Wait on foot at shltr", + "Sprint to area", + "Kill char on boat", + "Solicit ped", + "Wait at bus stop", + "Goto ice cream van foot", + "Wait foot icecream van" +}; + +static char StateText[][18] = { + "None", + "Idle", + "Look Entity", + "Look Heading", + "Wander Range", + "Wander Path", + "Seek Pos", + "Seek Entity", + "Flee Pos", + "Flee Entity", + "Pursue", + "Follow Path", + "Sniper Mode", + "Rocket Mode", + "Dummy", + "Pause", + "Attack", + "Fight", + "Face Phone", + "Make Call", + "Chat", + "Mug", + "AimGun", + "AI Control", + "Seek Car", + "Seek InBoat", + "Follow Route", + "C.P.R.", + "Solicit", + "Buy IceCream", + "Investigate", + "Step away", + "On Fire", + "Bathe", + "Flash", + "Jog", + "Answer mobile", + "Hang out", + "STATES_NO_AI", + "Abseil", + "Sit", + "Jump", + "Fall", + "GetUp", + "Stagger", + "Dive away", + "STATES_NO_ST", + "Enter Train", + "Exit Train", + "Arrest Plyr", + "Driving", + "Passenger", + "Taxi Passngr", + "Open Door", + "Die", + "Dead", + "CarJack", + "Drag fm Car", + "Enter Car", + "Steal Car", + "Exit Car", + "Hands Up", + "Arrested", + "Deply stgr" +}; + +static char PersonalityTypeText[][18] = { + "Player", + "Cop", + "Medic", + "Fireman", + "Gang 1", + "Gang 2", + "Gang 3", + "Gang 4", + "Gang 5", + "Gang 6", + "Gang 7", + "Street Guy", + "Suit Guy", + "Sensible Guy", + "Geek Guy", + "Old Guy", + "Tough Guy", + "Street Girl", + "Suit Girl", + "Sensible Girl", + "Geek Girl", + "Old Girl", + "Tough Girl", + "Tramp", +#ifdef FIX_BUGS // there's male and female ones + "Tramp", +#endif + "Tourist", + "Prostitute", + "Criminal", + "Busker", + "Taxi Driver", + "Psycho", + "Steward", + "Sports Fan", +}; + +static char WaitStateText[][16] = { + "No Wait", + "Traffic Lights", + "Pause CrossRoad", + "Look CrossRoad", + "Look Ped", + "Look Shop", + "Look Accident", + "FaceOff Gang", + "Double Back", + "Hit Wall", + "Turn 180deg", + "Surprised", + "Ped Stuck", + "Look About", + "Play Duck", + "Play Cower", + "Play Taxi", + "Play HandsUp", + "Play HandsCower", + "Play Chat", + "Finish Flee", + "Sit down", + "Sit down rvrs", + "Sit up", + "Sit idle", + "Use atm", + "Sunbth pre", + "Sunbth down", + "Sunbth idle", + "Riot", + "Fast fall", + "Bomber", + "Stripper", + "Ground attack", + "Lance sitting", + "Handsup simple" +}; + +void +CPed::DebugDrawPedDestination(CPed *, int, int) +{ +#ifndef FINAL + // TODO: something was here +#endif // !FINAL +} + +void +CPed::DebugDrawPedDesiredHeading(CPed *, int, int) +{ +#ifndef FINAL + // TODO: something was here +#endif // !FINAL +} + +void +CPed::DebugDrawCollisionRadius(float, float, float, float, int) +{ +#ifndef FINAL + // TODO: something was here +#endif // !FINAL +} + +void +CPed::DebugDrawVisionRange(CVector a1, float) +{ + for (int i = a1.x - 90; i < a1.x + 89; i += 30) { +#ifndef FINAL + // TODO: something was here +#endif // !FINAL + } +} + +void +CPed::DebugDrawVisionSimple(CVector, float) +{ +#ifndef FINAL + // TODO: something was here +#endif // !FINAL +} + +void +CPed::DebugDrawLook() +{ +#ifndef FINAL + // TODO: something was here +#endif // !FINAL +} + +void +CPed::DebugDrawPedPsyche() +{ +#ifndef FINAL + // TODO: something was here +#endif // !FINAL +} + +void +CPed::DebugDrawDebugLines() +{ +#ifndef FINAL + // TODO: something was here +#endif // !FINAL +} + +int nDisplayDebugInfo = 0; + +void +CPed::SwitchDebugDisplay(void) +{ + if (++nDisplayDebugInfo > 2) + nDisplayDebugInfo = 0; +} + +int +CPed::GetDebugDisplay(void) +{ + return nDisplayDebugInfo; +} + +void +CPed::DebugDrawLookAtPoints() +{ + // TODO: mobile code +} + +void +CPed::DebugRenderOnePedText(void) +{ + if ((GetPosition() - TheCamera.GetPosition()).MagnitudeSqr() < sq(30.0f)) { + float width, height; + RwV3d screenCoords; + CVector bitAbove = GetPosition(); + bitAbove.z += 2.0f; + if (CSprite::CalcScreenCoors(bitAbove, &screenCoords, &width, &height, true)) { + + float lineHeight = SCREEN_SCALE_Y(Min(height / 100.0f, 0.7f) * 22.0f); + + DefinedState(); + CFont::SetPropOn(); + CFont::SetBackgroundOn(); + + // Originally both of them were being divided by 60.0f. + float xScale = Min(width / 240.0f, 0.7f); + float yScale = Min(height / 80.0f, 0.7f); + + CFont::SetScale(SCREEN_SCALE_X(xScale), SCREEN_SCALE_Y(yScale)); + CFont::SetCentreOn(); + CFont::SetCentreSize(SCREEN_WIDTH); + CFont::SetJustifyOff(); + CFont::SetColor(CRGBA(255, 255, 0, 255)); + CFont::SetBackGroundOnlyTextOn(); + CFont::SetFontStyle(1); + AsciiToUnicode(StateText[m_nPedState], gUString); + CFont::PrintString(screenCoords.x, screenCoords.y, gUString); + AsciiToUnicode(ObjectiveText[m_objective], gUString); + CFont::PrintString(screenCoords.x, screenCoords.y + lineHeight, gUString); + AsciiToUnicode(PersonalityTypeText[m_pedStats->m_type], gUString); + CFont::PrintString(screenCoords.x, screenCoords.y + 2 * lineHeight, gUString); + AsciiToUnicode(WaitStateText[m_nWaitState], gUString); + CFont::PrintString(screenCoords.x, screenCoords.y + 3 * lineHeight, gUString); + if (m_nPedState == PED_SEEK_POS || m_nPedState == PED_SEEK_ENTITY) { + sprintf(gString, "Safe distance to target: %.2f", m_distanceToCountSeekDone); + AsciiToUnicode(gString, gUString); + CFont::PrintString(screenCoords.x, screenCoords.y + 4 * lineHeight, gUString); + } + DefinedState(); + } + } +} + +void +CPed::DebugRenderClosePedText() +{ + // TODO: mobile code +} +#endif \ No newline at end of file diff --git a/src/miami/peds/PedFight.cpp b/src/miami/peds/PedFight.cpp new file mode 100644 index 00000000..43ded57a --- /dev/null +++ b/src/miami/peds/PedFight.cpp @@ -0,0 +1,4203 @@ +#include "common.h" + +#include "main.h" +#include "RpAnimBlend.h" +#include "AnimBlendClumpData.h" +#include "AnimBlendAssociation.h" +#include "Camera.h" +#include "CarCtrl.h" +#include "Darkel.h" +#include "DMAudio.h" +#include "FileMgr.h" +#include "General.h" +#include "Object.h" +#include "Pad.h" +#include "Particle.h" +#include "Ped.h" +#include "PlayerPed.h" +#include "Stats.h" +#include "TempColModels.h" +#include "VisibilityPlugins.h" +#include "Vehicle.h" +#include "Automobile.h" +#include "WaterLevel.h" +#include "World.h" +#include "Bike.h" +#include "Glass.h" +#include "SpecialFX.h" + +uint16 nPlayerInComboMove; +RpClump* flyingClumpTemp; + +FightMove tFightMoves[NUM_FIGHTMOVES] = +{ + { ANIM_STD_NUM, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, + { ANIM_STD_PUNCH, 0.2f, 8.f/30.f, 0.0f, 0.3f, 1.0f, HITLEVEL_HIGH, 1, 0 }, + { ANIM_STD_FIGHT_IDLE, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, + { ANIM_STD_FIGHT_SHUFFLE_F, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, + { ANIM_STD_FIGHT_KNEE, 4.f/30.f, 0.2f, 0.0f, 0.6f, 1.0f, HITLEVEL_LOW, 2, 0 }, + { ANIM_STD_FIGHT_LHOOK, 8.f/30.f, 10.f/30.f, 0.0f, 0.4f, 1.0f, HITLEVEL_HIGH, 3, 0 }, + { ANIM_STD_FIGHT_JAB, 4.f/30.f, 0.2f, 0.0f, 0.7f, 1.0f, HITLEVEL_HIGH, 3, 0 }, + { ANIM_STD_FIGHT_PUNCH, 4.f/30.f, 7.f/30.f, 10.f/30.f, 0.4f, 1.0f, HITLEVEL_HIGH, 1, 0 }, + { ANIM_STD_FIGHT_LONGKICK, 8.f/30.f, 10.f/30.f, 0.0f, 0.5f, 1.0f, HITLEVEL_MEDIUM, 4, 0 }, + { ANIM_STD_FIGHT_ROUNDHOUSE, 8.f/30.f, 10.f/30.f, 0.0f, 0.6f, 1.0f, HITLEVEL_MEDIUM, 4, 0 }, + { ANIM_STD_FIGHT_KICK, 8.f/30.f, 10.f/30.f, 0.0f, 0.5f, 1.0f, HITLEVEL_HIGH, 2, 0 }, + { ANIM_STD_FIGHT_HEAD, 8.f/30.f, 10.f/30.f, 0.0f, 0.5f, 1.0f, HITLEVEL_MEDIUM, 2, 0 }, + { ANIM_STD_FIGHT_BKICK_L, 8.f/30.f, 10.f/30.f, 0.0f, 0.5f, 1.0f, HITLEVEL_LOW, 2, 0 }, + { ANIM_STD_FIGHT_BKICK_L, 8.f/30.f, 10.f/30.f, 0.0f, 0.5f, 1.0f, HITLEVEL_LOW, 2, 0 }, + { ANIM_STD_FIGHT_ELBOW_L, 8.f/30.f, 10.f/30.f, 0.0f, 0.5f, 1.0f, HITLEVEL_MEDIUM, 2, 0 }, + { ANIM_STD_FIGHT_BKICK_R, 8.f/30.f, 10.f/30.f, 0.0f, 0.5f, 1.0f, HITLEVEL_MEDIUM, 2, 0 }, + { ANIM_STD_FIGHT_ELBOW_R, 8.f/30.f, 10.f/30.f, 0.0f, 0.5f, 1.0f, HITLEVEL_HIGH, 2, 0 }, + { ANIM_STD_KICKGROUND, 10.f/30.f, 14.f/30.f, 0.0f, 0.4f, 1.0f, HITLEVEL_GROUND, 1, 0 }, + { ANIM_STD_HIT_FRONT, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, + { ANIM_STD_HIT_BACK, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, + { ANIM_STD_HIT_RIGHT, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, + { ANIM_STD_HIT_LEFT, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, + { ANIM_STD_HIT_BODYBLOW, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, + { ANIM_STD_HIT_CHEST, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, + { ANIM_STD_HIT_HEAD, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, + { ANIM_STD_HIT_WALK, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, + { ANIM_STD_HIT_FLOOR, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, + { ANIM_STD_HIT_BEHIND, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, + { ANIM_ATTACK_1, 4.f/30.f, 7.f/30.f, 10.f/30.f, 0.4f, 1.0f, HITLEVEL_HIGH, 1, 0 }, + { ANIM_ATTACK_2, 4.f/30.f, 7.f/30.f, 10.f/30.f, 0.4f, 1.0f, HITLEVEL_HIGH, 1, 0 }, + { ANIM_ATTACK_3, 4.f / 30.f, 7.f / 30.f, 10.f / 30.f, 0.4f, 1.0f, HITLEVEL_HIGH, 1, 0 }, + { ANIM_STD_FIGHT_2IDLE, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 } +}; + +static PedOnGroundState +CheckForPedsOnGroundToAttack(CPed *attacker, CPed **pedOnGround) +{ + PedOnGroundState stateToReturn; + float angleToFace; + CPed *currentPed = nil; + PedState currentPedState; + CPed *pedOnTheFloor = nil; + CPed *deadPed = nil; + CPed *pedBelow = nil; + bool foundDead = false; + bool foundOnTheFloor = false; + bool foundBelow = false; + float angleDiff; + float distance; + + if (!CGame::nastyGame) + return NO_PED; + + for (int currentPedId = 0; currentPedId < attacker->m_numNearPeds; currentPedId++) { + + currentPed = attacker->m_nearPeds[currentPedId]; + + CVector posDifference = currentPed->GetPosition() - attacker->GetPosition(); + distance = posDifference.Magnitude(); + + if (distance < 2.0f) { + angleToFace = CGeneral::GetRadianAngleBetweenPoints( + currentPed->GetPosition().x, currentPed->GetPosition().y, + attacker->GetPosition().x, attacker->GetPosition().y); + + angleToFace = CGeneral::LimitRadianAngle(angleToFace); + attacker->m_fRotationCur = CGeneral::LimitRadianAngle(attacker->m_fRotationCur); + + angleDiff = Abs(angleToFace - attacker->m_fRotationCur); + + if (angleDiff > PI) + angleDiff = 2 * PI - angleDiff; + + currentPedState = currentPed->m_nPedState; + + if (currentPed->OnGroundOrGettingUp()) { + if (distance < 2.0f && angleDiff < DEGTORAD(65.0f)) { + if (currentPedState == PED_DEAD) { + foundDead = 1; + if (!deadPed) + deadPed = currentPed; + } else if (!currentPed->IsPedHeadAbovePos(-0.6f)) { + foundOnTheFloor = 1; + if (!pedOnTheFloor) + pedOnTheFloor = currentPed; + } + } + } else if ((distance < 0.8f && angleDiff < DEGTORAD(75.0f)) + || (distance < 1.3f && angleDiff < DEGTORAD(55.0f)) + || (distance < 1.7f && angleDiff < DEGTORAD(35.0f)) + || (distance < 2.0f && angleDiff < DEGTORAD(30.0f))) { + + // Either this condition or below one was probably returning 4 early in development. See Fight(). + foundBelow = 1; + pedBelow = currentPed; + break; + } else { + if (angleDiff < DEGTORAD(75.0f)) { + foundBelow = 1; + if (!pedBelow) + pedBelow = currentPed; + } + } + } + } + + if (foundOnTheFloor) { + currentPed = pedOnTheFloor; + stateToReturn = PED_ON_THE_FLOOR; + } else if (foundDead) { + currentPed = deadPed; + stateToReturn = PED_DEAD_ON_THE_FLOOR; + } else if (foundBelow) { + currentPed = pedBelow; + stateToReturn = PED_IN_FRONT_OF_ATTACKER; + } else { + currentPed = nil; + stateToReturn = NO_PED; + } + + if (pedOnGround) + *pedOnGround = currentPed; + + return stateToReturn; +} + +void +CPed::SetPointGunAt(CEntity *to) +{ + if (to) { + SetLookFlag(to, true, true); + SetAimFlag(to); + SetLookTimer(INT32_MAX); + } + + CWeaponInfo* curWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + if (m_nPedState == PED_AIM_GUN || (bIsDucking && !IsPlayer()) || m_nWaitState == WAITSTATE_PLAYANIM_DUCK || curWeapon->m_AnimToPlay == ASSOCGRP_STD) + return; + + if (m_nPedState != PED_ATTACK) + SetStoredState(); + + SetPedState(PED_AIM_GUN); + bIsPointingGunAt = true; + SetMoveState(PEDMOVE_STILL); + + CAnimBlendAssociation *aimAssoc; + + if (bCrouchWhenShooting && bIsDucking && GetCrouchFireAnim(curWeapon)) { + aimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(curWeapon)); + } else { + aimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_FIRE); + } + + if (!aimAssoc || aimAssoc->blendDelta < 0.0f) { + if (bCrouchWhenShooting && bIsDucking && GetCrouchFireAnim(curWeapon)) { + aimAssoc = CAnimManager::BlendAnimation(GetClump(), curWeapon->m_AnimToPlay, GetCrouchFireAnim(curWeapon), 4.0f); + } else { + aimAssoc = CAnimManager::AddAnimation(GetClump(), curWeapon->m_AnimToPlay, ANIM_WEAPON_FIRE); + } + + aimAssoc->blendAmount = 0.0f; + aimAssoc->blendDelta = 8.0f; + } + if (to && !IsPlayer()) + Say(SOUND_PED_ATTACK); +} + +void +CPed::PointGunAt(void) +{ + CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + float animLoopStart = weaponInfo->m_fAnimLoopStart; + CAnimBlendAssociation *weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_FIRE); + if (!weaponAssoc || weaponAssoc->blendDelta < 0.0f) { + if (weaponInfo->IsFlagSet(WEAPONFLAG_CROUCHFIRE)) { + weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(weaponInfo)); + animLoopStart = weaponInfo->m_fAnim2LoopStart; + } + } + + if (weaponAssoc && weaponAssoc->currentTime > animLoopStart * 0.4f) { + weaponAssoc->SetCurrentTime(animLoopStart); + weaponAssoc->flags &= ~ASSOC_RUNNING; + + if (bIsDucking) + m_pedIK.m_flags &= ~CPedIK::AIMS_WITH_ARM; + + if (weaponInfo->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM)) + m_pedIK.m_flags |= CPedIK::AIMS_WITH_ARM; + else + m_pedIK.m_flags &= ~CPedIK::AIMS_WITH_ARM; + } +} + +void +CPed::ClearPointGunAt(void) +{ + CAnimBlendAssociation *animAssoc; + CWeaponInfo *weaponInfo; + + ClearLookFlag(); + ClearAimFlag(); + bIsPointingGunAt = false; + if (m_nPedState == PED_AIM_GUN || m_nPedState == PED_ATTACK) { + SetPedState(PED_IDLE); + RestorePreviousState(); + } + weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_FIRE); + if (!animAssoc || animAssoc->blendDelta < 0.0f) { + if (weaponInfo->IsFlagSet(WEAPONFLAG_CROUCHFIRE)) { + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(weaponInfo)); + } + } + if (animAssoc) { + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + animAssoc->blendDelta = -4.0f; + } +} + +void +CPed::SetAttack(CEntity *victim) +{ + CPed *victimPed = nil; + CWeaponInfo *curWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + CAnimBlendAssociation *animAssoc; + + if (victim && victim->IsPed()) + victimPed = (CPed*)victim; + + if (m_attackTimer > CTimer::GetTimeInMilliseconds() || m_nWaitState == WAITSTATE_SURPRISE || (bIsDucking && !bCrouchWhenShooting)) + return; + + if (curWeapon->IsFlagSet(WEAPONFLAG_RELOAD) && + (RpAnimBlendClumpGetAssociation(GetClump(), GetReloadAnim(curWeapon)) || RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchReloadAnim(curWeapon)))) { + if (!IsPlayer() || m_nPedState != PED_ATTACK || ((CPlayerPed*)this)->m_bHaveTargetSelected) + bIsAttacking = false; + else + bIsAttacking = true; + + return; + } + + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED || curWeapon->IsFlagSet(WEAPONFLAG_FIGHTMODE) || GetWeapon()->m_eWeaponType == WEAPONTYPE_BRASSKNUCKLE) { + if (IsPlayer() || + (m_nPedState != PED_FIGHT && m_nMoveState != PEDMOVE_NONE && m_nMoveState != PEDMOVE_STILL + && !(m_pedStats->m_flags & STAT_SHOPPING_BAGS) && curWeapon->IsFlagSet(WEAPONFLAG_PARTIALATTACK))) { + + if (m_nPedState != PED_ATTACK) { + SetPedState(PED_ATTACK); + bIsAttacking = false; + + CAnimBlendAssociation *animAssoc = CAnimManager::BlendAnimation(GetClump(), curWeapon->m_AnimToPlay, ANIM_MELEE_ATTACK_START, 8.0f); + animAssoc->SetRun(); + if (animAssoc->currentTime == animAssoc->hierarchy->totalLength) + animAssoc->SetCurrentTime(0.0f); + + animAssoc->SetFinishCallback(FinishedAttackCB, this); + } + } else { + StartFightAttack(CGeneral::GetRandomNumber()); + } + return; + } + + if (curWeapon->IsFlagSet(WEAPONFLAG_PARTIALATTACK) && + (IsPlayer() && ((CPlayerPed*)this)->m_fMoveSpeed >= 1.0f || + m_nMoveState == PEDMOVE_WALK || m_nMoveState == PEDMOVE_RUN)) { + + if (m_nPedState != PED_ATTACK) { + SetPedState(PED_ATTACK); + bIsAttacking = false; + CAnimBlendAssociation* animAssoc = CAnimManager::BlendAnimation(GetClump(), curWeapon->m_AnimToPlay, ANIM_MELEE_ATTACK_START, 8.0f); + animAssoc->SetRun(); + if (animAssoc->currentTime == animAssoc->hierarchy->totalLength) + animAssoc->SetCurrentTime(0.0f); + + animAssoc->SetFinishCallback(FinishedAttackCB, this); + } + return; + } + + if (m_pSeekTarget) + m_pSeekTarget->CleanUpOldReference(&m_pSeekTarget); + m_pSeekTarget = victim; + if (m_pSeekTarget) + m_pSeekTarget->RegisterReference((CEntity **) &m_pSeekTarget); + + if (curWeapon->IsFlagSet(WEAPONFLAG_CANAIM)) { + CVector aimPos = GetRight() * 0.1f + GetForward() * 0.2f + GetPosition(); + aimPos += GetUp() * 0.35f; + CEntity *obstacle = CWorld::TestSphereAgainstWorld(aimPos, 0.2f, nil, true, false, false, true, false, false); + if (obstacle) { + if(gaTempSphereColPoints[0].surfaceB != SURFACE_TRANSPARENT_CLOTH && gaTempSphereColPoints[0].surfaceB != SURFACE_METAL_CHAIN_FENCE && + gaTempSphereColPoints[0].surfaceB != SURFACE_WOOD_BENCH && gaTempSphereColPoints[0].surfaceB != SURFACE_SCAFFOLD_POLE) { + if (!IsPlayer()) { + bObstacleShowedUpDuringKillObjective = true; + m_shootTimer = 0; + SetAttackTimer(1500); + m_shotTime = CTimer::GetTimeInMilliseconds(); + } + return; + } + } + + m_pLookTarget = victim; + if (victim) { + m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); + m_pSeekTarget->RegisterReference((CEntity **) &m_pSeekTarget); + } + + if (m_pLookTarget) { + SetAimFlag(m_pLookTarget); +#ifdef FREE_CAM + } else if (this != FindPlayerPed() || !((CPlayerPed*)this)->m_bFreeAimActive) { +#else + } else { +#endif + if (this == FindPlayerPed() && TheCamera.Cams[0].Using3rdPersonMouseCam()) { + SetAimFlag(m_fRotationCur); + ((CPlayerPed*)this)->m_fFPSMoveHeading = TheCamera.Find3rdPersonQuickAimPitch(); + } else if (curWeapon->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM)) { + SetAimFlag(m_fRotationCur); + } + } + } +#ifdef FIX_BUGS + // fix aiming for flamethrower and minigun while using PC controls + else if (curWeapon->m_AnimToPlay == ASSOCGRP_FLAMETHROWER && TheCamera.Cams[0].Using3rdPersonMouseCam() && this == FindPlayerPed()) + { + SetAimFlag(m_fRotationCur); + ((CPlayerPed*)this)->m_fFPSMoveHeading = TheCamera.Find3rdPersonQuickAimPitch(); + } +#endif + if (m_nPedState == PED_ATTACK) { + bIsAttacking = true; + return; + } + + if (IsPlayer() || (!victimPed || victimPed->IsPedInControl())) { + if (IsPlayer()) + CPad::GetPad(0)->ResetAverageWeapon(); + + uint8 pointBlankStatus; + if ((curWeapon->m_eWeaponFire == WEAPON_FIRE_INSTANT_HIT || GetWeapon()->m_eWeaponType == WEAPONTYPE_FLAMETHROWER) + && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_M16_1STPERSON + && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_M16_1STPERSON_RUNABOUT + && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_SNIPER + && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_SNIPER_RUNABOUT + && (pointBlankStatus = CheckForPointBlankPeds(victimPed)) != NO_POINT_BLANK_PED) { + ClearAimFlag(); + + // This condition is pointless + if (pointBlankStatus == POINT_BLANK_FOR_WANTED_PED || !victimPed && (IsPlayer() || !m_carInObjective)) + StartFightAttack(200); + } else { + if (!curWeapon->IsFlagSet(WEAPONFLAG_CANAIM)) + m_pSeekTarget = nil; + + if (m_nPedState != PED_AIM_GUN) + SetStoredState(); + + SetPedState(PED_ATTACK); + SetMoveState(PEDMOVE_NONE); + if (bCrouchWhenShooting && bIsDucking && curWeapon->IsFlagSet(WEAPONFLAG_CROUCHFIRE)) { + CAnimBlendAssociation* curMoveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(curWeapon)); + if (curMoveAssoc) { + if (strcmp(CAnimManager::GetAnimAssociation(curWeapon->m_AnimToPlay, GetCrouchFireAnim(curWeapon))->hierarchy->name, curMoveAssoc->hierarchy->name) != 0) { + delete curMoveAssoc; + } + } + animAssoc = CAnimManager::BlendAnimation(GetClump(), curWeapon->m_AnimToPlay, GetCrouchFireAnim(curWeapon), 8.0f); + } else { + float animDelta = 8.0f; + if (curWeapon->m_eWeaponFire == WEAPON_FIRE_MELEE) + animDelta = 1000.0f; + + AnimationId fireAnim; + if (curWeapon->IsFlagSet(WEAPONFLAG_THROW)) + fireAnim = ANIM_THROWABLE_START_THROW; + else if (CGame::nastyGame && (curWeapon->IsFlagSet(WEAPONFLAG_GROUND_2ND) || curWeapon->IsFlagSet(WEAPONFLAG_GROUND_3RD))) { + PedOnGroundState pedOnGround = CheckForPedsOnGroundToAttack(this, nil); + if (pedOnGround > PED_IN_FRONT_OF_ATTACKER || pedOnGround == NO_PED && bIsStanding && m_pCurSurface && m_pCurSurface->IsVehicle()) { + fireAnim = GetFireAnimGround(curWeapon, false); + } else { + fireAnim = GetFireAnimNotDucking(curWeapon); + } + } else { + fireAnim = GetFireAnimNotDucking(curWeapon); + } + + CAnimBlendAssociation* curFireAssoc = RpAnimBlendClumpGetAssociation(GetClump(), fireAnim); + if (curFireAssoc) { + if (strcmp(CAnimManager::GetAnimAssociation(curWeapon->m_AnimToPlay, fireAnim)->hierarchy->name, curFireAssoc->hierarchy->name) != 0) { + delete curFireAssoc; + } + } + animAssoc = CAnimManager::BlendAnimation(GetClump(), curWeapon->m_AnimToPlay, fireAnim, animDelta); + } + + animAssoc->SetRun(); + if (animAssoc->currentTime == animAssoc->hierarchy->totalLength) + animAssoc->SetCurrentTime(0.0f); + + animAssoc->SetFinishCallback(FinishedAttackCB, this); + } + return; + } + + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT && victimPed->m_nPedState == PED_GETUP) + SetWaitState(WAITSTATE_SURPRISE, nil); + + SetLookFlag(victim, true, true); + SetLookTimer(100); +} + +void +CPed::ClearAttack(void) +{ + if (m_nPedState != PED_ATTACK || (bIsDucking && !IsPlayer()) || m_nWaitState == WAITSTATE_PLAYANIM_DUCK) + return; + + if (FindPlayerPed() == this && TheCamera.Using1stPersonWeaponMode()) { + SetPointGunAt(nil); + } else if (bIsPointingGunAt) { + if (m_pLookTarget) + SetPointGunAt(m_pLookTarget); + else + ClearPointGunAt(); + } else if (m_objective != OBJECTIVE_NONE) { + SetIdle(); + } else { + RestorePreviousState(); + } +} + +void +CPed::ClearAttackByRemovingAnim(void) +{ + if (m_nPedState != PED_ATTACK) + return; + + CWeaponInfo *weapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + CAnimBlendAssociation *weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetPrimaryFireAnim(weapon)); + + if (!weaponAssoc) { + if (GetCrouchFireAnim(weapon)) + weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(weapon)); + } + if (!weaponAssoc) { + if(GetFinishingAttackAnim(weapon)) + weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetFinishingAttackAnim(weapon)); + } + if (!weaponAssoc) { + if(GetSecondFireAnim(weapon)) + weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetSecondFireAnim(weapon)); + } + if (!weaponAssoc) { + if(Get3rdFireAnim(weapon)) + weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), Get3rdFireAnim(weapon)); + } + if (weaponAssoc) { + weaponAssoc->blendDelta = -8.0f; + weaponAssoc->flags &= ~ASSOC_RUNNING; + weaponAssoc->flags |= ASSOC_DELETEFADEDOUT; + weaponAssoc->SetDeleteCallback(FinishedAttackCB, this); + } else { + ClearAttack(); + } +} + +void +CPed::FinishedAttackCB(CAnimBlendAssociation *attackAssoc, void *arg) +{ + CAnimBlendAssociation *newAnim, *reloadAnimAssoc = nil; + CPed *ped = (CPed*)arg; + CWeaponInfo *currentWeapon = CWeaponInfo::GetWeaponInfo(ped->GetWeapon()->m_eWeaponType); + + if (ped->m_nPedState != PED_ATTACK) { + if (ped->bIsDucking && ped->IsPedInControl()) { + if (GetCrouchReloadAnim(currentWeapon)) { + reloadAnimAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), GetCrouchReloadAnim(currentWeapon)); + } + if (GetCrouchFireAnim(currentWeapon) && attackAssoc) { + if (attackAssoc->animId == GetCrouchFireAnim(currentWeapon) && !reloadAnimAssoc) { + newAnim = CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_DUCK_WEAPON, 8.0f); + newAnim->SetCurrentTime(newAnim->hierarchy->totalLength); + newAnim->flags &= ~ASSOC_RUNNING; + } + } + } + } else if (attackAssoc && attackAssoc->animId == ANIM_THROWABLE_START_THROW && currentWeapon->m_AnimToPlay == ASSOCGRP_THROW) { + if ((!ped->IsPlayer() || ((CPlayerPed*)ped)->m_bHaveTargetSelected) && ped->IsPlayer()) { + attackAssoc->blendDelta = -1000.0f; + newAnim = CAnimManager::AddAnimation(ped->GetClump(), currentWeapon->m_AnimToPlay, ANIM_THROWABLE_THROWU); + } else { + attackAssoc->blendDelta = -1000.0; + newAnim = CAnimManager::AddAnimation(ped->GetClump(), currentWeapon->m_AnimToPlay, ANIM_THROWABLE_THROW); + } + newAnim->SetFinishCallback(FinishedAttackCB, ped); + + } else if (ped->bIsDucking && ped->bCrouchWhenShooting) { + if (GetCrouchReloadAnim(currentWeapon)) { + reloadAnimAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), GetCrouchReloadAnim(currentWeapon)); + } + if (GetCrouchFireAnim(currentWeapon) && attackAssoc) { + if (attackAssoc->animId == GetCrouchFireAnim(currentWeapon) && !reloadAnimAssoc) { + newAnim = CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_DUCK_WEAPON, 8.0f); + newAnim->SetCurrentTime(newAnim->hierarchy->totalLength); + newAnim->flags &= ~ASSOC_RUNNING; + } + } + + if (!ped->bIsAttacking) + ped->ClearAttack(); + + } else if (GetSecondFireAnim(currentWeapon) && ped->bIsAttacking && currentWeapon->m_AnimToPlay != ASSOCGRP_THROW) { + AnimationId groundAnim = GetFireAnimGround(currentWeapon); + CAnimBlendAssociation *groundAnimAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), groundAnim); + if (!(groundAnimAssoc && (groundAnimAssoc->blendAmount > 0.95f || groundAnimAssoc->blendDelta > 0.0f))) { + if (attackAssoc && attackAssoc->animId == ANIM_MELEE_ATTACK) { + newAnim = CAnimManager::BlendAnimation( + ped->GetClump(), currentWeapon->m_AnimToPlay, GetSecondFireAnim(currentWeapon), 8.0f); + } else { + newAnim = CAnimManager::BlendAnimation( + ped->GetClump(), currentWeapon->m_AnimToPlay, ANIM_MELEE_ATTACK, 8.0f); + } + newAnim->SetFinishCallback(FinishedAttackCB, ped); + } + } else { + if (attackAssoc && attackAssoc->animId == ANIM_MELEE_ATTACK && currentWeapon->m_AnimToPlay == ASSOCGRP_UNARMED) { + attackAssoc->blendDelta = -8.0f; + attackAssoc->flags |= ASSOC_DELETEFADEDOUT; + ped->ClearAttack(); + return; + } + if (attackAssoc) { + if (currentWeapon->m_AnimToPlay == ASSOCGRP_THROW) { + if ((attackAssoc->animId == ANIM_THROWABLE_THROW || attackAssoc->animId == ANIM_THROWABLE_THROWU) && ped->GetWeapon()->m_nAmmoTotal > 0) { + ped->RemoveWeaponModel(currentWeapon->m_nModelId); + ped->AddWeaponModel(currentWeapon->m_nModelId); + } + } + } + + if (!ped->bIsAttacking) + ped->ClearAttack(); + } +} + +void +CPed::FinishedReloadCB(CAnimBlendAssociation *reloadAssoc, void *arg) +{ + CPed *ped = (CPed*)arg; + CWeaponInfo *weapon = CWeaponInfo::GetWeaponInfo(ped->GetWeapon()->m_eWeaponType); + + if (ped->DyingOrDead()) + return; + + if (ped->bIsDucking && ped->bCrouchWhenShooting) { + CAnimBlendAssociation *crouchFireAssoc = nil; + if (weapon->IsFlagSet(WEAPONFLAG_CROUCHFIRE)) { + crouchFireAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), GetCrouchFireAnim(weapon)); + } + if (weapon->IsFlagSet(WEAPONFLAG_RELOAD) && reloadAssoc) { + if (reloadAssoc->animId == GetCrouchReloadAnim(weapon) && !crouchFireAssoc) { + CAnimBlendAssociation *crouchAssoc = CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_DUCK_WEAPON, 8.0f); + crouchAssoc->SetCurrentTime(crouchAssoc->hierarchy->totalLength); + crouchAssoc->flags &= ~ASSOC_RUNNING; + } + } + } else if (weapon->IsFlagSet(WEAPONFLAG_RELOAD_LOOP2START) && ped->bIsAttacking) { + CAnimBlendAssociation *fireAssoc = + CAnimManager::BlendAnimation(ped->GetClump(), weapon->m_AnimToPlay, GetPrimaryFireAnim(weapon), 8.0f); + fireAssoc->SetFinishCallback(FinishedAttackCB, ped); + fireAssoc->SetRun(); + if (fireAssoc->currentTime == reloadAssoc->hierarchy->totalLength) + fireAssoc->SetCurrentTime(Max(weapon->m_fAnimLoopStart - 0.04f, 0.0f)); + else if (fireAssoc->currentTime < weapon->m_fAnimLoopStart) + fireAssoc->SetCurrentTime(Max(weapon->m_fAnimLoopStart - 0.04f, 0.0f)); + } +} + +uint8 +CPed::CheckForPointBlankPeds(CPed *pedToVerify) +{ + float pbDistance = 1.1f; + if (GetWeapon()->IsType2Handed()) + pbDistance = 1.6f; + + for (int i = 0; i < m_numNearPeds; i++) { + CPed *nearPed = m_nearPeds[i]; + + if (!pedToVerify || pedToVerify == nearPed) { + + CVector diff = nearPed->GetPosition() - GetPosition(); + if (diff.MagnitudeSqr() < SQR(pbDistance)) { + + float neededAngle = CGeneral::GetRadianAngleBetweenPoints( + nearPed->GetPosition().x, nearPed->GetPosition().y, + GetPosition().x, GetPosition().y); + neededAngle = CGeneral::LimitRadianAngle(neededAngle); + m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); + + float neededTurn = Abs(neededAngle - m_fRotationCur); + + if (neededTurn > PI) + neededTurn = 2*PI - neededTurn; + + PedState nearPedState = nearPed->m_nPedState; + + if (nearPedState == PED_FALL || nearPedState == PED_GETUP || nearPedState == PED_DIE || nearPedState == PED_DEAD || nearPedState == PED_DIVE_AWAY) + return NO_POINT_BLANK_PED; + + if (neededTurn < CAN_SEE_ENTITY_ANGLE_THRESHOLD) { + if (pedToVerify == nearPed) + return POINT_BLANK_FOR_WANTED_PED; + else + return POINT_BLANK_FOR_SOMEONE_ELSE; + } + } + } + } + return NO_POINT_BLANK_PED; +} + +void +CPed::Attack(void) +{ + CAnimBlendAssociation *weaponAnimAssoc; + int32 weaponAnim; + float weaponAnimTime; + float animLoopEnd; + CWeaponInfo *ourWeapon; + bool attackShouldContinue; + CAnimBlendAssociation *reloadAnimAssoc; + CAnimBlendAssociation *throwAssoc; + float delayBetweenAnimAndFire; + float animLoopStart; + CVector firePos; + + ourWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + weaponAnimAssoc = nil; + attackShouldContinue = !!bIsAttacking; + reloadAnimAssoc = nil; + throwAssoc = nil; + animLoopStart = ourWeapon->m_fAnimLoopStart; + animLoopEnd = ourWeapon->m_fAnimLoopEnd; + delayBetweenAnimAndFire = ourWeapon->m_fAnimFrameFire; + weaponAnim = ourWeapon->m_AnimToPlay; + + if (bIsDucking) { + if(GetCrouchFireAnim(ourWeapon) && bCrouchWhenShooting) { + weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(ourWeapon)); + if (weaponAnimAssoc) { + animLoopStart = ourWeapon->m_fAnim2LoopStart; + animLoopEnd = ourWeapon->m_fAnim2LoopEnd; + delayBetweenAnimAndFire = ourWeapon->m_fAnim2FrameFire; + } + } + } else if (m_nPedType == PEDTYPE_COP && Get3rdFireAnim(ourWeapon)){ + weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), Get3rdFireAnim(ourWeapon)); + if (weaponAnimAssoc) { + animLoopStart = 11.f/30.f; + animLoopEnd = 19.f/30.f; + delayBetweenAnimAndFire = 14.f/30.f; + } + } else { + weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetPrimaryFireAnim(ourWeapon)); + } + + if (GetReloadAnim(ourWeapon)) { + reloadAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetReloadAnim(ourWeapon)); + } + + if (GetCrouchReloadAnim(ourWeapon) && !reloadAnimAssoc) { + reloadAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchReloadAnim(ourWeapon)); + } + + if ( reloadAnimAssoc && reloadAnimAssoc->IsRunning() ) { + if (!IsPlayer() || ((CPlayerPed*)this)->m_bHaveTargetSelected) + ClearAttack(); + return; + } + + if ( reloadAnimAssoc ) { + reloadAnimAssoc->flags |= ASSOC_DELETEFADEDOUT; + if ( reloadAnimAssoc->blendDelta >= 0.0f ) + reloadAnimAssoc->blendDelta = -8.0f; + } + + if (GetThrowAnim(ourWeapon)) { + throwAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetThrowAnim(ourWeapon)); + } + + if ( CTimer::GetTimeInMilliseconds() < m_shootTimer ) + attackShouldContinue = true; + + bool meleeAttackStarted = false; + if ( !weaponAnimAssoc ) { + if (GetMeleeStartAnim(ourWeapon)) { + weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetMeleeStartAnim(ourWeapon)); + if ( weaponAnimAssoc ) { + if ( IsPlayer() ) + meleeAttackStarted = true; + + switch ( ourWeapon->m_AnimToPlay ) { + case ASSOCGRP_UNARMED: + case ASSOCGRP_SCREWDRIVER: + case ASSOCGRP_KNIFE: + case ASSOCGRP_BASEBALLBAT: + case ASSOCGRP_GOLFCLUB: + case ASSOCGRP_CHAINSAW: + delayBetweenAnimAndFire = 0.2f; + animLoopStart = 0.1f; + break; + default: + break; + } + animLoopEnd = 99.9f; + } + } + } + if (!weaponAnimAssoc) { + if (GetSecondFireAnim(ourWeapon)) { + weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetSecondFireAnim(ourWeapon)); + if (weaponAnimAssoc) { + animLoopStart = ourWeapon->m_fAnim2LoopStart; + animLoopEnd = ourWeapon->m_fAnim2LoopEnd; + delayBetweenAnimAndFire = ourWeapon->m_fAnim2FrameFire; + } + } + } + if (!weaponAnimAssoc) { + weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetFireAnimGround(ourWeapon)); + if (weaponAnimAssoc) { + animLoopStart = ourWeapon->m_fAnim2LoopStart; + animLoopEnd = ourWeapon->m_fAnim2LoopEnd; + delayBetweenAnimAndFire = ourWeapon->m_fAnim2FrameFire; + } + } + + if (!weaponAnimAssoc) { + if (!throwAssoc) { + if (attackShouldContinue) { + if (ourWeapon->m_eWeaponFire != WEAPON_FIRE_PROJECTILE || !IsPlayer() || ((CPlayerPed*)this)->m_bHaveTargetSelected) { + if (bCrouchWhenShooting && bIsDucking && GetCrouchFireAnim(ourWeapon)) { + weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, GetCrouchFireAnim(ourWeapon), 8.0f); + + } else if(GetSecondFireAnim(ourWeapon) && CGeneral::GetRandomNumber() & 1){ + weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, GetSecondFireAnim(ourWeapon), 8.0f); + + } else if(!CGame::nastyGame || ourWeapon->m_eWeaponFire != WEAPON_FIRE_MELEE || + !GetFireAnimGround(ourWeapon, false) || + CheckForPedsOnGroundToAttack(this, nil) < PED_ON_THE_FLOOR) { + + weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, GetFireAnimNotDucking(ourWeapon), 8.0f); + + } else { + weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, GetFireAnimGround(ourWeapon, false), 8.0f); + } + + weaponAnimAssoc->SetFinishCallback(FinishedAttackCB, this); + weaponAnimAssoc->SetRun(); + + if (weaponAnimAssoc->currentTime == weaponAnimAssoc->hierarchy->totalLength) + weaponAnimAssoc->SetCurrentTime(0.0f); + + if (IsPlayer()) { + ((CPlayerPed*)this)->m_fAttackButtonCounter = 0.0f; + ((CPlayerPed*)this)->m_bHaveTargetSelected = false; + } + } + } else + FinishedAttackCB(nil, this); + + } + return; + } + + if (meleeAttackStarted && IsPlayer()) { + if (((CPlayerPed*)this)->m_bHaveTargetSelected || ((CPlayerPed*)this)->m_fMoveSpeed < 0.5f) { + weaponAnimAssoc->SetRun(); + } else { + if (weaponAnimAssoc->currentTime > animLoopStart && weaponAnimAssoc->currentTime - weaponAnimAssoc->timeStep <= animLoopStart) + weaponAnimAssoc->flags &= ~ASSOC_RUNNING; + } + } + + float animStart = animLoopStart * 0.4f; + weaponAnimTime = weaponAnimAssoc->currentTime; + if (weaponAnimTime > animStart && weaponAnimTime - weaponAnimAssoc->timeStep <= animStart) { + if (!bIsDucking && !GetFireAnimNotDucking(ourWeapon) && ourWeapon->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM)) + m_pedIK.m_flags |= CPedIK::AIMS_WITH_ARM; + else +#ifdef FREE_CAM + if (!IsPlayer() || !((CPlayerPed*)this)->m_bFreeAimActive) +#endif + m_pedIK.m_flags &= ~CPedIK::AIMS_WITH_ARM; + } + + if (GetWeapon()->m_eWeaponType != WEAPONTYPE_CHAINSAW + || !meleeAttackStarted && delayBetweenAnimAndFire - 0.5f >= weaponAnimAssoc->currentTime + || weaponAnimAssoc->currentTime - weaponAnimAssoc->timeStep > delayBetweenAnimAndFire) { + + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_CHAINSAW) { + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_CHAINSAW_IDLE, 0.0f); + } else if (weaponAnimTime <= delayBetweenAnimAndFire || weaponAnimTime - weaponAnimAssoc->timeStep > delayBetweenAnimAndFire || !weaponAnimAssoc->IsRunning()) { + if (weaponAnimAssoc->speed < 1.0f) + weaponAnimAssoc->speed = 1.0f; + + } else { + firePos = ourWeapon->m_vecFireOffset; + + if(ourWeapon->m_AnimToPlay != ASSOCGRP_BASEBALLBAT && ourWeapon->m_AnimToPlay != ASSOCGRP_GOLFCLUB) { + if (ourWeapon->m_eWeaponFire != WEAPON_FIRE_MELEE) { + TransformToNode(firePos, (weaponAnimAssoc->animId == ANIM_MELEE_ATTACK_2ND && ourWeapon->m_AnimToPlay == ASSOCGRP_UNARMED) ? PED_FOOTR : PED_HANDR); + } else { + firePos = GetMatrix() * firePos; + } + } else { + if (weaponAnimAssoc->animId == ANIM_MELEE_ATTACK_2ND) + firePos.z = 0.7f * ourWeapon->m_fRadius - 1.0f; + + firePos = GetMatrix() * firePos; + } + + GetWeapon()->Fire(this, &firePos); + + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_MOLOTOV || GetWeapon()->m_eWeaponType == WEAPONTYPE_GRENADE || GetWeapon()->m_eWeaponType == WEAPONTYPE_DETONATOR_GRENADE || + GetWeapon()->m_eWeaponType == WEAPONTYPE_TEARGAS) { + RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_nModelId); + } + if (GetWeapon()->m_nAmmoTotal == 0 && ourWeapon->m_eWeaponFire != WEAPON_FIRE_MELEE && FindPlayerPed() != this) { + SelectGunIfArmed(); + } + + if (GetWeapon()->m_eWeaponState == WEAPONSTATE_MELEE_MADECONTACT) { + int damagerType = ENTITY_TYPE_NOTHING; + if (m_pDamageEntity && (m_fDamageImpulse == 0.0f || !m_pDamageEntity->IsBuilding())) { + damagerType = m_pDamageEntity->GetType(); + } + switch (ourWeapon->m_AnimToPlay) { + case ASSOCGRP_UNARMED: + if (weaponAnimAssoc->animId == ANIM_MELEE_ATTACK || weaponAnimAssoc->animId == ANIM_MELEE_ATTACK_START) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_FIGHT_46, (damagerType | (GetWeapon()->m_eWeaponType << 8))); + break; + case ASSOCGRP_KNIFE: + case ASSOCGRP_BASEBALLBAT: + case ASSOCGRP_GOLFCLUB: + case ASSOCGRP_CHAINSAW: + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_BAT_ATTACK, (damagerType | (GetWeapon()->m_eWeaponType << 8))); + break; + default: + break; + } + + if (bIsAttacking || CTimer::GetTimeInMilliseconds() < m_shootTimer) { + weaponAnimAssoc->callbackType = 0; + } + } + + attackShouldContinue = false; + } + } else { + CVector firePos = ourWeapon->m_vecFireOffset; + + if (weaponAnimAssoc->animId == ANIM_MELEE_ATTACK_2ND) + firePos.z = 0.7f * ourWeapon->m_fRadius - 1.0f; + + firePos = GetMatrix() * firePos; + GetWeapon()->Fire(this, &firePos); + if (GetWeapon()->m_eWeaponState == WEAPONSTATE_MELEE_MADECONTACT) { + int damagerType = ENTITY_TYPE_PED; + if (m_pDamageEntity) + damagerType = m_pDamageEntity->GetType(); + + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_CHAINSAW_MADECONTACT, (float)damagerType); + if (IsPlayer()) { + CPad::GetPad(0)->StartShake(240, 180); + } + } else { + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_CHAINSAW_ATTACK, 0.0f); + if (IsPlayer()) { + CPad::GetPad(0)->StartShake(240, 90); + } + } + attackShouldContinue = false; + } + + if (ourWeapon->m_eWeaponFire == WEAPON_FIRE_INSTANT_HIT && ourWeapon->m_AnimToPlay == ASSOCGRP_SHOTGUN) { + weaponAnimTime = weaponAnimAssoc->currentTime; + + if (weaponAnimTime > 1.0f && weaponAnimTime - weaponAnimAssoc->timeStep <= 1.0f && weaponAnimAssoc->IsRunning()) { + firePos = ourWeapon->m_vecFireOffset; + TransformToNode(firePos, PED_HANDR); + + CVector gunshellPos( + firePos.x - 0.6f * GetForward().x, + firePos.y - 0.6f * GetForward().y, + firePos.z - 0.15f * GetUp().z + ); + + CVector2D gunshellRot( + GetRight().x, + GetRight().y + ); + + gunshellRot.Normalise(); + GetWeapon()->AddGunshell(this, gunshellPos, gunshellRot, 0.025f); + } + } + + if (IsPlayer()) { + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT || GetWeapon()->m_eWeaponType == WEAPONTYPE_GOLFCLUB || GetWeapon()->m_eWeaponType == WEAPONTYPE_KATANA) { + float loopEndWithDelay = animLoopEnd; + if (loopEndWithDelay >= 98.0f) + loopEndWithDelay = (14.0f / 30.0f) + delayBetweenAnimAndFire; + if (weaponAnimAssoc->flags & ASSOC_RUNNING) { + if (weaponAnimAssoc->currentTime >= animLoopStart && weaponAnimAssoc->currentTime <= loopEndWithDelay) + CSpecialFX::AddWeaponStreak(GetWeapon()->m_eWeaponType); + } + } + } + + // Anim breakout on running + if (IsPlayer()) { + if (CPad::GetPad(0)->GetSprint()) { + if (!attackShouldContinue && weaponAnimAssoc->currentTime > ourWeapon->m_fAnimBreakout) { + weaponAnimAssoc->blendDelta = -4.0f; + FinishedAttackCB(nil, this); + return; + } + } + } + + weaponAnimTime = weaponAnimAssoc->currentTime; + + // Anim loop end, either start the loop again or finish the attack + if (weaponAnimTime > animLoopEnd || !weaponAnimAssoc->IsRunning() && ourWeapon->m_eWeaponFire != WEAPON_FIRE_PROJECTILE) { + if (GetWeapon()->m_eWeaponState == WEAPONSTATE_RELOADING) { + if (GetReloadAnim(ourWeapon) && !reloadAnimAssoc) { + if (!CWorld::Players[CWorld::PlayerInFocus].m_bFastReload) { + CAnimBlendAssociation *newReloadAssoc = CAnimManager::BlendAnimation( + GetClump(), ourWeapon->m_AnimToPlay, + bIsDucking && GetCrouchReloadAnim(ourWeapon) ? GetCrouchReloadAnim(ourWeapon) : GetReloadAnim(ourWeapon), + 8.0f); + newReloadAssoc->SetFinishCallback(FinishedReloadCB, this); + } + ClearLookFlag(); + ClearAimFlag(); + bIsAttacking = false; + bIsPointingGunAt = false; + m_shootTimer = CTimer::GetTimeInMilliseconds(); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_AK47_BULLET_ECHO, GetWeapon()->m_eWeaponType); + return; + } + } + if (weaponAnimTime - 2.0f * weaponAnimAssoc->timeStep <= animLoopEnd + && (bIsAttacking || CTimer::GetTimeInMilliseconds() < m_shootTimer) + && (GetWeapon()->m_eWeaponState != WEAPONSTATE_RELOADING + || GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN)) { + + PedOnGroundState pedOnGroundState; + if (ourWeapon->m_eWeaponFire == WEAPON_FIRE_MELEE && + (CGame::nastyGame && ((pedOnGroundState = CheckForPedsOnGroundToAttack(this, nil)) > PED_IN_FRONT_OF_ATTACKER) + || GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT && pedOnGroundState == NO_PED && bIsStanding && m_pCurSurface && m_pCurSurface->IsVehicle())) { + + AnimationId fireAnim = GetFireAnimGround(ourWeapon, false); + if (weaponAnimAssoc->animId == fireAnim) + weaponAnimAssoc->SetCurrentTime(0.1f); + else { + if (GetFireAnimGround(ourWeapon, false)) { + weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, fireAnim, 8.0f); + } else { + weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_KICKGROUND, 8.0f); + } + } + weaponAnimAssoc->SetFinishCallback(FinishedAttackCB, this); + } else if (GetSecondFireAnim(ourWeapon)) { + if (weaponAnimAssoc->animId == GetSecondFireAnim(ourWeapon)) { + weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, ANIM_WEAPON_FIRE, 8.0f); + } else { + weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, GetSecondFireAnim(ourWeapon), 8.0f); + } + weaponAnimAssoc->SetFinishCallback(FinishedAttackCB, this); + } else { + weaponAnimAssoc->SetCurrentTime(animLoopStart); + weaponAnimAssoc->SetRun(); + } + } else if (IsPlayer() && m_pPointGunAt && bIsAimingGun && GetWeapon()->m_eWeaponState != WEAPONSTATE_RELOADING) { + weaponAnimAssoc->SetCurrentTime(animLoopEnd); + weaponAnimAssoc->flags &= ~ASSOC_RUNNING; + SetPointGunAt(m_pPointGunAt); +#ifdef FREE_CAM + } else if (IsPlayer() && ((CPlayerPed*)this)->m_bFreeAimActive && GetWeapon()->m_eWeaponState != WEAPONSTATE_RELOADING) { + float limitedCam = CGeneral::LimitRadianAngle(-TheCamera.Orientation); + SetLookFlag(limitedCam, true, true); + SetAimFlag(limitedCam); + SetLookTimer(INT32_MAX); + SetPointGunAt(nil); + ((CPlayerPed*)this)->m_fFPSMoveHeading = TheCamera.Find3rdPersonQuickAimPitch(); +#endif + } else { + ClearAimFlag(); + + // Echoes of bullets, at the end of the attack. (Bug: doesn't play while reloading) + if (weaponAnimAssoc->currentTime - weaponAnimAssoc->timeStep < animLoopEnd) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_AK47_BULLET_ECHO, GetWeapon()->m_eWeaponType); + + // Fun fact: removing this part leds to reloading flamethrower + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_FLAMETHROWER && weaponAnimAssoc->IsRunning()) { + weaponAnimAssoc->flags |= ASSOC_DELETEFADEDOUT; + weaponAnimAssoc->flags &= ~ASSOC_RUNNING; + weaponAnimAssoc->blendDelta = -4.0f; + } + } + } + + if (weaponAnimAssoc->currentTime > delayBetweenAnimAndFire) + attackShouldContinue = false; + + bIsAttacking = attackShouldContinue; +} + +void +CPed::StartFightAttack(uint8 buttonPressure) +{ + if (!IsPedInControl() || (m_attackTimer > CTimer::GetTimeInMilliseconds() && buttonPressure != 0)) + return; + + if (m_nPedState == PED_FIGHT) { + m_fightButtonPressure = buttonPressure; + return; + } + + if (m_nPedState != PED_AIM_GUN) + SetStoredState(); + + if (m_nWaitState != WAITSTATE_FALSE) { + ClearWaitState(); + RestoreHeadingRate(); + } + + CAnimBlendAssociation* animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNSTOP1); + if (!animAssoc) + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNSTOP2); + + if (animAssoc) { + RestoreHeadingRate(); + } + SetMoveState(PEDMOVE_NONE); + m_nStoredMoveState = PEDMOVE_NONE; + bool fightWithWeapon = false; + CAnimBlendAssociation *fightIdleAssoc; + + CWeaponInfo* weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) { + if (GetFightIdleWithMeleeAnim(weaponInfo)) { + fightIdleAssoc = CAnimManager::BlendAnimation(GetClump(), weaponInfo->m_AnimToPlay, GetFightIdleWithMeleeAnim(weaponInfo), 1000.0f); + fightWithWeapon = true; + } else { + fightIdleAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FIGHT_IDLE, 1000.0f); + } + } else { + fightIdleAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FIGHT_IDLE, 1000.0f); + } + m_lastFightMove = FIGHTMOVE_IDLE; + m_curFightMove = IsPlayer() ? ChooseAttackPlayer(buttonPressure, fightWithWeapon) : ChooseAttackAI(buttonPressure, fightWithWeapon); + + SetPedState(PED_FIGHT); + m_fightButtonPressure = 0; + + if (m_curFightMove > FIGHTMOVE_NULL && m_curFightMove != FIGHTMOVE_IDLE) { + animAssoc = CAnimManager::BlendAnimation(GetClump(), m_curFightMove < FIGHTMOVE_MELEE1 ? ASSOCGRP_STD : weaponInfo->m_AnimToPlay, + tFightMoves[m_curFightMove].animId, 8.0f); + + if (weaponInfo->m_AnimToPlay == ASSOCGRP_KNIFE && m_curFightMove >= FIGHTMOVE_MELEE1) { + switch (GetWeapon()->m_eWeaponType) { + case WEAPONTYPE_SCREWDRIVER: + case WEAPONTYPE_KNIFE: + animAssoc->speed = 1.05f; + break; + case WEAPONTYPE_GOLFCLUB: + case WEAPONTYPE_NIGHTSTICK: + case WEAPONTYPE_BASEBALLBAT: + case WEAPONTYPE_HAMMER: + case WEAPONTYPE_KATANA: + animAssoc->speed = 0.8f; + break; + case WEAPONTYPE_CLEAVER: + case WEAPONTYPE_MACHETE: + animAssoc->speed = 0.9f; + break; + } + } else { + if (m_curFightMove == FIGHTMOVE_BACKKICK) + animAssoc->speed = 1.15f; + else + animAssoc->speed = 0.8f; + } + if (IsPlayer()) + animAssoc->SetCurrentTime(0.08f); + + animAssoc->SetFinishCallback(FinishFightMoveCB, this); + } else { + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 2000; + } + + m_fightState = FIGHTSTATE_NO_MOVE; + m_takeAStepAfterAttack = false; + bIsAttacking = true; + + if (IsPlayer()) + nPlayerInComboMove = 0; +} + +void +CPed::StartFightDefend(uint8 direction, uint8 hitLevel, uint8 unk) +{ + if (m_nPedState == PED_DEAD) { + if (CGame::nastyGame) { + if (hitLevel == HITLEVEL_GROUND) { + CAnimBlendAssociation *floorHitAssoc; + if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL)) { + floorHitAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR_FRONT, 8.0f); + } else { + floorHitAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[FIGHTMOVE_HITONFLOOR].animId, 8.0f); + } + if (floorHitAssoc) { + floorHitAssoc->SetCurrentTime(0.0f); + floorHitAssoc->SetRun(); + floorHitAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; + } + } + if (CGame::nastyGame) { + CVector headPos = GetNodePosition(PED_HEAD); + for(int i = 0; i < 4; ++i) { + CVector bloodDir(0.0f, 0.0f, 0.1f); + CVector bloodPos = headPos - 0.2f * GetForward(); + CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, bloodDir, nil, 0.0f, 0, 0, 0, 0); + } + } + } + } else if (m_nPedState == PED_FALL) { + if (hitLevel == HITLEVEL_GROUND && !IsPedHeadAbovePos(-0.3f)) { + CAnimBlendAssociation *floorHitAssoc = RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL) ? + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR_FRONT, 8.0f) : + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR, 8.0f); + if (floorHitAssoc) { + floorHitAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; + floorHitAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + } + } else if (IsPedInControl()) { + if ((IsPlayer() && m_nPedState != PED_FIGHT && ((CPlayerPed*)this)->m_fMoveSpeed > 1.0f) + || (!IsPlayer() && m_objective == OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE)) { + + if (hitLevel != HITLEVEL_HIGH && hitLevel != HITLEVEL_LOW || (IsPlayer() || CGeneral::GetRandomNumber() & 1) && CGeneral::GetRandomNumber() & 7) { + if (IsPlayer() || CGeneral::GetRandomNumber() & 1) { + AnimationId shotAnim; + switch (direction) { + case 1: + shotAnim = ANIM_STD_HITBYGUN_LEFT; + break; + case 2: + shotAnim = ANIM_STD_HITBYGUN_BACK; + break; + case 3: + shotAnim = ANIM_STD_HITBYGUN_RIGHT; + break; + default: + shotAnim = ANIM_STD_HITBYGUN_FRONT; + break; + } + CAnimBlendAssociation *shotAssoc = RpAnimBlendClumpGetAssociation(GetClump(), shotAnim); + if (!shotAssoc || shotAssoc->blendDelta < 0.0f) + shotAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, shotAnim, 8.0f); + + shotAssoc->SetCurrentTime(0.0f); + shotAssoc->SetRun(); + shotAssoc->flags |= ASSOC_FADEOUTWHENDONE; + } else { + int time = CGeneral::GetRandomNumberInRange(1000, 3000); + SetWaitState(WAITSTATE_PLAYANIM_DUCK, &time); + } + } else { + bool fall = true; + AnimationId hitAnim; + switch (direction) { + case 1: + hitAnim = ANIM_STD_HIGHIMPACT_LEFT; + break; + case 2: + if (CGeneral::GetRandomNumber() & 1) { + fall = false; + hitAnim = ANIM_STD_HIT_BACK; + } else { + hitAnim = ANIM_STD_HIGHIMPACT_BACK; + } + break; + case 3: + hitAnim = ANIM_STD_HIGHIMPACT_RIGHT; + break; + default: + if (hitLevel == HITLEVEL_LOW) { + hitAnim = ANIM_STD_KO_SHOT_STOMACH; + } else if (CGeneral::GetRandomNumber() & 1) { + fall = false; + hitAnim = ANIM_STD_HIT_WALK; + } else if (CGeneral::GetRandomNumber() & 1) { + fall = false; + hitAnim = ANIM_STD_HIT_HEAD; + } else { + hitAnim = ANIM_STD_KO_SHOT_FACE; + } + break; + } + if (fall) { + SetFall(500, hitAnim, false); + } else { + CAnimBlendAssociation *hitAssoc = RpAnimBlendClumpGetAssociation(GetClump(), hitAnim); + if (!hitAssoc || hitAssoc->blendDelta < 0.0f) + hitAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, hitAnim, 8.0f); + + hitAssoc->SetCurrentTime(0.0f); + hitAssoc->SetRun(); + hitAssoc->flags |= ASSOC_FADEOUTWHENDONE; + } + } + Say(SOUND_PED_DEFEND); + } else { + Say(SOUND_PED_DEFEND); + switch (hitLevel) { + case HITLEVEL_GROUND: + m_curFightMove = FIGHTMOVE_HITONFLOOR; + break; + case HITLEVEL_LOW: + if (direction == 2 && (!IsPlayer() || ((CGeneral::GetRandomNumber() & 1) && m_fHealth < 30.0f))) { + SetFall(1000, ANIM_STD_HIGHIMPACT_BACK, false); + Say(SOUND_PED_DEFEND); + return; + } else if (direction != 2 && !IsPlayer() && (CGeneral::GetRandomNumber() & 1) && m_fHealth < 30.0f) { + SetFall(1000, ANIM_STD_KO_SHOT_STOMACH, false); + Say(SOUND_PED_DEFEND); + return; + } + m_curFightMove = FIGHTMOVE_HITBODY; + break; + case HITLEVEL_HIGH: + switch (direction) { + case 1: + m_curFightMove = FIGHTMOVE_HITLEFT; + break; + case 2: + m_curFightMove = FIGHTMOVE_HITBACK; + break; + case 3: + m_curFightMove = FIGHTMOVE_HITRIGHT; + break; + default: + if (unk <= 5) + m_curFightMove = FIGHTMOVE_HITHEAD; + else + m_curFightMove = FIGHTMOVE_HITBIGSTEP; + break; + } + break; + default: + switch (direction) { + case 1: + m_curFightMove = FIGHTMOVE_HITLEFT; + break; + case 2: + m_curFightMove = FIGHTMOVE_HITBACK; + break; + case 3: + m_curFightMove = FIGHTMOVE_HITRIGHT; + break; + default: + if (unk <= 5) + m_curFightMove = FIGHTMOVE_HITCHEST; + else + m_curFightMove = FIGHTMOVE_HITBIGSTEP; + break; + } + break; + } + if (m_nPedState == PED_GETUP && !IsPedHeadAbovePos(0.0f)) + m_curFightMove = FIGHTMOVE_HITONFLOOR; + + if (m_nPedState == PED_FIGHT) { + CAnimBlendAssociation *moveAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[m_curFightMove].animId, 8.0f); + moveAssoc->SetCurrentTime(0.0f); + moveAssoc->SetFinishCallback(FinishFightMoveCB, this); + if (IsPlayer()) + moveAssoc->speed = 1.2f; + + m_takeAStepAfterAttack = false; + m_fightButtonPressure = 0; + + } else if (IsPlayer() && GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED && GetWeapon()->m_eWeaponType != WEAPONTYPE_BRASSKNUCKLE && + !CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_FIGHTMODE)) { + CAnimBlendAssociation *moveAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[m_curFightMove].animId, 4.0f); + moveAssoc->SetCurrentTime(0.0f); + moveAssoc->speed = 1.2f; + + } else { + if (m_nPedState != PED_AIM_GUN && m_nPedState != PED_ATTACK) + SetStoredState(); + + if (m_nWaitState != WAITSTATE_FALSE) { + ClearWaitState(); + RestoreHeadingRate(); + } + SetPedState(PED_FIGHT); + m_fightButtonPressure = 0; + m_lastFightMove = FIGHTMOVE_IDLE; + RpAnimBlendClumpRemoveAssociations(GetClump(), ASSOC_REPEAT); + CAnimBlendAssociation *walkStartAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_STARTWALK); + if (walkStartAssoc) { + walkStartAssoc->flags |= ASSOC_DELETEFADEDOUT; + walkStartAssoc->blendDelta = -1000.0f; + } + CAnimBlendAssociation *walkStopAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNSTOP1); + if (!walkStopAssoc) + walkStopAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNSTOP2); + if (walkStopAssoc) { + walkStopAssoc->flags |= ASSOC_DELETEFADEDOUT; + walkStopAssoc->blendDelta = -1000.0f; + RestoreHeadingRate(); + } + SetMoveState(PEDMOVE_NONE); + m_nStoredMoveState = PEDMOVE_NONE; + CAnimBlendAssociation *fightIdleAssoc; + + if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) { + CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + if (GetFightIdleWithMeleeAnim(weaponInfo)) { + fightIdleAssoc = CAnimManager::AddAnimation(GetClump(), weaponInfo->m_AnimToPlay, GetFightIdleWithMeleeAnim(weaponInfo)); + } else { + fightIdleAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FIGHT_IDLE); + } + } else { + fightIdleAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FIGHT_IDLE); + } + fightIdleAssoc->blendAmount = 1.0f; + CAnimBlendAssociation *moveAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[m_curFightMove].animId, 8.0f); + moveAssoc->SetFinishCallback(FinishFightMoveCB, this); + m_fightState = FIGHTSTATE_NO_MOVE; + m_takeAStepAfterAttack = false; + bIsAttacking = true; + } + + if (m_pedInObjective && m_pedInObjective->IsPlayer() && !IsPlayer()) + ((CPlayerPed*)m_pedInObjective)->RemovePedFromMeleeList(this); + } + } +} + +void +CPed::Fight(void) +{ + CAnimBlendAssociation *currentAssoc, *animAssoc; + bool fightWithWeapon = false; + + eWeaponType weapon = GetWeapon()->m_eWeaponType; + CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(weapon); + + if (weaponInfo->IsFlagSet(WEAPONFLAG_FIGHTMODE) && weapon != WEAPONTYPE_UNARMED) { + fightWithWeapon = true; + tFightMoves[FIGHTMOVE_MELEE1].startFireTime = weaponInfo->m_fAnimFrameFire; + tFightMoves[FIGHTMOVE_MELEE1].endFireTime = weaponInfo->m_fAnimLoopEnd; + tFightMoves[FIGHTMOVE_MELEE2].startFireTime = weaponInfo->m_fAnim2FrameFire; + tFightMoves[FIGHTMOVE_MELEE2].endFireTime = weaponInfo->m_fAnim2LoopEnd; + tFightMoves[FIGHTMOVE_MELEE3].startFireTime = weaponInfo->m_fAnim2FrameFire; + tFightMoves[FIGHTMOVE_MELEE3].endFireTime = weaponInfo->m_fAnim2LoopEnd; + } + + switch (m_curFightMove) { + case FIGHTMOVE_NULL: + return; + case FIGHTMOVE_IDLE2NORM: + m_curFightMove = FIGHTMOVE_NULL; + RestorePreviousState(); + + // FIX: Uninitialized + currentAssoc = nil; + break; + case FIGHTMOVE_IDLE: + currentAssoc = nil; + break; + default: + currentAssoc = RpAnimBlendClumpGetAssociation(GetClump(), tFightMoves[m_curFightMove].animId); + break; + } + + if (m_curFightMove == FIGHTMOVE_SHUFFLE_F && !currentAssoc) + currentAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FIGHT_SHUFFLE_B); + + if (IsPlayer() && currentAssoc && weapon == WEAPONTYPE_KATANA) { + if (m_curFightMove == FIGHTMOVE_MELEE1 || m_curFightMove == FIGHTMOVE_MELEE2) { + static float streakDelay = 0.2f; + + if (tFightMoves[m_curFightMove].startFireTime - streakDelay < currentAssoc->currentTime && + streakDelay + tFightMoves[m_curFightMove].endFireTime > currentAssoc->currentTime) { + CSpecialFX::AddWeaponStreak(GetWeapon()->m_eWeaponType); + } + } + } + + if (!bIsAttacking && IsPlayer()) { + if (currentAssoc) { + currentAssoc->blendDelta = -1000.0f; + currentAssoc->flags |= ASSOC_DELETEFADEDOUT; + currentAssoc->flags &= ~ASSOC_RUNNING; + } + if (m_takeAStepAfterAttack) + EndFight(ENDFIGHT_WITH_A_STEP); + else + EndFight(ENDFIGHT_FAST); + + } else if (currentAssoc && m_fightState > FIGHTSTATE_MOVE_FINISHED) { + float animTime = currentAssoc->currentTime; + FightMove &curMove = tFightMoves[m_curFightMove]; + if (curMove.hitLevel != HITLEVEL_NULL && animTime > curMove.startFireTime && animTime <= curMove.endFireTime && m_fightState >= FIGHTSTATE_NO_MOVE) { + + if (animTime > curMove.startFireTime && animTime - currentAssoc->timeStep < curMove.startFireTime && + (IsPlayer() || weapon != WEAPONTYPE_UNARMED)) { + + DMAudio.PlayOneShot(m_audioEntityId, SOUND_MELEE_ATTACK_START, weapon << 8); + } + + CVector touchingNodePos(0.0f, 0.0f, 0.0f); + + switch (m_curFightMove) { + case FIGHTMOVE_KNEE: + TransformToNode(touchingNodePos, PED_LOWERLEGR); + break; + case FIGHTMOVE_PUNCHHOOK: + case FIGHTMOVE_PUNCHJAB: + TransformToNode(touchingNodePos, PED_HANDL); + break; + case FIGHTMOVE_LONGKICK: + case FIGHTMOVE_ROUNDHOUSE: + case FIGHTMOVE_FWDLEFT: + case FIGHTMOVE_BACKRIGHT: + case FIGHTMOVE_GROUNDKICK: + TransformToNode(touchingNodePos, PED_FOOTR); + break; + case FIGHTMOVE_FWDRIGHT: + TransformToNode(touchingNodePos, PED_HEAD); + break; + case FIGHTMOVE_BACKKICK: + case FIGHTMOVE_BACKFLIP: + TransformToNode(touchingNodePos, PED_FOOTL); + break; + case FIGHTMOVE_BACKLEFT: + TransformToNode(touchingNodePos, PED_UPPERARML); + break; + default: + TransformToNode(touchingNodePos, PED_HANDR); + break; + } + + FightStrike(touchingNodePos, fightWithWeapon); + m_fightButtonPressure = 0; + return; + } + + if (curMove.hitLevel != HITLEVEL_NULL) { + if (animTime > curMove.endFireTime && weaponInfo->m_AnimToPlay != ASSOCGRP_KNIFE) { + if (IsPlayer()) + currentAssoc->speed = 1.0f; + else + currentAssoc->speed = 0.8f; + } + + if (IsPlayer() && !nPlayerInComboMove && !fightWithWeapon) { + if (curMove.comboFollowOnTime > 0.0f && m_fightButtonPressure != 0 && animTime > curMove.comboFollowOnTime) { + + m_lastFightMove = m_curFightMove; + // Notice that it increases fight move index, because we're in combo! + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[++m_curFightMove].animId, 8.0f); + animAssoc->SetFinishCallback(FinishFightMoveCB, this); + animAssoc->SetCurrentTime(0.1f * animAssoc->hierarchy->totalLength); + animAssoc->speed = 0.8f; + m_fightButtonPressure = 0; + nPlayerInComboMove = 1; + } + } + } + + } else if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED && GetWeapon()->m_eWeaponType != WEAPONTYPE_BRASSKNUCKLE && !fightWithWeapon) { + EndFight(ENDFIGHT_FAST); + + } else if (m_fightButtonPressure != 0) { + if (!IsPlayer()) + Say(SOUND_PED_ATTACK); + + if (m_curFightMove != FIGHTMOVE_IDLE) + m_lastFightMove = m_curFightMove; + + m_curFightMove = IsPlayer() ? ChooseAttackPlayer(m_fightButtonPressure, fightWithWeapon) : ChooseAttackAI(m_fightButtonPressure, fightWithWeapon); + + if (m_curFightMove != FIGHTMOVE_IDLE) { + + animAssoc = CAnimManager::BlendAnimation(GetClump(), m_curFightMove < FIGHTMOVE_MELEE1 ? ASSOCGRP_STD : weaponInfo->m_AnimToPlay, + tFightMoves[m_curFightMove].animId, 8.0f); + + if (weaponInfo->m_AnimToPlay != ASSOCGRP_KNIFE || m_curFightMove < FIGHTMOVE_MELEE1) { + if (m_curFightMove == FIGHTMOVE_BACKKICK) + animAssoc->speed = 1.15f; + else + animAssoc->speed = 0.8f; + } else { + switch (GetWeapon()->m_eWeaponType) { + case WEAPONTYPE_SCREWDRIVER: + case WEAPONTYPE_KNIFE: + animAssoc->speed = 1.05f; + break; + case WEAPONTYPE_GOLFCLUB: + case WEAPONTYPE_NIGHTSTICK: + case WEAPONTYPE_BASEBALLBAT: + case WEAPONTYPE_HAMMER: + case WEAPONTYPE_KATANA: + animAssoc->speed = 0.8f; + break; + case WEAPONTYPE_CLEAVER: + case WEAPONTYPE_MACHETE: + animAssoc->speed = 0.9f; + break; + } + } + + if (m_fightState == FIGHTSTATE_MOVE_FINISHED && animAssoc->currentTime != 0.0f) { + animAssoc->SetRun(); + if (!IsPlayer()) + animAssoc->SetCurrentTime(0.0f); + } + if (IsPlayer()) + animAssoc->SetCurrentTime(0.08f); + + animAssoc->SetFinishCallback(FinishFightMoveCB, this); + m_fightButtonPressure = 0; + } + m_fightState = FIGHTSTATE_NO_MOVE; + } else if (m_takeAStepAfterAttack && m_curFightMove != FIGHTMOVE_SHUFFLE_F +#ifndef FIX_BUGS + && CheckForPedsOnGroundToAttack(this, nil) == 4) { +#else + && CheckForPedsOnGroundToAttack(this, nil) == PED_IN_FRONT_OF_ATTACKER) { +#endif + m_lastFightMove = m_curFightMove; + m_curFightMove = FIGHTMOVE_SHUFFLE_F; + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), tFightMoves[m_curFightMove].animId); + + if (animAssoc) { + animAssoc->SetCurrentTime(0.0f); + animAssoc->blendDelta = 4.0f; + animAssoc->SetRun(); + } else { + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[m_curFightMove].animId, 32.0f); + } + animAssoc->SetFinishCallback(FinishFightMoveCB, this); + m_fightState = FIGHTSTATE_NO_MOVE; + m_fightButtonPressure = 0; + m_takeAStepAfterAttack = false; + + } else if (m_takeAStepAfterAttack) { + EndFight(ENDFIGHT_FAST); + + } else if (m_curFightMove == FIGHTMOVE_IDLE) { + if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { + EndFight(ENDFIGHT_NORMAL); + } + + } else { + m_lastFightMove = m_curFightMove; + m_curFightMove = FIGHTMOVE_IDLE; + if (IsPlayer()) + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 500; + else + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 2000; + } +} + +int32 +CPed::ChooseAttackAI(uint8 buttonPressure, bool fightWithWeapon) +{ + eWeaponType weapon = GetWeapon()->m_eWeaponType; + CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(weapon); + if (!fightWithWeapon && weapon != WEAPONTYPE_UNARMED && weapon != WEAPONTYPE_BRASSKNUCKLE) { + return FIGHTMOVE_PUNCH; + } + + if (!m_pedInObjective) + return FIGHTMOVE_IDLE; + if (buttonPressure == 0) + return FIGHTMOVE_IDLE; + + uint16 pedFeatures = m_pedStats->m_flags; + bool punchOnly = !!(pedFeatures & STAT_PUNCH_ONLY); + bool canRoundhouse = !!(pedFeatures & STAT_CAN_ROUNDHOUSE); + bool canKneeHead = !!(pedFeatures & STAT_CAN_KNEE_HEAD); + bool canKick = !!(pedFeatures & STAT_CAN_KICK); + bool hasShoppingBags = !!(pedFeatures & STAT_SHOPPING_BAGS); + + CVector distVec(m_pedInObjective->GetPosition() - GetPosition()); + float dist = distVec.Magnitude(); + m_fRotationDest = CGeneral::LimitRadianAngle(distVec.Heading()); + m_fRotationCur = m_fRotationDest; + + if (fightWithWeapon) { + if (m_pedInObjective->OnGroundOrGettingUp()) { + if (CGame::nastyGame && dist < 1.2f && !m_pedInObjective->IsPlayer() + && (m_pedInObjective->m_nPedState == PED_DEAD || !m_pedInObjective->IsPedHeadAbovePos(-0.3f))) { + if (weaponInfo->IsFlagSet(WEAPONFLAG_GROUND_2ND)) + return FIGHTMOVE_MELEE2; + if (weaponInfo->IsFlagSet(WEAPONFLAG_GROUND_3RD)) + return FIGHTMOVE_MELEE3; + + return FIGHTMOVE_GROUNDKICK; + } else { + return FIGHTMOVE_IDLE; + } + } + if (dist < 2.f) { + if (m_curFightMove == FIGHTMOVE_MELEE1) { + if (GetSecondFireAnim(weaponInfo)) + return FIGHTMOVE_MELEE2; + } + if (m_curFightMove == FIGHTMOVE_MELEE2) { + if (GetFinishingAttackAnim(weaponInfo)) + return FIGHTMOVE_MELEE3; + } + return FIGHTMOVE_MELEE1; + } + return FIGHTMOVE_SHUFFLE_F; + } + if (!hasShoppingBags) { + if (punchOnly) { + if (dist < 1.4f) + return FIGHTMOVE_PUNCH; + } else { + if (m_pedInObjective->OnGroundOrGettingUp()) { + if (CGame::nastyGame && dist < 1.2f && !m_pedInObjective->IsPlayer() + && (m_pedInObjective->m_nPedState == PED_DEAD || !m_pedInObjective->IsPedHeadAbovePos(-0.3f))) { + + return FIGHTMOVE_GROUNDKICK; + } else { + return FIGHTMOVE_IDLE; + } + } + if (dist < 0.95f && canKneeHead) + return FIGHTMOVE_KNEE; + if (dist < 1.4f) + return FIGHTMOVE_PUNCH; + if (dist < 2.f && canKick) { + int nextMove = FIGHTMOVE_LONGKICK; + if (canRoundhouse && CGeneral::GetRandomNumber() & 1) + nextMove = FIGHTMOVE_ROUNDHOUSE; + return nextMove; + } + } + return FIGHTMOVE_SHUFFLE_F; + } + if (dist < 2.f) + return FIGHTMOVE_ROUNDHOUSE; + else + return FIGHTMOVE_SHUFFLE_F; +} + +int32 +CPed::ChooseAttackPlayer(uint8 buttonPressure, bool fightWithWeapon) +{ + CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + const float maxAttackDist = 2.7f; + float weaponAttackDist = 2.0f; + CPed *victimPed = nil; + CPed *walkUpTo = nil; + CPed *groundAttackDeadPed = nil; + CPed *groundAttackAlivePed = nil; + if (fightWithWeapon) + weaponAttackDist = weaponInfo->m_fRange; + + bool willWalkUp = false; + PedFightMoves choosenMove = FIGHTMOVE_IDLE; + int numPedsWeCanReach = 0; + if (m_takeAStepAfterAttack) + willWalkUp = true; + + float groundAttackDeadAngle, groundAttackAliveAngle, walkAngle, victimAngle, distToVictim; + + for (int i = 0; i < m_numNearPeds; ++i) { + CPed *nearPed = m_nearPeds[i]; + CVector distVec(nearPed->GetPosition() - GetPosition()); + float dist = distVec.Magnitude(); + if (dist < maxAttackDist) { + float nearPedAngle = CGeneral::LimitRadianAngle(distVec.Heading()); + m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); + float neededTurn = Abs(nearPedAngle - m_fRotationCur); + if (neededTurn > PI) + neededTurn = TWOPI - neededTurn; + + if (!nearPed->OnGroundOrGettingUp() && nearPed->m_nWaitState != WAITSTATE_SUN_BATHE_IDLE) { + if (!willWalkUp || neededTurn <= DEGTORAD(45.0f)) { + + if (neededTurn <= DEGTORAD(30.0f) || nearPed->m_pedInObjective == this + && (nearPed->m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || nearPed->m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS)) { + + if (dist < weaponAttackDist) { + if (!victimPed + || nearPed->m_attackTimer < victimPed->m_attackTimer && nearPed->m_attackTimer > CTimer::GetTimeInMilliseconds() - 100) { + victimPed = nearPed; + victimAngle = nearPedAngle; + distToVictim = dist; + } + ++numPedsWeCanReach; + + } else { + if (neededTurn < DEGTORAD(30.0f)) { + walkUpTo = nearPed; + walkAngle = nearPedAngle; + } + } + } + } + } else if (CGame::nastyGame && dist < 1.2f && neededTurn < DEGTORAD(55.0f)) { + if (!nearPed->DyingOrDead() || groundAttackDeadPed) { + if (!nearPed->IsPedHeadAbovePos(-0.3f)) { + groundAttackAlivePed = nearPed; + groundAttackAliveAngle = nearPedAngle; + } + } else { + groundAttackDeadPed = nearPed; + groundAttackDeadAngle = nearPedAngle; + } + ++numPedsWeCanReach; + + } else if (dist > 1.4f && dist < maxAttackDist && neededTurn < DEGTORAD(30.0f)) { + if (!walkUpTo) { + walkUpTo = nearPed; + walkAngle = nearPedAngle; + } +#ifdef FIX_BUGS + if(dist < 2.1f) +#endif + ++numPedsWeCanReach; + } + } + } + + if (victimPed) { + float adjustedAngleDiff = victimAngle - m_fRotationCur + DEGTORAD(30.0f); + if (adjustedAngleDiff < 0.0f) + adjustedAngleDiff += TWOPI; + + int16 dir = Floor(adjustedAngleDiff / DEGTORAD(60.0f)); + + // Just focus on who we're fighting with, don't care peds on ground + if (numPedsWeCanReach < 2 || fightWithWeapon) { + float angleDiff = Abs(victimAngle - m_fRotationCur); + if (angleDiff > PI) + angleDiff = TWOPI - angleDiff; + + if (angleDiff < DEGTORAD(60.0f)) + dir = 0; // forward + } + int16 randVal = CGeneral::GetRandomNumber() & 3; + switch (dir) { + case 0: // forward + if (fightWithWeapon) { + if (distToVictim < 0.95f - 0.2f && m_nPedState == PED_FIGHT) { + choosenMove = FIGHTMOVE_KNEE; + } else { + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_CLEAVER) { + if (distToVictim < 0.85f * weaponInfo->m_fRange) + choosenMove = FIGHTMOVE_MELEE1; + else + choosenMove = FIGHTMOVE_SHUFFLE_F; + } else { + float weaponRange = weaponInfo->m_fRange; + if (distToVictim < 0.75f * weaponRange && GetWeapon()->m_eWeaponType != WEAPONTYPE_SCREWDRIVER) { + if (m_lastFightMove == FIGHTMOVE_MELEE1 && GetFinishingAttackAnim(weaponInfo)) { + choosenMove = FIGHTMOVE_MELEE2; + } else if (m_lastFightMove == FIGHTMOVE_MELEE2 && GetFinishingAttackAnim(weaponInfo)) { + choosenMove = FIGHTMOVE_MELEE3; + } else { + choosenMove = FIGHTMOVE_MELEE1; + } + } else if (distToVictim < weaponRange && GetFinishingAttackAnim(weaponInfo)) { + choosenMove = FIGHTMOVE_MELEE3; + } else { + choosenMove = FIGHTMOVE_SHUFFLE_F; + } + } + } + } else if (distToVictim < 0.95f && m_nPedState == PED_FIGHT) { + choosenMove = FIGHTMOVE_KNEE; + + } else if (distToVictim < 1.4f) { + if (m_curFightMove == FIGHTMOVE_PUNCHJAB) { + choosenMove = FIGHTMOVE_PUNCH; + + } else if (m_curFightMove != FIGHTMOVE_PUNCH || randVal != 1) { + if (randVal == 2) + choosenMove = FIGHTMOVE_PUNCH; + else + choosenMove = FIGHTMOVE_PUNCHJAB; + } else { + choosenMove = FIGHTMOVE_LONGKICK; + } + } else { + choosenMove = FIGHTMOVE_LONGKICK; + } + break; + case 1: + choosenMove = FIGHTMOVE_FWDLEFT; + break; + case 2: + choosenMove = FIGHTMOVE_BACKLEFT; + break; + case 3: + choosenMove = FIGHTMOVE_BACKKICK; + break; + case 4: + choosenMove = FIGHTMOVE_BACKRIGHT; + break; + default: + choosenMove = FIGHTMOVE_FWDRIGHT; + break; + } + + // forward + if (dir == 0) { + m_fRotationDest = CGeneral::LimitRadianAngle(victimAngle); + } else { + m_fRotationDest = victimAngle - dir * DEGTORAD(60.0f); + m_fRotationDest = CGeneral::LimitRadianAngle(m_fRotationDest); + } + + m_fRotationCur = m_fRotationDest; + Say(SOUND_PED_ATTACK); + + } else if (groundAttackAlivePed || groundAttackDeadPed) { + if (fightWithWeapon && weaponInfo->IsFlagSet(WEAPONFLAG_GROUND_2ND)) { + choosenMove = FIGHTMOVE_MELEE2; + } else if (fightWithWeapon && weaponInfo->IsFlagSet(WEAPONFLAG_GROUND_3RD)) { + choosenMove = FIGHTMOVE_MELEE3; + } else { + choosenMove = FIGHTMOVE_GROUNDKICK; + } + if (groundAttackAlivePed) + m_fRotationDest = groundAttackAliveAngle; + else + m_fRotationDest = groundAttackDeadAngle; + + m_fRotationCur = m_fRotationDest; + m_lookTimer = 0; + if (groundAttackAlivePed) + SetLookFlag(groundAttackAlivePed, 1, 0); + else + SetLookFlag(groundAttackDeadPed, 1, 0); + + SetLookTimer(1500u); + + } else if (walkUpTo) { + choosenMove = FIGHTMOVE_SHUFFLE_F; + m_fRotationCur = m_fRotationDest = walkAngle; + m_lookTimer = 0; + SetLookFlag(walkUpTo, true); + SetLookTimer(1500); + + } else if (fightWithWeapon) { + // No enemy, fight with space + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_SCREWDRIVER) { + choosenMove = FIGHTMOVE_MELEE3; + } else { + if (m_lastFightMove == FIGHTMOVE_MELEE1 && GetFinishingAttackAnim(weaponInfo)) { + choosenMove = FIGHTMOVE_MELEE2; + } else if (m_lastFightMove == FIGHTMOVE_MELEE2 && GetFinishingAttackAnim(weaponInfo)) { + choosenMove = FIGHTMOVE_MELEE3; + } else { + choosenMove = FIGHTMOVE_MELEE1; + } + } + } else { + // Max number GetRandomNumberInRange returns is max-1 +#ifdef FIX_BUGS + switch (CGeneral::GetRandomNumberInRange(0,4)) { +#else + switch (CGeneral::GetRandomNumberInRange(0,3)) { +#endif + case 0: + choosenMove = FIGHTMOVE_PUNCHJAB; + break; + case 1: + choosenMove = FIGHTMOVE_PUNCH; + break; + case 2: + choosenMove = FIGHTMOVE_LONGKICK; + break; + case 3: + choosenMove = FIGHTMOVE_KNEE; + break; + default: + break; + } + } + return choosenMove; +} + +void +CPed::EndFight(uint8 endType) +{ + if (m_nPedState != PED_FIGHT) + return; + + m_curFightMove = FIGHTMOVE_NULL; + RestorePreviousState(); + CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FIGHT_IDLE); + if (!animAssoc) + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_MELEE_IDLE_FIGHTMODE); + + if (animAssoc) + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + + switch (endType) { + case ENDFIGHT_NORMAL: + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 8.0f); + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FIGHT_2IDLE, 8.0f); + break; + case ENDFIGHT_WITH_A_STEP: + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 1.0f); + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_STARTWALK, 8.0f); + break; + case ENDFIGHT_FAST: + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 8.0f); + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FIGHT_2IDLE, 8.0f)->speed = 2.0f; + break; + default: + break; + } + m_nWaitTimer = 0; +} + +void +CPed::PlayHitSound(CPed *hitTo) +{ + // That was very complicated to reverse for me... + // First index is our fight move ID (from 1 to 17, total 17), second is the one of we fight with (from 18 to 27, total 10). + enum { + S37 = SOUND_FIGHT_37, + S38 = SOUND_FIGHT_38, + S39 = SOUND_FIGHT_39, + S40 = SOUND_FIGHT_40, + S41 = SOUND_FIGHT_41, + S42 = SOUND_FIGHT_42, + S43 = SOUND_FIGHT_43, + S44 = SOUND_FIGHT_44, + S45 = SOUND_FIGHT_45, + S46 = SOUND_FIGHT_46, + S47 = SOUND_FIGHT_47, + S48 = SOUND_FIGHT_48, + NO_SND = SOUND_NO_SOUND + }; + const uint16 hitSoundsByFightMoves[17][10] = { + { S37, S46, S41, S41, S46, S46, S40, S41, S43, S40 }, + { NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND }, + { NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND }, + { S46, S46, S46, S46, S37, S47, S37, S38, S43, S38 }, + { S46, S46, S46, S46, S46, S46, S40, S41, S43, S46 }, + { S46, S46, S46, S46, S46, S46, S40, S41, S43, S40 }, + { S46, S46, S46, S46, S46, S46, S40, S41, S43, S40 }, + { S46, S46, S37, S46, S37, S47, S40, S47, S43, S37 }, + { S46, S46, S46, S46, S46, S46, S43, S44, S43, S43 }, + { S37, S46, S46, S46, S38, S47, S40, S38, S43, S46 }, + { S46, S37, S46, S37, S39, S46, S40, S39, S43, S37 }, + { S46, S37, S46, S46, S38, S47, S40, S38, S43, S46 }, + { S37, S37, S46, S46, S38, S47, S48, S38, S43, S37 }, + { S46, S46, S46, S46, S37, S46, S40, S38, S43, S46 }, + { S46, S46, S46, S37, S39, S46, S40, S39, S43, S46 }, + { S37, S46, S46, S46, S37, S46, S40, S37, S43, S46 }, + { S43, S43, S43, S43, S43, S43, S43, S43, S43, S43 } + }; + + eWeaponType weapon = GetWeapon()->m_eWeaponType; + CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(weapon); + if (weaponInfo->m_AnimToPlay == ASSOCGRP_KNIFE) { + if (m_curFightMove >= FIGHTMOVE_MELEE1) { + if (m_curFightMove == FIGHTMOVE_MELEE3) { + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_BAT_ATTACK, (weapon << 8) | ENTITY_TYPE_PED); + } else { + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_KNIFE_ATTACK, (weapon << 8) | ENTITY_TYPE_PED); + } + return; + } + } + + // This is why first dimension is between FightMove 1 and 17. + if (m_curFightMove <= FIGHTMOVE_NULL || m_curFightMove >= FIGHTMOVE_HITFRONT) + return; + + uint16 soundId; + + // And this is why second dimension is between 18 and 27. + if (hitTo->m_curFightMove > FIGHTMOVE_GROUNDKICK && hitTo->m_curFightMove < FIGHTMOVE_IDLE2NORM) { + soundId = hitSoundsByFightMoves[m_curFightMove - FIGHTMOVE_STDPUNCH][hitTo->m_curFightMove - FIGHTMOVE_HITFRONT]; + + } else { + if (hitTo->m_nPedState == PED_DEAD || hitTo->UseGroundColModel()) { + soundId = hitSoundsByFightMoves[m_curFightMove - FIGHTMOVE_STDPUNCH][FIGHTMOVE_HITONFLOOR - FIGHTMOVE_HITFRONT]; + } else { + soundId = hitSoundsByFightMoves[m_curFightMove - FIGHTMOVE_STDPUNCH][FIGHTMOVE_HITFRONT - FIGHTMOVE_HITFRONT]; + } + } + + if (soundId != NO_SND) + DMAudio.PlayOneShot(m_audioEntityId, soundId, (weapon << 8) | ENTITY_TYPE_PED); +} + +bool +CPed::FightStrike(CVector &touchedNodePos, bool fightWithWeapon) +{ + CColModel *hisCol; + CVector attackDistance; + float maxDistanceToBeat; + CPed *nearPed; + CVector extendedTouchPoint; + + CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + float radius = tFightMoves[m_curFightMove].strikeRadius; + if (fightWithWeapon) + radius = weaponInfo->m_fRadius; + + if (m_fightState == FIGHTSTATE_JUST_ATTACKED) + return false; + + if (this == FindPlayerPed() && fightWithWeapon && GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) + CGlass::BreakGlassPhysically(touchedNodePos, radius); + + for (int i = 0; i < m_numNearPeds; i++) { + int8 pedFound = 0; + nearPed = m_nearPeds[i]; + if (!fightWithWeapon && GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED && GetWeapon()->m_eWeaponType != WEAPONTYPE_BRASSKNUCKLE) + maxDistanceToBeat = nearPed->GetBoundRadius() + radius + 0.1f; + else + maxDistanceToBeat = nearPed->GetBoundRadius() + radius; + + if ((nearPed->bUsesCollision || nearPed->m_nPedState == PED_DEAD) && (m_pedInObjective != FindPlayerPed() || nearPed == FindPlayerPed())) { + CVector nearPedCentre; + + // Have to animate a skinned clump because the initial col model is useless + hisCol = ((CPedModelInfo*)CModelInfo::GetModelInfo(nearPed->GetModelIndex()))->AnimatePedColModelSkinnedWorld(nearPed->GetClump()); + + nearPed->GetBoundCentre(nearPedCentre); + CVector potentialAttackDistance = nearPedCentre - touchedNodePos; + + // He can beat us + if (sq(maxDistanceToBeat) > potentialAttackDistance.MagnitudeSqr()) { + + for (int j = 0; j < hisCol->numSpheres; j++) { + attackDistance = hisCol->spheres[j].center; + attackDistance -= touchedNodePos; + CColSphere *hisPieces = hisCol->spheres; + maxDistanceToBeat = hisPieces[j].radius + radius; + + // We can beat him too + if (sq(maxDistanceToBeat) > attackDistance.MagnitudeSqr()) { + FightHitPed(nearPed, touchedNodePos, attackDistance, hisPieces[j].piece); + pedFound = 1; + break; + } + } + } + if (!pedFound && !fightWithWeapon) { + extendedTouchPoint = touchedNodePos - GetPosition(); + if (DotProduct(touchedNodePos - GetPosition(), nearPed->GetPosition() - GetPosition()) > 0.f) { + if (m_curFightMove == FIGHTMOVE_GROUNDKICK) { + extendedTouchPoint += tFightMoves[FIGHTMOVE_GROUNDKICK].extendReachMultiplier * GetForward(); + } else { + extendedTouchPoint.x *= tFightMoves[m_curFightMove].extendReachMultiplier; + extendedTouchPoint.y *= tFightMoves[m_curFightMove].extendReachMultiplier; + } + pedFound = -1; + extendedTouchPoint += GetPosition(); + } + } + if (pedFound == -1) { + CVector nearPedCentre = nearPed->GetBoundCentre(); + if (sq(maxDistanceToBeat) > (nearPedCentre - extendedTouchPoint).MagnitudeSqr()) { + + for (int j = 0; j < hisCol->numSpheres; j++) { + attackDistance = hisCol->spheres[j].center; + attackDistance -= extendedTouchPoint; + CColSphere* hisPieces = hisCol->spheres; + float maxDistanceToBeat2 = hisPieces[j].radius + tFightMoves[m_curFightMove].strikeRadius; + + // We can beat him too + if (sq(maxDistanceToBeat2) > attackDistance.MagnitudeSqr()) { + FightHitPed(nearPed, extendedTouchPoint, attackDistance, hisPieces[j].piece); + break; + } + } + } + } + } + } + + + if (m_fightState == FIGHTSTATE_NO_MOVE) + m_fightState = FIGHTSTATE_1; + + m_vecHitLastPos = touchedNodePos; + return false; +} + +void +CPed::FightHitPed(CPed *victim, CVector &touchPoint, CVector &dir, int16 piece) +{ + if (victim->IsPlayer() && victim->m_nPedState == PED_GETUP) + return; + + CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + bool fightingWithWeapon = false; + int damageMult = tFightMoves[m_curFightMove].damage * ((CGeneral::GetRandomNumber() & 1) + 2) + 1; + + if (weaponInfo->IsFlagSet(WEAPONFLAG_FIGHTMODE)) { + fightingWithWeapon = true; + if (m_curFightMove >= FIGHTMOVE_MELEE1) { + damageMult = weaponInfo->m_nDamage; + if (m_curFightMove == FIGHTMOVE_MELEE3 && GetWeapon()->m_eWeaponType != WEAPONTYPE_SCREWDRIVER) + damageMult *= 5; + } + } + + if (IsPlayer()) { + if (((CPlayerPed*)this)->m_bAdrenalineActive) + damageMult = 20; + } else if (!fightingWithWeapon) { + damageMult *= m_pedStats->m_attackStrength; + } + + float oldVictimHealth = victim->m_fHealth; + CVector bloodPos = 0.5f * dir + touchPoint; + CVector2D diff(GetPosition() - victim->GetPosition()); + int direction = victim->GetLocalDirection(diff); + + bool brassKnucklePunch = false; + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_BRASSKNUCKLE) { + if (m_curFightMove == FIGHTMOVE_PUNCHHOOK || m_curFightMove == FIGHTMOVE_PUNCHJAB || m_curFightMove == FIGHTMOVE_BACKLEFT || + m_curFightMove == FIGHTMOVE_STDPUNCH || m_curFightMove == FIGHTMOVE_PUNCH) { + brassKnucklePunch = true; + damageMult *= 1.5f; + } + } + victim->ReactToAttack(this); + + // Mostly unused. if > 5, ANIM_HIT_BIGSTEP will be run, that's it. + int unk2; + if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED && GetWeapon()->m_eWeaponType != WEAPONTYPE_BRASSKNUCKLE && + !victim->IsPlayer() && !fightingWithWeapon) + unk2 = 101; + else + unk2 = damageMult; + + victim->StartFightDefend(direction, tFightMoves[m_curFightMove].hitLevel, unk2); + PlayHitSound(victim); + m_fightState = FIGHTSTATE_JUST_ATTACKED; + RpAnimBlendClumpGetAssociation(GetClump(), tFightMoves[m_curFightMove].animId)->speed = 0.6f; + + if (!victim->DyingOrDead()) { + if(fightingWithWeapon) + victim->InflictDamage(this, GetWeapon()->m_eWeaponType, damageMult, (ePedPieceTypes)piece, direction); + else + victim->InflictDamage(this, WEAPONTYPE_UNARMED, damageMult * 3.0f, (ePedPieceTypes)piece, direction); + } + + if (CGame::nastyGame && weaponInfo->m_AnimToPlay == ASSOCGRP_KNIFE && m_curFightMove >= FIGHTMOVE_MELEE1 + && victim->GetIsOnScreen()) { + + static float particleRightLen = 0.05f; + static float particleUpLen = 0.05f; + + // Just for particles. We will restore it below. + dir /= (20.0f * dir.Magnitude()); + if (m_curFightMove == FIGHTMOVE_MELEE1) { + float rightMult = -particleRightLen; + dir += particleUpLen * GetUp() + rightMult * GetRight(); + + } else if (m_curFightMove == FIGHTMOVE_MELEE2) { + float upMult = 2.0f * particleUpLen; + dir += particleRightLen * GetRight() + upMult * GetUp(); + } + CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir, nil, 0.0f, 0, 0, 0, 0); + CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir, nil, 0.0f, 0, 0, 0, 0); + if (IsPlayer()) { + CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir, nil, 0.0f, 0, 0, 0, 0); + CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir, nil, 0.0f, 0, 0, 0, 0); + } + if (!(CGeneral::GetRandomNumber() & 3)) { + CParticle::AddParticle(PARTICLE_TEST, bloodPos, dir, nil, 0.0f, 0, 0, 0, 0); + } + } else if (CGame::nastyGame && (tFightMoves[m_curFightMove].hitLevel > HITLEVEL_MEDIUM || fightingWithWeapon) + && victim->GetIsOnScreen()) { + + // Just for particles. We will restore it below. + dir /= (10.0f * dir.Magnitude()); + for (int i = 0; i < 4; i++) { + CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, dir, nil, 0.0f, 0, 0, 0, 0); + } + } + + eWeaponType weaponType = GetWeapon()->m_eWeaponType; + if (!fightingWithWeapon) { + if (!victim->OnGround()) { + float curVictimHealth = victim->m_fHealth; + if (curVictimHealth > 0.0f + && (curVictimHealth < 30.0f && oldVictimHealth > 30.0f + || weaponType != WEAPONTYPE_UNARMED && weaponType != WEAPONTYPE_BRASSKNUCKLE && IsPlayer() + || victim->m_pedStats->m_flags & STAT_ONE_HIT_KNOCKDOWN || brassKnucklePunch)) { + + victim->SetFall(0, (AnimationId)(direction + ANIM_STD_HIGHIMPACT_FRONT), 0); + if (victim->m_nPedState == PED_FALL) + victim->bIsStanding = false; + } + } + } + + if (victim->m_nPedState == PED_DIE || !victim->bIsStanding) { + dir = victim->GetPosition() - GetPosition(); + dir.Normalise(); + dir.z = 1.0f; + victim->bIsStanding = false; + + float moveMult; + if (fightingWithWeapon) { + moveMult = Min(damageMult * 0.02f, 1.0f); + } else if (m_curFightMove == FIGHTMOVE_GROUNDKICK) { + moveMult = Min(damageMult * 0.6f, 4.0f); + } else { + if (victim->m_nPedState != PED_DIE || damageMult >= 20) { + moveMult = damageMult; + } else { + moveMult = Min(damageMult * 2.0f, 14.0f); + } + } + + victim->ApplyMoveForce(moveMult * 0.6 * dir); + } + + if (weaponType != WEAPONTYPE_KNIFE && weaponType != WEAPONTYPE_MACHETE + && weaponType != WEAPONTYPE_KATANA && weaponType != WEAPONTYPE_CHAINSAW) { + + if (victim->m_nPedType == PEDTYPE_COP) + CEventList::RegisterEvent(EVENT_ASSAULT_POLICE, EVENT_ENTITY_PED, victim, this, 2000); + else + CEventList::RegisterEvent(EVENT_ASSAULT, EVENT_ENTITY_PED, victim, this, 2000); + } else { + if (victim->m_nPedType == PEDTYPE_COP) + CEventList::RegisterEvent(EVENT_ASSAULT_NASTYWEAPON_POLICE, EVENT_ENTITY_PED, victim, this, 2000); + else + CEventList::RegisterEvent(EVENT_ASSAULT_NASTYWEAPON, EVENT_ENTITY_PED, victim, this, 2000); + } +} + +void +CPed::FinishFightMoveCB(CAnimBlendAssociation *animAssoc, void *arg) +{ + CPed *ped = (CPed*)arg; + + if (tFightMoves[ped->m_curFightMove].animId == animAssoc->animId) { + ped->m_fightState = FIGHTSTATE_MOVE_FINISHED; + animAssoc->blendDelta = -1000.0f; + } +} + +void +CPed::LoadFightData(void) +{ + float startFireTime, endFireTime, comboFollowOnTime, strikeRadius, extendReachMultiplier; + int damage, flags; + char line[256], moveName[32], animName[32], hitLevel; + int moveId = 0; + + CAnimBlendAssociation *animAssoc; + + size_t bp, buflen; + int lp, linelen; + + buflen = CFileMgr::LoadFile("DATA\\fistfite.dat", work_buff, sizeof(work_buff), "r"); + + for (bp = 0; bp < buflen; ) { + // read file line by line + for (linelen = 0; work_buff[bp] != '\n' && bp < buflen; bp++) { + line[linelen++] = work_buff[bp]; + } + bp++; + line[linelen] = '\0'; + + // skip white space + for (lp = 0; line[lp] <= ' ' && line[lp] != '\0'; lp++); + + if (line[lp] == '\0' || + line[lp] == '#') + continue; + + sscanf( + &line[lp], + "%s %f %f %f %f %f %c %s %d %d", + moveName, + &startFireTime, + &endFireTime, + &comboFollowOnTime, + &strikeRadius, + &extendReachMultiplier, + &hitLevel, + animName, + &damage, + &flags); + + if (strncmp(moveName, "ENDWEAPONDATA", 13) == 0) + return; + + tFightMoves[moveId].startFireTime = startFireTime / 30.0f; + tFightMoves[moveId].endFireTime = endFireTime / 30.0f; + tFightMoves[moveId].comboFollowOnTime = comboFollowOnTime / 30.0f; + tFightMoves[moveId].strikeRadius = strikeRadius; + tFightMoves[moveId].extendReachMultiplier = extendReachMultiplier; + tFightMoves[moveId].damage = damage; + tFightMoves[moveId].flags = flags; + + switch (hitLevel) { + case 'G': + tFightMoves[moveId].hitLevel = HITLEVEL_GROUND; + break; + case 'H': + tFightMoves[moveId].hitLevel = HITLEVEL_HIGH; + break; + case 'L': + tFightMoves[moveId].hitLevel = HITLEVEL_LOW; + break; + case 'M': + tFightMoves[moveId].hitLevel = HITLEVEL_MEDIUM; + break; + case 'N': + tFightMoves[moveId].hitLevel = HITLEVEL_NULL; + break; + default: + break; + } + + if (strcmp(animName, "default") != 0) { + if (strcmp(animName, "null") != 0) { + animAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, animName); + tFightMoves[moveId].animId = (AnimationId)animAssoc->animId; + } else { + tFightMoves[moveId].animId = ANIM_STD_WALK; + } + } + moveId++; + } +} + +void +CPed::SetInvestigateEvent(eEventType event, CVector2D pos, float distanceToCountDone, uint16 time, float angle) +{ + if (!IsPedInControl() || CharCreatedBy == MISSION_CHAR) + return; + + SetStoredState(); + bFindNewNodeAfterStateRestore = false; + SetPedState(PED_INVESTIGATE); + m_chatTimer = CTimer::GetTimeInMilliseconds() + time; + m_eventType = event; + m_eventOrThreat = pos; + m_distanceToCountSeekDone = distanceToCountDone; + m_fAngleToEvent = angle; + + if (m_eventType >= EVENT_ICECREAM) + m_lookTimer = 0; + else + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HANDSCOWER, 4.0f); + +} + +void +CPed::InvestigateEvent(void) +{ + CAnimBlendAssociation *animAssoc; + AnimationId animToPlay; + AssocGroupId animGroup; + + if (m_nWaitState == WAITSTATE_TURN180) + return; + + if (CTimer::GetTimeInMilliseconds() > m_chatTimer) { + + if (m_chatTimer) { + if (m_eventType < EVENT_UNK) + SetWaitState(WAITSTATE_TURN180, nil); + + m_chatTimer = 0; + } else { + ClearInvestigateEvent(); + } + return; + } + + CVector2D vecDist = m_eventOrThreat - GetPosition(); + float distSqr = vecDist.MagnitudeSqr(); + if (sq(m_distanceToCountSeekDone) >= distSqr) { + + m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(vecDist.x, vecDist.y, 0.0f, 0.0f); + SetMoveState(PEDMOVE_STILL); + + switch (m_eventType) { + case EVENT_DEAD_PED: + case EVENT_HIT_AND_RUN: + case EVENT_HIT_AND_RUN_COP: + + if (CTimer::GetTimeInMilliseconds() > m_lookTimer) { + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROADCROSS); + + if (animAssoc) { + animAssoc->blendDelta = -8.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + if (m_pEventEntity) + SetLookFlag(m_pEventEntity, true); + + SetLookTimer(CGeneral::GetRandomNumberInRange(1500, 4000)); + + } else if (CGeneral::GetRandomNumber() & 3) { + ClearLookFlag(); + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ROADCROSS, 4.0f); + + SetLookTimer(CGeneral::GetRandomNumberInRange(1000, 2500)); + Say(SOUND_PED_CHAT_EVENT); + + } else { + ClearInvestigateEvent(); + } + } + break; + case EVENT_FIRE: + case EVENT_EXPLOSION: + + if (bHasACamera && CTimer::GetTimeInMilliseconds() > m_lookTimer) { + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_CAM); + if (!animAssoc) + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE); + + if (animAssoc && animAssoc->animId == ANIM_STD_IDLE_CAM) { + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 4.0f); + SetLookTimer(CGeneral::GetRandomNumberInRange(1000, 2500)); + + } else if (CGeneral::GetRandomNumber() & 3) { + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE_CAM, 4.0f); + SetLookTimer(CGeneral::GetRandomNumberInRange(2500, 5000)); + if (!CGame::germanGame) + Say(SOUND_PED_CHAT_EVENT); + + } else { + m_chatTimer = 0; + } + + } else if (CTimer::GetTimeInMilliseconds() > m_lookTimer) { + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE); + if (!animAssoc) + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_HBHB); + + if (!animAssoc) + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_XPRESS_SCRATCH); + + if (animAssoc && animAssoc->animId == ANIM_STD_IDLE) { + if (CGeneral::GetRandomNumber() & 1) + animToPlay = ANIM_STD_IDLE_HBHB; + else + animToPlay = ANIM_STD_XPRESS_SCRATCH; + + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, animToPlay, 4.0f); + SetLookTimer(CGeneral::GetRandomNumberInRange(1500, 4000)); + + } else if (animAssoc && animAssoc->animId == ANIM_STD_IDLE_HBHB) { + animAssoc->blendDelta = -8.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + if (CGeneral::GetRandomNumber() & 1) { + animToPlay = ANIM_STD_IDLE; + animGroup = m_animGroup; + } else { + animToPlay = ANIM_STD_XPRESS_SCRATCH; + animGroup = ASSOCGRP_STD; + } + + CAnimManager::BlendAnimation(GetClump(), animGroup, animToPlay, 4.0f); + SetLookTimer(CGeneral::GetRandomNumberInRange(1000, 2500)); + + } else { + if (CGeneral::GetRandomNumber() & 1) { + animToPlay = ANIM_STD_IDLE; + animGroup = m_animGroup; + } else { + animToPlay = ANIM_STD_IDLE_HBHB; + animGroup = ASSOCGRP_STD; + } + + CAnimManager::BlendAnimation(GetClump(), animGroup, animToPlay, 4.0f); + SetLookTimer(CGeneral::GetRandomNumberInRange(1000, 2500)); + } + if (!CGame::germanGame) + Say(SOUND_PED_CHAT_EVENT); + } + break; + case EVENT_ICECREAM: + case EVENT_SHOPSTALL: + + m_fRotationDest = m_fAngleToEvent; + if (CTimer::GetTimeInMilliseconds() > m_lookTimer) { + + if (m_lookTimer) { + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROADCROSS); + + if (animAssoc) { + animAssoc->blendDelta = -8.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + if (m_eventType == EVENT_ICECREAM) + animToPlay = ANIM_STD_CHAT; + else + animToPlay = ANIM_STD_XPRESS_SCRATCH; + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, animToPlay,4.0f); + SetLookTimer(CGeneral::GetRandomNumberInRange(2000, 5000)); + + } else { + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CHAT); + if (animAssoc) { + animAssoc->blendDelta = -8.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + ClearInvestigateEvent(); + } else { + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_XPRESS_SCRATCH); + if (animAssoc) { + animAssoc->blendDelta = -8.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + ClearInvestigateEvent(); + } + } + } else { + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ROADCROSS, 4.0f); + SetLookTimer(CGeneral::GetRandomNumberInRange(1000, 2500)); + } + } + break; + default: + return; + } + } else { + m_vecSeekPos.x = m_eventOrThreat.x; + m_vecSeekPos.y = m_eventOrThreat.y; + m_vecSeekPos.z = GetPosition().z; + Seek(); + + if (m_eventType < EVENT_ICECREAM) { + if (sq(5.0f + m_distanceToCountSeekDone) < distSqr) { + SetMoveState(PEDMOVE_RUN); + return; + } + } + if (m_eventType <= EVENT_EXPLOSION || m_eventType >= EVENT_SHOPSTALL) { + SetMoveState(PEDMOVE_WALK); + return; + } + if (distSqr > sq(1.2f)) { + SetMoveState(PEDMOVE_WALK); + return; + } + + bool willStandStill = false; + for (int i = 0; i < m_numNearPeds; i++) { + if ((m_eventOrThreat - m_nearPeds[i]->GetPosition()).MagnitudeSqr() < sq(0.4f)) { + SetMoveState(PEDMOVE_STILL); + willStandStill = true; + break; + } + } + + if (!willStandStill) + SetMoveState(PEDMOVE_WALK); + } +} + +void +CPed::ClearInvestigateEvent(void) +{ + CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROADCROSS); + if (!animAssoc) + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_XPRESS_SCRATCH); + if (!animAssoc) + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_HBHB); + if (!animAssoc) + animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CHAT); + if (animAssoc) { + animAssoc->blendDelta = -8.0f; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + if (m_eventType > EVENT_EXPLOSION) + m_chatTimer = CTimer::GetTimeInMilliseconds() + 15000; + + bGonnaInvestigateEvent = false; + m_pEventEntity = nil; + ClearLookFlag(); + RestorePreviousState(); + if(m_nMoveState == PEDMOVE_NONE || m_nMoveState == PEDMOVE_STILL) + SetMoveState(PEDMOVE_WALK); +} + +bool +CPed::InflictDamage(CEntity *damagedBy, eWeaponType method, float damage, ePedPieceTypes pedPiece, uint8 direction) +{ + CPlayerPed *player = FindPlayerPed(); + float dieDelta = 4.0f; + float dieSpeed = 0.0f; + AnimationId dieAnim = ANIM_STD_KO_FRONT; + bool headShot = false; + bool willLinger = false; + int random; + + if (damagedBy == FindPlayerPed() && damagedBy != this && damage > 3.0f) + ++CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel; + + if (player == this) { + if (!player->m_bCanBeDamaged) + return false; + + if (damagedBy && damagedBy->IsPed() && ((CPed*)damagedBy)->m_nPedType == PEDTYPE_GANG7) + return false; + + if ((method == WEAPONTYPE_FLAMETHROWER || method == WEAPONTYPE_MOLOTOV) && CWorld::Players[CWorld::PlayerInFocus].m_bFireproof) + return false; + + player->AnnoyPlayerPed(false); + } + + if (DyingOrDead()) + return false; + + if (method == WEAPONTYPE_DROWNING && !bDrownsInWater) + return false; + + if (!bUsesCollision && (!bInVehicle || m_nPedState != PED_DRIVING) && method != WEAPONTYPE_DROWNING) + return false; + + if (bOnlyDamagedByPlayer && damagedBy != player && damagedBy != FindPlayerVehicle() && + method != WEAPONTYPE_DROWNING && method != WEAPONTYPE_EXPLOSION) + return false; + + float healthImpact; + if (IsPlayer()) + healthImpact = damage * 0.33f; + else + healthImpact = damage * m_pedStats->m_defendWeakness; + + if (!IsPlayer() && + (method == WEAPONTYPE_SCREWDRIVER || method == WEAPONTYPE_KNIFE || (method >= WEAPONTYPE_CLEAVER && method <= WEAPONTYPE_CHAINSAW))) + m_bleedCounter = 200; + + bool detectDieAnim = true; + if (m_nPedState == PED_GETUP) { + if (!IsPedHeadAbovePos(-0.3f)) { + if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL)) + dieAnim = ANIM_STD_HIT_FLOOR_FRONT; + else + dieAnim = ANIM_STD_HIT_FLOOR; + dieDelta *= 2.0f; + dieSpeed = 0.5f; + detectDieAnim = false; + } + } else if (m_nPedState == PED_FALL) { + CAnimBlendAssociation *fallAssoc = RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_PARTIAL); + if (!fallAssoc || fallAssoc->IsRunning()) { + if (fallAssoc && fallAssoc->blendDelta >= 0.0f) + dieAnim = ANIM_STD_NUM; + else + dieAnim = ANIM_STD_KO_FRONT; + } else { + if (fallAssoc->flags & ASSOC_FRONTAL) + dieAnim = ANIM_STD_HIT_FLOOR_FRONT; + else + dieAnim = ANIM_STD_HIT_FLOOR; + + dieDelta *= 2.0f; + dieSpeed = 0.5f; + } + detectDieAnim = false; + } + + if (detectDieAnim) { + switch (method) { + case WEAPONTYPE_UNARMED: + case WEAPONTYPE_BRASSKNUCKLE: + if (bMeleeProof) + return false; + + if (m_nPedState == PED_FALL) { + if (IsPedHeadAbovePos(-0.3f)) { + dieAnim = ANIM_STD_NUM; + } else { + if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL)) + dieAnim = ANIM_STD_HIT_FLOOR_FRONT; + else + dieAnim = ANIM_STD_HIT_FLOOR; + dieDelta = dieDelta * 2.0f; + dieSpeed = 0.5f; + } + } else { + switch (direction) { + case 0: + dieAnim = ANIM_STD_HIGHIMPACT_FRONT; + break; + case 1: + dieAnim = ANIM_STD_HIGHIMPACT_LEFT; + break; + case 2: + dieAnim = ANIM_STD_HIGHIMPACT_BACK; + break; + case 3: + dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; + break; + default: + break; + } + } + break; + case WEAPONTYPE_SCREWDRIVER: + case WEAPONTYPE_GOLFCLUB: + case WEAPONTYPE_NIGHTSTICK: + case WEAPONTYPE_KNIFE: + case WEAPONTYPE_BASEBALLBAT: + case WEAPONTYPE_HAMMER: + case WEAPONTYPE_CLEAVER: + case WEAPONTYPE_MACHETE: + case WEAPONTYPE_KATANA: + case WEAPONTYPE_CHAINSAW: + if (bMeleeProof) + return false; + + if (method != WEAPONTYPE_KATANA || + damagedBy != FindPlayerPed() + || FindPlayerPed()->m_nPedState != PED_FIGHT + || FindPlayerPed()->m_curFightMove != FIGHTMOVE_MELEE1 && FindPlayerPed()->m_curFightMove != FIGHTMOVE_MELEE2 + || CGeneral::GetRandomNumber() & 3) { + + if (m_nPedState == PED_FALL) { + if (IsPedHeadAbovePos(-0.3f)) { + dieAnim = ANIM_STD_NUM; + } else { + if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL)) + dieAnim = ANIM_STD_HIT_FLOOR_FRONT; + else + dieAnim = ANIM_STD_HIT_FLOOR; + dieDelta = dieDelta * 2.0f; + dieSpeed = 0.5f; + } + } else if (damagedBy != FindPlayerPed() || FindPlayerPed()->m_curFightMove != FIGHTMOVE_MELEE2) { + if (damagedBy != FindPlayerPed() || FindPlayerPed()->m_curFightMove != FIGHTMOVE_MELEE3) { + switch (direction) { + case 0: + dieAnim = ANIM_STD_HIGHIMPACT_FRONT; + break; + case 1: + dieAnim = ANIM_STD_HIGHIMPACT_LEFT; + break; + case 2: + dieAnim = ANIM_STD_HIGHIMPACT_BACK; + break; + case 3: + dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; + break; + default: + break; + } + } else { + dieAnim = ANIM_STD_KO_SHOT_STOMACH; + } + } else { + dieAnim = ANIM_STD_KO_SHOT_FACE; + } + } else { + dieAnim = ANIM_STD_KO_SHOT_FACE; + RemoveBodyPart(PED_HEAD, direction); + headShot = true; + willLinger = true; + } + break; + case WEAPONTYPE_COLT45: + case WEAPONTYPE_SHOTGUN: + case WEAPONTYPE_STUBBY_SHOTGUN: + case WEAPONTYPE_SPAS12_SHOTGUN: + case WEAPONTYPE_TEC9: + case WEAPONTYPE_UZI: + case WEAPONTYPE_SILENCED_INGRAM: + case WEAPONTYPE_MP5: + case WEAPONTYPE_M4: + case WEAPONTYPE_RUGER: + case WEAPONTYPE_SNIPERRIFLE: + case WEAPONTYPE_LASERSCOPE: + case WEAPONTYPE_M60: + case WEAPONTYPE_MINIGUN: + case WEAPONTYPE_UZI_DRIVEBY: + + if (bBulletProof) + return false; + + bool dontRemoveLimb; + if (IsPlayer() || bNoCriticalHits) + dontRemoveLimb = true; + else if (method != WEAPONTYPE_M4 && method != WEAPONTYPE_RUGER && method != WEAPONTYPE_SNIPERRIFLE && + method != WEAPONTYPE_LASERSCOPE) { + if (method == WEAPONTYPE_SHOTGUN) + dontRemoveLimb = CGeneral::GetRandomNumber() & 7; + else + dontRemoveLimb = CGeneral::GetRandomNumber() & 15; + } else + dontRemoveLimb = false; + + if (dontRemoveLimb) { + if (method == WEAPONTYPE_SHOTGUN) { + switch (direction) { + case 0: + dieAnim = ANIM_STD_HIGHIMPACT_FRONT; + break; + case 1: + dieAnim = ANIM_STD_HIGHIMPACT_LEFT; + break; + case 2: + dieAnim = ANIM_STD_HIGHIMPACT_BACK; + break; + case 3: + dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; + break; + default: + break; + } + } else + dieAnim = ANIM_STD_KO_FRONT; + + willLinger = false; + } else { + switch (pedPiece) { + case PEDPIECE_TORSO: + willLinger = false; + dieAnim = ANIM_STD_KO_FRONT; + break; + case PEDPIECE_MID: + willLinger = false; + dieAnim = ANIM_STD_KO_SHOT_STOMACH; + break; + case PEDPIECE_LEFTARM: + dieAnim = ANIM_STD_KO_SHOT_ARM_L; + RemoveBodyPart(PED_UPPERARML, direction); + willLinger = true; + break; + case PEDPIECE_RIGHTARM: + dieAnim = ANIM_STD_KO_SHOT_ARM_R; + RemoveBodyPart(PED_UPPERARMR, direction); + willLinger = true; + break; + case PEDPIECE_LEFTLEG: + dieAnim = ANIM_STD_KO_SHOT_LEG_L; + RemoveBodyPart(PED_UPPERLEGL, direction); + willLinger = true; + break; + case PEDPIECE_RIGHTLEG: + dieAnim = ANIM_STD_KO_SHOT_LEG_R; + RemoveBodyPart(PED_UPPERLEGR, direction); + willLinger = true; + break; + case PEDPIECE_HEAD: + dieAnim = ANIM_STD_KO_SHOT_FACE; + RemoveBodyPart(PED_HEAD, direction); + headShot = true; + willLinger = true; + break; + default: + break; + } + } + break; + case WEAPONTYPE_GRENADE: + case WEAPONTYPE_ROCKETLAUNCHER: + case WEAPONTYPE_EXPLOSION: + if (bExplosionProof) + return false; + + if (CGame::nastyGame && !IsPlayer() && !bInVehicle && + 1.0f + healthImpact > m_fArmour + m_fHealth) { + + random = CGeneral::GetRandomNumber(); + if (random & 1) + RemoveBodyPart(PED_UPPERARML, direction); + if (random & 2) + RemoveBodyPart(PED_UPPERLEGR, direction); + if (random & 4) + RemoveBodyPart(PED_HEAD, direction); + if (random & 8) + RemoveBodyPart(PED_UPPERARMR, direction); + if (random & 0x10) + RemoveBodyPart(PED_UPPERLEGL, direction); + if (bBodyPartJustCameOff) + willLinger = true; + } + // fall through + case WEAPONTYPE_MOLOTOV: + if (bExplosionProof) + return false; + + switch (direction) { + case 0: + dieAnim = ANIM_STD_HIGHIMPACT_FRONT; + break; + case 1: + dieAnim = ANIM_STD_HIGHIMPACT_LEFT; + break; + case 2: + dieAnim = ANIM_STD_HIGHIMPACT_BACK; + break; + case 3: + dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; + break; + default: + break; + } + break; + case WEAPONTYPE_FLAMETHROWER: + if (bFireProof) + return false; + + dieAnim = ANIM_STD_KO_FRONT; + break; + case WEAPONTYPE_RAMMEDBYCAR: + case WEAPONTYPE_RUNOVERBYCAR: + if (bCollisionProof) + return false; + + random = CGeneral::GetRandomNumber() & 3; + switch (random) { + case 0: + if ((pedPiece != PEDPIECE_LEFTARM || random <= 1) + && (pedPiece != PEDPIECE_MID || random != 1)) { + if (pedPiece == PEDPIECE_RIGHTARM && random > 1 + || pedPiece == PEDPIECE_MID && random == 2) + + dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; + else + dieAnim = ANIM_STD_HIGHIMPACT_FRONT; + } else + dieAnim = ANIM_STD_HIGHIMPACT_LEFT; + + break; + case 1: + if (m_nPedState == PED_DIVE_AWAY) + dieAnim = ANIM_STD_SPINFORWARD_LEFT; + else + dieAnim = ANIM_STD_HIGHIMPACT_LEFT; + break; + case 2: + if ((pedPiece != PEDPIECE_LEFTARM || random <= 1) + && (pedPiece != PEDPIECE_MID || random != 1)) { + if ((pedPiece != PEDPIECE_RIGHTARM || random <= 1) + && (pedPiece != PEDPIECE_MID || random != 2)) { + dieAnim = ANIM_STD_HIGHIMPACT_BACK; + } else { + dieAnim = ANIM_STD_SPINFORWARD_RIGHT; + } + } else + dieAnim = ANIM_STD_SPINFORWARD_LEFT; + break; + case 3: + if (m_nPedState == PED_DIVE_AWAY) + dieAnim = ANIM_STD_SPINFORWARD_RIGHT; + else + dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; + break; + default: + break; + } + if (damagedBy && pedPiece != PEDPIECE_TORSO) { + CVehicle *vehicle = (CVehicle*)damagedBy; + if (method == WEAPONTYPE_RAMMEDBYCAR) { + float vehSpeed = vehicle->m_vecMoveSpeed.Magnitude(); + dieDelta = 8.0f * vehSpeed + 4.0f; + } else { + float vehSpeed = vehicle->m_vecMoveSpeed.Magnitude(); + dieDelta = 12.0f * vehSpeed + 4.0f; + dieSpeed = 16.0f * vehSpeed + 1.0f; + } + } + break; + case WEAPONTYPE_DROWNING: + dieAnim = ANIM_STD_DROWN; + break; + case WEAPONTYPE_FALL: + if (bCollisionProof) + return false; + + switch (direction) { + case 0: + dieAnim = ANIM_STD_HIGHIMPACT_FRONT; + break; + case 1: + dieAnim = ANIM_STD_HIGHIMPACT_LEFT; + break; + case 2: + dieAnim = ANIM_STD_HIGHIMPACT_BACK; + break; + case 3: + dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; + break; + default: + break; + } + break; + default: + break; + } + } + + if (m_fArmour != 0.0f && method != WEAPONTYPE_DROWNING) { + if (player == this) + CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastArmourLoss = CTimer::GetTimeInMilliseconds(); + + if (healthImpact < m_fArmour) { + m_fArmour = m_fArmour - healthImpact; + healthImpact = 0.0f; + } else { + healthImpact = healthImpact - m_fArmour; + m_fArmour = 0.0f; + } + } + + if (healthImpact != 0.0f) { + if (player == this) + CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss = CTimer::GetTimeInMilliseconds(); + + m_lastWepDam = method; + m_lastDamEntity = damagedBy; + } + + if (method == WEAPONTYPE_FALL) { + if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROLLOUT_LHS)) { + if (m_fHealth >= 1.0 && m_fHealth - healthImpact < 5.0f) { + m_fHealth = Min(m_fHealth, 5.0f); + return false; + } + } + } + + if (m_fHealth - healthImpact >= 1.0f && !willLinger) { + m_fHealth -= healthImpact; + return false; + } + + if (bInVehicle) { + if (method != WEAPONTYPE_DROWNING) { + if (m_pMyVehicle) { + CVehicle* pVehicle = m_pMyVehicle; + bool bDone = false; + if (m_pMyVehicle->IsBike()) { + m_fHealth = 0.0f; + ((CBike*)m_pMyVehicle)->KnockOffRider(method, direction, this, false); + bDone = true; + } else { + if (m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_CAR) { + if (m_pMyVehicle->pDriver == this) { + if (m_pMyVehicle->GetStatus() == STATUS_SIMPLE) { + m_pMyVehicle->SetStatus(STATUS_PHYSICS); + CCarCtrl::SwitchVehicleToRealPhysics(m_pMyVehicle); + } + m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_NONE; + m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; + m_pMyVehicle->AutoPilot.m_nTempAction = TEMPACT_HANDBRAKESTRAIGHT; + m_pMyVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 2000; + } + } + if (m_pMyVehicle->CanPedExitCar(true)) { + SetObjective(OBJECTIVE_LEAVE_CAR_AND_DIE, m_pMyVehicle); + } else { + m_fHealth = 0.0f; + if (m_pMyVehicle && m_pMyVehicle->pDriver == this) { + SetRadioStation(); + m_pMyVehicle->SetStatus(STATUS_ABANDONED); + } + SetDie(dieAnim, dieDelta, dieSpeed); + + if (damagedBy == FindPlayerPed() && damagedBy != this) { + CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel += 10; + CWorld::Players[CWorld::PlayerInFocus].m_fMediaAttention += 5.f; + } + } + } + for (int i = 0; i < ARRAY_SIZE(pVehicle->pPassengers); i++) { + CPed* passenger = pVehicle->pPassengers[i]; + if (passenger && passenger != this && damagedBy) + passenger->ReactToAttack(damagedBy); + } + + CPed *driverOfVeh = pVehicle->pDriver; + if (driverOfVeh && driverOfVeh != this && damagedBy) + driverOfVeh->ReactToAttack(damagedBy); + + if (damagedBy == FindPlayerPed() || damagedBy && damagedBy == FindPlayerVehicle()) { + CDarkel::RegisterKillByPlayer(this, method, headShot); + m_threatEntity = FindPlayerPed(); + } else { + CDarkel::RegisterKillNotByPlayer(this, method); + } + if (bDone) + return true; + } + m_fHealth = 1.0f; + return false; + } + m_fHealth = 0.0f; + if (player == this) + m_pMyVehicle->SetStatus(STATUS_PLAYER_DISABLED); + + SetDie(ANIM_STD_NUM, 4.0f, 0.0f); + return true; + } else { + m_fHealth = 0.0f; + SetDie(dieAnim, dieDelta, dieSpeed); + + if (damagedBy == player || damagedBy && damagedBy == FindPlayerVehicle()) { + CDarkel::RegisterKillByPlayer(this, method, headShot); + CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel += 10; + CWorld::Players[CWorld::PlayerInFocus].m_fMediaAttention += 5.f; + m_threatEntity = player; + } else { + CDarkel::RegisterKillNotByPlayer(this, method); + } + if (method == WEAPONTYPE_DROWNING) { + bIsInTheAir = false; + if (FindPlayerPed() == this) + CStats::TimesDrowned++; + } + + return true; + } +} + +static RwObject* +SetPedAtomicVisibilityCB(RwObject* object, void* data) +{ + if (data == nil) + RpAtomicSetFlags((RpAtomic*)object, 0); + return object; +} + +static RwFrame* +RecurseFrameChildrenVisibilityCB(RwFrame* frame, void* data) +{ + RwFrameForAllObjects(frame, SetPedAtomicVisibilityCB, data); + RwFrameForAllChildren(frame, RecurseFrameChildrenVisibilityCB, nil); + return frame; +} + +static RwObject* +CloneAtomicToFrameCB(RwObject *frame, void *data) +{ + RpAtomic *newAtomic = RpAtomicClone((RpAtomic*)frame); + RpAtomicSetFrame(newAtomic, (RwFrame*)data); + RpClumpAddAtomic(flyingClumpTemp, newAtomic); + CVisibilityPlugins::SetAtomicRenderCallback(newAtomic, nil); + return frame; +} + +static RwFrame* +RecurseFrameChildrenToCloneCB(RwFrame *frame, void *data) +{ + RwFrame *newFrame = RwFrameCreate(); + RwFrameAddChild((RwFrame*)data, newFrame); + RwFrameTransform(newFrame, RwFrameGetMatrix(frame), rwCOMBINEREPLACE); + RwFrameForAllObjects(frame, CloneAtomicToFrameCB, newFrame); + RwFrameForAllChildren(frame, RecurseFrameChildrenToCloneCB, newFrame); + return newFrame; +} + +void +CPed::RemoveBodyPart(PedNode nodeId, int8 direction) +{ + RwFrame *frame; + CVector pos; + + frame = m_pFrames[nodeId]->frame; + if (frame) { + if (CGame::nastyGame) { + if (CEntity::GetIsOnScreen()) { + m_pedIK.GetComponentPosition(pos, nodeId); + CParticle::AddParticle(PARTICLE_TEST, pos, + CVector(0.0f, 0.0f, 0.0f), + nil, 0.1f, 0, 0, 0, 0); + + for (int i = 0; i < 16; i++) { + CParticle::AddParticle(PARTICLE_BLOOD_SMALL, + pos, + CVector(0.0f, 0.0f, 0.03f), + nil, 0.0f, 0, 0, 0, 0); + } + } + bBodyPartJustCameOff = true; + m_bodyPartBleeding = nodeId; + } + } else { + printf("Trying to remove ped component"); + } +} + +CObject* +CPed::SpawnFlyingComponent(int pedNode, int8 direction) +{ + // VC doesn't have detachable limbs :shrug: + return nil; +} + +// III leftover and unused +void +CPed::ApplyHeadShot(eWeaponType weaponType, CVector pos, bool evenOnPlayer) +{ + CVector pos2 = CVector( + pos.x, + pos.y, + pos.z + 0.1f + ); + + if (!IsPlayer() || evenOnPlayer) { + ++CStats::HeadsPopped; + + // BUG: This condition will always return true. Even fixing it won't work, because these states are unused. + // if (m_nPedState != PED_PASSENGER || m_nPedState != PED_TAXI_PASSENGER) { + SetDie(); + // } + + bBodyPartJustCameOff = true; + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 150; + + RemoveBodyPart(PED_HEAD, 0); + CParticle::AddParticle(PARTICLE_TEST, pos2, + CVector(0.0f, 0.0f, 0.0f), nil, 0.2f, 0, 0, 0, 0); + + if (CEntity::GetIsOnScreen()) { + for(int i=0; i < 32; i++) { + CParticle::AddParticle(PARTICLE_BLOOD_SMALL, + pos2, CVector(0.0f, 0.0f, 0.03f), + nil, 0.0f, 0, 0, 0, 0); + } + + for (int i = 0; i < 16; i++) { + CParticle::AddParticle(PARTICLE_DEBRIS2, + pos2, + CVector(0.0f, 0.0f, 0.01f), + nil, 0.0f, 0, 0, 0, 0); + } + } + } +} + +bool +CPed::IsPedHeadAbovePos(float zOffset) +{ + return zOffset + GetPosition().z < GetNodePosition(PED_HEAD).z; +} + +bool +CPed::PlacePedOnDryLand(void) +{ + float waterLevel = 0.0f; + CEntity *foundEnt = nil; + CColPoint foundCol; + float foundColZ; + + CWaterLevel::GetWaterLevelNoWaves(GetPosition().x, GetPosition().y, GetPosition().z, &waterLevel); + + CVector potentialGround = GetPosition(); + potentialGround.z = waterLevel; + + if (!CWorld::TestSphereAgainstWorld(potentialGround, 5.0f, nil, true, false, false, false, false, false)) + return false; + + CVector potentialGroundDist = gaTempSphereColPoints[0].point - GetPosition(); + potentialGroundDist.z = 0.0f; + potentialGroundDist.Normalise(); + + CVector posToCheck = 0.5f * potentialGroundDist + gaTempSphereColPoints[0].point; + posToCheck.z = 3.0f + waterLevel; + + if (CWorld::ProcessVerticalLine(posToCheck, waterLevel - 1.0f, foundCol, foundEnt, true, true, false, true, false, false, nil)) { + foundColZ = foundCol.point.z; + if (foundColZ >= waterLevel) { + posToCheck.z = 0.8f + foundColZ; + SetPosition(posToCheck); + bIsStanding = true; + bWasStanding = true; + return true; + } + } + + posToCheck = 5.0f * potentialGroundDist + GetPosition(); + posToCheck.z = 3.0f + waterLevel; + + if (!CWorld::ProcessVerticalLine(posToCheck, waterLevel - 1.0f, foundCol, foundEnt, true, true, false, true, false, false, nil)) + return false; + + foundColZ = foundCol.point.z; + if (foundColZ < waterLevel) + return false; + + posToCheck.z = 0.8f + foundColZ; + SetPosition(posToCheck); + bIsStanding = true; + bWasStanding = true; + return true; +} + +void +CPed::CollideWithPed(CPed *collideWith) +{ + CAnimBlendAssociation *animAssoc; + AnimationId animToPlay; + + bool weAreMissionChar = CharCreatedBy == MISSION_CHAR; + bool heIsMissionChar = collideWith->CharCreatedBy == MISSION_CHAR; + CVector posDiff = collideWith->GetPosition() - GetPosition(); + int waitTime = 0; + + if (weAreMissionChar || !collideWith->IsPlayer() || collideWith->m_nPedState != PED_MAKE_CALL) { + if (m_nWaitState == WAITSTATE_SUN_BATHE_IDLE) { + SetGetUp(); + return; + } + if (collideWith->m_nWaitState == WAITSTATE_SUN_BATHE_IDLE) { + collideWith->SetGetUp(); + return; + } + + bool weDontLookToHim = DotProduct(posDiff, GetForward()) > 0.0f; + bool heLooksToUs = DotProduct(posDiff, collideWith->GetForward()) < 0.0f; + + if (m_nMoveState != PEDMOVE_NONE && m_nMoveState != PEDMOVE_STILL) { + + if ((!IsPlayer() || ((CPlayerPed*)this)->m_fMoveSpeed <= 1.8f) + && (IsPlayer() || heIsMissionChar && weAreMissionChar || m_nMoveState != PEDMOVE_RUN && m_nMoveState != PEDMOVE_SPRINT + || m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION && m_pedInObjective == collideWith + || collideWith->m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION && collideWith->m_pedInObjective == this + )) { + + if (m_objective != OBJECTIVE_FOLLOW_CHAR_IN_FORMATION && m_objective != OBJECTIVE_GOTO_CHAR_ON_FOOT) { + + if (CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) { + + if (heIsMissionChar || !weAreMissionChar && collideWith->m_nMoveState != PEDMOVE_STILL) { + + if (weAreMissionChar && (m_nPedState == PED_SEEK_POS || m_nPedState == PED_SEEK_ENTITY)) { + + if (collideWith->m_nMoveState != PEDMOVE_STILL + && (!collideWith->IsPlayer() || collideWith->IsPlayer() && CPad::GetPad(0)->ArePlayerControlsDisabled())) { + float seekPosDist = (GetPosition() - m_vecSeekPos).MagnitudeSqr2D(); + float heAndSeekPosDist = (collideWith->GetPosition() - m_vecSeekPos).MagnitudeSqr2D(); + + if (seekPosDist <= heAndSeekPosDist) { + waitTime = 1000; + collideWith->SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &waitTime); + collideWith->m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + waitTime; + } else { + waitTime = 500; + SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &waitTime); + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + waitTime; + } + } else if (collideWith->m_nMoveState == PEDMOVE_STILL) { + SetDirectionToWalkAroundObject(collideWith); + } + } else { + if (FindPlayerPed() != m_pedInObjective + || m_objective != OBJECTIVE_KILL_CHAR_ANY_MEANS && m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT + || collideWith == m_pedInObjective) { + + if (weAreMissionChar || m_pedStats->m_fear <= 100 - collideWith->m_pedStats->m_temper + || (collideWith->IsPlayer() || collideWith->m_nMoveState == PEDMOVE_NONE || collideWith->m_nMoveState == PEDMOVE_STILL) && + (!collideWith->IsPlayer() || ((CPlayerPed*)collideWith)->m_fMoveSpeed <= 1.0f)) { + SetDirectionToWalkAroundObject(collideWith); + if (!weAreMissionChar) + Say(SOUND_PED_CHAT); + } else { + SetEvasiveStep(collideWith, 2); + } + } else if (collideWith->m_nMoveState != PEDMOVE_STILL && GetWeapon()->IsTypeMelee() + && collideWith->m_pedInObjective == m_pedInObjective) { + + int colliderIndexAtPlayersKillList = -1; + int ourIndexAtPlayersKillList = -1; + for (int i = 0; i < ARRAY_SIZE(((CPlayerPed*)m_pedInObjective)->m_pMeleeList); i++) { + CPed *pedInKillList = ((CPlayerPed*)m_pedInObjective)->m_pMeleeList[i]; + if (pedInKillList == this) { + ourIndexAtPlayersKillList = i; + } else if (pedInKillList == collideWith) { + colliderIndexAtPlayersKillList = i; + } + } + bool weAreCloserToTargetThenCollider = false; + if ((GetPosition() - m_vecSeekPos).MagnitudeSqr2D() < (collideWith->GetPosition() - m_vecSeekPos).MagnitudeSqr2D()) + weAreCloserToTargetThenCollider = true; + + if (ourIndexAtPlayersKillList > 0 && !weAreCloserToTargetThenCollider) { + if (colliderIndexAtPlayersKillList > 0) { + int time = 300; + SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &time); + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + time; + + } else if (collideWith->m_pedInObjective == FindPlayerPed()) { + ((CPlayerPed*)m_pedInObjective)->RemovePedFromMeleeList(this); + int time = 500; + SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &time); + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + time; + } + } else if (!weAreCloserToTargetThenCollider) { + int time = 300; + SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &time); + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + time; + } + } else { + SetDirectionToWalkAroundObject(collideWith); + } + } + } else { + if (m_pedStats->m_temper <= m_pedStats->m_fear + || GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED + || weAreMissionChar + || collideWith->m_nPedType == PEDTYPE_CIVFEMALE + || collideWith->m_nPedType == m_nPedType + || collideWith->GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) { + SetDirectionToWalkAroundObject(collideWith); + Say(SOUND_PED_CHAT); + } else { + TurnBody(); + SetAttack(collideWith); + m_fRotationCur = 0.3f + m_fRotationCur; + m_fRotationDest = m_fRotationCur; + } + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(250, 450); + } + } + } else { + if (m_pedInObjective && (collideWith == m_pedInObjective || collideWith->m_pedInObjective == m_pedInObjective) && CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) { + if (heLooksToUs) { + SetEvasiveStep(collideWith, 1); + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 3000; + } + } else if (weDontLookToHim && IsPedInControl()) { + + if (m_pedStats != collideWith->m_pedStats) { + + if (collideWith->m_pedStats->m_fear <= 100 - m_pedStats->m_temper || collideWith->IsPlayer() + || CTimer::GetTimeInMilliseconds() < m_nPedStateTimer) { + + if (collideWith->IsPlayer()) { + // He's on our right side + if (DotProduct(posDiff,GetRight()) <= 0.0f) + m_fRotationCur -= m_headingRate; + else + m_fRotationCur += m_headingRate; + } else { + // He's on our right side + if (DotProduct(posDiff, collideWith->GetRight()) <= 0.0f) + collideWith->m_fRotationCur -= collideWith->m_headingRate; + else + collideWith->m_fRotationCur += collideWith->m_headingRate; + } + } else { + SetLookFlag(collideWith, false); + TurnBody(); + animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_PARTIAL_PUNCH, 8.0f); + animAssoc->flags |= ASSOC_FADEOUTWHENDONE; + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 2000; + if (!heIsMissionChar) { + CVector2D posDiff2D(posDiff); + int direction = collideWith->GetLocalDirection(posDiff2D); + collideWith->StartFightDefend(direction, HITLEVEL_HIGH, 5); + } + } + } + } + } + } else if (collideWith->m_pedStats->m_defendWeakness <= 1.5f || heIsMissionChar || + m_pedStats->m_defendWeakness <= collideWith->m_pedStats->m_defendWeakness) { + + // He looks us and we're not at his right side + if (heLooksToUs && DotProduct(posDiff,collideWith->GetRight()) > 0.0f) { + CVector moveForce = GetRight(); + moveForce.z += 0.1f; + ApplyMoveForce(moveForce); + if (collideWith->m_nMoveState != PEDMOVE_RUN && collideWith->m_nMoveState != PEDMOVE_SPRINT) + animToPlay = ANIM_STD_HIT_LEFT; + else + animToPlay = ANIM_STD_HITBYGUN_LEFT; + } else if (heLooksToUs) { + CVector moveForce = GetRight() * -1.0f; + moveForce.z += 0.1f; + ApplyMoveForce(moveForce); + if (collideWith->m_nMoveState != PEDMOVE_RUN && collideWith->m_nMoveState != PEDMOVE_SPRINT) + animToPlay = ANIM_STD_HIT_RIGHT; + else + animToPlay = ANIM_STD_HITBYGUN_RIGHT; + } else { + if (collideWith->m_nMoveState != PEDMOVE_RUN && collideWith->m_nMoveState != PEDMOVE_SPRINT) + animToPlay = ANIM_STD_HIT_BACK; + else + animToPlay = ANIM_STD_HITBYGUN_BACK; + } + + if (collideWith->IsPedInControl() && CTimer::GetTimeInMilliseconds() > collideWith->m_nPedStateTimer) { + animAssoc = CAnimManager::BlendAnimation(collideWith->GetClump(), ASSOCGRP_STD, animToPlay, 8.0f); + animAssoc->flags |= ASSOC_FADEOUTWHENDONE; + collideWith->m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 1000; + if (m_nPedState == PED_ATTACK) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_49, 0.0f); + } + } else { + // We're at his right side + if (DotProduct(posDiff, collideWith->GetRight()) <= 0.0f) { + CVector moveForce = GetRight() * -1.0f; + moveForce.z += 0.1f; + ApplyMoveForce(moveForce); + if (heLooksToUs) + animToPlay = ANIM_STD_HIGHIMPACT_RIGHT; + else + animToPlay = ANIM_STD_SPINFORWARD_RIGHT; + } else { + CVector moveForce = GetRight(); + moveForce.z += 0.1f; + ApplyMoveForce(moveForce); + if (heLooksToUs) + animToPlay = ANIM_STD_HIGHIMPACT_LEFT; + else + animToPlay = ANIM_STD_SPINFORWARD_LEFT; + } + + if (m_nPedState == PED_ATTACK && collideWith->IsPedInControl()) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_49, 0.0f); + + collideWith->SetFall(3000, animToPlay, 0); + } + } else { + if (!IsPedInControl()) + return; + + if (collideWith->m_nMoveState == PEDMOVE_NONE || collideWith->m_nMoveState == PEDMOVE_STILL) + return; + + if (m_nPedType != collideWith->m_nPedType || m_nPedType == PEDTYPE_CIVMALE || m_nPedType == PEDTYPE_CIVFEMALE) { + + if (!weAreMissionChar && heLooksToUs && m_pedStats->m_fear > 100 - collideWith->m_pedStats->m_temper) { + + if (CGeneral::GetRandomNumber() & 1 && CTimer::GetTimeInMilliseconds() < m_nPedStateTimer){ + SetEvasiveStep(collideWith, 2); + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 3000; + } else if (collideWith->m_nMoveState > PEDMOVE_WALK) { + waitTime = 2000; + SetWaitState(WAITSTATE_PLAYANIM_DUCK, &waitTime); + } + } + } else if (heLooksToUs + && collideWith->m_nPedState != PED_STEP_AWAY + && m_nPedState != PED_STEP_AWAY + && CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) { + + SetEvasiveStep(collideWith, 1); + m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 3000; + } + } + + if (IsPlayer()) { + SetLookFlag(collideWith, true); + SetLookTimer(800); + } + } else { + bool isRunning = m_nMoveState == PEDMOVE_RUN || m_nMoveState == PEDMOVE_SPRINT; + SetFindPathAndFlee(collideWith, 5000, !isRunning); + } +} + +void +CPed::KillPedWithCar(CVehicle *car, float impulse) +{ + CVehicleModelInfo *vehModel; + CColModel *vehColModel; + uint8 damageDir; + PedNode nodeToDamage; + eWeaponType killMethod; + + if (m_nPedState == PED_FALL || m_nPedState == PED_DIE) { + if (!m_pCollidingEntity || car->GetStatus() == STATUS_PLAYER) + m_pCollidingEntity = car; + return; + } + + if (m_nPedState == PED_DEAD) + return; + + if (m_pCurSurface) { + if (m_pCurSurface->IsVehicle() && (((CVehicle*)m_pCurSurface)->IsBoat()|| IsPlayer())) + return; + } + + CVector distVec = GetPosition() - car->GetPosition(); + + if ((impulse > 12.0f || car->GetModelIndex() == MI_TRAIN) && !IsPlayer()) { + nodeToDamage = PED_TORSO; + killMethod = WEAPONTYPE_RAMMEDBYCAR; + uint8 randVal = CGeneral::GetRandomNumber() & 3; + + if (car == FindPlayerVehicle()) { + float carSpeed = car->m_vecMoveSpeed.Magnitude(); + uint8 shakeFreq; + if (100.0f * carSpeed * 2000.0f / car->m_fMass + 80.0f <= 250.0f) { + shakeFreq = 100.0f * carSpeed * 2000.0f / car->m_fMass + 80.0f; + } else { + shakeFreq = 250.0f; + } + CPad::GetPad(0)->StartShake(40000 / shakeFreq, shakeFreq); + } + bIsStanding = false; + damageDir = GetLocalDirection(-m_vecMoveSpeed); + vehModel = (CVehicleModelInfo *)CModelInfo::GetModelInfo(car->GetModelIndex()); + vehColModel = vehModel->GetColModel(); + float carRightAndDistDotProd = DotProduct(distVec, car->GetRight()); + + if (car->GetModelIndex() == MI_TRAIN) { + killMethod = WEAPONTYPE_RUNOVERBYCAR; + nodeToDamage = PED_HEAD; + m_vecMoveSpeed = 0.9f * car->m_vecMoveSpeed; + m_vecMoveSpeed.z = 0.0f; + if (damageDir == 1 || damageDir == 3) + damageDir = 2; + if (CGame::nastyGame) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_SPLATTER, 0.0f); + + // Car doesn't look to us + } else if (DotProduct(car->m_vecMoveSpeed, car->GetForward()) >= 0.0f){ + + if (0.99f * vehColModel->boundingBox.max.x < Abs(carRightAndDistDotProd)) { + + // We're at the right of the car + if (carRightAndDistDotProd <= 0.0f) + nodeToDamage = PED_UPPERARML; + else + nodeToDamage = PED_UPPERARMR; + + if (Abs(DotProduct(distVec, car->GetForward())) < 0.85f * vehColModel->boundingBox.max.y) { + killMethod = WEAPONTYPE_RUNOVERBYCAR; + m_vecMoveSpeed = 0.9f * car->m_vecMoveSpeed; + m_vecMoveSpeed.z = 0.0f; + if (damageDir == 1 || damageDir == 3) + damageDir = 2; + if (CGame::nastyGame) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_SPLATTER, 0.0f); + + } + } else { + float carFrontAndDistDotProd = DotProduct(distVec, car->GetForward()); + + // carFrontAndDistDotProd <= 0.0 car looks to us + if ((carFrontAndDistDotProd <= 0.1 || randVal <= 1) && randVal != 0) { + killMethod = WEAPONTYPE_RUNOVERBYCAR; + nodeToDamage = PED_HEAD; + m_vecMoveSpeed = 0.9f * car->m_vecMoveSpeed; + m_vecMoveSpeed.z = 0.0f; + if (damageDir == 1 || damageDir == 3) + damageDir = 2; + + if (CGame::nastyGame) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_SPLATTER, 0.0f); + + } else { + nodeToDamage = PED_MID; + float vehColMaxY = vehColModel->boundingBox.max.y; + float vehColMinY = vehColModel->boundingBox.min.y; + float vehColMaxZ = vehColModel->boundingBox.max.z; + float carFrontZ = car->GetForward().z; + float carHighestZ, carLength; + + if (carFrontZ < -0.2f) { + // Highest point of car's back + carHighestZ = (car->GetMatrix() * CVector(0.0f, vehColMinY, vehColMaxZ)).z; + carLength = vehColMaxY - vehColMinY; + + } else if (carFrontZ > 0.1f) { + // Highest point of car's front + carHighestZ = (car->GetMatrix() * CVector(0.0f, vehColMaxY, vehColMaxZ)).z; + float highestZDist = carHighestZ - GetPosition().z; + if (highestZDist > 0.0f) { + GetMatrix().GetPosition().z += 0.5f * highestZDist; + carHighestZ += highestZDist * 0.25f; + } + carLength = vehColMaxY; + + } else { + // Highest point of car's front + carHighestZ = (car->GetMatrix() * CVector(0.0f, vehColMaxY, vehColMaxZ)).z; + carLength = vehColMaxY; + } + + float pedJumpSpeedToReachHighestZ = (carHighestZ - GetPosition().z) / (carLength / car->m_vecMoveSpeed.Magnitude()); + + // TODO: What are we doing down here? + float unknown = ((CGeneral::GetRandomNumber() % 256) * 0.002 + 1.5) * pedJumpSpeedToReachHighestZ; + + // After this point distVec isn't really distVec. + distVec = car->m_vecMoveSpeed; + distVec.Normalise(); + distVec *= 0.2 * unknown; + + if (damageDir != 1 && damageDir != 3) + distVec.z += unknown; + else + distVec.z += 1.5f * unknown; + + m_vecMoveSpeed = distVec; + damageDir += 2; + if (damageDir > 3) + damageDir = damageDir - 4; + + if (car->IsCar()) { + CObject *bonnet = ((CAutomobile*)car)->RemoveBonnetInPedCollision(); + + if (bonnet) { + if (CGeneral::GetRandomNumber() & 1) { + bonnet->m_vecMoveSpeed += Multiply3x3(car->GetMatrix(), CVector(0.1f, 0.0f, 0.5f)); + } else { + bonnet->m_vecMoveSpeed += Multiply3x3(car->GetMatrix(), CVector(-0.1f, 0.0f, 0.5f)); + } + CVector forceDir = car->GetUp() * 10.0f; + bonnet->ApplyTurnForce(forceDir, car->GetForward()); + } + } + } + } + } + + if (car->pDriver) { + CEventList::RegisterEvent((m_nPedType == PEDTYPE_COP ? EVENT_HIT_AND_RUN_COP : EVENT_HIT_AND_RUN), EVENT_ENTITY_PED, this, car->pDriver, 1000); + } + + ePedPieceTypes pieceToDamage; + switch (nodeToDamage) { + case PED_HEAD: + pieceToDamage = PEDPIECE_HEAD; + break; + case PED_UPPERARML: + pieceToDamage = PEDPIECE_LEFTARM; + break; + case PED_UPPERARMR: + pieceToDamage = PEDPIECE_RIGHTARM; + break; + default: + pieceToDamage = PEDPIECE_MID; + break; + } + InflictDamage(car, killMethod, 1000.0f, pieceToDamage, damageDir); + + if (DyingOrDead() + && bIsPedDieAnimPlaying && !m_pCollidingEntity) { + m_pCollidingEntity = car; + } + if (nodeToDamage == PED_MID) + bKnockedUpIntoAir = true; + else + bKnockedUpIntoAir = false; + + distVec.Normalise(); + + distVec *= Min(car->m_fMass / 1400.0f, 1.0f); + car->ApplyMoveForce(distVec * -100.0f); + Say(SOUND_PED_DEFEND); + + } else if (m_vecDamageNormal.z < -0.8f && impulse > 3.0f + || impulse > 6.0f && (!IsPlayer() || impulse > 10.0f)) { + + bIsStanding = false; + uint8 fallDirection = GetLocalDirection(-car->m_vecMoveSpeed); + float damage; + if (IsPlayer() && car->GetModelIndex() == MI_TRAIN) + damage = 150.0f; + else + damage = 30.0f; + + InflictDamage(car, WEAPONTYPE_RAMMEDBYCAR, damage, PEDPIECE_TORSO, fallDirection); + SetFall(1000, (AnimationId)(fallDirection + ANIM_STD_HIGHIMPACT_FRONT), true); + + if (OnGround() && !m_pCollidingEntity && + (!IsPlayer() || bHasHitWall || car->GetModelIndex() == MI_TRAIN || m_vecDamageNormal.z < -0.8f)) { + + m_pCollidingEntity = car; + } + + bKnockedUpIntoAir = false; + if (car->GetModelIndex() != MI_TRAIN && !bHasHitWall) { + m_vecMoveSpeed = car->m_vecMoveSpeed * 0.75f; + } + m_vecMoveSpeed.z = 0.0f; + distVec.Normalise(); + distVec *= Min(car->m_fMass / 1400.0f, 1.0f); + car->ApplyMoveForce(distVec * -60.0f); + Say(SOUND_PED_DEFEND); + } + + if (IsGangMember()) { + CPed *driver = car->pDriver; + if (driver && driver->IsPlayer() +#ifdef FIX_BUGS + && (CharCreatedBy != MISSION_CHAR || bRespondsToThreats) && (!m_leader || m_leader != driver) +#endif + ) { + RegisterThreatWithGangPeds(driver); + } + } +} + +void +CPed::DriveVehicle(void) +{ + if (bOffscreen) + return; + + CVehicle *veh = m_pMyVehicle; + if (veh->IsBike()) { + CBike *bike = (CBike*)veh; + float blendDelta = 1.0f; + float targetUDLean = 0.0f; + CAnimBlendAssociation *leftAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_LEFT); + CAnimBlendAssociation *rightAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_RIGHT); + CAnimBlendAssociation *stillAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_READY); + CAnimBlendAssociation *fwdAssoc, *backAssoc; + if (IsPlayer()) { + fwdAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_LEANF); + backAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_LEANB); + } + CAnimBlendAssociation *walkbackAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_WALKBACK); + CAnimBlendAssociation *drivebyAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_DRIVEBY_LHS); + if (!drivebyAssoc) + drivebyAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_DRIVEBY_RHS); + if (!drivebyAssoc) + drivebyAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_DRIVEBY_FORWARD); + + float velocityFwdDotProd = DotProduct(bike->m_vecMoveSpeed, bike->GetForward()); + if (m_vecTurnSpeed.MagnitudeSqr() > 0.09f) { + bike->KnockOffRider(WEAPONTYPE_FALL, 2, this, false); + if (bike->pPassengers[0]) + bike->KnockOffRider(WEAPONTYPE_FALL, 2, bike->pPassengers[0], false); + return; + } + if (!drivebyAssoc && Abs(velocityFwdDotProd) < 0.02f) { + if (!stillAssoc || stillAssoc->blendAmount < 1.0 && stillAssoc->blendDelta <= 0.0) { + stillAssoc = CAnimManager::BlendAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_READY, 2.0f); + } + } else { + if (velocityFwdDotProd >= 0.0f) { + if (stillAssoc && stillAssoc->blendDelta >= 0.0f) + stillAssoc->blendDelta = -4.0f; + if (walkbackAssoc && walkbackAssoc->blendDelta >= 0.0f) + walkbackAssoc->blendDelta = -4.0f; + } else { + float maxReverseSpeed = bike->pHandling->Transmission.fMaxReverseVelocity; + if (3.5f * maxReverseSpeed > velocityFwdDotProd && (bike->m_nWheelsOnGround || bike->GetUp().z < -0.5f)) { + bike->KnockOffRider(WEAPONTYPE_FALL, 2, this, false); + if (bike->pPassengers[0]) + bike->KnockOffRider(WEAPONTYPE_FALL, 2, bike->pPassengers[0], false); + return; + } + if (bike->m_fGasPedal >= 0.0 || velocityFwdDotProd <= maxReverseSpeed * 1.5) { + if (IsPlayer() && velocityFwdDotProd < maxReverseSpeed * 1.5) + targetUDLean = -1.0f; + + if (stillAssoc && stillAssoc->blendDelta >= 0.0f) + stillAssoc->blendDelta = -4.0f; + + if (walkbackAssoc && walkbackAssoc->blendDelta >= 0.0f) { + walkbackAssoc->blendDelta = -4.0f; + } + } else if (!walkbackAssoc || walkbackAssoc->blendAmount < 1.0f && walkbackAssoc->blendDelta <= 0.0f) { + walkbackAssoc = CAnimManager::BlendAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_WALKBACK, 4.0f); + } + } + } + if (stillAssoc) + blendDelta -= Min(1.0f, CTimer::GetTimeStepNonClipped() * 0.02f * stillAssoc->blendDelta + stillAssoc->blendAmount); + + if (drivebyAssoc) + blendDelta -= Min(blendDelta, CTimer::GetTimeStepNonClipped() * 0.02f * drivebyAssoc->blendDelta + drivebyAssoc->blendAmount); + + if (walkbackAssoc) + blendDelta -= Min(blendDelta, CTimer::GetTimeStepNonClipped() * 0.02f * walkbackAssoc->blendDelta + walkbackAssoc->blendAmount); + + float targetLRLean, timeBlend, neededAngForWheelie, stoppieAng; + + // Smooth the lean amount + if (targetUDLean == -1.0f) { + targetLRLean = 0.0f; + timeBlend = Pow(0.86f, CTimer::GetTimeStep()); + } else { + targetLRLean = Clamp(bike->m_fLeanLRAngle / bike->pBikeHandling->fFullAnimLean, -1.0f, 1.0f); + timeBlend = Pow(0.86f, CTimer::GetTimeStep()); + } + + bike->m_fPedLeanAmountLR = bike->m_fPedLeanAmountLR * timeBlend + (1.0 - timeBlend) * targetLRLean; + + if (!IsPlayer()) { + targetUDLean = 0.0f; + + } else if (targetUDLean > -1.0f) { + targetUDLean = bike->m_fLeanInput; + bike->bWheelieCam = false; + neededAngForWheelie = 1.0f; + if (bike->m_aWheelTimer[0] != 0.0f || bike->m_aWheelTimer[1] != 0.0f || bike->GetForward().z <= 0.0f || + (0.0f == bike->m_aWheelTimer[2] && 0.0f == bike->m_aWheelTimer[3])) { + + if (0.0f == bike->m_aWheelTimer[2] && 0.0f == bike->m_aWheelTimer[3] && + (bike->GetForward().z < 0.0f && (bike->m_aWheelTimer[0] != 0.0f || bike->m_aWheelTimer[1] != 0.0f))) { + + stoppieAng = bike->pBikeHandling->fStoppieAng; + if (stoppieAng - bike->GetForward().z > 0.6f * stoppieAng) + bike->bWheelieCam = true; + } + } else { + float wheelieAng = bike->pBikeHandling->fWheelieAng; + neededAngForWheelie = wheelieAng - bike->GetForward().z; + if (neededAngForWheelie < wheelieAng / 2.f) + bike->bWheelieCam = true; + } + if (neededAngForWheelie >= 0.15f) { + if (bike->m_fBrakePedal <= 0.5f || velocityFwdDotProd <= 0.01f) { + if (bike->m_fGasPedal > 0.5f && targetUDLean <= 0.0f && 0.3f * bike->pHandling->Transmission.fMaxCruiseVelocity > velocityFwdDotProd) { + targetUDLean = Min(0.1f, targetUDLean); + } + } else { + targetUDLean = Max(0.1f, targetUDLean); + } + } else { + targetUDLean = Max(0.25f, targetUDLean); + } + float targetLRLeanABS = Abs(targetLRLean); + if (targetLRLeanABS > 0.3f) { + // Yes, UD + targetUDLean *= Max(0.0f, 1.0f - (targetLRLeanABS - 0.3f) * 50.f / 13.f); + } + } + if (IsPlayer()) { + float timeBlend = Pow(0.89f, CTimer::GetTimeStep()); + bike->m_fPedLeanAmountUD = (timeBlend * bike->m_fPedLeanAmountUD) + ((1.0f - timeBlend) * targetUDLean); + } else { + bike->m_fPedLeanAmountUD = 0.0f; + } + + float fwdBackLeanAmount, leftRightLeanAmount; + if (Abs(bike->m_fPedLeanAmountLR) <= 0.56f && IsPlayer()) { + + if (Abs(bike->m_fPedLeanAmountUD) <= 0.56f) { + CVector2D smoothedLean(bike->m_fPedLeanAmountLR, bike->m_fPedLeanAmountUD); + float smoothLeanMag = smoothedLean.Magnitude(); + if (smoothLeanMag <= 0.01f) { + fwdBackLeanAmount = Abs(smoothedLean.y); + leftRightLeanAmount = Abs(smoothedLean.x); + } else { + fwdBackLeanAmount = Abs(smoothedLean.y / smoothLeanMag); + leftRightLeanAmount = Abs(smoothedLean.x / smoothLeanMag); + } + } else { + fwdBackLeanAmount = 1.0f; + leftRightLeanAmount = 0.0f; + } + } else { + fwdBackLeanAmount = 0.0f; + leftRightLeanAmount = 1.0f; + } + float fwdBackBlend = fwdBackLeanAmount * blendDelta; + float leftRightBlend = leftRightLeanAmount * blendDelta; + if (IsPlayer()) { + if (!fwdAssoc) + fwdAssoc = CAnimManager::AddAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_LEANF); + if (!backAssoc) + backAssoc = CAnimManager::AddAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_LEANB); + + if (bike->m_fPedLeanAmountUD < 0.0f) { + backAssoc->blendAmount = fwdBackBlend; + backAssoc->SetCurrentTime(-(bike->m_fPedLeanAmountUD * backAssoc->hierarchy->totalLength)); + backAssoc->flags &= ~ASSOC_RUNNING; + fwdAssoc->blendAmount = 0.0f; + } else { + fwdAssoc->blendAmount = fwdBackBlend; + fwdAssoc->SetCurrentTime(bike->m_fPedLeanAmountUD* fwdAssoc->hierarchy->totalLength); + fwdAssoc->flags &= ~ASSOC_RUNNING; + backAssoc->blendAmount = 0.0f; + } + } + if (!leftAssoc) + leftAssoc = CAnimManager::AddAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_LEFT); + if (!rightAssoc) + rightAssoc = CAnimManager::AddAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_RIGHT); + + if (bike->m_fPedLeanAmountLR < 0.0f) { + leftAssoc->blendAmount = leftRightBlend; + leftAssoc->SetCurrentTime(-(bike->m_fPedLeanAmountLR * leftAssoc->hierarchy->totalLength)); + leftAssoc->flags &= ~ASSOC_RUNNING; + rightAssoc->blendAmount = 0.0f; + } else { + rightAssoc->blendAmount = leftRightBlend; + rightAssoc->SetCurrentTime(bike->m_fPedLeanAmountLR* rightAssoc->hierarchy->totalLength); + rightAssoc->flags &= ~ASSOC_RUNNING; + leftAssoc->blendAmount = 0.0f; + } + if (velocityFwdDotProd > 0.3f) { + RwV3d Xaxis = { 1.0f, 0.0f, 0.0f }; + RwV3d Yaxis = { 0.0f, 1.0f, 0.0f }; + RtQuatRotate(&m_pFrames[PED_HEAD]->hanimFrame->q, &Xaxis, CGeneral::GetRandomNumberInRange(-6.0f * velocityFwdDotProd, 6.0f * velocityFwdDotProd), rwCOMBINEPOSTCONCAT); + RtQuatRotate(&m_pFrames[PED_HEAD]->hanimFrame->q, &Yaxis, CGeneral::GetRandomNumberInRange(-6.0f * velocityFwdDotProd, 6.0f * velocityFwdDotProd), rwCOMBINEPOSTCONCAT); + bDontAcceptIKLookAts = true; + } + return; + } + + if (!IsPlayer()) + return; + + float steerAngle = m_pMyVehicle->m_fSteerAngle; + CAnimBlendAssociation* lDriveAssoc; + CAnimBlendAssociation* rDriveAssoc; + CAnimBlendAssociation* lbAssoc; + CAnimBlendAssociation* sitAssoc; + if (m_pMyVehicle->IsBoat() && !(m_pMyVehicle->pHandling->Flags & HANDLING_SIT_IN_BOAT)) { + sitAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_BOAT_DRIVE); + + if (!sitAssoc || sitAssoc->blendAmount < 1.0f) { + return; + } + + lDriveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_BOAT_DRIVE_LEFT); + rDriveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_BOAT_DRIVE_RIGHT); + lbAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_BOAT_LOOKBEHIND); + } else if (m_pMyVehicle->bLowVehicle) { + sitAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_SIT_LO); + + if (!sitAssoc || sitAssoc->blendAmount < 1.0f) { + return; + } + + lDriveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_DRIVE_LEFT_LO); + lbAssoc = nil; + rDriveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_DRIVE_RIGHT_LO); + } else { + sitAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_SIT); + + if (!sitAssoc || sitAssoc->blendAmount < 1.0f) { + return; + } + + lDriveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_DRIVE_LEFT); + rDriveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_DRIVE_RIGHT); + lbAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_LOOKBEHIND); + } + + if (lbAssoc && + TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON + && TheCamera.Cams[TheCamera.ActiveCam].DirectionWasLooking == LOOKING_LEFT) { + lbAssoc->blendDelta = -1000.0f; + } + + CAnimBlendAssociation* driveByAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_DRIVEBY_LEFT); + if (!driveByAssoc) + driveByAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_DRIVEBY_RIGHT); + if (!driveByAssoc) + driveByAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_DRIVEBY_LEFT_LO); + if (!driveByAssoc) + driveByAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_DRIVEBY_RIGHT_LO); + + if (m_pMyVehicle->bLowVehicle || m_pMyVehicle->m_fGasPedal >= 0.0f || driveByAssoc || + m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI || m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE) { + if (steerAngle == 0.0f || driveByAssoc) { + if (lDriveAssoc) + lDriveAssoc->blendAmount = 0.0f; + if (rDriveAssoc) + rDriveAssoc->blendAmount = 0.0f; + + } else if (steerAngle <= 0.0f) { + if (lDriveAssoc) + lDriveAssoc->blendAmount = 0.0f; + + if (rDriveAssoc) + rDriveAssoc->blendAmount = Clamp(steerAngle * -100.0f / 61.0f, 0.0f, 1.0f); + else if (m_pMyVehicle->IsBoat() && !(m_pMyVehicle->pHandling->Flags & HANDLING_SIT_IN_BOAT)) + CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_BOAT_DRIVE_RIGHT); + else if (m_pMyVehicle->bLowVehicle) + CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_DRIVE_RIGHT_LO); + else + CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_DRIVE_RIGHT); + + } else { + if (rDriveAssoc) + rDriveAssoc->blendAmount = 0.0f; + + if (lDriveAssoc) + lDriveAssoc->blendAmount = Clamp(steerAngle * 100.0f / 61.0f, 0.0f, 1.0f); + else if (m_pMyVehicle->IsBoat() && !(m_pMyVehicle->pHandling->Flags & HANDLING_SIT_IN_BOAT)) + CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_BOAT_DRIVE_LEFT); + else if (m_pMyVehicle->bLowVehicle) + CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_DRIVE_LEFT_LO); + else + CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_DRIVE_LEFT); + } + + if (lbAssoc) + lbAssoc->blendDelta = -4.0f; + } else { + + if ((TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_1STPERSON + || TheCamera.Cams[TheCamera.ActiveCam].DirectionWasLooking != LOOKING_LEFT) + && (!lbAssoc || lbAssoc->blendAmount < 1.0f && lbAssoc->blendDelta <= 0.0f)) { + + if (m_pMyVehicle->IsBoat() && !(m_pMyVehicle->pHandling->Flags & HANDLING_SIT_IN_BOAT)) + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_BOAT_LOOKBEHIND, 4.0f); + else + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_LOOKBEHIND, 4.0f); + } + } +} + +void +CPed::RemoveWeaponAnims(int unused, float animDelta) +{ + CAnimBlendAssociation *weaponAssoc; + //CWeaponInfo::GetWeaponInfo(unused); + + weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_FIRE); + if (weaponAssoc) { + weaponAssoc->blendDelta = animDelta; + weaponAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_FIRE_2ND); + if (weaponAssoc) { + weaponAssoc->blendDelta = animDelta; + weaponAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_FIRE_3RD); + if (weaponAssoc) { + weaponAssoc->blendDelta = animDelta; + weaponAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_RELOAD); + if (weaponAssoc) { + weaponAssoc->blendDelta = animDelta; + weaponAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_MELEE_IDLE_FIGHTMODE); + if (weaponAssoc) { + weaponAssoc->flags |= ASSOC_DELETEFADEDOUT; + if (weaponAssoc->flags & ASSOC_PARTIAL) + weaponAssoc->blendDelta = animDelta; + else + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, -animDelta); + } +} diff --git a/src/miami/peds/PedIK.cpp b/src/miami/peds/PedIK.cpp new file mode 100644 index 00000000..ea3cf1aa --- /dev/null +++ b/src/miami/peds/PedIK.cpp @@ -0,0 +1,375 @@ +#include "common.h" + +#include "Bones.h" +#include "Camera.h" +#include "PedIK.h" +#include "Ped.h" +#include "General.h" +#include "RwHelper.h" + +LimbMovementInfo CPedIK::ms_torsoInfo = { DEGTORAD(50.0f), DEGTORAD(-50.0f), DEGTORAD(8.0f), DEGTORAD(45.0f), DEGTORAD(-45.0f), DEGTORAD(5.0f) }; +LimbMovementInfo CPedIK::ms_headInfo = { DEGTORAD(90.0f), DEGTORAD(-90.0f), DEGTORAD(15.0f), DEGTORAD(45.0f), DEGTORAD(-45.0f), DEGTORAD(8.0f) }; +LimbMovementInfo CPedIK::ms_headRestoreInfo = { DEGTORAD(90.0f), DEGTORAD(-90.0f), DEGTORAD(10.0f), DEGTORAD(45.0f), DEGTORAD(-45.0f), DEGTORAD(5.0f) }; +LimbMovementInfo CPedIK::ms_upperArmInfo = { DEGTORAD(5.0f), DEGTORAD(-120.0f), DEGTORAD(20.0f), DEGTORAD(70.0f), DEGTORAD(-70.0f), DEGTORAD(20.0f) }; +LimbMovementInfo CPedIK::ms_lowerArmInfo = { DEGTORAD(60.0f), DEGTORAD(0.0f), DEGTORAD(15.0f), DEGTORAD(90.0f), DEGTORAD(-90.0f), DEGTORAD(10.0f) }; + +const RwV3d XaxisIK = { 1.0f, 0.0f, 0.0f}; +const RwV3d YaxisIK = { 0.0f, 1.0f, 0.0f}; +const RwV3d ZaxisIK = { 0.0f, 0.0f, 1.0f}; + +CPedIK::CPedIK(CPed *ped) : m_ped(ped) +{ + assert(ped != nil); + m_flags = 0; + m_headOrient.yaw = 0.0f; + m_headOrient.pitch = 0.0f; + m_torsoOrient.yaw = 0.0f; + m_torsoOrient.pitch = 0.0f; + m_upperArmOrient.yaw = 0.0f; + m_upperArmOrient.pitch = 0.0f; + m_lowerArmOrient.yaw = 0.0f; + m_lowerArmOrient.pitch = 0.0f; +} + +inline RwMatrix* +GetBoneMatrix(CPed *ped, int32 bone) +{ + RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(ped->GetClump()); + int idx = RpHAnimIDGetIndex(hier, bone); + RwMatrix *mats = RpHAnimHierarchyGetMatrixArray(hier); + return &mats[idx]; +} +inline RwMatrix* +GetComponentMatrix(CPed *ped, int32 node) +{ + return GetBoneMatrix(ped, ped->m_pFrames[node]->nodeID); +} + +void +CPedIK::RotateTorso(AnimBlendFrameData *node, LimbOrientation *limb, bool changeRoll) +{ + RtQuat *q = &node->hanimFrame->q; + RtQuatRotate(q, &XaxisIK, RADTODEG(limb->yaw), rwCOMBINEREPLACE); + RtQuatRotate(q, &ZaxisIK, RADTODEG(limb->pitch), rwCOMBINEPRECONCAT); + m_ped->bDontAcceptIKLookAts = true; +} + +void +CPedIK::GetComponentPosition(RwV3d &pos, uint32 node) +{ + pos = GetComponentMatrix(m_ped, node)->pos; +} + +LimbMoveStatus +CPedIK::MoveLimb(LimbOrientation &limb, float targetYaw, float targetPitch, LimbMovementInfo &moveInfo) +{ + LimbMoveStatus result = ONE_ANGLE_COULDNT_BE_SET_EXACTLY; + + // yaw + + if(Abs(limb.yaw-targetYaw) < moveInfo.yawD){ + limb.yaw = targetYaw; + result = ANGLES_SET_EXACTLY; + }else{ + if (limb.yaw > targetYaw) { + limb.yaw -= moveInfo.yawD; + } else if (limb.yaw < targetYaw) { + limb.yaw += moveInfo.yawD; + } + } + + if (limb.yaw > moveInfo.maxYaw || limb.yaw < moveInfo.minYaw) { + limb.yaw = Clamp(limb.yaw, moveInfo.minYaw, moveInfo.maxYaw); + result = ANGLES_SET_TO_MAX; + } + + // pitch + + if (Abs(limb.pitch - targetPitch) < moveInfo.pitchD){ + limb.pitch = targetPitch; + }else{ + if (limb.pitch > targetPitch) { + limb.pitch -= moveInfo.pitchD; + } else if (limb.pitch < targetPitch) { + limb.pitch += moveInfo.pitchD; + } + result = ONE_ANGLE_COULDNT_BE_SET_EXACTLY; + } + + if (limb.pitch > moveInfo.maxPitch || limb.pitch < moveInfo.minPitch) { + limb.pitch = Clamp(limb.pitch, moveInfo.minPitch, moveInfo.maxPitch); + result = ANGLES_SET_TO_MAX; + } + return result; +} + +bool +CPedIK::RestoreGunPosn(void) +{ + LimbMoveStatus limbStatus = MoveLimb(m_torsoOrient, 0.0f, 0.0f, ms_torsoInfo); + RotateTorso(m_ped->m_pFrames[PED_MID], &m_torsoOrient, false); + return limbStatus == ANGLES_SET_EXACTLY; +} + +bool +CPedIK::LookInDirection(float targetYaw, float targetPitch) +{ + bool success = true; + float yaw, pitch; + if (!(m_ped->m_pFrames[PED_HEAD]->flag & AnimBlendFrameData::IGNORE_ROTATION)) { + m_ped->m_pFrames[PED_HEAD]->flag |= AnimBlendFrameData::IGNORE_ROTATION; + RwMatrix *m = GetComponentMatrix(m_ped, PED_NECK); + m_headOrient.yaw = Atan2(-m->at.y, -m->at.x); + m_headOrient.yaw -= m_ped->m_fRotationCur; + m_headOrient.yaw = CGeneral::LimitRadianAngle(m_headOrient.yaw); + float up = Clamp(m->up.z, -1.0f, 1.0f); + m_headOrient.pitch = Atan2(-up, Sqrt(1.0f - SQR(-up))); + } + + // parent of head is neck + RwMatrix *m = GetComponentMatrix(m_ped, PED_NECK); + yaw = CGeneral::LimitRadianAngle(Atan2(-m->at.y, -m->at.x)); + float up = Clamp(m->up.z, -1.0f, 1.0f); + pitch = Atan2(-up, Sqrt(1.0f - SQR(-up))); + float headYaw = CGeneral::LimitRadianAngle(targetYaw - (yaw + m_torsoOrient.yaw)); + float headPitch = CGeneral::LimitRadianAngle(targetPitch - pitch) * Cos(Min(Abs(headYaw), HALFPI)); + + LimbMoveStatus headStatus = MoveLimb(m_headOrient, headYaw, headPitch, ms_headInfo); + if (headStatus == ANGLES_SET_TO_MAX) + success = false; + + if (headStatus != ANGLES_SET_EXACTLY && !(m_flags & LOOKAROUND_HEAD_ONLY)) + if (MoveLimb(m_torsoOrient, CGeneral::LimitRadianAngle(targetYaw-m_ped->m_fRotationCur), targetPitch, ms_torsoInfo)) + success = true; + + // This was RotateHead + RtQuat *q = &m_ped->m_pFrames[PED_HEAD]->hanimFrame->q; + RtQuatRotate(q, &ZaxisIK, RADTODEG(m_headOrient.pitch), rwCOMBINEREPLACE); + RtQuatRotate(q, &XaxisIK, RADTODEG(m_headOrient.yaw), rwCOMBINEPRECONCAT); + m_ped->bDontAcceptIKLookAts = true; + + if (!(m_flags & LOOKAROUND_HEAD_ONLY)) + RotateTorso(m_ped->m_pFrames[PED_MID], &m_torsoOrient, false); + return success; +} + +bool +CPedIK::LookAtPosition(CVector const &pos) +{ + RwV3d *pedpos = &GetComponentMatrix(m_ped, PED_MID)->pos; + float yawToFace = CGeneral::GetRadianAngleBetweenPoints( + pos.x, pos.y, + pedpos->x, pedpos->y); + + float pitchToFace = CGeneral::GetRadianAngleBetweenPoints( + // BUG? not using pedpos here + pos.z, (m_ped->GetPosition() - pos).Magnitude2D(), + pedpos->z, 0.0f); + + return LookInDirection(yawToFace, pitchToFace); +} + +bool +CPedIK::PointGunInDirection(float targetYaw, float targetPitch) +{ + bool result = true; + bool armPointedToGun = false; + targetYaw = CGeneral::LimitRadianAngle(targetYaw - m_ped->GetForward().Heading()); + m_flags &= ~GUN_POINTED_SUCCESSFULLY; + m_flags |= LOOKAROUND_HEAD_ONLY; + if (m_flags & AIMS_WITH_ARM) { + armPointedToGun = PointGunInDirectionUsingArm(targetYaw, targetPitch); + targetYaw = CGeneral::LimitRadianAngle(targetYaw - (m_upperArmOrient.yaw + m_lowerArmOrient.yaw)); + } + if (armPointedToGun) { + if (m_flags & AIMS_WITH_ARM && m_torsoOrient.yaw * m_upperArmOrient.yaw < 0.0f) + MoveLimb(m_torsoOrient, 0.0f, m_torsoOrient.pitch, ms_torsoInfo); + } else { + // Unused code + RwMatrix *matrix; + float yaw, pitch; + matrix = RwMatrixCreate(); + *matrix = *GetComponentMatrix(m_ped, PED_CLAVICLER); + ExtractYawAndPitchWorld(matrix, &yaw, &pitch); + RwMatrixDestroy(matrix); + + if(m_flags & AIMS_WITH_ARM){ + if(targetPitch > 0.0f) + targetPitch = Max(targetPitch - Abs(targetYaw), 0.0f); + else + targetPitch = Min(targetPitch + Abs(targetYaw), 0.0f); + } + LimbMoveStatus status = MoveLimb(m_torsoOrient, targetYaw, targetPitch, ms_torsoInfo); + if (status == ANGLES_SET_TO_MAX) + result = false; + else if (status == ANGLES_SET_EXACTLY) + m_flags |= GUN_POINTED_SUCCESSFULLY; + } + + // Game uses index 2 directly, which happens to be identical to BONE_spine +#ifdef FIX_BUGS + RwMatrix *m = GetBoneMatrix(m_ped, BONE_spine); +#else + RpHAnimHierarchy* hier = GetAnimHierarchyFromSkinClump(m_ped->GetClump()); + RwMatrix *mats = RpHAnimHierarchyGetMatrixArray(hier); + RwMatrix *m = &mats[2]; +#endif + RwV3d axis = { 0.0f, 0.0f, 0.0f }; + float axisangle = -CGeneral::LimitRadianAngle(Atan2(-m->at.y, -m->at.x) - m_ped->m_fRotationCur); + axis.y = -Sin(axisangle); + axis.z = Cos(axisangle); + + // this was RotateTorso + RtQuat *q = &m_ped->m_pFrames[PED_MID]->hanimFrame->q; + RtQuatRotate(q, &axis, RADTODEG(m_torsoOrient.pitch), rwCOMBINEPOSTCONCAT); + RtQuatRotate(q, &XaxisIK, RADTODEG(m_torsoOrient.yaw), rwCOMBINEPOSTCONCAT); + m_ped->bDontAcceptIKLookAts = true; + + return result; +} + +bool +CPedIK::PointGunInDirectionUsingArm(float targetYaw, float targetPitch) +{ + bool result = false; + RwMatrix *matrix; + float yaw, pitch; + + float uaRoll = 45.0f; + float handRoll = 30.0f; + + matrix = GetComponentMatrix(m_ped, PED_CLAVICLER); + yaw = CGeneral::LimitRadianAngle(Atan2(matrix->right.y, matrix->right.x) - m_ped->m_fRotationCur); + pitch = Atan2(matrix->up.z, Sqrt(1.0f - SQR(matrix->up.z))); + + float uaYaw, uaPitch; + uaYaw = CGeneral::LimitRadianAngle(targetYaw - yaw - DEGTORAD(15.0f)); + uaPitch = CGeneral::LimitRadianAngle(targetPitch - pitch + DEGTORAD(10.0f)); + LimbMoveStatus uaStatus = MoveLimb(m_upperArmOrient, uaYaw, uaPitch, ms_upperArmInfo); + if (uaStatus == ANGLES_SET_EXACTLY) { + m_flags |= GUN_POINTED_SUCCESSFULLY; + result = true; + } + + if (uaStatus == ANGLES_SET_TO_MAX) { + float laYaw = uaYaw - m_upperArmOrient.yaw; + + LimbMoveStatus laStatus; + if (laYaw > 0.0f){ + float rollReduce = laYaw/DEGTORAD(30.0f); + uaRoll *= 1.0f - Min(rollReduce, 1.0f); + handRoll *= 1.0f - Min(rollReduce, 1.0f); + + laYaw *= 1.9f; + laStatus = MoveLimb(m_lowerArmOrient, laYaw, 0.0f, ms_lowerArmInfo); + + // some unused statics here + float uaPitchAmount = 1.0f - (m_lowerArmOrient.yaw + m_upperArmOrient.yaw) * 0.34f; + float f1 = ms_upperArmInfo.maxPitch * Max(uaPitchAmount, 0.0f); + float f2 = 0.2f*m_lowerArmOrient.yaw + m_upperArmOrient.pitch; + m_upperArmOrient.pitch = Min(f1, f2); + }else + laStatus = MoveLimb(m_lowerArmOrient, laYaw, 0.0f, ms_lowerArmInfo); + + if (laStatus == ANGLES_SET_EXACTLY) { + m_flags |= GUN_POINTED_SUCCESSFULLY; + result = true; + } + + // game does this stupidly by going through the clump extension... + RtQuat *q = &m_ped->m_pFrames[PED_FOREARMR]->hanimFrame->q; + RtQuatRotate(q, &ZaxisIK, -RADTODEG(m_lowerArmOrient.yaw), rwCOMBINEREPLACE); + RtQuatRotate(q, &XaxisIK, -RADTODEG(m_lowerArmOrient.pitch), rwCOMBINEPOSTCONCAT); + m_ped->bDontAcceptIKLookAts = true; + } + + RtQuat *q = &m_ped->m_pFrames[PED_UPPERARMR]->hanimFrame->q; + RtQuatRotate(q, &XaxisIK, uaRoll, rwCOMBINEREPLACE); + RtQuatRotate(q, &YaxisIK, -RADTODEG(m_upperArmOrient.pitch), rwCOMBINEPOSTCONCAT); + RtQuatRotate(q, &ZaxisIK, -RADTODEG(m_upperArmOrient.yaw+HALFPI), rwCOMBINEPOSTCONCAT); + m_ped->bDontAcceptIKLookAts = true; + + q = &m_ped->m_pFrames[PED_HANDR]->hanimFrame->q; + RtQuatRotate(q, &XaxisIK, handRoll, rwCOMBINEPRECONCAT); + + return result; +} + +bool +CPedIK::PointGunAtPosition(CVector const& position) +{ + CVector startPoint; + if (m_ped->GetWeapon()->m_eWeaponType == WEAPONTYPE_SPAS12_SHOTGUN || m_ped->GetWeapon()->m_eWeaponType == WEAPONTYPE_STUBBY_SHOTGUN) + startPoint = m_ped->GetPosition(); + else { + RwV3d armPos; + GetComponentPosition(armPos, PED_UPPERARMR); + startPoint.x = m_ped->GetPosition().x; + startPoint.y = m_ped->GetPosition().y; + startPoint.z = armPos.z; + } + + return PointGunInDirection( + CGeneral::GetRadianAngleBetweenPoints(position.x, position.y, startPoint.x, startPoint.y), + CGeneral::GetRadianAngleBetweenPoints(position.z, Distance2D(m_ped->GetPosition(), position.x, position.y), startPoint.z, 0.0f)); +} + +bool +CPedIK::RestoreLookAt(void) +{ + bool result = false; + float yaw, pitch; + + if (m_ped->m_pFrames[PED_HEAD]->flag & AnimBlendFrameData::IGNORE_ROTATION) { + m_ped->m_pFrames[PED_HEAD]->flag &= (~AnimBlendFrameData::IGNORE_ROTATION); + } else { + ExtractYawAndPitchLocalSkinned(m_ped->m_pFrames[PED_HEAD], &yaw, &pitch); + if (MoveLimb(m_headOrient, yaw, pitch, ms_headRestoreInfo) == ANGLES_SET_EXACTLY) + result = true; + } + + // This was RotateHead + RtQuat *q = &m_ped->m_pFrames[PED_HEAD]->hanimFrame->q; + RtQuatRotate(q, &XaxisIK, RADTODEG(m_headOrient.yaw), rwCOMBINEREPLACE); + RtQuatRotate(q, &ZaxisIK, RADTODEG(m_headOrient.pitch), rwCOMBINEPRECONCAT); + m_ped->bDontAcceptIKLookAts = true; + + if (!(m_flags & LOOKAROUND_HEAD_ONLY)) + MoveLimb(m_torsoOrient, 0.0f, 0.0f, ms_torsoInfo); + if (!(m_flags & LOOKAROUND_HEAD_ONLY)) + RotateTorso(m_ped->m_pFrames[PED_MID], &m_torsoOrient, false); + return result; +} + +void +CPedIK::ExtractYawAndPitchWorld(RwMatrix *mat, float *yaw, float *pitch) +{ + float f = Clamp(DotProduct(mat->up, CVector(0.0f, 1.0f, 0.0f)), -1.0f, 1.0f); + *yaw = Acos(f); + if (mat->up.x > 0.0f) *yaw = -*yaw; + + f = Clamp(DotProduct(mat->right, CVector(0.0f, 0.0f, 1.0f)), -1.0f, 1.0f); + *pitch = Acos(f); + if (mat->up.z > 0.0f) *pitch = -*pitch; +} + +void +CPedIK::ExtractYawAndPitchLocal(RwMatrix *mat, float *yaw, float *pitch) +{ + float f = Clamp(DotProduct(mat->at, CVector(0.0f, 0.0f, 1.0f)), -1.0f, 1.0f); + *yaw = Acos(f); + if (mat->at.y > 0.0f) *yaw = -*yaw; + + f = Clamp(DotProduct(mat->right, CVector(1.0f, 0.0f, 0.0f)), -1.0f, 1.0f); + *pitch = Acos(f); + if (mat->up.x > 0.0f) *pitch = -*pitch; +} + +void +CPedIK::ExtractYawAndPitchLocalSkinned(AnimBlendFrameData *node, float *yaw, float *pitch) +{ + RwMatrix *mat = RwMatrixCreate(); + RtQuatConvertToMatrix(&node->hanimFrame->q, mat); + ExtractYawAndPitchLocal(mat, yaw, pitch); + RwMatrixDestroy(mat); +} diff --git a/src/miami/peds/PedIK.h b/src/miami/peds/PedIK.h new file mode 100644 index 00000000..3011dd5f --- /dev/null +++ b/src/miami/peds/PedIK.h @@ -0,0 +1,66 @@ +#pragma once +#include "common.h" +#include "AnimBlendClumpData.h" + +struct LimbOrientation +{ + float yaw; + float pitch; +}; + +struct LimbMovementInfo { + float maxYaw; + float minYaw; + float yawD; + float maxPitch; + float minPitch; + float pitchD; +}; + +enum LimbMoveStatus { + ANGLES_SET_TO_MAX, // because given angles were unreachable + ONE_ANGLE_COULDNT_BE_SET_EXACTLY, // because it can't be reached in a jiffy + ANGLES_SET_EXACTLY +}; + +class CPed; + +class CPedIK +{ +public: + enum { + GUN_POINTED_SUCCESSFULLY = 1, + LOOKAROUND_HEAD_ONLY = 2, + AIMS_WITH_ARM = 4, + }; + + CPed *Const m_ped; + LimbOrientation m_headOrient; + LimbOrientation m_torsoOrient; + LimbOrientation m_upperArmOrient; + LimbOrientation m_lowerArmOrient; + int32 m_flags; + + static LimbMovementInfo ms_torsoInfo; + static LimbMovementInfo ms_headInfo; + static LimbMovementInfo ms_headRestoreInfo; + static LimbMovementInfo ms_upperArmInfo; + static LimbMovementInfo ms_lowerArmInfo; + + CPedIK(CPed *ped); + bool PointGunInDirection(float targetYaw, float targetPitch); + bool PointGunInDirectionUsingArm(float targetYaw, float targetPitch); + bool PointGunAtPosition(CVector const& position); + void GetComponentPosition(RwV3d &pos, uint32 node); + void RotateTorso(AnimBlendFrameData* animBlend, LimbOrientation* limb, bool changeRoll); + void ExtractYawAndPitchLocal(RwMatrix *mat, float *yaw, float *pitch); + void ExtractYawAndPitchLocalSkinned(AnimBlendFrameData *node, float *yaw, float *pitch); + void ExtractYawAndPitchWorld(RwMatrix *mat, float *yaw, float *pitch); + LimbMoveStatus MoveLimb(LimbOrientation &limb, float targetYaw, float targetPitch, LimbMovementInfo &moveInfo); + bool RestoreGunPosn(void); + bool LookInDirection(float targetYaw, float targetPitch); + bool LookAtPosition(CVector const& pos); + bool RestoreLookAt(void); +}; + +VALIDATE_SIZE(CPedIK, 0x28); diff --git a/src/miami/peds/PedPlacement.cpp b/src/miami/peds/PedPlacement.cpp new file mode 100644 index 00000000..840d33fc --- /dev/null +++ b/src/miami/peds/PedPlacement.cpp @@ -0,0 +1,58 @@ +#include "common.h" + +#include "Ped.h" +#include "PedPlacement.h" +#include "World.h" + +bool +CPedPlacement::FindZCoorForPed(CVector* pos) +{ + float zForPed; + float startZ = pos->z - 100.0f; + float foundColZ = -100.0f; + float foundColZ2 = -100.0f; + CColPoint foundCol; + CEntity* foundEnt; + + CVector vec( + pos->x, + pos->y, + pos->z + 1.0f + ); + + if (CWorld::ProcessVerticalLine(vec, startZ, foundCol, foundEnt, true, false, false, false, true, false, nil)) + foundColZ = foundCol.point.z; + + // Adjust coords and do a second test + vec.x += 0.1f; + vec.y += 0.1f; + + if (CWorld::ProcessVerticalLine(vec, startZ, foundCol, foundEnt, true, false, false, false, true, false, nil)) + foundColZ2 = foundCol.point.z; + + zForPed = Max(foundColZ, foundColZ2); + + if (zForPed > -99.0f) { + pos->z = FEET_OFFSET + zForPed; + return true; + } + return false; +} + +CEntity* +CPedPlacement::IsPositionClearOfCars(Const CVector *pos) +{ + return CWorld::TestSphereAgainstWorld(*pos, 0.25f, nil, true, true, false, false, false, false); +} + +bool +CPedPlacement::IsPositionClearForPed(const CVector& pos, float radius, int total, CEntity** entities) +{ + int16 count; + if (radius == -1.0f) + radius = 0.75f; + if (total == -1) + total = 2; + CWorld::FindObjectsKindaColliding(pos, radius, true, &count, total, entities, false, true, true, false, false); + return count == 0; +} diff --git a/src/miami/peds/PedPlacement.h b/src/miami/peds/PedPlacement.h new file mode 100644 index 00000000..d1b0cd16 --- /dev/null +++ b/src/miami/peds/PedPlacement.h @@ -0,0 +1,8 @@ +#pragma once + +class CPedPlacement { +public: + static bool FindZCoorForPed(CVector* pos); + static CEntity* IsPositionClearOfCars(Const CVector*); + static bool IsPositionClearForPed(const CVector& pos, float radius = -1.0f, int total = -1, CEntity** entities = nil); +}; \ No newline at end of file diff --git a/src/miami/peds/PedRoutes.cpp b/src/miami/peds/PedRoutes.cpp new file mode 100644 index 00000000..3ff080e6 --- /dev/null +++ b/src/miami/peds/PedRoutes.cpp @@ -0,0 +1,79 @@ +#include "common.h" + +#include "main.h" +#include "PedRoutes.h" + +CRouteNode gaRoutes[NUMPEDROUTES]; + +void +CRouteNode::Initialise() +{ + for (int i = 0; i < NUMPEDROUTES; i++) { + gaRoutes[i].m_route = -1; + gaRoutes[i].m_pos = CVector(0.0f, 0.0f, 0.0f); + } +} + +int16 +CRouteNode::GetRouteThisPointIsOn(int16 point) +{ + return gaRoutes[point].m_route; +} + +// Actually GetFirstPointOfRoute +int16 +CRouteNode::GetRouteStart(int16 route) +{ + for (int i = 0; i < NUMPEDROUTES; i++) { + if (route == gaRoutes[i].m_route) + return i; + } + return -1; +} + +CVector +CRouteNode::GetPointPosition(int16 point) +{ + return gaRoutes[point].m_pos; +} + +void +CRouteNode::AddRoutePoint(int16 route, CVector pos) +{ + uint16 point; + for (point = 0; point < NUMPEDROUTES; point++) { + if (gaRoutes[point].m_route == -1) + break; + } +#ifdef FIX_BUGS + if (point == NUMPEDROUTES) + return; +#endif + gaRoutes[point].m_route = route; + gaRoutes[point].m_pos = pos; +} + +void +CRouteNode::RemoveRoute(int16 route) +{ + uint16 first_point, last_point, i; + for (first_point = 0; first_point < NUMPEDROUTES; first_point++) { + if (gaRoutes[first_point].m_route == route) + break; + } + if (first_point == NUMPEDROUTES) + return; + for (last_point = first_point; last_point < NUMPEDROUTES; last_point++) + if (gaRoutes[last_point].m_route != route) + break; + uint16 diff = last_point - first_point; +#ifdef FIX_BUGS + for (i = first_point; i < NUMPEDROUTES - diff; i++) + gaRoutes[i] = gaRoutes[i + diff]; +#else + for (i = 0; i < diff; i++) + gaRoutes[first_point + i] = gaRoutes[last_point + i]; +#endif + for (i = NUMPEDROUTES - diff; i < NUMPEDROUTES; i++) + gaRoutes[i].m_route = -1; +} diff --git a/src/miami/peds/PedRoutes.h b/src/miami/peds/PedRoutes.h new file mode 100644 index 00000000..c478e38d --- /dev/null +++ b/src/miami/peds/PedRoutes.h @@ -0,0 +1,15 @@ +#pragma once + +class CRouteNode +{ +public: + int16 m_route; + CVector m_pos; + + static int16 GetRouteThisPointIsOn(int16); + static CVector GetPointPosition(int16); + static int16 GetRouteStart(int16); + static void AddRoutePoint(int16, CVector); + static void RemoveRoute(int16); + static void Initialise(void); +}; diff --git a/src/miami/peds/PedType.cpp b/src/miami/peds/PedType.cpp new file mode 100644 index 00000000..dcd4c717 --- /dev/null +++ b/src/miami/peds/PedType.cpp @@ -0,0 +1,317 @@ +#include "common.h" + +#include "General.h" +#include "FileMgr.h" +#include "PedType.h" +#include "SaveBuf.h" + +CPedType *CPedType::ms_apPedType[NUM_PEDTYPES]; +CPedStats *CPedStats::ms_apPedStats[NUM_PEDSTATS]; + +void +CPedType::Initialise(void) +{ + int i; + + debug("Initialising CPedType...\n"); + for(i = 0; i < NUM_PEDTYPES; i++){ + ms_apPedType[i] = new CPedType; + ms_apPedType[i]->m_flag = PED_FLAG_PLAYER1; + ms_apPedType[i]->unknown1 = 0.0f; + ms_apPedType[i]->unknown2 = 0.0f; + // unknown3 not initialized + ms_apPedType[i]->unknown4 = 0.0f; + ms_apPedType[i]->unknown5 = 0.0f; + ms_apPedType[i]->m_threats = 0; + ms_apPedType[i]->m_avoid = 0; + } + debug("Loading ped data...\n"); + LoadPedData(); + debug("CPedType ready\n"); +} + +void +CPedType::Shutdown(void) +{ + int i; + debug("Shutting down CPedType...\n"); + for(i = 0; i < NUM_PEDTYPES; i++) + delete ms_apPedType[i]; + debug("CPedType shut down\n"); +} + +void +CPedType::LoadPedData(void) +{ + char *buf; + char line[256]; + char word[32]; + ssize_t bp, buflen; + int lp, linelen; + int type; + uint32 flags; + float f1, f2, f3, f4, f5; + + type = NUM_PEDTYPES; + buf = new char[16 * 1024]; + + CFileMgr::SetDir("DATA"); + buflen = CFileMgr::LoadFile("PED.DAT", (uint8*)buf, 16 * 1024, "r"); + CFileMgr::SetDir(""); + + for(bp = 0; bp < buflen; ){ + // read file line by line + for(linelen = 0; buf[bp] != '\n' && bp < buflen; bp++){ + if(buf[bp] == '\r' || buf[bp] == ',' || buf[bp] == '\t') + line[linelen++] = ' '; + else + line[linelen++] = buf[bp]; + } + bp++; + line[linelen] = '\0'; + + // skip white space + for(lp = 0; line[lp] <= ' '; lp++); + + if(lp >= linelen || // FIX: game uses == here, but this is safer if we have empty lines + line[lp] == '#') + continue; + + // Game uses just "line" here since sscanf already trims whitespace, but this is safer + sscanf(&line[lp], "%s", word); + + if(strcmp(word, "Threat") == 0){ + flags = 0; + lp += 7; + while(sscanf(&line[lp], "%s", word) == 1 && lp <= linelen){ + flags |= FindPedFlag(word); + // skip word + while(line[lp] != ' ' && line[lp] != '\n' && line[lp] != '\0') + lp++; + // skip white space + while(line[lp] == ' ') + lp++; + } + ms_apPedType[type]->m_threats = flags; + }else if(strcmp(word, "Avoid") == 0){ + flags = 0; + lp += 6; + while(sscanf(&line[lp], "%s", word) == 1 && lp <= linelen){ + flags |= FindPedFlag(word); + // skip word + while(line[lp] != ' ' && line[lp] != '\n' && line[lp] != '\0') + lp++; + // skip white space + while(line[lp] == ' ') + lp++; + } + ms_apPedType[type]->m_avoid = flags; + }else{ + sscanf(line, "%s %f %f %f %f %f", word, &f1, &f2, &f3, &f4, &f5); + type = FindPedType(word); + ms_apPedType[type]->m_flag = FindPedFlag(word); + // unknown values + ms_apPedType[type]->unknown1 = f1 / 50.0f; + ms_apPedType[type]->unknown2 = f2 / 50.0f; + ms_apPedType[type]->unknown3 = f3 / 50.0f; + ms_apPedType[type]->unknown4 = f4; + ms_apPedType[type]->unknown5 = f5; + + } + } + + delete[] buf; +} + +ePedType +CPedType::FindPedType(char *type) +{ + if(strcmp(type, "PLAYER1") == 0) return PEDTYPE_PLAYER1; + if(strcmp(type, "PLAYER2") == 0) return PEDTYPE_PLAYER2; + if(strcmp(type, "PLAYER3") == 0) return PEDTYPE_PLAYER3; + if(strcmp(type, "PLAYER4") == 0) return PEDTYPE_PLAYER4; + if(strcmp(type, "CIVMALE") == 0) return PEDTYPE_CIVMALE; + if(strcmp(type, "CIVFEMALE") == 0) return PEDTYPE_CIVFEMALE; + if(strcmp(type, "COP") == 0) return PEDTYPE_COP; + if(strcmp(type, "GANG1") == 0) return PEDTYPE_GANG1; + if(strcmp(type, "GANG2") == 0) return PEDTYPE_GANG2; + if(strcmp(type, "GANG3") == 0) return PEDTYPE_GANG3; + if(strcmp(type, "GANG4") == 0) return PEDTYPE_GANG4; + if(strcmp(type, "GANG5") == 0) return PEDTYPE_GANG5; + if(strcmp(type, "GANG6") == 0) return PEDTYPE_GANG6; + if(strcmp(type, "GANG7") == 0) return PEDTYPE_GANG7; + if(strcmp(type, "GANG8") == 0) return PEDTYPE_GANG8; + if(strcmp(type, "GANG9") == 0) return PEDTYPE_GANG9; + if(strcmp(type, "EMERGENCY") == 0) return PEDTYPE_EMERGENCY; + if(strcmp(type, "FIREMAN") == 0) return PEDTYPE_FIREMAN; + if(strcmp(type, "CRIMINAL") == 0) return PEDTYPE_CRIMINAL; + if(strcmp(type, "SPECIAL") == 0) return PEDTYPE_SPECIAL; + if(strcmp(type, "PROSTITUTE") == 0) return PEDTYPE_PROSTITUTE; + Error("Unknown ped type, Pedtype.cpp"); + return NUM_PEDTYPES; +} + +uint32 +CPedType::FindPedFlag(char *type) +{ + if(strcmp(type, "PLAYER1") == 0) return PED_FLAG_PLAYER1; + if(strcmp(type, "PLAYER2") == 0) return PED_FLAG_PLAYER2; + if(strcmp(type, "PLAYER3") == 0) return PED_FLAG_PLAYER3; + if(strcmp(type, "PLAYER4") == 0) return PED_FLAG_PLAYER4; + if(strcmp(type, "CIVMALE") == 0) return PED_FLAG_CIVMALE; + if(strcmp(type, "CIVFEMALE") == 0) return PED_FLAG_CIVFEMALE; + if(strcmp(type, "COP") == 0) return PED_FLAG_COP; + if(strcmp(type, "GANG1") == 0) return PED_FLAG_GANG1; + if(strcmp(type, "GANG2") == 0) return PED_FLAG_GANG2; + if(strcmp(type, "GANG3") == 0) return PED_FLAG_GANG3; + if(strcmp(type, "GANG4") == 0) return PED_FLAG_GANG4; + if(strcmp(type, "GANG5") == 0) return PED_FLAG_GANG5; + if(strcmp(type, "GANG6") == 0) return PED_FLAG_GANG6; + if(strcmp(type, "GANG7") == 0) return PED_FLAG_GANG7; + if(strcmp(type, "GANG8") == 0) return PED_FLAG_GANG8; + if(strcmp(type, "GANG9") == 0) return PED_FLAG_GANG9; + if(strcmp(type, "EMERGENCY") == 0) return PED_FLAG_EMERGENCY; + if(strcmp(type, "FIREMAN") == 0) return PED_FLAG_FIREMAN; + if(strcmp(type, "CRIMINAL") == 0) return PED_FLAG_CRIMINAL; + if(strcmp(type, "SPECIAL") == 0) return PED_FLAG_SPECIAL; + if(strcmp(type, "GUN") == 0) return PED_FLAG_GUN; + if(strcmp(type, "COP_CAR") == 0) return PED_FLAG_COP_CAR; + if(strcmp(type, "FAST_CAR") == 0) return PED_FLAG_FAST_CAR; + if(strcmp(type, "EXPLOSION") == 0) return PED_FLAG_EXPLOSION; + if(strcmp(type, "PROSTITUTE") == 0) return PED_FLAG_PROSTITUTE; + if(strcmp(type, "DEADPEDS") == 0) return PED_FLAG_DEADPEDS; + return 0; +} + +void +CPedType::Save(uint8 *buf, uint32 *size) +{ + *size = sizeof(CPedType) * NUM_PEDTYPES + SAVE_HEADER_SIZE; +INITSAVEBUF + WriteSaveHeader(buf, 'P','T','P','\0', *size - SAVE_HEADER_SIZE); + for(int i = 0; i < NUM_PEDTYPES; i++) + WriteSaveBuf(buf, *ms_apPedType[i]); +VALIDATESAVEBUF(*size) +} + +void +CPedType::Load(uint8 *buf, uint32 size) +{ +INITSAVEBUF + // original: SkipSaveBuf(buf, SAVE_HEADER_SIZE); + CheckSaveHeader(buf, 'P', 'T', 'P', '\0', size - SAVE_HEADER_SIZE); + + for(int i = 0; i < NUM_PEDTYPES; i++) + ReadSaveBuf(ms_apPedType[i], buf); +VALIDATESAVEBUF(size) +} + +void +CPedStats::Initialise(void) +{ + int i; + + debug("Initialising CPedStats...\n"); + for(i = 0; i < NUM_PEDSTATS; i++){ + ms_apPedStats[i] = new CPedStats; + ms_apPedStats[i]->m_type = PEDSTAT_PLAYER; + ms_apPedStats[i]->m_name[8] = 'R'; // WHAT? + ms_apPedStats[i]->m_fleeDistance = 20.0f; + ms_apPedStats[i]->m_headingChangeRate = 15.0f; + ms_apPedStats[i]->m_fear = 50; + ms_apPedStats[i]->m_temper = 50; + ms_apPedStats[i]->m_lawfulness = 50; + ms_apPedStats[i]->m_sexiness = 50; + ms_apPedStats[i]->m_attackStrength = 1.0f; + ms_apPedStats[i]->m_defendWeakness = 1.0f; + ms_apPedStats[i]->m_flags = 0; + } + debug("Loading pedstats data...\n"); + CPedStats::LoadPedStats(); + debug("CPedStats ready\n"); +} + +void +CPedStats::Shutdown(void) +{ + int i; + debug("Shutting down CPedStats...\n"); + for(i = 0; i < NUM_PEDSTATS; i++) + delete ms_apPedStats[i]; + debug("CPedStats shut down\n"); +} + +void +CPedStats::LoadPedStats(void) +{ + char *buf; + char line[256]; + char name[32]; + ssize_t bp, buflen; + int lp, linelen; + int type; + float fleeDist, headingChangeRate, attackStrength, defendWeakness; + int fear, temper, lawfullness, sexiness, flags; + + type = 0; + buf = new char[16 * 1024]; + + CFileMgr::SetDir("DATA"); + buflen = CFileMgr::LoadFile("PEDSTATS.DAT", (uint8*)buf, 16 * 1024, "r"); + CFileMgr::SetDir(""); + + for(bp = 0; bp < buflen; ){ + // read file line by line + for(linelen = 0; buf[bp] != '\n' && bp < buflen; bp++){ + if(buf[bp] == '\r' || buf[bp] == ',' || buf[bp] == '\t') + line[linelen++] = ' '; + else + line[linelen++] = buf[bp]; + } + bp++; + line[linelen] = '\0'; + + // skip white space + for(lp = 0; line[lp] <= ' '; lp++); + + if(lp >= linelen || // FIX: game uses == here, but this is safer if we have empty lines + line[lp] == '#') + continue; + + sscanf(&line[lp], "%s %f %f %d %d %d %d %f %f %d", + name, + &fleeDist, + &headingChangeRate, + &fear, + &temper, + &lawfullness, + &sexiness, + &attackStrength, + &defendWeakness, + &flags); + ms_apPedStats[type]->m_type = (ePedStats)type; + strncpy(ms_apPedStats[type]->m_name, name, 24); // FIX: game uses strcpy + ms_apPedStats[type]->m_fleeDistance = fleeDist; + ms_apPedStats[type]->m_headingChangeRate = headingChangeRate; + ms_apPedStats[type]->m_fear = fear; + ms_apPedStats[type]->m_temper = temper; + ms_apPedStats[type]->m_lawfulness = lawfullness; + ms_apPedStats[type]->m_sexiness = sexiness; + ms_apPedStats[type]->m_attackStrength = attackStrength; + ms_apPedStats[type]->m_defendWeakness = defendWeakness; + ms_apPedStats[type]->m_flags = flags; + type++; + } + + delete[] buf; +} + +ePedStats +CPedStats::GetPedStatType(char *name) +{ + for(uint16 type = 0; type < NUM_PEDSTATS; type++) + if(!CGeneral::faststrcmp(ms_apPedStats[type]->m_name, name)) + return (ePedStats) type; + + return NUM_PEDSTATS; +} diff --git a/src/miami/peds/PedType.h b/src/miami/peds/PedType.h new file mode 100644 index 00000000..a4698bbb --- /dev/null +++ b/src/miami/peds/PedType.h @@ -0,0 +1,178 @@ +#pragma once + +// Index into the PedType array +enum ePedType +{ + PEDTYPE_PLAYER1, + PEDTYPE_PLAYER2, + PEDTYPE_PLAYER3, + PEDTYPE_PLAYER4, + PEDTYPE_CIVMALE, + PEDTYPE_CIVFEMALE, + PEDTYPE_COP, + PEDTYPE_GANG1, + PEDTYPE_GANG2, + PEDTYPE_GANG3, + PEDTYPE_GANG4, + PEDTYPE_GANG5, // Security - hardcoded + PEDTYPE_GANG6, + PEDTYPE_GANG7, // Vercetti gang - hardcoded + PEDTYPE_GANG8, + PEDTYPE_GANG9, + PEDTYPE_EMERGENCY, + PEDTYPE_FIREMAN, + PEDTYPE_CRIMINAL, + PEDTYPE_UNUSED1, + PEDTYPE_PROSTITUTE, + PEDTYPE_SPECIAL, + PEDTYPE_UNUSED2, + + NUM_PEDTYPES +}; + +enum +{ + PED_FLAG_PLAYER1 = 1 << 0, + PED_FLAG_PLAYER2 = 1 << 1, + PED_FLAG_PLAYER3 = 1 << 2, + PED_FLAG_PLAYER4 = 1 << 3, + PED_FLAG_CIVMALE = 1 << 4, + PED_FLAG_CIVFEMALE = 1 << 5, + PED_FLAG_COP = 1 << 6, + PED_FLAG_GANG1 = 1 << 7, + PED_FLAG_GANG2 = 1 << 8, + PED_FLAG_GANG3 = 1 << 9, + PED_FLAG_GANG4 = 1 << 10, + PED_FLAG_GANG5 = 1 << 11, + PED_FLAG_GANG6 = 1 << 12, + PED_FLAG_GANG7 = 1 << 13, + PED_FLAG_GANG8 = 1 << 14, + PED_FLAG_GANG9 = 1 << 15, + PED_FLAG_EMERGENCY = 1 << 16, + PED_FLAG_PROSTITUTE = 1 << 17, + PED_FLAG_CRIMINAL = 1 << 18, + PED_FLAG_SPECIAL = 1 << 19, + PED_FLAG_GUN = 1 << 20, + PED_FLAG_COP_CAR = 1 << 21, + PED_FLAG_FAST_CAR = 1 << 22, + PED_FLAG_EXPLOSION = 1 << 23, + PED_FLAG_FIREMAN = 1 << 24, + PED_FLAG_DEADPEDS = 1 << 25, +}; + +class CPedType +{ + uint32 m_flag; + float unknown1; + float unknown2; + float unknown3; + float unknown4; + float unknown5; + uint32 m_threats; + uint32 m_avoid; + + static CPedType *ms_apPedType[NUM_PEDTYPES]; +public: + + static void Initialise(void); + static void Shutdown(void); + static void LoadPedData(void); + static ePedType FindPedType(char *type); + static uint32 FindPedFlag(char *type); + static void Save(uint8 *buf, uint32 *size); + static void Load(uint8 *buf, uint32 size); + + static uint32 GetFlag(int type) { return ms_apPedType[type]->m_flag; } + static uint32 GetAvoid(int type) { return ms_apPedType[type]->m_avoid; } + static uint32 GetThreats(int type) { return ms_apPedType[type]->m_threats; } + static void SetThreats(int type, uint32 threat) { ms_apPedType[type]->m_threats = threat; } + static void AddThreat(int type, int threat) { ms_apPedType[type]->m_threats |= threat; } + static void RemoveThreat(int type, int threat) { ms_apPedType[type]->m_threats &= ~threat; } + static bool IsThreat(int type, int threat) { return ms_apPedType[type]->m_threats & threat; } +}; + +VALIDATE_SIZE(CPedType, 0x20); + +enum ePedStats +{ + PEDSTAT_PLAYER, + PEDSTAT_COP, + PEDSTAT_MEDIC, + PEDSTAT_FIREMAN, + PEDSTAT_GANG1, + PEDSTAT_GANG2, + PEDSTAT_GANG3, + PEDSTAT_GANG4, + PEDSTAT_GANG5, + PEDSTAT_GANG6, + PEDSTAT_GANG7, + PEDSTAT_STREET_GUY, + PEDSTAT_SUIT_GUY, + PEDSTAT_SENSIBLE_GUY, + PEDSTAT_GEEK_GUY, + PEDSTAT_OLD_GUY, + PEDSTAT_TOUGH_GUY, + PEDSTAT_STREET_GIRL, + PEDSTAT_SUIT_GIRL, + PEDSTAT_SENSIBLE_GIRL, + PEDSTAT_GEEK_GIRL, + PEDSTAT_OLD_GIRL, + PEDSTAT_TOUGH_GIRL, + PEDSTAT_TRAMP_MALE, + PEDSTAT_TRAMP_FEMALE, + PEDSTAT_TOURIST, + PEDSTAT_PROSTITUTE, + PEDSTAT_CRIMINAL, + PEDSTAT_BUSKER, + PEDSTAT_TAXIDRIVER, + PEDSTAT_PSYCHO, + PEDSTAT_STEWARD, + PEDSTAT_SPORTSFAN, + PEDSTAT_SHOPPER, + PEDSTAT_OLDSHOPPER, + PEDSTAT_BEACH_GUY, + PEDSTAT_BEACH_GIRL, + PEDSTAT_SKATER, + PEDSTAT_STD_MISSION, + PEDSTAT_COWARD, + + NUM_PEDSTATS +}; + +// flags +enum +{ + STAT_PUNCH_ONLY = 1, + STAT_CAN_KNEE_HEAD = 2, + STAT_CAN_KICK = 4, + STAT_CAN_ROUNDHOUSE = 8, + STAT_NO_DIVE = 0x10, + STAT_ONE_HIT_KNOCKDOWN = 0x20, + STAT_SHOPPING_BAGS = 0x40, + STAT_GUN_PANIC = 0x80 +}; + +class CPedStats +{ +public: + ePedStats m_type; + char m_name[24]; + float m_fleeDistance; + float m_headingChangeRate; + int8 m_fear; + int8 m_temper; + int8 m_lawfulness; + int8 m_sexiness; + float m_attackStrength; + float m_defendWeakness; + int16 m_flags; + + static CPedStats *ms_apPedStats[NUM_PEDSTATS]; + + static void Initialise(void); + static void Shutdown(void); + static void LoadPedStats(void); + static ePedStats GetPedStatType(char *name); +}; + +VALIDATE_SIZE(CPedStats, 0x34); diff --git a/src/miami/peds/PlayerPed.cpp b/src/miami/peds/PlayerPed.cpp new file mode 100644 index 00000000..37618eda --- /dev/null +++ b/src/miami/peds/PlayerPed.cpp @@ -0,0 +1,2267 @@ +#include "common.h" + +#include "RwHelper.h" +#include "PlayerPed.h" +#include "Wanted.h" +#include "Fire.h" +#include "DMAudio.h" +#include "Pad.h" +#include "Camera.h" +#include "WeaponEffects.h" +#include "ModelIndices.h" +#include "World.h" +#include "RpAnimBlend.h" +#include "AnimBlendAssociation.h" +#include "General.h" +#include "Pools.h" +#include "Darkel.h" +#include "CarCtrl.h" +#include "MBlur.h" +#include "Streaming.h" +#include "Population.h" +#include "Script.h" +#include "Replay.h" +#include "PedPlacement.h" +#include "VarConsole.h" +#include "SaveBuf.h" + +#define PAD_MOVE_TO_GAME_WORLD_MOVE 60.0f + +bool CPlayerPed::bDontAllowWeaponChange; +#ifndef MASTER +bool CPlayerPed::bDebugPlayerInfo; +#endif + +const uint32 CPlayerPed::nSaveStructSize = +#ifdef COMPATIBLE_SAVES + 1752; +#else + sizeof(CPlayerPed); +#endif + +int32 idleAnimBlockIndex; + +CPad* +GetPadFromPlayer(CPlayerPed*) +{ + return CPad::GetPad(0); +} + +CPlayerPed::~CPlayerPed() +{ + delete m_pWanted; +} + +CPlayerPed::CPlayerPed(void) : CPed(PEDTYPE_PLAYER1) +{ + m_fMoveSpeed = 0.0f; + SetModelIndex(MI_PLAYER); +#ifdef FIX_BUGS + m_fCurrentStamina = m_fMaxStamina = 150.0f; +#endif + SetInitialState(); + + m_pWanted = new CWanted(); + m_pWanted->Initialise(); + m_pArrestingCop = nil; + m_currentWeapon = WEAPONTYPE_UNARMED; + m_nSelectedWepSlot = WEAPONSLOT_UNARMED; + m_nSpeedTimer = 0; + m_bSpeedTimerFlag = false; + SetWeaponLockOnTarget(nil); + SetPedState(PED_IDLE); +#ifndef FIX_BUGS + m_fCurrentStamina = m_fMaxStamina = 150.0f; +#endif + m_fStaminaProgress = 0.0f; + m_nEvadeAmount = 0; + m_pEvadingFrom = nil; + m_nHitAnimDelayTimer = 0; + m_fAttackButtonCounter = 0.0f; + m_bHaveTargetSelected = false; + m_bHasLockOnTarget = false; + m_bCanBeDamaged = true; + m_bNoPosForMeleeAttack = false; + m_fWalkAngle = 0.0f; + m_fFPSMoveHeading = 0.0f; + m_pMinigunTopAtomic = nil; + m_fGunSpinSpeed = 0.0; + m_fGunSpinAngle = 0.0; + m_nPadDownPressedInMilliseconds = 0; + m_nTargettableObjects[0] = m_nTargettableObjects[1] = m_nTargettableObjects[2] = m_nTargettableObjects[3] = -1; + unk1 = false; + for (int i = 0; i < 6; i++) { + m_vecSafePos[i] = CVector(0.0f, 0.0f, 0.0f); + m_pPedAtSafePos[i] = nil; + m_pMeleeList[i] = nil; + } + m_nAttackDirToCheck = 0; + m_nLastBusFareCollected = 0; + idleAnimBlockIndex = CAnimManager::GetAnimationBlockIndex("playidles"); +#ifdef FREE_CAM + m_bFreeAimActive = false; +#endif +} + +void +CPlayerPed::ClearWeaponTarget() +{ + if (m_nPedType == PEDTYPE_PLAYER1) { + SetWeaponLockOnTarget(nil); + TheCamera.ClearPlayerWeaponMode(); + CWeaponEffects::ClearCrossHair(); + } + ClearPointGunAt(); +} + +void +CPlayerPed::SetWantedLevel(int32 level) +{ + m_pWanted->SetWantedLevel(level); +} + +void +CPlayerPed::SetWantedLevelNoDrop(int32 level) +{ + m_pWanted->SetWantedLevelNoDrop(level); +} + +void +CPlayerPed::MakeObjectTargettable(int32 handle) +{ + for (int i = 0; i < ARRAY_SIZE(m_nTargettableObjects); i++) { + if (CPools::GetObjectPool()->GetAt(m_nTargettableObjects[i]) == nil) { + m_nTargettableObjects[i] = handle; + return; + } + } +} + +// I don't know the actual purpose of parameter +void +CPlayerPed::AnnoyPlayerPed(bool annoyedByPassingEntity) +{ + if (m_pedStats->m_temper < 52) { + m_pedStats->m_temper++; + } else if (annoyedByPassingEntity && m_pedStats->m_temper < 55) { + m_pedStats->m_temper++; + } else if (annoyedByPassingEntity) { + m_pedStats->m_temper = 46; + } +} + +void +CPlayerPed::ClearAdrenaline(void) +{ + if (m_bAdrenalineActive && m_nAdrenalineTime != 0) { + m_nAdrenalineTime = 0; + CTimer::SetTimeScale(1.0f); + } +} + +CPlayerInfo * +CPlayerPed::GetPlayerInfoForThisPlayerPed() +{ + if (CWorld::Players[0].m_pPed == this) + return &CWorld::Players[0]; + + return nil; +} + +void +CPlayerPed::SetupPlayerPed(int32 index) +{ + CPlayerPed *player = new CPlayerPed(); + CWorld::Players[index].m_pPed = player; +#ifdef FIX_BUGS + player->RegisterReference((CEntity**)&CWorld::Players[index].m_pPed); +#endif + + player->SetOrientation(0.0f, 0.0f, 0.0f); + + CWorld::Add(player); + player->m_wepAccuracy = 100; + +#ifndef MASTER + VarConsole.Add("Debug PlayerPed", &CPlayerPed::bDebugPlayerInfo, true); + VarConsole.Add("Tweak Vehicle Handling", &CVehicle::m_bDisplayHandlingInfo, true); +#endif +} + +void +CPlayerPed::DeactivatePlayerPed(int32 index) +{ + CWorld::Remove(CWorld::Players[index].m_pPed); +} + +void +CPlayerPed::ReactivatePlayerPed(int32 index) +{ + CWorld::Add(CWorld::Players[index].m_pPed); +} + +void +CPlayerPed::UseSprintEnergy(void) +{ + if (m_fCurrentStamina > -150.0f && !CWorld::Players[CWorld::PlayerInFocus].m_bInfiniteSprint + && !m_bAdrenalineActive) { + m_fCurrentStamina = m_fCurrentStamina - CTimer::GetTimeStep(); + m_fStaminaProgress = m_fStaminaProgress + CTimer::GetTimeStep(); + } + + if (m_fStaminaProgress >= 500.0f) { + m_fStaminaProgress = 0; + if (m_fMaxStamina < 1000.0f) + m_fMaxStamina += 10.0f; + } +} + +void +CPlayerPed::MakeChangesForNewWeapon(eWeaponType weapon) +{ + if (m_nPedState == PED_SNIPER_MODE) { + RestorePreviousState(); + TheCamera.ClearPlayerWeaponMode(); + } + SetCurrentWeapon(weapon); + m_nSelectedWepSlot = m_currentWeapon; + + GetWeapon()->m_nAmmoInClip = Min(GetWeapon()->m_nAmmoTotal, CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_nAmountofAmmunition); + + if (CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_CANAIM)) + ClearWeaponTarget(); + + // WEAPONTYPE_SNIPERRIFLE? Wut? + CAnimBlendAssociation* weaponAnim = RpAnimBlendClumpGetAssociation(GetClump(), GetPrimaryFireAnim(CWeaponInfo::GetWeaponInfo(WEAPONTYPE_SNIPERRIFLE))); + if (weaponAnim) { + weaponAnim->SetRun(); + weaponAnim->flags |= ASSOC_FADEOUTWHENDONE; + } + TheCamera.ClearPlayerWeaponMode(); +} + +void +CPlayerPed::MakeChangesForNewWeapon(int32 slot) +{ + if(slot != -1) + MakeChangesForNewWeapon(m_weapons[slot].m_eWeaponType); +} + +void +CPlayerPed::ReApplyMoveAnims(void) +{ + static AnimationId moveAnims[] = { ANIM_STD_WALK, ANIM_STD_RUN, ANIM_STD_RUNFAST, ANIM_STD_IDLE, ANIM_STD_STARTWALK }; + + for(int i = 0; i < ARRAY_SIZE(moveAnims); i++) { + CAnimBlendAssociation *curMoveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), moveAnims[i]); + if (curMoveAssoc) { + if (CGeneral::faststrcmp(CAnimManager::GetAnimAssociation(m_animGroup, moveAnims[i])->hierarchy->name, curMoveAssoc->hierarchy->name)) { + CAnimBlendAssociation *newMoveAssoc = CAnimManager::AddAnimation(GetClump(), m_animGroup, moveAnims[i]); + newMoveAssoc->blendDelta = curMoveAssoc->blendDelta; + newMoveAssoc->blendAmount = curMoveAssoc->blendAmount; + curMoveAssoc->blendDelta = -1000.0f; + curMoveAssoc->flags |= ASSOC_DELETEFADEDOUT; + } + } + } +} + +void +CPlayerPed::SetInitialState(void) +{ + m_nDrunkenness = 0; + m_nFadeDrunkenness = 0; + CMBlur::ClearDrunkBlur(); + m_nDrunkCountdown = 0; + m_bAdrenalineActive = false; + m_nAdrenalineTime = 0; + CTimer::SetTimeScale(1.0f); + m_pSeekTarget = nil; + m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f); + m_fleeFromPos = CVector2D(0.0f, 0.0f); + m_fleeFrom = nil; + m_fleeTimer = 0; + m_objective = OBJECTIVE_NONE; + m_prevObjective = OBJECTIVE_NONE; + bUsesCollision = true; + ClearAimFlag(); + ClearLookFlag(); + bIsPointingGunAt = false; + bRenderPedInCar = true; + if (m_pFire) + m_pFire->Extinguish(); + + RpAnimBlendClumpRemoveAllAssociations(GetClump()); + SetPedState(PED_IDLE); + SetMoveState(PEDMOVE_STILL); + m_nLastPedState = PED_NONE; + m_animGroup = ASSOCGRP_PLAYER; + m_fMoveSpeed = 0.0f; + m_nSelectedWepSlot = WEAPONSLOT_UNARMED; + m_nEvadeAmount = 0; + m_pEvadingFrom = nil; + bIsPedDieAnimPlaying = false; + SetRealMoveAnim(); + m_bCanBeDamaged = true; + m_pedStats->m_temper = 50; + m_fWalkAngle = 0.0f; + if (m_attachedTo && !bUsesCollision) + bUsesCollision = true; + + m_attachedTo = nil; + m_attachWepAmmo = 0; +} + +void +CPlayerPed::SetRealMoveAnim(void) +{ + CAnimBlendAssociation *curWalkAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_WALK); + CAnimBlendAssociation *curRunAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUN); + CAnimBlendAssociation *curSprintAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNFAST); + CAnimBlendAssociation *curWalkStartAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_STARTWALK); + CAnimBlendAssociation *curIdleAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE); + CAnimBlendAssociation *curRunStopAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNSTOP1); + CAnimBlendAssociation *curRunStopRAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNSTOP2); + if (bResetWalkAnims) { + if (curWalkAssoc) + curWalkAssoc->SetCurrentTime(0.0f); + if (curRunAssoc) + curRunAssoc->SetCurrentTime(0.0f); + if (curSprintAssoc) + curSprintAssoc->SetCurrentTime(0.0f); + bResetWalkAnims = false; + } + + if (!curIdleAssoc) + curIdleAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_TIRED); + if (!curIdleAssoc) + curIdleAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FIGHT_IDLE); + if (!curIdleAssoc) + curIdleAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_MELEE_IDLE_FIGHTMODE); + + if (!((curRunStopAssoc && curRunStopAssoc->IsRunning()) || (curRunStopRAssoc && curRunStopRAssoc->IsRunning()))) { + + if (curRunStopAssoc && curRunStopAssoc->blendDelta >= 0.0f || curRunStopRAssoc && curRunStopRAssoc->blendDelta >= 0.0f) { + if (curRunStopAssoc) { + curRunStopAssoc->flags |= ASSOC_DELETEFADEDOUT; + curRunStopAssoc->blendAmount = 1.0f; + curRunStopAssoc->blendDelta = -8.0f; + } else if (curRunStopRAssoc) { + curRunStopRAssoc->flags |= ASSOC_DELETEFADEDOUT; + curRunStopRAssoc->blendAmount = 1.0f; + curRunStopRAssoc->blendDelta = -8.0f; + } + + RestoreHeadingRate(); + if (!curIdleAssoc) { + if (m_fCurrentStamina < 0.0f && !bIsAimingGun && !CWorld::TestSphereAgainstWorld(GetPosition(), 0.5f, + nil, true, false, false, false, false, false)) { + curIdleAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE_TIRED, 8.0f); + + } else { + curIdleAssoc = CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 8.0f); + } + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(2500, 4000); + } + curIdleAssoc->blendAmount = 0.0f; + curIdleAssoc->blendDelta = 8.0f; + + } else if (m_fMoveSpeed == 0.0f && !curSprintAssoc) { + if (!curIdleAssoc) { + if (m_fCurrentStamina < 0.0f && !bIsAimingGun && !CWorld::TestSphereAgainstWorld(GetPosition(), 0.5f, + nil, true, false, false, false, false, false)) { + curIdleAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE_TIRED, 4.0f); + + } else { + curIdleAssoc = CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 4.0f); + } + + m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(2500, 4000); + } + + if ((m_fCurrentStamina > 0.0f || bIsAimingGun) && curIdleAssoc->animId == ANIM_STD_IDLE_TIRED) { + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 4.0f); + + } else if (m_nPedState != PED_FIGHT) { + if (m_fCurrentStamina < 0.0f && !bIsAimingGun && curIdleAssoc->animId != ANIM_STD_IDLE_TIRED + && !CWorld::TestSphereAgainstWorld(GetPosition(), 0.5f, nil, true, false, false, false, false, false)) { + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE_TIRED, 4.0f); + + } else if (curIdleAssoc->animId != ANIM_STD_IDLE) { + CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 4.0f); + } + } + m_nMoveState = PEDMOVE_STILL; + + } else { + if (curIdleAssoc) { + if (curWalkStartAssoc) { + curWalkStartAssoc->blendAmount = 1.0f; + curWalkStartAssoc->blendDelta = 0.0f; + } else { + curWalkStartAssoc = CAnimManager::AddAnimation(GetClump(), m_animGroup, ANIM_STD_STARTWALK); + } + if (curWalkAssoc) + curWalkAssoc->SetCurrentTime(0.0f); + if (curRunAssoc) + curRunAssoc->SetCurrentTime(0.0f); + + delete curIdleAssoc; + delete RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_TIRED); + CAnimBlendAssociation *fightIdleAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FIGHT_IDLE); + if (!fightIdleAnim) + fightIdleAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_MELEE_IDLE_FIGHTMODE); + delete fightIdleAnim; + delete curSprintAssoc; + + curSprintAssoc = nil; + m_nMoveState = PEDMOVE_WALK; + } + if (curRunStopAssoc) { + delete curRunStopAssoc; + RestoreHeadingRate(); + } + if (curRunStopRAssoc) { + delete curRunStopRAssoc; + RestoreHeadingRate(); + } + if (!curWalkAssoc) { + curWalkAssoc = CAnimManager::AddAnimation(GetClump(), m_animGroup, ANIM_STD_WALK); + curWalkAssoc->blendAmount = 0.0f; + } + if (!curRunAssoc) { + curRunAssoc = CAnimManager::AddAnimation(GetClump(), m_animGroup, ANIM_STD_RUN); + curRunAssoc->blendAmount = 0.0f; + } + if (curWalkStartAssoc && !(curWalkStartAssoc->IsRunning())) { + delete curWalkStartAssoc; + curWalkStartAssoc = nil; + curWalkAssoc->SetRun(); + curRunAssoc->SetRun(); + } + if (m_nMoveState == PEDMOVE_SPRINT) { + if (m_fCurrentStamina < 0.0f && (m_fCurrentStamina <= -150.0f || !curSprintAssoc || curSprintAssoc->blendDelta < 0.0f)) + m_nMoveState = PEDMOVE_STILL; + + if (curWalkStartAssoc) + m_nMoveState = PEDMOVE_STILL; + } + + if (curSprintAssoc && (m_nMoveState != PEDMOVE_SPRINT || m_fMoveSpeed < 0.4f)) { + // Stop sprinting in various conditions + if (curSprintAssoc->blendAmount == 0.0f) { + curSprintAssoc->blendDelta = -1000.0f; + curSprintAssoc->flags |= ASSOC_DELETEFADEDOUT; + + } else if (curSprintAssoc->blendDelta >= 0.0f || curSprintAssoc->blendAmount >= 0.8f) { + if (m_fMoveSpeed < 0.4f) { + AnimationId runStopAnim; + if (curSprintAssoc->GetProgress() < 0.5) // double + runStopAnim = ANIM_STD_RUNSTOP1; + else + runStopAnim = ANIM_STD_RUNSTOP2; + CAnimBlendAssociation* newRunStopAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, runStopAnim); + newRunStopAssoc->blendAmount = 1.0f; + newRunStopAssoc->SetDeleteCallback(RestoreHeadingRateCB, this); + m_headingRate = 0.0f; + curSprintAssoc->flags |= ASSOC_DELETEFADEDOUT; + curSprintAssoc->blendDelta = -1000.0f; + curWalkAssoc->flags &= ~ASSOC_RUNNING; + curWalkAssoc->blendAmount = 0.0f; + curWalkAssoc->blendDelta = 0.0f; + curRunAssoc->flags &= ~ASSOC_RUNNING; + curRunAssoc->blendAmount = 0.0f; + curRunAssoc->blendDelta = 0.0f; + + } else if (curSprintAssoc->blendDelta >= 0.0f) { // this condition is absent on mobile + // Stop sprinting when tired + curSprintAssoc->flags |= ASSOC_DELETEFADEDOUT; + curSprintAssoc->blendDelta = -1.0f; + curRunAssoc->blendDelta = 1.0f; + } + } else if (m_fMoveSpeed < 1.0f) { + curSprintAssoc->blendDelta = -8.0f; + curRunAssoc->blendDelta = 8.0f; + } + + } else if (curWalkStartAssoc) { + // Walk start and walk/run shouldn't run at the same time + curWalkAssoc->flags &= ~ASSOC_RUNNING; + curRunAssoc->flags &= ~ASSOC_RUNNING; + curWalkAssoc->blendAmount = 0.0f; + curRunAssoc->blendAmount = 0.0f; + + } else if (m_nMoveState == PEDMOVE_SPRINT) { + if (curSprintAssoc) { + // We have anim, do it + if (curSprintAssoc->blendDelta < 0.0f) { + curSprintAssoc->blendDelta = 2.0f; + curRunAssoc->blendDelta = -2.0f; + } + } else { + // Transition between run-sprint + curWalkAssoc->blendAmount = 0.0f; + curRunAssoc->blendAmount = 1.0f; + curSprintAssoc = CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_RUNFAST, 2.0f); + } + UseSprintEnergy(); + } else { + if (m_fMoveSpeed < 1.0f) { + curWalkAssoc->blendAmount = 1.0f; + curRunAssoc->blendAmount = 0.0f; + m_nMoveState = PEDMOVE_WALK; + } else if (m_fMoveSpeed < 2.0f) { + curWalkAssoc->blendAmount = 2.0f - m_fMoveSpeed; + curRunAssoc->blendAmount = m_fMoveSpeed - 1.0f; + m_nMoveState = PEDMOVE_RUN; + } else { + curWalkAssoc->blendAmount = 0.0f; + curRunAssoc->blendAmount = 1.0f; + m_nMoveState = PEDMOVE_RUN; + } + curWalkAssoc->blendDelta = 0.0f; + curRunAssoc->blendDelta = 0.0f; + } + } + } + if (m_bAdrenalineActive) { + if (CTimer::GetTimeInMilliseconds() > m_nAdrenalineTime) { + m_bAdrenalineActive = false; + CTimer::SetTimeScale(1.0f); + if (curWalkStartAssoc) + curWalkStartAssoc->speed = 1.0f; + if (curWalkAssoc) + curWalkAssoc->speed = 1.0f; + if (curRunAssoc) + curRunAssoc->speed = 1.0f; + if (curSprintAssoc) + curSprintAssoc->speed = 1.0f; + } else { + CTimer::SetTimeScale(1.0f / 3); + if (curWalkStartAssoc) + curWalkStartAssoc->speed = 2.0f; + if (curWalkAssoc) + curWalkAssoc->speed = 2.0f; + if (curRunAssoc) + curRunAssoc->speed = 2.0f; + if (curSprintAssoc) + curSprintAssoc->speed = 2.0f; + } + } else if (curSprintAssoc) { + if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_FIXED) { + curSprintAssoc->speed = 0.7f; + } else + curSprintAssoc->speed = 1.0f; + } +} + +void +CPlayerPed::RestoreSprintEnergy(float restoreSpeed) +{ + if (m_fCurrentStamina < m_fMaxStamina) + m_fCurrentStamina += restoreSpeed * CTimer::GetTimeStep() * 0.5f; +} + +float +CPlayerPed::DoWeaponSmoothSpray(void) +{ + if (m_nPedState == PED_ATTACK && !m_pPointGunAt) { + CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + switch (GetWeapon()->m_eWeaponType) { + case WEAPONTYPE_GOLFCLUB: + case WEAPONTYPE_NIGHTSTICK: + case WEAPONTYPE_BASEBALLBAT: + if (GetFireAnimGround(weaponInfo, false) && RpAnimBlendClumpGetAssociation(GetClump(), GetFireAnimGround(weaponInfo, false))) + return PI / 176.f; + else + return -1.0f; + + case WEAPONTYPE_CHAINSAW: + if (GetMeleeStartAnim(weaponInfo) && RpAnimBlendClumpGetAssociation(GetClump(), GetMeleeStartAnim(weaponInfo))) { +#ifdef FREE_CAM + if (TheCamera.Cams[0].Using3rdPersonMouseCam()) return -1.0f; +#endif + return PI / 128.0f; + } + else if (GetFireAnimGround(weaponInfo, false) && RpAnimBlendClumpGetAssociation(GetClump(), GetFireAnimGround(weaponInfo, false))) + return PI / 176.f; + else + return PI / 80.f; + + case WEAPONTYPE_PYTHON: + return PI / 112.f; + case WEAPONTYPE_SHOTGUN: + case WEAPONTYPE_SPAS12_SHOTGUN: + case WEAPONTYPE_STUBBY_SHOTGUN: + return PI / 112.f; + case WEAPONTYPE_UZI: + case WEAPONTYPE_MP5: + return PI / 112.f; + case WEAPONTYPE_M4: + case WEAPONTYPE_RUGER: + return PI / 112.f; + case WEAPONTYPE_FLAMETHROWER: + return PI / 80.f; + case WEAPONTYPE_M60: + case WEAPONTYPE_MINIGUN: + case WEAPONTYPE_HELICANNON: + return PI / 176.f; + default: + return -1.0f; + } + } else if (bIsDucking) + return PI / 112.f; + else + return -1.0f; +} + +void +CPlayerPed::DoStuffToGoOnFire(void) +{ + if (m_nPedState == PED_SNIPER_MODE) + TheCamera.ClearPlayerWeaponMode(); +} + +bool +CPlayerPed::DoesTargetHaveToBeBroken(CVector target, CWeapon *weaponUsed) +{ + CVector distVec = target - GetPosition(); + + if (distVec.Magnitude() > CWeaponInfo::GetWeaponInfo(weaponUsed->m_eWeaponType)->m_fRange) + return true; + + return false; +} + +// Cancels landing anim while running & jumping? I think +void +CPlayerPed::RunningLand(CPad *padUsed) +{ + CAnimBlendAssociation *landAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL_LAND); + if (landAssoc && landAssoc->currentTime == 0.0f && m_fMoveSpeed > 1.5f + && padUsed && (padUsed->GetPedWalkLeftRight() != 0.0f || padUsed->GetPedWalkUpDown() != 0.0f)) { + + landAssoc->blendDelta = -1000.0f; + landAssoc->flags |= ASSOC_DELETEFADEDOUT; + + CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_JUMP_LAND)->SetFinishCallback(FinishJumpCB, this); + + if (m_nPedState == PED_JUMP) + RestorePreviousState(); + } +} + +bool +CPlayerPed::IsThisPedAnAimingPriority(CPed *suspect) +{ + if (!suspect->bIsPlayerFriend) + return true; + + if (suspect->m_pPointGunAt == this) + return true; + + switch (suspect->m_objective) { + case OBJECTIVE_KILL_CHAR_ON_FOOT: + case OBJECTIVE_KILL_CHAR_ANY_MEANS: + if (suspect->m_pedInObjective == this) + return true; + + break; + default: + break; + } + return suspect->m_nPedState == PED_ABSEIL; +} + +void +CPlayerPed::PlayerControlSniper(CPad *padUsed) +{ + ProcessWeaponSwitch(padUsed); + TheCamera.PlayerExhaustion = (1.0f - (m_fCurrentStamina - -150.0f) / 300.0f) * 0.9f + 0.1f; + + if (padUsed->DuckJustDown() && !bIsDucking && m_nMoveState != PEDMOVE_SPRINT) { + bCrouchWhenShooting = true; + SetDuck(60000, true); + } else if (bIsDucking && (padUsed->DuckJustDown() || m_nMoveState == PEDMOVE_SPRINT)) { + ClearDuck(true); + bCrouchWhenShooting = false; + } + + if (!padUsed->GetTarget() && !m_attachedTo) { + RestorePreviousState(); + TheCamera.ClearPlayerWeaponMode(); + return; + } + + int firingRate = GetWeapon()->m_eWeaponType == WEAPONTYPE_LASERSCOPE ? 333 : 266; + if (padUsed->WeaponJustDown() && CTimer::GetTimeInMilliseconds() > GetWeapon()->m_nTimer) { + CVector firePos(0.0f, 0.0f, 0.6f); + firePos = GetMatrix() * firePos; + GetWeapon()->Fire(this, &firePos); + m_nPadDownPressedInMilliseconds = CTimer::GetTimeInMilliseconds(); + } else if (CTimer::GetTimeInMilliseconds() > m_nPadDownPressedInMilliseconds + firingRate && + CTimer::GetTimeInMilliseconds() - CTimer::GetTimeStepInMilliseconds() < m_nPadDownPressedInMilliseconds + firingRate && padUsed->GetWeapon()) { + + if (GetWeapon()->m_nAmmoTotal > 0) { + DMAudio.PlayFrontEndSound(SOUND_WEAPON_AK47_BULLET_ECHO, GetWeapon()->m_eWeaponType); + } + } + GetWeapon()->Update(m_audioEntityId, nil); +} + +// I think R* also used goto in here. +void +CPlayerPed::ProcessWeaponSwitch(CPad *padUsed) +{ + if (CDarkel::FrenzyOnGoing() || m_attachedTo) + goto switchDetectDone; + + if (!m_pPointGunAt && !bDontAllowWeaponChange && GetWeapon()->m_eWeaponType != WEAPONTYPE_DETONATOR) { + if (padUsed->CycleWeaponRightJustDown()) { + + if (TheCamera.PlayerWeaponMode.Mode != CCam::MODE_M16_1STPERSON + && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_M16_1STPERSON_RUNABOUT + && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_SNIPER + && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_SNIPER_RUNABOUT + && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_ROCKETLAUNCHER + && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_ROCKETLAUNCHER_RUNABOUT + && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_CAMERA) { + + for (m_nSelectedWepSlot = m_currentWeapon + 1; m_nSelectedWepSlot < TOTAL_WEAPON_SLOTS; ++m_nSelectedWepSlot) { + if (HasWeaponSlot(m_nSelectedWepSlot) && GetWeapon(m_nSelectedWepSlot).HasWeaponAmmoToBeUsed()) { +#ifdef FIX_BUGS + goto switchDetectDone; +#else + goto spentAmmoCheck; +#endif + } + } + m_nSelectedWepSlot = 0; +#ifdef FIX_BUGS + goto switchDetectDone; +#endif + } + } else if (padUsed->CycleWeaponLeftJustDown()) { + if (TheCamera.PlayerWeaponMode.Mode != CCam::MODE_M16_1STPERSON + && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_SNIPER + && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_ROCKETLAUNCHER + && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_CAMERA) { + + // I don't know what kind of loop that was + m_nSelectedWepSlot = m_currentWeapon - 1; + do { + if (m_nSelectedWepSlot < 0) + m_nSelectedWepSlot = TOTAL_WEAPON_SLOTS - 1; + + if (m_nSelectedWepSlot == WEAPONSLOT_UNARMED) + break; + + if (HasWeaponSlot(m_nSelectedWepSlot) && GetWeapon(m_nSelectedWepSlot).HasWeaponAmmoToBeUsed()) + break; + + --m_nSelectedWepSlot; + } while (m_nSelectedWepSlot != WEAPONSLOT_UNARMED); +#ifdef FIX_BUGS + goto switchDetectDone; +#endif + + } + } + } + +spentAmmoCheck: + if (CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_eWeaponFire != WEAPON_FIRE_MELEE + && (!padUsed->GetWeapon() || GetWeapon()->m_eWeaponType != WEAPONTYPE_MINIGUN)) { + if (GetWeapon()->m_nAmmoTotal <= 0) { + if (TheCamera.PlayerWeaponMode.Mode == CCam::MODE_M16_1STPERSON + || TheCamera.PlayerWeaponMode.Mode == CCam::MODE_SNIPER + || TheCamera.PlayerWeaponMode.Mode == CCam::MODE_ROCKETLAUNCHER) + return; + + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_DETONATOR + && GetWeapon(WEAPONSLOT_PROJECTILE).m_eWeaponType == WEAPONTYPE_DETONATOR_GRENADE) + m_nSelectedWepSlot = WEAPONSLOT_PROJECTILE; + else + m_nSelectedWepSlot = m_currentWeapon - 1; + + for (; m_nSelectedWepSlot >= WEAPONSLOT_UNARMED; --m_nSelectedWepSlot) { + + // BUG: m_nSelectedWepSlot and GetWeapon(..) takes slot in VC but they compared them against weapon types in whole condition! jeez +#ifdef FIX_BUGS + if (m_nSelectedWepSlot == WEAPONSLOT_MELEE || + GetWeapon(m_nSelectedWepSlot).m_nAmmoTotal > 0 && (m_nSelectedWepSlot != WEAPONSLOT_PROJECTILE || GetWeapon(WEAPONSLOT_PROJECTILE).m_eWeaponType == WEAPONTYPE_DETONATOR_GRENADE)) { +#else + if (m_nSelectedWepSlot == WEAPONTYPE_BASEBALLBAT && GetWeapon(WEAPONTYPE_BASEBALLBAT).m_eWeaponType == WEAPONTYPE_BASEBALLBAT + || GetWeapon(m_nSelectedWepSlot).m_nAmmoTotal > 0 + && m_nSelectedWepSlot != WEAPONTYPE_MOLOTOV && m_nSelectedWepSlot != WEAPONTYPE_GRENADE && m_nSelectedWepSlot != WEAPONTYPE_TEARGAS) { +#endif + goto switchDetectDone; + } + } + m_nSelectedWepSlot = WEAPONSLOT_UNARMED; + } + } + +switchDetectDone: + if (m_nSelectedWepSlot != m_currentWeapon) { + if (m_nPedState != PED_ATTACK && m_nPedState != PED_AIM_GUN && m_nPedState != PED_FIGHT) { + RemoveWeaponAnims(m_currentWeapon, -1000.0f); + MakeChangesForNewWeapon(m_nSelectedWepSlot); + } + } +} + +void +CPlayerPed::PlayerControlM16(CPad *padUsed) +{ + ProcessWeaponSwitch(padUsed); + TheCamera.PlayerExhaustion = (1.0f - (m_fCurrentStamina - -150.0f) / 300.0f) * 0.9f + 0.1f; + + if (padUsed->DuckJustDown() && !bIsDucking && m_nMoveState != PEDMOVE_SPRINT) { + bCrouchWhenShooting = true; + SetDuck(60000, true); + } else if (bIsDucking && (padUsed->DuckJustDown() || m_nMoveState == PEDMOVE_SPRINT)) { + ClearDuck(true); + bCrouchWhenShooting = false; + } + + if (!padUsed->GetTarget() && !m_attachedTo) { + RestorePreviousState(); + TheCamera.ClearPlayerWeaponMode(); + } + + if (padUsed->GetWeapon() && CTimer::GetTimeInMilliseconds() > GetWeapon()->m_nTimer) { + if (GetWeapon()->m_eWeaponState == WEAPONSTATE_OUT_OF_AMMO) { + DMAudio.PlayFrontEndSound(SOUND_WEAPON_SNIPER_SHOT_NO_ZOOM, 0.f); + GetWeapon()->m_nTimer = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_nFiringRate + CTimer::GetTimeInMilliseconds(); + } else { + CVector firePos(0.0f, 0.0f, 0.6f); + firePos = GetMatrix() * firePos; + GetWeapon()->Fire(this, &firePos); + m_nPadDownPressedInMilliseconds = CTimer::GetTimeInMilliseconds(); + } + } else if (CTimer::GetTimeInMilliseconds() > GetWeapon()->m_nTimer && + CTimer::GetTimeInMilliseconds() - CTimer::GetTimeStepInMilliseconds() < GetWeapon()->m_nTimer && GetWeapon()->m_eWeaponState != WEAPONSTATE_OUT_OF_AMMO) { + DMAudio.PlayFrontEndSound(SOUND_WEAPON_AK47_BULLET_ECHO, GetWeapon()->m_eWeaponType); + } + GetWeapon()->Update(m_audioEntityId, nil); +} + +void +CPlayerPed::PlayerControlFighter(CPad *padUsed) +{ + float leftRight = padUsed->GetPedWalkLeftRight(); + float upDown = padUsed->GetPedWalkUpDown(); + float padMove = CVector2D(leftRight, upDown).Magnitude(); + + if (padMove > 0.0f) { + m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(0.0f, 0.0f, -leftRight, upDown) - TheCamera.Orientation; + m_takeAStepAfterAttack = padMove > (2 * PAD_MOVE_TO_GAME_WORLD_MOVE); + if (padUsed->GetSprint() && padMove > (1 * PAD_MOVE_TO_GAME_WORLD_MOVE)) + bIsAttacking = false; + } + + if (!CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_HEAVY) && padUsed->JumpJustDown()) { + if (m_nEvadeAmount != 0 && m_pEvadingFrom) { + SetEvasiveDive((CPhysical*)m_pEvadingFrom, 1); + m_nEvadeAmount = 0; + m_pEvadingFrom = nil; + } else { + SetJump(); + } + } +} + +void +CPlayerPed::PlayerControl1stPersonRunAround(CPad *padUsed) +{ + float leftRight = padUsed->GetPedWalkLeftRight(); + float upDown = padUsed->GetPedWalkUpDown(); + float padMove = CVector2D(leftRight, upDown).Magnitude(); + float padMoveInGameUnit = padMove / PAD_MOVE_TO_GAME_WORLD_MOVE; + if (padMoveInGameUnit > 0.0f) { + m_fRotationDest = CGeneral::LimitRadianAngle(TheCamera.Orientation); + m_fMoveSpeed = Min(padMoveInGameUnit, 0.07f * CTimer::GetTimeStep() + m_fMoveSpeed); + } else { + m_fMoveSpeed = 0.0f; + } + + if (m_nPedState == PED_JUMP) { + if (bIsInTheAir) { + if (bUsesCollision && !bHitSteepSlope && (!bHitSomethingLastFrame || m_vecDamageNormal.z > 0.6f) + && m_fDistanceTravelled < CTimer::GetTimeStepInSeconds() && m_vecMoveSpeed.MagnitudeSqr() < 0.01f) { + + float angleSin = Sin(m_fRotationCur); // originally sin(DEGTORAD(RADTODEG(m_fRotationCur))) o_O + float angleCos = Cos(m_fRotationCur); + ApplyMoveForce(-angleSin * 3.0f, 3.0f * angleCos, 0.05f); + } + } else if (bIsLanding) { + m_fMoveSpeed = 0.0f; + } + } + + if (m_nPedState == PED_ANSWER_MOBILE) { + SetRealMoveAnim(); + return; + } + + if (!CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_HEAVY) && padUsed->GetSprint()) { + m_nMoveState = PEDMOVE_SPRINT; + } + if (m_nPedState != PED_FIGHT) + SetRealMoveAnim(); + + if (!bIsInTheAir && !(CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_HEAVY)) && + padUsed->JumpJustDown() && m_nPedState != PED_JUMP) { + + ClearAttack(); + ClearWeaponTarget(); + if (m_nEvadeAmount != 0 && m_pEvadingFrom) { + SetEvasiveDive((CPhysical*)m_pEvadingFrom, 1); + m_nEvadeAmount = 0; + m_pEvadingFrom = nil; + } else { + SetJump(); + } + } + + // FIX: Fact that PlayIdleAnimations only called through PlayerControlZelda was making it visible to only Classic control players. This isn't fair! +#ifdef FIX_BUGS + if (m_nPedState != PED_FIGHT) + PlayIdleAnimations(padUsed); +#endif +} + +void +CPlayerPed::KeepAreaAroundPlayerClear(void) +{ + BuildPedLists(); + for (int i = 0; i < m_numNearPeds; ++i) { + CPed *nearPed = m_nearPeds[i]; + if (nearPed->CharCreatedBy == RANDOM_CHAR && nearPed->m_nPedState != PED_DRIVING && !nearPed->DyingOrDead()) { + if (nearPed->GetIsOnScreen()) { + if (nearPed->m_objective == OBJECTIVE_NONE) { + nearPed->SetFindPathAndFlee(this, 5000, true); + } else { + if (nearPed->EnteringCar()) + nearPed->QuitEnteringCar(); + + nearPed->ClearObjective(); + } + } else { + nearPed->FlagToDestroyWhenNextProcessed(); + } + } + } + CVector playerPos = (InVehicle() ? m_pMyVehicle->GetPosition() : GetPosition()); + + CVector pos = GetPosition(); + int16 lastVehicle; + CEntity *vehicles[8]; + CWorld::FindObjectsInRange(pos, CHECK_NEARBY_THINGS_MAX_DIST, true, &lastVehicle, 6, vehicles, false, true, false, false, false); + + for (int i = 0; i < lastVehicle; i++) { + CVehicle *veh = (CVehicle*)vehicles[i]; + if (veh->VehicleCreatedBy != MISSION_VEHICLE) { + if (veh->GetStatus() != STATUS_PLAYER && veh->GetStatus() != STATUS_PLAYER_DISABLED) { + if ((veh->GetPosition() - playerPos).MagnitudeSqr() > 25.0f) { + veh->AutoPilot.m_nTempAction = TEMPACT_WAIT; + veh->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 5000; + } else { + if (DotProduct2D(playerPos - veh->GetPosition(), veh->GetForward()) > 0.0f) + veh->AutoPilot.m_nTempAction = TEMPACT_REVERSE; + else + veh->AutoPilot.m_nTempAction = TEMPACT_GOFORWARD; + + veh->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 2000; + } + CCarCtrl::PossiblyRemoveVehicle(veh); + } + } + } +} + +void +CPlayerPed::EvaluateNeighbouringTarget(CEntity *candidate, CEntity **targetPtr, float *lastCloseness, float distLimit, float angleOffset, bool lookToLeft, bool priority) +{ + // priority param is unused + CVector distVec = candidate->GetPosition() - GetPosition(); + if (distVec.Magnitude2D() <= distLimit) { + if (!DoesTargetHaveToBeBroken(candidate->GetPosition(), GetWeapon())) { + float angleBetweenUs = CGeneral::GetATanOfXY(candidate->GetPosition().x - TheCamera.GetPosition().x, + candidate->GetPosition().y - TheCamera.GetPosition().y); + + angleBetweenUs = CGeneral::LimitAngle(angleBetweenUs - angleOffset); + float closeness; + if (lookToLeft) { + closeness = angleBetweenUs > 0.0f ? -Abs(angleBetweenUs) : -100000.0f; + } else { + closeness = angleBetweenUs > 0.0f ? -100000.0f : -Abs(angleBetweenUs); + } + + if (closeness > *lastCloseness) { + *targetPtr = candidate; + *lastCloseness = closeness; + } + } + } +} + +void +CPlayerPed::EvaluateTarget(CEntity *candidate, CEntity **targetPtr, float *lastCloseness, float distLimit, float angleOffset, bool priority) +{ + CVector distVec = candidate->GetPosition() - GetPosition(); + float dist = distVec.Magnitude2D(); + if (dist <= distLimit) { + if (!DoesTargetHaveToBeBroken(candidate->GetPosition(), GetWeapon())) { + float angleBetweenUs = CGeneral::GetATanOfXY(distVec.x, distVec.y); + angleBetweenUs = CGeneral::LimitAngle(angleBetweenUs - angleOffset); + + float closeness = -dist - 5.0f * Abs(angleBetweenUs); + if (priority) { + closeness += 30.0f; + } + + if (closeness > *lastCloseness) { + *targetPtr = candidate; + *lastCloseness = closeness; + } + } + } +} + +bool +CPlayerPed::CanIKReachThisTarget(CVector target, CWeapon* weapon, bool zRotImportant) +{ + float angleToFace = CGeneral::GetRadianAngleBetweenPoints(target.x, target.y, GetPosition().x, GetPosition().y); + float angleDiff = CGeneral::LimitRadianAngle(angleToFace - m_fRotationCur); + + return (!zRotImportant || CWeaponInfo::GetWeaponInfo(weapon->m_eWeaponType)->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM) || Abs(angleDiff) <= HALFPI) && + (CWeaponInfo::GetWeaponInfo(weapon->m_eWeaponType)->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM) || Abs(target.z - GetPosition().z) <= (target - GetPosition()).Magnitude2D()); +} + +void +CPlayerPed::RotatePlayerToTrackTarget(void) +{ + if (CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM)) + return; + + float angleToFace = CGeneral::GetRadianAngleBetweenPoints( + m_pPointGunAt->GetPosition().x, m_pPointGunAt->GetPosition().y, + GetPosition().x, GetPosition().y); + + float angleDiff = CGeneral::LimitRadianAngle(m_fRotationCur - angleToFace); + if (angleDiff < -DEGTORAD(25.0f)) { + m_fRotationCur -= angleDiff + DEGTORAD(25.0f); + m_fRotationDest -= angleDiff + DEGTORAD(25.0f); + + } else if (angleDiff > DEGTORAD(25.0f)) { + m_fRotationCur -= angleDiff - DEGTORAD(25.0f); + m_fRotationDest -= angleDiff - DEGTORAD(25.0f); + } +} + +bool +CPlayerPed::FindNextWeaponLockOnTarget(CEntity *previousTarget, bool lookToLeft) +{ + CEntity *nextTarget = nil; + float weaponRange = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_fRange; + // nextTarget = nil; // duplicate + float lastCloseness = -10000.0f; + // CGeneral::GetATanOfXY(GetForward().x, GetForward().y); // unused + CVector distVec = previousTarget->GetPosition() - TheCamera.GetPosition(); + float referenceBeta = CGeneral::GetATanOfXY(distVec.x, distVec.y); + + for (int h = CPools::GetPedPool()->GetSize() - 1; h >= 0; h--) { + CPed *pedToCheck = CPools::GetPedPool()->GetSlot(h); + if (pedToCheck) { + if (pedToCheck != this && pedToCheck != previousTarget) { + if (!pedToCheck->DyingOrDead() +#ifndef AIMING_VEHICLE_OCCUPANTS // Mobile thing + && (!pedToCheck->bInVehicle || (pedToCheck->m_pMyVehicle && pedToCheck->m_pMyVehicle->IsBike())) +#endif + && pedToCheck->m_leader != this && !pedToCheck->bNeverEverTargetThisPed + && OurPedCanSeeThisOne(pedToCheck, true) && CanIKReachThisTarget(pedToCheck->GetPosition(), GetWeapon(), true)) { + + EvaluateNeighbouringTarget(pedToCheck, &nextTarget, &lastCloseness, + weaponRange, referenceBeta, lookToLeft, IsThisPedAnAimingPriority(pedToCheck)); + } + } + } + } + for (int i = 0; i < ARRAY_SIZE(m_nTargettableObjects); i++) { + CObject *obj = CPools::GetObjectPool()->GetAt(m_nTargettableObjects[i]); + if (obj && !obj->bHasBeenDamaged && CanIKReachThisTarget(obj->GetPosition(), GetWeapon(), true)) + EvaluateNeighbouringTarget(obj, &nextTarget, &lastCloseness, weaponRange, referenceBeta, lookToLeft, true); + } + if (!nextTarget) + return false; + + SetWeaponLockOnTarget(nextTarget); + bDontAllowWeaponChange = true; + SetPointGunAt(nextTarget); + return true; +} + +bool +CPlayerPed::FindWeaponLockOnTarget(void) +{ + CEntity *nextTarget = nil; + float weaponRange = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_fRange; + + if (m_pPointGunAt) { + CVector distVec = m_pPointGunAt->GetPosition() - GetPosition(); + if (distVec.Magnitude2D() > weaponRange) { + SetWeaponLockOnTarget(nil); + return false; + } else { + return true; + } + } + + // nextTarget = nil; // duplicate + float lastCloseness = -10000.0f; + float referenceBeta = CGeneral::GetATanOfXY(GetForward().x, GetForward().y); + for (int h = CPools::GetPedPool()->GetSize() - 1; h >= 0; h--) { + CPed *pedToCheck = CPools::GetPedPool()->GetSlot(h); + if (pedToCheck) { + if (pedToCheck != this) { + if (!pedToCheck->DyingOrDead() +#ifndef AIMING_VEHICLE_OCCUPANTS // Mobile thing + && (!pedToCheck->bInVehicle || (pedToCheck->m_pMyVehicle && pedToCheck->m_pMyVehicle->IsBike())) +#endif + && pedToCheck->m_leader != this && !pedToCheck->bNeverEverTargetThisPed + && OurPedCanSeeThisOne(pedToCheck) && CanIKReachThisTarget(pedToCheck->GetPosition(), GetWeapon(), true)) { + + EvaluateTarget(pedToCheck, &nextTarget, &lastCloseness, + weaponRange, referenceBeta, IsThisPedAnAimingPriority(pedToCheck)); + } + } + } + } + for (int i = 0; i < ARRAY_SIZE(m_nTargettableObjects); i++) { + CObject *obj = CPools::GetObjectPool()->GetAt(m_nTargettableObjects[i]); + if (obj && !obj->bHasBeenDamaged && CanIKReachThisTarget(obj->GetPosition(), GetWeapon(), true)) + EvaluateTarget(obj, &nextTarget, &lastCloseness, weaponRange, referenceBeta, true); + } + if (!nextTarget) + return false; + + SetWeaponLockOnTarget(nextTarget); + bDontAllowWeaponChange = true; + SetPointGunAt(nextTarget); + Say(SOUND_PED_AIMING); + return true; +} + +void +CPlayerPed::ProcessAnimGroups(void) +{ + AssocGroupId groupToSet; +#ifdef PC_PLAYER_CONTROLS + if ((m_fWalkAngle <= -DEGTORAD(50.0f) || m_fWalkAngle >= DEGTORAD(50.0f)) + && TheCamera.Cams[TheCamera.ActiveCam].Using3rdPersonMouseCam() + && CanStrafeOrMouseControl()) { + + if (m_fWalkAngle >= -DEGTORAD(130.0f) && m_fWalkAngle <= DEGTORAD(130.0f)) { + if (m_fWalkAngle > 0.0f) { + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_ROCKETLAUNCHER) + groupToSet = ASSOCGRP_ROCKETLEFT; + else if (GetWeapon()->m_eWeaponType == WEAPONTYPE_CHAINSAW || + GetWeapon()->m_eWeaponType == WEAPONTYPE_FLAMETHROWER || + GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN) + groupToSet = ASSOCGRP_CHAINSAWLEFT; + else + groupToSet = ASSOCGRP_PLAYERLEFT; + } else { + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_ROCKETLAUNCHER) + groupToSet = ASSOCGRP_ROCKETRIGHT; + else if (GetWeapon()->m_eWeaponType == WEAPONTYPE_CHAINSAW || + GetWeapon()->m_eWeaponType == WEAPONTYPE_FLAMETHROWER || + GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN) + groupToSet = ASSOCGRP_CHAINSAWRIGHT; + else + groupToSet = ASSOCGRP_PLAYERRIGHT; + } + } else { + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_ROCKETLAUNCHER) + groupToSet = ASSOCGRP_ROCKETBACK; + else if (GetWeapon()->m_eWeaponType == WEAPONTYPE_CHAINSAW || + GetWeapon()->m_eWeaponType == WEAPONTYPE_FLAMETHROWER || + GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN) + groupToSet = ASSOCGRP_CHAINSAWBACK; + else + groupToSet = ASSOCGRP_PLAYERBACK; + } + } else +#endif + { + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_ROCKETLAUNCHER) { + groupToSet = ASSOCGRP_PLAYERROCKET; + } else { + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT + || GetWeapon()->m_eWeaponType == WEAPONTYPE_MACHETE) + groupToSet = ASSOCGRP_PLAYERBBBAT; + else if (GetWeapon()->m_eWeaponType == WEAPONTYPE_CHAINSAW || + GetWeapon()->m_eWeaponType == WEAPONTYPE_FLAMETHROWER || + GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN) + groupToSet = ASSOCGRP_PLAYERCHAINSAW; + else if (GetWeapon()->m_eWeaponType != WEAPONTYPE_COLT45 && GetWeapon()->m_eWeaponType != WEAPONTYPE_UZI + // I hope this is an inlined function... + && GetWeapon()->m_eWeaponType != WEAPONTYPE_PYTHON && GetWeapon()->m_eWeaponType != WEAPONTYPE_TEC9 + && GetWeapon()->m_eWeaponType != WEAPONTYPE_SILENCED_INGRAM && GetWeapon()->m_eWeaponType != WEAPONTYPE_MP5 + && GetWeapon()->m_eWeaponType != WEAPONTYPE_GOLFCLUB && GetWeapon()->m_eWeaponType != WEAPONTYPE_KATANA + && GetWeapon()->m_eWeaponType != WEAPONTYPE_CAMERA) { + if (!GetWeapon()->IsType2Handed()) { + groupToSet = ASSOCGRP_PLAYER; + } else { + groupToSet = ASSOCGRP_PLAYER2ARMED; + } + } else { + groupToSet = ASSOCGRP_PLAYER1ARMED; + } + } + } + + if (m_animGroup != groupToSet) { + m_animGroup = groupToSet; + ReApplyMoveAnims(); + } +} + +void +CPlayerPed::ProcessPlayerWeapon(CPad *padUsed) +{ + CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + if (m_bHasLockOnTarget && !m_pPointGunAt) { + TheCamera.ClearPlayerWeaponMode(); + CWeaponEffects::ClearCrossHair(); + ClearPointGunAt(); + } + + if (padUsed->DuckJustDown() && !bIsDucking && m_nMoveState != PEDMOVE_SPRINT) { +#ifdef FIX_BUGS + // fix tommy being locked into looking at the same spot if you duck just after starting to shoot + if(!m_pPointGunAt) + ClearPointGunAt(); +#endif + bCrouchWhenShooting = true; + SetDuck(60000, true); + } else if (bIsDucking && (padUsed->DuckJustDown() || m_nMoveState == PEDMOVE_SPRINT || + padUsed->GetSprint() || padUsed->JumpJustDown() || padUsed->ExitVehicleJustDown())) { + +#ifdef FIX_BUGS + // same fix as above except for standing up + if(!m_pPointGunAt) + ClearPointGunAt(); +#endif + ClearDuck(true); + bCrouchWhenShooting = false; + } + + if(weaponInfo->IsFlagSet(WEAPONFLAG_CANAIM)) + m_wepAccuracy = 95; + else + m_wepAccuracy = 100; + + if (!m_pFire) { + eWeaponType weapon = GetWeapon()->m_eWeaponType; + if (weapon == WEAPONTYPE_ROCKETLAUNCHER || weapon == WEAPONTYPE_SNIPERRIFLE || + weapon == WEAPONTYPE_LASERSCOPE || weapon == WEAPONTYPE_M4 || + weapon == WEAPONTYPE_RUGER || weapon == WEAPONTYPE_M60 || + weapon == WEAPONTYPE_CAMERA) { + + if (padUsed->TargetJustDown() || TheCamera.m_bJustJumpedOutOf1stPersonBecauseOfTarget) { +#ifdef FREE_CAM + if (CCamera::bFreeCam && TheCamera.Cams[0].Using3rdPersonMouseCam()) { + m_fRotationCur = CGeneral::LimitRadianAngle(-TheCamera.Orientation); + SetHeading(m_fRotationCur); + } +#endif + if (weapon == WEAPONTYPE_ROCKETLAUNCHER) + TheCamera.SetNewPlayerWeaponMode(CCam::MODE_ROCKETLAUNCHER, 0, 0); + else if (weapon == WEAPONTYPE_SNIPERRIFLE || weapon == WEAPONTYPE_LASERSCOPE) + TheCamera.SetNewPlayerWeaponMode(CCam::MODE_SNIPER, 0, 0); + else if (weapon == WEAPONTYPE_CAMERA) + TheCamera.SetNewPlayerWeaponMode(CCam::MODE_CAMERA, 0, 0); + else + TheCamera.SetNewPlayerWeaponMode(CCam::MODE_M16_1STPERSON, 0, 0); + + m_fMoveSpeed = 0.0f; + CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE, 1000.0f); + SetPedState(PED_SNIPER_MODE); + return; + } + if (!TheCamera.Using1stPersonWeaponMode()) + if (weapon == WEAPONTYPE_ROCKETLAUNCHER || weapon == WEAPONTYPE_SNIPERRIFLE || weapon == WEAPONTYPE_LASERSCOPE || weapon == WEAPONTYPE_CAMERA) + return; + } + } + + if (padUsed->GetWeapon() && m_nMoveState != PEDMOVE_SPRINT) { + if (m_nSelectedWepSlot == m_currentWeapon) { + if (m_pPointGunAt) { + if (m_nPedState == PED_ATTACK) { + m_fAttackButtonCounter *= Pow(0.94f, CTimer::GetTimeStep()); + } else { + m_fAttackButtonCounter = 0.0f; + } + SetAttack(m_pPointGunAt); + } else { + if (m_nPedState == PED_ATTACK) { + if (padUsed->WeaponJustDown()) { + m_bHaveTargetSelected = true; + } else if (!m_bHaveTargetSelected) { + m_fAttackButtonCounter += CTimer::GetTimeStepNonClipped(); + } + } else { + m_fAttackButtonCounter = 0.0f; + m_bHaveTargetSelected = false; + } + if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED && GetWeapon()->m_eWeaponType != WEAPONTYPE_BRASSKNUCKLE && + !weaponInfo->IsFlagSet(WEAPONFLAG_FIGHTMODE)) { + + if (GetWeapon()->m_eWeaponType != WEAPONTYPE_DETONATOR && GetWeapon()->m_eWeaponType != WEAPONTYPE_DETONATOR_GRENADE || + padUsed->WeaponJustDown()) + SetAttack(nil); + + } else if (padUsed->WeaponJustDown()) { + if (m_fMoveSpeed < 1.0f || m_nPedState == PED_FIGHT) + StartFightAttack(padUsed->GetWeapon()); + else + SetAttack(nil); + } + } + } + } else { + m_pedIK.m_flags &= ~CPedIK::LOOKAROUND_HEAD_ONLY; + if (m_nPedState == PED_ATTACK) { + m_bHaveTargetSelected = true; + bIsAttacking = false; + } + } + +#ifdef FREE_CAM + static int8 changedHeadingRate = 0; + static int8 pointedGun = 0; + if (changedHeadingRate == 2) changedHeadingRate = 1; + if (pointedGun == 2) pointedGun = 1; + + // Rotate player/arm when shooting. We don't have auto-rotation anymore + if (CCamera::m_bUseMouse3rdPerson && CCamera::bFreeCam && + m_nSelectedWepSlot == m_currentWeapon && m_nMoveState != PEDMOVE_SPRINT) { + +#define CAN_AIM_WITH_ARM (weaponInfo->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM) && !bIsDucking && !bCrouchWhenShooting) + // Weapons except throwable and melee ones + if (weaponInfo->m_nWeaponSlot > 2) { + if ((padUsed->GetTarget() && CAN_AIM_WITH_ARM) || padUsed->GetWeapon()) { + float limitedCam = CGeneral::LimitRadianAngle(-TheCamera.Orientation); + + m_cachedCamSource = TheCamera.Cams[TheCamera.ActiveCam].Source; + m_cachedCamFront = TheCamera.Cams[TheCamera.ActiveCam].Front; + m_cachedCamUp = TheCamera.Cams[TheCamera.ActiveCam].Up; + + // On this one we can rotate arm. + if (CAN_AIM_WITH_ARM) { + pointedGun = 2; + m_bFreeAimActive = true; + SetLookFlag(limitedCam, true, true); + SetAimFlag(limitedCam); + SetLookTimer(INT32_MAX); + ((CPlayerPed*)this)->m_fFPSMoveHeading = TheCamera.Find3rdPersonQuickAimPitch(); + if (m_nPedState != PED_ATTACK && m_nPedState != PED_AIM_GUN) { + // This is a seperate ped state just for pointing gun. Used for target button + SetPointGunAt(nil); + } + } else { + m_fRotationDest = limitedCam; + changedHeadingRate = 2; + m_headingRate = 12.5f; + + // Anim. fix for shotgun, ak47 and m16 (we must finish rot. it quickly) + if (weaponInfo->IsFlagSet(WEAPONFLAG_CANAIM) && padUsed->WeaponJustDown()) { + m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); + float limitedRotDest = m_fRotationDest; + + if (m_fRotationCur - PI > m_fRotationDest) { + limitedRotDest += 2 * PI; + } else if (PI + m_fRotationCur < m_fRotationDest) { + limitedRotDest -= 2 * PI; + } + + m_fRotationCur += (limitedRotDest - m_fRotationCur) / 2; + } + } + } + } +#undef CAN_AIM_WITH_ARM + } + if (changedHeadingRate == 1) { + changedHeadingRate = 0; + RestoreHeadingRate(); + } + if (pointedGun == 1) { + if (m_nPedState == PED_ATTACK) { + if (!padUsed->GetWeapon() && (m_pedIK.m_flags & CPedIK::GUN_POINTED_SUCCESSFULLY) == 0) { + float limitedCam = CGeneral::LimitRadianAngle(-TheCamera.Orientation); + + SetAimFlag(limitedCam); + ((CPlayerPed*)this)->m_fFPSMoveHeading = TheCamera.Find3rdPersonQuickAimPitch(); + m_bFreeAimActive = true; + } + } else { + pointedGun = 0; + ClearPointGunAt(); + } + } +#endif + + if (padUsed->GetTarget() && m_nSelectedWepSlot == m_currentWeapon && m_nMoveState != PEDMOVE_SPRINT && !TheCamera.Using1stPersonWeaponMode() && weaponInfo->IsFlagSet(WEAPONFLAG_CANAIM)) { + if (m_pPointGunAt) { + // what?? + if (!m_pPointGunAt +#ifdef FREE_CAM + || (!CCamera::bFreeCam && CCamera::m_bUseMouse3rdPerson) +#else + || CCamera::m_bUseMouse3rdPerson +#endif + ) { + ClearWeaponTarget(); + return; + } + + if (m_pPointGunAt->IsPed() && ( +#ifndef AIMING_VEHICLE_OCCUPANTS + (((CPed*)m_pPointGunAt)->bInVehicle && (!((CPed*)m_pPointGunAt)->m_pMyVehicle || !((CPed*)m_pPointGunAt)->m_pMyVehicle->IsBike())) || +#endif + !CGame::nastyGame && ((CPed*)m_pPointGunAt)->DyingOrDead())) { + ClearWeaponTarget(); + return; + } + if (CPlayerPed::DoesTargetHaveToBeBroken(m_pPointGunAt->GetPosition(), GetWeapon()) || + (!bCanPointGunAtTarget && !weaponInfo->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM))) { // this line isn't on Mobile, idk why + ClearWeaponTarget(); + return; + } + + if (m_pPointGunAt) { + RotatePlayerToTrackTarget(); + } + + if (m_pPointGunAt) { + if (padUsed->ShiftTargetLeftJustDown()) + FindNextWeaponLockOnTarget(m_pPointGunAt, true); + if (padUsed->ShiftTargetRightJustDown()) + FindNextWeaponLockOnTarget(m_pPointGunAt, false); + } + TheCamera.SetNewPlayerWeaponMode(CCam::MODE_SYPHON, 0, 0); + TheCamera.UpdateAimingCoors(m_pPointGunAt->GetPosition()); + + } else if (!CCamera::m_bUseMouse3rdPerson) { + if (padUsed->TargetJustDown() || TheCamera.m_bJustJumpedOutOf1stPersonBecauseOfTarget) + FindWeaponLockOnTarget(); + } + } else if (m_pPointGunAt) { + ClearWeaponTarget(); + } + + if (m_pPointGunAt) { + CVector markPos; + if (m_pPointGunAt->IsPed()) { + ((CPed*)m_pPointGunAt)->m_pedIK.GetComponentPosition(markPos, PED_MID); + } else { + markPos = m_pPointGunAt->GetPosition(); + } + if (bCanPointGunAtTarget) { + CWeaponEffects::MarkTarget(markPos, 64, 0, 0, 255, 0.8f); + } else { + CWeaponEffects::MarkTarget(markPos, 64, 32, 0, 255, 0.8f); + } + } + m_bHasLockOnTarget = m_pPointGunAt != nil; +} + +bool +CPlayerPed::MovementDisabledBecauseOfTargeting(void) +{ + return m_pPointGunAt && !CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM); +} + +void +CPlayerPed::PlayerControlZelda(CPad *padUsed) +{ + float smoothSprayRate = DoWeaponSmoothSpray(); + float camOrientation = TheCamera.Orientation; + float leftRight = padUsed->GetPedWalkLeftRight(); + float upDown = padUsed->GetPedWalkUpDown(); + float padMoveInGameUnit; + bool smoothSprayWithoutMove = false; + + if (MovementDisabledBecauseOfTargeting()) { + upDown = 0.0f; + leftRight = 0.0f; + } + + if (smoothSprayRate > 0.0f && upDown > 0.0f) { + padMoveInGameUnit = 0.0f; + smoothSprayWithoutMove = true; + } else { + padMoveInGameUnit = CVector2D(leftRight, upDown).Magnitude() / PAD_MOVE_TO_GAME_WORLD_MOVE; + } + +#ifdef FREE_CAM + if (TheCamera.Cams[0].Using3rdPersonMouseCam() && smoothSprayRate > 0.0f) { + padMoveInGameUnit = 0.0f; + smoothSprayWithoutMove = false; + } +#endif + + if (padMoveInGameUnit > 0.0f || smoothSprayWithoutMove) { + float padHeading = CGeneral::GetRadianAngleBetweenPoints(0.0f, 0.0f, -leftRight, upDown); + float neededTurn = CGeneral::LimitRadianAngle(padHeading - camOrientation); + if (smoothSprayRate > 0.0f) { + m_fRotationDest = m_fRotationCur - leftRight / 128.0f * smoothSprayRate * CTimer::GetTimeStep(); + } else { + m_fRotationDest = neededTurn; + } + + float maxAcc = 0.07f * CTimer::GetTimeStep(); + m_fMoveSpeed = Min(padMoveInGameUnit, m_fMoveSpeed + maxAcc); + + } else { + m_fMoveSpeed = 0.0f; + } + + if (m_nPedState == PED_JUMP) { + if (bIsInTheAir) { + if (bUsesCollision && !bHitSteepSlope && (!bHitSomethingLastFrame || m_vecDamageNormal.z > 0.6f) + && m_fDistanceTravelled < CTimer::GetTimeStepInSeconds() && m_vecMoveSpeed.MagnitudeSqr() < 0.01f) { + + float angleSin = Sin(m_fRotationCur); // originally sin(DEGTORAD(RADTODEG(m_fRotationCur))) o_O + float angleCos = Cos(m_fRotationCur); + ApplyMoveForce(-angleSin * 3.0f, 3.0f * angleCos, 0.05f); + } + } else if (bIsLanding) { + m_fMoveSpeed = 0.0f; + } + } + + if (m_nPedState == PED_ANSWER_MOBILE) { + SetRealMoveAnim(); + return; + } + + if (!CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_HEAVY) && padUsed->GetSprint()) { + if (!m_pCurrentPhysSurface || (!m_pCurrentPhysSurface->bInfiniteMass || m_pCurrentPhysSurface->m_phy_flagA08)) + m_nMoveState = PEDMOVE_SPRINT; + } + + if (m_nPedState != PED_FIGHT) + SetRealMoveAnim(); + + if (!bIsInTheAir && !CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_HEAVY) + && padUsed->JumpJustDown() && m_nPedState != PED_JUMP) { + ClearAttack(); + ClearWeaponTarget(); + if (m_nEvadeAmount != 0 && m_pEvadingFrom) { + SetEvasiveDive((CPhysical*)m_pEvadingFrom, 1); + m_nEvadeAmount = 0; + m_pEvadingFrom = nil; + } else { + SetJump(); + } + } + PlayIdleAnimations(padUsed); +} + +// Finds nice positions for peds to duck and shoot player. And it's inside PlayerPed, this is treachery! +void +CPlayerPed::FindNewAttackPoints(void) +{ + for (int i=0; im_nPedState == PED_DEAD || safeNeighbour->m_pedInObjective != this) { + m_vecSafePos[i].x = 0.0f; + m_vecSafePos[i].y = 0.0f; + m_vecSafePos[i].z = 0.0f; + m_pPedAtSafePos[i] = nil; + } + } else { + m_vecSafePos[i].x = 0.0f; + m_vecSafePos[i].y = 0.0f; + m_vecSafePos[i].z = 0.0f; + } + } + CEntity *entities[6]; + int16 numEnts; + float rightMult, fwdMult; + CWorld::FindObjectsInRange(GetPosition(), 18.0f, true, &numEnts, 6, entities, true, false, false, true, false); + for (int i = 0; i < numEnts; ++i) { + CEntity *ent = entities[i]; + int16 mi = ent->GetModelIndex(); + if (!ent->IsObject() || ((CObject*)ent)->m_nSpecialCollisionResponseCases == COLLRESPONSE_FENCEPART) + if (!IsTreeModel(mi)) + continue; + + if (mi == MI_TRAFFICLIGHTS) { + rightMult = 2.957f; + fwdMult = 0.147f; + + } else if (mi == MI_SINGLESTREETLIGHTS1) { + rightMult = 0.744f; + fwdMult = 0.0f; + + } else if (mi == MI_SINGLESTREETLIGHTS2) { + rightMult = 0.043f; + fwdMult = 0.0f; + + } else if (mi == MI_SINGLESTREETLIGHTS3) { + rightMult = 1.143f; + fwdMult = 0.145f; + + } else if (mi == MI_DOUBLESTREETLIGHTS) { + rightMult = 0.744f; + fwdMult = 0.0f; + + } else if (mi == MI_LAMPPOST1) { + rightMult = 0.744f; + fwdMult = 0.0f; + + } else if (mi == MI_TRAFFICLIGHT01) { + rightMult = 2.957f; + fwdMult = 0.147f; + + } else if (mi == MI_LITTLEHA_POLICE) { + rightMult = 0.0f; + fwdMult = 0.0f; + + } else if (mi == MI_PARKBENCH) { + rightMult = 0.0f; + fwdMult = 0.0f; + + } else if (IsTreeModel(mi)) { + rightMult = 0.0f; + fwdMult = 0.0f; + } else + continue; + + CVector entAttackPoint(rightMult * ent->GetRight().x + fwdMult * ent->GetForward().x + ent->GetPosition().x, + rightMult * ent->GetRight().y + fwdMult * ent->GetForward().y + ent->GetPosition().y, + ent->GetPosition().z); + CVector attackerPos = GetPosition() - entAttackPoint; // for now it's dist, not attackerPos + CVector dirTowardsUs = attackerPos; + dirTowardsUs.Normalise(); + dirTowardsUs *= 2.0f; + attackerPos = entAttackPoint - dirTowardsUs; // to make cop farther from us + CPedPlacement::FindZCoorForPed(&attackerPos); + if (CPedPlacement::IsPositionClearForPed(attackerPos)) + m_vecSafePos[i] = attackerPos; + } +} + +void +CPlayerPed::ProcessControl(void) +{ + // Mobile has some debug/abandoned cheat thing in here: "gbFrankenTommy" + + if (m_nEvadeAmount != 0) + --m_nEvadeAmount; + + if (m_nEvadeAmount == 0) + m_pEvadingFrom = nil; + + if (m_pWanted->GetWantedLevel() > 0) + FindNewAttackPoints(); + + UpdateMeleeAttackers(); + + if (m_pCurrentPhysSurface && m_pCurrentPhysSurface->IsVehicle() && ((CVehicle*)m_pCurrentPhysSurface)->IsBoat()) { + bTryingToReachDryLand = true; + + } else if (!(((uint8)CTimer::GetFrameCounter() + m_randomSeed) & 0xF)) { + CVehicle *nearVeh = (CVehicle*)CWorld::TestSphereAgainstWorld(GetPosition(), 7.0f, nil, false, true, false, false, false, false); + if (nearVeh && nearVeh->IsBoat()) + bTryingToReachDryLand = true; + else + bTryingToReachDryLand = false; + } + + if (m_nFadeDrunkenness) { + if (m_nDrunkenness - 1 > 0) { + --m_nDrunkenness; + } else { + m_nDrunkenness = 0; + CMBlur::ClearDrunkBlur(); + m_nFadeDrunkenness = 0; + } + } + if (m_nDrunkenness != 0) { + CMBlur::SetDrunkBlur(m_nDrunkenness / 255.f); + } + CPed::ProcessControl(); + SetNearbyPedsToInteractWithPlayer(); + if (bWasPostponed) + return; + + CPad *padUsed = GetPadFromPlayer(this); + m_pWanted->Update(); + PruneReferences(); + + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN) { + CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); + CAnimBlendAssociation *fireAnim = RpAnimBlendClumpGetAssociation(GetClump(), GetPrimaryFireAnim(weaponInfo)); + if (fireAnim && fireAnim->currentTime - fireAnim->timeStep < weaponInfo->m_fAnimLoopEnd && m_nPedState == PED_ATTACK) { + if (m_fGunSpinSpeed < 0.45f) { + m_fGunSpinSpeed = Min(0.45f, m_fGunSpinSpeed + CTimer::GetTimeStep() * 0.013f); + } + + if (padUsed->GetWeapon() && GetWeapon()->m_nAmmoTotal > 0 && fireAnim->currentTime >= weaponInfo->m_fAnimLoopStart) { + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_MINIGUN_ATTACK, 0.0f); + } else { + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_MINIGUN_2, m_fGunSpinSpeed * (20.f / 9)); + } + } else { + if (m_fGunSpinSpeed > 0.0f) { + if (m_fGunSpinSpeed >= 0.45f) { + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_MINIGUN_3, 0.0f); + } + m_fGunSpinSpeed = Max(0.0f, m_fGunSpinSpeed - CTimer::GetTimeStep() * 0.003f); + } + } + } + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_CHAINSAW && m_nPedState != PED_ATTACK && !bInVehicle) { + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_CHAINSAW_IDLE, 0.0f); + } + + if (m_nMoveState != PEDMOVE_RUN && m_nMoveState != PEDMOVE_SPRINT) + RestoreSprintEnergy(1.0f); + else if (m_nMoveState == PEDMOVE_RUN) + RestoreSprintEnergy(0.3f); + + if (m_nPedState == PED_DEAD) { + ClearWeaponTarget(); + return; + } + if (m_nPedState == PED_DIE) { + ClearWeaponTarget(); + if (CTimer::GetTimeInMilliseconds() > m_bloodyFootprintCountOrDeathTime + 4000) + SetDead(); + return; + } + if (m_nPedState == PED_DRIVING && m_objective != OBJECTIVE_LEAVE_CAR) { + if (!CReplay::IsPlayingBack() || m_pMyVehicle) { + if (m_pMyVehicle->IsCar() && ((CAutomobile*)m_pMyVehicle)->Damage.GetDoorStatus(DOOR_FRONT_LEFT) == DOOR_STATUS_SWINGING) { + CAnimBlendAssociation *rollDoorAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LHS); + + if (m_pMyVehicle->m_nGettingOutFlags & CAR_DOOR_FLAG_LF || rollDoorAssoc || (rollDoorAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LO_LHS))) { + if (rollDoorAssoc) + m_pMyVehicle->ProcessOpenDoor(CAR_DOOR_LF, ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LHS, rollDoorAssoc->currentTime); + + } else { + // These comparisons are wrong, they return uint16 + if (padUsed && (padUsed->GetAccelerate() != 0.0f || padUsed->GetSteeringLeftRight() != 0.0f || padUsed->GetBrake() != 0.0f)) { + if (rollDoorAssoc) + m_pMyVehicle->ProcessOpenDoor(CAR_DOOR_LF, ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LHS, rollDoorAssoc->currentTime); + + } else { + m_pMyVehicle->m_nGettingOutFlags |= CAR_DOOR_FLAG_LF; + if (m_pMyVehicle->bLowVehicle) + rollDoorAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LO_LHS); + else + rollDoorAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LHS); + + rollDoorAssoc->SetFinishCallback(PedAnimDoorCloseRollingCB, this); + } + } + } + } + return; + } + if (m_objective == OBJECTIVE_NONE) + m_nMoveState = PEDMOVE_STILL; + if (bIsLanding) + RunningLand(padUsed); + + if (padUsed && padUsed->WeaponJustDown() && !TheCamera.Using1stPersonWeaponMode()) { + // ...Really? + eWeaponType playerWeapon = FindPlayerPed()->GetWeapon()->m_eWeaponType; + if (playerWeapon == WEAPONTYPE_SNIPERRIFLE || playerWeapon == WEAPONTYPE_LASERSCOPE) { + DMAudio.PlayFrontEndSound(SOUND_WEAPON_SNIPER_SHOT_NO_ZOOM, 0); + } else if (playerWeapon == WEAPONTYPE_ROCKETLAUNCHER) { + DMAudio.PlayFrontEndSound(SOUND_WEAPON_ROCKET_SHOT_NO_ZOOM, 0); + } + } + + switch (m_nPedState) { + case PED_NONE: + case PED_IDLE: + case PED_FLEE_POS: + case PED_FLEE_ENTITY: + case PED_ATTACK: + case PED_FIGHT: + case PED_AIM_GUN: + case PED_ANSWER_MOBILE: + if (!RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_BLOCK) && !m_attachedTo) { + if (TheCamera.Using1stPersonWeaponMode()) { + if (padUsed) + PlayerControlSniper(padUsed); + + } else if (TheCamera.Cams[0].Using3rdPersonMouseCam() +#ifdef FREE_CAM + && !CCamera::bFreeCam +#endif + ) { + if (padUsed) + PlayerControl1stPersonRunAround(padUsed); + + } else if (m_nPedState == PED_FIGHT) { + if (padUsed) + PlayerControlFighter(padUsed); + + } else if (padUsed) { + PlayerControlZelda(padUsed); + } + } + if (IsPedInControl() && m_nPedState != PED_ANSWER_MOBILE && padUsed) + ProcessPlayerWeapon(padUsed); + break; + case PED_SEEK_ENTITY: + m_vecSeekPos = m_pSeekTarget->GetPosition(); + + // fall through + case PED_SEEK_POS: + switch (m_nMoveState) { + case PEDMOVE_WALK: + m_fMoveSpeed = 1.0f; + break; + case PEDMOVE_RUN: + m_fMoveSpeed = 1.8f; + break; + case PEDMOVE_SPRINT: + m_fMoveSpeed = 2.5f; + break; + default: + m_fMoveSpeed = 0.0f; + break; + } + SetRealMoveAnim(); + if (Seek()) { + RestorePreviousState(); + SetMoveState(PEDMOVE_STILL); + } + break; + case PED_SNIPER_MODE: + if (GetWeapon()->m_eWeaponType == WEAPONTYPE_SNIPERRIFLE || GetWeapon()->m_eWeaponType == WEAPONTYPE_LASERSCOPE) { + if (padUsed) + PlayerControlSniper(padUsed); + + } else if (padUsed) { + PlayerControlM16(padUsed); + } + break; + case PED_SEEK_CAR: + case PED_SEEK_IN_BOAT: + if (bVehEnterDoorIsBlocked || bKindaStayInSamePlace) { + m_fMoveSpeed = 0.0f; + } else { + m_fMoveSpeed = Min(2.0f, 2.0f * (m_vecSeekPos - GetPosition()).Magnitude2D()); + } + if (padUsed && !padUsed->ArePlayerControlsDisabled()) { + if (padUsed->GetTarget() || padUsed->GetLeftStickXJustDown() || padUsed->GetLeftStickYJustDown() || + padUsed->GetDPadUpJustDown() || padUsed->GetDPadDownJustDown() || padUsed->GetDPadLeftJustDown() || + padUsed->GetDPadRightJustDown()) { + + RestorePreviousState(); + if (m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER || m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) { + RestorePreviousObjective(); + } + } + } + if (padUsed && padUsed->GetSprint()) + m_nMoveState = PEDMOVE_SPRINT; + SetRealMoveAnim(); + break; + case PED_JUMP: + if (padUsed) + PlayerControlZelda(padUsed); + if (bIsLanding) + break; + + // This has been added later it seems + return; + case PED_FALL: + case PED_GETUP: + case PED_ENTER_TRAIN: + case PED_EXIT_TRAIN: + case PED_CARJACK: + case PED_DRAG_FROM_CAR: + case PED_ENTER_CAR: + case PED_STEAL_CAR: + case PED_EXIT_CAR: + ClearWeaponTarget(); + break; + case PED_ARRESTED: + if (m_nLastPedState == PED_DRAG_FROM_CAR && m_pVehicleAnim) + BeingDraggedFromCar(); + break; + default: + break; + } + if (padUsed && IsPedShootable() && m_nPedState != PED_ANSWER_MOBILE && m_nLastPedState != PED_ANSWER_MOBILE) { + ProcessWeaponSwitch(padUsed); + GetWeapon()->Update(m_audioEntityId, this); + } + ProcessAnimGroups(); + if (padUsed) { + if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_FOLLOWPED + && TheCamera.Cams[TheCamera.ActiveCam].DirectionWasLooking == LOOKING_BEHIND) { + + m_lookTimer = 0; + float camAngle = CGeneral::LimitRadianAngle(TheCamera.Cams[TheCamera.ActiveCam].Front.Heading()); + float angleBetweenPlayerAndCam = Abs(camAngle - m_fRotationCur); + + if (m_nPedState != PED_ATTACK && angleBetweenPlayerAndCam > DEGTORAD(30.0f) && angleBetweenPlayerAndCam < DEGTORAD(330.0f)) { + if (angleBetweenPlayerAndCam > DEGTORAD(150.0f) && angleBetweenPlayerAndCam < DEGTORAD(210.0f)) { + float rightTurnAngle = CGeneral::LimitRadianAngle(m_fRotationCur - DEGTORAD(150.0f)); + float leftTurnAngle = CGeneral::LimitRadianAngle(DEGTORAD(150.0f) + m_fRotationCur); + + if (m_fLookDirection == 999999.0f || bIsDucking) + camAngle = rightTurnAngle; + else if (Abs(rightTurnAngle - m_fLookDirection) < Abs(leftTurnAngle - m_fLookDirection)) + camAngle = rightTurnAngle; + else + camAngle = leftTurnAngle; + } + SetLookFlag(camAngle, true); + SetLookTimer(CTimer::GetTimeStepInMilliseconds() * 5.0f); + } else { + ClearLookFlag(); + } + } + } + if (m_nMoveState == PEDMOVE_SPRINT && bIsLooking) { + ClearLookFlag(); + SetLookTimer(250); + } + + if (m_vecMoveSpeed.Magnitude2D() < 0.1f) { + if (m_nSpeedTimer) { + if (CTimer::GetTimeInMilliseconds() > m_nSpeedTimer) + m_bSpeedTimerFlag = true; + } else { + m_nSpeedTimer = CTimer::GetTimeInMilliseconds() + 500; + } + } else { + m_nSpeedTimer = 0; + m_bSpeedTimerFlag = false; + } + + if (bDontAllowWeaponChange && FindPlayerPed() == this) { + if (!CPad::GetPad(0)->GetTarget()) + bDontAllowWeaponChange = false; + } + + if (m_nPedState != PED_SNIPER_MODE && (GetWeapon()->m_eWeaponState == WEAPONSTATE_FIRING || m_nPedState == PED_ATTACK)) + m_nPadDownPressedInMilliseconds = CTimer::GetTimeInMilliseconds(); + + if (!bIsVisible) + UpdateRpHAnim(); +} + +bool +CPlayerPed::DoesPlayerWantNewWeapon(eWeaponType weapon, bool onlyIfSlotIsEmpty) +{ + // GetPadFromPlayer(); // unused + uint32 slot = CWeaponInfo::GetWeaponInfo(weapon)->m_nWeaponSlot; + + if (!HasWeaponSlot(slot) || GetWeapon(slot).m_eWeaponType == weapon) + return true; + + if (onlyIfSlotIsEmpty) + return false; + + // Check if he's using that slot right now. + return m_nPedState != PED_ATTACK && m_nPedState != PED_AIM_GUN || slot != m_currentWeapon; +} + +void +CPlayerPed::PlayIdleAnimations(CPad *padUsed) +{ + CAnimBlendAssociation* assoc; + + if (TheCamera.m_WideScreenOn || bIsDucking) + return; + + struct animAndGroup { + AnimationId animId; + AssocGroupId groupId; + }; + + const animAndGroup idleAnims[] = { + {ANIM_PLAYER_IDLE1, ASSOCGRP_PLAYER_IDLE}, + {ANIM_PLAYER_IDLE2, ASSOCGRP_PLAYER_IDLE}, + {ANIM_PLAYER_IDLE3, ASSOCGRP_PLAYER_IDLE}, + {ANIM_PLAYER_IDLE4, ASSOCGRP_PLAYER_IDLE}, + {ANIM_STD_XPRESS_SCRATCH, ASSOCGRP_STD}, + }; + + static int32 lastTime = 0; + static int32 lastAnim = -1; + + bool hasIdleAnim = false; + CAnimBlock *idleAnimBlock = CAnimManager::GetAnimationBlock(idleAnimBlockIndex); + uint32 sinceLastInput = padUsed->InputHowLongAgo(); + if (sinceLastInput <= 30000) { + if (idleAnimBlock->isLoaded) { + for (assoc = RpAnimBlendClumpGetFirstAssociation(GetClump()); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { + if (assoc->flags & ASSOC_IDLE) { + hasIdleAnim = true; + assoc->blendDelta = -8.0f; + } + } + if (!hasIdleAnim) + CStreaming::RemoveAnim(idleAnimBlockIndex); + } else { + lastTime = 0; + } + } else { + CStreaming::RequestAnim(idleAnimBlockIndex, STREAMFLAGS_DONT_REMOVE); + if (idleAnimBlock->isLoaded) { + for(CAnimBlendAssociation *assoc = RpAnimBlendClumpGetFirstAssociation(GetClump()); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { + int firstIdle = idleAnimBlock->firstIndex; + int index = assoc->hierarchy - CAnimManager::GetAnimation(0); + if (index >= firstIdle && index < firstIdle + idleAnimBlock->numAnims) { + hasIdleAnim = true; + break; + } + } + + if (!hasIdleAnim && !bIsLooking && !bIsRestoringLook && sinceLastInput - lastTime > 25000) { + int anim; + do + anim = CGeneral::GetRandomNumberInRange(0, (int32)ARRAY_SIZE(idleAnims)); + while (lastAnim == anim); + + assoc = CAnimManager::BlendAnimation(GetClump(), idleAnims[anim].groupId, idleAnims[anim].animId, 8.0f); + assoc->flags |= ASSOC_IDLE; + lastAnim = anim; + lastTime = sinceLastInput; + } + } + } +} + +void +CPlayerPed::SetNearbyPedsToInteractWithPlayer(void) +{ + if (CGame::noProstitutes) + return; + + for (int i = 0; i < m_numNearPeds; ++i) { + CPed *nearPed = m_nearPeds[i]; + if (nearPed && nearPed->m_objectiveTimer < CTimer::GetTimeInMilliseconds() && !CTheScripts::IsPlayerOnAMission()) { + int mi = nearPed->GetModelIndex(); + if (CPopulation::CanSolicitPlayerOnFoot(mi)) { + CVector distToMe = nearPed->GetPosition() - GetPosition(); + CVector dirToMe = GetPosition() - nearPed->GetPosition(); + dirToMe.Normalise(); + if (DotProduct(dirToMe, nearPed->GetForward()) > 0.707 && DotProduct(GetForward(), nearPed->GetForward()) < -0.707 // those are double + && distToMe.MagnitudeSqr() < 9.0f && nearPed->m_objective == OBJECTIVE_NONE) { + nearPed->SetObjective(OBJECTIVE_SOLICIT_FOOT, this); + nearPed->m_objectiveTimer = CTimer::GetTimeInMilliseconds() + 10000; + nearPed->Say(SOUND_PED_SOLICIT); + } + } else if (CPopulation::CanSolicitPlayerInCar(mi)) { + if (InVehicle() && m_pMyVehicle->IsVehicleNormal()) { + if (m_pMyVehicle->IsCar()) { + CVector distToVeh = nearPed->GetPosition() - m_pMyVehicle->GetPosition(); + if (distToVeh.MagnitudeSqr() < 25.0f && m_pMyVehicle->IsRoomForPedToLeaveCar(CAR_DOOR_LF, nil) && nearPed->m_objective == OBJECTIVE_NONE) { + nearPed->SetObjective(OBJECTIVE_SOLICIT_VEHICLE, m_pMyVehicle); + } + } + } + } + } + } +} + +void +CPlayerPed::UpdateMeleeAttackers(void) +{ + CVector attackCoord; + if (((CTimer::GetFrameCounter() + m_randomSeed + 7) & 3) == 0) { + GetMeleeAttackCoords(attackCoord, m_nAttackDirToCheck, 2.0f); + + // Check if there is any vehicle/building inbetween us and m_nAttackDirToCheck. Peds will be able to attack us from those available directions. + if (CWorld::GetIsLineOfSightClear(GetPosition(), attackCoord, true, true, false, true, false, false, false) + && !CWorld::TestSphereAgainstWorld(attackCoord, 0.4f, m_pMeleeList[m_nAttackDirToCheck], true, true, false, true, false, false)) { + if (m_pMeleeList[m_nAttackDirToCheck] == this) + m_pMeleeList[m_nAttackDirToCheck] = nil; // mark it as available + } else { + m_pMeleeList[m_nAttackDirToCheck] = this; // slot not available. useful for m_bNoPosForMeleeAttack + } + if (++m_nAttackDirToCheck >= ARRAY_SIZE(m_pMeleeList)) + m_nAttackDirToCheck = 0; + } + // 6 directions + for (int i = 0; i < ARRAY_SIZE(m_pMeleeList); ++i) { + CPed *victim = m_pMeleeList[i]; + if (victim && victim != this) { + if (victim->m_nPedState != PED_DEAD && victim->m_pedInObjective == this) { + if (victim->m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || victim->m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS || victim->m_objective == OBJECTIVE_KILL_CHAR_ON_BOAT) { + GetMeleeAttackCoords(attackCoord, i, 2.0f); + if ((attackCoord - GetPosition()).MagnitudeSqr() > 12.25f) + m_pMeleeList[i] = nil; + } else { + m_pMeleeList[i] = nil; + } + } else { + m_pMeleeList[i] = nil; + } + } + } + m_bNoPosForMeleeAttack = m_pMeleeList[0] == this && m_pMeleeList[1] == this && m_pMeleeList[2] == this +#ifdef FIX_BUGS + && m_pMeleeList[3] == this +#endif + && m_pMeleeList[4] == this && m_pMeleeList[5] == this; +} + +void +CPlayerPed::RemovePedFromMeleeList(CPed *ped) +{ + for (uint16 i = 0; i < ARRAY_SIZE(m_pMeleeList); i++) { + if (m_pMeleeList[i] == ped) { + m_pMeleeList[i] = nil; + ped->m_attackTimer = 0; + return; + } + } +} + +void +CPlayerPed::GetMeleeAttackCoords(CVector& coords, int8 dir, float dist) +{ + coords = GetPosition(); + switch (dir) { + case 0: + coords.y += dist; + break; + case 1: + coords.x += Sqrt(3.f / 4.f) * dist; + coords.y += 0.5f * dist; + break; + case 2: + coords.x += Sqrt(3.f / 4.f) * dist; + coords.y -= 0.5f * dist; + break; + case 3: + coords.y -= dist; + break; + case 4: + coords.x -= Sqrt(3.f / 4.f) * dist; + coords.y -= 0.5f * dist; + break; + case 5: + coords.x -= Sqrt(3.f / 4.f) * dist; + coords.y += 0.5f * dist; + break; + default: + break; + } +} + +int32 +CPlayerPed::FindMeleeAttackPoint(CPed *victim, CVector &dist, uint32 &endOfAttackOut) +{ + endOfAttackOut = 0; + bool thereIsAnEmptySlot = false; + int dirToAttack = -1; + for (int i = 0; i < ARRAY_SIZE(m_pMeleeList); i++) { + CPed* pedAtThisDir = m_pMeleeList[i]; + if (pedAtThisDir) { + if (pedAtThisDir == victim) { + dirToAttack = i; + } else { + if (pedAtThisDir->m_attackTimer > endOfAttackOut) + endOfAttackOut = pedAtThisDir->m_attackTimer; + } + } else { + thereIsAnEmptySlot = true; + } + } + + // We don't have victim ped in our melee list + if (dirToAttack == -1 && thereIsAnEmptySlot) { + float angle = Atan2(-dist.x, -dist.y); + float adjustedAngle = angle + DEGTORAD(30.0f); + if (adjustedAngle < 0.f) + adjustedAngle += TWOPI; + + int wantedDir = Floor(adjustedAngle / DEGTORAD(60.0f)); + + // And we have another ped at the direction of victim ped, so store victim to next empty direction to it's real direction. (Bollocks) + if (m_pMeleeList[wantedDir]) { + int closestDirToPreferred = -99; + int preferredDir = wantedDir; + + for (int i = 0; i < ARRAY_SIZE(m_pMeleeList); i++) { + if (!m_pMeleeList[i]) { + if (Abs(i - preferredDir) < Abs(closestDirToPreferred - preferredDir)) + closestDirToPreferred = i; + } + } + if (closestDirToPreferred > 0) + dirToAttack = closestDirToPreferred; + } else { + + // Luckily the direction of victim ped is already empty, good + dirToAttack = wantedDir; + } + + if (dirToAttack != -1) { + m_pMeleeList[dirToAttack] = victim; + victim->RegisterReference((CEntity**) &m_pMeleeList[dirToAttack]); + if (endOfAttackOut > CTimer::GetTimeInMilliseconds()) + victim->m_attackTimer = endOfAttackOut + CGeneral::GetRandomNumberInRange(1000, 2000); + else + victim->m_attackTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(500, 1000); + } + } + return dirToAttack; +} + +#ifdef COMPATIBLE_SAVES +#define CopyFromBuf(buf, data) memcpy(&data, buf, sizeof(data)); SkipSaveBuf(buf, sizeof(data)); +#define CopyToBuf(buf, data) memcpy(buf, &data, sizeof(data)); SkipSaveBuf(buf, sizeof(data)); +void +CPlayerPed::Save(uint8*& buf) +{ + CPed::Save(buf); + ZeroSaveBuf(buf, 16); + CopyToBuf(buf, m_fMaxStamina); + ZeroSaveBuf(buf, 28); + CopyToBuf(buf, m_nTargettableObjects[0]); + CopyToBuf(buf, m_nTargettableObjects[1]); + CopyToBuf(buf, m_nTargettableObjects[2]); + CopyToBuf(buf, m_nTargettableObjects[3]); + ZeroSaveBuf(buf, 164); +} + +void +CPlayerPed::Load(uint8*& buf) +{ + CPed::Load(buf); + SkipSaveBuf(buf, 16); + CopyFromBuf(buf, m_fMaxStamina); + SkipSaveBuf(buf, 28); + CopyFromBuf(buf, m_nTargettableObjects[0]); + CopyFromBuf(buf, m_nTargettableObjects[1]); + CopyFromBuf(buf, m_nTargettableObjects[2]); + CopyFromBuf(buf, m_nTargettableObjects[3]); + SkipSaveBuf(buf, 164); +} +#undef CopyFromBuf +#undef CopyToBuf +#endif diff --git a/src/miami/peds/PlayerPed.h b/src/miami/peds/PlayerPed.h new file mode 100644 index 00000000..30b67199 --- /dev/null +++ b/src/miami/peds/PlayerPed.h @@ -0,0 +1,120 @@ +#pragma once + +#include "Ped.h" + +class CPad; +class CCopPed; +class CWanted; + +class CPlayerPed : public CPed +{ +public: + CWanted *m_pWanted; + CCopPed *m_pArrestingCop; + float m_fMoveSpeed; + float m_fCurrentStamina; + float m_fMaxStamina; + float m_fStaminaProgress; + int8 m_nSelectedWepSlot; + bool m_bSpeedTimerFlag; + uint8 m_nEvadeAmount; + uint32 m_nSpeedTimer; // m_nStandStillTimer? + uint32 m_nHitAnimDelayTimer; // m_nShotDelay? + float m_fAttackButtonCounter; + bool m_bHaveTargetSelected; // may have better name + CEntity *m_pEvadingFrom; // is this CPhysical? + int32 m_nTargettableObjects[4]; + uint32 m_nAdrenalineTime; + uint8 m_nDrunkenness; // Needed to work out whether we lost target this frame + uint8 m_nFadeDrunkenness; + uint8 m_nDrunkCountdown; //countdown in frames when the drunk effect ends + bool m_bAdrenalineActive; + bool m_bHasLockOnTarget; + bool m_bCanBeDamaged; + bool m_bNoPosForMeleeAttack; + bool unk1; + CVector m_vecSafePos[6]; // safe places from the player, for example behind a tree + CPed *m_pPedAtSafePos[6]; + CPed *m_pMeleeList[6]; // reachable peds at each direction(6) + int16 m_nAttackDirToCheck; + float m_fWalkAngle; //angle between heading and walking direction + float m_fFPSMoveHeading; + RpAtomic* m_pMinigunTopAtomic; //atomic for the spinning part of the minigun model + float m_fGunSpinSpeed; // for minigun + float m_fGunSpinAngle; + unsigned int m_nPadDownPressedInMilliseconds; + unsigned int m_nLastBusFareCollected; +#ifdef FREE_CAM + bool m_bFreeAimActive; + CVector m_cachedCamSource; + CVector m_cachedCamFront; + CVector m_cachedCamUp; +#endif + + static bool bDontAllowWeaponChange; +#ifndef MASTER + static bool bDebugPlayerInfo; +#endif + + CPlayerPed(); + ~CPlayerPed(); + void SetMoveAnim() { }; + + void ReApplyMoveAnims(void); + void ClearWeaponTarget(void); + void SetWantedLevel(int32 level); + void SetWantedLevelNoDrop(int32 level); + void KeepAreaAroundPlayerClear(void); + void AnnoyPlayerPed(bool); + void MakeChangesForNewWeapon(int32); + void MakeChangesForNewWeapon(eWeaponType); + void SetInitialState(void); + void ProcessControl(void); + void ClearAdrenaline(void); + void UseSprintEnergy(void); + class CPlayerInfo *GetPlayerInfoForThisPlayerPed(); + void SetRealMoveAnim(void); + void RestoreSprintEnergy(float); + float DoWeaponSmoothSpray(void); + void DoStuffToGoOnFire(void); + bool DoesTargetHaveToBeBroken(CVector, CWeapon*); + void RunningLand(CPad*); + bool IsThisPedAnAimingPriority(CPed*); + void PlayerControlSniper(CPad*); + void PlayerControlM16(CPad*); + void PlayerControlFighter(CPad*); + void ProcessWeaponSwitch(CPad*); + void MakeObjectTargettable(int32); + void PlayerControl1stPersonRunAround(CPad *padUsed); + void EvaluateNeighbouringTarget(CEntity*, CEntity**, float*, float, float, bool, bool); + void EvaluateTarget(CEntity*, CEntity**, float*, float, float, bool); + bool FindNextWeaponLockOnTarget(CEntity*, bool); + bool FindWeaponLockOnTarget(void); + void ProcessAnimGroups(void); + void ProcessPlayerWeapon(CPad*); + void PlayerControlZelda(CPad*); + bool DoesPlayerWantNewWeapon(eWeaponType, bool); + void PlayIdleAnimations(CPad*); + void RemovePedFromMeleeList(CPed*); + void GetMeleeAttackCoords(CVector&, int8, float); + int32 FindMeleeAttackPoint(CPed*, CVector&, uint32&); + bool CanIKReachThisTarget(CVector, CWeapon*, bool); + void RotatePlayerToTrackTarget(void); + bool MovementDisabledBecauseOfTargeting(void); + void FindNewAttackPoints(void); + void SetNearbyPedsToInteractWithPlayer(void); + void UpdateMeleeAttackers(void); + + static void SetupPlayerPed(int32); + static void DeactivatePlayerPed(int32); + static void ReactivatePlayerPed(int32); + +#ifdef COMPATIBLE_SAVES + virtual void Save(uint8*& buf); + virtual void Load(uint8*& buf); +#endif + + static const uint32 nSaveStructSize; +}; + +//VALIDATE_SIZE(CPlayerPed, 0x5F0); diff --git a/src/miami/peds/Population.cpp b/src/miami/peds/Population.cpp new file mode 100644 index 00000000..248482a7 --- /dev/null +++ b/src/miami/peds/Population.cpp @@ -0,0 +1,1815 @@ +#include "common.h" + +#include "Game.h" +#include "General.h" +#include "World.h" +#include "Population.h" +#include "CopPed.h" +#include "Wanted.h" +#include "FileMgr.h" +#include "Gangs.h" +#include "ModelIndices.h" +#include "Zones.h" +#include "CivilianPed.h" +#include "EmergencyPed.h" +#include "Replay.h" +#include "Camera.h" +#include "CutsceneMgr.h" +#include "CarCtrl.h" +#include "IniFile.h" +#include "VisibilityPlugins.h" +#include "PedPlacement.h" +#include "DummyObject.h" +#include "Script.h" +#include "Shadows.h" +#include "SurfaceTable.h" +#include "Weather.h" +#include "Darkel.h" +#include "Streaming.h" +#include "Clock.h" +#include "WaterLevel.h" + +#define MIN_CREATION_DIST 40.0f // not for start of the game (look at the GeneratePedsAtStartOfGame) +#define CREATION_RANGE 10.0f // added over the MIN_CREATION_DIST. +#define OFFSCREEN_CREATION_MULT 0.5f +#define PED_REMOVE_DIST (MIN_CREATION_DIST + CREATION_RANGE + 1.0f) +#define PED_REMOVE_DIST_SPECIAL (MIN_CREATION_DIST + CREATION_RANGE + 15.0f) // for peds with bCullExtraFarAway flag + +PedGroup CPopulation::ms_pPedGroups[NUMPEDGROUPS]; +bool CPopulation::ms_bGivePedsWeapons; +int32 CPopulation::m_AllRandomPedsThisType = -1; +float CPopulation::PedDensityMultiplier = 1.0f; +uint32 CPopulation::ms_nTotalMissionPeds; +int32 CPopulation::MaxNumberOfPedsInUse = DEFAULT_MAX_NUMBER_OF_PEDS; +int32 CPopulation::MaxNumberOfPedsInUseInterior = DEFAULT_MAX_NUMBER_OF_PEDS_INTERIOR; +uint32 CPopulation::ms_nNumCivMale; +uint32 CPopulation::ms_nNumCivFemale; +uint32 CPopulation::ms_nNumCop; +bool CPopulation::bZoneChangeHasHappened; +uint32 CPopulation::ms_nNumEmergency; +int8 CPopulation::m_CountDownToPedsAtStart; +uint32 CPopulation::ms_nNumGang1; +uint32 CPopulation::ms_nNumGang2; +uint32 CPopulation::ms_nTotalPeds; +uint32 CPopulation::ms_nNumGang3; +uint32 CPopulation::ms_nTotalGangPeds; +uint32 CPopulation::ms_nNumGang4; +uint32 CPopulation::ms_nTotalCivPeds; +uint32 CPopulation::ms_nNumGang5; +uint32 CPopulation::ms_nNumDummy; +uint32 CPopulation::ms_nNumGang6; +uint32 CPopulation::ms_nNumGang9; +uint32 CPopulation::ms_nNumGang7; +uint32 CPopulation::ms_nNumGang8; + +uint32 CPopulation::ms_nTotalCarPassengerPeds; +uint32 CPopulation::NumMiamiViceCops; + +uint32 gLastSelectedCivilianIndex; +CEntity *gSunbatheObstacles[2]; +CEntity *gCoupleObstacles[3]; + +void +CPopulation::Initialise() +{ + debug("Initialising CPopulation...\n"); + + ms_nNumCivMale = 0; + ms_nNumCivFemale = 0; + ms_nNumCop = 0; + ms_nNumEmergency = 0; + ms_nNumGang1 = 0; + ms_nNumGang2 = 0; + ms_nNumGang3 = 0; + ms_nNumGang4 = 0; + ms_nNumGang5 = 0; + ms_nNumGang6 = 0; + ms_nNumGang7 = 0; + ms_nNumGang8 = 0; + ms_nNumGang9 = 0; + ms_nNumDummy = 0; + + ms_nTotalCarPassengerPeds = 0; + ms_nTotalCivPeds = 0; + ms_nTotalGangPeds = 0; + ms_nTotalPeds = 0; + ms_nTotalMissionPeds = 0; + m_CountDownToPedsAtStart = 2; + bZoneChangeHasHappened = false; // III leftover + + m_AllRandomPedsThisType = -1; + PedDensityMultiplier = 1.0f; + + + LoadPedGroups(); + + debug("CPopulation ready\n"); +} + +void +CPopulation::RemovePed(CPed *ent) +{ + CWorld::Remove((CEntity*)ent); + delete ent; +} + +int32 +CPopulation::ChooseCivilianOccupation(int32 group) +{ + if (CWeather::Rain > 0.1f) { + int32 lastModel; + for (int i = 0; i < 8; i++) { + gLastSelectedCivilianIndex = CGeneral::GetRandomNumberInRange(0, NUMMODELSPERPEDGROUP); + lastModel = ms_pPedGroups[group].models[gLastSelectedCivilianIndex]; + + if (!CPopulation::IsSunbather(lastModel)) + break; + } + return lastModel; + + } else { + gLastSelectedCivilianIndex = CGeneral::GetRandomNumberInRange(0, NUMMODELSPERPEDGROUP); + return ms_pPedGroups[group].models[gLastSelectedCivilianIndex]; + } +} + +int32 +CPopulation::ChooseNextCivilianOccupation(int32 group) +{ + if (CWeather::Rain > 0.1f) { + int32 lastModel; + for (int i = 0; i < NUMMODELSPERPEDGROUP; i++) { + ++gLastSelectedCivilianIndex; + if (gLastSelectedCivilianIndex >= NUMMODELSPERPEDGROUP) + gLastSelectedCivilianIndex = 0; + lastModel = ms_pPedGroups[group].models[gLastSelectedCivilianIndex]; + + if (!CPopulation::IsSunbather(ms_pPedGroups[group].models[gLastSelectedCivilianIndex])) + break; + } + return lastModel; + + } else { + ++gLastSelectedCivilianIndex; + if (gLastSelectedCivilianIndex >= NUMMODELSPERPEDGROUP) + gLastSelectedCivilianIndex = 0; + return ms_pPedGroups[group].models[gLastSelectedCivilianIndex]; + } +} + +// returns eCopType +int32 +CPopulation::ChoosePolicePedOccupation() +{ + CGeneral::GetRandomNumber(); + return COP_STREET; +} + +void +CPopulation::LoadPedGroups() +{ + int fd; + char line[1024]; + int nextPedGroup = 0; + // char unused[16]; // non-existence of that in mobile kinda verifies that + char modelName[256]; + + CFileMgr::ChangeDir("\\DATA\\"); + fd = CFileMgr::OpenFile("PEDGRP.DAT", "r"); + CFileMgr::ChangeDir("\\"); + while (CFileMgr::ReadLine(fd, line, sizeof(line))) { + int end; + // find end of line + for (end = 0; ; end++) { + if (line[end] == '\n') + break; + if (line[end] == ',' || line[end] == '\r') + line[end] = ' '; + } + line[end] = '\0'; + int cursor = 0; + int i; + for (i = 0; i < NUMMODELSPERPEDGROUP; i++) { + while (line[cursor] <= ' ' && line[cursor] != '\0') + ++cursor; + + if (line[cursor] == '#') + break; + + // find next whitespace + int nextWhitespace; + for (nextWhitespace = cursor; line[nextWhitespace] > ' '; ++nextWhitespace) + ; + + if (cursor == nextWhitespace) + break; + + // read until next whitespace + strncpy(modelName, &line[cursor], nextWhitespace - cursor); + modelName[nextWhitespace - cursor] = '\0'; + CModelInfo::GetModelInfo(modelName, &ms_pPedGroups[nextPedGroup].models[i]); + cursor = nextWhitespace; + } + if (i == NUMMODELSPERPEDGROUP) + nextPedGroup++; + } + CFileMgr::CloseFile(fd); +} + +void +CPopulation::UpdatePedCount(ePedType pedType, bool decrease) +{ + if (decrease) { + switch (pedType) { + case PEDTYPE_PLAYER1: + case PEDTYPE_PLAYER2: + case PEDTYPE_PLAYER3: + case PEDTYPE_PLAYER4: + case PEDTYPE_UNUSED1: + case PEDTYPE_SPECIAL: + return; + case PEDTYPE_CIVMALE: + --ms_nNumCivMale; + break; + case PEDTYPE_CIVFEMALE: + --ms_nNumCivFemale; + break; + case PEDTYPE_COP: + --ms_nNumCop; + break; + case PEDTYPE_GANG1: + --ms_nNumGang1; + break; + case PEDTYPE_GANG2: + --ms_nNumGang2; + break; + case PEDTYPE_GANG3: + --ms_nNumGang3; + break; + case PEDTYPE_GANG4: + --ms_nNumGang4; + break; + case PEDTYPE_GANG5: + --ms_nNumGang5; + break; + case PEDTYPE_GANG6: + --ms_nNumGang6; + break; + case PEDTYPE_GANG7: + --ms_nNumGang7; + break; + case PEDTYPE_GANG8: + --ms_nNumGang8; + break; + case PEDTYPE_GANG9: + --ms_nNumGang9; + break; + case PEDTYPE_EMERGENCY: + case PEDTYPE_FIREMAN: + --ms_nNumEmergency; + break; + case PEDTYPE_CRIMINAL: + --ms_nNumCivMale; + break; + case PEDTYPE_PROSTITUTE: + --ms_nNumCivFemale; + break; + case PEDTYPE_UNUSED2: + --ms_nNumDummy; + break; + default: + Error("Unknown ped type, UpdatePedCount, Population.cpp"); + break; + } + } else { + switch (pedType) { + case PEDTYPE_PLAYER1: + case PEDTYPE_PLAYER2: + case PEDTYPE_PLAYER3: + case PEDTYPE_PLAYER4: + case PEDTYPE_UNUSED1: + case PEDTYPE_SPECIAL: + return; + case PEDTYPE_CIVMALE: + ++ms_nNumCivMale; + break; + case PEDTYPE_CIVFEMALE: + ++ms_nNumCivFemale; + break; + case PEDTYPE_COP: + ++ms_nNumCop; + break; + case PEDTYPE_GANG1: + ++ms_nNumGang1; + break; + case PEDTYPE_GANG2: + ++ms_nNumGang2; + break; + case PEDTYPE_GANG3: + ++ms_nNumGang3; + break; + case PEDTYPE_GANG4: + ++ms_nNumGang4; + break; + case PEDTYPE_GANG5: + ++ms_nNumGang5; + break; + case PEDTYPE_GANG6: + ++ms_nNumGang6; + break; + case PEDTYPE_GANG7: + ++ms_nNumGang7; + break; + case PEDTYPE_GANG8: + ++ms_nNumGang8; + break; + case PEDTYPE_GANG9: + ++ms_nNumGang9; + break; + case PEDTYPE_EMERGENCY: + case PEDTYPE_FIREMAN: + ++ms_nNumEmergency; + break; + case PEDTYPE_CRIMINAL: + ++ms_nNumCivMale; + break; + case PEDTYPE_PROSTITUTE: + ++ms_nNumCivFemale; + break; + case PEDTYPE_UNUSED2: + ++ms_nNumDummy; + break; + default: + Error("Unknown ped type, UpdatePedCount, Population.cpp"); + break; + } + } +} + +int32 +CPopulation::ChooseGangOccupation(int gangId) +{ + return CGangs::ChooseGangPedModel(gangId); +} + +void +CPopulation::DealWithZoneChange(eLevelName oldLevel, eLevelName newLevel, bool forceIndustrialZone) +{ +} + +void +CPopulation::Update(bool addPeds) +{ + if (!CReplay::IsPlayingBack()) { + ManagePopulation(); + RemovePedsIfThePoolGetsFull(); + MoveCarsAndPedsOutOfAbandonedZones(); + if (m_CountDownToPedsAtStart != 0) { + if (--m_CountDownToPedsAtStart == 0) + GeneratePedsAtStartOfGame(); + } else { + ms_nTotalCivPeds = ms_nNumCivFemale + ms_nNumCivMale; + ms_nTotalGangPeds = ms_nNumGang9 + ms_nNumGang8 + ms_nNumGang7 + + ms_nNumGang6 + ms_nNumGang5 + ms_nNumGang4 + ms_nNumGang3 + + ms_nNumGang2 + ms_nNumGang1; + ms_nTotalPeds = ms_nNumDummy + ms_nNumEmergency + ms_nNumCop + + ms_nTotalGangPeds + ms_nNumCivFemale + ms_nNumCivMale; + ms_nTotalPeds -= ms_nTotalCarPassengerPeds; + if (!CCutsceneMgr::IsRunning() && addPeds) { + float pcdm = PedCreationDistMultiplier(); + AddToPopulation(pcdm * (MIN_CREATION_DIST * TheCamera.GenerationDistMultiplier), + pcdm * ((MIN_CREATION_DIST + CREATION_RANGE) * TheCamera.GenerationDistMultiplier), + pcdm * (MIN_CREATION_DIST + CREATION_RANGE) * OFFSCREEN_CREATION_MULT - CREATION_RANGE, + pcdm * (MIN_CREATION_DIST + CREATION_RANGE) * OFFSCREEN_CREATION_MULT); + } + } + } +} + +void +CPopulation::GeneratePedsAtStartOfGame() +{ + for (int i = 0; i < 100; i++) { + ms_nTotalCivPeds = ms_nNumCivFemale + ms_nNumCivMale; + ms_nTotalGangPeds = ms_nNumGang9 + ms_nNumGang8 + ms_nNumGang7 + + ms_nNumGang6 + ms_nNumGang5 + ms_nNumGang4 + + ms_nNumGang3 + ms_nNumGang2 + ms_nNumGang1; + ms_nTotalPeds = ms_nNumDummy + ms_nNumEmergency + ms_nNumCop + + ms_nTotalGangPeds + ms_nNumCivFemale + ms_nNumCivMale; + ms_nTotalPeds -= ms_nTotalCarPassengerPeds; + + // Min dist is 10.0f only for start of the game (naturally) + AddToPopulation(10.0f, PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE), + 10.0f, PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE)); + } +} + +// More speed = wider area to spawn peds +float +CPopulation::PedCreationDistMultiplier() +{ + CVehicle *veh = FindPlayerVehicle(); + if (!veh) + return 1.0f; + + float vehSpeed = veh->m_vecMoveSpeed.Magnitude2D(); + return Clamp(vehSpeed - 0.1f + 1.0f, 1.0f, 1.5f); +} + +CPed* +CPopulation::AddPed(ePedType pedType, uint32 miOrCopType, CVector const &coors, int32 modifier) +{ + switch (pedType) { + case PEDTYPE_CIVMALE: + case PEDTYPE_CIVFEMALE: + { + CCivilianPed *ped = new CCivilianPed(pedType, miOrCopType); + ped->SetPosition(coors); + ped->SetOrientation(0.0f, 0.0f, 0.0f); + CWorld::Add(ped); + if (ms_bGivePedsWeapons) { + eWeaponType weapon; + + switch (CGeneral::GetRandomNumber() & 3) { + case 0: + weapon = WEAPONTYPE_COLT45; + break; + case 1: + weapon = WEAPONTYPE_NIGHTSTICK; + break; + case 2: + weapon = WEAPONTYPE_GOLFCLUB; + break; + case 3: + weapon = WEAPONTYPE_TEC9; + break; + default: + break; + } + if (weapon != WEAPONTYPE_UNARMED) { + ped->GiveDelayedWeapon(weapon, 25001); + ped->SetCurrentWeapon(CWeaponInfo::GetWeaponInfo(weapon)->m_nWeaponSlot); + } + } + return ped; + } + case PEDTYPE_COP: + { + CCopPed *ped = new CCopPed((eCopType)miOrCopType, modifier); + ped->SetPosition(coors); + ped->SetOrientation(0.0f, 0.0f, 0.0f); + CWorld::Add(ped); + return ped; + } + case PEDTYPE_GANG1: + case PEDTYPE_GANG2: + case PEDTYPE_GANG3: + case PEDTYPE_GANG4: + case PEDTYPE_GANG5: + case PEDTYPE_GANG6: + case PEDTYPE_GANG7: + case PEDTYPE_GANG8: + case PEDTYPE_GANG9: + { + CCivilianPed *ped = new CCivilianPed(pedType, miOrCopType); + ped->SetPosition(coors); + ped->SetOrientation(0.0f, 0.0f, 0.0f); + CWorld::Add(ped); + + eWeaponType weapon; + if (CGeneral::GetRandomNumberInRange(0, 100) >= 50) + weapon = (eWeaponType)CGangs::GetGangInfo(pedType - PEDTYPE_GANG1)->m_Weapon2; + else + weapon = (eWeaponType)CGangs::GetGangInfo(pedType - PEDTYPE_GANG1)->m_Weapon1; + + ped->GiveDelayedWeapon(weapon, 25001); + ped->SetCurrentWeapon(CWeaponInfo::GetWeaponInfo(weapon)->m_nWeaponSlot); + return ped; + } + case PEDTYPE_EMERGENCY: + { + CEmergencyPed *ped = new CEmergencyPed(PEDTYPE_EMERGENCY); + ped->SetPosition(coors); + ped->SetOrientation(0.0f, 0.0f, 0.0f); + CWorld::Add(ped); + return ped; + } + case PEDTYPE_FIREMAN: + { + CEmergencyPed *ped = new CEmergencyPed(PEDTYPE_FIREMAN); + ped->SetPosition(coors); + ped->SetOrientation(0.0f, 0.0f, 0.0f); + CWorld::Add(ped); + return ped; + } + case PEDTYPE_CRIMINAL: + case PEDTYPE_PROSTITUTE: + { + CCivilianPed *ped = new CCivilianPed(pedType, miOrCopType); + ped->SetPosition(coors); + ped->SetOrientation(0.0f, 0.0f, 0.0f); + CWorld::Add(ped); + return ped; + } + default: + Error("Unknown ped type, AddPed, Population.cpp"); + return nil; + } +} + +void +CPopulation::AddToPopulation(float minDist, float maxDist, float minDistOffScreen, float maxDistOffScreen) +{ + uint32 pedTypeToAdd; + int32 modelToAdd; + int pedAmount; + + CZoneInfo zoneInfo; + int32 man = -1, woman = -1; + CPed *gangLeader = nil; + bool addCop = false; + bool isSecurityGuard = false; + bool forceAddingCop = false; + CPlayerInfo *playerInfo = &CWorld::Players[CWorld::PlayerInFocus]; + CVector playerCentreOfWorld = FindPlayerCentreOfWorld(CWorld::PlayerInFocus); + CTheZones::GetZoneInfoForTimeOfDay(&playerCentreOfWorld, &zoneInfo); + CWanted *wantedInfo = playerInfo->m_pPed->m_pWanted; + + if (wantedInfo->GetWantedLevel() > 2) { + if (!CGame::IsInInterior() && (CGeneral::GetRandomNumber() % 32 == 0) && FindPlayerVehicle()) + forceAddingCop = true; + + uint32 maxCops = CGame::IsInInterior() ? wantedInfo->m_MaxCops * 1.6f : wantedInfo->m_MaxCops; + if ((ms_nNumCop < maxCops || forceAddingCop) && + (!playerInfo->m_pPed->bInVehicle && + (CCarCtrl::NumLawEnforcerCars >= wantedInfo->m_MaximumLawEnforcerVehicles + || CCarCtrl::NumRandomCars >= playerInfo->m_nTrafficMultiplier * CCarCtrl::CarDensityMultiplier + || CCarCtrl::NumFiretrucksOnDuty + CCarCtrl::NumAmbulancesOnDuty + CCarCtrl::NumParkedCars + + CCarCtrl::NumMissionCars + CCarCtrl::NumLawEnforcerCars + CCarCtrl::NumRandomCars >= CCarCtrl::MaxNumberOfCarsInUse) || forceAddingCop)) { + addCop = true; + minDist = PedCreationDistMultiplier() * MIN_CREATION_DIST; + maxDist = PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE); + } + } + float missionAndWeatherMult = -0.8f * Sqrt(CWeather::Rain) + 1.0f; + + // Taxi side mission + if (CTheScripts::IsPlayerOnAMission()) { + CPed *player = FindPlayerPed(); + if (player && player->InVehicle() && player->m_pMyVehicle->IsTaxi()) + missionAndWeatherMult = 1.0f; + } + if (CDarkel::FrenzyOnGoing()) + missionAndWeatherMult = 1.0f; + int selectedMaxPeds = CGame::IsInInterior() ? CPopulation::MaxNumberOfPedsInUseInterior : CPopulation::MaxNumberOfPedsInUse; + + // Yeah, float + float maxPossiblePedsForArea = (zoneInfo.pedDensity + zoneInfo.carDensity) * playerInfo->m_fRoadDensity * PedDensityMultiplier + * (CDarkel::FrenzyOnGoing() ? 1.f : CIniFile::PedNumberMultiplier) * missionAndWeatherMult; + maxPossiblePedsForArea = Min(maxPossiblePedsForArea, selectedMaxPeds); + + if (ms_nTotalPeds < maxPossiblePedsForArea || addCop) { + int decisionThreshold = CGeneral::GetRandomNumberInRange(0, 1000); + if (decisionThreshold < zoneInfo.copPedThreshold || addCop) { + pedTypeToAdd = PEDTYPE_COP; + modelToAdd = ChoosePolicePedOccupation(); + } else { + int i = 0; + for (i = 0; i < NUM_GANGS; i++) { + if (decisionThreshold < zoneInfo.gangPedThreshold[i]) { + break; + } + } + + if (i == NUM_GANGS) { + if (CGeneral::GetRandomNumberInRange(0.0f, 1.0f) <= 0.95f) { + modelToAdd = ChooseCivilianOccupation(zoneInfo.pedGroup); + + if (modelToAdd == -1) + return; + pedTypeToAdd = ((CPedModelInfo*)CModelInfo::GetModelInfo(modelToAdd))->m_pedType; + + } else { + ChooseCivilianCoupleOccupations(zoneInfo.pedGroup, man, woman); + if (man == -1 || woman == -1) + return; + pedTypeToAdd = ((CPedModelInfo*)CModelInfo::GetModelInfo(woman))->m_pedType; + } + } else { + pedTypeToAdd = PEDTYPE_GANG1 + i; + + if (IsSecurityGuard((ePedType)pedTypeToAdd)) { + isSecurityGuard = true; + modelToAdd = ChooseGangOccupation(pedTypeToAdd - PEDTYPE_GANG1); + + if (modelToAdd == -1) + return; + pedTypeToAdd = ((CPedModelInfo*)CModelInfo::GetModelInfo(modelToAdd))->m_pedType; + + } + } + } + if (!addCop && m_AllRandomPedsThisType > PEDTYPE_PLAYER1) + pedTypeToAdd = m_AllRandomPedsThisType; + + if (pedTypeToAdd >= PEDTYPE_GANG1 && pedTypeToAdd <= PEDTYPE_GANG9 && !isSecurityGuard) { + minDist += 30.0f; + maxDist += 30.0f; + pedAmount = ComputeRandomisedGangSize(); + } else + pedAmount = 1; + + CVector generatedCoors; + int32 node1, node2; + float randomPos; + bool foundCoors = !!ThePaths.GeneratePedCreationCoors(playerCentreOfWorld.x, playerCentreOfWorld.y, minDist, maxDist, minDistOffScreen, maxDistOffScreen, + &generatedCoors, &node1, &node2, &randomPos, nil); + + if (!foundCoors) + return; + + uint8 nodeSpawnRate = Min(ThePaths.m_pathNodes[node1].spawnRate, ThePaths.m_pathNodes[node2].spawnRate); + int randomRate = CGeneral::GetRandomNumber() & 0xF; + if (randomRate > nodeSpawnRate) + return; + + CPathFind::TakeWidthIntoAccountForCoors(&ThePaths.m_pathNodes[node1], &ThePaths.m_pathNodes[node2], CGeneral::GetRandomNumber(), &generatedCoors.x, &generatedCoors.y); + if (CGame::currArea == AREA_MALL && (pedTypeToAdd == PEDTYPE_CIVMALE || pedTypeToAdd == PEDTYPE_CIVFEMALE || pedTypeToAdd == PEDTYPE_CRIMINAL) && + CGeneral::GetRandomNumberInRange(0.f, 1.f) > 0.5f) { + + PlaceMallPedsAsStationaryGroup(generatedCoors, zoneInfo.pedGroup); + return; + } + + if (pedTypeToAdd >= PEDTYPE_GANG1 && pedTypeToAdd <= PEDTYPE_GANG9 && !isSecurityGuard) { + PlaceGangMembers((ePedType)pedTypeToAdd, pedAmount, generatedCoors); + return; + } + + if (man > -1 && woman > -1) { + PlaceCouple(PEDTYPE_CIVMALE, man, PEDTYPE_CIVFEMALE, woman, generatedCoors); + return; + } + + for (int i = 0; i < pedAmount; ++i) { + + if (pedTypeToAdd == PEDTYPE_COP) { + // Unused code, ChoosePolicePedOccupation returns COP_STREET. Spawning FBI/SWAT/Army done in somewhere else. + if (modelToAdd == COP_STREET) { + if (!CStreaming::HasModelLoaded(MI_COP)) + return; + + } else if (modelToAdd == COP_FBI) { + if (!CStreaming::HasModelLoaded(MI_COP) || !CStreaming::HasModelLoaded(CWeaponInfo::GetWeaponInfo(WEAPONTYPE_MP5)->m_nModelId)) + return; + + } else if (modelToAdd == COP_SWAT) { + if (!CStreaming::HasModelLoaded(MI_SWAT) || !CStreaming::HasModelLoaded(CWeaponInfo::GetWeaponInfo(WEAPONTYPE_UZI)->m_nModelId)) + return; + + } else if (modelToAdd == COP_ARMY) { + if (!CStreaming::HasModelLoaded(MI_ARMY) || + !CStreaming::HasModelLoaded(CWeaponInfo::GetWeaponInfo(WEAPONTYPE_MP5)->m_nModelId) || !CStreaming::HasModelLoaded(CWeaponInfo::GetWeaponInfo(WEAPONTYPE_GRENADE)->m_nModelId)) + return; + } + } else if (!CStreaming::HasModelLoaded(modelToAdd)) { + return; + } + generatedCoors.z += 0.7f; + + // What? How can this not be met? + if (i < pedAmount) { + // rand() + // III leftover, unused + if (gangLeader) { + // Align gang members in formation. (btw i can't be 0 in here) + float offsetMin = i * 0.75f; + float offsetMax = (i + 1.0f) * 0.75f - offsetMin; + float xOffset = CGeneral::GetRandomNumberInRange(offsetMin, offsetMin + offsetMax); + float yOffset = CGeneral::GetRandomNumberInRange(offsetMin, offsetMin + offsetMax); + if (CGeneral::GetRandomNumber() & 1) + xOffset = -xOffset; + if (CGeneral::GetRandomNumber() & 1) + yOffset = -yOffset; + generatedCoors.x = xOffset + gangLeader->GetPosition().x; + generatedCoors.y = yOffset + gangLeader->GetPosition().y; + } + } + if (!CPedPlacement::IsPositionClearForPed(generatedCoors)) + break; + + // Why no love for last gang member?! + if (i + 1 < pedAmount) { + bool foundGround; + float groundZ = CWorld::FindGroundZFor3DCoord(generatedCoors.x, generatedCoors.y, 2.0f + generatedCoors.z, &foundGround) + 0.7f; + if (!foundGround) + return; + + generatedCoors.z = Max(generatedCoors.z, groundZ); + } + bool surfaceAndDistIsOk = true; + if (TheCamera.IsSphereVisible(generatedCoors, 2.0f)) { + if (PedCreationDistMultiplier() * MIN_CREATION_DIST > (generatedCoors - playerCentreOfWorld).Magnitude2D()) + surfaceAndDistIsOk = false; + } + + // Place skaters if only they're on tarmac. + if (((CPedModelInfo*)CModelInfo::GetModelInfo(modelToAdd))->m_pedStatType == PEDSTAT_SKATER) { + CEntity* foundEnt = nil; + CColPoint foundCol; + CWorld::ProcessVerticalLine(generatedCoors + CVector(0.f, 0.f, 2.f), generatedCoors.z - 2.0f, foundCol, foundEnt, true, false, false, false, false, false, nil); + if (foundEnt) { + if (foundCol.surfaceB == SURFACE_TARMAC || foundCol.surfaceB == SURFACE_PAVEMENT) + surfaceAndDistIsOk = true; + else + surfaceAndDistIsOk = false; + + } else { + surfaceAndDistIsOk = false; + } + } + if (!surfaceAndDistIsOk) + break; + CPed *newPed = AddPed((ePedType)pedTypeToAdd, modelToAdd, generatedCoors); + if (forceAddingCop && newPed->m_nPedType == PEDTYPE_COP) + ((CCopPed*)newPed)->m_bThrowsSpikeTrap = true; + + bool gonnaSunbathe = false; + if (CPopulation::IsSunbather(modelToAdd)) { + CEntity* foundEnt = nil; + CColPoint foundCol; + CWorld::ProcessVerticalLine(generatedCoors + CVector(0.f, 0.f, 2.f), generatedCoors.z - 2.0f, foundCol, foundEnt, true, false, false, false, false, false, nil); + if (foundEnt) { + if ((foundCol.surfaceB == SURFACE_CONCRETE_BEACH || foundCol.surfaceB == SURFACE_SAND) + && CClock::GetHours() >= 10 && CClock::GetHours() <= 18 && 0.0f == CWeather::Rain) { + gonnaSunbathe = true; + if (CPedPlacement::IsPositionClearForPed(generatedCoors, 3.0f, ARRAY_SIZE(gSunbatheObstacles), gSunbatheObstacles)) { + for (int j = 0; j < ARRAY_SIZE(gSunbatheObstacles); j++) { + if (gSunbatheObstacles[j] && gSunbatheObstacles[j] != newPed) + gonnaSunbathe = false; + } + } + } + } + } + if (gonnaSunbathe) { + float heading = CGeneral::GetRandomNumberInRange(0.f, 1.f) * TWOPI; + newPed->m_fRotationDest = heading; + newPed->m_fRotationCur = heading; + // unused + // v61 = CGeneral::GetRandomTrueFalse(); + newPed->SetWaitState(WAITSTATE_SUN_BATHE_IDLE, nil); + CVector toyPos(newPed->GetPosition()); + float waterLevel; + if (CWaterLevel::GetGroundLevel(toyPos, &waterLevel, nil, 30.0f)) { + toyPos.z = 0.04f + waterLevel; + CEntity *toy = CWaterLevel::CreateBeachToy(toyPos, BEACHTOY_ANY_TOWEL); + if (toy) + toy->SetHeading(heading); + + if (!(CGeneral::GetRandomNumber() & 3)) { + CWaterLevel::CreateBeachToy(toyPos + CVector(CGeneral::GetRandomNumberInRange(-2.f, 2.f), CGeneral::GetRandomNumberInRange(-2.f, 2.f), 0.f), BEACHTOY_LOTION); + } + } + } else { + newPed->SetWanderPath(CGeneral::GetRandomNumberInRange(0, 8)); + } + + if (i != 0) { + // Gang member + newPed->SetLeader(gangLeader); + + newPed->SetPedState(PED_UNKNOWN); + gangLeader->SetPedState(PED_UNKNOWN); + + } else { + gangLeader = newPed; + } + CVisibilityPlugins::SetClumpAlpha(newPed->GetClump(), 0); + /* + // Pointless, this is already a for loop + if (i + 1 > pedAmount) + break; + if (pedAmount <= 1) + break; */ + } + } +} + +CPed* +CPopulation::AddPedInCar(CVehicle* car, bool isDriver) +{ + const int defaultModel = MI_MALE01; + int miamiViceIndex = 0; + bool imSureThatModelIsLoaded = true; + CVector coors = FindPlayerCoors(); + CZoneInfo zoneInfo; + int pedType; + + // May be eCopType, model index or non-sense(for medic), AddPed knows that by looking to ped type. + int preferredModel; + + CTheZones::GetZoneInfoForTimeOfDay(&coors, &zoneInfo); + switch (car->GetModelIndex()) { + case MI_FIRETRUCK: + preferredModel = 0; + pedType = PEDTYPE_FIREMAN; + break; + case MI_AMBULAN: + preferredModel = 0; + pedType = PEDTYPE_EMERGENCY; + break; + case MI_POLICE: + case MI_PREDATOR: + preferredModel = COP_STREET; + pedType = PEDTYPE_COP; + break; + case MI_ENFORCER: + preferredModel = COP_SWAT; + pedType = PEDTYPE_COP; + break; + case MI_RHINO: + case MI_BARRACKS: + preferredModel = COP_ARMY; + pedType = PEDTYPE_COP; + break; + case MI_FBIRANCH: + preferredModel = COP_FBI; + pedType = PEDTYPE_COP; + break; + default: + if (car->IsTaxi()) { + if (isDriver) { + pedType = PEDTYPE_CIVMALE; + preferredModel = MI_TAXI_D; + break; + } + // fall through if not + } else if (car->GetModelIndex() == MI_VICECHEE) { + if (car->bIsLawEnforcer) { + preferredModel = COP_MIAMIVICE; + pedType = PEDTYPE_COP; + miamiViceIndex = (isDriver ? 2 * CCarCtrl::MiamiViceCycle : 2 * CCarCtrl::MiamiViceCycle + 1); + break; + } + // fall through if not + } + + int gangOfPed = 0; + imSureThatModelIsLoaded = false; + + while (gangOfPed < NUM_GANGS && CGangs::GetGangInfo(gangOfPed)->m_nVehicleMI != car->GetModelIndex()) + gangOfPed++; + + if (gangOfPed < NUM_GANGS) { + pedType = gangOfPed + PEDTYPE_GANG1; + preferredModel = ChooseGangOccupation(gangOfPed); + } else if (gangOfPed == NUM_GANGS) { + CVehicleModelInfo *carModel = ((CVehicleModelInfo *)CModelInfo::GetModelInfo(car->GetModelIndex())); + preferredModel = ChooseCivilianOccupation(zoneInfo.pedGroup); + int i = 15; + for(; i >= 0; i--) { + CPedModelInfo* pedModel = (CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel); + + if (pedModel->GetRwObject()) { + if (!car->IsPassenger(preferredModel) && !car->IsDriver(preferredModel)) { + if (((CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel))->m_carsCanDrive & (1 << carModel->m_vehicleClass)) + break; + } + } + + preferredModel = ChooseNextCivilianOccupation(zoneInfo.pedGroup); + } + if (i == -1) + preferredModel = defaultModel; + + pedType = ((CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel))->m_pedType; + } + break; + } + if (!imSureThatModelIsLoaded && !((CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel))->GetRwObject()) { + preferredModel = defaultModel; + pedType = ((CPedModelInfo*)CModelInfo::GetModelInfo(defaultModel))->m_pedType; + } + + CPed *newPed = CPopulation::AddPed((ePedType)pedType, preferredModel, car->GetPosition(), miamiViceIndex); + newPed->bUsesCollision = false; + + if (newPed->GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) { + newPed->RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(newPed->GetWeapon()->m_eWeaponType)->m_nModelId); + } + + newPed->AddInCarAnims(car, isDriver); + return newPed; +} + +void +CPopulation::MoveCarsAndPedsOutOfAbandonedZones() +{ +} + +void +CPopulation::ConvertAllObjectsToDummyObjects() +{ + uint32 i = CPools::GetObjectPool()->GetSize(); + while(i--) { + CObject *obj = CPools::GetObjectPool()->GetSlot(i); + if (obj) { + if (obj->CanBeDeleted()) + ConvertToDummyObject(obj); + } + } +} + +void +CPopulation::ConvertToRealObject(CDummyObject *dummy) +{ + if (!TestSafeForRealObject(dummy)) + return; + + CObject *obj = new CObject(dummy); + if (!obj) + return; + + CWorld::Remove(dummy); + delete dummy; + CWorld::Add(obj); + + CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(obj->GetModelIndex()); + if (IsGlass(obj->GetModelIndex()) && !mi->m_isArtistGlass) { + obj->bIsVisible = false; + } else if (obj->GetModelIndex() == MI_BUOY) { + obj->SetIsStatic(false); + obj->m_vecMoveSpeed = CVector(0.0f, 0.0f, -0.001f); + obj->bTouchingWater = true; + obj->AddToMovingList(); + } +} + +void +CPopulation::ConvertToDummyObject(CObject *obj) +{ + CDummyObject *dummy = new CDummyObject(obj); + + dummy->GetMatrix() = obj->m_objectMatrix; + dummy->GetMatrix().UpdateRW(); + dummy->UpdateRwFrame(); + + CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(obj->GetModelIndex()); + if (IsGlass(obj->GetModelIndex()) && !mi->m_isArtistGlass) + dummy->bIsVisible = false; + + CWorld::Remove(obj); + delete obj; + CWorld::Add(dummy); +} + +bool +CPopulation::TestRoomForDummyObject(CObject *obj) +{ + int16 collidingObjs; + CWorld::FindObjectsKindaColliding(obj->m_objectMatrix.GetPosition(), obj->GetBoundRadius(), + false, &collidingObjs, 2, nil, false, true, true, false, false); + + return collidingObjs == 0; +} + +bool +CPopulation::TestSafeForRealObject(CDummyObject *dummy) +{ + CPtrNode *ptrNode; + CColModel *dummyCol = dummy->GetColModel(); + + float radius = dummyCol->boundingSphere.radius; + int minX = CWorld::GetSectorIndexX(dummy->GetPosition().x - radius); + if (minX < 0) minX = 0; + int minY = CWorld::GetSectorIndexY(dummy->GetPosition().y - radius); + if (minY < 0) minY = 0; + int maxX = CWorld::GetSectorIndexX(dummy->GetPosition().x + radius); +#ifdef FIX_BUGS + if (maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X - 1; +#else + if (maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X; +#endif + + int maxY = CWorld::GetSectorIndexY(dummy->GetPosition().y + radius); +#ifdef FIX_BUGS + if (maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y - 1; +#else + if (maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y; +#endif + + float colRadius = dummy->GetBoundRadius(); + CVUVECTOR colCentre; + dummy->GetBoundCentre(colCentre); + + static CColPoint aTempColPoints[MAX_COLLISION_POINTS]; + + for (int curY = minY; curY <= maxY; curY++) { + for (int curX = minX; curX <= maxX; curX++) { + CSector *sector = CWorld::GetSector(curX, curY); + + for (ptrNode = sector->m_lists[ENTITYLIST_VEHICLES].first; ptrNode; ptrNode = ptrNode->next) { + CVehicle *veh = (CVehicle*)ptrNode->item; + if (veh->m_scanCode != CWorld::GetCurrentScanCode()) { + if (veh->GetIsTouching(colCentre, colRadius)) { + veh->m_scanCode = CWorld::GetCurrentScanCode(); + if (CCollision::ProcessColModels(dummy->GetMatrix(), *dummyCol, veh->GetMatrix(), *veh->GetColModel(), aTempColPoints, nil, nil) > 0) + return false; + } + } + } + + for (ptrNode = sector->m_lists[ENTITYLIST_VEHICLES_OVERLAP].first; ptrNode; ptrNode = ptrNode->next) { + CVehicle *veh = (CVehicle*)ptrNode->item; + if (veh->m_scanCode != CWorld::GetCurrentScanCode()) { + if (veh->GetIsTouching(colCentre, colRadius)) { + veh->m_scanCode = CWorld::GetCurrentScanCode(); + if (CCollision::ProcessColModels(dummy->GetMatrix(), *dummyCol, veh->GetMatrix(), *veh->GetColModel(), aTempColPoints, nil, nil) > 0) + return false; + } + } + } + } + } + return true; +} + +void +CPopulation::ManagePopulation(void) +{ + int frameMod32 = CTimer::GetFrameCounter() & 31; + CVector playerPos = FindPlayerCentreOfWorld(CWorld::PlayerInFocus); + + // Why this code is here?! Delete temporary objects when they got too far, and convert others to "dummy" objects. (like lamp posts) + int objectPoolSize = CPools::GetObjectPool()->GetSize(); + for (int i = objectPoolSize * frameMod32 / 32; i < objectPoolSize * (frameMod32 + 1) / 32; i++) { + CObject *obj = CPools::GetObjectPool()->GetSlot(i); + if (obj && obj->CanBeDeleted()) { + float objPlayerDist = (obj->GetPosition() - playerPos).Magnitude(); + if (obj->ObjectCreatedBy == TEMP_OBJECT) { + if (obj->GetModelIndex() != MI_ROADWORKBARRIER1 && obj->GetModelIndex() != MI_BEACHBALL) { + if (objPlayerDist > 51.0f || objPlayerDist > 25.0f && !obj->GetIsOnScreen() || CTimer::GetTimeInMilliseconds() > obj->m_nEndOfLifeTime) { + CWorld::Remove(obj); + delete obj; + } + } else if (objPlayerDist > 120.0f) { + CWorld::Remove(obj); + delete obj; + } + + } else if (objPlayerDist > 80.0f && (obj->m_objectMatrix.GetPosition() - playerPos).Magnitude() > 80.0f) { + if (obj->ObjectCreatedBy != CUTSCENE_OBJECT && TestRoomForDummyObject(obj)) { + ConvertToDummyObject(obj); + } + } + } + } + + // Convert them back to real objects. Dummy objects don't have collisions, so they need to be converted. + int dummyPoolSize = CPools::GetDummyPool()->GetSize(); + for (int i = dummyPoolSize * frameMod32 / 32; i < dummyPoolSize * (frameMod32 + 1) / 32; i++) { + CDummy *dummy = CPools::GetDummyPool()->GetSlot(i); + if (dummy && (dummy->m_area == CGame::currArea || dummy->m_area == AREA_EVERYWHERE)) { + if ((dummy->GetPosition() - playerPos).Magnitude() < 80.0f) + ConvertToRealObject((CDummyObject*)dummy); + } + } + + int pedPoolSize = CPools::GetPedPool()->GetSize(); +#ifndef SQUEEZE_PERFORMANCE + for (int poolIndex = pedPoolSize-1; poolIndex >= 0; poolIndex--) { +#else + for (int poolIndex = (pedPoolSize * (frameMod32 + 1) / 32) - 1; poolIndex >= pedPoolSize * frameMod32 / 32; poolIndex--) { +#endif + CPed *ped = CPools::GetPedPool()->GetSlot(poolIndex); + + if (ped && !ped->IsPlayer() && ped->CanBeDeleted() && !ped->bInVehicle) { + uint32 timeSinceDeath = CTimer::GetTimeInMilliseconds() - ped->m_bloodyFootprintCountOrDeathTime; + if (ped->m_nPedState == PED_DEAD && (timeSinceDeath > 30000 || CDarkel::FrenzyOnGoing() && timeSinceDeath > 15000)) + ped->bFadeOut = true; + + if (ped->bFadeOut && CVisibilityPlugins::GetClumpAlpha(ped->GetClump()) == 0) { + RemovePed(ped); + continue; + } + + float dist = (ped->GetPosition() - playerPos).Magnitude2D(); + + bool pedIsFarAway = false; + + if (ped->IsGangMember()) + dist -= 30.0f; + else if (ped->bDeadPedInFrontOfCar && ped->m_vehicleInAccident) + dist = 0.0f; + + if (PedCreationDistMultiplier() * (PED_REMOVE_DIST_SPECIAL * TheCamera.GenerationDistMultiplier) < dist || + (!ped->bCullExtraFarAway && PedCreationDistMultiplier() * PED_REMOVE_DIST * TheCamera.GenerationDistMultiplier < dist)) { + pedIsFarAway = true; + } +#ifndef EXTENDED_OFFSCREEN_DESPAWN_RANGE + else if (PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE) * OFFSCREEN_CREATION_MULT < dist) { + if (CTimer::GetTimeInMilliseconds() > ped->m_nExtendedRangeTimer && !ped->GetIsOnScreen()) { + if (TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_SNIPER + && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_SNIPER_RUNABOUT + && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_CAMERA + && !TheCamera.Cams[TheCamera.ActiveCam].LookingLeft + && !TheCamera.Cams[TheCamera.ActiveCam].LookingRight + && !TheCamera.Cams[TheCamera.ActiveCam].LookingBehind) { + pedIsFarAway = true; + } + } + + } +#endif + else { + ped->m_nExtendedRangeTimer = ped->m_nPedType == PEDTYPE_COP ? CTimer::GetTimeInMilliseconds() + 10000 : CTimer::GetTimeInMilliseconds() + 4000; + } + + if (!pedIsFarAway) + continue; + + if (ped->m_nPedState == PED_DEAD && !ped->bFadeOut) { + CVector pedPos = ped->GetPosition(); + + float randAngle = (uint8) CGeneral::GetRandomNumber() * (3.14f / 128.0f); // Not PI, 3.14 + switch (CGeneral::GetRandomNumber() % 3) { + case 0: + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpOutline1Tex, &pedPos, + 0.9f * Cos(randAngle), 0.9f * Sin(randAngle), 0.9f * Sin(randAngle), -0.9f * Cos(randAngle), + 255, 255, 255, 255, 4.0f, 40000, 1.0f); + break; + case 1: + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpOutline2Tex, &pedPos, + 0.9f * Cos(randAngle), 0.9f * Sin(randAngle), 0.9f * Sin(randAngle), -0.9f * Cos(randAngle), + 255, 255, 255, 255, 4.0f, 40000, 1.0f); + break; + case 2: + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpOutline3Tex, &pedPos, + 0.9f * Cos(randAngle), 0.9f * Sin(randAngle), 0.9f * Sin(randAngle), -0.9f * Cos(randAngle), + 255, 255, 255, 255, 4.0f, 40000, 1.0f); + break; + default: + break; + } + } + if (ped->GetIsOnScreen()) + ped->bFadeOut = true; + else + RemovePed(ped); + } + } +} + +CPed* +CPopulation::AddDeadPedInFrontOfCar(const CVector& pos, CVehicle* pCulprit) +{ + if (TheCamera.IsSphereVisible(pos, 2.0f) && MIN_CREATION_DIST * PedCreationDistMultiplier() > (pos - FindPlayerPed()->GetPosition()).Magnitude2D()) { + return nil; + } + + bool found; + float z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &found) + 1.0f; + if (!found) + return nil; + z = Max(z, pos.z); + if (!CModelInfo::GetModelInfo(MI_MALE01)->GetRwObject()) + return nil; + CPed* pPed = CPopulation::AddPed(PEDTYPE_CIVMALE, MI_MALE01, pos); + pPed->SetDie(); + pPed->m_nPedMoney = 0; + pPed->bDeadPedInFrontOfCar = true; + pPed->m_vehicleInAccident = pCulprit; + pCulprit->RegisterReference((CEntity**)&pPed->m_vehicleInAccident); + CEntity* pEntities[3] = { 0 }; + if (!CPedPlacement::IsPositionClearForPed(pos, 2.0f, 3, pEntities)) { + for (int i = 0; i < 3; i++) { + if (pEntities[i] && pEntities[i] != pCulprit && pEntities[i] != pPed) { + RemovePed(pPed); + return nil; + } + } + } + CColPoint colpts[MAX_COLLISION_POINTS]; + if (CCollision::ProcessColModels(pCulprit->GetMatrix(), *pCulprit->GetColModel(), pPed->GetMatrix(), *pPed->GetColModel(), colpts, nil, nil)) { + RemovePed(pPed); + return nil; + } + CVisibilityPlugins::SetClumpAlpha(pPed->GetClump(), 0); + return pPed; +} + +bool +CPopulation::IsSkateable(CVector const& pos) +{ + CColPoint foundCol; + CEntity* foundEnt = nil; + CWorld::ProcessVerticalLine(pos + CVector(0.f, 0.f, 2.f), pos.z - 2.0f, foundCol, foundEnt, true, false, false, false, false, false, nil); + if (!foundEnt) + return false; + + return foundCol.surfaceB == SURFACE_TARMAC || foundCol.surfaceB == SURFACE_PAVEMENT; +} + +bool +CPopulation::CanJeerAtStripper(int32 model) +{ + return model == MI_WMOBE || model == MI_WMYBE || model == MI_WMOST || model == MI_BMYBB; +} + +void +CPopulation::RemovePedsIfThePoolGetsFull(void) +{ + if ((CTimer::GetFrameCounter() & 7) == 5) { + if (CPools::GetPedPool()->GetNoOfFreeSpaces() < 8) { + CPed *closestPed = nil; + float closestDist = 10000000.0; + int poolSize = CPools::GetPedPool()->GetSize(); + for (int i = poolSize - 1; i >= 0; i--) { + CPed* ped = CPools::GetPedPool()->GetSlot(i); + if (ped && ped->CanBeDeleted()) { + float dist = (TheCamera.GetPosition() - ped->GetPosition()).Magnitude(); + if (dist < closestDist) { + closestDist = dist; + closestPed = ped; + } + } + } + if (closestPed) { + RemovePed(closestPed); + } + } + } +} + +bool +CPopulation::IsMale(int32 model) +{ + switch (model) { + case MI_HMYST: + case MI_HMOST: + case MI_HMYRI: + case MI_HMORI: + case MI_HMYBE: + case MI_HMOBE: + case MI_HMOTR: + case MI_HMYAP: + case MI_HMOCA: + case MI_BMODK: + case MI_BMYKR: + case MI_BMYST: + case MI_BMOST: + case MI_BMYRI: + case MI_BMYBE: + case MI_BMOBE: + case MI_BMYBU: + case MI_BMOTR: + case MI_BMYPI: + case MI_BMYBB: + case MI_WMYCR: + case MI_WMYST: + case MI_WMOST: + case MI_WMYRI: + case MI_WMORI: + case MI_WMYBE: + case MI_WMOBE: + case MI_WMYCW: + case MI_WMYGO: + case MI_WMOGO: + case MI_WMYLG: + case MI_WMYBU: + case MI_WMOBU: + case MI_WMOTR: + case MI_WMYPI: + case MI_WMOCA: + case MI_WMYJG: + case MI_WMYSK: + + // BUG? Why no JMOTO? + return true; + default: + return false; + } +} + +bool +CPopulation::IsFemale(int32 model) +{ + switch (model) { + case MI_HFYST: + case MI_HFOST: + case MI_HFYRI: + case MI_HFORI: + case MI_HFYBE: + case MI_HFOBE: + case MI_HFYBU: + case MI_HFYMD: + case MI_HFYCG: + case MI_HFYPR: + case MI_HFOTR: + case MI_BFYST: + case MI_BFOST: + case MI_BFYRI: + case MI_BFORI: + case MI_BFYBE: + case MI_BFOBE: + case MI_BFYPR: + case MI_BFOTR: + case MI_WFYST: + case MI_WFOST: + case MI_WFYRI: + case MI_WFORI: + case MI_WFYBE: + case MI_WFOBE: + case MI_WFOGO: + case MI_WFYLG: + case MI_WFYBU: + case MI_WFYPR: + case MI_WFOTR: + case MI_WFYJG: + case MI_WFYSK: + case MI_WFYSH: + case MI_WFOSH: + case MI_JFOTO: + return true; + default: + return false; + } +} + +bool +CPopulation::IsSunbather(int32 model) +{ + switch (model) { + case MI_HFYBE: + case MI_HFOBE: + case MI_HMYBE: + case MI_HMOBE: + case MI_BFYBE: + case MI_BMYBE: + case MI_BFOBE: + case MI_BMOBE: + case MI_WFYBE: + case MI_WMYBE: + case MI_WFOBE: + case MI_WMOBE: + return true; + default: + return false; + } +} + +int32 +CPopulation::ComputeRandomisedGangSize(void) +{ + return CGeneral::GetRandomNumberInRange(3, 6); +} + +bool +CPopulation::CanSolicitPlayerInCar(int32 model) +{ + return model == MI_HFYPR || model == MI_BFYPR || model == MI_WFYPR; +} + +bool +CPopulation::CanSolicitPlayerOnFoot(int32 model) +{ + return model == MI_HFYMD || model == MI_HFYCG || model == MI_BFOTR || model == MI_BMOTR || model == MI_WFOTR || model == MI_WMOTR; +} + +bool +CPopulation::IsSecurityGuard(ePedType pedType) +{ + return pedType == PEDTYPE_GANG5; +} + +void +CPopulation::ChooseCivilianCoupleOccupations(int32 group, int32& man, int32& woman) +{ + man = -1; + woman = -1; + + for (int i = 0; i < 8; i++) { + if (man > -1) + break; + + int32 model = ms_pPedGroups[group].models[CGeneral::GetRandomNumberInRange(0, NUMMODELSPERPEDGROUP)]; + if (man == -1 && IsMale(model) && ((CPedModelInfo*)CModelInfo::GetModelInfo(model))->m_pedType == PEDTYPE_CIVMALE) { + man = model; + } + } + + if (man != -1) { + int32 model; + for (int i = 0; i < NUMMODELSPERPEDGROUP; i++) { + model = ms_pPedGroups[group].models[i]; + if (IsFemale(model)) { + CPedModelInfo* womanModelInfo = (CPedModelInfo*)CModelInfo::GetModelInfo(model); + if (womanModelInfo->m_pedType == PEDTYPE_CIVFEMALE) { + CPedModelInfo* manModelInfo = (CPedModelInfo*)CModelInfo::GetModelInfo(man); + + // If both are skater or not, finalize the decision + if (manModelInfo && womanModelInfo) { + if (manModelInfo->m_animGroup == womanModelInfo->m_animGroup) { + if (manModelInfo->m_pedStatType != PEDSTAT_SKATER && womanModelInfo->m_pedStatType != PEDSTAT_SKATER) + break; + + if (manModelInfo->m_pedStatType == PEDSTAT_SKATER && womanModelInfo->m_pedStatType == PEDSTAT_SKATER) + break; + } + } + } + } + } + woman = model; + } +} + +void +CPopulation::PlaceGangMembers(ePedType pedType, int32 pedAmount, CVector const& coors) +{ + if (CGeneral::GetRandomNumberInRange(0.f, 1.f) < 0.333f) { + PlaceGangMembersInFormation(pedType, pedAmount, coors); + } else { + PlaceGangMembersInCircle(pedType, pedAmount, coors); + } +} + +void +CPopulation::PlaceGangMembersInFormation(ePedType pedType, int32 pedAmount, CVector const& coors) +{ + CPed *createdPeds[5]; + + if (!TheCamera.IsSphereVisible(coors, 3.0f) || MIN_CREATION_DIST * PedCreationDistMultiplier() <= (coors - FindPlayerPed()->GetPosition()).Magnitude2D()) { + if (CPedPlacement::IsPositionClearForPed(coors, 3.0f, -1, nil)) { + bool leaderFoundGround; + float leaderGroundZ = CWorld::FindGroundZFor3DCoord(coors.x, coors.y, coors.z, &leaderFoundGround) + 1.0f; + if (leaderFoundGround) { + float finalZ = coors.z > leaderGroundZ ? coors.z : leaderGroundZ; + int leaderModel = ChooseGangOccupation(pedType - PEDTYPE_GANG1); + if (((CPedModelInfo*)CModelInfo::GetModelInfo(leaderModel))->GetRwObject()) { + CPed *leader = AddPed(pedType, leaderModel, CVector(coors.x, coors.y, finalZ)); + if (leader) { + leader->SetObjective(OBJECTIVE_NONE); + leader->SetWanderPath(CGeneral::GetRandomNumberInRange(0, 8)); + leader->bIsLeader = true; + if (CGangs::GetWillAttackPlayerWithCops(pedType)) + leader->bCanAttackPlayerWithCops = true; + + int pedIdx = 1; + createdPeds[0] = leader; + for (int i = 1; i < pedAmount; ++i) { + int memberModel = ChooseGangOccupation(pedType - PEDTYPE_GANG1); + if (!((CPedModelInfo*)CModelInfo::GetModelInfo(memberModel))->GetRwObject()) + continue; + + CPed* memberPed = AddPed(pedType, memberModel, CVector(coors.x, coors.y, finalZ)); + if (!memberPed) + continue; + + memberPed->SetObjective(OBJECTIVE_FOLLOW_CHAR_IN_FORMATION, leader); + memberPed->SetFormation((eFormation)i); + CVector formationPos = memberPed->GetFormationPosition(); + CVector finalFormationPos = formationPos; + bool formationFoundGround; + float formationGroundZ = CWorld::FindGroundZFor3DCoord(formationPos.x, formationPos.y, 1.0f + formationPos.z, &formationFoundGround) + 1.0f; + + finalFormationPos.z = Max(finalFormationPos.z, formationGroundZ); + if (formationFoundGround) { + if (Abs(finalFormationPos.z - leader->GetPosition().z) <= 1.0f) { + if (CWorld::GetIsLineOfSightClear(finalFormationPos, leader->GetPosition(), true, false, false, false, false, false, false)) { + memberPed->SetPosition(finalFormationPos); + createdPeds[pedIdx++] = memberPed; + if (CGangs::GetWillAttackPlayerWithCops(pedType)) + leader->bCanAttackPlayerWithCops = true; + + CVisibilityPlugins::SetClumpAlpha(memberPed->GetClump(), 0); + continue; + } + } + } + RemovePed(memberPed); + } + if (pedIdx >= 3) { + for (int j = 1; j < pedIdx; ++j) + createdPeds[j]->SetLeader(createdPeds[0]); + + } else { + for (int k = 0; k < pedIdx; ++k) { + RemovePed(createdPeds[k]); + } + } + } + } + } + } + } +} + +void +CPopulation::PlaceGangMembersInCircle(ePedType pedType, int32 pedAmount, CVector const& coors) +{ + CPed *createdPeds[5]; + + if (pedAmount < 2) + return; + + float circleSector = TWOPI / pedAmount; + + float circleR = Sqrt(0.5f / (1.0f - Cos(circleSector))); + + if (!TheCamera.IsSphereVisible(coors, circleR) || + MIN_CREATION_DIST * PedCreationDistMultiplier() <= (coors - FindPlayerPed()->GetPosition()).Magnitude2D()) { + + if (CPedPlacement::IsPositionClearForPed(coors, circleR, -1, nil)) { + int pedIdx = 0; + CVector leaderPos; +#ifdef FIX_BUGS + bool createLeader = true; +#endif + + for (int i = 0; i < pedAmount; i++) { + float angleMult = i + CGeneral::GetRandomNumberInRange(-0.2f, 0.2f); + float randomR = circleR + CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * circleR; + float xOffset = randomR * Cos(angleMult * circleSector); + float yOffset = randomR * Sin(angleMult * circleSector); + bool foundGround; + float groundZ = CWorld::FindGroundZFor3DCoord(xOffset + coors.x, yOffset + coors.y, coors.z + 1.0, &foundGround) + 1.0f; + if (foundGround) { + CVector finalPos(coors.x + xOffset, coors.y + yOffset, coors.z > groundZ ? coors.z : groundZ); +#ifndef FIX_BUGS + const bool createLeader = i == 0; +#endif + if (createLeader) + leaderPos = finalPos; + + int gangModel = ChooseGangOccupation(pedType - PEDTYPE_GANG1); + if (((CPedModelInfo*)CModelInfo::GetModelInfo(gangModel))->GetRwObject()) { + CEntity* obstacles[6] = { nil, nil, nil, nil, nil, nil }; + CPedPlacement::IsPositionClearForPed(finalPos, CModelInfo::GetColModel(gangModel)->boundingSphere.radius, ARRAY_SIZE(obstacles), obstacles); + bool foundObstacle = false; + for (int m = 0; m < ARRAY_SIZE(obstacles); m++) { + CEntity* obstacle = obstacles[m]; + if (obstacle) { + int n = 0; + bool obstacleIsHarmless = false; + for (int n = 0; n < pedIdx; n++) { + if (obstacle == createdPeds[n]) + obstacleIsHarmless = true; + } + if (!obstacleIsHarmless) { + foundObstacle = true; + break; + } + } + } + bool memberCanSeeLeader = createLeader ? true : CWorld::GetIsLineOfSightClear(finalPos, leaderPos, true, false, false, false, false, false, false); + + bool notTooHighFromLeader = createLeader ? true : !(Abs(finalPos.z - leaderPos.z) >= 1.0f); + + if (!foundObstacle && memberCanSeeLeader && notTooHighFromLeader) { + CPed* newPed = AddPed(pedType, gangModel, finalPos); + if (newPed) { + createdPeds[pedIdx++] = newPed; + float angle = CGeneral::GetRadianAngleBetweenPoints( + coors.x, coors.y, + finalPos.x, finalPos.y); + newPed->m_fRotationDest = angle; + newPed->m_fRotationCur = angle; + if (CGangs::GetWillAttackPlayerWithCops(pedType)) + newPed->bCanAttackPlayerWithCops = true; + + CVisibilityPlugins::SetClumpAlpha(newPed->GetClump(), 0); +#ifdef FIX_BUGS + createLeader = false; +#endif + } + // No. +#ifndef FIX_BUGS + else + CWorld::Remove(nil); +#endif + } + } + } + } + if (pedIdx >= 3) { + for (int j = 0; j < pedIdx / 2; ++j) { + createdPeds[j]->SetChat(createdPeds[pedIdx - 1 - j], 100000); + createdPeds[pedIdx - 1 - j]->SetChat(createdPeds[j], 100000); + } + + // Make that extra guy in the middle stand there(PED_UNKNOWN locks him) and do nothing :lmao: + if (pedIdx % 2 != 0) { + CPed *tmim = createdPeds[(pedIdx - 1) / 2]; + float angle = CGeneral::GetRadianAngleBetweenPoints( + tmim->GetPosition().x, tmim->GetPosition().y, + createdPeds[0]->GetPosition().x, createdPeds[0]->GetPosition().y); + tmim->SetHeading(angle); + tmim->SetPedState(PED_UNKNOWN); + } + createdPeds[0]->bIsLeader = true; + + for (int l = 1; l < pedIdx; ++l) + createdPeds[l]->SetLeader(createdPeds[0]); + + } else { + for (int k = 0; k < pedIdx; ++k) { + RemovePed(createdPeds[k]); + } + } + } + } +} + +void +CPopulation::PlaceCouple(ePedType manType, int32 manModel, ePedType womanType, int32 womanModel, CVector coors) +{ + // Homosexuality filter!!!! Homophobic R* >>>:( + if (manType != PEDTYPE_CIVMALE || womanType != PEDTYPE_CIVFEMALE) + return; + + if (!TheCamera.IsSphereVisible(coors, 1.5f) || MIN_CREATION_DIST * PedCreationDistMultiplier() <= (coors - FindPlayerPed()->GetPosition()).Magnitude2D()) { + if (CPedPlacement::IsPositionClearForPed(coors, CModelInfo::GetColModel(manModel)->boundingSphere.radius, -1, nil)) { + bool manFoundGround; + float manGroundZ = CWorld::FindGroundZFor3DCoord(coors.x, coors.y, coors.z, &manFoundGround) + 1.0f; + if (manFoundGround) { + CVector correctedManPos = coors; + correctedManPos.z = Max(coors.z, manGroundZ); + if (((CPedModelInfo*)CModelInfo::GetModelInfo(manModel))->GetRwObject()) { + CPed *man = AddPed(PEDTYPE_CIVMALE, manModel, correctedManPos); + if (man) { + man->SetObjective(OBJECTIVE_NONE); + man->SetWanderPath(CGeneral::GetRandomNumberInRange(0, 8)); + man->bIsLeader = true; + CVisibilityPlugins::SetClumpAlpha(man->GetClump(), 0); + + if (((CPedModelInfo*)CModelInfo::GetModelInfo(womanModel))->GetRwObject()) { + CPed* woman = AddPed(PEDTYPE_CIVFEMALE, womanModel, correctedManPos); // will set the correct position later + if (woman) { + woman->SetObjective(OBJECTIVE_FOLLOW_CHAR_IN_FORMATION, man); + woman->SetFormation(FORMATION_RIGHT); + + CVector formationPos = woman->GetFormationPosition(); + CVector womanPos = formationPos; + bool womanFoundGround; + float formationGroundZ = CWorld::FindGroundZFor3DCoord(formationPos.x, formationPos.y, 1.0f + formationPos.z, &womanFoundGround) + 1.0f; + + if (womanFoundGround) { + CVector correctedWomanPos = womanPos; + correctedWomanPos.z = Max(womanPos.z, formationGroundZ); + woman->SetPosition(correctedWomanPos); + + // What's the point of this?? + CEntity* obstacles[3]; + memcpy(obstacles, gCoupleObstacles, sizeof(gCoupleObstacles)); + + CPedPlacement::IsPositionClearForPed(womanPos, CModelInfo::GetColModel(womanModel)->boundingSphere.radius, ARRAY_SIZE(obstacles), obstacles); + for (int i = 0; i < ARRAY_SIZE(obstacles); i++) { + CEntity *obstacle = obstacles[i]; + if (obstacle) { + + // We found a real obstacle, so let's break and we can delete them... + if (obstacle != man && obstacle != woman) + break; + } + if (i == ARRAY_SIZE(obstacles) - 1) { + CVisibilityPlugins::SetClumpAlpha(woman->GetClump(), 0); + return; + } + } + } + RemovePed(woman); + RemovePed(man); + } + } + } + } + } + } + } +} + +// Mostly copy paste of PlaceGangMembersInFormation. +void +CPopulation::PlaceMallPedsAsStationaryGroup(CVector const& coors, int32 group) +{ +#ifdef FIX_BUGS + CPed *createdPeds[6]; +#else + CPed *createdPeds[5]; +#endif + + if (CGame::currArea != AREA_MALL) + return; + + int pedAmount = CGeneral::GetRandomNumberInRange(0, 4) + 3; + + float circleSector = TWOPI / pedAmount; + + float circleR = Sqrt(0.5f / (1.0f - Cos(circleSector))); + + if (!TheCamera.IsSphereVisible(coors, circleR) || + MIN_CREATION_DIST * PedCreationDistMultiplier() <= (coors - FindPlayerPed()->GetPosition()).Magnitude2D()) { + + if (CPedPlacement::IsPositionClearForPed(coors, circleR, -1, nil)) { + int pedIdx = 0; + CVector leaderPos; +#ifdef FIX_BUGS + bool createLeader = true; +#endif + + for (int i = 0; i < pedAmount; i++) { + float angleMult = i + CGeneral::GetRandomNumberInRange(-0.2f, 0.2f); + float randomR = circleR + CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * circleR; + float xOffset = randomR * Cos(angleMult * circleSector); + float yOffset = randomR * Sin(angleMult * circleSector); + bool foundGround; + float groundZ = CWorld::FindGroundZFor3DCoord(xOffset + coors.x, yOffset + coors.y, coors.z + 1.0, &foundGround) + 1.0f; + if (foundGround) { + CVector finalPos(coors.x + xOffset, coors.y + yOffset, coors.z > groundZ ? coors.z : groundZ); + +#ifndef FIX_BUGS + const bool createLeader = i == 0; +#endif + if (createLeader) + leaderPos = finalPos; + + int pedModel = ChooseCivilianOccupation(group); + CPedModelInfo *pedModelInfo = (CPedModelInfo*)CModelInfo::GetModelInfo(pedModel); + + if (pedModelInfo->GetRwObject()) { + CEntity* obstacles[6] = { nil, nil, nil, nil, nil, nil }; + CPedPlacement::IsPositionClearForPed(finalPos, CModelInfo::GetColModel(pedModel)->boundingSphere.radius, ARRAY_SIZE(obstacles), obstacles); + bool foundObstacle = false; + for (int m = 0; m < ARRAY_SIZE(obstacles); m++) { + CEntity* obstacle = obstacles[m]; + if (obstacle) { + int n = 0; + bool obstacleIsHarmless = false; + for (int n = 0; n < pedIdx; n++) { + if (obstacle == createdPeds[n]) + obstacleIsHarmless = true; + } + if (!obstacleIsHarmless) { + foundObstacle = true; + break; + } + } + } + bool memberCanSeeLeader = createLeader ? true : CWorld::GetIsLineOfSightClear(finalPos, leaderPos, true, false, false, false, false, false, false); + + bool notTooHighFromLeader = createLeader ? true : !(Abs(finalPos.z - leaderPos.z) >= 1.0f); + + if (!foundObstacle && memberCanSeeLeader && notTooHighFromLeader) { + CPed *newPed = AddPed(pedModelInfo->m_pedType, pedModel, finalPos); + if (newPed) { + createdPeds[pedIdx++] = newPed; + float angle = CGeneral::GetRadianAngleBetweenPoints( + coors.x, coors.y, + finalPos.x, finalPos.y); + newPed->m_fRotationDest = angle; + newPed->m_fRotationCur = angle; + newPed->m_fearFlags = 0; + CVisibilityPlugins::SetClumpAlpha(newPed->GetClump(), 0); +#ifdef FIX_BUGS + createLeader = false; +#endif + } + // No. +#ifndef FIX_BUGS + else + CWorld::Remove(nil); +#endif + } + } + } + } + if (pedIdx >= 3) { + for (int j = 0; j < pedIdx / 2; ++j) { + createdPeds[j]->SetChat(createdPeds[pedIdx - 1 - j], 100000); + createdPeds[pedIdx - 1 - j]->SetChat(createdPeds[j], 100000); + } + + // Make that extra guy in the middle stand there(PED_UNKNOWN locks him) and do nothing :lmao: + if (pedIdx % 2 != 0) { + CPed *tmim = createdPeds[(pedIdx - 1) / 2]; + float angle = CGeneral::GetRadianAngleBetweenPoints( + tmim->GetPosition().x, tmim->GetPosition().y, + createdPeds[0]->GetPosition().x, createdPeds[0]->GetPosition().y); + tmim->SetHeading(angle); + tmim->SetPedState(PED_UNKNOWN); + } + createdPeds[0]->bIsLeader = true; + + for (int l = 1; l < pedIdx; ++l) + createdPeds[l]->SetLeader(createdPeds[0]); + + } else { + for (int k = 0; k < pedIdx; ++k) { + RemovePed(createdPeds[k]); + } + } + } + } +} diff --git a/src/miami/peds/Population.h b/src/miami/peds/Population.h new file mode 100644 index 00000000..8c58f1b6 --- /dev/null +++ b/src/miami/peds/Population.h @@ -0,0 +1,88 @@ +#pragma once + +#include "Game.h" +#include "PedType.h" + +class CPed; +class CVehicle; +class CDummyObject; +class CObject; + +struct PedGroup +{ + int32 models[NUMMODELSPERPEDGROUP]; +}; + +class CPopulation +{ +public: + static PedGroup ms_pPedGroups[NUMPEDGROUPS]; + static bool ms_bGivePedsWeapons; + static int32 m_AllRandomPedsThisType; + static float PedDensityMultiplier; + static uint32 ms_nTotalMissionPeds; + static int32 MaxNumberOfPedsInUse; + static int32 MaxNumberOfPedsInUseInterior; + static uint32 ms_nNumCivMale; + static uint32 ms_nNumCivFemale; + static uint32 ms_nNumCop; + static bool bZoneChangeHasHappened; + static uint32 ms_nNumEmergency; + static int8 m_CountDownToPedsAtStart; + static uint32 ms_nNumGang1; + static uint32 ms_nNumGang2; + static uint32 ms_nTotalPeds; + static uint32 ms_nNumGang3; + static uint32 ms_nTotalGangPeds; + static uint32 ms_nNumGang4; + static uint32 ms_nTotalCivPeds; + static uint32 ms_nNumGang5; + static uint32 ms_nNumDummy; + static uint32 ms_nNumGang6; + static uint32 ms_nNumGang9; + static uint32 ms_nNumGang7; + static uint32 ms_nNumGang8; + + static uint32 ms_nTotalCarPassengerPeds; + static uint32 NumMiamiViceCops; + + static void Initialise(); + static void Update(bool); + static void LoadPedGroups(); + static void UpdatePedCount(ePedType, bool); + static void DealWithZoneChange(eLevelName oldLevel, eLevelName newLevel, bool); + static CPed *AddPedInCar(CVehicle *car, bool isDriver); + static void RemovePed(CPed *ent); + static int32 ChooseCivilianOccupation(int32); + static int32 ChooseNextCivilianOccupation(int32); + static void ChooseCivilianCoupleOccupations(int32, int32&, int32&); + static int32 ChoosePolicePedOccupation(); + static int32 ChooseGangOccupation(int); + static void GeneratePedsAtStartOfGame(); + static float PedCreationDistMultiplier(); + static CPed *AddPed(ePedType pedType, uint32 mi, CVector const &coors, int32 modifier = 0); + static void AddToPopulation(float, float, float, float); + static void ManagePopulation(void); + static void MoveCarsAndPedsOutOfAbandonedZones(void); + static void ConvertToRealObject(CDummyObject*); + static void ConvertToDummyObject(CObject*); + static void ConvertAllObjectsToDummyObjects(void); + static bool TestRoomForDummyObject(CObject*); + static bool TestSafeForRealObject(CDummyObject*); + static bool IsSkateable(CVector const&); + static bool CanJeerAtStripper(int32 model); + static void RemovePedsIfThePoolGetsFull(void); + static bool IsMale(int32); + static bool IsFemale(int32); + static bool IsSunbather(int32); + static int32 ComputeRandomisedGangSize(void); + static bool CanSolicitPlayerInCar(int32); + static bool CanSolicitPlayerOnFoot(int32); + static bool IsSecurityGuard(ePedType); + static void PlaceGangMembers(ePedType, int32, CVector const&); + static void PlaceGangMembersInFormation(ePedType, int32, CVector const&); + static void PlaceGangMembersInCircle(ePedType, int32, CVector const&); + static void PlaceCouple(ePedType, int32, ePedType, int32, CVector); + static void PlaceMallPedsAsStationaryGroup(CVector const&, int32); + static CPed* AddDeadPedInFrontOfCar(const CVector& pos, CVehicle* pCulprit); +}; diff --git a/src/miami/prof/profiler.cpp b/src/miami/prof/profiler.cpp new file mode 100644 index 00000000..b7ca8e4d --- /dev/null +++ b/src/miami/prof/profiler.cpp @@ -0,0 +1,376 @@ +/* + profiler.cpp + Copyright (C) 2020 Luke Benstead + Copyright (C) 2023, 2024 Ruslan Rostovtsev +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +perf_cntr_event_t profilerEvent = PMCR_PARALLEL_INSTRUCTION_ISSUED_MODE; +// perf_cntr_event_t profilerMode = PMCR_PIPELINE_FREEZE_BY_DCACHE_MISS_MODE; + +FILE *kernel_file = NULL; +static char KERNEL_OUTPUT_FILENAME[128]; +static char SECOND_OUTPUT_FILENAME[128]; +static kthread_t* THREAD; +static volatile bool PROFILER_RUNNING = false; +static volatile bool PROFILER_RECORDING = false; +// static tid_t KERNEL_TID = 0; +// static tid_t SECOND_TID = 0; +static int prof_count = 0; + +#define BASE_ADDRESS 0x8c010000 +#define BUCKET_SIZE 10000 + +#define INTERVAL_IN_MS 1 + +/* Simple hash table of samples. An array of Samples + * but, each sample in that array can be the head of + * a linked list of other samples */ +typedef struct Arc { + uint32_t pc; + uint32_t pr; // Caller return address + uint32_t count; + // tid_t tid; + struct Arc* next; +} Arc; + +static Arc ARCS[BUCKET_SIZE]; + +/* Hashing function for two uint32_ts */ +#define HASH_PAIR(x, y) ((x * 0x1f1f1f1f) ^ y) + +#define BUFFER_SIZE (1024 * 64) // 64K buffer + +const static size_t MAX_ARC_COUNT = BUFFER_SIZE / sizeof(Arc); +Arc arcbuf[MAX_ARC_COUNT]; + +static size_t ARC_COUNT = 0; + +static bool write_samples(tid_t tid, FILE *out); +static void clear_samples(); + +static Arc* new_arc(tid_t tid, uint32_t PC, uint32_t PR, uint32_t counter) { + Arc* s = &arcbuf[ARC_COUNT]; + s->count = counter; + s->pc = PC; + s->pr = PR; + // s->tid = tid; + s->next = NULL; + + ++ARC_COUNT; + + return s; +} + +static void record_thread(tid_t tid, uint32_t PC, uint32_t PR, uint32_t counter) { + uint32_t bucket = HASH_PAIR(PC, PR) % BUCKET_SIZE; + + Arc* s = &ARCS[bucket]; + // profileTick = (profileTick*3 + counter)/4; + + if(s->pc) { + /* Initialized sample in this bucket, + * does it match though? */ + while(s->pc != PC || s->pr != PR) { + if(s->next) { + s = s->next; + } else { + s->next = new_arc(tid, PC, PR, counter); + return; // We're done + } + } + + s->count+=counter; + } else { + /* Initialize this sample */ + s->count = counter; + s->pc = PC; + s->pr = PR; + // s->tid = tid; + s->next = NULL; + // ++ARC_COUNT; + } +} + + + +#define GMON_COOKIE "gmon" +#define GMON_VERSION 1 + +typedef struct { + char cookie[4]; // 'g','m','o','n' + int32_t version; // 1 + char spare[3 * 4]; // Padding +} GmonHeader; + +typedef struct { + uint32_t low_pc; + uint32_t high_pc; + uint32_t hist_size; + uint32_t prof_rate; + char dimen[15]; /* phys. dim., usually "seconds" */ + char dimen_abbrev; /* usually 's' for "seconds" */ +} GmonHistHeader; + +typedef struct { + unsigned char tag; // GMON_TAG_TIME_HIST = 0, GMON_TAG_CG_ARC = 1, GMON_TAG_BB_COUNT = 2 + size_t ncounts; // Number of address/count pairs in this sequence +} GmonBBHeader; + +typedef struct { + uint32_t from_pc; /* address within caller's body */ + uint32_t self_pc; /* address within callee's body */ + uint32_t count; /* number of arc traversals */ +} GmonArc; + +static bool init_sample_file(const char* path) { + + kernel_file = fopen(path, "w"); + if(!kernel_file) { + return false; + } + + /* Write the GMON header */ + + GmonHeader header; + memcpy(&header.cookie[0], GMON_COOKIE, sizeof(header.cookie)); + header.version = 1; + memset(header.spare, '\0', sizeof(header.spare)); + + fwrite(&header, sizeof(header), 1, kernel_file); + + return true; +} + +#define ROUNDDOWN(x,y) (((x)/(y))*(y)) +#define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y)) + +static bool write_samples(tid_t tid, FILE *out) { + /* Appends the samples to the output file in gmon format + * + * We iterate the data twice, first generating arcs, then generating + * basic block counts. While we do that though we calculate the data + * for the histogram so we don't need a third iteration */ + + + // Seek to the end of the file + fseek(out, 0, SEEK_END); + + uint8_t tag = 1; + size_t written = 0; + + /* Write arcs */ + Arc* root = ARCS; + for(int i = 0; i < BUCKET_SIZE; ++i) { + if(root->pc) { + GmonArc arc; + arc.from_pc = root->pr; + arc.self_pc = root->pc; + arc.count = root->count; + + /* Write the root sample if it has a program counter */ + fwrite(&tag, sizeof(tag), 1, out); + fwrite(&arc, sizeof(GmonArc), 1, out); + + ++written; + + /* If there's a next pointer, traverse the list */ + Arc* s = root->next; + while(s) { + arc.from_pc = s->pr; + arc.self_pc = s->pc; + arc.count = s->count; + + /* Write the root sample if it has a program counter */ + fwrite(&tag, sizeof(tag), 1, out); + fwrite(&arc, sizeof(GmonArc), 1, out); + + ++written; + s = s->next; + } + } + + root++; + } + + + dbglog(DBG_INFO, "-- Written %d arcs\n", written); + + return true; +} + +static void prof_thd_timer_hnd(irq_context_t *context) { + + if (PROFILER_RECORDING) { + record_thread(1, context->pc, context->pr, 1); + + if(ARC_COUNT >= MAX_ARC_COUNT) { + PROFILER_RECORDING = 0; + } + } + + // replicate thd timer behaviour + { + /* Get the system time */ + uint64_t now = timer_ms_gettime64(); + + (void)context; + + //printf("timer woke at %d\n", (uint32_t)now); + + thd_schedule(0, now); + timer_primary_wakeup(PROFILER_RECORDING ? 1 : 10); + } +} + + +static void* run(void* args) { + dbglog(DBG_INFO, "-- Entered profiler thread!\n"); + + while(PROFILER_RUNNING){ + auto mask = irq_disable(); + if(!PROFILER_RECORDING) { + if (ARC_COUNT > 0) { + if(!write_samples(1, kernel_file)) { + dbglog(DBG_ERROR, "Error writing samples\n"); + } + fclose(kernel_file); + kernel_file = nullptr; + clear_samples(); + dbglog(DBG_INFO, "-- Profiler thread stopped recording\n"); + } + } else { + uint64_t counter = perf_cntr_count(PRFC1); + assert(counter <= UINT32_MAX); + + perf_cntr_clear(PRFC1); + + perf_cntr_resume(PRFC1); + + dbglog(DBG_INFO, "-- Profiler thread recording..., %d arcs, %u events\n", ARC_COUNT, (unsigned)counter); + } + irq_restore(mask); + + usleep(1000 * 1000); //usleep takes microseconds + } + + dbglog(DBG_INFO, "-- Profiler thread finished!\n"); + + return NULL; +} + +void profiler_init(const char* output) { + + timer_primary_set_callback(prof_thd_timer_hnd); + + /* Store the filenames */ + sprintf(KERNEL_OUTPUT_FILENAME, "%s/kernel_gmon_%d.out", output, ++prof_count); + // sprintf(SECOND_OUTPUT_FILENAME, "%s/second_gmon_%d.out", output, prof_count); + + /* Initialize the file */ + dbglog(DBG_INFO, "Creating profiler samples file for kernel thread...\n"); + if(!init_sample_file(KERNEL_OUTPUT_FILENAME)) { + dbglog(DBG_ERROR, "Can't create %s\n", KERNEL_OUTPUT_FILENAME); + return; + } + + // dbglog(DBG_INFO, "Creating profiler samples file for video thread...\n"); + // if(!init_sample_file(SECOND_OUTPUT_FILENAME)) { + // dbglog(DBG_ERROR, "Can't create %s\n", SECOND_OUTPUT_FILENAME); + // return; + // } + + dbglog(DBG_INFO, "Creating profiler thread...\n"); + // Initialize the samples to zero + memset(ARCS, 0, sizeof(ARCS)); + + PROFILER_RUNNING = true; + THREAD = thd_create(0, run, NULL); + + // /* Lower priority is... er, higher */ + thd_set_prio(THREAD, PRIO_DEFAULT / 2); + + dbglog(DBG_INFO, "Profiler thread started.\n"); +} + +void profiler_start() { + assert(PROFILER_RUNNING); + + if(PROFILER_RECORDING) { + return; + } + + auto mask = irq_disable(); + dbglog(DBG_INFO, "Starting profiling...\n"); + if (profilerEvent != PMCR_INIT_NO_MODE) { + dbglog(DBG_INFO, "Using event profiling...\n"); + perf_cntr_start(PRFC1, profilerEvent, PMCR_COUNT_CPU_CYCLES); + } + PROFILER_RECORDING = true; + clear_samples(); + irq_restore(mask); +} + +static void clear_samples() { + /* Free the samples we've collected to start again */ + + // Wipe the lot + memset(ARCS, 0, sizeof(ARCS)); + ARC_COUNT = 0; +} + +bool profiler_stop() { + if(!PROFILER_RECORDING) { + return false; + } + + bool rv = true; + + auto mask = irq_disable(); + + if (PROFILER_RECORDING) { + dbglog(DBG_INFO, "profiler_stop: Stopping profiling...\n"); + + PROFILER_RECORDING = false; + + if(!write_samples(1, kernel_file)) { + dbglog(DBG_ERROR, "ERROR WRITING SAMPLES (RO filesystem?)!\n"); + rv = false; + } + clear_samples(); + fclose(kernel_file); + kernel_file = nullptr; + } + irq_restore(mask); + + return rv; +} + +bool profiler_recording() { + return PROFILER_RECORDING; +} + +void profiler_clean_up() { + profiler_stop(); // Make sure everything is stopped + + PROFILER_RUNNING = false; + thd_join(THREAD, NULL); + + if(kernel_file != NULL) { + fclose(kernel_file); + } +} diff --git a/src/miami/prof/profiler.h b/src/miami/prof/profiler.h new file mode 100644 index 00000000..fc82be8b --- /dev/null +++ b/src/miami/prof/profiler.h @@ -0,0 +1,27 @@ + +/* + profiler.h + Copyright (C) 2020 Luke Benstead + Copyright (C) 2023 Ruslan Rostovtsev +*/ + +/** \file src/profiler.h + \brief gprof compatible sampling profiler. + + The Dreamcast doesn't have any kind of profiling support from GCC + so this is a cumbersome sampling profiler that runs in a background thread. + Once profiling has begun, the background thread will regularly gather the PC and PR registers stored by + the other threads. + The way thread scheduling works is that when other threads are blocked their current program counter is stored + in a context. If the profiler thread is doing work then all the other threads aren't and so the stored program + counters will be up-to-date. + The profiling thread gathers PC/PR pairs and how often that pairing appears. +*/ + +#pragma once + +void profiler_init(const char* output); +void profiler_start(); +void profiler_stop(); +void profiler_clean_up(); +bool profiler_recording(); diff --git a/src/miami/renderer/2dEffect.h b/src/miami/renderer/2dEffect.h new file mode 100644 index 00000000..8ad2b946 --- /dev/null +++ b/src/miami/renderer/2dEffect.h @@ -0,0 +1,99 @@ +#pragma once + +enum { + EFFECT_LIGHT, + EFFECT_PARTICLE, + EFFECT_ATTRACTOR, + EFFECT_PED_ATTRACTOR, + EFFECT_SUNGLARE +}; + +enum { + LIGHT_ON, + LIGHT_ON_NIGHT, + LIGHT_FLICKER, + LIGHT_FLICKER_NIGHT, + LIGHT_FLASH1, + LIGHT_FLASH1_NIGHT, + LIGHT_FLASH2, + LIGHT_FLASH2_NIGHT, + LIGHT_FLASH3, + LIGHT_FLASH3_NIGHT, + LIGHT_RANDOM_FLICKER, + LIGHT_RANDOM_FLICKER_NIGHT, + LIGHT_SPECIAL, + LIGHT_BRIDGE_FLASH1, + LIGHT_BRIDGE_FLASH2, +}; + +enum { + ATTRACTORTYPE_ICECREAM, + ATTRACTORTYPE_STARE +}; + +enum { + LIGHTFLAG_LOSCHECK = 1, + // same order as CPointLights flags, must start at 2 + LIGHTFLAG_FOG_NORMAL = 2, // can have light and fog + LIGHTFLAG_FOG_ALWAYS = 4, // fog only + LIGHTFLAG_HIDE_OBJECT = 8, // hide the object instead of rendering light (???) + LIGHTFLAG_LONG_DIST = 16, + LIGHTFLAG_FOG = (LIGHTFLAG_FOG_NORMAL|LIGHTFLAG_FOG_ALWAYS) +}; + +class C2dEffect +{ +public: + struct Light { + float dist; + float range; // of pointlight + float size; + float shadowSize; + uint8 lightType; // LIGHT_ + uint8 roadReflection; + uint8 flareType; + uint8 shadowIntensity; + uint8 flags; // LIGHTFLAG_ + RwTexture *corona; + RwTexture *shadow; + }; + struct Particle { + int particleType; + CVector dir; + float scale; + }; + struct Attractor { + CVector dir; + int8 type; + uint8 probability; + }; + struct PedAttractor { + CVector queueDir; + CVector useDir; + int8 type; + }; + + CVector pos; + CRGBA col; + uint8 type; + union { + Light light; + Particle particle; + Attractor attractor; + PedAttractor pedattr; + }; + + C2dEffect(void) {} + void Shutdown(void){ + if(type == EFFECT_LIGHT){ + if(light.corona) + RwTextureDestroy(light.corona); + light.corona = nil; + if(light.shadow) + RwTextureDestroy(light.shadow); + light.shadow = nil; + } + } +}; + +VALIDATE_SIZE(C2dEffect, 0x34); diff --git a/src/miami/renderer/Antennas.cpp b/src/miami/renderer/Antennas.cpp new file mode 100644 index 00000000..5e30aca2 --- /dev/null +++ b/src/miami/renderer/Antennas.cpp @@ -0,0 +1,129 @@ +#include "common.h" + +#include "main.h" +#include "Antennas.h" + +CAntenna CAntennas::aAntennas[NUMANTENNAS]; + +void +CAntennas::Init(void) +{ + int i; + for(i = 0; i < NUMANTENNAS; i++){ + aAntennas[i].active = false; + aAntennas[i].updatedLastFrame = false; + } +} + +// Free antennas that aren't used anymore +void +CAntennas::Update(void) +{ + int i; + + for(i = 0; i < NUMANTENNAS; i++){ + if(aAntennas[i].active && !aAntennas[i].updatedLastFrame) + aAntennas[i].active = false; + aAntennas[i].updatedLastFrame = false; + } +} + +// Add a new one or update an old one +void +CAntennas::RegisterOne(uint32 id, CVector dir, CVector position, float length) +{ + int i, j; + + for(i = 0; i < NUMANTENNAS; i++) + if(aAntennas[i].active && aAntennas[i].id == id) + break; + + if(i >= NUMANTENNAS){ + // not found, register new one + + // find empty slot + for(i = 0; i < NUMANTENNAS; i++) + if(!aAntennas[i].active) + break; + + // there is space + if(i < NUMANTENNAS){ + aAntennas[i].active = true; + aAntennas[i].updatedLastFrame = true; + aAntennas[i].id = id; + aAntennas[i].segmentLength = length/6.0f; + for(j = 0; j < 6; j++){ + aAntennas[i].pos[j] = position + dir*j*aAntennas[i].segmentLength; + aAntennas[i].speed[j] = CVector(0.0f, 0.0f, 0.0f); + } + } + }else{ + // found, update + aAntennas[i].Update(dir, position); + aAntennas[i].updatedLastFrame = true; + } +} + +static RwIm3DVertex vertexbufferA[2]; + +void +CAntennas::Render(void) +{ + int i, j; + + PUSH_RENDERGROUP("CAntennas::Render"); + for(i = 0; i < NUMANTENNAS; i++){ + if(!aAntennas[i].active) + continue; + + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + + for(j = 0; j < 5; j++){ + RwIm3DVertexSetRGBA(&vertexbufferA[0], 200, 200, 200, 100); + RwIm3DVertexSetPos(&vertexbufferA[0], + aAntennas[i].pos[j].x, + aAntennas[i].pos[j].y, + aAntennas[i].pos[j].z); + RwIm3DVertexSetRGBA(&vertexbufferA[1], 200, 200, 200, 100); + RwIm3DVertexSetPos(&vertexbufferA[1], + aAntennas[i].pos[j+1].x, + aAntennas[i].pos[j+1].y, + aAntennas[i].pos[j+1].z); + + // LittleTest(); + if(RwIm3DTransform(vertexbufferA, 2, nil, 0)){ + RwIm3DRenderLine(0, 1); + RwIm3DEnd(); + } + } + } + + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + + POP_RENDERGROUP(); +} + +void +CAntenna::Update(CVector dir, CVector basepos) +{ + int i; + + pos[0] = basepos; + pos[1] = basepos + dir*segmentLength; + + for(i = 2; i < 6; i++){ + CVector basedir = pos[i-1] - pos[i-2]; + CVector newdir = pos[i] - pos[i-1] + // drag along + dir*0.1f + // also drag up a bit for stiffness + speed[i]; // and keep moving + newdir.Normalise(); + newdir *= segmentLength; + CVector newpos = pos[i-1] + (basedir + newdir)/2.0f; + speed[i] = (newpos - pos[i])*0.9f; + pos[i] = newpos; + } +} diff --git a/src/miami/renderer/Antennas.h b/src/miami/renderer/Antennas.h new file mode 100644 index 00000000..47cb1dad --- /dev/null +++ b/src/miami/renderer/Antennas.h @@ -0,0 +1,25 @@ +#pragma once + +class CAntenna +{ +public: + bool active; + bool updatedLastFrame; + uint32 id; + float segmentLength; + CVector pos[6]; + CVector speed[6]; + + void Update(CVector dir, CVector pos); +}; + +class CAntennas +{ + // no need to use game's array + static CAntenna aAntennas[NUMANTENNAS]; +public: + static void Init(void); + static void Update(void); + static void RegisterOne(uint32 id, CVector dir, CVector position, float length); + static void Render(void); +}; diff --git a/src/miami/renderer/Clouds.cpp b/src/miami/renderer/Clouds.cpp new file mode 100644 index 00000000..7ef72b56 --- /dev/null +++ b/src/miami/renderer/Clouds.cpp @@ -0,0 +1,473 @@ +#include "common.h" + +#include "main.h" +#include "Sprite.h" +#include "Sprite2d.h" +#include "General.h" +#include "Game.h" +#include "Coronas.h" +#include "Camera.h" +#include "TxdStore.h" +#include "Weather.h" +#include "Clock.h" +#include "Timer.h" +#include "Timecycle.h" +#include "Renderer.h" +#include "Clouds.h" + +#define SMALLSTRIPHEIGHT 4.0f +#define HORIZSTRIPHEIGHT 48.0f + +RwTexture *gpCloudTex[5]; + +float CClouds::CloudRotation; +uint32 CClouds::IndividualRotation; + +float CClouds::ms_cameraRoll; +float CClouds::ms_horizonZ; +float CClouds::ms_HorizonTilt; +CRGBA CClouds::ms_colourTop; +CRGBA CClouds::ms_colourBottom; +CRGBA CClouds::ms_colourBkGrd; + +void +CClouds::Init(void) +{ + CTxdStore::PushCurrentTxd(); + CTxdStore::SetCurrentTxd(CTxdStore::FindTxdSlot("particle")); + gpCloudTex[0] = RwTextureRead("cloud1", nil); + gpCloudTex[1] = RwTextureRead("cloud2", nil); + gpCloudTex[2] = RwTextureRead("cloud3", nil); + gpCloudTex[3] = RwTextureRead("cloudhilit", nil); + gpCloudTex[4] = RwTextureRead("cloudmasked", nil); + CTxdStore::PopCurrentTxd(); + CloudRotation = 0.0f; +} + +void +CClouds::Shutdown(void) +{ + RwTextureDestroy(gpCloudTex[0]); + gpCloudTex[0] = nil; + RwTextureDestroy(gpCloudTex[1]); + gpCloudTex[1] = nil; + RwTextureDestroy(gpCloudTex[2]); + gpCloudTex[2] = nil; + RwTextureDestroy(gpCloudTex[3]); + gpCloudTex[3] = nil; + RwTextureDestroy(gpCloudTex[4]); + gpCloudTex[4] = nil; +} + +void +CClouds::Update(void) +{ + float s = Sin(TheCamera.Orientation - 0.85f); +#ifdef FIX_BUGS + CloudRotation += CWeather::Wind*s*0.001f*CTimer::GetTimeStepFix(); + IndividualRotation += (CWeather::Wind*CTimer::GetTimeStep()*0.5f + 0.3f*CTimer::GetTimeStepFix()) * 60.0f; +#else + CloudRotation += CWeather::Wind*s*0.001f; + IndividualRotation += (CWeather::Wind*CTimer::GetTimeStep()*0.5f + 0.3f) * 60.0f; +#endif +} + +float StarCoorsX[9] = { 0.0f, 0.05f, 0.13f, 0.4f, 0.7f, 0.6f, 0.27f, 0.55f, 0.75f }; +float StarCoorsY[9] = { 0.0f, 0.45f, 0.9f, 1.0f, 0.85f, 0.52f, 0.48f, 0.35f, 0.2f }; +float StarSizes[9] = { 1.0f, 1.4f, 0.9f, 1.0f, 0.6f, 1.5f, 1.3f, 1.0f, 0.8f }; + +float LowCloudsX[12] = { 1.0f, 0.7f, 0.0f, -0.7f, -1.0f, -0.7f, 0.0f, 0.7f, 0.8f, -0.8f, 0.4f, -0.4f }; +float LowCloudsY[12] = { 0.0f, -0.7f, -1.0f, -0.7f, 0.0f, 0.7f, 1.0f, 0.7f, 0.4f, 0.4f, -0.8f, -0.8f }; +float LowCloudsZ[12] = { 0.0f, 1.0f, 0.5f, 0.0f, 1.0f, 0.3f, 0.9f, 0.4f, 1.3f, 1.4f, 1.2f, 1.7f }; + +float CoorsOffsetX[37] = { + 0.0f, 60.0f, 72.0f, 48.0f, 21.0f, 12.0f, + 9.0f, -3.0f, -8.4f, -18.0f, -15.0f, -36.0f, + -40.0f, -48.0f, -60.0f, -24.0f, 100.0f, 100.0f, + 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, + 100.0f, 100.0f, -30.0f, -20.0f, 10.0f, 30.0f, + 0.0f, -100.0f, -100.0f, -100.0f, -100.0f, -100.0f, -100.0f +}; +float CoorsOffsetY[37] = { + 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, + 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, + 100.0f, 100.0f, 100.0f, 100.0f, -30.0f, 10.0f, + -25.0f, -5.0f, 28.0f, -10.0f, 10.0f, 0.0f, + 15.0f, 40.0f, -100.0f, -100.0f, -100.0f, -100.0f, + -100.0f, -40.0f, -20.0f, 0.0f, 10.0f, 30.0f, 35.0f +}; +float CoorsOffsetZ[37] = { + 2.0f, 1.0f, 0.0f, 0.3f, 0.7f, 1.4f, + 1.7f, 0.24f, 0.7f, 1.3f, 1.6f, 1.0f, + 1.2f, 0.3f, 0.7f, 1.4f, 0.0f, 0.1f, + 0.5f, 0.4f, 0.55f, 0.75f, 1.0f, 1.4f, + 1.7f, 2.0f, 2.0f, 2.3f, 1.9f, 2.4f, + 2.0f, 2.0f, 1.5f, 1.2f, 1.7f, 1.5f, 2.1f +}; + +uint8 BowRed[6] = { 30, 30, 30, 10, 0, 15 }; +uint8 BowGreen[6] = { 0, 15, 30, 30, 0, 0 }; +uint8 BowBlue[6] = { 0, 0, 0, 10, 30, 30 }; + +void +CClouds::Render(void) +{ + int i; + float szx, szy; + RwV3d screenpos; + RwV3d worldpos; + + if(!CGame::CanSeeOutSideFromCurrArea()) + return; + + PUSH_RENDERGROUP("CClouds::Render"); + + CCoronas::SunBlockedByClouds = false; + + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + CSprite::InitSpriteBuffer(); + + float minute = CClock::GetHours()*60 + CClock::GetMinutes() + CClock::GetSeconds()/60.0f; + RwV3d campos = TheCamera.GetPosition(); + + // Moon + float moonfadeout = Abs(minute - 180.0f); // fully visible at 3AM + if((int)moonfadeout < 180){ // fade in/out 3 hours + float coverage = Max(CWeather::Foggyness, CWeather::CloudCoverage); + int brightness = (1.0f - coverage) * (180 - (int)moonfadeout); + RwV3d pos = { 0.0f, -100.0f, 15.0f }; + RwV3dAdd(&worldpos, &campos, &pos); + if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){ + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[2])); + szx *= CCoronas::MoonSize*2.0f + 4.0f; + szy *= CCoronas::MoonSize*2.0f + 4.0f; + CSprite::RenderOneXLUSprite(screenpos.x, screenpos.y, screenpos.z, + szx, szy, brightness, brightness, brightness, 255, 1.0f/screenpos.z, 255); + } + } + + // The R* logo + int starintens = 0; + if(CClock::GetHours() < 22 && CClock::GetHours() > 5) + starintens = 0; + else if(CClock::GetHours() > 22 || CClock::GetHours() < 5) + starintens = 255; + else if(CClock::GetHours() == 22) + starintens = 255 * CClock::GetMinutes()/60.0f; + else if(CClock::GetHours() == 5) + starintens = 255 * (60 - CClock::GetMinutes())/60.0f; + if(starintens != 0){ + float coverage = Max(CWeather::Foggyness, CWeather::CloudCoverage); + int brightness = (1.0f - coverage) * starintens; + + // R + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[0])); + for(i = 0; i < 11; i++){ + RwV3d pos = { 100.0f, 0.0f, 10.0f }; + if(i >= 9) pos.x = -pos.x; + RwV3dAdd(&worldpos, &campos, &pos); + worldpos.y -= 90.0f*StarCoorsX[i%9]; + worldpos.z += 80.0f*StarCoorsY[i%9]; + if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){ + float sz = 0.8f*StarSizes[i%9]; + CSprite::RenderBufferedOneXLUSprite(screenpos.x, screenpos.y, screenpos.z, + szx*sz, szy*sz, brightness, brightness, brightness, 255, 1.0f/screenpos.z, 255); + } + } + CSprite::FlushSpriteBuffer(); + + // * + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[0])); + RwV3d pos = { 100.0f, 0.0f, 10.0f }; + RwV3dAdd(&worldpos, &campos, &pos); + worldpos.y -= 90.0f; + if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){ + brightness *= (CGeneral::GetRandomNumber()&127) / 640.0f + 0.5f; + CSprite::RenderOneXLUSprite(screenpos.x, screenpos.y, screenpos.z, + szx*5.0f, szy*5.0f, brightness, brightness, brightness, 255, 1.0f/screenpos.z, 255); + } + } + + // Low clouds + float lowcloudintensity = 1.0f - Max(Max(CWeather::Foggyness, CWeather::CloudCoverage), CWeather::ExtraSunnyness); + int r = CTimeCycle::GetLowCloudsRed() * lowcloudintensity; + int g = CTimeCycle::GetLowCloudsGreen() * lowcloudintensity; + int b = CTimeCycle::GetLowCloudsBlue() * lowcloudintensity; + for(int cloudtype = 0; cloudtype < 3; cloudtype++){ + for(i = cloudtype; i < 12; i += 3){ + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCloudTex[cloudtype])); + RwV3d pos = { 800.0f*LowCloudsX[i], 800.0f*LowCloudsY[i], 60.0f*LowCloudsZ[i] }; + worldpos.x = campos.x + pos.x; + worldpos.y = campos.y + pos.y; + worldpos.z = 40.0f + pos.z; + if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)) + CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(screenpos.x, screenpos.y, screenpos.z, + szx*320.0f, szy*40.0f, r, g, b, 255, 1.0f/screenpos.z, ms_cameraRoll, 255); + } + CSprite::FlushSpriteBuffer(); + } + + // Fluffy clouds + float rot_sin = Sin(CloudRotation); + float rot_cos = Cos(CloudRotation); + int fluffyalpha = 160 * (1.0f - Max(CWeather::Foggyness, CWeather::ExtraSunnyness)); + if(fluffyalpha != 0){ + static bool bCloudOnScreen[37]; + float sundist, hilight; + + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCloudTex[4])); + for(i = 0; i < 37; i++){ + RwV3d pos = { 2.0f*CoorsOffsetX[i], 2.0f*CoorsOffsetY[i], 40.0f*CoorsOffsetZ[i] + 40.0f }; + worldpos.x = pos.x*rot_cos + pos.y*rot_sin + campos.x; + worldpos.y = pos.x*rot_sin - pos.y*rot_cos + campos.y; + worldpos.z = pos.z; + + if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){ + sundist = Sqrt(sq(screenpos.x-CCoronas::SunScreenX) + sq(screenpos.y-CCoronas::SunScreenY)); + int tr = CTimeCycle::GetFluffyCloudsTopRed(); + int tg = CTimeCycle::GetFluffyCloudsTopGreen(); + int tb = CTimeCycle::GetFluffyCloudsTopBlue(); + int br = CTimeCycle::GetFluffyCloudsBottomRed(); + int bg = CTimeCycle::GetFluffyCloudsBottomGreen(); + int bb = CTimeCycle::GetFluffyCloudsBottomBlue(); + int distLimit = (3*SCREEN_WIDTH)/4; + if(sundist < distLimit){ + hilight = (1.0f - Max(CWeather::Foggyness, CWeather::CloudCoverage)) * (1.0f - sundist/(float)distLimit); + tr = tr*(1.0f-hilight) + 255*hilight; + tg = tg*(1.0f-hilight) + 190*hilight; + tb = tb*(1.0f-hilight) + 190*hilight; + br = br*(1.0f-hilight) + 255*hilight; + bg = bg*(1.0f-hilight) + 190*hilight; + bb = bb*(1.0f-hilight) + 190*hilight; + if(sundist < SCREEN_WIDTH/10) + CCoronas::SunBlockedByClouds = true; + }else + hilight = 0.0f; + CSprite::RenderBufferedOneXLUSprite_Rotate_2Colours(screenpos.x, screenpos.y, screenpos.z, + szx*55.0f, szy*55.0f, + tr, tg, tb, br, bg, bb, 0.0f, -1.0f, + 1.0f/screenpos.z, + (uint16)IndividualRotation/65536.0f * 6.28f + ms_cameraRoll, //was: 65336 + fluffyalpha); + bCloudOnScreen[i] = true; + }else + bCloudOnScreen[i] = false; + } + CSprite::FlushSpriteBuffer(); + + // Highlights + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCloudTex[3])); + + for(i = 0; i < 37; i++){ + RwV3d pos = { 2.0f*CoorsOffsetX[i], 2.0f*CoorsOffsetY[i], 40.0f*CoorsOffsetZ[i] + 40.0f }; + worldpos.x = pos.x*rot_cos + pos.y*rot_sin + campos.x; + worldpos.y = pos.x*rot_sin + pos.y*rot_cos + campos.y; + worldpos.z = pos.z; + if(bCloudOnScreen[i] && CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){ + if(sundist < SCREEN_WIDTH/3){ + CSprite::RenderBufferedOneXLUSprite_Rotate_Aspect(screenpos.x, screenpos.y, screenpos.z, + szx*30.0f, szy*30.0f, + 200*hilight, 0, 0, 255, 1.0f/screenpos.z, + 1.7f - CGeneral::GetATanOfXY(screenpos.x-CCoronas::SunScreenX, screenpos.y-CCoronas::SunScreenY) + CClouds::ms_cameraRoll, 255); + } + } + } + CSprite::FlushSpriteBuffer(); + } + + // Rainbow + if(CWeather::Rainbow != 0.0f){ + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[0])); + for(i = 0; i < 6; i++){ + RwV3d pos = { i*1.5f, 100.0f, 5.0f }; + RwV3dAdd(&worldpos, &campos, &pos); + if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)) + CSprite::RenderBufferedOneXLUSprite(screenpos.x, screenpos.y, screenpos.z, + 2.0f*szx, 50.0*szy, + BowRed[i]*CWeather::Rainbow, BowGreen[i]*CWeather::Rainbow, BowBlue[i]*CWeather::Rainbow, + 255, 1.0f/screenpos.z, 255); + + } + CSprite::FlushSpriteBuffer(); + } + + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + + POP_RENDERGROUP(); +} + +bool +UseDarkBackground(void) +{ + return TheCamera.GetForward().z < -0.9f || gbShowCollisionPolys; +} + +void +CClouds::RenderBackground(int16 topred, int16 topgreen, int16 topblue, + int16 botred, int16 botgreen, int16 botblue, int16 alpha) +{ + PUSH_RENDERGROUP("CClouds::RenderBackground"); + + CVector right = CrossProduct(TheCamera.GetUp(), TheCamera.GetForward()); + right.Normalise(); + float c = right.Magnitude2D(); + if(c > 1.0f) + c = 1.0f; + ms_cameraRoll = Acos(c); + if(right.z < 0.0f) + ms_cameraRoll = -ms_cameraRoll; + + ms_HorizonTilt = SCREEN_WIDTH/2.0f * Tan(ms_cameraRoll); + + if(UseDarkBackground()){ + ms_colourTop.r = 50; + ms_colourTop.g = 50; + ms_colourTop.b = 50; + ms_colourTop.a = 255; + if(gbShowCollisionPolys){ + if(CTimer::GetFrameCounter() & 1){ + ms_colourTop.r = 0; + ms_colourTop.g = 0; + ms_colourTop.b = 0; + }else{ + ms_colourTop.r = 255; + ms_colourTop.g = 255; + ms_colourTop.b = 255; + } + } + ms_colourBottom = ms_colourTop; + CRect r(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + CSprite2d::DrawRect(r, ms_colourBottom, ms_colourBottom, ms_colourTop, ms_colourTop, DRAW_FAR); + }else{ + ms_horizonZ = CSprite::CalcHorizonCoors(); + + int fogr = (topred + 2 * botred) / 3; + int fogg = (topgreen + 2 * botgreen) / 3; + int fogb = (topblue + 2 * botblue) / 3; + + // Draw top/bottom gradient + float gradheight = SCREEN_HEIGHT/2.0f; + + ms_colourTop.r = topred; + ms_colourTop.g = topgreen; + ms_colourTop.b = topblue; + ms_colourTop.a = alpha; + ms_colourBottom.r = botred; + ms_colourBottom.g = botgreen; + ms_colourBottom.b = botblue; + ms_colourBottom.a = alpha; + + float botright = ms_horizonZ - ms_HorizonTilt; + float botleft = ms_horizonZ + ms_HorizonTilt; + float topright = botright - gradheight; + float topleft = botleft - gradheight; + + CSprite2d::DrawAnyRect(0.0f, topleft, SCREEN_WIDTH, topright, 0.0f, botleft, SCREEN_WIDTH, botright, + ms_colourTop, ms_colourTop, ms_colourBottom, ms_colourBottom, DRAW_FAR); + + // draw the small stripe (whatever it's supposed to be) + ms_colourTop.r = fogr; + ms_colourTop.g = fogg; + ms_colourTop.b = fogb; + ms_colourTop.a = alpha; + topright = ms_horizonZ - ms_HorizonTilt; + topleft = ms_horizonZ + ms_HorizonTilt; + botright = topright + SMALLSTRIPHEIGHT; + botleft = topleft + SMALLSTRIPHEIGHT; + CSprite2d::DrawAnyRect(0.0f, topleft, SCREEN_WIDTH, topright, 0.0f, botleft, SCREEN_WIDTH, botright, + ms_colourTop, ms_colourTop, ms_colourTop, ms_colourTop, DRAW_FAR); + + // Only top + if(ms_horizonZ + ms_HorizonTilt - gradheight > 0.0f || + ms_horizonZ - ms_HorizonTilt - gradheight > 0.0f){ + ms_colourTop.r = topred; + ms_colourTop.g = topgreen; + ms_colourTop.b = topblue; + ms_colourTop.a = alpha; + + if(ms_horizonZ - Abs(ms_HorizonTilt) - gradheight > SCREEN_HEIGHT){ + // only top is visible + topleft = 0.0f; + topright = 0.0f; + botleft = SCREEN_HEIGHT; + botright = SCREEN_HEIGHT; + }else{ + botright = ms_horizonZ - ms_HorizonTilt - gradheight; + botleft = ms_horizonZ + ms_HorizonTilt - gradheight; + topright = Min(ms_horizonZ - ms_HorizonTilt - 2*SCREEN_HEIGHT, 0.0f); + topleft = Min(ms_horizonZ + ms_HorizonTilt - 2*SCREEN_HEIGHT, 0.0f); + } + + CSprite2d::DrawAnyRect(0.0f, topleft, SCREEN_WIDTH, topright, 0.0f, botleft, SCREEN_WIDTH, botright, + ms_colourTop, ms_colourTop, ms_colourTop, ms_colourTop, DRAW_FAR); + } + + // Set both to fog colour for RenderHorizon + ms_colourTop.r = fogr; + ms_colourTop.g = fogg; + ms_colourTop.b = fogb; + ms_colourBottom.r = fogr; + ms_colourBottom.g = fogg; + ms_colourBottom.b = fogb; + } + + POP_RENDERGROUP(); +} + +void +CClouds::RenderHorizon(void) +{ + if(UseDarkBackground()) + return; + + PUSH_RENDERGROUP("CClouds::RenderHorizon"); + + ms_colourBottom.a = 230; + ms_colourTop.a = 80; + + float topright = ms_horizonZ - ms_HorizonTilt; + float topleft = ms_horizonZ + ms_HorizonTilt; + float botright = topright + SMALLSTRIPHEIGHT; + float botleft = topleft + SMALLSTRIPHEIGHT; + + CSprite2d::DrawAnyRect(0.0f, topleft, SCREEN_WIDTH, topright, 0.0f, botleft, SCREEN_WIDTH, botright, + ms_colourTop, ms_colourTop, ms_colourBottom, ms_colourBottom, DRAW_FAR); + + + ms_colourBkGrd.r = 128.0f*CTimeCycle::GetAmbientRed(); + ms_colourBkGrd.g = 128.0f*CTimeCycle::GetAmbientGreen(); + ms_colourBkGrd.b = 128.0f*CTimeCycle::GetAmbientBlue(); + ms_colourBkGrd.a = 255; + + float horzstrip = SCREEN_STRETCH_Y(HORIZSTRIPHEIGHT); + topright = botright; + topleft = botleft; + botright = topright + horzstrip; + botleft = topleft + horzstrip; + + CSprite2d::DrawAnyRect(0.0f, topleft, SCREEN_WIDTH, topright, 0.0f, botleft, SCREEN_WIDTH, botright, + ms_colourBottom, ms_colourBottom, ms_colourBkGrd, ms_colourBkGrd, DRAW_FAR); + + + topright = botright; + topleft = botleft; + botright = Max(topright, SCREEN_HEIGHT); + botleft = Max(topleft, SCREEN_HEIGHT); + + CSprite2d::DrawAnyRect(0.0f, topleft, SCREEN_WIDTH, topright, 0.0f, botleft, SCREEN_WIDTH, botright, + ms_colourBkGrd, ms_colourBkGrd, ms_colourBkGrd, ms_colourBkGrd, DRAW_FAR); + + POP_RENDERGROUP(); +} diff --git a/src/miami/renderer/Clouds.h b/src/miami/renderer/Clouds.h new file mode 100644 index 00000000..ef33030b --- /dev/null +++ b/src/miami/renderer/Clouds.h @@ -0,0 +1,23 @@ +#pragma once + +class CClouds +{ +public: + static float CloudRotation; + static uint32 IndividualRotation; + + static float ms_cameraRoll; + static float ms_horizonZ; + static float ms_HorizonTilt; + static CRGBA ms_colourTop; + static CRGBA ms_colourBottom; + static CRGBA ms_colourBkGrd; + + static void Init(void); + static void Shutdown(void); + static void Update(void); + static void Render(void); + static void RenderBackground(int16 topred, int16 topgreen, int16 topblue, + int16 botred, int16 botgreen, int16 botblue, int16 alpha); + static void RenderHorizon(void); +}; diff --git a/src/miami/renderer/Console.cpp b/src/miami/renderer/Console.cpp new file mode 100644 index 00000000..244bfb17 --- /dev/null +++ b/src/miami/renderer/Console.cpp @@ -0,0 +1,96 @@ +#include "common.h" +#include + +#include "Console.h" +#include "Font.h" +#include "Timer.h" + +#define CONSOLE_X_POS (30.0f) +#define CONSOLE_Y_POS (10.0f) +#define CONSOLE_LINE_HEIGHT (12.0f) + +CConsole TheConsole; + +void +CConsole::AddLine(char *s, uint8 r, uint8 g, uint8 b) +{ + char tempstr[MAX_STR_LEN+1]; + + while (strlen(s) > MAX_STR_LEN) { + strncpy(tempstr, s, MAX_STR_LEN); + tempstr[MAX_STR_LEN-1] = '\0'; + s += MAX_STR_LEN - 1; + AddOneLine(tempstr, r, g, b); + } + AddOneLine(s, r, g, b); +} + +void +CConsole::AddOneLine(char *s, uint8 r, uint8 g, uint8 b) +{ + int32 StrIndex = (m_nLineCount + m_nCurrentLine) % MAX_LINES; + + for (int32 i = 0; i < MAX_STR_LEN; i++) { + Buffers[StrIndex][i] = s[i]; + if (s[i] == '\0') break; + } + + uint8 _strNum1 = m_nLineCount; + if (_strNum1 < MAX_LINES) + _strNum1++; + + m_aTimer[StrIndex] = CTimer::GetTimeInMilliseconds(); + Buffers[StrIndex][MAX_STR_LEN-1] = '\0'; + m_aRed[StrIndex] = r; + m_aGreen[StrIndex] = g; + m_aBlue[StrIndex] = b; + + if (_strNum1 >= MAX_LINES) + m_nCurrentLine = (m_nCurrentLine + 1) % MAX_LINES; + else + m_nLineCount = _strNum1; + +} + +void +CConsole::Display() +{ + CFont::SetPropOn(); + CFont::SetBackgroundOff(); + CFont::SetScale(0.6f, 0.6f); + CFont::SetCentreOff(); + CFont::SetRightJustifyOff(); + CFont::SetJustifyOn(); + CFont::SetRightJustifyWrap(0.0f); + CFont::SetBackGroundOnlyTextOff(); + CFont::SetFontStyle(FONT_STANDARD); +#ifndef FIX_BUGS + CFont::SetPropOff(); // not sure why this is here anyway +#endif + CFont::SetWrapx(RsGlobal.width); + + while (m_nLineCount != 0 && CTimer::GetTimeInMilliseconds() - m_aTimer[m_nCurrentLine] > 20000) { + m_nLineCount--; + m_nCurrentLine = (m_nCurrentLine + 1) % MAX_LINES; + } + + for (int16 i = 0; i < m_nLineCount; i++) { + int16 line = (i + m_nCurrentLine) % MAX_LINES; + CFont::SetColor(CRGBA(0, 0, 0, 200)); + CFont::PrintString(CONSOLE_X_POS + 1.0f, CONSOLE_Y_POS + 1.0f + i * CONSOLE_LINE_HEIGHT, Buffers[line]); + CFont::SetColor(CRGBA(m_aRed[line], m_aGreen[line], m_aBlue[line], 200)); + CFont::PrintString(CONSOLE_X_POS, CONSOLE_Y_POS + i * CONSOLE_LINE_HEIGHT, Buffers[line]); + } +} + +void +cprintf(char* format, ...) +{ + char s[256]; + va_list vl1, vl2; + + va_start(vl1, format); + va_copy(vl2, vl1); + vsprintf(s, format, vl1); + TheConsole.AddLine(s, 255, 255, 128); +} diff --git a/src/miami/renderer/Console.h b/src/miami/renderer/Console.h new file mode 100644 index 00000000..9f22236f --- /dev/null +++ b/src/miami/renderer/Console.h @@ -0,0 +1,27 @@ +#pragma once + +class CConsole +{ + enum + { + MAX_LINES = 8, // BUG? only shows 7 + MAX_STR_LEN = 40, + }; + + uint8 m_nLineCount; + uint8 m_nCurrentLine; + wchar Buffers[MAX_LINES][MAX_STR_LEN]; + uint32 m_aTimer[MAX_LINES]; + uint8 m_aRed[MAX_LINES]; + uint8 m_aGreen[MAX_LINES]; + uint8 m_aBlue[MAX_LINES]; +public: + void AddLine(char *s, uint8 r, uint8 g, uint8 b); + void AddOneLine(char *s, uint8 r, uint8 g, uint8 b); + void Display(); + void Init() { m_nCurrentLine = 0; m_nLineCount = 0; } +}; + +extern CConsole TheConsole; + +void cprintf(char*, ...); \ No newline at end of file diff --git a/src/miami/renderer/Coronas.cpp b/src/miami/renderer/Coronas.cpp new file mode 100644 index 00000000..d9bf88d1 --- /dev/null +++ b/src/miami/renderer/Coronas.cpp @@ -0,0 +1,959 @@ +#include "common.h" + +#include "main.h" +#include "General.h" +#include "Entity.h" +#include "RenderBuffer.h" +#include "TxdStore.h" +#include "Camera.h" +#include "Sprite.h" +#include "Timer.h" +#include "World.h" +#include "Weather.h" +#include "Collision.h" +#include "Timecycle.h" +#include "Coronas.h" +#include "PointLights.h" +#include "Shadows.h" +#include "Clock.h" +#include "Bridge.h" + +struct FlareDef +{ + float position; + float size; + int16 red; + int16 green; + int16 blue; + int16 alpha; + int16 texture; +}; + +FlareDef SunFlareDef[] = { + { -0.5f, 15.0f, 50, 50, 0, 200, 1 }, + { -1.0f, 10.0f, 50, 20, 0, 200, 2 }, + { -1.5f, 15.0f, 50, 0, 0, 200, 3 }, + { -2.5f, 25.0f, 50, 0, 0, 200, 1 }, + { 0.5f, 12.5f, 40, 40, 25, 200, 1 }, + { 0.05f, 20.0f, 30, 22, 9, 200, 2 }, + { 1.3f, 7.5f, 50, 30, 9, 200, 3 }, + { 0.0f, 0.0f, 255, 255, 255, 255, 0 } +}; + +FlareDef HeadLightsFlareDef[] = { + { -0.5f, 15.5, 70, 70, 70, 200, 1 }, + { -1.0f, 10.0, 70, 70, 70, 200, 2 }, + { -1.5f, 5.5f, 50, 50, 50, 200, 3 }, + { 0.5f, 12.0f, 50, 50, 50, 200, 1 }, + { 0.05f, 20.0f, 40, 40, 40, 200, 2 }, + { 1.3f, 8.0f, 60, 60, 60, 200, 3 }, + { -2.0f, 12.0f, 50, 50, 50, 200, 1 }, + { -2.3f, 15.0f, 40, 40, 40, 200, 2 }, + { -3.0f, 16.0f, 40, 40, 40, 200, 3 }, + { 0.0f, 0.0f, 255, 255, 255, 255, 0 } +}; + + +RwTexture *gpCoronaTexture[9] = { nil, nil, nil, nil, nil, nil, nil, nil, nil }; + +float CCoronas::LightsMult = 1.0f; +float CCoronas::SunScreenX; +float CCoronas::SunScreenY; +int CCoronas::MoonSize; +bool CCoronas::SunBlockedByClouds; +int CCoronas::bChangeBrightnessImmediately; + +CRegisteredCorona CCoronas::aCoronas[NUMCORONAS]; + +const char aCoronaSpriteNames[][32] = { + "coronastar", + "corona", + "coronamoon", + "coronareflect", + "coronaheadlightline", + "coronahex", + "coronacircle", + "coronaringa", + "streek" +}; + +void +CCoronas::Init(void) +{ + int i; + + CTxdStore::PushCurrentTxd(); + CTxdStore::SetCurrentTxd(CTxdStore::FindTxdSlot("particle")); + + for(i = 0; i < 9; i++) + if(gpCoronaTexture[i] == nil) + gpCoronaTexture[i] = RwTextureRead(aCoronaSpriteNames[i], nil); + + CTxdStore::PopCurrentTxd(); + + for(i = 0; i < NUMCORONAS; i++) + aCoronas[i].id = 0; +} + +void +CCoronas::Shutdown(void) +{ + int i; + for(i = 0; i < 9; i++) + if(gpCoronaTexture[i]){ + RwTextureDestroy(gpCoronaTexture[i]); + gpCoronaTexture[i] = nil; + } +} + +void +CCoronas::Update(void) +{ + int i; + static int LastCamLook = 0; + + LightsMult = Min(LightsMult + 0.03f * CTimer::GetTimeStep(), 1.0f); + + int CamLook = 0; + if(TheCamera.Cams[TheCamera.ActiveCam].LookingLeft) CamLook |= 1; + if(TheCamera.Cams[TheCamera.ActiveCam].LookingRight) CamLook |= 2; + if(TheCamera.Cams[TheCamera.ActiveCam].LookingBehind) CamLook |= 4; + // BUG? + if(TheCamera.GetLookDirection() == LOOKING_BEHIND) CamLook |= 8; + + if(LastCamLook != CamLook) + bChangeBrightnessImmediately = 3; + else + bChangeBrightnessImmediately = Max(bChangeBrightnessImmediately-1, 0); + LastCamLook = CamLook; + + for(i = 0; i < NUMCORONAS; i++) + if(aCoronas[i].id != 0) + aCoronas[i].Update(); +} + +void +CCoronas::RegisterCorona(uint32 id, uint8 red, uint8 green, uint8 blue, uint8 alpha, + const CVector &coors, float size, float drawDist, RwTexture *tex, + int8 flareType, uint8 reflection, uint8 LOScheck, uint8 drawStreak, float someAngle, + bool useNearDist, float nearDist) +{ + int i; + + if(sq(drawDist) < (TheCamera.GetPosition() - coors).MagnitudeSqr2D()) + return; + + if(useNearDist){ + float dist = (TheCamera.GetPosition() - coors).Magnitude(); + if(dist < 35.0f) + return; + if(dist < 50.0f) + alpha *= (dist - 35.0f)/(50.0f - 35.0f); + } + + for(i = 0; i < NUMCORONAS; i++) + if(aCoronas[i].id == id) + break; + + if(i == NUMCORONAS){ + // add a new one + + // find empty slot + for(i = 0; i < NUMCORONAS; i++) + if(aCoronas[i].id == 0) + break; + if(i == NUMCORONAS) + return; // no space + + aCoronas[i].fadeAlpha = 0; + aCoronas[i].offScreen = true; + aCoronas[i].firstUpdate = true; + aCoronas[i].renderReflection = false; + aCoronas[i].lastLOScheck = 0; + aCoronas[i].sightClear = false; + aCoronas[i].hasValue[0] = false; + aCoronas[i].hasValue[1] = false; + aCoronas[i].hasValue[2] = false; + aCoronas[i].hasValue[3] = false; + aCoronas[i].hasValue[4] = false; + aCoronas[i].hasValue[5] = false; + + }else{ + // use existing one + + if(aCoronas[i].fadeAlpha == 0 && alpha == 0){ + // unregister + aCoronas[i].id = 0; + return; + } + } + + aCoronas[i].id = id; + aCoronas[i].red = red; + aCoronas[i].green = green; + aCoronas[i].blue = blue; + aCoronas[i].alpha = alpha; + aCoronas[i].coors = coors; + aCoronas[i].size = size; + aCoronas[i].someAngle = someAngle; + aCoronas[i].registeredThisFrame = true; + aCoronas[i].drawDist = drawDist; + aCoronas[i].texture = tex; + aCoronas[i].flareType = flareType; + aCoronas[i].reflection = reflection; + aCoronas[i].LOScheck = LOScheck; + aCoronas[i].drawStreak = drawStreak; + aCoronas[i].useNearDist = useNearDist; + aCoronas[i].nearDist = nearDist; +} + +void +CCoronas::RegisterCorona(uint32 id, uint8 red, uint8 green, uint8 blue, uint8 alpha, + const CVector &coors, float size, float drawDist, uint8 type, + int8 flareType, uint8 reflection, uint8 LOScheck, uint8 drawStreak, float someAngle, + bool useNearDist, float nearDist) +{ + RegisterCorona(id, red, green, blue, alpha, coors, size, drawDist, + gpCoronaTexture[type], flareType, reflection, LOScheck, drawStreak, someAngle, + useNearDist, nearDist); +} + +void +CCoronas::UpdateCoronaCoors(uint32 id, const CVector &coors, float drawDist, float someAngle) +{ + int i; + + if(sq(drawDist) < (TheCamera.GetPosition() - coors).MagnitudeSqr2D()) + return; + + for(i = 0; i < NUMCORONAS; i++) + if(aCoronas[i].id == id) + break; + + if(i == NUMCORONAS) + return; + + if(aCoronas[i].fadeAlpha == 0) + aCoronas[i].id = 0; // faded out, remove + else{ + aCoronas[i].coors = coors; + aCoronas[i].someAngle = someAngle; + } +} + +static RwIm2DVertex vertexbufferX[2]; + +void +CCoronas::Render(void) +{ + int i, j; + int screenw, screenh; + + PUSH_RENDERGROUP("CCoronas::Render"); + + screenw = RwRasterGetWidth(RwCameraGetRaster(Scene.camera)); + screenh = RwRasterGetHeight(RwCameraGetRaster(Scene.camera)); + + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + + for(i = 0; i < NUMCORONAS; i++){ + for(j = 5; j > 0; j--){ + aCoronas[i].prevX[j] = aCoronas[i].prevX[j-1]; + aCoronas[i].prevY[j] = aCoronas[i].prevY[j-1]; + aCoronas[i].prevRed[j] = aCoronas[i].prevRed[j-1]; + aCoronas[i].prevGreen[j] = aCoronas[i].prevGreen[j-1]; + aCoronas[i].prevBlue[j] = aCoronas[i].prevBlue[j-1]; + aCoronas[i].hasValue[j] = aCoronas[i].hasValue[j-1]; + } + aCoronas[i].hasValue[0] = false; + + if(aCoronas[i].id == 0 || + aCoronas[i].fadeAlpha == 0 && aCoronas[i].alpha == 0) + continue; + + CVector spriteCoors; + float spritew, spriteh; + if(!CSprite::CalcScreenCoors(aCoronas[i].coors, &spriteCoors, &spritew, &spriteh, true)){ + aCoronas[i].offScreen = true; + aCoronas[i].sightClear = false; + }else{ + aCoronas[i].offScreen = false; + + if(spriteCoors.x < 0.0f || spriteCoors.y < 0.0f || + spriteCoors.x > screenw || spriteCoors.y > screenh){ + aCoronas[i].offScreen = true; + aCoronas[i].sightClear = false; + }else{ + if(CTimer::GetTimeInMilliseconds() > aCoronas[i].lastLOScheck + 2000){ + aCoronas[i].lastLOScheck = CTimer::GetTimeInMilliseconds(); + aCoronas[i].sightClear = CWorld::GetIsLineOfSightClear( + aCoronas[i].coors, TheCamera.Cams[TheCamera.ActiveCam].Source, + true, true, false, false, false, true, false); + } + + // add new streak point + if(aCoronas[i].sightClear){ + aCoronas[i].prevX[0] = spriteCoors.x; + aCoronas[i].prevY[0] = spriteCoors.y; + aCoronas[i].prevRed[0] = aCoronas[i].red; + aCoronas[i].prevGreen[0] = aCoronas[i].green; + aCoronas[i].prevBlue[0] = aCoronas[i].blue; + aCoronas[i].hasValue[0] = true; + } + + // if distance too big, break streak + if(aCoronas[i].hasValue[1]){ + if(Abs(aCoronas[i].prevX[0] - aCoronas[i].prevX[1]) > 50.0f || + Abs(aCoronas[i].prevY[0] - aCoronas[i].prevY[1]) > 50.0f) + aCoronas[i].hasValue[0] = false; + } + } + + + if(aCoronas[i].fadeAlpha && spriteCoors.z < aCoronas[i].drawDist){ + float recipz = 1.0f/spriteCoors.z; + float fadeDistance = aCoronas[i].drawDist / 2.0f; + float distanceFade = spriteCoors.z < fadeDistance ? 1.0f : 1.0f - (spriteCoors.z - fadeDistance)/fadeDistance; + int totalFade = aCoronas[i].fadeAlpha * distanceFade; + + if(aCoronas[i].LOScheck) + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + else + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + + // render corona itself + if(aCoronas[i].texture){ + float fogscale = CWeather::Foggyness*Min(spriteCoors.z, 40.0f)/40.0f + 1.0f; + if(CCoronas::aCoronas[i].id == SUN_CORE) + spriteCoors.z = 0.95f * RwCameraGetFarClipPlane(Scene.camera); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(aCoronas[i].texture)); + spriteCoors.z -= aCoronas[i].nearDist; + + if(aCoronas[i].texture == gpCoronaTexture[8]){ + // what's this? + float f = 1.0f - aCoronas[i].someAngle*2.0f/PI; + float wscale = 6.0f*sq(sq(sq(f))) + 0.5f; + float hscale = 0.35f - (wscale - 0.5f) * 0.06f; + hscale = Max(hscale, 0.15f); + + CSprite::RenderOneXLUSprite(spriteCoors.x, spriteCoors.y, spriteCoors.z, + spritew * aCoronas[i].size * wscale, + spriteh * aCoronas[i].size * fogscale * hscale, + CCoronas::aCoronas[i].red / fogscale, + CCoronas::aCoronas[i].green / fogscale, + CCoronas::aCoronas[i].blue / fogscale, + totalFade, + recipz, + 255); + }else{ + CSprite::RenderOneXLUSprite_Rotate_Aspect( + spriteCoors.x, spriteCoors.y, spriteCoors.z, + spritew * aCoronas[i].size * fogscale, + spriteh * aCoronas[i].size * fogscale, + CCoronas::aCoronas[i].red / fogscale, + CCoronas::aCoronas[i].green / fogscale, + CCoronas::aCoronas[i].blue / fogscale, + totalFade, + recipz, + 20.0f * recipz, + 255); + } + } + + // render flares + if(aCoronas[i].flareType != FLARE_NONE){ + FlareDef *flare; + + switch(aCoronas[i].flareType){ + case FLARE_SUN: flare = SunFlareDef; break; + case FLARE_HEADLIGHTS: flare = HeadLightsFlareDef; break; + default: assert(0); + } + + for(; flare->texture; flare++){ + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[flare->texture + 4])); + CSprite::RenderOneXLUSprite( + (spriteCoors.x - (screenw/2)) * flare->position + (screenw/2), + (spriteCoors.y - (screenh/2)) * flare->position + (screenh/2), + spriteCoors.z, + 4.0f*flare->size * spritew/spriteh, + 4.0f*flare->size, + (flare->red * aCoronas[i].red)>>8, + (flare->green * aCoronas[i].green)>>8, + (flare->blue * aCoronas[i].blue)>>8, + (totalFade * flare->alpha)>>8, + recipz, 255); + } + } + } + } + } + + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + + // streaks + for(i = 0; i < NUMCORONAS; i++){ + if(aCoronas[i].id == 0 || !aCoronas[i].drawStreak) + continue; + + for(j = 0; j < 5; j++){ + if(!aCoronas[i].hasValue[j] || !aCoronas[i].hasValue[j+1]) + continue; + + int alpha1 = (float)(6 - j) / 6 * 128; + int alpha2 = (float)(6 - (j+1)) / 6 * 128; + + RwIm2DVertexSetScreenX(&vertexbufferX[0], aCoronas[i].prevX[j]); + RwIm2DVertexSetScreenY(&vertexbufferX[0], aCoronas[i].prevY[j]); + RwIm2DVertexSetIntRGBA(&vertexbufferX[0], aCoronas[i].prevRed[j] * alpha1 / 256, aCoronas[i].prevGreen[j] * alpha1 / 256, aCoronas[i].prevBlue[j] * alpha1 / 256, 255); + RwIm2DVertexSetScreenX(&vertexbufferX[1], aCoronas[i].prevX[j+1]); + RwIm2DVertexSetScreenY(&vertexbufferX[1], aCoronas[i].prevY[j+1]); + RwIm2DVertexSetIntRGBA(&vertexbufferX[1], aCoronas[i].prevRed[j+1] * alpha2 / 256, aCoronas[i].prevGreen[j+1] * alpha2 / 256, aCoronas[i].prevBlue[j+1] * alpha2 / 256, 255); + +#ifdef FIX_BUGS + RwIm2DVertexSetScreenZ(&vertexbufferX[0], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&vertexbufferX[0], RwCameraGetNearClipPlane(Scene.camera)); + RwIm2DVertexSetRecipCameraZ(&vertexbufferX[0], 1.0f/RwCameraGetNearClipPlane(Scene.camera)); + RwIm2DVertexSetScreenZ(&vertexbufferX[1], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&vertexbufferX[1], RwCameraGetNearClipPlane(Scene.camera)); + RwIm2DVertexSetRecipCameraZ(&vertexbufferX[1], 1.0f/RwCameraGetNearClipPlane(Scene.camera)); +#endif + + RwIm2DRenderLine(vertexbufferX, 2, 0, 1); + } + } + + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + + POP_RENDERGROUP(); +} + +void +CCoronas::RenderReflections(void) +{ + int i; + CColPoint point; + CEntity *entity; + + if(CWeather::WetRoads > 0.0f){ + PUSH_RENDERGROUP("CCoronas::RenderReflections"); + + CSprite::InitSpriteBuffer(); + + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[3])); + + for(i = 0; i < NUMCORONAS; i++){ + if(aCoronas[i].id == 0 || + aCoronas[i].fadeAlpha == 0 && aCoronas[i].alpha == 0 || + aCoronas[i].reflection == 0) + continue; + + // check if we want a reflection on this corona + if(aCoronas[i].renderReflection){ + if(((CTimer::GetFrameCounter() + i) & 0xF) == 0 && + CWorld::ProcessVerticalLine(aCoronas[i].coors, -1000.0f, point, entity, true, false, false, false, true, false, nil)) + aCoronas[i].heightAboveRoad = aCoronas[i].coors.z - point.point.z; + }else{ + if(CWorld::ProcessVerticalLine(aCoronas[i].coors, -1000.0f, point, entity, true, false, false, false, true, false, nil)){ + aCoronas[i].heightAboveRoad = aCoronas[i].coors.z - point.point.z; + aCoronas[i].renderReflection = true; + } + } + + // Don't draw if reflection is too high + if(aCoronas[i].renderReflection && aCoronas[i].heightAboveRoad < 20.0f){ + // don't draw if camera is below road + if(CCoronas::aCoronas[i].coors.z - aCoronas[i].heightAboveRoad > TheCamera.GetPosition().z) + continue; + + CVector coors = aCoronas[i].coors; + coors.z -= 2.0f*aCoronas[i].heightAboveRoad; + + CVector spriteCoors; + float spritew, spriteh; + if(CSprite::CalcScreenCoors(coors, &spriteCoors, &spritew, &spriteh, true)) { + float drawDist = 0.75f * aCoronas[i].drawDist; + drawDist = Min(drawDist, 55.0f); + if(spriteCoors.z < drawDist){ + float fadeDistance = drawDist / 2.0f; + float distanceFade = spriteCoors.z < fadeDistance ? 1.0f : 1.0f - (spriteCoors.z - fadeDistance)/fadeDistance; + distanceFade = Clamp(distanceFade, 0.0f, 1.0f); + float recipz = 1.0f/RwCameraGetNearClipPlane(Scene.camera); + float heightFade = (20.0f - aCoronas[i].heightAboveRoad)/20.0f; + int intensity = distanceFade*heightFade * 230.0 * CWeather::WetRoads; + + CSprite::RenderBufferedOneXLUSprite( +#ifdef FIX_BUGS + spriteCoors.x, spriteCoors.y, spriteCoors.z, +#else + spriteCoors.x, spriteCoors.y, RwIm2DGetNearScreenZ(), +#endif + spritew * aCoronas[i].size * 0.75f, + spriteh * aCoronas[i].size * 2.0f, + (intensity * CCoronas::aCoronas[i].red)>>8, + (intensity * CCoronas::aCoronas[i].green)>>8, + (intensity * CCoronas::aCoronas[i].blue)>>8, + 255, + recipz, + 255); + } + } + } + } + CSprite::FlushSpriteBuffer(); + + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + + POP_RENDERGROUP(); + }else{ + for(i = 0; i < NUMCORONAS; i++) + aCoronas[i].renderReflection = false; + } +} + +void +CCoronas::RenderSunReflection(void) +{ + float sunZDir = CTimeCycle::GetSunDirection().z; + if(sunZDir > -0.05f){ + float intensity = (0.3f - Abs(sunZDir - 0.25f))/0.3f * + (1.0f - CWeather::CloudCoverage) * + (1.0f - CWeather::Foggyness) * + (1.0f - CWeather::Wind); + if(intensity > 0.0f){ + int r = (CTimeCycle::GetSunCoreRed() + CTimeCycle::GetSunCoronaRed())*intensity*0.25f; + int g = (CTimeCycle::GetSunCoreGreen() + CTimeCycle::GetSunCoronaGreen())*intensity*0.25f; + int b = (CTimeCycle::GetSunCoreBlue() + CTimeCycle::GetSunCoronaBlue())*intensity*0.25f; + + CVector sunPos = 40.0f*CTimeCycle::GetSunDirection() + TheCamera.GetPosition(); + sunPos.z = 0.5f*CWeather::Wind + 6.1f; + CVector sunDir = CTimeCycle::GetSunDirection(); + sunDir.z = 0.0; + sunDir.Normalise(); + + TempBufferIndicesStored = 6; + TempBufferRenderIndexList[0] = 2; + TempBufferRenderIndexList[1] = 1; + TempBufferRenderIndexList[2] = 0; + TempBufferRenderIndexList[3] = 2; + TempBufferRenderIndexList[4] = 3; + TempBufferRenderIndexList[5] = 1; + + // 60 unit square in sun direction + TempBufferVerticesStored = 4; + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[0], r, g, b, 255); + RwIm3DVertexSetPos(&TempBufferRenderVertices[0], + sunPos.x + 30.0f*sunDir.y, + sunPos.y - 30.0f*sunDir.x, + sunPos.z); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[1], r, g, b, 255); + RwIm3DVertexSetPos(&TempBufferRenderVertices[1], + sunPos.x - 30.0f*sunDir.y, + sunPos.y + 30.0f*sunDir.x, + sunPos.z); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[2], r, g, b, 255); + RwIm3DVertexSetPos(&TempBufferRenderVertices[2], + sunPos.x + 60.0f*sunDir.x + 30.0f*sunDir.y, + sunPos.y + 60.0f*sunDir.y - 30.0f*sunDir.x, + sunPos.z); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[3], r, g, b, 255); + RwIm3DVertexSetPos(&TempBufferRenderVertices[3], + sunPos.x + 60.0f*sunDir.x - 30.0f*sunDir.y, + sunPos.y + 60.0f*sunDir.y + 30.0f*sunDir.x, + sunPos.z); + + RwIm3DVertexSetU(&TempBufferRenderVertices[0], 0.0f); + RwIm3DVertexSetV(&TempBufferRenderVertices[0], 1.0f); + RwIm3DVertexSetU(&TempBufferRenderVertices[1], 1.0f); + RwIm3DVertexSetV(&TempBufferRenderVertices[1], 1.0f); + RwIm3DVertexSetU(&TempBufferRenderVertices[2], 0.0f); + RwIm3DVertexSetV(&TempBufferRenderVertices[2], 0.5f); + RwIm3DVertexSetU(&TempBufferRenderVertices[3], 1.0f); + RwIm3DVertexSetV(&TempBufferRenderVertices[3], 0.5f); + + int timeInc = 0; + int sideInc = 0; + int fwdInc = 0; + for(int i = 0; i < 20; i++){ + TempBufferRenderIndexList[TempBufferIndicesStored + 0] = TempBufferVerticesStored; + TempBufferRenderIndexList[TempBufferIndicesStored + 1] = TempBufferVerticesStored-1; + TempBufferRenderIndexList[TempBufferIndicesStored + 2] = TempBufferVerticesStored-2; + TempBufferRenderIndexList[TempBufferIndicesStored + 3] = TempBufferVerticesStored; + TempBufferRenderIndexList[TempBufferIndicesStored + 4] = TempBufferVerticesStored+1; + TempBufferRenderIndexList[TempBufferIndicesStored + 5] = TempBufferVerticesStored-1; + TempBufferIndicesStored += 6; + + // What a weird way to do it... + float fwdLen = fwdInc/20 + 60; + float sideLen = sideInc/20 + 30; + sideLen += 10.0f*Sin((float)(CTimer::GetTimeInMilliseconds()+timeInc & 0x7FF)/0x800*TWOPI); + timeInc += 900; + sideInc += 970; + fwdInc += 1440; + + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+0], r, g, b, 255); + RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+0], + sunPos.x + fwdLen*sunDir.x + sideLen*sunDir.y, + sunPos.y + fwdLen*sunDir.y - sideLen*sunDir.x, + sunPos.z); + + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+1], r, g, b, 255); + RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+1], + sunPos.x + fwdLen*sunDir.x - sideLen*sunDir.y, + sunPos.y + fwdLen*sunDir.y + sideLen*sunDir.x, + sunPos.z); + + RwIm3DVertexSetU(&TempBufferRenderVertices[TempBufferVerticesStored+0], 0.0f); + RwIm3DVertexSetV(&TempBufferRenderVertices[TempBufferVerticesStored+0], 0.5f); + RwIm3DVertexSetU(&TempBufferRenderVertices[TempBufferVerticesStored+1], 1.0f); + RwIm3DVertexSetV(&TempBufferRenderVertices[TempBufferVerticesStored+1], 0.5f); + TempBufferVerticesStored += 2; + } + + + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEFOGTYPE, (void*)rwFOGTYPELINEAR); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[4])); + if(RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, rwIM3D_VERTEXUV)){ + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored); + RwIm3DEnd(); + } + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + TempBufferVerticesStored = 0; + TempBufferIndicesStored = 0; + } + } +} + +void +CCoronas::DoSunAndMoon(void) +{ + // yeah, moon is done somewhere else.... + + CVector sunCoors = CTimeCycle::GetSunDirection(); + sunCoors *= 150.0f; + sunCoors += TheCamera.GetPosition(); + + if(CTimeCycle::GetSunDirection().z > -0.2f){ + float size = ((CGeneral::GetRandomNumber()&0xFF) * 0.005f + 10.0f) * CTimeCycle::GetSunSize(); + RegisterCorona(SUN_CORE, + CTimeCycle::GetSunCoreRed(), CTimeCycle::GetSunCoreGreen(), CTimeCycle::GetSunCoreBlue(), + 255, sunCoors, size, + 999999.88f, TYPE_STAR, FLARE_NONE, REFLECTION_OFF, LOSCHECK_OFF, STREAK_OFF, 0.0f); + + if(CTimeCycle::GetSunDirection().z > 0.0f && !CGame::IsInInterior()) + RegisterCorona(SUN_CORONA, + CTimeCycle::GetSunCoronaRed(), CTimeCycle::GetSunCoronaGreen(), CTimeCycle::GetSunCoronaBlue(), + 255, sunCoors, 25.0f * CTimeCycle::GetSunSize(), + 999999.88f, TYPE_STAR, FLARE_SUN, REFLECTION_OFF, LOSCHECK_ON, STREAK_OFF, 0.0f); + } + + CVector spriteCoors; + float spritew, spriteh; + if(CSprite::CalcScreenCoors(sunCoors, &spriteCoors, &spritew, &spriteh, true)) { + SunScreenX = spriteCoors.x; + SunScreenY = spriteCoors.y; + }else{ + SunScreenX = 1000000.0f; + SunScreenY = 1000000.0f; + } +} + +void +CRegisteredCorona::Update(void) +{ + if(!registeredThisFrame) + alpha = 0; + + if(LOScheck && + (CCoronas::SunBlockedByClouds && id == CCoronas::SUN_CORONA || + !CWorld::GetIsLineOfSightClear(coors, TheCamera.GetPosition(), true, false, false, false, false, false))){ + // Corona is blocked, fade out + fadeAlpha = Max(fadeAlpha - 15.0f*CTimer::GetTimeStep(), 0.0f); + }else if(offScreen){ + // Same when off screen + fadeAlpha = Max(fadeAlpha - 15.0f*CTimer::GetTimeStep(), 0.0f); + }else{ + // Visible + if(alpha > fadeAlpha){ + // fade in + fadeAlpha = Min(fadeAlpha + 15.0f*CTimer::GetTimeStep(), alpha); + if(CCoronas::bChangeBrightnessImmediately) + fadeAlpha = alpha; + }else if(alpha < fadeAlpha){ + // too visible, decrease alpha but not below alpha + fadeAlpha = Max(fadeAlpha - 15.0f*CTimer::GetTimeStep(), alpha); + } + + // darken scene when the sun is visible + if(id == CCoronas::SUN_CORONA) + CCoronas::LightsMult = Max(CCoronas::LightsMult - CTimer::GetTimeStep()*0.06f, 0.6f); + } + + // remove if invisible + if(fadeAlpha == 0 && !firstUpdate) + id = 0; + firstUpdate = false; + registeredThisFrame = false; +} + +void +CEntity::ProcessLightsForEntity(void) +{ + int i, n; + C2dEffect *effect; + CVector pos; + bool lightOn, lightFlickering; + uint32 flashTimer1, flashTimer2, flashTimer3; + + if(bRenderDamaged || !bIsVisible || GetUp().z < 0.96f) + return; + + flashTimer1 = 0; + flashTimer2 = 0; + flashTimer3 = 0; + + n = CModelInfo::GetModelInfo(GetModelIndex())->GetNum2dEffects(); + for(i = 0; i < n; i++, flashTimer1 += 0x80, flashTimer2 += 0x100, flashTimer3 += 0x200){ + effect = CModelInfo::GetModelInfo(GetModelIndex())->Get2dEffect(i); + + switch(effect->type){ + case EFFECT_LIGHT: + pos = GetMatrix() * effect->pos; + + lightOn = false; + lightFlickering = false; + switch(effect->light.lightType){ + case LIGHT_ON: + lightOn = true; + break; + case LIGHT_ON_NIGHT: + if(CClock::GetHours() > 18 || CClock::GetHours() < 7) + lightOn = true; + break; + case LIGHT_FLICKER: + if((CTimer::GetTimeInMilliseconds() ^ m_randomSeed) & 0x60) + lightOn = true; + else + lightFlickering = true; + if((CTimer::GetTimeInMilliseconds()>>11 ^ m_randomSeed) & 3) + lightOn = true; + break; + case LIGHT_FLICKER_NIGHT: + if(CClock::GetHours() > 18 || CClock::GetHours() < 7 || CWeather::WetRoads > 0.5f){ + if((CTimer::GetTimeInMilliseconds() ^ m_randomSeed) & 0x60) + lightOn = true; + else + lightFlickering = true; + if((CTimer::GetTimeInMilliseconds()>>11 ^ m_randomSeed) & 3) + lightOn = true; + } + break; + case LIGHT_FLASH1: + if((CTimer::GetTimeInMilliseconds() + flashTimer1) & 0x200) + lightOn = true; + break; + case LIGHT_FLASH1_NIGHT: + if(CClock::GetHours() > 18 || CClock::GetHours() < 7) + if((CTimer::GetTimeInMilliseconds() + flashTimer1) & 0x200) + lightOn = true; + break; + case LIGHT_FLASH2: + if((CTimer::GetTimeInMilliseconds() + flashTimer2) & 0x400) + lightOn = true; + break; + case LIGHT_FLASH2_NIGHT: + if(CClock::GetHours() > 18 || CClock::GetHours() < 7) + if((CTimer::GetTimeInMilliseconds() + flashTimer2) & 0x400) + lightOn = true; + break; + case LIGHT_FLASH3: + if((CTimer::GetTimeInMilliseconds() + flashTimer3) & 0x800) + lightOn = true; + break; + case LIGHT_FLASH3_NIGHT: + if(CClock::GetHours() > 18 || CClock::GetHours() < 7) + if((CTimer::GetTimeInMilliseconds() + flashTimer3) & 0x800) + lightOn = true; + break; + case LIGHT_RANDOM_FLICKER: + if(m_randomSeed > 16) + lightOn = true; + else{ + if((CTimer::GetTimeInMilliseconds() ^ m_randomSeed*8) & 0x60) + lightOn = true; + else + lightFlickering = true; + if((CTimer::GetTimeInMilliseconds()>>11 ^ m_randomSeed*8) & 3) + lightOn = true; + } + break; + case LIGHT_RANDOM_FLICKER_NIGHT: + if(CClock::GetHours() > 18 || CClock::GetHours() < 7){ + if(m_randomSeed > 16) + lightOn = true; + else{ + if((CTimer::GetTimeInMilliseconds() ^ m_randomSeed*8) & 0x60) + lightOn = true; + else + lightFlickering = true; + if((CTimer::GetTimeInMilliseconds()>>11 ^ m_randomSeed*8) & 3) + lightOn = true; + } + } + break; + case LIGHT_BRIDGE_FLASH1: + if(CBridge::ShouldLightsBeFlashing() && CTimer::GetTimeInMilliseconds() & 0x200) + lightOn = true; + break; + case LIGHT_BRIDGE_FLASH2: + if(CBridge::ShouldLightsBeFlashing() && (CTimer::GetTimeInMilliseconds() & 0x1FF) < 60) + lightOn = true; + break; + } + + if(effect->light.flags & LIGHTFLAG_HIDE_OBJECT){ + if(lightOn) + bDoNotRender = false; + else + bDoNotRender = true; + return; + } + + // Corona + if(lightOn) + CCoronas::RegisterCorona((uintptr)this + i, + effect->col.r, effect->col.g, effect->col.b, 255, + pos, effect->light.size, effect->light.dist, + effect->light.corona, effect->light.flareType, effect->light.roadReflection, + effect->light.flags&LIGHTFLAG_LOSCHECK, CCoronas::STREAK_OFF, 0.0f, + !!(effect->light.flags&LIGHTFLAG_LONG_DIST)); + else if(lightFlickering) + CCoronas::RegisterCorona((uintptr)this + i, + 0, 0, 0, 255, + pos, effect->light.size, effect->light.dist, + effect->light.corona, effect->light.flareType, effect->light.roadReflection, + effect->light.flags&LIGHTFLAG_LOSCHECK, CCoronas::STREAK_OFF, 0.0f, + !!(effect->light.flags&LIGHTFLAG_LONG_DIST)); + + // Pointlight + bool alreadyProcessedFog; + alreadyProcessedFog = false; + if(effect->light.range != 0.0f && lightOn){ + if(effect->col.r == 0 && effect->col.g == 0 && effect->col.b == 0){ + CPointLights::AddLight(CPointLights::LIGHT_POINT, + pos, CVector(0.0f, 0.0f, 0.0f), + effect->light.range, + 0.0f, 0.0f, 0.0f, + CPointLights::FOG_NONE, true); + }else{ + CPointLights::AddLight(CPointLights::LIGHT_POINT, + pos, CVector(0.0f, 0.0f, 0.0f), + effect->light.range, + effect->col.r*CTimeCycle::GetSpriteBrightness()/255.0f, + effect->col.g*CTimeCycle::GetSpriteBrightness()/255.0f, + effect->col.b*CTimeCycle::GetSpriteBrightness()/255.0f, + (effect->light.flags & LIGHTFLAG_FOG) >> 1, + true); + alreadyProcessedFog = true; + } + } + + if(!alreadyProcessedFog){ + if(effect->light.flags & LIGHTFLAG_FOG_ALWAYS){ + CPointLights::AddLight(CPointLights::LIGHT_FOGONLY_ALWAYS, + pos, CVector(0.0f, 0.0f, 0.0f), + 0.0f, + effect->col.r/255.0f, effect->col.g/255.0f, effect->col.b/255.0f, + CPointLights::FOG_ALWAYS, true); + }else if(effect->light.flags & LIGHTFLAG_FOG_NORMAL && lightOn && effect->light.range == 0.0f){ + CPointLights::AddLight(CPointLights::LIGHT_FOGONLY, + pos, CVector(0.0f, 0.0f, 0.0f), + 0.0f, + effect->col.r/255.0f, effect->col.g/255.0f, effect->col.b/255.0f, + CPointLights::FOG_NORMAL, true); + } + } + + // Light shadow + if(effect->light.shadowSize != 0.0f){ + if(lightOn){ + CShadows::StoreStaticShadow((uintptr)this + i, SHADOWTYPE_ADDITIVE, + effect->light.shadow, &pos, + effect->light.shadowSize, 0.0f, + 0.0f, -effect->light.shadowSize, + 128, + effect->col.r*CTimeCycle::GetSpriteBrightness()*effect->light.shadowIntensity/255.0f, + effect->col.g*CTimeCycle::GetSpriteBrightness()*effect->light.shadowIntensity/255.0f, + effect->col.b*CTimeCycle::GetSpriteBrightness()*effect->light.shadowIntensity/255.0f, + 15.0f, 1.0f, 40.0f, false, 0.0f); + }else if(lightFlickering){ + CShadows::StoreStaticShadow((uintptr)this + i, SHADOWTYPE_ADDITIVE, + effect->light.shadow, &pos, + effect->light.shadowSize, 0.0f, + 0.0f, -effect->light.shadowSize, + 0, 0.0f, 0.0f, 0.0f, + 15.0f, 1.0f, 40.0f, false, 0.0f); + } + } + break; + + case EFFECT_SUNGLARE: + if(CWeather::SunGlare >= 0.0f){ + CVector pos = GetMatrix() * effect->pos; + CVector glareDir = pos - GetPosition(); + glareDir.Normalise(); + CVector camDir = TheCamera.GetPosition() - pos; + float dist = camDir.Magnitude(); + camDir *= 2.0f/dist; + glareDir += camDir; + glareDir.Normalise(); + float camAngle = -DotProduct(glareDir, CTimeCycle::GetSunDirection()); + if(camAngle > 0.0f){ + float intens = Sqrt(camAngle) * CWeather::SunGlare; + pos += camDir; + CCoronas::RegisterCorona((uintptr)this + 33 + i, + intens * (CTimeCycle::GetSunCoreRed() + 2*255)/3.0f, + intens * (CTimeCycle::GetSunCoreGreen() + 2*255)/3.0f, + intens * (CTimeCycle::GetSunCoreBlue() + 2*255)/3.0f, + 255, + pos, 0.5f*CWeather::SunGlare*Sqrt(dist), 120.0f, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, + CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, + CCoronas::STREAK_OFF, 0.0f); + } + } + break; + } + } +} \ No newline at end of file diff --git a/src/miami/renderer/Coronas.h b/src/miami/renderer/Coronas.h new file mode 100644 index 00000000..45f027d8 --- /dev/null +++ b/src/miami/renderer/Coronas.h @@ -0,0 +1,105 @@ +#pragma once + +extern RwTexture *gpCoronaTexture[9]; + +struct CRegisteredCorona +{ + CVector coors; + uint32 id; + uint32 lastLOScheck; + RwTexture *texture; + float size; + float someAngle; + float drawDist; + float nearDist; + float heightAboveRoad; + uint8 red; + uint8 green; + uint8 blue; + uint8 alpha; // alpha when fully visible + uint8 fadeAlpha; // actual value used for rendering, faded + bool registeredThisFrame; + int8 flareType; + int8 reflection; + + uint8 LOScheck : 1; + uint8 offScreen : 1; + uint8 firstUpdate : 1; + uint8 drawStreak : 1; + uint8 sightClear : 1; + uint8 useNearDist : 1; + uint8 renderReflection : 1; + + int16 prevX[6]; + int16 prevY[6]; + uint8 prevRed[6]; + uint8 prevGreen[6]; + uint8 prevBlue[6]; + bool hasValue[6]; + + void Update(void); +}; + +VALIDATE_SIZE(CRegisteredCorona, 0x68); + +class CCoronas +{ + static CRegisteredCorona aCoronas[NUMCORONAS]; +public: + enum { + SUN_CORE = 1, + SUN_CORONA + }; + enum { + TYPE_STAR, + TYPE_NORMAL, + TYPE_MOON, + TYPE_REFLECT, + TYPE_HEADLIGHT, + TYPE_HEX, + TYPE_CIRCLE, + TYPE_RING, + TYPE_STREAK, + }; + enum { + FLARE_NONE, + FLARE_SUN, + FLARE_HEADLIGHTS + }; + enum { + REFLECTION_OFF, + REFLECTION_ON, + }; + enum { + LOSCHECK_OFF, + LOSCHECK_ON, + }; + enum { + STREAK_OFF, + STREAK_ON, + }; + + static float LightsMult; + static float SunScreenY; + static float SunScreenX; + static int MoonSize; + static bool SunBlockedByClouds; + static int bChangeBrightnessImmediately; + + static void Init(void); + static void Shutdown(void); + static void Update(void); + static void RegisterCorona(uint32 id, uint8 red, uint8 green, uint8 blue, uint8 alpha, + const CVector &coors, float size, float drawDist, RwTexture *tex, + int8 flareType, uint8 reflection, uint8 LOScheck, uint8 drawStreak, float someAngle, + bool useNearDist = false, float nearDist = 1.5f); + static void RegisterCorona(uint32 id, uint8 red, uint8 green, uint8 blue, uint8 alpha, + const CVector &coors, float size, float drawDist, uint8 type, + int8 flareType, uint8 reflection, uint8 LOScheck, uint8 drawStreak, float someAngle, + bool useNearDist = false, float nearDist = 1.5f); + static void UpdateCoronaCoors(uint32 id, const CVector &coors, float drawDist, float someAngle); + static void Render(void); + static void RenderReflections(void); + static void RenderSunReflection(void); + static void DoSunAndMoon(void); +}; diff --git a/src/miami/renderer/Credits.cpp b/src/miami/renderer/Credits.cpp new file mode 100644 index 00000000..81e76625 --- /dev/null +++ b/src/miami/renderer/Credits.cpp @@ -0,0 +1,820 @@ +#include "common.h" + +#include "Timer.h" +#include "Font.h" +#include "Frontend.h" +#include "RwHelper.h" +#include "Camera.h" +#include "Text.h" +#include "Credits.h" +#include "Pad.h" + +bool CCredits::bCreditsGoing; +uint32 CCredits::CreditsStartTime; + +void +CCredits::Init(void) +{ + Stop(); +} + +void +CCredits::Start(void) +{ + bCreditsGoing = true; + CreditsStartTime = CTimer::GetTimeInMilliseconds(); +} + +void +CCredits::Stop(void) +{ + bCreditsGoing = false; +} + +void +CCredits::PrintCreditSpace(float space, uint32 &line) +{ + line += space * 25.0f; +} + +void +CCredits::PrintCreditText(float scaleX, float scaleY, wchar *text, uint32 &lineoffset, float scrolloffset) +{ + CPad::UpdatePads(); + if (CPad::GetPad(0)->GetCrossJustDown()) + bCreditsGoing = false; + else { + float start = DEFAULT_SCREEN_HEIGHT + 20.0f; + float y = lineoffset + start - scrolloffset; + if (y > 20.0f && DEFAULT_SCREEN_HEIGHT - 20.0f > y) { + CFont::SetScale(SCREEN_SCALE_X(scaleX), SCREEN_SCALE_Y(scaleY)); + CFont::SetColor(CRGBA(0, 0, 0, 255)); + CFont::PrintString(SCREEN_WIDTH / 2.0f, SCREEN_SCALE_Y(y), (uint16*)text); + CFont::SetColor(CRGBA(220, 220, 220, 220)); + CFont::PrintString(SCREEN_WIDTH / 2.0f - SCREEN_SCALE_X(1.0f), SCREEN_SCALE_Y(y - 1.0f), (uint16*)text); + } + lineoffset += scaleY*25.0f; + } +} + +void +CCredits::Render(void) +{ + uint32 lineoffset; + float scrolloffset; + + if(!bCreditsGoing || FrontEndMenuManager.m_bMenuActive) + return; + + DefinedState(); + lineoffset = 0; + scrolloffset = (CTimer::GetTimeInMilliseconds() - CreditsStartTime) / 24.0f; + CFont::SetJustifyOff(); + CFont::SetBackgroundOff(); + CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH * 0.75f)); + CFont::SetCentreOn(); + CFont::SetPropOn(); + CFont::SetFontStyle(FONT_STANDARD); + + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED001"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED002"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED003"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED004"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED005"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED006"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED007"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED008"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED025"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED026"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED027"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED028"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED029"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED030"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED031"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD031A"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD031B"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD031C"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRD031D"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD031E"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRD024A"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH) + PrintCreditSpace(0.5f, lineoffset); + + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED024"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED023"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD023A"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD023B"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED018"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED019"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRD018A"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD019A"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD019B"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED020"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED021"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRD022A"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED022"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD022B"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD022C"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED032"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED033"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRD032A"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED034"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED035"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED036"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED037"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD037A"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD037B"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD037C"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRD041B"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED042"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED039"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED044"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED040"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD042A"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED142"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD142A"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED009"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED010"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED011"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED012"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED013"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD013A"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD013B"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD013C"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED089"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH) + PrintCreditSpace(0.5f, lineoffset); + + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED090"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED347"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED047"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH) + PrintCreditSpace(0.5f, lineoffset); + + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED048"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED049"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED348"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED050"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED051"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED052"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED053"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED054"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED055"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED056"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD056A"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD056B"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD056C"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD056D"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED349"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED350"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED351"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED352"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED353"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED354"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED355"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED356"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED357"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED359"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED360"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED361"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED362"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED363"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED364"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED365"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED366"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED367"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED368"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED369"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED370"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED371"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED372"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED373"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED256"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED257"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED258"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED057"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH) + PrintCreditSpace(0.5f, lineoffset); + + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED058"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRD057A"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED059"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRD060A"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD060B"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD060C"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRD002A"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH) + PrintCreditSpace(0.5f, lineoffset); + + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED003"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRD001A"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD001B"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED060"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED061"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED062"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED063"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED064"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED069"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED070"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED065"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH) + PrintCreditSpace(0.5f, lineoffset); + + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED066"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED067"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED068"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRD071A"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD072A"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED091"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED094"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED095"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED097"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED098"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD098A"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD098B"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD098C"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED099"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED096"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED273"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD092A"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED092"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD092B"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED073"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED074"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED076"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED075"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED077"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED078"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED081"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED082"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED079"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED080"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED083"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED084"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD084A"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD084B"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD084C"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRD084D"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD084E"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED085"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED086"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD086A"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED087"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED088"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD088A"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD088B"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD088C"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD088D"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD088E"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD088F"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD088G"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED107"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED108"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED109"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED110"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD110A"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED111"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED112"), lineoffset, scrolloffset); + + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED113"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED114"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED115"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED116"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED117"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED118"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED119"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED120"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED121"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED122"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED123"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED124"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED125"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED126"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED127"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED128"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED129"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + + CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH * 0.8f)); + + PrintCreditText(1.1f, 0.8f, TheText.Get("CRD111A"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED130"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED131"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED132"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED133"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED134"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD134A"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD134B"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD134C"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD134D"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD134E"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD134F"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD134G"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD134H"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD134I"), lineoffset, scrolloffset); + + CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH * 0.7f)); + + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED135"), lineoffset, scrolloffset); + + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH) + PrintCreditSpace(0.5f, lineoffset); + + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD136A"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD137A"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED138"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED066"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD138B"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED139"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(1.1f, 0.8f, TheText.Get("CRED140"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) + PrintCreditSpace(0.5f, lineoffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140A"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140B"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140C"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140D"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140E"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140F"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140G"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140H"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140I"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140J"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140K"), lineoffset, scrolloffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140L"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + + CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH * 0.85f)); + + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED259"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED260"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED261"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED262"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED263"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED264"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED265"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED266"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED141"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD141A"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD141B"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED143"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED144"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED145"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED146"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED147"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED148"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED149"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED150"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED151"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED152"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED153"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED154"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED155"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED156"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED157"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED158"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED159"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED160"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED161"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED162"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED163"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED164"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED165"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED166"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED167"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED168"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED169"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED170"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED171"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED172"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + + CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH * 0.75f)); + + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED217"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED218"), lineoffset, scrolloffset); + PrintCreditSpace(1.0f, lineoffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRD218A"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED219"), lineoffset, scrolloffset); + PrintCreditSpace(1.0f, lineoffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED220"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED221"), lineoffset, scrolloffset); + PrintCreditSpace(1.0f, lineoffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED222"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED223"), lineoffset, scrolloffset); + PrintCreditSpace(1.0f, lineoffset); + PrintCreditText(1.1f, 1.1f, TheText.Get("CRED224"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED227"), lineoffset, scrolloffset); + PrintCreditSpace(1.0f, lineoffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED228"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED229"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD229A"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD229B"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED274"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED275"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED276"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED277"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED278"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED279"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED280"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED281"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED282"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED283"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED284"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED285"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED286"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED287"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED288"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED289"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED290"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED291"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED292"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED293"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED294"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED295"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED296"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED297"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED298"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED299"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED300"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED301"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED302"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED303"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED304"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED305"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED306"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED307"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED308"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED309"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED310"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED314"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED315"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED316"), lineoffset, scrolloffset); + PrintCreditSpace(1.0f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED317"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED318"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED319"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED320"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED321"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED322"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED323"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED324"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED325"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED326"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED327"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED328"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED329"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED330"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED331"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED332"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + + CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH * 0.8f)); + + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED333"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED334"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED335"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED336"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED337"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED338"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED339"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED340"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED341"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED342"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditSpace(1.0f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD344A"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED344"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED345"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRD345A"), lineoffset, scrolloffset); + PrintCreditSpace(1.0f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED346"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(1.0f, lineoffset); + + PrintCreditSpace(1.5f, lineoffset); + + CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH * 0.75f)); + + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED267"), lineoffset, scrolloffset); + PrintCreditSpace(0.5f, lineoffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED268"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED269"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED270"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED271"), lineoffset, scrolloffset); + PrintCreditText(0.65f, 0.65f, TheText.Get("CRED272"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditText(0.95f, 0.7f, TheText.Get("CRED230"), lineoffset, scrolloffset); + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) + PrintCreditSpace(0.5f, lineoffset); + + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED231"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED232"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED233"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED234"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED235"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED236"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED237"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED238"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED239"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED240"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED241"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED242"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED243"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED244"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED245"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED246"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED247"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED248"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED249"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED358"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED250"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED251"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED252"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRD251A"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRD252A"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED253"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED254"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED374"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED375"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED376"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED377"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED378"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED379"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED380"), lineoffset, scrolloffset); + PrintCreditText(0.95f, 0.95f, TheText.Get("CRED381"), lineoffset, scrolloffset); + PrintCreditSpace(1.5f, lineoffset); + PrintCreditSpace(1.5f, lineoffset); + CFont::DrawFonts(); +#ifdef CUTSCENE_BORDERS_SWITCH + if (CMenuManager::m_PrefsCutsceneBorders) +#endif + if(TheCamera.m_WideScreenOn) + TheCamera.DrawBordersForWideScreen(); + +#ifdef FIX_BUGS + if(lineoffset + DEFAULT_SCREEN_HEIGHT - scrolloffset < -10.0f) +#else + if(lineoffset + SCREEN_HEIGHT - scrolloffset < -10.0f) +#endif + { + bCreditsGoing = false; + } +} + +bool CCredits::AreCreditsDone(void) +{ + return !bCreditsGoing; +} diff --git a/src/miami/renderer/Credits.h b/src/miami/renderer/Credits.h new file mode 100644 index 00000000..e049ce76 --- /dev/null +++ b/src/miami/renderer/Credits.h @@ -0,0 +1,15 @@ +#pragma once + +class CCredits +{ + static bool bCreditsGoing; + static uint32 CreditsStartTime; +public: + static void Init(void); + static void Start(void); + static void Stop(void); + static bool AreCreditsDone(void); + static void Render(void); + static void PrintCreditSpace(float space, uint32 &line); + static void PrintCreditText(float scaleX, float scaleY, wchar *text, uint32 &lineoffset, float scrolloffset); +}; diff --git a/src/miami/renderer/CutsceneShadow.cpp b/src/miami/renderer/CutsceneShadow.cpp new file mode 100644 index 00000000..8cb33896 --- /dev/null +++ b/src/miami/renderer/CutsceneShadow.cpp @@ -0,0 +1,269 @@ +#include "common.h" +#include "main.h" +#include "rwcore.h" +#include "rwplcore.h" +#include "CutsceneShadow.h" +#include "RwHelper.h" + +#define DLIGHT_VALUE 0.8f /* Directional light intensity */ + + +CCutsceneShadow::CCutsceneShadow() +{ + m_pAtomic = nil; + m_nRwObjectType = -1; + m_pLight = nil; + m_nBlurPasses = 0; + m_bResample = false; + m_bGradient = false; +} + +CCutsceneShadow::~CCutsceneShadow() +{ + Destroy(); +} + +bool +CCutsceneShadow::Create(RwObject *object, int32 rasterSize, bool resample, int32 blurPasses, bool gradient) +{ + ASSERT(object != nil); + + RwRGBAReal color; + RwFrame *frame; + + if (!object) + return false; + + m_pLight = RpLightCreate(rpLIGHTDIRECTIONAL); + ASSERT(m_pLight != nil); + + if (!m_pLight) + return false; + + color.red = color.green = color.blue = DLIGHT_VALUE; + color.alpha = 0.0f; + + RpLightSetColor(m_pLight, &color); + + frame = RwFrameCreate(); + ASSERT(frame != nil); + + RpLightSetFrame(m_pLight, frame); + + SetLightProperties(180.0f, 90.0f, false); + + m_pObject = object; + m_nRwObjectType = RwObjectGetType(m_pObject); + + switch ( m_nRwObjectType ) + { + case rpCLUMP: + { + RpClumpGetBoundingSphere(m_pClump, &m_BoundingSphere, 1); + m_BaseSphere.radius = m_BoundingSphere.radius; + RwV3dTransformPoints(&m_BaseSphere.center, &m_BoundingSphere.center, 1, RwFrameGetMatrix(RpClumpGetFrame(m_pClump))); + break; + } + + case rpATOMIC: + { + m_BoundingSphere = *RpAtomicGetBoundingSphere(m_pAtomic); + m_BaseSphere.radius = m_BoundingSphere.radius; + RwV3dTransformPoints(&m_BaseSphere.center, &m_BoundingSphere.center, 1, RwFrameGetMatrix(RpAtomicGetFrame(m_pAtomic))); + break; + } + + default: + { + Destroy(); + return false; + break; + } + } + + if ( !m_Camera.Create(rasterSize) ) + { + Destroy(); + return false; + } + + m_nBlurPasses = blurPasses; + m_bResample = resample; + m_bGradient = gradient; + + if ( m_bResample && !m_ResampleCamera.Create(rasterSize - 1) ) + { + Destroy(); + return false; + } + + if ( m_nBlurPasses != 0 ) + { + if ( !m_BlurCamera.Create(resample ? rasterSize - 1 : rasterSize) ) + { + Destroy(); + return false; + } + } + + if ( m_bGradient ) + { + if ( !m_GradientCamera.Create(resample ? rasterSize - 1 : rasterSize) ) + { + Destroy(); + return false; + } + + m_GradientCamera.MakeGradientRaster(); + } + + m_Camera.SetLight(m_pLight); + + switch ( m_nRwObjectType ) + { + case rpATOMIC: + m_Camera.SetFrustum(1.1f * m_BoundingSphere.radius); + break; + + case rpCLUMP: + m_Camera.SetFrustum(1.1f * m_BoundingSphere.radius); + break; + } + + m_Camera.SetCenter(&m_BaseSphere.center); + return true; +} + +RwFrame * +CCutsceneShadow::SetLightProperties(float angleY, float angleX, bool setLight) +{ + ASSERT(m_pLight != nil); + + RwFrame *frame; + static RwV3d Xaxis = { 1.0f, 0.0f, 0.0f }; + static RwV3d Yaxis = { 0.0f, 1.0f, 0.0f }; + + frame = RpLightGetFrame(m_pLight); + ASSERT(frame != nil); + + if ( !frame ) + return nil; + + RwFrameRotate(frame, &Yaxis, angleY, rwCOMBINEREPLACE); + RwFrameRotate(frame, &Xaxis, angleX, rwCOMBINEPOSTCONCAT); + + if ( setLight ) + m_Camera.SetLight(m_pLight); + + return frame; +} + +bool +CCutsceneShadow::IsInitialized() +{ + return m_pObject != nil; +} + +void +CCutsceneShadow::Destroy() +{ + m_Camera.Destroy(); + m_ResampleCamera.Destroy(); + m_BlurCamera.Destroy(); + m_GradientCamera.Destroy(); + + m_pAtomic = nil; + + m_nRwObjectType = -1; + + if (m_pLight) + { + RwFrame *frame = RpLightGetFrame(m_pLight); + RpLightSetFrame(m_pLight, nil); + RwFrameDestroy(frame); + RpLightDestroy(m_pLight); + m_pLight = nil; + } +} + +RwRaster * +CCutsceneShadow::Update() +{ + switch ( m_nRwObjectType ) + { + case rpCLUMP: + ASSERT(m_pClump != nil); + RwV3dTransformPoints(&m_BaseSphere.center, &m_BoundingSphere.center, 1, RwFrameGetMatrix(RpClumpGetFrame(m_pClump))); + break; + + case rpATOMIC: + ASSERT(m_pAtomic != nil); + RwV3dTransformPoints(&m_BaseSphere.center, &m_BoundingSphere.center, 1, RwFrameGetMatrix(RpAtomicGetFrame(m_pAtomic))); + break; + } + + m_Camera.SetCenter(&m_BaseSphere.center); + + switch ( m_nRwObjectType ) + { + case rpCLUMP: + m_Camera.Update(m_pClump); + break; + + case rpATOMIC: + m_Camera.Update(m_pAtomic); + break; + } + + RwRaster *raster = m_Camera.GetRwRenderRaster(); + ASSERT(raster != nil); + + if ( m_bResample ) + return m_ResampleCamera.RasterResample(raster); + + if ( m_nBlurPasses ) + return m_BlurCamera.RasterBlur(raster, m_nBlurPasses); + + if ( m_bGradient ) + return m_GradientCamera.RasterGradient(raster); + + return raster; +} + +RwTexture * +CCutsceneShadow::UpdateForCutscene() +{ + Update(); + return GetShadowRwTexture(); +} + +CShadowCamera * +CCutsceneShadow::GetShadowCamera(int32 camType) +{ + switch ( camType ) + { + case RESAMPLE: return &m_ResampleCamera; + case BLUR: return &m_BlurCamera; + case GRADIENT: return &m_GradientCamera; + } + + return &m_Camera; +} + +RwTexture * +CCutsceneShadow::GetShadowRwTexture() +{ + if ( m_bResample ) + return m_ResampleCamera.GetRwRenderTexture(); + else + return m_Camera.GetRwRenderTexture(); +} + +void +CCutsceneShadow::DrawBorderAroundTexture(RwRGBA const& color) +{ + if ( m_bResample ) + m_ResampleCamera.DrawOutlineBorder(color); + else + m_Camera.DrawOutlineBorder(color); +} \ No newline at end of file diff --git a/src/miami/renderer/CutsceneShadow.h b/src/miami/renderer/CutsceneShadow.h new file mode 100644 index 00000000..a59fe78f --- /dev/null +++ b/src/miami/renderer/CutsceneShadow.h @@ -0,0 +1,52 @@ +#pragma once +#include "ShadowCamera.h" + +class CCutsceneShadow +{ +public: + enum + { + RASTER = 0, + RESAMPLE, + BLUR, + GRADIENT, + }; + + CShadowCamera m_Camera; + bool m_bResample; + CShadowCamera m_ResampleCamera; + int32 m_nBlurPasses; + CShadowCamera m_BlurCamera; + bool m_bGradient; + CShadowCamera m_GradientCamera; + + union + { + RwObject *m_pObject; + RpAtomic *m_pAtomic; + RpClump *m_pClump; + }; + + int32 m_nRwObjectType; + RpLight *m_pLight; + RwSphere m_BoundingSphere; + RwSphere m_BaseSphere; + + CCutsceneShadow(); + ~CCutsceneShadow(); + + RwSphere GetBaseSphere() + { + return m_BaseSphere; + } + + bool Create(RwObject *object, int32 rasterSize, bool resample, int32 blurPasses, bool gradient); + RwFrame *SetLightProperties(float angleY, float angleX, bool setLight); + bool IsInitialized(); + void Destroy(); + RwRaster *Update(); + RwTexture *UpdateForCutscene(); + CShadowCamera *GetShadowCamera(int32 camType = RASTER); + RwTexture *GetShadowRwTexture(); + void DrawBorderAroundTexture(RwRGBA const& color); +}; diff --git a/src/miami/renderer/Draw.cpp b/src/miami/renderer/Draw.cpp new file mode 100644 index 00000000..a5e7504b --- /dev/null +++ b/src/miami/renderer/Draw.cpp @@ -0,0 +1,113 @@ +#include "common.h" + +#include "Draw.h" +#include "Frontend.h" +#include "Camera.h" +#include "CutsceneMgr.h" + +float CDraw::ms_fAspectRatio = DEFAULT_ASPECT_RATIO; +#ifdef ASPECT_RATIO_SCALE +float CDraw::ms_fScaledFOV = 45.0f; +#endif + +float CDraw::ms_fNearClipZ; +float CDraw::ms_fFarClipZ; +float CDraw::ms_fFOV = 45.0f; +float CDraw::ms_fLODDistance; + +uint8 CDraw::FadeValue; +uint8 CDraw::FadeRed; +uint8 CDraw::FadeGreen; +uint8 CDraw::FadeBlue; + +#ifdef PROPER_SCALING +bool CDraw::ms_bProperScaling = true; +#endif +#ifdef FIX_RADAR +bool CDraw::ms_bFixRadar = true; +#endif +#ifdef FIX_SPRITES +bool CDraw::ms_bFixSprites = true; +#endif + +#ifdef ASPECT_RATIO_SCALE +float +FindAspectRatio(void) +{ + switch (FrontEndMenuManager.m_PrefsUseWideScreen) { + case AR_AUTO: + return SCREEN_WIDTH / SCREEN_HEIGHT; + default: + case AR_4_3: + return 4.0f / 3.0f; + case AR_5_4: + return 5.0f / 4.0f; + case AR_16_10: + return 16.0f / 10.0f; + case AR_16_9: + return 16.0f / 9.0f; + case AR_21_9: + return 21.0f / 9.0f; + }; +} +#endif + +float +CDraw::CalculateAspectRatio(void) +{ +#ifdef ASPECT_RATIO_SCALE + if (TheCamera.m_WideScreenOn) + CDraw::ms_fAspectRatio = (5.f / 3.f) * FindAspectRatio() / (16.f / 9.f); // It's used on theatrical showings according to Wiki + else + CDraw::ms_fAspectRatio = FindAspectRatio(); +#else + if(FrontEndMenuManager.m_PrefsUseWideScreen) { + if (TheCamera.m_WideScreenOn) + CDraw::ms_fAspectRatio = 5.f / 3.f; // It's used on theatrical showings according to Wiki + else + CDraw::ms_fAspectRatio = 16.f / 9.f; + } else if (TheCamera.m_WideScreenOn) { + CDraw::ms_fAspectRatio = 5.f/4.f; + } else { + CDraw::ms_fAspectRatio = 4.f/3.f; + } +#endif + return CDraw::ms_fAspectRatio; +} + +#ifdef ASPECT_RATIO_SCALE +// convert a 4:3 hFOV to vFOV, +// then convert that vFOV to hFOV for our aspect ratio, +// i.e. HOR+ +float +CDraw::ConvertFOV(float hfov) +{ + // => tan(hFOV/2) = tan(vFOV/2)*aspectRatio + // => tan(vFOV/2) = tan(hFOV/2)/aspectRatio + float ar1 = DEFAULT_ASPECT_RATIO; + float ar2 = GetAspectRatio(); + hfov = DEGTORAD(hfov); + float vfov = Atan(tan(hfov/2) / ar1) *2; + hfov = Atan(tan(vfov/2) * ar2) *2; + return RADTODEG(hfov); +} +#endif + +void +CDraw::SetFOV(float fov) +{ +#ifdef ASPECT_RATIO_SCALE + if (!CCutsceneMgr::IsRunning()) + ms_fScaledFOV = ConvertFOV(fov); + else + ms_fScaledFOV = fov; +#endif + ms_fFOV = fov; +} + +#ifdef PROPER_SCALING +float CDraw::ScaleY(float y) +{ + return ms_bProperScaling ? y : y * ((float)DEFAULT_SCREEN_HEIGHT/SCREEN_HEIGHT_NTSC); +} +#endif \ No newline at end of file diff --git a/src/miami/renderer/Draw.h b/src/miami/renderer/Draw.h new file mode 100644 index 00000000..b96fa813 --- /dev/null +++ b/src/miami/renderer/Draw.h @@ -0,0 +1,70 @@ +#pragma once + +enum eAspectRatio +{ + // Make sure these work the same as FrontEndMenuManager.m_PrefsUseWideScreen + // without widescreen support + AR_AUTO, + AR_4_3, + AR_5_4, + AR_16_10, + AR_16_9, + AR_21_9, + + AR_MAX, +}; + +class CDraw +{ +private: + static float ms_fNearClipZ; + static float ms_fFarClipZ; + static float ms_fFOV; + // we use this variable to scale a lot of 2D elements + // so better cache it + static float ms_fAspectRatio; +#ifdef ASPECT_RATIO_SCALE + // similar thing for 3D rendering + static float ms_fScaledFOV; +#endif +public: + static float ms_fLODDistance; // set but unused? + + static uint8 FadeValue; + static uint8 FadeRed; + static uint8 FadeGreen; + static uint8 FadeBlue; + +#ifdef PROPER_SCALING + static bool ms_bProperScaling; +#endif +#ifdef FIX_RADAR + static bool ms_bFixRadar; +#endif +#ifdef FIX_SPRITES + static bool ms_bFixSprites; +#endif + + static void SetNearClipZ(float nearclip) { ms_fNearClipZ = nearclip; } + static float GetNearClipZ(void) { return ms_fNearClipZ; } + static void SetFarClipZ(float farclip) { ms_fFarClipZ = farclip; } + static float GetFarClipZ(void) { return ms_fFarClipZ; } + + static void SetFOV(float fov); + static float GetFOV(void) { return ms_fFOV; } +#ifdef ASPECT_RATIO_SCALE + static float GetScaledFOV(void) { return ms_fScaledFOV; } +#else + static float GetScaledFOV(void) { return ms_fFOV; } +#endif + + static float CalculateAspectRatio(void); +#ifdef ASPECT_RATIO_SCALE + static float ConvertFOV(float fov); +#endif + static float GetAspectRatio(void) { return ms_fAspectRatio; } + static void SetAspectRatio(float ratio) { ms_fAspectRatio = ratio; } +#ifdef PROPER_SCALING + static float ScaleY(float y); +#endif +}; diff --git a/src/miami/renderer/Fluff.cpp b/src/miami/renderer/Fluff.cpp new file mode 100644 index 00000000..7574a16d --- /dev/null +++ b/src/miami/renderer/Fluff.cpp @@ -0,0 +1,1364 @@ +#include "common.h" +#include "main.h" + +#include "RenderBuffer.h" +#include "Entity.h" +#include "Fluff.h" +#include "Camera.h" +#include "Sprite.h" +#include "Coronas.h" +#include "PointLights.h" +#include "Rubbish.h" +#include "Timecycle.h" +#include "General.h" +#include "Timer.h" +#include "Clock.h" +#include "Weather.h" +#include "Stats.h" +#include "maths.h" +#include "Frontend.h" +#include "CutsceneMgr.h" +#include "PlayerPed.h" +#include "Bones.h" +#include "World.h" +#include "Replay.h" +#include "Coronas.h" +#include "SaveBuf.h" + +#ifdef COMPATIBLE_SAVES +#define SCRIPTPATHS_SAVE_SIZE 0x9C +#else +#define SCRIPTPATHS_SAVE_SIZE sizeof(aArray) +#endif + +CPlaneTrail CPlaneTrails::aArray[6]; +RwImVertexIndex TrailIndices[32] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, + 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16 +}; + +void +CPlaneTrail::Init(void) +{ + int i; + for(i = 0; i < ARRAY_SIZE(m_time); i++) + m_time[i] = 0; +} + +void +CPlaneTrail::Render(float visibility) +{ + int i; + int numVerts = 0; + if(!TheCamera.IsSphereVisible(m_pos[0], 1000.0f)) + return; + + int alpha = visibility*110.0f; + if(alpha == 0) + return; + + for(i = 0; i < ARRAY_SIZE(m_pos); i++){ + int32 time = CTimer::GetTimeInMilliseconds() - m_time[i]; + if(time > 30000) + m_time[i] = 0; + if(m_time[i] != 0){ + float fade = (30000.0f - time) / 10000.0f; + fade = Min(fade, 1.0f); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[numVerts], 255, 255, 255, (int)(alpha*fade)); + RwIm3DVertexSetPos(&TempBufferRenderVertices[numVerts], m_pos[i].x, m_pos[i].y, m_pos[i].z); + numVerts++; + } + } + if(numVerts > 1){ + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + + if(RwIm3DTransform(TempBufferRenderVertices, numVerts, nil, rwIM3D_VERTEXXYZ|rwIM3D_VERTEXRGBA)){ + RwIm3DRenderIndexedPrimitive(rwPRIMTYPELINELIST, TrailIndices, (numVerts-1)*2); + RwIm3DEnd(); + } + } +} + +void +CPlaneTrail::RegisterPoint(CVector pos) +{ + int i; + bool bNewPoint = false; + if(m_time[0] != 0 && CTimer::GetTimeInMilliseconds() - m_time[0] > 2000){ + bNewPoint = true; + for(i = ARRAY_SIZE(m_pos)-1; i > 0; i--){ + m_pos[i] = m_pos[i-1]; + m_time[i] = m_time[i-1]; + } + } + m_pos[0] = pos; + if(bNewPoint || m_time[0] == 0) + m_time[0] = CTimer::GetTimeInMilliseconds(); +} + +void +CPlaneTrails::Init(void) +{ + int i; + for(i = 0; i < ARRAY_SIZE(aArray); i++) + aArray[i].Init(); +} + +void +CPlaneTrails::Update(void) +{ + CVector planePos; + + planePos.x = 1590.0f * Sin((float)(CTimer::GetTimeInMilliseconds() & 0x1FFFF)/0x20000 * TWOPI); + planePos.y = 1200.0f * Cos((float)(CTimer::GetTimeInMilliseconds() & 0x1FFFF)/0x20000 * TWOPI); + planePos.z = 550.0f; + RegisterPoint(planePos, 3); + if(CClock::GetHours() > 22 || CClock::GetHours() < 7){ + if(CTimer::GetTimeInMilliseconds() & 0x200) + CCoronas::RegisterCorona(101, 255, 0, 0, 255, planePos, 5.0f, 2000.0f, + CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + else + CCoronas::UpdateCoronaCoors(101, planePos, 2000.0f, 0.0f); + } + + planePos.x = 1000.0f * Sin((float)(CTimer::GetTimeInMilliseconds() & 0x1FFFF)/0x20000 * TWOPI); + planePos.y = -1600.0f * Cos((float)(CTimer::GetTimeInMilliseconds() & 0x1FFFF)/0x20000 * TWOPI); + planePos.z = 500.0f; + RegisterPoint(planePos, 4); + if(CClock::GetHours() > 22 || CClock::GetHours() < 7){ + if(CTimer::GetTimeInMilliseconds() & 0x200) + CCoronas::RegisterCorona(102, 255, 0, 0, 255, planePos, 5.0f, 2000.0f, + CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + else + CCoronas::UpdateCoronaCoors(102, planePos, 2000.0f, 0.0f); + } + + planePos.x = 1100.0f * Cos((float)(CTimer::GetTimeInMilliseconds() & 0x1FFFF)/0x20000 * TWOPI); + planePos.y = 700.0f * Sin((float)(CTimer::GetTimeInMilliseconds() & 0x1FFFF)/0x20000 * TWOPI); + planePos.z = 600.0f; + RegisterPoint(planePos, 5); + if(CClock::GetHours() > 22 || CClock::GetHours() < 7){ + if(CTimer::GetTimeInMilliseconds() & 0x200) + CCoronas::RegisterCorona(103, 255, 0, 0, 255, planePos, 5.0f, 2000.0f, + CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + else + CCoronas::UpdateCoronaCoors(103, planePos, 2000.0f, 0.0f); + } +} + +void +CPlaneTrails::Render(void) +{ + int i; + float visibility = Min(1.0f-CWeather::Foggyness, 1.0f-CWeather::CloudCoverage); + visibility = Min(visibility, 1.0f-CWeather::Rain); + visibility = Min(Max(Max(CTimeCycle::GetSkyTopRed(), CTimeCycle::GetSkyTopGreen()), CTimeCycle::GetSkyTopBlue())/256.0f, visibility); + if(visibility > 0.0001f) + for(i = 0; i < ARRAY_SIZE(aArray); i++) + aArray[i].Render(visibility); +} + +void +CPlaneTrails::RegisterPoint(CVector pos, uint32 id) +{ + aArray[id].RegisterPoint(pos); +} + + + +CPlaneBanner CPlaneBanners::aArray[5]; + +void +CPlaneBanner::Init(void) +{ + int i; + for(i = 0; i < ARRAY_SIZE(m_pos); i++){ + m_pos[i].x = i; + m_pos[i].y = 0.0f; + m_pos[i].z = -60.0f; + } +} + +void +CPlaneBanner::Update(void) +{ + int i; + if(m_pos[0].z > -50.0f){ + m_pos[0].z -= 0.05f*CTimer::GetTimeStep(); + m_pos[0].z = Max(m_pos[0].z, -100.0f); + for(i = 1; i < ARRAY_SIZE(m_pos); i++){ + CVector dist = m_pos[i] - m_pos[i-1]; + float len = dist.Magnitude(); + if(len > 8.0f) + m_pos[i] = m_pos[i-1] + dist/len*8.0f; + } + } +} + +void +CPlaneBanner::Render(void) +{ + int i; + if(m_pos[0].z > -50.0f){ + float camDist = (TheCamera.GetPosition() - m_pos[0]).Magnitude(); + if(TheCamera.IsSphereVisible(m_pos[4], 32.0f) && camDist < 300.0f){ + TempBufferVerticesStored = 0; + TempBufferIndicesStored = 0; + int alpha = camDist < 250.0f ? 160 : (300.0f-camDist)/(300.0f-250.0f)*160; + + TempBufferVerticesStored += 2; + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[0], 255, 255, 255, alpha); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[1], 255, 255, 255, alpha); + RwIm3DVertexSetPos(&TempBufferRenderVertices[0], m_pos[2].x, m_pos[2].y, m_pos[2].z); + RwIm3DVertexSetPos(&TempBufferRenderVertices[1], m_pos[2].x, m_pos[2].y, m_pos[2].z - 4.0f); + RwIm3DVertexSetU(&TempBufferRenderVertices[0], 0.0f); + RwIm3DVertexSetV(&TempBufferRenderVertices[0], 0.0f); + RwIm3DVertexSetU(&TempBufferRenderVertices[1], 0.0f); + RwIm3DVertexSetV(&TempBufferRenderVertices[1], 1.0f); + for(i = 2; i < 8; i++){ + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+0], 255, 255, 255, alpha); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+1], 255, 255, 255, alpha); + RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+0], m_pos[i].x, m_pos[i].y, m_pos[i].z); + RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+1], m_pos[i].x, m_pos[i].y, m_pos[i].z - 4.0f); + RwIm3DVertexSetU(&TempBufferRenderVertices[TempBufferVerticesStored+0], (i-2)/5.0f); + RwIm3DVertexSetV(&TempBufferRenderVertices[TempBufferVerticesStored+0], 0.0f); + RwIm3DVertexSetU(&TempBufferRenderVertices[TempBufferVerticesStored+1], (i-2)/5.0f); + RwIm3DVertexSetV(&TempBufferRenderVertices[TempBufferVerticesStored+1], 1.0f); + TempBufferRenderIndexList[TempBufferIndicesStored+0] = TempBufferVerticesStored-2; + TempBufferRenderIndexList[TempBufferIndicesStored+1] = TempBufferVerticesStored-1; + TempBufferRenderIndexList[TempBufferIndicesStored+2] = TempBufferVerticesStored+1; + TempBufferRenderIndexList[TempBufferIndicesStored+3] = TempBufferVerticesStored-2; + TempBufferRenderIndexList[TempBufferIndicesStored+4] = TempBufferVerticesStored+1; + TempBufferRenderIndexList[TempBufferIndicesStored+5] = TempBufferVerticesStored; + TempBufferVerticesStored += 2; + TempBufferIndicesStored += 6; + } + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpRubbishTexture[2])); + +#ifdef FIX_BUGS + if(RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, rwIM3D_VERTEXXYZ|rwIM3D_VERTEXUV|rwIM3D_VERTEXRGBA)){ +#else + if(RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, 0)){ +#endif + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored); + RwIm3DEnd(); + } + + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + + TempBufferVerticesStored = 0; + TempBufferIndicesStored = 0; + } + } +} + +void +CPlaneBanner::RegisterPoint(CVector pos) +{ + m_pos[0] = pos; +} + +void +CPlaneBanners::Init(void) +{ + int i; + for(i = 0; i < ARRAY_SIZE(aArray); i++) + aArray[i].Init(); +} + +void +CPlaneBanners::Update(void) +{ + int i; + for(i = 0; i < ARRAY_SIZE(aArray); i++) + aArray[i].Update(); +} + +void +CPlaneBanners::Render(void) +{ + int i; + for(i = 0; i < ARRAY_SIZE(aArray); i++) + aArray[i].Render(); +} + +void +CPlaneBanners::RegisterPoint(CVector pos, uint32 id) +{ + aArray[id].RegisterPoint(pos); +} + +bool CSmokeTrails::CigOn = false; +CSmokeTrail CSmokeTrails::aSmoke[3]; + +RwImVertexIndex SmokeTrailIndices[32] = { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, +9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16 }; + +float RandomSmoke[16] = { 10.0f, 5.0f, -1.0f, -9.0f, -7.0f, -1.0f, 0.0f, 3.0f, 6.0f, 7.0f, 4.0f, 2.0f, +5.0f, 7.0f }; + +uint8 ScrollCharSet[59][5] = { + { 0x00, 0x00, 0x00, 0x00, 0x00 }, // ' ' + { 0x00, 0x00, 0x1D, 0x00, 0x00 }, // '!' + { 0x00, 0x00, 0x00, 0x00, 0x00 }, // '"' + { 0x0A, 0x1F, 0x0A, 0x1F, 0x0A }, // '#' + { 0x00, 0x09, 0x1F, 0x12, 0x00 }, // '$' + { 0x18, 0x18, 0x00, 0x03, 0x03 }, // '%' + { 0x00, 0x00, 0x00, 0x00, 0x00 }, // '&' + { 0x00, 0x00, 0x00, 0x00, 0x00 }, // ''' + { 0x01, 0x02, 0x04, 0x08, 0x10 }, // '(' + { 0x00, 0x00, 0x18, 0x00, 0x00 }, // ')' + { 0x15, 0x04, 0x1F, 0x04, 0x15 }, // '*' + { 0x00, 0x04, 0x0E, 0x04, 0x00 }, // '+' + { 0x00, 0x00, 0x03, 0x00, 0x00 }, // ',' + { 0x00, 0x04, 0x04, 0x04, 0x00 }, // '-' + { 0x00, 0x00, 0x01, 0x00, 0x00 }, // '.' + { 0x00, 0x00, 0x00, 0x00, 0x00 }, // '/' + { 0x0E, 0x11, 0x11, 0x11, 0x0E }, // '0' + { 0x01, 0x09, 0x1F, 0x01, 0x01 }, // '1' + { 0x03, 0x15, 0x15, 0x15, 0x09 }, // '2' + { 0x11, 0x11, 0x15, 0x15, 0x0A }, // '3' + { 0x02, 0x06, 0x0A, 0x1F, 0x02 }, // '4' + { 0x1D, 0x15, 0x15, 0x15, 0x12 }, // '5' + { 0x0E, 0x15, 0x15, 0x15, 0x12 }, // '6' + { 0x18, 0x10, 0x13, 0x14, 0x18 }, // '7' + { 0x0A, 0x15, 0x15, 0x15, 0x0A }, // '8' + { 0x08, 0x15, 0x15, 0x15, 0x0E }, // '9' + { 0x00, 0x00, 0x0A, 0x00, 0x00 }, // ':' + { 0x18, 0x18, 0x00, 0x03, 0x03 }, // ';' + { 0x04, 0x08, 0x1F, 0x08, 0x04 }, // '<' + { 0x00, 0x0A, 0x0A, 0x0A, 0x00 }, // '=' + { 0x04, 0x02, 0x1F, 0x02, 0x04 }, // '>' + { 0x10, 0x10, 0x15, 0x14, 0x1D }, // '?' + { 0x00, 0x1C, 0x14, 0x1C, 0x00 }, // '@' + { 0x0F, 0x12, 0x12, 0x12, 0x0F }, // 'A' + { 0x1F, 0x15, 0x15, 0x15, 0x0A }, // 'B' + { 0x0E, 0x11, 0x11, 0x11, 0x0A }, // 'C' + { 0x1F, 0x11, 0x11, 0x11, 0x0E }, // 'D' + { 0x1F, 0x15, 0x15, 0x11, 0x11 }, // 'E' + { 0x1F, 0x14, 0x14, 0x10, 0x10 }, // 'F' + { 0x0E, 0x11, 0x15, 0x15, 0x06 }, // 'G' + { 0x1F, 0x04, 0x04, 0x04, 0x1F }, // 'H' + { 0x11, 0x11, 0x1F, 0x11, 0x11 }, // 'I' + { 0x02, 0x01, 0x01, 0x01, 0x1E }, // 'J' + { 0x1F, 0x04, 0x0C, 0x12, 0x01 }, // 'K' + { 0x1F, 0x01, 0x01, 0x01, 0x01 }, // 'L' + { 0x1F, 0x08, 0x06, 0x08, 0x1F }, // 'M' + { 0x1F, 0x08, 0x04, 0x02, 0x1F }, // 'N' + { 0x0E, 0x11, 0x11, 0x11, 0x0E }, // 'O' + { 0x1F, 0x12, 0x12, 0x12, 0x0C }, // 'P' + { 0x0C, 0x12, 0x12, 0x13, 0x0D }, // 'Q' + { 0x1F, 0x14, 0x14, 0x16, 0x09 }, // 'R' + { 0x09, 0x15, 0x15, 0x15, 0x02 }, // 'S' + { 0x10, 0x10, 0x1F, 0x10, 0x10 }, // 'T' + { 0x1E, 0x01, 0x01, 0x01, 0x1E }, // 'U' + { 0x1C, 0x02, 0x01, 0x02, 0x1C }, // 'V' + { 0x1E, 0x01, 0x06, 0x01, 0x1E }, // 'W' + { 0x11, 0x0A, 0x04, 0x0A, 0x11 }, // 'X' + { 0x18, 0x04, 0x03, 0x04, 0x18 }, // 'Y' + { 0x11, 0x13, 0x15, 0x19, 0x11 } // 'Z' +}; + +// ---------- CMovingThings ---------- +enum eScrollBarTypes +{ + SCROLL_ARENA_STRING +}; + +CScrollBar aScrollBars[1]; + +CMovingThing CMovingThings::StartCloseList; +CMovingThing CMovingThings::EndCloseList; +int16 CMovingThings::Num; +CMovingThing CMovingThings::aMovingThings[NUMMOVINGTHINGS]; + +int CScrollBar::TonightsEvent; + +void CMovingThings::Init() +{ + StartCloseList.m_pNext = &CMovingThings::EndCloseList; + StartCloseList.m_pPrev = nil; + EndCloseList.m_pNext = nil; + EndCloseList.m_pPrev = &CMovingThings::StartCloseList; + + CPlaneTrails::Init(); + CSmokeTrails::Init(); + CPlaneBanners::Init(); + CPointLights::Init(); + + Num = 0; + + for (int32 i = 0; i < NUMMOVINGTHINGS; i++) { + aMovingThings[i].m_nType = 0; + aMovingThings[i].m_farAway = 0; + } + + for (int i = 0; i < NUMSECTORS_X; i++) { + for (int j = 0; j < NUMSECTORS_Y; j++) { + for (CPtrNode *pNode = CWorld::GetSector(i, j)->m_lists[ENTITYLIST_BUILDINGS].first; pNode; pNode = pNode->next) { + CEntity *pEntity = (CEntity *)pNode->item; + PossiblyAddThisEntity(pEntity); + } + } + } + + for (int32 i = 0; i < NUM_LEVELS; i++) { + for (CPtrNode *pNode = CWorld::GetBigBuildingList((eLevelName)i).first; pNode; pNode = pNode->next) { + CEntity *pEntity = (CEntity *)pNode->item; + PossiblyAddThisEntity(pEntity); + } + } + + CEscalators::Init(); + aScrollBars[0].Init(CVector(-1069.209f, 1320.126f, 18.848f), CVector(-1069.209f, 1342.299f, 22.612f), SCROLL_ARENA_STRING, 128, 255, 0, 0.3f); +} + +void CMovingThings::Shutdown() +{ + + aScrollBars[0].SetVisibility(false); + CEscalators::Shutdown(); +} + +void CMovingThings::Update() +{ + CPlaneBanners::Update(); + CPlaneTrails::Update(); + CEscalators::Update(); + + const int TIME_SPAN = 8; // frames to process all aMovingThings + + int16 i; + + int block = CTimer::GetFrameCounter() % TIME_SPAN; + + for (i = (block * NUMMOVINGTHINGS) / TIME_SPAN; i < ((block + 1) * NUMMOVINGTHINGS) / TIME_SPAN; i++) { + if (aMovingThings[i].m_farAway == 1) + aMovingThings[i].Update(); + } + + for (i = 0; i < CMovingThings::Num; i++) { + if (aMovingThings[i].m_farAway == 0) + aMovingThings[i].Update(); + } + + for (i = 0; i < ARRAY_SIZE(aScrollBars); ++i) + { + if (aScrollBars[i].IsVisible() || (CTimer::GetFrameCounter() + i) % 8 == 0) + aScrollBars[i].Update(); + } +} + +void CMovingThings::Render() +{ + PUSH_RENDERGROUP("CMovingThings::Render"); + CSmokeTrails::Update(); + + int i; + for (i = 0; i < ARRAY_SIZE(aScrollBars); ++i) + { + if (aScrollBars[i].IsVisible()) + aScrollBars[i].Render(); + } + + CPlaneTrails::Render(); + CSmokeTrails::Render(); + CPlaneBanners::Render(); + POP_RENDERGROUP(); +} + +void CMovingThings::RegisterOne(CEntity *pEnt, uint16 nType) { + if (Num >= NUMMOVINGTHINGS) + return; + + aMovingThings[Num].m_pEntity = pEnt; + aMovingThings[Num].m_nType = nType; + aMovingThings[Num].m_farAway = 0; + aMovingThings[Num].m_vecPosn = pEnt->GetPosition(); + aMovingThings[Num].AddToList(&CMovingThings::StartCloseList); + Num++; +} + +void CMovingThings::PossiblyAddThisEntity(CEntity *pEnt) { + if (pEnt->GetModelIndex() == MI_LIGHTBEAM) { + RegisterOne(pEnt, 1); + } + else if (pEnt->GetModelIndex() == MI_AIRPORTRADAR) { + RegisterOne(pEnt, 2); + } + else if (pEnt->GetModelIndex() == MI_MALLFAN || pEnt->GetModelIndex() == MI_HOTELFAN_NIGHT + || pEnt->GetModelIndex() == MI_HOTELFAN_DAY || pEnt->GetModelIndex() == MI_HOTROOMFAN) { + RegisterOne(pEnt, 3); + } + else if (pEnt->GetModelIndex() == MI_BLIMP_NIGHT || pEnt->GetModelIndex() == MI_BLIMP_DAY) { + RegisterOne(pEnt, 4); + } +} + +// ---------- CMovingThing ---------- +static float maxUpdateDists[5] = { 100.0f, 1500.0f, 400.0f, 100.0f, 2000.0f }; + +void CMovingThing::Update() +{ + switch (m_nType) { + case 1: { + float angle = (CTimer::GetTimeInMilliseconds() % 0x3FFF) * TWOPI / 0x3FFF; + float s = Sin(angle); + float c = Cos(angle); + m_pEntity->GetRight() = CVector(-s, c, 0.0f); + m_pEntity->GetForward() = CVector(0.0f, 0.0f, 1.0f); + m_pEntity->GetUp() = CVector(c, s, 0.0f); + + if (CClock::GetHours() >= 20 || CClock::GetHours() < 5) { + if (Abs(TheCamera.GetPosition().x - m_pEntity->GetPosition().x) < 600.0f && + Abs(TheCamera.GetPosition().y - m_pEntity->GetPosition().y) < 600.0f) { + CVector delta = m_pEntity->GetPosition() - TheCamera.GetPosition(); + delta /= delta.Magnitude(); + + if (DotProduct(delta, CVector(c, s, 0.0f)) < -0.92f) { + CVector coors = m_pEntity->GetPosition() - 10.0f * delta; + CCoronas::RegisterCorona(43, 128, 128, 100, 255, coors, 70.0f, 600.0f, 0.0f, CCoronas::TYPE_STAR, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + } + } + } + } + break; + case 2: { + float angle = (CTimer::GetTimeInMilliseconds() % 0x7FF) * TWOPI / 0x7FF; + float s = Sin(angle); + float c = Cos(angle); + m_pEntity->GetRight() = CVector(c, s, 0.0f); + m_pEntity->GetForward() = CVector(-s, c, 0.0f); + m_pEntity->GetUp() = CVector(0.0f, 0.0f, 1.0f); + } + break; + case 3: { + float angle = (CTimer::GetTimeInMilliseconds() % 0x3FF) * TWOPI / 0x3FF; + float s = Sin(angle); + float c = Cos(angle); + m_pEntity->GetRight() = CVector(c, s, 0.0f); + m_pEntity->GetForward() = CVector(-s, c, 0.0f); + m_pEntity->GetUp() = CVector(0.0f, 0.0f, 1.0f); + } + break; + case 4: { + float angle = (CTimer::GetTimeInMilliseconds() % 0x3FFFF) * TWOPI / 0x3FFFF; + float s = Sin(angle); + float c = Cos(angle); + m_pEntity->GetRight() = CVector(-c, -s, 0.0f); + m_pEntity->GetForward() = CVector(s, -c, 0.0f); + m_pEntity->GetUp() = CVector(0.0f, 0.0f, 1.0f); + m_pEntity->SetPosition(CVector(350.0f * c - 465.0f, 350.0f * s + 1163.0f, 260.0f)); + } + break; + default: + break; + } + + m_pEntity->GetMatrix().UpdateRW(); + m_pEntity->UpdateRwFrame(); + + if (SQR(m_pEntity->GetPosition().x - TheCamera.GetPosition().x) + SQR(m_pEntity->GetPosition().y - TheCamera.GetPosition().y) < SQR(maxUpdateDists[m_nType])) { + if (m_farAway == 1) { + AddToList(&CMovingThings::StartCloseList); + m_farAway = 0; + } + } + else { + if (m_farAway == 0) { + RemoveFromList(); + m_farAway = 1; + } + } +} + +void CMovingThing::AddToList(CMovingThing *pThing) +{ + m_pNext = pThing->m_pNext; + m_pPrev = pThing; + pThing->m_pNext = this; + m_pNext->m_pPrev = this; +} + +void CMovingThing::RemoveFromList() +{ + m_pNext->m_pPrev = m_pPrev; + m_pPrev->m_pNext = m_pNext; +} + +int16 CMovingThing::SizeList() +{ + CMovingThing *next = m_pNext; + int16 count = 0; + + while (next != nil) { + next = next->m_pNext; + count++; + } + + return count; +} + +char String_Time[] = "THE TIME IS 12:34 "; +const char* FindTimeMessage() +{ + String_Time[12] = '0' + CClock::GetHours() / 10; + String_Time[13] = '0' + CClock::GetHours() % 10; + String_Time[15] = '0' + CClock::GetMinutes() / 10; + String_Time[16] = '0' + CClock::GetMinutes() % 10; + return String_Time; +} + +// ---------- CScrollBar ---------- +void CScrollBar::Init(CVector pos1, CVector pos2, uint8 type, uint8 red, uint8 green, uint8 blue, float scale) +{ + for (int i = 0; i < ARRAY_SIZE(m_MessageBar); ++i) + m_MessageBar[i] = 0; + + m_pMessage = ". "; + m_MessageCurrentChar = 0; + m_MessageLength = strlen(m_pMessage); + + m_Counter = 0; + m_bVisible = false; + m_Position = pos1; + m_Type = type; + m_Size.x = (pos2.x - pos1.x) * 0.025f; + m_Size.y = (pos2.y - pos1.y) * 0.025f; + m_Size.z = (pos2.z - pos1.z) * 0.2f; + m_uRed = red; + m_uGreen = green; + m_uBlue = blue; + m_fScale = scale; +} + +void CScrollBar::Update() +{ + float distanceFromCamera = (TheCamera.GetPosition() - m_Position).Magnitude(); + if (distanceFromCamera > 100.0f) + { + m_bVisible = false; + return; + } + + m_bVisible = true; + + if (distanceFromCamera < 75.0f) + m_fIntensity = 1.0f; + else + m_fIntensity = 1.0f - 4.0f * (distanceFromCamera - 75.0f) / 100.0f; + + m_Counter = (m_Counter + 1) % 8; + + // if message is fully printed, load up the next one + if (m_Counter == 0 && ++m_MessageCurrentChar >= m_MessageLength) + { + const char* previousMessage = m_pMessage; + if (m_Type == SCROLL_ARENA_STRING) { + while (previousMessage == m_pMessage) + { + switch (CGeneral::GetRandomNumber() % 4) + { + case 0: + switch (TonightsEvent) { + case 0: + m_pMessage = "MAIN EVENT TONIGHT: CAR RACING . . . "; + break; + case 1: + m_pMessage = "MAIN EVENT TONIGHT: DESTRUCTION DERBY . . . "; + break; + case 2: + m_pMessage = "MAIN EVENT TONIGHT: BIKE RACING . . . "; + break; + } + break; + case 1: + switch (TonightsEvent) { + case 0: + m_pMessage = "FOR TICKETS TO THE HOT RING EVENT CALL 555-3764 . . . "; + break; + case 1: + m_pMessage = "FOR TICKETS TO THE BLOOD RING EVENT CALL 555-3765 . . . "; + break; + case 2: + m_pMessage = "FOR TICKETS TO THE DIRT RING EVENT CALL 555-3766 . . . "; + break; + } + break; + case 2: + m_pMessage = "HYMAN MEMORIAL STADIUM. HOME TO SOME OF THE BIGGEST EVENTS OF" + " THE WESTERN HEMISPHERE. ALSO AVAILABLE FOR CHILDREN PARTIES. . . "; + break; + case 3: + m_pMessage = FindTimeMessage(); + break; + default: + break; + } + } + } + + m_MessageLength = (uint32)strlen(m_pMessage); + m_MessageCurrentChar = 0; + } + + // Scroll + for (int i = 0; i < ARRAY_SIZE(m_MessageBar)-1; i++) + m_MessageBar[i] = m_MessageBar[i + 1]; + m_MessageBar[ARRAY_SIZE(m_MessageBar)-1] = m_Counter < 5 ? ScrollCharSet[m_pMessage[m_MessageCurrentChar] - ' '][m_Counter] : 0; + + // Introduce some random displaying glitches; signs aren't supposed to be perfect :P + switch (CGeneral::GetRandomNumber() & 0xFF) + { + case 0x0D: m_MessageBar[ARRAY_SIZE(m_MessageBar)-1] = 0; break; + case 0xE3: m_MessageBar[ARRAY_SIZE(m_MessageBar)-1] = 0xE3; break; + case 0x64: m_MessageBar[ARRAY_SIZE(m_MessageBar)-1] = ~m_MessageBar[ARRAY_SIZE(m_MessageBar)-1]; break; + } +} + +void CScrollBar::Render() +{ + if (!TheCamera.IsSphereVisible(m_Position, 2.0f * 20.0f * (ABS(m_Size.x) + ABS(m_Size.y)))) + return; + + CSprite::InitSpriteBuffer(); + + // Calculate intensity of colours + uint8 r = m_fIntensity * m_uRed; + uint8 g = m_fIntensity * m_uGreen; + uint8 b = m_fIntensity * m_uBlue; + + // Set render states + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[0])); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + + CVector coronaCoord, screenCoord; + float screenW, screenH; + for (int i = 1; i < ARRAY_SIZE(m_MessageBar); ++i) + { + for (int j = 0; j < 5; ++j) + { + coronaCoord.x = m_Position.x + m_Size.x * i; + coronaCoord.y = m_Position.y + m_Size.y * i; + coronaCoord.z = m_Position.z + m_Size.z * j; + + // Render main coronas + if (m_MessageBar[i] & (1 << j)) + { + if (CSprite::CalcScreenCoors(coronaCoord, &screenCoord, &screenW, &screenH, true)) + { + CSprite::RenderBufferedOneXLUSprite( + screenCoord.x, screenCoord.y, screenCoord.z, + screenW * m_fScale, screenH * m_fScale, + r, g, b, + 255, 1.0f / screenCoord.z, 255); + } + } + // Render smaller and faded coronas for a trailing effect + else if (m_MessageBar[i - 1] & (1 << j)) + { + if (CSprite::CalcScreenCoors(coronaCoord, &screenCoord, &screenW, &screenH, true)) + { + CSprite::RenderBufferedOneXLUSprite( + screenCoord.x, screenCoord.y, screenCoord.z, + screenW * m_fScale * 0.8f, + screenH * m_fScale * 0.8f, + r / 2, + g / 2, + b / 2, + 255, 1.0f / screenCoord.z, 255); + } + } + } + } + + CSprite::FlushSpriteBuffer(); +} + +void +CSmokeTrail::RegisterPoint(CVector regPosition, float opacity) { + bool bAddedNewPoint = false; + + if (m_time[0] && CTimer::GetTimeInMilliseconds() - m_time[0] > 150) { + bAddedNewPoint = true; + for (int32 i = 15; i > 0; i--) { + m_pos[i] = m_pos[i - 1]; + m_time[i] = m_time[i - 1]; + m_opacity[i] = m_opacity[i - 1]; + } + ++m_seed; + } + m_pos[0] = regPosition; + + if (bAddedNewPoint || !m_time[0]) { + m_time[0] = CTimer::GetTimeInMilliseconds(); + float density = 0.1f / (m_pos[1] - m_pos[2]).Magnitude(); + m_opacity[1] = opacity * Min(density, 1.0f); + } + m_opacity[0] = 0.0f; +} + +void +CSmokeTrail::Init(int num) { + for (int32 i = 0; i < 16; i++) + m_time[i] = 0; + m_seed = num * 2; +} + +void +CSmokeTrails::Init(void) { + for (int32 i = 0; i < 3; i++) + aSmoke[i].Init(i); +} + +void +CSmokeTrails::Render(void) { + for (int32 i = 0; i < 3; i++) + aSmoke[i].Render(); +} + +void +CSmokeTrail::Render(void) { + int numVerts = 0; + + if (TheCamera.IsSphereVisible(m_pos[0], 10.0f)) { + for (int32 i = 0; i < 16; i++) { + int timeSinceSpawned = CTimer::GetTimeInMilliseconds() - m_time[i]; + + if (timeSinceSpawned > 2250) + m_time[i] = 0; + + if (m_time[i]) { + int alpha = (1.0f - timeSinceSpawned / 2250.0f) * 110.0f * m_opacity[i]; + float offset = timeSinceSpawned * CWeather::Wind * 0.0001f; + float posX = (m_pos[i].x + timeSinceSpawned * RandomSmoke[(i - m_seed) & 0xF] * 0.00001f) - offset; + float posY = (m_pos[i].y + timeSinceSpawned * RandomSmoke[(i - m_seed + 5) & 0xF] * 0.00001f) - offset; + float posZ = m_pos[i].z + timeSinceSpawned * 0.0004f; + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[i], 200, 200, 200, alpha); + RwIm3DVertexSetPos(&TempBufferRenderVertices[i], posX, posY, posZ); + numVerts++; + } + } + } + + if (numVerts > 1) { + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + + if (RwIm3DTransform(TempBufferRenderVertices, numVerts, nil, rwIM3D_VERTEXXYZ | rwIM3D_VERTEXRGBA)) { + RwIm3DRenderIndexedPrimitive(rwPRIMTYPEPOLYLINE, SmokeTrailIndices, 2 * (numVerts - 1)); + RwIm3DEnd(); + } + } +} + +void +CSmokeTrails::Update(void) { + + if (!CSmokeTrails::CigOn || TheCamera.Using1stPersonWeaponMode() || !FindPlayerPed() || + FindPlayerVehicle() || CCutsceneMgr::IsRunning() || !FindPlayerPed()->GetClump()) + return; + + RwV3d startPos = { 0.026f, 0.15f, 0.02f }; + RwV3d endPos = { 0.026f, 0.05f, 0.02f }; + + RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(FindPlayerPed()->GetClump()); + int32 idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_HEAD)); + RwMatrix *head = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; + RwV3dTransformPoints(&startPos, &startPos, 1, head); + RwV3dTransformPoints(&endPos, &endPos, 1, head); + + aSmoke[0].RegisterPoint(startPos, 1.0f); + aSmoke[1].RegisterPoint(startPos, 0.75f); + aSmoke[2].RegisterPoint(startPos, 0.5f); + + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[0], 255, 255, 255, 255); + RwIm3DVertexSetPos(&TempBufferRenderVertices[0], startPos.x, startPos.y, startPos.z); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[1], 255, 255, 255, 255); + RwIm3DVertexSetPos(&TempBufferRenderVertices[1], endPos.x, endPos.y, endPos.z); + + if (RwIm3DTransform(TempBufferRenderVertices, 2, nil, rwIM3D_VERTEXXYZ | rwIM3D_VERTEXRGBA)) { + RwIm3DRenderIndexedPrimitive(rwPRIMTYPEPOLYLINE, SmokeTrailIndices, 2); + RwIm3DEnd(); + } +} + +CEscalator CEscalators::aEscalators[NUM_ESCALATORS]; +int32 CEscalators::NumEscalators; + +CEscalator::CEscalator() { + m_bIsActive = false; + + for (int i = 0; i < 24; i++) { + m_pSteps[i] = nil; + } +} + +void +CEscalator::AddThisOne(CVector pos0, CVector pos1, CVector pos2, CVector pos3, bool b_isMovingDown) { + m_pos0 = pos0; + m_pos1 = pos1; + m_pos2 = pos2; + m_pos3 = pos3; + + float escalatorStepHeight = CModelInfo::GetColModel(MI_ESCALATORSTEP)->boundingBox.max.z; + m_pos0.z -= escalatorStepHeight; + m_pos1.z -= escalatorStepHeight; + m_pos2.z -= escalatorStepHeight; + m_pos3.z -= escalatorStepHeight; + + float magnitudes[3]; + magnitudes[0] = (m_pos0 - m_pos1).Magnitude(); + magnitudes[1] = (m_pos1 - m_pos2).Magnitude(); + magnitudes[2] = (m_pos2 - m_pos3).Magnitude(); + + float length = magnitudes[0] + magnitudes[1] + magnitudes[2]; + + m_lowerEnd = magnitudes[0] / length; + m_upperEnd = (magnitudes[0] + magnitudes[1]) / length; + + m_stepsCount = Min(24.0f, length / 0.6f); + + CVector direction(m_pos0.x - m_pos1.x, m_pos0.y - m_pos1.y, 0.0f); + direction.Normalise(); + + m_matrix.GetUp() = CVector(0.0f, 0.0f, 1.0f); + m_matrix.GetForward() = CVector(direction.x, direction.y, 0.0f); + m_matrix.GetRight() = CVector(direction.y, -direction.x, 0.0f); + m_matrix.GetPosition() = CVector(0.0f, 0.0f, 0.0f); + + m_bIsMovingDown = b_isMovingDown; + + m_midPoint = (m_pos0 + m_pos3) / 2.0f; + + m_radius = (m_pos0 - m_midPoint).Magnitude(); +} + +void +CEscalator::Update(void) { + if (!m_bIsActive) { + if ((TheCamera.GetPosition() - m_midPoint).Magnitude() < 25.0f) { + if (TheCamera.IsSphereVisible(m_midPoint, m_radius) && (m_stepsCount + 10 < CPools::GetObjectPool()->GetNoOfFreeSpaces())) { + m_bIsActive = true; + for (int i = 0; i < m_stepsCount; i++) { + m_pSteps[i] = new CObject(MI_ESCALATORSTEP, TRUE); + if (m_pSteps[i]) { + m_pSteps[i]->SetPosition(m_pos1); + CWorld::Add(m_pSteps[i]); + m_pSteps[i]->ObjectCreatedBy = CONTROLLED_SUB_OBJECT; + } + } + } + } + } + + if (m_bIsActive) { + float time = (CTimer::GetTimeInMilliseconds() % 16384) / 16384.0f; + for (int i = 0; i < m_stepsCount; i++) { + if (m_pSteps[i]) { + float t = i / (float)m_stepsCount + time; + + if (t > 1.0f) + t -= 1.0f; + + if (m_bIsMovingDown) + t = 1.0f - t; + + CVector oldPosition = m_pSteps[i]->GetPosition(); + m_pSteps[i]->GetMatrix() = m_matrix; + + CVector newPosition; + if (t < m_lowerEnd) { + float ratio = t / m_lowerEnd; + newPosition = (ratio * m_pos1) + ((1.0f - ratio) * m_pos0); + } + else if (t < m_upperEnd) { + float ratio = (t - m_lowerEnd) / (m_upperEnd - m_lowerEnd); + newPosition = (ratio * m_pos2) + ((1.0f - ratio) * m_pos1); + } + else { + float ratio = (t - m_upperEnd) / (1.0f - m_upperEnd); + newPosition = (ratio * m_pos3) + ((1.0f - ratio) * m_pos2); + } + + m_pSteps[i]->SetPosition(newPosition); + m_pSteps[i]->m_vecMoveSpeed = (newPosition - oldPosition) / Max(CTimer::GetTimeStep(), 1.0f); + m_pSteps[i]->GetMatrix().UpdateRW(); + m_pSteps[i]->UpdateRwFrame(); + } + if ((TheCamera.GetPosition() - m_midPoint).Magnitude() > 28.0f || !TheCamera.IsSphereVisible(m_midPoint, m_radius)) + SwitchOff(); + } + } +} + +bool deletingEscalator; + +void +CEscalator::SwitchOff(void) { + if (m_bIsActive) { + for (int i = 0; i < m_stepsCount; i++) { + if (m_pSteps[i]) { + CWorld::Remove(m_pSteps[i]); + deletingEscalator = true; + delete m_pSteps[i]; + m_pSteps[i] = nil; + deletingEscalator = false; + } + } + m_bIsActive = false; + } +} + +void +CEscalators::AddOne(CVector pos0, CVector pos1, CVector pos2, CVector pos3, bool b_isMovingDown) { + aEscalators[NumEscalators++].AddThisOne(pos0, pos1, pos2, pos3, b_isMovingDown); +} + +void +CEscalators::Init(void) { + Shutdown(); + NumEscalators = 0; + + AddOne(CVector(-9.82999f, -938.04498f, 9.4219f), CVector(-8.573f, -938.04498f, 9.4219f), + CVector(-0.747f, -938.045f, 15.065f), CVector(0.88f, -938.045f, 15.065f), TRUE); + + AddOne(CVector(-9.83f, -939.966f, 9.422f), CVector(-8.573f, -939.966f, 9.422f), + CVector(-0.747f, -939.966f, 15.065f), CVector(0.880f, -939.966f, 15.065f), FALSE); + + AddOne(CVector(408.116f, 1058.36f, 18.261f), CVector(408.094f, 1057.04f, 18.261f), + CVector(408.116f, 1048.0f, 24.765f), CVector(408.094f, 1046.57f, 24.799f), TRUE); + + AddOne(CVector(406.195f, 1058.36f, 18.261f), CVector(406.173f, 1057.04f, 18.261f), + CVector(406.195f, 1048.0f, 24.729f), CVector(406.173f, 1046.57f, 24.79f), FALSE); + + AddOne(CVector(421.729f, 1058.3789f, 18.075f), CVector(421.707f, 1057.052f, 18.099f), + CVector(421.729f, 1048.016f, 24.604f), CVector(421.707f, 1046.589f, 24.637f), TRUE); + + AddOne(CVector(419.808f, 1058.378f, 18.099f), CVector(419.786f, 1057.052f, 18.099f), + CVector(419.808f, 1048.016f, 24.568f), CVector(419.786f, 1046.589f, 24.637f), FALSE); + + AddOne(CVector(412.69901f, 1102.729f, 17.569f), CVector(412.72198f, 1104.057f, 17.57f), + CVector(412.69901f, 1113.092f, 24.073f), CVector(412.72198f, 1114.3201f, 24.108f), TRUE); + + AddOne(CVector(414.62f, 1102.729f, 17.569f), CVector(414.64301f, 1104.057f, 17.57f), + CVector(414.62f, 1113.092f, 24.037001f), CVector(414.64301f, 1114.3201f, 24.099001f), FALSE); + + AddOne(CVector(414.64301f, 1145.589f, 17.57f), CVector(414.62f, 1144.261f, 17.569f), + CVector(414.64301f, 1135.226f, 24.073999f), CVector(414.62f, 1133.798f, 24.107f), TRUE); + + AddOne(CVector(412.72198f, 1145.589f, 17.57f), CVector(412.69901f, 1144.261f, 17.569f), + CVector(412.72198f, 1135.226f, 24.038f), CVector(412.69901f, 1133.798f, 24.098f), FALSE); + + AddOne(CVector(406.05099f, 1193.4771f, 18.016001f), CVector(406.07401f, 1194.8051f, 18.017f), + CVector(406.05099f, 1203.84f, 24.52f), CVector(406.07401f, 1205.2679f, 24.555f), TRUE); + + AddOne(CVector(407.97198f, 1193.4771f, 18.016001f), CVector(407.995f, 1194.8051f, 18.017f), + CVector(407.97198f, 1203.84f, 24.483999f), CVector(407.995f, 1205.2679f, 24.546f), FALSE); + + AddOne(CVector(419.659f, 1193.479f, 17.979f), CVector(419.68201f, 1194.807f, 17.98f), + CVector(419.659f, 1203.842f, 24.483f), CVector(419.68201f, 1205.27f, 24.518f), TRUE); + + AddOne(CVector(421.57999f, 1193.479f, 17.979f), CVector(421.603f, 1194.807f, 17.98f), + CVector(421.57999f, 1203.842f, 24.447001f), CVector(421.603f, 1205.27f, 24.509001f), FALSE); + + AddOne(CVector(406.23199f, 1022.857f, 17.917f), CVector(406.23199f, 1024.1851f, 17.917f), + CVector(406.23199f, 1033.22f, 24.521f), CVector(406.23199f, 1034.647f, 24.555f), TRUE); + + AddOne(CVector(408.15302f, 1022.857f, 17.917f), CVector(408.15302f, 1024.1851f, 17.916f), + CVector(408.15302f, 1033.22f, 24.486f), CVector(408.15302f, 1034.647f, 24.52f), FALSE); + + AddOne(CVector(-1506.39f, -813.13f, 13.834f), CVector(-1506.177f, -814.51703f, 13.834f), + CVector(-1504.566f, -823.20898f, 19.836f), CVector(-1504.329f, -824.48499f, 19.837f), FALSE); + + AddOne(CVector(-1481.951f, -859.05402f, 13.834f), CVector(-1482.7791f, -858.22498f, 13.834f), + CVector(-1489.03f, -851.974f, 19.836f), CVector(-1489.948f, -851.05701f, 19.837f), TRUE); + + AddOne(CVector(-1461.743f, -871.35901f, 13.834f), CVector(-1460.62f, -871.69202f, 13.834f), + CVector(-1452.144f, -874.20203f, 19.836f), CVector(-1450.9f, -874.57098f, 19.837f), FALSE); + + AddOne(CVector(-1409.889f, -871.41498f, 13.834f), CVector(-1411.0129f, -871.74701f, 13.834f), + CVector(-1419.489f, -874.258f, 19.836f), CVector(-1420.733f, -874.62701f, 19.837f), TRUE); + + AddOne(CVector(-1389.577f, -858.89301f, 13.834f), CVector(-1388.7271f, -858.08698f, 13.834f), + CVector(-1382.314f, -852.00201f, 19.836f), CVector(-1381.373f, -851.10797f, 19.837f), FALSE); + + AddOne(CVector(-1364.981f, -813.13f, 13.834f), CVector(-1365.204f, -814.28003f, 13.834f), + CVector(-1366.891f, -822.95801f, 19.83f), CVector(-1367.139f, -824.23199f, 19.837f), TRUE); + + for (int i = 0; i < NUM_ESCALATORS; i++) { + aEscalators[i].SwitchOff(); + } +} + +void +CEscalators::Update(void) { + if (CReplay::IsPlayingBack()) + return; + + for (int i = 0; i < NUM_ESCALATORS; i++) { + aEscalators[i].Update(); + } +} + +void +CEscalators::Shutdown(void) { + for (int i = 0; i < NUM_ESCALATORS; i++) { + aEscalators[i].SwitchOff(); + } + NumEscalators = 0; +} + + +CScriptPath CScriptPaths::aArray[3]; + +void CScriptPath::FindCoorsFromDistanceOnPath(float t, float *pX, float *pY, float *pZ) +{ + int32 i; + for (i = 0; m_pNode[i + 1].t < t; i++) + if (i == m_numNodes - 1) { + // don't go beyond last node + *pX = m_pNode[m_numNodes - 1].p.x; + *pY = m_pNode[m_numNodes - 1].p.y; + *pZ = m_pNode[m_numNodes - 1].p.z; + return; + } + float f = (t - m_pNode[i].t) / (m_pNode[i + 1].t - m_pNode[i].t); + *pX = (1.0f - f)*m_pNode[i].p.x + f*m_pNode[i + 1].p.x; + *pY = (1.0f - f)*m_pNode[i].p.y + f*m_pNode[i + 1].p.y; + *pZ = (1.0f - f)*m_pNode[i].p.z + f*m_pNode[i + 1].p.z; +} + +void CScriptPath::Update(void) { + if (m_state != SCRIPT_PATH_ACTIVE) + return; + + m_fPosition += m_fSpeed * CTimer::GetTimeStepInSeconds(); + m_fPosition = Clamp(m_fPosition, 0.0f, m_fTotalLength); + + if (m_pObjects[0] || m_pObjects[1] || m_pObjects[2] || m_pObjects[3] + || m_pObjects[4] || m_pObjects[5]) { + + float t1, t2; + CVector pos1, pos2; + + t1 = Max(m_fPosition - m_fObjectLength / 2.0f, 0.0f); + FindCoorsFromDistanceOnPath(t1, &pos1.x, &pos1.y, &pos1.z); + t2 = Min(m_fPosition + m_fObjectLength / 2.0f, m_fTotalLength); + FindCoorsFromDistanceOnPath(t2, &pos2.x, &pos2.y, &pos2.z); + + CVector newForward, newUp(0.0f, 0.0f, 1.0f), newRight; + + newForward = pos2 - pos1; + newForward.Normalise(); + newRight = CrossProduct(newForward, newUp); + newRight.Normalise(); + newUp = CrossProduct(newRight, newForward); + + for (int i = 0; i < 6; i++) { + if (m_pObjects[i]) { + CMatrix prevMat(m_pObjects[i]->GetMatrix()); + CVector prevPosition = m_pObjects[i]->GetPosition(); + + m_pObjects[i]->SetPosition((pos1 + pos2) / 2.0f); + m_pObjects[i]->GetRight() = newRight; + m_pObjects[i]->GetUp() = newUp; + m_pObjects[i]->GetForward() = newForward; + m_pObjects[i]->GetMatrix().UpdateRW(); + m_pObjects[i]->UpdateRwFrame(); + + if (!m_pObjects[i]->bIsBIGBuilding && prevPosition != m_pObjects[i]->GetPosition()) + m_pObjects[i]->RemoveAndAdd(); + + m_pObjects[i]->GetMatrix().UpdateRW(); + m_pObjects[i]->UpdateRwFrame(); + + m_pObjects[i]->m_vecMoveSpeed = (m_pObjects[i]->GetPosition() - prevMat.GetPosition()) / CTimer::GetTimeStep(); + + float deltaAngle = m_pObjects[i]->GetForward().Heading() - prevMat.GetForward().Heading(); + while (deltaAngle < (float)PI) deltaAngle += (float)TWOPI; + while (deltaAngle > (float)PI) deltaAngle -= (float)TWOPI; + float zTurnSpeed = deltaAngle / CTimer::GetTimeStep(); + + m_pObjects[i]->m_vecTurnSpeed = CVector(0.0f, 0.0f, zTurnSpeed); + m_pObjects[i]->m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); + m_pObjects[i]->m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); + } + } + } +} + +void CScriptPath::Clear(void) { + if (m_pNode) + delete[] m_pNode; + m_pNode = nil; + m_numNodes = 0; + for (int i = 0; i < 6; i++) + m_pObjects[i] = nil; + m_state = SCRIPT_PATH_DISABLED; +} + +void CScriptPath::InitialiseOne(int32 numNodes, float length) { + char Dest[32]; + sprintf(Dest, "data\\paths\\spath%d.dat", numNodes); + m_pNode = CPlane::LoadPath(Dest, m_numNodes, m_fTotalLength, false); + m_fSpeed = 1.0f; + m_fPosition = 0.0f; + m_fObjectLength = length; + m_state = SCRIPT_PATH_INITIALIZED; +} + +void CScriptPath::SetObjectToControl(CObject *pObj) { + int32 i = 0; + while (i < 6 && m_pObjects[i]) + i++; + m_pObjects[i] = pObj; + pObj->RegisterReference((CEntity**)&m_pObjects[i]); + pObj->m_phy_flagA08 = false; + m_state = SCRIPT_PATH_ACTIVE; +} + +void CScriptPaths::Init(void) { + for (int i = 0; i < 3; i++) + aArray[i].Clear(); +} + +void CScriptPaths::Shutdown(void) { + for (int i = 0; i < 3; i++) + aArray[i].Clear(); +} + +void CScriptPaths::Update(void) { + for (int i = 0; i < 3; i++) + aArray[i].Update(); +} + +bool CScriptPaths::IsOneActive(void) { + for (int i = 0; i < 3; i++) + if (aArray[i].m_state == SCRIPT_PATH_ACTIVE && aArray[i].m_fSpeed != 0.0f) + return true; + + return false; +} + +void CScriptPaths::Load(uint8 *buf, uint32 size) { +INITSAVEBUF + for (int32 i = 0; i < 3; i++) + aArray[i].Clear(); + + for (int32 i = 0; i < 3; i++) { +#ifdef COMPATIBLE_SAVES + ReadSaveBuf(&aArray[i].m_numNodes, buf); + SkipSaveBuf(buf, 4); + ReadSaveBuf(&aArray[i].m_fTotalLength, buf); + ReadSaveBuf(&aArray[i].m_fSpeed, buf); + ReadSaveBuf(&aArray[i].m_fPosition, buf); + ReadSaveBuf(&aArray[i].m_fObjectLength, buf); + ReadSaveBuf(&aArray[i].m_state, buf); +#else + ReadSaveBuf(&aArray[i], buf); +#endif + + for (int32 j = 0; j < 6; j++) { +#ifdef COMPATIBLE_SAVES + aArray[i].m_pObjects[j] = nil; + int32 tmp; + ReadSaveBuf(&tmp, buf); + if (tmp != 0) { + aArray[i].m_pObjects[j] = CPools::GetObjectPool()->GetSlot(tmp - 1); + aArray[i].m_pObjects[j]->m_phy_flagA08 = false; + } +#else + CScriptPath *pPath = &aArray[i]; + if (pPath->m_pObjects[j] != nil) { + pPath->m_pObjects[j] = CPools::GetObjectPool()->GetSlot((uintptr)pPath->m_pObjects[j] - 1); + pPath->m_pObjects[j]->m_phy_flagA08 = false; + } +#endif + } + + aArray[i].m_pNode = new CPlaneNode[aArray[i].m_numNodes]; + for (int32 j = 0; j < aArray[i].m_numNodes; j++) { + ReadSaveBuf(&aArray[i].m_pNode[j], buf); + } + } +VALIDATESAVEBUF(size) +} + +void CScriptPaths::Save(uint8 *buf, uint32 *size) { + *size = SCRIPTPATHS_SAVE_SIZE; +INITSAVEBUF + for (int32 i = 0; i < 3; i++) { +#ifdef COMPATIBLE_SAVES + WriteSaveBuf(buf, aArray[i].m_numNodes); + ZeroSaveBuf(buf, 4); + WriteSaveBuf(buf, aArray[i].m_fTotalLength); + WriteSaveBuf(buf, aArray[i].m_fSpeed); + WriteSaveBuf(buf, aArray[i].m_fPosition); + WriteSaveBuf(buf, aArray[i].m_fObjectLength); + WriteSaveBuf(buf, aArray[i].m_state); +#else + CScriptPath *pPath = WriteSaveBuf(buf, aArray[i]); +#endif + + for (int32 j = 0; j < 6; j++) { +#ifdef COMPATIBLE_SAVES + WriteSaveBuf(buf, aArray[i].m_pObjects[j] != nil ? CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(aArray[i].m_pObjects[j]) + 1 : 0); +#else + if (pPath->m_pObjects[j] != nil) + pPath->m_pObjects[j] = (CObject*)(CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(pPath->m_pObjects[j]) + 1); +#endif + } + + for (int32 j = 0; j < aArray[i].m_numNodes; j++) { + WriteSaveBuf(buf, aArray[i].m_pNode[j]); + *size += sizeof(aArray[i].m_pNode[j]); + } + } +VALIDATESAVEBUF(*size); +} + +CObject *g_pScriptPathObjects[18]; + +void CScriptPaths::Load_ForReplay(void) { + for (int i = 0; i < 3; i++) { + for (int32 j = 0; j < 6; j++) { + aArray[i].m_pObjects[j] = g_pScriptPathObjects[6 * i + j]; + } + } +} + +void CScriptPaths::Save_ForReplay(void) { + for (int i = 0; i < 3; i++) { + for (int32 j = 0; j < 6; j++) { + g_pScriptPathObjects[6 * i + j] = aArray[i].m_pObjects[j]; + } + } +} diff --git a/src/miami/renderer/Fluff.h b/src/miami/renderer/Fluff.h new file mode 100644 index 00000000..58c8410c --- /dev/null +++ b/src/miami/renderer/Fluff.h @@ -0,0 +1,205 @@ +#pragma once +#include "common.h" +#include "Vector.h" +#include "Object.h" +#include "Plane.h" + +enum { + SCRIPT_PATH_DISABLED = 0, + SCRIPT_PATH_INITIALIZED, + SCRIPT_PATH_ACTIVE +}; + +class CScriptPath +{ +public: + int32 m_numNodes; + CPlaneNode *m_pNode; + float m_fTotalLength; + float m_fSpeed; + float m_fPosition; + float m_fObjectLength; + int32 m_state; + CObject *m_pObjects[6]; + + void Clear(void); + void Update(void); + void InitialiseOne(int32 numNodes, float length); + void FindCoorsFromDistanceOnPath(float t, float *pX, float *pY, float *pZ); + void SetObjectToControl(CObject *pObj); +}; + +class CScriptPaths +{ +public: + static CScriptPath aArray[3]; + static void Init(void); + static void Shutdown(void); + static void Update(void); + static bool IsOneActive(void); + static void Save(uint8 *buf, uint32 *size); + static void Load(uint8 *buf, uint32 size); + static void Save_ForReplay(); + static void Load_ForReplay(); +}; + +class CPlaneTrail +{ + CVector m_pos[16]; + int32 m_time[16]; +public: + void Init(void); + void Render(float visibility); + void RegisterPoint(CVector pos); +}; + +class CPlaneTrails +{ + static CPlaneTrail aArray[6]; // NB: 3 CPlanes and 3 hardcoded far away ones +public: + static void Init(void); + static void Update(void); + static void Render(void); + static void RegisterPoint(CVector pos, uint32 id); +}; + +class CPlaneBanner +{ + CVector m_pos[8]; +public: + void Init(void); + void Update(void); + void Render(void); + void RegisterPoint(CVector pos); +}; + +class CPlaneBanners +{ + static CPlaneBanner aArray[5]; +public: + static void Init(void); + static void Update(void); + static void Render(void); + static void RegisterPoint(CVector pos, uint32 id); +}; + +class CEscalator +{ + CVector m_pos0; + CVector m_pos1; + CVector m_pos2; + CVector m_pos3; + CMatrix m_matrix; + bool m_bIsActive; + bool m_bIsMovingDown; + int32 m_stepsCount; + float m_lowerEnd; + float m_upperEnd; + CVector m_midPoint; + float m_radius; + CObject *m_pSteps[24]; +public: + CEscalator(); + void Update(void); + void SwitchOff(void); + void AddThisOne(CVector pos0, CVector pos1, CVector pos2, CVector pos3, bool b_isMovingDown); + bool IsActive() const { return m_bIsActive; }; + const CVector& GetPosition() const { return m_midPoint; }; +}; + +class CEscalators +{ + static CEscalator aEscalators[NUM_ESCALATORS]; +public: + static int32 NumEscalators; + static void Init(void); + static void Update(void); + static void AddOne(CVector pos0, CVector pos1, CVector pos2, CVector pos3, bool b_isMovingDown); + static void Shutdown(void); + static const CEscalator& GetEscalator(int ind) { return aEscalators[ind]; }; +}; + +class CMovingThing +{ +public: + CMovingThing *m_pNext; + CMovingThing *m_pPrev; + int16 m_nType; + int16 m_farAway; + CVector m_vecPosn; + CEntity* m_pEntity; + + void Update(); + void AddToList(CMovingThing *pThing); + void RemoveFromList(); + int16 SizeList(); +}; + +#define NUMMOVINGTHINGS 48 + +class CMovingThings +{ +public: + static CMovingThing StartCloseList; + static CMovingThing EndCloseList; + static int16 Num; + static CMovingThing aMovingThings[NUMMOVINGTHINGS]; + + static void Init(); + static void Shutdown(); + static void Update(); + static void Render(); + static void PossiblyAddThisEntity(CEntity *pEnt); + static void RegisterOne(CEntity *pEnt, uint16 nType); +}; + +class CScrollBar +{ +private: + uint8 m_Counter; + const char* m_pMessage; + CVector m_Position; + uint32 m_MessageCurrentChar; + uint32 m_MessageLength; + CVector m_Size; + float m_fIntensity; + uint8 m_MessageBar[40]; + uint8 m_Type; + bool m_bVisible; + uint8 m_uRed; + uint8 m_uGreen; + uint8 m_uBlue; + float m_fScale; + +public: + static int TonightsEvent; + +public: + void SetVisibility(bool visible) { m_bVisible = visible; } + bool IsVisible() { return m_bVisible; } + + void Init(CVector pos1, CVector pos2, uint8 type, uint8 red, uint8 green, uint8 blue, float scale); + void Update(); + void Render(); +}; + +class CSmokeTrail { + CVector m_pos[16]; + float m_opacity[16]; + int m_time[16]; + char m_unused[536]; + int m_seed; +public: + void Render(void); + void RegisterPoint(CVector position, float a); + void Init(int num); +}; + +class CSmokeTrails { + static CSmokeTrail aSmoke[3]; +public: + static bool CigOn; + static void Update(void); + static void Render(void); + static void Init(void); +}; \ No newline at end of file diff --git a/src/miami/renderer/Font.cpp b/src/miami/renderer/Font.cpp new file mode 100644 index 00000000..606193b9 --- /dev/null +++ b/src/miami/renderer/Font.cpp @@ -0,0 +1,1938 @@ +#include "common.h" + +#include "Sprite2d.h" +#include "TxdStore.h" +#include "Font.h" +#ifdef BUTTON_ICONS +#include "FileMgr.h" +#endif +#include "Timer.h" + +void +AsciiToUnicode(const char *src, wchar *dst) +{ + while((*dst++ = (unsigned char)*src++) != '\0'); +} + +void +UnicodeStrcat(wchar *dst, wchar *append) +{ + UnicodeStrcpy(&dst[UnicodeStrlen(dst)], append); +} + +void +UnicodeStrcpy(wchar *dst, const wchar *src) +{ + while((*dst++ = *src++) != '\0'); +} + +int +UnicodeStrlen(const wchar *str) +{ + int len; + for(len = 0; *str != '\0'; len++, str++); + return len; +} + +void +UnicodeMakeUpperCase(wchar *dst, const wchar *src) //idk what to do with it, seems to be incorrect implementation by R* +{ + while (*src != '\0') { + if (*src < 'a' || *src > 'z') + *dst = *src; + else + *dst = *src - 32; + dst++; + src++; + } + *dst = '\0'; +} + +CFontDetails CFont::Details; +bool16 CFont::NewLine; +CSprite2d CFont::Sprite[MAX_FONTS]; +CFontRenderState CFont::RenderState; + +#ifdef MORE_LANGUAGES +uint8 CFont::LanguageSet = FONT_LANGSET_EFIGS; +int32 CFont::Slot = -1; +#define JAP_TERMINATION (0x8000 | '~') + +int16 CFont::Size[LANGSET_MAX][MAX_FONTS][210] = { + { +#else +int16 CFont::Size[MAX_FONTS][210] = { +#endif + { + //FONT2 EFIGS + //SPC,!, $, %, &, ', [, ], +, , -, ., + 12, 9, 22, 17, 19, 19, 25, 4, 33, 33, 25, 35, 11, 10, 6, 33, + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ??, + 18, 10, 17, 17, 17, 17, 17, 15, 12, 16, 5, 30, 30, 30, 30, 30, + // A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, + 12, 16, 19, 16, 19, 18, 18, 17, 22, 11, 17, 18, 18, 30, 22, 19, + //P, Q, R, S, T, U, V, W, X, Y, Z, ??, ??, ??, �, \, + #ifdef FIX_BUGS + 22, 19, 19, 20, 18, 19, 19, 29, 19, 18, 19, 19, 33, 33, 10, 19, + #else + 22, 19, 19, 20, 18, 19, 19, 29, 19, 18, 19, 19, 33, 33, 19, 19, + #endif + //??,a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, + 12, 14, 11, 11, 16, 11, 12, 14, 14, 10, 13, 12, 10, 19, 18, 12, + //p, q, r, s, t, u, v, w, x, y, z, ??, ??, ??, ??, ??, + 16, 13, 13, 11, 12, 15, 12, 15, 13, 12, 12, 37, 33, 37, 35, 37, + //�, �, �, �, �, �, �, �, �, �, �, �, �, �, �, �, + 16, 16, 16, 16, 33, 17, 18, 18, 18, 18, 11, 11, 11, 11, 19, 19, + //�, �, �, �, �, �, �, �, �, �, �, �, �, �, �, �, + 19, 19, 19, 19, 19, 19, 15, 14, 14, 14, 14, 20, 14, 11, 11, 11, + //�, �, �, �, �, �, �, �, �, �, �, �, �, �, �, �, + #ifdef FIX_BUGS + 11, 10, 10, 10, 10, 12, 12, 12, 12, 15, 15, 15, 15, 22, 18, 21, + #else + 11, 10, 10, 10, 10, 12, 12, 12, 12, 15, 15, 15, 15, 24, 18, 21, + #endif + //i,BLANKS + 10, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + //space, unprop + 19, 16 + }, + { + //FONT1 EFIGS + //Characters with a '2' refer to the Pricedown font. + //Characters that are referred as '*I' are characters that contain icons for PS2/XBOX, but contain regular characters on PC + //in order to display them properly in the Keyboard controls menu. + //!2,!, *I,(R), $, %, &, ', [, ], *I, +, , -, ., *I, + 15, 7, 31, 25, 20, 23, 21, 7, 11, 10, 26, 14, 6, 12, 6, 26, + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, *I, *I, *I, *I, ?, + 20, 7, 20, 20, 21, 20, 20, 19, 21, 20, 8, 30, 24, 30, 24, 19, + //TM,A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, + 20, 22, 22, 21, 22, 18, 18, 22, 22, 9, 14, 21, 18, 27, 21, 24, + //P, Q, R, S, T, U, V, W, X, Y, Z, *I, \, *I, �, �, + #ifdef FIX_BUGS + 22, 22, 23, 20, 19, 23, 22, 31, 23, 23, 21, 25, 13, 30, 7, 19, + #else + 22, 22, 23, 20, 19, 23, 22, 31, 23, 23, 21, 25, 13, 30, 10, 19, + #endif + //(C),a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, + 10, 17, 17, 16, 17, 17, 11, 17, 17, 7, 7, 18, 7, 25, 17, 17, + //p, q, r, s, t, u, v, w, x, y, z, *I, *I, $2, (2, )2, + 17, 17, 11, 17, 11, 17, 18, 25, 19, 18, 17, 28, 26, 20, 15, 15, + //�, �, �, �, �, �, �, �, �, �, �, �, �, �, �, �, + 20, 20, 20, 20, 29, 22, 19, 19, 19, 19, 9, 9, 9, 9, 23, 23, + //�, �, �, �, �, �, �, �, �, �, �, �, �, �, �, �, + 23, 23, 24, 24, 24, 24, 20, 19, 17, 17, 17, 30, 16, 17, 17, 17, + //�, �, �, �, �, �, �, �, �, �, �, �, �, �, �, �, + #ifdef FIX_BUGS + 17, 11, 11, 15, 12, 17, 17, 17, 17, 17, 17, 17, 17, 21, 17, 19, + #else + 17, 11, 11, 15, 12, 17, 17, 17, 17, 17, 17, 17, 17, 19, 20, 20, + #endif + //02,12,22, 32, 42, 52, 62, 72, 82, 92, :2, A2, B2, C2, D2, E2, + 20, 18, 19, 19, 21, 19, 19, 19, 19, 19, 16, 19, 19, 19, 20, 19, + //F2,G2,H2, I2, J2, K2, L2, M2, N2, O2, P2, Q2, R2, S2, T2, U2, + 16, 19, 19, 9, 19, 20, 14, 29, 19, 19, 19, 19, 19, 19, 21, 19, + //V2,W2,X2, Y2, Z2, �2, �2, �2, �2, �2, �2, �2, �2, �2, �2, �2, + 20, 32, 20, 19, 19, 19, 19, 19, 19, 29, 19, 19, 19, 19, 19, 9, + //�2,�2,�2, �2, �2, �2, �2, �2, �2, �2, �2, �2, �2, �2, '2, .2, + #ifdef FIX_BUGS + 9, 9, 9, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 9, + #else + 9, 9, 9, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 21, 10, 9, + #endif + //space, unprop + 10, 20 + } + +#ifdef MORE_LANGUAGES + }, + { + { + 5, 9, 9, 0, 17, 17, 23, 3, 21, 18, 0, 8, 3, 8, 3, 0, + 16, 9, 16, 16, 15, 19, 15, 14, 17, 17, 4, 4, 0, 0, 0, 17, + 19, 17, 19, 15, 21, 18, 19, 16, 21, 13, 15, 21, 20, 28, 21, 18, + 22, 17, 21, 20, 18, 18, 20, 26, 22, 18, 18, 0, 8, 0, 9, 8, + 0, 14, 11, 12, 16, 11, 13, 13, 15, 10, 14, 15, 11, 21, 17, 10, + 20, 15, 12, 12, 16, 17, 13, 16, 13, 21, 11, 0, 0, 0, 0, 0, + 20, 19, 19, 22, 27, 15, 18, 18, 20, 26, 21, 23, 17, 22, 21, 17, + 26, 25, 26, 17, 20, 26, 17, 16, 11, 12, 13, 21, 11, 17, 17, 12, + 21, 17, 17, 15, 24, 16, 10, 20, 23, 16, 7, 9, 16, 23, 12, 11, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, + 19, 16 + }, + { + 11, 5, 10, 15, 19, 22, 20, 5, 9, 8, 11, 12, 5, 12, 6, 12, + 19, 5, 18, 19, 20, 18, 19, 18, 20, 19, 5, 6, 26, 12, 30, 19, + 23, 21, 20, 20, 20, 16, 16, 21, 19, 5, 13, 19, 16, 24, 20, 21, + 20, 21, 20, 19, 17, 20, 21, 30, 22, 21, 20, 25, 13, 30, 5, 9, + 10, 15, 15, 14, 15, 16, 10, 15, 15, 5, 5, 15, 5, 23, 15, 16, + 15, 15, 9, 16, 10, 15, 17, 24, 18, 15, 15, 27, 5, 19, 2, 2, + 20, 20, 16, 23, 30, 19, 20, 20, 21, 24, 19, 19, 20, 23, 22, 19, + 27, 29, 25, 20, 20, 28, 24, 16, 16, 14, 19, 25, 16, 16, 16, 17, + 19, 16, 16, 17, 25, 19, 15, 23, 26, 21, 16, 14, 22, 20, 16, 19, + 15, 14, 15, 16, 17, 15, 15, 15, 15, 15, 7, 15, 15, 15, 15, 15, + 13, 15, 15, 7, 15, 16, 13, 23, 15, 15, 15, 15, 15, 15, 17, 15, + 16, 24, 17, 17, 17, 15, 15, 13, 20, 23, 15, 17, 17, 16, 24, 15, + 15, 15, 23, 18, 15, 23, 26, 23, 16, 15, 23, 15, 15, 19, 2, 2, + 10, 20 + }, + }, + { + { + //FONT2 EFIGS + //SPC,!, $, %, &, ', [, ], +, , -, ., + 12, 9, 22, 17, 19, 19, 25, 4, 33, 33, 25, 35, 11, 10, 6, 33, + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ??, + 18, 10, 17, 17, 17, 17, 17, 15, 12, 16, 5, 30, 30, 30, 30, 30, + // A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, + 12, 16, 19, 16, 19, 18, 18, 17, 22, 11, 17, 18, 18, 30, 22, 19, + //P, Q, R, S, T, U, V, W, X, Y, Z, ??, ??, ??, �, \, + 22, 19, 19, 20, 18, 19, 19, 29, 19, 18, 19, 19, 33, 33, 10, 19, + //??,a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, + 12, 14, 11, 11, 16, 11, 12, 14, 14, 10, 13, 12, 10, 19, 18, 12, + //p, q, r, s, t, u, v, w, x, y, z, ??, ??, ??, ??, ??, + 16, 13, 13, 11, 12, 15, 12, 15, 13, 12, 12, 37, 33, 37, 35, 37, + //�, �, �, �, �, �, �, �, �, �, �, �, �, �, �, �, + 16, 16, 16, 16, 33, 17, 18, 18, 18, 18, 11, 11, 11, 11, 19, 19, + //�, �, �, �, �, �, �, �, �, �, �, �, �, �, �, �, + 19, 19, 19, 19, 19, 19, 15, 14, 14, 14, 14, 20, 14, 11, 11, 11, + //�, �, �, �, �, �, �, �, �, �, �, �, �, �, �, �, + 11, 10, 10, 10, 10, 12, 12, 12, 12, 15, 15, 15, 15, 22, 18, 21, + //i,BLANKS + 10, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + //space, unprop + 19, 16 + }, + { + //FONT1 EFIGS + //Characters with a '2' refer to the Pricedown font. + //Characters that are referred as '*I' are characters that contain icons for PS2/XBOX, but contain regular characters on PC + //in order to display them properly in the Keyboard controls menu. + //!2,!, *I,(R), $, %, &, ', [, ], *I, +, , -, ., *I, + 15, 7, 31, 25, 20, 23, 21, 7, 11, 10, 26, 14, 6, 12, 6, 26, + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, *I, *I, *I, *I, ?, + 20, 7, 20, 20, 21, 20, 20, 19, 21, 20, 8, 30, 24, 30, 24, 19, + //TM,A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, + 20, 22, 22, 21, 22, 18, 18, 22, 22, 9, 14, 21, 18, 27, 21, 24, + //P, Q, R, S, T, U, V, W, X, Y, Z, *I, \, *I, �, �, + 22, 22, 23, 20, 19, 23, 22, 31, 23, 23, 21, 25, 13, 30, 7, 19, + //(C),a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, + 10, 17, 17, 16, 17, 17, 11, 17, 17, 7, 7, 18, 7, 25, 17, 17, + //p, q, r, s, t, u, v, w, x, y, z, *I, *I, $2, (2, )2, + 17, 17, 11, 17, 11, 17, 18, 25, 19, 18, 17, 28, 26, 20, 15, 15, + //�, �, �, �, �, �, �, �, �, �, �, �, �, �, �, �, + 20, 20, 20, 20, 29, 22, 19, 19, 19, 19, 9, 9, 9, 9, 23, 23, + //�, �, �, �, �, �, �, �, �, �, �, �, �, �, �, �, + 23, 23, 24, 24, 24, 24, 20, 19, 17, 17, 17, 30, 16, 17, 17, 17, + //�, �, �, �, �, �, �, �, �, �, �, �, �, �, �, �, + 17, 11, 11, 15, 12, 17, 17, 17, 17, 17, 17, 17, 17, 21, 17, 19, + //02,12,22, 32, 42, 52, 62, 72, 82, 92, :2, A2, B2, C2, D2, E2, + 20, 18, 19, 19, 21, 19, 19, 19, 19, 19, 16, 19, 19, 19, 20, 19, + //F2,G2,H2, I2, J2, K2, L2, M2, N2, O2, P2, Q2, R2, S2, T2, U2, + 16, 19, 19, 9, 19, 20, 14, 29, 19, 19, 19, 19, 19, 19, 21, 19, + //V2,W2,X2, Y2, Z2, �2, �2, �2, �2, �2, �2, �2, �2, �2, �2, �2, + 20, 32, 20, 19, 19, 19, 19, 19, 19, 29, 19, 19, 19, 19, 19, 9, + //�2,�2,�2, �2, �2, �2, �2, �2, �2, �2, �2, �2, �2, �2, '2, .2, + 9, 9, 9, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 9, + //space, unprop + 10, 20 + } + } +#endif +}; + +#ifdef MORE_LANGUAGES +int16 Size_jp[] = { + 15, 14, 16, 20, 19, 26, 22, 11, 18, 18, 27, 26, 13, //; 0 + 19, 20, 27, 19, 15, 19, 19, 21, 19, 20, 18, 19, 15, //; 13 + 13, 28, 15, 32, 15, 35, 15, 19, 19, 19, 19, 17, 16, //; 26 + 19, 20, 15, 19, 20, 14, 17, 19, 19, 19, 19, 19, 19, //; 39 + 19, 19, 20, 25, 20, 19, 19, 33, 31, 39, 37, 39, 37, //; 52 + 21, 21, 21, 19, 17, 15, 23, 21, 15, 19, 20, 16, 19, //; 65 + 19, 19, 20, 20, 17, 22, 19, 22, 22, 19, 22, 22, 23, //; 78 + 35, 35, 35, 35, 37, 19, 19, 19, 19, 29, 19, 19, 19, //; 91 + 19, 19, 9, 9, 9, 9, 19, 19, 19, 19, 19, 19, 19, 19, //; 104 + 19, 19, 19, 19, 19, 30, 19, 19, 19, 19, 19, 10, 10, //; 118 + 10, 10, 19, 19, 19, 19, 19, 19, 19, 19, 19, 23, 35, //; 131 + 12, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, //; 144 + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, //; 157 + 19, 19, 19, 11, 19, 19, 19, 19, 19, 19, 19, 19, 19, //; 170 + 19, 19, 19, 19, 19, 19, 19, 19, 19, 21 +}; +#endif + +wchar foreign_table[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 177, 0, 0, 0, 0, 0, 95, 0, 0, 0, 0, 175, + 128, 129, 130, 0, 131, 0, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 0, 173, 142, 143, 144, 0, 145, 0, 0, 146, 147, 148, 149, 0, 0, 150, + 151, 152, 153, 0, 154, 0, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 0, 174, 165, 166, 167, 0, 168, 0, 0, 169, 170, 171, 172, 0, 0, 0, +}; + +union tFontRenderStatePointer +{ + CFontRenderState *pRenderState; + wchar *pStr; + + void Align() + { + if ((uintptr)pStr % 4) + pStr++; + } +}; + +tFontRenderStatePointer FontRenderStatePointer; +uint8 FontRenderStateBuf[1024]; + +#ifdef BUTTON_ICONS +CSprite2d CFont::ButtonSprite[MAX_BUTTON_ICONS]; +int CFont::PS2Symbol = BUTTON_NONE; +int32 CFont::ButtonsSlot = -1; +#endif // BUTTON_ICONS + +void +CFont::Initialise(void) +{ + int slot; + + slot = CTxdStore::AddTxdSlot("fonts"); +#ifdef MORE_LANGUAGES + Slot = slot; + switch (LanguageSet) + { + case FONT_LANGSET_EFIGS: + default: + CTxdStore::LoadTxd(slot, "MODELS/FONTS.TXD"); + break; + case FONT_LANGSET_POLISH: + CTxdStore::LoadTxd(slot, "MODELS/FONTS_P.TXD"); + break; + case FONT_LANGSET_RUSSIAN: + CTxdStore::LoadTxd(slot, "MODELS/FONTS_R.TXD"); + break; + case FONT_LANGSET_JAPANESE: + CTxdStore::LoadTxd(slot, "MODELS/FONTS_J.TXD"); + break; + } +#else + CTxdStore::LoadTxd(slot, "MODELS/FONTS.TXD"); +#endif + CTxdStore::AddRef(slot); + CTxdStore::PushCurrentTxd(); + CTxdStore::SetCurrentTxd(slot); + Sprite[0].SetTexture("font2", "font2m"); +#ifdef MORE_LANGUAGES + if (IsJapanese()) { + Sprite[1].SetTexture("FONTJAP", "FONTJAP_mask"); + Sprite[3].SetTexture("FONTJAP", "FONTJAP_mask"); + } +#endif // MORE_LANGUAGES + Sprite[1].SetTexture("font1", "font1m"); + SetScale(1.0f, 1.0f); + SetSlantRefPoint(SCREEN_WIDTH, 0.0f); + SetSlant(0.0f); + SetColor(CRGBA(255, 255, 255, 0)); + SetJustifyOff(); + SetCentreOff(); + SetWrapx(SCREEN_WIDTH); + SetCentreSize(SCREEN_WIDTH); + SetBackgroundOff(); + SetBackgroundColor(CRGBA(128, 128, 128, 128)); + SetBackGroundOnlyTextOff(); + SetPropOn(); + SetFontStyle(FONT_BANK); + SetRightJustifyWrap(0.0f); + SetAlphaFade(255.0f); + SetDropShadowPosition(0); + CTxdStore::PopCurrentTxd(); + +#if !defined(GAMEPAD_MENU) && defined(BUTTON_ICONS) + // loaded in CMenuManager with GAMEPAD_MENU defined + LoadButtons("MODELS/X360BTNS.TXD"); +#endif +} + +#ifdef BUTTON_ICONS +void +CFont::LoadButtons(const char *txdPath) +{ + if (int file = CFileMgr::OpenFile(txdPath)) { + CFileMgr::CloseFile(file); + if (ButtonsSlot == -1) + ButtonsSlot = CTxdStore::AddTxdSlot("buttons"); + else { + for (int i = 0; i < MAX_BUTTON_ICONS; i++) + ButtonSprite[i].Delete(); + CTxdStore::RemoveTxd(ButtonsSlot); + } + CTxdStore::LoadTxd(ButtonsSlot, txdPath); + CTxdStore::AddRef(ButtonsSlot); + CTxdStore::PushCurrentTxd(); + CTxdStore::SetCurrentTxd(ButtonsSlot); + ButtonSprite[BUTTON_UP].SetTexture("thumblyu"); + ButtonSprite[BUTTON_DOWN].SetTexture("thumblyd"); + ButtonSprite[BUTTON_LEFT].SetTexture("thumblxl"); + ButtonSprite[BUTTON_RIGHT].SetTexture("thumblxr"); + ButtonSprite[BUTTON_CROSS].SetTexture("cross"); + ButtonSprite[BUTTON_CIRCLE].SetTexture("circle"); + ButtonSprite[BUTTON_SQUARE].SetTexture("square"); + ButtonSprite[BUTTON_TRIANGLE].SetTexture("triangle"); + ButtonSprite[BUTTON_L1].SetTexture("l1"); + ButtonSprite[BUTTON_L2].SetTexture("l2"); + ButtonSprite[BUTTON_L3].SetTexture("l3"); + ButtonSprite[BUTTON_R1].SetTexture("r1"); + ButtonSprite[BUTTON_R2].SetTexture("r2"); + ButtonSprite[BUTTON_R3].SetTexture("r3"); + ButtonSprite[BUTTON_RSTICK_UP].SetTexture("thumbryu"); + ButtonSprite[BUTTON_RSTICK_DOWN].SetTexture("thumbryd"); + ButtonSprite[BUTTON_RSTICK_LEFT].SetTexture("thumbrxl"); + ButtonSprite[BUTTON_RSTICK_RIGHT].SetTexture("thumbrxr"); + CTxdStore::PopCurrentTxd(); + } + else { + if (ButtonsSlot != -1) { + for (int i = 0; i < MAX_BUTTON_ICONS; i++) + ButtonSprite[i].Delete(); + CTxdStore::RemoveTxdSlot(ButtonsSlot); + ButtonsSlot = -1; + } + } +} +#endif // BUTTON_ICONS + +#ifdef MORE_LANGUAGES +void +CFont::ReloadFonts(uint8 set) +{ + if (Slot != -1 && LanguageSet != set) { + Sprite[0].Delete(); + Sprite[1].Delete(); + if (IsJapanese()) + Sprite[2].Delete(); + CTxdStore::PushCurrentTxd(); + CTxdStore::RemoveTxd(Slot); + switch (set) + { + case FONT_LANGSET_EFIGS: + default: + CTxdStore::LoadTxd(Slot, "MODELS/FONTS.TXD"); + break; + case FONT_LANGSET_POLISH: + CTxdStore::LoadTxd(Slot, "MODELS/FONTS_P.TXD"); + break; + case FONT_LANGSET_RUSSIAN: + CTxdStore::LoadTxd(Slot, "MODELS/FONTS_R.TXD"); + break; + case FONT_LANGSET_JAPANESE: + CTxdStore::LoadTxd(Slot, "MODELS/FONTS_J.TXD"); + break; + } + CTxdStore::SetCurrentTxd(Slot); + Sprite[0].SetTexture("font2", "font2_mask"); + if (set == FONT_LANGSET_JAPANESE) { + Sprite[2].SetTexture("FONTJAP", "FONTJAP_mask"); + } + Sprite[1].SetTexture("font1", "font1_mask"); + CTxdStore::PopCurrentTxd(); + } + LanguageSet = set; +} +#endif + +void +CFont::Shutdown(void) +{ +#ifdef BUTTON_ICONS + if (ButtonsSlot != -1) { + for (int i = 0; i < MAX_BUTTON_ICONS; i++) + ButtonSprite[i].Delete(); + CTxdStore::RemoveTxdSlot(ButtonsSlot); + ButtonsSlot = -1; + } +#endif + Sprite[0].Delete(); + Sprite[1].Delete(); +#ifdef MORE_LANGUAGES + if (IsJapanese()) + Sprite[3].Delete(); + CTxdStore::RemoveTxdSlot(Slot); + Slot = -1; +#else + CTxdStore::RemoveTxdSlot(CTxdStore::FindTxdSlot("fonts")); +#endif +} + +void +CFont::InitPerFrame(void) +{ + RenderState.style = -1; + Details.anonymous_25 = 0; + FontRenderStatePointer.pRenderState = (CFontRenderState*)FontRenderStateBuf; + SetDropShadowPosition(0); + NewLine = false; +#ifdef BUTTON_ICONS + PS2Symbol = BUTTON_NONE; +#endif +} + +#ifdef BUTTON_ICONS +void +CFont::DrawButton(float x, float y) +{ + if (x <= 0.0f || x > SCREEN_WIDTH || y <= 0.0f || y > SCREEN_HEIGHT) + return; + + if (PS2Symbol != BUTTON_NONE) { + CRect rect; + rect.left = x; + rect.top = RenderState.scaleY + RenderState.scaleY + y; + rect.right = RenderState.scaleY * 17.0f + x; + rect.bottom = RenderState.scaleY * 19.0f + y; + + int vertexAlphaState; + void *raster; + RwRenderStateGet(rwRENDERSTATEVERTEXALPHAENABLE, &vertexAlphaState); + RwRenderStateGet(rwRENDERSTATETEXTURERASTER, &raster); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); + if (RenderState.bIsShadow) + ButtonSprite[PS2Symbol].Draw(rect, RenderState.color); + else + ButtonSprite[PS2Symbol].Draw(rect, CRGBA(255, 255, 255, RenderState.color.a)); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, raster); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)vertexAlphaState); + } +} +#endif + +void +CFont::PrintChar(float x, float y, wchar c) +{ + bool bDontPrint = false; + if(x <= 0.0f || x > SCREEN_WIDTH || + y <= 0.0f || y > SCREEN_HEIGHT) // BUG: game uses SCREENW again + return; + + bDontPrint = c == '\0'; + float w = GetCharacterWidth(c) / 32.0f; + if (Details.bFontHalfTexture && c == 208) + c = '\0'; + float xoff = c % 16; + float yoff = c / 16; +#ifdef MORE_LANGUAGES + if (IsJapaneseFont()) { + w = 21.0f; + xoff = (float)(c % 48); + yoff = c / 48; + } +#endif + + if(RenderState.style == FONT_BANK || RenderState.style == FONT_STANDARD){ + if (bDontPrint) return; + if (RenderState.slant == 0.0f) { +#ifdef FIX_BUGS + if (c < 192) { +#else + if (c < 193) { +#endif + CSprite2d::AddToBuffer( + CRect(x, y, + x + 32.0f * RenderState.scaleX * 1.0f, + y + 40.0f * RenderState.scaleY * 0.5f), + RenderState.color, + xoff / 16.0f, yoff / 12.8f + 0.0021f, + (xoff + 1.0f) / 16.0f - 0.001f, yoff / 12.8f + 0.0021f, + xoff / 16.0f, (yoff + 1.0f) / 12.8f - 0.0021f, + (xoff + 1.0f) / 16.0f - 0.001f, (yoff + 1.0f) / 12.8f - 0.0021f); + } else { + CSprite2d::AddToBuffer( + CRect(x, y, + x + 32.0f * RenderState.scaleX * 1.0f, + y + 33.0f * RenderState.scaleY * 0.5f), + RenderState.color, + xoff / 16.0f, yoff / 12.8f + 0.0021f, + (xoff + 1.0f) / 16.0f - 0.001f, yoff / 12.8f + 0.0021f, + xoff / 16.0f, (yoff + 1.0f) / 12.8f - 0.017f, + (xoff + 1.0f) / 16.0f - 0.001f, (yoff + 1.0f) / 12.8f - 0.017f); + } + } else + CSprite2d::AddToBuffer( + CRect(x, y, + x + 32.0f * RenderState.scaleX * 1.0f, + y + 40.0f * RenderState.scaleY * 0.5f), + RenderState.color, + xoff / 16.0f, yoff / 12.8f + 0.00055f, + (xoff + 1.0f) / 16.0f - 0.001f, yoff / 12.8f + 0.0021f + 0.01f, + xoff / 16.0f, (yoff + 1.0f) / 12.8f - 0.009f, + (xoff + 1.0f) / 16.0f - 0.001f, (yoff + 1.0f) / 12.8f - 0.0021f + 0.01f); +#ifdef MORE_LANGUAGES + /*}else if (IsJapaneseFont()) { + if (Details.dropShadowPosition != 0) { + CSprite2d::AddSpriteToBank(Details.bank + Details.style, // BUG: game doesn't add bank + CRect(x + SCREEN_SCALE_X(Details.dropShadowPosition), + y + SCREEN_SCALE_Y(Details.dropShadowPosition), + x + SCREEN_SCALE_X(Details.dropShadowPosition) + 32.0f * Details.scaleX * 1.0f, + y + SCREEN_SCALE_Y(Details.dropShadowPosition) + 40.0f * Details.scaleY / 2.75f), + Details.dropColor, + xoff * w / 1024.0f, yoff / 25.6f, + xoff * w / 1024.0f + (1.0f / 48.0f) - 0.001f, yoff / 25.6f, + xoff * w / 1024.0f, (yoff + 1.0f) / 25.6f, + xoff * w / 1024.0f + (1.0f / 48.0f) - 0.001f, (yoff + 1.0f) / 25.6f - 0.0001f); + } + CSprite2d::AddSpriteToBank(Details.bank + Details.style, // BUG: game doesn't add bank + CRect(x, y, + x + 32.0f * Details.scaleX * 1.0f, + y + 40.0f * Details.scaleY / 2.75f), + Details.color, + xoff * w / 1024.0f, yoff / 25.6f, + xoff * w / 1024.0f + (1.0f / 48.0f) - 0.001f, yoff / 25.6f, + xoff * w / 1024.0f, (yoff + 1.0f) / 25.6f - 0.002f, + xoff * w / 1024.0f + (1.0f / 48.0f) - 0.001f, (yoff + 1.0f) / 25.6f - 0.0001f);*/ +#endif + } else { + if (bDontPrint) return; + CSprite2d::AddToBuffer( + CRect(x, y, + x + 32.0f * RenderState.scaleX * w, + y + 32.0f * RenderState.scaleY * 0.5f), + RenderState.color, + xoff / 16.0f, yoff / 16.0f, + (xoff + w) / 16.0f, yoff / 16.0f, + xoff / 16.0f, (yoff + 1.0f) / 16.0f, + (xoff + w) / 16.0f - 0.0001f, (yoff + 1.0f) / 16.0f - 0.0001f); + } +} + +#ifdef MORE_LANGUAGES +bool CFont::IsJapanesePunctuation(wchar *str) +{ + return (*str == 0xE7 || *str == 0x124 || *str == 0x126 || *str == 0x128 || *str == 0x104 || *str == ',' || *str == '>' || *str == '!' || *str == 0x99 || *str == '?' || *str == ':'); +} + +bool CFont::IsAnsiCharacter(wchar *s) +{ + if (*s >= 'A' && *s <= 'Z') + return true; + if (*s >= 'a' && *s <= 'z') + return true; + if (*s >= '0' && *s <= ':') + return true; + if (*s == '(' || *s == ')') + return true; + if (*s == 'D' || *s == '$') + return true; + return false; +} +#endif + +void +CFont::RenderFontBuffer() +{ + if (FontRenderStatePointer.pRenderState == (CFontRenderState*)FontRenderStateBuf) return; + + float textPosX; + float textPosY; + CRGBA color; + bool bBold = false; + bool bFlash = false; + + Sprite[RenderState.style].SetRenderState(); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RenderState = *(CFontRenderState*)&FontRenderStateBuf[0]; + textPosX = RenderState.fTextPosX; + textPosY = RenderState.fTextPosY; + color = RenderState.color; + tFontRenderStatePointer pRenderStateBufPointer; + pRenderStateBufPointer.pRenderState = (CFontRenderState*)&FontRenderStateBuf[0]; + for (++pRenderStateBufPointer.pRenderState; pRenderStateBufPointer.pStr < FontRenderStatePointer.pStr; pRenderStateBufPointer.pStr++) { + if (*pRenderStateBufPointer.pStr == '\0') { + tFontRenderStatePointer tmpPointer = pRenderStateBufPointer; + tmpPointer.pStr++; + tmpPointer.Align(); + if (tmpPointer.pStr >= FontRenderStatePointer.pStr) + break; + + RenderState = *(tmpPointer.pRenderState++); + + pRenderStateBufPointer = tmpPointer; + + textPosX = RenderState.fTextPosX; + textPosY = RenderState.fTextPosY; + color = RenderState.color; + } + if (*pRenderStateBufPointer.pStr == '~') { +#ifdef BUTTON_ICONS + PS2Symbol = BUTTON_NONE; +#endif + pRenderStateBufPointer.pStr = ParseToken(pRenderStateBufPointer.pStr, color, bFlash, bBold); +#ifdef BUTTON_ICONS + if(PS2Symbol != BUTTON_NONE) { + DrawButton(textPosX, textPosY); + textPosX += RenderState.scaleY * 17.0f; + PS2Symbol = BUTTON_NONE; + } +#endif + if (bFlash) { + if (CTimer::GetTimeInMilliseconds() - Details.nFlashTimer > 300) { + Details.bFlashState = !Details.bFlashState; + Details.nFlashTimer = CTimer::GetTimeInMilliseconds(); + } + Details.color.alpha = Details.bFlashState ? 0 : 255; + } + if (!RenderState.bIsShadow) + RenderState.color = color; + } + wchar c = *pRenderStateBufPointer.pStr; + c -= ' '; + if (RenderState.bFontHalfTexture) + c = FindNewCharacter(c); + else if (c > 155) + c = '\0'; + + if (RenderState.slant != 0.0f) + textPosY = (RenderState.slantRefX - textPosX) * RenderState.slant + RenderState.slantRefY; + PrintChar(textPosX, textPosY, c); + if (bBold) { + PrintChar(textPosX + 1.0f, textPosY, c); + PrintChar(textPosX + 2.0f, textPosY, c); + textPosX += 2.0f; + } +#ifdef FIX_BUGS + // PS2 uses different chars for some symbols + if (!RenderState.bFontHalfTexture && c == 30) c = 61; // wanted star +#endif + textPosX += RenderState.scaleX * GetCharacterWidth(c); + if (c == '\0') + textPosX += RenderState.fExtraSpace; + } + CSprite2d::RenderVertexBuffer(); + FontRenderStatePointer.pRenderState = (CFontRenderState*)FontRenderStateBuf; +} + +#if 0 //def MORE_LANGUAGES +bool +CFont::PrintString(float x, float y, wchar *start, wchar *&end, float spwidth, float japX) +{ + wchar *s, c, unused; + + if (IsJapanese()) { + float jx = 0.0f; + for (s = start; s < end; s++) { + if (*s == JAP_TERMINATION || *s == '~') + s = ParseToken(s, &unused, true); + if (NewLine) { + NewLine = false; + break; + } + jx += GetCharacterSize(*s - ' '); + } + s = start; + if (Details.centre) + x = japX - jx / 2.0f; + else if (Details.rightJustify) + x = japX - jx; + } + + for (s = start; s < end; s++) { + if (*s == '~' || (IsJapanese() && *s == JAP_TERMINATION)) + s = ParseToken(s, &unused); + if (NewLine && IsJapanese()) { + NewLine = false; + end = s; + return true; + } + c = *s - ' '; + if (Details.slant != 0.0f && !IsJapanese()) + y = (Details.slantRefX - x) * Details.slant + Details.slantRefY; + + PrintChar(x, y, c); + x += GetCharacterSize(c); + if (c == 0 && (!NewLine || !IsJapanese())) // space + x += spwidth; + } + return false; +} +#else +void +CFont::PrintString(float x, float y, uint32, wchar *start, wchar *end, float spwidth) +{ + wchar *s; + + if (RenderState.style != Details.style) { + RenderFontBuffer(); + RenderState.style = Details.style; + } + + float dropShadowPosition = Details.dropShadowPosition; + if (dropShadowPosition != 0.0f && (Details.style == FONT_BANK || Details.style == FONT_STANDARD)) { + CRGBA color = Details.color; + Details.color = Details.dropColor; + Details.dropShadowPosition = 0; + Details.bIsShadow = true; + if (Details.slant != 0.0f) { + Details.slantRefX += SCREEN_SCALE_X(dropShadowPosition); + Details.slantRefY += SCREEN_SCALE_Y(dropShadowPosition); + PrintString(SCREEN_SCALE_X(dropShadowPosition) + x, SCREEN_SCALE_Y(dropShadowPosition) + y, Details.anonymous_25, start, end, spwidth); + Details.slantRefX -= SCREEN_SCALE_X(dropShadowPosition); + Details.slantRefY -= SCREEN_SCALE_Y(dropShadowPosition); + } else { + PrintString(SCREEN_SCALE_X(dropShadowPosition) + x, SCREEN_SCALE_Y(dropShadowPosition) + y, Details.anonymous_25, start, end, spwidth); + } + Details.color = color; + Details.dropShadowPosition = dropShadowPosition; + Details.bIsShadow = false; + } + if (FontRenderStatePointer.pStr >= (wchar*)&FontRenderStateBuf[ARRAY_SIZE(FontRenderStateBuf)] - (end - start + 26)) // why 26? + RenderFontBuffer(); + CFontRenderState *pRenderState = FontRenderStatePointer.pRenderState; + pRenderState->fTextPosX = x; + pRenderState->fTextPosY = y; + pRenderState->scaleX = Details.scaleX; + pRenderState->scaleY = Details.scaleY; + pRenderState->color = Details.color; + pRenderState->fExtraSpace = spwidth; + pRenderState->slant = Details.slant; + pRenderState->slantRefX = Details.slantRefX; + pRenderState->slantRefY = Details.slantRefY; + pRenderState->bFontHalfTexture = Details.bFontHalfTexture; + pRenderState->proportional = Details.proportional; + pRenderState->style = Details.style; + pRenderState->bIsShadow = Details.bIsShadow; + FontRenderStatePointer.pRenderState++; + + for(s = start; s < end;){ + if (*s == '~') { + for (wchar *i = ParseToken(s); s != i; FontRenderStatePointer.pStr++) { + *FontRenderStatePointer.pStr = *(s++); + } + if (Details.bFlash) { + if (CTimer::GetTimeInMilliseconds() - Details.nFlashTimer > 300) { + Details.bFlashState = !Details.bFlashState; + Details.nFlashTimer = CTimer::GetTimeInMilliseconds(); + } + Details.color.a = Details.bFlashState ? 0 : 255; + } + } else + *(FontRenderStatePointer.pStr++) = *(s++); + } + *(FontRenderStatePointer.pStr++) = '\0'; + FontRenderStatePointer.Align(); +} +#endif + +void +CFont::PrintStringFromBottom(float x, float y, wchar *str) +{ + y -= (32.0f * Details.scaleY / 2.0f + 2.0f * Details.scaleY) * GetNumberLines(x, y, str); + if (Details.slant != 0.0f) + y -= ((Details.slantRefX - x) * Details.slant + Details.slantRefY); + PrintString(x, y, str); +} + +void +CFont::PrintString(float xstart, float ystart, wchar *s) +{ + CRect rect; + int numSpaces; + float lineLength; + float x, y; + bool first; + wchar *start, *t; + + Details.bFlash = false; + + if(*s == '*') + return; + + Details.anonymous_25++; + if(Details.background){ + RenderState.color = Details.color; + GetNumberLines(xstart, ystart, s); // BUG: result not used + GetTextRect(&rect, xstart, ystart, s); + CSprite2d::DrawRect(rect, Details.backgroundColor); + } + + lineLength = 0.0f; + numSpaces = 0; + first = true; + if(Details.centre || Details.rightJustify) + x = 0.0f; + else + x = xstart; + y = ystart; + start = s; + + // This is super ugly, I blame R* + for(;;){ + for(;;){ + for(;;){ + if(*s == '\0') + return; + float xend = Details.centre ? Details.centreSize : + Details.rightJustify ? xstart - Details.rightJustifyWrap : + Details.wrapX; +#ifdef MORE_LANGUAGES + if (IsJapaneseFont()) + xend -= SCREEN_SCALE_X(21.0f * 2.0f); +#endif + if(x + GetStringWidth(s) > xend && !first){ +#ifdef MORE_LANGUAGES + if (IsJapanese() && IsJapanesePunctuation(s)) + s--; +#endif + // flush line + float spaceWidth = !Details.justify || Details.centre ? 0.0f : + (Details.wrapX - lineLength) / numSpaces; + float xleft = Details.centre ? xstart - x/2 : + Details.rightJustify ? xstart - x : + xstart; +#if 0//def MORE_LANGUAGES + PrintString(xleft, y, start, s, spaceWidth, xstart); +#else + PrintString(xleft, y, Details.anonymous_25, start, s, spaceWidth); +#endif + // reset things + lineLength = 0.0f; + numSpaces = 0; + first = true; + if(Details.centre || Details.rightJustify) + x = 0.0f; + else + x = xstart; +#ifdef MORE_LANGUAGES + if (IsJapaneseFont()) + y += 32.0f * Details.scaleY / 2.75f + 2.0f * Details.scaleY; + else +#endif + y += 32.0f * Details.scaleY * 0.5f + 2.0f * Details.scaleY; + start = s; + }else + break; + } + // advance by one word + t = GetNextSpace(s); + if(t[0] == '\0' || + t[0] == ' ' && t[1] == '\0') + break; + if(!first) + numSpaces++; + first = false; + x += GetStringWidth(s) + GetCharacterSize(*t - ' '); +#ifdef MORE_LANGUAGES + if (IsJapaneseFont() && IsAnsiCharacter(s)) + x += 21.0f; +#endif + lineLength = x; + s = t+1; +#if 0 //def MORE_LANGUAGES + if (IsJapaneseFont() && !*s) { + x += GetStringWidth(s); + if (IsAnsiCharacter(s)) + x += 21.0f; + float xleft = Details.centre ? xstart - x / 2 : + Details.rightJustify ? xstart - x : + xstart; + if (PrintString(xleft, y, start, s, 0.0f, xstart)) + { + start = s; + if (!Details.centre && !Details.rightJustify) + x = xstart; + else + x = 0.0f; + + y += 32.0f * Details.scaleY / 2.75f + 2.0f * Details.scaleY; + numSpaces = 0; + first = true; + lineLength = 0.0f; + } + } +#endif + } + // print rest + if(t[0] == ' ' && t[1] == '\0') + t[0] = '\0'; + x += GetStringWidth(s); + s = t; + float xleft = Details.centre ? xstart - x/2 : + Details.rightJustify ? xstart - x : + xstart; +#if 0 //def MORE_LANGUAGES + if (PrintString(xleft, y, start, s, 0.0f, xstart) && IsJapaneseFont()) { + start = s; + if (!Details.centre && !Details.rightJustify) + x = xstart; + else + x = 0.0f; + y += 32.0f * Details.scaleY / 2.75f + 2.0f * Details.scaleY; + numSpaces = 0; + first = true; + lineLength = 0.0f; + } +#else + PrintString(xleft, y, Details.anonymous_25, start, s, 0.0f); +#endif + } +} + +int +CFont::GetNumberLines(float xstart, float ystart, wchar *s) +{ + int n; + float x, y; + wchar *t; + n = 0; + +#if 0//def MORE_LANGUAGES + bool bSomeJapBool = false; + + if (IsJapanese()) { + t = s; + wchar unused; + while (*t) { + if (*t == JAP_TERMINATION || *t == '~') + t = ParseToken(t, &unused, true); + if (NewLine) { + n++; + NewLine = false; + bSomeJapBool = true; + } + t++; + } + } + + if (bSomeJapBool) n--; +#endif + + if(Details.centre || Details.rightJustify) + x = 0.0f; + else + x = xstart; + y = ystart; + + while(*s){ +#ifdef FIX_BUGS + float f = Details.centre ? Details.centreSize : + Details.rightJustify ? xstart - Details.rightJustifyWrap : + Details.wrapX; +#else + float f = (Details.centre ? Details.centreSize : Details.wrapX); +#endif + +#ifdef MORE_LANGUAGES + if (IsJapaneseFont()) + f -= SCREEN_SCALE_X(21.0f * 2.0f); +#endif + + if(x + GetStringWidth(s) > f){ +#ifdef MORE_LANGUAGES + if (IsJapanese()) + { + if (IsJapanesePunctuation(s)) + s--; + } +#endif + // reached end of line + if(Details.centre || Details.rightJustify) + x = 0.0f; + else + x = xstart; + n++; + // Why even? +#ifdef MORE_LANGUAGES + if (IsJapanese()) + y += 32.0f * Details.scaleY / 2.75f + 2.0f * Details.scaleY; + else +#endif + y += 32.0f * Details.scaleY * 0.5f + 2.0f * Details.scaleY; + }else{ + // still space in current line + t = GetNextSpace(s); + if(*t == '\0'){ + // end of string + x += GetStringWidth(s); +#ifdef MORE_LANGUAGES + if (IsJapanese() && IsAnsiCharacter(s)) + x += 21.0f; +#endif + n++; + s = t; + }else{ + x += GetStringWidth(s); +#ifdef MORE_LANGUAGES + if (IsJapanese() && IsAnsiCharacter(s)) + x += 21.0f; +#endif + s = t+1; + x += GetCharacterSize(*t - ' '); +#ifdef MORE_LANGUAGES + if (IsJapanese() && !*s) + n++; +#endif + } + } + } + + return n; +} + +void +CFont::GetTextRect(CRect *rect, float xstart, float ystart, wchar *s) +{ + int numLines; + float x, y; + int16 maxlength; + wchar *t; + + maxlength = 0; + numLines = 0; + +#ifdef MORE_LANGUAGES + if (IsJapanese()) { + numLines = GetNumberLines(xstart, ystart, s); + }else{ +#endif + if(Details.centre || Details.rightJustify) + x = 0.0f; + else + x = xstart; + y = ystart; + +#ifdef FIX_BUGS + float xEnd = Details.centre ? Details.centreSize : + Details.rightJustify ? xstart - Details.rightJustifyWrap : + Details.wrapX; +#else + float xEnd = (Details.centre ? Details.centreSize : Details.wrapX); +#endif + while(*s){ + if(x + GetStringWidth(s) > xEnd){ + // reached end of line + if(x > maxlength) + maxlength = x; + if(Details.centre || Details.rightJustify) + x = 0.0f; + else + x = xstart; + numLines++; + y += 32.0f * Details.scaleY * 0.5f + 2.0f * Details.scaleY; + }else{ + // still space in current line + t = GetNextSpace(s); + if(*t == '\0'){ + // end of string + x += GetStringWidth(s); + if(x > maxlength) + maxlength = x; + numLines++; + s = t; + }else{ + x += GetStringWidth(s); + x += GetCharacterSize(*t - ' '); + s = t+1; + } + } + } +#ifdef MORE_LANGUAGES + } +#endif + + if(Details.centre){ + if(Details.backgroundOnlyText){ + rect->left = xstart - maxlength/2 - 4.0f; + rect->right = xstart + maxlength/2 + 4.0f; +#ifdef MORE_LANGUAGES + if (IsJapaneseFont()) { + rect->bottom = (32.0f * Details.scaleY / 2.75f + 2.0f * Details.scaleY) * numLines + ystart + (4.0f / 2.75f); + rect->top = ystart - (4.0f / 2.75f); + } else { +#endif + rect->bottom = (32.0f * Details.scaleY * 0.5f + 2.0f * Details.scaleY) * numLines + ystart + 2.0f; + rect->top = ystart - 2.0f; +#ifdef MORE_LANGUAGES + } +#endif + }else{ + rect->left = xstart - Details.centreSize*0.5f - 4.0f; + rect->right = xstart + Details.centreSize*0.5f + 4.0f; +#ifdef MORE_LANGUAGES + if (IsJapaneseFont()) { + rect->bottom = (32.0f * Details.scaleY / 2.75f + 2.0f * Details.scaleY) * numLines + ystart + (4.0f / 2.75f); + rect->top = ystart - (4.0f / 2.75f); + } else { +#endif + rect->bottom = (32.0f * Details.scaleY * 0.5f + 2.0f * Details.scaleY) * numLines + ystart + 2.0f; + rect->top = ystart - 2.0f; +#ifdef MORE_LANGUAGES + } +#endif + } + }else{ + rect->left = xstart - 4.0f; + rect->right = Details.wrapX; + // WTF? + rect->bottom = ystart - 4.0f + 4.0f; +#ifdef MORE_LANGUAGES + if (IsJapaneseFont()) + rect->top = (32.0f * Details.scaleY / 2.75f + 2.0f * Details.scaleY) * numLines + ystart + 2.0f + (4.0f / 2.75f); + else +#endif + rect->top = (32.0f * Details.scaleY * 0.5f + 2.0f * Details.scaleY) * numLines + ystart + 2.0f + 2.0f; + } +} + +float +CFont::GetCharacterWidth(wchar c) +{ +#ifdef MORE_LANGUAGES + if (IsJapanese()) { + if (!RenderState.proportional) + return Size[0][Details.style][192]; + if (c <= 94 || Details.style == FONT_HEADING || RenderState.style == FONT_BANK) { + switch (RenderState.style) + { + case FONT_JAPANESE: + return Size_jp[c]; + default: + return Size[0][RenderState.style][c]; + } + } + + switch (RenderState.style) + { + case FONT_JAPANESE: + return 29.4f; + case FONT_BANK: + return 10.0f; + default: + return Size[0][RenderState.style][c]; + } + } + + else if (RenderState.proportional) + return Size[LanguageSet][RenderState.style][c]; + else + return Size[LanguageSet][RenderState.style][209]; +#else + + if (RenderState.proportional) + return Size[RenderState.style][c]; + else + return Size[RenderState.style][209]; +#endif // MORE_LANGUAGES +} + +float +CFont::GetCharacterSize(wchar c) +{ +#ifdef MORE_LANGUAGES + + if (IsJapanese()) + { + if (!Details.proportional) + return Size[0][Details.style][209] * Details.scaleX; + if (c <= 94 || Details.style == FONT_HEADING || Details.style == FONT_BANK) { + switch (Details.style) + { + case FONT_JAPANESE: + return Size_jp[c] * Details.scaleX; + default: + return Size[0][Details.style][c] * Details.scaleX; + } + } + + switch (Details.style) + { + case FONT_JAPANESE: + return 29.4f * Details.scaleX; + case FONT_BANK: + return 10.0f * Details.scaleX; + default: + return Size[0][Details.style][c] * Details.scaleX; + } + } + else + { + if (!Details.bFontHalfTexture && c == 30) c = 61; // wanted star + if (Details.bFontHalfTexture) + c = FindNewCharacter(c); + if (Details.proportional) + return Size[LanguageSet][Details.style][c] * Details.scaleX; + else + return Size[LanguageSet][Details.style][209] * Details.scaleX; + } +#else + +#ifdef FIX_BUGS + // PS2 don't call FindNewCharacter in here at all, and also uses different chars for some symbols + if (!Details.bFontHalfTexture && c == 30) c = 61; // wanted star +#endif + if (Details.bFontHalfTexture) + c = FindNewCharacter(c); + if (Details.proportional) + return Size[Details.style][c] * Details.scaleX; + else + return Size[Details.style][209] * Details.scaleX; +#endif // MORE_LANGUAGES +} + +float +CFont::GetStringWidth(wchar *s, bool spaces) +{ + float w; + + w = 0.0f; +#ifdef MORE_LANGUAGES + if (IsJapanese()) + { + do + { + if ((*s != ' ' || spaces) && *s != '\0') { + do { + while (*s == '~' || *s == JAP_TERMINATION) { + s++; +#ifdef BUTTON_ICONS + switch (*s) { + case 'U': + case 'D': + case '<': + case '>': + case 'X': + case 'O': + case 'Q': + case 'T': + case 'K': + case 'M': + case 'A': + case 'J': + case 'V': + case 'C': + case '(': + case ')': + w += 17.0f * Details.scaleY; + break; + default: + break; + } +#endif + while (!(*s == '~' || *s == JAP_TERMINATION)) s++; + s++; + } + w += GetCharacterSize(*s - ' '); + ++s; + } while (*s == '~' || *s == JAP_TERMINATION); + } + } while (IsAnsiCharacter(s)); + } else +#endif + { + for (wchar c = *s; (c != ' ' || spaces) && c != '\0'; c = *(++s)) { + if (c == '~') { + s++; +#ifdef BUTTON_ICONS + switch (*s) { + case 'U': + case 'D': + case '<': + case '>': + case 'X': + case 'O': + case 'Q': + case 'T': + case 'K': + case 'M': + case 'A': + case 'J': + case 'V': + case 'C': + case '(': + case ')': + w += 17.0f * Details.scaleY; + break; + default: + break; + } +#endif + while (*s != '~') { + s++; + } + } + else { + w += GetCharacterSize(c - ' '); + } + } + } + return w; +} + + +#ifdef MORE_LANGUAGES +float +CFont::GetStringWidth_Jap(wchar* s) +{ + float w; + + w = 0.0f; + for (; *s != '\0';) { + do { + while (*s == '~' || *s == JAP_TERMINATION) { + s++; + while (!(*s == '~' || *s == JAP_TERMINATION)) s++; + s++; + } + w += GetCharacterSize(*s - ' '); + ++s; + } while (*s == '~' || *s == JAP_TERMINATION); + } + return w; +} +#endif + +wchar* +CFont::GetNextSpace(wchar *s) +{ +#ifdef MORE_LANGUAGES + if (IsJapanese()) { + do + { + if (*s != ' ' && *s != '\0') { + do { + while (*s == '~' || *s == JAP_TERMINATION) { + s++; + while (!(*s == '~' || *s == JAP_TERMINATION)) s++; + s++; + } + ++s; + } while (*s == '~' || *s == JAP_TERMINATION); + } + } while (IsAnsiCharacter(s)); + } else +#endif + { + for(; *s != ' ' && *s != '\0'; s++) + if(*s == '~'){ + s++; + while(*s != '~') s++; + } + } + return s; +} + +wchar* +CFont::ParseToken(wchar* str, CRGBA &color, bool &flash, bool &bold) +{ + Details.anonymous_23 = false; + wchar *s = str + 1; + if (Details.color.r || Details.color.g || Details.color.b) + { + switch (*s) + { + case 'B': + bold = !bold; + break; + case 'b': + color.r = 27; + color.g = 89; + color.b = 130; + break; + case 'f': + flash = !flash; + break; + case 'g': + color.r = 255; + color.g = 150; + color.b = 225; + break; + case 'h': + color.r = 225; + color.g = 225; + color.b = 225; + break; + case 'l': + color.r = 0; + color.g = 0; + color.b = 0; + break; + case 'o': + color.r = 229; + color.g = 125; + color.b = 126; + break; + case 'p': + color.r = 168; + color.g = 110; + color.b = 252; + break; + case 'q': + color.r = 199; + color.g = 144; + color.b = 203; + break; + case 'r': + color.r = 255; + color.g = 150; + color.b = 225; + break; + case 't': + color.r = 86; + color.g = 212; + color.b = 146; + break; + case 'w': + color.r = 175; + color.g = 175; + color.b = 175; + break; +#ifdef FIX_BUGS + case 'x': + color.r = 0; + color.g = 255; + color.b = 255; + break; +#else + case 'x': + color.r = 132; + color.g = 146; + color.b = 197; + break; +#endif + case 'y': + color.r = 255; + color.g = 227; + color.b = 79; + break; +#ifdef BUTTON_ICONS + case 'U': PS2Symbol = BUTTON_UP; break; + case 'D': PS2Symbol = BUTTON_DOWN; break; + case '<': PS2Symbol = BUTTON_LEFT; break; + case '>': PS2Symbol = BUTTON_RIGHT; break; + case 'X': PS2Symbol = BUTTON_CROSS; break; + case 'O': PS2Symbol = BUTTON_CIRCLE; break; + case 'Q': PS2Symbol = BUTTON_SQUARE; break; + case 'T': PS2Symbol = BUTTON_TRIANGLE; break; + case 'K': PS2Symbol = BUTTON_L1; break; + case 'M': PS2Symbol = BUTTON_L2; break; + case 'A': PS2Symbol = BUTTON_L3; break; + case 'J': PS2Symbol = BUTTON_R1; break; + case 'V': PS2Symbol = BUTTON_R2; break; + case 'C': PS2Symbol = BUTTON_R3; break; + case 'H': PS2Symbol = BUTTON_RSTICK_UP; break; + case 'L': PS2Symbol = BUTTON_RSTICK_DOWN; break; + case '(': PS2Symbol = BUTTON_RSTICK_LEFT; break; + case ')': PS2Symbol = BUTTON_RSTICK_RIGHT; break; +#endif + default: + break; + } + } + while (*s != '~') + ++s; + if (*(++s) == '~') + s = ParseToken(s, color, flash, bold); + return s; +} + +#if 0//def MORE_LANGUAGES +wchar* +CFont::ParseToken(wchar *s, bool japShit) +{ + s++; + if ((Details.color.r || Details.color.g || Details.color.b) && !japShit) { + wchar c = *s; + if (IsJapanese()) + c &= 0x7FFF; + switch (c) { + case 'N': + case 'n': + NewLine = true; + break; + case 'b': SetColor(CRGBA(128, 167, 243, 255)); break; + case 'g': SetColor(CRGBA(95, 160, 106, 255)); break; + case 'h': SetColor(CRGBA(225, 225, 225, 255)); break; + case 'l': SetColor(CRGBA(0, 0, 0, 255)); break; + case 'p': SetColor(CRGBA(168, 110, 252, 255)); break; + case 'r': SetColor(CRGBA(113, 43, 73, 255)); break; + case 'w': SetColor(CRGBA(175, 175, 175, 255)); break; + case 'y': SetColor(CRGBA(210, 196, 106, 255)); break; +#ifdef BUTTON_ICONS + case 'U': PS2Symbol = BUTTON_UP; break; + case 'D': PS2Symbol = BUTTON_DOWN; break; + case '<': PS2Symbol = BUTTON_LEFT; break; + case '>': PS2Symbol = BUTTON_RIGHT; break; + case 'X': PS2Symbol = BUTTON_CROSS; break; + case 'O': PS2Symbol = BUTTON_CIRCLE; break; + case 'Q': PS2Symbol = BUTTON_SQUARE; break; + case 'T': PS2Symbol = BUTTON_TRIANGLE; break; + case 'K': PS2Symbol = BUTTON_L1; break; + case 'M': PS2Symbol = BUTTON_L2; break; + case 'A': PS2Symbol = BUTTON_L3; break; + case 'J': PS2Symbol = BUTTON_R1; break; + case 'V': PS2Symbol = BUTTON_R2; break; + case 'C': PS2Symbol = BUTTON_R3; break; + case 'H': PS2Symbol = BUTTON_RSTICK_UP; break; + case 'L': PS2Symbol = BUTTON_RSTICK_DOWN; break; + case '(': PS2Symbol = BUTTON_RSTICK_LEFT; break; + case ')': PS2Symbol = BUTTON_RSTICK_RIGHT; break; +#endif + } + } else if (IsJapanese()) { + if ((*s & 0x7FFF) == 'N' || (*s & 0x7FFF) == 'n') + NewLine = true; + } + while ((!IsJapanese() || (*s != JAP_TERMINATION)) && *s != '~') s++; + return s + 1; +} +#else +wchar* +CFont::ParseToken(wchar *s) +{ + Details.anonymous_23 = false; + s++; + if(Details.color.r || Details.color.g || Details.color.b) + switch(*s){ + case 'B': + Details.bBold = !Details.bBold; + break; + case 'N': + case 'n': + NewLine = true; + break; + case 'b': + Details.color.r = 27; + Details.color.g = 89; + Details.color.b = 130; + Details.anonymous_23 = true; + break; + case 'f': + Details.bFlash = !Details.bFlash; + if (!Details.bFlash) + Details.color.a = 255; + break; + case 'g': + Details.color.r = 255; + Details.color.g = 150; + Details.color.b = 225; + Details.anonymous_23 = true; + break; + case 'h': + Details.color.r = 225; + Details.color.g = 225; + Details.color.b = 225; + Details.anonymous_23 = true; + break; + case 'l': + Details.color.r = 0; + Details.color.g = 0; + Details.color.b = 0; + Details.anonymous_23 = true; + break; + case 'o': + Details.color.r = 229; + Details.color.g = 125; + Details.color.b = 126; + Details.anonymous_23 = true; + break; + case 'p': + Details.color.r = 168; + Details.color.g = 110; + Details.color.b = 252; + Details.anonymous_23 = true; + break; + case 'q': + Details.color.r = 199; + Details.color.g = 144; + Details.color.b = 203; + Details.anonymous_23 = true; + break; + case 'r': + Details.color.r = 255; + Details.color.g = 150; + Details.color.b = 225; + Details.anonymous_23 = true; + break; + case 't': + Details.color.r = 86; + Details.color.g = 212; + Details.color.b = 146; + Details.anonymous_23 = true; + break; + case 'w': + Details.color.r = 175; + Details.color.g = 175; + Details.color.b = 175; + Details.anonymous_23 = true; + break; + case 'x': +#ifdef FIX_BUGS + Details.color.r = 0; + Details.color.g = 255; + Details.color.b = 255; +#else + Details.color.r = 132; + Details.color.g = 146; + Details.color.b = 197; +#endif + Details.anonymous_23 = true; + break; + case 'y': + Details.color.r = 255; + Details.color.g = 227; + Details.color.b = 79; + Details.anonymous_23 = true; + break; +#ifdef BUTTON_ICONS + case 'U': PS2Symbol = BUTTON_UP; break; + case 'D': PS2Symbol = BUTTON_DOWN; break; + case '<': PS2Symbol = BUTTON_LEFT; break; + case '>': PS2Symbol = BUTTON_RIGHT; break; + case 'X': PS2Symbol = BUTTON_CROSS; break; + case 'O': PS2Symbol = BUTTON_CIRCLE; break; + case 'Q': PS2Symbol = BUTTON_SQUARE; break; + case 'T': PS2Symbol = BUTTON_TRIANGLE; break; + case 'K': PS2Symbol = BUTTON_L1; break; + case 'M': PS2Symbol = BUTTON_L2; break; + case 'A': PS2Symbol = BUTTON_L3; break; + case 'J': PS2Symbol = BUTTON_R1; break; + case 'V': PS2Symbol = BUTTON_R2; break; + case 'C': PS2Symbol = BUTTON_R3; break; + case 'H': PS2Symbol = BUTTON_RSTICK_UP; break; + case 'L': PS2Symbol = BUTTON_RSTICK_DOWN; break; + case '(': PS2Symbol = BUTTON_RSTICK_LEFT; break; + case ')': PS2Symbol = BUTTON_RSTICK_RIGHT; break; +#endif + } + while(*s != '~') s++; + if (*(++s) == '~') + s = ParseToken(s); + return s; +} +#endif + +void +CFont::FilterOutTokensFromString(wchar *str) +{ + int newIdx = 0; + wchar copy[256], *c; + UnicodeStrcpy(copy, str); + + for (c = copy; *c != '\0'; c++) { + if (*c == '~') { + c++; + while (*c != '~') c++; + } else { + str[newIdx++] = *c; + } + } + str[newIdx] = '\0'; +} + +void +CFont::DrawFonts(void) +{ + RenderFontBuffer(); +} + +void +CFont::SetScale(float x, float y) +{ +#ifdef MORE_LANGUAGES + /*if (IsJapanese()) { + x *= 1.35f; + y *= 1.25f; + }*/ +#endif + Details.scaleX = x; + Details.scaleY = y; +} + +void +CFont::SetSlantRefPoint(float x, float y) +{ + Details.slantRefX = x; + Details.slantRefY = y; +} + +void +CFont::SetSlant(float s) +{ + Details.slant = s; +} + +void +CFont::SetColor(CRGBA col) +{ + Details.color = col; + if (Details.alphaFade < 255.0f) + Details.color.a *= Details.alphaFade / 255.0f; +} + +void +CFont::SetJustifyOn(void) +{ + Details.justify = true; + Details.centre = false; + Details.rightJustify = false; +} + +void +CFont::SetJustifyOff(void) +{ + Details.justify = false; + Details.rightJustify = false; +} + +void +CFont::SetCentreOn(void) +{ + Details.centre = true; + Details.justify = false; + Details.rightJustify = false; +} + +void +CFont::SetCentreOff(void) +{ + Details.centre = false; +} + +void +CFont::SetWrapx(float x) +{ + Details.wrapX = x; +} + +void +CFont::SetCentreSize(float s) +{ + Details.centreSize = s; +} + +void +CFont::SetBackgroundOn(void) +{ + Details.background = true; +} + +void +CFont::SetBackgroundOff(void) +{ + Details.background = false; +} + +void +CFont::SetBackgroundColor(CRGBA col) +{ + Details.backgroundColor = col; +} + +void +CFont::SetBackGroundOnlyTextOn(void) +{ + Details.backgroundOnlyText = true; +} + +void +CFont::SetBackGroundOnlyTextOff(void) +{ + Details.backgroundOnlyText = false; +} + +void +CFont::SetRightJustifyOn(void) +{ + Details.rightJustify = true; + Details.justify = false; + Details.centre = false; +} + +void +CFont::SetRightJustifyOff(void) +{ + Details.rightJustify = false; + Details.justify = false; + Details.centre = false; +} + +void +CFont::SetPropOff(void) +{ + Details.proportional = false; +} + +void +CFont::SetPropOn(void) +{ + Details.proportional = true; +} + +void +CFont::SetFontStyle(int16 style) +{ + if (style == FONT_HEADING) { + Details.style = FONT_STANDARD; + Details.bFontHalfTexture = true; + } else { + Details.style = style; + Details.bFontHalfTexture = false; + } +} + +void +CFont::SetRightJustifyWrap(float wrap) +{ + Details.rightJustifyWrap = wrap; +} + +void +CFont::SetAlphaFade(float fade) +{ + Details.alphaFade = fade; +} + +void +CFont::SetDropColor(CRGBA col) +{ + Details.dropColor = col; + if (Details.alphaFade < 255.0f) + Details.dropColor.a *= Details.alphaFade / 255.0f; +} + +void +CFont::SetDropShadowPosition(int16 pos) +{ + Details.dropShadowPosition = pos; +} + +wchar CFont::FindNewCharacter(wchar c) +{ + if (c >= 16 && c <= 26) return c + 128; + if (c >= 8 && c <= 9) return c + 86; + if (c == 4) return c + 89; + if (c == 7) return 206; + if (c == 14) return 207; + if (c >= 33 && c <= 58) return c + 122; + if (c >= 65 && c <= 90) return c + 90; + if (c >= 96 && c <= 118) return c + 85; + if (c >= 119 && c <= 140) return c + 62; + if (c >= 141 && c <= 142) return 204; + if (c == 143) return 205; + if (c == 1) return 208; + return c; +} + +wchar +CFont::character_code(uint8 c) +{ + if(c < 128) + return c; + return foreign_table[c-128]; +} \ No newline at end of file diff --git a/src/miami/renderer/Font.h b/src/miami/renderer/Font.h new file mode 100644 index 00000000..02e7df3b --- /dev/null +++ b/src/miami/renderer/Font.h @@ -0,0 +1,217 @@ +#pragma once + +#include "Sprite2d.h" + +void AsciiToUnicode(const char *src, wchar *dst); +void UnicodeStrcpy(wchar *dst, const wchar *src); +void UnicodeStrcat(wchar *dst, wchar *append); +int UnicodeStrlen(const wchar *str); +void UnicodeMakeUpperCase(wchar *dst, const wchar *src); + +struct CFontDetails +{ + CRGBA color; + float scaleX; + float scaleY; + float slant; + float slantRefX; + float slantRefY; + bool8 justify; + bool8 centre; + bool8 rightJustify; + bool8 background; + bool8 backgroundOnlyText; + bool8 proportional; + bool8 bIsShadow; + bool8 bFlash; + bool8 bBold; + float alphaFade; + CRGBA backgroundColor; + float wrapX; + float centreSize; + float rightJustifyWrap; + int16 style; + bool8 bFontHalfTexture; + uint32 bank; + int16 dropShadowPosition; + CRGBA dropColor; + bool8 bFlashState; + int nFlashTimer; + bool8 anonymous_23; + uint32 anonymous_25; +}; + +struct CFontRenderState +{ + uint32 anonymous_0; + float fTextPosX; + float fTextPosY; + float scaleX; + float scaleY; + CRGBA color; + float fExtraSpace; + float slant; + float slantRefX; + float slantRefY; + bool8 bIsShadow; + bool8 bFontHalfTexture; + bool8 proportional; + bool8 anonymous_14; + int16 style; +}; + +class CSprite2d; + +enum { + FONT_BANK, + FONT_STANDARD, + FONT_HEADING, +#ifdef MORE_LANGUAGES + FONT_JAPANESE, +#endif + MAX_FONTS = FONT_HEADING +}; + +enum { + ALIGN_LEFT, + ALIGN_CENTER, + ALIGN_RIGHT, +}; + +#ifdef MORE_LANGUAGES +enum +{ + FONT_LANGSET_EFIGS, + FONT_LANGSET_RUSSIAN, + FONT_LANGSET_POLISH, + FONT_LANGSET_JAPANESE, + LANGSET_MAX +}; + +#define FONT_LOCALE(style) (CFont::IsJapanese() ? FONT_JAPANESE : style) +#else +#define FONT_LOCALE(style) (style) +#endif + +#ifdef BUTTON_ICONS +enum +{ + BUTTON_NONE = -1, + BUTTON_UP, + BUTTON_DOWN, + BUTTON_LEFT, + BUTTON_RIGHT, + BUTTON_CROSS, + BUTTON_CIRCLE, + BUTTON_SQUARE, + BUTTON_TRIANGLE, + BUTTON_L1, + BUTTON_L2, + BUTTON_L3, + BUTTON_R1, + BUTTON_R2, + BUTTON_R3, + BUTTON_RSTICK_UP, + BUTTON_RSTICK_DOWN, + BUTTON_RSTICK_LEFT, + BUTTON_RSTICK_RIGHT, + MAX_BUTTON_ICONS +}; +#endif // BUTTON_ICONS + + +class CFont +{ +#ifdef MORE_LANGUAGES + static int16 Size[LANGSET_MAX][MAX_FONTS][210]; + static uint8 LanguageSet; + static int32 Slot; +#else + static int16 Size[MAX_FONTS][210]; +#endif + static bool16 NewLine; +public: + static CSprite2d Sprite[MAX_FONTS]; + static CFontDetails Details; + static CFontRenderState RenderState; + +#ifdef BUTTON_ICONS + static int32 ButtonsSlot; + static CSprite2d ButtonSprite[MAX_BUTTON_ICONS]; + static int PS2Symbol; + + static void LoadButtons(const char *txdPath); + static void DrawButton(float x, float y); +#endif // BUTTON_ICONS + + + static void Initialise(void); + static void Shutdown(void); + static void InitPerFrame(void); + static void PrintChar(float x, float y, wchar c); + static void PrintString(float x, float y, wchar *s); +#ifdef XBOX_SUBTITLES + static void PrintStringFromBottom(float x, float y, wchar *str); + static void PrintOutlinedString(float x, float y, wchar *str, float outlineStrength, bool fromBottom, CRGBA outlineColor); +#endif + static int GetNumberLines(float xstart, float ystart, wchar *s); + static void GetTextRect(CRect *rect, float xstart, float ystart, wchar *s); +//#ifdef MORE_LANGUAGES +// static bool PrintString(float x, float y, wchar *start, wchar* &end, float spwidth, float japX); +//#else + static void PrintString(float x, float y, uint32, wchar *start, wchar *end, float spwidth); +//#endif + static void PrintStringFromBottom(float x, float y, wchar *str); + static float GetCharacterWidth(wchar c); + static float GetCharacterSize(wchar c); + static float GetStringWidth(wchar *s, bool spaces = false); +#ifdef MORE_LANGUAGES + static float GetStringWidth_Jap(wchar* s); +#endif + static uint16 *GetNextSpace(wchar *s); +//#ifdef MORE_LANGUAGES +// static uint16 *ParseToken(wchar *s, bool japShit = false); +//#else + static uint16 *ParseToken(wchar *s); + static uint16 *ParseToken(wchar *s, CRGBA &color, bool &flash, bool &bold); +//#endif + static void DrawFonts(void); + static void RenderFontBuffer(void); + static uint16 character_code(uint8 c); + + static void SetScale(float x, float y); + static void SetSlantRefPoint(float x, float y); + static void SetSlant(float s); + static void SetJustifyOn(void); + static void SetJustifyOff(void); + static void SetRightJustifyOn(void); + static void SetRightJustifyOff(void); + static void SetCentreOn(void); + static void SetCentreOff(void); + static void SetWrapx(float x); + static void SetCentreSize(float s); + static void SetBackgroundOn(void); + static void SetBackgroundOff(void); + static void SetBackGroundOnlyTextOn(void); + static void SetBackGroundOnlyTextOff(void); + static void SetPropOn(void); + static void SetPropOff(void); + static void SetFontStyle(int16 style); + static void SetRightJustifyWrap(float wrap); + static void SetAlphaFade(float fade); + static void SetDropShadowPosition(int16 pos); + static void SetBackgroundColor(CRGBA col); + static void SetColor(CRGBA col); + static void SetDropColor(CRGBA col); + static wchar FindNewCharacter(wchar c); + static void FilterOutTokensFromString(wchar*); +#ifdef MORE_LANGUAGES + static void ReloadFonts(uint8 set); + + // japanese stuff + static bool IsAnsiCharacter(wchar* s); + static bool IsJapanesePunctuation(wchar* str); + static bool IsJapanese() { return LanguageSet == FONT_LANGSET_JAPANESE; } + static bool IsJapaneseFont() { return IsJapanese() && (Details.style == FONT_JAPANESE); } +#endif +}; diff --git a/src/miami/renderer/Glass.cpp b/src/miami/renderer/Glass.cpp new file mode 100644 index 00000000..40a5fa0d --- /dev/null +++ b/src/miami/renderer/Glass.cpp @@ -0,0 +1,1027 @@ +#include "common.h" + +#include "Glass.h" +#include "Timer.h" +#include "Object.h" +#include "Vehicle.h" +#include "Pools.h" +#include "General.h" +#include "AudioScriptObject.h" +#include "World.h" +#include "Timecycle.h" +#include "Particle.h" +#include "Camera.h" +#include "RenderBuffer.h" +#include "Shadows.h" +#include "ModelIndices.h" +#include "main.h" +#include "soundlist.h" +#include "SurfaceTable.h" + + +uint32 CGlass::NumGlassEntities; +CEntity *CGlass::apEntitiesToBeRendered[NUM_GLASSENTITIES]; +CFallingGlassPane CGlass::aGlassPanes[NUM_GLASSPANES]; + + +CVector2D CentersWithTriangle[NUM_GLASSTRIANGLES]; +const CVector2D CoorsWithTriangle[NUM_GLASSTRIANGLES][3] = +{ + { + CVector2D(0.0f, 0.0f), + CVector2D(0.0f, 1.0f), + CVector2D(0.4f, 0.5f) + }, + + { + CVector2D(0.0f, 1.0f), + CVector2D(1.0f, 1.0f), + CVector2D(0.4f, 0.5f) + }, + + { + CVector2D(0.0f, 0.0f), + CVector2D(0.4f, 0.5f), + CVector2D(0.7f, 0.0f) + }, + + { + CVector2D(0.7f, 0.0f), + CVector2D(0.4f, 0.5f), + CVector2D(1.0f, 1.0f) + }, + + { + CVector2D(0.7f, 0.0f), + CVector2D(1.0f, 1.0f), + CVector2D(1.0f, 0.0f) + } +}; + +#define TEMPBUFFERVERTHILIGHTOFFSET 0 +#define TEMPBUFFERINDEXHILIGHTOFFSET 0 +#define TEMPBUFFERVERTHILIGHTSIZE 256 +#define TEMPBUFFERINDEXHILIGHTSIZE 512 + +#define TEMPBUFFERVERTSHATTEREDOFFSET TEMPBUFFERVERTHILIGHTSIZE +#define TEMPBUFFERINDEXSHATTEREDOFFSET TEMPBUFFERINDEXHILIGHTSIZE +#define TEMPBUFFERVERTSHATTEREDSIZE 384 +#define TEMPBUFFERINDEXSHATTEREDSIZE 768 + +#define TEMPBUFFERVERTREFLECTIONOFFSET TEMPBUFFERVERTSHATTEREDSIZE +#define TEMPBUFFERINDEXREFLECTIONOFFSET TEMPBUFFERINDEXSHATTEREDSIZE +#define TEMPBUFFERVERTREFLECTIONSIZE 512 +#define TEMPBUFFERINDEXREFLECTIONSIZE 1024 + +int32 TempBufferIndicesStoredHiLight = 0; +int32 TempBufferVerticesStoredHiLight = 0; +int32 TempBufferIndicesStoredShattered = 0; +int32 TempBufferVerticesStoredShattered = 0; +int32 TempBufferIndicesStoredReflection = 0; +int32 TempBufferVerticesStoredReflection = 0; + +void +CFallingGlassPane::Update(void) +{ + if ( CTimer::GetTimeInMilliseconds() >= m_nTimer ) + { + // Apply MoveSpeed + if ( m_bCarGlass ) + GetPosition() += m_vecMoveSpeed * CTimer::GetTimeStep() * 0.35f; + else + GetPosition() += m_vecMoveSpeed * CTimer::GetTimeStep(); + + // Apply Gravity + if ( m_bCarGlass ) + m_vecMoveSpeed.z -= 0.01f * CTimer::GetTimeStep(); + else + m_vecMoveSpeed.z -= 0.02f * CTimer::GetTimeStep(); + + // Apply TurnSpeed + GetRight() += CrossProduct(m_vecTurn, GetRight()); + GetForward() += CrossProduct(m_vecTurn, GetForward()); + GetUp() += CrossProduct(m_vecTurn, GetUp()); + + if ( GetPosition().z < m_fGroundZ ) + { + CVector pos; + CVector dir; + + m_bActive = false; + + pos = CVector(GetPosition().x, GetPosition().y, m_fGroundZ); + + PlayOneShotScriptObject(SCRIPT_SOUND_GLASS_LIGHT_BREAK, pos); + + RwRGBA color = { 255, 255, 255, 255 }; + + if ( !m_bCarGlass ) + { + static int32 nFrameGen = 0; + + for ( int32 i = 0; i < 4; i++ ) + { + dir.x = CGeneral::GetRandomNumberInRange(-0.35f, 0.35f); + dir.y = CGeneral::GetRandomNumberInRange(-0.35f, 0.35f); + dir.z = CGeneral::GetRandomNumberInRange(0.05f, 0.20f); + + CParticle::AddParticle(PARTICLE_CAR_DEBRIS, + pos, + dir, + nil, + CGeneral::GetRandomNumberInRange(0.02f, 0.2f), + color, + CGeneral::GetRandomNumberInRange(-40, 40), + 0, + ++nFrameGen & 3, + 500); + } + } + } + } +} + +void +CFallingGlassPane::Render(void) +{ + float distToCamera = (TheCamera.GetPosition() - GetPosition()).Magnitude(); + + CVector fwdNorm = GetForward(); + fwdNorm.Normalise(); + uint8 alpha = CGlass::CalcAlphaWithNormal(&fwdNorm); + +#ifdef FIX_BUGS + uint16 time = Clamp(CTimer::GetTimeInMilliseconds() > m_nTimer ? CTimer::GetTimeInMilliseconds() - m_nTimer : 0u, 0u, 500u); +#else + uint16 time = Clamp(CTimer::GetTimeInMilliseconds() - m_nTimer, 0, 500); +#endif + + uint8 color = int32( float(alpha) * (float(time) / 500) ); + + if ( TempBufferIndicesStoredHiLight >= TEMPBUFFERINDEXHILIGHTSIZE-7 || TempBufferVerticesStoredHiLight >= TEMPBUFFERVERTHILIGHTSIZE-4 ) + CGlass::RenderHiLightPolys(); + + // HiLight Polys + + if ( m_bCarGlass && color < 64 ) + color = 64; + + RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 0], color, color, color, color); + RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 1], color, color, color, color); + RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 2], color, color, color, color); + + RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 0], 0.5f); + RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 0], 0.5f); + RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 1], 0.5f); + RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 1], 0.6f); + RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 2], 0.6f); + RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 2], 0.6f); + + ASSERT(m_nTriIndex < NUM_GLASSTRIANGLES); + + CVector2D p0 = CoorsWithTriangle[m_nTriIndex][0] - CentersWithTriangle[m_nTriIndex]; + CVector2D p1 = CoorsWithTriangle[m_nTriIndex][1] - CentersWithTriangle[m_nTriIndex]; + CVector2D p2 = CoorsWithTriangle[m_nTriIndex][2] - CentersWithTriangle[m_nTriIndex]; + CVector v0 = *this * CVector(p0.x, 0.0f, p0.y); + CVector v1 = *this * CVector(p1.x, 0.0f, p1.y); + CVector v2 = *this * CVector(p2.x, 0.0f, p2.y); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 0], v0.x, v0.y, v0.z); + RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 1], v1.x, v1.y, v1.z); + RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 2], v2.x, v2.y, v2.z); + + TempBufferRenderIndexList[TempBufferIndicesStoredHiLight + 0] = TempBufferVerticesStoredHiLight + 0; + TempBufferRenderIndexList[TempBufferIndicesStoredHiLight + 1] = TempBufferVerticesStoredHiLight + 1; + TempBufferRenderIndexList[TempBufferIndicesStoredHiLight + 2] = TempBufferVerticesStoredHiLight + 2; + TempBufferRenderIndexList[TempBufferIndicesStoredHiLight + 3] = TempBufferVerticesStoredHiLight + 0; + TempBufferRenderIndexList[TempBufferIndicesStoredHiLight + 4] = TempBufferVerticesStoredHiLight + 2; + TempBufferRenderIndexList[TempBufferIndicesStoredHiLight + 5] = TempBufferVerticesStoredHiLight + 1; + + TempBufferVerticesStoredHiLight += 3; + TempBufferIndicesStoredHiLight += 6; + + if ( m_bShattered ) + { + if ( TempBufferIndicesStoredShattered >= TEMPBUFFERINDEXSHATTEREDSIZE-7 || TempBufferVerticesStoredShattered >= TEMPBUFFERVERTSHATTEREDSIZE-4 ) + CGlass::RenderShatteredPolys(); + + uint8 shatteredColor = 140; + if ( distToCamera > 30.0f ) + shatteredColor = int32((1.0f - (distToCamera - 30.0f) * 4.0f / 40.0f) * 140); + + RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 0], shatteredColor, shatteredColor, shatteredColor, shatteredColor); + RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 1], shatteredColor, shatteredColor, shatteredColor, shatteredColor); + RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 2], shatteredColor, shatteredColor, shatteredColor, shatteredColor); + + RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 0], 4.0f * CoorsWithTriangle[m_nTriIndex][0].x * m_fStep); + RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 0], 4.0f * CoorsWithTriangle[m_nTriIndex][0].y * m_fStep); + RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 1], 4.0f * CoorsWithTriangle[m_nTriIndex][1].x * m_fStep); + RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 1], 4.0f * CoorsWithTriangle[m_nTriIndex][1].y * m_fStep); + RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 2], 4.0f * CoorsWithTriangle[m_nTriIndex][2].x * m_fStep); + RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 2], 4.0f * CoorsWithTriangle[m_nTriIndex][2].y * m_fStep); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 0], v0.x, v0.y, v0.z); + RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 1], v1.x, v1.y, v1.z); + RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 2], v2.x, v2.y, v2.z); + + TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 0] = TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET + 0; + TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 1] = TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET + 1; + TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 2] = TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET + 2; + TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 3] = TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET + 0; + TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 4] = TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET + 2; + TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 5] = TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET + 1; + + TempBufferIndicesStoredShattered += 6; + TempBufferVerticesStoredShattered += 3; + } +} + +void +CGlass::Init(void) +{ + for ( int32 i = 0; i < NUM_GLASSPANES; i++ ) + aGlassPanes[i].m_bActive = false; + + for ( int32 i = 0; i < NUM_GLASSTRIANGLES; i++ ) + CentersWithTriangle[i] = (CoorsWithTriangle[i][0] + CoorsWithTriangle[i][1] + CoorsWithTriangle[i][2]) / 3; +} + +void +CGlass::Update(void) +{ + for ( int32 i = 0; i < NUM_GLASSPANES; i++ ) + { + if ( aGlassPanes[i].m_bActive ) + aGlassPanes[i].Update(); + } +} + +void +CGlass::Render(void) +{ + TempBufferVerticesStoredHiLight = 0; + TempBufferIndicesStoredHiLight = 0; + + TempBufferVerticesStoredShattered = TEMPBUFFERVERTSHATTEREDOFFSET; + TempBufferIndicesStoredShattered = TEMPBUFFERINDEXSHATTEREDOFFSET; + + TempBufferVerticesStoredReflection = TEMPBUFFERVERTREFLECTIONOFFSET; + TempBufferIndicesStoredReflection = TEMPBUFFERINDEXREFLECTIONOFFSET; + + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void *)rwFILTERLINEAR); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATEFOGCOLOR, (void *)RWRGBALONG(CTimeCycle::GetFogRed(), CTimeCycle::GetFogGreen(), CTimeCycle::GetFogBlue(), 255)); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); + + PUSH_RENDERGROUP("CGlass::Render"); + + for ( int32 i = 0; i < NUM_GLASSPANES; i++ ) + { + if ( aGlassPanes[i].m_bActive ) + aGlassPanes[i].Render(); + } + + for ( uint32 i = 0; i < NumGlassEntities; i++ ) + RenderEntityInGlass(apEntitiesToBeRendered[i]); + + POP_RENDERGROUP(); + + NumGlassEntities = 0; + + RenderHiLightPolys(); + RenderShatteredPolys(); + RenderReflectionPolys(); + + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)FALSE); +} + +CFallingGlassPane * +CGlass::FindFreePane(void) +{ + for ( int32 i = 0; i < NUM_GLASSPANES; i++ ) + { + if ( !aGlassPanes[i].m_bActive ) + return &aGlassPanes[i]; + } + + return nil; +} + +void +CGlass::GeneratePanesForWindow(uint32 type, CVector pos, CVector up, CVector right, CVector speed, CVector center, + float moveSpeed, bool cracked, bool explosion, int32 stepmul, bool carGlass) +{ + float upLen = up.Magnitude(); + float rightLen = right.Magnitude(); + + float upSteps = upLen + 0.75f; + if ( upSteps < 1.0f ) upSteps = 1.0f; + + float rightSteps = rightLen + 0.75f; + if ( rightSteps < 1.0f ) rightSteps = 1.0f; + + uint32 ysteps = stepmul * (uint32)upSteps; + if ( ysteps > 3 ) ysteps = 3; + + uint32 xsteps = stepmul * (uint32)rightSteps; + if ( xsteps > 3 ) xsteps = 3; + + if ( explosion ) + { + if ( ysteps > 1 ) ysteps = 1; + if ( xsteps > 1 ) xsteps = 1; + } + + float upScl = upLen / float(ysteps); + float rightScl = rightLen / float(xsteps); + + bool bZFound; + float groundZ = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &bZFound); + if ( !bZFound ) groundZ = pos.z - 2.0f; + + for ( uint32 y = 0; y < ysteps; y++ ) + { + for ( uint32 x = 0; x < xsteps; x++ ) + { + float stepy = float(y) * upLen / float(ysteps); + float stepx = float(x) * rightLen / float(xsteps); + + for ( int32 i = 0; i < NUM_GLASSTRIANGLES; i++ ) + { + CFallingGlassPane *pane = FindFreePane(); + if ( pane ) + { + pane->m_nTriIndex = i; + + pane->GetRight() = (right * rightScl) / rightLen; + pane->GetUp() = (up * upScl) / upLen; + + CVector fwd = CrossProduct(pane->GetRight(), pane->GetUp()); + fwd.Normalise(); + + pane->GetForward() = fwd; + + pane->GetPosition() = right / rightLen * (rightScl * CentersWithTriangle[i].x + stepx) + + up / upLen * (upScl * CentersWithTriangle[i].y + stepy) + + pos; + + pane->m_vecMoveSpeed.x = float((CGeneral::GetRandomNumber() & 127) - 64) * 0.0015f + speed.x; + pane->m_vecMoveSpeed.y = float((CGeneral::GetRandomNumber() & 127) - 64) * 0.0015f + speed.y; + pane->m_vecMoveSpeed.z = 0.0f + speed.z; + + if ( moveSpeed != 0.0f ) + { + CVector dist = pane->GetPosition() - center; + dist.Normalise(); + + pane->m_vecMoveSpeed += moveSpeed * dist; + } + + pane->m_vecTurn.x = float((CGeneral::GetRandomNumber() & 127) - 64) * 0.002f; + pane->m_vecTurn.y = float((CGeneral::GetRandomNumber() & 127) - 64) * 0.002f; + pane->m_vecTurn.z = float((CGeneral::GetRandomNumber() & 127) - 64) * 0.002f; + + switch ( type ) + { + case 0: + case 2: + pane->m_nTimer = CTimer::GetTimeInMilliseconds(); + break; + case 1: + float dist = (pane->GetPosition() - center).Magnitude(); + pane->m_nTimer = uint32(dist*100 + CTimer::GetTimeInMilliseconds()); + break; + } + + pane->m_fGroundZ = groundZ; + pane->m_bShattered = cracked; + pane->m_fStep = upLen / float(ysteps); + pane->m_bCarGlass = carGlass; + pane->m_bActive = true; + } + } + } + } +} + +void +CGlass::AskForObjectToBeRenderedInGlass(CEntity *entity) +{ +#ifdef FIX_BUGS + if ( NumGlassEntities < NUM_GLASSENTITIES ) +#else + if ( NumGlassEntities < NUM_GLASSENTITIES-1 ) +#endif + { + apEntitiesToBeRendered[NumGlassEntities++] = entity; + } +} + +void +CGlass::RenderEntityInGlass(CEntity *entity) +{ + ASSERT(entity!=nil); + CObject *object = (CObject *)entity; + + if ( object->bGlassBroken ) + return; + + float distToCamera = (TheCamera.GetPosition() - object->GetPosition()).Magnitude(); + + if ( distToCamera > 40.0f ) + return; + + CVector fwdNorm = object->GetForward(); + fwdNorm.Normalise(); + uint8 alpha = CalcAlphaWithNormal(&fwdNorm); + + CColModel *col = object->GetColModel(); + ASSERT(col!=nil); + if ( col->numTriangles >= 2 ) + { + CVector a = object->GetMatrix() * col->vertices[0].Get(); + CVector b = object->GetMatrix() * col->vertices[1].Get(); + CVector c = object->GetMatrix() * col->vertices[2].Get(); + CVector d = object->GetMatrix() * col->vertices[3].Get(); + + if ( object->bGlassCracked ) + { + uint8 color = 255; + if ( distToCamera > 30.0f ) + color = int32((1.0f - (distToCamera - 30.0f) * 4.0f / 40.0f) * 255); + + if ( TempBufferIndicesStoredShattered >= TEMPBUFFERINDEXSHATTEREDSIZE-13 || TempBufferVerticesStoredShattered >= TEMPBUFFERVERTSHATTEREDSIZE-5 ) + RenderShatteredPolys(); + + RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 0], color, color, color, color); + RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 1], color, color, color, color); + RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 2], color, color, color, color); + RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 3], color, color, color, color); + + RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 0], 0.0f); + RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 0], 0.0f); + RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 1], 16.0f); + RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 1], 0.0f); + RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 2], 0.0f); + RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 2], 16.0f); + RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 3], 16.0f); + RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 3], 16.0f); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 0], a.x, a.y, a.z); + RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 1], b.x, b.y, b.z); + RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 2], c.x, c.y, c.z); + RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 3], d.x, d.y, d.z); + + TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 0] = col->triangles[0].a + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 1] = col->triangles[0].b + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 2] = col->triangles[0].c + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 3] = col->triangles[1].a + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 4] = col->triangles[1].b + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 5] = col->triangles[1].c + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 6] = col->triangles[0].a + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 7] = col->triangles[0].c + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 8] = col->triangles[0].b + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 9] = col->triangles[1].a + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 10] = col->triangles[1].c + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 11] = col->triangles[1].b + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; + + TempBufferIndicesStoredShattered += 12; + TempBufferVerticesStoredShattered += 4; + } + + if ( TempBufferIndicesStoredReflection >= TEMPBUFFERINDEXREFLECTIONSIZE-13 || TempBufferVerticesStoredReflection >= TEMPBUFFERVERTREFLECTIONSIZE-5 ) + RenderReflectionPolys(); + + uint8 color = 100; + if ( distToCamera > 30.0f ) + color = int32((1.0f - (distToCamera - 30.0f) * 4.0f / 40.0f) * 100); + + RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 0], color, color, color, color); + RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 1], color, color, color, color); + RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 2], color, color, color, color); + RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 3], color, color, color, color); + + float FwdAngle = CGeneral::GetATanOfXY(TheCamera.GetForward().x, TheCamera.GetForward().y); + float v = 2.0f * TheCamera.GetForward().z * 0.2f; + float u = float(object->m_randomSeed & 15) * 0.02f + (FwdAngle / TWOPI); + + RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 0], u); + RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 0], v); + RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 1], u+0.2f); + RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 1], v); + RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 2], u); + RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 2], v+0.2f); + RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 3], u+0.2f); + RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 3], v+0.2f); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 0], a.x, a.y, a.z); + RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 1], b.x, b.y, b.z); + RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 2], c.x, c.y, c.z); + RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 3], d.x, d.y, d.z); + + TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 0] = col->triangles[0].a + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 1] = col->triangles[0].b + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 2] = col->triangles[0].c + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 3] = col->triangles[1].a + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 4] = col->triangles[1].b + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 5] = col->triangles[1].c + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 6] = col->triangles[0].a + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 7] = col->triangles[0].c + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 8] = col->triangles[0].b + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 9] = col->triangles[1].a + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 10] = col->triangles[1].c + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; + TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 11] = col->triangles[1].b + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; + + TempBufferIndicesStoredReflection += 12; + TempBufferVerticesStoredReflection += 4; + } +} + +int32 +CGlass::CalcAlphaWithNormal(CVector *normal) +{ + ASSERT(normal!=nil); + + float fwdDir = 2.0f * DotProduct(*normal, TheCamera.GetForward()); + float fwdDot = DotProduct(TheCamera.GetForward()-fwdDir*(*normal), CVector(0.57f, 0.57f, -0.57f)); + return int32(Lerp(fwdDot*fwdDot*fwdDot*fwdDot*fwdDot*fwdDot, 20.0f, 255.0f)); +} + +void +CGlass::RenderHiLightPolys(void) +{ + if ( TempBufferVerticesStoredHiLight != TEMPBUFFERVERTHILIGHTOFFSET ) + { + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)RwTextureGetRaster(gpShadowExplosionTex)); + + LittleTest(); + + if ( RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStoredHiLight, nil, rwIM3D_VERTEXUV) ) + { + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStoredHiLight); + RwIm3DEnd(); + } + + TempBufferVerticesStoredHiLight = TEMPBUFFERVERTHILIGHTOFFSET; + TempBufferIndicesStoredHiLight = TEMPBUFFERINDEXHILIGHTOFFSET; + } +} + +void +CGlass::RenderShatteredPolys(void) +{ + if ( TempBufferVerticesStoredShattered != TEMPBUFFERVERTSHATTEREDOFFSET ) + { + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)RwTextureGetRaster(gpCrackedGlassTex)); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); + + LittleTest(); + + if ( RwIm3DTransform(&TempBufferRenderVertices[TEMPBUFFERVERTSHATTEREDOFFSET], TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET, nil, rwIM3D_VERTEXUV) ) + { + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, &TempBufferRenderIndexList[TEMPBUFFERINDEXSHATTEREDOFFSET], TempBufferIndicesStoredShattered - TEMPBUFFERINDEXSHATTEREDOFFSET); + RwIm3DEnd(); + } + + TempBufferIndicesStoredShattered = TEMPBUFFERINDEXSHATTEREDOFFSET; + TempBufferVerticesStoredShattered = TEMPBUFFERVERTSHATTEREDOFFSET; + } +} + +void +CGlass::RenderReflectionPolys(void) +{ + if ( TempBufferVerticesStoredReflection != TEMPBUFFERVERTREFLECTIONOFFSET ) + { + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)RwTextureGetRaster(gpShadowHeadLightsTex)); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); + + LittleTest(); + + if ( RwIm3DTransform(&TempBufferRenderVertices[TEMPBUFFERVERTREFLECTIONOFFSET], TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET, nil, rwIM3D_VERTEXUV) ) + { + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, &TempBufferRenderIndexList[TEMPBUFFERINDEXREFLECTIONOFFSET], TempBufferIndicesStoredReflection - TEMPBUFFERINDEXREFLECTIONOFFSET); + RwIm3DEnd(); + } + + TempBufferIndicesStoredReflection = TEMPBUFFERINDEXREFLECTIONOFFSET; + TempBufferVerticesStoredReflection = TEMPBUFFERVERTREFLECTIONOFFSET; + } +} + +void +CGlass::WindowRespondsToCollision(CEntity *entity, float amount, CVector speed, CVector point, bool explosion) +{ + ASSERT(entity!=nil); + + CObject *object = (CObject *)entity; + + if ( object->bGlassBroken ) + return; + + object->bGlassCracked = true; + + CColModel *col = object->GetColModel(); + ASSERT(col!=nil); + + if ( col->numTriangles == 2 ) + { + CVector a = col->vertices[0].Get(); + CVector b = col->vertices[1].Get(); + CVector c = col->vertices[2].Get(); + CVector d = col->vertices[3].Get(); + + float minx = Min(Min(a.x, b.x), Min(c.x, d.x)); + float maxx = Max(Max(a.x, b.x), Max(c.x, d.x)); + float miny = Min(Min(a.y, b.y), Min(c.y, d.y)); + float maxy = Max(Max(a.y, b.y), Max(c.y, d.y)); + float minz = Min(Min(a.z, b.z), Min(c.z, d.z)); + float maxz = Max(Max(a.z, b.z), Max(c.z, d.z)); + + CVector pa = object->GetMatrix() * CVector(minx, miny, minz); + CVector pb = object->GetMatrix() * CVector(maxx, maxy, minz); + + if ( amount > 300.0f ) + { + PlayOneShotScriptObject(SCRIPT_SOUND_GLASS_BREAK_L, object->GetPosition()); + + GeneratePanesForWindow(0, + pa, + CVector(0.0f, 0.0f, maxz-minz), + pb - pa, + speed, point, 0.1f, !!object->bGlassCracked, explosion, 1, false); + } + else + { + PlayOneShotScriptObject(SCRIPT_SOUND_GLASS_BREAK_S, object->GetPosition()); + + GeneratePanesForWindow(1, + pa, + CVector(0.0f, 0.0f, maxz-minz), + pb - pa, + speed, point, 0.1f, !!object->bGlassCracked, explosion, 1, false); + } + } + + object->bGlassBroken = true; + object->bIsVisible = false; + object->bUsesCollision = false; +} + +void +CGlass::WindowRespondsToSoftCollision(CEntity *entity, float amount) +{ + ASSERT(entity!=nil); + + CObject *object = (CObject *)entity; + + if ( entity->bUsesCollision && amount > 50.0f && !object->bGlassCracked ) + { + PlayOneShotScriptObject(SCRIPT_SOUND_GLASS_CRACK, object->GetPosition()); + object->bGlassCracked = true; + } +} + +void +CGlass::WasGlassHitByBullet(CEntity *entity, CVector point) +{ + ASSERT(entity!=nil); + + CObject *object = (CObject *)entity; + + if ( IsGlass(object->GetModelIndex()) ) + { + if ( object->bUsesCollision ) + { + if ( !object->bGlassCracked ) + { + PlayOneShotScriptObject(SCRIPT_SOUND_GLASS_CRACK, object->GetPosition()); + object->bGlassCracked = true; + } + else + { + if ( (CGeneral::GetRandomNumber() & 3) == 2 ) + WindowRespondsToCollision(object, 0.0f, CVector(0.0f, 0.0f, 0.0f), point, false); + } + } + } +} + +void +CGlass::WindowRespondsToExplosion(CEntity *entity, CVector point) +{ + ASSERT(entity!=nil); + + CObject *object = (CObject *)entity; + + if ( object->bUsesCollision ) + { + CVector distToGlass = object->GetPosition() - point; + + float fDistToGlass = distToGlass.Magnitude(); + + if ( fDistToGlass < 10.0f ) + { + distToGlass *= (0.3f / fDistToGlass); // normalise + WindowRespondsToCollision(object, 10000.0f, distToGlass, object->GetPosition(), true); + } + else + { + if ( fDistToGlass < 30.0f ) + object->bGlassCracked = true; + } + } +} + + +void +CGlass::CarWindscreenShatters(CVehicle *vehicle, bool unk) +{ + ASSERT(vehicle!=nil); + + CColModel *col = vehicle->GetColModel(); + ASSERT(col!=nil); + + if ( col->numTriangles < 2 ) + return; + + CColTriangle *tria = nil; + int32 triIndex = -1; + CColTriangle *trib = nil; + + for ( int32 i = 0; i < col->numTriangles; i++ ) + { + CColTriangle *tri = &col->triangles[i]; + if ( tri->surface == SURFACE_GLASS ) + { + if ( tria ) + { + trib = tri; + break; + } + + triIndex = i; + tria = tri; + } + } + + if ( trib == nil ) + return; + + CCollision::CalculateTrianglePlanes(col); + + CColTrianglePlane *triPlanes = col->trianglePlanes; + + if ( triPlanes == nil ) + return; + + CVector planeNormal; + triPlanes[triIndex].GetNormal(planeNormal); + planeNormal = Multiply3x3(vehicle->GetMatrix(), planeNormal); + + CVector vec1 = CrossProduct(vehicle->GetRight(), planeNormal); + vec1.Normalise(); + + CVector vec2 = CrossProduct(planeNormal, vehicle->GetUp()); + vec2.Normalise(); + + CVector v[6]; + float proj1[6]; + float proj2[6]; + + v[0] = col->vertices[tria->a].Get(); + v[1] = col->vertices[tria->b].Get(); + v[2] = col->vertices[tria->c].Get(); + + v[3] = col->vertices[trib->a].Get(); + v[4] = col->vertices[trib->b].Get(); + v[5] = col->vertices[trib->c].Get(); + + v[0] = vehicle->GetMatrix() * v[0]; + v[1] = vehicle->GetMatrix() * v[1]; + v[2] = vehicle->GetMatrix() * v[2]; + v[3] = vehicle->GetMatrix() * v[3]; + v[4] = vehicle->GetMatrix() * v[4]; + v[5] = vehicle->GetMatrix() * v[5]; + + proj1[0] = DotProduct(v[0], vec1); + proj2[0] = DotProduct(v[0], vec2); + proj1[1] = DotProduct(v[1], vec1); + proj2[1] = DotProduct(v[1], vec2); + proj1[2] = DotProduct(v[2], vec1); + proj2[2] = DotProduct(v[2], vec2); + + proj1[3] = DotProduct(v[3], vec1); + proj2[3] = DotProduct(v[3], vec2); + proj1[4] = DotProduct(v[4], vec1); + proj2[4] = DotProduct(v[4], vec2); + proj1[5] = DotProduct(v[5], vec1); + proj2[5] = DotProduct(v[5], vec2); + + int32 originIndex = 0; + float max1 = proj1[0]; + float max2 = proj2[0]; + float origin = proj1[0] + proj2[0]; + + for ( int32 i = 1; i < 6; i++ ) + { + float o = proj1[i] + proj2[i]; + if ( o < origin ) + { + origin = o; + originIndex = i; + } + + if ( proj1[i] > max1 ) + max1 = proj1[i]; + if ( proj2[i] > max2 ) + max2 = proj2[i]; + } + + float bound1 = max1 - proj1[originIndex]; + float bound2 = max2 - proj2[originIndex]; + + PlayOneShotScriptObject(SCRIPT_SOUND_GLASS_BREAK_L, vehicle->GetPosition()); + + CVector center = v[originIndex] + ((0.5f*bound1) * vec1) + ((0.5f*bound2) * vec2); + CVector speed = vehicle->m_vecMoveSpeed; + CVector right = bound2 * vec2; + CVector up = bound1 * vec1; + CVector pos = v[originIndex]; + + GeneratePanesForWindow(2, pos, up, right, speed, center, 0.1f, false, false, 2, true); +} + +bool +CGlass::HasGlassBeenShatteredAtCoors(float x, float y, float z) +{ + CEntity *entity = nil; + float dist = 20.0f; + + int32 nStartX = Max(CWorld::GetSectorIndexX(x - 30.0f), 0); + int32 nStartY = Max(CWorld::GetSectorIndexY(y - 30.0f), 0); + int32 nEndX = Min(CWorld::GetSectorIndexX(x + 30.0f), NUMSECTORS_X-1); + int32 nEndY = Min(CWorld::GetSectorIndexY(y + 30.0f), NUMSECTORS_Y-1); + + CWorld::AdvanceCurrentScanCode(); + + for ( int32 ys = nStartY; ys <= nEndY; ys++ ) + { + for ( int32 xs = nStartX; xs <= nEndX; xs++ ) + { + CSector *sector = CWorld::GetSector(xs, ys); + + ASSERT(sector != nil); + + FindWindowSectorList(sector->m_lists[ENTITYLIST_OBJECTS], &dist, &entity, x, y, z); + FindWindowSectorList(sector->m_lists[ENTITYLIST_DUMMIES], &dist, &entity, x, y, z); + } + } + + if ( entity ) + { + if ( entity->GetType() == ENTITY_TYPE_DUMMY ) + return false; + + return !!((CObject*)entity)->bGlassBroken; + } + + return false; +} + +void +CGlass::FindWindowSectorList(CPtrList &list, float *dist, CEntity **entity, float x, float y, float z) +{ + ASSERT(dist!=nil); + ASSERT(entity!=nil); + + CPtrNode *node = list.first; + + while ( node != nil ) + { + CEntity *ent = (CEntity *)node->item; + uint16 scanCode = ent->m_scanCode; + node = node->next; + + ASSERT(ent!=nil); + + if ( IsGlass(ent->GetModelIndex()) ) + { + if ( scanCode != CWorld::GetCurrentScanCode() ) + { + ent->m_scanCode = CWorld::GetCurrentScanCode(); + + float dst = (CVector(x,y,z) - ent->GetPosition()).Magnitude(); + + if ( dst < *dist ) + { + *dist = dst; + *entity = ent; + } + } + } + } +} + +void +CGlass::BreakGlassPhysically(CVector pos, float radius) +{ + static uint32 breakTime = 0; + + if ( CTimer::GetTimeInMilliseconds() < breakTime + 1000 && CTimer::GetTimeInMilliseconds() >= breakTime ) + return; + + CColSphere sphere; + sphere.piece = 0; + sphere.radius = radius; + sphere.surface = 0; + + for ( int32 i = CPools::GetObjectPool()->GetSize() - 1; i >= 0; i-- ) + { + CObject *object = CPools::GetObjectPool()->GetSlot(i); + if (object) + { + if ( IsGlass(object->GetModelIndex()) ) + { + if ( object->bUsesCollision ) + { + CColModel *col = object->GetColModel(); + ASSERT(col!=nil); + + if ( col->numTriangles < 2 ) + continue; + + bool hit = false; + + CVector dist = pos - object->GetPosition(); + + sphere.center.x = DotProduct(dist, object->GetRight()); + sphere.center.y = DotProduct(dist, object->GetForward()); + sphere.center.z = DotProduct(dist, object->GetUp()); + + CCollision::CalculateTrianglePlanes(col); + + for ( int32 j = 0; j < col->numTriangles; j++ ) + { + if ( CCollision::TestSphereTriangle(sphere, + col->vertices, col->triangles[j], col->trianglePlanes[j]) ) + { + hit = true; + } + } + + if ( hit ) + { + breakTime = CTimer::GetTimeInMilliseconds(); + + if ( object->bGlassCracked ) + { + CVector a = col->vertices[0].Get(); + CVector b = col->vertices[1].Get(); + CVector c = col->vertices[2].Get(); + CVector d = col->vertices[3].Get(); + + float minx = Min(Min(a.x, b.x), Min(c.x, d.x)); + float maxx = Max(Max(a.x, b.x), Max(c.x, d.x)); + float miny = Min(Min(a.y, b.y), Min(c.y, d.y)); + float maxy = Max(Max(a.y, b.y), Max(c.y, d.y)); + float minz = Min(Min(a.z, b.z), Min(c.z, d.z)); + float maxz = Max(Max(a.z, b.z), Max(c.z, d.z)); + + CVector pa = object->GetMatrix() * CVector(minx, miny, minz); + CVector pb = object->GetMatrix() * CVector(maxx, maxy, minz); + + PlayOneShotScriptObject(SCRIPT_SOUND_GLASS_BREAK_S, object->GetPosition()); + + GeneratePanesForWindow(1, + pa, + CVector(0.0f, 0.0f, maxz-minz), + pb - pa, + CVector(0.0f, 0.0f, 0.0f), pos, 0.1f, !!object->bGlassCracked, false, 1, false); + + object->bGlassBroken = true; + object->bIsVisible = false; + object->bUsesCollision = false; + } + else + { + PlayOneShotScriptObject(SCRIPT_SOUND_GLASS_CRACK, object->GetPosition()); + object->bGlassCracked = true; + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/miami/renderer/Glass.h b/src/miami/renderer/Glass.h new file mode 100644 index 00000000..f1c85779 --- /dev/null +++ b/src/miami/renderer/Glass.h @@ -0,0 +1,59 @@ +#pragma once + +class CEntity; +class CVehicle; +class CPtrList; + +class CFallingGlassPane : public CMatrix +{ +public: + CVector m_vecMoveSpeed; + CVector m_vecTurn; + uint32 m_nTimer; + float m_fGroundZ; + float m_fStep; + uint8 m_nTriIndex; + bool m_bActive; + bool m_bShattered; + bool m_bCarGlass; + + CFallingGlassPane() { } + ~CFallingGlassPane() { } + + void Update(void); + void Render(void); +}; + +VALIDATE_SIZE(CFallingGlassPane, 0x70); + +enum +{ + NUM_GLASSTRIANGLES = 5, +}; + +class CGlass +{ + static uint32 NumGlassEntities; + static CEntity *apEntitiesToBeRendered[NUM_GLASSENTITIES]; + static CFallingGlassPane aGlassPanes[NUM_GLASSPANES]; +public: + static void Init(void); + static void Update(void); + static void Render(void); + static CFallingGlassPane *FindFreePane(void); + static void GeneratePanesForWindow(uint32 type, CVector pos, CVector up, CVector right, CVector speed, CVector center, float moveSpeed, bool cracked, bool explosion, int32 stepmul, bool carGlass); + static void AskForObjectToBeRenderedInGlass(CEntity *entity); + static void RenderEntityInGlass(CEntity *entity); + static int32 CalcAlphaWithNormal(CVector *normal); + static void RenderHiLightPolys(void); + static void RenderShatteredPolys(void); + static void RenderReflectionPolys(void); + static void WindowRespondsToCollision(CEntity *entity, float amount, CVector speed, CVector point, bool explosion); + static void WindowRespondsToSoftCollision(CEntity *entity, float amount); + static void WasGlassHitByBullet(CEntity *entity, CVector point); + static void WindowRespondsToExplosion(CEntity *entity, CVector point); + static void CarWindscreenShatters(CVehicle *vehicle, bool unk); + static bool HasGlassBeenShatteredAtCoors(float x, float y, float z); + static void FindWindowSectorList(CPtrList &list, float *dist, CEntity **entity, float x, float y, float z); + static void BreakGlassPhysically(CVector pos, float radius); +}; \ No newline at end of file diff --git a/src/miami/renderer/Hud.cpp b/src/miami/renderer/Hud.cpp new file mode 100644 index 00000000..45ea3278 --- /dev/null +++ b/src/miami/renderer/Hud.cpp @@ -0,0 +1,2091 @@ +#include "common.h" + +#include "Camera.h" +#include "DMAudio.h" +#include "Clock.h" +#include "Darkel.h" +#include "Hud.h" +#include "Messages.h" +#include "Frontend.h" +#include "Font.h" +#include "Pad.h" +#include "Radar.h" +#include "Replay.h" +#include "Wanted.h" +#include "Sprite.h" +#include "Sprite2d.h" +#include "Text.h" +#include "Timer.h" +#include "Script.h" +#include "TxdStore.h" +#include "User.h" +#include "World.h" +#include "CutsceneMgr.h" +#include "Stats.h" +#include "main.h" +#include "General.h" +#include "VarConsole.h" + +#if defined(FIX_BUGS) + #define SCREEN_SCALE_X_FIX(a) SCREEN_SCALE_X(a) + #define SCREEN_SCALE_Y_FIX(a) SCREEN_SCALE_Y(a) + #define SCALE_AND_CENTER_X_FIX(a) SCALE_AND_CENTER_X(a) +#else + #define SCREEN_SCALE_X_FIX(a) (a) + #define SCREEN_SCALE_Y_FIX(a) (a) + #define SCALE_AND_CENTER_X_FIX(a) (a) +#endif + +#ifdef FIX_BUGS +#define FRAMECOUNTER CTimer::GetLogicalFrameCounter() +#else +#define FRAMECOUNTER CTimer::GetFrameCounter() +#endif + +// Game has colors inlined in code. +// For easier modification we collect them here: +CRGBA MONEY_COLOR(0, 207, 133, 255); +CRGBA AMMO_COLOR(255, 150, 225, 255); +CRGBA HEALTH_COLOR(255, 150, 225, 255); +CRGBA ARMOUR_COLOR(185, 185, 185, 255); +CRGBA NOTWANTED_COLOR(27, 89, 130, 255); +CRGBA WANTED_COLOR_FLASH(62, 141, 181, 255); +CRGBA WANTED_COLOR(97, 194, 247, 255); +CRGBA ZONE_COLOR(45, 155, 90, 255); +CRGBA VEHICLE_COLOR(97, 194, 247, 255); +CRGBA CLOCK_COLOR(97, 194, 247, 255); +CRGBA TIMER_COLOR(97, 194, 247, 255); +CRGBA COUNTER_COLOR(97, 194, 247, 255); +CRGBA PAGER_COLOR(32, 162, 66, 205); +CRGBA RADARDISC_COLOR(255, 255, 255, 255); +CRGBA BIGMESSAGE_COLOR(255, 150, 225, 255); +CRGBA WASTEDBUSTED_COLOR(0, 207, 133, 255); +CRGBA ODDJOB_COLOR(0, 207, 133, 255); +CRGBA ODDJOB2_COLOR(97, 194, 247, 255); +CRGBA MISSIONTITLE_COLOR(220, 172, 2, 255); + +wchar CHud::m_HelpMessage[HELP_MSG_LENGTH]; +wchar CHud::m_LastHelpMessage[HELP_MSG_LENGTH]; +uint32 CHud::m_HelpMessageState; +uint32 CHud::m_HelpMessageTimer; +int32 CHud::m_HelpMessageFadeTimer; +wchar CHud::m_HelpMessageToPrint[HELP_MSG_LENGTH]; +float CHud::m_HelpMessageDisplayTime; +bool CHud::m_HelpMessageDisplayForever; +bool CHud::m_HelpMessageQuick; +uint32 CHud::m_ZoneState; +int32 CHud::m_ZoneFadeTimer; +uint32 CHud::m_ZoneNameTimer; +wchar *CHud::m_pZoneName; +wchar *CHud::m_pLastZoneName; +wchar *CHud::m_ZoneToPrint; +uint32 CHud::m_VehicleState; +int32 CHud::m_VehicleFadeTimer; +uint32 CHud::m_VehicleNameTimer; +wchar *CHud::m_VehicleName; +wchar *CHud::m_pLastVehicleName; +wchar *CHud::m_pVehicleNameToPrint; +wchar CHud::m_Message[256]; +wchar CHud::m_PagerMessage[256]; +bool CHud::m_Wants_To_Draw_Hud; +bool CHud::m_Wants_To_Draw_3dMarkers; +wchar CHud::m_BigMessage[6][128]; +int16 CHud::m_ItemToFlash; +bool CHud::m_HideRadar; +int32 CHud::m_ClockState; + +// These aren't really in CHud +float CHud::BigMessageInUse[6]; +float CHud::BigMessageAlpha[6]; +float CHud::BigMessageX[6]; +float CHud::OddJob2OffTimer; +bool CHud::CounterOnLastFrame[NUMONSCREENCOUNTERS]; +float CHud::OddJob2XOffset; +uint16 CHud::CounterFlashTimer[NUMONSCREENCOUNTERS]; +uint16 CHud::OddJob2Timer; +bool CHud::TimerOnLastFrame; +int16 CHud::OddJob2On; +uint16 CHud::TimerFlashTimer; +int16 CHud::PagerSoundPlayed; +int32 CHud::SpriteBrightness; +float CHud::PagerXOffset; +int16 CHud::PagerTimer; +int16 CHud::PagerOn; + +wchar *prevChaseString; + +uint32 CHud::m_WantedFadeTimer; +uint32 CHud::m_WantedState; +uint32 CHud::m_WantedTimer; +uint32 CHud::m_EnergyLostFadeTimer; +uint32 CHud::m_EnergyLostState; +uint32 CHud::m_EnergyLostTimer; +uint32 CHud::m_DisplayScoreFadeTimer; +uint32 CHud::m_DisplayScoreState; +uint32 CHud::m_DisplayScoreTimer; +uint32 CHud::m_WeaponFadeTimer; +uint32 CHud::m_WeaponState; +uint32 CHud::m_WeaponTimer; + +uint32 CHud::m_LastDisplayScore; +uint32 CHud::m_LastWanted; +uint32 CHud::m_LastWeapon; +uint32 CHud::m_LastTimeEnergyLost; + +CSprite2d CHud::Sprites[NUM_HUD_SPRITES]; + +struct +{ + const char *name; + const char *mask; +} WeaponFilenames[] = { + { "fist", "fistm" }, + { "brassk", "brasskA" }, + { "screw", "screwA" }, + { "golf", "golfA" }, + { "nightstick", "nightstickA" }, + { "knife", "knifeA" }, + { "bat", "batm" }, + { "hammer", "hammerA" }, + { "cleaver", "cleaverA" }, + { "machete", "macheteA" }, + { "sword", "swordA" }, + { "chainsaw", "chainsawA" }, + { "grenade", "grenadeA" }, + { "grenade", "grenadeA" }, + { "teargas", "teargasA" }, + { "molotov", "molotovA" }, + { "rocket", "rocketA" }, + { "handGun1", "handGun1A" }, + { "", "" }, + { "python", "pythonA" }, + { "chromegun", "chromegunA" }, + { "spasshotGun", "spasshotGunA" }, + { "stubshotGun", "stubshotGunA" }, + { "tec9", "tec9A" }, + { "uzi1", "uzi1A" }, + { "uzi2", "uzi2A" }, + { "mp5", "mp5A" }, + { "", "" }, + { "m4", "m4A" }, + { "ruger", "rugerA" }, + { "sniper", "sniperA" }, + { "laserscope", "laserscopeA" }, + { "", "" }, + { "rocket", "rocketA" }, + { "flamer", "flamerA" }, + { "m60", "m60A" }, + { "minigun", "minigunA" }, + { "bomb", "bombA" }, + { "", "" }, + { "camera", "cameraA" }, + { "", "" }, + { "siterocket", "siterocket" }, + { "", "" }, + { "", "" }, + { "", "" }, + { "", "" }, + { "", "" }, + { "", "" }, + { "", "" }, + { "", "" }, + { "radardisc", "radardisc" }, + { "", "" }, + { "", "" }, + { "", "" }, + { "", "" }, + { "", "" }, + { "", "" }, + { "", "" }, + { "", "" }, + { "", "" }, + { "", "" }, + { "", "" }, + { "", "" }, + { "sitesniper", "sitesniperm" }, + { "siteM16", "siteM16m" }, + { "sitelaser", "sitelaserm" }, + { "laserdot", "laserdotm" }, + { "viewfinder_128", "viewfinder_128m" }, + { "bleeder", "" } +}; + +RwTexture *gpSniperSightTex; +RwTexture *gpRocketSightTex; +RwTexture *gpLaserSightTex; +RwTexture *gpLaserDotTex; +RwTexture *gpViewFinderTex; + +void CHud::Draw() +{ + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST); + RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + + // disable hud via second controller + if (CPad::GetPad(1)->GetStartJustDown()) + m_Wants_To_Draw_Hud = !m_Wants_To_Draw_Hud; + + if (CReplay::IsPlayingBack()) + return; + + if (m_Wants_To_Draw_Hud && !TheCamera.m_WideScreenOn) { + // unused statics in here + bool DrawCrossHair = false; + bool CrossHairHidesHud = false; + bool DrawCrossHairPC = false; + + CPlayerPed *playerPed = FindPlayerPed(); + eWeaponType WeaponType = playerPed->GetWeapon()->m_eWeaponType; + int32 Mode = TheCamera.Cams[TheCamera.ActiveCam].Mode; + + if ((Mode == CCam::MODE_SNIPER || Mode == CCam::MODE_ROCKETLAUNCHER || Mode == CCam::MODE_M16_1STPERSON || Mode == CCam::MODE_HELICANNON_1STPERSON || Mode == CCam::MODE_CAMERA) + && playerPed && !playerPed->GetWeapon()->IsTypeMelee()) + DrawCrossHair = true; + + if (Mode == CCam::MODE_M16_1STPERSON_RUNABOUT || Mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || Mode == CCam::MODE_SNIPER_RUNABOUT) + DrawCrossHairPC = true; + if (TheCamera.Cams[TheCamera.ActiveCam].Using3rdPersonMouseCam() && (!CPad::GetPad(0)->GetLookBehindForPed() || TheCamera.m_bPlayerIsInGarage) + || Mode == CCam::MODE_1STPERSON_RUNABOUT) { + if (playerPed) { + if (playerPed->m_nPedState != PED_ENTER_CAR && playerPed->m_nPedState != PED_CARJACK) { + + if (WeaponType >= WEAPONTYPE_COLT45 && WeaponType <= WEAPONTYPE_RUGER + || WeaponType == WEAPONTYPE_M60 || WeaponType == WEAPONTYPE_MINIGUN + || WeaponType == WEAPONTYPE_FLAMETHROWER) { + DrawCrossHairPC = 1; + } + } + } + } + + if (DrawCrossHair || DrawCrossHairPC) { + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void *)rwFILTERLINEAR); + + SpriteBrightness = Min(SpriteBrightness+1, 30); + + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + + float fStep = Sin((CTimer::GetTimeInMilliseconds() & 1023)/1024.0f * 6.28f); + float fMultBright = SpriteBrightness * 0.03f * (0.25f * fStep + 0.75f); + CRect rect; + if (DrawCrossHairPC && TheCamera.Cams[TheCamera.ActiveCam].Using3rdPersonMouseCam()) { + float f3rdX = SCREEN_WIDTH * TheCamera.m_f3rdPersonCHairMultX; + float f3rdY = SCREEN_HEIGHT * TheCamera.m_f3rdPersonCHairMultY; +#ifdef ASPECT_RATIO_SCALE + f3rdY -= SCREEN_SCALE_Y(2.0f); +#endif + if (playerPed && (WeaponType == WEAPONTYPE_M4 || WeaponType == WEAPONTYPE_RUGER || WeaponType == WEAPONTYPE_M60)) { + rect.left = f3rdX - SCREEN_SCALE_X(32.0f * 0.6f); + rect.top = f3rdY - SCREEN_SCALE_Y(32.0f * 0.6f); + rect.right = f3rdX + SCREEN_SCALE_X(32.0f * 0.6f); + rect.bottom = f3rdY + SCREEN_SCALE_Y(32.0f * 0.6f); + + Sprites[HUD_SITEM16].Draw(CRect(rect), CRGBA(255, 255, 255, 255), + 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f); + } + else { + rect.left = f3rdX - SCREEN_SCALE_X(32.0f * 0.4f); + rect.top = f3rdY - SCREEN_SCALE_Y(32.0f * 0.4f); + rect.right = f3rdX + SCREEN_SCALE_X(32.0f * 0.4f); + rect.bottom = f3rdY + SCREEN_SCALE_Y(32.0f * 0.4f); + + Sprites[HUD_SITEM16].Draw(CRect(rect), CRGBA(255, 255, 255, 255), + 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f); + } + } else { + if (Mode == CCam::MODE_M16_1STPERSON || + Mode == CCam::MODE_M16_1STPERSON_RUNABOUT || + Mode == CCam::MODE_HELICANNON_1STPERSON) { + rect.left = (SCREEN_WIDTH / 2) - SCREEN_SCALE_X(32.0f); + rect.top = (SCREEN_HEIGHT / 2) - SCREEN_SCALE_Y(32.0f); + rect.right = (SCREEN_WIDTH / 2) + SCREEN_SCALE_X(32.0f); + rect.bottom = (SCREEN_HEIGHT / 2) + SCREEN_SCALE_Y(32.0f); + Sprites[HUD_SITEM16].Draw(CRect(rect), CRGBA(255, 255, 255, 255), + 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f); + } + else if (Mode == CCam::MODE_1STPERSON_RUNABOUT) { + rect.left = (SCREEN_WIDTH / 2) - SCREEN_SCALE_X(32.0f * 0.7f); + rect.top = (SCREEN_HEIGHT / 2) - SCREEN_SCALE_Y(32.0f * 0.7f); + rect.right = (SCREEN_WIDTH / 2) + SCREEN_SCALE_X(32.0f * 0.7f); + rect.bottom = (SCREEN_HEIGHT / 2) + SCREEN_SCALE_Y(32.0f * 0.7f); + + Sprites[HUD_SITEM16].Draw(CRect(rect), CRGBA(255, 255, 255, 255), + 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f); + } + else if (Mode == CCam::MODE_ROCKETLAUNCHER || Mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT) { + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpRocketSightTex)); + + CSprite::RenderOneXLUSprite(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 1.0f, SCREEN_SCALE_X(40.0f), SCREEN_SCALE_Y(40.0f), (100.0f * fMultBright), (200.0f * fMultBright), (100.0f * fMultBright), 255, 1.0f, 255); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + } + else { + int sprite = HUD_SITESNIPER; + float xOffset = SCREEN_SCALE_X(210.0f); + float yOffset = SCREEN_SCALE_Y(210.0f); + + if (FindPlayerPed()->GetWeapon()->m_eWeaponType == WEAPONTYPE_LASERSCOPE) + sprite = HUD_SITELASER; + + if (FindPlayerPed()->GetWeapon()->m_eWeaponType == WEAPONTYPE_CAMERA) { + sprite = HUD_VIEWFINDER; + CrossHairHidesHud = true; + xOffset = SCREEN_SCALE_X(256.0f); + yOffset = SCREEN_SCALE_Y(192.0f); + } + + rect.left = SCREEN_WIDTH/2 - xOffset; + rect.top = SCREEN_HEIGHT/2 - yOffset; + rect.right = SCREEN_WIDTH/2; + rect.bottom = SCREEN_HEIGHT/2; + Sprites[sprite].Draw(CRect(rect), CRGBA(255, 255, 255, 255), + 0.01f, 0.01f, 1.0f, 0.0f, 0.01f, 1.0f, 1.0f, 1.0f); + + rect.left = SCREEN_WIDTH/2; + rect.top = SCREEN_HEIGHT/2 - yOffset; + rect.right = SCREEN_WIDTH/2 + xOffset; + rect.bottom = SCREEN_HEIGHT/2; + Sprites[sprite].Draw(CRect(rect), CRGBA(255, 255, 255, 255), + 0.99f, 0.0f, 0.01f, 0.01f, 0.99f, 1.0f, 0.01f, 1.0f); + + rect.left = SCREEN_WIDTH/2 - xOffset; + rect.top = SCREEN_HEIGHT/2; + rect.right = SCREEN_WIDTH/2; + rect.bottom = SCREEN_HEIGHT/2 + yOffset; + Sprites[sprite].Draw(CRect(rect), CRGBA(255, 255, 255, 255), + 0.01f, 0.99f, 1.0f, 0.99f, 0.01f, 0.01f, 1.0f, 0.01f); + + rect.left = SCREEN_WIDTH/2; + rect.top = SCREEN_HEIGHT/2; + rect.right = SCREEN_WIDTH/2 + xOffset; + rect.bottom = SCREEN_HEIGHT/2 + yOffset; + Sprites[sprite].Draw(CRect(rect), CRGBA(255, 255, 255, 255), + 0.99f, 0.99f, 0.01f, 0.99f, 0.99f, 0.01f, 0.01f, 0.01f); + + CVector dotPos; + float size = 25.0f; + if (FindPlayerPed()->GetWeapon()->m_eWeaponType == WEAPONTYPE_LASERSCOPE && FindPlayerPed()->GetWeapon()->LaserScopeDot(&dotPos, &size)) { + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); +#ifdef FIX_BUGS + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); +#else + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVDESTALPHA); +#endif + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpLaserDotTex)); +#ifdef FIX_BUGS + int intensity = CGeneral::GetRandomNumberInRange(0, 37); +#else + int intensity = CGeneral::GetRandomNumberInRange(0, 35); +#endif + CSprite::RenderOneXLUSprite(dotPos.x, dotPos.y, dotPos.z, + SCREEN_SCALE_X(size), SCREEN_SCALE_Y(size), intensity - 36, 0, 0, intensity - 36, 1.0f, 127); + + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + } + } + } + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + } + else { + SpriteBrightness = 0; + } + + if (CrossHairHidesHud) + return; + + /* + DrawMoneyCounter + */ + + wchar sPrint[16]; + wchar sPrintIcon[16]; + char sTemp[16]; + float alpha; + + if (m_LastDisplayScore == CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney) { + alpha = DrawFadeState(HUD_SCORE_FADING, 0); + } else { + alpha = DrawFadeState(HUD_SCORE_FADING, 1); + m_LastDisplayScore = CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney; + } + if (m_DisplayScoreState != FADED_OUT) { + sprintf(sTemp, "$%08d", CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney); + AsciiToUnicode(sTemp, sPrint); + + CFont::SetPropOff(); + CFont::SetBackgroundOff(); + CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); + CFont::SetCentreOff(); + CFont::SetRightJustifyOn(); + CFont::SetRightJustifyWrap(0.0f); + CFont::SetBackGroundOnlyTextOff(); + CFont::SetFontStyle(FONT_HEADING); + CFont::SetPropOff(); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, alpha)); + MONEY_COLOR.a = alpha; + CFont::SetColor(MONEY_COLOR); + + if (FrontEndMenuManager.m_PrefsShowHud) { + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f), SCREEN_SCALE_Y(43.0f), sPrint); + } + } + + /* + DrawAmmo + */ + if (m_LastWeapon == playerPed->GetWeapon()->m_eWeaponType) { + alpha = CHud::DrawFadeState(HUD_WEAPON_FADING, 0); + } else { + alpha = CHud::DrawFadeState(HUD_WEAPON_FADING, 1); + m_LastWeapon = playerPed->GetWeapon()->m_eWeaponType; + } + if (m_WeaponState != FADED_OUT) { + CWeapon *weapon = playerPed->GetWeapon(); + int32 AmmoAmount = CWeaponInfo::GetWeaponInfo((eWeaponType)WeaponType)->m_nAmountofAmmunition; + int32 AmmoInClip = weapon->m_nAmmoInClip; + int32 TotalAmmo = weapon->m_nAmmoTotal; + int32 Ammo, Clip; + + if (AmmoAmount <= 1 || AmmoAmount >= 1000) + sprintf(sTemp, "%d", TotalAmmo); + else { + if (WeaponType == WEAPONTYPE_FLAMETHROWER) { + Clip = AmmoInClip / 10; + + Ammo = Min((TotalAmmo - AmmoInClip) / 10, 9999); + } else { + Clip = AmmoInClip; + + Ammo = Min(TotalAmmo - AmmoInClip, 9999); + } + + sprintf(sTemp, "%d-%d", Ammo, Clip); + } + + AsciiToUnicode(sTemp, sPrint); + CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo((eWeaponType)WeaponType); + /* + DrawWeaponIcon + */ + + if (FrontEndMenuManager.m_PrefsShowHud) { + if (weaponInfo->m_nModelId <= 0) { + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); + if (FrontEndMenuManager.m_PrefsShowHud) + Sprites[WeaponType].Draw( + CRect(SCREEN_SCALE_FROM_RIGHT(99.0f), SCREEN_SCALE_Y(27.0f), SCREEN_SCALE_FROM_RIGHT(35.0f), SCREEN_SCALE_Y(91.0f)), + CRGBA(255, 255, 255, alpha), + 0.015f, 0.015f, + 1.0f, 0.0f, + 0.015f, 1.0f, + 1.0f, 1.0f); + } else { + CBaseModelInfo *weaponModel = CModelInfo::GetModelInfo(weaponInfo->m_nModelId); + RwTexDictionary *weaponTxd = CTxdStore::GetSlot(weaponModel->GetTxdSlot())->texDict; + if (weaponTxd) { + RwTexture *weaponIcon = RwTexDictionaryFindNamedTexture(weaponTxd, weaponModel->GetModelName()); + if (weaponIcon) { + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); +#ifndef FIX_BUGS + const float xSize = SCREEN_SCALE_X(64.0f / 2.0f); + const float ySize = SCREEN_SCALE_Y(64.0f / 2.0f); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(weaponIcon)); + CSprite::RenderOneXLUSprite(SCREEN_SCALE_FROM_RIGHT(99.0f) + xSize, SCREEN_SCALE_Y(25.0f) + ySize, 1.0f, xSize, ySize, + 255, 255, 255, 255, 1.0f, 255); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); +#else + static CSprite2d sprite; + sprite.m_pTexture = weaponIcon; + sprite.Draw( + CRect(SCREEN_SCALE_FROM_RIGHT(99.0f), SCREEN_SCALE_Y(27.0f), SCREEN_SCALE_FROM_RIGHT(35.0f), SCREEN_SCALE_Y(91.0f)), + CRGBA(255, 255, 255, alpha), + 0.015f, 0.015f, + 1.0f, 0.0f, + 0.015f, 1.0f, + 1.0f, 1.0f); + sprite.m_pTexture = nil; +#endif + } + } + } + + CFont::SetBackgroundOff(); + CFont::SetScale(SCREEN_SCALE_X(0.5f), SCREEN_SCALE_Y(0.8f)); + CFont::SetJustifyOff(); + CFont::SetCentreOn(); + CFont::SetCentreSize(SCREEN_STRETCH_X(DEFAULT_SCREEN_WIDTH)); + CFont::SetPropOn(); + CFont::SetDropShadowPosition(0); + CFont::SetFontStyle(FONT_STANDARD); + + if (Min(9999, TotalAmmo - AmmoInClip) != 9999 && !CDarkel::FrenzyOnGoing() && weaponInfo->m_nWeaponSlot > 1 && weapon->m_eWeaponType != WEAPONTYPE_DETONATOR) { + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, alpha)); + AMMO_COLOR.a = alpha; + CFont::SetColor(AMMO_COLOR); + if (FrontEndMenuManager.m_PrefsShowHud) + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(66.0f), SCREEN_SCALE_Y(90.0f), sPrint); + CFont::SetDropShadowPosition(0); + } + } + } + + /* + DrawHealth + */ + if ( m_LastTimeEnergyLost == CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss ) { + CHud::DrawFadeState(HUD_ENERGY_FADING, 0); + } else { + CHud::DrawFadeState(HUD_ENERGY_FADING, 1); + m_LastTimeEnergyLost = CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss; + } + + if (m_EnergyLostState != FADED_OUT) { + CFont::SetBackgroundOff(); + CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); + CFont::SetJustifyOff(); + CFont::SetCentreOff(); + CFont::SetRightJustifyWrap(0.0f); + CFont::SetRightJustifyOn(); + CFont::SetPropOff(); + CFont::SetFontStyle(FONT_HEADING); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + + if (m_ItemToFlash == ITEM_HEALTH && FRAMECOUNTER & 8 + || m_ItemToFlash != ITEM_HEALTH + || playerPed->m_fHealth < 10 + && FRAMECOUNTER & 8) { + if (playerPed->m_fHealth >= 10 + || playerPed->m_fHealth < 10 && FRAMECOUNTER & 8) { + + AsciiToUnicode("{", sPrintIcon); +#ifdef FIX_BUGS + sprintf(sTemp, "%03d", int32(playerPed->m_fHealth + 0.5f)); +#else + sprintf(sTemp, "%03d", (int32)playerPed->m_fHealth); +#endif + AsciiToUnicode(sTemp, sPrint); + + CFont::SetColor(HEALTH_COLOR); + if (FrontEndMenuManager.m_PrefsShowHud) { + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f), SCREEN_SCALE_Y(65.0f), sPrint); + + if (!CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss || CTimer::GetTimeInMilliseconds() > CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss + 2000 || FRAMECOUNTER & 4) { + // CFont::SetColor(HEALTH_COLOR); + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f + 54.0f), SCREEN_SCALE_Y(65.0f), sPrintIcon); + } + } + } + } + + /* + DrawArmour + */ + if (m_ItemToFlash == ITEM_ARMOUR && FRAMECOUNTER & 8 || m_ItemToFlash != ITEM_ARMOUR) { + CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); + if (playerPed->m_fArmour > 1.0f) { + AsciiToUnicode("<", sPrintIcon); +#ifdef FIX_BUGS + sprintf(sTemp, "%03d", int32(playerPed->m_fArmour + 0.5f)); +#else + sprintf(sTemp, "%03d", (int32)playerPed->m_fArmour); +#endif + AsciiToUnicode(sTemp, sPrint); + + CFont::SetColor(ARMOUR_COLOR); + if (FrontEndMenuManager.m_PrefsShowHud) { + + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(182.0f), SCREEN_SCALE_Y(65.0f), sPrint); + + if (!CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastArmourLoss || CTimer::GetTimeInMilliseconds() > CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastArmourLoss + 2000 || FRAMECOUNTER & 4) { + // CFont::SetColor(ARMOUR_COLOR); + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(182.0f + 52.0f), SCREEN_SCALE_Y(65.0f), sPrintIcon); + } + } + } + } + } + + /* + DrawWantedLevel + */ + if (m_LastWanted == playerPed->m_pWanted->GetWantedLevel()) { + alpha = DrawFadeState(HUD_WANTED_FADING, 0); + } else { + alpha = DrawFadeState(HUD_WANTED_FADING, 1); + m_LastWanted = playerPed->m_pWanted->GetWantedLevel(); + } + + if (m_WantedState != FADED_OUT) { + CFont::SetBackgroundOff(); + CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); + CFont::SetJustifyOff(); + CFont::SetCentreOff(); + CFont::SetRightJustifyOn(); + CFont::SetPropOn(); + CFont::SetFontStyle(FONT_STANDARD); + + AsciiToUnicode(">", sPrintIcon); + + for (int i = 0; i < 6; i++) { + if (FrontEndMenuManager.m_PrefsShowHud) { + if (playerPed->m_pWanted->GetWantedLevel() > i + && (CTimer::GetTimeInMilliseconds() > playerPed->m_pWanted->m_nLastWantedLevelChange + + 2000 || FRAMECOUNTER & 4)) { + + WANTED_COLOR.a = alpha; + CFont::SetColor(WANTED_COLOR); + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f + 23.0f * i), SCREEN_SCALE_Y(87.0f), sPrintIcon); + + } else if (playerPed->m_pWanted->m_nMinWantedLevel > i && FRAMECOUNTER & 4) { + WANTED_COLOR_FLASH.a = alpha; + CFont::SetColor(WANTED_COLOR_FLASH); + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f + 23.0f * i), SCREEN_SCALE_Y(87.0f), sPrintIcon); + + } else if (playerPed->m_pWanted->GetWantedLevel() <= i) { + NOTWANTED_COLOR.a = alpha; + CFont::SetColor(NOTWANTED_COLOR); + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f + 23.0f * i), SCREEN_SCALE_Y(87.0f), sPrintIcon); + } + } + } + } + + static int32 nMediaLevelCounter = 0; + if (CStats::ShowChaseStatOnScreen != 0) { + float fCurAttentionLevel = CWorld::Players[CWorld::PlayerInFocus].m_fMediaAttention; + if (0.7f * CStats::HighestChaseValue > fCurAttentionLevel + || fCurAttentionLevel <= 40.0f || CTheScripts::IsPlayerOnAMission()) { + nMediaLevelCounter = 0; + } + else { + if (fCurAttentionLevel == CStats::HighestChaseValue) { + sprintf(gString, "%s %d", UnicodeToAscii(TheText.Get("CHSE")), (int32)fCurAttentionLevel); + } + else { + sprintf(gString, "%s %d" "-%d-", UnicodeToAscii(TheText.Get("CHSE")), (int32)fCurAttentionLevel, (int32)CStats::HighestChaseValue); + } + AsciiToUnicode(gString, gUString); + CFont::SetBackgroundOff(); + CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); + CFont::SetCentreOff(); + CFont::SetRightJustifyOn(); + CFont::SetRightJustifyWrap(0.0f); + CFont::SetBackGroundOnlyTextOff(); + CFont::SetFontStyle(FONT_HEADING); + CFont::SetPropOff(); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + + CRGBA colour; + if (CTimer::GetTimeInMilliseconds() & 0x200) + colour = CRGBA(204, 0, 185, 180); + else + colour = CRGBA(178, 0, 162, 180); + CFont::SetColor(colour); + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f), SCREEN_SCALE_Y(113.0f), gUString); + + if (CStats::FindChaseString(fCurAttentionLevel) != prevChaseString) { + prevChaseString = CStats::FindChaseString(fCurAttentionLevel); + nMediaLevelCounter = 100; + } + + if (nMediaLevelCounter != 0) { + nMediaLevelCounter--; + UnicodeMakeUpperCase(gUString, CStats::FindChaseString(fCurAttentionLevel)); + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f), SCREEN_SCALE_Y(138.0f), gUString); + } + } + } + + /* + DrawZoneName + */ + if (m_pZoneName) { + + if (m_pZoneName != m_pLastZoneName) { + switch (m_ZoneState) { + case 0: + m_ZoneState = 2; + m_ZoneToPrint = m_pZoneName; + m_ZoneNameTimer = 0; + m_ZoneFadeTimer = 0; + if (m_VehicleState == 1 || m_VehicleState == 2) + m_VehicleState = 3; + break; + case 1: + case 2: + case 3: + case 4: + m_ZoneNameTimer = 5; + m_ZoneState = 4; + break; + default: + break; + } + m_pLastZoneName = m_pZoneName; + } + + float fZoneAlpha = 255.0f; + if (m_ZoneState) { + switch (m_ZoneState) { + case 1: + fZoneAlpha = 255.0f; + m_ZoneFadeTimer = 1000; + if (m_ZoneNameTimer > 10000.0f) { + m_ZoneFadeTimer = 1000; + m_ZoneState = 3; + } + break; + case 2: + m_ZoneFadeTimer += CTimer::GetTimeStepInMilliseconds(); + if (m_ZoneFadeTimer > 1000.0f) { + m_ZoneState = 1; + m_ZoneFadeTimer = 1000; + } + fZoneAlpha = m_ZoneFadeTimer * 0.001f * 255.0f; + break; + case 3: + m_ZoneFadeTimer -= CTimer::GetTimeStepInMilliseconds(); + if (m_ZoneFadeTimer < 0.0f) { + m_ZoneState = 0; + m_ZoneFadeTimer = 0; + } + fZoneAlpha = m_ZoneFadeTimer * 0.001f * 255.0f; + break; + case 4: + m_ZoneFadeTimer -= CTimer::GetTimeStepInMilliseconds(); + if (m_ZoneFadeTimer < 0.0f) { + m_ZoneFadeTimer = 0; + m_ZoneToPrint = m_pLastZoneName; + m_ZoneState = 2; + } + fZoneAlpha = m_ZoneFadeTimer * 0.001f * 255.0f; + break; + default: + break; + + } + + if (!m_Message[0] && BigMessageInUse[1] == 0.0f && BigMessageInUse[2] == 0.0f) { + + m_ZoneNameTimer += CTimer::GetTimeStepInMilliseconds(); + CFont::SetJustifyOff(); + CFont::SetPropOn(); + CFont::SetBackgroundOff(); + + if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH) + CFont::SetScale(SCREEN_SCALE_X(1.7f * 0.8f), SCREEN_SCALE_Y(1.8f)); + else + CFont::SetScale(SCREEN_SCALE_X(1.7f), SCREEN_SCALE_Y(1.8f)); + + CFont::SetSlantRefPoint(SCREEN_SCALE_FROM_RIGHT(32.0f), SCREEN_SCALE_FROM_BOTTOM(128.0f)); + CFont::SetSlant(0.15f); + + CFont::SetRightJustifyOn(); + CFont::SetRightJustifyWrap(0.0f); + CFont::SetBackGroundOnlyTextOff(); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, fZoneAlpha)); + CFont::SetFontStyle(FONT_BANK); + CFont::SetColor(CRGBA(ZONE_COLOR.r, ZONE_COLOR.g, ZONE_COLOR.b, fZoneAlpha)); + + if (!CTheScripts::bPlayerIsInTheStatium) + CFont::PrintStringFromBottom(SCREEN_SCALE_FROM_RIGHT(32.0f), SCREEN_SCALE_FROM_BOTTOM(128.0f), m_ZoneToPrint); + + CFont::SetSlant(0.f); + } else { + m_ZoneState = 3; + } + } + } + + /* + DrawVehicleName + */ + if (m_VehicleName) { + float fVehicleAlpha = 0.0f; + + if (m_VehicleName != m_pLastVehicleName) { + switch (m_VehicleState) { + case 0: + m_VehicleState = 2; + m_pVehicleNameToPrint = m_VehicleName; + m_VehicleNameTimer = 0; + m_VehicleFadeTimer = 0; + if (m_ZoneState == 1 || m_ZoneState == 2) + m_ZoneState = 3; + break; + case 1: + case 2: + case 3: + case 4: + m_VehicleNameTimer = 0; + m_VehicleState = 4; + break; + default: + break; + } + m_pLastVehicleName = m_VehicleName; + } + + if (m_VehicleState) { + switch (m_VehicleState) { + case 1: + if (m_VehicleNameTimer > 10000) { + m_VehicleFadeTimer = 1000; + m_VehicleState = 3; + } + fVehicleAlpha = 255.0f; + break; + case 2: + m_VehicleFadeTimer += CTimer::GetTimeStepInMilliseconds(); + if (m_VehicleFadeTimer > 1000) { + m_VehicleState = 1; + m_VehicleFadeTimer = 1000; + } + fVehicleAlpha = m_VehicleFadeTimer * 0.001f * 255.0f; + break; + case 3: + m_VehicleFadeTimer -= CTimer::GetTimeStepInMilliseconds(); + if (m_VehicleFadeTimer < 0) { + m_VehicleState = 0; + m_VehicleFadeTimer = 0; + } + fVehicleAlpha = m_VehicleFadeTimer * 0.001f * 255.0f; + break; + case 4: + m_VehicleFadeTimer -= CTimer::GetTimeStepInMilliseconds(); + if (m_VehicleFadeTimer < 0) { + m_VehicleFadeTimer = 0; + m_pVehicleNameToPrint = m_pLastVehicleName; + m_VehicleNameTimer = 0; + m_VehicleState = 2; + } + fVehicleAlpha = m_VehicleFadeTimer * 0.001f * 255.0f; + break; + default: + break; + } + + if (!m_Message[0]) { + m_VehicleNameTimer += CTimer::GetTimeStepInMilliseconds(); + CFont::SetJustifyOff(); + CFont::SetPropOn(); + CFont::SetBackgroundOff(); + + if (FrontEndMenuManager.m_PrefsLanguage != CMenuManager::LANGUAGE_ITALIAN && FrontEndMenuManager.m_PrefsLanguage != CMenuManager::LANGUAGE_SPANISH) + CFont::SetScale(SCREEN_SCALE_X(1.7f), SCREEN_SCALE_Y(1.8f)); + else + CFont::SetScale(SCREEN_SCALE_X(1.7f * 0.85f), SCREEN_SCALE_Y(1.8f)); + + CFont::SetSlantRefPoint(SCREEN_SCALE_FROM_RIGHT(32.0f), SCREEN_SCALE_FROM_BOTTOM(105.0f)); + CFont::SetSlant(0.15f); + + CFont::SetRightJustifyOn(); + CFont::SetRightJustifyWrap(0.0f); + CFont::SetBackGroundOnlyTextOff(); + CFont::SetFontStyle(FONT_BANK); + CFont::SetDropShadowPosition(2); + CFont::SetColor(CRGBA(VEHICLE_COLOR.r, VEHICLE_COLOR.g, VEHICLE_COLOR.b, fVehicleAlpha)); + CFont::SetDropColor(CRGBA(0, 0, 0, fVehicleAlpha)); + + CFont::PrintStringFromBottom(SCREEN_SCALE_FROM_RIGHT(32.0f), SCREEN_SCALE_FROM_BOTTOM(105.0f), m_pVehicleNameToPrint); + + CFont::SetSlant(0.f); + } + } + } + else { + m_pLastVehicleName = nil; + m_VehicleState = 0; + m_VehicleFadeTimer = 0; + m_VehicleNameTimer = 0; + } + + /* + DrawClock + */ + if (m_ClockState) { + CFont::SetJustifyOff(); + CFont::SetCentreOff(); + CFont::SetBackgroundOff(); + CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); + CFont::SetBackGroundOnlyTextOff(); + CFont::SetPropOff(); + CFont::SetFontStyle(FONT_HEADING); + CFont::SetRightJustifyOn(); + CFont::SetRightJustifyWrap(0.0f); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + + sprintf(sTemp, "%02d:%02d", CClock::GetHours(), CClock::GetMinutes()); + AsciiToUnicode(sTemp, sPrint); + + CFont::SetColor(CLOCK_COLOR); + if (FrontEndMenuManager.m_PrefsShowHud) + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(111.0f), SCREEN_SCALE_Y(22.0f), sPrint); + } + + /* + DrawOnScreenTimer + */ + + wchar sTimer[16]; + + if (!CUserDisplay::OnscnTimer.m_sClocks[0].m_bClockProcessed) + TimerOnLastFrame = false; + + for(uint32 i = 0; i < NUMONSCREENCOUNTERS; i++) { + if (!CUserDisplay::OnscnTimer.m_sCounters[0].m_bCounterProcessed) + CounterOnLastFrame[i] = false; + } + + if (CUserDisplay::OnscnTimer.m_bProcessed) { + if (CUserDisplay::OnscnTimer.m_sClocks[0].m_bClockProcessed) { + if (!TimerOnLastFrame) + TimerFlashTimer = 1; + + TimerOnLastFrame = true; + + if (TimerFlashTimer != 0) { + if (++TimerFlashTimer > 50) + TimerFlashTimer = 0; + } + + if (FRAMECOUNTER & 4 || TimerFlashTimer == 0) { + AsciiToUnicode(CUserDisplay::OnscnTimer.m_sClocks[0].m_aClockBuffer, sTimer); + CFont::SetPropOn(); + CFont::SetBackgroundOff(); + CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); + CFont::SetRightJustifyOn(); + CFont::SetRightJustifyWrap(0.0f); + CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); + CFont::SetPropOff(); + CFont::SetBackGroundOnlyTextOn(); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); + CFont::SetColor(TIMER_COLOR); + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(37.0f), SCREEN_SCALE_Y(110.0f), sTimer); + CFont::SetPropOn(); + + if (CUserDisplay::OnscnTimer.m_sClocks[0].m_aClockText[0]) { + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + CFont::SetColor(TIMER_COLOR); + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(37.0f) - SCREEN_SCALE_X(80.0f), SCREEN_SCALE_Y(110.0f), TheText.Get(CUserDisplay::OnscnTimer.m_sClocks[0].m_aClockText)); + } + } + } + + for(uint32 i = 0; i < NUMONSCREENCOUNTERS; i++) { + if (CUserDisplay::OnscnTimer.m_sCounters[i].m_bCounterProcessed) { + if (!CounterOnLastFrame[i]) + CounterFlashTimer[i] = 1; + + CounterOnLastFrame[i] = true; + + if (CounterFlashTimer[i] != 0) { + if (++CounterFlashTimer[i] > 50) + CounterFlashTimer[i] = 0; + } + + if (FRAMECOUNTER & 4 || CounterFlashTimer[i] == 0) { + if (CUserDisplay::OnscnTimer.m_sCounters[i].m_nType == COUNTER_DISPLAY_NUMBER) { + AsciiToUnicode(CUserDisplay::OnscnTimer.m_sCounters[i].m_aCounterBuffer, sTimer); + CFont::SetPropOn(); + CFont::SetBackgroundOff(); + CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); + CFont::SetCentreOff(); + CFont::SetRightJustifyOn(); + CFont::SetRightJustifyWrap(0.0f); + CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); + CFont::SetWrapx(SCREEN_STRETCH_X(DEFAULT_SCREEN_WIDTH)); + CFont::SetPropOn(); + CFont::SetBackGroundOnlyTextOn(); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + CFont::SetColor(COUNTER_COLOR); + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(37.0f), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y * 20.f * i) + SCREEN_SCALE_Y(132.0f), sTimer); + } else { + int counter = atoi(CUserDisplay::OnscnTimer.m_sCounters[i].m_aCounterBuffer); + + const float barWidth = SCREEN_SCALE_X(100.f / 2.f); + const float right = SCREEN_SCALE_FROM_RIGHT(37.0f); + const float left = right - barWidth; + + const float barHeight = SCREEN_SCALE_Y(11.0f); + const float top = SCREEN_SCALE_Y(132.0f) + SCREEN_SCALE_Y(8.0f) + SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y * 20.f * i); + const float bottom = top + barHeight; + + // shadow + CSprite2d::DrawRect(CRect(left + SCREEN_SCALE_X(6.0f), top + SCREEN_SCALE_Y(2.0f), right + SCREEN_SCALE_X(6.0f), bottom + SCREEN_SCALE_Y(2.0f)), CRGBA(0, 0, 0, 255)); + + CSprite2d::DrawRect(CRect(left + SCREEN_SCALE_X(4.0f), top, right + SCREEN_SCALE_X(4.0f), bottom), CRGBA(27, 89, 130, 255)); + CSprite2d::DrawRect(CRect(left + SCREEN_SCALE_X(4.0f), top, left + SCREEN_SCALE_X(counter) / 2.0f + SCREEN_SCALE_X(4.0f), bottom), CRGBA(97, 194, 247, 255)); + } + + if (CUserDisplay::OnscnTimer.m_sCounters[i].m_aCounterText[0]) { + CFont::SetPropOn(); + CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); + CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + CFont::SetColor(COUNTER_COLOR); + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(37.0f) - SCREEN_SCALE_X(61.0f), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y * 20.f * i) + SCREEN_SCALE_Y(132.0f), TheText.Get(CUserDisplay::OnscnTimer.m_sCounters[i].m_aCounterText)); + } + // unused/leftover color. I wonder what was it for + CFont::SetColor(CRGBA(244, 225, 91, 255)); + } + } + } + } + + /* + DrawRadar + */ + if (FrontEndMenuManager.m_PrefsRadarMode != 2 && + !m_HideRadar && (m_ItemToFlash == ITEM_RADAR && FRAMECOUNTER & 8 || m_ItemToFlash != ITEM_RADAR)) { + + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST); + CRadar::DrawMap(); + if (FrontEndMenuManager.m_PrefsRadarMode != 1) { + CRect rect(0.0f, 0.0f, SCREEN_SCALE_X(RADAR_WIDTH), SCREEN_SCALE_Y(RADAR_HEIGHT)); + + rect.Translate(SCREEN_SCALE_X_FIX(RADAR_LEFT), SCREEN_SCALE_FROM_BOTTOM(RADAR_BOTTOM + RADAR_HEIGHT)); + +#ifdef FIX_BUGS + rect.Grow(SCREEN_SCALE_X(6.0f), SCREEN_SCALE_X(6.0f), SCREEN_SCALE_Y(6.0f), SCREEN_SCALE_Y(6.0f)); +#else + rect.Grow(6.0f); +#endif + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + rect.Translate(SCREEN_SCALE_X_FIX(0.0f), SCREEN_SCALE_Y_FIX(2.0f)); + Sprites[HUD_RADARDISC].Draw(rect, CRGBA(0, 0, 0, 255)); + rect.Translate(SCREEN_SCALE_X_FIX(0.0f), SCREEN_SCALE_Y_FIX(-2.0f)); + Sprites[HUD_RADARDISC].Draw(rect, RADARDISC_COLOR); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + } + CRadar::DrawBlips(); + } + } + + /* + Draw3dMarkers + */ + if (m_Wants_To_Draw_3dMarkers && !TheCamera.m_WideScreenOn && !m_BigMessage[0][0] && !m_BigMessage[2][0]) { + CRadar::Draw3dMarkers(); + } + + /* + DrawScriptText + */ + if (!CTimer::GetIsUserPaused()) { + for (int i = 0; i < ARRAY_SIZE(CTheScripts::IntroTextLines); i++) { + if (CTheScripts::IntroTextLines[i].m_Text[0] && CTheScripts::IntroTextLines[i].m_bTextBeforeFade) { + CFont::SetScale(SCREEN_SCALE_X(CTheScripts::IntroTextLines[i].m_fScaleX), SCREEN_SCALE_Y(CTheScripts::IntroTextLines[i].m_fScaleY * 0.5f)); + CFont::SetColor(CTheScripts::IntroTextLines[i].m_sColor); + + if (CTheScripts::IntroTextLines[i].m_bJustify) + CFont::SetJustifyOn(); + else + CFont::SetJustifyOff(); + + if (CTheScripts::IntroTextLines[i].m_bRightJustify) + CFont::SetRightJustifyOn(); + else + CFont::SetRightJustifyOff(); + + if (CTheScripts::IntroTextLines[i].m_bCentered) + CFont::SetCentreOn(); + else + CFont::SetCentreOff(); + + CFont::SetWrapx(SCALE_AND_CENTER_X(CTheScripts::IntroTextLines[i].m_fWrapX)); + CFont::SetCentreSize(SCREEN_SCALE_X(CTheScripts::IntroTextLines[i].m_fCenterSize)); + + if (CTheScripts::IntroTextLines[i].m_bBackground) + CFont::SetBackgroundOn(); + else + CFont::SetBackgroundOff(); + + CFont::SetBackgroundColor(CTheScripts::IntroTextLines[i].m_sBackgroundColor); + + if (CTheScripts::IntroTextLines[i].m_bBackgroundOnly) + CFont::SetBackGroundOnlyTextOn(); + else + CFont::SetBackGroundOnlyTextOff(); + + if (CTheScripts::IntroTextLines[i].m_bTextProportional) + CFont::SetPropOn(); + else + CFont::SetPropOff(); + + CFont::SetFontStyle(FONT_LOCALE(CTheScripts::IntroTextLines[i].m_nFont)); + CFont::PrintString(SCREEN_WIDTH - SCALE_AND_CENTER_X(DEFAULT_SCREEN_WIDTH - CTheScripts::IntroTextLines[i].m_fAtX), SCREEN_HEIGHT - SCREEN_SCALE_Y(DEFAULT_SCREEN_HEIGHT - CTheScripts::IntroTextLines[i].m_fAtY), CTheScripts::IntroTextLines[i].m_Text); + } + } + for (int i = 0; i < ARRAY_SIZE(CTheScripts::IntroRectangles); i++) { + intro_script_rectangle &IntroRect = CTheScripts::IntroRectangles[i]; + + // Yeah, top and bottom changed place. R* vision + if (IntroRect.m_bIsUsed && IntroRect.m_bBeforeFade) { + if (IntroRect.m_nTextureId >= 0) { + CRect rect ( + IntroRect.m_sRect.left, + IntroRect.m_sRect.bottom, + IntroRect.m_sRect.right, + IntroRect.m_sRect.top ); + + CTheScripts::ScriptSprites[IntroRect.m_nTextureId].Draw(rect, IntroRect.m_sColor); + } + else { + CRect rect ( + IntroRect.m_sRect.left, + IntroRect.m_sRect.bottom, + IntroRect.m_sRect.right, + IntroRect.m_sRect.top ); + + CSprite2d::DrawRect(rect, IntroRect.m_sColor); + } + } + } + + /* + DrawSubtitles + */ + if (m_Message[0] && !m_BigMessage[2][0]) { + if (m_VehicleState != 0) + m_VehicleState = 3; + if (m_ZoneState != 0) + m_ZoneState = 3; + + CFont::SetJustifyOff(); + CFont::SetBackgroundOff(); + CFont::SetBackgroundColor(CRGBA(0, 0, 0, 128)); + CFont::SetCentreOn(); + CFont::SetPropOn(); +#ifdef CUTSCENE_BORDERS_SWITCH + if (!FrontEndMenuManager.m_PrefsCutsceneBorders) { + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + CFont::SetDropShadowPosition(2); + } + else +#endif + CFont::SetDropShadowPosition(0); + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); + CFont::SetColor(CRGBA(225, 225, 225, 255)); + + static bool onceItWasWidescreen = false; + + if (TheCamera.m_WideScreenOn) { + onceItWasWidescreen = true; + + if (FrontEndMenuManager.m_PrefsShowSubtitles || !CCutsceneMgr::IsRunning()) { + CFont::SetCentreSize(SCREEN_WIDTH - SCREEN_SCALE_X(60.0f)); + CFont::SetScale(SCREEN_SCALE_X(0.58f), SCREEN_SCALE_Y(1.2f)); + CFont::PrintString(SCREEN_WIDTH / 2.f, SCREEN_SCALE_FROM_BOTTOM(80.0f), m_Message); + } + } else { + if (onceItWasWidescreen) + m_Message[0] = '\0'; + + onceItWasWidescreen = false; + CFont::DrawFonts(); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + CFont::SetScale(SCREEN_SCALE_X(0.58f), SCREEN_SCALE_Y(1.22f)); + + float radarBulge = SCREEN_SCALE_X(140.0f) + SCREEN_SCALE_X(8.0f); + float rectWidth = SCREEN_WIDTH - SCREEN_SCALE_X(20.0f) - SCREEN_SCALE_X(8.0f) - radarBulge; + CFont::SetCentreSize(rectWidth); + + CFont::PrintString(rectWidth / 2.0f + radarBulge, SCREEN_SCALE_FROM_BOTTOM(105.f + 2.0f), m_Message); + } + CFont::SetDropShadowPosition(0); + } + + /* + HelpMessage + */ + + if (m_HelpMessage[0]) { + if (!CMessages::WideStringCompare(m_HelpMessage, m_LastHelpMessage, HELP_MSG_LENGTH)) { + switch (m_HelpMessageState) { + case 0: + m_HelpMessageFadeTimer = 0; + m_HelpMessageState = 2; + m_HelpMessageTimer = 0; + CMessages::WideStringCopy(m_HelpMessageToPrint, m_HelpMessage, HELP_MSG_LENGTH); + m_HelpMessageDisplayTime = CMessages::GetWideStringLength(m_HelpMessage) * 0.05f + 3.0f; + + if (TheCamera.m_ScreenReductionPercentage == 0.0f) + DMAudio.PlayFrontEndSound(SOUND_HUD, 0); + break; + case 1: + case 2: + case 3: + case 4: + m_HelpMessageTimer = 5; + m_HelpMessageState = 4; + break; + default: + break; + } + CMessages::WideStringCopy(m_LastHelpMessage, m_HelpMessage, HELP_MSG_LENGTH); + } + + float fAlpha = 225.0f; + + if (m_HelpMessageState != 0) { + switch (m_HelpMessageState) { + case 1: + fAlpha = 225.0f; + m_HelpMessageFadeTimer = 600; + if (!m_HelpMessageDisplayForever && m_HelpMessageTimer > m_HelpMessageDisplayTime * 1000.0f || + m_HelpMessageQuick && m_HelpMessageTimer > 1500.0f) { + + m_HelpMessageFadeTimer = 600; + m_HelpMessageState = 3; + } + break; + case 2: + if (TheCamera.m_WideScreenOn) + break; + + m_HelpMessageFadeTimer += 2 * CTimer::GetTimeStepInMilliseconds(); + if (m_HelpMessageFadeTimer > 0) { + m_HelpMessageState = 1; + m_HelpMessageFadeTimer = 0; + } + fAlpha = m_HelpMessageFadeTimer * 0.001f * 225.0f; + break; + case 3: + m_HelpMessageFadeTimer -= 2 * CTimer::GetTimeStepInMilliseconds(); + if (m_HelpMessageFadeTimer < 0 || TheCamera.m_WideScreenOn) { + m_HelpMessageState = 0; + m_HelpMessageFadeTimer = 0; + } + fAlpha = m_HelpMessageFadeTimer * 0.001f * 225.0f; + break; + case 4: + m_HelpMessageFadeTimer -= 2 * CTimer::GetTimeStepInMilliseconds(); + if (m_HelpMessageFadeTimer < 0) { + m_HelpMessageState = 2; + m_HelpMessageFadeTimer = 0; + CMessages::WideStringCopy(m_HelpMessageToPrint, m_LastHelpMessage, HELP_MSG_LENGTH); + } + fAlpha = m_HelpMessageFadeTimer * 0.001f * 225.0f; + break; + default: + break; + } + + if (!TheCamera.m_WideScreenOn) { + m_HelpMessageTimer += CTimer::GetTimeStepInMilliseconds(); + + CFont::SetAlphaFade(fAlpha); + CFont::SetCentreOff(); + CFont::SetPropOn(); + + if (CGame::germanGame) + CFont::SetScale(SCREEN_SCALE_X(0.52f * 0.85f), SCREEN_SCALE_Y(1.1f * 0.85f)); +#ifdef MORE_LANGUAGES + else if (CFont::IsJapanese()) + CFont::SetScale(SCREEN_SCALE_X(0.52f) * 1.35f, SCREEN_SCALE_Y(1.1f) * 1.25f); +#endif + else + CFont::SetScale(SCREEN_SCALE_X(0.52f), SCREEN_SCALE_Y(1.1f)); + + CFont::DrawFonts(); + CFont::SetColor(CRGBA(175, 175, 175, 255)); + CFont::SetJustifyOff(); +#ifdef MORE_LANGUAGES + if (CFont::IsJapanese()) + CFont::SetWrapx(SCREEN_SCALE_X(229.0f + 34.0f - 4.0f)); + else +#endif + CFont::SetWrapx(SCREEN_SCALE_X(200.0f + 34.0f - 4.0f)); + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); + CFont::SetBackgroundOn(); + CFont::SetBackGroundOnlyTextOff(); + CFont::SetDropShadowPosition(0); + CFont::SetBackgroundColor(CRGBA(0, 0, 0, fAlpha * 0.9f)); + CFont::SetColor(CRGBA(175, 175, 175, 255)); + CFont::PrintString(SCREEN_SCALE_X(34.0f), SCREEN_SCALE_Y(28.0f + (150.0f - PagerXOffset) * 0.6f), m_HelpMessageToPrint); + CFont::SetAlphaFade(255.0f); + CFont::SetWrapx(SCREEN_WIDTH); + } + } + } else + m_HelpMessageState = 0; + + /* + DrawBigMessage + */ + // MissionCompleteFailedText + if (m_BigMessage[0][0]) { + if (BigMessageInUse[0] != 0.0f) { + CFont::SetJustifyOff(); + CFont::SetBackgroundOff(); + CFont::SetBackGroundOnlyTextOff(); + if (CGame::frenchGame || CGame::germanGame) { + CFont::SetScale(SCREEN_SCALE_X(1.6f), SCREEN_SCALE_Y(1.8f)); + } else { + CFont::SetScale(SCREEN_SCALE_X(1.8f), SCREEN_SCALE_Y(1.8f)); + } + CFont::SetPropOn(); + CFont::SetCentreOn(); + CFont::SetCentreSize(SCREEN_SCALE_X(590.0f)); + CFont::SetColor(CRGBA(255, 255, 0, BigMessageAlpha[0])); // unused color + CFont::SetFontStyle(FONT_HEADING); + + // Appearently sliding text in here was abandoned very early, since this text is centered now. + + if (BigMessageX[0] >= SCALE_AND_CENTER_X(620.0f)) { + BigMessageInUse[0] += CTimer::GetTimeStep(); + + if (BigMessageInUse[0] >= 120.0f) { + BigMessageInUse[0] = 120.0f; + BigMessageAlpha[0] -= (CTimer::GetTimeStepInMilliseconds() * 0.3f); + } + + if (BigMessageAlpha[0] <= 0.0f) { + m_BigMessage[0][0] = 0; + BigMessageAlpha[0] = 0.0f; + } + } + else { + BigMessageX[0] += SCREEN_SCALE_X((CTimer::GetTimeStepInMilliseconds() * 0.3f)); + BigMessageAlpha[0] += (CTimer::GetTimeStepInMilliseconds() * 0.3f); + + if (BigMessageAlpha[0] > 255.0f) + BigMessageAlpha[0] = 255.0f; + } + CFont::DrawFonts(); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, BigMessageAlpha[0])); + CFont::SetColor(CRGBA(BIGMESSAGE_COLOR.r, BIGMESSAGE_COLOR.g, BIGMESSAGE_COLOR.b, BigMessageAlpha[0])); + CFont::PrintString(SCREEN_WIDTH / 2, (SCREEN_HEIGHT / 2) - SCREEN_SCALE_Y(18.0f), m_BigMessage[0]); + } + else { + BigMessageAlpha[0] = 0.0f; + BigMessageX[0] = SCALE_AND_CENTER_X(-60.0f); + BigMessageInUse[0] = 1.0f; + } + } + else { + BigMessageInUse[0] = 0.0f; + } + + // WastedBustedText + if (m_BigMessage[2][0]) { + if (BigMessageInUse[2] != 0.0f) { + BigMessageAlpha[2] += (CTimer::GetTimeStepInMilliseconds() * 0.4f); + + if (BigMessageAlpha[2] > 255.0f) + BigMessageAlpha[2] = 255.0f; + + CFont::SetBackgroundOff(); + + if (CGame::frenchGame || CGame::germanGame) + CFont::SetScale(SCREEN_SCALE_X(1.4f), SCREEN_SCALE_Y(1.4f)); + else + CFont::SetScale(SCREEN_SCALE_X(2.0f), SCREEN_SCALE_Y(2.0f)); + + CFont::SetPropOn(); + CFont::SetRightJustifyOn(); + CFont::SetFontStyle(FONT_HEADING); + + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, BigMessageAlpha[2])); + + CFont::SetColor(CRGBA(WASTEDBUSTED_COLOR.r, WASTEDBUSTED_COLOR.g, WASTEDBUSTED_COLOR.b, BigMessageAlpha[2])); + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(20.0f), SCREEN_SCALE_FROM_BOTTOM(90.0f), m_BigMessage[2]); + } + else { + BigMessageInUse[2] = 1.0f; + BigMessageAlpha[2] = 0.0f; + if (m_VehicleState != 0) // Hide vehicle name if wasted/busted text is displaying + m_VehicleState = 0; + if (m_ZoneState != 0) + m_ZoneState = 0; + } + } + else { + BigMessageInUse[2] = 0.0f; + } + } +} + +void CHud::DrawAfterFade() +{ + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST); + RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + + if (CTimer::GetIsUserPaused() || CReplay::IsPlayingBack()) + return; + + for (int i = 0; i < ARRAY_SIZE(CTheScripts::IntroTextLines); i++) { + intro_text_line &line = CTheScripts::IntroTextLines[i]; + if (line.m_Text[0] != '\0' && !line.m_bTextBeforeFade) { + CFont::SetScale(SCREEN_SCALE_X(line.m_fScaleX), SCREEN_SCALE_Y(line.m_fScaleY) / 2); + + CFont::SetColor(line.m_sColor); + if (line.m_bJustify) + CFont::SetJustifyOn(); + else + CFont::SetJustifyOff(); + + if (line.m_bRightJustify) + CFont::SetRightJustifyOn(); + else + CFont::SetRightJustifyOff(); + + if (line.m_bCentered) + CFont::SetCentreOn(); + else + CFont::SetCentreOff(); + + CFont::SetWrapx(SCALE_AND_CENTER_X(line.m_fWrapX)); + CFont::SetCentreSize(SCREEN_SCALE_X(line.m_fCenterSize)); + if (line.m_bBackground) + CFont::SetBackgroundOn(); + else + CFont::SetBackgroundOff(); + + CFont::SetBackgroundColor(line.m_sBackgroundColor); + if (line.m_bBackgroundOnly) + CFont::SetBackGroundOnlyTextOn(); + else + CFont::SetBackGroundOnlyTextOff(); + + if (line.m_bTextProportional) + CFont::SetPropOn(); + else + CFont::SetPropOff(); + + CFont::SetFontStyle(line.m_nFont); + CFont::PrintString(SCREEN_WIDTH - SCALE_AND_CENTER_X(DEFAULT_SCREEN_WIDTH - line.m_fAtX), SCREEN_HEIGHT - SCREEN_SCALE_Y(DEFAULT_SCREEN_HEIGHT - line.m_fAtY), line.m_Text); + } + } + for (int i = 0; i < ARRAY_SIZE(CTheScripts::IntroRectangles); i++) { + intro_script_rectangle &rectangle = CTheScripts::IntroRectangles[i]; + if (rectangle.m_bIsUsed && !rectangle.m_bBeforeFade) { + + // Yeah, top and bottom changed place. R* vision + if (rectangle.m_nTextureId >= 0) { + CTheScripts::ScriptSprites[rectangle.m_nTextureId].Draw(CRect(rectangle.m_sRect.left, rectangle.m_sRect.bottom, + rectangle.m_sRect.right, rectangle.m_sRect.top), rectangle.m_sColor); + } else { + CSprite2d::DrawRect(CRect(rectangle.m_sRect.left, rectangle.m_sRect.bottom, + rectangle.m_sRect.right, rectangle.m_sRect.top), rectangle.m_sColor); + } + } + } + + /* + DrawBigMessage2 + */ + // Oddjob + if (m_BigMessage[3][0]) { + CFont::SetJustifyOff(); + CFont::SetBackgroundOff(); + CFont::SetScale(SCREEN_SCALE_X(1.2f), SCREEN_SCALE_Y(1.5f)); + CFont::SetCentreOn(); + CFont::SetPropOn(); + CFont::SetCentreSize(SCREEN_SCALE_X(600.0f)); + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + CFont::SetColor(ODDJOB_COLOR); + CFont::PrintString((SCREEN_WIDTH / 2), SCREEN_SCALE_Y(140.0f) - SCREEN_SCALE_Y(16.0f), m_BigMessage[3]); + } + + if (!m_BigMessage[1][0] && m_BigMessage[4][0]) { + CFont::SetJustifyOff(); + CFont::SetBackgroundOff(); + CFont::SetScale(SCREEN_SCALE_X(1.2f), SCREEN_SCALE_Y(1.5f)); + CFont::SetCentreOn(); + CFont::SetPropOn(); + CFont::SetCentreSize(SCREEN_SCALE_X(580.0f)); + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + CFont::SetColor(ODDJOB_COLOR); + CFont::PrintString((SCREEN_WIDTH / 2), SCREEN_SCALE_Y(140.0f), m_BigMessage[4]); + } + + // Oddjob result + if (OddJob2OffTimer > 0) + OddJob2OffTimer -= CTimer::GetTimeStepInMilliseconds(); + + float fStep; + if (m_BigMessage[5][0] && OddJob2OffTimer <= 0.0f) { + switch (OddJob2On) { + case 0: + OddJob2On = 1; + OddJob2XOffset = 380.0f; + break; + case 1: + if (OddJob2XOffset <= 2.0f) { + OddJob2Timer = 0; + OddJob2On = 2; + } + else { + fStep = Min(40.0f, OddJob2XOffset / 6.0f); + OddJob2XOffset = OddJob2XOffset - fStep; + } + break; + case 2: + OddJob2Timer += CTimer::GetTimeStepInMilliseconds(); + if (OddJob2Timer > 1500) { + OddJob2On = 3; + } + break; + case 3: + fStep = Max(30.0f, OddJob2XOffset / 5.0f); + + OddJob2XOffset = OddJob2XOffset - fStep; + + if (OddJob2XOffset < -380.0f) { + OddJob2OffTimer = 5000.0f; + OddJob2On = 0; + } + break; + default: + break; + } + + if (!m_BigMessage[1][0]) { + CFont::SetJustifyOff(); + CFont::SetBackgroundOff(); + CFont::SetScale(SCREEN_SCALE_X(1.0f), SCREEN_SCALE_Y(1.2f)); + CFont::SetCentreOn(); + CFont::SetPropOn(); + CFont::SetCentreSize(SCREEN_SCALE_X(560.0f)); + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + CFont::SetColor(ODDJOB2_COLOR); + CFont::PrintString(SCREEN_WIDTH / 2, SCREEN_SCALE_Y(217.0f), m_BigMessage[5]); + } + } + + /* + DrawMissionTitle + */ + if (m_BigMessage[1][0]) { + if (BigMessageInUse[1] != 0.0f) { + CFont::SetJustifyOff(); + CFont::SetBackgroundOff(); + + // will be overwritten below + if (CGame::frenchGame || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH) + CFont::SetScale(SCREEN_SCALE_X(0.884f), SCREEN_SCALE_Y(1.36f)); + else + CFont::SetScale(SCREEN_SCALE_X(1.04f), SCREEN_SCALE_Y(1.6f)); + + CFont::SetPropOn(); + CFont::SetRightJustifyWrap(SCALE_AND_CENTER_X(0.0f)); + CFont::SetRightJustifyOn(); + CFont::SetFontStyle(FONT_BANK); + CFont::SetScale(FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_AMERICAN ? SCREEN_SCALE_X(1.7f) : SCREEN_SCALE_X(1.5f), SCREEN_SCALE_Y(1.8f)); + + if (BigMessageX[1] >= SCREEN_SCALE_FROM_RIGHT(20.0f)) { + BigMessageInUse[1] += CTimer::GetTimeStep(); + + if (BigMessageInUse[1] >= 120.0f) { + BigMessageInUse[1] = 120.0f; + BigMessageAlpha[1] -= CTimer::GetTimeStepInMilliseconds(); + } + if (BigMessageAlpha[1] <= 0.0f) { + m_BigMessage[1][0] = 0; + BigMessageInUse[1] = 0.0f; + BigMessageAlpha[1] = 0.0f; + } + } else { + BigMessageX[1] += SCREEN_SCALE_X((CTimer::GetTimeStepInMilliseconds() * 0.3f)); + BigMessageAlpha[1] += CTimer::GetTimeStepInMilliseconds(); + + if (BigMessageAlpha[1] > 255.0f) + BigMessageAlpha[1] = 255.0f; + } + + CFont::SetColor(CRGBA(40, 40, 40, BigMessageAlpha[1])); // what was that for? + + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, BigMessageAlpha[1])); + CFont::SetColor(CRGBA(MISSIONTITLE_COLOR.r, MISSIONTITLE_COLOR.g, MISSIONTITLE_COLOR.b, BigMessageAlpha[1])); + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(20.0f), SCREEN_SCALE_FROM_BOTTOM(140.0f), m_BigMessage[1]); + } else { + m_ZoneFadeTimer = 0; + BigMessageX[1] = SCREEN_SCALE_FROM_RIGHT(DEFAULT_SCREEN_WIDTH + 60.0f); + BigMessageInUse[1] = 1.0f; + m_ZoneState = 0; + } + } else { + BigMessageInUse[1] = 0.0f; + } +} + +void CHud::GetRidOfAllHudMessages() +{ + m_ZoneNameTimer = 0; + m_pZoneName = nil; + m_ZoneState = 0; + + for (int i = 0; i < HELP_MSG_LENGTH; i++) { + m_HelpMessage[i] = 0; + m_LastHelpMessage[i] = 0; + m_HelpMessageToPrint[i] = 0; + } + + m_HelpMessageTimer = 0; + m_HelpMessageFadeTimer = 0; + m_HelpMessageState = 0; + m_HelpMessageQuick = 0; + m_HelpMessageDisplayForever = false; + m_HelpMessageDisplayTime = 1.0f; + m_VehicleName = nil; + m_pVehicleNameToPrint = nil; + m_VehicleNameTimer = 0; + m_VehicleFadeTimer = 0; + m_VehicleState = 0; + + for (int i = 0; i < ARRAY_SIZE(m_Message); i++) + m_Message[i] = 0; + + for (int i = 0; i < 6; i++) { + BigMessageInUse[i] = 0.0f; + + for (int j = 0; j < 128; j++) + m_BigMessage[i][j] = 0; + } +} + +#ifdef RELOADABLES +void CHud::ReloadTXD() +{ + for (int i = 0; i < NUM_HUD_SPRITES; ++i) { + Sprites[i].Delete(); + } + + int HudTXD = CTxdStore::FindTxdSlot("hud"); + CTxdStore::RemoveTxdSlot(HudTXD); + + debug("Reloading HUD.TXD...\n"); + + HudTXD = CTxdStore::AddTxdSlot("hud"); + CTxdStore::LoadTxd(HudTXD, "MODELS/HUD.TXD"); + CTxdStore::AddRef(HudTXD); + CTxdStore::PopCurrentTxd(); + CTxdStore::SetCurrentTxd(HudTXD); + + for (int i = 0; i < NUM_HUD_SPRITES; i++) { + Sprites[i].SetTexture(WeaponFilenames[i].name, WeaponFilenames[i].mask); + } +} +#endif + +void CHud::Initialise() +{ + m_Wants_To_Draw_Hud = true; + m_Wants_To_Draw_3dMarkers = true; + + int HudTXD = CTxdStore::AddTxdSlot("hud"); + CTxdStore::LoadTxd(HudTXD, "MODELS/HUD.TXD"); + CTxdStore::AddRef(HudTXD); + CTxdStore::PopCurrentTxd(); + CTxdStore::SetCurrentTxd(HudTXD); + + for (int i = 0; i < NUM_HUD_SPRITES; i++) { + Sprites[i].SetTexture(WeaponFilenames[i].name, WeaponFilenames[i].mask); + } + + m_pLastZoneName = nil; + GetRidOfAllHudMessages(); + m_pLastVehicleName = nil; + + if (gpSniperSightTex == nil) + gpSniperSightTex = RwTextureRead("sitesniper", nil); // unused + if (gpRocketSightTex == nil) + gpRocketSightTex = RwTextureRead("siterocket", nil); + if (gpLaserSightTex == nil) + gpLaserSightTex = RwTextureRead("sitelaser", nil); // unused + if (gpLaserDotTex == nil) + gpLaserDotTex = RwTextureRead("laserdot", "laserdotm"); + if (gpViewFinderTex == nil) + gpViewFinderTex = RwTextureRead("viewfinder_128", "viewfinder_128m"); // unused + + m_ClockState = 1; + CounterOnLastFrame[0] = false; + CounterOnLastFrame[1] = false; + CounterOnLastFrame[2] = false; + + m_ItemToFlash = ITEM_NONE; + OddJob2Timer = 0; + OddJob2OffTimer = 0.0f; + OddJob2On = 0; + OddJob2XOffset = 0.0f; + CounterFlashTimer[0] = 0; + CounterFlashTimer[1] = 0; + CounterFlashTimer[2] = 0; + TimerOnLastFrame = false; + TimerFlashTimer = 0; + SpriteBrightness = 0; + PagerOn = 0; + PagerTimer = 0; + PagerSoundPlayed = 0; + PagerXOffset = 150.0f; + +#ifdef HUD_AUTO_FADE + m_EnergyLostState = START_FADE_OUT; + m_WantedState = START_FADE_OUT; + m_DisplayScoreState = START_FADE_OUT; + m_WeaponState = START_FADE_OUT; +#else + m_EnergyLostState = FADE_DISABLED; + m_WantedState = FADE_DISABLED; + m_DisplayScoreState = FADE_DISABLED; + m_WeaponState = FADE_DISABLED; +#endif + m_WantedFadeTimer = 0; + m_WantedTimer = 0; + m_EnergyLostFadeTimer = 0; + m_EnergyLostTimer = 0; + m_DisplayScoreFadeTimer = 0; + m_DisplayScoreTimer = 0; + m_WeaponFadeTimer = 0; + m_WeaponTimer = 0; + + m_HideRadar = false; + m_LastDisplayScore = CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney; + m_LastTimeEnergyLost = CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss; + m_LastWanted = 0; + m_LastWeapon = 0; + +#ifndef MASTER + VarConsole.Add("Draw HUD", &m_Wants_To_Draw_Hud, false); +#endif + CTxdStore::PopCurrentTxd(); +} + +void CHud::ReInitialise() { + m_Wants_To_Draw_Hud = true; + m_Wants_To_Draw_3dMarkers = true; + + m_pLastZoneName = nil; + GetRidOfAllHudMessages(); + m_pLastVehicleName = nil; + + CounterOnLastFrame[0] = false; + CounterOnLastFrame[1] = false; + CounterOnLastFrame[2] = false; + m_ItemToFlash = ITEM_NONE; + m_ClockState = 1; + OddJob2Timer = 0; + OddJob2OffTimer = 0.0f; + OddJob2On = 0; + OddJob2XOffset = 0.0f; + CounterFlashTimer[0] = 0; + CounterFlashTimer[1] = 0; + CounterFlashTimer[2] = 0; + TimerOnLastFrame = false; + TimerFlashTimer = 0; + SpriteBrightness = 0; + PagerOn = 0; + PagerTimer = 0; + PagerSoundPlayed = 0; + PagerXOffset = 150.0f; + +#ifdef HUD_AUTO_FADE + m_EnergyLostState = START_FADE_OUT; + m_WantedState = START_FADE_OUT; + m_DisplayScoreState = START_FADE_OUT; + m_WeaponState = START_FADE_OUT; +#else + m_EnergyLostState = FADE_DISABLED; + m_WantedState = FADE_DISABLED; + m_DisplayScoreState = FADE_DISABLED; + m_WeaponState = FADE_DISABLED; +#endif + m_WantedFadeTimer = 0; + m_WantedTimer = 0; + m_EnergyLostFadeTimer = 0; + m_EnergyLostTimer = 0; + m_DisplayScoreFadeTimer = 0; + m_DisplayScoreTimer = 0; + m_WeaponFadeTimer = 0; + m_WeaponTimer = 0; + + m_HideRadar = false; + m_LastDisplayScore = CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney; + m_LastTimeEnergyLost = CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss; + m_LastWanted = 0; + m_LastWeapon = 0; +} + +wchar LastBigMessage[6][128]; + +void CHud::SetBigMessage(wchar *message, uint16 style) +{ + int i = 0; + + if (BigMessageInUse[style] != 0.0f) + return; + + if (style == 5) { + for (i = 0; i < 128; i++) { + if (message[i] == 0) + break; + + if (message[i] != LastBigMessage[5][i]) { + OddJob2On = 0; + OddJob2OffTimer = 0.0f; + } + + m_BigMessage[5][i] = message[i]; + LastBigMessage[5][i] = message[i]; + } + } else { + for (i = 0; i < 128; i++) { + if (message[i] == 0) + break; + m_BigMessage[style][i] = message[i]; + } + } + LastBigMessage[style][i] = 0; + m_BigMessage[style][i] = 0; +} + +void CHud::SetHelpMessage(wchar *message, bool quick, bool displayForever) +{ + if (!CReplay::IsPlayingBack()) { + for (int i = 0; i < HELP_MSG_LENGTH; i++) { + m_HelpMessage[i] = 0; + } + for (int i = 0; i < HELP_MSG_LENGTH; i++) { + m_LastHelpMessage[i] = 0; + } + for (int i = 0; i < HELP_MSG_LENGTH; i++) { + m_HelpMessageToPrint[i] = 0; + } + + CMessages::WideStringCopy(m_HelpMessage, message, HELP_MSG_LENGTH); + CMessages::InsertPlayerControlKeysInString(m_HelpMessage); + if (m_HelpMessageState == 0 || !CMessages::WideStringCompare(m_HelpMessage, m_HelpMessageToPrint, HELP_MSG_LENGTH)) { + for (int i = 0; i < HELP_MSG_LENGTH; i++) { + m_LastHelpMessage[i] = 0; + } + + if (!message) { + m_HelpMessage[0] = 0; + m_HelpMessageToPrint[0] = 0; + } + if (!displayForever) { + m_HelpMessageState = displayForever; + } else { + m_HelpMessageState = 1; + CMessages::WideStringCopy(m_HelpMessageToPrint, m_HelpMessage, HELP_MSG_LENGTH); + CMessages::WideStringCopy(m_LastHelpMessage, m_HelpMessage, HELP_MSG_LENGTH); + } + + m_HelpMessageQuick = quick; + m_HelpMessageDisplayForever = displayForever; + } + + } +} + +bool CHud::IsHelpMessageBeingDisplayed(void) +{ + return m_HelpMessageState != 0; +} + +void CHud::SetMessage(wchar *message) +{ + int i = 0; + for (i = 0; i < ARRAY_SIZE(m_Message); i++) { + if (message[i] == 0) + break; + + m_Message[i] = message[i]; + } + m_Message[i] = 0; +} + +void CHud::SetPagerMessage(wchar *message) +{ + int i = 0; + for (i = 0; i < ARRAY_SIZE(m_PagerMessage); i++) { + if (message[i] == 0) + break; + + m_PagerMessage[i] = message[i]; + } + m_PagerMessage[i] = 0; +} + +void CHud::SetVehicleName(wchar *name) +{ + m_VehicleName = name; +} + +void CHud::SetZoneName(wchar *name) +{ + m_pZoneName = name; +} + +void CHud::Shutdown() +{ + for (int i = 0; i < NUM_HUD_SPRITES; ++i) { + Sprites[i].Delete(); + } + + RwTextureDestroy(gpSniperSightTex); + gpSniperSightTex = nil; + + RwTextureDestroy(gpRocketSightTex); + gpRocketSightTex = nil; + + RwTextureDestroy(gpLaserSightTex); + gpLaserSightTex = nil; + + RwTextureDestroy(gpLaserDotTex); + gpLaserDotTex = nil; + + RwTextureDestroy(gpViewFinderTex); + gpViewFinderTex = nil; + + int HudTXD = CTxdStore::FindTxdSlot("hud"); + CTxdStore::RemoveTxdSlot(HudTXD); +} + +float CHud::DrawFadeState(DRAW_FADE_STATE fadingElement, int forceFadingIn) +{ + float alpha = 255.0f; + uint32 operation, timer; + int32 fadeTimer; + + switch (fadingElement) { + case HUD_WANTED_FADING: + fadeTimer = m_WantedFadeTimer; + operation = m_WantedState; + timer = m_WantedTimer; + break; + case HUD_ENERGY_FADING: + fadeTimer = m_EnergyLostFadeTimer; + operation = m_EnergyLostState; + timer = m_EnergyLostTimer; + break; + case HUD_SCORE_FADING: + fadeTimer = m_DisplayScoreFadeTimer; + operation = m_DisplayScoreState; + timer = m_DisplayScoreTimer; + break; + case HUD_WEAPON_FADING: + fadeTimer = m_WeaponFadeTimer; + operation = m_WeaponState; + timer = m_WeaponTimer; + break; + default: + break; + } + if (forceFadingIn) { + switch (operation) { + case FADED_OUT: + fadeTimer = 0; + case START_FADE_OUT: + case FADING_OUT: + timer = 5; + operation = FADING_IN; + break; + default: + break; + } + } + if (operation != FADED_OUT && operation != FADE_DISABLED) { + switch (operation) { + case START_FADE_OUT: + fadeTimer = 1000; + alpha = 255.0f; + if (timer > 10000) { + fadeTimer = 3000; + operation = FADING_OUT; + } + break; + case FADING_IN: + fadeTimer += CTimer::GetTimeStepInMilliseconds(); + if (fadeTimer > 1000.0f) { + operation = START_FADE_OUT; + fadeTimer = 1000; + } + alpha = fadeTimer / 1000.0f * 255.0f; + break; + case FADING_OUT: + fadeTimer -= CTimer::GetTimeStepInMilliseconds(); + if (fadeTimer < 0.0f) { + fadeTimer = 0; + operation = FADED_OUT; + } + alpha = fadeTimer / 1000.0f * 255.0f; + break; + default: + break; + } + timer += CTimer::GetTimeStepInMilliseconds(); + } + + switch (fadingElement) { + case HUD_WANTED_FADING: + m_WantedFadeTimer = fadeTimer; + m_WantedState = operation; + m_WantedTimer = timer; + break; + case HUD_ENERGY_FADING: + m_EnergyLostFadeTimer = fadeTimer; + m_EnergyLostState = operation; + m_EnergyLostTimer = timer; + break; + case HUD_SCORE_FADING: + m_DisplayScoreFadeTimer = fadeTimer; + m_DisplayScoreState = operation; + m_DisplayScoreTimer = timer; + break; + case HUD_WEAPON_FADING: + m_WeaponFadeTimer = fadeTimer; + m_WeaponState = operation; + m_WeaponTimer = timer; + break; + default: + break; + } + + return Clamp(alpha, 0.0f, 255.0f); +} + +void +CHud::ResetWastedText(void) +{ + BigMessageInUse[2] = 0.0f; + BigMessageInUse[0] = 0.0f; + m_BigMessage[2][0] = 0; + m_BigMessage[0][0] = 0; +} diff --git a/src/miami/renderer/Hud.h b/src/miami/renderer/Hud.h new file mode 100644 index 00000000..a4b9609a --- /dev/null +++ b/src/miami/renderer/Hud.h @@ -0,0 +1,139 @@ +#pragma once +#include "Sprite2d.h" + +#define HELP_MSG_LENGTH 256 + +#define HUD_TEXT_SCALE_X 0.7f +#define HUD_TEXT_SCALE_Y 1.25f + +enum eItems +{ + ITEM_NONE = -1, + ITEM_ARMOUR = 3, + ITEM_HEALTH = 4, + ITEM_RADAR = 8 +}; + +// Thanks for vague name, R* +enum DRAW_FADE_STATE +{ + HUD_WANTED_FADING = 0, + HUD_ENERGY_FADING, + HUD_SCORE_FADING, + HUD_WEAPON_FADING, +}; + +// My name +enum eFadeOperation +{ + FADED_OUT = 0, + START_FADE_OUT, + FADING_IN, + FADING_OUT, + FADE_DISABLED = 5, +}; + +enum eSprites +{ + HUD_FIST, + HUD_SITEROCKET = 41, + HUD_RADARDISC = 50, + HUD_SITESNIPER = 63, + HUD_SITEM16, + HUD_SITELASER, + HUD_LASERDOT, + HUD_VIEWFINDER, + HUD_BLEEDER, + NUM_HUD_SPRITES = 69, +}; + +class CHud +{ +public: + static CSprite2d Sprites[NUM_HUD_SPRITES]; + static wchar m_HelpMessage[HELP_MSG_LENGTH]; + static wchar m_LastHelpMessage[HELP_MSG_LENGTH]; + static uint32 m_HelpMessageState; + static uint32 m_HelpMessageTimer; + static int32 m_HelpMessageFadeTimer; + static wchar m_HelpMessageToPrint[HELP_MSG_LENGTH]; + static float m_HelpMessageDisplayTime; + static bool m_HelpMessageDisplayForever; + static bool m_HelpMessageQuick; + static uint32 m_ZoneState; + static int32 m_ZoneFadeTimer; + static uint32 m_ZoneNameTimer; + static wchar *m_pZoneName; + static wchar *m_pLastZoneName; + static wchar *m_ZoneToPrint; + static wchar *m_VehicleName; + static wchar *m_pLastVehicleName; + static wchar *m_pVehicleNameToPrint; + static uint32 m_VehicleState; + static int32 m_VehicleFadeTimer; + static uint32 m_VehicleNameTimer; + static wchar m_Message[256]; + static wchar m_PagerMessage[256]; + static bool m_Wants_To_Draw_Hud; + static bool m_Wants_To_Draw_3dMarkers; + static wchar m_BigMessage[6][128]; + static int16 m_ItemToFlash; + static bool m_HideRadar; + static int32 m_ClockState; + + // These aren't really in CHud + static float BigMessageInUse[6]; + static float BigMessageAlpha[6]; + static float BigMessageX[6]; + static float OddJob2OffTimer; + static bool CounterOnLastFrame[NUMONSCREENCOUNTERS]; + static float OddJob2XOffset; + static uint16 CounterFlashTimer[NUMONSCREENCOUNTERS]; + static uint16 OddJob2Timer; + static bool TimerOnLastFrame; + static int16 OddJob2On; + static uint16 TimerFlashTimer; + static int16 PagerSoundPlayed; + static int32 SpriteBrightness; + static float PagerXOffset; + static int16 PagerTimer; + static int16 PagerOn; + + static uint32 m_WantedFadeTimer; + static uint32 m_WantedState; + static uint32 m_WantedTimer; + static uint32 m_EnergyLostFadeTimer; + static uint32 m_EnergyLostState; + static uint32 m_EnergyLostTimer; + static uint32 m_DisplayScoreFadeTimer; + static uint32 m_DisplayScoreState; + static uint32 m_DisplayScoreTimer; + static uint32 m_WeaponFadeTimer; + static uint32 m_WeaponState; + static uint32 m_WeaponTimer; + + static uint32 m_LastDisplayScore; + static uint32 m_LastWanted; + static uint32 m_LastWeapon; + static uint32 m_LastTimeEnergyLost; + +public: + static void Draw(); + static void DrawAfterFade(); + static void GetRidOfAllHudMessages(); +#ifdef RELOADABLES + static void ReloadTXD(); +#endif + static void Initialise(); + static void ReInitialise(); + static void SetBigMessage(wchar *message, uint16 style); + static void SetHelpMessage(wchar *message, bool quick, bool displayForever = false); + static bool IsHelpMessageBeingDisplayed(void); + static void SetMessage(wchar *message); + static void SetPagerMessage(wchar *message); + static void SetVehicleName(wchar *name); + static void SetZoneName(wchar *name); + static void Shutdown(); + static float DrawFadeState(DRAW_FADE_STATE, int); + static void ResetWastedText(void); +}; diff --git a/src/miami/renderer/Instance.cpp b/src/miami/renderer/Instance.cpp new file mode 100644 index 00000000..be6d73d6 --- /dev/null +++ b/src/miami/renderer/Instance.cpp @@ -0,0 +1,9 @@ +#include "common.h" + +#include "Instance.h" + +void +CInstance::Shutdown() +{ + GetMatrix().Detach(); +} diff --git a/src/miami/renderer/Instance.h b/src/miami/renderer/Instance.h new file mode 100644 index 00000000..693cfdf1 --- /dev/null +++ b/src/miami/renderer/Instance.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Placeable.h" + +// unused + +class CInstance : public CPlaceable +{ +public: + int m_modelIndex; +public: + ~CInstance() { } + void Shutdown(); +}; diff --git a/src/miami/renderer/Lines.cpp b/src/miami/renderer/Lines.cpp new file mode 100644 index 00000000..b5c85149 --- /dev/null +++ b/src/miami/renderer/Lines.cpp @@ -0,0 +1,74 @@ +#include "common.h" + +#include "main.h" +#include "Lines.h" + +// This is super inefficient, why split the line into segments at all? +void +CLines::RenderLineWithClipping(float x1, float y1, float z1, float x2, float y2, float z2, uint32 c1, uint32 c2) +{ + static RwIm3DVertex v[2]; +#ifdef THIS_IS_STUPID + int i; + float f1, f2; + float len = sqrt(sq(x1-x2) + sq(y1-y2) + sq(z1-z2)); + int numsegs = len/1.5f + 1.0f; + + RwRGBA col1; + col1.red = c1>>24; + col1.green = c1>>16; + col1.blue = c1>>8; + col1.alpha = c1; + RwRGBA col2; + col2.red = c2>>24; + col2.green = c2>>16; + col2.blue = c2>>8; + col2.alpha = c2; + + float dx = x2 - x1; + float dy = y2 - y1; + float dz = z2 - z1; + for(i = 0; i < numsegs; i++){ + f1 = (float)i/numsegs; + f2 = (float)(i+1)/numsegs; + + RwIm3DVertexSetRGBA(&v[0], (int)(col1.red + (col2.red-col1.red)*f1), + (int)(col1.green + (col2.green-col1.green)*f1), + (int)(col1.blue + (col2.blue-col1.blue)*f1), + (int)(col1.alpha + (col2.alpha-col1.alpha)*f1)); + RwIm3DVertexSetRGBA(&v[1], (int)(col1.red + (col2.red-col1.red)*f2), + (int)(col1.green + (col2.green-col1.green)*f2), + (int)(col1.blue + (col2.blue-col1.blue)*f2), + (int)(col1.alpha + (col2.alpha-col1.alpha)*f2)); + RwIm3DVertexSetPos(&v[0], x1 + dx*f1, y1 + dy*f1, z1 + dz*f1); + RwIm3DVertexSetPos(&v[1], x1 + dx*f2, y1 + dy*f2, z1 + dz*f2); + + LittleTest(); + if(RwIm3DTransform(v, 2, nil, 0)){ + RwIm3DRenderLine(0, 1); + RwIm3DEnd(); + } + } +#else + RwRGBA col1; + col1.red = c1>>24; + col1.green = c1>>16; + col1.blue = c1>>8; + col1.alpha = c1; + RwRGBA col2; + col2.red = c2>>24; + col2.green = c2>>16; + col2.blue = c2>>8; + col2.alpha = c2; + + RwIm3DVertexSetRGBA(&v[0], col1.red, col1.green, col1.blue, col1.alpha); + RwIm3DVertexSetRGBA(&v[1], col2.red, col2.green, col2.blue, col2.alpha); + RwIm3DVertexSetPos(&v[0], x1, y1, z1); + RwIm3DVertexSetPos(&v[1], x2, y2, z2); + LittleTest(); + if(RwIm3DTransform(v, 2, nil, 0)){ + RwIm3DRenderLine(0, 1); + RwIm3DEnd(); + } +#endif +} diff --git a/src/miami/renderer/Lines.h b/src/miami/renderer/Lines.h new file mode 100644 index 00000000..f2694fc0 --- /dev/null +++ b/src/miami/renderer/Lines.h @@ -0,0 +1,7 @@ +#pragma once + +class CLines +{ +public: + static void RenderLineWithClipping(float x1, float y1, float z1, float x2, float y2, float z2, uint32 c1, uint32 c2); +}; diff --git a/src/miami/renderer/MBlur.cpp b/src/miami/renderer/MBlur.cpp new file mode 100644 index 00000000..04314a83 --- /dev/null +++ b/src/miami/renderer/MBlur.cpp @@ -0,0 +1,802 @@ +#ifndef LIBRW +#define WITHD3D +#endif +#include "common.h" +#ifndef LIBRW +#include +#endif + +#include "main.h" +#include "General.h" +#include "RwHelper.h" +#include "Camera.h" +#include "Timecycle.h" +#include "Particle.h" +#include "Timer.h" +#include "Hud.h" +#include "Frontend.h" +#include "MBlur.h" +#include "postfx.h" + +// Originally taken from RW example 'mblur' + +RwRaster *CMBlur::pFrontBuffer; +bool CMBlur::ms_bJustInitialised; +bool CMBlur::ms_bScaledBlur; +bool CMBlur::BlurOn; +float CMBlur::Drunkness; + +int32 CMBlur::pBufVertCount; + +static RwIm2DVertex Vertex[4]; +static RwIm2DVertex Vertex2[4]; +static RwImVertexIndex Index[6] = { 0, 1, 2, 0, 2, 3 }; + +#ifndef LIBRW +extern "C" D3DCAPS8 _RwD3D8DeviceCaps; +#endif +RwBool +CMBlur::MotionBlurOpen(RwCamera *cam) +{ +#ifdef EXTENDED_COLOURFILTER + CPostFX::Open(cam); + return TRUE; +#else +#ifdef GTA_PS2 + RwRect rect = {0, 0, 0, 0}; + + if (pFrontBuffer) + return TRUE; + + BlurOn = true; + + rect.w = RwRasterGetWidth(RwCameraGetRaster(cam)); + rect.h = RwRasterGetHeight(RwCameraGetRaster(cam)); + + pFrontBuffer = RwRasterCreate(0, 0, 0, rwRASTERDONTALLOCATE|rwRASTERTYPECAMERATEXTURE); + if (!pFrontBuffer) + { + printf("Error creating raster\n"); + return FALSE; + } + + RwRaster *raster = RwRasterSubRaster(pFrontBuffer, RwCameraGetRaster(cam), &rect); + if (!raster) + { + RwRasterDestroy(pFrontBuffer); + pFrontBuffer = NULL; + printf("Error subrastering\n"); + return FALSE; + } + + CreateImmediateModeData(cam, &rect); +#else + RwRect rect = { 0, 0, 0, 0 }; + + if(pFrontBuffer) + MotionBlurClose(); + +#ifndef LIBRW + extern void _GetVideoMemInfo(LPDWORD total, LPDWORD avaible); + DWORD total, avaible; + + _GetVideoMemInfo(&total, &avaible); + debug("Available video memory %d\n", avaible); +#endif + + if(BlurOn) + { + uint32 width = Pow(2.0f, int32(log2(RwRasterGetWidth (RwCameraGetRaster(cam))))+1); + uint32 height = Pow(2.0f, int32(log2(RwRasterGetHeight(RwCameraGetRaster(cam))))+1); + uint32 depth = RwRasterGetDepth(RwCameraGetRaster(cam)); + +#ifndef LIBRW + extern DWORD _dwMemTotalVideo; + if ( _RwD3D8DeviceCaps.MaxTextureWidth >= width && _RwD3D8DeviceCaps.MaxTextureHeight >= height ) + { + total = _dwMemTotalVideo - 3 * + ( RwRasterGetDepth(RwCameraGetRaster(cam)) + * RwRasterGetHeight(RwCameraGetRaster(cam)) + * RwRasterGetWidth(RwCameraGetRaster(cam)) / 8 ); + BlurOn = total >= height*width*(depth/8) + (12*1024*1024) /*12 MB*/; + } + else + BlurOn = false; +#endif + + if ( BlurOn ) + { + ms_bScaledBlur = false; + rect.w = width; + rect.h = height; + + // MBlur disabled for now (skmp) + pFrontBuffer = nullptr; // RwRasterCreate(rect.w, rect.h, depth, rwRASTERTYPECAMERATEXTURE); + if ( !pFrontBuffer ) + { + debug("MBlurOpen can't create raster."); + BlurOn = false; + rect.w = RwRasterGetWidth(RwCameraGetRaster(cam)); + rect.h = RwRasterGetHeight(RwCameraGetRaster(cam)); + } + else + ms_bJustInitialised = true; + } + else + { + rect.w = RwRasterGetWidth(RwCameraGetRaster(cam)); + rect.h = RwRasterGetHeight(RwCameraGetRaster(cam)); + } + +#ifndef LIBRW + _GetVideoMemInfo(&total, &avaible); + debug("Available video memory %d\n", avaible); +#endif + CreateImmediateModeData(cam, &rect); + } + else + { + rect.w = RwRasterGetWidth(RwCameraGetRaster(cam)); + rect.h = RwRasterGetHeight(RwCameraGetRaster(cam)); + CreateImmediateModeData(cam, &rect); + } + + return TRUE; +#endif +#endif +} + +RwBool +CMBlur::MotionBlurClose(void) +{ +#ifdef EXTENDED_COLOURFILTER + CPostFX::Close(); +#else + if(pFrontBuffer){ + RwRasterDestroy(pFrontBuffer); + pFrontBuffer = nil; + + return TRUE; + } +#endif + return FALSE; +} + +void +CMBlur::CreateImmediateModeData(RwCamera *cam, RwRect *rect) +{ + float zero, xmax, ymax; + + if(RwRasterGetDepth(RwCameraGetRaster(cam)) == 16){ + zero = HALFPX; + xmax = rect->w + HALFPX; + ymax = rect->h + HALFPX; + }else{ + zero = -HALFPX; + xmax = rect->w - HALFPX; + ymax = rect->h - HALFPX; + } + + RwIm2DVertexSetScreenX(&Vertex[0], zero); + RwIm2DVertexSetScreenY(&Vertex[0], zero); + RwIm2DVertexSetScreenZ(&Vertex[0], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&Vertex[0], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&Vertex[0], 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetU(&Vertex[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetV(&Vertex[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetIntRGBA(&Vertex[0], 255, 255, 255, 255); + + RwIm2DVertexSetScreenX(&Vertex[1], zero); + RwIm2DVertexSetScreenY(&Vertex[1], ymax); + RwIm2DVertexSetScreenZ(&Vertex[1], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&Vertex[1], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&Vertex[1], 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetU(&Vertex[1], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetV(&Vertex[1], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetIntRGBA(&Vertex[1], 255, 255, 255, 255); + + RwIm2DVertexSetScreenX(&Vertex[2], xmax); + RwIm2DVertexSetScreenY(&Vertex[2], ymax); + RwIm2DVertexSetScreenZ(&Vertex[2], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&Vertex[2], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&Vertex[2], 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetU(&Vertex[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetV(&Vertex[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetIntRGBA(&Vertex[2], 255, 255, 255, 255); + + RwIm2DVertexSetScreenX(&Vertex[3], xmax); + RwIm2DVertexSetScreenY(&Vertex[3], zero); + RwIm2DVertexSetScreenZ(&Vertex[3], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&Vertex[3], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&Vertex[3], 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetU(&Vertex[3], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetV(&Vertex[3], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetIntRGBA(&Vertex[3], 255, 255, 255, 255); + + + RwIm2DVertexSetScreenX(&Vertex2[0], zero + 2.0f); + RwIm2DVertexSetScreenY(&Vertex2[0], zero + 2.0f); + RwIm2DVertexSetScreenZ(&Vertex2[0], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&Vertex2[0], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&Vertex2[0], 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetU(&Vertex2[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetV(&Vertex2[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetIntRGBA(&Vertex2[0], 255, 255, 255, 255); + + RwIm2DVertexSetScreenX(&Vertex2[1], 2.0f); + RwIm2DVertexSetScreenY(&Vertex2[1], ymax + 2.0f); + RwIm2DVertexSetScreenZ(&Vertex2[1], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&Vertex2[1], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&Vertex2[1], 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetU(&Vertex2[1], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetV(&Vertex2[1], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetIntRGBA(&Vertex2[1], 255, 255, 255, 255); + + RwIm2DVertexSetScreenX(&Vertex2[2], xmax + 2.0f); + RwIm2DVertexSetScreenY(&Vertex2[2], ymax + 2.0f); + RwIm2DVertexSetScreenZ(&Vertex2[2], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&Vertex2[2], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&Vertex2[2], 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetU(&Vertex2[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetV(&Vertex2[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetIntRGBA(&Vertex2[2], 255, 255, 255, 255); + + RwIm2DVertexSetScreenX(&Vertex2[3], xmax + 2.0f); + RwIm2DVertexSetScreenY(&Vertex2[3], zero + 2.0f); + RwIm2DVertexSetScreenZ(&Vertex2[3], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&Vertex2[3], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&Vertex2[3], 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetU(&Vertex2[3], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetV(&Vertex2[3], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetIntRGBA(&Vertex2[3], 255, 255, 255, 255); +} + +void +CMBlur::CreateImmediateModeData(RwCamera *cam, RwRect *rect, RwIm2DVertex *verts, RwRGBA color, float u1Off, float v1Off, float u2Off, float v2Off, float z, int fullTexture) +{ + float x1 = rect->x; + float y1 = rect->y; + float x2 = rect->w; + float y2 = rect->h; + + float u1, v1, u2, v2; + if(fullTexture){ + u1 = 0.0f; + v1 = 0.0f; + u2 = 1.0f; + v2 = 1.0f; + }else{ + if(RwRasterGetDepth(RwCameraGetRaster(cam)) == 16){ + x1 += HALFPX; + y1 += HALFPX; + x2 += HALFPX; + y2 += HALFPX; + }else{ + x1 -= HALFPX; + y1 -= HALFPX; + x2 -= HALFPX; + y2 -= HALFPX; + } + + int32 width = Pow(2.0f, int32(log2(RwRasterGetWidth (RwCameraGetRaster(cam))))+1); + int32 height = Pow(2.0f, int32(log2(RwRasterGetHeight(RwCameraGetRaster(cam))))+1); + u1 = x1/width + u1Off; + v1 = y1/height + v1Off; + u2 = x2/width + u2Off; + v2 = y2/height + v2Off; + u1 = Clamp(u1, 0.0f, 1.0f); + v1 = Clamp(v1, 0.0f, 1.0f); + u2 = Clamp(u2, 0.0f, 1.0f); + v2 = Clamp(v2, 0.0f, 1.0f); + } + + float recipz = 1.0f/z; + // TODO: CameraZ is wrong, what should we do? + RwIm2DVertexSetScreenX(&verts[0], x1); + RwIm2DVertexSetScreenY(&verts[0], y1); + RwIm2DVertexSetScreenZ(&verts[0], z); + RwIm2DVertexSetCameraZ(&verts[0], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&verts[0], recipz); + RwIm2DVertexSetU(&verts[0], u1, recipz); + RwIm2DVertexSetV(&verts[0], v1, recipz); + RwIm2DVertexSetIntRGBA(&verts[0], color.red, color.green, color.blue, color.alpha); + + RwIm2DVertexSetScreenX(&verts[1], x1); + RwIm2DVertexSetScreenY(&verts[1], y2); + RwIm2DVertexSetScreenZ(&verts[1], z); + RwIm2DVertexSetCameraZ(&verts[1], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&verts[1], recipz); + RwIm2DVertexSetU(&verts[1], u1, recipz); + RwIm2DVertexSetV(&verts[1], v2, recipz); + RwIm2DVertexSetIntRGBA(&verts[1], color.red, color.green, color.blue, color.alpha); + + RwIm2DVertexSetScreenX(&verts[2], x2); + RwIm2DVertexSetScreenY(&verts[2], y2); + RwIm2DVertexSetScreenZ(&verts[2], z); + RwIm2DVertexSetCameraZ(&verts[2], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&verts[2], recipz); + RwIm2DVertexSetU(&verts[2], u2, recipz); + RwIm2DVertexSetV(&verts[2], v2, recipz); + RwIm2DVertexSetIntRGBA(&verts[2], color.red, color.green, color.blue, color.alpha); + + RwIm2DVertexSetScreenX(&verts[3], x2); + RwIm2DVertexSetScreenY(&verts[3], y1); + RwIm2DVertexSetScreenZ(&verts[3], z); + RwIm2DVertexSetCameraZ(&verts[3], RwCameraGetNearClipPlane(cam)); + RwIm2DVertexSetRecipCameraZ(&verts[3], recipz); + RwIm2DVertexSetU(&verts[3], u2, recipz); + RwIm2DVertexSetV(&verts[3], v1, recipz); + RwIm2DVertexSetIntRGBA(&verts[3], color.red, color.green, color.blue, color.alpha); +} + +void +CMBlur::MotionBlurRender(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 blur, int32 type, uint32 bluralpha) +{ +#ifdef EXTENDED_COLOURFILTER + CPostFX::Render(cam, red, green, blue, blur, type, bluralpha); +#else + PUSH_RENDERGROUP("CMBlur::MotionBlurRender"); + RwRGBA color = { (RwUInt8)red, (RwUInt8)green, (RwUInt8)blue, (RwUInt8)blur }; +#ifdef GTA_PS2 + if( pFrontBuffer ) + OverlayRender(cam, pFrontBuffer, color, type, bluralpha); +#else + if(ms_bJustInitialised) + ms_bJustInitialised = false; + else + OverlayRender(cam, pFrontBuffer, color, type, bluralpha); + if(BlurOn){ + RwRasterPushContext(pFrontBuffer); + RwRasterRenderFast(RwCameraGetRaster(cam), 0, 0); + RwRasterPopContext(); + } +#endif + POP_RENDERGROUP(); +#endif +} + +static uint8 DrunkBlurRed = 128; +static uint8 DrunkBlurGreen = 128; +static uint8 DrunkBlurBlue = 128; +static int32 DrunkBlurIncrement = 1; + +void +CMBlur::OverlayRender(RwCamera *cam, RwRaster *raster, RwRGBA color, int32 type, int32 bluralpha) +{ + int r, g, b, a; + + r = color.red; + g = color.green; + b = color.blue; + a = color.alpha; + + DefinedState(); + + switch(type) + { + case MOTION_BLUR_SECURITY_CAM: + r = 0; + g = 255; + b = 0; + a = 128; + break; + case MOTION_BLUR_INTRO: + r = 100; + g = 220; + b = 230; + a = 158; + break; + case MOTION_BLUR_INTRO2: + r = 80; + g = 255; + b = 230; + a = 138; + break; + case MOTION_BLUR_INTRO3: + r = 255; + g = 60; + b = 60; + a = 200; + break; + case MOTION_BLUR_INTRO4: + r = 255; + g = 180; + b = 180; + a = 128; + break; + } + + if(!BlurOn){ + // gta clamps these to 255 (probably a macro or inlined function) + int ovR = r * 0.6f; + int ovG = g * 0.6f; + int ovB = b * 0.6f; + int ovA = type == MOTION_BLUR_SNIPER ? a : a*0.6f; + RwIm2DVertexSetIntRGBA(&Vertex[0], ovR, ovG, ovB, ovA); + RwIm2DVertexSetIntRGBA(&Vertex[1], ovR, ovG, ovB, ovA); + RwIm2DVertexSetIntRGBA(&Vertex[2], ovR, ovG, ovB, ovA); + RwIm2DVertexSetIntRGBA(&Vertex[3], ovR, ovG, ovB, ovA); + }else{ + RwIm2DVertexSetIntRGBA(&Vertex2[0], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex[0], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex2[1], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex[1], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex2[2], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex[2], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex2[3], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex[3], r, g, b, a); + } + + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, raster); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + + if(BlurOn){ + if(type == MOTION_BLUR_SNIPER){ + RwIm2DVertexSetIntRGBA(&Vertex2[0], r, g, b, 80); + RwIm2DVertexSetIntRGBA(&Vertex2[1], r, g, b, 80); + RwIm2DVertexSetIntRGBA(&Vertex2[2], r, g, b, 80); + RwIm2DVertexSetIntRGBA(&Vertex2[3], r, g, b, 80); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + pBufVertCount = 0; + }else{ + RwIm2DVertexSetIntRGBA(&Vertex2[0], r*2, g*2, b*2, 30); + RwIm2DVertexSetIntRGBA(&Vertex2[1], r*2, g*2, b*2, 30); + RwIm2DVertexSetIntRGBA(&Vertex2[2], r*2, g*2, b*2, 30); + RwIm2DVertexSetIntRGBA(&Vertex2[3], r*2, g*2, b*2, 30); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex2, 4, Index, 6); + + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + + RwIm2DVertexSetIntRGBA(&Vertex2[0], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex[0], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex2[1], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex[1], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex2[2], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex[2], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex2[3], r, g, b, a); + RwIm2DVertexSetIntRGBA(&Vertex[3], r, g, b, a); + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6); + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex2, 4, Index, 6); + } + } + + int DrunkBlurAlpha = 175.0f * Drunkness; + if(DrunkBlurAlpha != 0){ + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + if(BlurOn){ + RwIm2DVertexSetIntRGBA(&Vertex[0], 255, 255, 255, DrunkBlurAlpha); + RwIm2DVertexSetIntRGBA(&Vertex[1], 255, 255, 255, DrunkBlurAlpha); + RwIm2DVertexSetIntRGBA(&Vertex[2], 255, 255, 255, DrunkBlurAlpha); + RwIm2DVertexSetIntRGBA(&Vertex[3], 255, 255, 255, DrunkBlurAlpha); + }else{ + RwIm2DVertexSetIntRGBA(&Vertex[0], DrunkBlurRed, DrunkBlurGreen, DrunkBlurBlue, DrunkBlurAlpha); + RwIm2DVertexSetIntRGBA(&Vertex[1], DrunkBlurRed, DrunkBlurGreen, DrunkBlurBlue, DrunkBlurAlpha); + RwIm2DVertexSetIntRGBA(&Vertex[2], DrunkBlurRed, DrunkBlurGreen, DrunkBlurBlue, DrunkBlurAlpha); + RwIm2DVertexSetIntRGBA(&Vertex[3], DrunkBlurRed, DrunkBlurGreen, DrunkBlurBlue, DrunkBlurAlpha); + if(DrunkBlurIncrement){ + if(DrunkBlurRed < 255) DrunkBlurRed++; + if(DrunkBlurGreen < 255) DrunkBlurGreen++; + if(DrunkBlurBlue < 255) DrunkBlurBlue++; + if(DrunkBlurRed == 255) + DrunkBlurIncrement = 0; + }else{ + if(DrunkBlurRed > 128) DrunkBlurRed--; + if(DrunkBlurGreen > 128) DrunkBlurGreen--; + if(DrunkBlurBlue > 128) DrunkBlurBlue--; + if(DrunkBlurRed == 128) + DrunkBlurIncrement = 1; + } + } + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6); + } + + if(type != MOTION_BLUR_SNIPER) + OverlayRenderFx(cam, pFrontBuffer); + + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); +} + +void +CMBlur::SetDrunkBlur(float drunkness) +{ + Drunkness = Clamp(drunkness, 0.0f, 1.0f); +} + +void +CMBlur::ClearDrunkBlur() +{ + Drunkness = 0.0f; + CTimer::SetTimeScale(1.0f); +} + +#define NUM_RENDER_FX 64 + +static RwRect fxRect[NUM_RENDER_FX]; +static FxType fxType[NUM_RENDER_FX]; +static float fxZ[NUM_RENDER_FX]; + +bool +CMBlur::PosInside(RwRect *rect, float x1, float y1, float x2, float y2) +{ + if((rect->x < x1 - 10.0f || rect->x > x2 + 10.0f || rect->y < y1 - 10.0f || rect->y > y2 + 10.0f) && + (rect->w < x1 - 10.0f || rect->w > x2 + 10.0f || rect->h < y1 - 10.0f || rect->h > y2 + 10.0f) && + (rect->x < x1 - 10.0f || rect->x > x2 + 10.0f || rect->h < y1 - 10.0f || rect->h > y2 + 10.0f) && + (rect->w < x1 - 10.0f || rect->w > x2 + 10.0f || rect->y < y1 - 10.0f || rect->y > y2 + 10.0f)) + return false; + return true; +} + +bool +CMBlur::AddRenderFx(RwCamera *cam, RwRect *rect, float z, FxType type) +{ + if(pBufVertCount >= NUM_RENDER_FX) + return false; + + rect->x = Max(rect->x, 0); + rect->y = Max(rect->y, 0); + rect->w = Min(rect->w, SCREEN_WIDTH); + rect->h = Min(rect->h, SCREEN_HEIGHT); + if(rect->x >= rect->w || rect->y >= rect->h) + return false; + + switch(type){ + case FXTYPE_WATER1: + case FXTYPE_WATER2: + case FXTYPE_BLOOD1: + case FXTYPE_BLOOD2: + case FXTYPE_HEATHAZE: // code seems to be duplicated for this case + for(int i = 0; i < pBufVertCount; i++) + if(fxType[i] == type && PosInside(rect, fxRect[i].x-10.0f, fxRect[i].y-10.0f, fxRect[i].w+10.0f, fxRect[i].h+10.0f)) + return false; + // TODO: fix aspect ratio scaling + // radar + if(PosInside(rect, 40.0f, SCREEN_SCALE_FROM_BOTTOM(116.0f), 40.0f + SCREEN_SCALE_X(94.0f), SCREEN_SCALE_FROM_BOTTOM(116.0f - 76.0f))) + return false; + // HUD + if(PosInside(rect, 400.0f, 0.0f, SCREEN_WIDTH, 90.0f)) + return false; + // vehicle name + if(CHud::m_VehicleState != 0 && PosInside(rect, SCREEN_WIDTH/2, 350.0f, SCREEN_WIDTH, SCREEN_HEIGHT)) + return false; + // zone name + if(CHud::m_ZoneState != 0 && PosInside(rect, SCREEN_WIDTH/2, 350.0f, SCREEN_WIDTH, SCREEN_HEIGHT)) + return false; + break; + } + + fxRect[pBufVertCount] = *rect; + fxZ[pBufVertCount] = z; + fxType[pBufVertCount] = type; + pBufVertCount++; + + return true; +} + +void +CMBlur::OverlayRenderFx(RwCamera *cam, RwRaster *frontBuf) +{ + bool drawWaterDrops = false; + RwIm2DVertex verts[4]; + int red = (0.75f*CTimeCycle::GetDirectionalRed() + CTimeCycle::GetAmbientRed())*0.55f * 255; + int green = (0.75f*CTimeCycle::GetDirectionalGreen() + CTimeCycle::GetAmbientGreen())*0.55f * 255; + int blue = (0.75f*CTimeCycle::GetDirectionalBlue() + CTimeCycle::GetAmbientBlue())*0.55f * 255; + red = Clamp(red, 0, 255); + green = Clamp(green, 0, 255); + blue = Clamp(blue, 0, 255); + + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + +#ifdef LIBRW + rw::SetRenderState(rw::STENCILENABLE, TRUE); +#else + RwD3D8SetRenderState(D3DRS_STENCILENABLE, TRUE); +#endif + + for(int i = 0; i < pBufVertCount; i++) + switch(fxType[i]){ + case FXTYPE_WATER1: + case FXTYPE_WATER2: + case FXTYPE_BLOOD1: + case FXTYPE_BLOOD2: { + drawWaterDrops = true; + int32 width = Pow(2.0f, int32(log2(RwRasterGetWidth (RwCameraGetRaster(cam))))+1); + int32 height = Pow(2.0f, int32(log2(RwRasterGetHeight(RwCameraGetRaster(cam))))+1); + + float u1Off = (fxRect[i].w - fxRect[i].x)/width; + float u2Off = u1Off - (fxRect[i].w - fxRect[i].x + 0.5f)*0.66f/width; + float halfHeight = (fxRect[i].h - fxRect[i].y + 0.5f)*0.25f/height; + + if(RwRasterGetDepth(RwCameraGetRaster(cam)) == 16){ + if(fxType[i] == FXTYPE_BLOOD1 || fxType[i] == FXTYPE_BLOOD2) + CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(255, 0, 0, 128), 0.0f, 0.0f, 0.0f, 0.0f, fxZ[i], true); + else + CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(32, 32, 32, 225), 0.0f, 0.0f, 0.0f, 0.0f, fxZ[i], true); + }else{ + CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(32, 32, 32, 225), 0.0f, 0.0f, 0.0f, 0.0f, fxZ[i], true); + } + + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpDotRaster); +#ifdef LIBRW + rw::SetRenderState(rw::STENCILFUNCTION, rw::STENCILALWAYS); + rw::SetRenderState(rw::STENCILFUNCTIONREF, 1); + rw::SetRenderState(rw::STENCILFUNCTIONMASK, 0xFFFFFFFF); + rw::SetRenderState(rw::STENCILFUNCTIONWRITEMASK, 0xFFFFFFFF); + rw::SetRenderState(rw::STENCILZFAIL, rw::STENCILKEEP); + rw::SetRenderState(rw::STENCILFAIL, rw::STENCILKEEP); + rw::SetRenderState(rw::STENCILPASS, rw::STENCILREPLACE); +#else + RwD3D8SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS); + RwD3D8SetRenderState(D3DRS_STENCILREF, 1); + RwD3D8SetRenderState(D3DRS_STENCILMASK, 0xFFFFFFFF); + RwD3D8SetRenderState(D3DRS_STENCILWRITEMASK, 0xFFFFFFFF); + RwD3D8SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP); + RwD3D8SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP); + RwD3D8SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE); +#endif + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, verts, 4, Index, 6); + + if(RwRasterGetDepth(RwCameraGetRaster(cam)) != 16){ + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, frontBuf); +#ifdef LIBRW + rw::SetRenderState(rw::STENCILFUNCTION, rw::STENCILEQUAL); + rw::SetRenderState(rw::STENCILPASS, rw::STENCILKEEP); +#else + RwD3D8SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL); + RwD3D8SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP); +#endif + if(BlurOn){ + if(fxType[i] == FXTYPE_BLOOD1 || fxType[i] == FXTYPE_BLOOD2) + CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(255, 0, 0, 255), u1Off, 0.0f+halfHeight, u2Off, 0.0f-halfHeight, fxZ[i], false); + else + CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(225, 225, 225, 160), u1Off, 0.0f+halfHeight, u2Off, 0.0f-halfHeight, fxZ[i], false); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDDESTALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVDESTALPHA); + }else{ + if(fxType[i] == FXTYPE_BLOOD1 || fxType[i] == FXTYPE_BLOOD2) + CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(255, 0, 0, 128), u1Off, 0.0f+halfHeight, u2Off, 0.0f-halfHeight, fxZ[i], false); + else + CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(128, 128, 128, 32), u1Off, 0.0f+halfHeight, u2Off, 0.0f-halfHeight, fxZ[i], false); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + } + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, verts, 4, Index, 6); + } + break; + } + case FXTYPE_SPLASH1: + case FXTYPE_SPLASH2: + case FXTYPE_SPLASH3: + drawWaterDrops = true; + break; + + case FXTYPE_HEATHAZE: + if(TheCamera.GetScreenFadeStatus() == FADE_0 && frontBuf){ + int alpha = FrontEndMenuManager.m_PrefsBrightness > 255 ? + FrontEndMenuManager.m_PrefsBrightness - 90 : + FrontEndMenuManager.m_PrefsBrightness - 130; + alpha = Clamp(alpha, 16, 200)/2; + + CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(0, 0, 0, alpha), 0.0f, 0.0f, 0.0f, 0.0f, fxZ[i], true); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpHeatHazeRaster); +#ifdef LIBRW + rw::SetRenderState(rw::STENCILFUNCTION, rw::STENCILALWAYS); + rw::SetRenderState(rw::STENCILFUNCTIONREF, 1); + rw::SetRenderState(rw::STENCILFUNCTIONMASK, 0xFFFFFFFF); + rw::SetRenderState(rw::STENCILFUNCTIONWRITEMASK, 0xFFFFFFFF); + rw::SetRenderState(rw::STENCILZFAIL, rw::STENCILKEEP); + rw::SetRenderState(rw::STENCILFAIL, rw::STENCILKEEP); + rw::SetRenderState(rw::STENCILPASS, rw::STENCILREPLACE); +#else + RwD3D8SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS); + RwD3D8SetRenderState(D3DRS_STENCILREF, 1); + RwD3D8SetRenderState(D3DRS_STENCILMASK, 0xFFFFFFFF); + RwD3D8SetRenderState(D3DRS_STENCILWRITEMASK, 0xFFFFFFFF); + RwD3D8SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP); + RwD3D8SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP); + RwD3D8SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE); +#endif + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, verts, 4, Index, 6); + + CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(255, 255, 255, alpha), + CGeneral::GetRandomNumberInRange(-0.002f, 0.002f), + CGeneral::GetRandomNumberInRange(-0.002f, 0.002f), + CGeneral::GetRandomNumberInRange(-0.002f, 0.002f), + CGeneral::GetRandomNumberInRange(-0.002f, 0.002f), + fxZ[i], false); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, frontBuf); +#ifdef LIBRW + rw::SetRenderState(rw::STENCILFUNCTION, rw::STENCILEQUAL); + rw::SetRenderState(rw::STENCILPASS, rw::STENCILKEEP); +#else + RwD3D8SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL); + RwD3D8SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP); +#endif + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, verts, 4, Index, 6); + } + break; + } +#ifdef LIBRW + rw::SetRenderState(rw::STENCILENABLE, FALSE); +#else + RwD3D8SetRenderState(D3DRS_STENCILENABLE, FALSE); +#endif + + if(drawWaterDrops){ + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + + // Draw drops + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpRainDripRaster[0]); + for(int i = 0; i < pBufVertCount; i++) + if(fxType[i] == FXTYPE_WATER1 || fxType[i] == FXTYPE_BLOOD1){ + CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(red, green, blue, fxType[i] == FXTYPE_BLOOD1 ? 255 : 192), + 0.0f, 0.0f, 0.0f, 0.0f, fxZ[i], true); + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, verts, 4, Index, 6); + } + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpRainDripRaster[1]); + for(int i = 0; i < pBufVertCount; i++) + if(fxType[i] == FXTYPE_WATER2 || fxType[i] == FXTYPE_BLOOD2){ + CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(red, green, blue, fxType[i] == FXTYPE_BLOOD2 ? 255 : 192), + 0.0f, 0.0f, 0.0f, 0.0f, fxZ[i], true); + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, verts, 4, Index, 6); + } + + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpCarSplashRaster[0]); + for(int i = 0; i < pBufVertCount; i++) + if(fxType[i] == FXTYPE_SPLASH1 || fxType[i] == FXTYPE_SPLASH2 || fxType[i] == FXTYPE_SPLASH3){ + CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(200, 200, 200, 255), + 0.0f, 0.0f, 0.0f, 0.0f, fxZ[i], true); + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, verts, 4, Index, 6); + } + + // Darken the water drops + int alpha = 192*0.5f; + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpRainDripDarkRaster[0]); + for(int i = 0; i < pBufVertCount; i++) + if(fxType[i] == FXTYPE_WATER1){ + CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(red, green, blue, alpha), + 0.0f, 0.0f, 0.0f, 0.0f, fxZ[i], true); + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, verts, 4, Index, 6); + } + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpRainDripDarkRaster[1]); + for(int i = 0; i < pBufVertCount; i++) + if(fxType[i] == FXTYPE_WATER2){ + CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(red, green, blue, alpha), + 0.0f, 0.0f, 0.0f, 0.0f, fxZ[i], true); + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, verts, 4, Index, 6); + } + } + + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + pBufVertCount = 0; +} diff --git a/src/miami/renderer/MBlur.h b/src/miami/renderer/MBlur.h new file mode 100644 index 00000000..3dc53082 --- /dev/null +++ b/src/miami/renderer/MBlur.h @@ -0,0 +1,39 @@ +#pragma once + +enum FxType +{ + FXTYPE_WATER1, + FXTYPE_WATER2, + FXTYPE_BLOOD1, + FXTYPE_BLOOD2, + FXTYPE_HEATHAZE, + FXTYPE_SPLASH1, + FXTYPE_SPLASH2, + FXTYPE_SPLASH3 +}; + +class CMBlur +{ +public: + static RwRaster *pFrontBuffer; + static bool ms_bJustInitialised; + static bool ms_bScaledBlur; + static bool BlurOn; + static float Drunkness; + + static int32 pBufVertCount; + +public: + static RwBool MotionBlurOpen(RwCamera *cam); + static RwBool MotionBlurClose(void); + static void CreateImmediateModeData(RwCamera *cam, RwRect *rect); + static void CreateImmediateModeData(RwCamera *cam, RwRect *rect, RwIm2DVertex *verts, RwRGBA color, float u1Off, float v1Off, float u2Off, float v2Off, float z, int fullTexture); + static void MotionBlurRender(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 blur, int32 type, uint32 bluralpha); + static void OverlayRender(RwCamera *cam, RwRaster *raster, RwRGBA color, int32 type, int32 bluralpha); + static void SetDrunkBlur(float drunkness); + static void ClearDrunkBlur(); + + static bool PosInside(RwRect *rect, float x1, float y1, float x2, float y2); + static bool AddRenderFx(RwCamera *cam, RwRect *rect, float z, FxType type); + static void OverlayRenderFx(RwCamera *cam, RwRaster *frontBuf); +}; diff --git a/src/miami/renderer/Occlusion.cpp b/src/miami/renderer/Occlusion.cpp new file mode 100644 index 00000000..ec7101a6 --- /dev/null +++ b/src/miami/renderer/Occlusion.cpp @@ -0,0 +1,530 @@ +#include "common.h" + +#include "main.h" +#include "Entity.h" +#include "Occlusion.h" +#include "Game.h" +#include "Camera.h" +#include "Vector.h" +#include "Draw.h" +#include "Timer.h" +#include "RwHelper.h" +#include "VarConsole.h" + +int32 COcclusion::NumOccludersOnMap; +int16 COcclusion::FarAwayList; +int16 COcclusion::NearbyList; +int16 COcclusion::ListWalkThroughFA; +int16 COcclusion::PreviousListWalkThroughFA; +int16 COcclusion::NumActiveOccluders; +COccluder COcclusion::aOccluders[NUMOCCLUSIONVOLUMES]; +CActiveOccluder COcclusion::aActiveOccluders[NUMACTIVEOCCLUDERS]; + +CVector gCenterOnScreen; + +float gMinYInOccluder; +float gMinXInOccluder; +float gMaxYInOccluder; +float gMaxXInOccluder; + +bool gOccluderCoorsValid[8]; +CVector gOccluderCoorsOnScreen[8]; +CVector gOccluderCoors[8]; + +#ifndef MASTER +bool bDispayOccDebugStuff; // disPAY, yeah +#endif + +void +COcclusion::Init(void) +{ + NumOccludersOnMap = 0; +#ifndef MASTER + VarConsole.Add("Occlusion debug", &bDispayOccDebugStuff, true); +#endif + FarAwayList = -1; + NearbyList = -1; + ListWalkThroughFA = -1; + PreviousListWalkThroughFA = -1; +} + +void +COcclusion::AddOne(float x, float y, float z, float width, float length, float height, float angle) +{ + if(NumOccludersOnMap >= NUMOCCLUSIONVOLUMES) + return; + + aOccluders[NumOccludersOnMap].x = x; + aOccluders[NumOccludersOnMap].y = y; + aOccluders[NumOccludersOnMap].z = z; + aOccluders[NumOccludersOnMap].width = width; + aOccluders[NumOccludersOnMap].length = length; + aOccluders[NumOccludersOnMap].height = height; + while(angle < 0.0f) angle += 360.0f; + while(angle > 360.0f) angle -= 360.0f; + aOccluders[NumOccludersOnMap].angle = angle/360.0f * UINT16_MAX; + aOccluders[NumOccludersOnMap].listIndex = FarAwayList; + FarAwayList = NumOccludersOnMap++; +} + +bool +COccluder::NearCamera() { + return (TheCamera.GetPosition() - CVector(x, y, z)).Magnitude() - (Max(width, length) / 2.0f) < 250.0f; +} + +bool +DoesInfiniteLineCrossFiniteLine(float p1X, float p1Y, float p2X, float p2Y, float lineX, float lineY, float lineDX, float lineDY) +{ + float side1 = (p1X - lineX) * lineDY - (p1Y - lineY) * lineDX; + float side2 = (p2X - lineX) * lineDY - (p2Y - lineY) * lineDX; + return side1 * side2 < 0.0f; // if points lie on opposite sides of the infinte line, the line between them crosses it +} + +bool DoesInfiniteLineTouchScreen(float lineX, float lineY, float lineDX, float lineDY) { + if (lineX > 0.0f && lineY > 0.0f && SCREEN_WIDTH > lineX && SCREEN_HEIGHT > lineY) + return true; + + return (DoesInfiniteLineCrossFiniteLine(0.0f, 0.0f, SCREEN_WIDTH, 0.0f, lineX, lineY, lineDX, lineDY) || + DoesInfiniteLineCrossFiniteLine(0.0f, 0.0f, 0.0f, SCREEN_HEIGHT, lineX, lineY, lineDX, lineDY) || + DoesInfiniteLineCrossFiniteLine(SCREEN_WIDTH, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, lineX, lineY, lineDX, lineDY) || + DoesInfiniteLineCrossFiniteLine(0.0f, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT, lineX, lineY, lineDX, lineDY)); +} + +bool IsPointInsideLine(float lineX, float lineY, float lineDX, float lineDY, float pX, float pY, float area = 0.0f) { + return (pX - lineX) * lineDY - (pY - lineY) * lineDX >= area; +} + +bool CalcScreenCoors(CVector const &in, CVector *out, float *outw, float *outh) { + *out = TheCamera.m_viewMatrix * in; + + if (out->z <= 1.0f) return false; + + float recip = 1.0f / out->z; + out->x *= SCREEN_WIDTH * recip; + out->y *= SCREEN_HEIGHT * recip; + + float fovScale = DefaultFOV / CDraw::GetFOV(); + + *outw = fovScale * recip * SCREEN_WIDTH; + *outh = fovScale * recip * SCREEN_HEIGHT; + + return true; +} + +bool CalcScreenCoors(CVector const &in, CVector *out) { + *out = TheCamera.m_viewMatrix * in; + + if (out->z <= 1.0f) return false; + + float recip = 1.0f / out->z; + out->x *= SCREEN_WIDTH * recip; + out->y *= SCREEN_HEIGHT * recip; + + return true; +} + +bool +COccluder::ProcessLineSegment(int corner1, int corner2, CActiveOccluder *occl) { + if (!gOccluderCoorsValid[corner1] && !gOccluderCoorsValid[corner2]) + return false; + + float x1, y1, x2, y2; + + CVector p1, p2; + if (!gOccluderCoorsValid[corner1]) { + float clipDist1 = Abs((TheCamera.m_viewMatrix * gOccluderCoors[corner1]).z - 1.1f); + float clipDist2 = Abs((TheCamera.m_viewMatrix * gOccluderCoors[corner2]).z - 1.1f); + float ratio = clipDist2 / (clipDist1 + clipDist2); + CVector clippedCoors = (1.0f - ratio) * gOccluderCoors[corner2] + ratio * gOccluderCoors[corner1]; + + if (!CalcScreenCoors(clippedCoors, &p1, &x1, &y1)) + return true; + } + else { + p1 = gOccluderCoorsOnScreen[corner1]; + } + + if (!gOccluderCoorsValid[corner2]) { + float clipDist1 = Abs((TheCamera.m_viewMatrix * gOccluderCoors[corner1]).z - 1.1f); + float clipDist2 = Abs((TheCamera.m_viewMatrix * gOccluderCoors[corner2]).z - 1.1f); + float ratio = clipDist1 / (clipDist1 + clipDist2); + CVector clippedCoors = (1.0f - ratio) * gOccluderCoors[corner2] + ratio * gOccluderCoors[corner1]; + + if (!CalcScreenCoors(clippedCoors, &p2, &x2, &y2)) + return true; + } + else { + p2 = gOccluderCoorsOnScreen[corner2]; + } + + gMinXInOccluder = Min(Min(gMinXInOccluder, p1.x), p2.x); + gMaxXInOccluder = Max(Max(gMaxXInOccluder, p1.x), p2.x); + gMinYInOccluder = Min(Min(gMinYInOccluder, p1.y), p2.y); + gMaxYInOccluder = Max(Max(gMaxYInOccluder, p1.y), p2.y); + + CVector2D origin = p1; + CVector2D direction = p2 - p1; + + // Make sure lines are counter-clockwise around center + if (!IsPointInsideLine(origin.x, origin.y, direction.x, direction.y, gCenterOnScreen.x, gCenterOnScreen.y, 0.0f)) { + origin += direction; + direction *= -1.0f; + } + + float magnitude = direction.Magnitude(); + + occl->lines[occl->linesCount].origin = origin; + occl->lines[occl->linesCount].direction = direction / magnitude; + occl->lines[occl->linesCount].length = magnitude; + + if (!DoesInfiniteLineTouchScreen(origin.x, origin.y, direction.x, direction.y)) + return !IsPointInsideLine(origin.x, origin.y, direction.x, direction.y, SCREEN_WIDTH / 2.0f, SCREEN_HEIGHT / 2.0f, 0.0f); + + occl->linesCount++; + + return false; +} + +bool +COccluder::ProcessOneOccluder(CActiveOccluder *occl) { + float outX, outY; + + occl->linesCount = 0; + CVector pos(x, y, z); + + if (!CalcScreenCoors(pos, &gCenterOnScreen, &outX, &outY) || gCenterOnScreen.z < -150.0f || gCenterOnScreen.z > 300.0f) { + return false; + } + + occl->radius = Max(width, length) * 0.35f + gCenterOnScreen.z; + + CVector vec[3]; + + vec[0].x = length / 2.0f * Sin(GetAngle()); + vec[0].y = -length / 2.0f * Cos(GetAngle()); + vec[0].z = 0.0f; + + vec[1].x = width / 2.0f * Cos(GetAngle()); + vec[1].y = width / 2.0f * Sin(GetAngle()); + vec[1].z = 0.0f; + + vec[2].x = 0.0f; + vec[2].y = 0.0f; + vec[2].z = height / 2.0f; + + // Figure out if we see the front or back of a face + bool bFrontFace[6]; + for (int i = 0; i < 3; i++) { + bFrontFace[i*2+0] = DotProduct((pos + vec[i] - TheCamera.GetPosition()), vec[i]) < 0.0f; + bFrontFace[i*2+1] = DotProduct((pos - vec[i] - TheCamera.GetPosition()), -vec[i]) < 0.0f; + } + + //calculating vertices of a box + gOccluderCoors[0] = pos + vec[0] + vec[1] + vec[2]; + gOccluderCoors[1] = pos - vec[0] + vec[1] + vec[2]; + gOccluderCoors[2] = pos + vec[0] - vec[1] + vec[2]; + gOccluderCoors[3] = pos - vec[0] - vec[1] + vec[2]; + gOccluderCoors[4] = pos + vec[0] + vec[1] - vec[2]; + gOccluderCoors[5] = pos - vec[0] + vec[1] - vec[2]; + gOccluderCoors[6] = pos + vec[0] - vec[1] - vec[2]; + gOccluderCoors[7] = pos - vec[0] - vec[1] - vec[2]; + + for(int i = 0; i < 8; i++) + gOccluderCoorsValid[i] = CalcScreenCoors(gOccluderCoors[i], &gOccluderCoorsOnScreen[i], &outX, &outY); + + gMinYInOccluder = 999999.875f; + gMinXInOccluder = 999999.875f; + gMaxYInOccluder = -999999.875f; + gMaxXInOccluder = -999999.875f; + + // Between two differently facing sides we see an edge, so process those + if (bFrontFace[2] != bFrontFace[0] && ProcessLineSegment(0, 4, occl)) + return false; + if (bFrontFace[3] != bFrontFace[0] && ProcessLineSegment(2, 6, occl)) + return false; + if (bFrontFace[4] != bFrontFace[0] && ProcessLineSegment(0, 2, occl)) + return false; + if (bFrontFace[5] != bFrontFace[0] && ProcessLineSegment(4, 6, occl)) + return false; + if (bFrontFace[2] != bFrontFace[1] && ProcessLineSegment(1, 5, occl)) + return false; + if (bFrontFace[3] != bFrontFace[1] && ProcessLineSegment(3, 7, occl)) + return false; + if (bFrontFace[4] != bFrontFace[1] && ProcessLineSegment(1, 3, occl)) + return false; + if (bFrontFace[5] != bFrontFace[1] && ProcessLineSegment(5, 7, occl)) + return false; + if (bFrontFace[4] != bFrontFace[2] && ProcessLineSegment(0, 1, occl)) + return false; + if (bFrontFace[3] != bFrontFace[4] && ProcessLineSegment(2, 3, occl)) + return false; + if (bFrontFace[5] != bFrontFace[3] && ProcessLineSegment(6, 7, occl)) + return false; + if (bFrontFace[2] != bFrontFace[5] && ProcessLineSegment(4, 5, occl)) + return false; + + if (gMaxXInOccluder - gMinXInOccluder < SCREEN_WIDTH * 0.1f || + gMaxYInOccluder - gMinYInOccluder < SCREEN_HEIGHT * 0.07f) + return false; + + return true; +} + +bool +COcclusion::OccluderHidesBehind(CActiveOccluder *occl1, CActiveOccluder *occl2) { + for (int i = 0; i < occl1->linesCount; i++) { + for (int j = 0; j < occl2->linesCount; j++) { + if (!IsPointInsideLine(occl2->lines[j].origin.x, occl2->lines[j].origin.y, occl2->lines[j].direction.x, + occl2->lines[j].direction.y, occl1->lines[i].origin.x, occl1->lines[i].origin.y, 0.0f)) + return false; + + + if (!IsPointInsideLine(occl2->lines[j].origin.x, occl2->lines[j].origin.y, occl2->lines[j].direction.x, + occl2->lines[j].direction.y, (occl1->lines[i].origin.x + occl1->lines[i].direction.x * occl1->lines[i].length), + (occl1->lines[i].origin.y + occl1->lines[i].direction.y * occl1->lines[i].length), 0.0f)) + return false; + } + } + + return true; +} + +void +COcclusion::ProcessBeforeRendering(void) +{ + NumActiveOccluders = 0; + +#ifndef MASTER + if (gbModelViewer) + return; +#endif + + if (CGame::currArea != AREA_MAIN_MAP) + return; + + if (ListWalkThroughFA == -1) { + PreviousListWalkThroughFA = -1; + ListWalkThroughFA = FarAwayList; + } + + int i; + for (i = 0; i < 16 && ListWalkThroughFA != -1; i++) { + if (aOccluders[ListWalkThroughFA].NearCamera()) { + int prevListWalkThroughFA = ListWalkThroughFA; + + if (PreviousListWalkThroughFA == -1) { + FarAwayList = aOccluders[ListWalkThroughFA].listIndex; + } + else { + aOccluders[PreviousListWalkThroughFA].listIndex = aOccluders[ListWalkThroughFA].listIndex; + } + + int prevNearbyList = NearbyList; + ListWalkThroughFA = aOccluders[ListWalkThroughFA].listIndex; + NearbyList = prevListWalkThroughFA; + aOccluders[prevListWalkThroughFA].listIndex = prevNearbyList; + } + else { + PreviousListWalkThroughFA = ListWalkThroughFA; + ListWalkThroughFA = aOccluders[ListWalkThroughFA].listIndex; + } + } + + int prevNearbyList = -1; + int tmpNearbyList = NearbyList; + int indexTmpNearbyList, storeTmpNearbyList, prevFarAwayList; + while (tmpNearbyList != -1) + { + if (NumActiveOccluders < NUMACTIVEOCCLUDERS && aOccluders[tmpNearbyList].ProcessOneOccluder(&aActiveOccluders[NumActiveOccluders])) + ++NumActiveOccluders; + + indexTmpNearbyList = tmpNearbyList; + if (aOccluders[indexTmpNearbyList].NearCamera()) + { + prevNearbyList = tmpNearbyList; + tmpNearbyList = aOccluders[indexTmpNearbyList].listIndex; + + } + else + { + storeTmpNearbyList = tmpNearbyList; + if (prevNearbyList == -1) { + NearbyList = aOccluders[indexTmpNearbyList].listIndex; + } + else { + aOccluders[prevNearbyList].listIndex = aOccluders[indexTmpNearbyList].listIndex; + } + tmpNearbyList = aOccluders[indexTmpNearbyList].listIndex; + prevFarAwayList = FarAwayList; + FarAwayList = storeTmpNearbyList; + aOccluders[storeTmpNearbyList].listIndex = prevFarAwayList; + } + } + + for (i = 0; i < NumActiveOccluders; i++) { + for (int j = 0; j < NumActiveOccluders; j++) { + if (i != j && aActiveOccluders[j].radius < aActiveOccluders[i].radius) { + if (OccluderHidesBehind(&aActiveOccluders[i], &aActiveOccluders[j])) { + for (int k = i; k < NumActiveOccluders - 1; k++) { + for (int l = 0; l < aActiveOccluders[k + 1].linesCount; l++) + aActiveOccluders[k].lines[l] = aActiveOccluders[k + 1].lines[l]; + aActiveOccluders[k].linesCount = aActiveOccluders[k + 1].linesCount; + aActiveOccluders[k].radius = aActiveOccluders[k + 1].radius; + } + NumActiveOccluders--; + i--; + // Taken from Mobile! +#ifdef FIX_BUGS + if (i == -1) { + i = 0; + } +#endif + } + } + } + } +} + +bool CActiveOccluder::IsPointWithinOcclusionArea(float pX, float pY, float area) { + for (int i = 0; i < linesCount; i++) { + if (!IsPointInsideLine(lines[i].origin.x, lines[i].origin.y, lines[i].direction.x, lines[i].direction.y, pX, pY, area)) + return false; + } + + return true; +} + +bool COcclusion::IsAABoxOccluded(CVector pos, float width, float length, float height) { + + CVector coors; + float outW, outH; + + if (!NumActiveOccluders || !CalcScreenCoors(pos, &coors, &outW, &outH)) + return false; + + float side = CVector(width, length, height).Magnitude() / 4.0f; + float area = Max(outW, outH) * side; + + CVector minCorner, maxCorner; + + minCorner.x = pos.x - width / 2.0f; + minCorner.y = pos.y - length / 2.0f; + minCorner.z = pos.z - height / 2.0f; + + maxCorner.x = pos.x + width / 2.0f; + maxCorner.y = pos.y + length / 2.0f; + maxCorner.z = pos.z + height / 2.0f; + + for (int i = 0; i < NumActiveOccluders; i++) { + if (coors.z - (side * 0.85f) > aActiveOccluders[i].radius) { + if (aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, area)) + return true; + + if (aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) { + if (CalcScreenCoors(minCorner, &coors) && !aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) continue; + if (CalcScreenCoors(CVector(maxCorner.x, maxCorner.y, minCorner.z), &coors) && !aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) continue; + if (CalcScreenCoors(CVector(maxCorner.x, minCorner.y, maxCorner.z), &coors) && !aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) continue; + if (CalcScreenCoors(CVector(minCorner.x, maxCorner.y, maxCorner.z), &coors, &outW, &outH) && !aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) continue; + + return true; + } + } + } + + return false; +} + +bool COcclusion::IsPositionOccluded(CVector pos, float side) { + + CVector coors; + float width, height; + + if (!NumActiveOccluders || !CalcScreenCoors(pos, &coors, &width, &height)) + return false; + + float area = Max(width, height) * side; + + for (int i = 0; i < NumActiveOccluders; i++) { + if (coors.z - (side * 0.85f) > aActiveOccluders[i].radius) + if (aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, area)) + return true; + } + + return false; +} + +#ifndef MASTER +#include "Lines.h" + +RwIm2DVertex vertexbufferT[2]; + +void COcclusion::Render() { + if (!bDispayOccDebugStuff || !(CTimer::GetTimeInMilliseconds() & 0x200)) + return; + + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, FALSE); + + float recipz = 1.0f/RwCameraGetNearClipPlane(Scene.camera); + for (int i = 0; i < NumActiveOccluders; i++) { + for (int j = 0; j < aActiveOccluders[i].linesCount; j++) { + RwIm2DVertexSetScreenX(&vertexbufferT[0], aActiveOccluders[i].lines[j].origin.x); + RwIm2DVertexSetScreenY(&vertexbufferT[0], aActiveOccluders[i].lines[j].origin.y); + RwIm2DVertexSetScreenZ(&vertexbufferT[0], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&vertexbufferT[0], RwCameraGetNearClipPlane(Scene.camera)); + RwIm2DVertexSetRecipCameraZ(&vertexbufferT[0], recipz); + + RwIm2DVertexSetScreenX(&vertexbufferT[1], + aActiveOccluders[i].lines[j].origin.x + aActiveOccluders[i].lines[j].direction.x * aActiveOccluders[i].lines[j].length); + RwIm2DVertexSetScreenY(&vertexbufferT[1], + aActiveOccluders[i].lines[j].origin.y + aActiveOccluders[i].lines[j].direction.y * aActiveOccluders[i].lines[j].length); + RwIm2DVertexSetScreenZ(&vertexbufferT[1], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetCameraZ(&vertexbufferT[1], RwCameraGetNearClipPlane(Scene.camera)); + RwIm2DVertexSetRecipCameraZ(&vertexbufferT[1], recipz); + + RwIm2DVertexSetIntRGBA(&vertexbufferT[0], 255, 255, 0, 255); + RwIm2DVertexSetIntRGBA(&vertexbufferT[1], 255, 255, 0, 255); + RwIm2DRenderLine(vertexbufferT, 2, 0, 1); + } + } + + DefinedState(); +} +#endif + +bool CEntity::IsEntityOccluded(void) { + + CVector coors; + float width, height; + + if (COcclusion::NumActiveOccluders == 0 || !CalcScreenCoors(GetBoundCentre(), &coors, &width, &height)) + return false; + + float area = Max(width, height) * GetBoundRadius() * 0.9f; + + for (int i = 0; i < COcclusion::NumActiveOccluders; i++) { + if (coors.z - (GetBoundRadius() * 0.85f) > COcclusion::aActiveOccluders[i].radius) { + if (COcclusion::aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, area)) { + return true; + } + + if (COcclusion::aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) { + CVector min = m_matrix * CModelInfo::GetColModel(m_modelIndex)->boundingBox.min; + CVector max = m_matrix * CModelInfo::GetColModel(m_modelIndex)->boundingBox.max; + + if (CalcScreenCoors(min, &coors) && !COcclusion::aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) continue; + if (CalcScreenCoors(CVector(max.x, max.y, min.z), &coors) && !COcclusion::aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) continue; + if (CalcScreenCoors(CVector(max.x, min.y, max.z), &coors) && !COcclusion::aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) continue; + if (CalcScreenCoors(CVector(min.x, max.y, max.z), &coors) && !COcclusion::aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) continue; + + return true; + } + } + } + + return false; +} \ No newline at end of file diff --git a/src/miami/renderer/Occlusion.h b/src/miami/renderer/Occlusion.h new file mode 100644 index 00000000..e0edef53 --- /dev/null +++ b/src/miami/renderer/Occlusion.h @@ -0,0 +1,62 @@ +#pragma once + +struct ActiveOccluderLine { + CVector2D origin; + CVector2D direction; + float length; +}; + +class CActiveOccluder { + +public: + ActiveOccluderLine lines[6]; + int32 linesCount; + float radius; + + bool IsPointWithinOcclusionArea(float x, float y, float area); +}; + +class COccluder +{ +public: + int16 length, width, height; + int16 x, y, z; + uint16 angle; + int16 listIndex; + + bool NearCamera(); + bool ProcessOneOccluder(CActiveOccluder *occl); + bool ProcessLineSegment(int corner1, int corner2, CActiveOccluder* occl); + float GetAngle(void) { return angle*TWOPI/UINT16_MAX; } +}; + +class COcclusion +{ +public: + static int32 NumOccludersOnMap; + static int16 FarAwayList; + static int16 NearbyList; + static int16 ListWalkThroughFA; + static int16 PreviousListWalkThroughFA; + static int16 NumActiveOccluders; + + static COccluder aOccluders[NUMOCCLUSIONVOLUMES]; + static CActiveOccluder aActiveOccluders[NUMACTIVEOCCLUDERS]; + + static void Init(void); + static void AddOne(float x, float y, float z, float width, float length, float height, float angle); + static void ProcessBeforeRendering(void); + static bool OccluderHidesBehind(CActiveOccluder *occl1, CActiveOccluder *occl2); + static bool IsAABoxOccluded(CVector pos, float width, float length, float height); + static bool IsPositionOccluded(CVector pos, float side); +#ifndef MASTER + static void Render(); +#endif +}; + +bool CalcScreenCoors(CVector const &in, CVector *out, float *outw, float *outh); +bool CalcScreenCoors(CVector const &in, CVector *out); + +#ifndef MASTER +extern bool bDispayOccDebugStuff; +#endif \ No newline at end of file diff --git a/src/miami/renderer/Particle.cpp b/src/miami/renderer/Particle.cpp new file mode 100644 index 00000000..461a10a6 --- /dev/null +++ b/src/miami/renderer/Particle.cpp @@ -0,0 +1,2534 @@ +#include "common.h" + +#include "main.h" +#include "General.h" +#include "Timer.h" +#include "TxdStore.h" +#include "Sprite.h" +#include "Camera.h" +#include "Clock.h" +#include "Collision.h" +#include "World.h" +#include "Shadows.h" +#include "Replay.h" +#include "Stats.h" +#include "Weather.h" +#include "MBlur.h" +#include "main.h" +#include "AudioScriptObject.h" +#include "ParticleObject.h" +#include "Particle.h" +#include "soundlist.h" +#include "SaveBuf.h" +#include "debugmenu.h" + +#define MAX_PARTICLES_ON_SCREEN (750) + + +//(5) +#define MAX_SMOKE_FILES ARRAY_SIZE(SmokeFiles) + +//(5) +#define MAX_RUBBER_FILES ARRAY_SIZE(RubberFiles) +//(5) +#define MAX_RAINSPLASH_FILES ARRAY_SIZE(RainSplashFiles) +//(3) +#define MAX_WATERSPRAY_FILES ARRAY_SIZE(WatersprayFiles) +//(6) +#define MAX_EXPLOSIONMEDIUM_FILES ARRAY_SIZE(ExplosionMediumFiles) +//(4) +#define MAX_GUNFLASH_FILES ARRAY_SIZE(GunFlashFiles) +//(2) +#define MAX_RAINSPLASHUP_FILES ARRAY_SIZE(RainSplashupFiles) +//(4) +#define MAX_BIRDFRONT_FILES ARRAY_SIZE(BirdfrontFiles) +//(8) +#define MAX_BOAT_FILES ARRAY_SIZE(BoatFiles) +//(4) +#define MAX_CARDEBRIS_FILES ARRAY_SIZE(CardebrisFiles) +//(4) +#define MAX_CARSPLASH_FILES ARRAY_SIZE(CarsplashFiles) + +#define MAX_RAINDRIP_FILES (2) + +#define MAX_LEAF_FILES (2) + + +const char SmokeFiles[][6+1] = +{ + "smoke1", + "smoke2", + "smoke3", + "smoke4", + "smoke5" +}; + + +const char RubberFiles[][7+1] = +{ + "rubber1", + "rubber2", + "rubber3", + "rubber4", + "rubber5" +}; + +const char RainSplashFiles[][7+1] = +{ + "splash1", + "splash2", + "splash3", + "splash4", + "splash5" +}; + +const char WatersprayFiles[][11+1] = +{ + "waterspray1", + "waterspray2", + "waterspray3" +}; + +const char ExplosionMediumFiles[][7+1] = +{ + "explo01", + "explo02", + "explo03", + "explo04", + "explo05", + "explo06" +}; + +const char GunFlashFiles[][9+1] = +{ + "gunflash1", + "gunflash2", + "gunflash3", + "gunflash4" +}; + +const char RainSplashupFiles[][10+1] = +{ + "splash_up1", + "splash_up2" +}; + +const char BirdfrontFiles[][8+1] = +{ + "birdf_01", + "birdf_02", + "birdf_03", + "birdf_04" +}; + +const char BoatFiles[][8+1] = +{ + "boats_01", + "boats_02", + "boats_03", + "boats_04", + "boats_05", + "boats_06", + "boats_07", + "boats_08" +}; + +const char CardebrisFiles[][12+1] = +{ + "cardebris_01", + "cardebris_02", + "cardebris_03", + "cardebris_04" +}; + +const char CarsplashFiles[][12+1] = +{ + "carsplash_01", + "carsplash_02", + "carsplash_03", + "carsplash_04" +}; + +CParticle gParticleArray[MAX_PARTICLES_ON_SCREEN]; + +RwTexture *gpSmokeTex[MAX_SMOKE_FILES]; +RwTexture *gpSmoke2Tex; +RwTexture *gpRubberTex[MAX_RUBBER_FILES]; +RwTexture *gpRainSplashTex[MAX_RAINSPLASH_FILES]; +RwTexture *gpWatersprayTex[MAX_WATERSPRAY_FILES]; +RwTexture *gpExplosionMediumTex[MAX_EXPLOSIONMEDIUM_FILES]; +RwTexture *gpGunFlashTex[MAX_GUNFLASH_FILES]; +RwTexture *gpRainSplashupTex[MAX_RAINSPLASHUP_FILES]; +RwTexture *gpBirdfrontTex[MAX_BIRDFRONT_FILES]; +RwTexture *gpBoatTex[MAX_BOAT_FILES]; +RwTexture *gpCarDebrisTex[MAX_CARDEBRIS_FILES]; +RwTexture *gpCarSplashTex[MAX_CARSPLASH_FILES]; + +RwTexture *gpBoatWakeTex; +RwTexture *gpFlame1Tex; +RwTexture *gpFlame5Tex; +RwTexture *gpRainDropSmallTex; +RwTexture *gpBloodTex; +RwTexture *gpLeafTex[MAX_LEAF_FILES]; +RwTexture *gpCloudTex1; +RwTexture *gpCloudTex4; +RwTexture *gpBloodSmallTex; +RwTexture *gpGungeTex; +RwTexture *gpCollisionSmokeTex; +RwTexture *gpBulletHitTex; +RwTexture *gpGunShellTex; +RwTexture *gpPointlightTex; + +RwRaster *gpSmokeRaster[MAX_SMOKE_FILES]; +RwRaster *gpSmoke2Raster; +RwRaster *gpRubberRaster[MAX_RUBBER_FILES]; +RwRaster *gpRainSplashRaster[MAX_RAINSPLASH_FILES]; +RwRaster *gpWatersprayRaster[MAX_WATERSPRAY_FILES]; +RwRaster *gpExplosionMediumRaster[MAX_EXPLOSIONMEDIUM_FILES]; +RwRaster *gpGunFlashRaster[MAX_GUNFLASH_FILES]; +RwRaster *gpRainSplashupRaster[MAX_RAINSPLASHUP_FILES]; +RwRaster *gpBirdfrontRaster[MAX_BIRDFRONT_FILES]; +RwRaster *gpBoatRaster[MAX_BOAT_FILES]; +RwRaster *gpCarDebrisRaster[MAX_CARDEBRIS_FILES]; +RwRaster *gpCarSplashRaster[MAX_CARSPLASH_FILES]; + +RwRaster *gpBoatWakeRaster; +RwRaster *gpFlame1Raster; +RwRaster *gpFlame5Raster; +RwRaster *gpRainDropSmallRaster; +RwRaster *gpBloodRaster; +RwRaster *gpLeafRaster[MAX_LEAF_FILES]; +RwRaster *gpCloudRaster1; +RwRaster *gpCloudRaster4; +RwRaster *gpBloodSmallRaster; +RwRaster *gpGungeRaster; +RwRaster *gpCollisionSmokeRaster; +RwRaster *gpBulletHitRaster; +RwRaster *gpGunShellRaster; +RwRaster *gpPointlightRaster; + +RwTexture *gpRainDropTex; +RwRaster *gpRainDropRaster; + +RwTexture *gpSparkTex; +RwTexture *gpNewspaperTex; +RwTexture *gpGunSmokeTex; +RwTexture *gpDotTex; +RwTexture *gpHeatHazeTex; +RwTexture *gpBeastieTex; +RwTexture *gpRainDripTex[MAX_RAINDRIP_FILES]; +RwTexture *gpRainDripDarkTex[MAX_RAINDRIP_FILES]; + +RwRaster *gpSparkRaster; +RwRaster *gpNewspaperRaster; +RwRaster *gpGunSmokeRaster; +RwRaster *gpDotRaster; +RwRaster *gpHeatHazeRaster; +RwRaster *gpBeastieRaster; +RwRaster *gpRainDripRaster[MAX_RAINDRIP_FILES]; +RwRaster *gpRainDripDarkRaster[MAX_RAINDRIP_FILES]; + +float CParticle::ms_afRandTable[CParticle::RAND_TABLE_SIZE]; +CParticle *CParticle::m_pUnusedListHead; +float CParticle::m_SinTable[CParticle::SIN_COS_TABLE_SIZE]; +float CParticle::m_CosTable[CParticle::SIN_COS_TABLE_SIZE]; + +int32 Randomizer; +int32 nParticleCreationInterval = 1; +float PARTICLE_WIND_TEST_SCALE = 0.002f; +float fParticleScaleLimit = 0.5f; + +bool clearWaterDrop; +int32 numWaterDropOnScreen; + +#ifdef DEBUGMENU +SETTWEAKPATH("Particle"); +TWEAKINT32(nParticleCreationInterval, 0, 5, 1); +TWEAKFLOAT(fParticleScaleLimit, 0.0f, 1.0f, 0.1f); +TWEAKFUNC(CParticle::ReloadConfig); +#endif + + + +void CParticle::ReloadConfig() +{ + debug("Initialising CParticleMgr..."); + + mod_ParticleSystemManager.Initialise(); + + debug("Initialising CParticle..."); + + m_pUnusedListHead = gParticleArray; + + for ( int32 i = 0; i < MAX_PARTICLES_ON_SCREEN; i++ ) + { + if ( i == MAX_PARTICLES_ON_SCREEN - 1 ) + gParticleArray[i].m_pNext = nil; + else + gParticleArray[i].m_pNext = &gParticleArray[i + 1]; + + gParticleArray[i].m_vecPosition = CVector(0.0f, 0.0f, 0.0f); + + gParticleArray[i].m_vecVelocity = CVector(0.0f, 0.0f, 0.0f); + + gParticleArray[i].m_nTimeWhenWillBeDestroyed = 0; + + gParticleArray[i].m_nTimeWhenColorWillBeChanged = 0; + + gParticleArray[i].m_fSize = 0.2f; + + gParticleArray[i].m_fExpansionRate = 0.0f; + + gParticleArray[i].m_nColorIntensity = 255; + + gParticleArray[i].m_nFadeToBlackTimer = 0; + + gParticleArray[i].m_nAlpha = 255; + + gParticleArray[i].m_nFadeAlphaTimer = 0; + + gParticleArray[i].m_nCurrentZRotation = 0; + + gParticleArray[i].m_nZRotationTimer = 0; + + gParticleArray[i].m_fCurrentZRadius = 0.0f; + + gParticleArray[i].m_nZRadiusTimer = 0; + + gParticleArray[i].m_nCurrentFrame = 0; + + gParticleArray[i].m_nAnimationSpeedTimer = 0; + + gParticleArray[i].m_nRotation = 0; + + gParticleArray[i].m_nRotationStep = 0; + } +} + +void CParticle::Initialise() +{ + ReloadConfig(); + + CParticleObject::Initialise(); + + float randVal = -1.0f; + for ( int32 i = 0; i < RAND_TABLE_SIZE; i++ ) + { + ms_afRandTable[i] = randVal; + randVal += 0.1f; + } + + for ( int32 i = 0; i < SIN_COS_TABLE_SIZE; i++ ) + { + float angle = DEGTORAD(float(i) * float(360.0f / SIN_COS_TABLE_SIZE)); + + m_SinTable[i] = ::Sin(angle); + m_CosTable[i] = ::Cos(angle); + } + + int32 slot = CTxdStore::FindTxdSlot("particle"); + + CTxdStore::PushCurrentTxd(); + CTxdStore::SetCurrentTxd(slot); + + for ( int32 i = 0; i < MAX_SMOKE_FILES; i++ ) + { + gpSmokeTex[i] = RwTextureRead(SmokeFiles[i], nil); + gpSmokeRaster[i] = RwTextureGetRaster(gpSmokeTex[i]); + } + + gpSmoke2Tex = RwTextureRead("smokeII_3", nil); + gpSmoke2Raster = RwTextureGetRaster(gpSmoke2Tex); + + for ( int32 i = 0; i < MAX_RUBBER_FILES; i++ ) + { + gpRubberTex[i] = RwTextureRead(RubberFiles[i], nil); + gpRubberRaster[i] = RwTextureGetRaster(gpRubberTex[i]); + } + + for ( int32 i = 0; i < MAX_RAINSPLASH_FILES; i++ ) + { + gpRainSplashTex[i] = RwTextureRead(RainSplashFiles[i], nil); + gpRainSplashRaster[i] = RwTextureGetRaster(gpRainSplashTex[i]); + } + + for ( int32 i = 0; i < MAX_WATERSPRAY_FILES; i++ ) + { + gpWatersprayTex[i] = RwTextureRead(WatersprayFiles[i], nil); + gpWatersprayRaster[i] = RwTextureGetRaster(gpWatersprayTex[i]); + } + + for ( int32 i = 0; i < MAX_EXPLOSIONMEDIUM_FILES; i++ ) + { + gpExplosionMediumTex[i] = RwTextureRead(ExplosionMediumFiles[i], nil); + gpExplosionMediumRaster[i] = RwTextureGetRaster(gpExplosionMediumTex[i]); + } + + for ( int32 i = 0; i < MAX_GUNFLASH_FILES; i++ ) + { + gpGunFlashTex[i] = RwTextureRead(GunFlashFiles[i], nil); + gpGunFlashRaster[i] = RwTextureGetRaster(gpGunFlashTex[i]); + } + + gpRainDropTex = RwTextureRead("raindrop4", nil); + gpRainDropRaster = RwTextureGetRaster(gpRainDropTex); + + + for ( int32 i = 0; i < MAX_RAINSPLASHUP_FILES; i++ ) + { + gpRainSplashupTex[i] = RwTextureRead(RainSplashupFiles[i], nil); + gpRainSplashupRaster[i] = RwTextureGetRaster(gpRainSplashupTex[i]); + } + + for ( int32 i = 0; i < MAX_BIRDFRONT_FILES; i++ ) + { + gpBirdfrontTex[i] = RwTextureRead(BirdfrontFiles[i], nil); + gpBirdfrontRaster[i] = RwTextureGetRaster(gpBirdfrontTex[i]); + } + + for ( int32 i = 0; i < MAX_BOAT_FILES; i++ ) + { + gpBoatTex[i] = RwTextureRead(BoatFiles[i], nil); + gpBoatRaster[i] = RwTextureGetRaster(gpBoatTex[i]); + } + + for ( int32 i = 0; i < MAX_CARDEBRIS_FILES; i++ ) + { + gpCarDebrisTex[i] = RwTextureRead(CardebrisFiles[i], nil); + gpCarDebrisRaster[i] = RwTextureGetRaster(gpCarDebrisTex[i]); + } + + for ( int32 i = 0; i < MAX_CARSPLASH_FILES; i++ ) + { + gpCarSplashTex[i] = RwTextureRead(CarsplashFiles[i], nil); + gpCarSplashRaster[i] = RwTextureGetRaster(gpCarSplashTex[i]); + } + + gpBoatWakeTex = RwTextureRead("boatwake2", nil); + gpBoatWakeRaster = RwTextureGetRaster(gpBoatWakeTex); + + gpFlame1Tex = RwTextureRead("flame1", nil); + gpFlame1Raster = RwTextureGetRaster(gpFlame1Tex); + + gpFlame5Tex = RwTextureRead("flame5", nil); + +//#ifdef FIX_BUGS +#if 0 + gpFlame5Raster = RwTextureGetRaster(gpFlame5Tex); +#else + // this seems to have become more of a design choice + gpFlame5Raster = RwTextureGetRaster(gpFlame1Tex); // copy-paste bug ? +#endif + + gpRainDropSmallTex = RwTextureRead("rainsmall", nil); + gpRainDropSmallRaster = RwTextureGetRaster(gpRainDropSmallTex); + + gpBloodTex = RwTextureRead("blood", nil); + gpBloodRaster = RwTextureGetRaster(gpBloodTex); + + gpLeafTex[0] = RwTextureRead("gameleaf01_64", nil); + gpLeafRaster[0] = RwTextureGetRaster(gpLeafTex[0]); + + gpLeafTex[1] = RwTextureRead("letter", nil); + gpLeafRaster[1] = RwTextureGetRaster(gpLeafTex[1]); + + gpCloudTex1 = RwTextureRead("cloud3", nil); + gpCloudRaster1 = RwTextureGetRaster(gpCloudTex1); + + gpCloudTex4 = RwTextureRead("cloudmasked", nil); + gpCloudRaster4 = RwTextureGetRaster(gpCloudTex4); + + gpBloodSmallTex = RwTextureRead("bloodsplat2", nil); + gpBloodSmallRaster = RwTextureGetRaster(gpBloodSmallTex); + + gpGungeTex = RwTextureRead("gunge", nil); + gpGungeRaster = RwTextureGetRaster(gpGungeTex); + + gpCollisionSmokeTex = RwTextureRead("collisionsmoke", nil); + gpCollisionSmokeRaster = RwTextureGetRaster(gpCollisionSmokeTex); + + gpBulletHitTex = RwTextureRead("bullethitsmoke", nil); + gpBulletHitRaster = RwTextureGetRaster(gpBulletHitTex); + + gpGunShellTex = RwTextureRead("gunshell", nil); + gpGunShellRaster = RwTextureGetRaster(gpGunShellTex); + + gpPointlightTex = RwTextureRead("pointlight", nil); + gpPointlightRaster = RwTextureGetRaster(gpPointlightTex); + + gpSparkTex = RwTextureRead("spark", nil); + gpSparkRaster = RwTextureGetRaster(gpSparkTex); + + gpNewspaperTex = RwTextureRead("newspaper02_64", nil); + gpNewspaperRaster = RwTextureGetRaster(gpNewspaperTex); + + gpGunSmokeTex = RwTextureRead("gunsmoke3", nil); + gpGunSmokeRaster = RwTextureGetRaster(gpGunSmokeTex); + + gpDotTex = RwTextureRead("dot", nil); + gpDotRaster = RwTextureGetRaster(gpDotTex); + + gpHeatHazeTex = RwTextureRead("heathaze", nil); + gpHeatHazeRaster = RwTextureGetRaster(gpHeatHazeTex); + + gpBeastieTex = RwTextureRead("beastie", nil); + gpBeastieRaster = RwTextureGetRaster(gpBeastieTex); + + gpRainDripTex[0] = RwTextureRead("raindrip64", nil); + gpRainDripRaster[0] = RwTextureGetRaster(gpRainDripTex[0]); + + gpRainDripTex[1] = RwTextureRead("raindripb64", nil); + gpRainDripRaster[1] = RwTextureGetRaster(gpRainDripTex[1]); + + gpRainDripDarkTex[0] = RwTextureRead("raindrip64_d", nil); + gpRainDripDarkRaster[0] = RwTextureGetRaster(gpRainDripDarkTex[0]); + + gpRainDripDarkTex[1] = RwTextureRead("raindripb64_d", nil); + gpRainDripDarkRaster[1] = RwTextureGetRaster(gpRainDripDarkTex[1]); + + CTxdStore::PopCurrentTxd(); + + for ( int32 i = 0; i < MAX_PARTICLES; i++ ) + { + tParticleSystemData *entry = &mod_ParticleSystemManager.m_aParticles[i]; + + switch ( i ) + { + case PARTICLE_SPARK: + case PARTICLE_SPARK_SMALL: + case PARTICLE_RAINDROP_SMALL: + case PARTICLE_HELI_ATTACK: + entry->m_ppRaster = &gpRainDropSmallRaster; + break; + + case PARTICLE_WATER_SPARK: + entry->m_ppRaster = &gpSparkRaster; + break; + + case PARTICLE_WHEEL_DIRT: + case PARTICLE_SAND: + case PARTICLE_STEAM2: + case PARTICLE_STEAM_NY: + case PARTICLE_STEAM_NY_SLOWMOTION: + case PARTICLE_GROUND_STEAM: + case PARTICLE_ENGINE_STEAM: + case PARTICLE_PEDFOOT_DUST: + case PARTICLE_CAR_DUST: + case PARTICLE_EXHAUST_FUMES: + entry->m_ppRaster = &gpSmoke2Raster; + break; + + case PARTICLE_WHEEL_WATER: + case PARTICLE_WATER: + case PARTICLE_SMOKE: + case PARTICLE_SMOKE_SLOWMOTION: + case PARTICLE_DRY_ICE: + case PARTICLE_GARAGEPAINT_SPRAY: + case PARTICLE_STEAM: + case PARTICLE_WATER_CANNON: + case PARTICLE_EXTINGUISH_STEAM: + case PARTICLE_HELI_DUST: + case PARTICLE_PAINT_SMOKE: + case PARTICLE_BULLETHIT_SMOKE: + entry->m_ppRaster = gpSmokeRaster; + break; + + case PARTICLE_BLOOD: + entry->m_ppRaster = &gpBloodRaster; + break; + + case PARTICLE_BLOOD_SMALL: + case PARTICLE_BLOOD_SPURT: + entry->m_ppRaster = &gpBloodSmallRaster; + break; + + case PARTICLE_DEBRIS: + case PARTICLE_TREE_LEAVES: + entry->m_ppRaster = gpLeafRaster; + break; + + case PARTICLE_DEBRIS2: + entry->m_ppRaster = &gpGungeRaster; + break; + + case PARTICLE_FLYERS: + entry->m_ppRaster = &gpNewspaperRaster; + break; + + case PARTICLE_FLAME: + case PARTICLE_CARFLAME: + entry->m_ppRaster = &gpFlame1Raster; + break; + + case PARTICLE_FIREBALL: + entry->m_ppRaster = &gpFlame5Raster; + break; + + case PARTICLE_GUNFLASH: + case PARTICLE_GUNFLASH_NOANIM: + entry->m_ppRaster = gpGunFlashRaster; + break; + + + case PARTICLE_GUNSMOKE: + case PARTICLE_WATERDROP: + case PARTICLE_BLOODDROP: + case PARTICLE_HEATHAZE: + case PARTICLE_HEATHAZE_IN_DIST: + entry->m_ppRaster = nil; + break; + + case PARTICLE_GUNSMOKE2: + case PARTICLE_BOAT_THRUSTJET: + case PARTICLE_RUBBER_SMOKE: + entry->m_ppRaster = gpRubberRaster; + break; + + case PARTICLE_CIGARETTE_SMOKE: + entry->m_ppRaster = &gpGunSmokeRaster; + break; + + case PARTICLE_TEARGAS: + entry->m_ppRaster = &gpHeatHazeRaster; + break; + + case PARTICLE_SHARD: + case PARTICLE_RAINDROP: + case PARTICLE_RAINDROP_2D: + entry->m_ppRaster = &gpRainDropRaster; + break; + + case PARTICLE_SPLASH: + case PARTICLE_PED_SPLASH: + case PARTICLE_CAR_SPLASH: + case PARTICLE_WATER_HYDRANT: + entry->m_ppRaster = gpCarSplashRaster; + break; + + case PARTICLE_RAIN_SPLASH: + case PARTICLE_RAIN_SPLASH_BIGGROW: + entry->m_ppRaster = gpRainSplashRaster; + break; + + case PARTICLE_RAIN_SPLASHUP: + entry->m_ppRaster = gpRainSplashupRaster; + break; + + case PARTICLE_WATERSPRAY: + entry->m_ppRaster = gpWatersprayRaster; + break; + + case PARTICLE_EXPLOSION_MEDIUM: + case PARTICLE_EXPLOSION_LARGE: + case PARTICLE_EXPLOSION_MFAST: + case PARTICLE_EXPLOSION_LFAST: + entry->m_ppRaster = gpExplosionMediumRaster; + break; + + case PARTICLE_BOAT_SPLASH: + entry->m_ppRaster = &gpBoatWakeRaster; + break; + + case PARTICLE_ENGINE_SMOKE: + case PARTICLE_ENGINE_SMOKE2: + case PARTICLE_CARFLAME_SMOKE: + case PARTICLE_FIREBALL_SMOKE: + case PARTICLE_ROCKET_SMOKE: + case PARTICLE_TEST: + entry->m_ppRaster = &gpCloudRaster4; + break; + + case PARTICLE_CARCOLLISION_DUST: + case PARTICLE_BURNINGRUBBER_SMOKE: + entry->m_ppRaster = &gpCollisionSmokeRaster; + break; + + case PARTICLE_CAR_DEBRIS: + case PARTICLE_HELI_DEBRIS: + case PARTICLE_BIRD_DEBRIS: + entry->m_ppRaster = gpCarDebrisRaster; + break; + + case PARTICLE_GUNSHELL_FIRST: + case PARTICLE_GUNSHELL: + case PARTICLE_GUNSHELL_BUMP1: + case PARTICLE_GUNSHELL_BUMP2: + entry->m_ppRaster = &gpGunShellRaster; + break; + + + case PARTICLE_BIRD_FRONT: + entry->m_ppRaster = gpBirdfrontRaster; + break; + + case PARTICLE_SHIP_SIDE: + entry->m_ppRaster = gpBoatRaster; + break; + + case PARTICLE_BEASTIE: + entry->m_ppRaster = &gpBeastieRaster; + break; + } + } + + debug("CParticle ready"); +} + +void CParticle::Shutdown() +{ + debug("Shutting down CParticle..."); + + for ( int32 i = 0; i < MAX_SMOKE_FILES; i++ ) + { + RwTextureDestroy(gpSmokeTex[i]); + gpSmokeTex[i] = nil; + } + + RwTextureDestroy(gpSmoke2Tex); + gpSmoke2Tex = nil; + + for ( int32 i = 0; i < MAX_RUBBER_FILES; i++ ) + { + RwTextureDestroy(gpRubberTex[i]); + gpRubberTex[i] = nil; + } + + for ( int32 i = 0; i < MAX_RAINSPLASH_FILES; i++ ) + { + RwTextureDestroy(gpRainSplashTex[i]); + gpRainSplashTex[i] = nil; + } + + for ( int32 i = 0; i < MAX_WATERSPRAY_FILES; i++ ) + { + RwTextureDestroy(gpWatersprayTex[i]); + gpWatersprayTex[i] = nil; + } + + for ( int32 i = 0; i < MAX_EXPLOSIONMEDIUM_FILES; i++ ) + { + RwTextureDestroy(gpExplosionMediumTex[i]); + gpExplosionMediumTex[i] = nil; + } + + for ( int32 i = 0; i < MAX_GUNFLASH_FILES; i++ ) + { + RwTextureDestroy(gpGunFlashTex[i]); + gpGunFlashTex[i] = nil; + } + + RwTextureDestroy(gpRainDropTex); + gpRainDropTex = nil; + + for ( int32 i = 0; i < MAX_RAINSPLASHUP_FILES; i++ ) + { + RwTextureDestroy(gpRainSplashupTex[i]); + gpRainSplashupTex[i] = nil; + } + + for ( int32 i = 0; i < MAX_BIRDFRONT_FILES; i++ ) + { + RwTextureDestroy(gpBirdfrontTex[i]); + gpBirdfrontTex[i] = nil; + } + + for ( int32 i = 0; i < MAX_BOAT_FILES; i++ ) + { + RwTextureDestroy(gpBoatTex[i]); + gpBoatTex[i] = nil; + } + + for ( int32 i = 0; i < MAX_CARDEBRIS_FILES; i++ ) + { + RwTextureDestroy(gpCarDebrisTex[i]); + gpCarDebrisTex[i] = nil; + } + + for ( int32 i = 0; i < MAX_CARSPLASH_FILES; i++ ) + { + RwTextureDestroy(gpCarSplashTex[i]); + gpCarSplashTex[i] = nil; + } + + for ( int32 i = 0; i < MAX_RAINDRIP_FILES; i++ ) + { + RwTextureDestroy(gpRainDripTex[i]); + gpRainDripTex[i] = nil; + + RwTextureDestroy(gpRainDripDarkTex[i]); + gpRainDripDarkTex[i] = nil; + } + + RwTextureDestroy(gpBoatWakeTex); + gpBoatWakeTex = nil; + + RwTextureDestroy(gpFlame1Tex); + gpFlame1Tex = nil; + + RwTextureDestroy(gpFlame5Tex); + gpFlame5Tex = nil; + + RwTextureDestroy(gpRainDropSmallTex); + gpRainDropSmallTex = nil; + + RwTextureDestroy(gpBloodTex); + gpBloodTex = nil; + + RwTextureDestroy(gpLeafTex[0]); + gpLeafTex[0] = nil; + + RwTextureDestroy(gpLeafTex[1]); + gpLeafTex[1] = nil; + + RwTextureDestroy(gpCloudTex1); + gpCloudTex1 = nil; + + RwTextureDestroy(gpCloudTex4); + gpCloudTex4 = nil; + + RwTextureDestroy(gpBloodSmallTex); + gpBloodSmallTex = nil; + + RwTextureDestroy(gpGungeTex); + gpGungeTex = nil; + + RwTextureDestroy(gpCollisionSmokeTex); + gpCollisionSmokeTex = nil; + + RwTextureDestroy(gpBulletHitTex); + gpBulletHitTex = nil; + + RwTextureDestroy(gpGunShellTex); + gpGunShellTex = nil; + + RwTextureDestroy(gpPointlightTex); + gpPointlightTex = nil; + + RwTextureDestroy(gpSparkTex); + gpSparkTex = nil; + + RwTextureDestroy(gpNewspaperTex); + gpNewspaperTex = nil; + + RwTextureDestroy(gpGunSmokeTex); + gpGunSmokeTex = nil; + + RwTextureDestroy(gpDotTex); + gpDotTex = nil; + RwTextureDestroy(gpHeatHazeTex); + gpHeatHazeTex = nil; + + RwTextureDestroy(gpBeastieTex); + gpBeastieTex = nil; + + int32 slot; + + slot = CTxdStore::FindTxdSlot("particle"); + CTxdStore::RemoveTxdSlot(slot); + + debug("CParticle shut down"); +} + + +void CParticle::AddParticlesAlongLine(tParticleType type, CVector const &vecStart, CVector const &vecEnd, CVector const &vecDir, float fPower, CEntity *pEntity, float fSize, int32 nRotationSpeed, int32 nRotation, int32 nCurFrame, int32 nLifeSpan) +{ + CVector vecDist = vecEnd - vecStart; + float fDist = vecDist.Magnitude(); + float fSteps = Max(fDist / fPower, 1.0f); + int32 nSteps = (int32)fSteps; + + CVector vecStep = vecDist * (1.0f / (float)nSteps); + + for ( int32 i = 0; i < nSteps; i++ ) + { + CVector vecPos = float(i) * vecStep + vecStart; + AddParticle(type, vecPos, vecDir, pEntity, fSize, nRotationSpeed, nRotation, nCurFrame, nLifeSpan); + } +} + +void CParticle::AddParticlesAlongLine(tParticleType type, CVector const &vecStart, CVector const &vecEnd, CVector const &vecDir, float fPower, CEntity *pEntity, float fSize, RwRGBA const &color, int32 nRotationSpeed, int32 nRotation, int32 nCurFrame, int32 nLifeSpan) +{ + CVector vecDist = vecEnd - vecStart; + float fDist = vecDist.Magnitude(); + float fSteps = Max(fDist / fPower, 1.0f); + int32 nSteps = (int32)fSteps; + + CVector vecStep = vecDist * (1.0f / (float)nSteps); + + for ( int32 i = 0; i < nSteps; i++ ) + { + CVector vecPos = float(i) * vecStep + vecStart; + + AddParticle(type, vecPos, vecDir, pEntity, fSize, color, nRotationSpeed, nRotation, nCurFrame, nLifeSpan); + } +} + +CParticle *CParticle::AddParticle(tParticleType type, CVector const &vecPos, CVector const &vecDir, CEntity *pEntity, float fSize, int32 nRotationSpeed, int32 nRotation, int32 nCurFrame, int32 nLifeSpan) +{ + CRGBA color(0, 0, 0, 0); + return AddParticle(type, vecPos, vecDir, pEntity, fSize, color, nRotationSpeed, nRotation, nCurFrame, nLifeSpan); +} + +CParticle *CParticle::AddParticle(tParticleType type, CVector const &vecPos, CVector const &vecDir, CEntity *pEntity, float fSize, RwRGBA const &color, int32 nRotationSpeed, int32 nRotation, int32 nCurFrame, int32 nLifeSpan) +{ + if ( CTimer::GetIsPaused() ) + return nil; + + if ( ( type == PARTICLE_ENGINE_SMOKE + || type == PARTICLE_ENGINE_SMOKE2 + || type == PARTICLE_ENGINE_STEAM + || type == PARTICLE_CARFLAME_SMOKE + || type == PARTICLE_RUBBER_SMOKE + || type == PARTICLE_BURNINGRUBBER_SMOKE + || type == PARTICLE_EXHAUST_FUMES + || type == PARTICLE_CARCOLLISION_DUST ) + && nParticleCreationInterval & CTimer::GetFrameCounter() ) + { + return nil; + } + + if ( !CReplay::IsPlayingBack() ) + CReplay::RecordParticle(type, vecPos, vecDir, fSize, color); + + CParticle *pParticle = m_pUnusedListHead; + + if ( pParticle == nil ) + return nil; + + tParticleSystemData *psystem = &mod_ParticleSystemManager.m_aParticles[type]; + + if ( psystem->m_fCreateRange != 0.0f && psystem->m_fCreateRange < ( TheCamera.GetPosition() - vecPos ).MagnitudeSqr() ) + return nil; + + + pParticle->m_fSize = psystem->m_fDefaultInitialRadius; + pParticle->m_fExpansionRate = psystem->m_fExpansionRate; + + if ( nLifeSpan != 0 ) + pParticle->m_nTimeWhenWillBeDestroyed = CTimer::GetTimeInMilliseconds() + nLifeSpan; + else + pParticle->m_nTimeWhenWillBeDestroyed = CTimer::GetTimeInMilliseconds() + psystem->m_nLifeSpan; + + pParticle->m_nColorIntensity = psystem->m_nFadeToBlackInitialIntensity; + + pParticle->m_nFadeToBlackTimer = psystem->m_nFadeToBlackAmount; + + if ( psystem->m_nFadeToBlackTime ) + pParticle->m_nFadeToBlackTimer /= psystem->m_nFadeToBlackTime; + + pParticle->m_nAlpha = psystem->m_nFadeAlphaInitialIntensity; + + pParticle->m_nFadeAlphaTimer = psystem->m_nFadeAlphaAmount; + + if ( psystem->m_nFadeAlphaTime ) + pParticle->m_nFadeAlphaTimer /= psystem->m_nFadeAlphaTime; + + pParticle->m_nCurrentZRotation = psystem->m_nZRotationInitialAngle; + pParticle->m_fCurrentZRadius = psystem->m_fInitialZRadius; + + if ( nCurFrame != 0 ) + pParticle->m_nCurrentFrame = nCurFrame; + else + pParticle->m_nCurrentFrame = psystem->m_nStartAnimationFrame; + + + pParticle->m_nZRotationTimer = 0; + pParticle->m_nZRadiusTimer = 0; + pParticle->m_nAnimationSpeedTimer = 0; + pParticle->m_fZGround = 0.0f; + + if ( type != PARTICLE_HEATHAZE ) + pParticle->m_vecPosition = vecPos; + else + { + CVector screen; + float w, h; + + if ( !CSprite::CalcScreenCoors(vecPos, &screen, &w, &h, true) ) + return nil; + + pParticle->m_vecPosition = screen; + psystem->m_vecTextureStretch.x = w; + psystem->m_vecTextureStretch.y = h; + } + + pParticle->m_vecVelocity = vecDir; + + pParticle->m_vecParticleMovementOffset = CVector(0.0f, 0.0f, 0.0f); + pParticle->m_nTimeWhenColorWillBeChanged = 0; + + if ( color.alpha != 0 ) + RwRGBAAssign(&pParticle->m_Color, &color); + else + { + RwRGBAAssign(&pParticle->m_Color, psystem->m_RenderColouring); + + if ( psystem->m_ColorFadeTime != 0 ) + pParticle->m_nTimeWhenColorWillBeChanged = CTimer::GetTimeInMilliseconds() + psystem->m_ColorFadeTime; + + if ( psystem->m_InitialColorVariation != 0 ) + { + int32 ColorVariation = CGeneral::GetRandomNumberInRange(-psystem->m_InitialColorVariation, psystem->m_InitialColorVariation); + //float ColorVariation = CGeneral::GetRandomNumberInRange((float)-psystem->m_InitialColorVariation, (float)psystem->m_InitialColorVariation); + + pParticle->m_Color.red = Clamp(pParticle->m_Color.red + + PERCENT(pParticle->m_Color.red, ColorVariation), + 0, 255); + + pParticle->m_Color.green = Clamp(pParticle->m_Color.green + + PERCENT(pParticle->m_Color.green, ColorVariation), + 0, 255); + + pParticle->m_Color.blue = Clamp(pParticle->m_Color.blue + + PERCENT(pParticle->m_Color.blue, ColorVariation), + 0, 255); + } + } + + pParticle->m_nRotation = nRotation; + + if ( nRotationSpeed != 0 ) + pParticle->m_nRotationStep = nRotationSpeed; + else + pParticle->m_nRotationStep = psystem->m_nRotationSpeed; + + if ( CGeneral::GetRandomNumber() & 1 ) + pParticle->m_nRotationStep = -pParticle->m_nRotationStep; + + if ( psystem->m_fPositionRandomError != 0.0f ) + { + pParticle->m_vecPosition.x += psystem->m_fPositionRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE]; + pParticle->m_vecPosition.y += psystem->m_fPositionRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE]; + + if ( psystem->Flags & RAND_VERT_V ) + pParticle->m_vecPosition.z += psystem->m_fPositionRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE]; + } + + if ( psystem->m_fVelocityRandomError != 0.0f ) + { + pParticle->m_vecVelocity.x += psystem->m_fVelocityRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE]; + pParticle->m_vecVelocity.y += psystem->m_fVelocityRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE]; + + if ( psystem->Flags & RAND_VERT_V ) + pParticle->m_vecVelocity.z += psystem->m_fVelocityRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE]; + } + + if ( psystem->m_fExpansionRateError != 0.0f && !(psystem->Flags & SCREEN_TRAIL) ) + pParticle->m_fExpansionRate += psystem->m_fExpansionRateError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE] + psystem->m_fExpansionRateError; + + if ( psystem->m_nRotationRateError != 0 ) + pParticle->m_nRotationStep += CGeneral::GetRandomNumberInRange(-psystem->m_nRotationRateError, psystem->m_nRotationRateError); + + if ( psystem->m_nLifeSpanErrorShape != 0 ) + { + float randVal = ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE]; + if ( randVal > 0.0f ) + pParticle->m_nTimeWhenWillBeDestroyed += int32(float(psystem->m_nLifeSpan) * randVal * float(psystem->m_nLifeSpanErrorShape)); + else + pParticle->m_nTimeWhenWillBeDestroyed += int32(float(psystem->m_nLifeSpan) * randVal / float(psystem->m_nLifeSpanErrorShape)); + } + + if ( psystem->Flags & ZCHECK_FIRST ) + { + static bool bValidGroundFound = false; + static CVector LastTestCoors; + static float LastTestGroundZ; + + if ( bValidGroundFound + && vecPos.x == LastTestCoors.x + && vecPos.y == LastTestCoors.y + && vecPos.z == LastTestCoors.z ) + { + pParticle->m_fZGround = LastTestGroundZ; + } + else + { + bValidGroundFound = false; + + CColPoint point; + CEntity *entity; + + if ( !CWorld::ProcessVerticalLine( + pParticle->m_vecPosition + CVector(0.0f, 0.0f, 0.5f), + -100.0f, point, entity, true, true, false, false, true, false, nil) ) + { + return nil; + } + + if ( point.point.z >= pParticle->m_vecPosition.z ) + return nil; + + pParticle->m_fZGround = point.point.z; + bValidGroundFound = true; + LastTestCoors = vecPos; + LastTestGroundZ = point.point.z; + } + } + + if ( psystem->Flags & ZCHECK_BUMP ) + { + static float Z_Ground = 0.0f; + + if ( psystem->Flags & ZCHECK_BUMP_FIRST ) + { + bool bZFound = false; + + Z_Ground = CWorld::FindGroundZFor3DCoord(vecPos.x, vecPos.y, vecPos.z, (bool *)&bZFound); + + if ( bZFound == false ) + return nil; + + pParticle->m_fZGround = Z_Ground; + } + + pParticle->m_fZGround = Z_Ground; + } + + switch ( type ) + { + case PARTICLE_DEBRIS: + pParticle->m_vecVelocity.z *= CGeneral::GetRandomNumberInRange(0.5f, 3.0f); + break; + + case PARTICLE_EXPLOSION_MEDIUM: + pParticle->m_nColorIntensity -= 30 * (CGeneral::GetRandomNumber() & 1); // mb "+= -30 * rand" here ? + pParticle->m_nAnimationSpeedTimer = CGeneral::GetRandomNumber() & 7; + pParticle->m_fSize = CGeneral::GetRandomNumberInRange(0.3f, 0.8f); + pParticle->m_vecPosition.z -= CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); + break; + + case PARTICLE_EXPLOSION_LARGE: + pParticle->m_nColorIntensity -= 30 * (CGeneral::GetRandomNumber() & 1); // mb "+= -30 * rand" here ? + pParticle->m_nAnimationSpeedTimer = CGeneral::GetRandomNumber() & 7; + pParticle->m_fSize = CGeneral::GetRandomNumberInRange(0.8f, 1.4f); + pParticle->m_vecPosition.z -= CGeneral::GetRandomNumberInRange(-0.3f, 0.3f); + break; + + case PARTICLE_WATER_HYDRANT: + pParticle->m_vecPosition.z += 20.0f * psystem->m_fPositionRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE]; + break; + default: break; + } + + if ( fSize != 0.0f ) + pParticle->m_fSize = fSize; + + m_pUnusedListHead = pParticle->m_pNext; + + pParticle->m_pNext = psystem->m_pParticles; + + psystem->m_pParticles = pParticle; + + return pParticle; +} + +void CParticle::Update() +{ + if ( CTimer::GetIsPaused() ) + return; + + CRGBA color(0, 0, 0, 0); + + float fFricDeccel50 = pow(0.50f, CTimer::GetTimeStep()); + float fFricDeccel80 = pow(0.80f, CTimer::GetTimeStep()); + float fFricDeccel90 = pow(0.90f, CTimer::GetTimeStep()); + float fFricDeccel95 = pow(0.95f, CTimer::GetTimeStep()); + float fFricDeccel96 = pow(0.96f, CTimer::GetTimeStep()); + float fFricDeccel99 = pow(0.99f, CTimer::GetTimeStep()); + + CParticleObject::UpdateAll(); + + // ejaculation at 23:00, 23:15, 23:30, 23:45 + if ( CClock::ms_nGameClockHours == 23 && + ( CClock::ms_nGameClockMinutes == 0 + || CClock::ms_nGameClockMinutes == 15 + || CClock::ms_nGameClockMinutes == 30 + || CClock::ms_nGameClockMinutes == 45 ) ) + { + AddParticle(PARTICLE_CAR_SPLASH, + CVector(557.03f, -4.0f, 151.46f), + CVector(0.0f, 0.0f, 2.5f), + NULL, + 2.0f, + CRGBA(255, 255, 255, 255), + 0, + 0, + 1, + 1000); + } + + for ( int32 i = 0; i < MAX_PARTICLES; i++ ) + { + tParticleSystemData *psystem = &mod_ParticleSystemManager.m_aParticles[i]; + CParticle *particle = psystem->m_pParticles; + CParticle *prevParticle = nil; + bool bRemoveParticle; + + if ( particle == nil ) + continue; + + for ( ; particle != nil; _Next(particle, prevParticle, psystem, bRemoveParticle) ) + { + CVector vecWind(0.0f, 0.0f, 0.0f); + + bRemoveParticle = false; + + CVector vecMoveStep = particle->m_vecVelocity * CTimer::GetTimeStep(); + CVector vecPos = particle->m_vecPosition; + + if ( numWaterDropOnScreen == 0 ) + clearWaterDrop = false; + + if ( psystem->m_Type == PARTICLE_WATERDROP ) + { + if ( CGame::IsInInterior() || clearWaterDrop == true ) + { + bRemoveParticle = true; + continue; + } + + static uint8 nWaterDropCount; + + if ( nWaterDropCount == 5 ) + { + vecMoveStep = CVector(0.0f, 0.0f, 0.0f); + particle->m_nTimeWhenWillBeDestroyed += 1250; + nWaterDropCount = 0; + } + else + { + if ( TheCamera.m_CameraAverageSpeed > 0.35f ) + { + if ( vecMoveStep.Magnitude() > 0.5f ) + { + if ( vecMoveStep.Magnitude() > 0.4f && vecMoveStep.Magnitude() < 0.8f ) + { + vecMoveStep.x += TheCamera.m_CameraAverageSpeed * 1.5f; + vecMoveStep.y += TheCamera.m_CameraAverageSpeed * 1.5f; + } + else if ( vecMoveStep.Magnitude() != 0.0f ) + { + vecMoveStep.x += CGeneral::GetRandomNumberInRange(0.01f, 0.05f); + vecMoveStep.y += CGeneral::GetRandomNumberInRange(0.01f, 0.05f); + } + } + } + + nWaterDropCount++; + } + + if ( vecPos.z <= 1.5f ) + vecMoveStep.z = 0.0f; + } + + if ( psystem->m_Type == PARTICLE_HEATHAZE || psystem->m_Type == PARTICLE_HEATHAZE_IN_DIST ) + { +#ifdef FIX_BUGS + int32 nSinCosIndex = (int32(DEGTORAD((float)particle->m_nRotation) * float(SIN_COS_TABLE_SIZE) / TWOPI) + SIN_COS_TABLE_SIZE) % SIN_COS_TABLE_SIZE; +#else + int32 nSinCosIndex = int32(DEGTORAD((float)particle->m_nRotation) * float(SIN_COS_TABLE_SIZE) / TWOPI) % SIN_COS_TABLE_SIZE; +#endif + vecMoveStep.x = Sin(nSinCosIndex); + vecMoveStep.y = Sin(nSinCosIndex); + + if ( psystem->m_Type == PARTICLE_HEATHAZE_IN_DIST ) + particle->m_nRotation = int16((float)particle->m_nRotation + 0.75f); + else + particle->m_nRotation = int16((float)particle->m_nRotation + 1.0f); + } + + if ( psystem->m_Type == PARTICLE_BEASTIE ) + { +#ifdef FIX_BUGS + int32 nSinCosIndex = (int32(DEGTORAD((float)particle->m_nRotation) * float(SIN_COS_TABLE_SIZE) / TWOPI) + SIN_COS_TABLE_SIZE) % SIN_COS_TABLE_SIZE; +#else + int32 nSinCosIndex = int32(DEGTORAD((float)particle->m_nRotation) * float(SIN_COS_TABLE_SIZE) / TWOPI) % SIN_COS_TABLE_SIZE; +#endif + particle->m_vecVelocity.x = 0.50f * Cos(nSinCosIndex); + particle->m_vecVelocity.y = Cos(nSinCosIndex); + particle->m_vecVelocity.z = 0.25f * Sin(nSinCosIndex); + + if ( particle->m_vecVelocity.Magnitude() > 2.0f + || vecPos.z > 40.0f + || (TheCamera.GetPosition() - vecPos).Magnitude() < 60.0f + ) + { + bRemoveParticle = true; + continue; + } + } + + vecPos += vecMoveStep; + + if ( psystem->m_Type == PARTICLE_FIREBALL ) + { + AddParticle(PARTICLE_HEATHAZE, particle->m_vecPosition, CVector(0.0f, 0.0f, 0.0f), + nil, particle->m_fSize * 5.0f); + } + + if ( psystem->m_Type == PARTICLE_GUNSMOKE2 ) + { + if ( CTimer::GetFrameCounter() & 10 ) + { +#ifdef FIX_BUGS + if ( FindPlayerPed() && FindPlayerPed()->GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN ) +#else + if ( FindPlayerPed()->GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN ) +#endif + { + AddParticle(PARTICLE_HEATHAZE, particle->m_vecPosition, CVector(0.0f, 0.0f, 0.0f)); + } + } + } + + if ( CWeather::Wind > 0.0f ) + { + if ( vecMoveStep.Magnitude() != 0.0f ) + { + vecWind.x = CGeneral::GetRandomNumberInRange(0.75f, 1.25f) * -CWeather::Wind; + vecWind.y = CGeneral::GetRandomNumberInRange(0.75f, 1.25f) * -CWeather::Wind; + vecWind *= PARTICLE_WIND_TEST_SCALE * psystem->m_fWindFactor * CTimer::GetTimeStep(); + particle->m_vecVelocity += vecWind; + } + } + + if ( psystem->m_Type == PARTICLE_RAINDROP + || psystem->m_Type == PARTICLE_RAINDROP_SMALL + || psystem->m_Type == PARTICLE_RAIN_SPLASH + || psystem->m_Type == PARTICLE_RAIN_SPLASH_BIGGROW + || psystem->m_Type == PARTICLE_CAR_SPLASH + || psystem->m_Type == PARTICLE_BOAT_SPLASH + || psystem->m_Type == PARTICLE_RAINDROP_2D ) + { + int32 nMaxDrops = int32(6.0f * TheCamera.m_CameraAverageSpeed + 1.0f); + float fDistToCam = 0.0f; + + if ( psystem->m_Type == PARTICLE_BOAT_SPLASH || psystem->m_Type == PARTICLE_CAR_SPLASH ) + { + if ( vecPos.z + particle->m_fSize < 5.0f ) + { + bRemoveParticle = true; + continue; + } + + switch ( TheCamera.GetLookDirection() ) + { + case LOOKING_LEFT: + case LOOKING_RIGHT: + case LOOKING_FORWARD: + nMaxDrops /= 2; + break; + + default: + nMaxDrops = 0; + break; + } + + fDistToCam = (TheCamera.GetPosition() - vecPos).Magnitude(); + } + + if ( numWaterDropOnScreen < nMaxDrops && numWaterDropOnScreen < 63 + && fDistToCam < 10.0f + && clearWaterDrop == false + && !CGame::IsInInterior() ) + { + CVector vecWaterdropTarget + ( + CGeneral::GetRandomNumberInRange(-0.25f, 0.25f), + CGeneral::GetRandomNumberInRange(0.1f, 0.75f), + -0.01f + ); + + CVector vecWaterdropPos; + + if ( TheCamera.m_CameraAverageSpeed < 0.35f ) + vecWaterdropPos.x = (float)CGeneral::GetRandomNumberInRange(50, int32(SCREEN_WIDTH) - 50); + else + vecWaterdropPos.x = (float)CGeneral::GetRandomNumberInRange(200, int32(SCREEN_WIDTH) - 200); + + if ( psystem->m_Type == PARTICLE_BOAT_SPLASH || psystem->m_Type == PARTICLE_CAR_SPLASH ) + vecWaterdropPos.y = (float)CGeneral::GetRandomNumberInRange(SCREEN_HEIGHT / 2, SCREEN_HEIGHT); + else + { + if ( TheCamera.m_CameraAverageSpeed < 0.35f ) + vecWaterdropPos.y = (float)CGeneral::GetRandomNumberInRange(0, int32(SCREEN_HEIGHT)); + else + vecWaterdropPos.y = (float)CGeneral::GetRandomNumberInRange(150, int32(SCREEN_HEIGHT) - 200); + } + + vecWaterdropPos.z = 2.0f; + + if ( AddParticle(PARTICLE_WATERDROP, + vecWaterdropPos, + vecWaterdropTarget, + nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.15f), + 0, + 0, + CGeneral::GetRandomNumber() & 1, + 0) != nil ) + { + numWaterDropOnScreen++; + } + } + } + + if ( CTimer::GetTimeInMilliseconds() > particle->m_nTimeWhenWillBeDestroyed || particle->m_nAlpha == 0 ) + { + bRemoveParticle = true; + continue; + } + + if ( particle->m_nTimeWhenColorWillBeChanged != 0 ) + { + if ( particle->m_nTimeWhenColorWillBeChanged > CTimer::GetTimeInMilliseconds() ) + { + float colorMul = 1.0f - float(particle->m_nTimeWhenColorWillBeChanged - CTimer::GetTimeInMilliseconds()) / float(psystem->m_ColorFadeTime); + + particle->m_Color.red = Clamp( + psystem->m_RenderColouring.red + int32(float(psystem->m_FadeDestinationColor.red - psystem->m_RenderColouring.red) * colorMul), + 0, 255); + + particle->m_Color.green = Clamp( + psystem->m_RenderColouring.green + int32(float(psystem->m_FadeDestinationColor.green - psystem->m_RenderColouring.green) * colorMul), + 0, 255); + + particle->m_Color.blue = Clamp( + psystem->m_RenderColouring.blue + int32(float(psystem->m_FadeDestinationColor.blue - psystem->m_RenderColouring.blue) * colorMul), + 0, 255); + } + else + RwRGBAAssign(&particle->m_Color, psystem->m_FadeDestinationColor); + } + + if ( psystem->Flags & CLIPOUT2D ) + { + if ( particle->m_vecPosition.x < -10.0f || particle->m_vecPosition.x > SCREEN_WIDTH + 10.0f + || particle->m_vecPosition.y < -10.0f || particle->m_vecPosition.y > SCREEN_HEIGHT + 10.0f ) + { + bRemoveParticle = true; + continue; + } + } + + if ( !(psystem->Flags & SCREEN_TRAIL) ) + { + float size; + + if ( particle->m_fExpansionRate > 0.0f ) + { + float speed = Max(vecWind.Magnitude(), vecMoveStep.Magnitude()); + + if ( psystem->m_Type == PARTICLE_EXHAUST_FUMES || psystem->m_Type == PARTICLE_ENGINE_STEAM ) + speed *= 2.0f; + + if ( ( psystem->m_Type == PARTICLE_BOAT_SPLASH || psystem->m_Type == PARTICLE_CAR_SPLASH ) + && particle->m_fSize > 1.2f ) + { + size = particle->m_fSize - (1.0f + speed) * particle->m_fExpansionRate; + particle->m_vecVelocity.z -= 0.15f; + } + else + size = particle->m_fSize + (1.0f + speed) * particle->m_fExpansionRate; + } + else + size = particle->m_fSize + particle->m_fExpansionRate; + + if ( psystem->m_Type == PARTICLE_WATERDROP ) + size = (size - Abs(vecMoveStep.x) * 0.000150000007f) + (Abs(vecMoveStep.z) * 0.0500000007f); //TODO: + + if ( size < 0.0f ) + { + bRemoveParticle = true; + continue; + } + + particle->m_fSize = size; + } + + switch ( psystem->m_nFrictionDecceleration ) + { + case 50: + particle->m_vecVelocity *= fFricDeccel50; + break; + + case 80: + particle->m_vecVelocity *= fFricDeccel80; + break; + + case 90: + particle->m_vecVelocity *= fFricDeccel90; + break; + + case 95: + particle->m_vecVelocity *= fFricDeccel95; + break; + + case 96: + particle->m_vecVelocity *= fFricDeccel96; + break; + + case 99: + particle->m_vecVelocity *= fFricDeccel99; + break; + } + + if ( psystem->m_fGravitationalAcceleration > 0.0f ) + { + if ( -50.0f * psystem->m_fGravitationalAcceleration < particle->m_vecVelocity.z ) + particle->m_vecVelocity.z -= psystem->m_fGravitationalAcceleration * CTimer::GetTimeStep(); + + if ( psystem->Flags & ZCHECK_FIRST ) + { + if ( particle->m_vecPosition.z < particle->m_fZGround ) + { + switch ( psystem->m_Type ) + { + case PARTICLE_RAINDROP: + case PARTICLE_RAINDROP_SMALL: + { + bRemoveParticle = true; + + if ( CGeneral::GetRandomNumber() & 1 ) + { + AddParticle(PARTICLE_RAIN_SPLASH, + CVector + ( + particle->m_vecPosition.x, + particle->m_vecPosition.y, + 0.05f + particle->m_fZGround + ), + CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 0, 0, 0, 0); + } + else + { + AddParticle(PARTICLE_RAIN_SPLASHUP, + CVector + ( + particle->m_vecPosition.x, + particle->m_vecPosition.y, + 0.05f + particle->m_fZGround + ), + CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 0, 0, 0, 0); + } + + continue; + } + break; + + case PARTICLE_WHEEL_WATER: + { + bRemoveParticle = true; + + int32 randVal = CGeneral::GetRandomNumber(); + + if ( randVal & 1 ) + { + if ( (randVal % 5) == 0 ) + { + AddParticle(PARTICLE_RAIN_SPLASH, + CVector + ( + particle->m_vecPosition.x, + particle->m_vecPosition.y, + 0.05f + particle->m_fZGround + ), + CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 0, 0, 0, 0); + } + else + { + AddParticle(PARTICLE_RAIN_SPLASHUP, + CVector + ( + particle->m_vecPosition.x, + particle->m_vecPosition.y, + 0.05f + particle->m_fZGround + ), + CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 0, 0, 0, 0); + } + + } + continue; + } + break; + + case PARTICLE_BLOOD: + case PARTICLE_BLOOD_SMALL: + { + bRemoveParticle = true; + + CVector vecPosn = particle->m_vecPosition; + vecPosn.z += 1.0f; + + Randomizer++; + int32 randVal = int32(Randomizer & 7); + + if ( randVal == 5 ) + { + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &vecPosn, + 0.1f, 0.0f, 0.0f, -0.1f, + 255, + 255, 0, 0, + 4.0f, (CGeneral::GetRandomNumber() & 4095) + 2000, 1.0f); + } + else if ( randVal == 2 ) + { + CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &vecPosn, + 0.2f, 0.0f, 0.0f, -0.2f, + 255, + 255, 0, 0, + 4.0f, (CGeneral::GetRandomNumber() & 4095) + 8000, 1.0f); + } + continue; + } + break; + default: break; + } + } + } + else if ( psystem->Flags & ZCHECK_STEP ) + { + CColPoint point; + CEntity *entity; + + if ( CWorld::ProcessVerticalLine(particle->m_vecPosition, vecPos.z, point, entity, + true, true, false, false, true, false, nil) ) + { + if ( vecPos.z <= point.point.z ) + { + vecPos.z = point.point.z; + if ( psystem->m_Type == PARTICLE_DEBRIS2 ) + { + particle->m_vecVelocity.x *= 0.8f; + particle->m_vecVelocity.y *= 0.8f; + particle->m_vecVelocity.z *= -0.4f; + if ( particle->m_vecVelocity.z < 0.005f ) + particle->m_vecVelocity.z = 0.0f; + } + } + } + } + else if ( psystem->Flags & ZCHECK_BUMP ) + { + if ( particle->m_vecPosition.z < particle->m_fZGround ) + { + switch ( psystem->m_Type ) + { + case PARTICLE_GUNSHELL_FIRST: + case PARTICLE_GUNSHELL: + { + bRemoveParticle = true; + + AddParticle(PARTICLE_GUNSHELL_BUMP1, + CVector + ( + particle->m_vecPosition.x, + particle->m_vecPosition.y, + 0.05f + particle->m_fZGround + ), + CVector + ( + CGeneral::GetRandomNumberInRange(-0.02f, 0.02f), + CGeneral::GetRandomNumberInRange(-0.02f, 0.02f), + CGeneral::GetRandomNumberInRange(0.05f, 0.1f) + ), + nil, + particle->m_fSize, color, particle->m_nRotationStep, 0, 0, 0); + + PlayOneShotScriptObject(SCRIPT_SOUND_GUNSHELL_DROP, particle->m_vecPosition); + } + break; + + case PARTICLE_GUNSHELL_BUMP1: + { + bRemoveParticle = true; + + AddParticle(PARTICLE_GUNSHELL_BUMP2, + CVector + ( + particle->m_vecPosition.x, + particle->m_vecPosition.y, + 0.05f + particle->m_fZGround + ), + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.03f, 0.06f)), + nil, + particle->m_fSize, color, 0, 0, 0, 0); + + PlayOneShotScriptObject(SCRIPT_SOUND_GUNSHELL_DROP_SOFT, particle->m_vecPosition); + } + break; + + case PARTICLE_GUNSHELL_BUMP2: + { + bRemoveParticle = true; + continue; + } + break; + default: break; + } + } + } + } + else + { + if ( psystem->m_fGravitationalAcceleration < 0.0f ) + { + if ( -5.0f * psystem->m_fGravitationalAcceleration > particle->m_vecVelocity.z ) + particle->m_vecVelocity.z -= psystem->m_fGravitationalAcceleration * CTimer::GetTimeStep(); + } + else + { + if ( psystem->Flags & ZCHECK_STEP ) + { + CColPoint point; + CEntity *entity; + + if ( CWorld::ProcessVerticalLine(particle->m_vecPosition, vecPos.z, point, entity, + true, false, false, false, true, false, nil) ) + { + if ( vecPos.z <= point.point.z ) + { + vecPos.z = point.point.z; + if ( psystem->m_Type == PARTICLE_HELI_ATTACK ) + { + bRemoveParticle = true; + AddParticle(PARTICLE_STEAM, vecPos, CVector(0.0f, 0.0f, 0.05f), nil, 0.2f, 0, 0, 0, 0); + continue; + } + } + } + } + } + } + + if ( particle->m_nFadeToBlackTimer != 0 ) + { + particle->m_nColorIntensity = Clamp(particle->m_nColorIntensity - particle->m_nFadeToBlackTimer, + 0, 255); + } + + if ( particle->m_nFadeAlphaTimer != 0 ) + { + particle->m_nAlpha = Clamp(particle->m_nAlpha - particle->m_nFadeAlphaTimer, + 0, 255); + if ( particle->m_nAlpha == 0 ) + { + bRemoveParticle = true; + continue; + } + } + + if ( psystem->m_nZRotationAngleChangeAmount != 0 ) + { + if ( particle->m_nZRotationTimer >= psystem->m_nZRotationChangeTime ) + { + particle->m_nZRotationTimer = 0; + particle->m_nCurrentZRotation += psystem->m_nZRotationAngleChangeAmount; + } + else + ++particle->m_nZRotationTimer; + } + + if ( psystem->m_fZRadiusChangeAmount != 0.0f ) + { + if ( particle->m_nZRadiusTimer >= psystem->m_nZRadiusChangeTime ) + { + particle->m_nZRadiusTimer = 0; + particle->m_fCurrentZRadius += psystem->m_fZRadiusChangeAmount; + } + else + ++particle->m_nZRadiusTimer; + } + + if ( psystem->m_nAnimationSpeed != 0 ) + { + if ( particle->m_nAnimationSpeedTimer > psystem->m_nAnimationSpeed ) + { + particle->m_nAnimationSpeedTimer = 0; + + if ( ++particle->m_nCurrentFrame > psystem->m_nFinalAnimationFrame ) + { + if ( psystem->Flags & CYCLE_ANIM ) + particle->m_nCurrentFrame = psystem->m_nStartAnimationFrame; + else + --particle->m_nCurrentFrame; + } + } + else + ++particle->m_nAnimationSpeedTimer; + } + + if ( particle->m_nRotationStep != 0 ) +#ifdef FIX_BUGS + particle->m_nRotation = CGeneral::LimitAngle(particle->m_nRotation + particle->m_nRotationStep); +#else + particle->m_nRotation += particle->m_nRotationStep; +#endif + + if ( particle->m_fCurrentZRadius != 0.0f ) + { + int32 nSinCosIndex = particle->m_nCurrentZRotation % SIN_COS_TABLE_SIZE; + + float fX = (Cos(nSinCosIndex) - Sin(nSinCosIndex)) * particle->m_fCurrentZRadius; + + float fY = (Sin(nSinCosIndex) + Cos(nSinCosIndex)) * particle->m_fCurrentZRadius; + + vecPos -= particle->m_vecParticleMovementOffset; + + vecPos += CVector(fX, fY, 0.0f); + + particle->m_vecParticleMovementOffset = CVector(fX, fY, 0.0f); + } + + particle->m_vecPosition = vecPos; + } + } +} + +void CParticle::Render() +{ + PUSH_RENDERGROUP("CParticle::Render"); + + RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void *)rwTEXTUREADDRESSWRAP); + RwRenderStateSet(rwRENDERSTATETEXTUREPERSPECTIVE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); + + CSprite::InitSpriteBuffer2D(); + + uint32 flags = DRAW_OPAQUE; + + RwRaster *prevFrame = nil; + + for ( int32 i = 0; i < MAX_PARTICLES; i++ ) + { + tParticleSystemData *psystem = &mod_ParticleSystemManager.m_aParticles[i]; + bool particleBanned = false; + CParticle *particle = psystem->m_pParticles; + + RwRaster **frames = psystem->m_ppRaster; + tParticleType type = psystem->m_Type; + + if ( type == PARTICLE_ENGINE_SMOKE + || type == PARTICLE_ENGINE_SMOKE2 + || type == PARTICLE_ENGINE_STEAM + || type == PARTICLE_CARFLAME_SMOKE + || type == PARTICLE_RUBBER_SMOKE + || type == PARTICLE_BURNINGRUBBER_SMOKE + || type == PARTICLE_EXHAUST_FUMES + || type == PARTICLE_CARCOLLISION_DUST ) + { + particleBanned = true; + } + + if ( particle ) + { + if ( (flags & DRAW_OPAQUE) != (psystem->Flags & DRAW_OPAQUE) + || (flags & DRAW_DARK) != (psystem->Flags & DRAW_DARK) ) + { + CSprite::FlushSpriteBuffer(); + + if ( psystem->Flags & DRAW_OPAQUE ) + { + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); + } + else + { + if ( psystem->Flags & DRAW_DARK ) + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); + else + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE); + + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDONE); + } + + flags = psystem->Flags; + } + + if ( frames != nil ) + { + RwRaster *curFrame = *frames; + if ( curFrame != prevFrame ) + { + CSprite::FlushSpriteBuffer(); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)curFrame); + prevFrame = curFrame; + } + } + } + + while ( particle != nil ) + { + bool canDraw = true; + + if ( particle->m_nAlpha == 0 ) + canDraw = false; + + if ( canDraw && psystem->m_nFinalAnimationFrame != 0 && frames != nil ) + { + RwRaster *curFrame = frames[particle->m_nCurrentFrame]; + if ( prevFrame != curFrame ) + { + CSprite::FlushSpriteBuffer(); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)curFrame); + prevFrame = curFrame; + } + } + + if ( canDraw && psystem->Flags & DRAWTOP2D ) + { + float screenZ; +#ifdef FIX_BUGS + bool zIsZero = true; + if ( particle->m_vecPosition.z != 0.0f ) { +#endif + screenZ = (particle->m_vecPosition.z - CDraw::GetNearClipZ()) + * (CSprite::GetFarScreenZ() - CSprite::GetNearScreenZ()) + * CDraw::GetFarClipZ() + / ( (CDraw::GetFarClipZ() - CDraw::GetNearClipZ()) * particle->m_vecPosition.z ) + + CSprite::GetNearScreenZ(); +#ifdef FIX_BUGS + zIsZero = false; + } +#endif + + float stretchTexW; + float stretchTexH; + + if ( i == PARTICLE_RAINDROP || i == PARTICLE_RAINDROP_SMALL || i == PARTICLE_RAINDROP_2D ) + { + stretchTexW = CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.x * (float)particle->m_nCurrentFrame + 63.0f; + stretchTexH = CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.y * (float)particle->m_nCurrentFrame + 63.0f; + } + else + { + stretchTexW = CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.x + 63.0f; + stretchTexH = CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.y + 63.0f; + } + +#ifdef FIX_BUGS + if (!zIsZero) { +#endif + + if ( i == PARTICLE_WATERDROP ) + { + int32 timeLeft = (particle->m_nTimeWhenWillBeDestroyed - CTimer::GetTimeInMilliseconds()) / particle->m_nTimeWhenWillBeDestroyed; + + stretchTexH += (1.0f - (float)timeLeft ) * psystem->m_vecTextureStretch.y; + + RwRect rect; + + rect.x = int32(particle->m_vecPosition.x - SCREEN_STRETCH_X(particle->m_fSize * stretchTexW)); + rect.y = int32(particle->m_vecPosition.y - SCREEN_STRETCH_Y(particle->m_fSize * stretchTexH)); + rect.w = int32(particle->m_vecPosition.x + SCREEN_STRETCH_X(particle->m_fSize * stretchTexW)); + rect.h = int32(particle->m_vecPosition.y + SCREEN_STRETCH_Y(particle->m_fSize * stretchTexH)); + + FxType fxtype; + + if ( particle->m_nCurrentFrame != 0 ) + fxtype = FXTYPE_WATER2; + else + fxtype = FXTYPE_WATER1; + + CMBlur::AddRenderFx(Scene.camera, &rect, screenZ, fxtype); + + canDraw = false; + } + + if ( i == PARTICLE_BLOODDROP ) + { + int32 timeLeft = (particle->m_nTimeWhenWillBeDestroyed - CTimer::GetTimeInMilliseconds()) / particle->m_nTimeWhenWillBeDestroyed; + + stretchTexH += (1.0f + (float)timeLeft) * psystem->m_vecTextureStretch.y; + stretchTexW += (1.0f - (float)timeLeft) * psystem->m_vecTextureStretch.x; + + RwRect rect; + + rect.x = int32(particle->m_vecPosition.x - SCREEN_STRETCH_X(particle->m_fSize * stretchTexW)); + rect.y = int32(particle->m_vecPosition.y - SCREEN_STRETCH_Y(particle->m_fSize * stretchTexH)); + rect.w = int32(particle->m_vecPosition.x + SCREEN_STRETCH_X(particle->m_fSize * stretchTexW)); + rect.h = int32(particle->m_vecPosition.y + SCREEN_STRETCH_Y(particle->m_fSize * stretchTexH)); + + FxType fxtype; + + if ( particle->m_nCurrentFrame ) + fxtype = FXTYPE_BLOOD2; + else + fxtype = FXTYPE_BLOOD1; + + CMBlur::AddRenderFx(Scene.camera, &rect, screenZ, fxtype); + + canDraw = false; + } + + if ( i == PARTICLE_HEATHAZE_IN_DIST ) + { + RwRect rect; + + rect.x = int32(particle->m_vecPosition.x - SCREEN_STRETCH_X(particle->m_fSize * stretchTexW)); + rect.y = int32(particle->m_vecPosition.y - SCREEN_STRETCH_Y(particle->m_fSize * stretchTexH * 0.15f)); + rect.w = int32(particle->m_vecPosition.x + SCREEN_STRETCH_X(particle->m_fSize * stretchTexW)); + rect.h = int32(particle->m_vecPosition.y + SCREEN_STRETCH_Y(particle->m_fSize * stretchTexH * 0.15f)); + + CMBlur::AddRenderFx(Scene.camera, &rect, screenZ, FXTYPE_HEATHAZE); + + canDraw = false; + } + + if ( i == PARTICLE_HEATHAZE ) + { + RwRect rect; + + switch ( TheCamera.GetLookDirection() ) + { + case LOOKING_LEFT: + rect.x = int32(particle->m_vecPosition.x - SCREEN_STRETCH_X(particle->m_fSize * psystem->m_vecTextureStretch.x * 2.0f)); + rect.y = int32(particle->m_vecPosition.y - SCREEN_STRETCH_Y(particle->m_fSize * psystem->m_vecTextureStretch.y)); + rect.w = int32(particle->m_vecPosition.x - SCREEN_STRETCH_X(particle->m_fSize * psystem->m_vecTextureStretch.x)); + rect.h = int32(particle->m_vecPosition.y + SCREEN_STRETCH_Y(particle->m_fSize * psystem->m_vecTextureStretch.y)); + + break; + + case LOOKING_RIGHT: + rect.x = int32(particle->m_vecPosition.x + SCREEN_STRETCH_X(particle->m_fSize * psystem->m_vecTextureStretch.x)); + rect.y = int32(particle->m_vecPosition.y - SCREEN_STRETCH_Y(particle->m_fSize * psystem->m_vecTextureStretch.y)); + rect.w = int32(particle->m_vecPosition.x + SCREEN_STRETCH_X(particle->m_fSize * psystem->m_vecTextureStretch.x * 4.0f)); + rect.h = int32(particle->m_vecPosition.y + SCREEN_STRETCH_Y(particle->m_fSize * psystem->m_vecTextureStretch.y)); + + break; + + default: + rect.x = int32(particle->m_vecPosition.x - SCREEN_STRETCH_X(particle->m_fSize * psystem->m_vecTextureStretch.x)); + rect.y = int32(particle->m_vecPosition.y - SCREEN_STRETCH_Y(particle->m_fSize * psystem->m_vecTextureStretch.y)); + rect.w = int32(particle->m_vecPosition.x + SCREEN_STRETCH_X(particle->m_fSize * psystem->m_vecTextureStretch.x)); + rect.h = int32(particle->m_vecPosition.y + SCREEN_STRETCH_Y(particle->m_fSize * psystem->m_vecTextureStretch.y)); + + break; + } + + CMBlur::AddRenderFx(Scene.camera, &rect, screenZ, FXTYPE_HEATHAZE); + + canDraw = false; + } +#ifdef FIX_BUGS + } + if ( !(zIsZero && (i == PARTICLE_WATERDROP || i == PARTICLE_BLOODDROP || i == PARTICLE_HEATHAZE_IN_DIST || i == PARTICLE_HEATHAZE) ) ) +#endif + if ( canDraw ) + { + if ( particle->m_nRotation != 0 ) + { + CSprite::RenderBufferedOneXLUSprite2D_Rotate_Dimension( + particle->m_vecPosition.x, + particle->m_vecPosition.y, + particle->m_fSize * stretchTexW, + particle->m_fSize * stretchTexH, + particle->m_Color, + particle->m_nColorIntensity, + DEGTORAD((float)particle->m_nRotation), + particle->m_nAlpha); + } + else + { + CSprite::RenderBufferedOneXLUSprite2D( + particle->m_vecPosition.x, + particle->m_vecPosition.y, + particle->m_fSize * stretchTexW, + particle->m_fSize * stretchTexH, + particle->m_Color, + particle->m_nColorIntensity, + particle->m_nAlpha); + } + } + + canDraw = false; + } + + if ( canDraw ) + { + CVector coors; + float w; + float h; + + if ( CSprite::CalcScreenCoors(particle->m_vecPosition, &coors, &w, &h, true) ) + { + + if ( i == PARTICLE_ENGINE_STEAM + || i == PARTICLE_ENGINE_SMOKE + || i == PARTICLE_ENGINE_SMOKE2 + || i == PARTICLE_CARFLAME_SMOKE + || i == PARTICLE_CARCOLLISION_DUST + || i == PARTICLE_EXHAUST_FUMES + || i == PARTICLE_RUBBER_SMOKE + || i == PARTICLE_BURNINGRUBBER_SMOKE ) + { + switch ( TheCamera.GetLookDirection() ) + { + case LOOKING_LEFT: + case LOOKING_RIGHT: + w += CGeneral::GetRandomNumberInRange(1.0f, 7.5f) * psystem->m_vecTextureStretch.x; + h += CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.y; + break; + + default: + w += CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.x; + h += CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.y; + break; + } + } + else if ( i == PARTICLE_WATER_HYDRANT ) + { + int32 timeLeft = (particle->m_nTimeWhenWillBeDestroyed - CTimer::GetTimeInMilliseconds()) / particle->m_nTimeWhenWillBeDestroyed; + + w += (1.0f - (float)timeLeft) * psystem->m_vecTextureStretch.x; + h += (1.0f - (float)timeLeft) * psystem->m_vecTextureStretch.y; + } + else if ( i == PARTICLE_FLYERS ) + { + w += psystem->m_vecTextureStretch.x; + h += psystem->m_vecTextureStretch.y; + + w = Max(w, 12.0f); + h = Max(h, 12.0f); + } + else + { + w += CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.x; + h += CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.y; + } + + if ( i == PARTICLE_WATER_HYDRANT + || (!particleBanned || SCREEN_WIDTH * fParticleScaleLimit >= w) + && SCREEN_HEIGHT * fParticleScaleLimit >= h ) + { + if ( i == PARTICLE_WATER_HYDRANT ) + { + RwRect rect; + + if ( w > 0.0f ) + { + rect.x = int32(coors.x - SCREEN_STRETCH_X(particle->m_fSize * w)); + rect.w = int32(coors.x + SCREEN_STRETCH_X(particle->m_fSize * w)); + } + else + { + rect.w = int32(coors.x - SCREEN_STRETCH_X(particle->m_fSize * w)); + rect.x = int32(coors.x + SCREEN_STRETCH_X(particle->m_fSize * w)); + } + + if ( h > 0.0f ) + { + rect.y = int32(coors.y - SCREEN_STRETCH_Y(particle->m_fSize * h)); + rect.h = int32(coors.y + SCREEN_STRETCH_Y(particle->m_fSize * h)); + } + else + { + rect.h = int32(coors.y - SCREEN_STRETCH_Y(particle->m_fSize * h)); + rect.y = int32(coors.y + SCREEN_STRETCH_Y(particle->m_fSize * h)); + } + + float screenZ = (coors.z - CDraw::GetNearClipZ()) + * (CSprite::GetFarScreenZ() - CSprite::GetNearScreenZ()) * CDraw::GetFarClipZ() + / ( (CDraw::GetFarClipZ() - CDraw::GetNearClipZ()) * coors.z ) + CSprite::GetNearScreenZ(); + + CMBlur::AddRenderFx(Scene.camera, &rect, screenZ, FXTYPE_SPLASH1); + } + else + { + if ( particle->m_nRotation != 0 && i != PARTICLE_BEASTIE ) + { + CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(coors.x, coors.y, coors.z, + particle->m_fSize * w, particle->m_fSize * h, + particle->m_Color.red, + particle->m_Color.green, + particle->m_Color.blue, + particle->m_nColorIntensity, + 1.0f / coors.z, + DEGTORAD((float)particle->m_nRotation), + particle->m_nAlpha); + } + else if ( psystem->Flags & SCREEN_TRAIL ) + { + float fRotation; + float fTrailLength; + + if ( particle->m_fZGround == 0.0f ) + { + fTrailLength = 0.0f; + fRotation = 0.0f; + } + else + { + CVector2D vecDist + ( + coors.x - particle->m_fZGround, + coors.y - particle->m_fExpansionRate + ); + + float fDist = vecDist.Magnitude(); + + fTrailLength = fDist; + + float fRot = Asin(vecDist.x / fDist); + + fRotation = fRot; + + if ( vecDist.y < 0.0f ) + fRotation = -1.0f * fRot + DEGTORAD(180.0f); + + float fSpeed = particle->m_vecVelocity.Magnitude(); + + float fNewTrailLength = fSpeed * CTimer::GetTimeStep() * w * 2.0f; + + if ( fDist > fNewTrailLength ) + fTrailLength = fNewTrailLength; + } + + CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(coors.x, coors.y, coors.z, + particle->m_fSize * w, + particle->m_fSize * h + fTrailLength * psystem->m_fTrailLengthMultiplier, + particle->m_Color.red, + particle->m_Color.green, + particle->m_Color.blue, + particle->m_nColorIntensity, + 1.0f / coors.z, + fRotation, + particle->m_nAlpha); + + particle->m_fZGround = coors.x; // WTF ? + particle->m_fExpansionRate = coors.y; // WTF ? + } + else if ( psystem->Flags & SPEED_TRAIL ) + { + CVector vecPrevPos = particle->m_vecPosition - particle->m_vecVelocity; + float fRotation; + float fTrailLength; + CVector vecScreenPosition; + + if ( CSprite::CalcScreenCoors(vecPrevPos, &vecScreenPosition, &fTrailLength, &fRotation, true) ) + { + CVector2D vecDist + ( + coors.x - vecScreenPosition.x, + coors.y - vecScreenPosition.y + ); + + float fDist = vecDist.Magnitude(); + + fTrailLength = fDist; + + float fRot = Asin(vecDist.x / fDist); + + fRotation = fRot; + + if ( vecDist.y < 0.0f ) + fRotation = -1.0f * fRot + DEGTORAD(180.0f); + } + else + { + fRotation = 0.0f; + fTrailLength = 0.0f; + } + + CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(coors.x, coors.y, coors.z, + particle->m_fSize * w, + particle->m_fSize * h + fTrailLength * psystem->m_fTrailLengthMultiplier, + particle->m_Color.red, + particle->m_Color.green, + particle->m_Color.blue, + particle->m_nColorIntensity, + 1.0f / coors.z, + fRotation, + particle->m_nAlpha); + } + else if ( psystem->Flags & VERT_TRAIL ) + { + float fTrailLength = fabsf(particle->m_vecVelocity.z * 10.0f); + + CSprite::RenderBufferedOneXLUSprite(coors.x, coors.y, coors.z, + particle->m_fSize * w, + (particle->m_fSize + fTrailLength * psystem->m_fTrailLengthMultiplier) * h, + particle->m_Color.red, + particle->m_Color.green, + particle->m_Color.blue, + particle->m_nColorIntensity, + 1.0f / coors.z, + particle->m_nAlpha); + } + else if ( i == PARTICLE_RAINDROP_SMALL ) + { + CSprite::RenderBufferedOneXLUSprite(coors.x, coors.y, coors.z, + particle->m_fSize * w * 0.05f, + particle->m_fSize * h, + particle->m_Color.red, + particle->m_Color.green, + particle->m_Color.blue, + particle->m_nColorIntensity, + 1.0f / coors.z, + particle->m_nAlpha); + } + /*else if ( i == PARTICLE_BOAT_WAKE )*/ + else + { + CSprite::RenderBufferedOneXLUSprite(coors.x, coors.y, coors.z, + particle->m_fSize * w, + particle->m_fSize * h, + particle->m_Color.red, + particle->m_Color.green, + particle->m_Color.blue, + particle->m_nColorIntensity, + 1.0f / coors.z, + particle->m_nAlpha); + } + } + } + } + } + + particle = particle->m_pNext; + } + + CSprite::FlushSpriteBuffer(); + + } + + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); + + POP_RENDERGROUP(); +} + +void CParticle::RemovePSystem(tParticleType type) +{ + tParticleSystemData *psystemdata = &mod_ParticleSystemManager.m_aParticles[type]; + + for ( CParticle *particle = psystemdata->m_pParticles; particle; particle = psystemdata->m_pParticles ) + RemoveParticle(particle, nil, psystemdata); +} + +void CParticle::RemoveParticle(CParticle *pParticle, CParticle *pPrevParticle, tParticleSystemData *pPSystemData) +{ + if ( pPSystemData->m_Type == PARTICLE_WATERDROP ) + --numWaterDropOnScreen; + + if ( pPrevParticle ) + pPrevParticle->m_pNext = pParticle->m_pNext; + else + pPSystemData->m_pParticles = pParticle->m_pNext; + + pParticle->m_pNext = m_pUnusedListHead; + m_pUnusedListHead = pParticle; +} + +void CParticle::AddJetExplosion(CVector const &vecPos, float fPower, float fSize) +{ + CRGBA color(240, 240, 240, 255); + + if ( fPower < 1.0f ) + fPower = 1.0f; + + CVector vecRandOffset + ( + CGeneral::GetRandomNumberInRange(-0.4f, 0.4f), + CGeneral::GetRandomNumberInRange(-0.4f, 0.4f), + CGeneral::GetRandomNumberInRange(0.1f, 0.3f) + ); + + vecRandOffset *= 2.0f; + + CVector vecStepPos = vecPos; + + for ( int32 i = 0; i < int32(fPower * 4.0f); i++ ) + { + AddParticle(PARTICLE_EXPLOSION_MFAST, + vecStepPos, + CVector + ( + CGeneral::GetRandomNumberInRange(-0.02f, 0.02f), + CGeneral::GetRandomNumberInRange(-0.02f, 0.02f), + CGeneral::GetRandomNumberInRange(-0.02f, 0.0f) + ), + nil, + fSize, color, 0, 0, 0, 0); + + AddParticle(PARTICLE_EXPLOSION_MFAST, + vecStepPos, + CVector + ( + CGeneral::GetRandomNumberInRange(-0.04f, 0.04f), + CGeneral::GetRandomNumberInRange(-0.04f, 0.04f), + CGeneral::GetRandomNumberInRange(0.0f, 0.07f) + ), + nil, + fSize, color, 0, 0, 0, 0); + + AddParticle(PARTICLE_EXPLOSION_MFAST, + vecStepPos, + CVector + ( + CGeneral::GetRandomNumberInRange(-0.04f, 0.04f), + CGeneral::GetRandomNumberInRange(-0.04f, 0.04f), + CGeneral::GetRandomNumberInRange(0.0f, 0.07f) + ), + nil, + fSize, color, 0, 0, 0, 0); + + vecStepPos += vecRandOffset; + } +} + +void CParticle::AddYardieDoorSmoke(CVector const &vecPos, CMatrix const &matMatrix) +{ + CRGBA color(0, 0, 0, 0); + + CMatrix invMat(Invert(matMatrix)); + + CVector vecBasePos = matMatrix * (invMat * vecPos + CVector(0.0f, -1.0f, 0.5f)); + + for ( int32 i = 0; i < 5; i++ ) + { + CVector pos = vecBasePos; + + pos.x += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f); + pos.y += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f); + + AddParticle(PARTICLE_CARCOLLISION_DUST, + pos, + CVector(0.0f, 0.0f, 0.0f), + nil, + 0.3f, color, 0, 0, 0, 0); + } +} + +void CParticle::CalWindDir(CVector *vecDirIn, CVector *vecDirOut) +{ + vecDirOut->x = (Cos(128) * vecDirIn->x) + (Sin(128) * vecDirIn->y); + + vecDirOut->x = (Cos(128) * vecDirIn->x) + (Sin(128) * vecDirIn->y) * CWeather::Wind; + vecDirOut->y = (Sin(128) * vecDirIn->x) - (Cos(128) * vecDirIn->y) * CWeather::Wind; +} + +void CParticle::HandleShipsAtHorizonStuff() +{ + tParticleSystemData *psystemdata = &mod_ParticleSystemManager.m_aParticles[PARTICLE_SHIP_SIDE]; + + for ( CParticle *particle = psystemdata->m_pParticles; particle; particle = particle->m_pNext ) + { + if ( CTimer::GetTimeInMilliseconds() > particle->m_nTimeWhenWillBeDestroyed - 32000 + && CTimer::GetTimeInMilliseconds() < particle->m_nTimeWhenWillBeDestroyed - 22000 ) + { + particle->m_nAlpha = Min(particle->m_nAlpha + 1, 96); + } + if ( CTimer::GetTimeInMilliseconds() > particle->m_nTimeWhenWillBeDestroyed - 10000 ) + particle->m_nFadeAlphaTimer = 1; + } +} + +void CParticle::HandleShootableBirdsStuff(CEntity *entity, CVector const&camPos) +{ + float fHeadingRad = entity->GetForward().Heading(); + float fHeading = RADTODEG(fHeadingRad); + float fBirdAngle = ::Cos(DEGTORAD(1.5f)); + + tParticleSystemData *psystem = &mod_ParticleSystemManager.m_aParticles[PARTICLE_BIRD_FRONT]; + CParticle *particle = psystem->m_pParticles; + CParticle *prevParticle = nil; + bool bRemoveParticle; + + for ( ; particle != nil; _Next(particle, prevParticle, psystem, bRemoveParticle) ) + { + bRemoveParticle = false; + + CVector2D vecPos(particle->m_vecPosition.x, particle->m_vecPosition.y); + CVector2D vecCamPos(camPos.x, camPos.y); + + CVector2D vecDist = vecPos - vecCamPos; + vecDist.Normalise(); + + float fHead = DEGTORAD(fHeading); + + CVector2D vecDir(-::Sin(fHead), ::Cos(fHead)); + vecDir.Normalise(); + + float fDot = DotProduct2D(vecDir, vecDist); + + if ( fDot > 0.0f && fDot > fBirdAngle ) + { + if ( (camPos - particle->m_vecPosition).MagnitudeSqr() < 40000.0f ) + { + CStats::SeagullsKilled++; + + bRemoveParticle = true; + + for ( int32 i = 0; i < 8; i++ ) + { + CParticle *pBirdDerbis = AddParticle(PARTICLE_BIRD_DEBRIS, + particle->m_vecPosition, + CVector + ( + CGeneral::GetRandomNumberInRange(-3.0f, 3.0f), + CGeneral::GetRandomNumberInRange(-3.0f, 3.0f), + CGeneral::GetRandomNumberInRange(-3.0f, 3.0f) + ), + nil, + 0.3f, + particle->m_Color, + CGeneral::GetRandomNumberInRange(20, 40), + 0, + CGeneral::GetRandomNumber() & 3, + 200); + if ( pBirdDerbis ) + pBirdDerbis->m_nAlpha = particle->m_nAlpha; + } + } + } + } + +} + +void +CEntity::AddSteamsFromGround(CVector *unused) +{ + int i, n; + C2dEffect *effect; + CVector pos; + + n = CModelInfo::GetModelInfo(GetModelIndex())->GetNum2dEffects(); + for(i = 0; i < n; i++){ + effect = CModelInfo::GetModelInfo(GetModelIndex())->Get2dEffect(i); + if(effect->type != EFFECT_PARTICLE) + continue; + + pos = GetMatrix() * effect->pos; + switch(effect->particle.particleType){ + case 0: + CParticleObject::AddObject(POBJECT_PAVEMENT_STEAM, pos, effect->particle.dir, effect->particle.scale, false); + break; + case 1: + CParticleObject::AddObject(POBJECT_WALL_STEAM, pos, effect->particle.dir, effect->particle.scale, false); + break; + case 2: + CParticleObject::AddObject(POBJECT_DRY_ICE, pos, effect->particle.scale, false); + break; + case 3: + CParticleObject::AddObject(POBJECT_SMALL_FIRE, pos, effect->particle.dir, effect->particle.scale, false); + break; + case 4: + CParticleObject::AddObject(POBJECT_DARK_SMOKE, pos, effect->particle.dir, effect->particle.scale, false); + break; + case 5: + CParticleObject::AddObject(POBJECT_WATER_FOUNTAIN_VERT, pos, effect->particle.dir, effect->particle.scale, false); + break; + case 6: + CParticleObject::AddObject(POBJECT_WATER_FOUNTAIN_HORIZ, pos, effect->particle.dir, effect->particle.scale, false); + break; + } + } +} diff --git a/src/miami/renderer/Particle.h b/src/miami/renderer/Particle.h new file mode 100644 index 00000000..5542dc02 --- /dev/null +++ b/src/miami/renderer/Particle.h @@ -0,0 +1,108 @@ +#pragma once +#include "ParticleMgr.h" + + +class CEntity; + +class CParticle +{ +public: + enum + { + RAND_TABLE_SIZE = 20, + SIN_COS_TABLE_SIZE = 1024 + }; + + CVector m_vecPosition; + CVector m_vecVelocity; + uint32 m_nTimeWhenWillBeDestroyed; + uint32 m_nTimeWhenColorWillBeChanged; + float m_fZGround; + CVector m_vecParticleMovementOffset; + int16 m_nCurrentZRotation; + uint16 m_nZRotationTimer; + float m_fCurrentZRadius; + uint16 m_nZRadiusTimer; + uint8 m_nColorIntensity; + uint8 m_nAlpha; + float m_fSize; + float m_fExpansionRate; + int16 m_nFadeToBlackTimer; + int16 m_nFadeAlphaTimer; + int16 m_nAnimationSpeedTimer; + int16 m_nRotationStep; + int16 m_nRotation; + uint8 m_nCurrentFrame; + RwRGBA m_Color; + CParticle *m_pNext; + + CParticle() + { + ; + } + + ~CParticle() + { + ; + } + + static float ms_afRandTable[RAND_TABLE_SIZE]; + static CParticle *m_pUnusedListHead; + + static float m_SinTable[SIN_COS_TABLE_SIZE]; + static float m_CosTable[SIN_COS_TABLE_SIZE]; + + static float Sin(int32 value) { return m_SinTable[value]; } + static float Cos(int32 value) { return m_CosTable[value]; } + + static void ReloadConfig(); + static void Initialise(); + static void Shutdown(); + + static void AddParticlesAlongLine(tParticleType type, CVector const &vecStart, CVector const &vecEnd, CVector const &vecDir, float fPower, CEntity *pEntity = nil, float fSize = 0.0f, int32 nRotationSpeed = 0, int32 nRotation = 0, int32 nCurFrame = 0, int32 nLifeSpan = 0); + static void AddParticlesAlongLine(tParticleType type, CVector const &vecStart, CVector const &vecEnd, CVector const &vecDir, float fPower, CEntity *pEntity, float fSize, RwRGBA const&color, int32 nRotationSpeed = 0, int32 nRotation = 0, int32 nCurFrame = 0, int32 nLifeSpan = 0); + + static CParticle *AddParticle(tParticleType type, CVector const &vecPos, CVector const &vecDir, CEntity *pEntity = nil, float fSize = 0.0f, int32 nRotationSpeed = 0, int32 nRotation = 0, int32 nCurFrame = 0, int32 nLifeSpan = 0); + static CParticle *AddParticle(tParticleType type, CVector const &vecPos, CVector const &vecDir, CEntity *pEntity, float fSize, RwRGBA const &color, int32 nRotationSpeed = 0, int32 nRotation = 0, int32 nCurFrame = 0, int32 nLifeSpan = 0); + + static void Update(); + static void Render(); + + static void RemovePSystem(tParticleType type); + static void RemoveParticle(CParticle *pParticle, CParticle *pPrevParticle, tParticleSystemData *pPSystemData); + + static void _Next(CParticle *&pParticle, CParticle *&pPrevParticle, tParticleSystemData *pPSystemData, bool bRemoveParticle) + { + if ( bRemoveParticle ) + { + RemoveParticle(pParticle, pPrevParticle, pPSystemData); + + if ( pPrevParticle ) + pParticle = pPrevParticle->m_pNext; + else + pParticle = pPSystemData->m_pParticles; + } + else + { + pPrevParticle = pParticle; + pParticle = pParticle->m_pNext; + } + } + + static void AddJetExplosion(CVector const &vecPos, float fPower, float fSize); + static void AddYardieDoorSmoke(CVector const &vecPos, CMatrix const &matMatrix); + static void CalWindDir(CVector *vecDirIn, CVector *vecDirOut); + + static void HandleShipsAtHorizonStuff(); + static void HandleShootableBirdsStuff(CEntity *entity, CVector const&camPos); +}; + +extern bool clearWaterDrop; +extern int32 numWaterDropOnScreen; +extern RwRaster *gpCarSplashRaster[]; +extern RwRaster *gpHeatHazeRaster; +extern RwRaster *gpDotRaster; +extern RwRaster *gpRainDripRaster[]; +extern RwRaster *gpRainDripDarkRaster[]; + +VALIDATE_SIZE(CParticle, 0x58); diff --git a/src/miami/renderer/ParticleMgr.cpp b/src/miami/renderer/ParticleMgr.cpp new file mode 100644 index 00000000..f6919435 --- /dev/null +++ b/src/miami/renderer/ParticleMgr.cpp @@ -0,0 +1,255 @@ +#include "common.h" + +#include "main.h" +#include "FileMgr.h" +#include "ParticleMgr.h" + +cParticleSystemMgr mod_ParticleSystemManager; + +const char *ParticleFilename = "PARTICLE.CFG"; + +cParticleSystemMgr::cParticleSystemMgr() +{ + memset(this, 0, sizeof(*this)); +} + +void cParticleSystemMgr::Initialise() +{ + LoadParticleData(); + + for ( int32 i = 0; i < MAX_PARTICLES; i++ ) + m_aParticles[i].m_pParticles = nil; +} + +void cParticleSystemMgr::LoadParticleData() +{ + CFileMgr::SetDir("DATA"); + CFileMgr::LoadFile(ParticleFilename, work_buff, ARRAY_SIZE(work_buff), "r"); + CFileMgr::SetDir(""); + + tParticleSystemData *entry = nil; + int32 type = PARTICLE_FIRST; + + char *lineStart = (char *)work_buff; + char *lineEnd = lineStart + 1; + + char line[500]; + char delims[4]; + + while ( true ) + { + ASSERT(lineStart != nil); + ASSERT(lineEnd != nil); + + while ( *lineEnd != '\n' ) + ++lineEnd; + + int32 lineLength = lineEnd - lineStart; + + ASSERT(lineLength < 500); + + strncpy(line, lineStart, lineLength); + + line[lineLength] = '\0'; + + if ( !strcmp(line, ";the end") ) + break; + + if ( *line != ';' ) + { + int32 param = CFG_PARAM_FIRST; + + strcpy(delims, " \t"); + + char *value = strtok(line, delims); + + ASSERT(value != nil); + + do + { + switch ( param ) + { + case CFG_PARAM_PARTICLE_TYPE_NAME: + ASSERT(type < MAX_PARTICLES); + entry = &m_aParticles[type]; + ASSERT(entry != nil); + entry->m_Type = (tParticleType)type++; + strcpy(entry->m_aName, value); + break; + + case CFG_PARAM_RENDER_COLOURING_R: + entry->m_RenderColouring.red = atoi(value); + break; + + case CFG_PARAM_RENDER_COLOURING_G: + entry->m_RenderColouring.green = atoi(value); + break; + + case CFG_PARAM_RENDER_COLOURING_B: + entry->m_RenderColouring.blue = atoi(value); + break; + + case CFG_PARAM_INITIAL_COLOR_VARIATION: + entry->m_InitialColorVariation = Min(atoi(value), 100); + break; + + case CFG_PARAM_FADE_DESTINATION_COLOR_R: + entry->m_FadeDestinationColor.red = atoi(value); + break; + + case CFG_PARAM_FADE_DESTINATION_COLOR_G: + entry->m_FadeDestinationColor.green = atoi(value); + break; + + case CFG_PARAM_FADE_DESTINATION_COLOR_B: + entry->m_FadeDestinationColor.blue = atoi(value); + break; + + case CFG_PARAM_COLOR_FADE_TIME: + entry->m_ColorFadeTime = atoi(value); + break; + + case CFG_PARAM_DEFAULT_INITIAL_RADIUS: + entry->m_fDefaultInitialRadius = atof(value); + break; + + case CFG_PARAM_EXPANSION_RATE: + entry->m_fExpansionRate = atof(value); + break; + + case CFG_PARAM_INITIAL_INTENSITY: + entry->m_nFadeToBlackInitialIntensity = atoi(value); + break; + + case CFG_PARAM_FADE_TIME: + entry->m_nFadeToBlackTime = atoi(value); + break; + + case CFG_PARAM_FADE_AMOUNT: + entry->m_nFadeToBlackAmount = atoi(value); + break; + + case CFG_PARAM_INITIAL_ALPHA_INTENSITY: + entry->m_nFadeAlphaInitialIntensity = atoi(value); + break; + + case CFG_PARAM_FADE_ALPHA_TIME: + entry->m_nFadeAlphaTime = atoi(value); + break; + + case CFG_PARAM_FADE_ALPHA_AMOUNT: + entry->m_nFadeAlphaAmount = atoi(value); + break; + + case CFG_PARAM_INITIAL_ANGLE: + entry->m_nZRotationInitialAngle = atoi(value); + break; + + case CFG_PARAM_CHANGE_TIME: + entry->m_nZRotationChangeTime = atoi(value); + break; + + case CFG_PARAM_ANGLE_CHANGE_AMOUNT: + entry->m_nZRotationAngleChangeAmount = atoi(value); + break; + + case CFG_PARAM_INITIAL_Z_RADIUS: + entry->m_fInitialZRadius = atof(value); + break; + + case CFG_PARAM_Z_RADIUS_CHANGE_TIME: + entry->m_nZRadiusChangeTime = atoi(value); + break; + + case CFG_PARAM_Z_RADIUS_CHANGE_AMOUNT: + entry->m_fZRadiusChangeAmount = atof(value); + break; + + case CFG_PARAM_ANIMATION_SPEED: + entry->m_nAnimationSpeed = atoi(value); + break; + + case CFG_PARAM_START_ANIMATION_FRAME: + entry->m_nStartAnimationFrame = atoi(value); + break; + + case CFG_PARAM_FINAL_ANIMATION_FRAME: + entry->m_nFinalAnimationFrame = atoi(value); + break; + + case CFG_PARAM_ROTATION_SPEED: + entry->m_nRotationSpeed = atoi(value); + break; + + case CFG_PARAM_GRAVITATIONAL_ACCELERATION: + entry->m_fGravitationalAcceleration = atof(value); + break; + + case CFG_PARAM_FRICTION_DECCELERATION: + entry->m_nFrictionDecceleration = atoi(value); + break; + + case CFG_PARAM_LIFE_SPAN: + entry->m_nLifeSpan = atoi(value); + break; + + case CFG_PARAM_POSITION_RANDOM_ERROR: + entry->m_fPositionRandomError = atof(value); + break; + + case CFG_PARAM_VELOCITY_RANDOM_ERROR: + entry->m_fVelocityRandomError = atof(value); + break; + + case CFG_PARAM_EXPANSION_RATE_ERROR: + entry->m_fExpansionRateError = atof(value); + break; + + case CFG_PARAM_ROTATION_RATE_ERROR: + entry->m_nRotationRateError = atoi(value); + break; + + case CFG_PARAM_LIFE_SPAN_ERROR_SHAPE: + entry->m_nLifeSpanErrorShape = atoi(value); + break; + + case CFG_PARAM_TRAIL_LENGTH_MULTIPLIER: + entry->m_fTrailLengthMultiplier = atof(value); + break; + + case CFG_PARAM_STRETCH_VALUE_X: + entry->m_vecTextureStretch.x = atof(value); + break; + + case CFG_PARAM_STRETCH_VALUE_Y: + entry->m_vecTextureStretch.y = atof(value); + break; + + case CFG_PARAM_WIND_FACTOR: + entry->m_fWindFactor = atof(value); + break; + + case CFG_PARAM_PARTICLE_CREATE_RANGE: + entry->m_fCreateRange = SQR(atof(value)); + break; + + case CFG_PARAM_FLAGS: + entry->Flags = atoi(value); + break; + } + + value = strtok(nil, delims); + + param++; + + if ( param > CFG_PARAM_LAST ) + param = CFG_PARAM_FIRST; + + } while ( value != nil ); + } + + lineEnd++; + lineStart = lineEnd; + lineEnd++; + } +} diff --git a/src/miami/renderer/ParticleMgr.h b/src/miami/renderer/ParticleMgr.h new file mode 100644 index 00000000..f4afc018 --- /dev/null +++ b/src/miami/renderer/ParticleMgr.h @@ -0,0 +1,138 @@ +#pragma once + +#include "ParticleType.h" + +class CParticle; + +enum +{ + ZCHECK_FIRST = BIT(0), + ZCHECK_STEP = BIT(1), + DRAW_OPAQUE = BIT(2), + SCREEN_TRAIL = BIT(3), + SPEED_TRAIL = BIT(4), + RAND_VERT_V = BIT(5), + CYCLE_ANIM = BIT(6), + DRAW_DARK = BIT(7), + VERT_TRAIL = BIT(8), + _FLAG9 = BIT(9), // unused + DRAWTOP2D = BIT(10), + CLIPOUT2D = BIT(11), + ZCHECK_BUMP = BIT(12), + ZCHECK_BUMP_FIRST = BIT(13) +}; + + +struct tParticleSystemData +{ + tParticleType m_Type; + char m_aName[20]; + float m_fCreateRange; + float m_fDefaultInitialRadius; + float m_fExpansionRate; + uint16 m_nZRotationInitialAngle; + int16 m_nZRotationAngleChangeAmount; + uint16 m_nZRotationChangeTime; + uint16 m_nZRadiusChangeTime; + float m_fInitialZRadius; + float m_fZRadiusChangeAmount; + int16 m_nFadeToBlackTime; + uint8 m_nFadeToBlackInitialIntensity; + int16 m_nFadeToBlackAmount; + uint8 m_nFadeAlphaInitialIntensity; + int16 m_nFadeAlphaTime; + int16 m_nFadeAlphaAmount; + uint8 m_nStartAnimationFrame; + uint8 m_nFinalAnimationFrame; + uint16 m_nAnimationSpeed; + uint16 m_nRotationSpeed; + float m_fGravitationalAcceleration; + int32 m_nFrictionDecceleration; + int32 m_nLifeSpan; + float m_fPositionRandomError; + float m_fVelocityRandomError; + float m_fExpansionRateError; + int32 m_nRotationRateError; + uint32 m_nLifeSpanErrorShape; + float m_fTrailLengthMultiplier; + uint32 Flags; + CRGBA m_RenderColouring; + uint8 m_InitialColorVariation; + CRGBA m_FadeDestinationColor; + uint32 m_ColorFadeTime; + + CVector2D m_vecTextureStretch; + float m_fWindFactor; + + RwRaster **m_ppRaster; + CParticle *m_pParticles; +}; + +VALIDATE_SIZE(tParticleSystemData, 0x94); + +class cParticleSystemMgr +{ + enum + { + CFG_PARAM_PARTICLE_TYPE_NAME = 0, + CFG_PARAM_RENDER_COLOURING_R, + CFG_PARAM_RENDER_COLOURING_G, + CFG_PARAM_RENDER_COLOURING_B, + CFG_PARAM_INITIAL_COLOR_VARIATION, + CFG_PARAM_FADE_DESTINATION_COLOR_R, + CFG_PARAM_FADE_DESTINATION_COLOR_G, + CFG_PARAM_FADE_DESTINATION_COLOR_B, + CFG_PARAM_COLOR_FADE_TIME, + CFG_PARAM_DEFAULT_INITIAL_RADIUS, + CFG_PARAM_EXPANSION_RATE, + CFG_PARAM_INITIAL_INTENSITY, + CFG_PARAM_FADE_TIME, + CFG_PARAM_FADE_AMOUNT, + CFG_PARAM_INITIAL_ALPHA_INTENSITY, + CFG_PARAM_FADE_ALPHA_TIME, + CFG_PARAM_FADE_ALPHA_AMOUNT, + CFG_PARAM_INITIAL_ANGLE, + CFG_PARAM_CHANGE_TIME, + CFG_PARAM_ANGLE_CHANGE_AMOUNT, + CFG_PARAM_INITIAL_Z_RADIUS, + CFG_PARAM_Z_RADIUS_CHANGE_TIME, + CFG_PARAM_Z_RADIUS_CHANGE_AMOUNT, + CFG_PARAM_ANIMATION_SPEED, + CFG_PARAM_START_ANIMATION_FRAME, + CFG_PARAM_FINAL_ANIMATION_FRAME, + CFG_PARAM_ROTATION_SPEED, + CFG_PARAM_GRAVITATIONAL_ACCELERATION, + CFG_PARAM_FRICTION_DECCELERATION, + CFG_PARAM_LIFE_SPAN, + CFG_PARAM_POSITION_RANDOM_ERROR, + CFG_PARAM_VELOCITY_RANDOM_ERROR, + CFG_PARAM_EXPANSION_RATE_ERROR, + CFG_PARAM_ROTATION_RATE_ERROR, + CFG_PARAM_LIFE_SPAN_ERROR_SHAPE, + CFG_PARAM_TRAIL_LENGTH_MULTIPLIER, + + CFG_PARAM_STRETCH_VALUE_X, + CFG_PARAM_STRETCH_VALUE_Y, + CFG_PARAM_WIND_FACTOR, + + CFG_PARAM_PARTICLE_CREATE_RANGE, + CFG_PARAM_FLAGS, + + MAX_CFG_PARAMS, + CFG_PARAM_FIRST = CFG_PARAM_PARTICLE_TYPE_NAME, + CFG_PARAM_LAST = CFG_PARAM_FLAGS + }; + +public: + tParticleSystemData m_aParticles[MAX_PARTICLES]; + + cParticleSystemMgr(); + + void Initialise(); + void LoadParticleData(); + void RangeCheck(tParticleSystemData *pData) { } +}; + +VALIDATE_SIZE(cParticleSystemMgr, 0x2FFC); + +extern cParticleSystemMgr mod_ParticleSystemManager; diff --git a/src/miami/renderer/ParticleType.h b/src/miami/renderer/ParticleType.h new file mode 100644 index 00000000..9578083d --- /dev/null +++ b/src/miami/renderer/ParticleType.h @@ -0,0 +1,92 @@ +#pragma once + +enum tParticleType +{ + PARTICLE_SPARK = 0, + PARTICLE_SPARK_SMALL, + PARTICLE_WATER_SPARK, + PARTICLE_WHEEL_DIRT, + PARTICLE_SAND, + PARTICLE_WHEEL_WATER, + PARTICLE_BLOOD, + PARTICLE_BLOOD_SMALL, + PARTICLE_BLOOD_SPURT, + PARTICLE_DEBRIS, + PARTICLE_DEBRIS2, + PARTICLE_FLYERS, + PARTICLE_WATER, + PARTICLE_FLAME, + PARTICLE_FIREBALL, + PARTICLE_GUNFLASH, + PARTICLE_GUNFLASH_NOANIM, + PARTICLE_GUNSMOKE, + PARTICLE_GUNSMOKE2, + PARTICLE_CIGARETTE_SMOKE, + PARTICLE_SMOKE, + PARTICLE_SMOKE_SLOWMOTION, + PARTICLE_DRY_ICE, + PARTICLE_TEARGAS, + PARTICLE_GARAGEPAINT_SPRAY, + PARTICLE_SHARD, + PARTICLE_SPLASH, + PARTICLE_CARFLAME, + PARTICLE_STEAM, + PARTICLE_STEAM2, + PARTICLE_STEAM_NY, + PARTICLE_STEAM_NY_SLOWMOTION, + PARTICLE_GROUND_STEAM, + PARTICLE_ENGINE_STEAM, + PARTICLE_RAINDROP, + PARTICLE_RAINDROP_SMALL, + PARTICLE_RAIN_SPLASH, + PARTICLE_RAIN_SPLASH_BIGGROW, + PARTICLE_RAIN_SPLASHUP, + PARTICLE_WATERSPRAY, + PARTICLE_WATERDROP, + PARTICLE_BLOODDROP, + PARTICLE_EXPLOSION_MEDIUM, + PARTICLE_EXPLOSION_LARGE, + PARTICLE_EXPLOSION_MFAST, + PARTICLE_EXPLOSION_LFAST, + PARTICLE_CAR_SPLASH, + PARTICLE_BOAT_SPLASH, + PARTICLE_BOAT_THRUSTJET, + PARTICLE_WATER_HYDRANT, + PARTICLE_WATER_CANNON, + PARTICLE_EXTINGUISH_STEAM, + PARTICLE_PED_SPLASH, + PARTICLE_PEDFOOT_DUST, + PARTICLE_CAR_DUST, + PARTICLE_HELI_DUST, + PARTICLE_HELI_ATTACK, + PARTICLE_ENGINE_SMOKE, + PARTICLE_ENGINE_SMOKE2, + PARTICLE_CARFLAME_SMOKE, + PARTICLE_FIREBALL_SMOKE, + PARTICLE_PAINT_SMOKE, + PARTICLE_TREE_LEAVES, + PARTICLE_CARCOLLISION_DUST, + PARTICLE_CAR_DEBRIS, + PARTICLE_BIRD_DEBRIS, + PARTICLE_HELI_DEBRIS, + PARTICLE_EXHAUST_FUMES, + PARTICLE_RUBBER_SMOKE, + PARTICLE_BURNINGRUBBER_SMOKE, + PARTICLE_BULLETHIT_SMOKE, + PARTICLE_GUNSHELL_FIRST, + PARTICLE_GUNSHELL, + PARTICLE_GUNSHELL_BUMP1, + PARTICLE_GUNSHELL_BUMP2, + PARTICLE_ROCKET_SMOKE, + PARTICLE_TEST, + PARTICLE_BIRD_FRONT, + PARTICLE_SHIP_SIDE, + PARTICLE_BEASTIE, + PARTICLE_RAINDROP_2D, + PARTICLE_HEATHAZE, + PARTICLE_HEATHAZE_IN_DIST, + + MAX_PARTICLES, + PARTICLE_FIRST = PARTICLE_SPARK, + PARTICLE_LAST = PARTICLE_HEATHAZE_IN_DIST +}; \ No newline at end of file diff --git a/src/miami/renderer/PlayerSkin.cpp b/src/miami/renderer/PlayerSkin.cpp new file mode 100644 index 00000000..8ef7e099 --- /dev/null +++ b/src/miami/renderer/PlayerSkin.cpp @@ -0,0 +1,166 @@ +#include "common.h" + +#include "main.h" +#include "PlayerSkin.h" +#include "TxdStore.h" +#include "rtbmp.h" +#include "ClumpModelInfo.h" +#include "VisibilityPlugins.h" +#include "World.h" +#include "PlayerInfo.h" +#include "CdStream.h" +#include "FileMgr.h" +#include "Directory.h" +#include "RwHelper.h" +#include "Timer.h" +#include "Lights.h" +#include "MemoryMgr.h" + +RpClump *gpPlayerClump; +float gOldFov; + +int CPlayerSkin::m_txdSlot; + +void +FindPlayerDff(uint32 &offset, uint32 &size) +{ + int file; + CDirectory::DirectoryInfo info; + + file = CFileMgr::OpenFile("models\\gta3.dir", "rb"); + + do { + if (!CFileMgr::Read(file, (char*)&info, sizeof(CDirectory::DirectoryInfo))) + return; + } while (strcasecmp("player.dff", info.name) != 0); + + offset = info.offset; + size = info.size; +} + +void +LoadPlayerDff(void) +{ + RwStream *stream; + RwMemory mem; + uint32 offset, size; + uint8 *buffer; + bool streamWasAdded = false; + + if (CdStreamGetNumImages() == 0) { + CdStreamAddImage("models\\gta3.img"); + streamWasAdded = true; + } + + FindPlayerDff(offset, size); + buffer = (uint8*)RwMallocAlign(size << 11, 2048); + CdStreamRead(0, buffer, offset, size); + CdStreamSync(0); + + mem.start = buffer; + mem.length = size << 11; + stream = RwStreamOpen(rwSTREAMMEMORY, rwSTREAMREAD, &mem); + + if (RwStreamFindChunk(stream, rwID_CLUMP, nil, nil)) + gpPlayerClump = RpClumpStreamRead(stream); + + RwStreamClose(stream, &mem); + RwFreeAlign(buffer); + + if (streamWasAdded) + CdStreamRemoveImages(); +} + +void +CPlayerSkin::Initialise(void) +{ + // empty on PS2 + m_txdSlot = CTxdStore::AddTxdSlot("skin"); + CTxdStore::Create(m_txdSlot); + CTxdStore::AddRef(m_txdSlot); +} + +void +CPlayerSkin::Shutdown(void) +{ + // empty on PS2 + CTxdStore::RemoveTxdSlot(m_txdSlot); +} + +RwTexture * +CPlayerSkin::GetSkinTexture(const char *texName) +{ + RwTexture *tex; + RwRaster *raster; + int32 width, height, depth, format; + + CTxdStore::PushCurrentTxd(); + CTxdStore::SetCurrentTxd(m_txdSlot); + tex = RwTextureRead(texName, NULL); + CTxdStore::PopCurrentTxd(); + if (tex != nil) return tex; + + if (strcmp(DEFAULT_SKIN_NAME, texName) == 0 || texName[0] == '\0') + sprintf(gString, "models\\generic\\player.bmp"); + else + sprintf(gString, "skins\\%s.bmp", texName); + +#if 0 // we don't support .bmp custom skins in DCA3 + if (RwImage *image = RtBMPImageRead(gString)) { + RwImageFindRasterFormat(image, rwRASTERTYPETEXTURE, &width, &height, &depth, &format); + raster = RwRasterCreate(width, height, depth, format); + RwRasterSetFromImage(raster, image); + + tex = RwTextureCreate(raster); + RwTextureSetName(tex, texName); + RwTextureSetFilterMode(tex, rwFILTERLINEAR); + RwTexDictionaryAddTexture(CTxdStore::GetSlot(m_txdSlot)->texDict, tex); + + RwImageDestroy(image); + } +#endif + return tex; +} + +void +CPlayerSkin::BeginFrontendSkinEdit(void) +{ + LoadPlayerDff(); + RpClumpForAllAtomics(gpPlayerClump, CClumpModelInfo::SetAtomicRendererCB, (void*)CVisibilityPlugins::RenderPlayerCB); + CWorld::Players[0].LoadPlayerSkin(); + gOldFov = CDraw::GetFOV(); + CDraw::SetFOV(30.0f); +} + +void +CPlayerSkin::EndFrontendSkinEdit(void) +{ + RpClumpDestroy(gpPlayerClump); + gpPlayerClump = NULL; + CDraw::SetFOV(gOldFov); +} + +void +CPlayerSkin::RenderFrontendSkinEdit(void) +{ + static float rotation = 0.0f; + RwRGBAReal AmbientColor = { 0.65f, 0.65f, 0.65f, 1.0f }; + const RwV3d pos = { 1.35f, 0.35f, 7.725f }; + const RwV3d axis = { 0.0f, 1.0f, 0.0f }; + static uint32 LastFlash = 0; + + RwFrame *frame = RpClumpGetFrame(gpPlayerClump); + + if (CTimer::GetTimeInMillisecondsPauseMode() - LastFlash > 7) { + rotation += 2.0f; + if (rotation > 360.0f) + rotation -= 360.0f; + LastFlash = CTimer::GetTimeInMillisecondsPauseMode(); + } + RwFrameTransform(frame, RwFrameGetMatrix(RwCameraGetFrame(Scene.camera)), rwCOMBINEREPLACE); + RwFrameTranslate(frame, &pos, rwCOMBINEPRECONCAT); + RwFrameRotate(frame, &axis, rotation, rwCOMBINEPRECONCAT); + RwFrameUpdateObjects(frame); + SetAmbientColours(&AmbientColor); + RpClumpRender(gpPlayerClump); +} diff --git a/src/miami/renderer/PlayerSkin.h b/src/miami/renderer/PlayerSkin.h new file mode 100644 index 00000000..e0214ce0 --- /dev/null +++ b/src/miami/renderer/PlayerSkin.h @@ -0,0 +1,15 @@ +#pragma once + +#define DEFAULT_SKIN_NAME "$$\"\"" + +class CPlayerSkin +{ + static int m_txdSlot; +public: + static void Initialise(); + static void Shutdown(); + static RwTexture *GetSkinTexture(const char *texName); + static void BeginFrontendSkinEdit(); + static void EndFrontendSkinEdit(); + static void RenderFrontendSkinEdit(); +}; \ No newline at end of file diff --git a/src/miami/renderer/PointLights.cpp b/src/miami/renderer/PointLights.cpp new file mode 100644 index 00000000..13872401 --- /dev/null +++ b/src/miami/renderer/PointLights.cpp @@ -0,0 +1,330 @@ +#include "common.h" + +#include "main.h" +#include "CutsceneMgr.h" +#include "Lights.h" +#include "Camera.h" +#include "Weather.h" +#include "World.h" +#include "Collision.h" +#include "Sprite.h" +#include "Timer.h" +#include "PointLights.h" + +int16 CPointLights::NumLights; +CRegisteredPointLight CPointLights::aLights[NUMPOINTLIGHTS]; +CVector CPointLights::aCachedMapReads[32]; +float CPointLights::aCachedMapReadResults[32]; +int32 CPointLights::NextCachedValue; + +void +CPointLights::Init(void) +{ + for(int i = 0; i < ARRAY_SIZE(aCachedMapReads); i++){ + aCachedMapReads[i] = CVector(0.0f, 0.0f, 0.0f); + aCachedMapReadResults[i] = 0.0f; + } + NextCachedValue = 0; +} + +void +CPointLights::InitPerFrame(void) +{ + NumLights = 0; +} + +#define MAX_DIST 22.0f + +void +CPointLights::AddLight(uint8 type, CVector coors, CVector dir, float radius, float red, float green, float blue, uint8 fogType, bool castExtraShadows) +{ + CVector dist; + float distance; + + // The check is done in some weird way in the game + // we're doing it a bit better here + if(NumLights >= NUMPOINTLIGHTS) + return; + + dist = coors - TheCamera.GetPosition(); + if(Abs(dist.x) < MAX_DIST && Abs(dist.y) < MAX_DIST){ + distance = dist.Magnitude(); + if(distance < MAX_DIST){ + aLights[NumLights].type = type; + aLights[NumLights].fogType = fogType; + aLights[NumLights].coors = coors; + aLights[NumLights].dir = dir; + aLights[NumLights].radius = radius; + aLights[NumLights].castExtraShadows = castExtraShadows; + if(distance < MAX_DIST*0.75f){ + aLights[NumLights].red = red; + aLights[NumLights].green = green; + aLights[NumLights].blue = blue; + }else{ + float fade = 1.0f - (distance/MAX_DIST - 0.75f)*4.0f; + aLights[NumLights].red = red * fade; + aLights[NumLights].green = green * fade; + aLights[NumLights].blue = blue * fade; + } + NumLights++; + } + } +} + +float +CPointLights::GenerateLightsAffectingObject(Const CVector *objCoors) +{ + int i; + float ret; + CVector dist; + float radius, distance; + + ret = 1.0f; + for(i = 0; i < NumLights; i++){ + if(aLights[i].type == LIGHT_FOGONLY || aLights[i].type == LIGHT_FOGONLY_ALWAYS) + continue; + + // same weird distance calculation. simplified here + dist = aLights[i].coors - *objCoors; + radius = aLights[i].radius; + if(Abs(dist.x) < radius && + Abs(dist.y) < radius && + Abs(dist.z) < radius){ + + distance = dist.Magnitude(); + if(distance < radius){ + + float distNorm = distance/radius; + if(aLights[i].type == LIGHT_DARKEN){ + // darken the object the closer it is + ret *= distNorm; + }else{ + float intensity; + // distance fade + if(distNorm < 0.5f) + intensity = 1.0f; + else + intensity = 1.0f - (distNorm - 0.5f)/(1.0f - 0.5f); + + if(distance != 0.0f){ + CVector dir = dist / distance; + + if(aLights[i].type == LIGHT_DIRECTIONAL){ + float dot = -DotProduct(dir, aLights[i].dir); + intensity *= Max((dot-0.5f)*2.0f, 0.0f); + } + + if(intensity > 0.0f) + AddAnExtraDirectionalLight(Scene.world, + dir.x, dir.y, dir.z, + aLights[i].red*intensity, aLights[i].green*intensity, aLights[i].blue*intensity); + } + } + } + } + } + + return ret; +} + +extern RwRaster *gpPointlightRaster; + +void +CPointLights::RemoveLightsAffectingObject(void) +{ + RemoveExtraDirectionalLights(Scene.world); +} + +// for directional fog +#define FOG_AREA_LENGTH 12.0f +#define FOG_AREA_WIDTH 5.0f +// for pointlight fog +#define FOG_AREA_RADIUS 9.0f + +float FogSizes[8] = { 1.3f, 2.0f, 1.7f, 2.0f, 1.4f, 2.1f, 1.5f, 2.3f }; + +void +CPointLights::RenderFogEffect(void) +{ + int i; + float fogginess; + CColPoint point; + CEntity *entity; + float xmin, ymin; + float xmax, ymax; + int16 xi, yi; + CVector spriteCoors; + float spritew, spriteh; + + if(CCutsceneMgr::IsRunning()) + return; + + PUSH_RENDERGROUP("CPointLights::RenderFogEffect"); + + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpPointlightRaster); + + CSprite::InitSpriteBuffer(); + + for(i = 0; i < NumLights; i++){ + if(aLights[i].fogType != FOG_NORMAL && aLights[i].fogType != FOG_ALWAYS) + continue; + + fogginess = aLights[i].fogType == FOG_NORMAL ? CWeather::Foggyness : 1.0f; + if(fogginess == 0.0f) + continue; + + if(aLights[i].type == LIGHT_DIRECTIONAL){ + + // TODO: test this. haven't found directional fog so far + + float coors2X = aLights[i].coors.x + FOG_AREA_LENGTH*aLights[i].dir.x; + float coors2Y = aLights[i].coors.y + FOG_AREA_LENGTH*aLights[i].dir.y; + + if(coors2X < aLights[i].coors.x){ + xmin = coors2X; + xmax = aLights[i].coors.x; + }else{ + xmax = coors2X; + xmin = aLights[i].coors.x; + } + if(coors2Y < aLights[i].coors.y){ + ymin = coors2Y; + ymax = aLights[i].coors.y; + }else{ + ymax = coors2Y; + ymin = aLights[i].coors.y; + } + + xmin -= 5.0f; + ymin -= 5.0f; + xmax += 5.0f; + ymax += 5.0f; + + for(xi = (int16)xmin - (int16)xmin % 4; xi <= (int16)xmax + 4; xi += 4){ + for(yi = (int16)ymin - (int16)ymin % 4; yi <= (int16)ymax + 4; yi += 4){ + // Some kind of pseudo random number? + int r = (xi ^ yi)>>2 & 0xF; + if((r & 1) == 0) + continue; + + // Check if fog effect is close enough to directional line in x and y + float dx = xi - aLights[i].coors.x; + float dy = yi - aLights[i].coors.y; + float dot = dx*aLights[i].dir.x + dy*aLights[i].dir.y; + float distsq = sq(dx) + sq(dy); + float linedistsq = distsq - sq(dot); + if(dot > 0.0f && dot < FOG_AREA_LENGTH && linedistsq < sq(FOG_AREA_WIDTH)){ + CVector fogcoors(xi, yi, aLights[i].coors.z + 10.0f); + if(CWorld::ProcessVerticalLine(fogcoors, fogcoors.z - 20.0f, + point, entity, true, false, false, false, true, false, nil)){ + // Now same check again in xyz + fogcoors.z = point.point.z + 1.3f; + // actually we don't have to recalculate x and y, but the game does it that way + dx = xi - aLights[i].coors.x; + dy = yi - aLights[i].coors.y; + float dz = fogcoors.z - aLights[i].coors.z; + dot = dx*aLights[i].dir.x + dy*aLights[i].dir.y + dz*aLights[i].dir.z; + distsq = sq(dx) + sq(dy) + sq(dz); + linedistsq = distsq - sq(dot); + if(dot > 0.0f && dot < FOG_AREA_LENGTH && linedistsq < sq(FOG_AREA_WIDTH)){ + float intensity = 158.0f * fogginess; + // more intensity the smaller the angle + intensity *= dot/Sqrt(distsq); + // more intensity the closer to light source + intensity *= 1.0f - sq(dot/FOG_AREA_LENGTH); + // more intensity the closer to line + intensity *= 1.0f - sq(Sqrt(linedistsq) / FOG_AREA_WIDTH); + + if(CSprite::CalcScreenCoors(fogcoors, &spriteCoors, &spritew, &spriteh, true)) { + float rotation = (CTimer::GetTimeInMilliseconds()&0x1FFF) * 2*3.14f / 0x2000; + float size = FogSizes[r>>1]; + CSprite::RenderBufferedOneXLUSprite_Rotate_Aspect(spriteCoors.x, spriteCoors.y, spriteCoors.z, + spritew * size, spriteh * size, + aLights[i].red * intensity, aLights[i].green * intensity, aLights[i].blue * intensity, + intensity, 1/spriteCoors.z, rotation, 255); + } + } + } + } + } + } + + }else if(aLights[i].type == LIGHT_POINT || aLights[i].type == LIGHT_FOGONLY || aLights[i].type == LIGHT_FOGONLY_ALWAYS){ + float groundZ; + if(ProcessVerticalLineUsingCache(aLights[i].coors, &groundZ)){ + xmin = aLights[i].coors.x - FOG_AREA_RADIUS; + ymin = aLights[i].coors.y - FOG_AREA_RADIUS; + xmax = aLights[i].coors.x + FOG_AREA_RADIUS; + ymax = aLights[i].coors.y + FOG_AREA_RADIUS; + + for(xi = (int16)xmin - (int16)xmin % 2; xi <= (int16)xmax + 2; xi += 2){ + for(yi = (int16)ymin - (int16)ymin % 2; yi <= (int16)ymax + 2; yi += 2){ + // Some kind of pseudo random number? + int r = (xi ^ yi)>>1 & 0xF; + if((r & 1) == 0) + continue; + + float dx = xi - aLights[i].coors.x; + float dy = yi - aLights[i].coors.y; + float lightdist = Sqrt(sq(dx) + sq(dy)); + if(lightdist < FOG_AREA_RADIUS){ + dx = xi - TheCamera.GetPosition().x; + dy = yi - TheCamera.GetPosition().y; + float camdist = Sqrt(sq(dx) + sq(dy)); + if(camdist < MAX_DIST){ + float intensity; + // distance fade + if(camdist < MAX_DIST/2) + intensity = 1.0f; + else + intensity = 1.0f - (camdist - MAX_DIST/2) / (MAX_DIST/2); + intensity *= 132.0f * fogginess; + // more intensity the closer to light source + intensity *= 1.0f - sq(lightdist / FOG_AREA_RADIUS); + + CVector fogcoors(xi, yi, groundZ + 1.6f); + if(CSprite::CalcScreenCoors(fogcoors, &spriteCoors, &spritew, &spriteh, true)) { + float rotation = (CTimer::GetTimeInMilliseconds()&0x3FFF) * 2*3.14f / 0x4000; + float size = FogSizes[r>>1]; + CSprite::RenderBufferedOneXLUSprite_Rotate_Aspect(spriteCoors.x, spriteCoors.y, spriteCoors.z, + spritew * size, spriteh * size, + aLights[i].red * intensity, aLights[i].green * intensity, aLights[i].blue * intensity, + intensity, 1/spriteCoors.z, rotation, 255); + } + } + } + } + } + } + } + } + + CSprite::FlushSpriteBuffer(); + + POP_RENDERGROUP(); +} + +bool +CPointLights::ProcessVerticalLineUsingCache(CVector coors, float *groundZ) +{ + for(int i = 0; i < ARRAY_SIZE(aCachedMapReads); i++) + if(aCachedMapReads[i] == coors){ + *groundZ = aCachedMapReadResults[i]; + return true; + } + + CColPoint point; + CEntity *entity; + if(CWorld::ProcessVerticalLine(coors, coors.z - 20.0f, point, entity, true, false, false, false, true, false, nil)){ + aCachedMapReads[NextCachedValue] = coors; + aCachedMapReadResults[NextCachedValue] = point.point.z; + NextCachedValue = (NextCachedValue+1) % ARRAY_SIZE(aCachedMapReads); + *groundZ = point.point.z; + return true; + } + return false; +} diff --git a/src/miami/renderer/PointLights.h b/src/miami/renderer/PointLights.h new file mode 100644 index 00000000..827200b9 --- /dev/null +++ b/src/miami/renderer/PointLights.h @@ -0,0 +1,50 @@ +#pragma once + +class CRegisteredPointLight +{ +public: + CVector coors; + CVector dir; + float radius; + float red; + float green; + float blue; + int8 type; + int8 fogType; + bool castExtraShadows; +}; +VALIDATE_SIZE(CRegisteredPointLight, 0x2C); + +class CPointLights +{ +public: + static int16 NumLights; + static CRegisteredPointLight aLights[NUMPOINTLIGHTS]; + static CVector aCachedMapReads[32]; + static float aCachedMapReadResults[32]; + static int32 NextCachedValue; + + enum { + LIGHT_POINT, + LIGHT_DIRECTIONAL, + LIGHT_DARKEN, // no effects at all + // these have only fog, otherwise no difference? + // only used by CEntity::ProcessLightsForEntity it seems + // and there used together with fog type + LIGHT_FOGONLY_ALWAYS, + LIGHT_FOGONLY, + }; + enum { + FOG_NONE, + FOG_NORMAL, // taken from Foggyness + FOG_ALWAYS + }; + + static void Init(void); + static void InitPerFrame(void); + static void AddLight(uint8 type, CVector coors, CVector dir, float radius, float red, float green, float blue, uint8 fogType, bool castExtraShadows); + static float GenerateLightsAffectingObject(Const CVector *objCoors); + static void RemoveLightsAffectingObject(void); + static void RenderFogEffect(void); + static bool ProcessVerticalLineUsingCache(CVector coors, float *groundZ); +}; diff --git a/src/miami/renderer/RenderBuffer.cpp b/src/miami/renderer/RenderBuffer.cpp new file mode 100644 index 00000000..687cc76b --- /dev/null +++ b/src/miami/renderer/RenderBuffer.cpp @@ -0,0 +1,52 @@ +#include "common.h" + +#include "RenderBuffer.h" + +int32 TempBufferVerticesStored; +int32 TempBufferIndicesStored; + +VertexBufferUnion TempVertexBuffer; +RwImVertexIndex TempBufferRenderIndexList[TEMPBUFFERINDEXSIZE]; + +int RenderBuffer::VerticesToBeStored; +int RenderBuffer::IndicesToBeStored; + +void +RenderBuffer::ClearRenderBuffer(void) +{ + TempBufferVerticesStored = 0; + TempBufferIndicesStored = 0; +} + +void +RenderBuffer::StartStoring(int numIndices, int numVertices, RwImVertexIndex **indexStart, RwIm3DVertex **vertexStart) +{ + if(TempBufferIndicesStored + numIndices >= TEMPBUFFERINDEXSIZE) + RenderStuffInBuffer(); + if(TempBufferVerticesStored + numVertices >= TEMPBUFFERVERTSIZE) + RenderStuffInBuffer(); + *indexStart = &TempBufferRenderIndexList[TempBufferIndicesStored]; + *vertexStart = &TempBufferRenderVertices[TempBufferVerticesStored]; + IndicesToBeStored = numIndices; + VerticesToBeStored = numVertices; +} + +void +RenderBuffer::StopStoring(void) +{ + int i; + for(i = TempBufferIndicesStored; i < TempBufferIndicesStored+IndicesToBeStored; i++) + TempBufferRenderIndexList[i] += TempBufferVerticesStored; + TempBufferIndicesStored += IndicesToBeStored; + TempBufferVerticesStored += VerticesToBeStored; +} + +void +RenderBuffer::RenderStuffInBuffer(void) +{ + if(TempBufferVerticesStored && RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, rwIM3D_VERTEXUV)){ + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored); + RwIm3DEnd(); + } + ClearRenderBuffer(); +} diff --git a/src/miami/renderer/RenderBuffer.h b/src/miami/renderer/RenderBuffer.h new file mode 100644 index 00000000..a0f3e7b9 --- /dev/null +++ b/src/miami/renderer/RenderBuffer.h @@ -0,0 +1,25 @@ +class RenderBuffer +{ +public: + static int VerticesToBeStored; + static int IndicesToBeStored; + static void ClearRenderBuffer(void); + static void StartStoring(int numIndices, int numVertices, RwImVertexIndex **indexStart, RwIm3DVertex **vertexStart); + static void StopStoring(void); + static void RenderStuffInBuffer(void); +}; + +#define TEMPBUFFERVERTSIZE 512 +#define TEMPBUFFERINDEXSIZE 1024 + +struct VertexBufferUnion +{ + RwIm2DVertex im2d[TEMPBUFFERVERTSIZE]; + RwIm3DVertex im3d[TEMPBUFFERVERTSIZE]; +}; + +extern int32 TempBufferVerticesStored; +extern int32 TempBufferIndicesStored; +extern VertexBufferUnion TempVertexBuffer; +#define TempBufferRenderVertices (TempVertexBuffer.im3d) +extern RwImVertexIndex TempBufferRenderIndexList[TEMPBUFFERINDEXSIZE]; \ No newline at end of file diff --git a/src/miami/renderer/Renderer.cpp b/src/miami/renderer/Renderer.cpp new file mode 100644 index 00000000..96e3a329 --- /dev/null +++ b/src/miami/renderer/Renderer.cpp @@ -0,0 +1,1687 @@ +#define WITHD3D +#include "common.h" + +#include "main.h" +#include "Lights.h" +#include "ModelInfo.h" +#include "Treadable.h" +#include "Ped.h" +#include "Vehicle.h" +#include "Boat.h" +#include "Heli.h" +#include "Bike.h" +#include "Object.h" +#include "PathFind.h" +#include "Collision.h" +#include "VisibilityPlugins.h" +#include "Clock.h" +#include "World.h" +#include "Camera.h" +#include "ModelIndices.h" +#include "Streaming.h" +#include "Shadows.h" +#include "PointLights.h" +#include "Occlusion.h" +#include "Renderer.h" +#include "custompipes.h" +#include "Frontend.h" + +bool gbShowPedRoadGroups; +bool gbShowCarRoadGroups; +bool gbShowCollisionPolys; +bool gbShowCollisionPolysReflections; +bool gbShowCollisionPolysNoShadows; +bool gbShowCollisionLines; +bool gbBigWhiteDebugLightSwitchedOn; + +bool gbDontRenderBuildings; +bool gbDontRenderBigBuildings; +bool gbDontRenderPeds; +bool gbDontRenderObjects; +bool gbDontRenderVehicles; + +// unused +int16 TestCloseThings; +int16 TestBigThings; + +struct EntityInfo +{ + CEntity *ent; + float sort; +}; + +CLinkList gSortedVehiclesAndPeds; + +int32 CRenderer::ms_nNoOfVisibleEntities; +CEntity *CRenderer::ms_aVisibleEntityPtrs[NUMVISIBLEENTITIES]; +CEntity *CRenderer::ms_aInVisibleEntityPtrs[NUMINVISIBLEENTITIES]; +int32 CRenderer::ms_nNoOfInVisibleEntities; +#ifdef NEW_RENDERER +int32 CRenderer::ms_nNoOfVisibleVehicles; +CEntity *CRenderer::ms_aVisibleVehiclePtrs[NUMVISIBLEENTITIES]; +int32 CRenderer::ms_nNoOfVisibleBuildings; +CEntity *CRenderer::ms_aVisibleBuildingPtrs[NUMVISIBLEENTITIES]; +#endif + +CVector CRenderer::ms_vecCameraPosition; +CVehicle *CRenderer::m_pFirstPersonVehicle; +bool CRenderer::m_loadingPriority; +float CRenderer::ms_lodDistScale = 1.2f; + +// unused +BlockedRange CRenderer::aBlockedRanges[16]; +BlockedRange* CRenderer::pFullBlockedRanges; +BlockedRange* CRenderer::pEmptyBlockedRanges; + +void +CRenderer::Init(void) +{ + gSortedVehiclesAndPeds.Init(40); + SortBIGBuildings(); +} + +void +CRenderer::Shutdown(void) +{ + gSortedVehiclesAndPeds.Shutdown(); +} + +void +CRenderer::PreRender(void) +{ + int i; + CLink *node; + + for(i = 0; i < ms_nNoOfVisibleEntities; i++) + ms_aVisibleEntityPtrs[i]->PreRender(); + +#ifdef NEW_RENDERER + if(gbNewRenderer){ + for(i = 0; i < ms_nNoOfVisibleVehicles; i++) + ms_aVisibleVehiclePtrs[i]->PreRender(); + // How is this done with cWorldStream? + for(i = 0; i < ms_nNoOfVisibleBuildings; i++) + ms_aVisibleBuildingPtrs[i]->PreRender(); + for(node = CVisibilityPlugins::m_alphaBuildingList.head.next; + node != &CVisibilityPlugins::m_alphaBuildingList.tail; + node = node->next) + ((CEntity*)node->item.entity)->PreRender(); + } +#endif + + for (i = 0; i < ms_nNoOfInVisibleEntities; i++) { +#ifdef SQUEEZE_PERFORMANCE + if (ms_aInVisibleEntityPtrs[i]->IsVehicle() && ((CVehicle*)ms_aInVisibleEntityPtrs[i])->IsHeli()) +#endif + ms_aInVisibleEntityPtrs[i]->PreRender(); + } + + for(node = CVisibilityPlugins::m_alphaEntityList.head.next; + node != &CVisibilityPlugins::m_alphaEntityList.tail; + node = node->next) + ((CEntity*)node->item.entity)->PreRender(); + + CHeli::SpecialHeliPreRender(); + CShadows::RenderExtraPlayerShadows(); +} + +void +CRenderer::RenderOneRoad(CEntity *e) +{ +#ifndef FINAL + if(gbDontRenderBuildings) + return; +#endif +#ifndef MASTER + if(gbShowCollisionPolys || gbShowCollisionPolysReflections || gbShowCollisionPolysNoShadows) + CCollision::DrawColModel_Coloured(e->GetMatrix(), *CModelInfo::GetColModel(e->GetModelIndex()), e->GetModelIndex()); + else +#endif + { + PUSH_RENDERGROUP(CModelInfo::GetModelInfo(e->GetModelIndex())->GetModelName()); + + e->Render(); + + POP_RENDERGROUP(); + } +} + +void +CRenderer::RenderOneNonRoad(CEntity *e) +{ + CPed *ped; + CVehicle *veh; + int i; + bool resetLights; + +#ifndef MASTER + if(gbShowCollisionPolys || gbShowCollisionPolysReflections || gbShowCollisionPolysNoShadows){ + if(!e->IsVehicle()){ + CCollision::DrawColModel_Coloured(e->GetMatrix(), *CModelInfo::GetColModel(e->GetModelIndex()), e->GetModelIndex()); + return; + } + }else +#endif +#ifndef FINAL + if(e->IsBuilding()){ + if(e->bIsBIGBuilding){ + if(gbDontRenderBigBuildings) + return; + }else{ + if(gbDontRenderBuildings) + return; + } + }else +#endif + if(e->IsPed()){ +#ifndef FINAL + if(gbDontRenderPeds) + return; +#endif + ped = (CPed*)e; + if(ped->m_nPedState == PED_DRIVING) + return; + } +#ifndef FINAL + else if(e->IsObject() || e->IsDummy()){ + if(gbDontRenderObjects) + return; + }else if(e->IsVehicle()){ + // re3 addition + if(gbDontRenderVehicles) + return; + } +#endif + + PUSH_RENDERGROUP(CModelInfo::GetModelInfo(e->GetModelIndex())->GetModelName()); + + resetLights = e->SetupLighting(); + + if(e->IsVehicle()){ + // unfortunately can't use GetClump here + CVisibilityPlugins::SetupVehicleVariables((RpClump*)e->m_rwObject); + CVisibilityPlugins::InitAlphaAtomicList(); + } + + // Render Peds in vehicle before vehicle itself + if(e->IsVehicle()){ + veh = (CVehicle*)e; + if(veh->pDriver && veh->pDriver->m_nPedState == PED_DRIVING) + veh->pDriver->Render(); + for(i = 0; i < 8; i++) + if(veh->pPassengers[i] && veh->pPassengers[i]->m_nPedState == PED_DRIVING) + veh->pPassengers[i]->Render(); + SetCullMode(rwCULLMODECULLNONE); + } + e->Render(); + + if(e->IsVehicle()){ + e->bImBeingRendered = true; + CVisibilityPlugins::RenderAlphaAtomics(); + e->bImBeingRendered = false; + SetCullMode(rwCULLMODECULLBACK); + } + + e->RemoveLighting(resetLights); + + POP_RENDERGROUP(); +} + +void +CRenderer::RenderFirstPersonVehicle(void) +{ + if(m_pFirstPersonVehicle == nil) + return; + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RenderOneNonRoad(m_pFirstPersonVehicle); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); +} + +inline bool IsRoad(CEntity *e) { return e->IsBuilding() && ((CSimpleModelInfo*)CModelInfo::GetModelInfo(e->GetModelIndex()))->m_wetRoadReflection; } + +void +CRenderer::RenderRoads(void) +{ + int i; + CEntity *e; + + PUSH_RENDERGROUP("CRenderer::RenderRoads"); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + SetCullMode(rwCULLMODECULLBACK); + DeActivateDirectional(); + SetAmbientColours(); + + for(i = 0; i < ms_nNoOfVisibleEntities; i++){ + e = ms_aVisibleEntityPtrs[i]; + if(IsRoad(e)) + RenderOneRoad(e); + } + POP_RENDERGROUP(); +} + +inline bool PutIntoSortedVehicleList(CVehicle *veh) +{ + if(veh->IsBoat()){ + int mode = TheCamera.Cams[TheCamera.ActiveCam].Mode; + if(mode == CCam::MODE_WHEELCAM || + mode == CCam::MODE_1STPERSON && TheCamera.GetLookDirection() != LOOKING_FORWARD && TheCamera.GetLookDirection() != LOOKING_BEHIND || + CVisibilityPlugins::GetClumpAlpha(veh->GetClump()) != 255) + return false; + return true; + }else + return veh->bTouchingWater; +} + +void +CRenderer::RenderEverythingBarRoads(void) +{ + int i; + CEntity *e; + EntityInfo ei; + + PUSH_RENDERGROUP("CRenderer::RenderEverythingBarRoads"); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + SetCullMode(rwCULLMODECULLBACK); + gSortedVehiclesAndPeds.Clear(); + + for(i = 0; i < ms_nNoOfVisibleEntities; i++){ + e = ms_aVisibleEntityPtrs[i]; + + if(IsRoad(e)) + continue; + +#ifdef EXTENDED_PIPELINES + if(CustomPipes::bRenderingEnvMap && (e->IsPed() || e->IsVehicle())) + continue; +#endif + + if(e->IsVehicle() || + e->IsPed() && CVisibilityPlugins::GetClumpAlpha((RpClump*)e->m_rwObject) != 255){ + if(e->IsVehicle() && PutIntoSortedVehicleList((CVehicle*)e)){ + ei.ent = e; + ei.sort = (ms_vecCameraPosition - e->GetPosition()).MagnitudeSqr(); + gSortedVehiclesAndPeds.InsertSorted(ei); + }else{ + if(!CVisibilityPlugins::InsertEntityIntoSortedList(e, (ms_vecCameraPosition - e->GetPosition()).Magnitude())){ + printf("Ran out of space in alpha entity list"); + RenderOneNonRoad(e); + } + } + }else + RenderOneNonRoad(e); + } + POP_RENDERGROUP(); +} + +void +CRenderer::RenderBoats(void) +{ + CLink *node; + + PUSH_RENDERGROUP("CRenderer::RenderBoats"); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + SetCullMode(rwCULLMODECULLBACK); + +#ifdef NEW_RENDERER + int i; + CEntity *e; + EntityInfo ei; + if(gbNewRenderer){ + gSortedVehiclesAndPeds.Clear(); + // not the real thing + for(i = 0; i < ms_nNoOfVisibleVehicles; i++){ + e = ms_aVisibleVehiclePtrs[i]; + if(e->IsVehicle() && PutIntoSortedVehicleList((CVehicle*)e)){ + ei.ent = e; + ei.sort = (ms_vecCameraPosition - e->GetPosition()).MagnitudeSqr(); + gSortedVehiclesAndPeds.InsertSorted(ei); + } + } + } +#endif + + for(node = gSortedVehiclesAndPeds.tail.prev; + node != &gSortedVehiclesAndPeds.head; + node = node->prev){ + CVehicle *v = (CVehicle*)node->item.ent; + RenderOneNonRoad(v); + } + POP_RENDERGROUP(); +} + +#ifdef NEW_RENDERER +#ifndef LIBRW +#error "Need librw for EXTENDED_PIPELINES" +#endif +#include "WaterLevel.h" + +enum { + // blend passes + PASS_NOZ, // no z-write + PASS_ADD, // additive + PASS_BLEND // normal blend +}; + +static void +SetStencilState(int state) +{ + switch(state){ + // disable stencil + case 0: + rw::SetRenderState(rw::STENCILENABLE, FALSE); + break; + // test against stencil + case 1: + rw::SetRenderState(rw::STENCILENABLE, TRUE); + rw::SetRenderState(rw::STENCILFUNCTION, rw::STENCILNOTEQUAL); + rw::SetRenderState(rw::STENCILPASS, rw::STENCILKEEP); + rw::SetRenderState(rw::STENCILFAIL, rw::STENCILKEEP); + rw::SetRenderState(rw::STENCILZFAIL, rw::STENCILKEEP); + rw::SetRenderState(rw::STENCILFUNCTIONMASK, 0xFF); + rw::SetRenderState(rw::STENCILFUNCTIONREF, 0xFF); + break; + // write to stencil + case 2: + rw::SetRenderState(rw::STENCILENABLE, TRUE); + rw::SetRenderState(rw::STENCILFUNCTION, rw::STENCILALWAYS); + rw::SetRenderState(rw::STENCILPASS, rw::STENCILREPLACE); + rw::SetRenderState(rw::STENCILFUNCTIONREF, 0xFF); + break; + } +} + +void +CRenderer::RenderOneBuilding(CEntity *ent, float camdist) +{ + if(ent->m_rwObject == nil) + return; + + ent->bImBeingRendered = true; // TODO: this seems wrong, but do we even need it? + + assert(RwObjectGetType(ent->m_rwObject) == rpATOMIC); + RpAtomic *atomic = (RpAtomic*)ent->m_rwObject; + CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(ent->GetModelIndex()); + + int pass = PASS_BLEND; + if(mi->m_additive) // very questionable + pass = PASS_ADD; + if(mi->m_noZwrite) + pass = PASS_NOZ; + + if(ent->bDistanceFade){ + RpAtomic *lodatm; + float fadefactor; + uint32 alpha; + + lodatm = mi->GetAtomicFromDistance(camdist - FADE_DISTANCE); + fadefactor = (mi->GetLargestLodDistance() - (camdist - FADE_DISTANCE))/FADE_DISTANCE; + if(fadefactor > 1.0f) + fadefactor = 1.0f; + alpha = mi->m_alpha * fadefactor; + + if(alpha == 255) + WorldRender::AtomicFirstPass(atomic, pass); + else{ + // not quite sure what this is about, do we have to do that? + RpGeometry *geo = RpAtomicGetGeometry(lodatm); + if(geo != RpAtomicGetGeometry(atomic)) + RpAtomicSetGeometry(atomic, geo, rpATOMICSAMEBOUNDINGSPHERE); + WorldRender::AtomicFullyTransparent(atomic, pass, alpha); + } + }else + WorldRender::AtomicFirstPass(atomic, pass); + + ent->bImBeingRendered = false; // TODO: this seems wrong, but do we even need it? +} + +void +CRenderer::RenderWorld(int pass) +{ + int i; + CEntity *e; + CLink *node; + + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); + SetCullMode(rwCULLMODECULLBACK); + DeActivateDirectional(); + SetAmbientColours(); + + // Temporary...have to figure out sorting better + switch(pass){ + case 0: + // Roads + PUSH_RENDERGROUP("CRenderer::RenderWorld - Roads"); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + for(i = 0; i < ms_nNoOfVisibleBuildings; i++){ + e = ms_aVisibleBuildingPtrs[i]; + if(e->bIsBIGBuilding || IsRoad(e)) + RenderOneBuilding(e); + } + for(node = CVisibilityPlugins::m_alphaBuildingList.tail.prev; + node != &CVisibilityPlugins::m_alphaBuildingList.head; + node = node->prev){ + e = node->item.entity; + if(e->bIsBIGBuilding || IsRoad(e)) + RenderOneBuilding(e, node->item.sort); + } + POP_RENDERGROUP(); + break; + case 1: + // Opaque + PUSH_RENDERGROUP("CRenderer::RenderWorld - Opaque"); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + for(i = 0; i < ms_nNoOfVisibleBuildings; i++){ + e = ms_aVisibleBuildingPtrs[i]; + if(!(e->bIsBIGBuilding || IsRoad(e))) + RenderOneBuilding(e); + } + for(node = CVisibilityPlugins::m_alphaBuildingList.tail.prev; + node != &CVisibilityPlugins::m_alphaBuildingList.head; + node = node->prev){ + e = node->item.entity; + if(!(e->bIsBIGBuilding || IsRoad(e))) + RenderOneBuilding(e, node->item.sort); + } + // Now we have iterated through all visible buildings (unsorted and sorted) + // and the transparency list is done. + + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, FALSE); + WorldRender::RenderBlendPass(PASS_NOZ); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + POP_RENDERGROUP(); + break; + case 2: + // Transparent + PUSH_RENDERGROUP("CRenderer::RenderWorld - Transparent"); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + WorldRender::RenderBlendPass(PASS_ADD); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + WorldRender::RenderBlendPass(PASS_BLEND); + POP_RENDERGROUP(); + break; + } +} + +void +CRenderer::RenderPeds(void) +{ + int i; + CEntity *e; + + PUSH_RENDERGROUP("CRenderer::RenderPeds"); + for(i = 0; i < ms_nNoOfVisibleVehicles; i++){ + e = ms_aVisibleVehiclePtrs[i]; + if(e->IsPed()) + RenderOneNonRoad(e); + } + POP_RENDERGROUP(); +} + +void +CRenderer::RenderVehicles(void) +{ + int i; + CEntity *e; + EntityInfo ei; + CLink *node; + + PUSH_RENDERGROUP("CRenderer::RenderVehicles"); + // not the real thing + for(i = 0; i < ms_nNoOfVisibleVehicles; i++){ + e = ms_aVisibleVehiclePtrs[i]; + if(!e->IsVehicle()) + continue; + if(PutIntoSortedVehicleList((CVehicle*)e)) + continue; // boats handled elsewhere + ei.ent = e; + ei.sort = (ms_vecCameraPosition - e->GetPosition()).MagnitudeSqr(); + gSortedVehiclesAndPeds.InsertSorted(ei); + } + + for(node = gSortedVehiclesAndPeds.tail.prev; + node != &gSortedVehiclesAndPeds.head; + node = node->prev) + RenderOneNonRoad(node->item.ent); + POP_RENDERGROUP(); +} + +void +CRenderer::RenderTransparentWater(void) +{ + int i; + CEntity *e; + + PUSH_RENDERGROUP("CRenderer::RenderTransparentWater"); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDZERO); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + SetStencilState(2); + + for(i = 0; i < ms_nNoOfVisibleVehicles; i++){ + e = ms_aVisibleVehiclePtrs[i]; + if(e->IsVehicle() && ((CVehicle*)e)->IsBoat()) + ((CBoat*)e)->RenderWaterOutPolys(); + } + + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + SetStencilState(1); + + CWaterLevel::RenderTransparentWater(); + + SetStencilState(0); + POP_RENDERGROUP(); +} + +void +CRenderer::ClearForFrame(void) +{ + ms_nNoOfVisibleEntities = 0; + ms_nNoOfVisibleVehicles = 0; + ms_nNoOfVisibleBuildings = 0; + ms_nNoOfInVisibleEntities = 0; + gSortedVehiclesAndPeds.Clear(); + + WorldRender::numBlendInsts[PASS_NOZ] = 0; + WorldRender::numBlendInsts[PASS_ADD] = 0; + WorldRender::numBlendInsts[PASS_BLEND] = 0; +} +#endif + +void +CRenderer::RenderFadingInEntities(void) +{ + PUSH_RENDERGROUP("CRenderer::RenderFadingInEntities"); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + SetCullMode(rwCULLMODECULLBACK); + DeActivateDirectional(); + SetAmbientColours(); + CVisibilityPlugins::RenderFadingEntities(); + POP_RENDERGROUP(); +} + +void +CRenderer::RenderFadingInUnderwaterEntities(void) +{ + PUSH_RENDERGROUP("CRenderer::RenderFadingInUnderwaterEntities"); + DeActivateDirectional(); + SetAmbientColours(); + CVisibilityPlugins::RenderFadingUnderwaterEntities(); + POP_RENDERGROUP(); +} + +void +CRenderer::RenderCollisionLines(void) +{ + int i; + + // game doesn't draw fading in entities + // this should probably be fixed + for(i = 0; i < ms_nNoOfVisibleEntities; i++){ + CEntity *e = ms_aVisibleEntityPtrs[i]; + if(Abs(e->GetPosition().x - ms_vecCameraPosition.x) < 100.0f && + Abs(e->GetPosition().y - ms_vecCameraPosition.y) < 100.0f) + CCollision::DrawColModel(e->GetMatrix(), *e->GetColModel()); + } +} + +enum Visbility +{ + VIS_INVISIBLE, + VIS_VISIBLE, + VIS_OFFSCREEN, + VIS_STREAMME +}; + +// Time Objects can be time culled if +// other == -1 || CModelInfo::GetModelInfo(other)->GetRwObject() +// i.e. we have to draw even at the wrong time if +// other != -1 && CModelInfo::GetModelInfo(other)->GetRwObject() == nil + +#define OTHERUNAVAILABLE (other != -1 && CModelInfo::GetModelInfo(other)->GetRwObject() == nil) +#define CANTIMECULL (!OTHERUNAVAILABLE) + +int32 +CRenderer::SetupEntityVisibility(CEntity *ent) +{ + CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(ent->m_modelIndex); + CTimeModelInfo *ti; + int32 other; + float dist; + + bool request = true; + if(mi->GetModelType() == MITYPE_TIME){ + ti = (CTimeModelInfo*)mi; + other = ti->GetOtherTimeModel(); + if(CClock::GetIsTimeInRange(ti->GetTimeOn(), ti->GetTimeOff())){ + // don't fade in, or between time objects + if(CANTIMECULL) + ti->m_alpha = 255; + }else{ + // Hide if possible + if(CANTIMECULL){ + ent->DeleteRwObject(); + return VIS_INVISIBLE; + } + // can't cull, so we'll try to draw this one, but don't request + // it since what we really want is the other one. + request = false; + } + }else{ + if(mi->GetModelType() != MITYPE_SIMPLE && mi->GetModelType() != MITYPE_WEAPON){ + if(FindPlayerVehicle() == ent && + TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON && + !(FindPlayerVehicle()->IsBike() && ((CBike*)FindPlayerVehicle())->bWheelieCam)){ + // Player's vehicle in first person mode + CVehicle *veh = (CVehicle*)ent; + int model = veh->GetModelIndex(); + int direction = TheCamera.Cams[TheCamera.ActiveCam].DirectionWasLooking; + if(direction == LOOKING_FORWARD || + ent->GetModelIndex() == MI_RHINO || + ent->GetModelIndex() == MI_COACH || + TheCamera.m_bInATunnelAndABigVehicle || + direction == LOOKING_BEHIND && veh->pHandling->Flags & HANDLING_UNKNOWN){ + ent->bNoBrightHeadLights = true; + return VIS_OFFSCREEN; + } + + if(direction != LOOKING_BEHIND || + !veh->IsBoat() || model == MI_REEFER || model == MI_TROPIC || model == MI_PREDATOR || model == MI_SKIMMER){ + m_pFirstPersonVehicle = (CVehicle*)ent; + ent->bNoBrightHeadLights = false; + return VIS_OFFSCREEN; + } + } + + // All sorts of Clumps + if(ent->m_rwObject == nil || !ent->bIsVisible) + return VIS_INVISIBLE; + if(!ent->GetIsOnScreen() || ent->IsEntityOccluded()) + return VIS_OFFSCREEN; + if(ent->bDrawLast){ + dist = (ent->GetPosition() - ms_vecCameraPosition).Magnitude(); + CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); + ent->bDistanceFade = false; + return VIS_INVISIBLE; + } + return VIS_VISIBLE; + } + if(ent->bDontStream){ + if(ent->m_rwObject == nil || !ent->bIsVisible) + return VIS_INVISIBLE; + if(!ent->GetIsOnScreen() || ent->IsEntityOccluded()) + return VIS_OFFSCREEN; + if(ent->bDrawLast){ + dist = (ent->GetPosition() - ms_vecCameraPosition).Magnitude(); + CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); + ent->bDistanceFade = false; + return VIS_INVISIBLE; + } + return VIS_VISIBLE; + } + } + + // Simple ModelInfo + + if(!IsAreaVisible(ent->m_area)) + return VIS_INVISIBLE; + + dist = (ent->GetPosition() - ms_vecCameraPosition).Magnitude(); + +#ifndef FIX_BUGS + // Whatever this is supposed to do, it breaks fading for objects + // whose draw dist is > LOD_DISTANCE-FADE_DISTANCE, i.e. 280 + // because decreasing dist here makes the object visible above LOD_DISTANCE + // before fading normally once below LOD_DISTANCE. + // aha! this must be a workaround for the fact that we're not taking + // the LOD multiplier into account here anywhere + if(LOD_DISTANCE < dist && dist < mi->GetLargestLodDistance() + FADE_DISTANCE) + dist += mi->GetLargestLodDistance() - LOD_DISTANCE; +#endif + + if(ent->IsObject() && ent->bRenderDamaged) + mi->m_isDamaged = true; + + RpAtomic *a = mi->GetAtomicFromDistance(dist); + if(a){ + mi->m_isDamaged = false; + if(ent->m_rwObject == nil) + ent->CreateRwObject(); + assert(ent->m_rwObject); + RpAtomic *rwobj = (RpAtomic*)ent->m_rwObject; + // Make sure our atomic uses the right geometry and not + // that of an atomic for another draw distance. + if(RpAtomicGetGeometry(a) != RpAtomicGetGeometry(rwobj)) + RpAtomicSetGeometry(rwobj, RpAtomicGetGeometry(a), rpATOMICSAMEBOUNDINGSPHERE); // originally 5 (mistake?) + mi->IncreaseAlpha(); + if(ent->m_rwObject == nil || !ent->bIsVisible) + return VIS_INVISIBLE; + + if(!ent->GetIsOnScreen() || ent->IsEntityOccluded()){ + mi->m_alpha = 255; + return VIS_OFFSCREEN; + } + + if(mi->m_alpha != 255){ + CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); + ent->bDistanceFade = true; + return VIS_INVISIBLE; + } + + if(mi->m_drawLast || ent->bDrawLast){ + if(CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist)){ + ent->bDistanceFade = false; + return VIS_INVISIBLE; + } + } + return VIS_VISIBLE; + } + + // Object is not loaded, figure out what to do + + if(mi->m_noFade){ + mi->m_isDamaged = false; + // request model + if(dist - STREAM_DISTANCE < mi->GetLargestLodDistance() && request) + return VIS_STREAMME; + return VIS_INVISIBLE; + } + + // We might be fading + + a = mi->GetAtomicFromDistance(dist - FADE_DISTANCE); + mi->m_isDamaged = false; + if(a == nil){ + // request model + if(dist - FADE_DISTANCE - STREAM_DISTANCE < mi->GetLargestLodDistance() && request) + return VIS_STREAMME; + return VIS_INVISIBLE; + } + + if(ent->m_rwObject == nil) + ent->CreateRwObject(); + assert(ent->m_rwObject); + RpAtomic *rwobj = (RpAtomic*)ent->m_rwObject; + if(RpAtomicGetGeometry(a) != RpAtomicGetGeometry(rwobj)) + RpAtomicSetGeometry(rwobj, RpAtomicGetGeometry(a), rpATOMICSAMEBOUNDINGSPHERE); // originally 5 (mistake?) + mi->IncreaseAlpha(); + if(ent->m_rwObject == nil || !ent->bIsVisible) + return VIS_INVISIBLE; + + if(!ent->GetIsOnScreen() || ent->IsEntityOccluded()){ + mi->m_alpha = 255; + return VIS_OFFSCREEN; + }else{ + CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); + ent->bDistanceFade = true; + return VIS_OFFSCREEN; // Why this? + } +} + +int32 +CRenderer::SetupBigBuildingVisibility(CEntity *ent) +{ + CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(ent->m_modelIndex); + CTimeModelInfo *ti; + int32 other; + + if(!IsAreaVisible(ent->m_area)) + return VIS_INVISIBLE; + + bool request = true; + if(mi->GetModelType() == MITYPE_TIME){ + ti = (CTimeModelInfo*)mi; + other = ti->GetOtherTimeModel(); + if(CClock::GetIsTimeInRange(ti->GetTimeOn(), ti->GetTimeOff())){ + // don't fade in, or between time objects + if(CANTIMECULL) + ti->m_alpha = 255; + }else{ + // Hide if possible + if(CANTIMECULL){ + ent->DeleteRwObject(); + return VIS_INVISIBLE; + } + // can't cull, so we'll try to draw this one, but don't request + // it since what we really want is the other one. + request = false; + } + }else if(mi->GetModelType() == MITYPE_VEHICLE) + return ent->IsVisible() ? VIS_VISIBLE : VIS_INVISIBLE; + + float dist = (ms_vecCameraPosition-ent->GetPosition()).Magnitude(); + CSimpleModelInfo *nonLOD = mi->GetRelatedModel(); + + // Find out whether to draw below near distance. + // This is only the case if there is a non-LOD which is either not + // loaded or not completely faded in yet. + if(dist < mi->GetNearDistance() && dist < LOD_DISTANCE){ + // No non-LOD or non-LOD is completely visible. + if(nonLOD == nil || + nonLOD->GetRwObject() && nonLOD->m_alpha == 255) + return VIS_INVISIBLE; + + // But if it is a time object, we'd rather draw the wrong + // non-LOD than the right LOD. + if(nonLOD->GetModelType() == MITYPE_TIME){ + ti = (CTimeModelInfo*)nonLOD; + other = ti->GetOtherTimeModel(); + if(other != -1 && CModelInfo::GetModelInfo(other)->GetRwObject()) + return VIS_INVISIBLE; + } + } + + RpAtomic *a = mi->GetFirstAtomicFromDistance(dist); + if(a){ + if(ent->m_rwObject == nil) + ent->CreateRwObject(); + assert(ent->m_rwObject); + RpAtomic *rwobj = (RpAtomic*)ent->m_rwObject; + + // Make sure our atomic uses the right geometry and not + // that of an atomic for another draw distance. + if(RpAtomicGetGeometry(a) != RpAtomicGetGeometry(rwobj)) + RpAtomicSetGeometry(rwobj, RpAtomicGetGeometry(a), rpATOMICSAMEBOUNDINGSPHERE); // originally 5 (mistake?) + mi->IncreaseAlpha(); + if(!ent->IsVisible() || !ent->GetIsOnScreenComplex() || ent->IsEntityOccluded()){ + mi->m_alpha = 255; + return VIS_INVISIBLE; + } + + if(mi->m_alpha != 255){ + CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); + ent->bDistanceFade = true; + return VIS_INVISIBLE; + } + + if(mi->m_drawLast){ + CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); + ent->bDistanceFade = false; + return VIS_INVISIBLE; + } + return VIS_VISIBLE; + } + + if(mi->m_noFade){ + ent->DeleteRwObject(); + return VIS_INVISIBLE; + } + + + // get faded atomic + a = mi->GetFirstAtomicFromDistance(dist - FADE_DISTANCE); + if(a == nil){ + if(ent->bStreamBIGBuilding && dist-STREAM_DISTANCE < mi->GetLodDistance(0) && request){ + return ent->GetIsOnScreen() ? VIS_STREAMME : VIS_INVISIBLE; + }else{ + ent->DeleteRwObject(); + return VIS_INVISIBLE; + } + } + + // Fade... + if(ent->m_rwObject == nil) + ent->CreateRwObject(); + assert(ent->m_rwObject); + RpAtomic *rwobj = (RpAtomic*)ent->m_rwObject; + if(RpAtomicGetGeometry(a) != RpAtomicGetGeometry(rwobj)) + RpAtomicSetGeometry(rwobj, RpAtomicGetGeometry(a), rpATOMICSAMEBOUNDINGSPHERE); // originally 5 (mistake?) + mi->IncreaseAlpha(); + if(!ent->IsVisible() || !ent->GetIsOnScreenComplex() || ent->IsEntityOccluded()){ + mi->m_alpha = 255; + return VIS_INVISIBLE; + } + CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); + ent->bDistanceFade = true; + return VIS_INVISIBLE; +} + +void +CRenderer::ConstructRenderList(void) +{ + COcclusion::ProcessBeforeRendering(); +#ifdef NEW_RENDERER + if(!gbNewRenderer) +#endif +{ + ms_nNoOfVisibleEntities = 0; + ms_nNoOfInVisibleEntities = 0; +} + ms_vecCameraPosition = TheCamera.GetPosition(); + + // unused + pFullBlockedRanges = nil; + pEmptyBlockedRanges = aBlockedRanges; + for(int i = 0; i < 16; i++){ + aBlockedRanges[i].prev = &aBlockedRanges[i-1]; + aBlockedRanges[i].next = &aBlockedRanges[i+1]; + } + aBlockedRanges[0].prev = nil; + aBlockedRanges[15].next = nil; + + // unused + TestCloseThings = 0; + TestBigThings = 0; + + ScanWorld(); +} + +void +LimitFrustumVector(CVector &vec1, const CVector &vec2, float l) +{ + float f; + f = (l - vec2.z) / (vec1.z - vec2.z); + vec1.x = f*(vec1.x - vec2.x) + vec2.x; + vec1.y = f*(vec1.y - vec2.y) + vec2.y; + vec1.z = f*(vec1.z - vec2.z) + vec2.z; +} + +enum Corners +{ + CORNER_CAM = 0, + CORNER_FAR_TOPLEFT, + CORNER_FAR_TOPRIGHT, + CORNER_FAR_BOTRIGHT, + CORNER_FAR_BOTLEFT, + CORNER_LOD_LEFT, + CORNER_LOD_RIGHT, + CORNER_PRIO_LEFT, + CORNER_PRIO_RIGHT, +}; + +void +CRenderer::ScanWorld(void) +{ + float f = RwCameraGetFarClipPlane(TheCamera.m_pRwCamera); + RwV2d vw = *RwCameraGetViewWindow(TheCamera.m_pRwCamera); + CVector vectors[9]; + RwMatrix *cammatrix; + RwV2d poly[3]; + + memset(vectors, 0, sizeof(vectors)); + vectors[CORNER_FAR_TOPLEFT].x = -vw.x * f; + vectors[CORNER_FAR_TOPLEFT].y = vw.y * f; + vectors[CORNER_FAR_TOPLEFT].z = f; + vectors[CORNER_FAR_TOPRIGHT].x = vw.x * f; + vectors[CORNER_FAR_TOPRIGHT].y = vw.y * f; + vectors[CORNER_FAR_TOPRIGHT].z = f; + vectors[CORNER_FAR_BOTRIGHT].x = vw.x * f; + vectors[CORNER_FAR_BOTRIGHT].y = -vw.y * f; + vectors[CORNER_FAR_BOTRIGHT].z = f; + vectors[CORNER_FAR_BOTLEFT].x = -vw.x * f; + vectors[CORNER_FAR_BOTLEFT].y = -vw.y * f; + vectors[CORNER_FAR_BOTLEFT].z = f; + + cammatrix = RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera)); + + m_pFirstPersonVehicle = nil; + CVisibilityPlugins::InitAlphaEntityList(); + CWorld::AdvanceCurrentScanCode(); + + // unused + static CVector prevPos; + static CVector prevFwd; + static bool smallMovement; + smallMovement = (TheCamera.GetPosition() - prevPos).MagnitudeSqr() < SQR(4.0f) && + DotProduct(TheCamera.GetForward(), prevFwd) > 0.98f; + prevPos = TheCamera.GetPosition(); + prevFwd = TheCamera.GetForward(); + + if(cammatrix->at.z > 0.0f){ + // looking up, bottom corners are further away + vectors[CORNER_LOD_LEFT] = vectors[CORNER_FAR_BOTLEFT] * LOD_DISTANCE/f; + vectors[CORNER_LOD_RIGHT] = vectors[CORNER_FAR_BOTRIGHT] * LOD_DISTANCE/f; + }else{ + // looking down, top corners are further away + vectors[CORNER_LOD_LEFT] = vectors[CORNER_FAR_TOPLEFT] * LOD_DISTANCE/f; + vectors[CORNER_LOD_RIGHT] = vectors[CORNER_FAR_TOPRIGHT] * LOD_DISTANCE/f; + } + vectors[CORNER_PRIO_LEFT].x = vectors[CORNER_LOD_LEFT].x * 0.2f; + vectors[CORNER_PRIO_LEFT].y = vectors[CORNER_LOD_LEFT].y * 0.2f; + vectors[CORNER_PRIO_LEFT].z = vectors[CORNER_LOD_LEFT].z; + vectors[CORNER_PRIO_RIGHT].x = vectors[CORNER_LOD_RIGHT].x * 0.2f; + vectors[CORNER_PRIO_RIGHT].y = vectors[CORNER_LOD_RIGHT].y * 0.2f; + vectors[CORNER_PRIO_RIGHT].z = vectors[CORNER_LOD_RIGHT].z; + RwV3dTransformPoints(vectors, vectors, 9, cammatrix); + + m_loadingPriority = false; + if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || +#ifdef FIX_BUGS + TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_GTACLASSIC || +#endif + TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED){ + CRect rect; + int x1, x2, y1, y2; + LimitFrustumVector(vectors[CORNER_FAR_TOPLEFT], vectors[CORNER_CAM], -100.0f); + rect.ContainPoint(vectors[CORNER_FAR_TOPLEFT]); + LimitFrustumVector(vectors[CORNER_FAR_TOPRIGHT], vectors[CORNER_CAM], -100.0f); + rect.ContainPoint(vectors[CORNER_FAR_TOPRIGHT]); + LimitFrustumVector(vectors[CORNER_FAR_BOTRIGHT], vectors[CORNER_CAM], -100.0f); + rect.ContainPoint(vectors[CORNER_FAR_BOTRIGHT]); + LimitFrustumVector(vectors[CORNER_FAR_BOTLEFT], vectors[CORNER_CAM], -100.0f); + rect.ContainPoint(vectors[CORNER_FAR_BOTLEFT]); + x1 = CWorld::GetSectorIndexX(rect.left); + if(x1 < 0) x1 = 0; + x2 = CWorld::GetSectorIndexX(rect.right); + if(x2 >= NUMSECTORS_X-1) x2 = NUMSECTORS_X-1; + y1 = CWorld::GetSectorIndexY(rect.top); + if(y1 < 0) y1 = 0; + y2 = CWorld::GetSectorIndexY(rect.bottom); + if(y2 >= NUMSECTORS_Y-1) y2 = NUMSECTORS_Y-1; + for(; x1 <= x2; x1++) + for(int y = y1; y <= y2; y++) + ScanSectorList(CWorld::GetSector(x1, y)->m_lists); + }else{ +#ifdef GTA_TRAIN + CVehicle *train = FindPlayerTrain(); + if(train && train->GetPosition().z < 0.0f){ + poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x); + poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y); + poly[1].x = CWorld::GetSectorX(vectors[CORNER_LOD_LEFT].x); + poly[1].y = CWorld::GetSectorY(vectors[CORNER_LOD_LEFT].y); + poly[2].x = CWorld::GetSectorX(vectors[CORNER_LOD_RIGHT].x); + poly[2].y = CWorld::GetSectorY(vectors[CORNER_LOD_RIGHT].y); + ScanSectorPoly(poly, 3, ScanSectorList_Subway); + }else +#endif + { + if(f > LOD_DISTANCE){ + // priority + poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x); + poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y); + poly[1].x = CWorld::GetSectorX(vectors[CORNER_PRIO_LEFT].x); + poly[1].y = CWorld::GetSectorY(vectors[CORNER_PRIO_LEFT].y); + poly[2].x = CWorld::GetSectorX(vectors[CORNER_PRIO_RIGHT].x); + poly[2].y = CWorld::GetSectorY(vectors[CORNER_PRIO_RIGHT].y); + ScanSectorPoly(poly, 3, ScanSectorList_Priority); + + // below LOD + poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x); + poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y); + poly[1].x = CWorld::GetSectorX(vectors[CORNER_LOD_LEFT].x); + poly[1].y = CWorld::GetSectorY(vectors[CORNER_LOD_LEFT].y); + poly[2].x = CWorld::GetSectorX(vectors[CORNER_LOD_RIGHT].x); + poly[2].y = CWorld::GetSectorY(vectors[CORNER_LOD_RIGHT].y); + ScanSectorPoly(poly, 3, ScanSectorList); + }else{ + poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x); + poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y); + poly[1].x = CWorld::GetSectorX(vectors[CORNER_FAR_TOPLEFT].x); + poly[1].y = CWorld::GetSectorY(vectors[CORNER_FAR_TOPLEFT].y); + poly[2].x = CWorld::GetSectorX(vectors[CORNER_FAR_TOPRIGHT].x); + poly[2].y = CWorld::GetSectorY(vectors[CORNER_FAR_TOPRIGHT].y); + ScanSectorPoly(poly, 3, ScanSectorList); + } + +#ifdef NO_ISLAND_LOADING + if (FrontEndMenuManager.m_PrefsIslandLoading == CMenuManager::ISLAND_LOADING_HIGH) { + ScanBigBuildingList(CWorld::GetBigBuildingList(LEVEL_BEACH)); + ScanBigBuildingList(CWorld::GetBigBuildingList(LEVEL_MAINLAND)); + } else +#endif + { +#ifdef FIX_BUGS + if(CCollision::ms_collisionInMemory != LEVEL_GENERIC) +#endif + ScanBigBuildingList(CWorld::GetBigBuildingList(CGame::currLevel)); + } + ScanBigBuildingList(CWorld::GetBigBuildingList(LEVEL_GENERIC)); + } + } +} + +void +CRenderer::RequestObjectsInFrustum(void) +{ + float f = RwCameraGetFarClipPlane(TheCamera.m_pRwCamera); + RwV2d vw = *RwCameraGetViewWindow(TheCamera.m_pRwCamera); + CVector vectors[9]; + RwMatrix *cammatrix; + RwV2d poly[3]; + + memset(vectors, 0, sizeof(vectors)); + vectors[CORNER_FAR_TOPLEFT].x = -vw.x * f; + vectors[CORNER_FAR_TOPLEFT].y = vw.y * f; + vectors[CORNER_FAR_TOPLEFT].z = f; + vectors[CORNER_FAR_TOPRIGHT].x = vw.x * f; + vectors[CORNER_FAR_TOPRIGHT].y = vw.y * f; + vectors[CORNER_FAR_TOPRIGHT].z = f; + vectors[CORNER_FAR_BOTRIGHT].x = vw.x * f; + vectors[CORNER_FAR_BOTRIGHT].y = -vw.y * f; + vectors[CORNER_FAR_BOTRIGHT].z = f; + vectors[CORNER_FAR_BOTLEFT].x = -vw.x * f; + vectors[CORNER_FAR_BOTLEFT].y = -vw.y * f; + vectors[CORNER_FAR_BOTLEFT].z = f; + + cammatrix = RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera)); + + CWorld::AdvanceCurrentScanCode(); + ms_vecCameraPosition = TheCamera.GetPosition(); + + if(cammatrix->at.z > 0.0f){ + // looking up, bottom corners are further away + vectors[CORNER_LOD_LEFT] = vectors[CORNER_FAR_BOTLEFT] * LOD_DISTANCE/f; + vectors[CORNER_LOD_RIGHT] = vectors[CORNER_FAR_BOTRIGHT] * LOD_DISTANCE/f; + }else{ + // looking down, top corners are further away + vectors[CORNER_LOD_LEFT] = vectors[CORNER_FAR_TOPLEFT] * LOD_DISTANCE/f; + vectors[CORNER_LOD_RIGHT] = vectors[CORNER_FAR_TOPRIGHT] * LOD_DISTANCE/f; + } + vectors[CORNER_PRIO_LEFT].x = vectors[CORNER_LOD_LEFT].x * 0.2f; + vectors[CORNER_PRIO_LEFT].y = vectors[CORNER_LOD_LEFT].y * 0.2f; + vectors[CORNER_PRIO_LEFT].z = vectors[CORNER_LOD_LEFT].z; + vectors[CORNER_PRIO_RIGHT].x = vectors[CORNER_LOD_RIGHT].x * 0.2f; + vectors[CORNER_PRIO_RIGHT].y = vectors[CORNER_LOD_RIGHT].y * 0.2f; + vectors[CORNER_PRIO_RIGHT].z = vectors[CORNER_LOD_RIGHT].z; + RwV3dTransformPoints(vectors, vectors, 9, cammatrix); + + if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || +#ifdef FIX_BUGS + TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_GTACLASSIC || +#endif + TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED){ + CRect rect; + int x1, x2, y1, y2; + LimitFrustumVector(vectors[CORNER_FAR_TOPLEFT], vectors[CORNER_CAM], -100.0f); + rect.ContainPoint(vectors[CORNER_FAR_TOPLEFT]); + LimitFrustumVector(vectors[CORNER_FAR_TOPRIGHT], vectors[CORNER_CAM], -100.0f); + rect.ContainPoint(vectors[CORNER_FAR_TOPRIGHT]); + LimitFrustumVector(vectors[CORNER_FAR_BOTRIGHT], vectors[CORNER_CAM], -100.0f); + rect.ContainPoint(vectors[CORNER_FAR_BOTRIGHT]); + LimitFrustumVector(vectors[CORNER_FAR_BOTLEFT], vectors[CORNER_CAM], -100.0f); + rect.ContainPoint(vectors[CORNER_FAR_BOTLEFT]); + x1 = CWorld::GetSectorIndexX(rect.left); + if(x1 < 0) x1 = 0; + x2 = CWorld::GetSectorIndexX(rect.right); + if(x2 >= NUMSECTORS_X-1) x2 = NUMSECTORS_X-1; + y1 = CWorld::GetSectorIndexY(rect.top); + if(y1 < 0) y1 = 0; + y2 = CWorld::GetSectorIndexY(rect.bottom); + if(y2 >= NUMSECTORS_Y-1) y2 = NUMSECTORS_Y-1; + for(; x1 <= x2; x1++) + for(int y = y1; y <= y2; y++) + ScanSectorList_RequestModels(CWorld::GetSector(x1, y)->m_lists); + }else{ + poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x); + poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y); + poly[1].x = CWorld::GetSectorX(vectors[CORNER_LOD_LEFT].x); + poly[1].y = CWorld::GetSectorY(vectors[CORNER_LOD_LEFT].y); + poly[2].x = CWorld::GetSectorX(vectors[CORNER_LOD_RIGHT].x); + poly[2].y = CWorld::GetSectorY(vectors[CORNER_LOD_RIGHT].y); + ScanSectorPoly(poly, 3, ScanSectorList_RequestModels); + } +} + +bool +CEntity::SetupLighting(void) +{ + return false; +} + +void +CEntity::RemoveLighting(bool) +{ +} + +bool +CPed::SetupLighting(void) +{ + ActivateDirectional(); + SetAmbientColoursForPedsCarsAndObjects(); + +#ifndef MASTER + // Originally this was being called through iteration of Sectors, but putting it here is better. + if (GetDebugDisplay() != 0 && !IsPlayer()) + DebugRenderOnePedText(); +#endif + + if (bRenderScorched) { + WorldReplaceNormalLightsWithScorched(Scene.world, 0.1f); + } else { + // Note that this lightMult is only affected by LIGHT_DARKEN. If there's no LIGHT_DARKEN, it will be 1.0. + float lightMult = CPointLights::GenerateLightsAffectingObject(&GetPosition()); + if (lightMult != 1.0f) { + SetAmbientAndDirectionalColours(lightMult); + return true; + } + } + return false; +} + +void +CPed::RemoveLighting(bool reset) +{ + if (!bRenderScorched) { + CRenderer::RemoveVehiclePedLights(this, reset); + if (reset) + ReSetAmbientAndDirectionalColours(); + } + SetAmbientColours(); + DeActivateDirectional(); +} + +float +CalcNewDelta(RwV2d *a, RwV2d *b) +{ + return (b->x - a->x) / (b->y - a->y); +} + +#ifdef FIX_BUGS +#define TOINT(x) ((int)Floor(x)) +#else +#define TOINT(x) ((int)(x)) +#endif + +void +CRenderer::ScanSectorPoly(RwV2d *poly, int32 numVertices, void (*scanfunc)(CPtrList *)) +{ + float miny, maxy; + int y, yend; + int x, xstart, xend; + int i; + int a1, a2, b1, b2; + float deltaA, deltaB; + float xA, xB; + + miny = poly[0].y; + maxy = poly[0].y; + a2 = 0; + xstart = 9999; + xend = -9999; + + for(i = 1; i < numVertices; i++){ + if(poly[i].y > maxy) + maxy = poly[i].y; + if(poly[i].y < miny){ + miny = poly[i].y; + a2 = i; + } + } + y = TOINT(miny); + yend = TOINT(maxy); + + // Go left in poly to find first edge b + b2 = a2; + for(i = 0; i < numVertices; i++){ + b1 = b2--; + if(b2 < 0) b2 = numVertices-1; + if(poly[b1].x < xstart) + xstart = TOINT(poly[b1].x); + if(TOINT(poly[b1].y) != TOINT(poly[b2].y)) + break; + } + // Go right to find first edge a + for(i = 0; i < numVertices; i++){ + a1 = a2++; + if(a2 == numVertices) a2 = 0; + if(poly[a1].x > xend) + xend = TOINT(poly[a1].x); + if(TOINT(poly[a1].y) != TOINT(poly[a2].y)) + break; + } + + // prestep x1 and x2 to next integer y + deltaA = CalcNewDelta(&poly[a1], &poly[a2]); + xA = deltaA * (Ceil(poly[a1].y) - poly[a1].y) + poly[a1].x; + deltaB = CalcNewDelta(&poly[b1], &poly[b2]); + xB = deltaB * (Ceil(poly[b1].y) - poly[b1].y) + poly[b1].x; + + if(y != yend){ + if(deltaB < 0.0f && TOINT(xB) < xstart) + xstart = TOINT(xB); + if(deltaA >= 0.0f && TOINT(xA) > xend) + xend = TOINT(xA); + } + + while(y <= yend && y < NUMSECTORS_Y){ + // scan one x-line + if(y >= 0 && xstart < NUMSECTORS_X) + for(x = xstart; x <= xend && x != NUMSECTORS_X; x++) + if(x >= 0) + scanfunc(CWorld::GetSector(x, y)->m_lists); + + // advance one scan line + y++; + xA += deltaA; + xB += deltaB; + + // update left side + if(y == TOINT(poly[b2].y)){ + // reached end of edge + if(y == yend){ + if(deltaB < 0.0f){ + do{ + xstart = TOINT(poly[b2--].x); + if(b2 < 0) b2 = numVertices-1; + }while(xstart > TOINT(poly[b2].x)); + }else + xstart = TOINT(xB - deltaB); + }else{ + // switch edges + if(deltaB < 0.0f) + xstart = TOINT(poly[b2].x); + else + xstart = TOINT(xB - deltaB); + do{ + b1 = b2--; + if(b2 < 0) b2 = numVertices-1; + if(TOINT(poly[b1].x) < xstart) + xstart = TOINT(poly[b1].x); + }while(y == TOINT(poly[b2].y)); + deltaB = CalcNewDelta(&poly[b1], &poly[b2]); + xB = deltaB * (Ceil(poly[b1].y) - poly[b1].y) + poly[b1].x; + if(deltaB < 0.0f && TOINT(xB) < xstart) + xstart = TOINT(xB); + } + }else{ + if(deltaB < 0.0f) + xstart = TOINT(xB); + else + xstart = TOINT(xB - deltaB); + } + + // update right side + if(y == TOINT(poly[a2].y)){ + // reached end of edge + if(y == yend){ + if(deltaA < 0.0f) + xend = TOINT(xA - deltaA); + else{ + do{ + xend = TOINT(poly[a2++].x); + if(a2 == numVertices) a2 = 0; + }while(xend < TOINT(poly[a2].x)); + } + }else{ + // switch edges + if(deltaA < 0.0f) + xend = TOINT(xA - deltaA); + else + xend = TOINT(poly[a2].x); + do{ + a1 = a2++; + if(a2 == numVertices) a2 = 0; + if(TOINT(poly[a1].x) > xend) + xend = TOINT(poly[a1].x); + }while(y == TOINT(poly[a2].y)); + deltaA = CalcNewDelta(&poly[a1], &poly[a2]); + xA = deltaA * (Ceil(poly[a1].y) - poly[a1].y) + poly[a1].x; + if(deltaA >= 0.0f && TOINT(xA) > xend) + xend = TOINT(xA); + } + }else{ + if(deltaA < 0.0f) + xend = TOINT(xA - deltaA); + else + xend = TOINT(xA); + } + } +} + +void +CRenderer::InsertEntityIntoList(CEntity *ent) +{ +#ifdef FIX_BUGS + if (!ent->m_rwObject) return; +#endif + +#ifdef NEW_RENDERER + // TODO: there are more flags being checked here + if(gbNewRenderer && (ent->IsVehicle() || ent->IsPed())) + ms_aVisibleVehiclePtrs[ms_nNoOfVisibleVehicles++] = ent; + else if(gbNewRenderer && ent->IsBuilding()) + ms_aVisibleBuildingPtrs[ms_nNoOfVisibleBuildings++] = ent; + else +#endif + ms_aVisibleEntityPtrs[ms_nNoOfVisibleEntities++] = ent; +} + +void +CRenderer::ScanBigBuildingList(CPtrList &list) +{ + CPtrNode *node; + CEntity *ent; + int vis; + + int f = CTimer::GetFrameCounter() & 3; + for(node = list.first; node; node = node->next){ + ent = (CEntity*)node->item; + if(ent->bOffscreen || (ent->m_randomSeed&3) != f){ + ent->bOffscreen = true; + vis = SetupBigBuildingVisibility(ent); + }else + vis = VIS_VISIBLE; + switch(vis){ + case VIS_VISIBLE: + InsertEntityIntoList(ent); + ent->bOffscreen = false; + break; + case VIS_STREAMME: + if(!CStreaming::ms_disableStreaming) + CStreaming::RequestModel(ent->GetModelIndex(), 0); + break; + } + } +} + +void +CRenderer::ScanSectorList(CPtrList *lists) +{ + CPtrNode *node; + CPtrList *list; + CEntity *ent; + int i; + float dx, dy; + + for(i = 0; i < NUMSECTORENTITYLISTS; i++){ + list = &lists[i]; + for(node = list->first; node; node = node->next){ + ent = (CEntity*)node->item; + if(ent->m_scanCode == CWorld::GetCurrentScanCode()) + continue; // already seen + ent->m_scanCode = CWorld::GetCurrentScanCode(); + ent->bOffscreen = false; + + switch(SetupEntityVisibility(ent)){ + case VIS_VISIBLE: + InsertEntityIntoList(ent); + break; + case VIS_INVISIBLE: + if(!IsGlass(ent->GetModelIndex())) + break; + // fall through + case VIS_OFFSCREEN: + ent->bOffscreen = true; + dx = ms_vecCameraPosition.x - ent->GetPosition().x; + dy = ms_vecCameraPosition.y - ent->GetPosition().y; + if(dx > -30.0f && dx < 30.0f && + dy > -30.0f && dy < 30.0f && + ms_nNoOfInVisibleEntities < NUMINVISIBLEENTITIES - 1) + ms_aInVisibleEntityPtrs[ms_nNoOfInVisibleEntities++] = ent; + break; + case VIS_STREAMME: + if(!CStreaming::ms_disableStreaming) + if(!m_loadingPriority || CStreaming::ms_numModelsRequested < 10) + CStreaming::RequestModel(ent->GetModelIndex(), 0); + break; + } + } + } +} + +void +CRenderer::ScanSectorList_Priority(CPtrList *lists) +{ + CPtrNode *node; + CPtrList *list; + CEntity *ent; + int i; + float dx, dy; + + for(i = 0; i < NUMSECTORENTITYLISTS; i++){ + list = &lists[i]; + for(node = list->first; node; node = node->next){ + ent = (CEntity*)node->item; + if(ent->m_scanCode == CWorld::GetCurrentScanCode()) + continue; // already seen + ent->m_scanCode = CWorld::GetCurrentScanCode(); + ent->bOffscreen = false; + + switch(SetupEntityVisibility(ent)){ + case VIS_VISIBLE: + InsertEntityIntoList(ent); + break; + case VIS_INVISIBLE: + if(!IsGlass(ent->GetModelIndex())) + break; + // fall through + case VIS_OFFSCREEN: + ent->bOffscreen = true; + dx = ms_vecCameraPosition.x - ent->GetPosition().x; + dy = ms_vecCameraPosition.y - ent->GetPosition().y; + if(dx > -30.0f && dx < 30.0f && + dy > -30.0f && dy < 30.0f && + ms_nNoOfInVisibleEntities < NUMINVISIBLEENTITIES - 1) + ms_aInVisibleEntityPtrs[ms_nNoOfInVisibleEntities++] = ent; + break; + case VIS_STREAMME: + if(!CStreaming::ms_disableStreaming){ + CStreaming::RequestModel(ent->GetModelIndex(), 0); + if(CStreaming::ms_aInfoForModel[ent->GetModelIndex()].m_loadState != STREAMSTATE_LOADED) + m_loadingPriority = true; + } + break; + } + } + } +} + +#ifdef GTA_TRAIN +void +CRenderer::ScanSectorList_Subway(CPtrList *lists) +{ + CPtrNode *node; + CPtrList *list; + CEntity *ent; + int i; + float dx, dy; + + for(i = 0; i < NUMSECTORENTITYLISTS; i++){ + list = &lists[i]; + for(node = list->first; node; node = node->next){ + ent = (CEntity*)node->item; + if(ent->m_scanCode == CWorld::GetCurrentScanCode()) + continue; // already seen + ent->m_scanCode = CWorld::GetCurrentScanCode(); + ent->bOffscreen = false; + switch(SetupEntityVisibility(ent)){ + case VIS_VISIBLE: + InsertEntityIntoList(ent); + break; + case VIS_OFFSCREEN: + ent->bOffscreen = true; + dx = ms_vecCameraPosition.x - ent->GetPosition().x; + dy = ms_vecCameraPosition.y - ent->GetPosition().y; + if(dx > -30.0f && dx < 30.0f && + dy > -30.0f && dy < 30.0f && + ms_nNoOfInVisibleEntities < NUMINVISIBLEENTITIES - 1) + ms_aInVisibleEntityPtrs[ms_nNoOfInVisibleEntities++] = ent; + break; + } + } + } +} +#endif + +void +CRenderer::ScanSectorList_RequestModels(CPtrList *lists) +{ + CPtrNode *node; + CPtrList *list; + CEntity *ent; + int i; + + for(i = 0; i < NUMSECTORENTITYLISTS; i++){ + list = &lists[i]; + for(node = list->first; node; node = node->next){ + ent = (CEntity*)node->item; + if(ent->m_scanCode == CWorld::GetCurrentScanCode()) + continue; // already seen + ent->m_scanCode = CWorld::GetCurrentScanCode(); + if(ShouldModelBeStreamed(ent, ms_vecCameraPosition)) + CStreaming::RequestModel(ent->GetModelIndex(), 0); + } + } +} + +// Put big buildings in front +// This seems pointless because the sector lists shouldn't have big buildings in the first place +void +CRenderer::SortBIGBuildings(void) +{ + int x, y; + for(y = 0; y < NUMSECTORS_Y; y++) + for(x = 0; x < NUMSECTORS_X; x++){ + SortBIGBuildingsForSectorList(&CWorld::GetSector(x, y)->m_lists[ENTITYLIST_BUILDINGS]); + SortBIGBuildingsForSectorList(&CWorld::GetSector(x, y)->m_lists[ENTITYLIST_BUILDINGS_OVERLAP]); + } +} + +void +CRenderer::SortBIGBuildingsForSectorList(CPtrList *list) +{ + CPtrNode *node; + CEntity *ent; + + for(node = list->first; node; node = node->next){ + ent = (CEntity*)node->item; + if(ent->bIsBIGBuilding){ + list->RemoveNode(node); + list->InsertNode(node); + } + } +} + +bool +CRenderer::ShouldModelBeStreamed(CEntity *ent, const CVector &campos) +{ + if(!IsAreaVisible(ent->m_area)) + return false; + CTimeModelInfo *mi = (CTimeModelInfo *)CModelInfo::GetModelInfo(ent->GetModelIndex()); + if(mi->GetModelType() == MITYPE_TIME) + if(!CClock::GetIsTimeInRange(mi->GetTimeOn(), mi->GetTimeOff())) + return false; + float dist = (ent->GetPosition() - campos).Magnitude(); + if(mi->m_noFade) + return dist - STREAM_DISTANCE < mi->GetLargestLodDistance(); + else + return dist - FADE_DISTANCE - STREAM_DISTANCE < mi->GetLargestLodDistance(); +} + +void +CRenderer::RemoveVehiclePedLights(CEntity *ent, bool reset) +{ + if(!ent->bRenderScorched){ + CPointLights::RemoveLightsAffectingObject(); + if(reset) + ReSetAmbientAndDirectionalColours(); + } + SetAmbientColours(); + DeActivateDirectional(); +} diff --git a/src/miami/renderer/Renderer.h b/src/miami/renderer/Renderer.h new file mode 100644 index 00000000..9b202098 --- /dev/null +++ b/src/miami/renderer/Renderer.h @@ -0,0 +1,105 @@ +#pragma once + +class CEntity; + +#ifdef FIX_BUGS +#define LOD_DISTANCE (300.0f*TheCamera.LODDistMultiplier) +#else +#define LOD_DISTANCE 300.0f +#endif +#define FADE_DISTANCE 20.0f +#define STREAM_DISTANCE 30.0f + +extern bool gbShowPedRoadGroups; +extern bool gbShowCarRoadGroups; +extern bool gbShowCollisionPolys; +extern bool gbShowCollisionLines; +extern bool gbBigWhiteDebugLightSwitchedOn; + +extern bool gbDontRenderBuildings; +extern bool gbDontRenderBigBuildings; +extern bool gbDontRenderPeds; +extern bool gbDontRenderObjects; +extern bool gbDontRenderVehicles; + +class CVehicle; +class CPtrList; + +// unused +struct BlockedRange +{ + float a, b; // unknown + BlockedRange *prev, *next; +}; + +class CRenderer +{ + static int32 ms_nNoOfVisibleEntities; + static CEntity *ms_aVisibleEntityPtrs[NUMVISIBLEENTITIES]; + static int32 ms_nNoOfInVisibleEntities; + static CEntity *ms_aInVisibleEntityPtrs[NUMINVISIBLEENTITIES]; +#ifdef NEW_RENDERER + static int32 ms_nNoOfVisibleVehicles; + static CEntity *ms_aVisibleVehiclePtrs[NUMVISIBLEENTITIES]; + // for cWorldStream emulation + static int32 ms_nNoOfVisibleBuildings; + static CEntity *ms_aVisibleBuildingPtrs[NUMVISIBLEENTITIES]; +#endif + + static CVector ms_vecCameraPosition; + static CVehicle *m_pFirstPersonVehicle; + + // unused + static BlockedRange aBlockedRanges[16]; + static BlockedRange *pFullBlockedRanges; + static BlockedRange *pEmptyBlockedRanges; +public: + static float ms_lodDistScale; + static bool m_loadingPriority; + + static void Init(void); + static void Shutdown(void); + static void PreRender(void); + + static void RenderRoads(void); + static void RenderFadingInEntities(void); + static void RenderFadingInUnderwaterEntities(void); + static void RenderEverythingBarRoads(void); + static void RenderBoats(void); + static void RenderOneRoad(CEntity *); + static void RenderOneNonRoad(CEntity *); + static void RenderFirstPersonVehicle(void); + + static void RenderCollisionLines(void); + + static int32 SetupEntityVisibility(CEntity *ent); + static int32 SetupBigBuildingVisibility(CEntity *ent); + + static void ConstructRenderList(void); + static void ScanWorld(void); + static void RequestObjectsInFrustum(void); + static void ScanSectorPoly(RwV2d *poly, int32 numVertices, void (*scanfunc)(CPtrList *)); + static void ScanBigBuildingList(CPtrList &list); + static void ScanSectorList(CPtrList *lists); + static void ScanSectorList_Priority(CPtrList *lists); + static void ScanSectorList_Subway(CPtrList *lists); + static void ScanSectorList_RequestModels(CPtrList *lists); + + static void SortBIGBuildings(void); + static void SortBIGBuildingsForSectorList(CPtrList *list); + + static bool ShouldModelBeStreamed(CEntity *ent, const CVector &campos); + + static void RemoveVehiclePedLights(CEntity *ent, bool reset); + + +#ifdef NEW_RENDERER + static void ClearForFrame(void); + static void RenderPeds(void); + static void RenderVehicles(void); // also renders peds in LCS + static void RenderOneBuilding(CEntity *ent, float camdist = 0.0f); + static void RenderWorld(int pass); // like cWorldStream::Render(int) + static void RenderTransparentWater(void); // keep-out polys and transparent water +#endif + static void InsertEntityIntoList(CEntity *ent); +}; diff --git a/src/miami/renderer/Rubbish.cpp b/src/miami/renderer/Rubbish.cpp new file mode 100644 index 00000000..147c97b1 --- /dev/null +++ b/src/miami/renderer/Rubbish.cpp @@ -0,0 +1,423 @@ +#include "common.h" +#include "main.h" + +#include "General.h" +#include "Timer.h" +#include "Weather.h" +#include "Camera.h" +#include "World.h" +#include "Vehicle.h" +#include "ZoneCull.h" +#include "Stats.h" +#include "TxdStore.h" +#include "RenderBuffer.h" +#include "Rubbish.h" + +#define RUBBISH_MAX_DIST (23.0f) +#define RUBBISH_FADE_DIST (20.0f) + +RwTexture *gpRubbishTexture[4]; +RwImVertexIndex RubbishIndexList[6]; +bool CRubbish::bRubbishInvisible; +int CRubbish::RubbishVisibility; +COneSheet CRubbish::aSheets[NUM_RUBBISH_SHEETS]; +COneSheet CRubbish::StartEmptyList; +COneSheet CRubbish::EndEmptyList; +COneSheet CRubbish::StartStaticsList; +COneSheet CRubbish::EndStaticsList; +COneSheet CRubbish::StartMoversList; +COneSheet CRubbish::EndMoversList; + + +void +COneSheet::AddToList(COneSheet *list) +{ + this->m_next = list->m_next; + this->m_prev = list; + list->m_next = this; + this->m_next->m_prev = this; +} + +void +COneSheet::RemoveFromList(void) +{ + m_next->m_prev = m_prev; + m_prev->m_next = m_next; +} + + +void +CRubbish::Render(void) +{ + int type; + + if(RubbishVisibility == 0) + return; + + PUSH_RENDERGROUP("CRubbish::Render"); + + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); + + for(type = 0; type < 4; type++){ + if(type < 3 || CStats::PamphletMissionPassed) + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpRubbishTexture[type])); + + TempBufferIndicesStored = 0; + TempBufferVerticesStored = 0; + + COneSheet *sheet; + for(sheet = &aSheets[type*NUM_RUBBISH_SHEETS / 4]; + sheet < &aSheets[(type+1)*NUM_RUBBISH_SHEETS / 4]; + sheet++){ + if(sheet->m_state == 0) + continue; + + uint32 alpha = 100; + CVector pos; + if(sheet->m_state == 1){ + pos = sheet->m_basePos; + if(!sheet->m_isVisible) + alpha = 0; + }else{ + pos = sheet->m_animatedPos; + // Not fully visible during animation, calculate current alpha + if(!sheet->m_isVisible || !sheet->m_targetIsVisible){ + float t = (float)(CTimer::GetTimeInMilliseconds() - sheet->m_moveStart)/sheet->m_moveDuration; + float f1 = sheet->m_isVisible ? 1.0f-t : 0.0f; + float f2 = sheet->m_targetIsVisible ? t : 0.0f; + alpha = 100 * (f1+f2); + } + } + + float camDist = (pos - TheCamera.GetPosition()).Magnitude2D(); + if(camDist < RUBBISH_MAX_DIST){ + if(camDist >= RUBBISH_FADE_DIST) + alpha -= alpha*(camDist-RUBBISH_FADE_DIST)/(RUBBISH_MAX_DIST-RUBBISH_FADE_DIST); + alpha = (RubbishVisibility*alpha)/256; + + float vx1, vy1, vx2, vy2; + if(type == 0 || type == 1){ + vx1 = 0.9f*Sin(sheet->m_angle); + vy1 = 0.9f*Cos(sheet->m_angle); + vx2 = 0.3f*Cos(sheet->m_angle); + vy2 = -0.3f*Sin(sheet->m_angle); + }else{ + vx1 = 0.3f*Sin(sheet->m_angle); + vy1 = 0.3f*Cos(sheet->m_angle); + vx2 = 0.3f*Cos(sheet->m_angle); + vy2 = -0.3f*Sin(sheet->m_angle); + } + + int v = TempBufferVerticesStored; + RwIm3DVertexSetPos(&TempBufferRenderVertices[v+0], pos.x + vx1 + vx2, pos.y + vy1 + vy2, pos.z); + RwIm3DVertexSetPos(&TempBufferRenderVertices[v+1], pos.x + vx1 - vx2, pos.y + vy1 - vy2, pos.z); + RwIm3DVertexSetPos(&TempBufferRenderVertices[v+2], pos.x - vx1 + vx2, pos.y - vy1 + vy2, pos.z); + RwIm3DVertexSetPos(&TempBufferRenderVertices[v+3], pos.x - vx1 - vx2, pos.y - vy1 - vy2, pos.z); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+0], 255, 255, 255, alpha); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+1], 255, 255, 255, alpha); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+2], 255, 255, 255, alpha); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+3], 255, 255, 255, alpha); + RwIm3DVertexSetU(&TempBufferRenderVertices[v+0], 0.0f); + RwIm3DVertexSetV(&TempBufferRenderVertices[v+0], 0.0f); + RwIm3DVertexSetU(&TempBufferRenderVertices[v+1], 1.0f); + RwIm3DVertexSetV(&TempBufferRenderVertices[v+1], 0.0f); + RwIm3DVertexSetU(&TempBufferRenderVertices[v+2], 0.0f); + RwIm3DVertexSetV(&TempBufferRenderVertices[v+2], 1.0f); + RwIm3DVertexSetU(&TempBufferRenderVertices[v+3], 1.0f); + RwIm3DVertexSetV(&TempBufferRenderVertices[v+3], 1.0f); + + int i = TempBufferIndicesStored; + TempBufferRenderIndexList[i+0] = RubbishIndexList[0] + TempBufferVerticesStored; + TempBufferRenderIndexList[i+1] = RubbishIndexList[1] + TempBufferVerticesStored; + TempBufferRenderIndexList[i+2] = RubbishIndexList[2] + TempBufferVerticesStored; + TempBufferRenderIndexList[i+3] = RubbishIndexList[3] + TempBufferVerticesStored; + TempBufferRenderIndexList[i+4] = RubbishIndexList[4] + TempBufferVerticesStored; + TempBufferRenderIndexList[i+5] = RubbishIndexList[5] + TempBufferVerticesStored; + TempBufferVerticesStored += 4; + TempBufferIndicesStored += 6; + } + } + + if(TempBufferIndicesStored != 0){ + LittleTest(); + if(RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, rwIM3D_VERTEXUV)){ + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored); + RwIm3DEnd(); + } + } + } + + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + + POP_RENDERGROUP(); +} + +void +CRubbish::StirUp(CVehicle *veh) +{ + if((CTimer::GetFrameCounter() ^ (veh->m_randomSeed&3)) == 0) + return; + + if(Abs(veh->GetPosition().x - TheCamera.GetPosition().x) < 20.0f && + Abs(veh->GetPosition().y - TheCamera.GetPosition().y) < 20.0f) + if(Abs(veh->GetMoveSpeed().x) > 0.05f || Abs(veh->GetMoveSpeed().y) > 0.05f){ + float speed = veh->GetMoveSpeed().Magnitude2D(); + if(speed > 0.05f){ + bool movingForward = DotProduct2D(veh->GetMoveSpeed(), veh->GetForward()) > 0.0f; + COneSheet *sheet = StartStaticsList.m_next; + CVector2D size = veh->GetColModel()->boundingBox.max; + + // Check all static sheets + while(sheet != &EndStaticsList){ + COneSheet *next = sheet->m_next; + CVector2D carToSheet = sheet->m_basePos - veh->GetPosition(); + float distFwd = DotProduct2D(carToSheet, veh->GetForward()); + + // sheet has to be a bit behind car + if(movingForward && distFwd < -0.5f*size.y && distFwd > -1.5f*size.y || + !movingForward && distFwd > 0.5f*size.y && distFwd < 1.5f*size.y){ + float distSide = Abs(DotProduct2D(carToSheet, veh->GetRight())); + if(distSide < 1.5*size.x){ + // Check with higher speed for sheet directly behind car + float speedToCheck = distSide < size.x ? speed : speed*0.5f; + if(speedToCheck > 0.05f){ + sheet->m_state = 2; + if(speedToCheck > 0.15f) + sheet->m_animationType = 2; + else + sheet->m_animationType = 1; + sheet->m_moveDuration = 2000; + sheet->m_xDist = veh->GetMoveSpeed().x; + sheet->m_yDist = veh->GetMoveSpeed().y; + float dist = Sqrt(SQR(sheet->m_xDist)+SQR(sheet->m_yDist)); + sheet->m_xDist *= 25.0f*speed/dist; + sheet->m_yDist *= 25.0f*speed/dist; + sheet->m_animHeight = 3.0f*speed; + sheet->m_moveStart = CTimer::GetTimeInMilliseconds(); + float tx = sheet->m_basePos.x + sheet->m_xDist; + float ty = sheet->m_basePos.y + sheet->m_yDist; + float tz = sheet->m_basePos.z + 3.0f; + sheet->m_targetZ = CWorld::FindGroundZFor3DCoord(tx, ty, tz, nil) + 0.1f; + sheet->RemoveFromList(); + sheet->AddToList(&StartMoversList); + } + } + } + + sheet = next; + } + } + } +} + +static float aAnimations[3][34] = { + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + + // Normal move + { 0.0f, 0.05f, 0.12f, 0.25f, 0.42f, 0.57f, 0.68f, 0.8f, 0.86f, 0.9f, 0.93f, 0.95f, 0.96f, 0.97f, 0.98f, 0.99f, 1.0f, // XY movemnt + 0.15f, 0.35f, 0.6f, 0.9f, 1.2f, 1.25f, 1.3f, 1.2f, 1.1f, 0.95f, 0.8f, 0.6f, 0.45f, 0.3f, 0.2f, 0.1f, 0 }, // Z movement + + // Stirred up by fast vehicle + { 0.0f, 0.05f, 0.12f, 0.25f, 0.42f, 0.57f, 0.68f, 0.8f, 0.95f, 1.1f, 1.15f, 1.18f, 1.15f, 1.1f, 1.05f, 1.03f, 1.0f, + 0.15f, 0.35f, 0.6f, 0.9f, 1.2f, 1.25f, 1.3f, 1.2f, 1.1f, 0.95f, 0.8f, 0.6f, 0.45f, 0.3f, 0.2f, 0.1f, 0 } +}; + +void +CRubbish::Update(void) +{ + bool foundGround; + + // FRAMETIME + if(bRubbishInvisible) + RubbishVisibility = Max(RubbishVisibility-5, 0); + else + RubbishVisibility = Min(RubbishVisibility+5, 255); + + // Spawn a new sheet + COneSheet *sheet = StartEmptyList.m_next; + if(sheet != &EndEmptyList){ + float spawnDist; + float spawnAngle; + + spawnDist = (CGeneral::GetRandomNumber()&0xFF)/256.0f + RUBBISH_MAX_DIST; + uint8 r = CGeneral::GetRandomNumber(); + if(r&1) + spawnAngle = (CGeneral::GetRandomNumber()&0xFF)/256.0f * 6.28f; + else + spawnAngle = (r-128)/160.0f + TheCamera.Orientation; + sheet->m_basePos.x = TheCamera.GetPosition().x + spawnDist*Sin(spawnAngle); + sheet->m_basePos.y = TheCamera.GetPosition().y + spawnDist*Cos(spawnAngle); + sheet->m_basePos.z = CWorld::FindGroundZFor3DCoord(sheet->m_basePos.x, sheet->m_basePos.y, TheCamera.GetPosition().z, &foundGround) + 0.1f; + if(foundGround){ + // Found ground, so add to statics list + sheet->m_angle = (CGeneral::GetRandomNumber()&0xFF)/256.0f * 6.28f; + sheet->m_state = 1; + if(CCullZones::FindAttributesForCoors(sheet->m_basePos, nil) & ATTRZONE_NORAIN) + sheet->m_isVisible = false; + else + sheet->m_isVisible = true; + sheet->RemoveFromList(); + sheet->AddToList(&StartStaticsList); + } + } + + // Process animation + sheet = StartMoversList.m_next; + while(sheet != &EndMoversList){ + uint32 currentTime = CTimer::GetTimeInMilliseconds() - sheet->m_moveStart; + if(currentTime < sheet->m_moveDuration){ + // Animation + int step = 16 * currentTime / sheet->m_moveDuration; // 16 steps in animation + int stepTime = sheet->m_moveDuration/16; // time in each step + float s = (float)(currentTime - stepTime*step) / stepTime; // position on step + float t = (float)currentTime / sheet->m_moveDuration; // position on total animation + // factors for xy and z-movment + float fxy = aAnimations[sheet->m_animationType][step]*(1.0f-s) + aAnimations[sheet->m_animationType][step+1]*s; + float fz = aAnimations[sheet->m_animationType][step+17]*(1.0f-s) + aAnimations[sheet->m_animationType][step+1+17]*s; + sheet->m_animatedPos.x = sheet->m_basePos.x + fxy*sheet->m_xDist; + sheet->m_animatedPos.y = sheet->m_basePos.y + fxy*sheet->m_yDist; + sheet->m_animatedPos.z = (1.0f-t)*sheet->m_basePos.z + t*sheet->m_targetZ + fz*sheet->m_animHeight; + sheet->m_angle += CTimer::GetTimeStep()*0.04f; + if(sheet->m_angle > 6.28f) + sheet->m_angle -= 6.28f; + sheet = sheet->m_next; + }else{ + // End of animation, back into statics list + sheet->m_basePos.x += sheet->m_xDist; + sheet->m_basePos.y += sheet->m_yDist; + sheet->m_basePos.z = sheet->m_targetZ; + sheet->m_state = 1; + sheet->m_isVisible = sheet->m_targetIsVisible; + + COneSheet *next = sheet->m_next; + sheet->RemoveFromList(); + sheet->AddToList(&StartStaticsList); + sheet = next; + } + } + + // Stir up a sheet by wind + // FRAMETIME + int freq; + if(CWeather::Wind < 0.1f) + freq = 31; + else if(CWeather::Wind < 0.4f) + freq = 7; + else if(CWeather::Wind < 0.7f) + freq = 1; + else + freq = 0; + if((CTimer::GetFrameCounter() & freq) == 0){ + // Pick a random sheet and set animation state if static + int i = CGeneral::GetRandomNumber() % NUM_RUBBISH_SHEETS; + if(aSheets[i].m_state == 1){ + aSheets[i].m_moveStart = CTimer::GetTimeInMilliseconds(); + aSheets[i].m_moveDuration = CWeather::Wind*1500.0f + 1000.0f; + aSheets[i].m_animHeight = 0.2f; + aSheets[i].m_xDist = 3.0f*CWeather::Wind; + aSheets[i].m_yDist = 3.0f*CWeather::Wind; + // Check if target position is ok + float tx = aSheets[i].m_basePos.x + aSheets[i].m_xDist; + float ty = aSheets[i].m_basePos.y + aSheets[i].m_yDist; + float tz = aSheets[i].m_basePos.z + 3.0f; + aSheets[i].m_targetZ = CWorld::FindGroundZFor3DCoord(tx, ty, tz, &foundGround) + 0.1f; + if(CCullZones::FindAttributesForCoors(CVector(tx, ty, aSheets[i].m_targetZ), nil) & ATTRZONE_NORAIN) + aSheets[i].m_targetIsVisible = false; + else + aSheets[i].m_targetIsVisible = true; + if(foundGround){ + // start animation + aSheets[i].m_state = 2; + aSheets[i].m_animationType = 1; + aSheets[i].RemoveFromList(); + aSheets[i].AddToList(&StartMoversList); + } + } + } + + // Remove sheets that are too far away + int i = (CTimer::GetFrameCounter()%(NUM_RUBBISH_SHEETS/4))*4; + int last = ((CTimer::GetFrameCounter()%(NUM_RUBBISH_SHEETS/4)) + 1)*4; + for(; i < last; i++){ + if(aSheets[i].m_state == 1 && + (aSheets[i].m_basePos - TheCamera.GetPosition()).MagnitudeSqr2D() > SQR(RUBBISH_MAX_DIST+1.0f)){ + aSheets[i].m_state = 0; + aSheets[i].RemoveFromList(); + aSheets[i].AddToList(&StartEmptyList); + } + } +} + +void +CRubbish::SetVisibility(bool visible) +{ + bRubbishInvisible = !visible; +} + +void +CRubbish::Init(void) +{ + int i; + for(i = 0; i < NUM_RUBBISH_SHEETS; i++){ + aSheets[i].m_state = 0; + if(i < NUM_RUBBISH_SHEETS-1) + aSheets[i].m_next = &aSheets[i+1]; + else + aSheets[i].m_next = &EndEmptyList; + if(i > 0) + aSheets[i].m_prev = &aSheets[i-1]; + else + aSheets[i].m_prev = &StartEmptyList; + } + + StartEmptyList.m_next = &aSheets[0]; + StartEmptyList.m_prev = nil; + EndEmptyList.m_next = nil; + EndEmptyList.m_prev = &aSheets[NUM_RUBBISH_SHEETS-1]; + + StartStaticsList.m_next = &EndStaticsList; + StartStaticsList.m_prev = nil; + EndStaticsList.m_next = nil; + EndStaticsList.m_prev = &StartStaticsList; + + StartMoversList.m_next = &EndMoversList; + StartMoversList.m_prev = nil; + EndMoversList.m_next = nil; + EndMoversList.m_prev = &StartMoversList; + + RubbishIndexList[0] = 0; + RubbishIndexList[1] = 1; + RubbishIndexList[2] = 2; + RubbishIndexList[3] = 1; + RubbishIndexList[4] = 3; + RubbishIndexList[5] = 2; + + CTxdStore::PushCurrentTxd(); + int slot = CTxdStore::FindTxdSlot("particle"); + CTxdStore::SetCurrentTxd(slot); + gpRubbishTexture[0] = RwTextureRead("gameleaf01_64", nil); + gpRubbishTexture[1] = RwTextureRead("gameleaf02_64", nil); + gpRubbishTexture[2] = RwTextureRead("newspaper01_64", nil); + gpRubbishTexture[3] = RwTextureRead("newspaper02_64", nil); + CTxdStore::PopCurrentTxd(); + RubbishVisibility = 255; + bRubbishInvisible = false; +} + +void +CRubbish::Shutdown(void) +{ + RwTextureDestroy(gpRubbishTexture[0]); + gpRubbishTexture[0] = nil; + RwTextureDestroy(gpRubbishTexture[1]); + gpRubbishTexture[1] = nil; + RwTextureDestroy(gpRubbishTexture[2]); + gpRubbishTexture[2] = nil; + RwTextureDestroy(gpRubbishTexture[3]); + gpRubbishTexture[3] = nil; +} diff --git a/src/miami/renderer/Rubbish.h b/src/miami/renderer/Rubbish.h new file mode 100644 index 00000000..5a4e479b --- /dev/null +++ b/src/miami/renderer/Rubbish.h @@ -0,0 +1,57 @@ +#pragma once + +class CVehicle; + +enum { + // NB: not all values are allowed, check the code +#ifdef SQUEEZE_PERFORMANCE + NUM_RUBBISH_SHEETS = 32 +#else + NUM_RUBBISH_SHEETS = 64 +#endif +}; + +class COneSheet +{ +public: + CVector m_basePos; + CVector m_animatedPos; + float m_targetZ; + int8 m_state; + int8 m_animationType; + uint32 m_moveStart; + uint32 m_moveDuration; + float m_animHeight; + float m_xDist; + float m_yDist; + float m_angle; + bool m_isVisible; + bool m_targetIsVisible; + COneSheet *m_next; + COneSheet *m_prev; + + void AddToList(COneSheet *list); + void RemoveFromList(void); +}; + +class CRubbish +{ + static bool bRubbishInvisible; + static int RubbishVisibility; + static COneSheet aSheets[NUM_RUBBISH_SHEETS]; + static COneSheet StartEmptyList; + static COneSheet EndEmptyList; + static COneSheet StartStaticsList; + static COneSheet EndStaticsList; + static COneSheet StartMoversList; + static COneSheet EndMoversList; +public: + static void Render(void); + static void StirUp(CVehicle *veh); // CAutomobile on PS2 + static void Update(void); + static void SetVisibility(bool visible); + static void Init(void); + static void Shutdown(void); +}; + +extern RwTexture *gpRubbishTexture[4]; diff --git a/src/miami/renderer/ShadowCamera.cpp b/src/miami/renderer/ShadowCamera.cpp new file mode 100644 index 00000000..f69c234f --- /dev/null +++ b/src/miami/renderer/ShadowCamera.cpp @@ -0,0 +1,549 @@ +#include "common.h" +#include "rwcore.h" +#include "ShadowCamera.h" +#include "RwHelper.h" + +#define TEXELOFFSET 0.5f + +RpAtomic *ShadowRenderCallBack(RpAtomic *atomic, void *data) +{ + RpAtomicCallBackRender savedCB = RpAtomicGetRenderCallBack(atomic); + RpAtomicSetRenderCallBack(atomic, AtomicDefaultRenderCallBack); + RpAtomicRender(atomic); + RpAtomicSetRenderCallBack(atomic, savedCB); + return atomic; +} + +CShadowCamera::CShadowCamera() +{ + m_pCamera = nil; + m_pTexture = nil; +} + +CShadowCamera::~CShadowCamera() +{ + Destroy(); +} + +void +CShadowCamera::Destroy() +{ + if ( m_pCamera ) + { + RwRaster *raster; + RwFrame *frame; + + frame = RwCameraGetFrame(m_pCamera); + + if ( frame ) + { + RwCameraSetFrame(m_pCamera, nil); + RwFrameDestroy(frame); + } + + raster = RwCameraGetZRaster(m_pCamera); + if ( raster ) + { + RwCameraSetZRaster(m_pCamera, nil); + RwRasterDestroy(raster); + } + + raster = RwCameraGetRaster(m_pCamera); + if ( raster ) + { + RwCameraSetRaster(m_pCamera, nil); + RwRasterDestroy(raster); + } + + if ( m_pTexture ) + { + RwTextureSetRaster(m_pTexture, nil); + RwTextureDestroy(m_pTexture); + m_pTexture = nil; + } + + RwCameraDestroy(m_pCamera); + m_pCamera = nil; + } + return; +} + +RwCamera * +CShadowCamera::Create(int32 rasterSize) +{ + int32 size = 1 << rasterSize; + + m_pCamera = RwCameraCreate(); + ASSERT(m_pCamera != nil); + + if ( m_pCamera ) + { + RwCameraSetFrame(m_pCamera, RwFrameCreate()); + + if( RwCameraGetFrame(m_pCamera) ) + { + RwRaster *zRaster = RwRasterCreate(size, size, 0, rwRASTERTYPEZBUFFER); + ASSERT(zRaster != nil); + + if ( zRaster ) + { + RwCameraSetZRaster(m_pCamera, zRaster); + + RwRaster *raster = RwRasterCreate(size, size, 0, rwRASTERTYPECAMERATEXTURE); + ASSERT(raster != nil); + + if ( raster ) + { + RwCameraSetRaster(m_pCamera, raster); + m_pTexture = RwTextureCreate(raster); + ASSERT(m_pTexture != nil); + + if ( m_pTexture ) + { + RwTextureSetAddressing(m_pTexture, rwTEXTUREADDRESSCLAMP); + RwTextureSetFilterMode(m_pTexture, rwFILTERLINEAR); + RwCameraSetProjection(m_pCamera, rwPARALLEL); + return (m_pCamera); + } + } + } + } + } + + Destroy(); + + return (nil); +} + +RwCamera * +CShadowCamera::SetFrustum(float objectRadius) +{ + ASSERT(m_pCamera != nil); + + RwV2d vw; + + RwCameraSetFarClipPlane (m_pCamera, 2.0f * objectRadius); + RwCameraSetNearClipPlane(m_pCamera, 0.001f * objectRadius); + + vw.x = objectRadius; + vw.y = objectRadius; + RwCameraSetViewWindow(m_pCamera, &vw); + + return m_pCamera; +} + +RwCamera * +CShadowCamera::SetLight(RpLight *light) +{ + ASSERT(light != nil); + ASSERT(m_pCamera != nil); + + RwFrame *camFrame = RwCameraGetFrame(m_pCamera); + RwMatrix *camMatrix = RwFrameGetMatrix(camFrame); + RwFrame *lightFrame = RpLightGetFrame(light); + RwMatrix *lightMatrix = RwFrameGetMatrix(lightFrame); + + *RwMatrixGetRight(camMatrix) = *RwMatrixGetRight(lightMatrix); + *RwMatrixGetUp(camMatrix) = *RwMatrixGetUp(lightMatrix); + *RwMatrixGetAt(camMatrix) = *RwMatrixGetAt(lightMatrix); + + RwMatrixUpdate(camMatrix); + RwFrameUpdateObjects(camFrame); + + return m_pCamera; +} + +RwCamera * +CShadowCamera::SetCenter(RwV3d *center) +{ + ASSERT(center != nil); + ASSERT(m_pCamera != nil); + + RwFrame *camFrame = RwCameraGetFrame(m_pCamera); + RwMatrix *camMatrix = RwFrameGetMatrix(camFrame); + + *RwMatrixGetPos(camMatrix) = *center; + + RwV3dIncrementScaled(RwMatrixGetPos(camMatrix), RwMatrixGetAt(camMatrix), -0.5f * RwCameraGetFarClipPlane(m_pCamera)); + + RwMatrixUpdate(camMatrix); + RwFrameUpdateObjects(camFrame); + RwFrameOrthoNormalize(camFrame); + + return m_pCamera; +} + +RwCamera * +CShadowCamera::Update(RpClump *clump) +{ + ASSERT(clump != nil); + ASSERT(m_pCamera != nil); + + RwUInt32 flags; + RpGeometry *geometry; + + RwRGBA bgColor = { 255, 255, 255, 0 }; + + RwCameraClear(m_pCamera, &bgColor, rwCAMERACLEARZ | rwCAMERACLEARIMAGE); + + if ( RwCameraBeginUpdate(m_pCamera) ) + { + geometry = RpAtomicGetGeometry(GetFirstAtomic(clump)); + ASSERT(geometry != nil); + + flags = RpGeometryGetFlags(geometry); + + RpGeometrySetFlags(geometry, flags & ~(rpGEOMETRYPRELIT|rpGEOMETRYLIGHT + |rpGEOMETRYTEXTURED|rpGEOMETRYTEXTURED2|rpGEOMETRYMODULATEMATERIALCOLOR)); + + RpClumpForAllAtomics(clump, ShadowRenderCallBack, nil); + + RpGeometrySetFlags(geometry, flags); + + InvertRaster(); + RwCameraEndUpdate(m_pCamera); + } + + return m_pCamera; +} + +RwCamera * +CShadowCamera::Update(RpAtomic *atomic) +{ + ASSERT(atomic != nil); + ASSERT(m_pCamera != nil); + + RwUInt32 flags; + RpGeometry *geometry; + + RwRGBA bgColor = { 255, 255, 255, 0 }; + + RwCameraClear(m_pCamera, &bgColor, rwCAMERACLEARZ | rwCAMERACLEARIMAGE); + + if ( RwCameraBeginUpdate(m_pCamera) ) + { + geometry = RpAtomicGetGeometry(atomic); + ASSERT(geometry != nil); + flags = RpGeometryGetFlags(geometry); + + RpGeometrySetFlags(geometry, flags & ~(rpGEOMETRYPRELIT|rpGEOMETRYLIGHT + |rpGEOMETRYTEXTURED|rpGEOMETRYTEXTURED2|rpGEOMETRYMODULATEMATERIALCOLOR|rpGEOMETRYNORMALS)); + + ShadowRenderCallBack(atomic, nil); + + RpGeometrySetFlags(geometry, flags); + + InvertRaster(); + RwCameraEndUpdate(m_pCamera); + } + + return m_pCamera; +} + +void +CShadowCamera::InvertRaster() +{ + ASSERT(m_pCamera != nil); + + RwIm2DVertex vx[4]; + float crw, crh; + RwRaster *raster; + float recipZ; + + raster = RwCameraGetRaster(m_pCamera); + ASSERT(raster != nil); + + crw = (float)RwRasterGetWidth(raster); + crh = (float)RwRasterGetHeight(raster); + + recipZ = 1.0f / RwCameraGetNearClipPlane(m_pCamera); + + RwIm2DVertexSetScreenX (&vx[0], 0.0f); + RwIm2DVertexSetScreenY (&vx[0], 0.0f); + RwIm2DVertexSetScreenZ (&vx[0], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetRecipCameraZ(&vx[0], recipZ); + RwIm2DVertexSetIntRGBA (&vx[0], 255, 255, 255, 255); + + RwIm2DVertexSetScreenX (&vx[1], 0.0f); + RwIm2DVertexSetScreenY (&vx[1], crh); + RwIm2DVertexSetScreenZ (&vx[1], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetRecipCameraZ(&vx[1], recipZ); + RwIm2DVertexSetIntRGBA (&vx[1], 255, 255, 255, 255); + + RwIm2DVertexSetScreenX (&vx[2], crw); + RwIm2DVertexSetScreenY (&vx[2], 0.0f); + RwIm2DVertexSetScreenZ (&vx[2], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetRecipCameraZ(&vx[2], recipZ); + RwIm2DVertexSetIntRGBA (&vx[2], 255, 255, 255, 255); + + RwIm2DVertexSetScreenX (&vx[3], crw); + RwIm2DVertexSetScreenY (&vx[3], crh); + RwIm2DVertexSetScreenZ (&vx[3], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetRecipCameraZ(&vx[3], recipZ); + RwIm2DVertexSetIntRGBA (&vx[3], 255, 255, 255, 255); + + + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)nil); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDINVDESTCOLOR); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDZERO); + + RwIm2DRenderPrimitive(rwPRIMTYPETRISTRIP, vx, 4); + + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); +} + +RwRaster * +CShadowCamera::MakeGradientRaster() +{ + ASSERT(m_pCamera != nil); + + RwIm2DVertex vx[2]; + + if ( !m_pCamera ) + return nil; + + float recipCamZ = 1.0f / RwCameraGetNearClipPlane(m_pCamera); + + RwRaster *raster = RwCameraGetRaster(m_pCamera); + ASSERT(raster != nil); + + float width = (float)RwRasterGetWidth(raster); + float height = (float)RwRasterGetHeight(raster); + + if ( height < 1 ) + return nil; + + if ( RwCameraBeginUpdate(m_pCamera) ) + { + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)rwFILTERNAFILTERMODE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDZERO); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDINVDESTCOLOR); + RwRenderStateSet(rwRENDERSTATESHADEMODE, (void *)rwSHADEMODEFLAT); + + float color = 255.0f; + float step = (-191.0f / height); + + for ( int32 i = 0; i < height; i++ ) + { + RwIm2DVertexSetScreenX (&vx[0], 0.0f); + RwIm2DVertexSetScreenY (&vx[0], i); + RwIm2DVertexSetScreenZ (&vx[0], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetRecipCameraZ(&vx[0], recipCamZ); + RwIm2DVertexSetIntRGBA (&vx[0], (uint32)color, (uint32)color, (uint32)color, (uint32)color); + + RwIm2DVertexSetScreenX (&vx[1], width - 1); + RwIm2DVertexSetScreenY (&vx[1], i); + RwIm2DVertexSetScreenZ (&vx[1], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetRecipCameraZ(&vx[1], recipCamZ); + RwIm2DVertexSetIntRGBA (&vx[1], (uint32)color, (uint32)color, (uint32)color, (uint32)color); + + RwIm2DRenderLine(vx, 2, 0, 1); + + color += step; + } + + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATESHADEMODE, (void *)rwSHADEMODEGOURAUD); + + RwCameraEndUpdate(m_pCamera); + } + + return raster; +} + +RwRaster * +CShadowCamera::RasterResample(RwRaster *dstRaster) +{ + ASSERT(dstRaster != nil); + ASSERT(m_pCamera != nil); + + if ( !m_pCamera ) + return nil; + + RwRaster *raster = RwCameraGetRaster(m_pCamera); + ASSERT(raster != nil); + + float size = (float) RwRasterGetWidth(raster); + float uvOffset = TEXELOFFSET / size; + float recipCamZ = 1.0f / RwCameraGetNearClipPlane(m_pCamera); + + if ( RwCameraBeginUpdate(m_pCamera) ) + { + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDZERO); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void *)rwFILTERLINEAR); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)dstRaster); + + Im2DRenderQuad(0.0f, 0.0f, size, size, RwIm2DGetNearScreenZ(), recipCamZ, uvOffset); + + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); + + RwCameraEndUpdate(m_pCamera); + } + + return raster; +} + +RwRaster * +CShadowCamera::RasterBlur(RwRaster *dstRaster, int32 numPasses) +{ + ASSERT(dstRaster != nil); + ASSERT(m_pCamera != nil); + + if ( !m_pCamera ) + return nil; + + RwRaster *raster = RwCameraGetRaster(m_pCamera); + ASSERT(raster != nil); + + float size = (float) RwRasterGetWidth(dstRaster); + float recipCamZ = 1.0f / RwCameraGetNearClipPlane(m_pCamera); + + for (int i = 0; i < numPasses; i++ ) + { + RwCameraSetRaster(m_pCamera, raster); + + if ( RwCameraBeginUpdate(m_pCamera) ) + { + if ( i == 0 ) + { + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDZERO); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void *)rwFILTERLINEAR); + } + + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)dstRaster); + Im2DRenderQuad(0.0f, 0.0f, size, size, RwIm2DGetNearScreenZ(), recipCamZ, 1.0f / size); + RwCameraEndUpdate(m_pCamera); + } + + RwCameraSetRaster(m_pCamera, dstRaster); + + if ( RwCameraBeginUpdate(m_pCamera) ) + { + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)raster); + Im2DRenderQuad(0.0f, 0.0f, size, size, RwIm2DGetNearScreenZ(), recipCamZ, 0); + + if ( i == numPasses - 1 ) + { + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); + } + + RwCameraEndUpdate(m_pCamera); + } + } + + RwCameraSetRaster(m_pCamera, raster); + + return dstRaster; +} + +RwRaster * +CShadowCamera::RasterGradient(RwRaster *dstRaster) +{ + ASSERT(dstRaster != nil); + ASSERT(m_pCamera != nil); + + RwRaster *raster = RwCameraGetRaster(m_pCamera); + ASSERT(raster != nil); + + float size = (float)RwRasterGetWidth(dstRaster); + float recipCamZ = 1.0f / RwCameraGetNearClipPlane(m_pCamera); + + RwCameraSetRaster(m_pCamera, dstRaster); + + if ( RwCameraBeginUpdate(m_pCamera) ) + { + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDZERO); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDSRCCOLOR); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void *)rwFILTERLINEAR); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)raster); + + Im2DRenderQuad(0, 0, size, size, RwIm2DGetNearScreenZ(), recipCamZ, 0); + + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); + + RwCameraEndUpdate(m_pCamera); + } + + RwCameraSetRaster(m_pCamera, raster); + + return dstRaster; +} + +RwRaster *CShadowCamera::DrawOutlineBorder(RwRGBA const& color) +{ + ASSERT(m_pCamera != nil); + + RwIm2DVertex vx[4]; + RwImVertexIndex ix[5]; + + RwRaster *raster = RwCameraGetRaster(m_pCamera); + ASSERT(raster != nil); + + float size = (float)RwRasterGetWidth(raster) - 1.0f; + float recipCamZ = 1.0f / RwCameraGetNearClipPlane(m_pCamera); + + RwIm2DVertexSetScreenX (&vx[0], 0.0f); + RwIm2DVertexSetScreenY (&vx[0], 0.0f); + RwIm2DVertexSetScreenZ (&vx[0], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetIntRGBA (&vx[0], color.red, color.green, color.blue, color.alpha); + RwIm2DVertexSetRecipCameraZ(&vx[0], recipCamZ); + + RwIm2DVertexSetScreenX (&vx[1], size); + RwIm2DVertexSetScreenY (&vx[1], 0.0f); + RwIm2DVertexSetScreenZ (&vx[1], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetIntRGBA (&vx[1], color.red, color.green, color.blue, color.alpha); + RwIm2DVertexSetRecipCameraZ(&vx[1], recipCamZ); + + RwIm2DVertexSetScreenX (&vx[2], size); + RwIm2DVertexSetScreenY (&vx[2], size); + RwIm2DVertexSetScreenZ (&vx[2], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetIntRGBA (&vx[2], color.red, color.green, color.blue, color.alpha); + RwIm2DVertexSetRecipCameraZ(&vx[2], recipCamZ); + + RwIm2DVertexSetScreenX (&vx[3], 0.0f); + RwIm2DVertexSetScreenY (&vx[3], size); + RwIm2DVertexSetScreenZ (&vx[3], RwIm2DGetNearScreenZ()); + RwIm2DVertexSetIntRGBA (&vx[3], color.red, color.green, color.blue, color.alpha); + RwIm2DVertexSetRecipCameraZ(&vx[3], recipCamZ); + + ix[0] = 0; + ix[4] = 0; + ix[1] = 1; + ix[2] = 2; + ix[3] = 3; + + if ( RwCameraBeginUpdate(m_pCamera) ) + { + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)nil); + + RwIm2DRenderIndexedPrimitive(rwPRIMTYPEPOLYLINE, vx, 4, ix, 5); + + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); + + RwCameraEndUpdate(m_pCamera); + } + + return raster; +} \ No newline at end of file diff --git a/src/miami/renderer/ShadowCamera.h b/src/miami/renderer/ShadowCamera.h new file mode 100644 index 00000000..a2149db7 --- /dev/null +++ b/src/miami/renderer/ShadowCamera.h @@ -0,0 +1,54 @@ +#pragma once + + +class CShadowCamera +{ +public: + RwCamera *m_pCamera; + RwTexture *m_pTexture; + + CShadowCamera(); + ~CShadowCamera(); + + RwCamera *Create(int32 rasterSize); + void Destroy(); + + RwCamera *SetFrustum(float objectRadius); + RwCamera *SetLight(RpLight *light); + RwCamera *SetCenter(RwV3d *center); + + RwCamera *Update(RpClump *clump); + RwCamera *Update(RpAtomic *atomic); + + void InvertRaster(); + + RwRaster* GetRwRenderRaster() + { + return RwCameraGetRaster(m_pCamera); + } + + // ShadowRasterRender(RwV2d *) + // ApplyAlphaMapToRaster(void) + + RwRaster *MakeGradientRaster(); + + RwTexture *GetRwRenderTexture() + { + return m_pTexture; + } + + RwRaster* GetRwZRaster() + { + return RwCameraGetZRaster(m_pCamera); + } + + RwRaster *RasterResample(RwRaster *dstRaster); + RwRaster *RasterBlur(RwRaster *dstRaster, int32 numPasses); + RwRaster *RasterGradient(RwRaster *dstRaster); + RwRaster *DrawOutlineBorder(RwRGBA const& color); + + RwCamera *GetRwCamera() + { + return m_pCamera; + } +}; \ No newline at end of file diff --git a/src/miami/renderer/Shadows.cpp b/src/miami/renderer/Shadows.cpp new file mode 100644 index 00000000..5339916a --- /dev/null +++ b/src/miami/renderer/Shadows.cpp @@ -0,0 +1,2515 @@ +#include "common.h" + +#include "main.h" +#include "TxdStore.h" +#include "Timer.h" +#include "Camera.h" +#include "Timecycle.h" +#include "CutsceneMgr.h" +#include "Automobile.h" +#include "Bike.h" +#include "Ped.h" +#include "PlayerPed.h" +#include "World.h" +#include "Weather.h" +#include "ModelIndices.h" +#include "RenderBuffer.h" +#ifdef FIX_BUGS +#include "Replay.h" +#endif +#include "PointLights.h" +#include "SpecialFX.h" +#include "Script.h" +#include "TimeStep.h" +#include "Shadows.h" +#include "CutsceneObject.h" +#include "CutsceneShadow.h" +#include "Clock.h" +#include "VarConsole.h" + +#ifdef DEBUGMENU +//SETTWEAKPATH("Shadows"); +//TWEAKBOOL(gbPrintShite); +#endif + +RwImVertexIndex ShadowIndexList[24]; + +RwTexture *gpShadowCarTex; +RwTexture *gpShadowPedTex; +RwTexture *gpShadowHeliTex; +RwTexture *gpShadowBikeTex; +RwTexture *gpShadowBaronTex; +RwTexture *gpShadowExplosionTex; +RwTexture *gpShadowHeadLightsTex; +RwTexture *gpOutline1Tex; +RwTexture *gpOutline2Tex; +RwTexture *gpOutline3Tex; +RwTexture *gpBloodPoolTex; +RwTexture *gpReflectionTex; +RwTexture *gpWalkDontTex; +RwTexture *gpCrackedGlassTex; +RwTexture *gpPostShadowTex; +RwTexture *gpGoalTex; + +int16 CShadows::ShadowsStoredToBeRendered; +CStoredShadow CShadows::asShadowsStored [MAX_STOREDSHADOWS]; +CPolyBunch CShadows::aPolyBunches [MAX_POLYBUNCHES]; +CStaticShadow CShadows::aStaticShadows [MAX_STATICSHADOWS]; +CPolyBunch *CShadows::pEmptyBunchList; +CPermanentShadow CShadows::aPermanentShadows[MAX_PERMAMENTSHADOWS]; + +#ifndef MASTER +bool gbCountPolysInShadow; +#endif + +void +CShadows::Init(void) +{ + CTxdStore::PushCurrentTxd(); + + int32 slut = CTxdStore::FindTxdSlot("particle"); + CTxdStore::SetCurrentTxd(slut); + + gpShadowCarTex = RwTextureRead("shad_car", nil); + gpShadowPedTex = RwTextureRead("shad_ped", nil); + gpShadowHeliTex = RwTextureRead("shad_heli", nil); + gpShadowBikeTex = RwTextureRead("shad_bike", nil); + gpShadowBaronTex = RwTextureRead("shad_rcbaron", nil); + gpShadowExplosionTex = RwTextureRead("shad_exp", nil); + gpShadowHeadLightsTex = RwTextureRead("headlight", nil); + gpOutline1Tex = RwTextureRead("outline_64", nil); + gpOutline2Tex = RwTextureRead("outline2_64", nil); + gpOutline3Tex = RwTextureRead("outline3_64", nil); + gpBloodPoolTex = RwTextureRead("bloodpool_64", nil); + gpReflectionTex = RwTextureRead("reflection01", nil); + gpWalkDontTex = RwTextureRead("walk_dont", nil); + gpCrackedGlassTex = RwTextureRead("wincrack_32", nil); + gpPostShadowTex = RwTextureRead("lamp_shad_64", nil); + + CTxdStore::PopCurrentTxd(); + + ASSERT(gpShadowCarTex != nil); + ASSERT(gpShadowPedTex != nil); + ASSERT(gpShadowHeliTex != nil); + ASSERT(gpShadowBikeTex != nil); + ASSERT(gpShadowBaronTex != nil); + ASSERT(gpShadowExplosionTex != nil); + ASSERT(gpShadowHeadLightsTex != nil); + ASSERT(gpOutline1Tex != nil); + ASSERT(gpOutline2Tex != nil); + ASSERT(gpOutline3Tex != nil); + ASSERT(gpBloodPoolTex != nil); + ASSERT(gpReflectionTex != nil); + ASSERT(gpWalkDontTex != nil); + ASSERT(gpCrackedGlassTex != nil); + ASSERT(gpPostShadowTex != nil); + + + ShadowIndexList[0] = 0; + ShadowIndexList[1] = 2; + ShadowIndexList[2] = 1; + + ShadowIndexList[3] = 0; + ShadowIndexList[4] = 3; + ShadowIndexList[5] = 2; + + ShadowIndexList[6] = 0; + ShadowIndexList[7] = 4; + ShadowIndexList[8] = 3; + + ShadowIndexList[9] = 0; + ShadowIndexList[10] = 5; + ShadowIndexList[11] = 4; + + ShadowIndexList[12] = 0; + ShadowIndexList[13] = 6; + ShadowIndexList[14] = 5; + + ShadowIndexList[15] = 0; + ShadowIndexList[16] = 7; + ShadowIndexList[17] = 6; + + ShadowIndexList[18] = 0; + ShadowIndexList[19] = 8; + ShadowIndexList[20] = 7; + + ShadowIndexList[21] = 0; + ShadowIndexList[22] = 9; + ShadowIndexList[23] = 8; + + + for ( int32 i = 0; i < MAX_STATICSHADOWS; i++ ) + { + aStaticShadows[i].m_nId = 0; + aStaticShadows[i].m_pPolyBunch = nil; + } + + pEmptyBunchList = &aPolyBunches[0]; + + for ( int32 i = 0; i < MAX_POLYBUNCHES; i++ ) + { + if ( i == MAX_POLYBUNCHES - 1 ) + aPolyBunches[i].m_pNext = nil; + else + aPolyBunches[i].m_pNext = &aPolyBunches[i + 1]; + } + + for ( int32 i = 0; i < MAX_PERMAMENTSHADOWS; i++ ) + { + aPermanentShadows[i].m_nType = SHADOWTYPE_NONE; + } + +#ifndef MASTER + VarConsole.Add("Count polys in shadow", &gbCountPolysInShadow, true); +#endif +} + +void +CShadows::Shutdown(void) +{ + ASSERT(gpShadowCarTex != nil); + ASSERT(gpShadowPedTex != nil); + ASSERT(gpShadowHeliTex != nil); + ASSERT(gpShadowBikeTex != nil); + ASSERT(gpShadowBaronTex != nil); + ASSERT(gpShadowExplosionTex != nil); + ASSERT(gpShadowHeadLightsTex != nil); + ASSERT(gpOutline1Tex != nil); + ASSERT(gpOutline2Tex != nil); + ASSERT(gpOutline3Tex != nil); + ASSERT(gpBloodPoolTex != nil); + ASSERT(gpReflectionTex != nil); + ASSERT(gpWalkDontTex != nil); + ASSERT(gpCrackedGlassTex != nil); + ASSERT(gpPostShadowTex != nil); + + RwTextureDestroy(gpShadowCarTex); + RwTextureDestroy(gpShadowPedTex); + RwTextureDestroy(gpShadowHeliTex); + RwTextureDestroy(gpShadowBikeTex); + RwTextureDestroy(gpShadowBaronTex); + RwTextureDestroy(gpShadowExplosionTex); + RwTextureDestroy(gpShadowHeadLightsTex); + RwTextureDestroy(gpOutline1Tex); + RwTextureDestroy(gpOutline2Tex); + RwTextureDestroy(gpOutline3Tex); + RwTextureDestroy(gpBloodPoolTex); + RwTextureDestroy(gpReflectionTex); + RwTextureDestroy(gpWalkDontTex); + RwTextureDestroy(gpCrackedGlassTex); + RwTextureDestroy(gpPostShadowTex); +} + +void +CShadows::AddPermanentShadow(uint8 ShadowType, RwTexture *pTexture, CVector *pPosn, + float fFrontX, float fFrontY, float fSideX, float fSideY, + int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, + float fZDistance, uint32 nTime, float fScale) +{ + ASSERT(pTexture != nil); + ASSERT(pPosn != nil); + + + // find free slot + int32 nSlot = 0; + while ( nSlot < MAX_PERMAMENTSHADOWS && aPermanentShadows[nSlot].m_nType != SHADOWTYPE_NONE ) + nSlot++; + + if ( nSlot < MAX_PERMAMENTSHADOWS ) + { + aPermanentShadows[nSlot].m_nType = ShadowType; + aPermanentShadows[nSlot].m_pTexture = pTexture; + aPermanentShadows[nSlot].m_vecPos = *pPosn; + aPermanentShadows[nSlot].m_vecFront.x = fFrontX; + aPermanentShadows[nSlot].m_vecFront.y = fFrontY; + aPermanentShadows[nSlot].m_vecSide.x = fSideX; + aPermanentShadows[nSlot].m_vecSide.y = fSideY; + aPermanentShadows[nSlot].m_nIntensity = nIntensity; + aPermanentShadows[nSlot].m_nRed = nRed; + aPermanentShadows[nSlot].m_nGreen = nGreen; + aPermanentShadows[nSlot].m_nBlue = nBlue; + aPermanentShadows[nSlot].m_fZDistance = fZDistance; + aPermanentShadows[nSlot].m_nLifeTime = nTime; + aPermanentShadows[nSlot].m_nTimeCreated = CTimer::GetTimeInMilliseconds(); + } +} + +bool +CShadows::StoreStaticShadow(uint32 nID, uint8 ShadowType, RwTexture *pTexture, Const CVector *pPosn, + float fFrontX, float fFrontY, float fSideX, float fSideY, + int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, + float fZDistance, float fScale, float fDrawDistance, bool bTempShadow, float fUpDistance) +{ + ASSERT(pPosn != nil); + + float fDistToCamSqr = (*pPosn - TheCamera.GetPosition()).MagnitudeSqr2D(); + + if ( SQR(fDrawDistance) > fDistToCamSqr || fDrawDistance == 0.0f ) + { + if ( fDrawDistance != 0.0f ) + { + float fDistToCam = Sqrt(fDistToCamSqr); + + if ( fDistToCam >= (fDrawDistance*(1.0f-(1.0f/4.0f))) ) + { + //fDistToCam == 0 -> 4 + //fDistToCam == fDrawDistance -> 0 + float fMult = 1.0f - (4.0f / fDrawDistance) * (fDistToCam - (fDrawDistance*(1.0f-(1.0f/4.0f)))); + + nIntensity = (int32)(nIntensity * fMult); + nRed = (int32)(nRed * fMult); + nGreen = (int32)(nGreen * fMult); + nBlue = (int32)(nBlue * fMult); + } + } + + int32 nSlot; + + nSlot = 0; + while ( nSlot < MAX_STATICSHADOWS && !(nID == aStaticShadows[nSlot].m_nId && aStaticShadows[nSlot].m_pPolyBunch != nil) ) + nSlot++; + + if ( nSlot < MAX_STATICSHADOWS ) + { + if ( Abs(pPosn->x - aStaticShadows[nSlot].m_vecPosn.x) < fUpDistance + && Abs(pPosn->y - aStaticShadows[nSlot].m_vecPosn.y) < fUpDistance ) + { + aStaticShadows[nSlot].m_bJustCreated = true; + aStaticShadows[nSlot].m_nType = ShadowType; + aStaticShadows[nSlot].m_pTexture = pTexture; + aStaticShadows[nSlot].m_nIntensity = nIntensity; + aStaticShadows[nSlot].m_nRed = nRed; + aStaticShadows[nSlot].m_nGreen = nGreen; + aStaticShadows[nSlot].m_nBlue = nBlue; + aStaticShadows[nSlot].m_fZDistance = fZDistance; + aStaticShadows[nSlot].m_fScale = fScale; + aStaticShadows[nSlot].m_bTemp = bTempShadow; + aStaticShadows[nSlot].m_nTimeCreated = CTimer::GetTimeInMilliseconds(); + + return true; + } + else if ( Abs(pPosn->x - aStaticShadows[nSlot].m_vecPosn.x) < 0.05f + && Abs(pPosn->y - aStaticShadows[nSlot].m_vecPosn.y) < 0.05f + && Abs(pPosn->z - aStaticShadows[nSlot].m_vecPosn.z) < 2.0f + + && fFrontX == aStaticShadows[nSlot].m_vecFront.x + && fFrontY == aStaticShadows[nSlot].m_vecFront.y + && fSideX == aStaticShadows[nSlot].m_vecSide.x + && fSideY == aStaticShadows[nSlot].m_vecSide.y ) + { + aStaticShadows[nSlot].m_bJustCreated = true; + aStaticShadows[nSlot].m_nType = ShadowType; + aStaticShadows[nSlot].m_pTexture = pTexture; + aStaticShadows[nSlot].m_nIntensity = nIntensity; + aStaticShadows[nSlot].m_nRed = nRed; + aStaticShadows[nSlot].m_nGreen = nGreen; + aStaticShadows[nSlot].m_nBlue = nBlue; + aStaticShadows[nSlot].m_fZDistance = fZDistance; + aStaticShadows[nSlot].m_fScale = fScale; + aStaticShadows[nSlot].m_bTemp = bTempShadow; + aStaticShadows[nSlot].m_nTimeCreated = CTimer::GetTimeInMilliseconds(); + + return true; + } + else + { + aStaticShadows[nSlot].Free(); + + aStaticShadows[nSlot].m_nId = nID; + aStaticShadows[nSlot].m_nType = ShadowType; + aStaticShadows[nSlot].m_pTexture = pTexture; + aStaticShadows[nSlot].m_nIntensity = nIntensity; + aStaticShadows[nSlot].m_nRed = nRed; + aStaticShadows[nSlot].m_nGreen = nGreen; + aStaticShadows[nSlot].m_nBlue = nBlue; + aStaticShadows[nSlot].m_fZDistance = fZDistance; + aStaticShadows[nSlot].m_fScale = fScale; + aStaticShadows[nSlot].m_vecPosn = *pPosn; + aStaticShadows[nSlot].m_vecFront.x = fFrontX; + aStaticShadows[nSlot].m_vecFront.y = fFrontY; + aStaticShadows[nSlot].m_vecSide.x = fSideX; + aStaticShadows[nSlot].m_vecSide.y = fSideY; + aStaticShadows[nSlot].m_bJustCreated = true; + aStaticShadows[nSlot].m_bTemp = bTempShadow; + aStaticShadows[nSlot].m_nTimeCreated = CTimer::GetTimeInMilliseconds(); + + GeneratePolysForStaticShadow(nSlot); + + return aStaticShadows[nSlot].m_pPolyBunch != nil; + } + } + else + { + nSlot = 0; + while ( nSlot < MAX_STATICSHADOWS && aStaticShadows[nSlot].m_pPolyBunch != nil ) + nSlot++; + + if ( nSlot != MAX_STATICSHADOWS ) + { + aStaticShadows[nSlot].m_nId = nID; + aStaticShadows[nSlot].m_nType = ShadowType; + aStaticShadows[nSlot].m_pTexture = pTexture; + aStaticShadows[nSlot].m_nIntensity = nIntensity; + aStaticShadows[nSlot].m_nRed = nRed; + aStaticShadows[nSlot].m_nGreen = nGreen; + aStaticShadows[nSlot].m_nBlue = nBlue; + aStaticShadows[nSlot].m_fZDistance = fZDistance; + aStaticShadows[nSlot].m_fScale = fScale; + aStaticShadows[nSlot].m_vecPosn = *pPosn; + aStaticShadows[nSlot].m_vecFront.x = fFrontX; + aStaticShadows[nSlot].m_vecFront.y = fFrontY; + aStaticShadows[nSlot].m_vecSide.x = fSideX; + aStaticShadows[nSlot].m_vecSide.y = fSideY; + aStaticShadows[nSlot].m_bJustCreated = true; + aStaticShadows[nSlot].m_bTemp = bTempShadow; + aStaticShadows[nSlot].m_nTimeCreated = CTimer::GetTimeInMilliseconds(); + + GeneratePolysForStaticShadow(nSlot); + + return aStaticShadows[nSlot].m_pPolyBunch != nil; + } + } + } + + return true; +} + +void +CShadows::StoreShadowToBeRendered(uint8 ShadowTexture, CVector *pPosn, + float fFrontX, float fFrontY, float fSideX, float fSideY, + int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue) +{ + ASSERT(pPosn != nil); + + switch ( ShadowTexture ) + { + case SHADOWTEX_NONE: + { + break; + } + + case SHADOWTEX_CAR: + { + StoreShadowToBeRendered(SHADOWTYPE_DARK, gpShadowCarTex, pPosn, + fFrontX, fFrontY, fSideX, fSideY, + nIntensity, nRed, nGreen, nBlue, + 15.0f, false, 1.0f, nil, false); + + break; + } + + case SHADOWTEX_PED: + { + StoreShadowToBeRendered(SHADOWTYPE_DARK, gpShadowPedTex, pPosn, + fFrontX, fFrontY, fSideX, fSideY, + nIntensity, nRed, nGreen, nBlue, + 15.0f, false, 1.0f, nil, false); + + break; + } + + case SHADOWTEX_EXPLOSION: + { + StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, pPosn, + fFrontX, fFrontY, fSideX, fSideY, + nIntensity, nRed, nGreen, nBlue, + 15.0f, false, 1.0f, nil, false); + + break; + } + + case SHADOWTEX_HELI: + { + StoreShadowToBeRendered(SHADOWTYPE_DARK, gpShadowHeliTex, pPosn, + fFrontX, fFrontY, fSideX, fSideY, + nIntensity, nRed, nGreen, nBlue, + 15.0f, false, 1.0f, nil, false); + + break; + } + + case SHADOWTEX_HEADLIGHTS: + { + StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, gpShadowHeadLightsTex, pPosn, + fFrontX, fFrontY, fSideX, fSideY, + nIntensity, nRed, nGreen, nBlue, + 15.0f, false, 1.0f, nil, false); + + break; + } + + case SHADOWTEX_BLOOD: + { + StoreShadowToBeRendered(SHADOWTYPE_DARK, gpBloodPoolTex, pPosn, + fFrontX, fFrontY, fSideX, fSideY, + nIntensity, nRed, 150, 0, + 15.0f, false, 1.0f, nil, false); + + break; + } + } + + //ASSERT(false); +} + +void +CShadows::StoreShadowToBeRendered(uint8 ShadowType, RwTexture *pTexture, CVector *pPosn, + float fFrontX, float fFrontY, float fSideX, float fSideY, + int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, + float fZDistance, bool bDrawOnWater, float fScale, CCutsceneShadow *pShadow, bool bDrawOnBuildings) +{ + ASSERT(pTexture != nil); + ASSERT(pPosn != nil); + + if ( ShadowsStoredToBeRendered < MAX_STOREDSHADOWS ) + { + asShadowsStored[ShadowsStoredToBeRendered].m_ShadowType = ShadowType; + asShadowsStored[ShadowsStoredToBeRendered].m_pTexture = pTexture; + asShadowsStored[ShadowsStoredToBeRendered].m_vecPos = *pPosn; + asShadowsStored[ShadowsStoredToBeRendered].m_vecFront.x = fFrontX; + asShadowsStored[ShadowsStoredToBeRendered].m_vecFront.y = fFrontY; + asShadowsStored[ShadowsStoredToBeRendered].m_vecSide.x = fSideX; + asShadowsStored[ShadowsStoredToBeRendered].m_vecSide.y = fSideY; + asShadowsStored[ShadowsStoredToBeRendered].m_nIntensity = nIntensity; + asShadowsStored[ShadowsStoredToBeRendered].m_nRed = nRed; + asShadowsStored[ShadowsStoredToBeRendered].m_nGreen = nGreen; + asShadowsStored[ShadowsStoredToBeRendered].m_nBlue = nBlue; + asShadowsStored[ShadowsStoredToBeRendered].m_fZDistance = fZDistance; + asShadowsStored[ShadowsStoredToBeRendered].m_nFlags.bDrawOnWater = bDrawOnWater; + asShadowsStored[ShadowsStoredToBeRendered].m_nFlags.bDrawOnBuildings = bDrawOnBuildings; + asShadowsStored[ShadowsStoredToBeRendered].m_fScale = fScale; + asShadowsStored[ShadowsStoredToBeRendered].m_pCutsceneShadow = pShadow; + + ShadowsStoredToBeRendered++; + } +} + + +void +CShadows::StoreShadowForVehicle(CVehicle *pCar, VEH_SHD_TYPE type) +{ + ASSERT(pCar != nil); + + if ( CTimeCycle::GetShadowStrength() != 0 ) + { + CVector CarPos = pCar->GetPosition(); + float fDistToCamSqr = (CarPos - TheCamera.GetPosition()).MagnitudeSqr2D(); + + if ( CCutsceneMgr::IsRunning() ) + fDistToCamSqr /= SQR(TheCamera.LODDistMultiplier) * 4.0f; + + float fDrawDistance; + switch ( type ) + { + case VEH_SHD_TYPE_SEAPLANE: + case VEH_SHD_TYPE_RCPLANE: + fDrawDistance = 144.0f; + break; + + case VEH_SHD_TYPE_HELI: + fDrawDistance = 144.0f; + break; + + default: + fDrawDistance = 18.0f; + break; + } + + if ( fDistToCamSqr < SQR(fDrawDistance) ) + { + float fDistToCam = Sqrt(fDistToCamSqr); + + //fDistToCam == 0 -> 4 + //fDistToCam == fDrawDistance -> 0 + float fMult = 1.0f - (fDistToCam - (fDrawDistance*(1.0f-(1.0f/4.0f)))) / (fDrawDistance*(1.0f/4.0f)); + + int32 nColorStrength; + + if ( fDistToCam >= (fDrawDistance*(1.0f-(1.0f/4.0f))) ) + nColorStrength = (int32)(fMult * CTimeCycle::GetShadowStrength()); + else + nColorStrength = CTimeCycle::GetShadowStrength(); + + + float fVehicleHeight = pCar->GetColModel()->boundingBox.GetSize().y; + float fVehicleWidth = pCar->GetColModel()->boundingBox.GetSize().x; + + float size = 1.0f; + + switch ( pCar->GetModelIndex() ) + { + case MI_PIZZABOY: + case MI_PCJ600: + case MI_FAGGIO: + { + fVehicleHeight *= 1.2f; + size = 0.05f; + break; + } + + case MI_ANGEL: + case MI_FREEWAY: + case MI_SANCHEZ: + { + fVehicleHeight *= 1.5f; + size *= 0.03f; + break; + } + + case MI_HUNTER: + case MI_SEASPAR: + case MI_SPARROW: + case MI_MAVERICK: + case MI_VCNMAV: + case MI_POLMAV: + { + fVehicleWidth *= 3.0f; + fVehicleHeight *= 1.4f; + size *= 0.5f; + break; + } + + case MI_RCGOBLIN: + case MI_RCRAIDER: + { + fVehicleHeight *= 1.5f; + fVehicleWidth *= 2.0f; + size *= 0.2f; + break; + } + + case MI_DODO: + { + fVehicleHeight *= 0.9f; + fVehicleWidth *= 0.4f; + break; + } + } + + + CarPos.x -= pCar->GetForward().x * (((fVehicleHeight/2) - pCar->GetColModel()->boundingBox.max.y)*size); + CarPos.y -= pCar->GetForward().y * (((fVehicleHeight/2) - pCar->GetColModel()->boundingBox.max.y)*size); + + RwTexture *tex = gpShadowCarTex; + switch ( type ) + { + case VEH_SHD_TYPE_BIKE: + { + float wheelZ = Abs(((CBike*)pCar)->m_fLeanLRAngle); + float mul = 5.092958f * wheelZ + 1.0f; + if (pCar->GetStatus() == STATUS_PHYSICS) + { + float z = pCar->GetRight().z; + if (z > 0.6f) + mul += 4.0f * z; + } + fVehicleWidth *= mul; + tex = gpShadowBikeTex; + break; + } + + case VEH_SHD_TYPE_HELI: + tex = gpShadowHeliTex; + break; + + case VEH_SHD_TYPE_SEAPLANE: + nColorStrength = CTimeCycle::GetShadowStrength(); + tex = gpShadowBaronTex; + break; + + case VEH_SHD_TYPE_RCPLANE: + tex = gpShadowBaronTex; + fVehicleHeight *= 1.5f; + fVehicleWidth *= 2.2f; + break; + + case VEH_SHD_TYPE_CAR: + tex = gpShadowCarTex; + break; + } + + float frontx = pCar->GetForward().x; + float fronty = pCar->GetForward().y; + float sidex = pCar->GetRight().x; + float sidey = pCar->GetRight().y; + + switch ( type ) + { + case VEH_SHD_TYPE_BIKE: + if ( Abs(pCar->GetRight().z) > 0.6f ) + { + sidex = pCar->GetUp().x; + sidey = pCar->GetUp().y; + } + break; + + case VEH_SHD_TYPE_HELI: + if ( Abs(pCar->GetRight().z) > 0.57f ) + { + sidex = pCar->GetUp().x; + sidey = pCar->GetUp().y; + } + if ( Abs(pCar->GetForward().z) > 0.57f ) + { + frontx = pCar->GetUp().x; + fronty = pCar->GetUp().y; + } + break; + } + + bool bDrawOnBuildings = false; + if ( pCar->GetModelIndex() == MI_RCBANDIT + || pCar->GetModelIndex() == MI_RCBARON + || pCar->GetModelIndex() == MI_RCRAIDER + || pCar->GetModelIndex() == MI_RCGOBLIN + || pCar == FindPlayerVehicle() ) + { + bDrawOnBuildings = true; + } + + if ( pCar->m_vecMoveSpeed.Magnitude() * CTimeStep::ms_fTimeStep > 0.1f || bDrawOnBuildings ) + { + if ( pCar->GetUp().z > 0.0f ) + { + StoreShadowToBeRendered(SHADOWTYPE_DARK, tex, &CarPos, + frontx * (fVehicleHeight / 2), + fronty * (fVehicleHeight / 2), + sidex * (fVehicleWidth / 2), + sidey * (fVehicleWidth / 2), + nColorStrength, nColorStrength, nColorStrength, nColorStrength, + 4.5f, false, 1.0f, nil, bDrawOnBuildings); + } + else + { + StoreShadowToBeRendered(SHADOWTYPE_DARK, tex, &CarPos, + frontx * (fVehicleHeight / 2), + fronty * (fVehicleHeight / 2), + -sidex * (fVehicleWidth / 2), + -sidey * (fVehicleWidth / 2), + nColorStrength, nColorStrength, nColorStrength, nColorStrength, + 4.5f, false, 1.0f, nil, bDrawOnBuildings); + } + } + else + { + if ( pCar->GetUp().z > 0.0f ) + { + StoreStaticShadow((uintptr)pCar + 1, SHADOWTYPE_DARK, tex, &CarPos, + frontx * (fVehicleHeight / 2), + fronty * (fVehicleHeight / 2), + sidex * (fVehicleWidth / 2), + sidey * (fVehicleWidth / 2), + nColorStrength, nColorStrength, nColorStrength, nColorStrength, + 4.5f, 1.0f, 0.0f, false, 0.1f); + } + else + { + StoreStaticShadow((uintptr)pCar + 1, SHADOWTYPE_DARK, tex, &CarPos, + frontx * (fVehicleHeight / 2), + fronty * (fVehicleHeight / 2), + -sidex * (fVehicleWidth / 2), + -sidey * (fVehicleWidth / 2), + nColorStrength, nColorStrength, nColorStrength, nColorStrength, + 4.5f, 1.0f, 0.0f, false, 0.1f); + } + } + } + } +} + +void +CShadows::StoreCarLightShadow(CVehicle *pCar, int32 nID, RwTexture *pTexture, CVector *pPosn, + float fFrontX, float fFrontY, float fSideX, float fSideY, uint8 nRed, uint8 nGreen, uint8 nBlue, + float fMaxViewAngle) +{ + ASSERT(pCar != nil); + ASSERT(pPosn != nil); + + float fDistToCamSqr = (*pPosn - TheCamera.GetPosition()).MagnitudeSqr2D(); + + bool bSpecialCam = TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN + || TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED + || CCutsceneMgr::IsRunning(); + + float fDrawDistance = 27.0f; + + if ( fDistToCamSqr < SQR(fDrawDistance) || bSpecialCam ) + { + if ( bSpecialCam || DotProduct2D(CVector2D(TheCamera.CamFrontXNorm, TheCamera.CamFrontYNorm), + *pPosn - TheCamera.GetPosition() ) > -fMaxViewAngle ) + { + float fDistToCam = Sqrt(fDistToCamSqr); + + if ( fDistToCam >= (fDrawDistance*(1.0f-(1.0f/4.0f))) && !bSpecialCam ) // BUG? must be 3.0? + { + //fDistToCam == 0 -> 3 + //fDistToCam == fDrawDistance -> 0 + float fMult = 1.0f - (3.0f / fDrawDistance) * (fDistToCam - (fDrawDistance*(1.0f-(1.0f/3.0f))) ); + + nRed = (int32)(nRed * fMult); + nGreen = (int32)(nGreen * fMult); + nBlue = (int32)(nBlue * fMult); + } + + if ( pCar->m_vecMoveSpeed.Magnitude() * CTimeStep::ms_fTimeStep > 0.4f || pCar == FindPlayerVehicle() ) + { + StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, pTexture, pPosn, + fFrontX, fFrontY, + fSideX, fSideY, + 128, nRed, nGreen, nBlue, + 6.0f, false, 1.0f, + nil, pCar == FindPlayerVehicle()); + } + else + { + StoreStaticShadow((uintptr)pCar + nID, SHADOWTYPE_ADDITIVE, pTexture, pPosn, + fFrontX, fFrontY, + fSideX, fSideY, + 128, nRed, nGreen, nBlue, + 6.0f, 1.0f, 27.0f, + false, 0.4f); + } + } + } +} + + +#ifdef USE_CUTSCENE_SHADOW_FOR_PED +void +StoreShadowForCutscenePedObject(CPed *pObject, float fDisplacementX, float fDisplacementY, + float fFrontX, float fFrontY, float fSideX, float fSideY) +{ + ASSERT(pObject != nil); + + CCutsceneShadow *shadow = pObject->m_pRTShadow; + + if ( shadow == nil ) + return; + + if ( !shadow->IsInitialized() ) + return; + + CVector pos = pObject->GetPosition(); + + float fDistToCamSqr = (pos - TheCamera.GetPosition()).MagnitudeSqr2D(); + + float fDrawDistance = 100.0f; + + if ( fDistToCamSqr < SQR(fDrawDistance*0.5f) ) + { + if ( (CEntity*)pObject == FindPlayerPed() || TheCamera.IsSphereVisible(pos, 2.0f) ) + { + float fDistToCam = Sqrt(fDistToCamSqr); + + float fMult = 1.0f - (4.0f / fDrawDistance) * (fDistToCam - (fDrawDistance*(1.0f/4.0f))); + int32 nColorStrength; + + if ( fDistToCam >= (fDrawDistance*(1.0f/4.0f)) ) + nColorStrength = (int32)(CTimeCycle::GetShadowStrength() * fMult); + else + nColorStrength = CTimeCycle::GetShadowStrength(); + + int32 color = int32(nColorStrength * 0.8f); + + pos.x += fDisplacementX; + pos.y += fDisplacementY; + + RwTexture *texture = shadow->GetShadowRwTexture(); + ASSERT(texture); + RwRGBA bordercolor = {0, 0, 0, 0}; + shadow->DrawBorderAroundTexture(bordercolor); + + pos.x -= fDisplacementX; + pos.y -= fDisplacementY; + + float angleY = 360.0f - RADTODEG((CClock::ms_nGameClockMinutes + +60*CClock::ms_nGameClockHours+CClock::ms_nGameClockSeconds/60)*(HALFPI/360.0f)); + + RwFrame *frame = shadow->SetLightProperties(angleY, -85.0f, true); + ASSERT(frame); + CVector at(RwFrameGetMatrix(frame)->at); + at.Normalise(); + + CShadows::CalcPedShadowValues(at, &fFrontX, &fFrontY, &fSideX, &fSideY, &fDisplacementX, &fDisplacementY); + + pos.x -= 2.5f * fDisplacementX; + pos.y -= 2.5f * fDisplacementY; + + CShadows::StoreShadowToBeRendered(SHADOWTYPE_INVCOLOR, texture, &pos, + fFrontX * 1.5f, fFrontY * 1.5f, + fSideX * 1.5f, fSideY * 1.5f, + color, color, color, color, + 4.0f, false, 1.0f, shadow, false); + } + } +} +#endif + + +void +CShadows::StoreShadowForPed(CPed *pPed, float fDisplacementX, float fDisplacementY, + float fFrontX, float fFrontY, float fSideX, float fSideY) +{ + ASSERT(pPed != nil); + + if ( pPed->bIsVisible ) + { + if ( !(pPed->bInVehicle && pPed->m_nPedState != PED_DRAG_FROM_CAR && pPed->m_nPedState != PED_EXIT_CAR) ) + { + if ( CTimeCycle::GetShadowStrength() != 0 ) + { +#ifdef USE_CUTSCENE_SHADOW_FOR_PED + CCutsceneShadow *pShadow = pPed->m_pRTShadow; + + if (pShadow) + { + if (pShadow->IsInitialized()) + pShadow->UpdateForCutscene(); + ::StoreShadowForCutscenePedObject(pPed, fDisplacementX, fDisplacementY, fFrontX, fFrontY, fSideX, fSideY); + } + + return; +#endif + + StoreShadowForPedObject(pPed, + fDisplacementX, fDisplacementY, + fFrontX, fFrontY, + fSideX, fSideY); + } + } + } +} + +void +CShadows::StoreShadowForPedObject(CEntity *pPedObject, float fDisplacementX, float fDisplacementY, + float fFrontX, float fFrontY, float fSideX, float fSideY) +{ + ASSERT(pPedObject != nil); + + CVector PedPos = pPedObject->GetPosition(); + + float fDistToCamSqr = (PedPos - TheCamera.GetPosition()).MagnitudeSqr2D(); + + float fDrawDistance = 26.0f; + + if ( fDistToCamSqr < SQR(fDrawDistance*0.5f) ) + { + if ( pPedObject == FindPlayerPed() || TheCamera.IsSphereVisible(PedPos, 2.0f) != false ) + { + float fDistToCam = Sqrt(fDistToCamSqr); + + //fDistToCam == 0 -> 2 + //fDistToCam == fDrawDistance -> -2 + float fMult = 1.0f - (4.0f / fDrawDistance) * (fDistToCam - (fDrawDistance*(1.0f/4.0f))); // BUG ? negative + int32 nColorStrength; + + if ( fDistToCam >= (fDrawDistance*(1.0f/4.0f)) ) // BUG ? negative + nColorStrength = (int32)(CTimeCycle::GetShadowStrength() * fMult); + else + nColorStrength = CTimeCycle::GetShadowStrength(); + + PedPos.x += fDisplacementX; + PedPos.y += fDisplacementY; + + StoreShadowToBeRendered(SHADOWTYPE_DARK, gpShadowPedTex, &PedPos, + fFrontX, fFrontY, + fSideX, fSideY, + nColorStrength, nColorStrength, nColorStrength, nColorStrength, + 4.0f, false, 1.0f, nil, pPedObject == FindPlayerPed()); + } + } +} + + +void +CShadows::StoreShadowForCutscenePedObject(CCutsceneObject *pObject, float fDisplacementX, float fDisplacementY, + float fFrontX, float fFrontY, float fSideX, float fSideY) +{ +#ifdef DISABLE_CUTSCENE_SHADOWS + return; +#endif + ASSERT(pObject != nil); + + CCutsceneShadow *shadow = pObject->m_pShadow; + + if ( shadow == nil ) + return; + + if ( !shadow->IsInitialized() ) + return; + + CVector pos = pObject->GetPosition(); + + float fDistToCamSqr = (pos - TheCamera.GetPosition()).MagnitudeSqr2D(); + + float fDrawDistance = 100.0f; + + if ( fDistToCamSqr < SQR(fDrawDistance*0.5f) ) + { + if ( (CEntity*)pObject == FindPlayerPed() || TheCamera.IsSphereVisible(pos, 2.0f) ) + { + float fDistToCam = Sqrt(fDistToCamSqr); + + float fMult = 1.0f - (4.0f / fDrawDistance) * (fDistToCam - (fDrawDistance*(1.0f/4.0f))); + int32 nColorStrength; + + if ( fDistToCam >= (fDrawDistance*(1.0f/4.0f)) ) + nColorStrength = (int32)(CTimeCycle::GetShadowStrength() * fMult); + else + nColorStrength = CTimeCycle::GetShadowStrength(); + + int32 color = int32(nColorStrength * 0.8f); + + pos.x += fDisplacementX; + pos.y += fDisplacementY; + + RwTexture *texture = shadow->GetShadowRwTexture(); + ASSERT(texture); + RwRGBA bordercolor = {0, 0, 0, 0}; + shadow->DrawBorderAroundTexture(bordercolor); + + pos.x -= fDisplacementX; + pos.y -= fDisplacementY; + + float angleY = 360.0f - RADTODEG((CClock::ms_nGameClockMinutes+60* + CClock::ms_nGameClockHours+CClock::ms_nGameClockSeconds/60)*(HALFPI/360.0f)); + + RwFrame *frame = shadow->SetLightProperties(angleY, -85.0f, true); + ASSERT(frame); + CVector at(RwFrameGetMatrix(frame)->at); + at.Normalise(); + + CalcPedShadowValues(at, &fFrontX, &fFrontY, &fSideX, &fSideY, &fDisplacementX, &fDisplacementY); + + pos.x -= 2.5f * fDisplacementX; + pos.y -= 2.5f * fDisplacementY; + + StoreShadowToBeRendered(SHADOWTYPE_INVCOLOR, texture, &pos, + fFrontX * 1.5f, fFrontY * 1.5f, + fSideX * 1.5f, fSideY * 1.5f, + color, color, color, color, + 4.0f, false, 1.0f, shadow, false); + } + } +} + +void +CShadows::StoreShadowForTree(CEntity *pTree) +{ + ASSERT(pTree != nil); +} + + +void +CShadows::StoreShadowForPole(CEntity *pPole, float fOffsetX, float fOffsetY, float fOffsetZ, + float fPoleHeight, float fPoleWidth, uint32 nID) +{ + ASSERT(pPole != nil); + + if ( CTimeCycle::GetShadowStrength() != 0 ) + { + if ( pPole->GetUp().z < 0.5f ) + return; + + CVector PolePos = pPole->GetPosition(); + + PolePos.x += fOffsetX * pPole->GetRight().x + fOffsetY * pPole->GetForward().x; + PolePos.y += fOffsetX * pPole->GetRight().y + fOffsetY * pPole->GetForward().y; + PolePos.z += fOffsetZ; + + PolePos.x += -CTimeCycle::GetSunDirection().x * (fPoleHeight / 2); + PolePos.y += -CTimeCycle::GetSunDirection().y * (fPoleHeight / 2); + + StoreStaticShadow((uintptr)pPole + nID + _TODOCONST(51), SHADOWTYPE_DARK, gpPostShadowTex, &PolePos, + -CTimeCycle::GetSunDirection().x * (fPoleHeight / 2), + -CTimeCycle::GetSunDirection().y * (fPoleHeight / 2), + CTimeCycle::GetShadowSideX() * fPoleWidth, + CTimeCycle::GetShadowSideY() * fPoleWidth, + 2 * (int32)((pPole->GetUp().z - 0.5f) * CTimeCycle::GetShadowStrength() * 2.0f) / 3, + 0, 0, 0, + 15.0f, 1.0f, 40.0f, false, 0.0f); + } +} + +void +CShadows::SetRenderModeForShadowType(uint8 ShadowType) +{ + switch ( ShadowType ) + { + case SHADOWTYPE_DARK: + { + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); + break; + } + + case SHADOWTYPE_ADDITIVE: + { + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDONE); + break; + } + + case SHADOWTYPE_INVCOLOR: + { + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDZERO); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCCOLOR); + break; + } + } +} + + +void +CShadows::RenderStoredShadows(void) +{ + PUSH_RENDERGROUP("CShadows::RenderStoredShadows"); + + RenderBuffer::ClearRenderBuffer(); + + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void *)rwTEXTUREADDRESSCLAMP); + + + for ( int32 i = 0; i < ShadowsStoredToBeRendered; i++ ) + asShadowsStored[i].m_nFlags.bRendered = false; + + + for ( int32 i = 0; i < ShadowsStoredToBeRendered; i++ ) + { + if ( !asShadowsStored[i].m_nFlags.bRendered ) + { + SetRenderModeForShadowType(asShadowsStored[i].m_ShadowType); + + ASSERT(asShadowsStored[i].m_pTexture != nil); + + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(asShadowsStored[i].m_pTexture)); + + for ( int32 j = i; j < ShadowsStoredToBeRendered; j++ ) + { + if ( asShadowsStored[i].m_ShadowType == asShadowsStored[j].m_ShadowType + && asShadowsStored[i].m_pTexture == asShadowsStored[j].m_pTexture ) + { + float fWidth = Abs(asShadowsStored[j].m_vecFront.x) + Abs(asShadowsStored[j].m_vecSide.x); + float fHeight = Abs(asShadowsStored[j].m_vecFront.y) + Abs(asShadowsStored[j].m_vecSide.y); + + CVector shadowPos = asShadowsStored[j].m_vecPos; + + float fStartX = shadowPos.x - fWidth; + float fEndX = shadowPos.x + fWidth; + float fStartY = shadowPos.y - fHeight; + float fEndY = shadowPos.y + fHeight; + + int32 nStartX = Max(CWorld::GetSectorIndexX(fStartX), 0); + int32 nStartY = Max(CWorld::GetSectorIndexY(fStartY), 0); + int32 nEndX = Min(CWorld::GetSectorIndexX(fEndX), NUMSECTORS_X-1); + int32 nEndY = Min(CWorld::GetSectorIndexY(fEndY), NUMSECTORS_Y-1); + + CWorld::AdvanceCurrentScanCode(); + + for ( int32 y = nStartY; y <= nEndY; y++ ) + { + for ( int32 x = nStartX; x <= nEndX; x++ ) + { + CSector *pCurSector = CWorld::GetSector(x, y); + + ASSERT(pCurSector != nil); + + if ( asShadowsStored[j].m_pCutsceneShadow ) + { + CastCutsceneShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS], + fStartX, fStartY, + fEndX, fEndY, + &shadowPos, + asShadowsStored[j].m_vecFront.x, + asShadowsStored[j].m_vecFront.y, + asShadowsStored[j].m_vecSide.x, + asShadowsStored[j].m_vecSide.y, + asShadowsStored[j].m_nIntensity, + asShadowsStored[j].m_nRed, + asShadowsStored[j].m_nGreen, + asShadowsStored[j].m_nBlue, + asShadowsStored[j].m_fZDistance, + asShadowsStored[j].m_fScale, + nil, + asShadowsStored[j].m_pCutsceneShadow); + + CastCutsceneShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], + fStartX, fStartY, + fEndX, fEndY, + &shadowPos, + asShadowsStored[j].m_vecFront.x, + asShadowsStored[j].m_vecFront.y, + asShadowsStored[j].m_vecSide.x, + asShadowsStored[j].m_vecSide.y, + asShadowsStored[j].m_nIntensity, + asShadowsStored[j].m_nRed, + asShadowsStored[j].m_nGreen, + asShadowsStored[j].m_nBlue, + asShadowsStored[j].m_fZDistance, + asShadowsStored[j].m_fScale, + nil, + asShadowsStored[j].m_pCutsceneShadow); + } + else if ( asShadowsStored[j].m_nFlags.bDrawOnBuildings ) + { + CastPlayerShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS], + fStartX, fStartY, + fEndX, fEndY, + &shadowPos, + asShadowsStored[j].m_vecFront.x, + asShadowsStored[j].m_vecFront.y, + asShadowsStored[j].m_vecSide.x, + asShadowsStored[j].m_vecSide.y, + asShadowsStored[j].m_nIntensity, + asShadowsStored[j].m_nRed, + asShadowsStored[j].m_nGreen, + asShadowsStored[j].m_nBlue, + asShadowsStored[j].m_fZDistance, + asShadowsStored[j].m_fScale, + nil); + + CastPlayerShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], + fStartX, fStartY, + fEndX, fEndY, + &shadowPos, + asShadowsStored[j].m_vecFront.x, + asShadowsStored[j].m_vecFront.y, + asShadowsStored[j].m_vecSide.x, + asShadowsStored[j].m_vecSide.y, + asShadowsStored[j].m_nIntensity, + asShadowsStored[j].m_nRed, + asShadowsStored[j].m_nGreen, + asShadowsStored[j].m_nBlue, + asShadowsStored[j].m_fZDistance, + asShadowsStored[j].m_fScale, + nil); + } + else + { + CastShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS], + fStartX, fStartY, + fEndX, fEndY, + &shadowPos, + asShadowsStored[j].m_vecFront.x, + asShadowsStored[j].m_vecFront.y, + asShadowsStored[j].m_vecSide.x, + asShadowsStored[j].m_vecSide.y, + asShadowsStored[j].m_nIntensity, + asShadowsStored[j].m_nRed, + asShadowsStored[j].m_nGreen, + asShadowsStored[j].m_nBlue, + asShadowsStored[j].m_fZDistance, + asShadowsStored[j].m_fScale, + nil); + + CastShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], + fStartX, fStartY, + fEndX, fEndY, + &shadowPos, + asShadowsStored[j].m_vecFront.x, + asShadowsStored[j].m_vecFront.y, + asShadowsStored[j].m_vecSide.x, + asShadowsStored[j].m_vecSide.y, + asShadowsStored[j].m_nIntensity, + asShadowsStored[j].m_nRed, + asShadowsStored[j].m_nGreen, + asShadowsStored[j].m_nBlue, + asShadowsStored[j].m_fZDistance, + asShadowsStored[j].m_fScale, + nil); + } + } + } + + asShadowsStored[j].m_nFlags.bRendered = true; + } + } + + RenderBuffer::RenderStuffInBuffer(); + } + } + + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void *)rwTEXTUREADDRESSWRAP); + + ShadowsStoredToBeRendered = 0; + + POP_RENDERGROUP(); +} + + +void +CShadows::RenderStaticShadows(void) +{ + PUSH_RENDERGROUP("CShadows::RenderStaticShadows"); + + RenderBuffer::ClearRenderBuffer(); + + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)FALSE); + + SetAlphaTest(0); + + for ( int32 i = 0; i < MAX_STATICSHADOWS; i++ ) + aStaticShadows[i].m_bRendered = false; + + for ( int32 i = 0; i < MAX_STATICSHADOWS; i++ ) + { + if ( aStaticShadows[i].m_pPolyBunch && !aStaticShadows[i].m_bRendered ) + { + SetRenderModeForShadowType(aStaticShadows[i].m_nType); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(aStaticShadows[i].m_pTexture)); + + // optimization trick, render all shadows with same renderstate and texture + for ( int32 j = i; j < MAX_STATICSHADOWS; j++ ) + { + if ( aStaticShadows[j].m_pPolyBunch != nil + && aStaticShadows[i].m_nType == aStaticShadows[j].m_nType + && aStaticShadows[i].m_pTexture == aStaticShadows[j].m_pTexture ) + { + for ( CPolyBunch *bunch = aStaticShadows[j].m_pPolyBunch; bunch != nil; bunch = bunch->m_pNext ) + { + RwImVertexIndex *pIndexes; + RwIm3DVertex *pVerts; + + RenderBuffer::StartStoring(3 * (bunch->m_nNumVerts - 2), bunch->m_nNumVerts, &pIndexes, &pVerts); + + ASSERT(pIndexes != nil); + ASSERT(pVerts != nil); + + for ( int32 k = 0; k < bunch->m_nNumVerts; k++ ) + { + RwIm3DVertexSetRGBA(&pVerts[k], + aStaticShadows[j].m_nRed, + aStaticShadows[j].m_nGreen, + aStaticShadows[j].m_nBlue, + (int32)(aStaticShadows[j].m_nIntensity * (1.0f - CWeather::Foggyness * 0.5f))); + + RwIm3DVertexSetU (&pVerts[k], bunch->m_aU[k] / 200.0f); + RwIm3DVertexSetV (&pVerts[k], bunch->m_aV[k] / 200.0f); + RwIm3DVertexSetPos(&pVerts[k], bunch->m_aVerts[k].x, bunch->m_aVerts[k].y, bunch->m_aVerts[k].z + 0.03f); + } + + for ( int32 k = 0; k < 3 * (bunch->m_nNumVerts - 2); k++ ) + pIndexes[k] = ShadowIndexList[k]; + + RenderBuffer::StopStoring(); + } + + aStaticShadows[j].m_bRendered = true; + } + } + + RenderBuffer::RenderStuffInBuffer(); + } + } + RestoreAlphaTest(); + + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE); + + POP_RENDERGROUP(); +} + + +void +CShadows::GeneratePolysForStaticShadow(int16 nStaticShadowID) +{ + float fWidth = Abs(aStaticShadows[nStaticShadowID].m_vecFront.x) + Abs(aStaticShadows[nStaticShadowID].m_vecSide.x); + float fHeight = Abs(aStaticShadows[nStaticShadowID].m_vecFront.y) + Abs(aStaticShadows[nStaticShadowID].m_vecSide.y); + + CVector shadowPos = aStaticShadows[nStaticShadowID].m_vecPosn; + + float fStartX = shadowPos.x - fWidth; + float fEndX = shadowPos.x + fWidth; + float fStartY = shadowPos.y - fHeight; + float fEndY = shadowPos.y + fHeight; + + int32 nStartX = Max(CWorld::GetSectorIndexX(fStartX), 0); + int32 nStartY = Max(CWorld::GetSectorIndexY(fStartY), 0); + int32 nEndX = Min(CWorld::GetSectorIndexX(fEndX), NUMSECTORS_X-1); + int32 nEndY = Min(CWorld::GetSectorIndexY(fEndY), NUMSECTORS_Y-1); + + CWorld::AdvanceCurrentScanCode(); + + for ( int32 y = nStartY; y <= nEndY; y++ ) + { + for ( int32 x = nStartX; x <= nEndX; x++ ) + { + CSector *pCurSector = CWorld::GetSector(x, y); + + ASSERT(pCurSector != nil); + + CastShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS], + fStartX, fStartY, + fEndX, fEndY, + &shadowPos, + aStaticShadows[nStaticShadowID].m_vecFront.x, + aStaticShadows[nStaticShadowID].m_vecFront.y, + aStaticShadows[nStaticShadowID].m_vecSide.x, + aStaticShadows[nStaticShadowID].m_vecSide.y, + 0, 0, 0, 0, + aStaticShadows[nStaticShadowID].m_fZDistance, + aStaticShadows[nStaticShadowID].m_fScale, + &aStaticShadows[nStaticShadowID].m_pPolyBunch); + + CastShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], + fStartX, fStartY, + fEndX, fEndY, + &shadowPos, + aStaticShadows[nStaticShadowID].m_vecFront.x, + aStaticShadows[nStaticShadowID].m_vecFront.y, + aStaticShadows[nStaticShadowID].m_vecSide.x, + aStaticShadows[nStaticShadowID].m_vecSide.y, + 0, 0, 0, 0, + aStaticShadows[nStaticShadowID].m_fZDistance, + aStaticShadows[nStaticShadowID].m_fScale, + &aStaticShadows[nStaticShadowID].m_pPolyBunch); + } + } +} + + +void +CShadows::CastShadowSectorList(CPtrList &PtrList, float fStartX, float fStartY, float fEndX, float fEndY, CVector *pPosn, + float fFrontX, float fFrontY, float fSideX, float fSideY, + int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, + float fZDistance, float fScale, CPolyBunch **ppPolyBunch) +{ + ASSERT(pPosn != nil); + + CPtrNode *pNode = PtrList.first; + + CRect Bound; + + while ( pNode != nil ) + { + CEntity *pEntity = (CEntity *)pNode->item; + uint16 nScanCode = pEntity->m_scanCode; + pNode = pNode->next; + + ASSERT( pEntity != nil ); + + if ( nScanCode != CWorld::GetCurrentScanCode() ) + { + pEntity->m_scanCode = CWorld::GetCurrentScanCode(); + + if ( pEntity->bUsesCollision && !pEntity->bDontCastShadowsOn) + { + if ( IsAreaVisible(pEntity->m_area) ) + { + Bound = pEntity->GetBoundRect(); + + if ( fStartX < Bound.right + && fEndX > Bound.left + && fStartY < Bound.bottom + && fEndY > Bound.top ) + { + if ( pPosn->z - fZDistance < pEntity->GetPosition().z + pEntity->GetColModel()->boundingBox.max.z + && pEntity->GetPosition().z + pEntity->GetColModel()->boundingBox.min.z < pPosn->z ) + { + CastShadowEntityXY(pEntity, + fStartX, fStartY, + fEndX, fEndY, + pPosn, + fFrontX, fFrontY, + fSideX, fSideY, + nIntensity, nRed, nGreen, nBlue, + fZDistance, fScale, ppPolyBunch); + } + } + } + } + } + } +} + + +void +CShadows::CastPlayerShadowSectorList(CPtrList &PtrList, float fStartX, float fStartY, float fEndX, float fEndY, CVector *pPosn, + float fFrontX, float fFrontY, float fSideX, float fSideY, + int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, + float fZDistance, float fScale, CPolyBunch **ppPolyBunch) +{ + ASSERT(pPosn != nil); + + CPtrNode *pNode = PtrList.first; + + CRect Bound; + + while ( pNode != nil ) + { + CEntity *pEntity = (CEntity *)pNode->item; + uint16 nScanCode = pEntity->m_scanCode; + pNode = pNode->next; + + ASSERT( pEntity != nil ); + + if ( nScanCode != CWorld::GetCurrentScanCode() ) + { + pEntity->m_scanCode = CWorld::GetCurrentScanCode(); + + if ( pEntity->bUsesCollision ) + { + if ( IsAreaVisible(pEntity->m_area) ) + { + Bound = pEntity->GetBoundRect(); + + if ( fStartX < Bound.right + && fEndX > Bound.left + && fStartY < Bound.bottom + && fEndY > Bound.top ) + { + if ( pPosn->z - fZDistance < pEntity->GetPosition().z + pEntity->GetColModel()->boundingBox.max.z + && pEntity->GetPosition().z + pEntity->GetColModel()->boundingBox.min.z < pPosn->z ) + { + CastShadowEntityXY(pEntity, + fStartX, fStartY, + fEndX, fEndY, + pPosn, + fFrontX, fFrontY, + fSideX, fSideY, + nIntensity, nRed, nGreen, nBlue, + fZDistance, fScale, ppPolyBunch); + } + } + } + } + } + } +} + + +void +CShadows::CastCutsceneShadowSectorList(CPtrList &PtrList, float fStartX, float fStartY, float fEndX, float fEndY, CVector *pPosn, + float fFrontX, float fFrontY, float fSideX, float fSideY, + int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, + float fZDistance, float fScale, CPolyBunch **ppPolyBunch, CCutsceneShadow *pShadow) +{ + ASSERT(pPosn != nil); + ASSERT(pShadow != nil); + + CPtrNode *pNode = PtrList.first; + + CRect Bound; + + while ( pNode != nil ) + { + CEntity *pEntity = (CEntity *)pNode->item; + uint16 nScanCode = pEntity->m_scanCode; + pNode = pNode->next; + + ASSERT( pEntity != nil ); + + if ( nScanCode != CWorld::GetCurrentScanCode() ) + { + pEntity->m_scanCode = CWorld::GetCurrentScanCode(); + + if ( pEntity->bUsesCollision ) + { + if ( IsAreaVisible(pEntity->m_area) ) + { + Bound = pEntity->GetBoundRect(); + + if ( fStartX < Bound.right + && fEndX > Bound.left + && fStartY < Bound.bottom + && fEndY > Bound.top ) + { + if ( pPosn->z - fZDistance < pEntity->GetPosition().z + pEntity->GetColModel()->boundingBox.max.z + && pEntity->GetPosition().z + pEntity->GetColModel()->boundingBox.min.z < pPosn->z ) + { + CastShadowEntityXYZ(pEntity, pPosn, + fFrontX, fFrontY, + fSideX, fSideY, + nIntensity, nRed, nGreen, nBlue, + fZDistance, fScale, ppPolyBunch, pShadow); + } + } + } + } + } + } +} + +void +CShadows::CastShadowEntityXY(CEntity *pEntity, float fStartX, float fStartY, float fEndX, float fEndY, CVector *pPosn, + float fFrontX, float fFrontY, float fSideX, float fSideY, + int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, + float fZDistance, float fScale, CPolyBunch **ppPolyBunch) +{ + ASSERT(pEntity != nil); + ASSERT(pPosn != nil); + + static CVector List [20]; + static CVector Texture[20]; + static CVector Points [4]; + + CColModel *pCol = pEntity->GetColModel(); + ASSERT(pCol != nil); + +#ifndef MASTER + if ( gbPrintShite ) + printf("MI:%d Triangles:%d Coors:%f %f BBoxXY:%f %f\n", + pEntity->GetModelIndex(), + pCol->numTriangles, + pEntity->GetPosition().x, + pEntity->GetPosition().y, + pCol->boundingBox.GetSize().x, + pCol->boundingBox.GetSize().y); +#endif + + CCollision::CalculateTrianglePlanes(pCol); + + float fFrontRight = DotProduct2D(CVector2D(fFrontX, fFrontY), pEntity->GetRight()); + float fFrontForward = DotProduct2D(CVector2D(fFrontX, fFrontY), pEntity->GetForward()); + float fSideRight = DotProduct2D(CVector2D(fSideX, fSideY), pEntity->GetRight()); + float fSideForward = DotProduct2D(CVector2D(fSideX, fSideY), pEntity->GetForward()); + float fLengthRight = DotProduct2D(*pPosn - pEntity->GetPosition(), pEntity->GetRight()); + float fLengthForward = DotProduct2D(*pPosn - pEntity->GetPosition(), pEntity->GetForward()); + + Points[0].x = (fLengthRight + fFrontRight ) - fSideRight; + Points[0].y = (fLengthForward + fFrontForward) - fSideForward; + + Points[1].x = fSideRight + (fLengthRight + fFrontRight); + Points[1].y = fSideForward + (fLengthForward + fFrontForward); + + Points[2].x = fSideRight + (fLengthRight - fFrontRight); + Points[2].y = fSideForward + (fLengthForward - fFrontForward); + + Points[3].x = (fLengthRight - fFrontRight) - fSideRight; + Points[3].y = (fLengthForward - fFrontForward) - fSideForward; + + float MinX = Min(Min(Points[0].x, Points[1].x), Min(Points[2].x, Points[3].x)); + float MaxX = Max(Max(Points[0].x, Points[1].x), Max(Points[2].x, Points[3].x)); + + float MinY = Min(Min(Points[0].y, Points[1].y), Min(Points[2].y, Points[3].y)); + float MaxY = Max(Max(Points[0].y, Points[1].y), Max(Points[2].y, Points[3].y)); + + float MaxZ = pPosn->z - pEntity->GetPosition().z; + float MinZ = MaxZ - fZDistance; + + for ( int32 i = 0; i < pCol->numTriangles; i++ ) + { + CColTrianglePlane *pColTriPlanes = pCol->trianglePlanes; + ASSERT(pColTriPlanes != nil); + + CVector normal; + pColTriPlanes[i].GetNormal(normal); + if ( Abs(normal.z) > 0.1f ) + { + CColTriangle *pColTri = pCol->triangles; + ASSERT(pColTri != nil); + + CVector PointA, PointB, PointC; + + pCol->GetTrianglePoint(PointA, pColTri[i].a); + pCol->GetTrianglePoint(PointB, pColTri[i].b); + pCol->GetTrianglePoint(PointC, pColTri[i].c); + + if ( (PointA.x > MinX || PointB.x > MinX || PointC.x > MinX) + && (PointA.x < MaxX || PointB.x < MaxX || PointC.x < MaxX) + && (PointA.y > MinY || PointB.y > MinY || PointC.y > MinY) + && (PointA.y < MaxY || PointB.y < MaxY || PointC.y < MaxY) + && (PointA.z < MaxZ || PointB.z < MaxZ || PointC.z < MaxZ) + && (PointA.z > MinZ || PointB.z > MinZ || PointC.z > MinZ) ) + + { + List[0].x = Points[0].x; + List[0].y = Points[0].y; + + List[1].x = Points[1].x; + List[1].y = Points[1].y; + + List[2].x = Points[2].x; + List[2].y = Points[2].y; + + List[3].x = Points[3].x; + List[3].y = Points[3].y; + + Texture[0].x = 0.0f; + Texture[0].y = 0.0f; + + Texture[1].x = 1.0f; + Texture[1].y = 0.0f; + + Texture[2].x = 1.0f; + Texture[2].y = 1.0f; + + Texture[3].x = 0.0f; + Texture[3].y = 1.0f; + + + CVector2D start; + CVector2D dist; + + int32 numVerts1 = 0; + int16 vertType1 = 0; + { + for ( int32 j = 0; j < 4; j++ ) + { + start = PointA; + dist = PointB - PointA; + + int32 in = j; + + float cp = CrossProduct2D(CVector2D(List[in]) - start, dist); + + if ( cp > 0.0f ) + { + switch ( vertType1 ) + { + case 0: + { + int32 out = numVerts1++ + 10; + + Texture[out].x = Texture[in].x; + Texture[out].y = Texture[in].y; + List[out].x = List[in].x; + List[out].y = List[in].y; + + break; + } + + case 1: + { + int32 out = numVerts1++ + 10; + + Texture[out].x = Texture[in].x; + Texture[out].y = Texture[in].y; + List[out].x = List[in].x; + List[out].y = List[in].y; + + break; + } + + case 2: + { + float prevcp = CrossProduct2D(CVector2D(List[in-1]) - start, dist); + + float Scale = Abs(prevcp) / (Abs(prevcp) + Abs(cp)); + float Compl = 1.0f - Scale; + + int32 out1 = numVerts1++ + 10; + int32 out2 = numVerts1++ + 10; + + Texture[out1].x = Compl*Texture[in-1].x + Scale*Texture[in].x; + Texture[out1].y = Compl*Texture[in-1].y + Scale*Texture[in].y; + List[out1].x = Compl*List[in-1].x + Scale*List[in].x; + List[out1].y = Compl*List[in-1].y + Scale*List[in].y; + + Texture[out2].x = Texture[in].x; + Texture[out2].y = Texture[in].y; + List[out2].x = List[in].x; + List[out2].y = List[in].y; + + break; + } + } + + vertType1 = 1; + } + else + { + switch ( vertType1 ) + { + case 1: + { + float prevcp = CrossProduct2D(CVector2D(List[in-1]) - start, dist); + + float Scale = Abs(prevcp) / (Abs(prevcp) + Abs(cp)); + float Compl = 1.0f - Scale; + + int32 out = numVerts1++ + 10; + + Texture[out].x = Compl*Texture[in-1].x + Scale*Texture[in].x; + Texture[out].y = Compl*Texture[in-1].y + Scale*Texture[in].y; + List[out].x = Compl*List[in-1].x + Scale*List[in].x; + List[out].y = Compl*List[in-1].y + Scale*List[in].y; + + break; + } + } + + vertType1 = 2; + } + } + + float cp1 = CrossProduct2D(CVector2D(List[0]) - start, dist); + if ( cp1 > 0.0f && vertType1 == 2 || cp1 <= 0.0f && vertType1 == 1 ) + { + float cp2 = CrossProduct2D(CVector2D(List[3]) - start, dist); + + float Scale = Abs(cp2) / (Abs(cp2) + Abs(cp1)); + float Compl = 1.0f - Scale; + + int32 out = numVerts1++ + 10; + + Texture[out].x = Compl*Texture[3].x + Scale*Texture[0].x; + Texture[out].y = Compl*Texture[3].y + Scale*Texture[0].y; + List[out].x = Compl*List[3].x + Scale*List[0].x; + List[out].y = Compl*List[3].y + Scale*List[0].y; + } + } + + int32 numVerts2 = 0; + int16 vertType2 = 0; + { + for ( int32 j = 0; j < numVerts1; j++ ) + { + start = PointB; + dist = PointC - PointB; + + int32 in = j + 10; + float cp = CrossProduct2D(CVector2D(List[in]) - start, dist); + + if ( cp > 0.0f ) + { + switch ( vertType2 ) + { + case 0: + { + int32 out = numVerts2++; + + Texture[out].x = Texture[in].x; + Texture[out].y = Texture[in].y; + List[out].x = List[in].x; + List[out].y = List[in].y; + + break; + } + + case 1: + { + int32 out = numVerts2++; + + Texture[out].x = Texture[in].x; + Texture[out].y = Texture[in].y; + List[out].x = List[in].x; + List[out].y = List[in].y; + + break; + } + + case 2: + { + float prevcp = CrossProduct2D(CVector2D(List[in-1]) - start, dist); + + float Scale = Abs(prevcp) / (Abs(prevcp) + Abs(cp)); + float Compl = 1.0f - Scale; + + int32 out1 = numVerts2++; + int32 out2 = numVerts2++; + + Texture[out1].x = Compl*Texture[in-1].x + Scale*Texture[in].x; + Texture[out1].y = Compl*Texture[in-1].y + Scale*Texture[in].y; + List[out1].x = Compl*List[in-1].x + Scale*List[in].x; + List[out1].y = Compl*List[in-1].y + Scale*List[in].y; + + Texture[out2].x = Texture[in].x; + Texture[out2].y = Texture[in].y; + List[out2].x = List[in].x; + List[out2].y = List[in].y; + + break; + } + } + + vertType2 = 1; + } + else + { + switch ( vertType2 ) + { + case 1: + { + float prevcp = CrossProduct2D(CVector2D(List[in-1]) - start, dist); + + float Scale = Abs(prevcp) / (Abs(prevcp) + Abs(cp)); + float Compl = 1.0f - Scale; + + int32 out = numVerts2++; + + Texture[out].x = Compl*Texture[in-1].x + Scale*Texture[in].x; + Texture[out].y = Compl*Texture[in-1].y + Scale*Texture[in].y; + List[out].x = Compl*List[in-1].x + Scale*List[in].x; + List[out].y = Compl*List[in-1].y + Scale*List[in].y; + + break; + } + } + + vertType2 = 2; + } + } + + float cp1 = CrossProduct2D(CVector2D(List[10]) - start, dist); + if ( cp1 > 0.0f && vertType2 == 2 || cp1 <= 0.0f && vertType2 == 1 ) + { + int32 in = numVerts1 + 10; + + float cp2 = CrossProduct2D(CVector2D(List[in-1]) - start, dist); + + float Scale = Abs(cp2) / (Abs(cp2) + Abs(cp1)); + float Compl = 1.0f - Scale; + + int32 out = numVerts2++; + + Texture[out].x = Compl*Texture[in-1].x + Scale*Texture[10].x; + Texture[out].y = Compl*Texture[in-1].y + Scale*Texture[10].y; + List[out].x = Compl*List[in-1].x + Scale*List[10].x; + List[out].y = Compl*List[in-1].y + Scale*List[10].y; + } + } + + int32 numVerts3 = 0; + int16 vertType3 = 0; + { + for ( int32 j = 0; j < numVerts2; j++ ) + { + start = PointC; + dist = PointA - PointC; + + int32 in = j; + float cp = CrossProduct2D(CVector2D(List[in]) - start, dist); + + if ( cp > 0.0f ) + { + switch ( vertType3 ) + { + case 0: + { + int32 out = numVerts3++ + 10; + + Texture[out].x = Texture[in].x; + Texture[out].y = Texture[in].y; + List[out].x = List[in].x; + List[out].y = List[in].y; + + break; + } + + case 1: + { + int32 out = numVerts3++ + 10; + + Texture[out].x = Texture[in].x; + Texture[out].y = Texture[in].y; + List[out].x = List[in].x; + List[out].y = List[in].y; + + break; + } + + case 2: + { + float prevcp = CrossProduct2D(CVector2D(List[in-1]) - start, dist); + + float Scale = Abs(prevcp) / (Abs(prevcp) + Abs(cp)); + float Compl = 1.0f - Scale; + + int32 out1 = numVerts3++ + 10; + int32 out2 = numVerts3++ + 10; + + Texture[out1].x = Compl*Texture[in-1].x + Scale*Texture[in].x; + Texture[out1].y = Compl*Texture[in-1].y + Scale*Texture[in].y; + List[out1].x = Compl*List[in-1].x + Scale*List[in].x; + List[out1].y = Compl*List[in-1].y + Scale*List[in].y; + + Texture[out2].x = Texture[in].x; + Texture[out2].y = Texture[in].y; + List[out2].x = List[in].x; + List[out2].y = List[in].y; + + break; + } + } + + vertType3 = 1; + } + else + { + switch ( vertType3 ) + { + case 1: + { + float prevcp = CrossProduct2D(CVector2D(List[in-1]) - start, dist); + + float Scale = Abs(prevcp) / (Abs(prevcp) + Abs(cp)); + float Compl = 1.0f - Scale; + + int32 out = numVerts3++ + 10; + + Texture[out].x = Compl*Texture[in-1].x + Scale*Texture[in].x; + Texture[out].y = Compl*Texture[in-1].y + Scale*Texture[in].y; + List[out].x = Compl*List[in-1].x + Scale*List[in].x; + List[out].y = Compl*List[in-1].y + Scale*List[in].y; + + break; + } + } + + vertType3 = 2; + } + } + + float cp1 = CrossProduct2D(CVector2D(List[0]) - start, dist); + if ( cp1 > 0.0f && vertType3 == 2 || cp1 <= 0.0f && vertType3 == 1 ) + { + int32 in = numVerts2; + + float cp2 = CrossProduct2D(CVector2D(List[in-1]) - start, dist); + + float Scale = Abs(cp2) / (Abs(cp2) + Abs(cp1)); + float Compl = 1.0f - Scale; + + int32 out = numVerts3++ + 10; + + Texture[out].x = Compl*Texture[in-1].x + Scale*Texture[0].x; + Texture[out].y = Compl*Texture[in-1].y + Scale*Texture[0].y; + List[out].x = Compl*List[in-1].x + Scale*List[0].x; + List[out].y = Compl*List[in-1].y + Scale*List[0].y; + } + } + + if ( numVerts3 >= 3 ) + { + CVector norm; + + pColTriPlanes[i].GetNormal(norm); + + float dot = DotProduct(norm, PointA); + + for ( int32 j = 0; j < numVerts3; j++ ) + { + int32 idx = j + 10; + + List[idx].z = -(DotProduct2D(norm, List[idx]) - dot) / norm.z; + } + + for ( int32 j = 0; j < numVerts3; j++ ) + { + int32 idx = j + 10; + + CVector p = List[idx]; + + List[idx].x = p.y * pEntity->GetForward().x + p.x * pEntity->GetRight().x + pEntity->GetPosition().x; + List[idx].y = p.y * pEntity->GetForward().y + p.x * pEntity->GetRight().y + pEntity->GetPosition().y; + List[idx].z = p.z + pEntity->GetPosition().z; + } + + + if ( ppPolyBunch != nil ) + { + if ( pEmptyBunchList != nil ) + { + CPolyBunch *pBunch = pEmptyBunchList; + ASSERT(pBunch != nil); + pEmptyBunchList = pEmptyBunchList->m_pNext; + pBunch->m_pNext = *ppPolyBunch; + *ppPolyBunch = pBunch; + + pBunch->m_nNumVerts = numVerts3; + + for ( int32 j = 0; j < numVerts3; j++ ) + { + int32 in = j + 10; + + pBunch->m_aVerts[j] = List[in]; + + pBunch->m_aU[j] = (int32)(Texture[in].x * 200.0f); + pBunch->m_aV[j] = (int32)(Texture[in].y * 200.0f); + } + } + } + else + { + RwImVertexIndex *pIndexes; + RwIm3DVertex *pVerts; + + RenderBuffer::StartStoring(3 * (numVerts3 - 2), numVerts3, &pIndexes, &pVerts); + + ASSERT(pIndexes != nil); + ASSERT(pVerts != nil); + + + for ( int32 j = 0; j < numVerts3; j++ ) + { + int32 in = j + 10; + + RwIm3DVertexSetRGBA(&pVerts[j], nRed, nGreen, nBlue, nIntensity); + RwIm3DVertexSetU (&pVerts[j], Texture[in].x*fScale); + RwIm3DVertexSetV (&pVerts[j], Texture[in].y*fScale); + RwIm3DVertexSetPos (&pVerts[j], List[in].x, List[in].y, List[in].z + 0.03f); + } + + for ( int32 j = 0; j < 3*(numVerts3 - 2); j++ ) + pIndexes[j] = ShadowIndexList[j]; + + RenderBuffer::StopStoring(); + } + } + } + } + } +} + + +typedef struct _ProjectionParam +{ + RwV3d at; /* Camera at vector */ + RwMatrix invMatrix; /* Transforms to shadow camera space */ + RwUInt8 shadowValue; /* Shadow opacity value */ + RwBool fade; /* Shadow fades with distance */ + RwUInt32 numIm3DBatch; /* Number of buffer flushes */ + RwMatrix entityMatrix; +} +ProjectionParam; + +RwV3d *ShadowRenderTriangleCB(RwV3d *points, RwV3d *normal, ProjectionParam *param) +{ + RwV3d vIn[3]; + RwV3d vShad[3]; + + RwV3dTransformPoints(&vIn[0], points, 3, ¶m->entityMatrix); + + /* + * Reject backfacing triangles + * This reject the triangles parallel to the light as well + */ + if (RwV3dDotProduct(normal, ¶m->at) > 0.0f) + { + return points; + } + + RwV3dTransformPoints(&vShad[0], &vIn[0], 3, ¶m->invMatrix); + + /* + * Reject triangles behind the camera (z test). Note that any world + * triangles lying in front of the camera but before the object may + * have a shadow applied. To minimize such artefacts, this test could + * be modified to use a specific value rather than 0.0f, perhaps + * to reject triangles behind the center plane of the object. + * + * Reject triangles that lie entirely outside the shadow texture range + * (x,y test). + */ + if (((vShad[0].z < 0.0f) && (vShad[1].z < 0.0f) + && (vShad[2].z < 0.0f)) || ((vShad[0].x < 0.0f) + && (vShad[1].x < 0.0f) + && (vShad[2].x < 0.0f)) + || ((vShad[0].x > 1.0f) && (vShad[1].x > 1.0f) + && (vShad[2].x > 1.0f)) || ((vShad[0].y < 0.0f) + && (vShad[1].y < 0.0f) + && (vShad[2].y < 0.0f)) + || ((vShad[0].y > 1.0f) && (vShad[1].y > 1.0f) + && (vShad[2].y > 1.0f))) + { + return points; + } + + RwIm3DVertex *imv = nil; + RwImVertexIndex *imi = nil; + + RenderBuffer::StartStoring(3, 3, &imi, &imv); + + /* + * Set the immediate mode vertices for this triangle + */ + + RwIm3DVertexSetPos(imv, vIn[0].x, vIn[0].y, vIn[0].z); + RwIm3DVertexSetPos(imv + 1, vIn[1].x, vIn[1].y, vIn[1].z); + RwIm3DVertexSetPos(imv + 2, vIn[2].x, vIn[2].y, vIn[2].z); + + RwIm3DVertexSetU(imv, vShad[0].x); + RwIm3DVertexSetU(imv + 1, vShad[1].x); + RwIm3DVertexSetU(imv + 2, vShad[2].x); + + RwIm3DVertexSetV(imv, vShad[0].y); + RwIm3DVertexSetV(imv + 1, vShad[1].y); + RwIm3DVertexSetV(imv + 2, vShad[2].y); + + /* + * Do we fade out the shadow with distance? + */ + if (param->fade) + { + RwReal fadeVal; + RwUInt8 val; + + fadeVal = 1.0f - vShad[0].z * vShad[0].z; + val = + (fadeVal < + 0.0f) ? 0 : (RwUInt8) (fadeVal * param->shadowValue); + RwIm3DVertexSetRGBA(imv, val, val, val, val); + + fadeVal = 1.0f - vShad[1].z * vShad[1].z; + val = + (fadeVal < + 0.0f) ? 0 : (RwUInt8) (fadeVal * param->shadowValue); + RwIm3DVertexSetRGBA(imv + 1, val, val, val, val); + + fadeVal = 1.0f - vShad[2].z * vShad[2].z; + val = + (fadeVal < + 0.0f) ? 0 : (RwUInt8) (fadeVal * param->shadowValue); + RwIm3DVertexSetRGBA(imv + 2, val, val, val, val); + } + else + { + RwUInt8 val = param->shadowValue; + + RwIm3DVertexSetRGBA(imv, val, val, val, val); + RwIm3DVertexSetRGBA(imv + 1, val, val, val, val); + RwIm3DVertexSetRGBA(imv + 2, val, val, val, val); + } + + /* + * Update buffer position + */ + imi[0] = 0; + imi[1] = 1; + imi[2] = 2; + + RenderBuffer::StopStoring(); + + return points; +} + +void +CShadows::CastShadowEntityXYZ(CEntity *pEntity, CVector *pPosn, + float fFrontX, float fFrontY, float fSideX, float fSideY, + int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, + float fZDistance, float fScale, CPolyBunch **ppPolyBunch, CCutsceneShadow *pShadow) +{ + ASSERT(pEntity != nil); + ASSERT(pPosn != nil); + + if ( pShadow ) + { + ProjectionParam proj; + RwV3d scl; + RwV3d tr; + + CShadowCamera *shadow = pShadow->GetShadowCamera(); + CColModel *collision = pEntity->GetColModel(); + + CCollision::CalculateTrianglePlanes(collision); + + RwMatrix mat; + mat = *RwFrameGetMatrix(RwCameraGetFrame(shadow->GetRwCamera())); + + RwV3d Xaxis = { 1.0f, 0.0f, 0.0f }; + + RwMatrixRotate(&mat, &Xaxis, -45.0f, rwCOMBINEPRECONCAT); + + proj.at = mat.at; + pEntity->GetMatrix().CopyToRwMatrix(&proj.entityMatrix); + + RwMatrixInvert(&proj.invMatrix, &mat); + RwReal radius = RwCameraGetViewWindow(shadow->GetRwCamera())->x; + + scl.x = scl.y = -0.5f / (radius*0.9f); + scl.z = 1.0f / (radius*0.8f); + RwMatrixScale(&proj.invMatrix, &scl, rwCOMBINEPOSTCONCAT); + + tr.x = 0.5f; + tr.y = tr.z = 0.0f; + RwMatrixTranslate(&proj.invMatrix, &tr, rwCOMBINEPOSTCONCAT); + + proj.shadowValue = nIntensity; + proj.fade = 0; + + RwMatrix matrix; + pEntity->GetMatrix().CopyToRwMatrix(&matrix); + RwMatrix invMatrix; + RwMatrixInvert(&invMatrix, &matrix); + + + CVector center(pShadow->GetBaseSphere().center); + center += CVector(-fFrontX * 1.1f, -fFrontY * 1.1f, -0.5f); + + CSphere sphere; + sphere.Set(2.0f, center); + + RwV3d point; + RwV3dTransformPoints(&point, ¢er, 1, &invMatrix); + + CColSphere colSphere; + colSphere.Set(2.0f, CVector(point), 0, 0); + + int i = 0; + while ( i < collision->numTriangles ) + { + CVector p[3]; + + collision->GetTrianglePoint(p[0], collision->triangles[i].a); + collision->GetTrianglePoint(p[1], collision->triangles[i].b); + collision->GetTrianglePoint(p[2], collision->triangles[i].c); + + if ( CCollision::TestSphereTriangle(colSphere, collision->vertices, collision->triangles[i], collision->trianglePlanes[i]) ) + { + CVector n(collision->trianglePlanes[i].GetNormalX(), collision->trianglePlanes[i].GetNormalY(), collision->trianglePlanes[i].GetNormalZ()); + CVector offset = n * 0.028f; + + p[0] += offset; + p[1] += offset; + p[2] += offset; + + if ( !ShadowRenderTriangleCB(p, &n, &proj) ) + break; + } + i++; + } + } +} + +void +CShadows::UpdateStaticShadows(void) +{ + for ( int32 i = 0; i < MAX_STATICSHADOWS; i++ ) + { + if ( aStaticShadows[i].m_pPolyBunch != nil && !aStaticShadows[i].m_bJustCreated + && (!aStaticShadows[i].m_bTemp || CTimer::GetTimeInMilliseconds() > aStaticShadows[i].m_nTimeCreated + 5000) ) + { + aStaticShadows[i].Free(); + } + + aStaticShadows[i].m_bJustCreated = false; + } +} + +void +CShadows::UpdatePermanentShadows(void) +{ + for ( int32 i = 0; i < MAX_PERMAMENTSHADOWS; i++ ) + { + if ( aPermanentShadows[i].m_nType != SHADOWTYPE_NONE ) + { + uint32 timePassed = CTimer::GetTimeInMilliseconds() - aPermanentShadows[i].m_nTimeCreated; + + if ( timePassed >= aPermanentShadows[i].m_nLifeTime ) + aPermanentShadows[i].m_nType = SHADOWTYPE_NONE; + else + { + bool bOk; + if ( timePassed >= (aPermanentShadows[i].m_nLifeTime * 3 / 4) ) + { + // timePassed == 0 -> 4 + // timePassed == aPermanentShadows[i].m_nLifeTime -> 0 + float fMult = 1.0f - float(timePassed - (aPermanentShadows[i].m_nLifeTime * 3 / 4)) / (aPermanentShadows[i].m_nLifeTime / 4); + + bOk = StoreStaticShadow((uintptr)&aPermanentShadows[i], + aPermanentShadows[i].m_nType, + aPermanentShadows[i].m_pTexture, + &aPermanentShadows[i].m_vecPos, + aPermanentShadows[i].m_vecFront.x, + aPermanentShadows[i].m_vecFront.y, + aPermanentShadows[i].m_vecSide.x, + aPermanentShadows[i].m_vecSide.y, + (int32)(aPermanentShadows[i].m_nIntensity * fMult), + (int32)(aPermanentShadows[i].m_nRed * fMult), + (int32)(aPermanentShadows[i].m_nGreen * fMult), + (int32)(aPermanentShadows[i].m_nBlue * fMult), + aPermanentShadows[i].m_fZDistance, + 1.0f, 40.0f, false, 0.0f); + } + else + { + bOk = StoreStaticShadow((uintptr)&aPermanentShadows[i], + aPermanentShadows[i].m_nType, + aPermanentShadows[i].m_pTexture, + &aPermanentShadows[i].m_vecPos, + aPermanentShadows[i].m_vecFront.x, + aPermanentShadows[i].m_vecFront.y, + aPermanentShadows[i].m_vecSide.x, + aPermanentShadows[i].m_vecSide.y, + aPermanentShadows[i].m_nIntensity, + aPermanentShadows[i].m_nRed, + aPermanentShadows[i].m_nGreen, + aPermanentShadows[i].m_nBlue, + aPermanentShadows[i].m_fZDistance, + 1.0f, 40.0f, false, 0.0f); + } + + if ( !bOk ) + aPermanentShadows[i].m_nType = SHADOWTYPE_NONE; + } + } + } +} + +void +CStaticShadow::Free(void) +{ + if ( m_pPolyBunch != nil ) + { + CPolyBunch *pFree = CShadows::pEmptyBunchList; + CShadows::pEmptyBunchList = m_pPolyBunch; + + CPolyBunch *pUsed = m_pPolyBunch; + while (pUsed->m_pNext != nil) + pUsed = pUsed->m_pNext; + + pUsed->m_pNext = pFree; + } + + m_pPolyBunch = nil; + + m_nId = 0; +} + +void +CShadows::CalcPedShadowValues(CVector vecLightDir, + float *pfFrontX, float *pfFrontY, + float *pfSideX, float *pfSideY, + float *pfDisplacementX, float *pfDisplacementY) +{ + ASSERT(pfFrontX != nil); + ASSERT(pfFrontY != nil); + ASSERT(pfSideX != nil); + ASSERT(pfSideY != nil); + ASSERT(pfDisplacementX != nil); + ASSERT(pfDisplacementY != nil); + + *pfFrontX = -vecLightDir.x; + *pfFrontY = -vecLightDir.y; + + float fDist = Sqrt(*pfFrontY * *pfFrontY + *pfFrontX * *pfFrontX); + float fMult = (fDist + 1.0f) / fDist; + + *pfFrontX *= fMult; + *pfFrontY *= fMult; + + *pfSideX = -vecLightDir.y / fDist; + *pfSideY = vecLightDir.x / fDist; + + *pfDisplacementX = -vecLightDir.x; + *pfDisplacementY = -vecLightDir.y; + + *pfFrontX /= 2; + *pfFrontY /= 2; + + *pfSideX /= 2; + *pfSideY /= 2; + + *pfDisplacementX /= 2; + *pfDisplacementY /= 2; + +} + + +void +CShadows::RenderExtraPlayerShadows(void) +{ +#ifdef FIX_BUGS + if (CReplay::IsPlayingBack()) + return; +#endif + if ( CTimeCycle::GetLightShadowStrength() != 0 ) + { + CVehicle *pCar = FindPlayerVehicle(); + if ( pCar == nil ) + ; // R* cut it out for playerped + else + { + if ( pCar->GetModelIndex() != MI_RCBANDIT + && pCar->GetVehicleAppearance() != VEHICLE_APPEARANCE_BIKE + && !pCar->IsBike() && !pCar->IsPlane() && !pCar->IsBoat() ) + { + for ( int32 i = 0; i < CPointLights::NumLights; i++ ) + { + if ( CPointLights::aLights[i].type == CPointLights::LIGHT_POINT + && CPointLights::aLights[i].castExtraShadows + &&(0.0f != CPointLights::aLights[i].red + || 0.0f != CPointLights::aLights[i].green + || 0.0f != CPointLights::aLights[i].blue) ) + { + CVector vecLight = CPointLights::aLights[i].coors - FindPlayerCoors(); + float fLightDist = vecLight.Magnitude(); + float fRadius = CPointLights::aLights[i].radius; + + if ( fLightDist < fRadius ) + { + // fLightDist == 0 -> 2.0f + // fLightDist == fRadius -> 0.0f + float fMult = (1.0f - (2.0f * fLightDist - fRadius) / fRadius); + + int32 nColorStrength; + if ( fLightDist < fRadius*0.5f ) + nColorStrength = (5*CTimeCycle::GetLightShadowStrength()/8); + else + nColorStrength = int32((5*CTimeCycle::GetLightShadowStrength()/8) * fMult); + + float fInv = 1.0f / fLightDist; + vecLight.x *= fInv; + vecLight.y *= fInv; + vecLight.z *= fInv; + + CVector shadowPos = pCar->GetPosition(); + + shadowPos.x -= vecLight.x * 1.2f; + shadowPos.y -= vecLight.y * 1.2f; + + float fVehicleWidth = pCar->GetColModel()->boundingBox.GetSize().x; + float fVehicleHeight = pCar->GetColModel()->boundingBox.GetSize().y; + + shadowPos.x -= ((fVehicleHeight/2) - pCar->GetColModel()->boundingBox.max.y) + * pCar->GetForward().x; + + shadowPos.y -= ((fVehicleHeight/2) - pCar->GetColModel()->boundingBox.max.y) + * pCar->GetForward().y; + + if ( pCar->GetUp().z > 0.0f ) + { + StoreShadowToBeRendered(SHADOWTYPE_DARK, gpShadowCarTex, &shadowPos, + pCar->GetForward().x * (fVehicleHeight/2), + pCar->GetForward().y * (fVehicleHeight/2), + pCar->GetRight().x * (fVehicleWidth/3), + pCar->GetRight().y * (fVehicleWidth/3), + nColorStrength, 0, 0, 0, + 4.5f, false, 1.0f, nil, false); + } + else + { + StoreShadowToBeRendered(SHADOWTYPE_DARK, gpShadowCarTex, &shadowPos, + pCar->GetForward().x * (fVehicleHeight/2), + pCar->GetForward().y * (fVehicleHeight/2), + -pCar->GetRight().x * (fVehicleWidth/2), + -pCar->GetRight().y * (fVehicleWidth/2), + nColorStrength, 0, 0, 0, + 4.5f, false, 1.0f, nil, false); + } + } + } + } + } + } + } +} + +void +CShadows::TidyUpShadows(void) +{ + for ( int32 i = 0; i < MAX_PERMAMENTSHADOWS; i++ ) + aPermanentShadows[i].m_nType = SHADOWTYPE_NONE; +} + +void +CShadows::RenderIndicatorShadow(uint32 nID, uint8 ShadowType, RwTexture *pTexture, CVector *pPosn, + float fFrontX, float fFrontY, float fSideX, float fSideY, + int16 nIntensity) +{ + ASSERT(pPosn != nil); + + C3dMarkers::PlaceMarkerSet(nID, MARKERTYPE_CYLINDER, *pPosn, Max(fFrontX, -fSideY), + SPHERE_MARKER_R, SPHERE_MARKER_G, SPHERE_MARKER_B, + SPHERE_MARKER_A, SPHERE_MARKER_PULSE_PERIOD, 0.2f, 0); +} diff --git a/src/miami/renderer/Shadows.h b/src/miami/renderer/Shadows.h new file mode 100644 index 00000000..937ff4eb --- /dev/null +++ b/src/miami/renderer/Shadows.h @@ -0,0 +1,210 @@ +#pragma once + +#define MAX_STOREDSHADOWS 48 +#define MAX_POLYBUNCHES 380 +#define MAX_STATICSHADOWS 48 +#define MAX_PERMAMENTSHADOWS 48 + + + +class CEntity; +class CPtrList; +class CAutomobile; +class CVehicle; +class CPed; +class CCutsceneShadow; +class CCutsceneObject; + +enum eShadowType +{ + SHADOWTYPE_NONE = 0, + SHADOWTYPE_DARK, + SHADOWTYPE_ADDITIVE, + SHADOWTYPE_INVCOLOR +}; + +enum eShadowTextureType +{ + SHADOWTEX_NONE = 0, + SHADOWTEX_CAR, + SHADOWTEX_PED, + SHADOWTEX_EXPLOSION, + SHADOWTEX_HELI, + SHADOWTEX_HEADLIGHTS, + SHADOWTEX_BLOOD +}; + +enum VEH_SHD_TYPE +{ + VEH_SHD_TYPE_CAR = 0, + VEH_SHD_TYPE_BIKE, + VEH_SHD_TYPE_HELI, + VEH_SHD_TYPE_SEAPLANE, + VEH_SHD_TYPE_RCPLANE, +}; + + +class CStoredShadow +{ +public: + CVector m_vecPos; + CVector2D m_vecFront; + CVector2D m_vecSide; + float m_fZDistance; + float m_fScale; + RwTexture *m_pTexture; + CCutsceneShadow *m_pCutsceneShadow; + int16 m_nIntensity; + uint8 m_ShadowType; + uint8 m_nRed; + uint8 m_nGreen; + uint8 m_nBlue; + struct + { + uint8 bDrawOnWater : 1; + uint8 bRendered : 1; + uint8 bDrawOnBuildings : 1; + } m_nFlags; + + + CStoredShadow() + { } +}; + +VALIDATE_SIZE(CStoredShadow, 0x30); + +class CPolyBunch +{ +public: + CVector m_aVerts[7]; + CPolyBunch *m_pNext; + int16 m_nNumVerts; + uint8 m_aU[7]; + uint8 m_aV[7]; + + CPolyBunch() + { } +}; + +VALIDATE_SIZE(CPolyBunch, 0x6C); + +class CStaticShadow +{ +public: + uint32 m_nId; + CPolyBunch *m_pPolyBunch; + uint32 m_nTimeCreated; + CVector m_vecPosn; + CVector2D m_vecFront; + CVector2D m_vecSide; + float m_fZDistance; + float m_fScale; + RwTexture *m_pTexture; + int16 m_nIntensity; // unsigned ? + uint8 m_nType; + uint8 m_nRed; + uint8 m_nGreen; + uint8 m_nBlue; + bool m_bJustCreated; + bool m_bRendered; + bool m_bTemp; + + + CStaticShadow() + { } + + void Free(); +}; + +VALIDATE_SIZE(CStaticShadow, 0x40); + +class CPermanentShadow +{ +public: + CVector m_vecPos; + CVector2D m_vecFront; + CVector2D m_vecSide; + float m_fZDistance; + float m_fScale; + uint32 m_nTimeCreated; + uint32 m_nLifeTime; + RwTexture *m_pTexture; + int16 m_nIntensity; + uint8 m_nType; // eShadowType + uint8 m_nRed; + uint8 m_nGreen; + uint8 m_nBlue; + + CPermanentShadow() + { } +}; + +VALIDATE_SIZE(CPermanentShadow, 0x38); + +class CShadows +{ +public: + static int16 ShadowsStoredToBeRendered; + static CStoredShadow asShadowsStored [MAX_STOREDSHADOWS]; + static CPolyBunch aPolyBunches [MAX_POLYBUNCHES]; + static CStaticShadow aStaticShadows [MAX_STATICSHADOWS]; + static CPolyBunch *pEmptyBunchList; + static CPermanentShadow aPermanentShadows[MAX_PERMAMENTSHADOWS]; + + static void Init (void); + static void Shutdown (void); + static void AddPermanentShadow ( uint8 ShadowType, RwTexture *pTexture, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, uint32 nTime, float fScale); + + static bool StoreStaticShadow (uint32 nID, uint8 ShadowType, RwTexture *pTexture, Const CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, float fDrawDistance, bool bTempShadow, float fUpDistance); + static void StoreShadowToBeRendered ( uint8 ShadowType, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue); + static void StoreShadowToBeRendered ( uint8 ShadowType, RwTexture *pTexture, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, bool bDrawOnWater, float fScale, CCutsceneShadow *pShadow, bool bDrawOnBuildings); + static void StoreShadowForVehicle (CVehicle *pCar, VEH_SHD_TYPE type); + static void StoreCarLightShadow (CVehicle *pCar, int32 nID, RwTexture *pTexture, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, uint8 nRed, uint8 nGreen, uint8 nBlue, float fMaxViewAngle); + static void StoreShadowForPed (CPed *pPed, float fDisplacementX, float fDisplacementY, float fFrontX, float fFrontY, float fSideX, float fSideY); + static void StoreShadowForPedObject (CEntity *pPedObject, float fDisplacementX, float fDisplacementY, float fFrontX, float fFrontY, float fSideX, float fSideY); + static void StoreShadowForCutscenePedObject(CCutsceneObject *pObject, float fDisplacementX, float fDisplacementY, float fFrontX, float fFrontY, float fSideX, float fSideY); + static void StoreShadowForTree (CEntity *pTree); + static void StoreShadowForPole (CEntity *pPole, float fOffsetX, float fOffsetY, float fOffsetZ, float fPoleHeight, float fPoleWidth, uint32 nID); + static void SetRenderModeForShadowType (uint8 ShadowType); + static void RenderStoredShadows (void); + static void RenderStaticShadows (void); + + static void GeneratePolysForStaticShadow (int16 nStaticShadowID); + static void CastShadowSectorList (CPtrList &PtrList, float fStartX, float fStartY, float fEndX, float fEndY, + CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch); + + static void CastPlayerShadowSectorList (CPtrList &PtrList, float fStartX, float fStartY, float fEndX, float fEndY, + CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch); + + static void CastCutsceneShadowSectorList (CPtrList &PtrList, float fStartX, float fStartY, float fEndX, float fEndY, + CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch, CCutsceneShadow *pShadow); + + static void CastShadowEntityXY (CEntity *pEntity, float fStartX, float fStartY, float fEndX, float fEndY, + CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch); + + static void CastShadowEntityXYZ (CEntity *pEntity, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch, CCutsceneShadow *pShadow); + + static void UpdateStaticShadows (void); + static void UpdatePermanentShadows (void); + static void CalcPedShadowValues (CVector vecLightDir, float *pfFrontX, float *pfFrontY, float *pfSideX, float *pfSideY, float *pfDisplacementX, float *pfDisplacementY); + static void RenderExtraPlayerShadows (void); + static void TidyUpShadows (void); + static void RenderIndicatorShadow (uint32 nID, uint8 ShadowType, RwTexture *pTexture, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity); +}; + +extern RwTexture *gpShadowCarTex; +extern RwTexture *gpShadowPedTex; +extern RwTexture *gpShadowHeliTex; +extern RwTexture *gpShadowBikeTex; +extern RwTexture *gpShadowBaronTex; +extern RwTexture *gpShadowExplosionTex; +extern RwTexture *gpShadowHeadLightsTex; +extern RwTexture *gpOutline1Tex; +extern RwTexture *gpOutline2Tex; +extern RwTexture *gpOutline3Tex; +extern RwTexture *gpBloodPoolTex; +extern RwTexture *gpReflectionTex; +extern RwTexture *gpWalkDontTex; +extern RwTexture *gpCrackedGlassTex; +extern RwTexture *gpPostShadowTex; +extern RwTexture *gpGoalTex; diff --git a/src/miami/renderer/Skidmarks.cpp b/src/miami/renderer/Skidmarks.cpp new file mode 100644 index 00000000..08df330d --- /dev/null +++ b/src/miami/renderer/Skidmarks.cpp @@ -0,0 +1,259 @@ +#include "common.h" + +#include "main.h" +#include "TxdStore.h" +#include "Timer.h" +#include "Replay.h" +#include "Skidmarks.h" + +CSkidmark CSkidmarks::aSkidmarks[NUMSKIDMARKS]; + +RwImVertexIndex SkidmarkIndexList[SKIDMARK_LENGTH * 6]; +RwIm3DVertex SkidmarkVertices[SKIDMARK_LENGTH * 2]; +RwTexture *gpSkidTex; + +void +CSkidmarks::Init(void) +{ + int i, ix, slot; + CTxdStore::PushCurrentTxd(); + slot = CTxdStore::FindTxdSlot("particle"); + CTxdStore::SetCurrentTxd(slot); + gpSkidTex = RwTextureRead("particleskid", nil); + CTxdStore::PopCurrentTxd(); + + for(i = 0; i < NUMSKIDMARKS; i++){ + aSkidmarks[i].m_state = 0; + aSkidmarks[i].m_wasUpdated = false; + } + + ix = 0; + for(i = 0; i < SKIDMARK_LENGTH; i++){ + SkidmarkIndexList[i*6+0] = ix+0; + SkidmarkIndexList[i*6+1] = ix+2; + SkidmarkIndexList[i*6+2] = ix+1; + SkidmarkIndexList[i*6+3] = ix+1; + SkidmarkIndexList[i*6+4] = ix+2; + SkidmarkIndexList[i*6+5] = ix+3; + ix += 2; + } +} + +void +CSkidmarks::Shutdown(void) +{ + RwTextureDestroy(gpSkidTex); + gpSkidTex = nil; +} + +void +CSkidmarks::Clear(void) +{ + int i; + for(i = 0; i < NUMSKIDMARKS; i++){ + aSkidmarks[i].m_state = 0; + aSkidmarks[i].m_wasUpdated = false; + } +} + +void +CSkidmarks::Update(void) +{ + int i; + uint32 t1 = CTimer::GetTimeInMilliseconds() + 2500; + uint32 t2 = CTimer::GetTimeInMilliseconds() + 5000; + uint32 t3 = CTimer::GetTimeInMilliseconds() + 10000; + uint32 t4 = CTimer::GetTimeInMilliseconds() + 20000; + for(i = 0; i < NUMSKIDMARKS; i++){ + switch(aSkidmarks[i].m_state){ + case 1: + if(!aSkidmarks[i].m_wasUpdated){ + // Didn't continue this one last time, so finish it and set fade times + aSkidmarks[i].m_state = 2; + if(aSkidmarks[i].m_last < 4){ + aSkidmarks[i].m_fadeStart = t1; + aSkidmarks[i].m_fadeEnd = t2; + }else if(aSkidmarks[i].m_last < 9){ + aSkidmarks[i].m_fadeStart = t2; + aSkidmarks[i].m_fadeEnd = t3; + }else{ + aSkidmarks[i].m_fadeStart = t3; + aSkidmarks[i].m_fadeEnd = t4; + } + } + break; + case 2: + if(CTimer::GetTimeInMilliseconds() > aSkidmarks[i].m_fadeEnd) + aSkidmarks[i].m_state = 0; + break; + } + aSkidmarks[i].m_wasUpdated = false; + } +} + +void +CSkidmarks::Render(void) +{ + int i, j; + + PUSH_RENDERGROUP("CSkidmarks::Render"); + + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpSkidTex)); + + for(i = 0; i < NUMSKIDMARKS; i++){ + if(aSkidmarks[i].m_state == 0 || aSkidmarks[i].m_last < 1) + continue; + + CRGBA color(0, 0, 0, 255); + switch(aSkidmarks[i].m_type){ + case SKIDMARK_NORMAL: color = CRGBA(0, 0, 0, 255); break; + case SKIDMARK_MUDDY: color = CRGBA(90, 62, 9, 255); break; + case SKIDMARK_SANDY: color = CRGBA(108, 108, 96, 255); break; + case SKIDMARK_BLOODY: color = CRGBA(132, 34, 11, 255); break; + } + + uint32 fade, alpha; + if(aSkidmarks[i].m_state == 1 || CTimer::GetTimeInMilliseconds() < aSkidmarks[i].m_fadeStart) + fade = 255; + else + fade = 255*(aSkidmarks[i].m_fadeEnd - CTimer::GetTimeInMilliseconds()) / (aSkidmarks[i].m_fadeEnd - aSkidmarks[i].m_fadeStart); + + for(j = 0; j <= aSkidmarks[i].m_last; j++){ + alpha = 128; + if(j == 0 || j == aSkidmarks[i].m_last && aSkidmarks[i].m_state == 2) + alpha = 0; + alpha = alpha*fade/256; + + CVector p1 = aSkidmarks[i].m_pos[j]; + p1.x += aSkidmarks[i].m_sideX[j]; + p1.y += aSkidmarks[i].m_sideY[j]; + CVector p2 = aSkidmarks[i].m_pos[j]; + p2.x -= aSkidmarks[i].m_sideX[j]; + p2.y -= aSkidmarks[i].m_sideY[j]; + RwIm3DVertexSetRGBA(&SkidmarkVertices[j*2+0], color.red, color.green, color.blue, alpha); + RwIm3DVertexSetPos(&SkidmarkVertices[j*2+0], p1.x, p1.y, p1.z+0.1f); + RwIm3DVertexSetU(&SkidmarkVertices[j*2+0], 0.0f); + RwIm3DVertexSetV(&SkidmarkVertices[j*2+0], j*5.01f); + RwIm3DVertexSetRGBA(&SkidmarkVertices[j*2+1], color.red, color.green, color.blue, alpha); + RwIm3DVertexSetPos(&SkidmarkVertices[j*2+1], p2.x, p2.y, p2.z+0.1f); + RwIm3DVertexSetU(&SkidmarkVertices[j*2+1], 1.0f); + RwIm3DVertexSetV(&SkidmarkVertices[j*2+1], j*5.01f); + } + + LittleTest(); + if(RwIm3DTransform(SkidmarkVertices, 2*(aSkidmarks[i].m_last+1), nil, rwIM3D_VERTEXUV)){ + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, SkidmarkIndexList, 6*aSkidmarks[i].m_last); + RwIm3DEnd(); + } + } + + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + + POP_RENDERGROUP(); +} + +void +CSkidmarks::RegisterOne(uintptr id, const CVector &pos, float fwdX, float fwdY, bool *isMuddy, bool *isBloody) +{ + eSkidmarkType type; + if(*isBloody) + type = SKIDMARK_BLOODY; + else if(*isMuddy) + type = SKIDMARK_MUDDY; + else + type = SKIDMARK_NORMAL; + RegisterOne(id, pos, fwdX, fwdY, type, isBloody); +} + +void +CSkidmarks::RegisterOne(uintptr id, const CVector &pos, float fwdX, float fwdY, eSkidmarkType type, bool *isBloody) +{ + int i; + CVector2D fwd(fwdX, fwdY); + + if(CReplay::IsPlayingBack()) + return; + + // Find a skidmark to continue + for(i = 0; i < NUMSKIDMARKS; i++) + if(aSkidmarks[i].m_state == 1 && aSkidmarks[i].m_id == id) + break; + + if(i < NUMSKIDMARKS){ + // Continue this one + + if((aSkidmarks[i].m_type==SKIDMARK_BLOODY) != *isBloody){ + // Blood-status changed, end this one + aSkidmarks[i].m_state = 2; + aSkidmarks[i].m_fadeStart = CTimer::GetTimeInMilliseconds() + 10000; + aSkidmarks[i].m_fadeEnd = CTimer::GetTimeInMilliseconds() + 20000; + return; + } + + aSkidmarks[i].m_wasUpdated = true; + + if(CTimer::GetTimeInMilliseconds() - aSkidmarks[i].m_lastUpdate <= 100){ + // Last update was recently, just change last coords + aSkidmarks[i].m_pos[aSkidmarks[i].m_last] = pos; + return; + } + aSkidmarks[i].m_lastUpdate = CTimer::GetTimeInMilliseconds(); + + if(aSkidmarks[i].m_last >= SKIDMARK_LENGTH-1){ + // No space to continue, end it + aSkidmarks[i].m_state = 2; + aSkidmarks[i].m_fadeStart = CTimer::GetTimeInMilliseconds() + 10000; + aSkidmarks[i].m_fadeEnd = CTimer::GetTimeInMilliseconds() + 20000; + *isBloody = false; // stpo blood marks at end + return; + } + aSkidmarks[i].m_last++; + + aSkidmarks[i].m_pos[aSkidmarks[i].m_last] = pos; + + CVector2D right(aSkidmarks[i].m_pos[aSkidmarks[i].m_last].y - aSkidmarks[i].m_pos[aSkidmarks[i].m_last - 1].y, + aSkidmarks[i].m_pos[aSkidmarks[i].m_last - 1].x - aSkidmarks[i].m_pos[aSkidmarks[i].m_last].x); + + right.Normalise(); + fwd.Normalise(); + float turn = DotProduct2D(fwd, right); + turn = Abs(turn) + 1.0f; + aSkidmarks[i].m_sideX[aSkidmarks[i].m_last] = right.x * turn * 0.125f; + aSkidmarks[i].m_sideY[aSkidmarks[i].m_last] = right.y * turn * 0.125f; + if(aSkidmarks[i].m_last == 1){ + aSkidmarks[i].m_sideX[0] = aSkidmarks[i].m_sideX[1]; + aSkidmarks[i].m_sideY[0] = aSkidmarks[i].m_sideY[1]; + } + + if(aSkidmarks[i].m_last > 8) + *isBloody = false; // stop blood marks after 8 + return; + } + + // Start a new one + for(i = 0; i < NUMSKIDMARKS; i++) + if(aSkidmarks[i].m_state == 0) + break; + if(i < NUMSKIDMARKS){ + // Found a free slot + aSkidmarks[i].m_state = 1; + aSkidmarks[i].m_id = id; + aSkidmarks[i].m_pos[0] = pos; + aSkidmarks[i].m_sideX[0] = 0.0f; + aSkidmarks[i].m_sideY[0] = 0.0f; + aSkidmarks[i].m_wasUpdated = true; + aSkidmarks[i].m_last = 0; + aSkidmarks[i].m_lastUpdate = CTimer::GetTimeInMilliseconds() - 1000; + if(*isBloody) + aSkidmarks[i].m_type = SKIDMARK_BLOODY; + else + aSkidmarks[i].m_type = type; + }else + *isBloody = false; // stop blood marks if no space +} diff --git a/src/miami/renderer/Skidmarks.h b/src/miami/renderer/Skidmarks.h new file mode 100644 index 00000000..28082f08 --- /dev/null +++ b/src/miami/renderer/Skidmarks.h @@ -0,0 +1,41 @@ +#pragma once + +enum { SKIDMARK_LENGTH = 16 }; + +enum eSkidmarkType +{ + SKIDMARK_NORMAL, + SKIDMARK_MUDDY, + SKIDMARK_SANDY, + SKIDMARK_BLOODY +}; + +class CSkidmark +{ +public: + CVector m_pos[SKIDMARK_LENGTH]; + float m_sideX[SKIDMARK_LENGTH]; + float m_sideY[SKIDMARK_LENGTH]; + uintptr m_id; + uint32 m_lastUpdate; + uint32 m_fadeStart; + uint32 m_fadeEnd; + uint32 m_type; + int16 m_last; + uint8 m_state; + bool m_wasUpdated; +}; + +class CSkidmarks +{ + static CSkidmark aSkidmarks[NUMSKIDMARKS]; +public: + + static void Init(void); + static void Shutdown(void); + static void Clear(void); + static void Update(void); + static void Render(void); + static void RegisterOne(uintptr id, const CVector &pos, float fwdX, float fwdY, eSkidmarkType type, bool *isBloody); + static void RegisterOne(uintptr id, const CVector &pos, float fwdX, float fwdY, bool *isMuddy, bool *isBloody); +}; diff --git a/src/miami/renderer/SpecialFX.cpp b/src/miami/renderer/SpecialFX.cpp new file mode 100644 index 00000000..61750f85 --- /dev/null +++ b/src/miami/renderer/SpecialFX.cpp @@ -0,0 +1,1493 @@ +#include "common.h" + +#include "SpecialFX.h" +#include "RenderBuffer.h" +#include "Timer.h" +#include "Sprite.h" +#include "Font.h" +#include "Text.h" +#include "TxdStore.h" +#include "FileMgr.h" +#include "FileLoader.h" +#include "Timecycle.h" +#include "Lights.h" +#include "ModelIndices.h" +#include "VisibilityPlugins.h" +#include "World.h" +#include "PlayerPed.h" +#include "Particle.h" +#include "Shadows.h" +#include "General.h" +#include "Camera.h" +#include "Shadows.h" +#include "main.h" +#include "ColStore.h" +#include "Coronas.h" +#include "Script.h" +#include "DMAudio.h" + +RwIm3DVertex StreakVertices[4]; +RwImVertexIndex StreakIndexList[12]; + +RwIm3DVertex TraceVertices[10]; +static RwImVertexIndex TraceIndexList[48] = {0, 5, 7, 0, 7, 2, 0, 7, 5, 0, 2, 7, 0, 4, 9, 0, + 9, 5, 0, 9, 4, 0, 5, 9, 0, 1, 6, 0, 6, 5, 0, 6, + 1, 0, 5, 6, 0, 3, 8, 0, 8, 5, 0, 8, 3, 0, 5, 8 }; + +bool CSpecialFX::bVideoCam; +bool CSpecialFX::bLiftCam; +bool CSpecialFX::bSnapShotActive; +int32 CSpecialFX::SnapShotFrames; +static RwTexture* gpSmokeTrailTexture; + + +void +CSpecialFX::Init(void) +{ + CBulletTraces::Init(); + + RwIm3DVertexSetU(&TraceVertices[0], 0.0); + RwIm3DVertexSetV(&TraceVertices[0], 0.0); + RwIm3DVertexSetU(&TraceVertices[1], 1.0); + RwIm3DVertexSetV(&TraceVertices[1], 0.0); + RwIm3DVertexSetU(&TraceVertices[2], 1.0); + RwIm3DVertexSetV(&TraceVertices[2], 0.0); + RwIm3DVertexSetU(&TraceVertices[3], 1.0); + RwIm3DVertexSetV(&TraceVertices[3], 0.0); + RwIm3DVertexSetU(&TraceVertices[4], 1.0); + RwIm3DVertexSetV(&TraceVertices[4], 0.0); + RwIm3DVertexSetU(&TraceVertices[5], 0.0); + RwIm3DVertexSetU(&TraceVertices[6], 1.0); + RwIm3DVertexSetU(&TraceVertices[7], 1.0); + RwIm3DVertexSetU(&TraceVertices[8], 1.0); + RwIm3DVertexSetU(&TraceVertices[9], 1.0); + + RwIm3DVertexSetU(&StreakVertices[0], 0.0f); + RwIm3DVertexSetV(&StreakVertices[0], 0.0f); + RwIm3DVertexSetU(&StreakVertices[1], 1.0f); + RwIm3DVertexSetV(&StreakVertices[1], 0.0f); + RwIm3DVertexSetU(&StreakVertices[2], 0.0f); + RwIm3DVertexSetV(&StreakVertices[2], 0.0f); + RwIm3DVertexSetU(&StreakVertices[3], 1.0f); + RwIm3DVertexSetV(&StreakVertices[3], 0.0f); + StreakIndexList[0] = 0; + StreakIndexList[1] = 1; + StreakIndexList[2] = 2; + StreakIndexList[3] = 1; + StreakIndexList[4] = 3; + StreakIndexList[5] = 2; + StreakIndexList[6] = 0; + StreakIndexList[7] = 2; + StreakIndexList[8] = 1; + StreakIndexList[9] = 1; + StreakIndexList[10] = 2; + StreakIndexList[11] = 3; + + CMotionBlurStreaks::Init(); + CBrightLights::Init(); + CShinyTexts::Init(); + CMoneyMessages::Init(); + C3dMarkers::Init(); + CSpecialFX::bSnapShotActive = false; + CSpecialFX::bVideoCam = false; + CSpecialFX::SnapShotFrames = 0; + CSpecialFX::bLiftCam = false; + CTxdStore::PushCurrentTxd(); + CTxdStore::SetCurrentTxd(CTxdStore::FindTxdSlot("particle")); + if(gpSmokeTrailTexture == nil) + gpSmokeTrailTexture = RwTextureRead("smoketrail", 0); + CTxdStore::PopCurrentTxd(); +} + +void +CSpecialFX::AddWeaponStreak(int type) +{ + static CMatrix matrix; + CVector start; + CVector end; + + if (FindPlayerPed() != nil && FindPlayerPed()->m_pWeaponModel != nil) { + switch (type) { + case WEAPONTYPE_BASEBALLBAT: + matrix = RwFrameGetLTM(RpAtomicGetFrame(FindPlayerPed()->m_pWeaponModel)); + start = matrix * CVector(0.02f, 0.05f, 0.07f); + end = matrix * CVector(0.246f, 0.0325f, 0.796f); + break; + case WEAPONTYPE_GOLFCLUB: + matrix = RwFrameGetLTM(RpAtomicGetFrame(FindPlayerPed()->m_pWeaponModel)); + start = matrix * CVector(0.02f, 0.05f, 0.07f); + end = matrix * CVector(-0.054f, 0.0325f, 0.796f); + break; + case WEAPONTYPE_KATANA: + matrix = RwFrameGetLTM(RpAtomicGetFrame(FindPlayerPed()->m_pWeaponModel)); + start = matrix * CVector(0.02f, 0.05f, 0.07f); + end = matrix * CVector(0.096f, -0.0175f, 1.096f); + break; + default: + return; + } + CMotionBlurStreaks::RegisterStreak((uintptr)FindPlayerPed()->m_pWeaponModel, 100, 100, 100, start, end); + } +} + +RwObject* +LookForBatCB(RwObject *object, void *data) +{ + static CMatrix MatLTM; + + if(CVisibilityPlugins::GetAtomicModelInfo((RpAtomic*)object) == (CSimpleModelInfo*)data){ + MatLTM = CMatrix(RwFrameGetLTM(RpAtomicGetFrame((RpAtomic*)object))); + CVector p1 = MatLTM * CVector(0.02f, 0.05f, 0.07f); + CVector p2 = MatLTM * CVector(0.246f, 0.0325f, 0.796f); + CMotionBlurStreaks::RegisterStreak((uintptr)object, 100, 100, 100, p1, p2); + } + return nil; +} + +void +CSpecialFX::Update(void) +{ + CMotionBlurStreaks::Update(); + CBulletTraces::Update(); +} + +void +CSpecialFX::Shutdown(void) +{ + C3dMarkers::Shutdown(); + if (gpSmokeTrailTexture) { + RwTextureDestroy(gpSmokeTrailTexture); + gpSmokeTrailTexture = nil; + } +} + +void +CSpecialFX::Render(void) +{ + PUSH_RENDERGROUP("CSpecialFX::Render"); + CMotionBlurStreaks::Render(); + CBulletTraces::Render(); + CBrightLights::Render(); + CShinyTexts::Render(); + CMoneyMessages::Render(); +#ifdef NEW_RENDERER + if(!(gbNewRenderer && FredIsInFirstPersonCam())) +#endif + C3dMarkers::Render(); + POP_RENDERGROUP(); +} + +void +CSpecialFX::Render2DFXs(void) +{ + if (CSpecialFX::bVideoCam) { + CFont::SetScale(SCREEN_SCALE_X(1.5f), SCREEN_SCALE_Y(1.5f)); + CFont::SetJustifyOff(); + CFont::SetBackgroundOff(); + CFont::SetCentreSize(SCREEN_SCALE_X(620.0f)); // unused + CFont::SetCentreOff(); + CFont::SetPropOn(); + CFont::SetColor(CRGBA(0, 255, 0, 200)); + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); + sprintf(gString, "%d", CTimer::GetFrameCounter() & 0x3F); // mb % 63 + AsciiToUnicode(gString, gUString); + CFont::PrintString(SCREEN_WIDTH * 8 / 10, SCREEN_HEIGHT * 8 / 10, gUString); + for (int32 i = 0; i < SCREEN_HEIGHT; i += 4) { + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDONE); + CSprite2d::Draw2DPolygon(0.0f, i, SCREEN_WIDTH, i, 0.0f, i+1, SCREEN_WIDTH, i+1, CRGBA(0, 100, 0, 100)); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + CSprite2d::Draw2DPolygon(0.0f, i+2, SCREEN_WIDTH, i+2, 0.0f, i+3, SCREEN_WIDTH, i+3, CRGBA(0, 0, 0, 150)); + } + int32 tmp = (CTimer::GetTimeInMilliseconds() & 0x7ff) * (SCREEN_HEIGHT + 70.0f) / 2048 - 70.0f; //mb % 2048 + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + CSprite2d::Draw2DPolygon(0.0, tmp, SCREEN_WIDTH, tmp, 0.0, tmp + 70.0f, SCREEN_WIDTH, tmp + 70.0f , CRGBA(0, 100, 0, 60)); + } + if (CSpecialFX::bLiftCam) { + CFont::SetScale(SCREEN_SCALE_X(1.5f), SCREEN_SCALE_Y(1.5f)); + CFont::SetJustifyOff(); + CFont::SetBackgroundOff(); + CFont::SetCentreSize(SCREEN_SCALE_X(620.0f)); // unused + CFont::SetCentreOff(); + CFont::SetPropOn(); + CFont::SetColor(CRGBA(100, 100, 100, 200)); + CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); + for (int32 i = 0; i < SCREEN_HEIGHT; i += 4) { + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + CSprite2d::Draw2DPolygon(0.0f, i, SCREEN_WIDTH, i, 0.0f, i + 1, SCREEN_WIDTH, i + 1, CRGBA(100, 100, 100, 100)); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + CSprite2d::Draw2DPolygon(0.0f, i + 2, SCREEN_WIDTH, i + 2, 0.0f, i + 3, SCREEN_WIDTH, i + 3, CRGBA(0, 0, 0, 150)); + } + int32 tmp = (CTimer::GetTimeInMilliseconds() & 0x7ff) * (SCREEN_HEIGHT + 70.0f) / 2048 - 70.0f; //mb % 2048 + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + CSprite2d::Draw2DPolygon(0.0, tmp, SCREEN_WIDTH, tmp, 0.0, tmp + 70.0f, SCREEN_WIDTH, tmp + 70.0f, CRGBA(100, 100, 100, 60)); + for (int32 i = 0; i < 200; i++) { + int32 posX = CGeneral::GetRandomNumber() % (int32)SCREEN_WIDTH; + int32 posY = CGeneral::GetRandomNumber() % (int32)SCREEN_HEIGHT; + CSprite2d::DrawRect(CRect(posX, posY + 2, posX+20, posY), CRGBA(255, 255, 255, 64)); + } + } + if (CSpecialFX::bSnapShotActive) { + if (++CSpecialFX::SnapShotFrames > 20) { + CSpecialFX::bSnapShotActive = false; + CTimer::SetTimeScale(1.0f); + } else { + CTimer::SetTimeScale(0.0f); //in andro it's 0.00001 + if (CSpecialFX::SnapShotFrames < 10) { + int32 tmp = (255 - 255 * CSpecialFX::SnapShotFrames / 10) * 0.65f; + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + CSprite2d::Draw2DPolygon(0.0f, 0.0f, SCREEN_WIDTH, 0.0f, 0.0f, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT, CRGBA(tmp, tmp, tmp, tmp)); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + } + } + } +} + +CRegisteredMotionBlurStreak CMotionBlurStreaks::aStreaks[NUMMBLURSTREAKS]; + +void +CRegisteredMotionBlurStreak::Update(void) +{ + int i; + bool wasUpdated; + bool lastWasUpdated = false; + for(i = 2; i > 0; i--){ + m_pos1[i] = m_pos1[i-1]; + m_pos2[i] = m_pos2[i-1]; + m_isValid[i] = m_isValid[i-1]; + wasUpdated = true; + if(!lastWasUpdated && !m_isValid[i]) + wasUpdated = false; + lastWasUpdated = wasUpdated; + } + m_isValid[0] = false; + if(!wasUpdated) + m_id = 0; +} + +void +CRegisteredMotionBlurStreak::Render(void) +{ + int i; + int a1, a2; + for(i = 0; i < 2; i++) + if(m_isValid[i] && m_isValid[i+1]){ + a1 = (255/3)*(3-i)/3; + RwIm3DVertexSetRGBA(&StreakVertices[0], m_red, m_green, m_blue, a1); + RwIm3DVertexSetRGBA(&StreakVertices[1], m_red, m_green, m_blue, a1); + a2 = (255/3)*(3-(i+1))/3; + RwIm3DVertexSetRGBA(&StreakVertices[2], m_red, m_green, m_blue, a2); + RwIm3DVertexSetRGBA(&StreakVertices[3], m_red, m_green, m_blue, a2); + RwIm3DVertexSetPos(&StreakVertices[0], m_pos1[i].x, m_pos1[i].y, m_pos1[i].z); + RwIm3DVertexSetPos(&StreakVertices[1], m_pos2[i].x, m_pos2[i].y, m_pos2[i].z); + RwIm3DVertexSetPos(&StreakVertices[2], m_pos1[i+1].x, m_pos1[i+1].y, m_pos1[i+1].z); + RwIm3DVertexSetPos(&StreakVertices[3], m_pos2[i+1].x, m_pos2[i+1].y, m_pos2[i+1].z); + LittleTest(); + if(RwIm3DTransform(StreakVertices, 4, nil, rwIM3D_VERTEXUV)){ + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, StreakIndexList, 12); + RwIm3DEnd(); + } + } +} + +void +CMotionBlurStreaks::Init(void) +{ + int i; + for(i = 0; i < NUMMBLURSTREAKS; i++) + aStreaks[i].m_id = 0; +} + +void +CMotionBlurStreaks::Update(void) +{ + int i; + for(i = 0; i < NUMMBLURSTREAKS; i++) + if(aStreaks[i].m_id != 0) + aStreaks[i].Update(); +} + +void +CMotionBlurStreaks::RegisterStreak(uintptr id, uint8 r, uint8 g, uint8 b, CVector p1, CVector p2) +{ + int i; + + for(i = 0; i < NUMMBLURSTREAKS; i++){ + if(aStreaks[i].m_id == id){ + // Found a streak from last frame, update + aStreaks[i].m_red = r; + aStreaks[i].m_green = g; + aStreaks[i].m_blue = b; + aStreaks[i].m_pos1[0] = p1; + aStreaks[i].m_pos2[0] = p2; + aStreaks[i].m_isValid[0] = true; + return; + } + } + + // Find free slot + for(i = 0; aStreaks[i].m_id != 0 ; i++) + if(i == NUMMBLURSTREAKS-1) + return; + + // Create a new streak + aStreaks[i].m_id = id; + aStreaks[i].m_red = r; + aStreaks[i].m_green = g; + aStreaks[i].m_blue = b; + aStreaks[i].m_pos1[0] = p1; + aStreaks[i].m_pos2[0] = p2; + aStreaks[i].m_isValid[0] = true; + aStreaks[i].m_isValid[1] = false; + aStreaks[i].m_isValid[2] = false; +} + +void +CMotionBlurStreaks::Render(void) +{ + bool setRenderStates = false; + int i; + for(i = 0; i < NUMMBLURSTREAKS; i++) + if(aStreaks[i].m_id != 0){ + if(!setRenderStates){ + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATEFOGCOLOR, + (void*)RWRGBALONG(CTimeCycle::GetFogRed(), CTimeCycle::GetFogGreen(), CTimeCycle::GetFogBlue(), 255)); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void*)FALSE); + setRenderStates = true; + } + aStreaks[i].Render(); + } + if(setRenderStates){ + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)FALSE); + } +} + + +CBulletTrace CBulletTraces::aTraces[NUMBULLETTRACES]; + +void CBulletTraces::Init(void) +{ + for (int i = 0; i < NUMBULLETTRACES; i++) + aTraces[i].m_bInUse = false; +} + +void CBulletTraces::AddTrace(CVector* start, CVector* end, float thickness, uint32 lifeTime, uint8 visibility) +{ + int32 enabledCount; + uint32 modifiedLifeTime; + int32 nextSlot; + + enabledCount = 0; + for (int i = 0; i < NUMBULLETTRACES; i++) + if (aTraces[i].m_bInUse) + enabledCount++; + if (enabledCount >= 10) + modifiedLifeTime = lifeTime / 4; + else if (enabledCount >= 5) + modifiedLifeTime = lifeTime / 2; + else + modifiedLifeTime = lifeTime; + + nextSlot = 0; + for (int i = 0; nextSlot < NUMBULLETTRACES && aTraces[i].m_bInUse; i++) + nextSlot++; + if (nextSlot < 16) { + aTraces[nextSlot].m_vecStartPos = *start; + aTraces[nextSlot].m_vecEndPos = *end; + aTraces[nextSlot].m_bInUse = true; + aTraces[nextSlot].m_nCreationTime = CTimer::GetTimeInMilliseconds(); + aTraces[nextSlot].m_fVisibility = visibility; + aTraces[nextSlot].m_fThickness = thickness; + aTraces[nextSlot].m_nLifeTime = modifiedLifeTime; + } + + float startProjFwd = DotProduct(TheCamera.GetForward(), *start - TheCamera.GetPosition()); + float endProjFwd = DotProduct(TheCamera.GetForward(), *end - TheCamera.GetPosition()); + if (startProjFwd * endProjFwd < 0.0f) { //if one of point behind us and second before us + float fStartDistFwd = Abs(startProjFwd) / (Abs(startProjFwd) + Abs(endProjFwd)); + + float startProjUp = DotProduct(TheCamera.GetUp(), *start - TheCamera.GetPosition()); + float endProjUp = DotProduct(TheCamera.GetUp(), *end - TheCamera.GetPosition()); + float distUp = (endProjUp - startProjUp) * fStartDistFwd + startProjUp; + + float startProjRight = DotProduct(TheCamera.GetRight(), *start - TheCamera.GetPosition()); + float endProjRight = DotProduct(TheCamera.GetRight(), *end - TheCamera.GetPosition()); + float distRight = (endProjRight - startProjRight) * fStartDistFwd + startProjRight; + + float dist = Sqrt(SQR(distUp) + SQR(distRight)); + if (dist < 2.0f) { + if(distRight < 0.0f) + DMAudio.PlayFrontEndSound(SOUND_BULLETTRACE_2, 127 * (1.0f - dist * 0.5f)); + else + DMAudio.PlayFrontEndSound(SOUND_BULLETTRACE_1, 127 * (1.0f - dist * 0.5f)); + } + } +} + +void CBulletTraces::AddTrace(CVector* start, CVector* end, int32 weaponType, class CEntity* shooter) +{ + CPhysical* player; + float speed; + int16 camMode; + + if (shooter == (CEntity*)FindPlayerPed() || (FindPlayerVehicle() != nil && FindPlayerVehicle() == (CVehicle*)shooter)) { + camMode = TheCamera.Cams[TheCamera.ActiveCam].Mode; + if (camMode == CCam::MODE_M16_1STPERSON + || camMode == CCam::MODE_CAMERA + || camMode == CCam::MODE_SNIPER + || camMode == CCam::MODE_M16_1STPERSON_RUNABOUT + || camMode == CCam::MODE_ROCKETLAUNCHER + || camMode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT + || camMode == CCam::MODE_SNIPER_RUNABOUT + || camMode == CCam::MODE_HELICANNON_1STPERSON) { + + player = FindPlayerVehicle() ? (CPhysical*)FindPlayerVehicle() : (CPhysical*)FindPlayerPed(); + speed = player->m_vecMoveSpeed.Magnitude(); + if (speed < 0.05f) + return; + } + } + + switch (weaponType) { + case WEAPONTYPE_PYTHON: + case WEAPONTYPE_SHOTGUN: + case WEAPONTYPE_SPAS12_SHOTGUN: + case WEAPONTYPE_STUBBY_SHOTGUN: + CBulletTraces::AddTrace(start, end, 0.7f, 1000, 200); + break; + case WEAPONTYPE_M4: + case WEAPONTYPE_RUGER: + case WEAPONTYPE_SNIPERRIFLE: + case WEAPONTYPE_LASERSCOPE: + case WEAPONTYPE_M60: + case WEAPONTYPE_MINIGUN: + case WEAPONTYPE_HELICANNON: + CBulletTraces::AddTrace(start, end, 1.0f, 2000, 220); + break; + default: + CBulletTraces::AddTrace(start, end, 0.4f, 750, 150); + break; + } +} + +void CBulletTraces::Render(void) +{ + for (int i = 0; i < NUMBULLETTRACES; i++) { + if (!aTraces[i].m_bInUse) + continue; + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpSmokeTrailTexture)); + + float timeAlive = CTimer::GetTimeInMilliseconds() - aTraces[i].m_nCreationTime; + + float traceThickness = aTraces[i].m_fThickness * timeAlive / aTraces[i].m_nLifeTime; + CVector horizontalOffset = aTraces[i].m_vecEndPos - aTraces[i].m_vecStartPos; + horizontalOffset.Normalise(); + horizontalOffset *= traceThickness; + + //then closer trace to die then it more transparent + uint8 nAlphaValue = aTraces[i].m_fVisibility * (aTraces[i].m_nLifeTime - timeAlive) / aTraces[i].m_nLifeTime; + + CVector start = aTraces[i].m_vecStartPos; + CVector end = aTraces[i].m_vecEndPos; + float startProj = DotProduct(start - TheCamera.GetPosition(), TheCamera.GetForward()) - 0.7f; + float endProj = DotProduct(end - TheCamera.GetPosition(), TheCamera.GetForward()) - 0.7f; + if (startProj < 0.0f && endProj < 0.0f) //we dont need render trace behind us + continue; + + if (startProj < 0.0f) { //if strat behind us move it closer + float absStartProj = Abs(startProj); + float absEndProj = Abs(endProj); + start = (absEndProj * start + absStartProj * end) / (absStartProj + absEndProj); + } else if (endProj < 0.0f) { + float absStartProj = Abs(startProj); + float absEndProj = Abs(endProj); + end = (absEndProj * start + absStartProj * end) / (absStartProj + absEndProj); + } + + //we divide trace at three parts + CVector start2 = (7.0f * start + end) / 8; + CVector end2 = (7.0f * end + start) / 8; + + RwIm3DVertexSetV(&TraceVertices[5], 10.0f); + RwIm3DVertexSetV(&TraceVertices[6], 10.0f); + RwIm3DVertexSetV(&TraceVertices[7], 10.0f); + RwIm3DVertexSetV(&TraceVertices[8], 10.0f); + RwIm3DVertexSetV(&TraceVertices[9], 10.0f); + + RwIm3DVertexSetRGBA(&TraceVertices[0], 255, 255, 255, nAlphaValue); + RwIm3DVertexSetRGBA(&TraceVertices[1], 255, 255, 255, nAlphaValue); + RwIm3DVertexSetRGBA(&TraceVertices[2], 255, 255, 255, nAlphaValue); + RwIm3DVertexSetRGBA(&TraceVertices[3], 255, 255, 255, nAlphaValue); + RwIm3DVertexSetRGBA(&TraceVertices[4], 255, 255, 255, nAlphaValue); + RwIm3DVertexSetRGBA(&TraceVertices[5], 255, 255, 255, nAlphaValue); + RwIm3DVertexSetRGBA(&TraceVertices[6], 255, 255, 255, nAlphaValue); + RwIm3DVertexSetRGBA(&TraceVertices[7], 255, 255, 255, nAlphaValue); + RwIm3DVertexSetRGBA(&TraceVertices[8], 255, 255, 255, nAlphaValue); + RwIm3DVertexSetRGBA(&TraceVertices[9], 255, 255, 255, nAlphaValue); + //two points in center + RwIm3DVertexSetPos(&TraceVertices[0], start2.x, start2.y, start2.z); + RwIm3DVertexSetPos(&TraceVertices[5], end2.x, end2.y, end2.z); + //vertical planes + RwIm3DVertexSetPos(&TraceVertices[1], start2.x, start2.y, start2.z + traceThickness); + RwIm3DVertexSetPos(&TraceVertices[3], start2.x, start2.y, start2.z - traceThickness); + RwIm3DVertexSetPos(&TraceVertices[6], end2.x, end2.y, end2.z + traceThickness); + RwIm3DVertexSetPos(&TraceVertices[8], end2.x, end2.y, end2.z - traceThickness); + //horizontal planes + RwIm3DVertexSetPos(&TraceVertices[2], start2.x + horizontalOffset.y, start2.y - horizontalOffset.x, start2.z); + RwIm3DVertexSetPos(&TraceVertices[7], end2.x + horizontalOffset.y, end2.y - horizontalOffset.x, end2.z); +#ifdef FIX_BUGS //this point calculated wrong for some reason + RwIm3DVertexSetPos(&TraceVertices[4], start2.x - horizontalOffset.y, start2.y + horizontalOffset.x, start2.z); + RwIm3DVertexSetPos(&TraceVertices[9], end2.x - horizontalOffset.y, end2.y + horizontalOffset.x, end2.z); +#else + RwIm3DVertexSetPos(&TraceVertices[4], start2.x - horizontalOffset.y, start2.y - horizontalOffset.y, start2.z); + RwIm3DVertexSetPos(&TraceVertices[9], end2.x - horizontalOffset.y, end2.y - horizontalOffset.y, end2.z); +#endif + + if (RwIm3DTransform(TraceVertices, ARRAY_SIZE(TraceVertices), nil, 1)) { + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TraceIndexList, ARRAY_SIZE(TraceIndexList)); + RwIm3DEnd(); + } + + RwIm3DVertexSetV(&TraceVertices[5], 2.0f); + RwIm3DVertexSetV(&TraceVertices[6], 2.0f); + RwIm3DVertexSetV(&TraceVertices[7], 2.0f); + RwIm3DVertexSetV(&TraceVertices[8], 2.0f); + RwIm3DVertexSetV(&TraceVertices[9], 2.0f); + RwIm3DVertexSetRGBA(&TraceVertices[0], 255, 255, 255, 0); + RwIm3DVertexSetRGBA(&TraceVertices[1], 255, 255, 255, 0); + RwIm3DVertexSetRGBA(&TraceVertices[2], 255, 255, 255, 0); + RwIm3DVertexSetRGBA(&TraceVertices[3], 255, 255, 255, 0); + RwIm3DVertexSetRGBA(&TraceVertices[4], 255, 255, 255, 0); + + RwIm3DVertexSetPos(&TraceVertices[0], start.x, start.y, start.z); + RwIm3DVertexSetPos(&TraceVertices[1], start.x, start.y, start.z + traceThickness); + RwIm3DVertexSetPos(&TraceVertices[3], start.x, start.y, start.z - traceThickness); + RwIm3DVertexSetPos(&TraceVertices[2], start.x + horizontalOffset.y, start.y - horizontalOffset.x, start.z); + + RwIm3DVertexSetPos(&TraceVertices[5], start2.x, start2.y, start2.z); + RwIm3DVertexSetPos(&TraceVertices[6], start2.x, start2.y, start2.z + traceThickness); + RwIm3DVertexSetPos(&TraceVertices[8], start2.x, start2.y, start2.z - traceThickness); + RwIm3DVertexSetPos(&TraceVertices[7], start2.x + horizontalOffset.y, start2.y - horizontalOffset.x, start2.z); +#ifdef FIX_BUGS + RwIm3DVertexSetPos(&TraceVertices[4], start.x - horizontalOffset.y, start.y + horizontalOffset.x, start.z); + RwIm3DVertexSetPos(&TraceVertices[9], start2.x - horizontalOffset.y, start2.y + horizontalOffset.x, start2.z); +#else + RwIm3DVertexSetPos(&TraceVertices[4], start.x - horizontalOffset.y, start.y - horizontalOffset.y, start.z); + RwIm3DVertexSetPos(&TraceVertices[9], start2.x - horizontalOffset.y, start2.y - horizontalOffset.y, start2.z); +#endif + + if (RwIm3DTransform(TraceVertices, ARRAY_SIZE(TraceVertices), nil, rwIM3D_VERTEXUV)) { + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TraceIndexList, ARRAY_SIZE(TraceIndexList)); + RwIm3DEnd(); + } + + RwIm3DVertexSetPos(&TraceVertices[1], end.x, end.y, end.z); + RwIm3DVertexSetPos(&TraceVertices[2], end.x, end.y, end.z + traceThickness); + RwIm3DVertexSetPos(&TraceVertices[4], end.x, end.y, end.z - traceThickness); + RwIm3DVertexSetPos(&TraceVertices[3], end.x + horizontalOffset.y, end.y - horizontalOffset.x, end.z); + + RwIm3DVertexSetPos(&TraceVertices[5], end2.x, end2.y, end2.z); + RwIm3DVertexSetPos(&TraceVertices[6], end2.x, end2.y, end2.z + traceThickness); + RwIm3DVertexSetPos(&TraceVertices[8], end2.x, end2.y, end2.z - traceThickness); + RwIm3DVertexSetPos(&TraceVertices[7], end2.x + horizontalOffset.y, end2.y - horizontalOffset.x, end2.z); +#ifdef FIX_BUGS + RwIm3DVertexSetPos(&TraceVertices[5], end.x - horizontalOffset.y, end.y + horizontalOffset.x, end.z); + RwIm3DVertexSetPos(&TraceVertices[9], end2.x - horizontalOffset.y, end2.y + horizontalOffset.x, end2.z); +#else + RwIm3DVertexSetPos(&TraceVertices[5], end.x - horizontalOffset.y, end.y - horizontalOffset.y, end.z); + RwIm3DVertexSetPos(&TraceVertices[9], end2.x - horizontalOffset.y, end2.y - horizontalOffset.y, end2.z); +#endif + + if (RwIm3DTransform(TraceVertices, ARRAY_SIZE(TraceVertices), nil, rwIM3D_VERTEXUV)) { + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TraceIndexList, ARRAY_SIZE(TraceIndexList)); + RwIm3DEnd(); + } + } + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); +} + +void CBulletTraces::Update(void) +{ + for (int i = 0; i < NUMBULLETTRACES; i++) { + if (aTraces[i].m_bInUse) + aTraces[i].Update(); + } +} + +void CBulletTrace::Update(void) +{ + if (CTimer::GetTimeInMilliseconds() - m_nCreationTime >= m_nLifeTime) + m_bInUse = false; +} + +RpAtomic * +MarkerAtomicCB(RpAtomic *atomic, void *data) +{ + *(RpAtomic**)data = atomic; + return atomic; +} + +bool +C3dMarker::AddMarker(uint32 identifier, uint16 type, float fSize, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate) +{ + m_nIdentifier = identifier; + + m_Matrix.SetUnity(); + + RpAtomic *origAtomic; + origAtomic = nil; + RpClumpForAllAtomics(C3dMarkers::m_pRpClumpArray[type], MarkerAtomicCB, &origAtomic); + + RpAtomic *atomic = RpAtomicClone(origAtomic); + RwFrame *frame = RwFrameCreate(); + RpAtomicSetFrame(atomic, frame); + CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); + + RpGeometry *geometry = RpAtomicGetGeometry(atomic); + RpGeometrySetFlags(geometry, RpGeometryGetFlags(geometry) | rpGEOMETRYMODULATEMATERIALCOLOR); + + m_pAtomic = atomic; + m_Matrix.Attach(RwFrameGetMatrix(RpAtomicGetFrame(m_pAtomic))); + m_pMaterial = RpGeometryGetMaterial(geometry, 0); + m_fSize = fSize; + m_fStdSize = m_fSize; + m_Color.red = r; + m_Color.green = g; + m_Color.blue = b; + m_Color.alpha = a; + m_nPulsePeriod = pulsePeriod; + m_fPulseFraction = pulseFraction; + m_nRotateRate = rotateRate; + m_nStartTime = CTimer::GetTimeInMilliseconds(); + m_nType = type; + return m_pAtomic != nil; +} + +void +C3dMarker::DeleteMarkerObject() +{ + RwFrame *frame; + + m_nIdentifier = 0; + m_nStartTime = 0; + m_bIsUsed = false; + m_bFindZOnNextPlacement = false; + m_nType = MARKERTYPE_INVALID; + + frame = RpAtomicGetFrame(m_pAtomic); + RpAtomicDestroy(m_pAtomic); + RwFrameDestroy(frame); + m_pAtomic = nil; +} + +void +C3dMarker::Render() +{ + if (m_pAtomic == nil) return; + + RpMaterialSetColor(m_pMaterial, &m_Color); + + m_Matrix.UpdateRW(); + + CMatrix matrix; + matrix.Attach(m_Matrix.m_attachment); + matrix.Scale(m_fSize); + matrix.UpdateRW(); + + RwFrameUpdateObjects(RpAtomicGetFrame(m_pAtomic)); + SetBrightMarkerColours(m_fBrightness); + if (m_nType != MARKERTYPE_ARROW) + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RpAtomicRender(m_pAtomic); + if (m_nType != MARKERTYPE_ARROW) + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + ReSetAmbientAndDirectionalColours(); +} + +C3dMarker C3dMarkers::m_aMarkerArray[NUM3DMARKERS]; +int32 C3dMarkers::NumActiveMarkers; +RpClump* C3dMarkers::m_pRpClumpArray[NUMMARKERTYPES]; + +void +C3dMarkers::Init() +{ + for (int i = 0; i < NUM3DMARKERS; i++) { + m_aMarkerArray[i].m_pAtomic = nil; + m_aMarkerArray[i].m_nType = MARKERTYPE_INVALID; + m_aMarkerArray[i].m_bIsUsed = false; + m_aMarkerArray[i].m_bFindZOnNextPlacement = false; + m_aMarkerArray[i].m_nIdentifier = 0; + m_aMarkerArray[i].m_Color.red = 255; + m_aMarkerArray[i].m_Color.green = 255; + m_aMarkerArray[i].m_Color.blue = 255; + m_aMarkerArray[i].m_Color.alpha = 255; + m_aMarkerArray[i].m_nPulsePeriod = 1024; + m_aMarkerArray[i].m_nRotateRate = 5; + m_aMarkerArray[i].m_nStartTime = 0; + m_aMarkerArray[i].m_fPulseFraction = 0.25f; + m_aMarkerArray[i].m_fStdSize = 1.0f; + m_aMarkerArray[i].m_fSize = 1.0f; + m_aMarkerArray[i].m_fBrightness = 1.0f; + m_aMarkerArray[i].m_fCameraRange = 0.0f; + } + NumActiveMarkers = 0; + int txdSlot = CTxdStore::FindTxdSlot("particle"); + CTxdStore::PushCurrentTxd(); + CTxdStore::SetCurrentTxd(txdSlot); + CFileMgr::ChangeDir("\\"); + m_pRpClumpArray[MARKERTYPE_ARROW] = CFileLoader::LoadAtomicFile2Return("models/generic/arrow.dff"); + m_pRpClumpArray[MARKERTYPE_CYLINDER] = CFileLoader::LoadAtomicFile2Return("models/generic/zonecylb.dff"); + CTxdStore::PopCurrentTxd(); +} + +void +C3dMarkers::Shutdown() +{ + for (int i = 0; i < NUM3DMARKERS; i++) { + if (m_aMarkerArray[i].m_pAtomic != nil) + m_aMarkerArray[i].DeleteMarkerObject(); + } + + for (int i = 0; i < NUMMARKERTYPES; i++) { + if (m_pRpClumpArray[i] != nil) + RpClumpDestroy(m_pRpClumpArray[i]); + } +} + +void +C3dMarkers::Render() +{ + NumActiveMarkers = 0; + ActivateDirectional(); + for (int i = 0; i < NUM3DMARKERS; i++) { + if (m_aMarkerArray[i].m_bIsUsed) { + if (m_aMarkerArray[i].m_fCameraRange < 150.0f) { + m_aMarkerArray[i].Render(); + if (m_aMarkerArray[i].m_nType == MARKERTYPE_ARROW) { + CCoronas::RegisterCorona((uintptr)&m_aMarkerArray[i], + SPHERE_MARKER_R, SPHERE_MARKER_G, SPHERE_MARKER_B, 192, + m_aMarkerArray[i].m_Matrix.GetPosition(), 1.2f * m_aMarkerArray[i].m_fSize, 50.0f * TheCamera.LODDistMultiplier, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f, false); + } + } + NumActiveMarkers++; + m_aMarkerArray[i].m_bIsUsed = false; + } else if (m_aMarkerArray[i].m_pAtomic != nil) { + m_aMarkerArray[i].DeleteMarkerObject(); + } + } +} + +C3dMarker * +C3dMarkers::PlaceMarker(uint32 identifier, uint16 type, CVector &pos, float size, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate) +{ + C3dMarker *pMarker; + CVector2D playerPos = FindPlayerCentreOfWorld(0); + pMarker = nil; + float dist = ((CVector2D)pos - playerPos).Magnitude(); + + if (type != MARKERTYPE_ARROW && type != MARKERTYPE_CYLINDER) return nil; + + for (int i = 0; i < NUM3DMARKERS; i++) { + if (!m_aMarkerArray[i].m_bIsUsed && m_aMarkerArray[i].m_nIdentifier == identifier) { + pMarker = &m_aMarkerArray[i]; + break; + } + } + + if (pMarker == nil) { + for (int i = 0; i < NUM3DMARKERS; i++) { + if (m_aMarkerArray[i].m_nType == MARKERTYPE_INVALID) { + pMarker = &m_aMarkerArray[i]; + break; + } + } + } + + if (pMarker == nil && type == MARKERTYPE_ARROW) { + for (int i = 0; i < NUM3DMARKERS; i++) { + if (dist < m_aMarkerArray[i].m_fCameraRange && m_aMarkerArray[i].m_nType == MARKERTYPE_ARROW && (pMarker == nil || m_aMarkerArray[i].m_fCameraRange > pMarker->m_fCameraRange)) { + pMarker = &m_aMarkerArray[i]; + break; + } + } + + if (pMarker != nil) + pMarker->m_nType = MARKERTYPE_INVALID; + } + + if (pMarker == nil) return pMarker; + + pMarker->m_fCameraRange = dist; + if (pMarker->m_nIdentifier == identifier && pMarker->m_nType == type) { + if (type == MARKERTYPE_ARROW) { + if (dist < 25.0f) { + if (dist > 5.0f) + pMarker->m_fStdSize = size - (25.0f - dist) * (0.3f * size) / 20.0f; + else + pMarker->m_fStdSize = size - 0.3f * size; + } else { + pMarker->m_fStdSize = size; + } + } else if (type == MARKERTYPE_CYLINDER) { + if (dist < size + 12.0f) { + if (dist > size + 1.0f) + pMarker->m_Color.alpha = (1.0f - (size + 12.0f - dist) * 0.7f / 11.0f) * (float)a; + else + pMarker->m_Color.alpha = (float)a * 0.3f; + } else { + pMarker->m_Color.alpha = a; + } + } + float someSin = Sin(TWOPI * (float)((pMarker->m_nPulsePeriod - 1) & (CTimer::GetTimeInMilliseconds() - pMarker->m_nStartTime)) / (float)pMarker->m_nPulsePeriod); + pMarker->m_fSize = pMarker->m_fStdSize - pulseFraction * pMarker->m_fStdSize * someSin; + + if (type == MARKERTYPE_ARROW) { + pos.z += 0.25f * pMarker->m_fStdSize * someSin; + } else if (type == MARKERTYPE_0) { + if (someSin > 0.0f) + pMarker->m_Color.alpha = (float)a * 0.7f * someSin + a; + else + pMarker->m_Color.alpha = (float)a * 0.4f * someSin + a; + } + if (pMarker->m_nRotateRate != 0) { + CVector pos = pMarker->m_Matrix.GetPosition(); + pMarker->m_Matrix.RotateZ(DEGTORAD(pMarker->m_nRotateRate * CTimer::GetTimeStep())); + pMarker->m_Matrix.GetPosition() = pos; + } + if (type == MARKERTYPE_ARROW) + pMarker->m_Matrix.GetPosition() = pos; + + if (pMarker->m_bFindZOnNextPlacement) { + if ((playerPos - pos).MagnitudeSqr() < sq(100.f) && CColStore::HasCollisionLoaded(CVector2D(pos))) { + float z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z + 1.0f, nil); + if (z != 0.0f) + pMarker->m_Matrix.GetPosition().z = z - 0.05f * size; + pMarker->m_bFindZOnNextPlacement = false; + } + } + pMarker->m_bIsUsed = true; + return pMarker; + } + + if (pMarker->m_nIdentifier != 0) + pMarker->DeleteMarkerObject(); + + pMarker->AddMarker(identifier, type, size, r, g, b, a, pulsePeriod, pulseFraction, rotateRate); + if (type == MARKERTYPE_CYLINDER || type == MARKERTYPE_0 || type == MARKERTYPE_2) { + if ((playerPos - pos).MagnitudeSqr() < sq(100.f) && CColStore::HasCollisionLoaded(CVector2D(pos))) { + float z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z + 1.0f, nil); + if (z != 0.0f) + pos.z = z - 0.05f * size; + pMarker->m_bFindZOnNextPlacement = false; + } else { + pMarker->m_bFindZOnNextPlacement = true; + } + } + pMarker->m_Matrix.SetTranslate(pos.x, pos.y, pos.z); + if (type == MARKERTYPE_2) { + pMarker->m_Matrix.RotateX(PI); + pMarker->m_Matrix.GetPosition() = pos; + } + pMarker->m_Matrix.UpdateRW(); + if (type == MARKERTYPE_ARROW) { + if (dist < 25.0f) { + if (dist > 5.0f) + pMarker->m_fStdSize = size - (25.0f - dist) * (0.3f * size) / 20.0f; + else + pMarker->m_fStdSize = size - 0.3f * size; + } else { + pMarker->m_fStdSize = size; + } + } else if (type == MARKERTYPE_CYLINDER) { + if (dist < size + 12.0f) { + if (dist > size + 1.0f) + pMarker->m_Color.alpha = (1.0f - (size + 12.0f - dist) * 0.7f / 11.0f) * (float)a; + else + pMarker->m_Color.alpha = (float)a * 0.3f; + } else { + pMarker->m_Color.alpha = a; + } + } + pMarker->m_bIsUsed = true; + return pMarker; +} + +void +C3dMarkers::PlaceMarkerSet(uint32 id, uint16 type, CVector &pos, float size, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate) +{ + PlaceMarker(id, type, pos, size, r, g, b, a, pulsePeriod, pulseFraction, 1); + PlaceMarker(id, type, pos, size * 0.93f, r, g, b, a, pulsePeriod, pulseFraction, 2); + PlaceMarker(id, type, pos, size * 0.86f, r, g, b, a, pulsePeriod, pulseFraction, -1); +} + + +void +C3dMarkers::Update() +{ +} + + +#define BRIGHTLIGHTS_MAX_DIST (60.0f) // invisible beyond this +#define BRIGHTLIGHTS_FADE_DIST (45.0f) // strongest between these two +#define CARLIGHTS_MAX_DIST (30.0f) +#define CARLIGHTS_FADE_DIST (15.0f) // 31 for close lights + +int CBrightLights::NumBrightLights; +CBrightLight CBrightLights::aBrightLights[NUMBRIGHTLIGHTS]; + +void +CBrightLights::Init(void) +{ + NumBrightLights = 0; +} + +void +CBrightLights::RegisterOne(CVector pos, CVector up, CVector side, CVector front, + uint8 type, uint8 red, uint8 green, uint8 blue) +{ + if(NumBrightLights >= NUMBRIGHTLIGHTS) + return; + + aBrightLights[NumBrightLights].m_camDist = (pos - TheCamera.GetPosition()).Magnitude(); + if(aBrightLights[NumBrightLights].m_camDist > BRIGHTLIGHTS_MAX_DIST) + return; + + aBrightLights[NumBrightLights].m_pos = pos; + aBrightLights[NumBrightLights].m_up = up; + aBrightLights[NumBrightLights].m_side = side; + aBrightLights[NumBrightLights].m_front = front; + aBrightLights[NumBrightLights].m_type = type; + aBrightLights[NumBrightLights].m_red = red; + aBrightLights[NumBrightLights].m_green = green; + aBrightLights[NumBrightLights].m_blue = blue; + + NumBrightLights++; +} + +static float TrafficLightsSide[6] = { -0.09f, 0.09f, 0.162f, 0.09f, -0.09f, -0.162f }; +static float TrafficLightsUp[6] = { 0.162f, 0.162f, 0.0f, -0.162f, -0.162f, 0.0f }; +static float LongCarHeadLightsSide[8] = { -0.2f, 0.2f, -0.2f, 0.2f, -0.2f, 0.2f, -0.2f, 0.2f }; +static float LongCarHeadLightsFront[8] = { 0.1f, 0.1f, -0.1f, -0.1f, 0.1f, 0.1f, -0.1f, -0.1f }; +static float LongCarHeadLightsUp[8] = { 0.1f, 0.1f, 0.1f, 0.1f, -0.1f, -0.1f, -0.1f, -0.1f }; +static float SmallCarHeadLightsSide[8] = { -0.08f, 0.08f, -0.08f, 0.08f, -0.08f, 0.08f, -0.08f, 0.08f }; +static float SmallCarHeadLightsFront[8] = { 0.08f, 0.08f, -0.08f, -0.08f, 0.08f, 0.08f, -0.08f, -0.08f }; +static float SmallCarHeadLightsUp[8] = { 0.08f, 0.08f, 0.08f, 0.08f, -0.08f, -0.08f, -0.08f, -0.08f }; +static float BigCarHeadLightsSide[8] = { -0.15f, 0.15f, -0.15f, 0.15f, -0.15f, 0.15f, -0.15f, 0.15f }; +static float BigCarHeadLightsFront[8] = { 0.15f, 0.15f, -0.15f, -0.15f, 0.15f, 0.15f, -0.15f, -0.15f }; +static float BigCarHeadLightsUp[8] = { 0.15f, 0.15f, 0.15f, 0.15f, -0.15f, -0.15f, -0.15f, -0.15f }; +static float TallCarHeadLightsSide[8] = { -0.08f, 0.08f, -0.08f, 0.08f, -0.08f, 0.08f, -0.08f, 0.08f }; +static float TallCarHeadLightsFront[8] = { 0.08f, 0.08f, -0.08f, -0.08f, 0.08f, 0.08f, -0.08f, -0.08f }; +static float TallCarHeadLightsUp[8] = { 0.2f, 0.2f, 0.2f, 0.2f, -0.2f, -0.2f, -0.2f, -0.2f }; +static float SirenLightsSide[6] = { -0.04f, 0.04f, 0.06f, 0.04f, -0.04f, -0.06f }; +static float SirenLightsUp[6] = { 0.06f, 0.06f, 0.0f, -0.06f, -0.06f, 0.0f }; +static RwImVertexIndex TrafficLightIndices[4*3] = { 0, 1, 5, 1, 2, 3, 1, 3, 4, 1, 4, 5 }; +static RwImVertexIndex CubeIndices[12*3] = { + 0, 2, 1, 1, 2, 3, 3, 5, 1, 3, 7, 5, + 2, 7, 3, 2, 6, 7, 4, 0, 1, 4, 1, 5, + 6, 0, 4, 6, 2, 0, 6, 5, 7, 6, 4, 5 +}; + +void +CBrightLights::Render(void) +{ + int i, j; + CVector pos; + + if(NumBrightLights == 0) + return; + + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + + TempBufferVerticesStored = 0; + TempBufferIndicesStored = 0; + + for(i = 0; i < NumBrightLights; i++){ + if(TempBufferIndicesStored > TEMPBUFFERINDEXSIZE-40 || TempBufferVerticesStored > TEMPBUFFERVERTSIZE-40) + RenderOutGeometryBuffer(); + + int r, g, b, a; + float flicker = (CGeneral::GetRandomNumber()&0xFF) * 0.2f; + switch(aBrightLights[i].m_type){ + case BRIGHTLIGHT_TRAFFIC_GREEN: + r = flicker; g = 255; b = flicker; + break; + case BRIGHTLIGHT_TRAFFIC_YELLOW: + r = 255; g = 128; b = flicker; + break; + case BRIGHTLIGHT_TRAFFIC_RED: + r = 255; g = flicker; b = flicker; + break; + + case BRIGHTLIGHT_FRONT_LONG: + case BRIGHTLIGHT_FRONT_SMALL: + case BRIGHTLIGHT_FRONT_BIG: + case BRIGHTLIGHT_FRONT_TALL: + r = 255; g = 255; b = 255; + break; + + case BRIGHTLIGHT_REAR_LONG: + case BRIGHTLIGHT_REAR_SMALL: + case BRIGHTLIGHT_REAR_BIG: + case BRIGHTLIGHT_REAR_TALL: + r = 255; g = flicker; b = flicker; + break; + + case BRIGHTLIGHT_SIREN: + r = aBrightLights[i].m_red; + g = aBrightLights[i].m_green; + b = aBrightLights[i].m_blue; + break; +#ifdef FIX_BUGS //just to make sure that color never will be undefined + default: + return; +#endif + } + + if(aBrightLights[i].m_camDist < BRIGHTLIGHTS_FADE_DIST) + a = 255; + else + a = 255*(1.0f - (aBrightLights[i].m_camDist-BRIGHTLIGHTS_FADE_DIST)/(BRIGHTLIGHTS_MAX_DIST-BRIGHTLIGHTS_FADE_DIST)); + // fade car lights down to 31 as they come near + if(aBrightLights[i].m_type >= BRIGHTLIGHT_FRONT_LONG && aBrightLights[i].m_type <= BRIGHTLIGHT_REAR_TALL){ + if(aBrightLights[i].m_camDist < CARLIGHTS_FADE_DIST) + a = 31; + else if(aBrightLights[i].m_camDist < CARLIGHTS_MAX_DIST) + a = 31 + (255-31)*((aBrightLights[i].m_camDist-CARLIGHTS_FADE_DIST)/(CARLIGHTS_MAX_DIST-CARLIGHTS_FADE_DIST)); + } + + switch(aBrightLights[i].m_type){ + case BRIGHTLIGHT_TRAFFIC_GREEN: + case BRIGHTLIGHT_TRAFFIC_YELLOW: + case BRIGHTLIGHT_TRAFFIC_RED: + for(j = 0; j < 6; j++){ + pos = TrafficLightsSide[j]*aBrightLights[i].m_side + + TrafficLightsUp[j]*aBrightLights[i].m_up + + aBrightLights[i].m_pos; + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+j], r, g, b, a); + RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+j], pos.x, pos.y, pos.z); + } + for(j = 0; j < 4*3; j++) + TempBufferRenderIndexList[TempBufferIndicesStored+j] = TrafficLightIndices[j] + TempBufferVerticesStored; + TempBufferVerticesStored += 6; + TempBufferIndicesStored += 4*3; + break; + + case BRIGHTLIGHT_FRONT_LONG: + case BRIGHTLIGHT_REAR_LONG: + for(j = 0; j < 8; j++){ + pos = LongCarHeadLightsSide[j]*aBrightLights[i].m_side + + LongCarHeadLightsUp[j]*aBrightLights[i].m_up + + LongCarHeadLightsFront[j]*aBrightLights[i].m_front + + aBrightLights[i].m_pos; + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+j], r, g, b, a); + RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+j], pos.x, pos.y, pos.z); + } + for(j = 0; j < 12*3; j++) + TempBufferRenderIndexList[TempBufferIndicesStored+j] = CubeIndices[j] + TempBufferVerticesStored; + TempBufferVerticesStored += 8; + TempBufferIndicesStored += 12*3; + break; + + case BRIGHTLIGHT_FRONT_SMALL: + case BRIGHTLIGHT_REAR_SMALL: + for(j = 0; j < 8; j++){ + pos = SmallCarHeadLightsSide[j]*aBrightLights[i].m_side + + SmallCarHeadLightsUp[j]*aBrightLights[i].m_up + + SmallCarHeadLightsFront[j]*aBrightLights[i].m_front + + aBrightLights[i].m_pos; + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+j], r, g, b, a); + RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+j], pos.x, pos.y, pos.z); + } + for(j = 0; j < 12*3; j++) + TempBufferRenderIndexList[TempBufferIndicesStored+j] = CubeIndices[j] + TempBufferVerticesStored; + TempBufferVerticesStored += 8; + TempBufferIndicesStored += 12*3; + break; + + case BRIGHTLIGHT_FRONT_BIG: + case BRIGHTLIGHT_REAR_BIG: + for (j = 0; j < 8; j++) { + pos = BigCarHeadLightsSide[j] * aBrightLights[i].m_side + + BigCarHeadLightsUp[j] * aBrightLights[i].m_up + + BigCarHeadLightsFront[j] * aBrightLights[i].m_front + + aBrightLights[i].m_pos; + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored + j], r, g, b, a); + RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored + j], pos.x, pos.y, pos.z); + } + for (j = 0; j < 12 * 3; j++) + TempBufferRenderIndexList[TempBufferIndicesStored + j] = CubeIndices[j] + TempBufferVerticesStored; + TempBufferVerticesStored += 8; + TempBufferIndicesStored += 12 * 3; + break; + + case BRIGHTLIGHT_FRONT_TALL: + case BRIGHTLIGHT_REAR_TALL: + for(j = 0; j < 8; j++){ + pos = TallCarHeadLightsSide[j]*aBrightLights[i].m_side + + TallCarHeadLightsUp[j]*aBrightLights[i].m_up + + TallCarHeadLightsFront[j]*aBrightLights[i].m_front + + aBrightLights[i].m_pos; + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+j], r, g, b, a); + RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+j], pos.x, pos.y, pos.z); + } + for(j = 0; j < 12*3; j++) + TempBufferRenderIndexList[TempBufferIndicesStored+j] = CubeIndices[j] + TempBufferVerticesStored; + TempBufferVerticesStored += 8; + TempBufferIndicesStored += 12*3; + break; + + case BRIGHTLIGHT_SIREN: + for(j = 0; j < 6; j++){ + pos = SirenLightsSide[j] * TheCamera.GetRight() + + SirenLightsUp[j] * TheCamera.GetUp() + + aBrightLights[i].m_pos; + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+j], r, g, b, a); + RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+j], pos.x, pos.y, pos.z); + } + for(j = 0; j < 4*3; j++) + TempBufferRenderIndexList[TempBufferIndicesStored+j] = TrafficLightIndices[j] + TempBufferVerticesStored; + TempBufferVerticesStored += 6; + TempBufferIndicesStored += 4*3; + break; + + } + } + + RenderOutGeometryBuffer(); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + NumBrightLights = 0; +} + +void +CBrightLights::RenderOutGeometryBuffer(void) +{ + if(TempBufferIndicesStored != 0){ + LittleTest(); + if(RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, rwIM3D_VERTEXUV)){ + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored); + RwIm3DEnd(); + } + TempBufferVerticesStored = 0; + TempBufferIndicesStored = 0; + } +} + +int CShinyTexts::NumShinyTexts; +CShinyText CShinyTexts::aShinyTexts[NUMSHINYTEXTS]; + +void +CShinyTexts::Init(void) +{ + NumShinyTexts = 0; +} + +void +CShinyTexts::RegisterOne(CVector p0, CVector p1, CVector p2, CVector p3, + float u0, float v0, float u1, float v1, float u2, float v2, float u3, float v3, + uint8 type, uint8 red, uint8 green, uint8 blue, float maxDist) +{ + if(NumShinyTexts >= NUMSHINYTEXTS) + return; + + aShinyTexts[NumShinyTexts].m_camDist = (p0 - TheCamera.GetPosition()).Magnitude(); + if(aShinyTexts[NumShinyTexts].m_camDist > maxDist) + return; + aShinyTexts[NumShinyTexts].m_verts[0] = p0; + aShinyTexts[NumShinyTexts].m_verts[1] = p1; + aShinyTexts[NumShinyTexts].m_verts[2] = p2; + aShinyTexts[NumShinyTexts].m_verts[3] = p3; + aShinyTexts[NumShinyTexts].m_texCoords[0].x = u0; + aShinyTexts[NumShinyTexts].m_texCoords[0].y = v0; + aShinyTexts[NumShinyTexts].m_texCoords[1].x = u1; + aShinyTexts[NumShinyTexts].m_texCoords[1].y = v1; + aShinyTexts[NumShinyTexts].m_texCoords[2].x = u2; + aShinyTexts[NumShinyTexts].m_texCoords[2].y = v2; + aShinyTexts[NumShinyTexts].m_texCoords[3].x = u3; + aShinyTexts[NumShinyTexts].m_texCoords[3].y = v3; + aShinyTexts[NumShinyTexts].m_type = type; + aShinyTexts[NumShinyTexts].m_red = red; + aShinyTexts[NumShinyTexts].m_green = green; + aShinyTexts[NumShinyTexts].m_blue = blue; + // Fade out at half the max dist + float halfDist = maxDist*0.5f; + if(aShinyTexts[NumShinyTexts].m_camDist > halfDist){ + float f = 1.0f - (aShinyTexts[NumShinyTexts].m_camDist - halfDist)/halfDist; + aShinyTexts[NumShinyTexts].m_red *= f; + aShinyTexts[NumShinyTexts].m_green *= f; + aShinyTexts[NumShinyTexts].m_blue *= f; + } + + NumShinyTexts++; +} + +void +CShinyTexts::Render(void) +{ + int i, ix, v; + RwTexture *lastTex = nil; + + if(NumShinyTexts == 0) + return; + + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + + TempBufferVerticesStored = 0; + TempBufferIndicesStored = 0; + + for(i = 0; i < NumShinyTexts; i++){ + if(TempBufferIndicesStored > TEMPBUFFERINDEXSIZE-64 || TempBufferVerticesStored > TEMPBUFFERVERTSIZE-62) + RenderOutGeometryBuffer(); + + uint8 r = aShinyTexts[i].m_red; + uint8 g = aShinyTexts[i].m_green; + uint8 b = aShinyTexts[i].m_blue; + + switch(aShinyTexts[i].m_type){ + case SHINYTEXT_WALK: + if(lastTex != gpWalkDontTex){ + RenderOutGeometryBuffer(); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpWalkDontTex)); + lastTex = gpWalkDontTex; + } + quad: + v = TempBufferVerticesStored; + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+0], r, g, b, 255); + RwIm3DVertexSetPos(&TempBufferRenderVertices[v+0], aShinyTexts[i].m_verts[0].x, aShinyTexts[i].m_verts[0].y, aShinyTexts[i].m_verts[0].z); + RwIm3DVertexSetU(&TempBufferRenderVertices[v+0], aShinyTexts[i].m_texCoords[0].x); + RwIm3DVertexSetV(&TempBufferRenderVertices[v+0], aShinyTexts[i].m_texCoords[0].y); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+1], r, g, b, 255); + RwIm3DVertexSetPos(&TempBufferRenderVertices[v+1], aShinyTexts[i].m_verts[1].x, aShinyTexts[i].m_verts[1].y, aShinyTexts[i].m_verts[1].z); + RwIm3DVertexSetU(&TempBufferRenderVertices[v+1], aShinyTexts[i].m_texCoords[1].x); + RwIm3DVertexSetV(&TempBufferRenderVertices[v+1], aShinyTexts[i].m_texCoords[1].y); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+2], r, g, b, 255); + RwIm3DVertexSetPos(&TempBufferRenderVertices[v+2], aShinyTexts[i].m_verts[2].x, aShinyTexts[i].m_verts[2].y, aShinyTexts[i].m_verts[2].z); + RwIm3DVertexSetU(&TempBufferRenderVertices[v+2], aShinyTexts[i].m_texCoords[2].x); + RwIm3DVertexSetV(&TempBufferRenderVertices[v+2], aShinyTexts[i].m_texCoords[2].y); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+3], r, g, b, 255); + RwIm3DVertexSetPos(&TempBufferRenderVertices[v+3], aShinyTexts[i].m_verts[3].x, aShinyTexts[i].m_verts[3].y, aShinyTexts[i].m_verts[3].z); + RwIm3DVertexSetU(&TempBufferRenderVertices[v+3], aShinyTexts[i].m_texCoords[3].x); + RwIm3DVertexSetV(&TempBufferRenderVertices[v+3], aShinyTexts[i].m_texCoords[3].y); + ix = TempBufferIndicesStored; + TempBufferRenderIndexList[ix+0] = 0 + TempBufferVerticesStored; + TempBufferRenderIndexList[ix+1] = 1 + TempBufferVerticesStored; + TempBufferRenderIndexList[ix+2] = 2 + TempBufferVerticesStored; + TempBufferRenderIndexList[ix+3] = 2 + TempBufferVerticesStored; + TempBufferRenderIndexList[ix+4] = 1 + TempBufferVerticesStored; + TempBufferRenderIndexList[ix+5] = 3 + TempBufferVerticesStored; + TempBufferVerticesStored += 4; + TempBufferIndicesStored += 6; + break; + + case SHINYTEXT_FLAT: + if(lastTex != nil){ + RenderOutGeometryBuffer(); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + lastTex = nil; + } + goto quad; + } + } + + RenderOutGeometryBuffer(); + NumShinyTexts = 0; + + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); +} + +void +CShinyTexts::RenderOutGeometryBuffer(void) +{ + if(TempBufferIndicesStored != 0){ + LittleTest(); + if(RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, rwIM3D_VERTEXUV)){ + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored); + RwIm3DEnd(); + } + TempBufferVerticesStored = 0; + TempBufferIndicesStored = 0; + } +} + + + +#define MONEY_MESSAGE_LIFETIME_MS 2000 + +CMoneyMessage CMoneyMessages::aMoneyMessages[NUMMONEYMESSAGES]; + +void +CMoneyMessage::Render() +{ + const float MAX_SCALE = 4.0f; + uint32 nLifeTime = CTimer::GetTimeInMilliseconds() - m_nTimeRegistered; + if (nLifeTime >= MONEY_MESSAGE_LIFETIME_MS) { + m_nTimeRegistered = 0; + } else { + float fLifeTime = (float)nLifeTime / MONEY_MESSAGE_LIFETIME_MS; + RwV3d vecOut; + float fDistX, fDistY; + if (CSprite::CalcScreenCoors(m_vecPosition + CVector(0.0f, 0.0f, fLifeTime), &vecOut, &fDistX, &fDistY, true)) { + fDistX *= (0.7f * fLifeTime + 2.0f) * m_fSize; + fDistY *= (0.7f * fLifeTime + 2.0f) * m_fSize; + CFont::SetPropOn(); + CFont::SetBackgroundOff(); + float fScaleY = Min(fDistY / 100.0f, MAX_SCALE); + float fScaleX = Min(fDistX / 100.0f, MAX_SCALE); + +#ifdef FIX_BUGS + CFont::SetScale(SCREEN_SCALE_X(fScaleX), SCREEN_SCALE_Y(fScaleY)); +#else + CFont::SetScale(fScaleX, fScaleY); +#endif + CFont::SetCentreOn(); + CFont::SetCentreSize(SCREEN_WIDTH); + CFont::SetJustifyOff(); + CFont::SetColor(CRGBA(m_Colour.r, m_Colour.g, m_Colour.b, (255.0f - 255.0f * fLifeTime) * m_fOpacity)); + CFont::SetBackGroundOnlyTextOff(); + FONT_LOCALE(FONT_STANDARD); + CFont::PrintString(vecOut.x, vecOut.y, m_aText); + } + } +} + +void +CMoneyMessages::Init() +{ + for (int32 i = 0; i < NUMMONEYMESSAGES; i++) + aMoneyMessages[i].m_nTimeRegistered = 0; +} + +void +CMoneyMessages::Render() +{ + for (int32 i = 0; i < NUMMONEYMESSAGES; i++) { + if (aMoneyMessages[i].m_nTimeRegistered != 0) + aMoneyMessages[i].Render(); + } +} + +void +CMoneyMessages::RegisterOne(CVector vecPos, const char *pText, uint8 bRed, uint8 bGreen, uint8 bBlue, float fSize, float fOpacity) +{ + uint32 i; +#ifdef FIX_BUGS + for(i = 0; i < NUMMONEYMESSAGES && aMoneyMessages[i].m_nTimeRegistered != 0; i++); +#else + for(i = 0; aMoneyMessages[i].m_nTimeRegistered != 0 && i < NUMMONEYMESSAGES; i++); +#endif + + if(i < NUMMONEYMESSAGES) { + // Add data of this money message to the array + AsciiToUnicode(pText, aMoneyMessages[i].m_aText); + + aMoneyMessages[i].m_nTimeRegistered = CTimer::GetTimeInMilliseconds(); + aMoneyMessages[i].m_vecPosition = vecPos; + aMoneyMessages[i].m_Colour.red = bRed; + aMoneyMessages[i].m_Colour.green = bGreen; + aMoneyMessages[i].m_Colour.blue = bBlue; + aMoneyMessages[i].m_fSize = fSize; + aMoneyMessages[i].m_fOpacity = fOpacity; + } +} + +CRGBA FoamColour(255, 255, 255, 255); +uint32 CSpecialParticleStuff::BoatFromStart; + +void +CSpecialParticleStuff::CreateFoamAroundObject(CMatrix* pMatrix, float innerFw, float innerRg, float innerUp, int32 particles) +{ + float outerFw = innerFw + 5.0f; + float outerRg = innerRg + 5.0f; + float outerUp = innerUp + 5.0f; + for (int attempts = 0; particles > 0 && attempts < 1000; attempts++) { + CVector pos; + int rnd = CGeneral::GetRandomNumber(); + pos.x = (int8)(rnd - 128) * innerFw / 110.0f; + pos.y = (int8)((rnd >> 8) - 128) * innerFw / 110.0f; + pos.z = 0.0f; + if (DotProduct2D(pos, TheCamera.GetForward()) >= 0) + continue; + // was there any point in adding it here? + pos += pMatrix->GetPosition(); + pos.z = 2.0f; + float fw = Abs(DotProduct(pMatrix->GetForward(), pos - pMatrix->GetPosition())); + if (fw >= outerFw) + continue; + float rg = Abs(DotProduct(pMatrix->GetRight(), pos - pMatrix->GetPosition())); + if (rg >= outerRg) + continue; + float up = Abs(DotProduct(pMatrix->GetUp(), pos - pMatrix->GetPosition())); + if (up >= outerUp) + continue; + if (fw > innerFw || rg > innerRg || up > innerUp) { + CParticle::AddParticle(PARTICLE_STEAM2, pos, CVector(0.0f, 0.0f, 0.0f), nil, 4.0f, FoamColour, 1, 0, 0, 0); + particles--; + } + } +} + +void +CSpecialParticleStuff::StartBoatFoamAnimation() +{ + BoatFromStart = CTimer::GetTimeInMilliseconds(); +} + +void +CSpecialParticleStuff::UpdateBoatFoamAnimation(CMatrix* pMatrix) +{ + static int32 FrameInAnimation = 0; + static float X, Y, Z, dX, dY, dZ; + CreateFoamAroundObject(pMatrix, 107.0f, 24.1f, 30.5f, 2); + uint32 prev = CTimer::GetPreviousTimeInMilliseconds(); + uint32 cur = CTimer::GetTimeInMilliseconds(); + if (FrameInAnimation != 0) { + X += dX; + Y += dY; + Z += dZ; + CVector pos = *pMatrix * CVector(X, Y, Z); + CParticle::AddParticle(PARTICLE_STEAM_NY, pos, CVector(0.0f, 0.0f, 0.0f), + nil, FrameInAnimation * 0.5f + 2.0f, FoamColour, 1, 0, 0, 0); + if (++FrameInAnimation > 15) + FrameInAnimation = 0; + } + if ((cur & 0x3FF) < (prev & 0x3FF)) { + FrameInAnimation = 1; + int rnd = CGeneral::GetRandomNumber(); + X = (int8)(rnd - 128) * 0.2f; + Y = (int8)((rnd >> 8) - 128) * 0.2f; + Z = 10.0f; + rnd = CGeneral::GetRandomNumber(); + dX = (int8)(rnd - 128) * 0.02f; + dY = (int8)((rnd >> 8) - 128) * 0.02f; + dZ = 2.0f; + } +} diff --git a/src/miami/renderer/SpecialFX.h b/src/miami/renderer/SpecialFX.h new file mode 100644 index 00000000..f163d8ca --- /dev/null +++ b/src/miami/renderer/SpecialFX.h @@ -0,0 +1,249 @@ +#pragma once + +//file done + +class CSpecialFX +{ +public: + static bool bVideoCam; + static bool bLiftCam; + static bool bSnapShotActive; + static int32 SnapShotFrames; + + static void Render(void); + static void Update(void); + static void Init(void); + static void Shutdown(void); + static void AddWeaponStreak(int type); + static void Render2DFXs(); +}; + + +class CRegisteredMotionBlurStreak +{ +public: + uintptr m_id; + uint8 m_red; + uint8 m_green; + uint8 m_blue; + CVector m_pos1[3]; + CVector m_pos2[3]; + bool m_isValid[3]; + + void Update(void); + void Render(void); +}; + + +class CMotionBlurStreaks +{ + static CRegisteredMotionBlurStreak aStreaks[NUMMBLURSTREAKS]; +public: + static void Init(void); + static void Update(void); + static void RegisterStreak(uintptr id, uint8 r, uint8 g, uint8 b, CVector p1, CVector p2); + static void Render(void); +}; + + +struct CBulletTrace +{ + CVector m_vecStartPos; + CVector m_vecEndPos; + bool m_bInUse; + uint32 m_nCreationTime; + uint32 m_nLifeTime; + float m_fThickness; + uint8 m_fVisibility; + + void Update(void); +}; + + +class CBulletTraces +{ +public: + static CBulletTrace aTraces[NUMBULLETTRACES]; + + static void Init(void); + static void Render(void); + static void Update(void); + static void AddTrace(CVector* start, CVector* end, float thickness, uint32 lifeTime, uint8 visibility); + static void AddTrace(CVector* start, CVector* end, int32 weaponType, class CEntity* shooter); +}; + +enum +{ + MARKERTYPE_0 = 0, + MARKERTYPE_ARROW, + MARKERTYPE_2, + MARKERTYPE_3, + MARKERTYPE_CYLINDER, + NUMMARKERTYPES, + + MARKERTYPE_INVALID = 0x101 +}; + + +class C3dMarker +{ +public: + CMatrix m_Matrix; + RpAtomic *m_pAtomic; + RpMaterial *m_pMaterial; + uint16 m_nType; + bool m_bIsUsed; + bool m_bFindZOnNextPlacement; + uint32 m_nIdentifier; + RwRGBA m_Color; + uint16 m_nPulsePeriod; + int16 m_nRotateRate; + uint32 m_nStartTime; + float m_fPulseFraction; + float m_fStdSize; + float m_fSize; + float m_fBrightness; + float m_fCameraRange; + + bool AddMarker(uint32 identifier, uint16 type, float fSize, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate); + void DeleteMarkerObject(); + void Render(); +}; + + +class C3dMarkers +{ +public: + static void Init(); + static void Shutdown(); + static C3dMarker *PlaceMarker(uint32 id, uint16 type, CVector &pos, float size, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate); + static void PlaceMarkerSet(uint32 id, uint16 type, CVector &pos, float size, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate); + static void Render(); + static void Update(); + + static C3dMarker m_aMarkerArray[NUM3DMARKERS]; + static int32 NumActiveMarkers; + static RpClump* m_pRpClumpArray[NUMMARKERTYPES]; +}; + +enum +{ + BRIGHTLIGHT_INVALID, + BRIGHTLIGHT_TRAFFIC_GREEN, + BRIGHTLIGHT_TRAFFIC_YELLOW, + BRIGHTLIGHT_TRAFFIC_RED, + + // white + BRIGHTLIGHT_FRONT_LONG, + BRIGHTLIGHT_FRONT_SMALL, + BRIGHTLIGHT_FRONT_BIG, + BRIGHTLIGHT_FRONT_TALL, + + // red + BRIGHTLIGHT_REAR_LONG, + BRIGHTLIGHT_REAR_SMALL, + BRIGHTLIGHT_REAR_BIG, + BRIGHTLIGHT_REAR_TALL, + + BRIGHTLIGHT_SIREN, // unused + + BRIGHTLIGHT_FRONT = BRIGHTLIGHT_FRONT_LONG, + BRIGHTLIGHT_REAR = BRIGHTLIGHT_REAR_LONG, +}; + + +class CBrightLight +{ +public: + CVector m_pos; + CVector m_up; + CVector m_side; + CVector m_front; + float m_camDist; + uint8 m_type; + uint8 m_red; + uint8 m_green; + uint8 m_blue; +}; + + +class CBrightLights +{ + static int NumBrightLights; + static CBrightLight aBrightLights[NUMBRIGHTLIGHTS]; +public: + static void Init(void); + static void RegisterOne(CVector pos, CVector up, CVector side, CVector front, + uint8 type, uint8 red = 0, uint8 green = 0, uint8 blue = 0); + static void Render(void); + static void RenderOutGeometryBuffer(void); +}; + + +enum +{ + SHINYTEXT_WALK = 1, + SHINYTEXT_FLAT +}; + + +class CShinyText +{ +public: + CVector m_verts[4]; + CVector2D m_texCoords[4]; + float m_camDist; + uint8 m_type; + uint8 m_red; + uint8 m_green; + uint8 m_blue; +}; + + +class CShinyTexts +{ + static int NumShinyTexts; + static CShinyText aShinyTexts[NUMSHINYTEXTS]; +public: + static void Init(void); + static void RegisterOne(CVector p0, CVector p1, CVector p2, CVector p3, + float u0, float v0, float u1, float v1, float u2, float v2, float u3, float v3, + uint8 type, uint8 red, uint8 green, uint8 blue, float maxDist); //not used + static void Render(void); + static void RenderOutGeometryBuffer(void); +}; + + +class CMoneyMessage +{ + friend class CMoneyMessages; + + uint32 m_nTimeRegistered; + CVector m_vecPosition; + wchar m_aText[16]; + CRGBA m_Colour; + float m_fSize; + float m_fOpacity; +public: + void Render(); +}; + + +class CMoneyMessages +{ + static CMoneyMessage aMoneyMessages[NUMMONEYMESSAGES]; +public: + static void Init(); + static void Render(); + static void RegisterOne(CVector vecPos, const char *pText, uint8 bRed, uint8 bGreen, uint8 bBlue, float fSize, float fOpacity); +}; + + +class CSpecialParticleStuff +{ + static uint32 BoatFromStart; +public: + static void CreateFoamAroundObject(CMatrix*, float, float, float, int32); //not used + static void StartBoatFoamAnimation(); //not used + static void UpdateBoatFoamAnimation(CMatrix*); //not used +}; diff --git a/src/miami/renderer/Sprite.cpp b/src/miami/renderer/Sprite.cpp new file mode 100644 index 00000000..ecfd3fdc --- /dev/null +++ b/src/miami/renderer/Sprite.cpp @@ -0,0 +1,603 @@ +#include "common.h" + +#include "main.h" +#include "Draw.h" +#include "Camera.h" +#include "Sprite.h" + +#ifdef ASPECT_RATIO_SCALE +#include "Frontend.h" +#endif + +float CSprite::m_f2DNearScreenZ; +float CSprite::m_f2DFarScreenZ; +float CSprite::m_fRecipNearClipPlane; +int32 CSprite::m_bFlushSpriteBufferSwitchZTest; + +float +CSprite::CalcHorizonCoors(void) +{ + CVector p = TheCamera.GetPosition() + CVector(TheCamera.CamFrontXNorm, TheCamera.CamFrontYNorm, 0.0f)*3000.0f; + p.z = 0.0f; + p = TheCamera.m_viewMatrix * p; + return p.y * SCREEN_HEIGHT / p.z; +} + +bool +CSprite::CalcScreenCoors(const RwV3d &in, RwV3d *out, float *outw, float *outh, bool farclip) +{ + CVector viewvec = TheCamera.m_viewMatrix * in; + *out = viewvec; + if(out->z <= CDraw::GetNearClipZ() + 1.0f) return false; + if(out->z >= CDraw::GetFarClipZ() && farclip) return false; + float recip = 1.0f/out->z; + out->x *= SCREEN_WIDTH * recip; + out->y *= SCREEN_HEIGHT * recip; + const float fov = DefaultFOV; + // this is used to scale correctly if you zoom in with sniper rifle + float fovScale = fov / CDraw::GetFOV(); + +#ifdef FIX_SPRITES + *outw = CDraw::ms_bFixSprites ? (fovScale * recip * SCREEN_HEIGHT) : (fovScale * SCREEN_SCALE_AR(recip) * SCREEN_WIDTH); +#else + *outw = fovScale * SCREEN_SCALE_AR(recip) * SCREEN_WIDTH; +#endif + *outh = fovScale * recip * SCREEN_HEIGHT; + + return true; +} + +#define SPRITEBUFFERSIZE 64 +static int32 nSpriteBufferIndex; +static RwIm2DVertex SpriteBufferVerts[SPRITEBUFFERSIZE*6]; +static RwIm2DVertex verts[4]; + +void +CSprite::InitSpriteBuffer(void) +{ + m_f2DNearScreenZ = RwIm2DGetNearScreenZ(); + m_f2DFarScreenZ = RwIm2DGetFarScreenZ(); +} + +void +CSprite::InitSpriteBuffer2D(void) +{ + m_fRecipNearClipPlane = 1.0f / RwCameraGetNearClipPlane(Scene.camera); + InitSpriteBuffer(); +} + +void +CSprite::FlushSpriteBuffer(void) +{ + if(nSpriteBufferIndex > 0){ + if(m_bFlushSpriteBufferSwitchZTest){ + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + RwIm2DRenderPrimitive(rwPRIMTYPETRILIST, SpriteBufferVerts, nSpriteBufferIndex*6); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + }else + RwIm2DRenderPrimitive(rwPRIMTYPETRILIST, SpriteBufferVerts, nSpriteBufferIndex*6); + nSpriteBufferIndex = 0; + } +} + +void +CSprite::RenderOneXLUSprite(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, uint8 a) +{ + static short indices[] = { 0, 1, 2, 3 }; + // 0---3 + // | | + // 1---2 + float xs[4]; + float ys[4]; + float us[4]; + float vs[4]; + int i; + + xs[0] = x-w; us[0] = 0.0f; + xs[1] = x-w; us[1] = 0.0f; + xs[2] = x+w; us[2] = 1.0f; + xs[3] = x+w; us[3] = 1.0f; + + ys[0] = y-h; vs[0] = 0.0f; + ys[1] = y+h; vs[1] = 1.0f; + ys[2] = y+h; vs[2] = 1.0f; + ys[3] = y-h; vs[3] = 0.0f; + + // clip + for(i = 0; i < 4; i++){ + if(xs[i] < 0.0f){ + us[i] = -xs[i] / (2.0f*w); + xs[i] = 0.0f; + } + if(xs[i] > SCREEN_WIDTH){ + us[i] = 1.0f - (xs[i]-SCREEN_WIDTH) / (2.0f*w); + xs[i] = SCREEN_WIDTH; + } + if(ys[i] < 0.0f){ + vs[i] = -ys[i] / (2.0f*h); + ys[i] = 0.0f; + } + if(ys[i] > SCREEN_HEIGHT){ + vs[i] = 1.0f - (ys[i]-SCREEN_HEIGHT) / (2.0f*h); + ys[i] = SCREEN_HEIGHT; + } + } + + // (DrawZ - DrawNear)/(DrawFar - DrawNear) = (SpriteZ-SpriteNear)/(SpriteFar-SpriteNear) + // So to calculate SpriteZ: + float screenz = m_f2DNearScreenZ + + (z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() / + ((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z); + + for(i = 0; i < 4; i++){ + RwIm2DVertexSetScreenX(&verts[i], xs[i]); + RwIm2DVertexSetScreenY(&verts[i], ys[i]); + RwIm2DVertexSetScreenZ(&verts[i], screenz); + RwIm2DVertexSetCameraZ(&verts[i], z); + RwIm2DVertexSetRecipCameraZ(&verts[i], recipz); + RwIm2DVertexSetIntRGBA(&verts[i], r*intens>>8, g*intens>>8, b*intens>>8, a); + RwIm2DVertexSetU(&verts[i], us[i], recipz); + RwIm2DVertexSetV(&verts[i], vs[i], recipz); + } + RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, verts, 4); +} + +void +CSprite::RenderOneXLUSprite_Rotate_Aspect(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float rotation, uint8 a) +{ + float c = Cos(rotation); + float s = Sin(rotation); + + float xs[4]; + float ys[4]; + float us[4]; + float vs[4]; + int i; + + // Fade out when too near + // why not in buffered version? + if(z < 2.3f){ + if(z < 1.3f) + return; + int f = (z - 1.3f)/(2.3f-1.3f) * 255; + r = f*r >> 8; + g = f*g >> 8; + b = f*b >> 8; + intens = f*intens >> 8; + } + + xs[0] = x + w*(-c-s); us[0] = 0.0f; + xs[1] = x + w*(-c+s); us[1] = 0.0f; + xs[2] = x + w*(+c+s); us[2] = 1.0f; + xs[3] = x + w*(+c-s); us[3] = 1.0f; + + ys[0] = y + h*(-c+s); vs[0] = 0.0f; + ys[1] = y + h*(+c+s); vs[1] = 1.0f; + ys[2] = y + h*(+c-s); vs[2] = 1.0f; + ys[3] = y + h*(-c-s); vs[3] = 0.0f; + + // No clipping, just culling + if(xs[0] < 0.0f && xs[1] < 0.0f && xs[2] < 0.0f && xs[3] < 0.0f) return; + if(ys[0] < 0.0f && ys[1] < 0.0f && ys[2] < 0.0f && ys[3] < 0.0f) return; + if(xs[0] > SCREEN_WIDTH && xs[1] > SCREEN_WIDTH && + xs[2] > SCREEN_WIDTH && xs[3] > SCREEN_WIDTH) return; + if(ys[0] > SCREEN_HEIGHT && ys[1] > SCREEN_HEIGHT && + ys[2] > SCREEN_HEIGHT && ys[3] > SCREEN_HEIGHT) return; + + float screenz = m_f2DNearScreenZ + + (z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() / + ((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z); + + for(i = 0; i < 4; i++){ + RwIm2DVertexSetScreenX(&verts[i], xs[i]); + RwIm2DVertexSetScreenY(&verts[i], ys[i]); + RwIm2DVertexSetScreenZ(&verts[i], screenz); + RwIm2DVertexSetCameraZ(&verts[i], z); + RwIm2DVertexSetRecipCameraZ(&verts[i], recipz); + RwIm2DVertexSetIntRGBA(&verts[i], r*intens>>8, g*intens>>8, b*intens>>8, a); + RwIm2DVertexSetU(&verts[i], us[i], recipz); + RwIm2DVertexSetV(&verts[i], vs[i], recipz); + } + RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, verts, 4); +} + +void +CSprite::RenderBufferedOneXLUSprite(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, uint8 a) +{ + m_bFlushSpriteBufferSwitchZTest = 0; + + // 0---3 + // | | + // 1---2 + float xs[4]; + float ys[4]; + float us[4]; + float vs[4]; + int i; + + xs[0] = x-w; us[0] = 0.0f; + xs[1] = x-w; us[1] = 0.0f; + xs[2] = x+w; us[2] = 1.0f; + xs[3] = x+w; us[3] = 1.0f; + + ys[0] = y-h; vs[0] = 0.0f; + ys[1] = y+h; vs[1] = 1.0f; + ys[2] = y+h; vs[2] = 1.0f; + ys[3] = y-h; vs[3] = 0.0f; + + // clip + for(i = 0; i < 4; i++){ + if(xs[i] < 0.0f){ + us[i] = -xs[i] / (2.0f*w); + xs[i] = 0.0f; + } + if(xs[i] > SCREEN_WIDTH){ + us[i] = 1.0f - (xs[i]-SCREEN_WIDTH) / (2.0f*w); + xs[i] = SCREEN_WIDTH; + } + if(ys[i] < 0.0f){ + vs[i] = -ys[i] / (2.0f*h); + ys[i] = 0.0f; + } + if(ys[i] > SCREEN_HEIGHT){ + vs[i] = 1.0f - (ys[i]-SCREEN_HEIGHT) / (2.0f*h); + ys[i] = SCREEN_HEIGHT; + } + } + + float screenz = m_f2DNearScreenZ + + (z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() / + ((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z); + + RwIm2DVertex *vert = &SpriteBufferVerts[nSpriteBufferIndex*6]; + static int indices[6] = { 0, 1, 2, 3, 0, 2 }; + for(i = 0; i < 6; i++){ + RwIm2DVertexSetScreenX(&vert[i], xs[indices[i]]); + RwIm2DVertexSetScreenY(&vert[i], ys[indices[i]]); + RwIm2DVertexSetScreenZ(&vert[i], screenz); + RwIm2DVertexSetCameraZ(&vert[i], z); + RwIm2DVertexSetRecipCameraZ(&vert[i], recipz); + RwIm2DVertexSetIntRGBA(&vert[i], r*intens>>8, g*intens>>8, b*intens>>8, a); + RwIm2DVertexSetU(&vert[i], us[indices[i]], recipz); + RwIm2DVertexSetV(&vert[i], vs[indices[i]], recipz); + } + nSpriteBufferIndex++; + if(nSpriteBufferIndex >= SPRITEBUFFERSIZE) + FlushSpriteBuffer(); +} + +void +CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float rotation, uint8 a) +{ + m_bFlushSpriteBufferSwitchZTest = 0; + // TODO: replace with lookup + float c = Cos(rotation); + float s = Sin(rotation); + + float xs[4]; + float ys[4]; + float us[4]; + float vs[4]; + int i; + + xs[0] = x - c*w - s*h; us[0] = 0.0f; + xs[1] = x - c*w + s*h; us[1] = 0.0f; + xs[2] = x + c*w + s*h; us[2] = 1.0f; + xs[3] = x + c*w - s*h; us[3] = 1.0f; + + ys[0] = y - c*h + s*w; vs[0] = 0.0f; + ys[1] = y + c*h + s*w; vs[1] = 1.0f; + ys[2] = y + c*h - s*w; vs[2] = 1.0f; + ys[3] = y - c*h - s*w; vs[3] = 0.0f; + + // No clipping, just culling + if(xs[0] < 0.0f && xs[1] < 0.0f && xs[2] < 0.0f && xs[3] < 0.0f) return; + if(ys[0] < 0.0f && ys[1] < 0.0f && ys[2] < 0.0f && ys[3] < 0.0f) return; + if(xs[0] > SCREEN_WIDTH && xs[1] > SCREEN_WIDTH && + xs[2] > SCREEN_WIDTH && xs[3] > SCREEN_WIDTH) return; + if(ys[0] > SCREEN_HEIGHT && ys[1] > SCREEN_HEIGHT && + ys[2] > SCREEN_HEIGHT && ys[3] > SCREEN_HEIGHT) return; + + float screenz = m_f2DNearScreenZ + + (z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() / + ((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z); + + RwIm2DVertex *vert = &SpriteBufferVerts[nSpriteBufferIndex*6]; + static int indices[6] = { 0, 1, 2, 3, 0, 2 }; + for(i = 0; i < 6; i++){ + RwIm2DVertexSetScreenX(&vert[i], xs[indices[i]]); + RwIm2DVertexSetScreenY(&vert[i], ys[indices[i]]); + RwIm2DVertexSetScreenZ(&vert[i], screenz); + RwIm2DVertexSetCameraZ(&vert[i], z); + RwIm2DVertexSetRecipCameraZ(&vert[i], recipz); + RwIm2DVertexSetIntRGBA(&vert[i], r*intens>>8, g*intens>>8, b*intens>>8, a); + RwIm2DVertexSetU(&vert[i], us[indices[i]], recipz); + RwIm2DVertexSetV(&vert[i], vs[indices[i]], recipz); + } + nSpriteBufferIndex++; + if(nSpriteBufferIndex >= SPRITEBUFFERSIZE) + FlushSpriteBuffer(); +} + +void +CSprite::RenderBufferedOneXLUSprite_Rotate_Aspect(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float rotation, uint8 a) +{ + m_bFlushSpriteBufferSwitchZTest = 0; + float c = Cos(rotation); + float s = Sin(rotation); + + float xs[4]; + float ys[4]; + float us[4]; + float vs[4]; + int i; + + xs[0] = x + w*(-c-s); us[0] = 0.0f; + xs[1] = x + w*(-c+s); us[1] = 0.0f; + xs[2] = x + w*(+c+s); us[2] = 1.0f; + xs[3] = x + w*(+c-s); us[3] = 1.0f; + + ys[0] = y + h*(-c+s); vs[0] = 0.0f; + ys[1] = y + h*(+c+s); vs[1] = 1.0f; + ys[2] = y + h*(+c-s); vs[2] = 1.0f; + ys[3] = y + h*(-c-s); vs[3] = 0.0f; + + // No clipping, just culling + if(xs[0] < 0.0f && xs[1] < 0.0f && xs[2] < 0.0f && xs[3] < 0.0f) return; + if(ys[0] < 0.0f && ys[1] < 0.0f && ys[2] < 0.0f && ys[3] < 0.0f) return; + if(xs[0] > SCREEN_WIDTH && xs[1] > SCREEN_WIDTH && + xs[2] > SCREEN_WIDTH && xs[3] > SCREEN_WIDTH) return; + if(ys[0] > SCREEN_HEIGHT && ys[1] > SCREEN_HEIGHT && + ys[2] > SCREEN_HEIGHT && ys[3] > SCREEN_HEIGHT) return; + + float screenz = m_f2DNearScreenZ + + (z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() / + ((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z); + + RwIm2DVertex *vert = &SpriteBufferVerts[nSpriteBufferIndex*6]; + static int indices[6] = { 0, 1, 2, 3, 0, 2 }; + for(i = 0; i < 6; i++){ + RwIm2DVertexSetScreenX(&vert[i], xs[indices[i]]); + RwIm2DVertexSetScreenY(&vert[i], ys[indices[i]]); + RwIm2DVertexSetScreenZ(&vert[i], screenz); + RwIm2DVertexSetCameraZ(&vert[i], z); + RwIm2DVertexSetRecipCameraZ(&vert[i], recipz); + RwIm2DVertexSetIntRGBA(&vert[i], r*intens>>8, g*intens>>8, b*intens>>8, a); + RwIm2DVertexSetU(&vert[i], us[indices[i]], recipz); + RwIm2DVertexSetV(&vert[i], vs[indices[i]], recipz); + } + nSpriteBufferIndex++; + if(nSpriteBufferIndex >= SPRITEBUFFERSIZE) + FlushSpriteBuffer(); +} + +void +CSprite::RenderBufferedOneXLUSprite_Rotate_2Colours(float x, float y, float z, float w, float h, uint8 r1, uint8 g1, uint8 b1, uint8 r2, uint8 g2, uint8 b2, float cx, float cy, float recipz, float rotation, uint8 a) +{ + m_bFlushSpriteBufferSwitchZTest = 0; + float c = Cos(rotation); + float s = Sin(rotation); + + float xs[4]; + float ys[4]; + float us[4]; + float vs[4]; + float cf[4]; + int i; + + xs[0] = x + w*(-c-s); us[0] = 0.0f; + xs[1] = x + w*(-c+s); us[1] = 0.0f; + xs[2] = x + w*(+c+s); us[2] = 1.0f; + xs[3] = x + w*(+c-s); us[3] = 1.0f; + + ys[0] = y + h*(-c+s); vs[0] = 0.0f; + ys[1] = y + h*(+c+s); vs[1] = 1.0f; + ys[2] = y + h*(+c-s); vs[2] = 1.0f; + ys[3] = y + h*(-c-s); vs[3] = 0.0f; + + // No clipping, just culling + if(xs[0] < 0.0f && xs[1] < 0.0f && xs[2] < 0.0f && xs[3] < 0.0f) return; + if(ys[0] < 0.0f && ys[1] < 0.0f && ys[2] < 0.0f && ys[3] < 0.0f) return; + if(xs[0] > SCREEN_WIDTH && xs[1] > SCREEN_WIDTH && + xs[2] > SCREEN_WIDTH && xs[3] > SCREEN_WIDTH) return; + if(ys[0] > SCREEN_HEIGHT && ys[1] > SCREEN_HEIGHT && + ys[2] > SCREEN_HEIGHT && ys[3] > SCREEN_HEIGHT) return; + + // Colour factors, cx/y is the direction in which colours change from rgb1 to rgb2 + cf[0] = (cx*(-c-s) + cy*(-c+s))*0.5f + 0.5f; + cf[0] = Clamp(cf[0], 0.0f, 1.0f); + cf[1] = (cx*(-c+s) + cy*( c+s))*0.5f + 0.5f; + cf[1] = Clamp(cf[1], 0.0f, 1.0f); + cf[2] = (cx*( c+s) + cy*( c-s))*0.5f + 0.5f; + cf[2] = Clamp(cf[2], 0.0f, 1.0f); + cf[3] = (cx*( c-s) + cy*(-c-s))*0.5f + 0.5f; + cf[3] = Clamp(cf[3], 0.0f, 1.0f); + + float screenz = m_f2DNearScreenZ + + (z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() / + ((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z); + + RwIm2DVertex *vert = &SpriteBufferVerts[nSpriteBufferIndex*6]; + static int indices[6] = { 0, 1, 2, 3, 0, 2 }; + for(i = 0; i < 6; i++){ + RwIm2DVertexSetScreenX(&vert[i], xs[indices[i]]); + RwIm2DVertexSetScreenY(&vert[i], ys[indices[i]]); + RwIm2DVertexSetScreenZ(&vert[i], screenz); + RwIm2DVertexSetCameraZ(&vert[i], z); + RwIm2DVertexSetRecipCameraZ(&vert[i], recipz); + RwIm2DVertexSetIntRGBA(&vert[i], + r1*cf[indices[i]] + r2*(1.0f - cf[indices[i]]), + g1*cf[indices[i]] + g2*(1.0f - cf[indices[i]]), + b1*cf[indices[i]] + b2*(1.0f - cf[indices[i]]), + a); + RwIm2DVertexSetU(&vert[i], us[indices[i]], recipz); + RwIm2DVertexSetV(&vert[i], vs[indices[i]], recipz); + } + nSpriteBufferIndex++; + if(nSpriteBufferIndex >= SPRITEBUFFERSIZE) + FlushSpriteBuffer(); +} + +void +CSprite::Set6Vertices2D(RwIm2DVertex *verts, const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3) +{ + float screenz, recipz; + float z = RwCameraGetNearClipPlane(Scene.camera); // not done by game + + screenz = m_f2DNearScreenZ; + recipz = m_fRecipNearClipPlane; + + RwIm2DVertexSetScreenX(&verts[0], r.left); + RwIm2DVertexSetScreenY(&verts[0], r.top); + RwIm2DVertexSetScreenZ(&verts[0], screenz); + RwIm2DVertexSetCameraZ(&verts[0], z); + RwIm2DVertexSetRecipCameraZ(&verts[0], recipz); + RwIm2DVertexSetIntRGBA(&verts[0], c2.r, c2.g, c2.b, c2.a); + RwIm2DVertexSetU(&verts[0], 0.0f, recipz); + RwIm2DVertexSetV(&verts[0], 0.0f, recipz); + + RwIm2DVertexSetScreenX(&verts[1], r.right); + RwIm2DVertexSetScreenY(&verts[1], r.top); + RwIm2DVertexSetScreenZ(&verts[1], screenz); + RwIm2DVertexSetCameraZ(&verts[1], z); + RwIm2DVertexSetRecipCameraZ(&verts[1], recipz); + RwIm2DVertexSetIntRGBA(&verts[1], c3.r, c3.g, c3.b, c3.a); + RwIm2DVertexSetU(&verts[1], 1.0f, recipz); + RwIm2DVertexSetV(&verts[1], 0.0f, recipz); + + RwIm2DVertexSetScreenX(&verts[2], r.right); + RwIm2DVertexSetScreenY(&verts[2], r.bottom); + RwIm2DVertexSetScreenZ(&verts[2], screenz); + RwIm2DVertexSetCameraZ(&verts[2], z); + RwIm2DVertexSetRecipCameraZ(&verts[2], recipz); + RwIm2DVertexSetIntRGBA(&verts[2], c1.r, c1.g, c1.b, c1.a); + RwIm2DVertexSetU(&verts[2], 1.0f, recipz); + RwIm2DVertexSetV(&verts[2], 1.0f, recipz); + + RwIm2DVertexSetScreenX(&verts[3], r.left); + RwIm2DVertexSetScreenY(&verts[3], r.bottom); + RwIm2DVertexSetScreenZ(&verts[3], screenz); + RwIm2DVertexSetCameraZ(&verts[3], z); + RwIm2DVertexSetRecipCameraZ(&verts[3], recipz); + RwIm2DVertexSetIntRGBA(&verts[3], c0.r, c0.g, c0.b, c0.a); + RwIm2DVertexSetU(&verts[3], 0.0f, recipz); + RwIm2DVertexSetV(&verts[3], 1.0f, recipz); + + RwIm2DVertexSetScreenX(&verts[4], r.left); + RwIm2DVertexSetScreenY(&verts[4], r.top); + RwIm2DVertexSetScreenZ(&verts[4], screenz); + RwIm2DVertexSetCameraZ(&verts[4], z); + RwIm2DVertexSetRecipCameraZ(&verts[4], recipz); + RwIm2DVertexSetIntRGBA(&verts[4], c2.r, c2.g, c2.b, c2.a); + RwIm2DVertexSetU(&verts[4], 0.0f, recipz); + RwIm2DVertexSetV(&verts[4], 0.0f, recipz); + + RwIm2DVertexSetScreenX(&verts[5], r.right); + RwIm2DVertexSetScreenY(&verts[5], r.bottom); + RwIm2DVertexSetScreenZ(&verts[5], screenz); + RwIm2DVertexSetCameraZ(&verts[5], z); + RwIm2DVertexSetRecipCameraZ(&verts[5], recipz); + RwIm2DVertexSetIntRGBA(&verts[5], c1.r, c1.g, c1.b, c1.a); + RwIm2DVertexSetU(&verts[5], 1.0f, recipz); + RwIm2DVertexSetV(&verts[5], 1.0f, recipz); +} + +void +CSprite::Set6Vertices2D(RwIm2DVertex *verts, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, + const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3) +{ + float screenz, recipz; + float z = RwCameraGetNearClipPlane(Scene.camera); // not done by game + + screenz = m_f2DNearScreenZ; + recipz = m_fRecipNearClipPlane; + + RwIm2DVertexSetScreenX(&verts[0], x3); + RwIm2DVertexSetScreenY(&verts[0], y3); + RwIm2DVertexSetScreenZ(&verts[0], screenz); + RwIm2DVertexSetCameraZ(&verts[0], z); + RwIm2DVertexSetRecipCameraZ(&verts[0], recipz); + RwIm2DVertexSetIntRGBA(&verts[0], c2.r, c2.g, c2.b, c2.a); + RwIm2DVertexSetU(&verts[0], 0.0f, recipz); + RwIm2DVertexSetV(&verts[0], 0.0f, recipz); + + RwIm2DVertexSetScreenX(&verts[1], x4); + RwIm2DVertexSetScreenY(&verts[1], y4); + RwIm2DVertexSetScreenZ(&verts[1], screenz); + RwIm2DVertexSetCameraZ(&verts[1], z); + RwIm2DVertexSetRecipCameraZ(&verts[1], recipz); + RwIm2DVertexSetIntRGBA(&verts[1], c3.r, c3.g, c3.b, c3.a); + RwIm2DVertexSetU(&verts[1], 1.0f, recipz); + RwIm2DVertexSetV(&verts[1], 0.0f, recipz); + + RwIm2DVertexSetScreenX(&verts[2], x2); + RwIm2DVertexSetScreenY(&verts[2], y2); + RwIm2DVertexSetScreenZ(&verts[2], screenz); + RwIm2DVertexSetCameraZ(&verts[2], z); + RwIm2DVertexSetRecipCameraZ(&verts[2], recipz); + RwIm2DVertexSetIntRGBA(&verts[2], c1.r, c1.g, c1.b, c1.a); + RwIm2DVertexSetU(&verts[2], 1.0f, recipz); + RwIm2DVertexSetV(&verts[2], 1.0f, recipz); + + RwIm2DVertexSetScreenX(&verts[3], x1); + RwIm2DVertexSetScreenY(&verts[3], y1); + RwIm2DVertexSetScreenZ(&verts[3], screenz); + RwIm2DVertexSetCameraZ(&verts[3], z); + RwIm2DVertexSetRecipCameraZ(&verts[3], recipz); + RwIm2DVertexSetIntRGBA(&verts[3], c0.r, c0.g, c0.b, c0.a); + RwIm2DVertexSetU(&verts[3], 0.0f, recipz); + RwIm2DVertexSetV(&verts[3], 1.0f, recipz); + + RwIm2DVertexSetScreenX(&verts[4], x3); + RwIm2DVertexSetScreenY(&verts[4], y3); + RwIm2DVertexSetScreenZ(&verts[4], screenz); + RwIm2DVertexSetCameraZ(&verts[4], z); + RwIm2DVertexSetRecipCameraZ(&verts[4], recipz); + RwIm2DVertexSetIntRGBA(&verts[4], c2.r, c2.g, c2.b, c2.a); + RwIm2DVertexSetU(&verts[4], 0.0f, recipz); + RwIm2DVertexSetV(&verts[4], 0.0f, recipz); + + RwIm2DVertexSetScreenX(&verts[5], x2); + RwIm2DVertexSetScreenY(&verts[5], y2); + RwIm2DVertexSetScreenZ(&verts[5], screenz); + RwIm2DVertexSetCameraZ(&verts[5], z); + RwIm2DVertexSetRecipCameraZ(&verts[5], recipz); + RwIm2DVertexSetIntRGBA(&verts[5], c1.r, c1.g, c1.b, c1.a); + RwIm2DVertexSetU(&verts[5], 1.0f, recipz); + RwIm2DVertexSetV(&verts[5], 1.0f, recipz); +} + +void +CSprite::RenderBufferedOneXLUSprite2D(float x, float y, float w, float h, const RwRGBA &colour, int16 intens, uint8 alpha) +{ + m_bFlushSpriteBufferSwitchZTest = 1; + CRGBA col(intens * colour.red >> 8, intens * colour.green >> 8, intens * colour.blue >> 8, alpha); + CRect rect(x - w, y - h, x + h, y + h); + Set6Vertices2D(&SpriteBufferVerts[6 * nSpriteBufferIndex], rect, col, col, col, col); + nSpriteBufferIndex++; + if(nSpriteBufferIndex >= SPRITEBUFFERSIZE) + FlushSpriteBuffer(); +} + +void +CSprite::RenderBufferedOneXLUSprite2D_Rotate_Dimension(float x, float y, float w, float h, const RwRGBA &colour, int16 intens, float rotation, uint8 alpha) +{ + m_bFlushSpriteBufferSwitchZTest = 1; + CRGBA col(intens * colour.red >> 8, intens * colour.green >> 8, intens * colour.blue >> 8, alpha); + float c = Cos(rotation); + float s = Sin(rotation); + + Set6Vertices2D(&SpriteBufferVerts[6 * nSpriteBufferIndex], + x + c*w - s*h, + y - c*h - s*w, + x + c*w + s*h, + y + c*h - s*w, + x - c*w - s*h, + y - c*h + s*w, + x - c*w + s*h, + y + c*h + s*w, + col, col, col, col); + nSpriteBufferIndex++; + if(nSpriteBufferIndex >= SPRITEBUFFERSIZE) + FlushSpriteBuffer(); +} diff --git a/src/miami/renderer/Sprite.h b/src/miami/renderer/Sprite.h new file mode 100644 index 00000000..fae6684e --- /dev/null +++ b/src/miami/renderer/Sprite.h @@ -0,0 +1,31 @@ +#pragma once + +class CSprite +{ + static float m_f2DNearScreenZ; + static float m_f2DFarScreenZ; + static float m_fRecipNearClipPlane; + static int32 m_bFlushSpriteBufferSwitchZTest; +public: + static float GetNearScreenZ(void) { return m_f2DNearScreenZ; } + static float GetFarScreenZ(void) { return m_f2DFarScreenZ; } + + static float CalcHorizonCoors(void); + static bool CalcScreenCoors(const RwV3d &in, RwV3d *out, float *outw, float *outh, bool farclip); + static void InitSpriteBuffer(void); + static void InitSpriteBuffer2D(void); + static void FlushSpriteBuffer(void); + static void RenderOneXLUSprite(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, uint8 a); + static void RenderOneXLUSprite_Rotate_Aspect(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float roll, uint8 a); + static void RenderBufferedOneXLUSprite(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, uint8 a); + static void RenderBufferedOneXLUSprite_Rotate_Dimension(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float roll, uint8 a); + static void RenderBufferedOneXLUSprite_Rotate_Aspect(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float roll, uint8 a); + // cx/y is the direction in which the colour changes + static void RenderBufferedOneXLUSprite_Rotate_2Colours(float x, float y, float z, float w, float h, uint8 r1, uint8 g1, uint8 b1, uint8 r2, uint8 g2, uint8 b2, float cx, float cy, float recipz, float rotation, uint8 a); + static void Set6Vertices2D(RwIm2DVertex *verts, const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3); + static void Set6Vertices2D(RwIm2DVertex *verts, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, + const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3); + static void RenderBufferedOneXLUSprite2D(float x, float y, float w, float h, const RwRGBA &colour, int16 intens, uint8 alpha); + static void RenderBufferedOneXLUSprite2D_Rotate_Dimension(float x, float y, float w, float h, const RwRGBA &colour, int16 intens, float rotation, uint8 alpha); + +}; diff --git a/src/miami/renderer/Sprite2d.cpp b/src/miami/renderer/Sprite2d.cpp new file mode 100644 index 00000000..6f20cdbb --- /dev/null +++ b/src/miami/renderer/Sprite2d.cpp @@ -0,0 +1,470 @@ +#include "common.h" + +#include "main.h" +#include "Draw.h" +#include "Camera.h" +#include "Sprite2d.h" +#include "Font.h" +#include "RenderBuffer.h" + +float CSprite2d::RecipNearClip; +float CSprite2d::NearScreenZ; +float CSprite2d::NearCamZ; +float CSprite2d::RecipFarClip; +float CSprite2d::FarScreenZ; +float CSprite2d::FarCamZ; +int CSprite2d::nextBufferVertex; +int CSprite2d::nextBufferIndex; +RwIm2DVertex CSprite2d::maVertices[8]; + +void +CSprite2d::SetRecipNearClip(void) +{ + // Used but empty in VC, instead they set in InitPerFrame. Isn't that great? +} + +void +CSprite2d::InitPerFrame(void) +{ + nextBufferVertex = 0; + nextBufferIndex = 0; + RecipNearClip = 1.0f / RwCameraGetNearClipPlane(Scene.camera); + NearScreenZ = RwIm2DGetNearScreenZ(); + // not original but you're supposed to set camera z too + // wrapping all this in FIX_BUGS is too ugly + NearCamZ = RwCameraGetNearClipPlane(Scene.camera); + + RecipFarClip = 1.0f / RwCameraGetFarClipPlane(Scene.camera); + FarScreenZ = RwIm2DGetFarScreenZ(); + FarCamZ = RwCameraGetFarClipPlane(Scene.camera); +} +void +CSprite2d::Delete(void) +{ + if(m_pTexture){ + RwTextureDestroy(m_pTexture); + m_pTexture = nil; + } +} + +void +CSprite2d::SetTexture(const char *name) +{ + Delete(); + if(name) + m_pTexture = RwTextureRead(name, nil); +} + +void +CSprite2d::SetTexture(const char *name, const char *mask) +{ + Delete(); + if(name) + m_pTexture = RwTextureRead(name, mask); +} + +void +CSprite2d::SetAddressing(RwTextureAddressMode addr) +{ + if(m_pTexture) + RwTextureSetAddressing(m_pTexture, addr); +} + +void +CSprite2d::SetRenderState(void) +{ + if(m_pTexture) + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(m_pTexture)); + else + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); +} + +void +CSprite2d::Draw(float x, float y, float w, float h, const CRGBA &col) +{ + SetVertices(CRect(x, y, x + w, y + h), col, col, col, col); + SetRenderState(); + RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4); +} + +void +CSprite2d::Draw(const CRect &rect, const CRGBA &col) +{ + SetVertices(rect, col, col, col, col); + SetRenderState(); + RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4); +} + +void +CSprite2d::Draw(const CRect &rect, const CRGBA &col, + float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2) +{ + SetVertices(rect, col, col, col, col, u0, v0, u1, v1, u3, v3, u2, v2); + SetRenderState(); + RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4); +} + +void +CSprite2d::Draw(const CRect &rect, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3) +{ + SetVertices(rect, c0, c1, c2, c3); + SetRenderState(); + RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4); +} + +void +CSprite2d::Draw(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA &col) +{ + SetVertices(x1, y1, x2, y2, x3, y3, x4, y4, col, col, col, col); + SetRenderState(); + RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4); +} + +// Arguments: +// 2---3 +// | | +// 0---1 +void +CSprite2d::SetVertices(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3) +{ + float offset = 1.0f/1024.0f; + + // Workaround for PVR Z-Equality not being exact + NearCamZ /= 1.1f; + + // This is what we draw: + // 0---1 + // | / | + // 3---2 + RwIm2DVertexSetScreenX(&maVertices[0], r.left); + RwIm2DVertexSetScreenY(&maVertices[0], r.top); + RwIm2DVertexSetScreenZ(&maVertices[0], NearScreenZ); + RwIm2DVertexSetCameraZ(&maVertices[0], NearCamZ); + RwIm2DVertexSetRecipCameraZ(&maVertices[0], RecipNearClip); + RwIm2DVertexSetIntRGBA(&maVertices[0], c2.r, c2.g, c2.b, c2.a); + RwIm2DVertexSetU(&maVertices[0], 0.0f+offset, RecipNearClip); + RwIm2DVertexSetV(&maVertices[0], 0.0f+offset, RecipNearClip); + + RwIm2DVertexSetScreenX(&maVertices[1], r.right); + RwIm2DVertexSetScreenY(&maVertices[1], r.top); + RwIm2DVertexSetScreenZ(&maVertices[1], NearScreenZ); + RwIm2DVertexSetCameraZ(&maVertices[1], NearCamZ); + RwIm2DVertexSetRecipCameraZ(&maVertices[1], RecipNearClip); + RwIm2DVertexSetIntRGBA(&maVertices[1], c3.r, c3.g, c3.b, c3.a); + RwIm2DVertexSetU(&maVertices[1], 1.0f+offset, RecipNearClip); + RwIm2DVertexSetV(&maVertices[1], 0.0f+offset, RecipNearClip); + + RwIm2DVertexSetScreenX(&maVertices[2], r.right); + RwIm2DVertexSetScreenY(&maVertices[2], r.bottom); + RwIm2DVertexSetScreenZ(&maVertices[2], NearScreenZ); + RwIm2DVertexSetCameraZ(&maVertices[2], NearCamZ); + RwIm2DVertexSetRecipCameraZ(&maVertices[2], RecipNearClip); + RwIm2DVertexSetIntRGBA(&maVertices[2], c1.r, c1.g, c1.b, c1.a); + RwIm2DVertexSetU(&maVertices[2], 1.0f+offset, RecipNearClip); + RwIm2DVertexSetV(&maVertices[2], 1.0f+offset, RecipNearClip); + + RwIm2DVertexSetScreenX(&maVertices[3], r.left); + RwIm2DVertexSetScreenY(&maVertices[3], r.bottom); + RwIm2DVertexSetScreenZ(&maVertices[3], NearScreenZ); + RwIm2DVertexSetCameraZ(&maVertices[3], NearCamZ); + RwIm2DVertexSetRecipCameraZ(&maVertices[3], RecipNearClip); + RwIm2DVertexSetIntRGBA(&maVertices[3], c0.r, c0.g, c0.b, c0.a); + RwIm2DVertexSetU(&maVertices[3], 0.0f+offset, RecipNearClip); + RwIm2DVertexSetV(&maVertices[3], 1.0f+offset, RecipNearClip); +} + +void +CSprite2d::SetVertices(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3, + float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2) +{ + // Workaround for PVR Z-Equality not being exact + NearCamZ /= 1.1f; + + // This is what we draw: + // 0---1 + // | / | + // 3---2 + RwIm2DVertexSetScreenX(&maVertices[0], r.left); + RwIm2DVertexSetScreenY(&maVertices[0], r.top); + RwIm2DVertexSetScreenZ(&maVertices[0], NearScreenZ); + RwIm2DVertexSetCameraZ(&maVertices[0], NearCamZ); + RwIm2DVertexSetRecipCameraZ(&maVertices[0], RecipNearClip); + RwIm2DVertexSetIntRGBA(&maVertices[0], c2.r, c2.g, c2.b, c2.a); + RwIm2DVertexSetU(&maVertices[0], u0, RecipNearClip); + RwIm2DVertexSetV(&maVertices[0], v0, RecipNearClip); + + RwIm2DVertexSetScreenX(&maVertices[1], r.right); + RwIm2DVertexSetScreenY(&maVertices[1], r.top); + RwIm2DVertexSetScreenZ(&maVertices[1], NearScreenZ); + RwIm2DVertexSetCameraZ(&maVertices[1], NearCamZ); + RwIm2DVertexSetRecipCameraZ(&maVertices[1], RecipNearClip); + RwIm2DVertexSetIntRGBA(&maVertices[1], c3.r, c3.g, c3.b, c3.a); + RwIm2DVertexSetU(&maVertices[1], u1, RecipNearClip); + RwIm2DVertexSetV(&maVertices[1], v1, RecipNearClip); + + RwIm2DVertexSetScreenX(&maVertices[2], r.right); + RwIm2DVertexSetScreenY(&maVertices[2], r.bottom); + RwIm2DVertexSetScreenZ(&maVertices[2], NearScreenZ); + RwIm2DVertexSetCameraZ(&maVertices[2], NearCamZ); + RwIm2DVertexSetRecipCameraZ(&maVertices[2], RecipNearClip); + RwIm2DVertexSetIntRGBA(&maVertices[2], c1.r, c1.g, c1.b, c1.a); + RwIm2DVertexSetU(&maVertices[2], u2, RecipNearClip); + RwIm2DVertexSetV(&maVertices[2], v2, RecipNearClip); + + RwIm2DVertexSetScreenX(&maVertices[3], r.left); + RwIm2DVertexSetScreenY(&maVertices[3], r.bottom); + RwIm2DVertexSetScreenZ(&maVertices[3], NearScreenZ); + RwIm2DVertexSetCameraZ(&maVertices[3], NearCamZ); + RwIm2DVertexSetRecipCameraZ(&maVertices[3], RecipNearClip); + RwIm2DVertexSetIntRGBA(&maVertices[3], c0.r, c0.g, c0.b, c0.a); + RwIm2DVertexSetU(&maVertices[3], u3, RecipNearClip); + RwIm2DVertexSetV(&maVertices[3], v3, RecipNearClip); +} + +void +CSprite2d::SetVertices(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, + const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3, DrawType dt) +{ + // Workaround for PVR Z-Equality not being exact + if (dt == DRAW_NEAR) { + NearCamZ /= 1.1f; + } + + float screenZ = dt == DRAW_FAR ? FarScreenZ : NearScreenZ; + float camZ = dt == DRAW_FAR ? FarCamZ : NearCamZ; + float recipClip = dt == DRAW_FAR ? RecipFarClip : RecipNearClip; + + RwIm2DVertexSetScreenX(&maVertices[0], x3); + RwIm2DVertexSetScreenY(&maVertices[0], y3); + RwIm2DVertexSetScreenZ(&maVertices[0], screenZ); + RwIm2DVertexSetCameraZ(&maVertices[0], camZ); + RwIm2DVertexSetRecipCameraZ(&maVertices[0], recipClip); + RwIm2DVertexSetIntRGBA(&maVertices[0], c2.r, c2.g, c2.b, c2.a); + RwIm2DVertexSetU(&maVertices[0], 0.0f, recipClip); + RwIm2DVertexSetV(&maVertices[0], 0.0f, recipClip); + + RwIm2DVertexSetScreenX(&maVertices[1], x4); + RwIm2DVertexSetScreenY(&maVertices[1], y4); + RwIm2DVertexSetScreenZ(&maVertices[1], screenZ); + RwIm2DVertexSetCameraZ(&maVertices[1], camZ); + RwIm2DVertexSetRecipCameraZ(&maVertices[1], recipClip); + RwIm2DVertexSetIntRGBA(&maVertices[1], c3.r, c3.g, c3.b, c3.a); + RwIm2DVertexSetU(&maVertices[1], 1.0f, recipClip); + RwIm2DVertexSetV(&maVertices[1], 0.0f, recipClip); + + RwIm2DVertexSetScreenX(&maVertices[2], x2); + RwIm2DVertexSetScreenY(&maVertices[2], y2); + RwIm2DVertexSetScreenZ(&maVertices[2], screenZ); + RwIm2DVertexSetCameraZ(&maVertices[2], camZ); + RwIm2DVertexSetRecipCameraZ(&maVertices[2], recipClip); + RwIm2DVertexSetIntRGBA(&maVertices[2], c1.r, c1.g, c1.b, c1.a); + RwIm2DVertexSetU(&maVertices[2], 1.0f, recipClip); + RwIm2DVertexSetV(&maVertices[2], 1.0f, recipClip); + + RwIm2DVertexSetScreenX(&maVertices[3], x1); + RwIm2DVertexSetScreenY(&maVertices[3], y1); + RwIm2DVertexSetScreenZ(&maVertices[3], screenZ); + RwIm2DVertexSetCameraZ(&maVertices[3], camZ); + RwIm2DVertexSetRecipCameraZ(&maVertices[3], recipClip); + RwIm2DVertexSetIntRGBA(&maVertices[3], c0.r, c0.g, c0.b, c0.a); + RwIm2DVertexSetU(&maVertices[3], 0.0f, recipClip); + RwIm2DVertexSetV(&maVertices[3], 1.0f, recipClip); +} + +void +CSprite2d::SetVertices(int n, float *positions, float *uvs, const CRGBA &col) +{ + // Workaround for PVR Z-Equality not being exact + NearCamZ /= 1.1f; + + int i; + + for(i = 0; i < n; i++){ + RwIm2DVertexSetScreenX(&maVertices[i], positions[i*2 + 0]); + RwIm2DVertexSetScreenY(&maVertices[i], positions[i*2 + 1]); + RwIm2DVertexSetScreenZ(&maVertices[i], NearScreenZ + 0.0001f); + RwIm2DVertexSetCameraZ(&maVertices[i], NearCamZ); + RwIm2DVertexSetRecipCameraZ(&maVertices[i], RecipNearClip); + RwIm2DVertexSetIntRGBA(&maVertices[i], col.r, col.g, col.b, col.a); + RwIm2DVertexSetU(&maVertices[i], uvs[i*2 + 0], RecipNearClip); + RwIm2DVertexSetV(&maVertices[i], uvs[i*2 + 1], RecipNearClip); + } +} + +void +CSprite2d::SetMaskVertices(int n, float *positions) +{ + int i; + + for(i = 0; i < n; i++){ + RwIm2DVertexSetScreenX(&maVertices[i], positions[i*2 + 0]); + RwIm2DVertexSetScreenY(&maVertices[i], positions[i*2 + 1]); + RwIm2DVertexSetScreenZ(&maVertices[i], NearScreenZ); + RwIm2DVertexSetCameraZ(&maVertices[i], NearCamZ); + RwIm2DVertexSetRecipCameraZ(&maVertices[i], RecipNearClip); +#if !defined(GTA_PS2_STUFF) && defined(RWLIBS) + RwIm2DVertexSetIntRGBA(&maVertices[i], 0, 0, 0, 0); +#else + RwIm2DVertexSetIntRGBA(&maVertices[i], 255, 255, 255, 255); +#endif + } +} + +void +CSprite2d::SetVertices(RwIm2DVertex *verts, const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3, + float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2) +{ + // Workaround for PVR Z-Equality not being exact + NearCamZ /= 1.1f; + + RwIm2DVertexSetScreenX(&verts[0], r.left); + RwIm2DVertexSetScreenY(&verts[0], r.top); + RwIm2DVertexSetScreenZ(&verts[0], NearScreenZ); + RwIm2DVertexSetCameraZ(&verts[0], NearCamZ); + RwIm2DVertexSetRecipCameraZ(&verts[0], RecipNearClip); + RwIm2DVertexSetIntRGBA(&verts[0], c2.r, c2.g, c2.b, c2.a); + RwIm2DVertexSetU(&verts[0], u0, RecipNearClip); + RwIm2DVertexSetV(&verts[0], v0, RecipNearClip); + + RwIm2DVertexSetScreenX(&verts[1], r.right); + RwIm2DVertexSetScreenY(&verts[1], r.top); + RwIm2DVertexSetScreenZ(&verts[1], NearScreenZ); + RwIm2DVertexSetCameraZ(&verts[1], NearCamZ); + RwIm2DVertexSetRecipCameraZ(&verts[1], RecipNearClip); + RwIm2DVertexSetIntRGBA(&verts[1], c3.r, c3.g, c3.b, c3.a); + RwIm2DVertexSetU(&verts[1], u1, RecipNearClip); + RwIm2DVertexSetV(&verts[1], v1, RecipNearClip); + + RwIm2DVertexSetScreenX(&verts[2], r.right); + RwIm2DVertexSetScreenY(&verts[2], r.bottom); + RwIm2DVertexSetScreenZ(&verts[2], NearScreenZ); + RwIm2DVertexSetCameraZ(&verts[2], NearCamZ); + RwIm2DVertexSetRecipCameraZ(&verts[2], RecipNearClip); + RwIm2DVertexSetIntRGBA(&verts[2], c1.r, c1.g, c1.b, c1.a); + RwIm2DVertexSetU(&verts[2], u2, RecipNearClip); + RwIm2DVertexSetV(&verts[2], v2, RecipNearClip); + + RwIm2DVertexSetScreenX(&verts[3], r.left); + RwIm2DVertexSetScreenY(&verts[3], r.bottom); + RwIm2DVertexSetScreenZ(&verts[3], NearScreenZ); + RwIm2DVertexSetCameraZ(&verts[3], NearCamZ); + RwIm2DVertexSetRecipCameraZ(&verts[3], RecipNearClip); + RwIm2DVertexSetIntRGBA(&verts[3], c0.r, c0.g, c0.b, c0.a); + RwIm2DVertexSetU(&verts[3], u3, RecipNearClip); + RwIm2DVertexSetV(&verts[3], v3, RecipNearClip); +} + +void +CSprite2d::DrawRect(const CRect &r, const CRGBA &col) +{ + SetVertices(r, col, col, col, col); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEFLAT); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)(col.a != 255)); + RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, maVertices, 4); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEGOURAUD); +} + +void +CSprite2d::DrawRect(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3, DrawType dt) +{ + SetVertices(r, c0, c1, c2, c3); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + if (dt == DRAW_FAR) { + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + } else { + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + } + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, maVertices, 4); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); +} + +void +CSprite2d::DrawRectXLU(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3) +{ + SetVertices(r, c0, c1, c2, c3); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); +} + +void +CSprite2d::DrawAnyRect(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, + const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3, DrawType dt) +{ + SetVertices(x1, y1, x2, y2, x3, y3, x4, y4, c0, c1, c2, c3, dt); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); + RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEGOURAUD); + if (dt == DRAW_FAR) { + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + } else { + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + } + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)(c0.alpha != 255 || c1.alpha != 255 || c2.alpha != 255 || c3.alpha != 255)); + RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEGOURAUD); +} + +void CSprite2d::Draw2DPolygon(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA &color) +{ + SetVertices(x1, y1, x2, y2, x3, y3, x4, y4, color, color, color, color); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, 0); + RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEFLAT); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)(color.a != 255)); + RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEGOURAUD); +} + +void +CSprite2d::AddToBuffer(const CRect &r, const CRGBA &c, float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2) +{ + SetVertices(&TempVertexBuffer.im2d[nextBufferVertex], r, c, c, c, c, u0, v0, u1, v1, u3, v3, u2, v2); + RwImVertexIndex *pIndexList = &TempBufferRenderIndexList[nextBufferIndex]; + pIndexList[0] = nextBufferVertex; + pIndexList[1] = nextBufferVertex + 1; + pIndexList[2] = nextBufferVertex + 2; + pIndexList[3] = nextBufferVertex + 3; + pIndexList[4] = nextBufferVertex; + pIndexList[5] = nextBufferVertex + 2; + nextBufferIndex += 6; + nextBufferVertex += 4; + if (IsVertexBufferFull()) + RenderVertexBuffer(); +} + +bool +CSprite2d::IsVertexBufferFull() +{ + return (nextBufferVertex > TEMPBUFFERVERTSIZE-128-4 || nextBufferIndex > ARRAY_SIZE(TempBufferRenderIndexList)-6); +} + +void +CSprite2d::RenderVertexBuffer() +{ + if (nextBufferVertex > 0) { + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); + RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempVertexBuffer.im2d, nextBufferVertex, TempBufferRenderIndexList, nextBufferIndex); + nextBufferVertex = 0; + nextBufferIndex = 0; + } +} \ No newline at end of file diff --git a/src/miami/renderer/Sprite2d.h b/src/miami/renderer/Sprite2d.h new file mode 100644 index 00000000..f37bfdf0 --- /dev/null +++ b/src/miami/renderer/Sprite2d.h @@ -0,0 +1,64 @@ +#pragma once + +enum DrawType { + DRAW_FAR, + DRAW_NEAR +}; + +class CSprite2d +{ + static float RecipNearClip; + static float NearScreenZ; + static float NearCamZ; // not original + + static float RecipFarClip; + static float FarScreenZ; + static float FarCamZ; // not original + + static int nextBufferVertex; + static int nextBufferIndex; + static RwIm2DVertex maVertices[8]; +public: + RwTexture *m_pTexture; + + static void SetRecipNearClip(void); + static void InitPerFrame(void); + + CSprite2d(void) : m_pTexture(nil) {}; + ~CSprite2d(void) { Delete(); }; + void Delete(void); + void SetRenderState(void); + void SetTexture(const char *name); + void SetTexture(const char *name, const char *mask); + void SetAddressing(RwTextureAddressMode addr); + void Draw(float x, float y, float w, float h, const CRGBA &col); + void Draw(const CRect &rect, const CRGBA &col); + void Draw(const CRect &rect, const CRGBA &col, + float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2); + void Draw(const CRect &rect, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3); + void Draw(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA &col); + + static void SetVertices(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3); + static void SetVertices(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3, + float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2); + static void SetVertices(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, + const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3, DrawType dt = DRAW_NEAR); + static void SetVertices(int n, float *positions, float *uvs, const CRGBA &col); + static void SetMaskVertices(int n, float *positions); + static void SetVertices(RwIm2DVertex *verts, const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3, + float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2); + + static void DrawRect(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3, DrawType dt = DRAW_NEAR); + static void DrawRect(const CRect &r, const CRGBA &col); + static void DrawRectXLU(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3); + static void DrawAnyRect(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, + const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3, DrawType dt = DRAW_NEAR); + + static void Draw2DPolygon(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA &color); + + static RwIm2DVertex* GetVertices() { return maVertices; }; + + static bool IsVertexBufferFull(); + static void AddToBuffer(const CRect &a1, const CRGBA &a2, float a3, float a4, float a5, float a6, float a7, float a8, float a9, float a10); + static void RenderVertexBuffer(); +}; diff --git a/src/miami/renderer/TexList.cpp b/src/miami/renderer/TexList.cpp new file mode 100644 index 00000000..1689837f --- /dev/null +++ b/src/miami/renderer/TexList.cpp @@ -0,0 +1,41 @@ +#include "common.h" +#include "TexList.h" +#include "rtbmp.h" +#include "FileMgr.h" + +bool CTexList::ms_nTexUsed[MAX_TEXUSED]; + +void +CTexList::Initialise() +{} + +void +CTexList::Shutdown() +{} + +RwTexture * +CTexList::SetTexture(int32 slot, char *name) +{ + return nil; +} + +int32 +CTexList::GetFirstFreeTexture() +{ + for (int32 i = 0; i < MAX_TEXUSED; i++) + if (!ms_nTexUsed[i]) + return i; + return -1; +} + +RwTexture * +CTexList::LoadFileNameTexture(char *name) +{ + return SetTexture(GetFirstFreeTexture(), name); +} + +void +CTexList::LoadGlobalTextureList() +{ + CFileMgr::SetDir("TEXTURES"); +} \ No newline at end of file diff --git a/src/miami/renderer/TexList.h b/src/miami/renderer/TexList.h new file mode 100644 index 00000000..7e042211 --- /dev/null +++ b/src/miami/renderer/TexList.h @@ -0,0 +1,14 @@ +#pragma once + +class CTexList +{ + enum { MAX_TEXUSED = 400, }; + static bool ms_nTexUsed[MAX_TEXUSED]; +public: + static void Initialise(); + static void Shutdown(); + static RwTexture *SetTexture(int32 slot, char *name); + static int32 GetFirstFreeTexture(); + static RwTexture *LoadFileNameTexture(char *name); + static void LoadGlobalTextureList(); +}; \ No newline at end of file diff --git a/src/miami/renderer/Timecycle.cpp b/src/miami/renderer/Timecycle.cpp new file mode 100644 index 00000000..95d9fe3c --- /dev/null +++ b/src/miami/renderer/Timecycle.cpp @@ -0,0 +1,526 @@ +#include "common.h" + +#include "main.h" +#include "Clock.h" +#include "Weather.h" +#include "Camera.h" +#include "Shadows.h" +#include "ZoneCull.h" +#include "CutsceneMgr.h" +#include "FileMgr.h" +#include "Timecycle.h" + +uint8 CTimeCycle::m_nAmbientRed[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nAmbientGreen[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nAmbientBlue[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nAmbientRed_Obj[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nAmbientGreen_Obj[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nAmbientBlue_Obj[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nAmbientRed_Bl[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nAmbientGreen_Bl[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nAmbientBlue_Bl[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nAmbientRed_Obj_Bl[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nAmbientGreen_Obj_Bl[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nAmbientBlue_Obj_Bl[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nDirectionalRed[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nDirectionalGreen[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nDirectionalBlue[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nSkyTopRed[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nSkyTopGreen[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nSkyTopBlue[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nSkyBottomRed[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nSkyBottomGreen[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nSkyBottomBlue[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nSunCoreRed[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nSunCoreGreen[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nSunCoreBlue[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nSunCoronaRed[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nSunCoronaGreen[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nSunCoronaBlue[NUMHOURS][NUMWEATHERS]; +int8 CTimeCycle::m_fSunSize[NUMHOURS][NUMWEATHERS]; +int8 CTimeCycle::m_fSpriteSize[NUMHOURS][NUMWEATHERS]; +int8 CTimeCycle::m_fSpriteBrightness[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nShadowStrength[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nLightShadowStrength[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nPoleShadowStrength[NUMHOURS][NUMWEATHERS]; +int16 CTimeCycle::m_fFogStart[NUMHOURS][NUMWEATHERS]; +int16 CTimeCycle::m_fFarClip[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_fLightsOnGroundBrightness[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nLowCloudsRed[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nLowCloudsGreen[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nLowCloudsBlue[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nFluffyCloudsTopRed[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nFluffyCloudsTopGreen[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nFluffyCloudsTopBlue[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nFluffyCloudsBottomRed[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nFluffyCloudsBottomGreen[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_nFluffyCloudsBottomBlue[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_fBlurRed[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_fBlurGreen[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_fBlurBlue[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_fWaterRed[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_fWaterGreen[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_fWaterBlue[NUMHOURS][NUMWEATHERS]; +uint8 CTimeCycle::m_fWaterAlpha[NUMHOURS][NUMWEATHERS]; + + +float CTimeCycle::m_fCurrentAmbientRed; +float CTimeCycle::m_fCurrentAmbientGreen; +float CTimeCycle::m_fCurrentAmbientBlue; +float CTimeCycle::m_fCurrentAmbientRed_Obj; +float CTimeCycle::m_fCurrentAmbientGreen_Obj; +float CTimeCycle::m_fCurrentAmbientBlue_Obj; +float CTimeCycle::m_fCurrentAmbientRed_Bl; +float CTimeCycle::m_fCurrentAmbientGreen_Bl; +float CTimeCycle::m_fCurrentAmbientBlue_Bl; +float CTimeCycle::m_fCurrentAmbientRed_Obj_Bl; +float CTimeCycle::m_fCurrentAmbientGreen_Obj_Bl; +float CTimeCycle::m_fCurrentAmbientBlue_Obj_Bl; +float CTimeCycle::m_fCurrentDirectionalRed; +float CTimeCycle::m_fCurrentDirectionalGreen; +float CTimeCycle::m_fCurrentDirectionalBlue; +int32 CTimeCycle::m_nCurrentSkyTopRed; +int32 CTimeCycle::m_nCurrentSkyTopGreen; +int32 CTimeCycle::m_nCurrentSkyTopBlue; +int32 CTimeCycle::m_nCurrentSkyBottomRed; +int32 CTimeCycle::m_nCurrentSkyBottomGreen; +int32 CTimeCycle::m_nCurrentSkyBottomBlue; +int32 CTimeCycle::m_nCurrentSunCoreRed; +int32 CTimeCycle::m_nCurrentSunCoreGreen; +int32 CTimeCycle::m_nCurrentSunCoreBlue; +int32 CTimeCycle::m_nCurrentSunCoronaRed; +int32 CTimeCycle::m_nCurrentSunCoronaGreen; +int32 CTimeCycle::m_nCurrentSunCoronaBlue; +float CTimeCycle::m_fCurrentSunSize; +float CTimeCycle::m_fCurrentSpriteSize; +float CTimeCycle::m_fCurrentSpriteBrightness; +int32 CTimeCycle::m_nCurrentShadowStrength; +int32 CTimeCycle::m_nCurrentLightShadowStrength; +int32 CTimeCycle::m_nCurrentPoleShadowStrength; +float CTimeCycle::m_fCurrentFogStart; +float CTimeCycle::m_fCurrentFarClip; +float CTimeCycle::m_fCurrentLightsOnGroundBrightness; +int32 CTimeCycle::m_nCurrentLowCloudsRed; +int32 CTimeCycle::m_nCurrentLowCloudsGreen; +int32 CTimeCycle::m_nCurrentLowCloudsBlue; +int32 CTimeCycle::m_nCurrentFluffyCloudsTopRed; +int32 CTimeCycle::m_nCurrentFluffyCloudsTopGreen; +int32 CTimeCycle::m_nCurrentFluffyCloudsTopBlue; +int32 CTimeCycle::m_nCurrentFluffyCloudsBottomRed; +int32 CTimeCycle::m_nCurrentFluffyCloudsBottomGreen; +int32 CTimeCycle::m_nCurrentFluffyCloudsBottomBlue; +float CTimeCycle::m_fCurrentBlurRed; +float CTimeCycle::m_fCurrentBlurGreen; +float CTimeCycle::m_fCurrentBlurBlue; +float CTimeCycle::m_fCurrentWaterRed; +float CTimeCycle::m_fCurrentWaterGreen; +float CTimeCycle::m_fCurrentWaterBlue; +float CTimeCycle::m_fCurrentWaterAlpha; +int32 CTimeCycle::m_nCurrentFogColourRed; +int32 CTimeCycle::m_nCurrentFogColourGreen; +int32 CTimeCycle::m_nCurrentFogColourBlue; + +int32 CTimeCycle::m_FogReduction; +int32 CTimeCycle::m_bExtraColourOn; +int32 CTimeCycle::m_ExtraColour; +float CTimeCycle::m_ExtraColourInter; + +int32 CTimeCycle::m_CurrentStoredValue; +CVector CTimeCycle::m_VectorToSun[16]; +float CTimeCycle::m_fShadowFrontX[16]; +float CTimeCycle::m_fShadowFrontY[16]; +float CTimeCycle::m_fShadowSideX[16]; +float CTimeCycle::m_fShadowSideY[16]; +float CTimeCycle::m_fShadowDisplacementX[16]; +float CTimeCycle::m_fShadowDisplacementY[16]; + +void +CTimeCycle::Initialise(void) +{ + int w, h; + int li, bi; + char line[1040]; + + int ambR, ambG, ambB; + int ambobjR, ambobjG, ambobjB; + int ambblR, ambblG, ambblB; + int ambobjblR, ambobjblG, ambobjblB; + int dirR, dirG, dirB; + int skyTopR, skyTopG, skyTopB; + int skyBotR, skyBotG, skyBotB; + int sunCoreR, sunCoreG, sunCoreB; + int sunCoronaR, sunCoronaG, sunCoronaB; + float sunSz, sprSz, sprBght; + int shad, lightShad, poleShad; + float farClp, fogSt, lightGnd; + int cloudR, cloudG, cloudB; + int fluffyTopR, fluffyTopG, fluffyTopB; + int fluffyBotR, fluffyBotG, fluffyBotB; + float blurR, blurG, blurB; + float waterR, waterG, waterB, waterA; + + debug("Intialising CTimeCycle...\n"); + + CFileMgr::SetDir("DATA"); + CFileMgr::LoadFile("TIMECYC.DAT", work_buff, sizeof(work_buff), "rb"); + CFileMgr::SetDir(""); + + line[0] = '\0'; + bi = 0; + for(w = 0; w < NUMWEATHERS; w++) + for(h = 0; h < NUMHOURS; h++){ + li = 0; + while(work_buff[bi] == '/' || work_buff[bi] == '\n' || + work_buff[bi] == '\0' || work_buff[bi] == ' ' || work_buff[bi] == '\r'){ + while(work_buff[bi] != '\n' && work_buff[bi] != '\0' && work_buff[bi] != '\r') + bi++; + bi++; + } + while(work_buff[bi] != '\n' +#ifdef FIX_BUGS + && work_buff[bi] != '\0' +#endif + ) + line[li++] = work_buff[bi++]; + line[li] = '\0'; + bi++; + + sscanf(line, "%d %d %d %d %d %d %d %d %d %d %d %d " + "%d %d %d %d %d %d %d %d %d " + "%d %d %d %d %d %d %f %f %f %d %d %d %f %f %f " + "%d %d %d %d %d %d %d %d %d %f %f %f %f %f %f %f", + &ambR, &ambG, &ambB, + &ambobjR, &ambobjG, &ambobjB, + &ambblR, &ambblG, &ambblB, + &ambobjblR, &ambobjblG, &ambobjblB, + &dirR, &dirG, &dirB, + &skyTopR, &skyTopG, &skyTopB, + &skyBotR, &skyBotG, &skyBotB, + &sunCoreR, &sunCoreG, &sunCoreB, + &sunCoronaR, &sunCoronaG, &sunCoronaB, + &sunSz, &sprSz, &sprBght, + &shad, &lightShad, &poleShad, + &farClp, &fogSt, &lightGnd, + &cloudR, &cloudG, &cloudB, + &fluffyTopR, &fluffyTopG, &fluffyTopB, + &fluffyBotR, &fluffyBotG, &fluffyBotB, + &blurR, &blurG, &blurB, + &waterR, &waterG, &waterB, &waterA); + + m_nAmbientRed[h][w] = ambR; + m_nAmbientGreen[h][w] = ambG; + m_nAmbientBlue[h][w] = ambB; + m_nAmbientRed_Obj[h][w] = ambobjR; + m_nAmbientGreen_Obj[h][w] = ambobjG; + m_nAmbientBlue_Obj[h][w] = ambobjB; + m_nAmbientRed_Bl[h][w] = ambblR; + m_nAmbientGreen_Bl[h][w] = ambblG; + m_nAmbientBlue_Bl[h][w] = ambblB; + m_nAmbientRed_Obj_Bl[h][w] = ambobjblR; + m_nAmbientGreen_Obj_Bl[h][w] = ambobjblG; + m_nAmbientBlue_Obj_Bl[h][w] = ambobjblB; + m_nDirectionalRed[h][w] = dirR; + m_nDirectionalGreen[h][w] = dirG; + m_nDirectionalBlue[h][w] = dirB; + m_nSkyTopRed[h][w] = skyTopR; + m_nSkyTopGreen[h][w] = skyTopG; + m_nSkyTopBlue[h][w] = skyTopB; + m_nSkyBottomRed[h][w] = skyBotR; + m_nSkyBottomGreen[h][w] = skyBotG; + m_nSkyBottomBlue[h][w] = skyBotB; + m_nSunCoreRed[h][w] = sunCoreR; + m_nSunCoreGreen[h][w] = sunCoreG; + m_nSunCoreBlue[h][w] = sunCoreB; + m_nSunCoronaRed[h][w] = sunCoronaR; + m_nSunCoronaGreen[h][w] = sunCoronaG; + m_nSunCoronaBlue[h][w] = sunCoronaB; + m_fSunSize[h][w] = sunSz * 10.0f; + m_fSpriteSize[h][w] = sprSz * 10.0f; + m_fSpriteBrightness[h][w] = sprBght * 10.0f; + m_nShadowStrength[h][w] = shad; + m_nLightShadowStrength[h][w] = lightShad; + m_nPoleShadowStrength[h][w] = poleShad; + m_fFarClip[h][w] = farClp; + m_fFogStart[h][w] = fogSt; + m_fLightsOnGroundBrightness[h][w] = lightGnd * 10.0f; + m_nLowCloudsRed[h][w] = cloudR; + m_nLowCloudsGreen[h][w] = cloudG; + m_nLowCloudsBlue[h][w] = cloudB; + m_nFluffyCloudsTopRed[h][w] = fluffyTopR; + m_nFluffyCloudsTopGreen[h][w] = fluffyTopG; + m_nFluffyCloudsTopBlue[h][w] = fluffyTopB; + m_nFluffyCloudsBottomRed[h][w] = fluffyBotR; + m_nFluffyCloudsBottomGreen[h][w] = fluffyBotG; + m_nFluffyCloudsBottomBlue[h][w] = fluffyBotB; + m_fBlurRed[h][w] = blurR; + m_fBlurGreen[h][w] = blurG; + m_fBlurBlue[h][w] = blurB; + m_fWaterRed[h][w] = waterR; + m_fWaterGreen[h][w] = waterG; + m_fWaterBlue[h][w] = waterB; + m_fWaterAlpha[h][w] = waterA; + } + + m_FogReduction = 0; + + debug("CTimeCycle ready\n"); +} + +static float interp_c0, interp_c1, interp_c2, interp_c3; + +float CTimeCycle::Interpolate(int8 *a, int8 *b) +{ + return a[CWeather::OldWeatherType] * interp_c0 + + b[CWeather::OldWeatherType] * interp_c1 + + a[CWeather::NewWeatherType] * interp_c2 + + b[CWeather::NewWeatherType] * interp_c3; +} + +float CTimeCycle::Interpolate(uint8 *a, uint8 *b) +{ + return a[CWeather::OldWeatherType] * interp_c0 + + b[CWeather::OldWeatherType] * interp_c1 + + a[CWeather::NewWeatherType] * interp_c2 + + b[CWeather::NewWeatherType] * interp_c3; +} + +float CTimeCycle::Interpolate(int16 *a, int16 *b) +{ + return a[CWeather::OldWeatherType] * interp_c0 + + b[CWeather::OldWeatherType] * interp_c1 + + a[CWeather::NewWeatherType] * interp_c2 + + b[CWeather::NewWeatherType] * interp_c3; +} + +void +CTimeCycle::StartExtraColour(int32 c, bool fade) +{ + m_bExtraColourOn = true; + m_ExtraColour = c; + if(fade) + m_ExtraColourInter = 0.0f; + else + m_ExtraColourInter = 1.0f; +} + +void +CTimeCycle::StopExtraColour(bool fade) +{ + m_bExtraColourOn = false; + if(!fade) + m_ExtraColourInter = 0.0f; +} + +void +CTimeCycle::Update(void) +{ + int h1 = CClock::GetHours(); + int h2 = (h1+1)%24; + int w1 = CWeather::OldWeatherType; + int w2 = CWeather::NewWeatherType; + float timeInterp = (CClock::GetMinutes() + CClock::GetSeconds()/60.0f)/60.0f; + // coefficients for a bilinear interpolation + interp_c0 = (1.0f-timeInterp) * (1.0f-CWeather::InterpolationValue); + interp_c1 = timeInterp * (1.0f-CWeather::InterpolationValue); + interp_c2 = (1.0f-timeInterp) * CWeather::InterpolationValue; + interp_c3 = timeInterp * CWeather::InterpolationValue; + +#define INTERP(v) Interpolate(v[h1], v[h2]) + + m_nCurrentSkyTopRed = INTERP(m_nSkyTopRed); + m_nCurrentSkyTopGreen = INTERP(m_nSkyTopGreen); + m_nCurrentSkyTopBlue = INTERP(m_nSkyTopBlue); + + m_nCurrentSkyBottomRed = INTERP(m_nSkyBottomRed); + m_nCurrentSkyBottomGreen = INTERP(m_nSkyBottomGreen); + m_nCurrentSkyBottomBlue = INTERP(m_nSkyBottomBlue); + + m_fCurrentAmbientRed = INTERP(m_nAmbientRed); + m_fCurrentAmbientGreen = INTERP(m_nAmbientGreen); + m_fCurrentAmbientBlue = INTERP(m_nAmbientBlue); + + m_fCurrentAmbientRed_Obj = INTERP(m_nAmbientRed_Obj); + m_fCurrentAmbientGreen_Obj = INTERP(m_nAmbientGreen_Obj); + m_fCurrentAmbientBlue_Obj = INTERP(m_nAmbientBlue_Obj); + + m_fCurrentAmbientRed_Bl = INTERP(m_nAmbientRed_Bl); + m_fCurrentAmbientGreen_Bl = INTERP(m_nAmbientGreen_Bl); + m_fCurrentAmbientBlue_Bl = INTERP(m_nAmbientBlue_Bl); + + m_fCurrentAmbientRed_Obj_Bl = INTERP(m_nAmbientRed_Obj_Bl); + m_fCurrentAmbientGreen_Obj_Bl = INTERP(m_nAmbientGreen_Obj_Bl); + m_fCurrentAmbientBlue_Obj_Bl = INTERP(m_nAmbientBlue_Obj_Bl); + + m_fCurrentDirectionalRed = INTERP(m_nDirectionalRed); + m_fCurrentDirectionalGreen = INTERP(m_nDirectionalGreen); + m_fCurrentDirectionalBlue = INTERP(m_nDirectionalBlue); + + m_nCurrentSunCoreRed = INTERP(m_nSunCoreRed); + m_nCurrentSunCoreGreen = INTERP(m_nSunCoreGreen); + m_nCurrentSunCoreBlue = INTERP(m_nSunCoreBlue); + + m_nCurrentSunCoronaRed = INTERP(m_nSunCoronaRed); + m_nCurrentSunCoronaGreen = INTERP(m_nSunCoronaGreen); + m_nCurrentSunCoronaBlue = INTERP(m_nSunCoronaBlue); + + m_fCurrentSunSize = INTERP(m_fSunSize)/10.0f; + m_fCurrentSpriteSize = INTERP(m_fSpriteSize)/10.0f; + m_fCurrentSpriteBrightness = INTERP(m_fSpriteBrightness)/10.0f; + m_nCurrentShadowStrength = INTERP(m_nShadowStrength); + m_nCurrentLightShadowStrength = INTERP(m_nLightShadowStrength); + m_nCurrentPoleShadowStrength = INTERP(m_nPoleShadowStrength); + m_fCurrentFarClip = INTERP(m_fFarClip); + m_fCurrentFogStart = INTERP(m_fFogStart); + m_fCurrentLightsOnGroundBrightness = INTERP(m_fLightsOnGroundBrightness)/10.0f; + + m_nCurrentLowCloudsRed = INTERP(m_nLowCloudsRed); + m_nCurrentLowCloudsGreen = INTERP(m_nLowCloudsGreen); + m_nCurrentLowCloudsBlue = INTERP(m_nLowCloudsBlue); + + m_nCurrentFluffyCloudsTopRed = INTERP(m_nFluffyCloudsTopRed); + m_nCurrentFluffyCloudsTopGreen = INTERP(m_nFluffyCloudsTopGreen); + m_nCurrentFluffyCloudsTopBlue = INTERP(m_nFluffyCloudsTopBlue); + + m_nCurrentFluffyCloudsBottomRed = INTERP(m_nFluffyCloudsBottomRed); + m_nCurrentFluffyCloudsBottomGreen = INTERP(m_nFluffyCloudsBottomGreen); + m_nCurrentFluffyCloudsBottomBlue = INTERP(m_nFluffyCloudsBottomBlue); + + m_fCurrentBlurRed = INTERP(m_fBlurRed); + m_fCurrentBlurGreen = INTERP(m_fBlurGreen); + m_fCurrentBlurBlue = INTERP(m_fBlurBlue); + + m_fCurrentWaterRed = INTERP(m_fWaterRed); + m_fCurrentWaterGreen = INTERP(m_fWaterGreen); + m_fCurrentWaterBlue = INTERP(m_fWaterBlue); + m_fCurrentWaterAlpha = INTERP(m_fWaterAlpha); +#undef INTERP + + if(m_FogReduction != 0) + m_fCurrentFarClip = Max(m_fCurrentFarClip, m_FogReduction/64.0f * 650.0f); + + m_CurrentStoredValue = (m_CurrentStoredValue+1)&0xF; + + float sunAngle = 2*PI*(CClock::GetSeconds()/60.0f + CClock::GetMinutes() + CClock::GetHours()*60)/(24*60); + CVector &sunPos = GetSunDirection(); + sunPos.x = Sin(sunAngle); + sunPos.y = 1.0f; + sunPos.z = 0.2f - Cos(sunAngle); + sunPos.Normalise(); + + if(m_bExtraColourOn) + m_ExtraColourInter = Min(1.0f, m_ExtraColourInter + CTimer::GetTimeStep()/120.0f); + else + m_ExtraColourInter = Max(-.0f, m_ExtraColourInter - CTimer::GetTimeStep()/120.0f); + if(m_ExtraColourInter > 0.0f){ +#define INTERP(extra,cur) (m_ExtraColourInter*extra[m_ExtraColour][WEATHER_EXTRACOLOURS] + (1.0f-m_ExtraColourInter)*cur) +#define INTERPscl(extra,scl,cur) (m_ExtraColourInter*extra[m_ExtraColour][WEATHER_EXTRACOLOURS]/scl + (1.0f-m_ExtraColourInter)*cur) + if(m_nSkyTopRed[m_ExtraColour][WEATHER_EXTRACOLOURS] != 0 || + m_nSkyTopGreen[m_ExtraColour][WEATHER_EXTRACOLOURS] != 0 || + m_nSkyTopBlue[m_ExtraColour][WEATHER_EXTRACOLOURS] != 0){ + m_nCurrentSkyTopRed = INTERP(m_nSkyTopRed,m_nCurrentSkyTopRed); + m_nCurrentSkyTopGreen = INTERP(m_nSkyTopGreen,m_nCurrentSkyTopGreen); + m_nCurrentSkyTopBlue = INTERP(m_nSkyTopBlue,m_nCurrentSkyTopBlue); + + m_nCurrentSkyBottomRed = INTERP(m_nSkyBottomRed,m_nCurrentSkyBottomRed); + m_nCurrentSkyBottomGreen = INTERP(m_nSkyBottomGreen,m_nCurrentSkyBottomGreen); + m_nCurrentSkyBottomBlue = INTERP(m_nSkyBottomBlue,m_nCurrentSkyBottomBlue); + + m_nCurrentSunCoreRed = INTERP(m_nSunCoreRed,m_nCurrentSunCoreRed); + m_nCurrentSunCoreGreen = INTERP(m_nSunCoreGreen,m_nCurrentSunCoreGreen); + m_nCurrentSunCoreBlue = INTERP(m_nSunCoreBlue,m_nCurrentSunCoreBlue); + + m_nCurrentSunCoronaRed = INTERP(m_nSunCoronaRed,m_nCurrentSunCoronaRed); + m_nCurrentSunCoronaGreen = INTERP(m_nSunCoronaGreen,m_nCurrentSunCoronaGreen); + m_nCurrentSunCoronaBlue = INTERP(m_nSunCoronaBlue,m_nCurrentSunCoronaBlue); + + m_fCurrentSunSize = INTERPscl(m_fSunSize,10.0f,m_fCurrentSunSize); + + m_nCurrentLowCloudsRed = INTERP(m_nLowCloudsRed,m_nCurrentLowCloudsRed); + m_nCurrentLowCloudsGreen = INTERP(m_nLowCloudsGreen,m_nCurrentLowCloudsGreen); + m_nCurrentLowCloudsBlue = INTERP(m_nLowCloudsBlue,m_nCurrentLowCloudsBlue); + + m_nCurrentFluffyCloudsTopRed = INTERP(m_nFluffyCloudsTopRed,m_nCurrentFluffyCloudsTopRed); + m_nCurrentFluffyCloudsTopGreen = INTERP(m_nFluffyCloudsTopGreen,m_nCurrentFluffyCloudsTopGreen); + m_nCurrentFluffyCloudsTopBlue = INTERP(m_nFluffyCloudsTopBlue,m_nCurrentFluffyCloudsTopBlue); + + m_nCurrentFluffyCloudsBottomRed = INTERP(m_nFluffyCloudsBottomRed,m_nCurrentFluffyCloudsBottomRed); + m_nCurrentFluffyCloudsBottomGreen = INTERP(m_nFluffyCloudsBottomGreen,m_nCurrentFluffyCloudsBottomGreen); + m_nCurrentFluffyCloudsBottomBlue = INTERP(m_nFluffyCloudsBottomBlue,m_nCurrentFluffyCloudsBottomBlue); + + m_fCurrentWaterRed = INTERP(m_fWaterRed,m_fCurrentWaterRed); + m_fCurrentWaterGreen = INTERP(m_fWaterGreen,m_fCurrentWaterGreen); + m_fCurrentWaterBlue = INTERP(m_fWaterBlue,m_fCurrentWaterBlue); + m_fCurrentWaterAlpha = INTERP(m_fWaterAlpha,m_fCurrentWaterAlpha); + } + + m_fCurrentAmbientRed = INTERP(m_nAmbientRed,m_fCurrentAmbientRed); + m_fCurrentAmbientGreen = INTERP(m_nAmbientGreen,m_fCurrentAmbientGreen); + m_fCurrentAmbientBlue = INTERP(m_nAmbientBlue,m_fCurrentAmbientBlue); + + m_fCurrentAmbientRed_Obj = INTERP(m_nAmbientRed_Obj,m_fCurrentAmbientRed_Obj); + m_fCurrentAmbientGreen_Obj = INTERP(m_nAmbientGreen_Obj,m_fCurrentAmbientGreen_Obj); + m_fCurrentAmbientBlue_Obj = INTERP(m_nAmbientBlue_Obj,m_fCurrentAmbientBlue_Obj); + + m_fCurrentAmbientRed_Bl = INTERP(m_nAmbientRed_Bl,m_fCurrentAmbientRed_Bl); + m_fCurrentAmbientGreen_Bl = INTERP(m_nAmbientGreen_Bl,m_fCurrentAmbientGreen_Bl); + m_fCurrentAmbientBlue_Bl = INTERP(m_nAmbientBlue_Bl,m_fCurrentAmbientBlue_Bl); + + m_fCurrentAmbientRed_Obj_Bl = INTERP(m_nAmbientRed_Obj_Bl,m_fCurrentAmbientRed_Obj_Bl); + m_fCurrentAmbientGreen_Obj_Bl = INTERP(m_nAmbientGreen_Obj_Bl,m_fCurrentAmbientGreen_Obj_Bl); + m_fCurrentAmbientBlue_Obj_Bl = INTERP(m_nAmbientBlue_Obj_Bl,m_fCurrentAmbientBlue_Obj_Bl); + + m_fCurrentDirectionalRed = INTERP(m_nDirectionalRed,m_fCurrentDirectionalRed); + m_fCurrentDirectionalGreen = INTERP(m_nDirectionalGreen,m_fCurrentDirectionalGreen); + m_fCurrentDirectionalBlue = INTERP(m_nDirectionalBlue,m_fCurrentDirectionalBlue); + + m_fCurrentSpriteSize = INTERPscl(m_fSpriteSize,10.0f,m_fCurrentSpriteSize); + m_fCurrentSpriteBrightness = INTERPscl(m_fSpriteBrightness,10.0f,m_fCurrentSpriteBrightness); + m_nCurrentShadowStrength = INTERP(m_nShadowStrength,m_nCurrentShadowStrength); + m_nCurrentLightShadowStrength = INTERP(m_nLightShadowStrength,m_nCurrentLightShadowStrength); + m_nCurrentPoleShadowStrength = INTERP(m_nPoleShadowStrength,m_nCurrentPoleShadowStrength); + m_fCurrentFarClip = INTERP(m_fFarClip,m_fCurrentFarClip); + m_fCurrentFogStart = INTERP(m_fFogStart,m_fCurrentFogStart); + m_fCurrentLightsOnGroundBrightness = INTERPscl(m_fLightsOnGroundBrightness,10.0f,m_fCurrentLightsOnGroundBrightness); + + m_fCurrentBlurRed = INTERP(m_fBlurRed,m_fCurrentBlurRed); + m_fCurrentBlurGreen = INTERP(m_fBlurGreen,m_fCurrentBlurGreen); + m_fCurrentBlurBlue = INTERP(m_fBlurBlue,m_fCurrentBlurBlue); + +#undef INTERP +#undef INTERPscl + } + + if(TheCamera.m_BlurType == MOTION_BLUR_NONE || TheCamera.m_BlurType == MOTION_BLUR_LIGHT_SCENE) + TheCamera.SetMotionBlur(m_fCurrentBlurRed, m_fCurrentBlurGreen, m_fCurrentBlurBlue, 5, MOTION_BLUR_LIGHT_SCENE); + + m_nCurrentFogColourRed = (m_nCurrentSkyTopRed + 2*m_nCurrentSkyBottomRed) / 3; + m_nCurrentFogColourGreen = (m_nCurrentSkyTopGreen + 2*m_nCurrentSkyBottomGreen) / 3; + m_nCurrentFogColourBlue = (m_nCurrentSkyTopBlue + 2*m_nCurrentSkyBottomBlue) / 3; + + m_fCurrentAmbientRed /= 255.0f; + m_fCurrentAmbientGreen /= 255.0f; + m_fCurrentAmbientBlue /= 255.0f; + m_fCurrentAmbientRed_Obj /= 255.0f; + m_fCurrentAmbientGreen_Obj /= 255.0f; + m_fCurrentAmbientBlue_Obj /= 255.0f; + m_fCurrentAmbientRed_Bl /= 255.0f; + m_fCurrentAmbientGreen_Bl /= 255.0f; + m_fCurrentAmbientBlue_Bl /= 255.0f; + m_fCurrentAmbientRed_Obj_Bl /= 255.0f; + m_fCurrentAmbientGreen_Obj_Bl /= 255.0f; + m_fCurrentAmbientBlue_Obj_Bl /= 255.0f; + m_fCurrentDirectionalRed /= 255.0f; + m_fCurrentDirectionalGreen /= 255.0f; + m_fCurrentDirectionalBlue /= 255.0f; + + CShadows::CalcPedShadowValues(sunPos, + &m_fShadowFrontX[m_CurrentStoredValue], &m_fShadowFrontY[m_CurrentStoredValue], + &m_fShadowSideX[m_CurrentStoredValue], &m_fShadowSideY[m_CurrentStoredValue], + &m_fShadowDisplacementX[m_CurrentStoredValue], &m_fShadowDisplacementY[m_CurrentStoredValue]); + + if(TheCamera.GetForward().z < -0.9f || + !CWeather::bScriptsForceRain && (CCullZones::PlayerNoRain() || CCullZones::CamNoRain() || CCutsceneMgr::IsRunning())) + m_FogReduction = Min(m_FogReduction+1, 64); + else + m_FogReduction = Max(m_FogReduction-1, 0); +} diff --git a/src/miami/renderer/Timecycle.h b/src/miami/renderer/Timecycle.h new file mode 100644 index 00000000..da911b75 --- /dev/null +++ b/src/miami/renderer/Timecycle.h @@ -0,0 +1,201 @@ +#pragma once + +class CTimeCycle +{ + static uint8 m_nAmbientRed[NUMHOURS][NUMWEATHERS]; + static uint8 m_nAmbientGreen[NUMHOURS][NUMWEATHERS]; + static uint8 m_nAmbientBlue[NUMHOURS][NUMWEATHERS]; + static uint8 m_nAmbientRed_Obj[NUMHOURS][NUMWEATHERS]; + static uint8 m_nAmbientGreen_Obj[NUMHOURS][NUMWEATHERS]; + static uint8 m_nAmbientBlue_Obj[NUMHOURS][NUMWEATHERS]; + static uint8 m_nAmbientRed_Bl[NUMHOURS][NUMWEATHERS]; + static uint8 m_nAmbientGreen_Bl[NUMHOURS][NUMWEATHERS]; + static uint8 m_nAmbientBlue_Bl[NUMHOURS][NUMWEATHERS]; + static uint8 m_nAmbientRed_Obj_Bl[NUMHOURS][NUMWEATHERS]; + static uint8 m_nAmbientGreen_Obj_Bl[NUMHOURS][NUMWEATHERS]; + static uint8 m_nAmbientBlue_Obj_Bl[NUMHOURS][NUMWEATHERS]; + static uint8 m_nDirectionalRed[NUMHOURS][NUMWEATHERS]; + static uint8 m_nDirectionalGreen[NUMHOURS][NUMWEATHERS]; + static uint8 m_nDirectionalBlue[NUMHOURS][NUMWEATHERS]; + static uint8 m_nSkyTopRed[NUMHOURS][NUMWEATHERS]; + static uint8 m_nSkyTopGreen[NUMHOURS][NUMWEATHERS]; + static uint8 m_nSkyTopBlue[NUMHOURS][NUMWEATHERS]; + static uint8 m_nSkyBottomRed[NUMHOURS][NUMWEATHERS]; + static uint8 m_nSkyBottomGreen[NUMHOURS][NUMWEATHERS]; + static uint8 m_nSkyBottomBlue[NUMHOURS][NUMWEATHERS]; + static uint8 m_nSunCoreRed[NUMHOURS][NUMWEATHERS]; + static uint8 m_nSunCoreGreen[NUMHOURS][NUMWEATHERS]; + static uint8 m_nSunCoreBlue[NUMHOURS][NUMWEATHERS]; + static uint8 m_nSunCoronaRed[NUMHOURS][NUMWEATHERS]; + static uint8 m_nSunCoronaGreen[NUMHOURS][NUMWEATHERS]; + static uint8 m_nSunCoronaBlue[NUMHOURS][NUMWEATHERS]; + static int8 m_fSunSize[NUMHOURS][NUMWEATHERS]; + static int8 m_fSpriteSize[NUMHOURS][NUMWEATHERS]; + static int8 m_fSpriteBrightness[NUMHOURS][NUMWEATHERS]; + static uint8 m_nShadowStrength[NUMHOURS][NUMWEATHERS]; + static uint8 m_nLightShadowStrength[NUMHOURS][NUMWEATHERS]; + static uint8 m_nPoleShadowStrength[NUMHOURS][NUMWEATHERS]; + static int16 m_fFogStart[NUMHOURS][NUMWEATHERS]; + static int16 m_fFarClip[NUMHOURS][NUMWEATHERS]; + static uint8 m_fLightsOnGroundBrightness[NUMHOURS][NUMWEATHERS]; + static uint8 m_nLowCloudsRed[NUMHOURS][NUMWEATHERS]; + static uint8 m_nLowCloudsGreen[NUMHOURS][NUMWEATHERS]; + static uint8 m_nLowCloudsBlue[NUMHOURS][NUMWEATHERS]; + static uint8 m_nFluffyCloudsTopRed[NUMHOURS][NUMWEATHERS]; + static uint8 m_nFluffyCloudsTopGreen[NUMHOURS][NUMWEATHERS]; + static uint8 m_nFluffyCloudsTopBlue[NUMHOURS][NUMWEATHERS]; + static uint8 m_nFluffyCloudsBottomRed[NUMHOURS][NUMWEATHERS]; + static uint8 m_nFluffyCloudsBottomGreen[NUMHOURS][NUMWEATHERS]; + static uint8 m_nFluffyCloudsBottomBlue[NUMHOURS][NUMWEATHERS]; + static uint8 m_fBlurRed[NUMHOURS][NUMWEATHERS]; + static uint8 m_fBlurGreen[NUMHOURS][NUMWEATHERS]; + static uint8 m_fBlurBlue[NUMHOURS][NUMWEATHERS]; + static uint8 m_fWaterRed[NUMHOURS][NUMWEATHERS]; + static uint8 m_fWaterGreen[NUMHOURS][NUMWEATHERS]; + static uint8 m_fWaterBlue[NUMHOURS][NUMWEATHERS]; + static uint8 m_fWaterAlpha[NUMHOURS][NUMWEATHERS]; + + static float m_fCurrentAmbientRed; + static float m_fCurrentAmbientGreen; + static float m_fCurrentAmbientBlue; + static float m_fCurrentAmbientRed_Obj; + static float m_fCurrentAmbientGreen_Obj; + static float m_fCurrentAmbientBlue_Obj; + static float m_fCurrentAmbientRed_Bl; + static float m_fCurrentAmbientGreen_Bl; + static float m_fCurrentAmbientBlue_Bl; + static float m_fCurrentAmbientRed_Obj_Bl; + static float m_fCurrentAmbientGreen_Obj_Bl; + static float m_fCurrentAmbientBlue_Obj_Bl; + static float m_fCurrentDirectionalRed; + static float m_fCurrentDirectionalGreen; + static float m_fCurrentDirectionalBlue; + static int32 m_nCurrentSkyTopRed; + static int32 m_nCurrentSkyTopGreen; + static int32 m_nCurrentSkyTopBlue; + static int32 m_nCurrentSkyBottomRed; + static int32 m_nCurrentSkyBottomGreen; + static int32 m_nCurrentSkyBottomBlue; + static int32 m_nCurrentSunCoreRed; + static int32 m_nCurrentSunCoreGreen; + static int32 m_nCurrentSunCoreBlue; + static int32 m_nCurrentSunCoronaRed; + static int32 m_nCurrentSunCoronaGreen; + static int32 m_nCurrentSunCoronaBlue; + static float m_fCurrentSunSize; + static float m_fCurrentSpriteSize; + static float m_fCurrentSpriteBrightness; + static int32 m_nCurrentShadowStrength; + static int32 m_nCurrentLightShadowStrength; + static int32 m_nCurrentPoleShadowStrength; + static float m_fCurrentFogStart; + static float m_fCurrentFarClip; + static float m_fCurrentLightsOnGroundBrightness; + static int32 m_nCurrentLowCloudsRed; + static int32 m_nCurrentLowCloudsGreen; + static int32 m_nCurrentLowCloudsBlue; + static int32 m_nCurrentFluffyCloudsTopRed; + static int32 m_nCurrentFluffyCloudsTopGreen; + static int32 m_nCurrentFluffyCloudsTopBlue; + static int32 m_nCurrentFluffyCloudsBottomRed; + static int32 m_nCurrentFluffyCloudsBottomGreen; + static int32 m_nCurrentFluffyCloudsBottomBlue; + static float m_fCurrentBlurRed; + static float m_fCurrentBlurGreen; + static float m_fCurrentBlurBlue; + static float m_fCurrentWaterRed; + static float m_fCurrentWaterGreen; + static float m_fCurrentWaterBlue; + static float m_fCurrentWaterAlpha; + static int32 m_nCurrentFogColourRed; + static int32 m_nCurrentFogColourGreen; + static int32 m_nCurrentFogColourBlue; + + static int32 m_FogReduction; + +public: + static int32 m_bExtraColourOn; + static int32 m_ExtraColour; + static float m_ExtraColourInter; + static int32 m_CurrentStoredValue; + static CVector m_VectorToSun[16]; + static float m_fShadowFrontX[16]; + static float m_fShadowFrontY[16]; + static float m_fShadowSideX[16]; + static float m_fShadowSideY[16]; + static float m_fShadowDisplacementX[16]; + static float m_fShadowDisplacementY[16]; + + static float GetAmbientRed(void) { return m_fCurrentAmbientRed; } + static float GetAmbientGreen(void) { return m_fCurrentAmbientGreen; } + static float GetAmbientBlue(void) { return m_fCurrentAmbientBlue; } + static float GetAmbientRed_Obj(void) { return m_fCurrentAmbientRed_Obj; } + static float GetAmbientGreen_Obj(void) { return m_fCurrentAmbientGreen_Obj; } + static float GetAmbientBlue_Obj(void) { return m_fCurrentAmbientBlue_Obj; } + static float GetAmbientRed_Bl(void) { return m_fCurrentAmbientRed_Bl; } + static float GetAmbientGreen_Bl(void) { return m_fCurrentAmbientGreen_Bl; } + static float GetAmbientBlue_Bl(void) { return m_fCurrentAmbientBlue_Bl; } + static float GetAmbientRed_Obj_Bl(void) { return m_fCurrentAmbientRed_Obj_Bl; } + static float GetAmbientGreen_Obj_Bl(void) { return m_fCurrentAmbientGreen_Obj_Bl; } + static float GetAmbientBlue_Obj_Bl(void) { return m_fCurrentAmbientBlue_Obj_Bl; } + static float GetDirectionalRed(void) { return m_fCurrentDirectionalRed; } + static float GetDirectionalGreen(void) { return m_fCurrentDirectionalGreen; } + static float GetDirectionalBlue(void) { return m_fCurrentDirectionalBlue; } + static int32 GetSkyTopRed(void) { return m_nCurrentSkyTopRed; } + static int32 GetSkyTopGreen(void) { return m_nCurrentSkyTopGreen; } + static int32 GetSkyTopBlue(void) { return m_nCurrentSkyTopBlue; } + static int32 GetSkyBottomRed(void) { return m_nCurrentSkyBottomRed; } + static int32 GetSkyBottomGreen(void) { return m_nCurrentSkyBottomGreen; } + static int32 GetSkyBottomBlue(void) { return m_nCurrentSkyBottomBlue; } + static int32 GetSunCoreRed(void) { return m_nCurrentSunCoreRed; } + static int32 GetSunCoreGreen(void) { return m_nCurrentSunCoreGreen; } + static int32 GetSunCoreBlue(void) { return m_nCurrentSunCoreBlue; } + static int32 GetSunCoronaRed(void) { return m_nCurrentSunCoronaRed; } + static int32 GetSunCoronaGreen(void) { return m_nCurrentSunCoronaGreen; } + static int32 GetSunCoronaBlue(void) { return m_nCurrentSunCoronaBlue; } + static float GetSunSize(void) { return m_fCurrentSunSize; } + static float GetSpriteBrightness(void) { return m_fCurrentSpriteBrightness; } + static float GetSpriteSize(void) { return m_fCurrentSpriteSize; } + static int32 GetShadowStrength(void) { return m_nCurrentShadowStrength; } + static int32 GetLightShadowStrength(void) { return m_nCurrentLightShadowStrength; } + static float GetLightOnGroundBrightness(void) { return m_fCurrentLightsOnGroundBrightness; } + static float GetFarClip(void) { return m_fCurrentFarClip; } + static float GetFogStart(void) { return m_fCurrentFogStart; } + + static int32 GetLowCloudsRed(void) { return m_nCurrentLowCloudsRed; } + static int32 GetLowCloudsGreen(void) { return m_nCurrentLowCloudsGreen; } + static int32 GetLowCloudsBlue(void) { return m_nCurrentLowCloudsBlue; } + static int32 GetFluffyCloudsTopRed(void) { return m_nCurrentFluffyCloudsTopRed; } + static int32 GetFluffyCloudsTopGreen(void) { return m_nCurrentFluffyCloudsTopGreen; } + static int32 GetFluffyCloudsTopBlue(void) { return m_nCurrentFluffyCloudsTopBlue; } + static int32 GetFluffyCloudsBottomRed(void) { return m_nCurrentFluffyCloudsBottomRed; } + static int32 GetFluffyCloudsBottomGreen(void) { return m_nCurrentFluffyCloudsBottomGreen; } + static int32 GetFluffyCloudsBottomBlue(void) { return m_nCurrentFluffyCloudsBottomBlue; } + static int32 GetFogRed(void) { return m_nCurrentFogColourRed; } + static int32 GetFogGreen(void) { return m_nCurrentFogColourGreen; } + static int32 GetFogBlue(void) { return m_nCurrentFogColourBlue; } + static int32 GetFogReduction(void) { return m_FogReduction; } + + static int32 GetBlurRed(void) { return m_fCurrentBlurRed; } + static int32 GetBlurGreen(void) { return m_fCurrentBlurGreen; } + static int32 GetBlurBlue(void) { return m_fCurrentBlurBlue; } + static int32 GetWaterRed(void) { return m_fCurrentWaterRed; } + static int32 GetWaterGreen(void) { return m_fCurrentWaterGreen; } + static int32 GetWaterBlue(void) { return m_fCurrentWaterBlue; } + static int32 GetWaterAlpha(void) { return m_fCurrentWaterAlpha; } + + static void Initialise(void); + static void Update(void); + static float Interpolate(int8 *a, int8 *b); + static float Interpolate(uint8 *a, uint8 *b); + static float Interpolate(int16 *a, int16 *b); + static void StartExtraColour(int32 c, bool fade); + static void StopExtraColour(bool fade); + static CVector &GetSunDirection(void) { return m_VectorToSun[m_CurrentStoredValue]; } + static float GetShadowFrontX(void) { return m_fShadowFrontX[m_CurrentStoredValue]; } + static float GetShadowFrontY(void) { return m_fShadowFrontY[m_CurrentStoredValue]; } + static float GetShadowSideX(void) { return m_fShadowSideX[m_CurrentStoredValue]; } + static float GetShadowSideY(void) { return m_fShadowSideY[m_CurrentStoredValue]; } + static float GetShadowDisplacementX(void) { return m_fShadowDisplacementX[m_CurrentStoredValue]; } + static float GetShadowDisplacementY(void) { return m_fShadowDisplacementY[m_CurrentStoredValue]; } +}; diff --git a/src/miami/renderer/VarConsole.cpp b/src/miami/renderer/VarConsole.cpp new file mode 100644 index 00000000..372a091a --- /dev/null +++ b/src/miami/renderer/VarConsole.cpp @@ -0,0 +1,786 @@ +#include "common.h" +#include "VarConsole.h" +#include "Font.h" +#include "Pad.h" + +#define VAR_CONSOLE_PAD 1 + +CVarConsole VarConsole; + +void +CVarConsole::Initialise() +{ + m_nCountEntries = 0; + m_nCurPage = 1; + m_bIsOpen = false; + m_nCurEntry = 0; + m_nFirstEntryOnPage = 0; +} + +void +CVarConsole::Add(char *text, int8 *pVal, uint8 step, int8 min, int8 max, bool8 isVar) +{ + int i; + for (i = 0; i < m_nCountEntries; i++) { + if (m_aEntries[i].text == text) + return; + } + + m_aEntries[i].text = text; + m_aEntries[i].pInt8Value = pVal; + m_aEntries[i].bAllowExceedBounds = isVar; + m_aEntries[i].VarType = VCE_TYPE_INT8; + m_aEntries[i].I8_step = step; + m_aEntries[i].I8_min = min; + m_aEntries[i].I8_max = max; + m_nCountEntries++; +} + +void +CVarConsole::Add(char *text, int16 *pVal, uint16 step, int16 min, int16 max, bool8 isVar) +{ + int i; + for (i = 0; i < m_nCountEntries; i++) { + if (m_aEntries[i].text == text) + return; + } + + m_aEntries[i].text = text; + m_aEntries[i].pInt16Value = pVal; + m_aEntries[i].bAllowExceedBounds = isVar; + m_aEntries[i].VarType = VCE_TYPE_INT16; + m_aEntries[i].I16_step = step; + m_aEntries[i].I16_min = min; + m_aEntries[i].I16_max = max; + m_nCountEntries++; +} + +void +CVarConsole::Add(char *text, int32 *pVal, uint32 step, int32 min, int32 max, bool8 isVar) +{ + int i; + for (i = 0; i < m_nCountEntries; i++) { + if (m_aEntries[i].text == text) + return; + } + + m_aEntries[i].text = text; + m_aEntries[i].pInt32Value = pVal; + m_aEntries[i].bAllowExceedBounds = isVar; + m_aEntries[i].VarType = VCE_TYPE_INT32; + m_aEntries[i].I32_step = step; + m_aEntries[i].I32_min = min; + m_aEntries[i].I32_max = max; + m_nCountEntries++; +} + +void +CVarConsole::Add(char *text, int64 *pVal, uint64 step, int64 min, int64 max, bool8 isVar) +{ + int i; + for (i = 0; i < m_nCountEntries; i++) { + if (m_aEntries[i].text == text) + return; + } + + m_aEntries[i].text = text; + m_aEntries[i].pInt64Value = pVal; + m_aEntries[i].bAllowExceedBounds = isVar; + m_aEntries[i].VarType = VCE_TYPE_INT64; + m_aEntries[i].I64_step = step; + m_aEntries[i].I64_min = min; + m_aEntries[i].I64_max = max; + m_nCountEntries++; +} + +void +CVarConsole::Add(char *text, uint8 *pVal, uint8 step, int8 min, int8 max, bool8 isVar) +{ + int i; + for (i = 0; i < m_nCountEntries; i++) { + if (m_aEntries[i].text == text) + return; + } + + m_aEntries[i].text = text; + m_aEntries[i].pUint8Value = pVal; + m_aEntries[i].bAllowExceedBounds = isVar; + m_aEntries[i].VarType = VCE_TYPE_UINT8; + m_aEntries[i].I8_step = step; + m_aEntries[i].I8_min = min; + m_aEntries[i].I8_max = max; + m_nCountEntries++; +} + +void +CVarConsole::Add(char *text, uint16 *pVal, uint16 step, int16 min, int16 max, bool8 isVar) +{ + int i; + for (i = 0; i < m_nCountEntries; i++) { + if (m_aEntries[i].text == text) + return; + } + + m_aEntries[i].text = text; + m_aEntries[i].pUint16Value = pVal; + m_aEntries[i].bAllowExceedBounds = isVar; + m_aEntries[i].VarType = VCE_TYPE_UINT16; + m_aEntries[i].I16_step = step; + m_aEntries[i].I16_min = min; + m_aEntries[i].I16_max = max; + m_nCountEntries++; +} + +void +CVarConsole::Add(char *text, uint32 *pVal, uint32 step, int32 min, int32 max, bool8 isVar) +{ + int i; + for (i = 0; i < m_nCountEntries; i++) { + if (m_aEntries[i].text == text) + return; + } + + m_aEntries[i].text = text; + m_aEntries[i].pUint32Value = pVal; + m_aEntries[i].bAllowExceedBounds = isVar; + m_aEntries[i].VarType = VCE_TYPE_UINT32; + m_aEntries[i].I32_step = step; + m_aEntries[i].I32_min = min; + m_aEntries[i].I32_max = max; + m_nCountEntries++; +} + +void +CVarConsole::Add(char *text, uint64 *pVal, uint64 step, int64 min, int64 max, bool8 isVar) +{ + int i; + for (i = 0; i < m_nCountEntries; i++) { + if (m_aEntries[i].text == text) + return; + } + + m_aEntries[i].text = text; + m_aEntries[i].pUint64Value = pVal; + m_aEntries[i].bAllowExceedBounds = isVar; + m_aEntries[i].VarType = VCE_TYPE_UINT64; + m_aEntries[i].I64_step = step; + m_aEntries[i].I64_min = min; + m_aEntries[i].I64_max = max; + m_nCountEntries++; +} + +void +CVarConsole::Add(char *text, float *pVal, float step, float min, float max, bool8 isVar) +{ + int i; + for (i = 0; i < m_nCountEntries; i++) { + if (m_aEntries[i].text == text) + return; + } + + m_aEntries[i].text = text; + m_aEntries[i].pFloatValue = pVal; + m_aEntries[i].bAllowExceedBounds = isVar; + m_aEntries[i].VarType = VCE_TYPE_FLOAT; + m_aEntries[i].F_step = step; + m_aEntries[i].F_min = min; + m_aEntries[i].F_max = max; + m_nCountEntries++; +} + +void +CVarConsole::Add(char *text, bool *pVal, bool8 isVar) +{ + int i; + for (i = 0; i < m_nCountEntries; i++) { + if (m_aEntries[i].text == text) + return; + } + + m_aEntries[i].text = text; + m_aEntries[i].pBoolValue = pVal; + m_aEntries[i].bAllowExceedBounds = isVar; + m_aEntries[i].VarType = VCE_TYPE_BOOL; + m_nCountEntries++; +} + +void +CVarConsole::Add(char *text, bool8 *pVal, bool8 isVar) +{ + int i; + for (i = 0; i < m_nCountEntries; i++) { + if (m_aEntries[i].text == text) + return; + } + + m_aEntries[i].text = text; + m_aEntries[i].pUint8Value = pVal; + m_aEntries[i].bAllowExceedBounds = isVar; + m_aEntries[i].VarType = VCE_TYPE_BOOL8; + m_nCountEntries++; +} + +void +CVarConsole::Add(char *text, bool16 *pVal, bool8 isVar) +{ + int i; + for (i = 0; i < m_nCountEntries; i++) { + if (m_aEntries[i].text == text) + return; + } + + m_aEntries[i].text = text; + m_aEntries[i].pUint16Value = pVal; + m_aEntries[i].bAllowExceedBounds = isVar; + m_aEntries[i].VarType = VCE_TYPE_BOOL16; + m_nCountEntries++; +} + +void +CVarConsole::Add(char *text, bool32 *pVal, bool8 isVar) +{ + int i; + for (i = 0; i < m_nCountEntries; i++) { + if (m_aEntries[i].text == text) + return; + } + + m_aEntries[i].text = text; + m_aEntries[i].pUint32Value = pVal; + m_aEntries[i].bAllowExceedBounds = isVar; + m_aEntries[i].VarType = VCE_TYPE_BOOL32; + m_nCountEntries++; +} + +void +CVarConsole::Add(char *text, void (*pCallback)(void)) +{ + int i; + for (i = 0; i < m_nCountEntries; i++) { + if (m_aEntries[i].text == text) + return; + } + + m_aEntries[i].text = text; + m_aEntries[i].pCallback = pCallback; + m_aEntries[i].VarType = VCE_TYPE_FUNCTION; + m_nCountEntries++; +} + +void +CVarConsole::Remove(char *text) +{ + int i; + for (i = 0; i < m_nCountEntries; i++) { + if (m_aEntries[i].text == text) + { + for (int j = i; j < m_nCountEntries-1; j++) + m_aEntries[j] = m_aEntries[j+1]; + m_nCountEntries--; + return; + } + } +} + +void +CVarConsole::SortPages() +{ + m_nNumPages = m_nCountEntries / 30 + 1; +} + +void +CVarConsole::Display() +{ + char s[256]; + wchar ws[256]; + + CFont::SetColor(CRGBA(200, 200, 200, 255)); + CFont::SetFontStyle(FONT_STANDARD); + CFont::SetScale(SCREEN_SCALE_X(0.5f), SCREEN_SCALE_Y(0.6f)); + CFont::SetDropShadowPosition(2); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + CFont::SetPropOn(); + CFont::SetWrapx(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH)); + CFont::SetRightJustifyWrap(0.0f); + sprintf(s, "PAGE %d", m_nCurPage); + AsciiToUnicode(s, ws); + CFont::SetRightJustifyOn(); + CFont::PrintString(SCREEN_SCALE_X(310.0f), SCREEN_SCALE_Y(30.0f), ws); + CFont::SetRightJustifyOff(); + int y = 45; + for (int i = m_nFirstEntryOnPage; i < m_nCountEntries && i < m_nFirstEntryOnPage + 30; i++) + { + switch (m_aEntries[i].VarType) + { + case VCE_TYPE_INT8: + sprintf(s, "(%d) %s:I8:%d", i + 1, m_aEntries[i].text, *m_aEntries[i].pInt8Value); + break; + case VCE_TYPE_INT16: + sprintf(s, "(%d) %s:I16:%d", i + 1, m_aEntries[i].text, *m_aEntries[i].pInt16Value); + break; + case VCE_TYPE_INT32: + sprintf(s, "(%d) %s:I32:%d", i + 1, m_aEntries[i].text, *m_aEntries[i].pInt32Value); + break; + case VCE_TYPE_INT64: +#ifdef FIX_BUGS + sprintf(s, "(%d) %s:I64:%lld", i + 1, m_aEntries[i].text, *m_aEntries[i].pInt64Value); +#else + sprintf(s, "(%d) %s:I64:%d", i + 1, m_aEntries[i].text, (int32)*m_aEntries[i].pInt64Value); +#endif + break; + case VCE_TYPE_UINT8: + sprintf(s, "(%d) %s:U8:%d", i + 1, m_aEntries[i].text, *m_aEntries[i].pUint8Value); + break; + case VCE_TYPE_UINT16: + sprintf(s, "(%d) %s:U6:%d", i + 1, m_aEntries[i].text, *m_aEntries[i].pUint16Value); + break; + case VCE_TYPE_UINT32: + sprintf(s, "(%d) %s:U32:%d", i + 1, m_aEntries[i].text, *m_aEntries[i].pUint32Value); + break; + case VCE_TYPE_UINT64: +#ifdef FIX_BUGS + sprintf(s, "(%d) %s:U64:%llu", i + 1, m_aEntries[i].text, *m_aEntries[i].pUint64Value); +#else + sprintf(s, "(%d) %s:U64:%d", i + 1, m_aEntries[i].text, (uint32)*m_aEntries[i].pUint64Value); +#endif + break; + case VCE_TYPE_FLOAT: + sprintf(s, "(%d) %s:F:%f", i + 1, m_aEntries[i].text, *m_aEntries[i].pFloatValue); + break; + case VCE_TYPE_BOOL: + if (*m_aEntries[i].pBoolValue) + sprintf(s, "(%d) %s:B:TRUE", i + 1, m_aEntries[i].text); + else + sprintf(s, "(%d) %s:B : FALSE", i + 1, m_aEntries[i].text); + break; + case VCE_TYPE_BOOL8: + if (*m_aEntries[i].pUint8Value == FALSE) + sprintf(s, "(%d) %s:B8:FALSE", i + 1, m_aEntries[i].text); + else + sprintf(s, "(%d) %s:B8:TRUE", i + 1, m_aEntries[i].text); + break; + case VCE_TYPE_BOOL16: + if (*m_aEntries[i].pUint16Value == FALSE) + sprintf(s, "(%d) %s:B16:FALSE", i + 1, m_aEntries[i].text); + else + sprintf(s, "(%d) %s:B16:TRUE", i + 1, m_aEntries[i].text); + break; + case VCE_TYPE_BOOL32: + if (*m_aEntries[i].pUint32Value == FALSE) + sprintf(s, "(%d) %s:B32:FALSE", i + 1, m_aEntries[i].text); + else + sprintf(s, "(%d) %s:B32:TRUE", i + 1, m_aEntries[i].text); + break; + case VCE_TYPE_FUNCTION: + sprintf(s, "(%d) %s:FUNCTION:call this function?", i + 1, m_aEntries[i].text); + break; + } + AsciiToUnicode(s, ws); + if (m_nCurEntry == i) { + CFont::SetBackgroundOn(); +#ifdef FIX_BUGS + CFont::SetBackgroundColor(CRGBA(128, 128, 128, 128)); +#endif + } +#ifdef FIX_BUGS + else + CFont::SetBackgroundColor(CRGBA(128, 128, 128, 0)); +#endif + + CFont::SetColor(CRGBA(200, 200, 200, 255)); + CFont::PrintString(SCREEN_SCALE_X(30.0f), SCREEN_SCALE_Y(y), ws); + if (m_nCurEntry == i) + CFont::SetBackgroundOff(); + y += 12; + } +} + +void +CVarConsole::ModifyLeft() +{ + CVarConsoleEntry &entry = m_aEntries[m_nCurEntry]; + switch (entry.VarType) + { + case VCE_TYPE_INT8: + *entry.pInt8Value -= entry.I8_step; + if (entry.bAllowExceedBounds) { + if (*entry.pInt8Value < entry.I8_min) + *entry.pInt8Value = entry.I8_max; + } else { + if (*entry.pInt8Value < entry.I8_min) + *entry.pInt8Value = entry.I8_min; + } + break; + case VCE_TYPE_INT16: + *entry.pInt16Value -= entry.I16_step; + if (entry.bAllowExceedBounds) { + if (*entry.pInt16Value < entry.I16_min) + *entry.pInt16Value = entry.I16_max; + } + else { + if (*entry.pInt16Value < entry.I16_min) + *entry.pInt16Value = entry.I16_min; + } + break; + case VCE_TYPE_INT32: + *entry.pInt32Value -= entry.I32_step; + if (entry.bAllowExceedBounds) { + if (*entry.pInt32Value < entry.I32_min) + *entry.pInt32Value = entry.I32_max; + } + else { + if (*entry.pInt32Value < entry.I32_min) + *entry.pInt32Value = entry.I32_min; + } + break; + case VCE_TYPE_INT64: + *entry.pInt64Value -= entry.I64_step; + if (entry.bAllowExceedBounds) { + if (*entry.pInt64Value < entry.I64_min) + *entry.pInt64Value = entry.I64_max; + } + else { + if (*entry.pInt64Value < entry.I64_min) + *entry.pInt64Value = entry.I64_min; + } + break; + case VCE_TYPE_UINT8: + *entry.pUint8Value -= entry.I8_step; + if (entry.bAllowExceedBounds) { + if (*(int8*)entry.pUint8Value < entry.I8_min) + *entry.pUint8Value = entry.I8_max; + } + else { + if (*(int8*)entry.pUint8Value < entry.I8_min) + *entry.pUint8Value = entry.I8_min; + } + break; + case VCE_TYPE_UINT16: + *entry.pUint16Value -= entry.I16_step; + if (entry.bAllowExceedBounds) { + if (*(int16*)entry.pUint16Value < entry.I16_min) + *entry.pUint16Value = entry.I16_max; + } + else { + if (*(int16*)entry.pUint16Value < entry.I16_min) + *entry.pUint16Value = entry.I16_min; + } + break; + case VCE_TYPE_UINT32: + *entry.pUint32Value -= entry.I32_step; + if (entry.bAllowExceedBounds) { + if (*(int32*)entry.pUint32Value < entry.I32_min) + *entry.pUint32Value = entry.I32_max; + } + else { + if (*(int32*)entry.pUint32Value < entry.I32_min) + *entry.pUint32Value = entry.I32_min; + } + break; + case VCE_TYPE_UINT64: + *entry.pUint64Value -= entry.I64_step; + if (entry.bAllowExceedBounds) { + if (*(int64*)entry.pUint64Value < entry.I64_min) + *entry.pUint64Value = entry.I64_max; + } + else { + if (*(int64*)entry.pUint64Value < entry.I64_min) + *entry.pUint64Value = entry.I64_min; + } + break; + case VCE_TYPE_FLOAT: + *entry.pFloatValue -= entry.F_step; + if (entry.bAllowExceedBounds) { + if (*entry.pFloatValue < entry.F_min) + *entry.pFloatValue = entry.F_max; + } + else { + if (*entry.pFloatValue < entry.F_min) + *entry.pFloatValue = entry.F_min; + } + break; + case VCE_TYPE_BOOL: + if (entry.bAllowExceedBounds) + *entry.pBoolValue ^= true; + else + *entry.pBoolValue = false; + break; + case VCE_TYPE_BOOL8: + if (entry.bAllowExceedBounds) + *entry.pUint8Value = *entry.pUint8Value == false; + else + *entry.pUint8Value = false; + break; + case VCE_TYPE_BOOL16: + if (entry.bAllowExceedBounds) + *entry.pUint16Value = *entry.pUint16Value == false; + else + *entry.pUint16Value = false; + break; + case VCE_TYPE_BOOL32: + if (entry.bAllowExceedBounds) + *entry.pUint32Value = *entry.pUint32Value == false; + else + *entry.pUint32Value = false; + break; + case VCE_TYPE_FUNCTION: + entry.pCallback(); + break; + default: + return; + } +} + +void +CVarConsole::ModifyRight() +{ + CVarConsoleEntry &entry = m_aEntries[m_nCurEntry]; + switch (entry.VarType) + { + case VCE_TYPE_INT8: + *entry.pInt8Value += entry.I8_step; + if (entry.bAllowExceedBounds) { + if (*entry.pInt8Value > entry.I8_max) + *entry.pInt8Value = entry.I8_min; + } + else { + if (*entry.pInt8Value > entry.I8_max) + *entry.pInt8Value = entry.I8_max; + } + break; + case VCE_TYPE_INT16: + *entry.pInt16Value += entry.I16_step; + if (entry.bAllowExceedBounds) { + if (*entry.pInt16Value > entry.I16_max) + *entry.pInt16Value = entry.I16_min; + } + else { + if (*entry.pInt16Value > entry.I16_max) + *entry.pInt16Value = entry.I16_max; + } + break; + case VCE_TYPE_INT32: + *entry.pInt32Value += entry.I32_step; + if (entry.bAllowExceedBounds) { + if (*entry.pInt32Value > entry.I32_max) + *entry.pInt32Value = entry.I32_min; + } + else { + if (*entry.pInt32Value > entry.I32_max) + *entry.pInt32Value = entry.I32_max; + } + break; + case VCE_TYPE_INT64: + *entry.pInt64Value += entry.I64_step; + if (entry.bAllowExceedBounds) { + if (*entry.pInt64Value > entry.I64_max) + *entry.pInt64Value = entry.I64_min; + } + else { + if (*entry.pInt64Value > entry.I64_max) + *entry.pInt64Value = entry.I64_max; + } + break; + case VCE_TYPE_UINT8: + *entry.pUint8Value += entry.I8_step; + if (entry.bAllowExceedBounds) { + if (*entry.pUint8Value > (uint8)entry.I8_max) + *entry.pUint8Value = entry.I8_min; + } + else { + if (*entry.pUint8Value > (uint8)entry.I8_max) + *entry.pUint8Value = entry.I8_max; + } + break; + case VCE_TYPE_UINT16: + *entry.pUint16Value += entry.I16_step; + if (entry.bAllowExceedBounds) { + if (*entry.pUint16Value > (uint16)entry.I16_max) + *entry.pUint16Value = entry.I16_min; + } + else { + if (*entry.pUint16Value > (uint16)entry.I16_max) + *entry.pUint16Value = entry.I16_max; + } + break; + case VCE_TYPE_UINT32: + *entry.pUint32Value += entry.I32_step; + if (entry.bAllowExceedBounds) { + if (*entry.pUint32Value > (uint32)entry.I32_max) + *entry.pUint32Value = entry.I32_min; + } + else { + if (*entry.pUint32Value > (uint32)entry.I32_max) + *entry.pUint32Value = entry.I32_max; + } + break; + case VCE_TYPE_UINT64: + *entry.pUint64Value += entry.I64_step; + if (entry.bAllowExceedBounds) { + if (*entry.pUint64Value > (uint64)entry.I64_max) + *entry.pUint64Value = entry.I64_min; + } + else { + if (*entry.pUint64Value > (uint64)entry.I64_max) + *entry.pUint64Value = entry.I64_max; + } + break; + case VCE_TYPE_FLOAT: + *entry.pFloatValue += entry.F_step; + if (entry.bAllowExceedBounds) { + if (*entry.pFloatValue > entry.F_max) + *entry.pFloatValue = entry.F_min; + } + else { + if (*entry.pFloatValue > entry.F_max) + *entry.pFloatValue = entry.F_max; + } + break; + case VCE_TYPE_BOOL: + if (entry.bAllowExceedBounds) + *entry.pBoolValue ^= true; + else + *entry.pBoolValue = true; + break; + case VCE_TYPE_BOOL8: + if (entry.bAllowExceedBounds) + *entry.pUint8Value = *entry.pUint8Value == false; + else + *entry.pUint8Value = true; + break; + case VCE_TYPE_BOOL16: + if (entry.bAllowExceedBounds) + *entry.pUint16Value = *entry.pUint16Value == false; + else + *entry.pUint16Value = true; + break; + case VCE_TYPE_BOOL32: + if (entry.bAllowExceedBounds) + *entry.pUint32Value = *entry.pUint32Value == false; + else + *entry.pUint32Value = true; + break; + case VCE_TYPE_FUNCTION: + entry.pCallback(); + break; + default: + return; + } +} + +void +CVarConsole::Enter() +{ + m_bIsOpen = true; +} + +void +CVarConsole::Exit() +{ + m_bIsOpen = false; +} + +void +CVarConsole::Input() +{ + if (CPad::GetPad(VAR_CONSOLE_PAD)->GetDPadDownJustDown() || CPad::GetPad(VAR_CONSOLE_PAD)->GetAnaloguePadDown()) + { + m_nCurEntry++; + if (m_nCurEntry < m_nCountEntries) + { + if (m_nCurEntry > m_nFirstEntryOnPage + 29) + { + m_nFirstEntryOnPage = m_nCurEntry; + ++m_nCurPage; + } + } + else + { + m_nCurEntry = m_nCountEntries - 1; + } + } + + if (CPad::GetPad(VAR_CONSOLE_PAD)->GetDPadUpJustDown() || CPad::GetPad(VAR_CONSOLE_PAD)->GetAnaloguePadUp()) + { + m_nCurEntry--; + if (m_nCurEntry < m_nFirstEntryOnPage) + { + m_nFirstEntryOnPage = m_nCurEntry - 29; + --m_nCurPage; + } + if (m_nFirstEntryOnPage < 0) + { + m_nCurEntry = 0; + m_nFirstEntryOnPage = 0; + m_nCurPage = 1; + } + } + if (CPad::GetPad(VAR_CONSOLE_PAD)->GetSquare()) + ModifyLeft(); + if (CPad::GetPad(VAR_CONSOLE_PAD)->GetTriangle()) + ModifyRight(); + + if (CPad::GetPad(VAR_CONSOLE_PAD)->GetDPadLeftJustDown() || CPad::GetPad(VAR_CONSOLE_PAD)->GetAnaloguePadLeft()) + ModifyLeft(); + + if (CPad::GetPad(VAR_CONSOLE_PAD)->GetDPadRightJustDown() || CPad::GetPad(VAR_CONSOLE_PAD)->GetAnaloguePadRight()) + ModifyRight(); + + if (CPad::GetPad(VAR_CONSOLE_PAD)->GetLeftShoulder2JustDown()) + { + if (m_nCurPage > 1) + { + m_nCurPage--; + m_nFirstEntryOnPage -= 30; + m_nCurEntry = m_nFirstEntryOnPage; + if (m_nFirstEntryOnPage < 0) + { + m_nFirstEntryOnPage = 0; + m_nCurEntry = m_nFirstEntryOnPage; + m_nCurPage = 1; + } + } + } + + if (CPad::GetPad(VAR_CONSOLE_PAD)->GetRightShoulder2JustDown()) + { + if (m_nCurPage < m_nNumPages) + { + m_nCurPage++; + m_nFirstEntryOnPage += 30; + m_nCurEntry = m_nFirstEntryOnPage; + if (m_nFirstEntryOnPage >= m_nCountEntries) + { + m_nFirstEntryOnPage -= 30; + m_nCurEntry = m_nFirstEntryOnPage; + m_nCurPage--; + } + } + } + + if (CPad::GetPad(VAR_CONSOLE_PAD)->GetRightShoulder1JustDown() && CPad::GetPad(VAR_CONSOLE_PAD)->GetLeftShoulder1JustDown()) + Exit(); +} + +void +CVarConsole::Process() +{ + Input(); + SortPages(); + Display(); +} + +bool8 +CVarConsole::Open() +{ + return m_bIsOpen; +} + +void +CVarConsole::Check() +{ + if (Open()) + Process(); + else if (CPad::GetPad(VAR_CONSOLE_PAD)->GetRightShoulder1JustDown() && CPad::GetPad(VAR_CONSOLE_PAD)->GetLeftShoulder1JustDown()) + Enter(); +} \ No newline at end of file diff --git a/src/miami/renderer/VarConsole.h b/src/miami/renderer/VarConsole.h new file mode 100644 index 00000000..5179a10d --- /dev/null +++ b/src/miami/renderer/VarConsole.h @@ -0,0 +1,92 @@ +#pragma once + +enum eVarConsoleEntryType +{ + VCE_TYPE_INT8, + VCE_TYPE_INT16, + VCE_TYPE_INT32, + VCE_TYPE_INT64, + VCE_TYPE_UINT8, + VCE_TYPE_UINT16, + VCE_TYPE_UINT32, + VCE_TYPE_UINT64, + VCE_TYPE_FLOAT, + VCE_TYPE_BOOL, + VCE_TYPE_BOOL8, + VCE_TYPE_BOOL16, + VCE_TYPE_BOOL32, + VCE_TYPE_FUNCTION, +}; + +struct CVarConsoleEntry +{ + char *text; + int8 *pInt8Value; + int16 *pInt16Value; + int32 *pInt32Value; + int64 *pInt64Value; + uint8 *pUint8Value; + uint16 *pUint16Value; + uint32 *pUint32Value; + uint64 *pUint64Value; + float *pFloatValue; + bool *pBoolValue; + void (*pCallback)(void); + int8 I8_step, I8_max, I8_min; + int16 I16_step, I16_max, I16_min; + int32 I32_step, I32_max, I32_min; + int64 I64_step, I64_max, I64_min; + float F_step, F_max, F_min; + bool8 bAllowExceedBounds; + uint8 VarType; +}; + + +class CVarConsole +{ + int32 m_nCountEntries; + bool8 m_bIsOpen; + int32 m_nCurEntry; + int32 m_nFirstEntryOnPage; + int32 m_nCurPage; + int32 m_nNumPages; + CVarConsoleEntry m_aEntries[91]; +public: +#ifdef FIX_BUGS + CVarConsole() { Initialise(); } +#endif + void Initialise(); + void Add(char *text, int8 *pVal, uint8 step, int8 min, int8 max, bool8 isVar); + void Add(char *text, int16 *pVal, uint16 step, int16 min, int16 max, bool8 isVar); + void Add(char *text, int32 *pVal, uint32 step, int32 min, int32 max, bool8 isVar); + void Add(char *text, int64 *pVal, uint64 step, int64 min, int64 max, bool8 isVar); + void Add(char *text, uint8 *pVal, uint8 step, int8 min, int8 max, bool8 isVar); + void Add(char *text, uint16 *pVal, uint16 step, int16 min, int16 max, bool8 isVar); + void Add(char *text, uint32 *pVal, uint32 step, int32 min, int32 max, bool8 isVar); + void Add(char *text, uint64 *pVal, uint64 step, int64 min, int64 max, bool8 isVar); + void Add(char *text, float *pVal, float step, float min, float max, bool8 isVar); + void Add(char *text, bool *pVal, bool8 isVar); + void Add(char *text, bool8 *pVal, bool8 isVar); + void Add(char *text, bool16 *pVal, bool8 isVar); + void Add(char *text, bool32 *pVal, bool8 isVar); + void Add(char *text, void (*pVar)(void)); + + void Remove(char *text); + + void SortPages(); + void Display(); + + void ModifyLeft(); + void ModifyRight(); + + void Enter(); + void Exit(); + + void Input(); + void Process(); + + bool8 Open(); + void Check(); +}; + +extern CVarConsole VarConsole; \ No newline at end of file diff --git a/src/miami/renderer/WaterCannon.cpp b/src/miami/renderer/WaterCannon.cpp new file mode 100644 index 00000000..4976f8a3 --- /dev/null +++ b/src/miami/renderer/WaterCannon.cpp @@ -0,0 +1,317 @@ +#include "common.h" + +#include "WaterCannon.h" +#include "Vector.h" +#include "General.h" +#include "main.h" +#include "Timer.h" +#include "Pools.h" +#include "Ped.h" +#include "AnimManager.h" +#include "Fire.h" +#include "WaterLevel.h" +#include "Camera.h" +#include "Particle.h" + +#define WATERCANNONVERTS 4 +#define WATERCANNONINDEXES 12 + +RwIm3DVertex WaterCannonVertices[WATERCANNONVERTS]; +RwImVertexIndex WaterCannonIndexList[WATERCANNONINDEXES]; + +CWaterCannon CWaterCannons::aCannons[NUM_WATERCANNONS]; + +void CWaterCannon::Init(void) +{ + m_nId = 0; + m_nCur = 0; + m_nTimeCreated = CTimer::GetTimeInMilliseconds(); + + for ( int32 i = 0; i < NUM_SEGMENTPOINTS; i++ ) + m_abUsed[i] = false; + + RwIm3DVertexSetU(&WaterCannonVertices[0], 0.0f); + RwIm3DVertexSetV(&WaterCannonVertices[0], 0.0f); + + RwIm3DVertexSetU(&WaterCannonVertices[1], 1.0f); + RwIm3DVertexSetV(&WaterCannonVertices[1], 0.0f); + + RwIm3DVertexSetU(&WaterCannonVertices[2], 0.0f); + RwIm3DVertexSetV(&WaterCannonVertices[2], 0.0f); + + RwIm3DVertexSetU(&WaterCannonVertices[3], 1.0f); + RwIm3DVertexSetV(&WaterCannonVertices[3], 0.0f); + + WaterCannonIndexList[0] = 0; + WaterCannonIndexList[1] = 1; + WaterCannonIndexList[2] = 2; + + WaterCannonIndexList[3] = 1; + WaterCannonIndexList[4] = 3; + WaterCannonIndexList[5] = 2; + + WaterCannonIndexList[6] = 0; + WaterCannonIndexList[7] = 2; + WaterCannonIndexList[8] = 1; + + WaterCannonIndexList[9] = 1; + WaterCannonIndexList[10] = 2; + WaterCannonIndexList[11] = 3; +} + +void CWaterCannon::Update_OncePerFrame(int16 index) +{ + ASSERT(index < NUM_WATERCANNONS); + + if (CTimer::GetTimeInMilliseconds() > m_nTimeCreated + WATERCANNON_LIFETIME ) + { + m_nCur = (m_nCur + 1) % NUM_SEGMENTPOINTS; + m_abUsed[m_nCur] = false; + } + + for ( int32 i = 0; i < NUM_SEGMENTPOINTS; i++ ) + { + if ( m_abUsed[i] ) + { + m_avecVelocity[i].z += -WATERCANNON_GRAVITY * CTimer::GetTimeStep(); + m_avecPos[i] += m_avecVelocity[i] * CTimer::GetTimeStep(); + } + } + + for ( int32 i = 0; i < NUM_SEGMENTPOINTS; i++ ) + { + if ( m_abUsed[i] && gFireManager.ExtinguishPointWithWater(m_avecPos[i], 4.0f) ) + { + break; + } + } + + if ( ((index + CTimer::GetFrameCounter()) & 3) == 0 ) + PushPeds(); + + // free if unused + + int32 i = 0; + while ( 1 ) + { + if ( m_abUsed[i] ) + break; + + if ( ++i >= NUM_SEGMENTPOINTS ) + { + m_nId = 0; + return; + } + } +} + +void CWaterCannon::Update_NewInput(CVector *pos, CVector *dir) +{ + ASSERT(pos != NULL); + ASSERT(dir != NULL); + + m_avecPos[m_nCur] = *pos; + m_avecVelocity[m_nCur] = *dir; + m_abUsed[m_nCur] = true; +} + +void CWaterCannon::Render(void) +{ + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)gpWaterRaster); + + float v = float(CGeneral::GetRandomNumber() & 255) / 256; + + RwIm3DVertexSetV(&WaterCannonVertices[0], v); + RwIm3DVertexSetV(&WaterCannonVertices[1], v); + RwIm3DVertexSetV(&WaterCannonVertices[2], v); + RwIm3DVertexSetV(&WaterCannonVertices[3], v); + + int16 pointA = m_nCur % NUM_SEGMENTPOINTS; + + int16 pointB = pointA - 1; + if ( pointB < 0 ) + pointB += NUM_SEGMENTPOINTS; + + bool bInit = false; + CVector norm; + + for ( int32 i = 0; i < NUM_SEGMENTPOINTS - 1; i++ ) + { + if ( m_abUsed[pointA] && m_abUsed[pointB] ) + { + if ( !bInit ) + { + CVector cp = CrossProduct(m_avecPos[pointB] - m_avecPos[pointA], TheCamera.GetForward()); + norm = cp * (0.05f / cp.Magnitude()); + bInit = true; + } + + float dist = float(i*i*i) / 300.0f + 1.0f; + float brightness = float(i) / NUM_SEGMENTPOINTS; + + int32 color = (int32)((1.0f - brightness*brightness) * 255.0f); + CVector offset = dist * norm; + + RwIm3DVertexSetRGBA(&WaterCannonVertices[0], color, color, color, color); + RwIm3DVertexSetPos (&WaterCannonVertices[0], m_avecPos[pointA].x - offset.x, m_avecPos[pointA].y - offset.y, m_avecPos[pointA].z - offset.z); + + RwIm3DVertexSetRGBA(&WaterCannonVertices[1], color, color, color, color); + RwIm3DVertexSetPos (&WaterCannonVertices[1], m_avecPos[pointA].x + offset.x, m_avecPos[pointA].y + offset.y, m_avecPos[pointA].z + offset.z); + + RwIm3DVertexSetRGBA(&WaterCannonVertices[2], color, color, color, color); + RwIm3DVertexSetPos (&WaterCannonVertices[2], m_avecPos[pointB].x - offset.x, m_avecPos[pointB].y - offset.y, m_avecPos[pointB].z - offset.z); + + RwIm3DVertexSetRGBA(&WaterCannonVertices[3], color, color, color, color); + RwIm3DVertexSetPos (&WaterCannonVertices[3], m_avecPos[pointB].x + offset.x, m_avecPos[pointB].y + offset.y, m_avecPos[pointB].z + offset.z); + + LittleTest(); + + if ( RwIm3DTransform(WaterCannonVertices, WATERCANNONVERTS, NULL, rwIM3D_VERTEXUV) ) + { + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, WaterCannonIndexList, WATERCANNONINDEXES); + RwIm3DEnd(); + } + } + + pointA = pointB--; + if ( pointB < 0 ) + pointB += NUM_SEGMENTPOINTS; + } + + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)FALSE); +} + +void CWaterCannon::PushPeds(void) +{ + float minx = 10000.0f; + float maxx = -10000.0f; + float miny = 10000.0f; + float maxy = -10000.0f; + float minz = 10000.0f; + float maxz = -10000.0f; + + for ( int32 i = 0; i < NUM_SEGMENTPOINTS; i++ ) + { + if ( m_abUsed[i] ) + { + minx = Min(minx, m_avecPos[i].x); + maxx = Max(maxx, m_avecPos[i].x); + + miny = Min(miny, m_avecPos[i].y); + maxy = Max(maxy, m_avecPos[i].y); + + minz = Min(minz, m_avecPos[i].z); + maxz = Max(maxz, m_avecPos[i].z); + } + } + + for ( int32 i = CPools::GetPedPool()->GetSize() - 1; i >= 0; i--) + { + CPed *ped = CPools::GetPedPool()->GetSlot(i); + if ( ped ) + { + if ( ped->GetPosition().x > minx && ped->GetPosition().x < maxx + && ped->GetPosition().y > miny && ped->GetPosition().y < maxy + && ped->GetPosition().z > minz && ped->GetPosition().z < maxz ) + { + for ( int32 j = 0; j < NUM_SEGMENTPOINTS; j++ ) + { + if ( m_abUsed[j] ) + { + CVector dist = m_avecPos[j] - ped->GetPosition(); + + if ( dist.MagnitudeSqr() < 5.0f ) + { + int32 localDir = ped->GetLocalDirection(CVector2D(1.0f, 0.0f)); + + ped->bIsStanding = false; + + ped->ApplyMoveForce(0.0f, 0.0f, 2.0f * CTimer::GetTimeStep()); + + ped->m_vecMoveSpeed.x = (0.6f * m_avecVelocity[j].x + ped->m_vecMoveSpeed.x) * 0.5f; + ped->m_vecMoveSpeed.y = (0.6f * m_avecVelocity[j].y + ped->m_vecMoveSpeed.y) * 0.5f; + + float pedSpeed2D = ped->m_vecMoveSpeed.Magnitude2D(); + + if ( pedSpeed2D > 0.2f ) { + ped->m_vecMoveSpeed.x *= (0.2f / pedSpeed2D); + ped->m_vecMoveSpeed.y *= (0.2f / pedSpeed2D); + } + ped->SetFall(2000, (AnimationId)(localDir + ANIM_STD_HIGHIMPACT_FRONT), 0); + CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, ped->GetPosition(), ped->m_vecMoveSpeed * 0.3f, 0, 0.5f); + CParticle::AddParticle(PARTICLE_CAR_SPLASH, ped->GetPosition(), ped->m_vecMoveSpeed * -0.3f + CVector(0.f, 0.f, 0.5f), 0, 0.5f, + CGeneral::GetRandomNumberInRange(0.f, 10.f), CGeneral::GetRandomNumberInRange(0.f, 90.f), 1); + + j = NUM_SEGMENTPOINTS; + } + } + } + } + } + } +} + +void CWaterCannons::Init(void) +{ + for ( int32 i = 0; i < NUM_WATERCANNONS; i++ ) + aCannons[i].Init(); +} + +void CWaterCannons::UpdateOne(uint32 id, CVector *pos, CVector *dir) +{ + ASSERT(pos != NULL); + ASSERT(dir != NULL); + + // find the one by id + { + int32 n = 0; + while ( n < NUM_WATERCANNONS && id != aCannons[n].m_nId ) + n++; + + if ( n < NUM_WATERCANNONS ) + { + aCannons[n].Update_NewInput(pos, dir); + return; + } + } + + // if no luck then find a free one + { + int32 n = 0; + while ( n < NUM_WATERCANNONS && 0 != aCannons[n].m_nId ) + n++; + + if ( n < NUM_WATERCANNONS ) + { + aCannons[n].Init(); + aCannons[n].m_nId = id; + aCannons[n].Update_NewInput(pos, dir); + return; + } + } +} + +void CWaterCannons::Update(void) +{ + for ( int32 i = 0; i < NUM_WATERCANNONS; i++ ) + { + if ( aCannons[i].m_nId != 0 ) + aCannons[i].Update_OncePerFrame(i); + } +} + +void CWaterCannons::Render(void) +{ + PUSH_RENDERGROUP("CWaterCannons::Render"); + for ( int32 i = 0; i < NUM_WATERCANNONS; i++ ) + { + if ( aCannons[i].m_nId != 0 ) + aCannons[i].Render(); + } + POP_RENDERGROUP(); +} diff --git a/src/miami/renderer/WaterCannon.h b/src/miami/renderer/WaterCannon.h new file mode 100644 index 00000000..a37bdd12 --- /dev/null +++ b/src/miami/renderer/WaterCannon.h @@ -0,0 +1,39 @@ +#pragma once + +#define WATERCANNON_GRAVITY (0.009f) +#define WATERCANNON_LIFETIME (150) + +class CWaterCannon +{ +public: + enum + { + NUM_SEGMENTPOINTS = 16, + }; + + int32 m_nId; + int16 m_nCur; + uint32 m_nTimeCreated; + CVector m_avecPos[NUM_SEGMENTPOINTS]; + CVector m_avecVelocity[NUM_SEGMENTPOINTS]; + bool m_abUsed[NUM_SEGMENTPOINTS]; + + void Init(void); + void Update_OncePerFrame(int16 index); + void Update_NewInput(CVector *pos, CVector *dir); + void Render(void); + void PushPeds(void); +}; + +VALIDATE_SIZE(CWaterCannon, 412); + +class CWaterCannons +{ +public: + static CWaterCannon aCannons[NUM_WATERCANNONS]; + + static void Init(void); + static void UpdateOne(uint32 id, CVector *pos, CVector *dir); + static void Update(); + static void Render(void); +}; \ No newline at end of file diff --git a/src/miami/renderer/WaterCreatures.cpp b/src/miami/renderer/WaterCreatures.cpp new file mode 100644 index 00000000..9d591787 --- /dev/null +++ b/src/miami/renderer/WaterCreatures.cpp @@ -0,0 +1,275 @@ +#include "common.h" +#include "WaterCreatures.h" +#include "ModelIndices.h" +#include "World.h" +#include "WaterLevel.h" +#include "Camera.h" +#include "PlayerPed.h" +#include "General.h" +#include "Object.h" + +int32 CWaterCreatures::nNumActiveSeaLifeForms; +CWaterCreature CWaterCreatures::aWaterCreatures[NUM_WATER_CREATURES]; + +struct WaterCreatureProperties aProperties[65] = { + { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_JELLYFISH, 0.01f, 2.2f, 0.0005f, 3.5f }, + { &MI_JELLYFISH01, 0.01f, 2.2f, 0.0005f, 3.5f }, + { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_TURTLE, 0.01f, 2.0f, 0.0005f, 4.0f }, + { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_DOLPHIN, 0.03f, 1.5f, 0.0005f, 4.0f }, + { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_SHARK, 0.03f, 0.4f, 0.0005f, 4.0f }, + { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, + { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, + { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, +}; + +CWaterCreature::CWaterCreature() { + Free(); +} + +void CWaterCreature::Initialise(CObject *pObj, float fFwdSpeed, float fZTurnSpeed, float fWaterDepth, uint32 alpha, eFishSlotState state) { + this->m_pObj = pObj; + this->m_fFwdSpeed = fFwdSpeed; + this->m_fZTurnSpeed = fZTurnSpeed; + this->m_fWaterDepth = fWaterDepth; + this->m_alpha = alpha; + this->m_state = state; +} + +void CWaterCreature::Allocate(CObject *pObj, float fFwdSpeed, float fZTurnSpeed, float fWaterDepth, uint32 alpha, eFishSlotState state) { + CWaterCreature::Initialise(pObj, fFwdSpeed, fZTurnSpeed, fWaterDepth, alpha, state); +} + +void CWaterCreature::Free() { + CWaterCreature::Initialise(nil, 0.0f, 0.0f, 0.0f, 0, WATER_CREATURE_DISABLED); +} + +CWaterCreature *CWaterCreatures::GetFishStructSlot() { + for (int i = 0; i < NUM_WATER_CREATURES; i++) + if (aWaterCreatures[i].m_state == WATER_CREATURE_DISABLED) + return &aWaterCreatures[i]; + + return nil; +} + +CObject *CWaterCreatures::CreateSeaLifeForm(CVector const& pos, int16 modelID, int32 zRotAngle) { + if (CObject::nNoTempObjects >= NUMTEMPOBJECTS) + return nil; + + CObject *pObj = new CObject(modelID, true); + + if (!pObj) return nil; + + pObj->SetPosition(pos); + pObj->GetMatrix().UpdateRW(); + pObj->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + pObj->m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + pObj->GetMatrix().SetRotateZOnly(DEGTORAD(zRotAngle)); + pObj->GetMatrix().UpdateRW(); + pObj->ObjectCreatedBy = CONTROLLED_SUB_OBJECT; + pObj->bIsStatic = false; + + if (pObj->ObjectCreatedBy == TEMP_OBJECT) { + CObject::nNoTempObjects++; + pObj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 60000; + } + + pObj->bTouchingWater = true; + pObj->bUnderwater = true; + CWorld::Add(pObj); + + return pObj; +} + +bool CWaterCreatures::IsSpaceForMoreWaterCreatures() { + return nNumActiveSeaLifeForms < NUM_WATER_CREATURES; +} + +float CWaterCreatures::CalculateFishHeading(CVector const& pos1, CVector const& pos2) { + CVector delta = pos1 - pos2; + delta.Normalise(); + + return CGeneral::GetRandomNumberInRange(-90, 90) + + RADTODEG(delta.Heading() + HALFPI + PI); +} + +void CWaterCreatures::CreateOne(CVector const& pos, int32 modelID) { + if (!IsSpaceForMoreWaterCreatures()) + return; + + CVector playerPos = FindPlayerPed()->GetPosition(); + CVector fishPos = pos; + float fDepth, fLevelNoWaves; + if (!TheCamera.IsSphereVisible(fishPos, 3.0f) + && CWaterLevel::GetWaterDepth(fishPos, &fDepth, &fLevelNoWaves, nil) && fDepth > 4.5f) { + + if (modelID == -1 || modelID < 0 || modelID > 64) + modelID = CGeneral::GetRandomNumberInRange(0, 64); + + WaterCreatureProperties *creature = &aProperties[modelID]; + fishPos.z = fLevelNoWaves - creature->fLevel; + float fFwdSpeed = CGeneral::GetRandomNumberInRange(0.0f, creature->fFwdSpeed) + 0.01f; + float angle = CWaterCreatures::CalculateFishHeading(playerPos, fishPos); + + CObject *fish = CreateSeaLifeForm(fishPos, *creature->modelID, angle); + if (!fish) return; + + fish->SetRwObjectAlpha(255); + CWaterCreature *wc = GetFishStructSlot(); + wc->Allocate(fish, fFwdSpeed, 0.0f, creature->fWaterDepth, 255, WATER_CREATURE_INIT); + nNumActiveSeaLifeForms++; + } +} + +void CWaterCreatures::FreeFishStructSlot(CWaterCreature *wc) { + wc->Free(); +} + +void CWaterCreatures::UpdateAll() { + if (nNumActiveSeaLifeForms == 0) + return; + + CVector playerPos = FindPlayerPed()->GetPosition(); + for (int i = 0; i < NUM_WATER_CREATURES; i++) { + switch (aWaterCreatures[i].m_state) { + case WATER_CREATURE_ACTIVE: + // is this even reachable? + aWaterCreatures[i].m_pObj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 40000; + if (!aWaterCreatures[i].m_pObj->GetIsOnScreen()) { + aWaterCreatures[i].m_pObj->SetRwObjectAlpha(0); + aWaterCreatures[i].m_state = WATER_CREATURE_REMOVE; + break; + } + // fall through + case WATER_CREATURE_INIT: { + if ((playerPos - aWaterCreatures[i].m_pObj->GetPosition()).MagnitudeSqr() < SQR(75.0f)) { + if (aWaterCreatures[i].m_alpha < 255) + aWaterCreatures[i].m_alpha = Min(aWaterCreatures[i].m_alpha + 4, 255); + aWaterCreatures[i].m_pObj->SetRwObjectAlpha(aWaterCreatures[i].m_alpha); + CVector fwd = aWaterCreatures[i].m_pObj->GetRight(); // for some reason they used x for forward + fwd.Normalise(); + aWaterCreatures[i].m_pObj->m_vecMoveSpeed = fwd * aWaterCreatures[i].m_fFwdSpeed; + aWaterCreatures[i].m_pObj->m_vecTurnSpeed = CVector(0.0f, 0.0f, aWaterCreatures[i].m_fZTurnSpeed); + aWaterCreatures[i].m_pObj->bIsStatic = false; + float fDepth = 0.0; + CWaterLevel::GetWaterDepth(aWaterCreatures[i].m_pObj->GetPosition(), &fDepth, nil, nil); + if (aWaterCreatures[i].m_fWaterDepth < fDepth) { + // it looks like this can never be true initially, looks like a BUG + if (aWaterCreatures[i].m_pObj->m_nEndOfLifeTime - 40000 <= CTimer::GetTimeInMilliseconds()) + aWaterCreatures[i].m_state = WATER_CREATURE_ACTIVE; + } + else { + // creature is deeper than water + aWaterCreatures[i].m_state = WATER_CREATURE_FADE_OUT; + } + + } + else { + aWaterCreatures[i].m_state = WATER_CREATURE_REMOVE; + } + break; + } + case WATER_CREATURE_FADE_OUT: { + aWaterCreatures[i].m_pObj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 40000; + if (aWaterCreatures[i].m_alpha <= 0) { + aWaterCreatures[i].m_state = WATER_CREATURE_REMOVE; + } + else { + aWaterCreatures[i].m_alpha = Max(aWaterCreatures[i].m_alpha - 6, 0); + aWaterCreatures[i].m_pObj->SetRwObjectAlpha(aWaterCreatures[i].m_alpha); + CVector speed = aWaterCreatures[i].m_pObj->GetRight(); + speed.Normalise(); + speed.x *= aWaterCreatures[i].m_fFwdSpeed; + speed.y *= aWaterCreatures[i].m_fFwdSpeed; + speed.z = -0.015f; + aWaterCreatures[i].m_pObj->m_vecMoveSpeed = speed; + + if (!aWaterCreatures[i].m_pObj->GetIsOnScreen()) + aWaterCreatures[i].m_state = WATER_CREATURE_REMOVE; + } + break; + } + case WATER_CREATURE_REMOVE: + if (aWaterCreatures[i].m_pObj){ + CWorld::Remove(aWaterCreatures[i].m_pObj); + delete aWaterCreatures[i].m_pObj; + } + FreeFishStructSlot(&aWaterCreatures[i]); + nNumActiveSeaLifeForms--; + aWaterCreatures[i].m_state = WATER_CREATURE_DISABLED; + break; + default: + break; + } + } +} + +void CWaterCreatures::RemoveAll() { + for (int i = 0; i < NUM_WATER_CREATURES; i++) { + if (aWaterCreatures[i].m_state != WATER_CREATURE_DISABLED) { + if (aWaterCreatures[i].m_pObj){ + CWorld::Remove(aWaterCreatures[i].m_pObj); + delete aWaterCreatures[i].m_pObj; + } + FreeFishStructSlot(&aWaterCreatures[i]); + aWaterCreatures[i].m_state = WATER_CREATURE_DISABLED; + nNumActiveSeaLifeForms--; + } + } +} \ No newline at end of file diff --git a/src/miami/renderer/WaterCreatures.h b/src/miami/renderer/WaterCreatures.h new file mode 100644 index 00000000..32754a10 --- /dev/null +++ b/src/miami/renderer/WaterCreatures.h @@ -0,0 +1,49 @@ +#pragma once + +class CObject; + +enum eFishSlotState { + WATER_CREATURE_INIT = 0, + WATER_CREATURE_ACTIVE, + WATER_CREATURE_FADE_OUT, + WATER_CREATURE_REMOVE, + WATER_CREATURE_DISABLED +}; + +class CWaterCreature { +public: + CObject *m_pObj; + float m_fFwdSpeed; + float m_fZTurnSpeed; + int32 m_alpha; + float m_fWaterDepth; + int32 m_state; + + CWaterCreature(); + void Allocate(CObject *pObj, float fFwdSpeed, float fZTurnSpeed, float fWaterDepth, uint32 alpha, eFishSlotState state); + void Free(); + void Initialise(CObject *pObj, float fFwdSpeed, float fZTurnSpeed, float fWaterDepth, uint32 alpha, eFishSlotState state); +}; + +class CWaterCreatures { + +public: + static CWaterCreature aWaterCreatures[NUM_WATER_CREATURES]; + static int32 nNumActiveSeaLifeForms; + static CObject *CreateSeaLifeForm(CVector const& pos, int16 modelID, int32 zRotAngle); + static void CreateOne(CVector const& pos, int32 modelID); + static void UpdateAll(); + static void FreeFishStructSlot(CWaterCreature *wc); + static bool IsSpaceForMoreWaterCreatures(); + static float CalculateFishHeading(CVector const& pos1, CVector const& pos2); + static void RemoveAll(); + static CWaterCreature* GetFishStructSlot(); +}; + +struct WaterCreatureProperties { + int16 *modelID; + float fFwdSpeed; + float fLevel; + float fUnknown; //unused + float fWaterDepth; +}; \ No newline at end of file diff --git a/src/miami/renderer/WaterLevel.cpp b/src/miami/renderer/WaterLevel.cpp new file mode 100644 index 00000000..c0df745c --- /dev/null +++ b/src/miami/renderer/WaterLevel.cpp @@ -0,0 +1,3382 @@ +#include "common.h" +#include "main.h" +#include "FileMgr.h" +#include "FileLoader.h" +#include "TxdStore.h" +#include "Timer.h" +#include "Weather.h" +#include "Camera.h" +#include "Vehicle.h" +#include "PlayerPed.h" +#include "Boat.h" +#include "World.h" +#include "General.h" +#include "Timecycle.h" +#include "ZoneCull.h" +#include "Clock.h" +#include "Particle.h" +#include "ParticleMgr.h" +#include "RwHelper.h" +#include "Streaming.h" +#include "ColStore.h" +#include "CdStream.h" +#include "Pad.h" +#include "RenderBuffer.h" +#include +#include +#include +#include "Occlusion.h" +#include "Replay.h" +#include "WaterLevel.h" +#include "SurfaceTable.h" +#include "WaterCreatures.h" + +#define RwIm3DVertexSet_RGBA(vert, rgba) RwIm3DVertexSetRGBA(vert, rgba.red, rgba.green, rgba.blue, rgba.alpha) // (RwRGBAAssign(&(_dst)->color, &_src)) + +float TEXTURE_ADDU; +float TEXTURE_ADDV; + +float _TEXTURE_MASK_ADDU; +float _TEXTURE_MASK_ADDV; + +float _TEXTURE_WAKE_ADDU; +float _TEXTURE_WAKE_ADDV; + +int32 CWaterLevel::ms_nNoOfWaterLevels; +float CWaterLevel::ms_aWaterZs[48]; +CRect CWaterLevel::ms_aWaterRects[48]; +int8 CWaterLevel::aWaterBlockList[MAX_LARGE_SECTORS][MAX_LARGE_SECTORS]; +int8 CWaterLevel::aWaterFineBlockList[MAX_SMALL_SECTORS][MAX_SMALL_SECTORS]; +bool CWaterLevel::WavesCalculatedThisFrame; + + +bool CWaterLevel::RequireWavySector; +bool CWaterLevel::MaskCalculatedThisFrame; +CVector CWaterLevel::PreCalculatedMaskPosn; +bool CWaterLevel::m_bRenderSeaBed; +int32 CWaterLevel::m_nRenderWaterLayers; + +RpAtomic *CWaterLevel::ms_pWavyAtomic; +RpAtomic *CWaterLevel::ms_pMaskAtomic; +//"Custom" Don't Render Water Toggle +bool gbDontRenderWater; + + +RwTexture *gpWaterTex; +RwTexture *gpWaterEnvTex; +RwTexture *gpWaterEnvBaseTex; +RwTexture *gpWaterWakeTex; + +RwRaster *gpWaterRaster; +RwRaster *gpWaterEnvRaster; +RwRaster *gpWaterEnvBaseRaster; +RwRaster *gpWaterWakeRaster; + +bool _bSeaLife; +float _fWaterZOffset = WATER_Z_OFFSET; + +#ifdef PC_WATER +float fEnvScale = 0.25f; +#else +float fEnvScale = 0.5f; +#endif +float fWave2InvLength = 0.03f; +float fWave2NormScale = 0.5f; +float fWave2Ampl = 0.1f; +uint8 nWaterAlpha = 192; +uint8 nWakeAlpha = 192; +float fUnder1 = 4.0; +float fUnder2 = 2.5; +float fUnder3 = 1.5; +int nMaskAlpha = 230; +float fAdd1 = 180.0f; +float fAdd2 = 80.0; +float fRedMult = 0.6f; +float fGreenMult = 1.0f; +float fBlueMult = 1.4f; +float fAlphaMult = 500.0f; +float fAlphaBase = 30.0f; +float fRandomMoveDiv = 8.0f; +float fRandomDamp = 0.99f; +float fNormMult = 2.0f; +float fNormMultB = 1.0f; +float fBumpScale = 1.5; +float fBumpTexRepeat = 2.0; +float fNormalDirectionScalar1 = 2.0f; +float fNormalDirectionScalar2 = 1.0f; +bool bTestDoNormals = true; +float fSeaBedZ = 25.0f; +float aAlphaFade[5] = { 0.4f, 1.0f, 0.2f, 1.0f, 0.4f}; //CWaterLevel::RenderWakeSegment +float fFlatWaterBlendRange = 0.05f; +float fStartBlendDistanceAdd = 64.0f; +float fMinWaterAlphaMult = -30.0f; + + +void +CWaterLevel::Initialise(Const char *pWaterDat) +{ + ms_nNoOfWaterLevels = 0; + +#ifdef MASTER + int32 hFile = -1; + + do + { + hFile = CFileMgr::OpenFile("DATA\\waterpro.dat", "rb"); + } + while ( hFile < 0 ); +#else + int32 hFile = CFileMgr::OpenFile("DATA\\waterpro.dat", "rb"); +#endif + + if (hFile > 0) + { + CFileMgr::Read(hFile, (char *)&ms_nNoOfWaterLevels, sizeof(ms_nNoOfWaterLevels)); + CFileMgr::Read(hFile, (char *)ms_aWaterZs, sizeof(ms_aWaterZs)); + CFileMgr::Read(hFile, (char *)ms_aWaterRects, sizeof(ms_aWaterRects)); + CFileMgr::Read(hFile, (char *)aWaterBlockList, sizeof(aWaterBlockList)); + CFileMgr::Read(hFile, (char *)aWaterFineBlockList, sizeof(aWaterFineBlockList)); + CFileMgr::CloseFile(hFile); + } +#ifndef MASTER + else + { + printf("Init waterlevels\n"); + + // collision is streamed in VC + CColStore::LoadAllCollision(); + + CFileMgr::SetDir(""); + hFile = CFileMgr::OpenFile(pWaterDat, "r"); + + char *line; + + while ((line = CFileLoader::LoadLine(hFile))) + { + if (*line && *line != ';' && !strstr(line, "* ;end of file")) + { + float z, l, b, r, t; + sscanf(line, "%f %f %f %f %f", &z, &l, &b, &r, &t); + AddWaterLevel(l, b, r, t, z); + } + } + + CFileMgr::CloseFile(hFile); + + for (int32 x = 0; x < MAX_SMALL_SECTORS; x++) + { + for (int32 y = 0; y < MAX_SMALL_SECTORS; y++) + { + aWaterFineBlockList[x][y] = NO_WATER; + } + } + + // rasterize water rects read from file + for (int32 i = 0; i < ms_nNoOfWaterLevels; i++) + { + int32 l = WATER_HUGE_X(ms_aWaterRects[i].left + WATER_X_OFFSET); + int32 r = WATER_HUGE_X(ms_aWaterRects[i].right + WATER_X_OFFSET) + 1.0f; + int32 t = WATER_HUGE_Y(ms_aWaterRects[i].top); + int32 b = WATER_HUGE_Y(ms_aWaterRects[i].bottom) + 1.0f; + + l = Clamp(l, 0, MAX_SMALL_SECTORS - 1); + r = Clamp(r, 0, MAX_SMALL_SECTORS - 1); + t = Clamp(t, 0, MAX_SMALL_SECTORS - 1); + b = Clamp(b, 0, MAX_SMALL_SECTORS - 1); + + for (int32 x = l; x <= r; x++) + { + for (int32 y = t; y <= b; y++) + { + aWaterFineBlockList[x][y] = i; + } + } + } + + // remove tiles that are obscured by land + for (int32 x = 0; x < MAX_SMALL_SECTORS; x++) + { + float worldX = WATER_START_X + x * SMALL_SECTOR_SIZE - WATER_X_OFFSET; + + for (int32 y = 0; y < MAX_SMALL_SECTORS; y++) + { + if (CWaterLevel::aWaterFineBlockList[x][y] >= 0) + { + float worldY = WATER_START_Y + y * SMALL_SECTOR_SIZE; + + int32 i; + for (i = 0; i <= 8; i++) + { + for (int32 j = 0; j <= 8; j++) + { + CVector worldPos = CVector(worldX + i * (SMALL_SECTOR_SIZE / 8), worldY + j * (SMALL_SECTOR_SIZE / 8), ms_aWaterZs[aWaterFineBlockList[x][y]]); + + if ((worldPos.x > WORLD_MIN_X && worldPos.x < WORLD_MAX_X) && (worldPos.y > WORLD_MIN_Y && worldPos.y < WORLD_MAX_Y) && + (!WaterLevelAccordingToRectangles(worldPos.x, worldPos.y) || TestVisibilityForFineWaterBlocks(worldPos))) + continue; + + // at least one point in the tile wasn't blocked, so don't remove water + i = 1000; + break; + } + } + + if (i < 1000) + aWaterFineBlockList[x][y] = NO_WATER; + } + } + } + + RemoveIsolatedWater(); + + // calculate coarse tiles from fine tiles + for (int32 x = 0; x < MAX_LARGE_SECTORS; x++) + { + for (int32 y = 0; y < MAX_LARGE_SECTORS; y++) + { + if (aWaterFineBlockList[x * 2][y * 2] >= 0) + { + aWaterBlockList[x][y] = aWaterFineBlockList[x * 2][y * 2]; + } + else if (aWaterFineBlockList[x * 2 + 1][y * 2] >= 0) + { + aWaterBlockList[x][y] = aWaterFineBlockList[x * 2 + 1][y * 2]; + } + else if (aWaterFineBlockList[x * 2][y * 2 + 1] >= 0) + { + aWaterBlockList[x][y] = aWaterFineBlockList[x * 2][y * 2 + 1]; + } + else if (aWaterFineBlockList[x * 2 + 1][y * 2 + 1] >= 0) + { + aWaterBlockList[x][y] = aWaterFineBlockList[x * 2 + 1][y * 2 + 1]; + } + else + { + aWaterBlockList[x][y] = NO_WATER; + } + } + } + + hFile = CFileMgr::OpenFileForWriting("data\\waterpro.dat"); + + if (hFile > 0) + { + CFileMgr::Write(hFile, (char *)&ms_nNoOfWaterLevels, sizeof(ms_nNoOfWaterLevels)); + CFileMgr::Write(hFile, (char *)ms_aWaterZs, sizeof(ms_aWaterZs)); + CFileMgr::Write(hFile, (char *)ms_aWaterRects, sizeof(ms_aWaterRects)); + CFileMgr::Write(hFile, (char *)aWaterBlockList, sizeof(aWaterBlockList)); + CFileMgr::Write(hFile, (char *)aWaterFineBlockList, sizeof(aWaterFineBlockList)); + + CFileMgr::CloseFile(hFile); + } + + // collision is streamed in VC + CColStore::RemoveAllCollision(); + } +#endif + + CTxdStore::PushCurrentTxd(); + + int32 slot = CTxdStore::FindTxdSlot("particle"); + CTxdStore::SetCurrentTxd(slot); + + if ( gpWaterTex == nil ) + gpWaterTex = RwTextureRead("waterclear256", nil); + gpWaterRaster = RwTextureGetRaster(gpWaterTex); + + if ( gpWaterEnvTex == nil ) + gpWaterEnvTex = RwTextureRead("waterreflection2", nil); + gpWaterEnvRaster = RwTextureGetRaster(gpWaterEnvTex); + +#ifdef PC_WATER + if ( gpWaterEnvBaseTex == nil ) + gpWaterEnvBaseTex = RwTextureRead("sandywater", nil); + gpWaterEnvBaseRaster = RwTextureGetRaster(gpWaterEnvBaseTex); +#endif + + if ( gpWaterWakeTex == nil ) + gpWaterWakeTex = RwTextureRead("waterwake", nil); + gpWaterWakeRaster = RwTextureGetRaster(gpWaterWakeTex); + + CTxdStore::PopCurrentTxd(); + + CreateWavyAtomic(); + + printf("Done Initing waterlevels\n"); +} + +void +CWaterLevel::Shutdown() +{ + DestroyWavyAtomic(); + +#define _DELETE_TEXTURE(t) if ( t ) \ + { \ + RwTextureDestroy(t); \ + t = nil; \ + } + + _DELETE_TEXTURE(gpWaterTex); + _DELETE_TEXTURE(gpWaterEnvTex); + _DELETE_TEXTURE(gpWaterWakeTex); + _DELETE_TEXTURE(gpWaterEnvBaseTex); + +#undef _DELETE_TEXTURE +} + +void +CWaterLevel::CreateWavyAtomic() +{ + RpGeometry *wavyGeometry; + RpGeometry *maskGeometry; + RpMaterial *wavyMaterial; + RpMaterial *maskMaterial; + + RpTriangle *wavytlist; + RpTriangle *masktlist; + + RpMorphTarget *wavyMorphTarget; + RpMorphTarget *maskMorphTarget; + + RwSphere boundingSphere; + + RwV3d *wavyVert; + RwV3d *wavyNormal; + + RwV3d *maskVert; + RwV3d *maskNormal; + + RwFrame *wavyFrame; + RwFrame *maskFrame; + + { + wavyGeometry = RpGeometryCreate(17*17, 512, rpGEOMETRYTRISTRIP + |rpGEOMETRYTEXTURED + |rpGEOMETRYPRELIT + |rpGEOMETRYNORMALS + |rpGEOMETRYMODULATEMATERIALCOLOR + |rw::Geometry::HAS_TRIANGLES/* RW_DC specific */); +#ifdef PC_WATER + RpGeometryAddMorphTarget(wavyGeometry); +#endif + } + + { + maskGeometry = RpGeometryCreate(33*33, 2048, rpGEOMETRYTRISTRIP + |rpGEOMETRYTEXTURED + |rpGEOMETRYPRELIT + |rpGEOMETRYNORMALS + |rpGEOMETRYMODULATEMATERIALCOLOR + |rw::Geometry::HAS_TRIANGLES/* RW_DC specific */); +#ifdef PC_WATER + RpGeometryAddMorphTarget(maskGeometry); +#endif + } + + { + wavyMaterial = RpMaterialCreate(); + RpMaterialSetTexture(wavyMaterial, gpWaterTex); + RwRGBA watercolor = { 255, 255, 255, 192 }; + RpMaterialSetColor(wavyMaterial, &watercolor); + } + + { + maskMaterial = RpMaterialCreate(); +#ifdef PC_WATER + RpMaterialSetTexture(maskMaterial, gpWaterEnvBaseTex); +#else + RpMaterialSetTexture(maskMaterial, gpWaterTex); +#endif + RwRGBA watercolor = { 255, 255, 255, 192 }; + RpMaterialSetColor(maskMaterial, &watercolor); + } + + { + wavytlist = RpGeometryGetTriangles(wavyGeometry); + + for ( int32 i = 0; i < 16; i++ ) + { + for ( int32 j = 0; j < 16; j++ ) + { + const RwUInt16 base = (RwUInt16)((16 + 1)*i+j); + + RpGeometryTriangleSetVertexIndices(wavyGeometry, + wavytlist, (RwInt16)base, (RwInt16)(base+1), (RwInt16)(base+16+2)); + + RpGeometryTriangleSetVertexIndices(wavyGeometry, + (wavytlist+1), (RwInt16)base, (RwInt16)(base+16+2), (RwInt16)(base+16+1)); + + RpGeometryTriangleSetMaterial(wavyGeometry, wavytlist, wavyMaterial); + + RpGeometryTriangleSetMaterial(wavyGeometry, (wavytlist+1), wavyMaterial); + + wavytlist+=2; + } + } + } + + { + masktlist = RpGeometryGetTriangles(maskGeometry); + + for ( int32 i = 0; i < 32; i++ ) + { + for ( int32 j = 0; j < 32; j++ ) + { + const RwUInt16 base = (RwUInt16)((32 + 1)*i+j); + + RpGeometryTriangleSetVertexIndices(maskGeometry, + masktlist, (RwInt16)base, (RwInt16)(base+1), (RwInt16)(base+32+2)); + + RpGeometryTriangleSetVertexIndices(maskGeometry, + (masktlist+1), (RwInt16)base, (RwInt16)(base+32+2), (RwInt16)(base+32+1)); + + RpGeometryTriangleSetMaterial(maskGeometry, masktlist, maskMaterial); + + RpGeometryTriangleSetMaterial(maskGeometry, (masktlist+1), maskMaterial); + + masktlist+=2; + } + } + } + + { + wavyMorphTarget = RpGeometryGetMorphTarget(wavyGeometry, 0); + wavyVert = RpMorphTargetGetVertices(wavyMorphTarget); + wavyNormal = RpMorphTargetGetVertexNormals(wavyMorphTarget); + + for ( int32 i = 0; i < 17; i++ ) + { + for ( int32 j = 0; j < 17; j++ ) + { + (*wavyVert).x = (float)i * 2.0f; + (*wavyVert).y = (float)j * 2.0f; + (*wavyVert).z = 0.0f; + + (*wavyNormal).x = 0.0f; + (*wavyNormal).y = 0.0f; + (*wavyNormal).z = 1.0f; + + wavyVert++; + wavyNormal++; + } + } + + RpMorphTargetCalcBoundingSphere(wavyMorphTarget, &boundingSphere); + RpMorphTargetSetBoundingSphere(wavyMorphTarget, &boundingSphere); + RpGeometryUnlock(wavyGeometry); + } + + { + maskMorphTarget = RpGeometryGetMorphTarget(maskGeometry, 0); + maskVert = RpMorphTargetGetVertices(maskMorphTarget); + maskNormal = RpMorphTargetGetVertexNormals(maskMorphTarget); + + for ( int32 i = 0; i < 33; i++ ) + { + for ( int32 j = 0; j < 33; j++ ) + { + (*maskVert).x = (float)i * 2.0f; + (*maskVert).y = (float)j * 2.0f; + (*maskVert).z = 0.0f; + + (*maskNormal).x = 0.0f; + (*maskNormal).y = 0.0f; + (*maskNormal).z = 1.0f; + + maskVert++; + maskNormal++; + } + } + + RpMorphTargetCalcBoundingSphere(maskMorphTarget, &boundingSphere); + RpMorphTargetSetBoundingSphere(maskMorphTarget, &boundingSphere); + RpGeometryUnlock(maskGeometry); + } + + { + wavyFrame = RwFrameCreate(); + ms_pWavyAtomic = RpAtomicCreate(); + RpAtomicSetGeometry(ms_pWavyAtomic, wavyGeometry, 0); + RpAtomicSetFrame(ms_pWavyAtomic, wavyFrame); + RpMaterialDestroy(wavyMaterial); + RpGeometryDestroy(wavyGeometry); + } + + { + maskFrame = RwFrameCreate(); + ms_pMaskAtomic = RpAtomicCreate(); + RpAtomicSetGeometry(ms_pMaskAtomic, maskGeometry, 0); + RpAtomicSetFrame(ms_pMaskAtomic, maskFrame); + RpMaterialDestroy(maskMaterial); + RpGeometryDestroy(maskGeometry); + } + + static RwFrame *wakeEnvFrame; + + if ( wakeEnvFrame == nil ) + { + wakeEnvFrame = RwFrameCreate(); + RwMatrixSetIdentity(RwFrameGetMatrix(wakeEnvFrame)); + RwFrameUpdateObjects(wakeEnvFrame); + } + + RpMatFXMaterialSetEffects(maskMaterial, rpMATFXEFFECTENVMAP); + RpMatFXMaterialSetupEnvMap(maskMaterial, gpWaterEnvTex, wakeEnvFrame, TRUE, fEnvScale); + RpMatFXAtomicEnableEffects(ms_pMaskAtomic); +} + +void +CWaterLevel::DestroyWavyAtomic() +{ +#define _DELETE_ATOMIC(a) \ + { \ + RwFrame *frame; \ + frame = RpAtomicGetFrame(a); \ + RpAtomicDestroy(a); \ + RwFrameDestroy(frame); \ + } + + _DELETE_ATOMIC(ms_pWavyAtomic); + _DELETE_ATOMIC(ms_pMaskAtomic); + +#undef _DELETE_ATOMIC +} + +#ifndef MASTER +void +CWaterLevel::AddWaterLevel(float fXLeft, float fYBottom, float fXRight, float fYTop, float fLevel) +{ + ms_aWaterRects[ms_nNoOfWaterLevels] = CRect(fXLeft, fYBottom, fXRight, fYTop); + ms_aWaterZs[ms_nNoOfWaterLevels] = fLevel; + ms_nNoOfWaterLevels++; +} + +bool +CWaterLevel::WaterLevelAccordingToRectangles(float fX, float fY, float *pfOutLevel) +{ + if (ms_nNoOfWaterLevels <= 0) return false; + + for (int32 i = 0; i < ms_nNoOfWaterLevels; i++) + { + if (fX >= ms_aWaterRects[i].left && fX <= ms_aWaterRects[i].right + && fY >= ms_aWaterRects[i].top && fY <= ms_aWaterRects[i].bottom) + { + if (pfOutLevel) *pfOutLevel = ms_aWaterZs[i]; + + return true; + } + } + + return false; +} + +bool +CWaterLevel::TestVisibilityForFineWaterBlocks(const CVector &worldPos) +{ + static CVector2D tab[] = + { + { 50.0f, 50.0f }, + { -50.0f, 50.0f }, + { -50.0f, -50.0f }, + { 50.0f, -50.0f }, + { 50.0f, 0.0f }, + { -50.0f, 0.0f }, + { 0.0f, -50.0f }, + { 0.0f, 50.0f }, + }; + + CEntity *entity; + CColPoint col; + CVector lineStart, lineEnd; + + lineStart = worldPos; + + if (!CWorld::ProcessVerticalLine(lineStart, lineStart.z + 100.0f, col, entity, true, false, false, false, true, false, nil)) + { + lineStart.x += 0.4f; + lineStart.y += 0.4f; + + if (!CWorld::ProcessVerticalLine(lineStart, lineStart.z + 100.0f, col, entity, true, false, false, false, true, false, nil)) + { + return false; + } + } + + for (int32 i = 0; i < ARRAY_SIZE(tab); i++) + { + lineStart = worldPos; + lineEnd = worldPos; + + lineEnd.x += tab[i].x; + lineEnd.y += tab[i].y; + lineEnd.z += 100.0f; + + if ((lineEnd.x > WORLD_MIN_X && lineEnd.x < WORLD_MAX_X) && (lineEnd.y > WORLD_MIN_Y && lineEnd.y < WORLD_MAX_Y)) + { + if (!CWorld::ProcessLineOfSight(lineStart, lineEnd, col, entity, true, false, false, false, true, false)) + { + lineStart.x += 0.4f; + lineStart.y += 0.4f; + lineEnd.x += 0.4f; + lineEnd.y += 0.4f; + + if (!CWorld::ProcessLineOfSight(lineStart, lineEnd, col, entity, true, false, false, false, true, false)) + { + return false; + } + } + } + } + + return true; +} + +void +CWaterLevel::RemoveIsolatedWater() +{ + bool (*isConnected)[MAX_SMALL_SECTORS] = new bool[MAX_SMALL_SECTORS][MAX_SMALL_SECTORS]; + + for (int32 x = 0; x < MAX_SMALL_SECTORS; x++) + { + for (int32 y = 0; y < MAX_SMALL_SECTORS; y++) + { + isConnected[x][y] = false; + } + } + + isConnected[0][0] = true; + bool keepGoing; + + do + { + keepGoing = false; + + for (int32 x = 0; x < MAX_SMALL_SECTORS; x++) + { + for (int32 y = 0; y < MAX_SMALL_SECTORS; y++) + { + if (aWaterFineBlockList[x][y] < 0 || isConnected[x][y]) + continue; + + if (x > 0 && isConnected[x - 1][y]) + { + isConnected[x][y] = true; + keepGoing = true; + } + + if (y > 0 && isConnected[x][y - 1]) + { + isConnected[x][y] = true; + keepGoing = true; + } + + if (x + 1 < MAX_SMALL_SECTORS && isConnected[x + 1][y]) + { + isConnected[x][y] = true; + keepGoing = true; + } + + if (y + 1 < MAX_SMALL_SECTORS && isConnected[x][y + 1]) + { + isConnected[x][y] = true; + keepGoing = true; + } + } + } + } + while (keepGoing); + + int32 numRemoved = 0; + + for (int32 x = 0; x < MAX_SMALL_SECTORS; x++) + { + for (int32 y = 0; y < MAX_SMALL_SECTORS; y++) + { + if (aWaterFineBlockList[x][y] >= 0 && !isConnected[x][y] && ms_aWaterZs[aWaterFineBlockList[x][y]] == 6.0f) + { + numRemoved++; + aWaterFineBlockList[x][y] = NO_WATER; + } + } + } + + printf("Removed %d isolated patches of water\n", numRemoved); + + delete[] isConnected; +} +#endif + +bool +CWaterLevel::GetWaterLevel(float fX, float fY, float fZ, float *pfOutLevel, bool bDontCheckZ) +{ + int32 x = WATER_TO_SMALL_SECTOR_X(fX + WATER_X_OFFSET); + int32 y = WATER_TO_SMALL_SECTOR_Y(fY); + +#ifdef FIX_BUGS + if ( x < 0 || x >= MAX_SMALL_SECTORS ) return false; + if ( y < 0 || y >= MAX_SMALL_SECTORS ) return false; +#endif + + int8 nBlock = aWaterFineBlockList[x][y]; + + if ( nBlock == NO_WATER ) + return false; + + ASSERT( pfOutLevel != nil ); + *pfOutLevel = ms_aWaterZs[nBlock]; + + float fAngle = (CTimer::GetTimeInMilliseconds() & 4095) * (TWOPI / 4096.0f); + + float fWave = Sin + ( + ( WATER_UNSIGN_Y(fY) - y*SMALL_SECTOR_SIZE + + WATER_UNSIGN_X(fX + WATER_X_OFFSET) - x*SMALL_SECTOR_SIZE ) + + * (TWOPI / SMALL_SECTOR_SIZE ) + fAngle + ); + + float fWindFactor = CWeather::WindClipped * 0.4f + 0.2f; + + *pfOutLevel += fWave * fWindFactor; + + if ( bDontCheckZ == false && (*pfOutLevel - fZ) > 3.0f ) + { + *pfOutLevel = 0.0f; + return false; + } + + return true; +} + +bool +CWaterLevel::GetWaterLevelNoWaves(float fX, float fY, float fZ, float *pfOutLevel) +{ + int32 x = WATER_TO_SMALL_SECTOR_X(fX + WATER_X_OFFSET); + int32 y = WATER_TO_SMALL_SECTOR_Y(fY); + +#ifdef FIX_BUGS + if ( x < 0 || x >= MAX_SMALL_SECTORS ) return false; + if ( y < 0 || y >= MAX_SMALL_SECTORS ) return false; +#endif + + int8 nBlock = aWaterFineBlockList[x][y]; + + if ( nBlock == NO_WATER ) + return false; + + ASSERT( pfOutLevel != nil ); + *pfOutLevel = ms_aWaterZs[nBlock]; + + return true; +} + +float +CWaterLevel::GetWaterWavesOnly(short x, short y) +{ + float fAngle = (CTimer::GetTimeInMilliseconds() & 4095) * (TWOPI / 4096.0f); + + float fWindFactor = CWeather::WindClipped * 0.7f + 0.3f; + + float fWave = Sin( float(float(4 * y + 4 * x) * (TWOPI / SMALL_SECTOR_SIZE )) + fAngle ); + + return fWave * fWindFactor; +} + +CVector +CWaterLevel::GetWaterNormal(float fX, float fY) +{ + //TODO: BUG ? no x offset + + int32 x = WATER_TO_SMALL_SECTOR_X(fX); + int32 y = WATER_TO_SMALL_SECTOR_Y(fY); + + float fAngle = (CTimer::GetTimeInMilliseconds() & 4095) * (TWOPI / 4096.0f); + float fWindFactor = CWeather::WindClipped * 0.4f + 0.2f; + + float _fWave = (WATER_UNSIGN_Y(fY) - y*SMALL_SECTOR_SIZE + WATER_UNSIGN_X(fX) - x*SMALL_SECTOR_SIZE) + * (TWOPI / SMALL_SECTOR_SIZE ) + fAngle; + + CVector vA(1.0f, 0.0f, fWindFactor * (TWOPI / SMALL_SECTOR_SIZE ) * Cos(_fWave)); + CVector vB(0.0f, 1.0f, fWindFactor * (TWOPI / SMALL_SECTOR_SIZE ) * Cos(_fWave)); + + CVector norm = CrossProduct(vA, vB); + + norm.Normalise(); + + return norm; +} + + +inline float +_GetWaterDrawDist() +{ + if ( TheCamera.GetPosition().z < 15.0f ) return 1200.0f; + if ( TheCamera.GetPosition().z > 60.0f ) return 2000.0f; + return ( TheCamera.GetPosition().z + -15.0f ) * 800.0f / 45.0f + 1200.0f; +} + +inline float +_GetWavyDrawDist() +{ +#ifdef DISABLE_WAVY_WATER + return 0.0; +#endif + if ( FindPlayerVehicle() && FindPlayerVehicle()->IsBoat() ) + return 120.0f; + else + return 70.0f; +} + +inline void +_GetCamBounds(bool *bUseCamStartY, bool *bUseCamEndY, bool *bUseCamStartX, bool *bUseCamEndX) +{ + if ( TheCamera.GetForward().z > -0.8f ) + { + if ( Abs(TheCamera.GetForward().x) > Abs(TheCamera.GetForward().y) ) + { + if ( TheCamera.GetForward().x > 0.0f ) + *bUseCamStartX = true; + else + *bUseCamEndX = true; + } + else + { + if ( TheCamera.GetForward().y > 0.0f ) + *bUseCamStartY = true; + else + *bUseCamEndY = true; + } + } +} + + +inline bool +_IsColideWithBlock(int32 x, int32 y, int32 &block) +{ + block = CWaterLevel::aWaterFineBlockList[x + 0][y + 0]; + if (block >= 0) + return true; + + block = CWaterLevel::aWaterFineBlockList[x + 0][y + 1]; + if (block >= 0) + { + block = CWaterLevel::aWaterFineBlockList[x + 0][y + 2]; + if (block >= 0) + return true; + } + + block = CWaterLevel::aWaterFineBlockList[x + 1][y + 0]; + if (block >= 0) + return true; + + block = CWaterLevel::aWaterFineBlockList[x + 1][y + 1]; + if (block >= 0) + { + block = CWaterLevel::aWaterFineBlockList[x + 1][y + 2]; + if (block >= 0) + return true; + } + + block = CWaterLevel::aWaterFineBlockList[x + 2][y + 0]; + if (block >= 0) + return true; + + block = CWaterLevel::aWaterFineBlockList[x + 2][y + 1]; + if (block >= 0) + { + block = CWaterLevel::aWaterFineBlockList[x + 2][y + 2]; + if (block >= 0) + return true; + } + + return false; +} + +inline float +SectorRadius(float fSize) +{ + return Sqrt(Pow(fSize, 2) + Pow(fSize, 2)); +} + +void +CWaterLevel::RenderWater() +{ +//"Custom" Don't Render Water Toggle +#ifndef MASTER + if (gbDontRenderWater) + return; +#endif + bool bUseCamEndX = false; + bool bUseCamStartY = false; + + bool bUseCamStartX = false; + bool bUseCamEndY = false; + + if ( !CGame::CanSeeWaterFromCurrArea() ) + return; + + _GetCamBounds(&bUseCamStartY, &bUseCamEndY, &bUseCamStartX, &bUseCamEndX); + + float fHugeSectorMaxRenderDist = _GetWaterDrawDist(); + float fHugeSectorMaxRenderDistSqr = SQR(fHugeSectorMaxRenderDist); + + float windAddUV = CWeather::WindClipped * 0.0005f + 0.0006f; + + float fAngle = (CTimer::GetTimeInMilliseconds() & 4095) * (TWOPI / 4096.0f); + + if ( !CTimer::GetIsPaused() ) + { + TEXTURE_ADDU += windAddUV; + TEXTURE_ADDV += windAddUV; + + _TEXTURE_MASK_ADDU += Sin(fAngle) * 0.0005f + 1.1f * windAddUV; + _TEXTURE_MASK_ADDV -= Cos(fAngle * 1.3f) * 0.0005f + 1.2f * windAddUV; + + _TEXTURE_WAKE_ADDU -= Sin(fAngle) * 0.0003f + windAddUV; + _TEXTURE_WAKE_ADDV += Cos(fAngle * 0.7f) * 0.0003f + windAddUV; + } + + if ( _TEXTURE_MASK_ADDU >= 1.0f ) + _TEXTURE_MASK_ADDU = 0.0f; + if ( _TEXTURE_MASK_ADDV >= 1.0f ) + _TEXTURE_MASK_ADDV = 0.0f; + + if ( _TEXTURE_WAKE_ADDU >= 1.0f ) + _TEXTURE_WAKE_ADDU = 0.0f; + if ( _TEXTURE_WAKE_ADDV >= 1.0f ) + _TEXTURE_WAKE_ADDV = 0.0f; + + if ( TEXTURE_ADDU >= 1.0f ) + TEXTURE_ADDU = 0.0f; + if ( TEXTURE_ADDV >= 1.0f ) + TEXTURE_ADDV = 0.0f; + +#ifdef PC_WATER + _fWaterZOffset = CWeather::WindClipped * 0.5f + 0.25f; +#endif + + RwRGBA color = { 0, 0, 0, 255 }; + + color.red = CTimeCycle::GetWaterRed(); + color.green = CTimeCycle::GetWaterGreen(); + color.blue = CTimeCycle::GetWaterBlue(); + +#ifndef PC_WATER + RwRGBA colorUnderwater = { 0, 0, 0, 255 }; + colorUnderwater.red = (uint32)(0.8f * (float)colorUnderwater.red); + colorUnderwater.green = (uint32)(0.8f * (float)colorUnderwater.green); + colorUnderwater.blue = (uint32)(0.8f * (float)colorUnderwater.blue); +#endif + + TempBufferVerticesStored = 0; + TempBufferIndicesStored = 0; + +#ifndef PC_WATER + WavesCalculatedThisFrame = false; +#endif + + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)gpWaterRaster); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDZERO); + + CVector2D camPos(TheCamera.GetPosition().x, TheCamera.GetPosition().y); + + int32 nStartX = WATER_TO_HUGE_SECTOR_X(camPos.x - fHugeSectorMaxRenderDist + WATER_X_OFFSET); + int32 nEndX = WATER_TO_HUGE_SECTOR_X(camPos.x + fHugeSectorMaxRenderDist + WATER_X_OFFSET) + 1; + int32 nStartY = WATER_TO_HUGE_SECTOR_Y(camPos.y - fHugeSectorMaxRenderDist); + int32 nEndY = WATER_TO_HUGE_SECTOR_Y(camPos.y + fHugeSectorMaxRenderDist) + 1; + + if ( bUseCamStartX ) + nStartX = WATER_TO_HUGE_SECTOR_X(camPos.x + WATER_X_OFFSET); + if ( bUseCamEndX ) + nEndX = WATER_TO_HUGE_SECTOR_X(camPos.x + WATER_X_OFFSET); + if ( bUseCamStartY ) + nStartY = WATER_TO_HUGE_SECTOR_Y(camPos.y); + if ( bUseCamEndY ) + nEndY = WATER_TO_HUGE_SECTOR_Y(camPos.y); + + nStartX = Clamp(nStartX, 0, MAX_HUGE_SECTORS - 1); + nEndX = Clamp(nEndX, 0, MAX_HUGE_SECTORS - 1); + nStartY = Clamp(nStartY, 0, MAX_HUGE_SECTORS - 1); + nEndY = Clamp(nEndY, 0, MAX_HUGE_SECTORS - 1); + + for ( int32 x = nStartX; x <= nEndX; x++ ) + { + for ( int32 y = nStartY; y <= nEndY; y++ ) + { + if ( aWaterBlockList[2*x+0][2*y+0] >= 0 + || aWaterBlockList[2*x+1][2*y+0] >= 0 + || aWaterBlockList[2*x+0][2*y+1] >= 0 + || aWaterBlockList[2*x+1][2*y+1] >= 0 ) + { + float fX = WATER_FROM_HUGE_SECTOR_X(x) - WATER_X_OFFSET; + float fY = WATER_FROM_HUGE_SECTOR_Y(y); + + CVector2D vecHugeSectorCentre(fX + HUGE_SECTOR_SIZE/2,fY + HUGE_SECTOR_SIZE/2); + + float fHugeSectorDistToCamSqr = (camPos - vecHugeSectorCentre).MagnitudeSqr(); + + if ( fHugeSectorMaxRenderDistSqr > fHugeSectorDistToCamSqr ) + { + if ( TheCamera.IsSphereVisible(CVector(vecHugeSectorCentre.x, vecHugeSectorCentre.y, 0.0f), SectorRadius(HUGE_SECTOR_SIZE)) ) + { +#ifndef PC_WATER + WavesCalculatedThisFrame = true; +#endif + + + float fZ; + + if ( aWaterBlockList[2*x+0][2*y+0] >= 0 ) + fZ = ms_aWaterZs[ aWaterBlockList[2*x+0][2*y+0] ]; + + if ( aWaterBlockList[2*x+1][2*y+0] >= 0 ) + fZ = ms_aWaterZs[ aWaterBlockList[2*x+1][2*y+0] ]; + + if ( aWaterBlockList[2*x+0][2*y+1] >= 0 ) + fZ = ms_aWaterZs[ aWaterBlockList[2*x+0][2*y+1] ]; + + if ( aWaterBlockList[2*x+1][2*y+1] >= 0 ) + fZ = ms_aWaterZs[ aWaterBlockList[2*x+1][2*y+1] ]; + + if ( fHugeSectorDistToCamSqr >= SQR(500.0f) ) + { + RenderOneFlatHugeWaterPoly(fX, fY, fZ, color); + } + else + { +#ifndef PC_WATER + if (m_bRenderSeaBed) + RenderOneSlopedUnderWaterPoly(fX, fY, fZ, colorUnderwater); +#endif + // see RenderTransparentWater() + ; + } + } + } + } + } + } + + /* + ----------- ---------------------- ---------------------- + | [N] | | [ EndY ] | | [ top ] | + | | | | | | + |[W] [0] [E]| |[StartX] [] [ EndX ]| |[ left ] [] [ right]| + | | | | | | + | [S] | | [StartY] | | [bottom] | + ----------- ---------------------- ---------------------- + + + [S] [StartY] [bottom] + [N] [EndY] [top] + [W] [StartX] [left] + [E] [EndX] [right] + + [S] -> [N] && [W] -> [E] + bottom -> top && left -> right + */ + + for ( int32 x = 0; x < 26; x++ ) + { + for ( int32 y = 0; y < 5; y++ ) + { + float fX = WATER_SIGN_X(float(x) * EXTRAHUGE_SECTOR_SIZE) - 1280.0f - WATER_X_OFFSET; + float fY = WATER_SIGN_Y(float(y) * EXTRAHUGE_SECTOR_SIZE) - 1280.0f; + + if ( !bUseCamStartY ) + { + CVector2D vecExtraHugeSectorCentre(fX + EXTRAHUGE_SECTOR_SIZE/2, fY + EXTRAHUGE_SECTOR_SIZE/2); + + float fCamDistToSector = (vecExtraHugeSectorCentre - camPos).Magnitude(); + + if ( fCamDistToSector < fHugeSectorMaxRenderDistSqr ) + { + if ( TheCamera.IsSphereVisible(CVector(vecExtraHugeSectorCentre.x, vecExtraHugeSectorCentre.y, 0.0f), SectorRadius(EXTRAHUGE_SECTOR_SIZE)) ) + { + RenderOneFlatExtraHugeWaterPoly( + vecExtraHugeSectorCentre.x - EXTRAHUGE_SECTOR_SIZE/2, + vecExtraHugeSectorCentre.y - EXTRAHUGE_SECTOR_SIZE/2, + 0.0f, + color); + } + } + } + + if ( !bUseCamEndY ) + { + CVector2D vecExtraHugeSectorCentre(fX + EXTRAHUGE_SECTOR_SIZE/2, -(fY + EXTRAHUGE_SECTOR_SIZE/2)); + + float fCamDistToSector = (vecExtraHugeSectorCentre - camPos).Magnitude(); + + if ( fCamDistToSector < fHugeSectorMaxRenderDistSqr ) + { + if ( TheCamera.IsSphereVisible(CVector(vecExtraHugeSectorCentre.x, vecExtraHugeSectorCentre.y, 0.0f), SectorRadius(EXTRAHUGE_SECTOR_SIZE)) ) + { + RenderOneFlatExtraHugeWaterPoly( + vecExtraHugeSectorCentre.x - EXTRAHUGE_SECTOR_SIZE/2, + vecExtraHugeSectorCentre.y - EXTRAHUGE_SECTOR_SIZE/2, + 0.0f, + color); + } + } + } + } + } + + for ( int32 y = 5; y < 21; y++ ) + { + for ( int32 x = 0; x < 5; x++ ) + { + float fX = WATER_SIGN_X(float(x) * EXTRAHUGE_SECTOR_SIZE) - 1280.0f - WATER_X_OFFSET; + float fX2 = WATER_SIGN_X(float(x) * EXTRAHUGE_SECTOR_SIZE) - 1280.0f + WATER_X_OFFSET; + float fY = WATER_SIGN_Y(float(y) * EXTRAHUGE_SECTOR_SIZE) - 1280.0f; + + if ( !bUseCamStartX ) + { + CVector2D vecExtraHugeSectorCentre(fX + EXTRAHUGE_SECTOR_SIZE/2, fY + EXTRAHUGE_SECTOR_SIZE/2); + + float fCamDistToSector = (vecExtraHugeSectorCentre - camPos).Magnitude(); + + if ( fCamDistToSector < fHugeSectorMaxRenderDistSqr ) + { + if ( TheCamera.IsSphereVisible(CVector(vecExtraHugeSectorCentre.x, vecExtraHugeSectorCentre.y, 0.0f), SectorRadius(EXTRAHUGE_SECTOR_SIZE)) ) + { + RenderOneFlatExtraHugeWaterPoly( + vecExtraHugeSectorCentre.x - EXTRAHUGE_SECTOR_SIZE/2, + vecExtraHugeSectorCentre.y - EXTRAHUGE_SECTOR_SIZE/2, + 0.0f, + color); + } + } + } + + if ( !bUseCamEndX ) + { + CVector2D vecExtraHugeSectorCentre(-(fX2 + EXTRAHUGE_SECTOR_SIZE/2), fY + EXTRAHUGE_SECTOR_SIZE/2); + + float fCamDistToSector = (vecExtraHugeSectorCentre - camPos).Magnitude(); + + if ( fCamDistToSector < fHugeSectorMaxRenderDistSqr ) + { + if ( TheCamera.IsSphereVisible(CVector(vecExtraHugeSectorCentre.x, vecExtraHugeSectorCentre.x, 0.0f), SectorRadius(EXTRAHUGE_SECTOR_SIZE)) ) + { + RenderOneFlatExtraHugeWaterPoly( + vecExtraHugeSectorCentre.x - EXTRAHUGE_SECTOR_SIZE/2, + vecExtraHugeSectorCentre.y - EXTRAHUGE_SECTOR_SIZE/2, + 0.0f, + color); + } + } + } + } + } + + RenderAndEmptyRenderBuffer(); + + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); + + if ( WavesCalculatedThisFrame ) + { + RenderSeaBirds(); + RenderShipsOnHorizon(); + CParticle::HandleShipsAtHorizonStuff(); + HandleBeachToysStuff(); + } + + if ( _bSeaLife ) + HandleSeaLifeForms(); + + DefinedState(); +} + + +void +CWaterLevel::RenderTransparentWater(void) +{ + bool bUseCamEndX = false; + bool bUseCamStartY = false; + + bool bUseCamStartX = false; + bool bUseCamEndY = false; + + _bSeaLife = false; + + if ( !CGame::CanSeeWaterFromCurrArea() ) + return; + + PUSH_RENDERGROUP("CWaterLevel::RenderTransparentWater"); + + float fWaterDrawDist = _GetWavyDrawDist(); + float fWaterDrawDistLarge = fWaterDrawDist + 90.0f; + float fWavySectorMaxRenderDistSqr = SQR(fWaterDrawDist); + + _GetCamBounds(&bUseCamStartY, &bUseCamEndY, &bUseCamStartX, &bUseCamEndX); + + float fHugeSectorMaxRenderDist = _GetWaterDrawDist(); + float fHugeSectorMaxRenderDistSqr = SQR(fHugeSectorMaxRenderDist); + + RenderBoatWakes(); + + RwRGBA color; + + color.red = CTimeCycle::GetWaterRed(); + color.green = CTimeCycle::GetWaterGreen(); + color.blue = CTimeCycle::GetWaterBlue(); + color.alpha = 255; + + RwRGBA colorTrans; + + colorTrans.red = CTimeCycle::GetWaterRed(); + colorTrans.green = CTimeCycle::GetWaterGreen(); + colorTrans.blue = CTimeCycle::GetWaterBlue(); + colorTrans.alpha = CTimeCycle::GetWaterAlpha(); + + TempBufferVerticesStored = 0; + TempBufferIndicesStored = 0; + +#ifndef PC_WATER + WavesCalculatedThisFrame = false; +#endif + + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)gpWaterRaster); +#ifndef PC_WATER + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); +#endif + + CVector2D camPos(TheCamera.GetPosition().x, TheCamera.GetPosition().y); + + int32 nStartX = WATER_TO_HUGE_SECTOR_X(camPos.x - fHugeSectorMaxRenderDist + WATER_X_OFFSET); + int32 nEndX = WATER_TO_HUGE_SECTOR_X(camPos.x + fHugeSectorMaxRenderDist + WATER_X_OFFSET) + 1; + int32 nStartY = WATER_TO_HUGE_SECTOR_Y(camPos.y - fHugeSectorMaxRenderDist ); + int32 nEndY = WATER_TO_HUGE_SECTOR_Y(camPos.y + fHugeSectorMaxRenderDist ) + 1; + + if ( bUseCamStartX ) + nStartX = WATER_TO_HUGE_SECTOR_X(camPos.x + WATER_X_OFFSET); + if ( bUseCamEndX ) + nEndX = WATER_TO_HUGE_SECTOR_X(camPos.x + WATER_X_OFFSET); + if ( bUseCamStartY ) + nStartY = WATER_TO_HUGE_SECTOR_Y(camPos.y ); + if ( bUseCamEndY ) + nEndY = WATER_TO_HUGE_SECTOR_Y(camPos.y ); + + nStartX = Clamp(nStartX, 0, MAX_HUGE_SECTORS - 1); + nEndX = Clamp(nEndX, 0, MAX_HUGE_SECTORS - 1); + nStartY = Clamp(nStartY, 0, MAX_HUGE_SECTORS - 1); + nEndY = Clamp(nEndY, 0, MAX_HUGE_SECTORS - 1); + + + for ( int32 x = nStartX; x <= nEndX; x++ ) + { + for ( int32 y = nStartY; y <= nEndY; y++ ) + { + if ( aWaterBlockList[2*x+0][2*y+0] >= 0 + || aWaterBlockList[2*x+1][2*y+0] >= 0 + || aWaterBlockList[2*x+0][2*y+1] >= 0 + || aWaterBlockList[2*x+1][2*y+1] >= 0 ) + { + float fX = WATER_FROM_HUGE_SECTOR_X(x) - WATER_X_OFFSET; + float fY = WATER_FROM_HUGE_SECTOR_Y(y); + + CVector2D vecHugeSectorCentre + ( + fX + HUGE_SECTOR_SIZE/2, + fY + HUGE_SECTOR_SIZE/2 + ); + + float fHugeSectorDistToCamSqr = (camPos - vecHugeSectorCentre).MagnitudeSqr(); + + if ( fHugeSectorMaxRenderDistSqr > fHugeSectorDistToCamSqr ) + { + if ( TheCamera.IsSphereVisible(CVector(vecHugeSectorCentre.x, vecHugeSectorCentre.y, 0.0f), SectorRadius(HUGE_SECTOR_SIZE)) ) + { + if ( fHugeSectorDistToCamSqr >= SQR(500.0f) ) + { + // see RenderWater() + ; + } + else + { + for ( int32 x2 = 2*x; x2 <= 2*x+1; x2++ ) + { + for ( int32 y2 = 2*y; y2 <= 2*y+1; y2++ ) + { + if ( aWaterBlockList[x2][y2] >= 0 ) + { + float fLargeX = WATER_FROM_LARGE_SECTOR_X(x2) - WATER_X_OFFSET; + float fLargeY = WATER_FROM_LARGE_SECTOR_Y(y2); + + CVector2D vecLargeSectorCentre(fLargeX + LARGE_SECTOR_SIZE/2, fLargeY + LARGE_SECTOR_SIZE/2); + + float fLargeSectorDistToCamSqr = (camPos - vecLargeSectorCentre).MagnitudeSqr(); + + if ( fLargeSectorDistToCamSqr < fHugeSectorMaxRenderDistSqr ) + { + if ( TheCamera.IsSphereVisible(CVector(vecLargeSectorCentre.x, vecLargeSectorCentre.y, 0.0f), SectorRadius(LARGE_SECTOR_SIZE)) ) + { + // Render four small(32x32) sectors, or one large(64x64). + + // + // [N] + // --------- + // |0x1|1x1| + // [W] --------- [E] + // |0x0|1x0| + // --------- + // [S] + // + + float fLargeSectorDrawDistSqr = SQR((fWaterDrawDistLarge + 16.0f)); + + if ( fLargeSectorDistToCamSqr < fLargeSectorDrawDistSqr ) + { + _bSeaLife = true; + + float fZ; + + // WS + if ( aWaterFineBlockList[2*x2+0][2*y2+0] >= 0 ) + { + float fSmallX = fLargeX; + float fSmallY = fLargeY; + + CVector2D vecSmallSectorCentre(fSmallX + SMALL_SECTOR_SIZE/2, fSmallY + SMALL_SECTOR_SIZE/2); + + float fSmallSectorDistToCamSqr = (camPos - vecSmallSectorCentre).MagnitudeSqr(); + fZ = ms_aWaterZs[ aWaterFineBlockList[2*x2+0][2*y2+0] ]; + + if ( fSmallSectorDistToCamSqr < fWavySectorMaxRenderDistSqr ) + RenderOneWavySector(fSmallX, fSmallY, fZ, colorTrans); + else + RenderOneFlatSmallWaterPolyBlended(fSmallX, fSmallY, fZ, camPos.x, camPos.y, color, colorTrans, fWaterDrawDist); + } + + // SE + if ( aWaterFineBlockList[2*x2+1][2*y2+0] >= 0 ) + { + float fSmallX = fLargeX + (LARGE_SECTOR_SIZE/2); + float fSmallY = fLargeY; + + CVector2D vecSmallSectorCentre(fSmallX + SMALL_SECTOR_SIZE/2, fSmallY + SMALL_SECTOR_SIZE/2); + + float fSmallSectorDistToCamSqr = (camPos - vecSmallSectorCentre).MagnitudeSqr(); + fZ = ms_aWaterZs[ aWaterFineBlockList[2*x2+1][2*y2+0] ]; + + if ( fSmallSectorDistToCamSqr < fWavySectorMaxRenderDistSqr ) + RenderOneWavySector(fSmallX, fSmallY, fZ, colorTrans); + else + RenderOneFlatSmallWaterPolyBlended(fSmallX, fSmallY, fZ, camPos.x, camPos.y, color, colorTrans, fWaterDrawDist); + } + + // WN + if ( aWaterFineBlockList[2*x2+0][2*y2+1] >= 0 ) + { + float fSmallX = fLargeX; + float fSmallY = fLargeY + (LARGE_SECTOR_SIZE/2); + + CVector2D vecSmallSectorCentre(fSmallX + SMALL_SECTOR_SIZE/2,fSmallY + SMALL_SECTOR_SIZE/2); + + float fSmallSectorDistToCamSqr = (camPos - vecSmallSectorCentre).MagnitudeSqr(); + fZ = ms_aWaterZs[ aWaterFineBlockList[2*x2+0][2*y2+1] ]; + + if ( fSmallSectorDistToCamSqr < fWavySectorMaxRenderDistSqr ) + RenderOneWavySector(fSmallX, fSmallY, fZ, colorTrans); + else + RenderOneFlatSmallWaterPolyBlended(fSmallX, fSmallY, fZ, camPos.x, camPos.y, color, colorTrans, fWaterDrawDist); + } + + //NE + if ( aWaterFineBlockList[2*x2+1][2*y2+1] >= 0 ) + { + float fSmallX = fLargeX + (LARGE_SECTOR_SIZE/2); + float fSmallY = fLargeY + (LARGE_SECTOR_SIZE/2); + + CVector2D vecSmallSectorCentre(fSmallX + SMALL_SECTOR_SIZE/2, fSmallY + SMALL_SECTOR_SIZE/2); + + float fSmallSectorDistToCamSqr = (camPos - vecSmallSectorCentre).MagnitudeSqr(); + fZ = ms_aWaterZs[ aWaterFineBlockList[2*x2+1][2*y2+1] ]; + + if ( fSmallSectorDistToCamSqr < fWavySectorMaxRenderDistSqr ) + RenderOneWavySector(fSmallX, fSmallY, fZ, colorTrans); + else + RenderOneFlatSmallWaterPolyBlended(fSmallX, fSmallY, fZ, camPos.x, camPos.y, color, colorTrans, fWaterDrawDist); + } + } + else + { + float fZ; + + fZ = ms_aWaterZs[ aWaterBlockList[x2][y2] ]; + + RenderOneFlatLargeWaterPoly(fLargeX, fLargeY, fZ, color); + } + } + } + } + } + } + } + } + } + } + } + } + + RenderAndEmptyRenderBuffer(); + +#ifdef PC_WATER + if ( MaskCalculatedThisFrame + && (m_nRenderWaterLayers == 0 || m_nRenderWaterLayers == 2 || m_nRenderWaterLayers == 3) ) + { + RwV3d pos = { 0.0f, 0.0f, 0.0f }; + + pos.x = PreCalculatedMaskPosn.x; + pos.y = PreCalculatedMaskPosn.y; + pos.z = PreCalculatedMaskPosn.z; + + RpMatFXMaterialSetEnvMapFrame(RpGeometryGetMaterial(RpAtomicGetGeometry(ms_pMaskAtomic), 0), + RwCameraGetFrame(RwCameraGetCurrentCamera())); + + RwFrameTranslate(RpAtomicGetFrame(ms_pMaskAtomic), &pos, rwCOMBINEREPLACE); + + RpAtomicRender(ms_pMaskAtomic); + } +#else + if (!CCullZones::WaterFudge()) + { + int32 signX = 0; + int32 signY = 0; + + float fCamX = camPos.x - SMALL_SECTOR_SIZE; + float fCamY = camPos.y - SMALL_SECTOR_SIZE; + + if (TheCamera.GetForward().x > 0.3f) + signX = 1; + else if (TheCamera.GetForward().x < -0.3f) + signX = -1; + + fCamX += 0.3f * (float)signX * float(SMALL_SECTOR_SIZE * 2.0f); // 19.2f + + if (TheCamera.GetForward().y > 0.3f) + signY = 1; + else if (TheCamera.GetForward().y < -0.3f) + signY = -1; + + fCamY += 0.3f * (float)signY * float(SMALL_SECTOR_SIZE * 2.0f); // 19.2f + + int32 nBlock; + + int32 BlockX = WATER_TO_SMALL_SECTOR_X(fCamX + WATER_X_OFFSET) + 1; + int32 BlockY = WATER_TO_SMALL_SECTOR_Y(fCamY) + 1; + + if (_IsColideWithBlock(BlockX, BlockY, nBlock)) + { + if (m_nRenderWaterLayers != 1 && m_nRenderWaterLayers != 6) + { + float fMaskX = Floor(fCamX / 2.0f) * 2.0f; + float fMaskY = Floor(fCamY / 2.0f) * 2.0f; + float fWaterZ = CWaterLevel::ms_aWaterZs[nBlock]; + float fSectorX = WATER_FROM_SMALL_SECTOR_X(BlockX) - WATER_X_OFFSET; + float fSectorY = WATER_FROM_SMALL_SECTOR_Y(BlockY); + + RenderWavyMask(fMaskX, fMaskY, fWaterZ, + fSectorX, fSectorY, + signX, signY, colorTrans); + } + } + } + + DefinedState(); +#endif + + POP_RENDERGROUP(); +} + +void CWaterLevel::RenderOneFlatSmallWaterPoly(float fX, float fY, float fZ, RwRGBA const &color) +{ + if ( TempBufferIndicesStored >= TEMPBUFFERINDEXSIZE-6 || TempBufferVerticesStored >= TEMPBUFFERVERTSIZE-4 ) + RenderAndEmptyRenderBuffer(); + + int32 vidx = TempBufferVerticesStored; + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 0], fX, fY, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDU); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDV); + RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 0], color); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 1], fX, fY + SMALL_SECTOR_SIZE, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDU); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDV + 1.0f); + RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 1], color); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 2], fX + SMALL_SECTOR_SIZE, fY + SMALL_SECTOR_SIZE, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDU + 1.0f); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDV + 1.0f); + RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 2], color); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 3], fX + SMALL_SECTOR_SIZE, fY, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDU + 1.0f); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDV); + RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 3], color); + + + int32 iidx = TempBufferIndicesStored; + + TempBufferRenderIndexList[iidx + 0] = TempBufferVerticesStored + 0; + TempBufferRenderIndexList[iidx + 1] = TempBufferVerticesStored + 2; + TempBufferRenderIndexList[iidx + 2] = TempBufferVerticesStored + 1; + TempBufferRenderIndexList[iidx + 3] = TempBufferVerticesStored + 0; + TempBufferRenderIndexList[iidx + 4] = TempBufferVerticesStored + 3; + TempBufferRenderIndexList[iidx + 5] = TempBufferVerticesStored + 2; + + TempBufferVerticesStored += 4; + TempBufferIndicesStored += 6; +} + +void +CWaterLevel::RenderOneFlatLargeWaterPoly(float fX, float fY, float fZ, RwRGBA const &color) +{ + if ( TempBufferIndicesStored >= TEMPBUFFERINDEXSIZE-6 || TempBufferVerticesStored >= TEMPBUFFERVERTSIZE-4 ) + RenderAndEmptyRenderBuffer(); + + int32 vidx = TempBufferVerticesStored; + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 0], fX, fY, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDU); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDV); + RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 0], color); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 1], fX, fY + LARGE_SECTOR_SIZE, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDU); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDV + 2.0f); + RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 1], color); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 2], fX + LARGE_SECTOR_SIZE, fY + LARGE_SECTOR_SIZE, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDU + 2.0f); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDV + 2.0f); + RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 2], color); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 3], fX + LARGE_SECTOR_SIZE, fY, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDU + 2.0f); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDV); + RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 3], color); + + + int32 iidx = TempBufferIndicesStored; + + TempBufferRenderIndexList[iidx + 0] = TempBufferVerticesStored + 0; + TempBufferRenderIndexList[iidx + 1] = TempBufferVerticesStored + 2; + TempBufferRenderIndexList[iidx + 2] = TempBufferVerticesStored + 1; + TempBufferRenderIndexList[iidx + 3] = TempBufferVerticesStored + 0; + TempBufferRenderIndexList[iidx + 4] = TempBufferVerticesStored + 3; + TempBufferRenderIndexList[iidx + 5] = TempBufferVerticesStored + 2; + + TempBufferVerticesStored += 4; + TempBufferIndicesStored += 6; +} + +void +CWaterLevel::RenderOneFlatHugeWaterPoly(float fX, float fY, float fZ, RwRGBA const &color) +{ + if ( TempBufferIndicesStored >= TEMPBUFFERINDEXSIZE-6 || TempBufferVerticesStored >= TEMPBUFFERVERTSIZE-4 ) + RenderAndEmptyRenderBuffer(); + + int32 vidx = TempBufferVerticesStored; + + RwRGBA c; + + c.red = color.red; + c.green = color.green; + c.blue = color.blue; + c.alpha = 255; + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 0], fX, fY, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDU); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDV); + RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 0], c); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 1], fX, fY + HUGE_SECTOR_SIZE, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDU); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDV + 4.0f); + RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 1], c); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 2], fX + HUGE_SECTOR_SIZE, fY + HUGE_SECTOR_SIZE, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDU + 4.0f); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDV + 4.0f); + RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 2], c); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 3], fX + HUGE_SECTOR_SIZE, fY, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDU + 4.0f); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDV); + RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 3], c); + + + int32 iidx = TempBufferIndicesStored; + + TempBufferRenderIndexList[iidx + 0] = TempBufferVerticesStored + 0; + TempBufferRenderIndexList[iidx + 1] = TempBufferVerticesStored + 2; + TempBufferRenderIndexList[iidx + 2] = TempBufferVerticesStored + 1; + TempBufferRenderIndexList[iidx + 3] = TempBufferVerticesStored + 0; + TempBufferRenderIndexList[iidx + 4] = TempBufferVerticesStored + 3; + TempBufferRenderIndexList[iidx + 5] = TempBufferVerticesStored + 2; + + TempBufferVerticesStored += 4; + TempBufferIndicesStored += 6; +} + +void +CWaterLevel::RenderOneFlatExtraHugeWaterPoly(float fX, float fY, float fZ, RwRGBA const &color) +{ + if ( TempBufferIndicesStored >= TEMPBUFFERINDEXSIZE-6 || TempBufferVerticesStored >= TEMPBUFFERVERTSIZE-4 ) + RenderAndEmptyRenderBuffer(); + + int32 vidx = TempBufferVerticesStored; + + RwRGBA c; + + c.red = color.red; + c.green = color.green; + c.blue = color.blue; + c.alpha = 255; + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 0], fX, fY, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDU); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDV); + RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 0], c); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 1], fX, fY + EXTRAHUGE_SECTOR_SIZE, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDU); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDV + 8.0f); + RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 1], c); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 2], fX + EXTRAHUGE_SECTOR_SIZE, fY + EXTRAHUGE_SECTOR_SIZE, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDU + 8.0f); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDV + 8.0f); + RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 2], c); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 3], fX + EXTRAHUGE_SECTOR_SIZE, fY, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDU + 8.0f); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDV); + RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 3], c); + + + int32 iidx = TempBufferIndicesStored; + + TempBufferRenderIndexList[iidx + 0] = TempBufferVerticesStored + 0; + TempBufferRenderIndexList[iidx + 1] = TempBufferVerticesStored + 2; + TempBufferRenderIndexList[iidx + 2] = TempBufferVerticesStored + 1; + TempBufferRenderIndexList[iidx + 3] = TempBufferVerticesStored + 0; + TempBufferRenderIndexList[iidx + 4] = TempBufferVerticesStored + 3; + TempBufferRenderIndexList[iidx + 5] = TempBufferVerticesStored + 2; + + TempBufferVerticesStored += 4; + TempBufferIndicesStored += 6; +} + +void +CWaterLevel::RenderOneWavySector(float fX, float fY, float fZ, RwRGBA const &color, bool bDontRender) +{ + CVector vecSectorPos(fX + (SMALL_SECTOR_SIZE/2), fY + (SMALL_SECTOR_SIZE/2), fZ + 2.0f); + + if ( COcclusion::IsAABoxOccluded(vecSectorPos, SMALL_SECTOR_SIZE, SMALL_SECTOR_SIZE, 4.0f) ) + return; + +#ifdef PC_WATER + RequireWavySector = true; +#else + if (!WavesCalculatedThisFrame) + { + WavesCalculatedThisFrame = true; + + float fAngle = (CTimer::GetTimeInMilliseconds() & 4095) * (TWOPI / 4096.0f); + + RpGeometry *wavyGeometry = RpAtomicGetGeometry(ms_pWavyAtomic); + RwTexCoords *wavyTexCoords = RpGeometryGetVertexTexCoords(wavyGeometry, rwTEXTURECOORDINATEINDEX0); + RpMorphTarget *wavyMorph = RpGeometryGetMorphTarget(wavyGeometry, 0); + RwRGBA *wavyPreLight = RpGeometryGetPreLightColors(wavyGeometry); + RwV3d *wavyMorphVerts = RpMorphTargetGetVertices(wavyMorph); + RwV3d *wavyMorphNormals = RpMorphTargetGetVertexNormals(wavyMorph); + + RpGeometryLock(wavyGeometry, rpGEOMETRYLOCKVERTICES | rpGEOMETRYLOCKNORMALS | rpGEOMETRYLOCKPRELIGHT | rpGEOMETRYLOCKTEXCOORDS); + + RwMatrix *camMat = RwFrameGetLTM(RwCameraGetFrame(RwCameraGetCurrentCamera())); //or curWorld + + float randomDampInv2 = (1.0f - fRandomDamp) * 2.0f; + + float move = 1.0f / 16.0f; + float randomMove = 1.0f / (16.0f * fRandomMoveDiv); + + float vertMul = 0.5f; + + float wind = CWeather::WindClipped * 0.4f + 0.2f; + float waveWind = CWeather::WindClipped * fWave2Ampl + 0.05f; + + float waveA = (TWOPI / 16.0f) + * ((fNormalDirectionScalar1 * Abs(camMat->at.x + camMat->at.y) + fNormMult) * (CWeather::WindClipped * 0.4f + 0.2f)); + + float waveB = TWOPI / (16.0f * fWave2NormScale) + * ((fNormalDirectionScalar2 * Abs(camMat->at.y - camMat->at.x) + fNormMultB) * (CWeather::WindClipped * 0.2f + 0.1f)); + + CVector vA(1.0f, 0.0f, 0.0f); + CVector vB(0.0f, 1.0f, 0.0f); + + for ( int32 i = 0; i < 17; i++ ) + { + for ( int32 j = 0; j < 17; j++ ) + { + wavyTexCoords->u = float(i) * move + TEXTURE_ADDV; + wavyTexCoords->v = float(j) * move + TEXTURE_ADDU; + + RwRGBAAssign(wavyPreLight, &color); + + if (i > 0 && i < 16 && j > 0 && j < 16) + { + wavyMorphVerts->x += CGeneral::GetRandomNumberInRange(-1.0f, 1.0f) * randomMove; + wavyMorphVerts->x *= fRandomDamp; + wavyMorphVerts->x += float(i) * randomDampInv2; + + wavyMorphVerts->y += CGeneral::GetRandomNumberInRange(-1.0f, 1.0f) * randomMove; + wavyMorphVerts->y *= fRandomDamp; + wavyMorphVerts->y += float(j) * randomDampInv2; + } + + float morphVertXHalf = (i == 16) ? 0.0f : vertMul * wavyMorphVerts->x; + float morphVertYHalf = (j == 16) ? 0.0f : vertMul * wavyMorphVerts->y; + + float waveMulA = (morphVertYHalf + morphVertXHalf) * (TWOPI / 16.0f) + fAngle; + float waveMulB = (morphVertYHalf - morphVertXHalf) * (TWOPI / (16.0f * fWave2InvLength)) + fAngle; + + wavyMorphVerts->z = wind * Sin(waveMulA) + waveWind * Sin(waveMulB); + + vA.z = (waveA * Cos(waveMulA)) - (waveB * Cos(waveMulB)); + vB.z = (waveA * Cos(waveMulA)) + (waveB * Cos(waveMulB)); + + CVector norm = CrossProduct(vA, vB); + norm.Normalise(); + + wavyMorphNormals->x = norm.x; + wavyMorphNormals->y = norm.y; + wavyMorphNormals->z = norm.z; + + ++wavyPreLight; + ++wavyTexCoords; + + ++wavyMorphVerts; + ++wavyMorphNormals; + } + } + + RpGeometryUnlock(wavyGeometry); + } + + float fCentreX = fX + (SMALL_SECTOR_SIZE / 2); + float fCentreY = fY + (SMALL_SECTOR_SIZE / 2); +#endif + +#ifdef PC_WATER + if ( WavesCalculatedThisFrame ) +#endif + { + if (bDontRender == false + && m_nRenderWaterLayers != 2 + && m_nRenderWaterLayers != 4 + && m_nRenderWaterLayers != 6 ) + { + RwV3d pos = { 0.0f, 0.0f, 0.0f }; + + pos.x = fX; + pos.y = fY; + pos.z = fZ; + + RwFrameTranslate(RpAtomicGetFrame(ms_pWavyAtomic), &pos, rwCOMBINEREPLACE); + + RpAtomicRender(ms_pWavyAtomic); + } + } +} + +int16 +_RoundValue(int32 v) +{ + int16 result = v; + + while ( result < 0 ) result += 16; + while ( result > 16 ) result -= 16; + + return result; +} + +void +CWaterLevel::RenderWavyMask(float fX, float fY, float fZ, + float fSectorX, float fSectorY, +#ifdef PC_WATER + float fCamPosX, float fCamPosY, + float fCamDirX, float fCamDirY, RwRGBA const&color) +#else + int32 nCamDirX, int32 nCamDirY, RwRGBA const&color) +#endif +{ +#ifndef PC_WATER + bool bRender = true; + if (m_nRenderWaterLayers != 0 && m_nRenderWaterLayers != 2 && m_nRenderWaterLayers != 3) + bRender = false; +#endif + CVector vecSectorPos(fX + (LARGE_SECTOR_SIZE/2), fY + (LARGE_SECTOR_SIZE/2), fZ + 2.0f); + + if ( COcclusion::IsAABoxOccluded(vecSectorPos, LARGE_SECTOR_SIZE, LARGE_SECTOR_SIZE, 4.0f) ) + return; + +#ifndef PC_WATER + float fUOffset = fX - (MAX_LARGE_SECTORS * (int32)Floor(fX / MAX_LARGE_SECTORS)); + float fVOffset = fY - (MAX_LARGE_SECTORS * (int32)Floor(fY / MAX_LARGE_SECTORS)); + + int32 nSecsX = (int32)((fX - fSectorX) / 2.0f); + int32 nSecsY = (int32)((fY - fSectorY) / 2.0f); +#endif + + RpGeometry *wavyGeometry = RpAtomicGetGeometry(ms_pWavyAtomic); + RpMorphTarget *wavyMorph = RpGeometryGetMorphTarget(wavyGeometry, 0); + RwV3d *wavyMorphVerts = RpMorphTargetGetVertices(wavyMorph); + RwV3d *wavyMorphNormals = RpMorphTargetGetVertexNormals(wavyMorph); + + RpGeometry *maskGeometry = RpAtomicGetGeometry(ms_pMaskAtomic); + RwTexCoords *maskTexCoords = RpGeometryGetVertexTexCoords(maskGeometry, rwTEXTURECOORDINATEINDEX0); + RwRGBA *maskPreLight = RpGeometryGetPreLightColors(maskGeometry); + RpMorphTarget *maskMorph = RpGeometryGetMorphTarget(maskGeometry, 0); + RwV3d *maskMorphVerts = RpMorphTargetGetVertices(maskMorph); + RwV3d *maskMorphNormals = RpMorphTargetGetVertexNormals(maskMorph); + + RpGeometryLock(maskGeometry, rpGEOMETRYLOCKVERTICES|rpGEOMETRYLOCKNORMALS|rpGEOMETRYLOCKPRELIGHT|rpGEOMETRYLOCKTEXCOORDS); + +#ifndef PC_WATER + RpMaterial *maskMat = RpGeometryGetMaterial(maskGeometry, 0); + RpMatFXMaterialSetEnvMapFrame(maskMat, RwCameraGetFrame(RwCameraGetCurrentCamera())); + RpMatFXMaterialSetEnvMapCoefficient(maskMat, fEnvScale); + RpMatFXMaterialSetEnvMapFrameBufferAlpha(maskMat, TRUE); +#endif + +#ifndef PC_WATER + float fMinSparkZ = (CWeather::WindClipped * fWave2Ampl + 0.05f + + CWeather::WindClipped * 0.4f + 0.2) * (1.0f - 0.04f * CWeather::SunGlare); + + int32 randval = CGeneral::GetRandomNumber(); + + float fUVStep = 0.125f; + float f27 = 2.0f; + + float fMinU = (fUOffset / 16.0f) + _TEXTURE_MASK_ADDU; + float fMinV = (fVOffset / 16.0f) + _TEXTURE_MASK_ADDV; + + float fAlphaMul = ((float)color.alpha * 0.4f) / 16.0f; + + float fXOffset = 16.0f; + if (nCamDirX > 0) + fXOffset = 6.4f; + else if (nCamDirX < 0) + fXOffset = 25.6f; + + float fYOffset = 16.0f; + if (nCamDirY > 0) + fYOffset = 6.4f; + else if (nCamDirY < 0) + fYOffset = 25.6f; + + int16 nX = _RoundValue(nSecsX - 1); + int16 nY = _RoundValue(nSecsY - 1); +#else + float fMinSparkZ = (fWave2Ampl * CWeather::WindClipped + 0.05f + + 0.4f * CWeather::WindClipped + 0.2) * (1.0f - 0.02f * CWeather::SunGlare); + + int32 randval = CGeneral::GetRandomNumber() & 255; + + int16 nX = _RoundValue((int32)((fX - fSectorX) * 0.5f) - 1); + int16 nY = _RoundValue((int32)((fY - fSectorY) * 0.5f) - 1); +#endif + int16 idxX = nX; + + for ( int32 i = 0; i < 17; i++ ) + { + int16 idxY = nY; + + if ( ++idxX > 16 ) + idxX -= 16; + + for ( int32 j = 0; j < 17; j++ ) + { + if ( ++idxY > 16 ) + idxY -= 16; + + const int32 a = (0*16); + const int32 b = (1*16); + const int32 c = (33*16); + const int32 d = (34*16); + + int32 base = (i*33+j); + +#ifndef PC_WATER + maskTexCoords[base + a].u = fMinU + ((float)i * fUVStep); + maskTexCoords[base + a].v = fMinV + ((float)j * fUVStep); + + maskTexCoords[base + b].u = maskTexCoords[base + a].u; + maskTexCoords[base + b].v = maskTexCoords[base + a].v + (16.0f * fUVStep); + + maskTexCoords[base + c].u = maskTexCoords[base + a].u + (16.0f * fUVStep); + maskTexCoords[base + c].v = maskTexCoords[base + a].v; + + maskTexCoords[base + d].u = maskTexCoords[base + a].u + (16.0f * fUVStep); + maskTexCoords[base + d].v = maskTexCoords[base + a].v + (16.0f * fUVStep); +#else + maskTexCoords[base+a].v = float(j) / SMALL_SECTOR_SIZE + ((fCamPosY - fY) / 64); + maskTexCoords[base+c].v = maskTexCoords[base+a].v; + maskTexCoords[base+d].v = maskTexCoords[base+a].v + 0.5f; + maskTexCoords[base+b].v = maskTexCoords[base+d].v; + + maskTexCoords[base+a].u = float(i) / SMALL_SECTOR_SIZE + ((fCamPosX - fX) / 64); + maskTexCoords[base+b].u = maskTexCoords[base+a].u; + maskTexCoords[base+d].u = maskTexCoords[base+a].u + 0.5f; + maskTexCoords[base+c].u = maskTexCoords[base+d].u; +#endif + + maskMorphVerts[base+a].x = (wavyMorphVerts[idxY + (17 * idxX)].x - (float)idxX * 2.0f) + (float(i) * 2.0f); + maskMorphVerts[base+b].x = maskMorphVerts[base+a].x; + maskMorphVerts[base+c].x = maskMorphVerts[base+a].x + SMALL_SECTOR_SIZE; + maskMorphVerts[base+d].x = maskMorphVerts[base+c].x; + + maskMorphVerts[base+a].y = (wavyMorphVerts[idxY + (17 * idxX)].y - (float)idxY * 2.0f) + (float(j) * 2.0f); + maskMorphVerts[base+c].y = maskMorphVerts[base+a].y; + maskMorphVerts[base+b].y = maskMorphVerts[base+a].y + SMALL_SECTOR_SIZE; + maskMorphVerts[base+d].y = maskMorphVerts[base+b].y; + + maskMorphVerts[base+a].z = wavyMorphVerts[idxY + (17 * idxX)].z; + maskMorphVerts[base+d].z = maskMorphVerts[base+a].z; + maskMorphVerts[base+c].z = maskMorphVerts[base+d].z; + maskMorphVerts[base+b].z = maskMorphVerts[base+c].z; + +#ifndef PC_WATER + if (maskMorphVerts[base].z >= fMinSparkZ) +#else + if ( maskMorphVerts[base].z > fMinSparkZ ) +#endif + { + switch ( (i + j + randval) & 3 ) + { + case 0: + { + CVector vecPos + ( + fX + maskMorphVerts[base+a].x, + fY + maskMorphVerts[base+a].y, + fZ + maskMorphVerts[base+a].z + 0.12f + ); + + vecPos -= 0.05f * TheCamera.GetForward(); + + CParticle::AddParticle(PARTICLE_WATER_SPARK, + vecPos, + CVector(0.0f, 0.0f, 0.0f), + nil, + 0.0f, + 15, + CGeneral::GetRandomNumberInRange(-90, 90), + 0, + 0); + } + break; + + case 1: + { + CVector vecPos + ( + fX + maskMorphVerts[base+c].x, + fY + maskMorphVerts[base+c].y, + fZ + maskMorphVerts[base+c].z + 0.12f + ); + + vecPos -= 0.05f * TheCamera.GetForward(); + + CParticle::AddParticle(PARTICLE_WATER_SPARK, + vecPos, + CVector(0.0f, 0.0f, 0.0f), + nil, + 0.0f, + 15, + CGeneral::GetRandomNumberInRange(-90, 90), + 0, + 0); + } + break; + + case 2: + { + CVector vecPos + ( + fX + maskMorphVerts[base+b].x, + fY + maskMorphVerts[base+b].y, + fZ + maskMorphVerts[base+b].z + 0.12f + ); + + vecPos -= 0.05f * TheCamera.GetForward(); + + CParticle::AddParticle(PARTICLE_WATER_SPARK, + vecPos, + CVector(0.0f, 0.0f, 0.0f), + nil, + 0.0f, + 15, + CGeneral::GetRandomNumberInRange(-90, 90), + 0, + 0); + } + break; + + case 3: + { + CVector vecPos + ( + fX + maskMorphVerts[base+d].x, + fY + maskMorphVerts[base+d].y, + fZ + maskMorphVerts[base+d].z + 0.12f + ); + + vecPos -= 0.05f * TheCamera.GetForward(); + + CParticle::AddParticle(PARTICLE_WATER_SPARK, + vecPos, + CVector(0.0f, 0.0f, 0.0f), + nil, + 0.0f, + 15, + CGeneral::GetRandomNumberInRange(-90, 90), + 0, + 0); + } + break; + } + } + + maskMorphNormals[base+a].x = wavyMorphNormals[idxY + (17 * idxX)].x; + maskMorphNormals[base+a].y = wavyMorphNormals[idxY + (17 * idxX)].y; + maskMorphNormals[base+a].z = wavyMorphNormals[idxY + (17 * idxX)].z; + + maskMorphNormals[base+d].x = maskMorphNormals[base+a].x; + maskMorphNormals[base+d].y = maskMorphNormals[base+a].y; + maskMorphNormals[base+d].z = maskMorphNormals[base+a].z; + + maskMorphNormals[base+c].x = maskMorphNormals[base+d].x; + maskMorphNormals[base+c].y = maskMorphNormals[base+d].y; + maskMorphNormals[base+c].z = maskMorphNormals[base+d].z; + + maskMorphNormals[base+b].x = maskMorphNormals[base+c].x; + maskMorphNormals[base+b].y = maskMorphNormals[base+c].y; + maskMorphNormals[base+b].z = maskMorphNormals[base+c].z; + + maskPreLight[base+a].red = color.red; + maskPreLight[base+a].green = color.green; + maskPreLight[base+a].blue = color.blue; + maskPreLight[base+a].alpha = color.alpha; + + maskPreLight[base+d].red = maskPreLight[base+a].red; + maskPreLight[base+d].green = maskPreLight[base+a].green; + maskPreLight[base+d].blue = maskPreLight[base+a].blue; + maskPreLight[base+d].alpha = maskPreLight[base+a].alpha; + + maskPreLight[base+c].red = maskPreLight[base+d].red; + maskPreLight[base+c].green = maskPreLight[base+d].green; + maskPreLight[base+c].blue = maskPreLight[base+d].blue; + maskPreLight[base+c].alpha = maskPreLight[base+d].alpha; + + maskPreLight[base+b].red = maskPreLight[base+c].red; + maskPreLight[base+b].green = maskPreLight[base+c].green; + maskPreLight[base+b].blue = maskPreLight[base+c].blue; + maskPreLight[base+b].alpha = maskPreLight[base+c].alpha; + +#ifndef PC_WATER + maskPreLight[base + a].alpha = Max(0, (int32)((float)color.alpha - (fAlphaMul * (Abs((float)i - fXOffset) + Abs((float)j - fYOffset))))); + maskPreLight[base + b].alpha = Max(0, (int32)((float)color.alpha - (fAlphaMul * (Abs((float)i - fXOffset) + Abs(16.0f + (float)j - fYOffset))))); + maskPreLight[base + c].alpha = Max(0, (int32)((float)color.alpha - (fAlphaMul * (Abs(16.0f + (float)i - fXOffset) + Abs((float)j - fYOffset))))); + maskPreLight[base + d].alpha = Max(0, (int32)((float)color.alpha - (fAlphaMul * (Abs(16.0f + (float)i - fXOffset) + Abs(16.0f + (float)j - fYOffset))))); +#endif + } + } + + RpGeometryUnlock(maskGeometry); + +#ifndef PC_WATER + { + RwV3d pos = { 0.0f, 0.0f, 0.0f }; + + pos.x = fX; + pos.y = fY; + pos.z = fZ + 0.05f; + + RwFrameTranslate(RpAtomicGetFrame(ms_pMaskAtomic), &pos, rwCOMBINEREPLACE); + + if (bRender) + { +#ifdef PS2 + RpSkyTexCacheFlush(); +#endif + RpAtomicRender(ms_pMaskAtomic); + } + } +#endif +} + +#ifdef PC_WATER +void +CWaterLevel::PreCalcWaterGeometry(void) +{ + if ( !RequireWavySector ) + { + WavesCalculatedThisFrame = false; + MaskCalculatedThisFrame = false; + return; + } + + RequireWavySector = false; + WavesCalculatedThisFrame = true; + + RwRGBA color; + + color.red = CTimeCycle::GetWaterRed(); + color.green = CTimeCycle::GetWaterGreen(); + color.blue = CTimeCycle::GetWaterBlue(); + color.alpha = CTimeCycle::GetWaterAlpha(); + + PreCalcWavySector(color); + + if ( CCullZones::WaterFudge() ) + { + MaskCalculatedThisFrame = false; + return; + } + + CVector CamFwdDir = TheCamera.GetForward(); + CamFwdDir.z = 0.0f; + CamFwdDir.Normalise(); + + float fCamX = TheCamera.GetPosition().x - SMALL_SECTOR_SIZE; + float fCamY = TheCamera.GetPosition().y - SMALL_SECTOR_SIZE; + + //1.4144272f; 1.4144f; + float signX = CamFwdDir.x * 1.4144272f; + float signY = CamFwdDir.y * 1.4144272f; + + signX = Clamp(signX, -1.0f, 1.0f); + fCamX += 0.4f * signX * float(SMALL_SECTOR_SIZE * 2.0f); + + signY = Clamp(signY, -1.0f, 1.0f); + fCamY += 0.4f * signY * float(SMALL_SECTOR_SIZE * 2.0f); + + int32 nBlock; + + int32 BlockX = WATER_TO_SMALL_SECTOR_X(fCamX + WATER_X_OFFSET) + 1; + int32 BlockY = WATER_TO_SMALL_SECTOR_Y(fCamY ) + 1; + + ASSERT( BlockX >= 0 && BlockX < MAX_SMALL_SECTORS ); + ASSERT( BlockY >= 0 && BlockY < MAX_SMALL_SECTORS ); + + if ( _IsColideWithBlock(BlockX, BlockY, nBlock) ) + { + float fMaskX = Floor(fCamX / 2.0f) * 2.0f; + float fMaskY = Floor(fCamY / 2.0f) * 2.0f; + + float fSectorX = WATER_FROM_SMALL_SECTOR_X(BlockX) - WATER_X_OFFSET; + float fSectorY = WATER_FROM_SMALL_SECTOR_Y(BlockY); + + if ( PreCalcWavyMask( fMaskX, fMaskY, ms_aWaterZs[nBlock], + fSectorX, fSectorY, fCamX, fCamY, CamFwdDir.x, CamFwdDir.y, color ) ) + { + PreCalculatedMaskPosn.x = fMaskX; + PreCalculatedMaskPosn.y = fMaskY; + PreCalculatedMaskPosn.z = ms_aWaterZs[nBlock] + 0.05f; + + MaskCalculatedThisFrame = true; + } + else + MaskCalculatedThisFrame = false; + } + else + MaskCalculatedThisFrame = false; +} + +bool +CWaterLevel::PreCalcWavySector(RwRGBA const &color) +{ + float fAngle = (CTimer::GetTimeInMilliseconds() & 4095) * (TWOPI / 4096.0f); + + RpGeometry *wavyGeometry = RpAtomicGetGeometry(ms_pWavyAtomic); + + RwTexCoords *wavyTexCoords = RpGeometryGetVertexTexCoords(wavyGeometry, rwTEXTURECOORDINATEINDEX0); + RpMorphTarget *wavyMorph = RpGeometryGetMorphTarget(wavyGeometry, 0); + + RwRGBA *wavyPreLight = RpGeometryGetPreLightColors(wavyGeometry); + RwV3d *wavyMorphVerts = RpMorphTargetGetVertices(wavyMorph); + RwV3d *wavyMorphNormals = RpMorphTargetGetVertexNormals(wavyMorph); + + if ( !m_bRenderSeaBed ) + RpGeometryLock(wavyGeometry, rpGEOMETRYLOCKVERTICES + |rpGEOMETRYLOCKNORMALS + |rpGEOMETRYLOCKPRELIGHT + |rpGEOMETRYLOCKTEXCOORDS); + + CVector camPosUp = TheCamera.GetForward(); + + float randomDampInv2 = (1.0f - fRandomDamp) * 2.0f; + + float randomMove = 1.0f / (16.0f * fRandomMoveDiv); + + float wind = CWeather::WindClipped * 0.4f + 0.2f; + float waveWind = CWeather::WindClipped * fWave2Ampl + 0.05f; + + float waveA = (TWOPI / 16.0f) + * ((CWeather::WindClipped * 0.4f + 0.2f) * (fNormalDirectionScalar1 * Abs(camPosUp.x + camPosUp.y) + fNormMult)); + + float waveB = TWOPI / (16.0f * fWave2NormScale) + * ((CWeather::WindClipped * 0.2f + 0.1f) * (fNormalDirectionScalar2 * Abs(camPosUp.y - camPosUp.x) + fNormMultB)); + + + CVector vA(1.0f, 0.0f, 0.0f); + CVector vB(0.0f, 1.0f, 0.0f); + + for ( int32 i = 0; i < 17; i++ ) + { + for ( int32 j = 0; j < 17; j++ ) + { + wavyTexCoords->u = (float(i) / 16.0f) + TEXTURE_ADDV; + wavyTexCoords->v = (float(j) / 16.0f) + TEXTURE_ADDU; + + RwRGBAAssign(wavyPreLight, &color); + + if ( i > 0 && i < 16 && j > 0 && j < 16 ) + { + wavyMorphVerts->x += CGeneral::GetRandomNumberInRange(-1.0f, 1.0f) * randomMove; + wavyMorphVerts->x *= fRandomDamp; + wavyMorphVerts->x += float(i) * randomDampInv2; + + wavyMorphVerts->y += CGeneral::GetRandomNumberInRange(-1.0f, 1.0f) * randomMove; + wavyMorphVerts->y *= fRandomDamp; + wavyMorphVerts->y += float(j) * randomDampInv2; + } + + float morphVertXHalf = ( i == 16 ) ? 0.0f : 0.5f * wavyMorphVerts->x; + float morphVertYHalf = ( j == 16 ) ? 0.0f : 0.5f * wavyMorphVerts->y; + + float waveMulA = (morphVertYHalf + morphVertXHalf) * (TWOPI / 16.0f) + fAngle; + float waveMulB = (morphVertYHalf - morphVertXHalf) * (TWOPI / (16.0f * fWave2InvLength)) + fAngle; + + wavyMorphVerts->z = wind * Sin(waveMulA) + waveWind * Sin(waveMulB); + + vA.z = (waveA * Cos(waveMulA)) - (waveB * Cos(waveMulB)); + vB.z = (waveA * Cos(waveMulA)) + (waveB * Cos(waveMulB)); + + CVector norm = CrossProduct(vA, vB); + norm.Normalise(); + + wavyMorphNormals->x = norm.x; + wavyMorphNormals->y = norm.y; + wavyMorphNormals->z = norm.z; + + ++wavyPreLight; + ++wavyTexCoords; + + ++wavyMorphVerts; + ++wavyMorphNormals; + } + } + + RpGeometryUnlock(wavyGeometry); + + return true; +} + +bool +CWaterLevel::PreCalcWavyMask(float fX, float fY, float fZ, + float fSectorX, float fSectorY, + float fCamPosX, float fCamPosY, + float fCamDirX, float fCamDirY, + RwRGBA const&color) +{ + CVector vecSectorPos(fX + (MAX_LARGE_SECTORS/2), fY + (MAX_LARGE_SECTORS/2), fZ + 2.0f); + + if ( COcclusion::IsAABoxOccluded(vecSectorPos, MAX_LARGE_SECTORS, MAX_LARGE_SECTORS, 4.0f) ) + return false; + + Floor(fX / MAX_LARGE_SECTORS); + Floor(fY / MAX_LARGE_SECTORS); + + RpGeometry *wavyGeometry = RpAtomicGetGeometry(ms_pWavyAtomic); + RpMorphTarget *wavyMorph = RpGeometryGetMorphTarget(wavyGeometry, 0); + RwV3d *wavyMorphVerts = RpMorphTargetGetVertices(wavyMorph); + RwV3d *wavyMorphNormals = RpMorphTargetGetVertexNormals(wavyMorph); + + RpGeometry *maskGeometry = RpAtomicGetGeometry(ms_pMaskAtomic); + RwTexCoords *maskTexCoords = RpGeometryGetVertexTexCoords(maskGeometry, rwTEXTURECOORDINATEINDEX0); + RwRGBA *maskPreLight = RpGeometryGetPreLightColors(maskGeometry); + RpMorphTarget *maskMorph = RpGeometryGetMorphTarget(maskGeometry, 0); + RwV3d *maskMorphVerts = RpMorphTargetGetVertices(maskMorph); + RwV3d *maskMorphNormals = RpMorphTargetGetVertexNormals(maskMorph); + + if ( !m_bRenderSeaBed ) + RpGeometryLock(maskGeometry, rpGEOMETRYLOCKVERTICES | rpGEOMETRYLOCKNORMALS | rpGEOMETRYLOCKPRELIGHT | rpGEOMETRYLOCKTEXCOORDS); + + + float fMinSparkZ = (fWave2Ampl * CWeather::WindClipped + 0.05f + + 0.4f * CWeather::WindClipped + 0.2) * (1.0f - 0.02f * CWeather::SunGlare); + + int32 randval = CGeneral::GetRandomNumber() & 255; + + int16 nX = _RoundValue((int32)((fX - fSectorX) * 0.5f) - 1); + int16 nY = _RoundValue((int32)((fY - fSectorY) * 0.5f) - 1); + + int16 idxX = nX; + + for ( int32 i = 0; i < 17; i++ ) + { + int16 idxY = nY; + + if ( ++idxX > 16 ) + idxX -= 16; + + for ( int32 j = 0; j < 17; j++ ) + { + if ( ++idxY > 16 ) + idxY -= 16; + + const int32 a = (0*16); + const int32 b = (1*16); + const int32 c = (33*16); + const int32 d = (34*16); + + int32 base = (i*33+j); + + maskTexCoords[base+a].v = float(j) / 32 + ((fCamPosY - fY) / 64); + maskTexCoords[base+c].v = maskTexCoords[base+a].v; + maskTexCoords[base+d].v = maskTexCoords[base+a].v + 0.5f; + maskTexCoords[base+b].v = maskTexCoords[base+d].v; + + maskTexCoords[base+a].u = float(i) / 32 + ((fCamPosX - fX) / 64); + maskTexCoords[base+b].u = maskTexCoords[base+a].u; + maskTexCoords[base+d].u = maskTexCoords[base+a].u + 0.5f; + maskTexCoords[base+c].u = maskTexCoords[base+d].u; + + maskMorphVerts[base+a].x = (wavyMorphVerts[idxY + (17 * idxX)].x - (float)idxX * 2.0f) + (float(i) * 2.0f); + maskMorphVerts[base+b].x = maskMorphVerts[base+a].x; + maskMorphVerts[base+c].x = maskMorphVerts[base+a].x + SMALL_SECTOR_SIZE; + maskMorphVerts[base+d].x = maskMorphVerts[base+c].x; + + maskMorphVerts[base+a].y = (wavyMorphVerts[idxY + (17 * idxX)].y - (float)idxY * 2.0f) + (float(j) * 2.0f); + maskMorphVerts[base+c].y = maskMorphVerts[base+a].y; + maskMorphVerts[base+b].y = maskMorphVerts[base+a].y + SMALL_SECTOR_SIZE; + maskMorphVerts[base+d].y = maskMorphVerts[base+b].y; + + maskMorphVerts[base+a].z = wavyMorphVerts[idxY + (17 * idxX)].z; + maskMorphVerts[base+d].z = maskMorphVerts[base+a].z; + maskMorphVerts[base+c].z = maskMorphVerts[base+d].z; + maskMorphVerts[base+b].z = maskMorphVerts[base+c].z; + + if ( maskMorphVerts[base].z > fMinSparkZ ) + { + switch ( (i + j + randval) & 3 ) + { + case 0: + { + CVector vecPos + ( + fX + maskMorphVerts[base+a].x, + fY + maskMorphVerts[base+a].y, + fZ + maskMorphVerts[base+a].z + 0.12f + ); + + vecPos -= 0.05f * TheCamera.GetForward(); + + CParticle::AddParticle(PARTICLE_WATER_SPARK, + vecPos, + CVector(0.0f, 0.0f, 0.0f), + nil, + 0.0f, + 15, + CGeneral::GetRandomNumberInRange(-90, 90), + 0, + 0); + } + break; + + case 1: + { + CVector vecPos + ( + fX + maskMorphVerts[base+c].x, + fY + maskMorphVerts[base+c].y, + fZ + maskMorphVerts[base+c].z + 0.12f + ); + + vecPos -= 0.05f * TheCamera.GetForward(); + + CParticle::AddParticle(PARTICLE_WATER_SPARK, + vecPos, + CVector(0.0f, 0.0f, 0.0f), + nil, + 0.0f, + 15, + CGeneral::GetRandomNumberInRange(-90, 90), + 0, + 0); + } + break; + + case 2: + { + CVector vecPos + ( + fX + maskMorphVerts[base+b].x, + fY + maskMorphVerts[base+b].y, + fZ + maskMorphVerts[base+b].z + 0.12f + ); + + vecPos -= 0.05f * TheCamera.GetForward(); + + CParticle::AddParticle(PARTICLE_WATER_SPARK, + vecPos, + CVector(0.0f, 0.0f, 0.0f), + nil, + 0.0f, + 15, + CGeneral::GetRandomNumberInRange(-90, 90), + 0, + 0); + } + break; + + case 3: + { + CVector vecPos + ( + fX + maskMorphVerts[base+d].x, + fY + maskMorphVerts[base+d].y, + fZ + maskMorphVerts[base+d].z + 0.12f + ); + + vecPos -= 0.05f * TheCamera.GetForward(); + + CParticle::AddParticle(PARTICLE_WATER_SPARK, + vecPos, + CVector(0.0f, 0.0f, 0.0f), + nil, + 0.0f, + 15, + CGeneral::GetRandomNumberInRange(-90, 90), + 0, + 0); + } + break; + } + } + + maskMorphNormals[base+a].x = wavyMorphNormals[idxY + (17 * idxX)].x; + maskMorphNormals[base+a].y = wavyMorphNormals[idxY + (17 * idxX)].y; + maskMorphNormals[base+a].z = wavyMorphNormals[idxY + (17 * idxX)].z; + + maskMorphNormals[base+d].x = maskMorphNormals[base+a].x; + maskMorphNormals[base+d].y = maskMorphNormals[base+a].y; + maskMorphNormals[base+d].z = maskMorphNormals[base+a].z; + + maskMorphNormals[base+c].x = maskMorphNormals[base+d].x; + maskMorphNormals[base+c].y = maskMorphNormals[base+d].y; + maskMorphNormals[base+c].z = maskMorphNormals[base+d].z; + + maskMorphNormals[base+b].x = maskMorphNormals[base+c].x; + maskMorphNormals[base+b].y = maskMorphNormals[base+c].y; + maskMorphNormals[base+b].z = maskMorphNormals[base+c].z; + + maskPreLight[base+a].red = color.red; + maskPreLight[base+a].green = color.green; + maskPreLight[base+a].blue = color.blue; + maskPreLight[base+a].alpha = color.alpha; + + maskPreLight[base+d].red = maskPreLight[base+a].red; + maskPreLight[base+d].green = maskPreLight[base+a].green; + maskPreLight[base+d].blue = maskPreLight[base+a].blue; + maskPreLight[base+d].alpha = maskPreLight[base+a].alpha; + + maskPreLight[base+c].red = maskPreLight[base+d].red; + maskPreLight[base+c].green = maskPreLight[base+d].green; + maskPreLight[base+c].blue = maskPreLight[base+d].blue; + maskPreLight[base+c].alpha = maskPreLight[base+d].alpha; + + maskPreLight[base+b].red = maskPreLight[base+c].red; + maskPreLight[base+b].green = maskPreLight[base+c].green; + maskPreLight[base+b].blue = maskPreLight[base+c].blue; + maskPreLight[base+b].alpha = maskPreLight[base+c].alpha; + } + } + + RpGeometryUnlock(maskGeometry); + return true; +} +#endif + +void +CWaterLevel::RenderBoatWakes(void) +{ + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)gpWaterWakeRaster); +#ifndef PC_WATER + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); +#endif + +#ifdef _XBOX + // TODO save and restore rwRENDERSTATESRCBLEND rwRENDERSTATEDESTBLEND +#endif + + CBoat::FillBoatList(); + + float fWakeZ = 5.97f; + float fWakeLifeTimeMult = 0.01f / CBoat::WAKE_LIFETIME; + + for ( int32 idx = 0; idx < ARRAY_SIZE(CBoat::apFrameWakeGeneratingBoats); idx++ ) + { + CBoat *pBoat = CBoat::apFrameWakeGeneratingBoats[idx]; + + if ( pBoat == nil ) + break; + + CVector2D vecDistA(pBoat->GetForward().x, pBoat->GetForward().y); + + + float fSize = pBoat->GetColModel()->boundingBox.max.z + * 0.65f; + + if ( pBoat->GetModelIndex() == MI_SKIMMER) + fSize *= 0.4f; + + float fAplhaA = 255.0f; + float fSizeA = fSize; + float fAplhaB; + float fSizeB; + + for ( int32 wake = 1; wake < pBoat->m_nNumWakePoints; wake++ ) + { + bool bRender = true; + + float fTimeleft = CBoat::WAKE_LIFETIME - pBoat->m_afWakePointLifeTime[wake]; + + float fWakeSizeB = ((float)wake * 0.19f) + fSize - fWakeLifeTimeMult * Max(fTimeleft, 0.0f); + + fSizeB = fWakeSizeB / CBoat::MIN_WAKE_INTERVAL; + if ( fSizeB < 0.0f ) + fSizeB = 1.0f; + + if ( wake == pBoat->m_nNumWakePoints - 1 ) + { + // set alpha to 0 if it's last point + fAplhaB = 0.0f; + } + else + { + // clip (-100, 500), less lifetime - less val + float val = 500.0f - (CBoat::WAKE_LIFETIME - pBoat->m_afWakePointLifeTime[wake]) + * 600.0f / CBoat::WAKE_LIFETIME; + + fAplhaB = Clamp(val, 0.0f, 255.0f); + } + + CVector2D vecDistB = pBoat->m_avec2dWakePoints[wake - 1] - pBoat->m_avec2dWakePoints[wake]; + + float fScal = vecDistB.MagnitudeSqr(); + + // normalize if distance between points is greater than 3 + + if ( fScal > SQR(3.0f) ) + { + float fNorm = 1.0f / sqrt(fScal); + + vecDistB.x *= fNorm; + vecDistB.y *= fNorm; + + // disable render if distance between points too big + + if ( sqrt(fScal) > 13.0f ) + bRender = false; + } + + CVector2D vecAA + ( + pBoat->m_avec2dWakePoints[wake - 1].x - (fSizeA * vecDistA.y), + pBoat->m_avec2dWakePoints[wake - 1].y + (fSizeA * vecDistA.x) + ); + CVector2D vecAB + ( + pBoat->m_avec2dWakePoints[wake - 1].x + (fSizeA * vecDistA.y), + pBoat->m_avec2dWakePoints[wake - 1].y - (fSizeA * vecDistA.x) + ); + CVector2D vecBA + ( + pBoat->m_avec2dWakePoints[wake].x + (fSizeB * vecDistB.y), + pBoat->m_avec2dWakePoints[wake].y - (fSizeB * vecDistB.x) + ); + CVector2D vecBB + ( + pBoat->m_avec2dWakePoints[wake].x - (fSizeB * vecDistB.y), + pBoat->m_avec2dWakePoints[wake].y + (fSizeB * vecDistB.x) + ); + + if ( bRender ) + RenderWakeSegment(vecAA, vecAB, vecBA, vecBB, fSizeA, fSizeB, fAplhaA, fAplhaB, fWakeZ); + + vecDistA = vecDistB; + fSizeA = fSizeB; + + fAplhaB = fAplhaA; + } + } + + RenderAndEmptyRenderBuffer(); +} + +inline float +_GetWindedWave(float fX, float fY) +{ + float fAngle = (CTimer::GetTimeInMilliseconds() & 4095) * (TWOPI / 4096.0f); + float x = WATER_HUGE_X(fX + WATER_X_OFFSET); + float y = WATER_HUGE_Y(fY); + + float fWindFactor (CWeather::WindClipped * 0.4f + 0.2f); + float fWave = Sin(( (x - Floor(x)) + (y - Floor(y)) ) * TWOPI + fAngle); + + return fWindFactor * fWave; +} + +void +CWaterLevel::RenderWakeSegment(CVector2D &vecA, CVector2D &vecB, CVector2D &vecC, CVector2D &vecD, + float &fSizeA, float &fSizeB, + float &fAlphaA, float &fAlphaB, + float &fWakeZ) +{ + for ( int32 i = 0; i < 4; i++ ) + { + if ( TempBufferIndicesStored >= TEMPBUFFERINDEXSIZE-6 || TempBufferVerticesStored >= TEMPBUFFERVERTSIZE-4 ) + RenderAndEmptyRenderBuffer(); + + float fCurStep = (float)i / 4; + float fNxtStep = (float)(i + 1) / 4; + + float fLeftCurStep = 1.0f - fCurStep; + float fLeftNxtStep = 1.0f - fNxtStep; + + uint8 AlphaA = (uint32)(fAlphaA * aAlphaFade[i] ); + uint8 AlphaB = (uint32)(fAlphaA * aAlphaFade[i + 1]); + uint8 AlphaC = (uint32)(fAlphaB * aAlphaFade[i + 1]); + uint8 AlphaD = (uint32)(fAlphaB * aAlphaFade[i] ); + + CVector2D PosA = vecB*fCurStep + vecA*fLeftCurStep; + CVector2D PosB = vecB*fNxtStep + vecA*fLeftNxtStep; + CVector2D PosC = vecC*fNxtStep + vecD*fLeftNxtStep; + CVector2D PosD = vecC*fCurStep + vecD*fLeftCurStep; + + float fUA = (PosA.x / 4) + _TEXTURE_WAKE_ADDU; + float fVA = (PosA.y / 4) + _TEXTURE_WAKE_ADDV; + + float fUB = (PosB.x / 4) + _TEXTURE_WAKE_ADDU; + float fVB = (PosB.y / 4) + _TEXTURE_WAKE_ADDV; + + float fUC = (PosC.x / 4) + _TEXTURE_WAKE_ADDU; + float fVC = (PosC.y / 4) + _TEXTURE_WAKE_ADDV; + + float fUD = (PosD.x / 4) + _TEXTURE_WAKE_ADDU; + float fVD = (PosD.y / 4) + _TEXTURE_WAKE_ADDV; + +#define MIN4(a, b, c, d) (Min((a), Min((b), Min((c), (d))))) + float fMinU = Floor(MIN4(fUA, fUB, fUC, fUD)); + float fMinV = Floor(MIN4(fVA, fVB, fVC, fVD)); +#undef MIN4 + + float fZA = _GetWindedWave(PosA.x, PosA.y) + fWakeZ; + float fZB = _GetWindedWave(PosB.x, PosB.y) + fWakeZ; + float fZC = _GetWindedWave(PosC.x, PosC.y) + fWakeZ; + float fZD = _GetWindedWave(PosD.x, PosD.y) + fWakeZ; + + int32 vidx = TempBufferVerticesStored; + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 0], PosA.x, PosA.y, fZA); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 0], fUA - fMinU); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 0], fVA - fMinV); + RwIm3DVertexSetRGBA (&TempBufferRenderVertices[vidx + 0], 255, 255, 255, AlphaA); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 1], PosB.x, PosB.y, fZB); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 1], fUB - fMinU); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 1], fVB - fMinV); + RwIm3DVertexSetRGBA (&TempBufferRenderVertices[vidx + 1], 255, 255, 255, AlphaB); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 2], PosC.x, PosC.y, fZC); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 2], fUC - fMinU); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 2], fVC - fMinV); + RwIm3DVertexSetRGBA (&TempBufferRenderVertices[vidx + 2], 255, 255, 255, AlphaC); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 3], PosD.x, PosD.y, fZD); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 3], fUD - fMinU); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 3], fVD - fMinV); + RwIm3DVertexSetRGBA (&TempBufferRenderVertices[vidx + 3], 255, 255, 255, AlphaD); + + int32 iidx = TempBufferIndicesStored; + + TempBufferRenderIndexList[iidx + 0] = TempBufferVerticesStored + 0; + TempBufferRenderIndexList[iidx + 1] = TempBufferVerticesStored + 2; + TempBufferRenderIndexList[iidx + 2] = TempBufferVerticesStored + 1; + TempBufferRenderIndexList[iidx + 3] = TempBufferVerticesStored + 0; + TempBufferRenderIndexList[iidx + 4] = TempBufferVerticesStored + 3; + TempBufferRenderIndexList[iidx + 5] = TempBufferVerticesStored + 2; + + TempBufferVerticesStored += 4; + + TempBufferIndicesStored += 6; + } +} + +void +CWaterLevel::RenderOneSlopedUnderWaterPoly(float fX, float fY, float fZ, RwRGBA const&color) +{ + CVector2D camPos(TheCamera.GetPosition().x, TheCamera.GetPosition().y); + + float fDistA = (CVector2D(fX, fY) - camPos).Magnitude() + -140.0f; + float fDistB = (CVector2D(fX, fY + HUGE_SECTOR_SIZE) - camPos).Magnitude() + -140.0f; + float fDistC = (CVector2D(fX + HUGE_SECTOR_SIZE, fY + HUGE_SECTOR_SIZE) - camPos).Magnitude() + -140.0f; + float fDistD = (CVector2D(fX + HUGE_SECTOR_SIZE, fY) - camPos).Magnitude() + -140.0f; + +#ifndef PC_WATER +#define CALCSEABED(v, d) \ + { \ + if ( d < 0.0f ) \ + v = 0.1f + fSeaBedZ; \ + else if ( d > 240.0f ) \ + v = 0.1f; \ + else \ + v = 0.1f + ((fSeaBedZ * (240.0f - d)) / 240.0f); \ + } +#else + #define CALCSEABED(v, d) \ + { \ + v = 0.1f; \ + if ( d < 0.0f ) \ + v += fSeaBedZ; \ + else if ( d <= 240.0f ) \ + v += (fSeaBedZ / 240.0f) * (240.0f - d); \ + } +#endif + float fSeaBedA, fSeaBedB, fSeaBedC, fSeaBedD; + + CALCSEABED(fSeaBedA, fDistA); + CALCSEABED(fSeaBedB, fDistB); + CALCSEABED(fSeaBedC, fDistC); + CALCSEABED(fSeaBedD, fDistD); + + #undef CALCSEABED + + if ( TempBufferIndicesStored >= TEMPBUFFERINDEXSIZE-6 || TempBufferVerticesStored >= TEMPBUFFERVERTSIZE-4 ) + RenderAndEmptyRenderBuffer(); + + int32 vidx = TempBufferVerticesStored; + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 0], fX, fY, fZ - _fWaterZOffset - fSeaBedA); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 0], 0.0f); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 0], 0.0f); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[vidx + 0], color.red, color.green, color.blue, 255); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 1], fX, fY + HUGE_SECTOR_SIZE, fZ - _fWaterZOffset - fSeaBedB); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 1], 0.0f); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 1], 4.0f); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[vidx + 1], color.red, color.green, color.blue, 255); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 2], fX + HUGE_SECTOR_SIZE, fY + HUGE_SECTOR_SIZE, fZ - _fWaterZOffset - fSeaBedC); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 2], 4.0f); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 2], 4.0f); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[vidx + 2], color.red, color.green, color.blue, 255); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 3], fX + HUGE_SECTOR_SIZE, fY, fZ - _fWaterZOffset - fSeaBedD); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 3], 4.0f); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 3], 0.0f); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[vidx + 3], color.red, color.green, color.blue, 255); + + int32 iidx = TempBufferIndicesStored; + + TempBufferRenderIndexList[iidx + 0] = TempBufferVerticesStored + 0; + TempBufferRenderIndexList[iidx + 1] = TempBufferVerticesStored + 2; + TempBufferRenderIndexList[iidx + 2] = TempBufferVerticesStored + 1; + TempBufferRenderIndexList[iidx + 3] = TempBufferVerticesStored + 0; + TempBufferRenderIndexList[iidx + 4] = TempBufferVerticesStored + 3; + TempBufferRenderIndexList[iidx + 5] = TempBufferVerticesStored + 2; + + TempBufferVerticesStored += 4; + + TempBufferIndicesStored += 6; +} + +void +CWaterLevel::RenderOneFlatSmallWaterPolyBlended(float fX, float fY, float fZ, float fCamX, float fCamY, + RwRGBA const &color, RwRGBA const &colorTrans, + float fDrawDist) +{ + if ( TempBufferIndicesStored >= TEMPBUFFERINDEXSIZE-6 || TempBufferVerticesStored >= TEMPBUFFERVERTSIZE-4 ) + RenderAndEmptyRenderBuffer(); + + int32 vidx = TempBufferVerticesStored; + + float fBlendDrawDist = fDrawDist + fStartBlendDistanceAdd; + + float fDistStartX = SQR(fX - fCamX); + float fDistStartY = SQR(fY - fCamY); + float fDistEndX = SQR((fX + SMALL_SECTOR_SIZE) - fCamX); + float fDistEndY = SQR((fY + SMALL_SECTOR_SIZE) - fCamY); + + + float fAlphaBlendMulA + = Min(fFlatWaterBlendRange * Max(sqrt(fDistStartX + fDistStartY) - fBlendDrawDist, fMinWaterAlphaMult), 1.0f); + float fAlphaBlendMulB + = Min(fFlatWaterBlendRange * Max(sqrt(fDistStartX + fDistEndY ) - fBlendDrawDist, fMinWaterAlphaMult), 1.0f); + float fAlphaBlendMulC + = Min(fFlatWaterBlendRange * Max(sqrt(fDistEndX + fDistEndY ) - fBlendDrawDist, fMinWaterAlphaMult), 1.0f); + float fAlphaBlendMulD + = Min(fFlatWaterBlendRange * Max(sqrt(fDistEndX + fDistStartY) - fBlendDrawDist, fMinWaterAlphaMult), 1.0f); + + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 0], fX, fY, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDU); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDV); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[vidx + 0], color.red, color.green, color.blue, + (colorTrans.alpha + (color.alpha - colorTrans.alpha) * (uint8)(int32)fAlphaBlendMulA)); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 1], fX, fY + SMALL_SECTOR_SIZE, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDU); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDV + 1.0f); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[vidx + 1], color.red, color.green, color.blue, + (colorTrans.alpha + (color.alpha - colorTrans.alpha) * (uint8)(int32)fAlphaBlendMulB)); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 2], fX + SMALL_SECTOR_SIZE, fY + SMALL_SECTOR_SIZE, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDU + 1.0f); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDV + 1.0f); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[vidx + 2], color.red, color.green, color.blue, + (colorTrans.alpha + (color.alpha - colorTrans.alpha) * (uint8)(int32)fAlphaBlendMulC)); + + RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 3], fX + SMALL_SECTOR_SIZE, fY, fZ - _fWaterZOffset); + RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDU + 1.0f); + RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDV); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[vidx + 3], color.red, color.green, color.blue, + (colorTrans.alpha + (color.alpha - colorTrans.alpha) * (uint8)(int32)fAlphaBlendMulD)); + + + int32 iidx = TempBufferIndicesStored; + + TempBufferRenderIndexList[iidx + 0] = TempBufferVerticesStored + 0; + TempBufferRenderIndexList[iidx + 1] = TempBufferVerticesStored + 2; + TempBufferRenderIndexList[iidx + 2] = TempBufferVerticesStored + 1; + TempBufferRenderIndexList[iidx + 3] = TempBufferVerticesStored + 0; + TempBufferRenderIndexList[iidx + 4] = TempBufferVerticesStored + 3; + TempBufferRenderIndexList[iidx + 5] = TempBufferVerticesStored + 2; + + TempBufferVerticesStored += 4; + + TempBufferIndicesStored += 6; +} + +float +CWaterLevel::CalcDistanceToWater(float fX, float fY) +{ + const float fSectorMaxRenderDist = 250.0f; + + int32 nStartX = WATER_TO_SMALL_SECTOR_X(fX - fSectorMaxRenderDist + WATER_X_OFFSET) - 1; + int32 nEndX = WATER_TO_SMALL_SECTOR_X(fX + fSectorMaxRenderDist + WATER_X_OFFSET) + 1; + int32 nStartY = WATER_TO_SMALL_SECTOR_Y(fY - fSectorMaxRenderDist) - 1; + int32 nEndY = WATER_TO_SMALL_SECTOR_Y(fY + fSectorMaxRenderDist) + 1; + + nStartX = Clamp(nStartX, 0, MAX_SMALL_SECTORS - 1); + nEndX = Clamp(nEndX, 0, MAX_SMALL_SECTORS - 1); + nStartY = Clamp(nStartY, 0, MAX_SMALL_SECTORS - 1); + nEndY = Clamp(nEndY, 0, MAX_SMALL_SECTORS - 1); + + float fDistSqr = 1.0e10f; + + for ( int32 x = nStartX; x <= nEndX; x++ ) + { + for ( int32 y = nStartY; y <= nEndY; y++ ) + { + if ( aWaterFineBlockList[x][y] >= 0 ) + { + float fSectorX = WATER_FROM_SMALL_SECTOR_X(x) - WATER_X_OFFSET; + float fSectorY = WATER_FROM_SMALL_SECTOR_Y(y); + + CVector2D vecDist + ( + fSectorX + SMALL_SECTOR_SIZE - fX, + fSectorY + SMALL_SECTOR_SIZE - fY + ); + + fDistSqr = Min(vecDist.MagnitudeSqr(), fDistSqr); + } + } + } + + return Clamp(Sqrt(fDistSqr) - 23.0f, 0.0f, fSectorMaxRenderDist); +} + +void +CWaterLevel::RenderAndEmptyRenderBuffer() +{ + if ( TempBufferVerticesStored ) + { + LittleTest(); + + if ( RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, rwIM3D_VERTEXUV) ) + { + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored); + RwIm3DEnd(); + } + } + + TempBufferIndicesStored = 0; + TempBufferVerticesStored = 0; +} + +bool +CWaterLevel::GetGroundLevel(CVector const &vecPosn, float *pfOutLevel, ColData *pData, float fDistance) +{ + CColPoint point; + CEntity *entity; + + if ( !CWorld::ProcessVerticalLine(vecPosn + CVector(0.0f, 0.0f, fDistance), + -fDistance, point, entity, true, false, false, false, true, false, nil) ) + return false; + + *pfOutLevel = point.point.z; + + if ( pData != nil ) + { + pData->SurfaceType = point.surfaceB; + pData->PieceType = point.pieceB; + } + + return true; +} + +bool +CWaterLevel::IsLocationOutOfWorldBounds_WS(CVector const &vecPosn, int nOffset) +{ + int32 x = int32((vecPosn.x / 50.0f) + 48.0f); + int32 y = int32((vecPosn.y / 50.0f) + 40.0f); + + return x < nOffset || x >= 80 - nOffset || y < nOffset || y >= 80 - nOffset; +} + +bool +CWaterLevel::GetGroundLevel_WS(CVector const &vecPosn, float *pfOutLevel, ColData *pData, float fDistance) +{ + if ( IsLocationOutOfWorldBounds_WS(vecPosn, 0) ) + return false; + else + return GetGroundLevel(vecPosn, pfOutLevel, pData, fDistance); +} + +bool +CWaterLevel::GetWaterDepth(CVector const &vecPosn, float *pfDepth, float *pfLevelNoWaves, float *pfGroundLevel) +{ + float fLevelNoWaves; + float fGroundLevel; + + if ( !GetWaterLevelNoWaves(vecPosn.x, vecPosn.y, vecPosn.z, &fLevelNoWaves) ) + return false; + + if ( !GetGroundLevel(vecPosn, &fGroundLevel, nil, 30.0f) ) + fGroundLevel = -100.0; + + if ( pfDepth != nil ) + *pfDepth = fLevelNoWaves - fGroundLevel; + + if ( pfLevelNoWaves != nil ) + *pfLevelNoWaves = fLevelNoWaves; + + if ( pfGroundLevel != nil ) + *pfGroundLevel = fGroundLevel; + + return true; +} + +void +CWaterLevel::RenderSeaBirds() +{ + CVector cur_pos = TheCamera.GetPosition(); + + if ( !CCullZones::CamNoRain() + && !CCullZones::PlayerNoRain() + && (CWeather::NewWeatherType == WEATHER_SUNNY || CWeather::NewWeatherType == WEATHER_EXTRA_SUNNY) + && CClock::ms_nGameClockHours > 6 && CClock::ms_nGameClockHours < 20 ) + { + static CVector prev_pos(0.0f, 0.0f, 0.0f); + static CVector prev_front(0.0f, 0.0f, 0.0f); + static int32 timecounter; + + if ( Abs(prev_pos.x - cur_pos.x) + Abs(prev_pos.y - cur_pos.y) + Abs(prev_pos.z - cur_pos.z) > 1.5f ) + { + prev_pos = cur_pos; + timecounter = CTimer::GetTimeInMilliseconds(); + } + else if ( (CTimer::GetTimeInMilliseconds() - timecounter) > 5000 ) + { + static int32 birdgenTime = 0; + + if ( (CTimer::GetTimeInMilliseconds() - birdgenTime) > 1000 ) + { + birdgenTime = CTimer::GetTimeInMilliseconds(); + + CVector vecPos = cur_pos; + + float fAngle = CGeneral::GetRandomNumberInRange(90.0f, 150.0f); + + uint16 nSinCosIdx = CGeneral::GetRandomNumber() % (CParticle::SIN_COS_TABLE_SIZE-1); + + float fCos = CParticle::Cos(nSinCosIdx); + float fSin = CParticle::Sin(nSinCosIdx); + + vecPos.x += (fCos - fSin) * fAngle; + vecPos.y += (fSin + fCos) * fAngle; + vecPos.z += CGeneral::GetRandomNumberInRange(10.0f, 30.0f); + + CVector vecDir(CGeneral::GetRandomNumberInRange(-1.0f, 1.0f), + CGeneral::GetRandomNumberInRange(-1.0f, 1.0f), + 0.0f); + + CParticle::AddParticle(PARTICLE_BIRD_FRONT, vecPos, vecDir, nil, 0.0f, 0, 0, 0, 0); + } + } + } +} + +void +CWaterLevel::RenderShipsOnHorizon() +{ +#ifdef FIX_BUGS + CVector cur_pos = FindPlayerCoors(); +#else + CVector cur_pos = FindPlayerPed()->GetPosition(); +#endif + + static CVector prev_pos(0.0f, 0.0f, 0.0f); + static CVector prev_front(0.0f, 0.0f, 0.0f); + static int32 timecounter; + + if ( Abs(prev_pos.x - cur_pos.x) + Abs(prev_pos.y - cur_pos.y) + Abs(prev_pos.z - cur_pos.z) > 1.5f ) + { + prev_pos = cur_pos; + timecounter = CTimer::GetTimeInMilliseconds(); + } + else if ( (CTimer::GetTimeInMilliseconds() - timecounter) > 5000 ) + { + static int32 shipgenTime = 0; + + if ( (CTimer::GetTimeInMilliseconds() - shipgenTime) > 4000 ) + { + shipgenTime = CTimer::GetTimeInMilliseconds(); + + CVector vecPos = cur_pos; + + float fAngle = CGeneral::GetRandomNumberInRange(450.0f, 750.0f); + + uint16 nSinCosIdx = CGeneral::GetRandomNumber() % (CParticle::SIN_COS_TABLE_SIZE-1); + + float fCos = CParticle::Cos(nSinCosIdx); + float fSin = CParticle::Sin(nSinCosIdx); + + vecPos.x += (fCos - fSin) * fAngle; + vecPos.y += (fSin + fCos) * fAngle; + + float fLevelNoWaves; + + if ( GetWaterLevelNoWaves(vecPos.x, vecPos.y, vecPos.z, &fLevelNoWaves) ) + { + if ( IsLocationOutOfWorldBounds_WS(vecPos, 1) ) + { + vecPos.z = fLevelNoWaves + 9.5f; + + CVector vecDir + ( + CGeneral::GetRandomNumberInRange(-0.1f, 0.1f), + 0.0f, + 0.0f + ); + + CParticle::AddParticle(PARTICLE_SHIP_SIDE, vecPos, vecDir, + nil, 0.0f, 0, 0, CGeneral::GetRandomNumber() & 7, 0); + } + } + } + } +} + +void +CWaterLevel::HandleSeaLifeForms() +{ + if ( CReplay::IsPlayingBack() ) + return; + + CVector cur_pos = FindPlayerPed()->GetPosition(); + + static CVector prev_pos(0.0f, 0.0f, 0.0f); + static int32 timecounter; + + if ( Abs(prev_pos.x - cur_pos.x) + Abs(prev_pos.y - cur_pos.y) + Abs(prev_pos.z - cur_pos.z) > 1.5f ) + { + prev_pos = cur_pos; + timecounter = CTimer::GetTimeInMilliseconds(); + } + else if ( (CTimer::GetTimeInMilliseconds() - timecounter) > 5000 ) + { + if ( CWaterCreatures::IsSpaceForMoreWaterCreatures() ) + { + for ( int32 i = 0; i < 3; i++ ) + { + CVector vecPos = cur_pos; + + float fAngle = CGeneral::GetRandomNumberInRange(15.0f, 30.0f); + + uint16 nSinCosIdx = CGeneral::GetRandomNumber() % (CParticle::SIN_COS_TABLE_SIZE-1); + + float fCos = CParticle::Cos(nSinCosIdx); + float fSin = CParticle::Sin(nSinCosIdx); + + vecPos.x += (fCos - fSin) * fAngle; + vecPos.y += (fSin + fCos) * fAngle; + + CWaterCreatures::CreateOne(vecPos, -1); + } + } + } + + CWaterCreatures::UpdateAll(); +} + +void +CWaterLevel::HandleBeachToysStuff(void) +{ +#ifdef FIX_BUGS + CVector cur_pos = FindPlayerCoors(); +#else + CVector cur_pos = FindPlayerPed()->GetPosition(); +#endif + + static bool bBeachBallInit = true; + static CVector FirstBeachBallPos = cur_pos; + static bool bLoungeInit = true; + static CVector FirstLoungePos = cur_pos; + static CVector prev_pos(0.0f, 0.0f, 0.0f); + static int32 timecounter; + + if ( Abs(prev_pos.x - cur_pos.x) + Abs(prev_pos.y - cur_pos.y) + Abs(prev_pos.z - cur_pos.z) > 1.5f ) + { + prev_pos = cur_pos; + timecounter = CTimer::GetTimeInMilliseconds(); + } + else if ( (CTimer::GetTimeInMilliseconds() - timecounter) > 5000 ) + { + static int32 toygenTime = CTimer::GetTimeInMilliseconds(); + + if ( (CTimer::GetTimeInMilliseconds() - toygenTime) > 20000 ) + { + toygenTime = CTimer::GetTimeInMilliseconds(); + + if ( bBeachBallInit || (cur_pos - FirstBeachBallPos).MagnitudeSqr() > 6400.0f ) + { + for ( int32 i = 0; i < 3; i++ ) + { + CVector vecPos = cur_pos; + + float fAngle = CGeneral::GetRandomNumberInRange(20.0f, 35.0f); + + uint16 nSinCosIdx = CGeneral::GetRandomNumber() % (CParticle::SIN_COS_TABLE_SIZE-1); + + float fCos = CParticle::Cos(nSinCosIdx); + float fSin = CParticle::Sin(nSinCosIdx); + + vecPos.x += (fCos - fSin) * fAngle; + vecPos.y += (fSin + fCos) * fAngle; + + if ( TheCamera.IsSphereVisible(vecPos, 1.0f) ) + { + float fWaterLevel; + + if ( !GetWaterLevel(vecPos.x, vecPos.y, vecPos.z, &fWaterLevel, false) ) + { + float fGroundLevel; + ColData coldata; + + if ( GetGroundLevel(vecPos, &fGroundLevel, &coldata, 30.0f) ) + { + if ( coldata.SurfaceType == SURFACE_SAND ) + { + CEntity *toy = CreateBeachToy(vecPos, BEACHTOY_BALL); + + if ( toy ) + { + FirstBeachBallPos = cur_pos; + bBeachBallInit = false; + i = 10; + } + } + } + } + } + } + } + + if ( bLoungeInit || (cur_pos - FirstLoungePos).MagnitudeSqr() > 6400.0f ) + { + for ( int32 i = 0; i < 5; i++ ) + { + CVector vecPos = cur_pos; + + float fAngle = CGeneral::GetRandomNumberInRange(20.0f, 35.0f); + + uint16 nSinCosIdx = CGeneral::GetRandomNumber() % (CParticle::SIN_COS_TABLE_SIZE-1); + + float fCos = CParticle::Cos(nSinCosIdx); + float fSin = CParticle::Sin(nSinCosIdx); + + vecPos.x += (fCos - fSin) * fAngle; + vecPos.y += (fSin + fCos) * fAngle; + + if ( TheCamera.IsSphereVisible(vecPos, 2.0f) ) + { + float fWaterLevel; + + if ( !GetWaterLevel(vecPos.x, vecPos.y, vecPos.z, &fWaterLevel, false) ) + { + float fGroundLevel; + ColData coldata; + + if ( GetGroundLevel(vecPos, &fGroundLevel, &coldata, 30.0f) ) + { + if ( coldata.SurfaceType == SURFACE_SAND ) + { + CEntity *toy = CreateBeachToy(vecPos, BEACHTOY_ANY_LOUNGE); + if ( toy ) + { + toy->SetHeading(DEGTORAD(CGeneral::GetRandomNumberInRange(0.0f, 359.0f))); + FirstLoungePos = cur_pos; + bLoungeInit = false; + } + } + } + } + } + } + } + } + } +} + +CEntity * +CWaterLevel::CreateBeachToy(CVector const &vec, eBeachToy beachtoy) +{ + if (CObject::nNoTempObjects >= NUMTEMPOBJECTS) + return nil; + + int finalToy = beachtoy; + bool isStatic = false; + int model = MI_BEACHBALL; + switch (beachtoy) { + case BEACHTOY_ANY_LOUNGE: + switch ( CGeneral::GetRandomNumber() & 7 ) { + case 1: + case 7: + finalToy = BEACHTOY_LOUNGE_WOOD_UP; + break; + case 3: + case 5: + finalToy = BEACHTOY_LOUNGE_TOWEL_UP; + break; + default: + finalToy = BEACHTOY_LOUNGE_WOOD_ON; + break; + } + break; + case BEACHTOY_ANY_TOWEL: + switch ( CGeneral::GetRandomNumber() & 7 ) { + case 1: + case 7: + finalToy = BEACHTOY_TOWEL2; + break; + case 2: + case 6: + finalToy = BEACHTOY_TOWEL3; + break; + case 3: + case 5: + finalToy = BEACHTOY_TOWEL4; + break; + default: + finalToy = BEACHTOY_TOWEL1; + break; + } + if (CObject::nNoTempObjects >= 35) { + return nil; + } + default: + break; + } + switch (finalToy) { + case BEACHTOY_BALL: + isStatic = false; + model = MI_BEACHBALL; + break; + case BEACHTOY_LOUNGE_WOOD_UP: + isStatic = false; + model = MI_LOUNGE_WOOD_UP; + break; + case BEACHTOY_LOUNGE_TOWEL_UP: + isStatic = false; + model = MI_LOUNGE_TOWEL_UP; + break; + case BEACHTOY_LOUNGE_WOOD_ON: + isStatic = false; + model = MI_LOUNGE_WOOD_DN; + break; + case BEACHTOY_LOTION: + model = MI_LOTION; + isStatic = true; + break; + case BEACHTOY_TOWEL1: + model = MI_BEACHTOWEL01; + isStatic = true; + break; + case BEACHTOY_TOWEL2: + model = MI_BEACHTOWEL02; + isStatic = true; + break; + case BEACHTOY_TOWEL3: + model = MI_BEACHTOWEL03; + isStatic = true; + break; + case BEACHTOY_TOWEL4: + model = MI_BEACHTOWEL04; + isStatic = true; + break; + default: + break; + } + CObject *toy = new CObject(model, true); + if (toy) { + toy->SetPosition(vec); + toy->GetMatrix().UpdateRW(); + toy->m_vecMoveSpeed = CVector(0.f, 0.f, 0.f); + toy->m_vecTurnSpeed = CVector(0.f, 0.f, 0.f); + toy->ObjectCreatedBy = TEMP_OBJECT; + toy->bIsStatic = isStatic; + CObject::nNoTempObjects++; + toy->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 43200000; + CWorld::Add(toy); + return toy; + } else + return nil; +} \ No newline at end of file diff --git a/src/miami/renderer/WaterLevel.h b/src/miami/renderer/WaterLevel.h new file mode 100644 index 00000000..6d6614d8 --- /dev/null +++ b/src/miami/renderer/WaterLevel.h @@ -0,0 +1,184 @@ +#pragma once + +#define WATER_X_OFFSET (400.0f) + +#define WATER_Z_OFFSET (0.5f) + +#define NO_WATER -128 + +#define MAX_SMALL_SECTORS 128 +#define MAX_LARGE_SECTORS 64 +#define MAX_HUGE_SECTORS 32 +#define MAX_EXTRAHUGE_SECTORS 16 + +#define SMALL_SECTOR_SIZE 32 +#define LARGE_SECTOR_SIZE 64 +#define HUGE_SECTOR_SIZE 128 +#define EXTRAHUGE_SECTOR_SIZE 256 + +#define WATER_START_X -2048.0f +#define WATER_END_X 2048.0f + +#define WATER_START_Y -2048.0f +#define WATER_END_Y 2048.0f + +#define WATER_WIDTH ((WATER_END_X - WATER_START_X)) +#define WATER_HEIGHT ((WATER_END_Y - WATER_START_Y)) + +#define WATER_UNSIGN_X(x) ( (x) + (WATER_WIDTH /2) ) +#define WATER_UNSIGN_Y(y) ( (y) + (WATER_HEIGHT/2) ) +#define WATER_SIGN_X(x) ( (x) - (WATER_WIDTH /2) ) +#define WATER_SIGN_Y(y) ( (y) - (WATER_HEIGHT/2) ) + +// 64x64 Large blocks 64x64 each +#define WATER_TO_BLOCK_X(x) ( WATER_UNSIGN_X(x) / WATER_BLOCK_SECTORS ) +#define WATER_TO_BLOCK_Y(x) ( WATER_UNSIGN_Y(x) / WATER_BLOCK_SECTORS ) + +// 128x128 Small blocks 32x32 each +#define WATER_TO_FINEBLOCK_X(x) ( WATER_UNSIGN_X(x) / WATER_FINEBLOCK_SECTORS ) +#define WATER_TO_FINEBLOCK_Y(x) ( WATER_UNSIGN_Y(x) / WATER_FINEBLOCK_SECTORS ) + +// 32 +#define WATER_SMALL_X(x) ( WATER_UNSIGN_X(x) / MAX_SMALL_SECTORS ) +#define WATER_SMALL_Y(y) ( WATER_UNSIGN_Y(y) / MAX_SMALL_SECTORS ) +#define WATER_FROM_SMALL_SECTOR_X(x) ( ((x) - (MAX_SMALL_SECTORS/2) ) * SMALL_SECTOR_SIZE ) +#define WATER_FROM_SMALL_SECTOR_Y(y) ( ((y) - (MAX_SMALL_SECTORS/2) ) * SMALL_SECTOR_SIZE ) +#define WATER_TO_SMALL_SECTOR_X(x) ( WATER_UNSIGN_X(x) / SMALL_SECTOR_SIZE ) +#define WATER_TO_SMALL_SECTOR_Y(y) ( WATER_UNSIGN_Y(y) / SMALL_SECTOR_SIZE ) + +// 64 +#define WATER_LARGE_X(x) ( WATER_UNSIGN_X(x) / MAX_LARGE_SECTORS ) +#define WATER_LARGE_Y(y) ( WATER_UNSIGN_Y(y) / MAX_LARGE_SECTORS ) +#define WATER_FROM_LARGE_SECTOR_X(x) ( ((x) - (MAX_LARGE_SECTORS/2) ) * LARGE_SECTOR_SIZE ) +#define WATER_FROM_LARGE_SECTOR_Y(y) ( ((y) - (MAX_LARGE_SECTORS/2) ) * LARGE_SECTOR_SIZE ) +#define WATER_TO_LARGE_SECTOR_X(x) ( WATER_UNSIGN_X(x) / LARGE_SECTOR_SIZE ) +#define WATER_TO_LARGE_SECTOR_Y(y) ( WATER_UNSIGN_Y(y) / LARGE_SECTOR_SIZE ) + +// 128 +#define WATER_HUGE_X(x) ( WATER_UNSIGN_X(x) / MAX_HUGE_SECTORS ) +#define WATER_HUGE_Y(y) ( WATER_UNSIGN_Y(y) / MAX_HUGE_SECTORS ) +#define WATER_FROM_HUGE_SECTOR_X(x) ( ((x) - (MAX_HUGE_SECTORS/2) ) * HUGE_SECTOR_SIZE ) +#define WATER_FROM_HUGE_SECTOR_Y(y) ( ((y) - (MAX_HUGE_SECTORS/2) ) * HUGE_SECTOR_SIZE ) +#define WATER_TO_HUGE_SECTOR_X(x) ( WATER_UNSIGN_X(x) / HUGE_SECTOR_SIZE ) +#define WATER_TO_HUGE_SECTOR_Y(y) ( WATER_UNSIGN_Y(y) / HUGE_SECTOR_SIZE ) + +// 256 +#define WATER_EXTRAHUGE_X(x) ( WATER_UNSIGN_X(x) / MAX_EXTRAHUGE_SECTORS ) +#define WATER_EXTRAHUGE_Y(y) ( WATER_UNSIGN_Y(y) / MAX_EXTRAHUGE_SECTORS ) +#define WATER_FROM_EXTRAHUGE_SECTOR_X(x) ( ((x) - (MAX_EXTRAHUGE_SECTORS/2)) * EXTRAHUGE_SECTOR_SIZE ) +#define WATER_FROM_EXTRAHUGE_SECTOR_Y(y) ( ((y) - (MAX_EXTRAHUGE_SECTORS/2)) * EXTRAHUGE_SECTOR_SIZE ) +#define WATER_TO_EXTRAHUGE_SECTOR_X(x) ( WATER_UNSIGN_X(x) / EXTRAHUGE_SECTOR_SIZE ) +#define WATER_TO_EXTRAHUGE_SECTOR_Y(y) ( WATER_UNSIGN_Y(y) / EXTRAHUGE_SECTOR_SIZE ) + +struct ColData +{ + uint8 SurfaceType; + uint8 PieceType; +}; + +enum eBeachToy +{ + BEACHTOY_0 = 0, + BEACHTOY_BALL, + BEACHTOY_LOUNGE_WOOD_UP, + BEACHTOY_LOUNGE_TOWEL_UP, + BEACHTOY_LOUNGE_WOOD_ON, + BEACHTOY_ANY_LOUNGE, + BEACHTOY_LOTION, + BEACHTOY_TOWEL1, + BEACHTOY_TOWEL2, + BEACHTOY_TOWEL3, + BEACHTOY_TOWEL4, + BEACHTOY_ANY_TOWEL, +}; + +extern RwRaster* gpWaterRaster; +extern bool gbDontRenderWater; + +class CEntity; + +class CWaterLevel +{ +public: + static int32 ms_nNoOfWaterLevels; + static float ms_aWaterZs[48]; + static CRect ms_aWaterRects[48]; + static int8 aWaterBlockList[MAX_LARGE_SECTORS][MAX_LARGE_SECTORS]; // 64x64 Large blocks 64x64 each + static int8 aWaterFineBlockList[MAX_SMALL_SECTORS][MAX_SMALL_SECTORS]; // 128x128 Small blocks 32x32 each + static bool WavesCalculatedThisFrame; + + static bool RequireWavySector; + static bool MaskCalculatedThisFrame; + static CVector PreCalculatedMaskPosn; + static bool m_bRenderSeaBed; + static int32 m_nRenderWaterLayers; + + static RpAtomic *ms_pWavyAtomic; + static RpAtomic *ms_pMaskAtomic; + + static void Initialise(Const char *pWaterDat); // out of class in III PC and later because of SecuROM + static void Shutdown(); + + static void CreateWavyAtomic(); + static void DestroyWavyAtomic(); + + static void AddWaterLevel(float fXLeft, float fYBottom, float fXRight, float fYTop, float fLevel); + static bool WaterLevelAccordingToRectangles(float fX, float fY, float *pfOutLevel = nil); + static bool TestVisibilityForFineWaterBlocks(const CVector &worldPos); + static void RemoveIsolatedWater(); + + static bool GetWaterLevel(float fX, float fY, float fZ, float *pfOutLevel, bool bDontCheckZ); + static bool GetWaterLevel(CVector coors, float *pfOutLevel, bool bDontCheckZ) { return GetWaterLevel(coors.x, coors.y, coors.z, pfOutLevel, bDontCheckZ); } + static bool GetWaterLevelNoWaves(float fX, float fY, float fZ, float *pfOutLevel); + static float GetWaterWavesOnly(short x, short y); // unused + static CVector GetWaterNormal(float fX, float fY); + + static void RenderWater(); + static void RenderTransparentWater(void); + // unused + static void RenderOneFlatSmallWaterPoly (float fX, float fY, float fZ, RwRGBA const &color); + // inlined + static void RenderOneFlatLargeWaterPoly (float fX, float fY, float fZ, RwRGBA const &color); + static void RenderOneFlatHugeWaterPoly (float fX, float fY, float fZ, RwRGBA const &color); + static void RenderOneFlatExtraHugeWaterPoly(float fX, float fY, float fZ, RwRGBA const &color); + // inlined + static void RenderOneWavySector (float fX, float fY, float fZ, RwRGBA const &color, bool bDontRender = false); + // unused +#ifdef PC_WATER + static void RenderWavyMask(float fX, float fY, float fZ, float fSectorX, float fSectorY, float fCamPosX, float fCamPosY, float fCamDirX, float fCamDirY, RwRGBA const&color); +#else + static void RenderWavyMask(float fX, float fY, float fZ, float fSectorX, float fSectorY, int32 nCamDirX, int32 nCamDirY, RwRGBA const&color); +#endif + +#ifdef PC_WATER + static void PreCalcWaterGeometry(void); + static bool PreCalcWavySector(RwRGBA const &color); //fucked up + static bool PreCalcWavyMask(float fX, float fY, float fZ, float fSectorX, float fSectorY, float fCamPosX, float fCamPosY, float fCamDirX, float fCamDirY, RwRGBA const&color); +#endif + + + static void RenderBoatWakes(void); + static void RenderWakeSegment(CVector2D &vecA, CVector2D &vecB, CVector2D &vecC, CVector2D &vecD, float &fSizeA, float &fSizeB, float &fAlphaA, float &fAlphaB, float &fWakeZ); + + // unused + static void RenderOneSlopedUnderWaterPoly(float fX, float fY, float fZ, RwRGBA const&color); // UNUSED + static void RenderOneFlatSmallWaterPolyBlended(float fX, float fY, float fZ, float fCamX, float fCamY, RwRGBA const &color, RwRGBA const &colorTrans, float fDrawDist); + static float CalcDistanceToWater(float fX, float fY); + static void RenderAndEmptyRenderBuffer(); + + static bool GetGroundLevel(CVector const &vecPosn, float *pfOutLevel, ColData *pData, float fDistance); + + // unused + static bool IsLocationOutOfWorldBounds_WS(CVector const &vecPosn, int nOffset); + // unused + static bool GetGroundLevel_WS(CVector const & vecPosn, float *pfOutLevel, ColData *pData, float fDistance); + static bool GetWaterDepth(CVector const &vecPosn, float *pfDepth, float *pfLevelNoWaves, float *pfGroundLevel); + + static void RenderSeaBirds(); + static void RenderShipsOnHorizon(); + + static void HandleSeaLifeForms(); + + static void HandleBeachToysStuff(void); + static CEntity *CreateBeachToy(CVector const &vec, eBeachToy beachtoy); +}; diff --git a/src/miami/renderer/Weather.cpp b/src/miami/renderer/Weather.cpp new file mode 100644 index 00000000..9f925a8c --- /dev/null +++ b/src/miami/renderer/Weather.cpp @@ -0,0 +1,662 @@ +#include "common.h" + +#include "Weather.h" + +#include "Camera.h" +#include "Clock.h" +#include "CutsceneMgr.h" +#include "DMAudio.h" +#include "General.h" +#include "Pad.h" +#include "PlayerPed.h" +#include "Particle.h" +#include "RenderBuffer.h" +#include "Stats.h" +#include "Shadows.h" +#include "Timecycle.h" +#include "Timer.h" +#include "Vehicle.h" +#include "World.h" +#include "ZoneCull.h" +#include "SpecialFX.h" +#include "Replay.h" + +int32 CWeather::SoundHandle = -1; + +int32 CWeather::WeatherTypeInList; +int16 CWeather::OldWeatherType; +int16 CWeather::NewWeatherType; +int16 CWeather::ForcedWeatherType; + +bool CWeather::LightningFlash; +bool CWeather::LightningBurst; +uint32 CWeather::LightningStart; +uint32 CWeather::LightningFlashLastChange; +uint32 CWeather::WhenToPlayLightningSound; +uint32 CWeather::LightningDuration; +int32 CWeather::StreamAfterRainTimer; + +float CWeather::ExtraSunnyness; +float CWeather::Foggyness; +float CWeather::CloudCoverage; +float CWeather::Wind; +float CWeather::Rain; +float CWeather::InterpolationValue; +float CWeather::WetRoads; +float CWeather::Rainbow; +float CWeather::SunGlare; +float CWeather::WindClipped; +float CWeather::TrafficLightBrightness; + +bool CWeather::bScriptsForceRain; + +tRainStreak Streaks[NUM_RAIN_STREAKS]; + +int16 WeatherTypesList[] = { + WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, + WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, + WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_EXTRA_SUNNY, + WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, + WEATHER_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, + WEATHER_EXTRA_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_EXTRA_SUNNY, + WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, + WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, + WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, + WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, + WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_CLOUDY, + WEATHER_RAINY, WEATHER_RAINY, WEATHER_RAINY, WEATHER_RAINY, + WEATHER_CLOUDY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, + WEATHER_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, + WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, + WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY +}; + +int16 WeatherTypesList_WithHurricanes[] = { + WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, + WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, + WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_EXTRA_SUNNY, + WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, + WEATHER_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_CLOUDY, + WEATHER_HURRICANE, WEATHER_HURRICANE, WEATHER_CLOUDY, WEATHER_SUNNY, + WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, + WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, + WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, + WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, + WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, + WEATHER_CLOUDY, WEATHER_HURRICANE, WEATHER_HURRICANE, WEATHER_HURRICANE, + WEATHER_CLOUDY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, + WEATHER_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, + WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, + WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY +}; + +const float Windyness[] = { + 0.25f,// WEATHER_SUNNY + 0.7f, // WEATHER_CLOUDY + 1.0f, // WEATHER_RAINY + 0.0f, // WEATHER_FOGGY + 0.0f, // WEATHER_EXTRA_SUNNY + 2.0f, // WEATHER_HURRICANE + 0.0f +}; + +#define MIN_TIME_BETWEEN_LIGHTNING_FLASH_CHANGES (50) + +#define RAIN_CHANGE_SPEED (0.003f) + +#define DROPLETS_LEFT_OFFSET (10.0f) +#define DROPLETS_RIGHT_OFFSET (10.0f) +#define DROPLETS_TOP_OFFSET (10.0f) +#define DROPLETS_BOTTOM_OFFSET (10.0f) + +#define STREAK_U (10.0f) +#define STREAK_V (18.0f) +#define LARGE_STREAK_COEFFICIENT (1.23f) +#define STREAK_MIN_DISTANCE (8.0f) +#define STREAK_MAX_DISTANCE (16.0f) + +#define SPLASH_CHECK_RADIUS (7.0f) +#define SPLASH_OFFSET_RADIUS (2.0f) + +#define STREAK_LIFETIME (4.0f) +#define STREAK_INTEROLATION_TIME (0.3f) + +#define RAIN_COLOUR_R (200) +#define RAIN_COLOUR_G (200) +#define RAIN_COLOUR_B (256) +#define RAIN_ALPHA (255) + +void CWeather::Init(void) +{ + NewWeatherType = WEATHER_EXTRA_SUNNY; + bScriptsForceRain = false; + OldWeatherType = WEATHER_EXTRA_SUNNY; + InterpolationValue = 0.0f; + WhenToPlayLightningSound = 0; + WeatherTypeInList = 0; + ForcedWeatherType = WEATHER_RANDOM; + SoundHandle = DMAudio.CreateEntity(AUDIOTYPE_WEATHER, (void*)1); + if (SoundHandle >= 0) + DMAudio.SetEntityStatus(SoundHandle, TRUE); +} + +void CWeather::Update(void) +{ + if(!CReplay::IsPlayingBack()){ + float fNewInterpolation = (CClock::GetMinutes() + CClock::GetSeconds()/60.0f)/60.0f; + if (fNewInterpolation < InterpolationValue) { + // new hour + OldWeatherType = NewWeatherType; + if (ForcedWeatherType >= 0) + NewWeatherType = ForcedWeatherType; + else { + WeatherTypeInList = (WeatherTypeInList + 1) % ARRAY_SIZE(WeatherTypesList); + NewWeatherType = CStats::NoMoreHurricanes ? WeatherTypesList[WeatherTypeInList] : WeatherTypesList_WithHurricanes[WeatherTypeInList]; + } + } + InterpolationValue = fNewInterpolation; + } + +#ifndef FINAL + if (CPad::GetPad(1)->GetRightShockJustDown()) { + NewWeatherType = (NewWeatherType + 1) % WEATHER_TOTAL; + OldWeatherType = NewWeatherType; + } +#endif + + // Lightning + if (NewWeatherType != WEATHER_RAINY || OldWeatherType != WEATHER_RAINY) { + LightningFlash = false; + LightningBurst = false; + } + else{ + if (LightningBurst) { + if ((CGeneral::GetRandomNumber() & 255) >= 32) { + // 0.875 probability + if (CTimer::GetTimeInMilliseconds() - LightningFlashLastChange > MIN_TIME_BETWEEN_LIGHTNING_FLASH_CHANGES) { + bool bOldLightningFlash = LightningFlash; + LightningFlash = CGeneral::GetRandomTrueFalse(); + if (LightningFlash != bOldLightningFlash) + LightningFlashLastChange = CTimer::GetTimeInMilliseconds(); + } + } + else { + // 0.125 probability + LightningBurst = false; + LightningDuration = Min(CTimer::GetFrameCounter() - LightningStart, 20); + LightningFlash = false; + WhenToPlayLightningSound = CTimer::GetTimeInMilliseconds() + 150 * (20 - LightningDuration); + } + } + else { + if (CGeneral::GetRandomNumber() >= 200) { + // lower probability on PC due to randomness bug + LightningFlash = false; + } + else { + LightningBurst = true; + LightningStart = CTimer::GetFrameCounter(); + LightningFlashLastChange = CTimer::GetTimeInMilliseconds(); + LightningFlash = true; + } + } + } + if (WhenToPlayLightningSound && CTimer::GetTimeInMilliseconds() > WhenToPlayLightningSound) { + DMAudio.PlayOneShot(SoundHandle, SOUND_LIGHTNING, LightningDuration); + CPad::GetPad(0)->StartShake(40 * LightningDuration + 100, 2 * LightningDuration + 80); + WhenToPlayLightningSound = 0; + } + + // Wet roads + if (OldWeatherType == WEATHER_RAINY || OldWeatherType == WEATHER_HURRICANE) { + if (NewWeatherType == WEATHER_RAINY || NewWeatherType == WEATHER_HURRICANE) + WetRoads = 1.0f; + else + WetRoads = 1.0f - InterpolationValue; + } + else { + if (NewWeatherType == WEATHER_RAINY || NewWeatherType == WEATHER_HURRICANE) + WetRoads = InterpolationValue; + else + WetRoads = 0.0f; + } + + // Rain + float fNewRain; + if (NewWeatherType == WEATHER_RAINY || NewWeatherType == WEATHER_HURRICANE) { + // if raining for >1 hour, values: 0, 0.33, switching every ~16.5s + fNewRain = (((uint16)CTimer::GetTimeInMilliseconds() >> 14) & 1) * 0.33f; + if (OldWeatherType != WEATHER_RAINY && OldWeatherType != WEATHER_HURRICANE) { + if (InterpolationValue < 0.4f) + // if rain has just started (<24 minutes), always 0.5 + fNewRain = 0.5f; + else + // if rain is ongoing for >24 minutes, values: 0.25, 0.5, switching every ~16.5s + fNewRain = 0.25f + (((uint16)CTimer::GetTimeInMilliseconds() >> 14) & 1) * 0.25f; + } + fNewRain = Max(fNewRain, 0.5f); + } + else + fNewRain = 0.0f; + Rain = fNewRain; + + // Clouds + if (OldWeatherType != WEATHER_SUNNY && OldWeatherType != WEATHER_EXTRA_SUNNY) + CloudCoverage = 1.0f - InterpolationValue; + else + CloudCoverage = 0.0f; + if (NewWeatherType != WEATHER_SUNNY && OldWeatherType != WEATHER_EXTRA_SUNNY) + CloudCoverage += InterpolationValue; + + // Fog + if (OldWeatherType == WEATHER_FOGGY) + Foggyness = 1.0f - InterpolationValue; + else + Foggyness = 0.0f; + if (NewWeatherType == WEATHER_FOGGY) + Foggyness += InterpolationValue; + + // Extra Sunnyness + if (OldWeatherType == WEATHER_EXTRA_SUNNY) + ExtraSunnyness = 1.0f - InterpolationValue; + else + ExtraSunnyness = 0.0f; + if (NewWeatherType == WEATHER_EXTRA_SUNNY) + ExtraSunnyness += InterpolationValue; + + // Rainbow + if (OldWeatherType == WEATHER_CLOUDY && (NewWeatherType == WEATHER_SUNNY || NewWeatherType == WEATHER_EXTRA_SUNNY) && + InterpolationValue < 0.5f && CClock::GetHours() > 6 && CClock::GetHours() < 21) + Rainbow = 1.0f - 4.0f * Abs(InterpolationValue - 0.25f) / 4.0f; + else + Rainbow = 0.0f; + + // Sun Glare + if (OldWeatherType == WEATHER_EXTRA_SUNNY) + SunGlare = 1.0f - InterpolationValue; + else + SunGlare = 0.0f; + if (NewWeatherType == WEATHER_EXTRA_SUNNY) + SunGlare += InterpolationValue; + + if (SunGlare > 0.0f) { + SunGlare *= Min(1.0f, 7.0 * CTimeCycle::GetSunDirection().z); + SunGlare = Clamp(SunGlare, 0.0f, 1.0f); + if (!CSpecialFX::bSnapShotActive) + SunGlare *= (1.0f - (CGeneral::GetRandomNumber()&0x1F)*0.007f); + } + + Wind = InterpolationValue * Windyness[NewWeatherType] + (1.0f - InterpolationValue) * Windyness[OldWeatherType]; + WindClipped = Min(1.0f, Wind); + + if (CClock::GetHours() > 20) + TrafficLightBrightness = 1.0f; + else if (CClock::GetHours() > 19) + TrafficLightBrightness = CClock::GetMinutes() / 60.0f; + else if (CClock::GetHours() > 6) + TrafficLightBrightness = 0.0f; + else if (CClock::GetHours() > 5) + TrafficLightBrightness = 1.0f - CClock::GetMinutes() / 60.0f; + else + TrafficLightBrightness = 1.0f; + TrafficLightBrightness = Max(WetRoads, TrafficLightBrightness); + TrafficLightBrightness = Max(Foggyness, TrafficLightBrightness); + TrafficLightBrightness = Max(Rain, TrafficLightBrightness); + + AddRain(); + + if ((NewWeatherType == WEATHER_SUNNY || NewWeatherType == WEATHER_EXTRA_SUNNY) && + !CGame::IsInInterior() && !CCutsceneMgr::IsRunning() && (CTimer::GetFrameCounter() & 7) == 0) { +#ifdef FIX_BUGS + if (FindPlayerPed() && (!FindPlayerPed()->CheckIfInTheAir() || FindPlayerPed()->CheckIfInTheAir() && FindPlayerPed()->GetPosition().z < 7.5f && + CClock::GetHours() > 6 && CClock::GetHours() < 18)) +#else + if (!FindPlayerPed()->CheckIfInTheAir() || FindPlayerPed()->CheckIfInTheAir() && FindPlayerPed()->GetPosition().z < 7.5f && + CClock::GetHours() > 6 && CClock::GetHours() < 18) +#endif + AddHeatHaze(); + } + + if ((NewWeatherType == WEATHER_SUNNY || NewWeatherType == WEATHER_EXTRA_SUNNY) && !CGame::IsInInterior() && !CCutsceneMgr::IsRunning()) + AddBeastie(); +} + +void CWeather::AddHeatHaze() +{ + if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || + TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED) + return; + CVector pos; + pos.x = SCREEN_WIDTH*0.5f; + if(TheCamera.GetLookingForwardFirstPerson()) + pos.y = CGeneral::GetRandomNumberInRange(SCREEN_HEIGHT*0.25f, SCREEN_HEIGHT*0.9f); + else + pos.y = CGeneral::GetRandomNumberInRange(SCREEN_HEIGHT*0.4f, SCREEN_HEIGHT*0.9f); + pos.z = 100.0f; + CParticle::AddParticle(PARTICLE_HEATHAZE_IN_DIST, pos, CVector(0.0f, 0.0f, 0.0f)); +} + +void CWeather::AddBeastie() +{ + if(FindPlayerVehicle() || CTimer::GetFrameCounter()%10 || (CGeneral::GetRandomNumber()&5) == 0) + return; + CVector pos = TheCamera.GetPosition(); + float dist = CGeneral::GetRandomNumberInRange(90.0f, 60.0f); + int angle = CGeneral::GetRandomNumber() % CParticle::SIN_COS_TABLE_SIZE; + float c = CParticle::m_CosTable[angle]; + float s = CParticle::m_SinTable[angle]; + pos.x += dist*(c - s); + pos.y += dist*(c + s); + pos.z += CGeneral::GetRandomNumberInRange(7.5f, 30.0f); + CParticle::AddParticle(PARTICLE_BEASTIE, pos, CVector(0.0f, 0.0f, 0.0f)); +} + +void CWeather::ForceWeather(int16 weather) +{ + ForcedWeatherType = weather; +} + +void CWeather::ForceWeatherNow(int16 weather) +{ + OldWeatherType = weather; + NewWeatherType = weather; + ForcedWeatherType = weather; +} + +void CWeather::ReleaseWeather() +{ + ForcedWeatherType = -1; +} + +void CWeather::AddSplashesDuringHurricane() +{ + RwRGBA colour = { 255, 255, 255, 32 }; + CVector pos = TheCamera.pTargetEntity ? TheCamera.pTargetEntity->GetPosition() : TheCamera.GetPosition(); + bool foundGround; + float groundZ = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &foundGround) + 0.1f; + if(!foundGround) + groundZ = pos.z + 0.5f; + for(int i = 0; i < 20; i++){ + float dist = (CGeneral::GetRandomNumber()&0xFF)/255.0f + + CGeneral::GetRandomNumberInRange(-10.0f, 30.0f); + float angle; + uint8 rnd = CGeneral::GetRandomNumber(); + if(rnd&1) + angle = (CGeneral::GetRandomNumber()&0x7F)/128.0f * TWOPI; + else + angle = TheCamera.Orientation + (rnd-128)/160.0f; + pos.x = TheCamera.GetPosition().x + dist*Sin(angle); + pos.y = TheCamera.GetPosition().y + dist*Cos(angle); + pos.z = groundZ; + if(foundGround) + CParticle::AddParticle(PARTICLE_GROUND_STEAM, pos, CVector(-0.002f, -0.002f, 0.015f), nil, 0.0f, colour); + } +} + +static int startStreamAfterRain; + +void CWeather::AddStreamAfterRain() +{ + if(CClock::GetHours() > 6 && CClock::GetHours() < 18){ + RwRGBA colour = { 255, 255, 255, 24 }; + CVector pos = TheCamera.pTargetEntity ? TheCamera.pTargetEntity->GetPosition() : TheCamera.GetPosition(); + bool foundGround; + float groundZ = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &foundGround) + 0.2f; + if(!foundGround) + groundZ = pos.z + 0.75f; + for(int i = 0; i < 20; i++){ + float dist = (CGeneral::GetRandomNumber()&0xFF)/255.0f + + CGeneral::GetRandomNumberInRange(-10.0f, 30.0f); + float angle; + uint8 rnd = CGeneral::GetRandomNumber(); + if(rnd&1) + angle = (CGeneral::GetRandomNumber()&0x7F)/128.0f * TWOPI; + else + angle = TheCamera.Orientation + (rnd-128)/160.0f; + pos.x = TheCamera.GetPosition().x + dist*Sin(angle); + pos.y = TheCamera.GetPosition().y + dist*Cos(angle); + pos.z = groundZ; + CParticle::AddParticle(PARTICLE_GROUND_STEAM, pos, CVector(0.0f, 0.0f, 0.015f), nil, 0.0f, colour); + } + }else{ + startStreamAfterRain = 0; + StreamAfterRainTimer = 800; + } +} + +void CWeather::AddRain() +{ + if (CCullZones::CamNoRain() || CCullZones::PlayerNoRain()) + return; + if (TheCamera.GetLookingLRBFirstPerson()) { + CVehicle* pVehicle = FindPlayerVehicle(); + if (pVehicle && pVehicle->CarHasRoof()) { + CParticle::RemovePSystem(PARTICLE_RAINDROP_2D); + return; + } + } + + if(Rain > 0.0){ + startStreamAfterRain = 1; + StreamAfterRainTimer = 800; + }else if(startStreamAfterRain){ + if(StreamAfterRainTimer > 0){ + AddStreamAfterRain(); + StreamAfterRainTimer--; + }else{ + startStreamAfterRain = 0; + StreamAfterRainTimer = 800; + } + } + + if (Wind > 1.1f) + AddSplashesDuringHurricane(); + + if (Rain <= 0.1f) + return; + static RwRGBA colour; + int numDrops = 5.0f * Rain; + int numSplashes = 2.0f * Rain; + CVector pos, dir; + for(int i = 0; i < numDrops; i++){ + pos.x = CGeneral::GetRandomNumberInRange(0, (int)SCREEN_WIDTH); + pos.y = CGeneral::GetRandomNumberInRange(0, (int)SCREEN_HEIGHT/5); + pos.z = 0.0f; + dir.x = 0.0f; + dir.y = CGeneral::GetRandomNumberInRange(30.0f, 40.0f); + dir.z = 0.0f; + CParticle::AddParticle(PARTICLE_RAINDROP_2D, pos, dir, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.75f), 0, 0, (int)Rain&3, 0); + + pos.x = CGeneral::GetRandomNumberInRange(0, (int)SCREEN_WIDTH); + pos.y = CGeneral::GetRandomNumberInRange((int)SCREEN_HEIGHT/5, (int)SCREEN_HEIGHT/2); + pos.z = 0.0f; + dir.x = 0.0f; + dir.y = CGeneral::GetRandomNumberInRange(30.0f, 40.0f); + dir.z = 0.0f; + CParticle::AddParticle(PARTICLE_RAINDROP_2D, pos, dir, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.75f), 0, 0, (int)Rain&3, 0); + + pos.x = CGeneral::GetRandomNumberInRange(0, (int)SCREEN_WIDTH); + pos.y = 0.0f; + pos.z = 0.0f; + dir.x = 0.0f; + dir.y = CGeneral::GetRandomNumberInRange(30.0f, 40.0f); + dir.z = 0.0f; + CParticle::AddParticle(PARTICLE_RAINDROP_2D, pos, dir, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.75f), 0, 0, (int)Rain&3, 0); + + float dist = CGeneral::GetRandomNumberInRange(0.0f, Max(10.0f*Rain, 40.0f)/2.0f); + float angle; + uint8 rnd = CGeneral::GetRandomNumber(); + if(rnd&1) + angle = (CGeneral::GetRandomNumber()&0x7F)/128.0f * TWOPI; + else + angle = TheCamera.Orientation + (rnd-128)/160.0f; + pos.x = TheCamera.GetPosition().x + dist*Sin(angle); + pos.y = TheCamera.GetPosition().y + dist*Cos(angle); + pos.z = 0.0f; + CColPoint point; + CEntity *ent; + if(CWorld::ProcessVerticalLine(pos+CVector(0.0f, 0.0f, 40.0f), -40.0f, point, ent, true, false, false, false, true, false, nil)){ + pos.z = point.point.z; + for(int j = 0; j < numSplashes+15; j++){ + CVector pos2 = pos; + pos2.x += CGeneral::GetRandomNumberInRange(-15.0f, 15.0f); + pos2.y += CGeneral::GetRandomNumberInRange(-15.0f, 15.0f); + if(CGeneral::GetRandomNumber() & 1) + CParticle::AddParticle(PARTICLE_RAIN_SPLASH, pos2, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, colour); + else + CParticle::AddParticle(PARTICLE_RAIN_SPLASHUP, pos2, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, colour); + } + } + } +} + +void RenderOneRainStreak(CVector pos, CVector unused, int intensity, bool scale, float distance) +{ + static float RandomTex; + static float RandomTexX; + static float RandomTexY; + TempBufferRenderIndexList[TempBufferIndicesStored + 0] = TempBufferVerticesStored + 0; + TempBufferRenderIndexList[TempBufferIndicesStored + 1] = TempBufferVerticesStored + 2; + TempBufferRenderIndexList[TempBufferIndicesStored + 2] = TempBufferVerticesStored + 1; + TempBufferRenderIndexList[TempBufferIndicesStored + 3] = TempBufferVerticesStored + 0; + TempBufferRenderIndexList[TempBufferIndicesStored + 4] = TempBufferVerticesStored + 3; + TempBufferRenderIndexList[TempBufferIndicesStored + 5] = TempBufferVerticesStored + 2; + TempBufferRenderIndexList[TempBufferIndicesStored + 6] = TempBufferVerticesStored + 1; + TempBufferRenderIndexList[TempBufferIndicesStored + 7] = TempBufferVerticesStored + 2; + TempBufferRenderIndexList[TempBufferIndicesStored + 8] = TempBufferVerticesStored + 4; + TempBufferRenderIndexList[TempBufferIndicesStored + 9] = TempBufferVerticesStored + 2; + TempBufferRenderIndexList[TempBufferIndicesStored + 10] = TempBufferVerticesStored + 3; + TempBufferRenderIndexList[TempBufferIndicesStored + 11] = TempBufferVerticesStored + 4; + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored + 0], 0, 0, 0, 0); + RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored + 0], pos.x + 11.0f * TheCamera.GetUp().x, pos.y + 11.0f * TheCamera.GetUp().y, pos.z + 11.0f * TheCamera.GetUp().z); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored + 1], 0, 0, 0, 0); + RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored + 1], pos.x - 9.0f * TheCamera.GetRight().x, pos.y - 9.0f * TheCamera.GetRight().y, pos.z - 9.0f * TheCamera.GetRight().z); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored + 2], RAIN_COLOUR_R * intensity / 256, RAIN_COLOUR_G * intensity / 256, RAIN_COLOUR_B * intensity / 256, RAIN_ALPHA); + RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored + 2], pos.x, pos.y, pos.z); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored + 3], 0, 0, 0, 0); + RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored + 3], pos.x + 9.0f * TheCamera.GetRight().x, pos.y + 9.0f * TheCamera.GetRight().y, pos.z + 9.0f * TheCamera.GetRight().z); + RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored + 4], 0, 0, 0, 0); + RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored + 4], pos.x - 11.0f * TheCamera.GetUp().x, pos.y - 11.0f * TheCamera.GetUp().y, pos.z - 11.0f * TheCamera.GetUp().z); + float u = STREAK_U; + float v = STREAK_V; + if (scale) { + u *= LARGE_STREAK_COEFFICIENT; + v *= LARGE_STREAK_COEFFICIENT; + } + float distance_coefficient; + if (distance < STREAK_MIN_DISTANCE) + distance_coefficient = 1.0f; + else if (distance > STREAK_MAX_DISTANCE) + distance_coefficient = 0.5f; + else + distance_coefficient = 1.0f - 0.5f * (distance - STREAK_MIN_DISTANCE) / (STREAK_MAX_DISTANCE - STREAK_MIN_DISTANCE); + u *= distance_coefficient; + v *= distance_coefficient; + if (!CTimer::GetIsPaused()) { + RandomTex = 0.0f; + RandomTexX = 0.0f; + RandomTexY = 0.0f; + } + RwIm3DVertexSetU(&TempBufferRenderVertices[TempBufferVerticesStored + 0], 0.5f * u - RandomTex + RandomTexX); + RwIm3DVertexSetV(&TempBufferRenderVertices[TempBufferVerticesStored + 0], -v * 0.5f + RandomTexY); + RwIm3DVertexSetU(&TempBufferRenderVertices[TempBufferVerticesStored + 1], RandomTexX); + RwIm3DVertexSetV(&TempBufferRenderVertices[TempBufferVerticesStored + 1], RandomTexY); + RwIm3DVertexSetU(&TempBufferRenderVertices[TempBufferVerticesStored + 2], 0.5f * u + RandomTexX); + RwIm3DVertexSetV(&TempBufferRenderVertices[TempBufferVerticesStored + 2], RandomTexY); + RwIm3DVertexSetU(&TempBufferRenderVertices[TempBufferVerticesStored + 3], u + RandomTexX); + RwIm3DVertexSetV(&TempBufferRenderVertices[TempBufferVerticesStored + 3], RandomTexY); + RwIm3DVertexSetU(&TempBufferRenderVertices[TempBufferVerticesStored + 4], 0.5f * u + RandomTex + RandomTexX); + RwIm3DVertexSetV(&TempBufferRenderVertices[TempBufferVerticesStored + 5], 0.5f * v + RandomTexY); + TempBufferIndicesStored += 12; + TempBufferVerticesStored += 5; +} + +void CWeather::RenderRainStreaks(void) +{ + if (CTimer::GetIsCodePaused()) + return; + int base_intensity = (64.0f - CTimeCycle::GetFogReduction()) / 64.0f * int(255 * Rain); + if (base_intensity == 0) + return; + if (TheCamera.m_CameraAverageSpeed > 1.75f) + return; + TempBufferIndicesStored = 0; + TempBufferVerticesStored = 0; + for (int i = 0; i < NUM_RAIN_STREAKS; i++) { + if (Streaks[i].timer) { + float secondsElapsed = (CTimer::GetTimeInMilliseconds() - Streaks[i].timer) / 1024.0f; + if (secondsElapsed > STREAK_LIFETIME) + Streaks[i].timer = 0; + else{ + int intensity; + if (secondsElapsed < STREAK_INTEROLATION_TIME) + intensity = base_intensity * 0.25f * secondsElapsed / STREAK_INTEROLATION_TIME; + else if (secondsElapsed > (STREAK_LIFETIME - STREAK_INTEROLATION_TIME)) + intensity = (STREAK_LIFETIME - secondsElapsed) * 0.25f * base_intensity / STREAK_INTEROLATION_TIME; + else + intensity = base_intensity * 0.25f; + CVector dir = Streaks[i].direction; + dir.Normalise(); + CVector pos = Streaks[i].position + secondsElapsed * Streaks[i].direction; + RenderOneRainStreak(pos, dir, intensity, false, (pos - TheCamera.GetPosition()).Magnitude()); +#ifndef FIX_BUGS // remove useless code + if (secondsElapsed > 1.0f && secondsElapsed < STREAK_LIFETIME - 1.0f) { + CGeneral::GetRandomNumber(), CGeneral::GetRandomNumber(); + } +#endif + } + } + else if ((CGeneral::GetRandomNumber() & 0xF00) == 0){ + // 1/16 probability + Streaks[i].direction = CVector(0.0f, 0.0f, -12.0f); + Streaks[i].position = 6.0f * TheCamera.GetForward() + TheCamera.GetPosition() + CVector(-1.8f * Streaks[i].direction.x, -1.8f * Streaks[i].direction.y, 8.0f); + if (!CCutsceneMgr::IsRunning()) { + Streaks[i].position.x += 2.0f * FindPlayerSpeed().x * 60.0f; + Streaks[i].position.y += 2.0f * FindPlayerSpeed().y * 60.0f; + } + else + Streaks[i].position += (TheCamera.GetPosition() - TheCamera.m_RealPreviousCameraPosition) * 20.0f; + Streaks[i].position.x += ((CGeneral::GetRandomNumber() & 255) - 128) * 0.04f; + Streaks[i].position.y += ((CGeneral::GetRandomNumber() & 255) - 128) * 0.04f; + Streaks[i].timer = CTimer::GetTimeInMilliseconds(); + } + } + if (TempBufferIndicesStored){ + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEFOGTYPE, (void*)rwFOGTYPELINEAR); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpRainDropTex)); + if (RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, 1)) + { + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored); + RwIm3DEnd(); + } + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + } + TempBufferVerticesStored = 0; + TempBufferIndicesStored = 0; +} + +#ifdef SECUROM +void CWeather::ForceHurricaneWeather() +{ + for (int i = 0; i < ARRAY_SIZE(WeatherTypesList_WithHurricanes); i++) + { + WeatherTypesList[i] = WEATHER_HURRICANE; + WeatherTypesList_WithHurricanes[i] = WEATHER_HURRICANE; + } + + CWeather::OldWeatherType = WEATHER_HURRICANE; + CWeather::NewWeatherType = WEATHER_HURRICANE; + CWeather::ForcedWeatherType = WEATHER_HURRICANE; +} +#endif diff --git a/src/miami/renderer/Weather.h b/src/miami/renderer/Weather.h new file mode 100644 index 00000000..bda57d55 --- /dev/null +++ b/src/miami/renderer/Weather.h @@ -0,0 +1,73 @@ +enum { + WEATHER_RANDOM = -1, + WEATHER_SUNNY = 0, + WEATHER_CLOUDY, + WEATHER_RAINY, + WEATHER_FOGGY, + WEATHER_EXTRA_SUNNY, + WEATHER_HURRICANE, + WEATHER_TOTAL, + + WEATHER_EXTRACOLOURS = 6 +}; + +class CWeather +{ +public: + static int32 SoundHandle; + + static int32 WeatherTypeInList; + static int16 OldWeatherType; + static int16 NewWeatherType; + static int16 ForcedWeatherType; + + static bool LightningFlash; + static bool LightningBurst; + static uint32 LightningStart; + static uint32 LightningFlashLastChange; + static uint32 WhenToPlayLightningSound; + static uint32 LightningDuration; + static int32 StreamAfterRainTimer; + + static float ExtraSunnyness; + static float Foggyness; + static float CloudCoverage; + static float Wind; + static float Rain; + static float InterpolationValue; + static float WetRoads; + static float Rainbow; + static float SunGlare; + static float WindClipped; + static float TrafficLightBrightness; + + static bool bScriptsForceRain; + + static void RenderRainStreaks(void); + static void Update(void); + static void Init(void); + + static void ReleaseWeather(); + static void ForceWeather(int16); + static void ForceWeatherNow(int16); + static void AddSplashesDuringHurricane(); + static void AddStreamAfterRain(); + static void AddRain(); + static void AddHeatHaze(); + static void AddBeastie(); + + static void ForceHurricaneWeather(); +}; + +enum { + NUM_RAIN_STREAKS = 35 +}; + +struct tRainStreak +{ + CVector position; + CVector direction; + uint32 timer; +}; + +extern RwTexture* gpRainDropTex; \ No newline at end of file diff --git a/src/miami/renderer/WindModifiers.cpp b/src/miami/renderer/WindModifiers.cpp new file mode 100644 index 00000000..3bd6ac9c --- /dev/null +++ b/src/miami/renderer/WindModifiers.cpp @@ -0,0 +1,52 @@ +#include "common.h" +#include "WindModifiers.h" +#include "Camera.h" +#include "General.h" + +#define MAX_HEIGHT_DIST 40.0f +#define MIN_FADE_DIST 20.0f +#define MAX_FADE_DIST 50.0f + +CWindModifiers Array[16]; +int32 CWindModifiers::Number; + +void +CWindModifiers::RegisterOne(CVector pos, int32 type = 1) +{ + if (CWindModifiers::Number < 16 && (pos - TheCamera.GetPosition()).Magnitude() < 100.0f) { + Array[Number].m_pos = pos; + Array[Number].m_type = type; + Number++; + } +} + +bool +CWindModifiers::FindWindModifier(CVector pos, float *x, float *y) +{ + bool bWasWindModifierFound = false; + CVector2D dir; + for (int i = 0; i < Number; i++) { + if (Array[i].m_type == 1) { + float zDist = Abs(15.0f + pos.z - Array[i].m_pos.z); + + if (zDist < MAX_HEIGHT_DIST) { + float dist = (pos - Array[i].m_pos).Magnitude(); + if (dist < MAX_FADE_DIST) { + float distFade = dist < MIN_FADE_DIST ? 1.0f : 1.0f - (dist - MIN_FADE_DIST) / (MAX_FADE_DIST - MIN_FADE_DIST); + float heightFade = 1.0f - zDist / MAX_HEIGHT_DIST; + float fade = distFade * heightFade * 0.5f; + dir = (pos - Array[i].m_pos) * fade / dist; + bWasWindModifierFound = true; + } + } + } + } + + if (bWasWindModifierFound) { + float directionMult = ((CGeneral::GetRandomNumber() & 0x1F) - 16) * 0.0035f + 1.0f; + *x += dir.x * directionMult; + *y += dir.y * directionMult; + } + + return bWasWindModifierFound; +} diff --git a/src/miami/renderer/WindModifiers.h b/src/miami/renderer/WindModifiers.h new file mode 100644 index 00000000..7c2e57bd --- /dev/null +++ b/src/miami/renderer/WindModifiers.h @@ -0,0 +1,11 @@ +#pragma once + +class CWindModifiers +{ + CVector m_pos; + int32 m_type; +public: + static int32 Number; + static void RegisterOne(CVector pos, int32 windSourceType); + static bool FindWindModifier(CVector pos, float *x, float *y); +}; diff --git a/src/miami/rw/ClumpRead.cpp b/src/miami/rw/ClumpRead.cpp new file mode 100644 index 00000000..9c027dc5 --- /dev/null +++ b/src/miami/rw/ClumpRead.cpp @@ -0,0 +1,223 @@ +#include "common.h" + +struct rpGeometryList +{ + RpGeometry **geometries; + int32 numGeoms; +}; + +struct rpAtomicBinary +{ + RwInt32 frameIndex; + RwInt32 geomIndex; + RwInt32 flags; + RwInt32 unused; +}; + +static int32 numberGeometrys; +static int32 streamPosition; +static rpGeometryList gGeomList; +static rwFrameList gFrameList; +static RpClumpChunkInfo gClumpInfo; + +rpGeometryList* +GeometryListStreamRead1(RwStream *stream, rpGeometryList *geomlist) +{ + int i; + RwUInt32 size, version; + RwInt32 numGeoms; + + numberGeometrys = 0; + if(!RwStreamFindChunk(stream, rwID_STRUCT, &size, &version)) + return nil; + assert(size == 4); + if(RwStreamRead(stream, &numGeoms, 4) != 4) + return nil; + + numberGeometrys = numGeoms/2; + geomlist->numGeoms = numGeoms; + if(geomlist->numGeoms > 0){ + geomlist->geometries = (RpGeometry**)RwMalloc(geomlist->numGeoms * sizeof(RpGeometry*)); + if(geomlist->geometries == nil) + return nil; + memset(geomlist->geometries, 0, geomlist->numGeoms * sizeof(RpGeometry*)); + }else + geomlist->geometries = nil; + + for(i = 0; i < numberGeometrys; i++){ + if(!RwStreamFindChunk(stream, rwID_GEOMETRY, nil, &version)) + return nil; + geomlist->geometries[i] = RpGeometryStreamRead(stream); + if(geomlist->geometries[i] == nil) + return nil; + } + + return geomlist; +} + +rpGeometryList* +GeometryListStreamRead2(RwStream *stream, rpGeometryList *geomlist) +{ + int i; + RwUInt32 version; + + for(i = numberGeometrys; i < geomlist->numGeoms; i++){ + if(!RwStreamFindChunk(stream, rwID_GEOMETRY, nil, &version)) + return nil; + geomlist->geometries[i] = RpGeometryStreamRead(stream); + if(geomlist->geometries[i] == nil) + return nil; + } + + return geomlist; +} + +void +GeometryListDeinitialize(rpGeometryList *geomlist) +{ + int i; + + for(i = 0; i < geomlist->numGeoms; i++) + if(geomlist->geometries[i]) + RpGeometryDestroy(geomlist->geometries[i]); + + if(geomlist->numGeoms){ + RwFree(geomlist->geometries); + geomlist->numGeoms = 0; + } +} + +RpAtomic* +ClumpAtomicStreamRead(RwStream *stream, rwFrameList *frmList, rpGeometryList *geomList) +{ + RwUInt32 size, version; + rpAtomicBinary a; + RpAtomic *atomic; + + numberGeometrys = 0; + if(!RwStreamFindChunk(stream, rwID_STRUCT, &size, &version)) + return nil; + assert(size <= sizeof(rpAtomicBinary)); + if(RwStreamRead(stream, &a, size) != size) + return nil; + + atomic = RpAtomicCreate(); + if(atomic == nil) + return nil; + + RpAtomicSetFlags(atomic, a.flags); + + if(frmList->numFrames){ + assert(a.frameIndex < frmList->numFrames); + RpAtomicSetFrame(atomic, frmList->frames[a.frameIndex]); + } + + if(geomList->numGeoms){ + assert(a.geomIndex < geomList->numGeoms); + RpAtomicSetGeometry(atomic, geomList->geometries[a.geomIndex], 0); + }else{ + RpGeometry *geom; + if(!RwStreamFindChunk(stream, rwID_GEOMETRY, nil, &version)){ + RpAtomicDestroy(atomic); + return nil; + } + geom = RpGeometryStreamRead(stream); + if(geom == nil){ + RpAtomicDestroy(atomic); + return nil; + } + RpAtomicSetGeometry(atomic, geom, 0); + RpGeometryDestroy(geom); + } + + return atomic; +} + +bool +RpClumpGtaStreamRead1(RwStream *stream) +{ + RwUInt32 size, version; + + if(!RwStreamFindChunk(stream, rwID_STRUCT, &size, &version)) + return false; + if(version >= 0x33000){ + assert(size == 12); + if(RwStreamRead(stream, &gClumpInfo, 12) != 12) + return false; + }else{ + assert(size == 4); + if(RwStreamRead(stream, &gClumpInfo, 4) != 4) + return false; + } + + if(!RwStreamFindChunk(stream, rwID_FRAMELIST, nil, &version)) + return false; + if(rwFrameListStreamRead(stream, &gFrameList) == nil) + return false; + + if(!RwStreamFindChunk(stream, rwID_GEOMETRYLIST, nil, &version)){ + rwFrameListDeinitialize(&gFrameList); + return false; + } + if(GeometryListStreamRead1(stream, &gGeomList) == nil){ + rwFrameListDeinitialize(&gFrameList); + return false; + } + streamPosition = STREAMPOS(stream); + return true; +} + +RpClump* +RpClumpGtaStreamRead2(RwStream *stream) +{ + int i; + RwUInt32 version; + RpAtomic *atomic; + RpClump *clump; + + clump = RpClumpCreate(); + if(clump == nil) + return nil; + + RwStreamSkip(stream, streamPosition - STREAMPOS(stream)); + + if(GeometryListStreamRead2(stream, &gGeomList) == nil){ + GeometryListDeinitialize(&gGeomList); + rwFrameListDeinitialize(&gFrameList); + RpClumpDestroy(clump); + return nil; + } + + RpClumpSetFrame(clump, gFrameList.frames[0]); + + for(i = 0; i < gClumpInfo.numAtomics; i++){ + if(!RwStreamFindChunk(stream, rwID_ATOMIC, nil, &version)){ + GeometryListDeinitialize(&gGeomList); + rwFrameListDeinitialize(&gFrameList); + RpClumpDestroy(clump); + return nil; + } + + atomic = ClumpAtomicStreamRead(stream, &gFrameList, &gGeomList); + if(atomic == nil){ + GeometryListDeinitialize(&gGeomList); + rwFrameListDeinitialize(&gFrameList); + RpClumpDestroy(clump); + return nil; + } + + RpClumpAddAtomic(clump, atomic); + } + + GeometryListDeinitialize(&gGeomList); + rwFrameListDeinitialize(&gFrameList); + return clump; +} + +void +RpClumpGtaCancelStream(void) +{ + GeometryListDeinitialize(&gGeomList); + rwFrameListDeinitialize(&gFrameList); + gFrameList.numFrames = 0; +} diff --git a/src/miami/rw/Lights.cpp b/src/miami/rw/Lights.cpp new file mode 100644 index 00000000..772e1961 --- /dev/null +++ b/src/miami/rw/Lights.cpp @@ -0,0 +1,389 @@ +#include "common.h" +#include +#include + +#include "Lights.h" +#include "Timer.h" +#include "Timecycle.h" +#include "Coronas.h" +#include "Weather.h" +#include "ZoneCull.h" +#include "Frontend.h" +#include "MBlur.h" + +RpLight *pAmbient; +RpLight *pDirect; +RpLight *pExtraDirectionals[4] = { nil }; +int LightStrengths[4]; +int NumExtraDirLightsInWorld; + +RwRGBAReal AmbientLightColourForFrame; +RwRGBAReal AmbientLightColourForFrame_PedsCarsAndObjects; +RwRGBAReal DirectionalLightColourForFrame; + +RwRGBAReal AmbientLightColour; +RwRGBAReal DirectionalLightColour; + +#ifdef EXTENDED_COLOURFILTER +#include "postfx.h" +#define USEBLURCOLORS CPostFX::UseBlurColours() +#else +#define USEBLURCOLORS CMBlur::BlurOn +#endif + +void +SetLightsWithTimeOfDayColour(RpWorld *) +{ + CVector vec1, vec2, vecsun; + RwMatrix mat; + + if(pAmbient){ + if(USEBLURCOLORS){ + AmbientLightColourForFrame.red = CTimeCycle::GetAmbientRed_Bl() * CCoronas::LightsMult; + AmbientLightColourForFrame.green = CTimeCycle::GetAmbientGreen_Bl() * CCoronas::LightsMult; + AmbientLightColourForFrame.blue = CTimeCycle::GetAmbientBlue_Bl() * CCoronas::LightsMult; + }else{ + AmbientLightColourForFrame.red = CTimeCycle::GetAmbientRed() * CCoronas::LightsMult; + AmbientLightColourForFrame.green = CTimeCycle::GetAmbientGreen() * CCoronas::LightsMult; + AmbientLightColourForFrame.blue = CTimeCycle::GetAmbientBlue() * CCoronas::LightsMult; + } + + if(USEBLURCOLORS){ + AmbientLightColourForFrame_PedsCarsAndObjects.red = CTimeCycle::GetAmbientRed_Obj_Bl() * CCoronas::LightsMult; + AmbientLightColourForFrame_PedsCarsAndObjects.green = CTimeCycle::GetAmbientGreen_Obj_Bl() * CCoronas::LightsMult; + AmbientLightColourForFrame_PedsCarsAndObjects.blue = CTimeCycle::GetAmbientBlue_Obj_Bl() * CCoronas::LightsMult; + }else{ + AmbientLightColourForFrame_PedsCarsAndObjects.red = CTimeCycle::GetAmbientRed_Obj() * CCoronas::LightsMult; + AmbientLightColourForFrame_PedsCarsAndObjects.green = CTimeCycle::GetAmbientGreen_Obj() * CCoronas::LightsMult; + AmbientLightColourForFrame_PedsCarsAndObjects.blue = CTimeCycle::GetAmbientBlue_Obj() * CCoronas::LightsMult; + } + + if(CWeather::LightningFlash && !CCullZones::CamNoRain()){ + AmbientLightColourForFrame.red = 1.0f; + AmbientLightColourForFrame.green = 1.0f; + AmbientLightColourForFrame.blue = 1.0f; + + AmbientLightColourForFrame_PedsCarsAndObjects.red = 1.0f; + AmbientLightColourForFrame_PedsCarsAndObjects.green = 1.0f; + AmbientLightColourForFrame_PedsCarsAndObjects.blue = 1.0f; + } + RpLightSetColor(pAmbient, &AmbientLightColourForFrame); + } + + if(pDirect){ + DirectionalLightColourForFrame.red = CTimeCycle::GetDirectionalRed() * CCoronas::LightsMult; + DirectionalLightColourForFrame.green = CTimeCycle::GetDirectionalGreen() * CCoronas::LightsMult; + DirectionalLightColourForFrame.blue = CTimeCycle::GetDirectionalBlue() * CCoronas::LightsMult; + RpLightSetColor(pDirect, &DirectionalLightColourForFrame); + + vecsun = CTimeCycle::m_VectorToSun[CTimeCycle::m_CurrentStoredValue]; + vec1 = CVector(0.0f, 0.0f, 1.0f); + vec2 = CrossProduct(vec1, vecsun); + vec2.Normalise(); + vec1 = CrossProduct(vec2, vecsun); + mat.at.x = -vecsun.x; + mat.at.y = -vecsun.y; + mat.at.z = -vecsun.z; + mat.right.x = vec1.x; + mat.right.y = vec1.y; + mat.right.z = vec1.z; + mat.up.x = vec2.x; + mat.up.y = vec2.y; + mat.up.z = vec2.z; + RwFrameTransform(RpLightGetFrame(pDirect), &mat, rwCOMBINEREPLACE); + } + + if(FrontEndMenuManager.m_PrefsBrightness > 256){ + float f1 = 2.0f * (FrontEndMenuManager.m_PrefsBrightness/256.0f - 1.0f) * 0.6f + 1.0f; + float f2 = 3.0f * (FrontEndMenuManager.m_PrefsBrightness/256.0f - 1.0f) * 0.6f + 1.0f; + + AmbientLightColourForFrame.red = Min(1.0f, AmbientLightColourForFrame.red * f2); + AmbientLightColourForFrame.green = Min(1.0f, AmbientLightColourForFrame.green * f2); + AmbientLightColourForFrame.blue = Min(1.0f, AmbientLightColourForFrame.blue * f2); + AmbientLightColourForFrame_PedsCarsAndObjects.red = Min(1.0f, AmbientLightColourForFrame_PedsCarsAndObjects.red * f1); + AmbientLightColourForFrame_PedsCarsAndObjects.green = Min(1.0f, AmbientLightColourForFrame_PedsCarsAndObjects.green * f1); + AmbientLightColourForFrame_PedsCarsAndObjects.blue = Min(1.0f, AmbientLightColourForFrame_PedsCarsAndObjects.blue * f1); +#ifdef FIX_BUGS + DirectionalLightColourForFrame.red = Min(1.0f, DirectionalLightColourForFrame.red * f1); + DirectionalLightColourForFrame.green = Min(1.0f, DirectionalLightColourForFrame.green * f1); + DirectionalLightColourForFrame.blue = Min(1.0f, DirectionalLightColourForFrame.blue * f1); +#else + DirectionalLightColourForFrame.red = Min(1.0f, AmbientLightColourForFrame.red * f1); + DirectionalLightColourForFrame.green = Min(1.0f, AmbientLightColourForFrame.green * f1); + DirectionalLightColourForFrame.blue = Min(1.0f, AmbientLightColourForFrame.blue * f1); +#endif + } +} + +RpWorld* +LightsCreate(RpWorld *world) +{ + int i; + RwRGBAReal color; + RwFrame *frame; + + if(world == nil) + return nil; + + pAmbient = RpLightCreate(rpLIGHTAMBIENT); + RpLightSetFlags(pAmbient, rpLIGHTLIGHTATOMICS); + color.red = 0.25f; + color.green = 0.25f; + color.blue = 0.2f; + RpLightSetColor(pAmbient, &color); + + pDirect = RpLightCreate(rpLIGHTDIRECTIONAL); + RpLightSetFlags(pDirect, rpLIGHTLIGHTATOMICS); + color.red = 1.0f; + color.green = 0.85f; + color.blue = 0.45f; + RpLightSetColor(pDirect, &color); + RpLightSetRadius(pDirect, 2.0f); + frame = RwFrameCreate(); + RpLightSetFrame(pDirect, frame); + RwV3d axis = { 1.0f, 1.0f, 0.0f }; + RwFrameRotate(frame, &axis, 160.0f, rwCOMBINEPRECONCAT); + + RpWorldAddLight(world, pAmbient); + RpWorldAddLight(world, pDirect); + + for(i = 0; i < NUMEXTRADIRECTIONALS; i++){ + pExtraDirectionals[i] = RpLightCreate(rpLIGHTDIRECTIONAL); + RpLightSetFlags(pExtraDirectionals[i], 0); + color.red = 1.0f; + color.green = 0.5f; + color.blue = 0.0f; + RpLightSetColor(pExtraDirectionals[i], &color); + RpLightSetRadius(pExtraDirectionals[i], 2.0f); + frame = RwFrameCreate(); + RpLightSetFrame(pExtraDirectionals[i], frame); + RpWorldAddLight(world, pExtraDirectionals[i]); + } + + return world; +} + +void +LightsDestroy(RpWorld *world) +{ + int i; + + if(world == nil) + return; + + if(pAmbient){ + RpWorldRemoveLight(world, pAmbient); + RpLightDestroy(pAmbient); + pAmbient = nil; + } + + if(pDirect){ + RpWorldRemoveLight(world, pDirect); + RwFrameDestroy(RpLightGetFrame(pDirect)); + RpLightDestroy(pDirect); + pDirect = nil; + } + + for(i = 0; i < NUMEXTRADIRECTIONALS; i++) + if(pExtraDirectionals[i]){ + RpWorldRemoveLight(world, pExtraDirectionals[i]); + RwFrameDestroy(RpLightGetFrame(pExtraDirectionals[i])); + RpLightDestroy(pExtraDirectionals[i]); + pExtraDirectionals[i] = nil; + } +} + +void +WorldReplaceNormalLightsWithScorched(RpWorld *world, float l) +{ + RwRGBAReal color; + color.red = l; + color.green = l; + color.blue = l; + RpLightSetColor(pAmbient, &color); + RpLightSetFlags(pDirect, 0); +} + +void +WorldReplaceScorchedLightsWithNormal(RpWorld *world) +{ + RpLightSetColor(pAmbient, &AmbientLightColourForFrame); + RpLightSetFlags(pDirect, rpLIGHTLIGHTATOMICS); +} + +void +AddAnExtraDirectionalLight(RpWorld *world, float dirx, float diry, float dirz, float red, float green, float blue) +{ + float strength; + int weakest; + int i, n; + RwRGBAReal color; + RwV3d *dir; + + strength = Max(Max(red, green), blue); + n = -1; + if(NumExtraDirLightsInWorld < NUMEXTRADIRECTIONALS) + n = NumExtraDirLightsInWorld; + else{ + weakest = strength; + for(i = 0; i < NUMEXTRADIRECTIONALS; i++) + if(LightStrengths[i] < weakest){ + weakest = LightStrengths[i]; + n = i; + } + } + + if(n < 0) + return; + + color.red = red; + color.green = green; + color.blue = blue; + RpLightSetColor(pExtraDirectionals[n], &color); + dir = RwMatrixGetAt(RwFrameGetMatrix(RpLightGetFrame(pExtraDirectionals[n]))); + dir->x = -dirx; + dir->y = -diry; + dir->z = -dirz; + RwMatrixUpdate(RwFrameGetMatrix(RpLightGetFrame(pExtraDirectionals[n]))); + RwFrameUpdateObjects(RpLightGetFrame(pExtraDirectionals[n])); + RpLightSetFlags(pExtraDirectionals[n], rpLIGHTLIGHTATOMICS); + LightStrengths[n] = strength; + NumExtraDirLightsInWorld = Min(NumExtraDirLightsInWorld+1, NUMEXTRADIRECTIONALS); +} + +void +RemoveExtraDirectionalLights(RpWorld *world) +{ + int i; + for(i = 0; i < NumExtraDirLightsInWorld; i++) + RpLightSetFlags(pExtraDirectionals[i], 0); + NumExtraDirLightsInWorld = 0; +} + +void +SetAmbientAndDirectionalColours(float f) +{ + AmbientLightColour.red = AmbientLightColourForFrame.red * f; + AmbientLightColour.green = AmbientLightColourForFrame.green * f; + AmbientLightColour.blue = AmbientLightColourForFrame.blue * f; + + DirectionalLightColour.red = DirectionalLightColourForFrame.red * f; + DirectionalLightColour.green = DirectionalLightColourForFrame.green * f; + DirectionalLightColour.blue = DirectionalLightColourForFrame.blue * f; + + RpLightSetColor(pAmbient, &AmbientLightColour); + RpLightSetColor(pDirect, &DirectionalLightColour); +} + +// unused +void +SetFlashyColours(float f) +{ + if(CTimer::GetTimeInMilliseconds() & 0x100){ + AmbientLightColour.red = 1.0f; + AmbientLightColour.green = 1.0f; + AmbientLightColour.blue = 1.0f; + + DirectionalLightColour.red = DirectionalLightColourForFrame.red; + DirectionalLightColour.green = DirectionalLightColourForFrame.green; + DirectionalLightColour.blue = DirectionalLightColourForFrame.blue; + + RpLightSetColor(pAmbient, &AmbientLightColour); + RpLightSetColor(pDirect, &DirectionalLightColour); + }else{ + SetAmbientAndDirectionalColours(f * 0.75f); + } +} + +// unused +void +SetFlashyColours_Mild(float f) +{ + if(CTimer::GetTimeInMilliseconds() & 0x100){ + AmbientLightColour.red = 0.65f; + AmbientLightColour.green = 0.65f; + AmbientLightColour.blue = 0.65f; + + DirectionalLightColour.red = DirectionalLightColourForFrame.red; + DirectionalLightColour.green = DirectionalLightColourForFrame.green; + DirectionalLightColour.blue = DirectionalLightColourForFrame.blue; + + RpLightSetColor(pAmbient, &AmbientLightColour); + RpLightSetColor(pDirect, &DirectionalLightColour); + }else{ + SetAmbientAndDirectionalColours(f * 0.9f); + } +} + +void +SetBrightMarkerColours(float f) +{ + AmbientLightColour.red = 0.6f; + AmbientLightColour.green = 0.6f; + AmbientLightColour.blue = 0.6f; + + DirectionalLightColour.red = (1.0f - DirectionalLightColourForFrame.red) * 0.4f + DirectionalLightColourForFrame.red; + DirectionalLightColour.green = (1.0f - DirectionalLightColourForFrame.green) * 0.4f + DirectionalLightColourForFrame.green; + DirectionalLightColour.blue = (1.0f - DirectionalLightColourForFrame.blue) * 0.4f + DirectionalLightColourForFrame.blue; + + RpLightSetColor(pAmbient, &AmbientLightColour); + RpLightSetColor(pDirect, &DirectionalLightColour); +} + +void +ReSetAmbientAndDirectionalColours(void) +{ + RpLightSetColor(pAmbient, &AmbientLightColourForFrame); + RpLightSetColor(pDirect, &DirectionalLightColourForFrame); +} + +void +DeActivateDirectional(void) +{ + RpLightSetFlags(pDirect, 0); +} + +void +ActivateDirectional(void) +{ + RpLightSetFlags(pDirect, rpLIGHTLIGHTATOMICS); +} + +RwRGBAReal FullLight = { 1.0f, 1.0f, 1.0f, 1.0f }; + +void +SetFullAmbient(void) +{ + RpLightSetColor(pAmbient, &FullLight); +} + +void +SetAmbientColours(void) +{ + RpLightSetColor(pAmbient, &AmbientLightColourForFrame); +} + +void +SetAmbientColoursForPedsCarsAndObjects(void) +{ + RpLightSetColor(pAmbient, &AmbientLightColourForFrame_PedsCarsAndObjects); +} + +uint8 IndicateR[] = { 0, 255, 0, 0, 255, 255, 0 }; +uint8 IndicateG[] = { 0, 0, 255, 0, 255, 0, 255 }; +uint8 IndicateB[] = { 0, 0, 0, 255, 0, 255, 255 }; + +void +SetAmbientColoursToIndicateRoadGroup(int i) +{ + AmbientLightColour.red = IndicateR[i%7]/255.0f; + AmbientLightColour.green = IndicateG[i%7]/255.0f; + AmbientLightColour.blue = IndicateB[i%7]/255.0f; + RpLightSetColor(pAmbient, &AmbientLightColour); +} + +void +SetAmbientColours(RwRGBAReal *color) +{ + RpLightSetColor(pAmbient, color); +} diff --git a/src/miami/rw/Lights.h b/src/miami/rw/Lights.h new file mode 100644 index 00000000..ad355adb --- /dev/null +++ b/src/miami/rw/Lights.h @@ -0,0 +1,27 @@ +#pragma once + +extern RpLight *pAmbient; +extern RpLight *pDirect; +extern RpLight *pExtraDirectionals[4]; +extern int LightStrengths[4]; +extern int NumExtraDirLightsInWorld; + +void SetLightsWithTimeOfDayColour(RpWorld *); +RpWorld *LightsCreate(RpWorld *world); +void LightsDestroy(RpWorld *world); +void WorldReplaceNormalLightsWithScorched(RpWorld *world, float l); +void WorldReplaceScorchedLightsWithNormal(RpWorld *world); +void AddAnExtraDirectionalLight(RpWorld *world, float dirx, float diry, float dirz, float red, float green, float blue); +void RemoveExtraDirectionalLights(RpWorld *world); +void SetAmbientAndDirectionalColours(float f); +void SetFlashyColours(float f); +void SetFlashyColours_Mild(float f); +void SetBrightMarkerColours(float f); +void ReSetAmbientAndDirectionalColours(void); +void DeActivateDirectional(void); +void ActivateDirectional(void); +void SetAmbientColours(void); +void SetAmbientColoursForPedsCarsAndObjects(void); +void SetAmbientColoursToIndicateRoadGroup(int i); +void SetAmbientColours(RwRGBAReal *color); +void SetFullAmbient(void); diff --git a/src/miami/rw/MemoryHeap.cpp b/src/miami/rw/MemoryHeap.cpp new file mode 100644 index 00000000..285a7c70 --- /dev/null +++ b/src/miami/rw/MemoryHeap.cpp @@ -0,0 +1,499 @@ +#include "common.h" +#include "main.h" +#include "FileMgr.h" +#include "Timer.h" +#include "ModelInfo.h" +#include "Streaming.h" +#include "FileLoader.h" +#include "MemoryHeap.h" + +// TODO(MIAMI) + +#ifdef USE_CUSTOM_ALLOCATOR + +//#define MEMORYHEAP_ASSERT(cond) { if (!(cond)) { printf("ASSERT File:%s Line:%d\n", __FILE__, __LINE__); exit(1); } } +//#define MEMORYHEAP_ASSERT_MESSAGE(cond, message) { if (!(cond)) { printf("ASSERT File:%s Line:%d:\n\t%s\n", __FILE__, __LINE__, message); exit(1); } } + +#define MEMORYHEAP_ASSERT(cond) assert(cond) +#define MEMORYHEAP_ASSERT_MESSAGE(cond, message) assert(cond) + +// registered pointers that we keep track of +void **gPtrList[4000]; +int32 numPtrs; +int32 gPosnInList; +// indices into the ptr list in here are free +CStack m_ptrListIndexStack; +// how much memory we've moved +uint32 memMoved; + +CMemoryHeap gMainHeap; + +void +CMemoryHeap::Init(uint32 total) +{ + MEMORYHEAP_ASSERT((total != 0xF) != 0); + + m_totalMemUsed = 0; + m_memUsed = nil; + m_currentMemID = MEMID_FREE; + m_blocksUsed = nil; + m_totalBlocksUsed = 0; + m_unkMemId = -1; + + uint8 *mem = (uint8*)malloc(total); + assert(((uintptr)mem & 0xF) == 0); + m_start = (HeapBlockDesc*)mem; + m_end = (HeapBlockDesc*)(mem + total - sizeof(HeapBlockDesc)); + m_start->m_memId = MEMID_FREE; + m_start->m_size = total - 2*sizeof(HeapBlockDesc); + m_end->m_memId = MEMID_GAME; + m_end->m_size = 0; + + m_freeList.m_last.m_size = INT_MAX; + m_freeList.Init(); + m_freeList.Insert(m_start); + + // TODO: figure out what these are and use sizeof + m_fixedSize[0].Init(0x10); + m_fixedSize[1].Init(0x20); + m_fixedSize[2].Init(0xE0); + m_fixedSize[3].Init(0x60); + m_fixedSize[4].Init(0x1C0); + m_fixedSize[5].Init(0x50); + + m_currentMemID = MEMID_FREE; // disable registration + m_memUsed = (uint32*)Malloc(NUM_MEMIDS * sizeof(uint32)); + m_blocksUsed = (uint32*)Malloc(NUM_MEMIDS * sizeof(uint32)); + RegisterMalloc(GetDescFromHeapPointer(m_memUsed)); + RegisterMalloc(GetDescFromHeapPointer(m_blocksUsed)); + + m_currentMemID = MEMID_GAME; + for(int i = 0; i < NUM_MEMIDS; i++){ + m_memUsed[i] = 0; + m_blocksUsed[i] = 0; + } +} + +void +CMemoryHeap::RegisterMalloc(HeapBlockDesc *block) +{ + block->m_memId = m_currentMemID; + if(m_currentMemID == MEMID_FREE) + return; + m_totalMemUsed += block->m_size + sizeof(HeapBlockDesc); + m_memUsed[m_currentMemID] += block->m_size + sizeof(HeapBlockDesc); + m_blocksUsed[m_currentMemID]++; + m_totalBlocksUsed++; +} + +void +CMemoryHeap::RegisterFree(HeapBlockDesc *block) +{ + if(block->m_memId == MEMID_FREE) + return; + m_totalMemUsed -= block->m_size + sizeof(HeapBlockDesc); + m_memUsed[block->m_memId] -= block->m_size + sizeof(HeapBlockDesc); + m_blocksUsed[block->m_memId]--; + m_totalBlocksUsed--; +} + +void* +CMemoryHeap::Malloc(uint32 size) +{ + static int recursion = 0; + + // weird way to round up + if((size & 0xF) != 0) + size = (size&~0xF) + 0x10; + + recursion++; + + // See if we can allocate from one of the fixed-size lists + for(int i = 0; i < NUM_FIXED_MEMBLOCKS; i++){ + CommonSize *list = &m_fixedSize[i]; + if(m_fixedSize[i].m_size == size){ + HeapBlockDesc *block = list->Malloc(); + if(block){ + RegisterMalloc(block); + recursion--; + return block->GetDataPointer(); + } + break; + } + } + + // now try the normal free list + HeapBlockDesc *next; + for(HeapBlockDesc *block = m_freeList.m_first.m_next; + block != &m_freeList.m_last; + block = next){ + MEMORYHEAP_ASSERT(block->m_memId == MEMID_FREE); + MEMORYHEAP_ASSERT_MESSAGE(block >= m_start && block <= m_end, "Block outside of memory"); + + // make sure block has maximum size + uint32 initialsize = block->m_size; + uint32 blocksize = CombineFreeBlocks(block); +#ifdef FIX_BUGS + // has to be done here because block can be moved + next = block->m_next; +#endif + if(initialsize != blocksize){ + block->RemoveHeapFreeBlock(); + HeapBlockDesc *pos = block->m_prev->FindSmallestFreeBlock(block->m_size); + block->InsertHeapFreeBlock(pos->m_prev); + } + if(block->m_size >= size){ + // got space to allocate from! + block->RemoveHeapFreeBlock(); + FillInBlockData(block, block->GetNextConsecutive(), size); + recursion--; + return block->GetDataPointer(); + } +#ifndef FIX_BUGS + next = block->m_next; +#endif + } + + // oh no, we're losing, try to free some stuff + static bool removeCollision = false; + static bool removeIslands = false; + static bool removeBigBuildings = false; + size_t initialMemoryUsed = CStreaming::ms_memoryUsed; + CStreaming::MakeSpaceFor(0xCFE800 - CStreaming::ms_memoryUsed); + if (recursion > 10) + CGame::TidyUpMemory(true, false); + else if (recursion > 6) + CGame::TidyUpMemory(false, true); + if (initialMemoryUsed == CStreaming::ms_memoryUsed && recursion > 11) { + if (!removeCollision && !CGame::playingIntro) { + CModelInfo::RemoveColModelsFromOtherLevels(LEVEL_GENERIC); + removeCollision = true; + } + else if (!removeIslands && !CGame::playingIntro) { + CStreaming::RemoveIslandsNotUsed(LEVEL_INDUSTRIAL); + CStreaming::RemoveIslandsNotUsed(LEVEL_COMMERCIAL); + CStreaming::RemoveIslandsNotUsed(LEVEL_SUBURBAN); + removeIslands = true; + } + else if (!removeBigBuildings) { + CStreaming::RemoveBigBuildings(LEVEL_INDUSTRIAL); + CStreaming::RemoveBigBuildings(LEVEL_COMMERCIAL); + CStreaming::RemoveBigBuildings(LEVEL_SUBURBAN); + } + else { + LoadingScreen("NO MORE MEMORY", nil, nil); + LoadingScreen("NO MORE MEMORY", nil, nil); + } + CGame::TidyUpMemory(true, false); + } + void *mem = Malloc(size); + if (removeCollision) { + CTimer::Stop(); + // TODO: different on PS2 + CFileLoader::LoadCollisionFromDatFile(CCollision::ms_collisionInMemory); + removeCollision = false; + CTimer::Update(); + } + if (removeBigBuildings || removeIslands) { + CTimer::Stop(); + if (!CGame::playingIntro) + CStreaming::RequestBigBuildings(CGame::currLevel); + CStreaming::LoadAllRequestedModels(true); + removeBigBuildings = false; + removeIslands = false; + CTimer::Update(); + } + recursion--; + return mem; +} + +void* +CMemoryHeap::Realloc(void *ptr, uint32 size) +{ + if(ptr == nil) + return Malloc(size); + + // weird way to round up + if((size & 0xF) != 0) + size = (size&~0xF) + 0x10; + + HeapBlockDesc *block = GetDescFromHeapPointer(ptr); + +#ifdef FIX_BUGS + // better handling of size < block->m_size + if(size == 0){ + Free(ptr); + return nil; + } + if(block->m_size >= size){ + // shrink allocated block + RegisterFree(block); + PushMemId(block->m_memId); + FillInBlockData(block, block->GetNextConsecutive(), size); + PopMemId(); + return ptr; + } +#else + // not growing. just returning here is a bit cheap though + if(block->m_size >= size) + return ptr; +#endif + + // have to grow allocated block + HeapBlockDesc *next = block->GetNextConsecutive(); + MEMORYHEAP_ASSERT_MESSAGE(next >= m_start && next <= m_end, "Block outside of memory"); + if(next->m_memId == MEMID_FREE){ + // try to grow the current block + // make sure the next free block has maximum size + uint32 freespace = CombineFreeBlocks(next); + HeapBlockDesc *end = next->GetNextConsecutive(); + MEMORYHEAP_ASSERT_MESSAGE(end >= m_start && end <= m_end, "Block outside of memory"); + // why the sizeof here? + if(block->m_size + next->m_size + sizeof(HeapBlockDesc) >= size){ + // enough space to grow + next->RemoveHeapFreeBlock(); + RegisterFree(block); + PushMemId(block->m_memId); + FillInBlockData(block, next->GetNextConsecutive(), size); + PopMemId(); + return ptr; + } + } + + // can't grow the existing block, have to get a new one and copy + PushMemId(block->m_memId); + void *dst = Malloc(size); + PopMemId(); + memcpy(dst, ptr, block->m_size); + Free(ptr); + return dst; +} + +void +CMemoryHeap::Free(void *ptr) +{ + HeapBlockDesc *block = GetDescFromHeapPointer(ptr); + MEMORYHEAP_ASSERT_MESSAGE(block->m_memId != MEMID_FREE, "MemoryHeap corrupt"); + MEMORYHEAP_ASSERT(m_unkMemId == -1 || m_unkMemId == block->m_memId); + + RegisterFree(block); + block->m_memId = MEMID_FREE; + CombineFreeBlocks(block); + FreeBlock(block); + if(block->m_ptrListIndex != -1){ + int32 idx = block->m_ptrListIndex; + gPtrList[idx] = nil; + m_ptrListIndexStack.push(idx); + } + block->m_ptrListIndex = -1; +} + +// allocate 'size' bytes from 'block' +void +CMemoryHeap::FillInBlockData(HeapBlockDesc *block, HeapBlockDesc *end, uint32 size) +{ + block->m_size = size; + block->m_ptrListIndex = -1; + HeapBlockDesc *remainder = block->GetNextConsecutive(); + MEMORYHEAP_ASSERT(remainder <= end); + + if(remainder < end-1){ + RegisterMalloc(block); + + // can fit another block in the remaining space + remainder->m_size = GetSizeBetweenBlocks(remainder, end); + remainder->m_memId = MEMID_FREE; + MEMORYHEAP_ASSERT(remainder->m_size != 0); + FreeBlock(remainder); + }else{ + // fully allocate this one + if(remainder < end) + // no gaps allowed + block->m_size = GetSizeBetweenBlocks(block, end); + RegisterMalloc(block); + } +} + +// Make sure free block has no other free blocks after it +uint32 +CMemoryHeap::CombineFreeBlocks(HeapBlockDesc *block) +{ + HeapBlockDesc *next = block->GetNextConsecutive(); + if(next->m_memId != MEMID_FREE) + return block->m_size; + // get rid of free blocks after this one and adjust size + for(; next->m_memId == MEMID_FREE; next = next->GetNextConsecutive()) + next->RemoveHeapFreeBlock(); + block->m_size = GetSizeBetweenBlocks(block, next); + return block->m_size; +} + +// Try to move all registered memory blocks into more optimal location +void +CMemoryHeap::TidyHeap(void) +{ + for(int i = 0; i < numPtrs; i++){ + if(gPtrList[i] == nil || *gPtrList[i] == nil) + continue; + HeapBlockDesc *newblock = WhereShouldMemoryMove(*gPtrList[i]); + if(newblock) + *gPtrList[i] = MoveHeapBlock(newblock, GetDescFromHeapPointer(*gPtrList[i])); + } +} + +// MIAMI: this is empty +void +CMemoryHeap::RegisterMemPointer(void *ptr) +{ + HeapBlockDesc *block = GetDescFromHeapPointer(*(void**)ptr); + + if(block->m_ptrListIndex != -1) + return; // already registered + + int index; + if(m_ptrListIndexStack.sp > 0){ + // re-use a previously free'd index + index = m_ptrListIndexStack.pop(); + }else{ + // have to find a new index + index = gPosnInList; + + void **pp = gPtrList[index]; + // we're replacing an old pointer here?? + if(pp && *pp && *pp != (void*)0xDDDDDDDD) + GetDescFromHeapPointer(*pp)->m_ptrListIndex = -1; + + gPosnInList++; + if(gPosnInList == 4000) + gPosnInList = 0; + if(numPtrs < 4000) + numPtrs++; + } + gPtrList[index] = (void**)ptr; + block->m_ptrListIndex = index; +} + +void* +CMemoryHeap::MoveMemory(void *ptr) +{ + HeapBlockDesc *newblock = WhereShouldMemoryMove(ptr); + if(newblock) + return MoveHeapBlock(newblock, GetDescFromHeapPointer(ptr)); + else + return ptr; +} + +HeapBlockDesc* +CMemoryHeap::WhereShouldMemoryMove(void *ptr) +{ + HeapBlockDesc *block = GetDescFromHeapPointer(ptr); + MEMORYHEAP_ASSERT(block->m_memId != MEMID_FREE); + + HeapBlockDesc *next = block->GetNextConsecutive(); + if(next->m_memId != MEMID_FREE) + return nil; + + // we want to move the block into another block + // such that the free space between this and the next block can be minimized + HeapBlockDesc *newblock = m_freeList.m_first.FindSmallestFreeBlock(block->m_size); + // size of free space wouldn't decrease, so return + if(newblock->m_size >= block->m_size + next->m_size) + return nil; + // size of free space wouldn't decrease enough + if(newblock->m_size >= 16 + 1.125f*block->m_size) // what are 16 and 1.125 here? sizeof(HeapBlockDesc)? + return nil; + return newblock; +} + +void* +CMemoryHeap::MoveHeapBlock(HeapBlockDesc *dst, HeapBlockDesc *src) +{ + PushMemId(src->m_memId); + dst->RemoveHeapFreeBlock(); + FillInBlockData(dst, dst->GetNextConsecutive(), src->m_size); + PopMemId(); + memcpy(dst->GetDataPointer(), src->GetDataPointer(), src->m_size); + memMoved += src->m_size; + dst->m_ptrListIndex = src->m_ptrListIndex; + src->m_ptrListIndex = -1; + Free(src->GetDataPointer()); + return dst->GetDataPointer(); +} + +uint32 +CMemoryHeap::GetMemoryUsed(int32 id) +{ + return m_memUsed[id]; +} + +uint32 +CMemoryHeap::GetBlocksUsed(int32 id) +{ + return m_blocksUsed[id]; +} + +void +CMemoryHeap::PopMemId(void) +{ + assert(m_idStack.sp > 0); + m_currentMemID = m_idStack.pop(); + assert(m_currentMemID != MEMID_FREE); +} + +void +CMemoryHeap::PushMemId(int32 id) +{ + MEMORYHEAP_ASSERT(id != MEMID_FREE); + assert(m_idStack.sp < 16); + m_idStack.push(m_currentMemID); + m_currentMemID = id; +} + +void +CMemoryHeap::ParseHeap(void) +{ + char tmp[16]; + int fd = CFileMgr::OpenFileForWriting("heap.txt"); + CTimer::Stop(); + + // CMemoryHeap::IntegrityCheck(); + + uint32 addrQW = 0; + for(HeapBlockDesc *block = m_start; block < m_end; block = block->GetNextConsecutive()){ + char chr = '*'; // free + if(block->m_memId != MEMID_FREE) + chr = block->m_memId-1 + 'A'; + int numQW = block->m_size>>4; + + if((addrQW & 0x3F) == 0){ + sprintf(tmp, "\n%5dK:", addrQW>>6); + CFileMgr::Write(fd, tmp, 8); + } + CFileMgr::Write(fd, "#", 1); // the descriptor, has to be 16 bytes!!!! + addrQW++; + + while(numQW--){ + if((addrQW & 0x3F) == 0){ + sprintf(tmp, "\n%5dK:", addrQW>>6); + CFileMgr::Write(fd, tmp, 8); + } + CFileMgr::Write(fd, &chr, 1); + addrQW++; + } + } + + CTimer::Update(); + CFileMgr::CloseFile(fd); +} + + +void +CommonSize::Init(uint32 size) +{ + m_freeList.Init(); + m_size = size; + m_failed = 0; + m_remaining = 0; +} + +#endif diff --git a/src/miami/rw/MemoryHeap.h b/src/miami/rw/MemoryHeap.h new file mode 100644 index 00000000..1a9a51f8 --- /dev/null +++ b/src/miami/rw/MemoryHeap.h @@ -0,0 +1,204 @@ +#pragma once + +// some windows shit +#ifdef MoveMemory +#undef MoveMemory +#endif + +#ifdef USE_CUSTOM_ALLOCATOR +#define PUSH_MEMID(id) gMainHeap.PushMemId(id) +#define POP_MEMID() gMainHeap.PopMemId() +#define REGISTER_MEMPTR(ptr) gMainHeap.RegisterMemPointer(ptr) +#else +#define PUSH_MEMID(id) +#define POP_MEMID() +#define REGISTER_MEMPTR(ptr) +#endif + +enum { + MEMID_FREE, + MEMID_GAME = 1, // "Game" + MEMID_WORLD = 2, // "World" + MEMID_ANIMATION = 3, // "Animation" + MEMID_POOLS = 4, // "Pools" + MEMID_DEF_MODELS = 5, // "Default Models" + MEMID_STREAM = 6, // "Streaming" + MEMID_STREAM_MODELS = 7, // "Streamed Models" + MEMID_STREAM_LODS = 8, // "Streamed LODs" + MEMID_STREAM_TEXUTRES = 9, // "Streamed Textures" + MEMID_STREAM_COLLISION = 10, // "Streamed Collision" + MEMID_STREAM_ANIMATION = 11, // "Streamed Animation" + MEMID_TEXTURES = 12, // "Textures" + MEMID_COLLISION = 13, // "Collision" + MEMID_PRE_ALLOC = 14, // "PreAlloc" + MEMID_GAME_PROCESS = 15, // "Game Process" + MEMID_SCRIPT = 16, // "Script" + MEMID_CARS = 17, // "Cars" + MEMID_RENDER = 18, // "Render" + MEMID_PED_ATTR = 19, // "Ped Attr" + NUM_MEMIDS, + + NUM_FIXED_MEMBLOCKS = 6 +}; + +template +class CStack +{ +public: + T values[N]; + uint32 sp; + + CStack() : sp(0) {} + void push(const T& val) { values[sp++] = val; } + T& pop() { return values[--sp]; } +}; + + +struct HeapBlockDesc +{ + uint32 m_size; + int16 m_memId; + int16 m_ptrListIndex; + HeapBlockDesc *m_next; + HeapBlockDesc *m_prev; + + HeapBlockDesc *GetNextConsecutive(void) + { + return (HeapBlockDesc*)((uintptr)this + sizeof(HeapBlockDesc) + m_size); + } + + void *GetDataPointer(void) + { + return (void*)((uintptr)this + sizeof(HeapBlockDesc)); + } + + void RemoveHeapFreeBlock(void) + { + m_next->m_prev = m_prev; + m_prev->m_next = m_next; + } + + // after node + void InsertHeapFreeBlock(HeapBlockDesc *node) + { + m_next = node->m_next; + node->m_next->m_prev = this; + m_prev = node; + node->m_next = this; + } + + HeapBlockDesc *FindSmallestFreeBlock(uint32 size) + { + HeapBlockDesc *b; + for(b = m_next; b->m_size < size; b = b->m_next); + return b; + } +}; + +#ifdef USE_CUSTOM_ALLOCATOR +// TODO: figure something out for 64 bit pointers +static_assert(sizeof(HeapBlockDesc) == 0x10, "HeapBlockDesc must have 0x10 size otherwise most of assumptions don't make sense"); +#endif + +struct HeapBlockList +{ + HeapBlockDesc m_first; + HeapBlockDesc m_last; + + void Init(void) + { + m_first.m_next = &m_last; + m_last.m_prev = &m_first; + } + + void Insert(HeapBlockDesc *node) + { + node->InsertHeapFreeBlock(&m_first); + } +}; + +struct CommonSize +{ + HeapBlockList m_freeList; + uint32 m_size; + uint32 m_failed; + uint32 m_remaining; + + void Init(uint32 size); + void Free(HeapBlockDesc *node) + { + m_freeList.Insert(node); + m_remaining++; + } + HeapBlockDesc *Malloc(void) + { + if(m_freeList.m_first.m_next == &m_freeList.m_last){ + m_failed++; + return nil; + } + HeapBlockDesc *block = m_freeList.m_first.m_next; + m_remaining--; + block->RemoveHeapFreeBlock(); + block->m_ptrListIndex = -1; + return block; + } +}; + +class CMemoryHeap +{ +public: + HeapBlockDesc *m_start; + HeapBlockDesc *m_end; + HeapBlockList m_freeList; + CommonSize m_fixedSize[NUM_FIXED_MEMBLOCKS]; + uint32 m_totalMemUsed; + CStack m_idStack; + uint32 m_currentMemID; + uint32 *m_memUsed; + uint32 m_totalBlocksUsed; + uint32 *m_blocksUsed; + uint32 m_unkMemId; + + CMemoryHeap(void) : m_start(nil) {} + void Init(uint32 total); + void RegisterMalloc(HeapBlockDesc *block); + void RegisterFree(HeapBlockDesc *block); + void *Malloc(uint32 size); + void *Realloc(void *ptr, uint32 size); + void Free(void *ptr); + void FillInBlockData(HeapBlockDesc *block, HeapBlockDesc *end, uint32 size); + uint32 CombineFreeBlocks(HeapBlockDesc *block); + void *MoveMemory(void *ptr); + HeapBlockDesc *WhereShouldMemoryMove(void *ptr); + void *MoveHeapBlock(HeapBlockDesc *dst, HeapBlockDesc *src); + void PopMemId(void); + void PushMemId(int32 id); + void RegisterMemPointer(void *ptr); + void TidyHeap(void); + uint32 GetMemoryUsed(int32 id); + uint32 GetBlocksUsed(int32 id); + int32 GetLargestFreeBlock(void) { return m_freeList.m_last.m_prev->m_size; } + + void ParseHeap(void); + + HeapBlockDesc *GetDescFromHeapPointer(void *block) + { + return (HeapBlockDesc*)((uintptr)block - sizeof(HeapBlockDesc)); + } + uint32 GetSizeBetweenBlocks(HeapBlockDesc *first, HeapBlockDesc *second) + { + return (uintptr)second - (uintptr)first - sizeof(HeapBlockDesc); + } + void FreeBlock(HeapBlockDesc *block){ + for(int i = 0; i < NUM_FIXED_MEMBLOCKS; i++){ + if(m_fixedSize[i].m_size == block->m_size){ + m_fixedSize[i].Free(block); + return; + } + } + HeapBlockDesc *b = m_freeList.m_first.FindSmallestFreeBlock(block->m_size); + block->InsertHeapFreeBlock(b->m_prev); + } +}; + +extern CMemoryHeap gMainHeap; diff --git a/src/miami/rw/MemoryMgr.cpp b/src/miami/rw/MemoryMgr.cpp new file mode 100644 index 00000000..b9cff043 --- /dev/null +++ b/src/miami/rw/MemoryMgr.cpp @@ -0,0 +1,130 @@ +#include "common.h" +#include "MemoryHeap.h" +#include "MemoryMgr.h" + + +uint8 *pMemoryTop; + +void +InitMemoryMgr(void) +{ +#ifdef USE_CUSTOM_ALLOCATOR +#ifdef GTA_PS2 +#error "finish this" +#else + // randomly allocate 128mb + gMainHeap.Init(128*1024*1024); +#endif +#endif +} + + +RwMemoryFunctions memFuncs = { + MemoryMgrMalloc, + MemoryMgrFree, + MemoryMgrRealloc, + MemoryMgrCalloc +}; + +#ifdef USE_CUSTOM_ALLOCATOR +// game seems to be using heap directly here, but this is nicer +void *operator new(size_t sz) throw() { return MemoryMgrMalloc(sz); } +void *operator new[](size_t sz) throw() { return MemoryMgrMalloc(sz); } +void operator delete(void *ptr) throw() { MemoryMgrFree(ptr); } +void operator delete[](void *ptr) throw() { MemoryMgrFree(ptr); } +#endif + +void* +MemoryMgrMalloc(size_t size) +{ +#ifdef USE_CUSTOM_ALLOCATOR + void *mem = gMainHeap.Malloc(size); +#else + void *mem = malloc(size); +#endif + if((uint8*)mem + size > pMemoryTop) + pMemoryTop = (uint8*)mem + size ; + return mem; +} + +void* +MemoryMgrRealloc(void *ptr, size_t size) +{ +#ifdef USE_CUSTOM_ALLOCATOR + void *mem = gMainHeap.Realloc(ptr, size); +#else + void *mem = realloc(ptr, size); +#endif + if((uint8*)mem + size > pMemoryTop) + pMemoryTop = (uint8*)mem + size ; + return mem; +} + +void* +MemoryMgrCalloc(size_t num, size_t size) +{ +#ifdef USE_CUSTOM_ALLOCATOR + void *mem = gMainHeap.Malloc(num*size); +#else + void *mem = calloc(num, size); +#endif + if((uint8*)mem + size > pMemoryTop) + pMemoryTop = (uint8*)mem + size ; +#ifdef FIX_BUGS + memset(mem, 0, num*size); +#endif + return mem; +} + +void +MemoryMgrFree(void *ptr) +{ +#ifdef USE_CUSTOM_ALLOCATOR +#ifdef FIX_BUGS + // i don't suppose this is handled by RW? + if(ptr == nil) return; +#endif + gMainHeap.Free(ptr); +#else + free(ptr); +#endif +} + +void * +RwMallocAlign(RwUInt32 size, RwUInt32 align) +{ +#if defined (FIX_BUGS) || defined(FIX_BUGS_64) + uintptr ptralign = align-1; + void *mem = (void *)MemoryMgrMalloc(size + sizeof(uintptr) + ptralign); + + ASSERT(mem != nil); + + void *addr = (void *)((((uintptr)mem) + sizeof(uintptr) + ptralign) & ~ptralign); + + ASSERT(addr != nil); +#else + void *mem = (void *)MemoryMgrMalloc(size + align); + + ASSERT(mem != nil); + + void *addr = (void *)((((uintptr)mem) + align) & ~(align - 1)); + + ASSERT(addr != nil); +#endif + + *(((void **)addr) - 1) = mem; + + return addr; +} + +void +RwFreeAlign(void *mem) +{ + ASSERT(mem != nil); + + void *addr = *(((void **)mem) - 1); + + ASSERT(addr != nil); + + MemoryMgrFree(addr); +} diff --git a/src/miami/rw/MemoryMgr.h b/src/miami/rw/MemoryMgr.h new file mode 100644 index 00000000..e2962806 --- /dev/null +++ b/src/miami/rw/MemoryMgr.h @@ -0,0 +1,12 @@ +#pragma once + +extern RwMemoryFunctions memFuncs; +void InitMemoryMgr(void); + +void *MemoryMgrMalloc(size_t size); +void *MemoryMgrRealloc(void *ptr, size_t size); +void *MemoryMgrCalloc(size_t num, size_t size); +void MemoryMgrFree(void *ptr); + +void *RwMallocAlign(RwUInt32 size, RwUInt32 align); +void RwFreeAlign(void *mem); diff --git a/src/miami/rw/NodeName.cpp b/src/miami/rw/NodeName.cpp new file mode 100644 index 00000000..a7185e4f --- /dev/null +++ b/src/miami/rw/NodeName.cpp @@ -0,0 +1,77 @@ +#include "common.h" + +#include "NodeName.h" + +static int32 gPluginOffset; + +enum +{ + ID_NODENAME = MAKECHUNKID(rwVENDORID_ROCKSTAR, 0xFE), +}; + +#define NODENAMEEXT(o) (RWPLUGINOFFSET(char, o, gPluginOffset)) + +void* +NodeNameConstructor(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject) +{ + if(gPluginOffset > 0) + NODENAMEEXT(object)[0] = '\0'; + return object; +} + +void* +NodeNameDestructor(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject) +{ + return object; +} + +void* +NodeNameCopy(void *dstObject, const void *srcObject, RwInt32 offsetInObject, RwInt32 sizeInObject) +{ + strncpy(NODENAMEEXT(dstObject), NODENAMEEXT(srcObject), 23); + return nil; +} + +RwStream* +NodeNameStreamRead(RwStream *stream, RwInt32 binaryLength, void *object, RwInt32 offsetInObject, RwInt32 sizeInObject) +{ + RwStreamRead(stream, NODENAMEEXT(object), binaryLength); + NODENAMEEXT(object)[binaryLength] = '\0'; + return stream; +} + +RwStream* +NodeNameStreamWrite(RwStream *stream, RwInt32 binaryLength, const void *object, RwInt32 offsetInObject, RwInt32 sizeInObject) +{ + RwStreamWrite(stream, NODENAMEEXT(object), binaryLength); + return stream; +} + +RwInt32 +NodeNameStreamGetSize(const void *object, RwInt32 offsetInObject, RwInt32 sizeInObject) +{ + char *name = NODENAMEEXT(object); // can't be nil + return name ? (RwInt32)rwstrlen(name) : 0; +} + +bool +NodeNamePluginAttach(void) +{ + gPluginOffset = RwFrameRegisterPlugin(24, ID_NODENAME, + NodeNameConstructor, + NodeNameDestructor, + NodeNameCopy); + RwFrameRegisterPluginStream(ID_NODENAME, + NodeNameStreamRead, + NodeNameStreamWrite, + NodeNameStreamGetSize); + return gPluginOffset != -1; +} + +char* +GetFrameNodeName(RwFrame *frame) +{ + if(gPluginOffset < 0) + return nil; + return NODENAMEEXT(frame); +} diff --git a/src/miami/rw/NodeName.h b/src/miami/rw/NodeName.h new file mode 100644 index 00000000..1a3e057b --- /dev/null +++ b/src/miami/rw/NodeName.h @@ -0,0 +1,4 @@ +#pragma once + +bool NodeNamePluginAttach(void); +char *GetFrameNodeName(RwFrame *frame); diff --git a/src/miami/rw/RwHelper.cpp b/src/miami/rw/RwHelper.cpp new file mode 100644 index 00000000..d5d0885d --- /dev/null +++ b/src/miami/rw/RwHelper.cpp @@ -0,0 +1,829 @@ +#define WITHD3D +#include "common.h" +#include + +#include "Timecycle.h" +#include "skeleton.h" +#include "Debug.h" +#include "MBlur.h" +#if !defined(FINAL) || defined(DEBUGMENU) +#include "rtcharse.h" +#endif +#ifndef FINAL +RtCharset *debugCharset; +bool bDebugRenderGroups; +#endif + +#ifdef PS2_ALPHA_TEST +bool gPS2alphaTest = true; +#else +bool gPS2alphaTest = false; +#endif +bool gBackfaceCulling = false; // TODO: Investigate why this needs to be off (skmp) + +#if !defined(FINAL) || defined(DEBUGMENU) +static bool charsetOpen; +void OpenCharsetSafe() +{ + if(!charsetOpen) + RtCharsetOpen(); + charsetOpen = true; +} +#endif + +void CreateDebugFont() +{ +#ifndef FINAL + RwRGBA color = { 255, 255, 128, 255 }; + RwRGBA colorbg = { 0, 0, 0, 0 }; + OpenCharsetSafe(); + debugCharset = RtCharsetCreate(&color, &colorbg); +#endif +} + +void DestroyDebugFont() +{ +#ifndef FINAL + RtCharsetDestroy(debugCharset); + RtCharsetClose(); + charsetOpen = false; +#endif +} + +void ObrsPrintfString(const char *str, short x, short y) +{ +#ifndef FINAL + RtCharsetPrintBuffered(debugCharset, str, x*8, y*16, true); +#endif +} + +void FlushObrsPrintfs() +{ +#ifndef FINAL + RtCharsetBufferFlush(); +#endif +} + +#if !defined(DC_TEXCONV) +void +DefinedState(void) +{ + RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSWRAP); + RwRenderStateSet(rwRENDERSTATETEXTUREPERSPECTIVE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEGOURAUD); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + //RwRenderStateSet(rwRENDERSTATEALPHAPRIMITIVEBUFFER, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEBORDERCOLOR, (void*)RWRGBALONG(0, 0, 0, 255)); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEFOGCOLOR, + (void*)RWRGBALONG(CTimeCycle::GetFogRed(), CTimeCycle::GetFogGreen(), CTimeCycle::GetFogBlue(), 255)); + RwRenderStateSet(rwRENDERSTATEFOGTYPE, (void*)rwFOGTYPELINEAR); + RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE); + +#ifdef LIBRW + rw::SetRenderState(rw::ALPHATESTFUNC, rw::ALPHAGREATEREQUAL); + rw::SetRenderState(rw::ALPHATESTREF, 3); + + rw::SetRenderState(rw::GSALPHATEST, gPS2alphaTest); +#else + // D3D stuff + RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER); + RwD3D8SetRenderState(D3DRS_ALPHAREF, 2); +#endif +} +#else +void +DefinedState(void) { assert(false); } +#endif + +void +SetCullMode(uint32 mode) +{ + if(gBackfaceCulling) + RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)mode); + else + RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE); +} + +#ifndef FINAL +void +PushRendergroup(const char *name) +{ + if(!bDebugRenderGroups) + return; +#if defined(RW_OPENGL) + if(GLAD_GL_KHR_debug) + glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, name); +#elif defined(RW_D3D9) + static WCHAR tmp[256]; + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, name, -1, tmp, sizeof(tmp)); + D3DPERF_BeginEvent(0xFFFFFFFF, tmp); +#endif +} + +void +PopRendergroup(void) +{ + if(!bDebugRenderGroups) + return; +#if defined(RW_OPENGL) + if(GLAD_GL_KHR_debug) + glPopDebugGroup(); +#elif defined(RW_D3D9) + D3DPERF_EndEvent(); +#endif +} +#endif + +RwFrame* +GetFirstFrameCallback(RwFrame *child, void *data) +{ + *(RwFrame**)data = child; + return nil; +} + +RwFrame* +GetFirstChild(RwFrame *frame) +{ + RwFrame *child; + + child = nil; + RwFrameForAllChildren(frame, GetFirstFrameCallback, &child); + return child; +} + +RwObject* +GetFirstObjectCallback(RwObject *object, void *data) +{ + *(RwObject**)data = object; + return nil; +} + +RwObject* +GetFirstObject(RwFrame *frame) +{ + RwObject *obj; + + obj = nil; + RwFrameForAllObjects(frame, GetFirstObjectCallback, &obj); + return obj; +} + +RpAtomic* +GetFirstAtomicCallback(RpAtomic *atm, void *data) +{ + *(RpAtomic**)data = atm; + return nil; +} + +RpAtomic* +GetFirstAtomic(RpClump *clump) +{ + RpAtomic *atm; + + atm = nil; + RpClumpForAllAtomics(clump, GetFirstAtomicCallback, &atm); + return atm; +} + +RwTexture* +GetFirstTextureCallback(RwTexture *tex, void *data) +{ + *(RwTexture**)data = tex; + return nil; +} + +RwTexture* +GetFirstTexture(RwTexDictionary *txd) +{ + RwTexture *tex; + + tex = nil; + RwTexDictionaryForAllTextures(txd, GetFirstTextureCallback, &tex); + return tex; +} + +bool +IsClumpSkinned(RpClump *clump) +{ + RpAtomic *atomic = GetFirstAtomic(clump); + return atomic ? RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic)) : nil; +} + +static RpAtomic* +GetAnimHierarchyCallback(RpAtomic *atomic, void *data) +{ + *(RpHAnimHierarchy**)data = RpSkinAtomicGetHAnimHierarchy(atomic); + return nil; +} + +RpHAnimHierarchy* +GetAnimHierarchyFromSkinClump(RpClump *clump) +{ + RpHAnimHierarchy *hier = nil; + RpClumpForAllAtomics(clump, GetAnimHierarchyCallback, &hier); + return hier; +} + +static RwFrame* +GetAnimHierarchyFromClumpCB(RwFrame *frame, void *data) +{ + RpHAnimHierarchy *hier = RpHAnimFrameGetHierarchy(frame); + if(hier){ + *(RpHAnimHierarchy**)data = hier; + return nil; + } + RwFrameForAllChildren(frame, GetAnimHierarchyFromClumpCB, data); + return frame; +} + +RpHAnimHierarchy* +GetAnimHierarchyFromClump(RpClump *clump) +{ + RpHAnimHierarchy *hier = nil; + RwFrameForAllChildren(RpClumpGetFrame(clump), GetAnimHierarchyFromClumpCB, &hier); + return hier; +} + +void +SkinGetBonePositionsToTable(RpClump *clump, RwV3d *boneTable) +{ + int i, parent; + RpAtomic *atomic; + RpSkin *skin; + RpHAnimHierarchy *hier; + int numBones; + RwMatrix m, invmat; + int stack[32]; + int sp; + + if(boneTable == nil) + return; + + atomic = GetFirstAtomic(clump); // mobile, also VC + assert(atomic); + skin = RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic)); + assert(skin); + hier = GetAnimHierarchyFromSkinClump(clump); + assert(hier); + boneTable[0].x = 0.0f; + boneTable[0].y = 0.0f; + boneTable[0].z = 0.0f; + numBones = RpSkinGetNumBones(skin); + parent = 0; + sp = 0; +#ifdef FIX_BUGS + stack[0] = 0; // i think this is ok +#endif + for(i = 1; i < numBones; i++){ + RwMatrixCopy(&m, &RpSkinGetSkinToBoneMatrices(skin)[i]); + RwMatrixInvert(&invmat, &m); + const RwMatrix *x = RpSkinGetSkinToBoneMatrices(skin); + RwV3dTransformPoints(&boneTable[i], &invmat.pos, 1, &x[parent]); + if(HIERNODEINFO(hier)[i].flags & rpHANIMPUSHPARENTMATRIX) + stack[++sp] = parent; + if(HIERNODEINFO(hier)[i].flags & rpHANIMPOPPARENTMATRIX) + parent = stack[sp--]; + else + parent = i; + + //assert(parent >= 0 && parent < numBones); + } +} + +RpHAnimAnimation* +HAnimAnimationCreateForHierarchy(RpHAnimHierarchy *hier) +{ + int i; +#if defined FIX_BUGS || defined LIBRW + int numNodes = hier->numNodes*2; // you're supposed to have at least two KFs per node +#else + int numNodes = hier->numNodes; +#endif + RpHAnimAnimation *anim = RpHAnimAnimationCreate(rpHANIMSTDKEYFRAMETYPEID, numNodes, 0, 0.0f); + if(anim == nil) + return nil; + RpHAnimStdKeyFrame *frame; + for(i = 0; i < numNodes; i++){ + frame = (RpHAnimStdKeyFrame*)HANIMFRAME(anim, i); // games uses struct size here, not safe + frame->q.real = 1.0f; + frame->q.imag.x = frame->q.imag.y = frame->q.imag.z = 0.0f; + frame->t.x = frame->t.y = frame->t.z = 0.0f; +#if defined FIX_BUGS || defined LIBRW + // times are subtracted and divided giving NaNs + // so they can't both be 0 + frame->time = i/hier->numNodes; +#else + frame->time = 0.0f; +#endif + frame->prevFrame = nil; + } + return anim; +} + +#if !defined(DC_TEXCONV) +void +RenderSkeleton(RpHAnimHierarchy *hier) +{ + int i; + int sp; + int stack[32]; + int par; + CVector p1, p2; + int numNodes = hier->numNodes; + RwMatrix *mats = RpHAnimHierarchyGetMatrixArray(hier); + p1 = mats[0].pos; + + par = 0; + sp = 0; + stack[sp++] = par; + for(i = 1; i < numNodes; i++){ + p1 = mats[par].pos; + p2 = mats[i].pos; + CDebug::AddLine(p1, p2, 0xFFFFFFFF, 0xFFFFFFFF); + if(HIERNODEINFO(hier)[i].flags & rpHANIMPUSHPARENTMATRIX) + stack[sp++] = par; + par = i; + if(HIERNODEINFO(hier)[i].flags & rpHANIMPOPPARENTMATRIX) + par = stack[--sp]; + } +} +#else +void +RenderSkeleton(RpHAnimHierarchy *hier) { assert(false); } +#endif + + +RwBool Im2DRenderQuad(RwReal x1, RwReal y1, RwReal x2, RwReal y2, RwReal z, RwReal recipCamZ, RwReal uvOffset) +{ + RwIm2DVertex vx[4]; + + /* + * Render an opaque white 2D quad at the given coordinates and + * spanning a whole texture. + */ + + RwIm2DVertexSetScreenX(&vx[0], x1); + RwIm2DVertexSetScreenY(&vx[0], y1); + RwIm2DVertexSetScreenZ(&vx[0], z); + RwIm2DVertexSetIntRGBA(&vx[0], 255, 255, 255, 255); + RwIm2DVertexSetRecipCameraZ(&vx[0], recipCamZ); + RwIm2DVertexSetU(&vx[0], uvOffset, recipCamZ); + RwIm2DVertexSetV(&vx[0], uvOffset, recipCamZ); + + RwIm2DVertexSetScreenX(&vx[1], x1); + RwIm2DVertexSetScreenY(&vx[1], y2); + RwIm2DVertexSetScreenZ(&vx[1], z); + RwIm2DVertexSetIntRGBA(&vx[1], 255, 255, 255, 255); + RwIm2DVertexSetRecipCameraZ(&vx[1], recipCamZ); + RwIm2DVertexSetU(&vx[1], uvOffset, recipCamZ); + RwIm2DVertexSetV(&vx[1], 1.0f + uvOffset, recipCamZ); + + RwIm2DVertexSetScreenX(&vx[2], x2); + RwIm2DVertexSetScreenY(&vx[2], y1); + RwIm2DVertexSetScreenZ(&vx[2], z); + RwIm2DVertexSetIntRGBA(&vx[2], 255, 255, 255, 255); + RwIm2DVertexSetRecipCameraZ(&vx[2], recipCamZ); + RwIm2DVertexSetU(&vx[2], 1.0f + uvOffset, recipCamZ); + RwIm2DVertexSetV(&vx[2], uvOffset, recipCamZ); + + RwIm2DVertexSetScreenX(&vx[3], x2); + RwIm2DVertexSetScreenY(&vx[3], y2); + RwIm2DVertexSetScreenZ(&vx[3], z); + RwIm2DVertexSetIntRGBA(&vx[3], 255, 255, 255, 255); + RwIm2DVertexSetRecipCameraZ(&vx[3], recipCamZ); + RwIm2DVertexSetU(&vx[3], 1.0f + uvOffset, recipCamZ); + RwIm2DVertexSetV(&vx[3], 1.0f + uvOffset, recipCamZ); + + RwIm2DRenderPrimitive(rwPRIMTYPETRISTRIP, vx, 4); + + return TRUE; +} + +bool b_cbsUseLTM = true; + +RpAtomic *cbsCalcMeanBSphereRadiusCB(RpAtomic *atomic, void *data) +{ + RwV3d atomicPos; + + if ( b_cbsUseLTM ) + RwV3dTransformPoints(&atomicPos, &RpAtomicGetBoundingSphere(atomic)->center, 1, RwFrameGetLTM(RpClumpGetFrame(atomic->clump))); + else + RwV3dTransformPoints(&atomicPos, &RpAtomicGetBoundingSphere(atomic)->center, 1, RwFrameGetMatrix(RpClumpGetFrame(atomic->clump))); + + RwV3d temp; + RwV3dSub(&temp, &atomicPos, &((RwSphere *)data)->center); + RwReal radius = RwV3dLength(&temp) + RpAtomicGetBoundingSphere(atomic)->radius; + + if ( ((RwSphere *)data)->radius < radius ) + ((RwSphere *)data)->radius = radius; + + return atomic; +} + +RpAtomic *cbsCalcMeanBSphereCenterCB(RpAtomic *atomic, void *data) +{ + RwV3d atomicPos; + + if ( b_cbsUseLTM ) + RwV3dTransformPoints(&atomicPos, &RpAtomicGetBoundingSphere(atomic)->center, 1, RwFrameGetLTM(RpClumpGetFrame(atomic->clump))); + else + RwV3dTransformPoints(&atomicPos, &RpAtomicGetBoundingSphere(atomic)->center, 1, RwFrameGetMatrix(RpClumpGetFrame(atomic->clump))); + + RwV3dAdd(&((RwSphere *)data)->center, &((RwSphere *)data)->center, &atomicPos); + + return atomic; +} + +RpClump *RpClumpGetBoundingSphere(RpClump *clump, RwSphere *sphere, bool useLTM) +{ + RwMatrix matrix; + RwSphere result = { 0.0f, 0.0f, 0.0f, 0.0f }; + + b_cbsUseLTM = useLTM; + + if ( clump == nil || sphere == nil ) + return nil; + + sphere->radius = 0.0f; + sphere->center.x = 0.0f; + sphere->center.y = 0.0f; + sphere->center.z = 0.0f; + + RwInt32 numAtomics = RpClumpGetNumAtomics(clump); + if ( numAtomics < 1.0f ) + return nil; + + RpClumpForAllAtomics(clump, cbsCalcMeanBSphereCenterCB, &result); + + RwV3dScale(&result.center, &result.center, 1.0f/numAtomics); + + RpClumpForAllAtomics(clump, cbsCalcMeanBSphereRadiusCB, &result); + + if ( b_cbsUseLTM ) + RwMatrixInvert(&matrix, RwFrameGetLTM(RpClumpGetFrame(clump))); + else + RwMatrixInvert(&matrix, RwFrameGetMatrix(RpClumpGetFrame(clump))); + + RwV3dTransformPoints(&result.center, &result.center, 1, &matrix); + + *sphere = result; + + return clump; +} + +#if !defined(DC_TEXCONV) +void +CameraSize(RwCamera * camera, RwRect * rect, + RwReal viewWindow, RwReal aspectRatio) +{ + if (camera) + { + RwVideoMode videoMode; + RwRect r; + RwRect origSize = { 0, 0, 0, 0 }; // FIX just to make the compier happy + RwV2d vw; + + RwEngineGetVideoModeInfo(&videoMode, + RwEngineGetCurrentVideoMode()); + + origSize.w = RwRasterGetWidth(RwCameraGetRaster(camera)); + origSize.h = RwRasterGetHeight(RwCameraGetRaster(camera)); + + if (!rect) + { + if (videoMode.flags & rwVIDEOMODEEXCLUSIVE) + { + /* For full screen applications, resizing the camera just doesn't + * make sense, use the video mode size. + */ + + r.x = r.y = 0; + r.w = videoMode.width; + r.h = videoMode.height; + rect = &r; + } + else + { + /* + rect not specified - reuse current values + */ + r.w = RwRasterGetWidth(RwCameraGetRaster(camera)); + r.h = RwRasterGetHeight(RwCameraGetRaster(camera)); + r.x = r.y = 0; + rect = &r; + } + } + + if (( origSize.w != rect->w ) || ( origSize.h != rect->h )) + { + RwRaster *raster; + RwRaster *zRaster; + + // BUG: game just changes camera raster's sizes, but this is a hack +#if defined FIX_BUGS || defined LIBRW + /* + * Destroy rasters... + */ + + raster = RwCameraGetRaster(camera); + if( raster ) + { + RwRasterDestroy(raster); + camera->frameBuffer = nil; + } + + zRaster = RwCameraGetZRaster(camera); + if( zRaster ) + { + RwRasterDestroy(zRaster); + camera->zBuffer = nil; + } + + /* + * Create new rasters... + */ + + raster = RwRasterCreate(rect->w, rect->h, 0, rwRASTERTYPECAMERA); + zRaster = RwRasterCreate(rect->w, rect->h, 0, rwRASTERTYPEZBUFFER); + + if( raster && zRaster ) + { + RwCameraSetRaster(camera, raster); + RwCameraSetZRaster(camera, zRaster); + } + else + { + if( raster ) + { + RwRasterDestroy(raster); + } + + if( zRaster ) + { + RwRasterDestroy(zRaster); + } + + rect->x = origSize.x; + rect->y = origSize.y; + rect->w = origSize.w; + rect->h = origSize.h; + + /* + * Use default values... + */ + raster = + RwRasterCreate(rect->w, rect->h, 0, rwRASTERTYPECAMERA); + + zRaster = + RwRasterCreate(rect->w, rect->h, 0, rwRASTERTYPEZBUFFER); + + RwCameraSetRaster(camera, raster); + RwCameraSetZRaster(camera, zRaster); + } +#else + raster = RwCameraGetRaster(camera); + zRaster = RwCameraGetZRaster(camera); + + raster->width = zRaster->width = rect->w; + raster->height = zRaster->height = rect->h; +#endif +#ifdef FIX_BUGS + if(CMBlur::BlurOn){ + CMBlur::MotionBlurClose(); + CMBlur::MotionBlurOpen(camera); + } +#endif + } + + /* Figure out the view window */ + if (videoMode.flags & rwVIDEOMODEEXCLUSIVE) + { + /* derive ratio from aspect ratio */ + vw.x = viewWindow; + vw.y = viewWindow / aspectRatio; + } + else + { + /* derive from pixel ratios */ + if (rect->w > rect->h) + { + vw.x = viewWindow; + vw.y = (rect->h * viewWindow) / rect->w; + } + else + { + vw.x = (rect->w * viewWindow) / rect->h; + vw.y = viewWindow; + } + } + + RwCameraSetViewWindow(camera, &vw); + + RsGlobal.width = rect->w; + RsGlobal.height = rect->h; + } + + return; +} +#else +void +CameraSize(RwCamera * camera, RwRect * rect, + RwReal viewWindow, RwReal aspectRatio) { assert(false); } +#endif + +void +CameraDestroy(RwCamera *camera) +{ + RwRaster *raster, *tmpRaster; + RwFrame *frame; + + if (camera) + { + frame = RwCameraGetFrame(camera); + if (frame) + { + RwFrameDestroy(frame); + } + + raster = RwCameraGetRaster(camera); + if (raster) + { + tmpRaster = RwRasterGetParent(raster); + + RwRasterDestroy(raster); + + if ((tmpRaster != nil) && (tmpRaster != raster)) + { + RwRasterDestroy(tmpRaster); + } + } + + raster = RwCameraGetZRaster(camera); + if (raster) + { + tmpRaster = RwRasterGetParent(raster); + + RwRasterDestroy(raster); + + if ((tmpRaster != nil) && (tmpRaster != raster)) + { + RwRasterDestroy(tmpRaster); + } + } + + RwCameraDestroy(camera); + } + + return; +} + +RwCamera * +CameraCreate(RwInt32 width, RwInt32 height, RwBool zBuffer) +{ + RwCamera *camera; + + camera = RwCameraCreate(); + + if (camera) + { + RwCameraSetFrame(camera, RwFrameCreate()); + RwCameraSetRaster(camera, + RwRasterCreate(0, 0, 0, rwRASTERTYPECAMERA)); + + if (zBuffer) + { + RwCameraSetZRaster(camera, + RwRasterCreate(0, 0, 0, + rwRASTERTYPEZBUFFER)); + } + + /* now check that everything is valid */ + if (RwCameraGetFrame(camera) && + RwCameraGetRaster(camera) && + RwRasterGetParent(RwCameraGetRaster(camera)) && + (!zBuffer || (RwCameraGetZRaster(camera) && + RwRasterGetParent(RwCameraGetZRaster + (camera))))) + { + /* everything OK */ + return (camera); + } + } + + /* if we're here then an error must have occurred so clean up */ + + CameraDestroy(camera); + return (nil); +} + +#ifdef LIBRW +#include +#include "VehicleModelInfo.h" + +int32 +findPlatform(rw::Atomic *a) +{ + rw::Geometry *g = a->geometry; + if(g->instData) + return g->instData->platform; + return 0; +} + +#if !defined(DC_TEXCONV) +// Game doesn't read atomic extensions so we never get any other than the default pipe, +// but we need it for uninstancing +void +attachPipe(rw::Atomic *atomic) +{ + if(RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic))) + atomic->pipeline = rw::skinGlobals.pipelines[rw::platform]; + else{ + int fx = rpMATFXEFFECTNULL; + RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), CVehicleModelInfo::GetMatFXEffectMaterialCB, &fx); + if(fx != rpMATFXEFFECTNULL) + RpMatFXAtomicEnableEffects(atomic); + } +} +#else +void +attachPipe(rw::Atomic *atomic) { assert(false); } +#endif + +// Attach pipes for the platform we have native data for so we can uninstance +void +switchPipes(rw::Atomic *a, int32 platform) +{ + if(a->pipeline && a->pipeline->platform != platform){ + uint32 plgid = a->pipeline->pluginID; + switch(plgid){ + // assume default pipe won't be attached explicitly + case rw::ID_SKIN: + a->pipeline = rw::skinGlobals.pipelines[platform]; + break; + case rw::ID_MATFX: + a->pipeline = rw::matFXGlobals.pipelines[platform]; + break; + } + } +} + +RpAtomic* +ConvertPlatformAtomic(RpAtomic *atomic, void *data) +{ + int32 driver = rw::platform; + int32 platform = findPlatform(atomic); + if(platform != 0 && platform != driver){ + attachPipe(atomic); // kludge + rw::ObjPipeline *origPipe = atomic->pipeline; + rw::platform = platform; + switchPipes(atomic, rw::platform); + if(atomic->geometry->flags & rw::Geometry::NATIVE) + atomic->uninstance(); + // no ADC in this game + //rw::ps2::unconvertADC(atomic->geometry); + rw::platform = driver; + atomic->pipeline = origPipe; + } + return atomic; +} +#endif + +#if defined(FIX_BUGS) && defined(GTA_PC) +RwUInt32 saved_alphafunc, saved_alpharef; + +void +SetAlphaTest(RwUInt32 alpharef) +{ +#ifdef LIBRW + saved_alphafunc = rw::GetRenderState(rw::ALPHATESTFUNC); + saved_alpharef = rw::GetRenderState(rw::ALPHATESTREF); + + rw::SetRenderState(rw::ALPHATESTFUNC, rw::ALPHAGREATEREQUAL); + rw::SetRenderState(rw::ALPHATESTREF, 0); +#else + RwD3D8GetRenderState(D3DRS_ALPHAFUNC, &saved_alphafunc); + RwD3D8GetRenderState(D3DRS_ALPHAREF, &saved_alpharef); + + RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL); + RwD3D8SetRenderState(D3DRS_ALPHAREF, alpharef); +#endif +} + +void +RestoreAlphaTest() +{ +#ifdef LIBRW + rw::SetRenderState(rw::ALPHATESTFUNC, saved_alphafunc); + rw::SetRenderState(rw::ALPHATESTREF, saved_alpharef); +#else + RwD3D8SetRenderState(D3DRS_ALPHAFUNC, saved_alphafunc); + RwD3D8SetRenderState(D3DRS_ALPHAREF, saved_alpharef); +#endif +} +#endif diff --git a/src/miami/rw/RwHelper.h b/src/miami/rw/RwHelper.h new file mode 100644 index 00000000..45ce4c3f --- /dev/null +++ b/src/miami/rw/RwHelper.h @@ -0,0 +1,65 @@ +#pragma once + +extern bool bDebugRenderGroups; +extern bool gPS2alphaTest; +extern bool gBackfaceCulling; + +void OpenCharsetSafe(); +void CreateDebugFont(); +void DestroyDebugFont(); +void ObrsPrintfString(const char *str, short x, short y); +void FlushObrsPrintfs(); +void DefinedState(void); +void SetCullMode(uint32 mode); +RwFrame *GetFirstChild(RwFrame *frame); +RwObject *GetFirstObject(RwFrame *frame); +RpAtomic *GetFirstAtomic(RpClump *clump); +RwTexture *GetFirstTexture(RwTexDictionary *txd); + +bool IsClumpSkinned(RpClump *clump); +RpHAnimHierarchy *GetAnimHierarchyFromSkinClump(RpClump *clump); // get from atomic +RpHAnimHierarchy *GetAnimHierarchyFromClump(RpClump *clump); // get from frame +void SkinGetBonePositionsToTable(RpClump *clump, RwV3d *boneTable); +RpHAnimAnimation *HAnimAnimationCreateForHierarchy(RpHAnimHierarchy *hier); +RpAtomic *AtomicRemoveAnimFromSkinCB(RpAtomic *atomic, void *data); +void RenderSkeleton(RpHAnimHierarchy *hier); + +RwBool Im2DRenderQuad(RwReal x1, RwReal y1, RwReal x2, RwReal y2, RwReal z, RwReal recipCamZ, RwReal uvOffset); +RpClump *RpClumpGetBoundingSphere(RpClump *clump, RwSphere *sphere, bool useLTM); + +RwTexDictionary *RwTexDictionaryGtaStreamRead(RwStream *stream); +RwTexDictionary *RwTexDictionaryGtaStreamRead1(RwStream *stream); +RwTexDictionary *RwTexDictionaryGtaStreamRead2(RwStream *stream, RwTexDictionary *texDict); +void RwTexDictionaryGtaStreamWrite(RwStream *stream, RwTexDictionary *texDict); +void ReadVideoCardCapsFile(uint32&, uint32&, uint32&, uint32&); +bool CheckVideoCardCaps(void); +void WriteVideoCardCapsFile(void); +bool CanVideoCardDoDXT(void); +void ConvertingTexturesScreen(uint32, uint32, const char*); +void DealWithTxdWriteError(uint32, uint32, const char*); +bool CreateTxdImageForVideoCard(); + +bool RpClumpGtaStreamRead1(RwStream *stream); +RpClump *RpClumpGtaStreamRead2(RwStream *stream); +void RpClumpGtaCancelStream(void); + +void CameraSize(RwCamera *camera, + RwRect *rect, + RwReal viewWindow, + RwReal aspectRatio); +void CameraDestroy(RwCamera *camera); +RwCamera *CameraCreate(RwInt32 width, + RwInt32 height, + RwBool zBuffer); + + + +RpAtomic *ConvertPlatformAtomic(RpAtomic *atomic, void *data); + +#if defined(FIX_BUGS) && defined (GTA_PC) +void SetAlphaTest(RwUInt32 alpharef); +void RestoreAlphaTest(); +#else +#define SetAlphaTest(a) (0) +#define RestoreAlphaTest() (0) +#endif \ No newline at end of file diff --git a/src/miami/rw/RwMatFX.cpp b/src/miami/rw/RwMatFX.cpp new file mode 100644 index 00000000..c8384b0f --- /dev/null +++ b/src/miami/rw/RwMatFX.cpp @@ -0,0 +1,312 @@ +#ifndef LIBRW + +#define WITHD3D +#include "common.h" +#include "rpmatfx.h" + +struct MatFXNothing { int pad[5]; int effect; }; + +struct MatFXBump +{ + RwFrame *bumpFrame; + RwTexture *bumpedTex; + RwTexture *bumpTex; + float negBumpCoefficient; + int pad; + int effect; +}; + +struct MatFXEnv +{ + RwFrame *envFrame; + RwTexture *envTex; + float envCoeff; + int envFBalpha; + int pad; + int effect; +}; + +struct MatFXDual +{ + RwTexture *dualTex; + RwInt32 srcBlend; + RwInt32 dstBlend; +}; + + +struct MatFX +{ + union { + MatFXNothing n; + MatFXBump b; + MatFXEnv e; + MatFXDual d; + } fx[2]; + int effects; +}; + +extern "C" { + extern int MatFXMaterialDataOffset; + extern int MatFXAtomicDataOffset; + + void _rpMatFXD3D8AtomicMatFXEnvRender(RxD3D8InstanceData* inst, int flags, int sel, RwTexture* texture, RwTexture* envMap); + void _rpMatFXD3D8AtomicMatFXRenderBlack(RxD3D8InstanceData *inst); + void _rpMatFXD3D8AtomicMatFXBumpMapRender(RxD3D8InstanceData *inst, int flags, RwTexture *texture, RwTexture *bumpMap, RwTexture *envMap); + void _rpMatFXD3D8AtomicMatFXDualPassRender(RxD3D8InstanceData *inst, int flags, RwTexture *texture, RwTexture *dualTexture); +} + + +#ifdef PS2_MATFX + +void +_rpMatFXD3D8AtomicMatFXDefaultRender(RxD3D8InstanceData *inst, int flags, RwTexture *texture) +{ + if(flags & (rpGEOMETRYTEXTURED|rpGEOMETRYTEXTURED2) && texture) + RwD3D8SetTexture(texture, 0); + else + RwD3D8SetTexture(nil, 0); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)(inst->vertexAlpha || inst->material->color.alpha != 0xFF)); + RwD3D8SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, inst->vertexAlpha != 0); + RwD3D8SetPixelShader(0); + RwD3D8SetVertexShader(inst->vertexShader); + RwD3D8SetStreamSource(0, inst->vertexBuffer, inst->stride); + + if(inst->indexBuffer){ + RwD3D8SetIndices(inst->indexBuffer, inst->baseIndex); + RwD3D8DrawIndexedPrimitive(inst->primType, 0, inst->numVertices, 0, inst->numIndices); + }else + RwD3D8DrawPrimitive(inst->primType, inst->baseIndex, inst->numVertices); +} + +// map [-1; -1] -> [0; 1], flip V +static RwMatrix scalenormal = { + { 0.5f, 0.0f, 0.0f }, 0, + { 0.0f, -0.5f, 0.0f }, 0, + { 0.0f, 0.0f, 1.0f }, 0, + { 0.5f, 0.5f, 0.0f }, 0, + +}; + +// flipped U for PS2 +static RwMatrix scalenormal_flipU = { + { -0.5f, 0.0f, 0.0f }, 0, + { 0.0f, -0.5f, 0.0f }, 0, + { 0.0f, 0.0f, 1.0f }, 0, + { 0.5f, 0.5f, 0.0f }, 0, + +}; + +void +ApplyEnvMapTextureMatrix(RwTexture *tex, int n, RwFrame *frame) +{ + RwD3D8SetTexture(tex, n); + RwD3D8SetTextureStageState(n, D3DRS_ALPHAREF, 2); + RwD3D8SetTextureStageState(n, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACENORMAL); + if(frame){ + RwMatrix *envframemat = RwMatrixCreate(); + RwMatrix *tmpmat = RwMatrixCreate(); + RwMatrix *envmat = RwMatrixCreate(); + + RwMatrixInvert(envframemat, RwFrameGetLTM(frame)); + // PS2 + // can this be simplified? + *tmpmat = *RwFrameGetLTM(RwCameraGetFrame((RwCamera*)RWSRCGLOBAL(curCamera))); + RwV3dNegate(&tmpmat->right, &tmpmat->right); + tmpmat->flags = 0; + tmpmat->pos.x = 0.0f; + tmpmat->pos.y = 0.0f; + tmpmat->pos.z = 0.0f; + RwMatrixMultiply(envmat, tmpmat, envframemat); + *tmpmat = *envmat; + // important because envframemat can have a translation that we don't like + tmpmat->pos.x = 0.0f; + tmpmat->pos.y = 0.0f; + tmpmat->pos.z = 0.0f; + // for some reason we flip in U as well + RwMatrixMultiply(envmat, tmpmat, &scalenormal_flipU); + + RwD3D8SetTransform(D3DTS_TEXTURE0+n, envmat); + + RwMatrixDestroy(envmat); + RwMatrixDestroy(tmpmat); + RwMatrixDestroy(envframemat); + }else + RwD3D8SetTransform(D3DTS_TEXTURE0+n, &scalenormal); +} + +void +_rpMatFXD3D8AtomicMatFXEnvRender_ps2(RxD3D8InstanceData *inst, int flags, int sel, RwTexture *texture, RwTexture *envMap) +{ + MatFX *matfx = *RWPLUGINOFFSET(MatFX*, inst->material, MatFXMaterialDataOffset); + MatFXEnv *env = &matfx->fx[sel].e; + + uint8 intens = (uint8)(env->envCoeff*255.0f); + + if(intens == 0 || envMap == nil){ + if(sel == 0) + _rpMatFXD3D8AtomicMatFXDefaultRender(inst, flags, texture); + return; + } + + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)(inst->vertexAlpha || inst->material->color.alpha != 0xFF)); + if(flags & (rpGEOMETRYTEXTURED|rpGEOMETRYTEXTURED2) && texture) + RwD3D8SetTexture(texture, 0); + else + RwD3D8SetTexture(nil, 0); + RwD3D8SetPixelShader(0); + RwD3D8SetVertexShader(inst->vertexShader); + RwD3D8SetStreamSource(0, inst->vertexBuffer, inst->stride); + RwD3D8SetIndices(inst->indexBuffer, inst->baseIndex); + if(inst->indexBuffer) + RwD3D8DrawIndexedPrimitive(inst->primType, 0, inst->numVertices, 0, inst->numIndices); + else + RwD3D8DrawPrimitive(inst->primType, inst->baseIndex, inst->numVertices); + + // Effect pass + + ApplyEnvMapTextureMatrix(envMap, 0, env->envFrame); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwUInt32 src, dst, lighting, zwrite, fog, fogcol; + RwRenderStateGet(rwRENDERSTATESRCBLEND, &src); + RwRenderStateGet(rwRENDERSTATEDESTBLEND, &dst); + + // This is of course not using framebuffer alpha, + // but if the diffuse texture had no alpha, the result should actually be rather the same + if(env->envFBalpha) + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + else + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + RwD3D8GetRenderState(D3DRS_LIGHTING, &lighting); + RwD3D8GetRenderState(D3DRS_ZWRITEENABLE, &zwrite); + RwD3D8GetRenderState(D3DRS_FOGENABLE, &fog); + RwD3D8SetRenderState(D3DRS_ZWRITEENABLE, FALSE); + if(fog){ + RwD3D8GetRenderState(D3DRS_FOGCOLOR, &fogcol); + RwD3D8SetRenderState(D3DRS_FOGCOLOR, 0); + } + + D3DCOLOR texfactor = D3DCOLOR_RGBA(intens, intens, intens, intens); + RwD3D8SetRenderState(D3DRS_TEXTUREFACTOR, texfactor); + RwD3D8SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE); + RwD3D8SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT); + RwD3D8SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_TFACTOR); + // alpha unused + //RwD3D8SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + //RwD3D8SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT); + //RwD3D8SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_TFACTOR); + + if(inst->indexBuffer) + RwD3D8DrawIndexedPrimitive(inst->primType, 0, inst->numVertices, 0, inst->numIndices); + else + RwD3D8DrawPrimitive(inst->primType, inst->baseIndex, inst->numVertices); + + // Reset states + + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)src); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)dst); + RwD3D8SetRenderState(D3DRS_LIGHTING, lighting); + RwD3D8SetRenderState(D3DRS_ZWRITEENABLE, zwrite); + if(fog) + RwD3D8SetRenderState(D3DRS_FOGCOLOR, fogcol); + RwD3D8SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + RwD3D8SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + RwD3D8SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, 0); + RwD3D8SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0); +} + +void +_rwD3D8EnableClippingIfNeeded(void *object, RwUInt8 type) +{ + int clip; + if (type == rpATOMIC) + clip = !RwD3D8CameraIsSphereFullyInsideFrustum(RwCameraGetCurrentCameraMacro(), RpAtomicGetWorldBoundingSphere((RpAtomic *)object)); + else + clip = !RwD3D8CameraIsBBoxFullyInsideFrustum(RwCameraGetCurrentCameraMacro(), &((RpWorldSector *)object)->tightBoundingBox); + RwD3D8SetRenderState(D3DRS_CLIPPING, clip); +} + +void +_rwD3D8AtomicMatFXRenderCallback(RwResEntry *repEntry, void *object, RwUInt8 type, RwUInt32 flags) +{ + RwBool lighting; + RwBool forceBlack; + RxD3D8ResEntryHeader *header; + RxD3D8InstanceData *inst; + RwInt32 i; + + if (flags & rpGEOMETRYPRELIT) { + RwD3D8SetRenderState(D3DRS_COLORVERTEX, 1); + RwD3D8SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_COLOR1); + } else { + RwD3D8SetRenderState(D3DRS_COLORVERTEX, 0); + RwD3D8SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL); + } + + _rwD3D8EnableClippingIfNeeded(object, type); + + RwD3D8GetRenderState(D3DRS_LIGHTING, &lighting); + if (lighting || flags & rpGEOMETRYPRELIT) { + forceBlack = FALSE; + } else { + forceBlack = TRUE; + RwD3D8SetTexture(nil, 0); + RwD3D8SetRenderState(D3DRS_TEXTUREFACTOR, D3DCOLOR_RGBA(0, 0, 0, 255)); + RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2); + RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR); + } + + header = (RxD3D8ResEntryHeader *)(repEntry + 1); + inst = (RxD3D8InstanceData *)(header + 1); + for (i = 0; i < header->numMeshes; i++) { + if (forceBlack) + _rpMatFXD3D8AtomicMatFXRenderBlack(inst); + else { + if (lighting) + RwD3D8SetSurfaceProperties(&inst->material->color, &inst->material->surfaceProps, flags & rpGEOMETRYMODULATEMATERIALCOLOR); + MatFX *matfx = *RWPLUGINOFFSET(MatFX *, inst->material, MatFXMaterialDataOffset); + int effect = matfx ? matfx->effects : rpMATFXEFFECTNULL; + switch (effect) { + case rpMATFXEFFECTNULL: + default: + _rpMatFXD3D8AtomicMatFXDefaultRender(inst, flags, inst->material->texture); + break; + case rpMATFXEFFECTBUMPMAP: + _rpMatFXD3D8AtomicMatFXBumpMapRender(inst, flags, inst->material->texture, matfx->fx[0].b.bumpedTex, nil); + break; + case rpMATFXEFFECTENVMAP: + { + // TODO: matfx switch in the settings + //_rpMatFXD3D8AtomicMatFXEnvRender(inst, flags, 0, inst->material->texture, matfx->fx[0].e.envTex); + _rpMatFXD3D8AtomicMatFXEnvRender_ps2(inst, flags, 0, inst->material->texture, matfx->fx[0].e.envTex); + break; + } + case rpMATFXEFFECTBUMPENVMAP: + _rpMatFXD3D8AtomicMatFXBumpMapRender(inst, flags, inst->material->texture, matfx->fx[0].b.bumpedTex, matfx->fx[1].e.envTex); + break; + case rpMATFXEFFECTDUAL: + _rpMatFXD3D8AtomicMatFXDualPassRender(inst, flags, inst->material->texture, matfx->fx[0].d.dualTex); + break; + } + } + inst++; + } + + if (forceBlack) { + RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2); + RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + } +} + +void +ReplaceMatFxCallback() +{ + RxD3D8AllInOneSetRenderCallBack( + RxPipelineFindNodeByName(RpMatFXGetD3D8Pipeline(rpMATFXD3D8ATOMICPIPELINE), RxNodeDefinitionGetD3D8AtomicAllInOne()->name, nil, nil), + _rwD3D8AtomicMatFXRenderCallback); + +} +#endif // PS2_MATFX + +#endif // !LIBRW diff --git a/src/miami/rw/RwPS2AlphaTest.cpp b/src/miami/rw/RwPS2AlphaTest.cpp new file mode 100644 index 00000000..c0d68355 --- /dev/null +++ b/src/miami/rw/RwPS2AlphaTest.cpp @@ -0,0 +1,247 @@ +#ifndef LIBRW + +#define WITHD3D +#include "common.h" +#ifdef PS2_ALPHA_TEST +#include "rwcore.h" + +extern "C" { +RwBool _rwD3D8RenderStateIsVertexAlphaEnable(void); +RwBool _rwD3D8RenderStateVertexAlphaEnable(RwBool enable); +RwRaster *_rwD3D8RWGetRasterStage(RwUInt32 stage); +} + +extern bool gPS2alphaTest; + +void +_rxD3D8DualPassRenderCallback(RwResEntry *repEntry, void *object, RwUInt8 type, RwUInt32 flags) +{ + RxD3D8ResEntryHeader *resEntryHeader; + RxD3D8InstanceData *instancedData; + RwInt32 numMeshes; + RwBool lighting; + RwBool vertexAlphaBlend; + RwBool forceBlack; + RwUInt32 ditherEnable; + RwUInt32 shadeMode; + void *lastVertexBuffer; + + /* Get lighting state */ + RwD3D8GetRenderState(D3DRS_LIGHTING, &lighting); + + forceBlack = FALSE; + + if (lighting) { + if (flags & rxGEOMETRY_PRELIT) { + /* Emmisive color from the vertex colors */ + RwD3D8SetRenderState(D3DRS_COLORVERTEX, TRUE); + RwD3D8SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_COLOR1); + } else { + /* Emmisive color from material, set to black in the submit node */ + RwD3D8SetRenderState(D3DRS_COLORVERTEX, FALSE); + RwD3D8SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL); + } + } else { + if ((flags & rxGEOMETRY_PRELIT) == 0) { + forceBlack = TRUE; + + RwD3D8GetRenderState(D3DRS_DITHERENABLE, &ditherEnable); + RwD3D8GetRenderState(D3DRS_SHADEMODE, &shadeMode); + + RwD3D8SetRenderState(D3DRS_TEXTUREFACTOR, 0xff000000); + RwD3D8SetRenderState(D3DRS_DITHERENABLE, FALSE); + RwD3D8SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT); + } + } + + /* Enable clipping */ + if (type == rpATOMIC) { + RpAtomic *atomic; + RwCamera *cam; + + atomic = (RpAtomic *)object; + + cam = RwCameraGetCurrentCamera(); + // RWASSERT(cam); + + if (RwD3D8CameraIsSphereFullyInsideFrustum(cam, RpAtomicGetWorldBoundingSphere(atomic))) { + RwD3D8SetRenderState(D3DRS_CLIPPING, FALSE); + } else { + RwD3D8SetRenderState(D3DRS_CLIPPING, TRUE); + } + } else { + RpWorldSector *worldSector; + RwCamera *cam; + + worldSector = (RpWorldSector *)object; + + cam = RwCameraGetCurrentCamera(); + // RWASSERT(cam); + + if (RwD3D8CameraIsBBoxFullyInsideFrustum(cam, RpWorldSectorGetTightBBox(worldSector))) { + RwD3D8SetRenderState(D3DRS_CLIPPING, FALSE); + } else { + RwD3D8SetRenderState(D3DRS_CLIPPING, TRUE); + } + } + + /* Set texture to NULL if hasn't any texture flags */ + if ((flags & (rxGEOMETRY_TEXTURED | rpGEOMETRYTEXTURED2)) == 0) { + RwD3D8SetTexture(NULL, 0); + + if (forceBlack) { + RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2); + RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR); + + RwD3D8SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + } + } + + /* Get vertex alpha Blend state */ + vertexAlphaBlend = _rwD3D8RenderStateIsVertexAlphaEnable(); + + /* Set Last vertex buffer to force the call */ + lastVertexBuffer = (void *)0xffffffff; + + /* Get the instanced data */ + resEntryHeader = (RxD3D8ResEntryHeader *)(repEntry + 1); + instancedData = (RxD3D8InstanceData *)(resEntryHeader + 1); + + /* + * Data shared between meshes + */ + + /* + * Set the Default Pixel shader + */ + RwD3D8SetPixelShader(0); + + /* + * Vertex shader + */ + RwD3D8SetVertexShader(instancedData->vertexShader); + + /* Get the number of meshes */ + numMeshes = resEntryHeader->numMeshes; + while (numMeshes--) { + // RWASSERT(instancedData->material != NULL); + + if ((flags & (rxGEOMETRY_TEXTURED | rpGEOMETRYTEXTURED2))) { + RwD3D8SetTexture(instancedData->material->texture, 0); + + if (forceBlack) { + /* Only change the colorop, we need to use the texture alpha channel */ + RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2); + RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR); + } + } + + if (instancedData->vertexAlpha || (0xFF != instancedData->material->color.alpha)) { + if (!vertexAlphaBlend) { + vertexAlphaBlend = TRUE; + + _rwD3D8RenderStateVertexAlphaEnable(TRUE); + } + } else { + if (vertexAlphaBlend) { + vertexAlphaBlend = FALSE; + + _rwD3D8RenderStateVertexAlphaEnable(FALSE); + } + } + + if (lighting) { + if (instancedData->vertexAlpha) { + RwD3D8SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1); + } else { + RwD3D8SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL); + } + + RwD3D8SetSurfaceProperties(&instancedData->material->color, &instancedData->material->surfaceProps, (flags & rxGEOMETRY_MODULATE)); + } + + /* + * Render + */ + + /* Set the stream source */ + if (lastVertexBuffer != instancedData->vertexBuffer) { + RwD3D8SetStreamSource(0, instancedData->vertexBuffer, instancedData->stride); + + lastVertexBuffer = instancedData->vertexBuffer; + } + if (!gPS2alphaTest) { + /* Set the Index buffer */ + if (instancedData->indexBuffer != NULL) { + RwD3D8SetIndices(instancedData->indexBuffer, instancedData->baseIndex); + + /* Draw the indexed primitive */ + RwD3D8DrawIndexedPrimitive((D3DPRIMITIVETYPE)instancedData->primType, 0, instancedData->numVertices, 0, instancedData->numIndices); + } else { + RwD3D8DrawPrimitive((D3DPRIMITIVETYPE)instancedData->primType, instancedData->baseIndex, instancedData->numVertices); + } + } else { + RwD3D8SetIndices(instancedData->indexBuffer, instancedData->baseIndex); + + int hasAlpha, alphafunc, alpharef, zwrite; + RwD3D8GetRenderState(D3DRS_ALPHABLENDENABLE, &hasAlpha); + RwD3D8GetRenderState(D3DRS_ZWRITEENABLE, &zwrite); + if (hasAlpha && zwrite) { + RwD3D8GetRenderState(D3DRS_ALPHAFUNC, &alphafunc); + RwD3D8GetRenderState(D3DRS_ALPHAREF, &alpharef); + + RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL); + RwD3D8SetRenderState(D3DRS_ALPHAREF, 128); + + if (instancedData->indexBuffer) + RwD3D8DrawIndexedPrimitive(instancedData->primType, 0, instancedData->numVertices, 0, instancedData->numIndices); + else + RwD3D8DrawPrimitive(instancedData->primType, instancedData->baseIndex, instancedData->numVertices); + + RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_LESS); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, FALSE); + + if (instancedData->indexBuffer) + RwD3D8DrawIndexedPrimitive(instancedData->primType, 0, instancedData->numVertices, 0, instancedData->numIndices); + else + RwD3D8DrawPrimitive(instancedData->primType, instancedData->baseIndex, instancedData->numVertices); + + RwD3D8SetRenderState(D3DRS_ALPHAFUNC, alphafunc); + RwD3D8SetRenderState(D3DRS_ALPHAREF, alpharef); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE); + } else { + if (instancedData->indexBuffer) + RwD3D8DrawIndexedPrimitive(instancedData->primType, 0, instancedData->numVertices, 0, instancedData->numIndices); + else + RwD3D8DrawPrimitive(instancedData->primType, instancedData->baseIndex, instancedData->numVertices); + } + } + + /* Move onto the next instancedData */ + instancedData++; + } + + if (forceBlack) { + RwD3D8SetRenderState(D3DRS_DITHERENABLE, ditherEnable); + RwD3D8SetRenderState(D3DRS_SHADEMODE, shadeMode); + + if (_rwD3D8RWGetRasterStage(0)) { + RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + } else { + RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2); + RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + } + } +} + +void +ReplaceAtomicPipeCallback() +{ + RxD3D8AllInOneSetRenderCallBack(RxPipelineFindNodeByName(RXPIPELINEGLOBAL(platformAtomicPipeline), RxNodeDefinitionGetD3D8AtomicAllInOne()->name, nil, nil), + _rxD3D8DualPassRenderCallback); +} + +#endif // PS2_ALPHA_TEST + +#endif // !LIBRW \ No newline at end of file diff --git a/src/miami/rw/TexRead.cpp b/src/miami/rw/TexRead.cpp new file mode 100644 index 00000000..da6ee7a0 --- /dev/null +++ b/src/miami/rw/TexRead.cpp @@ -0,0 +1,515 @@ +#pragma warning( push ) +#pragma warning( disable : 4005) +#pragma warning( pop ) +#define FORCE_PC_SCALING +#ifndef LIBRW +#define WITHD3D +#endif +#include "common.h" +#ifdef ANISOTROPIC_FILTERING +#include "rpanisot.h" +#endif +#include "crossplatform.h" +#include "platform.h" + +#include "Timer.h" +#ifdef GTA_PC +#include "FileMgr.h" +#include "Pad.h" +#include "main.h" +#include "Directory.h" +#include "Streaming.h" +#include "TxdStore.h" +#include "CdStream.h" +#include "Font.h" +#include "Sprite2d.h" +#include "Text.h" +#include "RwHelper.h" +#include "Frontend.h" +#endif //GTA_PC + +float texLoadTime; +int32 texNumLoaded; + +#ifdef LIBRW +#define READNATIVE(stream, tex, size) rwNativeTextureHackRead(stream, tex, size) +#else +#define READNATIVE(stream, tex, size) RWSRCGLOBAL(stdFunc[rwSTANDARDNATIVETEXTUREREAD](stream, tex, size)) +#endif + +void RwTextureGtaStreamWrite(RwStream *stream, RwTexture* texture) +{ + auto fheader = stream->tell(); + // size will be written later + rw::writeChunkHeader(stream, rwID_TEXTURENATIVE, 0); + auto fbegin = stream->tell(); + + texture->streamWriteNative(stream); + + // rewrite header with correct size + auto fend = stream->tell(); + stream->seek(fheader, 0); + rw::writeChunkHeader(stream, rwID_TEXTURENATIVE, fend - fbegin); + stream->seek(fend, 0); +} + +RwTexture* +RwTextureGtaStreamRead(RwStream *stream) +{ + RwUInt32 size, version; + RwTexture *tex; + + if(!RwStreamFindChunk(stream, rwID_TEXTURENATIVE, &size, &version)) + return nil; + + float preloadTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerMillisecond(); + + if(!READNATIVE(stream, &tex, size)) + return nil; + + if (gGameState == GS_INIT_PLAYING_GAME) { + texLoadTime = (texNumLoaded * texLoadTime + (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerMillisecond() - preloadTime) / (float)(texNumLoaded+1); + texNumLoaded++; + } + +#ifdef ANISOTROPIC_FILTERING + if(tex && RpAnisotGetMaxSupportedMaxAnisotropy() > 1) // BUG? this was RpAnisotTextureGetMaxAnisotropy, but that doesn't make much sense + RpAnisotTextureSetMaxAnisotropy(tex, RpAnisotGetMaxSupportedMaxAnisotropy()); +#endif + + return tex; +} + +RwTexture* +destroyTexture(RwTexture *texture, void *data) +{ + RwTextureDestroy(texture); + return texture; +} + +void +RwTexDictionaryGtaStreamWrite(RwStream *stream, RwTexDictionary *texDict) { + rw::writeChunkHeader(stream, rwID_STRUCT, 4); + stream->writeI32(texDict->count()); + + RwTexDictionaryForAllTextures(texDict, [](RwTexture *texture, void* vstream) { + RwTextureGtaStreamWrite((RwStream*)vstream, texture); + return texture; + }, stream); +} + + +RwTexDictionary* +RwTexDictionaryGtaStreamRead(RwStream *stream) +{ + RwUInt32 size, version; + RwInt32 numTextures; + RwTexDictionary *texDict; + RwTexture *tex; + + if(!RwStreamFindChunk(stream, rwID_STRUCT, &size, &version)) + return nil; + if(RwStreamRead(stream, &numTextures, size) != size) + return nil; + + texDict = RwTexDictionaryCreate(); + if(texDict == nil) + return nil; + + while(numTextures--){ + tex = RwTextureGtaStreamRead(stream); + if(tex == nil){ + RwTexDictionaryForAllTextures(texDict, destroyTexture, nil); + RwTexDictionaryDestroy(texDict); + return nil; + } + RwTexDictionaryAddTexture(texDict, tex); + } + + return texDict; +} + +static int32 numberTextures = -1; +static int32 streamPosition; + +RwTexDictionary* +RwTexDictionaryGtaStreamRead1(RwStream *stream) +{ + RwUInt32 size, version; + RwInt32 numTextures; + RwTexDictionary *texDict; + RwTexture *tex; + + numberTextures = 0; + if(!RwStreamFindChunk(stream, rwID_STRUCT, &size, &version)) + return nil; + assert(size == 4); + if(RwStreamRead(stream, &numTextures, size) != size) + return nil; + + texDict = RwTexDictionaryCreate(); + if(texDict == nil) + return nil; + + numberTextures = numTextures/2; + + while(numTextures > numberTextures){ + numTextures--; + + tex = RwTextureGtaStreamRead(stream); + if(tex == nil){ + RwTexDictionaryForAllTextures(texDict, destroyTexture, nil); + RwTexDictionaryDestroy(texDict); + return nil; + } + RwTexDictionaryAddTexture(texDict, tex); + } + + numberTextures = numTextures; + streamPosition = STREAMPOS(stream); + + return texDict; +} + +RwTexDictionary* +RwTexDictionaryGtaStreamRead2(RwStream *stream, RwTexDictionary *texDict) +{ + RwTexture *tex; + + RwStreamSkip(stream, streamPosition - STREAMPOS(stream)); + + while(numberTextures--){ + tex = RwTextureGtaStreamRead(stream); + if(tex == nil){ + RwTexDictionaryForAllTextures(texDict, destroyTexture, nil); + RwTexDictionaryDestroy(texDict); + return nil; + } + RwTexDictionaryAddTexture(texDict, tex); + } + + return texDict; +} + +#ifdef GTA_PC + +#ifdef LIBRW + +#define CAPSVERSION 0 + +struct GPUcaps +{ + uint32 version; // so we can force regeneration easily + uint32 platform; + uint32 subplatform; + uint32 dxtSupport; +}; + +static void +GetGPUcaps(GPUcaps *caps) +{ + caps->version = CAPSVERSION; + caps->platform = rw::platform; + caps->subplatform = 0; + caps->dxtSupport = 0; + // TODO: more later +#ifdef RW_GL3 + caps->subplatform = rw::gl3::gl3Caps.gles; + caps->dxtSupport = rw::gl3::gl3Caps.dxtSupported; +#endif +#ifdef RW_D3D9 + caps->dxtSupport = 1; // TODO, probably +#endif +} + +void +ReadVideoCardCapsFile(GPUcaps *caps) +{ + memset(caps, 0, sizeof(GPUcaps)); + + int32 file = CFileMgr::OpenFile("DATA\\CAPS.DAT", "rb"); + if (file != 0) { + CFileMgr::Read(file, (char*)&caps->version, 4); + CFileMgr::Read(file, (char*)&caps->platform, 4); + CFileMgr::Read(file, (char*)&caps->subplatform, 4); + CFileMgr::Read(file, (char*)&caps->dxtSupport, 4); + CFileMgr::CloseFile(file); + } +} + +bool +CheckVideoCardCaps(void) +{ + GPUcaps caps, fcaps; + GetGPUcaps(&caps); + ReadVideoCardCapsFile(&fcaps); + return caps.version != fcaps.version || + caps.platform != fcaps.platform || + caps.subplatform != fcaps.subplatform || + caps.dxtSupport != fcaps.dxtSupport; +} + +void +WriteVideoCardCapsFile(void) +{ + GPUcaps caps; + GetGPUcaps(&caps); + int32 file = CFileMgr::OpenFile("DATA\\CAPS.DAT", "wb"); + if (file != 0) { + CFileMgr::Write(file, (char*)&caps.version, 4); + CFileMgr::Write(file, (char*)&caps.platform, 4); + CFileMgr::Write(file, (char*)&caps.subplatform, 4); + CFileMgr::Write(file, (char*)&caps.dxtSupport, 4); + CFileMgr::CloseFile(file); + } +} + + +#else +extern "C" RwInt32 _rwD3D8FindCorrectRasterFormat(RwRasterType type, RwInt32 flags); +extern "C" RwBool _rwD3D8CheckValidTextureFormat(RwInt32 format); +void +ReadVideoCardCapsFile(uint32 &cap32, uint32 &cap24, uint32 &cap16, uint32 &cap8) +{ + cap32 = UINT32_MAX; + cap24 = UINT32_MAX; + cap16 = UINT32_MAX; + cap8 = UINT32_MAX; + + int32 file = CFileMgr::OpenFile("DATA\\CAPS.DAT", "rb"); + if (file != 0) { + CFileMgr::Read(file, (char*)&cap32, 4); + CFileMgr::Read(file, (char*)&cap24, 4); + CFileMgr::Read(file, (char*)&cap16, 4); + CFileMgr::Read(file, (char*)&cap8, 4); + CFileMgr::CloseFile(file); + } +} + +bool +CheckVideoCardCaps(void) +{ + uint32 cap32 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT8888); + uint32 cap24 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT888); + uint32 cap16 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT1555); + uint32 cap8 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMATPAL8 | rwRASTERFORMAT8888); + uint32 fcap32, fcap24, fcap16, fcap8; + ReadVideoCardCapsFile(fcap32, fcap24, fcap16, fcap8); + return cap32 != fcap32 || cap24 != fcap24 || cap16 != fcap16 || cap8 != fcap8; +} + +void +WriteVideoCardCapsFile(void) +{ + uint32 cap32 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT8888); + uint32 cap24 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT888); + uint32 cap16 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT1555); + uint32 cap8 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMATPAL8 | rwRASTERFORMAT8888); + int32 file = CFileMgr::OpenFile("DATA\\CAPS.DAT", "wb"); + if (file != 0) { + CFileMgr::Write(file, (char*)&cap32, 4); + CFileMgr::Write(file, (char*)&cap24, 4); + CFileMgr::Write(file, (char*)&cap16, 4); + CFileMgr::Write(file, (char*)&cap8, 4); + CFileMgr::CloseFile(file); + } +} +#endif + +bool +CanVideoCardDoDXT(void) +{ +#ifdef LIBRW + // TODO +#ifdef RW_OPENGL + return false; +#else + return true; +#endif +#else + return _rwD3D8CheckValidTextureFormat(D3DFMT_DXT1) && _rwD3D8CheckValidTextureFormat(D3DFMT_DXT3); +#endif +} + +void +ConvertingTexturesScreen(uint32 num, uint32 count, const char *text) +{ + HandleExit(); + + CSprite2d *splash = LoadSplash(nil); + if (!DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255)) + return; + + CSprite2d::SetRecipNearClip(); + CSprite2d::InitPerFrame(); + CFont::InitPerFrame(); + DefinedState(); + + RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP); + splash->Draw(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), CRGBA(255, 255, 255, 255)); + + CSprite2d::DrawRect(CRect(SCREEN_SCALE_X(200.0f), SCREEN_SCALE_Y(240.0f), SCREEN_SCALE_FROM_RIGHT(200.0f), SCREEN_SCALE_Y(248.0f)), CRGBA(64, 64, 64, 255)); +#ifdef FIX_BUGS + CSprite2d::DrawRect(CRect(SCREEN_SCALE_X(200.0f), SCREEN_SCALE_Y(240.0f), (SCREEN_SCALE_FROM_RIGHT(200.0f) - SCREEN_SCALE_X(200.0f)) * ((float)num / (float)count) + SCREEN_SCALE_X(200.0f), SCREEN_SCALE_Y(248.0f)), CRGBA(255, 150, 225, 255)); +#else + CSprite2d::DrawRect(CRect(SCREEN_SCALE_X(200.0f), SCREEN_SCALE_Y(240.0f), (SCREEN_SCALE_FROM_RIGHT(200.0f) - SCREEN_SCALE_X(200.0f)) * ((float)num / (float)count) + SCREEN_SCALE_X(200.0f), SCREEN_SCALE_Y(248.0f)), CRGBA(255, 217, 106, 255)); +#endif + CSprite2d::DrawRect(CRect(SCREEN_SCALE_X(120.0f), SCREEN_SCALE_Y(150.0f), SCREEN_SCALE_FROM_RIGHT(120.0f), SCREEN_HEIGHT - SCREEN_SCALE_Y(220.0f)), CRGBA(50, 50, 50, 210)); + + CFont::SetBackgroundOff(); + CFont::SetPropOn(); + CFont::SetScale(SCREEN_SCALE_X(0.45f), SCREEN_SCALE_Y(0.7f)); + CFont::SetCentreOff(); + CFont::SetWrapx(SCREEN_SCALE_FROM_RIGHT(170.0f)); + CFont::SetJustifyOff(); +#ifdef FIX_BUGS + CFont::SetColor(CRGBA(255, 150, 225, 255)); +#else + CFont::SetColor(CRGBA(255, 217, 106, 255)); +#endif + CFont::SetBackGroundOnlyTextOff(); + CFont::SetFontStyle(FONT_STANDARD); + CFont::PrintString(SCREEN_SCALE_X(170.0f), SCREEN_SCALE_Y(160.0f), TheText.Get(text)); + CFont::DrawFonts(); + DoRWStuffEndOfFrame(); +} + +void +DealWithTxdWriteError(uint32 num, uint32 count, const char *text) +{ + while (!RsGlobal.quit) { + ConvertingTexturesScreen(num, count, text); + CPad::UpdatePads(); + if (CPad::GetPad(0)->GetEscapeJustDown()) + break; + } + RsGlobal.quit = false; + LoadingScreen(nil, nil, nil); + RsGlobal.quit = true; +} + +#ifdef LIBRW +#define STREAMTELL(str) str->tell() +#else +#define STREAMTELL(str) filesys->rwftell((str)->Type.file.fpFile) +#endif + +bool +CreateTxdImageForVideoCard() +{ + uint8 *buf = new uint8[CDSTREAM_SECTOR_SIZE]; + CDirectory *pDir = new CDirectory(TXDSTORESIZE); + CDirectory::DirectoryInfo dirInfo; + + CStreaming::FlushRequestList(); + +#ifndef LIBRW + RwFileFunctions *filesys = RwOsGetFileInterface(); +#endif + + RwStream *img = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMWRITE, "models\\txd.img"); + if (img == nil) { + // original code does otherwise and it leaks + delete []buf; + delete pDir; + + if (_dwOperatingSystemVersion == OS_WINNT || _dwOperatingSystemVersion == OS_WIN2000 || _dwOperatingSystemVersion == OS_WINXP) + DealWithTxdWriteError(0, TXDSTORESIZE, "CVT_CRT"); + + return false; + } + +#ifdef RW_GL3 + // so we can read back DXT with GLES + // only works for textures that are not yet loaded + // so let's hope that is the case for all + rw::gl3::needToReadBackTextures = true; +#endif + +#ifdef DISABLE_VSYNC_ON_TEXTURE_CONVERSION + // let's disable vsync and frame limiter to speed up texture conversion + // (actually we probably don't need to disable frame limiter in here, but let's do it just in case =P) + int8 vsyncState = FrontEndMenuManager.m_PrefsVsync; + int8 frameLimiterState = FrontEndMenuManager.m_PrefsFrameLimiter; + FrontEndMenuManager.m_PrefsVsync = 0; + FrontEndMenuManager.m_PrefsFrameLimiter = 0; +#endif + + int32 i; + for (i = 0; i < TXDSTORESIZE; i++) { + ConvertingTexturesScreen(i, TXDSTORESIZE, "CVT_MSG"); + + if (CTxdStore::GetSlot(i) != nil && CStreaming::IsObjectInCdImage(i + STREAM_OFFSET_TXD)) { +#ifdef FIX_BUGS + if(strcmp(CTxdStore::GetTxdName(i), "generic") == 0) + continue; +#endif + + CStreaming::RequestTxd(i, STREAMFLAGS_KEEP_IN_MEMORY); + CStreaming::RequestModelStream(0); + CStreaming::FlushChannels(); + + char filename[64]; + sprintf(filename, "%s.txd", CTxdStore::GetTxdName(i)); + + if (CTxdStore::GetSlot(i)->texDict) { + + int32 pos = STREAMTELL(img); + + if (RwTexDictionaryStreamWrite(CTxdStore::GetSlot(i)->texDict, img) == nil) { + DealWithTxdWriteError(i, TXDSTORESIZE, "CVT_ERR"); + RwStreamClose(img, nil); + delete []buf; + delete pDir; + CStreaming::RemoveTxd(i); +#ifdef RW_GL3 + rw::gl3::needToReadBackTextures = false; +#endif + return false; + } + + int32 size = STREAMTELL(img) - pos; + int32 num = size % CDSTREAM_SECTOR_SIZE; + + size /= CDSTREAM_SECTOR_SIZE; + if (num != 0) { + size++; + num = CDSTREAM_SECTOR_SIZE - num; + RwStreamWrite(img, buf, num); + } + + dirInfo.offset = pos / CDSTREAM_SECTOR_SIZE; + dirInfo.size = size; + strncpy(dirInfo.name, filename, sizeof(dirInfo.name)); + pDir->AddItem(dirInfo); + CStreaming::RemoveTxd(i); + } + CStreaming::FlushRequestList(); + } + } + +#ifdef DISABLE_VSYNC_ON_TEXTURE_CONVERSION + // restore vsync and frame limiter states + FrontEndMenuManager.m_PrefsVsync = vsyncState; + FrontEndMenuManager.m_PrefsFrameLimiter = frameLimiterState; +#endif + + RwStreamClose(img, nil); + delete []buf; + +#ifdef RW_GL3 + rw::gl3::needToReadBackTextures = false; +#endif + + if (!pDir->WriteDirFile("models\\txd.dir")) { + DealWithTxdWriteError(i, TXDSTORESIZE, "CVT_ERR"); + delete pDir; + return false; + } + + delete pDir; + + WriteVideoCardCapsFile(); + return true; +} +#endif // GTA_PC diff --git a/src/miami/rw/TexturePools.cpp b/src/miami/rw/TexturePools.cpp new file mode 100644 index 00000000..c2ba6cf9 --- /dev/null +++ b/src/miami/rw/TexturePools.cpp @@ -0,0 +1,221 @@ +#ifndef LIBRW + +#include +#define WITHD3D +#include "common.h" +#include "TexturePools.h" + +// TODO: this needs to be integrated into RW + +extern "C" LPDIRECT3DDEVICE8 _RwD3DDevice; + +CTexturePool aTexturePools[12]; +CPaletteList PaletteList; +int numTexturePools; +int MaxPaletteIndex; +bool bUsePaletteIndex = true; + + +void +CTexturePool::Create(D3DFORMAT _Format, int _size, uint32 mipmapLevels, int32 numTextures) +{ + Format = _Format; + size = _size; + levels = mipmapLevels; + pTextures = new IDirect3DTexture8 *[numTextures]; + texturesMax = numTextures; + texturesNum = 0; + texturesUsed = 0; +} + +void +CTexturePool::Release() +{ + int i = 0; + while (i < texturesNum) { + pTextures[i]->Release(); + i++; + } + + delete[] pTextures; + + pTextures = nil; + texturesNum = 0; + texturesUsed = 0; +} + +IDirect3DTexture8 * +CTexturePool::FindTexture() +{ + if (texturesNum == 0) + return nil; + texturesUsed--; + return pTextures[--texturesNum]; +} + +bool +CTexturePool::AddTexture(IDirect3DTexture8 *texture) +{ + ++texturesUsed; + if (texturesNum >= texturesMax) + return false; + pTextures[texturesNum] = texture; + ++texturesNum; + return true; +} + +void +CTexturePool::Resize(int numTextures) +{ + if (numTextures == texturesMax) + return; + + IDirect3DTexture8 **newTextures = new IDirect3DTexture8 *[numTextures]; + + for (int i = 0; i < texturesNum && i < numTextures; i++) + newTextures[i] = pTextures[i]; + + if (numTextures < texturesNum) { + for (int i = numTextures; i < texturesNum; i++) + pTextures[i]->Release(); + } + delete[] pTextures; + pTextures = newTextures; + texturesMax = numTextures; +} + +void +CPaletteList::Alloc(int max) +{ + Data = new int[max]; + Max = max; + Num = 0; +} + +void +CPaletteList::Free() +{ + delete[] Data; + Data = nil; + Num = 0; +} + +int +CPaletteList::Find() +{ + if (Num == 0) + return -1; + return Data[--Num]; +} + +void +CPaletteList::Add(int item) +{ + if (Num < Max) + Data[Num++] = item; + else { + Resize(2 * Max); + Add(item); + } +} + +void +CPaletteList::Resize(int max) +{ + if (max == Max) + return; + + int *newData = new int[4 * max]; + for (int i = 0; i < Num && i < max; i++) + newData[i] = Data[i]; + delete[] Data; + Data = newData; + Max = max; +} + +HRESULT +CreateTexture(int width, int height, int levels, D3DFORMAT Format, IDirect3DTexture8 **texture) +{ + if (width == height) { + for (int i = 0; i < numTexturePools; i++) { + if (width != aTexturePools[i].GetSize() && levels == aTexturePools[i].levels && Format == aTexturePools[i].Format) + *texture = aTexturePools[i].FindTexture(); + } + } + if (*texture) + return D3D_OK; + else + return _RwD3DDevice->CreateTexture(width, height, levels, 0, Format, D3DPOOL_MANAGED, texture); +} + +void +ReleaseTexture(IDirect3DTexture8 *texture) +{ + int levels = 1; + if (texture->GetLevelCount() > 1) + levels = 0; + + D3DSURFACE_DESC SURFACE_DESC; + + texture->GetLevelDesc(0, &SURFACE_DESC); + + if (SURFACE_DESC.Width == SURFACE_DESC.Height) { + for (int i = 0; i < numTexturePools; i++) { + if (SURFACE_DESC.Width == aTexturePools[i].GetSize() && SURFACE_DESC.Format == aTexturePools[i].Format && levels == aTexturePools[i].levels) { + if (!aTexturePools[i].AddTexture(texture)) { + if (aTexturePools[i].texturesUsed > 3 * aTexturePools[i].texturesMax / 2) { + aTexturePools[i].Resize(2 * aTexturePools[i].texturesMax); + aTexturePools[i].texturesUsed--; + aTexturePools[i].AddTexture(texture); + } else { + texture->Release(); + } + } + return; + } + } + } + if (numTexturePools < 12 && bUsePaletteIndex && levels != 0 && SURFACE_DESC.Width == SURFACE_DESC.Height && + (SURFACE_DESC.Width == 64 || SURFACE_DESC.Width == 128 || SURFACE_DESC.Width == 256)) { + aTexturePools[numTexturePools].Create(SURFACE_DESC.Format, SURFACE_DESC.Width, 1, 16); + aTexturePools[numTexturePools].AddTexture(texture); + numTexturePools++; + } else + texture->Release(); +} + +int +FindAvailablePaletteIndex() +{ + int index = PaletteList.Find(); + if (index == -1) + index = MaxPaletteIndex++; + return index; +} + +void +AddAvailablePaletteIndex(int index) +{ + if (bUsePaletteIndex) + PaletteList.Add(index); +} + +void +_TexturePoolsInitialise() +{ + PaletteList.Alloc(100); + MaxPaletteIndex = 0; +} + +void +_TexturePoolsShutdown() +{ + for (int i = 0; i < numTexturePools; i++) + aTexturePools[i].Release(); + + numTexturePools = 0; + bUsePaletteIndex = false; + PaletteList.Free(); +} + +#endif // !LIBRW \ No newline at end of file diff --git a/src/miami/rw/TexturePools.h b/src/miami/rw/TexturePools.h new file mode 100644 index 00000000..75187432 --- /dev/null +++ b/src/miami/rw/TexturePools.h @@ -0,0 +1,42 @@ +#pragma once + +class CTexturePool +{ +public: + D3DFORMAT Format; + int size; + uint32 levels; + int32 texturesMax; + int32 texturesUsed; + int32 texturesNum; + IDirect3DTexture8 **pTextures; + +public: + CTexturePool() {} + void Create(D3DFORMAT _Format, int size, uint32 mipmapLevels, int32 numTextures); + void Release(); + IDirect3DTexture8 *FindTexture(); + bool AddTexture(IDirect3DTexture8 *texture); + void Resize(int numTextures); +#ifdef FIX_BUGS + int GetSize() { return size; } +#else + float GetSize() { return size; } +#endif +}; + +class CPaletteList +{ + int Max; + int Num; + int *Data; +public: + void Alloc(int max); + void Free(); + int Find(); + void Add(int item); + void Resize(int max); +}; + +void _TexturePoolsInitialise(); +void _TexturePoolsShutdown(); \ No newline at end of file diff --git a/src/miami/rw/TxdStore.cpp b/src/miami/rw/TxdStore.cpp new file mode 100644 index 00000000..28f1acc3 --- /dev/null +++ b/src/miami/rw/TxdStore.cpp @@ -0,0 +1,211 @@ +#include "common.h" + +#include "templates.h" +#include "General.h" +#include "Streaming.h" +#include "RwHelper.h" +#include "TxdStore.h" + +CPool *CTxdStore::ms_pTxdPool; +RwTexDictionary *CTxdStore::ms_pStoredTxd; + +void +CTxdStore::Initialise(void) +{ + if(ms_pTxdPool == nil) + ms_pTxdPool = new CPool(TXDSTORESIZE, "TexDictionary"); +} + +void +CTxdStore::Shutdown(void) +{ + if(ms_pTxdPool) + delete ms_pTxdPool; +} + +void +CTxdStore::GameShutdown(void) +{ + int i; + + for(i = 0; i < TXDSTORESIZE; i++){ + TxdDef *def = GetSlot(i); + if(def && GetNumRefs(i) == 0) + RemoveTxdSlot(i); + } +} + +int +CTxdStore::AddTxdSlot(const char *name) +{ + TxdDef *def = ms_pTxdPool->New(); + assert(def); + def->texDict = nil; + def->refCount = 0; + strcpy(def->name, name); + return ms_pTxdPool->GetJustIndex(def); +} + +void +CTxdStore::RemoveTxdSlot(int slot) +{ + TxdDef *def = GetSlot(slot); + if(def->texDict) + RwTexDictionaryDestroy(def->texDict); + ms_pTxdPool->Delete(def); +} + +int +CTxdStore::FindTxdSlot(const char *name) +{ + int size = ms_pTxdPool->GetSize(); + for(int i = 0; i < size; i++){ + TxdDef *def = GetSlot(i); + if(def && !CGeneral::faststricmp(def->name, name)) + return i; + } + return -1; +} + +char* +CTxdStore::GetTxdName(int slot) +{ + return GetSlot(slot)->name; +} + +void +CTxdStore::PushCurrentTxd(void) +{ + ms_pStoredTxd = RwTexDictionaryGetCurrent(); +} + +void +CTxdStore::PopCurrentTxd(void) +{ + RwTexDictionarySetCurrent(ms_pStoredTxd); + ms_pStoredTxd = nil; +} + +void +CTxdStore::SetCurrentTxd(int slot) +{ + RwTexDictionarySetCurrent(GetSlot(slot)->texDict); +} + +void +CTxdStore::Create(int slot) +{ + GetSlot(slot)->texDict = RwTexDictionaryCreate(); +} + +int +CTxdStore::GetNumRefs(int slot) +{ + return GetSlot(slot)->refCount; +} + +void +CTxdStore::AddRef(int slot) +{ + GetSlot(slot)->refCount++; +} + +void +CTxdStore::RemoveRef(int slot) +{ + #if !defined(DC_TEXCONV) + if(--GetSlot(slot)->refCount <= 0) + CStreaming::RemoveTxd(slot); + #else + assert(false); + #endif +} + +void +CTxdStore::RemoveRefWithoutDelete(int slot) +{ + GetSlot(slot)->refCount--; +} + +void StoreTxd(RwTexDictionary *texDict, RwStream *stream) { + auto fheader = stream->tell(); + // size will be written later + writeChunkHeader(stream, rwID_TEXDICTIONARY, 4); + auto fbegin = stream->tell(); + + RwTexDictionaryGtaStreamWrite(stream, texDict); + + // rewrite header for correct length + auto fend = stream->tell(); + stream->seek(fheader, 0); + writeChunkHeader(stream, rwID_TEXDICTIONARY, fend - fbegin); + stream->seek(fend, 0); +} +RwTexDictionary * +LoadTxd(RwStream *stream) +{ + RwUInt32 len; + if(RwStreamFindChunk(stream, rwID_TEXDICTIONARY, &len, nil)){ + return RwTexDictionaryGtaStreamRead(stream); + } + return nullptr; +} + +bool +CTxdStore::LoadTxd(int slot, RwStream *stream) +{ + TxdDef *def = GetSlot(slot); + + if(RwStreamFindChunk(stream, rwID_TEXDICTIONARY, nil, nil)){ + def->texDict = RwTexDictionaryGtaStreamRead(stream); + return def->texDict != nil; + } + printf("Failed to load TXD\n"); + return false; +} + +bool +CTxdStore::LoadTxd(int slot, const char *filename) +{ + RwStream *stream; + bool ret; + + ret = false; + _rwD3D8TexDictionaryEnableRasterFormatConversion(true); + do + stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, filename); + while(stream == nil); + ret = LoadTxd(slot, stream); + RwStreamClose(stream, nil); + return ret; +} + +bool +CTxdStore::StartLoadTxd(int slot, RwStream *stream) +{ + TxdDef *def = GetSlot(slot); + if(RwStreamFindChunk(stream, rwID_TEXDICTIONARY, nil, nil)){ + def->texDict = RwTexDictionaryGtaStreamRead1(stream); + return def->texDict != nil; + }else{ + printf("Failed to load TXD\n"); + return false; + } +} + +bool +CTxdStore::FinishLoadTxd(int slot, RwStream *stream) +{ + TxdDef *def = GetSlot(slot); + def->texDict = RwTexDictionaryGtaStreamRead2(stream, def->texDict); + return def->texDict != nil; +} + +void +CTxdStore::RemoveTxd(int slot) +{ + TxdDef *def = GetSlot(slot); + if(def->texDict) + RwTexDictionaryDestroy(def->texDict); + def->texDict = nil; +} diff --git a/src/miami/rw/TxdStore.h b/src/miami/rw/TxdStore.h new file mode 100644 index 00000000..937fd1b7 --- /dev/null +++ b/src/miami/rw/TxdStore.h @@ -0,0 +1,44 @@ +#pragma once + +#include "templates.h" + +struct TxdDef { + RwTexDictionary *texDict; + int refCount; + char name[20]; +}; + +class CTxdStore +{ + static CPool *ms_pTxdPool; + static RwTexDictionary *ms_pStoredTxd; +public: + static void Initialise(void); + static void Shutdown(void); + static void GameShutdown(void); + static int AddTxdSlot(const char *name); + static void RemoveTxdSlot(int slot); + static int FindTxdSlot(const char *name); + static char *GetTxdName(int slot); + static void PushCurrentTxd(void); + static void PopCurrentTxd(void); + static void SetCurrentTxd(int slot); + static void Create(int slot); + static int GetNumRefs(int slot); + static void AddRef(int slot); + static void RemoveRef(int slot); + static void RemoveRefWithoutDelete(int slot); + static bool LoadTxd(int slot, RwStream *stream); + static bool LoadTxd(int slot, const char *filename); + static bool StartLoadTxd(int slot, RwStream *stream); + static bool FinishLoadTxd(int slot, RwStream *stream); + static void RemoveTxd(int slot); + + static TxdDef *GetSlot(int slot) { + assert(slot >= 0); + assert(ms_pTxdPool); + assert(slot < ms_pTxdPool->GetSize()); + return ms_pTxdPool->GetSlot(slot); + } + static bool isTxdLoaded(int slot); +}; diff --git a/src/miami/rw/VisibilityPlugins.cpp b/src/miami/rw/VisibilityPlugins.cpp new file mode 100644 index 00000000..c428b909 --- /dev/null +++ b/src/miami/rw/VisibilityPlugins.cpp @@ -0,0 +1,1045 @@ +#include "common.h" + +#include "RwHelper.h" +#include "templates.h" +#include "main.h" +#include "Entity.h" +#include "ModelInfo.h" +#include "Lights.h" +#include "RwHelper.h" +#include "Renderer.h" +#include "Camera.h" +#include "VisibilityPlugins.h" +#include "World.h" +#include "custompipes.h" +#include "MemoryHeap.h" + +CLinkList CVisibilityPlugins::m_alphaList; +CLinkList CVisibilityPlugins::m_alphaBoatAtomicList; +CLinkList CVisibilityPlugins::m_alphaEntityList; +CLinkList CVisibilityPlugins::m_alphaUnderwaterEntityList; +#ifdef NEW_RENDERER +CLinkList CVisibilityPlugins::m_alphaBuildingList; +#endif + +int32 CVisibilityPlugins::ms_atomicPluginOffset = -1; +int32 CVisibilityPlugins::ms_framePluginOffset = -1; +int32 CVisibilityPlugins::ms_clumpPluginOffset = -1; + +RwCamera *CVisibilityPlugins::ms_pCamera; +RwV3d *CVisibilityPlugins::ms_pCameraPosn; +float CVisibilityPlugins::ms_cullCompsDist; +float CVisibilityPlugins::ms_vehicleLod0Dist; +float CVisibilityPlugins::ms_vehicleLod1Dist; +float CVisibilityPlugins::ms_vehicleFadeDist; +float CVisibilityPlugins::ms_bigVehicleLod0Dist; +float CVisibilityPlugins::ms_bigVehicleLod1Dist; +float CVisibilityPlugins::ms_pedLod1Dist; +float CVisibilityPlugins::ms_pedFadeDist; + +#define RENDERCALLBACK AtomicDefaultRenderCallBack + +void +CVisibilityPlugins::Initialise(void) +{ + m_alphaList.Init(NUMALPHALIST); + m_alphaList.head.item.sort = 0.0f; + m_alphaList.tail.item.sort = 100000000.0f; + + m_alphaBoatAtomicList.Init(NUMBOATALPHALIST); + m_alphaBoatAtomicList.head.item.sort = 0.0f; + m_alphaBoatAtomicList.tail.item.sort = 100000000.0f; + +#ifdef ASPECT_RATIO_SCALE + // default 150 is not enough for bigger FOVs + m_alphaEntityList.Init(NUMALPHAENTITYLIST * 3); +#else + m_alphaEntityList.Init(NUMALPHAENTITYLIST); +#endif // ASPECT_RATIO_SCALE + m_alphaEntityList.head.item.sort = 0.0f; + m_alphaEntityList.tail.item.sort = 100000000.0f; + + m_alphaUnderwaterEntityList.Init(NUMALPHAUNTERWATERENTITYLIST); + m_alphaUnderwaterEntityList.head.item.sort = 0.0f; + m_alphaUnderwaterEntityList.tail.item.sort = 100000000.0f; + +#ifdef NEW_RENDERER + m_alphaBuildingList.Init(NUMALPHAENTITYLIST); + m_alphaBuildingList.head.item.sort = 0.0f; + m_alphaBuildingList.tail.item.sort = 100000000.0f; +#endif +} + +void +CVisibilityPlugins::Shutdown(void) +{ + m_alphaList.Shutdown(); + m_alphaBoatAtomicList.Shutdown(); + m_alphaEntityList.Shutdown(); + m_alphaUnderwaterEntityList.Shutdown(); +#ifdef NEW_RENDERER + m_alphaBuildingList.Shutdown(); +#endif +} + +void +CVisibilityPlugins::InitAlphaEntityList(void) +{ + m_alphaEntityList.Clear(); + m_alphaBoatAtomicList.Clear(); + m_alphaUnderwaterEntityList.Clear(); +#ifdef NEW_RENDERER + m_alphaBuildingList.Clear(); +#endif +} + +bool +CVisibilityPlugins::InsertEntityIntoSortedList(CEntity *e, float dist) +{ +#ifdef FIX_BUGS + if (!e->m_rwObject) return true; +#endif + + AlphaObjectInfo item; + item.entity = e; + item.sort = dist; +#ifdef NEW_RENDERER + if(gbNewRenderer && e->IsBuilding()) + return !!m_alphaBuildingList.InsertSorted(item); +#endif + if(e->bUnderwater && m_alphaUnderwaterEntityList.InsertSorted(item)) + return true; + return !!m_alphaEntityList.InsertSorted(item); +} + +void +CVisibilityPlugins::InitAlphaAtomicList(void) +{ + m_alphaList.Clear(); +} + +bool +CVisibilityPlugins::InsertAtomicIntoSortedList(RpAtomic *a, float dist) +{ + AlphaObjectInfo item; + item.atomic = a; + item.sort = dist; + return !!m_alphaList.InsertSorted(item); +} + +bool +CVisibilityPlugins::InsertAtomicIntoBoatSortedList(RpAtomic *a, float dist) +{ + AlphaObjectInfo item; + item.atomic = a; + item.sort = dist; + return !!m_alphaBoatAtomicList.InsertSorted(item); +} + +// can't increase this yet unfortunately... +// probably have to fix fading for this so material alpha isn't overwritten +#define VEHICLE_LODDIST_MULTIPLIER (TheCamera.GenerationDistMultiplier) + +#if !defined(DC_TEXCONV) +void +CVisibilityPlugins::SetRenderWareCamera(RwCamera *camera) +{ + ms_pCamera = camera; + ms_pCameraPosn = RwMatrixGetPos(RwFrameGetMatrix(RwCameraGetFrame(camera))); + + if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || + TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED) + ms_cullCompsDist = 1000000.0f; + else + ms_cullCompsDist = sq(TheCamera.LODDistMultiplier * 20.0f); + + ms_vehicleLod0Dist = sq(70.0f * VEHICLE_LODDIST_MULTIPLIER); + ms_vehicleLod1Dist = sq(90.0f * VEHICLE_LODDIST_MULTIPLIER); + ms_vehicleFadeDist = sq(100.0f * VEHICLE_LODDIST_MULTIPLIER); + ms_bigVehicleLod0Dist = sq(60.0f * VEHICLE_LODDIST_MULTIPLIER); + ms_bigVehicleLod1Dist = sq(150.0f * VEHICLE_LODDIST_MULTIPLIER); + ms_pedLod1Dist = sq(60.0f * TheCamera.LODDistMultiplier); + ms_pedFadeDist = sq(70.0f * TheCamera.LODDistMultiplier); +} +#else +void +CVisibilityPlugins::SetRenderWareCamera(RwCamera *camera) { assert(false); } +#endif + +static float DistToCameraSq; +static float PitchToCamera; + +void +CVisibilityPlugins::SetupVehicleVariables(RpClump *vehicle) +{ + if (RwObjectGetType((RwObject*)vehicle) != rpCLUMP) + return; + DistToCameraSq = GetDistanceSquaredFromCamera(RpClumpGetFrame(vehicle)); + RwV3d distToCam; + RwV3dSub(&distToCam, ms_pCameraPosn, &RwFrameGetMatrix(RpClumpGetFrame(vehicle))->pos); + float dist2d = Sqrt(SQR(distToCam.x) + SQR(distToCam.y)); + PitchToCamera = Atan2(distToCam.z, dist2d); +} + +RpMaterial* +SetAlphaCB(RpMaterial *material, void *data) +{ + ((RwRGBA*)RpMaterialGetColor(material))->alpha = (uint8)(uintptr)data; + return material; +} + +RpMaterial* +SetTextureCB(RpMaterial *material, void *data) +{ + RpMaterialSetTexture(material, (RwTexture*)data); + return material; +} + +void +CVisibilityPlugins::RenderAtomicList(CLinkList &list) +{ + CLink *node; + for(node = list.tail.prev; node != &list.head; node = node->prev) + RENDERCALLBACK(node->item.atomic); +} + +void +CVisibilityPlugins::RenderAlphaAtomics(void) +{ + RenderAtomicList(m_alphaList); +} + +void +CVisibilityPlugins::RenderBoatAlphaAtomics(void) +{ + SetCullMode(rwCULLMODECULLNONE); + RenderAtomicList(m_alphaBoatAtomicList); + SetCullMode(rwCULLMODECULLBACK); +} + +#if !defined(DC_TEXCONV) +void +CVisibilityPlugins::RenderFadingEntities(CLinkList &list) +{ + CLink *node; + CSimpleModelInfo *mi; + for(node = list.tail.prev; node != &list.head; node = node->prev){ + CEntity *e = node->item.entity; + if(e->m_rwObject == nil) + continue; +#ifdef EXTENDED_PIPELINES + if(CustomPipes::bRenderingEnvMap && (e->IsPed() || e->IsVehicle())) + continue; +#endif + mi = (CSimpleModelInfo *)CModelInfo::GetModelInfo(e->GetModelIndex()); + if(mi->GetModelType() == MITYPE_SIMPLE && mi->m_noZwrite) + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, FALSE); + + if(mi->m_isAlphaTest && !mi->IsClump()) { + rw::SetRenderState(rw::ALPHATESTREF, 129); + } + + if(e->bDistanceFade){ + DeActivateDirectional(); + SetAmbientColours(); + e->bImBeingRendered = true; + PUSH_RENDERGROUP(mi->GetModelName()); + RenderFadingAtomic((RpAtomic*)e->m_rwObject, node->item.sort); + POP_RENDERGROUP(); + e->bImBeingRendered = false; + }else + CRenderer::RenderOneNonRoad(e); + + if(mi->m_isAlphaTest && !mi->IsClump()) { + rw::SetRenderState(rw::ALPHATESTREF, 3); + } + + if(mi->GetModelType() == MITYPE_SIMPLE && mi->m_noZwrite) + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); + } +} +#else +void +CVisibilityPlugins::RenderFadingEntities(CLinkList &list) +{ + assert(false); +} +#endif + +void +CVisibilityPlugins::RenderFadingEntities(void) +{ + RenderFadingEntities(m_alphaEntityList); + RenderBoatAlphaAtomics(); +} + +void +CVisibilityPlugins::RenderFadingUnderwaterEntities(void) +{ + RenderFadingEntities(m_alphaUnderwaterEntityList); +} + +#if !defined(DC_TEXCONV) +RpAtomic* +CVisibilityPlugins::RenderWheelAtomicCB(RpAtomic *atomic) +{ + RpAtomic *lodatm; + float len; + CSimpleModelInfo *mi; + + mi = GetAtomicModelInfo(atomic); + len = Sqrt(DistToCameraSq); + lodatm = mi->GetAtomicFromDistance(len * TheCamera.LODDistMultiplier / VEHICLE_LODDIST_MULTIPLIER); + if(lodatm){ + if(RpAtomicGetGeometry(lodatm) != RpAtomicGetGeometry(atomic)) + RpAtomicSetGeometry(atomic, RpAtomicGetGeometry(lodatm), rpATOMICSAMEBOUNDINGSPHERE); + RENDERCALLBACK(atomic); + } + return atomic; +} +#else +RpAtomic* +CVisibilityPlugins::RenderWheelAtomicCB(RpAtomic *atomic) { assert(false); } +#endif + +RpAtomic* +CVisibilityPlugins::RenderObjNormalAtomic(RpAtomic *atomic) +{ + RwMatrix *m; + RwV3d view; + float len; + + m = RwFrameGetLTM(RpAtomicGetFrame(atomic)); + RwV3dSub(&view, RwMatrixGetPos(m), ms_pCameraPosn); + len = RwV3dLength(&view); + if(RwV3dDotProduct(&view, RwMatrixGetUp(m)) < -0.3f*len && len > 8.0f) + return atomic; + RENDERCALLBACK(atomic); + return atomic; +} + +RpAtomic* +CVisibilityPlugins::RenderAlphaAtomic(RpAtomic *atomic, int alpha) +{ + RpGeometry *geo; + uint32 flags; + + geo = RpAtomicGetGeometry(atomic); + flags = RpGeometryGetFlags(geo); + RpGeometrySetFlags(geo, flags | rpGEOMETRYMODULATEMATERIALCOLOR); + RpGeometryForAllMaterials(geo, SetAlphaCB, (void*)alpha); + RENDERCALLBACK(atomic); + RpGeometryForAllMaterials(geo, SetAlphaCB, (void*)255); + RpGeometrySetFlags(geo, flags); + return atomic; +} + +#if !defined(DC_TEXCONV) +RpAtomic* +CVisibilityPlugins::RenderWeaponCB(RpAtomic *atomic) +{ + RwMatrix *m; + RwV3d view; + float maxdist, distsq; + CSimpleModelInfo *mi; + + mi = GetAtomicModelInfo(atomic); + m = RwFrameGetLTM(RpAtomicGetFrame(atomic)); + RwV3dSub(&view, RwMatrixGetPos(m), ms_pCameraPosn); + maxdist = mi->GetLodDistance(0); + distsq = RwV3dDotProduct(&view, &view); + if(distsq < maxdist*maxdist) + RENDERCALLBACK(atomic); + return atomic; +} +#else +RpAtomic* +CVisibilityPlugins::RenderWeaponCB(RpAtomic *atomic) { assert(false); } +#endif + +#if !defined(DC_TEXCONV) +RpAtomic* +CVisibilityPlugins::RenderFadingAtomic(RpAtomic *atomic, float camdist) +{ + RpAtomic *lodatm; + float fadefactor; + uint32 alpha; + CSimpleModelInfo *mi; + + mi = GetAtomicModelInfo(atomic); + lodatm = mi->GetAtomicFromDistance(camdist - FADE_DISTANCE); + if(mi->m_additive) + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); + + fadefactor = (mi->GetLargestLodDistance() - (camdist - FADE_DISTANCE))/FADE_DISTANCE; + if(fadefactor > 1.0f) + fadefactor = 1.0f; + alpha = mi->m_alpha * fadefactor; + if(alpha == 255) + RENDERCALLBACK(atomic); + else{ + RpGeometry *geo = RpAtomicGetGeometry(lodatm); + uint32 flags = RpGeometryGetFlags(geo); + RpGeometrySetFlags(geo, flags | rpGEOMETRYMODULATEMATERIALCOLOR); + RpGeometryForAllMaterials(geo, SetAlphaCB, (void*)alpha); + if(geo != RpAtomicGetGeometry(atomic)) + RpAtomicSetGeometry(atomic, geo, rpATOMICSAMEBOUNDINGSPHERE); // originally 5 (mistake?) + RENDERCALLBACK(atomic); + RpGeometryForAllMaterials(geo, SetAlphaCB, (void*)255); + RpGeometrySetFlags(geo, flags); + } + + if(mi->m_additive) + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + + return atomic; +} +#else +RpAtomic* +CVisibilityPlugins::RenderFadingAtomic(RpAtomic *atomic, float camdist) +{ + assert(false); +} +#endif + + + +RpAtomic* +CVisibilityPlugins::RenderVehicleHiDetailCB(RpAtomic *atomic) +{ + RwFrame *clumpframe; + float dot; + uint32 flags; + + clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic)); + if(DistToCameraSq < ms_vehicleLod0Dist){ + flags = GetAtomicId(atomic); + if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f){ + dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)), + RwFrameGetLTM(clumpframe), flags); + if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*DistToCameraSq < dot*dot)) + return atomic; + } + RENDERCALLBACK(atomic); + } + return atomic; +} + +RpAtomic* +CVisibilityPlugins::RenderVehicleHiDetailAlphaCB(RpAtomic *atomic) +{ + RwFrame *clumpframe; + float dot; + uint32 flags; + + clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic)); + if(DistToCameraSq < ms_vehicleLod0Dist){ + flags = GetAtomicId(atomic); + dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)), + RwFrameGetLTM(clumpframe), flags); + if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f) + if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*DistToCameraSq < dot*dot)) + return atomic; + + if(flags & ATOMIC_FLAG_DRAWLAST){ + // sort before clump + if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq - 0.0001f)) + RENDERCALLBACK(atomic); + }else{ + if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq + dot)) + RENDERCALLBACK(atomic); + } + } + return atomic; +} + +RpAtomic* +CVisibilityPlugins::RenderVehicleHiDetailCB_BigVehicle(RpAtomic *atomic) +{ + RwFrame *clumpframe; + float dot; + uint32 flags; + + clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic)); + if(DistToCameraSq < ms_bigVehicleLod0Dist){ + flags = GetAtomicId(atomic); + if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f){ + dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)), + RwFrameGetLTM(clumpframe), flags); + if(dot > 0.0f) + return atomic; + } + RENDERCALLBACK(atomic); + } + return atomic; +} + +RpAtomic* +CVisibilityPlugins::RenderVehicleHiDetailAlphaCB_BigVehicle(RpAtomic *atomic) +{ + RwFrame *clumpframe; + float dot; + uint32 flags; + + clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic)); + if(DistToCameraSq < ms_bigVehicleLod0Dist){ + flags = GetAtomicId(atomic); + dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)), + RwFrameGetLTM(clumpframe), flags); + if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f) + if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*DistToCameraSq < dot*dot)) + return atomic; + + if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq + dot)) + RENDERCALLBACK(atomic); + } + return atomic; +} + +RpAtomic* +CVisibilityPlugins::RenderVehicleHiDetailCB_Boat(RpAtomic *atomic) +{ + if(DistToCameraSq < ms_vehicleLod0Dist) + RENDERCALLBACK(atomic); + return atomic; +} + +RpAtomic* +CVisibilityPlugins::RenderVehicleHiDetailAlphaCB_Boat(RpAtomic *atomic) +{ + if(DistToCameraSq < ms_vehicleLod0Dist){ + if(GetAtomicId(atomic) & ATOMIC_FLAG_DRAWLAST){ + if(!InsertAtomicIntoBoatSortedList(atomic, DistToCameraSq)) + RENDERCALLBACK(atomic); + }else + RENDERCALLBACK(atomic); + } + return atomic; +} + +RpAtomic* +CVisibilityPlugins::RenderVehicleLoDetailCB_Boat(RpAtomic *atomic) +{ + RpClump *clump; + int32 alpha; + + clump = RpAtomicGetClump(atomic); + if(DistToCameraSq >= ms_vehicleLod0Dist){ + alpha = GetClumpAlpha(clump); + if(alpha == 255) + RENDERCALLBACK(atomic); + else + RenderAlphaAtomic(atomic, alpha); + } + return atomic; +} + +RpAtomic* +CVisibilityPlugins::RenderVehicleLowDetailCB_BigVehicle(RpAtomic *atomic) +{ + RwFrame *clumpframe; + float dot; + uint32 flags; + + clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic)); + if(DistToCameraSq >= ms_bigVehicleLod0Dist && + DistToCameraSq < ms_bigVehicleLod1Dist){ + flags = GetAtomicId(atomic); + if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f){ + dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)), + RwFrameGetLTM(clumpframe), flags); + if(dot > 0.0f) + return atomic; + } + RENDERCALLBACK(atomic); + } + return atomic; +} + +RpAtomic* +CVisibilityPlugins::RenderVehicleLowDetailAlphaCB_BigVehicle(RpAtomic *atomic) +{ + RwFrame *clumpframe; + float dot; + uint32 flags; + + clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic)); + if(DistToCameraSq >= ms_bigVehicleLod0Dist && + DistToCameraSq < ms_bigVehicleLod1Dist){ + flags = GetAtomicId(atomic); + dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)), + RwFrameGetLTM(clumpframe), flags); + if(dot > 0.0f) + if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f) + return atomic; + + if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq + dot)) + RENDERCALLBACK(atomic); + } + return atomic; +} + +RpAtomic* +CVisibilityPlugins::RenderVehicleReallyLowDetailCB(RpAtomic *atomic) +{ + RpClump *clump; + int32 alpha; + + clump = RpAtomicGetClump(atomic); + if(DistToCameraSq >= ms_vehicleLod0Dist){ + alpha = GetClumpAlpha(clump); + if(alpha == 255) + RENDERCALLBACK(atomic); + else + RenderAlphaAtomic(atomic, alpha); + } + return atomic; + +} + +RpAtomic* +CVisibilityPlugins::RenderVehicleReallyLowDetailCB_BigVehicle(RpAtomic *atomic) +{ + if(DistToCameraSq >= ms_bigVehicleLod1Dist) + RENDERCALLBACK(atomic); + return atomic; +} + +RpAtomic* +CVisibilityPlugins::RenderTrainHiDetailCB(RpAtomic *atomic) +{ + RwFrame *clumpframe; + float dot; + uint32 flags; + + clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic)); + if(DistToCameraSq < ms_bigVehicleLod1Dist){ + flags = GetAtomicId(atomic); + if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f){ + dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)), + RwFrameGetLTM(clumpframe), flags); + if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*DistToCameraSq < dot*dot)) + return atomic; + } + RENDERCALLBACK(atomic); + } + return atomic; +} + +RpAtomic* +CVisibilityPlugins::RenderTrainHiDetailAlphaCB(RpAtomic *atomic) +{ + RwFrame *clumpframe; + float dot; + uint32 flags; + + clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic)); + if(DistToCameraSq < ms_bigVehicleLod1Dist){ + flags = GetAtomicId(atomic); + dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)), + RwFrameGetLTM(clumpframe), flags); + if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f) + if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*DistToCameraSq < dot*dot)) + return atomic; + + if(flags & ATOMIC_FLAG_DRAWLAST){ + if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq)) + RENDERCALLBACK(atomic); + }else{ + if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq + dot)) + RENDERCALLBACK(atomic); + } + } + return atomic; +} + +RpAtomic* +CVisibilityPlugins::RenderVehicleRotorAlphaCB(RpAtomic *atomic) +{ + RwFrame *clumpframe; + float dot; + RwV3d cam2atm; + + clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic)); + if(DistToCameraSq < ms_bigVehicleLod1Dist){ + RwV3dSub(&cam2atm, &RwFrameGetLTM(RpAtomicGetFrame(atomic))->pos, ms_pCameraPosn); + dot = RwV3dDotProduct(&cam2atm, &RwFrameGetLTM(clumpframe)->at); + if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq + dot*20.0f)) + RENDERCALLBACK(atomic); + } + return atomic; +} + +RpAtomic* +CVisibilityPlugins::RenderVehicleTailRotorAlphaCB(RpAtomic *atomic) +{ + RwMatrix *clumpMat, *atmMat; + float dot; + RwV3d cam2atm; + + if(DistToCameraSq < ms_bigVehicleLod0Dist){ + atmMat = RwFrameGetLTM(RpAtomicGetFrame(atomic)); + clumpMat = RwFrameGetLTM(RpClumpGetFrame(RpAtomicGetClump(atomic))); + RwV3dSub(&cam2atm, &atmMat->pos, ms_pCameraPosn); + dot = RwV3dDotProduct(&cam2atm, &clumpMat->up) + RwV3dDotProduct(&cam2atm, &clumpMat->right); + if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq - dot)) + RENDERCALLBACK(atomic); + } + return atomic; +} + +#if !defined(DC_TEXCONV) +RpAtomic* +CVisibilityPlugins::RenderPlayerCB(RpAtomic *atomic) +{ + if(CWorld::Players[0].m_pSkinTexture) + RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), SetTextureCB, CWorld::Players[0].m_pSkinTexture); + RENDERCALLBACK(atomic); + return atomic; +} +#else +RpAtomic* +CVisibilityPlugins::RenderPlayerCB(RpAtomic *atomic) +{ + assert(false); +} +#endif + +RpAtomic* +CVisibilityPlugins::RenderPedCB(RpAtomic *atomic) +{ + RpClump *clump; + float dist; + int32 alpha; + + clump = RpAtomicGetClump(atomic); + dist = GetDistanceSquaredFromCamera(RpClumpGetFrame(clump)); + if(dist < ms_pedLod1Dist){ + alpha = GetClumpAlpha(clump); + if(alpha == 255) + RENDERCALLBACK(atomic); + else + RenderAlphaAtomic(atomic, alpha); + } + return atomic; +} + +float +CVisibilityPlugins::GetDistanceSquaredFromCamera(RwV3d *pos) +{ + RwV3d dist; + RwV3dSub(&dist, pos, ms_pCameraPosn); + return RwV3dDotProduct(&dist, &dist); +} + +float +CVisibilityPlugins::GetDistanceSquaredFromCamera(RwFrame *frame) +{ + RwMatrix *m; + RwV3d dist; + m = RwFrameGetLTM(frame); + RwV3dSub(&dist, RwMatrixGetPos(m), ms_pCameraPosn); + return RwV3dDotProduct(&dist, &dist); +} + +float +CVisibilityPlugins::GetDotProductWithCameraVector(RwMatrix *atomicMat, RwMatrix *clumpMat, uint32 flags) +{ + RwV3d dist; + float dot, dotdoor; + + // Vehicle forward is the y axis (RwMatrix.up) + // Vehicle right is the x axis (RwMatrix.right) + + RwV3dSub(&dist, RwMatrixGetPos(atomicMat), ms_pCameraPosn); + // forward/backward facing + if(flags & (ATOMIC_FLAG_FRONT | ATOMIC_FLAG_REAR)) + dot = RwV3dDotProduct(&dist, RwMatrixGetUp(clumpMat)); + // left/right facing + else if(flags & (ATOMIC_FLAG_LEFT | ATOMIC_FLAG_RIGHT)) + dot = RwV3dDotProduct(&dist, RwMatrixGetRight(clumpMat)); + else + dot = 0.0f; + if(flags & (ATOMIC_FLAG_LEFT | ATOMIC_FLAG_REAR)) + dot = -dot; + + if(flags & (ATOMIC_FLAG_REARDOOR | ATOMIC_FLAG_FRONTDOOR)){ + if(flags & ATOMIC_FLAG_REARDOOR) + dotdoor = -RwV3dDotProduct(&dist, RwMatrixGetUp(clumpMat)); + else if(flags & ATOMIC_FLAG_FRONTDOOR) + dotdoor = RwV3dDotProduct(&dist, RwMatrixGetUp(clumpMat)); + else + dotdoor = 0.0f; + + if(dot < 0.0f && dotdoor < 0.0f) + dot += dotdoor; + if(dot > 0.0f && dotdoor > 0.0f) + dot += dotdoor; + } + + return dot; +} + +/* These are all unused */ + +bool +CVisibilityPlugins::DefaultVisibilityCB(RpClump *clump) +{ + return true; +} + +bool +CVisibilityPlugins::FrustumSphereCB(RpClump *clump) +{ + RwSphere sphere; + RwFrame *frame = RpClumpGetFrame(clump); + + CClumpModelInfo *modelInfo = (CClumpModelInfo*)GetFrameHierarchyId(frame); + sphere.radius = modelInfo->GetColModel()->boundingSphere.radius; + sphere.center.x = modelInfo->GetColModel()->boundingSphere.center.x; + sphere.center.y = modelInfo->GetColModel()->boundingSphere.center.y; + sphere.center.z = modelInfo->GetColModel()->boundingSphere.center.z; + RwV3dTransformPoints(&sphere.center, &sphere.center, 1, RwFrameGetLTM(frame)); + return RwCameraFrustumTestSphere(ms_pCamera, &sphere) != rwSPHEREOUTSIDE; +} + +bool +CVisibilityPlugins::MloVisibilityCB(RpClump *clump) +{ + RwFrame *frame = RpClumpGetFrame(clump); + CMloModelInfo *modelInfo = (CMloModelInfo*)GetFrameHierarchyId(frame); + if (SQR(modelInfo->drawDist) < GetDistanceSquaredFromCamera(frame)) + return false; + return CVisibilityPlugins::FrustumSphereCB(clump); +} + +bool +CVisibilityPlugins::VehicleVisibilityCB(RpClump *clump) +{ + RwFrame *frame = RpClumpGetFrame(clump); + if (ms_vehicleLod1Dist < GetDistanceSquaredFromCamera(frame)) + return false; + return FrustumSphereCB(clump); +} + +bool +CVisibilityPlugins::VehicleVisibilityCB_BigVehicle(RpClump *clump) +{ + return FrustumSphereCB(clump); +} + + + + +// +// RW Plugins +// + +enum +{ + ID_VISIBILITYATOMIC = MAKECHUNKID(rwVENDORID_ROCKSTAR, 0x00), + ID_VISIBILITYCLUMP = MAKECHUNKID(rwVENDORID_ROCKSTAR, 0x01), + ID_VISIBILITYFRAME = MAKECHUNKID(rwVENDORID_ROCKSTAR, 0x02), +}; + +bool +CVisibilityPlugins::PluginAttach(void) +{ + ms_atomicPluginOffset = RpAtomicRegisterPlugin(sizeof(AtomicExt), + ID_VISIBILITYATOMIC, + AtomicConstructor, AtomicDestructor, AtomicCopyConstructor); + + ms_framePluginOffset = RwFrameRegisterPlugin(sizeof(FrameExt), + ID_VISIBILITYFRAME, + FrameConstructor, FrameDestructor, FrameCopyConstructor); + + ms_clumpPluginOffset = RpClumpRegisterPlugin(sizeof(ClumpExt), + ID_VISIBILITYCLUMP, + ClumpConstructor, ClumpDestructor, ClumpCopyConstructor); + return ms_atomicPluginOffset != -1 && ms_clumpPluginOffset != -1; +} + +#define ATOMICEXT(o) (RWPLUGINOFFSET(AtomicExt, o, ms_atomicPluginOffset)) +#define FRAMEEXT(o) (RWPLUGINOFFSET(FrameExt, o, ms_framePluginOffset)) +#define CLUMPEXT(o) (RWPLUGINOFFSET(ClumpExt, o, ms_clumpPluginOffset)) + +// +// Atomic +// + +void* +CVisibilityPlugins::AtomicConstructor(void *object, RwInt32, RwInt32) +{ + ATOMICEXT(object)->modelInfo = nil; + return object; +} + +void* +CVisibilityPlugins::AtomicDestructor(void *object, RwInt32, RwInt32) +{ + return object; +} + +void* +CVisibilityPlugins::AtomicCopyConstructor(void *dst, const void *src, RwInt32, RwInt32) +{ + *ATOMICEXT(dst) = *ATOMICEXT(src); + return dst; +} + +void +CVisibilityPlugins::SetAtomicModelInfo(RpAtomic *atomic, + CSimpleModelInfo *modelInfo) +{ + AtomicExt *ext = ATOMICEXT(atomic); + ext->modelInfo = modelInfo; +} + +CSimpleModelInfo* +CVisibilityPlugins::GetAtomicModelInfo(RpAtomic *atomic) +{ + return ATOMICEXT(atomic)->modelInfo; +} + +void +CVisibilityPlugins::SetAtomicFlag(RpAtomic *atomic, int f) +{ + ATOMICEXT(atomic)->flags |= f; +} + +void +CVisibilityPlugins::ClearAtomicFlag(RpAtomic *atomic, int f) +{ + ATOMICEXT(atomic)->flags &= ~f; +} + +void +CVisibilityPlugins::SetAtomicId(RpAtomic *atomic, int id) +{ + ATOMICEXT(atomic)->flags = id; +} + +int +CVisibilityPlugins::GetAtomicId(RpAtomic *atomic) +{ + return ATOMICEXT(atomic)->flags; +} + +void +CVisibilityPlugins::SetAtomicRenderCallback(RpAtomic *atomic, RpAtomicCallBackRender cb) +{ + if(cb == nil) + cb = RENDERCALLBACK; // not necessary + RpAtomicSetRenderCallBack(atomic, cb); +} + +// +// Frame +// + +void* +CVisibilityPlugins::FrameConstructor(void *object, RwInt32, RwInt32) +{ + FRAMEEXT(object)->id = 0; + return object; +} + +void* +CVisibilityPlugins::FrameDestructor(void *object, RwInt32, RwInt32) +{ + return object; +} + +void* +CVisibilityPlugins::FrameCopyConstructor(void *dst, const void *src, RwInt32, RwInt32) +{ + *FRAMEEXT(dst) = *FRAMEEXT(src); + return dst; +} + +void +CVisibilityPlugins::SetFrameHierarchyId(RwFrame *frame, intptr id) +{ + FRAMEEXT(frame)->id = id; +} + +intptr +CVisibilityPlugins::GetFrameHierarchyId(RwFrame *frame) +{ + return FRAMEEXT(frame)->id; +} + + +// +// Clump +// + +void* +CVisibilityPlugins::ClumpConstructor(void *object, RwInt32, RwInt32) +{ + ClumpExt *ext = CLUMPEXT(object); + ext->visibilityCB = DefaultVisibilityCB; + ext->alpha = 0xFF; + return object; +} + +void* +CVisibilityPlugins::ClumpDestructor(void *object, RwInt32, RwInt32) +{ + return object; +} + +void* +CVisibilityPlugins::ClumpCopyConstructor(void *dst, const void *src, RwInt32, RwInt32) +{ + CLUMPEXT(dst)->visibilityCB = CLUMPEXT(src)->visibilityCB; + return dst; +} + +void +CVisibilityPlugins::SetClumpModelInfo(RpClump *clump, CClumpModelInfo *modelInfo) +{ + CVehicleModelInfo *vmi; + SetFrameHierarchyId(RpClumpGetFrame(clump), (intptr)modelInfo); + + // Unused + switch (modelInfo->GetModelType()) { + case MITYPE_MLO: + CLUMPEXT(clump)->visibilityCB = MloVisibilityCB; + break; + case MITYPE_VEHICLE: + vmi = (CVehicleModelInfo*)modelInfo; + if(vmi->m_vehicleType == VEHICLE_TYPE_TRAIN || + vmi->m_vehicleType == VEHICLE_TYPE_HELI || + vmi->m_vehicleType == VEHICLE_TYPE_PLANE) + CLUMPEXT(clump)->visibilityCB = VehicleVisibilityCB_BigVehicle; + else + CLUMPEXT(clump)->visibilityCB = VehicleVisibilityCB; + break; + default: break; + } +} + +CClumpModelInfo* +CVisibilityPlugins::GetClumpModelInfo(RpClump *clump) +{ + return (CClumpModelInfo*)GetFrameHierarchyId(RpClumpGetFrame(clump)); +} + +void +CVisibilityPlugins::SetClumpAlpha(RpClump *clump, int alpha) +{ + CLUMPEXT(clump)->alpha = alpha; +} + +int +CVisibilityPlugins::GetClumpAlpha(RpClump *clump) +{ + return CLUMPEXT(clump)->alpha; +} + +bool +CVisibilityPlugins::IsClumpVisible(RpClump *clump) +{ + return CLUMPEXT(clump)->visibilityCB(clump); +} diff --git a/src/miami/rw/VisibilityPlugins.h b/src/miami/rw/VisibilityPlugins.h new file mode 100644 index 00000000..4a53b7f3 --- /dev/null +++ b/src/miami/rw/VisibilityPlugins.h @@ -0,0 +1,152 @@ +#pragma once + +#include "templates.h" + +class CEntity; +class CSimpleModelInfo; +class CClumpModelInfo; + +typedef bool (*ClumpVisibilityCB)(RpClump*); + +class CVisibilityPlugins +{ +public: + struct AlphaObjectInfo + { + union { + CEntity *entity; + RpAtomic *atomic; + }; + float sort; + }; + + static CLinkList m_alphaList; + static CLinkList m_alphaBoatAtomicList; + static CLinkList m_alphaEntityList; + static CLinkList m_alphaUnderwaterEntityList; +#ifdef NEW_RENDERER + static CLinkList m_alphaBuildingList; +#endif + static RwCamera *ms_pCamera; + static RwV3d *ms_pCameraPosn; + static float ms_cullCompsDist; + static float ms_vehicleLod0Dist; + static float ms_vehicleLod1Dist; + static float ms_vehicleFadeDist; + static float ms_bigVehicleLod0Dist; + static float ms_bigVehicleLod1Dist; + static float ms_pedLod1Dist; + static float ms_pedFadeDist; + + static void Initialise(void); + static void Shutdown(void); + static void InitAlphaEntityList(void); + static bool InsertEntityIntoSortedList(CEntity *e, float dist); + static void InitAlphaAtomicList(void); + static bool InsertAtomicIntoSortedList(RpAtomic *a, float dist); + static bool InsertAtomicIntoBoatSortedList(RpAtomic *a, float dist); + + static void SetRenderWareCamera(RwCamera *camera); + static void SetupVehicleVariables(RpClump *vehicle); + + static RpAtomic *RenderWheelAtomicCB(RpAtomic *atomic); + static RpAtomic *RenderObjNormalAtomic(RpAtomic *atomic); + static RpAtomic *RenderAlphaAtomic(RpAtomic *atomic, int alpha); + static RpAtomic *RenderWeaponCB(RpAtomic *atomic); + static RpAtomic *RenderFadingAtomic(RpAtomic *atm, float dist); + + static RpAtomic *RenderVehicleHiDetailCB(RpAtomic *atomic); + static RpAtomic *RenderVehicleHiDetailAlphaCB(RpAtomic *atomic); + static RpAtomic *RenderVehicleHiDetailCB_BigVehicle(RpAtomic *atomic); + static RpAtomic *RenderVehicleHiDetailAlphaCB_BigVehicle(RpAtomic *atomic); + static RpAtomic *RenderVehicleHiDetailCB_Boat(RpAtomic *atomic); + static RpAtomic *RenderVehicleHiDetailAlphaCB_Boat(RpAtomic *atomic); + static RpAtomic *RenderVehicleLoDetailCB_Boat(RpAtomic *atomic); + static RpAtomic *RenderVehicleLowDetailCB_BigVehicle(RpAtomic *atomic); + static RpAtomic *RenderVehicleLowDetailAlphaCB_BigVehicle(RpAtomic *atomic); + static RpAtomic *RenderVehicleReallyLowDetailCB(RpAtomic *atomic); + static RpAtomic *RenderVehicleReallyLowDetailCB_BigVehicle(RpAtomic *atomic); + static RpAtomic *RenderTrainHiDetailCB(RpAtomic *atomic); + static RpAtomic *RenderTrainHiDetailAlphaCB(RpAtomic *atomic); + static RpAtomic *RenderVehicleRotorAlphaCB(RpAtomic *atomic); + static RpAtomic *RenderVehicleTailRotorAlphaCB(RpAtomic *atomic); + + static RpAtomic *RenderPlayerCB(RpAtomic *atomic); + static RpAtomic *RenderPedCB(RpAtomic *atomic); // for skinned models with only one clump + + static void RenderAtomicList(CLinkList &list); + static void RenderAlphaAtomics(void); + static void RenderBoatAlphaAtomics(void); + static void RenderFadingEntities(CLinkList &list); + static void RenderFadingEntities(void); + static void RenderFadingUnderwaterEntities(void); + + // All actually unused + static bool DefaultVisibilityCB(RpClump *clump); + static bool FrustumSphereCB(RpClump *clump); + static bool MloVisibilityCB(RpClump *clump); + static bool VehicleVisibilityCB(RpClump *clump); + static bool VehicleVisibilityCB_BigVehicle(RpClump *clump); + + static float GetDistanceSquaredFromCamera(RwV3d *pos); + static float GetDistanceSquaredFromCamera(RwFrame *frame); + static float GetDotProductWithCameraVector(RwMatrix *atomicMat, RwMatrix *clumpMat, uint32 flags); + + // + // RW Plugins + // + + union AtomicExt + { + CSimpleModelInfo *modelInfo; // used by SimpleModelInfo + int flags; // used by ClumpModelInfo + }; + static void SetAtomicModelInfo(RpAtomic*, CSimpleModelInfo*); + static CSimpleModelInfo *GetAtomicModelInfo(RpAtomic *atomic); + static void SetAtomicFlag(RpAtomic*, int); + static void ClearAtomicFlag(RpAtomic*, int); + static void SetAtomicId(RpAtomic *atomic, int); + static int GetAtomicId(RpAtomic *atomic); + static void SetAtomicRenderCallback(RpAtomic*, RpAtomicCallBackRender); + + static void *AtomicConstructor(void *object, RwInt32 offset, RwInt32 len); + static void *AtomicDestructor(void *object, RwInt32 offset, RwInt32 len); + static void *AtomicCopyConstructor(void *dst, const void *src, + RwInt32 offset, RwInt32 len); + static int32 ms_atomicPluginOffset; + + struct FrameExt + { + // BUG: this is abused to hold a pointer by SetClumpModelInfo + intptr id; + }; + static void SetFrameHierarchyId(RwFrame *frame, intptr id); + static intptr GetFrameHierarchyId(RwFrame *frame); + + static void *FrameConstructor(void *object, RwInt32 offset, RwInt32 len); + static void *FrameDestructor(void *object, RwInt32 offset, RwInt32 len); + static void *FrameCopyConstructor(void *dst, const void *src, + RwInt32 offset, RwInt32 len); + static int32 ms_framePluginOffset; + + struct ClumpExt + { + ClumpVisibilityCB visibilityCB; + int alpha; + }; + static void SetClumpModelInfo(RpClump*, CClumpModelInfo*); + static CClumpModelInfo *GetClumpModelInfo(RpClump*); + static void SetClumpAlpha(RpClump*, int); + static int GetClumpAlpha(RpClump*); + static bool IsClumpVisible(RpClump*); + + static void *ClumpConstructor(void *object, RwInt32 offset, RwInt32 len); + static void *ClumpDestructor(void *object, RwInt32 offset, RwInt32 len); + static void *ClumpCopyConstructor(void *dst, const void *src, + RwInt32 offset, RwInt32 len); + static int32 ms_clumpPluginOffset; + + static bool PluginAttach(void); +}; + +RpMaterial *SetAlphaCB(RpMaterial *material, void *data); diff --git a/src/miami/save/Date.cpp b/src/miami/save/Date.cpp new file mode 100644 index 00000000..ca75bb5e --- /dev/null +++ b/src/miami/save/Date.cpp @@ -0,0 +1,91 @@ +#include "common.h" +#include "Date.h" + +CDate::CDate() +{ + m_nYear = 0; + m_nSecond = 0; + m_nMinute = 0; + m_nHour = 0; + m_nDay = 0; + m_nMonth = 0; +} + +bool +CDate::operator>(const CDate &right) +{ + if (m_nYear > right.m_nYear) + return true; + if (m_nYear != right.m_nYear) + return false; + + if (m_nMonth > right.m_nMonth) + return true; + if (m_nMonth != right.m_nMonth) + return false; + + if (m_nDay > right.m_nDay) + return true; + if (m_nDay != right.m_nDay) + return false; + + if (m_nHour > right.m_nHour) + return true; + if (m_nHour != right.m_nHour) + return false; + + if (m_nMinute > right.m_nMinute) + return true; + if (m_nMinute != right.m_nMinute) + return false; + return m_nSecond > right.m_nSecond; +} + +bool +CDate::operator<(const CDate &right) +{ + if (m_nYear < right.m_nYear) + return true; + if (m_nYear != right.m_nYear) + return false; + + if (m_nMonth < right.m_nMonth) + return true; + if (m_nMonth != right.m_nMonth) + return false; + + if (m_nDay < right.m_nDay) + return true; + if (m_nDay != right.m_nDay) + return false; + + if (m_nHour < right.m_nHour) + return true; + if (m_nHour != right.m_nHour) + return false; + + if (m_nMinute < right.m_nMinute) + return true; + if (m_nMinute != right.m_nMinute) + return false; + return m_nSecond < right.m_nSecond; +} + +bool +CDate::operator==(const CDate &right) +{ + if (m_nYear != right.m_nYear || m_nMonth != right.m_nMonth || m_nDay != right.m_nDay || m_nHour != right.m_nHour || m_nMinute != right.m_nMinute) + return false; + return m_nSecond == right.m_nSecond; +} + +void +CDate::PopulateDateFields(int8 &second, int8 &minute, int8 &hour, int8 &day, int8 &month, int16 year) +{ + m_nSecond = second; + m_nMinute = minute; + m_nHour = hour; + m_nDay = day; + m_nMonth = month; + m_nYear = year; +} \ No newline at end of file diff --git a/src/miami/save/Date.h b/src/miami/save/Date.h new file mode 100644 index 00000000..15646c23 --- /dev/null +++ b/src/miami/save/Date.h @@ -0,0 +1,18 @@ +#pragma once + +class CDate +{ +public: + int m_nSecond; + int m_nMinute; + int m_nHour; + int m_nDay; + int m_nMonth; + int m_nYear; + + CDate(); + bool operator>(const CDate &right); + bool operator<(const CDate &right); + bool operator==(const CDate &right); + void PopulateDateFields(int8 &second, int8 &minute, int8 &hour, int8 &day, int8 &month, int16 year); +}; \ No newline at end of file diff --git a/src/miami/save/GenericGameStorage.cpp b/src/miami/save/GenericGameStorage.cpp new file mode 100644 index 00000000..0baf799f --- /dev/null +++ b/src/miami/save/GenericGameStorage.cpp @@ -0,0 +1,1273 @@ +#define WITHWINDOWS +#include "common.h" +#include "crossplatform.h" +#include "main.h" + +#include "DMAudio.h" +#include "AudioScriptObject.h" +#include "Camera.h" +#include "CarGen.h" +#include "Cranes.h" +#include "Clock.h" +#include "Date.h" +#include "FileMgr.h" +#include "Font.h" +#include "Frontend.h" +#include "GameLogic.h" +#include "Gangs.h" +#include "Garages.h" +#include "GenericGameStorage.h" +#include "Pad.h" +#include "Particle.h" +#include "ParticleObject.h" +#include "PathFind.h" +#include "PCSave.h" +#include "Phones.h" +#include "Pickups.h" +#include "PlayerPed.h" +#include "ProjectileInfo.h" +#include "Pools.h" +#include "Radar.h" +#include "Restart.h" +#include "Script.h" +#include "SetPieces.h" +#include "Stats.h" +#include "Streaming.h" +#include "Timer.h" +#include "TimeStep.h" +#include "Weather.h" +#include "World.h" +#include "Zones.h" +#include "Timecycle.h" +#include "Fluff.h" + +#include "vmu/vmu.h" + +#define BLOCK_COUNT 22 +#define SIZE_OF_SIMPLEVARS 0xE4 + +const uint32 SIZE_OF_ONE_GAME_IN_BYTES = 201729; + +#ifdef MISSION_REPLAY +int8 IsQuickSave; +const int PAUSE_SAVE_SLOT = SLOT_COUNT; +#endif + +char DefaultPCSaveFileName[260]; +char ValidSaveName[260]; +char LoadFileName[256]; +wchar SlotFileName[SLOT_COUNT][260]; +wchar SlotSaveDate[SLOT_COUNT][70]; +int CheckSum; +eLevelName m_LevelToLoad; +char SaveFileNameJustSaved[260]; +int Slots[SLOT_COUNT]; + +bool b_FoundRecentSavedGameWantToLoad; +bool JustLoadedDontFadeInYet; +bool StillToFadeOut; +uint32 TimeStartedCountingForFade; +uint32 TimeToStayFadedBeforeFadeOut = 1750; + +int32 RadioStationPosition[NUM_RADIOS]; + +void +InitRadioStationPositionList() +{ + for (int i = 0; i < NUM_RADIOS; i++) + RadioStationPosition[i] = -1; +} + +int32 +GetSavedRadioStationPosition(int32 station) +{ + return RadioStationPosition[station]; +} + +void +PopulateRadioStationPositionList() +{ + for (int i = 0; i < NUM_RADIOS; i++) + RadioStationPosition[i] = DMAudio.GetRadioPosition(i); +} + +#define ReadDataFromBufferPointer(buf, to) memcpy(&to, buf, sizeof(to)); buf += align4bytes(sizeof(to)); +#define WriteDataToBufferPointer(buf, from) memcpy(buf, &from, sizeof(from)); buf += align4bytes(sizeof(from)); + +#define LoadSaveDataBlock()\ +do {\ + size = C_PcSave::PcClassLoadRoutine(file, work_buff); \ + if (!size) {\ + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_READ; \ + if (!CloseFile(file)) { \ + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_CLOSE; \ + } \ + return false; \ + } \ + buf = work_buff;\ +} while (0) + +#define ReadDataFromBlock(msg,load_func)\ +do {\ + debug(msg);\ + ReadDataFromBufferPointer(buf, size);\ + load_func(buf, size);\ + size = align4bytes(size);\ + buf += size;\ +} while (0) + +#define WriteSaveDataBlock(save_func, msg)\ +do {\ + size = 0;\ + buf = work_buff;\ + reserved = 0;\ + MakeSpaceForSizeInBufferPointer(presize, buf, postsize);\ + save_func(buf, &size);\ + debug(msg"== %i \n", size);\ + CopySizeAndPreparePointer(presize, buf, postsize, reserved, size);\ + if (!PcSaveHelper.PcClassSaveRoutine(file, work_buff, buf - work_buff)) { \ + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; \ + if (!CloseFile(file)) \ + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_CLOSE; \ + return false;\ + } \ + totalSize += buf - work_buff;\ +} while (0) + +bool +GenericSave(int file) +{ + uint8 *buf, *presize, *postsize; + uint32 size; + uint32 reserved; + + uint32 totalSize; + + wchar *lastMissionPassed; + wchar suffix[6]; + wchar saveName[24]; + SYSTEMTIME saveTime; + CPad *currPad; + + CheckSum = 0; + buf = work_buff; + reserved = 0; + + // Save simple vars + lastMissionPassed = TheText.Get(CStats::LastMissionPassedName[0] ? CStats::LastMissionPassedName : "ITBEG"); + AsciiToUnicode("...'", suffix); + suffix[3] = L'\0'; +#ifdef FIX_BUGS + // fix buffer overflow + int len = UnicodeStrlen(lastMissionPassed); + if (len > ARRAY_SIZE(saveName)-1) + len = ARRAY_SIZE(saveName)-1; + memcpy(saveName, lastMissionPassed, sizeof(wchar) * len); +#else + TextCopy(saveName, lastMissionPassed); + int len = UnicodeStrlen(saveName); +#endif + saveName[len] = '\0'; + if (len > ARRAY_SIZE(saveName)-2) + TextCopy(&saveName[ARRAY_SIZE(saveName)-ARRAY_SIZE(suffix)], suffix); + saveName[ARRAY_SIZE(saveName)-1] = '\0'; + WriteDataToBufferPointer(buf, saveName); + GetLocalTime(&saveTime); + WriteDataToBufferPointer(buf, saveTime); +#ifdef MISSION_REPLAY + int32 data = IsQuickSave << 24 | SIZE_OF_ONE_GAME_IN_BYTES; + WriteDataToBufferPointer(buf, data); +#else + WriteDataToBufferPointer(buf, SIZE_OF_ONE_GAME_IN_BYTES); +#endif + WriteDataToBufferPointer(buf, CGame::currLevel); + WriteDataToBufferPointer(buf, TheCamera.GetPosition().x); + WriteDataToBufferPointer(buf, TheCamera.GetPosition().y); + WriteDataToBufferPointer(buf, TheCamera.GetPosition().z); + WriteDataToBufferPointer(buf, CClock::ms_nMillisecondsPerGameMinute); + WriteDataToBufferPointer(buf, CClock::ms_nLastClockTick); + WriteDataToBufferPointer(buf, CClock::ms_nGameClockHours); + WriteDataToBufferPointer(buf, CClock::ms_nGameClockMinutes); + currPad = CPad::GetPad(0); + WriteDataToBufferPointer(buf, currPad->Mode); + WriteDataToBufferPointer(buf, CTimer::m_snTimeInMilliseconds); + WriteDataToBufferPointer(buf, CTimer::ms_fTimeScale); + WriteDataToBufferPointer(buf, CTimer::ms_fTimeStep); + WriteDataToBufferPointer(buf, CTimer::ms_fTimeStepNonClipped); + WriteDataToBufferPointer(buf, CTimer::m_FrameCounter); + WriteDataToBufferPointer(buf, CTimeStep::ms_fTimeStep); + WriteDataToBufferPointer(buf, CTimeStep::ms_fFramesPerUpdate); + WriteDataToBufferPointer(buf, CTimeStep::ms_fTimeScale); + WriteDataToBufferPointer(buf, CWeather::OldWeatherType); + WriteDataToBufferPointer(buf, CWeather::NewWeatherType); + WriteDataToBufferPointer(buf, CWeather::ForcedWeatherType); + WriteDataToBufferPointer(buf, CWeather::InterpolationValue); + WriteDataToBufferPointer(buf, CWeather::WeatherTypeInList); +#ifdef COMPATIBLE_SAVES + // converted to float for compatibility with original format + // TODO: maybe remove this? not really gonna break anything vital + float f = TheCamera.CarZoomIndicator; + WriteDataToBufferPointer(buf, f); + f = TheCamera.PedZoomIndicator; + WriteDataToBufferPointer(buf, f); +#else + WriteDataToBufferPointer(buf, TheCamera.CarZoomIndicator); + WriteDataToBufferPointer(buf, TheCamera.PedZoomIndicator); +#endif + WriteDataToBufferPointer(buf, CGame::currArea); + WriteDataToBufferPointer(buf, CVehicle::bAllTaxisHaveNitro); + WriteDataToBufferPointer(buf, CPad::bInvertLook4Pad); + WriteDataToBufferPointer(buf, CTimeCycle::m_ExtraColour); + WriteDataToBufferPointer(buf, CTimeCycle::m_bExtraColourOn); + WriteDataToBufferPointer(buf, CTimeCycle::m_ExtraColourInter); + PopulateRadioStationPositionList(); + WriteDataToBufferPointer(buf, RadioStationPosition); + assert(buf - work_buff == SIZE_OF_SIMPLEVARS); + + // Save scripts, block is nested within the same block as simple vars for some reason + presize = buf; + buf += 4; + postsize = buf; + CTheScripts::SaveAllScripts(buf, &size); + debug("ScriptSize== %i \n", size); + CopySizeAndPreparePointer(presize, buf, postsize, reserved, size); + if (!PcSaveHelper.PcClassSaveRoutine(file, work_buff, buf - work_buff)) { + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; + if (!CloseFile(file)) + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_CLOSE; + return false; + } + + totalSize = buf - work_buff; + + // Save the rest + WriteSaveDataBlock(CPools::SavePedPool, "PedPoolSize"); + WriteSaveDataBlock(CGarages::Save, "GaragesSize"); + WriteSaveDataBlock(CGameLogic::Save, "GameLogicSize"); + WriteSaveDataBlock(CPools::SaveVehiclePool, "VehPoolSize"); + WriteSaveDataBlock(CPools::SaveObjectPool, "ObjectPoolSize"); + WriteSaveDataBlock(ThePaths.Save, "ThePathsSize"); + WriteSaveDataBlock(CCranes::Save, "CranesSize"); + WriteSaveDataBlock(CPickups::Save, "PickUpsSize"); + WriteSaveDataBlock(gPhoneInfo.Save, "PhoneInfoSize"); + WriteSaveDataBlock(CRestart::SaveAllRestartPoints, "RestartPointsBufferSize"); + WriteSaveDataBlock(CRadar::SaveAllRadarBlips, "RadarBlipsBufferSize"); + WriteSaveDataBlock(CTheZones::SaveAllZones, "AllZonesBufferSize"); + WriteSaveDataBlock(CGangs::SaveAllGangData, "AllGangDataSize"); + WriteSaveDataBlock(CTheCarGenerators::SaveAllCarGenerators, "AllCarGeneratorsSize"); + WriteSaveDataBlock(CParticleObject::SaveParticle, "ParticlesSize"); + WriteSaveDataBlock(cAudioScriptObject::SaveAllAudioScriptObjects, "AllAudioScriptObjectsSize"); + WriteSaveDataBlock(CScriptPaths::Save, "ScriptPathsSize"); + WriteSaveDataBlock(CWorld::Players[CWorld::PlayerInFocus].SavePlayerInfo, "PlayerInfoSize"); + WriteSaveDataBlock(CStats::SaveStats, "StatsSize"); + WriteSaveDataBlock(CSetPieces::Save, "SetPiecesSize"); + WriteSaveDataBlock(CStreaming::MemoryCardSave, "StreamingSize"); + WriteSaveDataBlock(CPedType::Save, "PedTypeSize"); + + // sure just write garbage data repeatedly ... +#ifndef THIS_IS_STUPID + memset(work_buff, 0, sizeof(work_buff)); +#endif + + // Write padding + for (int i = 0; i < 4; i++) { + size = align4bytes(SIZE_OF_ONE_GAME_IN_BYTES - totalSize - 4); + if (size > sizeof(work_buff)) + size = sizeof(work_buff); + if (size > 4) { + if (!PcSaveHelper.PcClassSaveRoutine(file, work_buff, size)) { + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; + if (!CloseFile(file)) + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_CLOSE; + return false; + } + totalSize += size; + } + } + + // Write checksum and close + bool err = CFileMgr::Write(file, (const char *) &CheckSum, sizeof(CheckSum)) != sizeof(CheckSum); + if (err || CFileMgr::GetErrorReadWrite(file)) { + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; + if (!CloseFile(file)) + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_CLOSE; + + return false; + } + + CPad::FixPadsAfterSave(); + return true; +} + +bool +GenericLoad() +{ + uint8 *buf; + int32 file; + uint32 size; +#ifdef MISSION_REPLAY + int8 qs; +#endif + + int32 saveSize; + CPad *currPad; + + // Load SimpleVars and Scripts + CheckSum = 0; + CDate dummy; // unused + CPad::ResetCheats(); + + file = CFileMgr::OpenFile(LoadFileName, "rb"); + if (file == 0) { + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_OPEN; + return false; + } + size = C_PcSave::PcClassLoadRoutine(file, work_buff); + if (!size) { + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_READ; + if (!CloseFile(file)) { + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_CLOSE; + } + return false; + } + buf = (work_buff + 0x40); + ReadDataFromBufferPointer(buf, saveSize); + +#ifdef MISSION_REPLAY // a hack to keep compatibility but get new data from save + qs = saveSize >> 24; +#endif + ReadDataFromBufferPointer(buf, CGame::currLevel); + ReadDataFromBufferPointer(buf, TheCamera.GetMatrix().GetPosition().x); + ReadDataFromBufferPointer(buf, TheCamera.GetMatrix().GetPosition().y); + ReadDataFromBufferPointer(buf, TheCamera.GetMatrix().GetPosition().z); + ReadDataFromBufferPointer(buf, CClock::ms_nMillisecondsPerGameMinute); + ReadDataFromBufferPointer(buf, CClock::ms_nLastClockTick); + ReadDataFromBufferPointer(buf, CClock::ms_nGameClockHours); + ReadDataFromBufferPointer(buf, CClock::ms_nGameClockMinutes); + currPad = CPad::GetPad(0); + ReadDataFromBufferPointer(buf, currPad->Mode); + ReadDataFromBufferPointer(buf, CTimer::m_snTimeInMilliseconds); + ReadDataFromBufferPointer(buf, CTimer::ms_fTimeScale); + ReadDataFromBufferPointer(buf, CTimer::ms_fTimeStep); + ReadDataFromBufferPointer(buf, CTimer::ms_fTimeStepNonClipped); + ReadDataFromBufferPointer(buf, CTimer::m_FrameCounter); + ReadDataFromBufferPointer(buf, CTimeStep::ms_fTimeStep); + ReadDataFromBufferPointer(buf, CTimeStep::ms_fFramesPerUpdate); + ReadDataFromBufferPointer(buf, CTimeStep::ms_fTimeScale); + ReadDataFromBufferPointer(buf, CWeather::OldWeatherType); + ReadDataFromBufferPointer(buf, CWeather::NewWeatherType); + ReadDataFromBufferPointer(buf, CWeather::ForcedWeatherType); +#ifdef SECUROM + if (CTimer::m_FrameCounter > 72000){ + buf += align4bytes(4); + } +#endif + ReadDataFromBufferPointer(buf, CWeather::InterpolationValue); + ReadDataFromBufferPointer(buf, CWeather::WeatherTypeInList); +#ifdef COMPATIBLE_SAVES + // converted to float for compatibility with original format + // TODO: maybe remove this? not really gonna break anything vital + float f; + ReadDataFromBufferPointer(buf, f); + TheCamera.CarZoomIndicator = f; + ReadDataFromBufferPointer(buf, f); + TheCamera.PedZoomIndicator = f; +#else + ReadDataFromBufferPointer(buf, TheCamera.CarZoomIndicator); + ReadDataFromBufferPointer(buf, TheCamera.PedZoomIndicator); +#endif + ReadDataFromBufferPointer(buf, CGame::currArea); + ReadDataFromBufferPointer(buf, CVehicle::bAllTaxisHaveNitro); +#ifdef LOAD_INI_SETTINGS + buf += align4bytes(sizeof(CPad::bInvertLook4Pad)); +#else + ReadDataFromBufferPointer(buf, CPad::bInvertLook4Pad); +#endif + ReadDataFromBufferPointer(buf, CTimeCycle::m_ExtraColour); + ReadDataFromBufferPointer(buf, CTimeCycle::m_bExtraColourOn); + ReadDataFromBufferPointer(buf, CTimeCycle::m_ExtraColourInter); + ReadDataFromBufferPointer(buf, RadioStationPosition); + assert(buf - work_buff == SIZE_OF_SIMPLEVARS); +#ifdef MISSION_REPLAY + WaitForSave = 0; + if (FrontEndMenuManager.m_nCurrSaveSlot == PAUSE_SAVE_SLOT && qs == 3) + WaitForMissionActivate = CTimer::GetTimeInMilliseconds() + 2000; +#endif + ReadDataFromBlock("Loading Scripts \n", CTheScripts::LoadAllScripts); + + // Load the rest + LoadSaveDataBlock(); + ReadDataFromBlock("Loading PedPool \n", CPools::LoadPedPool); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading Garages \n", CGarages::Load); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading GameLogic \n", CGameLogic::Load); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading Vehicles \n", CPools::LoadVehiclePool); + LoadSaveDataBlock(); + CProjectileInfo::RemoveAllProjectiles(); + CObject::DeleteAllTempObjects(); + ReadDataFromBlock("Loading Objects \n", CPools::LoadObjectPool); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading Paths \n", ThePaths.Load); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading Cranes \n", CCranes::Load); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading Pickups \n", CPickups::Load); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading Phoneinfo \n", gPhoneInfo.Load); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading Restart \n", CRestart::LoadAllRestartPoints); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading Radar Blips \n", CRadar::LoadAllRadarBlips); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading Zones \n", CTheZones::LoadAllZones); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading Gang Data \n", CGangs::LoadAllGangData); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading Car Generators \n", CTheCarGenerators::LoadAllCarGenerators); + CParticle::ReloadConfig(); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading Particles \n", CParticleObject::LoadParticle); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading AudioScript Objects \n", cAudioScriptObject::LoadAllAudioScriptObjects); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading ScriptPaths \n", CScriptPaths::Load); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading Player Info \n", CWorld::Players[CWorld::PlayerInFocus].LoadPlayerInfo); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading Stats \n", CStats::LoadStats); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading Set Pieces \n", CSetPieces::Load); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading Streaming Stuff \n", CStreaming::MemoryCardLoad); + LoadSaveDataBlock(); + ReadDataFromBlock("Loading PedType Stuff \n", CPedType::Load); + + DMAudio.SetMusicMasterVolume(FrontEndMenuManager.m_PrefsMusicVolume); + DMAudio.SetEffectsMasterVolume(FrontEndMenuManager.m_PrefsSfxVolume); + if (!CloseFile(file)) { + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_CLOSE; + return false; + } + + DoGameSpecificStuffAfterSucessLoad(); + debug("Game successfully loaded \n"); + return true; +} + +bool +ReadInSizeofSaveFileBuffer(int32 &file, uint32 &size) +{ + file = CFileMgr::OpenFile(LoadFileName, "rb"); + if (file == 0) { + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_OPEN; + return false; + } + CFileMgr::Read(file, (const char*)&size, sizeof(size)); + if (CFileMgr::GetErrorReadWrite(file)) { + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_READ; + if (!CloseFile(file)) + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_CLOSE; + return false; + } + return true; +} + +bool +ReadDataFromFile(int32 file, uint8 *buf, uint32 size) +{ + if (file == 0) { + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_OPEN; + return false; + } + size_t read_size = CFileMgr::Read(file, (const char*)buf, size); + if (CFileMgr::GetErrorReadWrite(file) || read_size != size) { + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_READ; + if (!CloseFile(file)) + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_CLOSE; + return false; + } + return true; +} + +bool +CloseFile(int32 file) +{ + return CFileMgr::CloseFile(file) == 0; +} + +void +DoGameSpecificStuffAfterSucessLoad() +{ + CCollision::SortOutCollisionAfterLoad(); + CStreaming::LoadSceneCollision(TheCamera.GetPosition()); + CStreaming::LoadScene(TheCamera.GetPosition()); + CGame::TidyUpMemory(true, false); + StillToFadeOut = true; + JustLoadedDontFadeInYet = true; + TheCamera.Fade(0.0f, FADE_OUT); + CTheScripts::Process(); +} + +bool +CheckSlotDataValid(int32 slot) +{ + PcSaveHelper.nErrorCode = SAVESTATUS_SUCCESSFUL; + if (CheckDataNotCorrupt(slot, LoadFileName)) { + CStreaming::DeleteAllRwObjects(); + return true; + } + + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_DATA_INVALID; + return false; +} + +void +MakeSpaceForSizeInBufferPointer(uint8 *&presize, uint8 *&buf, uint8 *&postsize) +{ + presize = buf; + buf += sizeof(uint32); + postsize = buf; +} + +void +CopySizeAndPreparePointer(uint8 *&buf, uint8 *&postbuf, uint8 *&postbuf2, uint32 &unused, uint32 &size) +{ + memcpy(buf, &size, sizeof(size)); + size = align4bytes(size); + postbuf2 += size; + postbuf = postbuf2; +} + +void +DoGameSpecificStuffBeforeSave() +{ + CGameLogic::PassTime(360); + CPlayerPed *ped = FindPlayerPed(); + ped->m_fCurrentStamina = ped->m_fMaxStamina; + CGame::TidyUpMemory(true, false); +} + + +void +MakeValidSaveName(int32 slot) +{ + ValidSaveName[0] = '\0'; + sprintf(ValidSaveName, "%s%i", slot==7?"GTAVCSF":DefaultPCSaveFileName, slot + 1); + strncat(ValidSaveName, ".b", 5); +} + +wchar * +GetSavedGameDateAndTime(int32 slot) +{ + return SlotSaveDate[slot]; +} + +wchar * +GetNameOfSavedGame(int32 slot) +{ + return SlotFileName[slot]; +} + +bool +CheckDataNotCorrupt(int32 slot, char *name) +{ +#ifdef FIX_BUGS + char filename[MAX_PATH]; +#else + char filename[100]; +#endif + + int32 blocknum = 0; + eLevelName level = LEVEL_GENERIC; + CheckSum = 0; + uint32 bytes_processed = 0; + #if !defined(RW_DC) + sprintf(filename, "%s%i%s", DefaultPCSaveFileName, slot + 1, ".b"); + #else + sprintf(filename, "%s%i%s", slot==7?"GTAVCSF":DefaultPCSaveFileName, slot + 1, ".b"); + #endif + int file = CFileMgr::OpenFile(filename, "rb"); + if (file == 0) + return false; + strcpy(name, filename); + while (SIZE_OF_ONE_GAME_IN_BYTES - sizeof(uint32) > bytes_processed && blocknum < 40) { + int32 blocksize; + blocksize = C_PcSave::PcClassLoadRoutine(file, work_buff); + if (blocksize == 0) { + CloseFile(file); + return false; + } + + CheckSum += ((uint8*)&blocksize)[0]; + CheckSum += ((uint8*)&blocksize)[1]; + CheckSum += ((uint8*)&blocksize)[2]; + CheckSum += ((uint8*)&blocksize)[3]; + uint8 *_work_buf = work_buff; + for (int i = 0; i < align4bytes(blocksize); i++) { + CheckSum += *_work_buf++; + bytes_processed++; + } + + if (blocknum == 0) + memcpy(&level, work_buff+4, sizeof(level)); + blocknum++; + } + int32 _checkSum; + if (ReadDataFromFile(file, (uint8*)&_checkSum, sizeof(_checkSum))) { + if (CloseFile(file)) { + if (CheckSum == _checkSum) { + m_LevelToLoad = level; + return true; + } + return false; + } + return false; + } + + CloseFile(file); + return false; +} + +bool +RestoreForStartLoad() +{ + int file = CFileMgr::OpenFile(LoadFileName, "rb"); + if (file == 0) { + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_OPEN; + return false; + } + + uint32_t size = C_PcSave::PcClassLoadRoutine(file, work_buff); + uint8 *buf = work_buff; + + if (size == 0 || CFileMgr::GetErrorReadWrite(file)) { + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_READ; + if (!CloseFile(file)) + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_CLOSE; + return false; + } else { + uint8 *_buf = buf + sizeof(int32) + sizeof(wchar[24]) + sizeof(SYSTEMTIME) + sizeof(SIZE_OF_ONE_GAME_IN_BYTES); + ReadDataFromBufferPointer(_buf, CGame::currLevel); + ReadDataFromBufferPointer(_buf, TheCamera.GetMatrix().GetPosition().x); + ReadDataFromBufferPointer(_buf, TheCamera.GetMatrix().GetPosition().y); + ReadDataFromBufferPointer(_buf, TheCamera.GetMatrix().GetPosition().z); + CStreaming::RemoveUnusedBigBuildings(CGame::currLevel); + CStreaming::RemoveUnusedBuildings(CGame::currLevel); + + if (CloseFile(file)) { + return true; + } else { + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_CLOSE; + return false; + } + } +} + +int +align4bytes(int32 size) +{ + return (size + 3) & 0xFFFFFFFC; +} + +#ifdef FIX_INCOMPATIBLE_SAVES +#define LoadSaveDataBlockNoCheck(buf, file, size) \ +do { \ + CFileMgr::Read(file, (const char *)&size, sizeof(size)); \ + size = align4bytes(size); \ + CFileMgr::Read(file, (const char *)work_buff, size); \ + buf = work_buff; \ +} while(0) + +#define WriteSavaDataBlockNoFunc(buf, file, size) \ +do { \ + if (!PcSaveHelper.PcClassSaveRoutine(file, buf, size)) \ + goto fail; \ + totalSize += size; \ +} while(0) + +#define FixSaveDataBlock(fix_func, file, size) \ +do { \ + ReadDataFromBufferPointer(buf, size); \ + memset(work_buff2, 0, sizeof(work_buff2)); \ + buf2 = work_buff2; \ + reserved = 0; \ + MakeSpaceForSizeInBufferPointer(presize, buf2, postsize); \ + fix_func(save_type, buf, buf2, &size); \ + CopySizeAndPreparePointer(presize, buf2, postsize, reserved, size); \ + if (!PcSaveHelper.PcClassSaveRoutine(file, work_buff2, buf2 - work_buff2)) \ + goto fail; \ + totalSize += buf2 - work_buff2; \ +} while(0) + +#define ReadDataFromBufferPointerWithSize(buf, to, size) memcpy(&to, buf, size); buf += align4bytes(size) + +#define ReadBuf(buf, to) memcpy(&to, buf, sizeof(to)); buf += sizeof(to) +#define WriteBuf(buf, from) memcpy(buf, &from, sizeof(from)); buf += sizeof(from) +#define CopyBuf(from, to, size) memcpy(to, from, size); to += (size); from += (size) +#define CopyPtr(from, to) memcpy(to, from, 4); to += 4; from += 8 +#define SkipBuf(buf, size) buf += (size) +#define SkipBoth(from, to, size) to += (size); from += (size) +#define SkipPtr(from, to) to += 4; from += 8 + +// unfortunately we need a 2nd buffer of the same size to store the fixed output ... +static uint8 work_buff2[sizeof(work_buff)]; + +enum +{ + SAVE_TYPE_NONE = 0, + SAVE_TYPE_32_BIT = 1, + SAVE_TYPE_64_BIT = 2, + SAVE_TYPE_MSVC = 4, + SAVE_TYPE_GCC = 8, + SAVE_TYPE_STEAM = 16, +}; + +uint8 +GetSaveType(char *savename) +{ + uint8 save_type = SAVE_TYPE_NONE; + int file = CFileMgr::OpenFile(savename, "rb"); + + uint32 size; + CFileMgr::Read(file, (const char *)&size, sizeof(size)); + + uint8 *buf = work_buff; + CFileMgr::Read(file, (const char *)work_buff, size); // simple vars + scripts + + buf += 0x40 + sizeof(int32) + sizeof(int32) + sizeof(float) * 3; + + int8 steam_byte; + ReadDataFromBufferPointer(buf, steam_byte); + + if (steam_byte == -3) + save_type |= SAVE_TYPE_STEAM; + + LoadSaveDataBlockNoCheck(buf, file, size); // ped pool + + LoadSaveDataBlockNoCheck(buf, file, size); // garages + ReadDataFromBufferPointer(buf, size); + + // store for later after we know how much data we need to skip + ReadDataFromBufferPointerWithSize(buf, work_buff2, size); + + LoadSaveDataBlockNoCheck(buf, file, size); // game logic + LoadSaveDataBlockNoCheck(buf, file, size); // vehicle pool + LoadSaveDataBlockNoCheck(buf, file, size); // object pool + LoadSaveDataBlockNoCheck(buf, file, size); // paths + + LoadSaveDataBlockNoCheck(buf, file, size); // cranes + + CFileMgr::CloseFile(file); + + ReadDataFromBufferPointer(buf, size); + + if (size == 1000) + save_type |= SAVE_TYPE_32_BIT; + else if (size == 1160) + save_type |= SAVE_TYPE_64_BIT; + else + assert(0); // this should never happen + + buf = work_buff2; + + buf += 1964; // skip everything before the first garage + buf += save_type & SAVE_TYPE_32_BIT ? 28 : 40; // skip first garage up to m_vecCorner1 + + CVector2D vecCorner1; + float fInfZ, fSupZ; + + ReadBuf(buf, vecCorner1); + ReadBuf(buf, fInfZ); + SkipBuf(buf, sizeof(CVector2D)); + SkipBuf(buf, sizeof(CVector2D)); + ReadBuf(buf, fSupZ); + + // SET_GARAGE -914.129028 -1263.540039 10.706000 -907.137024 -1246.625977 -906.299988 -1266.900024 14.421000 + if (vecCorner1.x == -914.129028f && vecCorner1.y == -1263.540039f && + fInfZ == 10.706000f && fSupZ == 14.421000f) + save_type |= SAVE_TYPE_MSVC; + else + save_type |= SAVE_TYPE_GCC; + + return save_type; +} + +static void +FixSimpleVarsAndScripts(uint8 save_type, uint8 *buf, uint8 *buf2, uint32 *size) +{ + uint8 *buf_start = buf; + uint8 *buf2_start = buf2; + uint32 read = *size; + uint32 written = *size - (sizeof(int8) + 3); + + uint32 pre_steam = 0x40 + sizeof(int32) + sizeof(int32) + sizeof(float) * 3; + uint32 post_steam = *size - (sizeof(int8) + 3) - pre_steam; + + CopyBuf(buf, buf2, pre_steam); + SkipBuf(buf, sizeof(int8) + 3); + CopyBuf(buf, buf2, post_steam); + + *size = 0; + + assert(buf - buf_start == read); + assert(buf2 - buf2_start == written); + + *size = written; +} + +static void +FixGarages(uint8 save_type, uint8 *buf, uint8 *buf2, uint32 *size) +{ + // hardcoded: 7876 + // x86 msvc: 7340 + // x86 gcc: 7020 + // amd64 msvc: 7852 + // amd64 gcc: 7660 + + uint8 *buf_start = buf; + uint8 *buf2_start = buf2; + uint32 read; + uint32 written = 7340; + + if (save_type & SAVE_TYPE_32_BIT && save_type & SAVE_TYPE_GCC) + read = 7020; + else if (save_type & SAVE_TYPE_64_BIT && save_type & SAVE_TYPE_GCC) + read = 7660; + else + read = 7852; + + uint32 ptrsize = save_type & SAVE_TYPE_32_BIT ? 4 : 8; + + CopyBuf(buf, buf2, 4 * 6); + CopyBuf(buf, buf2, 4 * TOTAL_COLLECTCARS_GARAGES); + CopyBuf(buf, buf2, 4); + + if (save_type & SAVE_TYPE_GCC) + { + for (int32 i = 0; i < NUM_GARAGE_STORED_CARS; i++) + { + for (int32 j = 0; j < TOTAL_HIDEOUT_GARAGES; j++) + { + CopyBuf(buf, buf2, 4 + sizeof(CVector) + sizeof(CVector)); + uint8 nFlags8; + ReadBuf(buf, nFlags8); + int32 nFlags32 = nFlags8; + WriteBuf(buf2, nFlags32); + CopyBuf(buf, buf2, 1 * 6); + SkipBuf(buf, 1); + SkipBuf(buf2, 2); + } + } + } + else + { + CopyBuf(buf, buf2, sizeof(CStoredCar) * NUM_GARAGE_STORED_CARS * TOTAL_HIDEOUT_GARAGES); + } + + for (int32 i = 0; i < NUM_GARAGES; i++) + { + CopyBuf(buf, buf2, 1 * 7); + SkipBoth(buf, buf2, 1); + CopyBuf(buf, buf2, 4); + SkipBuf(buf, ptrsize - 4); // write 4 bytes padding if 8 byte pointer, if not, write 0 + SkipBuf(buf, ptrsize * 2); + SkipBuf(buf2, 4 * 2); + CopyBuf(buf, buf2, 1 * 7); + SkipBoth(buf, buf2, 1); + CopyBuf(buf, buf2, sizeof(CVector2D) * 3 + 4 * 17 + 1); + SkipBoth(buf, buf2, 3); + SkipBuf(buf, ptrsize); + SkipBuf(buf2, 4); + + if (save_type & SAVE_TYPE_GCC) + SkipBuf(buf, save_type & SAVE_TYPE_64_BIT ? 36 + 4 : 36); // sizeof(CStoredCar) on gcc 64/32 before fix + else + SkipBuf(buf, sizeof(CStoredCar)); + + SkipBuf(buf2, sizeof(CStoredCar)); + } + + *size = 0; + + assert(buf - buf_start == read); + assert(buf2 - buf2_start == written); + + *size = 7876; +} + +static void +FixCranes(uint8 save_type, uint8 *buf, uint8 *buf2, uint32 *size) +{ + uint8 *buf_start = buf; + uint8 *buf2_start = buf2; + uint32 read = 2 * sizeof(uint32) + 0x480; // sizeof(aCranes) + uint32 written = 2 * sizeof(uint32) + 0x3E0; // see CRANES_SAVE_SIZE + + CopyBuf(buf, buf2, 4 + 4); + + for (int32 i = 0; i < NUM_CRANES; i++) + { + CopyPtr(buf, buf2); + CopyPtr(buf, buf2); + CopyBuf(buf, buf2, 14 * 4 + sizeof(CVector) * 3 + sizeof(CVector2D)); + SkipBuf(buf, 4); + CopyPtr(buf, buf2); + CopyBuf(buf, buf2, 4 + 7 * 1); + SkipBuf(buf, 5); + SkipBuf(buf2, 1); + } + + *size = 0; + + assert(buf - buf_start == read); + assert(buf2 - buf2_start == written); + + *size = written; +} + +static void +FixPickups(uint8 save_type, uint8 *buf, uint8 *buf2, uint32 *size) +{ + uint8 *buf_start = buf; + uint8 *buf2_start = buf2; + uint32 read = 0x5400 + sizeof(uint16) + sizeof(uint16) + sizeof(int32) * NUMCOLLECTEDPICKUPS; // sizeof(aPickUps) + uint32 written = 0x4440 + sizeof(uint16) + sizeof(uint16) + sizeof(int32) * NUMCOLLECTEDPICKUPS; // see PICKUPS_SAVE_SIZE + + for (int32 i = 0; i < NUMPICKUPS; i++) + { + CopyBuf(buf, buf2, sizeof(CVector) + 4); + CopyPtr(buf, buf2); + CopyPtr(buf, buf2); + CopyBuf(buf, buf2, 4 * 2 + 2 * 3 + 8 + 1 * 3); + SkipBuf(buf, 7); + SkipBuf(buf2, 3); + } + + CopyBuf(buf, buf2, 2); + SkipBoth(buf, buf2, 2); + + CopyBuf(buf, buf2, NUMCOLLECTEDPICKUPS * 4); + + *size = 0; + + assert(buf - buf_start == read); + assert(buf2 - buf2_start == written); + + *size = written; +} + +static void +FixPhoneInfo(uint8 save_type, uint8 *buf, uint8 *buf2, uint32 *size) +{ + uint8 *buf_start = buf; + uint8 *buf2_start = buf2; + uint32 read = 0x1138; // sizeof(CPhoneInfo) + uint32 written = 0xA30; // see PHONEINFO_SAVE_SIZE + + CopyBuf(buf, buf2, 4 + 4); + + for (int32 i = 0; i < NUMPHONES; i++) + { + CopyBuf(buf, buf2, sizeof(CVector)); + SkipBuf(buf, 4); + SkipPtr(buf, buf2); + SkipPtr(buf, buf2); + SkipPtr(buf, buf2); + SkipPtr(buf, buf2); + SkipPtr(buf, buf2); + SkipPtr(buf, buf2); + CopyBuf(buf, buf2, 4); + SkipBuf(buf, 4); + CopyPtr(buf, buf2); + CopyBuf(buf, buf2, 4 + 1); + SkipBoth(buf, buf2, 3); + } + + *size = 0; + + assert(buf - buf_start == read); + assert(buf2 - buf2_start == written); + + *size = written; +} + +static void +FixParticles(uint8 save_type, uint8 *buf, uint8 *buf2, uint32 *size) +{ + uint8 *buf_start = buf; + uint8 *buf2_start = buf2; + + int32 numObjects; + ReadBuf(buf, numObjects); + WriteBuf(buf2, numObjects); + + uint32 read = 0x98 * (numObjects + 1) + 4; // sizeof(CParticleObject) + uint32 written = 0x84 * (numObjects + 1) + 4; // see PARTICLE_OBJECT_SIZEOF + + for (int32 i = 0; i < numObjects; i++) + { + // CPlaceable + CopyBuf(buf, buf2, 4 * 4 * 4); + SkipPtr(buf, buf2); + CopyBuf(buf, buf2, 1); + SkipBuf(buf, 7); + SkipBuf(buf2, 3); + + // CParticleObject + SkipPtr(buf, buf2); + SkipPtr(buf, buf2); + SkipPtr(buf, buf2); + CopyBuf(buf, buf2, 4 * 3 + 2 * 1 + 2 * 2); + SkipBoth(buf, buf2, 2); + CopyBuf(buf, buf2, sizeof(CVector) + 2 * 4 + sizeof(CRGBA) + 2 * 1); + SkipBoth(buf, buf2, 2); + } + + SkipBuf(buf, 0x98); // sizeof(CParticleObject) + SkipBuf(buf2, 0x84); // see PARTICLE_OBJECT_SIZEOF + + *size = 0; + + assert(buf - buf_start == read); + assert(buf2 - buf2_start == written); + + *size = written; +} + +static void +FixScriptPaths(uint8 save_type, uint8 *buf, uint8 *buf2, uint32 *size) +{ + uint8 *buf_start = buf; + uint8 *buf2_start = buf2; + uint32 read = 0x108; // sizeof(CScriptPath) * 3 + uint32 written = 0x9C; // see SCRIPTPATHS_SAVE_SIZE + + for (int32 i = 0; i < 3; i++) + { + int32 numNodes; + ReadBuf(buf, numNodes); + WriteBuf(buf2, numNodes); + SkipBuf(buf, 4); + SkipPtr(buf, buf2); + CopyBuf(buf, buf2, 4 * 5); + SkipBuf(buf, 4); + + for (int32 i = 0; i < 6; i++) + { + CopyPtr(buf, buf2); + } + + for (int32 i = 0; i < numNodes; i++) + { + CopyBuf(buf, buf2, sizeof(CPlaneNode)); + read += sizeof(CPlaneNode); + written += sizeof(CPlaneNode); + } + } + + *size = 0; + + assert(buf - buf_start == read); + assert(buf2 - buf2_start == written); + + *size = written; +} + +bool +FixSave(int32 slot, uint8 save_type) +{ + if (save_type & SAVE_TYPE_32_BIT && save_type & SAVE_TYPE_MSVC && !(save_type & SAVE_TYPE_STEAM)) + return true; + + bool success = false; + + uint8 *buf, *presize, *postsize, *buf2; + uint32 size; + uint32 reserved; + + uint32 totalSize; + + char savename[MAX_PATH]; + char savename_bak[MAX_PATH]; + + sprintf(savename, "%s%i%s", DefaultPCSaveFileName, slot + 1, ".b"); + sprintf(savename_bak, "%s%i%s.%lld.bak", DefaultPCSaveFileName, slot + 1, ".b", time(nil)); + + assert(caserename(savename, savename_bak) == 0); + + int file_in = CFileMgr::OpenFile(savename_bak, "rb"); + int file_out = CFileMgr::OpenFileForWriting(savename); + + CheckSum = 0; + totalSize = 0; + + CFileMgr::Read(file_in, (const char *)&size, sizeof(size)); + size = align4bytes(size); + + buf = work_buff; + CFileMgr::Read(file_in, (const char *)work_buff, size); // simple vars + scripts + + if (save_type & SAVE_TYPE_STEAM && save_type & SAVE_TYPE_MSVC && save_type & SAVE_TYPE_32_BIT) { + memset(work_buff2, 0, sizeof(work_buff2)); + buf2 = work_buff2; + FixSimpleVarsAndScripts(save_type, buf, buf2, &size); + if (!PcSaveHelper.PcClassSaveRoutine(file_out, work_buff2, size)) + goto fail; + totalSize += size; + } else + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // ped pool + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // garages + if (!(save_type & SAVE_TYPE_STEAM && save_type & SAVE_TYPE_MSVC && save_type & SAVE_TYPE_32_BIT)) + FixSaveDataBlock(FixGarages, file_out, size); + else + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // game logic + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // vehicle pool + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // object pool + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // paths + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // cranes + if (save_type & SAVE_TYPE_64_BIT) + FixSaveDataBlock(FixCranes, file_out, size); + else + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // pickups + if (save_type & SAVE_TYPE_64_BIT) + FixSaveDataBlock(FixPickups, file_out, size); + else + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // phoneinfo + if (save_type & SAVE_TYPE_64_BIT) + FixSaveDataBlock(FixPhoneInfo, file_out, size); + else + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // restart + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // radar blips + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // zones + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // gang data + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // car generators + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // particles + if (save_type & SAVE_TYPE_64_BIT) + FixSaveDataBlock(FixParticles, file_out, size); + else + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // audio script objects + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // script paths + if (save_type & SAVE_TYPE_64_BIT) + FixSaveDataBlock(FixScriptPaths, file_out, size); + else + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // player info + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // stats + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // set pieces + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // streaming + WriteSavaDataBlockNoFunc(buf, file_out, size); + + LoadSaveDataBlockNoCheck(buf, file_in, size); // ped type + WriteSavaDataBlockNoFunc(buf, file_out, size); + + memset(work_buff, 0, sizeof(work_buff)); + + for (int i = 0; i < 4; i++) { + size = align4bytes(SIZE_OF_ONE_GAME_IN_BYTES - totalSize - 4); + if (size > sizeof(work_buff)) + size = sizeof(work_buff); + if (size > 4) { + if (!PcSaveHelper.PcClassSaveRoutine(file_out, work_buff, size)) + goto fail; + totalSize += size; + } + } + + if (!CFileMgr::Write(file_out, (const char *)&CheckSum, sizeof(CheckSum))) + goto fail; + + success = true; + +fail:; + CFileMgr::CloseFile(file_in); + CFileMgr::CloseFile(file_out); + + return success; +} + +#undef LoadSaveDataBlockNoCheck +#undef WriteSavaDataBlockNoFunc +#undef FixSaveDataBlock +#undef ReadDataFromBufferPointerWithSize +#undef ReadBuf +#undef WriteBuf +#undef CopyBuf +#undef CopyPtr +#undef SkipBuf +#undef SkipBoth +#undef SkipPtr +#endif + +#ifdef MISSION_REPLAY + +void DisplaySaveResult(int unk, char* name) +{} + +bool SaveGameForPause(int type) +{ + if (AllowMissionReplay != MISSION_RETRY_STAGE_NORMAL && AllowMissionReplay != MISSION_RETRY_STAGE_WAIT_FOR_TIMER_AFTER_RESTART) { + debug("SaveGameForPause failed during AllowMissionReplay %d", AllowMissionReplay); + return false; + } + if (type != SAVE_TYPE_QUICKSAVE_FOR_MISSION_REPLAY && WaitForSave > CTimer::GetTimeInMilliseconds()) { + debug("SaveGameForPause failed WaitForSave"); + return false; + } + WaitForSave = 0; + if (gGameState != GS_PLAYING_GAME || (CTheScripts::bAlreadyRunningAMissionScript && type != SAVE_TYPE_QUICKSAVE_FOR_SCRIPT_ON_A_MISSION)) { + DisplaySaveResult(3, CStats::LastMissionPassedName); + return false; + } + debug("SaveGameForPause ******************************** %s doSave %d", CStats::LastMissionPassedName, !CTheScripts::bAlreadyRunningAMissionScript); + IsQuickSave = type; + MissionStartTime = 0; + int res = PcSaveHelper.SaveSlot(PAUSE_SAVE_SLOT); + PcSaveHelper.PopulateSlotInfo(); + IsQuickSave = 0; + DisplaySaveResult(res, CStats::LastMissionPassedName); + return true; +} +#endif diff --git a/src/miami/save/GenericGameStorage.h b/src/miami/save/GenericGameStorage.h new file mode 100644 index 00000000..bebf426a --- /dev/null +++ b/src/miami/save/GenericGameStorage.h @@ -0,0 +1,66 @@ +#pragma once + +#include "Game.h" +#include "PCSave.h" + +#define SLOT_COUNT (8) + +void InitRadioStationPositionList(); +int32 GetSavedRadioStationPosition(int32 station); +void PopulateRadioStationPositionList(); +bool GenericSave(int file); +bool GenericLoad(); +bool ReadInSizeofSaveFileBuffer(int32 &file, uint32 &size); +bool ReadDataFromFile(int32 file, uint8 *buf, uint32 size); +bool CloseFile(int32 file); +void DoGameSpecificStuffAfterSucessLoad(); +bool CheckSlotDataValid(int32 slot); +void MakeSpaceForSizeInBufferPointer(uint8 *&presize, uint8 *&buf, uint8 *&postsize); +void CopySizeAndPreparePointer(uint8 *&buf, uint8 *&postbuf, uint8 *&postbuf2, uint32 &unused, uint32 &size); +void DoGameSpecificStuffBeforeSave(); +void MakeValidSaveName(int32 slot); +wchar *GetSavedGameDateAndTime(int32 slot); +wchar *GetNameOfSavedGame(int32 slot); +bool CheckDataNotCorrupt(int32 slot, char *name); +bool RestoreForStartLoad(); +int align4bytes(int32 size); + +#ifdef FIX_INCOMPATIBLE_SAVES +uint8 GetSaveType(char *savename); +bool FixSave(int32 slot, uint8 save_type); +#endif + +extern char DefaultPCSaveFileName[260]; +extern char ValidSaveName[260]; +extern char LoadFileName[256]; +extern wchar SlotFileName[SLOT_COUNT][260]; +extern wchar SlotSaveDate[SLOT_COUNT][70]; +extern int CheckSum; +extern enum eLevelName m_LevelToLoad; +extern int Slots[SLOT_COUNT]; + +extern bool b_FoundRecentSavedGameWantToLoad; +extern bool JustLoadedDontFadeInYet; +extern bool StillToFadeOut; +extern uint32 TimeStartedCountingForFade; +extern uint32 TimeToStayFadedBeforeFadeOut; + +extern char SaveFileNameJustSaved[260]; // 8F2570 + +const char TopLineEmptyFile[] = "THIS FILE IS NOT VALID YET"; + +#ifdef MISSION_REPLAY +extern int8 IsQuickSave; // originally int + +bool SaveGameForPause(int); + +enum { + SAVE_TYPE_NORMAL, + SAVE_TYPE_QUICKSAVE, + SAVE_TYPE_2, + SAVE_TYPE_QUICKSAVE_FOR_MISSION_REPLAY, + SAVE_TYPE_QUICKSAVE_FOR_SCRIPT, + SAVE_TYPE_QUICKSAVE_FOR_SCRIPT_ON_A_MISSION +}; + +#endif diff --git a/src/miami/save/MemoryCard.cpp b/src/miami/save/MemoryCard.cpp new file mode 100644 index 00000000..d6e95d33 --- /dev/null +++ b/src/miami/save/MemoryCard.cpp @@ -0,0 +1,3084 @@ +#define WITHWINDOWS +#include "common.h" +#ifdef PS2_MENU +#include "crossplatform.h" +#include "MemoryCard.h" +#include "main.h" +#include "DMAudio.h" +#include "AudioScriptObject.h" +#include "Camera.h" +#include "CarGen.h" +#include "Cranes.h" +#include "Clock.h" +#include "MBlur.h" +#include "Date.h" +#include "Font.h" +#include "FileMgr.h" +#include "Game.h" +#include "GameLogic.h" +#include "Gangs.h" +#include "Garages.h" +#include "GenericGameStorage.h" +#include "Pad.h" +#include "Particle.h" +#include "ParticleObject.h" +#include "PathFind.h" +#include "PCSave.h" +#include "Phones.h" +#include "Pickups.h" +#include "PlayerPed.h" +#include "ProjectileInfo.h" +#include "Pools.h" +#include "Radar.h" +#include "Restart.h" +#include "Script.h" +#include "Stats.h" +#include "Streaming.h" +#include "Sprite2d.h" +#include "Timer.h" +#include "TimeStep.h" +#include "Weather.h" +#include "World.h" +#include "Zones.h" +#include "Frontend_PS2.h" + +CMemoryCard TheMemoryCard; + +char icon_one[16] = "slime1.ico"; +char icon_two[16] = "slime2.ico"; +char icon_three[16] = "slime3.ico"; +char HostFileLocationOfIcons[64] = "icons\\"; +char TheGameRootDirectory[64] = "/BESLES-50330GTA30000"; + +#define ReadDataFromBufferPointer(buf, to) memcpy(&to, buf, sizeof(to)); buf += align4bytes(sizeof(to)); +#define WriteDataToBufferPointer(buf, from) memcpy(buf, &from, sizeof(from)); buf += align4bytes(sizeof(from)); + +static int +align4bytes(int32 size) +{ + return (size + 3) & 0xFFFFFFFC; +} + +unsigned short ascii_table[3][2] = +{ + { 0x824F, 0x30 }, /* 0-9 */ + { 0x8260, 0x41 }, /* A-Z */ + { 0x8281, 0x61 }, /* a-z */ +}; + +unsigned short ascii_special[33][2] = +{ + {0x8140, 0x20}, /* " " */ + {0x8149, 0x21}, /* "!" */ + {0x8168, 0x22}, /* """ */ + {0x8194, 0x23}, /* "#" */ + {0x8190, 0x24}, /* "$" */ + {0x8193, 0x25}, /* "%" */ + {0x8195, 0x26}, /* "&" */ + {0x8166, 0x27}, /* "'" */ + {0x8169, 0x28}, /* "(" */ + {0x816A, 0x29}, /* ")" */ + {0x8196, 0x2A}, /* "*" */ + {0x817B, 0x2B}, /* "+" */ + {0x8143, 0x2C}, /* "," */ + {0x817C, 0x2D}, /* "-" */ + {0x8144, 0x2E}, /* "." */ + {0x815E, 0x2F}, /* "/" */ + {0x8146, 0x3A}, /* ":" */ + {0x8147, 0x3B}, /* ";" */ + {0x8171, 0x3C}, /* "<" */ + {0x8181, 0x3D}, /* "=" */ + {0x8172, 0x3E}, /* ">" */ + {0x8148, 0x3F}, /* "?" */ + {0x8197, 0x40}, /* "@" */ + {0x816D, 0x5B}, /* "[" */ + {0x818F, 0x5C}, /* "\" */ + {0x816E, 0x5D}, /* "]" */ + {0x814F, 0x5E}, /* "^" */ + {0x8151, 0x5F}, /* "_" */ + {0x8165, 0x60}, /* "`" */ + {0x816F, 0x7B}, /* "{" */ + {0x8162, 0x7C}, /* "|" */ + {0x8170, 0x7D}, /* "}" */ + {0x8150, 0x7E}, /* "~" */ +}; + +unsigned short +Ascii2Sjis(unsigned char ascii_code) +{ + unsigned short sjis_code = 0; + unsigned char stmp; + unsigned char stmp2 = 0; + + if ((ascii_code >= 0x20) && (ascii_code <= 0x2f)) + stmp2 = 1; + else + if ((ascii_code >= 0x30) && (ascii_code <= 0x39)) + stmp = 0; + else + if ((ascii_code >= 0x3a) && (ascii_code <= 0x40)) + stmp2 = 11; + else + if ((ascii_code >= 0x41) && (ascii_code <= 0x5a)) + stmp = 1; + else + if ((ascii_code >= 0x5b) && (ascii_code <= 0x60)) + stmp2 = 37; + else + if ((ascii_code >= 0x61) && (ascii_code <= 0x7a)) + stmp = 2; + else + if ((ascii_code >= 0x7b) && (ascii_code <= 0x7e)) + stmp2 = 63; + else { + printf("bad ASCII code 0x%x\n", ascii_code); + return(0); + } + + if (stmp2) + sjis_code = ascii_special[ascii_code - 0x20 - (stmp2 - 1)][0]; + else + sjis_code = ascii_table[stmp][0] + ascii_code - ascii_table[stmp][1]; + + return(sjis_code); +} + +#if defined(GTA_PC) + +extern "C" +{ + extern void HandleExit(); +} + +char CardCurDir[MAX_CARDS][260] = { "", "" }; +char PCCardsPath[260]; +char PCCardDir[MAX_CARDS][12] = { "memcard1", "memcard2" }; + +const char* _psGetUserFilesFolder(); +void _psCreateFolder(LPCSTR path); + +void +PCMCInit() +{ + sprintf(PCCardsPath, "%s", _psGetUserFilesFolder()); + + char path[512]; + + sprintf(path, "%s\\%s", PCCardsPath, PCCardDir[CARD_ONE]); + _psCreateFolder(path); + + sprintf(path, "%s\\%s", PCCardsPath, PCCardDir[CARD_TWO]); + _psCreateFolder(path); +} +#endif + +CMemoryCardInfo::CMemoryCardInfo(void) +{ + type = 0; + free = 0; + format = 0; + + for ( int32 i = 0; i < sizeof(dir); i++ ) + dir[i] = '\0'; + + strncpy(dir, TheGameRootDirectory, sizeof(dir) - 1); +} + +int32 +CMemoryCard::Init(void) +{ +#if defined(PS2) + if ( sceMcInit() == sceMcIniSucceed ) + { + printf("Memory card initialsed\n"); + return RES_SUCCESS; + } + + printf("Memory Card not being initialised\n"); + return RES_FAILED; +#else + PCMCInit(); + printf("Memory card initialsed\n"); + return RES_SUCCESS; +#endif +} + +CMemoryCard::CMemoryCard(void) +{ + _unk0 = 0; + CurrentCard = CARD_ONE; + Cards[CARD_ONE].port = 0; + Cards[CARD_TWO].port = 1; + + for ( int32 i = 0; i < sizeof(_unkName3); i++ ) + _unkName3[i] = '\0'; + + m_bWantToLoad = false; + _bunk2 = false; + _bunk7 = false; + JustLoadedDontFadeInYet = false; + StillToFadeOut = false; + TimeStartedCountingForFade = 0; + TimeToStayFadedBeforeFadeOut = 1750; + b_FoundRecentSavedGameWantToLoad = false; + + char date[64]; + char time[64]; + char day[8]; + char month[8]; + char year[8]; + char hour[8]; + char minute[8]; + char second[8]; + + strncpy(date, "Oct 7 2001", 62); + strncpy(time, "15:48:32", 62); + + strncpy(month, date, 3); + month[3] = '\0'; + + strncpy(day, &date[4], 2); + day[2] = '\0'; + + strncpy(year, &date[7], 4); + year[4] = '\0'; + + strncpy(hour, time, 2); + hour[2] = '\0'; + + strncpy(minute, &time[3], 2); + minute[2] = '\0'; + + strncpy(second, &time[6], 2); + second[2] = '\0'; + + + #define _CMP(m) strncmp(month, m, sizeof(m)-1) + + if ( !_CMP("Jan") ) CompileDateAndTime.m_nMonth = 1; + else + if ( !_CMP("Feb") ) CompileDateAndTime.m_nMonth = 2; + else + if ( !_CMP("Mar") ) CompileDateAndTime.m_nMonth = 3; + else + if ( !_CMP("Apr") ) CompileDateAndTime.m_nMonth = 4; + else + if ( !_CMP("May") ) CompileDateAndTime.m_nMonth = 5; + else + if ( !_CMP("Jun") ) CompileDateAndTime.m_nMonth = 6; + else + if ( !_CMP("Jul") ) CompileDateAndTime.m_nMonth = 7; + else + if ( !_CMP("Aug") ) CompileDateAndTime.m_nMonth = 8; + else + if ( !_CMP("Oct") ) CompileDateAndTime.m_nMonth = 9; // BUG: oct and sep is swapped here + else + if ( !_CMP("Sep") ) CompileDateAndTime.m_nMonth = 10; + else + if ( !_CMP("Nov") ) CompileDateAndTime.m_nMonth = 11; + else + if ( !_CMP("Dec") ) CompileDateAndTime.m_nMonth = 12; + + #undef _CMP + + CompileDateAndTime.m_nDay = atoi(day); + CompileDateAndTime.m_nYear = atoi(year); + CompileDateAndTime.m_nHour = atoi(hour); + CompileDateAndTime.m_nMinute = atoi(minute); + CompileDateAndTime.m_nSecond = atoi(second); +} + +int32 +CMemoryCard::RestoreForStartLoad(void) +{ + uint8 buf[30]; + + int32 file = OpenMemCardFileForReading(CurrentCard, LoadFileName); + if ( nError != NO_ERR_SUCCESS ) + return RES_FAILED; + + ReadFromMemCard(file, buf, sizeof(buf) - 1); + if ( nError != NO_ERR_SUCCESS ) + return RES_FAILED; + + uint8 *pBuf = buf + sizeof(uint32) + sizeof(uint32); + ReadDataFromBufferPointer(pBuf, CGame::currLevel); + ReadDataFromBufferPointer(pBuf, TheCamera.GetMatrix().GetPosition().x); + ReadDataFromBufferPointer(pBuf, TheCamera.GetMatrix().GetPosition().y); + ReadDataFromBufferPointer(pBuf, TheCamera.GetMatrix().GetPosition().z); + + if ( CGame::currLevel != LEVEL_INDUSTRIAL ) + CStreaming::RemoveBigBuildings(LEVEL_INDUSTRIAL); + + if ( CGame::currLevel != LEVEL_COMMERCIAL ) + CStreaming::RemoveBigBuildings(LEVEL_COMMERCIAL); + + if ( CGame::currLevel != LEVEL_SUBURBAN ) + CStreaming::RemoveBigBuildings(LEVEL_SUBURBAN); + + CStreaming::RemoveIslandsNotUsed(CGame::currLevel); + CCollision::SortOutCollisionAfterLoad(); + CStreaming::RequestBigBuildings(CGame::currLevel); + CStreaming::LoadAllRequestedModels(false); + CStreaming::HaveAllBigBuildingsLoaded(CGame::currLevel); + CGame::TidyUpMemory(true, false); + + CloseMemCardFile(file); + + if ( nError != NO_ERR_SUCCESS ) + return RES_FAILED; + + return RES_SUCCESS; +} + +int32 +CMemoryCard::LoadSavedGame(void) +{ + CheckSum = 0; + CDate date; + + int32 saveSize = 0; + uint32 size = 0; + + int32 oldLang = CMenuManager::m_PrefsLanguage; + + CPad::ResetCheats(); + + ChangeDirectory(CurrentCard, Cards[CurrentCard].dir); + + if ( nError != NO_ERR_SUCCESS ) + return RES_FAILED; + + int32 file = OpenMemCardFileForReading(CurrentCard, LoadFileName); + + if ( nError != NO_ERR_SUCCESS ) + return RES_FAILED; + + + #define LoadSaveDataBlock()\ + do {\ + ReadFromMemCard(file, &size, sizeof(size)); \ + if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; \ + size = align4bytes(size); \ + ReadFromMemCard(file, work_buff, size); \ + if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; \ + buf = work_buff; \ + } while (0) + + uint8 *buf; + + LoadSaveDataBlock(); + + ReadDataFromBufferPointer(buf, saveSize); + ReadDataFromBufferPointer(buf, CGame::currLevel); + ReadDataFromBufferPointer(buf, TheCamera.GetMatrix().GetPosition().x); + ReadDataFromBufferPointer(buf, TheCamera.GetMatrix().GetPosition().y); + ReadDataFromBufferPointer(buf, TheCamera.GetMatrix().GetPosition().z); + ReadDataFromBufferPointer(buf, CClock::ms_nMillisecondsPerGameMinute); + ReadDataFromBufferPointer(buf, CClock::ms_nLastClockTick); + ReadDataFromBufferPointer(buf, CClock::ms_nGameClockHours); + ReadDataFromBufferPointer(buf, CClock::ms_nGameClockMinutes); + ReadDataFromBufferPointer(buf, CPad::GetPad(0)->Mode); + ReadDataFromBufferPointer(buf, CTimer::m_snTimeInMilliseconds); + ReadDataFromBufferPointer(buf, CTimer::ms_fTimeScale); + ReadDataFromBufferPointer(buf, CTimer::ms_fTimeStep); + ReadDataFromBufferPointer(buf, CTimer::ms_fTimeStepNonClipped); + ReadDataFromBufferPointer(buf, CTimer::m_FrameCounter); + ReadDataFromBufferPointer(buf, CTimeStep::ms_fTimeStep); + ReadDataFromBufferPointer(buf, CTimeStep::ms_fFramesPerUpdate); + ReadDataFromBufferPointer(buf, CTimeStep::ms_fTimeScale); + ReadDataFromBufferPointer(buf, CWeather::OldWeatherType); + ReadDataFromBufferPointer(buf, CWeather::NewWeatherType); + ReadDataFromBufferPointer(buf, CWeather::ForcedWeatherType); + ReadDataFromBufferPointer(buf, CWeather::InterpolationValue); + ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsMusicVolume); + ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsSfxVolume); + ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsControllerConfig); + ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsUseVibration); + ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsStereoMono); + ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsRadioStation); + ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsBrightness); + ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsShowTrails); + ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsShowSubtitles); + ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsLanguage); + ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsUseWideScreen); + ReadDataFromBufferPointer(buf, CPad::GetPad(0)->Mode); +#ifdef PS2 + ReadDataFromBufferPointer(buf, BlurOn); +#else + ReadDataFromBufferPointer(buf, CMBlur::BlurOn); +#endif + ReadDataFromBufferPointer(buf, date.m_nSecond); + ReadDataFromBufferPointer(buf, date.m_nMinute); + ReadDataFromBufferPointer(buf, date.m_nHour); + ReadDataFromBufferPointer(buf, date.m_nDay); + ReadDataFromBufferPointer(buf, date.m_nMonth); + ReadDataFromBufferPointer(buf, date.m_nYear); + ReadDataFromBufferPointer(buf, CWeather::WeatherTypeInList); + ReadDataFromBufferPointer(buf, TheCamera.CarZoomIndicator); + ReadDataFromBufferPointer(buf, TheCamera.PedZoomIndicator); + + if ( date > CompileDateAndTime ) + ; + else + if ( date < CompileDateAndTime ) + ; + + #define ReadDataFromBlock(load_func)\ + do {\ + ReadDataFromBufferPointer(buf, size);\ + load_func(buf, size);\ + size = align4bytes(size);\ + buf += size;\ + } while (0) + + + + printf("Loading Scripts \n"); + ReadDataFromBlock(CTheScripts::LoadAllScripts); + + printf("Loading PedPool \n"); + ReadDataFromBlock(CPools::LoadPedPool); + + printf("Loading Garages \n"); + ReadDataFromBlock(CGarages::Load); + + printf("Loading Vehicles \n"); + ReadDataFromBlock(CPools::LoadVehiclePool); + + LoadSaveDataBlock(); + + CProjectileInfo::RemoveAllProjectiles(); + CObject::DeleteAllTempObjects(); + + printf("Loading Objects \n"); + ReadDataFromBlock(CPools::LoadObjectPool); + + printf("Loading Paths \n"); + ReadDataFromBlock(ThePaths.Load); + + printf("Loading Cranes \n"); + ReadDataFromBlock(CCranes::Load); + + LoadSaveDataBlock(); + + printf("Loading Pickups \n"); + ReadDataFromBlock(CPickups::Load); + + printf("Loading Phoneinfo \n"); + ReadDataFromBlock(gPhoneInfo.Load); + + printf("Loading Restart \n"); + ReadDataFromBlock(CRestart::LoadAllRestartPoints); + + printf("Loading Radar Blips \n"); + ReadDataFromBlock(CRadar::LoadAllRadarBlips); + + printf("Loading Zones \n"); + ReadDataFromBlock(CTheZones::LoadAllZones); + + printf("Loading Gang Data \n"); + ReadDataFromBlock(CGangs::LoadAllGangData); + + printf("Loading Car Generators \n"); + ReadDataFromBlock(CTheCarGenerators::LoadAllCarGenerators); + + printf("Loading Particles \n"); + ReadDataFromBlock(CParticleObject::LoadParticle); + + printf("Loading AudioScript Objects \n"); + ReadDataFromBlock(cAudioScriptObject::LoadAllAudioScriptObjects); + + printf("Loading Player Info \n"); + ReadDataFromBlock(CWorld::Players[CWorld::PlayerInFocus].LoadPlayerInfo); + + printf("Loading Stats \n"); + ReadDataFromBlock(CStats::LoadStats); + + printf("Loading Streaming Stuff \n"); + ReadDataFromBlock(CStreaming::MemoryCardLoad); + + printf("Loading PedType Stuff \n"); + ReadDataFromBlock(CPedType::Load); + + #undef LoadSaveDataBlock + #undef ReadDataFromBlock + + FrontEndMenuManager.SetSoundLevelsForMusicMenu(); + FrontEndMenuManager.InitialiseMenuContentsAfterLoadingGame(); + + CloseMemCardFile(file); + + if ( nError != NO_ERR_SUCCESS ) + return RES_FAILED; + + if ( oldLang != CMenuManager::m_PrefsLanguage ) + { + TheText.Unload(); + TheText.Load(); + } + + JustLoadedDontFadeInYet = true; + StillToFadeOut = true; + + CTheScripts::Process(); + + printf("Game sucessfully loaded \n"); + + return RES_SUCCESS; +} + +int32 +CMemoryCard::CheckCardInserted(int32 cardID) +{ +#if defined(PS2) + int cmd = sceMcFuncNoCardInfo; + int type = sceMcTypeNoCard; + + CTimer::Stop(); + + while ( sceMcGetInfo(Cards[cardID].port, 0, &type, 0, 0) != sceMcResSucceed ) + ; + + int result; + sceMcSync(0, &cmd, &result); + + if ( type == sceMcTypePS2 ) + { + if ( result == sceMcResChangedCard || result == sceMcResSucceed ) + { + nError = NO_ERR_SUCCESS; + return nError; + } + else if ( result == sceMcResNoFormat ) + { + nError = ERR_NOFORMAT; + return nError; + } + } + + printf("Memory card %i not present\n", cardID); + + nError = ERR_NONE; + return nError; +#else + nError = NO_ERR_SUCCESS; + return nError; +#endif +} + +int32 +CMemoryCard::PopulateCardFlags(int32 cardID, bool bSlotFlag, bool bTypeFlag, bool bFreeFlag, bool bFormatFlag) +{ +#if defined(PS2) + int cmd = sceMcFuncNoCardInfo; + int type = sceMcTypeNoCard; + int free = 0; + int format = 0; + + CTimer::Stop(); + + Cards[cardID].type = 0; + Cards[cardID].free = 0; + Cards[cardID].format = 0; + + while ( sceMcGetInfo(Cards[cardID].port, 0, &type, &free, &format) != sceMcResSucceed ) + ; + + int result; + sceMcSync(0, &cmd, &result); + + if ( type == sceMcTypePS2 ) + { + if ( result == sceMcResChangedCard || result == sceMcResSucceed ) + { + if ( bSlotFlag ) + Cards[cardID].slot = 0; + + //if ( bTypeFlag ) + Cards[cardID].type = type; + + if ( bFreeFlag ) + Cards[cardID].free = free; + + if ( bFormatFlag ) + Cards[cardID].format = format; + + printf("Memory card %i present\n", cardID); + + nError = NO_ERR_SUCCESS; + return nError; + } + else if ( result == sceMcResNoFormat ) + { + nError = ERR_NOFORMAT; + return nError; + } + } + + printf("Memory card %i not present\n", cardID); + + nError = ERR_NONE; + return nError; +#else + CTimer::Stop(); + + Cards[cardID].type = 0; + Cards[cardID].free = 0; + Cards[cardID].format = 0; + + if ( bSlotFlag ) + Cards[cardID].slot = 0; + + //if ( bTypeFlag ) + Cards[cardID].type = 0; + + if ( bFreeFlag ) + Cards[cardID].free = 1024 * 1024 * 4; + + if ( bFormatFlag ) + Cards[cardID].format = 0; + + printf("Memory card %i present\n", cardID); + + nError = NO_ERR_SUCCESS; + return nError; +#endif +} + +int32 +CMemoryCard::FormatCard(int32 cardID) +{ + CTimer::Stop(); + +#if defined(PS2) + int cmd = sceMcFuncNoFormat; + + int32 r = CheckCardInserted(cardID); + if ( r == NO_ERR_SUCCESS ) + { + while ( sceMcFormat(Cards[cardID].port, 0) != sceMcResSucceed ) + ; + + int result; + sceMcSync(0, &cmd, &result); + + if ( result < sceMcResSucceed ) + { + printf("Memory card %i could not be formatted\n", cardID); + + nError = ERR_FORMATFAILED; + return nError; + } + + printf("Memory card %i present and formatted\n", cardID); + + nError = NO_ERR_SUCCESS; + return nError; + } + + return r; +#else + printf("Memory card %i present and formatted\n", cardID); + + nError = NO_ERR_SUCCESS; + return nError; +#endif +} + +int32 +CMemoryCard::PopulateFileTable(int32 cardID) +{ + CTimer::Stop(); + +#if defined (PS2) + int cmd = sceMcFuncNoGetDir; + + ClearFileTableBuffer(cardID); + + while ( sceMcGetDir(Cards[cardID].port, 0, "*", 0, ARRAY_SIZE(Cards[cardID].table), Cards[cardID].table) != sceMcResSucceed ) + ; + + int result; + sceMcSync(0, &cmd, &result); + + if ( result >= sceMcResSucceed ) + { + printf("Memory card %i present PopulateFileTables function successfull \n", cardID); + + nError = NO_ERR_SUCCESS; + return nError; + } + + if ( result == sceMcResNoFormat ) + { + printf("Memory card %i PopulateFileTables function successfull. MemoryCard not Formatted \n", cardID); + + nError = ERR_NOFORMAT; + return nError; + } + + if ( result == sceMcResNoEntry ) + { + printf("Memory card %i PopulateFileTables function unsuccessfull. Path does not exist \n", cardID); + + nError = ERR_FILETABLENOENTRY; + return nError; + } + + printf("Memory card %i not Present\n", cardID); + + nError = ERR_NONE; + return nError; +#else + ClearFileTableBuffer(cardID); + + char path[512]; + sprintf(path, "%s\\%s\\%s\\*", PCCardsPath, PCCardDir[cardID], CardCurDir[cardID]); + + memset(Cards[cardID].table, 0, sizeof(Cards[cardID].table)); + WIN32_FIND_DATA fd; HANDLE hFind; int32 num = 0; + if ( (hFind = FindFirstFile(path, &fd)) == INVALID_HANDLE_VALUE ) + { + printf("Memory card %i not Present\n", cardID); + nError = ERR_NONE; + return nError; + } + do + { + SYSTEMTIME st; + FileTimeToSystemTime(&fd.ftCreationTime, &st); + Cards[cardID].table[num]._Create.Sec = st.wSecond;Cards[cardID].table[num]._Create.Min = st.wMinute;Cards[cardID].table[num]._Create.Hour = st.wHour;Cards[cardID].table[num]._Create.Day = st.wDay;Cards[cardID].table[num]._Create.Month = st.wMonth;Cards[cardID].table[num]._Create.Year = st.wYear; + FileTimeToSystemTime(&fd.ftLastWriteTime, &st); + Cards[cardID].table[num]._Modify.Sec = st.wSecond;Cards[cardID].table[num]._Modify.Min = st.wMinute;Cards[cardID].table[num]._Modify.Hour = st.wHour;Cards[cardID].table[num]._Modify.Day = st.wDay;Cards[cardID].table[num]._Modify.Month = st.wMonth;Cards[cardID].table[num]._Modify.Year = st.wYear; + Cards[cardID].table[num].FileSizeByte = fd.nFileSizeLow; + strncpy((char *)Cards[cardID].table[num].EntryName, fd.cFileName, sizeof(Cards[cardID].table[num].EntryName) - 1); + num++; + } while( FindNextFile(hFind, &fd) && num < ARRAY_SIZE(Cards[cardID].table) ); + FindClose(hFind); + + //todo errors + + printf("Memory card %i present PopulateFileTables function successfull \n", cardID); + + nError = NO_ERR_SUCCESS; + return nError; +#endif +} + +int32 +CMemoryCard::CreateRootDirectory(int32 cardID) +{ + CTimer::Stop(); +#if defined(PS2) + int cmd = sceMcFuncNoMkdir; + + while ( sceMcMkdir(Cards[cardID].port, 0, Cards[cardID].dir) != sceMcResSucceed ) + ; + + int result; + sceMcSync(0, &cmd, &result); + + if ( result == sceMcResSucceed ) + { + printf("Memory card %i present. RootDirectory Created\n", cardID); + + nError = NO_ERR_SUCCESS; + return nError; + } + + if ( result == sceMcResNoFormat ) + { + printf("Memory card %i RootDirectory not created card unformatted\n", cardID); + + nError = ERR_NOFORMAT; + return nError; + } + + if ( result == sceMcResFullDevice ) + { + printf("Memory card %i RootDirectory not created due to insufficient memory card capacity\n", cardID); + + nError = ERR_DIRFULLDEVICE; + return nError; + } + + if ( result == sceMcResNoEntry ) + { + printf("Memory card %i RootDirectory not created due to problem with pathname\n", cardID); + + nError = ERR_DIRBADENTRY; + return nError; + } + + printf("Memory card %i not present so RootDirectory not created \n", cardID); + + nError = ERR_NONE; + return nError; +#else + char path[512]; + sprintf(path, "%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], Cards[cardID].dir); + _psCreateFolder(path); + + printf("Memory card %i present. RootDirectory Created\n", cardID); + + nError = NO_ERR_SUCCESS; + return nError; +#endif +} + +int32 +CMemoryCard::ChangeDirectory(int32 cardID, char *dir) +{ + CTimer::Stop(); + +#if defined(PS2) + int cmd = sceMcFuncNoChDir; + + while ( sceMcChdir(Cards[cardID].port, 0, dir, 0) != sceMcResSucceed ) + ; + + int result; + sceMcSync(0, &cmd, &result); + + if ( result == sceMcResSucceed ) + { + printf("Memory Card %i. Changed to the directory %s \n", cardID, dir); + + nError = NO_ERR_SUCCESS; + return nError; + } + + if ( result == sceMcResNoFormat ) + { + printf("Memory card %i. Couldn't change to the directory %s. MemoryCard not Formatted \n", cardID, dir); + + nError = ERR_NOFORMAT; + return nError; + } + + if ( result == sceMcResNoEntry ) + { + printf("Memory card %i Couldn't change to the directory %s. Path does not exist \n", cardID, dir); + + nError = ERR_DIRNOENTRY; + return nError; + } + + printf("Memory card %i not Present. So could not change to directory %s.\n", cardID, dir); + + nError = ERR_NONE; + return nError; +#else + + if ( !strcmp(dir, "/" ) ) + { + strncpy(CardCurDir[cardID], dir, sizeof(CardCurDir[cardID]) - 1); + printf("Memory Card %i. Changed to the directory %s \n", cardID, dir); + nError = NO_ERR_SUCCESS; + return nError; + } + + char path[512]; + sprintf(path, "%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], dir); + + WIN32_FIND_DATA fd; HANDLE hFind; + if ( (hFind = FindFirstFile(path, &fd)) == INVALID_HANDLE_VALUE ) + { + printf("Memory card %i Couldn't change to the directory %s. Path does not exist \n", cardID, dir); + + nError = ERR_DIRNOENTRY; + return nError; + } + + FindClose(hFind); + + strncpy(CardCurDir[cardID], dir, sizeof(CardCurDir[cardID]) - 1); + printf("Memory Card %i. Changed to the directory %s \n", cardID, dir); + nError = NO_ERR_SUCCESS; + return nError; +#endif +} + +int32 +CMemoryCard::CreateIconFiles(int32 cardID, char *icon_one, char *icon_two, char *icon_three) +{ +#if defined(PS2) + sceMcIconSys icon; + static sceVu0IVECTOR bgcolor[4] = { + { 0x80, 0, 0, 0 }, + { 0, 0x80, 0, 0 }, + { 0, 0, 0x80, 0 }, + { 0x80, 0x80, 0x80, 0 }, + }; + static sceVu0FVECTOR lightdir[3] = { + { 0.5, 0.5, 0.5, 0.0 }, + { 0.0,-0.4,-0.1, 0.0 }, + {-0.5,-0.5, 0.5, 0.0 }, + }; + static sceVu0FVECTOR lightcol[3] = { + { 0.48, 0.48, 0.03, 0.00 }, + { 0.50, 0.33, 0.20, 0.00 }, + { 0.14, 0.14, 0.38, 0.00 }, + }; + static sceVu0FVECTOR ambient = { 0.50, 0.50, 0.50, 0.00 }; + char head[8] = "PS2D"; + char title[8] = "GTA3"; + + memset(&icon, 0, sizeof(icon)); + + memcpy(icon.BgColor, bgcolor, sizeof(bgcolor)); + memcpy(icon.LightDir, lightdir, sizeof(lightdir)); + memcpy(icon.LightColor, lightcol, sizeof(lightcol)); + memcpy(icon.Ambient, ambient, sizeof(ambient)); + + icon.OffsLF = 24; + icon.TransRate = 0x60; + + unsigned short *titleName = (unsigned short *)icon.TitleName; + + uint32 titlec = 0; + while ( titlec < strlen(title) ) + { + unsigned short sjis = Ascii2Sjis(title[titlec]); + titleName[titlec] = (sjis << 8) | (sjis >> 8); + titlec++; + } + + titleName[titlec] = L'\0'; + + char icon1[80]; + char icon2[80]; + char icon3[80]; + + strncpy(icon1, icon_one, sizeof(icon1) - 1); + strncpy(icon2, icon_two, sizeof(icon2) - 1); + strncpy(icon3, icon_three, sizeof(icon3) - 1); + + strncpy((char *)icon.FnameView, icon1, sizeof(icon.FnameView) - 1); + strncpy((char *)icon.FnameCopy, icon2, sizeof(icon.FnameCopy) - 1); + strncpy((char *)icon.FnameDel, icon3, sizeof(icon.FnameDel) - 1); + strncpy((char *)icon.Head, head, sizeof(icon.Head)); + + int32 iconFile = CreateMemCardFileReadWrite(Cards[cardID].port, "icon.sys"); + + if ( nError != NO_ERR_SUCCESS ) + return RES_FAILED; + + WritetoMemCard(iconFile, &icon, sizeof(icon)); + + if ( nError != NO_ERR_SUCCESS ) + return RES_FAILED; + + CloseMemCardFile(iconFile); + + if ( nError != NO_ERR_SUCCESS ) + return RES_FAILED; + + if ( LoadIconFiles(Cards[cardID].port, icon_one, icon_two, icon_three) == RES_SUCCESS ) + { + printf("All Icon files Created and loaded. \n"); + return RES_SUCCESS; + } + + printf("Could not load all the icon files \n"); + + return RES_FAILED; +#else + return RES_SUCCESS; +#endif +} + +int32 +CMemoryCard::LoadIconFiles(int32 cardID, char *icon_one, char *icon_two, char *icon_three) +{ +#if defined(PS2) + const uint32 size = 50968; + uint8 *data = new uint8[size]; + + char icon1_path[80]; + char icon2_path[80]; + char icon3_path[80]; + char icon1[32]; + char icon2[32]; + char icon3[32]; + + strncpy(icon1, icon_one, sizeof(icon1) - 1); + strncpy(icon2, icon_two, sizeof(icon2) - 1); + strncpy(icon3, icon_three, sizeof(icon3) - 1); + + int hostlen = strlen(HostFileLocationOfIcons); + + strncpy(icon1_path, HostFileLocationOfIcons, sizeof(icon1_path) - 1); + strncpy(icon2_path, HostFileLocationOfIcons, sizeof(icon2_path) - 1); + strncpy(icon3_path, HostFileLocationOfIcons, sizeof(icon3_path) - 1); + + strncpy(icon1_path+hostlen, icon_one, sizeof(icon1_path) - 1 - hostlen); + strncpy(icon2_path+hostlen, icon_two, sizeof(icon2_path) - 1 - hostlen); + strncpy(icon3_path+hostlen, icon_three, sizeof(icon3_path) - 1 - hostlen); + + // ico1 copy + int32 ico1file = CFileMgr::OpenFile(icon1_path); + CFileMgr::Read(ico1file, (char *)data, size); + CFileMgr::CloseFile(ico1file); + + int32 ico1mc = CreateMemCardFileReadWrite(Cards[cardID].port, icon1); + + if ( nError != NO_ERR_SUCCESS ) + { + delete [] data; + return RES_FAILED; + } + + WritetoMemCard(ico1mc, data, size); + + if ( nError != NO_ERR_SUCCESS ) + { + delete [] data; + return RES_FAILED; + } + + CloseMemCardFile(ico1mc); + + if ( nError != NO_ERR_SUCCESS ) + { + delete [] data; + return RES_FAILED; + } + + // ico2 copy + int32 ico2file = CFileMgr::OpenFile(icon2_path); + CFileMgr::Read(ico2file, (char *)data, size); + CFileMgr::CloseFile(ico2file); + + int32 ico2mc = CreateMemCardFileReadWrite(Cards[cardID].port, icon2); + + if ( nError != NO_ERR_SUCCESS ) + { + delete [] data; + return RES_FAILED; + } + + WritetoMemCard(ico2mc, data, size); + + if ( nError != NO_ERR_SUCCESS ) + { + delete [] data; + return RES_FAILED; + } + + CloseMemCardFile(ico2mc); + + if ( nError != NO_ERR_SUCCESS ) + { + delete [] data; + return RES_FAILED; + } + + // ico3 copy + int32 ico3file = CFileMgr::OpenFile(icon3_path); + CFileMgr::Read(ico3file, (char *)data, size); + CFileMgr::CloseFile(ico3file); + + int32 ico3mc = CreateMemCardFileReadWrite(Cards[cardID].port, icon3); + + if ( nError != NO_ERR_SUCCESS ) + { + delete [] data; + return RES_FAILED; + } + + WritetoMemCard(ico3mc, data, size); + + if ( nError != NO_ERR_SUCCESS ) + { + delete [] data; + return RES_FAILED; + } + + CloseMemCardFile(ico3mc); + + if ( nError != NO_ERR_SUCCESS ) + { + delete [] data; + return RES_FAILED; + } + + delete [] data; + + return RES_SUCCESS; +#else + return RES_SUCCESS; +#endif +} + +int32 +CMemoryCard::CloseMemCardFile(int32 file) +{ + CTimer::Stop(); + +#if defined(PS2) + int cmd = sceMcFuncNoClose; + + while ( sceMcClose(file) != sceMcResSucceed ) + ; + + int result; + sceMcSync(0, &cmd, &result); + + if ( result == sceMcResSucceed ) + { + printf("File %i closed\n", file); + + nError = NO_ERR_SUCCESS; + return nError; + } + + if ( result == sceMcResNoFormat ) + { + printf("Memory Card is Unformatted"); + + nError = ERR_NOFORMAT; + return nError; + } + + if ( result == sceMcResNoEntry ) + { + printf("Memory Card File Handle %i has not been opened", file); + + nError = ERR_OPENNOENTRY; + return nError; + } + + nError = ERR_NONE; + return nError; +#else + CFileMgr::CloseFile(file); + printf("File %i closed\n", file); + + nError = NO_ERR_SUCCESS; + return nError; +#endif +} + +int32 +CMemoryCard::CreateMemCardFileReadWrite(int32 cardID, char *filename) +{ + CTimer::Stop(); + +#if defined(PS2) + int cmd = sceMcFuncNoOpen; + + char buff[255]; + + strncpy(buff, filename, sizeof(buff)); + + while ( sceMcOpen(Cards[cardID].port, 0, buff, SCE_RDWR|SCE_CREAT) != sceMcResSucceed ) + ; + + int result; + sceMcSync(0, &cmd, &result); + + if ( result >= sceMcResSucceed ) + { + printf("%s File Created for MemoryCard. Its File handle is %i. \n", buff, result); + + nError = NO_ERR_SUCCESS; + return result; + } + + if ( result == sceMcResNoFormat ) + { + nError = ERR_NOFORMAT; + return nError; + } + + if ( result == sceMcResFullDevice ) + { + nError = ERR_FILEFULLDEVICE; + return nError; + } + + if ( result == sceMcResNoEntry ) + { + nError = ERR_FILENOPATHENTRY; + return nError; + } + + if ( result == sceMcResDeniedPermit ) + { + nError = ERR_FILEDENIED; + return nError; + } + + if ( result == sceMcResUpLimitHandle ) + { + nError = ERR_FILEUPLIMIT; + return nError; + } + + printf("File %s not created on memory card.\n", buff); + + return ERR_NONE; +#else + char path[512]; + sprintf(path, "%s\\%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], CardCurDir[cardID], filename); + int32 file = CFileMgr::OpenFile(path, "wb+"); + if (file == 0) + { + sprintf(path, "%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], filename); + file = CFileMgr::OpenFile(path, "wb+"); + } + + if ( file ) + { + printf("%s File Created for MemoryCard. Its File handle is %i. \n", filename, file); + nError = NO_ERR_SUCCESS; + return file; + } + + printf("File %s not created on memory card.\n", path); + + nError = ERR_NONE; + return 0; +#endif +} + +int32 +CMemoryCard::OpenMemCardFileForReading(int32 cardID, char *filename) +{ + CTimer::Stop(); + +#if defined(PS2) + int cmd = sceMcFuncNoOpen; + + char buff[255]; + + strncpy(buff, filename, sizeof(buff)); + + while ( sceMcOpen(Cards[cardID].port, 0, buff, SCE_RDONLY) != sceMcResSucceed ) + ; + + int result; + sceMcSync(0, &cmd, &result); + + if ( result >= sceMcResSucceed ) + { + printf("%s File Created for MemoryCard. Its File handle is %i. \n", buff, result); + + nError = NO_ERR_SUCCESS; + return result; + } + + if ( result == sceMcResNoFormat ) + { + nError = ERR_NOFORMAT; + return nError; + } + + if ( result == sceMcResFullDevice ) + { + nError = ERR_FILEFULLDEVICE; + return nError; + } + + if ( result == sceMcResNoEntry ) + { + nError = ERR_FILENOPATHENTRY; + return nError; + } + + if ( result == sceMcResDeniedPermit ) + { + nError = ERR_FILEDENIED; + return nError; + } + + if ( result == sceMcResUpLimitHandle ) + { + nError = ERR_FILEUPLIMIT; + return nError; + } + + printf("File %s not created on memory card.\n", buff); + nError = ERR_NONE; + return nError; +#else + char path[512]; + sprintf(path, "%s\\%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], CardCurDir[cardID], filename); + int32 file = CFileMgr::OpenFile(path, "rb"); + if (file == 0) + { + sprintf(path, "%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], filename); + file = CFileMgr::OpenFile(path, "rb"); + } + + if ( file ) + { + printf("%s File Created for MemoryCard. Its File handle is %i. \n", filename, file); + nError = NO_ERR_SUCCESS; + return file; + } + + printf("File %s not created on memory card.\n", path); + nError = ERR_NONE; + return 0; +#endif +} + +int32 +CMemoryCard::ReadFromMemCard(int32 file, void *buff, int32 size) +{ + CTimer::Stop(); + +#if defined(PS2) + int cmd = sceMcFuncNoRead; + + while ( sceMcRead(file, buff, size) != sceMcResSucceed ) + ; + + int result; + sceMcSync(0, &cmd, &result); + + if ( result >= sceMcResSucceed ) + { + if ( size >= result ) + { + printf("%i Bytes Read for Filehandle %i \n", result, file); + + nError = NO_ERR_SUCCESS; + return result; + } + } + + if ( result == sceMcResNoFormat ) + { + nError = ERR_NOFORMAT; + return nError; + } + + if ( result == sceMcResNoEntry ) + { + nError = ERR_READNOENTRY; + return nError; + } + + if ( result == sceMcResDeniedPermit ) + { + nError = ERR_READDENIED; + return nError; + } + + printf("No Bytes Read for Filehandle %i \n", file); + + nError = ERR_NONE; + return result; +#else + int32 s = CFileMgr::Read(file, (const char *)buff, size); + if ( s == size ) + { + printf("%i Bytes Read for Filehandle %i \n", s, file); + + nError = NO_ERR_SUCCESS; + return s; + } + + printf("No Bytes Read for Filehandle %i \n", file); + + nError = ERR_NONE; + return s; +#endif +} + +int32 +CMemoryCard::DeleteMemoryCardFile(int32 cardID, char *filename) +{ + CTimer::Stop(); + +#if defined(PS2) + int cmd = sceMcFuncNoDelete; + + while ( sceMcDelete(Cards[cardID].port, 0, filename) != sceMcResSucceed ) + ; + + int result; + sceMcSync(0, &cmd, &result); + + if ( result == sceMcResSucceed ) + { + printf("Memory Card %i, %s NO_ERR_SUCCESSfully deleted", cardID, filename); + + nError = NO_ERR_SUCCESS; + return nError; + } + + if ( result == sceMcResNoFormat ) + { + printf("Memory Card %i, %s not deleted as memory Card is unformatted", cardID, filename); + + nError = ERR_NOFORMAT; + return nError; + } + + if ( result == sceMcResNoEntry ) + { + printf("Memory Card %i, %s attempt made to delete non existing file", cardID, filename); + + nError = ERR_DELETENOENTRY; + return nError; + } + + if ( result == sceMcResDeniedPermit ) + { + printf("Memory Card %i, %s not deleted as file is in use or write protected", cardID, filename); + + nError = ERR_DELETEDENIED; + return nError; + } + + if ( result == sceMcResNotEmpty ) + { + printf("Memory Card %i, %s not deleted. Entries remain in subdirectory", cardID, filename); + + nError = ERR_DELETEFAILED; + return nError; + } + + nError = ERR_NONE; + return nError; +#else + char path[512]; + sprintf(path, "%s\\%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], CardCurDir[cardID], filename); + int32 file = CFileMgr::OpenFile(path, "rb"); + if (file == 0) + { + sprintf(path, "%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], filename); + file = CFileMgr::OpenFile(path, "rb"); + } + + if ( file ) + { + CFileMgr::CloseFile(file); + + DeleteFile(path); + + printf("Memory Card %i, %s NO_ERR_SUCCESSfully deleted", cardID, filename); + + nError = NO_ERR_SUCCESS; + return nError; + } + + printf("Memory Card %i, %s attempt made to delete non existing file", cardID, filename); + nError = ERR_DELETENOENTRY; + + //nError = ERR_NONE; + return nError; + +#endif +} + +void +CMemoryCard::PopulateErrorMessage() +{ + switch ( nError ) + { + case ERR_WRITEFULLDEVICE: + case ERR_DIRFULLDEVICE: + pErrorMsg = TheText.Get("SLONDR"); break; // Insufficient space to save. Please insert a Memory Card (PS2) with at least 500KB of free space available into MEMORY CARD slot 1. + case ERR_FORMATFAILED: + pErrorMsg = TheText.Get("SLONFM"); break; // Error formatting Memory Card (PS2) in MEMORY CARD slot 1. + case ERR_SAVEFAILED: + pErrorMsg = TheText.Get("SLNSP"); break; // Insufficient space to save. Please insert a Memory Card (PS2) with at least 200KB of free space available into MEMORY CARD slot 1. + case ERR_DELETEDENIED: + case ERR_NOFORMAT: + pErrorMsg = TheText.Get("SLONNF"); break; // Memory Card (PS2) in MEMORY CARD slot 1 is unformatted. + case ERR_NONE: + pErrorMsg = TheText.Get("SLONNO"); break; // No Memory Card (PS2) in MEMORY CARD slot 1. + } +} + +int32 +CMemoryCard::WritetoMemCard(int32 file, void *buff, int32 size) +{ +#if defined(PS2) + int cmd = sceMcFuncNoWrite; + int result = sceMcResSucceed; + int result1 = sceMcResSucceed; + + CTimer::Stop(); + + while ( sceMcWrite(file, buff, size) != sceMcResSucceed ) + ; + + sceMcSync(0, &cmd, &result); + + if ( result == sceMcResNoFormat ) + { + printf("No Bytes written for Filehandle %i \n", file); + + nError = ERR_NOFORMAT; + return nError; + } + + if ( result == sceMcResFullDevice ) + { + printf("No Bytes written for Filehandle %i \n", file); + + nError = ERR_WRITEFULLDEVICE; + return nError; + } + + if ( result == sceMcResNoEntry ) + { + printf("No Bytes written for Filehandle %i \n", file); + + nError = ERR_WRITENOENTRY; + return nError; + } + + if ( result == sceMcResDeniedPermit ) + { + printf("No Bytes written for Filehandle %i \n", file); + + nError = ERR_WRITEDENIED; + return nError; + } + + if ( result == sceMcResFailReplace ) + { + printf("No Bytes written for Filehandle %i \n", file); + + nError = ERR_WRITEFAILED; + return nError; + } + + if ( result <= -10 ) + { + printf("No Bytes written for Filehandle %i \n", file); + + nError = ERR_NONE; + return nError; + } + + cmd = sceMcFuncNoFlush; + + while ( sceMcFlush(file) != sceMcResSucceed ) + ; + + sceMcSync(0, &cmd, &result1); + + if ( result1 == sceMcResNoFormat ) + { + printf("No Bytes written for Filehandle %i \n", file); + + nError = ERR_NOFORMAT; + return nError; + } + + if ( result1 == sceMcResNoEntry ) + { + printf("No Bytes written for Filehandle %i \n", file); + + nError = ERR_FLUSHNOENTRY; + return nError; + } + + if ( result1 <= -10 ) + { + printf("No Bytes written for Filehandle %i \n", file); + + nError = ERR_NONE; + return nError; + } + + if ( result > 0 && result1 == sceMcResSucceed ) + { + printf("%i Bytes written for Filehandle %i \n", result, file); + } + else if ( result == sceMcResSucceed && result1 == sceMcResSucceed ) + { + printf("Filehandle %i was flushed\n", file); + } + + nError = NO_ERR_SUCCESS; + return nError; +#else + CTimer::Stop(); + + int32 s = CFileMgr::Write(file, (const char *)buff, size); + if ( s == size ) + { + printf("%i Bytes written for Filehandle %i \n", s, file); + nError = NO_ERR_SUCCESS; + return s; + } + + nError = ERR_NONE; + return s; +#endif +} + +static inline void +MakeSpaceForSizeInBufferPointer(uint8 *&presize, uint8 *&buf, uint8 *&postsize) +{ + presize = buf; + buf += sizeof(uint32); + postsize = buf; +} + +static inline void +CopySizeAndPreparePointer(uint8 *&buf, uint8 *&postbuf, uint8 *&postbuf2, uint32 &unused, uint32 &size) +{ + memcpy(buf, &size, sizeof(size)); + size = align4bytes(size); + postbuf2 += size; + postbuf = postbuf2; +} + +bool +CMemoryCard::SaveGame(void) +{ + uint32 saveSize = 0; + uint32 totalSize = 0; + + CurrentCard = CARD_ONE; + + CheckSum = 0; + + CGameLogic::PassTime(360); + CPlayerPed *ped = FindPlayerPed(); + ped->m_fCurrentStamina = ped->m_fMaxStamina; + CGame::TidyUpMemory(true, false); + + saveSize = SAVE_FILE_SIZE; + int32 minfree = 198; + + PopulateCardFlags(CurrentCard, false, false, true, false); + + if ( nError != NO_ERR_SUCCESS ) + return false; + + minfree += GetClusterAmountForFileCreation(CurrentCard); + + if ( nError != NO_ERR_SUCCESS ) + return false; + + if ( Cards[CurrentCard].free < 200 ) + { + CTimer::Update(); + uint32 startTime = CTimer::GetTimeInMillisecondsPauseMode(); + + while ( CTimer::GetTimeInMillisecondsPauseMode()-startTime < 1250 ) + { + for ( int32 i = 0; i < 1000; i++ ) + powf(3.33f, 3.444f); + + CTimer::Update(); + } + + nError = ERR_SAVEFAILED; + return false; + } + + if ( Cards[CurrentCard].free < minfree ) + { + CTimer::Update(); + uint32 startTime = CTimer::GetTimeInMillisecondsPauseMode(); + + while ( CTimer::GetTimeInMillisecondsPauseMode()-startTime < 1250 ) + { + for ( int32 i = 0; i < 1000; i++ ) + powf(3.33f, 3.444f); + + CTimer::Update(); + } + + nError = ERR_SAVEFAILED; + return false; + } + + uint32 size; + uint8 *buf = work_buff; + uint32 reserved = 0; + + int32 file = CreateMemCardFileReadWrite(CurrentCard, ValidSaveName); + + if ( nError != NO_ERR_SUCCESS ) + { + strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(SaveFileNameJustSaved) - 1); + return false; + } + + WriteDataToBufferPointer(buf, saveSize); + WriteDataToBufferPointer(buf, CGame::currLevel); + WriteDataToBufferPointer(buf, TheCamera.GetPosition().x); + WriteDataToBufferPointer(buf, TheCamera.GetPosition().y); + WriteDataToBufferPointer(buf, TheCamera.GetPosition().z); + WriteDataToBufferPointer(buf, CClock::ms_nMillisecondsPerGameMinute); + WriteDataToBufferPointer(buf, CClock::ms_nLastClockTick); + WriteDataToBufferPointer(buf, CClock::ms_nGameClockHours); + WriteDataToBufferPointer(buf, CClock::ms_nGameClockMinutes); + WriteDataToBufferPointer(buf, CPad::GetPad(0)->Mode); + WriteDataToBufferPointer(buf, CTimer::m_snTimeInMilliseconds); + WriteDataToBufferPointer(buf, CTimer::ms_fTimeScale); + WriteDataToBufferPointer(buf, CTimer::ms_fTimeStep); + WriteDataToBufferPointer(buf, CTimer::ms_fTimeStepNonClipped); + WriteDataToBufferPointer(buf, CTimer::m_FrameCounter); + WriteDataToBufferPointer(buf, CTimeStep::ms_fTimeStep); + WriteDataToBufferPointer(buf, CTimeStep::ms_fFramesPerUpdate); + WriteDataToBufferPointer(buf, CTimeStep::ms_fTimeScale); + WriteDataToBufferPointer(buf, CWeather::OldWeatherType); + WriteDataToBufferPointer(buf, CWeather::NewWeatherType); + WriteDataToBufferPointer(buf, CWeather::ForcedWeatherType); + WriteDataToBufferPointer(buf, CWeather::InterpolationValue); + WriteDataToBufferPointer(buf, CMenuManager::m_PrefsMusicVolume); + WriteDataToBufferPointer(buf, CMenuManager::m_PrefsSfxVolume); + WriteDataToBufferPointer(buf, CMenuManager::m_PrefsControllerConfig); + WriteDataToBufferPointer(buf, CMenuManager::m_PrefsUseVibration); + WriteDataToBufferPointer(buf, CMenuManager::m_PrefsStereoMono); + WriteDataToBufferPointer(buf, CMenuManager::m_PrefsRadioStation); + WriteDataToBufferPointer(buf, CMenuManager::m_PrefsBrightness); + WriteDataToBufferPointer(buf, CMenuManager::m_PrefsShowTrails); + WriteDataToBufferPointer(buf, CMenuManager::m_PrefsShowSubtitles); + WriteDataToBufferPointer(buf, CMenuManager::m_PrefsLanguage); + WriteDataToBufferPointer(buf, CMenuManager::m_PrefsUseWideScreen); + WriteDataToBufferPointer(buf, CPad::GetPad(0)->Mode); +#ifdef PS2 + WriteDataToBufferPointer(buf, BlurOn); +#else + WriteDataToBufferPointer(buf, CMBlur::BlurOn); +#endif + WriteDataToBufferPointer(buf, CompileDateAndTime.m_nSecond); + WriteDataToBufferPointer(buf, CompileDateAndTime.m_nMinute); + WriteDataToBufferPointer(buf, CompileDateAndTime.m_nHour); + WriteDataToBufferPointer(buf, CompileDateAndTime.m_nDay); + WriteDataToBufferPointer(buf, CompileDateAndTime.m_nMonth); + WriteDataToBufferPointer(buf, CompileDateAndTime.m_nYear); + WriteDataToBufferPointer(buf, CWeather::WeatherTypeInList); + WriteDataToBufferPointer(buf, TheCamera.CarZoomIndicator); + WriteDataToBufferPointer(buf, TheCamera.PedZoomIndicator); + + if ( nError != NO_ERR_SUCCESS ) + { + strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(SaveFileNameJustSaved) - 1); + return false; + } + + uint8 *presize; + uint8 *postsize; + + #define WriteSaveDataBlock(save_func)\ + do {\ + MakeSpaceForSizeInBufferPointer(presize, buf, postsize);\ + save_func(buf, &size);\ + CopySizeAndPreparePointer(presize, buf, postsize, reserved, size);\ + } while (0) + + WriteSaveDataBlock(CTheScripts::SaveAllScripts); + printf("Script Save Size %d, \n", size); + + WriteSaveDataBlock(CPools::SavePedPool); + printf("PedPool Save Size %d, \n", size); + + WriteSaveDataBlock(CGarages::Save); + printf("Garage Save Size %d, \n", size); + + WriteSaveDataBlock(CPools::SaveVehiclePool); + printf("Vehicle Save Size %d, \n", size); + + DoClassSaveRoutine(file, work_buff, buf - work_buff); + totalSize += buf - work_buff; + + if ( nError != NO_ERR_SUCCESS ) + return false; + + buf = work_buff; + reserved = 0; + + WriteSaveDataBlock(CPools::SaveObjectPool); + printf("Object Save Size %d, \n", size); + + WriteSaveDataBlock(ThePaths.Save); + printf("The Paths Save Size %d, \n", size); + + WriteSaveDataBlock(CCranes::Save); + printf("Cranes Save Size %d, \n", size); + + DoClassSaveRoutine(file, work_buff, buf - work_buff); + totalSize += buf - work_buff; + + if ( nError != NO_ERR_SUCCESS ) + return false; + + buf = work_buff; + reserved = 0; + + WriteSaveDataBlock(CPickups::Save); + printf("Pick Ups Save Size %d, \n", size); + + WriteSaveDataBlock(gPhoneInfo.Save); + printf("Phones Save Size %d, \n", size); + + WriteSaveDataBlock(CRestart::SaveAllRestartPoints); + printf("RestartPoints Save Size %d, \n", size); + + WriteSaveDataBlock(CRadar::SaveAllRadarBlips); + printf("Radar Save Size %d, \n", size); + + WriteSaveDataBlock(CTheZones::SaveAllZones); + printf("Save Size %d, \n", size); + + WriteSaveDataBlock(CGangs::SaveAllGangData); + printf("Gangs Save Size %d, \n", size); + + WriteSaveDataBlock(CTheCarGenerators::SaveAllCarGenerators); + printf("Car Gens Save Size %d, \n", size); + + WriteSaveDataBlock(CParticleObject::SaveParticle); + printf("Particles Save Size %d, \n", size); + + WriteSaveDataBlock(cAudioScriptObject::SaveAllAudioScriptObjects); + printf("Audio Script Save Size %d, \n", size); + + WriteSaveDataBlock(CWorld::Players[CWorld::PlayerInFocus].SavePlayerInfo); + printf("Player Info Save Size %d, \n", size); + + WriteSaveDataBlock(CStats::SaveStats); + printf("Stats Save Size %d, \n", size); + + WriteSaveDataBlock(CStreaming::MemoryCardSave); + printf("Streaming Save Size %d, \n", size); + + WriteSaveDataBlock(CPedType::Save); + printf("PedType Save Size %d, \n", size); + + DoClassSaveRoutine(file, work_buff, buf - work_buff); + totalSize += buf - work_buff; + + if ( nError != NO_ERR_SUCCESS ) + return false; + + buf = work_buff; + reserved = 0; + + for (int32 i = 0; i < 3; i++) + { + size = align4bytes(saveSize - totalSize - 4); + if (size > sizeof(work_buff)) + size = sizeof(work_buff); + if (size > 4) { + DoClassSaveRoutine(file, work_buff, size); + totalSize += size; + } + } + + WritetoMemCard(file, &CheckSum, sizeof(CheckSum)); + + CloseMemCardFile(file); + + #undef WriteSaveDataBlock + + if ( nError != NO_ERR_SUCCESS ) + { + strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(SaveFileNameJustSaved) - 1); + DoHackRoundSTUPIDSonyDateTimeStuff(CARD_ONE, ValidSaveName); + return false; + } + + DoHackRoundSTUPIDSonyDateTimeStuff(CARD_ONE, ValidSaveName); + strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(SaveFileNameJustSaved) - 1); + return true; +} + +bool +CMemoryCard::DoHackRoundSTUPIDSonyDateTimeStuff(int32 port, char *filename) +{ +#if defined(PS2) + int cmd = sceMcFuncNoFileInfo; + int result = sceMcResSucceed; + + sceCdCLOCK rtc; + sceCdReadClock(&rtc); + + sceScfGetLocalTimefromRTC(&rtc); + + #define ROUNDHACK(a) ( ((a) & 15) + ( ( ( ((a) >> 4) << 2 ) + ((a) >> 4) ) << 1 ) ) + + sceMcTblGetDir info; + + info._Create.Sec = ROUNDHACK(rtc.second); + info._Create.Min = ROUNDHACK(rtc.minute); + info._Create.Hour = ROUNDHACK(rtc.hour); + info._Create.Day = ROUNDHACK(rtc.day); + info._Create.Month = ROUNDHACK(rtc.month); + info._Create.Year = ROUNDHACK(rtc.year) + 2000; + + #undef ROUNDHACK + + while ( sceMcSetFileInfo(port, 0, filename, (char *)&info, sceMcFileInfoCreate) != sceMcResSucceed ) + ; + + sceMcSync(0, &cmd, &result); + + return sceMcResSucceed >= result; +#else + return true; +#endif +} + +int32 +CMemoryCard::LookForRootDirectory(int32 cardID) +{ + CTimer::Stop(); + +#if defined(PS2) + int cmd = sceMcFuncNoGetDir; + + while ( sceMcGetDir(Cards[cardID].port, 0, Cards[cardID].dir, 0, ARRAY_SIZE(Cards[cardID].table), Cards[cardID].table) != sceMcResSucceed ) + ; + + int result; + sceMcSync(0, &cmd, &result); + + if ( result == 0 ) + { + nError = NO_ERR_SUCCESS; + return ERR_NOROOTDIR; + } + + if ( result > sceMcResSucceed ) + { + printf("Memory card %i present PopulateFileTables function NO_ERR_SUCCESSfull \n", cardID); + + nError = NO_ERR_SUCCESS; + return nError; + } + + if ( result == sceMcResNoFormat ) + { + printf("Memory card %i PopulateFileTables function unNO_ERR_SUCCESSfull. MemoryCard not Formatted \n", cardID); + + nError = ERR_NOFORMAT; + return nError; + } + + if ( result == sceMcResNoEntry ) + { + printf("Memory card %i PopulateFileTables function unNO_ERR_SUCCESSfull. Path does not exist \n", cardID); + + nError = ERR_FILETABLENOENTRY; + return nError; + } + + printf("Memory card %i not Present\n", cardID); + + nError = ERR_NONE; + return nError; +#else + char path[512]; + sprintf(path, "%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], Cards[cardID].dir); + + memset(Cards[cardID].table, 0, sizeof(Cards[cardID].table)); + WIN32_FIND_DATA fd; HANDLE hFind; int32 num = 0; + if ( (hFind = FindFirstFile(path, &fd)) == INVALID_HANDLE_VALUE ) + { + nError = NO_ERR_SUCCESS; + return ERR_NOROOTDIR; + } + do + { + SYSTEMTIME st; + FileTimeToSystemTime(&fd.ftCreationTime, &st); + Cards[cardID].table[num]._Create.Sec = st.wSecond;Cards[cardID].table[num]._Create.Min = st.wMinute;Cards[cardID].table[num]._Create.Hour = st.wHour;Cards[cardID].table[num]._Create.Day = st.wDay;Cards[cardID].table[num]._Create.Month = st.wMonth;Cards[cardID].table[num]._Create.Year = st.wYear; + FileTimeToSystemTime(&fd.ftLastWriteTime, &st); + Cards[cardID].table[num]._Modify.Sec = st.wSecond;Cards[cardID].table[num]._Modify.Min = st.wMinute;Cards[cardID].table[num]._Modify.Hour = st.wHour;Cards[cardID].table[num]._Modify.Day = st.wDay;Cards[cardID].table[num]._Modify.Month = st.wMonth;Cards[cardID].table[num]._Modify.Year = st.wYear; + Cards[cardID].table[num].FileSizeByte = fd.nFileSizeLow; + strncpy((char *)Cards[cardID].table[num].EntryName, fd.cFileName, sizeof(Cards[cardID].table[num].EntryName) - 1); + num++; + } while( FindNextFile(hFind, &fd) && num < ARRAY_SIZE(Cards[cardID].table) ); + FindClose(hFind); + + if ( num == 0 ) + { + nError = NO_ERR_SUCCESS; + return ERR_NOROOTDIR; + } + + //todo errors + + printf("Memory card %i present PopulateFileTables function NO_ERR_SUCCESSfull \n", cardID); + + nError = NO_ERR_SUCCESS; + return nError; +#endif +} + +int32 +CMemoryCard::FillFirstFileWithGuff(int32 cardID) +{ + CTimer::Stop(); + + char buff[80]; + strncpy(buff, Cards[cardID].dir+1, sizeof(buff) - 1); + + int32 file = CreateMemCardFileReadWrite(Cards[cardID].port, buff); + + if ( nError != NO_ERR_SUCCESS ) + return RES_FAILED; + + const int32 kBlockSize = GUFF_FILE_SIZE / 3; + + work_buff[kBlockSize-1] = 5; + WritetoMemCard(file, work_buff, kBlockSize); + WritetoMemCard(file, work_buff, kBlockSize); + WritetoMemCard(file, work_buff, kBlockSize); + + if ( nError != NO_ERR_SUCCESS ) + return RES_FAILED; + + CloseMemCardFile(file); + + if ( nError != NO_ERR_SUCCESS ) + return RES_FAILED; + + return RES_SUCCESS; +} + +bool +CMemoryCard::FindMostRecentFileName(int32 cardID, char *filename) +{ + CDate date1, date2; + + CTimer::Stop(); + +#if defined(PS2) + int cmd = sceMcFuncNoGetDir; + + ClearFileTableBuffer(cardID); + + while ( sceMcGetDir(Cards[cardID].port, 0, "*", 0, ARRAY_SIZE(Cards[cardID].table), Cards[cardID].table) != sceMcResSucceed ) + ; + + int result; + sceMcSync(0, &cmd, &result); + + if ( result >= sceMcResSucceed ) + { + printf("Memory card %i present PopulateFileTables function NO_ERR_SUCCESSfull \n", cardID); + nError = NO_ERR_SUCCESS; + + for ( int32 entry = 7; entry < ARRAY_SIZE(Cards[CARD_ONE].table); entry++ ) + { + bool found = false; + + if ( Cards[CARD_ONE].table[entry]._Modify.Sec != 0 + || Cards[CARD_ONE].table[entry]._Modify.Min != 0 + || Cards[CARD_ONE].table[entry]._Modify.Hour != 0 + || Cards[CARD_ONE].table[entry]._Modify.Day != 0 + || Cards[CARD_ONE].table[entry]._Modify.Month != 0 + || Cards[CARD_ONE].table[entry]._Modify.Year != 0 ) + { + date1.m_nSecond = Cards[CARD_ONE].table[entry]._Modify.Sec; + date1.m_nMinute = Cards[CARD_ONE].table[entry]._Modify.Min; + date1.m_nHour = Cards[CARD_ONE].table[entry]._Modify.Hour; + date1.m_nDay = Cards[CARD_ONE].table[entry]._Modify.Day; + date1.m_nMonth = Cards[CARD_ONE].table[entry]._Modify.Month; + date1.m_nYear = Cards[CARD_ONE].table[entry]._Modify.Year; + + if ( Cards[CARD_ONE].table[entry].FileSizeByte != 0 + && Cards[CARD_ONE].table[entry].AttrFile & sceMcFileAttrClosed + && Cards[CARD_ONE].table[entry].FileSizeByte >= SAVE_FILE_SIZE ) + { + found = true; + } + } + else + if ( Cards[CARD_ONE].table[entry]._Create.Sec != 0 + || Cards[CARD_ONE].table[entry]._Create.Min != 0 + || Cards[CARD_ONE].table[entry]._Create.Hour != 0 + || Cards[CARD_ONE].table[entry]._Create.Day != 0 + || Cards[CARD_ONE].table[entry]._Create.Month != 0 + || Cards[CARD_ONE].table[entry]._Create.Year != 0 ) + { + date1.m_nSecond = Cards[CARD_ONE].table[entry]._Create.Sec; + date1.m_nMinute = Cards[CARD_ONE].table[entry]._Create.Min; + date1.m_nHour = Cards[CARD_ONE].table[entry]._Create.Hour; + date1.m_nDay = Cards[CARD_ONE].table[entry]._Create.Day; + date1.m_nMonth = Cards[CARD_ONE].table[entry]._Create.Month; + date1.m_nYear = Cards[CARD_ONE].table[entry]._Create.Year; + + if ( Cards[CARD_ONE].table[entry].FileSizeByte != 0 + && Cards[CARD_ONE].table[entry].AttrFile & sceMcFileAttrClosed + && Cards[CARD_ONE].table[entry].FileSizeByte >= SAVE_FILE_SIZE ) + { + found = true; + } + } + + if ( found ) + { + int32 d; + if ( date1 > date2 ) d = 1; + else if ( date1 < date2 ) d = 2; + else d = 0; + + if ( d == 1 ) + { + char *entryname = (char *)Cards[CARD_ONE].table[entry].EntryName; + + date2 = date1; + strncpy(filename, entryname, 28); + } + else + { + int32 d; + if ( date1 > date2 ) d = 1; + else if ( date1 < date2 ) d = 2; + else d = 0; + + if ( d == 0 ) + { + char *entryname = (char *)Cards[CARD_ONE].table[entry].EntryName; + date2 = date1; + strncpy(filename, entryname, 28); + } + } + } + } + + if ( date2.m_nSecond != 0 + || date2.m_nMinute != 0 + || date2.m_nHour != 0 + || date2.m_nDay != 0 + || date2.m_nMonth != 0 + || date2.m_nYear != 0 ) + { + return true; + } + + return false; + } + + if ( result == sceMcResNoFormat ) + { + printf("Memory card %i PopulateFileTables function unNO_ERR_SUCCESSfull. MemoryCard not Formatted \n", cardID); + nError = ERR_NOFORMAT; + return false; + } + + if ( result == sceMcResNoEntry ) + { + printf("Memory card %i PopulateFileTables function unNO_ERR_SUCCESSfull. Path does not exist \n", cardID); + nError = ERR_FILETABLENOENTRY; + return false; + } + + printf("Memory card %i not Present\n", cardID); + nError = ERR_NONE; + return false; +#else + ClearFileTableBuffer(cardID); + + char path[512]; + sprintf(path, "%s\\%s\\%s\\*", PCCardsPath, PCCardDir[cardID], CardCurDir[cardID]); + + memset(Cards[cardID].table, 0, sizeof(Cards[cardID].table)); + WIN32_FIND_DATA fd; HANDLE hFind; int32 num = 0; + if ( (hFind = FindFirstFile(path, &fd)) == INVALID_HANDLE_VALUE ) + { + printf("Memory card %i not Present\n", cardID); + nError = ERR_NONE; + return nError; + } + do + { + SYSTEMTIME st; + FileTimeToSystemTime(&fd.ftCreationTime, &st); + Cards[cardID].table[num]._Create.Sec = st.wSecond;Cards[cardID].table[num]._Create.Min = st.wMinute;Cards[cardID].table[num]._Create.Hour = st.wHour;Cards[cardID].table[num]._Create.Day = st.wDay;Cards[cardID].table[num]._Create.Month = st.wMonth;Cards[cardID].table[num]._Create.Year = st.wYear; + FileTimeToSystemTime(&fd.ftLastWriteTime, &st); + Cards[cardID].table[num]._Modify.Sec = st.wSecond;Cards[cardID].table[num]._Modify.Min = st.wMinute;Cards[cardID].table[num]._Modify.Hour = st.wHour;Cards[cardID].table[num]._Modify.Day = st.wDay;Cards[cardID].table[num]._Modify.Month = st.wMonth;Cards[cardID].table[num]._Modify.Year = st.wYear; + Cards[cardID].table[num].FileSizeByte = fd.nFileSizeLow; + strncpy((char *)Cards[cardID].table[num].EntryName, fd.cFileName, sizeof(Cards[cardID].table[num].EntryName) - 1); + num++; + } while( FindNextFile(hFind, &fd) && num < ARRAY_SIZE(Cards[cardID].table) ); + FindClose(hFind); + + if ( num > 0 ) + { + printf("Memory card %i present PopulateFileTables function NO_ERR_SUCCESSfull \n", cardID); + nError = NO_ERR_SUCCESS; + + for ( int32 entry = 0; entry < ARRAY_SIZE(Cards[CARD_ONE].table); entry++ ) + { + bool found = false; + + if ( Cards[CARD_ONE].table[entry]._Modify.Sec != 0 + || Cards[CARD_ONE].table[entry]._Modify.Min != 0 + || Cards[CARD_ONE].table[entry]._Modify.Hour != 0 + || Cards[CARD_ONE].table[entry]._Modify.Day != 0 + || Cards[CARD_ONE].table[entry]._Modify.Month != 0 + || Cards[CARD_ONE].table[entry]._Modify.Year != 0 ) + { + date1.m_nSecond = Cards[CARD_ONE].table[entry]._Modify.Sec; + date1.m_nMinute = Cards[CARD_ONE].table[entry]._Modify.Min; + date1.m_nHour = Cards[CARD_ONE].table[entry]._Modify.Hour; + date1.m_nDay = Cards[CARD_ONE].table[entry]._Modify.Day; + date1.m_nMonth = Cards[CARD_ONE].table[entry]._Modify.Month; + date1.m_nYear = Cards[CARD_ONE].table[entry]._Modify.Year; + + if ( Cards[CARD_ONE].table[entry].FileSizeByte != 0 + && Cards[CARD_ONE].table[entry].FileSizeByte >= SAVE_FILE_SIZE ) + { + found = true; + } + } + else + if ( Cards[CARD_ONE].table[entry]._Create.Sec != 0 + || Cards[CARD_ONE].table[entry]._Create.Min != 0 + || Cards[CARD_ONE].table[entry]._Create.Hour != 0 + || Cards[CARD_ONE].table[entry]._Create.Day != 0 + || Cards[CARD_ONE].table[entry]._Create.Month != 0 + || Cards[CARD_ONE].table[entry]._Create.Year != 0 ) + { + date1.m_nSecond = Cards[CARD_ONE].table[entry]._Create.Sec; + date1.m_nMinute = Cards[CARD_ONE].table[entry]._Create.Min; + date1.m_nHour = Cards[CARD_ONE].table[entry]._Create.Hour; + date1.m_nDay = Cards[CARD_ONE].table[entry]._Create.Day; + date1.m_nMonth = Cards[CARD_ONE].table[entry]._Create.Month; + date1.m_nYear = Cards[CARD_ONE].table[entry]._Create.Year; + + if ( Cards[CARD_ONE].table[entry].FileSizeByte != 0 + && Cards[CARD_ONE].table[entry].FileSizeByte >= SAVE_FILE_SIZE ) + { + found = true; + } + } + + if ( found ) + { + int32 d; + if ( date1 > date2 ) d = 1; + else if ( date1 < date2 ) d = 2; + else d = 0; + + if ( d == 1 ) + { + char *entryname = (char *)Cards[CARD_ONE].table[entry].EntryName; + + date2 = date1; + strncpy(filename, entryname, 28); + } + else + { + int32 d; + if ( date1 > date2 ) d = 1; + else if ( date1 < date2 ) d = 2; + else d = 0; + + if ( d == 0 ) + { + char *entryname = (char *)Cards[CARD_ONE].table[entry].EntryName; + date2 = date1; + strncpy(filename, entryname, 28); + } + } + } + } + + if ( date2.m_nSecond != 0 + || date2.m_nMinute != 0 + || date2.m_nHour != 0 + || date2.m_nDay != 0 + || date2.m_nMonth != 0 + || date2.m_nYear != 0 ) + { + return true; + } + + return false; + } + + //todo errors + + nError = ERR_NONE; + return false; +#endif +} + +void +CMemoryCard::ClearFileTableBuffer(int32 cardID) +{ + for ( int32 i = 0; i < ARRAY_SIZE(Cards[cardID].table); i++ ) + { + Cards[cardID].table[i].FileSizeByte = 0; + strncpy((char *)Cards[cardID].table[i].EntryName, " ", sizeof(Cards[cardID].table[i].EntryName) - 1); + } +} + +int32 +CMemoryCard::GetClusterAmountForFileCreation(int32 port) +{ +#if defined(PS2) + int cmd = sceMcFuncNoEntSpace; + int result = 0; + + CTimer::Stop(); + + while ( sceMcGetEntSpace(port, 0, TheGameRootDirectory) != sceMcResSucceed ) + ; + + sceMcSync(0, &cmd, &result); + + if ( result >= sceMcResSucceed ) + { + nError = NO_ERR_SUCCESS; + return result; + } + + if ( result == sceMcResNoFormat ) + { + nError = ERR_NOFORMAT; + return nError; + } + + nError = ERR_NONE; + return nError; +#else + CTimer::Stop(); + nError = NO_ERR_SUCCESS; + return 0; +#endif +} + +bool +CMemoryCard::DeleteEverythingInGameRoot(int32 cardID) +{ + CTimer::Stop(); + + ChangeDirectory(CurrentCard, Cards[CurrentCard].dir); + + if ( nError != NO_ERR_SUCCESS ) + return false; + + PopulateFileTable(cardID); + + for ( int32 i = ARRAY_SIZE(Cards[cardID].table) - 1; i >= 0; i--) + DeleteMemoryCardFile(cardID, (char *)Cards[cardID].table[i].EntryName); + + ChangeDirectory(CurrentCard, "/"); + + DeleteMemoryCardFile(cardID, Cards[CurrentCard].dir); + + if ( nError != NO_ERR_SUCCESS ) + return false; + + return true; +} + +int32 +CMemoryCard::CheckDataNotCorrupt(char *filename) +{ + CheckSum = 0; + + int32 lang = 0; + int32 level = 0; + + LastBlockSize = 0; + + char buf[100*4]; + + for ( int32 i = 0; i < sizeof(buf); i++ ) + buf[i] = '\0'; + + strncpy(buf, Cards[CurrentCard].dir, sizeof(buf) - 1); + strncat(buf, "/", sizeof(buf) - 1); + strcat (buf, filename); + + ChangeDirectory(CurrentCard, Cards[CurrentCard].dir); + + if ( nError != NO_ERR_SUCCESS ) + return RES_FAILED; + + int32 file = OpenMemCardFileForReading(CurrentCard, buf); + + if ( nError != NO_ERR_SUCCESS ) + return RES_FAILED; + + int32 bytes_processed = 0; + int32 blocknum = 0; + int32 lastblocksize; + + while ( SAVE_FILE_SIZE - sizeof(int32) > bytes_processed && blocknum < 8 ) + { + int32 size; + + ReadFromMemCard(file, &size, sizeof(size)); + + if ( nError != NO_ERR_SUCCESS ) + return RES_FAILED; + + lastblocksize = ReadFromMemCard(file, work_buff, align4bytes(size)); + + if ( nError != NO_ERR_SUCCESS ) + return RES_FAILED; + + uint8 sizebuff[4]; + memcpy(sizebuff, &size, sizeof(size)); + + for ( int32 i = 0; i < ARRAY_SIZE(sizebuff); i++ ) + CheckSum += sizebuff[i]; + + uint8 *pWork_buf = work_buff; + for ( int32 i = 0; i < lastblocksize; i++ ) + { + CheckSum += *pWork_buf++; + bytes_processed++; + } + + if ( blocknum == 0 ) + { + uint8 *pBuf = work_buff + sizeof(uint32); + ReadDataFromBufferPointer(pBuf, level); + pBuf += sizeof(uint32) * 29; + ReadDataFromBufferPointer(pBuf, lang); + } + + blocknum++; + } + + int32 checkSum; + ReadFromMemCard(file, &checkSum, sizeof(checkSum)); + CloseMemCardFile(file); + + if ( nError != NO_ERR_SUCCESS ) + return RES_FAILED; + + if ( CheckSum == checkSum ) + { + m_LevelToLoad = level; + m_LanguageToLoad = lang; + LastBlockSize = lastblocksize; + + return RES_SUCCESS; + } + + nError = ERR_DATACORRUPTED; + return RES_FAILED; +} + +int32 +CMemoryCard::GetLanguageToLoad(void) +{ + return m_LanguageToLoad; +} + +int32 +CMemoryCard::GetLevelToLoad(void) +{ + return m_LevelToLoad; +} + +bool +CMemoryCard::CreateGameDirectoryFromScratch(int32 cardID) +{ + TheMemoryCard.PopulateCardFlags(cardID, false, false, true, true); + + int32 err = RES_SUCCESS; + + if ( nError != NO_ERR_SUCCESS ) + return false; + + if ( Cards[CurrentCard].free < 500 ) + { + CTimer::Update(); + uint32 startTime = CTimer::GetTimeInMillisecondsPauseMode(); + + while ( CTimer::GetTimeInMillisecondsPauseMode()-startTime < 1250 ) + { + for ( int32 i = 0; i < 1000; i++ ) + powf(3.33f, 3.444f); + + CTimer::Update(); + } + + nError = ERR_DIRFULLDEVICE; + return false; + } + + TheMemoryCard.ChangeDirectory(CARD_ONE, "/"); + + if ( nError != NO_ERR_SUCCESS ) + return false; + + int32 r = LookForRootDirectory(CARD_ONE); + + if ( nError != NO_ERR_SUCCESS ) + return false; + + if ( r == ERR_NOROOTDIR ) + { + CreateRootDirectory(CARD_ONE); + + if ( nError != NO_ERR_SUCCESS ) + DeleteEverythingInGameRoot(CARD_ONE); + } + + ChangeDirectory(CARD_ONE, Cards[CARD_ONE].dir); + + if ( nError != NO_ERR_SUCCESS ) + return false; + + PopulateFileTable(CARD_ONE); + + if ( nError != NO_ERR_SUCCESS ) + return false; + +#if defined(PS2) + bool entryExist; + + entryExist = false; + if ( TheMemoryCard.PopulateFileTable(CARD_ONE) == NO_ERR_SUCCESS ) + { + for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[CARD_ONE].table); i++ ) + { + if ( !strcmp("icon.sys", (char *)TheMemoryCard.Cards[CARD_ONE].table[i].EntryName) ) + { + entryExist = true; + break; + } + } + } + + if ( !entryExist ) + err = RES_FAILED; + + if ( nError != NO_ERR_SUCCESS ) + return false; + + entryExist = false; + if ( TheMemoryCard.PopulateFileTable(CARD_ONE) == NO_ERR_SUCCESS ) + { + for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[CARD_ONE].table); i++ ) + { + if ( !strcmp(icon_one, (char *)TheMemoryCard.Cards[CARD_ONE].table[i].EntryName) ) + { + entryExist = true; + break; + } + } + } + + if ( !entryExist ) + err = RES_FAILED; + + if ( nError != NO_ERR_SUCCESS ) + return false; + + entryExist = false; + if ( TheMemoryCard.PopulateFileTable(CARD_ONE) == NO_ERR_SUCCESS ) + { + for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[CARD_ONE].table); i++ ) + { + if ( !strcmp(icon_two, (char *)TheMemoryCard.Cards[CARD_ONE].table[i].EntryName) ) + { + entryExist = true; + break; + } + } + } + + if ( !entryExist ) + err = RES_FAILED; + + if ( nError != NO_ERR_SUCCESS ) + return false; + + entryExist = false; + if ( TheMemoryCard.PopulateFileTable(CARD_ONE) == NO_ERR_SUCCESS ) + { + for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[CARD_ONE].table); i++ ) + { + if ( !strcmp(icon_three, (char *)TheMemoryCard.Cards[CARD_ONE].table[i].EntryName) ) + { + entryExist = true; + break; + } + } + } + + if ( !entryExist ) + err = RES_FAILED; + + if ( nError != NO_ERR_SUCCESS ) + return false; + + if ( err != RES_SUCCESS ) + { + int32 icon = CreateIconFiles(CARD_ONE, icon_one, icon_two, icon_three); + + if ( nError != NO_ERR_SUCCESS ) + return false; + + if ( icon != RES_SUCCESS ) + DeleteEverythingInGameRoot(CARD_ONE); + } +#endif + + int32 guff = FillFirstFileWithGuff(CARD_ONE); + + if ( nError != NO_ERR_SUCCESS ) + return false; + + if ( guff == RES_SUCCESS ) + { + printf("Game Default directory present"); + return true; + } + + DeleteEverythingInGameRoot(CARD_ONE); + + return false; +} + +bool +CMemoryCard::CheckGameDirectoryThere(int32 cardID) +{ + TheMemoryCard.PopulateCardFlags(cardID, false, false, true, true); + + if ( TheMemoryCard.nError != NO_ERR_SUCCESS ) + return false; + + TheMemoryCard.ChangeDirectory(cardID, Cards[CARD_ONE].dir); + + if ( TheMemoryCard.nError != NO_ERR_SUCCESS ) + return false; + + PopulateFileTable(cardID); + + if ( TheMemoryCard.nError != NO_ERR_SUCCESS ) + return false; + + + bool entryExist; + +#if defined(PS2) + entryExist = false; + if ( TheMemoryCard.PopulateFileTable(cardID) == NO_ERR_SUCCESS ) + { + for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[cardID].table); i++ ) + { + if ( !strcmp("icon.sys", (char *)TheMemoryCard.Cards[cardID].table[i].EntryName) ) + { + entryExist = true; + break; + } + } + } + + if ( !entryExist ) + return false; + + entryExist = false; + if ( TheMemoryCard.PopulateFileTable(cardID) == NO_ERR_SUCCESS ) + { + for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[cardID].table); i++ ) + { + if ( !strcmp(icon_one, (char *)TheMemoryCard.Cards[cardID].table[i].EntryName) ) + { + entryExist = true; + break; + } + } + } + + if ( !entryExist ) + return false; + + entryExist = false; + if ( TheMemoryCard.PopulateFileTable(cardID) == NO_ERR_SUCCESS ) + { + for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[cardID].table); i++ ) + { + if ( !strcmp(icon_two, (char *)TheMemoryCard.Cards[cardID].table[i].EntryName) ) + { + entryExist = true; + break; + } + } + } + + if ( !entryExist ) + return false; + + entryExist = false; + if ( TheMemoryCard.PopulateFileTable(cardID) == NO_ERR_SUCCESS ) + { + for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[cardID].table); i++ ) + { + if ( !strcmp(icon_three, (char *)TheMemoryCard.Cards[cardID].table[i].EntryName) ) + { + entryExist = true; + break; + } + } + } + + if ( !entryExist ) + return false; +#endif + + char buff[80]; + + strncpy(buff, Cards[cardID].dir+1, sizeof(buff) - 1); + + + entryExist = false; + if ( TheMemoryCard.PopulateFileTable(cardID) == NO_ERR_SUCCESS ) + { + for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[cardID].table); i++ ) + { + if ( !strcmp(buff, (char *)TheMemoryCard.Cards[cardID].table[i].EntryName) ) + { + entryExist = true; + break; + } + } + } + + if ( !entryExist ) + return false; + + printf("Game directory present"); + + return true; +} + +void +CMemoryCard::PopulateSlotInfo(int32 cardID) +{ + CTimer::Stop(); + + for ( int32 i = 0; i < MAX_SLOTS; i++ ) + { + Slots[i] = SLOT_NOTPRESENT; + + for ( int32 j = 0; j < ARRAY_SIZE(SlotFileName[i]); j++ ) + SlotFileName[i][j] = L'\0'; + + for ( int32 j = 0; j < ARRAY_SIZE(SlotSaveDate[i]); j++ ) + SlotSaveDate[i][j] = L'\0'; + + UnicodeStrcpy(SlotSaveDate[i], TheText.Get("DEFDT")); + } + + TheMemoryCard.PopulateCardFlags(cardID, false, false, true, true); + + if ( nError != NO_ERR_SUCCESS ) + return; + + TheMemoryCard.ChangeDirectory(cardID, TheMemoryCard.Cards[CARD_ONE].dir); + + if ( nError != NO_ERR_SUCCESS && nError != ERR_DIRNOENTRY ) + return; + + PopulateFileTable(cardID); + + if ( nError != NO_ERR_SUCCESS && nError != ERR_FILETABLENOENTRY ) + return; + + for ( int32 slot = 0; slot < MAX_SLOTS; slot++ ) + { +#if defined(PS2) + for ( int32 entry = 7; entry < ARRAY_SIZE(Cards[cardID].table); entry++ ) +#else + for ( int32 entry = 0; entry < ARRAY_SIZE(Cards[cardID].table); entry++ ) +#endif + { + if ( TheMemoryCard.Cards[CARD_ONE].table[entry].FileSizeByte != 0 ) + { + char slotnum[30]; + char slotname[30]; + char slotdate[30]; + + if ( +#if defined(PS2) + TheMemoryCard.Cards[CARD_ONE].table[entry].AttrFile & sceMcFileAttrClosed && +#endif + TheMemoryCard.Cards[CARD_ONE].table[entry].FileSizeByte >= SAVE_FILE_SIZE ) + { + char *entryname = (char *)Cards[cardID].table[entry].EntryName; + + bool bFound = false; +#if defined(PS2) + for ( int32 i = 7; i < ARRAY_SIZE(Cards[cardID].table) && !bFound; i++ ) +#else + for ( int32 i = 0; i < ARRAY_SIZE(Cards[cardID].table) && !bFound; i++ ) +#endif + { + sprintf(slotnum, "%i ", slot+1); + + for ( int32 j = 0; j < sizeof(slotname); j++ ) + slotname[j] = '\0'; + + strncat(slotname, slotnum, sizeof(slotnum)-1); + + if ( !strncmp(slotname, entryname, 1) ) + { + bFound = true; + + Slots[slot] = SLOT_PRESENT; + AsciiToUnicode(entryname, SlotFileName[slot]); + + int32 sec = Cards[CARD_ONE].table[entry]._Create.Sec; + int32 month = Cards[CARD_ONE].table[entry]._Create.Month; + int32 year = Cards[CARD_ONE].table[entry]._Create.Year; + int32 min = Cards[CARD_ONE].table[entry]._Create.Min; + int32 hour = Cards[CARD_ONE].table[entry]._Create.Hour; + int32 day = Cards[CARD_ONE].table[entry]._Create.Day; + + for ( int32 j = 0; j < ARRAY_SIZE(SlotSaveDate[slot]); j++ ) + SlotSaveDate[slot][j] = L'\0'; + + for ( int32 j = 0; j < ARRAY_SIZE(slotdate); j++ ) + slotdate[j] = '\0'; + + char *monthstr; + switch ( month ) + { + case 1: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("JAN")); break; + case 2: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("FEB")); break; + case 3: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("MAR")); break; + case 4: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("APR")); break; + case 5: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("MAY")); break; + case 6: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("JUN")); break; + case 7: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("JUL")); break; + case 8: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("AUG")); break; + case 9: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("SEP")); break; + case 10: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("OCT")); break; + case 11: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("NOV")); break; + case 12: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("DEC")); break; + } + + sprintf(slotdate, "%02d %s %04d %02d:%02d:%02d", day, monthstr, year, hour, min, sec); + AsciiToUnicode(slotdate, SlotSaveDate[slot]); + } + } + } + else + { + char *entryname = (char *)Cards[cardID].table[entry].EntryName; + + bool bFound = false; +#if defined(PS2) + for ( int32 i = 7; i < ARRAY_SIZE(Cards[cardID].table) && !bFound; i++ ) // again ... +#else + for ( int32 i = 0; i < ARRAY_SIZE(Cards[cardID].table) && !bFound; i++ ) // again ... +#endif + { + sprintf(slotnum, "%i ", slot+1); + + for ( int32 j = 0; j < sizeof(slotname); j++ ) + slotname[j] = '\0'; + + strncat(slotname, slotnum, sizeof(slotnum)-1); + + if ( !strncmp(slotname, entryname, 1) ) + { + bFound = true; + + Slots[slot] = SLOT_CORRUPTED; + AsciiToUnicode(entryname, SlotFileName[slot]); + } + } + } + } + } + } + + nError = NO_ERR_SUCCESS; + return; +} + +int32 +CMemoryCard::GetInfoOnSpecificSlot(int32 slotID) +{ + return Slots[slotID]; +} + +wchar * +CMemoryCard::GetDateAndTimeOfSavedGame(int32 slotID) +{ + return SlotSaveDate[slotID]; +} + +int32 +CMemoryCard::CheckCardStateAtGameStartUp(int32 cardID) +{ + CheckCardInserted(cardID); + if ( nError == ERR_NOFORMAT ) + return MCSTATE_OK; + if ( nError == ERR_NONE ) + return MCSTATE_NOCARD; + + if ( !CheckGameDirectoryThere(cardID) ) + { + if ( nError == ERR_NONE ) + return MCSTATE_NOCARD; + + DeleteEverythingInGameRoot(cardID); + if ( nError == ERR_NONE ) + return MCSTATE_NOCARD; + + TheMemoryCard.PopulateCardFlags(cardID, false, false, true, true); + if ( nError == ERR_NONE ) + return MCSTATE_NOCARD; + + if ( Cards[CurrentCard].free < 500 ) + return MCSTATE_NEED_500KB; + + return MCSTATE_OK; + } + + TheMemoryCard.CheckCardInserted(CARD_ONE); + + if ( nError == NO_ERR_SUCCESS ) + { + if ( TheMemoryCard.ChangeDirectory(CARD_ONE, Cards[CARD_ONE].dir) != ERR_NONE ) + { + if ( TheMemoryCard.FindMostRecentFileName(CARD_ONE, MostRecentFile) == true ) + { + if ( TheMemoryCard.CheckDataNotCorrupt(MostRecentFile) == RES_FAILED ) + { + TheMemoryCard.PopulateCardFlags(cardID, false, false, true, true); + if ( Cards[CurrentCard].free < 200 ) + return MCSTATE_NEED_200KB; + } + } + else + { + TheMemoryCard.PopulateCardFlags(cardID, false, false, true, true); + if ( Cards[CurrentCard].free < 200 ) + return MCSTATE_NEED_200KB; + } + } + } + + if ( TheMemoryCard.CheckCardInserted(CARD_ONE) != NO_ERR_SUCCESS ) + return MCSTATE_NOCARD; + + return MCSTATE_OK; +} + +void +CMemoryCard::SaveSlot(int32 slotID) +{ + bool bSave = true; + + for ( int32 j = 0; j < sizeof(ValidSaveName); j++ ) + ValidSaveName[j] = '\0'; + + char buff[100]; + + sprintf(buff, "%i ", slotID+1); + strncat(ValidSaveName, buff, sizeof(ValidSaveName) - 1); + + if ( CStats::LastMissionPassedName[0] != '\0' ) + { + char mission[100]; + + strcpy(mission, UnicodeToAsciiForMemoryCard(TheText.Get(CStats::LastMissionPassedName))); + +#ifdef FIX_BUGS + strncat(ValidSaveName, mission, sizeof(ValidSaveName)-1); +#else + strncat(ValidSaveName, mission, 21); + strncat(ValidSaveName, "...", strlen("...")); +#endif + } + + if ( !CheckGameDirectoryThere(CARD_ONE) ) + { + DeleteEverythingInGameRoot(CARD_ONE); + bSave = CreateGameDirectoryFromScratch(CARD_ONE); + } + + if ( bSave ) + { + if ( Slots[slotID] == SLOT_PRESENT ) + { + TheMemoryCard.ChangeDirectory(CARD_ONE, Cards[CurrentCard].dir); + if ( nError == NO_ERR_SUCCESS ) + TheMemoryCard.DeleteMemoryCardFile(CARD_ONE, UnicodeToAsciiForMemoryCard(SlotFileName[slotID])); + } + + SaveGame(); + } + + CTimer::Stop(); + CStreaming::FlushRequestList(); + CStreaming::DeleteRwObjectsAfterDeath(FindPlayerPed()->GetPosition()); + CStreaming::RemoveUnusedModelsInLoadedList(); + CGame::DrasticTidyUpMemory(false); + CTimer::Update(); +} + +void +CMemoryCard::DeleteSlot(int32 slotID) +{ + TheMemoryCard.ChangeDirectory(CARD_ONE, Cards[CurrentCard].dir); + + if ( nError == NO_ERR_SUCCESS ) + TheMemoryCard.DeleteMemoryCardFile(CARD_ONE, UnicodeToAsciiForMemoryCard(SlotFileName[slotID])); +} + +void +CMemoryCard::LoadSlotToBuffer(int32 slotID) +{ + CStreaming::DeleteAllRwObjects(); + + strcpy(LoadFileName, UnicodeToAsciiForMemoryCard(SlotFileName[slotID])); + + TheMemoryCard.ChangeDirectory(CARD_ONE, Cards[CurrentCard].dir); + + if ( nError == NO_ERR_SUCCESS ) + TheMemoryCard.CheckDataNotCorrupt(LoadFileName); +} + +wchar * +CMemoryCard::GetNameOfSavedGame(int32 slotID) +{ + return SlotFileName[slotID]; +} + +int32 +CMemoryCard::DoClassSaveRoutine(int32 file, uint8 *data, uint32 size) +{ + WritetoMemCard(file, &size, sizeof(size)); + + if ( nError != NO_ERR_SUCCESS ) + { + strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(ValidSaveName) - 1); + return ERR_NONE; + } + + WritetoMemCard(file, data, align4bytes(size)); + + uint8 sizebuff[4]; + memcpy(sizebuff, &size, sizeof(size)); + + for ( int32 i = 0; i < ARRAY_SIZE(sizebuff); i++ ) + CheckSum += sizebuff[i]; + + for ( int32 i = 0; i < align4bytes(size); i++ ) + CheckSum += *data++; + + if ( nError != NO_ERR_SUCCESS ) + { + strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(ValidSaveName) - 1); + return ERR_NONE; + } + + return nError; +} + +#endif diff --git a/src/miami/save/MemoryCard.h b/src/miami/save/MemoryCard.h new file mode 100644 index 00000000..bae605ff --- /dev/null +++ b/src/miami/save/MemoryCard.h @@ -0,0 +1,197 @@ +#pragma once +#include "common.h" +#ifdef PS2_MENU +#include "Date.h" + +#if defined(PS2) +#include +#include +#include +#endif + +enum +{ + CARD_ONE = 0, + CARD_TWO, + MAX_CARDS, +}; + +class CMemoryCardInfo +{ +public: + int port; + int slot; + int type; + int free; + int format; + char dir[40]; +#if defined(PS2) + sceMcTblGetDir table[15]; +#else + struct + { + typedef struct {unsigned char Sec,Min,Hour; unsigned char Day,Month; unsigned short Year;} _time; + _time _Create; + _time _Modify; + unsigned int FileSizeByte; + unsigned short AttrFile; + unsigned char EntryName[32]; + }table[15]; +#endif + CMemoryCardInfo(void); +}; + + +#define GUFF_FILE_SIZE 147096 +#define SAVE_FILE_SIZE 201729 + +class CMemoryCard +{ +public: + enum + { + MAX_SLOTS = 8, + }; + + enum MCSTATE + { + MCSTATE_OK = 0, + MCSTATE_NEED_500KB, + MCSTATE_NEED_200KB, + MCSTATE_NOCARD, + }; + + enum SLOTINFO + { + SLOT_PRESENT = 0, + SLOT_NOTPRESENT, + SLOT_CORRUPTED, + }; + + int _unk0; + int _unk1; + bool m_bWantToLoad; + bool JustLoadedDontFadeInYet; + bool StillToFadeOut; + bool b_FoundRecentSavedGameWantToLoad; + uint32 TimeStartedCountingForFade; + uint32 TimeToStayFadedBeforeFadeOut; + uint32 LastBlockSize; + bool _bunk2; + char ValidSaveName [30]; + char MostRecentFile [30]; + char _unkName3 [30]; + char SaveFileNameJustSaved[30]; + char _pad0[3]; + wchar *pErrorMsg; + char _unk4[32]; + bool _bunk5; + bool _bunk6; + bool _bunk7; + bool _bunk8; + int nError; + wchar _unk9[30]; + char LoadFileName[30]; + char _pad1[2]; + CDate CompileDateAndTime; + int m_LanguageToLoad; + int m_LevelToLoad; + int CurrentCard; + CMemoryCardInfo Cards [MAX_CARDS]; + int Slots [MAX_SLOTS]; + wchar SlotFileName[MAX_SLOTS][30]; + wchar SlotSaveDate[MAX_SLOTS][30]; + char _unk10[32]; + + enum + { + ERR_NONE = 0, + ERR_NOFORMAT = 1, + ERR_DIRNOENTRY = 2, + ERR_OPENNOENTRY = 3, + ERR_DELETENOENTRY = 4, + ERR_DELETEDENIED = 5, + ERR_DELETEFAILED = 6, + ERR_WRITEFULLDEVICE = 7, + ERR_WRITENOENTRY = 8, + ERR_WRITEDENIED = 9, + ERR_FLUSHNOENTRY, + ERR_WRITEFAILED, + ERR_FORMATFAILED = 12, + ERR_FILETABLENOENTRY = 13, + ERR_DIRFULLDEVICE = 14, + ERR_DIRBADENTRY = 15, + ERR_FILEFULLDEVICE = 16, + ERR_FILENOPATHENTRY = 17, + ERR_FILEDENIED = 18, + ERR_FILEUPLIMIT = 19, + ERR_READNOENTRY = 20, + ERR_READDENIED = 21, + ERR_LOADFAILED = 22, // unused + ERR_SAVEFAILED = 23, + ERR_DATACORRUPTED = 24, + ERR_NOROOTDIR = 25, + NO_ERR_SUCCESS = 26, + }; + + enum + { + RES_SUCCESS = 1, + RES_FAILED = -1, + }; + + int32 GetError() + { + return nError; + } + + wchar *GetErrorMessage() + { + return pErrorMsg; + } + + int32 Init(void); + CMemoryCard(void); + int32 RestoreForStartLoad(void); + int32 LoadSavedGame(void); + int32 CheckCardInserted(int32 cardID); + int32 PopulateCardFlags(int32 cardID, bool bSlotFlag, bool bTypeFlag, bool bFreeFlag, bool bFormatFlag); + int32 FormatCard(int32 cardID); + int32 PopulateFileTable(int32 cardID); + int32 CreateRootDirectory(int32 cardID); + int32 ChangeDirectory(int32 cardID, char *dir); + int32 CreateIconFiles(int32 cardID, char *icon_one, char *icon_two, char *icon_three); + int32 LoadIconFiles(int32 cardID, char *icon_one, char *icon_two, char *icon_three); + int32 CloseMemCardFile(int32 file); + int32 CreateMemCardFileReadWrite(int32 cardID, char *filename); + int32 OpenMemCardFileForReading(int32 cardID, char *filename); + int32 ReadFromMemCard(int32 file, void *buff, int32 size); + int32 DeleteMemoryCardFile(int32 cardID, char *filename); + void PopulateErrorMessage(); + int32 WritetoMemCard(int32 file, void *buff, int32 size); + bool SaveGame(void); + bool DoHackRoundSTUPIDSonyDateTimeStuff(int32 port, char *filename); + int32 LookForRootDirectory(int32 cardID); + int32 FillFirstFileWithGuff(int32 cardID); + bool FindMostRecentFileName(int32 cardID, char *filename); + void ClearFileTableBuffer(int32 cardID); + int32 GetClusterAmountForFileCreation(int32 port); + bool DeleteEverythingInGameRoot(int32 cardID); + int32 CheckDataNotCorrupt(char *filename); + int32 GetLanguageToLoad(void); + int32 GetLevelToLoad(void); + bool CreateGameDirectoryFromScratch(int32 cardID); + bool CheckGameDirectoryThere(int32 cardID); + void PopulateSlotInfo(int32 cardID); + int32 GetInfoOnSpecificSlot(int32 slotID); + wchar *GetDateAndTimeOfSavedGame(int32 slotID); + int32 CheckCardStateAtGameStartUp(int32 cardID); + void SaveSlot(int32 slotID); + void DeleteSlot(int32 slotID); + void LoadSlotToBuffer(int32 slotID); + wchar *GetNameOfSavedGame(int32 slotID); + int32 DoClassSaveRoutine(int32 file, uint8 *data, uint32 size); +}; + +extern CMemoryCard TheMemoryCard; +#endif \ No newline at end of file diff --git a/src/miami/save/PCSave.cpp b/src/miami/save/PCSave.cpp new file mode 100644 index 00000000..6d6fac90 --- /dev/null +++ b/src/miami/save/PCSave.cpp @@ -0,0 +1,252 @@ +#define WITHWINDOWS +#include "common.h" +#include "crossplatform.h" + +#include "FileMgr.h" +#include "Font.h" +#ifdef MORE_LANGUAGES +#include "Game.h" +#endif +#include "GenericGameStorage.h" +#include "Messages.h" +#include "PCSave.h" +#include "Text.h" + +#include "minilzo.h" +#include "main.h" + +#include "vmu/vmu.h" + +const char* _psGetUserFilesFolder(); + +C_PcSave PcSaveHelper; + +void +C_PcSave::SetSaveDirectory(const char *path) +{ + #if defined(RW_DC) + sprintf(DefaultPCSaveFileName, "%s/%s", path, "GTAVCsf"); + #else + sprintf(DefaultPCSaveFileName, "%s\\%s", path, "GTAVCsf"); + #endif +} + +bool +C_PcSave::DeleteSlot(int32 slot) +{ +#ifdef FIX_BUGS + char FileName[MAX_PATH]; +#else + char FileName[200]; +#endif + + PcSaveHelper.nErrorCode = SAVESTATUS_SUCCESSFUL; + sprintf(FileName, "%s%i.b", DefaultPCSaveFileName, slot + 1); + DeleteFile(FileName); + SlotSaveDate[slot][0] = '\0'; + return true; +} + +int8 +C_PcSave::SaveSlot(int32 slot) +{ + MakeValidSaveName(slot); + PcSaveHelper.nErrorCode = SAVESTATUS_SUCCESSFUL; + _psGetUserFilesFolder(); + int file = CFileMgr::OpenFile(ValidSaveName, "wb"); + if (file != 0) { +#ifdef MISSION_REPLAY + if (!IsQuickSave) +#endif + DoGameSpecificStuffBeforeSave(); + if (GenericSave(file)) { + if (!!CFileMgr::CloseFile(file)) { + nErrorCode = SAVESTATUS_ERR_SAVE_CLOSE; + return 2; + } + return 0; + } + + return 2; + } + PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_CREATE; + return 2; +} + +bool +C_PcSave::PcClassSaveRoutine(int32 file, uint8 *data, uint32 size) +{ + void* wrkmem = malloc(LZO1X_1_MEM_COMPRESS); + uint8* compressed = (uint8*)malloc(size*2); + lzo_uint compressed_size; + int crv = lzo1x_1_compress(data, size, compressed, &compressed_size, wrkmem); + free(wrkmem); + + if (crv == LZO_E_OK) { + uint32_t compressed_size32 = compressed_size | 0x80000000; + bool err = CFileMgr::Write(file, (const char*)&compressed_size32, sizeof(compressed_size32)) != sizeof(compressed_size32); + if (err || CFileMgr::GetErrorReadWrite(file)) { + free(compressed); + nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; + strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(ValidSaveName) - 1); + return false; + } + + err = CFileMgr::Write(file, (const char*)compressed, compressed_size) != compressed_size; + free(compressed); + if (err || CFileMgr::GetErrorReadWrite(file)) { + nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; + strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(ValidSaveName) - 1); + return false; + } + } else if (crv == LZO_E_NOT_COMPRESSIBLE) { + free(compressed); + uint32_t compressed_size32 = size; + bool err = CFileMgr::Write(file, (const char*)&compressed_size32, sizeof(compressed_size32)) != sizeof(compressed_size32); + if (err || CFileMgr::GetErrorReadWrite(file)) { + nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; + strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(ValidSaveName) - 1); + return false; + } + err = CFileMgr::Write(file, (const char*)data, align4bytes(size)) != align4bytes(size); + if (err || CFileMgr::GetErrorReadWrite(file)) { + nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; + strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(ValidSaveName) - 1); + return false; + } + } else { + free(compressed); + return false; + } + + CheckSum += (uint8) size; + CheckSum += (uint8) (size >> 8); + CheckSum += (uint8) (size >> 16); + CheckSum += (uint8) (size >> 24); + for (int i = 0; i < align4bytes(size); i++) { + CheckSum += *data++; + } + if (CFileMgr::GetErrorReadWrite(file)) { + nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; + strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(ValidSaveName) - 1); + return false; + } + + return true; +} + +uint32_t C_PcSave::PcClassLoadRoutine(int32 file, uint8 *data) { + uint32 size; + bool err = CFileMgr::Read(file, (char*)&size, sizeof(size)) != sizeof(size); + if (err) { + return 0; + } + + assert(data == work_buff); + + if (!(size & 0x80000000)) { + assert(align4bytes(size) == size); + err = CFileMgr::Read(file, (char*)data, align4bytes(size)) != align4bytes(size); + if (err || CFileMgr::GetErrorReadWrite(file)) { + return 0; + } + return size; + } else { + size &= ~0x80000000; + uint8* compressed = (uint8*)malloc(size); + err = CFileMgr::Read(file, (const char*)compressed, size) != size; + if (err || CFileMgr::GetErrorReadWrite(file)) { + free(compressed); + return 0; + } + + lzo_uint decompressed_size = 0; + auto crv = lzo1x_decompress(compressed, size, data, &decompressed_size, NULL); + free(compressed); + if (crv != LZO_E_OK) { + return 0; + } + + if (align4bytes(decompressed_size) != decompressed_size) { + return 0; + } + + return decompressed_size; + } +} + +void +C_PcSave::PopulateSlotInfo() +{ + for (int i = 0; i < SLOT_COUNT; i++) { + Slots[i] = SLOT_EMPTY; + SlotFileName[i][0] = '\0'; + SlotSaveDate[i][0] = '\0'; + } + for (int i = 0; i < SLOT_COUNT; i++) { +#ifdef FIX_BUGS + char savename[MAX_PATH]; +#else + char savename[52]; +#endif + struct header_t { + wchar FileName[24]; + SYSTEMTIME SaveDateTime; + } header; + sprintf(savename, "%s%i%s", i==7?"GTAVCSF":DefaultPCSaveFileName, i + 1, ".b"); + int file = CFileMgr::OpenFile(savename, "rb"); + if (file != 0) { + if (C_PcSave::PcClassLoadRoutine(file, (uint8*)work_buff)) { + header = *(header_t*)work_buff; + + Slots[i] = SLOT_OK; + memcpy(SlotFileName[i], &header.FileName, sizeof(header.FileName)); + + SlotFileName[i][24] = '\0'; + } + CFileMgr::CloseFile(file); + } + if (Slots[i] == SLOT_OK) { + if (CheckDataNotCorrupt(i, savename)) { +#ifdef FIX_INCOMPATIBLE_SAVES + if (!FixSave(i, GetSaveType(savename))) { + CMessages::InsertNumberInString(TheText.Get("FEC_SLC"), i + 1, -1, -1, -1, -1, -1, SlotFileName[i]); + Slots[i] = SLOT_CORRUPTED; + continue; + } +#endif + SYSTEMTIME st; + memcpy(&st, &header.SaveDateTime, sizeof(SYSTEMTIME)); + const char *month; + switch (st.wMonth) + { + case 1: month = "JAN"; break; + case 2: month = "FEB"; break; + case 3: month = "MAR"; break; + case 4: month = "APR"; break; + case 5: month = "MAY"; break; + case 6: month = "JUN"; break; + case 7: month = "JUL"; break; + case 8: month = "AUG"; break; + case 9: month = "SEP"; break; + case 10: month = "OCT"; break; + case 11: month = "NOV"; break; + case 12: month = "DEC"; break; + default: assert(0); + } + char date[70]; +#ifdef MORE_LANGUAGES + if (CGame::japaneseGame) + sprintf(date, "%02d %02d %04d %02d:%02d:%02d", st.wDay, st.wMonth, st.wYear, st.wHour, st.wMinute, st.wSecond); + else +#endif // MORE_LANGUAGES + sprintf(date, "%02d %s %04d %02d:%02d:%02d", st.wDay, UnicodeToAsciiForSaveLoad(TheText.Get(month)), st.wYear, st.wHour, st.wMinute, st.wSecond); + AsciiToUnicode(date, SlotSaveDate[i]); + + } else { + CMessages::InsertNumberInString(TheText.Get("FEC_SLC"), i + 1, -1, -1, -1, -1, -1, SlotFileName[i]); + Slots[i] = SLOT_CORRUPTED; + } + } + } +} \ No newline at end of file diff --git a/src/miami/save/PCSave.h b/src/miami/save/PCSave.h new file mode 100644 index 00000000..2205105b --- /dev/null +++ b/src/miami/save/PCSave.h @@ -0,0 +1,41 @@ +#pragma once + +enum eSaveStatus +{ + SAVESTATUS_SUCCESSFUL = 0, + SAVESTATUS_ERR_SAVE_CREATE, + SAVESTATUS_ERR_SAVE_WRITE, + SAVESTATUS_ERR_SAVE_CLOSE, + SAVESTATUS_ERR_LOAD_OPEN, + SAVESTATUS_ERR_LOAD_READ, + SAVESTATUS_ERR_LOAD_CLOSE, + SAVESTATUS_ERR_DATA_INVALID, + + // unused + SAVESTATUS_DELETEFAILED8, + SAVESTATUS_DELETEFAILED9, + SAVESTATUS_DELETEFAILED10, +}; + +enum +{ + SLOT_OK = 0, + SLOT_EMPTY, + SLOT_CORRUPTED +}; + +class C_PcSave +{ +public: + eSaveStatus nErrorCode; + + C_PcSave() : nErrorCode(SAVESTATUS_SUCCESSFUL) {} + void PopulateSlotInfo(); + bool DeleteSlot(int32 slot); + int8 SaveSlot(int32 slot); + bool PcClassSaveRoutine(int32 file, uint8 *data, uint32 size); + static uint32_t PcClassLoadRoutine(int32 file, uint8 *data); + static void SetSaveDirectory(const char *path); +}; + +extern C_PcSave PcSaveHelper; diff --git a/src/miami/save/SaveBuf.h b/src/miami/save/SaveBuf.h new file mode 100644 index 00000000..2a7fb315 --- /dev/null +++ b/src/miami/save/SaveBuf.h @@ -0,0 +1,123 @@ +#pragma once + +#ifdef VALIDATE_SAVE_SIZE +extern int32 _saveBufCount; +#define INITSAVEBUF _saveBufCount = 0; +#define VALIDATESAVEBUF(b) assert(_saveBufCount == b); +#else +#define INITSAVEBUF +#define VALIDATESAVEBUF(b) +#endif + +inline void +SkipSaveBuf(uint8 *&buf, int32 skip) +{ + buf += skip; +#ifdef VALIDATE_SAVE_SIZE + _saveBufCount += skip; +#endif +} + +inline void +SkipSaveBuf(uint8*& buf, uint32 &length, int32 skip) +{ + buf += skip; + length += skip; +#ifdef VALIDATE_SAVE_SIZE + _saveBufCount += skip; +#endif +} + +template +inline void +ReadSaveBuf(T *out, uint8 *&buf) +{ + memcpy(out, buf, sizeof(T)); + SkipSaveBuf(buf, sizeof(T)); +} + +template +inline void +ReadSaveBuf(T *out, uint8 *&buf, uint32 &length) +{ + memcpy(out, buf, sizeof(T)); + SkipSaveBuf(buf, length, sizeof(T)); +} + +template +inline T * +WriteSaveBuf(uint8 *&buf, const T &value) +{ + T *p = (T*)buf; + memcpy(p, &value, sizeof(T)); + SkipSaveBuf(buf, sizeof(T)); + return p; +} + +template +inline T * +WriteSaveBuf(uint8 *&buf, uint32 &length, const T &value) +{ + T *p = (T*)buf; + memcpy(p, &value, sizeof(T)); + SkipSaveBuf(buf, length, sizeof(T)); + return p; +} + +#ifdef COMPATIBLE_SAVES +inline void +ZeroSaveBuf(uint8 *&buf, uint32 length) +{ + memset(buf, 0, length); + SkipSaveBuf(buf, length); +} +#endif + +#define SAVE_HEADER_SIZE (4*sizeof(char)+sizeof(uint32)) + +#define WriteSaveHeader(buf,a,b,c,d,size) \ + WriteSaveBuf(buf, a);\ + WriteSaveBuf(buf, b);\ + WriteSaveBuf(buf, c);\ + WriteSaveBuf(buf, d);\ + WriteSaveBuf(buf, size); + +#define WriteSaveHeaderWithLength(buf,len,a,b,c,d,size) \ + WriteSaveBuf(buf, len, a);\ + WriteSaveBuf(buf, len, b);\ + WriteSaveBuf(buf, len, c);\ + WriteSaveBuf(buf, len, d);\ + WriteSaveBuf(buf, len, (uint32)(size)); + +#ifdef VALIDATE_SAVE_SIZE +#define CheckSaveHeader(buf, a, b, c, d, size) do { \ + char _c; uint32 _size;\ + ReadSaveBuf(&_c, buf);\ + assert(_c == a);\ + ReadSaveBuf(&_c, buf);\ + assert(_c == b);\ + ReadSaveBuf(&_c, buf);\ + assert(_c == c);\ + ReadSaveBuf(&_c, buf);\ + assert(_c == d);\ + ReadSaveBuf(&_size, buf);\ + assert(_size == size);\ + } while(0) + +#define CheckSaveHeaderWithLength(buf,len,a,b,c,d,size) do { \ + char _c; uint32 _size;\ + ReadSaveBuf(&_c, buf, len);\ + assert(_c == a);\ + ReadSaveBuf(&_c, buf, len);\ + assert(_c == b);\ + ReadSaveBuf(&_c, buf, len);\ + assert(_c == c);\ + ReadSaveBuf(&_c, buf, len);\ + assert(_c == d);\ + ReadSaveBuf(&_size, buf, len);\ + assert(_size == size);\ + } while(0) +#else +#define CheckSaveHeader(buf, a, b, c, d, size) SkipSaveBuf(buf, 8); +#define CheckSaveHeaderWithLength(buf, len, a, b, c, d, size) SkipSaveBuf(buf, 8); +#endif diff --git a/src/miami/skel/crossplatform.cpp b/src/miami/skel/crossplatform.cpp new file mode 100644 index 00000000..729ca163 --- /dev/null +++ b/src/miami/skel/crossplatform.cpp @@ -0,0 +1,453 @@ +#include "common.h" +#include "crossplatform.h" + +// Codes compatible with Windows and Linux +#ifndef _WIN32 + +// For internal use +// wMilliseconds is not needed +void tmToSystemTime(const tm *tm, SYSTEMTIME *out) { + out->wYear = tm->tm_year + 1900; + out->wMonth = tm->tm_mon + 1; + out->wDayOfWeek = tm->tm_wday; + out->wDay = tm->tm_mday; + out->wHour = tm->tm_hour; + out->wMinute = tm->tm_min; + out->wSecond = tm->tm_sec; +} + +void GetLocalTime_CP(SYSTEMTIME *out) { + time_t timestamp = time(nil); + tm *localTm = localtime(×tamp); + tmToSystemTime(localTm, out); +} +#endif + +// Compatible with Linux/POSIX and MinGW on Windows +#ifndef _WIN32 +HANDLE FindFirstFile(const char* pathname, WIN32_FIND_DATA* firstfile) { + char pathCopy[MAX_PATH]; + strcpy(pathCopy, pathname); + + char *folder = strtok(pathCopy, "*"); + char *extension = strtok(NULL, "*"); + + // because I remember like strtok might not return NULL for last delimiter + if (extension && extension - folder == strlen(pathname)) + extension = nil; + + // Case-sensitivity and backslashes... + // Will be freed at the bottom + char *realFolder = casepath(folder); + if (realFolder) { + folder = realFolder; + } + + strncpy(firstfile->folder, folder, sizeof(firstfile->folder)); + + if (extension) + strncpy(firstfile->extension, extension, sizeof(firstfile->extension)); + else + firstfile->extension[0] = '\0'; + + if (realFolder) + free(realFolder); + + HANDLE d; + if ((d = (HANDLE)opendir(firstfile->folder)) == NULL || !FindNextFile(d, firstfile)) { + if (d != NULL) { + closedir((DIR*)d); + } + return NULL; + } + + return d; +} + +bool FindNextFile(HANDLE d, WIN32_FIND_DATA* finddata) { + dirent *file; + static struct stat fileStats; + static char path[PATH_MAX], relativepath[NAME_MAX + sizeof(finddata->folder) + 1]; + int extensionLen = strlen(finddata->extension); + while ((file = readdir((DIR*)d)) != NULL) { + + // We only want "DT_REG"ular Files, but reportedly some FS and OSes gives DT_UNKNOWN as type. + if ((file->d_type == DT_UNKNOWN || file->d_type == DT_REG || file->d_type == DT_LNK) && + (extensionLen == 0 || strncasecmp(&file->d_name[strlen(file->d_name) - extensionLen], finddata->extension, extensionLen) == 0)) { + + sprintf(relativepath, "%s/%s", finddata->folder, file->d_name); + realpath(relativepath, path); + stat(path, &fileStats); + strncpy(finddata->cFileName, file->d_name, sizeof(finddata->cFileName)); + finddata->ftLastWriteTime = fileStats.st_mtime; + return true; + } + } + return false; +} + +void GetDateFormat(int unused1, int unused2, SYSTEMTIME* in, int unused3, char* out, int size) { + tm linuxTime; + linuxTime.tm_year = in->wYear - 1900; + linuxTime.tm_mon = in->wMonth - 1; + linuxTime.tm_wday = in->wDayOfWeek; + linuxTime.tm_mday = in->wDay; + linuxTime.tm_hour = in->wHour; + linuxTime.tm_min = in->wMinute; + linuxTime.tm_sec = in->wSecond; + // strftime(out, size, nl_langinfo(D_FMT), &linuxTime); + printf("TODO: FIXME %s\n",__func__); + strcpy(out,"abc"); +} + +void FileTimeToSystemTime(time_t* writeTime, SYSTEMTIME* out) { + tm *ptm = gmtime(writeTime); + tmToSystemTime(ptm, out); +} +#endif + +// Because wchar length differs between platforms. +wchar* +AllocUnicode(const char* src) +{ + wchar *dst = (wchar*)malloc(strlen(src)*2 + 2); + wchar *i = dst; + while((*i++ = (unsigned char)*src++) != '\0'); + return dst; +} + +// Funcs/features from Windows that we need on other platforms +#ifndef _WIN32 +char *strupr(char *s) { + char* tmp = s; + + for (;*tmp;++tmp) { + *tmp = toupper((unsigned char) *tmp); + } + + return s; +} +char *strlwr(char *s) { + char* tmp = s; + + for (;*tmp;++tmp) { + *tmp = tolower((unsigned char) *tmp); + } + + return s; +} + +char *trim(char *s) { + char *ptr; + if (!s) + return NULL; // handle NULL string + if (!*s) + return s; // handle empty string + for (ptr = s + strlen(s) - 1; (ptr >= s) && isspace(*ptr); --ptr); + ptr[1] = '\0'; + return s; +} + +FILE* _fcaseopen(char const* filename, char const* mode) +{ + FILE* result; + char* real = casepath(filename); + if (!real) + result = fopen(filename, mode); + else { + result = fopen(real, mode); + free(real); + } + return result; +} + +int _caserename(const char *old_filename, const char *new_filename) +{ + int result; + char *real_old = casepath(old_filename); + char *real_new = casepath(new_filename); + + // hack so we don't even try to rename it to new_filename if it already exists + if (!real_new) { + free(real_old); + return -1; + } + + if (!real_old) + result = rename(old_filename, real_new); + else + result = rename(real_old, real_new); + + free(real_old); + free(real_new); + + return result; +} + +// Case-insensitivity on linux (from https://github.com/OneSadCookie/fcaseopen) +// Returned string should freed manually (if exists) +char* casepath(char const* path, bool checkPathFirst) +{ + //TODO: Implement this + bool access_ok = false; //access(path, F_OK) != -1 + // printf("TODO: FIXME %s\n", __func__); + + if (checkPathFirst && access_ok ) { + // File path is correct + return nil; + } + + size_t l = strlen(path); + if (l > 2 && path[0] == '.' && (path[1] == '/' || path[1] == '\\')) { + // remove ./ from the start of the path + path += 2; + } + char* p = (char*)alloca(l + 1); + char* out = (char*)malloc(l + 3); // for extra ./ + strcpy(p, path); + + // my addon: linux doesn't handle filenames with spaces at the end nicely + p = trim(p); + + size_t rl = 0; + + DIR* d; + char* c; + + #if defined(__SWITCH__) || defined(PSP2) + if( (c = strstr(p, ":/")) != NULL) // scheme used by some environments, eg. switch, vita + { + size_t deviceNameOffset = c - p + 3; + char* deviceNamePath = (char*)alloca(deviceNameOffset + 1); + strlcpy(deviceNamePath, p, deviceNameOffset); + deviceNamePath[deviceNameOffset] = 0; + d = opendir(deviceNamePath); + p = c + 1; + } + else + #endif + if (p[0] == '/' || p[0] == '\\') + { + d = opendir("/"); + } + else + { + d = opendir("."); + out[0] = '.'; + out[1] = 0; + rl = 1; + } + + bool cantProceed = false; // just convert slashes in what's left in string, don't correct case of letters(because we can't) + bool mayBeTrailingSlash = false; + + while (c = strsep(&p, "/\\")) + { + // May be trailing slash(allow), slash at the start(avoid), or multiple slashes(avoid) + if (*c == '\0') + { + mayBeTrailingSlash = true; + continue; + } else { + mayBeTrailingSlash = false; + } + + out[rl] = '/'; + rl += 1; + out[rl] = 0; + + if (cantProceed) + { + strcpy(out + rl, c); + rl += strlen(c); + continue; + } + + struct dirent* e; + while (e = readdir(d)) + { + if (strcasecmp(c, e->d_name) == 0) + { + strcpy(out + rl, e->d_name); + int reportedLen = (int)strlen(e->d_name); + rl += reportedLen; + assert(reportedLen == strlen(c) && "casepath: This is not good at all"); + + closedir(d); + d = opendir(out); + + // Either it wasn't a folder, or permission error, I/O error etc. + if (!d) { + cantProceed = true; + } + + break; + } + } + + if (!e) + { + printf("casepath couldn't find dir/file \"%s\", full path was %s\n", c, path); + // No match, add original name and continue converting further slashes. + strcpy(out + rl, c); + rl += strlen(c); + cantProceed = true; + } + } + + if (d) closedir(d); + if (mayBeTrailingSlash) { + out[rl] = '/'; rl += 1; + out[rl] = '\0'; + } + + if (rl > l + 2) { + printf("\n\ncasepath: Corrected path length is longer then original+2:\n\tOriginal: %s (%zu chars)\n\tCorrected: %s (%zu chars)\n\n", path, l, out, rl); + } + return out; +} +#endif + +#if !defined(_MSC_VER) && !defined(__CWCC__) +char *strdate(char *buf) { + time_t timestamp; + time(×tamp); + tm *localTm = localtime(×tamp); + strftime(buf, 10, "%m/%d/%y", localTm); + return buf; +} + +char *_strdate(char *buf) { + return strdate(buf); +} +#endif + +#ifdef __SWITCH__ +/* Taken from glibc */ +char *realpath(const char *name, char *resolved) +{ + char *rpath, *dest = NULL; + const char *start, *end, *rpath_limit; + long int path_max; + + /* As per Single Unix Specification V2 we must return an error if + either parameter is a null pointer. We extend this to allow + the RESOLVED parameter to be NULL in case the we are expected to + allocate the room for the return value. */ + if (!name) + return NULL; + + /* As per Single Unix Specification V2 we must return an error if + the name argument points to an empty string. */ + if (name[0] == '\0') + return NULL; + +#ifdef PATH_MAX + path_max = PATH_MAX; +#else + path_max = pathconf(name, _PC_PATH_MAX); + if (path_max <= 0) + path_max = 1024; +#endif + + if (!resolved) + { + rpath = (char*)malloc(path_max); + if (!rpath) + return NULL; + } + else + rpath = resolved; + rpath_limit = rpath + path_max; + + if (name[0] != '/') + { + if (!getcwd(rpath, path_max)) + { + rpath[0] = '\0'; + goto error; + } + dest = (char*)memchr(rpath, '\0', path_max); + } + else + { + rpath[0] = '/'; + dest = rpath + 1; + } + + for (start = end = name; *start; start = end) + { + /* Skip sequence of multiple path-separators. */ + while (*start == '/') + ++start; + + /* Find end of path component. */ + for (end = start; *end && *end != '/'; ++end) + /* Nothing. */; + + if (end - start == 0) + break; + else if (end - start == 1 && start[0] == '.') + /* nothing */; + else if (end - start == 2 && start[0] == '.' && start[1] == '.') + { + /* Back up to previous component, ignore if at root already. */ + if (dest > rpath + 1) + while ((--dest)[-1] != '/') + ; + } + else + { + size_t new_size; + + if (dest[-1] != '/') + *dest++ = '/'; + + if (dest + (end - start) >= rpath_limit) + { + ptrdiff_t dest_offset = dest - rpath; + char *new_rpath; + + if (resolved) + { + if (dest > rpath + 1) + dest--; + *dest = '\0'; + goto error; + } + new_size = rpath_limit - rpath; + if (end - start + 1 > path_max) + new_size += end - start + 1; + else + new_size += path_max; + new_rpath = (char *)realloc(rpath, new_size); + if (!new_rpath) + goto error; + rpath = new_rpath; + rpath_limit = rpath + new_size; + + dest = rpath + dest_offset; + } + + dest = (char*)memcpy(dest, start, end - start); + *dest = '\0'; + } + } + if (dest > rpath + 1 && dest[-1] == '/') + --dest; + *dest = '\0'; + + return rpath; + +error: + if (!resolved) + free(rpath); + return NULL; +} + +ssize_t readlink (const char * __path, char * __buf, size_t __buflen) +{ + errno = ENOSYS; + return -1; +} +#endif diff --git a/src/miami/skel/crossplatform.h b/src/miami/skel/crossplatform.h new file mode 100644 index 00000000..1ceb8ceb --- /dev/null +++ b/src/miami/skel/crossplatform.h @@ -0,0 +1,207 @@ +#include +#include + +// This is the common include for platform/renderer specific skeletons(glfw.cpp, win.cpp etc.) and using cross platform things (like Windows directories wrapper, platform specific global arrays etc.) +// Functions that's different on glfw and win but have same signature, should be located on platform.h. + +enum eWinVersion +{ + OS_WIN95 = 0, + OS_WIN98, + OS_WINNT, + OS_WIN2000, + OS_WINXP, +}; + +#if !defined(_MSC_VER) && !defined(__CWCC__) +char *_strdate(char *buf); +#endif + +#ifdef _WIN32 + +// As long as WITHWINDOWS isn't defined / isn't included, we only need type definitions so let's include . +// NOTE: It's perfectly fine to include here, but it can increase build size and time in *some* conditions, and maybe substantially in future if we'll use crossplatform.h more. +#ifndef _INC_WINDOWS + #ifndef __MWERKS__ + // #include // DreamSDK doesn't have IntSafe.h + #include + #else + #include + #endif +#endif +#if defined RW_D3D9 || defined RWLIBS +#include "win.h" +#endif +extern DWORD _dwOperatingSystemVersion; +#define fcaseopen fopen +#define caserename rename +#else +char *strupr(char *str); +char *strlwr(char *str); + +enum { + LANG_OTHER, + LANG_GERMAN, + LANG_FRENCH, + LANG_ENGLISH, + LANG_ITALIAN, + LANG_SPANISH, +}; + +enum { + SUBLANG_OTHER, + SUBLANG_ENGLISH_AUS +}; + +extern long _dwOperatingSystemVersion; +char *casepath(char const *path, bool checkPathFirst = true); +FILE *_fcaseopen(char const *filename, char const *mode); +#define fcaseopen _fcaseopen +int _caserename(const char *old_filename, const char *new_filename); +#define caserename _caserename +#endif + +#ifdef RW_GL3 +typedef struct +{ + GLFWwindow* window; + RwBool fullScreen; + RwV2d lastMousePos; + double mouseWheel; // glfw doesn't cache it + bool cursorIsInWindow; + RwInt8 joy1id; + RwInt8 joy2id; +} +psGlobalType; + +#define PSGLOBAL(var) (((psGlobalType *)(RsGlobal.ps))->var) + +void CapturePad(RwInt32 padID); +void joysChangeCB(int jid, int event); +#endif + +#ifdef RW_DC +typedef struct +{ + RwBool fullScreen; + RwV2d lastMousePos; + double mouseWheel; // glfw doesn't cache it + bool cursorIsInWindow; + RwInt8 joy1id; + RwInt8 joy2id; +} +psGlobalType; + +#define PSGLOBAL(var) (((psGlobalType *)(RsGlobal.ps))->var) + +void CapturePad(RwInt32 padID); +void joysChangeCB(int jid, int event); +#endif + +#ifdef DETECT_JOYSTICK_MENU +extern char gSelectedJoystickName[128]; +#endif + +enum eGameState +{ + GS_START_UP = 0, + GS_INIT_LOGO_MPEG, + GS_LOGO_MPEG, + GS_INIT_INTRO_MPEG, + GS_INTRO_MPEG, + GS_INIT_ONCE, + GS_INIT_FRONTEND, + GS_FRONTEND, + GS_INIT_PLAYING_GAME, + GS_PLAYING_GAME, +}; +extern RwUInt32 gGameState; + +RwBool IsForegroundApp(); + +#ifndef MAX_PATH + #if !defined _WIN32 || defined __MINGW32__ + #define MAX_PATH PATH_MAX + #else + #define MAX_PATH 260 + #endif +#endif + +// Codes compatible with Windows and Linux +#ifndef _WIN32 +#define DeleteFile unlink + +// Needed for save games +struct SYSTEMTIME { + RwUInt16 wYear; + RwUInt16 wMonth; + RwUInt16 wDayOfWeek; + RwUInt16 wDay; + RwUInt16 wHour; + RwUInt16 wMinute; + RwUInt16 wSecond; + RwUInt16 wMilliseconds; +}; + +void GetLocalTime_CP(SYSTEMTIME* out); +#define GetLocalTime GetLocalTime_CP +#define OutputDebugString(s) re3_debug("[DBG-2]: %s\n",s) +#endif + +// Compatible with Linux/POSIX and MinGW on Windows +#ifndef _WIN32 +#include +#include +#include +#include +// #include +#include + +typedef void* HANDLE; +#define INVALID_HANDLE_VALUE NULL +#define FindClose(h) \ + do { \ + if (h != nil) \ + closedir((DIR*)h); \ + } while(0) + +#define LOCALE_USER_DEFAULT 0 +#define DATE_SHORTDATE 0 + +struct WIN32_FIND_DATA { + char extension[32]; // for searching + char folder[MAX_PATH]; // for searching + char cFileName[256]; // because tSkinInfo has it 256 + time_t ftLastWriteTime; +}; + +HANDLE FindFirstFile(const char*, WIN32_FIND_DATA*); +bool FindNextFile(HANDLE, WIN32_FIND_DATA*); +void FileTimeToSystemTime(time_t*, SYSTEMTIME*); +void GetDateFormat(int, int, SYSTEMTIME*, int, char*, int); +#endif + +#ifdef __SWITCH__ + +// tweak glfw values for switch to match expected pc bindings +#ifdef GLFW_GAMEPAD_BUTTON_A + #undef GLFW_GAMEPAD_BUTTON_A +#endif +#define GLFW_GAMEPAD_BUTTON_A 1 + +#ifdef GLFW_GAMEPAD_BUTTON_B + #undef GLFW_GAMEPAD_BUTTON_B +#endif +#define GLFW_GAMEPAD_BUTTON_B 0 + +#ifdef GLFW_GAMEPAD_BUTTON_X + #undef GLFW_GAMEPAD_BUTTON_X +#endif +#define GLFW_GAMEPAD_BUTTON_X 3 + +#ifdef GLFW_GAMEPAD_BUTTON_Y + #undef GLFW_GAMEPAD_BUTTON_Y +#endif +#define GLFW_GAMEPAD_BUTTON_Y 2 + +#endif diff --git a/src/miami/skel/dc/dc.cpp b/src/miami/skel/dc/dc.cpp new file mode 100644 index 00000000..5868c3bd --- /dev/null +++ b/src/miami/skel/dc/dc.cpp @@ -0,0 +1,2879 @@ +#if defined RW_DC + +#include "vmu/vmu.h" +#include +#include +#include + +#if !defined(DC_SIM) +#include +KOS_INIT_FLAGS(INIT_IRQ | INIT_CONTROLLER | INIT_CDROM | INIT_VMU); +#include "../prof/profiler.h" +#endif + +#ifdef _WIN32 +#include +#include +#include +#include +#include +#include + +DWORD _dwOperatingSystemVersion; +#include "resource.h" +#else +long _dwOperatingSystemVersion; +#ifndef __SWITCH__ +#ifndef __APPLE__ +// #include +#else +#include +#include +#endif +#endif +#include +#include +#include +#include +#endif + +#include "common.h" +#if (defined(_MSC_VER)) +#include +#endif /* (defined(_MSC_VER)) */ +#include +#include "rwcore.h" +#include "skeleton.h" +#include "platform.h" +#include "crossplatform.h" + +#include "main.h" +#include "FileMgr.h" +#include "Text.h" +#include "Pad.h" +#include "Timer.h" +#include "DMAudio.h" +#include "ControllerConfig.h" +#include "Frontend.h" +#include "Game.h" +#include "PCSave.h" +#include "MemoryCard.h" +#include "Sprite2d.h" +#include "AnimViewer.h" +#include "Font.h" +#include "MemoryMgr.h" +#include "git-version.h" +#include "dc.h" + +#include + +// This is defined on project-level, via premake5 or cmake +#ifdef GET_KEYBOARD_INPUT_FROM_X11 +#include +#include +#define GLFW_EXPOSE_NATIVE_X11 +#include +#endif + +#ifdef _WIN32 +#define GLFW_EXPOSE_NATIVE_WIN32 +#include +#endif + +#include + +#if !defined(DC_SIM) +# if defined(WITH_IDE) +#include +#include +#include +# endif + +#include +#include +#endif + +// //TODO: these are somehow missing +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC ((clockid_t) 4) +#endif +// #define CLOCK_MONOTONIC_RAW ((clockid_t) 5) +// #define CLOCK_MONOTONIC_COARSE ((clockid_t) 6) + + +#define MAX_SUBSYSTEMS (16) + +rw::EngineOpenParams openParams; + +static RwBool ForegroundApp = TRUE; +static RwBool WindowIconified = FALSE; +static RwBool WindowFocused = TRUE; + +static RwBool RwInitialised = FALSE; + +static RwSubSystemInfo GsubSysInfo[MAX_SUBSYSTEMS]; +static RwInt32 GnumSubSystems = 0; +static RwInt32 GcurSel = 0, GcurSelVM = 0; + +static RwBool useDefault; + +// What is that for anyway? +#ifndef IMPROVED_VIDEOMODE +static RwBool defaultFullscreenRes = TRUE; +#else +static RwBool defaultFullscreenRes = FALSE; +static RwInt32 bestWndMode = -1; +#endif + +static psGlobalType PsGlobal; + + +#define PSGLOBAL(var) (((psGlobalType *)(RsGlobal.ps))->var) + +size_t _dwMemAvailPhys; +RwUInt32 gGameState; + +#ifdef DETECT_JOYSTICK_MENU +char gSelectedJoystickName[128] = ""; +#endif + + +#if !defined(DC_SIM) && defined(WITH_IDE) +static kos_blockdev_t ide_rv; + +static void ide_fat_init(void) +{ + uint8_t type; + int err; + + err = g1_ata_init(); + if (err) + return; + + err = g1_ata_blockdev_for_partition(0, 1, &ide_rv, &type); + if (err) + return; + + printf("Found IDE partition 0\n"); + + err = fs_fat_init(); + if (err) + return; + + err = fs_fat_mount("/ide", &ide_rv, FS_FAT_MOUNT_READONLY); + if (err) + return; + + printf("Mounted IDE partition 0 to /ide\n"); +} +#endif + +#if !defined(DC_SIM) +#if defined(WITH_PROF) +static bool profiling = false; +#endif +#if defined(WITH_SD) +static kos_blockdev_t sd_rv; + +static void sd_fat_init(void) +{ + uint8_t type; + int err; + + err = sd_init(); + if (err) + return; + + err = sd_blockdev_for_partition(0, &sd_rv, &type); + if (err) + return; + + printf("Found SD partition 0\n"); + + err = fs_fat_init(); + if (err) + return; + + err = fs_fat_mount("/sd", &sd_rv, FS_FAT_MOUNT_READWRITE); + if (err) + return; + + printf("Mounted SD partition 0 to /sd\n"); +} +#endif +#endif + +/* + ***************************************************************************** + */ +void _psCreateFolder(const char *path) +{ +#ifdef _WIN32 + HANDLE hfle = CreateFile(path, GENERIC_READ, + FILE_SHARE_READ, + nil, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL, + nil); + + if ( hfle == INVALID_HANDLE_VALUE ) + CreateDirectory(path, nil); + else + CloseHandle(hfle); +#else + struct stat info; + char fullpath[PATH_MAX]; + realpath(path, fullpath); + + if (stat(fullpath, &info) != 0) { + if (errno == ENOENT || (errno != EACCES && !S_ISDIR(info.st_mode))) { + mkdir(fullpath, 0755); + } + } +#endif +} + +/* + ***************************************************************************** + */ +const char *_psGetUserFilesFolder() +{ +#if defined USE_MY_DOCUMENTS && defined _WIN32 + HKEY hKey = NULL; + + static CHAR szUserFiles[256]; + + if ( RegOpenKeyEx(HKEY_CURRENT_USER, + REGSTR_PATH_SPECIAL_FOLDERS, + REG_OPTION_RESERVED, + KEY_READ, + &hKey) == ERROR_SUCCESS ) + { + DWORD KeyType; + DWORD KeycbData = sizeof(szUserFiles); + if ( RegQueryValueEx(hKey, + "Personal", + NULL, + &KeyType, + (LPBYTE)szUserFiles, + &KeycbData) == ERROR_SUCCESS ) + { + RegCloseKey(hKey); + strcat(szUserFiles, "\\GTA Vice City User Files"); + _psCreateFolder(szUserFiles); + return szUserFiles; + } + + RegCloseKey(hKey); + } + + strcpy(szUserFiles, "data"); + return szUserFiles; +#else + static char szUserFiles[256]; + strcpy(szUserFiles, "/vmu/" VMU_DEFAULT_PATH); + _psCreateFolder(szUserFiles); + return szUserFiles; +#endif +} + +/* + ***************************************************************************** + */ +RwBool +psCameraBeginUpdate(RwCamera *camera) +{ + if ( !RwCameraBeginUpdate(Scene.camera) ) + { + ForegroundApp = FALSE; + RsEventHandler(rsACTIVATE, (void *)FALSE); + return FALSE; + } + + return TRUE; +} + +/* + ***************************************************************************** + */ +void +psCameraShowRaster(RwCamera *camera) +{ +#ifdef LEGACY_MENU_OPTIONS + if (FrontEndMenuManager.m_PrefsVsync || FrontEndMenuManager.m_bMenuActive) +#else + if (FrontEndMenuManager.m_PrefsFrameLimiter || FrontEndMenuManager.m_bMenuActive) +#endif + RwCameraShowRaster(camera, NULL /*PSGLOBAL(window)*/, rwRASTERFLIPWAITVSYNC); + else + RwCameraShowRaster(camera, NULL /*PSGLOBAL(window)*/, rwRASTERFLIPDONTWAIT); + + return; +} + +/* + ***************************************************************************** + */ +RwImage * +psGrabScreen(RwCamera *pCamera) +{ +#ifndef LIBRW + RwRaster *pRaster = RwCameraGetRaster(pCamera); + if (RwImage *pImage = RwImageCreate(pRaster->width, pRaster->height, 32)) { + RwImageAllocatePixels(pImage); + RwImageSetFromRaster(pImage, pRaster); + return pImage; + } +#else + rw::Image *image = RwCameraGetRaster(pCamera)->toImage(); + image->removeMask(); + if(image) + return image; +#endif + return nil; +} + +/* + ***************************************************************************** + */ +#ifdef _WIN32 +#pragma comment( lib, "Winmm.lib" ) // Needed for time +RwUInt32 +psTimer(void) +{ + RwUInt32 time; + + TIMECAPS TimeCaps; + + timeGetDevCaps(&TimeCaps, sizeof(TIMECAPS)); + + timeBeginPeriod(TimeCaps.wPeriodMin); + + time = (RwUInt32) timeGetTime(); + + timeEndPeriod(TimeCaps.wPeriodMin); + + return time; +} +#else +double +psTimer(void) +{ + #if defined(DC_SH4) + // Clock off AICA + // + // according to purist, sh4 is 199.5MHz (KOS assumes 200 mhz) + // and the sh4 has a different clock domain from AICA + // + // This solves the sound drift issue in the part 2 of the intro + // + // N.B. This depends on the jiffies per second from AICA + // and only works after AICA has been initialized + #define AICA_MEM_CLOCK 0x021000 /* 4 bytes */ + uint32_t jiffies = g2_read_32(SPU_RAM_UNCACHED_BASE + AICA_MEM_CLOCK); + return jiffies / 4.410f; +#else + struct timespec start; +#if defined(CLOCK_MONOTONIC_RAW) + clock_gettime(CLOCK_MONOTONIC_RAW, &start); +#elif defined(CLOCK_MONOTONIC_FAST) + clock_gettime(CLOCK_MONOTONIC_FAST, &start); +#else + clock_gettime(CLOCK_MONOTONIC, &start); +#endif + return start.tv_sec * 1000.0 + start.tv_nsec/1000000.0; +#endif +} +#endif + + +/* + ***************************************************************************** + */ +void +psMouseSetPos(RwV2d *pos) +{ + //glfwSetCursorPos(PSGLOBAL(window), pos->x, pos->y); + + PSGLOBAL(lastMousePos.x) = (RwInt32)pos->x; + + PSGLOBAL(lastMousePos.y) = (RwInt32)pos->y; + + return; +} + +/* + ***************************************************************************** + */ +RwMemoryFunctions* +psGetMemoryFunctions(void) +{ +#ifdef USE_CUSTOM_ALLOCATOR + return &memFuncs; +#else + return nil; +#endif +} + +/* + ***************************************************************************** + */ +RwBool +psInstallFileSystem(void) +{ + return (TRUE); +} + + +/* + ***************************************************************************** + */ +RwBool +psNativeTextureSupport(void) +{ + return true; +} + +/* + ***************************************************************************** + */ +#ifdef UNDER_CE +#define CMDSTR LPWSTR +#else +#define CMDSTR LPSTR +#endif + +/* + ***************************************************************************** + */ + +#ifdef __SWITCH__ + +static HidVibrationValue SwitchVibrationValues[2]; +static HidVibrationDeviceHandle SwitchVibrationDeviceHandles[2][2]; +static HidVibrationDeviceHandle SwitchVibrationDeviceGC; + +static PadState SwitchPad; + +static Result HidInitializationResult[2]; +static Result HidInitializationGCResult; + +static void _psInitializeVibration() +{ + HidInitializationResult[0] = hidInitializeVibrationDevices(SwitchVibrationDeviceHandles[0], 2, HidNpadIdType_Handheld, HidNpadStyleTag_NpadHandheld); + if(R_FAILED(HidInitializationResult[0])) { + printf("Failed to initialize VibrationDevice for Handheld Mode\n"); + } + HidInitializationResult[1] = hidInitializeVibrationDevices(SwitchVibrationDeviceHandles[1], 2, HidNpadIdType_No1, HidNpadStyleSet_NpadFullCtrl); + if(R_FAILED(HidInitializationResult[1])) { + printf("Failed to initialize VibrationDevice for Detached Mode\n"); + } + HidInitializationGCResult = hidInitializeVibrationDevices(&SwitchVibrationDeviceGC, 1, HidNpadIdType_No1, HidNpadStyleTag_NpadGc); + if(R_FAILED(HidInitializationResult[1])) { + printf("Failed to initialize VibrationDevice for GC Mode\n"); + } + + SwitchVibrationValues[0].freq_low = 160.0f; + SwitchVibrationValues[0].freq_high = 320.0f; + + padConfigureInput(1, HidNpadStyleSet_NpadFullCtrl); + padInitializeDefault(&SwitchPad); +} + +static void _psHandleVibration() +{ + padUpdate(&SwitchPad); + + uint8 target_device = padIsHandheld(&SwitchPad) ? 0 : 1; + + if(R_SUCCEEDED(HidInitializationResult[target_device])) { + CPad* pad = CPad::GetPad(0); + + // value conversion based on SDL2 switch port + SwitchVibrationValues[0].amp_high = SwitchVibrationValues[0].amp_low = pad->ShakeFreq == 0 ? 0.0f : 320.0f; + SwitchVibrationValues[0].freq_low = pad->ShakeFreq == 0.0 ? 160.0f : (float)pad->ShakeFreq * 1.26f; + SwitchVibrationValues[0].freq_high = pad->ShakeFreq == 0.0 ? 320.0f : (float)pad->ShakeFreq * 1.26f; + + if (pad->ShakeDur < CTimer::GetTimeStepInMilliseconds()) + pad->ShakeDur = 0; + else + pad->ShakeDur -= CTimer::GetTimeStepInMilliseconds(); + if (pad->ShakeDur == 0) pad->ShakeFreq = 0; + + + if(target_device == 1 && R_SUCCEEDED(HidInitializationGCResult)) { + // gamecube rumble + hidSendVibrationGcErmCommand(SwitchVibrationDeviceGC, pad->ShakeFreq > 0 ? HidVibrationGcErmCommand_Start : HidVibrationGcErmCommand_Stop); + } + + memcpy(&SwitchVibrationValues[1], &SwitchVibrationValues[0], sizeof(HidVibrationValue)); + hidSendVibrationValues(SwitchVibrationDeviceHandles[target_device], SwitchVibrationValues, 2); + } +} +#else +static void _psInitializeVibration() {} +#ifndef __DREAMCAST__ +static void _psHandleVibration() {} +#endif +#endif + +/* + ***************************************************************************** + */ +RwBool +psInitialize(void) +{ + PsGlobal.lastMousePos.x = PsGlobal.lastMousePos.y = 0.0f; + + RsGlobal.ps = &PsGlobal; + + PsGlobal.fullScreen = FALSE; + PsGlobal.cursorIsInWindow = FALSE; + WindowFocused = TRUE; + WindowIconified = FALSE; + + PsGlobal.joy1id = -1; + PsGlobal.joy2id = -1; + + CFileMgr::Initialise(); + + CPad::Initialise(); + CPad::GetPad(0)->Mode = 0; + +#ifdef PS2_MENU + CPad::Initialise(); + CPad::GetPad(0)->Mode = 0; + + CGame::frenchGame = false; + CGame::germanGame = false; + CGame::nastyGame = true; + CMenuManager::m_PrefsAllowNastyGame = true; + +#ifndef _WIN32 + // Mandatory for Linux(Unix? Posix?) to set lang. to environment lang. + setlocale(LC_ALL, ""); + + char *systemLang, *keyboardLang; + + systemLang = setlocale (LC_ALL, NULL); + keyboardLang = setlocale (LC_CTYPE, NULL); + + short lang; + lang = !strncmp(systemLang, "fr_",3) ? LANG_FRENCH : + !strncmp(systemLang, "de_",3) ? LANG_GERMAN : + !strncmp(systemLang, "en_",3) ? LANG_ENGLISH : + !strncmp(systemLang, "it_",3) ? LANG_ITALIAN : + !strncmp(systemLang, "es_",3) ? LANG_SPANISH : + LANG_OTHER; +#else + WORD lang = PRIMARYLANGID(GetSystemDefaultLCID()); +#endif + + if ( lang == LANG_ITALIAN ) + CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_ITALIAN; + else if ( lang == LANG_SPANISH ) + CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_SPANISH; + else if ( lang == LANG_GERMAN ) + { + CGame::germanGame = true; + CGame::nastyGame = false; + CMenuManager::m_PrefsAllowNastyGame = false; + CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_GERMAN; + } + else if ( lang == LANG_FRENCH ) + { + CGame::frenchGame = true; + CGame::nastyGame = false; + CMenuManager::m_PrefsAllowNastyGame = false; + CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_FRENCH; + } + else + CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_AMERICAN; + + FrontEndMenuManager.InitialiseMenuContentsAfterLoadingGame(); + + TheMemoryCard.Init(); +#else + C_PcSave::SetSaveDirectory(_psGetUserFilesFolder()); + + InitialiseLanguage(); + +#endif + + _psInitializeVibration(); + + gGameState = GS_START_UP; + TRACE("gGameState = GS_START_UP"); +#ifdef _WIN32 + OSVERSIONINFO verInfo; + verInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + GetVersionEx(&verInfo); + + _dwOperatingSystemVersion = OS_WIN95; + + if ( verInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ) + { + if ( verInfo.dwMajorVersion == 4 ) + { + debug("Operating System is WinNT\n"); + _dwOperatingSystemVersion = OS_WINNT; + } + else if ( verInfo.dwMajorVersion == 5 ) + { + debug("Operating System is Win2000\n"); + _dwOperatingSystemVersion = OS_WIN2000; + } + else if ( verInfo.dwMajorVersion > 5 ) + { + debug("Operating System is WinXP or greater\n"); + _dwOperatingSystemVersion = OS_WINXP; + } + } + else if ( verInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) + { + if ( verInfo.dwMajorVersion > 4 || verInfo.dwMajorVersion == 4 && verInfo.dwMinorVersion != 0 ) + { + debug("Operating System is Win98\n"); + _dwOperatingSystemVersion = OS_WIN98; + } + else + { + debug("Operating System is Win95\n"); + _dwOperatingSystemVersion = OS_WIN95; + } + } +#else + _dwOperatingSystemVersion = OS_WINXP; // To fool other classes +#endif + + +#ifndef PS2_MENU + FrontEndMenuManager.LoadSettings(); +#endif + + +#ifdef _WIN32 + MEMORYSTATUS memstats; + GlobalMemoryStatus(&memstats); + + _dwMemAvailPhys = memstats.dwAvailPhys; + + debug("Physical memory size %u\n", memstats.dwTotalPhys); + debug("Available physical memory %u\n", memstats.dwAvailPhys); +#elif defined (__APPLE__) + uint64_t size = 0; + uint64_t page_size = 0; + size_t uint64_len = sizeof(uint64_t); + size_t ull_len = sizeof(unsigned long long); + sysctl((int[]){CTL_HW, HW_PAGESIZE}, 2, &page_size, &ull_len, NULL, 0); + sysctl((int[]){CTL_HW, HW_MEMSIZE}, 2, &size, &uint64_len, NULL, 0); + vm_statistics_data_t vm_stat; + mach_msg_type_number_t count = HOST_VM_INFO_COUNT; + host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vm_stat, &count); + _dwMemAvailPhys = (uint64_t)(vm_stat.free_count * page_size); + debug("Physical memory size %llu\n", _dwMemAvailPhys); + debug("Available physical memory %llu\n", size); +#elif defined (__SWITCH__) + svcGetInfo(&_dwMemAvailPhys, InfoType_UsedMemorySize, CUR_PROCESS_HANDLE, 0); + debug("Physical memory size %llu\n", _dwMemAvailPhys); +#else +#ifndef __APPLE__ + // struct sysinfo systemInfo; + // sysinfo(&systemInfo); + _dwMemAvailPhys = 10 * 1024 * 1024 ; //systemInfo.freeram; + // debug("Physical memory size %u\n", systemInfo.totalram); + // debug("Available physical memory %u\n", systemInfo.freeram); +#else + uint64_t size = 0; + uint64_t page_size = 0; + size_t uint64_len = sizeof(uint64_t); + size_t ull_len = sizeof(unsigned long long); + sysctl((int[]){CTL_HW, HW_PAGESIZE}, 2, &page_size, &ull_len, NULL, 0); + sysctl((int[]){CTL_HW, HW_MEMSIZE}, 2, &size, &uint64_len, NULL, 0); + vm_statistics_data_t vm_stat; + mach_msg_type_number_t count = HOST_VM_INFO_COUNT; + host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vm_stat, &count); + _dwMemAvailPhys = (uint64_t)(vm_stat.free_count * page_size); + debug("Physical memory size %llu\n", _dwMemAvailPhys); + debug("Available physical memory %llu\n", size); +#endif + _dwOperatingSystemVersion = OS_WINXP; // To fool other classes +#endif + + TheText.Unload(); + + return TRUE; +} + + +/* + ***************************************************************************** + */ +void +psTerminate(void) +{ + return; +} + +/* + ***************************************************************************** + */ +static RwChar **_VMList; + +RwInt32 _psGetNumVideModes() +{ + return RwEngineGetNumVideoModes(); +} + +/* + ***************************************************************************** + */ +RwBool _psFreeVideoModeList() +{ + RwInt32 numModes; + RwInt32 i; + + numModes = _psGetNumVideModes(); + + if ( _VMList == nil ) + return TRUE; + + for ( i = 0; i < numModes; i++ ) + { + RwFree(_VMList[i]); + } + + RwFree(_VMList); + + _VMList = nil; + + return TRUE; +} + +/* + ***************************************************************************** + */ +RwChar **_psGetVideoModeList() +{ + RwInt32 numModes; + RwInt32 i; + + if ( _VMList != nil ) + { + return _VMList; + } + + numModes = RwEngineGetNumVideoModes(); + + _VMList = (RwChar **)RwCalloc(numModes, sizeof(RwChar*)); + + for ( i = 0; i < numModes; i++ ) + { + RwVideoMode vm; + + RwEngineGetVideoModeInfo(&vm, i); + + if ( vm.flags & rwVIDEOMODEEXCLUSIVE ) + { + _VMList[i] = (RwChar*)RwCalloc(100, sizeof(RwChar)); + rwsprintf(_VMList[i],"%d X %d X %d", vm.width, vm.height, vm.depth); + } + else + _VMList[i] = nil; + } + + return _VMList; +} + +/* + ***************************************************************************** + */ +void _psSelectScreenVM(RwInt32 videoMode) +{ + RwTexDictionarySetCurrent( nil ); + + FrontEndMenuManager.UnloadTextures(); + + if (!_psSetVideoMode(RwEngineGetCurrentSubSystem(), videoMode)) + { + RsGlobal.quit = TRUE; + + printf("ERROR: Failed to select new screen resolution\n"); + } + else + FrontEndMenuManager.LoadAllTextures(); +} + +/* + ***************************************************************************** + */ + +RwBool IsForegroundApp() +{ + return !!ForegroundApp; +} +/* +UINT GetBestRefreshRate(UINT width, UINT height, UINT depth) +{ + LPDIRECT3D8 d3d = Direct3DCreate8(D3D_SDK_VERSION); + + ASSERT(d3d != nil); + + UINT refreshRate = INT_MAX; + D3DFORMAT format; + + if ( depth == 32 ) + format = D3DFMT_X8R8G8B8; + else if ( depth == 24 ) + format = D3DFMT_R8G8B8; + else + format = D3DFMT_R5G6B5; + + UINT modeCount = d3d->GetAdapterModeCount(GcurSel); + + for ( UINT i = 0; i < modeCount; i++ ) + { + D3DDISPLAYMODE mode; + + d3d->EnumAdapterModes(GcurSel, i, &mode); + + if ( mode.Width == width && mode.Height == height && mode.Format == format ) + { + if ( mode.RefreshRate == 0 ) + return 0; + + if ( mode.RefreshRate < refreshRate && mode.RefreshRate >= 60 ) + refreshRate = mode.RefreshRate; + } + } + +#ifdef FIX_BUGS + d3d->Release(); +#endif + + if ( refreshRate == -1 ) + return -1; + + return refreshRate; +} +*/ +/* + ***************************************************************************** + */ +RwBool +psSelectDevice() +{ + RwVideoMode vm; + RwInt32 subSysNum; + RwInt32 AutoRenderer = 0; + + + RwBool modeFound = FALSE; + + if ( !useDefault ) + { + GnumSubSystems = RwEngineGetNumSubSystems(); + if ( !GnumSubSystems ) + { + return FALSE; + } + + /* Just to be sure ... */ + GnumSubSystems = (GnumSubSystems > MAX_SUBSYSTEMS) ? MAX_SUBSYSTEMS : GnumSubSystems; + + /* Get the names of all the sub systems */ + for (subSysNum = 0; subSysNum < GnumSubSystems; subSysNum++) + { + RwEngineGetSubSystemInfo(&GsubSysInfo[subSysNum], subSysNum); + } + + /* Get the default selection */ + GcurSel = RwEngineGetCurrentSubSystem(); +#ifdef IMPROVED_VIDEOMODE + if(FrontEndMenuManager.m_nPrefsSubsystem < GnumSubSystems) + GcurSel = FrontEndMenuManager.m_nPrefsSubsystem; +#endif + } + + /* Set the driver to use the correct sub system */ + if (!RwEngineSetSubSystem(GcurSel)) + { + return FALSE; + } + +#ifdef IMPROVED_VIDEOMODE + FrontEndMenuManager.m_nPrefsSubsystem = GcurSel; +#endif + +#ifndef IMPROVED_VIDEOMODE + if ( !useDefault ) + { + if ( _psGetVideoModeList()[FrontEndMenuManager.m_nDisplayVideoMode] && FrontEndMenuManager.m_nDisplayVideoMode ) + { + FrontEndMenuManager.m_nPrefsVideoMode = FrontEndMenuManager.m_nDisplayVideoMode; + GcurSelVM = FrontEndMenuManager.m_nDisplayVideoMode; + } + else + { +#ifdef DEFAULT_NATIVE_RESOLUTION + // get the native video mode + HDC hDevice = GetDC(NULL); + int w = GetDeviceCaps(hDevice, HORZRES); + int h = GetDeviceCaps(hDevice, VERTRES); + int d = GetDeviceCaps(hDevice, BITSPIXEL); +#else + const int w = 640; + const int h = 480; + const int d = 16; +#endif + while ( !modeFound && GcurSelVM < RwEngineGetNumVideoModes() ) + { + RwEngineGetVideoModeInfo(&vm, GcurSelVM); + if ( defaultFullscreenRes && vm.width != w + || vm.height != h + || vm.depth != d + || !(vm.flags & rwVIDEOMODEEXCLUSIVE) ) + ++GcurSelVM; + else + modeFound = TRUE; + } + + if ( !modeFound ) + { +#ifdef DEFAULT_NATIVE_RESOLUTION + GcurSelVM = 1; +#else + printf("WARNING: Cannot find 640x480 video mode, selecting device cancelled\n"); + return FALSE; +#endif + } + } + } +#else + if ( !useDefault ) + { + if(FrontEndMenuManager.m_nPrefsWidth == 0 || + FrontEndMenuManager.m_nPrefsHeight == 0 || + FrontEndMenuManager.m_nPrefsDepth == 0){ + // Defaults if nothing specified + // const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + FrontEndMenuManager.m_nPrefsWidth = 640; //mode->width; + FrontEndMenuManager.m_nPrefsHeight = 480; //mode->height; + FrontEndMenuManager.m_nPrefsDepth = 16; + FrontEndMenuManager.m_nPrefsWindowed = 0; + } + + // Find the videomode that best fits what we got from the settings file + RwInt32 bestFsMode = -1; + RwInt32 bestWidth = -1; + RwInt32 bestHeight = -1; + RwInt32 bestDepth = -1; + for(GcurSelVM = 0; GcurSelVM < RwEngineGetNumVideoModes(); GcurSelVM++){ + RwEngineGetVideoModeInfo(&vm, GcurSelVM); + bestWndMode = GcurSelVM; + bestWidth = vm.width; + bestHeight = vm.height; + bestDepth = vm.depth; + bestFsMode = GcurSelVM; + break; + } + + if(bestFsMode < 0){ + printf("WARNING: Cannot find desired video mode, selecting device cancelled\n"); + return FALSE; + } + GcurSelVM = bestFsMode; + + FrontEndMenuManager.m_nDisplayVideoMode = GcurSelVM; + FrontEndMenuManager.m_nPrefsVideoMode = FrontEndMenuManager.m_nDisplayVideoMode; + + FrontEndMenuManager.m_nSelectedScreenMode = FrontEndMenuManager.m_nPrefsWindowed; + } +#endif + + RwEngineGetVideoModeInfo(&vm, GcurSelVM); + +#ifdef IMPROVED_VIDEOMODE + if (FrontEndMenuManager.m_nPrefsWindowed) + GcurSelVM = bestWndMode; + + // Now GcurSelVM is 0 but vm has sizes(and fullscreen flag) of the video mode we want, that's why we changed the rwVIDEOMODEEXCLUSIVE conditions below + FrontEndMenuManager.m_nPrefsWidth = vm.width; + FrontEndMenuManager.m_nPrefsHeight = vm.height; + FrontEndMenuManager.m_nPrefsDepth = vm.depth; +#endif + +#ifndef PS2_MENU + FrontEndMenuManager.m_nCurrOption = 0; +#endif + + /* Set up the video mode and set the apps window + * dimensions to match */ + if (!RwEngineSetVideoMode(GcurSelVM)) + { + return FALSE; + } + /* + TODO + if (vm.flags & rwVIDEOMODEEXCLUSIVE) + { + debug("%dx%dx%d", vm.width, vm.height, vm.depth); + + UINT refresh = GetBestRefreshRate(vm.width, vm.height, vm.depth); + + if ( refresh != (UINT)-1 ) + { + debug("refresh %d", refresh); + RwD3D8EngineSetRefreshRate((RwUInt32)refresh); + } + } + */ +#ifndef IMPROVED_VIDEOMODE + if (vm.flags & rwVIDEOMODEEXCLUSIVE) + { + RsGlobal.maximumWidth = vm.width; + RsGlobal.maximumHeight = vm.height; + RsGlobal.width = vm.width; + RsGlobal.height = vm.height; + + PSGLOBAL(fullScreen) = TRUE; + } +#else + RsGlobal.maximumWidth = FrontEndMenuManager.m_nPrefsWidth; + RsGlobal.maximumHeight = FrontEndMenuManager.m_nPrefsHeight; + RsGlobal.width = FrontEndMenuManager.m_nPrefsWidth; + RsGlobal.height = FrontEndMenuManager.m_nPrefsHeight; + + PSGLOBAL(fullScreen) = !FrontEndMenuManager.m_nPrefsWindowed; +#endif + +#ifdef MULTISAMPLING + RwD3D8EngineSetMultiSamplingLevels(1 << FrontEndMenuManager.m_nPrefsMSAALevel); +#endif + return TRUE; +} + +// #ifndef GET_KEYBOARD_INPUT_FROM_X11 +// void keypressCB(GLFWwindow* window, int key, int scancode, int action, int mods); +// #endif +// void resizeCB(GLFWwindow* window, int width, int height); +// void scrollCB(GLFWwindow* window, double xoffset, double yoffset); +// void cursorCB(GLFWwindow* window, double xpos, double ypos); +// void cursorEnterCB(GLFWwindow* window, int entered); +// void windowFocusCB(GLFWwindow* window, int focused); +// void windowIconifyCB(GLFWwindow* window, int iconified); +// void joysChangeCB(int jid, int event); + +bool IsThisJoystickBlacklisted(int i) +{ +#ifndef DETECT_JOYSTICK_MENU + return false; +#else + if (glfwJoystickIsGamepad(i)) + return false; + + const char* joyname = glfwGetJoystickName(i); + + if (gSelectedJoystickName[0] != '\0' && + strncmp(joyname, gSelectedJoystickName, strlen(gSelectedJoystickName)) == 0) + return false; + + return true; +#endif +} + +void _InputInitialiseJoys() +{ + PSGLOBAL(joy1id) = -1; + PSGLOBAL(joy2id) = -1; + + // Load our gamepad mappings. +#define SDL_GAMEPAD_DB_PATH "gamecontrollerdb.txt" + FILE *f = fcaseopen(SDL_GAMEPAD_DB_PATH, "rb"); + if (f) { + fseek(f, 0, SEEK_END); + size_t fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + char *db = (char*)malloc(fsize + 1); + if (fread(db, 1, fsize, f) == fsize) { + db[fsize] = '\0'; + + // if (glfwUpdateGamepadMappings(db) == GLFW_FALSE) + // Error("glfwUpdateGamepadMappings didn't succeed, check " SDL_GAMEPAD_DB_PATH ".\n"); + } else + Error("fread on " SDL_GAMEPAD_DB_PATH " wasn't successful.\n"); + + free(db); + fclose(f); + } else + printf("You don't seem to have copied " SDL_GAMEPAD_DB_PATH " file from reVC/gamefiles to GTA: Vice City directory. Some gamepads may not be recognized.\n"); + +#undef SDL_GAMEPAD_DB_PATH + + // But always overwrite it with the one in SDL_GAMECONTROLLERCONFIG. + char const* EnvControlConfig = getenv("SDL_GAMECONTROLLERCONFIG"); + if (EnvControlConfig != nil) { + // glfwUpdateGamepadMappings(EnvControlConfig); + } + + // for (int i = 0; i <= GLFW_JOYSTICK_LAST; i++) { + // if (glfwJoystickPresent(i) && !IsThisJoystickBlacklisted(i)) { + // if (PSGLOBAL(joy1id) == -1) + // PSGLOBAL(joy1id) = i; + // else if (PSGLOBAL(joy2id) == -1) + // PSGLOBAL(joy2id) = i; + // else + // break; + // } + // } + +// if (PSGLOBAL(joy1id) != -1) { +// int count; +// glfwGetJoystickButtons(PSGLOBAL(joy1id), &count); +// #ifdef DETECT_JOYSTICK_MENU +// strcpy(gSelectedJoystickName, glfwGetJoystickName(PSGLOBAL(joy1id))); +// #endif +// ControlsManager.InitDefaultControlConfigJoyPad(count); +// } +} + +// int lastCursorMode = GLFW_CURSOR_HIDDEN; +long _InputInitialiseMouse(bool exclusive) +{ + // Disabled = keep cursor centered and hide + // lastCursorMode = exclusive ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_HIDDEN; + // glfwSetInputMode(PSGLOBAL(window), GLFW_CURSOR, lastCursorMode); + return 0; +} + +void _InputShutdownMouse() +{ + // Not needed +} + +// Not "needs exclusive" on GLFW, but more like "needs to change mode" +bool _InputMouseNeedsExclusive() +{ + // That was the cause of infamous mouse bug on Win. + + RwVideoMode vm; + RwEngineGetVideoModeInfo(&vm, GcurSelVM); + + // If windowed, free the cursor on menu(where this func. is called and DISABLED-HIDDEN transition is done accordingly) + // If it's fullscreen, be sure that it didn't stuck on HIDDEN. + return !(vm.flags & rwVIDEOMODEEXCLUSIVE); //|| lastCursorMode == GLFW_CURSOR_HIDDEN; +} + +void psPostRWinit(void) +{ + RwVideoMode vm; + RwEngineGetVideoModeInfo(&vm, GcurSelVM); + +// glfwSetFramebufferSizeCallback(PSGLOBAL(window), resizeCB); +// #ifndef IGNORE_MOUSE_KEYBOARD +// #ifndef GET_KEYBOARD_INPUT_FROM_X11 +// glfwSetKeyCallback(PSGLOBAL(window), keypressCB); +// #endif +// glfwSetScrollCallback(PSGLOBAL(window), scrollCB); +// glfwSetCursorPosCallback(PSGLOBAL(window), cursorCB); +// glfwSetCursorEnterCallback(PSGLOBAL(window), cursorEnterCB); +// #endif +// glfwSetWindowIconifyCallback(PSGLOBAL(window), windowIconifyCB); +// glfwSetWindowFocusCallback(PSGLOBAL(window), windowFocusCB); +// glfwSetJoystickCallback(joysChangeCB); + + _InputInitialiseJoys(); + _InputInitialiseMouse(false); + + // if(!(vm.flags & rwVIDEOMODEEXCLUSIVE)) + // glfwSetWindowSize(PSGLOBAL(window), RsGlobal.maximumWidth, RsGlobal.maximumHeight); + + // Make sure all keys are released + CPad::GetPad(0)->Clear(true); + CPad::GetPad(1)->Clear(true); +} + +/* + ***************************************************************************** + */ +RwBool _psSetVideoMode(RwInt32 subSystem, RwInt32 videoMode) +{ + RwInitialised = FALSE; + + RsEventHandler(rsRWTERMINATE, nil); + + GcurSel = subSystem; + GcurSelVM = videoMode; + + useDefault = TRUE; + + if ( RsEventHandler(rsRWINITIALIZE, &openParams) == rsEVENTERROR ) + return FALSE; + + RwInitialised = TRUE; + useDefault = FALSE; + + RwRect r; + + r.x = 0; + r.y = 0; + r.w = RsGlobal.maximumWidth; + r.h = RsGlobal.maximumHeight; + + RsEventHandler(rsCAMERASIZE, &r); + + psPostRWinit(); + + return TRUE; +} + + +// /* +// ***************************************************************************** +// */ +// static RwChar ** +// CommandLineToArgv(RwChar *cmdLine, RwInt32 *argCount) +// { +// RwInt32 numArgs = 0; +// RwBool inArg, inString; +// RwInt32 i, len; +// RwChar *res, *str, **aptr; + +// len = strlen(cmdLine); + +// /* +// * Count the number of arguments... +// */ +// inString = FALSE; +// inArg = FALSE; + +// for(i=0; i<=len; i++) +// { +// if( cmdLine[i] == '"' ) +// { +// inString = !inString; +// } + +// if( (cmdLine[i] <= ' ' && !inString) || i == len ) +// { +// if( inArg ) +// { +// inArg = FALSE; + +// numArgs++; +// } +// } +// else if( !inArg ) +// { +// inArg = TRUE; +// } +// } + +// /* +// * Allocate memory for result... +// */ +// res = (RwChar *)malloc(sizeof(RwChar *) * numArgs + len + 1); +// str = res + sizeof(RwChar *) * numArgs; +// aptr = (RwChar **)res; + +// strcpy(str, cmdLine); + +// /* +// * Walk through cmdLine again this time setting pointer to each arg... +// */ +// inArg = FALSE; +// inString = FALSE; + +// for(i=0; i<=len; i++) +// { +// if( cmdLine[i] == '"' ) +// { +// inString = !inString; +// } + +// if( (cmdLine[i] <= ' ' && !inString) || i == len ) +// { +// if( inArg ) +// { +// if( str[i-1] == '"' ) +// { +// str[i-1] = '\0'; +// } +// else +// { +// str[i] = '\0'; +// } + +// inArg = FALSE; +// } +// } +// else if( !inArg && cmdLine[i] != '"' ) +// { +// inArg = TRUE; + +// *aptr++ = &str[i]; +// } +// } + +// *argCount = numArgs; + +// return (RwChar **)res; +// } + +/* + ***************************************************************************** + */ +void InitialiseLanguage() +{ +#ifndef _WIN32 + // Mandatory for Linux(Unix? Posix?) to set lang. to environment lang. + setlocale(LC_ALL, ""); + + char *systemLang, *keyboardLang; + + systemLang = setlocale (LC_ALL, NULL); + keyboardLang = setlocale (LC_CTYPE, NULL); + + short primUserLCID, primSystemLCID; + primUserLCID = primSystemLCID = !strncmp(systemLang, "fr_",3) ? LANG_FRENCH : + !strncmp(systemLang, "de_",3) ? LANG_GERMAN : + !strncmp(systemLang, "en_",3) ? LANG_ENGLISH : + !strncmp(systemLang, "it_",3) ? LANG_ITALIAN : + !strncmp(systemLang, "es_",3) ? LANG_SPANISH : + LANG_OTHER; + + short primLayout = !strncmp(keyboardLang, "fr_",3) ? LANG_FRENCH : (!strncmp(keyboardLang, "de_",3) ? LANG_GERMAN : LANG_ENGLISH); + + short subUserLCID, subSystemLCID; + subUserLCID = subSystemLCID = !strncmp(systemLang, "en_AU",5) ? SUBLANG_ENGLISH_AUS : SUBLANG_OTHER; + short subLayout = !strncmp(keyboardLang, "en_AU",5) ? SUBLANG_ENGLISH_AUS : SUBLANG_OTHER; + +#else + WORD primUserLCID = PRIMARYLANGID(GetSystemDefaultLCID()); + WORD primSystemLCID = PRIMARYLANGID(GetUserDefaultLCID()); + WORD primLayout = PRIMARYLANGID((DWORD)GetKeyboardLayout(0)); + + WORD subUserLCID = SUBLANGID(GetSystemDefaultLCID()); + WORD subSystemLCID = SUBLANGID(GetUserDefaultLCID()); + WORD subLayout = SUBLANGID((DWORD)GetKeyboardLayout(0)); +#endif + if ( primUserLCID == LANG_GERMAN + || primSystemLCID == LANG_GERMAN + || primLayout == LANG_GERMAN ) + { + CGame::nastyGame = false; + FrontEndMenuManager.m_PrefsAllowNastyGame = false; + CGame::germanGame = true; + } + + if ( primUserLCID == LANG_FRENCH + || primSystemLCID == LANG_FRENCH + || primLayout == LANG_FRENCH ) + { + CGame::nastyGame = false; + FrontEndMenuManager.m_PrefsAllowNastyGame = false; + CGame::frenchGame = true; + } + + if ( subUserLCID == SUBLANG_ENGLISH_AUS + || subSystemLCID == SUBLANG_ENGLISH_AUS + || subLayout == SUBLANG_ENGLISH_AUS ) + CGame::noProstitutes = true; + +#ifdef NASTY_GAME + CGame::nastyGame = true; + FrontEndMenuManager.m_PrefsAllowNastyGame = true; + CGame::noProstitutes = false; +#endif + + int32 lang; + + switch ( primSystemLCID ) + { + case LANG_GERMAN: + { + lang = LANG_GERMAN; + break; + } + case LANG_FRENCH: + { + lang = LANG_FRENCH; + break; + } + case LANG_SPANISH: + { + lang = LANG_SPANISH; + break; + } + case LANG_ITALIAN: + { + lang = LANG_ITALIAN; + break; + } + default: + { + lang = ( subSystemLCID == SUBLANG_ENGLISH_AUS ) ? -99 : LANG_ENGLISH; + break; + } + } + + FrontEndMenuManager.OS_Language = primUserLCID; + + switch ( lang ) + { + case LANG_GERMAN: + { + FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_GERMAN; + break; + } + case LANG_SPANISH: + { + FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_SPANISH; + break; + } + case LANG_FRENCH: + { + FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_FRENCH; + break; + } + case LANG_ITALIAN: + { + FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_ITALIAN; + break; + } + default: + { + FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_AMERICAN; + break; + } + } + +#ifndef _WIN32 + // TODO this is needed for strcasecmp to work correctly across all languages, but can these cause other problems?? + setlocale(LC_CTYPE, "C"); + setlocale(LC_COLLATE, "C"); + setlocale(LC_NUMERIC, "C"); +#endif + + TheText.Unload(); + TheText.Load(); +} + +/* + ***************************************************************************** + */ + +void HandleExit() +{ +#ifdef _WIN32 + MSG message; + while ( PeekMessage(&message, nil, 0U, 0U, PM_REMOVE|PM_NOYIELD) ) + { + if( message.message == WM_QUIT ) + { + RsGlobal.quit = TRUE; + } + else + { + TranslateMessage(&message); + DispatchMessage(&message); + } + } +#else + // We now handle terminate message always, why handle on some cases? + return; +#endif +} + +// #ifndef _WIN32 +// void terminateHandler(int sig, siginfo_t *info, void *ucontext) { +// RsGlobal.quit = TRUE; +// } + +// #ifdef FLUSHABLE_STREAMING +// void dummyHandler(int sig){ +// // Don't kill the app pls +// } +// #endif +// #endif + +// void resizeCB(GLFWwindow* window, int width, int height) { +// /* +// * Handle event to ensure window contents are displayed during re-size +// * as this can be disabled by the user, then if there is not enough +// * memory things don't work. +// */ +// /* redraw window */ + +// if (RwInitialised && gGameState == GS_PLAYING_GAME) +// { +// RsEventHandler(rsIDLE, (void *)TRUE); +// } + +// if (RwInitialised && height > 0 && width > 0) { +// RwRect r; + +// // TODO fix artifacts of resizing with mouse +// RsGlobal.maximumHeight = height; +// RsGlobal.maximumWidth = width; + +// r.x = 0; +// r.y = 0; +// r.w = width; +// r.h = height; + +// RsEventHandler(rsCAMERASIZE, &r); +// } +// // glfwSetWindowPos(window, 0, 0); +// } + +// void scrollCB(GLFWwindow* window, double xoffset, double yoffset) { +// PSGLOBAL(mouseWheel) = yoffset; +// } + +bool lshiftStatus = false; +bool rshiftStatus = false; + +#ifndef GET_KEYBOARD_INPUT_FROM_X11 +// int keymap[GLFW_KEY_LAST + 1]; + +static void +initkeymap(void) +{ + // int i; + // for (i = 0; i < GLFW_KEY_LAST + 1; i++) + // keymap[i] = rsNULL; + + // keymap[GLFW_KEY_SPACE] = ' '; + // keymap[GLFW_KEY_APOSTROPHE] = '\''; + // keymap[GLFW_KEY_COMMA] = ','; + // keymap[GLFW_KEY_MINUS] = '-'; + // keymap[GLFW_KEY_PERIOD] = '.'; + // keymap[GLFW_KEY_SLASH] = '/'; + // keymap[GLFW_KEY_0] = '0'; + // keymap[GLFW_KEY_1] = '1'; + // keymap[GLFW_KEY_2] = '2'; + // keymap[GLFW_KEY_3] = '3'; + // keymap[GLFW_KEY_4] = '4'; + // keymap[GLFW_KEY_5] = '5'; + // keymap[GLFW_KEY_6] = '6'; + // keymap[GLFW_KEY_7] = '7'; + // keymap[GLFW_KEY_8] = '8'; + // keymap[GLFW_KEY_9] = '9'; + // keymap[GLFW_KEY_SEMICOLON] = ';'; + // keymap[GLFW_KEY_EQUAL] = '='; + // keymap[GLFW_KEY_A] = 'A'; + // keymap[GLFW_KEY_B] = 'B'; + // keymap[GLFW_KEY_C] = 'C'; + // keymap[GLFW_KEY_D] = 'D'; + // keymap[GLFW_KEY_E] = 'E'; + // keymap[GLFW_KEY_F] = 'F'; + // keymap[GLFW_KEY_G] = 'G'; + // keymap[GLFW_KEY_H] = 'H'; + // keymap[GLFW_KEY_I] = 'I'; + // keymap[GLFW_KEY_J] = 'J'; + // keymap[GLFW_KEY_K] = 'K'; + // keymap[GLFW_KEY_L] = 'L'; + // keymap[GLFW_KEY_M] = 'M'; + // keymap[GLFW_KEY_N] = 'N'; + // keymap[GLFW_KEY_O] = 'O'; + // keymap[GLFW_KEY_P] = 'P'; + // keymap[GLFW_KEY_Q] = 'Q'; + // keymap[GLFW_KEY_R] = 'R'; + // keymap[GLFW_KEY_S] = 'S'; + // keymap[GLFW_KEY_T] = 'T'; + // keymap[GLFW_KEY_U] = 'U'; + // keymap[GLFW_KEY_V] = 'V'; + // keymap[GLFW_KEY_W] = 'W'; + // keymap[GLFW_KEY_X] = 'X'; + // keymap[GLFW_KEY_Y] = 'Y'; + // keymap[GLFW_KEY_Z] = 'Z'; + // keymap[GLFW_KEY_LEFT_BRACKET] = '['; + // keymap[GLFW_KEY_BACKSLASH] = '\\'; + // keymap[GLFW_KEY_RIGHT_BRACKET] = ']'; + // keymap[GLFW_KEY_GRAVE_ACCENT] = '`'; + // keymap[GLFW_KEY_ESCAPE] = rsESC; + // keymap[GLFW_KEY_ENTER] = rsENTER; + // keymap[GLFW_KEY_TAB] = rsTAB; + // keymap[GLFW_KEY_BACKSPACE] = rsBACKSP; + // keymap[GLFW_KEY_INSERT] = rsINS; + // keymap[GLFW_KEY_DELETE] = rsDEL; + // keymap[GLFW_KEY_RIGHT] = rsRIGHT; + // keymap[GLFW_KEY_LEFT] = rsLEFT; + // keymap[GLFW_KEY_DOWN] = rsDOWN; + // keymap[GLFW_KEY_UP] = rsUP; + // keymap[GLFW_KEY_PAGE_UP] = rsPGUP; + // keymap[GLFW_KEY_PAGE_DOWN] = rsPGDN; + // keymap[GLFW_KEY_HOME] = rsHOME; + // keymap[GLFW_KEY_END] = rsEND; + // keymap[GLFW_KEY_CAPS_LOCK] = rsCAPSLK; + // keymap[GLFW_KEY_SCROLL_LOCK] = rsSCROLL; + // keymap[GLFW_KEY_NUM_LOCK] = rsNUMLOCK; + // keymap[GLFW_KEY_PRINT_SCREEN] = rsNULL; + // keymap[GLFW_KEY_PAUSE] = rsPAUSE; + + // keymap[GLFW_KEY_F1] = rsF1; + // keymap[GLFW_KEY_F2] = rsF2; + // keymap[GLFW_KEY_F3] = rsF3; + // keymap[GLFW_KEY_F4] = rsF4; + // keymap[GLFW_KEY_F5] = rsF5; + // keymap[GLFW_KEY_F6] = rsF6; + // keymap[GLFW_KEY_F7] = rsF7; + // keymap[GLFW_KEY_F8] = rsF8; + // keymap[GLFW_KEY_F9] = rsF9; + // keymap[GLFW_KEY_F10] = rsF10; + // keymap[GLFW_KEY_F11] = rsF11; + // keymap[GLFW_KEY_F12] = rsF12; + // keymap[GLFW_KEY_F13] = rsNULL; + // keymap[GLFW_KEY_F14] = rsNULL; + // keymap[GLFW_KEY_F15] = rsNULL; + // keymap[GLFW_KEY_F16] = rsNULL; + // keymap[GLFW_KEY_F17] = rsNULL; + // keymap[GLFW_KEY_F18] = rsNULL; + // keymap[GLFW_KEY_F19] = rsNULL; + // keymap[GLFW_KEY_F20] = rsNULL; + // keymap[GLFW_KEY_F21] = rsNULL; + // keymap[GLFW_KEY_F22] = rsNULL; + // keymap[GLFW_KEY_F23] = rsNULL; + // keymap[GLFW_KEY_F24] = rsNULL; + // keymap[GLFW_KEY_F25] = rsNULL; + // keymap[GLFW_KEY_KP_0] = rsPADINS; + // keymap[GLFW_KEY_KP_1] = rsPADEND; + // keymap[GLFW_KEY_KP_2] = rsPADDOWN; + // keymap[GLFW_KEY_KP_3] = rsPADPGDN; + // keymap[GLFW_KEY_KP_4] = rsPADLEFT; + // keymap[GLFW_KEY_KP_5] = rsPAD5; + // keymap[GLFW_KEY_KP_6] = rsPADRIGHT; + // keymap[GLFW_KEY_KP_7] = rsPADHOME; + // keymap[GLFW_KEY_KP_8] = rsPADUP; + // keymap[GLFW_KEY_KP_9] = rsPADPGUP; + // keymap[GLFW_KEY_KP_DECIMAL] = rsPADDEL; + // keymap[GLFW_KEY_KP_DIVIDE] = rsDIVIDE; + // keymap[GLFW_KEY_KP_MULTIPLY] = rsTIMES; + // keymap[GLFW_KEY_KP_SUBTRACT] = rsMINUS; + // keymap[GLFW_KEY_KP_ADD] = rsPLUS; + // keymap[GLFW_KEY_KP_ENTER] = rsPADENTER; + // keymap[GLFW_KEY_KP_EQUAL] = rsNULL; + // keymap[GLFW_KEY_LEFT_SHIFT] = rsLSHIFT; + // keymap[GLFW_KEY_LEFT_CONTROL] = rsLCTRL; + // keymap[GLFW_KEY_LEFT_ALT] = rsLALT; + // keymap[GLFW_KEY_LEFT_SUPER] = rsLWIN; + // keymap[GLFW_KEY_RIGHT_SHIFT] = rsRSHIFT; + // keymap[GLFW_KEY_RIGHT_CONTROL] = rsRCTRL; + // keymap[GLFW_KEY_RIGHT_ALT] = rsRALT; + // keymap[GLFW_KEY_RIGHT_SUPER] = rsRWIN; + // keymap[GLFW_KEY_MENU] = rsNULL; +} + +// void +// keypressCB(GLFWwindow* window, int key, int scancode, int action, int mods) +// { +// if (key >= 0 && key <= GLFW_KEY_LAST && action != GLFW_REPEAT) { +// RsKeyCodes ks = (RsKeyCodes)keymap[key]; + +// if (key == GLFW_KEY_LEFT_SHIFT) +// lshiftStatus = action != GLFW_RELEASE; + +// if (key == GLFW_KEY_RIGHT_SHIFT) +// rshiftStatus = action != GLFW_RELEASE; + +// if (action == GLFW_RELEASE) RsKeyboardEventHandler(rsKEYUP, &ks); +// else if (action == GLFW_PRESS) RsKeyboardEventHandler(rsKEYDOWN, &ks); +// } +// } + +#else + +uint32 keymap[512]; // 256 ascii + 256 KeySyms between 0xff00 - 0xffff +bool keyStates[512]; +uint32 keyCodeToKeymapIndex[256]; // cache for physical keys + +#define KEY_MAP_OFFSET (0xff00 - 256) +static void +initkeymap(void) +{ + Display *display = glfwGetX11Display(); + int i; + + for (i = 0; i < ARRAY_SIZE(keymap); i++) + keymap[i] = rsNULL; + + // You can add new ASCII mappings to here freely (but beware that if right hand side of assignment isn't supported on CFont, it'll be blank/won't work on binding screen) + // Right hand side of assigments should always be uppercase counterpart of character + keymap[XK_space] = ' '; + keymap[XK_apostrophe] = '\''; + keymap[XK_ampersand] = '&'; + keymap[XK_percent] = '%'; + keymap[XK_dollar] = '$'; + keymap[XK_comma] = ','; + keymap[XK_minus] = '-'; + keymap[XK_period] = '.'; + keymap[XK_slash] = '/'; + keymap[XK_question] = '?'; + keymap[XK_exclam] = '!'; + keymap[XK_quotedbl] = '"'; + keymap[XK_colon] = ':'; + keymap[XK_semicolon] = ';'; + keymap[XK_equal] = '='; + keymap[XK_bracketleft] = '['; + keymap[XK_backslash] = '\\'; + keymap[XK_bracketright] = ']'; + keymap[XK_grave] = '`'; + keymap[XK_0] = '0'; + keymap[XK_1] = '1'; + keymap[XK_2] = '2'; + keymap[XK_3] = '3'; + keymap[XK_4] = '4'; + keymap[XK_5] = '5'; + keymap[XK_6] = '6'; + keymap[XK_7] = '7'; + keymap[XK_8] = '8'; + keymap[XK_9] = '9'; + keymap[XK_a] = 'A'; + keymap[XK_b] = 'B'; + keymap[XK_c] = 'C'; + keymap[XK_d] = 'D'; + keymap[XK_e] = 'E'; + keymap[XK_f] = 'F'; + keymap[XK_g] = 'G'; + keymap[XK_h] = 'H'; + keymap[XK_i] = 'I'; + keymap[XK_I] = 'I'; // Turkish I problem + keymap[XK_j] = 'J'; + keymap[XK_k] = 'K'; + keymap[XK_l] = 'L'; + keymap[XK_m] = 'M'; + keymap[XK_n] = 'N'; + keymap[XK_o] = 'O'; + keymap[XK_p] = 'P'; + keymap[XK_q] = 'Q'; + keymap[XK_r] = 'R'; + keymap[XK_s] = 'S'; + keymap[XK_t] = 'T'; + keymap[XK_u] = 'U'; + keymap[XK_v] = 'V'; + keymap[XK_w] = 'W'; + keymap[XK_x] = 'X'; + keymap[XK_y] = 'Y'; + keymap[XK_z] = 'Z'; + + // Some of regional but ASCII characters that GTA supports + keymap[XK_agrave] = 0x00c0; + keymap[XK_aacute] = 0x00c1; + keymap[XK_acircumflex] = 0x00c2; + keymap[XK_adiaeresis] = 0x00c4; + + keymap[XK_ae] = 0x00c6; + + keymap[XK_egrave] = 0x00c8; + keymap[XK_eacute] = 0x00c9; + keymap[XK_ecircumflex] = 0x00ca; + keymap[XK_ediaeresis] = 0x00cb; + + keymap[XK_igrave] = 0x00cc; + keymap[XK_iacute] = 0x00cd; + keymap[XK_icircumflex] = 0x00ce; + keymap[XK_idiaeresis] = 0x00cf; + + keymap[XK_ccedilla] = 0x00c7; + keymap[XK_odiaeresis] = 0x00d6; + keymap[XK_udiaeresis] = 0x00dc; + + // These are 0xff00 - 0xffff range of KeySym's, and subtracting KEY_MAP_OFFSET is needed + keymap[XK_Escape - KEY_MAP_OFFSET] = rsESC; + keymap[XK_Return - KEY_MAP_OFFSET] = rsENTER; + keymap[XK_Tab - KEY_MAP_OFFSET] = rsTAB; + keymap[XK_BackSpace - KEY_MAP_OFFSET] = rsBACKSP; + keymap[XK_Insert - KEY_MAP_OFFSET] = rsINS; + keymap[XK_Delete - KEY_MAP_OFFSET] = rsDEL; + keymap[XK_Right - KEY_MAP_OFFSET] = rsRIGHT; + keymap[XK_Left - KEY_MAP_OFFSET] = rsLEFT; + keymap[XK_Down - KEY_MAP_OFFSET] = rsDOWN; + keymap[XK_Up - KEY_MAP_OFFSET] = rsUP; + keymap[XK_Page_Up - KEY_MAP_OFFSET] = rsPGUP; + keymap[XK_Page_Down - KEY_MAP_OFFSET] = rsPGDN; + keymap[XK_Home - KEY_MAP_OFFSET] = rsHOME; + keymap[XK_End - KEY_MAP_OFFSET] = rsEND; + keymap[XK_Caps_Lock - KEY_MAP_OFFSET] = rsCAPSLK; + keymap[XK_Scroll_Lock - KEY_MAP_OFFSET] = rsSCROLL; + keymap[XK_Num_Lock - KEY_MAP_OFFSET] = rsNUMLOCK; + keymap[XK_Pause - KEY_MAP_OFFSET] = rsPAUSE; + + keymap[XK_F1 - KEY_MAP_OFFSET] = rsF1; + keymap[XK_F2 - KEY_MAP_OFFSET] = rsF2; + keymap[XK_F3 - KEY_MAP_OFFSET] = rsF3; + keymap[XK_F4 - KEY_MAP_OFFSET] = rsF4; + keymap[XK_F5 - KEY_MAP_OFFSET] = rsF5; + keymap[XK_F6 - KEY_MAP_OFFSET] = rsF6; + keymap[XK_F7 - KEY_MAP_OFFSET] = rsF7; + keymap[XK_F8 - KEY_MAP_OFFSET] = rsF8; + keymap[XK_F9 - KEY_MAP_OFFSET] = rsF9; + keymap[XK_F10 - KEY_MAP_OFFSET] = rsF10; + keymap[XK_F11 - KEY_MAP_OFFSET] = rsF11; + keymap[XK_F12 - KEY_MAP_OFFSET] = rsF12; + keymap[XK_F13 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F14 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F15 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F16 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F17 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F18 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F19 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F20 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F21 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F22 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F23 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F24 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F25 - KEY_MAP_OFFSET] = rsNULL; + + keymap[XK_KP_0 - KEY_MAP_OFFSET] = rsPADINS; + keymap[XK_KP_1 - KEY_MAP_OFFSET] = rsPADEND; + keymap[XK_KP_2 - KEY_MAP_OFFSET] = rsPADDOWN; + keymap[XK_KP_3 - KEY_MAP_OFFSET] = rsPADPGDN; + keymap[XK_KP_4 - KEY_MAP_OFFSET] = rsPADLEFT; + keymap[XK_KP_5 - KEY_MAP_OFFSET] = rsPAD5; + keymap[XK_KP_6 - KEY_MAP_OFFSET] = rsPADRIGHT; + keymap[XK_KP_7 - KEY_MAP_OFFSET] = rsPADHOME; + keymap[XK_KP_8 - KEY_MAP_OFFSET] = rsPADUP; + keymap[XK_KP_9 - KEY_MAP_OFFSET] = rsPADPGUP; + keymap[XK_KP_Insert - KEY_MAP_OFFSET] = rsPADINS; + keymap[XK_KP_End - KEY_MAP_OFFSET] = rsPADEND; + keymap[XK_KP_Down - KEY_MAP_OFFSET] = rsPADDOWN; + keymap[XK_KP_Page_Down - KEY_MAP_OFFSET] = rsPADPGDN; + keymap[XK_KP_Left - KEY_MAP_OFFSET] = rsPADLEFT; + keymap[XK_KP_Begin - KEY_MAP_OFFSET] = rsPAD5; + keymap[XK_KP_Right - KEY_MAP_OFFSET] = rsPADRIGHT; + keymap[XK_KP_Home - KEY_MAP_OFFSET] = rsPADHOME; + keymap[XK_KP_Up - KEY_MAP_OFFSET] = rsPADUP; + keymap[XK_KP_Page_Up - KEY_MAP_OFFSET] = rsPADPGUP; + + keymap[XK_KP_Decimal - KEY_MAP_OFFSET] = rsPADDEL; + keymap[XK_KP_Divide - KEY_MAP_OFFSET] = rsDIVIDE; + keymap[XK_KP_Multiply - KEY_MAP_OFFSET] = rsTIMES; + keymap[XK_KP_Subtract - KEY_MAP_OFFSET] = rsMINUS; + keymap[XK_KP_Add - KEY_MAP_OFFSET] = rsPLUS; + keymap[XK_KP_Enter - KEY_MAP_OFFSET] = rsPADENTER; + keymap[XK_KP_Equal - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_Shift_L - KEY_MAP_OFFSET] = rsLSHIFT; + keymap[XK_Control_L - KEY_MAP_OFFSET] = rsLCTRL; + keymap[XK_Alt_L - KEY_MAP_OFFSET] = rsLALT; + keymap[XK_Super_L - KEY_MAP_OFFSET] = rsLWIN; + keymap[XK_Shift_R - KEY_MAP_OFFSET] = rsRSHIFT; + keymap[XK_Control_R - KEY_MAP_OFFSET] = rsRCTRL; + keymap[XK_Alt_R - KEY_MAP_OFFSET] = rsRALT; + keymap[XK_Super_R - KEY_MAP_OFFSET] = rsRWIN; + keymap[XK_Menu - KEY_MAP_OFFSET] = rsNULL; + + // Cache the key codes' key symbol equivelants, otherwise we will have to do it on each frame + // KeyCode is always in [0,255], and represents a physical key + + int min_keycode, max_keycode, keysyms_per_keycode; + KeySym *keymap, *origkeymap; + + char *keyboardLang = setlocale (LC_CTYPE, NULL); + setlocale(LC_CTYPE, ""); + + XDisplayKeycodes(display, &min_keycode, &max_keycode); + origkeymap = XGetKeyboardMapping(display, min_keycode, (max_keycode - min_keycode + 1), &keysyms_per_keycode); + keymap = origkeymap; + for (int i = min_keycode; i <= max_keycode; i++) { + int j, lastKeysym; + + lastKeysym = keysyms_per_keycode - 1; + while ((lastKeysym >= 0) && (keymap[lastKeysym] == NoSymbol)) + lastKeysym--; + + for (j = 0; j <= lastKeysym; j++) { + KeySym ks = keymap[j]; + + if (ks == NoSymbol) + continue; + + if (ks < 256) { + keyCodeToKeymapIndex[i] = ks; + break; + } else if (ks >= 0xff00 && ks < 0xffff) { + keyCodeToKeymapIndex[i] = ks - KEY_MAP_OFFSET; + break; + } + } + keymap += keysyms_per_keycode; + } + XFree(origkeymap); + + setlocale(LC_CTYPE, keyboardLang); +} +#undef KEY_MAP_OFFSET + +void checkKeyPresses() +{ + Display *display = glfwGetX11Display(); + char keys[32]; + XQueryKeymap(display, keys); + for (int i = 0; i < sizeof(keys); i++) { + for (int j = 0; j < 8; j++) { + KeyCode keycode = 8 * i + j; + uint32 keymapIndex = keyCodeToKeymapIndex[keycode]; + if (keymapIndex != 0) { + int rsCode = keymap[keymapIndex]; + if (rsCode == rsNULL) + continue; + + bool pressed = WindowFocused && !!(keys[i] & (1 << j)); + + // idk why R* does that + if (rsCode == rsLSHIFT) + lshiftStatus = pressed; + else if (rsCode == rsRSHIFT) + rshiftStatus = pressed; + + if (keyStates[keymapIndex] != pressed) { + if (pressed) { + RsKeyboardEventHandler(rsKEYDOWN, &rsCode); + } else { + RsKeyboardEventHandler(rsKEYUP, &rsCode); + } + } + + keyStates[keymapIndex] = pressed; + } + } + } + +} +#endif + +// R* calls that in ControllerConfig, idk why +void +_InputTranslateShiftKeyUpDown(RsKeyCodes *rs) { + RsKeyboardEventHandler(lshiftStatus ? rsKEYDOWN : rsKEYUP, &(*rs = rsLSHIFT)); + RsKeyboardEventHandler(rshiftStatus ? rsKEYDOWN : rsKEYUP, &(*rs = rsRSHIFT)); +} + +// TODO this only works in frontend(and luckily only frontend use this). Fun fact: if I get pos manually in game, glfw reports that it's > 32000 +// void +// cursorCB(GLFWwindow* window, double xpos, double ypos) { +// if (!FrontEndMenuManager.m_bMenuActive) +// return; + +// int winw, winh; +// glfwGetWindowSize(PSGLOBAL(window), &winw, &winh); +// FrontEndMenuManager.m_nMouseTempPosX = xpos * (RsGlobal.maximumWidth / winw); +// FrontEndMenuManager.m_nMouseTempPosY = ypos * (RsGlobal.maximumHeight / winh); +// } + +// void +// cursorEnterCB(GLFWwindow* window, int entered) { +// PSGLOBAL(cursorIsInWindow) = !!entered; +// } + +// void +// windowFocusCB(GLFWwindow* window, int focused) { +// WindowFocused = !!focused; +// } + +// void +// windowIconifyCB(GLFWwindow* window, int iconified) { +// WindowIconified = !!iconified; +// } + +/* + ***************************************************************************** + */ +#ifdef _WIN32 +int PASCAL +WinMain(HINSTANCE instance, + HINSTANCE prevInstance __RWUNUSED__, + CMDSTR cmdLine, + int cmdShow) +{ + + RwInt32 argc; + RwChar** argv; + SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, nil, SPIF_SENDCHANGE); + +#ifndef MASTER + if (strstr(cmdLine, "-console")) + { + AllocConsole(); + freopen("CONIN$", "r", stdin); + freopen("CONOUT$", "w", stdout); + freopen("CONOUT$", "w", stderr); + } +#endif + +#else +#if !defined(DC_SIM) +extern "C" const char etext[]; + +__attribute__((noinline)) void stacktrace() { + uint32 sp=0, pr=0; + __asm__ __volatile__( + "mov r15,%0\n" + "sts pr,%1\n" + : "+r" (sp), "+r" (pr) + : + : ); + dbglog(DBG_CRITICAL, "DCA3: %s\n", getExecutableTag()); + dbglog(DBG_CRITICAL, "Stack trace: %p ", (void*)pr); + int found = 0; + if(!(sp & 3) && sp > 0x8c000000 && sp < _arch_mem_top) { + char** sp_ptr = (char**)sp; + for (int so = 0; so < 16384; so++) { + if (uintptr_t(&sp_ptr[so]) >= _arch_mem_top) { + dbglog(DBG_CRITICAL, "(@@%p) ", &sp_ptr[so]); + break; + } + if (sp_ptr[so] > (char*)0x8c000000 && sp_ptr[so] < etext) { + uintptr_t addr = uintptr_t(sp_ptr[so]); + // candidate return pointer + if (addr & 1) { + // dbglog(DBG_CRITICAL, "Stack trace: %p (@%p): misaligned\n", (void*)sp_ptr[so], &sp_ptr[so]); + continue; + } + + uint16_t* instrp = (uint16_t*)addr; + + uint16_t instr = instrp[-2]; + // BSR or BSRF or JSR @Rn ? + if (((instr & 0xf000) == 0xB000) || ((instr & 0xf0ff) == 0x0003) || ((instr & 0xf0ff) == 0x400B)) { + dbglog(DBG_CRITICAL, "%p ", instrp); + if (found++ > 24) { + dbglog(DBG_CRITICAL, "(@%p) ", &sp_ptr[so]); + break; + } + } else { + // dbglog(DBG_CRITICAL, "%p:%04X ", instrp, instr); + } + } else { + // dbglog(DBG_CRITICAL, "Stack trace: %p (@%p): out of range\n", (void*)sp_ptr[so], &sp_ptr[so]); + } + } + dbglog(DBG_CRITICAL, "end\n"); + } else { + dbglog(DBG_CRITICAL, "(@%p)\n", (void*)sp); + } +} + +#include +#include +#include +#include +#include + +#endif + +const char* getExecutableTag() { + return GIT_VERSION ":" CI_JOB_ID; +} + +int +main(int argc, char *argv[]) +{ + dbglog(DBG_CRITICAL, "DCA3: %s\n", getExecutableTag()); + #if !defined(DC_SIM) + std::set_terminate([]() { + fflush(stdout); + fflush(stderr); + dbglog(DBG_CRITICAL, "std::terminate() called\n"); + dbglog(DBG_CRITICAL, "POSIX error (may not be relevant): %s\n", strerror(errno)); + stacktrace(); + dbgio_dev_select("fb"); + sleep(1); + dbglog(DBG_CRITICAL, "std::terminate() called\n"); + dbglog(DBG_CRITICAL, "POSIX error (may not be relevant): %s\n", strerror(errno)); + stacktrace(); + dbgio_flush(); + + + abort(); + }); + #endif + + #if !defined(DC_SIM) + #if defined(WITH_IDE) + ide_fat_init(); + #ifndef DC_CHDIR + #define DC_CHDIR /ide/gta3 + #endif + #endif + + #ifndef DC_CHDIR + #define DC_CHDIR /cd + #endif + + /* Disable printf() buffering, because it's slow with latest Newlib/toolchain combo. */ + setvbuf(stdout, NULL, _IONBF, 0); +#if defined(MASTER) || defined(NDEBUG) + dbglog_set_level(DBG_CRITICAL); +#endif + + printf("sbrk: %p\n", sbrk(0)); + printf("Loading game assets from %s\n", STR(DC_CHDIR)); + chdir(STR(DC_CHDIR)); + +#if defined(WITH_PROF) + // dbgio_dev_select("null"); + #if defined(WITH_SD) + sd_fat_init(); + #endif + cont_btn_callback(0, CONT_A | CONT_B | CONT_X, [](uint8_t, uint32_t btns) { + if (profiler_recording()) + return; + if(profiling) { + profiling = false; + profiler_clean_up(); + } + else { + profiling = true; + #if defined(WITH_SD) + profiler_init("/sd"); + #else + profiler_init(WITH_PROF); + #endif + profiler_start(); + } + }); +#endif + + cont_btn_callback(0, CONT_RESET_BUTTONS, [](uint8_t, uint32_t) { + exit(EXIT_SUCCESS); + }); + + #else + chdir("./repack-data/miami"); + #endif + +#endif + RwV2d pos; + RwInt32 i; + +#ifdef USE_CUSTOM_ALLOCATOR + InitMemoryMgr(); +#endif + +#if !defined(_WIN32) && !defined(__SWITCH__) && !defined(RW_DC) + struct sigaction act; + act.sa_sigaction = terminateHandler; + act.sa_flags = SA_SIGINFO; + sigaction(SIGTERM, &act, NULL); +#ifdef FLUSHABLE_STREAMING + struct sigaction sa; + sigemptyset(&sa.sa_mask); + sa.sa_handler = dummyHandler; + sa.sa_flags = 0; + sigaction(SIGUSR1, &sa, NULL); +#endif +#endif + + /* + * Initialize the platform independent data. + * This will in turn initialize the platform specific data... + */ + if( RsEventHandler(rsINITIALIZE, nil) == rsEVENTERROR ) + { + return FALSE; + } + +#ifdef _WIN32 + /* + * Get proper command line params, cmdLine passed to us does not + * work properly under all circumstances... + */ + cmdLine = GetCommandLine(); + + /* + * Parse command line into standard (argv, argc) parameters... + */ + argv = CommandLineToArgv(cmdLine, &argc); + + + /* + * Parse command line parameters (except program name) one at + * a time BEFORE RenderWare initialization... + */ +#endif + + for(i=1; iGetLeftMouseJustDown()) +// ++gGameState; +// else if (CPad::GetPad(0)->GetEnterJustDown()) +// ++gGameState; +// else if (CPad::GetPad(0)->GetCharJustDown(' ')) +// ++gGameState; +// else if (CPad::GetPad(0)->GetAltJustDown()) +// ++gGameState; +// else if (CPad::GetPad(0)->GetTabJustDown()) +// ++gGameState; + + break; + } + + case GS_INIT_INTRO_MPEG: + { +//#ifndef NO_MOVIES +// CloseClip(); +// CoUninitialize(); +//#endif +// +// if (CMenuManager::OS_Language == LANG_FRENCH || CMenuManager::OS_Language == LANG_GERMAN) +// PlayMovieInWindow(cmdShow, "movies\\GTAtitlesGER.mpg"); +// else +// PlayMovieInWindow(cmdShow, "movies\\GTAtitles.mpg"); + + gGameState = GS_INTRO_MPEG; + TRACE("gGameState = GS_INTRO_MPEG;"); + break; + } + + case GS_INTRO_MPEG: + { +// CPad::UpdatePads(); +// +// if (startupDeactivate || ControlsManager.GetJoyButtonJustDown() != 0) + ++gGameState; +// else if (CPad::GetPad(0)->GetLeftMouseJustDown()) +// ++gGameState; +// else if (CPad::GetPad(0)->GetEnterJustDown()) +// ++gGameState; +// else if (CPad::GetPad(0)->GetCharJustDown(' ')) +// ++gGameState; +// else if (CPad::GetPad(0)->GetAltJustDown()) +// ++gGameState; +// else if (CPad::GetPad(0)->GetTabJustDown()) +// ++gGameState; + + break; + } + + case GS_INIT_ONCE: + { + //CoUninitialize(); + +#ifdef PS2_MENU + extern char version_name[64]; + if ( CGame::frenchGame || CGame::germanGame ) + LoadingScreen(NULL, version_name, "loadsc24"); + else + LoadingScreen(NULL, version_name, "loadsc0"); + + printf("Into TheGame!!!\n"); +#else + LoadingScreen(nil, nil, "loadsc0"); + // LoadingScreen(nil, nil, "loadsc0"); // duplicate +#endif + if ( !CGame::InitialiseOnceAfterRW() ) + RsGlobal.quit = TRUE; + +#ifdef PS2_MENU + gGameState = GS_INIT_PLAYING_GAME; +#else + gGameState = GS_INIT_FRONTEND; + TRACE("gGameState = GS_INIT_FRONTEND;"); +#endif + break; + } +#ifndef PS2_MENU + case GS_INIT_FRONTEND: + { + LoadingScreen(nil, nil, "loadsc0"); + // LoadingScreen(nil, nil, "loadsc0"); // duplicate + + FrontEndMenuManager.m_bGameNotLoaded = true; + + FrontEndMenuManager.m_bStartUpFrontEndRequested = true; + + if ( defaultFullscreenRes ) + { + defaultFullscreenRes = FALSE; + FrontEndMenuManager.m_nPrefsVideoMode = GcurSelVM; + FrontEndMenuManager.m_nDisplayVideoMode = GcurSelVM; + } + + gGameState = GS_FRONTEND; + TRACE("gGameState = GS_FRONTEND;"); + break; + } + + case GS_FRONTEND: + { + if(!WindowIconified) + RsEventHandler(rsFRONTENDIDLE, nil); + +#ifdef PS2_MENU + if ( !FrontEndMenuManager.m_bMenuActive || TheMemoryCard.m_bWantToLoad ) +#else + if ( !FrontEndMenuManager.m_bMenuActive || FrontEndMenuManager.m_bWantToLoad ) +#endif + { + gGameState = GS_INIT_PLAYING_GAME; + TRACE("gGameState = GS_INIT_PLAYING_GAME;"); + } + +#ifdef PS2_MENU + if (TheMemoryCard.m_bWantToLoad ) +#else + if ( FrontEndMenuManager.m_bWantToLoad ) +#endif + { + InitialiseGame(); + FrontEndMenuManager.m_bGameNotLoaded = false; + gGameState = GS_PLAYING_GAME; + TRACE("gGameState = GS_PLAYING_GAME;"); + } + break; + } +#endif + + case GS_INIT_PLAYING_GAME: + { +#ifdef PS2_MENU + CGame::Initialise("DATA\\GTA3.DAT"); + + //LoadingScreen("Starting Game", NULL, GetRandomSplashScreen()); + + if ( TheMemoryCard.CheckCardInserted(CARD_ONE) == CMemoryCard::NO_ERR_SUCCESS + && TheMemoryCard.ChangeDirectory(CARD_ONE, TheMemoryCard.Cards[CARD_ONE].dir) + && TheMemoryCard.FindMostRecentFileName(CARD_ONE, TheMemoryCard.MostRecentFile) == true + && TheMemoryCard.CheckDataNotCorrupt(TheMemoryCard.MostRecentFile)) + { + strcpy(TheMemoryCard.LoadFileName, TheMemoryCard.MostRecentFile); + TheMemoryCard.b_FoundRecentSavedGameWantToLoad = true; + + if (CMenuManager::m_PrefsLanguage != TheMemoryCard.GetLanguageToLoad()) + { + CMenuManager::m_PrefsLanguage = TheMemoryCard.GetLanguageToLoad(); + TheText.Unload(); + TheText.Load(); + } + + CGame::currLevel = (eLevelName)TheMemoryCard.GetLevelToLoad(); + } +#else + InitialiseGame(); + + FrontEndMenuManager.m_bGameNotLoaded = false; +#endif + gGameState = GS_PLAYING_GAME; + TRACE("gGameState = GS_PLAYING_GAME;"); + break; + } + + case GS_PLAYING_GAME: + { + float ms = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerMillisecond(); + if ( RwInitialised ) + { + if (!FrontEndMenuManager.m_PrefsFrameLimiter || (1000.0f / (float)RsGlobal.maxFPS) < ms) + RsEventHandler(rsIDLE, (void *)TRUE); + } + break; + } + } + } + else + { + if ( RwCameraBeginUpdate(Scene.camera) ) + { + RwCameraEndUpdate(Scene.camera); + ForegroundApp = TRUE; + RsEventHandler(rsACTIVATE, (void *)TRUE); + } + + } + } + + + /* + * About to shut down - block resize events again... + */ + RwInitialised = FALSE; + + FrontEndMenuManager.UnloadTextures(); +#ifdef PS2_MENU + if ( !(FrontEndMenuManager.m_bWantToRestart || TheMemoryCard.b_FoundRecentSavedGameWantToLoad)) + break; +#else + if ( !FrontEndMenuManager.m_bWantToRestart ) + break; +#endif + + CPad::ResetCheats(); + CPad::StopPadsShaking(); + + DMAudio.ChangeMusicMode(MUSICMODE_DISABLE); + +#ifdef PS2_MENU + CGame::ShutDownForRestart(); +#endif + + CTimer::Stop(); + +#ifdef PS2_MENU + if (FrontEndMenuManager.m_bWantToRestart || TheMemoryCard.b_FoundRecentSavedGameWantToLoad) + { + if (TheMemoryCard.b_FoundRecentSavedGameWantToLoad) + { + FrontEndMenuManager.m_bWantToRestart = true; + TheMemoryCard.m_bWantToLoad = true; + } + + CGame::InitialiseWhenRestarting(); + DMAudio.ChangeMusicMode(MUSICMODE_GAME); + FrontEndMenuManager.m_bWantToRestart = false; + + continue; + } + + CGame::ShutDown(); + CTimer::Stop(); + + break; +#else + if ( FrontEndMenuManager.m_bWantToLoad ) + { + CGame::ShutDownForRestart(); + CGame::InitialiseWhenRestarting(); + DMAudio.ChangeMusicMode(MUSICMODE_GAME); + LoadSplash(GetLevelSplashScreen(CGame::currLevel)); + FrontEndMenuManager.m_bWantToLoad = false; + } + else + { +#if !defined(RW_DC) +#ifndef MASTER + if ( gbModelViewer ) + CAnimViewer::Shutdown(); + else +#endif +#endif + if ( gGameState == GS_PLAYING_GAME ) + CGame::ShutDown(); + + CTimer::Stop(); + + if ( FrontEndMenuManager.m_bFirstTime == true ) + { + gGameState = GS_INIT_FRONTEND; + TRACE("gGameState = GS_INIT_FRONTEND;"); + } + else + { + gGameState = GS_INIT_PLAYING_GAME; + TRACE("gGameState = GS_INIT_PLAYING_GAME;"); + } + } + + FrontEndMenuManager.m_bFirstTime = false; + FrontEndMenuManager.m_bWantToRestart = false; +#endif + } + + #if !defined(RW_DC) +#ifndef MASTER + if ( gbModelViewer ) + CAnimViewer::Shutdown(); + else +#endif +#endif + if ( gGameState == GS_PLAYING_GAME ) + CGame::ShutDown(); + + DMAudio.Terminate(); + + _psFreeVideoModeList(); + + + /* + * Tidy up the 3D (RenderWare) components of the application... + */ + RsEventHandler(rsRWTERMINATE, nil); + + /* + * Free the platform dependent data... + */ + RsEventHandler(rsTERMINATE, nil); + +#ifdef _WIN32 + /* + * Free the argv strings... + */ + free(argv); + + SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &SavedStickyKeys, SPIF_SENDCHANGE); + SystemParametersInfo(SPI_SETPOWEROFFACTIVE, TRUE, nil, SPIF_SENDCHANGE); + SystemParametersInfo(SPI_SETLOWPOWERACTIVE, TRUE, nil, SPIF_SENDCHANGE); + SetErrorMode(0); +#endif + +#if defined(WITH_PROF) && !defined(DC_SIM) + if(profiling) { + profiling = false; + profiler_clean_up(); + } +#endif + + VmuProfiler::destroyInstance(); + return 0; +} + +/* + ***************************************************************************** + */ + +RwV2d leftStickPos; +RwV2d rightStickPos; + +void CapturePad(RwInt32 padID) +{ + // int8 glfwPad = -1; + + // if( padID == 0 ) + // glfwPad = PSGLOBAL(joy1id); + // else if( padID == 1) + // glfwPad = PSGLOBAL(joy2id); + // else + // assert("invalid padID"); + + // if ( glfwPad == -1 ) + // return; + + // int numButtons, numAxes; + // const uint8 *buttons = glfwGetJoystickButtons(glfwPad, &numButtons); + // const float *axes = glfwGetJoystickAxes(glfwPad, &numAxes); + // GLFWgamepadstate gamepadState; + + // if (ControlsManager.m_bFirstCapture == false) { + // memcpy(&ControlsManager.m_OldState, &ControlsManager.m_NewState, sizeof(ControlsManager.m_NewState)); + // } else { + // // In case connected gamepad doesn't have L-R trigger axes. + // ControlsManager.m_NewState.mappedButtons[15] = ControlsManager.m_NewState.mappedButtons[16] = 0; + // } + + // ControlsManager.m_NewState.buttons = (uint8*)buttons; + // ControlsManager.m_NewState.numButtons = numButtons; + // ControlsManager.m_NewState.id = glfwPad; + // ControlsManager.m_NewState.isGamepad = glfwGetGamepadState(glfwPad, &gamepadState); + // if (ControlsManager.m_NewState.isGamepad) { + // memcpy(&ControlsManager.m_NewState.mappedButtons, gamepadState.buttons, sizeof(gamepadState.buttons)); + // float lt = gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER], rt = gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER]; + + // // glfw returns 0.0 for non-existent axises(which is bullocks) so we treat it as deadzone, and keep value of previous frame. + // // otherwise if this axis is present, -1 = released, 1 = pressed + // if (lt != 0.0f) + // ControlsManager.m_NewState.mappedButtons[15] = lt > -0.8f; + + // if (rt != 0.0f) + // ControlsManager.m_NewState.mappedButtons[16] = rt > -0.8f; + // } + // // TODO? L2-R2 axes(not buttons-that's fine) on joysticks that don't have SDL gamepad mapping AREN'T handled, and I think it's impossible to do without mapping. + + // if (ControlsManager.m_bFirstCapture == true) { + // memcpy(&ControlsManager.m_OldState, &ControlsManager.m_NewState, sizeof(ControlsManager.m_NewState)); + + // ControlsManager.m_bFirstCapture = false; + // } + + // RsPadButtonStatus bs; + // bs.padID = padID; + + // RsPadEventHandler(rsPADBUTTONUP, (void *)&bs); + + // // Gamepad axes are guaranteed to return 0.0f if that particular gamepad doesn't have that axis. + // // And that's really good for sticks, because gamepads return 0.0 for them when sticks are in released state. + // if ( glfwPad != -1 ) { + // leftStickPos.x = ControlsManager.m_NewState.isGamepad ? gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_X] : numAxes >= 1 ? axes[0] : 0.0f; + // leftStickPos.y = ControlsManager.m_NewState.isGamepad ? gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_Y] : numAxes >= 2 ? axes[1] : 0.0f; + + // rightStickPos.x = ControlsManager.m_NewState.isGamepad ? gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_X] : numAxes >= 3 ? axes[2] : 0.0f; + // rightStickPos.y = ControlsManager.m_NewState.isGamepad ? gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y] : numAxes >= 4 ? axes[3] : 0.0f; + // } + + // { + // if (CPad::m_bMapPadOneToPadTwo) + // bs.padID = 1; + + // RsPadEventHandler(rsPADBUTTONUP, (void *)&bs); + // RsPadEventHandler(rsPADBUTTONDOWN, (void *)&bs); + // } + + // { + // if (CPad::m_bMapPadOneToPadTwo) + // bs.padID = 1; + + // CPad *pad = CPad::GetPad(bs.padID); + + // if ( Abs(leftStickPos.x) > 0.3f ) + // pad->PCTempJoyState.LeftStickX = (int32)(leftStickPos.x * 128.0f); + + // if ( Abs(leftStickPos.y) > 0.3f ) + // pad->PCTempJoyState.LeftStickY = (int32)(leftStickPos.y * 128.0f); + + // if ( Abs(rightStickPos.x) > 0.3f ) + // pad->PCTempJoyState.RightStickX = (int32)(rightStickPos.x * 128.0f); + + // if ( Abs(rightStickPos.y) > 0.3f ) + // pad->PCTempJoyState.RightStickY = (int32)(rightStickPos.y * 128.0f); + // } + + // _psHandleVibration(); + + return; +} +#if 0 +void joysChangeCB(int jid, int event) +{ + if (event == GLFW_CONNECTED && !IsThisJoystickBlacklisted(jid)) { + if (PSGLOBAL(joy1id) == -1) { + PSGLOBAL(joy1id) = jid; +#ifdef DETECT_JOYSTICK_MENU + strcpy(gSelectedJoystickName, glfwGetJoystickName(jid)); +#endif + // This is behind LOAD_INI_SETTINGS, because otherwise the Init call below will destroy/overwrite your bindings. +#ifdef LOAD_INI_SETTINGS + int count; + glfwGetJoystickButtons(PSGLOBAL(joy1id), &count); + ControlsManager.InitDefaultControlConfigJoyPad(count); +#endif + } else if (PSGLOBAL(joy2id) == -1) + PSGLOBAL(joy2id) = jid; + + } else if (event == GLFW_DISCONNECTED) { + if (PSGLOBAL(joy1id) == jid) { + PSGLOBAL(joy1id) = -1; + } else if (PSGLOBAL(joy2id) == jid) + PSGLOBAL(joy2id) = -1; + } +} +#endif + +#if (defined(_MSC_VER)) +int strcasecmp(const char* str1, const char* str2) +{ + return _strcmpi(str1, str2); +} +int strncasecmp(const char *str1, const char *str2, size_t len) +{ + return _strnicmp(str1, str2, len); +} +#endif +#endif diff --git a/src/miami/skel/dc/dc.h b/src/miami/skel/dc/dc.h new file mode 100644 index 00000000..a843f115 --- /dev/null +++ b/src/miami/skel/dc/dc.h @@ -0,0 +1,7 @@ +#pragma once +#include + +std::string getBuildId(); +const char* getSourceId(); +const char* getCIJobId(); +const char* getExecutableTag(); \ No newline at end of file diff --git a/src/miami/skel/events.cpp b/src/miami/skel/events.cpp new file mode 100644 index 00000000..87447819 --- /dev/null +++ b/src/miami/skel/events.cpp @@ -0,0 +1,831 @@ +#include "common.h" +#include "Pad.h" +#include "ControllerConfig.h" +#include "Frontend.h" +#include "Camera.h" + +#include "rwcore.h" +#include "skeleton.h" +#include "events.h" + + +/* + ***************************************************************************** + */ +static RsEventStatus +HandleKeyDown(RsKeyStatus *keyStatus) +{ + CPad *pad0 = CPad::GetPad(0); + CPad *pad1 = CPad::GetPad(1); + + RwInt32 c = keyStatus->keyCharCode; + + if ( c != rsNULL ) + { + switch (c) + { + case rsESC: + { + CPad::TempKeyState.ESC = 255; + break; + } + + case rsINS: + { + CPad::TempKeyState.INS = 255; + break; + } + + case rsDEL: + { + CPad::TempKeyState.DEL = 255; + break; + } + + case rsHOME: + { + CPad::TempKeyState.HOME = 255; + break; + } + + case rsEND: + { + CPad::TempKeyState.END = 255; + break; + } + + case rsPGUP: + { + CPad::TempKeyState.PGUP = 255; + break; + } + + case rsPGDN: + { + CPad::TempKeyState.PGDN = 255; + break; + } + + case rsUP: + { + CPad::TempKeyState.UP = 255; + break; + } + + case rsDOWN: + { + CPad::TempKeyState.DOWN = 255; + break; + } + + case rsLEFT: + { + CPad::TempKeyState.LEFT = 255; + break; + } + + case rsRIGHT: + { + CPad::TempKeyState.RIGHT = 255; + break; + } + + case rsNUMLOCK: + { + CPad::TempKeyState.NUMLOCK = 255; + break; + } + + case rsPADDEL: + { + CPad::TempKeyState.DECIMAL = 255; + break; + } + + case rsPADEND: + { + CPad::TempKeyState.NUM1 = 255; + break; + } + + case rsPADDOWN: + { + CPad::TempKeyState.NUM2 = 255; + break; + } + + case rsPADPGDN: + { + CPad::TempKeyState.NUM3 = 255; + break; + } + + case rsPADLEFT: + { + CPad::TempKeyState.NUM4 = 255; + break; + } + + case rsPAD5: + { + CPad::TempKeyState.NUM5 = 255; + break; + } + + case rsPADRIGHT: + { + CPad::TempKeyState.NUM6 = 255; + break; + } + + case rsPADHOME: + { + CPad::TempKeyState.NUM7 = 255; + break; + } + + case rsPADUP: + { + CPad::TempKeyState.NUM8 = 255; + break; + } + + case rsPADPGUP: + { + CPad::TempKeyState.NUM9 = 255; + break; + } + + case rsPADINS: + { + CPad::TempKeyState.NUM0 = 255; + break; + } + + case rsDIVIDE: + { + CPad::TempKeyState.DIV = 255; + break; + } + + case rsTIMES: + { + CPad::TempKeyState.MUL = 255; + break; + } + + case rsMINUS: + { + CPad::TempKeyState.SUB = 255; + break; + } + + case rsPADENTER: + { + CPad::TempKeyState.ENTER = 255; + break; + } + + case rsPLUS: + { + CPad::TempKeyState.ADD = 255; + break; + } + + case rsENTER: + { + CPad::TempKeyState.EXTENTER = 255; + break; + } + + case rsSCROLL: + { + CPad::TempKeyState.SCROLLLOCK = 255; + break; + } + + case rsPAUSE: + { + CPad::TempKeyState.PAUSE = 255; + break; + } + + case rsBACKSP: + { + CPad::TempKeyState.BACKSP = 255; + break; + } + + case rsTAB: + { + CPad::TempKeyState.TAB = 255; + break; + } + + case rsCAPSLK: + { + CPad::TempKeyState.CAPSLOCK = 255; + break; + } + + case rsLSHIFT: + { + CPad::TempKeyState.LSHIFT = 255; + break; + } + + case rsSHIFT: + { + CPad::TempKeyState.SHIFT = 255; + break; + } + + case rsRSHIFT: + { + CPad::TempKeyState.RSHIFT = 255; + break; + } + + case rsLCTRL: + { + CPad::TempKeyState.LCTRL = 255; + break; + } + + case rsRCTRL: + { + CPad::TempKeyState.RCTRL = 255; + break; + } + + case rsLALT: + { + CPad::TempKeyState.LALT = 255; + break; + } + + case rsRALT: + { + CPad::TempKeyState.RALT = 255; + break; + } + + + case rsLWIN: + { + CPad::TempKeyState.LWIN = 255; + break; + } + + case rsRWIN: + { + CPad::TempKeyState.RWIN = 255; + break; + } + + case rsAPPS: + { + CPad::TempKeyState.APPS = 255; + break; + } + + case rsF1: + case rsF2: + case rsF3: + case rsF4: + case rsF5: + case rsF6: + case rsF7: + case rsF8: + case rsF9: + case rsF10: + case rsF11: + case rsF12: + { + CPad::TempKeyState.F[c - rsF1] = 255; + break; + } + + default: + { + if ( c < 255 ) + { + CPad::TempKeyState.VK_KEYS[c] = 255; + pad0->AddToPCCheatString(c); + } + break; + } + } + + if ( CPad::m_bMapPadOneToPadTwo ) + { + if ( c == 'D' ) pad1->PCTempKeyState.LeftStickX = 128; + if ( c == 'A' ) pad1->PCTempKeyState.LeftStickX = -128; + if ( c == 'W' ) pad1->PCTempKeyState.LeftStickY = 128; + if ( c == 'S' ) pad1->PCTempKeyState.LeftStickY = -128; + if ( c == 'J' ) pad1->PCTempKeyState.RightStickX = 128; + if ( c == 'G' ) pad1->PCTempKeyState.RightStickX = -128; + if ( c == 'Y' ) pad1->PCTempKeyState.RightStickY = 128; + if ( c == 'H' ) pad1->PCTempKeyState.RightStickY = -128; + if ( c == 'Z' ) pad1->PCTempKeyState.LeftShoulder1 = 255; + if ( c == 'X' ) pad1->PCTempKeyState.LeftShoulder2 = 255; + if ( c == 'C' ) pad1->PCTempKeyState.RightShoulder1 = 255; + if ( c == 'V' ) pad1->PCTempKeyState.RightShoulder2 = 255; + if ( c == 'O' ) pad1->PCTempKeyState.DPadUp = 255; + if ( c == 'L' ) pad1->PCTempKeyState.DPadDown = 255; + if ( c == 'K' ) pad1->PCTempKeyState.DPadLeft = 255; + if ( c == ';' ) pad1->PCTempKeyState.DPadRight = 255; + if ( c == 'B' ) pad1->PCTempKeyState.Start = 255; + if ( c == 'N' ) pad1->PCTempKeyState.Select = 255; + if ( c == 'M' ) pad1->PCTempKeyState.Square = 255; + if ( c == ',' ) pad1->PCTempKeyState.Triangle = 255; + if ( c == '.' ) pad1->PCTempKeyState.Cross = 255; + if ( c == '/' ) pad1->PCTempKeyState.Circle = 255; + if ( c == rsRSHIFT ) pad1->PCTempKeyState.LeftShock = 255; + if ( c == rsRCTRL ) pad1->PCTempKeyState.RightShock = 255; + } + } + + return rsEVENTPROCESSED; +} + + +static RsEventStatus +HandleKeyUp(RsKeyStatus *keyStatus) +{ + CPad *pad0 = CPad::GetPad(0); + CPad *pad1 = CPad::GetPad(1); + + RwInt32 c = keyStatus->keyCharCode; + + if ( c != rsNULL ) + { + switch (c) + { + case rsESC: + { + CPad::TempKeyState.ESC = 0; + break; + } + + case rsINS: + { + CPad::TempKeyState.INS = 0; + break; + } + + case rsDEL: + { + CPad::TempKeyState.DEL = 0; + break; + } + + case rsHOME: + { + CPad::TempKeyState.HOME = 0; + break; + } + + case rsEND: + { + CPad::TempKeyState.END = 0; + break; + } + + case rsPGUP: + { + CPad::TempKeyState.PGUP = 0; + break; + } + + case rsPGDN: + { + CPad::TempKeyState.PGDN = 0; + break; + } + + case rsUP: + { + CPad::TempKeyState.UP = 0; + break; + } + + case rsDOWN: + { + CPad::TempKeyState.DOWN = 0; + break; + } + + case rsLEFT: + { + CPad::TempKeyState.LEFT = 0; + break; + } + + case rsRIGHT: + { + CPad::TempKeyState.RIGHT = 0; + break; + } + + case rsNUMLOCK: + { + CPad::TempKeyState.NUMLOCK = 0; + break; + } + + case rsPADDEL: + { + CPad::TempKeyState.DECIMAL = 0; + break; + } + + case rsPADEND: + { + CPad::TempKeyState.NUM1 = 0; + break; + } + + case rsPADDOWN: + { + CPad::TempKeyState.NUM2 = 0; + break; + } + + case rsPADPGDN: + { + CPad::TempKeyState.NUM3 = 0; + break; + } + + case rsPADLEFT: + { + CPad::TempKeyState.NUM4 = 0; + break; + } + + case rsPAD5: + { + CPad::TempKeyState.NUM5 = 0; + break; + } + + case rsPADRIGHT: + { + CPad::TempKeyState.NUM6 = 0; + break; + } + + case rsPADHOME: + { + CPad::TempKeyState.NUM7 = 0; + break; + } + + case rsPADUP: + { + CPad::TempKeyState.NUM8 = 0; + break; + } + + case rsPADPGUP: + { + CPad::TempKeyState.NUM9 = 0; + break; + } + + case rsPADINS: + { + CPad::TempKeyState.NUM0 = 0; + break; + } + + case rsDIVIDE: + { + CPad::TempKeyState.DIV = 0; + break; + } + + case rsTIMES: + { + CPad::TempKeyState.MUL = 0; + break; + } + + case rsMINUS: + { + CPad::TempKeyState.SUB = 0; + break; + } + + case rsPADENTER: + { + CPad::TempKeyState.ENTER = 0; + break; + } + + case rsPLUS: + { + CPad::TempKeyState.ADD = 0; + break; + } + + case rsENTER: + { + CPad::TempKeyState.EXTENTER = 0; + break; + } + + case rsSCROLL: + { + CPad::TempKeyState.SCROLLLOCK = 0; + break; + } + + case rsPAUSE: + { + CPad::TempKeyState.PAUSE = 0; + break; + } + + case rsBACKSP: + { + CPad::TempKeyState.BACKSP = 0; + break; + } + + case rsTAB: + { + CPad::TempKeyState.TAB = 0; + break; + } + + case rsCAPSLK: + { + CPad::TempKeyState.CAPSLOCK = 0; + break; + } + + case rsLSHIFT: + { + CPad::TempKeyState.LSHIFT = 0; + break; + } + + case rsSHIFT: + { + CPad::TempKeyState.SHIFT = 0; + break; + } + + case rsRSHIFT: + { + CPad::TempKeyState.RSHIFT = 0; + break; + } + + case rsLCTRL: + { + CPad::TempKeyState.LCTRL = 0; + break; + } + + case rsRCTRL: + { + CPad::TempKeyState.RCTRL = 0; + break; + } + + case rsLALT: + { + CPad::TempKeyState.LALT = 0; + break; + } + + case rsRALT: + { + CPad::TempKeyState.RALT = 0; + break; + } + + + case rsLWIN: + { + CPad::TempKeyState.LWIN = 0; + break; + } + + case rsRWIN: + { + CPad::TempKeyState.RWIN = 0; + break; + } + + case rsAPPS: + { + CPad::TempKeyState.APPS = 0; + break; + } + + case rsF1: + case rsF2: + case rsF3: + case rsF4: + case rsF5: + case rsF6: + case rsF7: + case rsF8: + case rsF9: + case rsF10: + case rsF11: + case rsF12: + { + CPad::TempKeyState.F[c - rsF1] = 0; + break; + } + + default: + { + if ( c < 255 ) + { + CPad::TempKeyState.VK_KEYS[c] = 0; + } + break; + } + } + + if ( CPad::m_bMapPadOneToPadTwo ) + { + if ( c == 'D' ) pad1->PCTempKeyState.LeftStickX = 0; + if ( c == 'A' ) pad1->PCTempKeyState.LeftStickX = 0; + if ( c == 'W' ) pad1->PCTempKeyState.LeftStickY = 0; + if ( c == 'S' ) pad1->PCTempKeyState.LeftStickY = 0; + if ( c == 'J' ) pad1->PCTempKeyState.RightStickX = 0; + if ( c == 'G' ) pad1->PCTempKeyState.RightStickX = 0; + if ( c == 'Y' ) pad1->PCTempKeyState.RightStickY = 0; + if ( c == 'H' ) pad1->PCTempKeyState.RightStickY = 0; + if ( c == 'Z' ) pad1->PCTempKeyState.LeftShoulder1 = 0; + if ( c == 'X' ) pad1->PCTempKeyState.LeftShoulder2 = 0; + if ( c == 'C' ) pad1->PCTempKeyState.RightShoulder1 = 0; + if ( c == 'V' ) pad1->PCTempKeyState.RightShoulder2 = 0; + if ( c == 'O' ) pad1->PCTempKeyState.DPadUp = 0; + if ( c == 'L' ) pad1->PCTempKeyState.DPadDown = 0; + if ( c == 'K' ) pad1->PCTempKeyState.DPadLeft = 0; + if ( c == ';' ) pad1->PCTempKeyState.DPadRight = 0; + if ( c == 'B' ) pad1->PCTempKeyState.Start = 0; + if ( c == 'N' ) pad1->PCTempKeyState.Select = 0; + if ( c == 'M' ) pad1->PCTempKeyState.Square = 0; + if ( c == ',' ) pad1->PCTempKeyState.Triangle = 0; + if ( c == '.' ) pad1->PCTempKeyState.Cross = 0; + if ( c == '/' ) pad1->PCTempKeyState.Circle = 0; + if ( c == rsRSHIFT ) pad1->PCTempKeyState.LeftShock = 0; + if ( c == rsRCTRL ) pad1->PCTempKeyState.RightShock = 0; + } + } + + return rsEVENTPROCESSED; +} + + +/* + ***************************************************************************** + */ +static RsEventStatus +KeyboardHandler(RsEvent event, void *param) +{ + /* + * ...then the application events, if necessary... + */ + switch( event ) + { + case rsKEYDOWN: + { + return HandleKeyDown((RsKeyStatus *)param); + } + + case rsKEYUP: + { + return HandleKeyUp((RsKeyStatus *)param); + } + + default: + { + return rsEVENTNOTPROCESSED; + } + } +} + +/* + ***************************************************************************** + */ +static RsEventStatus +HandlePadButtonDown(RsPadButtonStatus *padButtonStatus) +{ + bool bPadTwo = false; + int32 padNumber = padButtonStatus->padID; + + CPad *pad = CPad::GetPad(padNumber); + + if ( CPad::m_bMapPadOneToPadTwo ) + padNumber = 1; + + if ( padNumber == 1 ) + bPadTwo = true; + + ControlsManager.UpdateJoyButtonState(padNumber); + + for ( int32 i = 0; i < _TODOCONST(16); i++ ) + { + RsPadButtons btn = rsPADNULL; + if ( ControlsManager.m_aButtonStates[i] == TRUE ) + btn = (RsPadButtons)(i + 1); + + if ( FrontEndMenuManager.m_bMenuActive || bPadTwo ) + ControlsManager.UpdateJoyInConfigMenus_ButtonDown(btn, padNumber); + else + ControlsManager.AffectControllerStateOn_ButtonDown(btn, JOYSTICK); + } + + return rsEVENTPROCESSED; +} + + +/* + ***************************************************************************** + */ +static RsEventStatus +HandlePadButtonUp(RsPadButtonStatus *padButtonStatus) +{ + bool bPadTwo = false; + int32 padNumber = padButtonStatus->padID; + + CPad *pad = CPad::GetPad(padNumber); + + if ( CPad::m_bMapPadOneToPadTwo ) + padNumber = 1; + + if ( padNumber == 1 ) + bPadTwo = true; + + bool bCam = false; + int16 mode = TheCamera.Cams[TheCamera.ActiveCam].Mode; + if ( mode == CCam::MODE_FLYBY || mode == CCam::MODE_FIXED ) + bCam = true; + + ControlsManager.UpdateJoyButtonState(padNumber); + + for ( int32 i = 1; i < _TODOCONST(16); i++ ) + { + RsPadButtons btn = rsPADNULL; + if ( ControlsManager.m_aButtonStates[i] == FALSE ) + btn = (RsPadButtons)(i + 1); // bug ?, cycle begins from 1(not zero), 1+1==2==rsPADBUTTON2, so we skip rsPADBUTTON1, right ? + + if ( FrontEndMenuManager.m_bMenuActive || bPadTwo || bCam ) + ControlsManager.UpdateJoyInConfigMenus_ButtonUp(btn, padNumber); + else + ControlsManager.AffectControllerStateOn_ButtonUp(btn, JOYSTICK); + } + + return rsEVENTPROCESSED; +} + +/* + ***************************************************************************** + */ +static RsEventStatus +PadHandler(RsEvent event, void *param) +{ + switch( event ) + { + case rsPADBUTTONDOWN: + { + return HandlePadButtonDown((RsPadButtonStatus *)param); + } + + case rsPADBUTTONUP: + { + return HandlePadButtonUp((RsPadButtonStatus *)param); + } + + default: + { + return rsEVENTNOTPROCESSED; + } + } +} + + +/* + ***************************************************************************** + */ +RwBool +AttachInputDevices(void) +{ +#ifndef IGNORE_MOUSE_KEYBOARD + RsInputDeviceAttach(rsKEYBOARD, KeyboardHandler); +#endif + + RsInputDeviceAttach(rsPAD, PadHandler); + + return TRUE; +} diff --git a/src/miami/skel/events.h b/src/miami/skel/events.h new file mode 100644 index 00000000..ef812ebc --- /dev/null +++ b/src/miami/skel/events.h @@ -0,0 +1,7 @@ +#ifndef EVENTS_H +#define EVENTS_H + +#include +#include "skeleton.h" + +#endif /* EVENTS_H */ diff --git a/src/miami/skel/glfw/glfw.cpp b/src/miami/skel/glfw/glfw.cpp new file mode 100644 index 00000000..f1f97ee3 --- /dev/null +++ b/src/miami/skel/glfw/glfw.cpp @@ -0,0 +1,2609 @@ +#if defined RW_GL3 && !defined LIBRW_SDL2 + +#ifdef _WIN32 +#include +#include +#include +#include +#include +#include + +DWORD _dwOperatingSystemVersion; +#include "resource.h" +#else +long _dwOperatingSystemVersion; +#ifndef __SWITCH__ +#ifndef __APPLE__ +#include +#else +#include +#include +#endif +#endif +#include +#include +#include +#include +#endif + +#include "common.h" +#if (defined(_MSC_VER)) +#include +#endif /* (defined(_MSC_VER)) */ +#include +#include "rwcore.h" +#include "skeleton.h" +#include "platform.h" +#include "crossplatform.h" + +#include "main.h" +#include "FileMgr.h" +#include "Text.h" +#include "Pad.h" +#include "Timer.h" +#include "DMAudio.h" +#include "ControllerConfig.h" +#include "Frontend.h" +#include "Game.h" +#include "PCSave.h" +#include "MemoryCard.h" +#include "Sprite2d.h" +#include "AnimViewer.h" +#include "Font.h" +#include "MemoryMgr.h" + +// This is defined on project-level, via premake5 or cmake +#ifdef GET_KEYBOARD_INPUT_FROM_X11 +#include +#include +#define GLFW_EXPOSE_NATIVE_X11 +#include +#endif + +#ifdef _WIN32 +#define GLFW_EXPOSE_NATIVE_WIN32 +#include +#endif + +#define MAX_SUBSYSTEMS (16) + +rw::EngineOpenParams openParams; + +static RwBool ForegroundApp = TRUE; +static RwBool WindowIconified = FALSE; +static RwBool WindowFocused = TRUE; + +static RwBool RwInitialised = FALSE; + +static RwSubSystemInfo GsubSysInfo[MAX_SUBSYSTEMS]; +static RwInt32 GnumSubSystems = 0; +static RwInt32 GcurSel = 0, GcurSelVM = 0; + +static RwBool useDefault; + +// What is that for anyway? +#ifndef IMPROVED_VIDEOMODE +static RwBool defaultFullscreenRes = TRUE; +#else +static RwBool defaultFullscreenRes = FALSE; +static RwInt32 bestWndMode = -1; +#endif + +static psGlobalType PsGlobal; + + +#define PSGLOBAL(var) (((psGlobalType *)(RsGlobal.ps))->var) + +size_t _dwMemAvailPhys; +RwUInt32 gGameState; + +#ifdef DETECT_JOYSTICK_MENU +char gSelectedJoystickName[128] = ""; +#endif + +/* + ***************************************************************************** + */ +void _psCreateFolder(const char *path) +{ +#ifdef _WIN32 + HANDLE hfle = CreateFile(path, GENERIC_READ, + FILE_SHARE_READ, + nil, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL, + nil); + + if ( hfle == INVALID_HANDLE_VALUE ) + CreateDirectory(path, nil); + else + CloseHandle(hfle); +#else + struct stat info; + char fullpath[PATH_MAX]; + realpath(path, fullpath); + + if (lstat(fullpath, &info) != 0) { + if (errno == ENOENT || (errno != EACCES && !S_ISDIR(info.st_mode))) { + mkdir(fullpath, 0755); + } + } +#endif +} + +/* + ***************************************************************************** + */ +const char *_psGetUserFilesFolder() +{ +#if defined USE_MY_DOCUMENTS && defined _WIN32 + HKEY hKey = NULL; + + static CHAR szUserFiles[256]; + + if ( RegOpenKeyEx(HKEY_CURRENT_USER, + REGSTR_PATH_SPECIAL_FOLDERS, + REG_OPTION_RESERVED, + KEY_READ, + &hKey) == ERROR_SUCCESS ) + { + DWORD KeyType; + DWORD KeycbData = sizeof(szUserFiles); + if ( RegQueryValueEx(hKey, + "Personal", + NULL, + &KeyType, + (LPBYTE)szUserFiles, + &KeycbData) == ERROR_SUCCESS ) + { + RegCloseKey(hKey); + strcat(szUserFiles, "\\GTA Vice City User Files"); + _psCreateFolder(szUserFiles); + return szUserFiles; + } + + RegCloseKey(hKey); + } + + strcpy(szUserFiles, "data"); + return szUserFiles; +#else + static char szUserFiles[256]; + strcpy(szUserFiles, "userfiles"); + _psCreateFolder(szUserFiles); + return szUserFiles; +#endif +} + +/* + ***************************************************************************** + */ +RwBool +psCameraBeginUpdate(RwCamera *camera) +{ + if ( !RwCameraBeginUpdate(Scene.camera) ) + { + ForegroundApp = FALSE; + RsEventHandler(rsACTIVATE, (void *)FALSE); + return FALSE; + } + + return TRUE; +} + +/* + ***************************************************************************** + */ +void +psCameraShowRaster(RwCamera *camera) +{ +#ifdef LEGACY_MENU_OPTIONS + if (FrontEndMenuManager.m_PrefsVsync || FrontEndMenuManager.m_bMenuActive) +#else + if (FrontEndMenuManager.m_PrefsFrameLimiter || FrontEndMenuManager.m_bMenuActive) +#endif + RwCameraShowRaster(camera, PSGLOBAL(window), rwRASTERFLIPWAITVSYNC); + else + RwCameraShowRaster(camera, PSGLOBAL(window), rwRASTERFLIPDONTWAIT); + + return; +} + +/* + ***************************************************************************** + */ +RwImage * +psGrabScreen(RwCamera *pCamera) +{ +#ifndef LIBRW + RwRaster *pRaster = RwCameraGetRaster(pCamera); + if (RwImage *pImage = RwImageCreate(pRaster->width, pRaster->height, 32)) { + RwImageAllocatePixels(pImage); + RwImageSetFromRaster(pImage, pRaster); + return pImage; + } +#else + rw::Image *image = RwCameraGetRaster(pCamera)->toImage(); + image->removeMask(); + if(image) + return image; +#endif + return nil; +} + +/* + ***************************************************************************** + */ +#ifdef _WIN32 +#pragma comment( lib, "Winmm.lib" ) // Needed for time +RwUInt32 +psTimer(void) +{ + RwUInt32 time; + + TIMECAPS TimeCaps; + + timeGetDevCaps(&TimeCaps, sizeof(TIMECAPS)); + + timeBeginPeriod(TimeCaps.wPeriodMin); + + time = (RwUInt32) timeGetTime(); + + timeEndPeriod(TimeCaps.wPeriodMin); + + return time; +} +#else +double +psTimer(void) +{ + struct timespec start; +#if defined(CLOCK_MONOTONIC_RAW) + clock_gettime(CLOCK_MONOTONIC_RAW, &start); +#elif defined(CLOCK_MONOTONIC_FAST) + clock_gettime(CLOCK_MONOTONIC_FAST, &start); +#else + clock_gettime(CLOCK_MONOTONIC, &start); +#endif + return start.tv_sec * 1000.0 + start.tv_nsec/1000000.0; +} +#endif + + +/* + ***************************************************************************** + */ +void +psMouseSetPos(RwV2d *pos) +{ + glfwSetCursorPos(PSGLOBAL(window), pos->x, pos->y); + + PSGLOBAL(lastMousePos.x) = (RwInt32)pos->x; + + PSGLOBAL(lastMousePos.y) = (RwInt32)pos->y; + + return; +} + +/* + ***************************************************************************** + */ +RwMemoryFunctions* +psGetMemoryFunctions(void) +{ +#ifdef USE_CUSTOM_ALLOCATOR + return &memFuncs; +#else + return nil; +#endif +} + +/* + ***************************************************************************** + */ +RwBool +psInstallFileSystem(void) +{ + return (TRUE); +} + + +/* + ***************************************************************************** + */ +RwBool +psNativeTextureSupport(void) +{ + return true; +} + +/* + ***************************************************************************** + */ +#ifdef UNDER_CE +#define CMDSTR LPWSTR +#else +#define CMDSTR LPSTR +#endif + +/* + ***************************************************************************** + */ + +#ifdef __SWITCH__ + +static HidVibrationValue SwitchVibrationValues[2]; +static HidVibrationDeviceHandle SwitchVibrationDeviceHandles[2][2]; +static HidVibrationDeviceHandle SwitchVibrationDeviceGC; + +static PadState SwitchPad; + +static Result HidInitializationResult[2]; +static Result HidInitializationGCResult; + +static void _psInitializeVibration() +{ + HidInitializationResult[0] = hidInitializeVibrationDevices(SwitchVibrationDeviceHandles[0], 2, HidNpadIdType_Handheld, HidNpadStyleTag_NpadHandheld); + if(R_FAILED(HidInitializationResult[0])) { + printf("Failed to initialize VibrationDevice for Handheld Mode\n"); + } + HidInitializationResult[1] = hidInitializeVibrationDevices(SwitchVibrationDeviceHandles[1], 2, HidNpadIdType_No1, HidNpadStyleSet_NpadFullCtrl); + if(R_FAILED(HidInitializationResult[1])) { + printf("Failed to initialize VibrationDevice for Detached Mode\n"); + } + HidInitializationGCResult = hidInitializeVibrationDevices(&SwitchVibrationDeviceGC, 1, HidNpadIdType_No1, HidNpadStyleTag_NpadGc); + if(R_FAILED(HidInitializationResult[1])) { + printf("Failed to initialize VibrationDevice for GC Mode\n"); + } + + SwitchVibrationValues[0].freq_low = 160.0f; + SwitchVibrationValues[0].freq_high = 320.0f; + + padConfigureInput(1, HidNpadStyleSet_NpadFullCtrl); + padInitializeDefault(&SwitchPad); +} + +static void _psHandleVibration() +{ + padUpdate(&SwitchPad); + + uint8 target_device = padIsHandheld(&SwitchPad) ? 0 : 1; + + if(R_SUCCEEDED(HidInitializationResult[target_device])) { + CPad* pad = CPad::GetPad(0); + + // value conversion based on SDL2 switch port + SwitchVibrationValues[0].amp_high = SwitchVibrationValues[0].amp_low = pad->ShakeFreq == 0 ? 0.0f : 320.0f; + SwitchVibrationValues[0].freq_low = pad->ShakeFreq == 0.0 ? 160.0f : (float)pad->ShakeFreq * 1.26f; + SwitchVibrationValues[0].freq_high = pad->ShakeFreq == 0.0 ? 320.0f : (float)pad->ShakeFreq * 1.26f; + + if (pad->ShakeDur < CTimer::GetTimeStepInMilliseconds()) + pad->ShakeDur = 0; + else + pad->ShakeDur -= CTimer::GetTimeStepInMilliseconds(); + if (pad->ShakeDur == 0) pad->ShakeFreq = 0; + + + if(target_device == 1 && R_SUCCEEDED(HidInitializationGCResult)) { + // gamecube rumble + hidSendVibrationGcErmCommand(SwitchVibrationDeviceGC, pad->ShakeFreq > 0 ? HidVibrationGcErmCommand_Start : HidVibrationGcErmCommand_Stop); + } + + memcpy(&SwitchVibrationValues[1], &SwitchVibrationValues[0], sizeof(HidVibrationValue)); + hidSendVibrationValues(SwitchVibrationDeviceHandles[target_device], SwitchVibrationValues, 2); + } +} +#else +static void _psInitializeVibration() {} +static void _psHandleVibration() {} +#endif + +/* + ***************************************************************************** + */ +RwBool +psInitialize(void) +{ + PsGlobal.lastMousePos.x = PsGlobal.lastMousePos.y = 0.0f; + + RsGlobal.ps = &PsGlobal; + + PsGlobal.fullScreen = FALSE; + PsGlobal.cursorIsInWindow = FALSE; + WindowFocused = TRUE; + WindowIconified = FALSE; + + PsGlobal.joy1id = -1; + PsGlobal.joy2id = -1; + + CFileMgr::Initialise(); + +#ifdef PS2_MENU + CPad::Initialise(); + CPad::GetPad(0)->Mode = 0; + + CGame::frenchGame = false; + CGame::germanGame = false; + CGame::nastyGame = true; + CMenuManager::m_PrefsAllowNastyGame = true; + +#ifndef _WIN32 + // Mandatory for Linux(Unix? Posix?) to set lang. to environment lang. + setlocale(LC_ALL, ""); + + char *systemLang, *keyboardLang; + + systemLang = setlocale (LC_ALL, NULL); + keyboardLang = setlocale (LC_CTYPE, NULL); + + short lang; + lang = !strncmp(systemLang, "fr_",3) ? LANG_FRENCH : + !strncmp(systemLang, "de_",3) ? LANG_GERMAN : + !strncmp(systemLang, "en_",3) ? LANG_ENGLISH : + !strncmp(systemLang, "it_",3) ? LANG_ITALIAN : + !strncmp(systemLang, "es_",3) ? LANG_SPANISH : + LANG_OTHER; +#else + WORD lang = PRIMARYLANGID(GetSystemDefaultLCID()); +#endif + + if ( lang == LANG_ITALIAN ) + CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_ITALIAN; + else if ( lang == LANG_SPANISH ) + CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_SPANISH; + else if ( lang == LANG_GERMAN ) + { + CGame::germanGame = true; + CGame::nastyGame = false; + CMenuManager::m_PrefsAllowNastyGame = false; + CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_GERMAN; + } + else if ( lang == LANG_FRENCH ) + { + CGame::frenchGame = true; + CGame::nastyGame = false; + CMenuManager::m_PrefsAllowNastyGame = false; + CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_FRENCH; + } + else + CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_AMERICAN; + + FrontEndMenuManager.InitialiseMenuContentsAfterLoadingGame(); + + TheMemoryCard.Init(); +#else + C_PcSave::SetSaveDirectory(_psGetUserFilesFolder()); + + InitialiseLanguage(); + +#endif + + _psInitializeVibration(); + + gGameState = GS_START_UP; + TRACE("gGameState = GS_START_UP"); +#ifdef _WIN32 + OSVERSIONINFO verInfo; + verInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + GetVersionEx(&verInfo); + + _dwOperatingSystemVersion = OS_WIN95; + + if ( verInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ) + { + if ( verInfo.dwMajorVersion == 4 ) + { + debug("Operating System is WinNT\n"); + _dwOperatingSystemVersion = OS_WINNT; + } + else if ( verInfo.dwMajorVersion == 5 ) + { + debug("Operating System is Win2000\n"); + _dwOperatingSystemVersion = OS_WIN2000; + } + else if ( verInfo.dwMajorVersion > 5 ) + { + debug("Operating System is WinXP or greater\n"); + _dwOperatingSystemVersion = OS_WINXP; + } + } + else if ( verInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) + { + if ( verInfo.dwMajorVersion > 4 || verInfo.dwMajorVersion == 4 && verInfo.dwMinorVersion != 0 ) + { + debug("Operating System is Win98\n"); + _dwOperatingSystemVersion = OS_WIN98; + } + else + { + debug("Operating System is Win95\n"); + _dwOperatingSystemVersion = OS_WIN95; + } + } +#else + _dwOperatingSystemVersion = OS_WINXP; // To fool other classes +#endif + + +#ifndef PS2_MENU + FrontEndMenuManager.LoadSettings(); +#endif + + +#ifdef _WIN32 + MEMORYSTATUS memstats; + GlobalMemoryStatus(&memstats); + + _dwMemAvailPhys = memstats.dwAvailPhys; + + debug("Physical memory size %u\n", memstats.dwTotalPhys); + debug("Available physical memory %u\n", memstats.dwAvailPhys); +#elif defined (__APPLE__) + uint64_t size = 0; + uint64_t page_size = 0; + size_t uint64_len = sizeof(uint64_t); + size_t ull_len = sizeof(unsigned long long); + sysctl((int[]){CTL_HW, HW_PAGESIZE}, 2, &page_size, &ull_len, NULL, 0); + sysctl((int[]){CTL_HW, HW_MEMSIZE}, 2, &size, &uint64_len, NULL, 0); + vm_statistics_data_t vm_stat; + mach_msg_type_number_t count = HOST_VM_INFO_COUNT; + host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vm_stat, &count); + _dwMemAvailPhys = (uint64_t)(vm_stat.free_count * page_size); + debug("Physical memory size %llu\n", _dwMemAvailPhys); + debug("Available physical memory %llu\n", size); +#elif defined (__SWITCH__) + svcGetInfo(&_dwMemAvailPhys, InfoType_UsedMemorySize, CUR_PROCESS_HANDLE, 0); + debug("Physical memory size %llu\n", _dwMemAvailPhys); +#else +#ifndef __APPLE__ + struct sysinfo systemInfo; + sysinfo(&systemInfo); + _dwMemAvailPhys = systemInfo.freeram; + debug("Physical memory size %u\n", systemInfo.totalram); + debug("Available physical memory %u\n", systemInfo.freeram); +#else + uint64_t size = 0; + uint64_t page_size = 0; + size_t uint64_len = sizeof(uint64_t); + size_t ull_len = sizeof(unsigned long long); + sysctl((int[]){CTL_HW, HW_PAGESIZE}, 2, &page_size, &ull_len, NULL, 0); + sysctl((int[]){CTL_HW, HW_MEMSIZE}, 2, &size, &uint64_len, NULL, 0); + vm_statistics_data_t vm_stat; + mach_msg_type_number_t count = HOST_VM_INFO_COUNT; + host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vm_stat, &count); + _dwMemAvailPhys = (uint64_t)(vm_stat.free_count * page_size); + debug("Physical memory size %llu\n", _dwMemAvailPhys); + debug("Available physical memory %llu\n", size); +#endif + _dwOperatingSystemVersion = OS_WINXP; // To fool other classes +#endif + + TheText.Unload(); + + return TRUE; +} + + +/* + ***************************************************************************** + */ +void +psTerminate(void) +{ + return; +} + +/* + ***************************************************************************** + */ +static RwChar **_VMList; + +RwInt32 _psGetNumVideModes() +{ + return RwEngineGetNumVideoModes(); +} + +/* + ***************************************************************************** + */ +RwBool _psFreeVideoModeList() +{ + RwInt32 numModes; + RwInt32 i; + + numModes = _psGetNumVideModes(); + + if ( _VMList == nil ) + return TRUE; + + for ( i = 0; i < numModes; i++ ) + { + RwFree(_VMList[i]); + } + + RwFree(_VMList); + + _VMList = nil; + + return TRUE; +} + +/* + ***************************************************************************** + */ +RwChar **_psGetVideoModeList() +{ + RwInt32 numModes; + RwInt32 i; + + if ( _VMList != nil ) + { + return _VMList; + } + + numModes = RwEngineGetNumVideoModes(); + + _VMList = (RwChar **)RwCalloc(numModes, sizeof(RwChar*)); + + for ( i = 0; i < numModes; i++ ) + { + RwVideoMode vm; + + RwEngineGetVideoModeInfo(&vm, i); + + if ( vm.flags & rwVIDEOMODEEXCLUSIVE ) + { + _VMList[i] = (RwChar*)RwCalloc(100, sizeof(RwChar)); + rwsprintf(_VMList[i],"%d X %d X %d", vm.width, vm.height, vm.depth); + } + else + _VMList[i] = nil; + } + + return _VMList; +} + +/* + ***************************************************************************** + */ +void _psSelectScreenVM(RwInt32 videoMode) +{ + RwTexDictionarySetCurrent( nil ); + + FrontEndMenuManager.UnloadTextures(); + + if (!_psSetVideoMode(RwEngineGetCurrentSubSystem(), videoMode)) + { + RsGlobal.quit = TRUE; + + printf("ERROR: Failed to select new screen resolution\n"); + } + else + FrontEndMenuManager.LoadAllTextures(); +} + +/* + ***************************************************************************** + */ + +RwBool IsForegroundApp() +{ + return !!ForegroundApp; +} +/* +UINT GetBestRefreshRate(UINT width, UINT height, UINT depth) +{ + LPDIRECT3D8 d3d = Direct3DCreate8(D3D_SDK_VERSION); + + ASSERT(d3d != nil); + + UINT refreshRate = INT_MAX; + D3DFORMAT format; + + if ( depth == 32 ) + format = D3DFMT_X8R8G8B8; + else if ( depth == 24 ) + format = D3DFMT_R8G8B8; + else + format = D3DFMT_R5G6B5; + + UINT modeCount = d3d->GetAdapterModeCount(GcurSel); + + for ( UINT i = 0; i < modeCount; i++ ) + { + D3DDISPLAYMODE mode; + + d3d->EnumAdapterModes(GcurSel, i, &mode); + + if ( mode.Width == width && mode.Height == height && mode.Format == format ) + { + if ( mode.RefreshRate == 0 ) + return 0; + + if ( mode.RefreshRate < refreshRate && mode.RefreshRate >= 60 ) + refreshRate = mode.RefreshRate; + } + } + +#ifdef FIX_BUGS + d3d->Release(); +#endif + + if ( refreshRate == -1 ) + return -1; + + return refreshRate; +} +*/ +/* + ***************************************************************************** + */ +RwBool +psSelectDevice() +{ + RwVideoMode vm; + RwInt32 subSysNum; + RwInt32 AutoRenderer = 0; + + + RwBool modeFound = FALSE; + + if ( !useDefault ) + { + GnumSubSystems = RwEngineGetNumSubSystems(); + if ( !GnumSubSystems ) + { + return FALSE; + } + + /* Just to be sure ... */ + GnumSubSystems = (GnumSubSystems > MAX_SUBSYSTEMS) ? MAX_SUBSYSTEMS : GnumSubSystems; + + /* Get the names of all the sub systems */ + for (subSysNum = 0; subSysNum < GnumSubSystems; subSysNum++) + { + RwEngineGetSubSystemInfo(&GsubSysInfo[subSysNum], subSysNum); + } + + /* Get the default selection */ + GcurSel = RwEngineGetCurrentSubSystem(); +#ifdef IMPROVED_VIDEOMODE + if(FrontEndMenuManager.m_nPrefsSubsystem < GnumSubSystems) + GcurSel = FrontEndMenuManager.m_nPrefsSubsystem; +#endif + } + + /* Set the driver to use the correct sub system */ + if (!RwEngineSetSubSystem(GcurSel)) + { + return FALSE; + } + +#ifdef IMPROVED_VIDEOMODE + FrontEndMenuManager.m_nPrefsSubsystem = GcurSel; +#endif + +#ifndef IMPROVED_VIDEOMODE + if ( !useDefault ) + { + if ( _psGetVideoModeList()[FrontEndMenuManager.m_nDisplayVideoMode] && FrontEndMenuManager.m_nDisplayVideoMode ) + { + FrontEndMenuManager.m_nPrefsVideoMode = FrontEndMenuManager.m_nDisplayVideoMode; + GcurSelVM = FrontEndMenuManager.m_nDisplayVideoMode; + } + else + { +#ifdef DEFAULT_NATIVE_RESOLUTION + // get the native video mode + HDC hDevice = GetDC(NULL); + int w = GetDeviceCaps(hDevice, HORZRES); + int h = GetDeviceCaps(hDevice, VERTRES); + int d = GetDeviceCaps(hDevice, BITSPIXEL); +#else + const int w = 640; + const int h = 480; + const int d = 16; +#endif + while ( !modeFound && GcurSelVM < RwEngineGetNumVideoModes() ) + { + RwEngineGetVideoModeInfo(&vm, GcurSelVM); + if ( defaultFullscreenRes && vm.width != w + || vm.height != h + || vm.depth != d + || !(vm.flags & rwVIDEOMODEEXCLUSIVE) ) + ++GcurSelVM; + else + modeFound = TRUE; + } + + if ( !modeFound ) + { +#ifdef DEFAULT_NATIVE_RESOLUTION + GcurSelVM = 1; +#else + printf("WARNING: Cannot find 640x480 video mode, selecting device cancelled\n"); + return FALSE; +#endif + } + } + } +#else + if ( !useDefault ) + { + if(FrontEndMenuManager.m_nPrefsWidth == 0 || + FrontEndMenuManager.m_nPrefsHeight == 0 || + FrontEndMenuManager.m_nPrefsDepth == 0){ + // Defaults if nothing specified + const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + FrontEndMenuManager.m_nPrefsWidth = mode->width; + FrontEndMenuManager.m_nPrefsHeight = mode->height; + FrontEndMenuManager.m_nPrefsDepth = 32; + FrontEndMenuManager.m_nPrefsWindowed = 0; + } + + // Find the videomode that best fits what we got from the settings file + RwInt32 bestFsMode = -1; + RwInt32 bestWidth = -1; + RwInt32 bestHeight = -1; + RwInt32 bestDepth = -1; + for(GcurSelVM = 0; GcurSelVM < RwEngineGetNumVideoModes(); GcurSelVM++){ + RwEngineGetVideoModeInfo(&vm, GcurSelVM); + + if (!(vm.flags & rwVIDEOMODEEXCLUSIVE)){ + bestWndMode = GcurSelVM; + } else { + // try the largest one that isn't larger than what we wanted + if(vm.width >= bestWidth && vm.width <= FrontEndMenuManager.m_nPrefsWidth && + vm.height >= bestHeight && vm.height <= FrontEndMenuManager.m_nPrefsHeight && + vm.depth >= bestDepth && vm.depth <= FrontEndMenuManager.m_nPrefsDepth){ + bestWidth = vm.width; + bestHeight = vm.height; + bestDepth = vm.depth; + bestFsMode = GcurSelVM; + } + } + } + + if(bestFsMode < 0){ + printf("WARNING: Cannot find desired video mode, selecting device cancelled\n"); + return FALSE; + } + GcurSelVM = bestFsMode; + + FrontEndMenuManager.m_nDisplayVideoMode = GcurSelVM; + FrontEndMenuManager.m_nPrefsVideoMode = FrontEndMenuManager.m_nDisplayVideoMode; + + FrontEndMenuManager.m_nSelectedScreenMode = FrontEndMenuManager.m_nPrefsWindowed; + } +#endif + + RwEngineGetVideoModeInfo(&vm, GcurSelVM); + +#ifdef IMPROVED_VIDEOMODE + if (FrontEndMenuManager.m_nPrefsWindowed) + GcurSelVM = bestWndMode; + + // Now GcurSelVM is 0 but vm has sizes(and fullscreen flag) of the video mode we want, that's why we changed the rwVIDEOMODEEXCLUSIVE conditions below + FrontEndMenuManager.m_nPrefsWidth = vm.width; + FrontEndMenuManager.m_nPrefsHeight = vm.height; + FrontEndMenuManager.m_nPrefsDepth = vm.depth; +#endif + +#ifndef PS2_MENU + FrontEndMenuManager.m_nCurrOption = 0; +#endif + + /* Set up the video mode and set the apps window + * dimensions to match */ + if (!RwEngineSetVideoMode(GcurSelVM)) + { + return FALSE; + } + /* + TODO + if (vm.flags & rwVIDEOMODEEXCLUSIVE) + { + debug("%dx%dx%d", vm.width, vm.height, vm.depth); + + UINT refresh = GetBestRefreshRate(vm.width, vm.height, vm.depth); + + if ( refresh != (UINT)-1 ) + { + debug("refresh %d", refresh); + RwD3D8EngineSetRefreshRate((RwUInt32)refresh); + } + } + */ +#ifndef IMPROVED_VIDEOMODE + if (vm.flags & rwVIDEOMODEEXCLUSIVE) + { + RsGlobal.maximumWidth = vm.width; + RsGlobal.maximumHeight = vm.height; + RsGlobal.width = vm.width; + RsGlobal.height = vm.height; + + PSGLOBAL(fullScreen) = TRUE; + } +#else + RsGlobal.maximumWidth = FrontEndMenuManager.m_nPrefsWidth; + RsGlobal.maximumHeight = FrontEndMenuManager.m_nPrefsHeight; + RsGlobal.width = FrontEndMenuManager.m_nPrefsWidth; + RsGlobal.height = FrontEndMenuManager.m_nPrefsHeight; + + PSGLOBAL(fullScreen) = !FrontEndMenuManager.m_nPrefsWindowed; +#endif + +#ifdef MULTISAMPLING + RwD3D8EngineSetMultiSamplingLevels(1 << FrontEndMenuManager.m_nPrefsMSAALevel); +#endif + return TRUE; +} + +#ifndef GET_KEYBOARD_INPUT_FROM_X11 +void keypressCB(GLFWwindow* window, int key, int scancode, int action, int mods); +#endif +void resizeCB(GLFWwindow* window, int width, int height); +void scrollCB(GLFWwindow* window, double xoffset, double yoffset); +void cursorCB(GLFWwindow* window, double xpos, double ypos); +void cursorEnterCB(GLFWwindow* window, int entered); +void windowFocusCB(GLFWwindow* window, int focused); +void windowIconifyCB(GLFWwindow* window, int iconified); +void joysChangeCB(int jid, int event); + +bool IsThisJoystickBlacklisted(int i) +{ +#ifndef DETECT_JOYSTICK_MENU + return false; +#else + if (glfwJoystickIsGamepad(i)) + return false; + + const char* joyname = glfwGetJoystickName(i); + + if (gSelectedJoystickName[0] != '\0' && + strncmp(joyname, gSelectedJoystickName, strlen(gSelectedJoystickName)) == 0) + return false; + + return true; +#endif +} + +void _InputInitialiseJoys() +{ + PSGLOBAL(joy1id) = -1; + PSGLOBAL(joy2id) = -1; + + // Load our gamepad mappings. +#define SDL_GAMEPAD_DB_PATH "gamecontrollerdb.txt" + FILE *f = fopen(SDL_GAMEPAD_DB_PATH, "rb"); + if (f) { + fseek(f, 0, SEEK_END); + size_t fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + char *db = (char*)malloc(fsize + 1); + if (fread(db, 1, fsize, f) == fsize) { + db[fsize] = '\0'; + + if (glfwUpdateGamepadMappings(db) == GLFW_FALSE) + Error("glfwUpdateGamepadMappings didn't succeed, check " SDL_GAMEPAD_DB_PATH ".\n"); + } else + Error("fread on " SDL_GAMEPAD_DB_PATH " wasn't successful.\n"); + + free(db); + fclose(f); + } else + printf("You don't seem to have copied " SDL_GAMEPAD_DB_PATH " file from reVC/gamefiles to GTA: Vice City directory. Some gamepads may not be recognized.\n"); + +#undef SDL_GAMEPAD_DB_PATH + + // But always overwrite it with the one in SDL_GAMECONTROLLERCONFIG. + char const* EnvControlConfig = getenv("SDL_GAMECONTROLLERCONFIG"); + if (EnvControlConfig != nil) { + glfwUpdateGamepadMappings(EnvControlConfig); + } + + for (int i = 0; i <= GLFW_JOYSTICK_LAST; i++) { + if (glfwJoystickPresent(i) && !IsThisJoystickBlacklisted(i)) { + if (PSGLOBAL(joy1id) == -1) + PSGLOBAL(joy1id) = i; + else if (PSGLOBAL(joy2id) == -1) + PSGLOBAL(joy2id) = i; + else + break; + } + } + + if (PSGLOBAL(joy1id) != -1) { + int count; + glfwGetJoystickButtons(PSGLOBAL(joy1id), &count); +#ifdef DETECT_JOYSTICK_MENU + strcpy(gSelectedJoystickName, glfwGetJoystickName(PSGLOBAL(joy1id))); +#endif + ControlsManager.InitDefaultControlConfigJoyPad(count); + } +} + +int lastCursorMode = GLFW_CURSOR_HIDDEN; +long _InputInitialiseMouse(bool exclusive) +{ + // Disabled = keep cursor centered and hide + lastCursorMode = exclusive ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_HIDDEN; + glfwSetInputMode(PSGLOBAL(window), GLFW_CURSOR, lastCursorMode); + return 0; +} + +void _InputShutdownMouse() +{ + // Not needed +} + +// Not "needs exclusive" on GLFW, but more like "needs to change mode" +bool _InputMouseNeedsExclusive() +{ + // That was the cause of infamous mouse bug on Win. + + RwVideoMode vm; + RwEngineGetVideoModeInfo(&vm, GcurSelVM); + + // If windowed, free the cursor on menu(where this func. is called and DISABLED-HIDDEN transition is done accordingly) + // If it's fullscreen, be sure that it didn't stuck on HIDDEN. + return !(vm.flags & rwVIDEOMODEEXCLUSIVE) || lastCursorMode == GLFW_CURSOR_HIDDEN; +} + +void psPostRWinit(void) +{ + RwVideoMode vm; + RwEngineGetVideoModeInfo(&vm, GcurSelVM); + + glfwSetFramebufferSizeCallback(PSGLOBAL(window), resizeCB); +#ifndef IGNORE_MOUSE_KEYBOARD +#ifndef GET_KEYBOARD_INPUT_FROM_X11 + glfwSetKeyCallback(PSGLOBAL(window), keypressCB); +#endif + glfwSetScrollCallback(PSGLOBAL(window), scrollCB); + glfwSetCursorPosCallback(PSGLOBAL(window), cursorCB); + glfwSetCursorEnterCallback(PSGLOBAL(window), cursorEnterCB); +#endif + glfwSetWindowIconifyCallback(PSGLOBAL(window), windowIconifyCB); + glfwSetWindowFocusCallback(PSGLOBAL(window), windowFocusCB); + glfwSetJoystickCallback(joysChangeCB); + + _InputInitialiseJoys(); + _InputInitialiseMouse(false); + + if(!(vm.flags & rwVIDEOMODEEXCLUSIVE)) + glfwSetWindowSize(PSGLOBAL(window), RsGlobal.maximumWidth, RsGlobal.maximumHeight); + + // Make sure all keys are released + CPad::GetPad(0)->Clear(true); + CPad::GetPad(1)->Clear(true); +} + +/* + ***************************************************************************** + */ +RwBool _psSetVideoMode(RwInt32 subSystem, RwInt32 videoMode) +{ + RwInitialised = FALSE; + + RsEventHandler(rsRWTERMINATE, nil); + + GcurSel = subSystem; + GcurSelVM = videoMode; + + useDefault = TRUE; + + if ( RsEventHandler(rsRWINITIALIZE, &openParams) == rsEVENTERROR ) + return FALSE; + + RwInitialised = TRUE; + useDefault = FALSE; + + RwRect r; + + r.x = 0; + r.y = 0; + r.w = RsGlobal.maximumWidth; + r.h = RsGlobal.maximumHeight; + + RsEventHandler(rsCAMERASIZE, &r); + + psPostRWinit(); + + return TRUE; +} + + +/* + ***************************************************************************** + */ +static RwChar ** +CommandLineToArgv(RwChar *cmdLine, RwInt32 *argCount) +{ + RwInt32 numArgs = 0; + RwBool inArg, inString; + RwInt32 i, len; + RwChar *res, *str, **aptr; + + len = strlen(cmdLine); + + /* + * Count the number of arguments... + */ + inString = FALSE; + inArg = FALSE; + + for(i=0; i<=len; i++) + { + if( cmdLine[i] == '"' ) + { + inString = !inString; + } + + if( (cmdLine[i] <= ' ' && !inString) || i == len ) + { + if( inArg ) + { + inArg = FALSE; + + numArgs++; + } + } + else if( !inArg ) + { + inArg = TRUE; + } + } + + /* + * Allocate memory for result... + */ + res = (RwChar *)malloc(sizeof(RwChar *) * numArgs + len + 1); + str = res + sizeof(RwChar *) * numArgs; + aptr = (RwChar **)res; + + strcpy(str, cmdLine); + + /* + * Walk through cmdLine again this time setting pointer to each arg... + */ + inArg = FALSE; + inString = FALSE; + + for(i=0; i<=len; i++) + { + if( cmdLine[i] == '"' ) + { + inString = !inString; + } + + if( (cmdLine[i] <= ' ' && !inString) || i == len ) + { + if( inArg ) + { + if( str[i-1] == '"' ) + { + str[i-1] = '\0'; + } + else + { + str[i] = '\0'; + } + + inArg = FALSE; + } + } + else if( !inArg && cmdLine[i] != '"' ) + { + inArg = TRUE; + + *aptr++ = &str[i]; + } + } + + *argCount = numArgs; + + return (RwChar **)res; +} + +/* + ***************************************************************************** + */ +void InitialiseLanguage() +{ +#ifndef _WIN32 + // Mandatory for Linux(Unix? Posix?) to set lang. to environment lang. + setlocale(LC_ALL, ""); + + char *systemLang, *keyboardLang; + + systemLang = setlocale (LC_ALL, NULL); + keyboardLang = setlocale (LC_CTYPE, NULL); + + short primUserLCID, primSystemLCID; + primUserLCID = primSystemLCID = !strncmp(systemLang, "fr_",3) ? LANG_FRENCH : + !strncmp(systemLang, "de_",3) ? LANG_GERMAN : + !strncmp(systemLang, "en_",3) ? LANG_ENGLISH : + !strncmp(systemLang, "it_",3) ? LANG_ITALIAN : + !strncmp(systemLang, "es_",3) ? LANG_SPANISH : + LANG_OTHER; + + short primLayout = !strncmp(keyboardLang, "fr_",3) ? LANG_FRENCH : (!strncmp(keyboardLang, "de_",3) ? LANG_GERMAN : LANG_ENGLISH); + + short subUserLCID, subSystemLCID; + subUserLCID = subSystemLCID = !strncmp(systemLang, "en_AU",5) ? SUBLANG_ENGLISH_AUS : SUBLANG_OTHER; + short subLayout = !strncmp(keyboardLang, "en_AU",5) ? SUBLANG_ENGLISH_AUS : SUBLANG_OTHER; + +#else + WORD primUserLCID = PRIMARYLANGID(GetSystemDefaultLCID()); + WORD primSystemLCID = PRIMARYLANGID(GetUserDefaultLCID()); + WORD primLayout = PRIMARYLANGID((DWORD)GetKeyboardLayout(0)); + + WORD subUserLCID = SUBLANGID(GetSystemDefaultLCID()); + WORD subSystemLCID = SUBLANGID(GetUserDefaultLCID()); + WORD subLayout = SUBLANGID((DWORD)GetKeyboardLayout(0)); +#endif + if ( primUserLCID == LANG_GERMAN + || primSystemLCID == LANG_GERMAN + || primLayout == LANG_GERMAN ) + { + CGame::nastyGame = false; + FrontEndMenuManager.m_PrefsAllowNastyGame = false; + CGame::germanGame = true; + } + + if ( primUserLCID == LANG_FRENCH + || primSystemLCID == LANG_FRENCH + || primLayout == LANG_FRENCH ) + { + CGame::nastyGame = false; + FrontEndMenuManager.m_PrefsAllowNastyGame = false; + CGame::frenchGame = true; + } + + if ( subUserLCID == SUBLANG_ENGLISH_AUS + || subSystemLCID == SUBLANG_ENGLISH_AUS + || subLayout == SUBLANG_ENGLISH_AUS ) + CGame::noProstitutes = true; + +#ifdef NASTY_GAME + CGame::nastyGame = true; + FrontEndMenuManager.m_PrefsAllowNastyGame = true; + CGame::noProstitutes = false; +#endif + + int32 lang; + + switch ( primSystemLCID ) + { + case LANG_GERMAN: + { + lang = LANG_GERMAN; + break; + } + case LANG_FRENCH: + { + lang = LANG_FRENCH; + break; + } + case LANG_SPANISH: + { + lang = LANG_SPANISH; + break; + } + case LANG_ITALIAN: + { + lang = LANG_ITALIAN; + break; + } + default: + { + lang = ( subSystemLCID == SUBLANG_ENGLISH_AUS ) ? -99 : LANG_ENGLISH; + break; + } + } + + FrontEndMenuManager.OS_Language = primUserLCID; + + switch ( lang ) + { + case LANG_GERMAN: + { + FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_GERMAN; + break; + } + case LANG_SPANISH: + { + FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_SPANISH; + break; + } + case LANG_FRENCH: + { + FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_FRENCH; + break; + } + case LANG_ITALIAN: + { + FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_ITALIAN; + break; + } + default: + { + FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_AMERICAN; + break; + } + } + +#ifndef _WIN32 + // TODO this is needed for strcasecmp to work correctly across all languages, but can these cause other problems?? + setlocale(LC_CTYPE, "C"); + setlocale(LC_COLLATE, "C"); + setlocale(LC_NUMERIC, "C"); +#endif + + TheText.Unload(); + TheText.Load(); +} + +/* + ***************************************************************************** + */ + +void HandleExit() +{ +#ifdef _WIN32 + MSG message; + while ( PeekMessage(&message, nil, 0U, 0U, PM_REMOVE|PM_NOYIELD) ) + { + if( message.message == WM_QUIT ) + { + RsGlobal.quit = TRUE; + } + else + { + TranslateMessage(&message); + DispatchMessage(&message); + } + } +#else + // We now handle terminate message always, why handle on some cases? + return; +#endif +} + +#ifndef _WIN32 +void terminateHandler(int sig, siginfo_t *info, void *ucontext) { + RsGlobal.quit = TRUE; +} + +#ifdef FLUSHABLE_STREAMING +void dummyHandler(int sig){ + // Don't kill the app pls +} +#endif +#endif + +void resizeCB(GLFWwindow* window, int width, int height) { + /* + * Handle event to ensure window contents are displayed during re-size + * as this can be disabled by the user, then if there is not enough + * memory things don't work. + */ + /* redraw window */ + + if (RwInitialised && gGameState == GS_PLAYING_GAME) + { + RsEventHandler(rsIDLE, (void *)TRUE); + } + + if (RwInitialised && height > 0 && width > 0) { + RwRect r; + + // TODO fix artifacts of resizing with mouse + RsGlobal.maximumHeight = height; + RsGlobal.maximumWidth = width; + + r.x = 0; + r.y = 0; + r.w = width; + r.h = height; + + RsEventHandler(rsCAMERASIZE, &r); + } +// glfwSetWindowPos(window, 0, 0); +} + +void scrollCB(GLFWwindow* window, double xoffset, double yoffset) { + PSGLOBAL(mouseWheel) = yoffset; +} + +bool lshiftStatus = false; +bool rshiftStatus = false; + +#ifndef GET_KEYBOARD_INPUT_FROM_X11 +int keymap[GLFW_KEY_LAST + 1]; + +static void +initkeymap(void) +{ + int i; + for (i = 0; i < GLFW_KEY_LAST + 1; i++) + keymap[i] = rsNULL; + + keymap[GLFW_KEY_SPACE] = ' '; + keymap[GLFW_KEY_APOSTROPHE] = '\''; + keymap[GLFW_KEY_COMMA] = ','; + keymap[GLFW_KEY_MINUS] = '-'; + keymap[GLFW_KEY_PERIOD] = '.'; + keymap[GLFW_KEY_SLASH] = '/'; + keymap[GLFW_KEY_0] = '0'; + keymap[GLFW_KEY_1] = '1'; + keymap[GLFW_KEY_2] = '2'; + keymap[GLFW_KEY_3] = '3'; + keymap[GLFW_KEY_4] = '4'; + keymap[GLFW_KEY_5] = '5'; + keymap[GLFW_KEY_6] = '6'; + keymap[GLFW_KEY_7] = '7'; + keymap[GLFW_KEY_8] = '8'; + keymap[GLFW_KEY_9] = '9'; + keymap[GLFW_KEY_SEMICOLON] = ';'; + keymap[GLFW_KEY_EQUAL] = '='; + keymap[GLFW_KEY_A] = 'A'; + keymap[GLFW_KEY_B] = 'B'; + keymap[GLFW_KEY_C] = 'C'; + keymap[GLFW_KEY_D] = 'D'; + keymap[GLFW_KEY_E] = 'E'; + keymap[GLFW_KEY_F] = 'F'; + keymap[GLFW_KEY_G] = 'G'; + keymap[GLFW_KEY_H] = 'H'; + keymap[GLFW_KEY_I] = 'I'; + keymap[GLFW_KEY_J] = 'J'; + keymap[GLFW_KEY_K] = 'K'; + keymap[GLFW_KEY_L] = 'L'; + keymap[GLFW_KEY_M] = 'M'; + keymap[GLFW_KEY_N] = 'N'; + keymap[GLFW_KEY_O] = 'O'; + keymap[GLFW_KEY_P] = 'P'; + keymap[GLFW_KEY_Q] = 'Q'; + keymap[GLFW_KEY_R] = 'R'; + keymap[GLFW_KEY_S] = 'S'; + keymap[GLFW_KEY_T] = 'T'; + keymap[GLFW_KEY_U] = 'U'; + keymap[GLFW_KEY_V] = 'V'; + keymap[GLFW_KEY_W] = 'W'; + keymap[GLFW_KEY_X] = 'X'; + keymap[GLFW_KEY_Y] = 'Y'; + keymap[GLFW_KEY_Z] = 'Z'; + keymap[GLFW_KEY_LEFT_BRACKET] = '['; + keymap[GLFW_KEY_BACKSLASH] = '\\'; + keymap[GLFW_KEY_RIGHT_BRACKET] = ']'; + keymap[GLFW_KEY_GRAVE_ACCENT] = '`'; + keymap[GLFW_KEY_ESCAPE] = rsESC; + keymap[GLFW_KEY_ENTER] = rsENTER; + keymap[GLFW_KEY_TAB] = rsTAB; + keymap[GLFW_KEY_BACKSPACE] = rsBACKSP; + keymap[GLFW_KEY_INSERT] = rsINS; + keymap[GLFW_KEY_DELETE] = rsDEL; + keymap[GLFW_KEY_RIGHT] = rsRIGHT; + keymap[GLFW_KEY_LEFT] = rsLEFT; + keymap[GLFW_KEY_DOWN] = rsDOWN; + keymap[GLFW_KEY_UP] = rsUP; + keymap[GLFW_KEY_PAGE_UP] = rsPGUP; + keymap[GLFW_KEY_PAGE_DOWN] = rsPGDN; + keymap[GLFW_KEY_HOME] = rsHOME; + keymap[GLFW_KEY_END] = rsEND; + keymap[GLFW_KEY_CAPS_LOCK] = rsCAPSLK; + keymap[GLFW_KEY_SCROLL_LOCK] = rsSCROLL; + keymap[GLFW_KEY_NUM_LOCK] = rsNUMLOCK; + keymap[GLFW_KEY_PRINT_SCREEN] = rsNULL; + keymap[GLFW_KEY_PAUSE] = rsPAUSE; + + keymap[GLFW_KEY_F1] = rsF1; + keymap[GLFW_KEY_F2] = rsF2; + keymap[GLFW_KEY_F3] = rsF3; + keymap[GLFW_KEY_F4] = rsF4; + keymap[GLFW_KEY_F5] = rsF5; + keymap[GLFW_KEY_F6] = rsF6; + keymap[GLFW_KEY_F7] = rsF7; + keymap[GLFW_KEY_F8] = rsF8; + keymap[GLFW_KEY_F9] = rsF9; + keymap[GLFW_KEY_F10] = rsF10; + keymap[GLFW_KEY_F11] = rsF11; + keymap[GLFW_KEY_F12] = rsF12; + keymap[GLFW_KEY_F13] = rsNULL; + keymap[GLFW_KEY_F14] = rsNULL; + keymap[GLFW_KEY_F15] = rsNULL; + keymap[GLFW_KEY_F16] = rsNULL; + keymap[GLFW_KEY_F17] = rsNULL; + keymap[GLFW_KEY_F18] = rsNULL; + keymap[GLFW_KEY_F19] = rsNULL; + keymap[GLFW_KEY_F20] = rsNULL; + keymap[GLFW_KEY_F21] = rsNULL; + keymap[GLFW_KEY_F22] = rsNULL; + keymap[GLFW_KEY_F23] = rsNULL; + keymap[GLFW_KEY_F24] = rsNULL; + keymap[GLFW_KEY_F25] = rsNULL; + keymap[GLFW_KEY_KP_0] = rsPADINS; + keymap[GLFW_KEY_KP_1] = rsPADEND; + keymap[GLFW_KEY_KP_2] = rsPADDOWN; + keymap[GLFW_KEY_KP_3] = rsPADPGDN; + keymap[GLFW_KEY_KP_4] = rsPADLEFT; + keymap[GLFW_KEY_KP_5] = rsPAD5; + keymap[GLFW_KEY_KP_6] = rsPADRIGHT; + keymap[GLFW_KEY_KP_7] = rsPADHOME; + keymap[GLFW_KEY_KP_8] = rsPADUP; + keymap[GLFW_KEY_KP_9] = rsPADPGUP; + keymap[GLFW_KEY_KP_DECIMAL] = rsPADDEL; + keymap[GLFW_KEY_KP_DIVIDE] = rsDIVIDE; + keymap[GLFW_KEY_KP_MULTIPLY] = rsTIMES; + keymap[GLFW_KEY_KP_SUBTRACT] = rsMINUS; + keymap[GLFW_KEY_KP_ADD] = rsPLUS; + keymap[GLFW_KEY_KP_ENTER] = rsPADENTER; + keymap[GLFW_KEY_KP_EQUAL] = rsNULL; + keymap[GLFW_KEY_LEFT_SHIFT] = rsLSHIFT; + keymap[GLFW_KEY_LEFT_CONTROL] = rsLCTRL; + keymap[GLFW_KEY_LEFT_ALT] = rsLALT; + keymap[GLFW_KEY_LEFT_SUPER] = rsLWIN; + keymap[GLFW_KEY_RIGHT_SHIFT] = rsRSHIFT; + keymap[GLFW_KEY_RIGHT_CONTROL] = rsRCTRL; + keymap[GLFW_KEY_RIGHT_ALT] = rsRALT; + keymap[GLFW_KEY_RIGHT_SUPER] = rsRWIN; + keymap[GLFW_KEY_MENU] = rsNULL; +} + +void +keypressCB(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (key >= 0 && key <= GLFW_KEY_LAST && action != GLFW_REPEAT) { + RsKeyCodes ks = (RsKeyCodes)keymap[key]; + + if (key == GLFW_KEY_LEFT_SHIFT) + lshiftStatus = action != GLFW_RELEASE; + + if (key == GLFW_KEY_RIGHT_SHIFT) + rshiftStatus = action != GLFW_RELEASE; + + if (action == GLFW_RELEASE) RsKeyboardEventHandler(rsKEYUP, &ks); + else if (action == GLFW_PRESS) RsKeyboardEventHandler(rsKEYDOWN, &ks); + } +} + +#else + +uint32 keymap[512]; // 256 ascii + 256 KeySyms between 0xff00 - 0xffff +bool keyStates[512]; +uint32 keyCodeToKeymapIndex[256]; // cache for physical keys + +#define KEY_MAP_OFFSET (0xff00 - 256) +static void +initkeymap(void) +{ + Display *display = glfwGetX11Display(); + int i; + + for (i = 0; i < ARRAY_SIZE(keymap); i++) + keymap[i] = rsNULL; + + // You can add new ASCII mappings to here freely (but beware that if right hand side of assignment isn't supported on CFont, it'll be blank/won't work on binding screen) + // Right hand side of assigments should always be uppercase counterpart of character + keymap[XK_space] = ' '; + keymap[XK_apostrophe] = '\''; + keymap[XK_ampersand] = '&'; + keymap[XK_percent] = '%'; + keymap[XK_dollar] = '$'; + keymap[XK_comma] = ','; + keymap[XK_minus] = '-'; + keymap[XK_period] = '.'; + keymap[XK_slash] = '/'; + keymap[XK_question] = '?'; + keymap[XK_exclam] = '!'; + keymap[XK_quotedbl] = '"'; + keymap[XK_colon] = ':'; + keymap[XK_semicolon] = ';'; + keymap[XK_equal] = '='; + keymap[XK_bracketleft] = '['; + keymap[XK_backslash] = '\\'; + keymap[XK_bracketright] = ']'; + keymap[XK_grave] = '`'; + keymap[XK_0] = '0'; + keymap[XK_1] = '1'; + keymap[XK_2] = '2'; + keymap[XK_3] = '3'; + keymap[XK_4] = '4'; + keymap[XK_5] = '5'; + keymap[XK_6] = '6'; + keymap[XK_7] = '7'; + keymap[XK_8] = '8'; + keymap[XK_9] = '9'; + keymap[XK_a] = 'A'; + keymap[XK_b] = 'B'; + keymap[XK_c] = 'C'; + keymap[XK_d] = 'D'; + keymap[XK_e] = 'E'; + keymap[XK_f] = 'F'; + keymap[XK_g] = 'G'; + keymap[XK_h] = 'H'; + keymap[XK_i] = 'I'; + keymap[XK_I] = 'I'; // Turkish I problem + keymap[XK_j] = 'J'; + keymap[XK_k] = 'K'; + keymap[XK_l] = 'L'; + keymap[XK_m] = 'M'; + keymap[XK_n] = 'N'; + keymap[XK_o] = 'O'; + keymap[XK_p] = 'P'; + keymap[XK_q] = 'Q'; + keymap[XK_r] = 'R'; + keymap[XK_s] = 'S'; + keymap[XK_t] = 'T'; + keymap[XK_u] = 'U'; + keymap[XK_v] = 'V'; + keymap[XK_w] = 'W'; + keymap[XK_x] = 'X'; + keymap[XK_y] = 'Y'; + keymap[XK_z] = 'Z'; + + // Some of regional but ASCII characters that GTA supports + keymap[XK_agrave] = 0x00c0; + keymap[XK_aacute] = 0x00c1; + keymap[XK_acircumflex] = 0x00c2; + keymap[XK_adiaeresis] = 0x00c4; + + keymap[XK_ae] = 0x00c6; + + keymap[XK_egrave] = 0x00c8; + keymap[XK_eacute] = 0x00c9; + keymap[XK_ecircumflex] = 0x00ca; + keymap[XK_ediaeresis] = 0x00cb; + + keymap[XK_igrave] = 0x00cc; + keymap[XK_iacute] = 0x00cd; + keymap[XK_icircumflex] = 0x00ce; + keymap[XK_idiaeresis] = 0x00cf; + + keymap[XK_ccedilla] = 0x00c7; + keymap[XK_odiaeresis] = 0x00d6; + keymap[XK_udiaeresis] = 0x00dc; + + // These are 0xff00 - 0xffff range of KeySym's, and subtracting KEY_MAP_OFFSET is needed + keymap[XK_Escape - KEY_MAP_OFFSET] = rsESC; + keymap[XK_Return - KEY_MAP_OFFSET] = rsENTER; + keymap[XK_Tab - KEY_MAP_OFFSET] = rsTAB; + keymap[XK_BackSpace - KEY_MAP_OFFSET] = rsBACKSP; + keymap[XK_Insert - KEY_MAP_OFFSET] = rsINS; + keymap[XK_Delete - KEY_MAP_OFFSET] = rsDEL; + keymap[XK_Right - KEY_MAP_OFFSET] = rsRIGHT; + keymap[XK_Left - KEY_MAP_OFFSET] = rsLEFT; + keymap[XK_Down - KEY_MAP_OFFSET] = rsDOWN; + keymap[XK_Up - KEY_MAP_OFFSET] = rsUP; + keymap[XK_Page_Up - KEY_MAP_OFFSET] = rsPGUP; + keymap[XK_Page_Down - KEY_MAP_OFFSET] = rsPGDN; + keymap[XK_Home - KEY_MAP_OFFSET] = rsHOME; + keymap[XK_End - KEY_MAP_OFFSET] = rsEND; + keymap[XK_Caps_Lock - KEY_MAP_OFFSET] = rsCAPSLK; + keymap[XK_Scroll_Lock - KEY_MAP_OFFSET] = rsSCROLL; + keymap[XK_Num_Lock - KEY_MAP_OFFSET] = rsNUMLOCK; + keymap[XK_Pause - KEY_MAP_OFFSET] = rsPAUSE; + + keymap[XK_F1 - KEY_MAP_OFFSET] = rsF1; + keymap[XK_F2 - KEY_MAP_OFFSET] = rsF2; + keymap[XK_F3 - KEY_MAP_OFFSET] = rsF3; + keymap[XK_F4 - KEY_MAP_OFFSET] = rsF4; + keymap[XK_F5 - KEY_MAP_OFFSET] = rsF5; + keymap[XK_F6 - KEY_MAP_OFFSET] = rsF6; + keymap[XK_F7 - KEY_MAP_OFFSET] = rsF7; + keymap[XK_F8 - KEY_MAP_OFFSET] = rsF8; + keymap[XK_F9 - KEY_MAP_OFFSET] = rsF9; + keymap[XK_F10 - KEY_MAP_OFFSET] = rsF10; + keymap[XK_F11 - KEY_MAP_OFFSET] = rsF11; + keymap[XK_F12 - KEY_MAP_OFFSET] = rsF12; + keymap[XK_F13 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F14 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F15 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F16 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F17 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F18 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F19 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F20 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F21 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F22 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F23 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F24 - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_F25 - KEY_MAP_OFFSET] = rsNULL; + + keymap[XK_KP_0 - KEY_MAP_OFFSET] = rsPADINS; + keymap[XK_KP_1 - KEY_MAP_OFFSET] = rsPADEND; + keymap[XK_KP_2 - KEY_MAP_OFFSET] = rsPADDOWN; + keymap[XK_KP_3 - KEY_MAP_OFFSET] = rsPADPGDN; + keymap[XK_KP_4 - KEY_MAP_OFFSET] = rsPADLEFT; + keymap[XK_KP_5 - KEY_MAP_OFFSET] = rsPAD5; + keymap[XK_KP_6 - KEY_MAP_OFFSET] = rsPADRIGHT; + keymap[XK_KP_7 - KEY_MAP_OFFSET] = rsPADHOME; + keymap[XK_KP_8 - KEY_MAP_OFFSET] = rsPADUP; + keymap[XK_KP_9 - KEY_MAP_OFFSET] = rsPADPGUP; + keymap[XK_KP_Insert - KEY_MAP_OFFSET] = rsPADINS; + keymap[XK_KP_End - KEY_MAP_OFFSET] = rsPADEND; + keymap[XK_KP_Down - KEY_MAP_OFFSET] = rsPADDOWN; + keymap[XK_KP_Page_Down - KEY_MAP_OFFSET] = rsPADPGDN; + keymap[XK_KP_Left - KEY_MAP_OFFSET] = rsPADLEFT; + keymap[XK_KP_Begin - KEY_MAP_OFFSET] = rsPAD5; + keymap[XK_KP_Right - KEY_MAP_OFFSET] = rsPADRIGHT; + keymap[XK_KP_Home - KEY_MAP_OFFSET] = rsPADHOME; + keymap[XK_KP_Up - KEY_MAP_OFFSET] = rsPADUP; + keymap[XK_KP_Page_Up - KEY_MAP_OFFSET] = rsPADPGUP; + + keymap[XK_KP_Decimal - KEY_MAP_OFFSET] = rsPADDEL; + keymap[XK_KP_Divide - KEY_MAP_OFFSET] = rsDIVIDE; + keymap[XK_KP_Multiply - KEY_MAP_OFFSET] = rsTIMES; + keymap[XK_KP_Subtract - KEY_MAP_OFFSET] = rsMINUS; + keymap[XK_KP_Add - KEY_MAP_OFFSET] = rsPLUS; + keymap[XK_KP_Enter - KEY_MAP_OFFSET] = rsPADENTER; + keymap[XK_KP_Equal - KEY_MAP_OFFSET] = rsNULL; + keymap[XK_Shift_L - KEY_MAP_OFFSET] = rsLSHIFT; + keymap[XK_Control_L - KEY_MAP_OFFSET] = rsLCTRL; + keymap[XK_Alt_L - KEY_MAP_OFFSET] = rsLALT; + keymap[XK_Super_L - KEY_MAP_OFFSET] = rsLWIN; + keymap[XK_Shift_R - KEY_MAP_OFFSET] = rsRSHIFT; + keymap[XK_Control_R - KEY_MAP_OFFSET] = rsRCTRL; + keymap[XK_Alt_R - KEY_MAP_OFFSET] = rsRALT; + keymap[XK_Super_R - KEY_MAP_OFFSET] = rsRWIN; + keymap[XK_Menu - KEY_MAP_OFFSET] = rsNULL; + + // Cache the key codes' key symbol equivelants, otherwise we will have to do it on each frame + // KeyCode is always in [0,255], and represents a physical key + + int min_keycode, max_keycode, keysyms_per_keycode; + KeySym *keymap, *origkeymap; + + char *keyboardLang = setlocale (LC_CTYPE, NULL); + setlocale(LC_CTYPE, ""); + + XDisplayKeycodes(display, &min_keycode, &max_keycode); + origkeymap = XGetKeyboardMapping(display, min_keycode, (max_keycode - min_keycode + 1), &keysyms_per_keycode); + keymap = origkeymap; + for (int i = min_keycode; i <= max_keycode; i++) { + int j, lastKeysym; + + lastKeysym = keysyms_per_keycode - 1; + while ((lastKeysym >= 0) && (keymap[lastKeysym] == NoSymbol)) + lastKeysym--; + + for (j = 0; j <= lastKeysym; j++) { + KeySym ks = keymap[j]; + + if (ks == NoSymbol) + continue; + + if (ks < 256) { + keyCodeToKeymapIndex[i] = ks; + break; + } else if (ks >= 0xff00 && ks < 0xffff) { + keyCodeToKeymapIndex[i] = ks - KEY_MAP_OFFSET; + break; + } + } + keymap += keysyms_per_keycode; + } + XFree(origkeymap); + + setlocale(LC_CTYPE, keyboardLang); +} +#undef KEY_MAP_OFFSET + +void checkKeyPresses() +{ + Display *display = glfwGetX11Display(); + char keys[32]; + XQueryKeymap(display, keys); + for (int i = 0; i < sizeof(keys); i++) { + for (int j = 0; j < 8; j++) { + KeyCode keycode = 8 * i + j; + uint32 keymapIndex = keyCodeToKeymapIndex[keycode]; + if (keymapIndex != 0) { + int rsCode = keymap[keymapIndex]; + if (rsCode == rsNULL) + continue; + + bool pressed = WindowFocused && !!(keys[i] & (1 << j)); + + // idk why R* does that + if (rsCode == rsLSHIFT) + lshiftStatus = pressed; + else if (rsCode == rsRSHIFT) + rshiftStatus = pressed; + + if (keyStates[keymapIndex] != pressed) { + if (pressed) { + RsKeyboardEventHandler(rsKEYDOWN, &rsCode); + } else { + RsKeyboardEventHandler(rsKEYUP, &rsCode); + } + } + + keyStates[keymapIndex] = pressed; + } + } + } + +} +#endif + +// R* calls that in ControllerConfig, idk why +void +_InputTranslateShiftKeyUpDown(RsKeyCodes *rs) { + RsKeyboardEventHandler(lshiftStatus ? rsKEYDOWN : rsKEYUP, &(*rs = rsLSHIFT)); + RsKeyboardEventHandler(rshiftStatus ? rsKEYDOWN : rsKEYUP, &(*rs = rsRSHIFT)); +} + +// TODO this only works in frontend(and luckily only frontend use this). Fun fact: if I get pos manually in game, glfw reports that it's > 32000 +void +cursorCB(GLFWwindow* window, double xpos, double ypos) { + if (!FrontEndMenuManager.m_bMenuActive) + return; + + int winw, winh; + glfwGetWindowSize(PSGLOBAL(window), &winw, &winh); + FrontEndMenuManager.m_nMouseTempPosX = xpos * (RsGlobal.maximumWidth / winw); + FrontEndMenuManager.m_nMouseTempPosY = ypos * (RsGlobal.maximumHeight / winh); +} + +void +cursorEnterCB(GLFWwindow* window, int entered) { + PSGLOBAL(cursorIsInWindow) = !!entered; +} + +void +windowFocusCB(GLFWwindow* window, int focused) { + WindowFocused = !!focused; +} + +void +windowIconifyCB(GLFWwindow* window, int iconified) { + WindowIconified = !!iconified; +} + +/* + ***************************************************************************** + */ +#ifdef _WIN32 +int PASCAL +WinMain(HINSTANCE instance, + HINSTANCE prevInstance __RWUNUSED__, + CMDSTR cmdLine, + int cmdShow) +{ + + RwInt32 argc; + RwChar** argv; + SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, nil, SPIF_SENDCHANGE); + +#ifndef MASTER + if (strstr(cmdLine, "-console")) + { + AllocConsole(); + freopen("CONIN$", "r", stdin); + freopen("CONOUT$", "w", stdout); + freopen("CONOUT$", "w", stderr); + } +#endif + +#else +int +main(int argc, char *argv[]) +{ +#endif + RwV2d pos; + RwInt32 i; + +#ifdef USE_CUSTOM_ALLOCATOR + InitMemoryMgr(); +#endif + +#if !defined(_WIN32) && !defined(__SWITCH__) + struct sigaction act; + act.sa_sigaction = terminateHandler; + act.sa_flags = SA_SIGINFO; + sigaction(SIGTERM, &act, NULL); +#ifdef FLUSHABLE_STREAMING + struct sigaction sa; + sigemptyset(&sa.sa_mask); + sa.sa_handler = dummyHandler; + sa.sa_flags = 0; + sigaction(SIGUSR1, &sa, NULL); +#endif +#endif + + /* + * Initialize the platform independent data. + * This will in turn initialize the platform specific data... + */ + if( RsEventHandler(rsINITIALIZE, nil) == rsEVENTERROR ) + { + return FALSE; + } + +#ifdef _WIN32 + /* + * Get proper command line params, cmdLine passed to us does not + * work properly under all circumstances... + */ + cmdLine = GetCommandLine(); + + /* + * Parse command line into standard (argv, argc) parameters... + */ + argv = CommandLineToArgv(cmdLine, &argc); + + + /* + * Parse command line parameters (except program name) one at + * a time BEFORE RenderWare initialization... + */ +#endif + for(i=1; iGetLeftMouseJustDown()) +// ++gGameState; +// else if (CPad::GetPad(0)->GetEnterJustDown()) +// ++gGameState; +// else if (CPad::GetPad(0)->GetCharJustDown(' ')) +// ++gGameState; +// else if (CPad::GetPad(0)->GetAltJustDown()) +// ++gGameState; +// else if (CPad::GetPad(0)->GetTabJustDown()) +// ++gGameState; + + break; + } + + case GS_INIT_INTRO_MPEG: + { +//#ifndef NO_MOVIES +// CloseClip(); +// CoUninitialize(); +//#endif +// +// if (CMenuManager::OS_Language == LANG_FRENCH || CMenuManager::OS_Language == LANG_GERMAN) +// PlayMovieInWindow(cmdShow, "movies\\GTAtitlesGER.mpg"); +// else +// PlayMovieInWindow(cmdShow, "movies\\GTAtitles.mpg"); + + gGameState = GS_INTRO_MPEG; + TRACE("gGameState = GS_INTRO_MPEG;"); + break; + } + + case GS_INTRO_MPEG: + { +// CPad::UpdatePads(); +// +// if (startupDeactivate || ControlsManager.GetJoyButtonJustDown() != 0) + ++gGameState; +// else if (CPad::GetPad(0)->GetLeftMouseJustDown()) +// ++gGameState; +// else if (CPad::GetPad(0)->GetEnterJustDown()) +// ++gGameState; +// else if (CPad::GetPad(0)->GetCharJustDown(' ')) +// ++gGameState; +// else if (CPad::GetPad(0)->GetAltJustDown()) +// ++gGameState; +// else if (CPad::GetPad(0)->GetTabJustDown()) +// ++gGameState; + + break; + } + + case GS_INIT_ONCE: + { + //CoUninitialize(); + +#ifdef PS2_MENU + extern char version_name[64]; + if ( CGame::frenchGame || CGame::germanGame ) + LoadingScreen(NULL, version_name, "loadsc24"); + else + LoadingScreen(NULL, version_name, "loadsc0"); + + printf("Into TheGame!!!\n"); +#else + LoadingScreen(nil, nil, "loadsc0"); + // LoadingScreen(nil, nil, "loadsc0"); // duplicate +#endif + if ( !CGame::InitialiseOnceAfterRW() ) + RsGlobal.quit = TRUE; + +#ifdef PS2_MENU + gGameState = GS_INIT_PLAYING_GAME; +#else + gGameState = GS_INIT_FRONTEND; + TRACE("gGameState = GS_INIT_FRONTEND;"); +#endif + break; + } +#ifndef PS2_MENU + case GS_INIT_FRONTEND: + { + LoadingScreen(nil, nil, "loadsc0"); + // LoadingScreen(nil, nil, "loadsc0"); // duplicate + + FrontEndMenuManager.m_bGameNotLoaded = true; + + FrontEndMenuManager.m_bStartUpFrontEndRequested = true; + + if ( defaultFullscreenRes ) + { + defaultFullscreenRes = FALSE; + FrontEndMenuManager.m_nPrefsVideoMode = GcurSelVM; + FrontEndMenuManager.m_nDisplayVideoMode = GcurSelVM; + } + + gGameState = GS_FRONTEND; + TRACE("gGameState = GS_FRONTEND;"); + break; + } + + case GS_FRONTEND: + { + if(!WindowIconified) + RsEventHandler(rsFRONTENDIDLE, nil); + +#ifdef PS2_MENU + if ( !FrontEndMenuManager.m_bMenuActive || TheMemoryCard.m_bWantToLoad ) +#else + if ( !FrontEndMenuManager.m_bMenuActive || FrontEndMenuManager.m_bWantToLoad ) +#endif + { + gGameState = GS_INIT_PLAYING_GAME; + TRACE("gGameState = GS_INIT_PLAYING_GAME;"); + } + +#ifdef PS2_MENU + if (TheMemoryCard.m_bWantToLoad ) +#else + if ( FrontEndMenuManager.m_bWantToLoad ) +#endif + { + InitialiseGame(); + FrontEndMenuManager.m_bGameNotLoaded = false; + gGameState = GS_PLAYING_GAME; + TRACE("gGameState = GS_PLAYING_GAME;"); + } + break; + } +#endif + + case GS_INIT_PLAYING_GAME: + { +#ifdef PS2_MENU + CGame::Initialise("DATA\\GTA3.DAT"); + + //LoadingScreen("Starting Game", NULL, GetRandomSplashScreen()); + + if ( TheMemoryCard.CheckCardInserted(CARD_ONE) == CMemoryCard::NO_ERR_SUCCESS + && TheMemoryCard.ChangeDirectory(CARD_ONE, TheMemoryCard.Cards[CARD_ONE].dir) + && TheMemoryCard.FindMostRecentFileName(CARD_ONE, TheMemoryCard.MostRecentFile) == true + && TheMemoryCard.CheckDataNotCorrupt(TheMemoryCard.MostRecentFile)) + { + strcpy(TheMemoryCard.LoadFileName, TheMemoryCard.MostRecentFile); + TheMemoryCard.b_FoundRecentSavedGameWantToLoad = true; + + if (CMenuManager::m_PrefsLanguage != TheMemoryCard.GetLanguageToLoad()) + { + CMenuManager::m_PrefsLanguage = TheMemoryCard.GetLanguageToLoad(); + TheText.Unload(); + TheText.Load(); + } + + CGame::currLevel = (eLevelName)TheMemoryCard.GetLevelToLoad(); + } +#else + InitialiseGame(); + + FrontEndMenuManager.m_bGameNotLoaded = false; +#endif + gGameState = GS_PLAYING_GAME; + TRACE("gGameState = GS_PLAYING_GAME;"); + break; + } + + case GS_PLAYING_GAME: + { + float ms = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerMillisecond(); + if ( RwInitialised ) + { + if (!FrontEndMenuManager.m_PrefsFrameLimiter || (1000.0f / (float)RsGlobal.maxFPS) < ms) + RsEventHandler(rsIDLE, (void *)TRUE); + } + break; + } + } + } + else + { + if ( RwCameraBeginUpdate(Scene.camera) ) + { + RwCameraEndUpdate(Scene.camera); + ForegroundApp = TRUE; + RsEventHandler(rsACTIVATE, (void *)TRUE); + } + + } + } + + + /* + * About to shut down - block resize events again... + */ + RwInitialised = FALSE; + + FrontEndMenuManager.UnloadTextures(); +#ifdef PS2_MENU + if ( !(FrontEndMenuManager.m_bWantToRestart || TheMemoryCard.b_FoundRecentSavedGameWantToLoad)) + break; +#else + if ( !FrontEndMenuManager.m_bWantToRestart ) + break; +#endif + + CPad::ResetCheats(); + CPad::StopPadsShaking(); + + DMAudio.ChangeMusicMode(MUSICMODE_DISABLE); + +#ifdef PS2_MENU + CGame::ShutDownForRestart(); +#endif + + CTimer::Stop(); + +#ifdef PS2_MENU + if (FrontEndMenuManager.m_bWantToRestart || TheMemoryCard.b_FoundRecentSavedGameWantToLoad) + { + if (TheMemoryCard.b_FoundRecentSavedGameWantToLoad) + { + FrontEndMenuManager.m_bWantToRestart = true; + TheMemoryCard.m_bWantToLoad = true; + } + + CGame::InitialiseWhenRestarting(); + DMAudio.ChangeMusicMode(MUSICMODE_GAME); + FrontEndMenuManager.m_bWantToRestart = false; + + continue; + } + + CGame::ShutDown(); + CTimer::Stop(); + + break; +#else + if ( FrontEndMenuManager.m_bWantToLoad ) + { + CGame::ShutDownForRestart(); + CGame::InitialiseWhenRestarting(); + DMAudio.ChangeMusicMode(MUSICMODE_GAME); + LoadSplash(GetLevelSplashScreen(CGame::currLevel)); + FrontEndMenuManager.m_bWantToLoad = false; + } + else + { +#ifndef MASTER + if ( gbModelViewer ) + CAnimViewer::Shutdown(); + else +#endif + if ( gGameState == GS_PLAYING_GAME ) + CGame::ShutDown(); + + CTimer::Stop(); + + if ( FrontEndMenuManager.m_bFirstTime == true ) + { + gGameState = GS_INIT_FRONTEND; + TRACE("gGameState = GS_INIT_FRONTEND;"); + } + else + { + gGameState = GS_INIT_PLAYING_GAME; + TRACE("gGameState = GS_INIT_PLAYING_GAME;"); + } + } + + FrontEndMenuManager.m_bFirstTime = false; + FrontEndMenuManager.m_bWantToRestart = false; +#endif + } + +#ifndef MASTER + if ( gbModelViewer ) + CAnimViewer::Shutdown(); + else +#endif + if ( gGameState == GS_PLAYING_GAME ) + CGame::ShutDown(); + + DMAudio.Terminate(); + + _psFreeVideoModeList(); + + + /* + * Tidy up the 3D (RenderWare) components of the application... + */ + RsEventHandler(rsRWTERMINATE, nil); + + /* + * Free the platform dependent data... + */ + RsEventHandler(rsTERMINATE, nil); + +#ifdef _WIN32 + /* + * Free the argv strings... + */ + free(argv); + + SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &SavedStickyKeys, SPIF_SENDCHANGE); + SystemParametersInfo(SPI_SETPOWEROFFACTIVE, TRUE, nil, SPIF_SENDCHANGE); + SystemParametersInfo(SPI_SETLOWPOWERACTIVE, TRUE, nil, SPIF_SENDCHANGE); + SetErrorMode(0); +#endif + + return 0; +} + +/* + ***************************************************************************** + */ + +RwV2d leftStickPos; +RwV2d rightStickPos; + +void CapturePad(RwInt32 padID) +{ + int8 glfwPad = -1; + + if( padID == 0 ) + glfwPad = PSGLOBAL(joy1id); + else if( padID == 1) + glfwPad = PSGLOBAL(joy2id); + else + assert("invalid padID"); + + if ( glfwPad == -1 ) + return; + + int numButtons, numAxes; + const uint8 *buttons = glfwGetJoystickButtons(glfwPad, &numButtons); + const float *axes = glfwGetJoystickAxes(glfwPad, &numAxes); + GLFWgamepadstate gamepadState; + + if (ControlsManager.m_bFirstCapture == false) { + memcpy(&ControlsManager.m_OldState, &ControlsManager.m_NewState, sizeof(ControlsManager.m_NewState)); + } else { + // In case connected gamepad doesn't have L-R trigger axes. + ControlsManager.m_NewState.mappedButtons[15] = ControlsManager.m_NewState.mappedButtons[16] = 0; + } + + ControlsManager.m_NewState.buttons = (uint8*)buttons; + ControlsManager.m_NewState.numButtons = numButtons; + ControlsManager.m_NewState.id = glfwPad; + ControlsManager.m_NewState.isGamepad = glfwGetGamepadState(glfwPad, &gamepadState); + if (ControlsManager.m_NewState.isGamepad) { + memcpy(&ControlsManager.m_NewState.mappedButtons, gamepadState.buttons, sizeof(gamepadState.buttons)); + float lt = gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER], rt = gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER]; + + // glfw returns 0.0 for non-existent axises(which is bullocks) so we treat it as deadzone, and keep value of previous frame. + // otherwise if this axis is present, -1 = released, 1 = pressed + if (lt != 0.0f) + ControlsManager.m_NewState.mappedButtons[15] = lt > -0.8f; + + if (rt != 0.0f) + ControlsManager.m_NewState.mappedButtons[16] = rt > -0.8f; + } + // TODO? L2-R2 axes(not buttons-that's fine) on joysticks that don't have SDL gamepad mapping AREN'T handled, and I think it's impossible to do without mapping. + + if (ControlsManager.m_bFirstCapture == true) { + memcpy(&ControlsManager.m_OldState, &ControlsManager.m_NewState, sizeof(ControlsManager.m_NewState)); + + ControlsManager.m_bFirstCapture = false; + } + + RsPadButtonStatus bs; + bs.padID = padID; + + RsPadEventHandler(rsPADBUTTONUP, (void *)&bs); + + // Gamepad axes are guaranteed to return 0.0f if that particular gamepad doesn't have that axis. + // And that's really good for sticks, because gamepads return 0.0 for them when sticks are in released state. + if ( glfwPad != -1 ) { + leftStickPos.x = ControlsManager.m_NewState.isGamepad ? gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_X] : numAxes >= 1 ? axes[0] : 0.0f; + leftStickPos.y = ControlsManager.m_NewState.isGamepad ? gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_Y] : numAxes >= 2 ? axes[1] : 0.0f; + + rightStickPos.x = ControlsManager.m_NewState.isGamepad ? gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_X] : numAxes >= 3 ? axes[2] : 0.0f; + rightStickPos.y = ControlsManager.m_NewState.isGamepad ? gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y] : numAxes >= 4 ? axes[3] : 0.0f; + } + + { + if (CPad::m_bMapPadOneToPadTwo) + bs.padID = 1; + + RsPadEventHandler(rsPADBUTTONUP, (void *)&bs); + RsPadEventHandler(rsPADBUTTONDOWN, (void *)&bs); + } + + { + if (CPad::m_bMapPadOneToPadTwo) + bs.padID = 1; + + CPad *pad = CPad::GetPad(bs.padID); + + if ( Abs(leftStickPos.x) > 0.3f ) + pad->PCTempJoyState.LeftStickX = (int32)(leftStickPos.x * 128.0f); + + if ( Abs(leftStickPos.y) > 0.3f ) + pad->PCTempJoyState.LeftStickY = (int32)(leftStickPos.y * 128.0f); + + if ( Abs(rightStickPos.x) > 0.3f ) + pad->PCTempJoyState.RightStickX = (int32)(rightStickPos.x * 128.0f); + + if ( Abs(rightStickPos.y) > 0.3f ) + pad->PCTempJoyState.RightStickY = (int32)(rightStickPos.y * 128.0f); + } + + _psHandleVibration(); + + return; +} + +void joysChangeCB(int jid, int event) +{ + if (event == GLFW_CONNECTED && !IsThisJoystickBlacklisted(jid)) { + if (PSGLOBAL(joy1id) == -1) { + PSGLOBAL(joy1id) = jid; +#ifdef DETECT_JOYSTICK_MENU + strcpy(gSelectedJoystickName, glfwGetJoystickName(jid)); +#endif + // This is behind LOAD_INI_SETTINGS, because otherwise the Init call below will destroy/overwrite your bindings. +#ifdef LOAD_INI_SETTINGS + int count; + glfwGetJoystickButtons(PSGLOBAL(joy1id), &count); + ControlsManager.InitDefaultControlConfigJoyPad(count); +#endif + } else if (PSGLOBAL(joy2id) == -1) + PSGLOBAL(joy2id) = jid; + + } else if (event == GLFW_DISCONNECTED) { + if (PSGLOBAL(joy1id) == jid) { + PSGLOBAL(joy1id) = -1; + } else if (PSGLOBAL(joy2id) == jid) + PSGLOBAL(joy2id) = -1; + } +} + +#if (defined(_MSC_VER)) +int strcasecmp(const char* str1, const char* str2) +{ + return _strcmpi(str1, str2); +} +int strncasecmp(const char *str1, const char *str2, size_t len) +{ + return _strnicmp(str1, str2, len); +} +#endif +#endif diff --git a/src/miami/skel/platform.h b/src/miami/skel/platform.h new file mode 100644 index 00000000..0475d20a --- /dev/null +++ b/src/miami/skel/platform.h @@ -0,0 +1,61 @@ +#ifndef PLATFORM_H +#define PLATFORM_H + +// Functions that's different on glfw/win etc. but have same signature (but if a function only used in win.cpp you can keep in win.h) + +#include "rwcore.h" +#include "skeleton.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#ifdef _WIN32 +extern RwUInt32 psTimer(void); +#else +extern double psTimer(void); +#endif + +extern RwBool psInitialize(void); +extern void psTerminate(void); + +extern void psCameraShowRaster(RwCamera *camera); +extern RwBool psCameraBeginUpdate(RwCamera *camera); +extern RwImage *psGrabScreen(RwCamera *camera); + +extern void psMouseSetPos(RwV2d *pos); + +extern RwBool psSelectDevice(); + +extern RwMemoryFunctions *psGetMemoryFunctions(void); + +/* install the platform specific file system */ +extern RwBool psInstallFileSystem(void); + + +/* Handle native texture support */ +extern RwBool psNativeTextureSupport(void); + +extern void _InputTranslateShiftKeyUpDown(RsKeyCodes* rs); +extern long _InputInitialiseMouse(bool exclusive); // returns HRESULT on Windows actually +extern void _InputShutdownMouse(); +extern bool _InputMouseNeedsExclusive(); +extern void _InputInitialiseJoys(); + +extern void HandleExit(); + +extern void _psSelectScreenVM(RwInt32 videoMode); + +extern void InitialiseLanguage(); + +extern RwBool _psSetVideoMode(RwInt32 subSystem, RwInt32 videoMode); + +extern RwChar** _psGetVideoModeList(); + +extern RwInt32 _psGetNumVideModes(); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* PLATFORM_H */ diff --git a/src/miami/skel/skeleton.cpp b/src/miami/skel/skeleton.cpp new file mode 100644 index 00000000..2bb23460 --- /dev/null +++ b/src/miami/skel/skeleton.cpp @@ -0,0 +1,428 @@ +#include "common.h" + + +#include +#include +#include +#include + +#include "rwcore.h" + +#include "skeleton.h" +#include "platform.h" +#include "main.h" +#include "MemoryHeap.h" + +static RwBool DefaultVideoMode = TRUE; + +RsGlobalType RsGlobal; + +#ifdef _WIN32 +RwUInt32 +#else +double +#endif +RsTimer(void) +{ + return psTimer(); +} + + +/* + ***************************************************************************** + */ +void +RsCameraShowRaster(RwCamera * camera) +{ + psCameraShowRaster(camera); + + return; +} + +/* + ***************************************************************************** + */ +RwBool +RsCameraBeginUpdate(RwCamera * camera) +{ + return psCameraBeginUpdate(camera); +} + +/* + ***************************************************************************** + */ +RwImage* +RsGrabScreen(RwCamera *camera) +{ + return psGrabScreen(camera); +} + +/* + ***************************************************************************** + */ +RwBool +RsRegisterImageLoader(void) +{ + return TRUE; +} + +/* + ***************************************************************************** + */ +static RwBool +RsSetDebug(void) +{ + return TRUE; +} + +/* + ***************************************************************************** + */ +void +RsMouseSetPos(RwV2d * pos) +{ + psMouseSetPos(pos); + + return; +} + +/* + ***************************************************************************** + */ +RwBool +RsSelectDevice(void) +{ + return psSelectDevice(); +} + +/* + ***************************************************************************** + */ +RwBool +RsInputDeviceAttach(RsInputDeviceType inputDevice, + RsInputEventHandler inputEventHandler) +{ + switch (inputDevice) + { + case rsKEYBOARD: + { + RsGlobal.keyboard.inputEventHandler = inputEventHandler; + RsGlobal.keyboard.used = TRUE; + break; + } + case rsMOUSE: + { + RsGlobal.mouse.inputEventHandler = inputEventHandler; + RsGlobal.mouse.used = TRUE; + break; + } + case rsPAD: + { + RsGlobal.pad.inputEventHandler = inputEventHandler; + RsGlobal.pad.used = TRUE; + break; + } + default: + { + return FALSE; + } + } + + return TRUE; +} + + +/* + ***************************************************************************** + */ +static RwBool +rsCommandLine(RwChar *arg) +{ + RsEventHandler(rsFILELOAD, arg); + + return TRUE; +} + + +/* + ***************************************************************************** + */ +static RwBool +rsPreInitCommandLine(RwChar *arg) +{ + if( !strcmp(arg, RWSTRING("-vms")) ) + { + DefaultVideoMode = FALSE; + + return TRUE; + } +#ifndef MASTER + if (!strcmp(arg, RWSTRING("-animviewer"))) + { + gbModelViewer = TRUE; + + return TRUE; + } +#endif + return FALSE; +} + +/* + ***************************************************************************** + */ +RsEventStatus +RsKeyboardEventHandler(RsEvent event, void *param) +{ + if (RsGlobal.keyboard.used) + { + return RsGlobal.keyboard.inputEventHandler(event, param); + } + + return rsEVENTNOTPROCESSED; +} + +/* + ***************************************************************************** + */ +RsEventStatus +RsPadEventHandler(RsEvent event, void *param) +{ + if (RsGlobal.pad.used) + { + return RsGlobal.pad.inputEventHandler(event, param); + } + + return rsEVENTNOTPROCESSED; +} + +/* + ***************************************************************************** + */ +RsEventStatus +RsEventHandler(RsEvent event, void *param) +{ + RsEventStatus result; + RsEventStatus es; + + /* + * Give the application an opportunity to override any events... + */ + es = AppEventHandler(event, param); + + /* + * We never allow the app to replace the quit behaviour, + * only to intercept... + */ + if (event == rsQUITAPP) + { + /* + * Set the flag which causes the event loop to exit... + */ + RsGlobal.quit = TRUE; + } + + if (es == rsEVENTNOTPROCESSED) + { + switch (event) + { + case rsSELECTDEVICE: + result = + (RsSelectDevice()? rsEVENTPROCESSED : rsEVENTERROR); + break; + + case rsCOMMANDLINE: + result = (rsCommandLine((RwChar *) param) ? + rsEVENTPROCESSED : rsEVENTERROR); + break; + case rsPREINITCOMMANDLINE: + result = (rsPreInitCommandLine((RwChar *) param) ? + rsEVENTPROCESSED : rsEVENTERROR); + break; + case rsINITDEBUG: + result = + (RsSetDebug()? rsEVENTPROCESSED : rsEVENTERROR); + break; + + case rsREGISTERIMAGELOADER: + result = (RsRegisterImageLoader()? + rsEVENTPROCESSED : rsEVENTERROR); + break; + + case rsRWTERMINATE: + RsRwTerminate(); + result = (rsEVENTPROCESSED); + break; + + case rsRWINITIALIZE: + result = (RsRwInitialize(param) ? + rsEVENTPROCESSED : rsEVENTERROR); + break; + + case rsTERMINATE: + RsTerminate(); + result = (rsEVENTPROCESSED); + break; + + case rsINITIALIZE: + result = + (RsInitialize()? rsEVENTPROCESSED : rsEVENTERROR); + break; + + default: + result = (es); + break; + + } + } + else + { + result = (es); + } + + return result; +} + +/* + ***************************************************************************** + */ +void +RsRwTerminate(void) +{ + /* Close RenderWare */ + + RwEngineStop(); + RwEngineClose(); + RwEngineTerm(); + + return; +} + +/* + ***************************************************************************** + */ +RwBool +RsRwInitialize(void *displayID) +{ + RwEngineOpenParams openParams; + + /* + * Start RenderWare... + */ + + if (!RwEngineInit(psGetMemoryFunctions(), 0, rsRESOURCESDEFAULTARENASIZE)) + { + return (FALSE); + } + + /* + * Install any platform specific file systems... + */ + psInstallFileSystem(); + + /* + * Initialize debug message handling... + */ + RsEventHandler(rsINITDEBUG, nil); + + /* + * Attach all plugins... + */ + if (RsEventHandler(rsPLUGINATTACH, nil) == rsEVENTERROR) + { + return (FALSE); + } + + /* + * Attach input devices... + */ + if (RsEventHandler(rsINPUTDEVICEATTACH, nil) == rsEVENTERROR) + { + return (FALSE); + } + + openParams.displayID = displayID; + + if (!RwEngineOpen(&openParams)) + { + RwEngineTerm(); + return (FALSE); + } + + if (RsEventHandler(rsSELECTDEVICE, displayID) == rsEVENTERROR) + { + RwEngineClose(); + RwEngineTerm(); + return (FALSE); + } + + if (!RwEngineStart()) + { + RwEngineClose(); + RwEngineTerm(); + return (FALSE); + } + + /* + * Register loaders for an image with a particular file extension... + */ + RsEventHandler(rsREGISTERIMAGELOADER, nil); + + psNativeTextureSupport(); + + RwTextureSetAutoMipmapping(TRUE); + RwTextureSetMipmapping(FALSE); + + return TRUE; +} + +/* + ***************************************************************************** + */ +void +RsTerminate(void) +{ + psTerminate(); + + return; +} + +/* + ***************************************************************************** + */ +RwBool +RsInitialize(void) +{ + /* + * Initialize Platform independent data... + */ + RwBool result; + + RsGlobal.appName = RWSTRING("GTA: Vice City"); + RsGlobal.maximumWidth = DEFAULT_SCREEN_WIDTH; + RsGlobal.maximumHeight = DEFAULT_SCREEN_HEIGHT; + RsGlobal.width = DEFAULT_SCREEN_WIDTH; + RsGlobal.height = DEFAULT_SCREEN_HEIGHT; + + RsGlobal.maxFPS = 30; + + RsGlobal.quit = FALSE; + + /* setup the keyboard */ + RsGlobal.keyboard.inputDeviceType = rsKEYBOARD; + RsGlobal.keyboard.inputEventHandler = nil; + RsGlobal.keyboard.used = FALSE; + + /* setup the mouse */ + RsGlobal.mouse.inputDeviceType = rsMOUSE; + RsGlobal.mouse.inputEventHandler = nil; + RsGlobal.mouse.used = FALSE; + + /* setup the pad */ + RsGlobal.pad.inputDeviceType = rsPAD; + RsGlobal.pad.inputEventHandler = nil; + RsGlobal.pad.used = FALSE; + + result = psInitialize(); + + return result; +} diff --git a/src/miami/skel/skeleton.h b/src/miami/skel/skeleton.h new file mode 100644 index 00000000..380b6c05 --- /dev/null +++ b/src/miami/skel/skeleton.h @@ -0,0 +1,290 @@ +#ifndef SKELETON_H +#define SKELETON_H + +#include "rwcore.h" + +/* Default arena size depending on platform. */ +#define rsRESOURCESDEFAULTARENASIZE (1 << 20) + +#if (!defined(RsSprintf)) +#define RsSprintf rwsprintf +#endif /* (!defined(RsSprintf)) */ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#if (!defined(RSASSERT)) +#define RSASSERT(_condition) /* No-op */ +#endif /* (!defined(RSASSERT)) */ + +#define RSASSERTISTYPE(_f, _t) \ + RSASSERT( (!(_f)) || ((((const RwObject *)(_f))->type)==(_t)) ) + +enum RsInputDeviceType +{ + rsKEYBOARD, + rsMOUSE, + rsPAD +}; +typedef enum RsInputDeviceType RsInputDeviceType; + +enum RsEventStatus +{ + rsEVENTERROR, + rsEVENTPROCESSED, + rsEVENTNOTPROCESSED +}; +typedef enum RsEventStatus RsEventStatus; + +enum RsEvent +{ + rsCAMERASIZE, + rsCOMMANDLINE, + rsFILELOAD, + rsINITDEBUG, + rsINPUTDEVICEATTACH, + rsLEFTBUTTONDOWN, + rsLEFTBUTTONUP, + rsMOUSEMOVE, + rsMOUSEWHEELMOVE, + rsPLUGINATTACH, + rsREGISTERIMAGELOADER, + rsRIGHTBUTTONDOWN, + rsRIGHTBUTTONUP, + _rs_13, + _rs_14, + _rs_15, + _rs_16, + _rs_17, + _rs_18, + _rs_19, + _rs_20, + rsRWINITIALIZE, + rsRWTERMINATE, + rsSELECTDEVICE, + rsINITIALIZE, + rsTERMINATE, + rsIDLE, + rsFRONTENDIDLE, + rsKEYDOWN, + rsKEYUP, + rsQUITAPP, + rsPADBUTTONDOWN, + rsPADBUTTONUP, + rsPADANALOGUELEFT, + rsPADANALOGUELEFTRESET, + rsPADANALOGUERIGHT, + rsPADANALOGUERIGHTRESET, + rsPREINITCOMMANDLINE, + rsACTIVATE, +}; + +typedef enum RsEvent RsEvent; + +typedef RsEventStatus (*RsInputEventHandler)(RsEvent event, void *param); + +typedef struct RsInputDevice RsInputDevice; +struct RsInputDevice +{ + RsInputDeviceType inputDeviceType; + RwBool used; + RsInputEventHandler inputEventHandler; +}; + +typedef struct RsGlobalType RsGlobalType; +struct RsGlobalType +{ + const RwChar *appName; + RwInt32 width; + RwInt32 height; + RwInt32 maximumWidth; + RwInt32 maximumHeight; + RwInt32 maxFPS; + RwBool quit; + + void *ps; /* platform specific data */ + + RsInputDevice keyboard; + RsInputDevice mouse; + RsInputDevice pad; +}; + +enum RsKeyCodes +{ + rsESC = 1000, + + rsF1 = 1001, + rsF2 = 1002, + rsF3 = 1003, + rsF4 = 1004, + rsF5 = 1005, + rsF6 = 1006, + rsF7 = 1007, + rsF8 = 1008, + rsF9 = 1009, + rsF10 = 1010, + rsF11 = 1011, + rsF12 = 1012, + + rsINS = 1013, + rsDEL = 1014, + rsHOME = 1015, + rsEND = 1016, + rsPGUP = 1017, + rsPGDN = 1018, + + rsUP = 1019, + rsDOWN = 1020, + rsLEFT = 1021, + rsRIGHT = 1022, + + rsDIVIDE = 1023, + rsTIMES = 1024, + rsPLUS = 1025, + rsMINUS = 1026, + rsPADDEL = 1027, + rsPADEND = 1028, + rsPADDOWN = 1029, + rsPADPGDN = 1030, + rsPADLEFT = 1031, + rsPAD5 = 1032, + rsNUMLOCK = 1033, + rsPADRIGHT = 1034, + rsPADHOME = 1035, + rsPADUP = 1036, + rsPADPGUP = 1037, + rsPADINS = 1038, + rsPADENTER = 1039, + + rsSCROLL = 1040, + rsPAUSE = 1041, + + rsBACKSP = 1042, + rsTAB = 1043, + rsCAPSLK = 1044, + rsENTER = 1045, + rsLSHIFT = 1046, + rsRSHIFT = 1047, + rsSHIFT = 1048, + rsLCTRL = 1049, + rsRCTRL = 1050, + rsLALT = 1051, + rsRALT = 1052, + rsLWIN = 1053, + rsRWIN = 1054, + rsAPPS = 1055, + + rsNULL = 1056, + + rsMOUSELEFTBUTTON = 1, + rsMOUSMIDDLEBUTTON = 2, + rsMOUSERIGHTBUTTON = 3, + rsMOUSEWHEELUPBUTTON = 4, + rsMOUSEWHEELDOWNBUTTON = 5, + rsMOUSEX1BUTTON = 6, + rsMOUSEX2BUTTON = 7, +}; +typedef enum RsKeyCodes RsKeyCodes; + +typedef struct RsKeyStatus RsKeyStatus; +struct RsKeyStatus +{ + RwInt32 keyCharCode; +}; + +typedef struct RsPadButtonStatus RsPadButtonStatus; +struct RsPadButtonStatus +{ + RwInt32 padID; +}; + +enum RsPadButtons +{ + rsPADNULL = 0, + + rsPADBUTTON1 = 1, + rsPADBUTTON2 = 2, + rsPADBUTTON3 = 3, + rsPADBUTTON4 = 4, + + rsPADBUTTON5 = 5, + rsPADBUTTON6 = 6, + rsPADBUTTON7 = 7, + rsPADBUTTON8 = 8, + + rsPADSELECT = 9, + + rsPADBUTTONA1 = 10, + rsPADBUTTONA2 = 11, + + rsPADSTART = 12, + + rsPADDPADUP = 13, + rsPADDPADRIGHT = 14, + rsPADDPADDOWN = 15, + rsPADDPADLEFT = 16, +}; +typedef enum RsPadButtons RsPadButtons; + + +extern RsGlobalType RsGlobal; + +extern RsEventStatus AppEventHandler(RsEvent event, void *param); +extern RwBool AttachInputDevices(void); + +extern RsEventStatus RsEventHandler(RsEvent event, void *param); +extern RsEventStatus RsKeyboardEventHandler(RsEvent event, void *param); +extern RsEventStatus RsPadEventHandler(RsEvent event, void *param); + +extern RwBool +RsInitialize(void); + +extern RwBool +RsRegisterImageLoader(void); + +extern RwBool +RsRwInitialize(void *param); + +extern RwBool +RsSelectDevice(void); + +extern RwBool +RsInputDeviceAttach(RsInputDeviceType inputDevice, + RsInputEventHandler inputEventHandler); + +#ifdef _WIN32 +extern RwUInt32 +#else +extern double +#endif +RsTimer(void); + +extern void +RsCameraShowRaster(RwCamera *camera); + +extern RwBool +RsCameraBeginUpdate(RwCamera *camera); + +//TODO +//extern void +//RsMouseSetVisibility(RwBool visible); + +extern RwImage* +RsGrabScreen(RwCamera *camera); + +extern void +RsMouseSetPos(RwV2d *pos); + +extern void +RsRwTerminate(void); + +extern void +RsTerminate(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SKELETON_H */ diff --git a/src/miami/skel/win/gtavc.ico b/src/miami/skel/win/gtavc.ico new file mode 100644 index 00000000..7bfcc5a5 Binary files /dev/null and b/src/miami/skel/win/gtavc.ico differ diff --git a/src/miami/skel/win/resource.h b/src/miami/skel/win/resource.h new file mode 100644 index 00000000..93f14216 --- /dev/null +++ b/src/miami/skel/win/resource.h @@ -0,0 +1,21 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by dungeon.rc +// +#define IDD_DIALOG1 104 +#define IDC_DEVICESEL 1000 +#define IDC_VIDMODE 1001 +#define IDEXIT 1002 +#define IDC_SELECTDEVICE 1005 + +#define IDI_MAIN_ICON 100 +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 104 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/miami/skel/win/win.cpp b/src/miami/skel/win/win.cpp new file mode 100644 index 00000000..c49f0ab9 --- /dev/null +++ b/src/miami/skel/win/win.cpp @@ -0,0 +1,3462 @@ +#if defined RW_D3D9 || defined RWLIBS || defined __MWERKS__ + +#define _WIN32_WINDOWS 0x0500 +#define WINVER 0x0500 + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#pragma warning( push ) +#pragma warning( disable : 4005) + +#ifdef __MWERKS__ +#define MAPVK_VK_TO_CHAR (2) // this is missing from codewarrior win32 headers - but it gets used ... how? +#endif + +#include +#include +#pragma warning( pop ) + +#define WM_GRAPHNOTIFY WM_USER+13 + +#ifndef USE_D3D9 +#pragma comment( lib, "d3d8.lib" ) +#endif +#pragma comment( lib, "ddraw.lib" ) +#pragma comment( lib, "Winmm.lib" ) +#pragma comment( lib, "dxguid.lib" ) +#pragma comment( lib, "strmiids.lib" ) +#pragma comment( lib, "dinput8.lib" ) + +#define WITHD3D +#define WITHDINPUT +#include "common.h" +#if (defined(_MSC_VER)) +#include +#endif /* (defined(_MSC_VER)) */ +#include +#include "rwcore.h" +#include "resource.h" +#include "skeleton.h" +#include "platform.h" +#include "crossplatform.h" + +#define MAX_SUBSYSTEMS (16) + +static RwBool ForegroundApp = TRUE; + +static RwBool RwInitialised = FALSE; + +static RwSubSystemInfo GsubSysInfo[MAX_SUBSYSTEMS]; +static RwInt32 GnumSubSystems = 0; +static RwInt32 GcurSel = 0, GcurSelVM = 0; + +static RwBool startupDeactivate; + +static RwBool useDefault; + +/* Class name for the MS Window's window class. */ + +static const RwChar *AppClassName = RWSTRING("Grand theft auto 3"); + +static psGlobalType PsGlobal; + + +#define PSGLOBAL(var) (((psGlobalType *)(RsGlobal.ps))->var) + +#undef MAKEPOINTS +#define MAKEPOINTS(l) (*((POINTS /*FAR*/ *)&(l))) + +#define SAFE_RELEASE(x) { if (x) x->Release(); x = NULL; } +#define JIF(x) if (FAILED(hr=(x))) \ + {debug(TEXT("FAILED(hr=0x%x) in ") TEXT(#x) TEXT("\n"), hr); return;} + +#include "main.h" +#include "FileMgr.h" +#include "Text.h" +#include "Pad.h" +#include "Timer.h" +#include "DMAudio.h" +#include "ControllerConfig.h" +#include "Frontend.h" +#include "Game.h" +#include "PCSave.h" +#include "AnimViewer.h" +#include "MemoryMgr.h" + +#ifdef PS2_MENU +#include "MemoryCard.h" +#include "Font.h" +#endif + +VALIDATE_SIZE(psGlobalType, 0x28); + +// DirectShow interfaces +IGraphBuilder *pGB = nil; +IMediaControl *pMC = nil; +IMediaEventEx *pME = nil; +IVideoWindow *pVW = nil; +IMediaSeeking *pMS = nil; + +DWORD dwDXVersion; +SIZE_T _dwMemTotalPhys; +size_t _dwMemAvailPhys; +SIZE_T _dwMemTotalVirtual; +SIZE_T _dwMemAvailVirtual; +DWORD _dwMemTotalVideo; +DWORD _dwMemAvailVideo; +DWORD _dwOperatingSystemVersion; + +RwUInt32 gGameState; +CJoySticks AllValidWinJoys; + +#ifdef DETECT_JOYSTICK_MENU +char gSelectedJoystickName[128] = ""; +#endif + +// What is that for anyway? +#ifndef IMPROVED_VIDEOMODE +static RwBool defaultFullscreenRes = TRUE; +#else +static RwBool defaultFullscreenRes = FALSE; +static RwInt32 bestWndMode = -1; +#endif + +CJoySticks::CJoySticks() +{ + for (int i = 0; i < MAX_JOYSTICKS; i++) + { + ClearJoyInfo(i); + } +} + +void CJoySticks::ClearJoyInfo(int joyID) +{ + m_aJoys[joyID].m_State = JOYPAD_UNUSED; + m_aJoys[joyID].m_bInitialised = false; + m_aJoys[joyID].m_bHasAxisZ = false; + m_aJoys[joyID].m_bHasAxisR = false; +} + + + +/* + ***************************************************************************** + */ +void _psCreateFolder(LPCSTR path) +{ + HANDLE hfle = CreateFile(path, GENERIC_READ, + FILE_SHARE_READ, + nil, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL, + nil); + + if ( hfle == INVALID_HANDLE_VALUE ) + CreateDirectory(path, nil); + else + CloseHandle(hfle); +} + +/* + ***************************************************************************** + */ +const char *_psGetUserFilesFolder() +{ +#ifdef USE_MY_DOCUMENTS + HKEY hKey = NULL; + + static CHAR szUserFiles[256]; + + if ( RegOpenKeyEx(HKEY_CURRENT_USER, + REGSTR_PATH_SPECIAL_FOLDERS, + REG_OPTION_RESERVED, + KEY_READ, + &hKey) == ERROR_SUCCESS ) + { + DWORD KeyType; + DWORD KeycbData = sizeof(szUserFiles); + if ( RegQueryValueEx(hKey, + "Personal", + NULL, + &KeyType, + (LPBYTE)szUserFiles, + &KeycbData) == ERROR_SUCCESS ) + { + RegCloseKey(hKey); + strcat(szUserFiles, "\\GTA Vice City User Files"); + _psCreateFolder(szUserFiles); + return szUserFiles; + } + + RegCloseKey(hKey); + } + + strcpy(szUserFiles, "data"); + return szUserFiles; +#else + static CHAR szUserFiles[256]; + strcpy(szUserFiles, "userfiles"); + _psCreateFolder(szUserFiles); + return szUserFiles; +#endif +} + +/* + ***************************************************************************** + */ +RwBool +psCameraBeginUpdate(RwCamera *camera) +{ + if ( !RwCameraBeginUpdate(Scene.camera) ) + { + ForegroundApp = FALSE; + RsEventHandler(rsACTIVATE, (void *)FALSE); + return FALSE; + } + + return TRUE; +} + +/* + ***************************************************************************** + */ +void +psCameraShowRaster(RwCamera *camera) +{ +#ifdef LEGACY_MENU_OPTIONS + if (FrontEndMenuManager.m_PrefsVsync || FrontEndMenuManager.m_bMenuActive) +#else + if (FrontEndMenuManager.m_PrefsFrameLimiter || FrontEndMenuManager.m_bMenuActive) +#endif + RwCameraShowRaster(camera, PSGLOBAL(window), rwRASTERFLIPWAITVSYNC); + else + RwCameraShowRaster(camera, PSGLOBAL(window), rwRASTERFLIPDONTWAIT); + + return; +} + + +/* + ***************************************************************************** + */ +RwImage * +psGrabScreen(RwCamera *pCamera) +{ +#ifndef LIBRW + RwRaster *pRaster = RwCameraGetRaster(pCamera); + if (RwImage *pImage = RwImageCreate(pRaster->width, pRaster->height, 32)) { + RwImageAllocatePixels(pImage); + RwImageSetFromRaster(pImage, pRaster); + return pImage; + } +#else + rw::Image *image = RwCameraGetRaster(pCamera)->toImage(); + image->removeMask(); + if(image) + return image; +#endif + return nil; +} + +/* + ***************************************************************************** + */ +RwUInt32 +psTimer(void) +{ + RwUInt32 time; + + TIMECAPS TimeCaps; + + timeGetDevCaps(&TimeCaps, sizeof(TIMECAPS)); + + timeBeginPeriod(TimeCaps.wPeriodMin); + + time = (RwUInt32) timeGetTime(); + + timeEndPeriod(TimeCaps.wPeriodMin); + + return time; +} + +/* + ***************************************************************************** + */ +void +psMouseSetPos(RwV2d *pos) +{ + POINT point; + + point.x = (RwInt32) pos->x; + point.y = (RwInt32) pos->y; + + ClientToScreen(PSGLOBAL(window), &point); + + SetCursorPos(point.x, point.y); + + PSGLOBAL(lastMousePos.x) = (RwInt32)pos->x; + + PSGLOBAL(lastMousePos.y) = (RwInt32)pos->y; + + return; +} + +/* + ***************************************************************************** + */ +RwMemoryFunctions* +psGetMemoryFunctions(void) +{ +#ifdef USE_CUSTOM_ALLOCATOR + return &memFuncs; +#else + return nil; +#endif +} + +/* + ***************************************************************************** + */ +RwBool +psInstallFileSystem(void) +{ + return (TRUE); +} + + +/* + ***************************************************************************** + */ +RwBool +psNativeTextureSupport(void) +{ + return RwD3D8DeviceSupportsDXTTexture(); +} + +/* + ***************************************************************************** + */ +static HWND +InitInstance(HANDLE instance) +{ + /* + * Perform any necessary initialization for this instance of the + * application. + * + * Create the MS Window's window instance for this application. The + * initial window size is given by the defined camera size. The window + * is not given a title as we set it during Init3D() with information + * about the version of RenderWare being used. + */ + + RECT rect; + + rect.left = rect.top = 0; + rect.right = RsGlobal.maximumWidth; + rect.bottom = RsGlobal.maximumHeight; + + AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); + + return CreateWindow(AppClassName, RsGlobal.appName, + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + rect.right - rect.left, rect.bottom - rect.top, + (HWND)nil, (HMENU)nil, (HINSTANCE)instance, nil); +} + +void _GetVideoMemInfo(LPDWORD total, LPDWORD avaible) +{ + HRESULT hr; + LPDIRECTDRAW7 pDD7; + + hr = DirectDrawCreateEx(nil, (VOID**)&pDD7, IID_IDirectDraw7, nil); + + if ( FAILED(hr) ) + return; + + DDSCAPS2 caps; + + ZeroMemory(&caps, sizeof(DDSCAPS2)); + caps.dwCaps = DDSCAPS_VIDEOMEMORY; + + pDD7->GetAvailableVidMem(&caps, total, avaible); + + pDD7->Release(); +} + +/* + ***************************************************************************** + */ +typedef HRESULT(WINAPI * DIRECTDRAWCREATEEX)( GUID*, VOID**, REFIID, IUnknown* ); + + +//----------------------------------------------------------------------------- +// Name: GetDXVersion() +// Desc: This function returns the DirectX version number as follows: +// 0x0000 = No DirectX installed +// 0x0700 = At least DirectX 7 installed. +// 0x0800 = At least DirectX 8 installed. +// +// Please note that this code is intended as a general guideline. Your +// app will probably be able to simply query for functionality (via +// QueryInterface) for one or two components. +// +// Please also note: +// "if( dwDXVersion != 0x500 ) return FALSE;" is VERY BAD. +// "if( dwDXVersion < 0x500 ) return FALSE;" is MUCH BETTER. +// to ensure your app will run on future releases of DirectX. +//----------------------------------------------------------------------------- +DWORD GetDXVersion() +{ + DIRECTDRAWCREATEEX DirectDrawCreateEx = NULL; + HINSTANCE hDDrawDLL = nil; + HINSTANCE hD3D8DLL = nil; + HINSTANCE hDPNHPASTDLL = NULL; + DWORD dwDXVersion = 0; + //HRESULT hr; + + // First see if DDRAW.DLL even exists. + hDDrawDLL = LoadLibrary( "DDRAW.DLL" ); + if( hDDrawDLL == nil ) + { + dwDXVersion = 0; + OutputDebugString( "Couldn't LoadLibrary DDraw\r\n" ); + return dwDXVersion; + } + + + //------------------------------------------------------------------------- + // DirectX 7.0 Checks + //------------------------------------------------------------------------- + + // Check for DirectX 7 by creating a DDraw7 object + LPDIRECTDRAW7 pDD7; + DirectDrawCreateEx = (DIRECTDRAWCREATEEX)GetProcAddress( hDDrawDLL, + "DirectDrawCreateEx" ); + if( nil == DirectDrawCreateEx ) + { + FreeLibrary( hDDrawDLL ); + OutputDebugString( "Couldn't GetProcAddress DirectDrawCreateEx\r\n" ); + return dwDXVersion; + } + + if( FAILED( DirectDrawCreateEx( nil, (VOID**)&pDD7, IID_IDirectDraw7, + nil ) ) ) + { + FreeLibrary( hDDrawDLL ); + OutputDebugString( "Couldn't DirectDrawCreateEx\r\n" ); + return dwDXVersion; + } + + // DDraw7 was created successfully. We must be at least DX7.0 + dwDXVersion = 0x700; + pDD7->Release(); + +#ifdef USE_D3D9 + HINSTANCE hD3D9DLL = LoadLibrary("D3D9.DLL"); + if (hD3D9DLL != nil) { + FreeLibrary(hDDrawDLL); + FreeLibrary(hD3D9DLL); + + dwDXVersion = 0x900; + return dwDXVersion; + } +#endif + + //------------------------------------------------------------------------- + // DirectX 8.0 Checks + //------------------------------------------------------------------------- + + // Simply see if D3D8.dll exists. + hD3D8DLL = LoadLibrary( "D3D8.DLL" ); + if( hD3D8DLL == nil ) + { + FreeLibrary( hDDrawDLL ); + OutputDebugString( "Couldn't LoadLibrary D3D8.DLL\r\n" ); + return dwDXVersion; + } + + // D3D8.dll exists. We must be at least DX8.0 + dwDXVersion = 0x800; + + + //------------------------------------------------------------------------- + // DirectX 8.1 Checks + //------------------------------------------------------------------------- + + // Simply see if dpnhpast.dll exists. + hDPNHPASTDLL = LoadLibrary( "dpnhpast.dll" ); + if( hDPNHPASTDLL == nil ) + { + FreeLibrary( hDPNHPASTDLL ); + OutputDebugString( "Couldn't LoadLibrary dpnhpast.dll\r\n" ); + return dwDXVersion; + } + + // dpnhpast.dll exists. We must be at least DX8.1 + dwDXVersion = 0x801; + + + //------------------------------------------------------------------------- + // End of checking for versions of DirectX + //------------------------------------------------------------------------- + + // Close open libraries and return + FreeLibrary( hDDrawDLL ); + FreeLibrary( hD3D8DLL ); + + return dwDXVersion; +} + +/* + ***************************************************************************** + */ +#ifndef _WIN64 +static char cpuvendor[16] = "UnknownVendr"; +__declspec(naked) const char * _psGetCpuVendr() +{ + __asm + { + push ebx + xor eax, eax + cpuid + mov dword ptr [cpuvendor+0], ebx + mov dword ptr [cpuvendor+4], edx + mov dword ptr [cpuvendor+8], ecx + mov eax, offset cpuvendor + pop ebx + retn + } +} + +/* + ***************************************************************************** + */ +__declspec(naked) RwUInt32 _psGetCpuFeatures() +{ + __asm + { + mov eax, 1 + cpuid + mov eax, edx + retn + } +} + +/* + ***************************************************************************** + */ +__declspec(naked) RwUInt32 _psGetCpuFeaturesEx() +{ + __asm + { + mov eax, 80000000h + cpuid + + cmp eax, 80000000h + jbe short _NOEX + + mov eax, 80000001h + cpuid + + mov eax, edx + jmp short _RETEX + +_NOEX: + xor eax, eax + mov eax, eax + +_RETEX: + retn + } +} + +#ifdef __MWERKS__ +#pragma dont_inline on +#endif +void _psPrintCpuInfo() +{ + RwUInt32 features = _psGetCpuFeatures(); + RwUInt32 FeaturesEx = _psGetCpuFeaturesEx(); + + debug("Running on a %s", _psGetCpuVendr()); + + if ( features & 0x800000 ) + debug("with MMX"); + if ( features & 0x2000000 ) + debug("with SSE"); + if ( FeaturesEx & 0x80000000 ) + debug("with 3DNow"); +} +#ifdef __MWERKS__ +#pragma dont_inline off +#endif +#endif + +/* + ***************************************************************************** + */ +#ifdef UNDER_CE +#define CMDSTR LPWSTR +#else +#define CMDSTR LPSTR +#endif + +/* + ***************************************************************************** + */ +RwBool +psInitialize(void) +{ + PsGlobal.lastMousePos.x = PsGlobal.lastMousePos.y = 0.0f; + + RsGlobal.ps = &PsGlobal; + + PsGlobal.fullScreen = FALSE; + + PsGlobal.dinterface = nil; + PsGlobal.mouse = nil; + PsGlobal.joy1 = nil; + PsGlobal.joy2 = nil; + + CFileMgr::Initialise(); + +#ifdef PS2_MENU + CPad::Initialise(); + CPad::GetPad(0)->Mode = 0; + + CGame::frenchGame = false; + CGame::germanGame = false; + CGame::nastyGame = true; + CMenuManager::m_PrefsAllowNastyGame = true; + + WORD lang = PRIMARYLANGID(GetSystemDefaultLCID()); + if ( lang == LANG_ITALIAN ) + CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_ITALIAN; + else if ( lang == LANG_SPANISH ) + CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_SPANISH; + else if ( lang == LANG_GERMAN ) + { + CGame::germanGame = true; + CGame::nastyGame = false; + CMenuManager::m_PrefsAllowNastyGame = false; + CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_GERMAN; + } + else if ( lang == LANG_FRENCH ) + { + CGame::frenchGame = true; + CGame::nastyGame = false; + CMenuManager::m_PrefsAllowNastyGame = false; + CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_FRENCH; + } + else + CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_AMERICAN; + + FrontEndMenuManager.InitialiseMenuContentsAfterLoadingGame(); + + TheMemoryCard.Init(); +#else + C_PcSave::SetSaveDirectory(_psGetUserFilesFolder()); + + InitialiseLanguage(); +#endif + + gGameState = GS_START_UP; + TRACE("gGameState = GS_START_UP"); +#ifndef _WIN64 + _psPrintCpuInfo(); +#endif + OSVERSIONINFO verInfo; + verInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + GetVersionEx(&verInfo); + + _dwOperatingSystemVersion = OS_WIN95; + + if ( verInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ) + { + if ( verInfo.dwMajorVersion == 4 ) + { + debug("Operating System is WinNT\n"); + _dwOperatingSystemVersion = OS_WINNT; + } + else if ( verInfo.dwMajorVersion == 5 ) + { + debug("Operating System is Win2000\n"); + _dwOperatingSystemVersion = OS_WIN2000; + } + else if ( verInfo.dwMajorVersion > 5 ) + { + debug("Operating System is WinXP or greater\n"); + _dwOperatingSystemVersion = OS_WINXP; + } + } + else if ( verInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) + { + if ( verInfo.dwMajorVersion > 4 || verInfo.dwMajorVersion == 4 && verInfo.dwMinorVersion != 0 ) + { + debug("Operating System is Win98\n"); + _dwOperatingSystemVersion = OS_WIN98; + } + else + { + debug("Operating System is Win95\n"); + _dwOperatingSystemVersion = OS_WIN95; + } + } + +#ifndef PS2_MENU + FrontEndMenuManager.LoadSettings(); +#endif + + dwDXVersion = GetDXVersion(); + debug("DirectX version 0x%x\n", dwDXVersion); + + if ( _dwOperatingSystemVersion == OS_WIN95 ) + { + MessageBoxW(nil, + (LPCWSTR)TheText.Get("WIN_95"), // Grand Theft Auto III cannot run on Windows 95 + (LPCWSTR)TheText.Get("WIN_TTL"), // Grand Theft Auto III + MB_OK); + + return FALSE; + } + + if ( dwDXVersion < 0x801 ) + { + MessageBoxW(nil, + (LPCWSTR)TheText.Get("WIN_DX"), // Grand Theft Auto III requires at least DirectX version 8.1 + (LPCWSTR)TheText.Get("WIN_TTL"), // Grand Theft Auto III + MB_OK); + + return FALSE; + } + + MEMORYSTATUS memstats; + GlobalMemoryStatus(&memstats); + + _dwMemTotalPhys = memstats.dwTotalPhys; + _dwMemAvailPhys = memstats.dwAvailPhys; + _dwMemTotalVirtual = memstats.dwTotalVirtual; + _dwMemAvailVirtual = memstats.dwAvailVirtual; + + _GetVideoMemInfo(&_dwMemTotalVideo, &_dwMemAvailVideo); +#ifdef FIX_BUGS + debug("Physical memory size %lu\n", _dwMemTotalPhys); + debug("Available physical memory %lu\n", _dwMemAvailPhys); + debug("Video memory size %lu\n", _dwMemTotalVideo); + debug("Available video memory %lu\n", _dwMemAvailVideo); +#else + debug("Physical memory size %d\n", _dwMemTotalPhys); + debug("Available physical memory %d\n", _dwMemAvailPhys); + debug("Video memory size %d\n", _dwMemTotalVideo); + debug("Available video memory %d\n", _dwMemAvailVideo); +#endif + + if ( _dwMemAvailVideo < (12 * 1024 * 1024) /*12 MB*/ ) + { + MessageBoxW(nil, + (LPCWSTR)TheText.Get("WIN_VDM"), // Grand Theft Auto III requires at least 12MB of available video memory + (LPCWSTR)TheText.Get("WIN_TTL"), // Grand Theft Auto III + MB_OK); + + return FALSE; + } + + TheText.Unload(); + + return TRUE; +} + + +/* + ***************************************************************************** + */ +void +psTerminate(void) +{ + return; +} + +/* + ***************************************************************************** + */ +static RwChar **_VMList; + +RwInt32 _psGetNumVideModes() +{ + return RwEngineGetNumVideoModes(); +} + +/* + ***************************************************************************** + */ +RwBool _psFreeVideoModeList() +{ + RwInt32 numModes; + RwInt32 i; + + numModes = _psGetNumVideModes(); + + if ( _VMList == nil ) + return TRUE; + + for ( i = 0; i < numModes; i++ ) + { + RwFree(_VMList[i]); + } + + RwFree(_VMList); + + _VMList = nil; + + return TRUE; +} + +/* + ***************************************************************************** + */ +RwChar **_psGetVideoModeList() +{ + RwInt32 numModes; + RwInt32 i; + + if ( _VMList != nil ) + { + return _VMList; + } + + numModes = RwEngineGetNumVideoModes(); + + _VMList = (RwChar **)RwCalloc(numModes, sizeof(RwChar*)); + + for ( i = 0; i < numModes; i++ ) + { + RwVideoMode vm; + + RwEngineGetVideoModeInfo(&vm, i); + + if ( vm.flags & rwVIDEOMODEEXCLUSIVE ) + { + if ( vm.width >= 640 + && vm.height >= 480 + && (vm.width == 640 + && vm.height == 480) + || !(vm.flags & rwVIDEOMODEEXCLUSIVE) + || (_dwMemTotalVideo - vm.depth * vm.height * vm.width / 8) > (12 * 1024 * 1024)/*12 MB*/ ) + { + _VMList[i] = (RwChar*)RwCalloc(100, sizeof(RwChar)); + rwsprintf(_VMList[i],"%lu X %lu X %lu", vm.width, vm.height, vm.depth); + } + else + _VMList[i] = nil; + } + else + _VMList[i] = nil; + } + + return _VMList; +} + +/* + ***************************************************************************** + */ +void _psSelectScreenVM(RwInt32 videoMode) +{ + RwTexDictionarySetCurrent( nil ); + + FrontEndMenuManager.UnloadTextures(); + + if ( !_psSetVideoMode(RwEngineGetCurrentSubSystem(), videoMode) ) + { + RsGlobal.quit = TRUE; + + ShowWindow(PSGLOBAL(window), SW_HIDE); + + MessageBoxW(nil, + (LPCWSTR)TheText.Get("WIN_RSZ"), // Failed to select new screen resolution + (LPCWSTR)TheText.Get("WIN_TTL"), // Grand Theft Auto III + MB_OK); + } + else + FrontEndMenuManager.LoadAllTextures(); +} + +/* + ***************************************************************************** + */ +void WaitForState(FILTER_STATE State) +{ + HRESULT hr; + + ASSERT(pMC != nil); + + // Make sure we have switched to the required state + LONG lfs; + do + { + hr = pMC->GetState(10, &lfs); + } while (State != lfs); +} + +/* + ***************************************************************************** + */ +void HandleGraphEvent(void) +{ + LONG evCode; + LONG_PTR evParam1, evParam2; + HRESULT hr=S_OK; + + ASSERT(pME != nil); + + // Process all queued events + while (SUCCEEDED(pME->GetEvent(&evCode, &evParam1, &evParam2, 0))) + { + // Free memory associated with callback, since we're not using it + hr = pME->FreeEventParams(evCode, evParam1, evParam2); + + // If this is the end of the clip, reset to beginning + if (EC_COMPLETE == evCode) + { + switch (gGameState) + { + case GS_LOGO_MPEG: + { + gGameState = GS_INIT_INTRO_MPEG; + TRACE("gGameState = GS_INIT_INTRO_MPEG"); + break; + } + case GS_INTRO_MPEG: + { + gGameState = GS_INIT_ONCE; + TRACE("gGameState = GS_INIT_ONCE"); + break; + } + default: + { + break; + } + } + + pME->SetNotifyWindow((OAHWND)NULL, 0, 0); + } + } +} + +/* + ***************************************************************************** + */ +LRESULT CALLBACK +MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) +{ + POINTS points; + static BOOL noMemory = FALSE; + + + switch( message ) + { + case WM_SETCURSOR: + { + ShowCursor(FALSE); + + SetCursor(nil); + + break; // is this correct ? + } + + case WM_SIZE: + { + RwRect r; + + r.x = 0; + r.y = 0; + r.w = LOWORD(lParam); + r.h = HIWORD(lParam); + + if (RwInitialised && r.h > 0 && r.w > 0) + { + RsEventHandler(rsCAMERASIZE, &r); + + if (r.w != LOWORD(lParam) && r.h != HIWORD(lParam)) + { + WINDOWPLACEMENT wp; + + /* failed to create window of required size */ + noMemory = TRUE; + + /* stop re-sizing */ + ReleaseCapture(); + + /* handle maximised window */ + GetWindowPlacement(window, &wp); + if (wp.showCmd == SW_SHOWMAXIMIZED) + { + SendMessage(window, WM_WINDOWPOSCHANGED, 0, 0); + } + } + else + { + noMemory = FALSE; + } + + } + + return 0L; + } + + case WM_SIZING: + { + /* + * Handle event to ensure window contents are displayed during re-size + * as this can be disabled by the user, then if there is not enough + * memory things don't work. + */ + RECT *newPos = (LPRECT) lParam; + RECT rect; + + /* redraw window */ + + if (RwInitialised && gGameState == GS_PLAYING_GAME) + { + RsEventHandler(rsIDLE, (void *)TRUE); + } + + /* Manually resize window */ + rect.left = rect.top = 0; + rect.bottom = newPos->bottom - newPos->top; + rect.right = newPos->right - newPos->left; + + SetWindowPos(window, HWND_TOP, rect.left, rect.top, + (rect.right - rect.left), + (rect.bottom - rect.top), SWP_NOMOVE); + + return 0L; + } + + case WM_LBUTTONDOWN: + { + SetCapture(window); + + return 0L; + } + + case WM_RBUTTONDOWN: + { + SetCapture(window); + + return 0L; + } + + case WM_MBUTTONDOWN: + { + SetCapture(window); + + return 0L; + } + + case WM_MOUSEWHEEL: + { + return 0L; + } + + case WM_MOUSEMOVE: + { + points = MAKEPOINTS(lParam); + + FrontEndMenuManager.m_nMouseTempPosX = points.x; + FrontEndMenuManager.m_nMouseTempPosY = points.y; + + return 0L; + } + + case WM_LBUTTONUP: + { + ReleaseCapture(); + + return 0L; + } + + case WM_RBUTTONUP: + { + ReleaseCapture(); + + return 0L; + } + + case WM_MBUTTONUP: + { + ReleaseCapture(); + + return 0L; + } + + case WM_KEYDOWN: + { + RsKeyCodes ks; + + if ( _InputTranslateKey(&ks, lParam, wParam) ) + RsKeyboardEventHandler(rsKEYDOWN, &ks); + + if ( wParam == VK_SHIFT ) + _InputTranslateShiftKeyUpDown(&ks); +#ifdef FIX_BUGS + break; +#else + return 0L; +#endif + } + + case WM_KEYUP: + { + RsKeyCodes ks; + + if ( _InputTranslateKey(&ks, lParam, wParam) ) + RsKeyboardEventHandler(rsKEYUP, &ks); + + if ( wParam == VK_SHIFT ) + _InputTranslateShiftKeyUpDown(&ks); + +#ifdef FIX_BUGS + break; +#else + return 0L; +#endif + } + + case WM_SYSKEYDOWN: + { + RsKeyCodes ks; + + if ( _InputTranslateKey(&ks, lParam, wParam) ) + RsKeyboardEventHandler(rsKEYDOWN, &ks); + + if ( wParam == VK_SHIFT ) + _InputTranslateShiftKeyUpDown(&ks); + +#ifdef FIX_BUGS + break; +#else + return 0L; +#endif + } + + case WM_SYSKEYUP: + { + RsKeyCodes ks; + + if ( _InputTranslateKey(&ks, lParam, wParam) ) + RsKeyboardEventHandler(rsKEYUP, &ks); + + if ( wParam == VK_SHIFT ) + _InputTranslateShiftKeyUpDown(&ks); + +#ifdef FIX_BUGS + break; +#else + return 0L; +#endif + } + + case WM_ACTIVATEAPP: + { + switch ( gGameState ) + { + case GS_LOGO_MPEG: + case GS_INTRO_MPEG: + { + ASSERT(pMC != nil); + + LONG state; + pMC->GetState(10, &state); + + if ( !(BOOL)wParam ) // losing activation + { + if ( state == State_Running && pMC != nil ) + { + HRESULT hr = pMC->Pause(); + + if (hr == S_FALSE) + OutputDebugString("Failed to pause the MPEG"); + else + WaitForState(State_Paused); + } + } + else + { + CenterVideo(); + + if ( state != State_Running && pMC != nil ) + { + HRESULT hr = pMC->Run(); + + if ( hr == S_FALSE ) + OutputDebugString("Failed to run the MPEG"); + else + { + WaitForState(State_Running); + SetFocus(PSGLOBAL(window)); + } + } + } + + break; + } + + case GS_START_UP: + { + if ( !(BOOL)wParam && PSGLOBAL(fullScreen) ) // losing activation + startupDeactivate = TRUE; + + break; + } + } + + CPad::GetPad(0)->Clear(false); + CPad::GetPad(1)->Clear(false); + + return 0L; + } + + case WM_TIMER: + { + return 0L; + } + + case WM_GRAPHNOTIFY: + { + if (gGameState == GS_INTRO_MPEG || gGameState == GS_LOGO_MPEG) + HandleGraphEvent(); + + break; + } + + case WM_CLOSE: + case WM_DESTROY: + { + /* + * Quit message handling. + */ + ClipCursor(nil); + + _InputShutdown(); + + PostQuitMessage(0); + + return 0L; + } + + case WM_DEVICECHANGE: + { + if( wParam == DBT_DEVICEREMOVECOMPLETE ) + { + PDEV_BROADCAST_HDR pDev = (PDEV_BROADCAST_HDR)lParam; + + if (pDev->dbch_devicetype != DBT_DEVTYP_VOLUME) + break; + + if ( DMAudio.IsAudioInitialised() ) + { + PDEV_BROADCAST_VOLUME pVol = (PDEV_BROADCAST_VOLUME)pDev; + if ( pVol->dbcv_flags & DBTF_MEDIA ) + { + char c = DMAudio.GetCDAudioDriveLetter(); + + if ( c >= 'A' && pVol->dbcv_unitmask & (1 << (c - 'A')) ) + { + OutputDebugString("About to check CD drive..."); + + while ( true ) + { + FrontEndMenuManager.WaitForUserCD(); + + if ( !FrontEndMenuManager.m_bQuitGameNoCD ) + { + if ( DMAudio.CheckForAnAudioFileOnCD() ) + { + OutputDebugString("GTA3 Audio CD has been inserted"); + break; + } + } + else + { + OutputDebugString("Exiting game as Audio CD was not inserted"); + break; + } + } + } + } + } + } + + break; + } + +#ifdef FIX_BUGS // game turns on menu when focus is re-gained rather than lost + case WM_KILLFOCUS: +#else + case WM_SETFOCUS: +#endif + { + CGame::InitAfterFocusLoss(); + break; + } + + } + + /* + * Let Windows handle all other messages. + */ + return DefWindowProc(window, message, wParam, lParam); +} + +/* + ***************************************************************************** + */ +static BOOL +InitApplication(HANDLE instance) +{ + /* + * Perform any necessary MS Windows application initialization. Basically, + * this means registering the window class for this application. + */ + + WNDCLASS windowClass; + + windowClass.style = CS_BYTEALIGNWINDOW; + windowClass.lpfnWndProc = (WNDPROC)MainWndProc; + windowClass.cbClsExtra = 0; + windowClass.cbWndExtra = 0; + windowClass.hInstance = (HINSTANCE)instance; + windowClass.hIcon = LoadIcon((HINSTANCE)instance, (LPCSTR)IDI_MAIN_ICON); + windowClass.hCursor = LoadCursor(nil, IDC_ARROW); + windowClass.hbrBackground = nil; + windowClass.lpszMenuName = NULL; + windowClass.lpszClassName = AppClassName; + + return RegisterClass(&windowClass); +} + + +/* + ***************************************************************************** + */ + +RwBool IsForegroundApp() +{ + return !!ForegroundApp; +} + +UINT GetBestRefreshRate(UINT width, UINT height, UINT depth) +{ +#ifdef USE_D3D9 + LPDIRECT3D9 d3d = Direct3DCreate9(D3D_SDK_VERSION); +#else + LPDIRECT3D8 d3d = Direct3DCreate8(D3D_SDK_VERSION); +#endif + ASSERT(d3d != nil); + + UINT refreshRate = INT_MAX; + D3DFORMAT format; + + if ( depth == 32 ) + format = D3DFMT_X8R8G8B8; + else if ( depth == 24 ) + format = D3DFMT_R8G8B8; + else + format = D3DFMT_R5G6B5; + +#ifdef USE_D3D9 + UINT modeCount = d3d->GetAdapterModeCount(GcurSel, format); +#else + UINT modeCount = d3d->GetAdapterModeCount(GcurSel); +#endif + + for ( UINT i = 0; i < modeCount; i++ ) + { + D3DDISPLAYMODE mode; + +#ifdef USE_D3D9 + d3d->EnumAdapterModes(GcurSel, format, i, &mode); +#else + d3d->EnumAdapterModes(GcurSel, i, &mode); +#endif + if ( mode.Width == width && mode.Height == height && mode.Format == format ) + { + if ( mode.RefreshRate == 0 ) { + d3d->Release(); + return 0; + } + + if ( mode.RefreshRate < refreshRate && mode.RefreshRate >= 60 ) + refreshRate = mode.RefreshRate; + } + } + + d3d->Release(); + + if ( refreshRate == -1 ) + return -1; + + return refreshRate; +} + +/* + ***************************************************************************** + */ +RwBool +psSelectDevice() +{ + RwVideoMode vm; + RwInt32 subSysNum; + RwInt32 AutoRenderer = 0; + + + RwBool modeFound = FALSE; + + if ( !useDefault ) + { + GnumSubSystems = RwEngineGetNumSubSystems(); + if ( !GnumSubSystems ) + { + return FALSE; + } + + /* Just to be sure ... */ + GnumSubSystems = (GnumSubSystems > MAX_SUBSYSTEMS) ? MAX_SUBSYSTEMS : GnumSubSystems; + + /* Get the names of all the sub systems */ + for (subSysNum = 0; subSysNum < GnumSubSystems; subSysNum++) + { + RwEngineGetSubSystemInfo(&GsubSysInfo[subSysNum], subSysNum); + } + + /* Get the default selection */ + GcurSel = RwEngineGetCurrentSubSystem(); +#ifdef IMPROVED_VIDEOMODE + if(FrontEndMenuManager.m_nPrefsSubsystem < GnumSubSystems) + GcurSel = FrontEndMenuManager.m_nPrefsSubsystem; +#endif + } + + /* Set the driver to use the correct sub system */ + if (!RwEngineSetSubSystem(GcurSel)) + { + return FALSE; + } + +#ifdef IMPROVED_VIDEOMODE + FrontEndMenuManager.m_nPrefsSubsystem = GcurSel; +#endif + +#ifndef IMPROVED_VIDEOMODE + if ( !useDefault ) + { + if ( _psGetVideoModeList()[FrontEndMenuManager.m_nDisplayVideoMode] && FrontEndMenuManager.m_nDisplayVideoMode ) + { + FrontEndMenuManager.m_nPrefsVideoMode = FrontEndMenuManager.m_nDisplayVideoMode; + GcurSelVM = FrontEndMenuManager.m_nDisplayVideoMode; + } + else + { +#ifdef DEFAULT_NATIVE_RESOLUTION + // get the native video mode + HDC hDevice = GetDC(NULL); + int w = GetDeviceCaps(hDevice, HORZRES); + int h = GetDeviceCaps(hDevice, VERTRES); + int d = GetDeviceCaps(hDevice, BITSPIXEL); +#else + const int w = 640; + const int h = 480; + const int d = 16; +#endif + while ( !modeFound && GcurSelVM < RwEngineGetNumVideoModes() ) + { + RwEngineGetVideoModeInfo(&vm, GcurSelVM); + if ( defaultFullscreenRes && vm.width != w + || vm.height != h + || vm.depth != d + || !(vm.flags & rwVIDEOMODEEXCLUSIVE) ) + ++GcurSelVM; + else + modeFound = TRUE; + } + + if ( !modeFound ) + { +#ifdef DEFAULT_NATIVE_RESOLUTION + GcurSelVM = 1; +#else + MessageBox(nil, "Cannot find 640x480 video mode", "GTA: Vice City", MB_OK); + return FALSE; +#endif + } + } + } +#else + if ( !useDefault ) + { + if(FrontEndMenuManager.m_nPrefsWidth == 0 || + FrontEndMenuManager.m_nPrefsHeight == 0 || + FrontEndMenuManager.m_nPrefsDepth == 0){ + // Defaults if nothing specified + FrontEndMenuManager.m_nPrefsWidth = GetSystemMetrics(SM_CXSCREEN); + FrontEndMenuManager.m_nPrefsHeight = GetSystemMetrics(SM_CYSCREEN); + FrontEndMenuManager.m_nPrefsDepth = 32; + FrontEndMenuManager.m_nPrefsWindowed = 0; + } + + // Find the videomode that best fits what we got from the settings file + RwInt32 bestFsMode = -1; + RwInt32 bestWidth = -1; + RwInt32 bestHeight = -1; + RwInt32 bestDepth = -1; + for (GcurSelVM = 0; GcurSelVM < RwEngineGetNumVideoModes(); GcurSelVM++) { + RwEngineGetVideoModeInfo(&vm, GcurSelVM); + + if (!(vm.flags & rwVIDEOMODEEXCLUSIVE)) { + bestWndMode = GcurSelVM; + } else { + // try the largest one that isn't larger than what we wanted + if (vm.width >= bestWidth && vm.width <= FrontEndMenuManager.m_nPrefsWidth && + vm.height >= bestHeight && vm.height <= FrontEndMenuManager.m_nPrefsHeight && + vm.depth >= bestDepth && vm.depth <= FrontEndMenuManager.m_nPrefsDepth){ + bestWidth = vm.width; + bestHeight = vm.height; + bestDepth = vm.depth; + bestFsMode = GcurSelVM; + } + } + } + + if(bestFsMode < 0){ + MessageBox(nil, "Cannot find desired video mode", "GTA: Vice City", MB_OK); + return FALSE; + } + GcurSelVM = bestFsMode; + + FrontEndMenuManager.m_nDisplayVideoMode = GcurSelVM; + FrontEndMenuManager.m_nPrefsVideoMode = FrontEndMenuManager.m_nDisplayVideoMode; + + FrontEndMenuManager.m_nSelectedScreenMode = FrontEndMenuManager.m_nPrefsWindowed; + } +#endif + + RwEngineGetVideoModeInfo(&vm, GcurSelVM); + +#ifdef IMPROVED_VIDEOMODE + if (FrontEndMenuManager.m_nPrefsWindowed) + GcurSelVM = bestWndMode; + + // Now GcurSelVM is 0 but vm has sizes(and fullscreen flag) of the video mode we want, that's why we changed the rwVIDEOMODEEXCLUSIVE conditions below + FrontEndMenuManager.m_nPrefsWidth = vm.width; + FrontEndMenuManager.m_nPrefsHeight = vm.height; + FrontEndMenuManager.m_nPrefsDepth = vm.depth; +#endif + +#ifndef PS2_MENU + FrontEndMenuManager.m_nCurrOption = 0; +#endif + + /* Set up the video mode and set the apps window + * dimensions to match */ + if (!RwEngineSetVideoMode(GcurSelVM)) + { + return FALSE; + } + +#ifdef IMPROVED_VIDEOMODE + if (!FrontEndMenuManager.m_nPrefsWindowed) +#else + if (vm.flags & rwVIDEOMODEEXCLUSIVE) +#endif + { + debug("%dx%dx%d", vm.width, vm.height, vm.depth); + + UINT refresh = GetBestRefreshRate(vm.width, vm.height, vm.depth); + + if ( refresh != (UINT)-1 ) + { + debug("refresh %d", refresh); + RwD3D8EngineSetRefreshRate((RwUInt32)refresh); + } + } + +#ifdef IMPROVED_VIDEOMODE + if (!FrontEndMenuManager.m_nPrefsWindowed) +#else + if (vm.flags & rwVIDEOMODEEXCLUSIVE) +#endif + { + RsGlobal.maximumWidth = vm.width; + RsGlobal.maximumHeight = vm.height; + RsGlobal.width = vm.width; + RsGlobal.height = vm.height; + + PSGLOBAL(fullScreen) = TRUE; + +#ifdef IMPROVED_VIDEOMODE + SetWindowLong(PSGLOBAL(window), GWL_STYLE, WS_POPUP); + SetWindowPos(PSGLOBAL(window), nil, 0, 0, 0, 0, + SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER| + SWP_FRAMECHANGED); + }else{ + RECT rect; + rect.left = rect.top = 0; + rect.right = FrontEndMenuManager.m_nPrefsWidth; + rect.bottom = FrontEndMenuManager.m_nPrefsHeight; + AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); + + // center it + int spaceX = GetSystemMetrics(SM_CXSCREEN) - (rect.right-rect.left); + int spaceY = GetSystemMetrics(SM_CYSCREEN) - (rect.bottom-rect.top); + + SetWindowLong(PSGLOBAL(window), GWL_STYLE, WS_VISIBLE | WS_OVERLAPPEDWINDOW); + SetWindowPos(PSGLOBAL(window), HWND_NOTOPMOST, spaceX/2, spaceY/2, + (rect.right - rect.left), + (rect.bottom - rect.top), 0); + + // Have to get actual size because the window perhaps didn't fit + GetClientRect(PSGLOBAL(window), &rect); + RsGlobal.maximumWidth = rect.right; + RsGlobal.maximumHeight = rect.bottom; + RsGlobal.width = rect.right; + RsGlobal.height = rect.bottom; + + PSGLOBAL(fullScreen) = FALSE; +#endif + } +#ifdef MULTISAMPLING + RwD3D8EngineSetMultiSamplingLevels(1 << FrontEndMenuManager.m_nPrefsMSAALevel); +#endif + return TRUE; +} + +/* + ***************************************************************************** + */ +RwBool _psSetVideoMode(RwInt32 subSystem, RwInt32 videoMode) +{ + RwInitialised = FALSE; + + RsEventHandler(rsRWTERMINATE, nil); + + GcurSel = subSystem; + GcurSelVM = videoMode; + + useDefault = TRUE; + + if ( RsEventHandler(rsRWINITIALIZE, PSGLOBAL(window)) == rsEVENTERROR ) + return FALSE; + + RwInitialised = TRUE; + useDefault = FALSE; + + RwRect r; + + r.x = 0; + r.y = 0; + r.w = RsGlobal.maximumWidth; + r.h = RsGlobal.maximumHeight; + + RsEventHandler(rsCAMERASIZE, &r); + + return TRUE; +} + +/* + ***************************************************************************** + */ +static RwChar ** +CommandLineToArgv(RwChar *cmdLine, RwInt32 *argCount) +{ + RwInt32 numArgs = 0; + RwBool inArg, inString; + RwInt32 i, len; + RwChar *res, *str, **aptr; + + len = (int)strlen(cmdLine); + + /* + * Count the number of arguments... + */ + inString = FALSE; + inArg = FALSE; + + for(i=0; i<=len; i++) + { + if( cmdLine[i] == '"' ) + { + inString = !inString; + } + + if( (cmdLine[i] <= ' ' && !inString) || i == len ) + { + if( inArg ) + { + inArg = FALSE; + + numArgs++; + } + } + else if( !inArg ) + { + inArg = TRUE; + } + } + + /* + * Allocate memory for result... + */ + res = (RwChar *)malloc(sizeof(RwChar *) * numArgs + len + 1); + str = res + sizeof(RwChar *) * numArgs; + aptr = (RwChar **)res; + + strcpy(str, cmdLine); + + /* + * Walk through cmdLine again this time setting pointer to each arg... + */ + inArg = FALSE; + inString = FALSE; + + for(i=0; i<=len; i++) + { + if( cmdLine[i] == '"' ) + { + inString = !inString; + } + + if( (cmdLine[i] <= ' ' && !inString) || i == len ) + { + if( inArg ) + { + if( str[i-1] == '"' ) + { + str[i-1] = '\0'; + } + else + { + str[i] = '\0'; + } + + inArg = FALSE; + } + } + else if( !inArg && cmdLine[i] != '"' ) + { + inArg = TRUE; + + *aptr++ = &str[i]; + } + } + + *argCount = numArgs; + + return (RwChar **)res; +} + +/* + ***************************************************************************** + */ +void InitialiseLanguage() +{ + WORD primUserLCID = PRIMARYLANGID(GetSystemDefaultLCID()); + WORD primSystemLCID = PRIMARYLANGID(GetUserDefaultLCID()); + WORD primLayout = PRIMARYLANGID((DWORD_PTR)GetKeyboardLayout(0)); + + WORD subUserLCID = SUBLANGID(GetSystemDefaultLCID()); + WORD subSystemLCID = SUBLANGID(GetUserDefaultLCID()); + WORD subLayout = SUBLANGID((DWORD_PTR)GetKeyboardLayout(0)); + + if ( primUserLCID == LANG_GERMAN + || primSystemLCID == LANG_GERMAN + || primLayout == LANG_GERMAN ) + { + CGame::nastyGame = false; + FrontEndMenuManager.m_PrefsAllowNastyGame = false; + CGame::germanGame = true; + } + + if ( primUserLCID == LANG_FRENCH + || primSystemLCID == LANG_FRENCH + || primLayout == LANG_FRENCH ) + { + CGame::nastyGame = false; + FrontEndMenuManager.m_PrefsAllowNastyGame = false; + CGame::frenchGame = true; + } + + if ( subUserLCID == SUBLANG_ENGLISH_AUS + || subSystemLCID == SUBLANG_ENGLISH_AUS + || subLayout == SUBLANG_ENGLISH_AUS ) + CGame::noProstitutes = true; + +#ifdef NASTY_GAME + CGame::nastyGame = true; + FrontEndMenuManager.m_PrefsAllowNastyGame = true; + CGame::noProstitutes = false; +#endif + + int32 lang; + + switch ( primSystemLCID ) + { + case LANG_GERMAN: + { + lang = LANG_GERMAN; + break; + } + case LANG_FRENCH: + { + lang = LANG_FRENCH; + break; + } + case LANG_SPANISH: + { + lang = LANG_SPANISH; + break; + } + case LANG_ITALIAN: + { + lang = LANG_ITALIAN; + break; + } + default: + { + lang = ( subSystemLCID == SUBLANG_ENGLISH_AUS ) ? -99 : LANG_ENGLISH; + break; + } + } + + FrontEndMenuManager.OS_Language = primUserLCID; + + switch ( lang ) + { + case LANG_GERMAN: + { + FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_GERMAN; + break; + } + case LANG_SPANISH: + { + FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_SPANISH; + break; + } + case LANG_FRENCH: + { + FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_FRENCH; + break; + } + case LANG_ITALIAN: + { + FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_ITALIAN; + break; + } + default: + { + FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_AMERICAN; + break; + } + } + + TheText.Unload(); + TheText.Load(); +} + +/* + ***************************************************************************** + */ +void CenterVideo(void) +{ + HRESULT hr = S_OK; + RECT rect; + + ASSERT(pVW != nil); + + GetClientRect(PSGLOBAL(window), &rect); + + JIF(pVW->SetWindowPosition(rect.left, rect.top, rect.right, rect.bottom)); + + JIF(pVW->put_MessageDrain((OAHWND) PSGLOBAL(window))); + + SetFocus(PSGLOBAL(window)); +} + +/* + ***************************************************************************** + */ +void PlayMovieInWindow(int cmdShow, const char* szFile) +{ + WCHAR wFileName[256]; + HRESULT hr; + + // Clear open dialog remnants before calling RenderFile() + UpdateWindow(PSGLOBAL(window)); + + // Convert filename to wide character string + MultiByteToWideChar(CP_ACP, 0, szFile, -1, wFileName, sizeof(wFileName) - 1); + + // Initialize COM +#ifdef FIX_BUGS // will also return S_FALSE if it has already been inited in the same thread + CoInitialize(nil); +#else + JIF(CoInitialize(nil)); +#endif + + // Get the interface for DirectShow's GraphBuilder + JIF(CoCreateInstance(CLSID_FilterGraph, nil, CLSCTX_INPROC, + IID_IGraphBuilder, (void **)&pGB)); + + if(SUCCEEDED(hr)) + { + // Have the graph builder construct its the appropriate graph automatically + JIF(pGB->RenderFile(&wFileName[0], nil)); + + // QueryInterface for DirectShow interfaces + JIF(pGB->QueryInterface(IID_IMediaControl, (void **)&pMC)); + JIF(pGB->QueryInterface(IID_IMediaEventEx, (void **)&pME)); + JIF(pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS)); + + // Query for video interfaces, which may not be relevant for audio files + JIF(pGB->QueryInterface(IID_IVideoWindow, (void **)&pVW)); + + JIF(pVW->put_Owner((OAHWND) PSGLOBAL(window))); + JIF(pVW->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN)); + + // Have the graph signal event via window callbacks for performance + JIF(pME->SetNotifyWindow((OAHWND)PSGLOBAL(window), WM_GRAPHNOTIFY, 0)); + + CenterVideo(); + + // Run the graph to play the media file + JIF(pMC->Run()); + + SetFocus(PSGLOBAL(window)); + } + + ASSERT(pGB != nil); + ASSERT(pVW != nil); + ASSERT(pME != nil); + ASSERT(pMC != nil); + + if(FAILED(hr)) + CloseClip(); +} + +/* + ***************************************************************************** + */ +void CloseInterfaces(void) +{ + // Release and zero DirectShow interfaces + SAFE_RELEASE(pME); + SAFE_RELEASE(pMS); + SAFE_RELEASE(pMC); + SAFE_RELEASE(pVW); + SAFE_RELEASE(pGB); +} + +/* + ***************************************************************************** + */ +void CloseClip(void) +{ + HRESULT hr; + + // Stop playback + if(pMC) + hr = pMC->Stop(); + + // Free DirectShow interfaces + CloseInterfaces(); +} + +/* + ***************************************************************************** + */ +void HandleExit() +{ + MSG message; + while ( PeekMessage(&message, nil, 0U, 0U, PM_REMOVE|PM_NOYIELD) ) + { + if( message.message == WM_QUIT ) + { + RsGlobal.quit = TRUE; + } + else + { + TranslateMessage(&message); + DispatchMessage(&message); + } + } +} + +/* + ***************************************************************************** + */ +int PASCAL +WinMain(HINSTANCE instance, + HINSTANCE prevInstance __RWUNUSED__, + CMDSTR cmdLine, + int cmdShow) +{ + MSG message; + RwV2d pos; + RwInt32 argc, i; + RwChar **argv; + SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, nil, SPIF_SENDCHANGE); + +#ifndef MASTER + if (strstr(cmdLine, "-console")) + { + AllocConsole(); + freopen("CONIN$", "r", stdin); + freopen("CONOUT$", "w", stdout); + freopen("CONOUT$", "w", stderr); + } +#endif + +#ifdef USE_CUSTOM_ALLOCATOR + InitMemoryMgr(); +#endif + + /* + * Initialize the platform independent data. + * This will in turn initialize the platform specific data... + */ + if( RsEventHandler(rsINITIALIZE, nil) == rsEVENTERROR ) + { + return FALSE; + } + + /* + * Register the window class... + */ + if( !InitApplication(instance) ) + { + return FALSE; + } + + /* + * Get proper command line params, cmdLine passed to us does not + * work properly under all circumstances... + */ + cmdLine = GetCommandLine(); + + /* + * Parse command line into standard (argv, argc) parameters... + */ + argv = CommandLineToArgv(cmdLine, &argc); + + + /* + * Parse command line parameters (except program name) one at + * a time BEFORE RenderWare initialization... + */ + for(i=1; iNewState.CheckForInput() ) + ++gGameState; + else if ( CPad::GetPad(0)->GetLeftMouseJustDown() ) + ++gGameState; + else if ( CPad::GetPad(0)->GetEnterJustDown() ) + ++gGameState; + else if ( CPad::GetPad(0)->GetCharJustDown(' ') ) + ++gGameState; + else if ( CPad::GetPad(0)->GetAltJustDown() ) + ++gGameState; + else if ( CPad::GetPad(0)->GetTabJustDown() ) + ++gGameState; + + break; + } + + case GS_INIT_INTRO_MPEG: + { +#ifdef NO_MOVIES + if (!gbNoMovies) +#endif + CloseClip(); +#ifndef FIX_BUGS + CoUninitialize(); +#endif + + if ( FrontEndMenuManager.OS_Language == LANG_FRENCH || FrontEndMenuManager.OS_Language == LANG_GERMAN ) + PlayMovieInWindow(cmdShow, "movies\\GTAtitlesGER.mpg"); + else + PlayMovieInWindow(cmdShow, "movies\\GTAtitles.mpg"); + + gGameState = GS_INTRO_MPEG; + TRACE("gGameState = GS_INTRO_MPEG;"); + break; + } + + case GS_INTRO_MPEG: + { + CPad::UpdatePads(); + + if ( startupDeactivate || ControlsManager.GetJoyButtonJustDown() != 0 ) + ++gGameState; + else if ( CPad::GetPad(0)->NewState.CheckForInput() ) + ++gGameState; + else if ( CPad::GetPad(0)->GetLeftMouseJustDown() ) + ++gGameState; + else if ( CPad::GetPad(0)->GetEnterJustDown() ) + ++gGameState; + else if ( CPad::GetPad(0)->GetCharJustDown(' ') ) + ++gGameState; + else if ( CPad::GetPad(0)->GetAltJustDown() ) + ++gGameState; + else if ( CPad::GetPad(0)->GetTabJustDown() ) + ++gGameState; + + break; + } + + case GS_INIT_ONCE: + { +#ifdef NO_MOVIES + if (!gbNoMovies) +#endif + CloseClip(); +#ifndef FIX_BUGS + CoUninitialize(); +#endif + +#ifdef FIX_BUGS + // draw one frame because otherwise we'll end up looking at black screen for a while if vsync is on + RsCameraShowRaster(Scene.camera); +#endif + +#ifdef PS2_MENU + extern char version_name[64]; + if ( CGame::frenchGame || CGame::germanGame ) + LoadingScreen(NULL, version_name, "loadsc24"); + else + LoadingScreen(NULL, version_name, "loadsc0"); + + printf("Into TheGame!!!\n"); +#else + LoadingScreen(nil, nil, "loadsc0"); + // LoadingScreen(nil, nil, "loadsc0"); // duplicate +#endif + if ( !CGame::InitialiseOnceAfterRW() ) + RsGlobal.quit = TRUE; + +#ifdef PS2_MENU + gGameState = GS_INIT_PLAYING_GAME; +#else + gGameState = GS_INIT_FRONTEND; + TRACE("gGameState = GS_INIT_FRONTEND;"); +#endif + break; + } + +#ifndef PS2_MENU + case GS_INIT_FRONTEND: + { + LoadingScreen(nil, nil, "loadsc0"); + // LoadingScreen(nil, nil, "loadsc0"); // duplicate + + FrontEndMenuManager.m_bGameNotLoaded = true; + + FrontEndMenuManager.m_bStartUpFrontEndRequested = true; + + if ( defaultFullscreenRes ) + { + defaultFullscreenRes = FALSE; + FrontEndMenuManager.m_nPrefsVideoMode = GcurSelVM; + FrontEndMenuManager.m_nDisplayVideoMode = GcurSelVM; + } + + gGameState = GS_FRONTEND; + TRACE("gGameState = GS_FRONTEND;"); + break; + } + + case GS_FRONTEND: + { + GetWindowPlacement(PSGLOBAL(window), &wp); + + if (wp.showCmd != SW_SHOWMINIMIZED) + RsEventHandler(rsFRONTENDIDLE, nil); + +#ifdef PS2_MENU + if ( !FrontEndMenuManager.m_bMenuActive || TheMemoryCard.m_bWantToLoad ) +#else + if ( !FrontEndMenuManager.m_bMenuActive || FrontEndMenuManager.m_bWantToLoad ) +#endif + { + gGameState = GS_INIT_PLAYING_GAME; + TRACE("gGameState = GS_INIT_PLAYING_GAME;"); + } + +#ifdef PS2_MENU + if (TheMemoryCard.m_bWantToLoad ) +#else + if ( FrontEndMenuManager.m_bWantToLoad ) +#endif + { + InitialiseGame(); + FrontEndMenuManager.m_bGameNotLoaded = false; + gGameState = GS_PLAYING_GAME; + TRACE("gGameState = GS_PLAYING_GAME;"); + } + break; + } +#endif + + case GS_INIT_PLAYING_GAME: + { +#ifdef PS2_MENU + CGame::Initialise("DATA\\GTA3.DAT"); + + //LoadingScreen("Starting Game", NULL, GetRandomSplashScreen()); + + if ( TheMemoryCard.CheckCardInserted(CARD_ONE) == CMemoryCard::NO_ERR_SUCCESS + && TheMemoryCard.ChangeDirectory(CARD_ONE, TheMemoryCard.Cards[CARD_ONE].dir) + && TheMemoryCard.FindMostRecentFileName(CARD_ONE, TheMemoryCard.MostRecentFile) == true + && TheMemoryCard.CheckDataNotCorrupt(TheMemoryCard.MostRecentFile)) + { + strcpy(TheMemoryCard.LoadFileName, TheMemoryCard.MostRecentFile); + TheMemoryCard.b_FoundRecentSavedGameWantToLoad = true; + + if (CMenuManager::m_PrefsLanguage != TheMemoryCard.GetLanguageToLoad()) + { + CMenuManager::m_PrefsLanguage = TheMemoryCard.GetLanguageToLoad(); + TheText.Unload(); + TheText.Load(); + } + + CGame::currLevel = (eLevelName)TheMemoryCard.GetLevelToLoad(); + } +#else + InitialiseGame(); + + FrontEndMenuManager.m_bGameNotLoaded = false; +#endif + gGameState = GS_PLAYING_GAME; + TRACE("gGameState = GS_PLAYING_GAME;"); + break; + } + + case GS_PLAYING_GAME: + { + float ms = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerMillisecond(); + if ( RwInitialised ) + { + if (!FrontEndMenuManager.m_PrefsFrameLimiter || (1000.0f / (float)RsGlobal.maxFPS) < ms) + RsEventHandler(rsIDLE, (void *)TRUE); + } + break; + } + } + } + else + { + if ( RwCameraBeginUpdate(Scene.camera) ) + { + RwCameraEndUpdate(Scene.camera); + ForegroundApp = TRUE; + RsEventHandler(rsACTIVATE, (void *)TRUE); + } + + WaitMessage(); + } + } + + + /* + * About to shut down - block resize events again... + */ + RwInitialised = FALSE; + + FrontEndMenuManager.UnloadTextures(); +#ifdef PS2_MENU + if ( !(FrontEndMenuManager.m_bWantToRestart || TheMemoryCard.b_FoundRecentSavedGameWantToLoad)) + break; +#else + if ( !FrontEndMenuManager.m_bWantToRestart ) + break; +#endif + + CPad::ResetCheats(); + CPad::StopPadsShaking(); + + DMAudio.ChangeMusicMode(MUSICMODE_DISABLE); + +#ifdef PS2_MENU + CGame::ShutDownForRestart(); +#endif + CTimer::Stop(); + +#ifdef PS2_MENU + if (FrontEndMenuManager.m_bWantToRestart || TheMemoryCard.b_FoundRecentSavedGameWantToLoad) + { + if (TheMemoryCard.b_FoundRecentSavedGameWantToLoad) + { + FrontEndMenuManager.m_bWantToRestart = true; + TheMemoryCard.m_bWantToLoad = true; + } + + CGame::InitialiseWhenRestarting(); + DMAudio.ChangeMusicMode(MUSICMODE_GAME); + FrontEndMenuManager.m_bWantToRestart = false; + + continue; + } + + CGame::ShutDown(); + CTimer::Stop(); + + break; +#else + if ( FrontEndMenuManager.m_bWantToLoad ) + { + CGame::ShutDownForRestart(); + CGame::InitialiseWhenRestarting(); + DMAudio.ChangeMusicMode(MUSICMODE_GAME); + LoadSplash(GetLevelSplashScreen(CGame::currLevel)); + FrontEndMenuManager.m_bWantToLoad = false; + } + else + { +#ifndef MASTER + if ( gbModelViewer ) + CAnimViewer::Shutdown(); + else +#endif + if ( gGameState == GS_PLAYING_GAME ) + CGame::ShutDown(); + + CTimer::Stop(); + + if ( FrontEndMenuManager.m_bFirstTime == true ) + { + gGameState = GS_INIT_FRONTEND; + TRACE("gGameState = GS_INIT_FRONTEND;"); + } + else + { + gGameState = GS_INIT_PLAYING_GAME; + TRACE("gGameState = GS_INIT_PLAYING_GAME;"); + } + } + + FrontEndMenuManager.m_bFirstTime = false; + FrontEndMenuManager.m_bWantToRestart = false; +#endif + } + + +#ifndef MASTER + if ( gbModelViewer ) + CAnimViewer::Shutdown(); + else +#endif + if ( gGameState == GS_PLAYING_GAME ) + CGame::ShutDown(); + + DMAudio.Terminate(); + + _psFreeVideoModeList(); + + + /* + * Tidy up the 3D (RenderWare) components of the application... + */ + RsEventHandler(rsRWTERMINATE, nil); + + /* + * Kill the window... + */ + DestroyWindow(PSGLOBAL(window)); + + /* + * Free the platform dependent data... + */ + RsEventHandler(rsTERMINATE, nil); + + /* + * Free the argv strings... + */ + free(argv); + + ShowCursor(TRUE); + + SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &SavedStickyKeys, SPIF_SENDCHANGE); + SystemParametersInfo(SPI_SETPOWEROFFACTIVE, TRUE, nil, SPIF_SENDCHANGE); + SystemParametersInfo(SPI_SETLOWPOWERACTIVE, TRUE, nil, SPIF_SENDCHANGE); + SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, TRUE, nil, SPIF_SENDCHANGE); + + SetErrorMode(0); + + return message.wParam; +} + +/* + ***************************************************************************** + */ + +#define DEVICE_AXIS_MIN -2000 +#define DEVICE_AXIS_MAX 2000 + + +HRESULT _InputInitialise() +{ + HRESULT hr; + + // Create a DInput object + if( FAILED( hr = DirectInput8Create( GetModuleHandle(nil), DIRECTINPUT_VERSION, + IID_IDirectInput8, (VOID**)&PSGLOBAL(dinterface), nil ) ) ) + return hr; + + return S_OK; +} + +HRESULT _InputInitialiseMouse(bool exclusive) +{ + HRESULT hr; + + // Obtain an interface to the system mouse device. + if( FAILED( hr = PSGLOBAL(dinterface)->CreateDevice( GUID_SysMouse, &PSGLOBAL(mouse), nil ) ) ) + return hr; + + // Set the data format to "mouse format" - a predefined data format + // + // A data format specifies which controls on a device we + // are interested in, and how they should be reported. + // + // This tells DirectInput that we will be passing a + // DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState. + if( FAILED( hr = PSGLOBAL(mouse)->SetDataFormat( &c_dfDIMouse2 ) ) ) + return hr; + + if( FAILED( hr = PSGLOBAL(mouse)->SetCooperativeLevel( PSGLOBAL(window), (exclusive ? DISCL_EXCLUSIVE : DISCL_NONEXCLUSIVE) | DISCL_FOREGROUND ) ) ) + return hr; + + // Acquire the newly created device + PSGLOBAL(mouse)->Acquire(); + + return S_OK; +} + +RwV2d leftStickPos; +RwV2d rightStickPos; + +HRESULT CapturePad(RwInt32 padID) +{ + HRESULT hr; + DIJOYSTATE2 js; + LPDIRECTINPUTDEVICE8 *pPad = nil; + + if( padID == 0 ) + pPad = &PSGLOBAL(joy1); + else if( padID == 1) + pPad = &PSGLOBAL(joy2); + else + assert("invalid padID"); + + if ( nil == (*pPad) ) + return S_OK; + + // Poll the device to read the current state + hr = (*pPad)->Poll(); + + if( FAILED(hr) ) + { + // DInput is telling us that the input stream has been + // interrupted. We aren't tracking any state between polls, so + // we don't have any special reset that needs to be done. We + // just re-acquire and try again. + hr = (*pPad)->Acquire(); + while( hr == DIERR_INPUTLOST ) + hr = (*pPad)->Acquire(); + + // hr may be DIERR_OTHERAPPHASPRIO or other errors. This + // may occur when the app is minimized or in the process of + // switching, so just try again later + + if( FAILED(hr) ) + return hr; + + hr = (*pPad)->Poll(); + if( FAILED(hr) ) + return hr; + } + + // Get the input's device state + if( FAILED( hr = (*pPad)->GetDeviceState( sizeof(DIJOYSTATE2), &js ) ) ) + return hr; // The device should have been acquired during the Poll() + + if ( ControlsManager.m_bFirstCapture == true ) + { + memcpy(&ControlsManager.m_OldState, &js, sizeof(DIJOYSTATE2)); + memcpy(&ControlsManager.m_NewState, &js, sizeof(DIJOYSTATE2)); + + ControlsManager.m_bFirstCapture = false; + } + else + { + memcpy(&ControlsManager.m_OldState, &ControlsManager.m_NewState, sizeof(DIJOYSTATE2)); + memcpy(&ControlsManager.m_NewState, &js, sizeof(DIJOYSTATE2)); + } + + RsPadButtonStatus bs; + bs.padID = padID; + + RsPadEventHandler(rsPADBUTTONUP, (void *)&bs); + + bool deviceAvailable = (*pPad) != nil; + + if ( deviceAvailable ) + { + leftStickPos.x = (float)js.lX / (float)((DEVICE_AXIS_MAX - DEVICE_AXIS_MIN) / 2); + leftStickPos.y = (float)js.lY / (float)((DEVICE_AXIS_MAX - DEVICE_AXIS_MIN) / 2); + + if (LOWORD(js.rgdwPOV[0]) != 0xFFFF) + { + float angle = DEGTORAD((float)js.rgdwPOV[0] / 100.0f); + + leftStickPos.x = Sin(angle); + leftStickPos.y = -Cos(angle); + } + + if ( AllValidWinJoys.m_aJoys[bs.padID].m_bHasAxisR && AllValidWinJoys.m_aJoys[bs.padID].m_bHasAxisZ ) + { + rightStickPos.x = (float)js.lZ / (float)((DEVICE_AXIS_MAX - DEVICE_AXIS_MIN) / 2); + rightStickPos.y = (float)js.lRz / (float)((DEVICE_AXIS_MAX - DEVICE_AXIS_MIN) / 2); + } + } + + { + if (CPad::m_bMapPadOneToPadTwo) + bs.padID = 1; + + RsPadEventHandler(rsPADBUTTONUP, (void *)&bs); + RsPadEventHandler(rsPADBUTTONDOWN, (void *)&bs); + } + + { + if (CPad::m_bMapPadOneToPadTwo) + bs.padID = 1; + + CPad *pad = CPad::GetPad(bs.padID); + + if ( Abs(leftStickPos.x) > 0.3f ) + pad->PCTempJoyState.LeftStickX = (int32)(leftStickPos.x * 128.0f); + + if ( Abs(leftStickPos.y) > 0.3f ) + pad->PCTempJoyState.LeftStickY = (int32)(leftStickPos.y * 128.0f); + + if ( Abs(rightStickPos.x) > 0.3f ) + pad->PCTempJoyState.RightStickX = (int32)(rightStickPos.x * 128.0f); + + if ( Abs(rightStickPos.y) > 0.3f ) + pad->PCTempJoyState.RightStickY = (int32)(rightStickPos.y * 128.0f); + } + + return S_OK; +} + +void _InputInitialiseJoys() +{ + DIPROPDWORD prop; + DIDEVCAPS devCaps; + + for ( int32 i = 0; i < _TODOCONST(2); i++ ) + AllValidWinJoys.ClearJoyInfo(i); + + _InputAddJoys(); + + if ( PSGLOBAL(joy1) != nil ) + { + devCaps.dwSize = sizeof(DIDEVCAPS); + PSGLOBAL(joy1)->GetCapabilities(&devCaps); + + prop.diph.dwSize = sizeof(DIPROPDWORD); + prop.diph.dwHeaderSize = sizeof(DIPROPHEADER); + prop.diph.dwObj = 0; + prop.diph.dwHow = 0; + + PSGLOBAL(joy1)->GetProperty(DIPROP_VIDPID, (LPDIPROPHEADER)&prop); + AllValidWinJoys.m_aJoys[0].m_nVendorID = LOWORD(prop.dwData); + AllValidWinJoys.m_aJoys[0].m_nProductID = HIWORD(prop.dwData); + AllValidWinJoys.m_aJoys[0].m_bInitialised = true; + + ControlsManager.InitDefaultControlConfigJoyPad(devCaps.dwButtons); + } + + if ( PSGLOBAL(joy2) != nil ) + { + PSGLOBAL(joy2)->GetProperty(DIPROP_VIDPID, (LPDIPROPHEADER)&prop); + AllValidWinJoys.m_aJoys[1].m_nVendorID = LOWORD(prop.dwData); + AllValidWinJoys.m_aJoys[1].m_nProductID = HIWORD(prop.dwData); + AllValidWinJoys.m_aJoys[1].m_bInitialised = true; + } +} + +void _InputAddJoyStick(LPDIRECTINPUTDEVICE8 lpDevice, INT num) +{ + DIDEVICEOBJECTINSTANCE objInst; + + objInst.dwSize = sizeof( DIDEVICEOBJECTINSTANCE ); + + DIPROPRANGE range; + range.diph.dwSize = sizeof(DIPROPRANGE); + range.diph.dwHeaderSize = sizeof(DIPROPHEADER); + range.lMin = DEVICE_AXIS_MIN; + range.lMax = DEVICE_AXIS_MAX; + range.diph.dwHow = DIPH_BYOFFSET; + + // get the info about the object from the device + + range.diph.dwObj = DIJOFS_X; + if ( lpDevice != nil ) + { + if ( SUCCEEDED( lpDevice->GetObjectInfo( &objInst, DIJOFS_X, DIPH_BYOFFSET ) ) ) + { + if( FAILED( lpDevice->SetProperty( DIPROP_RANGE, (LPCDIPROPHEADER)&range ) ) ) + return; + else + ; + } + } + + range.diph.dwObj = DIJOFS_Y; + if ( lpDevice != nil ) + { + if ( SUCCEEDED( lpDevice->GetObjectInfo( &objInst, DIJOFS_Y, DIPH_BYOFFSET ) ) ) + { + if( FAILED( lpDevice->SetProperty( DIPROP_RANGE, (LPCDIPROPHEADER)&range ) ) ) + return; + else + ; + } + } + + range.diph.dwObj = DIJOFS_Z; + if ( lpDevice != nil ) + { + if ( SUCCEEDED( lpDevice->GetObjectInfo( &objInst, DIJOFS_Z, DIPH_BYOFFSET ) ) ) + { + if( FAILED( lpDevice->SetProperty( DIPROP_RANGE, (LPCDIPROPHEADER)&range ) ) ) + return; + else + AllValidWinJoys.m_aJoys[num].m_bHasAxisZ = true; // z rightStickPos.x + } + } + + range.diph.dwObj = DIJOFS_RZ; + if ( lpDevice != nil ) + { + if ( SUCCEEDED( lpDevice->GetObjectInfo( &objInst, DIJOFS_RZ, DIPH_BYOFFSET ) ) ) + { + if( FAILED( lpDevice->SetProperty( DIPROP_RANGE, (LPCDIPROPHEADER)&range ) ) ) + return; + else + AllValidWinJoys.m_aJoys[num].m_bHasAxisR = true; // r rightStickPos.y + } + } +} + +HRESULT _InputAddJoys() +{ + HRESULT hr; + + hr = PSGLOBAL(dinterface)->EnumDevices(DI8DEVCLASS_GAMECTRL, _InputEnumDevicesCallback, nil, DIEDFL_ATTACHEDONLY ); + + if( FAILED(hr) ) + return hr; + + if ( PSGLOBAL(joy1) == nil ) + return S_FALSE; + + _InputAddJoyStick(PSGLOBAL(joy1), 0); + + if ( PSGLOBAL(joy2) == nil ) + return S_OK; // we have one device already so return OK and ignore second + + _InputAddJoyStick(PSGLOBAL(joy2), 1); + + return S_OK; +} + +HRESULT _InputGetMouseState(DIMOUSESTATE2 *state) +{ + HRESULT hr; + + if ( PSGLOBAL(mouse) == nil ) + return S_FALSE; + + // Get the input's device state, and put the state in dims + ZeroMemory( state, sizeof(DIMOUSESTATE2) ); + + hr = PSGLOBAL(mouse)->GetDeviceState( sizeof(DIMOUSESTATE2), state ); + + if( FAILED(hr) ) + { + // DirectInput may be telling us that the input stream has been + // interrupted. We aren't tracking any state between polls, so + // we don't have any special reset that needs to be done. + // We just re-acquire and try again. + + // If input is lost then acquire and keep trying + hr = PSGLOBAL(mouse)->Acquire(); + while( hr == DIERR_INPUTLOST ) + hr = PSGLOBAL(mouse)->Acquire(); + + ZeroMemory( state, sizeof(DIMOUSESTATE2) ); + hr = PSGLOBAL(mouse)->GetDeviceState( sizeof(DIMOUSESTATE2), state ); + + return hr; + } + + return S_OK; +} + +void _InputShutdown() +{ + SAFE_RELEASE(PSGLOBAL(dinterface)); +} + +void _InputShutdownMouse() +{ + if (PSGLOBAL(mouse) == nil) + return; + + PSGLOBAL(mouse)->Unacquire(); + SAFE_RELEASE(PSGLOBAL(mouse)); +} + +bool _InputMouseNeedsExclusive(void) +{ + // FIX: I don't know why R* needed that, but it causes infamous mouse bug on modern systems. + // Probably DirectInput bug, since Acquire() and GetDeviceState() reports everything A-OK. +#ifdef FIX_BUGS + return false; +#endif + RwVideoMode vm; + RwEngineGetVideoModeInfo(&vm, GcurSelVM); + + return vm.flags & rwVIDEOMODEEXCLUSIVE; +} + +BOOL CALLBACK _InputEnumDevicesCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext ) +{ + HRESULT hr; + + static INT Count = 0; + + LPDIRECTINPUTDEVICE8 *pJoystick = nil; + + if ( Count == 0 ) + pJoystick = &PSGLOBAL(joy1); + else if ( Count == 1 ) + pJoystick = &PSGLOBAL(joy2); + else + assert("too many pads"); + + // Obtain an interface to the enumerated joystick. + hr = PSGLOBAL(dinterface)->CreateDevice( pdidInstance->guidInstance, pJoystick, nil ); + + // If it failed, then we can't use this joystick. (Maybe the user unplugged + // it while we were in the middle of enumerating it.) + if( hr != S_OK ) + return DIENUM_CONTINUE; + + hr = (*pJoystick)->SetDataFormat( &c_dfDIJoystick2 ); + if( hr != S_OK ) + { + (*pJoystick)->Release(); + return DIENUM_CONTINUE; + } + + ++Count; + + hr = (*pJoystick)->SetCooperativeLevel( PSGLOBAL(window), DISCL_NONEXCLUSIVE|DISCL_FOREGROUND ); + if( hr != S_OK ) + { + (*pJoystick)->Release(); +#ifdef FIX_BUGS + // BUG: enum will be called with Count == 2, which will write to a null pointer + // So decrement count again since we're not using this pad + --Count; +#endif + return DIENUM_CONTINUE; + } + + // Stop enumeration. Note: we're just taking the first two joysticks we get. You + // could store all the enumerated joysticks and let the user pick. + if ( Count == 2 ) + return DIENUM_STOP; + + return DIENUM_CONTINUE; +} + +BOOL _InputTranslateKey(RsKeyCodes *rs, UINT flag, UINT key) +{ + *rs = rsNULL; + + switch ( key ) + { + case VK_SHIFT: + { + if ( _dwOperatingSystemVersion == OS_WIN98 ) + *rs = rsSHIFT; + break; + } + + case VK_RETURN: + { + if ( _InputIsExtended(flag) ) + *rs = rsPADENTER; + else + *rs = rsENTER; + break; + } + + case VK_CONTROL: + { + if ( _InputIsExtended(flag) ) + *rs = rsRCTRL; + else + *rs = rsLCTRL; + break; + } + + case VK_MENU: + { + if ( _InputIsExtended(flag) ) + *rs = rsRALT; + else + *rs = rsLALT; + break; + } + + case VK_APPS: + { + *rs = rsAPPS; + break; + } + + case VK_PAUSE: + { + *rs = rsPAUSE; + break; + } + + case VK_CAPITAL: + { + *rs = rsCAPSLK; + break; + } + + case VK_ESCAPE: + { + *rs = rsESC; + break; + } + + case VK_PRIOR: + { + if ( _InputIsExtended(flag) ) + *rs = rsPGUP; + else + *rs = rsPADPGUP; + break; + } + + case VK_NEXT: + { + if ( _InputIsExtended(flag) ) + *rs = rsPGDN; + else + *rs = rsPADPGDN; + break; + } + + case VK_END: + { + if ( _InputIsExtended(flag) ) + *rs = rsEND; + else + *rs = rsPADEND; + break; + } + + case VK_HOME: + { + if ( _InputIsExtended(flag) ) + *rs = rsHOME; + else + *rs = rsPADHOME; + break; + } + + case VK_LEFT: + { + if ( _InputIsExtended(flag) ) + *rs = rsLEFT; + else + *rs = rsPADLEFT; + break; + } + + case VK_UP: + { + if ( _InputIsExtended(flag) ) + *rs = rsUP; + else + *rs = rsPADUP; + break; + } + + case VK_RIGHT: + { + if ( _InputIsExtended(flag) ) + *rs = rsRIGHT; + else + *rs = rsPADRIGHT; + break; + } + + case VK_DOWN: + { + if ( _InputIsExtended(flag) ) + *rs = rsDOWN; + else + *rs = rsPADDOWN; + break; + } + + case VK_INSERT: + { + if ( _InputIsExtended(flag) ) + *rs = rsINS; + else + *rs = rsPADINS; + break; + } + + case VK_DELETE: + { + if ( _InputIsExtended(flag) ) + *rs = rsDEL; + else + *rs = rsPADDEL; + break; + } + + case VK_LWIN: + { + *rs = rsLWIN; + break; + } + + case VK_RWIN: + { + *rs = rsRWIN; + break; + } + + case VK_NUMPAD0: + { + *rs = rsPADINS; + break; + } + + case VK_NUMPAD1: + { + *rs = rsPADEND; + break; + } + + case VK_NUMPAD2: + { + *rs = rsPADDOWN; + break; + } + + case VK_NUMPAD3: + { + *rs = rsPADPGDN; + break; + } + + case VK_NUMPAD4: + { + *rs = rsPADLEFT; + break; + } + + case VK_NUMPAD5: + { + *rs = rsPAD5; + break; + } + + case VK_NUMPAD6: + { + *rs = rsPADRIGHT; + break; + } + + case VK_NUMPAD7: + { + *rs = rsPADHOME; + break; + } + + case VK_NUMPAD8: + { + *rs = rsPADUP; + break; + } + + case VK_NUMPAD9: + { + *rs = rsPADPGUP; + break; + } + + case VK_MULTIPLY: + { + *rs = rsTIMES; + break; + } + + case VK_DIVIDE: + { + *rs = rsDIVIDE; + break; + } + + case VK_ADD: + { + *rs = rsPLUS; + break; + } + + case VK_SUBTRACT: + { + *rs = rsMINUS; + break; + } + + case VK_DECIMAL: + { + *rs = rsPADDEL; + break; + } + + case VK_F1: + { + *rs = rsF1; + break; + } + + case VK_F2: + { + *rs = rsF2; + break; + } + + case VK_F3: + { + *rs = rsF3; + break; + } + + case VK_F4: + { + *rs = rsF4; + break; + } + + case VK_F5: + { + *rs = rsF5; + break; + } + + case VK_F6: + { + *rs = rsF6; + break; + } + + case VK_F7: + { + *rs = rsF7; + break; + } + + case VK_F8: + { + *rs = rsF8; + break; + } + + case VK_F9: + { + *rs = rsF9; + break; + } + + case VK_F10: + { + *rs = rsF10; + break; + } + + case VK_F11: + { + *rs = rsF11; + break; + } + + case VK_F12: + { + *rs = rsF12; + break; + } + + case VK_NUMLOCK: + { + *rs = rsNUMLOCK; + break; + } + + case VK_SCROLL: + { + *rs = rsSCROLL; + break; + } + + case VK_BACK: + { + *rs = rsBACKSP; + break; + } + + case VK_TAB: + { + *rs = rsTAB; + break; + } + + default: + { + UINT vkey = MapVirtualKey(key, MAPVK_VK_TO_CHAR) & 0xFFFF; + if ( vkey < 255 ) + *rs = (RsKeyCodes)vkey; + break; + } + } + + return *rs != rsNULL; +} + +void _InputTranslateShiftKeyUpDown(RsKeyCodes *rs) +{ + if ( _dwOperatingSystemVersion != OS_WIN98 ) + { + if ( _InputTranslateShiftKey(rs, VK_LSHIFT, TRUE) ) + RsKeyboardEventHandler(rsKEYDOWN, rs); + if ( _InputTranslateShiftKey(rs, VK_RSHIFT, TRUE) ) + RsKeyboardEventHandler(rsKEYDOWN, rs); + if ( _InputTranslateShiftKey(rs, VK_LSHIFT, FALSE) ) + RsKeyboardEventHandler(rsKEYUP, rs); + if ( _InputTranslateShiftKey(rs, VK_RSHIFT, FALSE) ) + RsKeyboardEventHandler(rsKEYUP, rs); + } +} + +BOOL _InputTranslateShiftKey(RsKeyCodes *rs, UINT key, BOOLEAN bDown) +{ + *rs = rsNULL; + switch ( key ) + { + case VK_LSHIFT: + { + if ( bDown == (GetKeyState(VK_LSHIFT) & 0x8000) >> 15 ) + *rs = rsLSHIFT; + break; + } + + case VK_RSHIFT: + { + if ( bDown == (GetKeyState(VK_RSHIFT) & 0x8000) >> 15 ) + *rs = rsRSHIFT; + break; + } + + default: + { + return *rs != rsNULL; + } + } + + return TRUE; +} + +BOOL _InputIsExtended(INT flag) +{ + return (flag & 0x1000000) != 0; +} + +#if (defined(_MSC_VER)) +int strcasecmp(const char *str1, const char *str2) +{ + return _strcmpi(str1, str2); +} +int strncasecmp(const char *str1, const char *str2, size_t len) +{ + return _strnicmp(str1, str2, len); +} +#endif +#endif diff --git a/src/miami/skel/win/win.h b/src/miami/skel/win/win.h new file mode 100644 index 00000000..be840898 --- /dev/null +++ b/src/miami/skel/win/win.h @@ -0,0 +1,91 @@ + +// DON'T include directly. crossplatform.h includes this if you're using D3D9 backend(win.cpp). + +#if (!defined(_PLATFORM_WIN_H)) +#define _PLATFORM_WIN_H + +#if (!defined(RSREGSETBREAKALLOC)) +#define RSREGSETBREAKALLOC(_name) /* No op */ +#endif /* (!defined(RSREGSETBREAKALLOC)) */ + +#ifdef __DINPUT_INCLUDED__ +/* platform specfic global data */ +typedef struct +{ + HWND window; + HINSTANCE instance; + RwBool fullScreen; + RwV2d lastMousePos; + + DWORD field_14; + + LPDIRECTINPUT8 dinterface; + LPDIRECTINPUTDEVICE8 mouse; + LPDIRECTINPUTDEVICE8 joy1; + LPDIRECTINPUTDEVICE8 joy2; +} +psGlobalType; + +#define PSGLOBAL(var) (((psGlobalType *)(RsGlobal.ps))->var) + +enum eJoypads +{ + JOYSTICK1 = 0, + JOYSTICK2, + MAX_JOYSTICKS +}; + +enum eJoypadState +{ + JOYPAD_UNUSED, + JOYPAD_ATTACHED, +}; + +struct tJoy +{ + eJoypadState m_State; + bool m_bInitialised; + bool m_bHasAxisZ; + bool m_bHasAxisR; + int m_nVendorID; + int m_nProductID; +}; + +class CJoySticks +{ +public: + tJoy m_aJoys[MAX_JOYSTICKS]; + + CJoySticks(); + void ClearJoyInfo(int joyID); +}; + +extern CJoySticks AllValidWinJoys; +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#ifdef __DINPUT_INCLUDED__ +HRESULT _InputInitialise(); +HRESULT CapturePad(RwInt32 padID); +void _InputAddJoyStick(LPDIRECTINPUTDEVICE8 lpDevice, INT num); +HRESULT _InputAddJoys(); +HRESULT _InputGetMouseState(DIMOUSESTATE2 *state); +void _InputShutdown(); +BOOL CALLBACK _InputEnumDevicesCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext ); +BOOL _InputTranslateKey(RsKeyCodes *rs, UINT flag, UINT key); +BOOL _InputTranslateShiftKey(RsKeyCodes *rs, UINT key, BOOLEAN bDown); +BOOL _InputIsExtended(INT flag); +#endif + +void CenterVideo(void); +void CloseClip(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (!defined(_PLATFORM_WIN_H)) */ diff --git a/src/miami/skel/win/win.rc b/src/miami/skel/win/win.rc new file mode 100644 index 00000000..9b5aa305 --- /dev/null +++ b/src/miami/skel/win/win.rc @@ -0,0 +1,47 @@ +#include "resource.h" + +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +//#if !defined(__GNU_C__) +//#include "afxres.h" +//#else +#include "winresrc.h" +//#endif /* !defined(__GNU_C__) */ + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_DIALOG1 DIALOG DISCARDABLE 0, 0, 186, 90 +STYLE DS_MODALFRAME | DS_CENTER | DS_CENTERMOUSE | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Device Selection" +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_DEVICESEL,7,25,172,33,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_VIDMODE,7,46,172,74,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + DEFPUSHBUTTON "EXIT",IDEXIT,103,69,52,14 + DEFPUSHBUTTON "OK",IDOK,28,69,50,14 + LTEXT "Please select the Device To Use:",IDC_SELECTDEVICE,7,7, + 137,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_MAIN_ICON ICON DISCARDABLE "gtavc.ico" + +///////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/src/miami/text/Messages.cpp b/src/miami/text/Messages.cpp new file mode 100644 index 00000000..81339ae0 --- /dev/null +++ b/src/miami/text/Messages.cpp @@ -0,0 +1,815 @@ +#include "common.h" + +#include "Messages.h" +#include "RwHelper.h" +#include "Hud.h" +#include "User.h" +#include "Timer.h" +#include "Text.h" + +#include "ControllerConfig.h" + +#include "Font.h" + +tMessage CMessages::BriefMessages[NUMBRIEFMESSAGES]; +tPreviousBrief CMessages::PreviousBriefs[NUMPREVIOUSBRIEFS]; +tBigMessage CMessages::BIGMessages[NUMBIGMESSAGES]; +char CMessages::PreviousMissionTitle[16]; // unused + +void +CMessages::Init() +{ + ClearMessages(); + + for (int32 i = 0; i < NUMPREVIOUSBRIEFS; i++) { + PreviousBriefs[i].m_pText = nil; + PreviousBriefs[i].m_pString = nil; + } +} + +uint16 +CMessages::GetWideStringLength(wchar *src) +{ + uint16 length = 0; + while (*(src++)) length++; + return length; +} + +void +CMessages::WideStringCopy(wchar *dst, wchar *src, uint16 size) +{ + int32 i = 0; + if (src) { + while (i < size - 1) { + if (!src[i]) break; + dst[i] = src[i]; + i++; + } + } else { + while (i < size - 1) + dst[i++] = '\0'; + } + dst[i] = '\0'; +} + +wchar FixupChar(wchar c) +{ +#ifdef MORE_LANGUAGES + if (CFont::IsJapanese()) + return c & 0x7fff; +#endif + return c; +} + +bool +CMessages::WideStringCompare(wchar *str1, wchar *str2, uint16 size) +{ + uint16 len1 = GetWideStringLength(str1); + uint16 len2 = GetWideStringLength(str2); + if (len1 != len2 && (len1 < size || len2 < size)) + return false; + + for (int32 i = 0; i < size && FixupChar(str1[i]) != '\0'; i++) { + if (FixupChar(str1[i]) != FixupChar(str2[i])) + return false; + } + return true; +} + +void +CMessages::Process() +{ + for (int32 style = 0; style < 6; style++) { + if (BIGMessages[style].m_Stack[0].m_pText != nil && CTimer::GetTimeInMilliseconds() > BIGMessages[style].m_Stack[0].m_nTime + BIGMessages[style].m_Stack[0].m_nStartTime) { + BIGMessages[style].m_Stack[0].m_pText = nil; + + int32 i = 0; + while (i < 3) { + if (BIGMessages[style].m_Stack[i + 1].m_pText == nil) break; + BIGMessages[style].m_Stack[i] = BIGMessages[style].m_Stack[i + 1]; + i++; + } + + BIGMessages[style].m_Stack[i].m_pText = nil; + BIGMessages[style].m_Stack[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); + } + } + + if (BriefMessages[0].m_pText != nil && CTimer::GetTimeInMilliseconds() > BriefMessages[0].m_nTime + BriefMessages[0].m_nStartTime) { + BriefMessages[0].m_pText = nil; + int32 i; + for (i = 0; i < NUMBRIEFMESSAGES-1 && BriefMessages[i + 1].m_pText != nil; i++) { + BriefMessages[i] = BriefMessages[i + 1]; + } + CMessages::BriefMessages[i].m_pText = nil; + CMessages::BriefMessages[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); + if (BriefMessages[0].m_pText != nil) + AddToPreviousBriefArray( + BriefMessages[0].m_pText, + BriefMessages[0].m_nNumber[0], + BriefMessages[0].m_nNumber[1], + BriefMessages[0].m_nNumber[2], + BriefMessages[0].m_nNumber[3], + BriefMessages[0].m_nNumber[4], + BriefMessages[0].m_nNumber[5], + BriefMessages[0].m_pString); + } +} + +void +CMessages::Display() +{ + wchar outstr[256]; + + DefinedState(); + + for (int32 i = 0; i < NUMBIGMESSAGES; i++) { + InsertNumberInString( + BIGMessages[i].m_Stack[0].m_pText, + BIGMessages[i].m_Stack[0].m_nNumber[0], + BIGMessages[i].m_Stack[0].m_nNumber[1], + BIGMessages[i].m_Stack[0].m_nNumber[2], + BIGMessages[i].m_Stack[0].m_nNumber[3], + BIGMessages[i].m_Stack[0].m_nNumber[4], + BIGMessages[i].m_Stack[0].m_nNumber[5], + outstr); + InsertStringInString(outstr, BIGMessages[i].m_Stack[0].m_pString); + InsertPlayerControlKeysInString(outstr); + CHud::SetBigMessage(outstr, i); + } + + InsertNumberInString( + BriefMessages[0].m_pText, + BriefMessages[0].m_nNumber[0], + BriefMessages[0].m_nNumber[1], + BriefMessages[0].m_nNumber[2], + BriefMessages[0].m_nNumber[3], + BriefMessages[0].m_nNumber[4], + BriefMessages[0].m_nNumber[5], + outstr); + InsertStringInString(outstr, BriefMessages[0].m_pString); + InsertPlayerControlKeysInString(outstr); + CHud::SetMessage(outstr); +} + +void +CMessages::AddMessage(wchar *msg, uint32 time, uint16 flag) +{ + wchar outstr[512]; // unused + WideStringCopy(outstr, msg, 256); + InsertPlayerControlKeysInString(outstr); + GetWideStringLength(outstr); + + int32 i = 0; + while (i < NUMBRIEFMESSAGES && BriefMessages[i].m_pText != nil) + i++; + if (i >= NUMBRIEFMESSAGES) return; + + BriefMessages[i].m_pText = msg; + BriefMessages[i].m_nFlag = flag; + BriefMessages[i].m_nTime = time; + BriefMessages[i].m_nStartTime = CTimer::GetTimeInMilliseconds(); + BriefMessages[i].m_nNumber[0] = -1; + BriefMessages[i].m_nNumber[1] = -1; + BriefMessages[i].m_nNumber[2] = -1; + BriefMessages[i].m_nNumber[3] = -1; + BriefMessages[i].m_nNumber[4] = -1; + BriefMessages[i].m_nNumber[5] = -1; + BriefMessages[i].m_pString = nil; + if (i == 0) + AddToPreviousBriefArray( + BriefMessages[0].m_pText, + BriefMessages[0].m_nNumber[0], + BriefMessages[0].m_nNumber[1], + BriefMessages[0].m_nNumber[2], + BriefMessages[0].m_nNumber[3], + BriefMessages[0].m_nNumber[4], + BriefMessages[0].m_nNumber[5], + BriefMessages[0].m_pString); +} + +void +CMessages::AddMessageJumpQ(wchar *msg, uint32 time, uint16 flag) +{ + wchar outstr[512]; // unused + WideStringCopy(outstr, msg, 256); + InsertPlayerControlKeysInString(outstr); + GetWideStringLength(outstr); + + BriefMessages[0].m_pText = msg; + BriefMessages[0].m_nFlag = flag; + BriefMessages[0].m_nTime = time; + BriefMessages[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); + BriefMessages[0].m_nNumber[0] = -1; + BriefMessages[0].m_nNumber[1] = -1; + BriefMessages[0].m_nNumber[2] = -1; + BriefMessages[0].m_nNumber[3] = -1; + BriefMessages[0].m_nNumber[4] = -1; + BriefMessages[0].m_nNumber[5] = -1; + BriefMessages[0].m_pString = nil; + AddToPreviousBriefArray(msg, -1, -1, -1, -1, -1, -1, 0); +} + +void +CMessages::AddMessageSoon(wchar *msg, uint32 time, uint16 flag) +{ + wchar outstr[512]; // unused + WideStringCopy(outstr, msg, 256); + InsertPlayerControlKeysInString(outstr); + GetWideStringLength(outstr); + + if (BriefMessages[0].m_pText != nil) { + for (int i = NUMBRIEFMESSAGES-1; i > 1; i--) + BriefMessages[i] = BriefMessages[i-1]; + + BriefMessages[1].m_pText = msg; + BriefMessages[1].m_nFlag = flag; + BriefMessages[1].m_nTime = time; + BriefMessages[1].m_nStartTime = CTimer::GetTimeInMilliseconds(); + BriefMessages[1].m_nNumber[0] = -1; + BriefMessages[1].m_nNumber[1] = -1; + BriefMessages[1].m_nNumber[2] = -1; + BriefMessages[1].m_nNumber[3] = -1; + BriefMessages[1].m_nNumber[4] = -1; + BriefMessages[1].m_nNumber[5] = -1; + BriefMessages[1].m_pString = nil; + }else{ + BriefMessages[0].m_pText = msg; + BriefMessages[0].m_nFlag = flag; + BriefMessages[0].m_nTime = time; + BriefMessages[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); + BriefMessages[0].m_nNumber[0] = -1; + BriefMessages[0].m_nNumber[1] = -1; + BriefMessages[0].m_nNumber[2] = -1; + BriefMessages[0].m_nNumber[3] = -1; + BriefMessages[0].m_nNumber[4] = -1; + BriefMessages[0].m_nNumber[5] = -1; + BriefMessages[0].m_pString = nil; + AddToPreviousBriefArray(msg, -1, -1, -1, -1, -1, -1, nil); + } +} + +void +CMessages::ClearMessages() +{ + for (int32 i = 0; i < NUMBIGMESSAGES; i++) { + for (int32 j = 0; j < 4; j++) { + BIGMessages[i].m_Stack[j].m_pText = nil; + BIGMessages[i].m_Stack[j].m_pString = nil; + } + } + ClearSmallMessagesOnly(); +} + +void +CMessages::ClearSmallMessagesOnly() +{ + for (int32 i = 0; i < NUMBRIEFMESSAGES; i++) { + BriefMessages[i].m_pText = nil; + BriefMessages[i].m_pString = nil; + } +} + +void +CMessages::AddBigMessage(wchar *msg, uint32 time, uint16 style) +{ + wchar outstr[512]; // unused + WideStringCopy(outstr, msg, 256); + InsertPlayerControlKeysInString(outstr); + GetWideStringLength(outstr); + + BIGMessages[style].m_Stack[0].m_pText = msg; + BIGMessages[style].m_Stack[0].m_nFlag = 0; + BIGMessages[style].m_Stack[0].m_nTime = time; + BIGMessages[style].m_Stack[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); + BIGMessages[style].m_Stack[0].m_nNumber[0] = -1; + BIGMessages[style].m_Stack[0].m_nNumber[1] = -1; + BIGMessages[style].m_Stack[0].m_nNumber[2] = -1; + BIGMessages[style].m_Stack[0].m_nNumber[3] = -1; + BIGMessages[style].m_Stack[0].m_nNumber[4] = -1; + BIGMessages[style].m_Stack[0].m_nNumber[5] = -1; + BIGMessages[style].m_Stack[0].m_pString = nil; +} + +void +CMessages::AddBigMessageQ(wchar *msg, uint32 time, uint16 style) +{ + wchar outstr[512]; // unused + WideStringCopy(outstr, msg, 256); + InsertPlayerControlKeysInString(outstr); + GetWideStringLength(outstr); + + int32 i = 0; + while (i < 4 && BIGMessages[style].m_Stack[i].m_pText != nil) + i++; + + if (i >= 4) return; + + BIGMessages[style].m_Stack[i].m_pText = msg; + BIGMessages[style].m_Stack[i].m_nFlag = 0; + BIGMessages[style].m_Stack[i].m_nTime = time; + BIGMessages[style].m_Stack[i].m_nStartTime = CTimer::GetTimeInMilliseconds(); + BIGMessages[style].m_Stack[i].m_nNumber[0] = -1; + BIGMessages[style].m_Stack[i].m_nNumber[1] = -1; + BIGMessages[style].m_Stack[i].m_nNumber[2] = -1; + BIGMessages[style].m_Stack[i].m_nNumber[3] = -1; + BIGMessages[style].m_Stack[i].m_nNumber[4] = -1; + BIGMessages[style].m_Stack[i].m_nNumber[5] = -1; + BIGMessages[style].m_Stack[i].m_pString = nil; +} + +void +CMessages::AddToPreviousBriefArray(wchar *text, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6, wchar *string) +{ + int32 i; + for (i = 0; i < NUMPREVIOUSBRIEFS && PreviousBriefs[i].m_pText != nil; i++) { + if (PreviousBriefs[i].m_nNumber[0] == n1 + && PreviousBriefs[i].m_nNumber[1] == n2 + && PreviousBriefs[i].m_nNumber[2] == n3 + && PreviousBriefs[i].m_nNumber[3] == n4 + && PreviousBriefs[i].m_nNumber[4] == n5 + && PreviousBriefs[i].m_nNumber[5] == n6 + && PreviousBriefs[i].m_pText == text + && PreviousBriefs[i].m_pString == string) + return; + } + + if (i != 0) { + if (i == NUMPREVIOUSBRIEFS) i -= 2; + else i--; + + while (i >= 0) { + PreviousBriefs[i + 1] = PreviousBriefs[i]; + i--; + } + } + PreviousBriefs[0].m_pText = text; + PreviousBriefs[0].m_nNumber[0] = n1; + PreviousBriefs[0].m_nNumber[1] = n2; + PreviousBriefs[0].m_nNumber[2] = n3; + PreviousBriefs[0].m_nNumber[3] = n4; + PreviousBriefs[0].m_nNumber[4] = n5; + PreviousBriefs[0].m_nNumber[5] = n6; + PreviousBriefs[0].m_pString = string; +} + +void +CMessages::InsertNumberInString(wchar *str, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6, wchar *outstr) +{ + char numStr[10]; + wchar wNumStr[10]; + + if (str == nil) { + *outstr = '\0'; + return; + } + + sprintf(numStr, "%d", n1); + size_t outLen = strlen(numStr); + AsciiToUnicode(numStr, wNumStr); + if (str[0] == 0) { + *outstr = '\0'; + return; + } + + int32 size = GetWideStringLength(str); + + int32 i = 0; + + for (int32 c = 0; c < size;) { +#ifdef MORE_LANGUAGES + if ((CFont::IsJapanese() && str[c] == (0x8000 | '~') && str[c + 1] == (0x8000 | '1') && str[c + 2] == (0x8000 | '~')) || + (!CFont::IsJapanese() && str[c] == '~' && str[c + 1] == '1' && str[c + 2] == '~')) { +#else + if (str[c] == '~' && str[c + 1] == '1' && str[c + 2] == '~') { +#endif + c += 3; + for (int j = 0; j < outLen; ) + *(outstr++) = wNumStr[j++]; + + i++; + switch (i) { + case 1: sprintf(numStr, "%d", n2); break; + case 2: sprintf(numStr, "%d", n3); break; + case 3: sprintf(numStr, "%d", n4); break; + case 4: sprintf(numStr, "%d", n5); break; + case 5: sprintf(numStr, "%d", n6); break; + } + outLen = strlen(numStr); + AsciiToUnicode(numStr, wNumStr); + } else { + *(outstr++) = str[c++]; + } + } + *outstr = '\0'; +} + +void +CMessages::InsertStringInString(wchar *str1, wchar *str2) +{ + wchar tempstr[256]; + + if (!str1 || !str2) return; + + int32 str1_size = GetWideStringLength(str1); + int32 str2_size = GetWideStringLength(str2); + int32 total_size = str1_size + str2_size; + + wchar *_str1 = str1; + uint16 i; + for (i = 0; i < total_size; ) { +#ifdef MORE_LANGUAGES + if ((CFont::IsJapanese() && *_str1 == (0x8000 | '~') && *(_str1 + 1) == (0x8000 | 'a') && *(_str1 + 2) == (0x8000 | '~')) + || (*_str1 == '~' && *(_str1 + 1) == 'a' && *(_str1 + 2) == '~')) { +#else + if (*_str1 == '~' && *(_str1 + 1) == 'a' && *(_str1 + 2) == '~') { +#endif + _str1 += 3; + for (int j = 0; j < str2_size; j++) { + tempstr[i++] = str2[j]; + } + } else { + tempstr[i++] = *(_str1++); + } + } + tempstr[i] = '\0'; + + for (i = 0; i < total_size; i++) + str1[i] = tempstr[i]; + + while (i < 256) + str1[i++] = '\0'; +} + +void +CMessages::InsertPlayerControlKeysInString(wchar *str) +{ + uint16 i; + wchar outstr[256]; + wchar keybuf[256]; + + if (!str) return; + uint16 strSize = GetWideStringLength(str); + memset(keybuf, 0, 256*sizeof(wchar)); + + wchar *_outstr = outstr; + for (i = 0; i < strSize;) { +#ifdef MORE_LANGUAGES + if ((CFont::IsJapanese() && str[i] == (0x8000 | '~') && str[i + 1] == (0x8000 | 'k') && str[i + 2] == (0x8000 | '~')) || + (!CFont::IsJapanese() && str[i] == '~' && str[i + 1] == 'k' && str[i + 2] == '~')) { +#else + if (str[i] == '~' && str[i + 1] == 'k' && str[i + 2] == '~') { +#endif + i += 4; + bool done = false; + for (int32 cont = 0; cont < MAX_CONTROLLERACTIONS && !done; cont++) { + uint16 contSize = GetWideStringLength(ControlsManager.m_aActionNames[cont]); + if (contSize != 0) { + if (WideStringCompare(&str[i], ControlsManager.m_aActionNames[cont], contSize)) { + done = true; + ControlsManager.GetWideStringOfCommandKeys(cont, keybuf, 256); + uint16 keybuf_size = GetWideStringLength(keybuf); + for (uint16 j = 0; j < keybuf_size; j++) { + *(_outstr++) = keybuf[j]; + keybuf[j] = '\0'; + } + i += contSize + 1; + } + } + } + } else { + *(_outstr++) = str[i++]; + } + } + *_outstr = '\0'; + + for (i = 0; i < GetWideStringLength(outstr); i++) + str[i] = outstr[i]; + + while (i < 256) + str[i++] = '\0'; +} + +void +CMessages::AddMessageWithNumber(wchar *str, uint32 time, uint16 flag, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6) +{ + wchar outstr[512]; // unused + InsertNumberInString(str, n1, n2, n3, n4, n5, n6, outstr); + InsertPlayerControlKeysInString(outstr); + GetWideStringLength(outstr); + + uint16 i = 0; + while (i < NUMBRIEFMESSAGES && BriefMessages[i].m_pText != nil) + i++; + + if (i >= NUMBRIEFMESSAGES) return; + + BriefMessages[i].m_pText = str; + BriefMessages[i].m_nFlag = flag; + BriefMessages[i].m_nTime = time; + BriefMessages[i].m_nStartTime = CTimer::GetTimeInMilliseconds(); + BriefMessages[i].m_nNumber[0] = n1; + BriefMessages[i].m_nNumber[1] = n2; + BriefMessages[i].m_nNumber[2] = n3; + BriefMessages[i].m_nNumber[3] = n4; + BriefMessages[i].m_nNumber[4] = n5; + BriefMessages[i].m_nNumber[5] = n6; + BriefMessages[i].m_pString = nil; + if (i == 0) + AddToPreviousBriefArray( + BriefMessages[0].m_pText, + BriefMessages[0].m_nNumber[0], + BriefMessages[0].m_nNumber[1], + BriefMessages[0].m_nNumber[2], + BriefMessages[0].m_nNumber[3], + BriefMessages[0].m_nNumber[4], + BriefMessages[0].m_nNumber[5], + BriefMessages[0].m_pString); +} + +void +CMessages::AddMessageJumpQWithNumber(wchar *str, uint32 time, uint16 flag, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6) +{ + wchar outstr[512]; // unused + InsertNumberInString(str, n1, n2, n3, n4, n5, n6, outstr); + InsertPlayerControlKeysInString(outstr); + GetWideStringLength(outstr); + + BriefMessages[0].m_pText = str; + BriefMessages[0].m_nFlag = flag; + BriefMessages[0].m_nTime = time; + BriefMessages[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); + BriefMessages[0].m_nNumber[0] = n1; + BriefMessages[0].m_nNumber[1] = n2; + BriefMessages[0].m_nNumber[2] = n3; + BriefMessages[0].m_nNumber[3] = n4; + BriefMessages[0].m_nNumber[4] = n5; + BriefMessages[0].m_nNumber[5] = n6; + BriefMessages[0].m_pString = nil; + AddToPreviousBriefArray(str, n1, n2, n3, n4, n5, n6, nil); +} + +void +CMessages::AddMessageSoonWithNumber(wchar *str, uint32 time, uint16 flag, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6) +{ + wchar outstr[512]; // unused + InsertNumberInString(str, n1, n2, n3, n4, n5, n6, outstr); + InsertPlayerControlKeysInString(outstr); + GetWideStringLength(outstr); + + if (BriefMessages[0].m_pText != nil) { + for (int32 i = NUMBRIEFMESSAGES-1; i > 1; i--) + BriefMessages[i] = BriefMessages[i-1]; + + BriefMessages[1].m_pText = str; + BriefMessages[1].m_nFlag = flag; + BriefMessages[1].m_nTime = time; + BriefMessages[1].m_nStartTime = CTimer::GetTimeInMilliseconds(); + BriefMessages[1].m_nNumber[0] = n1; + BriefMessages[1].m_nNumber[1] = n2; + BriefMessages[1].m_nNumber[2] = n3; + BriefMessages[1].m_nNumber[3] = n4; + BriefMessages[1].m_nNumber[4] = n5; + BriefMessages[1].m_nNumber[5] = n6; + BriefMessages[1].m_pString = nil; + } else { + BriefMessages[0].m_pText = str; + BriefMessages[0].m_nFlag = flag; + BriefMessages[0].m_nTime = time; + BriefMessages[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); + BriefMessages[0].m_nNumber[0] = n1; + BriefMessages[0].m_nNumber[1] = n2; + BriefMessages[0].m_nNumber[2] = n3; + BriefMessages[0].m_nNumber[3] = n4; + BriefMessages[0].m_nNumber[4] = n5; + BriefMessages[0].m_nNumber[5] = n6; + BriefMessages[0].m_pString = nil; + AddToPreviousBriefArray(str, n1, n2, n3, n4, n5, n6, nil); + } +} + +void +CMessages::AddBigMessageWithNumber(wchar *str, uint32 time, uint16 style, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6) +{ + wchar outstr[512]; // unused + InsertNumberInString(str, n1, n2, n3, n4, n5, n6, outstr); + InsertPlayerControlKeysInString(outstr); + GetWideStringLength(outstr); + + BIGMessages[style].m_Stack[0].m_pText = str; + BIGMessages[style].m_Stack[0].m_nFlag = 0; + BIGMessages[style].m_Stack[0].m_nTime = time; + BIGMessages[style].m_Stack[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); + BIGMessages[style].m_Stack[0].m_nNumber[0] = n1; + BIGMessages[style].m_Stack[0].m_nNumber[1] = n2; + BIGMessages[style].m_Stack[0].m_nNumber[2] = n3; + BIGMessages[style].m_Stack[0].m_nNumber[3] = n4; + BIGMessages[style].m_Stack[0].m_nNumber[4] = n5; + BIGMessages[style].m_Stack[0].m_nNumber[5] = n6; + BIGMessages[style].m_Stack[0].m_pString = nil; +} + +void +CMessages::AddBigMessageWithNumberQ(wchar *str, uint32 time, uint16 style, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6) +{ + wchar outstr[512]; // unused + InsertNumberInString(str, n1, n2, n3, n4, n5, n6, outstr); + InsertPlayerControlKeysInString(outstr); + GetWideStringLength(outstr); + + int32 i = 0; + + while (i < 4 && BIGMessages[style].m_Stack[i].m_pText != nil) + i++; + + if (i >= 4) return; + + BIGMessages[style].m_Stack[i].m_pText = str; + BIGMessages[style].m_Stack[i].m_nFlag = 0; + BIGMessages[style].m_Stack[i].m_nTime = time; + BIGMessages[style].m_Stack[i].m_nStartTime = CTimer::GetTimeInMilliseconds(); + BIGMessages[style].m_Stack[i].m_nNumber[0] = n1; + BIGMessages[style].m_Stack[i].m_nNumber[1] = n2; + BIGMessages[style].m_Stack[i].m_nNumber[2] = n3; + BIGMessages[style].m_Stack[i].m_nNumber[3] = n4; + BIGMessages[style].m_Stack[i].m_nNumber[4] = n5; + BIGMessages[style].m_Stack[i].m_nNumber[5] = n6; + BIGMessages[style].m_Stack[i].m_pString = nil; +} + +void +CMessages::AddMessageWithString(wchar *text, uint32 time, uint16 flag, wchar *str) +{ + wchar outstr[512]; // unused + WideStringCopy(outstr, text, 256); + InsertStringInString(outstr, str); + InsertPlayerControlKeysInString(outstr); + GetWideStringLength(outstr); + + int32 i = 0; + while (i < NUMBRIEFMESSAGES && BriefMessages[i].m_pText != nil) + i++; + + if (i >= NUMBRIEFMESSAGES) return; + + BriefMessages[i].m_pText = text; + BriefMessages[i].m_nFlag = flag; + BriefMessages[i].m_nTime = time; + BriefMessages[i].m_nStartTime = CTimer::GetTimeInMilliseconds(); + BriefMessages[i].m_nNumber[0] = -1; + BriefMessages[i].m_nNumber[1] = -1; + BriefMessages[i].m_nNumber[2] = -1; + BriefMessages[i].m_nNumber[3] = -1; + BriefMessages[i].m_nNumber[4] = -1; + BriefMessages[i].m_nNumber[5] = -1; + BriefMessages[i].m_pString = str; + if (i == 0) + AddToPreviousBriefArray( + BriefMessages[0].m_pText, + BriefMessages[0].m_nNumber[0], + BriefMessages[0].m_nNumber[1], + BriefMessages[0].m_nNumber[2], + BriefMessages[0].m_nNumber[3], + BriefMessages[0].m_nNumber[4], + BriefMessages[0].m_nNumber[5], + BriefMessages[0].m_pString); +} + +void +CMessages::AddMessageJumpQWithString(wchar *text, uint32 time, uint16 flag, wchar *str) +{ + wchar outstr[512]; // unused + WideStringCopy(outstr, text, 256); + InsertStringInString(outstr, str); + InsertPlayerControlKeysInString(outstr); + GetWideStringLength(outstr); + + BriefMessages[0].m_pText = text; + BriefMessages[0].m_nFlag = flag; + BriefMessages[0].m_nTime = time; + BriefMessages[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); + BriefMessages[0].m_nNumber[0] = -1; + BriefMessages[0].m_nNumber[1] = -1; + BriefMessages[0].m_nNumber[2] = -1; + BriefMessages[0].m_nNumber[3] = -1; + BriefMessages[0].m_nNumber[4] = -1; + BriefMessages[0].m_nNumber[5] = -1; + BriefMessages[0].m_pString = str; + AddToPreviousBriefArray(text, -1, -1, -1, -1, -1, -1, str); +} + +inline bool +FastWideStringComparison(wchar *str1, wchar *str2) +{ + while (*str1 == *str2) { + ++str1; + ++str2; + if (!*str1 && !*str2) return true; + } + return false; +} + +void +CMessages::ClearThisPrint(wchar *str) +{ + bool equal; + + do { + equal = false; + uint16 i; + for (i = 0; i < NUMBRIEFMESSAGES && BriefMessages[i].m_pText != nil; i++) { + equal = FastWideStringComparison(str, BriefMessages[i].m_pText); + + if (equal) break; + } + + if (equal) { + if (i != 0) { + BriefMessages[i].m_pText = nil; + for (; i < NUMBRIEFMESSAGES-1 && BriefMessages[i+1].m_pText != nil; i++) { + BriefMessages[i] = BriefMessages[i + 1]; + } + BriefMessages[i].m_pText = nil; + } else { + BriefMessages[0].m_pText = nil; + for (; i < NUMBRIEFMESSAGES-1 && BriefMessages[i+1].m_pText != nil; i++) { + BriefMessages[i] = BriefMessages[i + 1]; + } + BriefMessages[i].m_pText = nil; + BriefMessages[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); + if (BriefMessages[0].m_pText != nil) + AddToPreviousBriefArray( + BriefMessages[0].m_pText, + BriefMessages[0].m_nNumber[0], + BriefMessages[0].m_nNumber[1], + BriefMessages[0].m_nNumber[2], + BriefMessages[0].m_nNumber[3], + BriefMessages[0].m_nNumber[4], + BriefMessages[0].m_nNumber[5], + BriefMessages[0].m_pString); + } + } + } while (equal); +} + +void +CMessages::ClearThisBigPrint(wchar *str) +{ + bool equal; + + do { + uint16 i = 0; + equal = false; + uint16 style = 0; + while (style < NUMBIGMESSAGES) + { + if (i >= 4) + break; + + if (CMessages::BIGMessages[style].m_Stack[i].m_pText == nil || equal) + break; + + equal = FastWideStringComparison(str, BIGMessages[style].m_Stack[i].m_pText); + + if (!equal && ++i == 4) { + i = 0; + style++; + } + } + if (equal) { + if (i != 0) { + BIGMessages[style].m_Stack[i].m_pText = nil; + while (i < 3) { + if (BIGMessages[style].m_Stack[i + 1].m_pText == nil) + break; + BIGMessages[style].m_Stack[i] = BIGMessages[style].m_Stack[i + 1]; + i++; + } + BIGMessages[style].m_Stack[i].m_pText = nil; + } else { + BIGMessages[style].m_Stack[0].m_pText = nil; + i = 0; + while (i < 3) { + if (BIGMessages[style].m_Stack[i + 1].m_pText == nil) + break; + BIGMessages[style].m_Stack[i] = BIGMessages[style].m_Stack[i + 1]; + i++; + } + BIGMessages[style].m_Stack[i].m_pText = nil; + BIGMessages[style].m_Stack[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); + } + } + } while (equal); +} + +void +CMessages::ClearAllMessagesDisplayedByGame() +{ + ClearMessages(); + for (int32 i = 0; i < NUMPREVIOUSBRIEFS; i++) { + PreviousBriefs[i].m_pText = nil; + PreviousBriefs[i].m_pString = nil; + } + CHud::GetRidOfAllHudMessages(); + CUserDisplay::Pager.ClearMessages(); +} diff --git a/src/miami/text/Messages.h b/src/miami/text/Messages.h new file mode 100644 index 00000000..e8ba1bf7 --- /dev/null +++ b/src/miami/text/Messages.h @@ -0,0 +1,69 @@ +#pragma once + +struct tMessage +{ + wchar *m_pText; + uint16 m_nFlag; + uint32 m_nTime; + uint32 m_nStartTime; + int32 m_nNumber[6]; + wchar *m_pString; +}; + +struct tBigMessage +{ + tMessage m_Stack[4]; +}; + +struct tPreviousBrief +{ + wchar *m_pText; + int32 m_nNumber[6]; + wchar *m_pString; +}; + +#define NUMBRIEFMESSAGES 8 +#define NUMBIGMESSAGES 6 +#define NUMPREVIOUSBRIEFS 5 + +class CMessages +{ +public: + static tMessage BriefMessages[NUMBRIEFMESSAGES]; + static tBigMessage BIGMessages[NUMBIGMESSAGES]; + static tPreviousBrief PreviousBriefs[NUMPREVIOUSBRIEFS]; + static char PreviousMissionTitle[16]; // unused +public: + static void Init(void); + static uint16 GetWideStringLength(wchar *src); + static void WideStringCopy(wchar *dst, wchar *src, uint16 size); + static bool WideStringCompare(wchar *str1, wchar *str2, uint16 size); + static void Process(void); + static void Display(void); + static void AddMessage(wchar *key, uint32 time, uint16 pos); + static void AddMessageJumpQ(wchar *key, uint32 time, uint16 pos); + static void AddMessageSoon(wchar *key, uint32 time, uint16 pos); + static void ClearMessages(void); + static void ClearSmallMessagesOnly(void); + static void AddBigMessage(wchar *key, uint32 time, uint16 pos); + static void AddBigMessageQ(wchar *key, uint32 time, uint16 pos); + static void AddToPreviousBriefArray(wchar *text, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6, wchar *string); + static void InsertNumberInString(wchar *src, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6, wchar *dst); + static void InsertStringInString(wchar *str1, wchar *str2); + static void InsertPlayerControlKeysInString(wchar *src); + static void AddMessageWithNumber(wchar *key, uint32 time, uint16 pos, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6); + static void AddMessageJumpQWithNumber(wchar *key, uint32 time, uint16 pos, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6); + static void AddMessageSoonWithNumber(wchar *key, uint32 time, uint16 pos, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6); + static void AddBigMessageWithNumber(wchar *key, uint32 time, uint16 pos, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6); + static void AddBigMessageWithNumberQ(wchar *key, uint32 time, uint16 pos, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6); + static void AddMessageWithString(wchar *text, uint32 time, uint16 flag, wchar *str); + static void AddMessageJumpQWithString(wchar *text, uint32 time, uint16 flag, wchar *str); + static void ClearThisPrint(wchar *str); + static void ClearThisBigPrint(wchar *str); + static void ClearAllMessagesDisplayedByGame(void); + + // unused or cut + //static void AddMessageSoonWithString(wchar*, uint32, uint16, wchar*); + //static void CutString(int16, char*, char**); + //static void PrintString(char*, int16, int16, int16); +}; diff --git a/src/miami/text/Pager.cpp b/src/miami/text/Pager.cpp new file mode 100644 index 00000000..609c6860 --- /dev/null +++ b/src/miami/text/Pager.cpp @@ -0,0 +1,184 @@ +#include "common.h" + +#include "Pager.h" +#include "Timer.h" +#include "Messages.h" +#include "Hud.h" +#include "Camera.h" + +void +CPager::Init() +{ + ClearMessages(); + m_nNumDisplayLetters = 8; +} + +void +CPager::Process() +{ + if (m_messages[0].m_pText != nil && m_messages[0].m_nCurrentPosition >= (int32)m_messages[0].m_nStringLength) { + m_messages[0].m_pText = nil; + uint16 i = 0; + while (i < NUMPAGERMESSAGES-1) { + if (m_messages[i + 1].m_pText == nil) break; + m_messages[i] = m_messages[i + 1]; + i++; + } + m_messages[i].m_pText = nil; + if (m_messages[0].m_pText != nil) + CMessages::AddToPreviousBriefArray( + m_messages[0].m_pText, + m_messages[0].m_nNumber[0], + m_messages[0].m_nNumber[1], + m_messages[0].m_nNumber[2], + m_messages[0].m_nNumber[3], + m_messages[0].m_nNumber[4], + m_messages[0].m_nNumber[5], + 0); + } + Display(); + if (m_messages[0].m_pText != nil) { + if (TheCamera.m_WideScreenOn || !CHud::m_Wants_To_Draw_Hud || CHud::m_BigMessage[0][0] || CHud::m_BigMessage[2][0]) { + RestartCurrentMessage(); + } else { + if (CTimer::GetTimeInMilliseconds() > m_messages[0].m_nTimeToChangePosition) { + m_messages[0].m_nCurrentPosition++; + m_messages[0].m_nTimeToChangePosition = CTimer::GetTimeInMilliseconds() + m_messages[0].m_nSpeedMs; + } + } + } +} + +void +CPager::Display() +{ + wchar outstr1[256]; + wchar outstr2[260]; + + wchar *pText = m_messages[0].m_pText; + uint16 i = 0; + if (pText != nil) { + CMessages::InsertNumberInString( + pText, + m_messages[0].m_nNumber[0], + m_messages[0].m_nNumber[1], + m_messages[0].m_nNumber[2], + m_messages[0].m_nNumber[3], + m_messages[0].m_nNumber[4], + m_messages[0].m_nNumber[5], + outstr1); + for (; i < m_nNumDisplayLetters; i++) { + int pos = m_messages[0].m_nCurrentPosition + i; + if (pos >= 0) { + if (!outstr1[pos]) break; + + outstr2[i] = outstr1[pos]; + } else { + outstr2[i] = ' '; + } + } + } + outstr2[i] = '\0'; + CHud::SetPagerMessage(outstr2); +} + +void +CPager::AddMessage(wchar *str, uint16 speed, uint16 priority, uint16 a5) +{ + uint16 size = CMessages::GetWideStringLength(str); + for (int32 i = 0; i < NUMPAGERMESSAGES; i++) { + if (m_messages[i].m_pText) { + if (m_messages[i].m_nPriority >= priority) + continue; + + for (int j = NUMPAGERMESSAGES-1; j > i; j--) + m_messages[j] = m_messages[j-1]; + + } + m_messages[i].m_pText = str; + m_messages[i].m_nSpeedMs = speed; + m_messages[i].m_nPriority = priority; + m_messages[i].unused = a5; + m_messages[i].m_nCurrentPosition = -(m_nNumDisplayLetters + 10); + m_messages[i].m_nTimeToChangePosition = CTimer::GetTimeInMilliseconds() + speed; + m_messages[i].m_nStringLength = size; + m_messages[i].m_nNumber[0] = -1; + m_messages[i].m_nNumber[1] = -1; + m_messages[i].m_nNumber[2] = -1; + m_messages[i].m_nNumber[3] = -1; + m_messages[i].m_nNumber[4] = -1; + m_messages[i].m_nNumber[5] = -1; + + if (i == 0) + CMessages::AddToPreviousBriefArray( + m_messages[0].m_pText, + m_messages[0].m_nNumber[0], + m_messages[0].m_nNumber[1], + m_messages[0].m_nNumber[2], + m_messages[0].m_nNumber[3], + m_messages[0].m_nNumber[4], + m_messages[0].m_nNumber[5], + nil); + return; + } +} + +void +CPager::AddMessageWithNumber(wchar *str, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6, uint16 speed, uint16 priority, uint16 a11) +{ + wchar nstr[520]; + + CMessages::InsertNumberInString(str, n1, n2, n3, n4, n5, n6, nstr); + uint16 size = CMessages::GetWideStringLength(nstr); + for (int32 i = 0; i < NUMPAGERMESSAGES; i++) { + if (m_messages[i].m_pText) { + if (m_messages[i].m_nPriority >= priority) + continue; + + for (int j = NUMPAGERMESSAGES-1; j > i; j--) + m_messages[j] = m_messages[j - 1]; + + } + m_messages[i].m_pText = str; + m_messages[i].m_nSpeedMs = speed; + m_messages[i].m_nPriority = priority; + m_messages[i].unused = a11; + m_messages[i].m_nCurrentPosition = -(m_nNumDisplayLetters + 10); + m_messages[i].m_nTimeToChangePosition = CTimer::GetTimeInMilliseconds() + speed; + m_messages[i].m_nStringLength = size; + m_messages[i].m_nNumber[0] = n1; + m_messages[i].m_nNumber[1] = n2; + m_messages[i].m_nNumber[2] = n3; + m_messages[i].m_nNumber[3] = n4; + m_messages[i].m_nNumber[4] = n5; + m_messages[i].m_nNumber[5] = n6; + + if (i == 0) + CMessages::AddToPreviousBriefArray( + m_messages[0].m_pText, + m_messages[0].m_nNumber[0], + m_messages[0].m_nNumber[1], + m_messages[0].m_nNumber[2], + m_messages[0].m_nNumber[3], + m_messages[0].m_nNumber[4], + m_messages[0].m_nNumber[5], + nil); + return; + } +} + +void +CPager::ClearMessages() +{ + for (int32 i = 0; i < NUMPAGERMESSAGES; i++) + m_messages[i].m_pText = nil; +} + +void +CPager::RestartCurrentMessage() +{ + if (m_messages[0].m_pText != nil) { + m_messages[0].m_nCurrentPosition = -(m_nNumDisplayLetters + 10); + m_messages[0].m_nTimeToChangePosition = CTimer::GetTimeInMilliseconds() + m_messages[0].m_nSpeedMs; + } +} \ No newline at end of file diff --git a/src/miami/text/Pager.h b/src/miami/text/Pager.h new file mode 100644 index 00000000..16307971 --- /dev/null +++ b/src/miami/text/Pager.h @@ -0,0 +1,28 @@ +#pragma once + +struct PagerMessage { + wchar *m_pText; + uint16 m_nSpeedMs; + int16 m_nCurrentPosition; + uint16 m_nStringLength; + uint16 m_nPriority; + uint32 m_nTimeToChangePosition; + int16 unused; // but still set in SCM. importance? ringtone? + int32 m_nNumber[6]; +}; + +#define NUMPAGERMESSAGES 8 + +class CPager +{ + int16 m_nNumDisplayLetters; + PagerMessage m_messages[NUMPAGERMESSAGES]; +public: + void Init(); + void Process(); + void Display(); + void AddMessage(wchar*, uint16, uint16, uint16); + void AddMessageWithNumber(wchar *str, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6, uint16 speed, uint16 priority, uint16 a11); + void ClearMessages(); + void RestartCurrentMessage(); +}; \ No newline at end of file diff --git a/src/miami/text/Text.cpp b/src/miami/text/Text.cpp new file mode 100644 index 00000000..1369d1db --- /dev/null +++ b/src/miami/text/Text.cpp @@ -0,0 +1,530 @@ +#include "common.h" + +#include "FileMgr.h" +#ifdef MORE_LANGUAGES +#include "Game.h" +#endif +#include "Frontend.h" +#include "Messages.h" +#include "Text.h" +#include "Timer.h" + +wchar WideErrorString[25]; + +CText TheText; + +CText::CText(void) +{ + encoding = 'e'; + bHasMissionTextOffsets = false; + bIsMissionTextLoaded = false; + memset(szMissionTableName, 0, sizeof(szMissionTableName)); + memset(WideErrorString, 0, sizeof(WideErrorString)); +} + +void +CText::Load(void) +{ + char filename[32]; + size_t offset; + int file; + bool tkey_loaded = false, tdat_loaded = false; + ChunkHeader m_ChunkHeader; + + bIsMissionTextLoaded = false; + bHasMissionTextOffsets = false; + + Unload(); + + CFileMgr::SetDir("TEXT"); + switch(FrontEndMenuManager.m_PrefsLanguage){ + case CMenuManager::LANGUAGE_AMERICAN: + sprintf(filename, "AMERICAN.GXT"); + break; + case CMenuManager::LANGUAGE_FRENCH: + sprintf(filename, "FRENCH.GXT"); + break; + case CMenuManager::LANGUAGE_GERMAN: + sprintf(filename, "GERMAN.GXT"); + break; + case CMenuManager::LANGUAGE_ITALIAN: + sprintf(filename, "ITALIAN.GXT"); + break; + case CMenuManager::LANGUAGE_SPANISH: + sprintf(filename, "SPANISH.GXT"); + break; +#ifdef MORE_LANGUAGES + case CMenuManager::LANGUAGE_POLISH: + sprintf(filename, "POLISH.GXT"); + break; + case CMenuManager::LANGUAGE_RUSSIAN: + sprintf(filename, "RUSSIAN.GXT"); + break; + case CMenuManager::LANGUAGE_JAPANESE: + sprintf(filename, "JAPANESE.GXT"); + break; +#endif + } + + file = CFileMgr::OpenFile(filename, "rb"); + + offset = 0; + while (!tkey_loaded || !tdat_loaded) { + ReadChunkHeader(&m_ChunkHeader, file, &offset); + if (m_ChunkHeader.size != 0) { + if (strncmp(m_ChunkHeader.magic, "TABL", 4) == 0) { + MissionTextOffsets.Load(m_ChunkHeader.size, file, &offset, 0x58000); + bHasMissionTextOffsets = true; + } else if (strncmp(m_ChunkHeader.magic, "TKEY", 4) == 0) { + this->keyArray.Load(m_ChunkHeader.size, file, &offset); + tkey_loaded = true; + } else if (strncmp(m_ChunkHeader.magic, "TDAT", 4) == 0) { + this->data.Load(m_ChunkHeader.size, file, &offset); + tdat_loaded = true; + } else { + CFileMgr::Seek(file, m_ChunkHeader.size, SEEK_CUR); + offset += m_ChunkHeader.size; + } + } + } + + keyArray.Update(data.chars); + CFileMgr::CloseFile(file); + CFileMgr::SetDir(""); +} + +void +CText::Unload(void) +{ + CMessages::ClearAllMessagesDisplayedByGame(); + keyArray.Unload(); + data.Unload(); + mission_keyArray.Unload(); + mission_data.Unload(); + bIsMissionTextLoaded = false; + memset(szMissionTableName, 0, sizeof(szMissionTableName)); +} + +wchar* +CText::Get(const char *key) +{ + uint8 result = false; +#if defined (FIX_BUGS) || defined(FIX_BUGS_64) + wchar *outstr = keyArray.Search(key, data.chars, &result); +#else + wchar *outstr = keyArray.Search(key, &result); +#endif + + if (!result && bHasMissionTextOffsets && bIsMissionTextLoaded) +#if defined (FIX_BUGS) || defined(FIX_BUGS_64) + outstr = mission_keyArray.Search(key, mission_data.chars, &result); +#else + outstr = mission_keyArray.Search(key, &result); +#endif + return outstr; +} + +wchar UpperCaseTable[128] = { + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, + 149, 173, 173, 175, 176, 177, 178, 179, 180, 181, 182, + 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, + 249, 250, 251, 252, 253, 254, 255 +}; + +wchar FrenchUpperCaseTable[128] = { + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 65, 65, 65, 65, 132, 133, 69, 69, 69, 69, 73, 73, + 73, 73, 79, 79, 79, 79, 85, 85, 85, 85, 173, 173, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, + 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, + 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, + 253, 254, 255 +}; + +wchar +CText::GetUpperCase(wchar c) +{ + switch (encoding) + { + case 'e': + if (c >= 'a' && c <= 'z') + return c - 32; + break; + case 'f': + if (c >= 'a' && c <= 'z') + return c - 32; + + if (c >= 128 && c <= 255) + return FrenchUpperCaseTable[c-128]; + break; + case 'g': + case 'i': + case 's': + if (c >= 'a' && c <= 'z') + return c - 32; + + if (c >= 128 && c <= 255) + return UpperCaseTable[c-128]; + break; + default: + break; + } + return c; +} + +void +CText::UpperCase(wchar *s) +{ + while(*s){ + *s = GetUpperCase(*s); + s++; + } +} + +void +CText::GetNameOfLoadedMissionText(char *outName) +{ + strcpy(outName, szMissionTableName); +} + +void +CText::ReadChunkHeader(ChunkHeader *buf, int32 file, size_t *offset) +{ +#ifdef THIS_IS_STUPID + char *_buf = (char*)buf; + for (int i = 0; i < sizeof(ChunkHeader); i++) { + CFileMgr::Read(file, &_buf[i], 1); + (*offset)++; + } +#else + // original code loops 8 times to read 1 byte with CFileMgr::Read, that's retarded + CFileMgr::Read(file, (char*)buf, sizeof(ChunkHeader)); + *offset += sizeof(ChunkHeader); +#endif +} + +void +CText::LoadMissionText(char *MissionTableName) +{ + char filename[32]; + CMessages::ClearAllMessagesDisplayedByGame(); + + mission_keyArray.Unload(); + mission_data.Unload(); + + bool search_result = false; + int missionTableId = 0; + + for (missionTableId = 0; missionTableId < MissionTextOffsets.size; missionTableId++) { + if (strncmp(MissionTextOffsets.data[missionTableId].szMissionName, MissionTableName, strlen(MissionTextOffsets.data[missionTableId].szMissionName)) == 0) { + search_result = true; + break; + } + } + + if (!search_result) { + printf("CText::LoadMissionText - couldn't find %s", MissionTableName); + return; + } + + CFileMgr::SetDir("TEXT"); + switch (FrontEndMenuManager.m_PrefsLanguage) { + case CMenuManager::LANGUAGE_AMERICAN: + sprintf(filename, "AMERICAN.GXT"); + break; + case CMenuManager::LANGUAGE_FRENCH: + sprintf(filename, "FRENCH.GXT"); + break; + case CMenuManager::LANGUAGE_GERMAN: + sprintf(filename, "GERMAN.GXT"); + break; + case CMenuManager::LANGUAGE_ITALIAN: + sprintf(filename, "ITALIAN.GXT"); + break; + case CMenuManager::LANGUAGE_SPANISH: + sprintf(filename, "SPANISH.GXT"); + break; +#ifdef MORE_LANGUAGES + case CMenuManager::LANGUAGE_POLISH: + sprintf(filename, "POLISH.GXT"); + break; + case CMenuManager::LANGUAGE_RUSSIAN: + sprintf(filename, "RUSSIAN.GXT"); + break; + case CMenuManager::LANGUAGE_JAPANESE: + sprintf(filename, "JAPANESE.GXT"); + break; +#endif + } + CTimer::Suspend(); + int file = CFileMgr::OpenFile(filename, "rb"); + CFileMgr::Seek(file, MissionTextOffsets.data[missionTableId].offset, SEEK_SET); + + char TableCheck[8]; + CFileMgr::Read(file, TableCheck, 8); + if (strncmp(TableCheck, MissionTableName, 8) != 0) + printf("CText::LoadMissionText - expected to find %s in the text file", MissionTableName); + + bool tkey_loaded = false, tdat_loaded = false; + ChunkHeader m_ChunkHeader; + while (!tkey_loaded || !tdat_loaded) { + size_t bytes_read = 0; + ReadChunkHeader(&m_ChunkHeader, file, &bytes_read); + if (m_ChunkHeader.size != 0) { + if (strncmp(m_ChunkHeader.magic, "TKEY", 4) == 0) { + size_t bytes_read = 0; + mission_keyArray.Load(m_ChunkHeader.size, file, &bytes_read); + tkey_loaded = true; + } else if (strncmp(m_ChunkHeader.magic, "TDAT", 4) == 0) { + size_t bytes_read = 0; + mission_data.Load(m_ChunkHeader.size, file, &bytes_read); + tdat_loaded = true; + } else + CFileMgr::Seek(file, m_ChunkHeader.size, SEEK_CUR); + } + } + + mission_keyArray.Update(mission_data.chars); + CFileMgr::CloseFile(file); + CTimer::Resume(); + CFileMgr::SetDir(""); + strcpy(szMissionTableName, MissionTableName); + bIsMissionTextLoaded = true; +} + + +void +CKeyArray::Load(size_t length, int file, size_t* offset) +{ + char *rawbytes; + + // You can make numEntries size_t if you want to exceed 32-bit boundaries, everything else should be ready. + numEntries = (int)(length / sizeof(CKeyEntry)); + entries = new CKeyEntry[numEntries]; + rawbytes = (char*)entries; + +#ifdef THIS_IS_STUPID + for (uint32 i = 0; i < length; i++) { + CFileMgr::Read(file, &rawbytes[i], 1); + (*offset)++; + } +#else + CFileMgr::Read(file, rawbytes, length); + *offset += length; +#endif +} + +void +CKeyArray::Unload(void) +{ + delete[] entries; + entries = nil; + numEntries = 0; +} + +void +CKeyArray::Update(wchar *chars) +{ +#if !defined(FIX_BUGS) && !defined(FIX_BUGS_64) + int i; + for(i = 0; i < numEntries; i++) + entries[i].value = (wchar*)((uint8*)chars + (uintptr)entries[i].value); +#endif +} + +CKeyEntry* +CKeyArray::BinarySearch(const char *key, CKeyEntry *entries, int16 low, int16 high) +{ + int mid; + int diff; + + if(low > high) + return nil; + + mid = (low + high)/2; + diff = strcmp(key, entries[mid].key); + if(diff == 0) + return &entries[mid]; + if(diff < 0) + return BinarySearch(key, entries, low, mid-1); + if(diff > 0) + return BinarySearch(key, entries, mid+1, high); + return nil; +} + +wchar* +#if defined (FIX_BUGS) || defined(FIX_BUGS_64) +CKeyArray::Search(const char *key, wchar *data, uint8 *result) +#else +CKeyArray::Search(const char *key, uint8 *result) +#endif +{ + CKeyEntry *found; + char errstr[25]; + int i; + +#if defined (FIX_BUGS) || defined(FIX_BUGS_64) + found = BinarySearch(key, entries, 0, numEntries-1); + if (found) { + *result = true; + return (wchar*)((uint8*)data + found->valueOffset); + } +#else + found = BinarySearch(key, entries, 0, numEntries-1); + if (found) { + *result = true; + return found->value; + } +#endif + *result = false; +#ifdef MASTER + sprintf(errstr, ""); +#else + sprintf(errstr, "%s missing", key); +#endif // MASTER + for(i = 0; i < 25; i++) + WideErrorString[i] = errstr[i]; + return WideErrorString; +} + +void +CData::Load(size_t length, int file, size_t * offset) +{ + char *rawbytes; + + // You can make numChars size_t if you want to exceed 32-bit boundaries, everything else should be ready. + numChars = (int)(length / sizeof(wchar)); + chars = new wchar[numChars]; + rawbytes = (char*)chars; + +#ifdef THIS_IS_STUPID + for(uint32 i = 0; i < length; i++){ + CFileMgr::Read(file, &rawbytes[i], 1); + (*offset)++; + } +#else + CFileMgr::Read(file, rawbytes, length); + *offset += length; +#endif +} + +void +CData::Unload(void) +{ + delete[] chars; + chars = nil; + numChars = 0; +} + +void +CMissionTextOffsets::Load(size_t table_size, int file, size_t *offset, int) +{ +#ifdef THIS_IS_STUPID + size_t num_of_entries = table_size / sizeof(CMissionTextOffsets::Entry); + for (size_t mi = 0; mi < num_of_entries; mi++) { + for (uint32 i = 0; i < sizeof(data[mi].szMissionName); i++) { + CFileMgr::Read(file, &data[i].szMissionName[i], 1); + (*offset)++; + } + char* _buf = (char*)&data[mi].offset; + for (uint32 i = 0; i < sizeof(data[mi].offset); i++) { + CFileMgr::Read(file, &_buf[i], 1); + (*offset)++; + } + } + size = (uint16)num_of_entries; +#else + // not exact VC code but smaller and better :P + + // You can make this size_t if you want to exceed 32-bit boundaries, everything else should be ready. + size = (uint16) (table_size / sizeof(CMissionTextOffsets::Entry)); + CFileMgr::Read(file, (char*)data, sizeof(CMissionTextOffsets::Entry) * size); + *offset += sizeof(CMissionTextOffsets::Entry) * size; +#endif +} + +char* +UnicodeToAscii(wchar *src) +{ + static char aStr[256]; + int len; + for(len = 0; *src != '\0' && len < 256-1; len++, src++) +#ifdef MORE_LANGUAGES + if(*src < 128 || ((CGame::russianGame || CGame::japaneseGame) && *src < 256)) +#else + if(*src < 128) +#endif + aStr[len] = *src; + // convert to CP1252 + else if(*src <= 131) + aStr[len] = *src + 64; + else if (*src <= 141) + aStr[len] = *src + 66; + else if (*src <= 145) + aStr[len] = *src + 68; + else if (*src <= 149) + aStr[len] = *src + 71; + else if (*src <= 154) + aStr[len] = *src + 73; + else if (*src <= 164) + aStr[len] = *src + 75; + else if (*src <= 168) + aStr[len] = *src + 77; + else if (*src <= 204) + aStr[len] = *src + 80; + else switch (*src) { + case 205: aStr[len] = 209; break; + case 206: aStr[len] = 241; break; + case 207: aStr[len] = 191; break; + default: aStr[len] = '#'; break; + } + aStr[len] = '\0'; + return aStr; +} + +char* +UnicodeToAsciiForSaveLoad(wchar *src) +{ + static char aStr[256]; + int len; + for(len = 0; *src != '\0' && len < 256; len++, src++) + if(*src < 256) + aStr[len] = *src; + else + aStr[len] = '#'; + aStr[len] = '\0'; + return aStr; +} + +char* +UnicodeToAsciiForMemoryCard(wchar *src) +{ + static char aStr[256]; + int len; + for(len = 0; *src != '\0' && len < 256; len++, src++) + if(*src < 256) + aStr[len] = *src; + else + aStr[len] = '#'; + aStr[len] = '\0'; + return aStr; +} + +void +TextCopy(wchar *dst, const wchar *src) +{ + while((*dst++ = *src++) != '\0'); +} diff --git a/src/miami/text/Text.h b/src/miami/text/Text.h new file mode 100644 index 00000000..1174216c --- /dev/null +++ b/src/miami/text/Text.h @@ -0,0 +1,99 @@ +#pragma once + +char *UnicodeToAscii(wchar *src); +char *UnicodeToAsciiForSaveLoad(wchar *src); +char *UnicodeToAsciiForMemoryCard(wchar *src); +void TextCopy(wchar *dst, const wchar *src); + +struct CKeyEntry +{ +#if defined(FIX_BUGS) || defined(FIX_BUGS_64) + uint32 valueOffset; +#else + wchar *value; +#endif + char key[8]; +}; + +// If this fails, CKeyArray::Load will have to be fixed +VALIDATE_SIZE(CKeyEntry, 12); + +class CKeyArray +{ +public: + CKeyEntry *entries; + int numEntries; // You can make this size_t if you want to exceed 32-bit boundaries, everything else should be ready. + + CKeyArray(void) : entries(nil), numEntries(0) {} + ~CKeyArray(void) { Unload(); } + void Load(size_t length, int file, size_t *offset); + void Unload(void); + void Update(wchar *chars); + CKeyEntry *BinarySearch(const char *key, CKeyEntry *entries, int16 low, int16 high); +#if defined (FIX_BUGS) || defined(FIX_BUGS_64) + wchar *Search(const char *key, wchar *data, uint8 *result); +#else + wchar *Search(const char *key, uint8* result); +#endif +}; + +class CData +{ +public: + wchar *chars; + int numChars; // You can make this size_t if you want to exceed 32-bit boundaries, everything else should be ready. + + CData(void) : chars(nil), numChars(0) {} + ~CData(void) { Unload(); } + void Load(size_t length, int file, size_t* offset); + void Unload(void); +}; + +class CMissionTextOffsets +{ +public: + struct Entry + { + char szMissionName[8]; + uint32 offset; + }; + + enum {MAX_MISSION_TEXTS = 90}; // beware that LCS has more + + Entry data[MAX_MISSION_TEXTS]; + uint16 size; // You can make this size_t if you want to exceed 32-bit boundaries, everything else should be ready. + + CMissionTextOffsets(void) : size(0) {} + void Load(size_t table_size, int file, size_t* bytes_read, int); +}; + +struct ChunkHeader +{ + char magic[4]; + int size; +}; + +class CText +{ + CKeyArray keyArray; + CData data; + CKeyArray mission_keyArray; + CData mission_data; + char encoding; + bool bHasMissionTextOffsets; + bool bIsMissionTextLoaded; + char szMissionTableName[8]; + CMissionTextOffsets MissionTextOffsets; +public: + CText(void); + void Load(void); + void Unload(void); + wchar *Get(const char *key); + wchar GetUpperCase(wchar c); + void UpperCase(wchar *s); + void GetNameOfLoadedMissionText(char *outName); + void ReadChunkHeader(ChunkHeader *buf, int32 file, size_t *bytes_read); + void LoadMissionText(char *MissionTableName); +}; + +extern CText TheText; diff --git a/src/miami/vehicles/Automobile.cpp b/src/miami/vehicles/Automobile.cpp new file mode 100644 index 00000000..815b2534 --- /dev/null +++ b/src/miami/vehicles/Automobile.cpp @@ -0,0 +1,5900 @@ +#include "common.h" +#include "main.h" + +#include "General.h" +#include "RwHelper.h" +#include "Pad.h" +#include "ModelIndices.h" +#include "VisibilityPlugins.h" +#include "DMAudio.h" +#include "Clock.h" +#include "Timecycle.h" +#include "ZoneCull.h" +#include "Camera.h" +#include "Darkel.h" +#include "Rubbish.h" +#include "Fire.h" +#include "Explosion.h" +#include "Particle.h" +#include "ParticleObject.h" +#include "Glass.h" +#include "Antennas.h" +#include "Skidmarks.h" +#include "WindModifiers.h" +#include "Shadows.h" +#include "PointLights.h" +#include "Coronas.h" +#include "SpecialFX.h" +#include "WaterCannon.h" +#include "WaterLevel.h" +#include "Floater.h" +#include "World.h" +#include "SurfaceTable.h" +#include "Weather.h" +#include "HandlingMgr.h" +#include "Record.h" +#include "Remote.h" +#include "Population.h" +#include "CarCtrl.h" +#include "CarAI.h" +#include "Stats.h" +#include "Garages.h" +#include "PathFind.h" +#include "Replay.h" +#include "AnimManager.h" +#include "RpAnimBlend.h" +#include "AnimBlendAssociation.h" +#include "Ped.h" +#include "PlayerPed.h" +#include "Object.h" +#include "Automobile.h" +#include "Bike.h" +#include "Wanted.h" +#include "SaveBuf.h" + +bool bAllCarCheat; + +RwObject *GetCurrentAtomicObjectCB(RwObject *object, void *data); + +bool CAutomobile::m_sAllTaxiLights; + +const uint32 CAutomobile::nSaveStructSize = +#ifdef COMPATIBLE_SAVES + 1500; +#else + sizeof(CAutomobile); +#endif + +CAutomobile::CAutomobile(int32 id, uint8 CreatedBy) + : CVehicle(CreatedBy) +{ + int i; + + m_vehType = VEHICLE_TYPE_CAR; + + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); + m_fFireBlowUpTimer = 0.0f; + m_doingBurnout = 0; + bTaxiLight = m_sAllTaxiLights; + bFixedColour = false; + bBigWheels = false; + bWaterTight = false; + + SetModelIndex(id); + + // Already done in CVehicle... + switch(GetModelIndex()){ + case MI_HUNTER: + case MI_ANGEL: + case MI_FREEWAY: + m_nRadioStation = V_ROCK; + break; + case MI_RCBARON: + case MI_RCBANDIT: + case MI_RCRAIDER: + case MI_RCGOBLIN: + case MI_TOPFUN: + case MI_CADDY: + case MI_BAGGAGE: + m_nRadioStation = RADIO_OFF; + break; + } + + pHandling = mod_HandlingManager.GetHandlingData((tVehicleType)mi->m_handlingId); + pFlyingHandling = mod_HandlingManager.GetFlyingPointer((tVehicleType)mi->m_handlingId); + + m_auto_unused1 = 20.0f; + m_auto_unused2 = 0; + + mi->ChooseVehicleColour(m_currentColour1, m_currentColour2); + + bIsVan = !!(pHandling->Flags & HANDLING_IS_VAN); + bIsBig = !!(pHandling->Flags & HANDLING_IS_BIG); + bIsBus = !!(pHandling->Flags & HANDLING_IS_BUS); + bLowVehicle = !!(pHandling->Flags & HANDLING_IS_LOW); + + // Doors + if(bIsBus){ + Doors[DOOR_FRONT_LEFT].Init(-HALFPI, 0.0f, 0, 2); + Doors[DOOR_FRONT_RIGHT].Init(0.0f, HALFPI, 1, 2); + }else{ + Doors[DOOR_FRONT_LEFT].Init(-PI*0.4f, 0.0f, 0, 2); + Doors[DOOR_FRONT_RIGHT].Init(0.0f, PI*0.4f, 1, 2); + } + if(bIsVan){ + Doors[DOOR_REAR_LEFT].Init(-HALFPI, 0.0f, 1, 2); + Doors[DOOR_REAR_RIGHT].Init(0.0f, HALFPI, 0, 2); + }else{ + Doors[DOOR_REAR_LEFT].Init(-PI*0.4f, 0.0f, 0, 2); + Doors[DOOR_REAR_RIGHT].Init(0.0f, PI*0.4f, 1, 2); + } + if(pHandling->Flags & HANDLING_REV_BONNET) + Doors[DOOR_BONNET].Init(-PI*0.3f, 0.0f, 1, 0); + else + Doors[DOOR_BONNET].Init(0.0f, PI*0.3f, 1, 0); + if(pHandling->Flags & HANDLING_HANGING_BOOT) + Doors[DOOR_BOOT].Init(-PI*0.4f, 0.0f, 0, 0); + else if(pHandling->Flags & HANDLING_TAILGATE_BOOT) + Doors[DOOR_BOOT].Init(0.0, HALFPI, 1, 0); + else + Doors[DOOR_BOOT].Init(-PI*0.3f, 0.0f, 1, 0); + if(pHandling->Flags & HANDLING_NO_DOORS){ + Damage.SetDoorStatus(DOOR_FRONT_LEFT, DOOR_STATUS_MISSING); + Damage.SetDoorStatus(DOOR_FRONT_RIGHT, DOOR_STATUS_MISSING); + Damage.SetDoorStatus(DOOR_REAR_LEFT, DOOR_STATUS_MISSING); + Damage.SetDoorStatus(DOOR_REAR_RIGHT, DOOR_STATUS_MISSING); + } + + for(i = 0; i < 6; i++) + m_randomValues[i] = CGeneral::GetRandomNumberInRange(-0.15f, 0.15f); + + m_fMass = pHandling->fMass; + m_fTurnMass = pHandling->fTurnMass; + m_vecCentreOfMass = pHandling->CentreOfMass; + m_fAirResistance = pHandling->Dimension.x*pHandling->Dimension.z/m_fMass; + m_fElasticity = 0.05f; + m_fBuoyancy = pHandling->fBuoyancy; + + m_fOrientation = m_fPlaneSteer = 0.0f; + + m_nBusDoorTimerEnd = 0; + m_nBusDoorTimerStart = 0; + + m_fSteerAngle = 0.0f; + m_fGasPedal = 0.0f; + m_fBrakePedal = 0.0f; + m_pSetOnFireEntity = nil; + m_fGasPedalAudio = 0.0f; + bNotDamagedUpsideDown = false; + bMoreResistantToDamage = false; + bTankDetonateCars = true; + bStuckInSand = false; + bHeliDestroyed = false; + m_fVelocityChangeForAudio = 0.0f; + m_hydraulicState = 0; + + for(i = 0; i < 4; i++){ + m_aGroundPhysical[i] = nil; + m_aGroundOffset[i] = CVector(0.0f, 0.0f, 0.0f); + m_aSuspensionSpringRatioPrev[i] = m_aSuspensionSpringRatio[i] = 1.0f; + m_aWheelTimer[i] = 0.0f; + m_aWheelRotation[i] = 0.0f; + m_aWheelSpeed[i] = 0.0f; + m_aWheelState[i] = WHEEL_STATE_NORMAL; + m_aWheelSkidmarkType[i] = SKIDMARK_NORMAL; + m_aWheelSkidmarkBloody[i] = false; + } + + m_nWheelsOnGround = 0; + m_nDriveWheelsOnGround = 0; + m_nDriveWheelsOnGroundPrev = 0; + m_fHeightAboveRoad = 0.0f; + m_fTraction = 1.0f; + m_fTireTemperature = 1.0f; + + CColModel *colModel = mi->GetColModel(); + if(colModel->lines == nil){ + colModel->lines = (CColLine*)RwMalloc(4*sizeof(CColLine)); + colModel->numLines = 4; + } + + SetupSuspensionLines(); + + SetStatus(STATUS_SIMPLE); + bUseCollisionRecords = true; + + m_nNumPassengers = 0; + + if(m_nDoorLock == CARLOCK_UNLOCKED && + (id == MI_POLICE || id == MI_ENFORCER || id == MI_RHINO)) + m_nDoorLock = CARLOCK_LOCKED_INITIALLY; + + m_fCarGunLR = 0.0f; + m_fCarGunUD = 0.05f; + m_fPropellerRotation = 0.0f; + m_fHeliOrientation = -1.0f; + m_weaponDoorTimerLeft = 0.0f; + m_weaponDoorTimerRight = m_weaponDoorTimerLeft; + + if(GetModelIndex() == MI_DODO){ + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LF]), 0); + CMatrix mat1; + mat1.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); + CMatrix mat2(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF])); + mat1.GetPosition() += CVector(mat2.GetPosition().x + 0.1f, 0.0f, mat2.GetPosition().z); + mat1.UpdateRW(); + }else if(GetModelIndex() == MI_HUNTER){ + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB]), 0); + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RB]), 0); + }else if(IsRealHeli()){ + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LF]), 0); + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF]), 0); + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB]), 0); + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RB]), 0); + }else if(GetModelIndex() == MI_RHINO){ + bExplosionProof = true; + bBulletProof = true; + } +} + +void +CAutomobile::SetModelIndex(uint32 id) +{ + CVehicle::SetModelIndex(id); + SetupModelNodes(); +} + +#define SAND_SLOWDOWN (0.01f) +float CAR_BALANCE_MULT = 0.3f; +float HELI_ROTOR_DOTPROD_LIMIT = 0.95f; +CVector vecSeaSparrowGunPos(-0.5f, 2.4f, -0.785f); +CVector vecHunterGunPos(0.0f, 4.8f, -1.3f); +CVector vecHunterRocketPos(2.5f, 1.0f, -0.5f); +CVector vecDAMAGE_ENGINE_POS_SMALL(-0.1f, -0.1f, 0.0f); +CVector vecDAMAGE_ENGINE_POS_BIG(-0.5f, -0.3f, 0.0f); + +#pragma optimize("", off) // a workaround for another compiler bug + +void +CAutomobile::ProcessControl(void) +{ + int i; + float wheelRot; + CColModel *colModel; + float brake = 0.0f; + + if(bUsingSpecialColModel) + colModel = &CWorld::Players[CWorld::PlayerInFocus].m_ColModel; + else + colModel = GetColModel(); + bool drivingInSand = false; + bWarnedPeds = false; + m_doingBurnout = 0; + bStuckInSand = false; + bRestingOnPhysical = false; + + bool carHasNitro = bAllTaxisHaveNitro && GetStatus() == STATUS_PLAYER && IsTaxi(); + + if(CReplay::IsPlayingBack()) + return; + + // Heli wind + if(IsRealHeli()) + if((GetStatus() == STATUS_PLAYER || GetStatus() == STATUS_PHYSICS) && m_aWheelSpeed[1] > 0.075f || + GetStatus() == STATUS_SIMPLE) + CWindModifiers::RegisterOne(GetPosition(), 1); + + UpdatePassengerList(); + + // Improve grip of vehicles in certain cases + bool strongGrip1 = false; + bool strongGrip2 = false; + if(FindPlayerVehicle() && this != FindPlayerVehicle() && FindPlayerPed()->m_pWanted->GetWantedLevel() > 3 && + (AutoPilot.m_nCarMission == MISSION_RAMPLAYER_FARAWAY || AutoPilot.m_nCarMission == MISSION_RAMPLAYER_CLOSE || + AutoPilot.m_nCarMission == MISSION_BLOCKPLAYER_FARAWAY || AutoPilot.m_nCarMission == MISSION_BLOCKPLAYER_CLOSE) && + FindPlayerSpeed().Magnitude() > 0.3f){ + + strongGrip1 = true; + if(FindPlayerSpeed().Magnitude() > 0.4f && + m_vecMoveSpeed.Magnitude() < 0.3f) + strongGrip2 = true; + else if((GetPosition() - FindPlayerCoors()).Magnitude() > 50.0f) + strongGrip2 = true; + }else if(GetModelIndex() == MI_RCBANDIT && GetStatus() != STATUS_PLAYER_REMOTE) + strongGrip1 = true; + + if(bIsBus) + ProcessAutoBusDoors(); + + ProcessCarAlarm(); + + // Scan if this car sees the player committing any crimes + if(GetStatus() != STATUS_ABANDONED && GetStatus() != STATUS_WRECKED && + GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE && GetStatus() != STATUS_PLAYER_DISABLED){ + switch(GetModelIndex()) + case MI_FBIRANCH: + case MI_POLICE: + case MI_ENFORCER: + case MI_SECURICA: + case MI_RHINO: + case MI_BARRACKS: + ScanForCrimes(); + } + + // Process driver + if(pDriver) + if(IsUpsideDown() && CanPedEnterCar()){ + if(!pDriver->IsPlayer() && + !(pDriver->m_leader && pDriver->m_leader->bInVehicle) && + pDriver->CharCreatedBy != MISSION_CHAR) + pDriver->SetObjective(OBJECTIVE_LEAVE_CAR, this); + } + + ActivateBombWhenEntered(); + + // Process passengers + if(m_nNumPassengers != 0 && IsUpsideDown() && CanPedEnterCar()){ + for(i = 0; i < m_nNumMaxPassengers; i++) + if(pPassengers[i]) + if(!pPassengers[i]->IsPlayer() && + !(pPassengers[i]->m_leader && pPassengers[i]->m_leader->bInVehicle) && + pPassengers[i]->CharCreatedBy != MISSION_CHAR) + pPassengers[i]->SetObjective(OBJECTIVE_LEAVE_CAR, this); + } + + CRubbish::StirUp(this); + + UpdateClumpAlpha(); + + AutoPilot.m_bSlowedDownBecauseOfCars = false; + AutoPilot.m_bSlowedDownBecauseOfPeds = false; + + // Set Center of Mass to make car more stable + if(strongGrip1 || bCheat3) + m_vecCentreOfMass.z = 0.3f*m_aSuspensionSpringLength[0] + -1.0f*m_fHeightAboveRoad; + else if(pHandling->Flags & HANDLING_NONPLAYER_STABILISER && GetStatus() == STATUS_PHYSICS) + m_vecCentreOfMass.z = pHandling->CentreOfMass.z - 0.2f*pHandling->Dimension.z; + else + m_vecCentreOfMass = pHandling->CentreOfMass; + + // Park car + if(bCanPark && !bParking && VehicleCreatedBy != MISSION_VEHICLE && AutoPilot.m_nCarMission == MISSION_CRUISE && + ((CTimer::GetFrameCounter() + m_randomSeed)&0xF) == 0 && !IsTaxi()){ + CVector parkPosition = GetPosition() + 3.0f*GetRight() + 10.0f*GetForward(); + CEntity *ent = nil; + CColPoint colpoint; + if(!CWorld::ProcessLineOfSight(GetPosition(), parkPosition, colpoint, ent, true, true, true, false, false, false) || + ent == this) + CCarAI::GetCarToParkAtCoors(this, &parkPosition); + } + + // Process depending on status + + bool playerRemote = false; + switch(GetStatus()){ + case STATUS_PLAYER_REMOTE: +#ifdef FIX_BUGS + if(CPad::GetPad(0)->CarGunJustDown() && !bDisableRemoteDetonation){ +#else + if(CPad::GetPad(0)->WeaponJustDown() && !bDisableRemoteDetonation){ +#endif + BlowUpCar(FindPlayerPed()); + CRemote::TakeRemoteControlledCarFromPlayer(); + } + + if(GetModelIndex() == MI_RCBANDIT && !bDisableRemoteDetonationOnContact){ + //CVector pos = GetPosition(); + // FindPlayerCoors unused + if(RcbanditCheckHitWheels() || bIsInWater){ + CRemote::TakeRemoteControlledCarFromPlayer(); + BlowUpCar(FindPlayerPed()); + } + } + + if(CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle == this) + playerRemote = true; + // fall through + case STATUS_PLAYER: + if(playerRemote || + pDriver && pDriver->GetPedState() != PED_EXIT_CAR && pDriver->GetPedState() != PED_DRAG_FROM_CAR && pDriver->GetPedState() != PED_ARRESTED){ + // process control input if controlled by player + if(playerRemote || pDriver->m_nPedType == PEDTYPE_PLAYER1) + ProcessControlInputs(0); + + PruneReferences(); + + if(GetStatus() == STATUS_PLAYER && !CRecordDataForChase::IsRecording()) + DoDriveByShootings(); + + // Tweak center on mass when driving on two wheels + int twoWheelTime = CWorld::Players[CWorld::PlayerInFocus].m_nTimeNotFullyOnGround; + if(twoWheelTime > 500 && !IsRealHeli() && !IsRealPlane()){ + float tweak = Min(twoWheelTime-500, 1000)/500.0f; + if(GetUp().z > 0.0f){ + // positive when on left wheels, negative on right wheels + if(GetRight().z <= 0.0f) + tweak *= -1.0f; + m_vecCentreOfMass.z = pHandling->CentreOfMass.z + + CPad::GetPad(0)->GetSteeringLeftRight()/128.0f * + CAR_BALANCE_MULT * tweak * colModel->boundingBox.max.z; + } + }else + m_vecCentreOfMass.z = pHandling->CentreOfMass.z; + + if(bHoverCheat) + DoHoverSuspensionRatios(); + + if(m_aSuspensionSpringRatio[0] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[0].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[1] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[1].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[2] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[2].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[3] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[3].surfaceB) == ADHESIVE_SAND){ + if(GetModelIndex() != MI_RCBANDIT && GetModelIndex() != MI_RHINO){ + float slowdown; + CVector parallelSpeed = m_vecMoveSpeed - DotProduct(m_vecMoveSpeed, GetUp())*GetUp(); + float fSpeed = parallelSpeed.MagnitudeSqr(); + if(fSpeed > SQR(0.3f)){ + fSpeed = Sqrt(fSpeed); + parallelSpeed *= 0.3f / fSpeed; + slowdown = SAND_SLOWDOWN * Max(1.0f - 2.0f*fSpeed, 0.2f); + }else{ + bStuckInSand = true; + slowdown = SAND_SLOWDOWN; + } + if(pHandling->Flags & HANDLING_GOOD_INSAND) + slowdown *= 0.5f; + if(CWeather::WetRoads > 0.2f) + slowdown *= (1.2f - CWeather::WetRoads); + ApplyMoveForce(parallelSpeed * -CTimer::GetTimeStep()*slowdown*m_fMass); + drivingInSand = true; + } + } + }else if(pDriver && pDriver->IsPlayer() && + (pDriver->GetPedState() == PED_ARRESTED || + pDriver->GetPedState() == PED_DRAG_FROM_CAR || + (pDriver->GetPedState() == PED_EXIT_CAR || pDriver->m_objective == OBJECTIVE_LEAVE_CAR) && !CanPedJumpOutCar())){ + bIsHandbrakeOn = true; + m_fBrakePedal = 1.0f; + m_fGasPedal = 0.0f; + } + if(CPad::GetPad(0)->CarGunJustDown()) + ActivateBomb(); + break; + + case STATUS_SIMPLE: + CCarAI::UpdateCarAI(this); + CPhysical::ProcessControl(); + CCarCtrl::UpdateCarOnRails(this); + + m_nWheelsOnGround = 4; + m_nDriveWheelsOnGroundPrev = m_nDriveWheelsOnGround; + m_nDriveWheelsOnGround = 4; + + pHandling->Transmission.CalculateGearForSimpleCar(AutoPilot.m_fMaxTrafficSpeed/50.0f, m_nCurrentGear); + + wheelRot = ProcessWheelRotation(WHEEL_STATE_NORMAL, GetForward(), m_vecMoveSpeed, 0.35f); + for(i = 0; i < 4; i++) + m_aWheelRotation[i] += wheelRot; + + PlayHornIfNecessary(); + ReduceHornCounter(); + bVehicleColProcessed = false; + bAudioChangingGear = false; + // that's all we do for simple vehicles + return; + + case STATUS_PHYSICS: + CCarAI::UpdateCarAI(this); + CCarCtrl::SteerAICarWithPhysics(this); + PlayHornIfNecessary(); + + if(bIsBeingCarJacked){ + m_fGasPedal = 0.0f; + m_fBrakePedal = 1.0f; + bIsHandbrakeOn = true; + } + + if(m_aSuspensionSpringRatio[0] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[0].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[1] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[1].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[2] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[2].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[3] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[3].surfaceB) == ADHESIVE_SAND){ + if(GetModelIndex() != MI_RCBANDIT && GetModelIndex() != MI_SANDKING && GetModelIndex() != MI_BFINJECT){ + bStuckInSand = true; + if(CWeather::WetRoads > 0.0f) + ApplyMoveForce(m_vecMoveSpeed * -CTimer::GetTimeStep()*SAND_SLOWDOWN*m_fMass * (1.0f-CWeather::WetRoads)); + else + ApplyMoveForce(m_vecMoveSpeed * -CTimer::GetTimeStep()*SAND_SLOWDOWN*m_fMass); + } + } + break; + + case STATUS_ABANDONED: + if(m_vecMoveSpeed.MagnitudeSqr() < SQR(0.1f)) + m_fBrakePedal = 0.2f; + else + m_fBrakePedal = 0.0f; + bIsHandbrakeOn = false; + + m_fSteerAngle = 0.0f; + m_fGasPedal = 0.0f; + if(!IsAlarmOn()) + m_nCarHornTimer = 0; + + if(bIsBeingCarJacked){ + m_fGasPedal = 0.0f; + m_fBrakePedal = 1.0f; + bIsHandbrakeOn = true; + } + break; + + case STATUS_WRECKED: + m_fBrakePedal = 0.05f; + bIsHandbrakeOn = true; + + m_fSteerAngle = 0.0f; + m_fGasPedal = 0.0f; + if(!IsAlarmOn()) + m_nCarHornTimer = 0; + break; + + case STATUS_PLAYER_DISABLED: + if(m_vecMoveSpeed.MagnitudeSqr() < SQR(0.1f) || + (pDriver && pDriver->IsPlayer() && + (pDriver->GetPedState() == PED_ARRESTED || + pDriver->GetPedState() == PED_DRAG_FROM_CAR || + (pDriver->GetPedState() == PED_EXIT_CAR || pDriver->m_objective == OBJECTIVE_LEAVE_CAR) && !CanPedJumpOutCar()))){ + bIsHandbrakeOn = true; + m_fBrakePedal = 1.0f; + m_fGasPedal = 0.0f; + }else{ + m_fBrakePedal = 0.0f; + bIsHandbrakeOn = false; + } + + m_fSteerAngle = 0.0f; + m_fGasPedal = 0.0f; + if(!IsAlarmOn()) + m_nCarHornTimer = 0; + break; + default: break; + } + + // Skip physics if object is found to have been static recently + bool skipPhysics = false; + if(!bIsStuck && (GetStatus() == STATUS_ABANDONED || GetStatus() == STATUS_WRECKED)){ + bool makeStatic = false; + float moveSpeedLimit, turnSpeedLimit, distanceLimit; + + if(!bVehicleColProcessed && + m_vecMoveSpeed.IsZero() && + // BUG? m_aSuspensionSpringRatioPrev[3] is checked twice in the game. also, why 3? + m_aSuspensionSpringRatioPrev[3] != 1.0f) + makeStatic = true; + + if(GetStatus() == STATUS_WRECKED){ + moveSpeedLimit = 0.006f; + turnSpeedLimit = 0.0015f; + distanceLimit = 0.015f; + }else{ + moveSpeedLimit = 0.003f; + turnSpeedLimit = 0.0009f; + distanceLimit = 0.005f; + } + + m_vecMoveSpeedAvg = (m_vecMoveSpeedAvg + m_vecMoveSpeed)/2.0f; + m_vecTurnSpeedAvg = (m_vecTurnSpeedAvg + m_vecTurnSpeed)/2.0f; + + if(m_vecMoveSpeedAvg.MagnitudeSqr() <= sq(moveSpeedLimit*CTimer::GetTimeStep()) && + m_vecTurnSpeedAvg.MagnitudeSqr() <= sq(turnSpeedLimit*CTimer::GetTimeStep()) && + m_fDistanceTravelled < distanceLimit && + !(m_fDamageImpulse > 0.0f && m_pDamageEntity && m_pDamageEntity->IsPed()) || + makeStatic){ + m_nStaticFrames++; + + if(m_nStaticFrames > 10 || makeStatic) + if(!CCarCtrl::MapCouldMoveInThisArea(GetPosition().x, GetPosition().y)){ + if(!makeStatic || m_nStaticFrames > 10) + m_nStaticFrames = 10; + + skipPhysics = true; + + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + } + }else + m_nStaticFrames = 0; + if(IsRealHeli() && m_aWheelSpeed[1] > 0.0f){ + skipPhysics = false; + m_nStaticFrames = 0; + } + } + + // Postpone + for(i = 0; i < 4; i++) + if(m_aGroundPhysical[i]){ + bRestingOnPhysical = true; + if(!CWorld::bForceProcessControl && m_aGroundPhysical[i]->bIsInSafePosition){ + bWasPostponed = true; + return; + } + } + + if(bRestingOnPhysical){ + skipPhysics = false; + m_nStaticFrames = 0; + } + + VehicleDamage(0.0f, 0); + + // special control + switch(GetModelIndex()){ + case MI_FIRETRUCK: + FireTruckControl(); + break; + case MI_RHINO: + TankControl(); + BlowUpCarsInPath(); + break; + case MI_VOODOO: + HydraulicControl(); + break; + default: + if(CVehicle::bCheat3 || carHasNitro){ + // Make vehicle jump when horn is sounded + if(GetStatus() == STATUS_PLAYER && m_vecMoveSpeed.MagnitudeSqr() > sq(0.2f) && + // BUG: game checks [0] four times, instead of all wheels + m_aSuspensionSpringRatio[0] < 1.0f && + CPad::GetPad(0)->HornJustDown()){ + + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_1, 0.0f); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_JUMP, 1.0f); + + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, + m_aWheelColPoints[0].point + 0.5f*GetUp(), + 1.3f*m_vecMoveSpeed, nil, 2.5f); + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, + m_aWheelColPoints[0].point + 0.5f*GetUp(), + 1.2f*m_vecMoveSpeed, nil, 2.0f); + + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, + m_aWheelColPoints[2].point + 0.5f*GetUp(), + 1.3f*m_vecMoveSpeed, nil, 2.5f); + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, + m_aWheelColPoints[2].point + 0.5f*GetUp(), + 1.2f*m_vecMoveSpeed, nil, 2.0f); + + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, + m_aWheelColPoints[0].point + 0.5f*GetUp() - GetForward(), + 1.3f*m_vecMoveSpeed, nil, 2.5f); + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, + m_aWheelColPoints[0].point + 0.5f*GetUp() - GetForward(), + 1.2f*m_vecMoveSpeed, nil, 2.0f); + + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, + m_aWheelColPoints[2].point + 0.5f*GetUp() - GetForward(), + 1.3f*m_vecMoveSpeed, nil, 2.5f); + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, + m_aWheelColPoints[2].point + 0.5f*GetUp() - GetForward(), + 1.2f*m_vecMoveSpeed, nil, 2.0f); + + ApplyMoveForce(CVector(0.0f, 0.0f, 1.0f)*m_fMass*0.4f); + ApplyTurnForce(GetUp()*m_fTurnMass*0.01f, GetForward()*1.0f); + } + } + break; + } + + if(GetStatus() == STATUS_PHYSICS || GetStatus() == STATUS_SIMPLE) + if(AutoPilot.m_nCarMission == MISSION_HELI_FLYTOCOORS || + AutoPilot.m_nCarMission == MISSION_PLANE_FLYTOCOORS) + skipPhysics = true; + + if(skipPhysics){ + bHasContacted = false; + bIsInSafePosition = false; + bWasPostponed = false; + bHasHitWall = false; + m_nCollisionRecords = 0; + bHasCollided = false; + bVehicleColProcessed = false; + bAudioChangingGear = false; + m_nDamagePieceType = 0; + m_fDamageImpulse = 0.0f; + m_pDamageEntity = nil; + m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); + m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); + m_fTireTemperature = 1.0f; + }else{ + + // This has to be done if ProcessEntityCollision wasn't called + if(!bVehicleColProcessed){ + CMatrix mat(GetMatrix()); + bIsStuck = false; + bHasContacted = false; + bIsInSafePosition = false; + bWasPostponed = false; + bHasHitWall = false; + m_fDistanceTravelled = 0.0f; + m_bIsVehicleBeingShifted = false; + bSkipLineCol = false; + ApplyMoveSpeed(); + ApplyTurnSpeed(); + for(i = 0; CheckCollision() && i < 5; i++){ + GetMatrix() = mat; + ApplyMoveSpeed(); + ApplyTurnSpeed(); + } + bIsInSafePosition = true; + bIsStuck = false; + } + + CPhysical::ProcessControl(); + + ProcessBuoyancy(); + + // Rescale spring ratios, i.e. subtract wheel radius + for(i = 0; i < 4; i++){ + // wheel radius in relation to suspension line + float wheelRadius = 1.0f - m_aSuspensionSpringLength[i]/m_aSuspensionLineLength[i]; + // rescale such that 0.0 is fully compressed and 1.0 is fully extended + m_aSuspensionSpringRatio[i] = (m_aSuspensionSpringRatio[i]-wheelRadius)/(1.0f-wheelRadius); + } + + float fwdSpeed = Abs(DotProduct(m_vecMoveSpeed, GetForward())); + CVector contactPoints[4]; // relative to model + CVector contactSpeeds[4]; // speed at contact points + CVector springDirections[4]; // normalized, in world space + + for(i = 0; i < 4; i++){ + // Set spring under certain circumstances + if(Damage.GetWheelStatus(i) == WHEEL_STATUS_MISSING) + m_aSuspensionSpringRatio[i] = 1.0f; + else if(Damage.GetWheelStatus(i) == WHEEL_STATUS_BURST){ + // wheel more bumpy the faster we are + if(CGeneral::GetRandomNumberInRange(0, (uint16)(40*fwdSpeed) + 98) < 100){ + m_aSuspensionSpringRatio[i] += 0.3f*(m_aSuspensionLineLength[i]-m_aSuspensionSpringLength[i])/m_aSuspensionSpringLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + }else if(CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[i].surfaceB) == ADHESIVE_SAND && + GetModelIndex() != MI_RHINO){ + fwdSpeed *= 0.7f; + float f = 1.0f - fwdSpeed/0.3f - 0.7f*CWeather::WetRoads; + f = Max(f, 0.4f); + m_aSuspensionSpringRatio[i] += 0.35f*f*(m_aSuspensionLineLength[i]-m_aSuspensionSpringLength[i])/m_aSuspensionSpringLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + + // get points and directions if spring is compressed + if(m_aSuspensionSpringRatio[i] < 1.0f){ + contactPoints[i] = m_aWheelColPoints[i].point - GetPosition(); + springDirections[i] = Multiply3x3(GetMatrix(), colModel->lines[i].p1 - colModel->lines[i].p0); + springDirections[i].Normalise(); + } + } + + // Make springs push up vehicle + for(i = 0; i < 4; i++){ + if(m_aSuspensionSpringRatio[i] < 1.0f){ + float bias = pHandling->fSuspensionBias; + if(i == CARWHEEL_REAR_LEFT || i == CARWHEEL_REAR_RIGHT) + bias = 1.0f - bias; + + ApplySpringCollisionAlt(pHandling->fSuspensionForceLevel, + springDirections[i], contactPoints[i], + m_aSuspensionSpringRatio[i], bias, m_aWheelColPoints[i].normal); + + m_aWheelSkidmarkUnk[i] = false; + if(m_aWheelColPoints[i].surfaceB == SURFACE_GRASS || + m_aWheelColPoints[i].surfaceB == SURFACE_MUD_DRY) + m_aWheelSkidmarkType[i] = SKIDMARK_MUDDY; + else if(m_aWheelColPoints[i].surfaceB == SURFACE_SAND || + m_aWheelColPoints[i].surfaceB == SURFACE_SAND_BEACH){ + m_aWheelSkidmarkType[i] = SKIDMARK_SANDY; + m_aWheelSkidmarkUnk[i] = true; + }else + m_aWheelSkidmarkType[i] = SKIDMARK_NORMAL; + }else{ + contactPoints[i] = Multiply3x3(GetMatrix(), colModel->lines[i].p1); + } + } + + // Get speed at contact points + for(i = 0; i < 4; i++){ + contactSpeeds[i] = GetSpeed(contactPoints[i]); + if(m_aGroundPhysical[i]){ + // subtract movement of physical we're standing on + contactSpeeds[i] -= m_aGroundPhysical[i]->GetSpeed(m_aGroundOffset[i]); +#ifndef FIX_BUGS + // this shouldn't be reset because we still need it below + m_aGroundPhysical[i] = nil; +#endif + } + if(m_aSuspensionSpringRatio[i] < 1.0f && m_aWheelColPoints[i].normal.z > 0.35f) + springDirections[i] = -m_aWheelColPoints[i].normal; + } + + // dampen springs + for(i = 0; i < 4; i++) + if(m_aSuspensionSpringRatio[i] < 0.99999f) + ApplySpringDampening(pHandling->fSuspensionDampingLevel, + springDirections[i], contactPoints[i], contactSpeeds[i]); + + // Get speed at contact points again + for(i = 0; i < 4; i++){ + contactSpeeds[i] = GetSpeed(contactPoints[i]); + if(m_aGroundPhysical[i]){ + // subtract movement of physical we're standing on + contactSpeeds[i] -= m_aGroundPhysical[i]->GetSpeed(m_aGroundOffset[i]); + m_aGroundPhysical[i] = nil; + } + } + + bool gripCheat = true; + fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward()); + if(!strongGrip1 && !CVehicle::bCheat3) + gripCheat = false; + float acceleration = pHandling->Transmission.CalculateDriveAcceleration(m_fGasPedal, m_nCurrentGear, m_fChangeGearTime, fwdSpeed, gripCheat); + acceleration /= m_fForceMultiplier; + + if(IsRealHeli() || IsRealPlane()) + acceleration = 0.0f; + + if(bAudioChangingGear && m_fGasPedal > 0.4f && m_fBrakePedal < 0.1f && fwdSpeed > 0.15f && + this == FindPlayerVehicle() && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_1STPERSON){ + if(GetStatus() == STATUS_PLAYER && !(pHandling->Flags & HANDLING_IS_BUS)){ + if(m_nBusDoorTimerEnd == 0) + m_nBusDoorTimerEnd = 1000; + else { + uint32 timeStepInMs = CTimer::GetTimeStepInMilliseconds(); + if(m_nBusDoorTimerEnd > timeStepInMs) + m_nBusDoorTimerEnd -= timeStepInMs; + else + m_nBusDoorTimerEnd = 0; + } + } + + if((m_aSuspensionSpringRatio[0] < 1.0f || m_aSuspensionSpringRatio[2] < 1.0f) && + (m_aSuspensionSpringRatio[1] < 1.0f || m_aSuspensionSpringRatio[3] < 1.0f)) + ApplyTurnForce(-GRAVITY*Min(m_fTurnMass, 2500.0f)*GetUp(), -1.0f*GetForward()); + } + + brake = m_fBrakePedal * pHandling->fBrakeDeceleration * CTimer::GetTimeStep(); + bool neutralHandling = GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE && (pHandling->Flags & HANDLING_NEUTRALHANDLING); + float brakeBiasFront = neutralHandling ? 1.0f : 2.0f*pHandling->fBrakeBias; + float brakeBiasRear = neutralHandling ? 1.0f : 2.0f-pHandling->fBrakeBias; // looks like a bug, but it was correct in III... + float tractionBiasFront = neutralHandling ? 1.0f : 2.0f*pHandling->fTractionBias; + float tractionBiasRear = neutralHandling ? 1.0f : 2.0f-tractionBiasFront; + + // Count how many wheels are touching the ground + + m_nWheelsOnGround = 0; + m_nDriveWheelsOnGroundPrev = m_nDriveWheelsOnGround; + m_nDriveWheelsOnGround = 0; + + for(i = 0; i < 4; i++){ + if(m_aSuspensionSpringRatio[i] < 1.0f) + m_aWheelTimer[i] = 4.0f; + else + m_aWheelTimer[i] = Max(m_aWheelTimer[i]-CTimer::GetTimeStep(), 0.0f); + + if(m_aWheelTimer[i] > 0.0f){ + m_nWheelsOnGround++; + switch(pHandling->Transmission.nDriveType){ + case '4': + m_nDriveWheelsOnGround++; + break; + case 'F': + if(i == CARWHEEL_FRONT_LEFT || i == CARWHEEL_FRONT_RIGHT) + m_nDriveWheelsOnGround++; + break; + case 'R': + if(i == CARWHEEL_REAR_LEFT || i == CARWHEEL_REAR_RIGHT) + m_nDriveWheelsOnGround++; + break; + } + } + } + + float traction; + if(GetStatus() == STATUS_PHYSICS) + traction = 0.004f * m_fTraction; + else + traction = 0.004f; + traction *= pHandling->fTractionMultiplier / 4.0f; + traction /= m_fForceMultiplier; + if(CVehicle::bCheat3) + traction *= 4.0f; + + if(FindPlayerVehicle() != this && (strongGrip1 || CVehicle::bCheat3)){ + traction *= 1.2f; + acceleration *= 1.4f; + if(strongGrip2 || CVehicle::bCheat3){ + traction *= 1.3f; + acceleration *= 1.4f; + } + } + + static float fThrust; + static tWheelState WheelState[4]; + + bool rearWheelsFirst = !!(pHandling->Flags & HANDLING_REARWHEEL_1ST); + + // Process front wheels on ground - first try + + if(!rearWheelsFirst){ + if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] > 0.0f || m_aWheelTimer[CARWHEEL_FRONT_RIGHT] > 0.0f){ + float s = Sin(m_fSteerAngle); + float c = Cos(m_fSteerAngle); + + CVector wheelFwd, wheelRight, tmp; + + if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] > 0.0f){ + if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier)) + fThrust = acceleration; + else + fThrust = 0.0f; + + wheelFwd = GetForward(); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal)*m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal); + wheelRight.Normalise(); + tmp = c*wheelFwd - s*wheelRight; + wheelRight = s*wheelFwd + c*wheelRight; + wheelFwd = tmp; + + m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceA = SURFACE_WHEELBASE; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_FRONT_LEFT])*traction; + if(GetStatus() == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceB); + WheelState[CARWHEEL_FRONT_LEFT] = m_aWheelState[CARWHEEL_FRONT_LEFT]; + + if(Damage.GetWheelStatus(CARWHEEL_FRONT_LEFT) == WHEEL_STATUS_BURST) + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_LEFT], contactPoints[CARWHEEL_FRONT_LEFT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront*Damage.m_fWheelDamageEffect, + CARWHEEL_FRONT_LEFT, + &m_aWheelSpeed[CARWHEEL_FRONT_LEFT], + &WheelState[CARWHEEL_FRONT_LEFT], + WHEEL_STATUS_BURST); + else + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_LEFT], contactPoints[CARWHEEL_FRONT_LEFT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront, + CARWHEEL_FRONT_LEFT, + &m_aWheelSpeed[CARWHEEL_FRONT_LEFT], + &WheelState[CARWHEEL_FRONT_LEFT], + WHEEL_STATUS_OK); + } + + if(m_aWheelTimer[CARWHEEL_FRONT_RIGHT] > 0.0f){ + if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier)) + fThrust = acceleration; + else + fThrust = 0.0f; + + wheelFwd = GetForward(); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal)*m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal); + wheelRight.Normalise(); + tmp = c*wheelFwd - s*wheelRight; + wheelRight = s*wheelFwd + c*wheelRight; + wheelFwd = tmp; + + m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceA = SURFACE_WHEELBASE; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_FRONT_RIGHT])*traction; + if(GetStatus() == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceB); + WheelState[CARWHEEL_FRONT_RIGHT] = m_aWheelState[CARWHEEL_FRONT_RIGHT]; + + if(Damage.GetWheelStatus(CARWHEEL_FRONT_RIGHT) == WHEEL_STATUS_BURST) + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_RIGHT], contactPoints[CARWHEEL_FRONT_RIGHT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront*Damage.m_fWheelDamageEffect, + CARWHEEL_FRONT_RIGHT, + &m_aWheelSpeed[CARWHEEL_FRONT_RIGHT], + &WheelState[CARWHEEL_FRONT_RIGHT], + WHEEL_STATUS_BURST); + else + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_RIGHT], contactPoints[CARWHEEL_FRONT_RIGHT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront, + CARWHEEL_FRONT_RIGHT, + &m_aWheelSpeed[CARWHEEL_FRONT_RIGHT], + &WheelState[CARWHEEL_FRONT_RIGHT], + WHEEL_STATUS_OK); + } + } + + // Process front wheels off ground + + if(!IsRealHeli()){ + if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] <= 0.0f){ + if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) && acceleration != 0.0f){ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_FRONT_LEFT] < 2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_FRONT_LEFT] > -2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] += 0.1f; + } + }else{ + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] *= 0.95f; + } + m_aWheelRotation[CARWHEEL_FRONT_LEFT] += m_aWheelSpeed[CARWHEEL_FRONT_LEFT]; + } + if(m_aWheelTimer[CARWHEEL_FRONT_RIGHT] <= 0.0f){ + if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) && acceleration != 0.0f){ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] < 2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] > -2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] += 0.1f; + } + }else{ + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] *= 0.95f; + } + m_aWheelRotation[CARWHEEL_FRONT_RIGHT] += m_aWheelSpeed[CARWHEEL_FRONT_RIGHT]; + } + } + } + + // Process rear wheels + + if(m_aWheelTimer[CARWHEEL_REAR_LEFT] > 0.0f || m_aWheelTimer[CARWHEEL_REAR_RIGHT] > 0.0f){ + CVector wheelFwd = GetForward(); + CVector wheelRight = GetRight(); // overwritten for resp. wheel + + float rearBrake = brake; + float rearTraction = traction; + if(bIsHandbrakeOn){ +#ifdef FIX_BUGS + // Not sure if this is needed, but brake usually has timestep as a factor + rearBrake = 20000.0f * CTimer::GetTimeStepFix(); +#else + rearBrake = 20000.0f; +#endif + if(fwdSpeed > 0.1f && pHandling->Flags & HANDLING_HANDBRAKE_TYRE){ + m_fTireTemperature += 0.005*CTimer::GetTimeStep(); + if(m_fTireTemperature > 2.0f) + m_fTireTemperature = 2.0f; + } + }else if(m_doingBurnout && mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier)){ + rearBrake = 0.0f; + rearTraction = 0.0f; + // BUG: missing timestep + ApplyTurnForce(contactPoints[CARWHEEL_REAR_LEFT], -0.001f*m_fTurnMass*m_fSteerAngle*GetRight()); + }else if(m_fTireTemperature > 1.0f){ + rearTraction *= m_fTireTemperature; + } + + if(m_aWheelTimer[CARWHEEL_REAR_LEFT] > 0.0f){ + if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier)) + fThrust = acceleration; + else + fThrust = 0.0f; + + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_REAR_LEFT].normal)*m_aWheelColPoints[CARWHEEL_REAR_LEFT].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_REAR_LEFT].normal); + wheelRight.Normalise(); + + m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceA = SURFACE_WHEELBASE; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_REAR_LEFT])*rearTraction; + if(GetStatus() == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceB); + WheelState[CARWHEEL_REAR_LEFT] = m_aWheelState[CARWHEEL_REAR_LEFT]; + + if(Damage.GetWheelStatus(CARWHEEL_REAR_LEFT) == WHEEL_STATUS_BURST) + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_REAR_LEFT], contactPoints[CARWHEEL_REAR_LEFT], + m_nWheelsOnGround, fThrust, + rearBrake*brakeBiasRear, + adhesion*tractionBiasRear*Damage.m_fWheelDamageEffect, + CARWHEEL_REAR_LEFT, + &m_aWheelSpeed[CARWHEEL_REAR_LEFT], + &WheelState[CARWHEEL_REAR_LEFT], + WHEEL_STATUS_BURST); + else + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_REAR_LEFT], contactPoints[CARWHEEL_REAR_LEFT], + m_nWheelsOnGround, fThrust, + rearBrake*brakeBiasRear, + adhesion*tractionBiasRear, + CARWHEEL_REAR_LEFT, + &m_aWheelSpeed[CARWHEEL_REAR_LEFT], + &WheelState[CARWHEEL_REAR_LEFT], + WHEEL_STATUS_OK); + } + +#ifdef FIX_BUGS + // Shouldn't we reset these after the left wheel? + wheelFwd = GetForward(); + wheelRight = GetRight(); // actually useless +#endif + + if(m_aWheelTimer[CARWHEEL_REAR_RIGHT] > 0.0f){ + if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier)) + fThrust = acceleration; + else + fThrust = 0.0f; + + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_REAR_RIGHT].normal)*m_aWheelColPoints[CARWHEEL_REAR_RIGHT].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_REAR_RIGHT].normal); + wheelRight.Normalise(); + + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceA = SURFACE_WHEELBASE; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_REAR_RIGHT])*rearTraction; + if(GetStatus() == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceB); + WheelState[CARWHEEL_REAR_RIGHT] = m_aWheelState[CARWHEEL_REAR_RIGHT]; + + if(Damage.GetWheelStatus(CARWHEEL_REAR_RIGHT) == WHEEL_STATUS_BURST) + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_REAR_RIGHT], contactPoints[CARWHEEL_REAR_RIGHT], + m_nWheelsOnGround, fThrust, + rearBrake*brakeBiasRear, + adhesion*tractionBiasRear*Damage.m_fWheelDamageEffect, + CARWHEEL_REAR_RIGHT, + &m_aWheelSpeed[CARWHEEL_REAR_RIGHT], + &WheelState[CARWHEEL_REAR_RIGHT], + WHEEL_STATUS_BURST); + else + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_REAR_RIGHT], contactPoints[CARWHEEL_REAR_RIGHT], + m_nWheelsOnGround, fThrust, + rearBrake*brakeBiasRear, + adhesion*tractionBiasRear, + CARWHEEL_REAR_RIGHT, + &m_aWheelSpeed[CARWHEEL_REAR_RIGHT], + &WheelState[CARWHEEL_REAR_RIGHT], + WHEEL_STATUS_OK); + } + } + + if(m_doingBurnout && mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) && + (m_aWheelState[CARWHEEL_REAR_LEFT] == WHEEL_STATE_SPINNING || m_aWheelState[CARWHEEL_REAR_RIGHT] == WHEEL_STATE_SPINNING)){ + m_fTireTemperature += 0.001f*CTimer::GetTimeStep(); + if(m_fTireTemperature > 3.0f) + m_fTireTemperature = 3.0f; + }else if(m_fTireTemperature > 1.0f){ + m_fTireTemperature = (m_fTireTemperature - 1.0f)*Pow(0.995f, CTimer::GetTimeStep()) + 1.0f; + } + + // Process rear wheels off ground + + if(!IsRealHeli()){ + if(m_aWheelTimer[CARWHEEL_REAR_LEFT] <= 0.0f){ + if(bIsHandbrakeOn) + m_aWheelSpeed[CARWHEEL_REAR_LEFT] = 0.0f; + else if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) && acceleration != 0.0f){ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_REAR_LEFT] < 2.0f) + m_aWheelSpeed[CARWHEEL_REAR_LEFT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_REAR_LEFT] > -2.0f) + m_aWheelSpeed[CARWHEEL_REAR_LEFT] += 0.1f; + } + }else{ + m_aWheelSpeed[CARWHEEL_REAR_LEFT] *= 0.95f; + } + m_aWheelRotation[CARWHEEL_REAR_LEFT] += m_aWheelSpeed[CARWHEEL_REAR_LEFT]; + } + if(m_aWheelTimer[CARWHEEL_REAR_RIGHT] <= 0.0f){ + if(bIsHandbrakeOn) + m_aWheelSpeed[CARWHEEL_REAR_RIGHT] = 0.0f; + else if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) && acceleration != 0.0f){ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_REAR_RIGHT] < 2.0f) + m_aWheelSpeed[CARWHEEL_REAR_RIGHT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_REAR_RIGHT] > -2.0f) + m_aWheelSpeed[CARWHEEL_REAR_RIGHT] += 0.1f; + } + }else{ + m_aWheelSpeed[CARWHEEL_REAR_RIGHT] *= 0.95f; + } + m_aWheelRotation[CARWHEEL_REAR_RIGHT] += m_aWheelSpeed[CARWHEEL_REAR_RIGHT]; + } + } + + // Process front wheels on ground - second try + + if(rearWheelsFirst){ + if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] > 0.0f || m_aWheelTimer[CARWHEEL_FRONT_RIGHT] > 0.0f){ + float s = Sin(m_fSteerAngle); + float c = Cos(m_fSteerAngle); + + CVector wheelFwd, wheelRight, tmp; + + if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] > 0.0f){ + if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier)) + fThrust = acceleration; + else + fThrust = 0.0f; + + wheelFwd = GetForward(); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal)*m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal); + wheelRight.Normalise(); + tmp = c*wheelFwd - s*wheelRight; + wheelRight = s*wheelFwd + c*wheelRight; + wheelFwd = tmp; + + m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceA = SURFACE_WHEELBASE; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_FRONT_LEFT])*traction; + if(GetStatus() == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceB); + WheelState[CARWHEEL_FRONT_LEFT] = m_aWheelState[CARWHEEL_FRONT_LEFT]; + + if(Damage.GetWheelStatus(CARWHEEL_FRONT_LEFT) == WHEEL_STATUS_BURST) + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_LEFT], contactPoints[CARWHEEL_FRONT_LEFT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront*Damage.m_fWheelDamageEffect, + CARWHEEL_FRONT_LEFT, + &m_aWheelSpeed[CARWHEEL_FRONT_LEFT], + &WheelState[CARWHEEL_FRONT_LEFT], + WHEEL_STATUS_BURST); + else + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_LEFT], contactPoints[CARWHEEL_FRONT_LEFT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront, + CARWHEEL_FRONT_LEFT, + &m_aWheelSpeed[CARWHEEL_FRONT_LEFT], + &WheelState[CARWHEEL_FRONT_LEFT], + WHEEL_STATUS_OK); + } + + if(m_aWheelTimer[CARWHEEL_FRONT_RIGHT] > 0.0f){ + if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier)) + fThrust = acceleration; + else + fThrust = 0.0f; + + wheelFwd = GetForward(); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal)*m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal); + wheelRight.Normalise(); + tmp = c*wheelFwd - s*wheelRight; + wheelRight = s*wheelFwd + c*wheelRight; + wheelFwd = tmp; + + m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceA = SURFACE_WHEELBASE; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_FRONT_RIGHT])*traction; + if(GetStatus() == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceB); + WheelState[CARWHEEL_FRONT_RIGHT] = m_aWheelState[CARWHEEL_FRONT_RIGHT]; + + if(Damage.GetWheelStatus(CARWHEEL_FRONT_RIGHT) == WHEEL_STATUS_BURST) + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_RIGHT], contactPoints[CARWHEEL_FRONT_RIGHT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront*Damage.m_fWheelDamageEffect, + CARWHEEL_FRONT_RIGHT, + &m_aWheelSpeed[CARWHEEL_FRONT_RIGHT], + &WheelState[CARWHEEL_FRONT_RIGHT], + WHEEL_STATUS_BURST); + else + ProcessWheel(wheelFwd, wheelRight, + contactSpeeds[CARWHEEL_FRONT_RIGHT], contactPoints[CARWHEEL_FRONT_RIGHT], + m_nWheelsOnGround, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront, + CARWHEEL_FRONT_RIGHT, + &m_aWheelSpeed[CARWHEEL_FRONT_RIGHT], + &WheelState[CARWHEEL_FRONT_RIGHT], + WHEEL_STATUS_OK); + } + } + + // Process front wheels off ground + + if (!IsRealHeli()) { + if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] <= 0.0f){ + if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) && acceleration != 0.0f){ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_FRONT_LEFT] < 2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_FRONT_LEFT] > -2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] += 0.1f; + } + }else{ + m_aWheelSpeed[CARWHEEL_FRONT_LEFT] *= 0.95f; + } + m_aWheelRotation[CARWHEEL_FRONT_LEFT] += m_aWheelSpeed[CARWHEEL_FRONT_LEFT]; + } + if(m_aWheelTimer[CARWHEEL_FRONT_RIGHT] <= 0.0f){ + if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) && acceleration != 0.0f){ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] < 2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] -= 0.2f; + }else{ + if(m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] > -2.0f) + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] += 0.1f; + } + }else{ + m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] *= 0.95f; + } + m_aWheelRotation[CARWHEEL_FRONT_RIGHT] += m_aWheelSpeed[CARWHEEL_FRONT_RIGHT]; + } + } + } + + for(i = 0; i < 4; i++){ + float wheelPos = colModel->lines[i].p0.z; + if(m_aSuspensionSpringRatio[i] > 0.0f) + wheelPos -= m_aSuspensionSpringRatio[i]*m_aSuspensionSpringLength[i]; + if(GetModelIndex() == MI_VOODOO && bUsingSpecialColModel) + m_aWheelPosition[i] = wheelPos; + else + m_aWheelPosition[i] += (wheelPos - m_aWheelPosition[i])*0.75f; + } + for(i = 0; i < 4; i++) + m_aWheelState[i] = WheelState[i]; + if(m_fGasPedal < 0.0f){ + if(m_aWheelState[CARWHEEL_REAR_LEFT] == WHEEL_STATE_SPINNING) + m_aWheelState[CARWHEEL_REAR_LEFT] = WHEEL_STATE_NORMAL; + if(m_aWheelState[CARWHEEL_REAR_RIGHT] == WHEEL_STATE_SPINNING) + m_aWheelState[CARWHEEL_REAR_RIGHT] = WHEEL_STATE_NORMAL; + } + + // Process horn + + if(GetStatus() != STATUS_PLAYER){ + if(!IsAlarmOn()) + ReduceHornCounter(); + }else{ + if(UsesSiren()){ + if(Pads[0].bHornHistory[Pads[0].iCurrHornHistory]){ + if(Pads[0].bHornHistory[(Pads[0].iCurrHornHistory+CPad::HORNHISTORY_SIZE-1) % CPad::HORNHISTORY_SIZE] && + Pads[0].bHornHistory[(Pads[0].iCurrHornHistory+CPad::HORNHISTORY_SIZE-2) % CPad::HORNHISTORY_SIZE]) + m_nCarHornTimer = 1; + else + m_nCarHornTimer = 0; + }else if(Pads[0].bHornHistory[(Pads[0].iCurrHornHistory+CPad::HORNHISTORY_SIZE-1) % CPad::HORNHISTORY_SIZE] && + !Pads[0].bHornHistory[(Pads[0].iCurrHornHistory+1) % CPad::HORNHISTORY_SIZE]){ + m_nCarHornTimer = 0; + m_bSirenOrAlarm = !m_bSirenOrAlarm; + }else + m_nCarHornTimer = 0; + }else if(GetModelIndex() != MI_VOODOO && !CVehicle::bCheat3 && !carHasNitro){ + if(!IsAlarmOn()){ + if(Pads[0].GetHorn()) + m_nCarHornTimer = 1; + else + m_nCarHornTimer = 0; + } + } + } + + // Flying + + bool playRotorSound = false; + bool isPlane = GetModelIndex() == MI_DODO || bAllDodosCheat; +#ifdef FIX_BUGS + isPlane = isPlane && !IsRealHeli(); +#endif + if(GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE && GetStatus() != STATUS_PHYSICS){ + if(IsRealHeli()){ + bEngineOn = false; + m_aWheelSpeed[1] = Max(m_aWheelSpeed[1]-0.0005f, 0.0f); + if(GetModelIndex() != MI_RCRAIDER && GetModelIndex() != MI_RCGOBLIN) + if(m_aWheelSpeed[1] < 0.154f && m_aWheelSpeed[1] > 0.0044f) + playRotorSound = true; + } + }else if(isPlane && m_vecMoveSpeed.Magnitude() > 0.0f && CTimer::GetTimeStep() > 0.0f){ + if(GetModelIndex() == MI_DODO) + FlyingControl(FLIGHT_MODEL_DODO); + else + FlyingControl(FLIGHT_MODEL_PLANE); + }else if(GetModelIndex() == MI_RCBARON){ + FlyingControl(FLIGHT_MODEL_RCPLANE); + }else if(IsRealHeli() || bAllCarCheat){ +#ifdef RESTORE_ALLCARSHELI_CHEAT + if (bAllCarCheat) + FlyingControl(FLIGHT_MODEL_HELI); + else +#endif + { + // Speed up rotor + if (m_aWheelSpeed[1] < 0.22f && !bIsInWater) { + if (GetModelIndex() == MI_RCRAIDER || GetModelIndex() == MI_RCGOBLIN) + m_aWheelSpeed[1] += 0.003f; + else + m_aWheelSpeed[1] += 0.001f; + } + + // Fly + if (m_aWheelSpeed[1] > 0.15f) { + if (GetModelIndex() == MI_RCRAIDER || GetModelIndex() == MI_RCGOBLIN) + FlyingControl(FLIGHT_MODEL_RCHELI); + else if (m_nWheelsOnGround < 4 && !(GetModelIndex() == MI_SEASPAR && bTouchingWater) || + CPad::GetPad(0)->GetAccelerate() != 0 || +#ifndef FREE_CAM + CPad::GetPad(0)->GetCarGunUpDown() > 1.0f || +#else + ((!CCamera::bFreeCam || (CCamera::bFreeCam && !CPad::IsAffectedByController)) && CPad::GetPad(0)->GetCarGunUpDown() > 1.0f) || +#endif + Abs(m_vecMoveSpeed.x) > 0.02f || + Abs(m_vecMoveSpeed.y) > 0.02f || + Abs(m_vecMoveSpeed.z) > 0.02f) + FlyingControl(FLIGHT_MODEL_HELI); + } + } + + // Blade collision + if(m_aWheelSpeed[1] > 0.015f && m_aCarNodes[CAR_BONNET]){ + CMatrix mat; + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BONNET])); + if(GetModelIndex() == MI_RCRAIDER || GetModelIndex() == MI_RCGOBLIN) + DoBladeCollision(mat.GetPosition(), GetMatrix(), ROTOR_TOP, 0.72f, 0.9f); + else if(GetModelIndex() == MI_SPARROW || GetModelIndex() == MI_SEASPAR) + DoBladeCollision(mat.GetPosition(), GetMatrix(), ROTOR_TOP, 5.15f, 0.8f); + else if(GetModelIndex() == MI_HUNTER) + DoBladeCollision(mat.GetPosition(), GetMatrix(), ROTOR_TOP, 6.15f, 0.5f); + else + DoBladeCollision(mat.GetPosition(), GetMatrix(), ROTOR_TOP, 6.15f, 1.0f); + } + + // Heli weapons + if(GetModelIndex() == MI_HUNTER && GetStatus() == STATUS_PLAYER){ + // Hunter rockets + if(CPad::GetPad(0)->CarGunJustDown() && CTimer::GetTimeInMilliseconds() > m_nGunFiringTime+350){ + CWeapon gun(WEAPONTYPE_ROCKETLAUNCHER, 100); + CVector source = vecHunterRocketPos; + source = GetMatrix()*source + Max(DotProduct(m_vecMoveSpeed, GetForward()), 0.0f)*GetForward()*CTimer::GetTimeStep(); + gun.FireProjectile(this, &source, 0.0f); + + source = vecHunterRocketPos; + source.x = -source.x; + source = GetMatrix()*source + Max(DotProduct(m_vecMoveSpeed, GetForward()), 0.0f)*GetForward()*CTimer::GetTimeStep(); + gun.FireProjectile(this, &source, 0.0f); + + CStats::RoundsFiredByPlayer++; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); + m_nGunFiringTime = CTimer::GetTimeInMilliseconds(); + // Hunter gun + }else if(CPad::GetPad(0)->GetHandBrake() && CTimer::GetTimeInMilliseconds() > m_nGunFiringTime+60){ + CWeapon gun(WEAPONTYPE_HELICANNON, 5000); + CVector source = vecHunterGunPos; + source = GetMatrix()*source + m_vecMoveSpeed*CTimer::GetTimeStep(); + gun.FireInstantHit(this, &source); + gun.AddGunshell(this, source, CVector2D(0.0f, 0.1f), 0.025f); + CStats::RoundsFiredByPlayer++; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); + m_nGunFiringTime = CTimer::GetTimeInMilliseconds(); + } + }else if(GetModelIndex() == MI_SEASPAR && GetStatus() == STATUS_PLAYER){ + // Sea sparrow gun + if(CPad::GetPad(0)->GetHandBrake() && CTimer::GetTimeInMilliseconds() > m_nGunFiringTime+40){ + CWeapon gun(WEAPONTYPE_M4, 5000); + CVector source = vecSeaSparrowGunPos; + source = GetMatrix()*source + m_vecMoveSpeed*CTimer::GetTimeStep(); + gun.FireInstantHit(this, &source); + gun.AddGunshell(this, source, CVector2D(0.0f, 0.1f), 0.025f); + CStats::RoundsFiredByPlayer++; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); + m_nGunFiringTime = CTimer::GetTimeInMilliseconds(); + } + } + + if(GetModelIndex() != MI_RCRAIDER && GetModelIndex() != MI_RCGOBLIN) + if(m_aWheelSpeed[1] < 0.154f && m_aWheelSpeed[1] > 0.0044f) + playRotorSound = true; + } + + // Play rotor sound + if(playRotorSound && m_aCarNodes[CAR_BONNET]){ + CVector camDist = TheCamera.GetPosition() - GetPosition(); + float distSq = camDist.MagnitudeSqr(); + if(distSq < SQR(20.0f) && Abs(m_fPropellerRotation - m_aWheelRotation[1]) > DEGTORAD(30.0f)){ + CMatrix mat; + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BONNET])); + CVector blade = mat.GetRight(); + blade = Multiply3x3(GetMatrix(), blade); + camDist /= Max(Sqrt(distSq), 0.01f); + if(Abs(DotProduct(camDist, blade)) > HELI_ROTOR_DOTPROD_LIMIT){ + DMAudio.PlayOneShot(m_audioEntityId, SOUND_HELI_BLADE, 0.0f); + m_fPropellerRotation = m_aWheelRotation[1]; + } + } + } + } + + + + // Process car on fire + // A similar calculation of damagePos is done elsewhere for smoke + + uint8 engineStatus = Damage.GetEngineStatus(); + CVector damagePos = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->m_positions[CAR_POS_HEADLIGHTS]; + + switch(Damage.GetDoorStatus(DOOR_BONNET)){ + case DOOR_STATUS_OK: + case DOOR_STATUS_SMASHED: + // Bonnet is still there, smoke comes out at the edge + damagePos += vecDAMAGE_ENGINE_POS_SMALL; + break; + case DOOR_STATUS_SWINGING: + case DOOR_STATUS_MISSING: + // Bonnet is gone, smoke comes out at the engine + damagePos += vecDAMAGE_ENGINE_POS_BIG; + break; + } + + // move fire forward if in first person + if(this == FindPlayerVehicle() && TheCamera.GetLookingForwardFirstPerson()) + if(m_fHealth < 250.0f && GetStatus() != STATUS_WRECKED){ + if(GetModelIndex() == MI_FIRETRUCK) + damagePos += CVector(0.0f, 3.0f, -0.2f); + else + damagePos += CVector(0.0f, 1.2f, -0.8f); + } + + damagePos = GetMatrix()*damagePos; + damagePos.z += 0.15f; + + if(m_fHealth < 250.0f && GetStatus() != STATUS_WRECKED){ + // Car is on fire + + CParticle::AddParticle(PARTICLE_CARFLAME, damagePos, + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.01125f, 0.09f)), + nil, 0.63f); + + CVector coors = damagePos; + coors.x += CGeneral::GetRandomNumberInRange(-0.5625f, 0.5625f), + coors.y += CGeneral::GetRandomNumberInRange(-0.5625f, 0.5625f), + coors.z += CGeneral::GetRandomNumberInRange(0.5625f, 2.25f); + CParticle::AddParticle(PARTICLE_CARFLAME_SMOKE, coors, CVector(0.0f, 0.0f, 0.0f)); + + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, damagePos, CVector(0.0f, 0.0f, 0.0f), nil, 0.5f); + + // Blow up car after 5 seconds + m_fFireBlowUpTimer += CTimer::GetTimeStepInMilliseconds(); + if(m_fFireBlowUpTimer > 5000.0f) + BlowUpCar(m_pSetOnFireEntity); + }else + m_fFireBlowUpTimer = 0.0f; + + // Decrease car health if engine is damaged badly + if(engineStatus > ENGINE_STATUS_ON_FIRE && m_fHealth > 250.0f) + m_fHealth -= 2.0f; + + ProcessDelayedExplosion(); + + + if(m_bSirenOrAlarm && (CTimer::GetFrameCounter()&7) == 5 && + UsesSiren() && GetModelIndex() != MI_MRWHOOP) + CCarAI::MakeWayForCarWithSiren(this); + + + // Find out how much to shake the pad depending on suspension and ground surface + + float suspShake = 0.0f; + float surfShake = 0.0f; + float speedsq = m_vecMoveSpeed.MagnitudeSqr(); + for(i = 0; i < 4; i++){ + float suspChange = m_aSuspensionSpringRatioPrev[i] - m_aSuspensionSpringRatio[i]; + if(suspChange > 0.3f && !drivingInSand && speedsq > SQR(0.2f)){ + if(Damage.GetWheelStatus(i) == WHEEL_STATUS_BURST) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_JUMP_2, suspChange); + else + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_JUMP, suspChange); + if(suspChange > suspShake) + suspShake = suspChange; + } + + uint8 surf = m_aWheelColPoints[i].surfaceB; + if(surf == SURFACE_GRAVEL || surf == SURFACE_WATER || surf == SURFACE_HEDGE){ + if(surfShake < 0.2f) + surfShake = 0.3f; + }else if(surf == SURFACE_MUD_DRY){ + if(surfShake < 0.1f) + surfShake = 0.2f; + }else if(surf == SURFACE_GRASS){ + if(surfShake < 0.05f) + surfShake = 0.1f; + } + + if(this == FindPlayerVehicle()) +// BUG: this only observes one of the wheels + TheCamera.m_bVehicleSuspenHigh = Abs(suspChange) > 0.05f; + + m_aSuspensionSpringRatioPrev[i] = m_aSuspensionSpringRatio[i]; + m_aSuspensionSpringRatio[i] = 1.0f; + } + + // Shake pad + + if(!drivingInSand && (suspShake > 0.0f || surfShake > 0.0f) && GetStatus() == STATUS_PLAYER){ + float speed = m_vecMoveSpeed.MagnitudeSqr(); + if(speed > sq(0.1f)){ + speed = Sqrt(speed); + if(suspShake > 0.0f){ + uint8 freq = Min(200.0f*suspShake*speed*2000.0f/m_fMass + 100.0f, 250.0f); + CPad::GetPad(0)->StartShake(20000.0f*CTimer::GetTimeStep()/freq, freq); + }else{ + uint8 freq = Min(200.0f*surfShake*speed*2000.0f/m_fMass + 40.0f, 150.0f); + CPad::GetPad(0)->StartShake(5000.0f*CTimer::GetTimeStep()/freq, freq); + } + } + } + + bVehicleColProcessed = false; + bAudioChangingGear = false; + + if(!bWarnedPeds && GetVehicleAppearance() != VEHICLE_APPEARANCE_HELI && GetVehicleAppearance() != VEHICLE_APPEARANCE_PLANE) + CCarCtrl::ScanForPedDanger(this); + + + // Turn around at the edge of the world + // TODO: make the numbers defines + + float heading; + if(GetPosition().x > 1950.0f-400.0f){ + if(m_vecMoveSpeed.x > 0.0f) + m_vecMoveSpeed.x *= -1.0f; + heading = GetForward().Heading(); + if(heading > 0.0f) // going west + SetHeading(-heading); + }else if(GetPosition().x < -1950.0f-400.0f){ + if(m_vecMoveSpeed.x < 0.0f) + m_vecMoveSpeed.x *= -1.0f; + heading = GetForward().Heading(); + if(heading < 0.0f) // going east + SetHeading(-heading); + } + if(GetPosition().y > 1950.0f){ + if(m_vecMoveSpeed.y > 0.0f) + m_vecMoveSpeed.y *= -1.0f; + heading = GetForward().Heading(); + if(heading < HALFPI && heading > 0.0f) + SetHeading(PI-heading); + else if(heading > -HALFPI && heading < 0.0f) + SetHeading(-PI-heading); + }else if(GetPosition().y < -1950.0f){ + if(m_vecMoveSpeed.y < 0.0f) + m_vecMoveSpeed.y *= -1.0f; + heading = GetForward().Heading(); + if(heading > HALFPI) + SetHeading(PI-heading); + else if(heading < -HALFPI) + SetHeading(-PI-heading); + } + + if(bInfiniteMass){ + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); + }else if(!skipPhysics && + (m_fGasPedal == 0.0f && brake == 0.0f || GetStatus() == STATUS_WRECKED)){ + if(Abs(m_vecMoveSpeed.x) < 0.005f && + Abs(m_vecMoveSpeed.y) < 0.005f && + Abs(m_vecMoveSpeed.z) < 0.005f && + !(m_fDamageImpulse > 0.0f && m_pDamageEntity == FindPlayerPed()) && + (m_aSuspensionSpringRatioPrev[0] < 1.0f || m_aSuspensionSpringRatioPrev[1] < 1.0f || + m_aSuspensionSpringRatioPrev[2] < 1.0f || m_aSuspensionSpringRatioPrev[3] < 1.0f)){ + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnSpeed.z = 0.0f; + } + } + + if(IsRealHeli() && bHeliDestroyed && !bRenderScorched){ + ApplyMoveForce(0.0f, 0.0f, -2.0f*CTimer::GetTimeStep()); + m_vecTurnSpeed.z += -0.002f*CTimer::GetTimeStep(); + m_vecTurnSpeed.x += -0.0002f*CTimer::GetTimeStep(); + + RwRGBA col = { 84, 84, 84, 255 }; + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, GetMatrix()*CVector(0.0f, 0.0f, -10.0f), + CVector(0.0f, 0.0f, 0.0f), nil, 0.7f, col, 0, 0, 0, 3000); + + if(CWorld::TestSphereAgainstWorld(GetPosition(), 10.0f, this, true, false, false, false, false, false) || + GetPosition().z < 6.0f) + if(!bRenderScorched){ // we already know this is true... + CExplosion::AddExplosion(this, nil, EXPLOSION_CAR, GetPosition(), 0); + bRenderScorched = true; + } + } +} + +#pragma optimize("", on) + +void +CAutomobile::Teleport(CVector pos) +{ + CWorld::Remove(this); + + SetPosition(pos); + SetOrientation(0.0f, 0.0f, 0.0f); + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + + ResetSuspension(); + + CWorld::Add(this); +} + +void +CAutomobile::PreRender(void) +{ + int i, j, n; + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + if(GetModelIndex() == MI_RHINO && m_aCarNodes[CAR_WINDSCREEN]){ + // Rotate Rhino turret + CMatrix m; + CVector p; + m.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WINDSCREEN])); + p = m.GetPosition(); + m.SetRotateZ(m_fCarGunLR); + m.Translate(p); + m.UpdateRW(); + } + + if(GetModelIndex() == MI_RCBANDIT){ + CVector pos = GetMatrix() * CVector(0.218f, -0.444f, 0.391f); + CAntennas::RegisterOne((uintptr)this, GetUp(), pos, 1.0f); + } + + float fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward())*180.0f; + + + // Wheel particles + + if(GetModelIndex() == MI_DODO || GetVehicleAppearance() != VEHICLE_APPEARANCE_CAR){ + ; // nothing + }else if(GetModelIndex() == MI_RCBANDIT){ + for(i = 0; i < 4; i++){ + // Game has same code three times here + switch(m_aWheelState[i]){ + case WHEEL_STATE_SPINNING: + case WHEEL_STATE_SKIDDING: + case WHEEL_STATE_FIXED: + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.05f), + CVector(0.0f, 0.0f, 0.0f), nil, 0.1f); + break; + default: break; + } + } + }else{ + if(GetStatus() == STATUS_SIMPLE){ + CMatrix mat; + CVector pos; + + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RB])); + pos = mat.GetPosition(); + pos.z = 1.5f*m_aWheelPosition[CARWHEEL_REAR_RIGHT]; + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].point = GetMatrix() * pos; + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceB = SURFACE_DEFAULT; + + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LB])); + pos = mat.GetPosition(); + pos.z = 1.5f*m_aWheelPosition[CARWHEEL_REAR_LEFT]; + m_aWheelColPoints[CARWHEEL_REAR_LEFT].point = GetMatrix() * pos; + m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceB = SURFACE_DEFAULT; + + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); + pos = mat.GetPosition(); + pos.z = 1.5f*m_aWheelPosition[CARWHEEL_FRONT_RIGHT]; + m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].point = GetMatrix() * pos; + m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceB = SURFACE_DEFAULT; + + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF])); + pos = mat.GetPosition(); + pos.z = 1.5f*m_aWheelPosition[CARWHEEL_FRONT_LEFT]; + m_aWheelColPoints[CARWHEEL_FRONT_LEFT].point = GetMatrix() * pos; + m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceB = SURFACE_DEFAULT; + } + + int drawParticles = Abs(fwdSpeed) < 90.0f; + if(GetStatus() == STATUS_SIMPLE || GetStatus() == STATUS_PHYSICS || + GetStatus() == STATUS_PLAYER || GetStatus() == STATUS_PLAYER_PLAYBACKFROMBUFFER){ + bool rearSkidding = false; + if(m_aWheelState[CARWHEEL_REAR_LEFT] == WHEEL_STATE_SKIDDING || + m_aWheelState[CARWHEEL_REAR_RIGHT] == WHEEL_STATE_SKIDDING) + rearSkidding = true; + + for(i = 0; i < 4; i++){ + if(m_aSuspensionSpringRatioPrev[i] < 1.0f && m_aWheelColPoints[i].surfaceB != SURFACE_WATER) + switch(m_aWheelState[i]){ + case WHEEL_STATE_SPINNING: + if(AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles)){ + CParticle::AddParticle(PARTICLE_BURNINGRUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.0f)); + + CParticle::AddParticle(PARTICLE_BURNINGRUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.05f)); + } + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.0f)); + + if(m_aWheelTimer[i] > 0.0f) + CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point, + GetForward().x, GetForward().y, + m_aWheelSkidmarkType[i], &m_aWheelSkidmarkBloody[i]); + break; + + case WHEEL_STATE_SKIDDING: + if(i == CARWHEEL_REAR_LEFT || i == CARWHEEL_REAR_RIGHT || rearSkidding){ + // same as below + + if(Abs(fwdSpeed) > 5.0f){ + AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.0f)); + } + + if(m_aWheelTimer[i] > 0.0f) + CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point, + GetForward().x, GetForward().y, + m_aWheelSkidmarkType[i], &m_aWheelSkidmarkBloody[i]); + } + break; + + case WHEEL_STATE_FIXED: + if(Abs(fwdSpeed) > 5.0f){ + AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[i].point + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.0f)); + } + + if(m_aWheelTimer[i] > 0.0f) + CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point, + GetForward().x, GetForward().y, + m_aWheelSkidmarkType[i], &m_aWheelSkidmarkBloody[i]); + break; + + default: + if(Abs(fwdSpeed) > 5.0f) + AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles); + if((m_aWheelSkidmarkBloody[i] || m_aWheelSkidmarkUnk[i]) && m_aWheelTimer[i] > 0.0f) + CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point, + GetForward().x, GetForward().y, + m_aWheelSkidmarkType[i], &m_aWheelSkidmarkBloody[i]); + } + + // Sparks for friction of burst wheels + if(Damage.GetWheelStatus(i) == WHEEL_STATUS_BURST && m_aSuspensionSpringRatioPrev[i] < 1.0f){ + static float speedSq; + speedSq = m_vecMoveSpeed.MagnitudeSqr(); + if(speedSq > SQR(0.1f) && + m_aWheelColPoints[i].surfaceB != SURFACE_GRASS && + m_aWheelColPoints[i].surfaceB != SURFACE_MUD_DRY && + m_aWheelColPoints[i].surfaceB != SURFACE_SAND && + m_aWheelColPoints[i].surfaceB != SURFACE_SAND_BEACH && + m_aWheelColPoints[i].surfaceB != SURFACE_WATER){ + CVector normalSpeed = m_aWheelColPoints[i].normal * DotProduct(m_aWheelColPoints[i].normal, m_vecMoveSpeed); + CVector frictionSpeed = m_vecMoveSpeed - normalSpeed; + if(i == CARWHEEL_FRONT_LEFT || i == CARWHEEL_REAR_LEFT) + frictionSpeed -= 0.05f*GetRight(); + else + frictionSpeed += 0.05f*GetRight(); + CVector unusedRight = 0.15f*GetRight(); + CVector sparkDir = 0.25f*frictionSpeed; + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[i].point, sparkDir); + + if(speedSq > 0.04f) + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[i].point, sparkDir); + if(speedSq > 0.16f){ + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[i].point, sparkDir); + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[i].point, sparkDir); + } + } + } + } + } + } + + if(m_aCarNodes[CAR_WHEEL_RM]){ + // assume middle wheels are two units before rear ones + CVector offset = GetForward()*2.0f; + + switch(m_aWheelState[CARWHEEL_REAR_LEFT]){ + // Game has same code three times here + case WHEEL_STATE_SPINNING: + case WHEEL_STATE_SKIDDING: + case WHEEL_STATE_FIXED: + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[CARWHEEL_REAR_LEFT].point + CVector(0.0f, 0.0f, 0.25f) + offset, + CVector(0.0f, 0.0f, 0.0f)); + + if(m_aWheelTimer[CARWHEEL_REAR_LEFT] > 0.0f) + CSkidmarks::RegisterOne((uintptr)this + 5, + m_aWheelColPoints[CARWHEEL_REAR_LEFT].point + offset, + GetForward().x, GetForward().y, + m_aWheelSkidmarkType[CARWHEEL_REAR_LEFT], &m_aWheelSkidmarkBloody[CARWHEEL_REAR_LEFT]); + break; + default: break; + } + + switch(m_aWheelState[CARWHEEL_REAR_RIGHT]){ + // Game has same code three times here + case WHEEL_STATE_SPINNING: + case WHEEL_STATE_SKIDDING: + case WHEEL_STATE_FIXED: + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].point + CVector(0.0f, 0.0f, 0.25f) + offset, + CVector(0.0f, 0.0f, 0.0f)); + + if(m_aWheelTimer[CARWHEEL_REAR_RIGHT] > 0.0f) + CSkidmarks::RegisterOne((uintptr)this + 6, + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].point + offset, + GetForward().x, GetForward().y, + m_aWheelSkidmarkType[CARWHEEL_REAR_RIGHT], &m_aWheelSkidmarkBloody[CARWHEEL_REAR_RIGHT]); + break; + default: break; + } + } + + + // Rain on roof + if(!CCullZones::CamNoRain() && !CCullZones::PlayerNoRain() && + Abs(fwdSpeed) < 20.0f && CWeather::Rain > 0.02f){ + CColModel *colModel = GetColModel(); + + for(i = 0; i < colModel->numTriangles; i++){ + CVector p1, p2, p3, c; + + colModel->GetTrianglePoint(p1, colModel->triangles[i].a); + p1 = GetMatrix() * p1; + colModel->GetTrianglePoint(p2, colModel->triangles[i].b); + p2 = GetMatrix() * p2; + colModel->GetTrianglePoint(p3, colModel->triangles[i].c); + p3 = GetMatrix() * p3; + c = (p1 + p2 + p3)/3.0f; + + n = 6.0f*CWeather::Rain; + for(j = 0; j <= n; j++) + CParticle::AddParticle(PARTICLE_RAIN_SPLASHUP, + c + CVector(CGeneral::GetRandomNumberInRange(-0.4f, 0.4f), CGeneral::GetRandomNumberInRange(-0.4f, 0.4f), 0.0f), + CVector(0.0f, 0.0f, 0.0f), + nil, 0.0f, 0, 0, CGeneral::GetRandomNumber() & 1); + } + } + + AddDamagedVehicleParticles(); + + // Exhaust smoke + if(bEngineOn && !(pHandling->Flags & HANDLING_NO_EXHAUST) && fwdSpeed < 130.0f){ + CVector exhaustPos = mi->m_positions[CAR_POS_EXHAUST]; + CVector pos1, pos2, dir1, dir2; + + if(exhaustPos != CVector(0.0f, 0.0f, 0.0f)){ + dir1.z = 0.0f; + dir2.z = 0.0f; + if(fwdSpeed < 10.0f){ + CVector steerFwd(-Sin(m_fSteerAngle), Cos(m_fSteerAngle), 0.0f); + steerFwd = Multiply3x3(GetMatrix(), steerFwd); + float r = CGeneral::GetRandomNumberInRange(-0.06f, -0.03f); + dir1.x = steerFwd.x * r; + dir1.y = steerFwd.y * r; + }else{ + dir1.x = m_vecMoveSpeed.x; + dir1.y = m_vecMoveSpeed.y; + } + + pos1 = GetMatrix() * exhaustPos; + if(pHandling->Flags & HANDLING_DBL_EXHAUST){ + pos2 = exhaustPos; + pos2.x = -pos2.x; + pos2 = GetMatrix() * pos2; + dir2 = dir1; + } + + static float fumesLimit = 2.0f; + if(CGeneral::GetRandomNumberInRange(1.0f, 3.0f)*(m_fGasPedal+1.1f) > fumesLimit) + for(i = 0; i < 4;){ + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir1); + if(pHandling->Flags & HANDLING_DBL_EXHAUST) + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos2, dir2); + + static float extraFumesLimit = 0.5f; + if(m_fGasPedal > extraFumesLimit && m_nCurrentGear < 3){ + if(CGeneral::GetRandomNumber() & 1) + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir1); + else if(pHandling->Flags & HANDLING_DBL_EXHAUST) + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos2, dir2); + } + + // Fire on Cuban hermes + if(GetModelIndex() == MI_CUBAN && i == 1 && m_fGasPedal > 0.9f){ + if(m_nCurrentGear == 1 || m_nCurrentGear == 3 && (CTimer::GetTimeInMilliseconds()%1500) > 750){ + if(CGeneral::GetRandomNumber() & 1){ + CParticle::AddParticle(PARTICLE_FIREBALL, pos1, dir1, nil, 0.05f, 0, 0, 2, 200); + CParticle::AddParticle(PARTICLE_FIREBALL, pos1, dir1, nil, 0.05f, 0, 0, 2, 200); + }else{ + CParticle::AddParticle(PARTICLE_FIREBALL, pos2, dir2, nil, 0.05f, 0, 0, 2, 200); + CParticle::AddParticle(PARTICLE_FIREBALL, pos2, dir2, nil, 0.05f, 0, 0, 2, 200); + } + } + } + + if(GetStatus() == STATUS_PLAYER && (CTimer::GetFrameCounter()&3) == 0 && + CWeather::Rain == 0.0f && i == 0){ + CVector camDist = GetPosition() - TheCamera.GetPosition(); + if(DotProduct(GetForward(), camDist) > 0.0f || + TheCamera.GetLookDirection() == LOOKING_LEFT || + TheCamera.GetLookDirection() == LOOKING_RIGHT){ + CParticle::AddParticle(PARTICLE_HEATHAZE, pos1, CVector(0.0f, 0.0f, 0.0f)); + if(pHandling->Flags & HANDLING_DBL_EXHAUST) + CParticle::AddParticle(PARTICLE_HEATHAZE, pos2, CVector(0.0f, 0.0f, 0.0f)); + + CParticle::AddParticle(PARTICLE_HEATHAZE, pos1, CVector(0.0f, 0.0f, 0.0f)); + if(pHandling->Flags & HANDLING_DBL_EXHAUST) + CParticle::AddParticle(PARTICLE_HEATHAZE, pos2, CVector(0.0f, 0.0f, 0.0f)); + } + } + + if(GetModelIndex() == MI_CUBAN && i < 1){ + i = 1; + pos1 = GetMatrix() * CVector(1.134f, -1.276f, -0.56f); + pos2 = GetMatrix() * CVector(-1.134f, -1.276f, -0.56f); + dir1 += 0.05f*GetRight(); + dir2 -= 0.05f*GetRight(); + }else + i = 99; + } + } + } + + + // Siren and taxi lights + switch(GetModelIndex()){ + case MI_FIRETRUCK: + case MI_AMBULAN: + case MI_POLICE: + case MI_ENFORCER: + if(m_bSirenOrAlarm){ + CVector pos1, pos2; + uint8 r1, g1, b1; + uint8 r2, g2, b2; + uint8 r, g, b; + + switch(GetModelIndex()){ + case MI_FIRETRUCK: + pos1 = CVector(1.1f, 1.7f, 2.0f); + pos2 = CVector(-1.1f, 1.7f, 2.0f); + r1 = 255; g1 = 0; b1 = 0; + r2 = 255; g2 = 255; b2 = 0; + break; + case MI_AMBULAN: + pos1 = CVector(1.1f, 0.9f, 1.6f); + pos2 = CVector(-1.1f, 0.9f, 1.6f); + r1 = 255; g1 = 0; b1 = 0; + r2 = 255; g2 = 255; b2 = 255; + break; + case MI_POLICE: + pos1 = CVector(0.7f, -0.4f, 1.0f); + pos2 = CVector(-0.7f, -0.4f, 1.0f); + r1 = 255; g1 = 0; b1 = 0; + r2 = 0; g2 = 0; b2 = 255; + break; + case MI_ENFORCER: + pos1 = CVector(1.1f, 0.8f, 1.2f); + pos2 = CVector(-1.1f, 0.8f, 1.2f); + r1 = 255; g1 = 0; b1 = 0; + r2 = 0; g2 = 0; b2 = 255; + break; + } + + uint32 t = CTimer::GetTimeInMilliseconds() & 0x3FF; // 1023 + if(t < 512){ + r = r1/6; + g = g1/6; + b = b1/6; + }else{ + r = r2/6; + g = g2/6; + b = b2/6; + } + + t = CTimer::GetTimeInMilliseconds() & 0x1FF; // 511 + if(t < 100){ + float f = t/100.0f; + r *= f; + g *= f; + b *= f; + }else if(t > (512-100)){ + float f = (512-t)/100.0f; + r *= f; + g *= f; + b *= f; + } + + CVector pos = GetPosition(); + float angle = (CTimer::GetTimeInMilliseconds() & 0x3FF)*TWOPI/0x3FF; + float s = 8.0f*Sin(angle); + float c = 8.0f*Cos(angle); + //CShadows::StoreCarLightShadow(this, (uintptr)this + 21, gpShadowHeadLightsTex, + // &pos, c, s, s, -c, r, g, b, 8.0f); + + CPointLights::AddLight(CPointLights::LIGHT_POINT, + pos + GetUp()*2.0f, CVector(0.0f, 0.0f, 0.0f), 12.0f, + r*0.02f, g*0.02f, b*0.02f, CPointLights::FOG_NONE, true); + + pos1 = GetMatrix() * pos1; + pos2 = GetMatrix() * pos2; + + for(i = 0; i < 4; i++){ + uint8 sirenTimer = ((CTimer::GetTimeInMilliseconds() + (i<<6))>>8) & 3; + pos = (pos1*i + pos2*(3.0f-i))/3.0f; + + switch(sirenTimer){ + case 0: + CCoronas::RegisterCorona((uintptr)this + 21 + i, + r1, g1, b1, 255, + pos, 0.4f, 50.0f, + CCoronas::TYPE_STAR, + i == 1 ? CCoronas::FLARE_HEADLIGHTS : CCoronas::FLARE_NONE, + CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + break; + case 2: + CCoronas::RegisterCorona((uintptr)this + 21 + i, + r2, g2, b2, 255, + pos, 0.4f, 50.0f, + CCoronas::TYPE_STAR, + CCoronas::FLARE_NONE, + CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + break; + default: + CCoronas::UpdateCoronaCoors((uintptr)this + 21 + i, pos, 50.0f, 0.0f); + break; + } + } + } + break; + + case MI_FBIRANCH: + case MI_VICECHEE: + if(m_bSirenOrAlarm){ + CVector pos = GetMatrix() * CVector(0.4f, 0.6f, 0.3f); + if(CTimer::GetTimeInMilliseconds() & 0x100 && + DotProduct(GetForward(), GetPosition() - TheCamera.GetPosition()) < 0.0f) + if(GetModelIndex() == MI_VICECHEE) + CCoronas::RegisterCorona((uintptr)this + 21, + 255, 70, 70, 255, + pos, 0.4f, 50.0f, + CCoronas::TYPE_STAR, + CCoronas::FLARE_NONE, + CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + else + CCoronas::RegisterCorona((uintptr)this + 21, + 0, 0, 255, 255, + pos, 0.4f, 50.0f, + CCoronas::TYPE_STAR, + CCoronas::FLARE_NONE, + CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + else + CCoronas::UpdateCoronaCoors((uintptr)this + 21, pos, 50.0f, 0.0f); + } + break; + + case MI_TAXI: + case MI_CABBIE: + case MI_ZEBRA: + case MI_KAUFMAN: + if(bTaxiLight){ + CVector pos = GetPosition() + GetUp()*0.95f; + CCoronas::RegisterCorona((uintptr)this + 21, + 128, 128, 0, 255, + pos, 0.8f, 50.0f, + CCoronas::TYPE_NORMAL, + CCoronas::FLARE_NONE, + CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + CPointLights::AddLight(CPointLights::LIGHT_POINT, + pos, CVector(0.0f, 0.0f, 0.0f), 10.0f, + 1.0f, 1.0f, 0.5f, CPointLights::FOG_NONE, true); + } + break; + } + + if(GetModelIndex() != MI_RCBANDIT && GetModelIndex() != MI_DODO && + GetModelIndex() != MI_RHINO && GetModelIndex() != MI_RCBARON && + GetVehicleAppearance() != VEHICLE_APPEARANCE_HELI) { + // Process lights + + // Turn lights on/off + bool shouldLightsBeOn = + CClock::GetHours() > 20 || + CClock::GetHours() > 19 && CClock::GetMinutes() > (m_randomSeed & 0x3F) || + CClock::GetHours() < 7 || + CClock::GetHours() < 8 && CClock::GetMinutes() < (m_randomSeed & 0x3F) || + m_randomSeed/50000.0f < CWeather::Foggyness || + m_randomSeed/50000.0f < CWeather::WetRoads; + if(shouldLightsBeOn != bLightsOn && GetStatus() != STATUS_WRECKED){ + if(GetStatus() == STATUS_ABANDONED){ + // Turn off lights on abandoned vehicles only when we they're far away + if(bLightsOn && + Abs(TheCamera.GetPosition().x - GetPosition().x) + Abs(TheCamera.GetPosition().y - GetPosition().y) > 100.0f) + bLightsOn = false; + }else + bLightsOn = shouldLightsBeOn; + } + + // Actually render the lights + bool alarmOn = false; + bool alarmOff = false; + if(IsAlarmOn()){ + if(CTimer::GetTimeInMilliseconds() & 0x100) + alarmOn = true; + else + alarmOff = true; + } + if(bEngineOn && bLightsOn || alarmOn || alarmOff){ + CVector lookVector = GetPosition() - TheCamera.GetPosition(); + float camDist = lookVector.Magnitude(); + if(camDist != 0.0f) + lookVector *= 1.0f/camDist; + else + lookVector = CVector(1.0f, 0.0f, 0.0f); + + // 1.0 if directly behind car, -1.0 if in front + float behindness = DotProduct(lookVector, GetForward()); + behindness = Clamp(behindness, -1.0f, 1.0f); // shouldn't be necessary + // 0.0 if behind car, PI if in front + // Abs not necessary + float angle = Abs(Acos(behindness)); + + // Headlights + + CVector headLightPos = mi->m_positions[CAR_POS_HEADLIGHTS]; + CVector lightR = GetMatrix() * headLightPos; + CVector lightL = lightR; + lightL -= GetRight()*2.0f*headLightPos.x; + + // Headlight coronas + if(DotProduct(lightR-TheCamera.GetPosition(), GetForward()) < 0.0f && + (TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_1STPERSON || this != FindPlayerVehicle())){ + // In front of car + float intensity = -0.5f*behindness + 0.3f; + float size = 1.0f - behindness; + + if(behindness < -0.97f && camDist < 30.0f){ + // Directly in front and not too far away + if(pHandling->Flags & HANDLING_HALOGEN_LIGHTS){ + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 6, 150, 150, 195, 255, + lightL, 1.2f, 45.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 7, 150, 150, 195, 255, + lightR, 1.2f, 45.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle); + }else{ + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 6, 160, 160, 140, 255, + lightL, 1.2f, 45.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 7, 160, 160, 140, 255, + lightR, 1.2f, 45.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle); + } + } + + if(alarmOff){ + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this, 0, 0, 0, 0, + lightL, size, 0.0f, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 1, 0, 0, 0, 0, + lightR, size, 0.0f, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + }else{ + if(pHandling->Flags & HANDLING_HALOGEN_LIGHTS){ + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this, 190*intensity, 190*intensity, 255*intensity, 255, + lightL, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 1, 190*intensity, 190*intensity, 255*intensity, 255, + lightR, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + }else{ + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this, 210*intensity, 210*intensity, 195*intensity, 255, + lightL, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 1, 210*intensity, 210*intensity, 195*intensity, 255, + lightR, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + } + } + }else{ + // Behind car + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this, lightL, 50.0f*TheCamera.LODDistMultiplier, angle); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 1, lightR, 50.0f*TheCamera.LODDistMultiplier, angle); + } + + // bright lights + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK && !bNoBrightHeadLights) + CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->FrontLights + BRIGHTLIGHT_FRONT); + if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK && !bNoBrightHeadLights) + CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->FrontLights + BRIGHTLIGHT_FRONT); + + // Taillights + + CVector tailLightPos = mi->m_positions[CAR_POS_TAILLIGHTS]; + lightR = GetMatrix() * tailLightPos; + lightL = lightR; + lightL -= GetRight()*2.0f*tailLightPos.x; + + // Taillight coronas + if(DotProduct(lightR-TheCamera.GetPosition(), GetForward()) > 0.0f){ + // Behind car + float intensity = (behindness + 1.0f)*0.4f; + float size = (behindness + 1.0f)*0.5f; + + if(m_fGasPedal < 0.0f){ + // reversing + intensity += 0.4f; + size += 0.3f; + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 14, 128*intensity, 128*intensity, 128*intensity, 255, + lightL, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 15, 128*intensity, 128*intensity, 128*intensity, 255, + lightR, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + }else{ + if(m_fBrakePedal > 0.0f){ + intensity += 0.4f; + size += 0.3f; + } + + if(alarmOff){ + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 14, 0, 0, 0, 0, + lightL, size, 0.0f, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 15, 0, 0, 0, 0, + lightR, size, 0.0f, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + }else{ + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 14, 128*intensity, 0, 0, 255, + lightL, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 15, 128*intensity, 0, 0, 255, + lightR, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + } + } + }else{ + // In front of car + // missing LODDistMultiplier probably a BUG + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 14, lightL, 50.0f, angle); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 15, lightR, 50.0f, angle); + } + + // bright lights + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_REAR); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_REAR); + + // Light shadows + if(!alarmOff){ + CVector pos = GetPosition(); + CVector2D fwd(GetForward()); + fwd.Normalise(); + float f = headLightPos.y + 6.0f; + pos += CVector(f*fwd.x, f*fwd.y, 2.0f); + + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK || + Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CShadows::StoreCarLightShadow(this, (uintptr)this + 22, gpShadowHeadLightsTex, &pos, + 7.0f*fwd.x, 7.0f*fwd.y, 5.5f*fwd.y, -5.5f*fwd.x, 45, 45, 45, 7.0f); + + f = (tailLightPos.y - 2.5f) - (headLightPos.y + 6.0f); + pos += CVector(f*fwd.x, f*fwd.y, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK || + Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CShadows::StoreCarLightShadow(this, (uintptr)this + 25, gpShadowExplosionTex, &pos, + 3.0f, 0.0f, 0.0f, -3.0f, 35, 0, 0, 4.0f); + } + + if(this == FindPlayerVehicle() && !alarmOff){ + if(Damage.GetLightStatus(VEHLIGHT_FRONT_LEFT) == LIGHT_STATUS_OK || + Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK) + CPointLights::AddLight(CPointLights::LIGHT_DIRECTIONAL, GetPosition(), GetForward(), + 20.0f, 1.0f, 1.0f, 1.0f, + FindPlayerVehicle()->m_vecMoveSpeed.MagnitudeSqr2D() < sq(0.45f) ? CPointLights::FOG_NORMAL : CPointLights::FOG_NONE, + false); + CVector pos = GetPosition() - 4.0f*GetForward(); + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK || + Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) { + if(m_fBrakePedal > 0.0f) + CPointLights::AddLight(CPointLights::LIGHT_POINT, pos, CVector(0.0f, 0.0f, 0.0f), + 10.0f, 1.0f, 0.0f, 0.0f, + CPointLights::FOG_NONE, false); + else + CPointLights::AddLight(CPointLights::LIGHT_POINT, pos, CVector(0.0f, 0.0f, 0.0f), + 7.0f, 0.6f, 0.0f, 0.0f, + CPointLights::FOG_NONE, false); + } + } + }else if(GetStatus() != STATUS_ABANDONED && GetStatus() != STATUS_WRECKED){ + // Lights off + + CVector lightPos = mi->m_positions[CAR_POS_TAILLIGHTS]; + CVector lightR = GetMatrix() * lightPos; + CVector lightL = lightR; + lightL -= GetRight()*2.0f*lightPos.x; + + if(m_fBrakePedal > 0.0f || m_fGasPedal < 0.0f){ + CVector lookVector = GetPosition() - TheCamera.GetPosition(); + lookVector.Normalise(); + float behindness = DotProduct(lookVector, GetForward()); + if(behindness > 0.0f){ + if(m_fGasPedal < 0.0f){ + // reversing + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 14, 120, 120, 120, 255, + lightL, 1.2f, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 15, 120, 120, 120, 255, + lightR, 1.2f, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_FRONT); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_FRONT); + }else{ + // braking + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 14, 120, 0, 0, 255, + lightL, 1.2f, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::RegisterCorona((uintptr)this + 15, 120, 0, 0, 255, + lightR, 1.2f, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CBrightLights::RegisterOne(lightL, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_REAR); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_REAR); + } + }else{ + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 14, lightL, 50.0f*TheCamera.LODDistMultiplier, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 15, lightR, 50.0f*TheCamera.LODDistMultiplier, 0.0f); + } + }else{ + if(Damage.GetLightStatus(VEHLIGHT_REAR_LEFT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 14, lightL, 50.0f*TheCamera.LODDistMultiplier, 0.0f); + if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) + CCoronas::UpdateCoronaCoors((uintptr)this + 15, lightR, 50.0f*TheCamera.LODDistMultiplier, 0.0f); + } + } + // end of lights + } + + if (IsRealHeli()) + CShadows::StoreShadowForVehicle(this, VEH_SHD_TYPE_HELI); + else if ( GetModelIndex() == MI_RCBARON) + CShadows::StoreShadowForVehicle(this, VEH_SHD_TYPE_RCPLANE); + else + CShadows::StoreShadowForVehicle(this, VEH_SHD_TYPE_CAR); + + DoSunGlare(); + + // Heli dust + if(IsRealHeli() && m_aWheelSpeed[1] > 0.1125f && GetPosition().z < 30.0f){ + bool foundGround = false; + float waterZ = -1000.0f; + float groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, GetPosition().z, &foundGround); + if(!CWaterLevel::GetWaterLevel(GetPosition(), &waterZ, false)) + waterZ = 0.0f; + groundZ = Max(groundZ, waterZ); + float rnd = (m_aWheelSpeed[1]-0.1125f)*((int)Max(16.0f-4.0f*CTimer::GetTimeStep(),2.0f))*400.0f/43.0f; + float radius = 10.0f; + if(GetModelIndex() == MI_RCGOBLIN || GetModelIndex() == MI_RCRAIDER) + radius = 3.0f; + if(GetPosition().z - groundZ < radius) + HeliDustGenerate(this, radius-(GetPosition().z - groundZ), groundZ, Ceil(rnd)); + } + + CMatrix mat; + CVector pos; + + bool onlyFrontWheels = false; + if(IsRealHeli()){ + // top rotor + m_aWheelRotation[1] += m_aWheelSpeed[1]*CTimer::GetTimeStep(); + while(m_aWheelRotation[1] > TWOPI) m_aWheelRotation[1] -= TWOPI; + // rear rotor + m_aWheelRotation[3] += m_aWheelSpeed[1]*CTimer::GetTimeStep(); + while(m_aWheelRotation[3] > TWOPI) m_aWheelRotation[3] -= TWOPI; + onlyFrontWheels = true; + } + + CVector contactPoints[4]; // relative to model + CVector contactSpeeds[4]; // speed at contact points + CVector frontWheelFwd = Multiply3x3(GetMatrix(), CVector(-Sin(m_fSteerAngle), Cos(m_fSteerAngle), 0.0f)); + CVector rearWheelFwd = GetForward(); + for(i = 0; i < 4; i++){ + if (m_aWheelTimer[i] > 0.0f && (!onlyFrontWheels || i == CARWHEEL_FRONT_LEFT || i == CARWHEEL_FRONT_RIGHT)) { + contactPoints[i] = m_aWheelColPoints[i].point - GetPosition(); + contactSpeeds[i] = GetSpeed(contactPoints[i]); + if (i == CARWHEEL_FRONT_LEFT || i == CARWHEEL_FRONT_RIGHT) + m_aWheelSpeed[i] = ProcessWheelRotation(m_aWheelState[i], frontWheelFwd, contactSpeeds[i], 0.5f*mi->m_wheelScale); + else + m_aWheelSpeed[i] = ProcessWheelRotation(m_aWheelState[i], rearWheelFwd, contactSpeeds[i], 0.5f*mi->m_wheelScale); + m_aWheelRotation[i] += m_aWheelSpeed[i]; + } + } + + RwRGBA hoverParticleCol = { 255, 255, 255, 32 }; + + // Rear right wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RB])); + pos = mat.GetPosition(); + pos.z = m_aWheelPosition[CARWHEEL_REAR_RIGHT]; + if(Damage.GetWheelStatus(CARWHEEL_REAR_RIGHT) == WHEEL_STATUS_BURST) + mat.SetRotate(m_aWheelRotation[CARWHEEL_REAR_RIGHT], 0.0f, 0.3f*Sin(m_aWheelRotation[CARWHEEL_REAR_RIGHT])); + else + mat.SetRotateX(m_aWheelRotation[CARWHEEL_REAR_RIGHT]); + if(GetStatus() == STATUS_PLAYER){ + if(bHoverCheat && m_aSuspensionSpringRatioPrev[CARWHEEL_REAR_RIGHT] < 1.0f && + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceB == SURFACE_WATER){ + // hovering on water + mat.RotateY(-HALFPI); + if((CTimer::GetFrameCounter()+CARWHEEL_REAR_RIGHT) & 1){ + CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, m_aWheelColPoints[CARWHEEL_REAR_RIGHT].point, + 0.5f*m_vecMoveSpeed+0.1f*GetRight(), nil, 0.4f, hoverParticleCol); + }else{ + CParticle::AddParticle(PARTICLE_CAR_SPLASH, m_aWheelColPoints[CARWHEEL_REAR_RIGHT].point, + 0.3f*m_vecMoveSpeed+0.15f*GetRight()+CVector(0.0f, 0.0f, 0.1f), nil, 0.15f, hoverParticleCol, + CGeneral::GetRandomNumberInRange(0.0f, 10.0f), + CGeneral::GetRandomNumberInRange(0.0f, 90.0f), 1); + } +#ifdef BETTER_ALLCARSAREDODO_CHEAT + } else if (bAllDodosCheat && m_nDriveWheelsOnGround == 0 && m_nDriveWheelsOnGroundPrev == 0) { + mat.RotateY(-HALFPI); +#endif + }else{ + // tilt wheel depending oh how much it presses on ground + float groundOffset = pos.z + m_fHeightAboveRoad - 0.5f*mi->m_wheelScale; + if(GetModelIndex() == MI_VOODOO) + groundOffset *= 0.6f; + mat.RotateY(Asin(Clamp(-groundOffset, -1.0f, 1.0f))); + } + } + if(pHandling->Flags & HANDLING_FAT_REARW) + mat.Scale(1.15f*mi->m_wheelScale, mi->m_wheelScale, mi->m_wheelScale); + else + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + + // Rear left wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LB])); + pos = mat.GetPosition(); + pos.z = m_aWheelPosition[CARWHEEL_REAR_LEFT]; + if(Damage.GetWheelStatus(CARWHEEL_REAR_LEFT) == WHEEL_STATUS_BURST) + mat.SetRotate(-m_aWheelRotation[CARWHEEL_REAR_LEFT], 0.0f, PI+0.3f*Sin(m_aWheelRotation[CARWHEEL_REAR_LEFT])); + else + mat.SetRotate(-m_aWheelRotation[CARWHEEL_REAR_LEFT], 0.0f, PI); + if(GetStatus() == STATUS_PLAYER){ + if(bHoverCheat && m_aSuspensionSpringRatioPrev[CARWHEEL_REAR_LEFT] < 1.0f && + m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceB == SURFACE_WATER){ + // hovering on water + mat.RotateY(HALFPI); + if((CTimer::GetFrameCounter()+CARWHEEL_REAR_LEFT) & 1){ + CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, m_aWheelColPoints[CARWHEEL_REAR_LEFT].point, + 0.5f*m_vecMoveSpeed-0.1f*GetRight(), nil, 0.4f, hoverParticleCol); + }else{ + CParticle::AddParticle(PARTICLE_CAR_SPLASH, m_aWheelColPoints[CARWHEEL_REAR_LEFT].point, + 0.3f*m_vecMoveSpeed-0.15f*GetRight()+CVector(0.0f, 0.0f, 0.1f), nil, 0.15f, hoverParticleCol, + CGeneral::GetRandomNumberInRange(0.0f, 10.0f), + CGeneral::GetRandomNumberInRange(0.0f, 90.0f), 1); + } +#ifdef BETTER_ALLCARSAREDODO_CHEAT + } else if (bAllDodosCheat && m_nDriveWheelsOnGround == 0 && m_nDriveWheelsOnGroundPrev == 0) { + mat.RotateY(HALFPI); +#endif + }else{ + // tilt wheel depending oh how much it presses on ground + float groundOffset = pos.z + m_fHeightAboveRoad - 0.5f*mi->m_wheelScale; + if(GetModelIndex() == MI_VOODOO) + groundOffset *= 0.6f; + mat.RotateY(Asin(Clamp(groundOffset, -1.0f, 1.0f))); + } + } + if(pHandling->Flags & HANDLING_FAT_REARW) + mat.Scale(1.15f*mi->m_wheelScale, mi->m_wheelScale, mi->m_wheelScale); + else + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + + // Mid right wheel + if(m_aCarNodes[CAR_WHEEL_RM]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RM])); + pos = mat.GetPosition(); + pos.z = m_aWheelPosition[CARWHEEL_REAR_RIGHT]; + if(Damage.GetWheelStatus(CARWHEEL_REAR_RIGHT) == WHEEL_STATUS_BURST) + mat.SetRotate(m_aWheelRotation[CARWHEEL_REAR_RIGHT], 0.0f, 0.3f*Sin(m_aWheelRotation[CARWHEEL_REAR_RIGHT])); + else + mat.SetRotateX(m_aWheelRotation[CARWHEEL_REAR_RIGHT]); + if(GetStatus() == STATUS_PLAYER){ + if(bHoverCheat && m_aSuspensionSpringRatioPrev[CARWHEEL_REAR_RIGHT] < 1.0f && + m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceB == SURFACE_WATER){ + // hovering on water + mat.RotateY(-HALFPI); +#ifdef BETTER_ALLCARSAREDODO_CHEAT + } else if (bAllDodosCheat && m_nDriveWheelsOnGround == 0 && m_nDriveWheelsOnGroundPrev == 0) { + mat.RotateY(-HALFPI); +#endif + }else{ + // tilt wheel depending oh how much it presses on ground + float groundOffset = pos.z + m_fHeightAboveRoad - 0.5f*mi->m_wheelScale; + if(GetModelIndex() == MI_VOODOO) + groundOffset *= 0.6f; + mat.RotateY(Asin(Clamp(-groundOffset, -1.0f, 1.0f))); + } + } + if(pHandling->Flags & HANDLING_FAT_REARW) + mat.Scale(1.15f*mi->m_wheelScale, mi->m_wheelScale, mi->m_wheelScale); + else + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + } + + // Mid left wheel + if(m_aCarNodes[CAR_WHEEL_LM]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LM])); + pos = mat.GetPosition(); + pos.z = m_aWheelPosition[CARWHEEL_REAR_LEFT]; + if(Damage.GetWheelStatus(CARWHEEL_REAR_LEFT) == WHEEL_STATUS_BURST) + mat.SetRotate(-m_aWheelRotation[CARWHEEL_REAR_LEFT], 0.0f, PI+0.3f*Sin(m_aWheelRotation[CARWHEEL_REAR_LEFT])); + else + mat.SetRotate(-m_aWheelRotation[CARWHEEL_REAR_LEFT], 0.0f, PI); + if(GetStatus() == STATUS_PLAYER){ + if(bHoverCheat && m_aSuspensionSpringRatioPrev[CARWHEEL_REAR_LEFT] < 1.0f && + m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceB == SURFACE_WATER){ + // hovering on water + mat.RotateY(HALFPI); +#ifdef BETTER_ALLCARSAREDODO_CHEAT + } else if (bAllDodosCheat && m_nDriveWheelsOnGround == 0 && m_nDriveWheelsOnGroundPrev == 0) { + mat.RotateY(HALFPI); +#endif + }else{ + // tilt wheel depending oh how much it presses on ground + float groundOffset = pos.z + m_fHeightAboveRoad - 0.5f*mi->m_wheelScale; + if(GetModelIndex() == MI_VOODOO) + groundOffset *= 0.6f; + mat.RotateY(Asin(Clamp(groundOffset, -1.0f, 1.0f))); + } + } + if(pHandling->Flags & HANDLING_FAT_REARW) + mat.Scale(1.15f*mi->m_wheelScale, mi->m_wheelScale, mi->m_wheelScale); + else + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + } + + if(GetModelIndex() == MI_DODO){ + // Front wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); + pos = mat.GetPosition(); + pos.z = m_aWheelPosition[CARWHEEL_FRONT_RIGHT]; + if(Damage.GetWheelStatus(CARWHEEL_FRONT_RIGHT) == WHEEL_STATUS_BURST) + mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, m_fSteerAngle+0.3f*Sin(m_aWheelRotation[CARWHEEL_FRONT_RIGHT])); + else + mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, m_fSteerAngle); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + + // Rotate propeller + if(m_aCarNodes[CAR_WINDSCREEN]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WINDSCREEN])); + pos = mat.GetPosition(); + mat.SetRotateY(m_fPropellerRotation); + mat.Translate(pos); + mat.UpdateRW(); + + m_fPropellerRotation += m_fGasPedal != 0.0f ? TWOPI/13.0f : TWOPI/26.0f; + if(m_fPropellerRotation > TWOPI) + m_fPropellerRotation -= TWOPI; + } + + // Rudder + if(Damage.GetDoorStatus(DOOR_BOOT) != DOOR_STATUS_MISSING && m_aCarNodes[CAR_BOOT]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BOOT])); + pos = mat.GetPosition(); + mat.SetRotate(0.0f, 0.0f, -m_fSteerAngle); + mat.Rotate(0.0f, Sin(m_fSteerAngle)*DEGTORAD(22.0f), 0.0f); + mat.Translate(pos); + mat.UpdateRW(); + } + + ProcessSwingingDoor(CAR_DOOR_LF, DOOR_FRONT_LEFT); + ProcessSwingingDoor(CAR_DOOR_RF, DOOR_FRONT_RIGHT); + }else if(GetModelIndex() == MI_RHINO){ + // Front right wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); + pos = mat.GetPosition(); + pos.z = m_aWheelPosition[CARWHEEL_FRONT_RIGHT]; + // no damaged wheels or steering + mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, 0.0f); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + + // Front left wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF])); + pos = mat.GetPosition(); + pos.z = m_aWheelPosition[CARWHEEL_FRONT_LEFT]; + // no damaged wheels or steering + mat.SetRotate(-m_aWheelRotation[CARWHEEL_FRONT_LEFT], 0.0f, PI); + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + }else if(IsRealHeli()){ + // Top rotor + if(m_aCarNodes[CAR_BONNET]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BONNET])); + pos = mat.GetPosition(); + mat.SetRotateZ(m_aWheelRotation[1]); + mat.Translate(pos); + mat.UpdateRW(); + } + // Blurred top rotor + if(m_aCarNodes[CAR_WINDSCREEN]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WINDSCREEN])); + pos = mat.GetPosition(); + mat.SetRotateZ(-m_aWheelRotation[1]); + mat.Translate(pos); + mat.UpdateRW(); + } + // Rear rotor + if(m_aCarNodes[CAR_BOOT]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BOOT])); + pos = mat.GetPosition(); + mat.SetRotateX(m_aWheelRotation[3]); + mat.Translate(pos); + mat.UpdateRW(); + } + // Blurred rear rotor + if(m_aCarNodes[CAR_BUMP_REAR]){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BUMP_REAR])); + pos = mat.GetPosition(); + mat.SetRotateX(-m_aWheelRotation[3]); + mat.Translate(pos); + mat.UpdateRW(); + } + }else{ + // Front right wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); + pos = mat.GetPosition(); + pos.z = m_aWheelPosition[CARWHEEL_FRONT_RIGHT]; + if(Damage.GetWheelStatus(CARWHEEL_FRONT_RIGHT) == WHEEL_STATUS_BURST) + mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, m_fSteerAngle+0.3f*Sin(m_aWheelRotation[CARWHEEL_FRONT_RIGHT])); + else + mat.SetRotate(m_aWheelRotation[CARWHEEL_FRONT_RIGHT], 0.0f, m_fSteerAngle); + if(GetStatus() == STATUS_PLAYER){ + if(bHoverCheat && m_aSuspensionSpringRatioPrev[CARWHEEL_FRONT_RIGHT] < 1.0f && + m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceB == SURFACE_WATER){ + // hovering on water + mat.RotateY(-HALFPI); + if((CTimer::GetFrameCounter()+CARWHEEL_FRONT_RIGHT) & 1){ + CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].point, + 0.5f*m_vecMoveSpeed+0.1f*GetRight(), nil, 0.4f, hoverParticleCol); + }else{ + CParticle::AddParticle(PARTICLE_CAR_SPLASH, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].point, + 0.3f*m_vecMoveSpeed+0.15f*GetRight()+CVector(0.0f, 0.0f, 0.1f), nil, 0.15f, hoverParticleCol, + CGeneral::GetRandomNumberInRange(0.0f, 90.0f), + CGeneral::GetRandomNumberInRange(0.0f, 10.0f), 1); + } +#ifdef BETTER_ALLCARSAREDODO_CHEAT + } else if (bAllDodosCheat && m_nDriveWheelsOnGround == 0 && m_nDriveWheelsOnGroundPrev == 0) { + mat.RotateY(-HALFPI); +#endif + }else{ + // tilt wheel depending oh how much it presses on ground + float groundOffset = pos.z + m_fHeightAboveRoad - 0.5f*mi->m_wheelScale; + if(GetModelIndex() == MI_VOODOO) + groundOffset *= 0.6f; + mat.RotateY(Asin(Clamp(-groundOffset, -1.0f, 1.0f))); + } + } + if(pHandling->Flags & HANDLING_NARROW_FRONTW) + mat.Scale(0.7f*mi->m_wheelScale, mi->m_wheelScale, mi->m_wheelScale); + else + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + + // Front left wheel + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF])); + pos = mat.GetPosition(); + pos.z = m_aWheelPosition[CARWHEEL_FRONT_LEFT]; + if(Damage.GetWheelStatus(CARWHEEL_FRONT_LEFT) == WHEEL_STATUS_BURST) + mat.SetRotate(-m_aWheelRotation[CARWHEEL_FRONT_LEFT], 0.0f, PI+m_fSteerAngle+0.3f*Sin(m_aWheelRotation[CARWHEEL_FRONT_LEFT])); + else + mat.SetRotate(-m_aWheelRotation[CARWHEEL_FRONT_LEFT], 0.0f, PI+m_fSteerAngle); + if(GetStatus() == STATUS_PLAYER){ + if(bHoverCheat && m_aSuspensionSpringRatioPrev[CARWHEEL_FRONT_LEFT] < 1.0f && + m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceB == SURFACE_WATER){ + // hovering on water + mat.RotateY(HALFPI); + if((CTimer::GetFrameCounter()+CARWHEEL_FRONT_LEFT) & 1){ + CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].point, + 0.5f*m_vecMoveSpeed-0.1f*GetRight(), nil, 0.4f, hoverParticleCol); + }else{ + CParticle::AddParticle(PARTICLE_CAR_SPLASH, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].point, + 0.3f*m_vecMoveSpeed-0.15f*GetRight()+CVector(0.0f, 0.0f, 0.1f), nil, 0.15f, hoverParticleCol, + CGeneral::GetRandomNumberInRange(0.0f, 90.0f), + CGeneral::GetRandomNumberInRange(0.0f, 10.0f), 1); + } +#ifdef BETTER_ALLCARSAREDODO_CHEAT + } else if (bAllDodosCheat && m_nDriveWheelsOnGround == 0 && m_nDriveWheelsOnGroundPrev == 0) { + mat.RotateY(HALFPI); +#endif + }else{ + // tilt wheel depending oh how much it presses on ground + float groundOffset = pos.z + m_fHeightAboveRoad - 0.5f*mi->m_wheelScale; + if(GetModelIndex() == MI_VOODOO) + groundOffset *= 0.6f; + mat.RotateY(Asin(Clamp(groundOffset, -1.0f, 1.0f))); + } + } + if(pHandling->Flags & HANDLING_NARROW_FRONTW) + mat.Scale(0.7f*mi->m_wheelScale, mi->m_wheelScale, mi->m_wheelScale); + else + mat.Scale(mi->m_wheelScale); + mat.Translate(pos); + mat.UpdateRW(); + + ProcessSwingingDoor(CAR_DOOR_LF, DOOR_FRONT_LEFT); + ProcessSwingingDoor(CAR_DOOR_RF, DOOR_FRONT_RIGHT); + ProcessSwingingDoor(CAR_DOOR_LR, DOOR_REAR_LEFT); + ProcessSwingingDoor(CAR_DOOR_RR, DOOR_REAR_RIGHT); + ProcessSwingingDoor(CAR_BONNET, DOOR_BONNET); + ProcessSwingingDoor(CAR_BOOT, DOOR_BOOT); + } + + if((GetModelIndex() == MI_PHEONIX || GetModelIndex() == MI_BFINJECT) && + GetStatus() == STATUS_PLAYER && m_aCarNodes[CAR_WING_LR]){ + float rotation = 0.0f; + + if(GetModelIndex() == MI_BFINJECT) + if(m_fPropellerRotation > TWOPI) m_fPropellerRotation -= TWOPI; + + if(Abs(m_fGasPedal) > 0.0f){ + if(GetModelIndex() == MI_BFINJECT){ + m_fPropellerRotation += 0.2f*CTimer::GetTimeStep(); + rotation = m_fPropellerRotation; + }else{ + if(m_fPropellerRotation < 1.3f){ + m_fPropellerRotation = Min(m_fPropellerRotation+0.1f*CTimer::GetTimeStep(), 1.3f); + rotation = m_fPropellerRotation; + }else{ + float wave = Sin((CTimer::GetTimeInMilliseconds()%10000)/70.0f); + rotation = m_fPropellerRotation + 0.13*wave; + } + } + }else{ + if(GetModelIndex() == MI_BFINJECT){ + m_fPropellerRotation += 0.1f*CTimer::GetTimeStep(); + rotation = m_fPropellerRotation; + }else{ + if(m_fPropellerRotation > 0.0f){ + m_fPropellerRotation = Max(m_fPropellerRotation-0.05f*CTimer::GetTimeStep(), 0.0f); + rotation = m_fPropellerRotation; + } + } + } + + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WING_LR])); + pos = mat.GetPosition(); + if(GetModelIndex() == MI_BFINJECT) + mat.SetRotateY(rotation); + else + mat.SetRotateX(rotation); + mat.Translate(pos); + mat.UpdateRW(); + } +} + +void +CAutomobile::Render(void) +{ + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + mi->SetVehicleColour(m_currentColour1, m_currentColour2); + + if(IsRealHeli()){ + RpAtomic *atomic = nil; + int rotorAlpha = (1.5f - Min(1.7f*Max(m_aWheelSpeed[1],0.0f)/0.22f, 1.5f))*255.0f; + rotorAlpha = Min(rotorAlpha, 255); + int blurAlpha = Max(1.5f*m_aWheelSpeed[1]/0.22f - 0.4f, 0.0f)*150.0f; + blurAlpha = Min(blurAlpha, 150); + + // Top rotor + if(m_aCarNodes[CAR_BONNET]){ + RwFrameForAllObjects(m_aCarNodes[CAR_BONNET], GetCurrentAtomicObjectCB, &atomic); + if(atomic) + SetComponentAtomicAlpha(atomic, rotorAlpha); + } + atomic = nil; + // Rear rotor + if(m_aCarNodes[CAR_BOOT]){ + RwFrameForAllObjects(m_aCarNodes[CAR_BOOT], GetCurrentAtomicObjectCB, &atomic); + if(atomic) + SetComponentAtomicAlpha(atomic, rotorAlpha); + } + atomic = nil; + // Blurred top rotor + if(m_aCarNodes[CAR_WINDSCREEN]){ + RwFrameForAllObjects(m_aCarNodes[CAR_WINDSCREEN], GetCurrentAtomicObjectCB, &atomic); + if(atomic) + SetComponentAtomicAlpha(atomic, blurAlpha); + } + atomic = nil; + // Blurred rear rotor + if(m_aCarNodes[CAR_BUMP_REAR]){ + RwFrameForAllObjects(m_aCarNodes[CAR_BUMP_REAR], GetCurrentAtomicObjectCB, &atomic); + if(atomic) + SetComponentAtomicAlpha(atomic, blurAlpha); + } + } + + if(CVehicle::bWheelsOnlyCheat){ + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RB])); + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB])); + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF])); + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LF])); + if(m_aCarNodes[CAR_WHEEL_RM]) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RM])); + if(m_aCarNodes[CAR_WHEEL_LM]) + RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LM])); + }else + CEntity::Render(); +} + +int32 +CAutomobile::ProcessEntityCollision(CEntity *ent, CColPoint *colpoints) +{ + int i; + CColModel *colModel; + + if(GetStatus() != STATUS_SIMPLE) + bVehicleColProcessed = true; + + if(bUsingSpecialColModel) + colModel = &CWorld::Players[CWorld::PlayerInFocus].m_ColModel; + else + colModel = GetColModel(); + + int numWheelCollisions = 0; + float prevRatios[4] = { 0.0f, 0.0f, 0.0f, 0.0f}; + for(i = 0; i < 4; i++) + prevRatios[i] = m_aSuspensionSpringRatio[i]; + + if(m_bIsVehicleBeingShifted || bSkipLineCol || ent->IsPed() || + GetModelIndex() == MI_DODO && ent->IsVehicle()) + colModel->numLines = 0; + + int numCollisions = CCollision::ProcessColModels(GetMatrix(), *colModel, + ent->GetMatrix(), *ent->GetColModel(), + colpoints, + m_aWheelColPoints, m_aSuspensionSpringRatio); + + // m_aSuspensionSpringRatio are now set to the point where the tyre touches ground. + // In ProcessControl these will be re-normalized to ignore the tyre radius. + + if(colModel->numLines){ + for(i = 0; i < 4; i++) + if(m_aSuspensionSpringRatio[i] < 1.0f && m_aSuspensionSpringRatio[i] < prevRatios[i]){ + numWheelCollisions++; + + // wheel is touching a physical + if(ent->IsVehicle() || ent->IsObject()){ + CPhysical *phys = (CPhysical*)ent; + + m_aGroundPhysical[i] = phys; + phys->RegisterReference((CEntity**)&m_aGroundPhysical[i]); + m_aGroundOffset[i] = m_aWheelColPoints[i].point - phys->GetPosition(); + } + + m_nSurfaceTouched = m_aWheelColPoints[i].surfaceB; + if(ent->IsBuilding()) + m_pCurGroundEntity = ent; + } + }else + colModel->numLines = 4; + + if(numCollisions > 0 || numWheelCollisions > 0){ + AddCollisionRecord(ent); + if(!ent->IsBuilding()) + ((CPhysical*)ent)->AddCollisionRecord(this); + + if(numCollisions > 0) + if(ent->IsBuilding() || + ent->IsObject() && ((CPhysical*)ent)->bInfiniteMass) + bHasHitWall = true; + } + + return numCollisions; +} + +static int16 nLastControlInput; +static float fMouseCentreRange = 0.35f; +static float fMouseSteerSens = -0.0035f; +static float fMouseCentreMult = 0.975f; + +void +CAutomobile::ProcessControlInputs(uint8 pad) +{ + float speed = DotProduct(m_vecMoveSpeed, GetForward()); + + if(!CPad::GetPad(pad)->GetExitVehicle() || + pDriver && pDriver->m_pVehicleAnim && (pDriver->m_pVehicleAnim->animId == ANIM_STD_ROLLOUT_LHS || + pDriver->m_pVehicleAnim->animId == ANIM_STD_ROLLOUT_RHS)) + bIsHandbrakeOn = !!CPad::GetPad(pad)->GetHandBrake(); + else + bIsHandbrakeOn = true; + + // Steer left/right + if(CCamera::m_bUseMouse3rdPerson && !CVehicle::m_bDisableMouseSteering){ + if(CPad::GetPad(pad)->GetMouseX() != 0.0f){ + m_fSteerInput += fMouseSteerSens*CPad::GetPad(pad)->GetMouseX(); + nLastControlInput = 2; + if(Abs(m_fSteerInput) < fMouseCentreRange) + m_fSteerInput *= Pow(fMouseCentreMult, CTimer::GetTimeStep()); + }else if(CPad::GetPad(pad)->GetSteeringLeftRight() || nLastControlInput != 2){ + // mouse hasn't move, steer with pad like below + m_fSteerInput += (-CPad::GetPad(pad)->GetSteeringLeftRight()/128.0f - m_fSteerInput)* + 0.2f*CTimer::GetTimeStep(); + nLastControlInput = 0; + } + }else{ + m_fSteerInput += (-CPad::GetPad(pad)->GetSteeringLeftRight()/128.0f - m_fSteerInput)* + 0.2f*CTimer::GetTimeStep(); + nLastControlInput = 0; + } + m_fSteerInput = Clamp(m_fSteerInput, -1.0f, 1.0f); + + // Accelerate/Brake + float acceleration = (CPad::GetPad(pad)->GetAccelerate() - CPad::GetPad(pad)->GetBrake())/255.0f; + if(GetModelIndex() == MI_DODO && acceleration < 0.0f) + acceleration *= 0.3f; + if(Abs(speed) < 0.01f){ + // standing still, go into direction we want + if(CPad::GetPad(pad)->GetAccelerate() > 150.0f && CPad::GetPad(pad)->GetBrake() > 150.0f){ + m_fGasPedal = CPad::GetPad(pad)->GetAccelerate()/255.0f; + m_fBrakePedal = CPad::GetPad(pad)->GetBrake()/255.0f; + m_doingBurnout = 1; + }else{ + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + } + }else{ +#if 1 + // simpler than the code below + if(speed * acceleration < 0.0f){ + // if opposite directions, have to brake first + m_fGasPedal = 0.0f; + m_fBrakePedal = Abs(acceleration); + }else{ + // accelerating in same direction we were already going + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + } +#else + if(speed < 0.0f){ + // moving backwards currently + if(acceleration < 0.0f){ + // still go backwards + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + }else{ + // want to go forwards, so brake + m_fGasPedal = 0.0f; + m_fBrakePedal = acceleration; + } + }else{ + // moving forwards currently + if(acceleration < 0.0f){ + // want to go backwards, so brake + m_fGasPedal = 0.0f; + m_fBrakePedal = -acceleration; + }else{ + // still go forwards + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + } + } +#endif + } + + // Actually turn wheels + static float fValue; // why static? + if(m_fSteerInput < 0.0f) + fValue = -sq(m_fSteerInput); + else + fValue = sq(m_fSteerInput); + m_fSteerAngle = DEGTORAD(pHandling->fSteeringLock) * fValue; + + if(bComedyControls){ + int rnd = CGeneral::GetRandomNumber() % 10; + switch(m_comedyControlState){ + case 0: + if(rnd < 2) + m_comedyControlState = 1; + else if(rnd < 4) + m_comedyControlState = 2; + break; + case 1: + m_fSteerAngle += 0.05f; + if(rnd < 2) + m_comedyControlState = 0; + break; + case 2: + m_fSteerAngle -= 0.05f; + if(rnd < 2) + m_comedyControlState = 0; + break; + } + }else + m_comedyControlState = 0; + + // Brake if player isn't in control + // BUG: game always uses pad 0 here +#ifdef FIX_BUGS + if(CPad::GetPad(pad)->ArePlayerControlsDisabled()){ +#else + if(CPad::GetPad(0)->ArePlayerControlsDisabled()){ +#endif + m_fBrakePedal = 1.0f; + bIsHandbrakeOn = true; + m_fGasPedal = 0.0f; + + FindPlayerPed()->KeepAreaAroundPlayerClear(); + + // slow down car immediately + speed = m_vecMoveSpeed.Magnitude(); + if(speed > 0.28f) + m_vecMoveSpeed *= 0.28f/speed; + } +} + +void +CAutomobile::FireTruckControl(void) +{ + if(this == FindPlayerVehicle()){ + if(!CPad::GetPad(0)->GetCarGunFired()) + return; +#ifdef FREE_CAM + if (!CCamera::bFreeCam) +#endif + { + m_fCarGunLR += CPad::GetPad(0)->GetCarGunLeftRight() * 0.00025f * CTimer::GetTimeStep(); + m_fCarGunUD += CPad::GetPad(0)->GetCarGunUpDown() * 0.0001f * CTimer::GetTimeStep(); + } + m_fCarGunUD = Clamp(m_fCarGunUD, 0.05f, 0.3f); + + + CVector cannonPos(0.0f, 1.5f, 1.9f); + cannonPos = GetMatrix() * cannonPos; + CVector cannonDir( + Sin(m_fCarGunLR) * Cos(m_fCarGunUD), + Cos(m_fCarGunLR) * Cos(m_fCarGunUD), + Sin(m_fCarGunUD)); + cannonDir = Multiply3x3(GetMatrix(), cannonDir); + cannonDir.z += (CGeneral::GetRandomNumber()&0xF)/1000.0f; + CWaterCannons::UpdateOne((uintptr)this, &cannonPos, &cannonDir); + }else if(GetStatus() == STATUS_PHYSICS){ + CFire *fire = gFireManager.FindFurthestFire_NeverMindFireMen(GetPosition(), 10.0f, 35.0f); + if(fire == nil) + return; + + // Target cannon onto fire + float targetAngle = CGeneral::GetATanOfXY(fire->m_vecPos.x-GetPosition().x, fire->m_vecPos.y-GetPosition().y); + float fwdAngle = CGeneral::GetATanOfXY(GetForward().x, GetForward().y); + float targetCannonAngle = fwdAngle - targetAngle; + float angleDelta = CTimer::GetTimeStep()*0.01f; + float cannonDelta = targetCannonAngle - m_fCarGunLR; + while(cannonDelta < PI) cannonDelta += TWOPI; + while(cannonDelta > PI) cannonDelta -= TWOPI; + if(Abs(cannonDelta) < angleDelta) + m_fCarGunLR = targetCannonAngle; + else if(cannonDelta > 0.0f) + m_fCarGunLR += angleDelta; + else + m_fCarGunLR -= angleDelta; + + // Go up and down a bit + float upDown = Sin((float)(CTimer::GetTimeInMilliseconds() & 0xFFF)/0x1000 * TWOPI); + m_fCarGunUD = 0.2f + 0.2f*upDown; + + // Spray water every once in a while + if((CTimer::GetTimeInMilliseconds()>>10) & 3){ + CVector cannonPos(0.0f, 0.0f, 2.2f); // different position than player's firetruck! + cannonPos = GetMatrix() * cannonPos; + CVector cannonDir( + Sin(m_fCarGunLR) * Cos(m_fCarGunUD), + Cos(m_fCarGunLR) * Cos(m_fCarGunUD), + Sin(m_fCarGunUD)); + cannonDir = Multiply3x3(GetMatrix(), cannonDir); + cannonDir.z += (CGeneral::GetRandomNumber()&0xF)/1000.0f; + CWaterCannons::UpdateOne((uintptr)this, &cannonPos, &cannonDir); + } + } +} + +void +CAutomobile::TankControl(void) +{ + int i; + + // These coords are 1 unit higher then they should be relative to model center + CVector turrentBase(0.0f, -1.394f, 2.296f); + CVector gunEnd(0.0f, 1.813f, 2.979f); + CVector baseToEnd = gunEnd - turrentBase; + + if(this != FindPlayerVehicle()) + return; + if(CWorld::Players[CWorld::PlayerInFocus].m_WBState != WBSTATE_PLAYING) + return; + + // Rotate turret + float prevAngle = m_fCarGunLR; +#ifdef FREE_CAM + if(!CCamera::bFreeCam) +#endif + m_fCarGunLR -= CPad::GetPad(0)->GetCarGunLeftRight() * 0.00015f * CTimer::GetTimeStep(); + + if(m_fCarGunLR < 0.0f) + m_fCarGunLR += TWOPI; + if(m_fCarGunLR > TWOPI) + m_fCarGunLR -= TWOPI; + if(m_fCarGunLR != prevAngle) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_TANK_TURRET_ROTATE, Abs(m_fCarGunLR - prevAngle)); + + // Shoot + if(CPad::GetPad(0)->CarGunJustDown() && + CTimer::GetTimeInMilliseconds() > CWorld::Players[CWorld::PlayerInFocus].m_nTimeTankShotGun + 800){ + CWorld::Players[CWorld::PlayerInFocus].m_nTimeTankShotGun = CTimer::GetTimeInMilliseconds(); + + // more like -sin(angle), cos(angle), i.e. rotated (0,1,0) + CVector turretDir = CVector(Sin(-m_fCarGunLR), Cos(-m_fCarGunLR), 0.0f); + turretDir = Multiply3x3(GetMatrix(), turretDir); + + float c = Cos(m_fCarGunLR); + float s = Sin(m_fCarGunLR); + CVector rotatedEnd( + c*baseToEnd.x - s*baseToEnd.y, + s*baseToEnd.x + c*baseToEnd.y, + baseToEnd.z - 1.0f); // correct offset here + rotatedEnd += turrentBase; + + CVector point1 = GetMatrix() * rotatedEnd; + CVector point2 = point1 + 60.0f*turretDir; + m_vecMoveSpeed -= 0.06f*turretDir; + m_vecMoveSpeed.z += 0.05f; + + CWeapon::DoTankDoomAiming(FindPlayerVehicle(), FindPlayerPed(), &point1, &point2); + CColPoint colpoint; + CEntity *entity = nil; + CWorld::ProcessLineOfSight(point1, point2, colpoint, entity, true, true, true, true, true, true, false); + if(entity) + point2 = colpoint.point - 0.04f*(colpoint.point - point1); + + CExplosion::AddExplosion(nil, FindPlayerPed(), EXPLOSION_TANK_GRENADE, point2, 0); + + // Add particles on the way to the explosion; + float shotDist = (point2 - point1).Magnitude(); + int n = shotDist/4.0f; + RwRGBA black = { 0, 0, 0, 0 }; + for(i = 0; i < n; i++){ + float f = (float)i/n; + CParticle::AddParticle(PARTICLE_HELI_DUST, + point1 + f*(point2 - point1), + CVector(0.0f, 0.0f, 0.0f), + nil, 0.1f, black); + } + + // More particles + CVector shotDir = point2 - point1; + shotDir.Normalise(); + for(i = 0; i < 15; i++){ + float f = i/15.0f; + CParticle::AddParticle(PARTICLE_GUNSMOKE2, point1, + shotDir*CGeneral::GetRandomNumberInRange(0.3f, 1.0f)*f, + nil, CGeneral::GetRandomNumberInRange(0.5f, 1.5f)*f, black); + } + + // And some gun flashes near the gun + CVector flashPos = point1; + CVector nullDir(0.0f, 0.0f, 0.0f); + int lifeSpan = 250; + if(m_vecMoveSpeed.Magnitude() > 0.08f){ + lifeSpan = 125; + flashPos.x += 5.0f*m_vecMoveSpeed.x; + flashPos.y += 5.0f*m_vecMoveSpeed.y; + } + CParticle::AddParticle(PARTICLE_GUNFLASH, flashPos, nullDir, nil, 0.4f, black, 0, 0, 0, lifeSpan); + flashPos += 0.3f*shotDir; + CParticle::AddParticle(PARTICLE_GUNFLASH, flashPos, nullDir, nil, 0.2f, black, 0, 0, 0, lifeSpan); + flashPos += 0.1f*shotDir; + CParticle::AddParticle(PARTICLE_GUNFLASH, flashPos, nullDir, nil, 0.15f, black, 0, 0, 0, lifeSpan); + } +} + +#define HYDRAULIC_UPPER_EXT (-0.16f) +#define HYDRAULIC_LOWER_EXT (0.16f) + +void +CAutomobile::HydraulicControl(void) +{ + int i; + float wheelPositions[4]; + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + CColModel *normalColModel = mi->GetColModel(); + float wheelRadius = 0.5f*mi->m_wheelScale; + CPlayerInfo *playerInfo = &CWorld::Players[CWorld::PlayerInFocus]; + CColModel *specialColModel = &playerInfo->m_ColModel; + + if(GetStatus() != STATUS_PLAYER){ + // reset hydraulics for non-player cars + + if(!bUsingSpecialColModel) + return; + if(specialColModel != nil) // this is always true + for(i = 0; i < 4; i++) + wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i]; + for(i = 0; i < 4; i++){ + m_aSuspensionSpringLength[i] = pHandling->fSuspensionUpperLimit - pHandling->fSuspensionLowerLimit; + m_aSuspensionLineLength[i] = normalColModel->lines[i].p0.z - normalColModel->lines[i].p1.z; + m_aSuspensionSpringRatio[i] = (normalColModel->lines[i].p0.z - wheelPositions[i]) / m_aSuspensionLineLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + + if(m_hydraulicState == 0) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_1, 0.0f); + else if(m_hydraulicState >= 100) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + + if(playerInfo->m_pVehicleEx == this) + playerInfo->m_pVehicleEx = nil; + bUsingSpecialColModel = false; + m_hydraulicState = 0; + return; + } + + // Player car + + float normalUpperLimit = pHandling->fSuspensionUpperLimit; + float normalLowerLimit = pHandling->fSuspensionLowerLimit; + float normalSpringLength = normalUpperLimit - normalLowerLimit; + float extendedUpperLimit = normalUpperLimit - 0.2f; + float extendedLowerLimit = normalLowerLimit - 0.2f; + float extendedSpringLength = extendedUpperLimit - extendedLowerLimit; + + if(!bUsingSpecialColModel){ + // Init special col model + + if(playerInfo->m_pVehicleEx && playerInfo->m_pVehicleEx == this) + playerInfo->m_pVehicleEx->bUsingSpecialColModel = false; + playerInfo->m_pVehicleEx = this; + playerInfo->m_ColModel = *normalColModel; + bUsingSpecialColModel = true; + specialColModel = &playerInfo->m_ColModel; + + if(m_fVelocityChangeForAudio > 0.1f) + m_hydraulicState = 20; + else{ + m_hydraulicState = 0; + normalUpperLimit += HYDRAULIC_UPPER_EXT; + normalSpringLength = normalUpperLimit - (normalLowerLimit+HYDRAULIC_LOWER_EXT); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + } + + // Setup suspension + float normalLineLength = normalSpringLength + wheelRadius; + CVector pos; + for(i = 0; i < 4; i++){ + wheelPositions[i] = normalColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i]; + mi->GetWheelPosn(i, pos); + pos.z += normalUpperLimit; + specialColModel->lines[i].p0 = pos; + pos.z -= normalLineLength; + specialColModel->lines[i].p1 = pos; + m_aSuspensionSpringLength[i] = normalSpringLength; + m_aSuspensionLineLength[i] = normalLineLength; + + if(m_aSuspensionSpringRatio[i] < 1.0f){ + m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + } + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + + // Adjust col model + mi->GetWheelPosn(0, pos); + float minz = pos.z + extendedLowerLimit - wheelRadius; + if(minz < specialColModel->boundingBox.min.z) + specialColModel->boundingBox.min.z = minz; + float radius = Max(specialColModel->boundingBox.min.Magnitude(), specialColModel->boundingBox.max.Magnitude()); + if(specialColModel->boundingSphere.radius < radius) + specialColModel->boundingSphere.radius = radius; + return; + } + + if(playerInfo->m_WBState != WBSTATE_PLAYING) + return; + + bool setPrevRatio = false; + if(m_hydraulicState < 20 && m_fVelocityChangeForAudio > 0.2f){ + if(m_hydraulicState == 0){ + m_hydraulicState = 20; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_1, 0.0f); + setPrevRatio = true; + }else{ + m_hydraulicState++; + } + }else if(m_hydraulicState != 0){ // must always be true + if(m_hydraulicState < 21 && m_fVelocityChangeForAudio < 0.1f){ + m_hydraulicState--; + if(m_hydraulicState == 0) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + } + } + + if(CPad::GetPad(0)->HornJustDown()){ + // Switch between normal and extended + + if(m_hydraulicState < 100) + m_hydraulicState = 100; + else{ + if(m_fVelocityChangeForAudio > 0.1f) + m_hydraulicState = 20; + else + m_hydraulicState = 0; + } + + if(m_hydraulicState < 100){ + if(m_hydraulicState == 0){ + normalUpperLimit += HYDRAULIC_UPPER_EXT; + normalLowerLimit += HYDRAULIC_LOWER_EXT; + normalSpringLength = normalUpperLimit - normalLowerLimit; + } + + // Reset suspension to normal + float normalLineLength = normalSpringLength + wheelRadius; + CVector pos; + for(i = 0; i < 4; i++){ + wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i]; + mi->GetWheelPosn(i, pos); + pos.z += normalUpperLimit; + specialColModel->lines[i].p0 = pos; + pos.z -= normalLineLength; + specialColModel->lines[i].p1 = pos; + m_aSuspensionSpringLength[i] = normalSpringLength; + m_aSuspensionLineLength[i] = normalLineLength; + + if(m_aSuspensionSpringRatio[i] < 1.0f){ + m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + } + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + }else{ + // Reset suspension to extended + float extendedLineLength = extendedSpringLength + wheelRadius; + CVector pos; + for(i = 0; i < 4; i++){ + wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i]; + mi->GetWheelPosn(i, pos); + pos.z += extendedUpperLimit; + specialColModel->lines[i].p0 = pos; + pos.z -= extendedLineLength; + specialColModel->lines[i].p1 = pos; + m_aSuspensionSpringLength[i] = extendedSpringLength; + m_aSuspensionLineLength[i] = extendedLineLength; + + if(m_aSuspensionSpringRatio[i] < 1.0f){ + m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + + setPrevRatio = true; + } + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + } + }else{ + float suspChange[4]; + float maxDelta = 0.0f; + float rear = CPad::GetPad(0)->GetCarGunUpDown()/128.0f; + float front = -rear; + float right = CPad::GetPad(0)->GetCarGunLeftRight()/128.0f; + float left = -right; + suspChange[CARWHEEL_FRONT_LEFT] = Max(front+left, 0.0f); + suspChange[CARWHEEL_REAR_LEFT] = Max(rear+left, 0.0f); + suspChange[CARWHEEL_FRONT_RIGHT] = Max(front+right, 0.0f); + suspChange[CARWHEEL_REAR_RIGHT] = Max(rear+right, 0.0f); + + if(m_hydraulicState < 100){ + // Lowered, move wheels up + + if(m_hydraulicState == 0){ + normalUpperLimit += HYDRAULIC_UPPER_EXT; + normalLowerLimit += HYDRAULIC_LOWER_EXT; + normalSpringLength = normalUpperLimit - normalLowerLimit; + } + + // Set suspension + CVector pos; + for(i = 0; i < 4; i++){ + if(suspChange[i] > 1.0f) + suspChange[i] = 1.0f; + + float upperLimit = suspChange[i]*(extendedUpperLimit-normalUpperLimit) + normalUpperLimit; + float springLength = suspChange[i]*(extendedSpringLength-normalSpringLength) + normalSpringLength; + float lineLength = springLength + wheelRadius; + + wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i]; + mi->GetWheelPosn(i, pos); + pos.z += upperLimit; + specialColModel->lines[i].p0 = pos; + pos.z -= lineLength; + if(Abs(pos.z - specialColModel->lines[i].p1.z) > Abs(maxDelta)) + maxDelta = pos.z - specialColModel->lines[i].p1.z; + specialColModel->lines[i].p1 = pos; + m_aSuspensionSpringLength[i] = springLength; + m_aSuspensionLineLength[i] = lineLength; + + if(m_aSuspensionSpringRatio[i] < 1.0f){ + m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + } + }else{ + if(m_hydraulicState < 104) + m_hydraulicState++; + + if(m_fVelocityChangeForAudio < 0.1f){ + normalUpperLimit += HYDRAULIC_UPPER_EXT; + normalLowerLimit += HYDRAULIC_LOWER_EXT; + normalSpringLength = normalUpperLimit - normalLowerLimit; + } + + // Set suspension + CVector pos; + for(i = 0; i < 4; i++){ + if(suspChange[i] > 1.0f) + suspChange[i] = 1.0f; + + float upperLimit = suspChange[i]*(normalUpperLimit-extendedUpperLimit) + extendedUpperLimit; + float springLength = suspChange[i]*(normalSpringLength-extendedSpringLength) + extendedSpringLength; + float lineLength = springLength + wheelRadius; + + wheelPositions[i] = specialColModel->lines[i].p0.z - m_aSuspensionSpringRatio[i]*m_aSuspensionLineLength[i]; + mi->GetWheelPosn(i, pos); + pos.z += upperLimit; + specialColModel->lines[i].p0 = pos; + pos.z -= lineLength; + if(Abs(pos.z - specialColModel->lines[i].p1.z) > Abs(maxDelta)) + maxDelta = pos.z - specialColModel->lines[i].p1.z; + specialColModel->lines[i].p1 = pos; + m_aSuspensionSpringLength[i] = springLength; + m_aSuspensionLineLength[i] = lineLength; + + if(m_aSuspensionSpringRatio[i] < 1.0f){ + m_aSuspensionSpringRatio[i] = (specialColModel->lines[i].p0.z - wheelPositions[i])/m_aSuspensionLineLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + } + } + + float limitDiff = extendedLowerLimit - normalLowerLimit; + if(limitDiff != 0.0f && Abs(maxDelta/limitDiff) > 0.01f){ + float f = (maxDelta + limitDiff)/2.0f/limitDiff; + f = Clamp(f, 0.0f, 1.0f); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_3, f); + if(f < 0.4f || f > 0.6f) + setPrevRatio = true; + if(f < 0.25f) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); + else if(f > 0.75f) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_1, 0.0f); + } + } + + if(setPrevRatio) + for(i = 0; i < 4; i++){ + // wheel radius in relation to suspension line + float wheelRadius = 1.0f - m_aSuspensionSpringLength[i]/m_aSuspensionLineLength[i]; + m_aSuspensionSpringRatioPrev[i] = (m_aSuspensionSpringRatio[i]-wheelRadius)/(1.0f-wheelRadius); + } +} + +void +CAutomobile::ProcessBuoyancy(void) +{ + int i; + CVector impulse, point; + + if(mod_Buoyancy.ProcessBuoyancy(this, m_fBuoyancy, &point, &impulse)){ + bTouchingWater = true; + float timeStep = Max(CTimer::GetTimeStep(), 0.01f); + float impulseRatio = impulse.z / (GRAVITY * m_fMass * timeStep); + float waterResistance = Pow(1.0f - 0.05f*impulseRatio, CTimer::GetTimeStep()); + m_vecMoveSpeed *= waterResistance; + m_vecTurnSpeed *= waterResistance; + + bool heliHitWaterHard = false; + if(IsRealHeli() && m_aWheelSpeed[1] > 0.15f){ + if(GetModelIndex() == MI_SEASPAR){ + if(impulseRatio > 3.0f){ + m_aWheelSpeed[1] = 0.0f; + heliHitWaterHard = true; + } + }else{ + float strength = Max(8.0f*impulseRatio, 1.0f); + ApplyMoveForce(-2.0f*impulse/strength); + ApplyTurnForce(-impulse/strength, point); + if(impulseRatio > 0.9f){ + m_aWheelSpeed[1] = 0.0f; + heliHitWaterHard = true; + }else + return; + } + } + + bTouchingWater = true; + ApplyMoveForce(impulse); + ApplyTurnForce(impulse, point); + CVector initialSpeed = m_vecMoveSpeed; + + if(m_modelIndex == MI_SEASPAR && impulseRatio < 3.0f && (GetUp().z > -0.5f || impulseRatio < 0.6f) || + CVehicle::bHoverCheat && GetStatus() == STATUS_PLAYER && GetUp().z > 0.1f){ + bIsInWater = false; + bIsDrowning = false; + }else if(heliHitWaterHard || impulseRatio > 1.0f || + impulseRatio > 0.6f && (m_aSuspensionSpringRatio[0] == 1.0f || + m_aSuspensionSpringRatio[1] == 1.0f || + m_aSuspensionSpringRatio[2] == 1.0f || + m_aSuspensionSpringRatio[3] == 1.0f)){ + bIsInWater = true; + bIsDrowning = true; + if(m_vecMoveSpeed.z < -0.1f) + m_vecMoveSpeed.z = -0.1f; + + if(pDriver){ + pDriver->bIsInWater = true; + if(pDriver->IsPlayer() || !bWaterTight) + pDriver->InflictDamage(nil, WEAPONTYPE_DROWNING, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); + } + for(i = 0; i < m_nNumMaxPassengers; i++) + if(pPassengers[i]){ + pPassengers[i]->bIsInWater = true; + if(pPassengers[i]->IsPlayer() || !bWaterTight) + pPassengers[i]->InflictDamage(nil, WEAPONTYPE_DROWNING, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); + } + }else{ + bIsInWater = false; + bIsDrowning = false; + } + + static uint32 nGenerateRaindrops = 0; + static uint32 nGenerateWaterCircles = 0; + + if(initialSpeed.z < -0.1f && impulse.z > 0.3f || heliHitWaterHard){ + RwRGBA color; + color.red = (0.5f * CTimeCycle::GetDirectionalRed() + CTimeCycle::GetAmbientRed_Obj())*0.45f*255; + color.green = (0.5f * CTimeCycle::GetDirectionalGreen() + CTimeCycle::GetAmbientGreen_Obj())*0.45f*255; + color.blue = (0.5f * CTimeCycle::GetDirectionalBlue() + CTimeCycle::GetAmbientBlue_Obj())*0.45f*255; + color.alpha = CGeneral::GetRandomNumberInRange(0, 32) + 128; + CVector target = CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.15f, 0.45f)); + CParticleObject::AddObject(POBJECT_CAR_WATER_SPLASH, GetPosition(), + target, 0.0f, 75, color, true); + + nGenerateRaindrops = CTimer::GetTimeInMilliseconds() + 300; + nGenerateWaterCircles = CTimer::GetTimeInMilliseconds() + 60; + + if(heliHitWaterHard){ + CVector right = CrossProduct(GetForward(), CVector(0.0f, 0.0f, 1.0f)); + CParticleObject::AddObject(POBJECT_CAR_WATER_SPLASH, GetPosition() + right, + target, 0.0f, 75, color, true); + CParticleObject::AddObject(POBJECT_CAR_WATER_SPLASH, GetPosition() - right, + target, 0.0f, 75, color, true); + } + + if(m_vecMoveSpeed.z < -0.2f) + m_vecMoveSpeed.z = -0.2f; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WATER_FALL, 0.0f); + } + + if(nGenerateWaterCircles > 0 && nGenerateWaterCircles <= CTimer::GetTimeInMilliseconds()){ + CVector pos = GetPosition(); + float waterLevel = 0.0f; + if(CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &waterLevel, false)) + pos.z = waterLevel; + static RwRGBA black; + if(pos.z != 0.0f){ + nGenerateWaterCircles = 0; + pos.z += 1.0f; + for(i = 0; i < 4; i++){ + CVector p = pos; + p.x += CGeneral::GetRandomNumberInRange(-2.5f, 2.5f); + p.y += CGeneral::GetRandomNumberInRange(-2.5f, 2.5f); + CParticle::AddParticle(PARTICLE_RAIN_SPLASH_BIGGROW, + p, CVector(0.0f, 0.0f, 0.0f), + nil, 0.0f, black, 0, 0, 0, 0); + } + } + } + + if(nGenerateRaindrops > 0 && nGenerateRaindrops <= CTimer::GetTimeInMilliseconds()){ + CVector pos = GetPosition(); + float waterLevel = 0.0f; + if(CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &waterLevel, false)) + pos.z = waterLevel; + static RwRGBA black; + if(pos.z >= 0.0f){ + nGenerateRaindrops = 0; + pos.z += 0.5f; + CParticleObject::AddObject(POBJECT_SPLASHES_AROUND, + pos, CVector(0.0f, 0.0f, 0.0f), 6.5f, 2500, black, true); + } + } + }else{ + bIsInWater = false; + bIsDrowning = false; + bTouchingWater = false; + + static RwRGBA splashCol = {155, 155, 185, 196}; + static RwRGBA smokeCol = {255, 255, 255, 255}; + + for(i = 0; i < 4; i++){ + if(m_aSuspensionSpringRatio[i] < 1.0f && m_aWheelColPoints[i].surfaceB == SURFACE_WATER){ + CVector pos = m_aWheelColPoints[i].point + 0.3f*GetUp() - GetPosition(); + CVector vSpeed = GetSpeed(pos); + vSpeed.z = 0.0f; + float fSpeed = vSpeed.MagnitudeSqr(); + if(fSpeed > sq(0.05f)){ + fSpeed = Sqrt(fSpeed); + + float size = Min((fSpeed < 0.15f ? 0.25f : 0.75f)*fSpeed, 0.6f); + CVector right = 0.2f*fSpeed*GetRight() + 0.2f*vSpeed; + + CParticle::AddParticle(PARTICLE_PED_SPLASH, + pos + GetPosition(), -0.5f*right, + nil, size, splashCol, + CGeneral::GetRandomNumberInRange(0.0f, 10.0f), + CGeneral::GetRandomNumberInRange(0.0f, 90.0f), 1, 0); + + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + pos + GetPosition(), -0.6f*right, + nil, size, smokeCol, 0, 0, 0, 0); + + if((CTimer::GetFrameCounter() & 0xF) == 0) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_SPLASH, 2000.0f*fSpeed); + } + } + } + } +} + +void +CAutomobile::DoDriveByShootings(void) +{ + CAnimBlendAssociation *anim = nil; + CPlayerInfo* playerInfo = ((CPlayerPed*)pDriver)->GetPlayerInfoForThisPlayerPed(); + if (playerInfo && !playerInfo->m_bDriveByAllowed) + return; + + CWeapon *weapon = pDriver->GetWeapon(); + if(CWeaponInfo::GetWeaponInfo(weapon->m_eWeaponType)->m_nWeaponSlot != WEAPONSLOT_SUBMACHINEGUN) + return; + + weapon->Update(pDriver->m_audioEntityId, nil); + + bool lookingLeft = false; + bool lookingRight = false; + if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || + TheCamera.m_bObbeCinematicCarCamOn){ + if(CPad::GetPad(0)->GetLookLeft()) + lookingLeft = true; + if(CPad::GetPad(0)->GetLookRight()) + lookingRight = true; + }else{ + if(TheCamera.Cams[TheCamera.ActiveCam].LookingLeft) + lookingLeft = true; + if(TheCamera.Cams[TheCamera.ActiveCam].LookingRight) + lookingRight = true; + } + + AnimationId rightAnim = ANIM_STD_CAR_DRIVEBY_RIGHT; + AnimationId leftAnim = ANIM_STD_CAR_DRIVEBY_LEFT; + if (pDriver->m_pMyVehicle->bLowVehicle) { + rightAnim = ANIM_STD_CAR_DRIVEBY_RIGHT_LO; + leftAnim = ANIM_STD_CAR_DRIVEBY_LEFT_LO; + } + + if(lookingLeft || lookingRight){ + if(lookingLeft){ + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), rightAnim); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), leftAnim); + if(anim == nil || anim->blendDelta < 0.0f) + anim = CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, leftAnim); + }else if(pDriver->m_pMyVehicle->pPassengers[0] == nil || TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON){ + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), leftAnim); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), rightAnim); + if(anim == nil || anim->blendDelta < 0.0f) + anim = CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, rightAnim); + } + + if (!anim || !anim->IsRunning()) { + if (CPad::GetPad(0)->GetCarGunFired() && CTimer::GetTimeInMilliseconds() > weapon->m_nTimer) { + weapon->FireFromCar(this, lookingLeft, true); + weapon->m_nTimer = CTimer::GetTimeInMilliseconds() + 70; + } + } + }else{ + weapon->Reload(); + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), leftAnim); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), rightAnim); + if(anim) + anim->blendDelta = -1000.0f; + } + + // TODO: what is this? + if(!lookingLeft && m_weaponDoorTimerLeft > 0.0f){ + m_weaponDoorTimerLeft = Max(m_weaponDoorTimerLeft - CTimer::GetTimeStep()*0.1f, 0.0f); + ProcessOpenDoor(CAR_DOOR_LF, ANIM_STD_NUM, m_weaponDoorTimerLeft); + } + if(!lookingRight && m_weaponDoorTimerRight > 0.0f){ + m_weaponDoorTimerRight = Max(m_weaponDoorTimerRight - CTimer::GetTimeStep()*0.1f, 0.0f); + ProcessOpenDoor(CAR_DOOR_RF, ANIM_STD_NUM, m_weaponDoorTimerRight); + } +} + +void +CAutomobile::DoHoverSuspensionRatios(void) +{ + int i; + + if(GetUp().z < 0.1f) + return; + + CColModel *colmodel = GetColModel(); + for(i = 0; i < 4; i++){ + float z, waterZ; + CVector upper = GetMatrix() * colmodel->lines[i].p0; + CVector lower = GetMatrix() * colmodel->lines[i].p1; + if(m_aSuspensionSpringRatio[i] < 1.0f) + z = m_aWheelColPoints[i].point.z; + else + z = -100.0f; + // see if touching water + if(CWaterLevel::GetWaterLevel(lower, &waterZ, false) && + waterZ > z && lower.z-1.0f < waterZ){ + // compress spring + if(lower.z < waterZ){ + if(upper.z < waterZ) + m_aSuspensionSpringRatio[i] = 0.0f; + else + m_aSuspensionSpringRatio[i] = (upper.z - waterZ)/(upper.z - lower.z); + }else + m_aSuspensionSpringRatio[i] = 0.99999f; + + m_aWheelColPoints[i].point = CVector((lower.x - upper.x)*m_aSuspensionSpringRatio[i] + upper.x, + (lower.y - upper.y)*m_aSuspensionSpringRatio[i] + upper.y, + waterZ); + m_aWheelColPoints[i].normal = CVector(0.0f, 0.0f, 1.0f); + m_aWheelColPoints[i].surfaceB = SURFACE_WATER; + } + } +} + +int32 +CAutomobile::RcbanditCheckHitWheels(void) +{ + int x, xmin, xmax; + int y, ymin, ymax; + + xmin = CWorld::GetSectorIndexX(GetPosition().x - 2.0f); + if(xmin < 0) xmin = 0; + xmax = CWorld::GetSectorIndexX(GetPosition().x + 2.0f); + if(xmax > NUMSECTORS_X-1) xmax = NUMSECTORS_X-1; + ymin = CWorld::GetSectorIndexY(GetPosition().y - 2.0f); + if(ymin < 0) ymin = 0; + ymax = CWorld::GetSectorIndexY(GetPosition().y + 2.0f); + if(ymax > NUMSECTORS_Y-1) ymax = NUMSECTORS_X-1; + + CWorld::AdvanceCurrentScanCode(); + + for(y = ymin; y <= ymax; y++) + for(x = xmin; x <= xmax; x++){ + CSector *s = CWorld::GetSector(x, y); + if(RcbanditCheck1CarWheels(s->m_lists[ENTITYLIST_VEHICLES]) || + RcbanditCheck1CarWheels(s->m_lists[ENTITYLIST_VEHICLES_OVERLAP])) + return 1; + } + return 0; +} + +int32 +CAutomobile::RcbanditCheck1CarWheels(CPtrList &list) +{ + static CMatrix matW2B; + int i; + CPtrNode *node; + CAutomobile *car; + CColModel *colModel = GetColModel(); + CVehicleModelInfo *mi; + + for(node = list.first; node; node = node->next){ + car = (CAutomobile*)node->item; + if(this != car && car->IsCar() && car->GetModelIndex() != MI_RCBANDIT && + car->m_scanCode != CWorld::GetCurrentScanCode()){ + car->m_scanCode = CWorld::GetCurrentScanCode(); + + if(Abs(this->GetPosition().x - car->GetPosition().x) < 10.0f && + Abs(this->GetPosition().y - car->GetPosition().y) < 10.0f){ + mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(car->GetModelIndex()); + + for(i = 0; i < 4; i++){ + if(car->m_aSuspensionSpringRatioPrev[i] < 1.0f || car->GetStatus() == STATUS_SIMPLE){ + CVector wheelPos; + CColSphere sph; + mi->GetWheelPosn(i, wheelPos); + matW2B = Invert(GetMatrix()); + sph.center = matW2B * (car->GetMatrix() * wheelPos); + sph.radius = mi->m_wheelScale*0.25f; + if(CCollision::TestSphereBox(sph, colModel->boundingBox)) + return 1; + } + } + } + } + } + return 0; +} + +void +CAutomobile::PlaceOnRoadProperly(void) +{ + CColPoint point; + CEntity *entity; + CColModel *colModel = GetColModel(); + float lenFwd, lenBack; + float frontZ, rearZ; + + lenFwd = colModel->boundingBox.max.y; + lenBack = -colModel->boundingBox.min.y; + + CVector front(GetPosition().x + GetForward().x*lenFwd, + GetPosition().y + GetForward().y*lenFwd, + GetPosition().z + 5.0f); + if(CWorld::ProcessVerticalLine(front, GetPosition().z - 5.0f, point, entity, + true, false, false, false, false, false, nil)){ + frontZ = point.point.z; + m_pCurGroundEntity = entity; + }else{ + frontZ = m_fMapObjectHeightAhead; + } + + CVector rear(GetPosition().x - GetForward().x*lenBack, + GetPosition().y - GetForward().y*lenBack, + GetPosition().z + 5.0f); + if(CWorld::ProcessVerticalLine(rear, GetPosition().z - 5.0f, point, entity, + true, false, false, false, false, false, nil)){ + rearZ = point.point.z; + m_pCurGroundEntity = entity; + }else{ + rearZ = m_fMapObjectHeightBehind; + } + + float len = lenFwd + lenBack; + float angle = Atan((frontZ - rearZ)/len); + float c = Cos(angle); + float s = Sin(angle); + + GetMatrix().GetRight() = CVector((front.y - rear.y) / len, -(front.x - rear.x) / len, 0.0f); + GetMatrix().GetForward() = CVector(-c * GetRight().y, c * GetRight().x, s); + GetMatrix().GetUp() = CrossProduct(GetRight(), GetForward()); + GetMatrix().GetPosition() = CVector((front.x + rear.x) / 2.0f, (front.y + rear.y) / 2.0f, (frontZ + rearZ) / 2.0f + GetHeightAboveRoad()); +} + +void +CAutomobile::VehicleDamage(float impulse, uint16 damagedPiece) +{ + int i; + float damageMultiplier = 0.333f; + + if(impulse == 0.0f){ + impulse = m_fDamageImpulse; + damagedPiece = m_nDamagePieceType; + damageMultiplier = 1.0f; + } + + if(GetStatus() == STATUS_PLAYER && CStats::GetPercentageProgress() >= 100.0f) + impulse *= 0.5f; + + CVector pos(0.0f, 0.0f, 0.0f); + + if(!bCanBeDamaged) + return; + + if(m_pDamageEntity && m_pDamageEntity->IsPed() && ((CPed*)m_pDamageEntity)->bIsStanding){ + float speed = ((CPed*)m_pDamageEntity)->m_vecAnimMoveDelta.y * DotProduct(GetForward(), m_vecDamageNormal); + if(speed < 0.0f) + impulse = Max(impulse + ((CPed*)m_pDamageEntity)->m_fMass * speed, 0.0f); + } + + // damage flipped over car + if(GetUp().z < 0.0f && this != FindPlayerVehicle()){ + if(bNotDamagedUpsideDown || GetStatus() == STATUS_PLAYER_REMOTE || bIsInWater) + return; + if(GetStatus() != STATUS_WRECKED) + m_fHealth = Max(m_fHealth - 4.0f*CTimer::GetTimeStep(), 0.0f); + } + + float minImpulse = GetModelIndex() == MI_RCRAIDER || GetModelIndex() == MI_RCGOBLIN ? 1.0f : 25.0f; + if(impulse > minImpulse && GetStatus() != STATUS_WRECKED){ + if(bIsLawEnforcer && + FindPlayerVehicle() && FindPlayerVehicle() == m_pDamageEntity && + GetStatus() != STATUS_ABANDONED && + FindPlayerVehicle()->m_vecMoveSpeed.Magnitude() >= m_vecMoveSpeed.Magnitude() && + FindPlayerVehicle()->m_vecMoveSpeed.Magnitude() > 0.1f) + FindPlayerPed()->SetWantedLevelNoDrop(1); + + if(GetStatus() == STATUS_PLAYER && impulse > 50.0f){ + uint8 freq = Min(0.4f*impulse*2000.0f/m_fMass + 100.0f, 250.0f); + CPad::GetPad(0)->StartShake(40000/freq, freq); + } + + if(GetStatus() != STATUS_PLAYER && bOnlyDamagedByPlayer){ + if(m_pDamageEntity != FindPlayerPed() && + m_pDamageEntity != FindPlayerVehicle()) + return; + } + + if(m_pDamageEntity && m_pDamageEntity->IsVehicle()){ + m_nLastWeaponDamage = WEAPONTYPE_RAMMEDBYCAR; + m_pLastDamageEntity = m_pDamageEntity; + } + + if(bCollisionProof) + return; + + if(m_pDamageEntity){ + if(m_pDamageEntity->IsBuilding() && + DotProduct(m_vecDamageNormal, GetUp()) > 0.6f) + return; + } + + int oldLightStatus[4]; + for(i = 0; i < 4; i++) + oldLightStatus[i] = Damage.GetLightStatus((eLights)i); + + if(GetUp().z > 0.0f || m_vecMoveSpeed.MagnitudeSqr() > 0.1f){ + float impulseMult = bMoreResistantToDamage ? 0.5f : 4.0f; + + switch(damagedPiece){ + case CAR_PIECE_BUMP_FRONT: + GetComponentWorldPosition(CAR_BUMP_FRONT, pos); + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_BUMPER_FRONT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) + SetBumperDamage(CAR_BUMP_FRONT, VEHBUMPER_FRONT); + if(m_aCarNodes[CAR_BONNET] && Damage.GetPanelStatus(VEHBUMPER_FRONT) == PANEL_STATUS_MISSING){ + case CAR_PIECE_BONNET: + GetComponentWorldPosition(CAR_BONNET, pos); + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(GetModelIndex() != MI_DODO) + if(Damage.ApplyDamage(COMPONENT_DOOR_BONNET, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) + SetDoorDamage(CAR_BONNET, DOOR_BONNET); + } + break; + + case CAR_PIECE_BUMP_REAR: + GetComponentWorldPosition(CAR_BUMP_REAR, pos); + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_BUMPER_REAR, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) + SetBumperDamage(CAR_BUMP_REAR, VEHBUMPER_REAR); + if(m_aCarNodes[CAR_BOOT] && Damage.GetPanelStatus(VEHBUMPER_REAR) == PANEL_STATUS_MISSING){ + case CAR_PIECE_BOOT: + GetComponentWorldPosition(CAR_BOOT, pos); + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_DOOR_BOOT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) + SetDoorDamage(CAR_BOOT, DOOR_BOOT); + } + break; + + case CAR_PIECE_DOOR_LF: + GetComponentWorldPosition(CAR_DOOR_LF, pos); + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_DOOR_FRONT_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) + SetDoorDamage(CAR_DOOR_LF, DOOR_FRONT_LEFT); + break; + case CAR_PIECE_DOOR_RF: + GetComponentWorldPosition(CAR_DOOR_RF, pos); + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_DOOR_FRONT_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) + SetDoorDamage(CAR_DOOR_RF, DOOR_FRONT_RIGHT); + break; + case CAR_PIECE_DOOR_LR: + GetComponentWorldPosition(CAR_DOOR_LR, pos); + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_DOOR_REAR_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) + SetDoorDamage(CAR_DOOR_LR, DOOR_REAR_LEFT); + break; + case CAR_PIECE_DOOR_RR: + GetComponentWorldPosition(CAR_DOOR_RR, pos); + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_DOOR_REAR_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) + SetDoorDamage(CAR_DOOR_RR, DOOR_REAR_RIGHT); + break; + + case CAR_PIECE_WING_LF: + GetComponentWorldPosition(CAR_WING_LF, pos); + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_PANEL_FRONT_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) + SetPanelDamage(CAR_WING_LF, VEHPANEL_FRONT_LEFT); + break; + case CAR_PIECE_WING_RF: + GetComponentWorldPosition(CAR_WING_RF, pos); + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_PANEL_FRONT_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) + SetPanelDamage(CAR_WING_RF, VEHPANEL_FRONT_RIGHT); + break; + case CAR_PIECE_WING_LR: + GetComponentWorldPosition(CAR_WING_LR, pos); + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_PANEL_REAR_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) + SetPanelDamage(CAR_WING_LR, VEHPANEL_REAR_LEFT); + break; + case CAR_PIECE_WING_RR: + GetComponentWorldPosition(CAR_WING_RR, pos); + dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); + if(Damage.ApplyDamage(COMPONENT_PANEL_REAR_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) + SetPanelDamage(CAR_WING_RR, VEHPANEL_REAR_RIGHT); + break; + + case CAR_PIECE_WHEEL_LF: + case CAR_PIECE_WHEEL_LR: + case CAR_PIECE_WHEEL_RF: + case CAR_PIECE_WHEEL_RR: + break; + + case CAR_PIECE_WINDSCREEN: + if(Damage.ApplyDamage(COMPONENT_PANEL_WINDSCREEN, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)){ + uint8 oldStatus = Damage.GetPanelStatus(VEHPANEL_WINDSCREEN); + SetPanelDamage(CAR_WINDSCREEN, VEHPANEL_WINDSCREEN); + if(oldStatus != Damage.GetPanelStatus(VEHPANEL_WINDSCREEN)){ + // DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_WINDSHIELD_CRACK, 0.0f); + } + } + break; + } + } + + float damage = (impulse-minImpulse)*pHandling->fCollisionDamageMultiplier*0.6f*damageMultiplier; + + if(GetModelIndex() == MI_SECURICA && m_pDamageEntity && m_pDamageEntity->GetStatus() == STATUS_PLAYER) + damage *= 7.0f; + + if(GetModelIndex() == MI_RCGOBLIN || GetModelIndex() == MI_RCRAIDER) + damage *= 30.0f; + + if(damage > 0.0f){ + if(damage > 5.0f && + pDriver && + m_pDamageEntity && m_pDamageEntity->IsVehicle() && + (this != FindPlayerVehicle() || ((CVehicle*)m_pDamageEntity)->VehicleCreatedBy == MISSION_VEHICLE) && + ((CVehicle*)m_pDamageEntity)->pDriver){ + if(GetVehicleAppearance() == VEHICLE_APPEARANCE_CAR) + pDriver->Say(SOUND_PED_CRASH_CAR); + else + pDriver->Say(SOUND_PED_CRASH_VEHICLE); + } + + int oldHealth = m_fHealth; + if(this == FindPlayerVehicle()) + m_fHealth -= bTakeLessDamage ? damage/6.0f : damage/2.0f; + else if(bTakeLessDamage) + m_fHealth -= damage/12.0f; + else if(m_pDamageEntity && m_pDamageEntity == FindPlayerVehicle()) + m_fHealth -= damage/1.5f; + else + m_fHealth -= damage/4.0f; + if(m_fHealth <= 0.0f && oldHealth > 0) + m_fHealth = 1.0f; + } + + // play sound if a light broke + for(i = 0; i < 4; i++) + if(oldLightStatus[i] != 1 && Damage.GetLightStatus((eLights)i) == 1){ + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_LIGHT_BREAK, i); // BUG? i? + break; + } + } + + if(m_fHealth < 250.0f){ + // Car is on fire + if(Damage.GetEngineStatus() < ENGINE_STATUS_ON_FIRE){ + // Set engine on fire and remember who did this + Damage.SetEngineStatus(ENGINE_STATUS_ON_FIRE); + m_fFireBlowUpTimer = 0.0f; + m_pSetOnFireEntity = m_pDamageEntity; + if(m_pSetOnFireEntity) + m_pSetOnFireEntity->RegisterReference(&m_pSetOnFireEntity); + } + }else{ + if(GetModelIndex() == MI_BFINJECT){ + if(m_fHealth < 400.0f) + Damage.SetEngineStatus(200); + else if(m_fHealth < 600.0f) + Damage.SetEngineStatus(100); + } + } +} + +void +CAutomobile::dmgDrawCarCollidingParticles(const CVector &pos, float amount) +{ + int i, n; + + if(!GetIsOnScreen()) + return; + + // FindPlayerSpeed() unused + + n = (int)amount/20; + + for(i = 0; i < ((n+4)&0x1F); i++) + CParticle::AddParticle(PARTICLE_SPARK_SMALL, pos, + CVector(CGeneral::GetRandomNumberInRange(-0.1f, 0.1f), + CGeneral::GetRandomNumberInRange(-0.1f, 0.1f), + 0.006f)); + + for(i = 0; i < n+2; i++) + CParticle::AddParticle(PARTICLE_CARCOLLISION_DUST, + CVector(CGeneral::GetRandomNumberInRange(-1.2f, 1.2f) + pos.x, + CGeneral::GetRandomNumberInRange(-1.2f, 1.2f) + pos.y, + pos.z), + CVector(0.0f, 0.0f, 0.0f), nil, 0.5f); + + n = (int)amount/50 + 1; + for(i = 0; i < n; i++) + CParticle::AddParticle(PARTICLE_CAR_DEBRIS, pos, + CVector(CGeneral::GetRandomNumberInRange(-0.25f, 0.25f), + CGeneral::GetRandomNumberInRange(-0.25f, 0.25f), + CGeneral::GetRandomNumberInRange(0.1f, 0.25f)), + nil, + CGeneral::GetRandomNumberInRange(0.02f, 0.08f), + CVehicleModelInfo::ms_vehicleColourTable[m_currentColour1], + CGeneral::GetRandomNumberInRange(-40, 40), + 0, + CGeneral::GetRandomNumberInRange(0, 4)); +} + +float fDamagePosSpeedShift = 0.4f; +float fSpeedMult[] = { + 0.8f, + 0.75f, + 0.85f, + 0.9f, + 0.85f, + 0.85f +}; + +void +CAutomobile::AddDamagedVehicleParticles(void) +{ + int i, n; + + if(this == FindPlayerVehicle() && TheCamera.GetLookingForwardFirstPerson()) + return; + if(this != FindPlayerVehicle() && (CTimer::GetFrameCounter() + m_randomSeed) & 1) + return; + if(m_fHealth >= 650.0f) + return; + + CVector direction = fSpeedMult[5]*m_vecMoveSpeed; + CVector damagePos = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->m_positions[CAR_POS_HEADLIGHTS]; + + switch(Damage.GetDoorStatus(DOOR_BONNET)){ + case DOOR_STATUS_OK: + case DOOR_STATUS_SMASHED: + // Bonnet is still there, smoke comes out at the edge + damagePos += vecDAMAGE_ENGINE_POS_SMALL; + break; + case DOOR_STATUS_SWINGING: + case DOOR_STATUS_MISSING: + // Bonnet is gone, smoke comes out at the engine + damagePos += vecDAMAGE_ENGINE_POS_BIG; + break; + } + + if(GetModelIndex() == MI_BFINJECT) + damagePos = CVector(0.3f, -1.5f, -0.1f); + else if(GetModelIndex() == MI_CADDY) + damagePos = CVector(0.6f, -1.0f, -0.25f); + else if(IsRealHeli()){ + damagePos.x = 0.4f*GetColModel()->boundingBox.max.x; + damagePos.y = 0.2f*GetColModel()->boundingBox.min.y; + damagePos.z = 0.3f*GetColModel()->boundingBox.max.z; + }else + damagePos.z += fDamagePosSpeedShift*(GetColModel()->boundingBox.max.z-damagePos.z) * DotProduct(GetForward(), m_vecMoveSpeed); + damagePos = GetMatrix()*damagePos; + damagePos.z += 0.15f; + + bool electric = pHandling->Transmission.nEngineType == 'E'; + + if(electric && m_fHealth < 320.0f && m_fHealth > 1.0f){ + direction = 0.85f*m_vecMoveSpeed; + direction += GetRight() * CGeneral::GetRandomNumberInRange(0.0f, 0.04f) * (1.0f - 2.0f*m_vecMoveSpeed.Magnitude()); + direction.z += 0.001f; + n = (CGeneral::GetRandomNumber() & 7) + 2; + for(i = 0; i < n; i++) + CParticle::AddParticle(PARTICLE_SPARK_SMALL, damagePos, direction); + if(((CTimer::GetFrameCounter() + m_randomSeed) & 7) == 0) + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, damagePos, 0.8f*m_vecMoveSpeed, nil, 0.1f, 0, 0, 0, 1000); + }else if(electric && m_fHealth < 460.0f){ + direction = 0.85f*m_vecMoveSpeed; + direction += GetRight() * CGeneral::GetRandomNumberInRange(0.0f, 0.04f) * (1.0f - 2.0f*m_vecMoveSpeed.Magnitude()); + direction.z += 0.001f; + n = (CGeneral::GetRandomNumber() & 3) + 1; + for(i = 0; i < n; i++) + CParticle::AddParticle(PARTICLE_SPARK_SMALL, damagePos, direction); + if(((CTimer::GetFrameCounter() + m_randomSeed) & 0xF) == 0) + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, damagePos, 0.8f*m_vecMoveSpeed, nil, 0.1f, 0, 0, 0, 1000); + }else if(m_fHealth < 250.0f){ + // nothing + }else if(m_fHealth < 320.0f){ + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, damagePos, fSpeedMult[0]*direction); + }else if(m_fHealth < 390.0f){ + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, fSpeedMult[1]*direction); + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, damagePos, fSpeedMult[2]*direction); + }else if(m_fHealth < 460.0f){ + if(((CTimer::GetFrameCounter() + m_randomSeed) & 3) == 0 || + ((CTimer::GetFrameCounter() + m_randomSeed) & 3) == 2) + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, fSpeedMult[3]*direction); + }else{ + int rnd = CTimer::GetFrameCounter() + m_randomSeed; + if(rnd < 10 || + rnd < 70 && rnd > 25 || + rnd < 160 && rnd > 100 || + rnd < 200 && rnd > 175 || + rnd > 235) + return; + direction.z += 0.05f*Max(1.0f - 1.6f*m_vecMoveSpeed.Magnitude(), 0.0f); + if(electric){ + direction = 0.85f*m_vecMoveSpeed; + direction += GetRight() * CGeneral::GetRandomNumberInRange(0.0f, 0.04f) * (1.0f - 2.0f*m_vecMoveSpeed.Magnitude()); + direction.z += 0.001f; + n = (CGeneral::GetRandomNumber() & 2) + 2; + for(i = 0; i < n; i++) + CParticle::AddParticle(PARTICLE_SPARK_SMALL, damagePos, direction); + if(((CTimer::GetFrameCounter() + m_randomSeed) & 0xF) == 0) + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, damagePos, 0.8f*m_vecMoveSpeed, nil, 0.1f, 0, 0, 0, 1000); + }else{ + if(TheCamera.GetLookDirection() != LOOKING_FORWARD) + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, direction); + else if(((CTimer::GetFrameCounter() + m_randomSeed) & 1) == 0) + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, fSpeedMult[4]*m_vecMoveSpeed); + } + } +} + +int32 +CAutomobile::AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed) +{ + int i; + CVector dir; + static RwRGBA grassCol = { 8, 24, 8, 255 }; + static RwRGBA gravelCol = { 64, 64, 64, 255 }; + static RwRGBA mudCol = { 64, 32, 16, 255 }; + static RwRGBA sandCol = { 170, 165, 140, 255 }; + static RwRGBA waterCol = { 48, 48, 64, 0 }; + + if(!belowEffectSpeed && + colpoint->surfaceB != SURFACE_SAND && colpoint->surfaceB != SURFACE_SAND_BEACH) + return 0; + + switch(colpoint->surfaceB){ + case SURFACE_GRASS: + dir.x = -0.05f*m_vecMoveSpeed.x; + dir.y = -0.05f*m_vecMoveSpeed.y; + for(i = 0; i < 4; i++){ + dir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.06f); + CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil, + CGeneral::GetRandomNumberInRange(0.02f, 0.1f), grassCol); + } + return 0; + case SURFACE_GRAVEL: + dir.x = -0.05f*m_vecMoveSpeed.x; + dir.y = -0.05f*m_vecMoveSpeed.y; + for(i = 0; i < 4; i++){ + dir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.06f); + CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil, + CGeneral::GetRandomNumberInRange(0.05f, 0.09f), gravelCol); + } + return 1; + case SURFACE_MUD_DRY: + dir.x = -0.05f*m_vecMoveSpeed.x; + dir.y = -0.05f*m_vecMoveSpeed.y; + for(i = 0; i < 4; i++){ + dir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.06f); + CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil, + CGeneral::GetRandomNumberInRange(0.02f, 0.06f), mudCol); + } + return 0; + case SURFACE_SAND: + case SURFACE_SAND_BEACH: + if(CTimer::GetFrameCounter() & 2 || + CWeather::WetRoads > 0.0f && CGeneral::GetRandomNumberInRange(CWeather::WetRoads, 1.01f) > 0.5f) + return 0; + dir.x = 0.5f*m_vecMoveSpeed.x; + dir.y = 0.5f*m_vecMoveSpeed.y; + for(i = 0; i < 1; i++){ + dir.z = CGeneral::GetRandomNumberInRange(0.02f, 0.055f); + CParticle::AddParticle(PARTICLE_SAND, colpoint->point, dir, nil, + 2.0f*m_vecMoveSpeed.Magnitude(), sandCol); + } + return 0; + default: + if(CWeather::WetRoads > 0.01f){ + if(CTimer::GetFrameCounter() & 1) + CParticle::AddParticle( + PARTICLE_WATERSPRAY, + colpoint->point + CVector(0.0f, 0.0f, 0.25f+0.25f), + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.005f, 0.04f)), + nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.5f), waterCol); + return 0; + } + return 1; + } +} + +void +CAutomobile::GetComponentWorldPosition(int32 component, CVector &pos) +{ + if(m_aCarNodes[component] == nil){ + printf("CarNode missing: %d %d\n", GetModelIndex(), component); + return; + } + RwMatrix *ltm = RwFrameGetLTM(m_aCarNodes[component]); + pos = *RwMatrixGetPos(ltm); +} + +bool +CAutomobile::IsComponentPresent(int32 comp) +{ + return m_aCarNodes[comp] != nil; +} + +void +CAutomobile::SetComponentRotation(int32 component, CVector rotation) +{ + CMatrix mat(RwFrameGetMatrix(m_aCarNodes[component])); + CVector pos = mat.GetPosition(); + // BUG: all these set the whole matrix + mat.SetRotateX(DEGTORAD(rotation.x)); + mat.SetRotateY(DEGTORAD(rotation.y)); + mat.SetRotateZ(DEGTORAD(rotation.z)); + mat.Translate(pos); + mat.UpdateRW(); +} + +void +CAutomobile::OpenDoor(int32 component, eDoors door, float openRatio) +{ + CMatrix mat(RwFrameGetMatrix(m_aCarNodes[component])); + CVector pos = mat.GetPosition(); + float axes[3] = { 0.0f, 0.0f, 0.0f }; + float wasClosed = false; + + if(Doors[door].IsClosed()){ + // enable angle cull for closed doors + RwFrameForAllObjects(m_aCarNodes[component], CVehicleModelInfo::ClearAtomicFlagCB, (void*)ATOMIC_FLAG_NOCULL); + wasClosed = true; + } + + Doors[door].Open(openRatio); + + if(wasClosed && Doors[door].RetAngleWhenClosed() != Doors[door].m_fAngle){ + // door opened + HideAllComps(); + // turn off angle cull for swinging door + RwFrameForAllObjects(m_aCarNodes[component], CVehicleModelInfo::SetAtomicFlagCB, (void*)ATOMIC_FLAG_NOCULL); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_DOOR_OPEN_BONNET + door, 0.0f); + } + + if(!wasClosed && openRatio == 0.0f){ + // door closed + if(Damage.GetDoorStatus(door) == DOOR_STATUS_SWINGING) + Damage.SetDoorStatus(door, DOOR_STATUS_OK); // huh? + ShowAllComps(); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_DOOR_CLOSE_BONNET + door, 0.0f); + } + + axes[Doors[door].m_nAxis] = Doors[door].m_fAngle; + mat.SetRotate(axes[0], axes[1], axes[2]); + mat.Translate(pos); + mat.UpdateRW(); +} + +inline void ProcessDoorOpenAnimation(CAutomobile *car, uint32 component, eDoors door, float time, float start, float end) +{ + if(time > start && time < end){ + float ratio = (time - start)/(end - start); + if(car->Doors[door].GetAngleOpenRatio() < ratio) + car->OpenDoor(component, door, ratio); + }else if(time > end){ + car->OpenDoor(component, door, 1.0f); + } +} + +inline void ProcessDoorCloseAnimation(CAutomobile *car, uint32 component, eDoors door, float time, float start, float end) +{ + if(time > start && time < end){ + float ratio = 1.0f - (time - start)/(end - start); + if(car->Doors[door].GetAngleOpenRatio() > ratio) + car->OpenDoor(component, door, ratio); + }else if(time > end){ + car->OpenDoor(component, door, 0.0f); + } +} + +inline void ProcessDoorOpenCloseAnimation(CAutomobile *car, uint32 component, eDoors door, float time, float start, float mid, float end) +{ + if(time > start && time < mid){ + // open + float ratio = (time - start)/(mid - start); + if(car->Doors[door].GetAngleOpenRatio() < ratio) + car->OpenDoor(component, door, ratio); + }else if(time > mid && time < end){ + // close + float ratio = 1.0f - (time - mid)/(end - mid); + if(car->Doors[door].GetAngleOpenRatio() > ratio) + car->OpenDoor(component, door, ratio); + }else if(time > end){ + car->OpenDoor(component, door, 0.0f); + } +} + +void +CAutomobile::ProcessOpenDoor(uint32 component, uint32 anim, float time) +{ + eDoors door; + + switch(component){ + case CAR_DOOR_RF: door = DOOR_FRONT_RIGHT; break; + case CAR_DOOR_RR: door = DOOR_REAR_RIGHT; break; + case CAR_DOOR_LF: door = DOOR_FRONT_LEFT; break; + case CAR_DOOR_LR: door = DOOR_REAR_LEFT; break; + default: assert(0); + } + + if(IsDoorMissing(door)) + return; + + switch(anim){ + case ANIM_STD_QUICKJACK: + case ANIM_STD_CAR_OPEN_DOOR_LHS: + case ANIM_STD_CAR_OPEN_DOOR_RHS: + ProcessDoorOpenAnimation(this, component, door, time, 0.41f, 0.89f); + break; + case ANIM_STD_CAR_CLOSE_DOOR_LHS: + case ANIM_STD_CAR_CLOSE_DOOR_LO_LHS: + case ANIM_STD_CAR_CLOSE_DOOR_RHS: + case ANIM_STD_CAR_CLOSE_DOOR_LO_RHS: + ProcessDoorCloseAnimation(this, component, door, time, 0.2f, 0.45f); + break; + case ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LHS: + case ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LO_LHS: + ProcessDoorOpenCloseAnimation(this, component, door, time, 0.1f, 0.6f, 0.95f); + break; + case ANIM_STD_GETOUT_LHS: + case ANIM_STD_GETOUT_LO_LHS: + case ANIM_STD_GETOUT_RHS: + case ANIM_STD_GETOUT_LO_RHS: + ProcessDoorOpenAnimation(this, component, door, time, 0.06f, 0.43f); + break; + case ANIM_STD_CAR_CLOSE_LHS: + case ANIM_STD_CAR_CLOSE_RHS: + ProcessDoorCloseAnimation(this, component, door, time, 0.1f, 0.23f); + break; + case ANIM_STD_CAR_PULL_OUT_PED_RHS: + case ANIM_STD_CAR_PULL_OUT_PED_LO_RHS: + OpenDoor(component, door, 1.0f); + break; + case ANIM_STD_COACH_OPEN_LHS: + case ANIM_STD_COACH_OPEN_RHS: + ProcessDoorOpenAnimation(this, component, door, time, 0.66f, 0.8f); + break; + case ANIM_STD_COACH_GET_OUT_LHS: + ProcessDoorOpenAnimation(this, component, door, time, 0.0f, 0.3f); + break; + case ANIM_STD_VAN_OPEN_DOOR_REAR_LHS: + case ANIM_STD_VAN_OPEN_DOOR_REAR_RHS: + ProcessDoorOpenAnimation(this, component, door, time, 0.37f, 0.55f); + break; + case ANIM_STD_VAN_CLOSE_DOOR_REAR_LHS: + case ANIM_STD_VAN_CLOSE_DOOR_REAR_RHS: + ProcessDoorCloseAnimation(this, component, door, time, 0.5f, 0.8f); + break; + case ANIM_STD_VAN_GET_OUT_REAR_LHS: + case ANIM_STD_VAN_GET_OUT_REAR_RHS: + ProcessDoorOpenAnimation(this, component, door, time, 0.5f, 0.6f); + break; + case ANIM_STD_NUM: + OpenDoor(component, door, time); + break; + } +} + +bool +CAutomobile::IsDoorReady(eDoors door) +{ + if(Doors[door].IsClosed() || IsDoorMissing(door)) + return true; + int doorflag = 0; + switch(door){ + case DOOR_FRONT_LEFT: doorflag = CAR_DOOR_FLAG_LF; break; + case DOOR_FRONT_RIGHT: doorflag = CAR_DOOR_FLAG_RF; break; + case DOOR_REAR_LEFT: doorflag = CAR_DOOR_FLAG_LR; break; + case DOOR_REAR_RIGHT: doorflag = CAR_DOOR_FLAG_RR; break; + default: break; + } + return (doorflag & m_nGettingInFlags) == 0; +} + +bool +CAutomobile::IsDoorFullyOpen(eDoors door) +{ + return Doors[door].IsFullyOpen() || IsDoorMissing(door); +} + +bool +CAutomobile::IsDoorClosed(eDoors door) +{ + return !!Doors[door].IsClosed(); +} + +bool +CAutomobile::IsDoorMissing(eDoors door) +{ + return Damage.GetDoorStatus(door) == DOOR_STATUS_MISSING; +} + +bool +CAutomobile::IsDoorReady(uint32 door) +{ + switch(door){ + case CAR_DOOR_RF: return IsDoorReady(DOOR_FRONT_RIGHT); + case CAR_DOOR_RR: return IsDoorReady(DOOR_REAR_RIGHT); + case CAR_DOOR_LF: return IsDoorReady(DOOR_FRONT_LEFT); + case CAR_DOOR_LR: return IsDoorReady(DOOR_REAR_LEFT); + default: + return false; + } +} + +bool +CAutomobile::IsDoorMissing(uint32 door) +{ + switch(door){ + case CAR_DOOR_RF: return IsDoorMissing(DOOR_FRONT_RIGHT); + case CAR_DOOR_RR: return IsDoorMissing(DOOR_REAR_RIGHT); + case CAR_DOOR_LF: return IsDoorMissing(DOOR_FRONT_LEFT); + case CAR_DOOR_LR: return IsDoorMissing(DOOR_REAR_LEFT); + default: + return false; + } +} + +bool +CAutomobile::IsOpenTopCar(void) +{ + return GetModelIndex() == MI_STINGER || + // component 0 is assumed to be a roof + GetModelIndex() == MI_COMET && m_aExtras[0] != 0 && m_aExtras[1] != 0 || + GetModelIndex() == MI_STALLION && m_aExtras[0] != 0 && m_aExtras[1] != 0; +} + +void +CAutomobile::RemoveRefsToVehicle(CEntity *ent) +{ + int i; + for(i = 0; i < 4; i++) + if(m_aGroundPhysical[i] == ent) + m_aGroundPhysical[i] = nil; +} + +void +CAutomobile::BlowUpCar(CEntity *culprit) +{ + RpAtomic *atomic; + + if(!bCanBeDamaged) + return; + + if(culprit == FindPlayerPed() || culprit == FindPlayerVehicle()){ + CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel += 20; + CWorld::Players[CWorld::PlayerInFocus].m_fMediaAttention += 10.0f; + CStats::PropertyDestroyed += CGeneral::GetRandomNumber()%6000 + 4000; + } + + // explosion pushes vehicle up + m_vecMoveSpeed.z += 0.13f; + SetStatus(STATUS_WRECKED); + bRenderScorched = true; + m_nTimeOfDeath = CTimer::GetTimeInMilliseconds(); + Damage.FuckCarCompletely(); + + if(GetModelIndex() != MI_RCBANDIT){ + SetBumperDamage(CAR_BUMP_FRONT, VEHBUMPER_FRONT); + SetBumperDamage(CAR_BUMP_REAR, VEHBUMPER_REAR); + SetDoorDamage(CAR_BONNET, DOOR_BONNET); + SetDoorDamage(CAR_BOOT, DOOR_BOOT); + SetDoorDamage(CAR_DOOR_LF, DOOR_FRONT_LEFT); + SetDoorDamage(CAR_DOOR_RF, DOOR_FRONT_RIGHT); + SetDoorDamage(CAR_DOOR_LR, DOOR_REAR_LEFT); + SetDoorDamage(CAR_DOOR_RR, DOOR_REAR_RIGHT); + SpawnFlyingComponent(CAR_WHEEL_LF, COMPGROUP_WHEEL); + atomic = nil; + RwFrameForAllObjects(m_aCarNodes[CAR_WHEEL_LF], GetCurrentAtomicObjectCB, &atomic); + if(atomic) + RpAtomicSetFlags(atomic, 0); + } + + m_fHealth = 0.0f; + m_nBombTimer = 0; + m_bombType = CARBOMB_NONE; + + TheCamera.CamShake(0.7f, GetPosition().x, GetPosition().y, GetPosition().z); + + KillPedsInVehicle(); + + bEngineOn = false; + bLightsOn = false; + m_bSirenOrAlarm = false; + bTaxiLight = false; + if(bIsAmbulanceOnDuty){ + bIsAmbulanceOnDuty = false; + CCarCtrl::NumAmbulancesOnDuty--; + } + if(bIsFireTruckOnDuty){ + bIsFireTruckOnDuty = false; + CCarCtrl::NumFiretrucksOnDuty--; + } + ChangeLawEnforcerState(false); + + gFireManager.StartFire(this, culprit, 0.8f, true); + CDarkel::RegisterCarBlownUpByPlayer(this); + if(GetModelIndex() == MI_RCBANDIT) + CExplosion::AddExplosion(this, culprit, EXPLOSION_CAR_QUICK, GetPosition(), 0); + else + CExplosion::AddExplosion(this, culprit, EXPLOSION_CAR, GetPosition(), 0); +} + +bool +CAutomobile::SetUpWheelColModel(CColModel *colModel) +{ + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + CColModel *vehColModel = mi->GetColModel(); + + if(GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI || + GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE) + return false; + + colModel->boundingSphere = vehColModel->boundingSphere; + colModel->boundingBox = vehColModel->boundingBox; + + CMatrix mat; + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF])); + colModel->spheres[0].Set(mi->m_wheelScale / 2, mat.GetPosition(), SURFACE_RUBBER, CAR_PIECE_WHEEL_LF); + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LB])); + colModel->spheres[1].Set(mi->m_wheelScale / 2, mat.GetPosition(), SURFACE_RUBBER, CAR_PIECE_WHEEL_LR); + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); + colModel->spheres[2].Set(mi->m_wheelScale / 2, mat.GetPosition(), SURFACE_RUBBER, CAR_PIECE_WHEEL_RF); + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RB])); + colModel->spheres[3].Set(mi->m_wheelScale / 2, mat.GetPosition(), SURFACE_RUBBER, CAR_PIECE_WHEEL_RR); + + if(m_aCarNodes[CAR_WHEEL_LM] != nil && m_aCarNodes[CAR_WHEEL_RM] != nil){ + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LM])); + colModel->spheres[4].Set(mi->m_wheelScale / 2, mat.GetPosition(), SURFACE_RUBBER, CAR_PIECE_WHEEL_LR); + mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RM])); + colModel->spheres[5].Set(mi->m_wheelScale / 2, mat.GetPosition(), SURFACE_RUBBER, CAR_PIECE_WHEEL_RR); + colModel->numSpheres = 6; + }else + colModel->numSpheres = 4; + + return true; +} + +float fBurstForceMult = 0.03f; + +void +CAutomobile::BurstTyre(uint8 wheel, bool applyForces) +{ + if(GetModelIndex() == MI_RHINO || bTyresDontBurst) + return; + + switch(wheel){ + case CAR_PIECE_WHEEL_LF: wheel = CARWHEEL_FRONT_LEFT; break; + case CAR_PIECE_WHEEL_RF: wheel = CARWHEEL_FRONT_RIGHT; break; + case CAR_PIECE_WHEEL_LR: wheel = CARWHEEL_REAR_LEFT; break; + case CAR_PIECE_WHEEL_RR: wheel = CARWHEEL_REAR_RIGHT; break; + } + + int status = Damage.GetWheelStatus(wheel); + if(status == WHEEL_STATUS_OK){ + Damage.SetWheelStatus(wheel, WHEEL_STATUS_BURST); + CStats::TyresPopped++; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_TYRE_POP, 0.0f); + + if(GetStatus() == STATUS_SIMPLE){ + SetStatus(STATUS_PHYSICS); + CCarCtrl::SwitchVehicleToRealPhysics(this); + } + + if(applyForces){ + ApplyMoveForce(GetRight() * m_fMass * CGeneral::GetRandomNumberInRange(-fBurstForceMult, fBurstForceMult)); + ApplyTurnForce(GetRight() * m_fTurnMass * CGeneral::GetRandomNumberInRange(-fBurstForceMult, fBurstForceMult), GetForward()); + } + } +} + +bool +CAutomobile::IsRoomForPedToLeaveCar(uint32 component, CVector *doorOffset) +{ + CColPoint colpoint; + CEntity *ent; + colpoint.point = CVector(0.0f, 0.0f, 0.0f); + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + CVector seatPos; + switch(component){ + case CAR_DOOR_RF: + seatPos = mi->GetFrontSeatPosn(); + break; + case CAR_DOOR_LF: + seatPos = mi->GetFrontSeatPosn(); + seatPos.x = -seatPos.x; + break; + case CAR_DOOR_RR: + seatPos = mi->m_positions[CAR_POS_BACKSEAT]; + break; + case CAR_DOOR_LR: + seatPos = mi->m_positions[CAR_POS_BACKSEAT]; + seatPos.x = -seatPos.x; + break; + } + seatPos = GetMatrix() * seatPos; + + CVector doorPos = CPed::GetPositionToOpenCarDoor(this, component); + if(doorOffset){ + CVector off = *doorOffset; + if(component == CAR_DOOR_RF || component == CAR_DOOR_RR) + off.x = -off.x; + doorPos += Multiply3x3(GetMatrix(), off); + } + + if(GetUp().z < 0.0f){ + seatPos.z += 0.5f; + doorPos.z += 0.5f; + } + + CVector dist = doorPos - seatPos; + + // Removing that makes this func. return false for van doors. + doorPos.z += 0.5f; + float length = dist.Magnitude(); + CVector pedPos = seatPos + dist*((length+0.6f)/length); + + if(!CWorld::GetIsLineOfSightClear(seatPos, pedPos, true, false, false, true, false, false)) + return false; + if(CWorld::TestSphereAgainstWorld(doorPos, 0.6f, this, true, true, false, true, false, false)) + return false; + if(CWorld::ProcessVerticalLine(doorPos, 1000.0f, colpoint, ent, true, false, false, true, false, false, nil)) + if(colpoint.point.z > doorPos.z && colpoint.point.z < doorPos.z + 0.6f) + return false; + float upperZ = colpoint.point.z; + if(!CWorld::ProcessVerticalLine(doorPos, -1000.0f, colpoint, ent, true, false, false, true, false, false, nil)) + return false; + if(upperZ != 0.0f && upperZ < colpoint.point.z) + return false; + return true; +} + +float +CAutomobile::GetHeightAboveRoad(void) +{ + return m_fHeightAboveRoad; +} + +void +CAutomobile::PlayCarHorn(void) +{ + uint32 r; + + if (IsAlarmOn() || m_nCarHornTimer != 0) + return; + + if (m_nCarHornDelay) { + m_nCarHornDelay--; + return; + } + + m_nCarHornDelay = (CGeneral::GetRandomNumber() & 0x7F) + 150; + r = m_nCarHornDelay & 7; + if(r < 2){ + m_nCarHornTimer = 45; + }else if(r < 4){ + if(pDriver) + pDriver->Say(SOUND_PED_ANNOYED_DRIVER); + m_nCarHornTimer = 45; + }else{ + if(pDriver) + pDriver->Say(SOUND_PED_ANNOYED_DRIVER); + } +} + +void +CAutomobile::PlayHornIfNecessary(void) +{ + if(AutoPilot.m_bSlowedDownBecauseOfPeds || + AutoPilot.m_bSlowedDownBecauseOfCars) + if(!HasCarStoppedBecauseOfLight()) + PlayCarHorn(); +} + +void +CAutomobile::ResetSuspension(void) +{ + int i; + for(i = 0; i < 4; i++){ + m_aSuspensionSpringRatio[i] = 1.0f; + m_aWheelTimer[i] = 0.0f; + m_aWheelRotation[i] = 0.0f; + m_aWheelState[i] = WHEEL_STATE_NORMAL; + } +} + +void +CAutomobile::SetupSuspensionLines(void) +{ + int i; + CVector posn; + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + CColModel *colModel = mi->GetColModel(); + + // Each suspension line starts at the uppermost wheel position + // and extends down to the lowermost point on the tyre + for(i = 0; i < 4; i++){ + mi->GetWheelPosn(i, posn); + m_aWheelPosition[i] = posn.z; + + // uppermost wheel position + posn.z += pHandling->fSuspensionUpperLimit; + colModel->lines[i].p0 = posn; + + // lowermost wheel position + posn.z += pHandling->fSuspensionLowerLimit - pHandling->fSuspensionUpperLimit; + // lowest point on tyre + posn.z -= mi->m_wheelScale*0.5f; + colModel->lines[i].p1 = posn; + + // this is length of the spring at rest + m_aSuspensionSpringLength[i] = pHandling->fSuspensionUpperLimit - pHandling->fSuspensionLowerLimit; + m_aSuspensionLineLength[i] = colModel->lines[i].p0.z - colModel->lines[i].p1.z; + } + + // Compress spring somewhat to get normal height on road + m_fHeightAboveRoad = m_aSuspensionSpringLength[0]*(1.0f - 1.0f/(4.0f*pHandling->fSuspensionForceLevel)) + - colModel->lines[0].p0.z + mi->m_wheelScale*0.5f; + for(i = 0; i < 4; i++) + m_aWheelPosition[i] = mi->m_wheelScale*0.5f - m_fHeightAboveRoad; + + // adjust col model to include suspension lines + if(colModel->boundingBox.min.z > colModel->lines[0].p1.z) + colModel->boundingBox.min.z = colModel->lines[0].p1.z; + float radius = Max(colModel->boundingBox.min.Magnitude(), colModel->boundingBox.max.Magnitude()); + if(colModel->boundingSphere.radius < radius) + colModel->boundingSphere.radius = radius; + + if(GetModelIndex() == MI_RCBANDIT){ + colModel->boundingSphere.radius = 2.0f; + for(i = 0; i < colModel->numSpheres; i++) + colModel->spheres[i].radius = 0.3f; + } +} + +// called on police cars +void +CAutomobile::ScanForCrimes(void) +{ + if(FindPlayerVehicle() && FindPlayerVehicle()->IsCar()) + if(FindPlayerVehicle()->IsAlarmOn()) + // if player's alarm is on, increase wanted level + if((FindPlayerVehicle()->GetPosition() - GetPosition()).MagnitudeSqr() < sq(20.0f)) + CWorld::Players[CWorld::PlayerInFocus].m_pPed->SetWantedLevelNoDrop(1); +} + +void +CAutomobile::BlowUpCarsInPath(void) +{ + int i; + + if(m_vecMoveSpeed.Magnitude() > 0.1f && bTankDetonateCars) + for(i = 0; i < m_nCollisionRecords; i++) + if(m_aCollisionRecords[i] && + m_aCollisionRecords[i]->IsVehicle() && + m_aCollisionRecords[i]->GetModelIndex() != MI_RHINO && + !m_aCollisionRecords[i]->bRenderScorched){ + if(this == FindPlayerVehicle()) + CEventList::RegisterEvent(EVENT_EXPLOSION, EVENT_ENTITY_VEHICLE, m_aCollisionRecords[i], FindPlayerPed(), 2000); + ((CVehicle*)m_aCollisionRecords[i])->BlowUpCar(this); + } +} + +bool +CAutomobile::HasCarStoppedBecauseOfLight(void) +{ + int i; + + if(GetStatus() != STATUS_SIMPLE && GetStatus() != STATUS_PHYSICS) + return false; + + if(AutoPilot.m_nCurrentRouteNode && AutoPilot.m_nNextRouteNode){ + CPathNode *curnode = &ThePaths.m_pathNodes[AutoPilot.m_nCurrentRouteNode]; + for(i = 0; i < curnode->numLinks; i++) + if(ThePaths.ConnectedNode(curnode->firstLink + i) == AutoPilot.m_nNextRouteNode) + break; + if(i < curnode->numLinks && + ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[curnode->firstLink + i]].trafficLightType & 3) + return true; + } + + if(AutoPilot.m_nCurrentRouteNode && AutoPilot.m_nPrevRouteNode){ + CPathNode *curnode = &ThePaths.m_pathNodes[AutoPilot.m_nCurrentRouteNode]; + for(i = 0; i < curnode->numLinks; i++) + if(ThePaths.ConnectedNode(curnode->firstLink + i) == AutoPilot.m_nPrevRouteNode) + break; + if(i < curnode->numLinks && + ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[curnode->firstLink + i]].trafficLightType & 3) + return true; + } + + return false; +} + +void +CPed::DeadPedMakesTyresBloody(void) +{ + int minX = CWorld::GetSectorIndexX(GetPosition().x - 2.0f); + if (minX < 0) minX = 0; + int minY = CWorld::GetSectorIndexY(GetPosition().y - 2.0f); + if (minY < 0) minY = 0; + int maxX = CWorld::GetSectorIndexX(GetPosition().x + 2.0f); + if (maxX > NUMSECTORS_X-1) maxX = NUMSECTORS_X-1; + int maxY = CWorld::GetSectorIndexY(GetPosition().y + 2.0f); + if (maxY > NUMSECTORS_Y-1) maxY = NUMSECTORS_Y-1; + + CWorld::AdvanceCurrentScanCode(); + + for (int curY = minY; curY <= maxY; curY++) { + for (int curX = minX; curX <= maxX; curX++) { + CSector *sector = CWorld::GetSector(curX, curY); + MakeTyresMuddySectorList(sector->m_lists[ENTITYLIST_VEHICLES]); + MakeTyresMuddySectorList(sector->m_lists[ENTITYLIST_VEHICLES_OVERLAP]); + } + } +} + +void +CPed::MakeTyresMuddySectorList(CPtrList &list) +{ + CAutomobile *car = nil; + CBike *bike = nil; + for (CPtrNode *node = list.first; node; node = node->next) { + CVehicle *veh = (CVehicle*)node->item; + if (veh->m_scanCode != CWorld::GetCurrentScanCode()) { + veh->m_scanCode = CWorld::GetCurrentScanCode(); + + if (Abs(GetPosition().x - veh->GetPosition().x) < 10.0f && Abs(GetPosition().y - veh->GetPosition().y) < 10.0f) { + if (veh->IsCar()) { + bike = nil; + car = (CAutomobile*)veh; + } else if (veh->IsBike()) { + bike = (CBike*)veh; + car = nil; + } + if (veh->m_vecMoveSpeed.MagnitudeSqr2D() > 0.05f) { + if (car) { + for (int wheel = 0; wheel < 4; wheel++) { + if (!car->m_aWheelSkidmarkBloody[wheel] && car->m_aSuspensionSpringRatio[wheel] < 1.0f) { + + CColModel* vehCol = car->GetModelInfo()->GetColModel(); + CVector approxWheelOffset; + switch (wheel) { + case 0: + approxWheelOffset = CVector(-vehCol->boundingBox.max.x, vehCol->boundingBox.max.y, 0.0f); + break; + case 1: + approxWheelOffset = CVector(-vehCol->boundingBox.max.x, vehCol->boundingBox.min.y, 0.0f); + break; + case 2: + approxWheelOffset = CVector(vehCol->boundingBox.max.x, vehCol->boundingBox.max.y, 0.0f); + break; + case 3: + approxWheelOffset = CVector(vehCol->boundingBox.max.x, vehCol->boundingBox.min.y, 0.0f); + break; + default: + break; + } + + // I hope so + CVector wheelPos = car->GetMatrix() * approxWheelOffset; + if (Abs(wheelPos.z - GetPosition().z) < 2.0f) { + + if ((wheelPos - GetPosition()).MagnitudeSqr2D() < 1.0f) { + if (CGame::nastyGame) { + car->m_aWheelSkidmarkBloody[wheel] = true; + DMAudio.PlayOneShot(car->m_audioEntityId, SOUND_SPLATTER, 0.0f); + } + if (car->m_fMass > 500.f) { + car->ApplyMoveForce(CVector(0.0f, 0.0f, 50.0f * Min(1.0f, m_fMass * 0.001f))); + + CVector vehAndWheelDist = wheelPos - car->GetPosition(); + car->ApplyTurnForce(CVector(0.0f, 0.0f, 50.0f * Min(1.0f, m_fTurnMass * 0.0005f)), vehAndWheelDist); + if (car == FindPlayerVehicle()) { + CPad::GetPad(0)->StartShake(300, 70); + } + } + } + } + } + } + } else if (bike) { + for (int wheel = 0; wheel < 2; wheel++) { + if (!bike->m_aWheelSkidmarkBloody[wheel] && bike->m_aSuspensionSpringRatio[wheel] < 1.0f) { + + CColModel* vehCol = bike->GetModelInfo()->GetColModel(); + CVector approxWheelOffset; + switch (wheel) { + case 0: + approxWheelOffset = CVector(0.0f, 0.8f * vehCol->boundingBox.max.y, 0.0f); + break; + case 1: + approxWheelOffset = CVector(0.0f, 0.8f * vehCol->boundingBox.min.y, 0.0f); + default: + break; + } + + // I hope so + CVector wheelPos = bike->GetMatrix() * approxWheelOffset; + if (Abs(wheelPos.z - GetPosition().z) < 2.0f) { + + if ((wheelPos - GetPosition()).MagnitudeSqr2D() < 1.0f) { + if (CGame::nastyGame) { + bike->m_aWheelSkidmarkBloody[wheel] = true; + DMAudio.PlayOneShot(bike->m_audioEntityId, SOUND_SPLATTER, 0.0f); + } + if (bike->m_fMass > 100.0f) { + bike->ApplyMoveForce(CVector(0.0f, 0.0f, 10.0f)); + + CVector vehAndWheelDist = wheelPos - bike->GetPosition(); + bike->ApplyTurnForce(CVector(0.0f, 0.0f, 10.0f), vehAndWheelDist); + + if (bike == FindPlayerVehicle()) { + CPad::GetPad(0)->StartShake(300, 70); + } + } + } + } + } + } + } + } + } + } + } +} + +void +CAutomobile::SetBusDoorTimer(uint32 timer, uint8 type) +{ + if(timer < 1000) + timer = 1000; + if(type == 0) + // open and close + m_nBusDoorTimerStart = CTimer::GetTimeInMilliseconds(); + else + // only close + m_nBusDoorTimerStart = CTimer::GetTimeInMilliseconds() - 500; + m_nBusDoorTimerEnd = m_nBusDoorTimerStart + timer; +} + +void +CAutomobile::ProcessAutoBusDoors(void) +{ + if(CTimer::GetTimeInMilliseconds() < m_nBusDoorTimerEnd){ + if(m_nBusDoorTimerEnd != 0 && CTimer::GetTimeInMilliseconds() > m_nBusDoorTimerEnd-500){ + // close door + if(!IsDoorMissing(DOOR_FRONT_LEFT) && (m_nGettingInFlags & CAR_DOOR_FLAG_LF) == 0){ + if(IsDoorClosed(DOOR_FRONT_LEFT)){ + m_nBusDoorTimerEnd = CTimer::GetTimeInMilliseconds(); + OpenDoor(CAR_DOOR_LF, DOOR_FRONT_LEFT, 0.0f); + }else{ + OpenDoor(CAR_DOOR_LF, DOOR_FRONT_LEFT, + 1.0f - (CTimer::GetTimeInMilliseconds() - (m_nBusDoorTimerEnd-500))/500.0f); + } + } + + if(!IsDoorMissing(DOOR_FRONT_RIGHT) && (m_nGettingInFlags & CAR_DOOR_FLAG_RF) == 0){ + if(IsDoorClosed(DOOR_FRONT_RIGHT)){ + m_nBusDoorTimerEnd = CTimer::GetTimeInMilliseconds(); + OpenDoor(CAR_DOOR_RF, DOOR_FRONT_RIGHT, 0.0f); + }else{ + OpenDoor(CAR_DOOR_RF, DOOR_FRONT_RIGHT, + 1.0f - (CTimer::GetTimeInMilliseconds() - (m_nBusDoorTimerEnd-500))/500.0f); + } + } + } + }else{ + // ended + if(m_nBusDoorTimerStart){ + if(!IsDoorMissing(DOOR_FRONT_LEFT) && (m_nGettingInFlags & CAR_DOOR_FLAG_LF) == 0) + OpenDoor(CAR_DOOR_LF, DOOR_FRONT_LEFT, 0.0f); + if(!IsDoorMissing(DOOR_FRONT_RIGHT) && (m_nGettingInFlags & CAR_DOOR_FLAG_RF) == 0) + OpenDoor(CAR_DOOR_RF, DOOR_FRONT_RIGHT, 0.0f); + m_nBusDoorTimerStart = 0; + m_nBusDoorTimerEnd = 0; + } + } +} + +void +CAutomobile::ProcessSwingingDoor(int32 component, eDoors door) +{ + if(Damage.GetDoorStatus(door) != DOOR_STATUS_SWINGING) + return; + + if (m_aCarNodes[component] == nil) + return; + + CMatrix mat(RwFrameGetMatrix(m_aCarNodes[component])); + CVector pos = mat.GetPosition(); + float axes[3] = { 0.0f, 0.0f, 0.0f }; + + Doors[door].Process(this); + axes[Doors[door].m_nAxis] = Doors[door].m_fAngle; + mat.SetRotate(axes[0], axes[1], axes[2]); + mat.Translate(pos); + mat.UpdateRW(); + + // make wind rip off bonnet + if(door == DOOR_BONNET && Doors[door].m_nDoorState == DOORST_OPEN && + DotProduct(m_vecMoveSpeed, GetForward()) > 0.4f){ +#ifdef FIX_BUGS + CObject *comp = SpawnFlyingComponent(CAR_BONNET, COMPGROUP_BONNET); +#else + CObject *comp = SpawnFlyingComponent(CAR_BONNET, COMPGROUP_DOOR); +#endif + // make both doors invisible on car + SetComponentVisibility(m_aCarNodes[CAR_BONNET], ATOMIC_FLAG_NONE); + Damage.SetDoorStatus(DOOR_BONNET, DOOR_STATUS_MISSING); + + if(comp){ + if(CGeneral::GetRandomNumber() & 1) + comp->m_vecMoveSpeed = 0.4f*m_vecMoveSpeed + 0.1f*GetRight() + 0.5f*GetUp(); + else + comp->m_vecMoveSpeed = 0.4f*m_vecMoveSpeed - 0.1f*GetRight() + 0.5f*GetUp(); + comp->ApplyTurnForce(10.0f*GetUp(), GetForward()); + } + } +} + +void +CAutomobile::Fix(void) +{ + int component; + + Damage.ResetDamageStatus(); + + if(pHandling->Flags & HANDLING_NO_DOORS){ + Damage.SetDoorStatus(DOOR_FRONT_LEFT, DOOR_STATUS_MISSING); + Damage.SetDoorStatus(DOOR_FRONT_RIGHT, DOOR_STATUS_MISSING); + Damage.SetDoorStatus(DOOR_REAR_LEFT, DOOR_STATUS_MISSING); + Damage.SetDoorStatus(DOOR_REAR_RIGHT, DOOR_STATUS_MISSING); + } + + bIsDamaged = false; + RpClumpForAllAtomics((RpClump*)m_rwObject, CVehicleModelInfo::HideAllComponentsAtomicCB, (void*)ATOMIC_FLAG_DAM); + + for(component = CAR_BUMP_FRONT; component < NUM_CAR_NODES; component++){ + if(m_aCarNodes[component]){ + CMatrix mat(RwFrameGetMatrix(m_aCarNodes[component])); + mat.SetTranslate(mat.GetPosition()); + mat.UpdateRW(); + } + } + + for(component = 0; component < 4; component++) + Damage.SetWheelStatus(component, WHEEL_STATUS_OK); + + if(GetModelIndex() == MI_HUNTER){ + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB]), 0); + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RB]), 0); + }else if(IsRealHeli()){ + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LF]), 0); + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF]), 0); + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB]), 0); + RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RB]), 0); + } +} + +void +CAutomobile::SetupDamageAfterLoad(void) +{ + if(m_aCarNodes[CAR_BUMP_FRONT]) + SetBumperDamage(CAR_BUMP_FRONT, VEHBUMPER_FRONT); + if(m_aCarNodes[CAR_BONNET]) + SetDoorDamage(CAR_BONNET, DOOR_BONNET); + if(m_aCarNodes[CAR_BUMP_REAR]) + SetBumperDamage(CAR_BUMP_REAR, VEHBUMPER_REAR); + if(m_aCarNodes[CAR_BOOT]) + SetDoorDamage(CAR_BOOT, DOOR_BOOT); + if(m_aCarNodes[CAR_DOOR_LF]) + SetDoorDamage(CAR_DOOR_LF, DOOR_FRONT_LEFT); + if(m_aCarNodes[CAR_DOOR_RF]) + SetDoorDamage(CAR_DOOR_RF, DOOR_FRONT_RIGHT); + if(m_aCarNodes[CAR_DOOR_LR]) + SetDoorDamage(CAR_DOOR_LR, DOOR_REAR_LEFT); + if(m_aCarNodes[CAR_DOOR_RR]) + SetDoorDamage(CAR_DOOR_RR, DOOR_REAR_RIGHT); + if(m_aCarNodes[CAR_WING_LF]) + SetPanelDamage(CAR_WING_LF, VEHPANEL_FRONT_LEFT); + if(m_aCarNodes[CAR_WING_RF]) + SetPanelDamage(CAR_WING_RF, VEHPANEL_FRONT_RIGHT); + if(m_aCarNodes[CAR_WING_LR]) + SetPanelDamage(CAR_WING_LR, VEHPANEL_REAR_LEFT); + if(m_aCarNodes[CAR_WING_RR]) + SetPanelDamage(CAR_WING_RR, VEHPANEL_REAR_RIGHT); +} + +RwObject* +GetCurrentAtomicObjectCB(RwObject *object, void *data) +{ + RpAtomic *atomic = (RpAtomic*)object; + assert(RwObjectGetType(object) == rpATOMIC); + if(RpAtomicGetFlags(atomic) & rpATOMICRENDER) + *(RpAtomic**)data = atomic; + return object; +} + +CObject* +CAutomobile::SpawnFlyingComponent(int32 component, uint32 type) +{ + RpAtomic *atomic; + RwFrame *frame; + RwMatrix *matrix; + CObject *obj; + + if(CObject::nNoTempObjects >= NUMTEMPOBJECTS) + return nil; + + atomic = nil; + RwFrameForAllObjects(m_aCarNodes[component], GetCurrentAtomicObjectCB, &atomic); + if(atomic == nil) + return nil; + + obj = new CObject(); + if(obj == nil) + return nil; + + if(component == CAR_WINDSCREEN){ + obj->SetModelIndexNoCreate(MI_CAR_BONNET); + }else switch(type){ + case COMPGROUP_BUMPER: + obj->SetModelIndexNoCreate(MI_CAR_BUMPER); + break; + case COMPGROUP_WHEEL: + obj->SetModelIndexNoCreate(MI_CAR_WHEEL); + break; + case COMPGROUP_DOOR: + obj->SetModelIndexNoCreate(MI_CAR_DOOR); + obj->SetCenterOfMass(0.0f, -0.5f, 0.0f); + obj->bDrawLast = true; + break; + case COMPGROUP_BONNET: + obj->SetModelIndexNoCreate(MI_CAR_BONNET); + obj->SetCenterOfMass(0.0f, 0.4f, 0.0f); + break; + case COMPGROUP_BOOT: + obj->SetModelIndexNoCreate(MI_CAR_BOOT); + obj->SetCenterOfMass(0.0f, -0.3f, 0.0f); + break; + case COMPGROUP_PANEL: + default: + obj->SetModelIndexNoCreate(MI_CAR_PANEL); + break; + } + + // object needs base model + obj->RefModelInfo(GetModelIndex()); + + // create new atomic + matrix = RwFrameGetLTM(m_aCarNodes[component]); + frame = RwFrameCreate(); + atomic = RpAtomicClone(atomic); + *RwFrameGetMatrix(frame) = *matrix; + RpAtomicSetFrame(atomic, frame); + CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); + obj->AttachToRwObject((RwObject*)atomic); + obj->bDontStream = true; + + // init object + obj->m_fMass = 10.0f; + obj->m_fTurnMass = 25.0f; + obj->m_fAirResistance = 0.97f; + obj->m_fElasticity = 0.1f; + obj->m_fBuoyancy = obj->m_fMass*GRAVITY/0.75f; + obj->ObjectCreatedBy = TEMP_OBJECT; + obj->SetIsStatic(false); + obj->bIsPickup = false; + obj->bUseVehicleColours = true; + obj->m_colour1 = m_currentColour1; + obj->m_colour2 = m_currentColour2; + + // life time - the more objects the are, the shorter this one will live + CObject::nNoTempObjects++; + if(CObject::nNoTempObjects > 20) + obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 20000/5.0f; + else if(CObject::nNoTempObjects > 10) + obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 20000/2.0f; + else + obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 20000; + + obj->m_vecMoveSpeed = m_vecMoveSpeed; + if(obj->m_vecMoveSpeed.z > 0.0f){ + obj->m_vecMoveSpeed.z *= 1.5f; + }else if(GetUp().z > 0.0f && + (component == COMPGROUP_BONNET || component == COMPGROUP_BOOT || component == CAR_WINDSCREEN)){ + obj->m_vecMoveSpeed.z *= -1.5f; + obj->m_vecMoveSpeed.z += 0.04f; + }else{ + obj->m_vecMoveSpeed.z *= 0.25f; + } + obj->m_vecMoveSpeed.x *= 0.75f; + obj->m_vecMoveSpeed.y *= 0.75f; + + obj->m_vecTurnSpeed = m_vecTurnSpeed*2.0f; + + // push component away from car + CVector dist = obj->GetPosition() - GetPosition(); + dist.Normalise(); + if(component == COMPGROUP_BONNET || component == COMPGROUP_BOOT || component == CAR_WINDSCREEN){ + // push these up some + dist += GetUp(); + if(GetUp().z > 0.0f){ + // simulate fast upward movement if going fast + float speed = CVector2D(m_vecMoveSpeed).Magnitude(); + obj->GetMatrix().Translate(GetUp()*speed); + } + } + obj->ApplyMoveForce(dist); + + if(type == COMPGROUP_WHEEL){ + obj->m_fTurnMass = 5.0f; + obj->m_vecTurnSpeed.x = 0.5f; + obj->m_fAirResistance = 0.99f; + } + + if(GetStatus() == STATUS_WRECKED && IsVisible() && DotProduct(dist, TheCamera.GetPosition() - GetPosition()) > -0.5f){ + dist = TheCamera.GetPosition() - GetPosition(); + dist.Normalise(); + dist.z += 0.3f; + ApplyMoveForce(5.0f*dist); + } + + if(CCollision::ProcessColModels(obj->GetMatrix(), *obj->GetColModel(), + this->GetMatrix(), *this->GetColModel(), + CWorld::m_aTempColPts, nil, nil) > 0) + obj->m_pCollidingEntity = this; + + if(bRenderScorched) + obj->bRenderScorched = true; + + CWorld::Add(obj); + + return obj; +} + +CObject* +CAutomobile::RemoveBonnetInPedCollision(void) +{ + CObject *obj; + + if(Damage.GetDoorStatus(DOOR_BONNET) == DOOR_STATUS_SWINGING && + Doors[DOOR_BONNET].RetAngleWhenOpen()*0.4f < Doors[DOOR_BONNET].m_fAngle){ +#ifdef FIX_BUGS + obj = SpawnFlyingComponent(CAR_BONNET, COMPGROUP_BONNET); +#else + obj = SpawnFlyingComponent(CAR_BONNET, COMPGROUP_DOOR); +#endif + // make both doors invisible on car + SetComponentVisibility(m_aCarNodes[CAR_BONNET], ATOMIC_FLAG_NONE); + Damage.SetDoorStatus(DOOR_BONNET, DOOR_STATUS_MISSING); + return obj; + } + return nil; +} + +void +CAutomobile::SetPanelDamage(int32 component, ePanels panel, bool noFlyingComponents) +{ + int status = Damage.GetPanelStatus(panel); + if(m_aCarNodes[component] == nil) + return; + if(status == PANEL_STATUS_SMASHED1){ + if(panel == VEHPANEL_WINDSCREEN) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_WINDSHIELD_CRACK, 0.0f); + // show damaged part + SetComponentVisibility(m_aCarNodes[component], ATOMIC_FLAG_DAM); + }else if(status == PANEL_STATUS_MISSING){ + if(!noFlyingComponents) + SpawnFlyingComponent(component, COMPGROUP_PANEL); + else if(panel == VEHPANEL_WINDSCREEN) + CGlass::CarWindscreenShatters(this, false); + // hide both + SetComponentVisibility(m_aCarNodes[component], ATOMIC_FLAG_NONE); + } +} + +void +CAutomobile::SetBumperDamage(int32 component, ePanels panel, bool noFlyingComponents) +{ + int status = Damage.GetPanelStatus(panel); + if(m_aCarNodes[component] == nil){ + printf("Trying to damage component %d of %s\n", + component, CModelInfo::GetModelInfo(GetModelIndex())->GetModelName()); + return; + } + if(status == PANEL_STATUS_SMASHED1){ + // show damaged part + SetComponentVisibility(m_aCarNodes[component], ATOMIC_FLAG_DAM); + }else if(status == PANEL_STATUS_MISSING){ + if(!noFlyingComponents) + SpawnFlyingComponent(component, COMPGROUP_BUMPER); + // hide both + SetComponentVisibility(m_aCarNodes[component], ATOMIC_FLAG_NONE); + } +} + +void +CAutomobile::SetDoorDamage(int32 component, eDoors door, bool noFlyingComponents) +{ + int status = Damage.GetDoorStatus(door); + if(m_aCarNodes[component] == nil){ + printf("Trying to damage component %d of %s\n", + component, CModelInfo::GetModelInfo(GetModelIndex())->GetModelName()); + return; + } + + if(!CanDoorsBeDamaged() && status > DOOR_STATUS_SMASHED && door != DOOR_BONNET && door != DOOR_BOOT){ + Damage.SetDoorStatus(door, DOOR_STATUS_SMASHED); + status = DOOR_STATUS_SMASHED; + } + + if(door == DOOR_BOOT && status == DOOR_STATUS_SWINGING && pHandling->Flags & HANDLING_NOSWING_BOOT){ + Damage.SetDoorStatus(DOOR_BOOT, DOOR_STATUS_MISSING); + status = DOOR_STATUS_MISSING; + } + + switch(status){ + case DOOR_STATUS_SMASHED: + // show damaged part + SetComponentVisibility(m_aCarNodes[component], ATOMIC_FLAG_DAM); + break; + case DOOR_STATUS_SWINGING: + // turn off angle cull for swinging doors + RwFrameForAllObjects(m_aCarNodes[component], CVehicleModelInfo::SetAtomicFlagCB, (void*)ATOMIC_FLAG_NOCULL); + break; + case DOOR_STATUS_MISSING: + if(!noFlyingComponents){ + if(door == DOOR_BONNET) + SpawnFlyingComponent(component, COMPGROUP_BONNET); + else if(door == DOOR_BOOT) + SpawnFlyingComponent(component, COMPGROUP_BOOT); + else + SpawnFlyingComponent(component, COMPGROUP_DOOR); + } + // hide both + SetComponentVisibility(m_aCarNodes[component], ATOMIC_FLAG_NONE); + break; + } +} + + +static RwObject* +SetVehicleAtomicVisibilityCB(RwObject *object, void *data) +{ + uint32 flags = (uint32)(uintptr)data; + RpAtomic *atomic = (RpAtomic*)object; + if((CVisibilityPlugins::GetAtomicId(atomic) & (ATOMIC_FLAG_OK|ATOMIC_FLAG_DAM)) == flags) + RpAtomicSetFlags(atomic, rpATOMICRENDER); + else + RpAtomicSetFlags(atomic, 0); + return object; +} + +void +CAutomobile::SetComponentVisibility(RwFrame *frame, uint32 flags) +{ + HideAllComps(); + bIsDamaged = true; + RwFrameForAllObjects(frame, SetVehicleAtomicVisibilityCB, (void*)flags); +} + +void +CAutomobile::SetupModelNodes(void) +{ + int i; + for(i = 0; i < NUM_CAR_NODES; i++) + m_aCarNodes[i] = nil; + CClumpModelInfo::FillFrameArray(GetClump(), m_aCarNodes); +} + +void +CAutomobile::SetTaxiLight(bool light) +{ + bTaxiLight = light; +} + +bool +CAutomobile::GetAllWheelsOffGround(void) +{ + return m_nDriveWheelsOnGround == 0; +} + +void +CAutomobile::HideAllComps(void) +{ + // empty +} + +void +CAutomobile::ShowAllComps(void) +{ + // empty +} + +void +CAutomobile::ReduceHornCounter(void) +{ + if(m_nCarHornTimer != 0) + m_nCarHornTimer--; +} + +void +CAutomobile::SetAllTaxiLights(bool set) +{ + m_sAllTaxiLights = set; +} + +void +CAutomobile::TellHeliToGoToCoors(float x, float y, float z, uint8 speed) +{ + AutoPilot.m_nCarMission = MISSION_HELI_FLYTOCOORS; + AutoPilot.m_vecDestinationCoors.x = x; + AutoPilot.m_vecDestinationCoors.y = y; + AutoPilot.m_vecDestinationCoors.z = z; + AutoPilot.m_nCruiseSpeed = speed; + SetStatus(STATUS_PHYSICS); + + if(m_fOrientation == 0.0f){ + m_fOrientation = CGeneral::GetATanOfXY(GetForward().x, GetForward().y) + PI; + while(m_fOrientation > TWOPI) m_fOrientation -= TWOPI; + } +} + +void +CAutomobile::TellPlaneToGoToCoors(float x, float y, float z, uint8 speed) +{ + AutoPilot.m_nCarMission = MISSION_PLANE_FLYTOCOORS; + AutoPilot.m_vecDestinationCoors.x = x; + AutoPilot.m_vecDestinationCoors.y = y; + AutoPilot.m_vecDestinationCoors.z = z; + AutoPilot.m_nCruiseSpeed = speed; + SetStatus(STATUS_PHYSICS); + + if(m_fOrientation == 0.0f) + m_fOrientation = CGeneral::GetATanOfXY(GetForward().x, GetForward().y); +} + +void +CAutomobile::PopBoot(void) +{ + switch(Damage.GetDoorStatus(DOOR_BOOT)){ + case DOOR_STATUS_OK: + case DOOR_STATUS_SMASHED: + Doors[DOOR_BOOT].m_fAngle = Doors[DOOR_BOOT].m_fMinAngle; + CMatrix mat(RwFrameGetMatrix(m_aCarNodes[CAR_BOOT])); + CVector pos = mat.GetPosition(); + float axes[3] = { 0.0f, 0.0f, 0.0f }; + axes[Doors[DOOR_BOOT].m_nAxis] = Doors[DOOR_BOOT].m_fAngle; + mat.SetRotate(axes[0], axes[1], axes[2]); + mat.Translate(pos); + mat.UpdateRW(); + } +} + +void +CAutomobile::PopBootUsingPhysics(void) +{ + switch(Damage.GetDoorStatus(DOOR_BOOT)) + case DOOR_STATUS_OK: + case DOOR_STATUS_SMASHED: + Damage.SetDoorStatus(DOOR_BOOT, DOOR_STATUS_SWINGING); + Doors[DOOR_BOOT].m_fAngVel = -2.0f; +} + +void +CAutomobile::CloseAllDoors(void) +{ + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + if(!IsDoorMissing(DOOR_FRONT_LEFT)) + OpenDoor(CAR_DOOR_LF, DOOR_FRONT_LEFT, 0.0f); + if(mi->m_numDoors > 1){ + if(!IsDoorMissing(DOOR_FRONT_RIGHT)) + OpenDoor(CAR_DOOR_RF, DOOR_FRONT_RIGHT, 0.0f); + if(mi->m_numDoors > 2){ + if(!IsDoorMissing(DOOR_REAR_LEFT)) + OpenDoor(CAR_DOOR_LR, DOOR_REAR_LEFT, 0.0f); + if(!IsDoorMissing(DOOR_REAR_RIGHT)) + OpenDoor(CAR_DOOR_RR, DOOR_REAR_RIGHT, 0.0f); + } + } +} + +CPed* +CAutomobile::KnockPedOutCar(eWeaponType weapon, uint16 door, CPed *ped) +{ + AnimationId anim = ANIM_STD_KO_FRONT; + if(ped == nil) + return nil; + + ped->m_vehDoor = door; + ped->SetPedState(PED_IDLE); + CAnimManager::BlendAnimation(ped->GetClump(), ped->m_animGroup, ANIM_STD_IDLE, 100.0f); + CPed::PedSetOutCarCB(nil, ped); + ped->SetMoveState(PEDMOVE_STILL); + if(GetUp().z < 0.0f) + ped->SetHeading(CGeneral::LimitRadianAngle(GetForward().Heading() + PI)); + else + ped->SetHeading(GetForward().Heading()); + + switch(weapon){ + case WEAPONTYPE_UNARMED: + case WEAPONTYPE_UNIDENTIFIED: + ped->m_vecMoveSpeed = m_vecMoveSpeed; + ped->m_pCollidingEntity = this; + anim = ANIM_STD_NUM; + break; + + case WEAPONTYPE_BASEBALLBAT: + case WEAPONTYPE_RAMMEDBYCAR: + case WEAPONTYPE_FALL: + ped->m_vecMoveSpeed = m_vecMoveSpeed; + anim = ANIM_STD_SPINFORWARD_LEFT; + ApplyMoveForce(4.0f*GetUp() + 8.0f*GetRight()); + break; + } + + if(weapon != WEAPONTYPE_UNARMED){ + ped->SetFall(1000, anim, 0); + ped->bIsStanding = false; + ped->m_headingRate = 0.0f; + } + ped->m_pMyVehicle = nil; + return ped; +} + +#ifdef COMPATIBLE_SAVES +void +CAutomobile::Save(uint8*& buf) +{ + CVehicle::Save(buf); + WriteSaveBuf(buf, Damage); + ZeroSaveBuf(buf, 1500 - 672 - sizeof(CDamageManager)); +} + +void +CAutomobile::Load(uint8*& buf) +{ + CVehicle::Load(buf); + ReadSaveBuf(&Damage, buf); + SkipSaveBuf(buf, 1500 - 672 - sizeof(CDamageManager)); + SetupDamageAfterLoad(); +} +#endif diff --git a/src/miami/vehicles/Automobile.h b/src/miami/vehicles/Automobile.h new file mode 100644 index 00000000..f4046e9e --- /dev/null +++ b/src/miami/vehicles/Automobile.h @@ -0,0 +1,175 @@ +#pragma once + +#include "Vehicle.h" +#include "DamageManager.h" +#include "Door.h" +#include "Skidmarks.h" + +class CObject; + +enum { + CARWHEEL_FRONT_LEFT, + CARWHEEL_REAR_LEFT, + CARWHEEL_FRONT_RIGHT, + CARWHEEL_REAR_RIGHT +}; + + +class CAutomobile : public CVehicle +{ +public: + CDamageManager Damage; + CDoor Doors[6]; + RwFrame *m_aCarNodes[NUM_CAR_NODES]; + CColPoint m_aWheelColPoints[4]; + float m_aSuspensionSpringRatio[4]; + float m_aSuspensionSpringRatioPrev[4]; + float m_aWheelTimer[4]; // set to 4.0 when wheel is touching ground, then decremented + float m_auto_unused1; + eSkidmarkType m_aWheelSkidmarkType[4]; + bool m_aWheelSkidmarkBloody[4]; + bool m_aWheelSkidmarkUnk[4]; + float m_aWheelRotation[4]; + float m_aWheelPosition[4]; + float m_aWheelSpeed[4]; + uint8 m_auto_unused2; +#if (defined GTA_PS2 && !defined FIX_BUGS) + uint8 m_bombType : 3; +#endif + uint8 bTaxiLight : 1; + uint8 bFixedColour : 1; + uint8 bBigWheels : 1; + uint8 bWaterTight : 1; // no damage for non-player peds + uint8 bNotDamagedUpsideDown : 1; + uint8 bMoreResistantToDamage : 1; + uint8 bTankDetonateCars : 1; + uint8 bStuckInSand : 1; + uint8 bHeliDestroyed : 1; +#if (defined GTA_PS2 && !defined FIX_BUGS) + CEntity* m_pBombRigger; +#endif + int16 m_doingBurnout; + uint16 m_hydraulicState; + uint32 m_nBusDoorTimerEnd; + uint32 m_nBusDoorTimerStart; + float m_aSuspensionSpringLength[4]; + float m_aSuspensionLineLength[4]; + float m_fHeightAboveRoad; + float m_fTraction; + float m_fTireTemperature; + float m_fOrientation; // for heli and plane go-to + float m_fPlaneSteer; // related to the above + float m_fVelocityChangeForAudio; + float m_randomValues[6]; // used for what? + float m_fFireBlowUpTimer; + CPhysical *m_aGroundPhysical[4]; // physicals touching wheels + CVector m_aGroundOffset[4]; // from ground object to colpoint + CEntity *m_pSetOnFireEntity; + float m_weaponDoorTimerLeft; // still don't know what exactly this is + float m_weaponDoorTimerRight; + float m_fCarGunLR; + float m_fCarGunUD; + float m_fHeliOrientation; + float m_fPropellerRotation; + uint8 stuff4[4]; + uint8 m_nWheelsOnGround; + uint8 m_nDriveWheelsOnGround; + uint8 m_nDriveWheelsOnGroundPrev; + float m_fGasPedalAudio; + tWheelState m_aWheelState[4]; + + static bool m_sAllTaxiLights; + + CAutomobile(int32 id, uint8 CreatedBy); + + // from CEntity + void SetModelIndex(uint32 id); + void ProcessControl(void); + void Teleport(CVector v); + void PreRender(void); + void Render(void); + + // from CPhysical + int32 ProcessEntityCollision(CEntity *ent, CColPoint *colpoints); + + // from CVehicle + void ProcessControlInputs(uint8); + void GetComponentWorldPosition(int32 component, CVector &pos); + bool IsComponentPresent(int32 component); + void SetComponentRotation(int32 component, CVector rotation); + void OpenDoor(int32 component, eDoors door, float openRatio); + void ProcessOpenDoor(uint32, uint32, float); + bool IsDoorReady(eDoors door); + bool IsDoorFullyOpen(eDoors door); + bool IsDoorClosed(eDoors door); + bool IsDoorMissing(eDoors door); + bool IsDoorReady(uint32 door); + bool IsDoorMissing(uint32 door); + bool IsOpenTopCar(void); + void RemoveRefsToVehicle(CEntity *ent); + void BlowUpCar(CEntity *ent); + bool SetUpWheelColModel(CColModel *colModel); + void BurstTyre(uint8 tyre, bool applyForces); + bool IsRoomForPedToLeaveCar(uint32 component, CVector *doorOffset); + float GetHeightAboveRoad(void); + void PlayCarHorn(void); + + void FireTruckControl(void); + void TankControl(void); + void HydraulicControl(void); + void VehicleDamage(float impulse, uint16 damagedPiece); + void ProcessBuoyancy(void); + void DoDriveByShootings(void); + void DoHoverSuspensionRatios(void); + int32 RcbanditCheckHitWheels(void); + int32 RcbanditCheck1CarWheels(CPtrList &list); + void PlaceOnRoadProperly(void); + void dmgDrawCarCollidingParticles(const CVector &pos, float amount); + void AddDamagedVehicleParticles(void); + int32 AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed); + void PlayHornIfNecessary(void); + void ResetSuspension(void); + void SetupSuspensionLines(void); + void ScanForCrimes(void); + void BlowUpCarsInPath(void); + bool HasCarStoppedBecauseOfLight(void); + void SetBusDoorTimer(uint32 timer, uint8 type); + void ProcessAutoBusDoors(void); + void ProcessSwingingDoor(int32 component, eDoors door); + void SetupDamageAfterLoad(void); + CObject *SpawnFlyingComponent(int32 component, uint32 type); + CObject *RemoveBonnetInPedCollision(void); + void SetPanelDamage(int32 component, ePanels panel, bool noFlyingComponents = false); + void SetBumperDamage(int32 component, ePanels panel, bool noFlyingComponents = false); + void SetDoorDamage(int32 component, eDoors door, bool noFlyingComponents = false); + + void TellHeliToGoToCoors(float x, float y, float z, uint8 speed); + void TellPlaneToGoToCoors(float x, float y, float z, uint8 speed); + void SetHeliOrientation(float orient) { m_fHeliOrientation = orient; } + void ClearHeliOrientation(void) { m_fHeliOrientation = -1.0f; } + + void Fix(void); + void SetComponentVisibility(RwFrame *frame, uint32 flags); + void SetupModelNodes(void); + void SetTaxiLight(bool light); + bool GetAllWheelsOffGround(void); + void HideAllComps(void); + void ShowAllComps(void); + void ReduceHornCounter(void); + + void PopBoot(void); + void PopBootUsingPhysics(void); + void CloseAllDoors(void); + CPed *KnockPedOutCar(eWeaponType weapon, uint16 door, CPed *ped); + +#ifdef COMPATIBLE_SAVES + virtual void Save(uint8*& buf); + virtual void Load(uint8*& buf); +#endif + static const uint32 nSaveStructSize; + + static void SetAllTaxiLights(bool set); +}; + +extern CVector vecHunterGunPos; +extern bool bAllCarCheat; \ No newline at end of file diff --git a/src/miami/vehicles/Bike.cpp b/src/miami/vehicles/Bike.cpp new file mode 100644 index 00000000..a65b64d2 --- /dev/null +++ b/src/miami/vehicles/Bike.cpp @@ -0,0 +1,2962 @@ +#include "common.h" +#include "General.h" +#include "Pad.h" +#include "DMAudio.h" +#include "Clock.h" +#include "Timecycle.h" +#include "ZoneCull.h" +#include "Camera.h" +#include "Darkel.h" +#include "Rubbish.h" +#include "Explosion.h" +#include "Particle.h" +#include "ParticleObject.h" +#include "Shadows.h" +#include "PointLights.h" +#include "Coronas.h" +#include "SpecialFX.h" +#include "WaterLevel.h" +#include "Floater.h" +#include "World.h" +#include "SurfaceTable.h" +#include "Weather.h" +#include "Record.h" +#include "CarCtrl.h" +#include "CarAI.h" +#include "Script.h" +#include "Stats.h" +#include "Replay.h" +#include "AnimManager.h" +#include "RpAnimBlend.h" +#include "AnimBlendAssociation.h" +#include "Ped.h" +#include "PlayerPed.h" +#include "DamageManager.h" +#include "Vehicle.h" +#include "Automobile.h" +#include "Bike.h" +#include "Debug.h" +#include "SaveBuf.h" + +const uint32 CBike::nSaveStructSize = +#ifdef COMPATIBLE_SAVES + 1260; +#else + sizeof(CBoat); +#endif + + +// TODO: maybe put this somewhere else +inline void +GetRelativeMatrix(RwMatrix *mat, RwFrame *frm, RwFrame *end) +{ + *mat = *RwFrameGetMatrix(frm); + frm = RwFrameGetParent(frm); + while(frm){ + RwMatrixTransform(mat, RwFrameGetMatrix(frm), rwCOMBINEPOSTCONCAT); + frm = RwFrameGetParent(frm); + if(frm == end) + frm = nil; + } +} + +#define FAKESUSPENSION (99999.992f) + +CBike::CBike(int32 id, uint8 CreatedBy) + : CVehicle(CreatedBy) +{ + int i; + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); + switch(id){ + case MI_ANGEL: + case MI_FREEWAY: + m_bikeAnimType = ASSOCGRP_BIKE_HARLEY; + break; + case MI_PIZZABOY: + case MI_FAGGIO: + m_bikeAnimType = ASSOCGRP_BIKE_VESPA; + break; + case MI_PCJ600: + m_bikeAnimType = ASSOCGRP_BIKE_STANDARD; + break; + case MI_SANCHEZ: + m_bikeAnimType = ASSOCGRP_BIKE_DIRT; + break; + default: assert(0 && "invalid bike model ID"); + } + m_vehType = VEHICLE_TYPE_BIKE; + + m_fFireBlowUpTimer = 0.0f; + m_doingBurnout = 0; + m_bike_flag01 = false; + + SetModelIndex(id); + + pHandling = mod_HandlingManager.GetHandlingData((tVehicleType)mi->m_handlingId); + pBikeHandling = mod_HandlingManager.GetBikePointer((tVehicleType)mi->m_handlingId); + pFlyingHandling = mod_HandlingManager.GetFlyingPointer((tVehicleType)mi->m_handlingId); + + m_bike_unused1 = 20.0f; + m_bike_unused2 = 0; + + mi->ChooseVehicleColour(m_currentColour1, m_currentColour2); + + m_fRearForkLength = 0.0f; + m_fFrontForkY = 0.0; + m_fFrontForkZ = 0.0; + m_fFrontForkSlope = Tan(DEGTORAD(mi->m_bikeSteerAngle)); + + m_fMass = pHandling->fMass; + m_fTurnMass = pHandling->fTurnMass; + m_vecCentreOfMass = pHandling->CentreOfMass; + m_vecCentreOfMass.z = 0.1f; + m_fAirResistance = pHandling->Dimension.x*pHandling->Dimension.z/m_fMass; + m_fElasticity = 0.05f; + m_fBuoyancy = pHandling->fBuoyancy; + + m_fSteerAngle = 0.0f; + m_fWheelAngle = 0.0f; + m_fLeanLRAngle = 0.0f; + m_fLeanLRAngle2 = 0.0f; + m_fGasPedal = 0.0f; + m_fBrakePedal = 0.0f; + m_fLeanInput = 0.0f; + m_fPedLeanAmountLR = 0.0f; + m_fPedLeanAmountUD = 0.0f; + m_pSetOnFireEntity = nil; + m_pBombRigger = nil; + m_fGasPedalAudio = 0.0f; + m_bike_flag02 = false; + bWaterTight = false; + bIsBeingPickedUp = false; + bIsStanding = false; + bExtraSpeed = false; + bIsOnFire = false; + bWheelieCam = false; + + m_fTireTemperature = 1.0f; + m_fBrakeDestabilization = 0.0f; + m_fVelocityChangeForAudio = 0; + + for(i = 0; i < 2; i++){ + m_aWheelRotation[i] = 0.0f; + m_aWheelSpeed[i] = 0.0f; + m_aWheelState[i] = WHEEL_STATE_NORMAL; + m_aWheelSkidmarkType[i] = SKIDMARK_NORMAL; + m_aWheelSkidmarkBloody[i] = false; + m_aWheelSkidmarkUnk[0] = false; + m_wheelStatus[i] = WHEEL_STATUS_OK; + } + + for(i = 0; i < 4; i++){ + m_aGroundPhysical[i] = nil; + m_aGroundOffset[i] = CVector(0.0f, 0.0f, 0.0f); + m_aSuspensionSpringRatioPrev[i] = m_aSuspensionSpringRatio[i] = 1.0f; + m_aWheelTimer[i] = 0.0f; + } + + m_nWheelsOnGround = 0; + m_nDriveWheelsOnGround = 0; + m_nDriveWheelsOnGroundPrev = 0; + m_fHeightAboveRoad = 0.0f; + m_fTraction = 1.0f; + + CColModel *colModel = mi->GetColModel(); + if(colModel->lines == nil){ + colModel->lines = (CColLine*)RwMalloc(4*sizeof(CColLine)); + colModel->numLines = 4; + } + // BUG? this would make more sense in the if above + colModel->lines[0].p0.z = FAKESUSPENSION; + + SetupSuspensionLines(); + + AutoPilot.m_nCarMission = MISSION_NONE; + AutoPilot.m_nTempAction = TEMPACT_NONE; + AutoPilot.m_nTimeToStartMission = CTimer::GetTimeInMilliseconds(); + AutoPilot.m_bStayInCurrentLevel = false; + + SetStatus(STATUS_SIMPLE); + bUseCollisionRecords = true; + m_nNumPassengers = 0; + bIsVan = false; + bIsBus = false; + bIsBig = false; + bLowVehicle = false; + bPedPhysics = false; + + bLeanMatrixClean = false; + m_leanMatrix = GetMatrix(); +} + +void +CBike::SetModelIndex(uint32 id) +{ + CVehicle::SetModelIndex(id); + SetupModelNodes(); +} + +#define SAND_SLOWDOWN (0.02f) +CVector vecTestResistance(0.9995f, 0.9f, 0.95f); +float fDAxisX = 1.0f; +float fDAxisXExtra = 100.0f; +float fDAxisY = 1000.0f; +float fInAirXRes = 0.98f; +float fFlySpeedMult = -0.6f; + +#pragma optimize("", off) // a workaround for another compiler bug =P, original had optimize off for this function too though + +void +CBike::ProcessControl(void) +{ + int i; + float wheelRot; + float acceleration = 0.0f; + bool bBalancedByRider = false; + bool bStuckInSand = false; + float brake = 0.0f; + CColModel *colModel = GetColModel(); + float wheelScale = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->m_wheelScale; + bWarnedPeds = false; + bLeanMatrixClean = false; + m_doingBurnout = 0; + bExtraSpeed = false; + bRestingOnPhysical = false; + + if(CReplay::IsPlayingBack()) + return; + + ProcessCarAlarm(); + + ActivateBombWhenEntered(); + + CRubbish::StirUp(this); + + UpdateClumpAlpha(); + + AutoPilot.m_bSlowedDownBecauseOfCars = false; + AutoPilot.m_bSlowedDownBecauseOfPeds = false; + + switch(GetStatus()){ + case STATUS_PLAYER: + bBalancedByRider = true; + bIsBeingPickedUp = false; + if(FindPlayerPed()->GetPedState() != PED_EXIT_CAR && FindPlayerPed()->GetPedState() != PED_DRAG_FROM_CAR){ + ProcessControlInputs(0); + + if(m_fLeanInput < 0.0f){ + m_vecCentreOfMass.y = pHandling->CentreOfMass.y + pBikeHandling->fLeanBakCOM*m_fLeanInput; + CVector com = m_vecCentreOfMass; +#ifdef FIX_BUGS + // center of mass has to have world space orientation. unfortunately we can't do wheelies + // at high speed then, flipping y here is like riding south without this fix where wheelies work + com.y = -com.y; + com = Multiply3x3(GetMatrix(), com); +#endif + if(m_fBrakePedal == 0.0f && !bIsHandbrakeOn || m_nWheelsOnGround == 0){ + if(GetModelIndex() == MI_SANCHEZ){ + float force = m_fLeanInput*m_fTurnMass*pBikeHandling->fLeanBackForce*Min(m_vecMoveSpeed.Magnitude(), 0.1f); + force *= 0.7f*m_fGasPedal + 0.3f; + ApplyTurnForce(-force*CTimer::GetTimeStep()*GetUp(), com+GetForward()); + }else{ + float force = m_fLeanInput*m_fTurnMass*pBikeHandling->fLeanBackForce*Min(m_vecMoveSpeed.Magnitude(), 0.1f); + force *= 0.5f*m_fGasPedal + 0.5f; + ApplyTurnForce(-force*CTimer::GetTimeStep()*GetUp(), com+GetForward()); + } + } + }else{ + m_vecCentreOfMass.y = pHandling->CentreOfMass.y + pBikeHandling->fLeanFwdCOM*m_fLeanInput; + CVector com = m_vecCentreOfMass; +#ifdef FIX_BUGS + // see above + com.y = -com.y; + com = Multiply3x3(GetMatrix(), com); +#endif + if(m_fBrakePedal < 0.0f || m_nWheelsOnGround == 0){ + float force = m_fLeanInput*m_fTurnMass*pBikeHandling->fLeanFwdForce*Min(m_vecMoveSpeed.Magnitude(), 0.1f); + ApplyTurnForce(-force*CTimer::GetTimeStep()*GetUp(), com+GetForward()); + } + } + + PruneReferences(); + + if(GetStatus() == STATUS_PLAYER && !CRecordDataForChase::IsRecording()) + DoDriveByShootings(); + + if(m_aSuspensionSpringRatio[0] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[0].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[1] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[1].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[2] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[2].surfaceB) == ADHESIVE_SAND || + m_aSuspensionSpringRatio[3] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[3].surfaceB) == ADHESIVE_SAND){ + CVector parallelSpeed = m_vecMoveSpeed - DotProduct(m_vecMoveSpeed, GetUp())*GetUp(); + if(m_fGasPedal > 0.3f){ + if(parallelSpeed.MagnitudeSqr() < SQR(0.3f)) + bStuckInSand = true; + parallelSpeed -= DotProduct(parallelSpeed, GetForward())*GetForward(); + } + ApplyMoveForce(parallelSpeed * -CTimer::GetTimeStep()*SAND_SLOWDOWN*m_fMass); + } + } + if(CPad::GetPad(0)->WeaponJustDown()) + ActivateBomb(); + break; + + case STATUS_PLAYER_PLAYBACKFROMBUFFER: + bBalancedByRider = true; + break; + + case STATUS_SIMPLE: + CCarAI::UpdateCarAI(this); + CPhysical::ProcessControl(); + CCarCtrl::UpdateCarOnRails(this); + + m_nWheelsOnGround = 2; + m_nDriveWheelsOnGroundPrev = m_nDriveWheelsOnGround; + m_nDriveWheelsOnGround = 2; + + pHandling->Transmission.CalculateGearForSimpleCar(AutoPilot.m_fMaxTrafficSpeed/50.0f, m_nCurrentGear); + + wheelRot = ProcessWheelRotation(WHEEL_STATE_NORMAL, GetForward(), m_vecMoveSpeed, 0.5f*wheelScale); + for(i = 0; i < 2; i++) + m_aWheelRotation[i] += wheelRot; + + PlayHornIfNecessary(); + ReduceHornCounter(); + bVehicleColProcessed = false; + bAudioChangingGear = false; + bWheelieCam = false; + // that's all we do for simple vehicles + return; + + case STATUS_PHYSICS: + CCarAI::UpdateCarAI(this); + CCarCtrl::SteerAICarWithPhysics(this); + PlayHornIfNecessary(); + + bBalancedByRider = true; + bWheelieCam = false; + + if(bIsBeingCarJacked){ + m_fGasPedal = 0.0f; + m_fBrakePedal = 1.0f; + bIsHandbrakeOn = true; + }else + bIsBeingPickedUp = false; + break; + + case STATUS_ABANDONED: + m_fBrakePedal = 0.0f; + if(m_vecMoveSpeed.MagnitudeSqr() < SQR(0.1f) || bIsStanding) + bIsHandbrakeOn = true; + else + bIsHandbrakeOn = false; + + m_fGasPedal = 0.0f; +#ifdef FIX_BUGS + if(!IsAlarmOn()) +#endif + m_nCarHornTimer = 0; + + bBalancedByRider = (pDriver || pPassengers[0] || bIsBeingCarJacked) && !bIsStanding; + m_fPedLeanAmountLR = 0.0f; + m_fPedLeanAmountUD = 0.0f; + bWheelieCam = false; + + if(bIsBeingCarJacked){ + m_fGasPedal = 0.0f; + m_fBrakePedal = 1.0f; + bIsHandbrakeOn = true; + } + break; + + case STATUS_WRECKED: + m_fBrakePedal = 0.05f; + bIsHandbrakeOn = true; + + m_fSteerAngle = 0.0f; + m_fGasPedal = 0.0f; +#ifdef FIX_BUGS + if(!IsAlarmOn()) +#endif + m_nCarHornTimer = 0; + + bBalancedByRider = false; + bWheelieCam = false; + m_fPedLeanAmountLR = 0.0f; + m_fPedLeanAmountUD = 0.0f; + break; + + case STATUS_PLAYER_DISABLED: + if(m_vecMoveSpeed.MagnitudeSqr() < SQR(0.1f)){ + m_fBrakePedal = 1.0f; + bIsHandbrakeOn = true; + }else{ + m_fBrakePedal = 0.0f; + bIsHandbrakeOn = false; + } + + m_fSteerAngle = 0.0f; + m_fGasPedal = 0.0f; +#ifdef FIX_BUGS + if(!IsAlarmOn()) +#endif + m_nCarHornTimer = 0; + + bBalancedByRider = true; + bWheelieCam = false; + break; + } + + if(bIsStanding) + if(Abs(GetRight().z) > 0.35f || Abs(GetForward().z) > 0.5f) + bIsStanding = false; + + if(bBalancedByRider || bIsBeingPickedUp || bIsStanding){ + float fDx = fDAxisX; + CVector res = vecTestResistance; + CVector localTurnSpeed = Multiply3x3(m_vecTurnSpeed, GetMatrix()); + + if(GetStatus() == STATUS_PLAYER){ + if(m_aWheelTimer[BIKESUSP_F1] == 0.0f && m_aWheelTimer[BIKESUSP_F2] == 0.0f){ + fDx = fDAxisXExtra; + if(!(m_aWheelTimer[BIKESUSP_R1] == 0.0f && m_aWheelTimer[BIKESUSP_R2] == 0.0f) && + GetForward().z > 0.0f) + res.x -= Min(0.25f*Abs(pBikeHandling->fWheelieAng-GetForward().z), 0.07f); + else + res.x = fInAirXRes; + }else if(m_aWheelTimer[BIKESUSP_R1] == 0.0f && m_aWheelTimer[BIKESUSP_R2] == 0.0f){ + fDx = fDAxisXExtra; + if(GetForward().z < 0.0f) + res.x *= Min(0.3f*Abs(pBikeHandling->fStoppieAng-GetForward().z), 0.1f) + 0.9f; + } + } + + res.x *= 1.0f/(fDx*SQR(localTurnSpeed.x) + 1.0f); + res.y *= 1.0f/(fDAxisY*SQR(localTurnSpeed.y) + 1.0f); + res.x = Pow(res.x, CTimer::GetTimeStep()); + res.y = Pow(res.y, CTimer::GetTimeStep()); + float turnX = localTurnSpeed.x*(res.x - 1.0f); + float turnY = localTurnSpeed.y*(res.y - 1.0f); + + res = -GetUp() * turnY * m_fTurnMass; + ApplyTurnForce(res, GetRight() + Multiply3x3(GetMatrix(), m_vecCentreOfMass)); + + res = GetUp() * turnX * m_fTurnMass; + ApplyTurnForce(res, GetForward() + Multiply3x3(GetMatrix(), m_vecCentreOfMass)); + + if(GetStatus() != STATUS_PLAYER) + m_vecCentreOfMass = pHandling->CentreOfMass; + }else{ + m_vecCentreOfMass = pHandling->CentreOfMass; + m_vecCentreOfMass.z = pBikeHandling->fNoPlayerCOMz; + } + + // Skip physics if object is found to have been static recently + bool skipPhysics = false; + if(!bIsStuck && (GetStatus() == STATUS_ABANDONED || GetStatus() == STATUS_WRECKED) && !bIsBeingPickedUp){ + bool makeStatic = false; + float moveSpeedLimit, turnSpeedLimit, distanceLimit; + + if(!bVehicleColProcessed && + m_vecMoveSpeed.IsZero() && + // BUG? m_aSuspensionSpringRatioPrev[3] is checked twice in the game. also, why 3? + m_aSuspensionSpringRatioPrev[3] != 1.0f) + makeStatic = true; + + if(GetStatus() == STATUS_WRECKED){ + moveSpeedLimit = 0.006f; + turnSpeedLimit = 0.0015f; + distanceLimit = 0.015f; + }else{ + moveSpeedLimit = 0.003f; + turnSpeedLimit = 0.0009f; + distanceLimit = 0.005f; + } + + m_vecMoveSpeedAvg = (m_vecMoveSpeedAvg + m_vecMoveSpeed)/2.0f; + m_vecTurnSpeedAvg = (m_vecTurnSpeedAvg + m_vecTurnSpeed)/2.0f; + + if(m_vecMoveSpeedAvg.MagnitudeSqr() <= sq(moveSpeedLimit*CTimer::GetTimeStep()) && + m_vecTurnSpeedAvg.MagnitudeSqr() <= sq(turnSpeedLimit*CTimer::GetTimeStep()) && + m_fDistanceTravelled < distanceLimit || + makeStatic){ + m_nStaticFrames++; + + if(m_nStaticFrames > 10 || makeStatic) + if(!CCarCtrl::MapCouldMoveInThisArea(GetPosition().x, GetPosition().y)){ + if(!makeStatic || m_nStaticFrames > 10) + m_nStaticFrames = 10; + + skipPhysics = true; + + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + } + }else + m_nStaticFrames = 0; + } + + // Postpone + for(i = 0; i < 4; i++) + if(m_aGroundPhysical[i]){ + bRestingOnPhysical = true; + if(!CWorld::bForceProcessControl && m_aGroundPhysical[i]->bIsInSafePosition){ + bWasPostponed = true; + return; + } + } + + if(bRestingOnPhysical){ + skipPhysics = false; + m_nStaticFrames = 0; + } + + VehicleDamage(); + + if(skipPhysics){ + bHasContacted = false; + bIsInSafePosition = false; + bWasPostponed = false; + bHasHitWall = false; + m_nCollisionRecords = 0; + bHasCollided = false; + bVehicleColProcessed = false; + bAudioChangingGear = false; + m_nDamagePieceType = 0; + m_fDamageImpulse = 0.0f; + m_pDamageEntity = nil; + m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); + m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); +// missing. BUG? +// m_fTireTemperature = 1.0f; + + if(bIsStanding && m_fWheelAngle < DEGTORAD(20.0f)) + m_fWheelAngle += DEGTORAD(1.0f)*CTimer::GetTimeStep(); + if(bIsStanding){ + float f = Pow(0.97f, CTimer::GetTimeStep()); + m_fLeanLRAngle2 = m_fLeanLRAngle2*f - (Asin(Clamp(GetRight().z,-1.0f,1.0f))+DEGTORAD(15.0f))*(1.0f-f); + m_fLeanLRAngle = m_fLeanLRAngle2; + } + }else{ + + // This has to be done if ProcessEntityCollision wasn't called + if(!bVehicleColProcessed){ + CMatrix mat(GetMatrix()); + bIsStuck = false; + bHasContacted = false; + bIsInSafePosition = false; + bWasPostponed = false; + bHasHitWall = false; + m_fDistanceTravelled = 0.0f; + m_bIsVehicleBeingShifted = false; + bSkipLineCol = false; + ApplyMoveSpeed(); + ApplyTurnSpeed(); + for(i = 0; CheckCollision() && i < 5; i++){ + GetMatrix() = mat; + ApplyMoveSpeed(); + ApplyTurnSpeed(); + } + bIsInSafePosition = true; + bIsStuck = false; + } + + if(!(bBalancedByRider || bIsBeingPickedUp || bIsStanding)){ + if(GetRight().z < 0.0f){ + if(m_fSteerAngle > -DEGTORAD(25.0f)) + m_fSteerAngle -= DEGTORAD(0.5f)*CTimer::GetTimeStep(); + }else{ + if(m_fSteerAngle < DEGTORAD(25.0f)) + m_fSteerAngle += DEGTORAD(0.5f)*CTimer::GetTimeStep(); + } + } + + // Lean forward speed up + float savedAirResistance = m_fAirResistance; + if(GetStatus() == STATUS_PLAYER && pDriver){ + CAnimBlendAssociation *assoc = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_LEANF); + if(assoc && assoc->blendAmount > 0.5f && + assoc->currentTime > 0.06f && assoc->currentTime < 0.14f){ + m_fAirResistance *= 0.6f; + if(m_fGasPedal > 0.5f && DotProduct(m_vecMoveSpeed, GetForward()) > 0.25f){ + ApplyMoveForce(0.2f*m_fMass*GRAVITY*CTimer::GetTimeStep()*GetForward()); + bExtraSpeed = true; + } + } + } + + CPhysical::ProcessControl(); + m_fAirResistance = savedAirResistance; + + ProcessBuoyancy(); + + // Rescale spring ratios, i.e. subtract wheel radius + for(i = 0; i < 4; i++){ + // wheel radius in relation to suspension line + float wheelRadius = 1.0f - m_aSuspensionSpringLength[i]/m_aSuspensionLineLength[i]; + // rescale such that 0.0 is fully compressed and 1.0 is fully extended + m_aSuspensionSpringRatio[i] = (m_aSuspensionSpringRatio[i]-wheelRadius)/(1.0f-wheelRadius); + } + + int rnd = 0; + float fwdSpeed = Abs(DotProduct(m_vecMoveSpeed, GetForward())); + CVector contactPoints[4]; // relative to model + CVector contactSpeeds[4]; // speed at contact points + CVector springDirections[4]; // normalized, in model space + + for(i = 0; i < 4; i++){ + // Set spring under certain circumstances + if(m_wheelStatus[i/2] == WHEEL_STATUS_MISSING) + m_aSuspensionSpringRatio[i] = 1.0f; + else if(m_wheelStatus[i/2] == WHEEL_STATUS_BURST){ + // wheel more bumpy the faster we are + if(i == BIKESUSP_F1 || i == BIKESUSP_R1) + rnd = CGeneral::GetRandomNumberInRange(0, (uint16)(40*fwdSpeed) + 98) < 100; + if(rnd){ + m_aSuspensionSpringRatio[i] += 0.3f*(m_aSuspensionLineLength[i]-m_aSuspensionSpringLength[i])/m_aSuspensionSpringLength[i]; + if(m_aSuspensionSpringRatio[i] > 1.0f) + m_aSuspensionSpringRatio[i] = 1.0f; + } + } + + // get points and directions if spring is compressed + if(m_aSuspensionSpringRatio[i] < 1.0f){ + contactPoints[i] = m_aWheelColPoints[i].point - GetPosition(); + springDirections[i] = Multiply3x3(GetMatrix(), colModel->lines[i].p1 - colModel->lines[i].p0); + springDirections[i].Normalise(); + } + } + + m_aWheelSkidmarkType[0] = m_aWheelSkidmarkType[1] = SKIDMARK_NORMAL; + m_aWheelSkidmarkUnk[0] = m_aWheelSkidmarkUnk[1] = false; + + // Make springs push up vehicle + for(i = 0; i < 4; i++){ + if(m_aSuspensionSpringRatio[i] < 1.0f){ + float bias = pHandling->fSuspensionBias; + if(i == BIKESUSP_R1 || i == BIKESUSP_R2) + bias = 1.0f - bias; + + if(m_aWheelColPoints[i].normal.z > 0.35f) + ApplySpringCollisionAlt(pHandling->fSuspensionForceLevel, + springDirections[i], contactPoints[i], + m_aSuspensionSpringRatio[i], bias, m_aWheelColPoints[i].normal); + else + ApplySpringCollision(pHandling->fSuspensionForceLevel, + springDirections[i], contactPoints[i], + m_aSuspensionSpringRatio[i], bias); + + if(m_aWheelColPoints[i].surfaceB == SURFACE_GRASS || + m_aWheelColPoints[i].surfaceB == SURFACE_MUD_DRY){ + if(i < 2) + m_aWheelSkidmarkType[0] = SKIDMARK_MUDDY; + else + m_aWheelSkidmarkType[1] = SKIDMARK_MUDDY; + }else if(m_aWheelColPoints[i].surfaceB == SURFACE_SAND || + m_aWheelColPoints[i].surfaceB == SURFACE_SAND_BEACH){ + if(i < 2){ + m_aWheelSkidmarkType[0] = SKIDMARK_SANDY; + m_aWheelSkidmarkUnk[0] = true; + }else{ + m_aWheelSkidmarkType[1] = SKIDMARK_SANDY; + m_aWheelSkidmarkUnk[1] = true; + } + } + }else{ + contactPoints[i] = Multiply3x3(GetMatrix(), colModel->lines[i].p1); + } + } + + // Get speed at contact points + for(i = 0; i < 4; i++){ + contactSpeeds[i] = GetSpeed(contactPoints[i]); + if(m_aGroundPhysical[i]){ + // subtract movement of physical we're standing on + contactSpeeds[i] -= m_aGroundPhysical[i]->GetSpeed(m_aGroundOffset[i]); +#ifndef FIX_BUGS + // this shouldn't be reset because we still need it below + m_aGroundPhysical[i] = nil; +#endif + } + } + + CVector normal; + if(m_aSuspensionSpringRatio[0] < 1.0f || m_aSuspensionSpringRatio[1] < 1.0f){ + normal = m_aSuspensionSpringRatio[0] < 1.0f ? m_aWheelColPoints[0].normal : m_aWheelColPoints[1].normal; + if(normal.z > 0.35f) + springDirections[0] = -normal; + normal = m_aSuspensionSpringRatio[1] < 1.0f ? m_aWheelColPoints[1].normal : m_aWheelColPoints[0].normal; + if(normal.z > 0.35f) + springDirections[1] = -normal; + } + if(m_aSuspensionSpringRatio[2] < 1.0f || m_aSuspensionSpringRatio[3] < 1.0f){ + normal = m_aSuspensionSpringRatio[2] < 1.0f ? m_aWheelColPoints[2].normal : m_aWheelColPoints[3].normal; + if(normal.z > 0.35f) + springDirections[2] = -normal; + normal = m_aSuspensionSpringRatio[3] < 1.0f ? m_aWheelColPoints[3].normal : m_aWheelColPoints[2].normal; + if(normal.z > 0.35f) + springDirections[3] = -normal; + } + + // game has dead code here if m_vecMoveSpeed.Magnitude() < 0.01f + + // dampen springs + for(i = 0; i < 4; i++) + if(m_aSuspensionSpringRatio[i] < 1.0f) + ApplySpringDampening(pHandling->fSuspensionDampingLevel, + springDirections[i], contactPoints[i], contactSpeeds[i]); + + // Get speed at contact points again + for(i = 0; i < 4; i++){ + contactSpeeds[i] = GetSpeed(contactPoints[i]); + if(m_aGroundPhysical[i]){ + // subtract movement of physical we're standing on + contactSpeeds[i] -= m_aGroundPhysical[i]->GetSpeed(m_aGroundOffset[i]); + m_aGroundPhysical[i] = nil; + } + } + + bool gripCheat = true; + fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward()); + if(!CVehicle::bCheat3) + gripCheat = false; + acceleration = pHandling->Transmission.CalculateDriveAcceleration(m_fGasPedal, m_nCurrentGear, m_fChangeGearTime, fwdSpeed, gripCheat); + acceleration /= m_fForceMultiplier; + + brake = m_fBrakePedal * pHandling->fBrakeDeceleration * CTimer::GetTimeStep(); + bool neutralHandling = GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE && (pHandling->Flags & HANDLING_NEUTRALHANDLING); + float brakeBiasFront = neutralHandling ? 1.0f : 2.0f*pHandling->fBrakeBias; + float brakeBiasRear = neutralHandling ? 1.0f : 2.0f*(1.0f-pHandling->fBrakeBias); + float tractionBiasFront = neutralHandling ? 1.0f : 2.0f*pHandling->fTractionBias; + float tractionBiasRear = neutralHandling ? 1.0f : 2.0f-tractionBiasFront; + + // Count how many wheels are touching the ground + + m_nWheelsOnGround = 0; + m_nDriveWheelsOnGroundPrev = m_nDriveWheelsOnGround; + m_nDriveWheelsOnGround = 0; + + for(i = 0; i < 4; i++){ + if(m_aSuspensionSpringRatio[i] < 1.0f) + m_aWheelTimer[i] = 4.0f; + else + m_aWheelTimer[i] = Max(m_aWheelTimer[i]-CTimer::GetTimeStep(), 0.0f); + + if(m_aWheelTimer[i] > 0.0f){ + m_nWheelsOnGround++; + if(i == BIKESUSP_R1 || i == BIKESUSP_R2) + m_nDriveWheelsOnGround = 1; + if(m_nWheelsOnGround == 1) + m_vecAvgSurfaceNormal = m_aWheelColPoints[i].normal; + else + m_vecAvgSurfaceNormal += m_aWheelColPoints[i].normal; + } + } + + if(m_nWheelsOnGround == 0) + m_vecAvgSurfaceNormal = CVector(0.0f, 0.0f, 1.0f); + else{ + m_vecAvgSurfaceNormal /= m_nWheelsOnGround; + if(DotProduct(m_vecAvgSurfaceNormal, GetUp()) < -0.5f) + m_vecAvgSurfaceNormal *= -1.0f; + } + + // Find contact points for wheel processing + int frontLine = m_aSuspensionSpringRatio[BIKESUSP_F1] < m_aSuspensionSpringRatio[BIKESUSP_F2] ? + BIKESUSP_F1 : BIKESUSP_F2; + CVector frontContact(0.0f, + colModel->lines[BIKESUSP_F1].p0.y, + colModel->lines[BIKESUSP_F1].p0.z - m_aSuspensionSpringRatio[frontLine]*m_aSuspensionSpringLength[BIKESUSP_F1] - 0.5f*wheelScale); + frontContact = Multiply3x3(GetMatrix(), frontContact); + + int rearLine = m_aSuspensionSpringRatio[BIKESUSP_R1] < m_aSuspensionSpringRatio[BIKESUSP_R2] ? + BIKESUSP_R1 : BIKESUSP_R2; + CVector rearContact(0.0f, + colModel->lines[BIKESUSP_R1].p0.y, + colModel->lines[BIKESUSP_R1].p0.z - m_aSuspensionSpringRatio[rearLine]*m_aSuspensionSpringLength[BIKESUSP_R1] - 0.5f*wheelScale); + rearContact = Multiply3x3(GetMatrix(), rearContact); + + float traction = 0.004f * m_fTraction; + traction *= pHandling->fTractionMultiplier / 4.0f; + + // Turn wheel + if(GetStatus() == STATUS_PLAYER || !bIsStanding || bIsBeingPickedUp){ + if(Abs(m_vecMoveSpeed.x) < 0.01f && Abs(m_vecMoveSpeed.y) < 0.01f && m_fSteerAngle == 0.0f){ + m_fWheelAngle *= Pow(0.96f, CTimer::GetTimeStep()); + }else{ + float f; + if(fwdSpeed > 0.01f && m_aWheelTimer[BIKESUSP_F1] > 0.0f && m_aWheelTimer[BIKESUSP_F2] > 0.0f && GetStatus() == STATUS_PLAYER){ + CColPoint point; + point.surfaceA = SURFACE_WHEELBASE; + point.surfaceB = SURFACE_TARMAC; + float steer = CSurfaceTable::GetAdhesiveLimit(point)*4.0f*pBikeHandling->fSpeedSteer*traction; + if(CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[rearLine].surfaceB) == ADHESIVE_LOOSE || + CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[rearLine].surfaceB) == ADHESIVE_SAND) + steer *= pBikeHandling->fSlipSteer; + f = Asin(Min(steer/SQR(fwdSpeed), 1.0))/DEGTORAD(pHandling->fSteeringLock); + if(m_fSteerAngle < 0.0f && m_fLeanLRAngle < 0.0f || + m_fSteerAngle > 0.0f && m_fLeanLRAngle > 0.0f) + f *= 2.0f; + f = Min(f, 1.0f); + }else{ + f = 1.0f; + } + if(GetStatus() != STATUS_PLAYER) + f = 1.0f; + m_fWheelAngle = m_fSteerAngle*f; + } + }else if(m_fWheelAngle < DEGTORAD(20.0f)) + m_fWheelAngle += DEGTORAD(1.5f)*CTimer::GetTimeStep(); + + static float fThrust; + static tWheelState WheelState[2]; + CVector initialMoveSpeed = m_vecMoveSpeed; + bool rearWheelsFirst = !!(pHandling->Flags & HANDLING_REARWHEEL_1ST); + + // Process front wheel - first try + + if(!rearWheelsFirst){ + if(m_aWheelTimer[BIKESUSP_F1] > 0.0f || m_aWheelTimer[BIKESUSP_F2] > 0.0f){ + // Wheel on ground + eBikeWheelSpecial spec; + if(m_aWheelTimer[BIKESUSP_R1] > 0.0f || m_aWheelTimer[BIKESUSP_R2] > 0.0f) + spec = BIKE_WHEELSPEC_0; + else + spec = BIKE_WHEELSPEC_2; + CVector wheelFwd = Multiply3x3(GetMatrix(), CVector(-Sin(m_fWheelAngle), Cos(m_fWheelAngle), 0.0f)); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[frontLine].normal)*m_aWheelColPoints[frontLine].normal; + wheelFwd.Normalise(); + CVector wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[frontLine].normal); + wheelRight.Normalise(); + + fThrust = 0.0f; + m_aWheelColPoints[frontLine].surfaceA = SURFACE_WHEELBASE; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[frontLine])*traction; + float adhesionDestab = 1.0f; + if(m_fBrakeDestabilization > 0.0f) + switch(CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[frontLine].surfaceB)){ + case ADHESIVE_HARD: + case ADHESIVE_LOOSE: + adhesionDestab = 0.9f; + break; + case ADHESIVE_ROAD: + adhesionDestab = 0.7f; + break; + } + if(GetStatus() == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[frontLine].surfaceB); + if(m_wheelStatus[BIKEWHEEL_FRONT] == WHEEL_STATUS_BURST) + adhesion *= 0.4f; + WheelState[BIKEWHEEL_FRONT] = m_aWheelState[BIKEWHEEL_FRONT]; + CVector contactSpeed = GetSpeed(frontContact); + ProcessBikeWheel(wheelFwd, wheelRight, + contactSpeed, frontContact, + 2, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront, adhesionDestab, + BIKEWHEEL_FRONT, + &m_aWheelSpeed[BIKEWHEEL_FRONT], + &WheelState[BIKEWHEEL_FRONT], + spec, + m_wheelStatus[BIKEWHEEL_FRONT]); + if(bStuckInSand && (WheelState[BIKEWHEEL_FRONT] == WHEEL_STATE_SPINNING || WheelState[BIKEWHEEL_FRONT] == WHEEL_STATE_SKIDDING)) + WheelState[BIKEWHEEL_FRONT] = WHEEL_STATE_NORMAL; + }else{ + // Wheel in the air + m_aWheelSpeed[BIKEWHEEL_FRONT] *= 0.95f; + m_aWheelRotation[BIKEWHEEL_FRONT] += m_aWheelSpeed[BIKEWHEEL_FRONT]; + } + } + + // Process rear wheel + + if(m_aWheelTimer[BIKESUSP_R1] > 0.0f || m_aWheelTimer[BIKESUSP_R2] > 0.0f){ + // Wheel on ground + float rearBrake = brake; + float rearTraction = traction; + + CVector wheelFwd = GetForward(); + CVector wheelRight = GetRight(); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[rearLine].normal)*m_aWheelColPoints[rearLine].normal; + wheelFwd.Normalise(); + wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[rearLine].normal); + wheelRight.Normalise(); + + if(bIsHandbrakeOn){ +#ifdef FIX_BUGS + // Not sure if this is needed, but brake usually has timestep as a factor + rearBrake = 20000.0f * CTimer::GetTimeStepFix(); +#else + rearBrake = 20000.0f; +#endif + m_fTireTemperature = 1.0f; + }else if(m_doingBurnout){ + rearBrake = 0.0f; + rearTraction = 0.0f; + ApplyTurnForce(contactPoints[BIKESUSP_R1], -0.0007f*m_fTurnMass*m_fSteerAngle*GetRight()*CTimer::GetTimeStep()); + }else if(m_fTireTemperature < 1.0f && m_fGasPedal > 0.75f){ + rearTraction *= m_fTireTemperature; + ApplyTurnForce(contactPoints[BIKESUSP_R1], (1.0f-m_fTireTemperature)*-0.0007f*m_fTurnMass*m_fSteerAngle*GetRight()*CTimer::GetTimeStep()); + } + + if(fThrust > 0.0f && brake > 0.0f) + brake = 0.0f; // only affects next front wheel. is this intended? + fThrust = acceleration; + m_aWheelColPoints[rearLine].surfaceA = SURFACE_WHEELBASE; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[rearLine])*rearTraction; + float adhesionDestab = 1.0f; + if(m_fBrakeDestabilization > 0.0f) + switch(CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[rearLine].surfaceB)){ + case ADHESIVE_HARD: + case ADHESIVE_LOOSE: + adhesionDestab = 0.9f; + break; + case ADHESIVE_ROAD: + adhesionDestab = 0.7f; + break; + } + if(GetStatus() == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[rearLine].surfaceB); + if(m_wheelStatus[BIKEWHEEL_REAR] == WHEEL_STATUS_BURST) + adhesion *= 0.4f; + WheelState[BIKEWHEEL_REAR] = m_aWheelState[BIKEWHEEL_REAR]; + CVector contactSpeed = GetSpeed(rearContact); + ProcessBikeWheel(wheelFwd, wheelRight, + contactSpeed, rearContact, + 2, fThrust, + rearBrake*brakeBiasRear, + adhesion*tractionBiasRear, adhesionDestab, + BIKEWHEEL_REAR, + &m_aWheelSpeed[BIKEWHEEL_REAR], + &WheelState[BIKEWHEEL_REAR], + BIKE_WHEELSPEC_1, + m_wheelStatus[BIKEWHEEL_REAR]); + if(bStuckInSand && (WheelState[BIKEWHEEL_REAR] == WHEEL_STATE_SPINNING || WheelState[BIKEWHEEL_REAR] == WHEEL_STATE_SKIDDING)) + WheelState[BIKEWHEEL_REAR] = WHEEL_STATE_NORMAL; + }else{ + // Wheel in the air + if(bIsHandbrakeOn) + m_aWheelSpeed[BIKEWHEEL_REAR] = 0.0f; + else{ + if(acceleration > 0.0f){ + if(m_aWheelSpeed[BIKEWHEEL_REAR] < 2.0f) + m_aWheelSpeed[BIKEWHEEL_REAR] -= 0.2f; + }else{ + if(m_aWheelSpeed[BIKEWHEEL_REAR] > -2.0f) + m_aWheelSpeed[BIKEWHEEL_REAR] += 0.1f; + } + } + m_aWheelRotation[BIKEWHEEL_REAR] += m_aWheelSpeed[BIKEWHEEL_REAR]; + } + + if(m_doingBurnout && m_aWheelState[BIKEWHEEL_REAR] == WHEEL_STATE_SPINNING){ + m_fTireTemperature += 0.001f*CTimer::GetTimeStep(); + if(m_fTireTemperature > 3.0f) + m_fTireTemperature = 3.0f; + }else if(m_fTireTemperature > 1.0f){ + m_fTireTemperature = (m_fTireTemperature - 1.0f)*Pow(0.995f, CTimer::GetTimeStep()) + 1.0f; + } + + // Process front wheel - second try + + if(rearWheelsFirst){ + if(m_aWheelTimer[BIKESUSP_F1] > 0.0f || m_aWheelTimer[BIKESUSP_F2] > 0.0f){ + // Wheel on ground + eBikeWheelSpecial spec; + if(m_aWheelTimer[BIKESUSP_R1] > 0.0f || m_aWheelTimer[BIKESUSP_R2] > 0.0f) + spec = BIKE_WHEELSPEC_0; + else + spec = BIKE_WHEELSPEC_2; + CVector wheelFwd = GetMatrix() * CVector(-Sin(m_fWheelAngle), Cos(m_fWheelAngle), 0.0f); + wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[frontLine].normal)*m_aWheelColPoints[frontLine].normal; + wheelFwd.Normalise(); + CVector wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[frontLine].normal); + wheelRight.Normalise(); + + fThrust = 0.0f; + m_aWheelColPoints[frontLine].surfaceA = SURFACE_WHEELBASE; + float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[frontLine])*traction; + float adhesionDestab = 1.0f; + if(m_fBrakeDestabilization > 0.0f) + switch(CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[frontLine].surfaceB)){ + case ADHESIVE_HARD: + case ADHESIVE_LOOSE: + adhesionDestab = 0.9f; + break; + case ADHESIVE_ROAD: + adhesionDestab = 0.7f; + break; + } + if(GetStatus() == STATUS_PLAYER) + adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[frontLine].surfaceB); + if(m_wheelStatus[BIKEWHEEL_FRONT] == WHEEL_STATUS_BURST) + adhesion *= 0.4f; + WheelState[BIKEWHEEL_FRONT] = m_aWheelState[BIKEWHEEL_FRONT]; + CVector contactSpeed = GetSpeed(frontContact); + ProcessBikeWheel(wheelFwd, wheelRight, + contactSpeed, frontContact, + 2, fThrust, + brake*brakeBiasFront, + adhesion*tractionBiasFront, adhesionDestab, + BIKEWHEEL_FRONT, + &m_aWheelSpeed[BIKEWHEEL_FRONT], + &WheelState[BIKEWHEEL_FRONT], + spec, + m_wheelStatus[BIKEWHEEL_FRONT]); + if(bStuckInSand && (WheelState[BIKEWHEEL_FRONT] == WHEEL_STATE_SPINNING || WheelState[BIKEWHEEL_FRONT] == WHEEL_STATE_SKIDDING)) + WheelState[BIKEWHEEL_FRONT] = WHEEL_STATE_NORMAL; + }else{ + // Wheel in the air + m_aWheelSpeed[BIKEWHEEL_FRONT] *= 0.95f; + m_aWheelRotation[BIKEWHEEL_FRONT] += m_aWheelSpeed[BIKEWHEEL_FRONT]; + } + } + + // Process leaning + float idleAngle = 0.0f; + if(pDriver){ + CAnimBlendAssociation *assoc = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_READY); + if(assoc) + idleAngle = DEGTORAD(10.0f) * assoc->blendAmount; + } + if(bBalancedByRider || bIsBeingPickedUp){ + m_vecAvgSurfaceRight = CrossProduct(GetForward(), m_vecAvgSurfaceNormal); + m_vecAvgSurfaceRight.Normalise(); + float lean; + if(m_nWheelsOnGround == 0) + lean = -(m_fSteerAngle/DEGTORAD(pHandling->fSteeringLock))*0.5f*GRAVITY*CTimer::GetTimeStep(); + else + lean = DotProduct(m_vecMoveSpeed-initialMoveSpeed, m_vecAvgSurfaceRight); + lean /= GRAVITY*Max(CTimer::GetTimeStep(), 0.01f); + if(m_wheelStatus[BIKEWHEEL_FRONT] == WHEEL_STATUS_BURST) + lean = Clamp(lean, -0.4f*pBikeHandling->fMaxLean, 0.4f*pBikeHandling->fMaxLean); + else + lean = Clamp(lean, -pBikeHandling->fMaxLean, pBikeHandling->fMaxLean); + float f = Pow(pBikeHandling->fDesLean, CTimer::GetTimeStep()); + m_fLeanLRAngle2 = (Asin(lean) - idleAngle)*(1.0f-f) + m_fLeanLRAngle2*f; + }else{ + if(bIsStanding){ + float f = Pow(0.97f, CTimer::GetTimeStep()); + m_fLeanLRAngle2 = m_fLeanLRAngle2*f - (Asin(GetRight().z) + DEGTORAD(15.0f) + idleAngle)*(1.0f-f); + }else{ + float f = Pow(0.95f, CTimer::GetTimeStep()); + m_fLeanLRAngle2 = m_fLeanLRAngle2*f; + } + } + m_fLeanLRAngle = m_fLeanLRAngle2; + + // Destabilize steering when braking + if((m_aSuspensionSpringRatio[BIKESUSP_F1] < 1.0f || m_aSuspensionSpringRatio[BIKESUSP_F2] < 1.0f) && + m_fBrakePedal - m_fGasPedal > 0.9f && + fwdSpeed > 0.02f && + !bIsHandbrakeOn){ + m_fBrakeDestabilization += CGeneral::GetRandomNumberInRange(0.5f, 1.0f)*0.2f*CTimer::GetTimeStep(); + if(m_aSuspensionSpringRatio[BIKESUSP_R1] < 1.0f || m_aSuspensionSpringRatio[BIKESUSP_R2] < 1.0f){ + // BUG: this clamp makes no sense and the arguments seem swapped too + ApplyTurnForce(contactPoints[BIKESUSP_R1], + m_fTurnMass*Sin(m_fBrakeDestabilization)*Clamp(fwdSpeed, 0.5f, 0.2f)*0.013f*GetRight()*CTimer::GetTimeStep()); + }else{ + // BUG: this clamp makes no sense and the arguments seem swapped too + ApplyTurnForce(contactPoints[BIKESUSP_R1], + m_fTurnMass*Sin(m_fBrakeDestabilization)*Clamp(fwdSpeed, 0.5f, 0.2f)*0.003f*GetRight()*CTimer::GetTimeStep()); + } + }else + m_fBrakeDestabilization = 0.0f; + + // Update wheel positions from suspension + float frontWheelPos = colModel->lines[frontLine].p0.z; + if(m_aSuspensionSpringRatio[frontLine] > 0.0f) + frontWheelPos -= m_aSuspensionSpringRatio[frontLine]*m_aSuspensionSpringLength[frontLine]; + m_aWheelPosition[BIKEWHEEL_FRONT] += (frontWheelPos - m_aWheelPosition[BIKEWHEEL_FRONT])*0.75f; + + float rearWheelPos = colModel->lines[rearLine].p0.z; + if(m_aSuspensionSpringRatio[rearLine] > 0.0f) + rearWheelPos -= m_aSuspensionSpringRatio[rearLine]*m_aSuspensionSpringLength[rearLine]; + m_aWheelPosition[BIKEWHEEL_REAR] += (rearWheelPos - m_aWheelPosition[BIKEWHEEL_REAR])*0.75f; + + for(i = 0; i < 2; i++) + m_aWheelState[i] = WheelState[i]; + // never spin when moving backwards + if(m_fGasPedal < 0.0f && m_aWheelState[BIKEWHEEL_REAR] == WHEEL_STATE_SPINNING) + m_aWheelState[BIKEWHEEL_REAR] = WHEEL_STATE_NORMAL; + + // Process horn + + if(GetStatus() != STATUS_PLAYER){ +#ifdef FIX_BUGS + if(!IsAlarmOn()) +#endif + ReduceHornCounter(); + }else{ +#ifdef FIX_BUGS + if(!IsAlarmOn()) +#endif + { + if(Pads[0].GetHorn()) + m_nCarHornTimer = 1; + else + m_nCarHornTimer = 0; + } + } + } + + if(m_fHealth < 250.0f && GetStatus() != STATUS_WRECKED){ + // Car is on fire + + CVector damagePos, fireDir; + + // move fire forward if in first person + if(this == FindPlayerVehicle() && TheCamera.GetLookingForwardFirstPerson()){ + damagePos = CVector(0.0f, 1.2f, -0.4f); + fireDir = CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.01125f, 0.09f)); + }else{ + damagePos = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->m_positions[CAR_POS_BACKSEAT]; + damagePos.z -= 0.3f; + fireDir = CGeneral::GetRandomNumberInRange(0.02025f, 0.09f) * GetRight(); + fireDir -= CGeneral::GetRandomNumberInRange(0.02025f, 0.18f) * GetForward(); + fireDir.z = CGeneral::GetRandomNumberInRange(0.00225f, 0.018f); + } + + damagePos = GetMatrix()*damagePos; + CParticle::AddParticle(PARTICLE_CARFLAME, damagePos, fireDir, + nil, 0.9f); + + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, damagePos, CVector(0.0f, 0.0f, 0.0f), nil, 0.5f); + + damagePos.x += CGeneral::GetRandomNumberInRange(-0.5625f, 0.5625f), + damagePos.y += CGeneral::GetRandomNumberInRange(-0.5625f, 0.5625f), + damagePos.z += CGeneral::GetRandomNumberInRange(0.5625f, 2.25f); + CParticle::AddParticle(PARTICLE_CARFLAME_SMOKE, damagePos, CVector(0.0f, 0.0f, 0.0f)); + + // Blow up car after 5 seconds + m_fFireBlowUpTimer += CTimer::GetTimeStepInMilliseconds(); + if(m_fFireBlowUpTimer > 5000.0f) + BlowUpCar(m_pSetOnFireEntity); + }else + m_fFireBlowUpTimer = 0.0f; + + ProcessDelayedExplosion(); + + // Find out how much to shake the pad depending on suspension and ground surface + + float suspShake = 0.0f; + float surfShake = 0.0f; + float speedsq = m_vecMoveSpeed.MagnitudeSqr(); + for(i = 0; i < 4; i++){ + float suspChange = m_aSuspensionSpringRatioPrev[i] - m_aSuspensionSpringRatio[i]; + if(suspChange > 0.3f && (i == BIKESUSP_F1 || i == BIKESUSP_R1) && speedsq > 0.04f){ + if(GetStatus() == STATUS_PLAYER || GetStatus() == STATUS_PHYSICS){ +#ifdef FIX_BUGS + // only two wheels but 4 suspensions + if(m_wheelStatus[i/2] == WHEEL_STATUS_BURST) +#else + if(m_wheelStatus[i] == WHEEL_STATUS_BURST) +#endif + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_JUMP_2, suspChange); + else + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_JUMP, suspChange); + if(suspChange > suspShake) + suspShake = suspChange; + } + } + + if(this == FindPlayerVehicle()){ + uint8 surf = m_aWheelColPoints[i].surfaceB; + if(surf == SURFACE_GRAVEL || surf == SURFACE_WATER || surf == SURFACE_HEDGE){ + if(surfShake < 0.2f) + surfShake = 0.3f; + }else if(surf == SURFACE_MUD_DRY || surf == SURFACE_SAND || surf == SURFACE_SAND_BEACH){ + if(surfShake < 0.1f) + surfShake = 0.2f; + }else if(surf == SURFACE_GRASS){ + if(surfShake < 0.05f) + surfShake = 0.1f; + } + +// BUG: this only observes one of the wheels + TheCamera.m_bVehicleSuspenHigh = Abs(suspChange) > 0.05f; + } + + m_aSuspensionSpringRatioPrev[i] = m_aSuspensionSpringRatio[i]; + m_aSuspensionSpringRatio[i] = 1.0f; + } + + // Shake pad + + if((suspShake > 0.0f || surfShake > 0.0f) && GetStatus() == STATUS_PLAYER){ + float speed = m_vecMoveSpeed.MagnitudeSqr(); + if(speed > sq(0.1f)){ + speed = Sqrt(speed); + if(suspShake > 0.0f){ + uint8 freq = Min(200.0f*suspShake*speed*2000.0f/m_fMass + 100.0f, 250.0f); + CPad::GetPad(0)->StartShake(20000.0f*CTimer::GetTimeStep()/freq, freq); + }else{ + uint8 freq = Min(200.0f*surfShake*speed*2000.0f/m_fMass + 40.0f, 150.0f); + CPad::GetPad(0)->StartShake(5000.0f*CTimer::GetTimeStep()/freq, freq); + } + } + } + + bVehicleColProcessed = false; + bAudioChangingGear = false; + + if(!bWarnedPeds) + CCarCtrl::ScanForPedDanger(this); + + if(bInfiniteMass){ + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); + }else if(!skipPhysics && + (acceleration == 0.0f && brake == 0.0f || GetStatus() == STATUS_WRECKED)){ + if(Abs(m_vecMoveSpeed.x) < 0.005f && + Abs(m_vecMoveSpeed.y) < 0.005f && + Abs(m_vecMoveSpeed.z) < 0.005f){ + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnSpeed.z = 0.0f; + } + } + + // Balance bike + if(bBalancedByRider || bIsBeingPickedUp || bIsStanding){ + float onSideness = DotProduct(GetRight(), m_vecAvgSurfaceNormal); + onSideness = Clamp(onSideness, -1.0f, 1.0f); + CVector worldCOM = Multiply3x3(GetMatrix(), m_vecCentreOfMass); + // Keep bike upright + if(bBalancedByRider){ + ApplyTurnForce(-0.07f*onSideness*m_fTurnMass*GetUp()*CTimer::GetTimeStep(), worldCOM+GetRight()); + bIsStanding = false; + }else + ApplyTurnForce(-0.1f*onSideness*m_fTurnMass*GetUp()*CTimer::GetTimeStep(), worldCOM+GetRight()); + + // Wheelie/Stoppie stabilization + if(GetStatus() == STATUS_PLAYER){ + if(m_aWheelTimer[BIKESUSP_F1] == 0.0f && m_aWheelTimer[BIKESUSP_F2] == 0.0f && GetForward().z > 0.0 && + !(m_aWheelTimer[BIKESUSP_R1] == 0.0f && m_aWheelTimer[BIKESUSP_R2] == 0.0f)){ + // Wheelie + float wheelie = pBikeHandling->fWheelieAng - GetForward().z; + if(wheelie > 0.15f) + // below wheelie angle + wheelie = Max(0.3f - wheelie, 0.0f); + else if(wheelie < -0.08f) + // above wheelie angle + wheelie = Min(-0.15f - wheelie, 0.0f); + float wheelieStab = pBikeHandling->fWheelieStabMult * Min(m_vecMoveSpeed.Magnitude(), 0.1f) * wheelie; + ApplyTurnForce(0.5f*CTimer::GetTimeStep()*wheelieStab*m_fTurnMass*GetUp(), worldCOM+GetForward()); + ApplyTurnForce(0.5f*CTimer::GetTimeStep()*m_fWheelAngle*pBikeHandling->fWheelieSteer*m_fTurnMass*GetRight(), worldCOM+GetForward()); + }else if(m_aWheelTimer[BIKESUSP_R1] == 0.0f && m_aWheelTimer[BIKESUSP_R2] == 0.0f && GetForward().z < 0.0 && + !(m_aWheelTimer[BIKESUSP_F1] == 0.0f && m_aWheelTimer[BIKESUSP_F2] == 0.0f)){ + // Stoppie + float stoppie = pBikeHandling->fStoppieAng - GetForward().z; + if(stoppie > 0.15f) + // below stoppie angle + stoppie = Max(0.3f - stoppie, 0.0f); + else if(stoppie < -0.15f) + // above stoppie angle + stoppie = Min(-0.3f - stoppie, 0.0f); + float speed = m_vecMoveSpeed.Magnitude(); + float stoppieStab = pBikeHandling->fStoppieStabMult * Min(speed, 0.1f) * stoppie; + ApplyTurnForce(0.5f*CTimer::GetTimeStep()*stoppieStab*m_fTurnMass*GetUp(), worldCOM+GetForward()); + ApplyTurnForce(0.5f*Min(5.0f*speed,1.0f)*CTimer::GetTimeStep()*m_fWheelAngle*pBikeHandling->fWheelieSteer*m_fTurnMass*GetRight(), worldCOM+GetForward()); + } + } + } +} + +#pragma optimize("", on) + +void +CBike::Teleport(CVector pos) +{ + CWorld::Remove(this); + + SetPosition(pos); + SetOrientation(0.0f, 0.0f, 0.0f); + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + + ResetSuspension(); + + CWorld::Add(this); +} + +void +CBike::PreRender(void) +{ + int i; + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + // Wheel particles + + if(m_aWheelState[BIKEWHEEL_REAR] != WHEEL_STATE_NORMAL && + m_aWheelColPoints[BIKESUSP_R2].surfaceB != SURFACE_WATER && m_aWheelTimer[BIKESUSP_R2] > 0.0f){ + static float smokeSize = 0.2f; + CVector groundPos = m_aWheelColPoints[BIKESUSP_R2].point; + if(m_aSuspensionSpringRatioPrev[BIKESUSP_R1] < 1.0f) + groundPos = (groundPos + m_aWheelColPoints[BIKESUSP_R1].point)/2.0f; + groundPos += Sin(m_fLeanLRAngle) * 0.8f*GetColModel()->boundingBox.min.z * GetRight(); + CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, + groundPos + CVector(0.0f, 0.0f, 0.25f), CVector(0.0f, 0.0f, 0.0f), + nil, smokeSize); + + CSkidmarks::RegisterOne((uintptr)this, groundPos, GetForward().x, GetForward().y, + m_aWheelSkidmarkType[BIKEWHEEL_REAR], &m_aWheelSkidmarkBloody[BIKEWHEEL_REAR]); + + if(m_aWheelState[BIKEWHEEL_REAR] == WHEEL_STATE_SPINNING && + (CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[BIKESUSP_R2].surfaceB) == ADHESIVE_HARD || + CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[BIKESUSP_R2].surfaceB) == ADHESIVE_ROAD)){ + CParticle::AddParticle(PARTICLE_BURNINGRUBBER_SMOKE, + groundPos + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.0f)); + CParticle::AddParticle(PARTICLE_BURNINGRUBBER_SMOKE, + groundPos + CVector(0.0f, 0.0f, 0.25f), + CVector(0.0f, 0.0f, 0.05f)); + } + }else if(m_aWheelSkidmarkBloody[BIKEWHEEL_REAR] || m_aWheelSkidmarkUnk[BIKEWHEEL_REAR]){ + CVector groundPos = m_aWheelColPoints[BIKESUSP_R2].point; + groundPos += Sin(m_fLeanLRAngle) * 0.8f*GetColModel()->boundingBox.min.z * GetRight(); + + CSkidmarks::RegisterOne((uintptr)this, groundPos, GetForward().x, GetForward().y, + m_aWheelSkidmarkType[BIKEWHEEL_REAR], &m_aWheelSkidmarkBloody[BIKEWHEEL_REAR]); + } + + // Process lights + + // Turn lights on/off + bool shouldLightsBeOn = + CClock::GetHours() > 20 || + CClock::GetHours() > 19 && CClock::GetMinutes() > (m_randomSeed & 0x3F) || + CClock::GetHours() < 7 || + CClock::GetHours() < 8 && CClock::GetMinutes() < (m_randomSeed & 0x3F) || + m_randomSeed/50000.0f < CWeather::Foggyness || + m_randomSeed/50000.0f < CWeather::WetRoads; + if(shouldLightsBeOn != bLightsOn && GetStatus() != STATUS_WRECKED){ + if(GetStatus() == STATUS_ABANDONED){ + // Turn off lights on abandoned vehicles only when we they're far away + if(bLightsOn && + Abs(TheCamera.GetPosition().x - GetPosition().x) + Abs(TheCamera.GetPosition().y - GetPosition().y) > 100.0f) + bLightsOn = false; + }else + bLightsOn = shouldLightsBeOn; + } + + // Actually render the lights + bool alarmOn = false; + bool alarmOff = false; + if(IsAlarmOn()){ + if(CTimer::GetTimeInMilliseconds() & 0x100) + alarmOn = true; + else + alarmOff = true; + } + if(bEngineOn && bLightsOn || alarmOn || alarmOff){ + CalculateLeanMatrix(); + CVector lookVector = GetPosition() - TheCamera.GetPosition(); + float camDist = lookVector.Magnitude(); + if(camDist != 0.0f) + lookVector *= 1.0f/camDist; + else + lookVector = CVector(1.0f, 0.0f, 0.0f); + + // 1.0 if directly behind car, -1.0 if in front + float behindness = DotProduct(lookVector, GetForward()); + // 0.0 if behind car, PI if in front + float angle = Abs(Acos(Abs(behindness))); + + // Headlight + + CMatrix mat; + CVector headLightPos = mi->m_positions[CAR_POS_HEADLIGHTS]; + if(GetModelIndex() == 152){ // this is the bobcat in VC, but we don't want that effect anyway + mat.SetUnity(); + mat.RotateZ(m_fWheelAngle); + mat = m_leanMatrix * mat; + }else + mat = m_leanMatrix; + CVector light = mat * headLightPos; + if(behindness < 0.0f){ + // In front of bike + float intensity = -0.5f*behindness + 0.3f; + float size = 1.0f - behindness; + + if(behindness < -0.97f && camDist < 30.0f){ + // Directly in front and not too far away + if(pHandling->Flags & HANDLING_HALOGEN_LIGHTS){ + CCoronas::RegisterCorona((uintptr)this + 6, 150, 150, 195, 255, + light, 1.2f, 45.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle); + }else{ + CCoronas::RegisterCorona((uintptr)this + 6, 160, 160, 140, 255, + light, 1.2f, 45.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle); + } + } + + if(alarmOff){ + CCoronas::RegisterCorona((uintptr)this, 0, 0, 0, 0, + light, size, 0.0f, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + }else{ + if(pHandling->Flags & HANDLING_HALOGEN_LIGHTS){ + CCoronas::RegisterCorona((uintptr)this + 1, 190*intensity, 190*intensity, 255*intensity, 255, + light, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + }else{ + CCoronas::RegisterCorona((uintptr)this + 1, 210*intensity, 210*intensity, 195*intensity, 255, + light, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + } + } + }else{ + CCoronas::UpdateCoronaCoors((uintptr)this, light, 50.0f*TheCamera.LODDistMultiplier, angle); + } + + // bright light + CBrightLights::RegisterOne(light, GetUp(), GetRight(), GetForward(), pHandling->FrontLights + BRIGHTLIGHT_FRONT); + + // Taillight + + CVector tailLightPos = mi->m_positions[CAR_POS_TAILLIGHTS]; + light = m_leanMatrix * tailLightPos; + + // Taillight corona + if(behindness > 0.0f){ + // Behind car + float intensity = 0.4f*behindness + 0.4f; + float size = (behindness + 1.0f)/2.0f; + + if(m_fGasPedal < 0.0f){ + // reversing + // no lights in this case + }else{ + if(m_fBrakePedal > 0.0f){ + intensity += 0.4f; + size += 0.3f; + } + + if(alarmOff){ + CCoronas::RegisterCorona((uintptr)this + 14, 0, 0, 0, 0, + light, size, 0.0f, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + }else{ + CCoronas::RegisterCorona((uintptr)this + 14, 128*intensity, 0, 0, 255, + light, size, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); + } + } + }else{ + CCoronas::UpdateCoronaCoors((uintptr)this + 14, light, 50.0f*TheCamera.LODDistMultiplier, angle); + } + + // bright light + CBrightLights::RegisterOne(light, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_REAR); + + // Light shadows + if(!alarmOff){ + CVector pos = GetPosition(); + CVector2D fwd(GetForward()); + fwd.Normalise(); + float f = headLightPos.y + 6.0f; + pos += CVector(f*fwd.x, f*fwd.y, 2.0f); + CShadows::StoreCarLightShadow(this, (uintptr)this + 22, gpShadowExplosionTex, &pos, + 7.0f*fwd.x, 7.0f*fwd.y, 3.5f*fwd.y, -3.5f*fwd.x, 45, 45, 45, 7.0f); + + f = (tailLightPos.y - 2.5f) - (headLightPos.y + 6.0f); + pos += CVector(f*fwd.x, f*fwd.y, 0.0f); + CShadows::StoreCarLightShadow(this, (uintptr)this + 25, gpShadowExplosionTex, &pos, + 3.0f, 0.0f, 0.0f, -3.0f, 35, 0, 0, 4.0f); + } + + if(this == FindPlayerVehicle() && !alarmOff){ + CPointLights::AddLight(CPointLights::LIGHT_DIRECTIONAL, GetPosition(), GetForward(), + 20.0f, 1.0f, 1.0f, 1.0f, + FindPlayerVehicle()->m_vecMoveSpeed.MagnitudeSqr2D() < sq(0.45f) ? CPointLights::FOG_NORMAL : CPointLights::FOG_NONE, + false); + CVector pos = GetPosition() - 4.0f*GetForward(); + if(m_fBrakePedal > 0.0f) + CPointLights::AddLight(CPointLights::LIGHT_POINT, pos, CVector(0.0f, 0.0f, 0.0f), + 10.0f, 1.0f, 0.0f, 0.0f, + CPointLights::FOG_NONE, false); + else + CPointLights::AddLight(CPointLights::LIGHT_POINT, pos, CVector(0.0f, 0.0f, 0.0f), + 7.0f, 0.6f, 0.0f, 0.0f, + CPointLights::FOG_NONE, false); + } + }else if(GetStatus() != STATUS_ABANDONED && GetStatus() != STATUS_WRECKED){ + // Lights off + CalculateLeanMatrix(); + + CVector tailLightPos = mi->m_positions[CAR_POS_TAILLIGHTS]; + CVector light = m_leanMatrix * tailLightPos; + + if(m_fBrakePedal > 0.0f || m_fGasPedal < 0.0f){ + CVector lookVector = GetPosition() - TheCamera.GetPosition(); + lookVector.Normalise(); + float behindness = DotProduct(lookVector, GetForward()); + if(behindness > 0.0f){ + if(m_fGasPedal < 0.0f){ + // reversing + // no lights in this case + }else{ + // braking + CCoronas::RegisterCorona((uintptr)this + 14, 120, 0, 0, 255, + light, 1.2f, 50.0f*TheCamera.LODDistMultiplier, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + CBrightLights::RegisterOne(light, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_REAR); + } + }else{ + CCoronas::UpdateCoronaCoors((uintptr)this + 14, light, 50.0f*TheCamera.LODDistMultiplier, 0.0f); + } + }else{ + CCoronas::UpdateCoronaCoors((uintptr)this + 14, light, 50.0f*TheCamera.LODDistMultiplier, 0.0f); + } + } + + + // Wheel particles + + float fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward())*180.0f; + int drawParticles = Abs(fwdSpeed) < 90.0f; + int susp = BIKESUSP_F1; + for(i = 0; i < 2; i++){ + if(i == BIKEWHEEL_REAR) + susp = BIKESUSP_R1; + + static float speedSq; + // Sparks for friction of burst wheels + if(m_wheelStatus[i] == WHEEL_STATUS_BURST && m_aSuspensionSpringRatioPrev[susp] < 1.0f && + (speedSq = m_vecMoveSpeed.MagnitudeSqr(), speedSq > SQR(0.1f)) && + m_aWheelColPoints[susp].surfaceB != SURFACE_GRASS && + m_aWheelColPoints[susp].surfaceB != SURFACE_MUD_DRY && + m_aWheelColPoints[susp].surfaceB != SURFACE_SAND && + m_aWheelColPoints[susp].surfaceB != SURFACE_SAND_BEACH){ + CVector normalSpeed = m_aWheelColPoints[susp].normal * DotProduct(m_aWheelColPoints[susp].normal, m_vecMoveSpeed); + CVector frictionSpeed = m_vecMoveSpeed - normalSpeed; + CVector sparkDir = 0.25f*frictionSpeed; + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[susp].point, sparkDir); + + if(speedSq > 0.04f) + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[susp].point, sparkDir); + if(speedSq > 0.16f){ + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[susp].point, sparkDir); + CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[susp].point, sparkDir); + } + }else if(m_aSuspensionSpringRatioPrev[i] < 1.0f && + (fwdSpeed > 0.2f || m_aWheelState[i] == WHEEL_STATE_SPINNING)){ + if(m_aWheelColPoints[susp].surfaceB == SURFACE_GRASS || + m_aWheelColPoints[susp].surfaceB == SURFACE_MUD_DRY || + m_aWheelColPoints[susp].surfaceB == SURFACE_SAND || + m_aWheelColPoints[susp].surfaceB == SURFACE_SAND_BEACH) + AddWheelDirtAndWater(&m_aWheelColPoints[susp], drawParticles); + } + } + + AddDamagedVehicleParticles(); + CShadows::StoreShadowForVehicle(this, VEH_SHD_TYPE_BIKE); + + CMatrix mat; + CVector pos; + CColModel *colModel = mi->GetColModel(); + + // Wheel rotation + CVector frontWheelFwd = Multiply3x3(GetMatrix(), CVector(-Sin(m_fSteerAngle), Cos(m_fSteerAngle), 0.0f)); + CVector rearWheelFwd = GetForward(); + if(m_aWheelTimer[BIKESUSP_F1] > 0.0f || m_aWheelTimer[BIKESUSP_F2] > 0.0f){ + float springRatio = Min(m_aSuspensionSpringRatioPrev[BIKESUSP_F1], m_aSuspensionSpringRatioPrev[BIKESUSP_F2]); + CVector contactPoint(0.0f, + (colModel->lines[BIKESUSP_F1].p0.y - colModel->lines[BIKESUSP_F2].p0.y)/2.0f, + colModel->lines[BIKESUSP_F1].p0.z - m_aSuspensionSpringLength[BIKESUSP_F1]*springRatio - 0.5f*mi->m_wheelScale); + CVector contactSpeed = GetSpeed(contactPoint); + // Why is wheel state always normal? + m_aWheelSpeed[BIKEWHEEL_FRONT] = ProcessWheelRotation(WHEEL_STATE_NORMAL, frontWheelFwd, contactSpeed, 0.5f*mi->m_wheelScale); + m_aWheelRotation[BIKEWHEEL_FRONT] += m_aWheelSpeed[BIKEWHEEL_FRONT]; + } + if(m_aWheelTimer[BIKESUSP_R1] > 0.0f || m_aWheelTimer[BIKESUSP_R2] > 0.0f){ + float springRatio = Min(m_aSuspensionSpringRatioPrev[BIKESUSP_R1], m_aSuspensionSpringRatioPrev[BIKESUSP_R2]); + CVector contactPoint(0.0f, + (colModel->lines[BIKESUSP_R1].p0.y - colModel->lines[BIKESUSP_R2].p0.y)/2.0f, + colModel->lines[BIKESUSP_R1].p0.z - m_aSuspensionSpringLength[BIKESUSP_R1]*springRatio - 0.5f*mi->m_wheelScale); + CVector contactSpeed = GetSpeed(contactPoint); + m_aWheelSpeed[BIKEWHEEL_REAR] = ProcessWheelRotation(m_aWheelState[BIKEWHEEL_REAR], rearWheelFwd, contactSpeed, 0.5f*mi->m_wheelScale); + m_aWheelRotation[BIKEWHEEL_REAR] += m_aWheelSpeed[BIKEWHEEL_REAR]; + } + + // Front fork + if(m_aBikeNodes[BIKE_FORKS_FRONT]){ + mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_FORKS_FRONT])); + pos = mat.GetPosition(); + + RwMatrix rwrot; + // TODO: this looks like some weird ctor we don't have + CMatrix rot; + rot.m_attachment = &rwrot; + rot.SetUnity(); + rot.UpdateRW(); + + // Make rotation matrix with front fork as axis + CVector forkAxis(0.0f, Sin(DEGTORAD(mi->m_bikeSteerAngle)), -Cos(DEGTORAD(mi->m_bikeSteerAngle))); + forkAxis.Normalise(); // as if that's not already the case + CQuaternion quat; + quat.Set(&forkAxis, -m_fWheelAngle); + quat.Get(rot.m_attachment); + rot.Update(); + + // Transform fork + mat.SetUnity(); + mat = mat * rot; + mat.Translate(pos); + mat.UpdateRW(); + + if(m_aBikeNodes[BIKE_HANDLEBARS]){ + // Transform handle + mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_HANDLEBARS])); + pos = mat.GetPosition(); + if(GetStatus() == STATUS_ABANDONED || GetStatus() == STATUS_WRECKED){ + mat.SetUnity(); + mat = mat * rot; + mat.Translate(pos); + }else + mat.SetTranslate(mat.GetPosition()); + mat.UpdateRW(); + } + } + + // Rear fork + if(m_aBikeNodes[BIKE_FORKS_REAR]){ + float sine = (m_aWheelPosition[BIKEWHEEL_REAR] - m_aWheelBasePosition[BIKEWHEEL_REAR])/m_fRearForkLength; + mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_FORKS_REAR])); + pos = mat.GetPosition(); + mat.SetRotate(-Asin(sine), 0.0f, 0.0f); + mat.Translate(pos); + mat.UpdateRW(); + } + + // Front wheel + mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_WHEEL_FRONT])); + pos.x = mat.GetPosition().x; + pos.z = m_aWheelPosition[BIKEWHEEL_FRONT] - m_fFrontForkZ; + float y = (colModel->lines[BIKESUSP_F1].p0.y+colModel->lines[BIKESUSP_F2].p0.y)/2.0f - m_fFrontForkY; + pos.y = y - (m_aWheelPosition[BIKEWHEEL_FRONT] - m_aWheelBasePosition[BIKEWHEEL_FRONT])*m_fFrontForkSlope; + if(m_wheelStatus[BIKEWHEEL_FRONT] == WHEEL_STATUS_BURST) + mat.SetRotate(m_aWheelRotation[BIKEWHEEL_FRONT], 0.0f, 0.05f*Sin(m_aWheelRotation[BIKEWHEEL_FRONT])); + else + mat.SetRotateX(m_aWheelRotation[BIKEWHEEL_FRONT]); + mat.Translate(pos); + mat.UpdateRW(); + // and mudguard + mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_MUDGUARD])); + mat.SetTranslateOnly(pos); + mat.UpdateRW(); + + // Rear wheel + mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_WHEEL_REAR])); + pos = mat.GetPosition(); + if(m_wheelStatus[BIKEWHEEL_REAR] == WHEEL_STATUS_BURST) + mat.SetRotate(m_aWheelRotation[BIKEWHEEL_REAR], 0.0f, 0.07f*Sin(m_aWheelRotation[BIKEWHEEL_REAR])); + else + mat.SetRotateX(m_aWheelRotation[BIKEWHEEL_REAR]); + mat.Translate(pos); + mat.UpdateRW(); + + // Chassis + if(m_aBikeNodes[BIKE_CHASSIS]){ + mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_CHASSIS])); + pos = mat.GetPosition(); + pos.z = (1.0f - Cos(m_fLeanLRAngle)) * (0.9*colModel->boundingBox.min.z); + mat.SetRotateX(-0.05f*Abs(m_fLeanLRAngle)); + mat.RotateY(m_fLeanLRAngle); + mat.Translate(pos); + mat.UpdateRW(); + } + + // Exhaust smoke + if(bEngineOn && !(pHandling->Flags & HANDLING_NO_EXHAUST) && fwdSpeed < 130.0f){ + CVector exhaustPos = mi->m_positions[CAR_POS_EXHAUST]; + CVector pos1, pos2, dir; + + if(exhaustPos != CVector(0.0f, 0.0f, 0.0f)){ + dir.z = 0.0f; + if(fwdSpeed < 10.0f){ + CVector steerFwd(-Sin(m_fSteerAngle), Cos(m_fSteerAngle), 0.0f); + steerFwd = Multiply3x3(GetMatrix(), steerFwd); + float r = CGeneral::GetRandomNumberInRange(-0.06f, -0.03f); + dir.x = steerFwd.x * r; + dir.y = steerFwd.y * r; + }else{ + dir.x = m_vecMoveSpeed.x; + dir.y = m_vecMoveSpeed.y; + } + + bool dblExhaust = false; + pos1 = GetMatrix() * exhaustPos; + if(pHandling->Flags & HANDLING_DBL_EXHAUST){ + dblExhaust = true; + pos2 = exhaustPos; + pos2.x = -pos2.x; + pos2 = GetMatrix() * pos2; + } + + static float fumesLimit = 2.0f; + if(CGeneral::GetRandomNumberInRange(1.0f, 3.0f)*(m_fGasPedal+1.1f) > fumesLimit){ + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir); + if(dblExhaust) + CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos2, dir); + + if(GetStatus() == STATUS_PLAYER && (CTimer::GetFrameCounter()&3) == 0 && + CWeather::Rain == 0.0f){ + CVector camDist = GetPosition() - TheCamera.GetPosition(); + if(DotProduct(GetForward(), camDist) > 0.0f || + TheCamera.GetLookDirection() == LOOKING_LEFT || + TheCamera.GetLookDirection() == LOOKING_RIGHT){ + if(dblExhaust) + pos1 = 0.5f*pos1 + 0.5f*pos2; + + if(TheCamera.GetLookDirection() == LOOKING_LEFT || + TheCamera.GetLookDirection() == LOOKING_RIGHT) + pos1 -= 0.2f*GetForward(); + + CParticle::AddParticle(PARTICLE_HEATHAZE, pos1, CVector(0.0f, 0.0f, 0.0f)); + } + } + } + } + } +} + +void +CBike::Render(void) +{ + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 3000; + mi->SetVehicleColour(m_currentColour1, m_currentColour2); + CEntity::Render(); +} + +int32 +CBike::ProcessEntityCollision(CEntity *ent, CColPoint *colpoints) +{ + int i; + CColModel *colModel; + + if(GetStatus() != STATUS_SIMPLE) + bVehicleColProcessed = true; + + colModel = GetColModel(); + + int numWheelCollisions = 0; + float prevRatios[4] = { 0.0f, 0.0f, 0.0f, 0.0f}; + for(i = 0; i < 4; i++) + prevRatios[i] = m_aSuspensionSpringRatio[i]; + + if(m_bIsVehicleBeingShifted || bSkipLineCol || ent->IsPed() || + GetModelIndex() == MI_DODO && ent->IsVehicle()) + colModel->numLines = 0; + + int numCollisions = CCollision::ProcessColModels(GetMatrix(), *colModel, + ent->GetMatrix(), *ent->GetColModel(), + colpoints, + m_aWheelColPoints, m_aSuspensionSpringRatio); + + // m_aSuspensionSpringRatio are now set to the point where the tyre touches ground. + // In ProcessControl these will be re-normalized to ignore the tyre radius. + if(colModel->numLines){ + for(i = 0; i < 4; i++){ + if(m_aSuspensionSpringRatio[i] < 1.0f && m_aSuspensionSpringRatio[i] < prevRatios[i]){ + numWheelCollisions++; + + // wheel is touching a physical + if(ent->IsVehicle() || ent->IsObject()){ + CPhysical *phys = (CPhysical*)ent; + + m_aGroundPhysical[i] = phys; + phys->RegisterReference((CEntity**)&m_aGroundPhysical[i]); + m_aGroundOffset[i] = m_aWheelColPoints[i].point - phys->GetPosition(); + } + + m_nSurfaceTouched = m_aWheelColPoints[i].surfaceB; + if(ent->IsBuilding()) + m_pCurGroundEntity = ent; + } + } + }else + colModel->numLines = 4; + + if(numCollisions > 0 || numWheelCollisions > 0){ + AddCollisionRecord(ent); + if(!ent->IsBuilding()) + ((CPhysical*)ent)->AddCollisionRecord(this); + + if(numCollisions > 0) + if(ent->IsBuilding() || + ent->IsObject() && ((CPhysical*)ent)->bInfiniteMass) + bHasHitWall = true; + } + + return numCollisions; +} + +static int16 nLastControlInput; +static float fMouseCentreRange = 0.35f; +static float fMouseSteerSens = -0.0035f; +static float fMouseCentreMult = 0.975f; + +void +CBike::ProcessControlInputs(uint8 pad) +{ + float speed = DotProduct(m_vecMoveSpeed, GetForward()); + + if(CPad::GetPad(pad)->GetExitVehicle()) + bIsHandbrakeOn = true; + else + bIsHandbrakeOn = !!CPad::GetPad(pad)->GetHandBrake(); + + // Steer left/right +#ifdef FIX_BUGS + if(CCamera::m_bUseMouse3rdPerson && !CVehicle::m_bDisableMouseSteering){ + if(CPad::GetPad(pad)->GetMouseX() != 0.0f){ + m_fSteerInput += fMouseSteerSens*CPad::GetPad(pad)->GetMouseX(); + nLastControlInput = 2; + if(Abs(m_fSteerInput) < fMouseCentreRange) + m_fSteerInput *= Pow(fMouseCentreMult, CTimer::GetTimeStep()); + }else if(CPad::GetPad(pad)->GetSteeringLeftRight() || nLastControlInput != 2){ + // mouse hasn't move, steer with pad like below + m_fSteerInput += (-CPad::GetPad(pad)->GetSteeringLeftRight()/128.0f - m_fSteerInput)* + 0.2f*CTimer::GetTimeStep(); + nLastControlInput = 0; + } + }else +#endif + { + m_fSteerInput += (-CPad::GetPad(pad)->GetSteeringLeftRight()/128.0f - m_fSteerInput)* + 0.2f*CTimer::GetTimeStep(); + nLastControlInput = 0; + } + m_fSteerInput = Clamp(m_fSteerInput, -1.0f, 1.0f); + + // Lean forward/backward + float updown; +#ifdef FREE_CAM + if (CCamera::bFreeCam) updown = CPad::IsAffectedByController ? -CPad::GetPad(pad)->GetSteeringUpDown()/128.0f : CPad::GetPad(pad)->GetCarGunUpDown()/128.0f; + else +#endif + updown = -CPad::GetPad(pad)->GetSteeringUpDown()/128.0f + CPad::GetPad(pad)->GetCarGunUpDown()/128.0f; + m_fLeanInput += (updown - m_fLeanInput)*0.2f*CTimer::GetTimeStep(); + m_fLeanInput = Clamp(m_fLeanInput, -1.0f, 1.0f); + + // Accelerate/Brake + float acceleration = (CPad::GetPad(pad)->GetAccelerate() - CPad::GetPad(pad)->GetBrake())/255.0f; + if(GetModelIndex() == MI_DODO && acceleration < 0.0f) + acceleration *= 0.3f; + if(Abs(speed) < 0.01f){ + // standing still, go into direction we want + if(CPad::GetPad(pad)->GetAccelerate() > 150.0f && CPad::GetPad(pad)->GetBrake() > 150.0f){ + m_fGasPedal = CPad::GetPad(pad)->GetAccelerate()/255.0f; + m_fBrakePedal = CPad::GetPad(pad)->GetBrake()/255.0f; + m_doingBurnout = 1; + }else{ + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + } + }else{ +#if 1 + // simpler than the code below + if(speed * acceleration < 0.0f){ + // if opposite directions, have to brake first + m_fGasPedal = 0.0f; + m_fBrakePedal = Abs(acceleration); + }else{ + // accelerating in same direction we were already going + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + } +#else + if(speed < 0.0f){ + // moving backwards currently + if(acceleration < 0.0f){ + // still go backwards + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + }else{ + // want to go forwards, so brake + m_fGasPedal = 0.0f; + m_fBrakePedal = acceleration; + } + }else{ + // moving forwards currently + if(acceleration < 0.0f){ + // want to go backwards, so brake + m_fGasPedal = 0.0f; + m_fBrakePedal = -acceleration; + }else{ + // still go forwards + m_fGasPedal = acceleration; + m_fBrakePedal = 0.0f; + } + } +#endif + } + + // Actually turn wheels + static float fValue; // why static? + if(m_fSteerInput < 0.0f) + fValue = -sq(m_fSteerInput); + else + fValue = sq(m_fSteerInput); + m_fSteerAngle = DEGTORAD(pHandling->fSteeringLock) * fValue; + + if(bComedyControls){ + if(((CTimer::GetTimeInMilliseconds() >> 10) & 0xF) < 12) + m_fGasPedal = 1.0f; + if((((CTimer::GetTimeInMilliseconds() >> 10)+6) & 0xF) < 12) + m_fBrakePedal = 0.0f; + bIsHandbrakeOn = false; + if(CTimer::GetTimeInMilliseconds() & 0x800) + m_fSteerAngle += 0.08f; + else + m_fSteerAngle -= 0.03f; + } + + // Brake if player isn't in control + // BUG: game always uses pad 0 here + if(CPad::GetPad(pad)->ArePlayerControlsDisabled()){ + m_fBrakePedal = 1.0f; + bIsHandbrakeOn = true; + m_fGasPedal = 0.0f; + + FindPlayerPed()->KeepAreaAroundPlayerClear(); + + // slow down car immediately + speed = m_vecMoveSpeed.Magnitude(); + if(speed > 0.28f) + m_vecMoveSpeed *= 0.28f/speed; + } +} + +void +CBike::ProcessBuoyancy(void) +{ + int i; + CVector impulse, point; + + if(mod_Buoyancy.ProcessBuoyancy(this, m_fBuoyancy, &point, &impulse)){ + bTouchingWater = true; + ApplyMoveForce(impulse); + ApplyTurnForce(impulse, point); + + float timeStep = Max(CTimer::GetTimeStep(), 0.01f); + float impulseRatio = impulse.z / (GRAVITY * m_fMass * timeStep); + float waterResistance = Pow(1.0f - 0.05f*impulseRatio, CTimer::GetTimeStep()); + m_vecMoveSpeed *= waterResistance; + m_vecTurnSpeed *= waterResistance; + + if(impulseRatio > 0.8f || + impulseRatio > 0.4f && (m_aSuspensionSpringRatio[0] == 1.0f || + m_aSuspensionSpringRatio[1] == 1.0f || + m_aSuspensionSpringRatio[2] == 1.0f || + m_aSuspensionSpringRatio[3] == 1.0f)){ + bIsInWater = true; + bIsDrowning = true; + if(m_vecMoveSpeed.z < -0.1f) + m_vecMoveSpeed.z = -0.1f; + + if(pDriver){ + pDriver->bIsInWater = true; + if(pDriver->IsPlayer() || !bWaterTight){ + if(m_aSuspensionSpringRatio[0] < 1.0f || + m_aSuspensionSpringRatio[1] < 1.0f || + m_aSuspensionSpringRatio[2] < 1.0f || + m_aSuspensionSpringRatio[3] < 1.0f) + pDriver->InflictDamage(nil, WEAPONTYPE_DROWNING, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); + else + KnockOffRider(WEAPONTYPE_DROWNING, 0, pDriver, false); + } + } + for(i = 0; i < m_nNumMaxPassengers; i++) + if(pPassengers[i]){ + pPassengers[i]->bIsInWater = true; + if(pPassengers[i]->IsPlayer() || !bWaterTight){ + if(m_aSuspensionSpringRatio[0] < 1.0f || + m_aSuspensionSpringRatio[1] < 1.0f || + m_aSuspensionSpringRatio[2] < 1.0f || + m_aSuspensionSpringRatio[3] < 1.0f) + pPassengers[i]->InflictDamage(nil, WEAPONTYPE_DROWNING, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); + else + KnockOffRider(WEAPONTYPE_DROWNING, 0, pPassengers[i], false); + } + } + }else{ + bIsInWater = false; + bIsDrowning = false; + } + }else{ + bIsInWater = false; + bIsDrowning = false; + bTouchingWater = false; + } +} + +void +CBike::DoDriveByShootings(void) +{ + CAnimBlendAssociation *anim; + CPlayerInfo* playerInfo = ((CPlayerPed*)pDriver)->GetPlayerInfoForThisPlayerPed(); + if (playerInfo && !playerInfo->m_bDriveByAllowed) + return; + + CWeapon *weapon = pDriver->GetWeapon(); + if(CWeaponInfo::GetWeaponInfo(weapon->m_eWeaponType)->m_nWeaponSlot != 5) + return; + + weapon->Update(pDriver->m_audioEntityId, nil); + + bool lookingLeft = false; + bool lookingRight = false; + if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || + TheCamera.m_bObbeCinematicCarCamOn){ + if(CPad::GetPad(0)->GetLookLeft()) + lookingLeft = true; + if(CPad::GetPad(0)->GetLookRight()) + lookingRight = true; + }else{ + if(TheCamera.Cams[TheCamera.ActiveCam].LookingLeft) + lookingLeft = true; + if(TheCamera.Cams[TheCamera.ActiveCam].LookingRight) + lookingRight = true; + } + + if(lookingLeft || lookingRight || CPad::GetPad(0)->GetCarGunFired()){ + if(lookingLeft){ + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_RHS); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_FORWARD); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_LHS); + if(anim == nil || anim->blendDelta < 0.0f) + anim = CAnimManager::AddAnimation(pDriver->GetClump(), m_bikeAnimType, ANIM_BIKE_DRIVEBY_LHS); + }else if(lookingRight){ + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_LHS); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_FORWARD); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_RHS); + if(anim == nil || anim->blendDelta < 0.0f) + anim = CAnimManager::AddAnimation(pDriver->GetClump(), m_bikeAnimType, ANIM_BIKE_DRIVEBY_RHS); + }else{ + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_RHS); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_LHS); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_FORWARD); + if(anim == nil || anim->blendDelta < 0.0f) + anim = CAnimManager::AddAnimation(pDriver->GetClump(), m_bikeAnimType, ANIM_BIKE_DRIVEBY_FORWARD); + } + + if (!anim || !anim->IsRunning()) { + if (CPad::GetPad(0)->GetCarGunFired() && CTimer::GetTimeInMilliseconds() > weapon->m_nTimer) { + weapon->FireFromCar(this, lookingLeft, lookingRight); + weapon->m_nTimer = CTimer::GetTimeInMilliseconds() + 70; + } + } + }else{ + weapon->Reload(); + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_LHS); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_RHS); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_FORWARD); + if(anim) + anim->blendDelta = -1000.0f; + } +} + +void +CBike::VehicleDamage(void) +{ + float impulse = m_fDamageImpulse; + float colSpeed = 800.0f*impulse/m_fMass; + if(GetStatus() == STATUS_PLAYER) + colSpeed *= 0.65f; + else if(VehicleCreatedBy == MISSION_VEHICLE) + colSpeed *= 0.4f; + + if(!bCanBeDamaged) + return; + + if(GetStatus() == STATUS_PLAYER && CStats::GetPercentageProgress() >= 100.0f) + impulse *= 0.5f; + + if(bIsStanding && impulse > 20.0f) + bIsStanding = false; + + // Inflict damage on the driver and passenger + if(pDriver && pDriver->GetPedState() == PED_DRIVING && colSpeed > 10.0f){ + float fwd = 0.6f; + if(Abs(DotProduct(m_vecDamageNormal, GetForward())) > 0.85f){ + float u = Max(DotProduct(m_vecDamageNormal, CVector(0.0f, 0.0f, 1.0f)), 0.0f); + if(u < 0.85f) + u = 0.0f; + fwd += 7.0f * SQR(u); + } + float up = 0.05f; + + if(GetModelIndex() == MI_SANCHEZ){ + fwd *= 0.65f; + up *= 0.75f; + } + + float total = fwd*Abs(DotProduct(m_vecDamageNormal, GetForward())) + + 0.45f*Abs(DotProduct(m_vecDamageNormal, GetRight())) + + up*Max(DotProduct(m_vecDamageNormal, GetUp()), 0.0f); + float damage = (total - 1.5f*Min(DotProduct(m_vecDamageNormal, GetUp()), 0.0f))*colSpeed; + + if(pDriver->IsPlayer() && CCullZones::CamStairsForPlayer() && CCullZones::FindZoneWithStairsAttributeForPlayer()) + damage = 0.0f; + + if(damage > 75.0f){ + int dir = -10; + if(pDriver){ + dir = pDriver->GetLocalDirection(-m_vecDamageNormal); + if(pDriver->m_fHealth > 0.0f) + pDriver->InflictDamage(m_pDamageEntity, WEAPONTYPE_RAMMEDBYCAR, 0.05f*damage, PEDPIECE_TORSO, dir); + if(pDriver && pDriver->GetPedState() == PED_DRIVING) + KnockOffRider(WEAPONTYPE_RAMMEDBYCAR, dir, pDriver, false); + } + if(pPassengers[0]){ + dir = pPassengers[0]->GetLocalDirection(-m_vecDamageNormal); + if(pPassengers[0]->m_fHealth > 0.0f) + pPassengers[0]->InflictDamage(m_pDamageEntity, WEAPONTYPE_RAMMEDBYCAR, 0.05f*damage, PEDPIECE_TORSO, dir); + if(pPassengers[0] && pPassengers[0]->GetPedState() == PED_DRIVING) + KnockOffRider(WEAPONTYPE_RAMMEDBYCAR, dir, pPassengers[0], false); + } + } + } + + if(impulse > 25.0f && GetStatus() != STATUS_WRECKED){ + float damage = (impulse-25.0f)*pHandling->fCollisionDamageMultiplier; + if(damage > 0.0f){ + if(damage > 5.0f && + pDriver && + m_pDamageEntity && m_pDamageEntity->IsVehicle() && + (this != FindPlayerVehicle() || ((CVehicle*)m_pDamageEntity)->VehicleCreatedBy == MISSION_VEHICLE) && + ((CVehicle*)m_pDamageEntity)->pDriver) + pDriver->Say(SOUND_PED_CRASH_VEHICLE); + + int oldHealth = m_fHealth; + if(this == FindPlayerVehicle()) + m_fHealth -= bTakeLessDamage ? damage/6.0f : damage/2.0f; + else if(bTakeLessDamage) + m_fHealth -= damage/12.0f; + else if(m_pDamageEntity && m_pDamageEntity == FindPlayerVehicle()) + m_fHealth -= damage/1.5f; + else + m_fHealth -= damage/4.0f; + if(m_fHealth <= 0.0f && oldHealth > 0) + m_fHealth = 1.0f; + } + } + + if(m_fHealth < 250.0f){ + // Car is on fire + if(!bIsOnFire){ + // Set engine on fire and remember who did this + bIsOnFire = true; + m_fFireBlowUpTimer = 0.0f; + m_pSetOnFireEntity = m_pDamageEntity; + if(m_pSetOnFireEntity) + m_pSetOnFireEntity->RegisterReference(&m_pSetOnFireEntity); + } + } +} + +void +CBike::AddDamagedVehicleParticles(void) +{ + if(this == FindPlayerVehicle() && TheCamera.GetLookingForwardFirstPerson()) + return; + if(this != FindPlayerVehicle() && (CTimer::GetFrameCounter() + m_randomSeed) & 1) + return; + if(m_fHealth >= 650.0f) + return; + + CVector direction = 0.5f*m_vecMoveSpeed; + CVector damagePos = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->GetFrontSeatPosn(); + + damagePos.z -= 0.4f; + damagePos = GetMatrix()*damagePos; + + CalculateLeanMatrix(); + + if(m_fHealth < 250.0f){ + // fire, done in processControl + }else if(m_fHealth < 320.0f){ + direction *= 0.2f; + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, damagePos, direction + 0.02f*m_leanMatrix.GetRight()); + }else if(m_fHealth < 390.0f){ + if(((CTimer::GetFrameCounter() + m_randomSeed) & 3) == 0 || + ((CTimer::GetFrameCounter() + m_randomSeed) & 3) == 2) + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, direction + 0.05f*m_leanMatrix.GetRight()); + direction *= 0.3f; + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, damagePos, direction + 0.04f*m_leanMatrix.GetRight()); + }else if(m_fHealth < 460.0f){ + int rnd = CTimer::GetFrameCounter() + m_randomSeed; + if(rnd < 10 || + rnd < 70 && rnd > 25 || + rnd < 160 && rnd > 100 || + rnd < 200 && rnd > 175 || + rnd > 235) + return; + direction.z += 0.05f; + if(TheCamera.GetLookDirection() != LOOKING_FORWARD){ + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, direction + 0.08f*m_leanMatrix.GetRight(), nil, 0.1f, 0, 0, 0, 1000); + }else if(((CTimer::GetFrameCounter() + m_randomSeed) & 1) == 0){ + direction = 0.8f*m_vecMoveSpeed; + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, direction + 0.07f*m_leanMatrix.GetRight(), nil, 0.1f, 0, 0, 0, 1000); + } + }else if(((CTimer::GetFrameCounter() + m_randomSeed) & 3) == 0 || + ((CTimer::GetFrameCounter() + m_randomSeed) & 3) == 2){ + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos + 0.06f*m_leanMatrix.GetRight(), direction); + } +} + +int32 +CBike::AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed) +{ + int i; + CVector dir; + static float minSize = 0.02f; + static float maxSize = 0.04f; + static RwRGBA grassCol = { 8, 24, 8, 255 }; + static RwRGBA gravelCol = { 64, 64, 64, 255 }; + static RwRGBA mudCol = { 64, 32, 16, 255 }; + static RwRGBA sandCol = { 170, 165, 140, 255 }; + static RwRGBA waterCol = { 48, 48, 64, 0 }; + + if(!belowEffectSpeed && + colpoint->surfaceB != SURFACE_SAND && colpoint->surfaceB != SURFACE_SAND_BEACH) + return 0; + + switch(colpoint->surfaceB){ + case SURFACE_GRASS: + dir.x = -0.05f*m_vecMoveSpeed.x; + dir.y = -0.05f*m_vecMoveSpeed.y; + for(i = 0; i < 4; i++){ + dir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.04f); + CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil, + CGeneral::GetRandomNumberInRange(minSize, maxSize), grassCol); + } + return 0; + case SURFACE_GRAVEL: + dir.x = -0.05f*m_vecMoveSpeed.x; + dir.y = -0.05f*m_vecMoveSpeed.y; + for(i = 0; i < 4; i++){ + dir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.04f); + CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil, + CGeneral::GetRandomNumberInRange(minSize, maxSize), gravelCol); + } + return 1; + case SURFACE_MUD_DRY: + dir.x = -0.05f*m_vecMoveSpeed.x; + dir.y = -0.05f*m_vecMoveSpeed.y; + for(i = 0; i < 4; i++){ + dir.z = CGeneral::GetRandomNumberInRange(0.03f, 0.04f); + CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil, + CGeneral::GetRandomNumberInRange(minSize, maxSize), mudCol); + } + return 0; + case SURFACE_SAND: + case SURFACE_SAND_BEACH: + if(CTimer::GetFrameCounter() & 2) + return 0; + dir.x = 0.75f*m_vecMoveSpeed.x; + dir.y = 0.75f*m_vecMoveSpeed.y; + for(i = 0; i < 1; i++){ + dir.z = CGeneral::GetRandomNumberInRange(0.02f, 0.055f); + CParticle::AddParticle(PARTICLE_SAND, colpoint->point, dir, nil, + 0.8f*m_vecMoveSpeed.Magnitude(), sandCol); + } + return 0; + default: + if(CWeather::WetRoads > 0.01f){ + CParticle::AddParticle( + PARTICLE_WATERSPRAY, + colpoint->point + CVector(0.0f, 0.0f, 0.25f+0.25f), + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.005f, 0.04f)), + nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.5f), waterCol); + return 0; + } + return 1; + } + + return 0; +} + +void +CBike::GetComponentWorldPosition(int32 component, CVector &pos) +{ + if(m_aBikeNodes[component] == nil){ + printf("BikeNode missing: %d %d\n", GetModelIndex(), component); + return; + } + RwMatrix *ltm = RwFrameGetLTM(m_aBikeNodes[component]); + pos = *RwMatrixGetPos(ltm); +} + +bool +CBike::IsComponentPresent(int32 component) +{ + return m_aBikeNodes[component] != nil; +} + +void +CBike::SetComponentRotation(int32 component, CVector rotation) +{ + CMatrix mat(RwFrameGetMatrix(m_aBikeNodes[component])); + CVector pos = mat.GetPosition(); + // BUG: all these set the whole matrix + mat.SetRotateX(DEGTORAD(rotation.x)); + mat.SetRotateY(DEGTORAD(rotation.y)); + mat.SetRotateZ(DEGTORAD(rotation.z)); + mat.Translate(pos); + mat.UpdateRW(); +} + +bool +CBike::IsDoorReady(eDoors door) +{ + return true; +} + +bool +CBike::IsDoorFullyOpen(eDoors door) +{ + return false; +} + +bool +CBike::IsDoorClosed(eDoors door) +{ + return false; +} + +bool +CBike::IsDoorMissing(eDoors door) +{ + return true; +} + +void +CBike::RemoveRefsToVehicle(CEntity *ent) +{ + int i; + for(i = 0; i < 4; i++) + if(m_aGroundPhysical[i] == ent) + m_aGroundPhysical[i] = nil; +} + +void +CBike::BlowUpCar(CEntity *culprit) +{ + if(!bCanBeDamaged) + return; + +#ifdef FIX_BUGS + // taken from CAutomobile. maybe tweak values? + if(culprit == FindPlayerPed() || culprit == FindPlayerVehicle()){ + CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel += 20; + CWorld::Players[CWorld::PlayerInFocus].m_fMediaAttention += 10.0f; + CStats::PropertyDestroyed += CGeneral::GetRandomNumber()%6000 + 4000; + } +#endif + + // explosion pushes vehicle up + m_vecMoveSpeed.z += 0.13f; + SetStatus(STATUS_WRECKED); + bRenderScorched = true; + + m_fHealth = 0.0f; + m_nBombTimer = 0; + + TheCamera.CamShake(0.7f, GetPosition().x, GetPosition().y, GetPosition().z); + + KillPedsInVehicle(); + + bEngineOn = false; + bLightsOn = false; + ChangeLawEnforcerState(false); + + CExplosion::AddExplosion(this, culprit, EXPLOSION_CAR, GetPosition(), 0); + CDarkel::RegisterCarBlownUpByPlayer(this); +} + +bool +CBike::SetUpWheelColModel(CColModel *colModel) +{ + RwMatrix *mat = RwMatrixCreate(); + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + CColModel *vehColModel = mi->GetColModel(); + + colModel->boundingSphere = vehColModel->boundingSphere; + colModel->boundingBox = vehColModel->boundingBox; + + GetRelativeMatrix(mat, m_aBikeNodes[BIKE_WHEEL_FRONT], m_aBikeNodes[BIKE_CHASSIS]); + colModel->spheres[0].Set(0.5f*mi->m_wheelScale, *RwMatrixGetPos(mat), SURFACE_RUBBER, CAR_PIECE_WHEEL_LF); + GetRelativeMatrix(mat, m_aBikeNodes[BIKE_WHEEL_REAR], m_aBikeNodes[BIKE_CHASSIS]); + colModel->spheres[1].Set(0.5f*mi->m_wheelScale, *RwMatrixGetPos(mat), SURFACE_RUBBER, CAR_PIECE_WHEEL_LR); + colModel->numSpheres = 2; +#ifdef FIX_BUGS + RwMatrixDestroy(mat); +#endif + return true; +} + +float fBikeBurstForceMult = 0.02f; +float fBikeBurstFallSpeed = 0.3f; +float fBikeBurstFallSpeedPlayer = 0.55f; + +void +CBike::BurstTyre(uint8 wheel, bool applyForces) +{ + if(bTyresDontBurst) + return; + + switch(wheel){ + case CAR_PIECE_WHEEL_LF: wheel = BIKEWHEEL_FRONT; break; + case CAR_PIECE_WHEEL_LR: wheel = BIKEWHEEL_REAR; break; + } + + if(m_wheelStatus[wheel] == WHEEL_STATUS_OK){ + m_wheelStatus[wheel] = WHEEL_STATUS_BURST; +#ifdef FIX_BUGS + CStats::TyresPopped++; +#endif + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_TYRE_POP, 0.0f); + + if(GetStatus() == STATUS_SIMPLE){ + SetStatus(STATUS_PHYSICS); + CCarCtrl::SwitchVehicleToRealPhysics(this); + } + + if(applyForces){ + ApplyMoveForce(GetRight() * m_fMass * CGeneral::GetRandomNumberInRange(-0.02f, 0.02f)); + ApplyTurnForce(GetRight() * m_fTurnMass * CGeneral::GetRandomNumberInRange(-0.02f, 0.02f), GetForward()); + } + + // This code checks piece types originally so it is never triggered + // as we have converted them to wheel indices above already. + if(pDriver){ +#ifdef FIX_SIGNIFICANT_BUGS + if(wheel == BIKEWHEEL_FRONT && (m_aSuspensionSpringRatioPrev[BIKESUSP_F1] < 1.0f || m_aSuspensionSpringRatioPrev[BIKESUSP_F2] < 1.0f) || + wheel == BIKEWHEEL_REAR && (m_aSuspensionSpringRatioPrev[BIKESUSP_R1] < 1.0f || m_aSuspensionSpringRatioPrev[BIKESUSP_R2] < 1.0f)){ +#else + if(wheel == CAR_PIECE_WHEEL_LF && (m_aSuspensionSpringRatioPrev[BIKESUSP_F1] < 1.0f || m_aSuspensionSpringRatioPrev[BIKESUSP_F2] < 1.0f) || + wheel == CAR_PIECE_WHEEL_LR && (m_aSuspensionSpringRatioPrev[BIKESUSP_R1] < 1.0f || m_aSuspensionSpringRatioPrev[BIKESUSP_R2] < 1.0f)){ +#endif + float speedSq = m_vecMoveSpeed.MagnitudeSqr(); + if(speedSq > fBikeBurstFallSpeed && + (GetStatus() != STATUS_PLAYER || speedSq > fBikeBurstFallSpeedPlayer)){ +#ifdef FIX_SIGNIFICANT_BUGS + if(wheel == BIKEWHEEL_FRONT){ +#else + if(wheel == CAR_PIECE_WHEEL_LF){ +#endif + KnockOffRider(WEAPONTYPE_RAMMEDBYCAR, 0, pDriver, false); + if(pPassengers[0]) + KnockOffRider(WEAPONTYPE_RAMMEDBYCAR, 0, pPassengers[0], false); + }else + ApplyTurnForce(2.0f*fBikeBurstForceMult*m_fTurnMass*GetRight(), GetForward()); + } + } + } + } +} + +bool +CBike::IsRoomForPedToLeaveCar(uint32 component, CVector *doorOffset) +{ + CColPoint colpoint; + CEntity *ent; + colpoint.point = CVector(0.0f, 0.0f, 0.0f); + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + CVector seatPos = mi->GetFrontSeatPosn(); + if(component == CAR_DOOR_RR || component == CAR_DOOR_LR) + seatPos = mi->m_positions[CAR_POS_BACKSEAT]; + if(component == CAR_DOOR_LF || component == CAR_DOOR_LR) + seatPos.x = -seatPos.x; + seatPos = GetMatrix() * seatPos; + + CVector doorPos = CPed::GetPositionToOpenCarDoor(this, component); + if(doorOffset){ + CVector off = *doorOffset; + if(component == CAR_DOOR_RF || component == CAR_DOOR_RR) + off.x = -off.x; + doorPos += Multiply3x3(GetMatrix(), off); + } + + if(GetUp().z < 0.0f){ + seatPos.z += 0.5f; + doorPos.z += 0.5f; + } + + CVector dist = doorPos - seatPos; + + // Removing that makes thiProcessEntityCollisions func. return false for van doors. + doorPos.z += 0.5f; + float length = dist.Magnitude(); + CVector pedPos = seatPos + dist*((length+0.6f)/length); + + if(!CWorld::GetIsLineOfSightClear(seatPos, pedPos, true, false, false, true, false, false)) + return false; + if(CWorld::TestSphereAgainstWorld(doorPos, 0.6f, this, true, true, false, true, false, false)) + return false; + if(CWorld::ProcessVerticalLine(doorPos, 1000.0f, colpoint, ent, true, false, false, true, false, false, nil)) + if(colpoint.point.z > doorPos.z && colpoint.point.z < doorPos.z + 0.6f) + return false; + float upperZ = colpoint.point.z; + if(!CWorld::ProcessVerticalLine(doorPos, -1000.0f, colpoint, ent, true, false, false, true, false, false, nil)) + return false; + if(upperZ != 0.0f && upperZ < colpoint.point.z) + return false; + return true; +} + +float +CBike::GetHeightAboveRoad(void) +{ + return m_fHeightAboveRoad; +} + +void +CBike::PlayCarHorn(void) +{ + uint32 r; + + if (IsAlarmOn() || m_nCarHornTimer != 0) + return; + + if (m_nCarHornDelay) { + m_nCarHornDelay--; + return; + } + + m_nCarHornDelay = (CGeneral::GetRandomNumber() & 0x7F) + 150; + r = m_nCarHornDelay & 7; + if(r < 2){ + m_nCarHornTimer = 45; + }else if(r < 4){ + if(pDriver) + pDriver->Say(SOUND_PED_ANNOYED_DRIVER); + m_nCarHornTimer = 45; + }else{ + if(pDriver) + pDriver->Say(SOUND_PED_ANNOYED_DRIVER); + } +} + +void +CBike::KnockOffRider(eWeaponType weapon, uint8 direction, CPed *ped, bool bGetBackOn) +{ + AnimationId anim = ANIM_STD_KO_FRONT; + if(ped == nil) + return; + + if(!ped->IsPlayer()){ + if(bGetBackOn){ + if(ped->m_pedStats->m_temper > ped->m_pedStats->m_fear && + ped->CharCreatedBy != MISSION_CHAR && ped->m_pMyVehicle->VehicleCreatedBy != MISSION_VEHICLE && + FindPlayerPed()->m_carInObjective == ped->m_pMyVehicle && + !CTheScripts::IsPlayerOnAMission()) + ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, ped->m_pMyVehicle); + else if(ped->m_pedStats->m_temper > ped->m_pedStats->m_fear && + ped->CharCreatedBy != MISSION_CHAR && ped->m_pMyVehicle->VehicleCreatedBy != MISSION_VEHICLE && + !CTheScripts::IsPlayerOnAMission()) + ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, ped->m_pMyVehicle); + else if(ped->m_pedStats->m_temper <= ped->m_pedStats->m_fear && + ped->CharCreatedBy != MISSION_CHAR && ped->m_pMyVehicle->VehicleCreatedBy != MISSION_VEHICLE && + !CTheScripts::IsPlayerOnAMission()){ + ped->SetObjective(OBJECTIVE_WANDER, ped->m_pMyVehicle); + ped->m_nPathDir = CGeneral::GetRandomNumberInRange(0, 8); + } + }else if(ped->m_leader == nil){ + if(pDriver == ped) + ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, this); + else + ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, this); + } + } + + if(ped->IsPed()){ + CAnimBlendAssociation *assoc; + for(assoc = RpAnimBlendClumpGetFirstAssociation(ped->GetClump(), ASSOC_DRIVING); + assoc; + assoc = RpAnimBlendGetNextAssociation(assoc)) + assoc->flags |= ASSOC_DELETEFADEDOUT; + } + + ped->SetPedState(PED_IDLE); + CAnimManager::BlendAnimation(ped->GetClump(), ped->m_animGroup, ANIM_STD_IDLE, 100.0f); + ped->m_vehDoor = CAR_DOOR_LF; + CPed::PedSetOutCarCB(nil, ped); + ped->SetMoveState(PEDMOVE_STILL); + if(GetUp().z < 0.0f) + ped->SetHeading(CGeneral::LimitRadianAngle(GetForward().Heading() + PI)); + else + ped->SetHeading(GetForward().Heading()); + + switch(weapon){ + case WEAPONTYPE_UNARMED: + case WEAPONTYPE_UNIDENTIFIED: + ped->m_vecMoveSpeed = m_vecMoveSpeed; + ped->m_pCollidingEntity = this; + anim = ANIM_STD_NUM; + break; + + case WEAPONTYPE_BASEBALLBAT: + default: + switch(direction){ + case 0: + anim = ANIM_STD_BIKE_FALLBACK; + ped->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.1f); + if(m_vecMoveSpeed.MagnitudeSqr() < SQR(0.3f)) + ped->ApplyMoveForce(5.0f*GetUp() - 6.0f*GetForward()); + ped->m_pCollidingEntity = this; + break; + case 1: + case 2: + if(m_vecMoveSpeed.MagnitudeSqr() > SQR(0.3f)){ + anim = ANIM_STD_HIGHIMPACT_LEFT; + ped->m_vecMoveSpeed = 0.3f*m_vecMoveSpeed; + ped->ApplyMoveForce(5.0f*GetUp() + 6.0f*GetRight()); + }else{ + anim = ANIM_STD_SPINFORWARD_LEFT; + ped->m_vecMoveSpeed = m_vecMoveSpeed; + ped->ApplyMoveForce(4.0f*GetUp() + 8.0f*GetRight()); + } + // BUG or is it intentionally missing? + //ped->m_pCollidingEntity = this; + break; + case 3: + if(m_vecMoveSpeed.MagnitudeSqr() > SQR(0.3f)){ + anim = ANIM_STD_HIGHIMPACT_RIGHT; + ped->m_vecMoveSpeed = 0.3f*m_vecMoveSpeed; + ped->ApplyMoveForce(5.0f*GetUp() - 6.0f*GetRight()); + }else{ + anim = ANIM_STD_SPINFORWARD_RIGHT; + ped->m_vecMoveSpeed = m_vecMoveSpeed; + ped->ApplyMoveForce(4.0f*GetUp() - 8.0f*GetRight()); + } + // BUG or is it intentionally missing? + //ped->m_pCollidingEntity = this; + break; + } + break; + + case WEAPONTYPE_DROWNING:{ + RwRGBA color; + anim = ANIM_STD_FALL; + ped->m_vecMoveSpeed = m_vecMoveSpeed*0.2f; + ped->m_vecMoveSpeed.z = 0.0f; + ped->m_pCollidingEntity = this; + color.red = (0.5f * CTimeCycle::GetDirectionalRed() + CTimeCycle::GetAmbientRed_Obj())*0.45f*255; + color.green = (0.5f * CTimeCycle::GetDirectionalGreen() + CTimeCycle::GetAmbientGreen_Obj())*0.45f*255; + color.blue = (0.5f * CTimeCycle::GetDirectionalBlue() + CTimeCycle::GetAmbientBlue_Obj())*0.45f*255; + color.alpha = CGeneral::GetRandomNumberInRange(48, 96); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_SPLASH, 0.0f); + CVector splashPos = ped->GetPosition() + 2.2f*ped->m_vecMoveSpeed; + float waterZ = 0.0f; + if(CWaterLevel::GetWaterLevel(splashPos, &waterZ, false)) + splashPos.z = waterZ; + CParticleObject::AddObject(POBJECT_PED_WATER_SPLASH, splashPos, CVector(0.0f, 0.0f, 0.1f), + 0.0f, 200, color, true); + break; + } + + case WEAPONTYPE_FALL: { + ped->m_vecMoveSpeed = ped->m_pMyVehicle->m_vecMoveSpeed; + float forceXY = -0.6*m_fDamageImpulse * ped->m_fMass / m_fMass; + ped->ApplyMoveForce(m_vecDamageNormal.x*forceXY, m_vecDamageNormal.y*forceXY, + CGeneral::GetRandomNumberInRange(3.0f, 7.0f)); + ped->m_pCollidingEntity = this; + switch(direction){ + case 0: anim = ANIM_STD_HIGHIMPACT_BACK; break; + case 1: anim = ANIM_STD_SPINFORWARD_RIGHT; break; + case 2: anim = ANIM_STD_BIKE_FALLBACK; break; + case 3: anim = ANIM_STD_SPINFORWARD_LEFT; break; + } + if(m_nWheelsOnGround == 0) + ped->bKnockedOffBike = true; + break; + } + + case WEAPONTYPE_RAMMEDBYCAR: { + ped->m_vecMoveSpeed = ped->m_pMyVehicle->m_vecMoveSpeed; + static float minForceZ = 8.0f; + static float maxForceZ = 15.0f; + float forceXY = -0.6*m_fDamageImpulse * ped->m_fMass / m_fMass; + ped->ApplyMoveForce(m_vecDamageNormal.x*forceXY, m_vecDamageNormal.y*forceXY, + CGeneral::GetRandomNumberInRange(minForceZ, maxForceZ)); + ped->m_pCollidingEntity = this; + switch(direction){ + case 0: anim = ANIM_STD_HIGHIMPACT_BACK; break; + case 1: anim = ANIM_STD_SPINFORWARD_RIGHT; break; + case 2: anim = ANIM_STD_HIGHIMPACT_FRONT; break; + case 3: anim = ANIM_STD_SPINFORWARD_LEFT; break; + } + ped->bKnockedOffBike = true; + if(ped->IsPlayer()) + ped->Say(SOUND_PED_DAMAGE); + break; + } + } + + if(weapon == WEAPONTYPE_DROWNING){ + ped->bIsStanding = false; + ped->bWasStanding = false; + ped->bIsInTheAir = true; + ped->bIsInWater = true; + ped->bTouchingWater = true; + CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_FALL, 4.0f); + }else if(weapon != WEAPONTYPE_UNARMED){ + if(ped->m_fHealth > 0.0f) + ped->SetFall(1000, anim, 0); + else + ped->SetDie(anim); + ped->bIsStanding = false; + } + + CEntity *ent = CWorld::TestSphereAgainstWorld(ped->GetPosition()+CVector(0.0f, 0.0, 0.5f), 0.4f, nil, true, false, false, false, false, false); + if(ent == nil) + ent = CWorld::TestSphereAgainstWorld(ped->GetPosition()+CVector(0.0f, 0.0, 0.8f), 0.4f, nil, true, false, false, false, false, false); + if(ent == nil) + ent = CWorld::TestSphereAgainstWorld(ped->GetPosition()+CTimer::GetTimeStep()*ped->m_vecMoveSpeed+CVector(0.0f, 0.0, 0.5f), 0.4f, nil, true, false, false, false, false, false); + if(ent == nil) + ent = CWorld::TestSphereAgainstWorld(ped->GetPosition()+CTimer::GetTimeStep()*ped->m_vecMoveSpeed+CVector(0.0f, 0.0, 0.8f), 0.4f, nil, true, false, false, false, false, false); + if(ent){ + CColPoint point; + ent = nil; + if(CWorld::ProcessVerticalLine(ped->GetPosition(), ped->GetPosition().z-2.0f, point, ent, true, false, false, false, false, false, nil)){ + if(ped->m_pMyVehicle == nil){ + ped->m_pMyVehicle = this; + ped->PositionPedOutOfCollision(); + ped->m_pMyVehicle = nil; + }else + ped->PositionPedOutOfCollision(); + }else + ped->GetMatrix().Translate(CVector(0.0f, 0.0f, -2.0f)); + ped->m_pCollidingEntity = ped->m_pMyVehicle; + ped->bKnockedOffBike = true; + ped->bHeadStuckInCollision = true; + }else if(weapon == WEAPONTYPE_RAMMEDBYCAR){ + if(CWorld::TestSphereAgainstWorld(ped->GetPosition()+CVector(0.0f, 0.0, 1.3f), 0.6f, nil, true, false, false, false, false, false) == nil) + ped->GetMatrix().Translate(CVector(0.0f, 0.0f, 0.5f)); + } + ped->m_pMyVehicle = nil; +} + +void +CBike::PlayHornIfNecessary(void) +{ + if(AutoPilot.m_bSlowedDownBecauseOfPeds || + AutoPilot.m_bSlowedDownBecauseOfCars) + PlayCarHorn(); +} + +void +CBike::ResetSuspension(void) +{ + int i; + for(i = 0; i < 2; i++){ + m_aWheelRotation[i] = 0.0f; + m_aWheelState[i] = WHEEL_STATE_NORMAL; + } + for(i = 0; i < 4; i++){ + m_aSuspensionSpringRatio[i] = 1.0f; + m_aWheelTimer[i] = 0.0f; + } +} + +void +CBike::SetupSuspensionLines(void) +{ + int i; + CVector posn; + float suspOffset = 0.0f; + RwFrame *node = nil; + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + CColModel *colModel = mi->GetColModel(); + RwMatrix *mat = RwMatrixCreate(); + + bool initialized = colModel->lines[0].p0.z != FAKESUSPENSION; + + for(i = 0; i < 4; i++){ + if(initialized){ + posn = colModel->lines[i].p0; + if(i < 2) + posn.z = m_aWheelBasePosition[0]; + else + posn.z = m_aWheelBasePosition[1]; + }else{ + switch(i){ + case BIKESUSP_F1: + node = m_aBikeNodes[BIKE_WHEEL_FRONT]; + suspOffset = 0.25f*mi->m_wheelScale; + break; + case BIKESUSP_F2: + node = m_aBikeNodes[BIKE_WHEEL_FRONT]; + suspOffset = -0.25f*mi->m_wheelScale; + break; + case BIKESUSP_R1: + node = m_aBikeNodes[BIKE_WHEEL_REAR]; + suspOffset = 0.25f*mi->m_wheelScale; + break; + case BIKESUSP_R2: + node = m_aBikeNodes[BIKE_WHEEL_REAR]; + suspOffset = -0.25f*mi->m_wheelScale; + break; + } + + GetRelativeMatrix(mat, node, node); + posn = *RwMatrixGetPos(mat); + if(i == BIKESUSP_F1) + m_aWheelBasePosition[BIKEWHEEL_FRONT] = posn.z; + else if(i == BIKESUSP_R1){ + m_aWheelBasePosition[BIKEWHEEL_REAR] = posn.z; + + GetRelativeMatrix(mat, m_aBikeNodes[BIKE_FORKS_REAR], m_aBikeNodes[BIKE_FORKS_REAR]); + float dz = posn.z - RwMatrixGetPos(mat)->z; + float dy = posn.y - RwMatrixGetPos(mat)->y; + m_fRearForkLength = Sqrt(SQR(dy) + SQR(dz)); + assert(m_fRearForkLength != 0.0f); // we want to divide by this + } + posn.y += suspOffset; + } + + // uppermost wheel position + posn.z += pHandling->fSuspensionUpperLimit; + colModel->lines[i].p0 = posn; + + // lowermost wheel position + posn.z += pHandling->fSuspensionLowerLimit - pHandling->fSuspensionUpperLimit; + // lowest point on tyre + posn.z -= mi->m_wheelScale*0.5f; + colModel->lines[i].p1 = posn; + + // this is length of the spring at rest + m_aSuspensionSpringLength[i] = pHandling->fSuspensionUpperLimit - pHandling->fSuspensionLowerLimit; + m_aSuspensionLineLength[i] = colModel->lines[i].p0.z - colModel->lines[i].p1.z; + } + + if(!initialized){ + GetRelativeMatrix(mat, m_aBikeNodes[BIKE_FORKS_FRONT], m_aBikeNodes[BIKE_FORKS_FRONT]); + m_fFrontForkY = RwMatrixGetPos(mat)->y; + m_fFrontForkZ = RwMatrixGetPos(mat)->z; + } + + // Compress spring somewhat to get normal height on road + m_fHeightAboveRoad = m_aSuspensionSpringLength[0]*(1.0f - 1.0f/(4.0f*pHandling->fSuspensionForceLevel)) + - colModel->lines[0].p0.z + mi->m_wheelScale*0.5f; + for(i = 0; i < 2; i++) + m_aWheelPosition[i] = mi->m_wheelScale*0.5f - m_fHeightAboveRoad; + + // adjust col model to include suspension lines + if(colModel->boundingBox.min.z > colModel->lines[0].p1.z) + colModel->boundingBox.min.z = colModel->lines[0].p1.z; + float radius = Max(colModel->boundingBox.min.Magnitude(), colModel->boundingBox.max.Magnitude()); + if(colModel->boundingSphere.radius < radius) + colModel->boundingSphere.radius = radius; + +#ifdef FIX_BUGS + RwMatrixDestroy(mat); +#endif +} + +void +CBike::CalculateLeanMatrix(void) +{ + if(bLeanMatrixClean) + return; + + CMatrix mat; + mat.SetRotateX(-0.05f*Abs(m_fLeanLRAngle)); + mat.RotateY(m_fLeanLRAngle); + m_leanMatrix = GetMatrix(); + m_leanMatrix = m_leanMatrix * mat; + // place wheel back on ground + m_leanMatrix.GetPosition() += GetUp()*(1.0f-Cos(m_fLeanLRAngle))*GetColModel()->boundingBox.min.z; + bLeanMatrixClean = true; +} + +void +CBike::GetCorrectedWorldDoorPosition(CVector &pos, CVector p1, CVector p2) +{ + CVector &fwd = GetForward(); + CVector rightWorld = CrossProduct(fwd, CVector(0.0f, 0.0f, 1.0f)); + CVector upWorld = CrossProduct(rightWorld, fwd); + CColModel *colModel = GetColModel(); + float onSide = DotProduct(GetUp(), rightWorld); + float diff = Max(colModel->boundingBox.max.z-colModel->boundingBox.max.x, 0.0f); + pos = CVector(0.0f, 0.0f, 0.0f); + float y = p2.y - p1.y; + float x = onSide*diff + p2.x + p1.x; + float z = p2.z - p1.z; + pos = x*rightWorld + y*fwd + z*upWorld + GetPosition(); +} + +void +CBike::Fix(void) +{ + bIsDamaged = false; + bIsOnFire = false; + m_wheelStatus[0] = WHEEL_STATUS_OK; + m_wheelStatus[1] = WHEEL_STATUS_OK; +} + +void +CBike::SetupModelNodes(void) +{ + int i; + for(i = 0; i < BIKE_NUM_NODES; i++) + m_aBikeNodes[i] = nil; + CClumpModelInfo::FillFrameArray(GetClump(), m_aBikeNodes); +} + +void +CBike::ReduceHornCounter(void) +{ + if(m_nCarHornTimer != 0) + m_nCarHornTimer--; +} + +#ifdef COMPATIBLE_SAVES +void +CBike::Save(uint8*& buf) +{ + CVehicle::Save(buf); + ZeroSaveBuf(buf, 1260 - 672); +} + +void +CBike::Load(uint8*& buf) +{ + CVehicle::Load(buf); + SkipSaveBuf(buf, 1260 - 672); +} +#endif diff --git a/src/miami/vehicles/Bike.h b/src/miami/vehicles/Bike.h new file mode 100644 index 00000000..219d8872 --- /dev/null +++ b/src/miami/vehicles/Bike.h @@ -0,0 +1,172 @@ +#pragma once + +#include "Vehicle.h" +#include "Skidmarks.h" +#include "AnimManager.h" + +enum eBikeNodes { + BIKE_NODE_NONE, + BIKE_CHASSIS, + BIKE_FORKS_FRONT, + BIKE_FORKS_REAR, + BIKE_WHEEL_FRONT, + BIKE_WHEEL_REAR, + BIKE_MUDGUARD, + BIKE_HANDLEBARS, + BIKE_NUM_NODES +}; + +enum { + BIKEWHEEL_FRONT, + BIKEWHEEL_REAR, +}; + +enum { + BIKESUSP_F1, + BIKESUSP_F2, + BIKESUSP_R1, + BIKESUSP_R2, +}; + +class CBike : public CVehicle +{ +public: + RwFrame *m_aBikeNodes[BIKE_NUM_NODES]; + bool bLeanMatrixClean; + CMatrix m_leanMatrix; + CVector m_vecAvgSurfaceNormal; + CVector m_vecAvgSurfaceRight; + tBikeHandlingData *pBikeHandling; + AssocGroupId m_bikeAnimType; + uint8 m_wheelStatus[2]; + CColPoint m_aWheelColPoints[4]; + float m_aSuspensionSpringRatio[4]; + float m_aSuspensionSpringRatioPrev[4]; + float m_aWheelTimer[4]; + float m_bike_unused1; + eSkidmarkType m_aWheelSkidmarkType[2]; + bool m_aWheelSkidmarkBloody[2]; + bool m_aWheelSkidmarkUnk[2]; + float m_aWheelRotation[2]; + float m_aWheelSpeed[2]; + float m_aWheelPosition[2]; + float m_aWheelBasePosition[2]; + float m_aSuspensionSpringLength[4]; + float m_aSuspensionLineLength[4]; + float m_fHeightAboveRoad; + float m_fTraction; + float m_fRearForkLength; + float m_fFrontForkY; + float m_fFrontForkZ; + float m_fFrontForkSlope; + float m_fWheelAngle; + float m_fLeanLRAngle; + float m_fLeanLRAngle2; + float m_fLeanInput; + float m_fPedLeanAmountLR; + float m_fPedLeanAmountUD; + uint8 m_bike_unused2; + uint8 unused[3]; // looks like padding..but for what? + uint8 m_bike_flag01 : 1; + uint8 m_bike_flag02 : 1; + uint8 bWaterTight : 1; + uint8 bIsBeingPickedUp : 1; + uint8 bIsStanding : 1; + uint8 bExtraSpeed : 1; // leaning forward + uint8 bIsOnFire : 1; + uint8 bWheelieCam : 1; + int16 m_doingBurnout; + float m_fTireTemperature; + float m_fBrakeDestabilization; + float m_fVelocityChangeForAudio; + float m_fFireBlowUpTimer; + CPhysical *m_aGroundPhysical[4]; + CVector m_aGroundOffset[4]; + CEntity *m_pSetOnFireEntity; + uint8 m_nWheelsOnGround; + uint8 m_nDriveWheelsOnGround; + uint8 m_nDriveWheelsOnGroundPrev; + float m_fGasPedalAudio; + tWheelState m_aWheelState[2]; + + CBike(int32 id, uint8 CreatedBy); + + // from CEntity + void SetModelIndex(uint32 id); + void ProcessControl(void); + void Teleport(CVector v); + void PreRender(void); + void Render(void); + + // from CPhysical + int32 ProcessEntityCollision(CEntity *ent, CColPoint *colpoints); + + // from CVehicle + void ProcessControlInputs(uint8); + void GetComponentWorldPosition(int32 component, CVector &pos); + bool IsComponentPresent(int32 component); + void SetComponentRotation(int32 component, CVector rotation); + bool IsDoorReady(eDoors door); + bool IsDoorFullyOpen(eDoors door); + bool IsDoorClosed(eDoors door); + bool IsDoorMissing(eDoors door); + void RemoveRefsToVehicle(CEntity *ent); + void BlowUpCar(CEntity *ent); + bool SetUpWheelColModel(CColModel *colModel); + void BurstTyre(uint8 tyre, bool applyForces); + bool IsRoomForPedToLeaveCar(uint32 component, CVector *doorOffset); + float GetHeightAboveRoad(void); + void PlayCarHorn(void); + + void KnockOffRider(eWeaponType weapon, uint8 direction, CPed *ped, bool bGetBackOn); + void VehicleDamage(void); + void ProcessBuoyancy(void); + void DoDriveByShootings(void); + void AddDamagedVehicleParticles(void); + int32 AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed); + void PlayHornIfNecessary(void); + void ResetSuspension(void); + void SetupSuspensionLines(void); + void CalculateLeanMatrix(void); + void GetCorrectedWorldDoorPosition(CVector &pos, CVector p1, CVector p2); + + void Fix(void); + void SetupModelNodes(void); + void ReduceHornCounter(void); + +#ifdef COMPATIBLE_SAVES + virtual void Save(uint8*& buf); + virtual void Load(uint8*& buf); +#endif + static const uint32 nSaveStructSize; +}; + +// These functions and function names are made up + +inline int8 GetBikeDoorFlag(int32 carnode) { + switch (carnode) { + case CAR_DOOR_RR: + case CAR_DOOR_LR: + return CAR_DOOR_FLAG_RR | CAR_DOOR_FLAG_LR; + case CAR_DOOR_RF: + case CAR_DOOR_LF: + return CAR_DOOR_FLAG_RF | CAR_DOOR_FLAG_LF; + default: + return CAR_DOOR_FLAG_UNKNOWN; + } +} + +// for m_nGettingOutFlags +inline int8 GetBikeDoorFlagInclJumpInFromFront(int32 carnode) { + switch (carnode) { + case CAR_DOOR_RR: + case CAR_DOOR_LR: + return CAR_DOOR_FLAG_RR | CAR_DOOR_FLAG_LR; + case CAR_DOOR_RF: + case CAR_DOOR_LF: + case CAR_WINDSCREEN: + return CAR_DOOR_FLAG_RF | CAR_DOOR_FLAG_LF; + default: + return CAR_DOOR_FLAG_UNKNOWN; + } +} \ No newline at end of file diff --git a/src/miami/vehicles/Boat.cpp b/src/miami/vehicles/Boat.cpp new file mode 100644 index 00000000..5da8e1cc --- /dev/null +++ b/src/miami/vehicles/Boat.cpp @@ -0,0 +1,1489 @@ +#include "common.h" + +#include "main.h" +#include "General.h" +#include "Timecycle.h" +#include "Weather.h" +#include "HandlingMgr.h" +#include "CarAI.h" +#include "CarCtrl.h" +#include "RwHelper.h" +#include "ModelIndices.h" +#include "VisibilityPlugins.h" +#include "DMAudio.h" +#include "Camera.h" +#include "Darkel.h" +#include "Explosion.h" +#include "Particle.h" +#include "ParticleObject.h" +#include "WaterLevel.h" +#include "Floater.h" +#include "World.h" +#include "Stats.h" +#include "Pools.h" +#include "Pad.h" +#include "Boat.h" +#include "AnimBlendAssociation.h" +#include "RpAnimBlend.h" +#include "Record.h" +#include "Shadows.h" +#include "Wanted.h" +#include "SaveBuf.h" + +#define INVALID_ORIENTATION (-9999.99f) + +float CBoat::MAX_WAKE_LENGTH = 50.0f; +float CBoat::MIN_WAKE_INTERVAL = 2.0f; +float CBoat::WAKE_LIFETIME = 150.0f; + +float fShapeLength = 0.4f; +float fShapeTime = 0.05f; +float fRangeMult = 0.6f; +float fTimeMult = 1.2f/CBoat::WAKE_LIFETIME; + +CBoat *CBoat::apFrameWakeGeneratingBoats[4]; + +const uint32 CBoat::nSaveStructSize = +#ifdef COMPATIBLE_SAVES + 1216; +#else + sizeof(CBoat); +#endif + +CBoat::CBoat(int mi, uint8 owner) : CVehicle(owner) +{ + CVehicleModelInfo *minfo = (CVehicleModelInfo*)CModelInfo::GetModelInfo(mi); + m_vehType = VEHICLE_TYPE_BOAT; + m_fAccelerate = 0.0f; + m_fBrake = 0.0f; + m_fSteeringLeftRight = 0.0f; + m_nPadID = 0; + m_fMovingRotation = 0.0f; + m_fMovingSpeed = 0.0f; + m_skimmerThingTimer = 0.0f; + m_nPoliceShoutTimer = CTimer::GetTimeInMilliseconds(); + SetModelIndex(mi); + + pHandling = mod_HandlingManager.GetHandlingData((tVehicleType)minfo->m_handlingId); + pFlyingHandling = mod_HandlingManager.GetFlyingPointer((tVehicleType)minfo->m_handlingId); + pBoatHandling = mod_HandlingManager.GetBoatPointer((tVehicleType)minfo->m_handlingId); + minfo->ChooseVehicleColour(m_currentColour1, m_currentColour2); + + m_fMass = pHandling->fMass; + m_fTurnMass = pHandling->fTurnMass / 2.0f; + m_vecCentreOfMass = pHandling->CentreOfMass; + m_fAirResistance = pHandling->Dimension.x * pHandling->Dimension.z / m_fMass; + m_fElasticity = 0.1f; + m_fBuoyancy = pHandling->fBuoyancy; + m_fSteerAngle = 0.0f; + m_fGasPedal = 0.0f; + m_fBrakePedal = 0.0f; + + m_boat_unused3 = false; + + m_fVolumeUnderWater = 7.0f; + m_fPrevVolumeUnderWater = 7.0f; + m_vecBuoyancePoint = CVector(0.0f, 0.0f, 0.0f); + + m_nDeltaVolumeUnderWater = 0; + bBoatInWater = true; + bPropellerInWater = true; + + bIsInWater = true; + + m_phys_unused1 = 0.0f; + m_boat_unused2 = 0; + m_bIsAnchored = true; + m_fOrientation = INVALID_ORIENTATION; + bTouchingWater = true; + m_fDamage = 0.0f; + m_pSetOnFireEntity = nil; + m_nNumWakePoints = 0; + + for (int16 i = 0; i < ARRAY_SIZE(m_afWakePointLifeTime); i++) + m_afWakePointLifeTime[i] = 0.0f; + + m_nAmmoInClip = 20; + + if(GetModelIndex() == MI_MARQUIS) + m_boom.Init(-PI/10.0f, PI/10.0f, 0, 2); + else + m_boom.Init(-PI/5.0f, PI/5.0f, 0, 2); +} + +void +CBoat::SetModelIndex(uint32 id) +{ + CVehicle::SetModelIndex(id); + SetupModelNodes(); +} + +void +CBoat::GetComponentWorldPosition(int32 component, CVector &pos) +{ + pos = *RwMatrixGetPos(RwFrameGetLTM(m_aBoatNodes[component])); +} + +void +CBoat::ProcessControl(void) +{ + bool onLand = m_fDamageImpulse > 0.0f && m_vecDamageNormal.z > 0.1f; + + PruneWakeTrail(); + + if(bRenderScorched) + m_fBuoyancy *= 0.99f; + +#ifdef FIX_BUGS + if(FindPlayerPed() && FindPlayerPed()->m_pWanted->GetWantedLevel() > 0 && GetModelIndex() == MI_PREDATOR && pDriver && IsPolicePedModel(pDriver->GetModelIndex())) { +#else + if(FindPlayerPed()->m_pWanted->GetWantedLevel() > 0 && GetModelIndex() == MI_PREDATOR){ +#endif + CVehicle *playerVeh = FindPlayerVehicle(); + if(playerVeh && playerVeh->GetVehicleAppearance() == VEHICLE_APPEARANCE_BOAT && + (AutoPilot.m_nCarMission == MISSION_RAMPLAYER_FARAWAY || + AutoPilot.m_nCarMission == MISSION_RAMPLAYER_CLOSE || + AutoPilot.m_nCarMission == MISSION_BLOCKPLAYER_FARAWAY || + AutoPilot.m_nCarMission == MISSION_BLOCKPLAYER_CLOSE || + AutoPilot.m_nCarMission == MISSION_ATTACKPLAYER) && + CTimer::GetTimeInMilliseconds() > m_nPoliceShoutTimer){ + DMAudio.PlayOneShot(m_audioEntityId, SOUND_PED_VCPA_PLAYER_FOUND, 0.0f); + m_nPoliceShoutTimer = CTimer::GetTimeInMilliseconds() + 4500 + (CGeneral::GetRandomNumber()&0xFFF); + } + } + + int r, g, b; + RwRGBA dropColor = { 0, 0, 0, 0 }; + RwRGBA splashColor, jetColor; + r = 127.5f*(CTimeCycle::GetAmbientRed_Obj() + 0.5f*CTimeCycle::GetDirectionalRed()); + g = 127.5f*(CTimeCycle::GetAmbientGreen_Obj() + 0.5f*CTimeCycle::GetDirectionalGreen()); + b = 127.5f*(CTimeCycle::GetAmbientBlue_Obj() + 0.5f*CTimeCycle::GetDirectionalBlue()); + r = Clamp(r, 0, 255); + g = Clamp(g, 0, 255); + b = Clamp(b, 0, 255); + splashColor.red = r; + splashColor.green = g; + splashColor.blue = b; + splashColor.alpha = CGeneral::GetRandomNumberInRange(160, 196); + + r = 229.5f*(CTimeCycle::GetAmbientRed() + 0.85f*CTimeCycle::GetDirectionalRed()); + g = 229.5f*(CTimeCycle::GetAmbientGreen() + 0.85f*CTimeCycle::GetDirectionalGreen()); + b = 229.5f*(CTimeCycle::GetAmbientBlue() + 0.85f*CTimeCycle::GetDirectionalBlue()); + r = Clamp(r, 0, 255); + g = Clamp(g, 0, 255); + b = Clamp(b, 0, 255); + jetColor.red = r; + jetColor.green = g; + jetColor.blue = b; + jetColor.alpha = CGeneral::GetRandomNumberInRange(196, 228); + + CGeneral::GetRandomNumber(); // unused + + UpdateClumpAlpha(); + ProcessCarAlarm(); + + switch(GetStatus()){ + case STATUS_PLAYER: + m_bIsAnchored = false; + m_fOrientation = INVALID_ORIENTATION; + ProcessControlInputs(0); + if(GetModelIndex() == MI_PREDATOR) + DoFixedMachineGuns(); + + if (!CRecordDataForChase::IsRecording()) + DoDriveByShootings(); + break; + case STATUS_SIMPLE: + m_bIsAnchored = false; + m_fOrientation = INVALID_ORIENTATION; + CCarAI::UpdateCarAI(this); + CPhysical::ProcessControl(); + bBoatInWater = true; + bPropellerInWater = true; + bIsInWater = true; + return; + case STATUS_PHYSICS: + m_bIsAnchored = false; + m_fOrientation = INVALID_ORIENTATION; + CCarAI::UpdateCarAI(this); + CCarCtrl::SteerAICarWithPhysics(this); + break; + case STATUS_ABANDONED: + case STATUS_WRECKED: + bBoatInWater = true; + bPropellerInWater = true; + bIsInWater = true; + m_fSteerAngle = 0.0; + bIsHandbrakeOn = false; + m_fBrakePedal = 0.5f; + m_fGasPedal = 0.0f; + if((GetPosition() - FindPlayerCentreOfWorld_NoSniperShift()).Magnitude() > 150.0f){ + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + return; + } + break; + default: break; + } + + float collisionDamage = pHandling->fCollisionDamageMultiplier * m_fDamageImpulse; +#ifdef FIX_BUGS + if(GetStatus() == STATUS_PLAYER && CStats::GetPercentageProgress() >= 100.0f) + collisionDamage *= 0.5f; + if (collisionDamage > 25.0f && GetStatus() != STATUS_WRECKED && !bCollisionProof) { +#else + if(collisionDamage > 25.0f && GetStatus() != STATUS_WRECKED){ +#endif + float prevHealth = m_fHealth; + if(prevHealth >= 250.0f){ +#ifndef FIX_BUGS + // if collisionDamage < 50 we actually increase health here... + if(GetStatus() == STATUS_PLAYER && CStats::GetPercentageProgress() >= 100.0f) + collisionDamage *= 0.5f; +#endif + if(this == FindPlayerVehicle()){ + if(bTakeLessDamage) + m_fHealth -= (collisionDamage-25.0f)/6.0f; + else + m_fHealth -= (collisionDamage-25.0f)/2.0f; + }else{ + if(collisionDamage > 60.0f && pDriver) + pDriver->Say(SOUND_PED_ANNOYED_DRIVER); + if(bTakeLessDamage) + m_fHealth -= (collisionDamage-25.0f)/12.0f; + else + m_fHealth -= (collisionDamage-25.0f)/4.0f; + } + + if(m_fHealth <= 0.0f && prevHealth > 0.0f){ + m_fHealth = 1.0f; + m_pSetOnFireEntity = m_pDamageEntity; + } + } + } + + // Damage particles + if(m_fHealth <= 460.0f && GetStatus() != STATUS_WRECKED && + Abs(GetPosition().x - TheCamera.GetPosition().x) < 200.0f && + Abs(GetPosition().y - TheCamera.GetPosition().y) < 200.0f){ + float speedSq = m_vecMoveSpeed.MagnitudeSqr(); + CVector smokeDir = 0.8f*m_vecMoveSpeed; + CVector smokePos; + switch(GetModelIndex()){ + case MI_SPEEDER: + smokePos = CVector(0.4f, -2.4f, 0.8f); + smokeDir += 0.05f*GetRight(); + smokeDir.z += 0.2f*m_vecMoveSpeed.z; + break; + case MI_REEFER: + smokePos = CVector(2.0f, -1.0f, 0.5f); + smokeDir += 0.07f*GetRight(); + break; + case MI_PREDATOR: + default: + smokePos = CVector(-1.5f, -0.5f, 1.2f); + smokeDir += -0.08f*GetRight(); + break; + } + + smokePos = GetMatrix() * smokePos; + + // On fire + if(m_fHealth < 250.0f){ + CParticle::AddParticle(PARTICLE_CARFLAME, smokePos, + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(2.25f/200.0f, 0.09f)), + nil, 0.9f); + CVector smokePos2 = smokePos; + smokePos2.x += CGeneral::GetRandomNumberInRange(-2.25f/4.0f, 2.25f/4.0f); + smokePos2.y += CGeneral::GetRandomNumberInRange(-2.25f/4.0f, 2.25f/4.0f); + smokePos2.z += CGeneral::GetRandomNumberInRange(2.25f/4.0f, 2.25f); + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, smokePos2, CVector(0.0f, 0.0f, 0.0f)); + + m_fDamage += CTimer::GetTimeStepInMilliseconds(); + if(m_fDamage > 5000.0f) + BlowUpCar(m_pSetOnFireEntity); + } + + if(speedSq < 0.25f && (CTimer::GetFrameCounter() + m_randomSeed) & 1) + CParticle::AddParticle(PARTICLE_ENGINE_STEAM, smokePos, smokeDir); + if(speedSq < 0.25f && m_fHealth <= 390.0f) + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, smokePos, 1.25f*smokeDir); + } + + bool bSeparateTurnForce = bHasHitWall; + CPhysical::ProcessControl(); + + CVector buoyanceImpulse(0.0f, 0.0f, 0.0f); + CVector buoyancePoint(0.0f, 0.0f, 0.0f); + if(mod_Buoyancy.ProcessBuoyancyBoat(this, pHandling->fBuoyancy, &buoyancePoint, &buoyanceImpulse, bSeparateTurnForce)){ + // Process boat in water + if(0.1f * m_fMass * GRAVITY*CTimer::GetTimeStep() < buoyanceImpulse.z){ + bBoatInWater = true; + bIsInWater = true; + if (GetUp().z < -0.6f && Abs(GetMoveSpeed().x) < 0.05 && Abs(GetMoveSpeed().y) < 0.05) { + bIsDrowning = true; + if (pDriver){ + pDriver->bTouchingWater = true; + pDriver->InflictDamage(nil, WEAPONTYPE_DROWNING, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); + } + } + else + bIsDrowning = false; + }else{ + bBoatInWater = false; + bIsInWater = false; + bIsDrowning = false; + } + + m_fVolumeUnderWater = mod_Buoyancy.m_volumeUnderWater; + m_vecBuoyancePoint = buoyancePoint; + if(GetModelIndex() == MI_SKIMMER && GetUp().z < -0.5f && Abs(m_vecMoveSpeed.x) < 0.2f && Abs(m_vecMoveSpeed.y) < 0.2f) + ApplyMoveForce(0.03f*buoyanceImpulse); + else + ApplyMoveForce(buoyanceImpulse); + if(bSeparateTurnForce) + ApplyTurnForce(0.4f*buoyanceImpulse, buoyancePoint); + + // TODO: what is this? + if(GetModelIndex() == MI_SKIMMER) + if(m_skimmerThingTimer != 0.0f || + GetForward().z < -0.5f && GetUp().z > -0.5f && m_vecMoveSpeed.z < -0.15f && + buoyanceImpulse.z > 0.01f*m_fMass * GRAVITY*CTimer::GetTimeStep() && + buoyanceImpulse.z < 0.4f*m_fMass * GRAVITY*CTimer::GetTimeStep()){ + float turnImpulse = -0.00017f*GetForward().z*buoyanceImpulse.z * m_fMass*CTimer::GetTimeStep(); + ApplyTurnForce(turnImpulse*GetForward(), GetUp()); + bBoatInWater = false; + //BUG? aren't we forgetting the timestep here? + float moveImpulse = -0.5f*DotProduct(m_vecMoveSpeed, GetForward()) * m_fMass; + ApplyMoveForce(moveImpulse*GetForward()); + if(m_skimmerThingTimer == 0.0f) + m_skimmerThingTimer = CTimer::GetTimeInMilliseconds() + 300.0f; + else if(m_skimmerThingTimer < CTimer::GetTimeInMilliseconds()) + m_skimmerThingTimer = 0.0f; + } + + if(!onLand && bBoatInWater && GetUp().z > 0.0f){ + float impulse = m_vecMoveSpeed.MagnitudeSqr()*pBoatHandling->fAqPlaneForce*buoyanceImpulse.z*CTimer::GetTimeStep()*0.5f; + if(GetModelIndex() == MI_SKIMMER) + impulse *= 1.0f + m_fGasPedal; + else if(m_fGasPedal > 0.05f) + impulse *= m_fGasPedal; + else + impulse = 0.0f; + impulse = Min(impulse, GRAVITY*pBoatHandling->fAqPlaneLimit*m_fMass*CTimer::GetTimeStep()); + ApplyMoveForce(impulse*GetUp()); + ApplyTurnForce(impulse*GetUp(), buoyancePoint - pBoatHandling->fAqPlaneOffset*GetForward()); + } + + // Handle boat moving forward + float fwdSpeed = 1.0f; + if(Abs(m_fGasPedal) > 0.05f || (fwdSpeed = m_vecMoveSpeed.Magnitude2D()) > 0.01f){ + if(bBoatInWater && fwdSpeed > 0.05f) + AddWakePoint(GetPosition()); + + float steerFactor = 1.0f; + if(GetStatus() == STATUS_PLAYER){ + float steerLoss = DotProduct(m_vecMoveSpeed, GetForward())*pHandling->fTractionBias; + if(CPad::GetPad(0)->GetHandBrake()) + steerLoss *= 0.5f; + steerFactor -= steerLoss; + steerFactor = Clamp(steerFactor, 0.0f, 1.0f); + } + + CVector boundMin = GetColModel()->boundingBox.min; + CVector propeller(0.0f, boundMin.y*pBoatHandling->fThrustY, boundMin.z*pBoatHandling->fThrustZ); + propeller = Multiply3x3(GetMatrix(), propeller); + CVector propellerWorld = GetPosition() + propeller; + + float steerSin = Sin(-m_fSteerAngle * steerFactor); + float steerCos = Cos(-m_fSteerAngle * steerFactor); + float waterLevel; + CWaterLevel::GetWaterLevel(propellerWorld, &waterLevel, true); + if(propellerWorld.z-0.5f < waterLevel){ + float propellerDepth = waterLevel - (propellerWorld.z - 0.5f); + if(propellerDepth > 1.0f) + propellerDepth = 1.0f; + else + propellerDepth = SQR(propellerDepth); + bPropellerInWater = true; + + bool bSlowAhead = false; + if(Abs(m_fGasPedal) > 0.01f && GetModelIndex() != MI_SKIMMER){ + if(Abs(m_fGasPedal) < 0.05f) + bSlowAhead = true; + + CVector forceDir = Multiply3x3(GetMatrix(), CVector(-steerSin, steerCos, -Abs(m_fSteerAngle))); + CVector force = propellerDepth * m_fGasPedal * 40.0f * pHandling->Transmission.fEngineAcceleration * pHandling->fMass * forceDir; + if(force.z > 0.2f) + force.z = SQR(1.2f - force.z) + 0.2f; + if(onLand){ + if(m_fGasPedal < 0.0f){ + force.x *= 5.0f; + force.y *= 5.0f; + } + if(force.z < 0.0f) + force.z = 0.0f; + ApplyMoveForce(force * CTimer::GetTimeStep()); + }else{ + ApplyMoveForce(force * CTimer::GetTimeStep()); + ApplyTurnForce(force * CTimer::GetTimeStep(), propeller - pBoatHandling->fThrustAppZ*GetUp()); + float rightForce = -DotProduct(GetRight(), force)*pHandling->fTractionMultiplier; + ApplyTurnForce(rightForce*GetRight() * CTimer::GetTimeStep(), GetUp()); + } + + // Spray some particles + CVector jetDir = -0.04f * force; + if(m_fGasPedal > 0.0f){ + if(GetStatus() == STATUS_PLAYER){ + CVector sternPos = GetColModel()->boundingBox.min; + sternPos.x = 0.0f; + sternPos.z = 0.0f; + sternPos = Multiply3x3(GetMatrix(), sternPos); + + CVector wakePos = GetPosition() + sternPos; + // no actual particles for player... + }else if(IsVisible() && ((CTimer::GetFrameCounter() + m_randomSeed) & 1) && + CVisibilityPlugins::GetDistanceSquaredFromCamera(&propellerWorld) < SQR(70.0f * TheCamera.GenerationDistMultiplier)){ + jetDir.z = 0.015f; + jetDir.x *= 3.5f; + jetDir.y *= 3.5f; + propellerWorld.z += 0.5f; + + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, propellerWorld, jetDir, nil, 1.25f, jetColor, + CGeneral::GetRandomNumberInRange(0, 5), + CGeneral::GetRandomNumberInRange(0, 90), 1, 500); + + CParticle::AddParticle(PARTICLE_CAR_SPLASH, propellerWorld, 0.75f * jetDir, nil, 0.5f, splashColor, + CGeneral::GetRandomNumberInRange(0, 30), + CGeneral::GetRandomNumberInRange(0, 45), 3, 500); + } + } + }else + bSlowAhead = true; + + if(!onLand && bSlowAhead){ + float force = pHandling->fTractionLoss*DotProduct(m_vecMoveSpeed, GetForward()); + force = Min(force, 0.01f*m_fTurnMass); + if(m_fGasPedal > 0.01f){ + if(GetStatus() == STATUS_PLAYER) + force *= (0.55f - Abs(m_fGasPedal)) * 1.3f; + else + force *= (0.55f - Abs(m_fGasPedal)) * 2.5f; + } + if(m_fGasPedal < 0.0f && force > 0.0f || m_fGasPedal > 0.0f && force < 0.0f) + force *= -1.0f; + CVector propellerForce = propellerDepth * Multiply3x3(GetMatrix(), force*CVector(-steerSin, 0.0f, 0.0f)); + ApplyMoveForce(propellerForce * CTimer::GetTimeStep()); + ApplyTurnForce(propellerForce * CTimer::GetTimeStep(), propeller); + float rightForce = -steerSin * force * 0.75f/steerFactor * Max(CTimer::GetTimeStep(), 0.01f); + ApplyTurnForce(GetRight() * rightForce, GetUp()); + } + }else + bPropellerInWater = false; + + if(pHandling->fSuspensionBias != 0.0f){ + CVector right = CrossProduct(GetForward(), CVector(0.0f, 0.0f, 1.0f)); + float rightSpeed = DotProduct(m_vecMoveSpeed, right); + float impulse = 0.1f*pHandling->fSuspensionBias * m_fMass * m_fVolumeUnderWater * rightSpeed * CTimer::GetTimeStep(); + ApplyMoveForce(right - impulse * 0.3f * CVector(-right.y, right.x, 0.0f)); + } + + if(GetStatus() == STATUS_PLAYER && CPad::GetPad(0)->GetHandBrake()){ + float fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward()); + if(fwdSpeed > 0.0f){ + float impulse = -0.1f*pHandling->fSuspensionLowerLimit * m_fMass * m_fVolumeUnderWater * fwdSpeed * CTimer::GetTimeStep(); + ApplyMoveForce(impulse * GetForward()); + } + } + } + + // Slow down or push down boat as it approaches the world limits + m_vecMoveSpeed.x = Min(m_vecMoveSpeed.x, -(GetPosition().x - (WORLD_MAX_X-100.0f))*0.01f); // east + m_vecMoveSpeed.x = Max(m_vecMoveSpeed.x, -(GetPosition().x - (WORLD_MIN_X+100.0f))*0.01f); // west + m_vecMoveSpeed.y = Min(m_vecMoveSpeed.y, -(GetPosition().y - (WORLD_MAX_Y-100.0f))*0.01f); // north + m_vecMoveSpeed.y = Max(m_vecMoveSpeed.y, -(GetPosition().y - (WORLD_MIN_Y+100.0f))*0.01f); // south + + if(!onLand && bBoatInWater && !bSeparateTurnForce) + ApplyWaterResistance(); + + if((GetModelIndex() != MI_SKIMMER || m_skimmerThingTimer == 0.0f) && !bSeparateTurnForce){ + // No idea what exactly is going on here besides drag in YZ + float fx = Pow(pBoatHandling->vecTurnRes.x, CTimer::GetTimeStep()); + float fy = Pow(pBoatHandling->vecTurnRes.y, CTimer::GetTimeStep()); + float fz = Pow(pBoatHandling->vecTurnRes.z, CTimer::GetTimeStep()); + m_vecTurnSpeed = Multiply3x3(m_vecTurnSpeed, GetMatrix()); // invert - to local space + // TODO: figure this out + float magic = 1.0f/(1000.0f * SQR(m_vecTurnSpeed.x) + 1.0f) * fx; + m_vecTurnSpeed.y *= fy; + m_vecTurnSpeed.z *= fz; + float forceUp = (magic - 1.0f) * m_vecTurnSpeed.x * m_fTurnMass; + m_vecTurnSpeed = Multiply3x3(GetMatrix(), m_vecTurnSpeed); // back to world + CVector com = Multiply3x3(GetMatrix(), m_vecCentreOfMass); + ApplyTurnForce(forceUp*GetUp(), com + GetForward()); + } + + m_nDeltaVolumeUnderWater = (m_fVolumeUnderWater-m_fPrevVolumeUnderWater)*10000; + + // Falling into water + if(!onLand && bBoatInWater && GetUp().z > 0.0f){ + float splashVol = m_nDeltaVolumeUnderWater*pBoatHandling->fWaveAudioMult; + if(splashVol > 200.0f) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_SPLASH, splashVol); + + if(m_nDeltaVolumeUnderWater > 200){ + float speedUp = m_vecMoveSpeed.MagnitudeSqr() * m_nDeltaVolumeUnderWater * 0.001f; + if(speedUp + m_vecMoveSpeed.z > pHandling->fBrakeDeceleration) + speedUp = pHandling->fBrakeDeceleration - m_vecMoveSpeed.z; + if(speedUp < 0.0f) speedUp = 0.0f; + float speedFwd = DotProduct(m_vecMoveSpeed, GetForward()); + speedFwd *= -m_nDeltaVolumeUnderWater * 0.01f * pHandling->fBrakeBias; + CVector speed = speedFwd*GetForward() + CVector(0.0f, 0.0f, speedUp); + CVector splashImpulse = speed * m_fMass; + ApplyMoveForce(splashImpulse); + ApplyTurnForce(splashImpulse, buoyancePoint); + } + } + + // Splashes + float speed = m_vecMoveSpeed.Magnitude(); + if(speed > 0.05f && GetUp().x > 0.0f && !TheCamera.GetLookingForwardFirstPerson() && IsVisible() && + (AutoPilot.m_nCarMission != MISSION_CRUISE || (CTimer::GetFrameCounter()&2) == 0)){ + CVector splashPos, splashDir; + float splashSize, front, waterLevel; + + switch(GetModelIndex()){ + case MI_RIO: + splashSize = speed; + front = 0.9f * GetColModel()->boundingBox.max.y; + splashDir = -0.5f * m_vecMoveSpeed; + splashDir.z += 0.25f*speed; + splashDir += 0.35f*speed*GetRight(); + splashPos = GetPosition() + 1.85f*GetRight() + front*GetForward(); + splashPos.z += 0.5f; + break; + case MI_SQUALO: + splashSize = speed; + front = 0.75f * GetColModel()->boundingBox.max.y; + splashDir = -0.125f * m_vecMoveSpeed; + splashDir.z += 0.15f*speed; + splashDir += 0.25f*speed*GetRight(); + splashPos = GetPosition() + m_vecBuoyancePoint + 0.5f*GetRight() + front*GetForward(); + splashPos.z += 0.5f; + break; + case MI_REEFER: + splashSize = speed; + front = 0.75f * GetColModel()->boundingBox.max.y; + splashDir = -0.5f * m_vecMoveSpeed; + splashDir.z += 0.15f*speed; + splashDir += 0.5f*speed*GetRight(); + splashPos = GetPosition() + m_vecBuoyancePoint + 1.3f*GetRight() + front*GetForward(); + break; + case MI_COASTG: + splashSize = 0.25f*speed; + front = 0.8f * GetColModel()->boundingBox.max.y; + splashDir = 0.165f * m_vecMoveSpeed; + splashDir.z += 0.25f*speed; + splashDir += 0.15f*speed*GetRight(); + splashPos = GetPosition() + 0.65f*GetRight() + front*GetForward(); + splashPos.z += 0.5f; + break; + case MI_DINGHY: + splashSize = 0.25f*speed; + front = 0.9f * GetColModel()->boundingBox.max.y; + splashDir = 0.35f * m_vecMoveSpeed; + splashDir.z += 0.25f*speed; + splashDir += 0.25f*speed*GetRight(); + splashPos = GetPosition() + 0.6f*GetRight() + front*GetForward(); + splashPos.z += 0.5f; + break; + default: + splashSize = speed; + front = 0.9f * GetColModel()->boundingBox.max.y; + splashDir = -0.5f * m_vecMoveSpeed; + splashDir.z += 0.25f*speed; + splashDir += 0.35f*speed*GetRight(); + splashPos = GetPosition() + m_vecBuoyancePoint + 0.5f*GetRight() + front*GetForward(); + break; + } + if(splashSize > 0.75f) splashSize = 0.75f; + if(AutoPilot.m_nCarMission == MISSION_CRUISE) + splashDir *= 1.5f; + static float lifeMult = 1000.0f; + static float lifeBase = 300.0f; + splashDir.z += 0.0003f*m_nDeltaVolumeUnderWater; + CWaterLevel::GetWaterLevel(splashPos, &waterLevel, true); + if(splashPos.z-waterLevel < 3.0f && + CVisibilityPlugins::GetDistanceSquaredFromCamera(&splashPos) < SQR(70.0f * TheCamera.GenerationDistMultiplier)){ + splashPos.z = waterLevel + 0.1f; + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashPos, 0.75f*splashDir, nil, splashSize+0.1f, splashColor, + CGeneral::GetRandomNumberInRange(0.0f, 10.0f), CGeneral::GetRandomNumberInRange(0.0f, 90.0f), + 1, lifeBase + splashDir.z*lifeMult); + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, splashPos, splashDir, nil, splashSize, jetColor, + CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), + 0, lifeBase + splashDir.z*lifeMult); + } + + switch(GetModelIndex()){ + case MI_RIO: + splashDir = -0.5f * m_vecMoveSpeed; + splashDir.z += 0.25f*speed; + splashDir -= 0.35f*speed*GetRight(); + splashPos = GetPosition() - 1.85f*GetRight() + front*GetForward(); + splashPos.z += 0.5f; + break; + case MI_SQUALO: + splashDir = -0.125f * m_vecMoveSpeed; + splashDir.z += 0.15f*speed; + splashDir -= 0.25f*speed*GetRight(); + splashPos = GetPosition() + m_vecBuoyancePoint - 0.5f*GetRight() + front*GetForward(); + splashPos.z += 0.5f; + break; + case MI_REEFER: + splashDir = -0.5f * m_vecMoveSpeed; + splashDir.z += 0.15f*speed; + splashDir -= 0.5f*speed*GetRight(); + splashPos = GetPosition() + m_vecBuoyancePoint - 1.3f*GetRight() + front*GetForward(); + break; + case MI_COASTG: + splashDir = 0.165f * m_vecMoveSpeed; + splashDir.z += 0.25f*speed; + splashDir -= 0.15f*speed*GetRight(); + splashPos = GetPosition() - 0.65f*GetRight() + front*GetForward(); + splashPos.z += 0.5f; + break; + case MI_DINGHY: + splashDir = 0.35f * m_vecMoveSpeed; + splashDir.z += 0.25f*speed; + splashDir -= 0.25f*speed*GetRight(); + splashPos = GetPosition() - 0.6f*GetRight() + front*GetForward(); + splashPos.z += 0.5f; + break; + default: + splashDir = -0.5f * m_vecMoveSpeed; + splashDir.z += 0.25f*speed; + splashDir -= 0.35f*speed*GetRight(); + splashPos = GetPosition() + m_vecBuoyancePoint - 0.5f*GetRight() + front*GetForward(); + break; + } + if(AutoPilot.m_nCarMission == MISSION_CRUISE) + splashDir *= 1.5f; + splashDir.z += 0.0003f*m_nDeltaVolumeUnderWater; + CWaterLevel::GetWaterLevel(splashPos, &waterLevel, true); + if(splashPos.z-waterLevel < 3.0f && + CVisibilityPlugins::GetDistanceSquaredFromCamera(&splashPos) < SQR(70.0f * TheCamera.GenerationDistMultiplier)){ + splashPos.z = waterLevel + 0.1f; + CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashPos, 0.75f*splashDir, nil, splashSize+0.1f, splashColor, + CGeneral::GetRandomNumberInRange(0.0f, 10.0f), CGeneral::GetRandomNumberInRange(0.0f, 90.0f), + 1, lifeBase + splashDir.z*lifeMult); + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, splashPos, splashDir, nil, splashSize, jetColor, + CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), + 0, lifeBase + splashDir.z*lifeMult); + } + } + + // Spray waterdrops on screen + if(TheCamera.GetLookingForwardFirstPerson() && FindPlayerVehicle() && FindPlayerVehicle()->IsBoat() && + m_nDeltaVolumeUnderWater > 0 && numWaterDropOnScreen < 20){ + CVector dropPos; + CVector dropDir(CGeneral::GetRandomNumberInRange(-0.25f, 0.25f), CGeneral::GetRandomNumberInRange(1.0f, 0.75f), 0.0f); + + int frm = CGeneral::GetRandomNumber() & 1; + if(TheCamera.m_CameraAverageSpeed < 0.35f){ + dropPos.x = CGeneral::GetRandomNumberInRange(50, (int)SCREEN_WIDTH-50); + dropPos.y = CGeneral::GetRandomNumberInRange(50, (int)SCREEN_HEIGHT-50); + }else{ + dropPos.x = CGeneral::GetRandomNumberInRange(200, (int)SCREEN_WIDTH-200); + dropPos.y = CGeneral::GetRandomNumberInRange(150, (int)SCREEN_HEIGHT-150); + } + dropPos.z = 1.0f; + + if(TheCamera.m_CameraAverageSpeed > 0.35f){ + if((int)SCREEN_WIDTH / 2 < dropPos.x) + dropPos.x += CGeneral::GetRandomNumberInRange(0.35f, TheCamera.m_CameraAverageSpeed)*7.5f; + else + dropPos.x -= CGeneral::GetRandomNumberInRange(0.35f, TheCamera.m_CameraAverageSpeed)*7.5f; + + if((int)SCREEN_HEIGHT / 2 < dropPos.y) + dropPos.y += CGeneral::GetRandomNumberInRange(0.35f, TheCamera.m_CameraAverageSpeed)*7.5f; + else + dropPos.y -= CGeneral::GetRandomNumberInRange(0.35f, TheCamera.m_CameraAverageSpeed)*7.5f; + } + + if(CParticle::AddParticle(PARTICLE_WATERDROP, dropPos, dropDir, nil, + CGeneral::GetRandomNumberInRange(0.1f, 0.15f), dropColor, 0, 0, frm)) + numWaterDropOnScreen++; + } + + if(m_fPrevVolumeUnderWater == 0.0f && m_fVolumeUnderWater > 0.0f && GetModelIndex() == MI_SKIMMER){ + CVector splashDir(0.0f, 0.0f, 0.25f*speed); + CVector splashPos = GetPosition(); + float level; + CWaterLevel::GetWaterLevel(splashPos, &level, true); + splashPos.z = level; + CParticleObject::AddObject(POBJECT_CAR_WATER_SPLASH, splashPos, splashDir, 0.0f, 65, splashColor, true); + } + + m_fPrevVolumeUnderWater = m_fVolumeUnderWater; + }else{ + bBoatInWater = false; + bIsInWater = false; +#ifdef FIX_BUGS + bIsDrowning = false; +#endif + } + + if(m_modelIndex == MI_SKIMMER && CTimer::GetTimeStep() > 0.0f){ + if(GetStatus() == STATUS_PLAYER){ + if(m_fMovingSpeed < 0.22f) + m_fMovingSpeed += 0.001f*CTimer::GetTimeStep(); + FlyingControl(FLIGHT_MODEL_SEAPLANE); + }else{ + if(m_fMovingSpeed > 0.0005f*CTimer::GetTimeStep()) + m_fMovingSpeed -= 0.0005f*CTimer::GetTimeStep(); + else + m_fMovingSpeed = 0.0f; + } + }else if(bCheat8) + FlyingControl(FLIGHT_MODEL_PLANE); + + if(m_bIsAnchored){ + m_vecMoveSpeed.x = 0.0f; + m_vecMoveSpeed.y = 0.0f; + + if(m_fOrientation == INVALID_ORIENTATION){ + m_fOrientation = GetForward().Heading(); + }else{ + // is this some inlined CPlaceable method? + CVector pos = GetPosition(); + GetMatrix().RotateZ(m_fOrientation - GetForward().Heading()); + GetMatrix().SetTranslateOnly(pos); + } + } + + ProcessDelayedExplosion(); +} + +void +CBoat::ProcessControlInputs(uint8 pad) +{ + m_nPadID = pad; + if(m_nPadID > 3) + m_nPadID = 3; + + m_fBrake += (CPad::GetPad(pad)->GetBrake()/255.0f - m_fBrake)*0.1f; + m_fBrake = Clamp(m_fBrake, 0.0f, 1.0f); + + if(m_fBrake < 0.05f){ + m_fBrake = 0.0f; + m_fAccelerate += (CPad::GetPad(pad)->GetAccelerate()/255.0f - m_fAccelerate)*0.1f; + m_fAccelerate = Clamp(m_fAccelerate, 0.0f, 1.0f); + }else + m_fAccelerate = -m_fBrake*0.3f; + + m_fSteeringLeftRight += (-CPad::GetPad(pad)->GetSteeringLeftRight()/128.0f - m_fSteeringLeftRight)*0.2f; + m_fSteeringLeftRight = Clamp(m_fSteeringLeftRight, -1.0f, 1.0f); + + float steeringSq = m_fSteeringLeftRight < 0.0f ? -SQR(m_fSteeringLeftRight) : SQR(m_fSteeringLeftRight); + m_fSteerAngle = pHandling->fSteeringLock * DEGTORAD(steeringSq); + m_fGasPedal = m_fAccelerate; +} + +float fSeaPlaneWaterResistance = 30.0f; + +void +CBoat::ApplyWaterResistance(void) +{ + // TODO: figure out how this works + float resistance = 0.001f * pHandling->fSuspensionForceLevel * SQR(m_fVolumeUnderWater) * m_fMass; + if(GetModelIndex() == MI_SKIMMER) + resistance *= fSeaPlaneWaterResistance; + float fwdSpeed = DotProduct(GetMoveSpeed(), GetForward()); + float magic = (SQR(fwdSpeed) + 0.05f) * resistance + 1.0f; + magic = Abs(magic); + float fx = Pow(pBoatHandling->vecMoveRes.x/magic, 0.5f*CTimer::GetTimeStep()); + float fy = Pow(pBoatHandling->vecMoveRes.y/magic, 0.5f*CTimer::GetTimeStep()); + float fz = Pow(pBoatHandling->vecMoveRes.z/magic, 0.5f*CTimer::GetTimeStep()); + + m_vecMoveSpeed = Multiply3x3(m_vecMoveSpeed, GetMatrix()); // invert - to local space + m_vecMoveSpeed.x *= fx; + m_vecMoveSpeed.y *= fy; + m_vecMoveSpeed.z *= fz; + float force = (fy - 1.0f) * m_vecMoveSpeed.y * m_fMass; + m_vecMoveSpeed = Multiply3x3(GetMatrix(), m_vecMoveSpeed); // back to world + + ApplyTurnForce(force*GetForward(), -GetUp()); + + if(m_vecMoveSpeed.z > 0.0f) + m_vecMoveSpeed.z *= fz; + else + m_vecMoveSpeed.z *= (1.0f - fz)*0.5f + fz; +} + +RwObject* +GetBoatAtomicObjectCB(RwObject *object, void *data) +{ + RpAtomic *atomic = (RpAtomic*)object; + assert(RwObjectGetType(object) == rpATOMIC); + if(RpAtomicGetFlags(atomic) & rpATOMICRENDER) + *(RpAtomic**)data = atomic; + return object; + + +} + +void +CBoat::BlowUpCar(CEntity *culprit) +{ + RpAtomic *atomic; + RwFrame *frame; + RwMatrix *matrix; + CObject *obj; + + if(!bCanBeDamaged) + return; + + // explosion pushes vehicle up + m_vecMoveSpeed.z += 0.13f; + SetStatus(STATUS_WRECKED); + bRenderScorched = true; + + m_fHealth = 0.0; + m_nBombTimer = 0; + TheCamera.CamShake(0.7f, GetPosition().x, GetPosition().y, GetPosition().z); + + KillPedsInVehicle(); + + bEngineOn = false; + bLightsOn = false; + ChangeLawEnforcerState(false); + + CExplosion::AddExplosion(this, culprit, EXPLOSION_BOAT, GetPosition(), 0); + CDarkel::RegisterCarBlownUpByPlayer(this); + if(m_aBoatNodes[BOAT_MOVING] == nil) + return; + + // much like CAutomobile::SpawnFlyingComponent from here on + + atomic = nil; + RwFrameForAllObjects(m_aBoatNodes[BOAT_MOVING], GetBoatAtomicObjectCB, &atomic); + if(atomic == nil) + return; + + obj = new CObject(); + if(obj == nil) + return; + + obj->SetModelIndexNoCreate(MI_CAR_WHEEL); + + // object needs base model + obj->RefModelInfo(GetModelIndex()); + + // create new atomic + matrix = RwFrameGetLTM(m_aBoatNodes[BOAT_MOVING]); + frame = RwFrameCreate(); + atomic = RpAtomicClone(atomic); + *RwFrameGetMatrix(frame) = *matrix; + RpAtomicSetFrame(atomic, frame); + CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); + obj->AttachToRwObject((RwObject*)atomic); + obj->bDontStream = true; + + // init object + obj->m_fMass = 10.0f; + obj->m_fTurnMass = 25.0f; + obj->m_fAirResistance = 0.99f; + obj->m_fElasticity = 0.1f; + obj->m_fBuoyancy = obj->m_fMass*GRAVITY/0.75f; + obj->ObjectCreatedBy = TEMP_OBJECT; + obj->SetIsStatic(false); + obj->bIsPickup = false; + + // life time + CObject::nNoTempObjects++; + obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 20000; + + obj->m_vecMoveSpeed = m_vecMoveSpeed; + if(GetUp().z > 0.0f) + obj->m_vecMoveSpeed.z = 0.3f; + else + obj->m_vecMoveSpeed.z = 0.0f; + + obj->m_vecTurnSpeed = m_vecTurnSpeed*2.0f; + obj->m_vecTurnSpeed.x = 0.5f; + + // push component away from boat + CVector dist = obj->GetPosition() - GetPosition(); + dist.Normalise(); + if(GetUp().z > 0.0f) + dist += GetUp(); + obj->GetMatrix().GetPosition() += dist; + + CWorld::Add(obj); + + atomic = nil; + RwFrameForAllObjects(m_aBoatNodes[BOAT_MOVING], GetBoatAtomicObjectCB, &atomic); + if(atomic) + RpAtomicSetFlags(atomic, 0); +} + +void +CBoat::PreRender(void) +{ + CMatrix matrix; + CVector pos; + RpAtomic *atomic; + + if(GetModelIndex() == MI_SKIMMER){ + m_fMovingRotation += m_fMovingSpeed*CTimer::GetTimeStep(); + if(m_fMovingRotation > TWOPI) m_fMovingRotation -= TWOPI; + int alpha = (1.0f - Min(2.0f*m_fMovingSpeed*8.0f/PI, 1.0f))*255.0f; + if(GetStatus() == STATUS_PLAYER || GetStatus() == STATUS_PLAYER_REMOTE || GetStatus() == STATUS_PLAYER_PLAYBACKFROMBUFFER){ + if(m_aBoatNodes[BOAT_RUDDER]){ + float sine = Sin(m_fSteerAngle); + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_RUDDER])); + pos = matrix.GetPosition(); + matrix.SetRotate(0.0f, 0.0f, -m_fSteerAngle); + matrix.Rotate(0.0f, DEGTORAD(22.0f)*sine, 0.0f); + matrix.Translate(pos); + matrix.UpdateRW(); + } + if(m_aBoatNodes[BOAT_FLAP_LEFT]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_FLAP_LEFT])); + pos = matrix.GetPosition(); + matrix.SetRotateX(-m_fSteerAngle); + matrix.Translate(pos); + matrix.UpdateRW(); + } + if(m_aBoatNodes[BOAT_FLAP_RIGHT]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_FLAP_RIGHT])); + pos = matrix.GetPosition(); + matrix.SetRotateX(m_fSteerAngle); + matrix.Translate(pos); + matrix.UpdateRW(); + } + // FIX: Planes can also be controlled with GetCarGunUpDown +#ifdef FIX_BUGS + static float steeringUpDown = 0.0f; +#ifdef FREE_CAM + if(!CCamera::bFreeCam || (CCamera::bFreeCam && !CPad::IsAffectedByController)) +#endif + steeringUpDown += ((Abs(CPad::GetPad(0)->GetCarGunUpDown()) > 1.0f ? (-CPad::GetPad(0)->GetCarGunUpDown()/128.0f) : (-CPad::GetPad(0)->GetSteeringUpDown()/128.0f)) - steeringUpDown) * Min(1.f, CTimer::GetTimeStep()/5.f); +#ifdef FREE_CAM + else + steeringUpDown = -CPad::GetPad(0)->GetSteeringUpDown()/128.0f; +#endif +#else + float steeringUpDown = -CPad::GetPad(0)->GetSteeringUpDown()/128.0f; +#endif + if(m_aBoatNodes[BOAT_REARFLAP_LEFT]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_REARFLAP_LEFT])); + pos = matrix.GetPosition(); + matrix.SetRotateX(steeringUpDown); + matrix.Translate(pos); + matrix.UpdateRW(); + } + if(m_aBoatNodes[BOAT_REARFLAP_RIGHT]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_REARFLAP_RIGHT])); + pos = matrix.GetPosition(); + matrix.SetRotateX(steeringUpDown); + matrix.Translate(pos); + matrix.UpdateRW(); + } + } + if(m_aBoatNodes[BOAT_MOVING]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_MOVING])); + pos = matrix.GetPosition(); + matrix.SetRotateY(m_fMovingRotation); + matrix.Translate(pos); + matrix.UpdateRW(); + + atomic = nil; + RwFrameForAllObjects(m_aBoatNodes[BOAT_MOVING], GetBoatAtomicObjectCB, &atomic); + if(atomic) + SetComponentAtomicAlpha(atomic, alpha); + } + if(m_aBoatNodes[BOAT_WINDSCREEN]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_WINDSCREEN])); + pos = matrix.GetPosition(); + matrix.SetRotateY(-m_fMovingRotation); + matrix.Translate(pos); + matrix.UpdateRW(); + + atomic = nil; + RwFrameForAllObjects(m_aBoatNodes[BOAT_WINDSCREEN], GetBoatAtomicObjectCB, &atomic); + if(atomic) + SetComponentAtomicAlpha(atomic, Max(150-alpha, 0)); + } + CShadows::StoreShadowForVehicle(this, VEH_SHD_TYPE_SEAPLANE); + }else if(GetModelIndex() == MI_COASTG || GetModelIndex() == MI_DINGHY || GetModelIndex() == MI_RIO || + GetModelIndex() == MI_SQUALO || GetModelIndex() == MI_MARQUIS){ + if(m_aBoatNodes[BOAT_RUDDER]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_RUDDER])); + pos = matrix.GetPosition(); + matrix.SetRotateZ(-m_fSteerAngle); + matrix.Translate(pos); + matrix.UpdateRW(); + } + if(m_aBoatNodes[BOAT_REARFLAP_LEFT]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_REARFLAP_LEFT])); + pos = matrix.GetPosition(); + matrix.SetRotateZ(-m_fSteerAngle); + matrix.Translate(pos); + matrix.UpdateRW(); + } + if(m_aBoatNodes[BOAT_REARFLAP_RIGHT]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_REARFLAP_RIGHT])); + pos = matrix.GetPosition(); + matrix.SetRotateZ(-m_fSteerAngle); + matrix.Translate(pos); + matrix.UpdateRW(); + } + } + + if(GetModelIndex() == MI_RIO || GetModelIndex() == MI_MARQUIS){ + float axes[3] = { 0.0f, 0.0f, 0.0f }; + m_boom.Process(this); + axes[m_boom.m_nAxis] = m_boom.m_fAngle; + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_FLAP_LEFT])); + pos = matrix.GetPosition(); + matrix.SetRotate(axes[0], axes[1], axes[2]); + matrix.Translate(pos); + matrix.UpdateRW(); + } + + if(GetModelIndex() == MI_RIO){ + // That little wind propeller + if(m_aBoatNodes[BOAT_FLAP_RIGHT]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_FLAP_RIGHT])); + pos = matrix.GetPosition(); + + float flapHeading = matrix.GetForward().Heading(); + float boatHeading = GetForward().Heading(); + float rot = -DEGTORAD(45.0f) - (flapHeading + boatHeading); + // eh what? + rot = CGeneral::LimitRadianAngle(rot); + if(rot > HALFPI) rot = PI; + else if(rot < -HALFPI) rot = -PI; + rot = Clamp(rot, -DEGTORAD(63.0f), DEGTORAD(63.0f)); + m_fMovingSpeed += (0.008f * CWeather::Wind + 0.002f) * rot; + m_fMovingSpeed *= Pow(0.9985f, CTimer::GetTimeStep())/(500.0f*SQR(m_fMovingSpeed) + 1.0f); + + matrix.SetRotateZ(flapHeading + m_fMovingSpeed*CTimer::GetTimeStep()); + matrix.Translate(pos); + matrix.UpdateRW(); + } + if(m_aBoatNodes[BOAT_MOVING]){ + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_MOVING])); + pos = matrix.GetPosition(); + matrix.SetRotateY(m_fMovingRotation); + matrix.Translate(pos); + matrix.UpdateRW(); + + CVector wind = CVector(0.707f, 0.707f, 0.0f) * (CWeather::Wind + 0.15f)*0.4f; + m_fMovingRotation += (m_vecMoveSpeed + wind).Magnitude()*CTimer::GetTimeStep(); + } + }else if(GetModelIndex() == MI_PREDATOR || GetModelIndex() == MI_REEFER){ + if (m_aBoatNodes[BOAT_MOVING] != nil) { + matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_MOVING])); + + CVector pos = matrix.GetPosition(); + matrix.SetRotateZ(m_fMovingRotation); + matrix.Translate(pos); + + matrix.UpdateRW(); + if (CVehicle::bWheelsOnlyCheat) { + RpAtomicRender((RpAtomic*)GetFirstObject(m_aBoatNodes[BOAT_MOVING])); + } + } + m_fMovingRotation += 0.02f * CTimer::GetTimeStep(); + } +} + +RwIm3DVertex KeepWaterOutVertices[4]; +RwImVertexIndex KeepWaterOutIndices[6]; + +void +CBoat::Render() +{ + ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->SetVehicleColour(m_currentColour1, m_currentColour2); + m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 3000; + if (!CVehicle::bWheelsOnlyCheat) + CEntity::Render(); +#ifdef NEW_RENDERER + if(!gbNewRenderer) +#endif + RenderWaterOutPolys(); // not separate function in VC +} + +void +CBoat::RenderWaterOutPolys(void) +{ + if(GetModelIndex() == MI_SKIMMER) + return; + KeepWaterOutIndices[0] = 0; + KeepWaterOutIndices[1] = 2; + KeepWaterOutIndices[2] = 1; + KeepWaterOutIndices[3] = 1; + KeepWaterOutIndices[4] = 2; + KeepWaterOutIndices[5] = 3; + RwIm3DVertexSetRGBA(&KeepWaterOutVertices[0], 255, 255, 255, 255); + RwIm3DVertexSetRGBA(&KeepWaterOutVertices[1], 255, 255, 255, 255); + RwIm3DVertexSetRGBA(&KeepWaterOutVertices[2], 255, 255, 255, 255); + RwIm3DVertexSetRGBA(&KeepWaterOutVertices[3], 255, 255, 255, 255); + switch (GetModelIndex()) { + case MI_RIO: + RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.3f, -1.016f, 0.51f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.3f, -1.016f, 0.51f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.3f, -2.832f, 0.51f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.3f, -2.832f, 0.51f); + break; + case MI_SQUALO: + RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.222f, 2.004f, 1.409f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.222f, 2.004f, 1.409f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.24f, -1.367f, 0.846f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.24f, -1.367f, 0.846f); + break; + case MI_SPEEDER: + RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.15f, 3.61f, 1.03f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.15f, 3.61f, 1.03f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.15f, 0.06f, 1.03f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.15f, 0.06f, 1.03f); + break; + case MI_REEFER: + RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.9f, 2.83f, 1.0f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.9f, 2.83f, 1.0f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.66f, -4.48f, 0.83f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.66f, -4.48f, 0.83f); + break; + case MI_PREDATOR: + RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.45f, 1.9f, 0.96f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.45f, 1.9f, 0.96f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.45f, -3.75f, 0.96f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.45f, -3.75f, 0.96f); + break; + case MI_TROPIC: + RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.886f, -2.347f, 0.787f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.886f, -2.347f, 0.787f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.886f, -4.67f, 0.842f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.886f, -4.67f, 0.842f); + break; + case MI_COASTG: + RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -0.663f, 3.565f, 0.382f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 0.663f, 3.565f, 0.382f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.087f, 0.83f, 0.381f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.087f, 0.83f, 0.381f); + break; + case MI_DINGHY: + RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -0.797f, 1.641f, 0.573f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 0.797f, 1.641f, 0.573f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -0.865f, -1.444f, 0.509f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 0.865f, -1.444f, 0.509f); + break; + case MI_MARQUIS: + RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.246f, -1.373f, 0.787f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.246f, -1.373f, 0.787f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.023f, -5.322f, 0.787f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.023f, -5.322f, 0.787f); + break; + default: + return; + } + KeepWaterOutVertices[0].u = 0.0f; + KeepWaterOutVertices[0].v = 0.0f; + KeepWaterOutVertices[1].u = 1.0f; + KeepWaterOutVertices[1].v = 0.0f; + KeepWaterOutVertices[2].u = 0.0f; + KeepWaterOutVertices[2].v = 1.0f; + KeepWaterOutVertices[3].u = 1.0f; + KeepWaterOutVertices[3].v = 1.0f; +#ifdef NEW_RENDERER + if(!gbNewRenderer) +#endif +{ + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpWaterRaster); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDZERO); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); +} + if (!CVehicle::bWheelsOnlyCheat && RwIm3DTransform(KeepWaterOutVertices, 4, GetMatrix().m_attachment, rwIM3D_VERTEXUV)) { + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, KeepWaterOutIndices, 6); + RwIm3DEnd(); + } + bool drawAnotherRect = false; + if(GetModelIndex() == MI_COASTG){ + drawAnotherRect = true; + RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.087f, 0.831f, 0.381f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.087f, 0.831f, 0.381f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.097f, -2.977f, 0.381f); + RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.097f, -2.977f, 0.381f); + } + if(drawAnotherRect){ + KeepWaterOutVertices[0].u = 0.0f; + KeepWaterOutVertices[0].v = 0.0f; + KeepWaterOutVertices[1].u = 1.0f; + KeepWaterOutVertices[1].v = 0.0f; + KeepWaterOutVertices[2].u = 0.0f; + KeepWaterOutVertices[2].v = 1.0f; + KeepWaterOutVertices[3].u = 1.0f; + KeepWaterOutVertices[3].v = 1.0f; + if (!CVehicle::bWheelsOnlyCheat && RwIm3DTransform(KeepWaterOutVertices, 4, GetMatrix().m_attachment, rwIM3D_VERTEXUV)) { + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, KeepWaterOutIndices, 6); + RwIm3DEnd(); + } + } +#ifdef NEW_RENDERER + if(!gbNewRenderer) +#endif +{ + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); +} +} + +void +CBoat::Teleport(CVector v) +{ + CWorld::Remove(this); + SetPosition(v); + SetOrientation(0.0f, 0.0f, 0.0f); + SetMoveSpeed(0.0f, 0.0f, 0.0f); + SetTurnSpeed(0.0f, 0.0f, 0.0f); + CWorld::Add(this); +} + +// unused +bool +CBoat::IsSectorAffectedByWake(CVector2D sector, float fSize, CBoat **apBoats) +{ + uint8 numVerts = 0; + + if ( apFrameWakeGeneratingBoats[0] == NULL ) + return false; + + for ( int32 i = 0; i < 4; i++ ) + { + CBoat *pBoat = apFrameWakeGeneratingBoats[i]; + if ( !pBoat ) + break; + + for ( int j = 0; j < pBoat->m_nNumWakePoints; j++ ) + { + float fDist = (WAKE_LIFETIME - pBoat->m_afWakePointLifeTime[j]) * fShapeTime + float(j) * fShapeLength + fSize; + + if ( Abs(pBoat->m_avec2dWakePoints[j].x - sector.x) < fDist + && Abs(pBoat->m_avec2dWakePoints[i].y - sector.y) < fDist ) + { + apBoats[numVerts] = pBoat; + numVerts = 1; // += ? + break; + } + } + } + + return numVerts != 0; +} + +// unused +float +CBoat::IsVertexAffectedByWake(CVector vecVertex, CBoat *pBoat) +{ + for ( int i = 0; i < pBoat->m_nNumWakePoints; i++ ) + { + float fMaxDist = (WAKE_LIFETIME - pBoat->m_afWakePointLifeTime[i]) * fShapeTime + float(i) * fShapeLength; + + CVector2D vecDist = pBoat->m_avec2dWakePoints[i] - CVector2D(vecVertex); + + float fDist = vecDist.MagnitudeSqr(); + + if ( fDist < SQR(fMaxDist) ) + return 1.0f - Min(fRangeMult * Sqrt(fDist / SQR(fMaxDist)) + (WAKE_LIFETIME - pBoat->m_afWakePointLifeTime[i]) * fTimeMult, 1.0f); + } + + return 0.0f; +} + +void +CBoat::SetupModelNodes() +{ + int i; + for(i = 0; i < ARRAY_SIZE(m_aBoatNodes); i++) + m_aBoatNodes[i] = nil; + CClumpModelInfo::FillFrameArray(GetClump(), m_aBoatNodes); +} + +void +CBoat::FillBoatList() +{ + int16 frameId = 0; + + apFrameWakeGeneratingBoats[0] = nil; + apFrameWakeGeneratingBoats[1] = nil; + apFrameWakeGeneratingBoats[2] = nil; + apFrameWakeGeneratingBoats[3] = nil; + CVector2D camPos = TheCamera.GetPosition(); + CVector2D camFwd = TheCamera.GetForward(); + float camDist = camFwd.Magnitude(); + if(camDist > 0.0f) + camFwd /= camDist; + for (int i = CPools::GetVehiclePool()->GetSize() - 1; i >= 0; i--) { + CBoat *boat = (CBoat *)(CPools::GetVehiclePool()->GetSlot(i)); + if (boat && boat->m_vehType == VEHICLE_TYPE_BOAT) { + if (boat->m_nNumWakePoints != 0) { + CVector2D camToBoat = CVector2D(boat->GetPosition()) - camPos; + float distToCam = DotProduct2D(camFwd, camToBoat); + if(distToCam > 100.0f || distToCam < -15.0f) + continue; + float distSq = camToBoat.MagnitudeSqr(); + if(distSq > SQR(70.0f)) + continue; + if (frameId >= ARRAY_SIZE(apFrameWakeGeneratingBoats)) { + float nearest = 999999.88f; + int16 frameId2 = -1; + for (int16 j = 0; j < ARRAY_SIZE(apFrameWakeGeneratingBoats); j++) { + float tmpDistSq = (CVector2D(apFrameWakeGeneratingBoats[j]->GetPosition()) - camPos).MagnitudeSqr(); + if (tmpDistSq < nearest) { + nearest = tmpDistSq; + frameId2 = j; + } + } + + if (frameId2 != -1 && + (distSq < nearest || boat->GetStatus() == STATUS_PLAYER)) + apFrameWakeGeneratingBoats[frameId2] = boat; + } else { + apFrameWakeGeneratingBoats[frameId++] = boat; + } + } + } + } +} + +void +CBoat::PruneWakeTrail(void) +{ + int i; + + for(i = 0; i < ARRAY_SIZE(m_afWakePointLifeTime); i++){ + if(m_afWakePointLifeTime[i] <= 0.0f) + break; + if(m_afWakePointLifeTime[i] <= CTimer::GetTimeStep()){ + m_afWakePointLifeTime[i] = 0.0f; + break; + } + m_afWakePointLifeTime[i] -= CTimer::GetTimeStep(); + } + m_nNumWakePoints = i; +} + +void +CBoat::AddWakePoint(CVector point) +{ + int i; + if(m_afWakePointLifeTime[0] > 0.0f){ + if((CVector2D(GetPosition()) - m_avec2dWakePoints[0]).MagnitudeSqr() < SQR(2.0f)) { + if(GetStatus() == STATUS_PLAYER){ + if(m_nNumWakePoints >= 31) + m_nNumWakePoints = 31; + }else if(VehicleCreatedBy == MISSION_VEHICLE){ + if(m_nNumWakePoints >= 20) + m_nNumWakePoints = 20; + }else{ + if(m_nNumWakePoints >= 15) + m_nNumWakePoints = 15; + } + for(i = m_nNumWakePoints; i != 0; i--){ + m_avec2dWakePoints[i] = m_avec2dWakePoints[i-1]; + m_afWakePointLifeTime[i] = m_afWakePointLifeTime[i-1]; + } + m_avec2dWakePoints[0] = point; + m_afWakePointLifeTime[0] = 150.0f; + if(m_nNumWakePoints < ARRAY_SIZE(m_afWakePointLifeTime)) + m_nNumWakePoints++; + } + }else{ + m_avec2dWakePoints[0] = point; + m_afWakePointLifeTime[0] = 150.0f; + m_nNumWakePoints = 1; + } +} + +void +CBoat::DoDriveByShootings(void) +{ + CAnimBlendAssociation *anim = nil; + CPlayerInfo* playerInfo = ((CPlayerPed*)pDriver)->GetPlayerInfoForThisPlayerPed(); + if (playerInfo && !playerInfo->m_bDriveByAllowed) + return; + + CWeapon *weapon = pDriver->GetWeapon(); + if(CWeaponInfo::GetWeaponInfo(weapon->m_eWeaponType)->m_nWeaponSlot != 5) + return; + + weapon->Update(pDriver->m_audioEntityId, nil); + + bool lookingLeft = false; + bool lookingRight = false; + if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || + TheCamera.m_bObbeCinematicCarCamOn){ + if(CPad::GetPad(0)->GetLookLeft()) + lookingLeft = true; + if(CPad::GetPad(0)->GetLookRight()) + lookingRight = true; + }else{ + if(TheCamera.Cams[TheCamera.ActiveCam].LookingLeft) + lookingLeft = true; + if(TheCamera.Cams[TheCamera.ActiveCam].LookingRight) + lookingRight = true; + } + + if(lookingLeft || lookingRight){ + if(lookingLeft){ + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_STD_CAR_DRIVEBY_RIGHT); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_STD_CAR_DRIVEBY_LEFT); + if(anim == nil || anim->blendDelta < 0.0f) + anim = CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_DRIVEBY_LEFT); + }else if(pDriver->m_pMyVehicle->pPassengers[0] == nil || TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON){ + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_STD_CAR_DRIVEBY_LEFT); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_STD_CAR_DRIVEBY_RIGHT); + if(anim == nil || anim->blendDelta < 0.0f) + anim = CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_DRIVEBY_RIGHT); + } + + if (!anim || !anim->IsRunning()) { + if (CPad::GetPad(0)->GetCarGunFired() && CTimer::GetTimeInMilliseconds() > weapon->m_nTimer) { + weapon->FireFromCar(this, lookingLeft, true); + weapon->m_nTimer = CTimer::GetTimeInMilliseconds() + 70; + } + } + }else{ + weapon->Reload(); + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_STD_CAR_DRIVEBY_LEFT); + if(anim) + anim->blendDelta = -1000.0f; + anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_STD_CAR_DRIVEBY_RIGHT); + if(anim) + anim->blendDelta = -1000.0f; + } +} + +#ifdef COMPATIBLE_SAVES +void +CBoat::Save(uint8*& buf) +{ + CVehicle::Save(buf); + ZeroSaveBuf(buf, 1216 - 672); +} + +void +CBoat::Load(uint8*& buf) +{ + CVehicle::Load(buf); + SkipSaveBuf(buf, 1216 - 672); +} +#endif diff --git a/src/miami/vehicles/Boat.h b/src/miami/vehicles/Boat.h new file mode 100644 index 00000000..5d866c48 --- /dev/null +++ b/src/miami/vehicles/Boat.h @@ -0,0 +1,85 @@ +#pragma once + +#include "Vehicle.h" +#include "Door.h" + +enum eBoatNodes +{ + BOAT_MOVING = 1, + BOAT_WINDSCREEN, + BOAT_RUDDER, + BOAT_FLAP_LEFT, + BOAT_FLAP_RIGHT, + BOAT_REARFLAP_LEFT, + BOAT_REARFLAP_RIGHT, + NUM_BOAT_NODES +}; + +class CBoat : public CVehicle +{ +public: + float m_fMovingRotation; + float m_fMovingSpeed; + int32 m_boat_unused1; + RwFrame *m_aBoatNodes[NUM_BOAT_NODES]; + CDoor m_boom; + tBoatHandlingData *pBoatHandling; + uint8 bBoatInWater : 1; + uint8 bPropellerInWater : 1; + bool m_bIsAnchored; + float m_fOrientation; + uint32 m_nPoliceShoutTimer; + int32 m_boat_unused2; + float m_fDamage; + CEntity *m_pSetOnFireEntity; + float m_skimmerThingTimer; + bool m_boat_unused3; + float m_fAccelerate; + float m_fBrake; + float m_fSteeringLeftRight; + uint8 m_nPadID; + int32 m_boat_unused4; + float m_fVolumeUnderWater; + CVector m_vecBuoyancePoint; + float m_fPrevVolumeUnderWater; + int16 m_nDeltaVolumeUnderWater; + uint16 m_nNumWakePoints; + CVector2D m_avec2dWakePoints[32]; + float m_afWakePointLifeTime[32]; + + static float MAX_WAKE_LENGTH; + static float MIN_WAKE_INTERVAL; + static float WAKE_LIFETIME; + + CBoat(int, uint8); + + virtual void SetModelIndex(uint32 id); + virtual void ProcessControl(); + virtual void Teleport(CVector v); + virtual void PreRender(void); + virtual void Render(void); + virtual void ProcessControlInputs(uint8); + virtual void GetComponentWorldPosition(int32 component, CVector &pos); + virtual bool IsComponentPresent(int32 component) { return true; } + virtual void BlowUpCar(CEntity *ent); + + void RenderWaterOutPolys(void); + void ApplyWaterResistance(void); + void SetupModelNodes(); + void PruneWakeTrail(void); + void AddWakePoint(CVector point); + void DoDriveByShootings(void); + + static CBoat *apFrameWakeGeneratingBoats[4]; + + static bool IsSectorAffectedByWake(CVector2D sector, float fSize, CBoat **apBoats); + static float IsVertexAffectedByWake(CVector vecVertex, CBoat *pBoat); + static void FillBoatList(void); + +#ifdef COMPATIBLE_SAVES + virtual void Save(uint8*& buf); + virtual void Load(uint8*& buf); +#endif + static const uint32 nSaveStructSize; + +}; \ No newline at end of file diff --git a/src/miami/vehicles/CarGen.cpp b/src/miami/vehicles/CarGen.cpp new file mode 100644 index 00000000..bce8cdab --- /dev/null +++ b/src/miami/vehicles/CarGen.cpp @@ -0,0 +1,309 @@ +#include "common.h" + +#include "CarGen.h" + +#include "Automobile.h" +#include "Bike.h" +#include "Boat.h" +#include "Camera.h" +#include "CarCtrl.h" +#include "CutsceneMgr.h" +#include "General.h" +#include "Pools.h" +#include "Streaming.h" +#include "Timer.h" +#include "Vehicle.h" +#include "VisibilityPlugins.h" +#include "World.h" +#include "Zones.h" +#include "Occlusion.h" +#include "SaveBuf.h" + +uint8 CTheCarGenerators::ProcessCounter; +uint32 CTheCarGenerators::NumOfCarGenerators; +CCarGenerator CTheCarGenerators::CarGeneratorArray[NUM_CARGENS]; +uint8 CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter; +uint32 CTheCarGenerators::CurrentActiveCount; + +void CCarGenerator::SwitchOff() +{ +#ifdef FIX_BUGS + if (m_nUsesRemaining != 0) +#endif + { + m_nUsesRemaining = 0; + --CTheCarGenerators::CurrentActiveCount; + } +} + +void CCarGenerator::SwitchOn() +{ + m_nUsesRemaining = UINT16_MAX; + m_nTimer = CalcNextGen(); + ++CTheCarGenerators::CurrentActiveCount; +} + +uint32 CCarGenerator::CalcNextGen() +{ + return CTimer::GetTimeInMilliseconds() + 4; +} + +void CCarGenerator::DoInternalProcessing() +{ + int mi; + if (CCarCtrl::NumParkedCars >= 10) + return; + if (m_nModelIndex >= 0) { + if (CheckForBlockage(m_nModelIndex)) { + m_nTimer += 4; + return; + } + CStreaming::RequestModel(m_nModelIndex, STREAMFLAGS_DEPENDENCY); + mi = m_nModelIndex; + } + else { + mi = -m_nModelIndex; + if (m_nModelIndex == -1 || !CStreaming::HasModelLoaded(mi)) { + CZoneInfo pZone; + CVector pos = FindPlayerCoors(); + CTheZones::GetZoneInfoForTimeOfDay(&pos, &pZone); + mi = CCarCtrl::ChooseCarModel(CCarCtrl::ChooseCarRating(&pZone)); + if (mi < 0) + return; + m_nModelIndex = -mi; + m_nColor1 = -1; + m_nColor2 = -1; + } + if (CheckForBlockage(mi)) { + m_nTimer += 4; + return; + } + } + if (!CStreaming::HasModelLoaded(mi)) + return; + CVehicle* pVehicle; + + CVector pos; + if (CModelInfo::IsBoatModel(mi)){ + CBoat* pBoat = new CBoat(mi, PARKED_VEHICLE); + pos = m_vecPos; + pVehicle = pBoat; + if (pos.z <= -100.0f) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + pBoat->bExtendedRange = true; + }else{ + bool groundFound; + pos = m_vecPos; + if (pos.z > -100.0f){ + pos.z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &groundFound); + }else{ + groundFound = false; + CColPoint cp; + CEntity* pEntity; + groundFound = CWorld::ProcessVerticalLine(CVector(pos.x, pos.y, 1000.0f), -1000.0f, + cp, pEntity, true, false, false, false, false, false, nil); + if (groundFound) + pos.z = cp.point.z; + } + if (!groundFound) { + debug("CCarGenerator::DoInternalProcessing - can't find ground z for new car x = %f y = %f \n", m_vecPos.x, m_vecPos.y); + return; + } + if (((CVehicleModelInfo*)CModelInfo::GetModelInfo(mi))->m_vehicleType == VEHICLE_TYPE_BIKE) { + CBike* pBike = new CBike(mi, PARKED_VEHICLE); + pBike->bIsStanding = true; + pVehicle = pBike; + } + else { + CAutomobile* pCar = new CAutomobile(mi, PARKED_VEHICLE); + pVehicle = pCar; + } + // pVehicle->GetDistanceFromCentreOfMassToBaseOfModel(); + pVehicle->bLightsOn = false; + } + pVehicle->bIsStatic = false; + pVehicle->bEngineOn = false; + pos.z += pVehicle->GetDistanceFromCentreOfMassToBaseOfModel(); + pVehicle->SetPosition(pos); + pVehicle->SetOrientation(0.0f, 0.0f, DEGTORAD(m_fAngle)); + pVehicle->SetStatus(STATUS_ABANDONED); + pVehicle->m_nDoorLock = CARLOCK_UNLOCKED; + CWorld::Add(pVehicle); + if (CGeneral::GetRandomNumberInRange(0, 100) < m_nAlarm) + pVehicle->m_nAlarmState = -1; + if (CGeneral::GetRandomNumberInRange(0, 100) < m_nDoorlock) + pVehicle->m_nDoorLock = CARLOCK_LOCKED; + if (m_nColor1 != -1 && m_nColor2 != -1) { + pVehicle->m_currentColour1 = m_nColor1; + pVehicle->m_currentColour2 = m_nColor2; + } + else if (m_nModelIndex < -1) { + m_nColor1 = pVehicle->m_currentColour1; + m_nColor2 = pVehicle->m_currentColour2; + } + CVisibilityPlugins::SetClumpAlpha(pVehicle->GetClump(), 0); + m_nVehicleHandle = CPools::GetVehiclePool()->GetIndex(pVehicle); + /* I don't think this is a correct comparasion */ +#ifdef FIX_BUGS + if (m_nUsesRemaining < UINT16_MAX) + --m_nUsesRemaining; +#else + if (m_nUsesRemaining < ~0) + --m_nUsesRemaining; +#endif + m_nTimer = CalcNextGen(); + if (m_nUsesRemaining == 0) + --CTheCarGenerators::CurrentActiveCount; +} + +void CCarGenerator::Process() +{ + if (m_nVehicleHandle == -1 && + (CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter || CTimer::GetTimeInMilliseconds() >= m_nTimer) && + m_nUsesRemaining != 0 && CheckIfWithinRangeOfAnyPlayers()) + DoInternalProcessing(); + if (m_nVehicleHandle == -1) + return; + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(m_nVehicleHandle); + if (!pVehicle){ + m_nVehicleHandle = -1; + return; + } + if (pVehicle->GetStatus() != STATUS_PLAYER) + return; + m_nTimer += 60000; + m_nVehicleHandle = -1; + m_bIsBlocking = true; + pVehicle->bExtendedRange = false; + if (m_nModelIndex < 0) + m_nModelIndex = -1; +} + +void CCarGenerator::Setup(float x, float y, float z, float angle, int32 mi, int16 color1, int16 color2, uint8 force, uint8 alarm, uint8 lock, uint16 min_delay, uint16 max_delay) +{ + CMatrix m1, m2, m3; /* Unused but present on stack, so I'll leave them. */ + m_vecPos = CVector(x, y, z); + m_fAngle = angle; + m_nModelIndex = mi; + m_nColor1 = color1; + m_nColor2 = color2; + m_bForceSpawn = force; + m_nAlarm = alarm; + m_nDoorlock = lock; + m_nMinDelay = min_delay; + m_nMaxDelay = max_delay; + m_nVehicleHandle = -1; + m_nTimer = CTimer::GetTimeInMilliseconds() + 1; + m_nUsesRemaining = 0; + m_bIsBlocking = false; +} + +bool CCarGenerator::CheckForBlockage(int32 mi) +{ + int16 entities; + CEntity* pEntities[8]; + CColModel* pColModel = CModelInfo::GetColModel(mi); + CWorld::FindObjectsKindaColliding(CVector(m_vecPos), pColModel->boundingSphere.radius, 1, &entities, 8, pEntities, false, true, true, false, false); + for (int i = 0; i < entities; i++) { + if (m_vecPos.z + pColModel->boundingBox.min.z < pEntities[i]->GetPosition().z + pEntities[i]->GetColModel()->boundingBox.max.z + 1.0f && + m_vecPos.z + pColModel->boundingBox.max.z > pEntities[i]->GetPosition().z + pEntities[i]->GetColModel()->boundingBox.min.z - 1.0f) { + m_bIsBlocking = true; + return true; + } + } + return false; +} + +bool CCarGenerator::CheckIfWithinRangeOfAnyPlayers() +{ + CVector2D direction = FindPlayerCentreOfWorld(CWorld::PlayerInFocus) - m_vecPos; + float distance = direction.Magnitude(); + float farclip = 110.0f * TheCamera.GenerationDistMultiplier; + float nearclip = farclip - 20.0f; + bool canBeRemoved = (m_nModelIndex > 0 && CModelInfo::IsBoatModel(m_nModelIndex) && 165.0f * TheCamera.GenerationDistMultiplier > distance && + TheCamera.IsSphereVisible(m_vecPos, 0.0f) && !COcclusion::IsPositionOccluded(m_vecPos, 0.0f)); + if (distance >= farclip && !canBeRemoved){ + if (m_bIsBlocking) + m_bIsBlocking = false; + return false; + } + if (CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter) + return true; + if (m_bIsBlocking) + return false; + if (distance < nearclip && !m_bForceSpawn) + return false; + return DotProduct2D(direction, FindPlayerSpeed()) <= 0; +} + +void CTheCarGenerators::Process() +{ + if (FindPlayerTrain() || CCutsceneMgr::IsCutsceneProcessing()) + return; + if (++CTheCarGenerators::ProcessCounter == 4) + CTheCarGenerators::ProcessCounter = 0; + for (uint32 i = ProcessCounter; i < NumOfCarGenerators; i += 4) + CTheCarGenerators::CarGeneratorArray[i].Process(); + if (GenerateEvenIfPlayerIsCloseCounter) + GenerateEvenIfPlayerIsCloseCounter--; +} + +int32 CTheCarGenerators::CreateCarGenerator(float x, float y, float z, float angle, int32 mi, int16 color1, int16 color2, uint8 force, uint8 alarm, uint8 lock, uint16 min_delay, uint16 max_delay) +{ + if (NumOfCarGenerators < NUM_CARGENS) + CarGeneratorArray[NumOfCarGenerators++].Setup(x, y, z, angle, mi, color1, color2, force, alarm, lock, min_delay, max_delay); + return NumOfCarGenerators - 1; +} + +void CTheCarGenerators::Init() +{ + GenerateEvenIfPlayerIsCloseCounter = 0; + NumOfCarGenerators = 0; + ProcessCounter = 0; + CurrentActiveCount = 0; +} + +void CTheCarGenerators::SaveAllCarGenerators(uint8 *buffer, uint32 *size) +{ + const uint32 nGeneralDataSize = sizeof(NumOfCarGenerators) + sizeof(CurrentActiveCount) + sizeof(ProcessCounter) + sizeof(GenerateEvenIfPlayerIsCloseCounter) + sizeof(int16); + *size = sizeof(int) + nGeneralDataSize + sizeof(uint32) + sizeof(CarGeneratorArray) + SAVE_HEADER_SIZE; +INITSAVEBUF + WriteSaveHeader(buffer, 'C','G','N','\0', *size - SAVE_HEADER_SIZE); + + WriteSaveBuf(buffer, nGeneralDataSize); + WriteSaveBuf(buffer, NumOfCarGenerators); + WriteSaveBuf(buffer, CurrentActiveCount); + WriteSaveBuf(buffer, ProcessCounter); + WriteSaveBuf(buffer, GenerateEvenIfPlayerIsCloseCounter); + WriteSaveBuf(buffer, (int16)0); // alignment + WriteSaveBuf(buffer, (uint32)sizeof(CarGeneratorArray)); + for (int i = 0; i < NUM_CARGENS; i++) + WriteSaveBuf(buffer, CarGeneratorArray[i]); +VALIDATESAVEBUF(*size) +} + +void CTheCarGenerators::LoadAllCarGenerators(uint8* buffer, uint32 size) +{ + NumOfCarGenerators = 0; + GenerateEvenIfPlayerIsCloseCounter = 0; + CurrentActiveCount = 0; + ProcessCounter = 0; + + const int32 nGeneralDataSize = sizeof(NumOfCarGenerators) + sizeof(CurrentActiveCount) + sizeof(ProcessCounter) + sizeof(GenerateEvenIfPlayerIsCloseCounter) + sizeof(int16); + Init(); +INITSAVEBUF + CheckSaveHeader(buffer, 'C','G','N','\0', size - SAVE_HEADER_SIZE); + uint32 tmp; + ReadSaveBuf(&tmp, buffer); + assert(tmp == nGeneralDataSize); + ReadSaveBuf(&NumOfCarGenerators, buffer); + ReadSaveBuf(&CurrentActiveCount, buffer); + ReadSaveBuf(&ProcessCounter, buffer); + ReadSaveBuf(&GenerateEvenIfPlayerIsCloseCounter, buffer); + SkipSaveBuf(buffer, 2); + ReadSaveBuf(&tmp, buffer); + assert(tmp == sizeof(CarGeneratorArray)); + for (int i = 0; i < NUM_CARGENS; i++) + ReadSaveBuf(&CarGeneratorArray[i], buffer); +VALIDATESAVEBUF(size) +} diff --git a/src/miami/vehicles/CarGen.h b/src/miami/vehicles/CarGen.h new file mode 100644 index 00000000..fccbee96 --- /dev/null +++ b/src/miami/vehicles/CarGen.h @@ -0,0 +1,51 @@ +#pragma once +#include "common.h" +#include "config.h" + +enum { + CARGEN_MAXACTUALLIMIT = 100 +}; + +class CCarGenerator +{ + int32 m_nModelIndex; + CVector m_vecPos; + float m_fAngle; + int16 m_nColor1; + int16 m_nColor2; + uint8 m_bForceSpawn; + uint8 m_nAlarm; + uint8 m_nDoorlock; + int16 m_nMinDelay; + int16 m_nMaxDelay; + uint32 m_nTimer; + int32 m_nVehicleHandle; + uint16 m_nUsesRemaining; + bool m_bIsBlocking; +public: + void SwitchOff(); + void SwitchOn(); + uint32 CalcNextGen(); + void DoInternalProcessing(); + void Process(); + void Setup(float x, float y, float z, float angle, int32 mi, int16 color1, int16 color2, uint8 force, uint8 alarm, uint8 lock, uint16 min_delay, uint16 max_delay); + bool CheckForBlockage(int32 mi); + bool CheckIfWithinRangeOfAnyPlayers(); + void SetUsesRemaining(uint16 uses) { m_nUsesRemaining = uses; } +}; + +class CTheCarGenerators +{ +public: + static uint8 ProcessCounter; + static uint32 NumOfCarGenerators; + static CCarGenerator CarGeneratorArray[NUM_CARGENS]; + static uint8 GenerateEvenIfPlayerIsCloseCounter; + static uint32 CurrentActiveCount; + + static void Process(); + static int32 CreateCarGenerator(float x, float y, float z, float angle, int32 mi, int16 color1, int16 color2, uint8 force, uint8 alarm, uint8 lock, uint16 min_delay, uint16 max_delay); + static void Init(); + static void SaveAllCarGenerators(uint8 *, uint32 *); + static void LoadAllCarGenerators(uint8 *, uint32); +}; diff --git a/src/miami/vehicles/Cranes.cpp b/src/miami/vehicles/Cranes.cpp new file mode 100644 index 00000000..934ccb08 --- /dev/null +++ b/src/miami/vehicles/Cranes.cpp @@ -0,0 +1,740 @@ +#include "common.h" + +#include "Cranes.h" + +#include "Camera.h" +#include "DMAudio.h" +#include "Garages.h" +#include "General.h" +#include "Entity.h" +#include "ModelIndices.h" +#include "Replay.h" +#include "Object.h" +#include "World.h" +#include "SaveBuf.h" + +#define MAX_DISTANCE_TO_FIND_CRANE (10.0f) +#define CRANE_UPDATE_RADIUS (300.0f) +#define CRANE_MOVEMENT_PROCESSING_RADIUS (150.0f) +#define CRUSHER_Z (-0.951f) +#define MILITARY_Z (10.7862f) +#define DISTANCE_FROM_PLAYER_TO_REMOVE_VEHICLE (5.0f) +#define DISTANCE_FROM_HOOK_TO_VEHICLE_TO_COLLECT (0.5f) +#define CAR_REWARD_MILITARY_CRANE (1500) +#define CAR_MOVING_SPEED_THRESHOLD (0.01f) +#define CRANE_SLOWDOWN_MULTIPLIER (0.3f) + +#define OSCILLATION_SPEED (0.002f) +#define CAR_ROTATION_SPEED (0.0035f) +#define CRANE_MOVEMENT_SPEED (0.001f) +#define HOOK_ANGLE_MOVEMENT_SPEED (0.004f) +#define HOOK_OFFSET_MOVEMENT_SPEED (0.1f) +#define HOOK_HEIGHT_MOVEMENT_SPEED (0.06f) + +#define MESSAGE_SHOW_DURATION (4000) + +#define MAX_DISTANCE (99999.9f) +#define MIN_VALID_POSITION (-10000.0f) +#define DEFAULT_OFFSET (20.0f) + +#ifdef COMPATIBLE_SAVES +#define CRANES_SAVE_SIZE 0x3E0 +#else +#define CRANES_SAVE_SIZE sizeof(aCranes) +#endif + +uint32 TimerForCamInterpolation; + +uint32 CCranes::CarsCollectedMilitaryCrane; +int32 CCranes::NumCranes; +CCrane CCranes::aCranes[NUM_CRANES]; + +void CCranes::InitCranes(void) +{ + CarsCollectedMilitaryCrane = 0; + NumCranes = 0; + for (int i = 0; i < NUMSECTORS_X; i++) { + for (int j = 0; j < NUMSECTORS_Y; j++) { + for (CPtrNode* pNode = CWorld::GetSector(i, j)->m_lists[ENTITYLIST_BUILDINGS].first; pNode; pNode = pNode->next) { + CEntity* pEntity = (CEntity*)pNode->item; + if (MODELID_CRANE_1 == pEntity->GetModelIndex() || + MODELID_CRANE_2 == pEntity->GetModelIndex() || + MODELID_CRANE_3 == pEntity->GetModelIndex() || + MODELID_CRANE_4 == pEntity->GetModelIndex() || + MODELID_CRANE_5 == pEntity->GetModelIndex() || + MODELID_CRANE_6 == pEntity->GetModelIndex()) + AddThisOneCrane(pEntity); + } + } + } + for (CPtrNode* pNode = CWorld::GetBigBuildingList(LEVEL_MAINLAND).first; pNode; pNode = pNode->next) { + CEntity* pEntity = (CEntity*)pNode->item; + if (MODELID_CRANE_1 == pEntity->GetModelIndex() || + MODELID_CRANE_2 == pEntity->GetModelIndex() || + MODELID_CRANE_3 == pEntity->GetModelIndex() || + MODELID_CRANE_4 == pEntity->GetModelIndex() || + MODELID_CRANE_5 == pEntity->GetModelIndex() || + MODELID_CRANE_6 == pEntity->GetModelIndex()) + AddThisOneCrane(pEntity); + } +} + +void CCranes::AddThisOneCrane(CEntity* pEntity) +{ + pEntity->GetMatrix().ResetOrientation(); + if (NumCranes >= NUM_CRANES) + return; + CCrane* pCrane = &aCranes[NumCranes]; + pCrane->Init(); + pCrane->m_pCraneEntity = (CBuilding*)pEntity; + pCrane->m_nCraneStatus = CCrane::NONE; + pCrane->m_fHookAngle = NumCranes; // lol wtf + while (pCrane->m_fHookAngle > TWOPI) + pCrane->m_fHookAngle -= TWOPI; + pCrane->m_fHookOffset = DEFAULT_OFFSET; + pCrane->m_fHookHeight = DEFAULT_OFFSET; + pCrane->m_nTimeForNextCheck = 0; + pCrane->m_nCraneState = CCrane::IDLE; + pCrane->m_bWasMilitaryCrane = false; + pCrane->m_bIsTop = (MODELID_CRANE_1 != pEntity->GetModelIndex()); + pCrane->m_pHook = nil; + NumCranes++; +} + +void CCranes::ActivateCrane(float fInfX, float fSupX, float fInfY, float fSupY, float fDropOffX, float fDropOffY, float fDropOffZ, float fHeading, bool bIsCrusher, bool bIsMilitary, float fPosX, float fPosY) +{ + float fMinDistance = MAX_DISTANCE; + float X = fPosX, Y = fPosY; + if (X <= MIN_VALID_POSITION || Y <= MIN_VALID_POSITION) { + X = fDropOffX; + Y = fDropOffY; + } + int index = 0; + for (int i = 0; i < NumCranes; i++) { + float distance = (CVector2D(X, Y) - aCranes[i].m_pCraneEntity->GetPosition()).Magnitude(); + if (distance < fMinDistance && distance < MAX_DISTANCE_TO_FIND_CRANE) { + fMinDistance = distance; + index = i; + } + } +#ifdef FIX_BUGS // classic + if (fMinDistance == MAX_DISTANCE) + return; +#endif + CCrane* pCrane = &aCranes[index]; + pCrane->m_fPickupX1 = fInfX; + pCrane->m_fPickupX2 = fSupX; + pCrane->m_fPickupY1 = fInfY; + pCrane->m_fPickupY2 = fSupY; + pCrane->m_vecDropoffTarget.x = fDropOffX; + pCrane->m_vecDropoffTarget.y = fDropOffY; + pCrane->m_vecDropoffTarget.z = fDropOffZ; + pCrane->m_nCraneStatus = CCrane::ACTIVATED; + pCrane->m_pVehiclePickedUp = nil; + pCrane->m_nVehiclesCollected = 0; + pCrane->m_fDropoffHeading = fHeading; + pCrane->m_bIsCrusher = bIsCrusher; + pCrane->m_bIsMilitaryCrane = bIsMilitary; + bool military = true; + if (!bIsMilitary && !pCrane->m_bWasMilitaryCrane) + military = false; + pCrane->m_bWasMilitaryCrane = military; + pCrane->m_nTimeForNextCheck = 0; + pCrane->m_nCraneState = CCrane::IDLE; + float Z; + if (bIsCrusher) + Z = CRUSHER_Z; + else if (bIsMilitary) + Z = MILITARY_Z; + else + Z = CWorld::FindGroundZForCoord((fInfX + fSupX) / 2, (fInfY + fSupY) / 2); + pCrane->FindParametersForTarget((fInfX + fSupX) / 2, (fInfY + fSupY) / 2, Z, &pCrane->m_fPickupAngle, &pCrane->m_fPickupDistance, &pCrane->m_fPickupHeight); + pCrane->FindParametersForTarget(fDropOffX, fDropOffY, fDropOffZ, &pCrane->m_fDropoffAngle, &pCrane->m_fDropoffDistance, &pCrane->m_fDropoffHeight); +} + +void CCranes::DeActivateCrane(float X, float Y) +{ + float fMinDistance = MAX_DISTANCE; + int index = 0; + for (int i = 0; i < NumCranes; i++) { + float distance = (CVector2D(X, Y) - aCranes[i].m_pCraneEntity->GetPosition()).Magnitude(); + if (distance < fMinDistance && distance < MAX_DISTANCE_TO_FIND_CRANE) { + fMinDistance = distance; + index = i; + } + } +#ifdef FIX_BUGS // classic + if (fMinDistance == MAX_DISTANCE) + return; +#endif + aCranes[index].m_nCraneStatus = CCrane::DEACTIVATED; + aCranes[index].m_nCraneState = CCrane::IDLE; +} + +bool CCranes::IsThisCarPickedUp(float X, float Y, CVehicle* pVehicle) +{ + int index = 0; + bool result = false; + for (int i = 0; i < NumCranes; i++) { + float distance = (CVector2D(X, Y) - aCranes[i].m_pCraneEntity->GetPosition()).Magnitude(); + if (distance < MAX_DISTANCE_TO_FIND_CRANE && aCranes[i].m_pVehiclePickedUp == pVehicle) { + if (aCranes[i].m_nCraneState == CCrane::LIFTING_TARGET || aCranes[i].m_nCraneState == CCrane::ROTATING_TARGET) + result = true; + } + } + return result; +} + +void CCranes::UpdateCranes(void) +{ + for (int i = 0; i < NumCranes; i++) { + if (aCranes[i].m_bIsTop || aCranes[i].m_bIsCrusher || + (TheCamera.GetPosition().x + CRANE_UPDATE_RADIUS > aCranes[i].m_pCraneEntity->GetPosition().x && + TheCamera.GetPosition().x - CRANE_UPDATE_RADIUS < aCranes[i].m_pCraneEntity->GetPosition().x && + TheCamera.GetPosition().y + CRANE_UPDATE_RADIUS > aCranes[i].m_pCraneEntity->GetPosition().y && + TheCamera.GetPosition().y + CRANE_UPDATE_RADIUS < aCranes[i].m_pCraneEntity->GetPosition().y)) + aCranes[i].Update(); + } +} + +void CCrane::Update(void) +{ + if (CReplay::IsPlayingBack()) + return; + if (((m_nCraneStatus == ACTIVATED || m_nCraneStatus == DEACTIVATED) && + Abs(TheCamera.GetGameCamPosition().x - m_pCraneEntity->GetPosition().x) < CRANE_MOVEMENT_PROCESSING_RADIUS && + Abs(TheCamera.GetGameCamPosition().y - m_pCraneEntity->GetPosition().y) < CRANE_MOVEMENT_PROCESSING_RADIUS) || + m_nCraneState != IDLE) { + switch (m_nCraneState) { + case IDLE: + if (GoTowardsTarget(m_fPickupAngle, m_fPickupDistance, GetHeightToPickup()) && + CTimer::GetTimeInMilliseconds() > m_nTimeForNextCheck) { + CWorld::AdvanceCurrentScanCode(); +#ifdef FIX_BUGS + int xstart = Max(0, CWorld::GetSectorIndexX(m_fPickupX1)); + int xend = Min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(m_fPickupX2)); + int ystart = Max(0, CWorld::GetSectorIndexY(m_fPickupY1)); + int yend = Min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(m_fPickupY2)); +#else + int xstart = CWorld::GetSectorIndexX(m_fPickupX1); + int xend = CWorld::GetSectorIndexX(m_fPickupX2); + int ystart = CWorld::GetSectorIndexY(m_fPickupY1); + int yend = CWorld::GetSectorIndexY(m_fPickupY1); +#endif + assert(xstart <= xend); + assert(ystart <= yend); + for (int i = xstart; i <= xend; i++) { + for (int j = ystart; j <= yend; j++) { + FindCarInSectorList(&CWorld::GetSector(i, j)->m_lists[ENTITYLIST_VEHICLES]); + FindCarInSectorList(&CWorld::GetSector(i, j)->m_lists[ENTITYLIST_VEHICLES_OVERLAP]); + } + } + } + break; + case GOING_TOWARDS_TARGET: + if (m_pVehiclePickedUp){ + if (m_pVehiclePickedUp->GetPosition().x < m_fPickupX1 || + m_pVehiclePickedUp->GetPosition().x > m_fPickupX2 || + m_pVehiclePickedUp->GetPosition().y < m_fPickupY1 || + m_pVehiclePickedUp->GetPosition().y > m_fPickupY2 || + m_pVehiclePickedUp->pDriver || + Abs(m_pVehiclePickedUp->GetMoveSpeed().x) > CAR_MOVING_SPEED_THRESHOLD || + Abs(m_pVehiclePickedUp->GetMoveSpeed().y) > CAR_MOVING_SPEED_THRESHOLD || + Abs(m_pVehiclePickedUp->GetMoveSpeed().z) > CAR_MOVING_SPEED_THRESHOLD || + (FindPlayerPed()->GetPedState() == PED_ENTER_CAR +#ifdef FIX_BUGS + || FindPlayerPed()->GetPedState() == PED_CARJACK +#endif + ) && FindPlayerPed()->m_pSeekTarget == m_pVehiclePickedUp) { + m_pVehiclePickedUp = nil; + m_nCraneState = IDLE; + } + else { + float fAngle, fOffset, fHeight; + FindParametersForTarget( + m_pVehiclePickedUp->GetPosition().x, + m_pVehiclePickedUp->GetPosition().y, + m_pVehiclePickedUp->GetPosition().z + m_pVehiclePickedUp->GetColModel()->boundingBox.max.z, + &fAngle, &fOffset, &fHeight); + if (GoTowardsTarget(fAngle, fOffset, fHeight)) { + CVector distance = m_pVehiclePickedUp->GetPosition() - m_vecHookCurPos; + distance.z += m_pVehiclePickedUp->GetColModel()->boundingBox.max.z; + if (distance.MagnitudeSqr() < SQR(DISTANCE_FROM_HOOK_TO_VEHICLE_TO_COLLECT)) { + m_nCraneState = GOING_TOWARDS_TARGET_ONLY_HEIGHT; + m_vecHookVelocity *= 0.4f; + m_pVehiclePickedUp->bLightsOn = false; + m_pVehiclePickedUp->bUsesCollision = false; + if (m_bIsCrusher) + m_pVehiclePickedUp->bCollisionProof = true; + } + } + } + } + else + m_nCraneState = IDLE; + break; + case LIFTING_TARGET: + RotateCarriedCarProperly(); + if (GoTowardsTarget(m_fDropoffAngle, m_fDropoffDistance, GetHeightToDropoff(), CRANE_SLOWDOWN_MULTIPLIER)) + m_nCraneState = ROTATING_TARGET; + if (!m_pVehiclePickedUp || m_pVehiclePickedUp->pDriver) { + m_pVehiclePickedUp = nil; + m_nCraneState = IDLE; + } + break; + case GOING_TOWARDS_TARGET_ONLY_HEIGHT: + RotateCarriedCarProperly(); + if (GoTowardsHeightTarget(GetHeightToPickupHeight(), CRANE_SLOWDOWN_MULTIPLIER)) + m_nCraneState = LIFTING_TARGET; + TimerForCamInterpolation = CTimer::GetTimeInMilliseconds(); + if (!m_pVehiclePickedUp || m_pVehiclePickedUp->pDriver) { + m_pVehiclePickedUp = nil; + m_nCraneState = IDLE; + } + break; + case ROTATING_TARGET: + { + bool bRotateFinished = RotateCarriedCarProperly(); + bool bMovementFinished = GoTowardsTarget(m_fDropoffAngle, m_fDropoffDistance, m_fDropoffHeight, 0.3f); + if (bMovementFinished && bRotateFinished) { + float fDistanceFromPlayer = m_pVehiclePickedUp ? ((CVector2D)FindPlayerCoors() - (CVector2D)m_pVehiclePickedUp->GetPosition()).Magnitude() : 0.0f; + if (fDistanceFromPlayer > DISTANCE_FROM_PLAYER_TO_REMOVE_VEHICLE || !m_bWasMilitaryCrane) { + m_nCraneState = DROPPING_TARGET; + if (m_pVehiclePickedUp) { + m_pVehiclePickedUp->bUsesCollision = true; + m_pVehiclePickedUp->m_nStaticFrames = 0; + ++m_nVehiclesCollected; + if (m_bIsMilitaryCrane) { + CCranes::RegisterCarForMilitaryCrane(m_pVehiclePickedUp->GetModelIndex()); + if (!CCranes::HaveAllCarsBeenCollectedByMilitaryCrane()) { + CWorld::Players[CWorld::PlayerInFocus].m_nMoney += CAR_REWARD_MILITARY_CRANE; + CGarages::TriggerMessage("GA_10", CAR_REWARD_MILITARY_CRANE, MESSAGE_SHOW_DURATION, -1); + } + CWorld::Remove(m_pVehiclePickedUp); + delete m_pVehiclePickedUp; + } + } + m_pVehiclePickedUp = nil; + } + } + break; + } + case DROPPING_TARGET: + if (GoTowardsTarget(m_fDropoffAngle, m_fDropoffDistance, GetHeightToDropoffHeight(), CRANE_SLOWDOWN_MULTIPLIER)) { + m_nCraneState = IDLE; + m_nTimeForNextCheck = CTimer::GetTimeInMilliseconds() + 10000; + } + break; + default: + break; + } + CVector vecHook; + CalcHookCoordinates(&vecHook.x, &vecHook.y, &vecHook.z); + m_vecHookVelocity += ((CVector2D)vecHook - (CVector2D)m_vecHookCurPos) * CTimer::GetTimeStep() * CRANE_MOVEMENT_SPEED; + m_vecHookVelocity *= Pow(0.98f, CTimer::GetTimeStep()); + m_vecHookCurPos.x += m_vecHookVelocity.x * CTimer::GetTimeStep(); + m_vecHookCurPos.y += m_vecHookVelocity.y * CTimer::GetTimeStep(); + m_vecHookCurPos.z = vecHook.z; + switch (m_nCraneState) { + case LIFTING_TARGET: + case GOING_TOWARDS_TARGET_ONLY_HEIGHT: + case ROTATING_TARGET: + if (m_pVehiclePickedUp) { + m_pVehiclePickedUp->SetPosition(m_vecHookCurPos.x, m_vecHookCurPos.y, m_vecHookCurPos.z - m_pVehiclePickedUp->GetColModel()->boundingBox.max.z); + m_pVehiclePickedUp->SetMoveSpeed(0.0f, 0.0f, 0.0f); + CVector up(vecHook.x - m_vecHookCurPos.x, vecHook.y - m_vecHookCurPos.y, 20.0f); + up.Normalise(); + m_pVehiclePickedUp->GetRight() = CrossProduct(m_pVehiclePickedUp->GetForward(), up); + m_pVehiclePickedUp->GetForward() = CrossProduct(up, m_pVehiclePickedUp->GetRight()); + m_pVehiclePickedUp->GetUp() = up; + } + break; + default: + break; + } + } + else { + int16 rnd = (m_pCraneEntity->m_randomSeed + (CTimer::GetTimeInMilliseconds() >> 11)) & 0xF; + // 16 options, lasting 2048 ms each + // a bit awkward: why there are 4 periods for -= and 6 for +=? is it a bug? + if (rnd < 4) { + m_fHookAngle -= OSCILLATION_SPEED * CTimer::GetTimeStep(); + if (m_fHookAngle < 0.0f) + m_fHookAngle += TWOPI; + } + else if (rnd > 5 && rnd < 12) { + m_fHookAngle += OSCILLATION_SPEED * CTimer::GetTimeStep(); + if (m_fHookAngle > TWOPI) + m_fHookAngle -= TWOPI; + } + CalcHookCoordinates(&m_vecHookCurPos.x, &m_vecHookCurPos.y, &m_vecHookCurPos.z); + m_vecHookVelocity.x = m_vecHookVelocity.y = 0.0f; + } + float fCos = Cos(m_fHookAngle); + float fSin = Sin(m_fHookAngle); + m_pCraneEntity->GetRight().x = fCos; + m_pCraneEntity->GetForward().y = fCos; + m_pCraneEntity->GetRight().y = fSin; + m_pCraneEntity->GetForward().x = -fSin; + m_pCraneEntity->GetMatrix().UpdateRW(); + m_pCraneEntity->UpdateRwFrame(); + SetHookMatrix(); +} + +bool CCrane::RotateCarriedCarProperly() +{ + if (m_fDropoffHeading <= 0.0f) + return true; + if (!m_pVehiclePickedUp) + return true; + float fAngleDelta = m_fDropoffHeading - CGeneral::GetATanOfXY(m_pVehiclePickedUp->GetForward().x, m_pVehiclePickedUp->GetForward().y); + while (fAngleDelta < -HALFPI) + fAngleDelta += PI; + while (fAngleDelta > HALFPI) + fAngleDelta -= PI; + float fDeltaThisFrame = CAR_ROTATION_SPEED * CTimer::GetTimeStep(); + if (Abs(fAngleDelta) <= fDeltaThisFrame) // no rotation is actually applied? + return true; + m_pVehiclePickedUp->GetMatrix().RotateZ(fAngleDelta < 0 ? -fDeltaThisFrame : fDeltaThisFrame); + return false; +} + +void CCrane::FindCarInSectorList(CPtrList* pList) +{ + CPtrNode* node; + for (node = pList->first; node; node = node->next) { + CVehicle* pVehicle = (CVehicle*)node->item; + if (pVehicle->m_scanCode == CWorld::GetCurrentScanCode()) + continue; + pVehicle->m_scanCode = CWorld::GetCurrentScanCode(); + if (pVehicle->GetPosition().x < m_fPickupX1 || pVehicle->GetPosition().x > m_fPickupX2 || + pVehicle->GetPosition().y < m_fPickupY1 || pVehicle->GetPosition().y > m_fPickupY2) + continue; + if (pVehicle->pDriver) + continue; + if (Abs(pVehicle->GetMoveSpeed().x) >= CAR_MOVING_SPEED_THRESHOLD || + Abs(pVehicle->GetMoveSpeed().y) >= CAR_MOVING_SPEED_THRESHOLD || + Abs(pVehicle->GetMoveSpeed().z) >= CAR_MOVING_SPEED_THRESHOLD) + continue; + if (!pVehicle->IsCar() || pVehicle->GetStatus() == STATUS_WRECKED || pVehicle->m_fHealth < 250.0f) + continue; + if (!DoesCranePickUpThisCarType(pVehicle->GetModelIndex()) || + m_bIsMilitaryCrane && CCranes::DoesMilitaryCraneHaveThisOneAlready(pVehicle->GetModelIndex())) { + if (!pVehicle->bCraneMessageDone) { + pVehicle->bCraneMessageDone = true; + if (!m_bIsMilitaryCrane) + CGarages::TriggerMessage("CR_1", -1, MESSAGE_SHOW_DURATION, -1); // Crane cannot lift this vehicle. + else if (DoesCranePickUpThisCarType(pVehicle->GetModelIndex())) + CGarages::TriggerMessage("GA_20", -1, MESSAGE_SHOW_DURATION, -1); // We got more of these than we can shift. Sorry man, no deal. + else + CGarages::TriggerMessage("GA_19", -1, MESSAGE_SHOW_DURATION, -1); // We're not interested in that model. + } + } + else { + m_pVehiclePickedUp = pVehicle; + pVehicle->RegisterReference((CEntity**)&m_pVehiclePickedUp); + m_nCraneState = GOING_TOWARDS_TARGET; + } + } +} + +bool CCrane::DoesCranePickUpThisCarType(uint32 mi) +{ + if (m_bIsCrusher) { + return mi != MI_FIRETRUCK && + mi != MI_TRASH && +#ifdef FIX_BUGS + mi != MI_COACH && +#endif + mi != MI_SECURICA && + mi != MI_BUS && + mi != MI_DODO && + mi != MI_RHINO; + } + if (m_bIsMilitaryCrane) { + return mi == MI_FIRETRUCK || + mi == MI_AMBULAN || + mi == MI_ENFORCER || + mi == MI_FBIRANCH || + mi == MI_RHINO || + mi == MI_BARRACKS || + mi == MI_POLICE; + } + return true; +} + +bool CCranes::DoesMilitaryCraneHaveThisOneAlready(uint32 mi) +{ + switch (mi) { + case MI_FIRETRUCK: return (CarsCollectedMilitaryCrane & 1); + case MI_AMBULAN: return (CarsCollectedMilitaryCrane & 2); + case MI_ENFORCER: return (CarsCollectedMilitaryCrane & 4); + case MI_FBIRANCH: return (CarsCollectedMilitaryCrane & 8); + case MI_RHINO: return (CarsCollectedMilitaryCrane & 0x10); + case MI_BARRACKS: return (CarsCollectedMilitaryCrane & 0x20); + case MI_POLICE: return (CarsCollectedMilitaryCrane & 0x40); + default: break; + } + return false; +} + +void CCranes::RegisterCarForMilitaryCrane(uint32 mi) +{ + switch (mi) { + case MI_FIRETRUCK: CarsCollectedMilitaryCrane |= 1; break; + case MI_AMBULAN: CarsCollectedMilitaryCrane |= 2; break; + case MI_ENFORCER: CarsCollectedMilitaryCrane |= 4; break; + case MI_FBIRANCH: CarsCollectedMilitaryCrane |= 8; break; + case MI_RHINO: CarsCollectedMilitaryCrane |= 0x10; break; + case MI_BARRACKS: CarsCollectedMilitaryCrane |= 0x20; break; + case MI_POLICE: CarsCollectedMilitaryCrane |= 0x40; break; + default: break; + } +} + +bool CCranes::HaveAllCarsBeenCollectedByMilitaryCrane() +{ + return (CarsCollectedMilitaryCrane & 0x7F) == 0x7F; +} + +bool CCrane::GoTowardsTarget(float fAngleToTarget, float fDistanceToTarget, float fTargetHeight, float fSpeedMultiplier) +{ + bool bAngleMovementFinished, bOffsetMovementFinished, bHeightMovementFinished; + float fHookAngleDelta = fAngleToTarget - m_fHookAngle; + while (fHookAngleDelta > PI) + fHookAngleDelta -= TWOPI; + while (fHookAngleDelta < -PI) + fHookAngleDelta += TWOPI; + float fHookAngleChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_ANGLE_MOVEMENT_SPEED; + if (Abs(fHookAngleDelta) < fHookAngleChangeThisFrame) { + m_fHookAngle = fAngleToTarget; + bAngleMovementFinished = true; + } else { + if (fHookAngleDelta < 0.0f) { + m_fHookAngle -= fHookAngleChangeThisFrame; + if (m_fHookAngle < 0.0f) + m_fHookAngle += TWOPI; + } + else { + m_fHookAngle += fHookAngleChangeThisFrame; + if (m_fHookAngle > TWOPI) + m_fHookAngle -= TWOPI; + } + bAngleMovementFinished = false; + } + float fHookOffsetDelta = fDistanceToTarget - m_fHookOffset; + float fHookOffsetChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_OFFSET_MOVEMENT_SPEED; + if (Abs(fHookOffsetDelta) < fHookOffsetChangeThisFrame) { + m_fHookOffset = fDistanceToTarget; + bOffsetMovementFinished = true; + } else { + if (fHookOffsetDelta < 0.0f) + m_fHookOffset -= fHookOffsetChangeThisFrame; + else + m_fHookOffset += fHookOffsetChangeThisFrame; + bOffsetMovementFinished = false; + } + float fHookHeightDelta = fTargetHeight - m_fHookHeight; + float fHookHeightChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_HEIGHT_MOVEMENT_SPEED; + if (Abs(fHookHeightDelta) < fHookHeightChangeThisFrame) { + m_fHookHeight = fTargetHeight; + bHeightMovementFinished = true; + } else { + if (fHookHeightDelta < 0.0f) + m_fHookHeight -= fHookHeightChangeThisFrame; + else + m_fHookHeight += fHookHeightChangeThisFrame; + bHeightMovementFinished = false; + } + return bAngleMovementFinished && bOffsetMovementFinished && bHeightMovementFinished; +} + +bool CCrane::GoTowardsHeightTarget(float fTargetHeight, float fSpeedMultiplier) +{ + bool bHeightMovementFinished; + float fHookHeightDelta = fTargetHeight - m_fHookHeight; + float fHookHeightChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_HEIGHT_MOVEMENT_SPEED; + if (Abs(fHookHeightDelta) < fHookHeightChangeThisFrame) { + m_fHookHeight = fTargetHeight; + bHeightMovementFinished = true; + } else { + if (fHookHeightDelta < 0.0f) + m_fHookHeight -= fHookHeightChangeThisFrame; + else + m_fHookHeight += fHookHeightChangeThisFrame; + bHeightMovementFinished = false; + } + return bHeightMovementFinished; +} + +void CCrane::FindParametersForTarget(float X, float Y, float Z, float* pAngle, float* pDistance, float* pHeight) +{ + *pAngle = CGeneral::GetATanOfXY(X - m_pCraneEntity->GetPosition().x, Y - m_pCraneEntity->GetPosition().y); + *pDistance = ((CVector2D(X, Y) - (CVector2D)m_pCraneEntity->GetPosition())).Magnitude(); + *pHeight = Z; +} + +void CCrane::CalcHookCoordinates(float* pX, float* pY, float* pZ) +{ + *pX = Cos(m_fHookAngle) * m_fHookOffset + m_pCraneEntity->GetPosition().x; + *pY = Sin(m_fHookAngle) * m_fHookOffset + m_pCraneEntity->GetPosition().y; + *pZ = m_fHookHeight; +} + +void CCrane::SetHookMatrix() +{ + if (m_pHook == nil) + return; + m_pHook->SetPosition(m_vecHookCurPos); + CVector up(m_vecHookInitPos.x - m_vecHookCurPos.x, m_vecHookInitPos.y - m_vecHookCurPos.y, 20.0f); + up.Normalise(); + m_pHook->GetRight() = CrossProduct(CVector(0.0f, 1.0f, 0.0f), up); + m_pHook->GetForward() = CrossProduct(up, m_pHook->GetRight()); + m_pHook->GetUp() = up; + m_pHook->SetOrientation(0.0f, 0.0f, -HALFPI); + m_pHook->GetMatrix().UpdateRW(); + m_pHook->UpdateRwFrame(); + CWorld::Remove(m_pHook); + CWorld::Add(m_pHook); +} + +bool CCranes::IsThisCarBeingCarriedByAnyCrane(CVehicle* pVehicle) +{ + for (int i = 0; i < NumCranes; i++) { + if (pVehicle == aCranes[i].m_pVehiclePickedUp) { + switch (aCranes[i].m_nCraneState) { + case CCrane::GOING_TOWARDS_TARGET_ONLY_HEIGHT: + case CCrane::LIFTING_TARGET: + case CCrane::ROTATING_TARGET: + return true; + default: + break; + } + } + } + return false; +} + +bool CCranes::IsThisCarBeingTargettedByAnyCrane(CVehicle* pVehicle) +{ + for (int i = 0; i < NumCranes; i++) { + if (pVehicle == aCranes[i].m_pVehiclePickedUp) + return true; + } + return false; +} + +void CCranes::Save(uint8* buf, uint32* size) +{ + INITSAVEBUF + + *size = 2 * sizeof(uint32) + CRANES_SAVE_SIZE; + WriteSaveBuf(buf, NumCranes); + WriteSaveBuf(buf, CarsCollectedMilitaryCrane); + for (int i = 0; i < NUM_CRANES; i++) { +#ifdef COMPATIBLE_SAVES + int32 tmp = aCranes[i].m_pCraneEntity != nil ? CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert(aCranes[i].m_pCraneEntity) + 1 : 0; + WriteSaveBuf(buf, tmp); + tmp = aCranes[i].m_pHook != nil ? CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(aCranes[i].m_pHook) + 1 : 0; + WriteSaveBuf(buf, tmp); + WriteSaveBuf(buf, aCranes[i].m_fPickupX1); + WriteSaveBuf(buf, aCranes[i].m_fPickupX2); + WriteSaveBuf(buf, aCranes[i].m_fPickupY1); + WriteSaveBuf(buf, aCranes[i].m_fPickupY2); + WriteSaveBuf(buf, aCranes[i].m_vecDropoffTarget); + WriteSaveBuf(buf, aCranes[i].m_fDropoffHeading); + WriteSaveBuf(buf, aCranes[i].m_fPickupAngle); + WriteSaveBuf(buf, aCranes[i].m_fDropoffAngle); + WriteSaveBuf(buf, aCranes[i].m_fPickupDistance); + WriteSaveBuf(buf, aCranes[i].m_fDropoffDistance); + WriteSaveBuf(buf, aCranes[i].m_fPickupHeight); + WriteSaveBuf(buf, aCranes[i].m_fDropoffHeight); + WriteSaveBuf(buf, aCranes[i].m_fHookAngle); + WriteSaveBuf(buf, aCranes[i].m_fHookOffset); + WriteSaveBuf(buf, aCranes[i].m_fHookHeight); + WriteSaveBuf(buf, aCranes[i].m_vecHookInitPos); + WriteSaveBuf(buf, aCranes[i].m_vecHookCurPos); + WriteSaveBuf(buf, aCranes[i].m_vecHookVelocity); + tmp = aCranes[i].m_pVehiclePickedUp != nil ? CPools::GetVehiclePool()->GetJustIndex_NoFreeAssert(aCranes[i].m_pVehiclePickedUp) + 1 : 0; + WriteSaveBuf(buf, tmp); + WriteSaveBuf(buf, aCranes[i].m_nTimeForNextCheck); + WriteSaveBuf(buf, aCranes[i].m_nCraneStatus); + WriteSaveBuf(buf, aCranes[i].m_nCraneState); + WriteSaveBuf(buf, aCranes[i].m_nVehiclesCollected); + WriteSaveBuf(buf, aCranes[i].m_bIsCrusher); + WriteSaveBuf(buf, aCranes[i].m_bIsMilitaryCrane); + WriteSaveBuf(buf, aCranes[i].m_bWasMilitaryCrane); + WriteSaveBuf(buf, aCranes[i].m_bIsTop); + ZeroSaveBuf(buf, 1); +#else + CCrane *pCrane = WriteSaveBuf(buf, aCranes[i]); + if (pCrane->m_pCraneEntity != nil) + pCrane->m_pCraneEntity = (CBuilding*)(CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert(pCrane->m_pCraneEntity) + 1); + if (pCrane->m_pHook != nil) + pCrane->m_pHook = (CObject*)(CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(pCrane->m_pHook) + 1); + if (pCrane->m_pVehiclePickedUp != nil) + pCrane->m_pVehiclePickedUp = (CVehicle*)(CPools::GetVehiclePool()->GetJustIndex_NoFreeAssert(pCrane->m_pVehiclePickedUp) + 1); +#endif + } + + VALIDATESAVEBUF(*size); +} + +void CCranes::Load(uint8* buf, uint32 size) +{ + INITSAVEBUF + + ReadSaveBuf(&NumCranes, buf); + ReadSaveBuf(&CarsCollectedMilitaryCrane, buf); + for (int i = 0; i < NUM_CRANES; i++) { +#ifdef COMPATIBLE_SAVES + int32 tmp; + ReadSaveBuf(&tmp, buf); + aCranes[i].m_pCraneEntity = tmp != 0 ? CPools::GetBuildingPool()->GetSlot(tmp - 1) : nil; + ReadSaveBuf(&tmp, buf); + aCranes[i].m_pHook = tmp != 0 ? CPools::GetObjectPool()->GetSlot(tmp - 1) : nil; + ReadSaveBuf(&aCranes[i].m_fPickupX1, buf); + ReadSaveBuf(&aCranes[i].m_fPickupX2, buf); + ReadSaveBuf(&aCranes[i].m_fPickupY1, buf); + ReadSaveBuf(&aCranes[i].m_fPickupY2, buf); + ReadSaveBuf(&aCranes[i].m_vecDropoffTarget, buf); + ReadSaveBuf(&aCranes[i].m_fDropoffHeading, buf); + ReadSaveBuf(&aCranes[i].m_fPickupAngle, buf); + ReadSaveBuf(&aCranes[i].m_fDropoffAngle, buf); + ReadSaveBuf(&aCranes[i].m_fPickupDistance, buf); + ReadSaveBuf(&aCranes[i].m_fDropoffDistance, buf); + ReadSaveBuf(&aCranes[i].m_fPickupHeight, buf); + ReadSaveBuf(&aCranes[i].m_fDropoffHeight, buf); + ReadSaveBuf(&aCranes[i].m_fHookAngle, buf); + ReadSaveBuf(&aCranes[i].m_fHookOffset, buf); + ReadSaveBuf(&aCranes[i].m_fHookHeight, buf); + ReadSaveBuf(&aCranes[i].m_vecHookInitPos, buf); + ReadSaveBuf(&aCranes[i].m_vecHookCurPos, buf); + ReadSaveBuf(&aCranes[i].m_vecHookVelocity, buf); + ReadSaveBuf(&tmp, buf); + aCranes[i].m_pVehiclePickedUp = tmp != 0 ? CPools::GetVehiclePool()->GetSlot(tmp - 1) : nil; + ReadSaveBuf(&aCranes[i].m_nTimeForNextCheck, buf); + ReadSaveBuf(&aCranes[i].m_nCraneStatus, buf); + ReadSaveBuf(&aCranes[i].m_nCraneState, buf); + ReadSaveBuf(&aCranes[i].m_nVehiclesCollected, buf); + ReadSaveBuf(&aCranes[i].m_bIsCrusher, buf); + ReadSaveBuf(&aCranes[i].m_bIsMilitaryCrane, buf); + ReadSaveBuf(&aCranes[i].m_bWasMilitaryCrane, buf); + ReadSaveBuf(&aCranes[i].m_bIsTop, buf); + SkipSaveBuf(buf, 1); +#else + ReadSaveBuf(&aCranes[i], buf); + } + for (int i = 0; i < NUM_CRANES; i++) { + CCrane *pCrane = &aCranes[i]; + if (pCrane->m_pCraneEntity != nil) + pCrane->m_pCraneEntity = CPools::GetBuildingPool()->GetSlot((uintptr)pCrane->m_pCraneEntity - 1); + if (pCrane->m_pHook != nil) + pCrane->m_pHook = CPools::GetObjectPool()->GetSlot((uintptr)pCrane->m_pHook - 1); + if (pCrane->m_pVehiclePickedUp != nil) + pCrane->m_pVehiclePickedUp = CPools::GetVehiclePool()->GetSlot((uintptr)pCrane->m_pVehiclePickedUp - 1); +#endif + } + + VALIDATESAVEBUF(size); +} diff --git a/src/miami/vehicles/Cranes.h b/src/miami/vehicles/Cranes.h new file mode 100644 index 00000000..e842ed3f --- /dev/null +++ b/src/miami/vehicles/Cranes.h @@ -0,0 +1,96 @@ +#pragma once +#include "common.h" + +#include "World.h" + +class CVehicle; +class CEntity; +class CObject; +class CBuilding; + +class CCrane +{ +public: + enum CraneState { + IDLE = 0, + GOING_TOWARDS_TARGET = 1, + LIFTING_TARGET = 2, + GOING_TOWARDS_TARGET_ONLY_HEIGHT = 3, + ROTATING_TARGET = 4, + DROPPING_TARGET = 5 + }; + enum CraneStatus { + NONE = 0, + ACTIVATED = 1, + DEACTIVATED = 2 + }; + CBuilding *m_pCraneEntity; + CObject *m_pHook; + float m_fPickupX1; + float m_fPickupX2; + float m_fPickupY1; + float m_fPickupY2; + CVector m_vecDropoffTarget; + float m_fDropoffHeading; + float m_fPickupAngle; + float m_fDropoffAngle; + float m_fPickupDistance; + float m_fDropoffDistance; + float m_fPickupHeight; + float m_fDropoffHeight; + float m_fHookAngle; + float m_fHookOffset; + float m_fHookHeight; + CVector m_vecHookInitPos; + CVector m_vecHookCurPos; + CVector2D m_vecHookVelocity; + CVehicle *m_pVehiclePickedUp; + uint32 m_nTimeForNextCheck; + uint8 m_nCraneStatus; + uint8 m_nCraneState; + uint8 m_nVehiclesCollected; + bool m_bIsCrusher; + bool m_bIsMilitaryCrane; + bool m_bWasMilitaryCrane; + bool m_bIsTop; + + void Init(void) { memset(this, 0, sizeof(*this)); } + void Update(void); + bool RotateCarriedCarProperly(void); + void FindCarInSectorList(CPtrList* pList); + bool DoesCranePickUpThisCarType(uint32 mi); + bool GoTowardsTarget(float fAngleToTarget, float fDistanceToTarget, float fTargetHeight, float fSpeedMultiplier = 1.0f); + bool GoTowardsHeightTarget(float fTargetHeight, float fSpeedMultiplier = 1.0f); + void FindParametersForTarget(float X, float Y, float Z, float* pAngle, float* pDistance, float* pHeight); + void CalcHookCoordinates(float* pX, float* pY, float* pZ); + void SetHookMatrix(void); + + float GetHeightToPickup() { return 4.0f + m_fPickupHeight + (m_bIsCrusher ? 4.5f : 0.0f); }; + float GetHeightToDropoff() { return m_bIsCrusher ? (2.0f + m_fDropoffHeight + 3.0f) : (2.0f + m_fDropoffHeight); } + float GetHeightToPickupHeight() { return m_fPickupHeight + (m_bIsCrusher ? 7.0f : 4.0f); } + float GetHeightToDropoffHeight() { return m_fDropoffHeight + (m_bIsCrusher ? 7.0f : 2.0f); } +}; + +VALIDATE_SIZE(CCrane, 128); + +class CCranes +{ +public: + static void InitCranes(void); + static void AddThisOneCrane(CEntity* pCraneEntity); + static void ActivateCrane(float fInfX, float fSupX, float fInfY, float fSupY, float fDropOffX, float fDropOffY, float fDropOffZ, float fHeading, bool bIsCrusher, bool bIsMilitary, float fPosX, float fPosY); + static void DeActivateCrane(float fX, float fY); + static bool IsThisCarPickedUp(float fX, float fY, CVehicle* pVehicle); + static void UpdateCranes(void); + static bool DoesMilitaryCraneHaveThisOneAlready(uint32 mi); + static void RegisterCarForMilitaryCrane(uint32 mi); + static bool HaveAllCarsBeenCollectedByMilitaryCrane(void); + static bool IsThisCarBeingCarriedByAnyCrane(CVehicle* pVehicle); + static bool IsThisCarBeingTargettedByAnyCrane(CVehicle* pVehicle); + static void Save(uint8* buf, uint32* size); + static void Load(uint8* buf, uint32 size); // out of class in III PC and later because of SecuROM + + static uint32 CarsCollectedMilitaryCrane; + static int32 NumCranes; + static CCrane aCranes[NUM_CRANES]; +}; diff --git a/src/miami/vehicles/DamageManager.cpp b/src/miami/vehicles/DamageManager.cpp new file mode 100644 index 00000000..8ba235b7 --- /dev/null +++ b/src/miami/vehicles/DamageManager.cpp @@ -0,0 +1,233 @@ +#include "common.h" + +#include "General.h" +#include "Vehicle.h" +#include "DamageManager.h" + + +float G_aComponentDamage[] = { 2.5f, 1.25f, 3.2f, 1.4f, 2.5f, 2.8f, 0.5f }; + +CDamageManager::CDamageManager(void) +{ + ResetDamageStatus(); + m_fWheelDamageEffect = 0.5f; + field_18 = 1; +} + +void +CDamageManager::ResetDamageStatus(void) +{ + int i; + m_fWheelDamageEffect = 0.0f; + m_engineStatus = 0; + for(i = 0; i < ARRAY_SIZE(m_wheelStatus); i++) m_wheelStatus[i] = 0; + for(i = 0; i < ARRAY_SIZE(m_doorStatus); i++) m_doorStatus[i] = 0; + m_lightStatus = 0; + m_panelStatus = 0; +} + +void +CDamageManager::FuckCarCompletely(void) +{ + int i; + + m_wheelStatus[0] = WHEEL_STATUS_MISSING; + // wheels 1-3 not reset? + + for(i = 0; i < ARRAY_SIZE(m_doorStatus); i++) + m_doorStatus[i] = DOOR_STATUS_MISSING; + + for(i = 0; i < 3; i++){ +#ifdef FIX_BUGS + ProgressPanelDamage(VEHBUMPER_FRONT); + ProgressPanelDamage(VEHBUMPER_REAR); +#else + // this can't be right + ProgressPanelDamage(COMPONENT_BUMPER_FRONT); + ProgressPanelDamage(COMPONENT_BUMPER_REAR); +#endif + } + // Why set to no damage? +#ifndef FIX_BUGS + m_lightStatus = 0; + m_panelStatus = 0; +#endif + SetEngineStatus(250); +} + +bool +CDamageManager::ApplyDamage(tComponent component, float damage, float unused) +{ + tComponentGroup group; + uint8 subComp; + + GetComponentGroup(component, &group, &subComp); + damage *= G_aComponentDamage[group]; + if(component == COMPONENT_PANEL_WINDSCREEN) + damage *= 0.6f; + if(damage > 150.0f){ + switch(group){ + case COMPGROUP_WHEEL: + ProgressWheelDamage(subComp); + break; + case COMPGROUP_DOOR: + case COMPGROUP_BOOT: + ProgressDoorDamage(subComp); + break; + case COMPGROUP_BONNET: + if(damage > 220.0f) + ProgressEngineDamage(); + ProgressDoorDamage(subComp); + break; + case COMPGROUP_PANEL: + // so windscreen is a light? + SetLightStatus((eLights)subComp, 1); + // fall through + case COMPGROUP_BUMPER: + if(damage > 220.0f && + (component == COMPONENT_PANEL_FRONT_LEFT || + component == COMPONENT_PANEL_FRONT_RIGHT || + component == COMPONENT_PANEL_WINDSCREEN)) + ProgressEngineDamage(); + ProgressPanelDamage(subComp); + break; + default: break; + } + return true; + } + return false; +} + +bool +CDamageManager::GetComponentGroup(tComponent component, tComponentGroup *componentGroup, uint8 *subComp) +{ + *subComp = -2; // ?? + + // This is done very strangely in the game, maybe an optimized switch? + if(component >= COMPONENT_PANEL_FRONT_LEFT){ + if(component >= COMPONENT_BUMPER_FRONT) + *componentGroup = COMPGROUP_BUMPER; + else + *componentGroup = COMPGROUP_PANEL; + *subComp = component - COMPONENT_PANEL_FRONT_LEFT; + return true; + }else if(component >= COMPONENT_DOOR_BONNET){ + if(component == COMPONENT_DOOR_BONNET) + *componentGroup = COMPGROUP_BONNET; + else if(component == COMPONENT_DOOR_BOOT) + *componentGroup = COMPGROUP_BOOT; + else + *componentGroup = COMPGROUP_DOOR; + *subComp = component - COMPONENT_DOOR_BONNET; + return true; + }else if(component >= COMPONENT_WHEEL_FRONT_LEFT){ + *componentGroup = COMPGROUP_WHEEL; + *subComp = component - COMPONENT_WHEEL_FRONT_LEFT; + return true; + }else if(component >= COMPONENT_DEFAULT){ + *componentGroup = COMPGROUP_DEFAULT; + *subComp = COMPONENT_DEFAULT; + return true; + }else + return false; +} + +void +CDamageManager::SetDoorStatus(int32 door, uint32 status) +{ + m_doorStatus[door] = status; +} + +int32 +CDamageManager::GetDoorStatus(int32 door) +{ + return m_doorStatus[door]; +} + +bool +CDamageManager::ProgressDoorDamage(uint8 door) +{ + int status = GetDoorStatus(door); + if(status == PANEL_STATUS_MISSING) + return false; + SetDoorStatus(door, status+1); + return true; +} + +void +CDamageManager::SetPanelStatus(int32 panel, uint32 status) +{ + m_panelStatus = dpb(status, panel*4, 4, m_panelStatus); +} + +int32 +CDamageManager::GetPanelStatus(int32 panel) +{ + return ldb(panel*4, 4, m_panelStatus); +} + +bool +CDamageManager::ProgressPanelDamage(uint8 panel) +{ + int status = GetPanelStatus(panel); + if(status == DOOR_STATUS_MISSING) + return false; + SetPanelStatus(panel, status+1); + return true; +} + +void +CDamageManager::SetLightStatus(eLights light, uint32 status) +{ + m_lightStatus = dpb(status, light*2, 2, m_lightStatus); +} + +int32 +CDamageManager::GetLightStatus(eLights light) +{ + return ldb(light*2, 2, m_lightStatus); +} + +void +CDamageManager::SetWheelStatus(int32 wheel, uint32 status) +{ + m_wheelStatus[wheel] = status; +} + +int32 +CDamageManager::GetWheelStatus(int32 wheel) +{ + return m_wheelStatus[wheel]; +} + +bool +CDamageManager::ProgressWheelDamage(uint8 wheel) +{ + int status = GetWheelStatus(wheel); + if(status == WHEEL_STATUS_MISSING) + return false; + SetWheelStatus(wheel, status+1); + return true; +} + +void +CDamageManager::SetEngineStatus(uint32 status) +{ + if(status > 250) + m_engineStatus = 250; + else + m_engineStatus = status; +} + +int32 +CDamageManager::GetEngineStatus(void) +{ + return m_engineStatus; +} + +bool +CDamageManager::ProgressEngineDamage(void) +{ + // gone in VC + return false; +} diff --git a/src/miami/vehicles/DamageManager.h b/src/miami/vehicles/DamageManager.h new file mode 100644 index 00000000..312006e3 --- /dev/null +++ b/src/miami/vehicles/DamageManager.h @@ -0,0 +1,115 @@ +#pragma once + +#include "common.h" + +// TODO: move some of this into Vehicle.h + +enum eEngineStatus +{ + ENGINE_STATUS_STEAM1 = 100, + ENGINE_STATUS_STEAM2 = 150, + ENGINE_STATUS_SMOKE = 200, + ENGINE_STATUS_ON_FIRE = 225 +}; + +enum eDoorStatus +{ + DOOR_STATUS_OK, + DOOR_STATUS_SMASHED, + DOOR_STATUS_SWINGING, + DOOR_STATUS_MISSING +}; + +enum ePanelStatus +{ + PANEL_STATUS_OK, + PANEL_STATUS_SMASHED1, + PANEL_STATUS_SMASHED2, + PANEL_STATUS_MISSING, +}; + +enum eWheelStatus +{ + WHEEL_STATUS_OK, + WHEEL_STATUS_BURST, + WHEEL_STATUS_MISSING +}; + +enum eLightStatus +{ + LIGHT_STATUS_OK, + LIGHT_STATUS_BROKEN +}; + +enum tComponent +{ + COMPONENT_DEFAULT, + COMPONENT_WHEEL_FRONT_LEFT, + COMPONENT_WHEEL_FRONT_RIGHT, + COMPONENT_WHEEL_REAR_LEFT, + COMPONENT_WHEEL_REAR_RIGHT, + COMPONENT_DOOR_BONNET, + COMPONENT_DOOR_BOOT, + COMPONENT_DOOR_FRONT_LEFT, + COMPONENT_DOOR_FRONT_RIGHT, + COMPONENT_DOOR_REAR_LEFT, + COMPONENT_DOOR_REAR_RIGHT, + COMPONENT_PANEL_FRONT_LEFT, + COMPONENT_PANEL_FRONT_RIGHT, + COMPONENT_PANEL_REAR_LEFT, + COMPONENT_PANEL_REAR_RIGHT, + COMPONENT_PANEL_WINDSCREEN, + COMPONENT_BUMPER_FRONT, + COMPONENT_BUMPER_REAR, +}; + +enum tComponentGroup +{ + COMPGROUP_BUMPER, + COMPGROUP_WHEEL, + COMPGROUP_DOOR, + COMPGROUP_BONNET, + COMPGROUP_BOOT, + COMPGROUP_PANEL, + COMPGROUP_DEFAULT, +}; + +enum eLights; + +class CDamageManager +{ +public: + + float m_fWheelDamageEffect; + uint8 m_engineStatus; + uint8 m_wheelStatus[4]; + uint8 m_doorStatus[6]; + uint32 m_lightStatus; + uint32 m_panelStatus; + uint8 field_18; + + CDamageManager(void); + + void ResetDamageStatus(void); + void FuckCarCompletely(void); + bool ApplyDamage(tComponent component, float damage, float unused); + bool GetComponentGroup(tComponent component, tComponentGroup *componentGroup, uint8 *foo); + + void SetDoorStatus(int32 door, uint32 status); + int32 GetDoorStatus(int32 door); + bool ProgressDoorDamage(uint8 door); + void SetPanelStatus(int32 panel, uint32 status); + int32 GetPanelStatus(int32 panel); + bool ProgressPanelDamage(uint8 panel); + // needed for CReplay + static int32 GetPanelStatus(uint32 panelstatus, int32 panel) { return ldb(panel*4, 4, panelstatus); } + void SetLightStatus(eLights light, uint32 status); + int32 GetLightStatus(eLights light); + void SetWheelStatus(int32 wheel, uint32 status); + int32 GetWheelStatus(int32 wheel); + bool ProgressWheelDamage(uint8 wheel); + void SetEngineStatus(uint32 status); + int32 GetEngineStatus(void); + bool ProgressEngineDamage(void); +}; +VALIDATE_SIZE(CDamageManager, 0x1C); diff --git a/src/miami/vehicles/Door.cpp b/src/miami/vehicles/Door.cpp new file mode 100644 index 00000000..1b3f9e8f --- /dev/null +++ b/src/miami/vehicles/Door.cpp @@ -0,0 +1,170 @@ +#include "common.h" + +#include "Vehicle.h" +#include "Door.h" + +CDoor::CDoor(void) +{ + memset(this, 0, sizeof(*this)); +} + +void +CDoor::Open(float ratio) +{ + float open; + + m_fPrevAngle = m_fAngle; + open = RetAngleWhenOpen(); + if(ratio < 1.0f){ + m_fAngle = open*ratio; + if(m_fAngle == 0.0f) + m_fAngVel = 0.0f; + }else{ + m_nDoorState = DOORST_OPEN; + m_fAngle = open; + } +} + +void +CDoor::Process(CVehicle *vehicle) +{ + static CVector vecOffset(1.0f, 0.0f, 0.0f); + CVector speed = vehicle->GetSpeed(vecOffset); + CVector vecSpeedDiff = speed - m_vecSpeed; + vecSpeedDiff = Multiply3x3(vecSpeedDiff, vehicle->GetMatrix()); + + // air resistance + float fSpeedDiff = 0.0f; // uninitialized in game + switch(m_nAxis){ + case 0: // x-axis + if(m_nDirn) + fSpeedDiff = vecSpeedDiff.y + vecSpeedDiff.z; + else + fSpeedDiff = -(vecSpeedDiff.y + vecSpeedDiff.z); + break; + + // we don't support y axis apparently? + + case 2: // z-axis + if(m_nDirn) + fSpeedDiff = -(vecSpeedDiff.y + vecSpeedDiff.x); + else + fSpeedDiff = vecSpeedDiff.y - vecSpeedDiff.x; + break; + } + fSpeedDiff = Clamp(fSpeedDiff, -0.2f, 0.2f); + if(Abs(fSpeedDiff) > 0.002f) + m_fAngVel += fSpeedDiff; + m_fAngVel *= 0.945f; + m_fAngVel = Clamp(m_fAngVel, -0.3f, 0.3f); + + m_fAngle += m_fAngVel; + m_nDoorState = DOORST_SWINGING; + if(m_fAngle > m_fMaxAngle){ + m_fAngle = m_fMaxAngle; + m_fAngVel *= -0.8f; + m_nDoorState = DOORST_OPEN; + } + if(m_fAngle < m_fMinAngle){ + m_fAngle = m_fMinAngle; + m_fAngVel *= -0.8f; + m_nDoorState = DOORST_CLOSED; + } + m_vecSpeed = speed; +} + +float +CDoor::RetAngleWhenClosed(void) +{ + if(Abs(m_fMaxAngle) < Abs(m_fMinAngle)) + return m_fMaxAngle; + else + return m_fMinAngle; +} + +float +CDoor::RetAngleWhenOpen(void) +{ + if(Abs(m_fMaxAngle) < Abs(m_fMinAngle)) + return m_fMinAngle; + else + return m_fMaxAngle; +} + +float +CDoor::GetAngleOpenRatio(void) +{ + float open = RetAngleWhenOpen(); + if(open == 0.0f) + return 0.0f; + return m_fAngle/open; +} + +bool +CDoor::IsFullyOpen(void) +{ + // why -0.5? that's around 28 deg less than fully open + if(Abs(m_fAngle) < Abs(RetAngleWhenOpen()) - 0.5f) + return false; + return true; +} + +bool +CDoor::IsClosed(void) +{ + return m_fAngle == RetAngleWhenClosed(); +} + + +CTrainDoor::CTrainDoor(void) +{ + memset(this, 0, sizeof(*this)); +} + +void +CTrainDoor::Open(float ratio) +{ + float open; + + m_fPrevPosn = m_fPosn; + open = RetTranslationWhenOpen(); + if(ratio < 1.0f){ + m_fPosn = open*ratio; + }else{ + m_nDoorState = DOORST_OPEN; + m_fPosn = open; + } +} + +float +CTrainDoor::RetTranslationWhenClosed(void) +{ + if(Abs(m_fClosedPosn) < Abs(m_fOpenPosn)) + return m_fClosedPosn; + else + return m_fOpenPosn; +} + +float +CTrainDoor::RetTranslationWhenOpen(void) +{ + if(Abs(m_fClosedPosn) < Abs(m_fOpenPosn)) + return m_fOpenPosn; + else + return m_fClosedPosn; +} + +bool +CTrainDoor::IsFullyOpen(void) +{ + // 0.5f again... + if(Abs(m_fPosn) < Abs(RetTranslationWhenOpen()) - 0.5f) + return false; + return true; +} + +bool +CTrainDoor::IsClosed(void) +{ + return m_fPosn == RetTranslationWhenClosed(); +} diff --git a/src/miami/vehicles/Door.h b/src/miami/vehicles/Door.h new file mode 100644 index 00000000..567d3263 --- /dev/null +++ b/src/miami/vehicles/Door.h @@ -0,0 +1,69 @@ +#pragma once + +class CVehicle; + +enum eDoorState +{ + DOORST_SWINGING, + // actually wrong though, + // OPEN is really MAX_ANGLE and CLOSED is MIN_ANGLE + DOORST_OPEN, + DOORST_CLOSED +}; + +class CDoor +{ +public: + float m_fMaxAngle; + float m_fMinAngle; + // direction of rotation for air resistance + int8 m_nDirn; + // axis in which this door rotates + int8 m_nAxis; + int8 m_nDoorState; + float m_fAngle; + float m_fPrevAngle; + float m_fAngVel; + CVector m_vecSpeed; + + CDoor(void); + void Init(float minAngle, float maxAngle, int8 dir, int8 axis) { + m_fMinAngle = minAngle; + m_fMaxAngle = maxAngle; + m_nDirn = dir; + m_nAxis = axis; + } + void Open(float ratio); + void Process(CVehicle *veh); + float RetAngleWhenClosed(void); // dead + float RetAngleWhenOpen(void); + float GetAngleOpenRatio(void); + bool IsFullyOpen(void); + bool IsClosed(void); // dead +}; + +class CTrainDoor +{ +public: + float m_fClosedPosn; + float m_fOpenPosn; + int8 m_nDirn; + int8 m_nDoorState; // same enum as above? + int8 m_nAxis; + float m_fPosn; + float m_fPrevPosn; + int field_14; // unused? + + CTrainDoor(void); + void Init(float open, float closed, int8 dir, int8 axis) { + m_fOpenPosn = open; + m_fClosedPosn = closed; + m_nDirn = dir; + m_nAxis = axis; + } + bool IsClosed(void); + bool IsFullyOpen(void); + float RetTranslationWhenClosed(void); + float RetTranslationWhenOpen(void); + void Open(float ratio); +}; diff --git a/src/miami/vehicles/Floater.cpp b/src/miami/vehicles/Floater.cpp new file mode 100644 index 00000000..08688a3c --- /dev/null +++ b/src/miami/vehicles/Floater.cpp @@ -0,0 +1,324 @@ +#include "common.h" + +#include "Timer.h" +#include "WaterLevel.h" +#include "ModelIndices.h" +#include "Physical.h" +#include "Vehicle.h" +#include "Floater.h" + +cBuoyancy mod_Buoyancy; + +float fVolMultiplier = 1.0f; +// amount of boat volume in bounding box +// 1.0-volume is the empty space in the bbox +float fBoatVolumeDistribution[9] = { + // rear + 0.75f, 0.9f, 0.75f, + 0.95f, 1.0f, 0.95f, + 0.4f, 0.7f, 0.4f + // bow +}; +float fBoatVolumeDistributionCat[9] = { + 0.9f, 0.3f, 0.9f, + 1.0f, 0.5f, 1.0f, + 0.95f, 0.4f, 0.95f +}; +float fBoatVolumeDistributionSail[9] = { + 0.55f, 0.95f, 0.55f, + 0.75f, 1.1f, 0.75f, + 0.3f, 0.8f, 0.3f +}; +float fBoatVolumeDistributionDinghy[9] = { + 0.65f, 0.85f, 0.65f, + 0.85f, 1.1f, 0.85f, + 0.65f, 0.95f, 0.65f +}; +float fBoatVolumeDistributionSpeed[9] = { + 0.7f, 0.9f, 0.7f, + 0.95f, 1.0f, 0.95f, + 0.6f, 0.7f, 0.6f +}; + +bool +cBuoyancy::ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *point, CVector *impulse) +{ + m_numSteps = 2.0f; + + if(!CWaterLevel::GetWaterLevel(phys->GetPosition(), &m_waterlevel, phys->bTouchingWater)) + return false; + m_matrix = phys->GetMatrix(); + + PreCalcSetup(phys, buoyancy); + SimpleCalcBuoyancy(); + float f = CalcBuoyancyForce(phys, point, impulse); + if(m_isBoat) + return true; + return f != 0.0f; +} + +bool +cBuoyancy::ProcessBuoyancyBoat(CVehicle *veh, float buoyancy, CVector *point, CVector *impulse, bool bNoTurnForce) +{ + m_numSteps = 2.0f; + + if(!CWaterLevel::GetWaterLevel(veh->GetPosition(), &m_waterlevel, veh->bTouchingWater)) + return false; + m_matrix = veh->GetMatrix(); + PreCalcSetup(veh, buoyancy); + + + float x, y; + int ix, i; + tWaterLevel waterPosition; + CVector waterNormal; + + // Floater is divided into 3x3 parts. Process and sum each of them + float volDiv = 1.0f/((m_dimMax.z - m_dimMin.z)*sq(m_numSteps+1.0f)); + ix = 0; + for(x = m_dimMin.x; x <= m_dimMax.x; x += m_step.x){ + i = ix; + for(y = m_dimMin.y; y <= m_dimMax.y; y += m_step.y){ + CVector waterLevel(x, y, 0.0f); + FindWaterLevelNorm(m_positionZ, &waterLevel, &waterPosition, &waterNormal); + switch(veh->GetModelIndex()){ + case MI_RIO: + fVolMultiplier = fBoatVolumeDistributionCat[i]; + break; + case MI_SQUALO: + case MI_SPEEDER: + case MI_JETMAX: + fVolMultiplier = fBoatVolumeDistributionSpeed[i]; + break; + case MI_COASTG: + case MI_DINGHY: + fVolMultiplier = fBoatVolumeDistributionDinghy[i]; + break; + case MI_MARQUIS: + fVolMultiplier = fBoatVolumeDistributionSail[i]; + break; + case MI_PREDATOR: + case MI_SKIMMER: + case MI_REEFER: + case MI_TROPIC: + default: + fVolMultiplier = fBoatVolumeDistribution[i]; + break; + } + if(waterPosition != FLOATER_ABOVE_WATER){ + float volume = SimpleSumBuoyancyData(waterLevel, waterPosition); + float upImpulse = volume * volDiv * buoyancy * CTimer::GetTimeStep(); + CVector speed = veh->GetSpeed(Multiply3x3(veh->GetMatrix(), CVector(x, y, 0.0f))); + float damp = 1.0f - DotProduct(speed, waterNormal)*veh->pHandling->fSuspensionDampingLevel; + float finalImpulse = upImpulse*Max(damp, 0.0f); + impulse->z += finalImpulse; + if(!bNoTurnForce) + veh->ApplyTurnForce(finalImpulse*waterNormal, Multiply3x3(m_matrix, waterLevel)); + } + i += 3; + } + ix++; + } + + m_volumeUnderWater *= volDiv; + + *point = Multiply3x3(m_matrix, m_impulsePoint); + return m_isBoat || m_haveVolume; + +} + +void +cBuoyancy::PreCalcSetup(CPhysical *phys, float buoyancy) +{ + CColModel *colModel; + + m_isBoat = phys->IsVehicle() && ((CVehicle*)phys)->IsBoat(); + colModel = phys->GetColModel(); + m_dimMin = colModel->boundingBox.min; + m_dimMax = colModel->boundingBox.max; + + if(m_isBoat){ + switch(phys->GetModelIndex()){ + case MI_PREDATOR: + default: + m_dimMax.y *= 1.05f; + m_dimMin.y *= 0.9f; + break; + case MI_SPEEDER: + m_dimMax.y *= 1.25f; + m_dimMin.y *= 0.83f; + break; + case MI_REEFER: + m_dimMin.y *= 0.9f; + break; + case MI_RIO: + m_dimMax.y *= 0.9f; + m_dimMin.y *= 0.9f; + m_dimMax.z += 0.25f; + m_dimMin.z -= 0.2f; + break; + case MI_SQUALO: + m_dimMax.y *= 0.9f; + m_dimMin.y *= 0.9f; + break; + case MI_TROPIC: + m_dimMax.y *= 1.3f; + m_dimMin.y *= 0.82f; + m_dimMin.z -= 0.2f; + break; + case MI_SKIMMER: + m_dimMin.y = -m_dimMax.y; + m_dimMax.y *= 1.2f; + break; + case MI_COASTG: + m_dimMax.y *= 1.1f; + m_dimMin.y *= 0.9f; + m_dimMin.z -= 0.3f; + break; + case MI_DINGHY: + m_dimMax.y *= 1.3f; + m_dimMin.y *= 0.9f; + m_dimMin.z -= 0.2f; + break; + case MI_MARQUIS: + m_dimMax.y *= 1.3f; + m_dimMin.y *= 0.9f; + break; + case MI_JETMAX: + m_dimMin.y *= 0.9f; + break; + } + } + + m_step = (m_dimMax - m_dimMin)/m_numSteps; + + if(m_step.z > m_step.x && m_step.z > m_step.y){ + m_stepRatio.x = m_step.x/m_step.z; + m_stepRatio.y = m_step.y/m_step.z; + m_stepRatio.z = 1.0f; + }else if(m_step.y > m_step.x && m_step.y > m_step.z){ + m_stepRatio.x = m_step.x/m_step.y; + m_stepRatio.y = 1.0f; + m_stepRatio.z = m_step.z/m_step.y; + }else{ + m_stepRatio.x = 1.0f; + m_stepRatio.y = m_step.y/m_step.x; + m_stepRatio.z = m_step.z/m_step.x; + } + + m_haveVolume = false; + m_numPartialVolumes = 1.0f; + m_volumeUnderWater = 0.0f; + m_impulsePoint = CVector(0.0f, 0.0f, 0.0f); + m_position = phys->GetPosition(); + m_positionZ = CVector(0.0f, 0.0f, m_position.z); + m_buoyancy = buoyancy; + m_waterlevel += m_waterLevelInc; +} + +void +cBuoyancy::SimpleCalcBuoyancy(void) +{ + float x, y; + tWaterLevel waterPosition; + + // Floater is divided into 3x3 parts. Process and sum each of them + for(x = m_dimMin.x; x <= m_dimMax.x; x += m_step.x){ + for(y = m_dimMin.y; y <= m_dimMax.y; y += m_step.y){ + CVector waterLevel(x, y, 0.0f); + FindWaterLevel(m_positionZ, &waterLevel, &waterPosition); + fVolMultiplier = 1.0f; + if(waterPosition != FLOATER_ABOVE_WATER) + SimpleSumBuoyancyData(waterLevel, waterPosition); + } + } + + m_volumeUnderWater /= (m_dimMax.z - m_dimMin.z)*sq(m_numSteps+1.0f); +} + +float +cBuoyancy::SimpleSumBuoyancyData(CVector &waterLevel, tWaterLevel waterPosition) +{ + static float fThisVolume; + static CVector AverageOfWaterLevel; + static float fFraction; + static float fRemainingSlice; + + float submerged = Abs(waterLevel.z - m_dimMin.z); + // subtract empty space from submerged volume + fThisVolume = submerged - (1.0f - fVolMultiplier); + if(fThisVolume < 0.0f) + return 0.0f; + + if(m_isBoat){ + fThisVolume *= fVolMultiplier; + fThisVolume = sq(fThisVolume); + } + + m_volumeUnderWater += fThisVolume; + + AverageOfWaterLevel.x = waterLevel.x * m_stepRatio.x; + AverageOfWaterLevel.y = waterLevel.y * m_stepRatio.y; + AverageOfWaterLevel.z = (waterLevel.z+m_dimMin.z)/2.0f * m_stepRatio.z; + + if(m_flipAverage) + AverageOfWaterLevel = -AverageOfWaterLevel; + + fFraction = 1.0f/m_numPartialVolumes; + fRemainingSlice = 1.0f - fFraction; + m_impulsePoint = m_impulsePoint*fRemainingSlice + AverageOfWaterLevel*fThisVolume*fFraction; + m_numPartialVolumes += 1.0f; + m_haveVolume = true; + return fThisVolume; +} + +void +cBuoyancy::FindWaterLevel(const CVector &zpos, CVector *waterLevel, tWaterLevel *waterPosition) +{ + *waterPosition = FLOATER_IN_WATER; + // waterLevel is a local x,y point + // m_position is the global position of our floater + // zpos is the global z coordinate of our floater + CVector xWaterLevel = Multiply3x3(m_matrix, *waterLevel); + CWaterLevel::GetWaterLevel(xWaterLevel.x + m_position.x, xWaterLevel.y + m_position.y, m_position.z, + &waterLevel->z, true); + waterLevel->z -= xWaterLevel.z + zpos.z; // make local + if(waterLevel->z > m_dimMax.z){ + waterLevel->z = m_dimMax.z; + *waterPosition = FLOATER_UNDER_WATER; + }else if(waterLevel->z < m_dimMin.z){ + waterLevel->z = m_dimMin.z; + *waterPosition = FLOATER_ABOVE_WATER; + } +} + +// Same as above but also get normal +void +cBuoyancy::FindWaterLevelNorm(const CVector &zpos, CVector *waterLevel, tWaterLevel *waterPosition, CVector *normal) +{ + *waterPosition = FLOATER_IN_WATER; + CVector xWaterLevel = Multiply3x3(m_matrix, *waterLevel); + CWaterLevel::GetWaterLevel(xWaterLevel.x + m_position.x, xWaterLevel.y + m_position.y, m_position.z, + &waterLevel->z, true); + waterLevel->z -= xWaterLevel.z + zpos.z; // make local + if(waterLevel->z >= m_dimMin.z) + *normal = CWaterLevel::GetWaterNormal(xWaterLevel.x + m_position.x, xWaterLevel.y + m_position.y); + if(waterLevel->z > m_dimMax.z){ + waterLevel->z = m_dimMax.z; + *waterPosition = FLOATER_UNDER_WATER; + }else if(waterLevel->z < m_dimMin.z){ + waterLevel->z = m_dimMin.z; + *waterPosition = FLOATER_ABOVE_WATER; + } +} + +bool +cBuoyancy::CalcBuoyancyForce(CPhysical *phys, CVector *point, CVector *impulse) +{ + if(!m_haveVolume) + return false; + + *point = Multiply3x3(m_matrix, m_impulsePoint); + *impulse = CVector(0.0f, 0.0f, m_volumeUnderWater*m_buoyancy*CTimer::GetTimeStep()); + return true; +} diff --git a/src/miami/vehicles/Floater.h b/src/miami/vehicles/Floater.h new file mode 100644 index 00000000..91ab70ae --- /dev/null +++ b/src/miami/vehicles/Floater.h @@ -0,0 +1,47 @@ +#pragma once + +class CPhysical; + +enum tWaterLevel +{ + FLOATER_ABOVE_WATER, + FLOATER_IN_WATER, + FLOATER_UNDER_WATER, +}; + +class cBuoyancy +{ +public: + CVector m_position; + CMatrix m_matrix; + int m_field_54; + CVector m_positionZ; + float m_waterlevel; + float m_waterLevelInc; + float m_buoyancy; + CVector m_dimMax; + CVector m_dimMin; + float m_numPartialVolumes; + int m_field_8C; + int m_field_90; + int m_field_94; + bool m_haveVolume; + CVector m_step; + CVector m_stepRatio; + float m_numSteps; + bool m_flipAverage; + char m_field_B9; + bool m_isBoat; + float m_volumeUnderWater; + CVector m_impulsePoint; + + bool ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *point, CVector *impulse); + bool ProcessBuoyancyBoat(CVehicle *phys, float buoyancy, CVector *point, CVector *impulse, bool bNoTurnForce); + void PreCalcSetup(CPhysical *phys, float buoyancy); + void SimpleCalcBuoyancy(void); + float SimpleSumBuoyancyData(CVector &waterLevel, tWaterLevel waterPosition); + void FindWaterLevel(const CVector &zpos, CVector *waterLevel, tWaterLevel *waterPosition); + void FindWaterLevelNorm(const CVector &zpos, CVector *waterLevel, tWaterLevel *waterPosition, CVector *normal); + bool CalcBuoyancyForce(CPhysical *phys, CVector *impulse, CVector *point); +}; +extern cBuoyancy mod_Buoyancy; diff --git a/src/miami/vehicles/HandlingMgr.cpp b/src/miami/vehicles/HandlingMgr.cpp new file mode 100644 index 00000000..8438c5c9 --- /dev/null +++ b/src/miami/vehicles/HandlingMgr.cpp @@ -0,0 +1,434 @@ +#include "common.h" + +#include "main.h" +#include "FileMgr.h" +#include "Physical.h" +#include "HandlingMgr.h" + +cHandlingDataMgr mod_HandlingManager; + +const char *HandlingFilename = "HANDLING.CFG"; + +const char VehicleNames[NUMHANDLINGS][14] = { + "LANDSTAL", + "IDAHO", + "STINGER", + "LINERUN", + "PEREN", + "SENTINEL", + "PATRIOT", + "FIRETRUK", + "TRASH", + "STRETCH", + "MANANA", + "INFERNUS", + "PONY", + "MULE", + "CHEETAH", + "AMBULAN", + "FBICAR", + "MOONBEAM", + "ESPERANT", + "TAXI", + "KURUMA", + "BOBCAT", + "MRWHOOP", + "BFINJECT", + "POLICE", + "ENFORCER", + "SECURICA", + "BANSHEE", + "BUS", + "RHINO", + "BARRACKS", + "TRAIN", + "HELI", + "DODO", + "COACH", + "CABBIE", + "STALLION", + "RUMPO", + "RCBANDIT", + "MAFIA", + "AIRTRAIN", + "DEADDODO", + "FLATBED", + "YANKEE", + "GOLFCART", + "VOODOO", + "WASHING", + "CUBAN", + "ROMERO", + "PACKER", + "ADMIRAL", + "GANGBUR", + "ZEBRA", + "TOPFUN", + "GLENDALE", + "OCEANIC", + "HERMES", + "SABRE1", + "SABRETUR", + "PHEONIX", + "WALTON", + "REGINA", + "COMET", + "DELUXO", + "BURRITO", + "SPAND", + "BAGGAGE", + "KAUFMAN", + "RANCHER", + "FBIRANCH", + "VIRGO", + "GREENWOO", + "HOTRING", + "SANDKING", + "BLISTAC", + "BOXVILLE", + "BENSON", + "DESPERAD", + "LOVEFIST", + "BLOODRA", + "BLOODRB", + "BIKE", + "MOPED", + "DIRTBIKE", + "ANGEL", + "FREEWAY", + "PREDATOR", + "SPEEDER", + "REEFER", + "RIO", + "SQUALO", + "TROPIC", + "COASTGRD", + "DINGHY", + "MARQUIS", + "CUPBOAT", + "SEAPLANE", + "SPARROW", + "SEASPAR", + "MAVERICK", + "COASTMAV", + "POLMAV", + "HUNTER", + "RCBARON", + "RCGOBLIN", + "RCCOPTER" +}; + +cHandlingDataMgr::cHandlingDataMgr(void) +{ + memset(this, 0, sizeof(*this)); +} + +void +cHandlingDataMgr::Initialise(void) +{ + LoadHandlingData(); + field_0 = 0.1f; + fWheelFriction = 0.9f; + field_8 = 1.0f; + field_C = 0.8f; + field_10 = 0.98f; +} + +void +cHandlingDataMgr::LoadHandlingData(void) +{ + char *start, *end; + char line[201]; // weird value + char delim[4]; // not sure + char *word; + int field, handlingId; + int keepGoing; + tHandlingData *handling; + tFlyingHandlingData *flyingHandling; + tBoatHandlingData *boatHandling; + tBikeHandlingData *bikeHandling; + + CFileMgr::SetDir("DATA"); + CFileMgr::LoadFile(HandlingFilename, work_buff, sizeof(work_buff), "r"); + CFileMgr::SetDir(""); + + start = (char*)work_buff; + end = start+1; + handling = nil; + flyingHandling = nil; + boatHandling = nil; + bikeHandling = nil; + keepGoing = 1; + + while(keepGoing){ + // find end of line + while(*end != '\n') end++; + + // get line + strncpy(line, start, end - start); + line[end - start] = '\0'; + start = end+1; + end = start+1; + + // yeah, this is kinda crappy + if(strcmp(line, ";the end") == 0) + keepGoing = 0; + else if(line[0] != ';'){ + if(line[0] == '!'){ + // Bike data + field = 0; + strcpy(delim, " \t"); + // FIX: game seems to use a do-while loop here + for(word = strtok(line, delim); word; word = strtok(nil, delim)){ + switch(field){ + case 0: break; + case 1: + handlingId = FindExactWord(word, (const char*)VehicleNames, 14, NUMHANDLINGS); + assert(handlingId >= 0 && handlingId < NUMHANDLINGS); + bikeHandling = GetBikePointer(handlingId); + bikeHandling->nIdentifier = (tVehicleType)handlingId; + break; + case 2: bikeHandling->fLeanFwdCOM = atof(word); break; + case 3: bikeHandling->fLeanFwdForce = atof(word); break; + case 4: bikeHandling->fLeanBakCOM = atof(word); break; + case 5: bikeHandling->fLeanBackForce = atof(word); break; + case 6: bikeHandling->fMaxLean = atof(word); break; + case 7: bikeHandling->fFullAnimLean = atof(word); break; + case 8: bikeHandling->fDesLean = atof(word); break; + case 9: bikeHandling->fSpeedSteer = atof(word); break; + case 10: bikeHandling->fSlipSteer = atof(word); break; + case 11: bikeHandling->fNoPlayerCOMz = atof(word); break; + case 12: bikeHandling->fWheelieAng = atof(word); break; + case 13: bikeHandling->fStoppieAng = atof(word); break; + case 14: bikeHandling->fWheelieSteer = atof(word); break; + case 15: bikeHandling->fWheelieStabMult = atof(word); break; + case 16: bikeHandling->fStoppieStabMult = atof(word); break; + } + field++; + } + ConvertBikeDataToGameUnits(bikeHandling); + }else if(line[0] == '$'){ + // Flying data + field = 0; + strcpy(delim, " \t"); + // FIX: game seems to use a do-while loop here + for(word = strtok(line, delim); word; word = strtok(nil, delim)){ + switch(field){ + case 0: break; + case 1: + handlingId = FindExactWord(word, (const char*)VehicleNames, 14, NUMHANDLINGS); + assert(handlingId >= 0 && handlingId < NUMHANDLINGS); + flyingHandling = GetFlyingPointer(handlingId); + flyingHandling->nIdentifier = (tVehicleType)handlingId; + break; + case 2: flyingHandling->fThrust = atof(word); break; + case 3: flyingHandling->fThrustFallOff = atof(word); break; + case 4: flyingHandling->fYaw = atof(word); break; + case 5: flyingHandling->fYawStab = atof(word); break; + case 6: flyingHandling->fSideSlip = atof(word); break; + case 7: flyingHandling->fRoll = atof(word); break; + case 8: flyingHandling->fRollStab = atof(word); break; + case 9: flyingHandling->fPitch = atof(word); break; + case 10: flyingHandling->fPitchStab = atof(word); break; + case 11: flyingHandling->fFormLift = atof(word); break; + case 12: flyingHandling->fAttackLift = atof(word); break; + case 13: flyingHandling->fMoveRes = atof(word); break; + case 14: flyingHandling->vecTurnRes.x = atof(word); break; + case 15: flyingHandling->vecTurnRes.y = atof(word); break; + case 16: flyingHandling->vecTurnRes.z = atof(word); break; + case 17: flyingHandling->vecSpeedRes.x = atof(word); break; + case 18: flyingHandling->vecSpeedRes.y = atof(word); break; + case 19: flyingHandling->vecSpeedRes.z = atof(word); break; + } + field++; + } + }else if(line[0] == '%'){ + // Boat data + field = 0; + strcpy(delim, " \t"); + // FIX: game seems to use a do-while loop here + for(word = strtok(line, delim); word; word = strtok(nil, delim)){ + switch(field){ + case 0: break; + case 1: + handlingId = FindExactWord(word, (const char*)VehicleNames, 14, NUMHANDLINGS); + assert(handlingId >= 0 && handlingId < NUMHANDLINGS); + boatHandling = GetBoatPointer(handlingId); + boatHandling->nIdentifier = (tVehicleType)handlingId; + break; + case 2: boatHandling->fThrustY = atof(word); break; + case 3: boatHandling->fThrustZ = atof(word); break; + case 4: boatHandling->fThrustAppZ = atof(word); break; + case 5: boatHandling->fAqPlaneForce = atof(word); break; + case 6: boatHandling->fAqPlaneLimit = atof(word); break; + case 7: boatHandling->fAqPlaneOffset = atof(word); break; + case 8: boatHandling->fWaveAudioMult = atof(word); break; + case 9: boatHandling->vecMoveRes.x = atof(word); break; + case 10: boatHandling->vecMoveRes.y = atof(word); break; + case 11: boatHandling->vecMoveRes.z = atof(word); break; + case 12: boatHandling->vecTurnRes.x = atof(word); break; + case 13: boatHandling->vecTurnRes.y = atof(word); break; + case 14: boatHandling->vecTurnRes.z = atof(word); break; + case 15: boatHandling->fLook_L_R_BehindCamHeight = atof(word); break; + } + field++; + } + }else{ + field = 0; + strcpy(delim, " \t"); + // FIX: game seems to use a do-while loop here + for(word = strtok(line, delim); word; word = strtok(nil, delim)){ + switch(field){ + case 0: + handlingId = FindExactWord(word, (const char*)VehicleNames, 14, NUMHANDLINGS); + assert(handlingId >= 0 && handlingId < NUMHANDLINGS); + handling = &HandlingData[handlingId]; + handling->nIdentifier = (tVehicleType)handlingId; + break; + case 1: handling->fMass = atof(word); break; + case 2: handling->Dimension.x = atof(word); break; + case 3: handling->Dimension.y = atof(word); break; + case 4: handling->Dimension.z = atof(word); break; + case 5: handling->CentreOfMass.x = atof(word); break; + case 6: handling->CentreOfMass.y = atof(word); break; + case 7: handling->CentreOfMass.z = atof(word); break; + case 8: handling->nPercentSubmerged = atoi(word); break; + case 9: handling->fTractionMultiplier = atof(word); break; + case 10: handling->fTractionLoss = atof(word); break; + case 11: handling->fTractionBias = atof(word); break; + case 12: handling->Transmission.nNumberOfGears = atoi(word); break; + case 13: handling->Transmission.fMaxVelocity = atof(word); break; + case 14: handling->Transmission.fEngineAcceleration = atof(word) * 0.4; break; + case 15: handling->Transmission.nDriveType = word[0]; break; + case 16: handling->Transmission.nEngineType = word[0]; break; + case 17: handling->fBrakeDeceleration = atof(word); break; + case 18: handling->fBrakeBias = atof(word); break; + case 19: handling->bABS = !!atoi(word); break; + case 20: handling->fSteeringLock = atof(word); break; + case 21: handling->fSuspensionForceLevel = atof(word); break; + case 22: handling->fSuspensionDampingLevel = atof(word); break; + case 23: handling->fSeatOffsetDistance = atof(word); break; + case 24: handling->fCollisionDamageMultiplier = atof(word); break; + case 25: handling->nMonetaryValue = atoi(word); break; + case 26: handling->fSuspensionUpperLimit = atof(word); break; + case 27: handling->fSuspensionLowerLimit = atof(word); break; + case 28: handling->fSuspensionBias = atof(word); break; + case 29: handling->fSuspensionAntidiveMultiplier = atof(word); break; + case 30: + sscanf(word, "%x", &handling->Flags); + handling->Transmission.Flags = handling->Flags; + break; + case 31: handling->FrontLights = atoi(word); break; + case 32: handling->RearLights = atoi(word); break; + } + field++; + } + ConvertDataToGameUnits(handling); + } + } + } +} + +int +cHandlingDataMgr::FindExactWord(const char *word, const char *words, int wordLen, int numWords) +{ + int i; + + for(i = 0; i < numWords; i++){ + // BUG: the game does something really stupid here, it's fixed here + if(strncmp(word, words, wordLen) == 0) + return i; + words += wordLen; + } + return numWords; +} + + +void +cHandlingDataMgr::ConvertDataToGameUnits(tHandlingData *handling) +{ + // convert distance to m, time to 1/50s + float velocity, a, b; + + handling->Transmission.fEngineAcceleration *= 1.0f/(50.0f*50.0f); + handling->Transmission.fMaxVelocity *= 1000.0f/(60.0f*60.0f * 50.0f); + handling->fBrakeDeceleration *= 1.0f/(50.0f*50.0f); + handling->fTurnMass = (sq(handling->Dimension.x) + sq(handling->Dimension.y)) * handling->fMass / 12.0f; + if(handling->fTurnMass < 10.0f) + handling->fTurnMass *= 5.0f; + handling->fInvMass = 1.0f/handling->fMass; + handling->fCollisionDamageMultiplier *= 2000.0f/handling->fMass; + handling->fBuoyancy = 100.0f/handling->nPercentSubmerged * GRAVITY*handling->fMass; + + // Don't quite understand this. What seems to be going on is that + // we calculate a drag (air resistance) deceleration for a given velocity and + // find the intersection between that and the max engine acceleration. + // at that point the car cannot accelerate any further and we've found the max velocity. + a = 0.0f; + b = 100.0f; + velocity = handling->Transmission.fMaxVelocity; + while(a < b && velocity > 0.0f){ + velocity -= 0.01f; + // what's the 1/6? + a = handling->Transmission.fEngineAcceleration/6.0f; + // no density or drag coefficient here... + float a_drag = 0.5f*SQR(velocity) * handling->Dimension.x*handling->Dimension.z / handling->fMass; + // can't make sense of this... maybe v - v/(drag + 1) ? but that doesn't make so much sense either + b = -velocity * (1.0f/(a_drag + 1.0f) - 1.0f); + } + + if(handling->nIdentifier == HANDLING_RCBANDIT){ + handling->Transmission.fMaxCruiseVelocity = handling->Transmission.fMaxVelocity; + handling->Transmission.fMaxReverseVelocity = -handling->Transmission.fMaxVelocity; + }else if(handling->nIdentifier >= HANDLING_BIKE && handling->nIdentifier <= HANDLING_FREEWAY){ + handling->Transmission.fMaxCruiseVelocity = velocity; + handling->Transmission.fMaxVelocity = velocity * 1.2f; + handling->Transmission.fMaxReverseVelocity = -0.05f; + }else{ + handling->Transmission.fMaxCruiseVelocity = velocity; + handling->Transmission.fMaxVelocity = velocity * 1.2f; + handling->Transmission.fMaxReverseVelocity = -0.2f; + } + + if(handling->Transmission.nDriveType == '4') + handling->Transmission.fEngineAcceleration /= 4.0f; + else + handling->Transmission.fEngineAcceleration /= 2.0f; + + handling->Transmission.InitGearRatios(); +} + +void +cHandlingDataMgr::ConvertBikeDataToGameUnits(tBikeHandlingData *handling) +{ + handling->fMaxLean = Sin(DEGTORAD(handling->fMaxLean)); + handling->fFullAnimLean = DEGTORAD(handling->fFullAnimLean); + handling->fWheelieAng = Sin(DEGTORAD(handling->fWheelieAng)); + handling->fStoppieAng = Sin(DEGTORAD(handling->fStoppieAng)); +} + +int32 +cHandlingDataMgr::GetHandlingId(const char *name) +{ + int i; + for(i = 0; i < NUMHANDLINGS; i++) + if(strncmp(VehicleNames[i], name, 14) == 0) + break; + return i; +} + +tFlyingHandlingData* +cHandlingDataMgr::GetFlyingPointer(uint8 id) +{ + if(id >= HANDLING_SEAPLANE && id <= HANDLING_RCCOPTER) + return &FlyingHandlingData[id-HANDLING_SEAPLANE]; + return &FlyingHandlingData[0]; +} + +tBoatHandlingData* +cHandlingDataMgr::GetBoatPointer(uint8 id) +{ + if(id >= HANDLING_PREDATOR && id <= HANDLING_SEAPLANE) + return &BoatHandlingData[id-HANDLING_PREDATOR]; + return &BoatHandlingData[0]; +} diff --git a/src/miami/vehicles/HandlingMgr.h b/src/miami/vehicles/HandlingMgr.h new file mode 100644 index 00000000..8d290f7d --- /dev/null +++ b/src/miami/vehicles/HandlingMgr.h @@ -0,0 +1,279 @@ +#pragma once + +#include "Transmission.h" + +enum tVehicleType +{ + HANDLING_LANDSTAL, + HANDLING_IDAHO, + HANDLING_STINGER, + HANDLING_LINERUN, + HANDLING_PEREN, + HANDLING_SENTINEL, + HANDLING_PATRIOT, + HANDLING_FIRETRUK, + HANDLING_TRASH, + HANDLING_STRETCH, + HANDLING_MANANA, + HANDLING_INFERNUS, + HANDLING_PONY, + HANDLING_MULE, + HANDLING_CHEETAH, + HANDLING_AMBULAN, + HANDLING_FBICAR, + HANDLING_MOONBEAM, + HANDLING_ESPERANT, + HANDLING_TAXI, + HANDLING_KURUMA, + HANDLING_BOBCAT, + HANDLING_MRWHOOP, + HANDLING_BFINJECT, + HANDLING_POLICE, + HANDLING_ENFORCER, + HANDLING_SECURICA, + HANDLING_BANSHEE, + HANDLING_BUS, + HANDLING_RHINO, + HANDLING_BARRACKS, + HANDLING_TRAIN, + HANDLING_HELI, + HANDLING_DODO, + HANDLING_COACH, + HANDLING_CABBIE, + HANDLING_STALLION, + HANDLING_RUMPO, + HANDLING_RCBANDIT, + HANDLING_MAFIA, + HANDLING_AIRTRAIN, + HANDLING_DEADDODO, + HANDLING_FLATBED, + HANDLING_YANKEE, + HANDLING_GOLFCART, + HANDLING_VOODOO, + HANDLING_WASHING, + HANDLING_CUBAN, + HANDLING_ROMERO, + HANDLING_PACKER, + HANDLING_ADMIRAL, + HANDLING_GANGBUR, + HANDLING_ZEBRA, + HANDLING_TOPFUN, + HANDLING_GLENDALE, + HANDLING_OCEANIC, + HANDLING_HERMES, + HANDLING_SABRE1, + HANDLING_SABRETUR, + HANDLING_PHEONIX, + HANDLING_WALTON, + HANDLING_REGINA, + HANDLING_COMET, + HANDLING_DELUXO, + HANDLING_BURRITO, + HANDLING_SPAND, + HANDLING_BAGGAGE, + HANDLING_KAUFMAN, + HANDLING_RANCHER, + HANDLING_FBIRANCH, + HANDLING_VIRGO, + HANDLING_GREENWOO, + HANDLING_HOTRING, + HANDLING_SANDKING, + HANDLING_BLISTAC, + HANDLING_BOXVILLE, + HANDLING_BENSON, + HANDLING_DESPERAD, + HANDLING_LOVEFIST, + HANDLING_BLOODRA, + HANDLING_BLOODRB, + + HANDLING_BIKE, + HANDLING_MOPED, + HANDLING_DIRTBIKE, + HANDLING_ANGEL, + HANDLING_FREEWAY, + + HANDLING_PREDATOR, + HANDLING_SPEEDER, + HANDLING_REEFER, + HANDLING_RIO, + HANDLING_SQUALO, + HANDLING_TROPIC, + HANDLING_COASTGRD, + HANDLING_DINGHY, + HANDLING_MARQUIS, + HANDLING_CUPBOAT, + HANDLING_SEAPLANE, // both boat and plane! + HANDLING_SPARROW, + HANDLING_SEASPAR, + HANDLING_MAVERICK, + HANDLING_COASTMAV, + HANDLING_POLMAV, + HANDLING_HUNTER, + HANDLING_RCBARON, + HANDLING_RCGOBLIN, + HANDLING_RCCOPTER, + + NUMHANDLINGS, + + NUMBIKEHANDLINGS = HANDLING_FREEWAY+1 - HANDLING_BIKE, + NUMFLYINGHANDLINGS = HANDLING_RCCOPTER+1 - HANDLING_SEAPLANE, + NUMBOATHANDLINGS = HANDLING_SEAPLANE+1 - HANDLING_PREDATOR, +}; + +enum tField // most likely a handling field enum, never used so :shrug: +{ + +}; + +enum +{ + HANDLING_1G_BOOST = 1, + HANDLING_2G_BOOST = 2, + HANDLING_REV_BONNET = 4, + HANDLING_HANGING_BOOT = 8, + HANDLING_NO_DOORS = 0x10, + HANDLING_IS_VAN = 0x20, + HANDLING_IS_BUS = 0x40, + HANDLING_IS_LOW = 0x80, + HANDLING_DBL_EXHAUST = 0x100, + HANDLING_TAILGATE_BOOT = 0x200, + HANDLING_NOSWING_BOOT = 0x400, + HANDLING_NONPLAYER_STABILISER = 0x800, + HANDLING_NEUTRALHANDLING = 0x1000, + HANDLING_HAS_NO_ROOF = 0x2000, + HANDLING_IS_BIG = 0x4000, + HANDLING_HALOGEN_LIGHTS = 0x8000, + HANDLING_IS_BIKE = 0x10000, + HANDLING_IS_HELI = 0x20000, + HANDLING_IS_PLANE = 0x40000, + HANDLING_IS_BOAT = 0x80000, + HANDLING_NO_EXHAUST = 0x100000, + HANDLING_REARWHEEL_1ST = 0x200000, + HANDLING_HANDBRAKE_TYRE = 0x400000, + HANDLING_SIT_IN_BOAT = 0x800000, + HANDLING_FAT_REARW = 0x1000000, + HANDLING_NARROW_FRONTW = 0x2000000, + HANDLING_GOOD_INSAND = 0x4000000, + HANDLING_UNKNOWN = 0x8000000, // something for helis and planes +}; + +struct tHandlingData +{ + tVehicleType nIdentifier; + float fMass; + float fInvMass; + float fTurnMass; + CVector Dimension; + CVector CentreOfMass; + int8 nPercentSubmerged; + float fBuoyancy; + float fTractionMultiplier; + cTransmission Transmission; + float fBrakeDeceleration; + float fBrakeBias; + int8 bABS; + float fSteeringLock; + float fTractionLoss; + float fTractionBias; + float fUnused; + float fSuspensionForceLevel; + float fSuspensionDampingLevel; + float fSuspensionUpperLimit; + float fSuspensionLowerLimit; + float fSuspensionBias; + float fSuspensionAntidiveMultiplier; + float fCollisionDamageMultiplier; + uint32 Flags; + float fSeatOffsetDistance; + int32 nMonetaryValue; + int8 FrontLights; + int8 RearLights; +}; + +struct tBikeHandlingData +{ + tVehicleType nIdentifier; + float fLeanFwdCOM; + float fLeanFwdForce; + float fLeanBakCOM; + float fLeanBackForce; + float fMaxLean; + float fFullAnimLean; + float fDesLean; + float fSpeedSteer; + float fSlipSteer; + float fNoPlayerCOMz; + float fWheelieAng; + float fStoppieAng; + float fWheelieSteer; + float fWheelieStabMult; + float fStoppieStabMult; +}; + +struct tBoatHandlingData +{ + tVehicleType nIdentifier; + float fThrustY; + float fThrustZ; + float fThrustAppZ; + float fAqPlaneForce; + float fAqPlaneLimit; + float fAqPlaneOffset; + float fWaveAudioMult; + float fLook_L_R_BehindCamHeight; + CVector vecMoveRes; + CVector vecTurnRes; +}; + +struct tFlyingHandlingData +{ + tVehicleType nIdentifier; + float fThrust; + float fThrustFallOff; + float fYaw; + float fYawStab; + float fSideSlip; + float fRoll; + float fRollStab; + float fPitch; + float fPitchStab; + float fFormLift; + float fAttackLift; + float fMoveRes; + CVector vecTurnRes; + CVector vecSpeedRes; +}; + +class CVehicle; + +class cHandlingDataMgr +{ + float field_0; // unused it seems +public: + float fWheelFriction; // wheel related +private: + float field_8; // + float field_C; // unused it seems + float field_10; // + tHandlingData HandlingData[NUMHANDLINGS]; + tBikeHandlingData BikeHandlingData[NUMBIKEHANDLINGS]; + tFlyingHandlingData FlyingHandlingData[NUMFLYINGHANDLINGS]; + tBoatHandlingData BoatHandlingData[NUMBOATHANDLINGS]; + +public: + cHandlingDataMgr(void); + void Initialise(void); + void LoadHandlingData(void); + int FindExactWord(const char *word, const char *words, int wordLen, int numWords); + void ConvertDataToWorldUnits(tHandlingData *handling); + void ConvertDataToGameUnits(tHandlingData *handling); + void ConvertBikeDataToGameUnits(tBikeHandlingData *handling); + int32 GetHandlingId(const char *name); + tHandlingData *GetHandlingData(tVehicleType id) { return &HandlingData[id]; } + tBikeHandlingData *GetBikePointer(uint8 id) { return &BikeHandlingData[id-HANDLING_BIKE]; } + tFlyingHandlingData *GetFlyingPointer(uint8 id); + tBoatHandlingData *GetBoatPointer(uint8 id); + bool HasRearWheelDrive(tVehicleType id) { return HandlingData[id].Transmission.nDriveType != 'F'; } + bool HasFrontWheelDrive(tVehicleType id) { return HandlingData[id].Transmission.nDriveType != 'R'; } +}; +extern cHandlingDataMgr mod_HandlingManager; diff --git a/src/miami/vehicles/Heli.cpp b/src/miami/vehicles/Heli.cpp new file mode 100644 index 00000000..f51c8481 --- /dev/null +++ b/src/miami/vehicles/Heli.cpp @@ -0,0 +1,1049 @@ +#include "common.h" +#include "main.h" + +#include "General.h" +#include "Darkel.h" +#include "Stats.h" +#include "SurfaceTable.h" +#include "ModelIndices.h" +#include "Streaming.h" +#include "Camera.h" +#include "VisibilityPlugins.h" +#include "ZoneCull.h" +#include "Particle.h" +#include "Shadows.h" +#include "Coronas.h" +#include "Explosion.h" +#include "WindModifiers.h" +#include "Timecycle.h" +#include "TempColModels.h" +#include "World.h" +#include "WaterLevel.h" +#include "Population.h" +#include "PlayerPed.h" +#include "CopPed.h" +#include "Wanted.h" +#include "DMAudio.h" +#include "Object.h" +#include "HandlingMgr.h" +#include "Ropes.h" +#include "Heli.h" +#ifdef FIX_BUGS +#include "Replay.h" +#endif + +enum +{ + HELI_STATUS_HOVER, + HELI_STATUS_CHASE_PLAYER, + HELI_STATUS_FLY_AWAY, + HELI_STATUS_SHOT_DOWN, + HELI_STATUS_HOVER2, +}; + +CHeli *CHeli::pHelis[NUM_HELIS]; +int16 CHeli::NumRandomHelis; +uint32 CHeli::TestForNewRandomHelisTimer; +bool CHeli::CatalinaHeliOn; +bool CHeli::CatalinaHasBeenShotDown; +bool CHeli::ScriptHeliOn; + +CHeli::CHeli(int32 id, uint8 CreatedBy) + : CVehicle(CreatedBy) +{ + int i; + + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); + m_vehType = VEHICLE_TYPE_HELI; + pHandling = mod_HandlingManager.GetHandlingData((tVehicleType)mi->m_handlingId); + SetModelIndex(id); + m_heliStatus = HELI_STATUS_HOVER; + m_pathState = 0; + + m_fMass = 100000000.0f; + m_fTurnMass = 100000000.0f; + m_fAirResistance = 0.9994f; + m_fElasticity = 0.05f; + + m_nHeliId = 0; + m_fRotorRotation = 0.0f; + m_nBulletDamage = 0; + m_fAngularSpeed = 0.0f; + m_fRotation = 0.0f; + + m_numSwat = 4; + + m_nSearchLightTimer = CTimer::GetTimeInMilliseconds(); + for(i = 0; i < 6; i++){ + m_aSearchLightHistoryX[i] = 0.0f; + m_aSearchLightHistoryY[i] = 0.0f; + } + + for(i = 0; i < 8; i++) + m_fHeliDustZ[i] = -50.0f; + + m_nPoliceShoutTimer = CTimer::GetTimeInMilliseconds(); + SetStatus(STATUS_HELI); + m_bTestRight = true; + m_fTargetOffset = 0.0f; + m_fSearchLightX = m_fSearchLightY = 0.0f; + + m_aSwatState[0] = m_aSwatState[1] = m_aSwatState[2] = m_aSwatState[3] = 0; + + // BUG: not in game but gets initialized to CDCDCDCD in debug + m_nLastShotTime = 0; +} + +void +CHeli::SetModelIndex(uint32 id) +{ + int i; + + CVehicle::SetModelIndex(id); + for(i = 0; i < NUM_HELI_NODES; i++) + m_aHeliNodes[i] = nil; + CClumpModelInfo::FillFrameArray(GetClump(), m_aHeliNodes); +} + +static float CatalinaTargetX[7] = { -478.0, -677.0, -907.0, -1095.0, -1152.0, -1161.0, -1161.0 }; +static float CatalinaTargetY[7] = { 227.0, 206.0, 210.0, 242.0, 278.0, 341.0, 341.0 }; +static float CatalinaTargetZ[7] = { 77.0, 66.0, 60.0, 53.0, 51.0, 46.0, 30.0 }; +static float DamPathX[6] = { -1191.0, -1176.0, -1128.0, -1072.0, -1007.0, -971.0 }; +static float DamPathY[6] = { 350.0, 388.0, 429.0, 447.0, 449.0, 416.0 }; +static float DamPathZ[6] = { 42.0, 37.0, 28.0, 28.0, 31.0, 33.0 }; +static float ShortPathX[4] = { -974.0, -1036.0, -1112.0, -1173.0 }; +static float ShortPathY[4] = { 340.0, 312.0, 317.0, 294.0 }; +static float ShortPathZ[4] = { 41.0, 38.0, 32.0, 39.0 }; +static float LongPathX[7] = { -934.0, -905.0, -906.0, -1063.0, -1204.0, -1233.0, -1207.0 }; +static float LongPathY[7] = { 371.0, 362.0, 488.0, 548.0, 451.0, 346.0, 308.0 }; +static float LongPathZ[7] = { 57.0, 90.0, 105.0, 100.0, 81.0, 79.0, 70.0 }; + +static int PathPoint; + +void +CHeli::ProcessControl(void) +{ + int i; + + if(gbModelViewer) + return; + + CWindModifiers::RegisterOne(GetPosition(), 1); + + // Find target + CVector target(0.0f, 0.0f, 0.0f); + CVector2D vTargetDist; + if(m_heliType == HELI_TYPE_CATALINA && m_heliStatus != HELI_STATUS_SHOT_DOWN){ + switch(m_pathState){ + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + target.x = CatalinaTargetX[m_pathState]; + target.y = CatalinaTargetY[m_pathState]; + target.z = CatalinaTargetZ[m_pathState]; + if((target - GetPosition()).Magnitude() < 9.0f) + m_pathState++; + break; + case 6: + target.x = CatalinaTargetX[m_pathState]; + target.y = CatalinaTargetY[m_pathState]; + target.z = CatalinaTargetZ[m_pathState]; + if(GetPosition().z > 31.55f) + break; + m_pathState = 7; + GetMatrix().GetPosition().z = 31.55f; + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + break; + case 7: + GetMatrix().GetPosition().z = 31.55f; + target = GetPosition(); + break; + + + // Take off + case 8: + target.x = GetPosition().x; + target.y = GetPosition().y; + target.z = 74.0f; + if(GetPosition().z < 40.0f) + break; + PathPoint = 2; + m_pathState = 9; + break; + // Circle around dam + case 9: + target.x = DamPathX[PathPoint]; + target.y = DamPathY[PathPoint]; + target.z = DamPathZ[PathPoint]; + if((target - GetPosition()).Magnitude() < 9.0f){ + PathPoint++; + if(PathPoint >= 6){ + m_pathState = 10; + PathPoint = 0; + } + } + break; + case 10: + target.x = ShortPathX[PathPoint]; + target.y = ShortPathY[PathPoint]; + target.z = ShortPathZ[PathPoint]; + if((target - GetPosition()).Magnitude() < 9.0f){ + PathPoint++; + if(PathPoint >= 3){ + m_pathState = 9; + PathPoint = 1; + } + } + break; + // how do we get here? + case 11: + target.x = LongPathX[PathPoint]; + target.y = LongPathY[PathPoint]; + target.z = LongPathZ[PathPoint]; + if((target - GetPosition()).Magnitude() < 9.0f){ + PathPoint++; + if(PathPoint >= 7){ + m_pathState = 9; + PathPoint = 0; + } + } + break; + + + // Fly away + case 12: + target.x = GetPosition().x; + target.y = GetPosition().y; + target.z = 200.0f; + break; + } + + vTargetDist = target - GetPosition(); + m_fTargetZ = target.z; + if(m_pathState == 6){ + GetMatrix().GetPosition().x = GetMatrix().GetPosition().x*0.99f + target.x*0.01f; + GetMatrix().GetPosition().y = GetMatrix().GetPosition().y*0.99f + target.y*0.01f; + } + }else{ + vTargetDist = FindPlayerCoors() - GetPosition(); + m_fTargetZ = FindPlayerCoors().z; + + // Heli flies away to (0, 0) + if(m_heliStatus == HELI_STATUS_FLY_AWAY && GetPosition().z > 20.0f){ + vTargetDist.x = 0.0f - GetPosition().x; + vTargetDist.y = 0.0f - GetPosition().y; + } + + float groundZ; + switch(m_heliStatus){ + case HELI_STATUS_HOVER: + groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, 1000.0f, nil); + m_fTargetZ = Max(groundZ, m_fTargetZ) + 8.0f; + break; + case HELI_STATUS_SHOT_DOWN: + groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, 1000.0f, nil); + m_fTargetZ = Max(groundZ, m_fTargetZ) + 8.0f + m_fTargetOffset; + break; + case HELI_STATUS_HOVER2: + groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, 1000.0f, nil); + m_fTargetZ = Max(groundZ, m_fTargetZ) + 8.0f + m_fTargetOffset; + break; + default: + groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, 1000.0f, nil); + m_fTargetZ = Max(groundZ, m_fTargetZ) + 12.0f; + break; + } + + // Move up if too low + if(GetPosition().z - 2.0f < groundZ && m_heliStatus != HELI_STATUS_SHOT_DOWN) + m_vecMoveSpeed.z += CTimer::GetTimeStep()*0.01f; + m_vecMoveSpeed.z = Clamp(m_vecMoveSpeed.z, -0.3f, 0.3f); + } + + float fTargetDist = vTargetDist.Magnitude(); + + switch(m_heliStatus){ + case HELI_STATUS_HOVER: + case HELI_STATUS_HOVER2:{ + float targetHeight; + if(m_heliType == HELI_TYPE_CATALINA) + targetHeight = 8.0f; + else + targetHeight = 40.0f - m_nHeliId*10.0f; + if(fTargetDist > targetHeight) + m_heliStatus = HELI_STATUS_CHASE_PLAYER; + } + if(m_numSwat) + SendDownSwat(); + break; + case HELI_STATUS_CHASE_PLAYER:{ + float targetHeight; + if(m_heliType == HELI_TYPE_CATALINA) + targetHeight = 4.0f; + else + targetHeight = 30.0f - m_nHeliId*7.5f; + if(fTargetDist < 1.0f || + fTargetDist < targetHeight && CWorld::GetIsLineOfSightClear(GetPosition(), FindPlayerCoors(), true, false, false, false, false, false)) + m_heliStatus = HELI_STATUS_HOVER; + } + break; + } + + // Find xy speed + float speed; + if(fTargetDist > 100.0f) + speed = 1.0f; + else if(fTargetDist > 75.0f) + speed = 0.7f; + else + speed = 0.4f; + if(m_heliStatus == HELI_STATUS_HOVER || m_heliStatus == HELI_STATUS_HOVER2 || m_heliStatus == HELI_STATUS_SHOT_DOWN) + speed = 0.0f; + + if(fTargetDist != 0.0f) + vTargetDist /= fTargetDist; + else + vTargetDist.x = 1.0f; + CVector2D targetSpeed = vTargetDist * speed; + + if(m_heliStatus == HELI_STATUS_HOVER2 || m_heliStatus == HELI_STATUS_SHOT_DOWN){ + bool force = !!((CTimer::GetFrameCounter() + m_randomSeed) & 8); + if(m_bTestRight){ + if(force || CWorld::TestSphereAgainstWorld(GetPosition() + 4.0f*GetRight(), 2.0f, this, true, false, false, false, false, false) == nil){ + if(m_heliStatus == HELI_STATUS_SHOT_DOWN){ + m_fTargetOffset -= CTimer::GetTimeStep()*0.05f; + targetSpeed.x -= -vTargetDist.x*0.15f; + targetSpeed.y -= vTargetDist.y*0.15f; + }else{ + targetSpeed.x -= -vTargetDist.x*0.05f; + targetSpeed.y -= vTargetDist.y*0.05f; + } + }else{ + m_bTestRight = false; + if(m_heliStatus == HELI_STATUS_HOVER2) + m_fTargetOffset += 5.0f; + else + m_fTargetOffset -= 5.0f; + } + }else{ + if(force || CWorld::TestSphereAgainstWorld(GetPosition() - 4.0f*GetRight(), 2.0f, this, true, false, false, false, false, false) == nil){ + if(m_heliStatus == HELI_STATUS_SHOT_DOWN){ + m_fTargetOffset -= CTimer::GetTimeStep()*0.05f; + targetSpeed.x += -vTargetDist.x*0.15f; + targetSpeed.y += vTargetDist.y*0.15f; + }else{ + targetSpeed.x += -vTargetDist.x*0.05f; + targetSpeed.y += vTargetDist.y*0.05f; + } + }else{ + m_bTestRight = true; + if(m_heliStatus == HELI_STATUS_HOVER2) + m_fTargetOffset += 5.0f; + else + m_fTargetOffset -= 5.0f; + } + } + + if(m_fTargetOffset > 30.0f) + m_fTargetOffset = 30.0f; + + if(m_heliStatus == HELI_STATUS_SHOT_DOWN && force){ + if(CWorld::TestSphereAgainstWorld(GetPosition() + 1.5f*GetForward(), 2.0f, this, true, false, false, false, false, false) || + CWorld::TestSphereAgainstWorld(GetPosition() - 1.5f*GetForward(), 2.0f, this, true, false, false, false, false, false)) + m_nExplosionTimer = CTimer::GetPreviousTimeInMilliseconds(); + } + }else + if(m_fTargetOffset >= 2.0f) + m_fTargetOffset -= 2.0f; + + CVector2D speedDir = targetSpeed - m_vecMoveSpeed; + float speedDiff = speedDir.Magnitude(); + if(speedDiff != 0.0f) + speedDir /= speedDiff; + else + speedDir.x = 1.0f; + float speedInc = CTimer::GetTimeStep()*0.002f; + if(speedDiff < speedInc){ + m_vecMoveSpeed.x = targetSpeed.x; + m_vecMoveSpeed.y = targetSpeed.y; + }else{ + m_vecMoveSpeed.x += speedDir.x*speedInc; + m_vecMoveSpeed.y += speedDir.y*speedInc; + } + GetMatrix().GetPosition().x += m_vecMoveSpeed.x*CTimer::GetTimeStep(); + GetMatrix().GetPosition().y += m_vecMoveSpeed.y*CTimer::GetTimeStep(); + + // Find z target + if(m_heliStatus == HELI_STATUS_FLY_AWAY) + m_fTargetZ = 1000.0f; + if((CTimer::GetTimeInMilliseconds() + 800*m_nHeliId) & 0x800) + m_fTargetZ += 2.0f; + m_fTargetZ += m_nHeliId*5.0f; + + // Find z speed + float targetSpeedZ = (m_fTargetZ - GetPosition().z)*0.01f; + float speedDiffZ = targetSpeedZ - m_vecMoveSpeed.z; + float speedIncZ = CTimer::GetTimeStep()*0.001f; + if(m_heliStatus == HELI_STATUS_FLY_AWAY) + speedIncZ *= 1.5f; + if(Abs(speedDiffZ) < speedIncZ) + m_vecMoveSpeed.z = targetSpeedZ; + else if(speedDiffZ < 0.0f) + m_vecMoveSpeed.z -= speedIncZ; + else + m_vecMoveSpeed.z += speedIncZ*1.5f; + GetMatrix().GetPosition().z += m_vecMoveSpeed.z*CTimer::GetTimeStep(); + + // Find angular speed + float targetAngularSpeed; + m_fAngularSpeed *= Pow(0.995f, CTimer::GetTimeStep()); + if(fTargetDist < 8.0f) + targetAngularSpeed = 0.0f; + else{ + float rotationDiff = CGeneral::GetATanOfXY(vTargetDist.x, vTargetDist.y) - m_fRotation; + while(rotationDiff < -3.14f) rotationDiff += 6.28f; + while(rotationDiff > 3.14f) rotationDiff -= 6.28f; + if(Abs(rotationDiff) > 0.4f){ + if(rotationDiff < 0.0f) + targetAngularSpeed = -0.2f; + else + targetAngularSpeed = 0.2f; + }else + targetAngularSpeed = 0.0f; + } + float angularSpeedDiff = targetAngularSpeed - m_fAngularSpeed; + float angularSpeedInc = CTimer::GetTimeStep()*0.0001f; + if(Abs(angularSpeedDiff) < angularSpeedInc) + m_fAngularSpeed = targetAngularSpeed; + else if(angularSpeedDiff < 0.0f) + m_fAngularSpeed -= angularSpeedInc; + else + m_fAngularSpeed += angularSpeedInc; + m_fRotation += m_fAngularSpeed * CTimer::GetTimeStep(); + + // Set matrix + CVector up(3.0f*m_vecMoveSpeed.x, 3.0f*m_vecMoveSpeed.y, 1.0f); + up.Normalise(); + CVector fwd(-Cos(m_fRotation), -Sin(m_fRotation), 0.0f); // not really forward + CVector right = CrossProduct(up, fwd); + fwd = CrossProduct(up, right); + GetRight() = right; + GetForward() = fwd; + GetUp() = up; + + // Search light and shooting + if(m_heliStatus == HELI_STATUS_FLY_AWAY || m_heliType == HELI_TYPE_CATALINA || CCullZones::PlayerNoRain()) + m_fSearchLightIntensity = 0.0f; + else { + // Update search light history once every 1000ms + int timeDiff = CTimer::GetTimeInMilliseconds() - m_nSearchLightTimer; + while (timeDiff > 1000) { + for (i = 5; i > 0; i--) { + m_aSearchLightHistoryX[i] = m_aSearchLightHistoryX[i - 1]; + m_aSearchLightHistoryY[i] = m_aSearchLightHistoryY[i - 1]; + } + m_aSearchLightHistoryX[0] = FindPlayerCoors().x + FindPlayerSpeed().x * 50.0f * (m_nHeliId + 2); + m_aSearchLightHistoryY[0] = FindPlayerCoors().y + FindPlayerSpeed().y * 50.0f * (m_nHeliId + 2); + + timeDiff -= 1000; + m_nSearchLightTimer += 1000; + } + assert(timeDiff <= 1000); + float f1 = timeDiff / 1000.0f; + float f2 = 1.0f - f1; + m_fSearchLightX = m_aSearchLightHistoryX[m_nHeliId + 2] * f2 + m_aSearchLightHistoryX[m_nHeliId + 2 - 1] * f1; + m_fSearchLightY = m_aSearchLightHistoryY[m_nHeliId + 2] * f2 + m_aSearchLightHistoryY[m_nHeliId + 2 - 1] * f1; + + float searchLightDist = (CVector2D(m_fSearchLightX, m_fSearchLightY) - GetPosition()).Magnitude(); + if (searchLightDist > 60.0f) + m_fSearchLightIntensity = 0.0f; + else if (searchLightDist < 40.0f) + m_fSearchLightIntensity = 1.0f; + else + m_fSearchLightIntensity = 1.0f - (40.0f - searchLightDist) / (60.0f-40.0f); + + if (m_fSearchLightIntensity < 0.9f || sq(FindPlayerCoors().x - m_fSearchLightX) + sq(FindPlayerCoors().y - m_fSearchLightY) > sq(7.0f)) + m_nShootTimer = CTimer::GetTimeInMilliseconds(); + else if (CTimer::GetTimeInMilliseconds() > m_nPoliceShoutTimer) { + DMAudio.PlayOneShot(m_audioEntityId, SOUND_PED_HELI_PLAYER_FOUND, 0.0f); + m_nPoliceShoutTimer = CTimer::GetTimeInMilliseconds() + 4500 + (CGeneral::GetRandomNumber() & 0xFFF); + } +#ifdef FIX_BUGS + if (!CReplay::IsPlayingBack()) +#endif + { + // Shoot + int shootTimeout; + if (m_heliType == HELI_TYPE_RANDOM) { + switch (FindPlayerPed()->m_pWanted->GetWantedLevel()) { + case 0: + case 1: + case 2: shootTimeout = 999999; break; + case 3: shootTimeout = 10000; break; + case 4: shootTimeout = 5000; break; + case 5: shootTimeout = 3500; break; + case 6: shootTimeout = 2000; break; + } + if (CCullZones::NoPolice()) + shootTimeout /= 2; + } + else + shootTimeout = 1500; + + if (FindPlayerPed()->m_pWanted->IsIgnored()) + m_nShootTimer = CTimer::GetTimeInMilliseconds(); + else { + // Check if line of sight is clear + if (CTimer::GetTimeInMilliseconds() > m_nShootTimer + shootTimeout && + CTimer::GetPreviousTimeInMilliseconds() <= m_nShootTimer + shootTimeout) { + if (CWorld::GetIsLineOfSightClear(GetPosition(), FindPlayerCoors(), true, false, false, false, false, false)) { + if (m_heliStatus == HELI_STATUS_HOVER2) + m_heliStatus = HELI_STATUS_HOVER; + } + else { + m_nShootTimer = CTimer::GetTimeInMilliseconds(); + if (m_heliStatus == HELI_STATUS_HOVER) + m_heliStatus = HELI_STATUS_HOVER2; + } + } + + // Shoot! + if (CTimer::GetTimeInMilliseconds() > m_nShootTimer + shootTimeout && + CTimer::GetTimeInMilliseconds() > m_nLastShotTime + 200) { + CVector shotTarget = FindPlayerCoors(); + // some inaccuracy + shotTarget.x += ((CGeneral::GetRandomNumber() & 0xFF) - 128) / 50.0f; + shotTarget.y += ((CGeneral::GetRandomNumber() & 0xFF) - 128) / 50.0f; + CVector direction = FindPlayerCoors() - GetPosition(); + direction.Normalise(); + shotTarget += 3.0f * direction; + CVector shotSource = GetPosition(); + shotSource += 3.0f * direction; + FireOneInstantHitRound(&shotSource, &shotTarget, 20); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); + m_nLastShotTime = CTimer::GetTimeInMilliseconds(); + } + } + } + } + + // Process ropes + for(i = 0; i < 4; i++){ + if(m_aSwatState[i] == 0) + continue; + + m_aSwatState[i]--; + CRopes::RegisterRope((uintptr)this + i, GetMatrix()*FindSwatPositionRelativeToHeli(i), false); + if(m_aSwatState[i] == 0){ + CVector speed = Multiply3x3(GetMatrix(), 0.05f*FindSwatPositionRelativeToHeli(i)); + speed.z = 0.0f; + CRopes::SetSpeedOfTopNode((uintptr)this + i, speed); + } + } + + RemoveAndAdd(); + bIsInSafePosition = true; + GetMatrix().UpdateRW(); + UpdateRwFrame(); +} + +void +CHeli::PreRender(void) +{ + float radius = (GetPosition().z - FindPlayerCoors().z - 10.0f - 1.0f) * 0.3f + 10.0f; + HeliDustGenerate(this, radius, FindPlayerCoors().z, Max(16.0f - 4.0f*CTimer::GetTimeStep(), 2.0f)); + CShadows::StoreShadowForVehicle(this, VEH_SHD_TYPE_HELI); +} + +void +CHeli::Render(void) +{ + CMatrix mat; + CVector pos; + + mat.Attach(RwFrameGetMatrix(m_aHeliNodes[HELI_TOPROTOR])); + pos = mat.GetPosition(); + mat.SetRotateZ(m_fRotorRotation); + mat.Translate(pos); + mat.UpdateRW(); + + m_fRotorRotation += 3.14f/6.5f; + if(m_fRotorRotation > 6.28f) + m_fRotorRotation -= 6.28f; + + mat.Attach(RwFrameGetMatrix(m_aHeliNodes[HELI_BACKROTOR])); + pos = mat.GetPosition(); + mat.SetRotateX(m_fRotorRotation); + mat.Translate(pos); + mat.UpdateRW(); + + CEntity::Render(); +} + +void +CHeli::PreRenderAlways(void) +{ + CVector shadowPos(m_fSearchLightX, m_fSearchLightY, GetPosition().z); + if(m_fSearchLightIntensity > 0.0f){ + CShadows::StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &shadowPos, + 6.0f, 0.0f, 0.0f, -6.0f, + 80*m_fSearchLightIntensity, 80*m_fSearchLightIntensity, 80*m_fSearchLightIntensity, 80*m_fSearchLightIntensity, + 50.0f, true, 1.0f, nil, false); + + CVector front = GetMatrix() * CVector(0.0f, 7.0f, 0.0f); + CVector toPlayer = FindPlayerCoors() - front; + toPlayer.Normalise(); + float intensity = m_fSearchLightIntensity*sq(CTimeCycle::GetSpriteBrightness()); + if(DotProduct(toPlayer, TheCamera.GetForward()) < -0.8f) + CCoronas::RegisterCorona((uintptr)this, 255*intensity, 255*intensity, 255*intensity, 255, + front, 10.0f, 60.0f, CCoronas::TYPE_STAR, + CCoronas::FLARE_HEADLIGHTS, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + else + CCoronas::RegisterCorona((uintptr)this, 200*intensity, 200*intensity, 200*intensity, 255, + front, 8.0f, 60.0f, CCoronas::TYPE_STAR, + CCoronas::FLARE_HEADLIGHTS, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + } + + CVector back = GetMatrix() * CVector(0.0f, -9.0f, 0.0f); + if(CTimer::GetTimeInMilliseconds() & 0x100) + CCoronas::RegisterCorona((uintptr)this + 2, 255, 0, 0, 255, + back, 1.0f, 60.0f, CCoronas::TYPE_STAR, + CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + else + CCoronas::RegisterCorona((uintptr)this + 2, 0, 0, 0, 255, + back, 1.0f, 60.0f, CCoronas::TYPE_STAR, + CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); +} + +RwObject* +GetHeliAtomicObjectCB(RwObject *object, void *data) +{ + RpAtomic *atomic = (RpAtomic*)object; + assert(RwObjectGetType(object) == rpATOMIC); + if(RpAtomicGetFlags(atomic) & rpATOMICRENDER) + *(RpAtomic**)data = atomic; + return object; +} + +CObject* +CHeli::SpawnFlyingComponent(int32 component) +{ + RpAtomic *atomic; + RwFrame *frame; + RwMatrix *matrix; + CObject *obj; + + if(m_aHeliNodes[component] == nil) + return nil; + + atomic = nil; + RwFrameForAllObjects(m_aHeliNodes[component], GetHeliAtomicObjectCB, &atomic); + if(atomic == nil) + return nil; + + obj = new CObject; + if(obj == nil) + return nil; + + obj->SetModelIndexNoCreate(MI_CAR_WHEEL); + // object needs base model + obj->RefModelInfo(GetModelIndex()); + + // create new atomic + matrix = RwFrameGetLTM(m_aHeliNodes[component]); + frame = RwFrameCreate(); + atomic = RpAtomicClone(atomic); + *RwFrameGetMatrix(frame) = *matrix; + RpAtomicSetFrame(atomic, frame); + CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); + obj->AttachToRwObject((RwObject*)atomic); + obj->bDontStream = true; + + // init object + obj->m_fMass = 10.0f; + obj->m_fTurnMass = 25.0f; + obj->m_fAirResistance = 0.99f; + obj->m_fElasticity = 0.1f; + obj->m_fBuoyancy = obj->m_fMass*GRAVITY/0.75f; + obj->ObjectCreatedBy = TEMP_OBJECT; + obj->SetIsStatic(false); + obj->bIsPickup = false; + + // life time + CObject::nNoTempObjects++; + if(component == HELI_TOPROTOR) + obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 1000; + else + obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 3000; + + obj->m_vecMoveSpeed = m_vecMoveSpeed; + if(obj->m_vecMoveSpeed.z > 0.0f) + obj->m_vecMoveSpeed.z = 0.3f; + else + obj->m_vecMoveSpeed.z = 0.0f; + + obj->m_vecTurnSpeed = m_vecTurnSpeed*2.0f; + + if(component == HELI_BACKROTOR) + obj->m_vecTurnSpeed.x = 0.5f; + else if(component == HELI_TOPROTOR || component == HELI_TOPKNOT) + obj->m_vecTurnSpeed.z = 0.5f; + else + obj->m_vecTurnSpeed.y = 0.5f; + + obj->bRenderScorched = true; + + CWorld::Add(obj); + + atomic = nil; + RwFrameForAllObjects(m_aHeliNodes[component], GetHeliAtomicObjectCB, &atomic); + if(atomic) + RpAtomicSetFlags(atomic, 0); + + return obj; +} + +CVector +CHeli::FindSwatPositionRelativeToHeli(int n) +{ + switch(n){ + case 0: return CVector(-1.2f, -1.0f, -0.5f); + case 1: return CVector( 1.2f, -1.0f, -0.5f); + case 2: return CVector(-1.2f, 1.0f, -0.5f); + case 3: return CVector( 1.2f, 1.0f, -0.5f); + default: return CVector(0.0f, 0.0f, 0.0f); + } +} + +bool +CHeli::SendDownSwat(void) +{ + if(m_numSwat == 0 || !CStreaming::HasModelLoaded(MI_SWAT) || + CGeneral::GetRandomNumber() & 0x7F || (GetPosition() - FindPlayerCoors()).Magnitude() > 50.0f) + return false; + + CMatrix mat(GetMatrix()); + CVector pos = Multiply3x3(mat, FindSwatPositionRelativeToHeli(m_numSwat-1)) + GetPosition(); + + float groundZ = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, nil); + if(Abs(FindPlayerCoors().z - groundZ) < 2.5f && CRopes::RegisterRope((uintptr)this + m_numSwat-1, pos, false)){ + CCopPed *swat = (CCopPed*)CPopulation::AddPed(PEDTYPE_COP, COP_HELI_SWAT, pos); + swat->bUsesCollision = false; + swat->m_pRopeEntity = this; + RegisterReference(&swat->m_pRopeEntity); + m_numSwat--; + swat->m_nRopeID = (uintptr)this + m_numSwat; + m_aSwatState[m_numSwat] = 255; + CAnimManager::BlendAnimation(swat->GetClump(), ASSOCGRP_STD, ANIM_STD_ABSEIL, 4.0f); + return true; + } + return false; +} + + +void +CHeli::InitHelis(void) +{ + int i; + + NumRandomHelis = 0; + TestForNewRandomHelisTimer = 0; + CatalinaHeliOn = false; + ScriptHeliOn = false; + for(i = 0; i < NUM_HELIS; i++) + pHelis[i] = nil; + + ((CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_CHOPPER))->SetColModel(&CTempColModels::ms_colModelPed1); +} + +CHeli* +CHeli::GenerateHeli(bool catalina) +{ + CHeli *heli; + CVector heliPos; + int i; + + if(catalina) + assert(0 && "can't create catalina's heli"); + else + heli = new CHeli(MI_CHOPPER, PERMANENT_VEHICLE); + + if(catalina) + heliPos = CVector(-224.0f, 201.0f, 83.0f); + else{ + heliPos = FindPlayerCoors(); + float angle = (float)(CGeneral::GetRandomNumber() & 0xFF)/0x100 * 6.28f; + heliPos.x += 250.0f*Sin(angle); + heliPos.y += 250.0f*Cos(angle); + if(heliPos.x < -2000.0f-400.0f || heliPos.x > 2000.0f-400.0f || heliPos.y < -2000.0f || heliPos.y > 2000.0f){ + heliPos = FindPlayerCoors(); + heliPos.x -= 250.0f*Sin(angle); + heliPos.y -= 250.0f*Cos(angle); + } + heliPos.z += 50.0f; + } + heli->GetMatrix().SetTranslate(heliPos); + if(catalina) + heli->GetMatrix().SetRotateZOnly(DEGTORAD(270.0f)); // game actually uses 3.14 here + + heli->SetStatus(STATUS_ABANDONED); + heli->bIsLocked = true; + + int id = -1; + bool found = false; + while(!found){ + id++; + found = true; + for(i = 0; i < 4; i++) + if(pHelis[i] && pHelis[i]->m_nHeliId == id) + found = false; + } + heli->m_nHeliId = id; + + CWorld::Add(heli); + + return heli; +} + +void +CHeli::UpdateHelis(void) +{ + int i, j; + + // Spawn new police helis + int numHelisRequired = +#ifdef FIX_BUGS + CReplay::IsPlayingBack() ? 0 : +#endif + FindPlayerPed()->m_pWanted->NumOfHelisRequired(); + if(CCullZones::PlayerNoRain() || CGame::IsInInterior()) + numHelisRequired = 0; + if(CStreaming::HasModelLoaded(MI_CHOPPER) && CTimer::GetTimeInMilliseconds() > TestForNewRandomHelisTimer){ + // Spawn a police heli + TestForNewRandomHelisTimer = CTimer::GetTimeInMilliseconds() + 15000; + if(NumRandomHelis < numHelisRequired){ + NumRandomHelis++; + CHeli *heli = GenerateHeli(false); + heli->m_heliType = HELI_TYPE_RANDOM; + if(pHelis[HELI_RANDOM0] == nil) + pHelis[HELI_RANDOM0] = heli; + else if(pHelis[HELI_RANDOM1] == nil) + pHelis[HELI_RANDOM1] = heli; + else + assert(0 && "too many helis"); + } + } + + // Handle script heli + if(ScriptHeliOn){ + if(CStreaming::HasModelLoaded(MI_CHOPPER) && pHelis[HELI_SCRIPT] == nil){ + pHelis[HELI_SCRIPT] = GenerateHeli(false); + pHelis[HELI_SCRIPT]->m_heliType = HELI_TYPE_SCRIPT; + }else + CStreaming::RequestModel(MI_CHOPPER, 0); + }else{ + if(pHelis[HELI_SCRIPT]) + pHelis[HELI_SCRIPT]->m_heliStatus = HELI_STATUS_FLY_AWAY; + } + + // Delete helis that we no longer need + for(i = 0; i < NUM_HELIS; i++) + if(pHelis[i] && pHelis[i]->m_heliStatus == HELI_STATUS_FLY_AWAY && pHelis[i]->GetPosition().z > 150.0f){ + CWorld::Remove(pHelis[i]); + delete pHelis[i]; + pHelis[i] = nil; + if(i != HELI_SCRIPT && i != HELI_CATALINA) + NumRandomHelis--; + } + + // Handle explosions + for(i = 0; i < NUM_HELIS; i++){ + if(pHelis[i] && pHelis[i]->m_heliStatus == HELI_STATUS_SHOT_DOWN && CTimer::GetTimeInMilliseconds() > pHelis[i]->m_nExplosionTimer){ + // Second part of explosion + static int nFrameGen; + CRGBA colors[8]; + + TheCamera.CamShake(0.7f, pHelis[i]->GetPosition().x, pHelis[i]->GetPosition().y, pHelis[i]->GetPosition().z); + + colors[0] = CRGBA(0, 0, 0, 255); + colors[1] = CRGBA(224, 224, 224, 255); + colors[2] = CRGBA(0, 0, 0, 255); + colors[3] = CRGBA(0, 0, 0, 255); + colors[4] = CRGBA(66, 162, 252, 255); + colors[5] = CRGBA(0, 0, 0, 255); + colors[6] = CRGBA(0, 0, 0, 255); + colors[7] = CRGBA(0, 0, 0, 255); + + CVector pos = pHelis[i]->GetPosition(); + CVector dir; + for(j = 0; j < 40; j++){ + dir.x = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f); + dir.y = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f); + dir.z = CGeneral::GetRandomNumberInRange(0.0f, 2.0f); + int rotSpeed = CGeneral::GetRandomNumberInRange(10, 30); + if(CGeneral::GetRandomNumber() & 1) + rotSpeed = -rotSpeed; + int f = ++nFrameGen & 3; + CParticle::AddParticle(PARTICLE_HELI_DEBRIS, pos, dir, + nil, CGeneral::GetRandomNumberInRange(0.1f, 1.0f), + colors[nFrameGen&7], rotSpeed, 0, f, 0); + } + + CExplosion::AddExplosion(nil, nil, EXPLOSION_HELI, pos, 0); + + pHelis[i]->SpawnFlyingComponent(HELI_SKID_LEFT); + pHelis[i]->SpawnFlyingComponent(HELI_SKID_RIGHT); + pHelis[i]->SpawnFlyingComponent(HELI_TOPROTOR); + + CDarkel::RegisterCarBlownUpByPlayer(pHelis[i]); + CWorld::Remove(pHelis[i]); + delete pHelis[i]; + pHelis[i] = nil; + if(i != HELI_SCRIPT && i != HELI_CATALINA) + NumRandomHelis--; + if(i == HELI_CATALINA) + CatalinaHasBeenShotDown = true; + + CStats::PeopleKilledByPlayer += 2; + CStats::PedsKilledOfThisType[PEDTYPE_COP] += 2; + CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 250; + pos = CWorld::Players[CWorld::PlayerInFocus].m_pPed->GetPosition(); + CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->RegisterCrime_Immediately(CRIME_SHOOT_HELI, + pos, i + 19843, false); + + TestForNewRandomHelisTimer = CTimer::GetTimeInMilliseconds() + 50000; + }else if(pHelis[i] && pHelis[i]->m_heliStatus == HELI_STATUS_SHOT_DOWN && CTimer::GetTimeInMilliseconds()+7000 > pHelis[i]->m_nExplosionTimer){ + // First part of explosion + if(CTimer::GetPreviousTimeInMilliseconds()+7000 < pHelis[i]->m_nExplosionTimer){ + pHelis[i]->SpawnFlyingComponent(HELI_BACKROTOR); + pHelis[i]->SpawnFlyingComponent(HELI_TAIL); + pHelis[i]->m_fAngularSpeed *= -2.5f; + pHelis[i]->bRenderScorched = true; + + TheCamera.CamShake(0.4f, pHelis[i]->GetPosition().x, pHelis[i]->GetPosition().y, pHelis[i]->GetPosition().z); + + CVector pos = pHelis[i]->GetPosition() - 2.5f*pHelis[i]->GetForward(); + CExplosion::AddExplosion(nil, nil, EXPLOSION_HELI2, pos, 0); + }else + pHelis[i]->m_fAngularSpeed *= 1.03f; + } + } + + // Find police helis to remove + for(i = 0; i < 2; i++) + if(pHelis[i] && pHelis[i]->m_heliStatus != HELI_STATUS_FLY_AWAY){ + if(numHelisRequired > 0) + numHelisRequired--; + else + pHelis[i]->m_heliStatus = HELI_STATUS_FLY_AWAY; + } + + // Remove all helis if in a tunnel or under water + if(FindPlayerCoors().z < - 2.0f) + for(i = 0; i < NUM_HELIS; i++) + if(pHelis[i] && pHelis[i]->m_heliStatus != HELI_STATUS_SHOT_DOWN) + pHelis[i]->m_heliStatus = HELI_STATUS_FLY_AWAY; +} + +void +CHeli::SpecialHeliPreRender(void) +{ + int i; + for(i = 0; i < NUM_HELIS; i++) + if(pHelis[i]) + pHelis[i]->PreRenderAlways(); +} + +bool +CHeli::TestRocketCollision(CVector *rocketPos) +{ + int i; + bool hit = false; + + for(i = 0; i < NUM_HELIS; i++){ + if(pHelis[i] && !pHelis[i]->bExplosionProof && (*rocketPos - pHelis[i]->GetPosition()).MagnitudeSqr() < sq(8.0f)){ + pHelis[i]->m_fAngularSpeed = CGeneral::GetRandomTrueFalse() ? 0.05f : -0.05f; + pHelis[i]->m_heliStatus = HELI_STATUS_SHOT_DOWN; + pHelis[i]->m_nExplosionTimer = CTimer::GetTimeInMilliseconds() + 10000; + hit = true; + } + } + return hit; +} + +bool +CHeli::TestBulletCollision(CVector *line0, CVector *line1, CVector *bulletPos, int32 damage) +{ + int i; + bool hit = false; + + for(i = 0; i < NUM_HELIS; i++) + if(pHelis[i] && !pHelis[i]->bBulletProof && CCollision::DistToLine(line0, line1, &pHelis[i]->GetPosition()) < 5.0f){ + // Find bullet position + float distToHeli = (pHelis[i]->GetPosition() - *line0).Magnitude(); + CVector line = (*line1 - *line0); + float lineLength = line.Magnitude(); + *bulletPos = *line0 + line*Max(1.0f, distToHeli-5.0f)/lineLength; + + pHelis[i]->m_nBulletDamage += damage; + + if(pHelis[i]->m_heliType == HELI_CATALINA && pHelis[i]->m_nBulletDamage > 400 || + pHelis[i]->m_heliType != HELI_CATALINA && pHelis[i]->m_nBulletDamage > 700){ + pHelis[i]->m_fAngularSpeed = CGeneral::GetRandomTrueFalse() ? 0.05f : -0.05f; + pHelis[i]->m_heliStatus = HELI_STATUS_SHOT_DOWN; + pHelis[i]->m_nExplosionTimer = CTimer::GetTimeInMilliseconds() + 10000; + } + + hit = true; + } + return hit; +} + +bool +CHeli::TestSniperCollision(CVector *line0, CVector *line1) +{ + int i; + bool hit = false; + + for(i = 0; i < NUM_HELIS; i++){ + if(pHelis[i] && !pHelis[i]->bBulletProof) { + CVector pilotPos = pHelis[i]->GetMatrix() * CVector(-0.43f, 1.49f, 1.5f); + if(CCollision::DistToLine(line0, line1, &pilotPos) < 0.8f){ + pHelis[i]->m_fAngularSpeed = CGeneral::GetRandomTrueFalse() ? 0.05f : -0.05f; + pHelis[i]->m_heliStatus = HELI_STATUS_SHOT_DOWN; + pHelis[i]->m_nExplosionTimer = CTimer::GetTimeInMilliseconds() + 9999999; + pHelis[i]->m_numSwat = 0; + + hit = true; + } + } + } + return hit; +} + +void CHeli::StartCatalinaFlyBy(void) +{ + CatalinaHeliOn = true; + CatalinaHasBeenShotDown = false; +} + +void +CHeli::RemoveCatalinaHeli(void) +{ + CatalinaHeliOn = false; + if(pHelis[HELI_CATALINA]){ + CWorld::Remove(pHelis[HELI_CATALINA]); + delete pHelis[HELI_CATALINA]; + pHelis[HELI_CATALINA] = nil; + } +} + +CHeli *CHeli::FindPointerToCatalinasHeli(void) { return pHelis[HELI_CATALINA]; } +void CHeli::CatalinaTakeOff(void) { pHelis[HELI_CATALINA]->m_pathState = 8; } +void CHeli::MakeCatalinaHeliFlyAway(void) { pHelis[HELI_CATALINA]->m_pathState = 12; } +bool CHeli::HasCatalinaBeenShotDown(void) { return CatalinaHasBeenShotDown; } + +void CHeli::ActivateHeli(bool activate) { ScriptHeliOn = activate; } diff --git a/src/miami/vehicles/Heli.h b/src/miami/vehicles/Heli.h new file mode 100644 index 00000000..c5ae08e2 --- /dev/null +++ b/src/miami/vehicles/Heli.h @@ -0,0 +1,101 @@ +#pragma once + +#include "Vehicle.h" + +class CObject; + +enum eHeliNodes +{ + HELI_CHASSIS = 1, + HELI_TOPROTOR, + HELI_BACKROTOR, + HELI_TAIL, + HELI_TOPKNOT, + HELI_SKID_LEFT, + HELI_SKID_RIGHT, + NUM_HELI_NODES +}; + +enum +{ + HELI_RANDOM0, + HELI_RANDOM1, + HELI_SCRIPT, + HELI_CATALINA, // TODO 2 in VC + NUM_HELIS +}; + +enum +{ + HELI_TYPE_RANDOM, + HELI_TYPE_SCRIPT, + HELI_TYPE_CATALINA, +}; + + +class CHeli : public CVehicle +{ +public: + RwFrame *m_aHeliNodes[NUM_HELI_NODES]; + int8 m_heliStatus; + float m_fSearchLightX; + float m_fSearchLightY; + uint32 m_nExplosionTimer; + float m_fRotation; + float m_fAngularSpeed; + float m_fTargetZ; + float m_fSearchLightIntensity; + int8 m_nHeliId; + int8 m_heliType; + int8 m_pathState; + int8 m_numSwat; + uint8 m_aSwatState[4]; + float m_aSearchLightHistoryX[6]; + float m_aSearchLightHistoryY[6]; + uint32 m_nSearchLightTimer; + uint32 m_nShootTimer; + uint32 m_nLastShotTime; + uint32 m_nBulletDamage; + float m_fRotorRotation; + float m_fHeliDustZ[8]; + uint32 m_nPoliceShoutTimer; + float m_fTargetOffset; + bool m_bTestRight; + + static CHeli *pHelis[NUM_HELIS]; + static int16 NumRandomHelis; + static uint32 TestForNewRandomHelisTimer; + static bool CatalinaHeliOn; + static bool CatalinaHasBeenShotDown; + static bool ScriptHeliOn; + + CHeli(int32 id, uint8 CreatedBy); + + // from CEntity + void SetModelIndex(uint32 id); + void ProcessControl(void); + void PreRender(void); + void Render(void); + + void PreRenderAlways(void); + CObject *SpawnFlyingComponent(int32 component); + CVector FindSwatPositionRelativeToHeli(int n); + bool SendDownSwat(void); + + static void InitHelis(void); + static CHeli *GenerateHeli(bool catalina); // out of class in III PC and later because of SecuROM + static void UpdateHelis(void); + static void SpecialHeliPreRender(void); + static bool TestRocketCollision(CVector *coors); + static bool TestBulletCollision(CVector *line0, CVector *line1, CVector *bulletPos, int32 damage); + static bool TestSniperCollision(CVector *line0, CVector *line1); + + static void StartCatalinaFlyBy(void); // out of class in III PC and later because of SecuROM + static void RemoveCatalinaHeli(void); + static CHeli *FindPointerToCatalinasHeli(void); + static void CatalinaTakeOff(void); + static void MakeCatalinaHeliFlyAway(void); + static bool HasCatalinaBeenShotDown(void); + + static void ActivateHeli(bool activate); +}; diff --git a/src/miami/vehicles/Plane.cpp b/src/miami/vehicles/Plane.cpp new file mode 100644 index 00000000..0b40ca7e --- /dev/null +++ b/src/miami/vehicles/Plane.cpp @@ -0,0 +1,1069 @@ +#include "common.h" +#include "main.h" + +#include "General.h" +#include "CutsceneMgr.h" +#include "ModelIndices.h" +#include "FileMgr.h" +#include "Streaming.h" +#include "Replay.h" +#include "Camera.h" +#include "DMAudio.h" +#include "Wanted.h" +#include "Coronas.h" +#include "Particle.h" +#include "Explosion.h" +#include "Fluff.h" +#include "World.h" +#include "HandlingMgr.h" +#include "Heli.h" +#include "Plane.h" +#include "MemoryHeap.h" + +CPlaneNode *pPathNodes; +CPlaneNode *pPath2Nodes; +CPlaneNode *pPath3Nodes; +CPlaneNode *pPath4Nodes; +int32 NumPathNodes; +int32 NumPath2Nodes; +int32 NumPath3Nodes; +int32 NumPath4Nodes; +float TotalLengthOfFlightPath; +float TotalLengthOfFlightPath2; +float TotalLengthOfFlightPath3; +float TotalLengthOfFlightPath4; +float TotalDurationOfFlightPath; +float TotalDurationOfFlightPath2; +float TotalDurationOfFlightPath3; +float TotalDurationOfFlightPath4; +float LandingPoint; +float TakeOffPoint; +CPlaneInterpolationLine aPlaneLineBits[6]; + +float PlanePathPosition[3]; +float OldPlanePathPosition[3]; +float PlanePathSpeed[3]; +float PlanePath2Position[5]; +float PlanePath3Position[4]; +float PlanePath2Speed[5]; +float PlanePath3Speed[4]; + + +enum +{ + CESNA_STATUS_NONE, // doesn't even exist + CESNA_STATUS_FLYING, + CESNA_STATUS_DESTROYED, + CESNA_STATUS_LANDED, +}; + +int32 CesnaMissionStatus; +int32 CesnaMissionStartTime; +CPlane *pDrugRunCesna; +int32 DropOffCesnaMissionStatus; +int32 DropOffCesnaMissionStartTime; +CPlane *pDropOffCesna; + +CPlane::CPlane(int32 id, uint8 CreatedBy) + : CVehicle(CreatedBy) +{ + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); + m_vehType = VEHICLE_TYPE_PLANE; + pHandling = mod_HandlingManager.GetHandlingData((tVehicleType)mi->m_handlingId); + SetModelIndex(id); + + m_fMass = 100000000.0f; + m_fTurnMass = 100000000.0f; + m_fAirResistance = 0.9994f; + m_fElasticity = 0.05f; + + bUsesCollision = false; + m_bHasBeenHit = false; + m_bIsDrugRunCesna = false; + m_bIsDropOffCesna = false; + m_bTempPlane = false; + + SetStatus(STATUS_PLANE); + bIsBIGBuilding = true; + m_level = LEVEL_GENERIC; + + m_isFarAway = false; +#ifdef CPLANE_ROTORS + m_fRotorRotation = 0.0f; +#endif +} + +CPlane::~CPlane() +{ + DeleteRwObject(); +} + +void +CPlane::SetModelIndex(uint32 id) +{ + CVehicle::SetModelIndex(id); +#ifdef CPLANE_ROTORS + int i; + for(i = 0; i < NUM_PLANE_NODES; i++) + m_aPlaneNodes[i] = nil; + if(GetModelIndex() == MI_CHOPPER){ + // This is surprisingly annoying... + RwFrame *heliNodes[NUM_HELI_NODES]; + for(i = 0; i < NUM_HELI_NODES; i++) + heliNodes[i] = nil; + CClumpModelInfo::FillFrameArray(GetClump(), heliNodes); + m_aPlaneNodes[PLANE_TOPROTOR] = heliNodes[HELI_TOPROTOR]; + m_aPlaneNodes[PLANE_BACKROTOR] = heliNodes[HELI_BACKROTOR]; + }else + CClumpModelInfo::FillFrameArray(GetClump(), m_aPlaneNodes); +#endif +} + +void +CPlane::DeleteRwObject(void) +{ + if(m_rwObject && RwObjectGetType(m_rwObject) == rpATOMIC){ + GetMatrix().Detach(); + if(RwObjectGetType(m_rwObject) == rpATOMIC){ // useless check + RwFrame *f = RpAtomicGetFrame((RpAtomic*)m_rwObject); + RpAtomicDestroy((RpAtomic*)m_rwObject); + RwFrameDestroy(f); + } + m_rwObject = nil; + } + CEntity::DeleteRwObject(); +} + +// There's a LOT of copy and paste in here. Maybe this could be refactored somehow +void +CPlane::ProcessControl(void) +{ + int i; + CVector pos; + + if(CReplay::IsPlayingBack()) + return; + + if(GetModelIndex() == MI_AIRTRAIN){ + if(GetPosition().z > 100.0f) + CPlaneTrails::RegisterPoint(GetPosition(), m_nPlaneId); + }else if(GetModelIndex() == MI_DEADDODO) + CPlaneBanners::RegisterPoint(GetPosition(), m_nPlaneId); + + // Explosion + if(m_bHasBeenHit){ + // BUG: since this is all based on frames, you can skip the explosion processing when you go into the menu + if(GetModelIndex() == MI_AIRTRAIN){ + int frm = CTimer::GetFrameCounter() - m_nFrameWhenHit; + if(frm == 20){ + static int nFrameGen; + CRGBA colors[8]; + + CExplosion::AddExplosion(nil, FindPlayerPed(), EXPLOSION_HELI, GetMatrix() * CVector(0.0f, 0.0f, 0.0f), 0); + + colors[0] = CRGBA(0, 0, 0, 255); + colors[1] = CRGBA(224, 230, 238, 255); + colors[2] = CRGBA(224, 230, 238, 255); + colors[3] = CRGBA(0, 0, 0, 255); + colors[4] = CRGBA(224, 230, 238, 255); + colors[5] = CRGBA(0, 0, 0, 255); + colors[6] = CRGBA(0, 0, 0, 255); + colors[7] = CRGBA(224, 230, 238, 255); + + CVector dir; + for(i = 0; i < 40; i++){ + dir.x = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f); + dir.y = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f); + dir.z = CGeneral::GetRandomNumberInRange(0.0f, 2.0f); + int rotSpeed = CGeneral::GetRandomNumberInRange(10, 30); + if(CGeneral::GetRandomNumber() & 1) + rotSpeed = -rotSpeed; + int f = ++nFrameGen & 3; + CParticle::AddParticle(PARTICLE_HELI_DEBRIS, GetMatrix() * CVector(0.0f, 0.0f, 0.0f), dir, + nil, CGeneral::GetRandomNumberInRange(0.1f, 1.0f), + colors[nFrameGen&7], rotSpeed, 0, f, 0); + } + } + if(frm >= 40 && frm <= 80 && frm & 1){ + if(frm & 1){ + pos.x = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.2f; + pos.z = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.2f; + pos.y = frm - 40; + pos = GetMatrix() * pos; + }else{ + pos.x = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.2f; + pos.z = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.2f; + pos.y = 40 - frm; + pos = GetMatrix() * pos; + } + CExplosion::AddExplosion(nil, FindPlayerPed(), EXPLOSION_HELI, pos, 0); + } + if(frm == 60) + bRenderScorched = true; + if(frm == 82){ + TheCamera.SetFadeColour(255, 255, 255); + TheCamera.Fade(0.0f, FADE_OUT); + TheCamera.ProcessFade(); + TheCamera.Fade(1.0f, FADE_IN); + FlagToDestroyWhenNextProcessed(); + } + }else{ + int frm = CTimer::GetFrameCounter() - m_nFrameWhenHit; + if(frm == 20){ + static int nFrameGen; + CRGBA colors[8]; + + CExplosion::AddExplosion(nil, FindPlayerPed(), EXPLOSION_HELI, GetMatrix() * CVector(0.0f, 0.0f, 0.0f), 0); + + colors[0] = CRGBA(0, 0, 0, 255); + colors[1] = CRGBA(224, 230, 238, 255); + colors[2] = CRGBA(224, 230, 238, 255); + colors[3] = CRGBA(0, 0, 0, 255); + colors[4] = CRGBA(252, 66, 66, 255); + colors[5] = CRGBA(0, 0, 0, 255); + colors[6] = CRGBA(0, 0, 0, 255); + colors[7] = CRGBA(252, 66, 66, 255); + + CVector dir; + for(i = 0; i < 40; i++){ + dir.x = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f); + dir.y = CGeneral::GetRandomNumberInRange(-2.0f, 2.0f); + dir.z = CGeneral::GetRandomNumberInRange(0.0f, 2.0f); + int rotSpeed = CGeneral::GetRandomNumberInRange(30.0f, 20.0f); + if(CGeneral::GetRandomNumber() & 1) + rotSpeed = -rotSpeed; + int f = ++nFrameGen & 3; + CParticle::AddParticle(PARTICLE_HELI_DEBRIS, GetMatrix() * CVector(0.0f, 0.0f, 0.0f), dir, + nil, CGeneral::GetRandomNumberInRange(0.1f, 1.0f), + colors[nFrameGen&7], rotSpeed, 0, f, 0); + } + } + if(frm >= 40 && frm <= 60 && frm & 1){ + if(frm & 1){ + pos.x = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.1f; + pos.z = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.1f; + pos.y = (frm - 40)*0.3f; + pos = GetMatrix() * pos; + }else{ + pos.x = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.1f; + pos.z = ((CGeneral::GetRandomNumber() & 0x3F) - 32) * 0.1f; + pos.y = (40 - frm)*0.3f; + pos = GetMatrix() * pos; + } + CExplosion::AddExplosion(nil, FindPlayerPed(), EXPLOSION_HELI, pos, 0); + } + if(frm == 30) + bRenderScorched = true; + if(frm == 62){ + TheCamera.SetFadeColour(200, 200, 200); + TheCamera.Fade(0.0f, FADE_OUT); + TheCamera.ProcessFade(); + TheCamera.Fade(1.0f, FADE_IN); + if(m_bIsDrugRunCesna){ + CesnaMissionStatus = CESNA_STATUS_DESTROYED; + pDrugRunCesna = nil; + } + if(m_bIsDropOffCesna){ + DropOffCesnaMissionStatus = CESNA_STATUS_DESTROYED; + pDropOffCesna = nil; + } + FlagToDestroyWhenNextProcessed(); + } + } + } + + // Update plane position and speed + if(GetModelIndex() == MI_AIRTRAIN || !m_isFarAway || ((CTimer::GetFrameCounter() + m_randomSeed) & 7) == 0){ + if(GetModelIndex() == MI_AIRTRAIN){ + float pathPositionRear = PlanePathPosition[m_nPlaneId] - 30.0f; + if(pathPositionRear < 0.0f) + pathPositionRear += TotalLengthOfFlightPath; + float pathPosition = pathPositionRear + 30.0f; + + float pitch = 0.0f; + float distSinceTakeOff = pathPosition - TakeOffPoint; + if(distSinceTakeOff <= 0.0f && distSinceTakeOff > -70.0f){ + // shortly before take off + pitch = 1.0f - distSinceTakeOff/-70.0f; + }else if(distSinceTakeOff >= 0.0f && distSinceTakeOff < 100.0f){ + // shortly after take off + pitch = 1.0f - distSinceTakeOff/100.0f; + } + + float distSinceLanding = pathPosition - LandingPoint; + if(distSinceLanding <= 0.0f && distSinceLanding > -200.0f){ + // shortly before landing + pitch = 1.0f - distSinceLanding/-200.0f; + }else if(distSinceLanding >= 0.0f && distSinceLanding < 70.0f){ + // shortly after landing + pitch = 1.0f - distSinceLanding/70.0f; + } + + + // Advance current node to appropriate position + float pos1, pos2; + int nextTrackNode = m_nCurPathNode + 1; + pos1 = pPathNodes[m_nCurPathNode].t; + if(nextTrackNode < NumPathNodes) + pos2 = pPathNodes[nextTrackNode].t; + else{ + nextTrackNode = 0; + pos2 = TotalLengthOfFlightPath; + } + while(pathPositionRear < pos1 || pathPositionRear > pos2){ + m_nCurPathNode = (m_nCurPathNode+1) % NumPathNodes; + nextTrackNode = m_nCurPathNode + 1; + pos1 = pPathNodes[m_nCurPathNode].t; + if(nextTrackNode < NumPathNodes) + pos2 = pPathNodes[nextTrackNode].t; + else{ + nextTrackNode = 0; + pos2 = TotalLengthOfFlightPath; + } + } + bool bothOnGround = pPathNodes[m_nCurPathNode].bOnGround && pPathNodes[nextTrackNode].bOnGround; + if(PlanePathPosition[m_nPlaneId] >= LandingPoint && OldPlanePathPosition[m_nPlaneId] < LandingPoint) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_PLANE_ON_GROUND, 0.0f); + float dist = pPathNodes[nextTrackNode].t - pPathNodes[m_nCurPathNode].t; + if(dist < 0.0f) + dist += TotalLengthOfFlightPath; + float f = (pathPositionRear - pPathNodes[m_nCurPathNode].t)/dist; + CVector posRear = (1.0f - f)*pPathNodes[m_nCurPathNode].p + f*pPathNodes[nextTrackNode].p; + + // Same for the front + float pathPositionFront = pathPositionRear + 60.0f; + if(pathPositionFront > TotalLengthOfFlightPath) + pathPositionFront -= TotalLengthOfFlightPath; + int curPathNodeFront = m_nCurPathNode; + int nextPathNodeFront = curPathNodeFront + 1; + pos1 = pPathNodes[curPathNodeFront].t; + if(nextPathNodeFront < NumPathNodes) + pos2 = pPathNodes[nextPathNodeFront].t; + else{ + nextPathNodeFront = 0; + pos2 = TotalLengthOfFlightPath; + } + while(pathPositionFront < pos1 || pathPositionFront > pos2){ + curPathNodeFront = (curPathNodeFront+1) % NumPathNodes; + nextPathNodeFront = curPathNodeFront + 1; + pos1 = pPathNodes[curPathNodeFront].t; + if(nextPathNodeFront < NumPathNodes) + pos2 = pPathNodes[nextPathNodeFront].t; + else{ + nextPathNodeFront = 0; + pos2 = TotalLengthOfFlightPath; + } + } + dist = pPathNodes[nextPathNodeFront].t - pPathNodes[curPathNodeFront].t; + if(dist < 0.0f) + dist += TotalLengthOfFlightPath; + f = (pathPositionFront - pPathNodes[curPathNodeFront].t)/dist; + CVector posFront = (1.0f - f)*pPathNodes[curPathNodeFront].p + f*pPathNodes[nextPathNodeFront].p; + + // And for another point 60 units in front of the plane, used to calculate roll + float pathPositionFront2 = pathPositionFront + 60.0f; + if(pathPositionFront2 > TotalLengthOfFlightPath) + pathPositionFront2 -= TotalLengthOfFlightPath; + int curPathNodeFront2 = m_nCurPathNode; + int nextPathNodeFront2 = curPathNodeFront2 + 1; + pos1 = pPathNodes[curPathNodeFront2].t; + if(nextPathNodeFront2 < NumPathNodes) + pos2 = pPathNodes[nextPathNodeFront2].t; + else{ + nextPathNodeFront2 = 0; + pos2 = TotalLengthOfFlightPath; + } + while(pathPositionFront2 < pos1 || pathPositionFront2 > pos2){ + curPathNodeFront2 = (curPathNodeFront2+1) % NumPathNodes; + nextPathNodeFront2 = curPathNodeFront2 + 1; + pos1 = pPathNodes[curPathNodeFront2].t; + if(nextPathNodeFront2 < NumPathNodes) + pos2 = pPathNodes[nextPathNodeFront2].t; + else{ + nextPathNodeFront2 = 0; + pos2 = TotalLengthOfFlightPath; + } + } + dist = pPathNodes[nextPathNodeFront2].t - pPathNodes[curPathNodeFront2].t; + if(dist < 0.0f) + dist += TotalLengthOfFlightPath; + f = (pathPositionFront2 - pPathNodes[curPathNodeFront2].t)/dist; + CVector posFront2 = (1.0f - f)*pPathNodes[curPathNodeFront2].p + f*pPathNodes[nextPathNodeFront2].p; + + // Now set matrix + GetMatrix().SetTranslateOnly((posRear + posFront) / 2.0f); + GetMatrix().GetPosition().z += 4.3f; + CVector fwd = posFront - posRear; + fwd.Normalise(); + if(pitch != 0.0f){ + fwd.z += 0.4f*pitch; + fwd.Normalise(); + } + CVector fwd2 = posFront2 - posRear; + fwd2.Normalise(); + CVector roll = CrossProduct(fwd, fwd2); + CVector right = CrossProduct(fwd, CVector(0.0f, 0.0f, 1.0f)); + if(!bothOnGround) + right.z += 3.0f*roll.z; + right.Normalise(); + CVector up = CrossProduct(right, fwd); + GetMatrix().GetRight() = right; + GetMatrix().GetUp() = up; + GetMatrix().GetForward() = fwd; + // Set speed + m_vecMoveSpeed = fwd*PlanePathSpeed[m_nPlaneId]/60.0f; + m_fSpeed = PlanePathSpeed[m_nPlaneId]/60.0f; + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + + m_isFarAway = !((posFront - TheCamera.GetPosition()).MagnitudeSqr2D() < sq(300.0f)); + }else{ + float planePathPosition; + float totalLengthOfFlightPath; + CPlaneNode *pathNodes; + float planePathSpeed; + int numPathNodes; + + if(GetModelIndex() == MI_CHOPPER){ + planePathPosition = PlanePath3Position[m_nPlaneId]; + totalLengthOfFlightPath = TotalLengthOfFlightPath3; + pathNodes = pPath3Nodes; + planePathSpeed = PlanePath3Speed[m_nPlaneId]; + numPathNodes = NumPath3Nodes; + }else{ + planePathPosition = PlanePath2Position[m_nPlaneId]; + totalLengthOfFlightPath = TotalLengthOfFlightPath2; + pathNodes = pPath2Nodes; + planePathSpeed = PlanePath2Speed[m_nPlaneId]; + numPathNodes = NumPath2Nodes; + } + + // Advance current node to appropriate position + float pathPositionRear = planePathPosition - 10.0f; + if(pathPositionRear < 0.0f) + pathPositionRear += totalLengthOfFlightPath; + float pos1, pos2; + int nextTrackNode = m_nCurPathNode + 1; + pos1 = pathNodes[m_nCurPathNode].t; + if(nextTrackNode < numPathNodes) + pos2 = pathNodes[nextTrackNode].t; + else{ + nextTrackNode = 0; + pos2 = totalLengthOfFlightPath; + } + while(pathPositionRear < pos1 || pathPositionRear > pos2){ + m_nCurPathNode = (m_nCurPathNode+1) % numPathNodes; + nextTrackNode = m_nCurPathNode + 1; + pos1 = pathNodes[m_nCurPathNode].t; + if(nextTrackNode < numPathNodes) + pos2 = pathNodes[nextTrackNode].t; + else{ + nextTrackNode = 0; + pos2 = totalLengthOfFlightPath; + } + } + float dist = pathNodes[nextTrackNode].t - pathNodes[m_nCurPathNode].t; + if(dist < 0.0f) + dist += totalLengthOfFlightPath; + float f = (pathPositionRear - pathNodes[m_nCurPathNode].t)/dist; + CVector posRear = (1.0f - f)*pathNodes[m_nCurPathNode].p + f*pathNodes[nextTrackNode].p; + + // Same for the front + float pathPositionFront = pathPositionRear + 20.0f; + if(pathPositionFront > totalLengthOfFlightPath) + pathPositionFront -= totalLengthOfFlightPath; + int curPathNodeFront = m_nCurPathNode; + int nextPathNodeFront = curPathNodeFront + 1; + pos1 = pathNodes[curPathNodeFront].t; + if(nextPathNodeFront < numPathNodes) + pos2 = pathNodes[nextPathNodeFront].t; + else{ + nextPathNodeFront = 0; + pos2 = totalLengthOfFlightPath; + } + while(pathPositionFront < pos1 || pathPositionFront > pos2){ + curPathNodeFront = (curPathNodeFront+1) % numPathNodes; + nextPathNodeFront = curPathNodeFront + 1; + pos1 = pathNodes[curPathNodeFront].t; + if(nextPathNodeFront < numPathNodes) + pos2 = pathNodes[nextPathNodeFront].t; + else{ + nextPathNodeFront = 0; + pos2 = totalLengthOfFlightPath; + } + } + dist = pathNodes[nextPathNodeFront].t - pathNodes[curPathNodeFront].t; + if(dist < 0.0f) + dist += totalLengthOfFlightPath; + f = (pathPositionFront - pathNodes[curPathNodeFront].t)/dist; + CVector posFront = (1.0f - f)*pathNodes[curPathNodeFront].p + f*pathNodes[nextPathNodeFront].p; + + // And for another point 30 units in front of the plane, used to calculate roll + float pathPositionFront2 = pathPositionFront + 30.0f; + if(pathPositionFront2 > totalLengthOfFlightPath) + pathPositionFront2 -= totalLengthOfFlightPath; + int curPathNodeFront2 = m_nCurPathNode; + int nextPathNodeFront2 = curPathNodeFront2 + 1; + pos1 = pathNodes[curPathNodeFront2].t; + if(nextPathNodeFront2 < numPathNodes) + pos2 = pathNodes[nextPathNodeFront2].t; + else{ + nextPathNodeFront2 = 0; + pos2 = totalLengthOfFlightPath; + } + while(pathPositionFront2 < pos1 || pathPositionFront2 > pos2){ + curPathNodeFront2 = (curPathNodeFront2+1) % numPathNodes; + nextPathNodeFront2 = curPathNodeFront2 + 1; + pos1 = pathNodes[curPathNodeFront2].t; + if(nextPathNodeFront2 < numPathNodes) + pos2 = pathNodes[nextPathNodeFront2].t; + else{ + nextPathNodeFront2 = 0; + pos2 = totalLengthOfFlightPath; + } + } + dist = pathNodes[nextPathNodeFront2].t - pathNodes[curPathNodeFront2].t; + if(dist < 0.0f) + dist += totalLengthOfFlightPath; + f = (pathPositionFront2 - pathNodes[curPathNodeFront2].t)/dist; + CVector posFront2 = (1.0f - f)*pathNodes[curPathNodeFront2].p + f*pathNodes[nextPathNodeFront2].p; + + // Now set matrix + GetMatrix().SetTranslateOnly((posRear + posFront) / 2.0f); + GetMatrix().GetPosition().z += 1.0f; + CVector fwd = posFront - posRear; + fwd.Normalise(); + CVector fwd2 = posFront2 - posRear; + fwd2.Normalise(); + CVector roll = CrossProduct(fwd, fwd2); + CVector right = CrossProduct(fwd, CVector(0.0f, 0.0f, 1.0f)); + right.z += 3.0f*roll.z; + right.Normalise(); + CVector up = CrossProduct(right, fwd); + GetMatrix().GetRight() = right; + GetMatrix().GetUp() = up; + GetMatrix().GetForward() = fwd; + + // Set speed + m_vecMoveSpeed = fwd*planePathSpeed/60.0f; + m_fSpeed = planePathSpeed/60.0f; + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + + m_isFarAway = !((posFront - TheCamera.GetPosition()).MagnitudeSqr2D() < sq(300.0f)); + } + } + + bIsInSafePosition = true; + GetMatrix().UpdateRW(); + UpdateRwFrame(); + + // Handle streaming and such + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + if(m_isFarAway){ + // Switch to LOD model + if(m_rwObject && RwObjectGetType(m_rwObject) == rpCLUMP){ + DeleteRwObject(); + if(mi->m_planeLodId != -1){ + PUSH_MEMID(MEMID_WORLD); + m_rwObject = CModelInfo::GetModelInfo(mi->m_planeLodId)->CreateInstance(); + POP_MEMID(); + if(m_rwObject) + GetMatrix().AttachRW(RwFrameGetMatrix(RpAtomicGetFrame((RpAtomic*)m_rwObject))); + } + } + }else if(CStreaming::HasModelLoaded(GetModelIndex())){ + if(m_rwObject && RwObjectGetType(m_rwObject) == rpATOMIC){ + // Get rid of LOD model + GetMatrix().Detach(); + if(m_rwObject){ // useless check + if(RwObjectGetType(m_rwObject) == rpATOMIC){ // useless check + RwFrame *f = RpAtomicGetFrame((RpAtomic*)m_rwObject); + RpAtomicDestroy((RpAtomic*)m_rwObject); + RwFrameDestroy(f); + } + m_rwObject = nil; + } + } + // Set high detail model + if(m_rwObject == nil){ + int id = GetModelIndex(); + m_modelIndex = -1; + SetModelIndex(id); + } + }else{ + CStreaming::RequestModel(GetModelIndex(), STREAMFLAGS_DEPENDENCY); + } +} + +void +CPlane::PreRender(void) +{ + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + CVector lookVector = GetPosition() - TheCamera.GetPosition(); + float camDist = lookVector.Magnitude(); + if(camDist != 0.0f) + lookVector *= 1.0f/camDist; + else + lookVector = CVector(1.0f, 0.0f, 0.0f); + float behindness = DotProduct(lookVector, GetForward()); + + // Wing lights + if(behindness < 0.0f){ + // in front of plane + CVector lightPos = mi->m_positions[PLANE_POS_LIGHT_RIGHT]; + CVector lightR = GetMatrix() * lightPos; + CVector lightL = lightR; + lightL -= GetRight()*2.0f*lightPos.x; + + float intensity = -0.6f*behindness + 0.4f; + float size = 1.0f - behindness; + + if(behindness < -0.9f && camDist < 50.0f){ + // directly in front + CCoronas::RegisterCorona((uintptr)this + 10, 255*intensity, 255*intensity, 255*intensity, 255, + lightL, size, 240.0f, + CCoronas::TYPE_NORMAL, CCoronas::FLARE_HEADLIGHTS, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + CCoronas::RegisterCorona((uintptr)this + 11, 255*intensity, 255*intensity, 255*intensity, 255, + lightR, size, 240.0f, + CCoronas::TYPE_NORMAL, CCoronas::FLARE_HEADLIGHTS, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + }else{ + CCoronas::RegisterCorona((uintptr)this + 10, 255*intensity, 255*intensity, 255*intensity, 255, + lightL, size, 240.0f, + CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + CCoronas::RegisterCorona((uintptr)this + 11, 255*intensity, 255*intensity, 255*intensity, 255, + lightR, size, 240.0f, + CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + } + } + + // Tail light + if(CTimer::GetTimeInMilliseconds() & 0x200){ + CVector pos = GetMatrix() * mi->m_positions[PLANE_POS_LIGHT_TAIL]; + + CCoronas::RegisterCorona((uintptr)this + 12, 255, 0, 0, 255, + pos, 1.0f, 120.0f, + CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + } + +#ifdef CPLANE_ROTORS + CMatrix mat; + CVector pos; + m_fRotorRotation += 3.14f/6.5f; + if(m_fRotorRotation > 6.28f) + m_fRotorRotation -= 6.28f; + + if(m_aPlaneNodes[PLANE_TOPROTOR]){ + mat.Attach(RwFrameGetMatrix(m_aPlaneNodes[PLANE_TOPROTOR])); + pos = mat.GetPosition(); + mat.SetRotateZ(m_fRotorRotation); + mat.Translate(pos); + mat.UpdateRW(); + } + if(m_aPlaneNodes[PLANE_BACKROTOR]){ + mat.Attach(RwFrameGetMatrix(m_aPlaneNodes[PLANE_BACKROTOR])); + pos = mat.GetPosition(); + mat.SetRotateX(m_fRotorRotation); + mat.Translate(pos); + mat.UpdateRW(); + } +#endif +} + +void +CPlane::Render(void) +{ + if(!CCutsceneMgr::IsRunning()) + CEntity::Render(); +} + +#define CRUISE_SPEED (50.0f) +#define TAXI_SPEED (5.0f) + +void +CPlane::InitPlanes(void) +{ + int i; + + CesnaMissionStatus = CESNA_STATUS_NONE; + + // Jumbo + if(pPathNodes == nil){ + pPathNodes = LoadPath("data\\paths\\flight.dat", NumPathNodes, TotalLengthOfFlightPath, true); + + // Figure out which nodes are on ground + for(i = 0; i < NumPathNodes; i++){ + if(pPathNodes[i].p.z < 14.0f){ + pPathNodes[i].p.z = 14.0f; + pPathNodes[i].bOnGround = true; + }else + pPathNodes[i].bOnGround = false; + } + + // Find lading and takeoff points + LandingPoint = -1.0f; + TakeOffPoint = -1.0f; + bool lastOnGround = pPathNodes[NumPathNodes-1].bOnGround; + for(i = 0; i < NumPathNodes; i++){ + if(pPathNodes[i].bOnGround && !lastOnGround) + LandingPoint = pPathNodes[i].t; + else if(!pPathNodes[i].bOnGround && lastOnGround) + TakeOffPoint = pPathNodes[i].t; + lastOnGround = pPathNodes[i].bOnGround; + } + + // Animation + float time = 0.0f; + float position = 0.0f; + // Start on ground with slow speed + aPlaneLineBits[0].type = 1; + aPlaneLineBits[0].time = time; + aPlaneLineBits[0].position = position; + aPlaneLineBits[0].speed = TAXI_SPEED; + aPlaneLineBits[0].acceleration = 0.0f; + float dist = (TakeOffPoint-500.0f) - position; + time += dist/TAXI_SPEED; + position += dist; + + // Accelerate to take off + aPlaneLineBits[1].type = 2; + aPlaneLineBits[1].time = time; + aPlaneLineBits[1].position = position; + aPlaneLineBits[1].speed = TAXI_SPEED; + aPlaneLineBits[1].acceleration = 618.75f/500.0f; + time += 500.0f/((CRUISE_SPEED+TAXI_SPEED)/2.0f); + position += 500.0f; + + // Fly at cruise speed + aPlaneLineBits[2].type = 1; + aPlaneLineBits[2].time = time; + aPlaneLineBits[2].position = position; + aPlaneLineBits[2].speed = CRUISE_SPEED; + aPlaneLineBits[2].acceleration = 0.0f; + dist = LandingPoint - TakeOffPoint; + time += dist/CRUISE_SPEED; + position += dist; + + // Brake after landing + aPlaneLineBits[3].type = 2; + aPlaneLineBits[3].time = time; + aPlaneLineBits[3].position = position; + aPlaneLineBits[3].speed = CRUISE_SPEED; + aPlaneLineBits[3].acceleration = -618.75f/500.0f; + time += 500.0f/((CRUISE_SPEED+TAXI_SPEED)/2.0f); + position += 500.0f; + + // Taxi + aPlaneLineBits[4].type = 1; + aPlaneLineBits[4].time = time; + aPlaneLineBits[4].position = position; + aPlaneLineBits[4].speed = TAXI_SPEED; + aPlaneLineBits[4].acceleration = 0.0f; + time += (TotalLengthOfFlightPath - position)/TAXI_SPEED; + + // end + aPlaneLineBits[5].time = time; + TotalDurationOfFlightPath = time; + } + + // Dodo + if(pPath2Nodes == nil){ + pPath2Nodes = LoadPath("data\\paths\\flight2.dat", NumPath2Nodes, TotalLengthOfFlightPath2, true); + TotalDurationOfFlightPath2 = TotalLengthOfFlightPath2/CRUISE_SPEED; + } + + // Heli + if(pPath3Nodes == nil){ + pPath3Nodes = LoadPath("data\\paths\\flight3.dat", NumPath3Nodes, TotalLengthOfFlightPath3, false); + TotalDurationOfFlightPath3 = TotalLengthOfFlightPath3/CRUISE_SPEED; + } + + CStreaming::LoadAllRequestedModels(false); + CStreaming::RequestModel(MI_AIRTRAIN, 0); + CStreaming::LoadAllRequestedModels(false); + + // NB: 3 hardcoded also in CPlaneTrails + for(i = 0; i < 3; i++){ + CPlane *plane = new CPlane(MI_AIRTRAIN, PERMANENT_VEHICLE); + plane->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); + plane->SetStatus(STATUS_ABANDONED); + plane->bIsLocked = true; + plane->m_nPlaneId = i; + plane->m_nCurPathNode = 0; + CWorld::Add(plane); + } +} + +void +CPlane::Shutdown(void) +{ + delete[] pPathNodes; + delete[] pPath2Nodes; + delete[] pPath3Nodes; + delete[] pPath4Nodes; + pPathNodes = nil; + pPath2Nodes = nil; + pPath3Nodes = nil; + pPath4Nodes = nil; +} + +CPlaneNode* +CPlane::LoadPath(char const *filename, int32 &numNodes, float &totalLength, bool loop) +{ + int bp, lp; + int i; + + CFileMgr::LoadFile(filename, work_buff, sizeof(work_buff), "r"); + *gString = '\0'; + for(bp = 0, lp = 0; work_buff[bp] != '\n'; bp++, lp++) + gString[lp] = work_buff[bp]; + bp++; + gString[lp] = '\0'; + sscanf(gString, "%d", &numNodes); + CPlaneNode *nodes = new CPlaneNode[numNodes]; + + for(i = 0; i < numNodes; i++){ + for(lp = 0; work_buff[bp] != '\n' && work_buff[bp] != '\0'; bp++, lp++) + gString[lp] = work_buff[bp]; + bp++; + // BUG: game doesn't terminate string + gString[lp] = '\0'; + sscanf(gString, "%f %f %f", &nodes[i].p.x, &nodes[i].p.y, &nodes[i].p.z); + } + + // Calculate length of segments and path + totalLength = 0.0f; + for(i = 0; i < numNodes; i++){ + nodes[i].t = totalLength; + float l = (nodes[(i+1) % numNodes].p - nodes[i].p).Magnitude2D(); + if(!loop && i == numNodes-1) + l = 0.0f; + totalLength += l; + } + + return nodes; +} + +int32 LastTimeInPlane, LastTimeNotInPlane; +bool bCesnasActivated; +bool bHelisActivated; + +void +CPlane::UpdatePlanes(void) +{ + int i, j; + uint32 time; + float t, deltaT; + + if(CReplay::IsPlayingBack()) + return; + + // Jumbo jets + time = CTimer::GetTimeInMilliseconds(); + for(i = 0; i < 3; i++){ + t = TotalDurationOfFlightPath * (float)(time & 0x7FFFF)/0x80000; + // find current frame + for(j = 0; t > aPlaneLineBits[j+1].time; j++); + + OldPlanePathPosition[i] = PlanePathPosition[i]; + deltaT = t - aPlaneLineBits[j].time; + switch(aPlaneLineBits[j].type){ + case 0: // standing still + PlanePathPosition[i] = aPlaneLineBits[j].position; + PlanePathSpeed[i] = 0.0f; + break; + case 1: // moving with constant speed + PlanePathPosition[i] = aPlaneLineBits[j].position + aPlaneLineBits[j].speed*deltaT; + PlanePathSpeed[i] = (TotalDurationOfFlightPath*1000.0f/0x80000) * aPlaneLineBits[j].speed; + break; + case 2: // accelerating/braking + PlanePathPosition[i] = aPlaneLineBits[j].position + (aPlaneLineBits[j].speed + aPlaneLineBits[j].acceleration*deltaT)*deltaT; + PlanePathSpeed[i] = (TotalDurationOfFlightPath*1000.0f/0x80000)*aPlaneLineBits[j].speed + 2.0f*aPlaneLineBits[j].acceleration*deltaT; + break; + } + + // time offset for each plane + time += 0x80000/3; + } + + time = CTimer::GetTimeInMilliseconds(); + + t = TotalDurationOfFlightPath2/0x80000; + PlanePath2Position[0] = CRUISE_SPEED * (time & 0x7FFFF)*t; + PlanePath2Position[1] = CRUISE_SPEED * ((time + 0x80000/5) & 0x7FFFF)*t; + PlanePath2Position[2] = CRUISE_SPEED * ((time + 0x80000/5*2) & 0x7FFFF)*t; + PlanePath2Position[3] = CRUISE_SPEED * ((time + 0x80000/5*3) & 0x7FFFF)*t; + PlanePath2Position[4] = CRUISE_SPEED * ((time + 0x80000/5*4) & 0x7FFFF)*t; + PlanePath2Speed[0] = CRUISE_SPEED*t; + PlanePath2Speed[1] = CRUISE_SPEED*t; + PlanePath2Speed[2] = CRUISE_SPEED*t; + PlanePath2Speed[3] = CRUISE_SPEED*t; + PlanePath2Speed[4] = CRUISE_SPEED*t; + + t = TotalDurationOfFlightPath3/0x80000; + PlanePath3Position[0] = CRUISE_SPEED * (time & 0x7FFFF)*t; + PlanePath3Position[1] = CRUISE_SPEED * ((time + 0x80000/4) & 0x7FFFF)*t; + PlanePath3Position[2] = CRUISE_SPEED * ((time + 0x80000/4*2) & 0x7FFFF)*t; + PlanePath3Position[3] = CRUISE_SPEED * ((time + 0x80000/4*3) & 0x7FFFF)*t; + PlanePath3Speed[0] = CRUISE_SPEED*t; + PlanePath3Speed[1] = CRUISE_SPEED*t; + PlanePath3Speed[2] = CRUISE_SPEED*t; + PlanePath3Speed[3] = CRUISE_SPEED*t; + + if(FindPlayerVehicle() && (FindPlayerVehicle()->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI || + FindPlayerVehicle()->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE)) + LastTimeInPlane = CTimer::GetTimeInMilliseconds(); + else + LastTimeNotInPlane = CTimer::GetTimeInMilliseconds(); + + if(CTimer::GetTimeInMilliseconds() - LastTimeNotInPlane > 10000){ + if(!bCesnasActivated){ + if(CStreaming::HasModelLoaded(MI_DEADDODO)){ + for(i = 0; i < 5; i++){ + CPlane *plane = new CPlane(MI_DEADDODO, PERMANENT_VEHICLE); + plane->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); + plane->SetStatus(STATUS_ABANDONED); + plane->bIsLocked = true; + plane->m_nPlaneId = i; + plane->m_nCurPathNode = 0; + plane->m_bTempPlane = true; + CWorld::Add(plane); + } + bCesnasActivated = true; + }else + CStreaming::RequestModel(MI_DEADDODO, 0); + } + + if(!bHelisActivated){ + if(CStreaming::HasModelLoaded(MI_CHOPPER)){ + for(i = 0; i < 4; i++){ + CPlane *plane = new CPlane(MI_CHOPPER, PERMANENT_VEHICLE); + plane->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); + plane->SetStatus(STATUS_ABANDONED); + plane->bIsLocked = true; + plane->m_nPlaneId = i; + plane->m_nCurPathNode = 0; + plane->m_bTempPlane = true; + CWorld::Add(plane); + } + bHelisActivated = true; + }else + CStreaming::RequestModel(MI_CHOPPER, 0); + } + }else if(CTimer::GetTimeInMilliseconds() - LastTimeInPlane > 10000) + RemoveTemporaryPlanes(); +} + +void +CPlane::RemoveTemporaryPlanes(void) +{ + int i; + if(!bHelisActivated && !bCesnasActivated) + return; + + i = CPools::GetVehiclePool()->GetSize(); + while(--i >= 0){ + CPlane *plane = (CPlane*)CPools::GetVehiclePool()->GetSlot(i); + if(plane && plane->IsPlane() && plane->m_bTempPlane){ + CWorld::Remove(plane); + delete plane; + } + } + bCesnasActivated = false; + bHelisActivated = false; +} + +bool +CPlane::TestRocketCollision(CVector *rocketPos) +{ + int i; + + i = CPools::GetVehiclePool()->GetSize(); + while(--i >= 0){ + CPlane *plane = (CPlane*)CPools::GetVehiclePool()->GetSlot(i); + if(plane && +#ifdef EXPLODING_AIRTRAIN + (plane->GetModelIndex() == MI_AIRTRAIN || plane->GetModelIndex() == MI_DEADDODO) && +#else + plane->GetModelIndex() != MI_AIRTRAIN && plane->GetModelIndex() == MI_DEADDODO && // strange check +#endif + !plane->m_bHasBeenHit && (*rocketPos - plane->GetPosition()).Magnitude() < 25.0f){ + plane->m_nFrameWhenHit = CTimer::GetFrameCounter(); + plane->m_bHasBeenHit = true; + CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->RegisterCrime_Immediately(CRIME_DESTROYED_CESSNA, + plane->GetPosition(), i+1983, false); + return true; + } + } + return false; +} + +// unused +// BUG: not in CPlane in the game +void +CPlane::CreateIncomingCesna(void) +{ + if(CesnaMissionStatus == CESNA_STATUS_FLYING){ + CWorld::Remove(pDrugRunCesna); + delete pDrugRunCesna; + pDrugRunCesna = nil; + } + pDrugRunCesna = new CPlane(MI_DEADDODO, PERMANENT_VEHICLE); + pDrugRunCesna->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); + pDrugRunCesna->SetStatus(STATUS_ABANDONED); + pDrugRunCesna->bIsLocked = true; + pDrugRunCesna->m_nPlaneId = 0; + pDrugRunCesna->m_nCurPathNode = 0; + pDrugRunCesna->m_bIsDrugRunCesna = true; + CWorld::Add(pDrugRunCesna); + + CesnaMissionStatus = CESNA_STATUS_FLYING; + CesnaMissionStartTime = CTimer::GetTimeInMilliseconds(); + printf("CPlane::CreateIncomingCesna(void)\n"); +} + +// unused +void +CPlane::CreateDropOffCesna(void) +{ + if(DropOffCesnaMissionStatus == CESNA_STATUS_FLYING){ + CWorld::Remove(pDropOffCesna); + delete pDropOffCesna; + pDropOffCesna = nil; + } + pDropOffCesna = new CPlane(MI_DEADDODO, PERMANENT_VEHICLE); + pDropOffCesna->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); + pDropOffCesna->SetStatus(STATUS_ABANDONED); + pDropOffCesna->bIsLocked = true; + pDropOffCesna->m_nPlaneId = 0; + pDropOffCesna->m_nCurPathNode = 0; + pDropOffCesna->m_bIsDropOffCesna = true; + CWorld::Add(pDropOffCesna); + + DropOffCesnaMissionStatus = CESNA_STATUS_FLYING; + DropOffCesnaMissionStartTime = CTimer::GetTimeInMilliseconds(); + printf("CPlane::CreateDropOffCesna(void)\n"); +} + +// all unused +const CVector CPlane::FindDrugPlaneCoordinates(void) { return pDrugRunCesna->GetPosition(); } +const CVector CPlane::FindDropOffCesnaCoordinates(void) { return pDropOffCesna->GetPosition(); } +bool CPlane::HasCesnaLanded(void) { return CesnaMissionStatus == CESNA_STATUS_LANDED; } +bool CPlane::HasCesnaBeenDestroyed(void) { return CesnaMissionStatus == CESNA_STATUS_DESTROYED; } +bool CPlane::HasDropOffCesnaBeenShotDown(void) { return DropOffCesnaMissionStatus == CESNA_STATUS_DESTROYED; } + +void +CPlane::Load(void) +{ + RemoveTemporaryPlanes(); +} + +void +CPlane::Save(void) +{ + RemoveTemporaryPlanes(); +} diff --git a/src/miami/vehicles/Plane.h b/src/miami/vehicles/Plane.h new file mode 100644 index 00000000..c8f02048 --- /dev/null +++ b/src/miami/vehicles/Plane.h @@ -0,0 +1,81 @@ +#pragma once + +#include "Vehicle.h" + +enum ePlaneNodes +{ +#ifdef CPLANE_ROTORS + // for heli + PLANE_TOPROTOR, + PLANE_BACKROTOR, +#endif + PLANE_WHEEL_FRONT = 2, + PLANE_WHEEL_READ, + NUM_PLANE_NODES +}; + +struct CPlaneNode +{ + CVector p; // position + float t; // xy-distance from start on path + bool bOnGround; // i.e. not flying +}; + +struct CPlaneInterpolationLine +{ + uint8 type; + float time; // when does this keyframe start + // initial values at start of frame + float position; + float speed; + float acceleration; +}; + +class CPlane : public CVehicle +{ +public: +#ifdef CPLANE_ROTORS + RwFrame *m_aPlaneNodes[NUM_PLANE_NODES]; + float m_fRotorRotation; +#endif + int16 m_nPlaneId; + int16 m_isFarAway; + int16 m_nCurPathNode; + float m_fSpeed; + uint32 m_nFrameWhenHit; + bool m_bHasBeenHit; + bool m_bIsDrugRunCesna; + bool m_bIsDropOffCesna; + bool m_bTempPlane; + + CPlane(int32 id, uint8 CreatedBy); + ~CPlane(void); + + // from CEntity + void SetModelIndex(uint32 id); + void DeleteRwObject(void); + void ProcessControl(void); + void PreRender(void); + void Render(void); + void FlagToDestroyWhenNextProcessed() { bRemoveFromWorld = true; } + + static void InitPlanes(void); + static void Shutdown(void); + static CPlaneNode *LoadPath(char const *filename, int32 &numNodes, float &totalLength, bool loop); + static void RemoveTemporaryPlanes(void); + static void UpdatePlanes(void); + static bool TestRocketCollision(CVector *rocketPos); + static void CreateIncomingCesna(void); + static void CreateDropOffCesna(void); + static const CVector FindDrugPlaneCoordinates(void); + static const CVector FindDropOffCesnaCoordinates(void); + static bool HasCesnaLanded(void); + static bool HasCesnaBeenDestroyed(void); + static bool HasDropOffCesnaBeenShotDown(void); + static void Load(void); + static void Save(void); +}; + +extern float LandingPoint; +extern float TakeOffPoint; +extern float PlanePathPosition[3]; diff --git a/src/miami/vehicles/Train.cpp b/src/miami/vehicles/Train.cpp new file mode 100644 index 00000000..3a04b614 --- /dev/null +++ b/src/miami/vehicles/Train.cpp @@ -0,0 +1,766 @@ +#include "common.h" +#include "main.h" + +#include "Timer.h" +#include "ModelIndices.h" +#include "FileMgr.h" +#include "Streaming.h" +#include "Pad.h" +#include "Camera.h" +#include "Coronas.h" +#include "World.h" +#include "Ped.h" +#include "DMAudio.h" +#include "HandlingMgr.h" +#include "Train.h" +#include "AudioScriptObject.h" + +static CTrainNode* pTrackNodes; +static int16 NumTrackNodes; +static float StationDist[3] = { 873.0f, 1522.0f, 2481.0f }; +static float TotalLengthOfTrack; +static float TotalDurationOfTrack; +static CTrainInterpolationLine aLineBits[17]; +static float EngineTrackPosition[2]; +static float EngineTrackSpeed[2]; + +static CTrainNode* pTrackNodes_S; +static int16 NumTrackNodes_S; +static float StationDist_S[4] = { 55.0f, 1388.0f, 2337.0f, 3989.0f }; +static float TotalLengthOfTrack_S; +static float TotalDurationOfTrack_S; +static CTrainInterpolationLine aLineBits_S[18]; +static float EngineTrackPosition_S[4]; +static float EngineTrackSpeed_S[4]; + +CVector CTrain::aStationCoors[3]; +CVector CTrain::aStationCoors_S[4]; + +static bool bTrainArrivalAnnounced[3] = {false, false, false}; + +CTrain::CTrain(int32 id, uint8 CreatedBy) + : CVehicle(CreatedBy) +{ +#ifdef GTA_TRAIN + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); + m_vehType = VEHICLE_TYPE_TRAIN; + pHandling = mod_HandlingManager.GetHandlingData((tVehicleType)mi->m_handlingId); + SetModelIndex(id); + + Doors[0].Init(0.8f, 0.0f, 1, 0); + Doors[1].Init(-0.8f, 0.0f, 0, 0); + + m_fMass = 100000000.0f; + m_fTurnMass = 100000000.0f; + m_fAirResistance = 0.9994f; + m_fElasticity = 0.05f; + + m_bProcessDoor = true; + m_bTrainStopping = false; + m_nTrackId = TRACK_ELTRAIN; + m_nNumMaxPassengers = 5; + m_nDoorTimer = CTimer::GetTimeInMilliseconds(); + m_nDoorState = TRAIN_DOOR_CLOSED; + + bUsesCollision = true; + SetStatus(STATUS_TRAIN_MOVING); + +#ifdef FIX_BUGS + m_isFarAway = true; +#endif +#else + assert(0 && "No trains in this game"); +#endif +} + +void +CTrain::SetModelIndex(uint32 id) +{ +#ifdef GTA_TRAIN + int i; + + CVehicle::SetModelIndex(id); + for(i = 0; i < NUM_TRAIN_NODES; i++) + m_aTrainNodes[i] = nil; + CClumpModelInfo::FillFrameArray(GetClump(), m_aTrainNodes); +#endif +} + +void +CTrain::ProcessControl(void) +{ +#ifdef GTA_TRAIN + if(gbModelViewer || m_isFarAway && (CTimer::GetFrameCounter() + m_nWagonId) & 0xF) + return; + + CTrainNode *trackNodes; + int16 numTrackNodes; + float totalLengthOfTrack; + float *engineTrackPosition; + float *engineTrackSpeed; + + if(m_nTrackId == TRACK_SUBWAY){ + trackNodes = pTrackNodes_S; + numTrackNodes = NumTrackNodes_S; + totalLengthOfTrack = TotalLengthOfTrack_S; + engineTrackPosition = EngineTrackPosition_S; + engineTrackSpeed = EngineTrackSpeed_S; + }else{ + trackNodes = pTrackNodes; + numTrackNodes = NumTrackNodes; + totalLengthOfTrack = TotalLengthOfTrack; + engineTrackPosition = EngineTrackPosition; + engineTrackSpeed = EngineTrackSpeed; + } + + float trackPositionRear = engineTrackPosition[m_nWagonGroup] - m_fWagonPosition; + if(trackPositionRear < 0.0f) + trackPositionRear += totalLengthOfTrack; + + // Advance current node to appropriate position + float pos1, pos2; + int nextTrackNode = m_nCurTrackNode + 1; + pos1 = trackNodes[m_nCurTrackNode].t; + if(nextTrackNode < numTrackNodes) + pos2 = trackNodes[nextTrackNode].t; + else{ + nextTrackNode = 0; + pos2 = totalLengthOfTrack; + } + while(trackPositionRear < pos1 || trackPositionRear > pos2){ + m_nCurTrackNode = (m_nCurTrackNode+1) % numTrackNodes; + nextTrackNode = m_nCurTrackNode + 1; + pos1 = trackNodes[m_nCurTrackNode].t; + if(nextTrackNode < numTrackNodes) + pos2 = trackNodes[nextTrackNode].t; + else{ + nextTrackNode = 0; + pos2 = totalLengthOfTrack; + } + } + float dist = trackNodes[nextTrackNode].t - trackNodes[m_nCurTrackNode].t; + if(dist < 0.0f) + dist += totalLengthOfTrack; + float f = (trackPositionRear - trackNodes[m_nCurTrackNode].t)/dist; + CVector posRear = (1.0f - f)*trackNodes[m_nCurTrackNode].p + f*trackNodes[nextTrackNode].p; + + // Now same again for the front + float trackPositionFront = trackPositionRear + 20.0f; + if(trackPositionFront > totalLengthOfTrack) + trackPositionFront -= totalLengthOfTrack; + int curTrackNodeFront = m_nCurTrackNode; + int nextTrackNodeFront = curTrackNodeFront + 1; + pos1 = trackNodes[curTrackNodeFront].t; + if(nextTrackNodeFront < numTrackNodes) + pos2 = trackNodes[nextTrackNodeFront].t; + else{ + nextTrackNodeFront = 0; + pos2 = totalLengthOfTrack; + } + while(trackPositionFront < pos1 || trackPositionFront > pos2){ + curTrackNodeFront = (curTrackNodeFront+1) % numTrackNodes; + nextTrackNodeFront = curTrackNodeFront + 1; + pos1 = trackNodes[curTrackNodeFront].t; + if(nextTrackNodeFront < numTrackNodes) + pos2 = trackNodes[nextTrackNodeFront].t; + else{ + nextTrackNodeFront = 0; + pos2 = totalLengthOfTrack; + } + } + dist = trackNodes[nextTrackNodeFront].t - trackNodes[curTrackNodeFront].t; + if(dist < 0.0f) + dist += totalLengthOfTrack; + f = (trackPositionFront - trackNodes[curTrackNodeFront].t)/dist; + CVector posFront = (1.0f - f)*trackNodes[curTrackNodeFront].p + f*trackNodes[nextTrackNodeFront].p; + + // Now set matrix + SetPosition((posRear + posFront)/2.0f); + CVector fwd = posFront - posRear; + fwd.Normalise(); + CVector right = CrossProduct(fwd, CVector(0.0f, 0.0f, 1.0f)); + right.Normalise(); + CVector up = CrossProduct(right, fwd); + GetRight() = right; + GetUp() = up; + GetForward() = fwd; + + // Set speed + m_vecMoveSpeed = fwd*engineTrackSpeed[m_nWagonGroup]/60.0f; + m_fSpeed = engineTrackSpeed[m_nWagonGroup]/60.0f; + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + + if(engineTrackSpeed[m_nWagonGroup] > 0.001f){ + SetStatus(STATUS_TRAIN_MOVING); + m_bTrainStopping = false; + m_bProcessDoor = true; + }else{ + SetStatus(STATUS_TRAIN_NOT_MOVING); + m_bTrainStopping = true; + } + + m_isFarAway = !((posFront - TheCamera.GetPosition()).Magnitude2D() < sq(250.0f)); + + if(m_fWagonPosition == 20.0f && m_fSpeed > 0.0001f) + if(Abs(TheCamera.GetPosition().z - GetPosition().z) < 15.0f) + CPad::GetPad(0)->StartShake_Train(GetPosition().x, GetPosition().y); + + if(m_bProcessDoor) + switch(m_nDoorState){ + case TRAIN_DOOR_CLOSED: + if(m_bTrainStopping){ + m_nDoorTimer = CTimer::GetTimeInMilliseconds() + 1000; + m_nDoorState = TRAIN_DOOR_OPENING; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_TRAIN_DOOR_CLOSE, 0.0f); + } + break; + + case TRAIN_DOOR_OPENING: + if(CTimer::GetTimeInMilliseconds() < m_nDoorTimer){ + OpenTrainDoor(1.0f - (m_nDoorTimer - CTimer::GetTimeInMilliseconds())/1000.0f); + }else{ + OpenTrainDoor(1.0f); + m_nDoorState = TRAIN_DOOR_OPEN; + } + break; + + case TRAIN_DOOR_OPEN: + if(!m_bTrainStopping){ + m_nDoorTimer = CTimer::GetTimeInMilliseconds() + 1000; + m_nDoorState = TRAIN_DOOR_CLOSING; + DMAudio.PlayOneShot(m_audioEntityId, SOUND_TRAIN_DOOR_OPEN, 0.0f); + } + break; + + case TRAIN_DOOR_CLOSING: + if(CTimer::GetTimeInMilliseconds() < m_nDoorTimer){ + OpenTrainDoor((m_nDoorTimer - CTimer::GetTimeInMilliseconds())/1000.0f); + }else{ + OpenTrainDoor(0.0f); + m_nDoorState = TRAIN_DOOR_CLOSED; + m_bProcessDoor = false; + } + break; + } + + GetMatrix().UpdateRW(); + UpdateRwFrame(); + RemoveAndAdd(); + + bIsStuck = false; + bIsInSafePosition = true; + bWasPostponed = false; + + // request/remove model + if(m_isFarAway){ + if(m_rwObject) + DeleteRwObject(); + }else if(CStreaming::HasModelLoaded(MI_TRAIN)){ + if(m_rwObject == nil){ + m_modelIndex = -1; + SetModelIndex(MI_TRAIN); + } + }else{ + if(FindPlayerCoors().z * GetPosition().z >= 0.0f) + CStreaming::RequestModel(MI_TRAIN, STREAMFLAGS_DEPENDENCY); + } + + // Hit stuff + if(m_bIsFirstWagon && GetStatus()== STATUS_TRAIN_MOVING){ + CVector front = GetPosition() + GetForward()*GetColModel()->boundingBox.max.y + m_vecMoveSpeed*CTimer::GetTimeStep(); + + int x, xmin, xmax; + int y, ymin, ymax; + + xmin = CWorld::GetSectorIndexX(front.x - 3.0f); + if(xmin < 0) xmin = 0; + xmax = CWorld::GetSectorIndexX(front.x + 3.0f); + if(xmax > NUMSECTORS_X-1) xmax = NUMSECTORS_X-1; + ymin = CWorld::GetSectorIndexY(front.y - 3.0f); + if(ymin < 0) ymin = 0; + ymax = CWorld::GetSectorIndexY(front.y + 3.0f); + if(ymax > NUMSECTORS_Y-1) ymax = NUMSECTORS_X-1; + + CWorld::AdvanceCurrentScanCode(); + + for(y = ymin; y <= ymax; y++) + for(x = xmin; x <= xmax; x++){ + CSector *s = CWorld::GetSector(x, y); + TrainHitStuff(s->m_lists[ENTITYLIST_VEHICLES]); + TrainHitStuff(s->m_lists[ENTITYLIST_VEHICLES_OVERLAP]); + TrainHitStuff(s->m_lists[ENTITYLIST_PEDS]); + TrainHitStuff(s->m_lists[ENTITYLIST_PEDS_OVERLAP]); + } + } +#endif // GTA_TRAIN +} + +void +CTrain::PreRender(void) +{ +#ifdef GTA_TRAIN + CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); + + if(m_bIsFirstWagon){ + CVector lookVector = GetPosition() - TheCamera.GetPosition(); + float camDist = lookVector.Magnitude(); + if(camDist != 0.0f) + lookVector *= 1.0f/camDist; + else + lookVector = CVector(1.0f, 0.0f, 0.0f); + float behindness = DotProduct(lookVector, GetForward()); + + if(behindness < 0.0f){ + // In front of train + CVector lightPos = mi->m_positions[TRAIN_POS_LIGHT_FRONT]; + CVector lightR = GetMatrix() * lightPos; + CVector lightL = lightR; + lightL -= GetRight()*2.0f*lightPos.x; + + float intensity = -0.4f*behindness + 0.2f; + float size = 1.0f - behindness; + + if(behindness < -0.9f && camDist < 35.0f){ + // directly in front + CCoronas::RegisterCorona((uintptr)this + 10, 255*intensity, 255*intensity, 255*intensity, 255, + lightL, size, 80.0f, + CCoronas::TYPE_NORMAL, CCoronas::FLARE_HEADLIGHTS, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + CCoronas::RegisterCorona((uintptr)this + 11, 255*intensity, 255*intensity, 255*intensity, 255, + lightR, size, 80.0f, + CCoronas::TYPE_NORMAL, CCoronas::FLARE_HEADLIGHTS, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + }else{ + CCoronas::RegisterCorona((uintptr)this + 10, 255*intensity, 255*intensity, 255*intensity, 255, + lightL, size, 80.0f, + CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + CCoronas::RegisterCorona((uintptr)this + 11, 255*intensity, 255*intensity, 255*intensity, 255, + lightR, size, 80.0f, + CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + } + } + } + + if(m_bIsLastWagon){ + CVector lightPos = mi->m_positions[TRAIN_POS_LIGHT_REAR]; + CVector lightR = GetMatrix() * lightPos; + CVector lightL = lightR; + lightL -= GetRight()*2.0f*lightPos.x; + + CCoronas::RegisterCorona((uintptr)this + 12, 255, 0, 0, 255, + lightL, 1.0f, 80.0f, + CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + CCoronas::RegisterCorona((uintptr)this + 13, 255, 0, 0, 255, + lightR, 1.0f, 80.0f, + CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, + CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + } +#endif +} + +void +CTrain::Render(void) +{ +#ifdef GTA_TRAIN + CEntity::Render(); +#endif +} + +void +CTrain::TrainHitStuff(CPtrList &list) +{ +#ifdef GTA_TRAIN + CPtrNode *node; + CPhysical *phys; + + for(node = list.first; node; node = node->next){ + phys = (CPhysical*)node->item; + if(phys != this && Abs(this->GetPosition().z - phys->GetPosition().z) < 1.5f) + phys->bHitByTrain = true; + } +#endif +} + +void +CTrain::AddPassenger(CPed *ped) +{ +#ifdef GTA_TRAIN + int i = ped->m_vehDoor; + if((i == TRAIN_POS_LEFT_ENTRY || i == TRAIN_POS_MID_ENTRY || i == TRAIN_POS_RIGHT_ENTRY) && pPassengers[i] == nil){ + pPassengers[i] = ped; + m_nNumPassengers++; + }else{ + for(i = 0; i < 6; i++) + if(pPassengers[i] == nil){ + pPassengers[i] = ped; + m_nNumPassengers++; + return; + } + } +#endif +} + +void +CTrain::OpenTrainDoor(float ratio) +{ +#ifdef GTA_TRAIN + if(m_rwObject == nil) + return; + + CMatrix doorL(RwFrameGetMatrix(m_aTrainNodes[TRAIN_DOOR_LHS])); + CMatrix doorR(RwFrameGetMatrix(m_aTrainNodes[TRAIN_DOOR_RHS])); + CVector posL = doorL.GetPosition(); + CVector posR = doorR.GetPosition(); + + bool isClosed = Doors[0].IsClosed(); // useless + + Doors[0].Open(ratio); + Doors[1].Open(ratio); + + if(isClosed) + Doors[0].RetTranslationWhenClosed(); // useless + + posL.y = Doors[0].m_fPosn; + posR.y = Doors[1].m_fPosn; + + doorL.SetTranslate(posL); + doorR.SetTranslate(posR); + + doorL.UpdateRW(); + doorR.UpdateRW(); +#endif +} + + + +void +CTrain::InitTrains(void) +{ +#ifdef GTA_TRAIN + int i, j; + CTrain *train; + + // El train + if(pTrackNodes == nil) + ReadAndInterpretTrackFile("data\\paths\\tracks.dat", &pTrackNodes, &NumTrackNodes, 3, StationDist, + &TotalLengthOfTrack, &TotalDurationOfTrack, aLineBits, false); + // Subway + if(pTrackNodes_S == nil) + ReadAndInterpretTrackFile("data\\paths\\tracks2.dat", &pTrackNodes_S, &NumTrackNodes_S, 4, StationDist_S, + &TotalLengthOfTrack_S, &TotalDurationOfTrack_S, aLineBits_S, true); + + int trainId; + CStreaming::LoadAllRequestedModels(false); + if(CModelInfo::GetModelInfo("train", &trainId)) + CStreaming::RequestModel(trainId, 0); + CStreaming::LoadAllRequestedModels(false); + + // El-Train wagons + float wagonPositions[] = { 0.0f, 20.0f, 40.0f, 0.0f, 20.0f }; + int8 firstWagon[] = { 1, 0, 0, 1, 0 }; + int8 lastWagon[] = { 0, 0, 1, 0, 1 }; + int16 wagonGroup[] = { 0, 0, 0, 1, 1 }; + for(i = 0; i < 5; i++){ + train = new CTrain(MI_TRAIN, PERMANENT_VEHICLE); + train->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); + train->SetStatus(STATUS_ABANDONED); + train->bIsLocked = true; + train->m_fWagonPosition = wagonPositions[i]; + train->m_bIsFirstWagon = firstWagon[i]; + train->m_bIsLastWagon = lastWagon[i]; + train->m_nWagonGroup = wagonGroup[i]; + train->m_nWagonId = i; + train->m_nCurTrackNode = 0; + CWorld::Add(train); + } + + // Subway wagons + float wagonPositions_S[] = { 0.0f, 20.0f, 0.0f, 20.0f, 0.0f, 20.0f, 0.0f, 20.0f }; + int8 firstWagon_S[] = { 1, 0, 1, 0, 1, 0, 1, 0 }; + int8 lastWagon_S[] = { 0, 1, 0, 1, 0, 1, 0, 1 }; + int16 wagonGroup_S[] = { 0, 0, 1, 1, 2, 2, 3, 3 }; + for(i = 0; i < 8; i++){ + train = new CTrain(MI_TRAIN, PERMANENT_VEHICLE); + train->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); + train->SetStatus(STATUS_ABANDONED); + train->bIsLocked = true; + train->m_fWagonPosition = wagonPositions_S[i]; + train->m_bIsFirstWagon = firstWagon_S[i]; + train->m_bIsLastWagon = lastWagon_S[i]; + train->m_nWagonGroup = wagonGroup_S[i]; + train->m_nWagonId = i; + train->m_nCurTrackNode = 0; + train->m_nTrackId = TRACK_SUBWAY; + CWorld::Add(train); + } + + // This code is actually useless, it seems it was used for announcements once + for(i = 0; i < 3; i++){ + for(j = 0; pTrackNodes[j].t < StationDist[i]; j++); + aStationCoors[i] = pTrackNodes[j].p; + } + for(i = 0; i < 4; i++){ + for(j = 0; pTrackNodes_S[j].t < StationDist_S[i]; j++); + aStationCoors_S[i] = pTrackNodes_S[j].p; + } +#endif +} + +void +CTrain::Shutdown(void) +{ +#ifdef GTA_TRAIN + delete[] pTrackNodes; + delete[] pTrackNodes_S; + pTrackNodes = nil; + pTrackNodes_S = nil; +#endif +} + +void +CTrain::ReadAndInterpretTrackFile(Const char *filename, CTrainNode **nodes, int16 *numNodes, int32 numStations, float *stationDists, + float *totalLength, float *totalDuration, CTrainInterpolationLine *interpLines, bool rightRail) +{ +#ifdef GTA_TRAIN + bool readingFile = false; + int bp, lp; + int i, tmp; + + if(*nodes == nil){ + readingFile = true; + + CFileMgr::LoadFile(filename, work_buff, sizeof(work_buff), "r"); + *gString = '\0'; + for(bp = 0, lp = 0; work_buff[bp] != '\n'; bp++, lp++) + gString[lp] = work_buff[bp]; + bp++; + // BUG: game doesn't terminate string and uses numNodes in sscanf directly + gString[lp] = '\0'; + sscanf(gString, "%d", &tmp); + *numNodes = tmp; + *nodes = new CTrainNode[*numNodes]; + + for(i = 0; i < *numNodes; i++){ + *gString = '\0'; + for(lp = 0; work_buff[bp] != '\n'; bp++, lp++) + gString[lp] = work_buff[bp]; + bp++; + // BUG: game doesn't terminate string + gString[lp] = '\0'; + sscanf(gString, "%f %f %f", &(*nodes)[i].p.x, &(*nodes)[i].p.y, &(*nodes)[i].p.z); + } + + // Coordinates are of one of the rails, but we want the center + float toCenter = rightRail ? 0.9f : -0.9f; + CVector fwd; + for(i = 0; i < *numNodes; i++){ + if(i == *numNodes-1) + fwd = (*nodes)[0].p - (*nodes)[i].p; + else + fwd = (*nodes)[i+1].p - (*nodes)[i].p; + CVector right = CrossProduct(fwd, CVector(0.0f, 0.0f, 1.0f)); + right.Normalise(); + (*nodes)[i].p -= right*toCenter; + } + } + + // Calculate length of segments and track + float t = 0.0f; + for(i = 0; i < *numNodes; i++){ + (*nodes)[i].t = t; + t += ((*nodes)[(i+1) % (*numNodes)].p - (*nodes)[i].p).Magnitude2D(); + } + *totalLength = t; + + // Find correct z values + if(readingFile){ + CColPoint colpoint; + CEntity *entity; + for(i = 0; i < *numNodes; i++){ + CVector p = (*nodes)[i].p; + p.z += 1.0f; + if(CWorld::ProcessVerticalLine(p, p.z-0.5f, colpoint, entity, true, false, false, false, true, false, nil)) + (*nodes)[i].p.z = colpoint.point.z; + (*nodes)[i].p.z += 0.2f; + } + } + + // Create animation for stopping at stations + // TODO: figure out magic numbers? + float position = 0.0f; + float time = 0.0f; + int j = 0; + for(i = 0; i < numStations; i++){ + // Start at full speed + interpLines[j].type = 1; + interpLines[j].time = time; + interpLines[j].position = position; + interpLines[j].speed = 15.0f; + interpLines[j].acceleration = 0.0f; + j++; + // distance to next keyframe + float dist = (stationDists[i]-40.0f) - position; + time += dist/15.0f; + position += dist; + + // Now slow down 40 units before stop + interpLines[j].type = 2; + interpLines[j].time = time; + interpLines[j].position = position; + interpLines[j].speed = 15.0f; + interpLines[j].acceleration = -45.0f/32.0f; + j++; + time += 80.0f/15.0f; + position += 40.0f; // at station + + // stopping + interpLines[j].type = 0; + interpLines[j].time = time; + interpLines[j].position = position; + interpLines[j].speed = 0.0f; + interpLines[j].acceleration = 0.0f; + j++; + time += 25.0f; + + // accelerate again + interpLines[j].type = 2; + interpLines[j].time = time; + interpLines[j].position = position; + interpLines[j].speed = 0.0f; + interpLines[j].acceleration = 45.0f/32.0f; + j++; + time += 80.0f/15.0f; + position += 40.0f; // after station + } + // last keyframe + interpLines[j].type = 1; + interpLines[j].time = time; + interpLines[j].position = position; + interpLines[j].speed = 15.0f; + interpLines[j].acceleration = 0.0f; + j++; + *totalDuration = time + (*totalLength - position)/15.0f; + + // end + interpLines[j].time = *totalDuration; +#endif +} + +void +PlayAnnouncement(uint8 sound, uint8 station) +{ + // this was gone in a PC version but inlined on PS2 + cAudioScriptObject *obj = new cAudioScriptObject; + obj->AudioId = sound; + obj->Posn = CTrain::aStationCoors[station]; + obj->AudioEntity = AEHANDLE_NONE; + DMAudio.CreateOneShotScriptObject(obj); +} + +void +ProcessTrainAnnouncements(void) +{ +#ifdef GTA_TRAIN + for (int i = 0; i < ARRAY_SIZE(StationDist); i++) { + for (int j = 0; j < ARRAY_SIZE(EngineTrackPosition); j++) { + if (!bTrainArrivalAnnounced[i]) { + float preDist = StationDist[i] - 100.0f; + if (preDist < 0.0f) + preDist += TotalLengthOfTrack; + if (EngineTrackPosition[j] > preDist && EngineTrackPosition[j] < StationDist[i]) { + bTrainArrivalAnnounced[i] = true; + PlayAnnouncement(SCRIPT_SOUND_TRAIN_ANNOUNCEMENT_1, i); + break; + } + } else { + float postDist = StationDist[i] + 10.0f; +#ifdef FIX_BUGS + if (postDist > TotalLengthOfTrack) + postDist -= TotalLengthOfTrack; +#else + if (postDist < 0.0f) // does this even make sense here? + postDist += TotalLengthOfTrack; +#endif + if (EngineTrackPosition[j] > StationDist[i] && EngineTrackPosition[j] < postDist) { + bTrainArrivalAnnounced[i] = false; + PlayAnnouncement(SCRIPT_SOUND_TRAIN_ANNOUNCEMENT_2, i); + break; + } + } + } + } +#endif +} + +void +CTrain::UpdateTrains(void) +{ +#ifdef GTA_TRAIN + int i, j; + uint32 time; + float t, deltaT; + + if(TheCamera.GetPosition().x > 200.0f && TheCamera.GetPosition().x < 1600.0f && + TheCamera.GetPosition().y > -1000.0f && TheCamera.GetPosition().y < 500.0f){ + // Update El-Train + + time = CTimer::GetTimeInMilliseconds(); + for(i = 0; i < 2; i++){ + t = TotalDurationOfTrack * (float)(time & 0x1FFFF)/0x20000; + // find current frame + for(j = 0; t > aLineBits[j+1].time; j++); + + deltaT = t - aLineBits[j].time; + switch(aLineBits[j].type){ + case 0: // standing still + EngineTrackPosition[i] = aLineBits[j].position; + EngineTrackSpeed[i] = 0.0f; + break; + case 1: // moving with constant speed + EngineTrackPosition[i] = aLineBits[j].position + aLineBits[j].speed*deltaT; + EngineTrackSpeed[i] = (TotalDurationOfTrack*1000.0f/0x20000) * aLineBits[j].speed; + break; + case 2: // accelerating/braking + EngineTrackPosition[i] = aLineBits[j].position + (aLineBits[j].speed + aLineBits[j].acceleration*deltaT)*deltaT; + EngineTrackSpeed[i] = (TotalDurationOfTrack*1000.0f/0x20000)*aLineBits[j].speed + 2.0f*aLineBits[j].acceleration*deltaT; + break; + } + + // time offset for each train + time += 0x20000/2; + } + + ProcessTrainAnnouncements(); + } + + // Update Subway + time = CTimer::GetTimeInMilliseconds(); + for(i = 0; i < 4; i++){ + t = TotalDurationOfTrack_S * (float)(time & 0x3FFFF)/0x40000; + // find current frame + for(j = 0; t > aLineBits_S[j+1].time; j++); + + deltaT = t - aLineBits_S[j].time; + switch(aLineBits_S[j].type){ + case 0: // standing still + EngineTrackPosition_S[i] = aLineBits_S[j].position; + EngineTrackSpeed_S[i] = 0.0f; + break; + case 1: // moving with constant speed + EngineTrackPosition_S[i] = aLineBits_S[j].position + aLineBits_S[j].speed*deltaT; + EngineTrackSpeed_S[i] = (TotalDurationOfTrack*1000.0f/0x40000) * aLineBits_S[j].speed; + break; + case 2: // accelerating/braking + EngineTrackPosition_S[i] = aLineBits_S[j].position + (aLineBits_S[j].speed + aLineBits_S[j].acceleration*deltaT)*deltaT; + EngineTrackSpeed_S[i] = (TotalDurationOfTrack*1000.0f/0x40000)*aLineBits_S[j].speed + 2.0f*aLineBits_S[j].acceleration*deltaT; + break; + } + + // time offset for each train + time += 0x40000/4; + } +#endif +} diff --git a/src/miami/vehicles/Train.h b/src/miami/vehicles/Train.h new file mode 100644 index 00000000..57cd28de --- /dev/null +++ b/src/miami/vehicles/Train.h @@ -0,0 +1,84 @@ +#pragma once + +#include "Vehicle.h" +#include "Door.h" + +enum +{ + TRACK_ELTRAIN, + TRACK_SUBWAY +}; + +enum +{ + TRAIN_DOOR_CLOSED, + TRAIN_DOOR_OPENING, + TRAIN_DOOR_OPEN, + TRAIN_DOOR_CLOSING +}; + +enum eTrainNodes +{ + TRAIN_DOOR_LHS = 1, + TRAIN_DOOR_RHS, + NUM_TRAIN_NODES +}; + +struct CTrainNode +{ + CVector p; // position + float t; // xy-distance from start on track +}; + +struct CTrainInterpolationLine +{ + uint8 type; + float time; // when does this keyframe start + // initial values at start of frame + float position; + float speed; + float acceleration; +}; + +class CTrain : public CVehicle +{ +public: + // 0x288 + float m_fWagonPosition; + int16 m_nWagonId; + int16 m_isFarAway; // don't update so often? + int16 m_nCurTrackNode; + int16 m_nWagonGroup; + float m_fSpeed; + bool m_bProcessDoor; + bool m_bTrainStopping; + bool m_bIsFirstWagon; + bool m_bIsLastWagon; + uint8 m_nTrackId; // or m_bUsesSubwayTracks? + uint32 m_nDoorTimer; + int16 m_nDoorState; + CTrainDoor Doors[2]; + RwFrame *m_aTrainNodes[NUM_TRAIN_NODES]; + + // unused + static CVector aStationCoors[3]; + static CVector aStationCoors_S[4]; + + CTrain(int32 id, uint8 CreatedBy); + + // from CEntity + void SetModelIndex(uint32 id); + void ProcessControl(void); + void PreRender(void); + void Render(void); + + void AddPassenger(CPed *ped); + void OpenTrainDoor(float ratio); + void TrainHitStuff(CPtrList &list); + + static void InitTrains(void); + static void Shutdown(void); + static void ReadAndInterpretTrackFile(Const char *filename, CTrainNode **nodes, int16 *numNodes, int32 numStations, float *stationDists, + float *totalLength, float *totalDuration, CTrainInterpolationLine *interpLines, bool rightRail); + static void UpdateTrains(void); +}; diff --git a/src/miami/vehicles/Transmission.cpp b/src/miami/vehicles/Transmission.cpp new file mode 100644 index 00000000..1aeabfe0 --- /dev/null +++ b/src/miami/vehicles/Transmission.cpp @@ -0,0 +1,130 @@ +#include "common.h" + +#include "Timer.h" +#include "HandlingMgr.h" +#include "Transmission.h" + +void +cTransmission::InitGearRatios(void) +{ + static tGear *pGearRatio0 = nil; + static tGear *pGearRatio1 = nil; + int i; + float velocityDiff; + + memset(Gears, 0, sizeof(Gears)); + + for(i = 1; i <= nNumberOfGears; i++){ + pGearRatio0 = &Gears[i-1]; + pGearRatio1 = &Gears[i]; + + pGearRatio1->fMaxVelocity = (float)i / nNumberOfGears * fMaxVelocity; + + velocityDiff = pGearRatio1->fMaxVelocity - pGearRatio0->fMaxVelocity; + + if(i >= nNumberOfGears){ + pGearRatio1->fShiftUpVelocity = fMaxVelocity; + }else{ + Gears[i+1].fShiftDownVelocity = velocityDiff*0.42f + pGearRatio0->fMaxVelocity; + pGearRatio1->fShiftUpVelocity = velocityDiff*0.6667f + pGearRatio0->fMaxVelocity; + } + } + + // Reverse gear + Gears[0].fMaxVelocity = fMaxReverseVelocity; + Gears[0].fShiftUpVelocity = -0.01f; + Gears[0].fShiftDownVelocity = fMaxReverseVelocity; + + Gears[1].fShiftDownVelocity = -0.01f; +} + +void +cTransmission::CalculateGearForSimpleCar(float speed, uint8 &gear) +{ + static tGear *pGearRatio; + + pGearRatio = &Gears[gear]; + fCurVelocity = speed; + if(speed > pGearRatio->fShiftUpVelocity) + gear++; + else if(speed < pGearRatio->fShiftDownVelocity){ + if(gear - 1 < 0) + gear = 0; + else + gear--; + } +} + +float +cTransmission::CalculateDriveAcceleration(const float &gasPedal, uint8 &gear, float &time, const float &velocity, bool cheat) +{ + static float fAcceleration = 0.0f; + static float fVelocity; + static float fCheat; + static tGear *pGearRatio; + + fVelocity = velocity; + if(fVelocity < fMaxReverseVelocity){ + fVelocity = fMaxReverseVelocity; + return 0.0f; + } + if(fVelocity > fMaxVelocity){ + fVelocity = fMaxVelocity; + return 0.0f; + } + fCurVelocity = fVelocity; + + assert(gear <= nNumberOfGears); + + pGearRatio = &Gears[gear]; + if(fVelocity > pGearRatio->fShiftUpVelocity){ + if(gear != 0 || gasPedal > 0.0f){ + gear++; + return CalculateDriveAcceleration(gasPedal, gear, time, fVelocity, false); + } + }else if(fVelocity < pGearRatio->fShiftDownVelocity && gear != 0){ + if(gear != 1 || gasPedal < 0.0f){ + gear--; + return CalculateDriveAcceleration(gasPedal, gear, time, fVelocity, false); + } + } + + float speedMul, accelMul; + + if(gear < 1){ + // going reverse + accelMul = (Flags & HANDLING_2G_BOOST) ? 2.0f : 1.0f; + speedMul = -1.0f; + }else if(nNumberOfGears == 1){ + accelMul = 1.0f; + speedMul = 1.0f; + }else{ + // BUG or not? this is 1.0 normally but 0.0 in the highest gear + float f = 1.0f - (gear-1)/(nNumberOfGears-1); + speedMul = 3.0f*sq(f) + 1.0f; + // This is pretty ugly, could be written more clearly + if(Flags & HANDLING_2G_BOOST){ + if(gear == 1) + accelMul = (Flags & HANDLING_1G_BOOST) ? 2.0f : 1.6f; + else if(gear == 2) + accelMul = 1.3f; + else + accelMul = 1.0f; + }else if(Flags & HANDLING_1G_BOOST && gear == 1){ + accelMul = 2.0f; + }else + accelMul = 1.0f; + } + + if(cheat) + fCheat = 1.2f; + else + fCheat = 1.0f; + float targetVelocity = Gears[gear].fMaxVelocity*speedMul*fCheat; + float accel = (targetVelocity - fVelocity) * (fEngineAcceleration*accelMul) / Abs(targetVelocity); + if(Abs(fVelocity) < Abs(Gears[gear].fMaxVelocity*fCheat)) + fAcceleration = gasPedal * accel * CTimer::GetTimeStep(); + else + fAcceleration = 0.0f; + return fAcceleration; +} diff --git a/src/miami/vehicles/Transmission.h b/src/miami/vehicles/Transmission.h new file mode 100644 index 00000000..a3d15513 --- /dev/null +++ b/src/miami/vehicles/Transmission.h @@ -0,0 +1,28 @@ +#pragma once + +struct tGear +{ + float fMaxVelocity; + float fShiftUpVelocity; + float fShiftDownVelocity; +}; + +class cTransmission +{ +public: + // Gear 0 is reverse, 1-5 are forward + tGear Gears[6]; + char nDriveType; + char nEngineType; + int8 nNumberOfGears; + uint8 Flags; + float fEngineAcceleration; + float fMaxVelocity; + float fMaxCruiseVelocity; + float fMaxReverseVelocity; + float fCurVelocity; + + void InitGearRatios(void); + void CalculateGearForSimpleCar(float speed, uint8 &gear); + float CalculateDriveAcceleration(const float &gasPedal, uint8 &gear, float &time, const float &velocity, bool cheat); +}; diff --git a/src/miami/vehicles/Vehicle.cpp b/src/miami/vehicles/Vehicle.cpp new file mode 100644 index 00000000..d1054191 --- /dev/null +++ b/src/miami/vehicles/Vehicle.cpp @@ -0,0 +1,2518 @@ +#include "common.h" +#include "main.h" + +#include "General.h" +#include "Timer.h" +#include "Pad.h" +#include "Vehicle.h" +#include "Bike.h" +#include "Automobile.h" +#include "Pools.h" +#include "HandlingMgr.h" +#include "CarCtrl.h" +#include "Population.h" +#include "ModelIndices.h" +#include "World.h" +#include "Lights.h" +#include "PointLights.h" +#include "Renderer.h" +#include "VisibilityPlugins.h" +#include "DMAudio.h" +#include "Radar.h" +#include "Fire.h" +#include "Darkel.h" +#include "Streaming.h" +#include "Camera.h" +#include "Stats.h" +#include "Garages.h" +#include "Wanted.h" +#include "SurfaceTable.h" +#include "Particle.h" +#include "WaterLevel.h" +#include "Timecycle.h" +#include "Weather.h" +#include "Coronas.h" +#include "SaveBuf.h" + +bool CVehicle::bWheelsOnlyCheat; +bool CVehicle::bAllDodosCheat; +bool CVehicle::bCheat3; +bool CVehicle::bCheat4; +bool CVehicle::bCheat5; +bool CVehicle::bCheat8; +bool CVehicle::bCheat9; +bool CVehicle::bCheat10; +bool CVehicle::bHoverCheat; +bool CVehicle::bAllTaxisHaveNitro; +bool CVehicle::m_bDisableMouseSteering = true; +bool CVehicle::bDisableRemoteDetonation; +bool CVehicle::bDisableRemoteDetonationOnContact; +#ifndef MASTER +bool CVehicle::m_bDisplayHandlingInfo; +#endif + +void *CVehicle::operator new(size_t sz) throw() { return CPools::GetVehiclePool()->New(); } +void *CVehicle::operator new(size_t sz, int handle) throw() { return CPools::GetVehiclePool()->New(handle); } +void CVehicle::operator delete(void *p, size_t sz) throw() { CPools::GetVehiclePool()->Delete((CVehicle*)p); } +void CVehicle::operator delete(void *p, int handle) throw() { CPools::GetVehiclePool()->Delete((CVehicle*)p); } + +#ifdef FIX_BUGS +// I think they meant that +#define DAMAGE_FLEE_IN_CAR_PROBABILITY_VALUE (MYRAND_MAX * 35 / 100) +#define DAMAGE_FLEE_ON_FOOT_PROBABILITY_VALUE (MYRAND_MAX * 70 / 100) +#else +#define DAMAGE_FLEE_IN_CAR_PROBABILITY_VALUE (35000) +#define DAMAGE_FLEE_ON_FOOT_PROBABILITY_VALUE (70000) +#endif +#define DAMAGE_HEALTH_TO_FLEE_ALWAYS (200.0f) +#define DAMAGE_HEALTH_TO_CATCH_FIRE (250.0f) + + +CVehicle::CVehicle(uint8 CreatedBy) +{ + int i; + + m_nCurrentGear = 1; + m_fChangeGearTime = 0.0f; + m_fSteerInput = 0.0f; + m_type = ENTITY_TYPE_VEHICLE; + VehicleCreatedBy = CreatedBy; + m_nRouteSeed = 0; + bIsLocked = false; + bIsLawEnforcer = false; + bIsAmbulanceOnDuty = false; + bIsFireTruckOnDuty = false; +#ifdef FIX_BUGS + bIsHandbrakeOn = false; +#endif + CCarCtrl::UpdateCarCount(this, false); + m_fHealth = 1000.0f; + bEngineOn = true; + bFreebies = true; + pDriver = nil; + m_nNumPassengers = 0; + m_nNumGettingIn = 0; + m_nGettingInFlags = 0; + m_nGettingOutFlags = 0; + m_nNumMaxPassengers = ARRAY_SIZE(pPassengers); + for(i = 0; i < m_nNumMaxPassengers; i++) + pPassengers[i] = nil; + m_nBombTimer = 0; + m_pBlowUpEntity = nil; + m_nPacManPickupsCarried = 0; + bComedyControls = false; + bCraneMessageDone = false; + bExtendedRange = false; + bTakeLessDamage = false; + bIsDamaged = false; + bFadeOut = false; + bIsBeingCarJacked = false; + m_nTimeOfDeath = 0; + m_pCarFire = nil; + bHasBeenOwnedByPlayer = false; + bCreateRoadBlockPeds = false; + bCanBeDamaged = true; + bUsingSpecialColModel = false; + bOccupantsHaveBeenGenerated = false; + bGunSwitchedOff = false; + m_nGunFiringTime = 0; + m_nTimeBlocked = 0; + bLightsOn = false; + bVehicleColProcessed = false; + m_numPedsUseItAsCover = 0; + bIsCarParkVehicle = false; + bHasAlreadyBeenRecorded = false; + m_bSirenOrAlarm = false; + m_nCarHornTimer = 0; + m_nCarHornPattern = 0; + m_nCarHornDelay = 0; + bPartOfConvoy = false; + bHeliMinimumTilt = false; + bAudioChangingGear = false; + bIsDrowning = false; + bTyresDontBurst = false; + bCreatedAsPoliceVehicle = false; + bRestingOnPhysical = false; + bParking = false; + bCanPark = CGeneral::GetRandomNumberInRange(0.0f, 1.0f) < 0.0f; // never true. probably doesn't work very well + bIsVan = false; + bIsBus = false; + bIsBig = false; + bLowVehicle = false; + + m_bombType = CARBOMB_NONE; + bDriverLastFrame = false; + m_pBombRigger = nil; + + m_nSetPieceExtendedRangeTime = 0; + m_nAlarmState = 0; + m_nDoorLock = CARLOCK_UNLOCKED; + m_nLastWeaponDamage = -1; + m_pLastDamageEntity = nil; + m_fMapObjectHeightAhead = m_fMapObjectHeightBehind = 0.0f; + m_audioEntityId = DMAudio.CreateEntity(AUDIOTYPE_PHYSICAL, this); + if(m_audioEntityId >= 0) + DMAudio.SetEntityStatus(m_audioEntityId, TRUE); + //m_nRadioStation = CGeneral::GetRandomNumber() % NUM_RADIOS; + switch(GetModelIndex()){ + case MI_HUNTER: + case MI_ANGEL: + case MI_FREEWAY: + m_nRadioStation = V_ROCK; + break; + case MI_RCBARON: + case MI_RCBANDIT: + case MI_RCRAIDER: + case MI_RCGOBLIN: + case MI_TOPFUN: + case MI_CADDY: + case MI_BAGGAGE: + m_nRadioStation = RADIO_OFF; + break; + default: + m_nRadioStation = CGeneral::GetRandomNumber() % NUM_RADIOS; + break; + } + m_pCurGroundEntity = nil; + m_bRainAudioCounter = 0; + m_bRainSamplesCounter = 0; + m_comedyControlState = 0; + m_aCollPolys[0].valid = false; + m_aCollPolys[1].valid = false; + AutoPilot.m_nCarMission = MISSION_NONE; + AutoPilot.m_nTempAction = TEMPACT_NONE; + AutoPilot.m_nTimeToStartMission = CTimer::GetTimeInMilliseconds(); + AutoPilot.m_bStayInCurrentLevel = false; + AutoPilot.m_bIgnorePathfinding = false; + AutoPilot.m_nSwitchDistance = 20; +} + +CVehicle::~CVehicle() +{ + m_nAlarmState = 0; + if (m_audioEntityId >= 0){ + DMAudio.DestroyEntity(m_audioEntityId); + m_audioEntityId = -5; + } + CRadar::ClearBlipForEntity(BLIP_CAR, CPools::GetVehiclePool()->GetIndex(this)); + if (pDriver) + pDriver->FlagToDestroyWhenNextProcessed(); + for (int i = 0; i < m_nNumMaxPassengers; i++){ + if (pPassengers[i]) + pPassengers[i]->FlagToDestroyWhenNextProcessed(); + } + if (m_pCarFire) + m_pCarFire->Extinguish(); + CCarCtrl::UpdateCarCount(this, true); + if (bIsAmbulanceOnDuty){ + CCarCtrl::NumAmbulancesOnDuty--; + bIsAmbulanceOnDuty = false; + } + if (bIsFireTruckOnDuty){ + CCarCtrl::NumFiretrucksOnDuty--; + bIsFireTruckOnDuty = false; + } +} + +void +CVehicle::SetModelIndex(uint32 id) +{ + CEntity::SetModelIndex(id); + m_aExtras[0] = CVehicleModelInfo::ms_compsUsed[0]; + m_aExtras[1] = CVehicleModelInfo::ms_compsUsed[1]; + m_nNumMaxPassengers = CVehicleModelInfo::GetMaximumNumberOfPassengersFromNumberOfDoors(id); +} + +bool +CVehicle::SetupLighting(void) +{ + ActivateDirectional(); + SetAmbientColoursForPedsCarsAndObjects(); + + if(bRenderScorched){ + WorldReplaceNormalLightsWithScorched(Scene.world, 0.1f); + }else{ + CVector coors = GetPosition(); + float lighting = CPointLights::GenerateLightsAffectingObject(&coors); + if(lighting != 1.0f){ + SetAmbientAndDirectionalColours(lighting); + return true; + } + } + + return false; +} + +void +CVehicle::RemoveLighting(bool reset) +{ + CRenderer::RemoveVehiclePedLights(this, reset); +} + +bool +CVehicle::IsClearToDriveAway(void) +{ + CColPoint point; + float length = GetColModel()->boundingBox.GetSize().y; + CEntity *ent = nil; + CVector front = GetForward() * (length*0.5f + 3.0f); + return !CWorld::ProcessLineOfSight(GetPosition() + front, GetPosition(), + point, ent, true, true, false, false, false, true, true) || + ent == this; +} + +float +CVehicle::GetHeightAboveRoad(void) +{ + return -1.0f * GetColModel()->boundingBox.min.z; +} + +void +CVehicle::FlyingControl(eFlightModel flightModel) +{ + if(pFlyingHandling == nil) + return; + + switch(flightModel){ + case FLIGHT_MODEL_DODO: + { + // This seems pretty magic + + // Move Left/Right + float moveSpeed = m_vecMoveSpeed.Magnitude(); + float sideSpeed = DotProduct(m_vecMoveSpeed, GetRight()); + float sideImpulse = -1.0f * sideSpeed / moveSpeed; + float fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward()); + float magic = m_vecMoveSpeed.MagnitudeSqr() * sq(fwdSpeed); + float turnImpulse = (sideImpulse*0.003f + m_fSteerAngle*0.001f) * + magic*m_fTurnMass*CTimer::GetTimeStep(); + ApplyTurnForce(turnImpulse*GetRight(), -4.0f*GetForward()); + + float impulse = sideImpulse*0.2f * + magic*m_fMass*CTimer::GetTimeStep(); + ApplyMoveForce(impulse*GetRight()); + ApplyTurnForce(impulse*GetRight(), 2.0f*GetUp()); + + + // Move Up/Down + moveSpeed = m_vecMoveSpeed.Magnitude(); + float upSpeed = DotProduct(m_vecMoveSpeed, GetUp()); + float upImpulse = -1.0f * upSpeed / moveSpeed; + turnImpulse = (upImpulse*0.002f + -CPad::GetPad(0)->GetSteeringUpDown()/128.0f*0.001f) * + magic*m_fTurnMass*CTimer::GetTimeStep(); + ApplyTurnForce(turnImpulse*GetUp(), -4.0f*GetForward()); + + impulse = (upImpulse*3.5f + 0.5f)*0.05f * + magic*m_fMass*CTimer::GetTimeStep(); + if(GRAVITY*m_fMass*CTimer::GetTimeStep() < impulse && + GetPosition().z > 100.0f) + impulse = 0.9f*GRAVITY*m_fMass*CTimer::GetTimeStep(); + CVector com = Multiply3x3(GetMatrix(), m_vecCentreOfMass); + ApplyMoveForce(impulse*GetUp()); + ApplyTurnForce(impulse*GetUp(), 2.0f*GetUp() + com); + + + m_vecTurnSpeed.y *= Pow(0.9f, CTimer::GetTimeStep()); + + + moveSpeed = m_vecMoveSpeed.MagnitudeSqr(); + if(moveSpeed > SQR(1.5f)) + m_vecMoveSpeed *= 1.5f/Sqrt(moveSpeed); + + float turnSpeed = m_vecTurnSpeed.MagnitudeSqr(); + if(turnSpeed > SQR(0.2f)) + m_vecTurnSpeed *= 0.2f/Sqrt(turnSpeed); + break; + } + + case FLIGHT_MODEL_RCPLANE: + case FLIGHT_MODEL_SEAPLANE: + case FLIGHT_MODEL_PLANE_UNUSED: + case FLIGHT_MODEL_PLANE: + { + float fSteerLR = CPad::GetPad(0)->GetSteeringLeftRight() / 128.0f; + float fSteerUD = CPad::GetPad(0)->GetSteeringUpDown() / 128.0f; + float fGunUD = Abs(CPad::GetPad(0)->GetCarGunUpDown()); +#ifdef FREE_CAM + if(!CCamera::bFreeCam || (CCamera::bFreeCam && !CPad::IsAffectedByController)) +#endif + if(fGunUD > 1.0f) + fSteerUD = -CPad::GetPad(0)->GetCarGunUpDown() / 128.0f; + + float fSteerAngle = Atan2(fSteerUD, fSteerLR); + float fSteerMult = 1.0f; + if(fSteerAngle > -PI/4.0f && fSteerAngle <= PI/4.0f) + fSteerMult = 1.0f/Cos(fSteerAngle); + else if(fSteerAngle > PI/4.0f && fSteerAngle <= PI*3.0f/4.0f) + fSteerMult = 1.0f/Cos(fSteerAngle - HALFPI); + else if(fSteerAngle > PI*3.0f/4.0f) + fSteerMult = 1.0f/Cos(fSteerAngle - PI); + else if(fSteerAngle <= -PI*3.0f/4.0f) + fSteerMult = 1.0f/Cos(fSteerAngle + PI); + else if(fSteerAngle > -PI*3.0f/4.0f && fSteerAngle < -PI/4.0f) + fSteerMult = 1.0f/Cos(fSteerAngle + HALFPI); + + fSteerLR *= fSteerMult; + fSteerUD *= -fSteerMult; + + // thrust + float fThrust = pFlyingHandling->fThrust; + float fThrustFallOff = pFlyingHandling->fThrustFallOff; + float fThrustFallOffBack = pFlyingHandling->fThrustFallOff * 8.0f; +#ifdef BETTER_ALLCARSAREDODO_CHEAT + if (bAllDodosCheat && !IsRealPlane()) { + fThrust = pHandling->Transmission.fEngineAcceleration + * (pHandling->Transmission.nDriveType == '4' ? 4.0f : 2.0f); + fThrust = 5.0f * Max(fThrust, pFlyingHandling->fThrust); //tweak: (cars engines too weak to thrust car on air) + fThrustFallOff = Min(0.7f / pHandling->Transmission.fMaxVelocity, fThrustFallOff); //tweak: (use 0.7 instead of 1.0 to make cars 30% faster) + fThrustFallOffBack = -1.0f / pHandling->Transmission.fMaxReverseVelocity; + } +#endif + float fForwSpeed = DotProduct(GetMoveSpeed(), GetForward()); + CVector vecTail = GetColModel()->boundingBox.min.y * GetForward(); + float fPedalState = (CPad::GetPad(0)->GetAccelerate() - CPad::GetPad(0)->GetBrake()) / 255.0f; + float fThrustAccel; + if(fForwSpeed > 0.0f || fPedalState > 0.0f) + fThrustAccel = (fPedalState - fThrustFallOff * fForwSpeed) * fThrust; + else + fThrustAccel = Min(fPedalState - fThrustFallOffBack * fForwSpeed, 0.0f) * fThrust; + if(flightModel == FLIGHT_MODEL_PLANE_UNUSED) + fThrustAccel *= 0.3f; + else if(flightModel == FLIGHT_MODEL_PLANE) + fThrustAccel *= 0.1f; + ApplyMoveForce(fThrustAccel * GetForward() * m_fMass * CTimer::GetTimeStep()); + + // left/right + float fSideSpeed = -DotProduct(GetMoveSpeed(), GetRight()); + float fSideSlipAccel = pFlyingHandling->fSideSlip * fSideSpeed * Abs(fSideSpeed); + ApplyMoveForce(m_fMass * GetRight() * fSideSlipAccel * CTimer::GetTimeStep()); + + float fYaw = -DotProduct(GetSpeed(vecTail), GetRight()); + float fYawAccel = pFlyingHandling->fYawStab * fYaw * Abs(fYaw) + pFlyingHandling->fYaw * fSteerLR * fForwSpeed; + ApplyTurnForce(fYawAccel * GetRight() * m_fTurnMass * CTimer::GetTimeStep(), vecTail); + + float fRollAccel; + if (flightModel == FLIGHT_MODEL_RCPLANE) { + float fDirectionMultiplier = CPad::GetPad(0)->GetLookRight(); + if (CPad::GetPad(0)->GetLookLeft()) + fDirectionMultiplier = -1; + fRollAccel = (0.5f * fDirectionMultiplier + fSteerLR) * pFlyingHandling->fRoll; + } + else + fRollAccel = fSteerLR * pFlyingHandling->fRoll; + ApplyTurnForce(GetRight() * fRollAccel * fForwSpeed * m_fTurnMass * CTimer::GetTimeStep(), GetUp()); + + CVector vecFRight = CrossProduct(GetForward(), CVector(0.0f, 0.0f, 1.0f)); + CVector vecStabilise = (GetUp().z > 0.0f) ? vecFRight : -vecFRight; + float fStabiliseDirection = (GetRight().z > 0.0f) ? -1.0f : 1.0f; + float fStabiliseSpeed = pFlyingHandling->fRollStab * fStabiliseDirection * (1.0f - DotProduct(GetRight(), vecStabilise)) * (1.0f - Abs(GetForward().z)); + ApplyTurnForce(fStabiliseSpeed * m_fTurnMass * GetRight(), GetUp()); // no CTimer::GetTimeStep(), is it right? + + // up/down + float fTail = -DotProduct(GetSpeed(vecTail), GetUp()); + float fPitchAccel = pFlyingHandling->fPitchStab * fTail * Abs(fTail) + pFlyingHandling->fPitch * fSteerUD * fForwSpeed; + ApplyTurnForce(fPitchAccel * m_fTurnMass * GetUp() * CTimer::GetTimeStep(), vecTail); + + float fLift = DotProduct(GetMoveSpeed(), GetUp()) / Max(0.01f, GetMoveSpeed().Magnitude()); //accel*angle + float fLiftAccel = (pFlyingHandling->fFormLift - pFlyingHandling->fAttackLift * fLift) * SQR(fForwSpeed); + float fLiftImpulse = fLiftAccel * m_fMass * CTimer::GetTimeStep(); + if (GRAVITY * CTimer::GetTimeStep() * m_fMass < fLiftImpulse) { + if (flightModel == FLIGHT_MODEL_RCPLANE && GetPosition().z > 50.0f) + fLiftImpulse = CTimer::GetTimeStep() * 0.9f*GRAVITY * m_fMass; + else if (flightModel == FLIGHT_MODEL_SEAPLANE && GetPosition().z > 80.0f) + fLiftImpulse = CTimer::GetTimeStep() * 0.9f*GRAVITY * m_fMass; +#ifdef BETTER_ALLCARSAREDODO_CHEAT + else if(bAllDodosCheat && GetPosition().z > 170.0f) + fLiftImpulse = CTimer::GetTimeStep() * 0.9f * GRAVITY * m_fMass; +#endif + } + ApplyMoveForce(fLiftImpulse * GetUp()); + + CVector vecResistance; + vecResistance = pFlyingHandling->vecTurnRes; + float rX = Pow(vecResistance.x, CTimer::GetTimeStep()); + float rY = Pow(vecResistance.y, CTimer::GetTimeStep()); + float rZ = Pow(vecResistance.z, CTimer::GetTimeStep()); + CVector vecTurnSpeed = Multiply3x3(m_vecTurnSpeed, GetMatrix()); + vecTurnSpeed.x *= rX; + float fResistance = vecTurnSpeed.y * (1.0f / (pFlyingHandling->vecSpeedRes.y * SQR(vecTurnSpeed.y) + 1.0f)) * rY - vecTurnSpeed.y; + vecTurnSpeed.z *= rZ; + m_vecTurnSpeed = Multiply3x3(GetMatrix(), vecTurnSpeed); + ApplyTurnForce(-GetUp() * fResistance * m_fTurnMass, GetRight() + Multiply3x3(GetMatrix(), m_vecCentreOfMass)); + + + float fMoveSpeed = m_vecMoveSpeed.MagnitudeSqr(); + if(fMoveSpeed > SQR(1.5f)) + m_vecMoveSpeed *= 1.5f/Sqrt(fMoveSpeed); + + float fTurnSpeed = m_vecTurnSpeed.MagnitudeSqr(); + if(fTurnSpeed > SQR(0.2f)) + m_vecTurnSpeed *= 0.2f/Sqrt(fTurnSpeed); + break; + } + case FLIGHT_MODEL_RCHELI: + case FLIGHT_MODEL_HELI: + { +#ifdef RESTORE_ALLCARSHELI_CHEAT + tFlyingHandlingData* flyingHandling = bAllCarCheat && !IsRealHeli() ? mod_HandlingManager.GetFlyingPointer(HANDLING_MAVERICK) : pFlyingHandling; +#else + tFlyingHandlingData* flyingHandling = pFlyingHandling; +#endif + float rm = Pow(flyingHandling->fMoveRes, CTimer::GetTimeStep()); + m_vecMoveSpeed *= rm; + if (GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE) + return; + float fUpSpeed = DotProduct(m_vecMoveSpeed, GetUp()); + float fThrust = (CPad::GetPad(0)->GetAccelerate() - CPad::GetPad(0)->GetBrake()) / 255.0f; + if(fThrust < 0.0f) + fThrust *= 2.0f; + if(flightModel == FLIGHT_MODEL_RCHELI){ + fThrust = flyingHandling->fThrust * fThrust + 0.45f; + ApplyMoveForce(GRAVITY * CVector(0.0f, 0.0f, 0.5f) * m_fMass * CTimer::GetTimeStep()); + }else + fThrust = flyingHandling->fThrust * fThrust + 0.95f; + fThrust -= flyingHandling->fThrustFallOff * fUpSpeed; + if(flightModel == FLIGHT_MODEL_RCHELI && GetPosition().z > 40.0f) + fThrust *= 10.0f/(GetPosition().z - 30.0f); + else if(GetPosition().z > 80.0f) + fThrust *= 10.0f/(GetPosition().z - 70.0f); + ApplyMoveForce(GRAVITY * GetUp() * fThrust * m_fMass * CTimer::GetTimeStep()); + + if (GetUp().z > 0.0f){ + float upRight = Clamp(GetRight().z, -flyingHandling->fFormLift, flyingHandling->fFormLift); + float upImpulseRight = -upRight * flyingHandling->fAttackLift * m_fTurnMass * CTimer::GetTimeStep(); + ApplyTurnForce(upImpulseRight * GetUp(), GetRight()); + + float upFwd = Clamp(GetForward().z, -flyingHandling->fFormLift, flyingHandling->fFormLift); + float upImpulseFwd = -upFwd * flyingHandling->fAttackLift * m_fTurnMass * CTimer::GetTimeStep(); + ApplyTurnForce(upImpulseFwd * GetUp(), GetForward()); + }else{ + float upRight = GetRight().z < 0.0f ? -flyingHandling->fFormLift : flyingHandling->fFormLift; + float upImpulseRight = -upRight * flyingHandling->fAttackLift * m_fTurnMass * CTimer::GetTimeStep(); + ApplyTurnForce(upImpulseRight * GetUp(), GetRight()); + + float upFwd = GetForward().z < 0.0f ? -flyingHandling->fFormLift : flyingHandling->fFormLift; + float upImpulseFwd = -upFwd * flyingHandling->fAttackLift * m_fTurnMass * CTimer::GetTimeStep(); + ApplyTurnForce(upImpulseFwd * GetUp(), GetForward()); + } + + float fRoll, fPitch, fYaw; + if (bCheat5) { + fPitch = CPad::GetPad(0)->GetSteeringUpDown() / 128.0f; + fRoll = CPad::GetPad(0)->GetLookLeft(); + if (CPad::GetPad(0)->GetLookRight()) + fRoll = -1.0f; + fYaw = CPad::GetPad(0)->GetSteeringLeftRight() / 128.0f; + } else { + fPitch = CPad::GetPad(0)->GetSteeringUpDown() / 128.0f; + fRoll = -CPad::GetPad(0)->GetSteeringLeftRight() / 128.0f; + fYaw = CPad::GetPad(0)->GetLookRight(); + if (CPad::GetPad(0)->GetLookLeft()) + fYaw = -1.0f; +#ifdef FREE_CAM + if (!CCamera::bFreeCam || (CCamera::bFreeCam && !CPad::IsAffectedByController)) +#endif + if(Abs(CPad::GetPad(0)->GetCarGunLeftRight()) > 1.0f) + fYaw = CPad::GetPad(0)->GetCarGunLeftRight() / 128.0f; + } +#ifdef FREE_CAM + if(!CCamera::bFreeCam || (CCamera::bFreeCam && !CPad::IsAffectedByController)) +#endif + if(Abs(CPad::GetPad(0)->GetCarGunUpDown()) > 1.0f) + fPitch = -CPad::GetPad(0)->GetCarGunUpDown() / 128.0f; + if (CPad::GetPad(0)->GetHorn()) { + fYaw = 0.0f; + fPitch = Clamp(flyingHandling->fPitchStab * DotProduct(m_vecMoveSpeed, GetForward()), -200.0f, 1.3f); + fRoll = Clamp(flyingHandling->fRollStab * DotProduct(m_vecMoveSpeed, GetRight()), -200.0f, 1.3f); + } + ApplyTurnForce(fPitch * GetUp() * flyingHandling->fPitch * m_fTurnMass * CTimer::GetTimeStep(), GetForward()); + ApplyTurnForce(fRoll * GetUp() * flyingHandling->fRoll * m_fTurnMass * CTimer::GetTimeStep(), GetRight()); + + float fSideSpeed = -DotProduct(GetMoveSpeed(), GetRight()); + float fSideSlipAccel = flyingHandling->fSideSlip * fSideSpeed * Abs(fSideSpeed); + ApplyMoveForce(m_fMass * GetRight() * fSideSlipAccel * CTimer::GetTimeStep()); + float fYawAccel = flyingHandling->fYawStab * fSideSpeed * Abs(fSideSpeed) + flyingHandling->fYaw * fYaw; + ApplyTurnForce(fYawAccel * GetRight() * m_fTurnMass * CTimer::GetTimeStep(), -GetForward()); + + ApplyTurnForce(fYaw * GetForward() * flyingHandling->fYaw * m_fTurnMass * CTimer::GetTimeStep(), GetRight()); + + float rX = Pow(flyingHandling->vecTurnRes.x, CTimer::GetTimeStep()); + float rY = Pow(flyingHandling->vecTurnRes.y, CTimer::GetTimeStep()); + float rZ = Pow(flyingHandling->vecTurnRes.z, CTimer::GetTimeStep()); + CVector vecTurnSpeed = Multiply3x3(m_vecTurnSpeed, GetMatrix()); + float fResistanceMultiplier = Pow(1.0f / (flyingHandling->vecSpeedRes.z * SQR(vecTurnSpeed.z) + 1.0f) * rZ, CTimer::GetTimeStep()); + float fResistance = vecTurnSpeed.z * fResistanceMultiplier - vecTurnSpeed.z; + vecTurnSpeed.x *= rX; + vecTurnSpeed.y *= rY; + vecTurnSpeed.z *= fResistanceMultiplier; + m_vecTurnSpeed = Multiply3x3(GetMatrix(), vecTurnSpeed); + ApplyTurnForce(-GetRight() * fResistance * m_fTurnMass, GetForward() + Multiply3x3(GetMatrix(), m_vecCentreOfMass)); + break; + } + } +} + +static CColModel rotorColModel; +static CColSphere rotorColSphere; +float ROTOR_SEMI_THICKNESS = 0.05f; +float ROTOR_TURN_SPEED = 0.2f; +float ROTOR_DISGUARD_MULT = 0.3f; +float ROTOR_COL_ELASTICITY = 1.0f; +float ROTOR_COL_TURNMULT = -0.001f; +float ROTOR_DEFAULT_DAMAGE = 100.0f; + +bool +CVehicle::DoBladeCollision(CVector pos, CMatrix &matrix, int16 rotorType, float radius, float damageMult) +{ + CVector max(radius, radius, radius); + CVector min(-radius, -radius, -radius); + + switch(rotorType){ + case ROTOR_TOP: + case ROTOR_BOTTOM: + min.z = -ROTOR_SEMI_THICKNESS; + max.z = ROTOR_SEMI_THICKNESS; + break; + case ROTOR_FRONT: + case ROTOR_BACK: + min.y = -ROTOR_SEMI_THICKNESS; + max.y = ROTOR_SEMI_THICKNESS; + break; + case ROTOR_RIGHT: + case ROTOR_LEFT: + min.x = -ROTOR_SEMI_THICKNESS; + max.x = ROTOR_SEMI_THICKNESS; + break; + } + + min += pos; + max += pos; + rotorColModel.boundingBox.Set(min, max); + rotorColModel.boundingSphere.Set(radius, pos); + rotorColSphere.Set(radius, pos, 0, 0); + rotorColModel.spheres = &rotorColSphere; + rotorColModel.numSpheres = 1; + + pos = matrix * pos; + bool hadCollision = false; + int minX = CWorld::GetSectorIndexX(pos.x - radius); + if(minX <= 0) minX = 0; + + int minY = CWorld::GetSectorIndexY(pos.y - radius); + if(minY <= 0) minY = 0; + + int maxX = CWorld::GetSectorIndexX(pos.x + radius); +#ifdef FIX_BUGS + if(maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X - 1; +#else + if(maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X; +#endif + + int maxY = CWorld::GetSectorIndexY(pos.y + radius); +#ifdef FIX_BUGS + if(maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y - 1; +#else + if(maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y; +#endif + + CWorld::AdvanceCurrentScanCode(); + for(int curY = minY; curY <= maxY; curY++) { + for(int curX = minX; curX <= maxX; curX++) { + CSector *sector = CWorld::GetSector(curX, curY); + if(BladeColSectorList(sector->m_lists[ENTITYLIST_BUILDINGS], rotorColModel, matrix, rotorType, damageMult)) + hadCollision = true; + if(BladeColSectorList(sector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], rotorColModel, matrix, rotorType, damageMult)) + hadCollision = true; + if(BladeColSectorList(sector->m_lists[ENTITYLIST_VEHICLES], rotorColModel, matrix, rotorType, damageMult)) + hadCollision = true; + if(BladeColSectorList(sector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], rotorColModel, matrix, rotorType, damageMult)) + hadCollision = true; + if(BladeColSectorList(sector->m_lists[ENTITYLIST_PEDS], rotorColModel, matrix, rotorType, 0.0f)) + hadCollision = true; + if(BladeColSectorList(sector->m_lists[ENTITYLIST_PEDS_OVERLAP], rotorColModel, matrix, rotorType, 0.0f)) + hadCollision = true; + if(BladeColSectorList(sector->m_lists[ENTITYLIST_OBJECTS], rotorColModel, matrix, rotorType, damageMult)) + hadCollision = true; + if(BladeColSectorList(sector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], rotorColModel, matrix, rotorType, damageMult)) + hadCollision = true; + } + } + rotorColModel.spheres = nil; + rotorColModel.numSpheres = 0; + + return hadCollision; +} + +bool +CVehicle::BladeColSectorList(CPtrList &list, CColModel &rotorColModel, CMatrix &matrix, int16 rotorType, float damageMult) +{ + int i; + CVector axis; + CVector turnSpeed(0.0f, 0.0f, 0.0f); + switch(rotorType){ + case ROTOR_TOP: + turnSpeed.z = -ROTOR_TURN_SPEED; + axis = -matrix.GetUp(); + break; + case ROTOR_BOTTOM: + turnSpeed.z = ROTOR_TURN_SPEED; + axis = matrix.GetUp(); + break; + + case ROTOR_FRONT: + turnSpeed.y = -ROTOR_TURN_SPEED; + axis = -matrix.GetForward(); + break; + case ROTOR_BACK: + turnSpeed.y = ROTOR_TURN_SPEED; + axis = matrix.GetForward(); + break; + + case ROTOR_RIGHT: + turnSpeed.x = -ROTOR_TURN_SPEED; + axis = -matrix.GetRight(); + break; + case ROTOR_LEFT: + turnSpeed.x = ROTOR_TURN_SPEED; + axis = matrix.GetRight(); + break; + } + turnSpeed = Multiply3x3(matrix, turnSpeed); + CVector center = rotorColModel.boundingSphere.center; + center = matrix*center; + + for(CPtrNode *node = list.first; node; node = node->next) { + CEntity *entity = (CEntity *)node->item; + if(entity == (CEntity*)this || + !entity->bUsesCollision || + entity->m_scanCode == CWorld::GetCurrentScanCode()) + continue; + + entity->m_scanCode = CWorld::GetCurrentScanCode(); + + int numCollisions; + CColModel *entityCol; + if(entity->IsPed()) + entityCol = ((CPedModelInfo*)CModelInfo::GetModelInfo(entity->GetModelIndex()))->AnimatePedColModelSkinned(entity->GetClump()); + else + entityCol = entity->GetColModel(); + if(entityCol) + numCollisions = CCollision::ProcessColModels(matrix, rotorColModel, entity->GetMatrix(), *entityCol, + CWorld::m_aTempColPts, nil, nil); + else + numCollisions = 0; + + if(numCollisions > 0 && entity->IsPed()){ + CPed *ped = (CPed*)entity; + CVector2D dirToRotor = GetPosition() - entity->GetPosition(); + dirToRotor.Normalise(); + int localDir = ped->GetLocalDirection(dirToRotor); + if(ped->m_attachedTo == nil){ + ped->bIsStanding = false; + ped->ApplyMoveForce(-5.0f*dirToRotor.x, -5.0f*dirToRotor.y, 5.0f); + } + ped->InflictDamage(this, WEAPONTYPE_RUNOVERBYCAR, 1000.0f, PEDPIECE_TORSO, localDir); + + if(CGame::nastyGame && ped->GetIsOnScreen()){ + for(i = 0; i < 16; i++) + CParticle::AddParticle(PARTICLE_BLOOD_SMALL, ped->GetPosition(), CVector(dirToRotor.x, dirToRotor.y, 1.0f) * 0.01f); + CParticle::AddParticle(PARTICLE_TEST, ped->GetPosition(), CVector(0.0f, 0.0f, 0.02f), nil, 0.1f); + CParticle::AddParticle(PARTICLE_TEST, ped->GetPosition()+CVector(0.0f, 0.0f, 0.2f), CVector(0.0f, 0.0f, -0.01f), nil, 0.1f); + } + }else if(numCollisions > 0 && entity->GetModelIndex() != MI_MISSILE){ + float impulse = 0.0f; + bool hadCollision = false; + float savedElasticity = m_fElasticity; + m_fElasticity = ROTOR_COL_ELASTICITY; + + for(i = 0; i < numCollisions; i++){ + CVector colpos = CWorld::m_aTempColPts[i].point; + CVector localColpos = colpos - center; + float axisDir = DotProduct(axis, localColpos); + float colDir = DotProduct(CWorld::m_aTempColPts[i].normal, localColpos); + + if(2.0f*ROTOR_SEMI_THICKNESS < Abs(axisDir) && + ROTOR_DISGUARD_MULT*Abs(colDir) < Abs(axisDir)) + continue; + + hadCollision = true; + colpos -= axisDir*axis; // get rid of axis component + + CVector tangentSpeed = CrossProduct(turnSpeed, colpos - center); + + // Particles + for(int j = 0; j < 4; j++){ + CParticle::AddParticle(PARTICLE_SPARK_SMALL, colpos, (tangentSpeed+m_vecMoveSpeed)/2.0f); + CParticle::AddParticle(PARTICLE_SPARK, colpos, 0.1f*CWorld::m_aTempColPts[i].normal); + } + + // Apply Collision + if(IsCar()){ + CAutomobile *heli = (CAutomobile*)this; + if(heli->m_aWheelSpeed[1] > 0.15f){ + ApplyCollision(CWorld::m_aTempColPts[i], impulse); + ApplyTurnForce(m_fTurnMass*ROTOR_COL_TURNMULT*tangentSpeed, colpos - center); + heli->m_aWheelSpeed[1] = 0.15f; + }else if(heli->m_aWheelSpeed[1] < 0.075f && heli->m_aWheelSpeed[1] > 0.0f) + heli->m_aWheelSpeed[1] *= -1.0f; + } + + float damageImpulse = damageMult * Max(impulse, ROTOR_DEFAULT_DAMAGE*m_fMass/3000.0f); + if(damageImpulse > m_fDamageImpulse) + SetDamagedPieceRecord(0, damageImpulse, entity, CWorld::m_aTempColPts[i].normal); + + } + + if(hadCollision && !entity->IsPed()) + DMAudio.ReportCollision(this, entity, SURFACE_CAR_PANEL, SURFACE_TARMAC, 50.0f, 0.09f); + m_fElasticity = savedElasticity; + } + } + return false; +} + + +float fBurstSpeedMax = 0.3f; +float fBurstTyreMod = 0.13f; + +void +CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelContactSpeed, CVector &wheelContactPoint, + int32 wheelsOnGround, float thrust, float brake, float adhesion, int8 wheelId, float *wheelSpeed, tWheelState *wheelState, uint16 wheelStatus) +{ + // BUG: using statics here is probably a bad idea + static bool bAlreadySkidding = false; // this is never reset + static bool bBraking; + static bool bDriving; + +#ifdef FIX_SIGNIFICANT_BUGS + bAlreadySkidding = false; +#endif + + // how much force we want to apply in these axes + float fwd = 0.0f; + float right = 0.0f; + + bBraking = brake != 0.0f; + if(bBraking) + thrust = 0.0f; + bDriving = thrust != 0.0f; + + float contactSpeedFwd = DotProduct(wheelContactSpeed, wheelFwd); + float contactSpeedRight = DotProduct(wheelContactSpeed, wheelRight); + + if(*wheelState != WHEEL_STATE_NORMAL) + bAlreadySkidding = true; + *wheelState = WHEEL_STATE_NORMAL; + + adhesion *= CTimer::GetTimeStep(); + if(bAlreadySkidding) + adhesion *= pHandling->fTractionLoss; + + // moving sideways + if(contactSpeedRight != 0.0f){ + // exert opposing force + right = -contactSpeedRight/wheelsOnGround; + // BUG? + // contactSpeedRight is independent of framerate but right has timestep as a factor + // so we probably have to fix this + // fixing this causes jittery cars at 15fps, and causes the car to move backwards slowly at 18fps + // at 19fps, the effects are gone ... + //right *= CTimer::GetTimeStepFix(); + + if(wheelStatus == WHEEL_STATUS_BURST){ + float fwdspeed = Min(contactSpeedFwd, fBurstSpeedMax); + right += fwdspeed * CGeneral::GetRandomNumberInRange(-fBurstTyreMod, fBurstTyreMod); + } + } + + if(bDriving){ + fwd = thrust; + + // limit sideways force (why?) + if(right > 0.0f){ + if(right > adhesion) + right = adhesion; + }else{ + if(right < -adhesion) + right = -adhesion; + } + }else if(contactSpeedFwd != 0.0f){ + fwd = -contactSpeedFwd/wheelsOnGround; +#ifdef FIX_BUGS + // contactSpeedFwd is independent of framerate but fwd has timestep as a factor + // so we probably have to fix this + // better get rid of it here too + //fwd *= CTimer::GetTimeStepFix(); +#endif + + if(!bBraking){ + if(m_fGasPedal < 0.01f){ + if(IsBike()) + brake = 0.6f * mod_HandlingManager.fWheelFriction / (pHandling->fMass + 200.0f); + else if(pHandling->fMass < 500.0f) + brake = 0.2f * mod_HandlingManager.fWheelFriction / pHandling->fMass; + else if(GetModelIndex() == MI_RCBANDIT) + brake = 0.2f * mod_HandlingManager.fWheelFriction / pHandling->fMass; + else + brake = mod_HandlingManager.fWheelFriction / pHandling->fMass; +#ifdef FIX_BUGS + brake *= CTimer::GetTimeStepFix(); +#endif + } + } + + if(brake > adhesion){ + if(Abs(contactSpeedFwd) > 0.005f) + *wheelState = WHEEL_STATE_FIXED; + }else { + if(fwd > 0.0f){ + if(fwd > brake) + fwd = brake; + }else{ + if(fwd < -brake) + fwd = -brake; + } + } + } + + float speedSq = sq(right) + sq(fwd); + if(sq(adhesion) < speedSq){ + if(*wheelState != WHEEL_STATE_FIXED){ + if(bDriving && contactSpeedFwd < 0.2f) + *wheelState = WHEEL_STATE_SPINNING; + else + *wheelState = WHEEL_STATE_SKIDDING; + } + + float l = Sqrt(speedSq); + float tractionLoss = bAlreadySkidding ? 1.0f : pHandling->fTractionLoss; + right *= adhesion * tractionLoss / l; + fwd *= adhesion * tractionLoss / l; + } + + if(fwd != 0.0f || right != 0.0f){ + CVector totalSpeed = fwd*wheelFwd + right*wheelRight; + + CVector turnDirection = totalSpeed; + bool separateTurnForce = false; // BUG: not initialized on PC + if(pHandling->fSuspensionAntidiveMultiplier > 0.0f){ + if(bBraking){ + separateTurnForce = true; + turnDirection = totalSpeed - pHandling->fSuspensionAntidiveMultiplier*fwd*wheelFwd; + }else if(bDriving){ + separateTurnForce = true; + turnDirection = totalSpeed - 0.5f*pHandling->fSuspensionAntidiveMultiplier*fwd*wheelFwd; + } + } + + CVector direction = totalSpeed; + + float speed = totalSpeed.Magnitude(); + float turnSpeed; + if(separateTurnForce) + turnSpeed = turnDirection.Magnitude(); + else + turnSpeed = speed; + direction.Normalise(); + if(separateTurnForce) + turnDirection.Normalise(); + else + turnDirection = direction; + + float impulse = speed*m_fMass; + float turnImpulse = turnSpeed*GetMass(wheelContactPoint, turnDirection); + + ApplyMoveForce(impulse * direction); + ApplyTurnForce(turnImpulse * turnDirection, wheelContactPoint); + } +} + +float fBurstBikeSpeedMax = 0.12f; +float fBurstBikeTyreMod = 0.05f; +float fTweakBikeWheelTurnForce = 2.0f; + +void +CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelContactSpeed, CVector &wheelContactPoint, + int32 wheelsOnGround, float thrust, float brake, float adhesion, float destabTraction, int8 wheelId, float *wheelSpeed, tWheelState *wheelState, eBikeWheelSpecial special, uint16 wheelStatus) +{ + // BUG: using statics here is probably a bad idea + static bool bAlreadySkidding = false; // this is never reset + static bool bBraking; + static bool bDriving; + static bool bReversing; + +#ifdef FIX_SIGNIFICANT_BUGS + bAlreadySkidding = false; +#endif + + // how much force we want to apply in these axes + float fwd = 0.0f; + float right = 0.0f; + + bBraking = brake != 0.0f; + if(bBraking) + thrust = 0.0f; + bDriving = thrust != 0.0f; + bReversing = thrust < 0.0f; + + float contactSpeedFwd = DotProduct(wheelContactSpeed, wheelFwd); + float contactSpeedRight; + + if(*wheelState != WHEEL_STATE_NORMAL) + bAlreadySkidding = true; + *wheelState = WHEEL_STATE_NORMAL; + + adhesion *= CTimer::GetTimeStep(); + if(bAlreadySkidding) + adhesion *= pHandling->fTractionLoss; + + if(special == BIKE_WHEELSPEC_2 || special == BIKE_WHEELSPEC_3) + contactSpeedRight = 0.0f; + else + contactSpeedRight = DotProduct(wheelContactSpeed, wheelRight); + + // moving sideways + if(contactSpeedRight != 0.0f){ + // exert opposing force + right = -contactSpeedRight/wheelsOnGround; +#ifdef FIX_BUGS + // contactSpeedRight is independent of framerate but right has timestep as a factor + // so we probably have to fix this + // see above + //right *= CTimer::GetTimeStepFix(); +#endif + + if(wheelStatus == WHEEL_STATUS_BURST){ + float fwdspeed = Min(contactSpeedFwd, fBurstBikeSpeedMax); + right += fwdspeed * CGeneral::GetRandomNumberInRange(-fBurstBikeTyreMod, fBurstBikeTyreMod); + } + } + + if(bDriving){ + fwd = thrust; + + // limit sideways force (why?) + if(right > 0.0f){ + if(right > adhesion) + right = adhesion; + }else{ + if(right < -adhesion) + right = -adhesion; + } + }else if(contactSpeedFwd != 0.0f){ + fwd = -contactSpeedFwd/wheelsOnGround; +#ifdef FIX_BUGS + // contactSpeedFwd is independent of framerate but fwd has timestep as a factor + // so we probably have to fix this + // see above + //fwd *= CTimer::GetTimeStepFix(); +#endif + + if(!bBraking){ + if(m_fGasPedal < 0.01f){ + if(IsBike()) + brake = 0.6f * mod_HandlingManager.fWheelFriction / (pHandling->fMass + 200.0f); + else if(pHandling->fMass < 500.0f) + brake = mod_HandlingManager.fWheelFriction / m_fMass; + else if(GetModelIndex() == MI_RCBANDIT) + brake = 0.2f * mod_HandlingManager.fWheelFriction / m_fMass; + else + brake = mod_HandlingManager.fWheelFriction / m_fMass; +#ifdef FIX_BUGS + brake *= CTimer::GetTimeStepFix(); +#endif + } + } + + if(brake > adhesion){ + if(Abs(contactSpeedFwd) > 0.005f) + *wheelState = WHEEL_STATE_FIXED; + }else { + if(fwd > 0.0f){ + if(fwd > brake) + fwd = brake; + }else{ + if(fwd < -brake) + fwd = -brake; + } + } + } + + float speedSq = sq(right) + sq(fwd); + if(sq(adhesion) < speedSq){ + if(*wheelState != WHEEL_STATE_FIXED){ + if(bDriving && contactSpeedFwd < 0.2f) + *wheelState = WHEEL_STATE_SPINNING; + else + *wheelState = WHEEL_STATE_SKIDDING; + } + + float l = Sqrt(speedSq); + float tractionLoss = bAlreadySkidding ? 1.0f : pHandling->fTractionLoss; + right *= adhesion * tractionLoss / l; + fwd *= adhesion * tractionLoss / l; + + if(destabTraction < 1.0f) + right *= destabTraction; + }else if(destabTraction < 1.0f){ + if(!bAlreadySkidding) + destabTraction *= pHandling->fTractionLoss; + if(sq(adhesion*destabTraction) < speedSq){ + float l = Sqrt(speedSq); + right *= adhesion * destabTraction / l; + } + } + + if(fwd != 0.0f || right != 0.0f){ + CVector direction = fwd*wheelFwd + right*wheelRight; + + float speed = direction.Magnitude(); + direction.Normalise(); + + float impulse = speed*m_fMass; + float turnImpulse = speed*GetMass(wheelContactPoint, direction); + CVector vTurnImpulse = turnImpulse * direction; + ApplyMoveForce(impulse * direction); + + float turnRight = DotProduct(vTurnImpulse, GetRight()); + float contactRight = DotProduct(wheelContactPoint, GetRight()); + float contactFwd = DotProduct(wheelContactPoint, GetForward()); + + if(wheelId != BIKEWHEEL_REAR || !bBraking && !bReversing) + ApplyTurnForce((vTurnImpulse - turnRight*GetRight()) * fTweakBikeWheelTurnForce, + wheelContactPoint - contactRight*GetRight()); + + ApplyTurnForce(turnRight*GetRight(), contactFwd*GetForward()); + } +} + +float +CVehicle::ProcessWheelRotation(tWheelState state, const CVector &fwd, const CVector &speed, float radius) +{ + float angularVelocity; + switch(state){ + case WHEEL_STATE_SPINNING: + angularVelocity = -1.1f; // constant speed forward + break; + case WHEEL_STATE_FIXED: + angularVelocity = 0.0f; // not moving + break; + default: + angularVelocity = -DotProduct(fwd, speed) / radius; // forward speed + break; + } + return angularVelocity * CTimer::GetTimeStep(); +} + +int +CVehicle::FindTyreNearestPoint(float x, float y) +{ + CVector pos = CVector(x - GetPosition().x, y - GetPosition().y, 0.0f); + float fwd = DotProduct(GetForward(), pos); + float right = DotProduct(GetRight(), pos); + + int piece; + if(IsBike()){ + piece = fwd > 0.0f ? CAR_PIECE_WHEEL_LF : CAR_PIECE_WHEEL_LR; + }else{ + piece = fwd > 0.0f ? + right > 0.0f ? CAR_PIECE_WHEEL_RF : CAR_PIECE_WHEEL_LF : + right > 0.0f ? CAR_PIECE_WHEEL_RR : CAR_PIECE_WHEEL_LR; + } + return piece - CAR_PIECE_WHEEL_LF; +} + +void +CVehicle::InflictDamage(CEntity *damagedBy, eWeaponType weaponType, float damage, CVector pos) +{ + if (!bCanBeDamaged) + return; + if(GetStatus() == STATUS_PLAYER && CStats::GetPercentageProgress() >= 100.0f) + damage *= 0.5f; + if (GetStatus() != STATUS_PLAYER && bOnlyDamagedByPlayer && (damagedBy != FindPlayerPed() && damagedBy != FindPlayerVehicle())) + return; + + if(damage > 10.0f && (damagedBy == FindPlayerPed() || damagedBy == FindPlayerVehicle()) && GetStatus() != STATUS_WRECKED){ + CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel += 2; + CWorld::Players[CWorld::PlayerInFocus].m_fMediaAttention += 1.0f; + CStats::PropertyDestroyed += CGeneral::GetRandomNumberInRange(5, 25); + } + + bool bFrightensDriver = false; + switch (weaponType) { + case WEAPONTYPE_UNARMED: + case WEAPONTYPE_BRASSKNUCKLE: + case WEAPONTYPE_SCREWDRIVER: + case WEAPONTYPE_GOLFCLUB: + case WEAPONTYPE_NIGHTSTICK: + case WEAPONTYPE_KNIFE: + case WEAPONTYPE_BASEBALLBAT: + case WEAPONTYPE_HAMMER: + case WEAPONTYPE_CLEAVER: + case WEAPONTYPE_MACHETE: + case WEAPONTYPE_KATANA: + case WEAPONTYPE_CHAINSAW: + if (bMeleeProof) + return; + break; + case WEAPONTYPE_COLT45: + case WEAPONTYPE_PYTHON: + case WEAPONTYPE_SHOTGUN: + case WEAPONTYPE_SPAS12_SHOTGUN: + case WEAPONTYPE_STUBBY_SHOTGUN: + case WEAPONTYPE_TEC9: + case WEAPONTYPE_UZI: + case WEAPONTYPE_SILENCED_INGRAM: + case WEAPONTYPE_MP5: + case WEAPONTYPE_M4: + case WEAPONTYPE_RUGER: + case WEAPONTYPE_SNIPERRIFLE: + case WEAPONTYPE_LASERSCOPE: + case WEAPONTYPE_M60: + case WEAPONTYPE_MINIGUN: + case WEAPONTYPE_HELICANNON: + case WEAPONTYPE_UZI_DRIVEBY: + if (bBulletProof) + return; + bFrightensDriver = true; + break; + case WEAPONTYPE_GRENADE: + case WEAPONTYPE_MOLOTOV: + case WEAPONTYPE_ROCKET: + case WEAPONTYPE_EXPLOSION: + if (bExplosionProof) + return; + bFrightensDriver = true; + break; + case WEAPONTYPE_FLAMETHROWER: + if (bFireProof) + return; + break; + case WEAPONTYPE_RAMMEDBYCAR: + if (bCollisionProof) + return; + break; + default: + break; + } + + if(bFrightensDriver && GetStatus() == STATUS_PLAYER && m_fHealth < 250.0f) + return; + + // Pop tires + if(damagedBy && damagedBy->IsPed() && (IsCar() || IsBike())){ + int accuracy = 0; + switch(weaponType){ + case WEAPONTYPE_COLT45: + accuracy = 10; + break; + case WEAPONTYPE_PYTHON: + if(!((CPed*)damagedBy)->IsPlayer()) + accuracy = 64; + break; + case WEAPONTYPE_SHOTGUN: + case WEAPONTYPE_STUBBY_SHOTGUN: + case WEAPONTYPE_M60: + case WEAPONTYPE_HELICANNON: + accuracy = 25; + break; + case WEAPONTYPE_TEC9: + case WEAPONTYPE_UZI: + case WEAPONTYPE_SILENCED_INGRAM: + case WEAPONTYPE_MP5: + case WEAPONTYPE_UZI_DRIVEBY: + accuracy = 15; + break; + case WEAPONTYPE_M4: + case WEAPONTYPE_RUGER: + if(!((CPed*)damagedBy)->IsPlayer()) + accuracy = 15; + break; + } + + if(((CPed*)damagedBy)->IsPlayer() && (CCamera::m_bUseMouse3rdPerson || TheCamera.Using1stPersonWeaponMode())) + accuracy = 0; + + if(accuracy != 0 && !bTyresDontBurst && (CGeneral::GetRandomNumber()&0x7F) < accuracy){ + if(IsBike()) + BurstTyre(FindTyreNearestPoint(pos.x, pos.y) + CAR_PIECE_WHEEL_LF, false); + else if(GetVehicleAppearance() == VEHICLE_APPEARANCE_CAR) + BurstTyre(FindTyreNearestPoint(pos.x, pos.y) + CAR_PIECE_WHEEL_LF, true); + } + } + + if (m_fHealth > 0.0f) { + if (VehicleCreatedBy == RANDOM_VEHICLE && pDriver && + (GetStatus() == STATUS_SIMPLE || GetStatus() == STATUS_PHYSICS) && + AutoPilot.m_nCarMission == MISSION_CRUISE) { + if (m_randomSeed < DAMAGE_FLEE_IN_CAR_PROBABILITY_VALUE) { + CCarCtrl::SwitchVehicleToRealPhysics(this); + AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; + AutoPilot.m_nCruiseSpeed = GAME_SPEED_TO_CARAI_SPEED * pHandling->Transmission.fMaxCruiseVelocity; + SetStatus(STATUS_PHYSICS); + } + } + m_nLastWeaponDamage = weaponType; + m_pLastDamageEntity = damagedBy; + float oldHealth = m_fHealth; + if (m_fHealth > damage) { + m_fHealth -= damage; + if (VehicleCreatedBy == RANDOM_VEHICLE && !IsBoat()){ + switch (GetStatus()) { + case STATUS_SIMPLE: + case STATUS_PHYSICS: + if(AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_PLOUGH_THROUGH || + CGeneral::GetRandomNumberInRange(0.0f, 1.0f) > 0.5f && AutoPilot.m_nCarMission == MISSION_CRUISE){ + // Drive away like a maniac + if(pDriver && pDriver->m_objective != OBJECTIVE_LEAVE_CAR){ + if(AutoPilot.m_nDrivingStyle != DRIVINGSTYLE_PLOUGH_THROUGH) + AutoPilot.m_nCruiseSpeed *= 1.5f; + AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_PLOUGH_THROUGH; + } + }else{ + // Leave vehicle + if (pDriver && pDriver->CharCreatedBy != MISSION_CHAR) { + SetStatus(STATUS_ABANDONED); + pDriver->bFleeAfterExitingCar = true; + pDriver->SetObjective(OBJECTIVE_LEAVE_CAR, this); + pDriver->Say(SOUND_PED_FLEE_SPRINT); + } + int time = 200; + for (int i = 0; i < m_nNumMaxPassengers; i++) { + if (pPassengers[i] && + pPassengers[i]->m_objective != OBJECTIVE_LEAVE_CAR && + pPassengers[i]->CharCreatedBy != MISSION_CHAR) { + pPassengers[i]->bFleeAfterExitingCar = true; + pPassengers[i]->SetObjective(OBJECTIVE_LEAVE_CAR, this); + pPassengers[i]->m_objectiveTimer = CTimer::GetTimeInMilliseconds() + time; + pPassengers[i]->Say(SOUND_PED_FLEE_SPRINT); + time += 200; + } + } + } + break; + default: + break; + } + } + if (oldHealth >= DAMAGE_HEALTH_TO_CATCH_FIRE && m_fHealth < DAMAGE_HEALTH_TO_CATCH_FIRE) { + if (IsCar()) { + CAutomobile* pThisCar = (CAutomobile*)this; + pThisCar->Damage.SetEngineStatus(ENGINE_STATUS_ON_FIRE); + pThisCar->m_pSetOnFireEntity = damagedBy; + if (damagedBy) + damagedBy->RegisterReference((CEntity**)&pThisCar->m_pSetOnFireEntity); + } + } + } + else { + m_fHealth = 0.0f; + if (weaponType == WEAPONTYPE_EXPLOSION) { + // between 1000 and 3047. Also not very nice: can't be saved by respray or cheat + m_nBombTimer = 1000 + CGeneral::GetRandomNumber() & 0x7FF; + m_pBlowUpEntity = damagedBy; + if (damagedBy) + damagedBy->RegisterReference((CEntity**)&m_pBlowUpEntity); + } + else + BlowUpCar(damagedBy); + } + } +#ifdef FIX_BUGS // removing dumb case when shooting police car in player's own garage gives wanted level + if (GetModelIndex() == MI_POLICE && damagedBy == FindPlayerPed() && damagedBy != nil && !bHasBeenOwnedByPlayer) +#else + if (GetModelIndex() == MI_POLICE && damagedBy == FindPlayerPed()) +#endif + FindPlayerPed()->SetWantedLevelNoDrop(1); +} + +void +CVehicle::DoFixedMachineGuns(void) +{ + if(TheCamera.Cams[TheCamera.ActiveCam].DirectionWasLooking == LOOKING_FORWARD){ + if(CPad::GetPad(0)->GetCarGunFired() && !bGunSwitchedOff){ + FireFixedMachineGuns(); + }else{ + if(CTimer::GetTimeInMilliseconds() > m_nGunFiringTime + 1400) + m_nAmmoInClip = 20; + } + } +} + +void +CVehicle::FireFixedMachineGuns(void) +{ + if (CTimer::GetTimeInMilliseconds() <= m_nGunFiringTime + 150) + return; + CVector source, target; + float dx, dy, len; + + dx = GetForward().x; + dy = GetForward().y; + len = Sqrt(SQR(dx) + SQR(dy)); + if (len < 0.1f) len = 0.1f; + dx /= len; + dy /= len; + + m_nGunFiringTime = CTimer::GetTimeInMilliseconds(); + + source = GetMatrix() * CVector(2.0f, 2.5f, 1.0f); + target = source + CVector(dx, dy, 0.0f) * 60.0f; + target += CVector( + ((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.015f, + ((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.015f, + ((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.02f); + CWeapon::DoTankDoomAiming(this, pDriver, &source, &target); + FireOneInstantHitRound(&source, &target, 15); + + source = GetMatrix() * CVector(-2.0f, 2.5f, 1.0f); + target = source + CVector(dx, dy, 0.0f) * 60.0f; + target += CVector( + ((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.015f, + ((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.015f, + ((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.02f); + CWeapon::DoTankDoomAiming(this, pDriver, &source, &target); + FireOneInstantHitRound(&source, &target, 15); + + DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); + + m_nAmmoInClip--; + if (m_nAmmoInClip == 0) { + m_nAmmoInClip = 20; + m_nGunFiringTime = CTimer::GetTimeInMilliseconds() + 1400; + } +} + +void +CVehicle::ActivateBomb(void) +{ + if(m_bombType == CARBOMB_TIMED){ + m_bombType = CARBOMB_TIMEDACTIVE; + m_nBombTimer = 7000; + m_pBlowUpEntity = FindPlayerPed(); + CGarages::TriggerMessage("GA_12", -1, 3000, -1); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_BOMB_TIMED_ACTIVATED, 1.0f); + }else if(m_bombType == CARBOMB_ONIGNITION){ + m_bombType = CARBOMB_ONIGNITIONACTIVE; + CGarages::TriggerMessage("GA_12", -1, 3000, -1); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_BOMB_ONIGNITION_ACTIVATED, 1.0f); + } +} + +void +CVehicle::ActivateBombWhenEntered(void) +{ + if(pDriver){ + if(!bDriverLastFrame && m_bombType == CARBOMB_ONIGNITIONACTIVE){ + // If someone enters the car and there is a bomb, detonate + m_nBombTimer = 1000; + m_pBlowUpEntity = m_pBombRigger; + if(m_pBlowUpEntity) + m_pBlowUpEntity->RegisterReference((CEntity**)&m_pBlowUpEntity); + DMAudio.PlayOneShot(m_audioEntityId, SOUND_BOMB_TICK, 1.0f); + } + bDriverLastFrame = true; + }else + bDriverLastFrame = false; +} + +void +CVehicle::ExtinguishCarFire(void) +{ + if(GetStatus() != STATUS_WRECKED) + m_fHealth = Max(m_fHealth, 300.0f); + if(m_pCarFire) + m_pCarFire->Extinguish(); + if(IsCar()){ + CAutomobile *car = (CAutomobile*)this; + if(car->Damage.GetEngineStatus() >= ENGINE_STATUS_ON_FIRE) + car->Damage.SetEngineStatus(ENGINE_STATUS_ON_FIRE-10); + car->m_fFireBlowUpTimer = 0.0f; + } +} + +bool +CVehicle::ShufflePassengersToMakeSpace(void) +{ + if (m_nNumPassengers >= m_nNumMaxPassengers) + return false; + if (pPassengers[1] && + !(m_nGettingInFlags & CAR_DOOR_FLAG_LR) && + IsRoomForPedToLeaveCar(CAR_DOOR_LR, nil)) { + if (!pPassengers[2] && !(m_nGettingInFlags & CAR_DOOR_FLAG_RR)) { + pPassengers[2] = pPassengers[1]; + pPassengers[1] = nil; + pPassengers[2]->m_vehDoor = CAR_DOOR_RR; + return true; + } + if (!pPassengers[0] && !(m_nGettingInFlags & CAR_DOOR_FLAG_RF)) { + pPassengers[0] = pPassengers[1]; + pPassengers[1] = nil; + pPassengers[0]->m_vehDoor = CAR_DOOR_RF; + return true; + } + return false; + } + if (pPassengers[2] && + !(m_nGettingInFlags & CAR_DOOR_FLAG_RR) && + IsRoomForPedToLeaveCar(CAR_DOOR_RR, nil)) { + if (!pPassengers[1] && !(m_nGettingInFlags & CAR_DOOR_FLAG_LR)) { + pPassengers[1] = pPassengers[2]; + pPassengers[2] = nil; + pPassengers[1]->m_vehDoor = CAR_DOOR_LR; + return true; + } + if (!pPassengers[0] && !(m_nGettingInFlags & CAR_DOOR_FLAG_RF)) { + pPassengers[0] = pPassengers[2]; + pPassengers[2] = nil; + pPassengers[0]->m_vehDoor = CAR_DOOR_RF; + return true; + } + return false; + } + if (pPassengers[0] && + !(m_nGettingInFlags & CAR_DOOR_FLAG_RF) && + IsRoomForPedToLeaveCar(CAR_DOOR_RF, nil)) { + if (!pPassengers[1] && !(m_nGettingInFlags & CAR_DOOR_FLAG_LR)) { + pPassengers[1] = pPassengers[0]; + pPassengers[0] = nil; + pPassengers[1]->m_vehDoor = CAR_DOOR_LR; + return true; + } + if (!pPassengers[2] && !(m_nGettingInFlags & CAR_DOOR_FLAG_RR)) { + pPassengers[2] = pPassengers[0]; + pPassengers[0] = nil; + pPassengers[2]->m_vehDoor = CAR_DOOR_RR; + return true; + } + return false; + } + return false; +} + +void +CVehicle::MakeNonDraggedPedsLeaveVehicle(CPed *ped1, CPed *ped2, CPlayerPed *&player, CCopPed *&cop) +{ + int i; + player = nil; + cop = nil; + + if(ped1->IsPlayer() && ped2->m_nPedType == PEDTYPE_COP && + ((CPlayerPed*)ped1)->m_pWanted->GetWantedLevel() > 0 && + ped2->m_pedInObjective == ped1){ + player = (CPlayerPed*)ped1; + cop = (CCopPed*)ped2; + return; + } + + bool ped1IsDriver = ped1 == pDriver; + + // Just what the hell is this weird code? + CPed *peds[9]; + CPed *peds2[9]; + int numPeds = 0; + int numPeds2 = 0; + for(i = 0; i < m_nNumMaxPassengers; i++){ + CPed *p = pPassengers[i]; + if(p && p != ped1 && !p->bStayInCarOnJack){ + peds[numPeds++] = p; + // uhh what? + if(i > 0 || ped1IsDriver) + peds2[numPeds2++] = p; + } + } + + // So we're copying this array for no reason... + CPed *peds3[9]; + int numPeds3 = 0; + for(i = 0; i < numPeds; i++){ + if(peds[i]->IsPlayer() && ped2->m_nPedType == PEDTYPE_COP && + ((CPlayerPed*)peds[i])->m_pWanted->GetWantedLevel() > 0 && + ped2->m_pedInObjective == peds[i]){ + player = (CPlayerPed*)peds[i]; + cop = (CCopPed*)ped2; + return; + } + peds3[numPeds3++] = peds[i]; + } + + int time = 1800; + for(i = 0; i < numPeds3; i++){ + peds3[i]->m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + time; + peds3[i]->SetObjective(OBJECTIVE_LEAVE_CAR, this); + time += CGeneral::GetRandomNumberInRange(300.0f, 600.0f); + } + + if(IsCar() && numPeds2 > 0 && CGeneral::GetRandomTrueFalse()) + for(i = 0; i < numPeds2; i++) + if(peds2[i]->IsFemale() || CGeneral::GetRandomTrueFalse()){ + peds2[i]->m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 10000; + peds2[i]->bHeldHostageInCar = true; + peds2[i]->bFleeAfterExitingCar = true; + } +} + +void +CVehicle::ProcessDelayedExplosion(void) +{ + if(m_nBombTimer == 0) + return; + + int tick = CTimer::GetTimeStep()/60.0f*1000.0f; + int16 prev = m_nBombTimer; + if(tick > m_nBombTimer) + m_nBombTimer = 0; + else + m_nBombTimer -= tick; + + if(IsCar() && ((CAutomobile*)this)->m_bombType == CARBOMB_TIMEDACTIVE && (m_nBombTimer & 0xFE00) != (prev & 0xFE00)) + DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_BOMB_TICK, 0.0f); + + if (m_nBombTimer == 0) + BlowUpCar(m_pBlowUpEntity); +} + +bool +CVehicle::IsLawEnforcementVehicle(void) +{ + switch(GetModelIndex()){ + case MI_POLICE: + case MI_ENFORCER: + case MI_PREDATOR: + case MI_RHINO: + case MI_BARRACKS: + case MI_FBIRANCH: + case MI_VICECHEE: + return true; + default: + return false; + } +} + +bool +CVehicle::UsesSiren(void) +{ + switch(GetModelIndex()){ + case MI_FIRETRUCK: + case MI_AMBULAN: + case MI_FBICAR: + case MI_MRWHOOP: + case MI_POLICE: + case MI_ENFORCER: + case MI_PREDATOR: + case MI_FBIRANCH: + case MI_VICECHEE: + return true; + default: + return false; + } +} + +bool +CVehicle::IsVehicleNormal(void) +{ + if (!pDriver || m_nNumPassengers != 0 || GetStatus() == STATUS_WRECKED) + return false; + return GetModelInfo()->m_vehicleClass != -1; +} + +bool +CVehicle::CarHasRoof(void) +{ + if((pHandling->Flags & HANDLING_HAS_NO_ROOF) == 0) + return true; + // component 0 is assumed to be a roof + return m_aExtras[0] == 0 || m_aExtras[1] == 0; +} + +bool +CVehicle::IsUpsideDown(void) +{ + if(GetUp().z > -0.9f) + return false; + return true; +} + +bool +CVehicle::IsOnItsSide(void) +{ + if(GetRight().z < 0.8f && GetRight().z > -0.8f) + return false; + return true; +} + +bool +CVehicle::CanBeDeleted(void) +{ + int i; + + if(m_nNumGettingIn || m_nGettingOutFlags) + return false; + + if(pDriver){ + // This looks like it was inlined + if(pDriver->CharCreatedBy == MISSION_CHAR) + return false; + if(pDriver->GetPedState() != PED_DRIVING && + pDriver->GetPedState() != PED_DEAD) + return false; + } + + for(i = 0; i < ARRAY_SIZE(pPassengers); i++){ + // Same check as above + if(pPassengers[i]){ + if(pPassengers[i]->CharCreatedBy == MISSION_CHAR) + return false; + if(pPassengers[i]->GetPedState() != PED_DRIVING && + pPassengers[i]->GetPedState() != PED_DEAD) + return false; + } + // and then again... probably because something was inlined + if(pPassengers[i]){ + if(pPassengers[i]->GetPedState() != PED_DRIVING && + pPassengers[i]->GetPedState() != PED_DEAD) + return false; + } + } + + switch(VehicleCreatedBy){ + case RANDOM_VEHICLE: return true; + case MISSION_VEHICLE: return false; + case PARKED_VEHICLE: return true; + case PERMANENT_VEHICLE: return false; + } + return true; +} + +bool +CVehicle::CanPedOpenLocks(CPed *ped) +{ + if(m_nDoorLock == CARLOCK_LOCKED || + m_nDoorLock == CARLOCK_LOCKED_INITIALLY || + m_nDoorLock == CARLOCK_LOCKED_PLAYER_INSIDE || + m_nDoorLock == CARLOCK_LOCKED_BUT_CAN_BE_DAMAGED) + return false; + if(ped->IsPlayer() && m_nDoorLock == CARLOCK_LOCKOUT_PLAYER_ONLY) + return false; + return true; +} + +bool +CVehicle::CanDoorsBeDamaged(void) +{ + return m_nDoorLock == CARLOCK_NOT_USED || + m_nDoorLock == CARLOCK_UNLOCKED || + m_nDoorLock == CARLOCK_LOCKED_BUT_CAN_BE_DAMAGED; +} + +bool +CVehicle::CanPedEnterCar(void) +{ + // can't enter when car is on side + if(IsBike() || GetUp().z > 0.1f || GetUp().z < -0.1f){ + // also when car is moving too fast + if(m_vecMoveSpeed.MagnitudeSqr() > sq(0.2f)) + return false; + if(m_vecTurnSpeed.MagnitudeSqr() > sq(0.2f)) + return false; + return true; + } + return false; +} + +bool +CVehicle::CanPedExitCar(bool jumpExit) +{ + CVector up = GetUp(); + if(up.z > 0.1f || up.z < -0.1f){ + if (IsBoat()) + return true; + // can't exit when car is moving too fast + if(m_vecMoveSpeed.MagnitudeSqr() > 0.005f && !jumpExit) + return false; + // if car is slow enough, check turn speed + if(Abs(m_vecTurnSpeed.x) > 0.01f || + Abs(m_vecTurnSpeed.y) > 0.01f || + Abs(m_vecTurnSpeed.z) > 0.01f) + return false; + return true; + }else{ + // What is this? just > replaced by >= ?? + + // can't exit when car is moving too fast + if(m_vecMoveSpeed.MagnitudeSqr() >= 0.005f) + return false; + // if car is slow enough, check turn speed + if(Abs(m_vecTurnSpeed.x) >= 0.01f || + Abs(m_vecTurnSpeed.y) >= 0.01f || + Abs(m_vecTurnSpeed.z) >= 0.01f) + return false; + return true; + } +} + +bool +CVehicle::CanPedJumpOutCar(void) +{ + if(GetUp().z < 0.3f) + return false; + float speed = m_vecMoveSpeed.MagnitudeSqr(); + return speed < 0.1f || speed > 0.5f ? false : true; +} + +bool +CVehicle::CanPedJumpOffBike(void) +{ + if(pPassengers[0]) + return false; + return m_vecMoveSpeed.MagnitudeSqr() < 0.07f ? false : true; +} + +void +CVehicle::ChangeLawEnforcerState(uint8 enable) +{ + if (enable) { + if (!bIsLawEnforcer) { + bIsLawEnforcer = true; + CCarCtrl::NumLawEnforcerCars++; + } + } else { + if (bIsLawEnforcer) { + bIsLawEnforcer = false; + CCarCtrl::NumLawEnforcerCars--; + } + } +} + +CPed* +CVehicle::SetUpDriver(void) +{ + if(pDriver) + return pDriver; + if(VehicleCreatedBy != RANDOM_VEHICLE) + return nil; + + pDriver = CPopulation::AddPedInCar(this, true); + pDriver->m_pMyVehicle = this; + pDriver->m_pMyVehicle->RegisterReference((CEntity**)&pDriver->m_pMyVehicle); + pDriver->bInVehicle = true; + pDriver->SetPedState(PED_DRIVING); + if(bIsBus) + pDriver->bRenderPedInCar = false; + return pDriver; +} + +CPed* +CVehicle::SetupPassenger(int n) +{ + int i; + + if(pPassengers[n]) + return pPassengers[n]; + + if((IsTaxi() || IsLimo()) && n == 0) + pPassengers[0] = nil; + else{ + CPed *passenger = CPopulation::AddPedInCar(this, false); + pPassengers[n] = passenger; + passenger->m_pMyVehicle = this; + passenger->m_pMyVehicle->RegisterReference((CEntity**)&pPassengers[n]->m_pMyVehicle); + passenger->bInVehicle = true; + passenger->SetPedState(PED_DRIVING); + + if(passenger->m_nPedType == PEDTYPE_CIVMALE || passenger->m_nPedType == PEDTYPE_CIVFEMALE) + for(i = 0; i < n; i++) + if(pPassengers[i] && pPassengers[n] && + (pPassengers[i]->m_nPedType == PEDTYPE_CIVMALE || pPassengers[i]->m_nPedType == PEDTYPE_CIVFEMALE) && + passenger->GetModelIndex() == pPassengers[i]->GetModelIndex()){ + pPassengers[n] = nil; + CPopulation::RemovePed(passenger); + } + } + if(bIsBus && pPassengers[n]) + pPassengers[n]->bRenderPedInCar = false; + ++m_nNumPassengers; + return pPassengers[n]; +} + +void +CVehicle::SetDriver(CPed *driver) +{ + pDriver = driver; + pDriver->RegisterReference((CEntity**)&pDriver); + + if(bFreebies && driver == FindPlayerPed()){ + bFreebies = false; + switch(GetModelIndex()){ + case MI_AMBULAN: + FindPlayerPed()->m_fHealth = Max(FindPlayerPed()->m_fHealth, Min(FindPlayerPed()->m_fHealth + 20.0f, CWorld::Players[0].m_nMaxHealth)); + break; + + case MI_TAXI: + case MI_CABBIE: + case MI_ZEBRA: + case MI_KAUFMAN: + CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 12; + break; + + case MI_POLICE: + CStreaming::RequestModel(MI_SHOTGUN, STREAMFLAGS_DONT_REMOVE); + bFreebies = true; + break; + + case MI_ENFORCER: + driver->m_fArmour = Max(driver->m_fArmour, CWorld::Players[0].m_nMaxArmour); + break; + + case MI_CADDY: + if(!(driver->IsPlayer() && ((CPlayerPed*)driver)->DoesPlayerWantNewWeapon(WEAPONTYPE_GOLFCLUB, true))) + CStreaming::RequestModel(MI_GOLFCLUB, STREAMFLAGS_DONT_REMOVE); + break; + } + } + + if(IsBike()) + ApplyMoveForce(-0.02f*driver->m_fMass * GetUp()); + else + ApplyTurnForce(0.0f, 0.0f, -0.02f*driver->m_fMass, + driver->GetPosition().x - GetPosition().x, + driver->GetPosition().y - GetPosition().y, + 0.0f); +} + +bool +CVehicle::AddPassenger(CPed *passenger) +{ + int i; + + if(IsBike()) + ApplyTurnForce(-0.02f*passenger->m_fMass * GetUp(), -0.1f*GetForward()); + else + ApplyTurnForce(0.0f, 0.0f, -0.2f*passenger->m_fMass, + passenger->GetPosition().x - GetPosition().x, + passenger->GetPosition().y - GetPosition().y, + 0.0f); + + for(i = 0; i < m_nNumMaxPassengers; i++) + if(pPassengers[i] == nil){ + pPassengers[i] = passenger; + m_nNumPassengers++; + return true; + } + return false; +} + +bool +CVehicle::AddPassenger(CPed *passenger, uint8 n) +{ + if(bIsBus) + return AddPassenger(passenger); + + if(IsBike()) + ApplyTurnForce(-0.02f*passenger->m_fMass * GetUp(), -0.1f*GetForward()); + else + ApplyTurnForce(0.0f, 0.0f, -0.2f*passenger->m_fMass, + passenger->GetPosition().x - GetPosition().x, + passenger->GetPosition().y - GetPosition().y, + 0.0f); + + if(n < m_nNumMaxPassengers && pPassengers[n] == nil){ + pPassengers[n] = passenger; + m_nNumPassengers++; + return true; + } + return false; +} + +void +CVehicle::RemoveDriver(void) +{ +#ifdef FIX_BUGS + if (GetStatus() != STATUS_WRECKED) +#endif + SetStatus(STATUS_ABANDONED); + if(pDriver == FindPlayerPed()){ + if(GetModelIndex() == MI_POLICE && CStreaming::HasModelLoaded(MI_SHOTGUN)){ + if(bFreebies){ + if(((CPlayerPed*)pDriver)->DoesPlayerWantNewWeapon(WEAPONTYPE_SHOTGUN, true)) + pDriver->GiveWeapon(WEAPONTYPE_SHOTGUN, 5, true); + else + pDriver->GrantAmmo(WEAPONTYPE_SHOTGUN, 5); + bFreebies = false; + } + CStreaming::SetModelIsDeletable(MI_SHOTGUN); + }else if(GetModelIndex() == MI_CADDY && CStreaming::HasModelLoaded(MI_GOLFCLUB)){ + if(((CPlayerPed*)pDriver)->DoesPlayerWantNewWeapon(WEAPONTYPE_GOLFCLUB, true)) + pDriver->GiveWeapon(WEAPONTYPE_GOLFCLUB, 1, true); + CStreaming::SetModelIsDeletable(MI_GOLFCLUB); + } + } + pDriver = nil; +} + +void +CVehicle::RemovePassenger(CPed *p) +{ + if (IsTrain()){ + for (int i = 0; i < ARRAY_SIZE(pPassengers); i++){ + if (pPassengers[i] == p) { + pPassengers[i] = nil; + m_nNumPassengers--; + return; + } + } + return; + } + for (int i = 0; i < m_nNumMaxPassengers; i++){ + if (pPassengers[i] == p){ + pPassengers[i] = nil; + m_nNumPassengers--; + return; + } + } +} + +bool +CVehicle::IsDriver(CPed *ped) +{ + return ped && ped == pDriver; +} + +bool +CVehicle::IsDriver(int32 model) +{ + return pDriver && pDriver->GetModelIndex() == model; +} + +bool +CVehicle::IsPassenger(CPed *ped) +{ + int i; + if(ped == nil) + return false; + for(i = 0; i < 8; i++) + if(pPassengers[i] == ped) + return true; + return false; +} + +bool +CVehicle::IsPassenger(int32 model) +{ + int i; + for(i = 0; i < 8; i++) + if(pPassengers[i] && pPassengers[i]->GetModelIndex() == model) + return true; + return false; +} + +void +CVehicle::UpdatePassengerList(void) +{ + int i; + bool hasPassenger = false; + if(m_nNumPassengers) + for(i = 0; i < 8; i++) + if(pPassengers[i]){ + hasPassenger = true; + break; + } + if(!hasPassenger) + m_nNumPassengers = 0; +} + +void +CVehicle::ProcessCarAlarm(void) +{ + uint32 step; + + if(m_nAlarmState == 0 || m_nAlarmState == -1) + return; + + step = CTimer::GetTimeStepInMilliseconds(); + if((uint16)m_nAlarmState < step){ + m_nAlarmState = 0; + m_nCarHornTimer = 0; + }else + m_nAlarmState -= step; +} + +bool +CVehicle::IsSphereTouchingVehicle(float sx, float sy, float sz, float radius) +{ + float x, y, z; + // sphere relative to vehicle + CVector sph = CVector(sx, sy, sz) - GetPosition(); + CColModel *colmodel = GetColModel(); + + x = DotProduct(sph, GetRight()); + if(colmodel->boundingBox.min.x - radius > x || + colmodel->boundingBox.max.x + radius < x) + return false; + y = DotProduct(sph, GetForward()); + if(colmodel->boundingBox.min.y - radius > y || + colmodel->boundingBox.max.y + radius < y) + return false; + z = DotProduct(sph, GetUp()); + if(colmodel->boundingBox.min.z - radius > z || + colmodel->boundingBox.max.z + radius < z) + return false; + + return true; +} + +RpMaterial* +SetCompAlphaCB(RpMaterial *material, void *data) +{ + uint32 alpha = (uint32)(uintptr)data; + RwRGBA *col = (RwRGBA*)RpMaterialGetColor(material); // get rid of const + col->alpha = alpha; + return material; +} + +void +CVehicle::SetComponentAtomicAlpha(RpAtomic *atomic, int32 alpha) +{ + RpGeometry *geo = RpAtomicGetGeometry(atomic); + RpGeometrySetFlags(geo, RpGeometryGetFlags(geo) | rpGEOMETRYMODULATEMATERIALCOLOR); + RpGeometryForAllMaterials(geo, SetCompAlphaCB, (void*)alpha); +} + +void +CVehicle::UpdateClumpAlpha(void) +{ + int clumpAlpha = CVisibilityPlugins::GetClumpAlpha((RpClump*)m_rwObject); + if(bFadeOut){ + clumpAlpha -= 8; + if(clumpAlpha < 0) + clumpAlpha = 0; + }else if(clumpAlpha < 255){ + clumpAlpha += 16; + if(clumpAlpha > 255) + clumpAlpha = 255; + } + CVisibilityPlugins::SetClumpAlpha((RpClump*)m_rwObject, clumpAlpha); +} + +void +CVehicle::HeliDustGenerate(CEntity *heli, float radius, float ground, int rnd) +{ + int i; + float angle; + CColPoint point; + CEntity *entity; + uint8 r, g, b; + + if(heli == nil) + return; + + uint8 surface = SURFACE_TARMAC; + int frm = CTimer::GetFrameCounter() & 7; + float testLowZ = ground - 10.0f; + float dustSize = 0.0f; + float baseSize = 1.0f; + float offset = 1.0f; // when heli is tilted + float particleZ = -101.0f; + int n = 0; + + if(heli->GetModelIndex() == MI_RCGOBLIN || heli->GetModelIndex() == MI_RCRAIDER){ + radius = 3.0f; + dustSize = 0.04f; + baseSize = 0.07f; + offset = 0.3f; + } + + CVector heliPos = heli->GetPosition(); + + if(heli->IsVehicle() && ((CVehicle*)heli)->IsCar()){ + heliPos.x -= (heliPos.z - ground)*heli->GetUp().x*offset*0.5f; + heliPos.y -= (heliPos.z - ground)*heli->GetUp().y*offset*0.5f; + } + + float steamSize = 0.25f * radius * baseSize; + float splashSize = 0.3f * radius * baseSize; + + i = 0; + for(i = 0; i < 32+rnd; i++){ + angle = i * TWOPI/32.0f; + CVector pos(radius*Cos(angle), radius*Sin(angle), 0.0f); + CVector dir = CVector(pos.x, pos.y, 1.0f)*0.01f; + pos += heliPos; + + if(i < 32 && i == 4*frm){ + if(CWorld::ProcessVerticalLine(pos, testLowZ, point, entity, true, false, false, false, true, false, nil)){ + n = rnd; + particleZ = point.point.z; + surface = point.surfaceB; + }else + n = 0; + + float waterLevel = 0.0f; + if(CWaterLevel::GetWaterLevel(pos, &waterLevel, false) && waterLevel > particleZ){ + surface = SURFACE_WATER; + n = rnd; + particleZ = waterLevel; + } + } + + if(n){ + pos.z = particleZ; + if(surface == SURFACE_WATER){ + float red = (0.3*CTimeCycle::GetDirectionalRed() + CTimeCycle::GetAmbientRed_Obj())*255.0f/4.0f; + float green = (0.3*CTimeCycle::GetDirectionalGreen() + CTimeCycle::GetAmbientGreen_Obj())*255.0f/4.0f; + float blue = (0.3*CTimeCycle::GetDirectionalBlue() + CTimeCycle::GetAmbientBlue_Obj())*255.0f/4.0f; + r = Clamp(red, 0.0f, 255.0f); + g = Clamp(green, 0.0f, 255.0f); + b = Clamp(blue, 0.0f, 255.0f); + RwRGBA col1 = { r, g, b, (RwUInt8)CGeneral::GetRandomNumberInRange(8, 32) }; + RwRGBA col2 = { 255, 255, 255, 32 }; + + if(n&1) + CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, pos, dir, nil, steamSize, col2); + else + CParticle::AddParticle(PARTICLE_CAR_SPLASH, pos, dir, nil, splashSize, col1, + CGeneral::GetRandomNumberInRange(0.0f, 10.0f), + CGeneral::GetRandomNumberInRange(0.0f, 90.0f), 1); + }else{ + switch(surface){ + default: + case SURFACE_TARMAC: + r = 10; + g = 10; + b = 10; + break; + case SURFACE_GRASS: + r = 10; + g = 10; + b = 3; + break; + case SURFACE_GRAVEL: + r = 10; + g = 8; + b = 7; + break; + case SURFACE_MUD_DRY: + r = 10; + g = 6; + b = 3; + break; + case SURFACE_SAND: + case SURFACE_SAND_BEACH: + r = 10; + g = 10; + b = 7; + break; + } + RwRGBA col = { r, g, b, 32 }; + if(heliPos.z - pos.z < 20.0f) + CParticle::AddParticle(PARTICLE_HELI_DUST, pos, dir, nil, dustSize, col); + } + + n--; + } + } +} + +#define GLARE_MIN_DIST (13.0f) +#define GLARE_FULL_DIST (30.0f) +#define GLARE_MIN_ANGLE (0.99f) +#define GLARE_FULL_ANGLE (0.995f) + +void +CVehicle::DoSunGlare(void) +{ + if(bRenderScorched || GetPosition().z < 0.0f || + GetVehicleAppearance() != VEHICLE_APPEARANCE_CAR || CWeather::SunGlare <= 0.0f) + return; + + CVector camDir = TheCamera.GetPosition() - GetPosition(); + float dist = camDir.Magnitude(); + camDir *= 2.0f/dist; + CVector glareVec = camDir + CTimeCycle::GetSunDirection(); + CVector localGlareVec; + localGlareVec.x = DotProduct(glareVec, GetRight()); + localGlareVec.y = DotProduct(glareVec, GetForward()); + localGlareVec.z = 0.0; + localGlareVec.Normalise(); + + CVector2D fwd2D = GetForward(); + fwd2D.Normalise(); + CVector2D camDir2D = camDir; + camDir2D.Normalise(); + float fwdness = Abs(DotProduct2D(fwd2D, camDir2D)); + + // check angle + float strength; + if(fwdness > GLARE_FULL_ANGLE) + strength = 1.0f; + else if(fwdness > GLARE_MIN_ANGLE) + strength = (fwdness - GLARE_MIN_ANGLE)/(GLARE_FULL_ANGLE-GLARE_MIN_ANGLE); + else + return; + // check distance + if(dist > GLARE_FULL_DIST){ + // no max distance + }else if(dist > GLARE_MIN_DIST) + strength *= (dist - GLARE_MIN_DIST)/(GLARE_FULL_DIST - GLARE_MIN_DIST); + else + return; + + float intens = 0.8f * strength * CWeather::SunGlare; + int r = intens * (CTimeCycle::GetSunCoreRed() + 2*255)/3.0f; + int g = intens * (CTimeCycle::GetSunCoreGreen() + 2*255)/3.0f; + int b = intens * (CTimeCycle::GetSunCoreBlue() + 2*255)/3.0f; + + CColModel *colmodel = GetColModel(); + CCollision::CalculateTrianglePlanes(colmodel); + + int i; + for(i = 0; i < colmodel->numTriangles-2; i += 2){ + int a1 = colmodel->triangles[i].a; + int b1 = colmodel->triangles[i].b; + int c1 = colmodel->triangles[i].c; + int a2 = colmodel->triangles[i+1].a; + int b2 = colmodel->triangles[i+1].b; + int c2 = colmodel->triangles[i+1].c; + CVector vert1 = colmodel->vertices[a1].Get(); + CVector vert4; + // Need an upward surface + if(vert1.z <= 0.0f) + continue; + + // trying to find a quad here + int numTri2Verts = 0; + if(a2 != a1 && a2 != b1 && a2 != c1){ + // a2 is not in tri1 + numTri2Verts++; + vert4 = colmodel->vertices[a2].Get(); + } + if(b2 != a1 && b2 != b1 && b2 != c1){ + // b2 is not in tri1 + numTri2Verts++; + vert4 = colmodel->vertices[b2].Get(); + } + if(c2 != a1 && c2 != b1 && c2 != c1){ + // c2 is not in tri1 + numTri2Verts++; + vert4 = colmodel->vertices[c2].Get(); + } + // Need exactly one vertex from tri2 for a quad with tri1 + if(numTri2Verts != 1) + continue; + + CVector mid = (vert1 + colmodel->vertices[b1].Get() + colmodel->vertices[c1].Get() + vert4)/4.0f; + float dy = mid.y - vert1.y; + float dx = mid.x - vert1.x; + float dist = 1.4f * Min(Abs(dx), Abs(dy)); + if(dist > 0.6f){ + CVector pos = GetMatrix() * (dist * localGlareVec + mid) + camDir; + CCoronas::RegisterCorona((uintptr)this + 27 + i, + r, g, b, 255, + pos, 0.9f*CWeather::SunGlare, 90.0f, + CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, + CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, + CCoronas::STREAK_OFF, 0.0f); + } + } +} + +void +CVehicle::KillPedsInVehicle(void) +{ + int i; + if(pDriver){ + CDarkel::RegisterKillByPlayer(pDriver, WEAPONTYPE_EXPLOSION); + if(pDriver->GetPedState() == PED_DRIVING){ + pDriver->SetDead(); + if(!pDriver->IsPlayer()) + pDriver->FlagToDestroyWhenNextProcessed(); + }else + pDriver->SetDie(); + } + for(i = 0; i < m_nNumMaxPassengers; i++){ + if(pPassengers[i]){ + CDarkel::RegisterKillByPlayer(pPassengers[i], WEAPONTYPE_EXPLOSION); + if(pPassengers[i]->GetPedState() == PED_DRIVING){ + pPassengers[i]->SetDead(); + if(!pPassengers[i]->IsPlayer()) + pPassengers[i]->FlagToDestroyWhenNextProcessed(); + }else + pPassengers[i]->SetDie(); + } + } +} + +void +DestroyVehicleAndDriverAndPassengers(CVehicle* pVehicle) +{ + if (pVehicle->pDriver) { + CDarkel::RegisterKillByPlayer(pVehicle->pDriver, WEAPONTYPE_UNIDENTIFIED); + pVehicle->pDriver->FlagToDestroyWhenNextProcessed(); + } + for (int i = 0; i < pVehicle->m_nNumMaxPassengers; i++) { + if (pVehicle->pPassengers[i]) { + CDarkel::RegisterKillByPlayer(pVehicle->pPassengers[i], WEAPONTYPE_UNIDENTIFIED); + pVehicle->pPassengers[i]->FlagToDestroyWhenNextProcessed(); + } + } + CWorld::Remove(pVehicle); + delete pVehicle; +} + +#ifdef COMPATIBLE_SAVES +void +CVehicle::Save(uint8*& buf) +{ + ZeroSaveBuf(buf, 4); + WriteSaveBuf(buf, GetRight().x); + WriteSaveBuf(buf, GetRight().y); + WriteSaveBuf(buf, GetRight().z); + ZeroSaveBuf(buf, 4); + WriteSaveBuf(buf, GetForward().x); + WriteSaveBuf(buf, GetForward().y); + WriteSaveBuf(buf, GetForward().z); + ZeroSaveBuf(buf, 4); + WriteSaveBuf(buf, GetUp().x); + WriteSaveBuf(buf, GetUp().y); + WriteSaveBuf(buf, GetUp().z); + ZeroSaveBuf(buf, 4); + WriteSaveBuf(buf, GetPosition().x); + WriteSaveBuf(buf, GetPosition().y); + WriteSaveBuf(buf, GetPosition().z); + ZeroSaveBuf(buf, 16); + SaveEntityFlags(buf); + ZeroSaveBuf(buf, 208); + AutoPilot.Save(buf); + WriteSaveBuf(buf, m_currentColour1); + WriteSaveBuf(buf, m_currentColour2); + ZeroSaveBuf(buf, 2); + WriteSaveBuf(buf, m_nAlarmState); + ZeroSaveBuf(buf, 42); + WriteSaveBuf(buf, m_nNumMaxPassengers); + ZeroSaveBuf(buf, 3); + WriteSaveBuf(buf, field_1D0[0]); + WriteSaveBuf(buf, field_1D0[1]); + WriteSaveBuf(buf, field_1D0[2]); + WriteSaveBuf(buf, field_1D0[3]); + ZeroSaveBuf(buf, 8); + WriteSaveBuf(buf, m_fSteerAngle); + WriteSaveBuf(buf, m_fGasPedal); + WriteSaveBuf(buf, m_fBrakePedal); + WriteSaveBuf(buf, VehicleCreatedBy); + uint8 flags = 0; + if (bIsLawEnforcer) flags |= BIT(0); + if (bIsLocked) flags |= BIT(3); + if (bEngineOn) flags |= BIT(4); + if (bIsHandbrakeOn) flags |= BIT(5); + if (bLightsOn) flags |= BIT(6); + if (bFreebies) flags |= BIT(7); + WriteSaveBuf(buf, flags); + ZeroSaveBuf(buf, 10); + WriteSaveBuf(buf, m_fHealth); + WriteSaveBuf(buf, m_nCurrentGear); + ZeroSaveBuf(buf, 3); + WriteSaveBuf(buf, m_fChangeGearTime); + ZeroSaveBuf(buf, 12); + WriteSaveBuf(buf, m_nTimeOfDeath); + ZeroSaveBuf(buf, 2); + WriteSaveBuf(buf, m_nBombTimer); + ZeroSaveBuf(buf, 12); + WriteSaveBuf(buf, m_nDoorLock); + ZeroSaveBuf(buf, 108); +} + +void +CVehicle::Load(uint8*& buf) +{ + CMatrix tmp; + SkipSaveBuf(buf, 4); + ReadSaveBuf(&tmp.GetRight().x, buf); + ReadSaveBuf(&tmp.GetRight().y, buf); + ReadSaveBuf(&tmp.GetRight().z, buf); + SkipSaveBuf(buf, 4); + ReadSaveBuf(&tmp.GetForward().x, buf); + ReadSaveBuf(&tmp.GetForward().y, buf); + ReadSaveBuf(&tmp.GetForward().z, buf); + SkipSaveBuf(buf, 4); + ReadSaveBuf(&tmp.GetUp().x, buf); + ReadSaveBuf(&tmp.GetUp().y, buf); + ReadSaveBuf(&tmp.GetUp().z, buf); + SkipSaveBuf(buf, 4); + ReadSaveBuf(&tmp.GetPosition().x, buf); + ReadSaveBuf(&tmp.GetPosition().y, buf); + ReadSaveBuf(&tmp.GetPosition().z, buf); + m_matrix = tmp; + SkipSaveBuf(buf, 16); + LoadEntityFlags(buf); + SkipSaveBuf(buf, 208); + AutoPilot.Load(buf); + ReadSaveBuf(&m_currentColour1, buf); + ReadSaveBuf(&m_currentColour2, buf); + SkipSaveBuf(buf, 2); + ReadSaveBuf(&m_nAlarmState, buf); + SkipSaveBuf(buf, 42); + ReadSaveBuf(&m_nNumMaxPassengers, buf); + SkipSaveBuf(buf, 3); + ReadSaveBuf(&field_1D0[0], buf); + ReadSaveBuf(&field_1D0[1], buf); + ReadSaveBuf(&field_1D0[2], buf); + ReadSaveBuf(&field_1D0[3], buf); + SkipSaveBuf(buf, 8); + ReadSaveBuf(&m_fSteerAngle, buf); + ReadSaveBuf(&m_fGasPedal, buf); + ReadSaveBuf(&m_fBrakePedal, buf); + ReadSaveBuf(&VehicleCreatedBy, buf); + uint8 flags; + ReadSaveBuf(&flags, buf); + bIsLawEnforcer = !!(flags & BIT(0)); + bIsLocked = !!(flags & BIT(3)); + bEngineOn = !!(flags & BIT(4)); + bIsHandbrakeOn = !!(flags & BIT(5)); + bLightsOn = !!(flags & BIT(6)); + bFreebies = !!(flags & BIT(7)); + SkipSaveBuf(buf, 10); + ReadSaveBuf(&m_fHealth, buf); + ReadSaveBuf(&m_nCurrentGear, buf); + SkipSaveBuf(buf, 3); + ReadSaveBuf(&m_fChangeGearTime, buf); + SkipSaveBuf(buf, 12); + ReadSaveBuf(&m_nTimeOfDeath, buf); + SkipSaveBuf(buf, 2); + ReadSaveBuf(&m_nBombTimer, buf); + SkipSaveBuf(buf, 12); + ReadSaveBuf(&m_nDoorLock, buf); + SkipSaveBuf(buf, 108); +} +#endif + +eVehicleAppearance +CVehicle::GetVehicleAppearance(void) +{ + uint32 flags = pHandling->Flags & 0xF0000; + if (flags == 0) + return VEHICLE_APPEARANCE_CAR; + if (flags == HANDLING_IS_BIKE) + return VEHICLE_APPEARANCE_BIKE; + if (flags == HANDLING_IS_HELI) + return VEHICLE_APPEARANCE_HELI; + if (flags == HANDLING_IS_PLANE) + return VEHICLE_APPEARANCE_PLANE; + if (flags == HANDLING_IS_BOAT) + return VEHICLE_APPEARANCE_BOAT; + return VEHICLE_APPEARANCE_NONE; +} + +bool +IsVehiclePointerValid(CVehicle* pVehicle) +{ + if (!pVehicle) + return false; + int index = CPools::GetVehiclePool()->GetJustIndex_NoFreeAssert(pVehicle); +#ifdef FIX_BUGS + if (index < 0 || index >= NUMVEHICLES) +#else + if (index < 0 || index > NUMVEHICLES) +#endif + return false; + return pVehicle->m_vehType == VEHICLE_TYPE_PLANE || pVehicle->m_entryInfoList.first; +} diff --git a/src/miami/vehicles/Vehicle.h b/src/miami/vehicles/Vehicle.h new file mode 100644 index 00000000..f83e1bb0 --- /dev/null +++ b/src/miami/vehicles/Vehicle.h @@ -0,0 +1,451 @@ +#pragma once + +#include "Physical.h" +#include "AutoPilot.h" +#include "ModelIndices.h" +#include "AnimationId.h" +#include "WeaponType.h" +#include "Collision.h" +#include "HandlingMgr.h" + +class CPed; +class CPlayerPed; +class CCopPed; +class CFire; + +enum { + RANDOM_VEHICLE = 1, + MISSION_VEHICLE = 2, + PARKED_VEHICLE = 3, + PERMANENT_VEHICLE = 4, +}; + +enum eCarNodes +{ + CAR_WHEEL_RF = 1, + CAR_WHEEL_RM, + CAR_WHEEL_RB, + CAR_WHEEL_LF, + CAR_WHEEL_LM, + CAR_WHEEL_LB, + CAR_BUMP_FRONT, + CAR_BUMP_REAR, + CAR_WING_RF, + CAR_WING_RR, + CAR_DOOR_RF, + CAR_DOOR_RR, + CAR_WING_LF, + CAR_WING_LR, + CAR_DOOR_LF, + CAR_DOOR_LR, + CAR_BONNET, + CAR_BOOT, + CAR_WINDSCREEN, + NUM_CAR_NODES, +}; + +enum { + CAR_DOOR_FLAG_UNKNOWN = 0x0, + CAR_DOOR_FLAG_LF = 0x1, + CAR_DOOR_FLAG_LR = 0x2, + CAR_DOOR_FLAG_RF = 0x4, + CAR_DOOR_FLAG_RR = 0x8 +}; + +enum eCarLock { + CARLOCK_NOT_USED, + CARLOCK_UNLOCKED, + CARLOCK_LOCKED, + CARLOCK_LOCKOUT_PLAYER_ONLY, + CARLOCK_LOCKED_PLAYER_INSIDE, + CARLOCK_LOCKED_INITIALLY, + CARLOCK_FORCE_SHUT_DOORS, + CARLOCK_LOCKED_BUT_CAN_BE_DAMAGED +}; + +enum eBombType +{ + CARBOMB_NONE, + CARBOMB_TIMED, + CARBOMB_ONIGNITION, + CARBOMB_REMOTE, + CARBOMB_TIMEDACTIVE, + CARBOMB_ONIGNITIONACTIVE, +}; + +enum eDoors +{ + DOOR_BONNET = 0, + DOOR_BOOT, + DOOR_FRONT_LEFT, + DOOR_FRONT_RIGHT, + DOOR_REAR_LEFT, + DOOR_REAR_RIGHT +}; + +enum ePanels +{ + VEHPANEL_FRONT_LEFT, + VEHPANEL_FRONT_RIGHT, + VEHPANEL_REAR_LEFT, + VEHPANEL_REAR_RIGHT, + VEHPANEL_WINDSCREEN, + VEHBUMPER_FRONT, + VEHBUMPER_REAR, +}; + +enum eLights +{ + VEHLIGHT_FRONT_LEFT, + VEHLIGHT_FRONT_RIGHT, + VEHLIGHT_REAR_LEFT, + VEHLIGHT_REAR_RIGHT, +}; + +enum +{ + CAR_PIECE_BONNET = 1, + CAR_PIECE_BOOT, + CAR_PIECE_BUMP_FRONT, + CAR_PIECE_BUMP_REAR, + CAR_PIECE_DOOR_LF, + CAR_PIECE_DOOR_RF, + CAR_PIECE_DOOR_LR, + CAR_PIECE_DOOR_RR, + CAR_PIECE_WING_LF, + CAR_PIECE_WING_RF, + CAR_PIECE_WING_LR, + CAR_PIECE_WING_RR, + CAR_PIECE_WHEEL_LF, + CAR_PIECE_WHEEL_RF, + CAR_PIECE_WHEEL_LR, + CAR_PIECE_WHEEL_RR, + CAR_PIECE_WINDSCREEN, +}; + +enum tWheelState +{ + WHEEL_STATE_NORMAL, // standing still or rolling normally + WHEEL_STATE_SPINNING, // rotating but not moving + WHEEL_STATE_SKIDDING, + WHEEL_STATE_FIXED, // not rotating +}; + +enum eFlightModel +{ + FLIGHT_MODEL_DODO, + FLIGHT_MODEL_RCPLANE, + FLIGHT_MODEL_RCHELI, + FLIGHT_MODEL_SEAPLANE, + FLIGHT_MODEL_PLANE_UNUSED, + FLIGHT_MODEL_PLANE, + FLIGHT_MODEL_HELI +}; + +enum eVehicleAppearance +{ + VEHICLE_APPEARANCE_NONE, + VEHICLE_APPEARANCE_CAR, + VEHICLE_APPEARANCE_BIKE, + VEHICLE_APPEARANCE_HELI, + VEHICLE_APPEARANCE_BOAT, + VEHICLE_APPEARANCE_PLANE, +}; + +// TODO: what is this even? +enum eBikeWheelSpecial +{ + BIKE_WHEELSPEC_0, // both wheels on ground + BIKE_WHEELSPEC_1, // rear wheel on ground + BIKE_WHEELSPEC_2, // only front wheel on ground + BIKE_WHEELSPEC_3, // can't happen +}; + +enum +{ + ROTOR_TOP = 3, + ROTOR_FRONT = 4, + ROTOR_RIGHT = 5, + ROTOR_LEFT = 7, + ROTOR_BACK = 8, + ROTOR_BOTTOM = 9, +}; + +class CVehicle : public CPhysical +{ +public: + tHandlingData *pHandling; + tFlyingHandlingData *pFlyingHandling; + CAutoPilot AutoPilot; + uint8 m_currentColour1; + uint8 m_currentColour2; + int8 m_aExtras[2]; + int16 m_nAlarmState; + int16 m_nRouteSeed; + CPed *pDriver; + CPed *pPassengers[8]; + uint8 m_nNumPassengers; + int8 m_nNumGettingIn; + int8 m_nGettingInFlags; + int8 m_nGettingOutFlags; + uint8 m_nNumMaxPassengers; + float field_1D0[4]; + CEntity *m_pCurGroundEntity; + CFire *m_pCarFire; + float m_fSteerAngle; + float m_fGasPedal; + float m_fBrakePedal; + uint8 VehicleCreatedBy; + + // cf. https://github.com/DK22Pac/plugin-sdk/blob/master/plugin_sa/game_sa/CVehicle.h from R* + uint8 bIsLawEnforcer: 1; // Is this guy chasing the player at the moment + uint8 bIsAmbulanceOnDuty: 1; // Ambulance trying to get to an accident + uint8 bIsFireTruckOnDuty: 1; // Firetruck trying to get to a fire + uint8 bIsLocked: 1; // Is this guy locked by the script (cannot be removed) + uint8 bEngineOn: 1; // For sound purposes. Parked cars have their engines switched off (so do destroyed cars) + uint8 bIsHandbrakeOn: 1; // How's the handbrake doing ? + uint8 bLightsOn: 1; // Are the lights switched on ? + uint8 bFreebies: 1; // Any freebies left in this vehicle ? + + uint8 bIsVan: 1; // Is this vehicle a van (doors at back of vehicle) + uint8 bIsBus: 1; // Is this vehicle a bus + uint8 bIsBig: 1; // Is this vehicle a bus + uint8 bLowVehicle: 1; // Need this for sporty type cars to use low getting-in/out anims + uint8 bComedyControls : 1; // Will make the car hard to control (hopefully in a funny way) + uint8 bWarnedPeds : 1; // Has scan and warn peds of danger been processed? + uint8 bCraneMessageDone : 1; // A crane message has been printed for this car allready + uint8 bExtendedRange : 1; // This vehicle needs to be a bit further away to get deleted + + uint8 bTakeLessDamage : 1; // This vehicle is stronger (takes about 1/4 of damage) + uint8 bIsDamaged : 1; // This vehicle has been damaged and is displaying all its components + uint8 bHasBeenOwnedByPlayer : 1;// To work out whether stealing it is a crime + uint8 bFadeOut : 1; // Fade vehicle out + uint8 bIsBeingCarJacked : 1; // Fade vehicle out + uint8 bCreateRoadBlockPeds : 1; // If this vehicle gets close enough we will create peds (coppers or gang members) round it + uint8 bCanBeDamaged : 1; // Set to FALSE during cut scenes to avoid explosions + uint8 bUsingSpecialColModel : 1;// Is player vehicle using special collision model, stored in player strucure + + uint8 bOccupantsHaveBeenGenerated : 1; // Is true if the occupants have already been generated. (Shouldn't happen again) + uint8 bGunSwitchedOff : 1; // Level designers can use this to switch off guns on boats + uint8 bVehicleColProcessed : 1;// Has ProcessEntityCollision been processed for this car? + uint8 bIsCarParkVehicle : 1; // Car has been created using the special CAR_PARK script command + uint8 bHasAlreadyBeenRecorded : 1; // Used for replays + uint8 bPartOfConvoy : 1; + uint8 bHeliMinimumTilt : 1; // This heli should have almost no tilt really + uint8 bAudioChangingGear : 1; // sounds like vehicle is changing gear + + uint8 bIsDrowning : 1; // is vehicle occupants taking damage in water (i.e. vehicle is dead in water) + uint8 bTyresDontBurst : 1; // If this is set the tyres are invincible + uint8 bCreatedAsPoliceVehicle : 1;// True if this guy was created as a police vehicle (enforcer, policecar, miamivice car etc) + uint8 bRestingOnPhysical : 1; // Dont go static cause car is sitting on a physical object that might get removed + uint8 bParking : 1; + uint8 bCanPark : 1; +#if (!defined GTA_PS2 || defined FIX_BUGS) + uint8 m_bombType : 3; +#endif + uint8 bDriverLastFrame : 1; + + int8 m_numPedsUseItAsCover; + uint8 m_nAmmoInClip; // Used to make the guns on boat do a reload (20 by default) + int8 m_nPacManPickupsCarried; + uint8 m_nRoadblockType; + float m_fHealth; // 1000.0f = full health. 250.0f = fire. 0 -> explode + uint8 m_nCurrentGear; + float m_fChangeGearTime; +#if (!defined GTA_PS2 || defined FIX_BUGS) + CEntity* m_pBombRigger; +#endif + uint32 m_nSetPieceExtendedRangeTime; + uint32 m_nGunFiringTime; // last time when gun on vehicle was fired (used on boats) + uint32 m_nTimeOfDeath; + uint16 m_nTimeBlocked; + int16 m_nBombTimer; // goes down with each frame + CEntity *m_pBlowUpEntity; + float m_fMapObjectHeightAhead; // front Z? + float m_fMapObjectHeightBehind; // rear Z? + eCarLock m_nDoorLock; + int8 m_nLastWeaponDamage; // see eWeaponType, -1 if no damage + CEntity *m_pLastDamageEntity; + uint8 m_nRadioStation; + uint8 m_bRainAudioCounter; + uint8 m_bRainSamplesCounter; + uint32 m_nCarHornTimer; + uint8 m_nCarHornPattern; + bool m_bSirenOrAlarm; + uint8 m_nCarHornDelay; + int8 m_comedyControlState; + CStoredCollPoly m_aCollPolys[2]; // poly which is under front/rear part of car + float m_fSteerInput; + eVehicleType m_vehType; + + static void *operator new(size_t) throw(); + static void *operator new(size_t sz, int slot) throw(); + static void operator delete(void*, size_t) throw(); + static void operator delete(void*, int) throw(); + + CVehicle(void) {} // FAKE + CVehicle(uint8 CreatedBy); + ~CVehicle(void); + // from CEntity + void SetModelIndex(uint32 id); + bool SetupLighting(void); + void RemoveLighting(bool); + void FlagToDestroyWhenNextProcessed(void) {} + + virtual void ProcessControlInputs(uint8) {} + virtual void GetComponentWorldPosition(int32 component, CVector &pos) {} + virtual bool IsComponentPresent(int32 component) { return false; } + virtual void SetComponentRotation(int32 component, CVector rotation) {} + virtual void OpenDoor(int32, eDoors door, float) {} + virtual void ProcessOpenDoor(uint32, uint32, float) {} + virtual bool IsDoorReady(eDoors door) { return false; } + virtual bool IsDoorFullyOpen(eDoors door) { return false; } + virtual bool IsDoorClosed(eDoors door) { return false; } + virtual bool IsDoorMissing(eDoors door) { return false; } + virtual bool IsDoorReady(uint32 door) { return false; } + virtual bool IsDoorMissing(uint32 door) { return false; } + virtual bool IsOpenTopCar(void) { return false; } + virtual void RemoveRefsToVehicle(CEntity *ent) {} + virtual void BlowUpCar(CEntity *ent) {} + virtual bool SetUpWheelColModel(CColModel *colModel) { return false; } + virtual void BurstTyre(uint8 tyre, bool applyForces) {} + virtual bool IsRoomForPedToLeaveCar(uint32 component, CVector *forcedDoorPos) { return false; } + virtual bool IsClearToDriveAway(void); + virtual float GetHeightAboveRoad(void); + virtual void PlayCarHorn(void) {} +#ifdef COMPATIBLE_SAVES + virtual void Save(uint8*& buf); + virtual void Load(uint8*& buf); +#endif + + eVehicleAppearance GetVehicleAppearance(void); + bool IsCar(void) { return m_vehType == VEHICLE_TYPE_CAR; } + bool IsBoat(void) { return m_vehType == VEHICLE_TYPE_BOAT; } + bool IsTrain(void) { return m_vehType == VEHICLE_TYPE_TRAIN; } + bool IsHeli(void) { return m_vehType == VEHICLE_TYPE_HELI; } + bool IsPlane(void) { return m_vehType == VEHICLE_TYPE_PLANE; } + bool IsBike(void) { return m_vehType == VEHICLE_TYPE_BIKE; } + + void FlyingControl(eFlightModel flightModel); + bool DoBladeCollision(CVector pos, CMatrix &matrix, int16 rotorType, float radius, float damageMult); + bool BladeColSectorList(CPtrList &list, CColModel &rotorColModel, CMatrix &matrix, int16 rotorType, float damageMult); + + void ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelContactSpeed, CVector &wheelContactPoint, + int32 wheelsOnGround, float thrust, float brake, float adhesion, int8 wheelId, float *wheelSpeed, tWheelState *wheelState, uint16 wheelStatus); + void ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelContactSpeed, CVector &wheelContactPoint, + int32 wheelsOnGround, float thrust, float brake, float adhesion, float destabTraction, int8 wheelId, float *wheelSpeed, tWheelState *wheelState, eBikeWheelSpecial special, uint16 wheelStatus); + void ExtinguishCarFire(void); + void ProcessDelayedExplosion(void); + float ProcessWheelRotation(tWheelState state, const CVector &fwd, const CVector &speed, float radius); + int FindTyreNearestPoint(float x, float y); + bool IsLawEnforcementVehicle(void); + void ChangeLawEnforcerState(uint8 enable); + bool UsesSiren(void); + bool IsVehicleNormal(void); + bool CarHasRoof(void); + bool IsUpsideDown(void); + bool IsOnItsSide(void); + bool CanBeDeleted(void); + bool CanPedOpenLocks(CPed *ped); + bool CanDoorsBeDamaged(void); + bool CanPedEnterCar(void); + bool CanPedExitCar(bool jumpExit); + bool CanPedJumpOutCar(void); + bool CanPedJumpOffBike(void); + // do these two actually return something? + CPed *SetUpDriver(void); + CPed *SetupPassenger(int n); + void SetDriver(CPed *driver); + bool AddPassenger(CPed *passenger); + bool AddPassenger(CPed *passenger, uint8 n); + void RemovePassenger(CPed *passenger); + void RemoveDriver(void); + bool IsDriver(CPed *ped); + bool IsDriver(int32 model); + bool IsPassenger(CPed *ped); + bool IsPassenger(int32 model); + void UpdatePassengerList(void); + void ProcessCarAlarm(void); + bool IsSphereTouchingVehicle(float sx, float sy, float sz, float radius); + bool ShufflePassengersToMakeSpace(void); + void MakeNonDraggedPedsLeaveVehicle(CPed *ped1, CPed *ped2, CPlayerPed *&player, CCopPed *&cop); + void InflictDamage(CEntity *damagedBy, eWeaponType weaponType, float damage, CVector pos = CVector(0.0f, 0.0f, 0.0f)); + void DoFixedMachineGuns(void); + void FireFixedMachineGuns(void); + void ActivateBomb(void); + void ActivateBombWhenEntered(void); + void KillPedsInVehicle(void); + + void SetComponentAtomicAlpha(RpAtomic *atomic, int32 alpha); + void UpdateClumpAlpha(void); + + static void HeliDustGenerate(CEntity *heli, float radius, float ground, int rnd); + void DoSunGlare(void); + + bool IsAlarmOn(void) { return m_nAlarmState != 0 && m_nAlarmState != -1 && GetStatus() != STATUS_WRECKED; } + CVehicleModelInfo* GetModelInfo() { return (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); } + bool IsTaxi(void) { return GetModelIndex() == MI_TAXI || GetModelIndex() == MI_CABBIE || GetModelIndex() == MI_ZEBRA || GetModelIndex() == MI_KAUFMAN; } + bool IsLimo(void) { return GetModelIndex() == MI_STRETCH || GetModelIndex() == MI_LOVEFIST; } + bool IsRealHeli(void) { return !!(pHandling->Flags & HANDLING_IS_HELI); } + bool IsRealPlane(void) { return !!(pHandling->Flags & HANDLING_IS_PLANE); } + + static bool bWheelsOnlyCheat; + static bool bAllDodosCheat; + static bool bCheat3; + static bool bCheat4; + static bool bCheat5; + static bool bCheat8; + static bool bCheat9; + static bool bCheat10; + static bool bHoverCheat; + static bool bAllTaxisHaveNitro; + static bool m_bDisableMouseSteering; + static bool bDisableRemoteDetonation; + static bool bDisableRemoteDetonationOnContact; +#ifndef MASTER + static bool m_bDisplayHandlingInfo; +#endif +}; + +void DestroyVehicleAndDriverAndPassengers(CVehicle* pVehicle); +bool IsVehiclePointerValid(CVehicle* pVehicle); + +// Names of functions below are made up by us. + +// Used in III and VC. +inline int8 GetCarDoorFlag(int32 carnode) { + switch (carnode) { + case CAR_DOOR_LF: + return CAR_DOOR_FLAG_LF; + case CAR_DOOR_LR: + return CAR_DOOR_FLAG_LR; + case CAR_DOOR_RF: + return CAR_DOOR_FLAG_RF; + case CAR_DOOR_RR: + return CAR_DOOR_FLAG_RR; + default: + return CAR_DOOR_FLAG_UNKNOWN; + } +} + +// VC. Accounts the case numMaxPassengers == 0, only for m_nGettingInFlags. +inline int8 GetEnterCarDoorFlag(int32 carnode, uint8 numMaxPassengers) { + switch (carnode) { + case CAR_DOOR_RF: + return CAR_DOOR_FLAG_RF; + case CAR_DOOR_RR: + return CAR_DOOR_FLAG_RR; + case CAR_DOOR_LF: + if (numMaxPassengers != 0) + return CAR_DOOR_FLAG_LF; + else + return CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_LR; + case CAR_DOOR_LR: + if (numMaxPassengers != 0) + return CAR_DOOR_FLAG_LR; + else + return CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_LR; + default: + return CAR_DOOR_FLAG_UNKNOWN; + } +} diff --git a/src/miami/weapons/BulletInfo.cpp b/src/miami/weapons/BulletInfo.cpp new file mode 100644 index 00000000..8bd4e5f1 --- /dev/null +++ b/src/miami/weapons/BulletInfo.cpp @@ -0,0 +1,341 @@ +#include "common.h" + +#include "BulletInfo.h" + +#include "AnimBlendAssociation.h" +#include "DMAudio.h" +#include "AudioScriptObject.h" +#ifdef FIX_BUGS +#include "Collision.h" +#endif +#include "RpAnimBlend.h" +#include "Entity.h" +#include "EventList.h" +#include "Fire.h" +#include "Glass.h" +#include "Particle.h" +#include "Ped.h" +#include "Object.h" +#include "Stats.h" +#include "Timer.h" +#include "Vehicle.h" +#include "Weapon.h" +#include "WeaponInfo.h" +#include "World.h" +#include "SurfaceTable.h" +#include "Heli.h" + +#ifdef SQUEEZE_PERFORMANCE +uint32 bulletInfoInUse; +#endif + +#define BULLET_LIFETIME (1000) +#define NUM_PED_BLOOD_PARTICLES (8) +#define BLOOD_PARTICLE_OFFSET (CVector(0.0f, 0.0f, 0.0f)) +#define NUM_VEHICLE_SPARKS (16) +#define NUM_TYRE_POP_SMOKES (4) +#define NUM_OTHER_SPARKS (8) +#define BULLET_HIT_FORCE (7.5f) + +#define BULLET_BOUNDARY_MIN_X -2400.0f +#define BULLET_BOUNDARY_MAX_X 1600.0f +#define BULLET_BOUNDARY_MIN_Y -2000.0f +#define BULLET_BOUNDARY_MAX_Y 2000.0f + +CBulletInfo gaBulletInfo[CBulletInfo::NUM_BULLETS]; +bool bPlayerSniperBullet; +CVector PlayerSniperBulletStart; +CVector PlayerSniperBulletEnd; + +void CBulletInfo::Initialise(void) +{ + debug("Initialising CBulletInfo...\n"); + for (int i = 0; i < NUM_BULLETS; i++) { + gaBulletInfo[i].m_bInUse = false; + gaBulletInfo[i].m_eWeaponType = WEAPONTYPE_COLT45; + gaBulletInfo[i].m_fTimer = 0.0f; + gaBulletInfo[i].m_pSource = nil; + } + debug("CBulletInfo ready\n"); +#ifdef SQUEEZE_PERFORMANCE + bulletInfoInUse = 0; +#endif +} + +void CBulletInfo::Shutdown(void) +{ + debug("Shutting down CBulletInfo...\n"); + debug("CBulletInfo shut down\n"); +} + +bool CBulletInfo::AddBullet(CEntity* pSource, eWeaponType type, CVector vecPosition, CVector vecSpeed) +{ + int i; + for (i = 0; i < NUM_BULLETS; i++) { + if (!gaBulletInfo[i].m_bInUse) + break; + } + if (i == NUM_BULLETS) + return false; + gaBulletInfo[i].m_pSource = pSource; + gaBulletInfo[i].m_eWeaponType = type; + gaBulletInfo[i].m_nDamage = CWeaponInfo::GetWeaponInfo(type)->m_nDamage; + gaBulletInfo[i].m_vecPosition = vecPosition; + gaBulletInfo[i].m_vecSpeed = vecSpeed; + gaBulletInfo[i].m_fTimer = CTimer::GetTimeInMilliseconds() + BULLET_LIFETIME; + gaBulletInfo[i].m_bInUse = true; + +#ifdef SQUEEZE_PERFORMANCE + bulletInfoInUse++; +#endif + return true; +} + +void CBulletInfo::Update(void) +{ +#ifdef SQUEEZE_PERFORMANCE + if (bulletInfoInUse == 0) + return; +#endif + bPlayerSniperBullet = false; + for (int i = 0; i < NUM_BULLETS; i++) { + CBulletInfo* pBullet = &gaBulletInfo[i]; + if (pBullet->m_pSource && pBullet->m_pSource->IsPed() && !((CPed*)pBullet->m_pSource)->IsPointerValid()) + pBullet->m_pSource = nil; + if (!pBullet->m_bInUse) + continue; + if (CTimer::GetTimeInMilliseconds() > pBullet->m_fTimer) { + pBullet->m_bInUse = false; +#ifdef SQUEEZE_PERFORMANCE + bulletInfoInUse--; +#endif + } + CVector vecOldPos = pBullet->m_vecPosition; + CVector vecNewPos = pBullet->m_vecPosition + pBullet->m_vecSpeed * CTimer::GetTimeStep() * 0.5f; + + if ( vecNewPos.x <= BULLET_BOUNDARY_MIN_X || vecNewPos.x >= BULLET_BOUNDARY_MAX_X || vecNewPos.y <= BULLET_BOUNDARY_MIN_Y || vecNewPos.y >= BULLET_BOUNDARY_MAX_Y ) { + pBullet->m_bInUse = false; + continue; + } + CWorld::bIncludeDeadPeds = true; + CWorld::bIncludeBikers = true; + CWorld::bIncludeCarTyres = true; + CWorld::pIgnoreEntity = pBullet->m_pSource; + CColPoint point; + CEntity* pHitEntity; + if (CWorld::ProcessLineOfSight(vecOldPos, vecNewPos, point, pHitEntity, true, true, true, true, true, false, false, true)) { + + CWeapon::CheckForShootingVehicleOccupant(&pHitEntity, &point, pBullet->m_eWeaponType, vecOldPos, vecNewPos); + if (pHitEntity->IsPed()) { + CPed* pPed = (CPed*)pHitEntity; + if (!pPed->DyingOrDead() && pPed != pBullet->m_pSource) { + if (pPed->IsPedInControl() && !pPed->bIsDucking) { + pPed->ClearAttackByRemovingAnim(); + CAnimBlendAssociation* pAnim = CAnimManager::AddAnimation(pPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HITBYGUN_FRONT); + pAnim->SetBlend(0.0f, 8.0f); + } + pPed->InflictDamage(pBullet->m_pSource, pBullet->m_eWeaponType, pBullet->m_nDamage, (ePedPieceTypes)point.pieceB, pPed->GetLocalDirection(pPed->GetPosition() - point.point)); + CEventList::RegisterEvent(pPed->m_nPedType == PEDTYPE_COP ? EVENT_SHOOT_COP : EVENT_SHOOT_PED, EVENT_ENTITY_PED, pPed, (CPed*)pBullet->m_pSource, 1000); + + if (CGame::nastyGame) { + CVector vecParticleDirection = (point.point - pPed->GetPosition()) * 0.01f; + vecParticleDirection.z = 0.01f; + if (pPed->GetIsOnScreen()) { + for (int j = 0; j < NUM_PED_BLOOD_PARTICLES; j++) + CParticle::AddParticle(PARTICLE_BLOOD_SMALL, point.point + BLOOD_PARTICLE_OFFSET, vecParticleDirection); + } + if (pPed->GetPedState() == PED_DEAD) { + CAnimBlendAssociation* pAnim; + if (RpAnimBlendClumpGetFirstAssociation(pPed->GetClump(), ASSOC_FRONTAL)) + pAnim = CAnimManager::BlendAnimation(pPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR_FRONT, 8.0f); + else + pAnim = CAnimManager::BlendAnimation(pPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR, 8.0f); + if (pAnim) { + pAnim->SetCurrentTime(0.0f); + pAnim->flags |= ASSOC_RUNNING; + pAnim->flags &= ~ASSOC_FADEOUTWHENDONE; + } + } + } + + pBullet->m_bInUse = false; +#ifdef SQUEEZE_PERFORMANCE + bulletInfoInUse--; +#endif + vecNewPos = point.point; + } + } + else if (pHitEntity->IsVehicle()) { + CEntity *source = pBullet->m_pSource; + if ( !source || !source->IsPed() || ((CPed*)source)->m_attachedTo != pHitEntity) { + if ( point.pieceB >= CAR_PIECE_WHEEL_LF && point.pieceB <= CAR_PIECE_WHEEL_RR ) { + ((CVehicle*)pHitEntity)->BurstTyre(point.pieceB, true); + for (int j=0; jInflictDamage(source, pBullet->m_eWeaponType, pBullet->m_nDamage); + if ( pBullet->m_eWeaponType == WEAPONTYPE_FLAMETHROWER ) { + gFireManager.StartFire(pHitEntity, pBullet->m_pSource, 0.8f, 1); + } else { + for (int j=0; jm_bInUse = false; +#ifdef SQUEEZE_PERFORMANCE + bulletInfoInUse--; +#endif + vecNewPos = point.point; +#endif + } else { + for (int j = 0; j < NUM_OTHER_SPARKS; j++) + CParticle::AddParticle(PARTICLE_SPARK, point.point, point.normal / 20); + CEntity *source = pBullet->m_pSource; + if ( !source || !source->IsPed() || ((CPed*)source)->m_attachedTo != pHitEntity) { + if (pHitEntity->IsObject()) { + CObject *pHitObject = (CObject*)pHitEntity; + if ( !pHitObject->bInfiniteMass && pHitObject->m_fCollisionDamageMultiplier < 99.9f) { + bool notStatic = !pHitObject->GetIsStatic(); + if (notStatic && pHitObject->m_fUprootLimit <= 0.0f) { + pHitObject->bIsStatic = false; + pHitObject->AddToMovingList(); + } + + notStatic = !pHitObject->GetIsStatic(); + if (!notStatic) { + CVector moveForce = point.normal * -BULLET_HIT_FORCE; + pHitObject->ApplyMoveForce(moveForce.x, moveForce.y, moveForce.z); + } + } else if (pHitObject->m_nCollisionDamageEffect >= DAMAGE_EFFECT_SMASH_COMPLETELY) { + pHitObject->ObjectDamage(50.f); + } + } + } +#ifdef FIX_BUGS + pBullet->m_bInUse = false; +#ifdef SQUEEZE_PERFORMANCE + bulletInfoInUse--; +#endif + vecNewPos = point.point; +#endif + } + if (pBullet->m_eWeaponType == WEAPONTYPE_SNIPERRIFLE || pBullet->m_eWeaponType == WEAPONTYPE_LASERSCOPE) { + cAudioScriptObject* pAudio; + switch (pHitEntity->GetType()) { + case ENTITY_TYPE_BUILDING: + if (!DMAudio.IsAudioInitialised()) + break; + + pAudio = new cAudioScriptObject(); + if (pAudio) + pAudio->Reset(); + pAudio->Posn = pHitEntity->GetPosition(); + pAudio->AudioId = SCRIPT_SOUND_BULLET_HIT_GROUND_1; + pAudio->AudioEntity = AEHANDLE_NONE; + DMAudio.CreateOneShotScriptObject(pAudio); + break; + case ENTITY_TYPE_OBJECT: + if (!DMAudio.IsAudioInitialised()) + break; + + pAudio = new cAudioScriptObject(); + if (pAudio) + pAudio->Reset(); + pAudio->Posn = pHitEntity->GetPosition(); + pAudio->AudioId = SCRIPT_SOUND_BULLET_HIT_GROUND_2; + pAudio->AudioEntity = AEHANDLE_NONE; + DMAudio.CreateOneShotScriptObject(pAudio); + break; + case ENTITY_TYPE_DUMMY: + if (!DMAudio.IsAudioInitialised()) + break; + + pAudio = new cAudioScriptObject(); + if (pAudio) + pAudio->Reset(); + pAudio->Posn = pHitEntity->GetPosition(); + pAudio->AudioId = SCRIPT_SOUND_BULLET_HIT_GROUND_3; + pAudio->AudioEntity = AEHANDLE_NONE; + DMAudio.CreateOneShotScriptObject(pAudio); + break; + case ENTITY_TYPE_PED: + ++CStats::BulletsThatHit; + DMAudio.PlayOneShot(((CPed*)pHitEntity)->m_audioEntityId, SOUND_WEAPON_HIT_PED, 1.0f); + ((CPed*)pHitEntity)->Say(SOUND_PED_BULLET_HIT); + break; + case ENTITY_TYPE_VEHICLE: + ++CStats::BulletsThatHit; + DMAudio.PlayOneShot(((CVehicle*)pHitEntity)->m_audioEntityId, SOUND_WEAPON_HIT_VEHICLE, 1.0f); + break; + default: break; + } + } + CGlass::WasGlassHitByBullet(pHitEntity, point.point); + CWeapon::BlowUpExplosiveThings(pHitEntity); + } + CWorld::pIgnoreEntity = nil; + CWorld::bIncludeDeadPeds = false; + CWorld::bIncludeCarTyres = false; + CWorld::bIncludeBikers = false; + if (pBullet->m_eWeaponType == WEAPONTYPE_SNIPERRIFLE || pBullet->m_eWeaponType == WEAPONTYPE_LASERSCOPE) { + bPlayerSniperBullet = true; + PlayerSniperBulletStart = pBullet->m_vecPosition; + PlayerSniperBulletEnd = vecNewPos; + } + pBullet->m_vecPosition = vecNewPos; + CHeli::TestSniperCollision(&PlayerSniperBulletStart, &PlayerSniperBulletEnd); + } +} + +bool CBulletInfo::TestForSniperBullet(float x1, float x2, float y1, float y2, float z1, float z2) +{ + if (!bPlayerSniperBullet) + return false; +#ifdef FIX_BUGS // original code is not going work anyway... + CColLine line(PlayerSniperBulletStart, PlayerSniperBulletEnd); + CColBox box; + box.Set(CVector(x1, y1, z1), CVector(x2, y2, z2), SURFACE_DEFAULT, 0); + return CCollision::TestLineBox(line, box); +#else + float minP = 0.0f; + float maxP = 1.0f; + float minX = Min(PlayerSniperBulletStart.x, PlayerSniperBulletEnd.x); + float maxX = Max(PlayerSniperBulletStart.x, PlayerSniperBulletEnd.x); + if (minX < x2 || maxX > x1) { + if (minX < x1) + minP = Min(minP, (x1 - minX) / (maxX - minX)); + if (maxX > x2) + maxP = Max(maxP, (maxX - x2) / (maxX - minX)); + } + else + return false; + float minY = Min(PlayerSniperBulletStart.y, PlayerSniperBulletEnd.y); + float maxY = Max(PlayerSniperBulletStart.y, PlayerSniperBulletEnd.y); + if (minY < y2 || maxY > y1) { + if (minY < y1) + minP = Min(minP, (y1 - minY) / (maxY - minY)); + if (maxY > y2) + maxP = Max(maxP, (maxY - y2) / (maxY - minY)); + } +#ifdef FIX_BUGS + else + return false; +#endif + float minZ = Min(PlayerSniperBulletStart.z, PlayerSniperBulletEnd.z); + float maxZ = Max(PlayerSniperBulletStart.z, PlayerSniperBulletEnd.z); + if (minZ < z2 || maxZ > z1) { + if (minZ < z1) + minP = Min(minP, (z1 - minZ) / (maxZ - minZ)); + if (maxZ > z2) + maxP = Max(maxP, (maxZ - z2) / (maxZ - minZ)); + } + else + return false; + return minP <= maxP; +#endif +} diff --git a/src/miami/weapons/BulletInfo.h b/src/miami/weapons/BulletInfo.h new file mode 100644 index 00000000..cf1dd27f --- /dev/null +++ b/src/miami/weapons/BulletInfo.h @@ -0,0 +1,25 @@ +#pragma once + +#include "WeaponType.h" + +class CEntity; + +class CBulletInfo +{ + eWeaponType m_eWeaponType; + CEntity* m_pSource; + float m_fTimer; // big mistake + bool m_bInUse; + CVector m_vecPosition; + CVector m_vecSpeed; + int16 m_nDamage; +public: + enum { + NUM_BULLETS = 100 + }; + static void Initialise(void); + static void Shutdown(void); + static bool AddBullet(CEntity* pSource, eWeaponType type, CVector vecPosition, CVector vecSpeed); + static void Update(void); + static bool TestForSniperBullet(float x1, float x2, float y1, float y2, float z1, float z2); +}; \ No newline at end of file diff --git a/src/miami/weapons/Explosion.cpp b/src/miami/weapons/Explosion.cpp new file mode 100644 index 00000000..7683ed97 --- /dev/null +++ b/src/miami/weapons/Explosion.cpp @@ -0,0 +1,514 @@ +#include "common.h" + +#include "Automobile.h" +#include "Bike.h" +#include "Camera.h" +#include "Coronas.h" +#include "DMAudio.h" +#include "Entity.h" +#include "EventList.h" +#include "Explosion.h" +#include "General.h" +#include "Fire.h" +#include "Pad.h" +#include "Particle.h" +#include "PointLights.h" +#include "Shadows.h" +#include "Timer.h" +#include "Vehicle.h" +#include "WaterLevel.h" +#include "World.h" + +CExplosion gaExplosion[NUM_EXPLOSIONS]; + +// these two were not initialised in original code, I'm really not sure what were they meant to be +RwRGBA colMedExpl = { 0, 0, 0, 0 }; +RwRGBA colUpdate = { 0, 0, 0, 0 }; + +const RwRGBA colAddExplosion = { 160, 160, 160, 255 }; +const RwRGBA colGrenade = { 96, 96, 96, 255 }; + +int AudioHandle = AEHANDLE_NONE; + +void +CExplosion::Initialise() +{ + debug("Initialising CExplosion...\n"); + ClearAllExplosions(); + AudioHandle = DMAudio.CreateEntity(AUDIOTYPE_EXPLOSION, (void*)1); + if (AudioHandle >= 0) + DMAudio.SetEntityStatus(AudioHandle, TRUE); + debug("CExplosion ready\n"); +} + +void +CExplosion::ClearAllExplosions() +{ + for (int i = 0; i < ARRAY_SIZE(gaExplosion); i++) { + gaExplosion[i].m_ExplosionType = EXPLOSION_GRENADE; + gaExplosion[i].m_vecPosition = CVector(0.0f, 0.0f, 0.0f); + gaExplosion[i].m_fRadius = 1.0f; + gaExplosion[i].m_fPropagationRate = 0.0f; + gaExplosion[i].m_fZshift = 0.0f; + gaExplosion[i].m_pCreatorEntity = nil; + gaExplosion[i].m_pVictimEntity = nil; + gaExplosion[i].m_fStopTime = 0.0f; + gaExplosion[i].m_nIteration = 0; + gaExplosion[i].m_fStartTime = 0.0f; + gaExplosion[i].m_bIsBoat = false; + gaExplosion[i].m_bMakeSound = true; + } +} + +void +CExplosion::Shutdown() +{ + debug("Shutting down CExplosion...\n"); + if (AudioHandle >= 0) { + DMAudio.DestroyEntity(AudioHandle); + AudioHandle = AEHANDLE_NONE; + } + debug("CExplosion shut down\n"); +} + +int8 +CExplosion::GetExplosionActiveCounter(uint8 id) +{ + return gaExplosion[id].m_nActiveCounter; +} + +void +CExplosion::ResetExplosionActiveCounter(uint8 id) +{ + gaExplosion[id].m_nActiveCounter = 0; +} + +uint8 +CExplosion::GetExplosionType(uint8 id) +{ + return gaExplosion[id].m_ExplosionType; +} + +bool +CExplosion::DoesExplosionMakeSound(uint8 id) +{ + return gaExplosion[id].m_bMakeSound; +}; + +CVector * +CExplosion::GetExplosionPosition(uint8 id) +{ + return &gaExplosion[id].m_vecPosition; +} + +bool +#ifdef SIMPLER_MISSIONS +CExplosion::AddExplosion(CEntity* explodingEntity, CEntity* culprit, eExplosionType type, const CVector& pos, uint32 lifetime, bool makeSound, float radius) +#else +CExplosion::AddExplosion(CEntity *explodingEntity, CEntity *culprit, eExplosionType type, const CVector &pos, uint32 lifetime, bool makeSound) +#endif +{ + CVector pPosn; + CVector posGround; + + RwRGBA colorMedium = colMedExpl; + RwRGBA color = colAddExplosion; + RwRGBA colorGrenade = colGrenade; + bool bDontExplode = false; + pPosn = pos; + pPosn.z += 5.0f; +#ifdef FIX_BUGS + CShadows::AddPermanentShadow(SHADOWTEX_CAR, gpShadowHeliTex, &pPosn, 8.0f, 0.0f, 0.0f, -8.0f, 200, 0, 0, 0, 10.0f, 30000, 1.0f); +#else + // last two arguments are swapped resulting in no shadow + CShadows::AddPermanentShadow(SHADOWTEX_CAR, gpShadowHeliTex, &pPosn, 8.0f, 0.0f, 0.0f, -8.0f, 200, 0, 0, 0, 10.0f, 1, 30000.0f); +#endif + + int n = 0; +#ifdef FIX_BUGS + while (n < ARRAY_SIZE(gaExplosion) && gaExplosion[n].m_nIteration != 0) +#else + // array overrun is UB + while (gaExplosion[n].m_nIteration != 0 && n < ARRAY_SIZE(gaExplosion)) +#endif + n++; + if (n == ARRAY_SIZE(gaExplosion)) + return false; + + CExplosion &explosion = gaExplosion[n]; + explosion.m_ExplosionType = type; + explosion.m_vecPosition = pos; + explosion.m_fRadius = 1.0f; + explosion.m_fZshift = 0.0f; + explosion.m_pCreatorEntity = culprit; + if (culprit != nil) + culprit->RegisterReference(&explosion.m_pCreatorEntity); + explosion.m_pVictimEntity = explodingEntity; + if (explodingEntity != nil) + explodingEntity->RegisterReference(&explosion.m_pVictimEntity); + explosion.m_nIteration = 1; + explosion.m_nActiveCounter = 1; + explosion.m_bIsBoat = false; + explosion.m_bMakeSound = makeSound; + explosion.m_nParticlesExpireTime = lifetime != 0 ? CTimer::GetTimeInMilliseconds() + lifetime : 0; + switch (type) + { + case EXPLOSION_GRENADE: +#ifdef SIMPLER_MISSIONS + explosion.m_fRadius = (radius == -1.0f ? 9.0f : radius); +#else + explosion.m_fRadius = 9.0f; +#endif + explosion.m_fPower = 300.0f; + explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750; + explosion.m_fPropagationRate = 0.5f; + posGround = pos; + posGround.z = CWorld::FindGroundZFor3DCoord(posGround.x, posGround.y, posGround.z + 3.0f, nil); + CEventList::RegisterEvent(EVENT_EXPLOSION, posGround, 250); + if (Distance(explosion.m_vecPosition, TheCamera.GetPosition()) < 40.0f) { + uint8 tmp = CGeneral::GetRandomNumberInRange(0, 64) - 64; + colorGrenade.green += tmp; + colorGrenade.blue += tmp; + CParticle::AddParticle(PARTICLE_EXPLOSION_LFAST, explosion.m_vecPosition, CVector(0.0f, 0.0f, 0.0f), nil, 4.5f, colorGrenade); + } + break; + case EXPLOSION_MOLOTOV: + { + explosion.m_fRadius = 6.0f; + explosion.m_fPower = 0.0f; + explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 3000; + explosion.m_fPropagationRate = 0.5f; + posGround = pos; + bool found; + float tmp = CWorld::FindGroundZFor3DCoord(posGround.x, posGround.y, posGround.z + 3.0f, &found); + if (found) + posGround.z = tmp; + + float waterLevel; + if (CWaterLevel::GetWaterLevelNoWaves(posGround.x, posGround.y, posGround.z, &waterLevel) + && posGround.z < waterLevel && waterLevel - 6.0f < posGround.z) { // some subway/tunnels check? + bDontExplode = true; + } else if (found) { + gFireManager.StartFire(posGround, 1.8f, false); + } + break; + } + case EXPLOSION_ROCKET: + explosion.m_fRadius = 10.0f; + explosion.m_fPower = 300.0f; + explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750; + explosion.m_fPropagationRate = 0.5f; + CEventList::RegisterEvent(EVENT_EXPLOSION, pos, 250); + if (Distance(explosion.m_vecPosition, TheCamera.GetPosition()) < 40.0f) + CParticle::AddParticle(PARTICLE_EXPLOSION_LFAST, explosion.m_vecPosition, CVector(0.0f, 0.0f, 0.0f), nil, 5.5f, color); + break; + case EXPLOSION_CAR: + case EXPLOSION_CAR_QUICK: + case EXPLOSION_BOAT: + explosion.m_fRadius = 9.0f; + explosion.m_fPower = 300.0f; + explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 4250; + explosion.m_fPropagationRate = 0.5f; + explosion.m_fStartTime = CTimer::GetTimeInMilliseconds(); + if (explosion.m_pVictimEntity != nil) { + if (explosion.m_pVictimEntity->IsVehicle() && ((CVehicle*)explosion.m_pVictimEntity)->IsBoat()) + explosion.m_bIsBoat = true; + CEventList::RegisterEvent(EVENT_EXPLOSION, EVENT_ENTITY_VEHICLE, explosion.m_pVictimEntity, nil, 1000); + } else { + CEventList::RegisterEvent(EVENT_EXPLOSION, pos, 1000); + } + + if (explosion.m_pVictimEntity != nil && !explosion.m_bIsBoat) { + CVehicle *veh = (CVehicle*)explosion.m_pVictimEntity; + CVector componentPos; + + if (veh->IsBike()) { + veh->GetComponentWorldPosition(BIKE_FORKS_REAR, componentPos); + } else if (veh->IsComponentPresent(CAR_BUMP_REAR) && veh->IsComponentPresent(CAR_WHEEL_LB)) { //mb it's another enum + CVector tmpVec; + veh->GetComponentWorldPosition(CAR_BUMP_REAR, componentPos); + veh->GetComponentWorldPosition(CAR_WHEEL_LB, tmpVec); + componentPos += tmpVec; + componentPos /= 2.0f; + } else if (veh->IsComponentPresent(CAR_BOOT)) { + veh->GetComponentWorldPosition(CAR_BOOT, componentPos); + } + if (componentPos.x != 0.0f) { + int rn = (CGeneral::GetRandomNumber() & 1) + 1; + for (int i = 0; i < rn; i++) + CParticle::AddJetExplosion(componentPos, (CGeneral::GetRandomNumber() & 7) / 7.0f + 1.5f, 0.5f); + } + } + break; + case EXPLOSION_HELI: + case EXPLOSION_HELI2: + if (type == EXPLOSION_HELI2) { + explosion.m_fRadius = 12.0f; + explosion.m_fPower = 500.0f; + } else { + explosion.m_fRadius = 6.0f; + explosion.m_fPower = 300.0f; + } + explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750; + explosion.m_fPropagationRate = 0.5f; + explosion.m_fStartTime = CTimer::GetTimeInMilliseconds(); + for (int i = 0; i < 10; i++) { + CVector randpos; + + randpos.x = CGeneral::GetRandomNumber(); + randpos.y = CGeneral::GetRandomNumber(); + randpos.z = CGeneral::GetRandomNumber(); + randpos -= CVector(128, 128, 128); + randpos /= 20.0f; + randpos += pos; + + CParticle::AddParticle(PARTICLE_EXPLOSION_MFAST, randpos, CVector(0.0f, 0.0f, 0.0f), nil, 2.5f, color); + + randpos.x = CGeneral::GetRandomNumber(); + randpos.y = CGeneral::GetRandomNumber(); + randpos.z = CGeneral::GetRandomNumber(); + randpos -= CVector(128, 128, 128); + randpos /= 20.0f; + randpos += pos; + + CParticle::AddParticle(PARTICLE_EXPLOSION_LFAST, randpos, CVector(0.0f, 0.0f, 0.0f), nil, 5.0f, color); + + randpos.x = CGeneral::GetRandomNumber(); + randpos.y = CGeneral::GetRandomNumber(); + randpos.z = CGeneral::GetRandomNumber(); + randpos -= CVector(128, 128, 128); + randpos /= 20.0f; + randpos += pos; + + CParticle::AddJetExplosion(randpos, 1.4f, 3.0f); + } + CEventList::RegisterEvent(EVENT_EXPLOSION, pos, 1000); + break; + case EXPLOSION_MINE: + explosion.m_fRadius = 10.0f; + explosion.m_fPower = 150.0f; + explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750; + explosion.m_fPropagationRate = 0.5f; + posGround = pos; + //posGround.z = + CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z + 4.0f, nil); // BUG? result is unused + CEventList::RegisterEvent(EVENT_EXPLOSION, posGround, 250); + break; + case EXPLOSION_BARREL: + explosion.m_fRadius = 7.0f; + explosion.m_fPower = 150.0f; + explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750; + explosion.m_fPropagationRate = 0.5f; + for (int i = 0; i < 6; i++) { + CVector randpos; + randpos.x = CGeneral::GetRandomNumber(); + randpos.y = CGeneral::GetRandomNumber(); + randpos.z = CGeneral::GetRandomNumber(); + randpos -= CVector(128, 128, 128); + randpos.x /= 50.0f; + randpos.y /= 50.0f; + randpos.z /= 25.0f; + randpos += pos; + CParticle::AddParticle(PARTICLE_EXPLOSION_MEDIUM, randpos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, colorMedium); + } + posGround = pos; + //posGround.z = + CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z + 4.0f, nil); // BUG? result is unused + CEventList::RegisterEvent(EVENT_EXPLOSION, posGround, 250); + break; + case EXPLOSION_TANK_GRENADE: + explosion.m_fRadius = 10.0f; + explosion.m_fPower = 150.0f; + explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750; + explosion.m_fPropagationRate = 0.5f; + posGround = pos; + //posGround.z = + CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z + 4.0f, nil); // BUG? result is unused + CEventList::RegisterEvent(EVENT_EXPLOSION, posGround, 250); + break; + case EXPLOSION_HELI_BOMB: + explosion.m_fRadius = 8.0f; + explosion.m_fPower = 50.0f; + explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750; + explosion.m_fPropagationRate = 0.5f; + posGround = pos; + //posGround.z = + CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z + 4.0f, nil); // BUG? result is unused + CEventList::RegisterEvent(EVENT_EXPLOSION, posGround, 250); + break; + default: + debug("Undefined explosion type, AddExplosion, Explosion.cpp"); + break; + } + + if (bDontExplode) { + explosion.m_nIteration = 0; + return false; + } + + if (explosion.m_fPower != 0.0f && explosion.m_nParticlesExpireTime == 0) + CWorld::TriggerExplosion(pos, explosion.m_fRadius, explosion.m_fPower, culprit, (type == EXPLOSION_ROCKET || type == EXPLOSION_CAR_QUICK || type == EXPLOSION_MINE || type == EXPLOSION_BARREL || type == EXPLOSION_TANK_GRENADE || type == EXPLOSION_HELI_BOMB)); + + if (type == EXPLOSION_MOLOTOV) { + TheCamera.CamShake(0.2f, pos.x, pos.y, pos.z); + } else { + TheCamera.CamShake(0.6f, pos.x, pos.y, pos.z); + CPad::GetPad(0)->StartShake_Distance(300, 128, pos.x, pos.y, pos.z); + } + return true; +} + +void +CExplosion::Update() +{ + RwRGBA color = colUpdate; + for (int i = 0; i < ARRAY_SIZE(gaExplosion); i++) { + CExplosion &explosion = gaExplosion[i]; + if (explosion.m_nIteration == 0) continue; + + if (explosion.m_nParticlesExpireTime != 0) { + if (CTimer::GetTimeInMilliseconds() > explosion.m_nParticlesExpireTime) { + explosion.m_nParticlesExpireTime = 0; + if (explosion.m_fPower != 0.0f) + CWorld::TriggerExplosion(explosion.m_vecPosition, explosion.m_fRadius, explosion.m_fPower, explosion.m_pCreatorEntity, (explosion.m_ExplosionType == EXPLOSION_ROCKET || explosion.m_ExplosionType == EXPLOSION_CAR_QUICK || explosion.m_ExplosionType == EXPLOSION_MINE || explosion.m_ExplosionType == EXPLOSION_BARREL || explosion.m_ExplosionType == EXPLOSION_TANK_GRENADE || explosion.m_ExplosionType == EXPLOSION_HELI_BOMB)); + } + } else { + explosion.m_fRadius += explosion.m_fPropagationRate * CTimer::GetTimeStep(); + int32 someTime = explosion.m_fStopTime - CTimer::GetTimeInMilliseconds(); + switch (explosion.m_ExplosionType) + { + case EXPLOSION_GRENADE: + case EXPLOSION_ROCKET: + case EXPLOSION_HELI: + case EXPLOSION_HELI2: + case EXPLOSION_MINE: + case EXPLOSION_BARREL: + if (CTimer::GetFrameCounter() & 1) { + CPointLights::AddLight(CPointLights::LIGHT_POINT, explosion.m_vecPosition, CVector(0.0f, 0.0f, 0.0f), 20.0f, 1.0f, 1.0f, 0.5f, CPointLights::FOG_NONE, true); + CCoronas::RegisterCorona((uintptr)&explosion, 255, 255, 200, 255, explosion.m_vecPosition, 8.0f, 120.0f, gpCoronaTexture[0], CCoronas::TYPE_NORMAL, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + } else + CCoronas::RegisterCorona((uintptr)&explosion, 128, 128, 100, 255, explosion.m_vecPosition, 8.0f, 120.0f, gpCoronaTexture[0], CCoronas::TYPE_NORMAL, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + CCoronas::RegisterCorona((uintptr)&explosion + 1, 30, 30, 25, 255, explosion.m_vecPosition, explosion.m_fRadius, 120.0f, gpCoronaTexture[7], CCoronas::TYPE_STAR, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + break; + case EXPLOSION_MOLOTOV: + CWorld::SetPedsOnFire(explosion.m_vecPosition.x, explosion.m_vecPosition.y, explosion.m_vecPosition.z, 6.0f, explosion.m_pCreatorEntity); + CWorld::SetCarsOnFire(explosion.m_vecPosition.x, explosion.m_vecPosition.y, explosion.m_vecPosition.z, 6.0f, explosion.m_pCreatorEntity); + if (explosion.m_nIteration < 10) { + if (explosion.m_nIteration == 1) { + CVector point1 = explosion.m_vecPosition; + point1.z += 5.0f; + CColPoint colPoint; + CEntity *pEntity; + if (CWorld::ProcessVerticalLine(point1, -1000.0f, colPoint, pEntity, true, false, false, false, true, false, nil)) + explosion.m_fZshift = colPoint.point.z; + else + explosion.m_fZshift = explosion.m_vecPosition.z; + } + float ff = ((float)explosion.m_nIteration * 0.55f); + for (int i = 0; i < 5 * ff; i++) { + float angle = CGeneral::GetRandomNumber() / 256.0f * 6.28f; + + CVector pos = explosion.m_vecPosition; + pos.x += ff * Sin(angle); + pos.y += ff * Cos(angle); + pos.z = explosion.m_fZshift + 0.5f; + CParticle::AddParticle(PARTICLE_EXPLOSION_MEDIUM, pos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, color, CGeneral::GetRandomNumberInRange(-3.0f, 3.0f), CGeneral::GetRandomNumberInRange(-180.0f, 180.0f)); + } + } + break; + case EXPLOSION_CAR: + case EXPLOSION_CAR_QUICK: + case EXPLOSION_BOAT: + if (someTime >= 3500) { + if (explosion.m_pVictimEntity != nil) { + if ((CGeneral::GetRandomNumber() & 0xF) == 0 && !explosion.m_bIsBoat) { + CVehicle *veh = (CVehicle*)explosion.m_pVictimEntity; + uint8 component = CAR_WING_LR; + + // miami leftover + if (veh->IsBike()) + component = BIKE_FORKS_REAR; + + if (veh->IsComponentPresent(component)) { + CVector componentPos; + veh->GetComponentWorldPosition(component, componentPos); + CParticle::AddJetExplosion(componentPos, 0.5f, 0.0f); + } + } + if (CTimer::GetTimeInMilliseconds() > explosion.m_fStartTime) { + explosion.m_fStartTime = CTimer::GetTimeInMilliseconds() + 125 + (CGeneral::GetRandomNumber() & 0x7F); + CVector pos = explosion.m_pVictimEntity->GetPosition(); + for (int i = 0; i < (CGeneral::GetRandomNumber() & 1) + 1; i++) + CParticle::AddParticle(PARTICLE_EXPLOSION_MEDIUM, pos, CVector(0.0f, 0.0f, 0.0f), nil, 3.5f, color); + } + } + if (CTimer::GetFrameCounter() & 1) { + CPointLights::AddLight(CPointLights::LIGHT_POINT, explosion.m_vecPosition, CVector(0.0f, 0.0f, 0.0f), 15.0f, 1.0f, 0.0f, 0.0f, CPointLights::FOG_NONE, true); + CCoronas::RegisterCorona((uintptr)&explosion, 200, 100, 0, 255, explosion.m_vecPosition, 6.0f, 80.0f, gpCoronaTexture[0], CCoronas::TYPE_NORMAL, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + } else + CCoronas::RegisterCorona((uintptr)&explosion, 128, 0, 0, 255, explosion.m_vecPosition, 8.0f, 80.0f, gpCoronaTexture[0], CCoronas::TYPE_NORMAL, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + + CCoronas::RegisterCorona((uintptr)&explosion + 1, 30, 15, 0, 255, explosion.m_vecPosition, explosion.m_fRadius, 80.0f, gpCoronaTexture[7], CCoronas::TYPE_STAR, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); + } else if (explosion.m_nIteration & 1) { + if (explosion.m_pVictimEntity != nil) + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, explosion.m_pVictimEntity->GetPosition(), CVector(0.0f, 0.0f, 0.0f), nil, CGeneral::GetRandomNumberInRange(0.5f, 0.8f), color); + CVector pos = explosion.m_vecPosition; + pos.z += 1.0f; + CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, pos, CVector(0.0f, 0.0f, 0.11f), nil, CGeneral::GetRandomNumberInRange(0.5f, 2.0f), color); + } + break; + case EXPLOSION_TANK_GRENADE: + case EXPLOSION_HELI_BOMB: + if (explosion.m_nIteration < 5) { + float ff = ((float)explosion.m_nIteration * 0.65f); + for (int i = 0; i < 10 * ff; i++) { + uint8 x = CGeneral::GetRandomNumber(), y = CGeneral::GetRandomNumber(), z = CGeneral::GetRandomNumber(); + CVector pos(x - 128, y - 128, (z % 128) + 1); + + pos.Normalise(); + pos *= (explosion.m_nIteration + 1) * ff / 5.0f; + pos += explosion.m_vecPosition; + pos.z += 0.5f; + CParticle::AddParticle(PARTICLE_EXPLOSION_LARGE, pos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, color, CGeneral::GetRandomNumberInRange(-3.0f, 3.0f), CGeneral::GetRandomNumberInRange(-180.0f, 180.0f)); + } + } + break; + default: + break; + } + if (someTime > 0) + explosion.m_nIteration++; + else + explosion.m_nIteration = 0; + } + } +} + +bool +CExplosion::TestForExplosionInArea(eExplosionType type, float x1, float x2, float y1, float y2, float z1, float z2) +{ + for (int i = 0; i < ARRAY_SIZE(gaExplosion); i++) { + if (gaExplosion[i].m_nIteration != 0) { + if (type == gaExplosion[i].m_ExplosionType) { + if (gaExplosion[i].m_vecPosition.x >= x1 && gaExplosion[i].m_vecPosition.x <= x2) { + if (gaExplosion[i].m_vecPosition.y >= y1 && gaExplosion[i].m_vecPosition.y <= y2) { + if (gaExplosion[i].m_vecPosition.z >= z1 && gaExplosion[i].m_vecPosition.z <= z2) + return true; + } + } + } + } + } + return false; +} + +void +CExplosion::RemoveAllExplosionsInArea(CVector pos, float radius) +{ + for (int i = 0; i < ARRAY_SIZE(gaExplosion); i++) { + if (gaExplosion[i].m_nIteration != 0) { + if ((pos - gaExplosion[i].m_vecPosition).MagnitudeSqr() < SQR(radius)) + gaExplosion[i].m_nIteration = 0; + } + } +} \ No newline at end of file diff --git a/src/miami/weapons/Explosion.h b/src/miami/weapons/Explosion.h new file mode 100644 index 00000000..e76c99ea --- /dev/null +++ b/src/miami/weapons/Explosion.h @@ -0,0 +1,58 @@ +#pragma once + +class CEntity; +class CVector; + +enum eExplosionType +{ + EXPLOSION_GRENADE, + EXPLOSION_MOLOTOV, + EXPLOSION_ROCKET, + EXPLOSION_CAR, + EXPLOSION_CAR_QUICK, + EXPLOSION_BOAT, + EXPLOSION_HELI, + EXPLOSION_HELI2, + EXPLOSION_MINE, + EXPLOSION_BARREL, + EXPLOSION_TANK_GRENADE, + EXPLOSION_HELI_BOMB +}; + +class CExplosion +{ + eExplosionType m_ExplosionType; + CVector m_vecPosition; + float m_fRadius; + float m_fPropagationRate; + CEntity *m_pCreatorEntity; + CEntity *m_pVictimEntity; + float m_fStopTime; + uint8 m_nIteration; + uint8 m_nActiveCounter; + bool m_bIsBoat; + bool m_bMakeSound; + float m_fStartTime; + uint32 m_nParticlesExpireTime; + float m_fPower; + float m_fZshift; +public: +#ifdef SIMPLER_MISSIONS + static bool AddExplosion(CEntity *explodingEntity, CEntity *culprit, eExplosionType type, const CVector &pos, uint32 lifetime, bool makeSound = true, float radius = -1.0f); +#else + static bool AddExplosion(CEntity* explodingEntity, CEntity* culprit, eExplosionType type, const CVector& pos, uint32 lifetime, bool makeSound = true); +#endif + static void ClearAllExplosions(); //done + static bool DoesExplosionMakeSound(uint8 id); //done + static int8 GetExplosionActiveCounter(uint8 id); //done + static CVector *GetExplosionPosition(uint8 id); //done + static uint8 GetExplosionType(uint8 id); //done, mb need change type to tExplosionType + static void Initialise(); //done + static void RemoveAllExplosionsInArea(CVector pos, float radius); //done + static void ResetExplosionActiveCounter(uint8 id); //done + static void Shutdown(); //done + static void Update(); //done + static bool TestForExplosionInArea(eExplosionType type, float x1, float x2, float y1, float y2, float z1, float z2); //done, not used +}; + +extern CExplosion gaExplosion[NUM_EXPLOSIONS]; \ No newline at end of file diff --git a/src/miami/weapons/ProjectileInfo.cpp b/src/miami/weapons/ProjectileInfo.cpp new file mode 100644 index 00000000..10aa3ef5 --- /dev/null +++ b/src/miami/weapons/ProjectileInfo.cpp @@ -0,0 +1,437 @@ +#include "common.h" + +#include "Camera.h" +#include "General.h" +#include "Heli.h" +#include "ModelIndices.h" +#include "Particle.h" +#include "Ped.h" +#include "Plane.h" +#include "ProjectileInfo.h" +#include "Projectile.h" +#include "Explosion.h" +#include "Weapon.h" +#include "World.h" + +#ifdef SQUEEZE_PERFORMANCE +uint32 projectileInUse; +#endif + +CProjectileInfo gaProjectileInfo[NUM_PROJECTILES]; +CProjectile *CProjectileInfo::ms_apProjectile[NUM_PROJECTILES]; + +#define PROJECTILE_BOUNDARY_MIN_X -2390.0f +#define PROJECTILE_BOUNDARY_MAX_X 1590.0f +#define PROJECTILE_BOUNDARY_MIN_Y -1990.0f +#define PROJECTILE_BOUNDARY_MAX_Y 1990.0f + +void +CProjectileInfo::Initialise() +{ + debug("Initialising CProjectileInfo...\n"); + + for (int i = 0; i < ARRAY_SIZE(ms_apProjectile); i++) { + ms_apProjectile[i] = nil; + gaProjectileInfo[i].m_eWeaponType = WEAPONTYPE_GRENADE; + gaProjectileInfo[i].m_pSource = nil; + gaProjectileInfo[i].m_nExplosionTime = 0; + gaProjectileInfo[i].m_bInUse = false; + } + + debug("CProjectileInfo ready\n"); + +#ifdef SQUEEZE_PERFORMANCE + projectileInUse = 0; +#endif +} + +void +CProjectileInfo::Shutdown() +{ + debug("Shutting down CProjectileInfo...\n"); + debug("CProjectileInfo shut down\n"); +} + +CProjectileInfo* +CProjectileInfo::GetProjectileInfo(int32 id) +{ + return &gaProjectileInfo[id]; +} + +bool +CProjectileInfo::AddProjectile(CEntity *entity, eWeaponType weapon, CVector pos, float speed) +{ + int8 SpecialCollisionResponseCase = COLLRESPONSE_NONE; + bool gravity = true; + CMatrix matrix; + float elasticity = 0.75f; + CPed* ped = (CPed*)entity; + int time; + CVector velocity; + + switch (weapon) + { + case WEAPONTYPE_ROCKET: + { + float vy = 0.35f; + time = CTimer::GetTimeInMilliseconds() + 2000; + if (entity->GetModelIndex() == MI_SPARROW || entity->GetModelIndex() == MI_HUNTER || entity->GetModelIndex() == MI_SENTINEL) { + matrix = ped->GetMatrix(); + matrix.GetPosition() = pos; + CVector vecSpeed = ((CPhysical*)entity)->m_vecMoveSpeed; + vy += Max(0.0f, DotProduct(vecSpeed, entity->GetForward())) + Max(0.0f, DotProduct(vecSpeed, entity->GetUp())); + } else { + if (ped->IsPlayer()) { + matrix.GetForward() = TheCamera.Cams[TheCamera.ActiveCam].Front; + matrix.GetUp() = TheCamera.Cams[TheCamera.ActiveCam].Up; + matrix.GetRight() = CrossProduct(TheCamera.Cams[TheCamera.ActiveCam].Up, TheCamera.Cams[TheCamera.ActiveCam].Front); + matrix.GetPosition() = pos; + } else if (ped->m_pSeekTarget != nil) { + float ry = CGeneral::GetRadianAngleBetweenPoints(1.0f, ped->m_pSeekTarget->GetPosition().z, 1.0f, pos.z); + float rz = Atan2(-ped->GetForward().x, ped->GetForward().y); + vy = 0.35f * speed + 0.15f; + matrix.SetTranslate(0.0f, 1.0f, 1.0f); + matrix.Rotate(0.0f, ry, rz); + matrix.GetPosition() += pos; + } else { + matrix = ped->GetMatrix(); + } + } + velocity = Multiply3x3(matrix, CVector(0.0f, vy, 0.0f)); + gravity = false; + break; + } + case WEAPONTYPE_MOLOTOV: + { + time = CTimer::GetTimeInMilliseconds() + 2000; + float scale = 0.22f * speed + 0.15f; + if (scale < 0.2f) + scale = 0.2f; + float angle = Atan2(-ped->GetForward().x, ped->GetForward().y); + matrix.SetTranslate(0.0f, 0.0f, 0.0f); + matrix.RotateZ(angle); + matrix.GetPosition() += pos; + velocity.x = -1.0f * scale * Sin(angle); + velocity.y = scale * Cos(angle); + velocity.z = (0.2f * speed + 0.4f) * scale; + break; + } + case WEAPONTYPE_TEARGAS: + { + time = CTimer::GetTimeInMilliseconds() + 20000; + float scale = 0.0f; + if (speed != 0.0f) + scale = 0.22f * speed + 0.15f; + float angle = Atan2(-ped->GetForward().x, ped->GetForward().y); + matrix.SetTranslate(0.0f, 0.0f, 0.0f); + matrix.RotateZ(angle); + matrix.GetPosition() += pos; + SpecialCollisionResponseCase = COLLRESPONSE_UNKNOWN5; + velocity.x = -1.0f * scale * Sin(angle); + velocity.y = scale * Cos(angle); + velocity.z = (0.4f * speed + 0.4f) * scale; + elasticity = 0.5f; + break; + } + case WEAPONTYPE_GRENADE: + case WEAPONTYPE_DETONATOR_GRENADE: + { + time = CTimer::GetTimeInMilliseconds() + 2000; + float scale = 0.0f; + if (speed != 0.0f) + scale = 0.22f * speed + 0.15f; + float angle = Atan2(-ped->GetForward().x, ped->GetForward().y); + matrix.SetTranslate(0.0f, 0.0f, 0.0f); + matrix.RotateZ(angle); + matrix.GetPosition() += pos; + SpecialCollisionResponseCase = COLLRESPONSE_UNKNOWN5; + velocity.x = -1.0f * scale * Sin(angle); + velocity.y = scale * Cos(angle); + velocity.z = (0.4f * speed + 0.4f) * scale; + elasticity = 0.5f; + break; + } + default: + Error("Undefined projectile type, AddProjectile, ProjectileInfo.cpp"); + break; + } + + int i = 0; +#ifdef FIX_BUGS + while (i < ARRAY_SIZE(gaProjectileInfo) && gaProjectileInfo[i].m_bInUse) i++; +#else + // array overrun is UB + while (gaProjectileInfo[i].m_bInUse && i < ARRAY_SIZE(gaProjectileInfo)) i++; +#endif + if (i == ARRAY_SIZE(gaProjectileInfo)) + return false; + + switch (weapon) + { + case WEAPONTYPE_ROCKET: + ms_apProjectile[i] = new CProjectile(MI_MISSILE); + break; + case WEAPONTYPE_TEARGAS: + ms_apProjectile[i] = new CProjectile(MI_TEARGAS); + break; + case WEAPONTYPE_MOLOTOV: + ms_apProjectile[i] = new CProjectile(MI_MOLOTOV); + break; + case WEAPONTYPE_GRENADE: + case WEAPONTYPE_DETONATOR_GRENADE: + ms_apProjectile[i] = new CProjectile(MI_GRENADE); + break; + default: break; + } + + if (ms_apProjectile[i] == nil) + return false; + + gaProjectileInfo[i].m_eWeaponType = weapon; + gaProjectileInfo[i].m_pSource = ped; + ms_apProjectile[i]->GetMatrix() = matrix; + ms_apProjectile[i]->SetMoveSpeed(velocity); + ms_apProjectile[i]->bAffectedByGravity = gravity; + + gaProjectileInfo[i].m_nExplosionTime = time; + ms_apProjectile[i]->m_fElasticity = elasticity; + ms_apProjectile[i]->m_nSpecialCollisionResponseCases = SpecialCollisionResponseCase; + +#ifdef SQUEEZE_PERFORMANCE + projectileInUse++; +#endif + + gaProjectileInfo[i].m_bInUse = true; + CWorld::Add(ms_apProjectile[i]); + + gaProjectileInfo[i].m_vecPos = ms_apProjectile[i]->GetPosition(); + + if (entity && entity->IsPed() && !ped->m_pCollidingEntity) { + ped->m_pCollidingEntity = ms_apProjectile[i]; + } + return true; +} + +void +CProjectileInfo::RemoveProjectile(CProjectileInfo *info, CProjectile *projectile) +{ + // TODO(Miami): New parameter: 1 + switch (info->m_eWeaponType) { + case WEAPONTYPE_GRENADE: + CExplosion::AddExplosion(nil, info->m_pSource, EXPLOSION_GRENADE, projectile->GetPosition(), 0); + break; + case WEAPONTYPE_MOLOTOV: + CExplosion::AddExplosion(nil, info->m_pSource, EXPLOSION_MOLOTOV, projectile->GetPosition(), 0); + break; + case WEAPONTYPE_ROCKET: + CExplosion::AddExplosion(nil, info->m_pSource->IsVehicle() ? ((CVehicle*)info->m_pSource)->pDriver : info->m_pSource, EXPLOSION_ROCKET, projectile->GetPosition(), 0); + break; + } +#ifdef SQUEEZE_PERFORMANCE + projectileInUse--; +#endif + + info->m_bInUse = false; + CWorld::Remove(projectile); + delete projectile; +} + +void +CProjectileInfo::RemoveNotAdd(CEntity *entity, eWeaponType weaponType, CVector pos) +{ + // TODO(Miami): New parameter: 1 + switch (weaponType) { + case WEAPONTYPE_GRENADE: + CExplosion::AddExplosion(nil, entity, EXPLOSION_GRENADE, pos, 0); + break; + case WEAPONTYPE_MOLOTOV: + CExplosion::AddExplosion(nil, entity, EXPLOSION_MOLOTOV, pos, 0); + break; + case WEAPONTYPE_ROCKET: + CExplosion::AddExplosion(nil, entity, EXPLOSION_ROCKET, pos, 0); + break; + } +} + +void +CProjectileInfo::Update() +{ +#ifdef SQUEEZE_PERFORMANCE + if (projectileInUse == 0) + return; +#endif + + int tearGasOffset = -0.0f; // unused + + for (int i = 0; i < ARRAY_SIZE(gaProjectileInfo); i++) { + if (!gaProjectileInfo[i].m_bInUse) continue; + + CPed *ped = (CPed*)gaProjectileInfo[i].m_pSource; + if (ped != nil && ped->IsPed() && !ped->IsPointerValid()) + gaProjectileInfo[i].m_pSource = nil; + + if (ms_apProjectile[i] == nil) { +#ifdef SQUEEZE_PERFORMANCE + projectileInUse--; +#endif + + gaProjectileInfo[i].m_bInUse = false; + continue; + } + if ( (gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_DETONATOR_GRENADE || gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_TEARGAS) && ms_apProjectile[i]->m_fElasticity > 0.1f ) { + if ( Abs(ms_apProjectile[i]->m_vecMoveSpeed.x) < 0.05f && Abs(ms_apProjectile[i]->m_vecMoveSpeed.y) < 0.05f && Abs(ms_apProjectile[i]->m_vecMoveSpeed.z) < 0.05f ) { + ms_apProjectile[i]->m_fElasticity = 0.03f; + } + } + const CVector &projectilePos = ms_apProjectile[i]->GetPosition(); + CVector nextPos = CTimer::GetTimeStep() * ms_apProjectile[i]->m_vecMoveSpeed + projectilePos; + + if ( nextPos.x <= PROJECTILE_BOUNDARY_MIN_X || nextPos.x >= PROJECTILE_BOUNDARY_MAX_X || nextPos.y <= PROJECTILE_BOUNDARY_MIN_Y || nextPos.y >= PROJECTILE_BOUNDARY_MAX_Y ) { + // Not RemoveProjectile, because we don't want no explosion + gaProjectileInfo[i].m_bInUse = false; + CWorld::Remove(ms_apProjectile[i]); + delete ms_apProjectile[i]; + continue; + } + if ( gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_TEARGAS && CTimer::GetTimeInMilliseconds() > gaProjectileInfo[i].m_nExplosionTime - 19500 ) { + CParticle::AddParticle(PARTICLE_TEARGAS, projectilePos, CVector(0.2f, tearGasOffset, 0.0f), 0, 0.0f, 0, 0, 0, 0); + CParticle::AddParticle(PARTICLE_TEARGAS, projectilePos, CVector(-0.2f, tearGasOffset, 0.0f), 0, 0.0f, 0, 0, 0, 0); + CParticle::AddParticle(PARTICLE_TEARGAS, projectilePos, CVector(tearGasOffset, tearGasOffset, 0.0f), 0, 0.0f, 0, 0, 0, 0); + + if ( CTimer::GetTimeInMilliseconds() & 0x200 ) + CWorld::SetPedsChoking(projectilePos.x, projectilePos.y, projectilePos.z, 6.0f, gaProjectileInfo[i].m_pSource); + } + + if (gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_ROCKET) { + CParticle::AddParticlesAlongLine(PARTICLE_ROCKET_SMOKE, gaProjectileInfo[i].m_vecPos, projectilePos, CVector(0.0f, 0.0f, 0.0f), 0.7f, 0, 0, 0, 3000); + } + + if (CTimer::GetTimeInMilliseconds() <= gaProjectileInfo[i].m_nExplosionTime || gaProjectileInfo[i].m_nExplosionTime == 0) { + if (gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_ROCKET) { + CVector pos = ms_apProjectile[i]->GetPosition(); + CWorld::pIgnoreEntity = ms_apProjectile[i]; + if (ms_apProjectile[i]->bHasCollided + || !CWorld::GetIsLineOfSightClear(gaProjectileInfo[i].m_vecPos, pos, true, true, true, true, false, false) + || CHeli::TestRocketCollision(&pos) || CPlane::TestRocketCollision(&pos)) { + RemoveProjectile(&gaProjectileInfo[i], ms_apProjectile[i]); + } + CWorld::pIgnoreEntity = nil; + ms_apProjectile[i]->m_vecMoveSpeed *= 1.07f; + + } else if (gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_MOLOTOV) { + CVector pos = ms_apProjectile[i]->GetPosition(); + CWorld::pIgnoreEntity = ms_apProjectile[i]; + + if (gaProjectileInfo[i].m_pSource == nil + || ((gaProjectileInfo[i].m_vecPos - gaProjectileInfo[i].m_pSource->GetPosition()).MagnitudeSqr() >= 2.0f)) + { + if (ms_apProjectile[i]->bHasCollided + || !CWorld::GetIsLineOfSightClear(gaProjectileInfo[i].m_vecPos, pos, true, true, true, true, false, false)) { + RemoveProjectile(&gaProjectileInfo[i], ms_apProjectile[i]); + } + } + CWorld::pIgnoreEntity = nil; + } + } else { + if (gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_DETONATOR_GRENADE) { + CEntity *ent = gaProjectileInfo[i].m_pSource; + if (ent->IsPed() && ((CPed*)ped)->IsPlayer()) { + CPed *ped = (CPed*)ent; + if (ped->GetWeapon(ped->GetWeaponSlot(WEAPONTYPE_DETONATOR)).m_eWeaponType != WEAPONTYPE_DETONATOR + || ped->GetWeapon(ped->GetWeaponSlot(WEAPONTYPE_DETONATOR)).m_nAmmoTotal == 0) { + gaProjectileInfo[i].m_nExplosionTime = 0; + } + } + } else { + RemoveProjectile(&gaProjectileInfo[i], ms_apProjectile[i]); + } + } + + gaProjectileInfo[i].m_vecPos = ms_apProjectile[i]->GetPosition(); + } +} + +bool +CProjectileInfo::IsProjectileInRange(float x1, float x2, float y1, float y2, float z1, float z2, bool remove) +{ + bool result = false; + for (int i = 0; i < ARRAY_SIZE(ms_apProjectile); i++) { + if (gaProjectileInfo[i].m_bInUse) { + if (gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_ROCKET || gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_MOLOTOV || gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_GRENADE) { + const CVector &pos = ms_apProjectile[i]->GetPosition(); + if (pos.x >= x1 && pos.x <= x2 && pos.y >= y1 && pos.y <= y2 && pos.z >= z1 && pos.z <= z2) { + result = true; + if (remove) { +#ifdef SQUEEZE_PERFORMANCE + projectileInUse--; +#endif + + gaProjectileInfo[i].m_bInUse = false; + CWorld::Remove(ms_apProjectile[i]); + delete ms_apProjectile[i]; + } + } + } + } + } + return result; +} + +void +CProjectileInfo::RemoveDetonatorProjectiles() +{ + for (int i = 0; i < ARRAY_SIZE(ms_apProjectile); i++) { + if (gaProjectileInfo[i].m_bInUse && gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_DETONATOR_GRENADE) { + CExplosion::AddExplosion(nil, gaProjectileInfo[i].m_pSource, EXPLOSION_GRENADE, gaProjectileInfo[i].m_vecPos, 0); // TODO(Miami): New parameter: 1 + gaProjectileInfo[i].m_bInUse = false; + CWorld::Remove(ms_apProjectile[i]); + delete ms_apProjectile[i]; + } + } +} + +void +CProjectileInfo::RemoveAllProjectiles() +{ +#ifdef SQUEEZE_PERFORMANCE + if (projectileInUse == 0) + return; +#endif + + for (int i = 0; i < ARRAY_SIZE(ms_apProjectile); i++) { + if (gaProjectileInfo[i].m_bInUse) { +#ifdef SQUEEZE_PERFORMANCE + projectileInUse--; +#endif + + gaProjectileInfo[i].m_bInUse = false; + CWorld::Remove(ms_apProjectile[i]); + delete ms_apProjectile[i]; + } + } +} + +bool +CProjectileInfo::RemoveIfThisIsAProjectile(CObject *object) +{ +#ifdef SQUEEZE_PERFORMANCE + if (projectileInUse == 0) + return false; +#endif + + int i = 0; + while (ms_apProjectile[i++] != object) { + if (i >= ARRAY_SIZE(ms_apProjectile)) + return false; + } + +#ifdef SQUEEZE_PERFORMANCE + projectileInUse--; +#endif + + gaProjectileInfo[i].m_bInUse = false; + CWorld::Remove(ms_apProjectile[i]); + delete ms_apProjectile[i]; + ms_apProjectile[i] = nil; + return true; +} diff --git a/src/miami/weapons/ProjectileInfo.h b/src/miami/weapons/ProjectileInfo.h new file mode 100644 index 00000000..d1688948 --- /dev/null +++ b/src/miami/weapons/ProjectileInfo.h @@ -0,0 +1,34 @@ +#pragma once + +#include "WeaponType.h" + +class CEntity; +class CObject; +class CProjectile; + +class CProjectileInfo +{ +public: + eWeaponType m_eWeaponType; + CEntity *m_pSource; + uint32 m_nExplosionTime; + bool m_bInUse; + CVector m_vecPos; + +public: + static CProjectileInfo *GetProjectileInfo(int32 id); + static CProjectile *ms_apProjectile[NUM_PROJECTILES]; + + static void Initialise(); + static void Shutdown(); + static bool AddProjectile(CEntity *ped, eWeaponType weapon, CVector pos, float speed); + static void RemoveProjectile(CProjectileInfo *info, CProjectile *projectile); + static void RemoveNotAdd(CEntity *entity, eWeaponType weaponType, CVector pos); + static bool RemoveIfThisIsAProjectile(CObject *pObject); + static void RemoveAllProjectiles(); + static void RemoveDetonatorProjectiles(); + static void Update(); + static bool IsProjectileInRange(float x1, float x2, float y1, float y2, float z1, float z2, bool remove); +}; + +extern CProjectileInfo gaProjectileInfo[NUM_PROJECTILES]; \ No newline at end of file diff --git a/src/miami/weapons/ShotInfo.cpp b/src/miami/weapons/ShotInfo.cpp new file mode 100644 index 00000000..788bcbe1 --- /dev/null +++ b/src/miami/weapons/ShotInfo.cpp @@ -0,0 +1,149 @@ +#include "common.h" + +#include "ShotInfo.h" +#include "Entity.h" +#include "Weapon.h" +#include "World.h" +#include "WeaponInfo.h" +#include "General.h" +#include "Timer.h" +#include "Ped.h" +#include "Fire.h" + +CShotInfo gaShotInfo[NUMSHOTINFOS]; +float CShotInfo::ms_afRandTable[20]; + +#ifdef SQUEEZE_PERFORMANCE +uint32 shotInfoInUse; +#endif + +/* + Used for flamethrower. I don't know why it's name is CShotInfo. + Has no relation with any visual, just calculates the area fire affects + (including spreading and slowing of fire) and make entities burn/flee. +*/ + +void +CShotInfo::Initialise() +{ + debug("Initialising CShotInfo...\n"); + for(int i=0; im_fRadius; + + if (weaponInfo->m_fSpread != 0.0f) { + gaShotInfo[slot].m_areaAffected.x += CShotInfo::ms_afRandTable[CGeneral::GetRandomNumber() % ARRAY_SIZE(ms_afRandTable)] * weaponInfo->m_fSpread; + gaShotInfo[slot].m_areaAffected.y += CShotInfo::ms_afRandTable[CGeneral::GetRandomNumber() % ARRAY_SIZE(ms_afRandTable)] * weaponInfo->m_fSpread; + gaShotInfo[slot].m_areaAffected.z += CShotInfo::ms_afRandTable[CGeneral::GetRandomNumber() % ARRAY_SIZE(ms_afRandTable)]; + } + gaShotInfo[slot].m_areaAffected.Normalise(); + if (weaponInfo->IsFlagSet(WEAPONFLAG_RAND_SPEED)) + gaShotInfo[slot].m_areaAffected *= CShotInfo::ms_afRandTable[CGeneral::GetRandomNumber() % ARRAY_SIZE(ms_afRandTable)] + weaponInfo->m_fSpeed; + else + gaShotInfo[slot].m_areaAffected *= weaponInfo->m_fSpeed; + + gaShotInfo[slot].m_sourceEntity = sourceEntity; + gaShotInfo[slot].m_timeout = CTimer::GetTimeInMilliseconds() + weaponInfo->m_fLifespan; + + return true; +} + +void +CShotInfo::Shutdown() +{ + debug("Shutting down CShotInfo...\n"); + debug("CShotInfo shut down\n"); +} + +void +CShotInfo::Update() +{ +#ifdef SQUEEZE_PERFORMANCE + if (shotInfoInUse == 0) + return; +#endif + for (int slot = 0; slot < ARRAY_SIZE(gaShotInfo); slot++) { + CShotInfo &shot = gaShotInfo[slot]; + if (shot.m_sourceEntity && shot.m_sourceEntity->IsPed() && !((CPed*)shot.m_sourceEntity)->IsPointerValid()) + shot.m_sourceEntity = nil; + + if (!shot.m_inUse) + continue; + + CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(shot.m_weapon); + if (CTimer::GetTimeInMilliseconds() > shot.m_timeout) { +#ifdef SQUEEZE_PERFORMANCE + shotInfoInUse--; +#endif + shot.m_inUse = false; + } + + if (weaponInfo->IsFlagSet(WEAPONFLAG_SLOWS_DOWN)) + shot.m_areaAffected *= pow(0.96, CTimer::GetTimeStep()); // FRAMERATE + + if (weaponInfo->IsFlagSet(WEAPONFLAG_EXPANDS)) + shot.m_radius += 0.075f * CTimer::GetTimeStep(); + + shot.m_startPos += CTimer::GetTimeStep() * shot.m_areaAffected; + if (shot.m_sourceEntity) { + assert(shot.m_sourceEntity->IsPed()); + CPed *ped = (CPed*) shot.m_sourceEntity; + float radius = Max(1.0f, shot.m_radius); + + for (int i = 0; i < ped->m_numNearPeds; ++i) { + CPed *nearPed = ped->m_nearPeds[i]; + if (nearPed->IsPointerValid()) { + if (nearPed->IsPedInControl() && (nearPed->GetPosition() - shot.m_startPos).MagnitudeSqr() < radius && !nearPed->bFireProof) { + + if (!nearPed->IsPlayer()) { + nearPed->SetFindPathAndFlee(shot.m_sourceEntity, 10000); + nearPed->SetMoveState(PEDMOVE_SPRINT); + } + gFireManager.StartFire(nearPed, shot.m_sourceEntity, 0.8f, true); + } + } + } + } + if (!((CTimer::GetFrameCounter() + slot) & 3)) + CWorld::SetCarsOnFire(shot.m_startPos.x, shot.m_startPos.y, shot.m_startPos.z, 4.0f, shot.m_sourceEntity); + } +} diff --git a/src/miami/weapons/ShotInfo.h b/src/miami/weapons/ShotInfo.h new file mode 100644 index 00000000..db6158c2 --- /dev/null +++ b/src/miami/weapons/ShotInfo.h @@ -0,0 +1,24 @@ +#pragma once + +#include "WeaponType.h" + +class CEntity; + +class CShotInfo +{ +public: + eWeaponType m_weapon; + CVector m_startPos; + CVector m_areaAffected; + float m_radius; + CEntity *m_sourceEntity; + float m_timeout; + bool m_inUse; + + static float ms_afRandTable[20]; + + static void Initialise(void); + static bool AddShot(CEntity*, eWeaponType, CVector, CVector); + static void Shutdown(void); + static void Update(void); +}; diff --git a/src/miami/weapons/Weapon.cpp b/src/miami/weapons/Weapon.cpp new file mode 100644 index 00000000..2d219e2d --- /dev/null +++ b/src/miami/weapons/Weapon.cpp @@ -0,0 +1,3384 @@ +#include "common.h" + +#include "Weapon.h" +#include "AnimBlendAssociation.h" +#include "AudioManager.h" +#include "BulletInfo.h" +#include "Camera.h" +#include "Coronas.h" +#include "DMAudio.h" +#include "Explosion.h" +#include "General.h" +#include "Glass.h" +#include "Heli.h" +#include "ModelIndices.h" +#include "Object.h" +#include "Pad.h" +#include "Particle.h" +#include "Ped.h" +#include "PointLights.h" +#include "Pools.h" +#include "ProjectileInfo.h" +#include "RpAnimBlend.h" +#include "ShotInfo.h" +#include "SpecialFX.h" +#include "Stats.h" +#include "TempColModels.h" +#include "Timer.h" +#include "Automobile.h" +#include "Boat.h" +#include "WaterLevel.h" +#include "WeaponInfo.h" +#include "World.h" +#include "SurfaceTable.h" +#include "Bike.h" +#include "Glass.h" +#include "Sprite.h" +#include "Pickups.h" +#include "SaveBuf.h" + +float fReloadAnimSampleFraction[5] = { 0.5f, 0.7f, 0.75f, 0.75f, 0.7f }; +float fSeaSparrowAimingAngle = 10.0f; +float fHunterAimingAngle = 30.0f; +float fPlayerAimScaleDist = 5.0f; +float fPlayerAimScale = 2.5f; + +bool CWeapon::bPhotographHasBeenTaken; + +#ifdef SECUROM +int32 sniperPirateCheck = 0x00797743; // 'Cwy\0' ??? +#endif + +#ifdef FREE_CAM +static bool +Find3rdPersonCamTargetVectorFromCachedVectors(float dist, CVector pos, CVector& source, CVector& target, CVector camSource, CVector camFront, CVector camUp) +{ + if (CPad::GetPad(0)->GetLookBehindForPed()) { + source = pos; + target = dist * FindPlayerPed()->GetForward() + source; + return false; + } else { + float angleX = DEGTORAD((CCamera::m_f3rdPersonCHairMultX - 0.5f) * 1.8f * 0.5f * TheCamera.Cams[TheCamera.ActiveCam].FOV * CDraw::GetAspectRatio()); + float angleY = DEGTORAD((0.5f - CCamera::m_f3rdPersonCHairMultY) * 1.8f * 0.5f * TheCamera.Cams[TheCamera.ActiveCam].FOV); + source = camSource; + target = camFront; + target += camUp * Tan(angleY); + target += CrossProduct(camFront, camUp) * Tan(angleX); + target.Normalise(); + source += DotProduct(pos - source, target) * target; + target = dist * target + source; + return true; + } +} +#endif + +CWeaponInfo * +CWeapon::GetInfo() +{ + CWeaponInfo *info = CWeaponInfo::GetWeaponInfo(m_eWeaponType); + ASSERT(info!=nil); + return info; +} + +CWeapon::CWeapon(eWeaponType type, int32 ammo) +{ + m_eWeaponType = type; + m_eWeaponState = WEAPONSTATE_READY; + m_nAmmoTotal = Min(ammo, 99999); + m_nAmmoInClip = 0; + Reload(); + m_nTimer = 0; + m_bAddRotOffset = false; +} + +void +CWeapon::InitialiseWeapons(void) +{ + CWeaponInfo::Initialise(); + CShotInfo::Initialise(); + CExplosion::Initialise(); + CProjectileInfo::Initialise(); + CBulletInfo::Initialise(); + bPhotographHasBeenTaken = false; +} + +void +CWeapon::ShutdownWeapons(void) +{ + CWeaponInfo::Shutdown(); + CShotInfo::Shutdown(); + CExplosion::Shutdown(); + CProjectileInfo::Shutdown(); + CBulletInfo::Shutdown(); +} + +void +CWeapon::UpdateWeapons(void) +{ + CShotInfo::Update(); + CExplosion::Update(); + CProjectileInfo::Update(); + CBulletInfo::Update(); +} + + +void +CWeapon::Initialise(eWeaponType type, int32 ammo) +{ + m_eWeaponType = type; + m_eWeaponState = WEAPONSTATE_READY; + m_nAmmoTotal = Min(ammo, 99999); + m_nAmmoInClip = 0; + Reload(); + m_nTimer = 0; + int32 modelId = CWeaponInfo::GetWeaponInfo(m_eWeaponType)->m_nModelId; + int32 model2Id = CWeaponInfo::GetWeaponInfo(m_eWeaponType)->m_nModel2Id; + + if ( modelId != -1 ) + CModelInfo::GetModelInfo(modelId)->AddRef(); + if ( model2Id != -1 ) + CModelInfo::GetModelInfo(model2Id)->AddRef(); +} + +void +CWeapon::Shutdown() +{ + int32 modelId = CWeaponInfo::GetWeaponInfo(m_eWeaponType)->m_nModelId; + if (modelId != -1) + CModelInfo::GetModelInfo(modelId)->RemoveRef(); + + int32 model2Id = CWeaponInfo::GetWeaponInfo(m_eWeaponType)->m_nModel2Id; + if (model2Id != -1) + CModelInfo::GetModelInfo(model2Id)->RemoveRef(); + + m_eWeaponType = WEAPONTYPE_UNARMED; + m_eWeaponState = WEAPONSTATE_READY; + m_nAmmoInClip = 0; + m_nAmmoTotal = 0; + m_nTimer = 0; +} + +bool +CWeapon::Fire(CEntity *shooter, CVector *fireSource) +{ + ASSERT(shooter!=nil); + + CVector fireOffset(0.0f, 0.0f, 0.6f); + CVector *source = fireSource; +#ifdef FIX_BUGS + static CVector shooterSource; +#else + CVector shooterSource; +#endif + + if ( !fireSource ) + { + shooterSource = shooter->GetMatrix() * fireOffset; + source = &shooterSource; + } + + if ( m_bAddRotOffset ) + { + float heading = RADTODEG(shooter->GetForward().Heading()); + float angle = DEGTORAD(heading); + (*source).x += -Sin(angle) * 0.15f; + (*source).y += Cos(angle) * 0.15f; + } + + if ( m_eWeaponState != WEAPONSTATE_READY && m_eWeaponState != WEAPONSTATE_FIRING ) + return false; + + bool fired; + bool addFireRateAsDelay = true; + + if ( GetInfo()->m_eWeaponFire != WEAPON_FIRE_MELEE ) + { + if (m_nAmmoInClip <= 0) { + if (m_nAmmoTotal <= 0 || m_eWeaponState == WEAPONSTATE_RELOADING) + return false; + + Reload(); + } + + switch ( m_eWeaponType ) + { + case WEAPONTYPE_SHOTGUN: + case WEAPONTYPE_SPAS12_SHOTGUN: + case WEAPONTYPE_STUBBY_SHOTGUN: + { + addFireRateAsDelay = true; + fired = FireShotgun(shooter, source); + + break; + } + + case WEAPONTYPE_SNIPERRIFLE: + case WEAPONTYPE_LASERSCOPE: + { + if (shooter == FindPlayerPed()) + fired = FireSniper(shooter); + else + fired = FireInstantHit(shooter, source); + + break; + } + + case WEAPONTYPE_COLT45: + case WEAPONTYPE_PYTHON: + case WEAPONTYPE_UZI: + case WEAPONTYPE_TEC9: + case WEAPONTYPE_SILENCED_INGRAM: + case WEAPONTYPE_MP5: + case WEAPONTYPE_M4: + case WEAPONTYPE_RUGER: + case WEAPONTYPE_M60: + case WEAPONTYPE_MINIGUN: + case WEAPONTYPE_HELICANNON: + { + if ((TheCamera.PlayerWeaponMode.Mode == CCam::MODE_HELICANNON_1STPERSON || TheCamera.PlayerWeaponMode.Mode == CCam::MODE_M16_1STPERSON) + && shooter == FindPlayerPed()) { + addFireRateAsDelay = true; + fired = FireM16_1stPerson(shooter); + } else { + addFireRateAsDelay = false; + fired = FireInstantHit(shooter, source); + } + break; + } + + case WEAPONTYPE_ROCKETLAUNCHER: + { + if ( shooter->IsPed() && ((CPed*)shooter)->m_pSeekTarget != nil ) + { + float distToTarget = (shooter->GetPosition() - ((CPed*)shooter)->m_pSeekTarget->GetPosition()).Magnitude(); + + if ( distToTarget > 8.0f || ((CPed*)shooter)->IsPlayer() ) + fired = FireProjectile(shooter, source, 0.0f); + else + fired = false; + } + else + fired = FireProjectile(shooter, source, 0.0f); + + break; + } + + case WEAPONTYPE_MOLOTOV: + case WEAPONTYPE_GRENADE: + case WEAPONTYPE_DETONATOR_GRENADE: + case WEAPONTYPE_TEARGAS: + { + if ( shooter == FindPlayerPed() ) + { + fired = FireProjectile(shooter, source, ((CPlayerPed*)shooter)->m_fAttackButtonCounter*0.0375f); + } + else if ( shooter->IsPed() && ((CPed*)shooter)->m_pSeekTarget != nil ) + { + float distToTarget = (shooter->GetPosition() - ((CPed*)shooter)->m_pSeekTarget->GetPosition()).Magnitude(); + float power = Clamp((distToTarget-10.0f)*0.02f, 0.2f, 1.0f); + + fired = FireProjectile(shooter, source, power); + } + else + fired = FireProjectile(shooter, source, 0.3f); + + if (m_eWeaponType == WEAPONTYPE_DETONATOR_GRENADE) { + ((CPed*)shooter)->GiveWeapon(WEAPONTYPE_DETONATOR, 1, true); + ((CPed*)shooter)->GetWeapon(((CPed*)shooter)->GetWeaponSlot(WEAPONTYPE_DETONATOR)).m_eWeaponState = WEAPONSTATE_READY; + ((CPed*)shooter)->SetCurrentWeapon(WEAPONTYPE_DETONATOR); + } + break; + } + + case WEAPONTYPE_FLAMETHROWER: + { + fired = FireAreaEffect(shooter, source); + + break; + } + + case WEAPONTYPE_DETONATOR: + { + CWorld::UseDetonator(shooter); + m_nAmmoTotal = 1; + m_nAmmoInClip = m_nAmmoTotal; + fired = true; + + break; + } + + case WEAPONTYPE_CAMERA: + { + fired = TakePhotograph(shooter); + + break; + } + + default: + { + debug("Unknown weapon type, Weapon.cpp"); + break; + } + } + + if (fired) + { + bool isPlayer = false; + + if (shooter->IsPed()) + { + CPed* shooterPed = (CPed*)shooter; + + if ( m_eWeaponType != WEAPONTYPE_CAMERA ) + { + shooterPed->bIsShooting = true; + + if (shooterPed->IsPlayer()) + isPlayer = true; + } + + DMAudio.PlayOneShot(shooterPed->m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); + + if ( isPlayer ) + { + CPed *aimPed = (CPed *)shooterPed->m_pSeekTarget; + if ( aimPed ) + { + if ( aimPed->IsPed() ) + shooterPed->Say(SOUND_PED_ON_FIRE); + } + } + } + + switch ( m_eWeaponType ) + { + case WEAPONTYPE_COLT45: + case WEAPONTYPE_PYTHON: + case WEAPONTYPE_SHOTGUN: + case WEAPONTYPE_SPAS12_SHOTGUN: + case WEAPONTYPE_STUBBY_SHOTGUN: + case WEAPONTYPE_TEC9: + case WEAPONTYPE_UZI: + case WEAPONTYPE_SILENCED_INGRAM: + case WEAPONTYPE_MP5: + case WEAPONTYPE_M4: + case WEAPONTYPE_RUGER: + case WEAPONTYPE_SNIPERRIFLE: + case WEAPONTYPE_LASERSCOPE: + case WEAPONTYPE_M60: + case WEAPONTYPE_MINIGUN: + CStats::RoundsFiredByPlayer++; + break; + + case WEAPONTYPE_GRENADE: + case WEAPONTYPE_DETONATOR_GRENADE: + case WEAPONTYPE_MOLOTOV: + case WEAPONTYPE_ROCKET: + case WEAPONTYPE_ROCKETLAUNCHER: + case WEAPONTYPE_DETONATOR: + case WEAPONTYPE_HELICANNON: + CStats::KgsOfExplosivesUsed++; + break; + } + + + if (m_nAmmoInClip > 0) + m_nAmmoInClip--; + + if (m_nAmmoTotal > 0 && (m_nAmmoTotal < 25000 || isPlayer) && (!isPlayer || CStats::GetPercentageProgress() < 100.0f || m_eWeaponType == WEAPONTYPE_DETONATOR)) + m_nAmmoTotal--; + + if (m_eWeaponState == WEAPONSTATE_READY && m_eWeaponType == WEAPONTYPE_FLAMETHROWER) + DMAudio.PlayOneShot(((CPhysical*)shooter)->m_audioEntityId, SOUND_WEAPON_FLAMETHROWER_FIRE, 0.0f); + + m_eWeaponState = WEAPONSTATE_FIRING; + + if (m_nAmmoInClip == 0) + { + if (m_nAmmoTotal == 0) { + if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_CAMERA) + CPad::GetPad(0)->Clear(false); + + return true; + } + + m_eWeaponState = WEAPONSTATE_RELOADING; + m_nTimer = CTimer::GetTimeInMilliseconds() + GetInfo()->m_nReload; + + if (shooter == FindPlayerPed()) + { + if (CWorld::Players[CWorld::PlayerInFocus].m_bFastReload) + m_nTimer = CTimer::GetTimeInMilliseconds() + GetInfo()->m_nReload / 4; + } + + return true; + } + + if ( addFireRateAsDelay ) + m_nTimer = CTimer::GetTimeInMilliseconds() + GetInfo()->m_nFiringRate; + else + m_nTimer = CTimer::GetTimeInMilliseconds(); + } + } + else + { + if ( m_eWeaponState != WEAPONSTATE_FIRING ) + { + m_nTimer = CTimer::GetTimeInMilliseconds() + GetInfo()->m_nReload; + m_eWeaponState = WEAPONSTATE_FIRING; + + if (shooter->IsPed() && m_eWeaponType != WEAPONTYPE_CHAINSAW) + { + DMAudio.PlayOneShot(((CPed*)shooter)->m_audioEntityId, SOUND_MELEE_ATTACK_START, m_eWeaponType << 8); + } + } + + fired = FireMelee(shooter, *source); + } + + if ( m_eWeaponType == WEAPONTYPE_UNARMED || m_eWeaponType == WEAPONTYPE_BASEBALLBAT ) + return true; + else + return fired; +} + +bool +CWeapon::FireFromCar(CVehicle *shooter, bool left, bool right) +{ + ASSERT(shooter!=nil); + + if ( m_eWeaponState != WEAPONSTATE_READY && m_eWeaponState != WEAPONSTATE_FIRING ) + return false; + + if ( m_nAmmoInClip <= 0 ) + return false; + + if ( FireInstantHitFromCar(shooter, left, right) ) + { + DMAudio.PlayOneShot(shooter->m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); + + if ( m_nAmmoInClip > 0 ) + m_nAmmoInClip--; + + if ( m_nAmmoTotal < 25000 && m_nAmmoTotal > 0 && (!shooter || shooter->GetStatus() != STATUS_PLAYER || CStats::GetPercentageProgress() < 100.f)) + m_nAmmoTotal--; + + m_eWeaponState = WEAPONSTATE_FIRING; + + if ( m_nAmmoInClip == 0 ) + { + if ( m_nAmmoTotal == 0 ) + return true; + + m_eWeaponState = WEAPONSTATE_RELOADING; + m_nTimer = CTimer::GetTimeInMilliseconds() + GetInfo()->m_nReload; + + return true; + } + + m_nTimer = CTimer::GetTimeInMilliseconds() + 1000; + } + + return true; +} + +bool +CWeapon::FireMelee(CEntity *shooter, CVector &fireSource) +{ + ASSERT(shooter!=nil); + + CWeaponInfo *info = GetInfo(); + + bool anim2Playing = false; + + if ( CPed::GetFireAnimGround(info, false) != (AnimationId)0 ) + { + if ( RpAnimBlendClumpGetAssociation(shooter->GetClump(), CPed::GetFireAnimGround(info, false)) ) + anim2Playing = true; + } + + ASSERT(shooter->IsPed()); + + CPed *shooterPed = (CPed*)shooter; + + if (shooterPed == FindPlayerPed()) + { + if (m_eWeaponType == WEAPONTYPE_GOLFCLUB || m_eWeaponType == WEAPONTYPE_NIGHTSTICK || + (m_eWeaponType >= WEAPONTYPE_BASEBALLBAT && m_eWeaponType <= WEAPONTYPE_CHAINSAW)) + { + CGlass::BreakGlassPhysically(fireSource, info->m_fRadius); + + if (m_eWeaponType == WEAPONTYPE_CHAINSAW) + CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_PED, FindPlayerPed(), FindPlayerPed(), 1000); + } + } + + int damageEntityRegistered = 0; + + for ( int32 i = 0; i < shooterPed->m_numNearPeds; i++ ) + { + CPed *victimPed = shooterPed->m_nearPeds[i]; + ASSERT(victimPed!=nil); + + if ( (victimPed->m_nPedType != shooterPed->m_nPedType || victimPed == shooterPed->m_pSeekTarget) + && victimPed != shooterPed->m_leader || !(CGeneral::GetRandomNumber() & 31) + && (!shooterPed->IsGangMember() || victimPed->CanBeDamagedByThisGangMember(shooterPed)) ) + { + bool collided = false; + + if (victimPed->m_nPedState == PED_DRIVING && (m_eWeaponType == WEAPONTYPE_UNARMED || m_eWeaponType == WEAPONTYPE_BRASSKNUCKLE + || info->IsFlagSet(WEAPONFLAG_FIGHTMODE))) + continue; + + float victimPedRadius = victimPed->GetBoundRadius() + info->m_fRadius; + if ( victimPed->bUsesCollision || victimPed->Dead() || victimPed->Driving() ) + { + CVector victimPedPos = victimPed->GetPosition(); + if ( SQR(victimPedRadius) > (victimPedPos-fireSource).MagnitudeSqr() ) + { + CVector collisionDist; + CColModel* victimPedCol = &CTempColModels::ms_colModelPed1; + bool useLocalPos = false; + if (victimPed->m_nPedState == PED_FALL + || victimPed->m_nPedState == PED_DIE && victimPed->bIsPedDieAnimPlaying + || victimPed->m_nWaitState == WAITSTATE_SIT_IDLE + || victimPed->m_nWaitState == WAITSTATE_SUN_BATHE_IDLE) + { + useLocalPos = true; + victimPedCol = ((CPedModelInfo*)CModelInfo::GetModelInfo(victimPed->GetModelIndex()))->AnimatePedColModelSkinnedWorld(victimPed->GetClump()); + } else if (victimPed->DyingOrDead()) { + victimPedCol = &CTempColModels::ms_colModelPedGroundHit; + } + + int32 s = 0; + while ( s < victimPedCol->numSpheres ) + { + CColSphere *sphere = &victimPedCol->spheres[s]; + + if (useLocalPos) + collisionDist = sphere->center - fireSource; + else + collisionDist = victimPedPos + sphere->center - fireSource; + + if ( SQR(sphere->radius + info->m_fRadius) > collisionDist.MagnitudeSqr() ) + { + collided = true; + break; + } + s++; + } + + if ( !(victimPed->IsPlayer() && victimPed->GetPedState() == PED_GETUP) ) + { + if ( collided ) + { + float victimPedHealth = victimPed->m_fHealth; + CVector bloodPos = fireSource + (collisionDist*0.7f); + + CVector2D posOffset(shooterPed->GetPosition().x-victimPedPos.x, shooterPed->GetPosition().y-victimPedPos.y); + + int32 localDir = victimPed->GetLocalDirection(posOffset); + + bool isHeavy = m_eWeaponType >= WEAPONTYPE_GOLFCLUB && m_eWeaponType <= WEAPONTYPE_KATANA && m_eWeaponType != WEAPONTYPE_HAMMER; + + if (shooterPed->m_fDamageImpulse == 0.0f) + { + shooterPed->m_pDamageEntity = victimPed; + victimPed->RegisterReference(&shooterPed->m_pDamageEntity); + } + + damageEntityRegistered = 3; + if (victimPed->bInVehicle) + { + CVehicle *victimVeh = victimPed->m_pMyVehicle; + if (victimVeh) + { + if (victimVeh->IsBike()) + { + CBike *victimBike = (CBike*)victimVeh; + victimBike->KnockOffRider(m_eWeaponType, localDir, victimPed, false); + if (victimBike->pDriver) + victimBike->pDriver->ReactToAttack(shooterPed); + else + { + if (victimVeh->pPassengers[0]) + victimVeh->pPassengers[0]->ReactToAttack(shooterPed); + } + continue; + } + } + } + + if ( !victimPed->DyingOrDead() ) + victimPed->ReactToAttack(shooterPed); + + uint8 hitLevel = HITLEVEL_HIGH; + if ( isHeavy && (victimPed->OnGround() || victimPed->m_nWaitState == WAITSTATE_SUN_BATHE_IDLE)) + hitLevel = HITLEVEL_GROUND; + + victimPed->StartFightDefend(localDir, hitLevel, 10); + + if ( !victimPed->DyingOrDead() ) + { + if ( shooterPed->IsPlayer() && isHeavy && anim2Playing ) + victimPed->InflictDamage(shooterPed, m_eWeaponType, 100.0f, PEDPIECE_TORSO, localDir); + else if ( shooterPed->IsPlayer() && ((CPlayerPed*)shooterPed)->m_bAdrenalineActive ) + victimPed->InflictDamage(shooterPed, m_eWeaponType, 3.5f*info->m_nDamage, PEDPIECE_TORSO, localDir); + else + { + if ( victimPed->IsPlayer() && isHeavy ) // wtf, it's not fair + victimPed->InflictDamage(shooterPed, m_eWeaponType, 2.0f*info->m_nDamage, PEDPIECE_TORSO, localDir); + else + victimPed->InflictDamage(shooterPed, m_eWeaponType, info->m_nDamage, PEDPIECE_TORSO, localDir); + } + } + + if ( CGame::nastyGame && victimPed->GetIsOnScreen() ) + { + CVector dir = collisionDist * RecipSqrt(1.0f, 10.0f*collisionDist.MagnitudeSqr()); + + CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, dir); + CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, dir); + CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, dir); + + if ( isHeavy ) + { + dir.x += CGeneral::GetRandomNumberInRange(-0.05f, 0.05f); + dir.y += CGeneral::GetRandomNumberInRange(-0.05f, 0.05f); + CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, dir); + + dir.x += CGeneral::GetRandomNumberInRange(-0.05f, 0.05f); + dir.y += CGeneral::GetRandomNumberInRange(-0.05f, 0.05f); + CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, dir); + } + + if (m_eWeaponType == WEAPONTYPE_CHAINSAW) + { + if (victimPed->m_nPedState != PED_DEAD && !((CTimer::GetFrameCounter() + 17) & 1) + || victimPed->m_nPedState == PED_DEAD && !((CTimer::GetFrameCounter() + 17) & 3)) + { + CParticle::AddParticle(PARTICLE_TEST, bloodPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.2f); + } + CVector newDir(dir); + newDir.z += 0.2f; + CParticle::AddParticle(PARTICLE_BLOOD_SMALL, bloodPos, newDir); + CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, newDir); + newDir.z = dir.z + 0.1f; + CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, newDir); + newDir.x = 0.0f; + newDir.y = 0.0f; + newDir.z = 0.01f; + CParticle::AddParticle(PARTICLE_DEBRIS2, bloodPos, newDir); + + CVector dropDir(CGeneral::GetRandomNumberInRange(-0.15f, 0.15f), CGeneral::GetRandomNumberInRange(0.1f, 0.35f), 0.f); + CVector dropPos(CGeneral::GetRandomNumberInRange(SCREEN_STRETCH_X(50.0f), SCREEN_STRETCH_FROM_RIGHT(50.0f)), + CGeneral::GetRandomNumberInRange(SCREEN_STRETCH_Y(50.0f), SCREEN_STRETCH_FROM_BOTTOM(50.0f)), 1.f); + CParticle::AddParticle(PARTICLE_BLOODDROP, dropPos, dropDir, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.15f), + CRGBA(0, 0, 0, 0), 0, 0, CGeneral::GetRandomNumber() & 1, 0); + + } + if (info->m_AnimToPlay == ASSOCGRP_KNIFE) + { + dir += 0.1f * shooterPed->GetUp() + 0.05f * shooterPed->GetRight(); + CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir); + CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir); + CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir); + } + } + + if ( !victimPed->OnGround() ) + { + if ( victimPed->m_fHealth > 0.0f + && (victimPed->m_fHealth < 30.0f && victimPedHealth > 30.0f || + (isHeavy || m_eWeaponType == WEAPONTYPE_BRASSKNUCKLE) && !victimPed->IsPlayer()) ) + { + posOffset.Normalise(); + victimPed->bIsStanding = false; + if(m_eWeaponType == WEAPONTYPE_CHAINSAW) + victimPed->ApplyMoveForce(posOffset.x*-2.0f, posOffset.y*-2.0f, 2.0f); + else + victimPed->ApplyMoveForce(posOffset.x*-5.0f, posOffset.y*-5.0f, 3.0f); + + if ( isHeavy && victimPed->IsPlayer() ) + victimPed->SetFall(3000, AnimationId(ANIM_STD_HIGHIMPACT_FRONT + localDir), false); + else + victimPed->SetFall(1500, AnimationId(ANIM_STD_HIGHIMPACT_FRONT + localDir), false); + + shooterPed->m_pSeekTarget = victimPed; + shooterPed->m_pSeekTarget->RegisterReference(&shooterPed->m_pSeekTarget); + } + } + else if (victimPed->Dying() && !anim2Playing) + { + posOffset.Normalise(); + victimPed->bIsStanding = false; + if(m_eWeaponType == WEAPONTYPE_CHAINSAW) + victimPed->ApplyMoveForce(posOffset.x*-1.0f, posOffset.y*-1.0f, 1.0f); + else + victimPed->ApplyMoveForce(posOffset.x*-5.0f, posOffset.y*-5.0f, 3.0f); + } + + m_eWeaponState = WEAPONSTATE_MELEE_MADECONTACT; + + if (m_eWeaponType != WEAPONTYPE_KNIFE && m_eWeaponType != WEAPONTYPE_MACHETE + && m_eWeaponType != WEAPONTYPE_KATANA && m_eWeaponType != WEAPONTYPE_CHAINSAW) { + + if (victimPed->m_nPedType == PEDTYPE_COP) + CEventList::RegisterEvent(EVENT_ASSAULT_POLICE, EVENT_ENTITY_PED, victimPed, shooterPed, 2000); + else + CEventList::RegisterEvent(EVENT_ASSAULT, EVENT_ENTITY_PED, victimPed, shooterPed, 2000); + } else { + if (victimPed->m_nPedType == PEDTYPE_COP) + CEventList::RegisterEvent(EVENT_ASSAULT_NASTYWEAPON_POLICE, EVENT_ENTITY_PED, victimPed, shooterPed, 2000); + else + CEventList::RegisterEvent(EVENT_ASSAULT_NASTYWEAPON, EVENT_ENTITY_PED, victimPed, shooterPed, 2000); + } + } + } + } + } + } + } + CVehicle *nearVeh = (CVehicle*)CWorld::TestSphereAgainstWorld(fireSource, info->m_fRadius, nil, false, true, false, false, false, false); + if (nearVeh && nearVeh->IsCar()) + { + CAutomobile *nearCar = (CAutomobile*)nearVeh; + m_eWeaponState = WEAPONSTATE_MELEE_MADECONTACT; + if (shooterPed == FindPlayerPed()) + { + if (nearCar->IsLawEnforcementVehicle()) + { + FindPlayerPed()->SetWantedLevelNoDrop(1); + } + CEventList::RegisterEvent(EVENT_ASSAULT, EVENT_ENTITY_VEHICLE, nearCar, shooterPed, 2000); + } + float oldHealth = nearCar->m_fHealth; + if (m_eWeaponType == WEAPONTYPE_CHAINSAW) + { + for( int32 i=0; i<4; i++ ) + { + CParticle::AddParticle(PARTICLE_SPARK_SMALL, gaTempSphereColPoints[0].point, CVector(0.0f, 0.0f, 0.3f)); + CParticle::AddParticle(PARTICLE_SPARK, gaTempSphereColPoints[0].point, gaTempSphereColPoints[0].normal * 0.1f); + } + } + if (m_eWeaponType == WEAPONTYPE_CHAINSAW) + { + nearCar->VehicleDamage(info->m_nDamage * (0.00075f * nearCar->pHandling->fMass), gaTempSphereColPoints[0].pieceB); + + CParticle::AddParticle(PARTICLE_HEATHAZE, gaTempSphereColPoints[0].point, CVector(0.0f, 0.0f, 0.0f), 0, 0.0f, 0, 0, 0, 0); + } + else + { + nearCar->VehicleDamage(info->m_nDamage* (0.01f * nearCar->pHandling->fMass), gaTempSphereColPoints[0].pieceB); + } + if (nearCar->m_fHealth < oldHealth) + { + nearCar->m_nLastWeaponDamage = m_eWeaponType; + nearCar->m_pLastDamageEntity = shooterPed; + } + if (shooterPed->m_fDamageImpulse == 0.0f) + { + shooterPed->m_pDamageEntity = nearCar; + nearCar->RegisterReference(&shooterPed->m_pDamageEntity); + } + damageEntityRegistered = 2; + if (FindPlayerPed()->GetWeapon() == this && nearCar->VehicleCreatedBy != MISSION_VEHICLE) + { + if (nearCar->AutoPilot.m_nDrivingStyle != DRIVINGSTYLE_PLOUGH_THROUGH + && (CGeneral::GetRandomTrueFalse() || nearCar->AutoPilot.m_nCarMission != MISSION_CRUISE)) + { + int leaveCarDelay = 200; + CPed *driver = nearCar->pDriver; + if (driver && driver->CharCreatedBy != MISSION_CHAR) + { + if (driver->m_pedStats->m_temper <= driver->m_pedStats->m_fear) + { + driver->SetObjective(OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE); + } + else + { + driver->SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, FindPlayerPed()); + driver->m_objectiveTimer = CTimer::GetTimeInMilliseconds() + 10000; + driver->m_prevObjective = OBJECTIVE_KILL_CHAR_ON_FOOT; + } + driver->m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 200; + leaveCarDelay = 400; + } + for (int j = 0; j < nearCar->m_nNumPassengers; ++j) + { + CPed *passenger = nearCar->pPassengers[j]; + if (passenger && passenger->CharCreatedBy != MISSION_CHAR) + { + nearCar->pPassengers[j]->SetObjective(OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE); + passenger->m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + leaveCarDelay; + leaveCarDelay += 200; + } + } + } + else + { + CPed *driver = nearCar->pDriver; + if (driver) + { + if (driver->m_objective != OBJECTIVE_LEAVE_CAR && driver->m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT && + driver->m_objective != OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE) + { + if (nearCar->AutoPilot.m_nDrivingStyle != DRIVINGSTYLE_PLOUGH_THROUGH) + nearCar->AutoPilot.m_nCruiseSpeed = nearCar->AutoPilot.m_nCruiseSpeed * 1.5f; + + nearCar->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_PLOUGH_THROUGH; + } + } + } + } + } + if (m_eWeaponType == WEAPONTYPE_CHAINSAW) + { + CEntity *nearStatic = (CObject*)CWorld::TestSphereAgainstWorld(fireSource, info->m_fRadius, nil, true, false, false, true, false, false); + if (nearStatic) + { + for(int i=0; i < 4; i++) { + CParticle::AddParticle(PARTICLE_SPARK_SMALL, gaTempSphereColPoints[0].point, CVector(0.0f, 0.0f, 0.3f), 0, 0.0f, 0, 0, 0, 0); + CParticle::AddParticle(PARTICLE_SPARK, gaTempSphereColPoints[0].point, 0.1f * gaTempSphereColPoints[0].normal, 0, 0.0f, 0, 0, 0, 0); + } + + CParticle::AddParticle(PARTICLE_HEATHAZE, gaTempSphereColPoints[0].point, CVector(0.0f, 0.0f, 0.0f), 0, 0.0f, 0, 0, 0, 0); + + if (!damageEntityRegistered) + { + m_eWeaponState = WEAPONSTATE_MELEE_MADECONTACT; + if (shooterPed->m_fDamageImpulse == 0.0f) + { + shooterPed->m_pDamageEntity = nearStatic; + nearStatic->RegisterReference(&shooterPed->m_pDamageEntity); + } + } + if (nearStatic->IsObject() && ((CObject*)nearStatic)->m_nCollisionDamageEffect >= DAMAGE_EFFECT_SMASH_COMPLETELY) + ((CObject*)nearStatic)->ObjectDamage(200.0f); + } + } + + return true; +} + +bool +CWeapon::FireInstantHit(CEntity *shooter, CVector *fireSource) +{ + ASSERT(shooter!=nil); + ASSERT(fireSource!=nil); + + CWeaponInfo *info = GetInfo(); + + CVector source, target; + CColPoint point; + CEntity *victim = nil; + + float heading = RADTODEG(shooter->GetForward().Heading()); + float angle = DEGTORAD(heading); + + CVector2D ahead(-Sin(angle), Cos(angle)); + ahead.Normalise(); + + CVector vel = ((CPed *)shooter)->m_vecMoveSpeed; + int32 shooterMoving = false; + if ( Abs(vel.x) > 0.0f && Abs(vel.y) > 0.0f ) + shooterMoving = true; + + if ( shooter == FindPlayerPed() ) + { + static float prev_heading = 0.0f; + prev_heading = ((CPed*)shooter)->m_fRotationCur; + } + + if ( shooter->IsPed() && ((CPed *)shooter)->m_pPointGunAt ) + { + CPed *shooterPed = (CPed *)shooter; + if ( shooterPed->m_pedIK.m_flags & CPedIK::GUN_POINTED_SUCCESSFULLY ) + { + int32 accuracy = shooterPed->m_wepAccuracy; + int32 inaccuracy = 100-accuracy; + + CPed *threatAttack = (CPed*)shooterPed->m_pPointGunAt; + if ( threatAttack->IsPed() ) + { + threatAttack->m_pedIK.GetComponentPosition(target, PED_MID); + threatAttack->ReactToPointGun(shooter); + } + else + target = threatAttack->GetPosition(); + + target -= *fireSource; + float distToTarget = Max(target.Magnitude(), 0.01f); + target *= info->m_fRange / distToTarget; + target += *fireSource; + + if (shooter == FindPlayerPed() && inaccuracy != 0.f) + { + float newInaccuracy = fPlayerAimScale * FindPlayerPed()->m_fAttackButtonCounter * (inaccuracy * Min(1.f, fPlayerAimScaleDist / distToTarget)); + if (FindPlayerPed()->bIsDucking) + newInaccuracy *= 0.4f; + + target.x += CGeneral::GetRandomNumberInRange(-0.15f, 0.15f) * newInaccuracy; + target.y += CGeneral::GetRandomNumberInRange(-0.15f, 0.15f) * newInaccuracy; + target.z += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * newInaccuracy; + FindPlayerPed()->m_fAttackButtonCounter += info->m_nDamage * 0.04f; + } + else if (inaccuracy > 0.f) + { + if (threatAttack == FindPlayerPed()) + { + float speed = Min(0.33f, FindPlayerPed()->m_vecMoveSpeed.Magnitude()); + inaccuracy *= (0.3f * speed * 100.f / 33.f + 0.8f); + } + target.x += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * inaccuracy; + target.y += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * inaccuracy; + target.z += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * inaccuracy; + } + + if (shooter == FindPlayerPed()) + CWorld::bIncludeDeadPeds = true; + + CWorld::bIncludeBikers = true; + ProcessLineOfSight(*fireSource, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, false, false); + CWorld::bIncludeDeadPeds = false; + CWorld::bIncludeBikers = false; + } + else + { + target.x = info->m_fRange; + target.y = 0.0f; + target.z = 0.0f; + + shooterPed->TransformToNode(target, PED_HANDR); + + CWorld::bIncludeBikers = true; + ProcessLineOfSight(*fireSource, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, false, false); + CWorld::bIncludeBikers = false; + } +#ifdef FIX_BUGS + // fix muzzleflash rotation + heading = CGeneral::GetAngleBetweenPoints(source.x, source.y, target.x, target.y); + angle = DEGTORAD(heading); + + ahead = CVector2D(-Sin(angle), Cos(angle)); + ahead.Normalise(); +#endif + } + else if ( shooter == FindPlayerPed() && TheCamera.Cams[0].Using3rdPersonMouseCam() ) + { +#ifdef FREE_CAM + if (CCamera::bFreeCam) { + CPlayerPed* shooterPed = (CPlayerPed*)shooter; + Find3rdPersonCamTargetVectorFromCachedVectors(info->m_fRange, *fireSource, source, target, shooterPed->m_cachedCamSource, shooterPed->m_cachedCamFront, shooterPed->m_cachedCamUp); + + if ((shooterPed->m_pedIK.m_flags & CPedIK::GUN_POINTED_SUCCESSFULLY) == 0) { + target.x = info->m_fRange; + target.y = 0.0f; + target.z = 0.0f; + + shooterPed->TransformToNode(target, PED_HANDR); + } + } else +#endif + { + TheCamera.Find3rdPersonCamTargetVector(info->m_fRange, *fireSource, source, target); + } + +#ifdef FIX_BUGS + // fix muzzleflash rotation + heading = CGeneral::GetAngleBetweenPoints(source.x, source.y, target.x, target.y); + angle = DEGTORAD(heading); + + ahead = CVector2D(-Sin(angle), Cos(angle)); + ahead.Normalise(); +#endif + CWorld::bIncludeBikers = true; + CWorld::bIncludeDeadPeds = true; + CWorld::bIncludeCarTyres = true; + ProcessLineOfSight(source, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, false, false); + CWorld::bIncludeBikers = false; + CWorld::bIncludeDeadPeds = false; + CWorld::bIncludeCarTyres = false; + + if (victim) + CheckForShootingVehicleOccupant(&victim, &point, m_eWeaponType, source, target); + + int32 rotSpeed = 1; + if ( m_eWeaponType == WEAPONTYPE_M4 ) + rotSpeed = 4; + + CVector bulletPos; + if ( CHeli::TestBulletCollision(&source, &target, &bulletPos, 4) ) + { + for ( int32 i = 0; i < 16; i++ ) + CParticle::AddParticle(PARTICLE_SPARK, bulletPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, rotSpeed); + } + } + else + { + uint32 model = shooter->GetModelIndex(); + if (model == MI_HUNTER || model == MI_SEASPAR || model == MI_SPARROW) + { + float inaccuracyMult = 0.6f; + target = shooter->GetForward(); + if (shooter->GetStatus() == STATUS_PLAYER) + { + target *= info->m_fRange; + target += *fireSource; + CWeapon::DoDriveByAutoAiming(FindPlayerPed(), (CVehicle*)shooter, fireSource, &target); + target -= *fireSource; + target.Normalise(); + if (model == MI_SEASPAR || model == MI_SPARROW) + inaccuracyMult = 0.1f; + else + inaccuracyMult = 0.3f; + } + target.x += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * inaccuracyMult; + target.y += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * inaccuracyMult; + target.z += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * inaccuracyMult; + + target.Normalise(); + target *= info->m_fRange; + target += *fireSource; + CWorld::pIgnoreEntity = shooter; + ProcessLineOfSight(*fireSource, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, true, false); + CWorld::pIgnoreEntity = nil; + + int32 rotSpeed = 1; + if (m_eWeaponType == WEAPONTYPE_M4) + rotSpeed = 4; + + CVector bulletPos; + if (CHeli::TestBulletCollision(fireSource, &target, &bulletPos, 4)) + { + for (int32 i = 0; i < 16; i++) + CParticle::AddParticle(PARTICLE_SPARK, bulletPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, rotSpeed); + } + } + else + { + float shooterHeading = RADTODEG(shooter->GetForward().Heading()); + float shooterAngle = DEGTORAD(shooterHeading); + + CVector2D rotOffset(-Sin(shooterAngle), Cos(shooterAngle)); + rotOffset.Normalise(); + + target = *fireSource; + target.x += rotOffset.x * info->m_fRange; + target.y += rotOffset.y * info->m_fRange; + + CParticle::HandleShootableBirdsStuff(shooter, *fireSource); + if (shooter->IsPed() && ((CPed*)shooter)->bDoomAim && (shooter != FindPlayerPed() || !info->IsFlagSet(WEAPONFLAG_CANAIM))) + { + CWeapon::DoDoomAiming(shooter, fireSource, &target); + } + + CWorld::bIncludeBikers = true; + ProcessLineOfSight(*fireSource, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, false, false); + CWorld::bIncludeBikers = false; + + int32 rotSpeed = 1; + if (m_eWeaponType == WEAPONTYPE_M4) + rotSpeed = 4; + + CVector bulletPos; + if (CHeli::TestBulletCollision(fireSource, &target, &bulletPos, 4)) + { + for (int32 i = 0; i < 16; i++) + CParticle::AddParticle(PARTICLE_SPARK, bulletPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, rotSpeed); + } + } + } + + if ( shooter->IsPed() && victim) + { + if (victim == ((CPed*)shooter)->m_leader) + return false; + + if (victim->IsPed() && ((CPed*)shooter)->IsGangMember() && !((CPed*)victim)->CanBeDamagedByThisGangMember((CPed*)shooter)) + return false; + } + + if (shooter->IsPed()) + CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_PED, shooter, (CPed*)shooter, 1000); + else if (shooter->IsVehicle() && ((CVehicle*)shooter)->pDriver) + CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_VEHICLE, shooter, ((CVehicle*)shooter)->pDriver, 1000); + + if ( shooter == FindPlayerPed() ) + { + if ( !(CTimer::GetFrameCounter() & 3) ) + MakePedsJumpAtShot((CPhysical*)shooter, fireSource, &target); + } + + switch ( m_eWeaponType ) + { + case WEAPONTYPE_M4: + case WEAPONTYPE_RUGER: + case WEAPONTYPE_M60: + case WEAPONTYPE_MINIGUN: + case WEAPONTYPE_HELICANNON: + { + static uint8 counter = 0; + + if ( info->m_nFiringRate >= 50 || !(++counter & 1) ) + { +#ifdef FIX_BUGS + AddGunFlashBigGuns(*fireSource, target); +#else + AddGunFlashBigGuns(*fireSource, *fireSource + target); +#endif + + CVector gunshellPos = *fireSource; + gunshellPos -= CVector(0.65f*ahead.x, 0.65f*ahead.y, 0.0f); + CVector dir = CrossProduct(CVector(ahead.x, ahead.y, 0.0f), CVector(0.0f, 0.0f, 5.0f)); + dir.Normalise2D(); + AddGunshell(shooter, gunshellPos, CVector2D(dir.x, dir.y), 0.02f); + } + + break; + } + + case WEAPONTYPE_UZI: + case WEAPONTYPE_TEC9: + case WEAPONTYPE_SILENCED_INGRAM: + case WEAPONTYPE_MP5: + { + CPointLights::AddLight(CPointLights::LIGHT_POINT, + *fireSource, CVector(0.0f, 0.0f, 0.0f), 5.0f, + 1.0f, 0.8f, 0.0f, CPointLights::FOG_NONE, false); + + CVector gunflashPos = *fireSource; + + if ( shooterMoving ) + gunflashPos += CVector(1.5f*vel.x, 1.5f*vel.y, 0.0f); + + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.07f); + gunflashPos += CVector(0.06f*ahead.x, 0.06f*ahead.y, 0.0f); + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.05f); + gunflashPos += CVector(0.04f*ahead.x, 0.04f*ahead.y, 0.0f); + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.04f); + gunflashPos += CVector(0.04f*ahead.x, 0.04f*ahead.y, 0.0f); + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.03f); + gunflashPos += CVector(0.03f*ahead.x, 0.03f*ahead.y, 0.0f); + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.03f); + gunflashPos += CVector(0.03f*ahead.x, 0.03f*ahead.y, 0.0f); + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f); + gunflashPos += CVector(0.02f*ahead.x, 0.02f*ahead.y, 0.0f); + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.01f); + + CVector gunsmokePos = *fireSource; + float rnd = CGeneral::GetRandomNumberInRange(0.05f, 0.25f); + CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(ahead.x*rnd, ahead.y*rnd, 0.0f)); + + CVector gunshellPos = *fireSource; + gunshellPos -= CVector(0.2f*ahead.x, 0.2f*ahead.y, 0.0f); + CVector dir = CrossProduct(CVector(ahead.x, ahead.y, 0.0f), CVector(0.0f, 0.0f, 5.0f)); + dir.Normalise2D(); + AddGunshell(shooter, gunshellPos, CVector2D(dir.x, dir.y), 0.015f); + + break; + } + + case WEAPONTYPE_COLT45: + case WEAPONTYPE_PYTHON: + case WEAPONTYPE_SNIPERRIFLE: + case WEAPONTYPE_LASERSCOPE: + { + CPointLights::AddLight(CPointLights::LIGHT_POINT, + *fireSource, CVector(0.0f, 0.0f, 0.0f), 5.0f, + 1.0f, 0.8f, 0.0f, CPointLights::FOG_NONE, false); + + CVector gunflashPos = *fireSource; + + if ( shooterMoving ) + gunflashPos += CVector(1.5f*vel.x, 1.5f*vel.y, 0.0f); + + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.06f); + gunflashPos += CVector(0.06f*ahead.x, 0.06f*ahead.y, 0.0f); + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.04f); + gunflashPos += CVector(0.04f*ahead.x, 0.04f*ahead.y, 0.0f); + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f); + + CVector gunsmokePos = *fireSource; + CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(ahead.x*0.10f, ahead.y*0.10f, 0.0f), nil, 0.005f); + CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(ahead.x*0.15f, ahead.y*0.15f, 0.0f), nil, 0.015f); + CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(ahead.x*0.20f, ahead.y*0.20f, 0.0f), nil, 0.025f); + + CVector gunshellPos = *fireSource; + gunshellPos -= CVector(0.2f*ahead.x, 0.2f*ahead.y, 0.0f); + CVector dir = CrossProduct(CVector(ahead.x, ahead.y, 0.0f), CVector(0.0f, 0.0f, 5.0f)); + dir.Normalise2D(); + AddGunshell(shooter, gunshellPos, CVector2D(dir.x, dir.y), 0.015f); + + break; + } + default: break; + } + + DoBulletImpact(shooter, victim, fireSource, &target, &point, ahead); + + return true; +} + +void +CWeapon::AddGunFlashBigGuns(CVector start, CVector end) +{ + CPointLights::AddLight(CPointLights::LIGHT_POINT, + start, CVector(0.0f, 0.0f, 0.0f), 5.0f, + 1.0f, 0.8f, 0.0f, CPointLights::FOG_NONE, false); + CVector gunflashPos = start; + + CVector shootVec = end - start; + + // Wtf did you do there R*? + shootVec.Normalise(); + CVector2D ahead = shootVec; + ahead.Normalise(); + + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.08f); + gunflashPos += CVector(0.06f * ahead.x, 0.06f * ahead.y, 0.0f); + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.06f); + gunflashPos += CVector(0.06f * ahead.x, 0.06f * ahead.y, 0.0f); + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.06f); + + gunflashPos = start; + gunflashPos += CVector(-0.1f * ahead.x, -0.1f * ahead.y, 0.0f); + gunflashPos.z += 0.04f; + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.04f); + gunflashPos.z += 0.04f; + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f); + gunflashPos.z += 0.03f; + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f); + + gunflashPos = start; + gunflashPos += CVector(-0.1f * ahead.x, -0.1f * ahead.y, 0.0f); + gunflashPos.z -= 0.04f; + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.04f); + gunflashPos.z -= 0.04f; + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f); + gunflashPos.z -= 0.03f; + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f); + + CVector offset = CrossProduct(CVector(ahead.x, ahead.y, 0.0f), CVector(0.0f, 0.0f, 5.0f)); + offset.Normalise2D(); + + gunflashPos = start; + gunflashPos += CVector(-0.1f * ahead.x, -0.1f * ahead.y, 0.0f); + gunflashPos += CVector(0.06f * offset.x, 0.06f * offset.y, 0.0f); + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.04f); + gunflashPos += CVector(0.04f * offset.x, 0.04f * offset.y, 0.0f); + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.03f); + gunflashPos += CVector(0.03f * offset.x, 0.03f * offset.y, 0.0f); + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f); + + gunflashPos = start; + gunflashPos += CVector(-0.1f * ahead.x, -0.1f * ahead.y, 0.0f); + gunflashPos -= CVector(0.06f * offset.x, 0.06f * offset.y, 0.0f); + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.04f); + gunflashPos -= CVector(0.04f * offset.x, 0.04f * offset.y, 0.0f); + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.03f); + gunflashPos -= CVector(0.03f * offset.x, 0.03f * offset.y, 0.0f); + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f); + + CVector gunsmokePos = start; + float rnd = CGeneral::GetRandomNumberInRange(0.05f, 0.25f); + CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(ahead.x * rnd, ahead.y * rnd, 0.0f)); +} + +void +CWeapon::AddGunshell(CEntity *shooter, CVector const &source, CVector2D const &direction, float size) +{ + ASSERT(shooter!=nil); + + if ( shooter == nil) + return; + + CVector dir(direction.x*0.05f, direction.y*0.05f, CGeneral::GetRandomNumberInRange(0.02f, 0.08f)); + + static CVector prevEntityPosition(0.0f, 0.0f, 0.0f); + CVector entityPosition = shooter->GetPosition(); + + CVector diff = entityPosition - prevEntityPosition; + + if ( Abs(diff.x)+Abs(diff.y)+Abs(diff.z) > 1.5f ) + { + prevEntityPosition = entityPosition; + + CParticle::AddParticle(PARTICLE_GUNSHELL_FIRST, + source, dir, nil, size, CGeneral::GetRandomNumberInRange(-20.0f, 20.0f)); + } + else + { + CParticle::AddParticle(PARTICLE_GUNSHELL, + source, dir, nil, size, CGeneral::GetRandomNumberInRange(-20.0f, 20.0f)); + } +} + + +void +CWeapon::DoBulletImpact(CEntity *shooter, CEntity *victim, + CVector *source, CVector *target, CColPoint *point, CVector2D ahead) +{ + ASSERT(shooter!=nil); + ASSERT(source!=nil); + ASSERT(target!=nil); + ASSERT(point!=nil); + + CWeaponInfo *info = GetInfo(); + + if ( victim ) + { + if (shooter) + { + if (shooter && shooter->IsPed() && ((CPed*)shooter)->m_attachedTo == victim) + return; + + if (shooter->IsPed() && !((CPed*)shooter)->IsPlayer()) + { + CPed* shooterPed = (CPed*)shooter; + CEntity* guyWePointGun = shooterPed->m_pPointGunAt; + if (guyWePointGun) + { + if (victim != guyWePointGun) + { + float distWithAim = (guyWePointGun->GetPosition() - shooter->GetPosition()).Magnitude(); + float distWithBullet = (point->point - shooter->GetPosition()).Magnitude(); + if (distWithAim > 0.1f && distWithBullet > 0.1f) + { + // Normalize + CVector aimDir = (guyWePointGun->GetPosition() - shooter->GetPosition()) * (1.0f / distWithAim); + CVector bulletDir = (point->point - shooter->GetPosition()) * (1.0f / distWithBullet); + + float dotProd = DotProduct(aimDir, bulletDir); + float aimAndBulletAngle; + if (dotProd <= 0.35f) + aimAndBulletAngle = PI; + else + aimAndBulletAngle = Acos(dotProd); + + if (aimAndBulletAngle <= DEGTORAD(45.0f) && (aimAndBulletAngle <= DEGTORAD(15.0f) || distWithBullet / distWithAim >= 0.75f) && distWithBullet / distWithAim >= 0.99f) + { + shooterPed->bObstacleShowedUpDuringKillObjective = false; + shooterPed->m_shotTime = 0; + } + else + { + shooterPed->bObstacleShowedUpDuringKillObjective = true; + shooterPed->m_shootTimer = 0; + shooterPed->m_shotTime = CTimer::GetTimeInMilliseconds(); + if (distWithAim < 10.0f) + shooterPed->SetAttackTimer(1500); + else + shooterPed->SetAttackTimer(3000); + } + } + } + } + } + } + CGlass::WasGlassHitByBullet(victim, point->point); + + CVector traceTarget = point->point; + CBulletTraces::AddTrace(source, &traceTarget, m_eWeaponType, shooter); + + if (victim->IsPed() && shooter->IsVehicle() && ((CVehicle*)shooter)->pDriver) + shooter = ((CVehicle*)shooter)->pDriver; + + if ( victim->IsPed() && shooter->IsPed() && + (((CPed*)shooter)->m_nPedType != ((CPed*)victim)->m_nPedType || ((CPed*)shooter)->m_nPedType == PEDTYPE_PLAYER2 || + !((CPed*)shooter)->IsGangMember() && ((CPed*)shooter)->m_nPedType != PEDTYPE_COP)) + { + CPed *victimPed = (CPed *)victim; + if ( !victimPed->DyingOrDead() && victim != shooter ) + { + CVector pos = victimPed->GetPosition(); + + CVector2D posOffset(source->x-pos.x, source->y-pos.y); + int32 localDir = victimPed->GetLocalDirection(posOffset); + + victimPed->ReactToAttack(shooter); + + if ( !victimPed->IsPedInControl() || victimPed->bIsDucking ) + { + victimPed->InflictDamage(shooter, m_eWeaponType, info->m_nDamage, (ePedPieceTypes)point->pieceB, localDir); + } + else + { + if ( victimPed->bCanBeShotInVehicle && (IsShotgun(m_eWeaponType) || + (!victimPed->IsPlayer() && (m_eWeaponType == WEAPONTYPE_HELICANNON || m_eWeaponType == WEAPONTYPE_M60 || m_eWeaponType == WEAPONTYPE_PYTHON)))) + { + posOffset.Normalise(); + victimPed->bIsStanding = false; + + victimPed->ApplyMoveForce(posOffset.x*-5.0f, posOffset.y*-5.0f, 5.0f); + victimPed->SetFall(1500, AnimationId(ANIM_STD_HIGHIMPACT_FRONT + localDir), false); + + victimPed->InflictDamage(shooter, m_eWeaponType, info->m_nDamage, (ePedPieceTypes)point->pieceB, localDir); + } + else + { + if ( victimPed->IsPlayer() ) + { + CPlayerPed *victimPlayer = (CPlayerPed *)victimPed; + if ( victimPlayer->m_nHitAnimDelayTimer < CTimer::GetTimeInMilliseconds() && victimPed->m_nPedState != PED_DRIVING ) + { + victimPed->ClearAttackByRemovingAnim(); + + CAnimBlendAssociation *asoc = CAnimManager::AddAnimation(victimPed->GetClump(), ASSOCGRP_STD, AnimationId(ANIM_STD_HITBYGUN_FRONT + localDir)); + ASSERT(asoc!=nil); + + asoc->blendAmount = 0.0f; + asoc->blendDelta = 8.0f; + + if ( m_eWeaponType == WEAPONTYPE_M4 ) + victimPlayer->m_nHitAnimDelayTimer = CTimer::GetTimeInMilliseconds() + 2500; + else + victimPlayer->m_nHitAnimDelayTimer = CTimer::GetTimeInMilliseconds() + 1000; + } + } + else + { + victimPed->ClearAttackByRemovingAnim(); + + CAnimBlendAssociation *asoc = CAnimManager::AddAnimation(victimPed->GetClump(), ASSOCGRP_STD, AnimationId(ANIM_STD_HITBYGUN_FRONT + localDir)); + ASSERT(asoc!=nil); + + asoc->blendAmount = 0.0f; + asoc->blendDelta = 8.0f; + } + + victimPed->InflictDamage(shooter, m_eWeaponType, info->m_nDamage, (ePedPieceTypes)point->pieceB, localDir); + } + } + + if ( victimPed->m_nPedType == PEDTYPE_COP ) + CEventList::RegisterEvent(EVENT_SHOOT_COP, EVENT_ENTITY_PED, victim, (CPed*)shooter, 10000); + else + CEventList::RegisterEvent(EVENT_SHOOT_PED, EVENT_ENTITY_PED, victim, (CPed*)shooter, 10000); + + if ( CGame::nastyGame ) + { + uint8 bloodAmount = 8; + if ( IsShotgun(m_eWeaponType) || m_eWeaponType == WEAPONTYPE_HELICANNON ) + bloodAmount = 32; + + CVector dir = (point->point - victim->GetPosition()) * 0.01f; + dir.z = 0.01f; + + if ( victimPed->GetIsOnScreen() ) + { + for ( uint8 i = 0; i < bloodAmount; i++ ) + CParticle::AddParticle(PARTICLE_BLOOD_SMALL, point->point, dir); + } + + if (m_eWeaponType == WEAPONTYPE_MINIGUN) + { + CParticle::AddParticle(PARTICLE_TEST, point->point, CVector(0.f, 0.f, 0.f), nil, 0.f, 0, 0, 0, 0); + CParticle::AddParticle(PARTICLE_TEST, point->point + CVector(0.2f, -0.2f, 0.f), CVector(0.f, 0.f, 0.f), nil, 0.f, 0, 0, 0, 0); + CParticle::AddParticle(PARTICLE_TEST, point->point + CVector(-0.2f, 0.2f, 0.f), CVector(0.f, 0.f, 0.f), nil, 0.f, 0, 0, 0, 0); + } + } + } + else + { + if ( CGame::nastyGame ) + { + CVector dir = (point->point - victim->GetPosition()) * 0.01f; + dir.z = 0.01f; + + if ( victim->GetIsOnScreen() ) + { + for ( int32 i = 0; i < 8; i++ ) + CParticle::AddParticle(PARTICLE_BLOOD_SMALL, point->point + CVector(0.0f, 0.0f, 0.15f), dir); + } + + if ( victimPed->Dead() ) + { + CAnimBlendAssociation *asoc; + if ( RpAnimBlendClumpGetFirstAssociation(victimPed->GetClump(), ASSOC_FRONTAL) ) + asoc = CAnimManager::BlendAnimation(victimPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR_FRONT, 8.0f); + else + asoc = CAnimManager::BlendAnimation(victimPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR, 8.0f); + + if ( asoc ) + { + asoc->SetCurrentTime(0.0f); + asoc->flags |= ASSOC_RUNNING; + asoc->flags &= ~ASSOC_FADEOUTWHENDONE; + } + } + } + } + } + else + { + switch ( victim->GetType() ) + { + case ENTITY_TYPE_BUILDING: + { + for ( int32 i = 0; i < 16; i++ ) + CParticle::AddParticle(PARTICLE_SPARK, point->point, point->normal*0.05f); + +#ifndef FIX_BUGS + CVector dist = point->point - (*source); + float distMagnitude = dist.Magnitude(); + CVector smokePos = point->point - Max(distMagnitude / 10.0f, 0.2f) * dist / distMagnitude; +#else + CVector smokePos = point->point; +#endif // !FIX_BUGS + + smokePos.x += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f); + smokePos.y += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f); + smokePos.z += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f); + + CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, smokePos, CVector(0.0f, 0.0f, 0.0f)); + + break; + } + case ENTITY_TYPE_VEHICLE: + { + if (point->pieceB >= CAR_PIECE_WHEEL_LF && point->pieceB <= CAR_PIECE_WHEEL_RR) { + ((CVehicle*)victim)->BurstTyre(point->pieceB, true); + + for (int32 i = 0; i < 4; i++) + CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, point->point, point->normal * 0.05f); + } + else + { + ((CVehicle*)victim)->InflictDamage(shooter, m_eWeaponType, info->m_nDamage); + + for (int32 i = 0; i < 16; i++) + CParticle::AddParticle(PARTICLE_SPARK, point->point, point->normal * 0.05f); + +#ifndef FIX_BUGS + CVector dist = point->point - (*source); + CVector offset = dist - Max(0.2f * dist.Magnitude(), 0.5f) * CVector(ahead.x, ahead.y, 0.0f); + CVector smokePos = *source + offset; +#else + CVector smokePos = point->point; +#endif + + CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, smokePos, CVector(0.0f, 0.0f, 0.0f)); + } + + + if ( shooter->IsPed() ) + { + CPed *shooterPed = (CPed *)shooter; + + if ( shooterPed->bNotAllowedToDuck ) + { + if ( shooterPed->bKindaStayInSamePlace && victim != shooterPed->m_pPointGunAt ) + { + shooterPed->bKindaStayInSamePlace = false; + shooterPed->m_duckAndCoverTimer = CTimer::GetTimeInMilliseconds() + 15000; + } + } + } + + break; + } + case ENTITY_TYPE_OBJECT: + { + for ( int32 i = 0; i < 8; i++ ) + CParticle::AddParticle(PARTICLE_SPARK, point->point, point->normal*0.05f); + + CObject *victimObject = (CObject *)victim; + + if ( !victimObject->bInfiniteMass && victimObject->m_fCollisionDamageMultiplier < 99.9f) + { + bool notStatic = !victimObject->GetIsStatic(); + if (notStatic && victimObject->m_fUprootLimit <= 0.0f) + { + victimObject->SetIsStatic(false); + victimObject->AddToMovingList(); + } + + notStatic = !victimObject->GetIsStatic(); + if (!notStatic) + { + CVector moveForce = point->normal * -4.0f; + victimObject->ApplyMoveForce(moveForce.x, moveForce.y, moveForce.z); + } + } else if (victimObject->m_nCollisionDamageEffect >= DAMAGE_EFFECT_SMASH_COMPLETELY) { + victimObject->ObjectDamage(50.f); + } + + break; + } + default: break; + } + } + + switch ( victim->GetType() ) + { + case ENTITY_TYPE_BUILDING: + { + PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_1, point->point); + break; + } + case ENTITY_TYPE_VEHICLE: + { + CStats::BulletsThatHit++; + DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_VEHICLE, 1.0f); + break; + } + case ENTITY_TYPE_PED: + { + CStats::BulletsThatHit++; + DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_PED, 1.0f); + ((CPed*)victim)->Say(SOUND_PED_BULLET_HIT); + break; + } + case ENTITY_TYPE_OBJECT: + { + CStats::BulletsThatHit++; + PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_2, point->point); + break; + } + case ENTITY_TYPE_DUMMY: + { + PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_3, point->point); + break; + } + default: break; + } + } + else + CBulletTraces::AddTrace(source, target, m_eWeaponType, shooter); + + if ( shooter == FindPlayerPed() ) + CPad::GetPad(0)->StartShake_Distance(240, 128, FindPlayerPed()->GetPosition().x, FindPlayerPed()->GetPosition().y, FindPlayerPed()->GetPosition().z); + + BlowUpExplosiveThings(victim); +} + + +bool +CWeapon::FireShotgun(CEntity *shooter, CVector *fireSource) +{ + ASSERT(shooter!=nil); + ASSERT(fireSource!=nil); + + CWeaponInfo *info = GetInfo(); + + float heading = RADTODEG(shooter->GetForward().Heading()); + float angle = DEGTORAD(heading); + + CVector2D rotOffset(-Sin(angle), Cos(angle)); + rotOffset.Normalise(); + + CVector gunflashPos = *fireSource; + gunflashPos += CVector(rotOffset.x*0.1f, rotOffset.y*0.1f, 0.0f); + CParticle::AddParticle(PARTICLE_GUNFLASH, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f); + gunflashPos += CVector(rotOffset.x*0.1f, rotOffset.y*0.1f, 0.0f); + CParticle::AddParticle(PARTICLE_GUNFLASH, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.15f); + gunflashPos += CVector(rotOffset.x*0.1f, rotOffset.y*0.1f, 0.0f); + CParticle::AddParticle(PARTICLE_GUNFLASH, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.2f); + CParticle::AddParticle(PARTICLE_GUNFLASH, *fireSource, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f); + + CVector gunsmokePos = *fireSource; + CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(rotOffset.x*0.10f, rotOffset.y*0.10f, 0.0f), nil, 0.1f); + CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(rotOffset.x*0.15f, rotOffset.y*0.15f, 0.0f), nil, 0.1f); + CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(rotOffset.x*0.20f, rotOffset.y*0.20f, 0.0f), nil, 0.1f); + CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(rotOffset.x*0.25f, rotOffset.y*0.25f, 0.0f), nil, 0.1f); + + CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_PED, shooter, (CPed*)shooter, 1000); + + CPointLights::AddLight(CPointLights::LIGHT_POINT, *fireSource, CVector(0.0, 0.0, 0.0), 5.0f, + 1.0f, 0.8f, 0.0f, CPointLights::FOG_NONE, false); + + float shooterAngle; + + if ( shooter->IsPed() && ((CPed*)shooter)->m_pPointGunAt != nil ) + { + CEntity *threatAttack = ((CPed*)shooter)->m_pPointGunAt; + shooterAngle = CGeneral::GetAngleBetweenPoints(threatAttack->GetPosition().x, threatAttack->GetPosition().y, + (*fireSource).x, (*fireSource).y); + } + else + shooterAngle = RADTODEG(shooter->GetForward().Heading()); + + int shootsAtOnce; + int checkObstacleOnShootNo; + float angleRange; + switch (m_eWeaponType) { + case WEAPONTYPE_SHOTGUN: + angleRange = DEGTORAD(9.0f); + checkObstacleOnShootNo = 1; + shootsAtOnce = 3; + break; + case WEAPONTYPE_SPAS12_SHOTGUN: + angleRange = DEGTORAD(6.0f); + checkObstacleOnShootNo = 1; + shootsAtOnce = 3; + break; + case WEAPONTYPE_STUBBY_SHOTGUN: + angleRange = DEGTORAD(18.0f); + checkObstacleOnShootNo = 2; + shootsAtOnce = 5; + break; + default: + break; + } + bool statUpdated = false; + float halfAngleRange = angleRange / 2.f; + float angleBetweenTwoShot = angleRange / (shootsAtOnce - 1.f); + + for ( int32 i = 0; i < shootsAtOnce; i++ ) + { + float shootAngle = DEGTORAD(RADTODEG(halfAngleRange - angleBetweenTwoShot * i) + shooterAngle); + CVector2D shootRot(-Sin(shootAngle), Cos(shootAngle)); + shootRot.Normalise(); + + CVector source, target; + CColPoint point; + CEntity *victim; + + if ( shooter == FindPlayerPed() && TheCamera.Cams[0].Using3rdPersonMouseCam() ) + { + CVector Left; +#ifdef FREE_CAM + if (CCamera::bFreeCam) { + CPlayerPed* shooterPed = (CPlayerPed*)shooter; + Find3rdPersonCamTargetVectorFromCachedVectors(1.0f, *fireSource, source, target, shooterPed->m_cachedCamSource, shooterPed->m_cachedCamFront, shooterPed->m_cachedCamUp); + Left = CrossProduct(shooterPed->m_cachedCamFront, shooterPed->m_cachedCamUp); + } + else +#endif + { + TheCamera.Find3rdPersonCamTargetVector(1.0f, *fireSource, source, target); + Left = CrossProduct(TheCamera.Cams[TheCamera.ActiveCam].Front, TheCamera.Cams[TheCamera.ActiveCam].Up); + } + + float f = (i - (shootsAtOnce / 2)) * angleBetweenTwoShot; + target = f * Left + target - source; + target *= info->m_fRange; + target += source; + CWorld::bIncludeCarTyres = true; + CWorld::bIncludeBikers = true; + CWorld::bIncludeDeadPeds = true; + ProcessLineOfSight(source, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, false, false); + CWorld::bIncludeDeadPeds = false; + CWorld::bIncludeCarTyres = false; + } + else + { + target = *fireSource; + target.x += shootRot.x * info->m_fRange; + target.y += shootRot.y * info->m_fRange; + + if ( shooter->IsPed() ) + { + CPed *shooterPed = (CPed *)shooter; + + if ( shooterPed->m_pPointGunAt == nil ) + DoDoomAiming(shooter, fireSource, &target); + else + { + CVector pos; + if (shooterPed->m_pPointGunAt->IsPed()) { + ((CPed*)shooterPed->m_pPointGunAt)->m_pedIK.GetComponentPosition(pos, PED_MID); + } else { + pos = ((CPed*)shooterPed->m_pPointGunAt)->GetPosition(); + } + + float distToTarget = (pos - (*fireSource)).Magnitude2D(); + target.z += info->m_fRange / distToTarget * (pos.z - target.z); + } + } + if (shooter == FindPlayerPed()) + CWorld::bIncludeDeadPeds = true; + + CWorld::bIncludeBikers = true; + ProcessLineOfSight(*fireSource, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, false, false); + CWorld::bIncludeDeadPeds = false; + } + CWorld::bIncludeBikers = false; + + if ( victim ) + { + CGlass::WasGlassHitByBullet(victim, point.point); + CWeapon::BlowUpExplosiveThings(victim); + if (i == checkObstacleOnShootNo) + { + if (shooter) + { + if (shooter->IsPed() && !((CPed*)shooter)->IsPlayer()) + { + CPed *shooterPed = (CPed*)shooter; + CEntity *guyWePointGun = shooterPed->m_pPointGunAt; + if (guyWePointGun) + { + if (victim != guyWePointGun) + { + float distWithAim = (guyWePointGun->GetPosition() - shooter->GetPosition()).Magnitude(); + float distWithBullet = (point.point - shooter->GetPosition()).Magnitude(); + if (distWithAim > 0.1f && distWithBullet > 0.1f) + { + // Normalize + CVector aimDir = (guyWePointGun->GetPosition() - shooter->GetPosition()) * (1.0f / distWithAim); + CVector bulletDir = (point.point - shooter->GetPosition()) * (1.0f / distWithBullet); + + float dotProd = DotProduct(aimDir, bulletDir); + float aimAndBulletAngle; + if (dotProd <= 0.35f) + aimAndBulletAngle = PI; + else + aimAndBulletAngle = Acos(dotProd); + + if (aimAndBulletAngle <= DEGTORAD(45.0f) && (aimAndBulletAngle <= DEGTORAD(15.0f) || distWithBullet / distWithAim >= 0.75f) && distWithBullet / distWithAim >= 0.99f) + { + shooterPed->bObstacleShowedUpDuringKillObjective = false; + shooterPed->m_shotTime = 0; + } + else + { + shooterPed->bObstacleShowedUpDuringKillObjective = true; + shooterPed->m_shootTimer = 0; + shooterPed->m_shotTime = CTimer::GetTimeInMilliseconds(); + if (distWithAim < 10.0f) + shooterPed->SetAttackTimer(1500); + else + shooterPed->SetAttackTimer(3000); + } + } + } + } + } + } + } + CBulletTraces::AddTrace(fireSource, &point.point, m_eWeaponType, shooter); + + if ( victim->IsPed() ) + { + CPed *victimPed = (CPed *)victim; + if ( !victimPed->DyingOrDead() && victim != shooter ) + { + bool cantStandup = true; + + CVector pos = victimPed->GetPosition(); + + CVector2D posOffset((*fireSource).x-pos.x, (*fireSource).y-pos.y); + int32 localDir = victimPed->GetLocalDirection(posOffset); + + victimPed->ReactToAttack(FindPlayerPed()); + + posOffset.Normalise(); + + if ( victimPed->m_getUpTimer > (CTimer::GetTimeInMilliseconds() - 3000) || + !victimPed->bCanBeShotInVehicle) + cantStandup = false; + + if ( victimPed->bIsStanding && cantStandup ) + { + victimPed->bIsStanding = false; + + victimPed->ApplyMoveForce(posOffset.x*-6.0f, posOffset.y*-6.0f, 5.0f); + } + else + victimPed->ApplyMoveForce(posOffset.x*-2.0f, posOffset.y*-2.0f, 0.0f); + + if ( cantStandup ) + victimPed->SetFall(1500, AnimationId(ANIM_STD_HIGHIMPACT_FRONT + localDir), false); + + victimPed->InflictDamage(shooter, m_eWeaponType, info->m_nDamage, (ePedPieceTypes)point.pieceB, localDir); + + if ( victimPed->m_nPedType == PEDTYPE_COP ) + CEventList::RegisterEvent(EVENT_SHOOT_COP, EVENT_ENTITY_PED, victim, (CPed*)shooter, 10000); + else + CEventList::RegisterEvent(EVENT_SHOOT_PED, EVENT_ENTITY_PED, victim, (CPed*)shooter, 10000); + + if ( CGame::nastyGame ) + { + uint8 bloodAmount = 8; + if ( IsShotgun(m_eWeaponType) ) + bloodAmount = 32; + + CVector dir = (point.point - victim->GetPosition()) * 0.01f; + dir.z = 0.01f; + + if ( victimPed->GetIsOnScreen() ) + { + for ( uint8 i = 0; i < bloodAmount; i++ ) + CParticle::AddParticle(PARTICLE_BLOOD_SMALL, point.point, dir); + } + } + } else { + if (CGame::nastyGame) + { + CVector dir = (point.point - victim->GetPosition()) * 0.01f; + dir.z = 0.01f; + + if (victimPed->GetIsOnScreen()) + { + for (uint8 i = 0; i < 8; i++) + CParticle::AddParticle(PARTICLE_BLOOD_SMALL, point.point + CVector(0.0f, 0.0f, 0.15f), dir); + } + if (victimPed->Dead()) + { + CAnimBlendAssociation *hitAssoc; + if (RpAnimBlendClumpGetFirstAssociation(victimPed->GetClump(), ASSOC_FRONTAL)) + { + hitAssoc = CAnimManager::BlendAnimation(victimPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR_FRONT, 8.0f); + } + else + { + hitAssoc = CAnimManager::BlendAnimation(victimPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR, 8.0f); + } + if (hitAssoc) + { + hitAssoc->SetCurrentTime(0.0f); + hitAssoc->SetRun(); + hitAssoc->flags &= ~ASSOC_DELETEFADEDOUT; + } + } + } + } + } + else + { + switch ( victim->GetType() ) + { + case ENTITY_TYPE_VEHICLE: + { + if (point.pieceB >= CAR_PIECE_WHEEL_LF && point.pieceB <= CAR_PIECE_WHEEL_RR) { + ((CVehicle*)victim)->BurstTyre(point.pieceB, true); + + for (int32 i = 0; i < 4; i++) + CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, point.point, point.normal * 0.05f); + } + else + { + ((CVehicle*)victim)->InflictDamage(shooter, m_eWeaponType, info->m_nDamage); + + for (int32 i = 0; i < 16; i++) + CParticle::AddParticle(PARTICLE_SPARK, point.point, point.normal * 0.05f); + +#ifndef FIX_BUGS + CVector dist = point.point - (*fireSource); + CVector offset = dist - Max(0.2f * dist.Magnitude(), 2.0f) * CVector(shootRot.x, shootRot.y, 0.0f); + CVector smokePos = *fireSource + offset; +#else + CVector smokePos = point.point; +#endif + + CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, smokePos, CVector(0.0f, 0.0f, 0.0f)); + } + break; + } + + case ENTITY_TYPE_BUILDING: + case ENTITY_TYPE_OBJECT: + { + for ( int32 i = 0; i < 16; i++ ) + CParticle::AddParticle(PARTICLE_SPARK, point.point, point.normal*0.05f); + +#ifndef FIX_BUGS + CVector dist = point.point - (*fireSource); + CVector offset = dist - Max(0.2f*dist.Magnitude(), 2.0f) * CVector(shootRot.x, shootRot.y, 0.0f); + CVector smokePos = *fireSource + offset; +#else + CVector smokePos = point.point; +#endif + + smokePos.x += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f); + smokePos.y += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f); + smokePos.z += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f); + + CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, smokePos, CVector(0.0f, 0.0f, 0.0f)); + + if ( victim->IsObject() ) + { + CObject *victimObject = (CObject *)victim; + + if ( !victimObject->bInfiniteMass ) + { + bool notStatic = !victimObject->GetIsStatic(); + if ( notStatic && victimObject->m_fUprootLimit <= 0.0f ) + { + victimObject->SetIsStatic(false); + victimObject->AddToMovingList(); + } + + notStatic = !victimObject->GetIsStatic(); + if ( !notStatic ) + { + CVector moveForce = point.normal*-5.0f; + victimObject->ApplyMoveForce(moveForce.x, moveForce.y, moveForce.z); + } + } + } + + break; + } + default: break; + } + } + + switch ( victim->GetType() ) + { + case ENTITY_TYPE_BUILDING: + { + PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_1, point.point); + break; + } + case ENTITY_TYPE_VEHICLE: + { + if (!statUpdated) { + CStats::BulletsThatHit++; + statUpdated = true; + } + DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_VEHICLE, 1.0f); + break; + } + case ENTITY_TYPE_PED: + { + if (!statUpdated) { + CStats::BulletsThatHit++; + statUpdated = true; + } + DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_PED, 1.0f); + ((CPed*)victim)->Say(SOUND_PED_BULLET_HIT); + break; + } + case ENTITY_TYPE_OBJECT: + { + if (!statUpdated) { + CStats::BulletsThatHit++; + statUpdated = true; + } + PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_2, point.point); + break; + } + case ENTITY_TYPE_DUMMY: + { + PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_3, point.point); + break; + } + default: break; + } + } + else + { + CVector traceTarget = *fireSource; + traceTarget += (target - (*fireSource)) * Min(info->m_fRange, 30.0f) / info->m_fRange; + CBulletTraces::AddTrace(fireSource, &traceTarget, m_eWeaponType, shooter); + } + } + + if ( shooter == FindPlayerPed() ) + CPad::GetPad(0)->StartShake_Distance(240, 128, FindPlayerPed()->GetPosition().x, FindPlayerPed()->GetPosition().y, FindPlayerPed()->GetPosition().z); + + return true; +} + +bool +CWeapon::FireProjectile(CEntity *shooter, CVector *fireSource, float power) +{ + ASSERT(shooter!=nil); + ASSERT(fireSource!=nil); + + CVector source, target; + eWeaponType projectileType = m_eWeaponType; + + if ( m_eWeaponType == WEAPONTYPE_ROCKETLAUNCHER ) + { + source = *fireSource; + projectileType = WEAPONTYPE_ROCKET; + + if ( shooter->IsPed() && ((CPed*)shooter)->IsPlayer() ) + { + int16 mode = TheCamera.Cams[TheCamera.ActiveCam].Mode; + if (!( mode == CCam::MODE_M16_1STPERSON + || mode == CCam::MODE_SNIPER + || mode == CCam::MODE_ROCKETLAUNCHER + || mode == CCam::MODE_M16_1STPERSON_RUNABOUT + || mode == CCam::MODE_SNIPER_RUNABOUT + || mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT) ) + { + return false; + } + + *fireSource += TheCamera.Cams[TheCamera.ActiveCam].Front; + } + else + *fireSource += shooter->GetForward(); + + target = *fireSource; + } + else + { + float dot = DotProduct(*fireSource-shooter->GetPosition(), shooter->GetForward()); + + if ( dot < 0.3f ) + *fireSource += (0.3f-dot) * shooter->GetForward(); + + target = *fireSource; + + if ( target.z - shooter->GetPosition().z > 0.0f ) + target += 0.6f*shooter->GetForward(); + + source = *fireSource - shooter->GetPosition(); + + source = *fireSource - DotProduct(source, shooter->GetForward()) * shooter->GetForward(); + } + + if ( !CWorld::GetIsLineOfSightClear(source, target, true, true, false, true, false, false, false) ) + { + if ( m_eWeaponType != WEAPONTYPE_GRENADE ) + CProjectileInfo::RemoveNotAdd(shooter, projectileType, *fireSource); + else + { + if ( shooter->IsPed() ) + { + source = shooter->GetPosition() - shooter->GetForward(); + source.z -= 0.4f; + + if ( !CWorld::TestSphereAgainstWorld(source, 0.5f, nil, false, false, true, false, false, false) ) + CProjectileInfo::AddProjectile(shooter, WEAPONTYPE_GRENADE, source, 0.0f); + else + CProjectileInfo::RemoveNotAdd(shooter, WEAPONTYPE_GRENADE, *fireSource); + } + } + } + else + CProjectileInfo::AddProjectile(shooter, projectileType, *fireSource, power); + + + CWorld::pIgnoreEntity = nil; + + if ( shooter->IsPed() ) + CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_PED, shooter, (CPed *)shooter, 1000); + else if ( shooter->IsVehicle() && ((CVehicle*)shooter)->pDriver ) + CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_VEHICLE, shooter, ((CVehicle*)shooter)->pDriver, 1000); + + return true; +} + +void +CWeapon::GenerateFlameThrowerParticles(CVector pos, CVector dir) +{ + dir *= 0.7f; + CParticle::AddParticle(PARTICLE_FIREBALL, pos, dir); + + dir *= 0.7f; + CParticle::AddParticle(PARTICLE_FIREBALL, pos, dir); + + dir *= 0.7f; + CParticle::AddParticle(PARTICLE_FIREBALL, pos, dir); + + dir *= 0.7f; + CParticle::AddParticle(PARTICLE_FIREBALL, pos, dir); + + dir *= 0.7f; + CParticle::AddParticle(PARTICLE_FIREBALL, pos, dir); +} + +bool +CWeapon::FireAreaEffect(CEntity *shooter, CVector *fireSource) +{ + ASSERT(shooter!=nil); + ASSERT(fireSource!=nil); + + CWeaponInfo *info = GetInfo(); + + float heading = RADTODEG(shooter->GetForward().Heading()); + + CVector source; + CVector target; + CVector dir; + + if ( shooter == FindPlayerPed() && TheCamera.Cams[0].Using3rdPersonMouseCam() ) + { +#ifdef FREE_CAM + if (CCamera::bFreeCam) { + CPlayerPed* shooterPed = (CPlayerPed*)shooter; + Find3rdPersonCamTargetVectorFromCachedVectors(info->m_fRange, *fireSource, source, target, shooterPed->m_cachedCamSource, shooterPed->m_cachedCamFront, shooterPed->m_cachedCamUp); + } + else +#endif + { + TheCamera.Find3rdPersonCamTargetVector(info->m_fRange, *fireSource, source, target); + } + float norm = (1.0f / info->m_fRange); + dir = (target - source) * norm; + } + else + { + float angle = DEGTORAD(heading); + dir = CVector(-Sin(angle)*0.5f, Cos(angle)*0.5f, 0.0f); + target = *fireSource + dir; + } + + CShotInfo::AddShot(shooter, m_eWeaponType, *fireSource, target); + CWeapon::GenerateFlameThrowerParticles(*fireSource, dir); + + if ( shooter == (CEntity *)FindPlayerPed() ) + { + for ( int32 i = 0; i < FindPlayerPed()->m_numNearPeds; i++ ) + { + if ( FindPlayerPed()->m_nearPeds[i]->CharCreatedBy == RANDOM_CHAR ) + { + if ( FindPlayerPed()->m_nearPeds[i]->IsPedInControl() && FindPlayerPed()->m_nearPeds[i]->m_nPedState != PED_FLEE_ENTITY ) + FindPlayerPed()->m_nearPeds[i]->SetFlee(shooter, 10000); + } + } + } + + return true; +} + +bool +CWeapon::LaserScopeDot(CVector *pOutPos, float *pOutSize) +{ + CWeaponInfo *info = GetInfo(); + + float range = info->m_fRange; + + CVector source, target; + CEntity *foundEnt = nil; + CColPoint foundCol; + + source = 0.5f * TheCamera.Cams[TheCamera.ActiveCam].Front + TheCamera.Cams[TheCamera.ActiveCam].Source; + target = TheCamera.Cams[TheCamera.ActiveCam].Front; + target.Normalise(); + target *= range; + target += source; + + if ( CWorld::ProcessLineOfSight(source, target, foundCol, foundEnt, true, true, true, true, false, false, false) ) + { + CVector pos = foundCol.point; + float w, h; + + if ( CSprite::CalcScreenCoors(foundCol.point, &pos, &w, &h, true) ) + { + *pOutPos = pos; + *pOutSize = w * 0.05f; + + CCoronas::RegisterCorona((uintptr)this + 7, 128, 0, 0, 255, pos, 1.2f, 50.0f, CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + + return true; + } + } + + return false; +} + +bool +CWeapon::FireSniper(CEntity *shooter) +{ + ASSERT(shooter!=nil); + + if ( (CEntity *)FindPlayerPed() == shooter ) + { + int16 mode = TheCamera.Cams[TheCamera.ActiveCam].Mode; + if (!( mode == CCam::MODE_M16_1STPERSON + || mode == CCam::MODE_SNIPER + || mode == CCam::MODE_CAMERA + || mode == CCam::MODE_ROCKETLAUNCHER + || mode == CCam::MODE_M16_1STPERSON_RUNABOUT + || mode == CCam::MODE_SNIPER_RUNABOUT + || mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT) ) + { + return false; + } + } + +#ifdef SECUROM + if (sniperPirateCheck){ + // if not pirated game + // sniperPirateCheck = 0; + } +#endif + +#ifndef FIX_BUGS + CWeaponInfo *info = GetInfo(); //unused +#endif + + CCam *cam = &TheCamera.Cams[TheCamera.ActiveCam]; + ASSERT(cam!=nil); + + CVector source = cam->Source; + CVector dir = cam->Front; + + if ( DotProduct(dir, CVector(0.0f, -0.9894f, 0.145f)) > 0.997f ) + CCoronas::MoonSize = (CCoronas::MoonSize+1) & 7; + + dir.Normalise(); + dir *= 16.0f; + +#ifdef SECUROM + if (sniperPirateCheck) return true; +#endif + + CBulletInfo::AddBullet(shooter, m_eWeaponType, source, dir); + + if ( shooter == FindPlayerPed() ) + { + CPad::GetPad(0)->StartShake_Distance(240, 128, + FindPlayerPed()->GetPosition().x, + FindPlayerPed()->GetPosition().y, + FindPlayerPed()->GetPosition().z); + + CParticle::HandleShootableBirdsStuff(shooter, source); + + CamShakeNoPos(&TheCamera, 0.2f); + } + + if ( shooter->IsPed() ) + CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_PED, shooter, (CPed*)shooter, 1000); + else if ( shooter->IsVehicle() && ((CVehicle*)shooter)->pDriver ) + CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_VEHICLE, shooter, ((CVehicle*)shooter)->pDriver, 1000); + + return true; +} + +bool +CWeapon::TakePhotograph(CEntity *shooter) +{ + if ( TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_CAMERA ) + { + CSpecialFX::bSnapShotActive = true; + CSpecialFX::SnapShotFrames = 0; + CStats::PhotosTaken++; + bPhotographHasBeenTaken = true; + + for ( int32 i = CPools::GetPedPool()->GetSize() - 1; i >= 0; i--) + { + CPed *ped = CPools::GetPedPool()->GetSlot(i); + if ( ped ) + { + if ( (ped->GetPosition() - TheCamera.GetPosition()).Magnitude() < 125.0f ) + { + CVector pedPos = ped->GetPosition(); + pedPos.z += 0.8f; + + CVector pos; + float w, h; + + if ( CSprite::CalcScreenCoors(pedPos, &pos, &w, &h, false) ) + { + if ( SCREEN_WIDTH * 0.1f < pos.x && SCREEN_WIDTH * 0.9f > pos.x + && SCREEN_HEIGHT * 0.1f < pos.y && SCREEN_HEIGHT * 0.9f > pos.y ) + { + CVector source, target; + CEntity *foundEnt = nil; + CColPoint foundCol; + + target = pedPos; + source = TheCamera.GetForward() * 2.0f + TheCamera.GetPosition(); + + if ( CWorld::ProcessLineOfSight(source, target, foundCol, foundEnt, true, true, true, true, true, true, false) ) + { + if ( foundEnt != (CEntity*)ped ) + continue; + } + + ped->bHasBeenPhotographed = true; + } + } + } + } + } + + return true; + } + + return false; +} + +bool +CWeapon::FireM16_1stPerson(CEntity *shooter) +{ + ASSERT(shooter!=nil); + + int16 mode = TheCamera.Cams[TheCamera.ActiveCam].Mode; + + if (!( mode == CCam::MODE_M16_1STPERSON + || mode == CCam::MODE_SNIPER + || mode == CCam::MODE_CAMERA + || mode == CCam::MODE_ROCKETLAUNCHER + || mode == CCam::MODE_M16_1STPERSON_RUNABOUT + || mode == CCam::MODE_SNIPER_RUNABOUT + || mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT + || mode == CCam::MODE_HELICANNON_1STPERSON) ) + { + return false; + } + + CWeaponInfo *info = GetInfo(); + + CWorld::bIncludeCarTyres = true; + CWorld::bIncludeBikers = true; + + CColPoint point; + CEntity *victim; + + CWorld::pIgnoreEntity = shooter; + CWorld::bIncludeDeadPeds = true; + + CCam *cam = &TheCamera.Cams[TheCamera.ActiveCam]; + ASSERT(cam!=nil); + + CVector source = cam->Source; + CVector target = cam->Front*info->m_fRange + source; + + if (ProcessLineOfSight(source, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, true, false)) { + CheckForShootingVehicleOccupant(&victim, &point, m_eWeaponType, source, target); + } + CWorld::pIgnoreEntity = nil; + CWorld::bIncludeDeadPeds = false; + CWorld::bIncludeBikers = false; + CWorld::bIncludeCarTyres = false; + + CVector2D front(cam->Front.x, cam->Front.y); + front.Normalise(); + + DoBulletImpact(shooter, victim, &source, &target, &point, front); + + CVector bulletPos; + + if ( CHeli::TestBulletCollision(&source, &target, &bulletPos, (m_eWeaponType == WEAPONTYPE_M60 || m_eWeaponType == WEAPONTYPE_HELICANNON ? 20 : 4)) ) + { + for ( int32 i = 0; i < 16; i++ ) + CParticle::AddParticle(PARTICLE_SPARK, bulletPos, CVector(0.0f, 0.0f, 0.0f)); + } + + if ( shooter == FindPlayerPed() ) + { + float mult; + switch (m_eWeaponType) { + case WEAPONTYPE_M4: + mult = 0.0003f; + break; + case WEAPONTYPE_RUGER: + mult = 0.00015f; + break; + case WEAPONTYPE_HELICANNON: + case WEAPONTYPE_M60: + mult = 0.0003f; + break; + default: + mult = 0.0002f; + break; + } + + if (FindPlayerPed()->bIsDucking || FindPlayerPed()->m_attachedTo) + mult *= 0.3f; + + TheCamera.Cams[TheCamera.ActiveCam].Beta += float((CGeneral::GetRandomNumber() & 127) - 64) * mult; + TheCamera.Cams[TheCamera.ActiveCam].Alpha += float((CGeneral::GetRandomNumber() & 127) - 64) * mult; + + // yes, double + double notFiringRate = (20.0 - info->m_nFiringRate) / 80.0; + double raisedNotFiringRate = Max(1.0, Max(0.0, notFiringRate)); + + uint8 shakeFreq = 80.0 * raisedNotFiringRate + 130.0; + CPad::GetPad(0)->StartShake(20000.0f * CTimer::GetTimeStep() / shakeFreq, shakeFreq); + } + + return true; +} + +bool +CWeapon::FireInstantHitFromCar(CVehicle *shooter, bool left, bool right) +{ + CWeaponInfo *info = GetInfo(); + + CVehicleModelInfo *modelInfo = shooter->GetModelInfo(); + + CVector source, target; + + if ( shooter->IsBike() ) + { + if ( shooter->pDriver ) + { + source = info->m_vecFireOffset; + + shooter->pDriver->TransformToNode(source, PED_HANDR); + source += CTimer::GetTimeStep() * shooter->m_vecMoveSpeed; + + if ( left ) + target = source - info->m_fRange * shooter->GetRight(); + else if ( right ) + target = source + info->m_fRange * shooter->GetRight(); + else + target = source + info->m_fRange * shooter->GetForward(); + + } + else if ( left ) + { + source = shooter->GetMatrix() * CVector(-shooter->GetColModel()->boundingBox.max.x + -0.25f, + float(CGeneral::GetRandomNumber() & 255) * 0.001f + modelInfo->GetFrontSeatPosn().y - 0.05f, + modelInfo->GetFrontSeatPosn().z + 0.63f); + source += CTimer::GetTimeStep() * shooter->m_vecMoveSpeed; + + + target = shooter->GetMatrix() * CVector(-info->m_fRange, + modelInfo->GetFrontSeatPosn().y, + modelInfo->GetFrontSeatPosn().z + 0.6f); + } + else if ( right ) + { + source = shooter->GetMatrix() * CVector(shooter->GetColModel()->boundingBox.max.x + 0.25f, + float(CGeneral::GetRandomNumber() & 255) * 0.001f + modelInfo->GetFrontSeatPosn().y - 0.18f, + modelInfo->GetFrontSeatPosn().z + 0.52f); + source += CTimer::GetTimeStep() * shooter->m_vecMoveSpeed; + + target = shooter->GetMatrix() * CVector(info->m_fRange, + modelInfo->GetFrontSeatPosn().y, + modelInfo->GetFrontSeatPosn().z + 0.5f); + } + else + { + source = shooter->GetMatrix() * CVector(float(CGeneral::GetRandomNumber() & 255) * 0.001f + -0.4f, + modelInfo->GetFrontSeatPosn().y + shooter->GetColModel()->boundingBox.max.y + 0.2f, + modelInfo->GetFrontSeatPosn().z + 0.55f); + source += CTimer::GetTimeStep() * shooter->m_vecMoveSpeed; + + target = shooter->GetMatrix() * CVector(0.0f, + info->m_fRange, + modelInfo->GetFrontSeatPosn().z + 0.5f); + } + } + else + { + if ( left ) + source = info->m_vecFireOffset; + else + { + source = 1.8f * info->m_vecFireOffset; + source.z -= 0.1f; + } + + shooter->pDriver->TransformToNode(source, PED_HANDR); + source += CTimer::GetTimeStep() * shooter->m_vecMoveSpeed; + + if ( left ) + target = source - info->m_fRange * shooter->GetRight(); + else + target = source + info->m_fRange * shooter->GetRight(); + } + + target += CVector(float(CGeneral::GetRandomNumber()&255)*0.01f-1.28f, + float(CGeneral::GetRandomNumber()&255)*0.01f-1.28f, + float(CGeneral::GetRandomNumber()&255)*0.01f-1.28f); + + DoDriveByAutoAiming(FindPlayerPed(), shooter, &source, &target); + + CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_PED, FindPlayerPed(), FindPlayerPed(), 1000); + + if ( !TheCamera.GetLookingLRBFirstPerson() ) + { + if ( !shooter->IsBike() ) + CParticle::AddParticle(PARTICLE_GUNFLASH, source, CVector(0.0f, 0.0f, 0.0f)); + else + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, source, 1.4f*shooter->m_vecMoveSpeed); + } + else + CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, source, 1.6f*shooter->m_vecMoveSpeed, nil, 0.18f); + + CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_VEHICLE, shooter, FindPlayerPed(), 1000); + + CPointLights::AddLight(CPointLights::LIGHT_POINT, source, CVector(0.0f, 0.0f, 0.0f), 5.0f, + 1.0f, 0.8f, 0.0f, CPointLights::FOG_NONE, false); + + CColPoint point; + CEntity *victim; + + CWorld::bIncludeBikers = true; + CWorld::pIgnoreEntity = shooter; + ProcessLineOfSight(source, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, true, false); + CWorld::pIgnoreEntity = NULL; + CWorld::bIncludeBikers = false; + + if ( !(CTimer::GetFrameCounter() & 3) ) + MakePedsJumpAtShot(shooter, &source, &target); + + if ( victim ) + { + CVector traceTarget = point.point; + CBulletTraces::AddTrace(&source, &traceTarget, m_eWeaponType, shooter); + + if ( victim->IsPed() ) + { + CPed *victimPed = (CPed*)victim; + + if ( !victimPed->DyingOrDead() && victim != (CEntity *)shooter ) + { + CVector pos = victimPed->GetPosition(); + + CVector2D posOffset(source.x-pos.x, source.y-pos.y); + int32 localDir = victimPed->GetLocalDirection(posOffset); + + victimPed->ReactToAttack(FindPlayerPed()); + victimPed->ClearAttackByRemovingAnim(); + + CAnimBlendAssociation *asoc = CAnimManager::AddAnimation(victimPed->GetClump(), ASSOCGRP_STD, AnimationId(ANIM_STD_HITBYGUN_FRONT + localDir)); + ASSERT(asoc!=nil); + asoc->blendAmount = 0.0f; + asoc->blendDelta = 8.0f; + + victimPed->InflictDamage(shooter, WEAPONTYPE_UZI_DRIVEBY, 3*info->m_nDamage, (ePedPieceTypes)point.pieceB, localDir); + + pos.z += 0.8f; + + if ( victimPed->GetIsOnScreen() ) + { + if ( CGame::nastyGame ) + { + for ( int32 i = 0; i < 4; i++ ) + { + CVector dir; + dir.x = CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); + dir.y = CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); + dir.z = CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); + + CParticle::AddParticle(PARTICLE_BLOOD, pos, dir); + } + } + } + + if ( victimPed->m_nPedType == PEDTYPE_COP ) + CEventList::RegisterEvent(EVENT_SHOOT_COP, EVENT_ENTITY_PED, victimPed, FindPlayerPed(), 10000); + else + CEventList::RegisterEvent(EVENT_SHOOT_PED, EVENT_ENTITY_PED, victimPed, FindPlayerPed(), 10000); + } + } + else if ( victim->IsVehicle() ) + ((CVehicle *)victim)->InflictDamage(FindPlayerPed(), WEAPONTYPE_UZI_DRIVEBY, info->m_nDamage); + else + CGlass::WasGlassHitByBullet(victim, point.point); + + switch ( victim->GetType() ) + { + case ENTITY_TYPE_BUILDING: + { + PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_1, point.point); + break; + } + case ENTITY_TYPE_VEHICLE: + { + DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_VEHICLE, 1.0f); + break; + } + case ENTITY_TYPE_PED: + { + DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_PED, 1.0f); + ((CPed*)victim)->Say(SOUND_PED_BULLET_HIT); + break; + } + case ENTITY_TYPE_OBJECT: + { + PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_2, point.point); + break; + } + case ENTITY_TYPE_DUMMY: + { + PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_3, point.point); + break; + } + default: break; + } + } + else + { + float norm = 30.0f/info->m_fRange; + CVector traceTarget = (target-source)*norm + source; + CBulletTraces::AddTrace(&source, &traceTarget, m_eWeaponType, shooter); + } + + if ( shooter == FindPlayerVehicle() ) + CPad::GetPad(0)->StartShake_Distance(240, 128, FindPlayerVehicle()->GetPosition().x, FindPlayerVehicle()->GetPosition().y, FindPlayerVehicle()->GetPosition().z); + + return true; +} + +void +CWeapon::DoDoomAiming(CEntity *shooter, CVector *source, CVector *target) +{ + ASSERT(shooter!=nil); + ASSERT(source!=nil); + ASSERT(target !=nil); + +#ifndef FIX_BUGS + CEntity entity; // unused +#endif + + CPed *shooterPed = (CPed*)shooter; + + int16 lastEntity; + CEntity *entities[16]; + CWorld::FindObjectsInRange(*source, (*target-*source).Magnitude(), true, &lastEntity, 15, entities, false, true, true, false, false); + + float closestEntityDist = 10000.0f; + int16 closestEntity; + + for ( int32 i = 0; i < lastEntity; i++ ) + { + CEntity *victim = entities[i]; + ASSERT(victim!=nil); + + if ( (CEntity*)shooterPed != victim && shooterPed->CanSeeEntity(victim, DEGTORAD(22.5f)) ) + { + if ( !(victim->GetStatus() == STATUS_TRAIN_MOVING + || victim->GetStatus() == STATUS_TRAIN_NOT_MOVING + || victim->GetStatus() == STATUS_HELI + || victim->GetStatus() == STATUS_PLANE + || victim->GetStatus() == STATUS_WRECKED) ) + { + float distToVictim = (shooterPed->GetPosition()-victim->GetPosition()).Magnitude2D(); + float distToVictimZ = Abs(shooterPed->GetPosition().z-victim->GetPosition().z); + + if ( 1.5f*distToVictimZ < distToVictim ) + { + float entityDist = Sqrt(SQR(distToVictim) + SQR(distToVictimZ)); + + if ( entityDist < closestEntityDist ) + { + closestEntityDist = entityDist; + closestEntity = i; + } + } + } + } + } + + CColPoint foundCol; + CEntity *foundEnt; + if (closestEntityDist < DOOMAUTOAIMING_MAXDIST + && !CWorld::ProcessLineOfSight(*source, entities[closestEntity]->GetPosition(), foundCol, foundEnt, true, false, false, false, false, false, false, true)) + { + CEntity *victim = entities[closestEntity]; + ASSERT(victim !=nil); + + float distToTarget = (*target - *source).Magnitude2D(); + float distToSource = (victim->GetPosition() - *source).Magnitude2D(); + + float victimZ = victim->GetPosition().z + 0.3f; + if ( victim->IsPed() ) + { + if ( ((CPed*)victim)->bIsDucking ) + victimZ -= 0.8f; + } + + (*target).z = (distToTarget / distToSource) * (victimZ - (*source).z) + (*source).z; + } +} + +void +CWeapon::DoTankDoomAiming(CEntity *shooter, CEntity *driver, CVector *source, CVector *target) +{ + ASSERT(shooter!=nil); + ASSERT(driver!=nil); + ASSERT(source!=nil); + ASSERT(target!=nil); + +#ifndef FIX_BUGS + CEntity entity; // unused +#endif + + int16 lastEntity; + CEntity *entities[16]; + CWorld::FindObjectsInRange(*source, (*target-*source).Magnitude(), true, &lastEntity, 15, entities, false, true, false, false, false); + + float closestEntityDist = 10000.0f; + int16 closestEntity; + + float normZ = (target->z - source->z) / (*target-*source).Magnitude(); + + for ( int32 i = 0; i < lastEntity; i++ ) + { + CEntity *victim = entities[i]; + + ASSERT(victim!=nil); + + if ( shooter != victim && driver != victim ) + { + if ( !(victim->GetStatus() == STATUS_TRAIN_MOVING + || victim->GetStatus() == STATUS_TRAIN_NOT_MOVING + || victim->GetStatus() == STATUS_HELI + || victim->GetStatus() == STATUS_PLANE) ) + { + if ( !(victim->IsVehicle() && victim->bRenderScorched) ) + { + float distToVictim = (shooter->GetPosition()-victim->GetPosition()).Magnitude2D(); + float distToVictimZ = Abs(shooter->GetPosition().z - (distToVictim*normZ + victim->GetPosition().z)); + + if ( 3.0f*distToVictimZ < distToVictim ) + { + CVector tmp = CVector(victim->GetPosition().x, victim->GetPosition().y, 0.0f); + if ( CCollision::DistToLine(source, target, + &tmp) < victim->GetBoundRadius()*3.0f ) + { + float vehicleDist = Sqrt(SQR(distToVictim) + SQR(distToVictimZ)); + if ( vehicleDist < closestEntityDist ) + { + closestEntityDist = vehicleDist; + closestEntity = i; + } + } + } + } + } + } + } + + if ( closestEntityDist < DOOMAUTOAIMING_MAXDIST ) + { + CEntity *victim = entities[closestEntity]; + ASSERT(victim!=nil); + + float distToTarget = (*target - *source).Magnitude2D(); + float distToSource = (victim->GetPosition() - *source).Magnitude2D(); + + (*target).z = (distToTarget / distToSource) * (0.3f + victim->GetPosition().z - (*source).z) + (*source).z; + } +} + + +void +CWeapon::DoDriveByAutoAiming(CEntity *driver, CVehicle *vehicle, CVector *source, CVector *target) +{ + ASSERT(driver!=nil); + ASSERT(source!=nil); + ASSERT(target!=nil); + +#ifndef FIX_BUGS + CEntity entity; // unused +#endif + + CPed *shooterPed = (CPed*)driver; + + int16 lastEntity; + CEntity *peds[16]; + CWorld::FindObjectsInRange(*source, (*target-*source).Magnitude(), true, &lastEntity, 15, peds, false, false, true, false, false); + + float closestEntityDist = 10000.0f; + int16 closestEntity; + + for ( int32 i = 0; i < lastEntity; i++ ) + { + CPed *victim = (CPed*)peds[i]; + ASSERT(victim!=nil); + + if (driver != victim && !victim->DyingOrDead() && victim->m_attachedTo != vehicle) + { + float lineDist = CCollision::DistToLine(source, target, &victim->GetPosition()); + + uint32 model = vehicle->GetModelIndex(); + float pedDist; + if (model == MI_HUNTER || model == MI_SEASPAR || model == MI_SPARROW) + { + float distToVictim = (victim->GetPosition() - vehicle->GetPosition()).Magnitude(); + pedDist = lineDist / Max(5.f, distToVictim); + } + else + { + float distToVictim = (victim->GetPosition() - driver->GetPosition()).Magnitude(); + pedDist = 0.15f * distToVictim + lineDist; + } + + if ( DotProduct((*target-*source), victim->GetPosition()-*source) > 0.0f && pedDist < closestEntityDist) + { + closestEntity = i; + closestEntityDist = pedDist; + } + } + } + uint32 model = vehicle->GetModelIndex(); + float maxAimDistance = CAR_DRIVEBYAUTOAIMING_MAXDIST; + if (model == MI_HUNTER) + { + maxAimDistance = Tan(DEGTORAD(fHunterAimingAngle)); + } + else if (model == MI_SEASPAR || model == MI_SPARROW) + { + maxAimDistance = Tan(DEGTORAD(fSeaSparrowAimingAngle)); + } + + if ( closestEntityDist < maxAimDistance ) + { + CEntity *victim = peds[closestEntity]; + ASSERT(victim!=nil); + + float distToTarget = (*source - *target).Magnitude(); + float distToSource = (*source - victim->GetPosition()).Magnitude(); + *target = (distToTarget / distToSource) * (victim->GetPosition() - *source) + *source; + } +} + +void +CWeapon::Reload(void) +{ + if (m_nAmmoTotal == 0) + return; + + CWeaponInfo *info = GetInfo(); + + if (m_nAmmoTotal >= info->m_nAmountofAmmunition) + m_nAmmoInClip = info->m_nAmountofAmmunition; + else + m_nAmmoInClip = m_nAmmoTotal; +} + +void +CWeapon::Update(int32 audioEntity, CPed *pedToAdjustSound) +{ + CWeaponInfo *info = GetInfo(); + + switch ( m_eWeaponState ) + { + case WEAPONSTATE_MELEE_MADECONTACT: + { + m_eWeaponState = WEAPONSTATE_READY; + break; + } + + case WEAPONSTATE_FIRING: + { + if ( IsShotgun(m_eWeaponType) && AEHANDLE_IS_OK(audioEntity) ) + { + uint32 timePassed = m_nTimer - CWeaponInfo::ms_aReloadSampleTime[m_eWeaponType]; + if ( CTimer::GetPreviousTimeInMilliseconds() < timePassed && CTimer::GetTimeInMilliseconds() >= timePassed ) + DMAudio.PlayOneShot(audioEntity, SOUND_WEAPON_RELOAD, 0.0f); + } + + if ( CTimer::GetTimeInMilliseconds() > m_nTimer ) + { + if ( GetInfo()->m_eWeaponFire != WEAPON_FIRE_MELEE && m_nAmmoTotal == 0 ) { + m_eWeaponState = WEAPONSTATE_OUT_OF_AMMO; + CPickups::RemoveAllPickupsOfACertainWeaponGroupWithNoAmmo(m_eWeaponType); + } else + m_eWeaponState = WEAPONSTATE_READY; + } + + break; + } + + case WEAPONSTATE_RELOADING: + { + if ( AEHANDLE_IS_OK(audioEntity) && m_eWeaponType < WEAPONTYPE_TOTALWEAPONS) + { + CAnimBlendAssociation *reloadAssoc = nil; + if (pedToAdjustSound) { + if (CPed::GetReloadAnim(info) && (!CWorld::Players[CWorld::PlayerInFocus].m_bFastReload || !pedToAdjustSound->IsPlayer())) { + reloadAssoc = RpAnimBlendClumpGetAssociation(pedToAdjustSound->GetClump(), CPed::GetReloadAnim(info)); + if (!reloadAssoc) { + reloadAssoc = RpAnimBlendClumpGetAssociation(pedToAdjustSound->GetClump(), CPed::GetCrouchReloadAnim(info)); + } + } + } + if (reloadAssoc && reloadAssoc->IsRunning() && reloadAssoc->blendAmount > 0.2f) { + float soundStart = 0.75f; + switch (info->m_AnimToPlay) { + case ASSOCGRP_PYTHON: + soundStart = fReloadAnimSampleFraction[0]; + break; + case ASSOCGRP_COLT: + case ASSOCGRP_TEC: + soundStart = fReloadAnimSampleFraction[1]; + break; + case ASSOCGRP_UZI: + soundStart = fReloadAnimSampleFraction[2]; + break; + case ASSOCGRP_RIFLE: + soundStart = fReloadAnimSampleFraction[3]; + break; + case ASSOCGRP_M60: + soundStart = fReloadAnimSampleFraction[4]; + break; + default: + break; + } + if (reloadAssoc->GetProgress() >= soundStart && (reloadAssoc->currentTime - reloadAssoc->timeStep) / reloadAssoc->hierarchy->totalLength < soundStart) + DMAudio.PlayOneShot(audioEntity, SOUND_WEAPON_RELOAD, m_eWeaponType); + if (CTimer::GetTimeInMilliseconds() > m_nTimer && reloadAssoc->GetProgress() < 0.9f) { + m_nTimer = CTimer::GetTimeInMilliseconds(); + } + } else { + uint32 timePassed = m_nTimer - CWeaponInfo::ms_aReloadSampleTime[m_eWeaponType]; + if (CTimer::GetPreviousTimeInMilliseconds() < timePassed && CTimer::GetTimeInMilliseconds() >= timePassed) + DMAudio.PlayOneShot(audioEntity, SOUND_WEAPON_RELOAD, m_eWeaponType); + } + } + + if ( CTimer::GetTimeInMilliseconds() > m_nTimer ) + { + Reload(); + m_eWeaponState = WEAPONSTATE_READY; + } + + break; + } + default: break; + } +} + +void +FireOneInstantHitRound(CVector *source, CVector *target, int32 damage) +{ + ASSERT(source!=nil); + ASSERT(target!=nil); + + CParticle::AddParticle(PARTICLE_GUNFLASH, *source, CVector(0.0f, 0.0f, 0.0f)); + + CPointLights::AddLight(CPointLights::LIGHT_POINT, + *source, CVector(0.0f, 0.0f, 0.0f), 5.0f, + 1.0f, 0.8f, 0.0f, CPointLights::FOG_NONE, false); + + CColPoint point; + CEntity *victim; + CWorld::ProcessLineOfSight(*source, *target, point, victim, true, true, true, true, true, true, false); + + CParticle::AddParticle(PARTICLE_HELI_ATTACK, *source, ((*target) - (*source)) * 0.15f); + + if ( victim ) + { + if ( victim->IsPed() ) + { + CPed *victimPed = (CPed *)victim; + if ( !victimPed->DyingOrDead() ) + { + CVector pos = victimPed->GetPosition(); + + CVector2D posOffset((*source).x-pos.x, (*source).y-pos.y); + int32 localDir = victimPed->GetLocalDirection(posOffset); + + victimPed->ClearAttackByRemovingAnim(); + + CAnimBlendAssociation *asoc = CAnimManager::AddAnimation(victimPed->GetClump(), ASSOCGRP_STD, AnimationId(ANIM_STD_HITBYGUN_FRONT + localDir)); + ASSERT(asoc!=nil); + asoc->blendAmount = 0.0f; + asoc->blendDelta = 8.0f; + + victimPed->InflictDamage(nil, WEAPONTYPE_UZI, damage, (ePedPieceTypes)point.pieceB, localDir); + + pos.z += 0.8f; + + if ( victimPed->GetIsOnScreen() ) + { + if ( CGame::nastyGame ) + { + for ( int32 i = 0; i < 4; i++ ) + { + CVector dir; + dir.x = CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); + dir.y = CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); + dir.z = CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); + + CParticle::AddParticle(PARTICLE_BLOOD, pos, dir); + } + } + } + } + } + else if ( victim->IsVehicle() ) + ((CVehicle *)victim)->InflictDamage(nil, WEAPONTYPE_UZI, damage); + //BUG ? no CGlass::WasGlassHitByBullet + + switch ( victim->GetType() ) + { + case ENTITY_TYPE_BUILDING: + { + PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_1, point.point); + CParticle::AddParticle(PARTICLE_SMOKE, point.point, CVector(0.0f, 0.0f, 0.01f)); + break; + } + case ENTITY_TYPE_VEHICLE: + { + CStats::BulletsThatHit++; + DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_VEHICLE, 1.0f); + break; + } + case ENTITY_TYPE_PED: + { + CStats::BulletsThatHit++; + DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_PED, 1.0f); + ((CPed*)victim)->Say(SOUND_PED_BULLET_HIT); + break; + } + case ENTITY_TYPE_OBJECT: + { + CStats::BulletsThatHit++; + PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_2, point.point); + break; + } + case ENTITY_TYPE_DUMMY: + { + PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_3, point.point); + break; + } + default: break; + } + } + else + { + float waterLevel; + if ( CWaterLevel::GetWaterLevel((*target).x, (*target).y, (*target).z + 10.0f, &waterLevel, false) ) + { + CParticle::AddParticle(PARTICLE_BOAT_SPLASH, CVector((*target).x, (*target).y, waterLevel), CVector(0.0f, 0.0f, 0.01f)); + PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_WATER, point.point); // no sound(empty) + } + } +} + +bool +CWeapon::IsTypeMelee(void) +{ + return CWeaponInfo::GetWeaponInfo(m_eWeaponType)->m_eWeaponFire == WEAPON_FIRE_MELEE; +} + +bool +CWeapon::IsType2Handed(void) +{ + return m_eWeaponType == WEAPONTYPE_FLAMETHROWER || m_eWeaponType == WEAPONTYPE_HELICANNON || m_eWeaponType == WEAPONTYPE_M60 || + m_eWeaponType == WEAPONTYPE_M4 || IsShotgun(m_eWeaponType) || + m_eWeaponType == WEAPONTYPE_RUGER || m_eWeaponType == WEAPONTYPE_SNIPERRIFLE || m_eWeaponType == WEAPONTYPE_LASERSCOPE; +} + +void +CWeapon::MakePedsJumpAtShot(CPhysical *shooter, CVector *source, CVector *target) +{ + ASSERT(shooter!=nil); + ASSERT(source!=nil); + ASSERT(target!=nil); + + float minx = Min(source->x, target->x) - 2.0f; + float maxx = Max(source->x, target->x) + 2.0f; + float miny = Min(source->y, target->y) - 2.0f; + float maxy = Max(source->y, target->y) + 2.0f; + float minz = Min(source->z, target->z) - 2.0f; + float maxz = Max(source->z, target->z) + 2.0f; + + for ( int32 i = CPools::GetPedPool()->GetSize() - 1; i >= 0; i--) + { + CPed *ped = CPools::GetPedPool()->GetSlot(i); + + if ( ped ) + { + if ( ped->GetPosition().x > minx && ped->GetPosition().x < maxx + && ped->GetPosition().y > miny && ped->GetPosition().y < maxy + && ped->GetPosition().z > minz && ped->GetPosition().z < maxz ) + { + if ( ped != FindPlayerPed() && !((uint8)(ped->m_randomSeed ^ CGeneral::GetRandomNumber()) & 31) ) + ped->SetEvasiveDive(shooter, 1); + } + } + } +} + +bool +CWeapon::HitsGround(CEntity *holder, CVector *fireSource, CEntity *aimingTo) +{ + ASSERT(holder!=nil); + ASSERT(aimingTo!=nil); + + if (!holder->IsPed() || !((CPed*)holder)->m_pSeekTarget) + return false; + + CWeaponInfo *info = GetInfo(); + + CVector adjustedOffset = info->m_vecFireOffset; + adjustedOffset.z += 0.6f; + + CVector source, target; + CEntity *foundEnt = nil; + CColPoint foundCol; + + if (fireSource) + source = *fireSource; + else + source = holder->GetMatrix() * adjustedOffset; + + CEntity *aimEntity = aimingTo ? aimingTo : ((CPed*)holder)->m_pSeekTarget; + ASSERT(aimEntity!=nil); + + target = aimEntity->GetPosition(); + target.z += 0.6f; + + CWorld::ProcessLineOfSight(source, target, foundCol, foundEnt, true, false, false, false, false, false, false); + if (foundEnt && foundEnt->IsBuilding()) { + // That was supposed to be Magnitude, according to leftover code in assembly + float diff = (foundCol.point.z - source.z); + if (diff < 0.0f && diff > -3.0f) + return true; + } + + return false; +} + +void +CWeapon::BlowUpExplosiveThings(CEntity *thing) +{ +#ifdef FIX_BUGS + if ( thing && thing->IsObject() ) +#else + if ( thing ) +#endif + { + CObject *object = (CObject*)thing; + int32 mi = object->GetModelIndex(); + if ( IsExplosiveThingModel(mi) && !object->bHasBeenDamaged && object->IsObject() ) + { + object->bHasBeenDamaged = true; + + CExplosion::AddExplosion(object, FindPlayerPed(), EXPLOSION_BARREL, object->GetPosition()+CVector(0.0f,0.0f,0.5f), 100); + + if ( MI_EXPLODINGBARREL == mi ) + object->m_vecMoveSpeed.z += 0.55f; + else + object->m_vecMoveSpeed.z += 0.45f; + + object->m_vecMoveSpeed.x += float((CGeneral::GetRandomNumber()&255) - 128) * 0.0002f; + object->m_vecMoveSpeed.y += float((CGeneral::GetRandomNumber()&255) - 128) * 0.0002f; + + if ( object->GetIsStatic()) + { + object->SetIsStatic(false); + object->AddToMovingList(); + } + } + } +} + +bool +CWeapon::HasWeaponAmmoToBeUsed(void) +{ + // FIX: This is better (not bug tho) +//#if 0 + if (m_eWeaponType <= WEAPONTYPE_CHAINSAW) +//#else +// if (CWeaponInfo::GetWeaponInfo(m_eWeaponType)->m_eWeaponFire == WEAPON_FIRE_MELEE) +//#endif + return true; + else + return m_nAmmoTotal != 0; +} + +bool +CPed::IsPedDoingDriveByShooting(void) +{ +#ifdef FIX_BUGS + if (FindPlayerPed() == this && CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_nWeaponSlot == WEAPONSLOT_SUBMACHINEGUN) { +#else + if (FindPlayerPed() == this && GetWeapon()->m_eWeaponType == WEAPONTYPE_UZI) { +#endif + if (TheCamera.Cams[TheCamera.ActiveCam].LookingLeft || TheCamera.Cams[TheCamera.ActiveCam].LookingRight) + return true; + } + return false; +} + +bool +CWeapon::ProcessLineOfSight(CVector const &point1, CVector const &point2, CColPoint &point, CEntity *&entity, eWeaponType type, CEntity *shooter, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects) +{ + return CWorld::ProcessLineOfSight(point1, point2, point, entity, checkBuildings, checkVehicles, checkPeds, checkObjects, checkDummies, false, ignoreSomeObjects, true); +} + + +void +CWeapon::CheckForShootingVehicleOccupant(CEntity **victim, CColPoint *point, eWeaponType weapon, CVector const& source, CVector const& target) +{ + if (!(*victim)->IsVehicle()) + return; + + CColSphere headSphere; + + CVehicle *veh = (CVehicle*)*victim; + CColPoint origPoint(*point); + float radius = 1.0f; + bool found = false; + CColLine shootLine(source, target); + + if (veh->pDriver && veh->pDriver->bCanBeShotInVehicle) { + CVector pos(0.f, 0.f, 0.f); + veh->pDriver->TransformToNode(pos, PED_HEAD); + headSphere.Set(0.2f, pos + CVector(0.f, 0.f, 0.1f), 0, PEDPIECE_HEAD); + if (CCollision::ProcessLineSphere(shootLine, headSphere, *point, radius)) { + *victim = veh->pDriver; + found = true; + } + } + + for(int i = 0; i < ARRAY_SIZE(veh->pPassengers); i++) { + CPed *passenger = veh->pPassengers[i]; + if (passenger && passenger->bCanBeShotInVehicle) { + CVector pos(0.f, 0.f, 0.f); + passenger->TransformToNode(pos, PED_HEAD); + headSphere.Set(0.2f, pos + CVector(0.f, 0.f, 0.1f), 0, PEDPIECE_HEAD); + if (CCollision::ProcessLineSphere(shootLine, headSphere, *point, radius)) { + *victim = passenger; + found = true; + } + } + } + if (veh->IsCar()) { + CVector distVec = target - source; + if (DotProduct(distVec, veh->GetForward()) < 0.0f && DotProduct(distVec, veh->GetUp()) <= 0.0f) { + CColModel *colModel = veh->GetColModel(); + if (colModel->numTriangles > 0) { + bool passesGlass = false; + CMatrix invVehMat; + Invert(veh->GetMatrix(), invVehMat); + shootLine.p0 = invVehMat * shootLine.p0; + shootLine.p1 = invVehMat * shootLine.p1; + CCollision::CalculateTrianglePlanes(colModel); + for (int i = 0; i < colModel->numTriangles; i++) { + if (colModel->triangles[i].surface == SURFACE_GLASS && + CCollision::TestLineTriangle(shootLine, colModel->vertices, colModel->triangles[i], colModel->trianglePlanes[i])) { + passesGlass = true; + break; + } + } + CAutomobile *car = (CAutomobile*)veh; + + // No need to damage windscreen if there isn't one. + if (passesGlass && car->Damage.ProgressPanelDamage(VEHPANEL_WINDSCREEN)) { + if (car->Damage.GetPanelStatus(VEHPANEL_WINDSCREEN) == PANEL_STATUS_SMASHED2) + car->Damage.ProgressPanelDamage(VEHPANEL_WINDSCREEN); + + car->SetPanelDamage(CAR_WINDSCREEN, VEHPANEL_WINDSCREEN, true); + DMAudio.PlayOneShot(veh->m_audioEntityId, SOUND_CAR_WINDSHIELD_CRACK, 0.f); + } + } + } + } + + if (!found) { + *victim = veh; + *point = origPoint; + } +} + +#ifdef COMPATIBLE_SAVES +#define CopyFromBuf(buf, data) memcpy(&data, buf, sizeof(data)); SkipSaveBuf(buf, sizeof(data)); +#define CopyToBuf(buf, data) memcpy(buf, &data, sizeof(data)); SkipSaveBuf(buf, sizeof(data)); +void +CWeapon::Save(uint8*& buf) +{ + CopyToBuf(buf, m_eWeaponType); + CopyToBuf(buf, m_eWeaponState); + CopyToBuf(buf, m_nAmmoInClip); + CopyToBuf(buf, m_nAmmoTotal); + CopyToBuf(buf, m_nTimer); + CopyToBuf(buf, m_bAddRotOffset); + ZeroSaveBuf(buf, 3); +} + +void +CWeapon::Load(uint8*& buf) +{ + CopyFromBuf(buf, m_eWeaponType); + CopyFromBuf(buf, m_eWeaponState); + CopyFromBuf(buf, m_nAmmoInClip); + CopyFromBuf(buf, m_nAmmoTotal); + CopyFromBuf(buf, m_nTimer); + CopyFromBuf(buf, m_bAddRotOffset); + SkipSaveBuf(buf, 3); +} + +#undef CopyFromBuf +#undef CopyToBuf +#endif diff --git a/src/miami/weapons/Weapon.h b/src/miami/weapons/Weapon.h new file mode 100644 index 00000000..f720b312 --- /dev/null +++ b/src/miami/weapons/Weapon.h @@ -0,0 +1,90 @@ +#pragma once + +#include "WeaponType.h" + +#define CAR_DRIVEBYAUTOAIMING_MAXDIST (2.5f) +#define DOOMAUTOAIMING_MAXDIST (9000.0f) + +class CEntity; +class CPhysical; +class CVehicle; +class CPed; +struct CColPoint; +class CWeaponInfo; + +class CWeapon +{ +public: + eWeaponType m_eWeaponType; + eWeaponState m_eWeaponState; + int32 m_nAmmoInClip; + int32 m_nAmmoTotal; + uint32 m_nTimer; + bool m_bAddRotOffset; + + static bool bPhotographHasBeenTaken; + + CWeapon() { + m_bAddRotOffset = false; + } + CWeapon(eWeaponType type, int32 ammo); + + CWeaponInfo *GetInfo(); + + static void InitialiseWeapons(void); + static void ShutdownWeapons (void); + static void UpdateWeapons (void); + + void Initialise(eWeaponType type, int32 ammo); + void Shutdown(); + + bool Fire (CEntity *shooter, CVector *fireSource); + bool FireFromCar (CVehicle *shooter, bool left, bool right); + bool FireMelee (CEntity *shooter, CVector &fireSource); + bool FireInstantHit(CEntity *shooter, CVector *fireSource); + + static void AddGunFlashBigGuns(CVector start, CVector end); + void AddGunshell (CEntity *shooter, CVector const &source, CVector2D const &direction, float size); + void DoBulletImpact(CEntity *shooter, CEntity *victim, CVector *source, CVector *target, CColPoint *point, CVector2D ahead); + + bool FireShotgun (CEntity *shooter, CVector *fireSource); + bool FireProjectile(CEntity *shooter, CVector *fireSource, float power); + + static void GenerateFlameThrowerParticles(CVector pos, CVector dir); + + bool FireAreaEffect (CEntity *shooter, CVector *fireSource); + bool LaserScopeDot (CVector *pOutPos, float *pOutSize); + bool FireSniper (CEntity *shooter); + bool TakePhotograph (CEntity *shooter); + bool FireM16_1stPerson (CEntity *shooter); + bool FireInstantHitFromCar(CVehicle *shooter, bool left, bool right); + + static void DoDoomAiming (CEntity *shooter, CVector *source, CVector *target); + static void DoTankDoomAiming (CEntity *shooter, CEntity *driver, CVector *source, CVector *target); + static void DoDriveByAutoAiming(CEntity *driver, CVehicle *vehicle, CVector *source, CVector *target); + + void Reload(void); + void Update(int32 audioEntity, CPed *pedToAdjustSound); + bool IsTypeMelee (void); + bool IsType2Handed(void); + + static void MakePedsJumpAtShot(CPhysical *shooter, CVector *source, CVector *target); + + bool HitsGround(CEntity *holder, CVector *fireSource, CEntity *aimingTo); + static void BlowUpExplosiveThings(CEntity *thing); + bool HasWeaponAmmoToBeUsed(void); + + static bool IsShotgun(int weapon) { return weapon == WEAPONTYPE_SHOTGUN || weapon == WEAPONTYPE_SPAS12_SHOTGUN || weapon == WEAPONTYPE_STUBBY_SHOTGUN; } + + static bool ProcessLineOfSight(CVector const &point1, CVector const &point2, CColPoint &point, CEntity *&entity, eWeaponType type, CEntity *shooter, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects); + + static void CheckForShootingVehicleOccupant(CEntity **victim, CColPoint *point, eWeaponType weapon, CVector const& source, CVector const& target); + +#ifdef COMPATIBLE_SAVES + void Save(uint8*& buf); + void Load(uint8*& buf); +#endif +}; +VALIDATE_SIZE(CWeapon, 0x18); + +void FireOneInstantHitRound(CVector *source, CVector *target, int32 damage); \ No newline at end of file diff --git a/src/miami/weapons/WeaponEffects.cpp b/src/miami/weapons/WeaponEffects.cpp new file mode 100644 index 00000000..bb95ea85 --- /dev/null +++ b/src/miami/weapons/WeaponEffects.cpp @@ -0,0 +1,133 @@ +#include "common.h" + +#include "main.h" +#include "WeaponEffects.h" +#include "TxdStore.h" +#include "Sprite.h" +#include "PlayerPed.h" +#include "World.h" +#include "WeaponType.h" + +RwTexture *gpCrossHairTex; + +CWeaponEffects gCrossHair; + +CWeaponEffects::CWeaponEffects() +{ + +} + +CWeaponEffects::~CWeaponEffects() +{ + +} + +void +CWeaponEffects::Init(void) +{ + gCrossHair.m_bActive = false; + gCrossHair.m_vecPos = CVector(0.0f, 0.0f, 0.0f); + gCrossHair.m_nRed = 255; + gCrossHair.m_nGreen = 0; + gCrossHair.m_nBlue = 0; + gCrossHair.m_nAlpha = 127; + gCrossHair.m_fSize = 1.0f; + gCrossHair.m_fRotation = 0.0f; + + + CTxdStore::PushCurrentTxd(); + int32 slot = CTxdStore::FindTxdSlot("particle"); + CTxdStore::SetCurrentTxd(slot); + + gpCrossHairTex = RwTextureRead("target256", "target256m"); + + CTxdStore::PopCurrentTxd(); +} + +void +CWeaponEffects::Shutdown(void) +{ + RwTextureDestroy(gpCrossHairTex); + gpCrossHairTex = nil; +} + +void +CWeaponEffects::MarkTarget(CVector pos, uint8 red, uint8 green, uint8 blue, uint8 alpha, float size) +{ + gCrossHair.m_bActive = true; + gCrossHair.m_vecPos = pos; + gCrossHair.m_fSize = size; +} + +void +CWeaponEffects::ClearCrossHair(void) +{ + gCrossHair.m_bActive = false; +} + +void +CWeaponEffects::Render(void) +{ + static float aCrossHairSize[WEAPONTYPE_TOTALWEAPONS] = + { + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, + 0.4f, 0.4f, + 0.5f, + 0.3f, + 0.9f, 0.9f, 0.9f, + 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, + 0.1f, 0.1f, + 1.0f, + 0.6f, + 0.7f, + 0.0f, 0.0f + }; + + + + if ( gCrossHair.m_bActive ) + { + float size = aCrossHairSize[FindPlayerPed()->GetWeapon()->m_eWeaponType]; + + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); +#ifdef FIX_BUGS + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); +#else + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVDESTALPHA); +#endif + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)RwTextureGetRaster(gpCrossHairTex)); + + RwV3d pos; + float w, h; + if ( CSprite::CalcScreenCoors(gCrossHair.m_vecPos, &pos, &w, &h, true) ) + { + PUSH_RENDERGROUP("CWeaponEffects::Render"); + + float recipz = 1.0f / pos.z; + CSprite::RenderOneXLUSprite_Rotate_Aspect(pos.x, pos.y, pos.z, + w, h, + 255, 88, 100, 158, + recipz, gCrossHair.m_fRotation, gCrossHair.m_nAlpha); + + float recipz2 = 1.0f / pos.z; + + CSprite::RenderOneXLUSprite_Rotate_Aspect(pos.x, pos.y, pos.z, + size*w, size*h, + 107, 134, 247, 158, + recipz2, TWOPI - gCrossHair.m_fRotation, gCrossHair.m_nAlpha); + + gCrossHair.m_fRotation += 0.02f; + if ( gCrossHair.m_fRotation > TWOPI ) + gCrossHair.m_fRotation = 0.0; + + POP_RENDERGROUP(); + } + + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); + } +} \ No newline at end of file diff --git a/src/miami/weapons/WeaponEffects.h b/src/miami/weapons/WeaponEffects.h new file mode 100644 index 00000000..f6592b3e --- /dev/null +++ b/src/miami/weapons/WeaponEffects.h @@ -0,0 +1,26 @@ +#pragma once + +class CWeaponEffects +{ +public: + bool m_bActive; + CVector m_vecPos; + uint8 m_nRed; + uint8 m_nGreen; + uint8 m_nBlue; + uint8 m_nAlpha; + float m_fSize; + float m_fRotation; + +public: + CWeaponEffects(); + ~CWeaponEffects(); + + static void Init(void); + static void Shutdown(void); + static void MarkTarget(CVector pos, uint8 red, uint8 green, uint8 blue, uint8 alpha, float size); + static void ClearCrossHair(void); + static void Render(void); +}; + +VALIDATE_SIZE(CWeaponEffects, 0x1C); \ No newline at end of file diff --git a/src/miami/weapons/WeaponInfo.cpp b/src/miami/weapons/WeaponInfo.cpp new file mode 100644 index 00000000..1f78b7d4 --- /dev/null +++ b/src/miami/weapons/WeaponInfo.cpp @@ -0,0 +1,295 @@ +#include "common.h" + +#include "main.h" +#include "FileMgr.h" +#include "WeaponInfo.h" +#include "AnimManager.h" +#include "AnimBlendAssociation.h" +#include "Weapon.h" +#include "ModelInfo.h" +#include "ModelIndices.h" + +uint16 CWeaponInfo::ms_aReloadSampleTime[WEAPONTYPE_TOTALWEAPONS] = +{ + 0, // UNARMED + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, // GRENADE + 0, // DETONATEGRENADE + 0, // TEARGAS + 0, // MOLOTOV + 0, // ROCKET + 250, // COLT45 + 250, // PYTHON + 650, // SHOTGUN + 650, // SPAS12 SHOTGUN + 650, // STUBBY SHOTGUN + 400, // TEC9 + 400, // UZIhec + 400, // SILENCED_INGRAM + 400, // MP5 + 300, // M16 + 300, // AK47 + 423, // SNIPERRIFLE + 423, // LASERSCOPE + 400, // ROCKETLAUNCHER + 0, // FLAMETHROWER + 0, // M60 + 0, // MINIGUN + 0, // DETONATOR + 0, // HELICANNON + 0 // CAMERA +}; + +// Yeah... +int32 CWeaponInfo::ms_aMaxAmmoForWeapon[WEAPONTYPE_TOTALWEAPONS] = +{ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +CWeaponInfo aWeaponInfo[WEAPONTYPE_TOTALWEAPONS]; +char CWeaponInfo::ms_aWeaponNames[WEAPONTYPE_TOTALWEAPONS][32] = +{ + "Unarmed", + "BrassKnuckle", + "ScrewDriver", + "GolfClub", + "NightStick", + "Knife", + "BaseballBat", + "Hammer", + "Cleaver", + "Machete", + "Katana", + "Chainsaw", + "Grenade", + "DetonateGrenade", + "TearGas", + "Molotov", + "Rocket", + "Colt45", + "Python", + "Shotgun", + "Spas12Shotgun", + "StubbyShotgun", + "Tec9", + "Uzi", + "SilencedIngram", + "Mp5", + "m4", + "Ruger", + "SniperRifle", + "LaserScope", + "RocketLauncher", + "FlameThrower", + "M60", + "Minigun", + "Detonator", + "HeliCannon", + "Camera", +}; + +CWeaponInfo* +CWeaponInfo::GetWeaponInfo(eWeaponType weaponType) +{ + return &aWeaponInfo[weaponType]; +} + +void +CWeaponInfo::Initialise(void) +{ + debug("Initialising CWeaponInfo...\n"); + for (int i = 0; i < WEAPONTYPE_TOTALWEAPONS; i++) { + aWeaponInfo[i].m_eWeaponFire = WEAPON_FIRE_INSTANT_HIT; + aWeaponInfo[i].m_fRange = 0.0f; + aWeaponInfo[i].m_nFiringRate = 0; + aWeaponInfo[i].m_nReload = 0; + aWeaponInfo[i].m_nAmountofAmmunition = 0; + aWeaponInfo[i].m_nDamage = 0; + aWeaponInfo[i].m_fSpeed = 0.0f; + aWeaponInfo[i].m_fRadius = 0.0f; + aWeaponInfo[i].m_fLifespan = 0.0f; + aWeaponInfo[i].m_fSpread = 0.0f; + aWeaponInfo[i].m_vecFireOffset = CVector(0.0f, 0.0f, 0.0f); + aWeaponInfo[i].m_AnimToPlay = ASSOCGRP_UNARMED; + aWeaponInfo[i].m_fAnimLoopStart = 0.0f; + aWeaponInfo[i].m_fAnimLoopEnd = 0.0f; + aWeaponInfo[i].m_fAnimFrameFire = 0.0f; + aWeaponInfo[i].m_fAnim2LoopStart = 0.0f; + aWeaponInfo[i].m_fAnim2LoopEnd = 0.0f; + aWeaponInfo[i].m_fAnim2FrameFire = 0.0f; + aWeaponInfo[i].m_fAnimBreakout = 0.0f; + aWeaponInfo[i].m_Flags = WEAPONFLAG_USE_GRAVITY | WEAPONFLAG_SLOWS_DOWN | WEAPONFLAG_RAND_SPEED | WEAPONFLAG_EXPANDS | WEAPONFLAG_EXPLODES; + aWeaponInfo[i].m_nWeaponSlot = WEAPONSLOT_UNARMED; + } + debug("Loading weapon data...\n"); + LoadWeaponData(); + debug("CWeaponInfo ready\n"); +} + +void +CWeaponInfo::LoadWeaponData(void) +{ + float spread, speed, lifeSpan, radius; + float range, fireOffsetX, fireOffsetY, fireOffsetZ; + float anim2LoopStart, anim2LoopEnd, delayBetweenAnim2AndFire, animBreakout; + float delayBetweenAnimAndFire, animLoopStart, animLoopEnd; + int flags, ammoAmount, damage, reload, weaponType; + int firingRate, modelId, modelId2, weaponSlot; + char line[256], weaponName[32], fireType[32]; + char animToPlay[32]; + + size_t bp, buflen; + int lp, linelen; + + CFileMgr::SetDir("DATA"); + buflen = CFileMgr::LoadFile("WEAPON.DAT", work_buff, sizeof(work_buff), "r"); + + for (bp = 0; bp < buflen; ) { + // read file line by line + for (linelen = 0; work_buff[bp] != '\n' && bp < buflen; bp++) { + line[linelen++] = work_buff[bp]; + } + bp++; + line[linelen] = '\0'; + + // skip white space + for (lp = 0; line[lp] <= ' ' && line[lp] != '\0'; lp++); + + if (line[lp] == '\0' || line[lp] == '#') + continue; + + spread = 0.0f; + flags = 0; + speed = 0.0f; + ammoAmount = 0; + lifeSpan = 0.0f; + radius = 0.0f; + range = 0.0f; + damage = 0; + reload = 0; + firingRate = 0; + fireOffsetX = 0.0f; + weaponName[0] = '\0'; + fireType[0] = '\0'; + fireOffsetY = 0.0f; + fireOffsetZ = 0.0f; + sscanf( + &line[lp], + "%s %s %f %d %d %d %d %f %f %f %f %f %f %f %s %f %f %f %f %f %f %f %d %d %x %d", + weaponName, + fireType, + &range, + &firingRate, + &reload, + &ammoAmount, + &damage, + &speed, + &radius, + &lifeSpan, + &spread, + &fireOffsetX, + &fireOffsetY, + &fireOffsetZ, + animToPlay, + &animLoopStart, + &animLoopEnd, + &delayBetweenAnimAndFire, + &anim2LoopStart, + &anim2LoopEnd, + &delayBetweenAnim2AndFire, + &animBreakout, + &modelId, + &modelId2, + &flags, + &weaponSlot); + + if (strncmp(weaponName, "ENDWEAPONDATA", 13) == 0) + return; + + weaponType = FindWeaponType(weaponName); + + CVector vecFireOffset(fireOffsetX, fireOffsetY, fireOffsetZ); + + aWeaponInfo[weaponType].m_eWeaponFire = FindWeaponFireType(fireType); + aWeaponInfo[weaponType].m_fRange = range; + aWeaponInfo[weaponType].m_nFiringRate = firingRate; + aWeaponInfo[weaponType].m_nReload = reload; + aWeaponInfo[weaponType].m_nAmountofAmmunition = ammoAmount; + aWeaponInfo[weaponType].m_nDamage = damage; + aWeaponInfo[weaponType].m_fSpeed = speed; + aWeaponInfo[weaponType].m_fRadius = radius; + aWeaponInfo[weaponType].m_fLifespan = lifeSpan; + aWeaponInfo[weaponType].m_fSpread = spread; + aWeaponInfo[weaponType].m_vecFireOffset = vecFireOffset; + aWeaponInfo[weaponType].m_fAnimLoopStart = animLoopStart / 30.0f; + aWeaponInfo[weaponType].m_fAnimLoopEnd = animLoopEnd / 30.0f; + aWeaponInfo[weaponType].m_fAnim2LoopStart = anim2LoopStart / 30.0f; + aWeaponInfo[weaponType].m_fAnim2LoopEnd = anim2LoopEnd / 30.0f; + aWeaponInfo[weaponType].m_fAnimFrameFire = delayBetweenAnimAndFire / 30.0f; + aWeaponInfo[weaponType].m_fAnim2FrameFire = delayBetweenAnim2AndFire / 30.0f; + aWeaponInfo[weaponType].m_fAnimBreakout = animBreakout / 30.0f; + aWeaponInfo[weaponType].m_nModelId = modelId; + aWeaponInfo[weaponType].m_nModel2Id = modelId2; + aWeaponInfo[weaponType].m_Flags = flags; + aWeaponInfo[weaponType].m_nWeaponSlot = weaponSlot; + + if (animLoopEnd < 98.0f && weaponType != WEAPONTYPE_FLAMETHROWER && !CWeapon::IsShotgun(weaponType)) + aWeaponInfo[weaponType].m_nFiringRate = ((aWeaponInfo[weaponType].m_fAnimLoopEnd - aWeaponInfo[weaponType].m_fAnimLoopStart) * 900.0f); + + if (weaponType == WEAPONTYPE_DETONATOR || weaponType == WEAPONTYPE_HELICANNON) + modelId = -1; + else if (weaponType == WEAPONTYPE_DETONATOR_GRENADE) + modelId = MI_BOMB; + + if (modelId != -1) + ((CWeaponModelInfo*)CModelInfo::GetModelInfo(modelId))->SetWeaponInfo(weaponType); + + for (int i = 0; i < NUM_ANIM_ASSOC_GROUPS; i++) { + if (!strcmp(animToPlay, CAnimManager::GetAnimGroupName((AssocGroupId)i))) { + aWeaponInfo[weaponType].m_AnimToPlay = (AssocGroupId)i; + break; + } + } + } +} + +eWeaponType +CWeaponInfo::FindWeaponType(char *name) +{ + for (int i = 0; i < WEAPONTYPE_TOTALWEAPONS; i++) { + if (strcmp(ms_aWeaponNames[i], name) == 0) { + return static_cast(i); + } + } + return WEAPONTYPE_UNARMED; +} + +eWeaponFire +CWeaponInfo::FindWeaponFireType(char *name) +{ + if (strcmp(name, "MELEE") == 0) return WEAPON_FIRE_MELEE; + if (strcmp(name, "INSTANT_HIT") == 0) return WEAPON_FIRE_INSTANT_HIT; + if (strcmp(name, "PROJECTILE") == 0) return WEAPON_FIRE_PROJECTILE; + if (strcmp(name, "AREA_EFFECT") == 0) return WEAPON_FIRE_AREA_EFFECT; + if (strcmp(name, "CAMERA") == 0) return WEAPON_FIRE_CAMERA; + Error("Unknown weapon fire type, WeaponInfo.cpp"); + return WEAPON_FIRE_INSTANT_HIT; +} + +void +CWeaponInfo::Shutdown(void) +{ + debug("Shutting down CWeaponInfo...\n"); + debug("CWeaponInfo shut down\n"); +} diff --git a/src/miami/weapons/WeaponInfo.h b/src/miami/weapons/WeaponInfo.h new file mode 100644 index 00000000..d7f1563d --- /dev/null +++ b/src/miami/weapons/WeaponInfo.h @@ -0,0 +1,74 @@ +#pragma once + +#include "AnimManager.h" +#include "AnimationId.h" +#include "WeaponType.h" + +enum +{ + WEAPONFLAG_USE_GRAVITY = 1, + WEAPONFLAG_SLOWS_DOWN = 1 << 1, + WEAPONFLAG_DISSIPATES = 1 << 2, + WEAPONFLAG_RAND_SPEED = 1 << 3, + WEAPONFLAG_EXPANDS = 1 << 4, + WEAPONFLAG_EXPLODES = 1 << 5, + WEAPONFLAG_CANAIM = 1 << 6, + WEAPONFLAG_CANAIM_WITHARM = 1 << 7, + WEAPONFLAG_1ST_PERSON = 1 << 8, + WEAPONFLAG_HEAVY = 1 << 9, + WEAPONFLAG_THROW = 1 << 10, + WEAPONFLAG_RELOAD_LOOP2START = 1 << 11, + WEAPONFLAG_USE_2ND = 1 << 12, + WEAPONFLAG_GROUND_2ND = 1 << 13, + WEAPONFLAG_FINISH_3RD = 1 << 14, + WEAPONFLAG_RELOAD = 1 << 15, + WEAPONFLAG_FIGHTMODE = 1 << 16, + WEAPONFLAG_CROUCHFIRE = 1 << 17, + WEAPONFLAG_COP3_RD = 1 << 18, + WEAPONFLAG_GROUND_3RD = 1 << 19, + WEAPONFLAG_PARTIALATTACK = 1 << 20, + WEAPONFLAG_ANIMDETONATE = 1 << 21, +}; + +class CWeaponInfo { + static char ms_aWeaponNames[WEAPONTYPE_TOTALWEAPONS][32]; +public: + static uint16 ms_aReloadSampleTime[WEAPONTYPE_TOTALWEAPONS]; + static int32 ms_aMaxAmmoForWeapon[WEAPONTYPE_TOTALWEAPONS]; + + eWeaponFire m_eWeaponFire; + float m_fRange; + uint32 m_nFiringRate; + uint32 m_nReload; + int32 m_nAmountofAmmunition; + uint32 m_nDamage; + float m_fSpeed; + float m_fRadius; + float m_fLifespan; + float m_fSpread; + CVector m_vecFireOffset; + AssocGroupId m_AnimToPlay; + float m_fAnimLoopStart; + float m_fAnimLoopEnd; + float m_fAnimFrameFire; + float m_fAnim2LoopStart; + float m_fAnim2LoopEnd; + float m_fAnim2FrameFire; + float m_fAnimBreakout; + int32 m_nModelId; + int32 m_nModel2Id; + uint32 m_Flags; + + uint32 m_nWeaponSlot; + + static void Initialise(void); + static void LoadWeaponData(void); + static CWeaponInfo *GetWeaponInfo(eWeaponType weaponType); + static eWeaponFire FindWeaponFireType(char *name); + static eWeaponType FindWeaponType(char *name); + static void Shutdown(void); + static bool IsWeaponSlotAmmoMergeable(uint32 slot) { return slot == WEAPONSLOT_SHOTGUN || slot == WEAPONSLOT_SUBMACHINEGUN || slot == WEAPONSLOT_RIFLE; } + bool IsFlagSet(uint32 flag) const { return (m_Flags & flag) != 0; } +}; + +VALIDATE_SIZE(CWeaponInfo, 0x64); diff --git a/src/miami/weapons/WeaponType.h b/src/miami/weapons/WeaponType.h new file mode 100644 index 00000000..1220196f --- /dev/null +++ b/src/miami/weapons/WeaponType.h @@ -0,0 +1,86 @@ +#pragma once + +enum eWeaponType +{ + WEAPONTYPE_UNARMED, + WEAPONTYPE_BRASSKNUCKLE, + WEAPONTYPE_SCREWDRIVER, + WEAPONTYPE_GOLFCLUB, + WEAPONTYPE_NIGHTSTICK, + WEAPONTYPE_KNIFE, + WEAPONTYPE_BASEBALLBAT, + WEAPONTYPE_HAMMER, + WEAPONTYPE_CLEAVER, + WEAPONTYPE_MACHETE, + WEAPONTYPE_KATANA, + WEAPONTYPE_CHAINSAW, + WEAPONTYPE_GRENADE, + WEAPONTYPE_DETONATOR_GRENADE, + WEAPONTYPE_TEARGAS, + WEAPONTYPE_MOLOTOV, + WEAPONTYPE_ROCKET, + WEAPONTYPE_COLT45, + WEAPONTYPE_PYTHON, + WEAPONTYPE_SHOTGUN, + WEAPONTYPE_SPAS12_SHOTGUN, + WEAPONTYPE_STUBBY_SHOTGUN, + WEAPONTYPE_TEC9, + WEAPONTYPE_UZI, + WEAPONTYPE_SILENCED_INGRAM, + WEAPONTYPE_MP5, + WEAPONTYPE_M4, + WEAPONTYPE_RUGER, + WEAPONTYPE_SNIPERRIFLE, + WEAPONTYPE_LASERSCOPE, + WEAPONTYPE_ROCKETLAUNCHER, + WEAPONTYPE_FLAMETHROWER, + WEAPONTYPE_M60, + WEAPONTYPE_MINIGUN, + WEAPONTYPE_DETONATOR, + WEAPONTYPE_HELICANNON, + WEAPONTYPE_CAMERA, + WEAPONTYPE_TOTALWEAPONS = 37, + WEAPONTYPE_HEALTH = 37, + WEAPONTYPE_ARMOUR, + WEAPONTYPE_RAMMEDBYCAR, + WEAPONTYPE_RUNOVERBYCAR, + WEAPONTYPE_EXPLOSION, + WEAPONTYPE_UZI_DRIVEBY, + WEAPONTYPE_DROWNING, + WEAPONTYPE_FALL, + WEAPONTYPE_UNIDENTIFIED, + WEAPONTYPE_ANYMELEE, + WEAPONTYPE_ANYWEAPON +}; + +enum { + WEAPONSLOT_UNARMED = 0, + WEAPONSLOT_MELEE, + WEAPONSLOT_PROJECTILE, + WEAPONSLOT_HANDGUN, + WEAPONSLOT_SHOTGUN, + WEAPONSLOT_SUBMACHINEGUN, + WEAPONSLOT_RIFLE, + WEAPONSLOT_HEAVY, + WEAPONSLOT_SNIPER, + WEAPONSLOT_OTHER, + TOTAL_WEAPON_SLOTS +}; + +enum eWeaponFire { + WEAPON_FIRE_MELEE, + WEAPON_FIRE_INSTANT_HIT, + WEAPON_FIRE_PROJECTILE, + WEAPON_FIRE_AREA_EFFECT, + WEAPON_FIRE_CAMERA +}; + +// Taken from MTA SA, seems it's unchanged +enum eWeaponState +{ + WEAPONSTATE_READY, + WEAPONSTATE_FIRING, + WEAPONSTATE_RELOADING, + WEAPONSTATE_OUT_OF_AMMO, + WEAPONSTATE_MELEE_MADECONTACT +}; \ No newline at end of file diff --git a/src/tools/adf2mp3.cpp b/src/tools/adf2mp3.cpp new file mode 100644 index 00000000..269ae98e --- /dev/null +++ b/src/tools/adf2mp3.cpp @@ -0,0 +1,50 @@ +#include +#include + +int main(int argc, char* argv[]) { + // Check for correct number of arguments. + if (argc != 3) { + std::cerr << "Usage: adf2mp3 " << std::endl; + return 1; + } + + // Get file names from command line arguments. + const char* inputFileName = argv[1]; + const char* outputFileName = argv[2]; + + // Open the input file in binary mode. + std::ifstream inputFile(inputFileName, std::ios::binary); + if (!inputFile) { + std::cerr << "Error: Unable to open input file " << inputFileName << std::endl; + return 1; + } + + // Open the output file in binary mode. + std::ofstream outputFile(outputFileName, std::ios::binary); + if (!outputFile) { + std::cerr << "Error: Unable to open output file " << outputFileName << std::endl; + return 1; + } + + // Buffer to hold file data. + const size_t bufferSize = 4096; + char buffer[bufferSize]; + + // Process the file in chunks. + while (inputFile.read(buffer, bufferSize) || inputFile.gcount() > 0) { + // Get the number of bytes actually read. + std::streamsize bytesRead = inputFile.gcount(); + // XOR each byte with 0x22. + for (std::streamsize i = 0; i < bytesRead; ++i) { + buffer[i] ^= 0x22; + } + // Write the modified buffer to the output file. + outputFile.write(buffer, bytesRead); + } + + // Close files. + inputFile.close(); + outputFile.close(); + + return 0; +} diff --git a/dreamcast/analyze-profile.cpp b/src/tools/analyze-profile.cpp similarity index 100% rename from dreamcast/analyze-profile.cpp rename to src/tools/analyze-profile.cpp diff --git a/src/tools/animtool.cpp b/src/tools/animtool.cpp new file mode 100644 index 00000000..c218a138 --- /dev/null +++ b/src/tools/animtool.cpp @@ -0,0 +1,1029 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846264338327950288 +#endif + +struct Writer: std::vector { + template + void write(float v); +}; + + +template<> +void Writer::write(float v) { + assert(v >= -127 && v <= 127); + push_back(static_cast(v)); +} + +template<> +void Writer::write(float v) { + assert(v >= 0 && v <= 255); + push_back(static_cast(v)); +} + +template<> +void Writer::write(float v) { + assert(v >= -32767 && v <= 32767); + int16_t fx = static_cast(v); + insert(end(), reinterpret_cast(&fx), reinterpret_cast(&fx) + sizeof(fx)); +} + +template<> +void Writer::write(float v) { + assert(v >= 0 && v <= 65535); + int16_t fx = static_cast(v); + insert(end(), reinterpret_cast(&fx), reinterpret_cast(&fx) + sizeof(fx)); +} + +template<> +void Writer::write(float v) { + insert(end(), reinterpret_cast(&v), reinterpret_cast(&v) + sizeof(float)); +} + +// Example Reader class interface (you need to implement this as appropriate) +class Reader { +public: + Reader(const std::vector& data) : buffer(data), offset(0) {} + + template + T read() { + if (offset + sizeof(T) > buffer.size()) { + fprintf(stderr, "Reader error: trying to read past end of buffer.\n"); + assert(false); + } + T value; + memcpy(&value, buffer.data() + offset, sizeof(T)); + offset += sizeof(T); + return value; + } +private: + const std::vector& buffer; + size_t offset; +}; + +#define FLAGS_KF_ROT ( 1 << 0 ) +#define FLAGS_KF_TRANS ( 1 << 1 ) + +#define FLAGS_HAS_ROT_Y ( 1 << 8 ) +#define FLAGS_HAS_ROT_P ( 1 << 9 ) +#define FLAGS_HAS_ROT_R ( 1 << 10 ) + +#define FLAGS_HAS_TRANS_X ( 1 << 11 ) +#define FLAGS_HAS_TRANS_Y ( 1 << 12 ) +#define FLAGS_HAS_TRANS_Z ( 1 << 13 ) +#define FLAGS_HAS_TRANS_ANY ( 7 << 11 ) +#define FLAGS_HAS_TRANS_LARGE ( 1 << 14 ) +#define FLAGS_QUAT0_NEG ( 1 << 15 ) + +using uint32 = uint32_t; + +#define RwStreamRead(f, p, s) fread(p, s, 1, f) +#define debug(...) // printf(__VA_ARGS__) +#define RwMalloc malloc + +struct rotation_t { + float y; // Theta, in [0, π/2] + float p; // phi, typically in [-π, π] + float r; // psi, typically in [-π, π] + + uint16_t fixed_y() { + return fixed(y); + } + + uint16_t fixed_p() { + return fixed(p); + } + + uint16_t fixed_r() { + return fixed(r); + } + + static uint16_t fixed(float a) { + // we use int16_t bellow instead since overflows are 2 * M_PI + // if (a < 0) + // a += 2 * M_PI; + // if (a > 2 * M_PI) + // a -= 2 * M_PI; + // assert(a >= 0 && a < 2 * M_PI); + return static_cast(a * 65536 / (2 * M_PI)); + } +}; + +struct quaternion_t { + float x, y, z, w; + + quaternion_t inverted() { + return {-x, -y, -z, w}; + } + + quaternion_t operator-() const { + return {-x, -y, -z, -w}; + } + + rotation_t toSpherical() const { + rotation_t rot; + // Compute the magnitude of the first complex component (w, x) + float A = sqrt(w * w + x * x); + // Compute the magnitude of the second complex component (y, z) + float B = sqrt(y * y + z * z); + + // Calculate theta from the ratio B/A. + // A is non-negative by definition, and so is B. + rot.y = atan2(B, A); + + // phi is the argument (angle) of the complex number A = w + i*x. + rot.p = atan2(x, w); + + // psi is the argument of the complex number B = y + i*z. + rot.r = atan2(z, y); + return rot; + } + + // Convert from spherical (yaw, pitch, roll) to quaternion + static quaternion_t fromSpherical(const rotation_t& rot) { + quaternion_t q; + q.w = cos(rot.y) * cos(rot.p); + q.x = cos(rot.y) * sin(rot.p); + q.y = sin(rot.y) * cos(rot.r); + q.z = sin(rot.y) * sin(rot.r); + return q; + } + + static quaternion_t fromSphericalFixed(uint16_t y, uint16_t p, uint16_t r) { + quaternion_t q; + q.w = cos((y / 65536.0f) * 2 * M_PI) * cos((p / 65536.0f) * 2 * M_PI); + q.x = cos((y / 65536.0f) * 2 * M_PI) * sin((p / 65536.0f) * 2 * M_PI); + q.y = sin((y / 65536.0f) * 2 * M_PI) * cos((r / 65536.0f) * 2 * M_PI); + q.z = sin((y / 65536.0f) * 2 * M_PI) * sin((r / 65536.0f) * 2 * M_PI); + return q; + } +}; +struct translation_t { + float x, y, z; +}; + +void assert_float(float v, float exp, float espilon = 0.01) { + assert(fabs(v - exp) < espilon); +} + +void assert_trans(const translation_t& v, const translation_t& exp, float espilon = 2/128.f) { + assert(fabs(v.x - exp.x) < espilon); + assert(fabs(v.y - exp.y) < espilon); + assert(fabs(v.z - exp.z) < espilon); +} + +void assert_quat(const quaternion_t& v, const quaternion_t& exp, float espilon = 0.04) { + assert(fabs(v.x - exp.x) < espilon); + assert(fabs(v.y - exp.y) < espilon); + assert(fabs(v.z - exp.z) < espilon); + if (fabs(exp.x) < 0.0001f && fabs(exp.y) < 0.0001f && fabs(exp.z) < 0.0001f) + ; + else + assert(fabs(v.w - exp.w) < espilon); +} + +float dot(const quaternion_t &a, const quaternion_t &b) { + return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; +} + +struct sequence_t { + std::vector rotations; + std::vector translations; + std::vector times; + std::string name; + int boneTag = -1; + + void removeQuaternionFlips() { + quaternion_t q1 = rotations.front(); + for(size_t i = 1; i < rotations.size(); i++){ + auto q2 = rotations[i]; + float dot = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; + if(dot < 0.0f) + rotations[i] = -q2; + q1 = rotations[i]; + } + } + + // This function is assumed to be a member of your sequence_t type. + void decompressDeltaCompressedData(const std::vector& compData, sequence_t ref) { + Reader reader(compData); + + // Read the flags written at the start. + uint16_t flags = reader.read(); + + // Read initial and final timestamps. + float initial_ts = reader.read(); + float final_ts = reader.read(); + + // Assume that the number of frames was set before calling this function. + int nFrames = times.size(); + if (nFrames == 0) + return; + + rotations.resize(nFrames); + + // Set first frame time. + times[0] = initial_ts; + float predicted_ts = initial_ts; + + // --- Translations --- + float predicted_tx = 0, predicted_ty = 0, predicted_tz = 0; + if (flags & FLAGS_KF_TRANS) { + translation_t initTrans, finalTrans; + translations.resize(nFrames); + if (flags & FLAGS_HAS_TRANS_LARGE) { + initTrans.x = reader.read(); + initTrans.y = reader.read(); + initTrans.z = reader.read(); + predicted_tx = initTrans.x; + predicted_ty = initTrans.y; + predicted_tz = initTrans.z; + translations[0] = initTrans; + // Read final translation (may be used for verification or ignored) + finalTrans.x = reader.read(); + finalTrans.y = reader.read(); + finalTrans.z = reader.read(); + } else { + initTrans.x = reader.read() / 128.f; + initTrans.y = reader.read() / 128.f; + initTrans.z = reader.read() / 128.f; + predicted_tx = initTrans.x; + predicted_ty = initTrans.y; + predicted_tz = initTrans.z; + translations[0] = initTrans; + // Read final translation (for completeness) + finalTrans.x = reader.read() / 128.f; + finalTrans.y = reader.read() / 128.f; + finalTrans.z = reader.read() / 128.f; + } + + assert_trans(initTrans, ref.translations.front()); + + assert_trans(finalTrans, ref.translations.back()); + } + + // --- Rotations --- + // Read the absolute fixed‑point rotation values for the first frame. + uint16_t fixed_y = reader.read(); + uint16_t fixed_p = reader.read(); + uint16_t fixed_r = reader.read(); + rotations[0] = quaternion_t::fromSphericalFixed(fixed_y, fixed_p, fixed_r); + uint16_t predicted_y = fixed_y; + uint16_t predicted_p = fixed_p; + uint16_t predicted_r = fixed_r; + + if (flags & FLAGS_QUAT0_NEG) { + rotations[0] = -rotations[0]; + } + + assert_quat(rotations[0], ref.rotations.front()); + + // --- Delta decoding for each subsequent frame --- + for (int frameId = 1; frameId < nFrames; frameId++) { + // --- Rotations --- + // For rotation Y: + if (flags & FLAGS_HAS_ROT_Y) { + uint8_t byteVal = reader.read(); + if (byteVal == 128) { + predicted_y = reader.read(); + } else { + int8_t diff = static_cast(byteVal); + predicted_y += diff * 8; + } + } + // For rotation P: + if (flags & FLAGS_HAS_ROT_P) { + uint8_t byteVal = reader.read(); + if (byteVal == 128) { + predicted_p = reader.read(); + } else { + int8_t diff = static_cast(byteVal); + predicted_p += diff * 8; + } + } + // For rotation R: + if (flags & FLAGS_HAS_ROT_R) { + uint8_t byteVal = reader.read(); + if (byteVal == 128) { + predicted_r = reader.read(); + } else { + int8_t diff = static_cast(byteVal); + predicted_r += diff * 8; + } + } + if (frameId < rotations.size()) { + rotations[frameId] = quaternion_t::fromSphericalFixed(predicted_y, predicted_p, predicted_r); + } + + // --- Translations --- + // Translation X: + if (flags & FLAGS_HAS_TRANS_X) { + uint8_t byteVal = reader.read(); + if (byteVal == 128) { + uint16_t diff = reader.read(); + if (diff != 32768) { + predicted_tx += static_cast(diff) / 128.f; + } else { + predicted_tx = reader.read(); + } + } else { + int8_t diff = static_cast(byteVal); + predicted_tx += diff / 127.f; + } + } + // Translation Y: + if (flags & FLAGS_HAS_TRANS_Y) { + uint8_t byteVal = reader.read(); + if (byteVal == 128) { + uint16_t diff = reader.read(); + if (diff != 32768) { + predicted_ty += static_cast(diff) / 128.f; + } else { + predicted_ty = reader.read(); + } + } else { + int8_t diff = static_cast(byteVal); + predicted_ty += diff / 127.f; + } + } + // Translation Z: + if (flags & FLAGS_HAS_TRANS_Z) { + uint8_t byteVal = reader.read(); + if (byteVal == 128) { + uint16_t diff = reader.read(); + if (diff != 32768) { + predicted_tz += static_cast(diff) / 128.f; + } else { + predicted_tz = reader.read(); + } + } else { + int8_t diff = static_cast(byteVal); + predicted_tz += diff / 127.f; + } + } + if (frameId < translations.size()) { + translations[frameId] = { predicted_tx, predicted_ty, predicted_tz }; + assert_trans(translations[frameId], ref.translations[frameId]); + } + + // --- Time Stamps --- + { + uint8_t byteValPacked = reader.read(); + uint8_t byteVal = byteValPacked & 127; + float diff; + if (byteVal == 127) { + uint16_t fixed_diff = reader.read(); + diff = fixed_diff / 256.f; + } else { + diff = byteVal / 256.f; + } + predicted_ts += diff; + times[frameId] = predicted_ts; + assert_float(times[frameId], ref.times[frameId]); + + if (byteValPacked & 128) { + rotations[frameId] = -rotations[frameId]; + } + } + + // assert for quaternion now that flip has been done, if needed + assert_quat(rotations[frameId], ref.rotations[frameId]); + } + + // Optionally verify that the final reconstructed timestamp matches the stored final timestamp. + if (fabs(predicted_ts - final_ts) > 0.001f) { + fprintf(stderr, "Warning: final timestamp mismatch: %f vs %f\n", predicted_ts, final_ts); + } + } + + Writer getDeltaCompressedData() { + Writer writer; + uint16_t flags = 0; + + std::vector diffs_ts(times.size()), ts(times.size()); + for (size_t i = 1; i < times.size(); i++) { + float diff = times[i] - times[i-1]; + assert (diff >=0 && diff < 256); + diffs_ts[i] = diff; + ts[i] = times[i]; + } + + diffs_ts[0] = ts[0] = times[0]; + + translation_t maxMag_trans = { 0, 0, 0}; + std::vector diffs_trans(translations.size()), trans(translations.size()); + if (translations.size() > 0) { + for (size_t i = translations.size() - 1; i != 0 ; i--) { + translation_t prev = translations[i-1]; + translation_t curr = translations[i]; + + translation_t diff; + diff.x = curr.x - prev.x; + diff.y = curr.y - prev.y; + diff.z = curr.z - prev.z; + + diffs_trans[i] = diff; + trans[i] = curr; + + maxMag_trans.x = std::max(maxMag_trans.x, std::abs(diff.x)); + maxMag_trans.y = std::max(maxMag_trans.y, std::abs(diff.y)); + maxMag_trans.z = std::max(maxMag_trans.z, std::abs(diff.z)); + } + + diffs_trans[0] = trans[0] = translations[0]; + } + + + std::vector diffs_rots(rotations.size()), rots(rotations.size()); + + rotation_t maxMag_rots = { 0, 0, 0}; + for (size_t i = rotations.size() - 1; i != 0 ; i--) { + rotation_t prev = rotations[i-1].toSpherical(); + rotation_t curr = rotations[i].toSpherical(); + + rotation_t diff; + diff.y = curr.y - prev.y; + diff.p = curr.p - prev.p; + diff.r = curr.r - prev.r; + + diffs_rots[i] = diff; + rots[i] = curr; + + maxMag_rots.y = std::max(maxMag_rots.y, std::abs(diff.y)); + maxMag_rots.p = std::max(maxMag_rots.p, std::abs(diff.p)); + maxMag_rots.r = std::max(maxMag_rots.r, std::abs(diff.r)); + } + diffs_rots[0] = rots[0] = rotations[0].toSpherical(); + + // okay, now we have the diffs, the max magnitudes, and the original values + // do the write out + flags = FLAGS_KF_ROT; // always have rotations + + if (translations.size() > 0) { + flags |= FLAGS_KF_TRANS; + } + + { + quaternion_t q1 = quaternion_t::fromSphericalFixed(rots.front().fixed_y(), rots.front().fixed_p(), rots.front().fixed_r()); + if (dot(rotations.front(), q1) < 0) { + flags |= FLAGS_QUAT0_NEG; + } + } + + if (maxMag_rots.p > 0.0001) { + flags |= FLAGS_HAS_ROT_P; + } + if (maxMag_rots.y > 0.0001) { + flags |= FLAGS_HAS_ROT_Y; + } + if (maxMag_rots.r > 0.0001) { + flags |= FLAGS_HAS_ROT_R; + } + + if (maxMag_trans.x > 0.0001f) { + flags |= FLAGS_HAS_TRANS_X; + } + if (maxMag_trans.y > 0.0001f) { + flags |= FLAGS_HAS_TRANS_Y; + } + if (maxMag_trans.z > 0.0001f) { + flags |= FLAGS_HAS_TRANS_Z; + } + + if (trans.size() > 0) { + if (fabs(trans.front().x) > 127 || fabs(trans.front().y) > 127 || fabs(trans.front().z) > 127) { + flags |= FLAGS_HAS_TRANS_LARGE; + } + + if (fabs(trans.back().x) > 127 || fabs(trans.back().y) > 127 || fabs(trans.back().z) > 127) { + flags |= FLAGS_HAS_TRANS_LARGE; + } + } + + assert(ts[0] < 256); + + writer.write(flags); + + writer.write(ts.front()); + size_t end_time_offset = writer.size(); + writer.write(0); // filler for end timestamp + + float predicted_ts = ts.front(); + + float predicted_tx = 0, predicted_ty = 0, predicted_tz = 0; + + if (flags & FLAGS_KF_TRANS) { + + if (flags & FLAGS_HAS_TRANS_LARGE) { + writer.write(trans.front().x); + writer.write(trans.front().y); + writer.write(trans.front().z); + + predicted_tx = trans.front().x; + predicted_ty = trans.front().y; + predicted_tz = trans.front().z; + + writer.write(trans.back().x); + writer.write(trans.back().y); + writer.write(trans.back().z); + } else { + writer.write(trans.front().x * 128); + writer.write(trans.front().y * 128); + writer.write(trans.front().z * 128); + + predicted_tx = int16_t(trans.front().x * 128) / 128.f; + predicted_ty = int16_t(trans.front().y * 128) / 128.f; + predicted_tz = int16_t(trans.front().z * 128) / 128.f; + + writer.write(trans.back().x * 128); + writer.write(trans.back().y * 128); + writer.write(trans.back().z * 128); + } + + if (flags & FLAGS_HAS_TRANS_ANY) { + translation_t t1 { predicted_tx, predicted_ty, predicted_tz }; + assert_trans(t1, trans[0]); + } + } + + writer.write(rots.front().fixed_y()); + writer.write(rots.front().fixed_p()); + writer.write(rots.front().fixed_r()); + + uint16_t predicted_y = rots.front().fixed_y(); + uint16_t predicted_p = rots.front().fixed_p(); + uint16_t predicted_r = rots.front().fixed_r(); + + for (int frameId = 1; frameId < times.size(); frameId++) { + if (flags & FLAGS_HAS_ROT_Y) { + int16_t diff = rots[frameId].fixed_y() - predicted_y; + if (abs(diff) > 127*8) { + writer.write(128); + writer.write(rots[frameId].fixed_y()); // special case: wraps around + predicted_y = rots[frameId].fixed_y(); + } else { + writer.write(diff/8); + predicted_y += int8_t(diff/8) * 8; + } + } + if (flags & FLAGS_HAS_ROT_P) { + int16_t diff = rots[frameId].fixed_p() - predicted_p; + if (abs(diff) > 127*8) { + writer.write(128); + writer.write(rots[frameId].fixed_p()); // special case: wraps around + predicted_p = rots[frameId].fixed_p(); + } else { + writer.write(diff/8); + predicted_p += int8_t(diff/8) * 8; + } + } + if (flags & FLAGS_HAS_ROT_R) { + int16_t diff = rots[frameId].fixed_r() - predicted_r; + if (abs(diff) > 127*8) { + writer.write(128); + writer.write(rots[frameId].fixed_r()); // special case: wraps around + predicted_r = rots[frameId].fixed_r(); + } else { + writer.write(diff/8); + predicted_r += int8_t(diff/8) * 8; + } + } + + bool quat_flip = false; + { + quaternion_t q1 = quaternion_t::fromSphericalFixed(predicted_y, predicted_p, predicted_r); + if (dot(rotations[frameId], q1) < 0) { + q1 = -q1; + quat_flip = true; + } + assert_quat(q1, rotations[frameId]); + } + + if (flags & FLAGS_HAS_TRANS_X) { + float diff = trans[frameId].x - predicted_tx; + if (fabs(diff) > 1) { + if (fabs(diff) < 128) { + writer.write(128); + writer.write(diff * 128); + predicted_tx += int16_t(diff * 128) / 128.f; + } else { + writer.write(128); + writer.write(32768); + writer.write(trans[frameId].x); + predicted_tx = trans[frameId].x; + } + } else { + int8_t fixed_diff = diff * 127; + writer.write(fixed_diff); + predicted_tx += fixed_diff / 127.f; + } + } + if (flags & FLAGS_HAS_TRANS_Y) { + float diff = trans[frameId].y - predicted_ty; + if (fabs(diff) > 1) { + if (fabs(diff) < 128) { + writer.write(128); + writer.write(diff * 128); + predicted_ty += int16_t(diff * 128) / 128.f; + } else { + writer.write(128); + writer.write(32768); + writer.write(trans[frameId].y); + predicted_ty = trans[frameId].y; + } + } else { + int8_t fixed_diff = diff * 127; + writer.write(fixed_diff); + predicted_ty += fixed_diff / 127.f; + } + } + if (flags & FLAGS_HAS_TRANS_Z) { + float diff = trans[frameId].z - predicted_tz; + if (fabs(diff) > 1) { + if (fabs(diff) < 128) { + writer.write(128); + writer.write(diff * 128); + predicted_tz += int16_t(diff * 128) / 128.f; + } else { + writer.write(128); + writer.write(32768); + writer.write(trans[frameId].z); + predicted_tz = trans[frameId].z; + } + } else { + int8_t fixed_diff = diff * 127; + writer.write(fixed_diff); + predicted_tz += fixed_diff / 127.f; + } + } + + if (flags & FLAGS_HAS_TRANS_ANY) { + translation_t t1 { predicted_tx, predicted_ty, predicted_tz }; + assert_trans(t1, trans[frameId]); + } + + { + float diff = ts[frameId] - predicted_ts; + uint16_t fixed_diff = diff * 256; + if (fixed_diff >= 127) { + writer.write(127 | quat_flip << 7); + writer.write(fixed_diff); + predicted_ts += fixed_diff / 256.f; + } else { + writer.write(fixed_diff | quat_flip << 7); + predicted_ts += fixed_diff / 256.f; + } + + assert_float(predicted_ts, ts[frameId]); + } + } + + memcpy(writer.data() + end_time_offset, &predicted_ts, sizeof(float)); + return writer; + } +}; + +struct animation_t { + std::vector sequences; + std::string name; +}; + +struct block_t { + std::vector animations; + std::string name; +}; + +#define ROUNDSIZE(x) if((x) & 3) (x) += 4 - ((x)&3) +struct IfpHeader { + uint32_t ident; + uint32 size; +}; + +block_t LoadAnimFile(FILE *stream) +{ + block_t block; + + size_t totalsize = 0; + size_t totalframes = 0; + + IfpHeader anpk, info, name, dgan, cpan, anim; + char buf[256]; + int j, k, l; + float *fbuf = (float*)buf; + + // block name + RwStreamRead(stream, &anpk, sizeof(IfpHeader)); + ROUNDSIZE(anpk.size); + RwStreamRead(stream, &info, sizeof(IfpHeader)); + ROUNDSIZE(info.size); + RwStreamRead(stream, buf, info.size); + int numAnims = *(int*)buf; + + debug("Loading ANIMS %s\n", buf+4); + block.name = buf+4; + + for(j = 0; j < numAnims; j++){ + + // animation name + RwStreamRead(stream, &name, sizeof(IfpHeader)); + ROUNDSIZE(name.size); + RwStreamRead(stream, buf, name.size); + + debug("Animation: %s\n", buf); + block.animations.push_back(animation_t()); + animation_t &animation = block.animations.back(); + animation.name = buf; + + // DG info has number of nodes/sequences + RwStreamRead(stream, (char*)&dgan, sizeof(IfpHeader)); + ROUNDSIZE(dgan.size); + RwStreamRead(stream, (char*)&info, sizeof(IfpHeader)); + ROUNDSIZE(info.size); + RwStreamRead(stream, buf, info.size); + int numSequences = *(int*)buf; + + for(k = 0; k < numSequences; k++){ + animation.sequences.push_back(sequence_t()); + sequence_t &seq = animation.sequences.back(); + + // Each node has a name and key frames + RwStreamRead(stream, &cpan, sizeof(IfpHeader)); + ROUNDSIZE(dgan.size); + RwStreamRead(stream, &anim, sizeof(IfpHeader)); + ROUNDSIZE(anim.size); + RwStreamRead(stream, buf, anim.size); + int numFrames = *(int*)(buf+28); + + debug("Sequence: %s\n", buf); + seq.name = buf; + if(anim.size == 44) + seq.boneTag = *(int*)(buf+40); + if(numFrames == 0) + continue; + + bool hasScale = false; + bool hasTranslation = false; + RwStreamRead(stream, &info, sizeof(info)); + size_t framesize = 0; + if(strncmp((char*)&info.ident, "KRTS", 4) == 0){ + hasScale = true; + // seq->SetNumFrames(numFrames, true, compressHier); + framesize = 3 * 2 + 4 * 2 + 2; + }else if(strncmp((char*)&info.ident, "KRT0", 4) == 0){ + hasTranslation = true; + // seq->SetNumFrames(numFrames, true, compressHier); + framesize = 3 * 2 + 4 * 2 + 2; + }else if(strncmp((char*)&info.ident, "KR00", 4) == 0){ + // seq->SetNumFrames(numFrames, false, compressHier); + framesize = 4 * 2 + 2; + } + + size_t animsize = numFrames * framesize; + totalsize += animsize; + totalframes += numFrames; + + // float *frameTimes = (float*)RwMalloc(sizeof(float) * numFrames); + debug("%d frames, %zu bytes\n", numFrames, animsize); + for(l = 0; l < numFrames; l++){ + if(hasScale){ + RwStreamRead(stream, buf, 0x2C); + seq.rotations.push_back(quaternion_t{fbuf[0], fbuf[1], fbuf[2], fbuf[3]}.inverted()); + seq.translations.push_back(translation_t{fbuf[4], fbuf[5], fbuf[6]}); + seq.times.push_back(fbuf[10]); + // CQuaternion rot(fbuf[0], fbuf[1], fbuf[2], fbuf[3]); + // rot.Invert(); + // CVector trans(fbuf[4], fbuf[5], fbuf[6]); + + // seq->SetRotation(l, rot); + // seq->SetTranslation(l, trans); + // // scaling ignored + // frameTimes[l] = fbuf[10]; // absolute time here + }else if(hasTranslation){ + RwStreamRead(stream, buf, 0x20); + seq.rotations.push_back(quaternion_t{fbuf[0], fbuf[1], fbuf[2], fbuf[3]}.inverted()); + seq.translations.push_back(translation_t{fbuf[4], fbuf[5], fbuf[6]}); + seq.times.push_back(fbuf[7]); + // CQuaternion rot(fbuf[0], fbuf[1], fbuf[2], fbuf[3]); + // rot.Invert(); + // CVector trans(fbuf[4], fbuf[5], fbuf[6]); + + // seq->SetRotation(l, rot); + // seq->SetTranslation(l, trans); + // frameTimes[l] = fbuf[7]; // absolute time here + }else{ + RwStreamRead(stream, buf, 0x14); + seq.rotations.push_back(quaternion_t{fbuf[0], fbuf[1], fbuf[2], fbuf[3]}.inverted()); + seq.times.push_back(fbuf[4]); + // CQuaternion rot(fbuf[0], fbuf[1], fbuf[2], fbuf[3]); + // rot.Invert(); + + // seq->SetRotation(l, rot); + // frameTimes[l] = fbuf[4]; // absolute time here + } + } + debug("Start time: %f, end time: %f\n", seq.times.front(), seq.times.back()); + + seq.removeQuaternionFlips(); + } + } + return block; +} + +void StoreAnimComprFile(FILE* stream, block_t block) { + IfpHeader anpv = { 0, (uint32_t)block.name.size() + 1 }; + memcpy(&anpv.ident, "ANPV", 4); + + fwrite(&anpv, sizeof(IfpHeader), 1, stream); + fwrite(block.name.c_str(), block.name.size() + 1, 1, stream); + int numAnims = block.animations.size(); + fwrite(&numAnims, sizeof(numAnims), 1, stream); + + for (int animId = 0; animId < numAnims; animId++) { + auto &anim = block.animations[animId]; + int animNameLength = anim.name.size() + 1; + fwrite(&animNameLength, sizeof(animNameLength), 1, stream); + fwrite(anim.name.c_str(), animNameLength, 1, stream); + int numSeqs = anim.sequences.size(); + fwrite(&numSeqs, sizeof(numSeqs), 1, stream); + + for (int seqId = 0; seqId < numSeqs; seqId++) { + auto &seq = anim.sequences[seqId]; + int seqNameLength = seq.name.size() + 1; + fwrite(&seqNameLength, sizeof(seqNameLength), 1, stream); + fwrite(seq.name.c_str(), seqNameLength, 1, stream); + int numFrames = seq.times.size(); + fwrite(&numFrames, sizeof(numFrames), 1, stream); + int boneTag = seq.boneTag; + fwrite(&boneTag, sizeof(boneTag), 1, stream); + + if (numFrames == 0) + continue; + + auto data = seq.getDeltaCompressedData(); + + uint32_t dataSize = data.size(); + fwrite(&dataSize, sizeof(dataSize), 1, stream); + fwrite(data.data(), dataSize, 1, stream); + } + } +} + +block_t LoadAnimComprFile(FILE* stream, block_t ref) { + block_t block; + + // Read and check header. + IfpHeader header; + if (fread(&header, sizeof(IfpHeader), 1, stream) != 1) { + fprintf(stderr, "Error reading IFP header.\n"); + exit(EXIT_FAILURE); + } + if (memcmp(&header.ident, "ANPV", 4) != 0) { + fprintf(stderr, "Invalid file header. Expected 'ANPV'.\n"); + exit(EXIT_FAILURE); + } + + // Read block name. (header.size equals block.name.size() + 1) + std::vector blockName(header.size); + if (fread(blockName.data(), header.size, 1, stream) != 1) { + fprintf(stderr, "Error reading block name.\n"); + exit(EXIT_FAILURE); + } + block.name = std::string(blockName.data()); + + // Read number of animations. + int numAnims = 0; + if (fread(&numAnims, sizeof(numAnims), 1, stream) != 1) { + fprintf(stderr, "Error reading number of animations.\n"); + exit(EXIT_FAILURE); + } + block.animations.resize(numAnims); + + // For each animation... + for (int animId = 0; animId < numAnims; animId++) { + animation_t anim; + + // Read animation name. + int animNameLength = 0; + if (fread(&animNameLength, sizeof(animNameLength), 1, stream) != 1) { + fprintf(stderr, "Error reading animation name length.\n"); + exit(EXIT_FAILURE); + } + std::vector animName(animNameLength); + if (fread(animName.data(), animNameLength, 1, stream) != 1) { + fprintf(stderr, "Error reading animation name.\n"); + exit(EXIT_FAILURE); + } + anim.name = std::string(animName.data()); + + // Read number of sequences. + int numSeqs = 0; + if (fread(&numSeqs, sizeof(numSeqs), 1, stream) != 1) { + fprintf(stderr, "Error reading number of sequences.\n"); + exit(EXIT_FAILURE); + } + anim.sequences.resize(numSeqs); + + // For each sequence... + for (int seqId = 0; seqId < numSeqs; seqId++) { + sequence_t seq; + + // Read sequence name. + int seqNameLength = 0; + if (fread(&seqNameLength, sizeof(seqNameLength), 1, stream) != 1) { + fprintf(stderr, "Error reading sequence name length.\n"); + exit(EXIT_FAILURE); + } + std::vector seqName(seqNameLength); + if (fread(seqName.data(), seqNameLength, 1, stream) != 1) { + fprintf(stderr, "Error reading sequence name.\n"); + exit(EXIT_FAILURE); + } + seq.name = std::string(seqName.data()); + + // Read number of frames. + int numFrames = 0; + if (fread(&numFrames, sizeof(numFrames), 1, stream) != 1) { + fprintf(stderr, "Error reading number of frames.\n"); + exit(EXIT_FAILURE); + } + // Prepare to store times (and, by convention, other per-frame data). + seq.times.resize(numFrames); + + // Read bone tag. + int boneTag = 0; + if (fread(&boneTag, sizeof(boneTag), 1, stream) != 1) { + fprintf(stderr, "Error reading bone tag.\n"); + exit(EXIT_FAILURE); + } + seq.boneTag = boneTag; + + // If there are keyframes, read the delta compressed data. + if (numFrames > 0) { + uint32_t dataSize = 0; + if (fread(&dataSize, sizeof(dataSize), 1, stream) != 1) { + fprintf(stderr, "Error reading delta compressed data size.\n"); + exit(EXIT_FAILURE); + } + std::vector compData(dataSize); + if (fread(compData.data(), dataSize, 1, stream) != 1) { + fprintf(stderr, "Error reading delta compressed data.\n"); + exit(EXIT_FAILURE); + } + + // Decompress the data. + seq.decompressDeltaCompressedData(compData, ref.animations[animId].sequences[seqId]); + } + + anim.sequences[seqId] = seq; + } + + block.animations[animId] = anim; + } + + return block; +} + + +int main(int argc, char **argv) +{ + if (argc != 3) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + FILE *stream = fopen(argv[1], "rb"); + if (!stream) { + fprintf(stderr, "Error: Unable to open input file %s\n", argv[1]); + return 1; + } + + auto block = LoadAnimFile(stream); + + fclose(stream); + + + stream = fopen(argv[2], "wb"); + if (!stream) { + fprintf(stderr, "Error: Unable to open output file %s\n", argv[2]); + return 2; + } + StoreAnimComprFile(stream, block); + fclose(stream); + stream = fopen(argv[2], "rb"); + if (!stream) { + fprintf(stderr, "Error: Unable to open output file %s\n", argv[2]); + return 2; + } + auto decompressedBlock = LoadAnimComprFile(stream, block); + fclose(stream); + + return 0; +} \ No newline at end of file diff --git a/dreamcast/aud2adpcm.c b/src/tools/aud2adpcm.c similarity index 98% rename from dreamcast/aud2adpcm.c rename to src/tools/aud2adpcm.c index 368d7b7d..52c9fe12 100644 --- a/dreamcast/aud2adpcm.c +++ b/src/tools/aud2adpcm.c @@ -558,6 +558,7 @@ void usage() { printf("based on wav2adpcm: 16bit mono wav to aica adpcm and vice-versa (c)2002 BERO\n" " wav2adpcm -q (To adpcm long stream)\n" " wav2adpcm -t (To adpcm long stream)\n" + " wav2adpcm -m (To adpcm MONO long stream)\n" " wav2adpcm -raw (To adpcm sfx)\n" "\n" "If you are having trouble with your input wav file you can run it" @@ -571,8 +572,10 @@ int main(int argc, char **argv) { if (argc == 4) { if (!strcmp(argv[1], "-t")) { return aud2adpcm(argv[2], argv[3], 1, 0, 0); + } else if (!strcmp(argv[1], "-m")) { + return aud2adpcm(argv[2], argv[3], 1, 1, 0); } else if (!strcmp(argv[1], "-q")) { - return aud2adpcm(argv[2], argv[3], 1, 1, 1); + return aud2adpcm(argv[2], argv[3], 1, 1, 1); } else if (!strcmp(argv[1], "-raw")) { return aud2adpcm(argv[2], argv[3], 0, 0, 0); } else { diff --git a/src/tools/coltool.cpp b/src/tools/coltool.cpp new file mode 100644 index 00000000..864f36dd --- /dev/null +++ b/src/tools/coltool.cpp @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include +#include +#include + +// Our collision file header. In the original code the header is read from the file. +// Here we assume it contains an identifier and a size. +struct ColHeader { + int32_t ident; // should be 'LLOC' + int32_t size; // size of the following collision data chunk +}; + +int main(int argc, char* argv[]) { + if (argc != 3) { + std::cerr << "Usage: repack_collision \n"; + return 1; + } + const char* inputFile = argv[1]; + const char* outputFile = argv[2]; + + std::ifstream infile(inputFile, std::ios::binary); + if (!infile) { + std::cerr << "Error: Cannot open input file: " << inputFile << "\n"; + return 1; + } + std::ofstream outfile(outputFile, std::ios::binary); + if (!outfile) { + std::cerr << "Error: Cannot open output file: " << outputFile << "\n"; + return 1; + } + + // Process each collision model in the file. + // Each chunk starts with a ColHeader followed by header.size bytes. + while (infile.peek() != EOF) { + ColHeader header; + infile.read(reinterpret_cast(&header), sizeof(header)); + if (!infile) break; + + // Check that the header ident is correct (should equal 'LLOC') + if (header.ident != 'LLOC') { + auto curpos = infile.tellg(); + infile.seekg(0, infile.end); + auto remaining = infile.tellg() - curpos; + + if (remaining < 2048) { + return 0; + } else { + std::cerr << "Error: Invalid header identifier encountered.\n"; + return 1; + } + } + + header.ident = 'CLOC'; + + // Read the collision chunk into a work buffer. + std::vector work_buff(header.size); + infile.read(work_buff.data(), header.size); + if (!infile) break; + + // The first 24 bytes of work_buff are the model name. + // The rest of the buffer is the collision model data. + // We now “repackage” the collision model data into a new vector. + std::vector out_buff; + // Copy model name (first 24 bytes) + out_buff.insert(out_buff.end(), work_buff.begin(), work_buff.begin() + 24); + + // Set an offset into work_buff where the collision model data begins. + size_t offset = 24; + + // === Process Collision Model Data === + // Copy first 44 bytes (bounding sphere, bounding box, and extra bytes) + out_buff.insert(out_buff.end(), work_buff.begin() + offset, work_buff.begin() + offset + 44); + // We need to extract numSpheres which is stored at offset 40 (within collision data) + int16_t numSpheres = *reinterpret_cast(&work_buff[24 + 40]); + offset += 44; + + assert(work_buff.size() > offset); + // Copy each sphere data block (20 bytes per sphere) + for (int i = 0; i < numSpheres; i++) { + out_buff.insert(out_buff.end(), work_buff.begin() + offset, work_buff.begin() + offset + 20); + offset += 20; + } + + // Process lines: read 4 bytes (includes numLines) + int16_t numLines = *reinterpret_cast(&work_buff[offset]); + out_buff.insert(out_buff.end(), work_buff.begin() + offset, work_buff.begin() + offset + 4); + offset += 4; + // For each line, copy 24 bytes + for (int i = 0; i < numLines; i++) { + out_buff.insert(out_buff.end(), work_buff.begin() + offset, work_buff.begin() + offset + 24); + offset += 24; + } + + // Process boxes: next 4 bytes hold numBoxes + int16_t numBoxes = *reinterpret_cast(&work_buff[offset]); + out_buff.insert(out_buff.end(), work_buff.begin() + offset, work_buff.begin() + offset + 4); + offset += 4; + // For each box, copy 28 bytes + for (int i = 0; i < numBoxes; i++) { + assert(work_buff.size() > offset); + out_buff.insert(out_buff.end(), work_buff.begin() + offset, work_buff.begin() + offset + 28); + offset += 28; + } + + // Process vertices: next 4 bytes (numVertices) + int16_t numVertices = *reinterpret_cast(&work_buff[offset]); + out_buff.insert(out_buff.end(), work_buff.begin() + offset, work_buff.begin() + offset + 4); + offset += 4; + assert(work_buff.size() > offset); + + // For each vertex, convert to fixed point (6 bytes for every 12 bytes in) + for (int i = 0; i < numVertices; i++) { + for (int v = 0; v < 3; v++) { + assert(work_buff.size() > offset); + float d = *(float*)(work_buff.data() + offset); + offset += 4; + auto fix = llround(d * 128); + assert(fix >= -32768 && fix <32767); + int16_t fix16 = int16_t(fix); + out_buff.insert(out_buff.end(), (uint8_t*)(&fix16), (uint8_t*)(&fix16 + 1)); + } + } + + // Process triangles: next 4 bytes (numTriangles) + int16_t numTriangles = *reinterpret_cast(&work_buff[offset]); + out_buff.insert(out_buff.end(), work_buff.begin() + offset, work_buff.begin() + offset + 4); + offset += 4; + // For each triangle, copy 16 bytes + for (int i = 0; i < numTriangles; i++) { + for (int v = 0; v < 3; v++) { + int32_t d = *(int32_t*)(work_buff.data() + offset); + offset += 4; + assert(d >= -32768 && d <32767); + assert(d < numVertices); + int16_t d16 = int16_t(d); + out_buff.insert(out_buff.end(), (uint8_t*)(&d16), (uint8_t*)(&d16 + 1)); + } + + out_buff.push_back(work_buff[offset]); + out_buff.push_back(work_buff[offset+1]); // 'piece', not actually used + offset += 4; + } + // === End repackaging of collision model data === + + // align to 4 bytes + while (out_buff.size() & 3) { + out_buff.push_back(0); + } + + // Update header.size to match our repackaged chunk size. + header.size = static_cast(out_buff.size()); + + // Write the header and the repackaged collision model data to the output file. + outfile.write(reinterpret_cast(&header), sizeof(header)); + outfile.write(out_buff.data(), out_buff.size()); + } + + return 0; +} diff --git a/dreamcast/extract-sfx.cpp b/src/tools/extract-sfx.cpp similarity index 100% rename from dreamcast/extract-sfx.cpp rename to src/tools/extract-sfx.cpp diff --git a/dreamcast/imgtool.cpp b/src/tools/imgtool.cpp similarity index 93% rename from dreamcast/imgtool.cpp rename to src/tools/imgtool.cpp index 416064e1..9901b242 100644 --- a/dreamcast/imgtool.cpp +++ b/src/tools/imgtool.cpp @@ -5,6 +5,7 @@ #include #include #include +#include // Sector size constant const size_t SECTOR_SIZE = 2048; @@ -27,7 +28,12 @@ std::vector readDirFile(const std::string& dirFilePath) { DirRecord record; while (dirFile.read(reinterpret_cast(&record), sizeof(DirRecord))) { - records.push_back(record); + // assert(record.size != 0); // plaster.dff is size zero on liberty + if (record.size > 0) { + records.push_back(record); + } else { + std::cerr << "Skipping record: " << record.name << " size is zero." << std::endl; + } } return records; diff --git a/dreamcast/pack-sfx.cpp b/src/tools/pack-sfx.cpp similarity index 100% rename from dreamcast/pack-sfx.cpp rename to src/tools/pack-sfx.cpp diff --git a/src/tools/streamheaderpack.cpp b/src/tools/streamheaderpack.cpp new file mode 100644 index 00000000..689bf783 --- /dev/null +++ b/src/tools/streamheaderpack.cpp @@ -0,0 +1,236 @@ +#include +#include +#include +#include +#include + +#include +#include + +struct WavHeader { + // RIFF Header + char riff[4]; // RIFF Header Magic header + uint32_t chunkSize; // RIFF Chunk Size + char wave[4]; // WAVE Header + // "fmt" sub-chunk + char fmt[4]; // FMT header + uint32_t subchunk1Size; // Size of the fmt chunk + uint16_t audioFormat; // Audio format 1=PCM, other values indicate compression + uint16_t numOfChan; // Number of channels 1=Mono, 2=Stereo + uint32_t samplesPerSec; // Sampling Frequency in Hz + uint32_t bytesPerSec; // bytes per second + uint16_t blockAlign; // 2=16-bit mono, 4=16-bit stereo + uint16_t bitsPerSample; // Number of bits per sample + // "data" sub-chunk + char data[4]; // "data" string + uint32_t dataSize; // Size of the data section +}; + +#define DCStreamedNameTable DCStreamedNameTable_liberty +#include "../liberty/audio/sampman_dc_streams.h" +#undef DCStreamedNameTable +#define DCStreamedNameTable DCStreamedNameTable_miami +#include "../miami/audio/sampman_dc_streams.h" +#undef DCStreamedNameTable + +#ifdef _WIN32 +#define fcaseopen fopen +#else +// Case-insensitivity on linux (from https://github.com/OneSadCookie/fcaseopen) +// Returned string should freed manually (if exists) +char* casepath(char const* path, bool checkPathFirst = true) +{ + //TODO: Implement this + bool access_ok = false; //access(path, F_OK) != -1 + // printf("TODO: FIXME %s\n", __func__); + + if (checkPathFirst && access_ok ) { + // File path is correct + return nullptr; + } + + size_t l = strlen(path); + if (l > 2 && path[0] == '.' && (path[1] == '/' || path[1] == '\\')) { + // remove ./ from the start of the path + path += 2; + } + char* p = (char*)alloca(l + 1); + char* out = (char*)malloc(l + 3); // for extra ./ + strcpy(p, path); + + size_t rl = 0; + + DIR* d; + char* c; + + #if defined(__SWITCH__) || defined(PSP2) + if( (c = strstr(p, ":/")) != NULL) // scheme used by some environments, eg. switch, vita + { + size_t deviceNameOffset = c - p + 3; + char* deviceNamePath = (char*)alloca(deviceNameOffset + 1); + strlcpy(deviceNamePath, p, deviceNameOffset); + deviceNamePath[deviceNameOffset] = 0; + d = opendir(deviceNamePath); + p = c + 1; + } + else + #endif + if (p[0] == '/' || p[0] == '\\') + { + d = opendir("/"); + } + else + { + d = opendir("."); + out[0] = '.'; + out[1] = 0; + rl = 1; + } + + bool cantProceed = false; // just convert slashes in what's left in string, don't correct case of letters(because we can't) + bool mayBeTrailingSlash = false; + + while (c = strsep(&p, "/\\")) + { + // May be trailing slash(allow), slash at the start(avoid), or multiple slashes(avoid) + if (*c == '\0') + { + mayBeTrailingSlash = true; + continue; + } else { + mayBeTrailingSlash = false; + } + + out[rl] = '/'; + rl += 1; + out[rl] = 0; + + if (cantProceed) + { + strcpy(out + rl, c); + rl += strlen(c); + continue; + } + + struct dirent* e; + while (e = readdir(d)) + { + if (strcasecmp(c, e->d_name) == 0) + { + strcpy(out + rl, e->d_name); + int reportedLen = (int)strlen(e->d_name); + rl += reportedLen; + assert(reportedLen == strlen(c) && "casepath: This is not good at all"); + + closedir(d); + d = opendir(out); + + // Either it wasn't a folder, or permission error, I/O error etc. + if (!d) { + cantProceed = true; + } + + break; + } + } + + if (!e) + { + printf("casepath couldn't find dir/file \"%s\", full path was %s\n", c, path); + // No match, add original name and continue converting further slashes. + strcpy(out + rl, c); + rl += strlen(c); + cantProceed = true; + } + } + + if (d) closedir(d); + if (mayBeTrailingSlash) { + out[rl] = '/'; rl += 1; + out[rl] = '\0'; + } + + if (rl > l + 2) { + printf("\n\ncasepath: Corrected path length is longer then original+2:\n\tOriginal: %s (%zu chars)\n\tCorrected: %s (%zu chars)\n\n", path, l, out, rl); + } + return out; +} + +FILE* fcaseopen(char const* filename, char const* mode) +{ + FILE* result; + char* real = casepath(filename); + if (!real) + result = fopen(filename, mode); + else { + result = fopen(real, mode); + free(real); + } + return result; +} +#endif + +int main(int argc, const char** argv) { + + size_t table_len = 0; + const char (* table)[25] = nullptr; + + if (argc != 4) { + std::cerr << "Usage: " << argv[0] << "liberty|miami " << std::endl; + return 1; + } + + if (strcmp(argv[1], "miami") == 0) { + table = DCStreamedNameTable_miami; + table_len = sizeof(DCStreamedNameTable_miami)/sizeof(DCStreamedNameTable_miami[0]); + } else if (strcmp(argv[1], "liberty") == 0) { + table = DCStreamedNameTable_liberty; + table_len = sizeof(DCStreamedNameTable_liberty)/sizeof(DCStreamedNameTable_liberty[0]); + } else { + std::cerr << "Invalid game name" << std::endl; + return 1; + } + + FILE* fout=fcaseopen(argv[3], "wb"); + if (!fout) { + std::cerr << "Failed to open " << argv[3] << " for writing" << std::endl; + return 1; + } + + for (int i = 0; i < table_len; i++) { + std::string filename = std::string(argv[2]) + "/" + table[i]; + + FILE* f = fcaseopen(filename.c_str(), "rb"); + if (!f) { + std::cerr << "Failed to open " << filename << std::endl; + fclose(fout); + return 1; + } + WavHeader hdr; + auto rv = fread(&hdr, sizeof(hdr), 1, f); + if (rv != 1) { + std::cerr << "Failed to read header from " << filename << std::endl; + fclose(f); + fclose(fout); + return 1; + } + + uint64_t sl64 = (uint64_t)hdr.dataSize * 2000 / hdr.numOfChan / hdr.samplesPerSec; + + assert(sl64 <= INT32_MAX); + + int32_t sl32 = (int32_t)sl64; + + rv = fwrite(&sl32, sizeof(sl32), 1, fout); + if (rv != 1) { + std::cerr << "Failed to write to " << argv[2] << std::endl; + fclose(f); + fclose(fout); + return 1; + } + fclose(f); + } + fclose(fout); + + return 0; +} \ No newline at end of file diff --git a/dreamcast/texconv.cpp b/src/tools/texconv.cpp similarity index 79% rename from dreamcast/texconv.cpp rename to src/tools/texconv.cpp index 286d3eb2..fd281a1a 100644 --- a/dreamcast/texconv.cpp +++ b/src/tools/texconv.cpp @@ -3,6 +3,7 @@ #include #include +#include #include using namespace std; @@ -51,6 +52,7 @@ void pvrRegWrite(uint32 A, uint32 D) {} uint32_t pvr_map32(uint32_t offset32) {return 0;} void Hackpresent() { } void re3RemoveLeastUsedModel() { assert(false); } +void re3EmergencyRemoveModel() { assert(false); } void RwTexDictionaryGtaStreamRead1(rw::Stream*){ assert(false); } void RwTexDictionaryGtaStreamRead2(rw::Stream*, rw::TexDictionary*) { assert(false); } void pvr_ta_data(void* data, int size) { @@ -238,6 +240,40 @@ PluginAttach(void) const char* currentFile; +namespace rw { + Image* readBMP(const char *filename); + Image* readPNG(const char *filename); +} + +void InsertImage(RwTexDictionary* texDict, const char* file, const char* texName) { + RwTexture *tex; + RwRaster *raster; + RwInt32 width, height, depth, format; + RwImage *image = rw::readBMP(file); + if (!image) { + image = rw::readPNG(file); + } + + assert(image); + + RwImageFindRasterFormat(image, rwRASTERTYPETEXTURE, &width, &height, &depth, &format); + raster = RwRasterCreate(width, height, depth, format); + RwRasterSetFromImage(raster, image); + + tex = RwTextureCreate(raster); + RwTextureSetName(tex, texName); + + RwTextureSetFilterMode(tex, rwFILTERLINEAR); + + RwTexDictionaryAddTexture(texDict, tex); + + RwImageDestroy(image); +} + +std::vector> ImagesToAdd; +std::vector ImagesToRemove; +bool listTextures = false; + int main(int argc, const char** argv) { if (argc >= 5) { int width = atoi(argv[3]); @@ -247,7 +283,7 @@ int main(int argc, const char** argv) { if(height >= 16 && height <= 1024) rw::dc::maxRasterHeight = height; } - for (int i = 0; i < argc; i++) { + for (int i = 5; i < argc; i++) { if (argv[i] != nullptr) { // Downsample Parameter if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "-D") == 0) { @@ -271,6 +307,22 @@ int main(int argc, const char** argv) { } } } + + if (strcmp(argv[i], "--include-tex") == 0) { + assert(i + 2 < argc); + ImagesToAdd.emplace_back(argv[i+1], argv[i+2]); + i += 2; + } + + if (strcmp(argv[i], "--delete-tex") == 0) { + assert(i + 1 < argc); + ImagesToRemove.emplace_back(argv[i+1]); + i += 1; + } + + if (strcmp(argv[i], "--list-tex") == 0) { + listTextures = true; + } } } @@ -298,6 +350,31 @@ int main(int argc, const char** argv) { RwStreamClose(stream, nil); + if (listTextures) { + fprintf(stdout, "Incoming textures:\n"); + FORLIST(lnk, texDict->textures) { + auto tex = rw::Texture::fromDict(lnk); + fprintf(stdout, "texture: '%s'\n", tex->name); + } + } + + for (auto&& removedTextureName: ImagesToRemove) { + auto removedTexture = texDict->find(removedTextureName); + assert(removedTexture); + texDict->remove(removedTexture); + } + for (auto&& extraTexture: ImagesToAdd) { + InsertImage(texDict, extraTexture.first, extraTexture.second); + } + + if (listTextures) { + fprintf(stdout, "Processing textures:\n"); + FORLIST(lnk, texDict->textures) { + auto tex = rw::Texture::fromDict(lnk); + fprintf(stdout, "texture: '%s'\n", tex->name); + } + } + auto streamOut = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMWRITE, argv[2]); assert(streamOut && "failed to open output"); diff --git a/vendor/dc-load-ser/loader.elf b/vendor/dc-load-ser/loader.elf new file mode 100755 index 00000000..ce3e146f Binary files /dev/null and b/vendor/dc-load-ser/loader.elf differ diff --git a/vendor/dca3-kos b/vendor/dca3-kos index 6e9ab783..bf330bec 160000 --- a/vendor/dca3-kos +++ b/vendor/dca3-kos @@ -1 +1 @@ -Subproject commit 6e9ab783f3861438128c80e786c2fd9cefb10968 +Subproject commit bf330beca26711cb2fcfb348c2451e8b0c3b40d8 diff --git a/vendor/emu/refsw/refsw_lists.cpp b/vendor/emu/refsw/refsw_lists.cpp index afe444e8..90a6e720 100644 --- a/vendor/emu/refsw/refsw_lists.cpp +++ b/vendor/emu/refsw/refsw_lists.cpp @@ -430,7 +430,7 @@ void RenderCORE() { auto base = (interlace && field) ? FB_W_SOF2 : FB_W_SOF1; // very few configurations supported here - verify(SCALER_CTL.hscale == 0); + size_t xpixels = SCALER_CTL.hscale ? 16 : 32; verify(SCALER_CTL.interlace == 0); // write both SOFs auto vscale = SCALER_CTL.vscalefactor; verify(vscale == 0x401 || vscale == 0x400 || vscale == 0x800); @@ -440,14 +440,14 @@ void RenderCORE() { auto src = copy; auto bpp = fb_packmode == 0x1 ? 2 : 4; - auto offset_bytes = entry.control.tilex * 32 * bpp + entry.control.tiley * 32 * FB_W_LINESTRIDE.stride * 8; + auto offset_bytes = entry.control.tilex * xpixels * bpp + entry.control.tiley * 32 * FB_W_LINESTRIDE.stride * 8; for (int y = 0; y < 32; y++) { //auto base = (y&1) ? FB_W_SOF2 : FB_W_SOF1; auto dst = base + offset_bytes + (y)*FB_W_LINESTRIDE.stride * 8; - for (int x = 0; x < 32; x++) + for (int x = 0; x < xpixels; x++) { if (fb_packmode == 0x1) { auto pixel = (((src[0] >> 3) & 0x1F) << 0) | (((src[1] >> 2) & 0x3F) << 5) | (((src[2] >> 3) & 0x1F) << 11); @@ -461,6 +461,11 @@ void RenderCORE() { dst += bpp; src += 4; // skip alpha + + // TODO: Actually do AA + if (SCALER_CTL.hscale) { + src += 4; + } } } } diff --git a/vendor/koshle/hlekos.cpp b/vendor/koshle/hlekos.cpp index 50f934f6..ef1f66c7 100644 --- a/vendor/koshle/hlekos.cpp +++ b/vendor/koshle/hlekos.cpp @@ -28,10 +28,6 @@ maple_device_t * maple_enum_type(int n, uint32 func) { int sem_wait_timed(semaphore_t *sem, int timeout) { auto count = sem->count.load(); - // This is a hack til vlbanks are raised in a nicer way - Hackpresent(); - pvr_queue_interrupt(ASIC_EVT_PVR_VBLANK_BEGIN); - while(count <= 0 || !sem->count.compare_exchange_strong(count, count-1)) { count = sem->count.load(); } diff --git a/vendor/koshle/hlematrix3d.cpp b/vendor/koshle/hlematrix3d.cpp index caa62509..af5530cc 100644 --- a/vendor/koshle/hlematrix3d.cpp +++ b/vendor/koshle/hlematrix3d.cpp @@ -9,10 +9,10 @@ */ -#include "common.h" #include #include #include +#include matrix_t XMTRX = { { 0.0f, 0.0f, 0.0f, 0.0f }, diff --git a/vendor/koshle/hlepvr_scene.cpp b/vendor/koshle/hlepvr_scene.cpp index ca619f90..269ae5be 100644 --- a/vendor/koshle/hlepvr_scene.cpp +++ b/vendor/koshle/hlepvr_scene.cpp @@ -123,7 +123,7 @@ void pvr_scene_begin(void) { /* Currently the resize functionality is not implemented, so make sure that rx and ry are appropriate (i.e. *rx = 1024 and *ry = 512 for 640x480). Also, note that this probably won't work with DMA mode for now... */ -void pvr_scene_begin_txr(pvr_ptr_t txr, uint32 *rx, uint32 *ry) { +void pvr_scene_begin_txr(pvr_ptr_t txr, uint32_t *rx, uint32_t *ry) { int buf = pvr_state.view_target ^ 1; (void)ry; @@ -340,11 +340,22 @@ int pvr_scene_finish(void) { return 0; } +#include "emu/emu.h" +#include "dc/asic.h" +#include "refsw/refsw_tile.h" + + int pvr_wait_ready(void) { int t; assert(pvr_state.valid); + // This is a hack til vlbanks are raised in a nicer way + if (!pvr_state.to_texture[pvr_state.view_target^1]) { + Hackpresent(); + } + pvr_queue_interrupt(ASIC_EVT_PVR_VBLANK_BEGIN); + t = sem_wait_timed((semaphore_t *)&pvr_state.ready_sem, 100); if(t < 0) { diff --git a/vendor/librw/src/camera.cpp b/vendor/librw/src/camera.cpp index 34cb8213..b0204585 100644 --- a/vendor/librw/src/camera.cpp +++ b/vendor/librw/src/camera.cpp @@ -440,6 +440,50 @@ Camera::frustumTestSphere(const Sphere *s) const return res; } +int32 +Camera::frustumTestSphereNear(const Sphere *s) const +{ + int32 res = SPHEREINSIDE; + const FrustumPlane *p = this->frustumPlanes; + + // far + float32 dist = dot(p->plane.normal, s->center) - p->plane.distance; + if(s->radius < dist) + return SPHEREOUTSIDE; + p++; + + // near + dist = dot(p->plane.normal, s->center) - p->plane.distance; + if(s->radius < dist) + return SPHEREOUTSIDE; + if(s->radius > -dist) + res = SPHEREBOUNDARY_NEAR; + p++; + + // others + dist = dot(p->plane.normal, s->center) - p->plane.distance; + if(s->radius < dist) + return SPHEREOUTSIDE; + p++; + + dist = dot(p->plane.normal, s->center) - p->plane.distance; + if(s->radius < dist) + return SPHEREOUTSIDE; + p++; + + dist = dot(p->plane.normal, s->center) - p->plane.distance; + if(s->radius < dist) + return SPHEREOUTSIDE; + p++; + + dist = dot(p->plane.normal, s->center) - p->plane.distance; + if(s->radius < dist) + return SPHEREOUTSIDE; + p++; + + return res; +} + struct CameraChunkData { V2d viewWindow; diff --git a/src/core/common_defines.h b/vendor/librw/src/common_defines.h similarity index 100% rename from src/core/common_defines.h rename to vendor/librw/src/common_defines.h diff --git a/vendor/librw/src/dc/rwdc.cpp b/vendor/librw/src/dc/rwdc.cpp index fd5b47aa..48549bc0 100644 --- a/vendor/librw/src/dc/rwdc.cpp +++ b/vendor/librw/src/dc/rwdc.cpp @@ -16,7 +16,7 @@ extern const char* currentFile; #define texconvf(...) // printf(__VA_ARGS__) #endif -#include "../../../src/vmu/vmu.h" +#include "vmu/vmu.h" #include "../rwbase.h" #include "../rwerror.h" #include "../rwplg.h" @@ -37,12 +37,15 @@ extern const char* currentFile; #include #include +#define errorf(...) dbglog(DBG_CRITICAL, __VA_ARGS__) #define logf(...) // printf(__VA_ARGS__) bool re3RemoveLeastUsedModel(); +bool re3EmergencyRemoveModel(); // #include "rwdcimpl.h" #include +#include #include "alloc.h" #undef PVR_TXRFMT_STRIDE @@ -167,18 +170,15 @@ static_assert(alignof(pvr_vertex16_t) == 32, "pvr_vertex16_t alignof mismatch"); #define MATH_Fast_Invert(x) ({ (((x) < 0.0f)? -1.0f : 1.0f) * frsqrt((x) * (x)); }) -#define logf(...) // printf(__VA_ARGS__) - static pvr_dr_state_t drState; #include +float VIDEO_MODE_SCALE_X; + #if !defined(DC_TEXCONV) && !defined(DC_SIM) #include -#define VIDEO_MODE_WIDTH vid_mode->width -#define VIDEO_MODE_HEIGHT vid_mode->height - #define mat_trans_nodiv_nomod(x, y, z, x2, y2, z2, w2) do { \ register float __x __asm__("fr12") = (x); \ register float __y __asm__("fr13") = (y); \ @@ -288,8 +288,6 @@ void rw_mat_load_4x4(rw::Matrix* mtx) { } #include -#define VIDEO_MODE_WIDTH 640 -#define VIDEO_MODE_HEIGHT 480 #define frsqrt(a) (1.0f/sqrt(a)) #define dcache_pref_block(a) __builtin_prefetch(a) @@ -305,6 +303,8 @@ void rw_mat_load_4x4(rw::Matrix* mtx) { #define mat_identity(a) #define pvr_fog_table_color(a,r,g,b) #define pvr_fog_table_linear(s,e) +#define pvr_fog_table_exp(d) +#define pvr_fog_table_custom(d) #endif #define mat_trans_single3_nomod(x_, y_, z_, x2, y2, z2) do { \ @@ -416,6 +416,66 @@ void leave_oix_() { #endif } +void enter_ocr_() { + #if defined(DC_SH4) + auto mask = irq_disable(); + dcache_purge_all(); + volatile uint32_t * CCN_CCR = (uint32_t *)0xFF00001C; + *CCN_CCR |= (1 << 5); // enable OCR (ORA) + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + + irq_restore(mask); + #endif +} + +void leave_ocr_() { + #if defined(DC_SH4) + auto mask = irq_disable(); + dcache_inval_range(0x92000000, 8192); + dcache_purge_all(); + volatile uint32_t * CCN_CCR = (uint32_t *)0xFF00001C; + *CCN_CCR &= ~( 1 << 5); // disable OCR (ORA) + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + __asm__ __volatile__ ("nop"); + irq_restore(mask); + #endif +} + #if defined(DC_SH4) #define FLUSH_TA_DATA(src) do { __asm__ __volatile__("ocbwb @%0" : : "r" (src) : "memory"); } while(0) #else @@ -483,7 +543,7 @@ void DCE_MatrixViewport(float x, float y, float width, float height) { DCE_MAT_SCREENVIEW[1][1] = height * 0.5f; DCE_MAT_SCREENVIEW[2][2] = 1; DCE_MAT_SCREENVIEW[3][0] = -DCE_MAT_SCREENVIEW[0][0] + x; - DCE_MAT_SCREENVIEW[3][1] = VIDEO_MODE_HEIGHT - (DCE_MAT_SCREENVIEW[1][1] + y); + DCE_MAT_SCREENVIEW[3][1] = height - (DCE_MAT_SCREENVIEW[1][1] + y); } void DCE_InitMatrices() { @@ -491,8 +551,6 @@ void DCE_InitMatrices() { mat_identity(); mat_store(&DCE_MAT_SCREENVIEW); - - DCE_MatrixViewport(0, 0, VIDEO_MODE_WIDTH, VIDEO_MODE_HEIGHT); } } @@ -663,7 +721,10 @@ void malloc_stats() { } #define UNIMPL_LOGV(...) #endif +Camera* rwdcCam; + void beginUpdate(Camera* cam) { + rwdcCam = cam; float view[16], proj[16]; // View Matrix @@ -723,6 +784,8 @@ void beginUpdate(Camera* cam) { proj[14] = -cam->nearPlane*proj[10]; memcpy4(&cam->devProj, proj, sizeof(RawMatrix)); + DCE_MatrixViewport(0, 0, cam->frameBuffer->width * VIDEO_MODE_SCALE_X, cam->frameBuffer->height); + mat_load((matrix_t*)&DCE_MAT_SCREENVIEW); mat_apply((matrix_t*)&cam->devProj); mat_store((matrix_t*)&cam->devProjScreen); @@ -763,7 +826,7 @@ void dcMotionBlur_v1(uint8_t a, uint8_t r, uint8_t g, uint8_t b) { auto doquad = [=](float x, float y, float w, float h, float tx, float ty, float tw, float th) { auto vtx = reinterpret_cast(pvr_dr_target(drState)); vtx->flags = PVR_CMD_VERTEX; - vtx->x = x; + vtx->x = x * VIDEO_MODE_SCALE_X; vtx->y = y; vtx->z = 1000000.0f; vtx->u = tx/1024.f; @@ -773,7 +836,7 @@ void dcMotionBlur_v1(uint8_t a, uint8_t r, uint8_t g, uint8_t b) { vtx = reinterpret_cast(pvr_dr_target(drState)); vtx->flags = PVR_CMD_VERTEX; - vtx->x = x+w; + vtx->x = (x+w) * VIDEO_MODE_SCALE_X; vtx->y = y; vtx->z = 1000000.0f; vtx->u = (tx+tw)/1024.f; @@ -783,7 +846,7 @@ void dcMotionBlur_v1(uint8_t a, uint8_t r, uint8_t g, uint8_t b) { vtx = reinterpret_cast(pvr_dr_target(drState)); vtx->flags = PVR_CMD_VERTEX; - vtx->x = x; + vtx->x = x * VIDEO_MODE_SCALE_X; vtx->y = y+h; vtx->z = 1000000.0f; vtx->u = tx/1024.f; @@ -793,7 +856,7 @@ void dcMotionBlur_v1(uint8_t a, uint8_t r, uint8_t g, uint8_t b) { vtx = reinterpret_cast(pvr_dr_target(drState)); vtx->flags = PVR_CMD_VERTEX_EOL; - vtx->x = x+w; + vtx->x = (x+w) * VIDEO_MODE_SCALE_X; vtx->y = y+h; vtx->z = 1000000.0f; vtx->u = (tx+tw)/1024.f; @@ -867,7 +930,7 @@ void dcMotionBlur_v3(uint8_t a, uint8_t r, uint8_t g, uint8_t b) { float umin, float umax, float vmin, float vmax, uint32_t col) { auto vtx = reinterpret_cast(pvr_dr_target(drState)); vtx->flags = PVR_CMD_VERTEX; - vtx->x = x; + vtx->x = x * VIDEO_MODE_SCALE_X; vtx->y = y; vtx->z = z; vtx->u = umin; @@ -877,7 +940,7 @@ void dcMotionBlur_v3(uint8_t a, uint8_t r, uint8_t g, uint8_t b) { vtx = reinterpret_cast(pvr_dr_target(drState)); vtx->flags = PVR_CMD_VERTEX; - vtx->x = x+w; + vtx->x = (x+w) * VIDEO_MODE_SCALE_X; vtx->y = y; vtx->z = z; vtx->u = umax; @@ -887,7 +950,7 @@ void dcMotionBlur_v3(uint8_t a, uint8_t r, uint8_t g, uint8_t b) { vtx = reinterpret_cast(pvr_dr_target(drState)); vtx->flags = PVR_CMD_VERTEX; - vtx->x = x; + vtx->x = x * VIDEO_MODE_SCALE_X; vtx->y = y+h; vtx->z = z; vtx->u = umin; @@ -897,7 +960,7 @@ void dcMotionBlur_v3(uint8_t a, uint8_t r, uint8_t g, uint8_t b) { vtx = reinterpret_cast(pvr_dr_target(drState)); vtx->flags = PVR_CMD_VERTEX_EOL; - vtx->x = x+w; + vtx->x = (x+w) * VIDEO_MODE_SCALE_X; vtx->y = y+h; vtx->z = z; vtx->u = umax; @@ -1012,15 +1075,29 @@ void dcMotionBlur_v3(uint8_t a, uint8_t r, uint8_t g, uint8_t b) { void allocDefrag(int iterations); -void endUpdate(Camera*) { +void endUpdate(Camera* cam) { + // TODO: Fix KOS RTT instead + if (cam->frameBuffer->type != Raster::CAMERATEXTURE) { #if !defined(DC_SIM) && defined(SKIP_FRAMES) if (pvr_check_ready() >= 0) #endif { pvr_set_zclip(0.0f); pvr_wait_ready(); - pvr_scene_begin(); + pvr_set_bg_color(cam->clearColor.red / 255.0f, cam->clearColor.green / 255.0f, cam->clearColor.blue / 255.0f); + + if (cam->frameBuffer->type == Raster::CAMERATEXTURE) { + auto natras = GETDCRASTEREXT(cam->frameBuffer); + uint32 rx = cam->frameBuffer->width; + uint32 ry = cam->frameBuffer->height; + pvr_scene_begin_txr(natras->raster->texaddr, &rx, &ry); + } else if (cam->frameBuffer->type == Raster::CAMERA) { + pvr_scene_begin(); + } else { + assert(false && "invalid cam->frameBuffer type"); + } + pvr_dr_init(&drState); pvr_list_begin(PVR_LIST_OP_POLY); enter_oix(); @@ -1031,7 +1108,7 @@ void endUpdate(Camera*) { } pvr_list_finish(); if (ptCallbacks.size()) { - PVR_SET(0x11C, 128); // PT Alpha test value + PVR_SET(0x11C, 64); // PT Alpha test value pvr_dr_init(&drState); pvr_list_begin(PVR_LIST_PT_POLY); for (auto&& cb: ptCallbacks) { @@ -1058,6 +1135,7 @@ void endUpdate(Camera*) { pvr_scene_finish(); leave_oix(); } + } opCallbacks.clear(); ptCallbacks.clear(); blendCallbacks.clear(); @@ -1067,7 +1145,10 @@ void endUpdate(Camera*) { matfxContexts.clear(); } -void clearCamera(Camera*,RGBA*,uint32) { +void clearCamera(Camera* cam,RGBA* col,uint32 flags) { + if (flags & rwCAMERACLEARIMAGE) { + cam->clearColor = *col; + } UNIMPL_LOG(); } @@ -1098,6 +1179,7 @@ static bool doAlphaTest; static uint8_t fogFuncPvr = PVR_FOG_DISABLE; static uint32_t fogColor = 0; +static float fogStart = 0.0f; static uint32 cullModePvr; static inline unsigned pvrCullMode(uint32_t cullMode) { @@ -1198,8 +1280,9 @@ setRenderState(int32 state, void *pvalue) fogFuncPvr = value ? PVR_FOG_TABLE : PVR_FOG_DISABLE; break; case FOGCOLOR: +#if !defined(DC_TEXCONV) // Set fog color when state changes - if(fogColor != value) { + if(fogColor != value || fogStart != RwCameraGetFogDistance(rwdcCam)) { fogColor = value; RGBA c; c.red = value; @@ -1207,9 +1290,22 @@ setRenderState(int32 state, void *pvalue) c.blue = value>>16; c.alpha = value>>24; pvr_fog_table_color(c.alpha / 255.0f, c.red / 255.0f, c.green / 255.0f, c.blue / 255.0f); - pvr_fog_table_linear(50.0f, 450.0f); + + fogStart = RwCameraGetFogDistance(rwdcCam); + float fogEnd = RwCameraGetFarClipPlane(rwdcCam); + float fogIntensity[129]; + uint8_t idx = 0; + float startIntensity = (-fogStart) / (fogEnd - fogStart); //interpolate between start and end to get initial intensity + float step = (1.0f - startIntensity) / 129; // we have 129 entries, create a step such that start + (step*129) = 1.0 + for(int i = 128; i >= 0; i--) { + fogIntensity[i] = startIntensity + (idx++ * step); + } + pvr_fog_far_depth(fogEnd); + pvr_fog_table_custom(fogIntensity); } +#endif break; + // case CULLMODE: // if(rwStateCache.cullmode != value){ // rwStateCache.cullmode = value; @@ -1407,7 +1503,7 @@ void im2DRenderPrimitive(PrimitiveType primType, void *vertices, int32_t numVert { auto *pvrVert = pvr_dr_target(drState); pvrVert->flags = flags; - pvrVert->x = gtaVert.x; + pvrVert->x = gtaVert.x * VIDEO_MODE_SCALE_X; pvrVert->y = gtaVert.y; pvrVert->z = MATH_Fast_Invert(gtaVert.w); // this is perfect for almost every case... pvrVert->u = gtaVert.u; @@ -2342,6 +2438,36 @@ __attribute__ ((noinline)) void submitMeshlet(uint8_t* OCR, const int8_t* indexD } while(--indexCount); } +template +__attribute__ ((noinline)) void submitMeshletFallback(uint8_t* OCR, const int8_t* indexData, uint32_t indexCount) { + struct SQBUF { + union { + uint32_t flags; + uint64_t data[4]; + uint8_t data8[32]; + }; + }; + + SQBUF* sq = (SQBUF*)pvr_dr_target(drState); + + static_assert(sizeof(SQBUF) == 32); + + do { + auto idx = *indexData++; + auto flags = idx & 0x80 ? PVR_CMD_VERTEX_EOL : PVR_CMD_VERTEX; + auto lookup_idx = idx & 0x7F; + + auto src = (SQBUF*)(OCR + lookup_idx * 64); + src[0].flags = flags; + *sq = src[0]; + pvr_dr_commit(sq); + if (textured) { + *sq = src[1]; + pvr_dr_commit(sq); + } + } while(--indexCount); +} + #if defined(DC_SH4) template<> @@ -2421,7 +2547,7 @@ __attribute__ ((noinline)) void submitMeshlet(uint8_t* OCR, const int8_t* // 8 kb in total #if defined(DC_SH4) -uint8_t* OCR_SPACE = (uint8_t*)0x92000000; +uint8_t* OCR_SPACE; #else uint8_t OCR_SPACE[32 * 256] __attribute__((aligned(32))); #endif @@ -2652,6 +2778,159 @@ __attribute__ ((noinline)) void clipAndsubmitMeshlet(uint8_t* vertexData, const } }; } while(indexCount != 0); + + #undef FILLVERT + #undef SUBMIT_VTX + #undef SUBMIT_INTERPOLATE +} + + +template +__attribute__ ((noinline)) void clipAndsubmitMeshletFallback(uint8_t* vertexData, const int8_t* indexData, uint32_t indexCount) { + + struct SQBUF { + union { + uint32_t flags; + uint64_t data[4]; + uint8_t data8[32]; + }; + }; + + static_assert(sizeof(SQBUF) == 32); + + SQBUF* sq = (SQBUF*)pvr_dr_target(drState); + + constexpr int8_t VERTEX = 0; + constexpr int8_t VERTEX_EOL = 0x80; + + #define FILLVERT(n) \ + do { \ + auto idx = *indexData++; \ + auto local_idx = idx & 0x7f; \ + eol_now = idx & 0x80; \ + auto local_ptr = (vertexData + local_idx * 64); \ + vpp[n] = local_ptr; \ + auto v = (const pvr_vertex64_t*)local_ptr; \ + vismask >>= 1; \ + if((textured?v->tex_z:v->o_b) >= -v->o_g) vismask |= 0b100; \ + indexCount--; \ + currentCount++; \ + } while(0) + + #define SUBMIT_VTX(vid, eolf) \ + do { \ + auto src = (SQBUF*) vpp[vid]; \ + src[0].flags = eolf ? PVR_CMD_VERTEX_EOL : PVR_CMD_VERTEX; \ + *sq = src[0]; \ + pvr_dr_commit(sq); \ + if (textured) { \ + *sq = src[1]; \ + pvr_dr_commit(sq); \ + } \ + } while(0) + + #define SUBMIT_INTERPOLATE(vid1, vid2, eolf) \ + do { \ + sq = (SQBUF*)interpolateAndSubmit(sq, vpp[vid1], vpp[vid2], eolf ? PVR_CMD_VERTEX_EOL : PVR_CMD_VERTEX); \ + } while(0) + + uint32_t vismask = 0; + + uint8_t* vpp[3]; + + int8_t eol = 0; + int8_t eol_now = 0; + + do { + uint32_t currentCount = -1; + + FILLVERT(0); + FILLVERT(1); + FILLVERT(2); + + if (vismask & 1) { + SUBMIT_VTX(0, VERTEX); + if (vismask & 2) { + // both first verts visible + SUBMIT_VTX(1, VERTEX); + } else { + // 0 visible, 1 hidden + SUBMIT_INTERPOLATE(0, 1, VERTEX); + } + } else if (vismask & 2) { + // 0 hidden, 1 visible + SUBMIT_INTERPOLATE(1, 0, VERTEX); + SUBMIT_VTX(1, VERTEX); + } + + eol = 0; + // each remaining vertex of the strip + while(!eol) { + // "ring buffery" indices + uint8_t vertZeroIdx = (currentCount - 2) % 3; + uint8_t vertOneIdx = (currentCount - 1) % 3; + uint8_t vertTwoIdx = currentCount % 3; + //dcache_pref_block(&vph[vertZeroIdx]); not sure where to put this honestly -jaxyn + + eol = eol_now; + + if (!vismask) { + if (!eol) { + // "ring buffery" filling + FILLVERT(vertZeroIdx); + } + continue; + } + + if (vismask == 7) { + // all visible + SUBMIT_VTX(vertTwoIdx, eol); + if (!eol) { + // "ring buffery" filling + FILLVERT(vertZeroIdx); + } + continue; + } + + switch (vismask) { + case 1: // 0 visible, 1 and 2 hidden + // pause strip + SUBMIT_INTERPOLATE(vertZeroIdx, vertTwoIdx, VERTEX_EOL); + break; + case 3: // 0 and 1 visible, 2 hidden + SUBMIT_INTERPOLATE(vertZeroIdx, vertTwoIdx, VERTEX); + SUBMIT_VTX(vertOneIdx, VERTEX); + case 2: // 0 hidden, 1 visible, 2 hidden + SUBMIT_INTERPOLATE(vertOneIdx, vertTwoIdx, eol); + break; + case 4: // 0 and 1 hidden, 2 visible + SUBMIT_INTERPOLATE(vertTwoIdx, vertZeroIdx, VERTEX); + if (currentCount & 0x01) { // flip directionality + case 5: // 0 visible, 1 hidden, 2 visible + SUBMIT_VTX(vertTwoIdx, VERTEX); + } + SUBMIT_INTERPOLATE(vertTwoIdx, vertOneIdx, VERTEX); + SUBMIT_VTX(vertTwoIdx, eol); + break; + case 6: // 0 hidden, 1 and 2 visible + SUBMIT_INTERPOLATE(vertTwoIdx, vertZeroIdx, VERTEX); + SUBMIT_VTX(vertOneIdx, VERTEX); + SUBMIT_VTX(vertTwoIdx, eol); + break; + default: + break; + } + + if (!eol) { + // "ring buffery" filling + FILLVERT(vertZeroIdx); + } + }; + } while(indexCount != 0); + + #undef FILLVERT + #undef SUBMIT_VTX + #undef SUBMIT_INTERPOLATE } @@ -2711,7 +2990,7 @@ void tnlMeshletSkinVertices(uint8_t *OCR, uint8_t *OCR_normal, const uint8_t* ve } while(--count != 0); } } else if (!(flags & 0x80)) { - int count = flags & 0x7FFF; + int count = (flags & 0x7F) + 1; uint8_t* dstVertexBytes = dest + *skinningIndexData++; do { @@ -2802,7 +3081,7 @@ void tnlMeshletSkinVertices(uint8_t *OCR, uint8_t *OCR_normal, const uint8_t* ve } while(--count != 0); } } else if (!(flags & 0x80)) { - int count = flags & 0x7FFF; + int count = (flags & 0x7F) + 1; uint8_t* dstNormalBytes = destNormal + *skinningIndexData++; do { @@ -2878,9 +3157,9 @@ void tnlMeshletEnvMap(uint8_t* OCR, uint8_t* normal, int vertexCount, int vertex } -inline __attribute__((always_inline)) RwFrustumTestResult AtomicFrustumSphereCB(Atomic *atomic, rw::Camera *cam) +inline __attribute__((always_inline)) int32 AtomicFrustumSphereNearCB(Atomic *atomic, rw::Camera *cam) { - return RwCameraFrustumTestSphere(cam, atomic->getWorldBoundingSphere()); + return cam->frustumTestSphereNear(atomic->getWorldBoundingSphere()); } static constexpr void (*tnlMeshletTransformSelector[6])(uint8_t* dst, const uint8_t* vertexData, uint32_t vertexCount, uint32_t vertexSize) { @@ -2918,16 +3197,26 @@ static constexpr void (*tnlMeshletDiffuseColorSelector[8])(uint8_t* dstCol, cons &tnlMeshletDiffuseColor<4, true>, }; -static constexpr void (*submitMeshletSelector[2])(uint8_t* OCR, const int8_t* indexData, uint32_t indexCount) = { +static void (*submitMeshletSelector[2])(uint8_t* OCR, const int8_t* indexData, uint32_t indexCount) = { &submitMeshlet, &submitMeshlet, }; -static constexpr void (*clipAndsubmitMeshletSelector[2])(uint8_t* OCR, const int8_t* indexData, uint32_t indexCount) = { +static void (*submitMeshletSelectorFallback[2])(uint8_t* OCR, const int8_t* indexData, uint32_t indexCount) = { + &submitMeshletFallback, + &submitMeshletFallback, +}; + +static void (*clipAndsubmitMeshletSelector[2])(uint8_t* OCR, const int8_t* indexData, uint32_t indexCount) = { &clipAndsubmitMeshlet, &clipAndsubmitMeshlet, }; +static void (*clipAndsubmitMeshletSelectorFallback[2])(uint8_t* OCR, const int8_t* indexData, uint32_t indexCount) = { + &clipAndsubmitMeshletFallback, + &clipAndsubmitMeshletFallback, +}; + static constexpr void(*tnlMeshletSkinVerticesSelector[4])(uint8_t *OCR, uint8_t *OCR_normal, const uint8_t* vertex, const uint8_t* normals, const uint8_t* skinWeights, const uint8_t* skinIndexes, int vertexCount, int vertexSize, Matrix* skinMatrices) = { &tnlMeshletSkinVertices, &tnlMeshletSkinVertices, @@ -3219,11 +3508,20 @@ void pvr_poly_cxt_txr_fast(pvr_poly_hdr_t *hdr, pvr_list_t list, +size_t vertexBufferFree() { + size_t end = PVR_GET(PVR_TA_VERTBUF_END); + size_t pos = PVR_GET(PVR_TA_VERTBUF_POS); + + size_t free = end - pos; + + return free; +} + void defaultRenderCB(ObjPipeline *pipe, Atomic *atomic) { rw::Camera *cam = engine->currentCamera; // Frustum Culling - auto global_frustumTestResult = AtomicFrustumSphereCB(atomic, cam); + auto global_frustumTestResult = AtomicFrustumSphereNearCB(atomic, cam); if (global_frustumTestResult == rwSPHEREOUTSIDE) { return; @@ -3285,6 +3583,8 @@ void defaultRenderCB(ObjPipeline *pipe, Atomic *atomic) { for (int16_t n = 0; n < numMeshes; n++) { bool doBlend = meshes[n].material->color.alpha != 255; // TODO: check all vertexes for alpha? + bool doBlendMaterial = doBlend; + bool textured = geo->numTexCoordSets && meshes[n].material->texture; if (textured) { doBlend |= Raster::formatHasAlpha(meshes[n].material->texture->raster->format); @@ -3334,7 +3634,7 @@ void defaultRenderCB(ObjPipeline *pipe, Atomic *atomic) { pvr_poly_cxt_t cxt; int pvrList; if (doBlend || isMatFX) { - if (doAlphaTest) { + if (doAlphaTest && !doBlendMaterial) { pvrList = PVR_LIST_PT_POLY; } else { pvrList = PVR_LIST_TR_POLY; @@ -3400,6 +3700,9 @@ void defaultRenderCB(ObjPipeline *pipe, Atomic *atomic) { // clipping performed per meshlet auto renderCB = [contextId, n] { + if (vertexBufferFree() < (128 * 1024)) { + return; + } const atomic_context_t* acp = &atomicContexts[contextId]; auto geo = acp->geo; auto mesh = geo->meshHeader->getMeshes() + n; @@ -3456,25 +3759,21 @@ void defaultRenderCB(ObjPipeline *pipe, Atomic *atomic) { unsigned clippingRequired = 0; if (!global_needsNoClip) { - RwSphere sphere = meshlet->boundingSphere; - RwV3dTransformPoints(&sphere.center, &sphere.center, 1, atomic->getFrame()->getLTM()); - auto local_frustumTestResult = RwCameraFrustumTestSphere(cam, &sphere); - if ( local_frustumTestResult == rwSPHEREOUTSIDE) { - // printf("Outside frustum cull\n"); - continue; - } - - if (local_frustumTestResult == rwSPHEREBOUNDARY) { - // printf("meshlet %d, vertexOffset %d, indexOffset %d, vertexCount %d, indexCount %d\n", meshletNum, meshlet->vertexOffset, meshlet->indexOffset, meshlet->vertexCount, meshlet->indexCount); - mat_load(&worldView); // Number of cycles: ~11. + if (!skin) { + RwSphere sphere = meshlet->boundingSphere; + RwV3dTransformPoints(&sphere.center, &sphere.center, 1, atomic->getFrame()->getLTM()); - float x, y, z, w; - - mat_trans_nodiv_nomod(meshlet->boundingSphere.center.x, meshlet->boundingSphere.center.y, meshlet->boundingSphere.center.z, x, y, z, w); - - if (z < meshlet->boundingSphere.radius) { + auto local_frustumTestResult = cam->frustumTestSphereNear(&sphere);; + if ( local_frustumTestResult == Camera::SPHEREOUTSIDE) { + // printf("Outside frustum cull\n"); + continue; + } + + if (local_frustumTestResult == Camera::SPHEREBOUNDARY_NEAR) { clippingRequired = 1 + textured; } + } else { + clippingRequired = 1 + textured; } } @@ -3634,70 +3933,73 @@ void defaultRenderCB(ObjPipeline *pipe, Atomic *atomic) { } } } else if (geo->meshHeader->flags & rw::MeshHeader::TRISTRIP) { - auto numIndices = mesh->numIndices; - auto vertices = geo->morphTargets[0].vertices; - auto texcoords = geo->texCoords[0]; - auto colors = geo->colors; + if (geo->numVertices <= 128) { + auto numIndices = mesh->numIndices; + auto vertices = geo->morphTargets[0].vertices; + auto texcoords = geo->texCoords[0]; + auto colors = geo->colors; - assert(numIndices >= 3); - assert(geo->numVertices <= 128); - bool isPrelit = !!(geo->flags & Geometry::PRELIT); - assert(isPrelit); - assert(textured); - bool isNormaled = !!(geo->flags & Geometry::NORMALS); - assert(!isNormaled); + assert(numIndices >= 3); + bool isPrelit = !!(geo->flags & Geometry::PRELIT); + assert(isPrelit); + assert(textured); + bool isNormaled = !!(geo->flags & Geometry::NORMALS); + assert(!isNormaled); - std::vector indices(numIndices); - for (int i = 0; i < numIndices; i++) { - auto idx = mesh->indices[i]; - assert(idx < 128); - indices[i] = idx; + std::vector indices(numIndices); + for (int i = 0; i < numIndices; i++) { + auto idx = mesh->indices[i]; + assert(idx < 128); + indices[i] = idx; + } + indices.back() |= 0x80; + + pvr_vertex64_t *vd = (pvr_vertex64_t *)OCR_SPACE; + mat_load(&mtx); // Number of cycles: ~11 + + for (int idx = 0; idx < geo->numVertices; idx++) { + auto& vert = vertices[idx]; + auto& c = colors[idx]; + auto& t = texcoords[idx]; + + float x, y, z, w; + mat_trans_nodiv_nomod(vert.x, vert.y, vert.z, + x, y, z, w); + + vd->o_a = x; + vd->o_r = y; + vd->tex_z = z; + vd->o_g = w; + + w = frsqrt(w * w); + + vd->x = x * w; + vd->y = y * w; + vd->z = w; + + vd->a = c.alpha * (1/255.0f); + vd->r = c.red * (1/255.0f); + vd->g = c.green * (1/255.0f); + vd->b = c.blue * (1/255.0f); + + float16 u = texcoords[idx].u; + float16 v = texcoords[idx].v; + vd->u = u.raw; + vd->v = v.raw; + vd++; + } + + clipAndsubmitMeshletSelector[textured](OCR_SPACE, indices.data(), indices.size()); + } else { + // TODO: Fix this for large meshes (water in miami) } - indices.back() |= 0x80; - - pvr_vertex64_t *vd = (pvr_vertex64_t *)OCR_SPACE; - mat_load(&mtx); // Number of cycles: ~11 - - for (int idx = 0; idx < geo->numVertices; idx++) { - auto& vert = vertices[idx]; - auto& c = colors[idx]; - auto& t = texcoords[idx]; - - float x, y, z, w; - mat_trans_nodiv_nomod(vert.x, vert.y, vert.z, - x, y, z, w); - - vd->o_a = x; - vd->o_r = y; - vd->tex_z = z; - vd->o_g = w; - - w = frsqrt(w * w); - - vd->x = x * w; - vd->y = y * w; - vd->z = w; - - vd->a = c.alpha * (1/255.0f); - vd->r = c.red * (1/255.0f); - vd->g = c.green * (1/255.0f); - vd->b = c.blue * (1/255.0f); - - float16 u = texcoords[idx].u; - float16 v = texcoords[idx].v; - vd->u = u.raw; - vd->v = v.raw; - vd++; - } - - clipAndsubmitMeshletSelector[textured](OCR_SPACE, indices.data(), indices.size()); } else { // no trilist assets anymore assert(false && "Unsupported geometry type"); } }; if (doBlend || isMatFX) { - if (doAlphaTest) { + if (doAlphaTest && !doBlendMaterial) { ptCallbacks.emplace_back(std::move(renderCB)); } else { blendCallbacks.emplace_back(std::move(renderCB)); @@ -3728,6 +4030,10 @@ pvr_ptr_t allocTexture(DcRaster* ctx, size_t size) { break; } dbglog(DBG_CRITICAL, "failed to free or defrag vram, sz: %lu, cont: %lu, free: %lu\n", size, alloc_count_continuous(), alloc_count_free()); + if (re3EmergencyRemoveModel()) { + dbglog(DBG_CRITICAL, "Managed to re3EmergencyRemoveModel, sz: %lu, cont: %lu, free: %lu\n", size, alloc_count_continuous(), alloc_count_free()); + continue; + } return 0; } rv = alloc_malloc(ctx, size); @@ -3746,31 +4052,58 @@ rasterCreate(Raster* raster) { auto natras = GETDCRASTEREXT(raster); - if (raster->type != Raster::TEXTURE) { - printf("rasterCreate: unsupported type %d\n", raster->type); + if (raster->type != Raster::TEXTURE && raster->type != Raster::CAMERATEXTURE && raster->type != Raster::ZBUFFER) { + logf("rasterCreate: unsupported type %d\n", raster->type); } + if (raster->width < 8) { + logf("rasterCreate: Increasing width to 8 from %d\n", raster->width); + raster->width = 8; + } + + if (raster->height < 8) { + logf("rasterCreate: Increasing height to 8 from %d\n", raster->height); + raster->height = 8; + } + + if (raster->type == Raster::CAMERATEXTURE) { + logf("CameraTexture: %d x %d\n", raster->width, raster->height); + } else if (raster->type == Raster::CAMERA) { + logf("Camera: %d x %d (ignored)\n", raster->width, raster->height); + raster->flags |= Raster::DONTALLOCATE; + raster->stride = 0; + return raster; + } else if (raster->type == Raster::ZBUFFER) { + logf("ZBuffer: %d x %d (ignored)\n", raster->width, raster->height); + raster->flags |= Raster::DONTALLOCATE; + raster->stride = 0; + return raster; + } + if(raster->width == 0 || raster->height == 0){ raster->flags |= Raster::DONTALLOCATE; raster->stride = 0; return raster; } - if (raster->width < 8) { - printf("rasterCreate: Increasing width to 8 from %d\n", raster->width); - raster->width = 8; + + auto rasterFmt = raster->format & 0x0F00; + + if (raster->type == Raster::CAMERATEXTURE) { + if (rasterFmt == Raster::DEFAULT && raster->depth == 0) { + logf("CameraTexture: Default means 565?\n"); + raster->depth = 16; + raster->format |= Raster::C565; + } } - if (raster->height < 8) { - printf("rasterCreate: Increasing height to 8 from %d\n", raster->height); - raster->height = 8; - } - auto rasterFmt = raster->format & 0x0F00; + rasterFmt = raster->format & 0x0F00; + // assert(raster->depth == 16); if (raster->depth != 16) { - raster->depth = 16; // TODO: stop this from happening - printf("rasterCreate: Usupported raster depth: this raster will be corrupted\n"); + errorf("rasterCreate: Usupported raster depth %d: this raster will be corrupted\n", raster->depth); + raster->depth = 16; } natras->raster = (DcRaster*)malloc(sizeof(DcRaster)); @@ -3779,6 +4112,9 @@ rasterCreate(Raster* raster) natras->raster->u = __builtin_ctz(raster->width) - 3; natras->raster->v = __builtin_ctz(raster->height) - 3; + assert(raster->width == 1 << (natras->raster->u + 3)); + assert(raster->height = 1 << (natras->raster->v + 3)); + if (rasterFmt == Raster::C565) { natras->raster->pvr_flags |= PVR_TXRFMT_RGB565; } else if (rasterFmt == Raster::C1555) { @@ -3787,11 +4123,17 @@ rasterCreate(Raster* raster) natras->raster->pvr_flags |= PVR_TXRFMT_ARGB4444; } else { // TODO: stop this from happening - printf("rasterCreate: Usupported raster depth: this raster will be corrupted\n"); + printf("rasterCreate: Usupported raster rasterFmt %X: this raster will be corrupted\n", rasterFmt); // assert(false && "unsupported rasterFmt"); } + raster->stride = raster->width * 2; + + if (raster->type == Raster::CAMERATEXTURE) { + natras->raster->texaddr = allocTexture(natras->raster, raster->width * raster->height * 2); + natras->raster->pvr_flags |= PVR_TXRFMT_NONTWIDDLED; + } return raster; } @@ -4017,7 +4359,7 @@ rasterFromImage(Raster* raster, Image* image) std::vector imageData; if (image->depth == 32) { - assert(rasterFmt == Raster::C4444 || rasterFmt == Raster::C1555); + assert(rasterFmt == Raster::C4444 || rasterFmt == Raster::C1555 || rasterFmt == Raster::C565 /* DXT compression */); imageData = createImageFromData_ARGB8888(image->pixels, image->width, image->height, image->stride); } else if (image->depth == 24) { assert(rasterFmt == Raster::C565); @@ -4061,7 +4403,7 @@ rasterFromImage(Raster* raster, Image* image) #if defined(_WIN32) || defined(_WIN64) case PVRTEX: snprintf(encodeCommand, sizeof(encodeCommand), - "pvrtex\\pvrtex.exe -i %s -o %s -c small -d", filename_tga, filename_pvr); + "..\\vendor\\pvrtex\\pvrtex.exe -i %s -o %s -c small -d", filename_tga, filename_pvr); break; case PVRTOOL: snprintf(encodeCommand, sizeof(encodeCommand), @@ -4071,7 +4413,7 @@ rasterFromImage(Raster* raster, Image* image) #else case PVRTEX: snprintf(encodeCommand, sizeof(encodeCommand), - "./pvrtex/pvrtex -i %s -o %s -c small -d", filename_tga, filename_pvr); + "../vendor/pvrtex/pvrtex -i %s -o %s -c small -d", filename_tga, filename_pvr); break; case PVRTOOL: snprintf(encodeCommand, sizeof(encodeCommand), @@ -4234,6 +4576,14 @@ rasterToImage(Raster*) return nil; } +static pvr_init_params_t pvr_params = { + .opb_sizes = { + PVR_BINSIZE_16, PVR_BINSIZE_0, PVR_BINSIZE_8, PVR_BINSIZE_0, + PVR_BINSIZE_8 + }, + .autosort_disabled = true +}; + int deviceSystem(DeviceReq req, void *arg0, int32 n) { @@ -4269,15 +4619,22 @@ deviceSystem(DeviceReq req, void *arg0, int32 n) rwmode->flags = VIDEOMODEEXCLUSIVE; return 1; } - - case DEVICEGETMAXMULTISAMPLINGLEVELS: - { - return 1; - } + return 2; case DEVICEGETMULTISAMPLINGLEVELS: - return 1; + return 1 << pvr_params.fsaa_enabled; case DEVICESETMULTISAMPLINGLEVELS: + if (n == 1) { + VIDEO_MODE_SCALE_X = 1; + pvr_params.fsaa_enabled = 0; + pvr_params.vertex_buf_size = (1024 + 1024) * 1024; + pvr_params.opb_overflow_count = 7; // 268800 bytes + } else { + VIDEO_MODE_SCALE_X = 2; + pvr_params.fsaa_enabled = 1; + pvr_params.vertex_buf_size = (1024 + 768) * 1024; + pvr_params.opb_overflow_count = 4; // 307200 bytes + } return 1; case DEVICESETSUBSYSTEM: return 1; @@ -4314,16 +4671,6 @@ Device renderdevice = { deviceSystem }; -static pvr_init_params_t pvr_params = { - .opb_sizes = { - PVR_BINSIZE_16, PVR_BINSIZE_0, PVR_BINSIZE_8, PVR_BINSIZE_0, - PVR_BINSIZE_8 - }, - .vertex_buf_size = (1024 + 1024) * 1024, - .autosort_disabled = true, - .opb_overflow_count = 7 // 268800 bytes -}; - void defaultInstance(ObjPipeline *pipe, Atomic *atomic) { #if defined(DC_TEXCONV) processGeom(atomic->geometry); @@ -4348,6 +4695,34 @@ ObjPipeline* makeDefaultPipeline(void) static void* driverOpen(void *o, int32, int32) { + #if defined(DC_SH4) + OCR_SPACE = (uint8_t*)0x92000000; + + bool has_oix = true; + enter_oix(); + *(volatile uint8_t*)OCR_SPACE = 1; + if (*(volatile uint8_t*)OCR_SPACE != 1) { + has_oix = false; + } + leave_oix(); + + if (!has_oix) { + dbglog(DBG_CRITICAL, "You appear to be using an emulator that does not support OIX. Attempting fallback to OCR\n"); + OCR_SPACE = (uint8_t*)0x7c001000; + enter_oix = (void(*)())(((uintptr_t)&enter_ocr_) - 0x8c000000 + 0xAc000000); + leave_oix = (void(*)())(((uintptr_t)&leave_ocr_) - 0x8c000000 + 0xAc000000); + + for (size_t i = 0; i < ARRAY_SIZE(submitMeshletSelector); i++) { + submitMeshletSelector[i] = submitMeshletSelectorFallback[i]; + } + + for (size_t i = 0; i < ARRAY_SIZE(clipAndsubmitMeshletSelector); i++) { + clipAndsubmitMeshletSelector[i] = clipAndsubmitMeshletSelectorFallback[i]; + } + } + #endif + + pvr_init(&pvr_params); fake_tex = pvr_mem_malloc(sizeof(fake_tex_data)); @@ -4383,6 +4758,8 @@ driverClose(void *o, int32, int32) { pvr_mem_free(fake_tex); + pvr_shutdown(); + return o; } @@ -4442,7 +4819,7 @@ readNativeTexture(Stream *stream) cached->second->refs++; natras->raster = cached->second; stream->seek(pvr_size); - printf("Raster reused for texture %s\n", tex->name); + logf("Raster reused for texture %s\n", tex->name); } else { natras->raster = (DcRaster*)malloc(sizeof(DcRaster)); memset(natras->raster, 0, sizeof(DcRaster)); @@ -4508,7 +4885,7 @@ readNativeTexture(Stream *stream) } } else { stream->seek(pvr_size); - printf("Failed to allocate raster pixels for texture %s\n", tex->name); + errorf("Failed to allocate raster pixels for texture %s\n", tex->name); } } @@ -4563,7 +4940,7 @@ writeNativeTexture(Texture *tex, Stream *stream) } #endif -#define DC_MODEL_VERSION 5 +#define DC_MODEL_VERSION 6 void* destroyNativeData(void *object, int32, int32) @@ -5354,8 +5731,9 @@ void processGeom(Geometry *geo) { } assert(spanCount); + assert(spanCount < 0x80); - skinningIndexData.write(0x8000 | spanCount); // count + clear flag + skinningIndexData.write(0x8000 | (spanCount-1)); // count + clear flag skinningIndexData.write(spanStartIdx * 64); // dst offset assert(spanStartIdx + spanCount <= meshlet.vertices.size()); @@ -5384,7 +5762,8 @@ void processGeom(Geometry *geo) { spanCount++; } if (spanCount) { - skinningIndexData.write(0x8000 | spanCount); // count + clear flag + assert(spanCount <= 0x80); + skinningIndexData.write(0x8000 | (spanCount - 1)); // count + clear flag skinningIndexData.write(spanStartIdx * 64); // dst offset } @@ -5410,7 +5789,7 @@ void processGeom(Geometry *geo) { assert(skinMatrix0Only[currentMtx0Idx++] == (startVtx + k)); } } else if (!(flags & 0x80)) { - int count = flags & 0x7FFF; + int count = (flags & 0x7F) + 1; int dstVertex = skinningIndexData[skinningIndexDataStart] | (skinningIndexData[skinningIndexDataStart + 1] << 8); skinningIndexDataStart += 2; texconvf("%s: Clear: count %d, dst %d\n", currentFile, count, dstVertex/64); diff --git a/vendor/librw/src/dc/rwdc.h b/vendor/librw/src/dc/rwdc.h index 2d18e0c0..449e751c 100644 --- a/vendor/librw/src/dc/rwdc.h +++ b/vendor/librw/src/dc/rwdc.h @@ -46,7 +46,7 @@ struct Im2DVertex void setScreenZ(float32 z) { this->z = z; } // This is a bit unefficient but we have to counteract GL's divide, so multiply void setCameraZ(float32 z) { this->w = z; } - void setRecipCameraZ(float32 recipz) { this->w = 1.0f/recipz; } + void setRecipCameraZ(float32 recipz) { /* don't change w here, as recipz is not always the correct one, see Coronas.cpp */ } void setColor(uint8 r, uint8 g, uint8 b, uint8 a) { this->r = r; this->g = g; this->b = b; this->a = a; } void setU(float32 u, float recipz) { this->u = u; } diff --git a/vendor/librw/src/engine.cpp b/vendor/librw/src/engine.cpp index bf73bafa..c49e4b08 100644 --- a/vendor/librw/src/engine.cpp +++ b/vendor/librw/src/engine.cpp @@ -10,7 +10,7 @@ #include "rwpipeline.h" #include "rwobjects.h" #include "rwengine.h" -// #include "ps2/rwps2.h" +#include "ps2-x/rwps2.h" // #include "d3d/rwxbox.h" // #include "d3d/rwd3d.h" #include "d3d-x/rwd3d8.h" @@ -143,8 +143,7 @@ void *mustmalloc_h(size_t sz, uint32 hint) ret = Engine::memfuncs.rwmalloc(sz, hint); if(ret || sz == 0) return ret; - fprintf(stderr, "Error: out of memory\n"); - exit(1); + assert(ret != 0); return nil; } void *mustrealloc_h(void *p, size_t sz, uint32 hint) @@ -153,8 +152,7 @@ void *mustrealloc_h(void *p, size_t sz, uint32 hint) ret = Engine::memfuncs.rwrealloc(p, sz, hint); if(ret || sz == 0) return ret; - fprintf(stderr, "Error: out of memory\n"); - exit(1); + assert(ret != 0); return nil; } @@ -240,6 +238,7 @@ Engine::init(MemoryFunctions *memfuncs) gl3::registerPlatformPlugins(); #else #if defined(DC_TEXCONV) + ps2::registerPlatformPlugins(); d3d8::registerPlatformPlugins(); #endif dc::registerPlatformPlugins(); diff --git a/src/skel/fcaseopen.h b/vendor/librw/src/fcaseopen.h similarity index 100% rename from src/skel/fcaseopen.h rename to vendor/librw/src/fcaseopen.h diff --git a/src/math/float16.h b/vendor/librw/src/float16.h similarity index 100% rename from src/math/float16.h rename to vendor/librw/src/float16.h diff --git a/vendor/librw/src/rwobjects.h b/vendor/librw/src/rwobjects.h index 88559908..51a97119 100644 --- a/vendor/librw/src/rwobjects.h +++ b/vendor/librw/src/rwobjects.h @@ -744,7 +744,7 @@ struct Camera enum { PERSPECTIVE = 1, PARALLEL }; enum { CLEARIMAGE = 0x1, CLEARZ = 0x2, CLEARSTENCIL = 0x4 }; // return value of frustumTestSphere - enum { SPHEREOUTSIDE, SPHEREBOUNDARY, SPHEREINSIDE }; + enum { SPHEREOUTSIDE, SPHEREBOUNDARY, SPHEREINSIDE, SPHEREBOUNDARY_NEAR /* frustumTestSphereEx only */}; ObjectWithFrame object; void (*beginUpdateCB)(Camera*); @@ -779,6 +779,7 @@ struct Camera /* RW: frustum sectors, space, position */ World *world; ObjectWithFrame::Sync originalSync; + RGBA clearColor; void (*originalBeginUpdate)(Camera*); void (*originalEndUpdate)(Camera*); @@ -801,6 +802,9 @@ struct Camera void setViewOffset(const V2d *offset); void setProjection(int32 proj); int32 frustumTestSphere(const Sphere *s) const; + + //considers SPHEREBOUNDARY as SPHEREINSIDE, returns SPHEREBOUNDARY_NEAR for near plane + int32 frustumTestSphereNear(const Sphere *s) const; static Camera *streamRead(Stream *stream); bool streamWrite(Stream *stream); uint32 streamGetSize(void); diff --git a/vendor/librw/src/texture.cpp b/vendor/librw/src/texture.cpp index aca7e2f5..0b2cd374 100644 --- a/vendor/librw/src/texture.cpp +++ b/vendor/librw/src/texture.cpp @@ -10,7 +10,7 @@ #include "rwpipeline.h" #include "rwobjects.h" #include "rwengine.h" -// #include "ps2/rwps2.h" +#include "ps2-x/rwps2.h" // #include "d3d-x/rwd3d.h" // #include "d3d/rwxbox.h" #include "d3d-x/rwd3d8.h" @@ -473,8 +473,6 @@ Texture::streamReadNative(Stream *stream) uint32 platform = stream->readU32(); stream->seek(-16); #if !defined(RW_DC) - if(platform == FOURCC_PS2) - return ps2::readNativeTexture(stream); if(platform == PLATFORM_D3D8) return d3d8::readNativeTexture(stream); if(platform == PLATFORM_D3D9) @@ -485,6 +483,8 @@ Texture::streamReadNative(Stream *stream) return gl3::readNativeTexture(stream); #else #if defined(DC_TEXCONV) + if(platform == FOURCC_PS2) + return ps2::readNativeTexture(stream); if(platform == PLATFORM_D3D8) return d3d8::readNativeTexture(stream); #endif diff --git a/dreamcast/pvrtex/.gitignore b/vendor/pvrtex/.gitignore similarity index 100% rename from dreamcast/pvrtex/.gitignore rename to vendor/pvrtex/.gitignore diff --git a/dreamcast/pvrtex/Makefile b/vendor/pvrtex/Makefile similarity index 100% rename from dreamcast/pvrtex/Makefile rename to vendor/pvrtex/Makefile diff --git a/dreamcast/pvrtex/README b/vendor/pvrtex/README similarity index 100% rename from dreamcast/pvrtex/README rename to vendor/pvrtex/README diff --git a/dreamcast/pvrtex/README.md b/vendor/pvrtex/README.md similarity index 100% rename from dreamcast/pvrtex/README.md rename to vendor/pvrtex/README.md diff --git a/dreamcast/pvrtex/avstring.c b/vendor/pvrtex/avstring.c similarity index 100% rename from dreamcast/pvrtex/avstring.c rename to vendor/pvrtex/avstring.c diff --git a/dreamcast/pvrtex/bprint.c b/vendor/pvrtex/bprint.c similarity index 100% rename from dreamcast/pvrtex/bprint.c rename to vendor/pvrtex/bprint.c diff --git a/dreamcast/pvrtex/compat/va_copy.h b/vendor/pvrtex/compat/va_copy.h similarity index 100% rename from dreamcast/pvrtex/compat/va_copy.h rename to vendor/pvrtex/compat/va_copy.h diff --git a/dreamcast/pvrtex/config.h b/vendor/pvrtex/config.h similarity index 100% rename from dreamcast/pvrtex/config.h rename to vendor/pvrtex/config.h diff --git a/dreamcast/pvrtex/crc.c b/vendor/pvrtex/crc.c similarity index 100% rename from dreamcast/pvrtex/crc.c rename to vendor/pvrtex/crc.c diff --git a/dreamcast/pvrtex/dither.cpp b/vendor/pvrtex/dither.cpp similarity index 100% rename from dreamcast/pvrtex/dither.cpp rename to vendor/pvrtex/dither.cpp diff --git a/dreamcast/pvrtex/elbg.c b/vendor/pvrtex/elbg.c similarity index 100% rename from dreamcast/pvrtex/elbg.c rename to vendor/pvrtex/elbg.c diff --git a/dreamcast/pvrtex/elbg.h b/vendor/pvrtex/elbg.h similarity index 100% rename from dreamcast/pvrtex/elbg.h rename to vendor/pvrtex/elbg.h diff --git a/dreamcast/pvrtex/file_common.c b/vendor/pvrtex/file_common.c similarity index 100% rename from dreamcast/pvrtex/file_common.c rename to vendor/pvrtex/file_common.c diff --git a/dreamcast/pvrtex/file_common.h b/vendor/pvrtex/file_common.h similarity index 100% rename from dreamcast/pvrtex/file_common.h rename to vendor/pvrtex/file_common.h diff --git a/dreamcast/pvrtex/file_dctex.c b/vendor/pvrtex/file_dctex.c similarity index 100% rename from dreamcast/pvrtex/file_dctex.c rename to vendor/pvrtex/file_dctex.c diff --git a/dreamcast/pvrtex/file_dctex.h b/vendor/pvrtex/file_dctex.h similarity index 100% rename from dreamcast/pvrtex/file_dctex.h rename to vendor/pvrtex/file_dctex.h diff --git a/dreamcast/pvrtex/file_pvr.c b/vendor/pvrtex/file_pvr.c similarity index 100% rename from dreamcast/pvrtex/file_pvr.c rename to vendor/pvrtex/file_pvr.c diff --git a/dreamcast/pvrtex/file_pvr.h b/vendor/pvrtex/file_pvr.h similarity index 100% rename from dreamcast/pvrtex/file_pvr.h rename to vendor/pvrtex/file_pvr.h diff --git a/dreamcast/pvrtex/file_tex.c b/vendor/pvrtex/file_tex.c similarity index 100% rename from dreamcast/pvrtex/file_tex.c rename to vendor/pvrtex/file_tex.c diff --git a/dreamcast/pvrtex/file_tex.h b/vendor/pvrtex/file_tex.h similarity index 100% rename from dreamcast/pvrtex/file_tex.h rename to vendor/pvrtex/file_tex.h diff --git a/dreamcast/pvrtex/lfg.c b/vendor/pvrtex/lfg.c similarity index 100% rename from dreamcast/pvrtex/lfg.c rename to vendor/pvrtex/lfg.c diff --git a/dreamcast/pvrtex/libavcodec/elbg.h b/vendor/pvrtex/libavcodec/elbg.h similarity index 100% rename from dreamcast/pvrtex/libavcodec/elbg.h rename to vendor/pvrtex/libavcodec/elbg.h diff --git a/dreamcast/pvrtex/libavutil/attributes.h b/vendor/pvrtex/libavutil/attributes.h similarity index 100% rename from dreamcast/pvrtex/libavutil/attributes.h rename to vendor/pvrtex/libavutil/attributes.h diff --git a/dreamcast/pvrtex/libavutil/attributes_internal.h b/vendor/pvrtex/libavutil/attributes_internal.h similarity index 100% rename from dreamcast/pvrtex/libavutil/attributes_internal.h rename to vendor/pvrtex/libavutil/attributes_internal.h diff --git a/dreamcast/pvrtex/libavutil/avassert.h b/vendor/pvrtex/libavutil/avassert.h similarity index 100% rename from dreamcast/pvrtex/libavutil/avassert.h rename to vendor/pvrtex/libavutil/avassert.h diff --git a/dreamcast/pvrtex/libavutil/avconfig.h b/vendor/pvrtex/libavutil/avconfig.h similarity index 100% rename from dreamcast/pvrtex/libavutil/avconfig.h rename to vendor/pvrtex/libavutil/avconfig.h diff --git a/dreamcast/pvrtex/libavutil/avstring.h b/vendor/pvrtex/libavutil/avstring.h similarity index 100% rename from dreamcast/pvrtex/libavutil/avstring.h rename to vendor/pvrtex/libavutil/avstring.h diff --git a/dreamcast/pvrtex/libavutil/avutil.h b/vendor/pvrtex/libavutil/avutil.h similarity index 100% rename from dreamcast/pvrtex/libavutil/avutil.h rename to vendor/pvrtex/libavutil/avutil.h diff --git a/dreamcast/pvrtex/libavutil/bprint.h b/vendor/pvrtex/libavutil/bprint.h similarity index 100% rename from dreamcast/pvrtex/libavutil/bprint.h rename to vendor/pvrtex/libavutil/bprint.h diff --git a/dreamcast/pvrtex/libavutil/bswap.h b/vendor/pvrtex/libavutil/bswap.h similarity index 100% rename from dreamcast/pvrtex/libavutil/bswap.h rename to vendor/pvrtex/libavutil/bswap.h diff --git a/dreamcast/pvrtex/libavutil/common.h b/vendor/pvrtex/libavutil/common.h similarity index 100% rename from dreamcast/pvrtex/libavutil/common.h rename to vendor/pvrtex/libavutil/common.h diff --git a/dreamcast/pvrtex/libavutil/crc.h b/vendor/pvrtex/libavutil/crc.h similarity index 100% rename from dreamcast/pvrtex/libavutil/crc.h rename to vendor/pvrtex/libavutil/crc.h diff --git a/dreamcast/pvrtex/libavutil/dynarray.h b/vendor/pvrtex/libavutil/dynarray.h similarity index 100% rename from dreamcast/pvrtex/libavutil/dynarray.h rename to vendor/pvrtex/libavutil/dynarray.h diff --git a/dreamcast/pvrtex/libavutil/error.h b/vendor/pvrtex/libavutil/error.h similarity index 100% rename from dreamcast/pvrtex/libavutil/error.h rename to vendor/pvrtex/libavutil/error.h diff --git a/dreamcast/pvrtex/libavutil/internal.h b/vendor/pvrtex/libavutil/internal.h similarity index 100% rename from dreamcast/pvrtex/libavutil/internal.h rename to vendor/pvrtex/libavutil/internal.h diff --git a/dreamcast/pvrtex/libavutil/intfloat.h b/vendor/pvrtex/libavutil/intfloat.h similarity index 100% rename from dreamcast/pvrtex/libavutil/intfloat.h rename to vendor/pvrtex/libavutil/intfloat.h diff --git a/dreamcast/pvrtex/libavutil/intreadwrite.h b/vendor/pvrtex/libavutil/intreadwrite.h similarity index 100% rename from dreamcast/pvrtex/libavutil/intreadwrite.h rename to vendor/pvrtex/libavutil/intreadwrite.h diff --git a/dreamcast/pvrtex/libavutil/lfg.c b/vendor/pvrtex/libavutil/lfg.c similarity index 100% rename from dreamcast/pvrtex/libavutil/lfg.c rename to vendor/pvrtex/libavutil/lfg.c diff --git a/dreamcast/pvrtex/libavutil/lfg.h b/vendor/pvrtex/libavutil/lfg.h similarity index 100% rename from dreamcast/pvrtex/libavutil/lfg.h rename to vendor/pvrtex/libavutil/lfg.h diff --git a/dreamcast/pvrtex/libavutil/libm.h b/vendor/pvrtex/libavutil/libm.h similarity index 100% rename from dreamcast/pvrtex/libavutil/libm.h rename to vendor/pvrtex/libavutil/libm.h diff --git a/dreamcast/pvrtex/libavutil/log.h b/vendor/pvrtex/libavutil/log.h similarity index 100% rename from dreamcast/pvrtex/libavutil/log.h rename to vendor/pvrtex/libavutil/log.h diff --git a/dreamcast/pvrtex/libavutil/macros.h b/vendor/pvrtex/libavutil/macros.h similarity index 100% rename from dreamcast/pvrtex/libavutil/macros.h rename to vendor/pvrtex/libavutil/macros.h diff --git a/dreamcast/pvrtex/libavutil/mathematics.c b/vendor/pvrtex/libavutil/mathematics.c similarity index 100% rename from dreamcast/pvrtex/libavutil/mathematics.c rename to vendor/pvrtex/libavutil/mathematics.c diff --git a/dreamcast/pvrtex/libavutil/mathematics.h b/vendor/pvrtex/libavutil/mathematics.h similarity index 100% rename from dreamcast/pvrtex/libavutil/mathematics.h rename to vendor/pvrtex/libavutil/mathematics.h diff --git a/dreamcast/pvrtex/libavutil/md5.h b/vendor/pvrtex/libavutil/md5.h similarity index 100% rename from dreamcast/pvrtex/libavutil/md5.h rename to vendor/pvrtex/libavutil/md5.h diff --git a/dreamcast/pvrtex/libavutil/mem.c b/vendor/pvrtex/libavutil/mem.c similarity index 100% rename from dreamcast/pvrtex/libavutil/mem.c rename to vendor/pvrtex/libavutil/mem.c diff --git a/dreamcast/pvrtex/libavutil/mem.h b/vendor/pvrtex/libavutil/mem.h similarity index 100% rename from dreamcast/pvrtex/libavutil/mem.h rename to vendor/pvrtex/libavutil/mem.h diff --git a/dreamcast/pvrtex/libavutil/pixfmt.h b/vendor/pvrtex/libavutil/pixfmt.h similarity index 100% rename from dreamcast/pvrtex/libavutil/pixfmt.h rename to vendor/pvrtex/libavutil/pixfmt.h diff --git a/dreamcast/pvrtex/libavutil/rational.c b/vendor/pvrtex/libavutil/rational.c similarity index 100% rename from dreamcast/pvrtex/libavutil/rational.c rename to vendor/pvrtex/libavutil/rational.c diff --git a/dreamcast/pvrtex/libavutil/rational.h b/vendor/pvrtex/libavutil/rational.h similarity index 100% rename from dreamcast/pvrtex/libavutil/rational.h rename to vendor/pvrtex/libavutil/rational.h diff --git a/dreamcast/pvrtex/libavutil/thread.h b/vendor/pvrtex/libavutil/thread.h similarity index 100% rename from dreamcast/pvrtex/libavutil/thread.h rename to vendor/pvrtex/libavutil/thread.h diff --git a/dreamcast/pvrtex/libavutil/version.h b/vendor/pvrtex/libavutil/version.h similarity index 100% rename from dreamcast/pvrtex/libavutil/version.h rename to vendor/pvrtex/libavutil/version.h diff --git a/dreamcast/pvrtex/log.c b/vendor/pvrtex/log.c similarity index 100% rename from dreamcast/pvrtex/log.c rename to vendor/pvrtex/log.c diff --git a/dreamcast/pvrtex/main.c b/vendor/pvrtex/main.c similarity index 100% rename from dreamcast/pvrtex/main.c rename to vendor/pvrtex/main.c diff --git a/dreamcast/pvrtex/md5.c b/vendor/pvrtex/md5.c similarity index 100% rename from dreamcast/pvrtex/md5.c rename to vendor/pvrtex/md5.c diff --git a/dreamcast/pvrtex/mem.c b/vendor/pvrtex/mem.c similarity index 100% rename from dreamcast/pvrtex/mem.c rename to vendor/pvrtex/mem.c diff --git a/dreamcast/pvrtex/mycommon.c b/vendor/pvrtex/mycommon.c similarity index 100% rename from dreamcast/pvrtex/mycommon.c rename to vendor/pvrtex/mycommon.c diff --git a/dreamcast/pvrtex/mycommon.h b/vendor/pvrtex/mycommon.h similarity index 100% rename from dreamcast/pvrtex/mycommon.h rename to vendor/pvrtex/mycommon.h diff --git a/dreamcast/pvrtex/nvmath.h b/vendor/pvrtex/nvmath.h similarity index 100% rename from dreamcast/pvrtex/nvmath.h rename to vendor/pvrtex/nvmath.h diff --git a/dreamcast/pvrtex/optparse.h b/vendor/pvrtex/optparse.h similarity index 100% rename from dreamcast/pvrtex/optparse.h rename to vendor/pvrtex/optparse.h diff --git a/dreamcast/pvrtex/optparse_impl.c b/vendor/pvrtex/optparse_impl.c similarity index 100% rename from dreamcast/pvrtex/optparse_impl.c rename to vendor/pvrtex/optparse_impl.c diff --git a/dreamcast/pvrtex/pixel.h b/vendor/pvrtex/pixel.h similarity index 100% rename from dreamcast/pvrtex/pixel.h rename to vendor/pvrtex/pixel.h diff --git a/dreamcast/pvrtex/pvr_texture.c b/vendor/pvrtex/pvr_texture.c similarity index 100% rename from dreamcast/pvrtex/pvr_texture.c rename to vendor/pvrtex/pvr_texture.c diff --git a/dreamcast/pvrtex/pvr_texture.h b/vendor/pvrtex/pvr_texture.h similarity index 100% rename from dreamcast/pvrtex/pvr_texture.h rename to vendor/pvrtex/pvr_texture.h diff --git a/dreamcast/pvrtex/pvr_texture_encoder.c b/vendor/pvrtex/pvr_texture_encoder.c similarity index 100% rename from dreamcast/pvrtex/pvr_texture_encoder.c rename to vendor/pvrtex/pvr_texture_encoder.c diff --git a/dreamcast/pvrtex/pvr_texture_encoder.h b/vendor/pvrtex/pvr_texture_encoder.h similarity index 100% rename from dreamcast/pvrtex/pvr_texture_encoder.h rename to vendor/pvrtex/pvr_texture_encoder.h diff --git a/dreamcast/pvrtex/readme_unformatted.txt b/vendor/pvrtex/readme_unformatted.txt similarity index 100% rename from dreamcast/pvrtex/readme_unformatted.txt rename to vendor/pvrtex/readme_unformatted.txt diff --git a/dreamcast/pvrtex/stb_image.h b/vendor/pvrtex/stb_image.h similarity index 100% rename from dreamcast/pvrtex/stb_image.h rename to vendor/pvrtex/stb_image.h diff --git a/dreamcast/pvrtex/stb_image_impl.c b/vendor/pvrtex/stb_image_impl.c similarity index 100% rename from dreamcast/pvrtex/stb_image_impl.c rename to vendor/pvrtex/stb_image_impl.c diff --git a/dreamcast/pvrtex/stb_image_resize.h b/vendor/pvrtex/stb_image_resize.h similarity index 100% rename from dreamcast/pvrtex/stb_image_resize.h rename to vendor/pvrtex/stb_image_resize.h diff --git a/dreamcast/pvrtex/stb_image_resize_impl.c b/vendor/pvrtex/stb_image_resize_impl.c similarity index 100% rename from dreamcast/pvrtex/stb_image_resize_impl.c rename to vendor/pvrtex/stb_image_resize_impl.c diff --git a/dreamcast/pvrtex/stb_image_write.h b/vendor/pvrtex/stb_image_write.h similarity index 100% rename from dreamcast/pvrtex/stb_image_write.h rename to vendor/pvrtex/stb_image_write.h diff --git a/dreamcast/pvrtex/stb_image_write_impl.c b/vendor/pvrtex/stb_image_write_impl.c similarity index 100% rename from dreamcast/pvrtex/stb_image_write_impl.c rename to vendor/pvrtex/stb_image_write_impl.c diff --git a/dreamcast/pvrtex/tddither.c b/vendor/pvrtex/tddither.c similarity index 100% rename from dreamcast/pvrtex/tddither.c rename to vendor/pvrtex/tddither.c diff --git a/dreamcast/pvrtex/tddither.h b/vendor/pvrtex/tddither.h similarity index 100% rename from dreamcast/pvrtex/tddither.h rename to vendor/pvrtex/tddither.h diff --git a/dreamcast/pvrtex/vqcompress.c b/vendor/pvrtex/vqcompress.c similarity index 100% rename from dreamcast/pvrtex/vqcompress.c rename to vendor/pvrtex/vqcompress.c diff --git a/dreamcast/pvrtex/vqcompress.h b/vendor/pvrtex/vqcompress.h similarity index 100% rename from dreamcast/pvrtex/vqcompress.h rename to vendor/pvrtex/vqcompress.h